From abb84101121bf74f85c59f9b11532f86f8ee2392 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 26 Jun 2014 01:14:26 -0700 Subject: [PATCH 0001/3817] skeleton. This commit was moved from ipfs/go-path@131d562d8e337f6d0e58a352e9e9f8014dd506a9 --- path/path.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 path/path.go diff --git a/path/path.go b/path/path.go new file mode 100644 index 000000000..e69de29bb From 303470d7731d7f13ead75dbfeaf585f4908254ba Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 26 Jun 2014 01:14:26 -0700 Subject: [PATCH 0002/3817] skeleton. This commit was moved from ipfs/go-ipfs-routing@7aeccb3167ee10c49e0453dad879803564de7eee --- routing/routing.go | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 routing/routing.go diff --git a/routing/routing.go b/routing/routing.go new file mode 100644 index 000000000..2ab572c9a --- /dev/null +++ b/routing/routing.go @@ -0,0 +1,4 @@ +package routing + + +TODO SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-routing/index.js From bd583e1463dfcb82f6d7a229d505b4ce40f04623 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 4 Jul 2014 14:54:20 -0700 Subject: [PATCH 0003/3817] merkledag This commit was moved from ipfs/go-merkledag@63e4477573afa18090eb45e6b836bdccacfa8d89 --- ipld/merkledag/coding.go | 61 ++++++++++++++++++++++++++++++++++ ipld/merkledag/merkledag.go | 36 ++++++++++++++++++++ ipld/merkledag/merkledag.proto | 40 ++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 ipld/merkledag/coding.go create mode 100644 ipld/merkledag/merkledag.go create mode 100644 ipld/merkledag/merkledag.proto diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go new file mode 100644 index 000000000..8f531b09e --- /dev/null +++ b/ipld/merkledag/coding.go @@ -0,0 +1,61 @@ +package merkledag + +import ( + "fmt" + mh "github.com/jbenet/go-multihash" +) + +// for now, we use a PBNode intermediate thing. +// because native go objects are nice. + +func (n *Node) Unmarshal(encoded []byte) error { + var pbn PBNode + if err := pbn.Unmarshal(encoded), err != nil { + return fmt.Errorf("Unmarshal failed. %v", err) + } + + pbnl := pbn.Links() + n.Links = make([]*Link, len(pbnl)) + for i, l := range(pbnl) { + n.Links[i] = &Link{Name: l.GetName(), Size: l.GetSize()} + n.Links[i].Hash, err := mh.Cast(l.GetHash()) + if err != nil { + return fmt.Errorf("Link hash is not valid multihash. %v", err) + } + } + + n.Data = pbn.GetData() + return nil +} + +func (n *Node) MarshalTo(encoded []byte) error { + pbn := n.getPBNode() + if err := pbn.MarshalTo(encoded), err != nil { + return fmt.Errorf("Marshal failed. %v", err) + } + return nil +} + +func (n *Node) Marshal() ([]byte, error) { + pbn := n.getPBNode() + data, err := pbn.Marshal() + if err != nil { + return data, fmt.Errorf("Marshal failed. %v", err) + } + return data, nil +} + +func (n *Node) getPBNode() *PBNode { + pbn := &PBNode{} + pbn.Links = make([]*PBLink, len(n.Links)) + for i, l := range(n.Links) { + pbn.Links[i] = &PBLink{} + n.Links[i].Name = &l.Name + n.Links[i].Size = l.Size + n.Links[i].Hash = &[]byte(l.Hash) + } + + pbn.Data = &n.Data + return pbn, nil +} + diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go new file mode 100644 index 000000000..619727795 --- /dev/null +++ b/ipld/merkledag/merkledag.go @@ -0,0 +1,36 @@ +package merkledag + +import ( + mh "github.com/jbenet/go-multihash" +) + +// A node in the IPFS Merkle DAG. +// nodes have opaque data and a set of navigable links. +type Node { + Links []*Link + Data []byte +} + + +// An IPFS Merkle DAG Link +type Link { + // utf string name. should be unique per object + Name string // utf8 + + // cumulative size of target object + Size uint64 + + // multihash of the target object + Hash mh.Multihash +} + + +type EncodedNode []byte + + +func (n *Node) Size() uint64 { + uint64 s = len(n.Encode()) + for _, l := range(n.Links) { + s += l.Size + } +} diff --git a/ipld/merkledag/merkledag.proto b/ipld/merkledag/merkledag.proto new file mode 100644 index 000000000..fdbd94c60 --- /dev/null +++ b/ipld/merkledag/merkledag.proto @@ -0,0 +1,40 @@ +package merkledag + +import "code.google.com/p/gogoprotobuf/gogoproto/gogo.proto"; + +option (gogoproto.gostring_all) = true; +option (gogoproto.equal_all) = true; +option (gogoproto.verbose_equal_all) = true; +option (gogoproto.goproto_stringer_all) = false; +option (gogoproto.stringer_all) = true; +option (gogoproto.populate_all) = true; +option (gogoproto.testgen_all) = true; +option (gogoproto.benchgen_all) = true; +option (gogoproto.marshaler_all) = true; +option (gogoproto.sizer_all) = true; +option (gogoproto.unmarshaler_all) = true; + + +// An IPFS MerkleDAG Node +message PBNode { + + // refs to other objects + repeated Link Links = 2; + + // opaque user data + optional bytes Data = 1; +} + + +// An IPFS MerkleDAG Link +message PBLink { + + // multihash of the target object + optional bytes Hash = 1; + + // utf string name. should be unique per object + optional string Name = 2; + + // cumulative size of target object + optional uint64 Size = 3; +} From 6ce7ba9dc493ea5c97b4d7cb53b5c3865584b3ff Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 4 Jul 2014 15:18:15 -0700 Subject: [PATCH 0004/3817] renamed This commit was moved from ipfs/go-merkledag@68d78e5f2eb86dd93983695701011ed6a47ea2fb --- .../merkledag/{merkledag.proto => node.proto} | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) rename ipld/merkledag/{merkledag.proto => node.proto} (94%) diff --git a/ipld/merkledag/merkledag.proto b/ipld/merkledag/node.proto similarity index 94% rename from ipld/merkledag/merkledag.proto rename to ipld/merkledag/node.proto index fdbd94c60..ce661a949 100644 --- a/ipld/merkledag/merkledag.proto +++ b/ipld/merkledag/node.proto @@ -1,4 +1,4 @@ -package merkledag +package merkledag; import "code.google.com/p/gogoprotobuf/gogoproto/gogo.proto"; @@ -15,17 +15,6 @@ option (gogoproto.sizer_all) = true; option (gogoproto.unmarshaler_all) = true; -// An IPFS MerkleDAG Node -message PBNode { - - // refs to other objects - repeated Link Links = 2; - - // opaque user data - optional bytes Data = 1; -} - - // An IPFS MerkleDAG Link message PBLink { @@ -38,3 +27,13 @@ message PBLink { // cumulative size of target object optional uint64 Size = 3; } + +// An IPFS MerkleDAG Node +message PBNode { + + // refs to other objects + repeated PBLink Links = 2; + + // opaque user data + optional bytes Data = 1; +} From 4b5e56ec7fa6f765e9498ef54d50378ca6bac145 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 4 Jul 2014 15:18:26 -0700 Subject: [PATCH 0005/3817] Makefile This commit was moved from ipfs/go-merkledag@757191129d44982974b0c85f79d5d19c90f52b5c --- ipld/merkledag/Makefile | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 ipld/merkledag/Makefile diff --git a/ipld/merkledag/Makefile b/ipld/merkledag/Makefile new file mode 100644 index 000000000..ec1a09650 --- /dev/null +++ b/ipld/merkledag/Makefile @@ -0,0 +1,5 @@ + +all: node.pb.go + +node.pb.go: node.proto + protoc --gogo_out=. --proto_path=../../../../:/usr/local/opt/protobuf/include:. $< From d114604d7fd65a122cd5a61fd86de4708df78d88 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 4 Jul 2014 15:29:18 -0700 Subject: [PATCH 0006/3817] coding with protobuf This commit was moved from ipfs/go-merkledag@dcdb1e32a0f42b53d49c930d99bcbc35d9f5b3e4 --- ipld/merkledag/coding.go | 21 +++++++++++---------- ipld/merkledag/merkledag.go | 14 ++++++++++---- ipld/merkledag/node.proto | 2 +- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 8f531b09e..08681f957 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -10,18 +10,19 @@ import ( func (n *Node) Unmarshal(encoded []byte) error { var pbn PBNode - if err := pbn.Unmarshal(encoded), err != nil { + if err := pbn.Unmarshal(encoded); err != nil { return fmt.Errorf("Unmarshal failed. %v", err) } - pbnl := pbn.Links() + pbnl := pbn.GetLinks() n.Links = make([]*Link, len(pbnl)) for i, l := range(pbnl) { - n.Links[i] = &Link{Name: l.GetName(), Size: l.GetSize()} - n.Links[i].Hash, err := mh.Cast(l.GetHash()) + n.Links[i] = &Link{Name: l.GetName(), Size: l.GetTsize()} + h, err := mh.Cast(l.GetHash()) if err != nil { return fmt.Errorf("Link hash is not valid multihash. %v", err) } + n.Links[i].Hash = h } n.Data = pbn.GetData() @@ -30,7 +31,7 @@ func (n *Node) Unmarshal(encoded []byte) error { func (n *Node) MarshalTo(encoded []byte) error { pbn := n.getPBNode() - if err := pbn.MarshalTo(encoded), err != nil { + if _, err := pbn.MarshalTo(encoded); err != nil { return fmt.Errorf("Marshal failed. %v", err) } return nil @@ -50,12 +51,12 @@ func (n *Node) getPBNode() *PBNode { pbn.Links = make([]*PBLink, len(n.Links)) for i, l := range(n.Links) { pbn.Links[i] = &PBLink{} - n.Links[i].Name = &l.Name - n.Links[i].Size = l.Size - n.Links[i].Hash = &[]byte(l.Hash) + pbn.Links[i].Name = &l.Name + pbn.Links[i].Tsize = &l.Size + pbn.Links[i].Hash = []byte(l.Hash) } - pbn.Data = &n.Data - return pbn, nil + pbn.Data = n.Data + return pbn } diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 619727795..88a3b6a6b 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -6,14 +6,14 @@ import ( // A node in the IPFS Merkle DAG. // nodes have opaque data and a set of navigable links. -type Node { +type Node struct { Links []*Link Data []byte } // An IPFS Merkle DAG Link -type Link { +type Link struct { // utf string name. should be unique per object Name string // utf8 @@ -28,9 +28,15 @@ type Link { type EncodedNode []byte -func (n *Node) Size() uint64 { - uint64 s = len(n.Encode()) +func (n *Node) Size() (uint64, error) { + d, err := n.Marshal() + if err != nil { + return 0, err + } + + s := uint64(len(d)) for _, l := range(n.Links) { s += l.Size } + return s, nil } diff --git a/ipld/merkledag/node.proto b/ipld/merkledag/node.proto index ce661a949..f0f82a425 100644 --- a/ipld/merkledag/node.proto +++ b/ipld/merkledag/node.proto @@ -25,7 +25,7 @@ message PBLink { optional string Name = 2; // cumulative size of target object - optional uint64 Size = 3; + optional uint64 Tsize = 3; } // An IPFS MerkleDAG Node From 2151673a2b30ada8cc31da721e1df04d632f2ea5 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 4 Jul 2014 15:29:43 -0700 Subject: [PATCH 0007/3817] gofmt This commit was moved from ipfs/go-merkledag@301a925a8f2c0ff4beddac3937e63ef744ddd006 --- ipld/merkledag/coding.go | 85 ++++++++++++++++++------------------- ipld/merkledag/merkledag.go | 41 +++++++++--------- 2 files changed, 61 insertions(+), 65 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 08681f957..7eac3d2da 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -1,62 +1,61 @@ package merkledag import ( - "fmt" - mh "github.com/jbenet/go-multihash" + "fmt" + mh "github.com/jbenet/go-multihash" ) // for now, we use a PBNode intermediate thing. // because native go objects are nice. func (n *Node) Unmarshal(encoded []byte) error { - var pbn PBNode - if err := pbn.Unmarshal(encoded); err != nil { - return fmt.Errorf("Unmarshal failed. %v", err) - } - - pbnl := pbn.GetLinks() - n.Links = make([]*Link, len(pbnl)) - for i, l := range(pbnl) { - n.Links[i] = &Link{Name: l.GetName(), Size: l.GetTsize()} - h, err := mh.Cast(l.GetHash()) - if err != nil { - return fmt.Errorf("Link hash is not valid multihash. %v", err) - } - n.Links[i].Hash = h - } - - n.Data = pbn.GetData() - return nil + var pbn PBNode + if err := pbn.Unmarshal(encoded); err != nil { + return fmt.Errorf("Unmarshal failed. %v", err) + } + + pbnl := pbn.GetLinks() + n.Links = make([]*Link, len(pbnl)) + for i, l := range pbnl { + n.Links[i] = &Link{Name: l.GetName(), Size: l.GetTsize()} + h, err := mh.Cast(l.GetHash()) + if err != nil { + return fmt.Errorf("Link hash is not valid multihash. %v", err) + } + n.Links[i].Hash = h + } + + n.Data = pbn.GetData() + return nil } func (n *Node) MarshalTo(encoded []byte) error { - pbn := n.getPBNode() - if _, err := pbn.MarshalTo(encoded); err != nil { - return fmt.Errorf("Marshal failed. %v", err) - } - return nil + pbn := n.getPBNode() + if _, err := pbn.MarshalTo(encoded); err != nil { + return fmt.Errorf("Marshal failed. %v", err) + } + return nil } func (n *Node) Marshal() ([]byte, error) { - pbn := n.getPBNode() - data, err := pbn.Marshal() - if err != nil { - return data, fmt.Errorf("Marshal failed. %v", err) - } - return data, nil + pbn := n.getPBNode() + data, err := pbn.Marshal() + if err != nil { + return data, fmt.Errorf("Marshal failed. %v", err) + } + return data, nil } func (n *Node) getPBNode() *PBNode { - pbn := &PBNode{} - pbn.Links = make([]*PBLink, len(n.Links)) - for i, l := range(n.Links) { - pbn.Links[i] = &PBLink{} - pbn.Links[i].Name = &l.Name - pbn.Links[i].Tsize = &l.Size - pbn.Links[i].Hash = []byte(l.Hash) - } - - pbn.Data = n.Data - return pbn + pbn := &PBNode{} + pbn.Links = make([]*PBLink, len(n.Links)) + for i, l := range n.Links { + pbn.Links[i] = &PBLink{} + pbn.Links[i].Name = &l.Name + pbn.Links[i].Tsize = &l.Size + pbn.Links[i].Hash = []byte(l.Hash) + } + + pbn.Data = n.Data + return pbn } - diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 88a3b6a6b..0dc814140 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -1,42 +1,39 @@ package merkledag import ( - mh "github.com/jbenet/go-multihash" + mh "github.com/jbenet/go-multihash" ) // A node in the IPFS Merkle DAG. // nodes have opaque data and a set of navigable links. type Node struct { - Links []*Link - Data []byte + Links []*Link + Data []byte } - // An IPFS Merkle DAG Link type Link struct { - // utf string name. should be unique per object - Name string // utf8 + // utf string name. should be unique per object + Name string // utf8 - // cumulative size of target object - Size uint64 + // cumulative size of target object + Size uint64 - // multihash of the target object - Hash mh.Multihash + // multihash of the target object + Hash mh.Multihash } - type EncodedNode []byte - func (n *Node) Size() (uint64, error) { - d, err := n.Marshal() - if err != nil { - return 0, err - } - - s := uint64(len(d)) - for _, l := range(n.Links) { - s += l.Size - } - return s, nil + d, err := n.Marshal() + if err != nil { + return 0, err + } + + s := uint64(len(d)) + for _, l := range n.Links { + s += l.Size + } + return s, nil } From f5af763f9d98ddbe0a57ad032d2f5dd3052909d8 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 4 Jul 2014 16:55:03 -0700 Subject: [PATCH 0008/3817] merkledag objects This commit was moved from ipfs/go-merkledag@e9cb869dc053083779167bd89c9e4475ce1b4a69 --- ipld/merkledag/coding.go | 12 + ipld/merkledag/merkledag.go | 35 +- ipld/merkledag/merkledag_test.go | 48 ++ ipld/merkledag/node.pb.go | 802 +++++++++++++++++++++++++++++++ ipld/merkledag/nodepb_test.go | 472 ++++++++++++++++++ 5 files changed, 1366 insertions(+), 3 deletions(-) create mode 100644 ipld/merkledag/merkledag_test.go create mode 100644 ipld/merkledag/node.pb.go create mode 100644 ipld/merkledag/nodepb_test.go diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 7eac3d2da..76763ec42 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -59,3 +59,15 @@ func (n *Node) getPBNode() *PBNode { pbn.Data = n.Data return pbn } + +func (n *Node) Encoded(force bool) ([]byte, error) { + if n.encoded == nil || force { + var err error + n.encoded, err = n.Marshal() + if err != nil { + return []byte{}, err + } + } + + return n.encoded, nil +} diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 0dc814140..46e41575e 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -9,6 +9,9 @@ import ( type Node struct { Links []*Link Data []byte + + // cache encoded/marshaled value + encoded []byte } // An IPFS Merkle DAG Link @@ -23,17 +26,43 @@ type Link struct { Hash mh.Multihash } -type EncodedNode []byte +func (n *Node) AddNodeLink(name string, that *Node) error { + s, err := that.Size() + if err != nil { + return err + } + + h, err := that.Multihash() + if err != nil { + return err + } + + n.Links = append(n.Links, &Link{ + Name: name, + Size: s, + Hash: h, + }) + return nil +} func (n *Node) Size() (uint64, error) { - d, err := n.Marshal() + b, err := n.Encoded(false) if err != nil { return 0, err } - s := uint64(len(d)) + s := uint64(len(b)) for _, l := range n.Links { s += l.Size } return s, nil } + +func (n *Node) Multihash() (mh.Multihash, error) { + b, err := n.Encoded(false) + if err != nil { + return nil, err + } + + return mh.Sum(b, mh.SHA2_256, -1) +} diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go new file mode 100644 index 000000000..fd261c1f5 --- /dev/null +++ b/ipld/merkledag/merkledag_test.go @@ -0,0 +1,48 @@ +package merkledag + +import ( + "fmt" + // mh "github.com/jbenet/go-multihash" + "testing" +) + +func TestNode(t *testing.T) { + + n1 := &Node{Data: []byte("beep")} + n2 := &Node{Data: []byte("boop")} + n3 := &Node{Data: []byte("beep boop")} + if err := n3.AddNodeLink("beep-link", n1); err != nil { + t.Error(err) + } + if err := n3.AddNodeLink("boop-link", n2); err != nil { + t.Error(err) + } + + printn := func(name string, n *Node) { + fmt.Println(">", name) + fmt.Println("data:", string(n.Data)) + + fmt.Println("links:") + for _, l := range n.Links { + fmt.Println("-", l.Name, l.Size, l.Hash) + } + + e, err := n.Encoded(false) + if err != nil { + t.Error(err) + } else { + fmt.Println("encoded:", e) + } + + h, err := n.Multihash() + if err != nil { + t.Error(err) + } else { + fmt.Println("hash:", h) + } + } + + printn("beep", n1) + printn("boop", n2) + printn("beep boop", n3) +} diff --git a/ipld/merkledag/node.pb.go b/ipld/merkledag/node.pb.go new file mode 100644 index 000000000..e6442e4d1 --- /dev/null +++ b/ipld/merkledag/node.pb.go @@ -0,0 +1,802 @@ +// Code generated by protoc-gen-gogo. +// source: node.proto +// DO NOT EDIT! + +/* + Package merkledag is a generated protocol buffer package. + + It is generated from these files: + node.proto + + It has these top-level messages: + PBLink + PBNode +*/ +package merkledag + +import proto "code.google.com/p/gogoprotobuf/proto" +import json "encoding/json" +import math "math" + +// discarding unused import gogoproto "code.google.com/p/gogoprotobuf/gogoproto/gogo.pb" + +import io "io" +import code_google_com_p_gogoprotobuf_proto "code.google.com/p/gogoprotobuf/proto" + +import fmt "fmt" +import strings "strings" +import reflect "reflect" + +import fmt1 "fmt" +import strings1 "strings" +import code_google_com_p_gogoprotobuf_proto1 "code.google.com/p/gogoprotobuf/proto" +import sort "sort" +import strconv "strconv" +import reflect1 "reflect" + +import fmt2 "fmt" +import bytes "bytes" + +// Reference proto, json, and math imports to suppress error if they are not otherwise used. +var _ = proto.Marshal +var _ = &json.SyntaxError{} +var _ = math.Inf + +// An IPFS MerkleDAG Link +type PBLink struct { + // multihash of the target object + Hash []byte `protobuf:"bytes,1,opt" json:"Hash,omitempty"` + // utf string name. should be unique per object + Name *string `protobuf:"bytes,2,opt" json:"Name,omitempty"` + // cumulative size of target object + Tsize *uint64 `protobuf:"varint,3,opt" json:"Tsize,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PBLink) Reset() { *m = PBLink{} } +func (*PBLink) ProtoMessage() {} + +func (m *PBLink) GetHash() []byte { + if m != nil { + return m.Hash + } + return nil +} + +func (m *PBLink) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *PBLink) GetTsize() uint64 { + if m != nil && m.Tsize != nil { + return *m.Tsize + } + return 0 +} + +// An IPFS MerkleDAG Node +type PBNode struct { + // refs to other objects + Links []*PBLink `protobuf:"bytes,2,rep" json:"Links,omitempty"` + // opaque user data + Data []byte `protobuf:"bytes,1,opt" json:"Data,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PBNode) Reset() { *m = PBNode{} } +func (*PBNode) ProtoMessage() {} + +func (m *PBNode) GetLinks() []*PBLink { + if m != nil { + return m.Links + } + return nil +} + +func (m *PBNode) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func init() { +} +func (m *PBLink) Unmarshal(data []byte) error { + l := len(data) + index := 0 + for index < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if index >= l { + return io.ErrUnexpectedEOF + } + b := data[index] + index++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + switch fieldNum { + case 1: + if wireType != 2 { + return code_google_com_p_gogoprotobuf_proto.ErrWrongType + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if index >= l { + return io.ErrUnexpectedEOF + } + b := data[index] + index++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + postIndex := index + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hash = append(m.Hash, data[index:postIndex]...) + index = postIndex + case 2: + if wireType != 2 { + return code_google_com_p_gogoprotobuf_proto.ErrWrongType + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if index >= l { + return io.ErrUnexpectedEOF + } + b := data[index] + index++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + postIndex := index + int(stringLen) + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(data[index:postIndex]) + m.Name = &s + index = postIndex + case 3: + if wireType != 0 { + return code_google_com_p_gogoprotobuf_proto.ErrWrongType + } + var v uint64 + for shift := uint(0); ; shift += 7 { + if index >= l { + return io.ErrUnexpectedEOF + } + b := data[index] + index++ + v |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Tsize = &v + default: + var sizeOfWire int + for { + sizeOfWire++ + wire >>= 7 + if wire == 0 { + break + } + } + index -= sizeOfWire + skippy, err := code_google_com_p_gogoprotobuf_proto.Skip(data[index:]) + if err != nil { + return err + } + if (index + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, data[index:index+skippy]...) + index += skippy + } + } + return nil +} +func (m *PBNode) Unmarshal(data []byte) error { + l := len(data) + index := 0 + for index < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if index >= l { + return io.ErrUnexpectedEOF + } + b := data[index] + index++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + switch fieldNum { + case 2: + if wireType != 2 { + return code_google_com_p_gogoprotobuf_proto.ErrWrongType + } + var msglen int + for shift := uint(0); ; shift += 7 { + if index >= l { + return io.ErrUnexpectedEOF + } + b := data[index] + index++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + postIndex := index + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Links = append(m.Links, &PBLink{}) + m.Links[len(m.Links)-1].Unmarshal(data[index:postIndex]) + index = postIndex + case 1: + if wireType != 2 { + return code_google_com_p_gogoprotobuf_proto.ErrWrongType + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if index >= l { + return io.ErrUnexpectedEOF + } + b := data[index] + index++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + postIndex := index + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data, data[index:postIndex]...) + index = postIndex + default: + var sizeOfWire int + for { + sizeOfWire++ + wire >>= 7 + if wire == 0 { + break + } + } + index -= sizeOfWire + skippy, err := code_google_com_p_gogoprotobuf_proto.Skip(data[index:]) + if err != nil { + return err + } + if (index + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, data[index:index+skippy]...) + index += skippy + } + } + return nil +} +func (this *PBLink) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PBLink{`, + `Hash:` + valueToStringNode(this.Hash) + `,`, + `Name:` + valueToStringNode(this.Name) + `,`, + `Tsize:` + valueToStringNode(this.Tsize) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *PBNode) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PBNode{`, + `Links:` + strings.Replace(fmt.Sprintf("%v", this.Links), "PBLink", "PBLink", 1) + `,`, + `Data:` + valueToStringNode(this.Data) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func valueToStringNode(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *PBLink) Size() (n int) { + var l int + _ = l + if m.Hash != nil { + l = len(m.Hash) + n += 1 + l + sovNode(uint64(l)) + } + if m.Name != nil { + l = len(*m.Name) + n += 1 + l + sovNode(uint64(l)) + } + if m.Tsize != nil { + n += 1 + sovNode(uint64(*m.Tsize)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} +func (m *PBNode) Size() (n int) { + var l int + _ = l + if len(m.Links) > 0 { + for _, e := range m.Links { + l = e.Size() + n += 1 + l + sovNode(uint64(l)) + } + } + if m.Data != nil { + l = len(m.Data) + n += 1 + l + sovNode(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovNode(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozNode(x uint64) (n int) { + return sovNode(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func NewPopulatedPBLink(r randyNode, easy bool) *PBLink { + this := &PBLink{} + if r.Intn(10) != 0 { + v1 := r.Intn(100) + this.Hash = make([]byte, v1) + for i := 0; i < v1; i++ { + this.Hash[i] = byte(r.Intn(256)) + } + } + if r.Intn(10) != 0 { + v2 := randStringNode(r) + this.Name = &v2 + } + if r.Intn(10) != 0 { + v3 := uint64(r.Uint32()) + this.Tsize = &v3 + } + if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedNode(r, 4) + } + return this +} + +func NewPopulatedPBNode(r randyNode, easy bool) *PBNode { + this := &PBNode{} + if r.Intn(10) != 0 { + v4 := r.Intn(10) + this.Links = make([]*PBLink, v4) + for i := 0; i < v4; i++ { + this.Links[i] = NewPopulatedPBLink(r, easy) + } + } + if r.Intn(10) != 0 { + v5 := r.Intn(100) + this.Data = make([]byte, v5) + for i := 0; i < v5; i++ { + this.Data[i] = byte(r.Intn(256)) + } + } + if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedNode(r, 3) + } + return this +} + +type randyNode interface { + Float32() float32 + Float64() float64 + Int63() int64 + Int31() int32 + Uint32() uint32 + Intn(n int) int +} + +func randUTF8RuneNode(r randyNode) rune { + res := rune(r.Uint32() % 1112064) + if 55296 <= res { + res += 2047 + } + return res +} +func randStringNode(r randyNode) string { + v6 := r.Intn(100) + tmps := make([]rune, v6) + for i := 0; i < v6; i++ { + tmps[i] = randUTF8RuneNode(r) + } + return string(tmps) +} +func randUnrecognizedNode(r randyNode, maxFieldNumber int) (data []byte) { + l := r.Intn(5) + for i := 0; i < l; i++ { + wire := r.Intn(4) + if wire == 3 { + wire = 5 + } + fieldNumber := maxFieldNumber + r.Intn(100) + data = randFieldNode(data, r, fieldNumber, wire) + } + return data +} +func randFieldNode(data []byte, r randyNode, fieldNumber int, wire int) []byte { + key := uint32(fieldNumber)<<3 | uint32(wire) + switch wire { + case 0: + data = encodeVarintPopulateNode(data, uint64(key)) + v7 := r.Int63() + if r.Intn(2) == 0 { + v7 *= -1 + } + data = encodeVarintPopulateNode(data, uint64(v7)) + case 1: + data = encodeVarintPopulateNode(data, uint64(key)) + data = append(data, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) + case 2: + data = encodeVarintPopulateNode(data, uint64(key)) + ll := r.Intn(100) + data = encodeVarintPopulateNode(data, uint64(ll)) + for j := 0; j < ll; j++ { + data = append(data, byte(r.Intn(256))) + } + default: + data = encodeVarintPopulateNode(data, uint64(key)) + data = append(data, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) + } + return data +} +func encodeVarintPopulateNode(data []byte, v uint64) []byte { + for v >= 1<<7 { + data = append(data, uint8(uint64(v)&0x7f|0x80)) + v >>= 7 + } + data = append(data, uint8(v)) + return data +} +func (m *PBLink) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +func (m *PBLink) MarshalTo(data []byte) (n int, err error) { + var i int + _ = i + var l int + _ = l + if m.Hash != nil { + data[i] = 0xa + i++ + i = encodeVarintNode(data, i, uint64(len(m.Hash))) + i += copy(data[i:], m.Hash) + } + if m.Name != nil { + data[i] = 0x12 + i++ + i = encodeVarintNode(data, i, uint64(len(*m.Name))) + i += copy(data[i:], *m.Name) + } + if m.Tsize != nil { + data[i] = 0x18 + i++ + i = encodeVarintNode(data, i, uint64(*m.Tsize)) + } + if m.XXX_unrecognized != nil { + i += copy(data[i:], m.XXX_unrecognized) + } + return i, nil +} +func (m *PBNode) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +func (m *PBNode) MarshalTo(data []byte) (n int, err error) { + var i int + _ = i + var l int + _ = l + if len(m.Links) > 0 { + for _, msg := range m.Links { + data[i] = 0x12 + i++ + i = encodeVarintNode(data, i, uint64(msg.Size())) + n, err := msg.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if m.Data != nil { + data[i] = 0xa + i++ + i = encodeVarintNode(data, i, uint64(len(m.Data))) + i += copy(data[i:], m.Data) + } + if m.XXX_unrecognized != nil { + i += copy(data[i:], m.XXX_unrecognized) + } + return i, nil +} +func encodeFixed64Node(data []byte, offset int, v uint64) int { + data[offset] = uint8(v) + data[offset+1] = uint8(v >> 8) + data[offset+2] = uint8(v >> 16) + data[offset+3] = uint8(v >> 24) + data[offset+4] = uint8(v >> 32) + data[offset+5] = uint8(v >> 40) + data[offset+6] = uint8(v >> 48) + data[offset+7] = uint8(v >> 56) + return offset + 8 +} +func encodeFixed32Node(data []byte, offset int, v uint32) int { + data[offset] = uint8(v) + data[offset+1] = uint8(v >> 8) + data[offset+2] = uint8(v >> 16) + data[offset+3] = uint8(v >> 24) + return offset + 4 +} +func encodeVarintNode(data []byte, offset int, v uint64) int { + for v >= 1<<7 { + data[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + data[offset] = uint8(v) + return offset + 1 +} +func (this *PBLink) GoString() string { + if this == nil { + return "nil" + } + s := strings1.Join([]string{`&merkledag.PBLink{` + `Hash:` + valueToGoStringNode(this.Hash, "byte"), `Name:` + valueToGoStringNode(this.Name, "string"), `Tsize:` + valueToGoStringNode(this.Tsize, "uint64"), `XXX_unrecognized:` + fmt1.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") + return s +} +func (this *PBNode) GoString() string { + if this == nil { + return "nil" + } + s := strings1.Join([]string{`&merkledag.PBNode{` + `Links:` + fmt1.Sprintf("%#v", this.Links), `Data:` + valueToGoStringNode(this.Data, "byte"), `XXX_unrecognized:` + fmt1.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") + return s +} +func valueToGoStringNode(v interface{}, typ string) string { + rv := reflect1.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect1.Indirect(rv).Interface() + return fmt1.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) +} +func extensionToGoStringNode(e map[int32]code_google_com_p_gogoprotobuf_proto1.Extension) string { + if e == nil { + return "nil" + } + s := "map[int32]proto.Extension{" + keys := make([]int, 0, len(e)) + for k := range e { + keys = append(keys, int(k)) + } + sort.Ints(keys) + ss := []string{} + for _, k := range keys { + ss = append(ss, strconv.Itoa(k)+": "+e[int32(k)].GoString()) + } + s += strings1.Join(ss, ",") + "}" + return s +} +func (this *PBLink) VerboseEqual(that interface{}) error { + if that == nil { + if this == nil { + return nil + } + return fmt2.Errorf("that == nil && this != nil") + } + + that1, ok := that.(*PBLink) + if !ok { + return fmt2.Errorf("that is not of type *PBLink") + } + if that1 == nil { + if this == nil { + return nil + } + return fmt2.Errorf("that is type *PBLink but is nil && this != nil") + } else if this == nil { + return fmt2.Errorf("that is type *PBLinkbut is not nil && this == nil") + } + if !bytes.Equal(this.Hash, that1.Hash) { + return fmt2.Errorf("Hash this(%v) Not Equal that(%v)", this.Hash, that1.Hash) + } + if this.Name != nil && that1.Name != nil { + if *this.Name != *that1.Name { + return fmt2.Errorf("Name this(%v) Not Equal that(%v)", *this.Name, *that1.Name) + } + } else if this.Name != nil { + return fmt2.Errorf("this.Name == nil && that.Name != nil") + } else if that1.Name != nil { + return fmt2.Errorf("Name this(%v) Not Equal that(%v)", this.Name, that1.Name) + } + if this.Tsize != nil && that1.Tsize != nil { + if *this.Tsize != *that1.Tsize { + return fmt2.Errorf("Tsize this(%v) Not Equal that(%v)", *this.Tsize, *that1.Tsize) + } + } else if this.Tsize != nil { + return fmt2.Errorf("this.Tsize == nil && that.Tsize != nil") + } else if that1.Tsize != nil { + return fmt2.Errorf("Tsize this(%v) Not Equal that(%v)", this.Tsize, that1.Tsize) + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return fmt2.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) + } + return nil +} +func (this *PBLink) Equal(that interface{}) bool { + if that == nil { + if this == nil { + return true + } + return false + } + + that1, ok := that.(*PBLink) + if !ok { + return false + } + if that1 == nil { + if this == nil { + return true + } + return false + } else if this == nil { + return false + } + if !bytes.Equal(this.Hash, that1.Hash) { + return false + } + if this.Name != nil && that1.Name != nil { + if *this.Name != *that1.Name { + return false + } + } else if this.Name != nil { + return false + } else if that1.Name != nil { + return false + } + if this.Tsize != nil && that1.Tsize != nil { + if *this.Tsize != *that1.Tsize { + return false + } + } else if this.Tsize != nil { + return false + } else if that1.Tsize != nil { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *PBNode) VerboseEqual(that interface{}) error { + if that == nil { + if this == nil { + return nil + } + return fmt2.Errorf("that == nil && this != nil") + } + + that1, ok := that.(*PBNode) + if !ok { + return fmt2.Errorf("that is not of type *PBNode") + } + if that1 == nil { + if this == nil { + return nil + } + return fmt2.Errorf("that is type *PBNode but is nil && this != nil") + } else if this == nil { + return fmt2.Errorf("that is type *PBNodebut is not nil && this == nil") + } + if len(this.Links) != len(that1.Links) { + return fmt2.Errorf("Links this(%v) Not Equal that(%v)", len(this.Links), len(that1.Links)) + } + for i := range this.Links { + if !this.Links[i].Equal(that1.Links[i]) { + return fmt2.Errorf("Links this[%v](%v) Not Equal that[%v](%v)", i, this.Links[i], i, that1.Links[i]) + } + } + if !bytes.Equal(this.Data, that1.Data) { + return fmt2.Errorf("Data this(%v) Not Equal that(%v)", this.Data, that1.Data) + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return fmt2.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) + } + return nil +} +func (this *PBNode) Equal(that interface{}) bool { + if that == nil { + if this == nil { + return true + } + return false + } + + that1, ok := that.(*PBNode) + if !ok { + return false + } + if that1 == nil { + if this == nil { + return true + } + return false + } else if this == nil { + return false + } + if len(this.Links) != len(that1.Links) { + return false + } + for i := range this.Links { + if !this.Links[i].Equal(that1.Links[i]) { + return false + } + } + if !bytes.Equal(this.Data, that1.Data) { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} diff --git a/ipld/merkledag/nodepb_test.go b/ipld/merkledag/nodepb_test.go new file mode 100644 index 000000000..b7ef81c3d --- /dev/null +++ b/ipld/merkledag/nodepb_test.go @@ -0,0 +1,472 @@ +// Code generated by protoc-gen-gogo. +// source: node.proto +// DO NOT EDIT! + +/* +Package merkledag is a generated protocol buffer package. + +It is generated from these files: + node.proto + +It has these top-level messages: + PBLink + PBNode +*/ +package merkledag + +import testing "testing" +import math_rand "math/rand" +import time "time" +import code_google_com_p_gogoprotobuf_proto "code.google.com/p/gogoprotobuf/proto" +import testing1 "testing" +import math_rand1 "math/rand" +import time1 "time" +import encoding_json "encoding/json" +import testing2 "testing" +import math_rand2 "math/rand" +import time2 "time" +import code_google_com_p_gogoprotobuf_proto1 "code.google.com/p/gogoprotobuf/proto" +import math_rand3 "math/rand" +import time3 "time" +import testing3 "testing" +import fmt "fmt" +import math_rand4 "math/rand" +import time4 "time" +import testing4 "testing" +import code_google_com_p_gogoprotobuf_proto2 "code.google.com/p/gogoprotobuf/proto" +import math_rand5 "math/rand" +import time5 "time" +import testing5 "testing" +import fmt1 "fmt" +import go_parser "go/parser" +import math_rand6 "math/rand" +import time6 "time" +import testing6 "testing" +import code_google_com_p_gogoprotobuf_proto3 "code.google.com/p/gogoprotobuf/proto" + +func TestPBLinkProto(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + p := NewPopulatedPBLink(popr, false) + data, err := code_google_com_p_gogoprotobuf_proto.Marshal(p) + if err != nil { + panic(err) + } + msg := &PBLink{} + if err := code_google_com_p_gogoprotobuf_proto.Unmarshal(data, msg); err != nil { + panic(err) + } + for i := range data { + data[i] = byte(popr.Intn(256)) + } + if err := p.VerboseEqual(msg); err != nil { + t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + } + if !p.Equal(msg) { + t.Fatalf("%#v !Proto %#v", msg, p) + } +} + +func TestPBLinkMarshalTo(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + p := NewPopulatedPBLink(popr, false) + size := p.Size() + data := make([]byte, size) + for i := range data { + data[i] = byte(popr.Intn(256)) + } + _, err := p.MarshalTo(data) + if err != nil { + panic(err) + } + msg := &PBLink{} + if err := code_google_com_p_gogoprotobuf_proto.Unmarshal(data, msg); err != nil { + panic(err) + } + for i := range data { + data[i] = byte(popr.Intn(256)) + } + if err := p.VerboseEqual(msg); err != nil { + t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + } + if !p.Equal(msg) { + t.Fatalf("%#v !Proto %#v", msg, p) + } +} + +func BenchmarkPBLinkProtoMarshal(b *testing.B) { + popr := math_rand.New(math_rand.NewSource(616)) + total := 0 + pops := make([]*PBLink, 10000) + for i := 0; i < 10000; i++ { + pops[i] = NewPopulatedPBLink(popr, false) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + data, err := code_google_com_p_gogoprotobuf_proto.Marshal(pops[i%10000]) + if err != nil { + panic(err) + } + total += len(data) + } + b.SetBytes(int64(total / b.N)) +} + +func BenchmarkPBLinkProtoUnmarshal(b *testing.B) { + popr := math_rand.New(math_rand.NewSource(616)) + total := 0 + datas := make([][]byte, 10000) + for i := 0; i < 10000; i++ { + data, err := code_google_com_p_gogoprotobuf_proto.Marshal(NewPopulatedPBLink(popr, false)) + if err != nil { + panic(err) + } + datas[i] = data + } + msg := &PBLink{} + b.ResetTimer() + for i := 0; i < b.N; i++ { + total += len(datas[i%10000]) + if err := code_google_com_p_gogoprotobuf_proto.Unmarshal(datas[i%10000], msg); err != nil { + panic(err) + } + } + b.SetBytes(int64(total / b.N)) +} + +func TestPBNodeProto(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + p := NewPopulatedPBNode(popr, false) + data, err := code_google_com_p_gogoprotobuf_proto.Marshal(p) + if err != nil { + panic(err) + } + msg := &PBNode{} + if err := code_google_com_p_gogoprotobuf_proto.Unmarshal(data, msg); err != nil { + panic(err) + } + for i := range data { + data[i] = byte(popr.Intn(256)) + } + if err := p.VerboseEqual(msg); err != nil { + t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + } + if !p.Equal(msg) { + t.Fatalf("%#v !Proto %#v", msg, p) + } +} + +func TestPBNodeMarshalTo(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + p := NewPopulatedPBNode(popr, false) + size := p.Size() + data := make([]byte, size) + for i := range data { + data[i] = byte(popr.Intn(256)) + } + _, err := p.MarshalTo(data) + if err != nil { + panic(err) + } + msg := &PBNode{} + if err := code_google_com_p_gogoprotobuf_proto.Unmarshal(data, msg); err != nil { + panic(err) + } + for i := range data { + data[i] = byte(popr.Intn(256)) + } + if err := p.VerboseEqual(msg); err != nil { + t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + } + if !p.Equal(msg) { + t.Fatalf("%#v !Proto %#v", msg, p) + } +} + +func BenchmarkPBNodeProtoMarshal(b *testing.B) { + popr := math_rand.New(math_rand.NewSource(616)) + total := 0 + pops := make([]*PBNode, 10000) + for i := 0; i < 10000; i++ { + pops[i] = NewPopulatedPBNode(popr, false) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + data, err := code_google_com_p_gogoprotobuf_proto.Marshal(pops[i%10000]) + if err != nil { + panic(err) + } + total += len(data) + } + b.SetBytes(int64(total / b.N)) +} + +func BenchmarkPBNodeProtoUnmarshal(b *testing.B) { + popr := math_rand.New(math_rand.NewSource(616)) + total := 0 + datas := make([][]byte, 10000) + for i := 0; i < 10000; i++ { + data, err := code_google_com_p_gogoprotobuf_proto.Marshal(NewPopulatedPBNode(popr, false)) + if err != nil { + panic(err) + } + datas[i] = data + } + msg := &PBNode{} + b.ResetTimer() + for i := 0; i < b.N; i++ { + total += len(datas[i%10000]) + if err := code_google_com_p_gogoprotobuf_proto.Unmarshal(datas[i%10000], msg); err != nil { + panic(err) + } + } + b.SetBytes(int64(total / b.N)) +} + +func TestPBLinkJSON(t *testing1.T) { + popr := math_rand1.New(math_rand1.NewSource(time1.Now().UnixNano())) + p := NewPopulatedPBLink(popr, true) + jsondata, err := encoding_json.Marshal(p) + if err != nil { + panic(err) + } + msg := &PBLink{} + err = encoding_json.Unmarshal(jsondata, msg) + if err != nil { + panic(err) + } + if err := p.VerboseEqual(msg); err != nil { + t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + } + if !p.Equal(msg) { + t.Fatalf("%#v !Json Equal %#v", msg, p) + } +} +func TestPBNodeJSON(t *testing1.T) { + popr := math_rand1.New(math_rand1.NewSource(time1.Now().UnixNano())) + p := NewPopulatedPBNode(popr, true) + jsondata, err := encoding_json.Marshal(p) + if err != nil { + panic(err) + } + msg := &PBNode{} + err = encoding_json.Unmarshal(jsondata, msg) + if err != nil { + panic(err) + } + if err := p.VerboseEqual(msg); err != nil { + t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + } + if !p.Equal(msg) { + t.Fatalf("%#v !Json Equal %#v", msg, p) + } +} +func TestPBLinkProtoText(t *testing2.T) { + popr := math_rand2.New(math_rand2.NewSource(time2.Now().UnixNano())) + p := NewPopulatedPBLink(popr, true) + data := code_google_com_p_gogoprotobuf_proto1.MarshalTextString(p) + msg := &PBLink{} + if err := code_google_com_p_gogoprotobuf_proto1.UnmarshalText(data, msg); err != nil { + panic(err) + } + if err := p.VerboseEqual(msg); err != nil { + t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + } + if !p.Equal(msg) { + t.Fatalf("%#v !Proto %#v", msg, p) + } +} + +func TestPBLinkProtoCompactText(t *testing2.T) { + popr := math_rand2.New(math_rand2.NewSource(time2.Now().UnixNano())) + p := NewPopulatedPBLink(popr, true) + data := code_google_com_p_gogoprotobuf_proto1.CompactTextString(p) + msg := &PBLink{} + if err := code_google_com_p_gogoprotobuf_proto1.UnmarshalText(data, msg); err != nil { + panic(err) + } + if err := p.VerboseEqual(msg); err != nil { + t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + } + if !p.Equal(msg) { + t.Fatalf("%#v !Proto %#v", msg, p) + } +} + +func TestPBNodeProtoText(t *testing2.T) { + popr := math_rand2.New(math_rand2.NewSource(time2.Now().UnixNano())) + p := NewPopulatedPBNode(popr, true) + data := code_google_com_p_gogoprotobuf_proto1.MarshalTextString(p) + msg := &PBNode{} + if err := code_google_com_p_gogoprotobuf_proto1.UnmarshalText(data, msg); err != nil { + panic(err) + } + if err := p.VerboseEqual(msg); err != nil { + t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + } + if !p.Equal(msg) { + t.Fatalf("%#v !Proto %#v", msg, p) + } +} + +func TestPBNodeProtoCompactText(t *testing2.T) { + popr := math_rand2.New(math_rand2.NewSource(time2.Now().UnixNano())) + p := NewPopulatedPBNode(popr, true) + data := code_google_com_p_gogoprotobuf_proto1.CompactTextString(p) + msg := &PBNode{} + if err := code_google_com_p_gogoprotobuf_proto1.UnmarshalText(data, msg); err != nil { + panic(err) + } + if err := p.VerboseEqual(msg); err != nil { + t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + } + if !p.Equal(msg) { + t.Fatalf("%#v !Proto %#v", msg, p) + } +} + +func TestPBLinkStringer(t *testing3.T) { + popr := math_rand3.New(math_rand3.NewSource(time3.Now().UnixNano())) + p := NewPopulatedPBLink(popr, false) + s1 := p.String() + s2 := fmt.Sprintf("%v", p) + if s1 != s2 { + t.Fatalf("String want %v got %v", s1, s2) + } +} +func TestPBNodeStringer(t *testing3.T) { + popr := math_rand3.New(math_rand3.NewSource(time3.Now().UnixNano())) + p := NewPopulatedPBNode(popr, false) + s1 := p.String() + s2 := fmt.Sprintf("%v", p) + if s1 != s2 { + t.Fatalf("String want %v got %v", s1, s2) + } +} +func TestPBLinkSize(t *testing4.T) { + popr := math_rand4.New(math_rand4.NewSource(time4.Now().UnixNano())) + p := NewPopulatedPBLink(popr, true) + size2 := code_google_com_p_gogoprotobuf_proto2.Size(p) + data, err := code_google_com_p_gogoprotobuf_proto2.Marshal(p) + if err != nil { + panic(err) + } + size := p.Size() + if len(data) != size { + t.Fatalf("size %v != marshalled size %v", size, len(data)) + } + if size2 != size { + t.Fatalf("size %v != before marshal proto.Size %v", size, size2) + } + size3 := code_google_com_p_gogoprotobuf_proto2.Size(p) + if size3 != size { + t.Fatalf("size %v != after marshal proto.Size %v", size, size3) + } +} + +func BenchmarkPBLinkSize(b *testing4.B) { + popr := math_rand4.New(math_rand4.NewSource(616)) + total := 0 + pops := make([]*PBLink, 1000) + for i := 0; i < 1000; i++ { + pops[i] = NewPopulatedPBLink(popr, false) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + total += pops[i%1000].Size() + } + b.SetBytes(int64(total / b.N)) +} + +func TestPBNodeSize(t *testing4.T) { + popr := math_rand4.New(math_rand4.NewSource(time4.Now().UnixNano())) + p := NewPopulatedPBNode(popr, true) + size2 := code_google_com_p_gogoprotobuf_proto2.Size(p) + data, err := code_google_com_p_gogoprotobuf_proto2.Marshal(p) + if err != nil { + panic(err) + } + size := p.Size() + if len(data) != size { + t.Fatalf("size %v != marshalled size %v", size, len(data)) + } + if size2 != size { + t.Fatalf("size %v != before marshal proto.Size %v", size, size2) + } + size3 := code_google_com_p_gogoprotobuf_proto2.Size(p) + if size3 != size { + t.Fatalf("size %v != after marshal proto.Size %v", size, size3) + } +} + +func BenchmarkPBNodeSize(b *testing4.B) { + popr := math_rand4.New(math_rand4.NewSource(616)) + total := 0 + pops := make([]*PBNode, 1000) + for i := 0; i < 1000; i++ { + pops[i] = NewPopulatedPBNode(popr, false) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + total += pops[i%1000].Size() + } + b.SetBytes(int64(total / b.N)) +} + +func TestPBLinkGoString(t *testing5.T) { + popr := math_rand5.New(math_rand5.NewSource(time5.Now().UnixNano())) + p := NewPopulatedPBLink(popr, false) + s1 := p.GoString() + s2 := fmt1.Sprintf("%#v", p) + if s1 != s2 { + t.Fatalf("GoString want %v got %v", s1, s2) + } + _, err := go_parser.ParseExpr(s1) + if err != nil { + panic(err) + } +} +func TestPBNodeGoString(t *testing5.T) { + popr := math_rand5.New(math_rand5.NewSource(time5.Now().UnixNano())) + p := NewPopulatedPBNode(popr, false) + s1 := p.GoString() + s2 := fmt1.Sprintf("%#v", p) + if s1 != s2 { + t.Fatalf("GoString want %v got %v", s1, s2) + } + _, err := go_parser.ParseExpr(s1) + if err != nil { + panic(err) + } +} +func TestPBLinkVerboseEqual(t *testing6.T) { + popr := math_rand6.New(math_rand6.NewSource(time6.Now().UnixNano())) + p := NewPopulatedPBLink(popr, false) + data, err := code_google_com_p_gogoprotobuf_proto3.Marshal(p) + if err != nil { + panic(err) + } + msg := &PBLink{} + if err := code_google_com_p_gogoprotobuf_proto3.Unmarshal(data, msg); err != nil { + panic(err) + } + if err := p.VerboseEqual(msg); err != nil { + t.Fatalf("%#v !VerboseEqual %#v, since %v", msg, p, err) + } +} +func TestPBNodeVerboseEqual(t *testing6.T) { + popr := math_rand6.New(math_rand6.NewSource(time6.Now().UnixNano())) + p := NewPopulatedPBNode(popr, false) + data, err := code_google_com_p_gogoprotobuf_proto3.Marshal(p) + if err != nil { + panic(err) + } + msg := &PBNode{} + if err := code_google_com_p_gogoprotobuf_proto3.Unmarshal(data, msg); err != nil { + panic(err) + } + if err := p.VerboseEqual(msg); err != nil { + t.Fatalf("%#v !VerboseEqual %#v, since %v", msg, p, err) + } +} + +//These tests are generated by code.google.com/p/gogoprotobuf/plugin/testgen From 383307dfc78564326157e37bc76eb8c5a67942f7 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 5 Jul 2014 15:01:33 -0700 Subject: [PATCH 0009/3817] added .Key This commit was moved from ipfs/go-merkledag@fc23250f9a9d357f41baf27e5b62fe1a305af6a3 --- ipld/merkledag/merkledag.go | 13 +++++++++++++ ipld/merkledag/merkledag_test.go | 11 ++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 46e41575e..56f020113 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -1,9 +1,14 @@ package merkledag import ( + u "github.com/jbenet/go-ipfs/util" mh "github.com/jbenet/go-multihash" ) +// can't use []byte/Multihash for keys :( +// so have to convert Multihash bytes to string +type NodeMap map[u.Key]*Node + // A node in the IPFS Merkle DAG. // nodes have opaque data and a set of navigable links. type Node struct { @@ -24,6 +29,9 @@ type Link struct { // multihash of the target object Hash mh.Multihash + + // a ptr to the actual node for graph manipulation + Node *Node } func (n *Node) AddNodeLink(name string, that *Node) error { @@ -66,3 +74,8 @@ func (n *Node) Multihash() (mh.Multihash, error) { return mh.Sum(b, mh.SHA2_256, -1) } + +func (n *Node) Key() (u.Key, error) { + h, err := n.Multihash() + return u.Key(h), err +} diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index fd261c1f5..7cd1649e2 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -2,7 +2,7 @@ package merkledag import ( "fmt" - // mh "github.com/jbenet/go-multihash" + u "github.com/jbenet/go-ipfs/util" "testing" ) @@ -40,6 +40,15 @@ func TestNode(t *testing.T) { } else { fmt.Println("hash:", h) } + + k, err := n.Key() + if err != nil { + t.Error(err) + } else if k != u.Key(h) { + t.Error("Key is not equivalent to multihash") + } else { + fmt.Println("key: ", k) + } } printn("beep", n1) From e4d15ed29748d007d5f4a2825a5ead9c285e89b6 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 5 Jul 2014 15:13:49 -0700 Subject: [PATCH 0010/3817] global hash func This commit was moved from ipfs/go-merkledag@9ebc81c558e7520314f36fcee84b8c41bde66219 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 56f020113..9529bc543 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -72,7 +72,7 @@ func (n *Node) Multihash() (mh.Multihash, error) { return nil, err } - return mh.Sum(b, mh.SHA2_256, -1) + return u.Hash(b) } func (n *Node) Key() (u.Key, error) { From 751e21060ea4afa8b8050b1aa1372e35f2e8965f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 5 Jul 2014 17:29:44 -0700 Subject: [PATCH 0011/3817] ipfs cat This commit was moved from ipfs/go-merkledag@eb9b75fb923925a61cb589a10be61daadfdd0244 --- ipld/merkledag/coding.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 76763ec42..7b256460a 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -71,3 +71,9 @@ func (n *Node) Encoded(force bool) ([]byte, error) { return n.encoded, nil } + +func Decoded(encoded []byte) (*Node, error) { + n := &Node{} + err := n.Unmarshal(encoded) + return n, err +} From e277c6eb38370044d2a210fb90cbe936bc4f9a2b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 5 Jul 2014 23:45:35 -0700 Subject: [PATCH 0012/3817] abstracted merkledag service This commit was moved from ipfs/go-merkledag@55f6c9ab05bc910980440ba8f11a635f09e1f80b --- ipld/merkledag/merkledag.go | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 9529bc543..77ca2c173 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -1,6 +1,8 @@ package merkledag import ( + "fmt" + blocks "github.com/jbenet/go-ipfs/blocks" u "github.com/jbenet/go-ipfs/util" mh "github.com/jbenet/go-multihash" ) @@ -79,3 +81,41 @@ func (n *Node) Key() (u.Key, error) { h, err := n.Multihash() return u.Key(h), err } + +// An IPFS Merkle DAG service. +// the root is virtual (like a forest) +// stores nodes' data in a blockService +type DAGService struct { + Blocks *blocks.BlockService +} + +func (n *DAGService) Put(nd *Node) (u.Key, error) { + if n == nil { + return "", fmt.Errorf("DAGService is nil") + } + + d, err := nd.Encoded(false) + if err != nil { + return "", err + } + + b, err := blocks.NewBlock(d) + if err != nil { + return "", err + } + + return n.Blocks.AddBlock(b) +} + +func (n *DAGService) Get(k u.Key) (*Node, error) { + if n == nil { + return nil, fmt.Errorf("DAGService is nil") + } + + b, err := n.Blocks.GetBlock(k) + if err != nil { + return nil, err + } + + return Decoded(b.Data) +} From a0ab31b2da2f985ecea888753850cc256102122b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 6 Jul 2014 00:07:04 -0700 Subject: [PATCH 0013/3817] added path resolution This commit was moved from ipfs/go-path@a628e6e95aa290642e0580ae87e58b5f5ad13e7a --- path/path.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/path/path.go b/path/path.go index e69de29bb..4eb7a3288 100644 --- a/path/path.go +++ b/path/path.go @@ -0,0 +1,77 @@ +package path + +import ( + "fmt" + merkledag "github.com/jbenet/go-ipfs/merkledag" + u "github.com/jbenet/go-ipfs/util" + mh "github.com/jbenet/go-multihash" + "path" + "strings" +) + +// Path resolution for IPFS + +type Resolver struct { + DAG *merkledag.DAGService +} + +func (s *Resolver) ResolvePath(fpath string) (*merkledag.Node, error) { + fpath = path.Clean(fpath) + + parts := strings.Split(fpath, "/") + + // skip over empty first elem + if len(parts[0]) == 0 { + parts = parts[1:] + } + + // if nothing, bail. + if len(parts) == 0 { + return nil, fmt.Errorf("ipfs path must contain at least one component.") + } + + // first element in the path is a b58 hash (for now) + h, err := mh.FromB58String(parts[0]) + if err != nil { + return nil, err + } + + nd, err := s.DAG.Get(u.Key(h)) + if err != nil { + return nil, err + } + + return s.ResolveLinks(nd, parts[1:]) +} + +func (s *Resolver) ResolveLinks(ndd *merkledag.Node, names []string) ( + nd *merkledag.Node, err error) { + + nd = ndd // dup arg workaround + + // for each of the path components + for _, name := range names { + + var next u.Key + // for each of the links in nd, the current object + for _, link := range nd.Links { + if link.Name == name { + next = u.Key(link.Hash) + break + } + } + + if next == "" { + h1, _ := nd.Multihash() + h2 := h1.B58String() + return nil, fmt.Errorf("no link named \"%s\" under %s", name, h2) + } + + // fetch object for link and assign to nd + nd, err = s.DAG.Get(next) + if err != nil { + return nd, err + } + } + return +} From 86898044bfad45b1e4f65225a891f7aa1975f648 Mon Sep 17 00:00:00 2001 From: Quinn Slack Date: Sun, 20 Jul 2014 23:18:05 -0700 Subject: [PATCH 0014/3817] add comment '//' before note so that package routing compiles This commit was moved from ipfs/go-ipfs-routing@dbe541a98afda0a49961083a021f6bd74544f12f --- routing/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/routing.go b/routing/routing.go index 2ab572c9a..7ff550539 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -1,4 +1,4 @@ package routing -TODO SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-routing/index.js +// TODO SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-routing/index.js From 7e2c357649b27b842184ba319e7234ca30c55cb7 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 21 Jul 2014 00:09:21 -0700 Subject: [PATCH 0015/3817] using %q This commit was moved from ipfs/go-path@b38521adef60a22850ca3fabbb04ae567aa2bb11 --- path/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/path.go b/path/path.go index 4eb7a3288..47d0346b7 100644 --- a/path/path.go +++ b/path/path.go @@ -64,7 +64,7 @@ func (s *Resolver) ResolveLinks(ndd *merkledag.Node, names []string) ( if next == "" { h1, _ := nd.Multihash() h2 := h1.B58String() - return nil, fmt.Errorf("no link named \"%s\" under %s", name, h2) + return nil, fmt.Errorf("no link named %q under %s", name, h2) } // fetch object for link and assign to nd From 9b4823daaa02fb41e6fd250c8e6554298e690e6c Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 21 Jul 2014 08:05:27 -0700 Subject: [PATCH 0016/3817] go lint link errors left: - protocol buffers output is not lint-friendly This commit was moved from ipfs/go-merkledag@b069916d519397c362c8391ca97c991adf048c4a --- ipld/merkledag/coding.go | 9 +++++++++ ipld/merkledag/merkledag.go | 22 +++++++++++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 7b256460a..d87f134f6 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -8,6 +8,8 @@ import ( // for now, we use a PBNode intermediate thing. // because native go objects are nice. +// Unmarshal decodes raw data into a *Node instance. +// The conversion uses an intermediate PBNode. func (n *Node) Unmarshal(encoded []byte) error { var pbn PBNode if err := pbn.Unmarshal(encoded); err != nil { @@ -29,6 +31,8 @@ func (n *Node) Unmarshal(encoded []byte) error { return nil } +// MarshalTo encodes a *Node instance into a given byte slice. +// The conversion uses an intermediate PBNode. func (n *Node) MarshalTo(encoded []byte) error { pbn := n.getPBNode() if _, err := pbn.MarshalTo(encoded); err != nil { @@ -37,6 +41,8 @@ func (n *Node) MarshalTo(encoded []byte) error { return nil } +// Marshal encodes a *Node instance into a new byte slice. +// The conversion uses an intermediate PBNode. func (n *Node) Marshal() ([]byte, error) { pbn := n.getPBNode() data, err := pbn.Marshal() @@ -60,6 +66,8 @@ func (n *Node) getPBNode() *PBNode { return pbn } +// Encoded returns the encoded raw data version of a Node instance. +// It may use a cached encoded version, unless the force flag is given. func (n *Node) Encoded(force bool) ([]byte, error) { if n.encoded == nil || force { var err error @@ -72,6 +80,7 @@ func (n *Node) Encoded(force bool) ([]byte, error) { return n.encoded, nil } +// Decoded decodes raw data and returns a new Node instance. func Decoded(encoded []byte) (*Node, error) { n := &Node{} err := n.Unmarshal(encoded) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 77ca2c173..251ea0bee 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -7,11 +7,12 @@ import ( mh "github.com/jbenet/go-multihash" ) -// can't use []byte/Multihash for keys :( -// so have to convert Multihash bytes to string +// NodeMap maps u.Keys to Nodes. +// We cannot use []byte/Multihash for keys :( +// so have to convert Multihash bytes to string (u.Key) type NodeMap map[u.Key]*Node -// A node in the IPFS Merkle DAG. +// Node represents a node in the IPFS Merkle DAG. // nodes have opaque data and a set of navigable links. type Node struct { Links []*Link @@ -21,7 +22,7 @@ type Node struct { encoded []byte } -// An IPFS Merkle DAG Link +// Link represents an IPFS Merkle DAG Link between Nodes. type Link struct { // utf string name. should be unique per object Name string // utf8 @@ -36,6 +37,7 @@ type Link struct { Node *Node } +// AddNodeLink adds a link to another node. func (n *Node) AddNodeLink(name string, that *Node) error { s, err := that.Size() if err != nil { @@ -55,6 +57,8 @@ func (n *Node) AddNodeLink(name string, that *Node) error { return nil } +// Size returns the total size of the data addressed by node, +// including the total sizes of references. func (n *Node) Size() (uint64, error) { b, err := n.Encoded(false) if err != nil { @@ -68,6 +72,7 @@ func (n *Node) Size() (uint64, error) { return s, nil } +// Multihash hashes the encoded data of this node. func (n *Node) Multihash() (mh.Multihash, error) { b, err := n.Encoded(false) if err != nil { @@ -77,18 +82,20 @@ func (n *Node) Multihash() (mh.Multihash, error) { return u.Hash(b) } +// Key returns the Multihash as a key, for maps. func (n *Node) Key() (u.Key, error) { h, err := n.Multihash() return u.Key(h), err } -// An IPFS Merkle DAG service. -// the root is virtual (like a forest) -// stores nodes' data in a blockService +// DAGService is an IPFS Merkle DAG service. +// - the root is virtual (like a forest) +// - stores nodes' data in a BlockService type DAGService struct { Blocks *blocks.BlockService } +// Put adds a node to the DAGService, storing the block in the BlockService func (n *DAGService) Put(nd *Node) (u.Key, error) { if n == nil { return "", fmt.Errorf("DAGService is nil") @@ -107,6 +114,7 @@ func (n *DAGService) Put(nd *Node) (u.Key, error) { return n.Blocks.AddBlock(b) } +// Get retrieves a node from the DAGService, fetching the block in the BlockService func (n *DAGService) Get(k u.Key) (*Node, error) { if n == nil { return nil, fmt.Errorf("DAGService is nil") From c9f1432ed51dd53e6f90bfd88c34e0d0a1a1e659 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 21 Jul 2014 08:05:27 -0700 Subject: [PATCH 0017/3817] go lint link errors left: - protocol buffers output is not lint-friendly This commit was moved from ipfs/go-path@566301d4da337d012b7e77fdf85976e0cec441c9 --- path/path.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/path/path.go b/path/path.go index 47d0346b7..3f1c3997e 100644 --- a/path/path.go +++ b/path/path.go @@ -9,12 +9,15 @@ import ( "strings" ) -// Path resolution for IPFS - +// Resolver provides path resolution to IPFS +// It has a pointer to a DAGService, which is uses to resolve nodes. type Resolver struct { DAG *merkledag.DAGService } +// ResolvePath fetches the node for given path. It uses the first +// path component as a hash (key) of the first node, then resolves +// all other components walking the links, with ResolveLinks. func (s *Resolver) ResolvePath(fpath string) (*merkledag.Node, error) { fpath = path.Clean(fpath) @@ -27,7 +30,7 @@ func (s *Resolver) ResolvePath(fpath string) (*merkledag.Node, error) { // if nothing, bail. if len(parts) == 0 { - return nil, fmt.Errorf("ipfs path must contain at least one component.") + return nil, fmt.Errorf("ipfs path must contain at least one component") } // first element in the path is a b58 hash (for now) @@ -44,6 +47,12 @@ func (s *Resolver) ResolvePath(fpath string) (*merkledag.Node, error) { return s.ResolveLinks(nd, parts[1:]) } +// ResolveLinks iteratively resolves names by walking the link hierarchy. +// Every node is fetched from the DAGService, resolving the next name. +// Returns the last node found. +// +// ResolveLinks(nd, []string{"foo", "bar", "baz"}) +// would retrieve "baz" in ("bar" in ("foo" in nd.Links).Links).Links func (s *Resolver) ResolveLinks(ndd *merkledag.Node, names []string) ( nd *merkledag.Node, err error) { From 8f5f39282973ab8c2e179471fffe5b083e9afa22 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 22 Jul 2014 03:11:08 -0700 Subject: [PATCH 0018/3817] routing interface This commit was moved from ipfs/go-ipfs-routing@09a3b4b23e0dbe78ce636e25d7b4488b4d7270c3 --- routing/routing.go | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/routing/routing.go b/routing/routing.go index 7ff550539..4a9240181 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -1,4 +1,36 @@ package routing +import ( + "time" + peer "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" +) -// TODO SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-routing/index.js +// IpfsRouting is the routing module interface +// It is implemented by things like DHTs, etc. +type IpfsRouting interface { + + // Basic Put/Get + + // PutValue adds value corresponding to given Key. + PutValue(key u.Key, value []byte) (error) + + // GetValue searches for the value corresponding to given Key. + GetValue(key u.Key, timeout time.Duration) ([]byte, error) + + + // Value provider layer of indirection. + // This is what DSHTs (Coral and MainlineDHT) do to store large values in a DHT. + + // Announce that this node can provide value for given key + Provide(key u.Key) (error) + + // FindProviders searches for peers who can provide the value for given key. + FindProviders(key u.Key, timeout time.Duration) (*peer.Peer, error) + + + // Find specific Peer + + // FindPeer searches for a peer with given ID. + FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) +} From a01cfb7278b6a9239443745bca3cdb7cb438337f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 23 Jul 2014 04:48:30 -0700 Subject: [PATCH 0019/3817] dht interface beginnings This commit was moved from ipfs/go-ipfs-routing@d2022da92512613c2dc09e0b1c3980aa79a61463 --- routing/dht/dht.go | 10 +++++++ routing/dht/routing.go | 44 ++++++++++++++++++++++++++++++ routing/dht/table.go | 61 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 routing/dht/dht.go create mode 100644 routing/dht/routing.go create mode 100644 routing/dht/table.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go new file mode 100644 index 000000000..e8f475a9e --- /dev/null +++ b/routing/dht/dht.go @@ -0,0 +1,10 @@ +package dht + +// TODO. SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-dht/index.js + + +// IpfsDHT is an implementation of Kademlia with Coral and S/Kademlia modifications. +// It is used to implement the base IpfsRouting module. +type IpfsDHT struct { + routes RoutingTable +} diff --git a/routing/dht/routing.go b/routing/dht/routing.go new file mode 100644 index 000000000..575f0a1bf --- /dev/null +++ b/routing/dht/routing.go @@ -0,0 +1,44 @@ +package dht + +import ( + "time" + peer "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" +) + + +// This file implements the Routing interface for the IpfsDHT struct. + +// Basic Put/Get + +// PutValue adds value corresponding to given Key. +func (s *IpfsDHT) PutValue(key u.Key, value []byte) (error) { + return u.ErrNotImplemented +} + +// GetValue searches for the value corresponding to given Key. +func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { + return nil, u.ErrNotImplemented +} + + +// Value provider layer of indirection. +// This is what DSHTs (Coral and MainlineDHT) do to store large values in a DHT. + +// Announce that this node can provide value for given key +func (s *IpfsDHT) Provide(key u.Key) (error) { + return u.ErrNotImplemented +} + +// FindProviders searches for peers who can provide the value for given key. +func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) (*peer.Peer, error) { + return nil, u.ErrNotImplemented +} + + +// Find specific Peer + +// FindPeer searches for a peer with given ID. +func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) { + return nil, u.ErrNotImplemented +} diff --git a/routing/dht/table.go b/routing/dht/table.go new file mode 100644 index 000000000..5ac514e04 --- /dev/null +++ b/routing/dht/table.go @@ -0,0 +1,61 @@ +package dht + +import ( + "container/list" +) + + +// ID for IpfsDHT should be a byte slice, to allow for simpler operations +// (xor). DHT ids are based on the peer.IDs. +// +// NOTE: peer.IDs are biased because they are (a) multihashes (first bytes +// biased), and (b) first bits are zeroes when using the S/Kademlia PoW. +// Thus, may need to re-hash keys (uniform dist). TODO(jbenet) +type ID []byte + +// Bucket holds a list of peers. +type Bucket []*list.List + + +// RoutingTable defines the routing table. +type RoutingTable struct { + + // kBuckets define all the fingers to other nodes. + Buckets []Bucket +} + + +func (id ID) commonPrefixLen() int { + for i := 0; i < len(id); i++ { + for j := 0; j < 8; j++ { + if (id[i] >> uint8(7 - j)) & 0x1 != 0 { + return i * 8 + j; + } + } + } + return len(id) * 8 - 1; +} + +func xor(a, b ID) ID { + + // ids may actually be of different sizes. + var ba ID + var bb ID + if len(a) >= len(b) { + ba = a + bb = b + } else { + ba = b + bb = a + } + + c := make(ID, len(ba)) + for i := 0; i < len(ba); i++ { + if len(bb) > i { + c[i] = ba[i] ^ bb[i] + } else { + c[i] = ba[i] ^ 0 + } + } + return c +} From 3130079c48b7575a0dd256d241d029af7de9e60e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Jul 2014 22:14:27 -0700 Subject: [PATCH 0020/3817] working on upper level dht implementations, protbuf, etc This commit was moved from ipfs/go-ipfs-routing@33e27d5a58152295cbd8ee16742654e011ab4ee1 --- routing/dht/dht.go | 12 +++++ routing/dht/messages.pb.go | 96 ++++++++++++++++++++++++++++++++++++++ routing/dht/messages.proto | 16 +++++++ routing/dht/routing.go | 36 +++++++++++++- 4 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 routing/dht/messages.pb.go create mode 100644 routing/dht/messages.proto diff --git a/routing/dht/dht.go b/routing/dht/dht.go index e8f475a9e..16a10c9e1 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -1,5 +1,9 @@ package dht +import ( + swarm "github.com/jbenet/go-ipfs/swarm" +) + // TODO. SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-dht/index.js @@ -7,4 +11,12 @@ package dht // It is used to implement the base IpfsRouting module. type IpfsDHT struct { routes RoutingTable + + network *swarm.Swarm +} + +func (dht *IpfsDHT) handleMessages() { + for mes := range dht.network.Chan.Incoming { + + } } diff --git a/routing/dht/messages.pb.go b/routing/dht/messages.pb.go new file mode 100644 index 000000000..718f86b92 --- /dev/null +++ b/routing/dht/messages.pb.go @@ -0,0 +1,96 @@ +// Code generated by protoc-gen-go. +// source: messages.proto +// DO NOT EDIT! + +/* +Package dht is a generated protocol buffer package. + +It is generated from these files: + messages.proto + +It has these top-level messages: + DHTMessage +*/ +package dht + +import proto "code.google.com/p/goprotobuf/proto" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = math.Inf + +type DHTMessage_MessageType int32 + +const ( + DHTMessage_PUT_VALUE DHTMessage_MessageType = 0 + DHTMessage_GET_VALUE DHTMessage_MessageType = 1 + DHTMessage_PING DHTMessage_MessageType = 2 + DHTMessage_FIND_NODE DHTMessage_MessageType = 3 +) + +var DHTMessage_MessageType_name = map[int32]string{ + 0: "PUT_VALUE", + 1: "GET_VALUE", + 2: "PING", + 3: "FIND_NODE", +} +var DHTMessage_MessageType_value = map[string]int32{ + "PUT_VALUE": 0, + "GET_VALUE": 1, + "PING": 2, + "FIND_NODE": 3, +} + +func (x DHTMessage_MessageType) Enum() *DHTMessage_MessageType { + p := new(DHTMessage_MessageType) + *p = x + return p +} +func (x DHTMessage_MessageType) String() string { + return proto.EnumName(DHTMessage_MessageType_name, int32(x)) +} +func (x *DHTMessage_MessageType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(DHTMessage_MessageType_value, data, "DHTMessage_MessageType") + if err != nil { + return err + } + *x = DHTMessage_MessageType(value) + return nil +} + +type DHTMessage struct { + Type *DHTMessage_MessageType `protobuf:"varint,1,req,name=type,enum=dht.DHTMessage_MessageType" json:"type,omitempty"` + Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` + Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *DHTMessage) Reset() { *m = DHTMessage{} } +func (m *DHTMessage) String() string { return proto.CompactTextString(m) } +func (*DHTMessage) ProtoMessage() {} + +func (m *DHTMessage) GetType() DHTMessage_MessageType { + if m != nil && m.Type != nil { + return *m.Type + } + return DHTMessage_PUT_VALUE +} + +func (m *DHTMessage) GetKey() string { + if m != nil && m.Key != nil { + return *m.Key + } + return "" +} + +func (m *DHTMessage) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func init() { + proto.RegisterEnum("dht.DHTMessage_MessageType", DHTMessage_MessageType_name, DHTMessage_MessageType_value) +} diff --git a/routing/dht/messages.proto b/routing/dht/messages.proto new file mode 100644 index 000000000..458b80fb6 --- /dev/null +++ b/routing/dht/messages.proto @@ -0,0 +1,16 @@ +package dht; + +//run `protoc --go_out=. *.proto` to generate + +message DHTMessage { + enum MessageType { + PUT_VALUE = 0; + GET_VALUE = 1; + PING = 2; + FIND_NODE = 3; + } + + required MessageType type = 1; + optional string key = 2; + optional bytes value = 3; +} diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 575f0a1bf..0242399dd 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -4,6 +4,7 @@ import ( "time" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" + swarm "github.com/jbenet/go-ipfs/swarm" ) @@ -13,12 +14,43 @@ import ( // PutValue adds value corresponding to given Key. func (s *IpfsDHT) PutValue(key u.Key, value []byte) (error) { - return u.ErrNotImplemented + var p *peer.Peer + p = s.routes.NearestNode(key) + + pmes := new(PutValue) + pmes.Key = &key + pmes.Value = value + + mes := new(swarm.Message) + mes.Data = []byte(pmes.String()) + mes.Peer = p + + s.network.Chan.Outgoing <- mes + return nil } // GetValue searches for the value corresponding to given Key. func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { - return nil, u.ErrNotImplemented + var p *peer.Peer + p = s.routes.NearestNode(key) + + // protobuf structure + pmes := new(GetValue) + pmes.Key = &key + pmes.Id = GenerateMessageID() + + mes := new(swarm.Message) + mes.Data = []byte(pmes.String()) + mes.Peer = p + + response_chan := s.network.ListenFor(pmes.Id) + + timeup := time.After(timeout) + select { + case <-timeup: + return nil, timeoutError + case resp := <-response_chan: + } } From 73c1ebcb5761f09e39bff5ca0eb49f9bb7f8c789 Mon Sep 17 00:00:00 2001 From: Jeromy Johnson Date: Tue, 29 Jul 2014 14:50:33 -0700 Subject: [PATCH 0021/3817] update messages and add some new code around handling/creating messages This commit was moved from ipfs/go-ipfs-routing@43c955316609ebe79c75a66523f5ad9f14495b8f --- routing/dht/dht.go | 34 ++++++++++++++++++++++++++++++++-- routing/dht/messages.proto | 7 +++++-- routing/dht/routing.go | 8 +++++++- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 16a10c9e1..21f054e69 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -2,6 +2,7 @@ package dht import ( swarm "github.com/jbenet/go-ipfs/swarm" + "sync" ) // TODO. SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-dht/index.js @@ -10,13 +11,42 @@ import ( // IpfsDHT is an implementation of Kademlia with Coral and S/Kademlia modifications. // It is used to implement the base IpfsRouting module. type IpfsDHT struct { - routes RoutingTable + routes RoutingTable - network *swarm.Swarm + network *swarm.Swarm + + listeners map[uint64]chan swarm.Message + listenLock sync.RWMutex } +// Read in all messages from swarm and handle them appropriately +// NOTE: this function is just a quick sketch func (dht *IpfsDHT) handleMessages() { for mes := range dht.network.Chan.Incoming { + for { + select { + case mes := <-dht.network.Chan.Incoming: + // Unmarshal message + dht.listenLock.RLock() + ch, ok := dht.listeners[id] + dht.listenLock.RUnlock() + if ok { + // Send message to waiting goroutine + ch <- mes + } + //case closeChan: or something + } + } } } + +// Register a handler for a specific message ID, used for getting replies +// to certain messages (i.e. response to a GET_VALUE message) +func (dht *IpfsDHT) ListenFor(mesid uint64) <-chan swarm.Message { + lchan := make(chan swarm.Message) + dht.listenLock.Lock() + dht.listeners[mesid] = lchan + dht.listenLock.Unlock() + return lchan +} diff --git a/routing/dht/messages.proto b/routing/dht/messages.proto index 458b80fb6..37024037b 100644 --- a/routing/dht/messages.proto +++ b/routing/dht/messages.proto @@ -6,11 +6,14 @@ message DHTMessage { enum MessageType { PUT_VALUE = 0; GET_VALUE = 1; - PING = 2; - FIND_NODE = 3; + ADD_PROVIDER = 2; + GET_PROVIDERS = 3; + FIND_NODE = 4; + PING = 5; } required MessageType type = 1; optional string key = 2; optional bytes value = 3; + required int64 id = 4; } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 0242399dd..bcbdec1da 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -43,14 +43,20 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { mes.Data = []byte(pmes.String()) mes.Peer = p - response_chan := s.network.ListenFor(pmes.Id) + response_chan := s.ListenFor(pmes.Id) + // Wait for either the response or a timeout timeup := time.After(timeout) select { case <-timeup: + // TODO: unregister listener return nil, timeoutError case resp := <-response_chan: + return resp.Data, nil } + + // Should never be hit + return nil, nil } From eabf71db11a4f339d7c84b85f1c4f6ede97fef8c Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 29 Jul 2014 17:53:31 -0700 Subject: [PATCH 0022/3817] equalize sizes This commit was moved from ipfs/go-ipfs-routing@543158eca63e736c0ed715ec2abe2cb91d7bb619 --- routing/dht/table.go | 86 +++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/routing/dht/table.go b/routing/dht/table.go index 5ac514e04..53fd0f0d1 100644 --- a/routing/dht/table.go +++ b/routing/dht/table.go @@ -1,61 +1,75 @@ package dht import ( - "container/list" + "bytes" + "container/list" ) - // ID for IpfsDHT should be a byte slice, to allow for simpler operations // (xor). DHT ids are based on the peer.IDs. // -// NOTE: peer.IDs are biased because they are (a) multihashes (first bytes -// biased), and (b) first bits are zeroes when using the S/Kademlia PoW. -// Thus, may need to re-hash keys (uniform dist). TODO(jbenet) +// NOTE: peer.IDs are biased because they are multihashes (first bytes +// biased). Thus, may need to re-hash keys (uniform dist). TODO(jbenet) type ID []byte // Bucket holds a list of peers. type Bucket []*list.List - // RoutingTable defines the routing table. type RoutingTable struct { - // kBuckets define all the fingers to other nodes. - Buckets []Bucket + // kBuckets define all the fingers to other nodes. + Buckets []Bucket } +func (id ID) Equal(other ID) bool { + return bytes.Equal(id, other) +} + +func (id ID) Less(other interface{}) bool { + a, b := equalizeSizes(id, other.(ID)) + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return a[i] < b[i] + } + } + return len(a) < len(b) +} func (id ID) commonPrefixLen() int { - for i := 0; i < len(id); i++ { - for j := 0; j < 8; j++ { - if (id[i] >> uint8(7 - j)) & 0x1 != 0 { - return i * 8 + j; - } - } - } - return len(id) * 8 - 1; + for i := 0; i < len(id); i++ { + for j := 0; j < 8; j++ { + if (id[i]>>uint8(7-j))&0x1 != 0 { + return i*8 + j + } + } + } + return len(id)*8 - 1 } func xor(a, b ID) ID { + a, b = equalizeSizes(a, b) + + c := make(ID, len(a)) + for i := 0; i < len(a); i++ { + c[i] = a[i] ^ b[i] + } + return c +} + +func equalizeSizes(a, b ID) (ID, ID) { + la := len(a) + lb := len(b) + + if la < lb { + na := make([]byte, lb) + copy(na, a) + a = na + } else if lb < la { + nb := make([]byte, la) + copy(nb, b) + b = nb + } - // ids may actually be of different sizes. - var ba ID - var bb ID - if len(a) >= len(b) { - ba = a - bb = b - } else { - ba = b - bb = a - } - - c := make(ID, len(ba)) - for i := 0; i < len(ba); i++ { - if len(bb) > i { - c[i] = ba[i] ^ bb[i] - } else { - c[i] = ba[i] ^ 0 - } - } - return c + return a, b } From f8da9f24e4fe015163389b60353ba54d74e66045 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 29 Jul 2014 17:55:19 -0700 Subject: [PATCH 0023/3817] whole project go fmt This commit was moved from ipfs/go-ipfs-routing@cb492e66606565bfc4841a207e0e7a7eae853a58 --- routing/dht/dht.go | 5 ++--- routing/dht/routing.go | 31 ++++++++++++++----------------- routing/routing.go | 36 +++++++++++++++++------------------- 3 files changed, 33 insertions(+), 39 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 21f054e69..c7e6f3c2c 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -7,7 +7,6 @@ import ( // TODO. SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-dht/index.js - // IpfsDHT is an implementation of Kademlia with Coral and S/Kademlia modifications. // It is used to implement the base IpfsRouting module. type IpfsDHT struct { @@ -15,7 +14,7 @@ type IpfsDHT struct { network *swarm.Swarm - listeners map[uint64]chan swarm.Message + listeners map[uint64]chan swarm.Message listenLock sync.RWMutex } @@ -35,7 +34,7 @@ func (dht *IpfsDHT) handleMessages() { ch <- mes } - //case closeChan: or something + //case closeChan: or something } } } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index bcbdec1da..2bbe93e32 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -1,19 +1,18 @@ package dht import ( - "time" - peer "github.com/jbenet/go-ipfs/peer" - u "github.com/jbenet/go-ipfs/util" - swarm "github.com/jbenet/go-ipfs/swarm" + peer "github.com/jbenet/go-ipfs/peer" + swarm "github.com/jbenet/go-ipfs/swarm" + u "github.com/jbenet/go-ipfs/util" + "time" ) - // This file implements the Routing interface for the IpfsDHT struct. // Basic Put/Get // PutValue adds value corresponding to given Key. -func (s *IpfsDHT) PutValue(key u.Key, value []byte) (error) { +func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { var p *peer.Peer p = s.routes.NearestNode(key) @@ -48,35 +47,33 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { // Wait for either the response or a timeout timeup := time.After(timeout) select { - case <-timeup: - // TODO: unregister listener - return nil, timeoutError - case resp := <-response_chan: - return resp.Data, nil + case <-timeup: + // TODO: unregister listener + return nil, timeoutError + case resp := <-response_chan: + return resp.Data, nil } // Should never be hit return nil, nil } - // Value provider layer of indirection. // This is what DSHTs (Coral and MainlineDHT) do to store large values in a DHT. // Announce that this node can provide value for given key -func (s *IpfsDHT) Provide(key u.Key) (error) { - return u.ErrNotImplemented +func (s *IpfsDHT) Provide(key u.Key) error { + return u.ErrNotImplemented } // FindProviders searches for peers who can provide the value for given key. func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) (*peer.Peer, error) { - return nil, u.ErrNotImplemented + return nil, u.ErrNotImplemented } - // Find specific Peer // FindPeer searches for a peer with given ID. func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) { - return nil, u.ErrNotImplemented + return nil, u.ErrNotImplemented } diff --git a/routing/routing.go b/routing/routing.go index 4a9240181..933032f46 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -1,36 +1,34 @@ package routing import ( - "time" - peer "github.com/jbenet/go-ipfs/peer" - u "github.com/jbenet/go-ipfs/util" + peer "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" + "time" ) // IpfsRouting is the routing module interface // It is implemented by things like DHTs, etc. type IpfsRouting interface { - // Basic Put/Get + // Basic Put/Get - // PutValue adds value corresponding to given Key. - PutValue(key u.Key, value []byte) (error) + // PutValue adds value corresponding to given Key. + PutValue(key u.Key, value []byte) error - // GetValue searches for the value corresponding to given Key. - GetValue(key u.Key, timeout time.Duration) ([]byte, error) + // GetValue searches for the value corresponding to given Key. + GetValue(key u.Key, timeout time.Duration) ([]byte, error) + // Value provider layer of indirection. + // This is what DSHTs (Coral and MainlineDHT) do to store large values in a DHT. - // Value provider layer of indirection. - // This is what DSHTs (Coral and MainlineDHT) do to store large values in a DHT. + // Announce that this node can provide value for given key + Provide(key u.Key) error - // Announce that this node can provide value for given key - Provide(key u.Key) (error) + // FindProviders searches for peers who can provide the value for given key. + FindProviders(key u.Key, timeout time.Duration) (*peer.Peer, error) - // FindProviders searches for peers who can provide the value for given key. - FindProviders(key u.Key, timeout time.Duration) (*peer.Peer, error) + // Find specific Peer - - // Find specific Peer - - // FindPeer searches for a peer with given ID. - FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) + // FindPeer searches for a peer with given ID. + FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) } From dafcb28099687af90a691f0aad1cdbd7f26dbf1e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 29 Jul 2014 19:33:51 -0700 Subject: [PATCH 0024/3817] work on framework for dht message handling This commit was moved from ipfs/go-ipfs-routing@3a85f36352cb48c9033ba18a8213e291fe70beb3 --- routing/dht/dht.go | 56 ++++++++++++++++++++++++++------------ routing/dht/messages.pb.go | 34 ++++++++++++++++------- routing/dht/messages.proto | 2 +- routing/dht/routing.go | 29 +++++++++++++++----- routing/dht/table.go | 8 ++++++ 5 files changed, 94 insertions(+), 35 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index c7e6f3c2c..e5d7e9422 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -2,6 +2,8 @@ package dht import ( swarm "github.com/jbenet/go-ipfs/swarm" + u "github.com/jbenet/go-ipfs/util" + "code.google.com/p/goprotobuf/proto" "sync" ) @@ -14,36 +16,56 @@ type IpfsDHT struct { network *swarm.Swarm - listeners map[uint64]chan swarm.Message + // map of channels waiting for reply messages + listeners map[uint64]chan *swarm.Message listenLock sync.RWMutex + + // Signal to shutdown dht + shutdown chan struct{} } // Read in all messages from swarm and handle them appropriately // NOTE: this function is just a quick sketch func (dht *IpfsDHT) handleMessages() { - for mes := range dht.network.Chan.Incoming { - for { - select { - case mes := <-dht.network.Chan.Incoming: - // Unmarshal message - dht.listenLock.RLock() - ch, ok := dht.listeners[id] - dht.listenLock.RUnlock() - if ok { - // Send message to waiting goroutine - ch <- mes - } - - //case closeChan: or something + for { + select { + case mes := <-dht.network.Chan.Incoming: + pmes := new(DHTMessage) + err := proto.Unmarshal(mes.Data, pmes) + if err != nil { + u.PErr("Failed to decode protobuf message: %s", err) + continue + } + + // Note: not sure if this is the correct place for this + dht.listenLock.RLock() + ch, ok := dht.listeners[pmes.GetId()] + dht.listenLock.RUnlock() + if ok { + ch <- mes } + // + + // Do something else with the messages? + switch pmes.GetType() { + case DHTMessage_ADD_PROVIDER: + case DHTMessage_FIND_NODE: + case DHTMessage_GET_PROVIDERS: + case DHTMessage_GET_VALUE: + case DHTMessage_PING: + case DHTMessage_PUT_VALUE: + } + + case <-dht.shutdown: + return } } } // Register a handler for a specific message ID, used for getting replies // to certain messages (i.e. response to a GET_VALUE message) -func (dht *IpfsDHT) ListenFor(mesid uint64) <-chan swarm.Message { - lchan := make(chan swarm.Message) +func (dht *IpfsDHT) ListenFor(mesid uint64) <-chan *swarm.Message { + lchan := make(chan *swarm.Message) dht.listenLock.Lock() dht.listeners[mesid] = lchan dht.listenLock.Unlock() diff --git a/routing/dht/messages.pb.go b/routing/dht/messages.pb.go index 718f86b92..f4fcb8dfd 100644 --- a/routing/dht/messages.pb.go +++ b/routing/dht/messages.pb.go @@ -23,23 +23,29 @@ var _ = math.Inf type DHTMessage_MessageType int32 const ( - DHTMessage_PUT_VALUE DHTMessage_MessageType = 0 - DHTMessage_GET_VALUE DHTMessage_MessageType = 1 - DHTMessage_PING DHTMessage_MessageType = 2 - DHTMessage_FIND_NODE DHTMessage_MessageType = 3 + DHTMessage_PUT_VALUE DHTMessage_MessageType = 0 + DHTMessage_GET_VALUE DHTMessage_MessageType = 1 + DHTMessage_ADD_PROVIDER DHTMessage_MessageType = 2 + DHTMessage_GET_PROVIDERS DHTMessage_MessageType = 3 + DHTMessage_FIND_NODE DHTMessage_MessageType = 4 + DHTMessage_PING DHTMessage_MessageType = 5 ) var DHTMessage_MessageType_name = map[int32]string{ 0: "PUT_VALUE", 1: "GET_VALUE", - 2: "PING", - 3: "FIND_NODE", + 2: "ADD_PROVIDER", + 3: "GET_PROVIDERS", + 4: "FIND_NODE", + 5: "PING", } var DHTMessage_MessageType_value = map[string]int32{ - "PUT_VALUE": 0, - "GET_VALUE": 1, - "PING": 2, - "FIND_NODE": 3, + "PUT_VALUE": 0, + "GET_VALUE": 1, + "ADD_PROVIDER": 2, + "GET_PROVIDERS": 3, + "FIND_NODE": 4, + "PING": 5, } func (x DHTMessage_MessageType) Enum() *DHTMessage_MessageType { @@ -63,6 +69,7 @@ type DHTMessage struct { Type *DHTMessage_MessageType `protobuf:"varint,1,req,name=type,enum=dht.DHTMessage_MessageType" json:"type,omitempty"` Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` + Id *uint64 `protobuf:"varint,4,req,name=id" json:"id,omitempty"` XXX_unrecognized []byte `json:"-"` } @@ -91,6 +98,13 @@ func (m *DHTMessage) GetValue() []byte { return nil } +func (m *DHTMessage) GetId() uint64 { + if m != nil && m.Id != nil { + return *m.Id + } + return 0 +} + func init() { proto.RegisterEnum("dht.DHTMessage_MessageType", DHTMessage_MessageType_name, DHTMessage_MessageType_value) } diff --git a/routing/dht/messages.proto b/routing/dht/messages.proto index 37024037b..67ffad447 100644 --- a/routing/dht/messages.proto +++ b/routing/dht/messages.proto @@ -15,5 +15,5 @@ message DHTMessage { required MessageType type = 1; optional string key = 2; optional bytes value = 3; - required int64 id = 4; + required uint64 id = 4; } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 2bbe93e32..dfdde4dd1 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -7,6 +7,11 @@ import ( "time" ) +// TODO: determine a way of creating and managing message IDs +func GenerateMessageID() uint64 { + return 4 +} + // This file implements the Routing interface for the IpfsDHT struct. // Basic Put/Get @@ -16,9 +21,15 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { var p *peer.Peer p = s.routes.NearestNode(key) - pmes := new(PutValue) - pmes.Key = &key + pmes_type := DHTMessage_PUT_VALUE + str_key := string(key) + mes_id := GenerateMessageID() + + pmes := new(DHTMessage) + pmes.Type = &pmes_type + pmes.Key = &str_key pmes.Value = value + pmes.Id = &mes_id mes := new(swarm.Message) mes.Data = []byte(pmes.String()) @@ -33,23 +44,27 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { var p *peer.Peer p = s.routes.NearestNode(key) + str_key := string(key) + mes_type := DHTMessage_GET_VALUE + mes_id := GenerateMessageID() // protobuf structure - pmes := new(GetValue) - pmes.Key = &key - pmes.Id = GenerateMessageID() + pmes := new(DHTMessage) + pmes.Type = &mes_type + pmes.Key = &str_key + pmes.Id = &mes_id mes := new(swarm.Message) mes.Data = []byte(pmes.String()) mes.Peer = p - response_chan := s.ListenFor(pmes.Id) + response_chan := s.ListenFor(*pmes.Id) // Wait for either the response or a timeout timeup := time.After(timeout) select { case <-timeup: // TODO: unregister listener - return nil, timeoutError + return nil, u.ErrTimeout case resp := <-response_chan: return resp.Data, nil } diff --git a/routing/dht/table.go b/routing/dht/table.go index 53fd0f0d1..d7625e462 100644 --- a/routing/dht/table.go +++ b/routing/dht/table.go @@ -3,6 +3,9 @@ package dht import ( "bytes" "container/list" + + peer "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" ) // ID for IpfsDHT should be a byte slice, to allow for simpler operations @@ -22,6 +25,11 @@ type RoutingTable struct { Buckets []Bucket } +//TODO: make this accept an ID, requires method of converting keys to IDs +func (rt *RoutingTable) NearestNode(key u.Key) *peer.Peer { + panic("Function not implemented.") +} + func (id ID) Equal(other ID) bool { return bytes.Equal(id, other) } From a4a8554bed87e7e5a84384b29393c09360a91a44 Mon Sep 17 00:00:00 2001 From: Jeromy Johnson Date: Wed, 30 Jul 2014 17:46:56 -0700 Subject: [PATCH 0025/3817] a little more work on message handling stuff This commit was moved from ipfs/go-ipfs-routing@4998a96231785f9b38c101eacbfe551ce1d946b8 --- routing/dht/dht.go | 75 +++++++++++++++++++++++++++++++++----- routing/dht/messages.pb.go | 20 +++++++--- routing/dht/messages.proto | 5 +++ routing/dht/routing.go | 9 ++--- 4 files changed, 89 insertions(+), 20 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index e5d7e9422..a1f63f106 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -1,10 +1,12 @@ package dht import ( + "sync" + + peer "github.com/jbenet/go-ipfs/peer" swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" "code.google.com/p/goprotobuf/proto" - "sync" ) // TODO. SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-dht/index.js @@ -16,6 +18,9 @@ type IpfsDHT struct { network *swarm.Swarm + // local data (TEMPORARY: until we formalize data storage with datastore) + data map[string][]byte + // map of channels waiting for reply messages listeners map[uint64]chan *swarm.Message listenLock sync.RWMutex @@ -38,22 +43,28 @@ func (dht *IpfsDHT) handleMessages() { } // Note: not sure if this is the correct place for this - dht.listenLock.RLock() - ch, ok := dht.listeners[pmes.GetId()] - dht.listenLock.RUnlock() - if ok { - ch <- mes + if pmes.GetResponse() { + dht.listenLock.RLock() + ch, ok := dht.listeners[pmes.GetId()] + dht.listenLock.RUnlock() + if ok { + ch <- mes + } + + // this is expected behaviour during a timeout + u.DOut("Received response with nobody listening...") + continue } // - // Do something else with the messages? switch pmes.GetType() { - case DHTMessage_ADD_PROVIDER: + case DHTMessage_GET_VALUE: + dht.handleGetValue(mes.Peer, pmes) + case DHTMessage_PUT_VALUE: case DHTMessage_FIND_NODE: + case DHTMessage_ADD_PROVIDER: case DHTMessage_GET_PROVIDERS: - case DHTMessage_GET_VALUE: case DHTMessage_PING: - case DHTMessage_PUT_VALUE: } case <-dht.shutdown: @@ -62,6 +73,44 @@ func (dht *IpfsDHT) handleMessages() { } } +func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *DHTMessage) { + val, found := dht.data[pmes.GetKey()] + if found { + isResponse := true + resp := new(DHTMessage) + resp.Response = &isResponse + resp.Id = pmes.Id + resp.Key = pmes.Key + resp.Value = val + } else { + // Find closest node(s) to desired key and reply with that info + // TODO: this will need some other metadata in the protobuf message + // to signal to the querying node that the data its receiving + // is actually a list of other nodes + } +} + +func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *DHTMessage) { + panic("Not implemented.") +} + +func (dht *IpfsDHT) handleFindNode(p *peer.Peer, pmes *DHTMessage) { + panic("Not implemented.") +} + +func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *DHTMessage) { + isResponse := true + resp := new(DHTMessage) + resp.Id = pmes.Id + resp.Response = &isResponse + + mes := new(swarm.Message) + mes.Peer = p + mes.Data = []byte(resp.String()) + dht.network.Chan.Outgoing <- mes +} + + // Register a handler for a specific message ID, used for getting replies // to certain messages (i.e. response to a GET_VALUE message) func (dht *IpfsDHT) ListenFor(mesid uint64) <-chan *swarm.Message { @@ -71,3 +120,9 @@ func (dht *IpfsDHT) ListenFor(mesid uint64) <-chan *swarm.Message { dht.listenLock.Unlock() return lchan } + +// Stop all communications from this node and shut down +func (dht *IpfsDHT) Halt() { + dht.shutdown <- struct{}{} + dht.network.Close() +} diff --git a/routing/dht/messages.pb.go b/routing/dht/messages.pb.go index f4fcb8dfd..3283ef4e2 100644 --- a/routing/dht/messages.pb.go +++ b/routing/dht/messages.pb.go @@ -66,11 +66,14 @@ func (x *DHTMessage_MessageType) UnmarshalJSON(data []byte) error { } type DHTMessage struct { - Type *DHTMessage_MessageType `protobuf:"varint,1,req,name=type,enum=dht.DHTMessage_MessageType" json:"type,omitempty"` - Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` - Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` - Id *uint64 `protobuf:"varint,4,req,name=id" json:"id,omitempty"` - XXX_unrecognized []byte `json:"-"` + Type *DHTMessage_MessageType `protobuf:"varint,1,req,name=type,enum=dht.DHTMessage_MessageType" json:"type,omitempty"` + Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` + Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` + // Unique ID of this message, used to match queries with responses + Id *uint64 `protobuf:"varint,4,req,name=id" json:"id,omitempty"` + // Signals whether or not this message is a response to another message + Response *bool `protobuf:"varint,5,opt,name=response" json:"response,omitempty"` + XXX_unrecognized []byte `json:"-"` } func (m *DHTMessage) Reset() { *m = DHTMessage{} } @@ -105,6 +108,13 @@ func (m *DHTMessage) GetId() uint64 { return 0 } +func (m *DHTMessage) GetResponse() bool { + if m != nil && m.Response != nil { + return *m.Response + } + return false +} + func init() { proto.RegisterEnum("dht.DHTMessage_MessageType", DHTMessage_MessageType_name, DHTMessage_MessageType_value) } diff --git a/routing/dht/messages.proto b/routing/dht/messages.proto index 67ffad447..d873c7559 100644 --- a/routing/dht/messages.proto +++ b/routing/dht/messages.proto @@ -15,5 +15,10 @@ message DHTMessage { required MessageType type = 1; optional string key = 2; optional bytes value = 3; + + // Unique ID of this message, used to match queries with responses required uint64 id = 4; + + // Signals whether or not this message is a response to another message + optional bool response = 5; } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index dfdde4dd1..e9ed64d98 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -1,15 +1,17 @@ package dht import ( + "math/rand" + "time" + peer "github.com/jbenet/go-ipfs/peer" swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" - "time" ) // TODO: determine a way of creating and managing message IDs func GenerateMessageID() uint64 { - return 4 + return uint64(rand.Uint32()) << 32 & uint64(rand.Uint32()) } // This file implements the Routing interface for the IpfsDHT struct. @@ -68,9 +70,6 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { case resp := <-response_chan: return resp.Data, nil } - - // Should never be hit - return nil, nil } // Value provider layer of indirection. From f6964ed3b12abbb0ac95b5be98856a41861661ad Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 30 Jul 2014 20:16:34 -0700 Subject: [PATCH 0026/3817] use datastore for local data This commit was moved from ipfs/go-ipfs-routing@098a903b5bc609a63e134a5bd5daf3b1a9ca5c76 --- routing/dht/dht.go | 60 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index a1f63f106..0f55ba45b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -3,9 +3,12 @@ package dht import ( "sync" - peer "github.com/jbenet/go-ipfs/peer" - swarm "github.com/jbenet/go-ipfs/swarm" - u "github.com/jbenet/go-ipfs/util" + peer "github.com/jbenet/go-ipfs/peer" + swarm "github.com/jbenet/go-ipfs/swarm" + u "github.com/jbenet/go-ipfs/util" + + ds "github.com/jbenet/datastore.go" + "code.google.com/p/goprotobuf/proto" ) @@ -18,8 +21,11 @@ type IpfsDHT struct { network *swarm.Swarm - // local data (TEMPORARY: until we formalize data storage with datastore) - data map[string][]byte + // Local peer (yourself) + self *peer.Peer + + // Local data + datastore ds.Datastore // map of channels waiting for reply messages listeners map[uint64]chan *swarm.Message @@ -29,6 +35,15 @@ type IpfsDHT struct { shutdown chan struct{} } +func NewDHT(p *peer.Peer) *IpfsDHT { + dht := new(IpfsDHT) + dht.self = p + dht.network = swarm.NewSwarm(p) + dht.listeners = make(map[uint64]chan *swarm.Message) + dht.shutdown = make(chan struct{}) + return dht +} + // Read in all messages from swarm and handle them appropriately // NOTE: this function is just a quick sketch func (dht *IpfsDHT) handleMessages() { @@ -61,10 +76,13 @@ func (dht *IpfsDHT) handleMessages() { case DHTMessage_GET_VALUE: dht.handleGetValue(mes.Peer, pmes) case DHTMessage_PUT_VALUE: + dht.handlePutValue(mes.Peer, pmes) case DHTMessage_FIND_NODE: + dht.handleFindNode(mes.Peer, pmes) case DHTMessage_ADD_PROVIDER: case DHTMessage_GET_PROVIDERS: case DHTMessage_PING: + dht.handleFindNode(mes.Peer, pmes) } case <-dht.shutdown: @@ -74,15 +92,22 @@ func (dht *IpfsDHT) handleMessages() { } func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *DHTMessage) { - val, found := dht.data[pmes.GetKey()] - if found { + dskey := ds.NewKey(pmes.GetKey()) + i_val, err := dht.datastore.Get(dskey) + if err == nil { isResponse := true resp := new(DHTMessage) resp.Response = &isResponse resp.Id = pmes.Id resp.Key = pmes.Key + + val := i_val.([]byte) resp.Value = val - } else { + + mes := new(swarm.Message) + mes.Peer = p + mes.Data = []byte(resp.String()) + } else if err == ds.ErrNotFound { // Find closest node(s) to desired key and reply with that info // TODO: this will need some other metadata in the protobuf message // to signal to the querying node that the data its receiving @@ -90,8 +115,14 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *DHTMessage) { } } +// Store a value in this nodes local storage func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *DHTMessage) { - panic("Not implemented.") + dskey := ds.NewKey(pmes.GetKey()) + err := dht.datastore.Put(dskey, pmes.GetValue()) + if err != nil { + // For now, just panic, handle this better later maybe + panic(err) + } } func (dht *IpfsDHT) handleFindNode(p *peer.Peer, pmes *DHTMessage) { @@ -121,6 +152,17 @@ func (dht *IpfsDHT) ListenFor(mesid uint64) <-chan *swarm.Message { return lchan } +func (dht *IpfsDHT) Unlisten(mesid uint64) { + dht.listenLock.Lock() + ch, ok := dht.listeners[mesid] + if ok { + delete(dht.listeners, mesid) + } + dht.listenLock.Unlock() + close(ch) +} + + // Stop all communications from this node and shut down func (dht *IpfsDHT) Halt() { dht.shutdown <- struct{}{} From 54a3b5e8b9677b04b55d2aba6dd5eb14f2082833 Mon Sep 17 00:00:00 2001 From: Jeromy Johnson Date: Thu, 31 Jul 2014 17:43:48 -0700 Subject: [PATCH 0027/3817] begin planning of identification process This commit was moved from ipfs/go-ipfs-routing@2702818816297c6eeb0f161087a7d4a72151cc60 --- routing/dht/dht.go | 74 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 8 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 0f55ba45b..62d4a71cf 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -2,10 +2,14 @@ package dht import ( "sync" + "time" peer "github.com/jbenet/go-ipfs/peer" swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" + identify "github.com/jbenet/go-ipfs/identify" + + ma "github.com/jbenet/go-multiaddr" ds "github.com/jbenet/datastore.go" @@ -35,15 +39,44 @@ type IpfsDHT struct { shutdown chan struct{} } -func NewDHT(p *peer.Peer) *IpfsDHT { +// Create a new DHT object with the given peer as the 'local' host +func NewDHT(p *peer.Peer) (*IpfsDHT, error) { dht := new(IpfsDHT) - dht.self = p + dht.network = swarm.NewSwarm(p) + //TODO: should Listen return an error? + dht.network.Listen() + + dht.datastore = ds.NewMapDatastore() + + dht.self = p dht.listeners = make(map[uint64]chan *swarm.Message) dht.shutdown = make(chan struct{}) - return dht + return dht, nil +} + +// Connect to a new peer at the given address +func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) error { + peer := new(peer.Peer) + peer.AddAddress(addr) + + conn,err := swarm.Dial("tcp", peer) + if err != nil { + return err + } + + err = identify.Handshake(dht.self, conn) + if err != nil { + return err + } + + dht.network.StartConn(conn.Peer.Key(), conn) + + // TODO: Add this peer to our routing table + return nil } + // Read in all messages from swarm and handle them appropriately // NOTE: this function is just a quick sketch func (dht *IpfsDHT) handleMessages() { @@ -134,11 +167,9 @@ func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *DHTMessage) { resp := new(DHTMessage) resp.Id = pmes.Id resp.Response = &isResponse + resp.Type = pmes.Type - mes := new(swarm.Message) - mes.Peer = p - mes.Data = []byte(resp.String()) - dht.network.Chan.Outgoing <- mes + dht.network.Chan.Outgoing <-swarm.NewMessage(p, []byte(resp.String())) } @@ -162,9 +193,36 @@ func (dht *IpfsDHT) Unlisten(mesid uint64) { close(ch) } - // Stop all communications from this node and shut down func (dht *IpfsDHT) Halt() { dht.shutdown <- struct{}{} dht.network.Close() } + +// Ping a node, log the time it took +func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) { + // Thoughts: maybe this should accept an ID and do a peer lookup? + id := GenerateMessageID() + mes_type := DHTMessage_PING + pmes := new(DHTMessage) + pmes.Id = &id + pmes.Type = &mes_type + + mes := new(swarm.Message) + mes.Peer = p + mes.Data = []byte(pmes.String()) + + before := time.Now() + response_chan := dht.ListenFor(id) + dht.network.Chan.Outgoing <- mes + + tout := time.After(timeout) + select { + case <-response_chan: + roundtrip := time.Since(before) + u.DOut("Ping took %s.", roundtrip.String()) + case <-tout: + // Timed out, think about removing node from network + u.DOut("Ping node timed out.") + } +} From 31b32ef46acf1d665daa1a79809b95d3847a57a1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 31 Jul 2014 21:55:44 -0700 Subject: [PATCH 0028/3817] making connections between nodes get closer to working This commit was moved from ipfs/go-ipfs-routing@e3ecf3200340b724c27462c12c4e7a7b2e943c6c --- routing/dht/dht.go | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 62d4a71cf..9d6d1223a 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -55,6 +55,10 @@ func NewDHT(p *peer.Peer) (*IpfsDHT, error) { return dht, nil } +func (dht *IpfsDHT) Start() { + go dht.handleMessages() +} + // Connect to a new peer at the given address func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) error { peer := new(peer.Peer) @@ -65,24 +69,26 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) error { return err } - err = identify.Handshake(dht.self, conn) + err = identify.Handshake(dht.self, peer, conn.Incoming.MsgChan, conn.Outgoing.MsgChan) if err != nil { return err } - dht.network.StartConn(conn.Peer.Key(), conn) + dht.network.StartConn(conn) // TODO: Add this peer to our routing table return nil } - // Read in all messages from swarm and handle them appropriately // NOTE: this function is just a quick sketch func (dht *IpfsDHT) handleMessages() { + u.DOut("Being message handling routine") for { select { case mes := <-dht.network.Chan.Incoming: + u.DOut("recieved message from swarm.") + pmes := new(DHTMessage) err := proto.Unmarshal(mes.Data, pmes) if err != nil { @@ -118,6 +124,8 @@ func (dht *IpfsDHT) handleMessages() { dht.handleFindNode(mes.Peer, pmes) } + case err := <-dht.network.Chan.Errors: + panic(err) case <-dht.shutdown: return } @@ -158,10 +166,6 @@ func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *DHTMessage) { } } -func (dht *IpfsDHT) handleFindNode(p *peer.Peer, pmes *DHTMessage) { - panic("Not implemented.") -} - func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *DHTMessage) { isResponse := true resp := new(DHTMessage) @@ -172,6 +176,18 @@ func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *DHTMessage) { dht.network.Chan.Outgoing <-swarm.NewMessage(p, []byte(resp.String())) } +func (dht *IpfsDHT) handleFindNode(p *peer.Peer, pmes *DHTMessage) { + panic("Not implemented.") +} + +func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *DHTMessage) { + panic("Not implemented.") +} + +func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *DHTMessage) { + panic("Not implemented.") +} + // Register a handler for a specific message ID, used for getting replies // to certain messages (i.e. response to a GET_VALUE message) @@ -202,6 +218,8 @@ func (dht *IpfsDHT) Halt() { // Ping a node, log the time it took func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) { // Thoughts: maybe this should accept an ID and do a peer lookup? + u.DOut("Enter Ping.") + id := GenerateMessageID() mes_type := DHTMessage_PING pmes := new(DHTMessage) From e12a3068b349f07303f1fca5b06b17538bee179d Mon Sep 17 00:00:00 2001 From: Jeromy Johnson Date: Fri, 1 Aug 2014 13:21:51 -0700 Subject: [PATCH 0029/3817] finish basic communcations between nodes and add a test of the ping operation This commit was moved from ipfs/go-ipfs-routing@6c1225f8070c031735895db047844f37e3492391 --- routing/dht/dht.go | 70 ++++++++++++++++++-------------------- routing/dht/dht_test.go | 55 ++++++++++++++++++++++++++++++ routing/dht/pDHTMessage.go | 24 +++++++++++++ 3 files changed, 112 insertions(+), 37 deletions(-) create mode 100644 routing/dht/dht_test.go create mode 100644 routing/dht/pDHTMessage.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 9d6d1223a..ae320426c 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -41,20 +41,22 @@ type IpfsDHT struct { // Create a new DHT object with the given peer as the 'local' host func NewDHT(p *peer.Peer) (*IpfsDHT, error) { - dht := new(IpfsDHT) - - dht.network = swarm.NewSwarm(p) - //TODO: should Listen return an error? - dht.network.Listen() + network := swarm.NewSwarm(p) + err := network.Listen() + if err != nil { + return nil,err + } + dht := new(IpfsDHT) + dht.network = network dht.datastore = ds.NewMapDatastore() - dht.self = p dht.listeners = make(map[uint64]chan *swarm.Message) dht.shutdown = make(chan struct{}) return dht, nil } +// Start up background goroutines needed by the DHT func (dht *IpfsDHT) Start() { go dht.handleMessages() } @@ -111,6 +113,7 @@ func (dht *IpfsDHT) handleMessages() { } // + u.DOut("Got message type: %d", pmes.GetType()) switch pmes.GetType() { case DHTMessage_GET_VALUE: dht.handleGetValue(mes.Peer, pmes) @@ -121,7 +124,7 @@ func (dht *IpfsDHT) handleMessages() { case DHTMessage_ADD_PROVIDER: case DHTMessage_GET_PROVIDERS: case DHTMessage_PING: - dht.handleFindNode(mes.Peer, pmes) + dht.handlePing(mes.Peer, pmes) } case err := <-dht.network.Chan.Errors: @@ -136,18 +139,15 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *DHTMessage) { dskey := ds.NewKey(pmes.GetKey()) i_val, err := dht.datastore.Get(dskey) if err == nil { - isResponse := true - resp := new(DHTMessage) - resp.Response = &isResponse - resp.Id = pmes.Id - resp.Key = pmes.Key - - val := i_val.([]byte) - resp.Value = val - - mes := new(swarm.Message) - mes.Peer = p - mes.Data = []byte(resp.String()) + resp := &pDHTMessage{ + Response: true, + Id: *pmes.Id, + Key: *pmes.Key, + Value: i_val.([]byte), + } + + mes := swarm.NewMessage(p, resp.ToProtobuf()) + dht.network.Chan.Outgoing <- mes } else if err == ds.ErrNotFound { // Find closest node(s) to desired key and reply with that info // TODO: this will need some other metadata in the protobuf message @@ -167,13 +167,13 @@ func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *DHTMessage) { } func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *DHTMessage) { - isResponse := true - resp := new(DHTMessage) - resp.Id = pmes.Id - resp.Response = &isResponse - resp.Type = pmes.Type + resp := &pDHTMessage{ + Type: pmes.GetType(), + Response: true, + Id: pmes.GetId(), + } - dht.network.Chan.Outgoing <-swarm.NewMessage(p, []byte(resp.String())) + dht.network.Chan.Outgoing <-swarm.NewMessage(p, resp.ToProtobuf()) } func (dht *IpfsDHT) handleFindNode(p *peer.Peer, pmes *DHTMessage) { @@ -199,6 +199,7 @@ func (dht *IpfsDHT) ListenFor(mesid uint64) <-chan *swarm.Message { return lchan } +// Unregister the given message id from the listener map func (dht *IpfsDHT) Unlisten(mesid uint64) { dht.listenLock.Lock() ch, ok := dht.listeners[mesid] @@ -216,31 +217,26 @@ func (dht *IpfsDHT) Halt() { } // Ping a node, log the time it took -func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) { +func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { // Thoughts: maybe this should accept an ID and do a peer lookup? u.DOut("Enter Ping.") - id := GenerateMessageID() - mes_type := DHTMessage_PING - pmes := new(DHTMessage) - pmes.Id = &id - pmes.Type = &mes_type - - mes := new(swarm.Message) - mes.Peer = p - mes.Data = []byte(pmes.String()) + pmes := pDHTMessage{Id: GenerateMessageID(), Type: DHTMessage_PING} + mes := swarm.NewMessage(p, pmes.ToProtobuf()) before := time.Now() - response_chan := dht.ListenFor(id) + response_chan := dht.ListenFor(pmes.Id) dht.network.Chan.Outgoing <- mes tout := time.After(timeout) select { case <-response_chan: roundtrip := time.Since(before) - u.DOut("Ping took %s.", roundtrip.String()) + u.POut("Ping took %s.", roundtrip.String()) + return nil case <-tout: // Timed out, think about removing node from network u.DOut("Ping node timed out.") + return u.ErrTimeout } } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go new file mode 100644 index 000000000..8485a1d83 --- /dev/null +++ b/routing/dht/dht_test.go @@ -0,0 +1,55 @@ +package dht + +import ( + "testing" + peer "github.com/jbenet/go-ipfs/peer" + ma "github.com/jbenet/go-multiaddr" + u "github.com/jbenet/go-ipfs/util" + + "time" +) + +func TestPing(t *testing.T) { + u.Debug = false + addr_a,err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1234") + if err != nil { + t.Fatal(err) + } + addr_b,err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5678") + if err != nil { + t.Fatal(err) + } + + peer_a := new(peer.Peer) + peer_a.AddAddress(addr_a) + peer_a.ID = peer.ID([]byte("peer_a")) + + peer_b := new(peer.Peer) + peer_b.AddAddress(addr_b) + peer_b.ID = peer.ID([]byte("peer_b")) + + dht_a,err := NewDHT(peer_a) + if err != nil { + t.Fatal(err) + } + + dht_b,err := NewDHT(peer_b) + if err != nil { + t.Fatal(err) + } + + + dht_a.Start() + dht_b.Start() + + err = dht_a.Connect(addr_b) + if err != nil { + t.Fatal(err) + } + + //Test that we can ping the node + err = dht_a.Ping(peer_b, time.Second * 2) + if err != nil { + t.Fatal(err) + } +} diff --git a/routing/dht/pDHTMessage.go b/routing/dht/pDHTMessage.go new file mode 100644 index 000000000..65c03b1f8 --- /dev/null +++ b/routing/dht/pDHTMessage.go @@ -0,0 +1,24 @@ +package dht + +// A helper struct to make working with protbuf types easier +type pDHTMessage struct { + Type DHTMessage_MessageType + Key string + Value []byte + Response bool + Id uint64 +} + +func (m *pDHTMessage) ToProtobuf() *DHTMessage { + pmes := new(DHTMessage) + if m.Value != nil { + pmes.Value = m.Value + } + + pmes.Type = &m.Type + pmes.Key = &m.Key + pmes.Response = &m.Response + pmes.Id = &m.Id + + return pmes +} From adb6643f08a905e498e86b2b80a648f689ac8b90 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 3 Aug 2014 13:37:09 -0700 Subject: [PATCH 0030/3817] rough kbucket implementation, tests and cleanup to follow This commit was moved from ipfs/go-ipfs-routing@7de73610a0a9e86c00230eda3d0de0fe2bd85c4c --- routing/dht/routing.go | 4 +- routing/dht/table.go | 170 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 168 insertions(+), 6 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index e9ed64d98..66e27e369 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -21,7 +21,7 @@ func GenerateMessageID() uint64 { // PutValue adds value corresponding to given Key. func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { var p *peer.Peer - p = s.routes.NearestNode(key) + p = s.routes.NearestPeer(convertKey(key)) pmes_type := DHTMessage_PUT_VALUE str_key := string(key) @@ -44,7 +44,7 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { // GetValue searches for the value corresponding to given Key. func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { var p *peer.Peer - p = s.routes.NearestNode(key) + p = s.routes.NearestPeer(convertKey(key)) str_key := string(key) mes_type := DHTMessage_GET_VALUE diff --git a/routing/dht/table.go b/routing/dht/table.go index d7625e462..b8c20f8ab 100644 --- a/routing/dht/table.go +++ b/routing/dht/table.go @@ -3,6 +3,9 @@ package dht import ( "bytes" "container/list" + "sort" + + "crypto/sha256" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" @@ -16,18 +19,177 @@ import ( type ID []byte // Bucket holds a list of peers. -type Bucket []*list.List +type Bucket list.List + +func (b *Bucket) Find(id peer.ID) *list.Element { + bucket_list := (*list.List)(b) + for e := bucket_list.Front(); e != nil; e = e.Next() { + if e.Value.(*peer.Peer).ID.Equal(id) { + return e + } + } + return nil +} + +func (b *Bucket) MoveToFront(e *list.Element) { + bucket_list := (*list.List)(b) + bucket_list.MoveToFront(e) +} + +func (b *Bucket) PushFront(p *peer.Peer) { + bucket_list := (*list.List)(b) + bucket_list.PushFront(p) +} + +func (b *Bucket) PopBack() *peer.Peer { + bucket_list := (*list.List)(b) + last := bucket_list.Back() + bucket_list.Remove(last) + return last.Value.(*peer.Peer) +} + +func (b *Bucket) Len() int { + bucket_list := (*list.List)(b) + return bucket_list.Len() +} + +func (b *Bucket) Split(cpl int, target ID) *Bucket { + bucket_list := (*list.List)(b) + out := list.New() + e := bucket_list.Front() + for e != nil { + peer_id := convertPeerID(e.Value.(*peer.Peer).ID) + peer_cpl := xor(peer_id, target).commonPrefixLen() + if peer_cpl > cpl { + cur := e + out.PushBack(e.Value) + e = e.Next() + bucket_list.Remove(cur) + continue + } + } + return (*Bucket)(out) +} // RoutingTable defines the routing table. type RoutingTable struct { + // ID of the local peer + local ID + // kBuckets define all the fingers to other nodes. - Buckets []Bucket + Buckets []*Bucket + bucketsize int +} + +func convertPeerID(id peer.ID) ID { + hash := sha256.Sum256(id) + return hash[:] +} + +func convertKey(id u.Key) ID { + hash := sha256.Sum256([]byte(id)) + return hash[:] +} + +// Update adds or moves the given peer to the front of its respective bucket +// If a peer gets removed from a bucket, it is returned +func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer { + peer_id := convertPeerID(p.ID) + cpl := xor(peer_id, rt.local).commonPrefixLen() + + b_id := cpl + if b_id >= len(rt.Buckets) { + b_id = len(rt.Buckets) - 1 + } + + bucket := rt.Buckets[b_id] + e := bucket.Find(p.ID) + if e == nil { + // New peer, add to bucket + bucket.PushFront(p) + + // Are we past the max bucket size? + if bucket.Len() > rt.bucketsize { + if b_id == len(rt.Buckets) - 1 { + new_bucket := bucket.Split(b_id, rt.local) + rt.Buckets = append(rt.Buckets, new_bucket) + + // If all elements were on left side of split... + if bucket.Len() > rt.bucketsize { + return bucket.PopBack() + } + } else { + // If the bucket cant split kick out least active node + return bucket.PopBack() + } + } + return nil + } else { + // If the peer is already in the table, move it to the front. + // This signifies that it it "more active" and the less active nodes + // Will as a result tend towards the back of the list + bucket.MoveToFront(e) + return nil + } +} + +// A helper struct to sort peers by their distance to the local node +type peerDistance struct { + p *peer.Peer + distance ID +} +type peerSorterArr []*peerDistance +func (p peerSorterArr) Len() int {return len(p)} +func (p peerSorterArr) Swap(a, b int) {p[a],p[b] = p[b],p[a]} +func (p peerSorterArr) Less(a, b int) bool { + return p[a].distance.Less(p[b]) +} +// + +func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer { + peers := rt.NearestPeers(id, 1) + return peers[0] } //TODO: make this accept an ID, requires method of converting keys to IDs -func (rt *RoutingTable) NearestNode(key u.Key) *peer.Peer { - panic("Function not implemented.") +func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { + cpl := xor(id, rt.local).commonPrefixLen() + + // Get bucket at cpl index or last bucket + var bucket *Bucket + if cpl >= len(rt.Buckets) { + bucket = rt.Buckets[len(rt.Buckets) - 1] + } else { + bucket = rt.Buckets[cpl] + } + + if bucket.Len() == 0 { + // This can happen, very rarely. + panic("Case not yet implemented.") + } + + var peerArr peerSorterArr + + plist := (*list.List)(bucket) + for e := plist.Front();e != nil; e = e.Next() { + p := e.Value.(*peer.Peer) + p_id := convertPeerID(p.ID) + pd := peerDistance{ + p: p, + distance: xor(rt.local, p_id), + } + peerArr = append(peerArr, &pd) + } + + sort.Sort(peerArr) + + var out []*peer.Peer + for i := 0; i < count && i < peerArr.Len(); i++ { + out = append(out, peerArr[i].p) + } + + return out } func (id ID) Equal(other ID) bool { From 68c02451ae1acf426a83c5130e46cc3b435eeaf3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 3 Aug 2014 15:04:24 -0700 Subject: [PATCH 0031/3817] tests for kbucket and some code cleanup This commit was moved from ipfs/go-ipfs-routing@007f055191318c23c7c68cf4cfb7d4b80d55bea1 --- routing/dht/bucket.go | 63 +++++++++++++++++ routing/dht/table.go | 143 +++++--------------------------------- routing/dht/table_test.go | 92 ++++++++++++++++++++++++ routing/dht/util.go | 78 +++++++++++++++++++++ 4 files changed, 249 insertions(+), 127 deletions(-) create mode 100644 routing/dht/bucket.go create mode 100644 routing/dht/table_test.go create mode 100644 routing/dht/util.go diff --git a/routing/dht/bucket.go b/routing/dht/bucket.go new file mode 100644 index 000000000..120ed29a4 --- /dev/null +++ b/routing/dht/bucket.go @@ -0,0 +1,63 @@ +package dht + +import ( + "container/list" + + peer "github.com/jbenet/go-ipfs/peer" +) +// Bucket holds a list of peers. +type Bucket list.List + +func (b *Bucket) Find(id peer.ID) *list.Element { + bucket_list := (*list.List)(b) + for e := bucket_list.Front(); e != nil; e = e.Next() { + if e.Value.(*peer.Peer).ID.Equal(id) { + return e + } + } + return nil +} + +func (b *Bucket) MoveToFront(e *list.Element) { + bucket_list := (*list.List)(b) + bucket_list.MoveToFront(e) +} + +func (b *Bucket) PushFront(p *peer.Peer) { + bucket_list := (*list.List)(b) + bucket_list.PushFront(p) +} + +func (b *Bucket) PopBack() *peer.Peer { + bucket_list := (*list.List)(b) + last := bucket_list.Back() + bucket_list.Remove(last) + return last.Value.(*peer.Peer) +} + +func (b *Bucket) Len() int { + bucket_list := (*list.List)(b) + return bucket_list.Len() +} + +// Splits a buckets peers into two buckets, the methods receiver will have +// peers with CPL equal to cpl, the returned bucket will have peers with CPL +// greater than cpl (returned bucket has closer peers) +func (b *Bucket) Split(cpl int, target ID) *Bucket { + bucket_list := (*list.List)(b) + out := list.New() + e := bucket_list.Front() + for e != nil { + peer_id := convertPeerID(e.Value.(*peer.Peer).ID) + peer_cpl := xor(peer_id, target).commonPrefixLen() + if peer_cpl > cpl { + cur := e + out.PushBack(e.Value) + e = e.Next() + bucket_list.Remove(cur) + continue + } + e = e.Next() + } + return (*Bucket)(out) +} diff --git a/routing/dht/table.go b/routing/dht/table.go index b8c20f8ab..c1eed534b 100644 --- a/routing/dht/table.go +++ b/routing/dht/table.go @@ -1,76 +1,12 @@ package dht import ( - "bytes" "container/list" "sort" - "crypto/sha256" - peer "github.com/jbenet/go-ipfs/peer" - u "github.com/jbenet/go-ipfs/util" ) -// ID for IpfsDHT should be a byte slice, to allow for simpler operations -// (xor). DHT ids are based on the peer.IDs. -// -// NOTE: peer.IDs are biased because they are multihashes (first bytes -// biased). Thus, may need to re-hash keys (uniform dist). TODO(jbenet) -type ID []byte - -// Bucket holds a list of peers. -type Bucket list.List - -func (b *Bucket) Find(id peer.ID) *list.Element { - bucket_list := (*list.List)(b) - for e := bucket_list.Front(); e != nil; e = e.Next() { - if e.Value.(*peer.Peer).ID.Equal(id) { - return e - } - } - return nil -} - -func (b *Bucket) MoveToFront(e *list.Element) { - bucket_list := (*list.List)(b) - bucket_list.MoveToFront(e) -} - -func (b *Bucket) PushFront(p *peer.Peer) { - bucket_list := (*list.List)(b) - bucket_list.PushFront(p) -} - -func (b *Bucket) PopBack() *peer.Peer { - bucket_list := (*list.List)(b) - last := bucket_list.Back() - bucket_list.Remove(last) - return last.Value.(*peer.Peer) -} - -func (b *Bucket) Len() int { - bucket_list := (*list.List)(b) - return bucket_list.Len() -} - -func (b *Bucket) Split(cpl int, target ID) *Bucket { - bucket_list := (*list.List)(b) - out := list.New() - e := bucket_list.Front() - for e != nil { - peer_id := convertPeerID(e.Value.(*peer.Peer).ID) - peer_cpl := xor(peer_id, target).commonPrefixLen() - if peer_cpl > cpl { - cur := e - out.PushBack(e.Value) - e = e.Next() - bucket_list.Remove(cur) - continue - } - } - return (*Bucket)(out) -} - // RoutingTable defines the routing table. type RoutingTable struct { @@ -82,14 +18,12 @@ type RoutingTable struct { bucketsize int } -func convertPeerID(id peer.ID) ID { - hash := sha256.Sum256(id) - return hash[:] -} - -func convertKey(id u.Key) ID { - hash := sha256.Sum256([]byte(id)) - return hash[:] +func NewRoutingTable(bucketsize int, local_id ID) *RoutingTable { + rt := new(RoutingTable) + rt.Buckets = []*Bucket{new(Bucket)} + rt.bucketsize = bucketsize + rt.local = local_id + return rt } // Update adds or moves the given peer to the front of its respective bucket @@ -114,6 +48,10 @@ func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer { if b_id == len(rt.Buckets) - 1 { new_bucket := bucket.Split(b_id, rt.local) rt.Buckets = append(rt.Buckets, new_bucket) + if new_bucket.Len() > rt.bucketsize { + // This is another very rare and annoying case + panic("Case not handled.") + } // If all elements were on left side of split... if bucket.Len() > rt.bucketsize { @@ -139,20 +77,23 @@ type peerDistance struct { p *peer.Peer distance ID } + +// peerSorterArr implements sort.Interface to sort peers by xor distance type peerSorterArr []*peerDistance func (p peerSorterArr) Len() int {return len(p)} func (p peerSorterArr) Swap(a, b int) {p[a],p[b] = p[b],p[a]} func (p peerSorterArr) Less(a, b int) bool { - return p[a].distance.Less(p[b]) + return p[a].distance.Less(p[b].distance) } // +// Returns a single peer that is nearest to the given ID func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer { peers := rt.NearestPeers(id, 1) return peers[0] } -//TODO: make this accept an ID, requires method of converting keys to IDs +// Returns a list of the 'count' closest peers to the given ID func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { cpl := xor(id, rt.local).commonPrefixLen() @@ -170,7 +111,6 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { } var peerArr peerSorterArr - plist := (*list.List)(bucket) for e := plist.Front();e != nil; e = e.Next() { p := e.Value.(*peer.Peer) @@ -182,6 +122,7 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { peerArr = append(peerArr, &pd) } + // Sort by distance to local peer sort.Sort(peerArr) var out []*peer.Peer @@ -191,55 +132,3 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { return out } - -func (id ID) Equal(other ID) bool { - return bytes.Equal(id, other) -} - -func (id ID) Less(other interface{}) bool { - a, b := equalizeSizes(id, other.(ID)) - for i := 0; i < len(a); i++ { - if a[i] != b[i] { - return a[i] < b[i] - } - } - return len(a) < len(b) -} - -func (id ID) commonPrefixLen() int { - for i := 0; i < len(id); i++ { - for j := 0; j < 8; j++ { - if (id[i]>>uint8(7-j))&0x1 != 0 { - return i*8 + j - } - } - } - return len(id)*8 - 1 -} - -func xor(a, b ID) ID { - a, b = equalizeSizes(a, b) - - c := make(ID, len(a)) - for i := 0; i < len(a); i++ { - c[i] = a[i] ^ b[i] - } - return c -} - -func equalizeSizes(a, b ID) (ID, ID) { - la := len(a) - lb := len(b) - - if la < lb { - na := make([]byte, lb) - copy(na, a) - a = na - } else if lb < la { - nb := make([]byte, la) - copy(nb, b) - b = nb - } - - return a, b -} diff --git a/routing/dht/table_test.go b/routing/dht/table_test.go new file mode 100644 index 000000000..cb52bd1a0 --- /dev/null +++ b/routing/dht/table_test.go @@ -0,0 +1,92 @@ +package dht + +import ( + crand "crypto/rand" + "crypto/sha256" + "math/rand" + "container/list" + "testing" + + peer "github.com/jbenet/go-ipfs/peer" +) + +func _randPeer() *peer.Peer { + p := new(peer.Peer) + p.ID = make(peer.ID, 16) + crand.Read(p.ID) + return p +} + +func _randID() ID { + buf := make([]byte, 16) + crand.Read(buf) + + hash := sha256.Sum256(buf) + return ID(hash[:]) +} + +// Test basic features of the bucket struct +func TestBucket(t *testing.T) { + b := new(Bucket) + + peers := make([]*peer.Peer, 100) + for i := 0; i < 100; i++ { + peers[i] = _randPeer() + b.PushFront(peers[i]) + } + + local := _randPeer() + local_id := convertPeerID(local.ID) + + i := rand.Intn(len(peers)) + e := b.Find(peers[i].ID) + if e == nil { + t.Errorf("Failed to find peer: %v", peers[i]) + } + + spl := b.Split(0, convertPeerID(local.ID)) + llist := (*list.List)(b) + for e := llist.Front(); e != nil; e = e.Next() { + p := convertPeerID(e.Value.(*peer.Peer).ID) + cpl := xor(p, local_id).commonPrefixLen() + if cpl > 0 { + t.Fatalf("Split failed. found id with cpl > 0 in 0 bucket") + } + } + + rlist := (*list.List)(spl) + for e := rlist.Front(); e != nil; e = e.Next() { + p := convertPeerID(e.Value.(*peer.Peer).ID) + cpl := xor(p, local_id).commonPrefixLen() + if cpl == 0 { + t.Fatalf("Split failed. found id with cpl == 0 in non 0 bucket") + } + } +} + +// Right now, this just makes sure that it doesnt hang or crash +func TestTableUpdate(t *testing.T) { + local := _randPeer() + rt := NewRoutingTable(10, convertPeerID(local.ID)) + + peers := make([]*peer.Peer, 100) + for i := 0; i < 100; i++ { + peers[i] = _randPeer() + } + + // Testing Update + for i := 0; i < 10000; i++ { + p := rt.Update(peers[rand.Intn(len(peers))]) + if p != nil { + t.Log("evicted peer.") + } + } + + for i := 0; i < 100; i++ { + id := _randID() + ret := rt.NearestPeers(id, 5) + if len(ret) == 0 { + t.Fatal("Failed to find node near ID.") + } + } +} diff --git a/routing/dht/util.go b/routing/dht/util.go new file mode 100644 index 000000000..eed8d9301 --- /dev/null +++ b/routing/dht/util.go @@ -0,0 +1,78 @@ +package dht + +import ( + "bytes" + "crypto/sha256" + + peer "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" +) + +// ID for IpfsDHT should be a byte slice, to allow for simpler operations +// (xor). DHT ids are based on the peer.IDs. +// +// NOTE: peer.IDs are biased because they are multihashes (first bytes +// biased). Thus, may need to re-hash keys (uniform dist). TODO(jbenet) +type ID []byte + +func (id ID) Equal(other ID) bool { + return bytes.Equal(id, other) +} + +func (id ID) Less(other interface{}) bool { + a, b := equalizeSizes(id, other.(ID)) + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return a[i] < b[i] + } + } + return len(a) < len(b) +} + +func (id ID) commonPrefixLen() int { + for i := 0; i < len(id); i++ { + for j := 0; j < 8; j++ { + if (id[i]>>uint8(7-j))&0x1 != 0 { + return i*8 + j + } + } + } + return len(id)*8 - 1 +} + +func xor(a, b ID) ID { + a, b = equalizeSizes(a, b) + + c := make(ID, len(a)) + for i := 0; i < len(a); i++ { + c[i] = a[i] ^ b[i] + } + return c +} + +func equalizeSizes(a, b ID) (ID, ID) { + la := len(a) + lb := len(b) + + if la < lb { + na := make([]byte, lb) + copy(na, a) + a = na + } else if lb < la { + nb := make([]byte, la) + copy(nb, b) + b = nb + } + + return a, b +} + +func convertPeerID(id peer.ID) ID { + hash := sha256.Sum256(id) + return hash[:] +} + +func convertKey(id u.Key) ID { + hash := sha256.Sum256([]byte(id)) + return hash[:] +} From 89b7f7e69192cda702d6f57d3064f48f4f904700 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 3 Aug 2014 17:35:12 -0700 Subject: [PATCH 0032/3817] finish implementation of Put and Get for DHT This commit was moved from ipfs/go-ipfs-routing@f92fff2d8b34fdb7d5987e7aa4efb0e831318c4e --- routing/dht/dht.go | 10 ++++--- routing/dht/dht_test.go | 60 ++++++++++++++++++++++++++++++++++++++++- routing/dht/routing.go | 58 ++++++++++++++++++++------------------- routing/dht/table.go | 51 +++++++++++++++++++++++------------ 4 files changed, 131 insertions(+), 48 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index ae320426c..02ef8c4f7 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -21,7 +21,7 @@ import ( // IpfsDHT is an implementation of Kademlia with Coral and S/Kademlia modifications. // It is used to implement the base IpfsRouting module. type IpfsDHT struct { - routes RoutingTable + routes *RoutingTable network *swarm.Swarm @@ -53,6 +53,7 @@ func NewDHT(p *peer.Peer) (*IpfsDHT, error) { dht.self = p dht.listeners = make(map[uint64]chan *swarm.Message) dht.shutdown = make(chan struct{}) + dht.routes = NewRoutingTable(20, convertPeerID(p.ID)) return dht, nil } @@ -78,14 +79,14 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) error { dht.network.StartConn(conn) - // TODO: Add this peer to our routing table + dht.routes.Update(peer) return nil } // Read in all messages from swarm and handle them appropriately // NOTE: this function is just a quick sketch func (dht *IpfsDHT) handleMessages() { - u.DOut("Being message handling routine") + u.DOut("Begin message handling routine") for { select { case mes := <-dht.network.Chan.Incoming: @@ -98,6 +99,9 @@ func (dht *IpfsDHT) handleMessages() { continue } + // Update peers latest visit in routing table + dht.routes.Update(mes.Peer) + // Note: not sure if this is the correct place for this if pmes.GetResponse() { dht.listenLock.RLock() diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 8485a1d83..0c4b7ee09 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -6,9 +6,13 @@ import ( ma "github.com/jbenet/go-multiaddr" u "github.com/jbenet/go-ipfs/util" + "fmt" + "time" ) +var _ = fmt.Println + func TestPing(t *testing.T) { u.Debug = false addr_a,err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1234") @@ -38,7 +42,6 @@ func TestPing(t *testing.T) { t.Fatal(err) } - dht_a.Start() dht_b.Start() @@ -52,4 +55,59 @@ func TestPing(t *testing.T) { if err != nil { t.Fatal(err) } + + dht_a.Halt() + dht_b.Halt() +} + +func TestValueGetSet(t *testing.T) { + u.Debug = false + addr_a,err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") + if err != nil { + t.Fatal(err) + } + addr_b,err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5679") + if err != nil { + t.Fatal(err) + } + + peer_a := new(peer.Peer) + peer_a.AddAddress(addr_a) + peer_a.ID = peer.ID([]byte("peer_a")) + + peer_b := new(peer.Peer) + peer_b.AddAddress(addr_b) + peer_b.ID = peer.ID([]byte("peer_b")) + + dht_a,err := NewDHT(peer_a) + if err != nil { + t.Fatal(err) + } + + dht_b,err := NewDHT(peer_b) + if err != nil { + t.Fatal(err) + } + + dht_a.Start() + dht_b.Start() + + err = dht_a.Connect(addr_b) + if err != nil { + t.Fatal(err) + } + + err = dht_a.PutValue("hello", []byte("world")) + if err != nil { + t.Fatal(err) + } + + val, err := dht_a.GetValue("hello", time.Second * 2) + if err != nil { + t.Fatal(err) + } + + if string(val) != "world" { + t.Fatalf("Expected 'world' got %s", string(val)) + } } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 66e27e369..2b37192cd 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -4,6 +4,8 @@ import ( "math/rand" "time" + proto "code.google.com/p/goprotobuf/proto" + peer "github.com/jbenet/go-ipfs/peer" swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" @@ -22,21 +24,20 @@ func GenerateMessageID() uint64 { func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { var p *peer.Peer p = s.routes.NearestPeer(convertKey(key)) + if p == nil { + u.POut("nbuckets: %d", len(s.routes.Buckets)) + u.POut("%d", s.routes.Buckets[0].Len()) + panic("Table returned nil peer!") + } - pmes_type := DHTMessage_PUT_VALUE - str_key := string(key) - mes_id := GenerateMessageID() - - pmes := new(DHTMessage) - pmes.Type = &pmes_type - pmes.Key = &str_key - pmes.Value = value - pmes.Id = &mes_id - - mes := new(swarm.Message) - mes.Data = []byte(pmes.String()) - mes.Peer = p + pmes := pDHTMessage{ + Type: DHTMessage_PUT_VALUE, + Key: string(key), + Value: value, + Id: GenerateMessageID(), + } + mes := swarm.NewMessage(p, pmes.ToProtobuf()) s.network.Chan.Outgoing <- mes return nil } @@ -45,21 +46,19 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { var p *peer.Peer p = s.routes.NearestPeer(convertKey(key)) + if p == nil { + panic("Table returned nil peer!") + } - str_key := string(key) - mes_type := DHTMessage_GET_VALUE - mes_id := GenerateMessageID() - // protobuf structure - pmes := new(DHTMessage) - pmes.Type = &mes_type - pmes.Key = &str_key - pmes.Id = &mes_id - - mes := new(swarm.Message) - mes.Data = []byte(pmes.String()) - mes.Peer = p + pmes := pDHTMessage{ + Type: DHTMessage_GET_VALUE, + Key: string(key), + Id: GenerateMessageID(), + } + response_chan := s.ListenFor(pmes.Id) - response_chan := s.ListenFor(*pmes.Id) + mes := swarm.NewMessage(p, pmes.ToProtobuf()) + s.network.Chan.Outgoing <- mes // Wait for either the response or a timeout timeup := time.After(timeout) @@ -68,7 +67,12 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { // TODO: unregister listener return nil, u.ErrTimeout case resp := <-response_chan: - return resp.Data, nil + pmes_out := new(DHTMessage) + err := proto.Unmarshal(resp.Data, pmes_out) + if err != nil { + return nil,err + } + return pmes_out.GetValue(), nil } } diff --git a/routing/dht/table.go b/routing/dht/table.go index c1eed534b..17af95756 100644 --- a/routing/dht/table.go +++ b/routing/dht/table.go @@ -49,7 +49,7 @@ func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer { new_bucket := bucket.Split(b_id, rt.local) rt.Buckets = append(rt.Buckets, new_bucket) if new_bucket.Len() > rt.bucketsize { - // This is another very rare and annoying case + // TODO: This is a very rare and annoying case panic("Case not handled.") } @@ -87,10 +87,27 @@ func (p peerSorterArr) Less(a, b int) bool { } // +func (rt *RoutingTable) copyPeersFromList(peerArr peerSorterArr, peerList *list.List) peerSorterArr { + for e := peerList.Front(); e != nil; e = e.Next() { + p := e.Value.(*peer.Peer) + p_id := convertPeerID(p.ID) + pd := peerDistance{ + p: p, + distance: xor(rt.local, p_id), + } + peerArr = append(peerArr, &pd) + } + return peerArr +} + // Returns a single peer that is nearest to the given ID func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer { peers := rt.NearestPeers(id, 1) - return peers[0] + if len(peers) > 0 { + return peers[0] + } else { + return nil + } } // Returns a list of the 'count' closest peers to the given ID @@ -100,26 +117,26 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { // Get bucket at cpl index or last bucket var bucket *Bucket if cpl >= len(rt.Buckets) { - bucket = rt.Buckets[len(rt.Buckets) - 1] - } else { - bucket = rt.Buckets[cpl] + cpl = len(rt.Buckets) - 1 } + bucket = rt.Buckets[cpl] + var peerArr peerSorterArr if bucket.Len() == 0 { - // This can happen, very rarely. - panic("Case not yet implemented.") - } + // In the case of an unusual split, one bucket may be empty. + // if this happens, search both surrounding buckets for nearest peer + if cpl > 0 { + plist := (*list.List)(rt.Buckets[cpl - 1]) + peerArr = rt.copyPeersFromList(peerArr, plist) + } - var peerArr peerSorterArr - plist := (*list.List)(bucket) - for e := plist.Front();e != nil; e = e.Next() { - p := e.Value.(*peer.Peer) - p_id := convertPeerID(p.ID) - pd := peerDistance{ - p: p, - distance: xor(rt.local, p_id), + if cpl < len(rt.Buckets) - 1 { + plist := (*list.List)(rt.Buckets[cpl + 1]) + peerArr = rt.copyPeersFromList(peerArr, plist) } - peerArr = append(peerArr, &pd) + } else { + plist := (*list.List)(bucket) + peerArr = rt.copyPeersFromList(peerArr, plist) } // Sort by distance to local peer From 27c08bd681aa6f436f6fae7690ea0096621488d4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 3 Aug 2014 21:46:01 -0700 Subject: [PATCH 0033/3817] working towards Providers implementation This commit was moved from ipfs/go-ipfs-routing@860a2a20f0925e7310f6fa57e37bd56184194c61 --- routing/dht/dht.go | 42 +++++++++++++++++++++-- routing/dht/routing.go | 78 ++++++++++++++++++++++++++++++++++++++---- routing/routing.go | 2 +- 3 files changed, 112 insertions(+), 10 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 02ef8c4f7..e0c665051 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -3,6 +3,7 @@ package dht import ( "sync" "time" + "encoding/json" peer "github.com/jbenet/go-ipfs/peer" swarm "github.com/jbenet/go-ipfs/swarm" @@ -31,6 +32,10 @@ type IpfsDHT struct { // Local data datastore ds.Datastore + // Map keys to peers that can provide their value + // TODO: implement a TTL on each of these keys + providers map[u.Key][]*peer.Peer + // map of channels waiting for reply messages listeners map[uint64]chan *swarm.Message listenLock sync.RWMutex @@ -185,11 +190,44 @@ func (dht *IpfsDHT) handleFindNode(p *peer.Peer, pmes *DHTMessage) { } func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *DHTMessage) { - panic("Not implemented.") + providers := dht.providers[u.Key(pmes.GetKey())] + if providers == nil || len(providers) == 0 { + // ????? + } + + var addrs []string + for _,prov := range providers { + ma := prov.NetAddress("tcp") + str,err := ma.String() + if err != nil { + u.PErr("Error: %s", err) + continue + } + + addrs = append(addrs, str) + } + + data,err := json.Marshal(addrs) + if err != nil { + panic(err) + } + + resp := pDHTMessage{ + Type: DHTMessage_GET_PROVIDERS, + Key: pmes.GetKey(), + Value: data, + Id: pmes.GetId(), + } + + mes := swarm.NewMessage(p, resp.ToProtobuf()) + dht.network.Chan.Outgoing <-mes } func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *DHTMessage) { - panic("Not implemented.") + //TODO: need to implement TTLs on providers + key := u.Key(pmes.GetKey()) + parr := dht.providers[key] + dht.providers[key] = append(parr, p) } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 2b37192cd..699242f56 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -11,6 +11,9 @@ import ( u "github.com/jbenet/go-ipfs/util" ) +// Pool size is the number of nodes used for group find/set RPC calls +var PoolSize = 6 + // TODO: determine a way of creating and managing message IDs func GenerateMessageID() uint64 { return uint64(rand.Uint32()) << 32 & uint64(rand.Uint32()) @@ -25,8 +28,6 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { var p *peer.Peer p = s.routes.NearestPeer(convertKey(key)) if p == nil { - u.POut("nbuckets: %d", len(s.routes.Buckets)) - u.POut("%d", s.routes.Buckets[0].Len()) panic("Table returned nil peer!") } @@ -64,7 +65,7 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { timeup := time.After(timeout) select { case <-timeup: - // TODO: unregister listener + s.Unlisten(pmes.Id) return nil, u.ErrTimeout case resp := <-response_chan: pmes_out := new(DHTMessage) @@ -81,17 +82,80 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { // Announce that this node can provide value for given key func (s *IpfsDHT) Provide(key u.Key) error { - return u.ErrNotImplemented + peers := s.routes.NearestPeers(convertKey(key), PoolSize) + if len(peers) == 0 { + //return an error + } + + pmes := pDHTMessage{ + Type: DHTMessage_ADD_PROVIDER, + Key: string(key), + } + pbmes := pmes.ToProtobuf() + + for _,p := range peers { + mes := swarm.NewMessage(p, pbmes) + s.network.Chan.Outgoing <-mes + } + return nil } // FindProviders searches for peers who can provide the value for given key. -func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) (*peer.Peer, error) { - return nil, u.ErrNotImplemented +func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) { + p := s.routes.NearestPeer(convertKey(key)) + + pmes := pDHTMessage{ + Type: DHTMessage_GET_PROVIDERS, + Key: string(key), + Id: GenerateMessageID(), + } + + mes := swarm.NewMessage(p, pmes.ToProtobuf()) + + listen_chan := s.ListenFor(pmes.Id) + s.network.Chan.Outgoing <-mes + after := time.After(timeout) + select { + case <-after: + s.Unlisten(pmes.Id) + return nil, u.ErrTimeout + case resp := <-listen_chan: + pmes_out := new(DHTMessage) + err := proto.Unmarshal(resp.Data, pmes_out) + if err != nil { + return nil, err + } + panic("Not yet implemented.") + } } // Find specific Peer // FindPeer searches for a peer with given ID. func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) { - return nil, u.ErrNotImplemented + p := s.routes.NearestPeer(convertPeerID(id)) + + pmes := pDHTMessage{ + Type: DHTMessage_FIND_NODE, + Key: string(id), + Id: GenerateMessageID(), + } + + mes := swarm.NewMessage(p, pmes.ToProtobuf()) + + listen_chan := s.ListenFor(pmes.Id) + s.network.Chan.Outgoing <-mes + after := time.After(timeout) + select { + case <-after: + s.Unlisten(pmes.Id) + return nil, u.ErrTimeout + case resp := <-listen_chan: + pmes_out := new(DHTMessage) + err := proto.Unmarshal(resp.Data, pmes_out) + if err != nil { + return nil, err + } + panic("Not yet implemented.") + } } diff --git a/routing/routing.go b/routing/routing.go index 933032f46..3826f13cb 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -25,7 +25,7 @@ type IpfsRouting interface { Provide(key u.Key) error // FindProviders searches for peers who can provide the value for given key. - FindProviders(key u.Key, timeout time.Duration) (*peer.Peer, error) + FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) // Find specific Peer From 4d64cdb924dcdb6b4e11e6feb431e68d83b3c90a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 5 Aug 2014 09:38:26 -0700 Subject: [PATCH 0034/3817] a little error handling and some work on providers This commit was moved from ipfs/go-ipfs-routing@94b1179309095720bddf9f86f75c4252b2432c02 --- routing/dht/dht.go | 16 ++++++++++++---- routing/dht/routing.go | 30 +++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index e0c665051..0341218ae 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -35,6 +35,7 @@ type IpfsDHT struct { // Map keys to peers that can provide their value // TODO: implement a TTL on each of these keys providers map[u.Key][]*peer.Peer + providerLock sync.RWMutex // map of channels waiting for reply messages listeners map[uint64]chan *swarm.Message @@ -46,6 +47,9 @@ type IpfsDHT struct { // Create a new DHT object with the given peer as the 'local' host func NewDHT(p *peer.Peer) (*IpfsDHT, error) { + if p == nil { + panic("Tried to create new dht with nil peer") + } network := swarm.NewSwarm(p) err := network.Listen() if err != nil { @@ -68,24 +72,27 @@ func (dht *IpfsDHT) Start() { } // Connect to a new peer at the given address -func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) error { +func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { + if addr == nil { + panic("addr was nil!") + } peer := new(peer.Peer) peer.AddAddress(addr) conn,err := swarm.Dial("tcp", peer) if err != nil { - return err + return nil, err } err = identify.Handshake(dht.self, peer, conn.Incoming.MsgChan, conn.Outgoing.MsgChan) if err != nil { - return err + return nil, err } dht.network.StartConn(conn) dht.routes.Update(peer) - return nil + return peer, nil } // Read in all messages from swarm and handle them appropriately @@ -195,6 +202,7 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *DHTMessage) { // ????? } + // This is just a quick hack, formalize method of sending addrs later var addrs []string for _,prov := range providers { ma := prov.NetAddress("tcp") diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 699242f56..0180998d9 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -3,9 +3,12 @@ package dht import ( "math/rand" "time" + "encoding/json" proto "code.google.com/p/goprotobuf/proto" + ma "github.com/jbenet/go-multiaddr" + peer "github.com/jbenet/go-ipfs/peer" swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" @@ -125,7 +128,32 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, if err != nil { return nil, err } - panic("Not yet implemented.") + var addrs map[string]string + err := json.Unmarshal(pmes_out.GetValue(), &addrs) + if err != nil { + return nil, err + } + + for key,addr := range addrs { + p := s.network.Find(u.Key(key)) + if p == nil { + maddr,err := ma.NewMultiaddr(addr) + if err != nil { + u.PErr("error connecting to new peer: %s", err) + continue + } + p, err := s.Connect(maddr) + if err != nil { + u.PErr("error connecting to new peer: %s", err) + continue + } + } + s.providerLock.Lock() + prov_arr := s.providers[key] + s.providers[key] = append(prov_arr, p) + s.providerLock.Unlock() + } + } } From 3417315188e2f7381dfde444566590b5b19e02e7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 5 Aug 2014 18:32:22 -0700 Subject: [PATCH 0035/3817] providers interface is coming along nicely This commit was moved from ipfs/go-ipfs-routing@5484d862e8d1ad1945ceee78dda8be3ce1c1e5cd --- routing/dht/dht.go | 39 +++++++++++++++++++++++++++----------- routing/dht/pDHTMessage.go | 11 +++++++++++ routing/dht/routing.go | 24 +++++++++++++---------- 3 files changed, 53 insertions(+), 21 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 0341218ae..c12e35190 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -61,6 +61,7 @@ func NewDHT(p *peer.Peer) (*IpfsDHT, error) { dht.datastore = ds.NewMapDatastore() dht.self = p dht.listeners = make(map[uint64]chan *swarm.Message) + dht.providers = make(map[u.Key][]*peer.Peer) dht.shutdown = make(chan struct{}) dht.routes = NewRoutingTable(20, convertPeerID(p.ID)) return dht, nil @@ -101,9 +102,11 @@ func (dht *IpfsDHT) handleMessages() { u.DOut("Begin message handling routine") for { select { - case mes := <-dht.network.Chan.Incoming: - u.DOut("recieved message from swarm.") - + case mes,ok := <-dht.network.Chan.Incoming: + if !ok { + u.DOut("handleMessages closing, bad recv on incoming") + return + } pmes := new(DHTMessage) err := proto.Unmarshal(mes.Data, pmes) if err != nil { @@ -121,15 +124,16 @@ func (dht *IpfsDHT) handleMessages() { dht.listenLock.RUnlock() if ok { ch <- mes + } else { + // this is expected behaviour during a timeout + u.DOut("Received response with nobody listening...") } - // this is expected behaviour during a timeout - u.DOut("Received response with nobody listening...") continue } // - u.DOut("Got message type: %d", pmes.GetType()) + u.DOut("Got message type: '%s' [id = %x]", mesNames[pmes.GetType()], pmes.GetId()) switch pmes.GetType() { case DHTMessage_GET_VALUE: dht.handleGetValue(mes.Peer, pmes) @@ -138,13 +142,15 @@ func (dht *IpfsDHT) handleMessages() { case DHTMessage_FIND_NODE: dht.handleFindNode(mes.Peer, pmes) case DHTMessage_ADD_PROVIDER: + dht.handleAddProvider(mes.Peer, pmes) case DHTMessage_GET_PROVIDERS: + dht.handleGetProviders(mes.Peer, pmes) case DHTMessage_PING: dht.handlePing(mes.Peer, pmes) } case err := <-dht.network.Chan.Errors: - panic(err) + u.DErr("dht err: %s", err) case <-dht.shutdown: return } @@ -197,13 +203,16 @@ func (dht *IpfsDHT) handleFindNode(p *peer.Peer, pmes *DHTMessage) { } func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *DHTMessage) { + dht.providerLock.RLock() providers := dht.providers[u.Key(pmes.GetKey())] + dht.providerLock.RUnlock() if providers == nil || len(providers) == 0 { // ????? + u.DOut("No known providers for requested key.") } // This is just a quick hack, formalize method of sending addrs later - var addrs []string + addrs := make(map[u.Key]string) for _,prov := range providers { ma := prov.NetAddress("tcp") str,err := ma.String() @@ -212,7 +221,7 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *DHTMessage) { continue } - addrs = append(addrs, str) + addrs[prov.Key()] = str } data,err := json.Marshal(addrs) @@ -225,6 +234,7 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *DHTMessage) { Key: pmes.GetKey(), Value: data, Id: pmes.GetId(), + Response: true, } mes := swarm.NewMessage(p, resp.ToProtobuf()) @@ -234,8 +244,7 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *DHTMessage) { func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *DHTMessage) { //TODO: need to implement TTLs on providers key := u.Key(pmes.GetKey()) - parr := dht.providers[key] - dht.providers[key] = append(parr, p) + dht.addProviderEntry(key, p) } @@ -290,3 +299,11 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { return u.ErrTimeout } } + +func (dht *IpfsDHT) addProviderEntry(key u.Key, p *peer.Peer) { + u.DOut("Adding %s as provider for '%s'", p.Key().Pretty(), key) + dht.providerLock.Lock() + provs := dht.providers[key] + dht.providers[key] = append(provs, p) + dht.providerLock.Unlock() +} diff --git a/routing/dht/pDHTMessage.go b/routing/dht/pDHTMessage.go index 65c03b1f8..8b862dbc9 100644 --- a/routing/dht/pDHTMessage.go +++ b/routing/dht/pDHTMessage.go @@ -9,6 +9,17 @@ type pDHTMessage struct { Id uint64 } +var mesNames [10]string + +func init() { + mesNames[DHTMessage_ADD_PROVIDER] = "add provider" + mesNames[DHTMessage_FIND_NODE] = "find node" + mesNames[DHTMessage_GET_PROVIDERS] = "get providers" + mesNames[DHTMessage_GET_VALUE] = "get value" + mesNames[DHTMessage_PUT_VALUE] = "put value" + mesNames[DHTMessage_PING] = "ping" +} + func (m *pDHTMessage) ToProtobuf() *DHTMessage { pmes := new(DHTMessage) if m.Value != nil { diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 0180998d9..cbe0e9246 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -19,7 +19,8 @@ var PoolSize = 6 // TODO: determine a way of creating and managing message IDs func GenerateMessageID() uint64 { - return uint64(rand.Uint32()) << 32 & uint64(rand.Uint32()) + //return (uint64(rand.Uint32()) << 32) & uint64(rand.Uint32()) + return uint64(rand.Uint32()) } // This file implements the Routing interface for the IpfsDHT struct. @@ -116,6 +117,7 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, mes := swarm.NewMessage(p, pmes.ToProtobuf()) listen_chan := s.ListenFor(pmes.Id) + u.DOut("Find providers for: '%s'", key) s.network.Chan.Outgoing <-mes after := time.After(timeout) select { @@ -123,37 +125,39 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, s.Unlisten(pmes.Id) return nil, u.ErrTimeout case resp := <-listen_chan: + u.DOut("FindProviders: got response.") pmes_out := new(DHTMessage) err := proto.Unmarshal(resp.Data, pmes_out) if err != nil { return nil, err } - var addrs map[string]string - err := json.Unmarshal(pmes_out.GetValue(), &addrs) + var addrs map[u.Key]string + err = json.Unmarshal(pmes_out.GetValue(), &addrs) if err != nil { return nil, err } - for key,addr := range addrs { - p := s.network.Find(u.Key(key)) + var prov_arr []*peer.Peer + for pid,addr := range addrs { + p := s.network.Find(pid) if p == nil { maddr,err := ma.NewMultiaddr(addr) if err != nil { u.PErr("error connecting to new peer: %s", err) continue } - p, err := s.Connect(maddr) + p, err = s.Connect(maddr) if err != nil { u.PErr("error connecting to new peer: %s", err) continue } } - s.providerLock.Lock() - prov_arr := s.providers[key] - s.providers[key] = append(prov_arr, p) - s.providerLock.Unlock() + s.addProviderEntry(key, p) + prov_arr = append(prov_arr, p) } + return prov_arr, nil + } } From 2897e072a0f0ff23afabdb64953706fce581c9cd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 5 Aug 2014 20:31:48 -0700 Subject: [PATCH 0036/3817] implement find peer rpc This commit was moved from ipfs/go-ipfs-routing@bedc1d551550a475298911c7767b168b3d1a72d4 --- routing/dht/dht.go | 65 +++++++++++++++++++++++++++++++++--------- routing/dht/routing.go | 8 +++++- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index c12e35190..c2c1b63be 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -73,6 +73,7 @@ func (dht *IpfsDHT) Start() { } // Connect to a new peer at the given address +// TODO: move this into swarm func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { if addr == nil { panic("addr was nil!") @@ -90,9 +91,21 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { return nil, err } + // Send node an address that you can be reached on + myaddr := dht.self.NetAddress("tcp") + mastr,err := myaddr.String() + if err != nil { + panic("No local address to send") + } + + conn.Outgoing.MsgChan <- []byte(mastr) + dht.network.StartConn(conn) - dht.routes.Update(peer) + removed := dht.routes.Update(peer) + if removed != nil { + panic("need to remove this peer.") + } return peer, nil } @@ -115,7 +128,10 @@ func (dht *IpfsDHT) handleMessages() { } // Update peers latest visit in routing table - dht.routes.Update(mes.Peer) + removed := dht.routes.Update(mes.Peer) + if removed != nil { + panic("Need to handle removed peer.") + } // Note: not sure if this is the correct place for this if pmes.GetResponse() { @@ -140,7 +156,7 @@ func (dht *IpfsDHT) handleMessages() { case DHTMessage_PUT_VALUE: dht.handlePutValue(mes.Peer, pmes) case DHTMessage_FIND_NODE: - dht.handleFindNode(mes.Peer, pmes) + dht.handleFindPeer(mes.Peer, pmes) case DHTMessage_ADD_PROVIDER: dht.handleAddProvider(mes.Peer, pmes) case DHTMessage_GET_PROVIDERS: @@ -171,14 +187,14 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *DHTMessage) { mes := swarm.NewMessage(p, resp.ToProtobuf()) dht.network.Chan.Outgoing <- mes } else if err == ds.ErrNotFound { - // Find closest node(s) to desired key and reply with that info + // Find closest peer(s) to desired key and reply with that info // TODO: this will need some other metadata in the protobuf message - // to signal to the querying node that the data its receiving - // is actually a list of other nodes + // to signal to the querying peer that the data its receiving + // is actually a list of other peer } } -// Store a value in this nodes local storage +// Store a value in this peer local storage func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *DHTMessage) { dskey := ds.NewKey(pmes.GetKey()) err := dht.datastore.Put(dskey, pmes.GetValue()) @@ -189,7 +205,7 @@ func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *DHTMessage) { } func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *DHTMessage) { - resp := &pDHTMessage{ + resp := pDHTMessage{ Type: pmes.GetType(), Response: true, Id: pmes.GetId(), @@ -198,8 +214,29 @@ func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *DHTMessage) { dht.network.Chan.Outgoing <-swarm.NewMessage(p, resp.ToProtobuf()) } -func (dht *IpfsDHT) handleFindNode(p *peer.Peer, pmes *DHTMessage) { - panic("Not implemented.") +func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *DHTMessage) { + closest := dht.routes.NearestPeer(convertKey(u.Key(pmes.GetKey()))) + if closest == nil { + } + + if len(closest.Addresses) == 0 { + panic("no addresses for connected peer...") + } + + addr,err := closest.Addresses[0].String() + if err != nil { + panic(err) + } + + resp := pDHTMessage{ + Type: pmes.GetType(), + Response: true, + Id: pmes.GetId(), + Value: []byte(addr), + } + + mes := swarm.NewMessage(p, resp.ToProtobuf()) + dht.network.Chan.Outgoing <-mes } func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *DHTMessage) { @@ -269,13 +306,13 @@ func (dht *IpfsDHT) Unlisten(mesid uint64) { close(ch) } -// Stop all communications from this node and shut down +// Stop all communications from this peer and shut down func (dht *IpfsDHT) Halt() { dht.shutdown <- struct{}{} dht.network.Close() } -// Ping a node, log the time it took +// Ping a peer, log the time it took func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { // Thoughts: maybe this should accept an ID and do a peer lookup? u.DOut("Enter Ping.") @@ -294,8 +331,8 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { u.POut("Ping took %s.", roundtrip.String()) return nil case <-tout: - // Timed out, think about removing node from network - u.DOut("Ping node timed out.") + // Timed out, think about removing peer from network + u.DOut("Ping peer timed out.") return u.ErrTimeout } } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index cbe0e9246..138a0ee92 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -188,6 +188,12 @@ func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error if err != nil { return nil, err } - panic("Not yet implemented.") + addr := string(pmes_out.GetValue()) + maddr, err := ma.NewMultiaddr(addr) + if err != nil { + return nil, err + } + + return s.Connect(maddr) } } From c8794ded008dbbbdcc6e778c68be2b4c8a60859f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 6 Aug 2014 10:02:53 -0700 Subject: [PATCH 0037/3817] fix bug in routing table lookups This commit was moved from ipfs/go-ipfs-routing@1d8385d1fc3ded2ab31dac191a0df6674e41e878 --- routing/dht/dht.go | 14 +++++++++++++- routing/dht/dht_test.go | 4 ++-- routing/dht/messages.pb.go | 17 +++++++++-------- routing/dht/messages.proto | 1 + routing/dht/pDHTMessage.go | 11 ----------- routing/dht/routing.go | 13 ++++++++++++- routing/dht/table.go | 24 +++++++++++++++++++----- routing/dht/table_test.go | 17 +++++++++++++++++ 8 files changed, 73 insertions(+), 28 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index c2c1b63be..9b4854cfe 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -106,6 +106,14 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { if removed != nil { panic("need to remove this peer.") } + + // Ping new peer to register in their routing table + // NOTE: this should be done better... + err = dht.Ping(peer, time.Second * 2) + if err != nil { + panic("Failed to ping new peer.") + } + return peer, nil } @@ -149,7 +157,7 @@ func (dht *IpfsDHT) handleMessages() { } // - u.DOut("Got message type: '%s' [id = %x]", mesNames[pmes.GetType()], pmes.GetId()) + u.DOut("Got message type: '%s' [id = %x]", DHTMessage_MessageType_name[int32(pmes.GetType())], pmes.GetId()) switch pmes.GetType() { case DHTMessage_GET_VALUE: dht.handleGetValue(mes.Peer, pmes) @@ -215,14 +223,18 @@ func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *DHTMessage) { } func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *DHTMessage) { + u.POut("handleFindPeer: searching for '%s'", peer.ID(pmes.GetKey()).Pretty()) closest := dht.routes.NearestPeer(convertKey(u.Key(pmes.GetKey()))) if closest == nil { + panic("could not find anything.") } if len(closest.Addresses) == 0 { panic("no addresses for connected peer...") } + u.POut("handleFindPeer: sending back '%s'", closest.ID.Pretty()) + addr,err := closest.Addresses[0].String() if err != nil { panic(err) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 0c4b7ee09..6217c29f2 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -45,7 +45,7 @@ func TestPing(t *testing.T) { dht_a.Start() dht_b.Start() - err = dht_a.Connect(addr_b) + _,err = dht_a.Connect(addr_b) if err != nil { t.Fatal(err) } @@ -92,7 +92,7 @@ func TestValueGetSet(t *testing.T) { dht_a.Start() dht_b.Start() - err = dht_a.Connect(addr_b) + _,err = dht_a.Connect(addr_b) if err != nil { t.Fatal(err) } diff --git a/routing/dht/messages.pb.go b/routing/dht/messages.pb.go index 3283ef4e2..e95f487c1 100644 --- a/routing/dht/messages.pb.go +++ b/routing/dht/messages.pb.go @@ -29,6 +29,7 @@ const ( DHTMessage_GET_PROVIDERS DHTMessage_MessageType = 3 DHTMessage_FIND_NODE DHTMessage_MessageType = 4 DHTMessage_PING DHTMessage_MessageType = 5 + DHTMessage_DIAGNOSTIC DHTMessage_MessageType = 6 ) var DHTMessage_MessageType_name = map[int32]string{ @@ -38,6 +39,7 @@ var DHTMessage_MessageType_name = map[int32]string{ 3: "GET_PROVIDERS", 4: "FIND_NODE", 5: "PING", + 6: "DIAGNOSTIC", } var DHTMessage_MessageType_value = map[string]int32{ "PUT_VALUE": 0, @@ -46,6 +48,7 @@ var DHTMessage_MessageType_value = map[string]int32{ "GET_PROVIDERS": 3, "FIND_NODE": 4, "PING": 5, + "DIAGNOSTIC": 6, } func (x DHTMessage_MessageType) Enum() *DHTMessage_MessageType { @@ -66,14 +69,12 @@ func (x *DHTMessage_MessageType) UnmarshalJSON(data []byte) error { } type DHTMessage struct { - Type *DHTMessage_MessageType `protobuf:"varint,1,req,name=type,enum=dht.DHTMessage_MessageType" json:"type,omitempty"` - Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` - Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` - // Unique ID of this message, used to match queries with responses - Id *uint64 `protobuf:"varint,4,req,name=id" json:"id,omitempty"` - // Signals whether or not this message is a response to another message - Response *bool `protobuf:"varint,5,opt,name=response" json:"response,omitempty"` - XXX_unrecognized []byte `json:"-"` + Type *DHTMessage_MessageType `protobuf:"varint,1,req,name=type,enum=dht.DHTMessage_MessageType" json:"type,omitempty"` + Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` + Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` + Id *uint64 `protobuf:"varint,4,req,name=id" json:"id,omitempty"` + Response *bool `protobuf:"varint,5,opt,name=response" json:"response,omitempty"` + XXX_unrecognized []byte `json:"-"` } func (m *DHTMessage) Reset() { *m = DHTMessage{} } diff --git a/routing/dht/messages.proto b/routing/dht/messages.proto index d873c7559..a9a7fd3c6 100644 --- a/routing/dht/messages.proto +++ b/routing/dht/messages.proto @@ -10,6 +10,7 @@ message DHTMessage { GET_PROVIDERS = 3; FIND_NODE = 4; PING = 5; + DIAGNOSTIC = 6; } required MessageType type = 1; diff --git a/routing/dht/pDHTMessage.go b/routing/dht/pDHTMessage.go index 8b862dbc9..65c03b1f8 100644 --- a/routing/dht/pDHTMessage.go +++ b/routing/dht/pDHTMessage.go @@ -9,17 +9,6 @@ type pDHTMessage struct { Id uint64 } -var mesNames [10]string - -func init() { - mesNames[DHTMessage_ADD_PROVIDER] = "add provider" - mesNames[DHTMessage_FIND_NODE] = "find node" - mesNames[DHTMessage_GET_PROVIDERS] = "get providers" - mesNames[DHTMessage_GET_VALUE] = "get value" - mesNames[DHTMessage_PUT_VALUE] = "put value" - mesNames[DHTMessage_PING] = "ping" -} - func (m *pDHTMessage) ToProtobuf() *DHTMessage { pmes := new(DHTMessage) if m.Value != nil { diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 138a0ee92..489498350 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -194,6 +194,17 @@ func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error return nil, err } - return s.Connect(maddr) + found_peer, err := s.Connect(maddr) + if err != nil { + u.POut("Found peer but couldnt connect.") + return nil, err + } + + if !found_peer.ID.Equal(id) { + u.POut("FindPeer: searching for '%s' but found '%s'", id.Pretty(), found_peer.ID.Pretty()) + return found_peer, u.ErrSearchIncomplete + } + + return found_peer, nil } } diff --git a/routing/dht/table.go b/routing/dht/table.go index 17af95756..ce8fdbc24 100644 --- a/routing/dht/table.go +++ b/routing/dht/table.go @@ -1,10 +1,12 @@ package dht import ( + "encoding/hex" "container/list" "sort" peer "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" ) // RoutingTable defines the routing table. @@ -87,13 +89,13 @@ func (p peerSorterArr) Less(a, b int) bool { } // -func (rt *RoutingTable) copyPeersFromList(peerArr peerSorterArr, peerList *list.List) peerSorterArr { +func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) peerSorterArr { for e := peerList.Front(); e != nil; e = e.Next() { p := e.Value.(*peer.Peer) p_id := convertPeerID(p.ID) pd := peerDistance{ p: p, - distance: xor(rt.local, p_id), + distance: xor(target, p_id), } peerArr = append(peerArr, &pd) } @@ -112,6 +114,7 @@ func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer { // Returns a list of the 'count' closest peers to the given ID func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { + u.POut("Searching table, size = %d", rt.Size()) cpl := xor(id, rt.local).commonPrefixLen() // Get bucket at cpl index or last bucket @@ -127,16 +130,16 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { // if this happens, search both surrounding buckets for nearest peer if cpl > 0 { plist := (*list.List)(rt.Buckets[cpl - 1]) - peerArr = rt.copyPeersFromList(peerArr, plist) + peerArr = copyPeersFromList(id, peerArr, plist) } if cpl < len(rt.Buckets) - 1 { plist := (*list.List)(rt.Buckets[cpl + 1]) - peerArr = rt.copyPeersFromList(peerArr, plist) + peerArr = copyPeersFromList(id, peerArr, plist) } } else { plist := (*list.List)(bucket) - peerArr = rt.copyPeersFromList(peerArr, plist) + peerArr = copyPeersFromList(id, peerArr, plist) } // Sort by distance to local peer @@ -145,7 +148,18 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { var out []*peer.Peer for i := 0; i < count && i < peerArr.Len(); i++ { out = append(out, peerArr[i].p) + u.POut("peer out: %s - %s", peerArr[i].p.ID.Pretty(), + hex.EncodeToString(xor(id, convertPeerID(peerArr[i].p.ID)))) } return out } + +// Returns the total number of peers in the routing table +func (rt *RoutingTable) Size() int { + var tot int + for _,buck := range rt.Buckets { + tot += buck.Len() + } + return tot +} diff --git a/routing/dht/table_test.go b/routing/dht/table_test.go index cb52bd1a0..debec5e16 100644 --- a/routing/dht/table_test.go +++ b/routing/dht/table_test.go @@ -90,3 +90,20 @@ func TestTableUpdate(t *testing.T) { } } } + +func TestTableFind(t *testing.T) { + local := _randPeer() + rt := NewRoutingTable(10, convertPeerID(local.ID)) + + peers := make([]*peer.Peer, 100) + for i := 0; i < 5; i++ { + peers[i] = _randPeer() + rt.Update(peers[i]) + } + + t.Logf("Searching for peer: '%s'", peers[2].ID.Pretty()) + found := rt.NearestPeer(convertPeerID(peers[2].ID)) + if !found.ID.Equal(peers[2].ID) { + t.Fatalf("Failed to lookup known node...") + } +} From 781aab21b4a8ce40304e025852d33c6e23f417be Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 6 Aug 2014 18:37:45 -0700 Subject: [PATCH 0038/3817] worked on gathering data for diagnostic messages and some other misc cleanup This commit was moved from ipfs/go-ipfs-routing@03eef3e3bcb87a405dd86654f5609301c6e429de --- routing/dht/bucket.go | 5 +++ routing/dht/dht.go | 87 +++++++++++++++++++++----------------- routing/dht/dht_test.go | 4 -- routing/dht/messages.pb.go | 8 ++++ routing/dht/messages.proto | 1 + routing/dht/pDHTMessage.go | 2 + routing/dht/routing.go | 27 ++++++++++++ routing/dht/table.go | 16 ++++--- routing/dht/util.go | 4 +- 9 files changed, 105 insertions(+), 49 deletions(-) diff --git a/routing/dht/bucket.go b/routing/dht/bucket.go index 120ed29a4..996d299d9 100644 --- a/routing/dht/bucket.go +++ b/routing/dht/bucket.go @@ -61,3 +61,8 @@ func (b *Bucket) Split(cpl int, target ID) *Bucket { } return (*Bucket)(out) } + +func (b *Bucket) getIter() *list.Element { + bucket_list := (*list.List)(b) + return bucket_list.Front() +} diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 9b4854cfe..44a56831b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -34,7 +34,7 @@ type IpfsDHT struct { // Map keys to peers that can provide their value // TODO: implement a TTL on each of these keys - providers map[u.Key][]*peer.Peer + providers map[u.Key][]*providerInfo providerLock sync.RWMutex // map of channels waiting for reply messages @@ -43,6 +43,9 @@ type IpfsDHT struct { // Signal to shutdown dht shutdown chan struct{} + + // When this peer started up + birth time.Time } // Create a new DHT object with the given peer as the 'local' host @@ -61,9 +64,10 @@ func NewDHT(p *peer.Peer) (*IpfsDHT, error) { dht.datastore = ds.NewMapDatastore() dht.self = p dht.listeners = make(map[uint64]chan *swarm.Message) - dht.providers = make(map[u.Key][]*peer.Peer) + dht.providers = make(map[u.Key][]*providerInfo) dht.shutdown = make(chan struct{}) dht.routes = NewRoutingTable(20, convertPeerID(p.ID)) + dht.birth = time.Now() return dht, nil } @@ -121,6 +125,8 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { // NOTE: this function is just a quick sketch func (dht *IpfsDHT) handleMessages() { u.DOut("Begin message handling routine") + + checkTimeouts := time.NewTicker(time.Minute * 5) for { select { case mes,ok := <-dht.network.Chan.Incoming: @@ -157,7 +163,10 @@ func (dht *IpfsDHT) handleMessages() { } // - u.DOut("Got message type: '%s' [id = %x]", DHTMessage_MessageType_name[int32(pmes.GetType())], pmes.GetId()) + u.DOut("[peer: %s]", dht.self.ID.Pretty()) + u.DOut("Got message type: '%s' [id = %x, from = %s]", + DHTMessage_MessageType_name[int32(pmes.GetType())], + pmes.GetId(), mes.Peer.ID.Pretty()) switch pmes.GetType() { case DHTMessage_GET_VALUE: dht.handleGetValue(mes.Peer, pmes) @@ -171,35 +180,57 @@ func (dht *IpfsDHT) handleMessages() { dht.handleGetProviders(mes.Peer, pmes) case DHTMessage_PING: dht.handlePing(mes.Peer, pmes) + case DHTMessage_DIAGNOSTIC: + // TODO: network diagnostic messages } case err := <-dht.network.Chan.Errors: u.DErr("dht err: %s", err) case <-dht.shutdown: + checkTimeouts.Stop() return + case <-checkTimeouts.C: + dht.providerLock.Lock() + for k,parr := range dht.providers { + var cleaned []*providerInfo + for _,v := range parr { + if time.Since(v.Creation) < time.Hour { + cleaned = append(cleaned, v) + } + } + dht.providers[k] = cleaned + } + dht.providerLock.Unlock() } } } func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *DHTMessage) { dskey := ds.NewKey(pmes.GetKey()) + var resp *pDHTMessage i_val, err := dht.datastore.Get(dskey) if err == nil { - resp := &pDHTMessage{ + resp = &pDHTMessage{ Response: true, Id: *pmes.Id, Key: *pmes.Key, Value: i_val.([]byte), + Success: true, } - - mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.network.Chan.Outgoing <- mes } else if err == ds.ErrNotFound { // Find closest peer(s) to desired key and reply with that info - // TODO: this will need some other metadata in the protobuf message - // to signal to the querying peer that the data its receiving - // is actually a list of other peer + closer := dht.routes.NearestPeer(convertKey(u.Key(pmes.GetKey()))) + resp = &pDHTMessage{ + Response: true, + Id: *pmes.Id, + Key: *pmes.Key, + Value: closer.ID, + Success: false, + } } + + mes := swarm.NewMessage(p, resp.ToProtobuf()) + dht.network.Chan.Outgoing <- mes } // Store a value in this peer local storage @@ -263,14 +294,14 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *DHTMessage) { // This is just a quick hack, formalize method of sending addrs later addrs := make(map[u.Key]string) for _,prov := range providers { - ma := prov.NetAddress("tcp") + ma := prov.Value.NetAddress("tcp") str,err := ma.String() if err != nil { u.PErr("Error: %s", err) continue } - addrs[prov.Key()] = str + addrs[prov.Value.Key()] = str } data,err := json.Marshal(addrs) @@ -290,6 +321,11 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *DHTMessage) { dht.network.Chan.Outgoing <-mes } +type providerInfo struct { + Creation time.Time + Value *peer.Peer +} + func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *DHTMessage) { //TODO: need to implement TTLs on providers key := u.Key(pmes.GetKey()) @@ -324,35 +360,10 @@ func (dht *IpfsDHT) Halt() { dht.network.Close() } -// Ping a peer, log the time it took -func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { - // Thoughts: maybe this should accept an ID and do a peer lookup? - u.DOut("Enter Ping.") - - pmes := pDHTMessage{Id: GenerateMessageID(), Type: DHTMessage_PING} - mes := swarm.NewMessage(p, pmes.ToProtobuf()) - - before := time.Now() - response_chan := dht.ListenFor(pmes.Id) - dht.network.Chan.Outgoing <- mes - - tout := time.After(timeout) - select { - case <-response_chan: - roundtrip := time.Since(before) - u.POut("Ping took %s.", roundtrip.String()) - return nil - case <-tout: - // Timed out, think about removing peer from network - u.DOut("Ping peer timed out.") - return u.ErrTimeout - } -} - func (dht *IpfsDHT) addProviderEntry(key u.Key, p *peer.Peer) { u.DOut("Adding %s as provider for '%s'", p.Key().Pretty(), key) dht.providerLock.Lock() provs := dht.providers[key] - dht.providers[key] = append(provs, p) + dht.providers[key] = append(provs, &providerInfo{time.Now(), p}) dht.providerLock.Unlock() } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 6217c29f2..b57ca3f7b 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -6,13 +6,9 @@ import ( ma "github.com/jbenet/go-multiaddr" u "github.com/jbenet/go-ipfs/util" - "fmt" - "time" ) -var _ = fmt.Println - func TestPing(t *testing.T) { u.Debug = false addr_a,err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1234") diff --git a/routing/dht/messages.pb.go b/routing/dht/messages.pb.go index e95f487c1..4f427efa1 100644 --- a/routing/dht/messages.pb.go +++ b/routing/dht/messages.pb.go @@ -74,6 +74,7 @@ type DHTMessage struct { Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` Id *uint64 `protobuf:"varint,4,req,name=id" json:"id,omitempty"` Response *bool `protobuf:"varint,5,opt,name=response" json:"response,omitempty"` + Success *bool `protobuf:"varint,6,opt,name=success" json:"success,omitempty"` XXX_unrecognized []byte `json:"-"` } @@ -116,6 +117,13 @@ func (m *DHTMessage) GetResponse() bool { return false } +func (m *DHTMessage) GetSuccess() bool { + if m != nil && m.Success != nil { + return *m.Success + } + return false +} + func init() { proto.RegisterEnum("dht.DHTMessage_MessageType", DHTMessage_MessageType_name, DHTMessage_MessageType_value) } diff --git a/routing/dht/messages.proto b/routing/dht/messages.proto index a9a7fd3c6..278a95202 100644 --- a/routing/dht/messages.proto +++ b/routing/dht/messages.proto @@ -22,4 +22,5 @@ message DHTMessage { // Signals whether or not this message is a response to another message optional bool response = 5; + optional bool success = 6; } diff --git a/routing/dht/pDHTMessage.go b/routing/dht/pDHTMessage.go index 65c03b1f8..bfe37d35f 100644 --- a/routing/dht/pDHTMessage.go +++ b/routing/dht/pDHTMessage.go @@ -7,6 +7,7 @@ type pDHTMessage struct { Value []byte Response bool Id uint64 + Success bool } func (m *pDHTMessage) ToProtobuf() *DHTMessage { @@ -19,6 +20,7 @@ func (m *pDHTMessage) ToProtobuf() *DHTMessage { pmes.Key = &m.Key pmes.Response = &m.Response pmes.Id = &m.Id + pmes.Success = &m.Success return pmes } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 489498350..82c88960d 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -208,3 +208,30 @@ func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error return found_peer, nil } } + +// Ping a peer, log the time it took +func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { + // Thoughts: maybe this should accept an ID and do a peer lookup? + u.DOut("Enter Ping.") + + pmes := pDHTMessage{Id: GenerateMessageID(), Type: DHTMessage_PING} + mes := swarm.NewMessage(p, pmes.ToProtobuf()) + + before := time.Now() + response_chan := dht.ListenFor(pmes.Id) + dht.network.Chan.Outgoing <- mes + + tout := time.After(timeout) + select { + case <-response_chan: + roundtrip := time.Since(before) + p.Distance = roundtrip //TODO: This isnt threadsafe + u.POut("Ping took %s.", roundtrip.String()) + return nil + case <-tout: + // Timed out, think about removing peer from network + u.DOut("Ping peer timed out.") + dht.Unlisten(pmes.Id) + return u.ErrTimeout + } +} diff --git a/routing/dht/table.go b/routing/dht/table.go index ce8fdbc24..07ed70cb4 100644 --- a/routing/dht/table.go +++ b/routing/dht/table.go @@ -1,12 +1,10 @@ package dht import ( - "encoding/hex" "container/list" "sort" peer "github.com/jbenet/go-ipfs/peer" - u "github.com/jbenet/go-ipfs/util" ) // RoutingTable defines the routing table. @@ -114,7 +112,6 @@ func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer { // Returns a list of the 'count' closest peers to the given ID func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { - u.POut("Searching table, size = %d", rt.Size()) cpl := xor(id, rt.local).commonPrefixLen() // Get bucket at cpl index or last bucket @@ -148,8 +145,6 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { var out []*peer.Peer for i := 0; i < count && i < peerArr.Len(); i++ { out = append(out, peerArr[i].p) - u.POut("peer out: %s - %s", peerArr[i].p.ID.Pretty(), - hex.EncodeToString(xor(id, convertPeerID(peerArr[i].p.ID)))) } return out @@ -163,3 +158,14 @@ func (rt *RoutingTable) Size() int { } return tot } + +// NOTE: This is potentially unsafe... use at your own risk +func (rt *RoutingTable) listpeers() []*peer.Peer { + var peers []*peer.Peer + for _,buck := range rt.Buckets { + for e := buck.getIter(); e != nil; e = e.Next() { + peers = append(peers, e.Value.(*peer.Peer)) + } + } + return peers +} diff --git a/routing/dht/util.go b/routing/dht/util.go index eed8d9301..2adc8b765 100644 --- a/routing/dht/util.go +++ b/routing/dht/util.go @@ -11,8 +11,8 @@ import ( // ID for IpfsDHT should be a byte slice, to allow for simpler operations // (xor). DHT ids are based on the peer.IDs. // -// NOTE: peer.IDs are biased because they are multihashes (first bytes -// biased). Thus, may need to re-hash keys (uniform dist). TODO(jbenet) +// The type dht.ID signifies that its contents have been hashed from either a +// peer.ID or a util.Key. This unifies the keyspace type ID []byte func (id ID) Equal(other ID) bool { From 4667843ec190089fd587e971a86f3cad38fbf621 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 6 Aug 2014 21:36:56 -0700 Subject: [PATCH 0039/3817] fixing some race conditions This commit was moved from ipfs/go-ipfs-routing@55c2ff223dd325fbe64967896f63e76356bee0bd --- routing/dht/dht.go | 1 + routing/dht/routing.go | 10 ++++++++-- routing/dht/table.go | 20 +++++++++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 44a56831b..19dfac99c 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -186,6 +186,7 @@ func (dht *IpfsDHT) handleMessages() { case err := <-dht.network.Chan.Errors: u.DErr("dht err: %s", err) + panic(err) case <-dht.shutdown: checkTimeouts.Stop() return diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 82c88960d..22a7cf886 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -48,6 +48,8 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { } // GetValue searches for the value corresponding to given Key. +// If the search does not succeed, a multiaddr string of a closer peer is +// returned along with util.ErrSearchIncomplete func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { var p *peer.Peer p = s.routes.NearestPeer(convertKey(key)) @@ -77,7 +79,11 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { if err != nil { return nil,err } - return pmes_out.GetValue(), nil + if pmes_out.GetSuccess() { + return pmes_out.GetValue(), nil + } else { + return pmes_out.GetValue(), u.ErrSearchIncomplete + } } } @@ -225,7 +231,7 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { select { case <-response_chan: roundtrip := time.Since(before) - p.Distance = roundtrip //TODO: This isnt threadsafe + p.SetDistance(roundtrip) u.POut("Ping took %s.", roundtrip.String()) return nil case <-tout: diff --git a/routing/dht/table.go b/routing/dht/table.go index 07ed70cb4..b4be9c321 100644 --- a/routing/dht/table.go +++ b/routing/dht/table.go @@ -3,8 +3,10 @@ package dht import ( "container/list" "sort" + "sync" peer "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" ) // RoutingTable defines the routing table. @@ -13,6 +15,9 @@ type RoutingTable struct { // ID of the local peer local ID + // Blanket lock, refine later for better performance + tabLock sync.RWMutex + // kBuckets define all the fingers to other nodes. Buckets []*Bucket bucketsize int @@ -29,6 +34,8 @@ func NewRoutingTable(bucketsize int, local_id ID) *RoutingTable { // Update adds or moves the given peer to the front of its respective bucket // If a peer gets removed from a bucket, it is returned func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer { + rt.tabLock.Lock() + defer rt.tabLock.Unlock() peer_id := convertPeerID(p.ID) cpl := xor(peer_id, rt.local).commonPrefixLen() @@ -88,7 +95,11 @@ func (p peerSorterArr) Less(a, b int) bool { // func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) peerSorterArr { - for e := peerList.Front(); e != nil; e = e.Next() { + if peerList == nil { + return peerSorterArr{} + } + e := peerList.Front() + for ; e != nil; { p := e.Value.(*peer.Peer) p_id := convertPeerID(p.ID) pd := peerDistance{ @@ -96,6 +107,11 @@ func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) pe distance: xor(target, p_id), } peerArr = append(peerArr, &pd) + if e != nil { + u.POut("list element was nil.") + return peerArr + } + e = e.Next() } return peerArr } @@ -112,6 +128,8 @@ func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer { // Returns a list of the 'count' closest peers to the given ID func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { + rt.tabLock.RLock() + defer rt.tabLock.RUnlock() cpl := xor(id, rt.local).commonPrefixLen() // Get bucket at cpl index or last bucket From 3614ba52fa30086f544ddbc5c2da7375aab611e2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 7 Aug 2014 14:16:24 -0700 Subject: [PATCH 0040/3817] fixed small bug introduced during race condition frustration This commit was moved from ipfs/go-ipfs-routing@e5ebcf6ac89e4af177fb3aea9ecd84f3244cf2db --- routing/dht/dht.go | 100 ++++++++++++++++++++++++++++++++++------- routing/dht/routing.go | 71 ++++++++++++++++++++++++----- routing/dht/table.go | 9 +--- 3 files changed, 148 insertions(+), 32 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 19dfac99c..5791c3fed 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -3,6 +3,7 @@ package dht import ( "sync" "time" + "bytes" "encoding/json" peer "github.com/jbenet/go-ipfs/peer" @@ -22,7 +23,7 @@ import ( // IpfsDHT is an implementation of Kademlia with Coral and S/Kademlia modifications. // It is used to implement the base IpfsRouting module. type IpfsDHT struct { - routes *RoutingTable + routes []*RoutingTable network *swarm.Swarm @@ -38,7 +39,7 @@ type IpfsDHT struct { providerLock sync.RWMutex // map of channels waiting for reply messages - listeners map[uint64]chan *swarm.Message + listeners map[uint64]*listenInfo listenLock sync.RWMutex // Signal to shutdown dht @@ -46,6 +47,14 @@ type IpfsDHT struct { // When this peer started up birth time.Time + + //lock to make diagnostics work better + diaglock sync.Mutex +} + +type listenInfo struct { + resp chan *swarm.Message + count int } // Create a new DHT object with the given peer as the 'local' host @@ -63,10 +72,11 @@ func NewDHT(p *peer.Peer) (*IpfsDHT, error) { dht.network = network dht.datastore = ds.NewMapDatastore() dht.self = p - dht.listeners = make(map[uint64]chan *swarm.Message) + dht.listeners = make(map[uint64]*listenInfo) dht.providers = make(map[u.Key][]*providerInfo) dht.shutdown = make(chan struct{}) - dht.routes = NewRoutingTable(20, convertPeerID(p.ID)) + dht.routes = make([]*RoutingTable, 1) + dht.routes[0] = NewRoutingTable(20, convertPeerID(p.ID)) dht.birth = time.Now() return dht, nil } @@ -106,7 +116,7 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { dht.network.StartConn(conn) - removed := dht.routes.Update(peer) + removed := dht.routes[0].Update(peer) if removed != nil { panic("need to remove this peer.") } @@ -142,7 +152,7 @@ func (dht *IpfsDHT) handleMessages() { } // Update peers latest visit in routing table - removed := dht.routes.Update(mes.Peer) + removed := dht.routes[0].Update(mes.Peer) if removed != nil { panic("Need to handle removed peer.") } @@ -150,10 +160,15 @@ func (dht *IpfsDHT) handleMessages() { // Note: not sure if this is the correct place for this if pmes.GetResponse() { dht.listenLock.RLock() - ch, ok := dht.listeners[pmes.GetId()] + list, ok := dht.listeners[pmes.GetId()] + if list.count > 1 { + list.count-- + } else if list.count == 1 { + delete(dht.listeners, pmes.GetId()) + } dht.listenLock.RUnlock() if ok { - ch <- mes + list.resp <- mes } else { // this is expected behaviour during a timeout u.DOut("Received response with nobody listening...") @@ -181,7 +196,7 @@ func (dht *IpfsDHT) handleMessages() { case DHTMessage_PING: dht.handlePing(mes.Peer, pmes) case DHTMessage_DIAGNOSTIC: - // TODO: network diagnostic messages + dht.handleDiagnostic(mes.Peer, pmes) } case err := <-dht.network.Chan.Errors: @@ -220,7 +235,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *DHTMessage) { } } else if err == ds.ErrNotFound { // Find closest peer(s) to desired key and reply with that info - closer := dht.routes.NearestPeer(convertKey(u.Key(pmes.GetKey()))) + closer := dht.routes[0].NearestPeer(convertKey(u.Key(pmes.GetKey()))) resp = &pDHTMessage{ Response: true, Id: *pmes.Id, @@ -256,7 +271,7 @@ func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *DHTMessage) { func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *DHTMessage) { u.POut("handleFindPeer: searching for '%s'", peer.ID(pmes.GetKey()).Pretty()) - closest := dht.routes.NearestPeer(convertKey(u.Key(pmes.GetKey()))) + closest := dht.routes[0].NearestPeer(convertKey(u.Key(pmes.GetKey()))) if closest == nil { panic("could not find anything.") } @@ -336,10 +351,10 @@ func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *DHTMessage) { // Register a handler for a specific message ID, used for getting replies // to certain messages (i.e. response to a GET_VALUE message) -func (dht *IpfsDHT) ListenFor(mesid uint64) <-chan *swarm.Message { +func (dht *IpfsDHT) ListenFor(mesid uint64, count int) <-chan *swarm.Message { lchan := make(chan *swarm.Message) dht.listenLock.Lock() - dht.listeners[mesid] = lchan + dht.listeners[mesid] = &listenInfo{lchan, count} dht.listenLock.Unlock() return lchan } @@ -347,12 +362,19 @@ func (dht *IpfsDHT) ListenFor(mesid uint64) <-chan *swarm.Message { // Unregister the given message id from the listener map func (dht *IpfsDHT) Unlisten(mesid uint64) { dht.listenLock.Lock() - ch, ok := dht.listeners[mesid] + list, ok := dht.listeners[mesid] if ok { delete(dht.listeners, mesid) } dht.listenLock.Unlock() - close(ch) + close(list.resp) +} + +func (dht *IpfsDHT) IsListening(mesid uint64) bool { + dht.listenLock.RLock() + _,ok := dht.listeners[mesid] + dht.listenLock.RUnlock() + return ok } // Stop all communications from this peer and shut down @@ -368,3 +390,51 @@ func (dht *IpfsDHT) addProviderEntry(key u.Key, p *peer.Peer) { dht.providers[key] = append(provs, &providerInfo{time.Now(), p}) dht.providerLock.Unlock() } + +func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *DHTMessage) { + dht.diaglock.Lock() + if dht.IsListening(pmes.GetId()) { + //TODO: ehhh.......... + dht.diaglock.Unlock() + return + } + dht.diaglock.Unlock() + + seq := dht.routes[0].NearestPeers(convertPeerID(dht.self.ID), 10) + listen_chan := dht.ListenFor(pmes.GetId(), len(seq)) + + for _,ps := range seq { + mes := swarm.NewMessage(ps, pmes) + dht.network.Chan.Outgoing <-mes + } + + + + buf := new(bytes.Buffer) + // NOTE: this shouldnt be a hardcoded value + after := time.After(time.Second * 20) + count := len(seq) + for count > 0 { + select { + case <-after: + //Timeout, return what we have + goto out + case req_resp := <-listen_chan: + buf.Write(req_resp.Data) + count-- + } + } + +out: + di := dht.getDiagInfo() + buf.Write(di.Marshal()) + resp := pDHTMessage{ + Type: DHTMessage_DIAGNOSTIC, + Id: pmes.GetId(), + Value: buf.Bytes(), + Response: true, + } + + mes := swarm.NewMessage(p, resp.ToProtobuf()) + dht.network.Chan.Outgoing <-mes +} diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 22a7cf886..8d45a4a43 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -3,6 +3,7 @@ package dht import ( "math/rand" "time" + "bytes" "encoding/json" proto "code.google.com/p/goprotobuf/proto" @@ -30,7 +31,7 @@ func GenerateMessageID() uint64 { // PutValue adds value corresponding to given Key. func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { var p *peer.Peer - p = s.routes.NearestPeer(convertKey(key)) + p = s.routes[0].NearestPeer(convertKey(key)) if p == nil { panic("Table returned nil peer!") } @@ -52,7 +53,7 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { // returned along with util.ErrSearchIncomplete func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { var p *peer.Peer - p = s.routes.NearestPeer(convertKey(key)) + p = s.routes[0].NearestPeer(convertKey(key)) if p == nil { panic("Table returned nil peer!") } @@ -62,7 +63,7 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { Key: string(key), Id: GenerateMessageID(), } - response_chan := s.ListenFor(pmes.Id) + response_chan := s.ListenFor(pmes.Id, 1) mes := swarm.NewMessage(p, pmes.ToProtobuf()) s.network.Chan.Outgoing <- mes @@ -92,7 +93,7 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { // Announce that this node can provide value for given key func (s *IpfsDHT) Provide(key u.Key) error { - peers := s.routes.NearestPeers(convertKey(key), PoolSize) + peers := s.routes[0].NearestPeers(convertKey(key), PoolSize) if len(peers) == 0 { //return an error } @@ -112,7 +113,7 @@ func (s *IpfsDHT) Provide(key u.Key) error { // FindProviders searches for peers who can provide the value for given key. func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) { - p := s.routes.NearestPeer(convertKey(key)) + p := s.routes[0].NearestPeer(convertKey(key)) pmes := pDHTMessage{ Type: DHTMessage_GET_PROVIDERS, @@ -122,7 +123,7 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, mes := swarm.NewMessage(p, pmes.ToProtobuf()) - listen_chan := s.ListenFor(pmes.Id) + listen_chan := s.ListenFor(pmes.Id, 1) u.DOut("Find providers for: '%s'", key) s.network.Chan.Outgoing <-mes after := time.After(timeout) @@ -163,7 +164,6 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, } return prov_arr, nil - } } @@ -171,7 +171,7 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, // FindPeer searches for a peer with given ID. func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) { - p := s.routes.NearestPeer(convertPeerID(id)) + p := s.routes[0].NearestPeer(convertPeerID(id)) pmes := pDHTMessage{ Type: DHTMessage_FIND_NODE, @@ -181,7 +181,7 @@ func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error mes := swarm.NewMessage(p, pmes.ToProtobuf()) - listen_chan := s.ListenFor(pmes.Id) + listen_chan := s.ListenFor(pmes.Id, 1) s.network.Chan.Outgoing <-mes after := time.After(timeout) select { @@ -224,7 +224,7 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { mes := swarm.NewMessage(p, pmes.ToProtobuf()) before := time.Now() - response_chan := dht.ListenFor(pmes.Id) + response_chan := dht.ListenFor(pmes.Id, 1) dht.network.Chan.Outgoing <- mes tout := time.After(timeout) @@ -241,3 +241,54 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { return u.ErrTimeout } } + +func (dht *IpfsDHT) GetDiagnostic(timeout time.Duration) ([]*diagInfo, error) { + u.DOut("Begin Diagnostic") + //Send to N closest peers + targets := dht.routes[0].NearestPeers(convertPeerID(dht.self.ID), 10) + + // TODO: Add timeout to this struct so nodes know when to return + pmes := pDHTMessage{ + Type: DHTMessage_DIAGNOSTIC, + Id: GenerateMessageID(), + } + + listen_chan := dht.ListenFor(pmes.Id, len(targets)) + + pbmes := pmes.ToProtobuf() + for _,p := range targets { + mes := swarm.NewMessage(p, pbmes) + dht.network.Chan.Outgoing <-mes + } + + var out []*diagInfo + after := time.After(timeout) + for count := len(targets); count > 0; { + select { + case <-after: + u.DOut("Diagnostic request timed out.") + return out, u.ErrTimeout + case resp := <-listen_chan: + pmes_out := new(DHTMessage) + err := proto.Unmarshal(resp.Data, pmes_out) + if err != nil { + // NOTE: here and elsewhere, need to audit error handling, + // some errors should be continued on from + return out, err + } + + dec := json.NewDecoder(bytes.NewBuffer(pmes_out.GetValue())) + for { + di := new(diagInfo) + err := dec.Decode(di) + if err != nil { + break + } + + out = append(out, di) + } + } + } + + return nil,nil +} diff --git a/routing/dht/table.go b/routing/dht/table.go index b4be9c321..be4a4b392 100644 --- a/routing/dht/table.go +++ b/routing/dht/table.go @@ -95,11 +95,7 @@ func (p peerSorterArr) Less(a, b int) bool { // func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) peerSorterArr { - if peerList == nil { - return peerSorterArr{} - } - e := peerList.Front() - for ; e != nil; { + for e := peerList.Front(); e != nil; e = e.Next() { p := e.Value.(*peer.Peer) p_id := convertPeerID(p.ID) pd := peerDistance{ @@ -107,11 +103,10 @@ func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) pe distance: xor(target, p_id), } peerArr = append(peerArr, &pd) - if e != nil { + if e == nil { u.POut("list element was nil.") return peerArr } - e = e.Next() } return peerArr } From 4ca6b62a3fbd2885dab98b503f79e68112794260 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 7 Aug 2014 18:06:50 -0700 Subject: [PATCH 0041/3817] implement timeouts on listeners for the dht and add diagnostic stuff This commit was moved from ipfs/go-ipfs-routing@bdafd309859b6e1b52718105c7c158a50e082f7f --- routing/dht/dht.go | 64 +++++++++++++++++++++++++++++++++------ routing/dht/diag.go | 44 +++++++++++++++++++++++++++ routing/dht/routing.go | 30 +++++++++--------- routing/dht/table_test.go | 17 +++++++++++ 4 files changed, 130 insertions(+), 25 deletions(-) create mode 100644 routing/dht/diag.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 5791c3fed..63a368b32 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -23,6 +23,8 @@ import ( // IpfsDHT is an implementation of Kademlia with Coral and S/Kademlia modifications. // It is used to implement the base IpfsRouting module. type IpfsDHT struct { + // Array of routing tables for differently distanced nodes + // NOTE: (currently, only a single table is used) routes []*RoutingTable network *swarm.Swarm @@ -55,6 +57,7 @@ type IpfsDHT struct { type listenInfo struct { resp chan *swarm.Message count int + eol time.Time } // Create a new DHT object with the given peer as the 'local' host @@ -161,14 +164,19 @@ func (dht *IpfsDHT) handleMessages() { if pmes.GetResponse() { dht.listenLock.RLock() list, ok := dht.listeners[pmes.GetId()] + dht.listenLock.RUnlock() + if time.Now().After(list.eol) { + dht.Unlisten(pmes.GetId()) + ok = false + } if list.count > 1 { list.count-- - } else if list.count == 1 { - delete(dht.listeners, pmes.GetId()) } - dht.listenLock.RUnlock() if ok { list.resp <- mes + if list.count == 1 { + dht.Unlisten(pmes.GetId()) + } } else { // this is expected behaviour during a timeout u.DOut("Received response with nobody listening...") @@ -217,10 +225,35 @@ func (dht *IpfsDHT) handleMessages() { dht.providers[k] = cleaned } dht.providerLock.Unlock() + dht.listenLock.Lock() + var remove []uint64 + now := time.Now() + for k,v := range dht.listeners { + if now.After(v.eol) { + remove = append(remove, k) + } + } + for _,k := range remove { + delete(dht.listeners, k) + } + dht.listenLock.Unlock() } } } +func (dht *IpfsDHT) putValueToPeer(p *peer.Peer, key string, value []byte) error { + pmes := pDHTMessage{ + Type: DHTMessage_PUT_VALUE, + Key: key, + Value: value, + Id: GenerateMessageID(), + } + + mes := swarm.NewMessage(p, pmes.ToProtobuf()) + dht.network.Chan.Outgoing <- mes + return nil +} + func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *DHTMessage) { dskey := ds.NewKey(pmes.GetKey()) var resp *pDHTMessage @@ -351,10 +384,10 @@ func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *DHTMessage) { // Register a handler for a specific message ID, used for getting replies // to certain messages (i.e. response to a GET_VALUE message) -func (dht *IpfsDHT) ListenFor(mesid uint64, count int) <-chan *swarm.Message { +func (dht *IpfsDHT) ListenFor(mesid uint64, count int, timeout time.Duration) <-chan *swarm.Message { lchan := make(chan *swarm.Message) dht.listenLock.Lock() - dht.listeners[mesid] = &listenInfo{lchan, count} + dht.listeners[mesid] = &listenInfo{lchan, count, time.Now().Add(timeout)} dht.listenLock.Unlock() return lchan } @@ -372,8 +405,14 @@ func (dht *IpfsDHT) Unlisten(mesid uint64) { func (dht *IpfsDHT) IsListening(mesid uint64) bool { dht.listenLock.RLock() - _,ok := dht.listeners[mesid] + li,ok := dht.listeners[mesid] dht.listenLock.RUnlock() + if time.Now().After(li.eol) { + dht.listenLock.Lock() + delete(dht.listeners, mesid) + dht.listenLock.Unlock() + return false + } return ok } @@ -401,7 +440,7 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *DHTMessage) { dht.diaglock.Unlock() seq := dht.routes[0].NearestPeers(convertPeerID(dht.self.ID), 10) - listen_chan := dht.ListenFor(pmes.GetId(), len(seq)) + listen_chan := dht.ListenFor(pmes.GetId(), len(seq), time.Second * 30) for _,ps := range seq { mes := swarm.NewMessage(ps, pmes) @@ -411,6 +450,9 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *DHTMessage) { buf := new(bytes.Buffer) + di := dht.getDiagInfo() + buf.Write(di.Marshal()) + // NOTE: this shouldnt be a hardcoded value after := time.After(time.Second * 20) count := len(seq) @@ -420,14 +462,18 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *DHTMessage) { //Timeout, return what we have goto out case req_resp := <-listen_chan: + pmes_out := new(DHTMessage) + err := proto.Unmarshal(req_resp.Data, pmes_out) + if err != nil { + // It broke? eh, whatever, keep going + continue + } buf.Write(req_resp.Data) count-- } } out: - di := dht.getDiagInfo() - buf.Write(di.Marshal()) resp := pDHTMessage{ Type: DHTMessage_DIAGNOSTIC, Id: pmes.GetId(), diff --git a/routing/dht/diag.go b/routing/dht/diag.go new file mode 100644 index 000000000..b8f211e40 --- /dev/null +++ b/routing/dht/diag.go @@ -0,0 +1,44 @@ +package dht + +import ( + "encoding/json" + "time" + + peer "github.com/jbenet/go-ipfs/peer" +) + +type connDiagInfo struct { + Latency time.Duration + Id peer.ID +} + +type diagInfo struct { + Id peer.ID + Connections []connDiagInfo + Keys []string + LifeSpan time.Duration + CodeVersion string +} + +func (di *diagInfo) Marshal() []byte { + b, err := json.Marshal(di) + if err != nil { + panic(err) + } + //TODO: also consider compressing this. There will be a lot of these + return b +} + + +func (dht *IpfsDHT) getDiagInfo() *diagInfo { + di := new(diagInfo) + di.CodeVersion = "github.com/jbenet/go-ipfs" + di.Id = dht.self.ID + di.LifeSpan = time.Since(dht.birth) + di.Keys = nil // Currently no way to query datastore + + for _,p := range dht.routes[0].listpeers() { + di.Connections = append(di.Connections, connDiagInfo{p.GetDistance(), p.ID}) + } + return di +} diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 8d45a4a43..1a90ce76b 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -29,6 +29,7 @@ func GenerateMessageID() uint64 { // Basic Put/Get // PutValue adds value corresponding to given Key. +// This is the top level "Store" operation of the DHT func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { var p *peer.Peer p = s.routes[0].NearestPeer(convertKey(key)) @@ -36,16 +37,7 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { panic("Table returned nil peer!") } - pmes := pDHTMessage{ - Type: DHTMessage_PUT_VALUE, - Key: string(key), - Value: value, - Id: GenerateMessageID(), - } - - mes := swarm.NewMessage(p, pmes.ToProtobuf()) - s.network.Chan.Outgoing <- mes - return nil + return s.putValueToPeer(p, string(key), value) } // GetValue searches for the value corresponding to given Key. @@ -63,7 +55,7 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { Key: string(key), Id: GenerateMessageID(), } - response_chan := s.ListenFor(pmes.Id, 1) + response_chan := s.ListenFor(pmes.Id, 1, time.Minute) mes := swarm.NewMessage(p, pmes.ToProtobuf()) s.network.Chan.Outgoing <- mes @@ -74,7 +66,13 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { case <-timeup: s.Unlisten(pmes.Id) return nil, u.ErrTimeout - case resp := <-response_chan: + case resp, ok := <-response_chan: + if !ok { + panic("Channel was closed...") + } + if resp == nil { + panic("Why the hell is this response nil?") + } pmes_out := new(DHTMessage) err := proto.Unmarshal(resp.Data, pmes_out) if err != nil { @@ -123,7 +121,7 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, mes := swarm.NewMessage(p, pmes.ToProtobuf()) - listen_chan := s.ListenFor(pmes.Id, 1) + listen_chan := s.ListenFor(pmes.Id, 1, time.Minute) u.DOut("Find providers for: '%s'", key) s.network.Chan.Outgoing <-mes after := time.After(timeout) @@ -181,7 +179,7 @@ func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error mes := swarm.NewMessage(p, pmes.ToProtobuf()) - listen_chan := s.ListenFor(pmes.Id, 1) + listen_chan := s.ListenFor(pmes.Id, 1, time.Minute) s.network.Chan.Outgoing <-mes after := time.After(timeout) select { @@ -224,7 +222,7 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { mes := swarm.NewMessage(p, pmes.ToProtobuf()) before := time.Now() - response_chan := dht.ListenFor(pmes.Id, 1) + response_chan := dht.ListenFor(pmes.Id, 1, time.Minute) dht.network.Chan.Outgoing <- mes tout := time.After(timeout) @@ -253,7 +251,7 @@ func (dht *IpfsDHT) GetDiagnostic(timeout time.Duration) ([]*diagInfo, error) { Id: GenerateMessageID(), } - listen_chan := dht.ListenFor(pmes.Id, len(targets)) + listen_chan := dht.ListenFor(pmes.Id, len(targets), time.Minute * 2) pbmes := pmes.ToProtobuf() for _,p := range targets { diff --git a/routing/dht/table_test.go b/routing/dht/table_test.go index debec5e16..393a1c585 100644 --- a/routing/dht/table_test.go +++ b/routing/dht/table_test.go @@ -107,3 +107,20 @@ func TestTableFind(t *testing.T) { t.Fatalf("Failed to lookup known node...") } } + +func TestTableFindMultiple(t *testing.T) { + local := _randPeer() + rt := NewRoutingTable(20, convertPeerID(local.ID)) + + peers := make([]*peer.Peer, 100) + for i := 0; i < 18; i++ { + peers[i] = _randPeer() + rt.Update(peers[i]) + } + + t.Logf("Searching for peer: '%s'", peers[2].ID.Pretty()) + found := rt.NearestPeers(convertPeerID(peers[2].ID), 15) + if len(found) != 15 { + t.Fatalf("Got back different number of peers than we expected.") + } +} From b36b9ccd3559c27076261c931359c7f33185e098 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 7 Aug 2014 21:52:11 -0700 Subject: [PATCH 0042/3817] add a unit test for provides functionality This commit was moved from ipfs/go-ipfs-routing@b4e278a5a6672cbe28faeaee5ba08e54f0be84da --- routing/dht/dht.go | 14 +++++++++ routing/dht/dht_test.go | 70 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 63a368b32..47f93e65f 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -92,6 +92,8 @@ func (dht *IpfsDHT) Start() { // Connect to a new peer at the given address // TODO: move this into swarm func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { + maddrstr,_ := addr.String() + u.DOut("Connect to new peer: %s", maddrstr) if addr == nil { panic("addr was nil!") } @@ -484,3 +486,15 @@ out: mes := swarm.NewMessage(p, resp.ToProtobuf()) dht.network.Chan.Outgoing <-mes } + +func (dht *IpfsDHT) GetLocal(key u.Key) ([]byte, error) { + v,err := dht.datastore.Get(ds.NewKey(string(key))) + if err != nil { + return nil, err + } + return v.([]byte), nil +} + +func (dht *IpfsDHT) PutLocal(key u.Key, value []byte) error { + return dht.datastore.Put(ds.NewKey(string(key)), value) +} diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index b57ca3f7b..2de027a3d 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -7,6 +7,7 @@ import ( u "github.com/jbenet/go-ipfs/util" "time" + "fmt" ) func TestPing(t *testing.T) { @@ -107,3 +108,72 @@ func TestValueGetSet(t *testing.T) { t.Fatalf("Expected 'world' got %s", string(val)) } } + +func TestProvides(t *testing.T) { + u.Debug = false + var addrs []*ma.Multiaddr + for i := 0; i < 4; i++ { + a,err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000 + i)) + if err != nil { + t.Fatal(err) + } + addrs = append(addrs, a) + } + + + var peers []*peer.Peer + for i := 0; i < 4; i++ { + p := new(peer.Peer) + p.AddAddress(addrs[i]) + p.ID = peer.ID([]byte(fmt.Sprintf("peer_%d", i))) + peers = append(peers, p) + } + + var dhts []*IpfsDHT + for i := 0; i < 4; i++ { + d,err := NewDHT(peers[i]) + if err != nil { + t.Fatal(err) + } + dhts = append(dhts, d) + d.Start() + } + + _, err := dhts[0].Connect(addrs[1]) + if err != nil { + t.Fatal(err) + } + + _, err = dhts[1].Connect(addrs[2]) + if err != nil { + t.Fatal(err) + } + + _, err = dhts[1].Connect(addrs[3]) + if err != nil { + t.Fatal(err) + } + + err = dhts[3].PutLocal(u.Key("hello"), []byte("world")) + if err != nil { + t.Fatal(err) + } + + err = dhts[3].Provide(u.Key("hello")) + if err != nil { + t.Fatal(err) + } + + time.Sleep(time.Millisecond * 60) + + provs,err := dhts[0].FindProviders(u.Key("hello"), time.Second) + if err != nil { + t.Fatal(err) + } + + if len(provs) != 1 { + t.Fatal("Didnt get back providers") + } +} + + From ab409cf7dd48f048e0a8d1e6f69a2c74f8d30068 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 8 Aug 2014 18:09:21 -0700 Subject: [PATCH 0043/3817] address issues from code review (issue #25) This commit was moved from ipfs/go-ipfs-routing@c3810e2a697eac377c587f43273caaa57ec1bc02 --- routing/dht/{pDHTMessage.go => DHTMessage.go} | 16 +- routing/dht/bucket.go | 2 +- routing/dht/dht.go | 276 +++++++++--------- routing/dht/dht_test.go | 42 +-- routing/dht/diag.go | 2 +- routing/dht/messages.pb.go | 106 ++++--- routing/dht/messages.proto | 10 +- routing/dht/routing.go | 85 +++--- routing/dht/table.go | 2 +- routing/dht/util.go | 4 + 10 files changed, 293 insertions(+), 252 deletions(-) rename routing/dht/{pDHTMessage.go => DHTMessage.go} (56%) diff --git a/routing/dht/pDHTMessage.go b/routing/dht/DHTMessage.go similarity index 56% rename from routing/dht/pDHTMessage.go rename to routing/dht/DHTMessage.go index bfe37d35f..64ca5bcab 100644 --- a/routing/dht/pDHTMessage.go +++ b/routing/dht/DHTMessage.go @@ -1,17 +1,17 @@ package dht // A helper struct to make working with protbuf types easier -type pDHTMessage struct { - Type DHTMessage_MessageType - Key string - Value []byte +type DHTMessage struct { + Type PBDHTMessage_MessageType + Key string + Value []byte Response bool - Id uint64 - Success bool + Id uint64 + Success bool } -func (m *pDHTMessage) ToProtobuf() *DHTMessage { - pmes := new(DHTMessage) +func (m *DHTMessage) ToProtobuf() *PBDHTMessage { + pmes := new(PBDHTMessage) if m.Value != nil { pmes.Value = m.Value } diff --git a/routing/dht/bucket.go b/routing/dht/bucket.go index 996d299d9..7aa8d0a94 100644 --- a/routing/dht/bucket.go +++ b/routing/dht/bucket.go @@ -49,7 +49,7 @@ func (b *Bucket) Split(cpl int, target ID) *Bucket { e := bucket_list.Front() for e != nil { peer_id := convertPeerID(e.Value.(*peer.Peer).ID) - peer_cpl := xor(peer_id, target).commonPrefixLen() + peer_cpl := prefLen(peer_id, target) if peer_cpl > cpl { cur := e out.PushBack(e.Value) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 47f93e65f..8fbd5c092 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -1,15 +1,15 @@ package dht import ( - "sync" - "time" "bytes" "encoding/json" + "errors" + "sync" + "time" - peer "github.com/jbenet/go-ipfs/peer" - swarm "github.com/jbenet/go-ipfs/swarm" - u "github.com/jbenet/go-ipfs/util" - identify "github.com/jbenet/go-ipfs/identify" + peer "github.com/jbenet/go-ipfs/peer" + swarm "github.com/jbenet/go-ipfs/swarm" + u "github.com/jbenet/go-ipfs/util" ma "github.com/jbenet/go-multiaddr" @@ -37,7 +37,7 @@ type IpfsDHT struct { // Map keys to peers that can provide their value // TODO: implement a TTL on each of these keys - providers map[u.Key][]*providerInfo + providers map[u.Key][]*providerInfo providerLock sync.RWMutex // map of channels waiting for reply messages @@ -54,21 +54,27 @@ type IpfsDHT struct { diaglock sync.Mutex } +// The listen info struct holds information about a message that is being waited for type listenInfo struct { + // Responses matching the listen ID will be sent through resp resp chan *swarm.Message + + // count is the number of responses to listen for count int + + // eol is the time at which this listener will expire eol time.Time } // Create a new DHT object with the given peer as the 'local' host func NewDHT(p *peer.Peer) (*IpfsDHT, error) { if p == nil { - panic("Tried to create new dht with nil peer") + return nil, errors.New("nil peer passed to NewDHT()") } network := swarm.NewSwarm(p) err := network.Listen() if err != nil { - return nil,err + return nil, err } dht := new(IpfsDHT) @@ -90,50 +96,24 @@ func (dht *IpfsDHT) Start() { } // Connect to a new peer at the given address -// TODO: move this into swarm func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { - maddrstr,_ := addr.String() + maddrstr, _ := addr.String() u.DOut("Connect to new peer: %s", maddrstr) - if addr == nil { - panic("addr was nil!") - } - peer := new(peer.Peer) - peer.AddAddress(addr) - - conn,err := swarm.Dial("tcp", peer) - if err != nil { - return nil, err - } - - err = identify.Handshake(dht.self, peer, conn.Incoming.MsgChan, conn.Outgoing.MsgChan) + npeer, err := dht.network.Connect(addr) if err != nil { return nil, err } - // Send node an address that you can be reached on - myaddr := dht.self.NetAddress("tcp") - mastr,err := myaddr.String() - if err != nil { - panic("No local address to send") - } - - conn.Outgoing.MsgChan <- []byte(mastr) - - dht.network.StartConn(conn) - - removed := dht.routes[0].Update(peer) - if removed != nil { - panic("need to remove this peer.") - } + dht.Update(npeer) // Ping new peer to register in their routing table // NOTE: this should be done better... - err = dht.Ping(peer, time.Second * 2) + err = dht.Ping(npeer, time.Second*2) if err != nil { - panic("Failed to ping new peer.") + return nil, errors.New("Failed to ping newly connected peer.") } - return peer, nil + return npeer, nil } // Read in all messages from swarm and handle them appropriately @@ -144,23 +124,19 @@ func (dht *IpfsDHT) handleMessages() { checkTimeouts := time.NewTicker(time.Minute * 5) for { select { - case mes,ok := <-dht.network.Chan.Incoming: + case mes, ok := <-dht.network.Chan.Incoming: if !ok { u.DOut("handleMessages closing, bad recv on incoming") return } - pmes := new(DHTMessage) + pmes := new(PBDHTMessage) err := proto.Unmarshal(mes.Data, pmes) if err != nil { u.PErr("Failed to decode protobuf message: %s", err) continue } - // Update peers latest visit in routing table - removed := dht.routes[0].Update(mes.Peer) - if removed != nil { - panic("Need to handle removed peer.") - } + dht.Update(mes.Peer) // Note: not sure if this is the correct place for this if pmes.GetResponse() { @@ -180,7 +156,6 @@ func (dht *IpfsDHT) handleMessages() { dht.Unlisten(pmes.GetId()) } } else { - // this is expected behaviour during a timeout u.DOut("Received response with nobody listening...") } @@ -190,65 +165,73 @@ func (dht *IpfsDHT) handleMessages() { u.DOut("[peer: %s]", dht.self.ID.Pretty()) u.DOut("Got message type: '%s' [id = %x, from = %s]", - DHTMessage_MessageType_name[int32(pmes.GetType())], + PBDHTMessage_MessageType_name[int32(pmes.GetType())], pmes.GetId(), mes.Peer.ID.Pretty()) switch pmes.GetType() { - case DHTMessage_GET_VALUE: + case PBDHTMessage_GET_VALUE: dht.handleGetValue(mes.Peer, pmes) - case DHTMessage_PUT_VALUE: + case PBDHTMessage_PUT_VALUE: dht.handlePutValue(mes.Peer, pmes) - case DHTMessage_FIND_NODE: + case PBDHTMessage_FIND_NODE: dht.handleFindPeer(mes.Peer, pmes) - case DHTMessage_ADD_PROVIDER: + case PBDHTMessage_ADD_PROVIDER: dht.handleAddProvider(mes.Peer, pmes) - case DHTMessage_GET_PROVIDERS: + case PBDHTMessage_GET_PROVIDERS: dht.handleGetProviders(mes.Peer, pmes) - case DHTMessage_PING: + case PBDHTMessage_PING: dht.handlePing(mes.Peer, pmes) - case DHTMessage_DIAGNOSTIC: + case PBDHTMessage_DIAGNOSTIC: dht.handleDiagnostic(mes.Peer, pmes) } case err := <-dht.network.Chan.Errors: u.DErr("dht err: %s", err) - panic(err) case <-dht.shutdown: checkTimeouts.Stop() return case <-checkTimeouts.C: - dht.providerLock.Lock() - for k,parr := range dht.providers { - var cleaned []*providerInfo - for _,v := range parr { - if time.Since(v.Creation) < time.Hour { - cleaned = append(cleaned, v) - } - } - dht.providers[k] = cleaned - } - dht.providerLock.Unlock() - dht.listenLock.Lock() - var remove []uint64 - now := time.Now() - for k,v := range dht.listeners { - if now.After(v.eol) { - remove = append(remove, k) - } - } - for _,k := range remove { - delete(dht.listeners, k) + // Time to collect some garbage! + dht.cleanExpiredProviders() + dht.cleanExpiredListeners() + } + } +} + +func (dht *IpfsDHT) cleanExpiredProviders() { + dht.providerLock.Lock() + for k, parr := range dht.providers { + var cleaned []*providerInfo + for _, v := range parr { + if time.Since(v.Creation) < time.Hour { + cleaned = append(cleaned, v) } - dht.listenLock.Unlock() } + dht.providers[k] = cleaned } + dht.providerLock.Unlock() +} + +func (dht *IpfsDHT) cleanExpiredListeners() { + dht.listenLock.Lock() + var remove []uint64 + now := time.Now() + for k, v := range dht.listeners { + if now.After(v.eol) { + remove = append(remove, k) + } + } + for _, k := range remove { + delete(dht.listeners, k) + } + dht.listenLock.Unlock() } func (dht *IpfsDHT) putValueToPeer(p *peer.Peer, key string, value []byte) error { - pmes := pDHTMessage{ - Type: DHTMessage_PUT_VALUE, - Key: key, + pmes := DHTMessage{ + Type: PBDHTMessage_PUT_VALUE, + Key: key, Value: value, - Id: GenerateMessageID(), + Id: GenerateMessageID(), } mes := swarm.NewMessage(p, pmes.ToProtobuf()) @@ -256,27 +239,27 @@ func (dht *IpfsDHT) putValueToPeer(p *peer.Peer, key string, value []byte) error return nil } -func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *DHTMessage) { +func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { dskey := ds.NewKey(pmes.GetKey()) - var resp *pDHTMessage + var resp *DHTMessage i_val, err := dht.datastore.Get(dskey) if err == nil { - resp = &pDHTMessage{ + resp = &DHTMessage{ Response: true, - Id: *pmes.Id, - Key: *pmes.Key, - Value: i_val.([]byte), - Success: true, + Id: *pmes.Id, + Key: *pmes.Key, + Value: i_val.([]byte), + Success: true, } } else if err == ds.ErrNotFound { // Find closest peer(s) to desired key and reply with that info closer := dht.routes[0].NearestPeer(convertKey(u.Key(pmes.GetKey()))) - resp = &pDHTMessage{ + resp = &DHTMessage{ Response: true, - Id: *pmes.Id, - Key: *pmes.Key, - Value: closer.ID, - Success: false, + Id: *pmes.Id, + Key: *pmes.Key, + Value: closer.ID, + Success: false, } } @@ -285,7 +268,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *DHTMessage) { } // Store a value in this peer local storage -func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *DHTMessage) { +func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *PBDHTMessage) { dskey := ds.NewKey(pmes.GetKey()) err := dht.datastore.Put(dskey, pmes.GetValue()) if err != nil { @@ -294,46 +277,51 @@ func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *DHTMessage) { } } -func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *DHTMessage) { - resp := pDHTMessage{ - Type: pmes.GetType(), +func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *PBDHTMessage) { + resp := DHTMessage{ + Type: pmes.GetType(), Response: true, - Id: pmes.GetId(), + Id: pmes.GetId(), } - dht.network.Chan.Outgoing <-swarm.NewMessage(p, resp.ToProtobuf()) + dht.network.Chan.Outgoing <- swarm.NewMessage(p, resp.ToProtobuf()) } -func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *DHTMessage) { +func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) { + success := true u.POut("handleFindPeer: searching for '%s'", peer.ID(pmes.GetKey()).Pretty()) closest := dht.routes[0].NearestPeer(convertKey(u.Key(pmes.GetKey()))) if closest == nil { - panic("could not find anything.") + u.PErr("handleFindPeer: could not find anything.") + success = false } if len(closest.Addresses) == 0 { - panic("no addresses for connected peer...") + u.PErr("handleFindPeer: no addresses for connected peer...") + success = false } u.POut("handleFindPeer: sending back '%s'", closest.ID.Pretty()) - addr,err := closest.Addresses[0].String() + addr, err := closest.Addresses[0].String() if err != nil { - panic(err) + u.PErr(err.Error()) + success = false } - resp := pDHTMessage{ - Type: pmes.GetType(), + resp := DHTMessage{ + Type: pmes.GetType(), Response: true, - Id: pmes.GetId(), - Value: []byte(addr), + Id: pmes.GetId(), + Value: []byte(addr), + Success: success, } mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.network.Chan.Outgoing <-mes + dht.network.Chan.Outgoing <- mes } -func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *DHTMessage) { +func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *PBDHTMessage) { dht.providerLock.RLock() providers := dht.providers[u.Key(pmes.GetKey())] dht.providerLock.RUnlock() @@ -344,9 +332,9 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *DHTMessage) { // This is just a quick hack, formalize method of sending addrs later addrs := make(map[u.Key]string) - for _,prov := range providers { + for _, prov := range providers { ma := prov.Value.NetAddress("tcp") - str,err := ma.String() + str, err := ma.String() if err != nil { u.PErr("Error: %s", err) continue @@ -355,35 +343,38 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *DHTMessage) { addrs[prov.Value.Key()] = str } - data,err := json.Marshal(addrs) + success := true + data, err := json.Marshal(addrs) if err != nil { - panic(err) + u.POut("handleGetProviders: error marshalling struct to JSON: %s", err) + data = nil + success = false } - resp := pDHTMessage{ - Type: DHTMessage_GET_PROVIDERS, - Key: pmes.GetKey(), - Value: data, - Id: pmes.GetId(), + resp := DHTMessage{ + Type: PBDHTMessage_GET_PROVIDERS, + Key: pmes.GetKey(), + Value: data, + Id: pmes.GetId(), Response: true, + Success: success, } mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.network.Chan.Outgoing <-mes + dht.network.Chan.Outgoing <- mes } type providerInfo struct { Creation time.Time - Value *peer.Peer + Value *peer.Peer } -func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *DHTMessage) { +func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *PBDHTMessage) { //TODO: need to implement TTLs on providers key := u.Key(pmes.GetKey()) dht.addProviderEntry(key, p) } - // Register a handler for a specific message ID, used for getting replies // to certain messages (i.e. response to a GET_VALUE message) func (dht *IpfsDHT) ListenFor(mesid uint64, count int, timeout time.Duration) <-chan *swarm.Message { @@ -407,7 +398,7 @@ func (dht *IpfsDHT) Unlisten(mesid uint64) { func (dht *IpfsDHT) IsListening(mesid uint64) bool { dht.listenLock.RLock() - li,ok := dht.listeners[mesid] + li, ok := dht.listeners[mesid] dht.listenLock.RUnlock() if time.Now().After(li.eol) { dht.listenLock.Lock() @@ -432,7 +423,7 @@ func (dht *IpfsDHT) addProviderEntry(key u.Key, p *peer.Peer) { dht.providerLock.Unlock() } -func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *DHTMessage) { +func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { dht.diaglock.Lock() if dht.IsListening(pmes.GetId()) { //TODO: ehhh.......... @@ -442,15 +433,13 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *DHTMessage) { dht.diaglock.Unlock() seq := dht.routes[0].NearestPeers(convertPeerID(dht.self.ID), 10) - listen_chan := dht.ListenFor(pmes.GetId(), len(seq), time.Second * 30) + listen_chan := dht.ListenFor(pmes.GetId(), len(seq), time.Second*30) - for _,ps := range seq { + for _, ps := range seq { mes := swarm.NewMessage(ps, pmes) - dht.network.Chan.Outgoing <-mes + dht.network.Chan.Outgoing <- mes } - - buf := new(bytes.Buffer) di := dht.getDiagInfo() buf.Write(di.Marshal()) @@ -464,7 +453,7 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *DHTMessage) { //Timeout, return what we have goto out case req_resp := <-listen_chan: - pmes_out := new(DHTMessage) + pmes_out := new(PBDHTMessage) err := proto.Unmarshal(req_resp.Data, pmes_out) if err != nil { // It broke? eh, whatever, keep going @@ -476,19 +465,19 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *DHTMessage) { } out: - resp := pDHTMessage{ - Type: DHTMessage_DIAGNOSTIC, - Id: pmes.GetId(), - Value: buf.Bytes(), + resp := DHTMessage{ + Type: PBDHTMessage_DIAGNOSTIC, + Id: pmes.GetId(), + Value: buf.Bytes(), Response: true, } mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.network.Chan.Outgoing <-mes + dht.network.Chan.Outgoing <- mes } func (dht *IpfsDHT) GetLocal(key u.Key) ([]byte, error) { - v,err := dht.datastore.Get(ds.NewKey(string(key))) + v, err := dht.datastore.Get(ds.NewKey(string(key))) if err != nil { return nil, err } @@ -498,3 +487,10 @@ func (dht *IpfsDHT) GetLocal(key u.Key) ([]byte, error) { func (dht *IpfsDHT) PutLocal(key u.Key, value []byte) error { return dht.datastore.Put(ds.NewKey(string(key)), value) } + +func (dht *IpfsDHT) Update(p *peer.Peer) { + removed := dht.routes[0].Update(p) + if removed != nil { + dht.network.Drop(removed) + } +} diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 2de027a3d..e24ada020 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -2,21 +2,22 @@ package dht import ( "testing" + peer "github.com/jbenet/go-ipfs/peer" - ma "github.com/jbenet/go-multiaddr" u "github.com/jbenet/go-ipfs/util" + ma "github.com/jbenet/go-multiaddr" - "time" "fmt" + "time" ) func TestPing(t *testing.T) { u.Debug = false - addr_a,err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1234") + addr_a, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1234") if err != nil { t.Fatal(err) } - addr_b,err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5678") + addr_b, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5678") if err != nil { t.Fatal(err) } @@ -29,12 +30,12 @@ func TestPing(t *testing.T) { peer_b.AddAddress(addr_b) peer_b.ID = peer.ID([]byte("peer_b")) - dht_a,err := NewDHT(peer_a) + dht_a, err := NewDHT(peer_a) if err != nil { t.Fatal(err) } - dht_b,err := NewDHT(peer_b) + dht_b, err := NewDHT(peer_b) if err != nil { t.Fatal(err) } @@ -42,13 +43,13 @@ func TestPing(t *testing.T) { dht_a.Start() dht_b.Start() - _,err = dht_a.Connect(addr_b) + _, err = dht_a.Connect(addr_b) if err != nil { t.Fatal(err) } //Test that we can ping the node - err = dht_a.Ping(peer_b, time.Second * 2) + err = dht_a.Ping(peer_b, time.Second*2) if err != nil { t.Fatal(err) } @@ -59,11 +60,11 @@ func TestPing(t *testing.T) { func TestValueGetSet(t *testing.T) { u.Debug = false - addr_a,err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") + addr_a, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") if err != nil { t.Fatal(err) } - addr_b,err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5679") + addr_b, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5679") if err != nil { t.Fatal(err) } @@ -76,12 +77,12 @@ func TestValueGetSet(t *testing.T) { peer_b.AddAddress(addr_b) peer_b.ID = peer.ID([]byte("peer_b")) - dht_a,err := NewDHT(peer_a) + dht_a, err := NewDHT(peer_a) if err != nil { t.Fatal(err) } - dht_b,err := NewDHT(peer_b) + dht_b, err := NewDHT(peer_b) if err != nil { t.Fatal(err) } @@ -89,7 +90,7 @@ func TestValueGetSet(t *testing.T) { dht_a.Start() dht_b.Start() - _,err = dht_a.Connect(addr_b) + _, err = dht_a.Connect(addr_b) if err != nil { t.Fatal(err) } @@ -99,7 +100,7 @@ func TestValueGetSet(t *testing.T) { t.Fatal(err) } - val, err := dht_a.GetValue("hello", time.Second * 2) + val, err := dht_a.GetValue("hello", time.Second*2) if err != nil { t.Fatal(err) } @@ -113,14 +114,13 @@ func TestProvides(t *testing.T) { u.Debug = false var addrs []*ma.Multiaddr for i := 0; i < 4; i++ { - a,err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000 + i)) + a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) if err != nil { t.Fatal(err) } addrs = append(addrs, a) } - var peers []*peer.Peer for i := 0; i < 4; i++ { p := new(peer.Peer) @@ -131,7 +131,7 @@ func TestProvides(t *testing.T) { var dhts []*IpfsDHT for i := 0; i < 4; i++ { - d,err := NewDHT(peers[i]) + d, err := NewDHT(peers[i]) if err != nil { t.Fatal(err) } @@ -166,7 +166,7 @@ func TestProvides(t *testing.T) { time.Sleep(time.Millisecond * 60) - provs,err := dhts[0].FindProviders(u.Key("hello"), time.Second) + provs, err := dhts[0].FindProviders(u.Key("hello"), time.Second) if err != nil { t.Fatal(err) } @@ -174,6 +174,8 @@ func TestProvides(t *testing.T) { if len(provs) != 1 { t.Fatal("Didnt get back providers") } -} - + for i := 0; i < 4; i++ { + dhts[i].Halt() + } +} diff --git a/routing/dht/diag.go b/routing/dht/diag.go index b8f211e40..4bc752f92 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -38,7 +38,7 @@ func (dht *IpfsDHT) getDiagInfo() *diagInfo { di.Keys = nil // Currently no way to query datastore for _,p := range dht.routes[0].listpeers() { - di.Connections = append(di.Connections, connDiagInfo{p.GetDistance(), p.ID}) + di.Connections = append(di.Connections, connDiagInfo{p.GetLatency(), p.ID}) } return di } diff --git a/routing/dht/messages.pb.go b/routing/dht/messages.pb.go index 4f427efa1..a852c5e1f 100644 --- a/routing/dht/messages.pb.go +++ b/routing/dht/messages.pb.go @@ -9,7 +9,7 @@ It is generated from these files: messages.proto It has these top-level messages: - DHTMessage + PBDHTMessage */ package dht @@ -20,19 +20,19 @@ import math "math" var _ = proto.Marshal var _ = math.Inf -type DHTMessage_MessageType int32 +type PBDHTMessage_MessageType int32 const ( - DHTMessage_PUT_VALUE DHTMessage_MessageType = 0 - DHTMessage_GET_VALUE DHTMessage_MessageType = 1 - DHTMessage_ADD_PROVIDER DHTMessage_MessageType = 2 - DHTMessage_GET_PROVIDERS DHTMessage_MessageType = 3 - DHTMessage_FIND_NODE DHTMessage_MessageType = 4 - DHTMessage_PING DHTMessage_MessageType = 5 - DHTMessage_DIAGNOSTIC DHTMessage_MessageType = 6 + PBDHTMessage_PUT_VALUE PBDHTMessage_MessageType = 0 + PBDHTMessage_GET_VALUE PBDHTMessage_MessageType = 1 + PBDHTMessage_ADD_PROVIDER PBDHTMessage_MessageType = 2 + PBDHTMessage_GET_PROVIDERS PBDHTMessage_MessageType = 3 + PBDHTMessage_FIND_NODE PBDHTMessage_MessageType = 4 + PBDHTMessage_PING PBDHTMessage_MessageType = 5 + PBDHTMessage_DIAGNOSTIC PBDHTMessage_MessageType = 6 ) -var DHTMessage_MessageType_name = map[int32]string{ +var PBDHTMessage_MessageType_name = map[int32]string{ 0: "PUT_VALUE", 1: "GET_VALUE", 2: "ADD_PROVIDER", @@ -41,7 +41,7 @@ var DHTMessage_MessageType_name = map[int32]string{ 5: "PING", 6: "DIAGNOSTIC", } -var DHTMessage_MessageType_value = map[string]int32{ +var PBDHTMessage_MessageType_value = map[string]int32{ "PUT_VALUE": 0, "GET_VALUE": 1, "ADD_PROVIDER": 2, @@ -51,79 +51,111 @@ var DHTMessage_MessageType_value = map[string]int32{ "DIAGNOSTIC": 6, } -func (x DHTMessage_MessageType) Enum() *DHTMessage_MessageType { - p := new(DHTMessage_MessageType) +func (x PBDHTMessage_MessageType) Enum() *PBDHTMessage_MessageType { + p := new(PBDHTMessage_MessageType) *p = x return p } -func (x DHTMessage_MessageType) String() string { - return proto.EnumName(DHTMessage_MessageType_name, int32(x)) +func (x PBDHTMessage_MessageType) String() string { + return proto.EnumName(PBDHTMessage_MessageType_name, int32(x)) } -func (x *DHTMessage_MessageType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(DHTMessage_MessageType_value, data, "DHTMessage_MessageType") +func (x *PBDHTMessage_MessageType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(PBDHTMessage_MessageType_value, data, "PBDHTMessage_MessageType") if err != nil { return err } - *x = DHTMessage_MessageType(value) + *x = PBDHTMessage_MessageType(value) return nil } -type DHTMessage struct { - Type *DHTMessage_MessageType `protobuf:"varint,1,req,name=type,enum=dht.DHTMessage_MessageType" json:"type,omitempty"` - Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` - Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` - Id *uint64 `protobuf:"varint,4,req,name=id" json:"id,omitempty"` - Response *bool `protobuf:"varint,5,opt,name=response" json:"response,omitempty"` - Success *bool `protobuf:"varint,6,opt,name=success" json:"success,omitempty"` - XXX_unrecognized []byte `json:"-"` +type PBDHTMessage struct { + Type *PBDHTMessage_MessageType `protobuf:"varint,1,req,name=type,enum=dht.PBDHTMessage_MessageType" json:"type,omitempty"` + Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` + Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` + Id *uint64 `protobuf:"varint,4,req,name=id" json:"id,omitempty"` + Response *bool `protobuf:"varint,5,opt,name=response" json:"response,omitempty"` + Success *bool `protobuf:"varint,6,opt,name=success" json:"success,omitempty"` + Peers []*PBDHTMessage_PBPeer `protobuf:"bytes,7,rep,name=peers" json:"peers,omitempty"` + XXX_unrecognized []byte `json:"-"` } -func (m *DHTMessage) Reset() { *m = DHTMessage{} } -func (m *DHTMessage) String() string { return proto.CompactTextString(m) } -func (*DHTMessage) ProtoMessage() {} +func (m *PBDHTMessage) Reset() { *m = PBDHTMessage{} } +func (m *PBDHTMessage) String() string { return proto.CompactTextString(m) } +func (*PBDHTMessage) ProtoMessage() {} -func (m *DHTMessage) GetType() DHTMessage_MessageType { +func (m *PBDHTMessage) GetType() PBDHTMessage_MessageType { if m != nil && m.Type != nil { return *m.Type } - return DHTMessage_PUT_VALUE + return PBDHTMessage_PUT_VALUE } -func (m *DHTMessage) GetKey() string { +func (m *PBDHTMessage) GetKey() string { if m != nil && m.Key != nil { return *m.Key } return "" } -func (m *DHTMessage) GetValue() []byte { +func (m *PBDHTMessage) GetValue() []byte { if m != nil { return m.Value } return nil } -func (m *DHTMessage) GetId() uint64 { +func (m *PBDHTMessage) GetId() uint64 { if m != nil && m.Id != nil { return *m.Id } return 0 } -func (m *DHTMessage) GetResponse() bool { +func (m *PBDHTMessage) GetResponse() bool { if m != nil && m.Response != nil { return *m.Response } return false } -func (m *DHTMessage) GetSuccess() bool { +func (m *PBDHTMessage) GetSuccess() bool { if m != nil && m.Success != nil { return *m.Success } return false } +func (m *PBDHTMessage) GetPeers() []*PBDHTMessage_PBPeer { + if m != nil { + return m.Peers + } + return nil +} + +type PBDHTMessage_PBPeer struct { + Id *string `protobuf:"bytes,1,req,name=id" json:"id,omitempty"` + Addr *string `protobuf:"bytes,2,req,name=addr" json:"addr,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PBDHTMessage_PBPeer) Reset() { *m = PBDHTMessage_PBPeer{} } +func (m *PBDHTMessage_PBPeer) String() string { return proto.CompactTextString(m) } +func (*PBDHTMessage_PBPeer) ProtoMessage() {} + +func (m *PBDHTMessage_PBPeer) GetId() string { + if m != nil && m.Id != nil { + return *m.Id + } + return "" +} + +func (m *PBDHTMessage_PBPeer) GetAddr() string { + if m != nil && m.Addr != nil { + return *m.Addr + } + return "" +} + func init() { - proto.RegisterEnum("dht.DHTMessage_MessageType", DHTMessage_MessageType_name, DHTMessage_MessageType_value) + proto.RegisterEnum("dht.PBDHTMessage_MessageType", PBDHTMessage_MessageType_name, PBDHTMessage_MessageType_value) } diff --git a/routing/dht/messages.proto b/routing/dht/messages.proto index 278a95202..4d4e8c61f 100644 --- a/routing/dht/messages.proto +++ b/routing/dht/messages.proto @@ -2,7 +2,7 @@ package dht; //run `protoc --go_out=. *.proto` to generate -message DHTMessage { +message PBDHTMessage { enum MessageType { PUT_VALUE = 0; GET_VALUE = 1; @@ -13,6 +13,11 @@ message DHTMessage { DIAGNOSTIC = 6; } + message PBPeer { + required string id = 1; + required string addr = 2; + } + required MessageType type = 1; optional string key = 2; optional bytes value = 3; @@ -23,4 +28,7 @@ message DHTMessage { // Signals whether or not this message is a response to another message optional bool response = 5; optional bool success = 6; + + // Used for returning peers from queries (normally, peers closer to X) + repeated PBPeer peers = 7; } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 1a90ce76b..57d087fdc 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -1,10 +1,11 @@ package dht import ( - "math/rand" - "time" "bytes" "encoding/json" + "errors" + "math/rand" + "time" proto "code.google.com/p/goprotobuf/proto" @@ -34,7 +35,7 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { var p *peer.Peer p = s.routes[0].NearestPeer(convertKey(key)) if p == nil { - panic("Table returned nil peer!") + return errors.New("Table returned nil peer!") } return s.putValueToPeer(p, string(key), value) @@ -47,13 +48,13 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { var p *peer.Peer p = s.routes[0].NearestPeer(convertKey(key)) if p == nil { - panic("Table returned nil peer!") + return nil, errors.New("Table returned nil peer!") } - pmes := pDHTMessage{ - Type: DHTMessage_GET_VALUE, - Key: string(key), - Id: GenerateMessageID(), + pmes := DHTMessage{ + Type: PBDHTMessage_GET_VALUE, + Key: string(key), + Id: GenerateMessageID(), } response_chan := s.ListenFor(pmes.Id, 1, time.Minute) @@ -68,15 +69,13 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { return nil, u.ErrTimeout case resp, ok := <-response_chan: if !ok { - panic("Channel was closed...") + u.PErr("response channel closed before timeout, please investigate.") + return nil, u.ErrTimeout } - if resp == nil { - panic("Why the hell is this response nil?") - } - pmes_out := new(DHTMessage) + pmes_out := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmes_out) if err != nil { - return nil,err + return nil, err } if pmes_out.GetSuccess() { return pmes_out.GetValue(), nil @@ -96,15 +95,15 @@ func (s *IpfsDHT) Provide(key u.Key) error { //return an error } - pmes := pDHTMessage{ - Type: DHTMessage_ADD_PROVIDER, - Key: string(key), + pmes := DHTMessage{ + Type: PBDHTMessage_ADD_PROVIDER, + Key: string(key), } pbmes := pmes.ToProtobuf() - for _,p := range peers { + for _, p := range peers { mes := swarm.NewMessage(p, pbmes) - s.network.Chan.Outgoing <-mes + s.network.Chan.Outgoing <- mes } return nil } @@ -113,17 +112,17 @@ func (s *IpfsDHT) Provide(key u.Key) error { func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) { p := s.routes[0].NearestPeer(convertKey(key)) - pmes := pDHTMessage{ - Type: DHTMessage_GET_PROVIDERS, - Key: string(key), - Id: GenerateMessageID(), + pmes := DHTMessage{ + Type: PBDHTMessage_GET_PROVIDERS, + Key: string(key), + Id: GenerateMessageID(), } mes := swarm.NewMessage(p, pmes.ToProtobuf()) listen_chan := s.ListenFor(pmes.Id, 1, time.Minute) u.DOut("Find providers for: '%s'", key) - s.network.Chan.Outgoing <-mes + s.network.Chan.Outgoing <- mes after := time.After(timeout) select { case <-after: @@ -131,7 +130,7 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, return nil, u.ErrTimeout case resp := <-listen_chan: u.DOut("FindProviders: got response.") - pmes_out := new(DHTMessage) + pmes_out := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmes_out) if err != nil { return nil, err @@ -143,10 +142,10 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, } var prov_arr []*peer.Peer - for pid,addr := range addrs { + for pid, addr := range addrs { p := s.network.Find(pid) if p == nil { - maddr,err := ma.NewMultiaddr(addr) + maddr, err := ma.NewMultiaddr(addr) if err != nil { u.PErr("error connecting to new peer: %s", err) continue @@ -171,23 +170,23 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) { p := s.routes[0].NearestPeer(convertPeerID(id)) - pmes := pDHTMessage{ - Type: DHTMessage_FIND_NODE, - Key: string(id), - Id: GenerateMessageID(), + pmes := DHTMessage{ + Type: PBDHTMessage_FIND_NODE, + Key: string(id), + Id: GenerateMessageID(), } mes := swarm.NewMessage(p, pmes.ToProtobuf()) listen_chan := s.ListenFor(pmes.Id, 1, time.Minute) - s.network.Chan.Outgoing <-mes + s.network.Chan.Outgoing <- mes after := time.After(timeout) select { case <-after: s.Unlisten(pmes.Id) return nil, u.ErrTimeout case resp := <-listen_chan: - pmes_out := new(DHTMessage) + pmes_out := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmes_out) if err != nil { return nil, err @@ -218,7 +217,7 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { // Thoughts: maybe this should accept an ID and do a peer lookup? u.DOut("Enter Ping.") - pmes := pDHTMessage{Id: GenerateMessageID(), Type: DHTMessage_PING} + pmes := DHTMessage{Id: GenerateMessageID(), Type: PBDHTMessage_PING} mes := swarm.NewMessage(p, pmes.ToProtobuf()) before := time.Now() @@ -229,7 +228,7 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { select { case <-response_chan: roundtrip := time.Since(before) - p.SetDistance(roundtrip) + p.SetLatency(roundtrip) u.POut("Ping took %s.", roundtrip.String()) return nil case <-tout: @@ -246,17 +245,17 @@ func (dht *IpfsDHT) GetDiagnostic(timeout time.Duration) ([]*diagInfo, error) { targets := dht.routes[0].NearestPeers(convertPeerID(dht.self.ID), 10) // TODO: Add timeout to this struct so nodes know when to return - pmes := pDHTMessage{ - Type: DHTMessage_DIAGNOSTIC, - Id: GenerateMessageID(), + pmes := DHTMessage{ + Type: PBDHTMessage_DIAGNOSTIC, + Id: GenerateMessageID(), } - listen_chan := dht.ListenFor(pmes.Id, len(targets), time.Minute * 2) + listen_chan := dht.ListenFor(pmes.Id, len(targets), time.Minute*2) pbmes := pmes.ToProtobuf() - for _,p := range targets { + for _, p := range targets { mes := swarm.NewMessage(p, pbmes) - dht.network.Chan.Outgoing <-mes + dht.network.Chan.Outgoing <- mes } var out []*diagInfo @@ -267,7 +266,7 @@ func (dht *IpfsDHT) GetDiagnostic(timeout time.Duration) ([]*diagInfo, error) { u.DOut("Diagnostic request timed out.") return out, u.ErrTimeout case resp := <-listen_chan: - pmes_out := new(DHTMessage) + pmes_out := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmes_out) if err != nil { // NOTE: here and elsewhere, need to audit error handling, @@ -288,5 +287,5 @@ func (dht *IpfsDHT) GetDiagnostic(timeout time.Duration) ([]*diagInfo, error) { } } - return nil,nil + return nil, nil } diff --git a/routing/dht/table.go b/routing/dht/table.go index be4a4b392..7a121234f 100644 --- a/routing/dht/table.go +++ b/routing/dht/table.go @@ -125,7 +125,7 @@ func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer { func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { rt.tabLock.RLock() defer rt.tabLock.RUnlock() - cpl := xor(id, rt.local).commonPrefixLen() + cpl := prefLen(id, rt.local) // Get bucket at cpl index or last bucket var bucket *Bucket diff --git a/routing/dht/util.go b/routing/dht/util.go index 2adc8b765..5961cd226 100644 --- a/routing/dht/util.go +++ b/routing/dht/util.go @@ -40,6 +40,10 @@ func (id ID) commonPrefixLen() int { return len(id)*8 - 1 } +func prefLen(a, b ID) int { + return xor(a, b).commonPrefixLen() +} + func xor(a, b ID) ID { a, b = equalizeSizes(a, b) From 59b791e9ef4c353b963cab164128987e5c1ccf31 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 8 Aug 2014 19:49:27 -0700 Subject: [PATCH 0044/3817] make tests pass a little more reliably by changing a port to not overlap This commit was moved from ipfs/go-ipfs-routing@f4af2651347c5d95d16ce1fe9bf6f28512240d36 --- routing/dht/dht_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index e24ada020..a5b47cb21 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -13,7 +13,7 @@ import ( func TestPing(t *testing.T) { u.Debug = false - addr_a, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1234") + addr_a, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/2222") if err != nil { t.Fatal(err) } From a59c5f93aeaffd572bdf319245cde62bfd00efeb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 8 Aug 2014 19:58:42 -0700 Subject: [PATCH 0045/3817] moved routing table code into its own package This commit was moved from ipfs/go-ipfs-routing@fed1d4079ee17350b4d15b6cee91b472fea262cf --- routing/dht/dht.go | 13 +++++++------ routing/dht/diag.go | 2 +- routing/dht/routing.go | 13 +++++++------ routing/{dht => kbucket}/bucket.go | 2 +- routing/{dht => kbucket}/table.go | 6 +++--- routing/{dht => kbucket}/table_test.go | 18 +++++++++--------- routing/{dht => kbucket}/util.go | 4 ++-- 7 files changed, 30 insertions(+), 28 deletions(-) rename routing/{dht => kbucket}/bucket.go (96%) rename routing/{dht => kbucket}/table.go (97%) rename routing/{dht => kbucket}/table_test.go (83%) rename routing/{dht => kbucket}/util.go (95%) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 8fbd5c092..7188fc893 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -10,6 +10,7 @@ import ( peer "github.com/jbenet/go-ipfs/peer" swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" + kb "github.com/jbenet/go-ipfs/routing/kbucket" ma "github.com/jbenet/go-multiaddr" @@ -25,7 +26,7 @@ import ( type IpfsDHT struct { // Array of routing tables for differently distanced nodes // NOTE: (currently, only a single table is used) - routes []*RoutingTable + routes []*kb.RoutingTable network *swarm.Swarm @@ -84,8 +85,8 @@ func NewDHT(p *peer.Peer) (*IpfsDHT, error) { dht.listeners = make(map[uint64]*listenInfo) dht.providers = make(map[u.Key][]*providerInfo) dht.shutdown = make(chan struct{}) - dht.routes = make([]*RoutingTable, 1) - dht.routes[0] = NewRoutingTable(20, convertPeerID(p.ID)) + dht.routes = make([]*kb.RoutingTable, 1) + dht.routes[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID)) dht.birth = time.Now() return dht, nil } @@ -253,7 +254,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { } } else if err == ds.ErrNotFound { // Find closest peer(s) to desired key and reply with that info - closer := dht.routes[0].NearestPeer(convertKey(u.Key(pmes.GetKey()))) + closer := dht.routes[0].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) resp = &DHTMessage{ Response: true, Id: *pmes.Id, @@ -290,7 +291,7 @@ func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *PBDHTMessage) { func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) { success := true u.POut("handleFindPeer: searching for '%s'", peer.ID(pmes.GetKey()).Pretty()) - closest := dht.routes[0].NearestPeer(convertKey(u.Key(pmes.GetKey()))) + closest := dht.routes[0].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) if closest == nil { u.PErr("handleFindPeer: could not find anything.") success = false @@ -432,7 +433,7 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { } dht.diaglock.Unlock() - seq := dht.routes[0].NearestPeers(convertPeerID(dht.self.ID), 10) + seq := dht.routes[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) listen_chan := dht.ListenFor(pmes.GetId(), len(seq), time.Second*30) for _, ps := range seq { diff --git a/routing/dht/diag.go b/routing/dht/diag.go index 4bc752f92..50d5a3d50 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -37,7 +37,7 @@ func (dht *IpfsDHT) getDiagInfo() *diagInfo { di.LifeSpan = time.Since(dht.birth) di.Keys = nil // Currently no way to query datastore - for _,p := range dht.routes[0].listpeers() { + for _,p := range dht.routes[0].Listpeers() { di.Connections = append(di.Connections, connDiagInfo{p.GetLatency(), p.ID}) } return di diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 57d087fdc..8898aaf15 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,6 +12,7 @@ import ( ma "github.com/jbenet/go-multiaddr" peer "github.com/jbenet/go-ipfs/peer" + kb "github.com/jbenet/go-ipfs/routing/kbucket" swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" ) @@ -33,7 +34,7 @@ func GenerateMessageID() uint64 { // This is the top level "Store" operation of the DHT func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { var p *peer.Peer - p = s.routes[0].NearestPeer(convertKey(key)) + p = s.routes[0].NearestPeer(kb.ConvertKey(key)) if p == nil { return errors.New("Table returned nil peer!") } @@ -46,7 +47,7 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { // returned along with util.ErrSearchIncomplete func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { var p *peer.Peer - p = s.routes[0].NearestPeer(convertKey(key)) + p = s.routes[0].NearestPeer(kb.ConvertKey(key)) if p == nil { return nil, errors.New("Table returned nil peer!") } @@ -90,7 +91,7 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { // Announce that this node can provide value for given key func (s *IpfsDHT) Provide(key u.Key) error { - peers := s.routes[0].NearestPeers(convertKey(key), PoolSize) + peers := s.routes[0].NearestPeers(kb.ConvertKey(key), PoolSize) if len(peers) == 0 { //return an error } @@ -110,7 +111,7 @@ func (s *IpfsDHT) Provide(key u.Key) error { // FindProviders searches for peers who can provide the value for given key. func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) { - p := s.routes[0].NearestPeer(convertKey(key)) + p := s.routes[0].NearestPeer(kb.ConvertKey(key)) pmes := DHTMessage{ Type: PBDHTMessage_GET_PROVIDERS, @@ -168,7 +169,7 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, // FindPeer searches for a peer with given ID. func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) { - p := s.routes[0].NearestPeer(convertPeerID(id)) + p := s.routes[0].NearestPeer(kb.ConvertPeerID(id)) pmes := DHTMessage{ Type: PBDHTMessage_FIND_NODE, @@ -242,7 +243,7 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { func (dht *IpfsDHT) GetDiagnostic(timeout time.Duration) ([]*diagInfo, error) { u.DOut("Begin Diagnostic") //Send to N closest peers - targets := dht.routes[0].NearestPeers(convertPeerID(dht.self.ID), 10) + targets := dht.routes[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) // TODO: Add timeout to this struct so nodes know when to return pmes := DHTMessage{ diff --git a/routing/dht/bucket.go b/routing/kbucket/bucket.go similarity index 96% rename from routing/dht/bucket.go rename to routing/kbucket/bucket.go index 7aa8d0a94..a56db74fe 100644 --- a/routing/dht/bucket.go +++ b/routing/kbucket/bucket.go @@ -48,7 +48,7 @@ func (b *Bucket) Split(cpl int, target ID) *Bucket { out := list.New() e := bucket_list.Front() for e != nil { - peer_id := convertPeerID(e.Value.(*peer.Peer).ID) + peer_id := ConvertPeerID(e.Value.(*peer.Peer).ID) peer_cpl := prefLen(peer_id, target) if peer_cpl > cpl { cur := e diff --git a/routing/dht/table.go b/routing/kbucket/table.go similarity index 97% rename from routing/dht/table.go rename to routing/kbucket/table.go index 7a121234f..de971ea21 100644 --- a/routing/dht/table.go +++ b/routing/kbucket/table.go @@ -36,7 +36,7 @@ func NewRoutingTable(bucketsize int, local_id ID) *RoutingTable { func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer { rt.tabLock.Lock() defer rt.tabLock.Unlock() - peer_id := convertPeerID(p.ID) + peer_id := ConvertPeerID(p.ID) cpl := xor(peer_id, rt.local).commonPrefixLen() b_id := cpl @@ -97,7 +97,7 @@ func (p peerSorterArr) Less(a, b int) bool { func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) peerSorterArr { for e := peerList.Front(); e != nil; e = e.Next() { p := e.Value.(*peer.Peer) - p_id := convertPeerID(p.ID) + p_id := ConvertPeerID(p.ID) pd := peerDistance{ p: p, distance: xor(target, p_id), @@ -173,7 +173,7 @@ func (rt *RoutingTable) Size() int { } // NOTE: This is potentially unsafe... use at your own risk -func (rt *RoutingTable) listpeers() []*peer.Peer { +func (rt *RoutingTable) Listpeers() []*peer.Peer { var peers []*peer.Peer for _,buck := range rt.Buckets { for e := buck.getIter(); e != nil; e = e.Next() { diff --git a/routing/dht/table_test.go b/routing/kbucket/table_test.go similarity index 83% rename from routing/dht/table_test.go rename to routing/kbucket/table_test.go index 393a1c585..4305d8d1f 100644 --- a/routing/dht/table_test.go +++ b/routing/kbucket/table_test.go @@ -36,7 +36,7 @@ func TestBucket(t *testing.T) { } local := _randPeer() - local_id := convertPeerID(local.ID) + local_id := ConvertPeerID(local.ID) i := rand.Intn(len(peers)) e := b.Find(peers[i].ID) @@ -44,10 +44,10 @@ func TestBucket(t *testing.T) { t.Errorf("Failed to find peer: %v", peers[i]) } - spl := b.Split(0, convertPeerID(local.ID)) + spl := b.Split(0, ConvertPeerID(local.ID)) llist := (*list.List)(b) for e := llist.Front(); e != nil; e = e.Next() { - p := convertPeerID(e.Value.(*peer.Peer).ID) + p := ConvertPeerID(e.Value.(*peer.Peer).ID) cpl := xor(p, local_id).commonPrefixLen() if cpl > 0 { t.Fatalf("Split failed. found id with cpl > 0 in 0 bucket") @@ -56,7 +56,7 @@ func TestBucket(t *testing.T) { rlist := (*list.List)(spl) for e := rlist.Front(); e != nil; e = e.Next() { - p := convertPeerID(e.Value.(*peer.Peer).ID) + p := ConvertPeerID(e.Value.(*peer.Peer).ID) cpl := xor(p, local_id).commonPrefixLen() if cpl == 0 { t.Fatalf("Split failed. found id with cpl == 0 in non 0 bucket") @@ -67,7 +67,7 @@ func TestBucket(t *testing.T) { // Right now, this just makes sure that it doesnt hang or crash func TestTableUpdate(t *testing.T) { local := _randPeer() - rt := NewRoutingTable(10, convertPeerID(local.ID)) + rt := NewRoutingTable(10, ConvertPeerID(local.ID)) peers := make([]*peer.Peer, 100) for i := 0; i < 100; i++ { @@ -93,7 +93,7 @@ func TestTableUpdate(t *testing.T) { func TestTableFind(t *testing.T) { local := _randPeer() - rt := NewRoutingTable(10, convertPeerID(local.ID)) + rt := NewRoutingTable(10, ConvertPeerID(local.ID)) peers := make([]*peer.Peer, 100) for i := 0; i < 5; i++ { @@ -102,7 +102,7 @@ func TestTableFind(t *testing.T) { } t.Logf("Searching for peer: '%s'", peers[2].ID.Pretty()) - found := rt.NearestPeer(convertPeerID(peers[2].ID)) + found := rt.NearestPeer(ConvertPeerID(peers[2].ID)) if !found.ID.Equal(peers[2].ID) { t.Fatalf("Failed to lookup known node...") } @@ -110,7 +110,7 @@ func TestTableFind(t *testing.T) { func TestTableFindMultiple(t *testing.T) { local := _randPeer() - rt := NewRoutingTable(20, convertPeerID(local.ID)) + rt := NewRoutingTable(20, ConvertPeerID(local.ID)) peers := make([]*peer.Peer, 100) for i := 0; i < 18; i++ { @@ -119,7 +119,7 @@ func TestTableFindMultiple(t *testing.T) { } t.Logf("Searching for peer: '%s'", peers[2].ID.Pretty()) - found := rt.NearestPeers(convertPeerID(peers[2].ID), 15) + found := rt.NearestPeers(ConvertPeerID(peers[2].ID), 15) if len(found) != 15 { t.Fatalf("Got back different number of peers than we expected.") } diff --git a/routing/dht/util.go b/routing/kbucket/util.go similarity index 95% rename from routing/dht/util.go rename to routing/kbucket/util.go index 5961cd226..095d0b03e 100644 --- a/routing/dht/util.go +++ b/routing/kbucket/util.go @@ -71,12 +71,12 @@ func equalizeSizes(a, b ID) (ID, ID) { return a, b } -func convertPeerID(id peer.ID) ID { +func ConvertPeerID(id peer.ID) ID { hash := sha256.Sum256(id) return hash[:] } -func convertKey(id u.Key) ID { +func ConvertKey(id u.Key) ID { hash := sha256.Sum256([]byte(id)) return hash[:] } From ea9f8367821d4007a7b3a55b515e44aa70ce2105 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 9 Aug 2014 22:28:46 -0700 Subject: [PATCH 0046/3817] tiered put/get implemented This commit was moved from ipfs/go-ipfs-routing@2af8878482b878fb1f8e2bb6c5260bf68642ecc8 --- routing/dht/DHTMessage.go | 21 ++++++ routing/dht/dht.go | 137 ++++++++++++++++++++++++++++------ routing/dht/dht_test.go | 84 ++++++++++++++++++++- routing/dht/diag.go | 11 ++- routing/dht/routing.go | 85 ++++++++++----------- routing/kbucket/bucket.go | 1 + routing/kbucket/table.go | 35 ++++++--- routing/kbucket/table_test.go | 2 +- 8 files changed, 283 insertions(+), 93 deletions(-) diff --git a/routing/dht/DHTMessage.go b/routing/dht/DHTMessage.go index 64ca5bcab..701f36687 100644 --- a/routing/dht/DHTMessage.go +++ b/routing/dht/DHTMessage.go @@ -1,5 +1,9 @@ package dht +import ( + peer "github.com/jbenet/go-ipfs/peer" +) + // A helper struct to make working with protbuf types easier type DHTMessage struct { Type PBDHTMessage_MessageType @@ -8,6 +12,20 @@ type DHTMessage struct { Response bool Id uint64 Success bool + Peers []*peer.Peer +} + +func peerInfo(p *peer.Peer) *PBDHTMessage_PBPeer { + pbp := new(PBDHTMessage_PBPeer) + addr, err := p.Addresses[0].String() + if err != nil { + //Temp: what situations could cause this? + panic(err) + } + pbp.Addr = &addr + pid := string(p.ID) + pbp.Id = &pid + return pbp } func (m *DHTMessage) ToProtobuf() *PBDHTMessage { @@ -21,6 +39,9 @@ func (m *DHTMessage) ToProtobuf() *PBDHTMessage { pmes.Response = &m.Response pmes.Id = &m.Id pmes.Success = &m.Success + for _, p := range m.Peers { + pmes.Peers = append(pmes.Peers, peerInfo(p)) + } return pmes } diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 7188fc893..10954d2ba 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -8,9 +8,9 @@ import ( "time" peer "github.com/jbenet/go-ipfs/peer" + kb "github.com/jbenet/go-ipfs/routing/kbucket" swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" - kb "github.com/jbenet/go-ipfs/routing/kbucket" ma "github.com/jbenet/go-multiaddr" @@ -37,7 +37,6 @@ type IpfsDHT struct { datastore ds.Datastore // Map keys to peers that can provide their value - // TODO: implement a TTL on each of these keys providers map[u.Key][]*providerInfo providerLock sync.RWMutex @@ -67,7 +66,7 @@ type listenInfo struct { eol time.Time } -// Create a new DHT object with the given peer as the 'local' host +// NewDHT creates a new DHT object with the given peer as the 'local' host func NewDHT(p *peer.Peer) (*IpfsDHT, error) { if p == nil { return nil, errors.New("nil peer passed to NewDHT()") @@ -111,7 +110,7 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { // NOTE: this should be done better... err = dht.Ping(npeer, time.Second*2) if err != nil { - return nil, errors.New("Failed to ping newly connected peer.") + return nil, errors.New("failed to ping newly connected peer") } return npeer, nil @@ -227,7 +226,7 @@ func (dht *IpfsDHT) cleanExpiredListeners() { dht.listenLock.Unlock() } -func (dht *IpfsDHT) putValueToPeer(p *peer.Peer, key string, value []byte) error { +func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) error { pmes := DHTMessage{ Type: PBDHTMessage_PUT_VALUE, Key: key, @@ -242,26 +241,32 @@ func (dht *IpfsDHT) putValueToPeer(p *peer.Peer, key string, value []byte) error func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { dskey := ds.NewKey(pmes.GetKey()) - var resp *DHTMessage - i_val, err := dht.datastore.Get(dskey) + resp := &DHTMessage{ + Response: true, + Id: pmes.GetId(), + Key: pmes.GetKey(), + } + iVal, err := dht.datastore.Get(dskey) if err == nil { - resp = &DHTMessage{ - Response: true, - Id: *pmes.Id, - Key: *pmes.Key, - Value: i_val.([]byte), - Success: true, - } + resp.Success = true + resp.Value = iVal.([]byte) } else if err == ds.ErrNotFound { - // Find closest peer(s) to desired key and reply with that info - closer := dht.routes[0].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) - resp = &DHTMessage{ - Response: true, - Id: *pmes.Id, - Key: *pmes.Key, - Value: closer.ID, - Success: false, + // Check if we know any providers for the requested value + provs, ok := dht.providers[u.Key(pmes.GetKey())] + if ok && len(provs) > 0 { + for _, prov := range provs { + resp.Peers = append(resp.Peers, prov.Value) + } + resp.Success = true + } else { + // No providers? + // Find closest peer(s) to desired key and reply with that info + closer := dht.routes[0].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) + resp.Peers = []*peer.Peer{closer} } + } else { + //temp: what other errors can a datastore throw? + panic(err) } mes := swarm.NewMessage(p, resp.ToProtobuf()) @@ -397,6 +402,7 @@ func (dht *IpfsDHT) Unlisten(mesid uint64) { close(list.resp) } +// Check whether or not the dht is currently listening for mesid func (dht *IpfsDHT) IsListening(mesid uint64) bool { dht.listenLock.RLock() li, ok := dht.listeners[mesid] @@ -424,6 +430,7 @@ func (dht *IpfsDHT) addProviderEntry(key u.Key, p *peer.Peer) { dht.providerLock.Unlock() } +// NOTE: not yet finished, low priority func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { dht.diaglock.Lock() if dht.IsListening(pmes.GetId()) { @@ -434,7 +441,7 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { dht.diaglock.Unlock() seq := dht.routes[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) - listen_chan := dht.ListenFor(pmes.GetId(), len(seq), time.Second*30) + listenChan := dht.ListenFor(pmes.GetId(), len(seq), time.Second*30) for _, ps := range seq { mes := swarm.NewMessage(ps, pmes) @@ -453,7 +460,7 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { case <-after: //Timeout, return what we have goto out - case req_resp := <-listen_chan: + case req_resp := <-listenChan: pmes_out := new(PBDHTMessage) err := proto.Unmarshal(req_resp.Data, pmes_out) if err != nil { @@ -477,6 +484,77 @@ out: dht.network.Chan.Outgoing <- mes } +func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duration) ([]byte, error) { + pmes := DHTMessage{ + Type: PBDHTMessage_GET_VALUE, + Key: string(key), + Id: GenerateMessageID(), + } + response_chan := dht.ListenFor(pmes.Id, 1, time.Minute) + + mes := swarm.NewMessage(p, pmes.ToProtobuf()) + dht.network.Chan.Outgoing <- mes + + // Wait for either the response or a timeout + timeup := time.After(timeout) + select { + case <-timeup: + dht.Unlisten(pmes.Id) + return nil, u.ErrTimeout + case resp, ok := <-response_chan: + if !ok { + u.PErr("response channel closed before timeout, please investigate.") + return nil, u.ErrTimeout + } + pmes_out := new(PBDHTMessage) + err := proto.Unmarshal(resp.Data, pmes_out) + if err != nil { + return nil, err + } + // TODO: debate moving this logic out of this function to be handled by the caller + if pmes_out.GetSuccess() { + if pmes_out.Value == nil { + // We were given provider[s] + return dht.getFromProviderList(key, timeout, pmes_out.GetPeers()) + } + // We were given the value + return pmes_out.GetValue(), nil + } else { + return pmes_out.GetValue(), u.ErrSearchIncomplete + } + } +} + +// TODO: Im not certain on this implementation, we get a list of providers from someone +// what do we do with it? Connect to each of them? randomly pick one to get the value from? +// Or just connect to one at a time until we get a successful connection and request the +// value from it? +func (dht *IpfsDHT) getFromProviderList(key u.Key, timeout time.Duration, provlist []*PBDHTMessage_PBPeer) ([]byte, error) { + for _, prov := range provlist { + prov_p, _ := dht.Find(peer.ID(prov.GetId())) + if prov_p == nil { + maddr, err := ma.NewMultiaddr(prov.GetAddr()) + if err != nil { + u.PErr("getValue error: %s", err) + continue + } + prov_p, err = dht.Connect(maddr) + if err != nil { + u.PErr("getValue error: %s", err) + continue + } + } + data, err := dht.getValueSingle(prov_p, key, timeout) + if err != nil { + u.DErr("getFromProvs error: %s", err) + continue + } + + return data, nil + } + return nil, u.ErrNotFound +} + func (dht *IpfsDHT) GetLocal(key u.Key) ([]byte, error) { v, err := dht.datastore.Get(ds.NewKey(string(key))) if err != nil { @@ -495,3 +573,14 @@ func (dht *IpfsDHT) Update(p *peer.Peer) { dht.network.Drop(removed) } } + +// Look for a peer with a given ID connected to this dht +func (dht *IpfsDHT) Find(id peer.ID) (*peer.Peer, *kb.RoutingTable) { + for _, table := range dht.routes { + p := table.Find(id) + if p != nil { + return p, table + } + } + return nil, nil +} diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index a5b47cb21..177128978 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -90,15 +90,21 @@ func TestValueGetSet(t *testing.T) { dht_a.Start() dht_b.Start() + go func() { + select { + case err := <-dht_a.network.Chan.Errors: + t.Fatal(err) + case err := <-dht_b.network.Chan.Errors: + t.Fatal(err) + } + }() + _, err = dht_a.Connect(addr_b) if err != nil { t.Fatal(err) } - err = dht_a.PutValue("hello", []byte("world")) - if err != nil { - t.Fatal(err) - } + dht_a.PutValue("hello", []byte("world")) val, err := dht_a.GetValue("hello", time.Second*2) if err != nil { @@ -179,3 +185,73 @@ func TestProvides(t *testing.T) { dhts[i].Halt() } } + +func TestLayeredGet(t *testing.T) { + u.Debug = false + var addrs []*ma.Multiaddr + for i := 0; i < 4; i++ { + a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) + if err != nil { + t.Fatal(err) + } + addrs = append(addrs, a) + } + + var peers []*peer.Peer + for i := 0; i < 4; i++ { + p := new(peer.Peer) + p.AddAddress(addrs[i]) + p.ID = peer.ID([]byte(fmt.Sprintf("peer_%d", i))) + peers = append(peers, p) + } + + var dhts []*IpfsDHT + for i := 0; i < 4; i++ { + d, err := NewDHT(peers[i]) + if err != nil { + t.Fatal(err) + } + dhts = append(dhts, d) + d.Start() + } + + _, err := dhts[0].Connect(addrs[1]) + if err != nil { + t.Fatal(err) + } + + _, err = dhts[1].Connect(addrs[2]) + if err != nil { + t.Fatal(err) + } + + _, err = dhts[1].Connect(addrs[3]) + if err != nil { + t.Fatal(err) + } + + err = dhts[3].PutLocal(u.Key("hello"), []byte("world")) + if err != nil { + t.Fatal(err) + } + + err = dhts[3].Provide(u.Key("hello")) + if err != nil { + t.Fatal(err) + } + + time.Sleep(time.Millisecond * 60) + + val, err := dhts[0].GetValue(u.Key("hello"), time.Second) + if err != nil { + t.Fatal(err) + } + + if string(val) != "world" { + t.Fatal("Got incorrect value.") + } + + for i := 0; i < 4; i++ { + dhts[i].Halt() + } +} diff --git a/routing/dht/diag.go b/routing/dht/diag.go index 50d5a3d50..03997c5e7 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -9,14 +9,14 @@ import ( type connDiagInfo struct { Latency time.Duration - Id peer.ID + Id peer.ID } type diagInfo struct { - Id peer.ID + Id peer.ID Connections []connDiagInfo - Keys []string - LifeSpan time.Duration + Keys []string + LifeSpan time.Duration CodeVersion string } @@ -29,7 +29,6 @@ func (di *diagInfo) Marshal() []byte { return b } - func (dht *IpfsDHT) getDiagInfo() *diagInfo { di := new(diagInfo) di.CodeVersion = "github.com/jbenet/go-ipfs" @@ -37,7 +36,7 @@ func (dht *IpfsDHT) getDiagInfo() *diagInfo { di.LifeSpan = time.Since(dht.birth) di.Keys = nil // Currently no way to query datastore - for _,p := range dht.routes[0].Listpeers() { + for _, p := range dht.routes[0].Listpeers() { di.Connections = append(di.Connections, connDiagInfo{p.GetLatency(), p.ID}) } return di diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 8898aaf15..2c6a9b74c 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "errors" + "fmt" "math/rand" "time" @@ -32,58 +33,50 @@ func GenerateMessageID() uint64 { // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT -func (s *IpfsDHT) PutValue(key u.Key, value []byte) error { - var p *peer.Peer - p = s.routes[0].NearestPeer(kb.ConvertKey(key)) - if p == nil { - return errors.New("Table returned nil peer!") +func (s *IpfsDHT) PutValue(key u.Key, value []byte) { + complete := make(chan struct{}) + for i, route := range s.routes { + p := route.NearestPeer(kb.ConvertKey(key)) + if p == nil { + s.network.Chan.Errors <- fmt.Errorf("No peer found on level %d", i) + continue + go func() { + complete <- struct{}{} + }() + } + go func() { + err := s.putValueToNetwork(p, string(key), value) + if err != nil { + s.network.Chan.Errors <- err + } + complete <- struct{}{} + }() + } + for _, _ = range s.routes { + <-complete } - - return s.putValueToPeer(p, string(key), value) } // GetValue searches for the value corresponding to given Key. // If the search does not succeed, a multiaddr string of a closer peer is // returned along with util.ErrSearchIncomplete func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { - var p *peer.Peer - p = s.routes[0].NearestPeer(kb.ConvertKey(key)) - if p == nil { - return nil, errors.New("Table returned nil peer!") - } - - pmes := DHTMessage{ - Type: PBDHTMessage_GET_VALUE, - Key: string(key), - Id: GenerateMessageID(), - } - response_chan := s.ListenFor(pmes.Id, 1, time.Minute) - - mes := swarm.NewMessage(p, pmes.ToProtobuf()) - s.network.Chan.Outgoing <- mes + for _, route := range s.routes { + var p *peer.Peer + p = route.NearestPeer(kb.ConvertKey(key)) + if p == nil { + return nil, errors.New("Table returned nil peer!") + } - // Wait for either the response or a timeout - timeup := time.After(timeout) - select { - case <-timeup: - s.Unlisten(pmes.Id) - return nil, u.ErrTimeout - case resp, ok := <-response_chan: - if !ok { - u.PErr("response channel closed before timeout, please investigate.") - return nil, u.ErrTimeout + b, err := s.getValueSingle(p, key, timeout) + if err == nil { + return b, nil } - pmes_out := new(PBDHTMessage) - err := proto.Unmarshal(resp.Data, pmes_out) - if err != nil { + if err != u.ErrSearchIncomplete { return nil, err } - if pmes_out.GetSuccess() { - return pmes_out.GetValue(), nil - } else { - return pmes_out.GetValue(), u.ErrSearchIncomplete - } } + return nil, u.ErrNotFound } // Value provider layer of indirection. @@ -121,7 +114,7 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, mes := swarm.NewMessage(p, pmes.ToProtobuf()) - listen_chan := s.ListenFor(pmes.Id, 1, time.Minute) + listenChan := s.ListenFor(pmes.Id, 1, time.Minute) u.DOut("Find providers for: '%s'", key) s.network.Chan.Outgoing <- mes after := time.After(timeout) @@ -129,7 +122,7 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, case <-after: s.Unlisten(pmes.Id) return nil, u.ErrTimeout - case resp := <-listen_chan: + case resp := <-listenChan: u.DOut("FindProviders: got response.") pmes_out := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmes_out) @@ -179,14 +172,14 @@ func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error mes := swarm.NewMessage(p, pmes.ToProtobuf()) - listen_chan := s.ListenFor(pmes.Id, 1, time.Minute) + listenChan := s.ListenFor(pmes.Id, 1, time.Minute) s.network.Chan.Outgoing <- mes after := time.After(timeout) select { case <-after: s.Unlisten(pmes.Id) return nil, u.ErrTimeout - case resp := <-listen_chan: + case resp := <-listenChan: pmes_out := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmes_out) if err != nil { @@ -251,7 +244,7 @@ func (dht *IpfsDHT) GetDiagnostic(timeout time.Duration) ([]*diagInfo, error) { Id: GenerateMessageID(), } - listen_chan := dht.ListenFor(pmes.Id, len(targets), time.Minute*2) + listenChan := dht.ListenFor(pmes.Id, len(targets), time.Minute*2) pbmes := pmes.ToProtobuf() for _, p := range targets { @@ -266,7 +259,7 @@ func (dht *IpfsDHT) GetDiagnostic(timeout time.Duration) ([]*diagInfo, error) { case <-after: u.DOut("Diagnostic request timed out.") return out, u.ErrTimeout - case resp := <-listen_chan: + case resp := <-listenChan: pmes_out := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmes_out) if err != nil { diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index a56db74fe..5abd2c910 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -5,6 +5,7 @@ import ( peer "github.com/jbenet/go-ipfs/peer" ) + // Bucket holds a list of peers. type Bucket list.List diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index de971ea21..788d12265 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -19,7 +19,7 @@ type RoutingTable struct { tabLock sync.RWMutex // kBuckets define all the fingers to other nodes. - Buckets []*Bucket + Buckets []*Bucket bucketsize int } @@ -52,7 +52,7 @@ func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer { // Are we past the max bucket size? if bucket.Len() > rt.bucketsize { - if b_id == len(rt.Buckets) - 1 { + if b_id == len(rt.Buckets)-1 { new_bucket := bucket.Split(b_id, rt.local) rt.Buckets = append(rt.Buckets, new_bucket) if new_bucket.Len() > rt.bucketsize { @@ -81,25 +81,27 @@ func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer { // A helper struct to sort peers by their distance to the local node type peerDistance struct { - p *peer.Peer + p *peer.Peer distance ID } // peerSorterArr implements sort.Interface to sort peers by xor distance type peerSorterArr []*peerDistance -func (p peerSorterArr) Len() int {return len(p)} -func (p peerSorterArr) Swap(a, b int) {p[a],p[b] = p[b],p[a]} + +func (p peerSorterArr) Len() int { return len(p) } +func (p peerSorterArr) Swap(a, b int) { p[a], p[b] = p[b], p[a] } func (p peerSorterArr) Less(a, b int) bool { return p[a].distance.Less(p[b].distance) } + // func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) peerSorterArr { - for e := peerList.Front(); e != nil; e = e.Next() { + for e := peerList.Front(); e != nil; e = e.Next() { p := e.Value.(*peer.Peer) p_id := ConvertPeerID(p.ID) pd := peerDistance{ - p: p, + p: p, distance: xor(target, p_id), } peerArr = append(peerArr, &pd) @@ -111,6 +113,15 @@ func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) pe return peerArr } +// Find a specific peer by ID or return nil +func (rt *RoutingTable) Find(id peer.ID) *peer.Peer { + srch := rt.NearestPeers(ConvertPeerID(id), 1) + if len(srch) == 0 || !srch[0].ID.Equal(id) { + return nil + } + return srch[0] +} + // Returns a single peer that is nearest to the given ID func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer { peers := rt.NearestPeers(id, 1) @@ -139,12 +150,12 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { // In the case of an unusual split, one bucket may be empty. // if this happens, search both surrounding buckets for nearest peer if cpl > 0 { - plist := (*list.List)(rt.Buckets[cpl - 1]) + plist := (*list.List)(rt.Buckets[cpl-1]) peerArr = copyPeersFromList(id, peerArr, plist) } - if cpl < len(rt.Buckets) - 1 { - plist := (*list.List)(rt.Buckets[cpl + 1]) + if cpl < len(rt.Buckets)-1 { + plist := (*list.List)(rt.Buckets[cpl+1]) peerArr = copyPeersFromList(id, peerArr, plist) } } else { @@ -166,7 +177,7 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { // Returns the total number of peers in the routing table func (rt *RoutingTable) Size() int { var tot int - for _,buck := range rt.Buckets { + for _, buck := range rt.Buckets { tot += buck.Len() } return tot @@ -175,7 +186,7 @@ func (rt *RoutingTable) Size() int { // NOTE: This is potentially unsafe... use at your own risk func (rt *RoutingTable) Listpeers() []*peer.Peer { var peers []*peer.Peer - for _,buck := range rt.Buckets { + for _, buck := range rt.Buckets { for e := buck.getIter(); e != nil; e = e.Next() { peers = append(peers, e.Value.(*peer.Peer)) } diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 4305d8d1f..842c92510 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -1,10 +1,10 @@ package dht import ( + "container/list" crand "crypto/rand" "crypto/sha256" "math/rand" - "container/list" "testing" peer "github.com/jbenet/go-ipfs/peer" From 032e25e412e1c3ea15a50de93c8bbf68c4baabf3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 10 Aug 2014 21:02:05 -0700 Subject: [PATCH 0047/3817] more work implementing coral type lookups This commit was moved from ipfs/go-ipfs-routing@e3e249ee656ca63b84aa91b9645752835a041d02 --- routing/dht/dht.go | 211 +++++++++++++++++++++------------------- routing/dht/dht_test.go | 111 +++++++++++++++++---- routing/dht/routing.go | 137 ++++++++++++++------------ routing/kbucket/util.go | 20 +++- 4 files changed, 296 insertions(+), 183 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 10954d2ba..388ab9ab1 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -2,7 +2,6 @@ package dht import ( "bytes" - "encoding/json" "errors" "sync" "time" @@ -28,7 +27,7 @@ type IpfsDHT struct { // NOTE: (currently, only a single table is used) routes []*kb.RoutingTable - network *swarm.Swarm + network swarm.Network // Local peer (yourself) self *peer.Peer @@ -95,7 +94,7 @@ func (dht *IpfsDHT) Start() { go dht.handleMessages() } -// Connect to a new peer at the given address +// Connect to a new peer at the given address, ping and add to the routing table func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { maddrstr, _ := addr.String() u.DOut("Connect to new peer: %s", maddrstr) @@ -104,8 +103,6 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { return nil, err } - dht.Update(npeer) - // Ping new peer to register in their routing table // NOTE: this should be done better... err = dht.Ping(npeer, time.Second*2) @@ -113,6 +110,8 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { return nil, errors.New("failed to ping newly connected peer") } + dht.Update(npeer) + return npeer, nil } @@ -122,9 +121,10 @@ func (dht *IpfsDHT) handleMessages() { u.DOut("Begin message handling routine") checkTimeouts := time.NewTicker(time.Minute * 5) + ch := dht.network.GetChan() for { select { - case mes, ok := <-dht.network.Chan.Incoming: + case mes, ok := <-ch.Incoming: if !ok { u.DOut("handleMessages closing, bad recv on incoming") return @@ -184,8 +184,8 @@ func (dht *IpfsDHT) handleMessages() { dht.handleDiagnostic(mes.Peer, pmes) } - case err := <-dht.network.Chan.Errors: - u.DErr("dht err: %s", err) + case err := <-ch.Errors: + u.PErr("dht err: %s", err) case <-dht.shutdown: checkTimeouts.Stop() return @@ -235,7 +235,7 @@ func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) er } mes := swarm.NewMessage(p, pmes.ToProtobuf()) - dht.network.Chan.Outgoing <- mes + dht.network.Send(mes) return nil } @@ -260,17 +260,26 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { resp.Success = true } else { // No providers? - // Find closest peer(s) to desired key and reply with that info - closer := dht.routes[0].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) - resp.Peers = []*peer.Peer{closer} + // Find closest peer on given cluster to desired key and reply with that info + + level := pmes.GetValue()[0] // Using value field to specify cluster level + + closer := dht.routes[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) + + // If this peer is closer than the one from the table, return nil + if kb.Closer(dht.self.ID, closer.ID, u.Key(pmes.GetKey())) { + resp.Peers = nil + } else { + resp.Peers = []*peer.Peer{closer} + } } } else { - //temp: what other errors can a datastore throw? + //temp: what other errors can a datastore return? panic(err) } mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.network.Chan.Outgoing <- mes + dht.network.Send(mes) } // Store a value in this peer local storage @@ -290,84 +299,66 @@ func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *PBDHTMessage) { Id: pmes.GetId(), } - dht.network.Chan.Outgoing <- swarm.NewMessage(p, resp.ToProtobuf()) + dht.network.Send(swarm.NewMessage(p, resp.ToProtobuf())) } func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) { - success := true - u.POut("handleFindPeer: searching for '%s'", peer.ID(pmes.GetKey()).Pretty()) - closest := dht.routes[0].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) + resp := DHTMessage{ + Type: pmes.GetType(), + Id: pmes.GetId(), + Response: true, + } + defer func() { + mes := swarm.NewMessage(p, resp.ToProtobuf()) + dht.network.Send(mes) + }() + level := pmes.GetValue()[0] + u.DOut("handleFindPeer: searching for '%s'", peer.ID(pmes.GetKey()).Pretty()) + closest := dht.routes[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) if closest == nil { u.PErr("handleFindPeer: could not find anything.") - success = false + return } if len(closest.Addresses) == 0 { u.PErr("handleFindPeer: no addresses for connected peer...") - success = false + return } - u.POut("handleFindPeer: sending back '%s'", closest.ID.Pretty()) - - addr, err := closest.Addresses[0].String() - if err != nil { - u.PErr(err.Error()) - success = false + // If the found peer further away than this peer... + if kb.Closer(dht.self.ID, closest.ID, u.Key(pmes.GetKey())) { + return } + u.DOut("handleFindPeer: sending back '%s'", closest.ID.Pretty()) + resp.Peers = []*peer.Peer{closest} + resp.Success = true +} + +func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *PBDHTMessage) { resp := DHTMessage{ - Type: pmes.GetType(), - Response: true, + Type: PBDHTMessage_GET_PROVIDERS, + Key: pmes.GetKey(), Id: pmes.GetId(), - Value: []byte(addr), - Success: success, + Response: true, } - mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.network.Chan.Outgoing <- mes -} - -func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *PBDHTMessage) { dht.providerLock.RLock() providers := dht.providers[u.Key(pmes.GetKey())] dht.providerLock.RUnlock() if providers == nil || len(providers) == 0 { - // ????? - u.DOut("No known providers for requested key.") - } - - // This is just a quick hack, formalize method of sending addrs later - addrs := make(map[u.Key]string) - for _, prov := range providers { - ma := prov.Value.NetAddress("tcp") - str, err := ma.String() - if err != nil { - u.PErr("Error: %s", err) - continue + // TODO: work on tiering this + closer := dht.routes[0].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) + resp.Peers = []*peer.Peer{closer} + } else { + for _, prov := range providers { + resp.Peers = append(resp.Peers, prov.Value) } - - addrs[prov.Value.Key()] = str - } - - success := true - data, err := json.Marshal(addrs) - if err != nil { - u.POut("handleGetProviders: error marshalling struct to JSON: %s", err) - data = nil - success = false - } - - resp := DHTMessage{ - Type: PBDHTMessage_GET_PROVIDERS, - Key: pmes.GetKey(), - Value: data, - Id: pmes.GetId(), - Response: true, - Success: success, + resp.Success = true } mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.network.Chan.Outgoing <- mes + dht.network.Send(mes) } type providerInfo struct { @@ -445,7 +436,7 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { for _, ps := range seq { mes := swarm.NewMessage(ps, pmes) - dht.network.Chan.Outgoing <- mes + dht.network.Send(mes) } buf := new(bytes.Buffer) @@ -481,19 +472,21 @@ out: } mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.network.Chan.Outgoing <- mes + dht.network.Send(mes) } -func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duration) ([]byte, error) { +// getValueSingle simply performs the get value RPC with the given parameters +func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duration, level int) (*PBDHTMessage, error) { pmes := DHTMessage{ - Type: PBDHTMessage_GET_VALUE, - Key: string(key), - Id: GenerateMessageID(), + Type: PBDHTMessage_GET_VALUE, + Key: string(key), + Value: []byte{byte(level)}, + Id: GenerateMessageID(), } response_chan := dht.ListenFor(pmes.Id, 1, time.Minute) mes := swarm.NewMessage(p, pmes.ToProtobuf()) - dht.network.Chan.Outgoing <- mes + dht.network.Send(mes) // Wait for either the response or a timeout timeup := time.After(timeout) @@ -511,46 +504,41 @@ func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duratio if err != nil { return nil, err } - // TODO: debate moving this logic out of this function to be handled by the caller - if pmes_out.GetSuccess() { - if pmes_out.Value == nil { - // We were given provider[s] - return dht.getFromProviderList(key, timeout, pmes_out.GetPeers()) - } - // We were given the value - return pmes_out.GetValue(), nil - } else { - return pmes_out.GetValue(), u.ErrSearchIncomplete - } + return pmes_out, nil } } -// TODO: Im not certain on this implementation, we get a list of providers from someone -// what do we do with it? Connect to each of them? randomly pick one to get the value from? -// Or just connect to one at a time until we get a successful connection and request the -// value from it? -func (dht *IpfsDHT) getFromProviderList(key u.Key, timeout time.Duration, provlist []*PBDHTMessage_PBPeer) ([]byte, error) { - for _, prov := range provlist { - prov_p, _ := dht.Find(peer.ID(prov.GetId())) - if prov_p == nil { - maddr, err := ma.NewMultiaddr(prov.GetAddr()) +// TODO: Im not certain on this implementation, we get a list of peers/providers +// from someone what do we do with it? Connect to each of them? randomly pick +// one to get the value from? Or just connect to one at a time until we get a +// successful connection and request the value from it? +func (dht *IpfsDHT) getFromPeerList(key u.Key, timeout time.Duration, + peerlist []*PBDHTMessage_PBPeer, level int) ([]byte, error) { + for _, pinfo := range peerlist { + p, _ := dht.Find(peer.ID(pinfo.GetId())) + if p == nil { + maddr, err := ma.NewMultiaddr(pinfo.GetAddr()) if err != nil { u.PErr("getValue error: %s", err) continue } - prov_p, err = dht.Connect(maddr) + p, err = dht.Connect(maddr) if err != nil { u.PErr("getValue error: %s", err) continue } } - data, err := dht.getValueSingle(prov_p, key, timeout) + pmes, err := dht.getValueSingle(p, key, timeout, level) if err != nil { - u.DErr("getFromProvs error: %s", err) + u.DErr("getFromPeers error: %s", err) continue } + dht.addProviderEntry(key, p) - return data, nil + // Make sure it was a successful get + if pmes.GetSuccess() && pmes.Value != nil { + return pmes.GetValue(), nil + } } return nil, u.ErrNotFound } @@ -584,3 +572,30 @@ func (dht *IpfsDHT) Find(id peer.ID) (*peer.Peer, *kb.RoutingTable) { } return nil, nil } + +func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Duration, level int) (*PBDHTMessage, error) { + pmes := DHTMessage{ + Type: PBDHTMessage_FIND_NODE, + Key: string(id), + Id: GenerateMessageID(), + Value: []byte{byte(level)}, + } + + mes := swarm.NewMessage(p, pmes.ToProtobuf()) + listenChan := dht.ListenFor(pmes.Id, 1, time.Minute) + dht.network.Send(mes) + after := time.After(timeout) + select { + case <-after: + dht.Unlisten(pmes.Id) + return nil, u.ErrTimeout + case resp := <-listenChan: + pmes_out := new(PBDHTMessage) + err := proto.Unmarshal(resp.Data, pmes_out) + if err != nil { + return nil, err + } + + return pmes_out, nil + } +} diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 177128978..3d1679397 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -11,6 +11,37 @@ import ( "time" ) +func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) { + var addrs []*ma.Multiaddr + for i := 0; i < 4; i++ { + a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) + if err != nil { + t.Fatal(err) + } + addrs = append(addrs, a) + } + + var peers []*peer.Peer + for i := 0; i < 4; i++ { + p := new(peer.Peer) + p.AddAddress(addrs[i]) + p.ID = peer.ID([]byte(fmt.Sprintf("peer_%d", i))) + peers = append(peers, p) + } + + var dhts []*IpfsDHT + for i := 0; i < 4; i++ { + d, err := NewDHT(peers[i]) + if err != nil { + t.Fatal(err) + } + dhts = append(dhts, d) + d.Start() + } + + return addrs, peers, dhts +} + func TestPing(t *testing.T) { u.Debug = false addr_a, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/2222") @@ -90,11 +121,13 @@ func TestValueGetSet(t *testing.T) { dht_a.Start() dht_b.Start() + errsa := dht_a.network.GetChan().Errors + errsb := dht_b.network.GetChan().Errors go func() { select { - case err := <-dht_a.network.Chan.Errors: + case err := <-errsa: t.Fatal(err) - case err := <-dht_b.network.Chan.Errors: + case err := <-errsb: t.Fatal(err) } }() @@ -118,6 +151,52 @@ func TestValueGetSet(t *testing.T) { func TestProvides(t *testing.T) { u.Debug = false + + addrs, _, dhts := setupDHTS(4, t) + + _, err := dhts[0].Connect(addrs[1]) + if err != nil { + t.Fatal(err) + } + + _, err = dhts[1].Connect(addrs[2]) + if err != nil { + t.Fatal(err) + } + + _, err = dhts[1].Connect(addrs[3]) + if err != nil { + t.Fatal(err) + } + + err = dhts[3].PutLocal(u.Key("hello"), []byte("world")) + if err != nil { + t.Fatal(err) + } + + err = dhts[3].Provide(u.Key("hello")) + if err != nil { + t.Fatal(err) + } + + time.Sleep(time.Millisecond * 60) + + provs, err := dhts[0].FindProviders(u.Key("hello"), time.Second) + if err != nil { + t.Fatal(err) + } + + if len(provs) != 1 { + t.Fatal("Didnt get back providers") + } + + for i := 0; i < 4; i++ { + dhts[i].Halt() + } +} + +func TestLayeredGet(t *testing.T) { + u.Debug = false var addrs []*ma.Multiaddr for i := 0; i < 4; i++ { a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) @@ -147,7 +226,7 @@ func TestProvides(t *testing.T) { _, err := dhts[0].Connect(addrs[1]) if err != nil { - t.Fatal(err) + t.Fatalf("Failed to connect: %s", err) } _, err = dhts[1].Connect(addrs[2]) @@ -172,13 +251,13 @@ func TestProvides(t *testing.T) { time.Sleep(time.Millisecond * 60) - provs, err := dhts[0].FindProviders(u.Key("hello"), time.Second) + val, err := dhts[0].GetValue(u.Key("hello"), time.Second) if err != nil { t.Fatal(err) } - if len(provs) != 1 { - t.Fatal("Didnt get back providers") + if string(val) != "world" { + t.Fatal("Got incorrect value.") } for i := 0; i < 4; i++ { @@ -186,7 +265,7 @@ func TestProvides(t *testing.T) { } } -func TestLayeredGet(t *testing.T) { +func TestFindPeer(t *testing.T) { u.Debug = false var addrs []*ma.Multiaddr for i := 0; i < 4; i++ { @@ -230,25 +309,17 @@ func TestLayeredGet(t *testing.T) { t.Fatal(err) } - err = dhts[3].PutLocal(u.Key("hello"), []byte("world")) + p, err := dhts[0].FindPeer(peers[2].ID, time.Second) if err != nil { t.Fatal(err) } - err = dhts[3].Provide(u.Key("hello")) - if err != nil { - t.Fatal(err) + if p == nil { + t.Fatal("Failed to find peer.") } - time.Sleep(time.Millisecond * 60) - - val, err := dhts[0].GetValue(u.Key("hello"), time.Second) - if err != nil { - t.Fatal(err) - } - - if string(val) != "world" { - t.Fatal("Got incorrect value.") + if !p.ID.Equal(peers[2].ID) { + t.Fatal("Didnt find expected peer.") } for i := 0; i < 4; i++ { diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 2c6a9b74c..711866f8e 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -3,8 +3,6 @@ package dht import ( "bytes" "encoding/json" - "errors" - "fmt" "math/rand" "time" @@ -35,19 +33,19 @@ func GenerateMessageID() uint64 { // This is the top level "Store" operation of the DHT func (s *IpfsDHT) PutValue(key u.Key, value []byte) { complete := make(chan struct{}) - for i, route := range s.routes { + for _, route := range s.routes { p := route.NearestPeer(kb.ConvertKey(key)) if p == nil { - s.network.Chan.Errors <- fmt.Errorf("No peer found on level %d", i) - continue + s.network.Error(kb.ErrLookupFailure) go func() { complete <- struct{}{} }() + continue } go func() { err := s.putValueToNetwork(p, string(key), value) if err != nil { - s.network.Chan.Errors <- err + s.network.Error(err) } complete <- struct{}{} }() @@ -61,19 +59,46 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) { // If the search does not succeed, a multiaddr string of a closer peer is // returned along with util.ErrSearchIncomplete func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { - for _, route := range s.routes { - var p *peer.Peer - p = route.NearestPeer(kb.ConvertKey(key)) - if p == nil { - return nil, errors.New("Table returned nil peer!") - } + route_level := 0 - b, err := s.getValueSingle(p, key, timeout) - if err == nil { - return b, nil + p := s.routes[route_level].NearestPeer(kb.ConvertKey(key)) + if p == nil { + return nil, kb.ErrLookupFailure + } + + for route_level < len(s.routes) && p != nil { + pmes, err := s.getValueSingle(p, key, timeout, route_level) + if err != nil { + return nil, u.WrapError(err, "getValue Error") } - if err != u.ErrSearchIncomplete { - return nil, err + + if pmes.GetSuccess() { + if pmes.Value == nil { // We were given provider[s] + return s.getFromPeerList(key, timeout, pmes.GetPeers(), route_level) + } + + // Success! We were given the value + return pmes.GetValue(), nil + } else { + // We were given a closer node + closers := pmes.GetPeers() + if len(closers) > 0 { + maddr, err := ma.NewMultiaddr(closers[0].GetAddr()) + if err != nil { + // ??? Move up route level??? + panic("not yet implemented") + } + + // TODO: dht.Connect has overhead due to an internal + // ping to the target. Use something else + p, err = s.Connect(maddr) + if err != nil { + // Move up route level + panic("not yet implemented.") + } + } else { + route_level++ + } } } return nil, u.ErrNotFound @@ -86,7 +111,7 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { func (s *IpfsDHT) Provide(key u.Key) error { peers := s.routes[0].NearestPeers(kb.ConvertKey(key), PoolSize) if len(peers) == 0 { - //return an error + return kb.ErrLookupFailure } pmes := DHTMessage{ @@ -97,7 +122,7 @@ func (s *IpfsDHT) Provide(key u.Key) error { for _, p := range peers { mes := swarm.NewMessage(p, pbmes) - s.network.Chan.Outgoing <- mes + s.network.Send(mes) } return nil } @@ -105,6 +130,9 @@ func (s *IpfsDHT) Provide(key u.Key) error { // FindProviders searches for peers who can provide the value for given key. func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) { p := s.routes[0].NearestPeer(kb.ConvertKey(key)) + if p == nil { + return nil, kb.ErrLookupFailure + } pmes := DHTMessage{ Type: PBDHTMessage_GET_PROVIDERS, @@ -116,7 +144,7 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, listenChan := s.ListenFor(pmes.Id, 1, time.Minute) u.DOut("Find providers for: '%s'", key) - s.network.Chan.Outgoing <- mes + s.network.Send(mes) after := time.After(timeout) select { case <-after: @@ -129,17 +157,12 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, if err != nil { return nil, err } - var addrs map[u.Key]string - err = json.Unmarshal(pmes_out.GetValue(), &addrs) - if err != nil { - return nil, err - } var prov_arr []*peer.Peer - for pid, addr := range addrs { - p := s.network.Find(pid) + for _, prov := range pmes_out.GetPeers() { + p := s.network.Find(u.Key(prov.GetId())) if p == nil { - maddr, err := ma.NewMultiaddr(addr) + maddr, err := ma.NewMultiaddr(prov.GetAddr()) if err != nil { u.PErr("error connecting to new peer: %s", err) continue @@ -162,48 +185,36 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, // FindPeer searches for a peer with given ID. func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) { - p := s.routes[0].NearestPeer(kb.ConvertPeerID(id)) - - pmes := DHTMessage{ - Type: PBDHTMessage_FIND_NODE, - Key: string(id), - Id: GenerateMessageID(), + route_level := 0 + p := s.routes[route_level].NearestPeer(kb.ConvertPeerID(id)) + if p == nil { + return nil, kb.ErrLookupFailure } - mes := swarm.NewMessage(p, pmes.ToProtobuf()) - - listenChan := s.ListenFor(pmes.Id, 1, time.Minute) - s.network.Chan.Outgoing <- mes - after := time.After(timeout) - select { - case <-after: - s.Unlisten(pmes.Id) - return nil, u.ErrTimeout - case resp := <-listenChan: - pmes_out := new(PBDHTMessage) - err := proto.Unmarshal(resp.Data, pmes_out) - if err != nil { - return nil, err + for route_level < len(s.routes) { + pmes, err := s.findPeerSingle(p, id, timeout, route_level) + plist := pmes.GetPeers() + if len(plist) == 0 { + route_level++ } - addr := string(pmes_out.GetValue()) - maddr, err := ma.NewMultiaddr(addr) + found := plist[0] + + addr, err := ma.NewMultiaddr(found.GetAddr()) if err != nil { - return nil, err + return nil, u.WrapError(err, "FindPeer received bad info") } - found_peer, err := s.Connect(maddr) + nxtPeer, err := s.Connect(addr) if err != nil { - u.POut("Found peer but couldnt connect.") - return nil, err + return nil, u.WrapError(err, "FindPeer failed to connect to new peer.") } - - if !found_peer.ID.Equal(id) { - u.POut("FindPeer: searching for '%s' but found '%s'", id.Pretty(), found_peer.ID.Pretty()) - return found_peer, u.ErrSearchIncomplete + if pmes.GetSuccess() { + return nxtPeer, nil + } else { + p = nxtPeer } - - return found_peer, nil } + return nil, u.ErrNotFound } // Ping a peer, log the time it took @@ -216,14 +227,14 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { before := time.Now() response_chan := dht.ListenFor(pmes.Id, 1, time.Minute) - dht.network.Chan.Outgoing <- mes + dht.network.Send(mes) tout := time.After(timeout) select { case <-response_chan: roundtrip := time.Since(before) p.SetLatency(roundtrip) - u.POut("Ping took %s.", roundtrip.String()) + u.DOut("Ping took %s.", roundtrip.String()) return nil case <-tout: // Timed out, think about removing peer from network @@ -249,7 +260,7 @@ func (dht *IpfsDHT) GetDiagnostic(timeout time.Duration) ([]*diagInfo, error) { pbmes := pmes.ToProtobuf() for _, p := range targets { mes := swarm.NewMessage(p, pbmes) - dht.network.Chan.Outgoing <- mes + dht.network.Send(mes) } var out []*diagInfo diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 095d0b03e..32ff2c269 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -3,11 +3,16 @@ package dht import ( "bytes" "crypto/sha256" + "errors" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" ) +// Returned if a routing table query returns no results. This is NOT expected +// behaviour +var ErrLookupFailure = errors.New("failed to find any peer in table") + // ID for IpfsDHT should be a byte slice, to allow for simpler operations // (xor). DHT ids are based on the peer.IDs. // @@ -19,8 +24,8 @@ func (id ID) Equal(other ID) bool { return bytes.Equal(id, other) } -func (id ID) Less(other interface{}) bool { - a, b := equalizeSizes(id, other.(ID)) +func (id ID) Less(other ID) bool { + a, b := equalizeSizes(id, other) for i := 0; i < len(a); i++ { if a[i] != b[i] { return a[i] < b[i] @@ -80,3 +85,14 @@ func ConvertKey(id u.Key) ID { hash := sha256.Sum256([]byte(id)) return hash[:] } + +// Returns true if a is closer to key than b is +func Closer(a, b peer.ID, key u.Key) bool { + aid := ConvertPeerID(a) + bid := ConvertPeerID(b) + tgt := ConvertKey(key) + adist := xor(aid, tgt) + bdist := xor(bid, tgt) + + return adist.Less(bdist) +} From d72ecc1d0ecec399f66c98dab28a1d6ef2cb227f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 10 Aug 2014 21:40:17 -0700 Subject: [PATCH 0048/3817] starting a new testing framework This commit was moved from ipfs/go-ipfs-routing@4868ae293bf406e80265d5c072a56a60d079c5e2 --- routing/dht/dht.go | 15 ++------ routing/dht/dht_test.go | 74 ++++++++++----------------------------- routing/dht/ext_test.go | 77 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 68 deletions(-) create mode 100644 routing/dht/ext_test.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 388ab9ab1..e62b1fda8 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -66,18 +66,9 @@ type listenInfo struct { } // NewDHT creates a new DHT object with the given peer as the 'local' host -func NewDHT(p *peer.Peer) (*IpfsDHT, error) { - if p == nil { - return nil, errors.New("nil peer passed to NewDHT()") - } - network := swarm.NewSwarm(p) - err := network.Listen() - if err != nil { - return nil, err - } - +func NewDHT(p *peer.Peer, net swarm.Network) *IpfsDHT { dht := new(IpfsDHT) - dht.network = network + dht.network = net dht.datastore = ds.NewMapDatastore() dht.self = p dht.listeners = make(map[uint64]*listenInfo) @@ -86,7 +77,7 @@ func NewDHT(p *peer.Peer) (*IpfsDHT, error) { dht.routes = make([]*kb.RoutingTable, 1) dht.routes[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID)) dht.birth = time.Now() - return dht, nil + return dht } // Start up background goroutines needed by the DHT diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 3d1679397..9c198eb6b 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -4,6 +4,7 @@ import ( "testing" peer "github.com/jbenet/go-ipfs/peer" + swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" ma "github.com/jbenet/go-multiaddr" @@ -31,10 +32,12 @@ func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) var dhts []*IpfsDHT for i := 0; i < 4; i++ { - d, err := NewDHT(peers[i]) + net := swarm.NewSwarm(peers[i]) + err := net.Listen() if err != nil { t.Fatal(err) } + d := NewDHT(peers[i], net) dhts = append(dhts, d) d.Start() } @@ -61,15 +64,19 @@ func TestPing(t *testing.T) { peer_b.AddAddress(addr_b) peer_b.ID = peer.ID([]byte("peer_b")) - dht_a, err := NewDHT(peer_a) + neta := swarm.NewSwarm(peer_a) + err = neta.Listen() if err != nil { t.Fatal(err) } + dht_a := NewDHT(peer_a, neta) - dht_b, err := NewDHT(peer_b) + netb := swarm.NewSwarm(peer_b) + err = netb.Listen() if err != nil { t.Fatal(err) } + dht_b := NewDHT(peer_b, netb) dht_a.Start() dht_b.Start() @@ -108,15 +115,19 @@ func TestValueGetSet(t *testing.T) { peer_b.AddAddress(addr_b) peer_b.ID = peer.ID([]byte("peer_b")) - dht_a, err := NewDHT(peer_a) + neta := swarm.NewSwarm(peer_a) + err = neta.Listen() if err != nil { t.Fatal(err) } + dht_a := NewDHT(peer_a, neta) - dht_b, err := NewDHT(peer_b) + netb := swarm.NewSwarm(peer_b) + err = netb.Listen() if err != nil { t.Fatal(err) } + dht_b := NewDHT(peer_b, netb) dht_a.Start() dht_b.Start() @@ -197,32 +208,7 @@ func TestProvides(t *testing.T) { func TestLayeredGet(t *testing.T) { u.Debug = false - var addrs []*ma.Multiaddr - for i := 0; i < 4; i++ { - a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) - if err != nil { - t.Fatal(err) - } - addrs = append(addrs, a) - } - - var peers []*peer.Peer - for i := 0; i < 4; i++ { - p := new(peer.Peer) - p.AddAddress(addrs[i]) - p.ID = peer.ID([]byte(fmt.Sprintf("peer_%d", i))) - peers = append(peers, p) - } - - var dhts []*IpfsDHT - for i := 0; i < 4; i++ { - d, err := NewDHT(peers[i]) - if err != nil { - t.Fatal(err) - } - dhts = append(dhts, d) - d.Start() - } + addrs,_,dhts := setupDHTS(4, t) _, err := dhts[0].Connect(addrs[1]) if err != nil { @@ -267,32 +253,8 @@ func TestLayeredGet(t *testing.T) { func TestFindPeer(t *testing.T) { u.Debug = false - var addrs []*ma.Multiaddr - for i := 0; i < 4; i++ { - a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) - if err != nil { - t.Fatal(err) - } - addrs = append(addrs, a) - } - - var peers []*peer.Peer - for i := 0; i < 4; i++ { - p := new(peer.Peer) - p.AddAddress(addrs[i]) - p.ID = peer.ID([]byte(fmt.Sprintf("peer_%d", i))) - peers = append(peers, p) - } - var dhts []*IpfsDHT - for i := 0; i < 4; i++ { - d, err := NewDHT(peers[i]) - if err != nil { - t.Fatal(err) - } - dhts = append(dhts, d) - d.Start() - } + addrs,peers,dhts := setupDHTS(4, t) _, err := dhts[0].Connect(addrs[1]) if err != nil { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go new file mode 100644 index 000000000..dd781d867 --- /dev/null +++ b/routing/dht/ext_test.go @@ -0,0 +1,77 @@ +package dht + +import ( + "testing" + + peer "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" + swarm "github.com/jbenet/go-ipfs/swarm" + //ma "github.com/jbenet/go-multiaddr" + + "fmt" + "time" +) + +// fauxNet is a standin for a swarm.Network in order to more easily recreate +// different testing scenarios +type fauxNet struct { + Chan *swarm.Chan + + swarm.Network + + handlers []mesHandleFunc +} + +type mesHandleFunc func(*swarm.Message) *swarm.Message + +func newFauxNet() *fauxNet { + fn := new(fauxNet) + fn.Chan = swarm.NewChan(8) + + return fn +} + +func (f *fauxNet) Listen() error { + go func() { + for { + select { + case in := <-f.Chan.Outgoing: + for _,h := range f.handlers { + reply := h(in) + if reply != nil { + f.Chan.Incoming <- reply + break + } + } + } + } + }() + return nil +} + +func (f *fauxNet) AddHandler(fn func(*swarm.Message) *swarm.Message) { + f.handlers = append(f.handlers, fn) +} + +func (f *fauxNet) Send(mes *swarm.Message) { + +} + +func TestGetFailure(t *testing.T) { + fn := newFauxNet() + fn.Listen() + + local := new(peer.Peer) + local.ID = peer.ID([]byte("test_peer")) + + d := NewDHT(local, fn) + + d.Start() + + b, err := d.GetValue(u.Key("test"), time.Second) + if err != nil { + t.Fatal(err) + } + + fmt.Println(b) +} From 6243d14f2409b5326fa4b6e99e84cf25477d15b1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 11 Aug 2014 09:06:20 -0700 Subject: [PATCH 0049/3817] add fauxNet to stand in for Swarm in tests to reproduce various network conditions This commit was moved from ipfs/go-ipfs-routing@f44fa212d3174540801e8cf3b4415ab91540c25f --- routing/dht/dht_test.go | 4 +-- routing/dht/ext_test.go | 64 +++++++++++++++++++++++++++++++++++------ 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 9c198eb6b..2b0fb4a6b 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -208,7 +208,7 @@ func TestProvides(t *testing.T) { func TestLayeredGet(t *testing.T) { u.Debug = false - addrs,_,dhts := setupDHTS(4, t) + addrs, _, dhts := setupDHTS(4, t) _, err := dhts[0].Connect(addrs[1]) if err != nil { @@ -254,7 +254,7 @@ func TestLayeredGet(t *testing.T) { func TestFindPeer(t *testing.T) { u.Debug = false - addrs,peers,dhts := setupDHTS(4, t) + addrs, peers, dhts := setupDHTS(4, t) _, err := dhts[0].Connect(addrs[1]) if err != nil { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index dd781d867..4dff36886 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -3,12 +3,13 @@ package dht import ( "testing" + "code.google.com/p/goprotobuf/proto" + peer "github.com/jbenet/go-ipfs/peer" - u "github.com/jbenet/go-ipfs/util" swarm "github.com/jbenet/go-ipfs/swarm" - //ma "github.com/jbenet/go-multiaddr" + u "github.com/jbenet/go-ipfs/util" + ma "github.com/jbenet/go-multiaddr" - "fmt" "time" ) @@ -36,7 +37,7 @@ func (f *fauxNet) Listen() error { for { select { case in := <-f.Chan.Outgoing: - for _,h := range f.handlers { + for _, h := range f.handlers { reply := h(in) if reply != nil { f.Chan.Incoming <- reply @@ -54,24 +55,69 @@ func (f *fauxNet) AddHandler(fn func(*swarm.Message) *swarm.Message) { } func (f *fauxNet) Send(mes *swarm.Message) { + f.Chan.Outgoing <- mes +} +func (f *fauxNet) GetChan() *swarm.Chan { + return f.Chan } -func TestGetFailure(t *testing.T) { +func (f *fauxNet) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { + return nil, nil +} + +func TestGetFailures(t *testing.T) { fn := newFauxNet() fn.Listen() local := new(peer.Peer) - local.ID = peer.ID([]byte("test_peer")) + local.ID = peer.ID("test_peer") d := NewDHT(local, fn) + other := &peer.Peer{ID: peer.ID("other_peer")} + d.Start() - b, err := d.GetValue(u.Key("test"), time.Second) + d.Update(other) + + // This one should time out + _, err := d.GetValue(u.Key("test"), time.Millisecond*5) if err != nil { - t.Fatal(err) + nerr, ok := err.(*u.IpfsError) + if !ok { + t.Fatal("Got different error than we expected.") + } + if nerr.Inner != u.ErrTimeout { + t.Fatal("Got different error than we expected.") + } + } else { + t.Fatal("Did not get expected error!") } - fmt.Println(b) + fn.AddHandler(func(mes *swarm.Message) *swarm.Message { + pmes := new(PBDHTMessage) + err := proto.Unmarshal(mes.Data, pmes) + if err != nil { + t.Fatal(err) + } + + resp := DHTMessage{ + Type: pmes.GetType(), + Id: pmes.GetId(), + Response: true, + Success: false, + } + return swarm.NewMessage(mes.Peer, resp.ToProtobuf()) + }) + + // This one should fail with NotFound + _, err = d.GetValue(u.Key("test"), time.Millisecond*5) + if err != nil { + if err != u.ErrNotFound { + t.Fatal("Expected ErrNotFound, got: %s", err) + } + } else { + t.Fatal("expected error, got none.") + } } From 9069eebb7cd579663209189b0d1d140b96322f22 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 11 Aug 2014 20:11:23 -0700 Subject: [PATCH 0050/3817] more tests and add in table filtering by peer latency This commit was moved from ipfs/go-ipfs-routing@6a54273799a62b83fa8c89d74f5f60d5a4490dc2 --- routing/dht/dht.go | 49 +++++++++++++++++--- routing/dht/ext_test.go | 39 ++++++++++++++-- routing/dht/routing.go | 8 ++-- routing/kbucket/bucket.go | 54 ++++++++++++++-------- routing/kbucket/table.go | 30 +++++++++--- routing/kbucket/table_test.go | 87 ++++++++++++++++++++++++++++++++--- 6 files changed, 221 insertions(+), 46 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index e62b1fda8..018c22a01 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -74,8 +74,12 @@ func NewDHT(p *peer.Peer, net swarm.Network) *IpfsDHT { dht.listeners = make(map[uint64]*listenInfo) dht.providers = make(map[u.Key][]*providerInfo) dht.shutdown = make(chan struct{}) - dht.routes = make([]*kb.RoutingTable, 1) - dht.routes[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID)) + + dht.routes = make([]*kb.RoutingTable, 3) + dht.routes[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*30) + dht.routes[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*100) + dht.routes[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Hour) + dht.birth = time.Now() return dht } @@ -253,7 +257,13 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { // No providers? // Find closest peer on given cluster to desired key and reply with that info - level := pmes.GetValue()[0] // Using value field to specify cluster level + level := 0 + if len(pmes.GetValue()) < 1 { + // TODO: maybe return an error? Defaulting isnt a good idea IMO + u.PErr("handleGetValue: no routing level specified, assuming 0") + } else { + level = int(pmes.GetValue()[0]) // Using value field to specify cluster level + } closer := dht.routes[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) @@ -477,6 +487,7 @@ func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duratio response_chan := dht.ListenFor(pmes.Id, 1, time.Minute) mes := swarm.NewMessage(p, pmes.ToProtobuf()) + t := time.Now() dht.network.Send(mes) // Wait for either the response or a timeout @@ -490,6 +501,8 @@ func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duratio u.PErr("response channel closed before timeout, please investigate.") return nil, u.ErrTimeout } + roundtrip := time.Since(t) + resp.Peer.SetLatency(roundtrip) pmes_out := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmes_out) if err != nil { @@ -513,7 +526,8 @@ func (dht *IpfsDHT) getFromPeerList(key u.Key, timeout time.Duration, u.PErr("getValue error: %s", err) continue } - p, err = dht.Connect(maddr) + + p, err = dht.network.Connect(maddr) if err != nil { u.PErr("getValue error: %s", err) continue @@ -547,9 +561,21 @@ func (dht *IpfsDHT) PutLocal(key u.Key, value []byte) error { } func (dht *IpfsDHT) Update(p *peer.Peer) { - removed := dht.routes[0].Update(p) - if removed != nil { - dht.network.Drop(removed) + for _, route := range dht.routes { + removed := route.Update(p) + // Only drop the connection if no tables refer to this peer + if removed != nil { + found := false + for _, r := range dht.routes { + if r.Find(removed.ID) != nil { + found = true + break + } + } + if !found { + dht.network.Drop(removed) + } + } } } @@ -574,6 +600,7 @@ func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Durati mes := swarm.NewMessage(p, pmes.ToProtobuf()) listenChan := dht.ListenFor(pmes.Id, 1, time.Minute) + t := time.Now() dht.network.Send(mes) after := time.After(timeout) select { @@ -581,6 +608,8 @@ func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Durati dht.Unlisten(pmes.Id) return nil, u.ErrTimeout case resp := <-listenChan: + roundtrip := time.Since(t) + resp.Peer.SetLatency(roundtrip) pmes_out := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmes_out) if err != nil { @@ -590,3 +619,9 @@ func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Durati return pmes_out, nil } } + +func (dht *IpfsDHT) PrintTables() { + for _, route := range dht.routes { + route.Print() + } +} diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 4dff36886..fbf52a263 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -16,13 +16,16 @@ import ( // fauxNet is a standin for a swarm.Network in order to more easily recreate // different testing scenarios type fauxNet struct { - Chan *swarm.Chan + Chan *swarm.Chan + handlers []mesHandleFunc swarm.Network - - handlers []mesHandleFunc } +// mesHandleFunc is a function that takes in outgoing messages +// and can respond to them, simulating other peers on the network. +// returning nil will chose not to respond and pass the message onto the +// next registered handler type mesHandleFunc func(*swarm.Message) *swarm.Message func newFauxNet() *fauxNet { @@ -32,6 +35,9 @@ func newFauxNet() *fauxNet { return fn } +// Instead of 'Listening' Start up a goroutine that will check +// all outgoing messages against registered message handlers, +// and reply if needed func (f *fauxNet) Listen() error { go func() { for { @@ -95,6 +101,7 @@ func TestGetFailures(t *testing.T) { t.Fatal("Did not get expected error!") } + // Reply with failures to every message fn.AddHandler(func(mes *swarm.Message) *swarm.Message { pmes := new(PBDHTMessage) err := proto.Unmarshal(mes.Data, pmes) @@ -120,4 +127,30 @@ func TestGetFailures(t *testing.T) { } else { t.Fatal("expected error, got none.") } + + success := make(chan struct{}) + fn.handlers = nil + fn.AddHandler(func(mes *swarm.Message) *swarm.Message { + resp := new(PBDHTMessage) + err := proto.Unmarshal(mes.Data, resp) + if err != nil { + t.Fatal(err) + } + if resp.GetSuccess() { + t.Fatal("Get returned success when it shouldnt have.") + } + success <- struct{}{} + return nil + }) + + // Now we test this DHT's handleGetValue failure + req := DHTMessage{ + Type: PBDHTMessage_GET_VALUE, + Key: "hello", + Id: GenerateMessageID(), + Value: []byte{0}, + } + fn.Chan.Incoming <- swarm.NewMessage(other, req.ToProtobuf()) + + <-success } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 711866f8e..71e950102 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -89,9 +89,7 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { panic("not yet implemented") } - // TODO: dht.Connect has overhead due to an internal - // ping to the target. Use something else - p, err = s.Connect(maddr) + p, err = s.network.Connect(maddr) if err != nil { // Move up route level panic("not yet implemented.") @@ -167,7 +165,7 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, u.PErr("error connecting to new peer: %s", err) continue } - p, err = s.Connect(maddr) + p, err = s.network.Connect(maddr) if err != nil { u.PErr("error connecting to new peer: %s", err) continue @@ -204,7 +202,7 @@ func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error return nil, u.WrapError(err, "FindPeer received bad info") } - nxtPeer, err := s.Connect(addr) + nxtPeer, err := s.network.Connect(addr) if err != nil { return nil, u.WrapError(err, "FindPeer failed to connect to new peer.") } diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 5abd2c910..1a55a0f69 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -2,16 +2,27 @@ package dht import ( "container/list" + "sync" peer "github.com/jbenet/go-ipfs/peer" ) // Bucket holds a list of peers. -type Bucket list.List +type Bucket struct { + lk sync.RWMutex + list *list.List +} + +func NewBucket() *Bucket { + b := new(Bucket) + b.list = list.New() + return b +} func (b *Bucket) Find(id peer.ID) *list.Element { - bucket_list := (*list.List)(b) - for e := bucket_list.Front(); e != nil; e = e.Next() { + b.lk.RLock() + defer b.lk.RUnlock() + for e := b.list.Front(); e != nil; e = e.Next() { if e.Value.(*peer.Peer).ID.Equal(id) { return e } @@ -20,34 +31,42 @@ func (b *Bucket) Find(id peer.ID) *list.Element { } func (b *Bucket) MoveToFront(e *list.Element) { - bucket_list := (*list.List)(b) - bucket_list.MoveToFront(e) + b.lk.Lock() + b.list.MoveToFront(e) + b.lk.Unlock() } func (b *Bucket) PushFront(p *peer.Peer) { - bucket_list := (*list.List)(b) - bucket_list.PushFront(p) + b.lk.Lock() + b.list.PushFront(p) + b.lk.Unlock() } func (b *Bucket) PopBack() *peer.Peer { - bucket_list := (*list.List)(b) - last := bucket_list.Back() - bucket_list.Remove(last) + b.lk.Lock() + defer b.lk.Unlock() + last := b.list.Back() + b.list.Remove(last) return last.Value.(*peer.Peer) } func (b *Bucket) Len() int { - bucket_list := (*list.List)(b) - return bucket_list.Len() + b.lk.RLock() + defer b.lk.RUnlock() + return b.list.Len() } // Splits a buckets peers into two buckets, the methods receiver will have // peers with CPL equal to cpl, the returned bucket will have peers with CPL // greater than cpl (returned bucket has closer peers) func (b *Bucket) Split(cpl int, target ID) *Bucket { - bucket_list := (*list.List)(b) + b.lk.Lock() + defer b.lk.Unlock() + out := list.New() - e := bucket_list.Front() + newbuck := NewBucket() + newbuck.list = out + e := b.list.Front() for e != nil { peer_id := ConvertPeerID(e.Value.(*peer.Peer).ID) peer_cpl := prefLen(peer_id, target) @@ -55,15 +74,14 @@ func (b *Bucket) Split(cpl int, target ID) *Bucket { cur := e out.PushBack(e.Value) e = e.Next() - bucket_list.Remove(cur) + b.list.Remove(cur) continue } e = e.Next() } - return (*Bucket)(out) + return newbuck } func (b *Bucket) getIter() *list.Element { - bucket_list := (*list.List)(b) - return bucket_list.Front() + return b.list.Front() } diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 788d12265..86a7031ce 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -2,8 +2,10 @@ package dht import ( "container/list" + "fmt" "sort" "sync" + "time" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" @@ -18,16 +20,20 @@ type RoutingTable struct { // Blanket lock, refine later for better performance tabLock sync.RWMutex + // Maximum acceptable latency for peers in this cluster + maxLatency time.Duration + // kBuckets define all the fingers to other nodes. Buckets []*Bucket bucketsize int } -func NewRoutingTable(bucketsize int, local_id ID) *RoutingTable { +func NewRoutingTable(bucketsize int, local_id ID, latency time.Duration) *RoutingTable { rt := new(RoutingTable) - rt.Buckets = []*Bucket{new(Bucket)} + rt.Buckets = []*Bucket{NewBucket()} rt.bucketsize = bucketsize rt.local = local_id + rt.maxLatency = latency return rt } @@ -48,6 +54,10 @@ func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer { e := bucket.Find(p.ID) if e == nil { // New peer, add to bucket + if p.GetLatency() > rt.maxLatency { + // Connection doesnt meet requirements, skip! + return nil + } bucket.PushFront(p) // Are we past the max bucket size? @@ -150,17 +160,16 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { // In the case of an unusual split, one bucket may be empty. // if this happens, search both surrounding buckets for nearest peer if cpl > 0 { - plist := (*list.List)(rt.Buckets[cpl-1]) + plist := rt.Buckets[cpl-1].list peerArr = copyPeersFromList(id, peerArr, plist) } if cpl < len(rt.Buckets)-1 { - plist := (*list.List)(rt.Buckets[cpl+1]) + plist := rt.Buckets[cpl+1].list peerArr = copyPeersFromList(id, peerArr, plist) } } else { - plist := (*list.List)(bucket) - peerArr = copyPeersFromList(id, peerArr, plist) + peerArr = copyPeersFromList(id, peerArr, bucket.list) } // Sort by distance to local peer @@ -193,3 +202,12 @@ func (rt *RoutingTable) Listpeers() []*peer.Peer { } return peers } + +func (rt *RoutingTable) Print() { + fmt.Printf("Routing Table, bs = %d, Max latency = %d\n", rt.bucketsize, rt.maxLatency) + rt.tabLock.RLock() + peers := rt.Listpeers() + for i, p := range peers { + fmt.Printf("%d) %s %s\n", i, p.ID.Pretty(), p.GetLatency().String()) + } +} diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 842c92510..02d8f5e0e 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -1,11 +1,11 @@ package dht import ( - "container/list" crand "crypto/rand" "crypto/sha256" "math/rand" "testing" + "time" peer "github.com/jbenet/go-ipfs/peer" ) @@ -27,7 +27,7 @@ func _randID() ID { // Test basic features of the bucket struct func TestBucket(t *testing.T) { - b := new(Bucket) + b := NewBucket() peers := make([]*peer.Peer, 100) for i := 0; i < 100; i++ { @@ -45,7 +45,7 @@ func TestBucket(t *testing.T) { } spl := b.Split(0, ConvertPeerID(local.ID)) - llist := (*list.List)(b) + llist := b.list for e := llist.Front(); e != nil; e = e.Next() { p := ConvertPeerID(e.Value.(*peer.Peer).ID) cpl := xor(p, local_id).commonPrefixLen() @@ -54,7 +54,7 @@ func TestBucket(t *testing.T) { } } - rlist := (*list.List)(spl) + rlist := spl.list for e := rlist.Front(); e != nil; e = e.Next() { p := ConvertPeerID(e.Value.(*peer.Peer).ID) cpl := xor(p, local_id).commonPrefixLen() @@ -67,7 +67,7 @@ func TestBucket(t *testing.T) { // Right now, this just makes sure that it doesnt hang or crash func TestTableUpdate(t *testing.T) { local := _randPeer() - rt := NewRoutingTable(10, ConvertPeerID(local.ID)) + rt := NewRoutingTable(10, ConvertPeerID(local.ID), time.Hour) peers := make([]*peer.Peer, 100) for i := 0; i < 100; i++ { @@ -93,7 +93,7 @@ func TestTableUpdate(t *testing.T) { func TestTableFind(t *testing.T) { local := _randPeer() - rt := NewRoutingTable(10, ConvertPeerID(local.ID)) + rt := NewRoutingTable(10, ConvertPeerID(local.ID), time.Hour) peers := make([]*peer.Peer, 100) for i := 0; i < 5; i++ { @@ -110,7 +110,7 @@ func TestTableFind(t *testing.T) { func TestTableFindMultiple(t *testing.T) { local := _randPeer() - rt := NewRoutingTable(20, ConvertPeerID(local.ID)) + rt := NewRoutingTable(20, ConvertPeerID(local.ID), time.Hour) peers := make([]*peer.Peer, 100) for i := 0; i < 18; i++ { @@ -124,3 +124,76 @@ func TestTableFindMultiple(t *testing.T) { t.Fatalf("Got back different number of peers than we expected.") } } + +// Looks for race conditions in table operations. For a more 'certain' +// test, increase the loop counter from 1000 to a much higher number +// and set GOMAXPROCS above 1 +func TestTableMultithreaded(t *testing.T) { + local := peer.ID("localPeer") + tab := NewRoutingTable(20, ConvertPeerID(local), time.Hour) + var peers []*peer.Peer + for i := 0; i < 500; i++ { + peers = append(peers, _randPeer()) + } + + done := make(chan struct{}) + go func() { + for i := 0; i < 1000; i++ { + n := rand.Intn(len(peers)) + tab.Update(peers[n]) + } + done <- struct{}{} + }() + + go func() { + for i := 0; i < 1000; i++ { + n := rand.Intn(len(peers)) + tab.Update(peers[n]) + } + done <- struct{}{} + }() + + go func() { + for i := 0; i < 1000; i++ { + n := rand.Intn(len(peers)) + tab.Find(peers[n].ID) + } + done <- struct{}{} + }() + <-done + <-done + <-done +} + +func BenchmarkUpdates(b *testing.B) { + b.StopTimer() + local := ConvertKey("localKey") + tab := NewRoutingTable(20, local, time.Hour) + + var peers []*peer.Peer + for i := 0; i < b.N; i++ { + peers = append(peers, _randPeer()) + } + + b.StartTimer() + for i := 0; i < b.N; i++ { + tab.Update(peers[i]) + } +} + +func BenchmarkFinds(b *testing.B) { + b.StopTimer() + local := ConvertKey("localKey") + tab := NewRoutingTable(20, local, time.Hour) + + var peers []*peer.Peer + for i := 0; i < b.N; i++ { + peers = append(peers, _randPeer()) + tab.Update(peers[i]) + } + + b.StartTimer() + for i := 0; i < b.N; i++ { + tab.Find(peers[i].ID) + } +} From c220c54c518f6b971d8b24682734ad382ee48b64 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 12 Aug 2014 15:37:26 -0700 Subject: [PATCH 0051/3817] modify use of swarm to not make duplicate connections This commit was moved from ipfs/go-ipfs-routing@b0f6618d30151a39aa6dcfdec4718b29363010ab --- routing/dht/DHTMessage.go | 2 ++ routing/dht/dht.go | 4 ++-- routing/dht/routing.go | 26 ++++++++++++++++++++------ 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/routing/dht/DHTMessage.go b/routing/dht/DHTMessage.go index 701f36687..e2034d7e0 100644 --- a/routing/dht/DHTMessage.go +++ b/routing/dht/DHTMessage.go @@ -28,6 +28,8 @@ func peerInfo(p *peer.Peer) *PBDHTMessage_PBPeer { return pbp } +// TODO: building the protobuf message this way is a little wasteful +// Unused fields wont be omitted, find a better way to do this func (m *DHTMessage) ToProtobuf() *PBDHTMessage { pmes := new(PBDHTMessage) if m.Value != nil { diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 018c22a01..c9b32f90b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -93,7 +93,7 @@ func (dht *IpfsDHT) Start() { func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { maddrstr, _ := addr.String() u.DOut("Connect to new peer: %s", maddrstr) - npeer, err := dht.network.Connect(addr) + npeer, err := dht.network.ConnectNew(addr) if err != nil { return nil, err } @@ -527,7 +527,7 @@ func (dht *IpfsDHT) getFromPeerList(key u.Key, timeout time.Duration, continue } - p, err = dht.network.Connect(maddr) + p, err = dht.network.GetConnection(peer.ID(pinfo.GetId()), maddr) if err != nil { u.PErr("getValue error: %s", err) continue diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 71e950102..045b17f41 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -3,6 +3,7 @@ package dht import ( "bytes" "encoding/json" + "errors" "math/rand" "time" @@ -89,10 +90,10 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { panic("not yet implemented") } - p, err = s.network.Connect(maddr) + p, err = s.network.GetConnection(peer.ID(closers[0].GetId()), maddr) if err != nil { - // Move up route level - panic("not yet implemented.") + u.PErr("[%s] Failed to connect to: %s", s.self.ID.Pretty(), closers[0].GetAddr()) + route_level++ } } else { route_level++ @@ -160,12 +161,13 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, for _, prov := range pmes_out.GetPeers() { p := s.network.Find(u.Key(prov.GetId())) if p == nil { + u.DOut("given provider %s was not in our network already.", peer.ID(prov.GetId()).Pretty()) maddr, err := ma.NewMultiaddr(prov.GetAddr()) if err != nil { u.PErr("error connecting to new peer: %s", err) continue } - p, err = s.network.Connect(maddr) + p, err = s.network.GetConnection(peer.ID(prov.GetId()), maddr) if err != nil { u.PErr("error connecting to new peer: %s", err) continue @@ -183,11 +185,20 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, // FindPeer searches for a peer with given ID. func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) { + // Check if were already connected to them + p, _ := s.Find(id) + if p != nil { + return p, nil + } + route_level := 0 - p := s.routes[route_level].NearestPeer(kb.ConvertPeerID(id)) + p = s.routes[route_level].NearestPeer(kb.ConvertPeerID(id)) if p == nil { return nil, kb.ErrLookupFailure } + if p.ID.Equal(id) { + return p, nil + } for route_level < len(s.routes) { pmes, err := s.findPeerSingle(p, id, timeout, route_level) @@ -202,11 +213,14 @@ func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error return nil, u.WrapError(err, "FindPeer received bad info") } - nxtPeer, err := s.network.Connect(addr) + nxtPeer, err := s.network.GetConnection(peer.ID(found.GetId()), addr) if err != nil { return nil, u.WrapError(err, "FindPeer failed to connect to new peer.") } if pmes.GetSuccess() { + if !id.Equal(nxtPeer.ID) { + return nil, errors.New("got back invalid peer from 'successful' response") + } return nxtPeer, nil } else { p = nxtPeer From d41cca04b8410b23ffde3ccd57a54bbcee1a9647 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 12 Aug 2014 22:10:44 -0700 Subject: [PATCH 0052/3817] not quite working yet, but closer This commit was moved from ipfs/go-ipfs-routing@591436962e6a6984db2d874a9a5d952cd29fe161 --- routing/dht/dht.go | 5 +++-- routing/dht/routing.go | 20 +++++++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index c9b32f90b..04935ea82 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -158,8 +158,8 @@ func (dht *IpfsDHT) handleMessages() { } // - u.DOut("[peer: %s]", dht.self.ID.Pretty()) - u.DOut("Got message type: '%s' [id = %x, from = %s]", + u.DOut("[peer: %s]\nGot message type: '%s' [id = %x, from = %s]", + dht.self.ID.Pretty(), PBDHTMessage_MessageType_name[int32(pmes.GetType())], pmes.GetId(), mes.Peer.ID.Pretty()) switch pmes.GetType() { @@ -235,6 +235,7 @@ func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) er } func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { + u.DOut("handleGetValue for key: %s", pmes.GetKey()) dskey := ds.NewKey(pmes.GetKey()) resp := &DHTMessage{ Response: true, diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 045b17f41..6dc4aa060 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -62,11 +62,22 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) { func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { route_level := 0 + // If we have it local, dont bother doing an RPC! + // NOTE: this might not be what we want to do... + val,err := s.GetLocal(key) + if err != nil { + return val, nil + } + p := s.routes[route_level].NearestPeer(kb.ConvertKey(key)) if p == nil { return nil, kb.ErrLookupFailure } + if kb.Closer(s.self.ID, p.ID, key) { + return nil, u.ErrNotFound + } + for route_level < len(s.routes) && p != nil { pmes, err := s.getValueSingle(p, key, timeout, route_level) if err != nil { @@ -84,17 +95,21 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { // We were given a closer node closers := pmes.GetPeers() if len(closers) > 0 { + if peer.ID(closers[0].GetId()).Equal(s.self.ID) { + return nil, u.ErrNotFound + } maddr, err := ma.NewMultiaddr(closers[0].GetAddr()) if err != nil { // ??? Move up route level??? panic("not yet implemented") } - p, err = s.network.GetConnection(peer.ID(closers[0].GetId()), maddr) + np, err := s.network.GetConnection(peer.ID(closers[0].GetId()), maddr) if err != nil { u.PErr("[%s] Failed to connect to: %s", s.self.ID.Pretty(), closers[0].GetAddr()) route_level++ } + p = np } else { route_level++ } @@ -159,6 +174,9 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, var prov_arr []*peer.Peer for _, prov := range pmes_out.GetPeers() { + if peer.ID(prov.GetId()).Equal(s.self.ID) { + continue + } p := s.network.Find(u.Key(prov.GetId())) if p == nil { u.DOut("given provider %s was not in our network already.", peer.ID(prov.GetId()).Pretty()) From 7ee2113410514ce0d3cf2fe68263fcfaaa006bfe Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 14 Aug 2014 08:32:17 -0700 Subject: [PATCH 0053/3817] fix a few infinitely looping RPCs This commit was moved from ipfs/go-ipfs-routing@14fb5f5d4326234f116a36c777d64e5c58bd9073 --- routing/dht/dht.go | 82 ++++++++++++++++++++++++++++++++-- routing/dht/dht_logger.go | 38 ++++++++++++++++ routing/dht/dht_test.go | 2 +- routing/dht/routing.go | 94 +++++++++++++++++++-------------------- 4 files changed, 165 insertions(+), 51 deletions(-) create mode 100644 routing/dht/dht_logger.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 04935ea82..ea8e1d861 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -244,12 +244,14 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { } iVal, err := dht.datastore.Get(dskey) if err == nil { + u.DOut("handleGetValue success!") resp.Success = true resp.Value = iVal.([]byte) } else if err == ds.ErrNotFound { // Check if we know any providers for the requested value provs, ok := dht.providers[u.Key(pmes.GetKey())] if ok && len(provs) > 0 { + u.DOut("handleGetValue returning %d provider[s]", len(provs)) for _, prov := range provs { resp.Peers = append(resp.Peers, prov.Value) } @@ -265,13 +267,21 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { } else { level = int(pmes.GetValue()[0]) // Using value field to specify cluster level } + u.DOut("handleGetValue searching level %d clusters", level) closer := dht.routes[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) + if closer.ID.Equal(dht.self.ID) { + u.DOut("Attempted to return self! this shouldnt happen...") + resp.Peers = nil + goto out + } // If this peer is closer than the one from the table, return nil if kb.Closer(dht.self.ID, closer.ID, u.Key(pmes.GetKey())) { resp.Peers = nil + u.DOut("handleGetValue could not find a closer node than myself.") } else { + u.DOut("handleGetValue returning a closer peer: '%s'", closer.ID.Pretty()) resp.Peers = []*peer.Peer{closer} } } @@ -280,6 +290,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { panic(err) } +out: mes := swarm.NewMessage(p, resp.ToProtobuf()) dht.network.Send(mes) } @@ -349,9 +360,17 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *PBDHTMessage) { providers := dht.providers[u.Key(pmes.GetKey())] dht.providerLock.RUnlock() if providers == nil || len(providers) == 0 { - // TODO: work on tiering this - closer := dht.routes[0].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) - resp.Peers = []*peer.Peer{closer} + level := 0 + if len(pmes.GetValue()) > 0 { + level = int(pmes.GetValue()[0]) + } + + closer := dht.routes[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) + if kb.Closer(dht.self.ID, closer.ID, u.Key(pmes.GetKey())) { + resp.Peers = nil + } else { + resp.Peers = []*peer.Peer{closer} + } } else { for _, prov := range providers { resp.Peers = append(resp.Peers, prov.Value) @@ -626,3 +645,60 @@ func (dht *IpfsDHT) PrintTables() { route.Print() } } + +func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, timeout time.Duration) (*PBDHTMessage, error) { + pmes := DHTMessage{ + Type: PBDHTMessage_GET_PROVIDERS, + Key: string(key), + Id: GenerateMessageID(), + Value: []byte{byte(level)}, + } + + mes := swarm.NewMessage(p, pmes.ToProtobuf()) + + listenChan := dht.ListenFor(pmes.Id, 1, time.Minute) + dht.network.Send(mes) + after := time.After(timeout) + select { + case <-after: + dht.Unlisten(pmes.Id) + return nil, u.ErrTimeout + case resp := <-listenChan: + u.DOut("FindProviders: got response.") + pmes_out := new(PBDHTMessage) + err := proto.Unmarshal(resp.Data, pmes_out) + if err != nil { + return nil, err + } + + return pmes_out, nil + } +} + +func (dht *IpfsDHT) addPeerList(key u.Key, peers []*PBDHTMessage_PBPeer) []*peer.Peer { + var prov_arr []*peer.Peer + for _, prov := range peers { + // Dont add outselves to the list + if peer.ID(prov.GetId()).Equal(dht.self.ID) { + continue + } + // Dont add someone who is already on the list + p := dht.network.Find(u.Key(prov.GetId())) + if p == nil { + u.DOut("given provider %s was not in our network already.", peer.ID(prov.GetId()).Pretty()) + maddr, err := ma.NewMultiaddr(prov.GetAddr()) + if err != nil { + u.PErr("error connecting to new peer: %s", err) + continue + } + p, err = dht.network.GetConnection(peer.ID(prov.GetId()), maddr) + if err != nil { + u.PErr("error connecting to new peer: %s", err) + continue + } + } + dht.addProviderEntry(key, p) + prov_arr = append(prov_arr, p) + } + return prov_arr +} diff --git a/routing/dht/dht_logger.go b/routing/dht/dht_logger.go new file mode 100644 index 000000000..c363add7b --- /dev/null +++ b/routing/dht/dht_logger.go @@ -0,0 +1,38 @@ +package dht + +import ( + "encoding/json" + "time" + + u "github.com/jbenet/go-ipfs/util" +) + +type logDhtRpc struct { + Type string + Start time.Time + End time.Time + Duration time.Duration + RpcCount int + Success bool +} + +func startNewRpc(name string) *logDhtRpc { + r := new(logDhtRpc) + r.Type = name + r.Start = time.Now() + return r +} + +func (l *logDhtRpc) EndLog() { + l.End = time.Now() + l.Duration = l.End.Sub(l.Start) +} + +func (l *logDhtRpc) Print() { + b, err := json.Marshal(l) + if err != nil { + u.POut(err.Error()) + } else { + u.POut(string(b)) + } +} diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 2b0fb4a6b..a7e14d703 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -156,7 +156,7 @@ func TestValueGetSet(t *testing.T) { } if string(val) != "world" { - t.Fatalf("Expected 'world' got %s", string(val)) + t.Fatalf("Expected 'world' got '%s'", string(val)) } } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 6dc4aa060..9923961d1 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -60,12 +60,19 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) { // If the search does not succeed, a multiaddr string of a closer peer is // returned along with util.ErrSearchIncomplete func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { + ll := startNewRpc("GET") + defer func() { + ll.EndLog() + ll.Print() + }() route_level := 0 // If we have it local, dont bother doing an RPC! // NOTE: this might not be what we want to do... - val,err := s.GetLocal(key) - if err != nil { + val, err := s.GetLocal(key) + if err == nil { + ll.Success = true + u.DOut("Found local, returning.") return val, nil } @@ -74,11 +81,8 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { return nil, kb.ErrLookupFailure } - if kb.Closer(s.self.ID, p.ID, key) { - return nil, u.ErrNotFound - } - for route_level < len(s.routes) && p != nil { + ll.RpcCount++ pmes, err := s.getValueSingle(p, key, timeout, route_level) if err != nil { return nil, u.WrapError(err, "getValue Error") @@ -86,16 +90,19 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { if pmes.GetSuccess() { if pmes.Value == nil { // We were given provider[s] + ll.RpcCount++ return s.getFromPeerList(key, timeout, pmes.GetPeers(), route_level) } // Success! We were given the value + ll.Success = true return pmes.GetValue(), nil } else { // We were given a closer node closers := pmes.GetPeers() if len(closers) > 0 { if peer.ID(closers[0].GetId()).Equal(s.self.ID) { + u.DOut("Got myself back as a closer peer.") return nil, u.ErrNotFound } maddr, err := ma.NewMultiaddr(closers[0].GetAddr()) @@ -108,6 +115,7 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { if err != nil { u.PErr("[%s] Failed to connect to: %s", s.self.ID.Pretty(), closers[0].GetAddr()) route_level++ + continue } p = np } else { @@ -143,60 +151,52 @@ func (s *IpfsDHT) Provide(key u.Key) error { // FindProviders searches for peers who can provide the value for given key. func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) { + ll := startNewRpc("FindProviders") + defer func() { + ll.EndLog() + ll.Print() + }() + u.DOut("Find providers for: '%s'", key) p := s.routes[0].NearestPeer(kb.ConvertKey(key)) if p == nil { return nil, kb.ErrLookupFailure } - pmes := DHTMessage{ - Type: PBDHTMessage_GET_PROVIDERS, - Key: string(key), - Id: GenerateMessageID(), - } - - mes := swarm.NewMessage(p, pmes.ToProtobuf()) - - listenChan := s.ListenFor(pmes.Id, 1, time.Minute) - u.DOut("Find providers for: '%s'", key) - s.network.Send(mes) - after := time.After(timeout) - select { - case <-after: - s.Unlisten(pmes.Id) - return nil, u.ErrTimeout - case resp := <-listenChan: - u.DOut("FindProviders: got response.") - pmes_out := new(PBDHTMessage) - err := proto.Unmarshal(resp.Data, pmes_out) + for level := 0; level < len(s.routes); { + pmes, err := s.findProvidersSingle(p, key, level, timeout) if err != nil { return nil, err } - - var prov_arr []*peer.Peer - for _, prov := range pmes_out.GetPeers() { - if peer.ID(prov.GetId()).Equal(s.self.ID) { + if pmes.GetSuccess() { + provs := s.addPeerList(key, pmes.GetPeers()) + ll.Success = true + return provs, nil + } else { + closer := pmes.GetPeers() + if len(closer) == 0 { + level++ continue } - p := s.network.Find(u.Key(prov.GetId())) - if p == nil { - u.DOut("given provider %s was not in our network already.", peer.ID(prov.GetId()).Pretty()) - maddr, err := ma.NewMultiaddr(prov.GetAddr()) - if err != nil { - u.PErr("error connecting to new peer: %s", err) - continue - } - p, err = s.network.GetConnection(peer.ID(prov.GetId()), maddr) - if err != nil { - u.PErr("error connecting to new peer: %s", err) - continue - } + if peer.ID(closer[0].GetId()).Equal(s.self.ID) { + u.DOut("Got myself back as a closer peer.") + return nil, u.ErrNotFound + } + maddr, err := ma.NewMultiaddr(closer[0].GetAddr()) + if err != nil { + // ??? Move up route level??? + panic("not yet implemented") } - s.addProviderEntry(key, p) - prov_arr = append(prov_arr, p) - } - return prov_arr, nil + np, err := s.network.GetConnection(peer.ID(closer[0].GetId()), maddr) + if err != nil { + u.PErr("[%s] Failed to connect to: %s", s.self.ID.Pretty(), closer[0].GetAddr()) + level++ + continue + } + p = np + } } + return nil, u.ErrNotFound } // Find specific Peer From df3bcc0ef72bb752bea11058895c51ea6367a199 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 15 Aug 2014 09:39:38 -0700 Subject: [PATCH 0054/3817] get implementation according to kademlia spec. This commit was moved from ipfs/go-ipfs-routing@2b20a4deb656bac0087372d2abb894a3c69b7825 --- routing/dht/dht.go | 39 ++++++++++++ routing/dht/dht_logger.go | 4 +- routing/dht/ext_test.go | 12 ++-- routing/dht/routing.go | 130 ++++++++++++++++++++++++++++---------- 4 files changed, 140 insertions(+), 45 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index ea8e1d861..b00ae0a4d 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -496,6 +496,45 @@ out: dht.network.Send(mes) } +func (dht *IpfsDHT) getValueOrPeers(p *peer.Peer, key u.Key, timeout time.Duration, level int) ([]byte, []*peer.Peer, error) { + pmes, err := dht.getValueSingle(p, key, timeout, level) + if err != nil { + return nil, nil, u.WrapError(err, "getValue Error") + } + + if pmes.GetSuccess() { + if pmes.Value == nil { // We were given provider[s] + val, err := dht.getFromPeerList(key, timeout, pmes.GetPeers(), level) + if err != nil { + return nil, nil, err + } + return val, nil, nil + } + + // Success! We were given the value + return pmes.GetValue(), nil, nil + } else { + // We were given a closer node + var peers []*peer.Peer + for _, pb := range pmes.GetPeers() { + addr, err := ma.NewMultiaddr(pb.GetAddr()) + if err != nil { + u.PErr(err.Error()) + continue + } + + np, err := dht.network.GetConnection(peer.ID(pb.GetId()), addr) + if err != nil { + u.PErr(err.Error()) + continue + } + + peers = append(peers, np) + } + return nil, peers, nil + } +} + // getValueSingle simply performs the get value RPC with the given parameters func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duration, level int) (*PBDHTMessage, error) { pmes := DHTMessage{ diff --git a/routing/dht/dht_logger.go b/routing/dht/dht_logger.go index c363add7b..c892959f0 100644 --- a/routing/dht/dht_logger.go +++ b/routing/dht/dht_logger.go @@ -31,8 +31,8 @@ func (l *logDhtRpc) EndLog() { func (l *logDhtRpc) Print() { b, err := json.Marshal(l) if err != nil { - u.POut(err.Error()) + u.DOut(err.Error()) } else { - u.POut(string(b)) + u.DOut(string(b)) } } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index fbf52a263..490c9f493 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -88,13 +88,9 @@ func TestGetFailures(t *testing.T) { d.Update(other) // This one should time out - _, err := d.GetValue(u.Key("test"), time.Millisecond*5) + _, err := d.GetValue(u.Key("test"), time.Millisecond*10) if err != nil { - nerr, ok := err.(*u.IpfsError) - if !ok { - t.Fatal("Got different error than we expected.") - } - if nerr.Inner != u.ErrTimeout { + if err != u.ErrTimeout { t.Fatal("Got different error than we expected.") } } else { @@ -119,10 +115,10 @@ func TestGetFailures(t *testing.T) { }) // This one should fail with NotFound - _, err = d.GetValue(u.Key("test"), time.Millisecond*5) + _, err = d.GetValue(u.Key("test"), time.Millisecond*1000) if err != nil { if err != u.ErrNotFound { - t.Fatal("Expected ErrNotFound, got: %s", err) + t.Fatalf("Expected ErrNotFound, got: %s", err) } } else { t.Fatal("expected error, got none.") diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 9923961d1..2ecd8ba45 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "math/rand" + "sync" "time" proto "code.google.com/p/goprotobuf/proto" @@ -56,6 +57,30 @@ func (s *IpfsDHT) PutValue(key u.Key, value []byte) { } } +// A counter for incrementing a variable across multiple threads +type counter struct { + n int + mut sync.RWMutex +} + +func (c *counter) Increment() { + c.mut.Lock() + c.n++ + c.mut.Unlock() +} + +func (c *counter) Decrement() { + c.mut.Lock() + c.n-- + c.mut.Unlock() +} + +func (c *counter) Size() int { + c.mut.RLock() + defer c.mut.RUnlock() + return c.n +} + // GetValue searches for the value corresponding to given Key. // If the search does not succeed, a multiaddr string of a closer peer is // returned along with util.ErrSearchIncomplete @@ -65,7 +90,6 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { ll.EndLog() ll.Print() }() - route_level := 0 // If we have it local, dont bother doing an RPC! // NOTE: this might not be what we want to do... @@ -76,54 +100,90 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { return val, nil } - p := s.routes[route_level].NearestPeer(kb.ConvertKey(key)) - if p == nil { + route_level := 0 + closest := s.routes[route_level].NearestPeers(kb.ConvertKey(key), PoolSize) + if closest == nil || len(closest) == 0 { return nil, kb.ErrLookupFailure } - for route_level < len(s.routes) && p != nil { - ll.RpcCount++ - pmes, err := s.getValueSingle(p, key, timeout, route_level) - if err != nil { - return nil, u.WrapError(err, "getValue Error") - } + val_chan := make(chan []byte) + npeer_chan := make(chan *peer.Peer, 30) + proc_peer := make(chan *peer.Peer, 30) + err_chan := make(chan error) + after := time.After(timeout) - if pmes.GetSuccess() { - if pmes.Value == nil { // We were given provider[s] - ll.RpcCount++ - return s.getFromPeerList(key, timeout, pmes.GetPeers(), route_level) - } + for _, p := range closest { + npeer_chan <- p + } - // Success! We were given the value - ll.Success = true - return pmes.GetValue(), nil - } else { - // We were given a closer node - closers := pmes.GetPeers() - if len(closers) > 0 { - if peer.ID(closers[0].GetId()).Equal(s.self.ID) { - u.DOut("Got myself back as a closer peer.") - return nil, u.ErrNotFound + c := counter{} + + // This limit value is referred to as k in the kademlia paper + limit := 20 + count := 0 + go func() { + for { + select { + case p := <-npeer_chan: + count++ + if count >= limit { + break } - maddr, err := ma.NewMultiaddr(closers[0].GetAddr()) - if err != nil { - // ??? Move up route level??? - panic("not yet implemented") + c.Increment() + proc_peer <- p + default: + if c.Size() == 0 { + err_chan <- u.ErrNotFound } + } + } + }() - np, err := s.network.GetConnection(peer.ID(closers[0].GetId()), maddr) + process := func() { + for { + select { + case p, ok := <-proc_peer: + if !ok || p == nil { + c.Decrement() + return + } + val, peers, err := s.getValueOrPeers(p, key, timeout/4, route_level) if err != nil { - u.PErr("[%s] Failed to connect to: %s", s.self.ID.Pretty(), closers[0].GetAddr()) - route_level++ + u.DErr(err.Error()) + c.Decrement() continue } - p = np - } else { - route_level++ + if val != nil { + val_chan <- val + c.Decrement() + return + } + + for _, np := range peers { + // TODO: filter out peers that arent closer + npeer_chan <- np + } + c.Decrement() } } } - return nil, u.ErrNotFound + + concurFactor := 3 + for i := 0; i < concurFactor; i++ { + go process() + } + + select { + case val := <-val_chan: + close(npeer_chan) + return val, nil + case err := <-err_chan: + close(npeer_chan) + return nil, err + case <-after: + close(npeer_chan) + return nil, u.ErrTimeout + } } // Value provider layer of indirection. From e9eb246948e6a0c652e8a0dbdbd8f31f0efac6bb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 15 Aug 2014 22:37:53 -0700 Subject: [PATCH 0055/3817] rewrite message response listening framework This commit was moved from ipfs/go-ipfs-routing@f85c3338a2ba590eefc3852210e53635c5618100 --- routing/dht/dht.go | 117 ++++++----------------------------------- routing/dht/routing.go | 51 ++++++++++++++---- 2 files changed, 57 insertions(+), 111 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index b00ae0a4d..c28ca0a0f 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -39,10 +39,6 @@ type IpfsDHT struct { providers map[u.Key][]*providerInfo providerLock sync.RWMutex - // map of channels waiting for reply messages - listeners map[uint64]*listenInfo - listenLock sync.RWMutex - // Signal to shutdown dht shutdown chan struct{} @@ -51,18 +47,9 @@ type IpfsDHT struct { //lock to make diagnostics work better diaglock sync.Mutex -} - -// The listen info struct holds information about a message that is being waited for -type listenInfo struct { - // Responses matching the listen ID will be sent through resp - resp chan *swarm.Message - - // count is the number of responses to listen for - count int - // eol is the time at which this listener will expire - eol time.Time + // listener is a server to register to listen for responses to messages + listener *MesListener } // NewDHT creates a new DHT object with the given peer as the 'local' host @@ -71,7 +58,6 @@ func NewDHT(p *peer.Peer, net swarm.Network) *IpfsDHT { dht.network = net dht.datastore = ds.NewMapDatastore() dht.self = p - dht.listeners = make(map[uint64]*listenInfo) dht.providers = make(map[u.Key][]*providerInfo) dht.shutdown = make(chan struct{}) @@ -80,6 +66,7 @@ func NewDHT(p *peer.Peer, net swarm.Network) *IpfsDHT { dht.routes[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*100) dht.routes[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Hour) + dht.listener = NewMesListener() dht.birth = time.Now() return dht } @@ -135,25 +122,7 @@ func (dht *IpfsDHT) handleMessages() { // Note: not sure if this is the correct place for this if pmes.GetResponse() { - dht.listenLock.RLock() - list, ok := dht.listeners[pmes.GetId()] - dht.listenLock.RUnlock() - if time.Now().After(list.eol) { - dht.Unlisten(pmes.GetId()) - ok = false - } - if list.count > 1 { - list.count-- - } - if ok { - list.resp <- mes - if list.count == 1 { - dht.Unlisten(pmes.GetId()) - } - } else { - u.DOut("Received response with nobody listening...") - } - + dht.listener.Respond(pmes.GetId(), mes) continue } // @@ -187,7 +156,6 @@ func (dht *IpfsDHT) handleMessages() { case <-checkTimeouts.C: // Time to collect some garbage! dht.cleanExpiredProviders() - dht.cleanExpiredListeners() } } } @@ -206,21 +174,6 @@ func (dht *IpfsDHT) cleanExpiredProviders() { dht.providerLock.Unlock() } -func (dht *IpfsDHT) cleanExpiredListeners() { - dht.listenLock.Lock() - var remove []uint64 - now := time.Now() - for k, v := range dht.listeners { - if now.After(v.eol) { - remove = append(remove, k) - } - } - for _, k := range remove { - delete(dht.listeners, k) - } - dht.listenLock.Unlock() -} - func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) error { pmes := DHTMessage{ Type: PBDHTMessage_PUT_VALUE, @@ -393,41 +346,6 @@ func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *PBDHTMessage) { dht.addProviderEntry(key, p) } -// Register a handler for a specific message ID, used for getting replies -// to certain messages (i.e. response to a GET_VALUE message) -func (dht *IpfsDHT) ListenFor(mesid uint64, count int, timeout time.Duration) <-chan *swarm.Message { - lchan := make(chan *swarm.Message) - dht.listenLock.Lock() - dht.listeners[mesid] = &listenInfo{lchan, count, time.Now().Add(timeout)} - dht.listenLock.Unlock() - return lchan -} - -// Unregister the given message id from the listener map -func (dht *IpfsDHT) Unlisten(mesid uint64) { - dht.listenLock.Lock() - list, ok := dht.listeners[mesid] - if ok { - delete(dht.listeners, mesid) - } - dht.listenLock.Unlock() - close(list.resp) -} - -// Check whether or not the dht is currently listening for mesid -func (dht *IpfsDHT) IsListening(mesid uint64) bool { - dht.listenLock.RLock() - li, ok := dht.listeners[mesid] - dht.listenLock.RUnlock() - if time.Now().After(li.eol) { - dht.listenLock.Lock() - delete(dht.listeners, mesid) - dht.listenLock.Unlock() - return false - } - return ok -} - // Stop all communications from this peer and shut down func (dht *IpfsDHT) Halt() { dht.shutdown <- struct{}{} @@ -444,16 +362,8 @@ func (dht *IpfsDHT) addProviderEntry(key u.Key, p *peer.Peer) { // NOTE: not yet finished, low priority func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { - dht.diaglock.Lock() - if dht.IsListening(pmes.GetId()) { - //TODO: ehhh.......... - dht.diaglock.Unlock() - return - } - dht.diaglock.Unlock() - seq := dht.routes[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) - listenChan := dht.ListenFor(pmes.GetId(), len(seq), time.Second*30) + listenChan := dht.listener.Listen(pmes.GetId(), len(seq), time.Second*30) for _, ps := range seq { mes := swarm.NewMessage(ps, pmes) @@ -499,7 +409,7 @@ out: func (dht *IpfsDHT) getValueOrPeers(p *peer.Peer, key u.Key, timeout time.Duration, level int) ([]byte, []*peer.Peer, error) { pmes, err := dht.getValueSingle(p, key, timeout, level) if err != nil { - return nil, nil, u.WrapError(err, "getValue Error") + return nil, nil, err } if pmes.GetSuccess() { @@ -517,6 +427,9 @@ func (dht *IpfsDHT) getValueOrPeers(p *peer.Peer, key u.Key, timeout time.Durati // We were given a closer node var peers []*peer.Peer for _, pb := range pmes.GetPeers() { + if peer.ID(pb.GetId()).Equal(dht.self.ID) { + continue + } addr, err := ma.NewMultiaddr(pb.GetAddr()) if err != nil { u.PErr(err.Error()) @@ -543,7 +456,7 @@ func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duratio Value: []byte{byte(level)}, Id: GenerateMessageID(), } - response_chan := dht.ListenFor(pmes.Id, 1, time.Minute) + response_chan := dht.listener.Listen(pmes.Id, 1, time.Minute) mes := swarm.NewMessage(p, pmes.ToProtobuf()) t := time.Now() @@ -553,7 +466,7 @@ func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duratio timeup := time.After(timeout) select { case <-timeup: - dht.Unlisten(pmes.Id) + dht.listener.Unlisten(pmes.Id) return nil, u.ErrTimeout case resp, ok := <-response_chan: if !ok { @@ -658,13 +571,13 @@ func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Durati } mes := swarm.NewMessage(p, pmes.ToProtobuf()) - listenChan := dht.ListenFor(pmes.Id, 1, time.Minute) + listenChan := dht.listener.Listen(pmes.Id, 1, time.Minute) t := time.Now() dht.network.Send(mes) after := time.After(timeout) select { case <-after: - dht.Unlisten(pmes.Id) + dht.listener.Unlisten(pmes.Id) return nil, u.ErrTimeout case resp := <-listenChan: roundtrip := time.Since(t) @@ -695,12 +608,12 @@ func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, time mes := swarm.NewMessage(p, pmes.ToProtobuf()) - listenChan := dht.ListenFor(pmes.Id, 1, time.Minute) + listenChan := dht.listener.Listen(pmes.Id, 1, time.Minute) dht.network.Send(mes) after := time.After(timeout) select { case <-after: - dht.Unlisten(pmes.Id) + dht.listener.Unlisten(pmes.Id) return nil, u.ErrTimeout case resp := <-listenChan: u.DOut("FindProviders: got response.") diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 2ecd8ba45..e56a54e0c 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -81,6 +81,36 @@ func (c *counter) Size() int { return c.n } +type peerSet struct { + ps map[string]bool + lk sync.RWMutex +} + +func newPeerSet() *peerSet { + ps := new(peerSet) + ps.ps = make(map[string]bool) + return ps +} + +func (ps *peerSet) Add(p *peer.Peer) { + ps.lk.Lock() + ps.ps[string(p.ID)] = true + ps.lk.Unlock() +} + +func (ps *peerSet) Contains(p *peer.Peer) bool { + ps.lk.RLock() + _, ok := ps.ps[string(p.ID)] + ps.lk.RUnlock() + return ok +} + +func (ps *peerSet) Size() int { + ps.lk.RLock() + defer ps.lk.RUnlock() + return len(ps.ps) +} + // GetValue searches for the value corresponding to given Key. // If the search does not succeed, a multiaddr string of a closer peer is // returned along with util.ErrSearchIncomplete @@ -111,8 +141,10 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { proc_peer := make(chan *peer.Peer, 30) err_chan := make(chan error) after := time.After(timeout) + pset := newPeerSet() for _, p := range closest { + pset.Add(p) npeer_chan <- p } @@ -130,6 +162,7 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { break } c.Increment() + proc_peer <- p default: if c.Size() == 0 { @@ -161,7 +194,10 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { for _, np := range peers { // TODO: filter out peers that arent closer - npeer_chan <- np + if !pset.Contains(np) && pset.Size() < limit { + pset.Add(np) //This is racey... make a single function to do operation + npeer_chan <- np + } } c.Decrement() } @@ -175,13 +211,10 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { select { case val := <-val_chan: - close(npeer_chan) return val, nil case err := <-err_chan: - close(npeer_chan) return nil, err case <-after: - close(npeer_chan) return nil, u.ErrTimeout } } @@ -288,12 +321,12 @@ func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error addr, err := ma.NewMultiaddr(found.GetAddr()) if err != nil { - return nil, u.WrapError(err, "FindPeer received bad info") + return nil, err } nxtPeer, err := s.network.GetConnection(peer.ID(found.GetId()), addr) if err != nil { - return nil, u.WrapError(err, "FindPeer failed to connect to new peer.") + return nil, err } if pmes.GetSuccess() { if !id.Equal(nxtPeer.ID) { @@ -316,7 +349,7 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { mes := swarm.NewMessage(p, pmes.ToProtobuf()) before := time.Now() - response_chan := dht.ListenFor(pmes.Id, 1, time.Minute) + response_chan := dht.listener.Listen(pmes.Id, 1, time.Minute) dht.network.Send(mes) tout := time.After(timeout) @@ -329,7 +362,7 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { case <-tout: // Timed out, think about removing peer from network u.DOut("Ping peer timed out.") - dht.Unlisten(pmes.Id) + dht.listener.Unlisten(pmes.Id) return u.ErrTimeout } } @@ -345,7 +378,7 @@ func (dht *IpfsDHT) GetDiagnostic(timeout time.Duration) ([]*diagInfo, error) { Id: GenerateMessageID(), } - listenChan := dht.ListenFor(pmes.Id, len(targets), time.Minute*2) + listenChan := dht.listener.Listen(pmes.Id, len(targets), time.Minute*2) pbmes := pmes.ToProtobuf() for _, p := range targets { From 102df0192196e6f0ba702e2ba6e1a92462409aa5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 16 Aug 2014 08:20:29 -0700 Subject: [PATCH 0056/3817] add files i forgot to last night This commit was moved from ipfs/go-ipfs-routing@f054a9ed517c6883442463fc401dd702357db382 --- routing/dht/mes_listener.go | 116 ++++++++++++++++++++++++++++++++++++ routing/dht/routing.go | 44 +++++++------- 2 files changed, 140 insertions(+), 20 deletions(-) create mode 100644 routing/dht/mes_listener.go diff --git a/routing/dht/mes_listener.go b/routing/dht/mes_listener.go new file mode 100644 index 000000000..2fcd99fc1 --- /dev/null +++ b/routing/dht/mes_listener.go @@ -0,0 +1,116 @@ +package dht + +import ( + "sync" + "time" + + swarm "github.com/jbenet/go-ipfs/swarm" + u "github.com/jbenet/go-ipfs/util" +) + +type MesListener struct { + listeners map[uint64]*listenInfo + haltchan chan struct{} + unlist chan uint64 + nlist chan *listenInfo + send chan *respMes +} + +// The listen info struct holds information about a message that is being waited for +type listenInfo struct { + // Responses matching the listen ID will be sent through resp + resp chan *swarm.Message + + // count is the number of responses to listen for + count int + + // eol is the time at which this listener will expire + eol time.Time + + // sendlock is used to prevent conditions where we try to send on the resp + // channel as its being closed by a timeout in another thread + sendLock sync.Mutex + + closed bool + + id uint64 +} + +func NewMesListener() *MesListener { + ml := new(MesListener) + ml.haltchan = make(chan struct{}) + ml.listeners = make(map[uint64]*listenInfo) + ml.nlist = make(chan *listenInfo, 16) + ml.send = make(chan *respMes, 16) + ml.unlist = make(chan uint64, 16) + go ml.run() + return ml +} + +func (ml *MesListener) Listen(id uint64, count int, timeout time.Duration) <-chan *swarm.Message { + li := new(listenInfo) + li.count = count + li.eol = time.Now().Add(timeout) + li.resp = make(chan *swarm.Message, count) + li.id = id + ml.nlist <- li + return li.resp +} + +func (ml *MesListener) Unlisten(id uint64) { + ml.unlist <- id +} + +type respMes struct { + id uint64 + mes *swarm.Message +} + +func (ml *MesListener) Respond(id uint64, mes *swarm.Message) { + ml.send <- &respMes{ + id: id, + mes: mes, + } +} + +func (ml *MesListener) Halt() { + ml.haltchan <- struct{}{} +} + +func (ml *MesListener) run() { + for { + select { + case <-ml.haltchan: + return + case id := <-ml.unlist: + trg, ok := ml.listeners[id] + if !ok { + continue + } + close(trg.resp) + delete(ml.listeners, id) + case li := <-ml.nlist: + ml.listeners[li.id] = li + case s := <-ml.send: + trg, ok := ml.listeners[s.id] + if !ok { + u.DOut("Send with no listener.") + continue + } + + if time.Now().After(trg.eol) { + close(trg.resp) + delete(ml.listeners, s.id) + continue + } + + trg.resp <- s.mes + trg.count-- + + if trg.count == 0 { + close(trg.resp) + delete(ml.listeners, s.id) + } + } + } +} diff --git a/routing/dht/routing.go b/routing/dht/routing.go index e56a54e0c..309962a93 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -21,6 +21,12 @@ import ( // Pool size is the number of nodes used for group find/set RPC calls var PoolSize = 6 +// We put the 'K' in kademlia! +var KValue = 10 + +// Its in the paper, i swear +var AlphaValue = 3 + // TODO: determine a way of creating and managing message IDs func GenerateMessageID() uint64 { //return (uint64(rand.Uint32()) << 32) & uint64(rand.Uint32()) @@ -35,24 +41,25 @@ func GenerateMessageID() uint64 { // This is the top level "Store" operation of the DHT func (s *IpfsDHT) PutValue(key u.Key, value []byte) { complete := make(chan struct{}) + count := 0 for _, route := range s.routes { - p := route.NearestPeer(kb.ConvertKey(key)) - if p == nil { - s.network.Error(kb.ErrLookupFailure) - go func() { + peers := route.NearestPeers(kb.ConvertKey(key), KValue) + for _, p := range peers { + if p == nil { + s.network.Error(kb.ErrLookupFailure) + continue + } + count++ + go func(sp *peer.Peer) { + err := s.putValueToNetwork(sp, string(key), value) + if err != nil { + s.network.Error(err) + } complete <- struct{}{} - }() - continue + }(p) } - go func() { - err := s.putValueToNetwork(p, string(key), value) - if err != nil { - s.network.Error(err) - } - complete <- struct{}{} - }() } - for _, _ = range s.routes { + for i := 0; i < count; i++ { <-complete } } @@ -150,15 +157,13 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { c := counter{} - // This limit value is referred to as k in the kademlia paper - limit := 20 count := 0 go func() { for { select { case p := <-npeer_chan: count++ - if count >= limit { + if count >= KValue { break } c.Increment() @@ -194,7 +199,7 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { for _, np := range peers { // TODO: filter out peers that arent closer - if !pset.Contains(np) && pset.Size() < limit { + if !pset.Contains(np) && pset.Size() < KValue { pset.Add(np) //This is racey... make a single function to do operation npeer_chan <- np } @@ -204,8 +209,7 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { } } - concurFactor := 3 - for i := 0; i < concurFactor; i++ { + for i := 0; i < AlphaValue; i++ { go process() } From d52e5632f7f89fcfa3b24ab4c833814675bbf421 Mon Sep 17 00:00:00 2001 From: Chas Leichner Date: Sat, 16 Aug 2014 17:23:10 -0700 Subject: [PATCH 0057/3817] Upgraded merkledag proto This commit was moved from ipfs/go-merkledag@4e418a76b953ea300204742896567b78e792a152 --- ipld/merkledag/Makefile | 3 ++ ipld/merkledag/merkledag.go | 1 + ipld/merkledag/node.pb.go | 75 ++++++++++++++++++------------------- 3 files changed, 41 insertions(+), 38 deletions(-) diff --git a/ipld/merkledag/Makefile b/ipld/merkledag/Makefile index ec1a09650..711f34bda 100644 --- a/ipld/merkledag/Makefile +++ b/ipld/merkledag/Makefile @@ -3,3 +3,6 @@ all: node.pb.go node.pb.go: node.proto protoc --gogo_out=. --proto_path=../../../../:/usr/local/opt/protobuf/include:. $< + +clean: + rm node.pb.go diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 251ea0bee..b906bbf89 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -2,6 +2,7 @@ package merkledag import ( "fmt" + blocks "github.com/jbenet/go-ipfs/blocks" u "github.com/jbenet/go-ipfs/util" mh "github.com/jbenet/go-multihash" diff --git a/ipld/merkledag/node.pb.go b/ipld/merkledag/node.pb.go index e6442e4d1..bbfdbcdd2 100644 --- a/ipld/merkledag/node.pb.go +++ b/ipld/merkledag/node.pb.go @@ -15,31 +15,30 @@ package merkledag import proto "code.google.com/p/gogoprotobuf/proto" -import json "encoding/json" import math "math" // discarding unused import gogoproto "code.google.com/p/gogoprotobuf/gogoproto/gogo.pb" import io "io" +import fmt "fmt" import code_google_com_p_gogoprotobuf_proto "code.google.com/p/gogoprotobuf/proto" -import fmt "fmt" +import fmt1 "fmt" import strings "strings" import reflect "reflect" -import fmt1 "fmt" +import fmt2 "fmt" import strings1 "strings" import code_google_com_p_gogoprotobuf_proto1 "code.google.com/p/gogoprotobuf/proto" import sort "sort" import strconv "strconv" import reflect1 "reflect" -import fmt2 "fmt" +import fmt3 "fmt" import bytes "bytes" -// Reference proto, json, and math imports to suppress error if they are not otherwise used. +// Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal -var _ = &json.SyntaxError{} var _ = math.Inf // An IPFS MerkleDAG Link @@ -126,7 +125,7 @@ func (m *PBLink) Unmarshal(data []byte) error { switch fieldNum { case 1: if wireType != 2 { - return code_google_com_p_gogoprotobuf_proto.ErrWrongType + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -148,7 +147,7 @@ func (m *PBLink) Unmarshal(data []byte) error { index = postIndex case 2: if wireType != 2 { - return code_google_com_p_gogoprotobuf_proto.ErrWrongType + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -171,7 +170,7 @@ func (m *PBLink) Unmarshal(data []byte) error { index = postIndex case 3: if wireType != 0 { - return code_google_com_p_gogoprotobuf_proto.ErrWrongType + return fmt.Errorf("proto: wrong wireType = %d for field Tsize", wireType) } var v uint64 for shift := uint(0); ; shift += 7 { @@ -230,7 +229,7 @@ func (m *PBNode) Unmarshal(data []byte) error { switch fieldNum { case 2: if wireType != 2 { - return code_google_com_p_gogoprotobuf_proto.ErrWrongType + return fmt.Errorf("proto: wrong wireType = %d for field Links", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -253,7 +252,7 @@ func (m *PBNode) Unmarshal(data []byte) error { index = postIndex case 1: if wireType != 2 { - return code_google_com_p_gogoprotobuf_proto.ErrWrongType + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -304,7 +303,7 @@ func (this *PBLink) String() string { `Hash:` + valueToStringNode(this.Hash) + `,`, `Name:` + valueToStringNode(this.Name) + `,`, `Tsize:` + valueToStringNode(this.Tsize) + `,`, - `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `XXX_unrecognized:` + fmt1.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -314,9 +313,9 @@ func (this *PBNode) String() string { return "nil" } s := strings.Join([]string{`&PBNode{`, - `Links:` + strings.Replace(fmt.Sprintf("%v", this.Links), "PBLink", "PBLink", 1) + `,`, + `Links:` + strings.Replace(fmt1.Sprintf("%v", this.Links), "PBLink", "PBLink", 1) + `,`, `Data:` + valueToStringNode(this.Data) + `,`, - `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `XXX_unrecognized:` + fmt1.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -327,7 +326,7 @@ func valueToStringNode(v interface{}) string { return "nil" } pv := reflect.Indirect(rv).Interface() - return fmt.Sprintf("*%v", pv) + return fmt1.Sprintf("*%v", pv) } func (m *PBLink) Size() (n int) { var l int @@ -601,14 +600,14 @@ func (this *PBLink) GoString() string { if this == nil { return "nil" } - s := strings1.Join([]string{`&merkledag.PBLink{` + `Hash:` + valueToGoStringNode(this.Hash, "byte"), `Name:` + valueToGoStringNode(this.Name, "string"), `Tsize:` + valueToGoStringNode(this.Tsize, "uint64"), `XXX_unrecognized:` + fmt1.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") + s := strings1.Join([]string{`&merkledag.PBLink{` + `Hash:` + valueToGoStringNode(this.Hash, "byte"), `Name:` + valueToGoStringNode(this.Name, "string"), `Tsize:` + valueToGoStringNode(this.Tsize, "uint64"), `XXX_unrecognized:` + fmt2.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") return s } func (this *PBNode) GoString() string { if this == nil { return "nil" } - s := strings1.Join([]string{`&merkledag.PBNode{` + `Links:` + fmt1.Sprintf("%#v", this.Links), `Data:` + valueToGoStringNode(this.Data, "byte"), `XXX_unrecognized:` + fmt1.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") + s := strings1.Join([]string{`&merkledag.PBNode{` + `Links:` + fmt2.Sprintf("%#v", this.Links), `Data:` + valueToGoStringNode(this.Data, "byte"), `XXX_unrecognized:` + fmt2.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") return s } func valueToGoStringNode(v interface{}, typ string) string { @@ -617,7 +616,7 @@ func valueToGoStringNode(v interface{}, typ string) string { return "nil" } pv := reflect1.Indirect(rv).Interface() - return fmt1.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) + return fmt2.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) } func extensionToGoStringNode(e map[int32]code_google_com_p_gogoprotobuf_proto1.Extension) string { if e == nil { @@ -641,44 +640,44 @@ func (this *PBLink) VerboseEqual(that interface{}) error { if this == nil { return nil } - return fmt2.Errorf("that == nil && this != nil") + return fmt3.Errorf("that == nil && this != nil") } that1, ok := that.(*PBLink) if !ok { - return fmt2.Errorf("that is not of type *PBLink") + return fmt3.Errorf("that is not of type *PBLink") } if that1 == nil { if this == nil { return nil } - return fmt2.Errorf("that is type *PBLink but is nil && this != nil") + return fmt3.Errorf("that is type *PBLink but is nil && this != nil") } else if this == nil { - return fmt2.Errorf("that is type *PBLinkbut is not nil && this == nil") + return fmt3.Errorf("that is type *PBLinkbut is not nil && this == nil") } if !bytes.Equal(this.Hash, that1.Hash) { - return fmt2.Errorf("Hash this(%v) Not Equal that(%v)", this.Hash, that1.Hash) + return fmt3.Errorf("Hash this(%v) Not Equal that(%v)", this.Hash, that1.Hash) } if this.Name != nil && that1.Name != nil { if *this.Name != *that1.Name { - return fmt2.Errorf("Name this(%v) Not Equal that(%v)", *this.Name, *that1.Name) + return fmt3.Errorf("Name this(%v) Not Equal that(%v)", *this.Name, *that1.Name) } } else if this.Name != nil { - return fmt2.Errorf("this.Name == nil && that.Name != nil") + return fmt3.Errorf("this.Name == nil && that.Name != nil") } else if that1.Name != nil { - return fmt2.Errorf("Name this(%v) Not Equal that(%v)", this.Name, that1.Name) + return fmt3.Errorf("Name this(%v) Not Equal that(%v)", this.Name, that1.Name) } if this.Tsize != nil && that1.Tsize != nil { if *this.Tsize != *that1.Tsize { - return fmt2.Errorf("Tsize this(%v) Not Equal that(%v)", *this.Tsize, *that1.Tsize) + return fmt3.Errorf("Tsize this(%v) Not Equal that(%v)", *this.Tsize, *that1.Tsize) } } else if this.Tsize != nil { - return fmt2.Errorf("this.Tsize == nil && that.Tsize != nil") + return fmt3.Errorf("this.Tsize == nil && that.Tsize != nil") } else if that1.Tsize != nil { - return fmt2.Errorf("Tsize this(%v) Not Equal that(%v)", this.Tsize, that1.Tsize) + return fmt3.Errorf("Tsize this(%v) Not Equal that(%v)", this.Tsize, that1.Tsize) } if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { - return fmt2.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) + return fmt3.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) } return nil } @@ -733,34 +732,34 @@ func (this *PBNode) VerboseEqual(that interface{}) error { if this == nil { return nil } - return fmt2.Errorf("that == nil && this != nil") + return fmt3.Errorf("that == nil && this != nil") } that1, ok := that.(*PBNode) if !ok { - return fmt2.Errorf("that is not of type *PBNode") + return fmt3.Errorf("that is not of type *PBNode") } if that1 == nil { if this == nil { return nil } - return fmt2.Errorf("that is type *PBNode but is nil && this != nil") + return fmt3.Errorf("that is type *PBNode but is nil && this != nil") } else if this == nil { - return fmt2.Errorf("that is type *PBNodebut is not nil && this == nil") + return fmt3.Errorf("that is type *PBNodebut is not nil && this == nil") } if len(this.Links) != len(that1.Links) { - return fmt2.Errorf("Links this(%v) Not Equal that(%v)", len(this.Links), len(that1.Links)) + return fmt3.Errorf("Links this(%v) Not Equal that(%v)", len(this.Links), len(that1.Links)) } for i := range this.Links { if !this.Links[i].Equal(that1.Links[i]) { - return fmt2.Errorf("Links this[%v](%v) Not Equal that[%v](%v)", i, this.Links[i], i, that1.Links[i]) + return fmt3.Errorf("Links this[%v](%v) Not Equal that[%v](%v)", i, this.Links[i], i, that1.Links[i]) } } if !bytes.Equal(this.Data, that1.Data) { - return fmt2.Errorf("Data this(%v) Not Equal that(%v)", this.Data, that1.Data) + return fmt3.Errorf("Data this(%v) Not Equal that(%v)", this.Data, that1.Data) } if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { - return fmt2.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) + return fmt3.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) } return nil } From a07b0942b86839341dc6fa2be31a06cdec7504ee Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 16 Aug 2014 18:06:14 -0700 Subject: [PATCH 0058/3817] POut should not have a newline This commit was moved from ipfs/go-ipfs-routing@da9107320d642e7c0af41bd624a344aad32015a4 --- routing/kbucket/table.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 86a7031ce..d0137c6d7 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -116,7 +116,7 @@ func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) pe } peerArr = append(peerArr, &pd) if e == nil { - u.POut("list element was nil.") + u.POut("list element was nil.\n") return peerArr } } From 876b58a8c382d7e3755afa53891356fa3f37ef14 Mon Sep 17 00:00:00 2001 From: Chas Leichner Date: Sat, 16 Aug 2014 23:03:36 -0700 Subject: [PATCH 0059/3817] Made the DHT module pass golint This commit was moved from ipfs/go-ipfs-routing@3a8c02460b79dc77d8a9ce0cdfdc4dd534318d28 --- routing/dht/{DHTMessage.go => Message.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename routing/dht/{DHTMessage.go => Message.go} (100%) diff --git a/routing/dht/DHTMessage.go b/routing/dht/Message.go similarity index 100% rename from routing/dht/DHTMessage.go rename to routing/dht/Message.go From 8b2c678d076725ee564ff2f988a7cb60a28378e7 Mon Sep 17 00:00:00 2001 From: Chas Leichner Date: Sat, 16 Aug 2014 23:03:36 -0700 Subject: [PATCH 0060/3817] Made the DHT module pass golint This commit was moved from ipfs/go-ipfs-routing@db5b20dc3c79a83e261bf6231ec801b34ef50074 --- routing/dht/Message.go | 11 +-- routing/dht/dht.go | 162 +++++++++++++++++----------------- routing/dht/dht_logger.go | 12 +-- routing/dht/dht_test.go | 74 ++++++++-------- routing/dht/diag.go | 8 +- routing/dht/mes_listener.go | 16 ++-- routing/dht/messages.pb.go | 23 ++--- routing/dht/routing.go | 158 ++++++++++++++++----------------- routing/kbucket/bucket.go | 22 ++--- routing/kbucket/table.go | 76 ++++++++-------- routing/kbucket/table_test.go | 38 ++++---- routing/kbucket/util.go | 18 ++-- routing/routing.go | 3 +- 13 files changed, 312 insertions(+), 309 deletions(-) diff --git a/routing/dht/Message.go b/routing/dht/Message.go index e2034d7e0..71a9537f9 100644 --- a/routing/dht/Message.go +++ b/routing/dht/Message.go @@ -4,13 +4,13 @@ import ( peer "github.com/jbenet/go-ipfs/peer" ) -// A helper struct to make working with protbuf types easier -type DHTMessage struct { +// Message is a a helper struct which makes working with protbuf types easier +type Message struct { Type PBDHTMessage_MessageType Key string Value []byte Response bool - Id uint64 + ID uint64 Success bool Peers []*peer.Peer } @@ -28,9 +28,10 @@ func peerInfo(p *peer.Peer) *PBDHTMessage_PBPeer { return pbp } +// ToProtobuf takes a Message and produces a protobuf with it. // TODO: building the protobuf message this way is a little wasteful // Unused fields wont be omitted, find a better way to do this -func (m *DHTMessage) ToProtobuf() *PBDHTMessage { +func (m *Message) ToProtobuf() *PBDHTMessage { pmes := new(PBDHTMessage) if m.Value != nil { pmes.Value = m.Value @@ -39,7 +40,7 @@ func (m *DHTMessage) ToProtobuf() *PBDHTMessage { pmes.Type = &m.Type pmes.Key = &m.Key pmes.Response = &m.Response - pmes.Id = &m.Id + pmes.Id = &m.ID pmes.Success = &m.Success for _, p := range m.Peers { pmes.Peers = append(pmes.Peers, peerInfo(p)) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index c28ca0a0f..901f1a861 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -25,7 +25,7 @@ import ( type IpfsDHT struct { // Array of routing tables for differently distanced nodes // NOTE: (currently, only a single table is used) - routes []*kb.RoutingTable + routingTables []*kb.RoutingTable network swarm.Network @@ -49,7 +49,7 @@ type IpfsDHT struct { diaglock sync.Mutex // listener is a server to register to listen for responses to messages - listener *MesListener + listener *mesListener } // NewDHT creates a new DHT object with the given peer as the 'local' host @@ -61,12 +61,11 @@ func NewDHT(p *peer.Peer, net swarm.Network) *IpfsDHT { dht.providers = make(map[u.Key][]*providerInfo) dht.shutdown = make(chan struct{}) - dht.routes = make([]*kb.RoutingTable, 3) - dht.routes[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*30) - dht.routes[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*100) - dht.routes[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Hour) - - dht.listener = NewMesListener() + dht.routingTables = make([]*kb.RoutingTable, 3) + dht.routingTables[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*30) + dht.routingTables[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*100) + dht.routingTables[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Hour) + dht.listener = newMesListener() dht.birth = time.Now() return dht } @@ -175,11 +174,11 @@ func (dht *IpfsDHT) cleanExpiredProviders() { } func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) error { - pmes := DHTMessage{ + pmes := Message{ Type: PBDHTMessage_PUT_VALUE, Key: key, Value: value, - Id: GenerateMessageID(), + ID: GenerateMessageID(), } mes := swarm.NewMessage(p, pmes.ToProtobuf()) @@ -190,9 +189,9 @@ func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) er func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { u.DOut("handleGetValue for key: %s", pmes.GetKey()) dskey := ds.NewKey(pmes.GetKey()) - resp := &DHTMessage{ + resp := &Message{ Response: true, - Id: pmes.GetId(), + ID: pmes.GetId(), Key: pmes.GetKey(), } iVal, err := dht.datastore.Get(dskey) @@ -222,7 +221,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { } u.DOut("handleGetValue searching level %d clusters", level) - closer := dht.routes[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) + closer := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) if closer.ID.Equal(dht.self.ID) { u.DOut("Attempted to return self! this shouldnt happen...") @@ -259,19 +258,19 @@ func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *PBDHTMessage) { } func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *PBDHTMessage) { - resp := DHTMessage{ + resp := Message{ Type: pmes.GetType(), Response: true, - Id: pmes.GetId(), + ID: pmes.GetId(), } dht.network.Send(swarm.NewMessage(p, resp.ToProtobuf())) } func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) { - resp := DHTMessage{ + resp := Message{ Type: pmes.GetType(), - Id: pmes.GetId(), + ID: pmes.GetId(), Response: true, } defer func() { @@ -280,7 +279,7 @@ func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) { }() level := pmes.GetValue()[0] u.DOut("handleFindPeer: searching for '%s'", peer.ID(pmes.GetKey()).Pretty()) - closest := dht.routes[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) + closest := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) if closest == nil { u.PErr("handleFindPeer: could not find anything.") return @@ -302,10 +301,10 @@ func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) { } func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *PBDHTMessage) { - resp := DHTMessage{ + resp := Message{ Type: PBDHTMessage_GET_PROVIDERS, Key: pmes.GetKey(), - Id: pmes.GetId(), + ID: pmes.GetId(), Response: true, } @@ -318,7 +317,7 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *PBDHTMessage) { level = int(pmes.GetValue()[0]) } - closer := dht.routes[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) + closer := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) if kb.Closer(dht.self.ID, closer.ID, u.Key(pmes.GetKey())) { resp.Peers = nil } else { @@ -346,7 +345,7 @@ func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *PBDHTMessage) { dht.addProviderEntry(key, p) } -// Stop all communications from this peer and shut down +// Halt stops all communications from this peer and shut down func (dht *IpfsDHT) Halt() { dht.shutdown <- struct{}{} dht.network.Close() @@ -362,7 +361,7 @@ func (dht *IpfsDHT) addProviderEntry(key u.Key, p *peer.Peer) { // NOTE: not yet finished, low priority func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { - seq := dht.routes[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) + seq := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) listenChan := dht.listener.Listen(pmes.GetId(), len(seq), time.Second*30) for _, ps := range seq { @@ -382,22 +381,22 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { case <-after: //Timeout, return what we have goto out - case req_resp := <-listenChan: - pmes_out := new(PBDHTMessage) - err := proto.Unmarshal(req_resp.Data, pmes_out) + case reqResp := <-listenChan: + pmesOut := new(PBDHTMessage) + err := proto.Unmarshal(reqResp.Data, pmesOut) if err != nil { // It broke? eh, whatever, keep going continue } - buf.Write(req_resp.Data) + buf.Write(reqResp.Data) count-- } } out: - resp := DHTMessage{ + resp := Message{ Type: PBDHTMessage_DIAGNOSTIC, - Id: pmes.GetId(), + ID: pmes.GetId(), Value: buf.Bytes(), Response: true, } @@ -423,40 +422,40 @@ func (dht *IpfsDHT) getValueOrPeers(p *peer.Peer, key u.Key, timeout time.Durati // Success! We were given the value return pmes.GetValue(), nil, nil - } else { - // We were given a closer node - var peers []*peer.Peer - for _, pb := range pmes.GetPeers() { - if peer.ID(pb.GetId()).Equal(dht.self.ID) { - continue - } - addr, err := ma.NewMultiaddr(pb.GetAddr()) - if err != nil { - u.PErr(err.Error()) - continue - } + } - np, err := dht.network.GetConnection(peer.ID(pb.GetId()), addr) - if err != nil { - u.PErr(err.Error()) - continue - } + // We were given a closer node + var peers []*peer.Peer + for _, pb := range pmes.GetPeers() { + if peer.ID(pb.GetId()).Equal(dht.self.ID) { + continue + } + addr, err := ma.NewMultiaddr(pb.GetAddr()) + if err != nil { + u.PErr(err.Error()) + continue + } - peers = append(peers, np) + np, err := dht.network.GetConnection(peer.ID(pb.GetId()), addr) + if err != nil { + u.PErr(err.Error()) + continue } - return nil, peers, nil + + peers = append(peers, np) } + return nil, peers, nil } // getValueSingle simply performs the get value RPC with the given parameters func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duration, level int) (*PBDHTMessage, error) { - pmes := DHTMessage{ + pmes := Message{ Type: PBDHTMessage_GET_VALUE, Key: string(key), Value: []byte{byte(level)}, - Id: GenerateMessageID(), + ID: GenerateMessageID(), } - response_chan := dht.listener.Listen(pmes.Id, 1, time.Minute) + responseChan := dht.listener.Listen(pmes.ID, 1, time.Minute) mes := swarm.NewMessage(p, pmes.ToProtobuf()) t := time.Now() @@ -466,21 +465,21 @@ func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duratio timeup := time.After(timeout) select { case <-timeup: - dht.listener.Unlisten(pmes.Id) + dht.listener.Unlisten(pmes.ID) return nil, u.ErrTimeout - case resp, ok := <-response_chan: + case resp, ok := <-responseChan: if !ok { u.PErr("response channel closed before timeout, please investigate.") return nil, u.ErrTimeout } roundtrip := time.Since(t) resp.Peer.SetLatency(roundtrip) - pmes_out := new(PBDHTMessage) - err := proto.Unmarshal(resp.Data, pmes_out) + pmesOut := new(PBDHTMessage) + err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { return nil, err } - return pmes_out, nil + return pmesOut, nil } } @@ -520,7 +519,7 @@ func (dht *IpfsDHT) getFromPeerList(key u.Key, timeout time.Duration, return nil, u.ErrNotFound } -func (dht *IpfsDHT) GetLocal(key u.Key) ([]byte, error) { +func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { v, err := dht.datastore.Get(ds.NewKey(string(key))) if err != nil { return nil, err @@ -528,17 +527,18 @@ func (dht *IpfsDHT) GetLocal(key u.Key) ([]byte, error) { return v.([]byte), nil } -func (dht *IpfsDHT) PutLocal(key u.Key, value []byte) error { +func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { return dht.datastore.Put(ds.NewKey(string(key)), value) } +// Update TODO(chas) Document this function func (dht *IpfsDHT) Update(p *peer.Peer) { - for _, route := range dht.routes { + for _, route := range dht.routingTables { removed := route.Update(p) // Only drop the connection if no tables refer to this peer if removed != nil { found := false - for _, r := range dht.routes { + for _, r := range dht.routingTables { if r.Find(removed.ID) != nil { found = true break @@ -551,9 +551,9 @@ func (dht *IpfsDHT) Update(p *peer.Peer) { } } -// Look for a peer with a given ID connected to this dht +// Find looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in. func (dht *IpfsDHT) Find(id peer.ID) (*peer.Peer, *kb.RoutingTable) { - for _, table := range dht.routes { + for _, table := range dht.routingTables { p := table.Find(id) if p != nil { return p, table @@ -563,72 +563,72 @@ func (dht *IpfsDHT) Find(id peer.ID) (*peer.Peer, *kb.RoutingTable) { } func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Duration, level int) (*PBDHTMessage, error) { - pmes := DHTMessage{ + pmes := Message{ Type: PBDHTMessage_FIND_NODE, Key: string(id), - Id: GenerateMessageID(), + ID: GenerateMessageID(), Value: []byte{byte(level)}, } mes := swarm.NewMessage(p, pmes.ToProtobuf()) - listenChan := dht.listener.Listen(pmes.Id, 1, time.Minute) + listenChan := dht.listener.Listen(pmes.ID, 1, time.Minute) t := time.Now() dht.network.Send(mes) after := time.After(timeout) select { case <-after: - dht.listener.Unlisten(pmes.Id) + dht.listener.Unlisten(pmes.ID) return nil, u.ErrTimeout case resp := <-listenChan: roundtrip := time.Since(t) resp.Peer.SetLatency(roundtrip) - pmes_out := new(PBDHTMessage) - err := proto.Unmarshal(resp.Data, pmes_out) + pmesOut := new(PBDHTMessage) + err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { return nil, err } - return pmes_out, nil + return pmesOut, nil } } -func (dht *IpfsDHT) PrintTables() { - for _, route := range dht.routes { +func (dht *IpfsDHT) printTables() { + for _, route := range dht.routingTables { route.Print() } } func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, timeout time.Duration) (*PBDHTMessage, error) { - pmes := DHTMessage{ + pmes := Message{ Type: PBDHTMessage_GET_PROVIDERS, Key: string(key), - Id: GenerateMessageID(), + ID: GenerateMessageID(), Value: []byte{byte(level)}, } mes := swarm.NewMessage(p, pmes.ToProtobuf()) - listenChan := dht.listener.Listen(pmes.Id, 1, time.Minute) + listenChan := dht.listener.Listen(pmes.ID, 1, time.Minute) dht.network.Send(mes) after := time.After(timeout) select { case <-after: - dht.listener.Unlisten(pmes.Id) + dht.listener.Unlisten(pmes.ID) return nil, u.ErrTimeout case resp := <-listenChan: u.DOut("FindProviders: got response.") - pmes_out := new(PBDHTMessage) - err := proto.Unmarshal(resp.Data, pmes_out) + pmesOut := new(PBDHTMessage) + err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { return nil, err } - return pmes_out, nil + return pmesOut, nil } } func (dht *IpfsDHT) addPeerList(key u.Key, peers []*PBDHTMessage_PBPeer) []*peer.Peer { - var prov_arr []*peer.Peer + var provArr []*peer.Peer for _, prov := range peers { // Dont add outselves to the list if peer.ID(prov.GetId()).Equal(dht.self.ID) { @@ -650,7 +650,7 @@ func (dht *IpfsDHT) addPeerList(key u.Key, peers []*PBDHTMessage_PBPeer) []*peer } } dht.addProviderEntry(key, p) - prov_arr = append(prov_arr, p) + provArr = append(provArr, p) } - return prov_arr + return provArr } diff --git a/routing/dht/dht_logger.go b/routing/dht/dht_logger.go index c892959f0..4a02fc304 100644 --- a/routing/dht/dht_logger.go +++ b/routing/dht/dht_logger.go @@ -7,28 +7,28 @@ import ( u "github.com/jbenet/go-ipfs/util" ) -type logDhtRpc struct { +type logDhtRPC struct { Type string Start time.Time End time.Time Duration time.Duration - RpcCount int + RPCCount int Success bool } -func startNewRpc(name string) *logDhtRpc { - r := new(logDhtRpc) +func startNewRPC(name string) *logDhtRPC { + r := new(logDhtRPC) r.Type = name r.Start = time.Now() return r } -func (l *logDhtRpc) EndLog() { +func (l *logDhtRPC) EndLog() { l.End = time.Now() l.Duration = l.End.Sub(l.Start) } -func (l *logDhtRpc) Print() { +func (l *logDhtRPC) Print() { b, err := json.Marshal(l) if err != nil { u.DOut(err.Error()) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index a7e14d703..581e19277 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -47,93 +47,93 @@ func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) func TestPing(t *testing.T) { u.Debug = false - addr_a, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/2222") + addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/2222") if err != nil { t.Fatal(err) } - addr_b, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5678") + addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5678") if err != nil { t.Fatal(err) } - peer_a := new(peer.Peer) - peer_a.AddAddress(addr_a) - peer_a.ID = peer.ID([]byte("peer_a")) + peerA := new(peer.Peer) + peerA.AddAddress(addrA) + peerA.ID = peer.ID([]byte("peerA")) - peer_b := new(peer.Peer) - peer_b.AddAddress(addr_b) - peer_b.ID = peer.ID([]byte("peer_b")) + peerB := new(peer.Peer) + peerB.AddAddress(addrB) + peerB.ID = peer.ID([]byte("peerB")) - neta := swarm.NewSwarm(peer_a) + neta := swarm.NewSwarm(peerA) err = neta.Listen() if err != nil { t.Fatal(err) } - dht_a := NewDHT(peer_a, neta) + dhtA := NewDHT(peerA, neta) - netb := swarm.NewSwarm(peer_b) + netb := swarm.NewSwarm(peerB) err = netb.Listen() if err != nil { t.Fatal(err) } - dht_b := NewDHT(peer_b, netb) + dhtB := NewDHT(peerB, netb) - dht_a.Start() - dht_b.Start() + dhtA.Start() + dhtB.Start() - _, err = dht_a.Connect(addr_b) + _, err = dhtA.Connect(addrB) if err != nil { t.Fatal(err) } //Test that we can ping the node - err = dht_a.Ping(peer_b, time.Second*2) + err = dhtA.Ping(peerB, time.Second*2) if err != nil { t.Fatal(err) } - dht_a.Halt() - dht_b.Halt() + dhtA.Halt() + dhtB.Halt() } func TestValueGetSet(t *testing.T) { u.Debug = false - addr_a, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") + addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") if err != nil { t.Fatal(err) } - addr_b, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5679") + addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5679") if err != nil { t.Fatal(err) } - peer_a := new(peer.Peer) - peer_a.AddAddress(addr_a) - peer_a.ID = peer.ID([]byte("peer_a")) + peerA := new(peer.Peer) + peerA.AddAddress(addrA) + peerA.ID = peer.ID([]byte("peerA")) - peer_b := new(peer.Peer) - peer_b.AddAddress(addr_b) - peer_b.ID = peer.ID([]byte("peer_b")) + peerB := new(peer.Peer) + peerB.AddAddress(addrB) + peerB.ID = peer.ID([]byte("peerB")) - neta := swarm.NewSwarm(peer_a) + neta := swarm.NewSwarm(peerA) err = neta.Listen() if err != nil { t.Fatal(err) } - dht_a := NewDHT(peer_a, neta) + dhtA := NewDHT(peerA, neta) - netb := swarm.NewSwarm(peer_b) + netb := swarm.NewSwarm(peerB) err = netb.Listen() if err != nil { t.Fatal(err) } - dht_b := NewDHT(peer_b, netb) + dhtB := NewDHT(peerB, netb) - dht_a.Start() - dht_b.Start() + dhtA.Start() + dhtB.Start() - errsa := dht_a.network.GetChan().Errors - errsb := dht_b.network.GetChan().Errors + errsa := dhtA.network.GetChan().Errors + errsb := dhtB.network.GetChan().Errors go func() { select { case err := <-errsa: @@ -143,14 +143,14 @@ func TestValueGetSet(t *testing.T) { } }() - _, err = dht_a.Connect(addr_b) + _, err = dhtA.Connect(addrB) if err != nil { t.Fatal(err) } - dht_a.PutValue("hello", []byte("world")) + dhtA.PutValue("hello", []byte("world")) - val, err := dht_a.GetValue("hello", time.Second*2) + val, err := dhtA.GetValue("hello", time.Second*2) if err != nil { t.Fatal(err) } diff --git a/routing/dht/diag.go b/routing/dht/diag.go index 03997c5e7..d6bc6bacf 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -9,11 +9,11 @@ import ( type connDiagInfo struct { Latency time.Duration - Id peer.ID + ID peer.ID } type diagInfo struct { - Id peer.ID + ID peer.ID Connections []connDiagInfo Keys []string LifeSpan time.Duration @@ -32,11 +32,11 @@ func (di *diagInfo) Marshal() []byte { func (dht *IpfsDHT) getDiagInfo() *diagInfo { di := new(diagInfo) di.CodeVersion = "github.com/jbenet/go-ipfs" - di.Id = dht.self.ID + di.ID = dht.self.ID di.LifeSpan = time.Since(dht.birth) di.Keys = nil // Currently no way to query datastore - for _, p := range dht.routes[0].Listpeers() { + for _, p := range dht.routingTables[0].Listpeers() { di.Connections = append(di.Connections, connDiagInfo{p.GetLatency(), p.ID}) } return di diff --git a/routing/dht/mes_listener.go b/routing/dht/mes_listener.go index 2fcd99fc1..133be877a 100644 --- a/routing/dht/mes_listener.go +++ b/routing/dht/mes_listener.go @@ -8,7 +8,7 @@ import ( u "github.com/jbenet/go-ipfs/util" ) -type MesListener struct { +type mesListener struct { listeners map[uint64]*listenInfo haltchan chan struct{} unlist chan uint64 @@ -36,8 +36,8 @@ type listenInfo struct { id uint64 } -func NewMesListener() *MesListener { - ml := new(MesListener) +func newMesListener() *mesListener { + ml := new(mesListener) ml.haltchan = make(chan struct{}) ml.listeners = make(map[uint64]*listenInfo) ml.nlist = make(chan *listenInfo, 16) @@ -47,7 +47,7 @@ func NewMesListener() *MesListener { return ml } -func (ml *MesListener) Listen(id uint64, count int, timeout time.Duration) <-chan *swarm.Message { +func (ml *mesListener) Listen(id uint64, count int, timeout time.Duration) <-chan *swarm.Message { li := new(listenInfo) li.count = count li.eol = time.Now().Add(timeout) @@ -57,7 +57,7 @@ func (ml *MesListener) Listen(id uint64, count int, timeout time.Duration) <-cha return li.resp } -func (ml *MesListener) Unlisten(id uint64) { +func (ml *mesListener) Unlisten(id uint64) { ml.unlist <- id } @@ -66,18 +66,18 @@ type respMes struct { mes *swarm.Message } -func (ml *MesListener) Respond(id uint64, mes *swarm.Message) { +func (ml *mesListener) Respond(id uint64, mes *swarm.Message) { ml.send <- &respMes{ id: id, mes: mes, } } -func (ml *MesListener) Halt() { +func (ml *mesListener) Halt() { ml.haltchan <- struct{}{} } -func (ml *MesListener) run() { +func (ml *mesListener) run() { for { select { case <-ml.haltchan: diff --git a/routing/dht/messages.pb.go b/routing/dht/messages.pb.go index a852c5e1f..7c337d306 100644 --- a/routing/dht/messages.pb.go +++ b/routing/dht/messages.pb.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-go. +// Code generated by protoc-gen-gogo. // source: messages.proto // DO NOT EDIT! @@ -13,7 +13,7 @@ It has these top-level messages: */ package dht -import proto "code.google.com/p/goprotobuf/proto" +import proto "code.google.com/p/gogoprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. @@ -69,14 +69,17 @@ func (x *PBDHTMessage_MessageType) UnmarshalJSON(data []byte) error { } type PBDHTMessage struct { - Type *PBDHTMessage_MessageType `protobuf:"varint,1,req,name=type,enum=dht.PBDHTMessage_MessageType" json:"type,omitempty"` - Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` - Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` - Id *uint64 `protobuf:"varint,4,req,name=id" json:"id,omitempty"` - Response *bool `protobuf:"varint,5,opt,name=response" json:"response,omitempty"` - Success *bool `protobuf:"varint,6,opt,name=success" json:"success,omitempty"` - Peers []*PBDHTMessage_PBPeer `protobuf:"bytes,7,rep,name=peers" json:"peers,omitempty"` - XXX_unrecognized []byte `json:"-"` + Type *PBDHTMessage_MessageType `protobuf:"varint,1,req,name=type,enum=dht.PBDHTMessage_MessageType" json:"type,omitempty"` + Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` + Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` + // Unique ID of this message, used to match queries with responses + Id *uint64 `protobuf:"varint,4,req,name=id" json:"id,omitempty"` + // Signals whether or not this message is a response to another message + Response *bool `protobuf:"varint,5,opt,name=response" json:"response,omitempty"` + Success *bool `protobuf:"varint,6,opt,name=success" json:"success,omitempty"` + // Used for returning peers from queries (normally, peers closer to X) + Peers []*PBDHTMessage_PBPeer `protobuf:"bytes,7,rep,name=peers" json:"peers,omitempty"` + XXX_unrecognized []byte `json:"-"` } func (m *PBDHTMessage) Reset() { *m = PBDHTMessage{} } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 309962a93..4e91e0eb4 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -27,6 +27,7 @@ var KValue = 10 // Its in the paper, i swear var AlphaValue = 3 +// GenerateMessageID creates and returns a new message ID // TODO: determine a way of creating and managing message IDs func GenerateMessageID() uint64 { //return (uint64(rand.Uint32()) << 32) & uint64(rand.Uint32()) @@ -39,21 +40,21 @@ func GenerateMessageID() uint64 { // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT -func (s *IpfsDHT) PutValue(key u.Key, value []byte) { +func (dht *IpfsDHT) PutValue(key u.Key, value []byte) { complete := make(chan struct{}) count := 0 - for _, route := range s.routes { + for _, route := range dht.routingTables { peers := route.NearestPeers(kb.ConvertKey(key), KValue) for _, p := range peers { if p == nil { - s.network.Error(kb.ErrLookupFailure) + dht.network.Error(kb.ErrLookupFailure) continue } count++ go func(sp *peer.Peer) { - err := s.putValueToNetwork(sp, string(key), value) + err := dht.putValueToNetwork(sp, string(key), value) if err != nil { - s.network.Error(err) + dht.network.Error(err) } complete <- struct{}{} }(p) @@ -121,8 +122,8 @@ func (ps *peerSet) Size() int { // GetValue searches for the value corresponding to given Key. // If the search does not succeed, a multiaddr string of a closer peer is // returned along with util.ErrSearchIncomplete -func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { - ll := startNewRpc("GET") +func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { + ll := startNewRPC("GET") defer func() { ll.EndLog() ll.Print() @@ -130,29 +131,29 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { // If we have it local, dont bother doing an RPC! // NOTE: this might not be what we want to do... - val, err := s.GetLocal(key) + val, err := dht.getLocal(key) if err == nil { ll.Success = true u.DOut("Found local, returning.") return val, nil } - route_level := 0 - closest := s.routes[route_level].NearestPeers(kb.ConvertKey(key), PoolSize) + routeLevel := 0 + closest := dht.routingTables[routeLevel].NearestPeers(kb.ConvertKey(key), PoolSize) if closest == nil || len(closest) == 0 { return nil, kb.ErrLookupFailure } - val_chan := make(chan []byte) - npeer_chan := make(chan *peer.Peer, 30) - proc_peer := make(chan *peer.Peer, 30) - err_chan := make(chan error) + valChan := make(chan []byte) + npeerChan := make(chan *peer.Peer, 30) + procPeer := make(chan *peer.Peer, 30) + errChan := make(chan error) after := time.After(timeout) pset := newPeerSet() for _, p := range closest { pset.Add(p) - npeer_chan <- p + npeerChan <- p } c := counter{} @@ -161,17 +162,17 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { go func() { for { select { - case p := <-npeer_chan: + case p := <-npeerChan: count++ if count >= KValue { break } c.Increment() - proc_peer <- p + procPeer <- p default: if c.Size() == 0 { - err_chan <- u.ErrNotFound + errChan <- u.ErrNotFound } } } @@ -180,19 +181,19 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { process := func() { for { select { - case p, ok := <-proc_peer: + case p, ok := <-procPeer: if !ok || p == nil { c.Decrement() return } - val, peers, err := s.getValueOrPeers(p, key, timeout/4, route_level) + val, peers, err := dht.getValueOrPeers(p, key, timeout/4, routeLevel) if err != nil { u.DErr(err.Error()) c.Decrement() continue } if val != nil { - val_chan <- val + valChan <- val c.Decrement() return } @@ -201,7 +202,7 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { // TODO: filter out peers that arent closer if !pset.Contains(np) && pset.Size() < KValue { pset.Add(np) //This is racey... make a single function to do operation - npeer_chan <- np + npeerChan <- np } } c.Decrement() @@ -214,9 +215,9 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { } select { - case val := <-val_chan: + case val := <-valChan: return val, nil - case err := <-err_chan: + case err := <-errChan: return nil, err case <-after: return nil, u.ErrTimeout @@ -226,14 +227,14 @@ func (s *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { // Value provider layer of indirection. // This is what DSHTs (Coral and MainlineDHT) do to store large values in a DHT. -// Announce that this node can provide value for given key -func (s *IpfsDHT) Provide(key u.Key) error { - peers := s.routes[0].NearestPeers(kb.ConvertKey(key), PoolSize) +// Provide makes this node announce that it can provide a value for the given key +func (dht *IpfsDHT) Provide(key u.Key) error { + peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), PoolSize) if len(peers) == 0 { return kb.ErrLookupFailure } - pmes := DHTMessage{ + pmes := Message{ Type: PBDHTMessage_ADD_PROVIDER, Key: string(key), } @@ -241,57 +242,57 @@ func (s *IpfsDHT) Provide(key u.Key) error { for _, p := range peers { mes := swarm.NewMessage(p, pbmes) - s.network.Send(mes) + dht.network.Send(mes) } return nil } // FindProviders searches for peers who can provide the value for given key. -func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) { - ll := startNewRpc("FindProviders") +func (dht *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) { + ll := startNewRPC("FindProviders") defer func() { ll.EndLog() ll.Print() }() u.DOut("Find providers for: '%s'", key) - p := s.routes[0].NearestPeer(kb.ConvertKey(key)) + p := dht.routingTables[0].NearestPeer(kb.ConvertKey(key)) if p == nil { return nil, kb.ErrLookupFailure } - for level := 0; level < len(s.routes); { - pmes, err := s.findProvidersSingle(p, key, level, timeout) + for level := 0; level < len(dht.routingTables); { + pmes, err := dht.findProvidersSingle(p, key, level, timeout) if err != nil { return nil, err } if pmes.GetSuccess() { - provs := s.addPeerList(key, pmes.GetPeers()) + provs := dht.addPeerList(key, pmes.GetPeers()) ll.Success = true return provs, nil - } else { - closer := pmes.GetPeers() - if len(closer) == 0 { - level++ - continue - } - if peer.ID(closer[0].GetId()).Equal(s.self.ID) { - u.DOut("Got myself back as a closer peer.") - return nil, u.ErrNotFound - } - maddr, err := ma.NewMultiaddr(closer[0].GetAddr()) - if err != nil { - // ??? Move up route level??? - panic("not yet implemented") - } + } - np, err := s.network.GetConnection(peer.ID(closer[0].GetId()), maddr) - if err != nil { - u.PErr("[%s] Failed to connect to: %s", s.self.ID.Pretty(), closer[0].GetAddr()) - level++ - continue - } - p = np + closer := pmes.GetPeers() + if len(closer) == 0 { + level++ + continue + } + if peer.ID(closer[0].GetId()).Equal(dht.self.ID) { + u.DOut("Got myself back as a closer peer.") + return nil, u.ErrNotFound + } + maddr, err := ma.NewMultiaddr(closer[0].GetAddr()) + if err != nil { + // ??? Move up route level??? + panic("not yet implemented") + } + + np, err := dht.network.GetConnection(peer.ID(closer[0].GetId()), maddr) + if err != nil { + u.PErr("[%s] Failed to connect to: %s", dht.self.ID.Pretty(), closer[0].GetAddr()) + level++ + continue } + p = np } return nil, u.ErrNotFound } @@ -299,15 +300,15 @@ func (s *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, // Find specific Peer // FindPeer searches for a peer with given ID. -func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) { +func (dht *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) { // Check if were already connected to them - p, _ := s.Find(id) + p, _ := dht.Find(id) if p != nil { return p, nil } - route_level := 0 - p = s.routes[route_level].NearestPeer(kb.ConvertPeerID(id)) + routeLevel := 0 + p = dht.routingTables[routeLevel].NearestPeer(kb.ConvertPeerID(id)) if p == nil { return nil, kb.ErrLookupFailure } @@ -315,11 +316,11 @@ func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error return p, nil } - for route_level < len(s.routes) { - pmes, err := s.findPeerSingle(p, id, timeout, route_level) + for routeLevel < len(dht.routingTables) { + pmes, err := dht.findPeerSingle(p, id, timeout, routeLevel) plist := pmes.GetPeers() if len(plist) == 0 { - route_level++ + routeLevel++ } found := plist[0] @@ -328,7 +329,7 @@ func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error return nil, err } - nxtPeer, err := s.network.GetConnection(peer.ID(found.GetId()), addr) + nxtPeer, err := dht.network.GetConnection(peer.ID(found.GetId()), addr) if err != nil { return nil, err } @@ -337,9 +338,8 @@ func (s *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error return nil, errors.New("got back invalid peer from 'successful' response") } return nxtPeer, nil - } else { - p = nxtPeer } + p = nxtPeer } return nil, u.ErrNotFound } @@ -349,16 +349,16 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { // Thoughts: maybe this should accept an ID and do a peer lookup? u.DOut("Enter Ping.") - pmes := DHTMessage{Id: GenerateMessageID(), Type: PBDHTMessage_PING} + pmes := Message{ID: GenerateMessageID(), Type: PBDHTMessage_PING} mes := swarm.NewMessage(p, pmes.ToProtobuf()) before := time.Now() - response_chan := dht.listener.Listen(pmes.Id, 1, time.Minute) + responseChan := dht.listener.Listen(pmes.ID, 1, time.Minute) dht.network.Send(mes) tout := time.After(timeout) select { - case <-response_chan: + case <-responseChan: roundtrip := time.Since(before) p.SetLatency(roundtrip) u.DOut("Ping took %s.", roundtrip.String()) @@ -366,23 +366,23 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { case <-tout: // Timed out, think about removing peer from network u.DOut("Ping peer timed out.") - dht.listener.Unlisten(pmes.Id) + dht.listener.Unlisten(pmes.ID) return u.ErrTimeout } } -func (dht *IpfsDHT) GetDiagnostic(timeout time.Duration) ([]*diagInfo, error) { +func (dht *IpfsDHT) getDiagnostic(timeout time.Duration) ([]*diagInfo, error) { u.DOut("Begin Diagnostic") //Send to N closest peers - targets := dht.routes[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) + targets := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) // TODO: Add timeout to this struct so nodes know when to return - pmes := DHTMessage{ + pmes := Message{ Type: PBDHTMessage_DIAGNOSTIC, - Id: GenerateMessageID(), + ID: GenerateMessageID(), } - listenChan := dht.listener.Listen(pmes.Id, len(targets), time.Minute*2) + listenChan := dht.listener.Listen(pmes.ID, len(targets), time.Minute*2) pbmes := pmes.ToProtobuf() for _, p := range targets { @@ -398,15 +398,15 @@ func (dht *IpfsDHT) GetDiagnostic(timeout time.Duration) ([]*diagInfo, error) { u.DOut("Diagnostic request timed out.") return out, u.ErrTimeout case resp := <-listenChan: - pmes_out := new(PBDHTMessage) - err := proto.Unmarshal(resp.Data, pmes_out) + pmesOut := new(PBDHTMessage) + err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { // NOTE: here and elsewhere, need to audit error handling, // some errors should be continued on from return out, err } - dec := json.NewDecoder(bytes.NewBuffer(pmes_out.GetValue())) + dec := json.NewDecoder(bytes.NewBuffer(pmesOut.GetValue())) for { di := new(diagInfo) err := dec.Decode(di) diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 1a55a0f69..77ed596db 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -13,13 +13,13 @@ type Bucket struct { list *list.List } -func NewBucket() *Bucket { +func newBucket() *Bucket { b := new(Bucket) b.list = list.New() return b } -func (b *Bucket) Find(id peer.ID) *list.Element { +func (b *Bucket) find(id peer.ID) *list.Element { b.lk.RLock() defer b.lk.RUnlock() for e := b.list.Front(); e != nil; e = e.Next() { @@ -30,19 +30,19 @@ func (b *Bucket) Find(id peer.ID) *list.Element { return nil } -func (b *Bucket) MoveToFront(e *list.Element) { +func (b *Bucket) moveToFront(e *list.Element) { b.lk.Lock() b.list.MoveToFront(e) b.lk.Unlock() } -func (b *Bucket) PushFront(p *peer.Peer) { +func (b *Bucket) pushFront(p *peer.Peer) { b.lk.Lock() b.list.PushFront(p) b.lk.Unlock() } -func (b *Bucket) PopBack() *peer.Peer { +func (b *Bucket) popBack() *peer.Peer { b.lk.Lock() defer b.lk.Unlock() last := b.list.Back() @@ -50,13 +50,13 @@ func (b *Bucket) PopBack() *peer.Peer { return last.Value.(*peer.Peer) } -func (b *Bucket) Len() int { +func (b *Bucket) len() int { b.lk.RLock() defer b.lk.RUnlock() return b.list.Len() } -// Splits a buckets peers into two buckets, the methods receiver will have +// Split splits a buckets peers into two buckets, the methods receiver will have // peers with CPL equal to cpl, the returned bucket will have peers with CPL // greater than cpl (returned bucket has closer peers) func (b *Bucket) Split(cpl int, target ID) *Bucket { @@ -64,13 +64,13 @@ func (b *Bucket) Split(cpl int, target ID) *Bucket { defer b.lk.Unlock() out := list.New() - newbuck := NewBucket() + newbuck := newBucket() newbuck.list = out e := b.list.Front() for e != nil { - peer_id := ConvertPeerID(e.Value.(*peer.Peer).ID) - peer_cpl := prefLen(peer_id, target) - if peer_cpl > cpl { + peerID := convertPeerID(e.Value.(*peer.Peer).ID) + peerCPL := prefLen(peerID, target) + if peerCPL > cpl { cur := e out.PushBack(e.Value) e = e.Next() diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index d0137c6d7..3bbd56d07 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -28,11 +28,11 @@ type RoutingTable struct { bucketsize int } -func NewRoutingTable(bucketsize int, local_id ID, latency time.Duration) *RoutingTable { +func newRoutingTable(bucketsize int, localID ID, latency time.Duration) *RoutingTable { rt := new(RoutingTable) - rt.Buckets = []*Bucket{NewBucket()} + rt.Buckets = []*Bucket{newBucket()} rt.bucketsize = bucketsize - rt.local = local_id + rt.local = localID rt.maxLatency = latency return rt } @@ -42,51 +42,50 @@ func NewRoutingTable(bucketsize int, local_id ID, latency time.Duration) *Routin func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer { rt.tabLock.Lock() defer rt.tabLock.Unlock() - peer_id := ConvertPeerID(p.ID) - cpl := xor(peer_id, rt.local).commonPrefixLen() + peerID := convertPeerID(p.ID) + cpl := xor(peerID, rt.local).commonPrefixLen() - b_id := cpl - if b_id >= len(rt.Buckets) { - b_id = len(rt.Buckets) - 1 + bucketID := cpl + if bucketID >= len(rt.Buckets) { + bucketID = len(rt.Buckets) - 1 } - bucket := rt.Buckets[b_id] - e := bucket.Find(p.ID) + bucket := rt.Buckets[bucketID] + e := bucket.find(p.ID) if e == nil { // New peer, add to bucket if p.GetLatency() > rt.maxLatency { // Connection doesnt meet requirements, skip! return nil } - bucket.PushFront(p) + bucket.pushFront(p) // Are we past the max bucket size? - if bucket.Len() > rt.bucketsize { - if b_id == len(rt.Buckets)-1 { - new_bucket := bucket.Split(b_id, rt.local) - rt.Buckets = append(rt.Buckets, new_bucket) - if new_bucket.Len() > rt.bucketsize { + if bucket.len() > rt.bucketsize { + if bucketID == len(rt.Buckets)-1 { + newBucket := bucket.Split(bucketID, rt.local) + rt.Buckets = append(rt.Buckets, newBucket) + if newBucket.len() > rt.bucketsize { // TODO: This is a very rare and annoying case panic("Case not handled.") } // If all elements were on left side of split... - if bucket.Len() > rt.bucketsize { - return bucket.PopBack() + if bucket.len() > rt.bucketsize { + return bucket.popBack() } } else { // If the bucket cant split kick out least active node - return bucket.PopBack() + return bucket.popBack() } } return nil - } else { - // If the peer is already in the table, move it to the front. - // This signifies that it it "more active" and the less active nodes - // Will as a result tend towards the back of the list - bucket.MoveToFront(e) - return nil } + // If the peer is already in the table, move it to the front. + // This signifies that it it "more active" and the less active nodes + // Will as a result tend towards the back of the list + bucket.moveToFront(e) + return nil } // A helper struct to sort peers by their distance to the local node @@ -101,7 +100,7 @@ type peerSorterArr []*peerDistance func (p peerSorterArr) Len() int { return len(p) } func (p peerSorterArr) Swap(a, b int) { p[a], p[b] = p[b], p[a] } func (p peerSorterArr) Less(a, b int) bool { - return p[a].distance.Less(p[b].distance) + return p[a].distance.less(p[b].distance) } // @@ -109,10 +108,10 @@ func (p peerSorterArr) Less(a, b int) bool { func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) peerSorterArr { for e := peerList.Front(); e != nil; e = e.Next() { p := e.Value.(*peer.Peer) - p_id := ConvertPeerID(p.ID) + pID := convertPeerID(p.ID) pd := peerDistance{ p: p, - distance: xor(target, p_id), + distance: xor(target, pID), } peerArr = append(peerArr, &pd) if e == nil { @@ -125,24 +124,23 @@ func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) pe // Find a specific peer by ID or return nil func (rt *RoutingTable) Find(id peer.ID) *peer.Peer { - srch := rt.NearestPeers(ConvertPeerID(id), 1) + srch := rt.NearestPeers(convertPeerID(id), 1) if len(srch) == 0 || !srch[0].ID.Equal(id) { return nil } return srch[0] } -// Returns a single peer that is nearest to the given ID +// NearestPeer returns a single peer that is nearest to the given ID func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer { peers := rt.NearestPeers(id, 1) if len(peers) > 0 { return peers[0] - } else { - return nil } + return nil } -// Returns a list of the 'count' closest peers to the given ID +// NearestPeers returns a list of the 'count' closest peers to the given ID func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { rt.tabLock.RLock() defer rt.tabLock.RUnlock() @@ -156,7 +154,7 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { bucket = rt.Buckets[cpl] var peerArr peerSorterArr - if bucket.Len() == 0 { + if bucket.len() == 0 { // In the case of an unusual split, one bucket may be empty. // if this happens, search both surrounding buckets for nearest peer if cpl > 0 { @@ -183,17 +181,17 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { return out } -// Returns the total number of peers in the routing table +// Size returns the total number of peers in the routing table func (rt *RoutingTable) Size() int { var tot int for _, buck := range rt.Buckets { - tot += buck.Len() + tot += buck.len() } return tot } // NOTE: This is potentially unsafe... use at your own risk -func (rt *RoutingTable) Listpeers() []*peer.Peer { +func (rt *RoutingTable) listPeers() []*peer.Peer { var peers []*peer.Peer for _, buck := range rt.Buckets { for e := buck.getIter(); e != nil; e = e.Next() { @@ -203,10 +201,10 @@ func (rt *RoutingTable) Listpeers() []*peer.Peer { return peers } -func (rt *RoutingTable) Print() { +func (rt *RoutingTable) print() { fmt.Printf("Routing Table, bs = %d, Max latency = %d\n", rt.bucketsize, rt.maxLatency) rt.tabLock.RLock() - peers := rt.Listpeers() + peers := rt.listPeers() for i, p := range peers { fmt.Printf("%d) %s %s\n", i, p.ID.Pretty(), p.GetLatency().String()) } diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 02d8f5e0e..ba5baee13 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -27,28 +27,28 @@ func _randID() ID { // Test basic features of the bucket struct func TestBucket(t *testing.T) { - b := NewBucket() + b := newBucket() peers := make([]*peer.Peer, 100) for i := 0; i < 100; i++ { peers[i] = _randPeer() - b.PushFront(peers[i]) + b.pushFront(peers[i]) } local := _randPeer() - local_id := ConvertPeerID(local.ID) + localID := convertPeerID(local.ID) i := rand.Intn(len(peers)) - e := b.Find(peers[i].ID) + e := b.find(peers[i].ID) if e == nil { t.Errorf("Failed to find peer: %v", peers[i]) } - spl := b.Split(0, ConvertPeerID(local.ID)) + spl := b.Split(0, convertPeerID(local.ID)) llist := b.list for e := llist.Front(); e != nil; e = e.Next() { - p := ConvertPeerID(e.Value.(*peer.Peer).ID) - cpl := xor(p, local_id).commonPrefixLen() + p := convertPeerID(e.Value.(*peer.Peer).ID) + cpl := xor(p, localID).commonPrefixLen() if cpl > 0 { t.Fatalf("Split failed. found id with cpl > 0 in 0 bucket") } @@ -56,8 +56,8 @@ func TestBucket(t *testing.T) { rlist := spl.list for e := rlist.Front(); e != nil; e = e.Next() { - p := ConvertPeerID(e.Value.(*peer.Peer).ID) - cpl := xor(p, local_id).commonPrefixLen() + p := convertPeerID(e.Value.(*peer.Peer).ID) + cpl := xor(p, localID).commonPrefixLen() if cpl == 0 { t.Fatalf("Split failed. found id with cpl == 0 in non 0 bucket") } @@ -67,7 +67,7 @@ func TestBucket(t *testing.T) { // Right now, this just makes sure that it doesnt hang or crash func TestTableUpdate(t *testing.T) { local := _randPeer() - rt := NewRoutingTable(10, ConvertPeerID(local.ID), time.Hour) + rt := newRoutingTable(10, convertPeerID(local.ID), time.Hour) peers := make([]*peer.Peer, 100) for i := 0; i < 100; i++ { @@ -93,7 +93,7 @@ func TestTableUpdate(t *testing.T) { func TestTableFind(t *testing.T) { local := _randPeer() - rt := NewRoutingTable(10, ConvertPeerID(local.ID), time.Hour) + rt := newRoutingTable(10, convertPeerID(local.ID), time.Hour) peers := make([]*peer.Peer, 100) for i := 0; i < 5; i++ { @@ -102,7 +102,7 @@ func TestTableFind(t *testing.T) { } t.Logf("Searching for peer: '%s'", peers[2].ID.Pretty()) - found := rt.NearestPeer(ConvertPeerID(peers[2].ID)) + found := rt.NearestPeer(convertPeerID(peers[2].ID)) if !found.ID.Equal(peers[2].ID) { t.Fatalf("Failed to lookup known node...") } @@ -110,7 +110,7 @@ func TestTableFind(t *testing.T) { func TestTableFindMultiple(t *testing.T) { local := _randPeer() - rt := NewRoutingTable(20, ConvertPeerID(local.ID), time.Hour) + rt := newRoutingTable(20, convertPeerID(local.ID), time.Hour) peers := make([]*peer.Peer, 100) for i := 0; i < 18; i++ { @@ -119,7 +119,7 @@ func TestTableFindMultiple(t *testing.T) { } t.Logf("Searching for peer: '%s'", peers[2].ID.Pretty()) - found := rt.NearestPeers(ConvertPeerID(peers[2].ID), 15) + found := rt.NearestPeers(convertPeerID(peers[2].ID), 15) if len(found) != 15 { t.Fatalf("Got back different number of peers than we expected.") } @@ -130,7 +130,7 @@ func TestTableFindMultiple(t *testing.T) { // and set GOMAXPROCS above 1 func TestTableMultithreaded(t *testing.T) { local := peer.ID("localPeer") - tab := NewRoutingTable(20, ConvertPeerID(local), time.Hour) + tab := newRoutingTable(20, convertPeerID(local), time.Hour) var peers []*peer.Peer for i := 0; i < 500; i++ { peers = append(peers, _randPeer()) @@ -167,8 +167,8 @@ func TestTableMultithreaded(t *testing.T) { func BenchmarkUpdates(b *testing.B) { b.StopTimer() - local := ConvertKey("localKey") - tab := NewRoutingTable(20, local, time.Hour) + local := convertKey("localKey") + tab := newRoutingTable(20, local, time.Hour) var peers []*peer.Peer for i := 0; i < b.N; i++ { @@ -183,8 +183,8 @@ func BenchmarkUpdates(b *testing.B) { func BenchmarkFinds(b *testing.B) { b.StopTimer() - local := ConvertKey("localKey") - tab := NewRoutingTable(20, local, time.Hour) + local := convertKey("localKey") + tab := newRoutingTable(20, local, time.Hour) var peers []*peer.Peer for i := 0; i < b.N; i++ { diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 32ff2c269..addd92565 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -20,11 +20,11 @@ var ErrLookupFailure = errors.New("failed to find any peer in table") // peer.ID or a util.Key. This unifies the keyspace type ID []byte -func (id ID) Equal(other ID) bool { +func (id ID) equal(other ID) bool { return bytes.Equal(id, other) } -func (id ID) Less(other ID) bool { +func (id ID) less(other ID) bool { a, b := equalizeSizes(id, other) for i := 0; i < len(a); i++ { if a[i] != b[i] { @@ -76,23 +76,23 @@ func equalizeSizes(a, b ID) (ID, ID) { return a, b } -func ConvertPeerID(id peer.ID) ID { +func convertPeerID(id peer.ID) ID { hash := sha256.Sum256(id) return hash[:] } -func ConvertKey(id u.Key) ID { +func convertKey(id u.Key) ID { hash := sha256.Sum256([]byte(id)) return hash[:] } -// Returns true if a is closer to key than b is +// Closer returns true if a is closer to key than b is func Closer(a, b peer.ID, key u.Key) bool { - aid := ConvertPeerID(a) - bid := ConvertPeerID(b) - tgt := ConvertKey(key) + aid := convertPeerID(a) + bid := convertPeerID(b) + tgt := convertKey(key) adist := xor(aid, tgt) bdist := xor(bid, tgt) - return adist.Less(bdist) + return adist.less(bdist) } diff --git a/routing/routing.go b/routing/routing.go index 3826f13cb..fdf350749 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -1,9 +1,10 @@ package routing import ( + "time" + peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" - "time" ) // IpfsRouting is the routing module interface From d0127a362242b443e008ece313954f76b57f4412 Mon Sep 17 00:00:00 2001 From: Chas Leichner Date: Sat, 16 Aug 2014 23:48:03 -0700 Subject: [PATCH 0061/3817] Made routing code pass golint. This commit was moved from ipfs/go-ipfs-routing@31356db8461d470697f90aaa163405d7e6a9fb21 --- routing/dht/dht_test.go | 4 ++-- routing/dht/diag.go | 2 +- routing/dht/ext_test.go | 8 ++++---- routing/kbucket/bucket.go | 2 +- routing/kbucket/table.go | 17 ++++++++++------- routing/kbucket/table_test.go | 28 ++++++++++++++-------------- routing/kbucket/util.go | 12 +++++++----- 7 files changed, 39 insertions(+), 34 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 581e19277..6296d1029 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -180,7 +180,7 @@ func TestProvides(t *testing.T) { t.Fatal(err) } - err = dhts[3].PutLocal(u.Key("hello"), []byte("world")) + err = dhts[3].putLocal(u.Key("hello"), []byte("world")) if err != nil { t.Fatal(err) } @@ -225,7 +225,7 @@ func TestLayeredGet(t *testing.T) { t.Fatal(err) } - err = dhts[3].PutLocal(u.Key("hello"), []byte("world")) + err = dhts[3].putLocal(u.Key("hello"), []byte("world")) if err != nil { t.Fatal(err) } diff --git a/routing/dht/diag.go b/routing/dht/diag.go index d6bc6bacf..8fd581a45 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -36,7 +36,7 @@ func (dht *IpfsDHT) getDiagInfo() *diagInfo { di.LifeSpan = time.Since(dht.birth) di.Keys = nil // Currently no way to query datastore - for _, p := range dht.routingTables[0].Listpeers() { + for _, p := range dht.routingTables[0].ListPeers() { di.Connections = append(di.Connections, connDiagInfo{p.GetLatency(), p.ID}) } return di diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 490c9f493..ee03c4850 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -105,9 +105,9 @@ func TestGetFailures(t *testing.T) { t.Fatal(err) } - resp := DHTMessage{ + resp := Message{ Type: pmes.GetType(), - Id: pmes.GetId(), + ID: pmes.GetId(), Response: true, Success: false, } @@ -140,10 +140,10 @@ func TestGetFailures(t *testing.T) { }) // Now we test this DHT's handleGetValue failure - req := DHTMessage{ + req := Message{ Type: PBDHTMessage_GET_VALUE, Key: "hello", - Id: GenerateMessageID(), + ID: GenerateMessageID(), Value: []byte{0}, } fn.Chan.Incoming <- swarm.NewMessage(other, req.ToProtobuf()) diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 77ed596db..a4eb91415 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -68,7 +68,7 @@ func (b *Bucket) Split(cpl int, target ID) *Bucket { newbuck.list = out e := b.list.Front() for e != nil { - peerID := convertPeerID(e.Value.(*peer.Peer).ID) + peerID := ConvertPeerID(e.Value.(*peer.Peer).ID) peerCPL := prefLen(peerID, target) if peerCPL > cpl { cur := e diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 3bbd56d07..5f1e5c870 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -28,7 +28,8 @@ type RoutingTable struct { bucketsize int } -func newRoutingTable(bucketsize int, localID ID, latency time.Duration) *RoutingTable { +// NewRoutingTable creates a new routing table with a given bucketsize, local ID, and latency tolerance. +func NewRoutingTable(bucketsize int, localID ID, latency time.Duration) *RoutingTable { rt := new(RoutingTable) rt.Buckets = []*Bucket{newBucket()} rt.bucketsize = bucketsize @@ -42,7 +43,7 @@ func newRoutingTable(bucketsize int, localID ID, latency time.Duration) *Routing func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer { rt.tabLock.Lock() defer rt.tabLock.Unlock() - peerID := convertPeerID(p.ID) + peerID := ConvertPeerID(p.ID) cpl := xor(peerID, rt.local).commonPrefixLen() bucketID := cpl @@ -108,7 +109,7 @@ func (p peerSorterArr) Less(a, b int) bool { func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) peerSorterArr { for e := peerList.Front(); e != nil; e = e.Next() { p := e.Value.(*peer.Peer) - pID := convertPeerID(p.ID) + pID := ConvertPeerID(p.ID) pd := peerDistance{ p: p, distance: xor(target, pID), @@ -124,7 +125,7 @@ func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) pe // Find a specific peer by ID or return nil func (rt *RoutingTable) Find(id peer.ID) *peer.Peer { - srch := rt.NearestPeers(convertPeerID(id), 1) + srch := rt.NearestPeers(ConvertPeerID(id), 1) if len(srch) == 0 || !srch[0].ID.Equal(id) { return nil } @@ -190,8 +191,9 @@ func (rt *RoutingTable) Size() int { return tot } +// ListPeers takes a RoutingTable and returns a list of all peers from all buckets in the table. // NOTE: This is potentially unsafe... use at your own risk -func (rt *RoutingTable) listPeers() []*peer.Peer { +func (rt *RoutingTable) ListPeers() []*peer.Peer { var peers []*peer.Peer for _, buck := range rt.Buckets { for e := buck.getIter(); e != nil; e = e.Next() { @@ -201,10 +203,11 @@ func (rt *RoutingTable) listPeers() []*peer.Peer { return peers } -func (rt *RoutingTable) print() { +// Print prints a descriptive statement about the provided RoutingTable +func (rt *RoutingTable) Print() { fmt.Printf("Routing Table, bs = %d, Max latency = %d\n", rt.bucketsize, rt.maxLatency) rt.tabLock.RLock() - peers := rt.listPeers() + peers := rt.ListPeers() for i, p := range peers { fmt.Printf("%d) %s %s\n", i, p.ID.Pretty(), p.GetLatency().String()) } diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index ba5baee13..13a55d14c 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -36,7 +36,7 @@ func TestBucket(t *testing.T) { } local := _randPeer() - localID := convertPeerID(local.ID) + localID := ConvertPeerID(local.ID) i := rand.Intn(len(peers)) e := b.find(peers[i].ID) @@ -44,10 +44,10 @@ func TestBucket(t *testing.T) { t.Errorf("Failed to find peer: %v", peers[i]) } - spl := b.Split(0, convertPeerID(local.ID)) + spl := b.Split(0, ConvertPeerID(local.ID)) llist := b.list for e := llist.Front(); e != nil; e = e.Next() { - p := convertPeerID(e.Value.(*peer.Peer).ID) + p := ConvertPeerID(e.Value.(*peer.Peer).ID) cpl := xor(p, localID).commonPrefixLen() if cpl > 0 { t.Fatalf("Split failed. found id with cpl > 0 in 0 bucket") @@ -56,7 +56,7 @@ func TestBucket(t *testing.T) { rlist := spl.list for e := rlist.Front(); e != nil; e = e.Next() { - p := convertPeerID(e.Value.(*peer.Peer).ID) + p := ConvertPeerID(e.Value.(*peer.Peer).ID) cpl := xor(p, localID).commonPrefixLen() if cpl == 0 { t.Fatalf("Split failed. found id with cpl == 0 in non 0 bucket") @@ -67,7 +67,7 @@ func TestBucket(t *testing.T) { // Right now, this just makes sure that it doesnt hang or crash func TestTableUpdate(t *testing.T) { local := _randPeer() - rt := newRoutingTable(10, convertPeerID(local.ID), time.Hour) + rt := NewRoutingTable(10, ConvertPeerID(local.ID), time.Hour) peers := make([]*peer.Peer, 100) for i := 0; i < 100; i++ { @@ -93,7 +93,7 @@ func TestTableUpdate(t *testing.T) { func TestTableFind(t *testing.T) { local := _randPeer() - rt := newRoutingTable(10, convertPeerID(local.ID), time.Hour) + rt := NewRoutingTable(10, ConvertPeerID(local.ID), time.Hour) peers := make([]*peer.Peer, 100) for i := 0; i < 5; i++ { @@ -102,7 +102,7 @@ func TestTableFind(t *testing.T) { } t.Logf("Searching for peer: '%s'", peers[2].ID.Pretty()) - found := rt.NearestPeer(convertPeerID(peers[2].ID)) + found := rt.NearestPeer(ConvertPeerID(peers[2].ID)) if !found.ID.Equal(peers[2].ID) { t.Fatalf("Failed to lookup known node...") } @@ -110,7 +110,7 @@ func TestTableFind(t *testing.T) { func TestTableFindMultiple(t *testing.T) { local := _randPeer() - rt := newRoutingTable(20, convertPeerID(local.ID), time.Hour) + rt := NewRoutingTable(20, ConvertPeerID(local.ID), time.Hour) peers := make([]*peer.Peer, 100) for i := 0; i < 18; i++ { @@ -119,7 +119,7 @@ func TestTableFindMultiple(t *testing.T) { } t.Logf("Searching for peer: '%s'", peers[2].ID.Pretty()) - found := rt.NearestPeers(convertPeerID(peers[2].ID), 15) + found := rt.NearestPeers(ConvertPeerID(peers[2].ID), 15) if len(found) != 15 { t.Fatalf("Got back different number of peers than we expected.") } @@ -130,7 +130,7 @@ func TestTableFindMultiple(t *testing.T) { // and set GOMAXPROCS above 1 func TestTableMultithreaded(t *testing.T) { local := peer.ID("localPeer") - tab := newRoutingTable(20, convertPeerID(local), time.Hour) + tab := NewRoutingTable(20, ConvertPeerID(local), time.Hour) var peers []*peer.Peer for i := 0; i < 500; i++ { peers = append(peers, _randPeer()) @@ -167,8 +167,8 @@ func TestTableMultithreaded(t *testing.T) { func BenchmarkUpdates(b *testing.B) { b.StopTimer() - local := convertKey("localKey") - tab := newRoutingTable(20, local, time.Hour) + local := ConvertKey("localKey") + tab := NewRoutingTable(20, local, time.Hour) var peers []*peer.Peer for i := 0; i < b.N; i++ { @@ -183,8 +183,8 @@ func BenchmarkUpdates(b *testing.B) { func BenchmarkFinds(b *testing.B) { b.StopTimer() - local := convertKey("localKey") - tab := newRoutingTable(20, local, time.Hour) + local := ConvertKey("localKey") + tab := NewRoutingTable(20, local, time.Hour) var peers []*peer.Peer for i := 0; i < b.N; i++ { diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index addd92565..1195022b0 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -76,21 +76,23 @@ func equalizeSizes(a, b ID) (ID, ID) { return a, b } -func convertPeerID(id peer.ID) ID { +// ConvertPeerID creates a DHT ID by hashing a Peer ID (Multihash) +func ConvertPeerID(id peer.ID) ID { hash := sha256.Sum256(id) return hash[:] } -func convertKey(id u.Key) ID { +// ConvertKey creates a DHT ID by hashing a local key (String) +func ConvertKey(id u.Key) ID { hash := sha256.Sum256([]byte(id)) return hash[:] } // Closer returns true if a is closer to key than b is func Closer(a, b peer.ID, key u.Key) bool { - aid := convertPeerID(a) - bid := convertPeerID(b) - tgt := convertKey(key) + aid := ConvertPeerID(a) + bid := ConvertPeerID(b) + tgt := ConvertKey(key) adist := xor(aid, tgt) bdist := xor(bid, tgt) From cc547ceaccc101715d193abb30fffab73f50fb6b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 17 Aug 2014 20:17:43 -0700 Subject: [PATCH 0062/3817] fix a few race conditions and add in newlines to print statements This commit was moved from ipfs/go-ipfs-routing@c3aa90580f0e8fc580321904ddfbe4dfbbc12b7a --- routing/dht/dht.go | 45 ++++++++++++++++++++++++------------------ routing/dht/routing.go | 11 +++++------ 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 901f1a861..e548a272d 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -34,6 +34,7 @@ type IpfsDHT struct { // Local data datastore ds.Datastore + dslock sync.Mutex // Map keys to peers that can provide their value providers map[u.Key][]*providerInfo @@ -78,7 +79,7 @@ func (dht *IpfsDHT) Start() { // Connect to a new peer at the given address, ping and add to the routing table func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { maddrstr, _ := addr.String() - u.DOut("Connect to new peer: %s", maddrstr) + u.DOut("Connect to new peer: %s\n", maddrstr) npeer, err := dht.network.ConnectNew(addr) if err != nil { return nil, err @@ -113,7 +114,7 @@ func (dht *IpfsDHT) handleMessages() { pmes := new(PBDHTMessage) err := proto.Unmarshal(mes.Data, pmes) if err != nil { - u.PErr("Failed to decode protobuf message: %s", err) + u.PErr("Failed to decode protobuf message: %s\n", err) continue } @@ -126,7 +127,7 @@ func (dht *IpfsDHT) handleMessages() { } // - u.DOut("[peer: %s]\nGot message type: '%s' [id = %x, from = %s]", + u.DOut("[peer: %s]\nGot message type: '%s' [id = %x, from = %s]\n", dht.self.ID.Pretty(), PBDHTMessage_MessageType_name[int32(pmes.GetType())], pmes.GetId(), mes.Peer.ID.Pretty()) @@ -148,7 +149,7 @@ func (dht *IpfsDHT) handleMessages() { } case err := <-ch.Errors: - u.PErr("dht err: %s", err) + u.PErr("dht err: %s\n", err) case <-dht.shutdown: checkTimeouts.Stop() return @@ -187,7 +188,7 @@ func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) er } func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { - u.DOut("handleGetValue for key: %s", pmes.GetKey()) + u.DOut("handleGetValue for key: %s\n", pmes.GetKey()) dskey := ds.NewKey(pmes.GetKey()) resp := &Message{ Response: true, @@ -201,9 +202,11 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { resp.Value = iVal.([]byte) } else if err == ds.ErrNotFound { // Check if we know any providers for the requested value + dht.providerLock.RLock() provs, ok := dht.providers[u.Key(pmes.GetKey())] + dht.providerLock.RUnlock() if ok && len(provs) > 0 { - u.DOut("handleGetValue returning %d provider[s]", len(provs)) + u.DOut("handleGetValue returning %d provider[s]\n", len(provs)) for _, prov := range provs { resp.Peers = append(resp.Peers, prov.Value) } @@ -219,7 +222,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { } else { level = int(pmes.GetValue()[0]) // Using value field to specify cluster level } - u.DOut("handleGetValue searching level %d clusters", level) + u.DOut("handleGetValue searching level %d clusters\n", level) closer := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) @@ -233,7 +236,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { resp.Peers = nil u.DOut("handleGetValue could not find a closer node than myself.") } else { - u.DOut("handleGetValue returning a closer peer: '%s'", closer.ID.Pretty()) + u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty()) resp.Peers = []*peer.Peer{closer} } } @@ -249,6 +252,8 @@ out: // Store a value in this peer local storage func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *PBDHTMessage) { + dht.dslock.Lock() + defer dht.dslock.Unlock() dskey := ds.NewKey(pmes.GetKey()) err := dht.datastore.Put(dskey, pmes.GetValue()) if err != nil { @@ -278,7 +283,7 @@ func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) { dht.network.Send(mes) }() level := pmes.GetValue()[0] - u.DOut("handleFindPeer: searching for '%s'", peer.ID(pmes.GetKey()).Pretty()) + u.DOut("handleFindPeer: searching for '%s'\n", peer.ID(pmes.GetKey()).Pretty()) closest := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) if closest == nil { u.PErr("handleFindPeer: could not find anything.") @@ -295,7 +300,7 @@ func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) { return } - u.DOut("handleFindPeer: sending back '%s'", closest.ID.Pretty()) + u.DOut("handleFindPeer: sending back '%s'\n", closest.ID.Pretty()) resp.Peers = []*peer.Peer{closest} resp.Success = true } @@ -352,7 +357,7 @@ func (dht *IpfsDHT) Halt() { } func (dht *IpfsDHT) addProviderEntry(key u.Key, p *peer.Peer) { - u.DOut("Adding %s as provider for '%s'", p.Key().Pretty(), key) + u.DOut("Adding %s as provider for '%s'\n", p.Key().Pretty(), key) dht.providerLock.Lock() provs := dht.providers[key] dht.providers[key] = append(provs, &providerInfo{time.Now(), p}) @@ -432,13 +437,13 @@ func (dht *IpfsDHT) getValueOrPeers(p *peer.Peer, key u.Key, timeout time.Durati } addr, err := ma.NewMultiaddr(pb.GetAddr()) if err != nil { - u.PErr(err.Error()) + u.PErr("%v\n", err.Error()) continue } np, err := dht.network.GetConnection(peer.ID(pb.GetId()), addr) if err != nil { - u.PErr(err.Error()) + u.PErr("%v\n", err.Error()) continue } @@ -494,19 +499,19 @@ func (dht *IpfsDHT) getFromPeerList(key u.Key, timeout time.Duration, if p == nil { maddr, err := ma.NewMultiaddr(pinfo.GetAddr()) if err != nil { - u.PErr("getValue error: %s", err) + u.PErr("getValue error: %s\n", err) continue } p, err = dht.network.GetConnection(peer.ID(pinfo.GetId()), maddr) if err != nil { - u.PErr("getValue error: %s", err) + u.PErr("getValue error: %s\n", err) continue } } pmes, err := dht.getValueSingle(p, key, timeout, level) if err != nil { - u.DErr("getFromPeers error: %s", err) + u.DErr("getFromPeers error: %s\n", err) continue } dht.addProviderEntry(key, p) @@ -520,6 +525,8 @@ func (dht *IpfsDHT) getFromPeerList(key u.Key, timeout time.Duration, } func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { + dht.dslock.Lock() + defer dht.dslock.Unlock() v, err := dht.datastore.Get(ds.NewKey(string(key))) if err != nil { return nil, err @@ -637,15 +644,15 @@ func (dht *IpfsDHT) addPeerList(key u.Key, peers []*PBDHTMessage_PBPeer) []*peer // Dont add someone who is already on the list p := dht.network.Find(u.Key(prov.GetId())) if p == nil { - u.DOut("given provider %s was not in our network already.", peer.ID(prov.GetId()).Pretty()) + u.DOut("given provider %s was not in our network already.\n", peer.ID(prov.GetId()).Pretty()) maddr, err := ma.NewMultiaddr(prov.GetAddr()) if err != nil { - u.PErr("error connecting to new peer: %s", err) + u.PErr("error connecting to new peer: %s\n", err) continue } p, err = dht.network.GetConnection(peer.ID(prov.GetId()), maddr) if err != nil { - u.PErr("error connecting to new peer: %s", err) + u.PErr("error connecting to new peer: %s\n", err) continue } } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 4e91e0eb4..e3d34325d 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -30,8 +30,7 @@ var AlphaValue = 3 // GenerateMessageID creates and returns a new message ID // TODO: determine a way of creating and managing message IDs func GenerateMessageID() uint64 { - //return (uint64(rand.Uint32()) << 32) & uint64(rand.Uint32()) - return uint64(rand.Uint32()) + return (uint64(rand.Uint32()) << 32) | uint64(rand.Uint32()) } // This file implements the Routing interface for the IpfsDHT struct. @@ -188,7 +187,7 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { } val, peers, err := dht.getValueOrPeers(p, key, timeout/4, routeLevel) if err != nil { - u.DErr(err.Error()) + u.DErr("%v\n", err.Error()) c.Decrement() continue } @@ -254,7 +253,7 @@ func (dht *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Pee ll.EndLog() ll.Print() }() - u.DOut("Find providers for: '%s'", key) + u.DOut("Find providers for: '%s'\n", key) p := dht.routingTables[0].NearestPeer(kb.ConvertKey(key)) if p == nil { return nil, kb.ErrLookupFailure @@ -288,7 +287,7 @@ func (dht *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Pee np, err := dht.network.GetConnection(peer.ID(closer[0].GetId()), maddr) if err != nil { - u.PErr("[%s] Failed to connect to: %s", dht.self.ID.Pretty(), closer[0].GetAddr()) + u.PErr("[%s] Failed to connect to: %s\n", dht.self.ID.Pretty(), closer[0].GetAddr()) level++ continue } @@ -361,7 +360,7 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { case <-responseChan: roundtrip := time.Since(before) p.SetLatency(roundtrip) - u.DOut("Ping took %s.", roundtrip.String()) + u.DOut("Ping took %s.\n", roundtrip.String()) return nil case <-tout: // Timed out, think about removing peer from network From 02183a984bebd093db86862c8acebb165bec91df Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 18 Aug 2014 20:38:44 -0700 Subject: [PATCH 0063/3817] change providers map and lock over to an agent based approach for managing providers This commit was moved from ipfs/go-ipfs-routing@1d0d5d6361864b0a9020f1b91d7345989af05a76 --- routing/dht/dht.go | 60 ++++++----------------------- routing/dht/providers.go | 83 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 48 deletions(-) create mode 100644 routing/dht/providers.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index e548a272d..4c6da064b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -36,9 +36,7 @@ type IpfsDHT struct { datastore ds.Datastore dslock sync.Mutex - // Map keys to peers that can provide their value - providers map[u.Key][]*providerInfo - providerLock sync.RWMutex + providers *ProviderManager // Signal to shutdown dht shutdown chan struct{} @@ -59,7 +57,7 @@ func NewDHT(p *peer.Peer, net swarm.Network) *IpfsDHT { dht.network = net dht.datastore = ds.NewMapDatastore() dht.self = p - dht.providers = make(map[u.Key][]*providerInfo) + dht.providers = NewProviderManager() dht.shutdown = make(chan struct{}) dht.routingTables = make([]*kb.RoutingTable, 3) @@ -102,7 +100,6 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { func (dht *IpfsDHT) handleMessages() { u.DOut("Begin message handling routine") - checkTimeouts := time.NewTicker(time.Minute * 5) ch := dht.network.GetChan() for { select { @@ -146,34 +143,18 @@ func (dht *IpfsDHT) handleMessages() { dht.handlePing(mes.Peer, pmes) case PBDHTMessage_DIAGNOSTIC: dht.handleDiagnostic(mes.Peer, pmes) + default: + u.PErr("Recieved invalid message type") } case err := <-ch.Errors: u.PErr("dht err: %s\n", err) case <-dht.shutdown: - checkTimeouts.Stop() return - case <-checkTimeouts.C: - // Time to collect some garbage! - dht.cleanExpiredProviders() } } } -func (dht *IpfsDHT) cleanExpiredProviders() { - dht.providerLock.Lock() - for k, parr := range dht.providers { - var cleaned []*providerInfo - for _, v := range parr { - if time.Since(v.Creation) < time.Hour { - cleaned = append(cleaned, v) - } - } - dht.providers[k] = cleaned - } - dht.providerLock.Unlock() -} - func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) error { pmes := Message{ Type: PBDHTMessage_PUT_VALUE, @@ -202,14 +183,10 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { resp.Value = iVal.([]byte) } else if err == ds.ErrNotFound { // Check if we know any providers for the requested value - dht.providerLock.RLock() - provs, ok := dht.providers[u.Key(pmes.GetKey())] - dht.providerLock.RUnlock() - if ok && len(provs) > 0 { + provs := dht.providers.GetProviders(u.Key(pmes.GetKey())) + if len(provs) > 0 { u.DOut("handleGetValue returning %d provider[s]\n", len(provs)) - for _, prov := range provs { - resp.Peers = append(resp.Peers, prov.Value) - } + resp.Peers = provs resp.Success = true } else { // No providers? @@ -313,9 +290,7 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *PBDHTMessage) { Response: true, } - dht.providerLock.RLock() - providers := dht.providers[u.Key(pmes.GetKey())] - dht.providerLock.RUnlock() + providers := dht.providers.GetProviders(u.Key(pmes.GetKey())) if providers == nil || len(providers) == 0 { level := 0 if len(pmes.GetValue()) > 0 { @@ -329,9 +304,7 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *PBDHTMessage) { resp.Peers = []*peer.Peer{closer} } } else { - for _, prov := range providers { - resp.Peers = append(resp.Peers, prov.Value) - } + resp.Peers = providers resp.Success = true } @@ -345,9 +318,8 @@ type providerInfo struct { } func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *PBDHTMessage) { - //TODO: need to implement TTLs on providers key := u.Key(pmes.GetKey()) - dht.addProviderEntry(key, p) + dht.providers.AddProvider(key, p) } // Halt stops all communications from this peer and shut down @@ -356,14 +328,6 @@ func (dht *IpfsDHT) Halt() { dht.network.Close() } -func (dht *IpfsDHT) addProviderEntry(key u.Key, p *peer.Peer) { - u.DOut("Adding %s as provider for '%s'\n", p.Key().Pretty(), key) - dht.providerLock.Lock() - provs := dht.providers[key] - dht.providers[key] = append(provs, &providerInfo{time.Now(), p}) - dht.providerLock.Unlock() -} - // NOTE: not yet finished, low priority func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { seq := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) @@ -514,7 +478,7 @@ func (dht *IpfsDHT) getFromPeerList(key u.Key, timeout time.Duration, u.DErr("getFromPeers error: %s\n", err) continue } - dht.addProviderEntry(key, p) + dht.providers.AddProvider(key, p) // Make sure it was a successful get if pmes.GetSuccess() && pmes.Value != nil { @@ -656,7 +620,7 @@ func (dht *IpfsDHT) addPeerList(key u.Key, peers []*PBDHTMessage_PBPeer) []*peer continue } } - dht.addProviderEntry(key, p) + dht.providers.AddProvider(key, p) provArr = append(provArr, p) } return provArr diff --git a/routing/dht/providers.go b/routing/dht/providers.go new file mode 100644 index 000000000..3dc3b7b05 --- /dev/null +++ b/routing/dht/providers.go @@ -0,0 +1,83 @@ +package dht + +import ( + "time" + + u "github.com/jbenet/go-ipfs/util" + peer "github.com/jbenet/go-ipfs/peer" +) + +type ProviderManager struct { + providers map[u.Key][]*providerInfo + newprovs chan *addProv + getprovs chan *getProv + halt chan struct{} +} + +type addProv struct { + k u.Key + val *peer.Peer +} + +type getProv struct { + k u.Key + resp chan []*peer.Peer +} + +func NewProviderManager() *ProviderManager { + pm := new(ProviderManager) + pm.getprovs = make(chan *getProv) + pm.newprovs = make(chan *addProv) + pm.providers = make(map[u.Key][]*providerInfo) + pm.halt = make(chan struct{}) + go pm.run() + return pm +} + +func (pm *ProviderManager) run() { + tick := time.NewTicker(time.Hour) + for { + select { + case np := <-pm.newprovs: + pi := new(providerInfo) + pi.Creation = time.Now() + pi.Value = np.val + arr := pm.providers[np.k] + pm.providers[np.k] = append(arr, pi) + case gp := <-pm.getprovs: + var parr []*peer.Peer + provs := pm.providers[gp.k] + for _, p := range provs { + parr = append(parr, p.Value) + } + gp.resp <- parr + case <-tick.C: + for k, provs := range pm.providers { + var filtered []*providerInfo + for _, p := range provs { + if time.Now().Sub(p.Creation) < time.Hour * 24 { + filtered = append(filtered, p) + } + } + pm.providers[k] = filtered + } + case <-pm.halt: + return + } + } +} + +func (pm *ProviderManager) AddProvider(k u.Key, val *peer.Peer) { + pm.newprovs <- &addProv{ + k: k, + val: val, + } +} + +func (pm *ProviderManager) GetProviders(k u.Key) []*peer.Peer { + gp := new(getProv) + gp.k = k + gp.resp = make(chan []*peer.Peer) + pm.getprovs <- gp + return <-gp.resp +} From 9fac1607a17adf2774f4ae3e19db970d2988b4d4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 19 Aug 2014 19:14:52 -0700 Subject: [PATCH 0064/3817] add some more tests in This commit was moved from ipfs/go-ipfs-routing@c945c1b5ce7d212766947b5e26ec010c2078dd3a --- routing/dht/Message.go | 15 ++++--- routing/dht/dht.go | 22 ++++----- routing/dht/ext_test.go | 76 ++++++++++++++++++++++++++++++++ routing/dht/mes_listener_test.go | 33 ++++++++++++++ 4 files changed, 130 insertions(+), 16 deletions(-) create mode 100644 routing/dht/mes_listener_test.go diff --git a/routing/dht/Message.go b/routing/dht/Message.go index 71a9537f9..20c311d80 100644 --- a/routing/dht/Message.go +++ b/routing/dht/Message.go @@ -1,6 +1,7 @@ package dht import ( + "code.google.com/p/goprotobuf/proto" peer "github.com/jbenet/go-ipfs/peer" ) @@ -17,12 +18,16 @@ type Message struct { func peerInfo(p *peer.Peer) *PBDHTMessage_PBPeer { pbp := new(PBDHTMessage_PBPeer) - addr, err := p.Addresses[0].String() - if err != nil { - //Temp: what situations could cause this? - panic(err) + if len(p.Addresses) == 0 || p.Addresses[0] == nil { + pbp.Addr = proto.String("") + } else { + addr, err := p.Addresses[0].String() + if err != nil { + //Temp: what situations could cause this? + panic(err) + } + pbp.Addr = &addr } - pbp.Addr = &addr pid := string(p.ID) pbp.Id = &pid return pbp diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 4c6da064b..146de751b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -87,7 +87,7 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { // NOTE: this should be done better... err = dht.Ping(npeer, time.Second*2) if err != nil { - return nil, errors.New("failed to ping newly connected peer") + return nil, errors.New("failed to ping newly connected peer\n") } dht.Update(npeer) @@ -98,14 +98,14 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { // Read in all messages from swarm and handle them appropriately // NOTE: this function is just a quick sketch func (dht *IpfsDHT) handleMessages() { - u.DOut("Begin message handling routine") + u.DOut("Begin message handling routine\n") ch := dht.network.GetChan() for { select { case mes, ok := <-ch.Incoming: if !ok { - u.DOut("handleMessages closing, bad recv on incoming") + u.DOut("handleMessages closing, bad recv on incoming\n") return } pmes := new(PBDHTMessage) @@ -178,7 +178,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { } iVal, err := dht.datastore.Get(dskey) if err == nil { - u.DOut("handleGetValue success!") + u.DOut("handleGetValue success!\n") resp.Success = true resp.Value = iVal.([]byte) } else if err == ds.ErrNotFound { @@ -195,7 +195,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { level := 0 if len(pmes.GetValue()) < 1 { // TODO: maybe return an error? Defaulting isnt a good idea IMO - u.PErr("handleGetValue: no routing level specified, assuming 0") + u.PErr("handleGetValue: no routing level specified, assuming 0\n") } else { level = int(pmes.GetValue()[0]) // Using value field to specify cluster level } @@ -204,14 +204,14 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { closer := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) if closer.ID.Equal(dht.self.ID) { - u.DOut("Attempted to return self! this shouldnt happen...") + u.DOut("Attempted to return self! this shouldnt happen...\n") resp.Peers = nil goto out } // If this peer is closer than the one from the table, return nil if kb.Closer(dht.self.ID, closer.ID, u.Key(pmes.GetKey())) { resp.Peers = nil - u.DOut("handleGetValue could not find a closer node than myself.") + u.DOut("handleGetValue could not find a closer node than myself.\n") } else { u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty()) resp.Peers = []*peer.Peer{closer} @@ -263,12 +263,12 @@ func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) { u.DOut("handleFindPeer: searching for '%s'\n", peer.ID(pmes.GetKey()).Pretty()) closest := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) if closest == nil { - u.PErr("handleFindPeer: could not find anything.") + u.PErr("handleFindPeer: could not find anything.\n") return } if len(closest.Addresses) == 0 { - u.PErr("handleFindPeer: no addresses for connected peer...") + u.PErr("handleFindPeer: no addresses for connected peer...\n") return } @@ -438,7 +438,7 @@ func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duratio return nil, u.ErrTimeout case resp, ok := <-responseChan: if !ok { - u.PErr("response channel closed before timeout, please investigate.") + u.PErr("response channel closed before timeout, please investigate.\n") return nil, u.ErrTimeout } roundtrip := time.Since(t) @@ -587,7 +587,7 @@ func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, time dht.listener.Unlisten(pmes.ID) return nil, u.ErrTimeout case resp := <-listenChan: - u.DOut("FindProviders: got response.") + u.DOut("FindProviders: got response.\n") pmesOut := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index ee03c4850..1ef68fbec 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -3,6 +3,8 @@ package dht import ( "testing" + crand "crypto/rand" + "code.google.com/p/goprotobuf/proto" peer "github.com/jbenet/go-ipfs/peer" @@ -72,6 +74,10 @@ func (f *fauxNet) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { return nil, nil } +func (f *fauxNet) GetConnection(id peer.ID, addr *ma.Multiaddr) (*peer.Peer, error) { + return &peer.Peer{ID: id, Addresses: []*ma.Multiaddr{addr}}, nil +} + func TestGetFailures(t *testing.T) { fn := newFauxNet() fn.Listen() @@ -150,3 +156,73 @@ func TestGetFailures(t *testing.T) { <-success } + +// TODO: Maybe put these in some sort of "ipfs_testutil" package +func _randPeer() *peer.Peer { + p := new(peer.Peer) + p.ID = make(peer.ID, 16) + p.Addresses = []*ma.Multiaddr{nil} + crand.Read(p.ID) + return p +} + +func TestNotFound(t *testing.T) { + u.Debug = true + fn := newFauxNet() + fn.Listen() + + local := new(peer.Peer) + local.ID = peer.ID("test_peer") + + d := NewDHT(local, fn) + d.Start() + + var ps []*peer.Peer + for i := 0; i < 5; i++ { + ps = append(ps, _randPeer()) + d.Update(ps[i]) + } + + // Reply with random peers to every message + fn.AddHandler(func(mes *swarm.Message) *swarm.Message { + t.Log("Handling message...") + pmes := new(PBDHTMessage) + err := proto.Unmarshal(mes.Data, pmes) + if err != nil { + t.Fatal(err) + } + + switch pmes.GetType() { + case PBDHTMessage_GET_VALUE: + resp := Message{ + Type: pmes.GetType(), + ID: pmes.GetId(), + Response: true, + Success: false, + } + + for i := 0; i < 7; i++ { + resp.Peers = append(resp.Peers, _randPeer()) + } + return swarm.NewMessage(mes.Peer, resp.ToProtobuf()) + default: + panic("Shouldnt recieve this.") + } + + }) + + _, err := d.GetValue(u.Key("hello"), time.Second*30) + if err != nil { + switch err { + case u.ErrNotFound: + t.Fail() + //Success! + return + case u.ErrTimeout: + t.Fatal("Should not have gotten timeout!") + default: + t.Fatalf("Got unexpected error: %s", err) + } + } + t.Fatal("Expected to recieve an error.") +} diff --git a/routing/dht/mes_listener_test.go b/routing/dht/mes_listener_test.go new file mode 100644 index 000000000..8e494aabc --- /dev/null +++ b/routing/dht/mes_listener_test.go @@ -0,0 +1,33 @@ +package dht + +import ( + "testing" + "time" + + "github.com/jbenet/go-ipfs/peer" + "github.com/jbenet/go-ipfs/swarm" +) + +// Ensure that the Message Listeners basic functionality works +func TestMesListenerBasic(t *testing.T) { + ml := newMesListener() + a := GenerateMessageID() + resp := ml.Listen(a, 1, time.Minute) + + pmes := new(swarm.PBWrapper) + pmes.Message = []byte("Hello") + pmes.Type = new(swarm.PBWrapper_MessageType) + mes := swarm.NewMessage(new(peer.Peer), pmes) + + go ml.Respond(a, mes) + + del := time.After(time.Millisecond * 10) + select { + case get := <-resp: + if string(get.Data) != string(mes.Data) { + t.Fatal("Something got really messed up") + } + case <-del: + t.Fatal("Waiting on message response timed out.") + } +} From fd2365be7d2e71765b5621ca98abd0b02cd9deb5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 19 Aug 2014 20:02:42 -0700 Subject: [PATCH 0065/3817] removed failure call i forgot to remove This commit was moved from ipfs/go-ipfs-routing@f544b4e454742a1c3d04976f42d8a21540637412 --- routing/dht/ext_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 1ef68fbec..8d1e74ba5 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -215,7 +215,6 @@ func TestNotFound(t *testing.T) { if err != nil { switch err { case u.ErrNotFound: - t.Fail() //Success! return case u.ErrTimeout: From 09bff5085e0253f65ce11ea8fa64a8cc0642d947 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 19 Aug 2014 22:05:49 -0700 Subject: [PATCH 0066/3817] add in message type routing to the swarm object. tired, needs cleanup. This commit was moved from ipfs/go-ipfs-routing@d1a94612fd301828be7c0e8a14f73f30b12aeed1 --- routing/dht/dht.go | 7 ++-- routing/dht/dht_test.go | 4 +-- routing/dht/ext_test.go | 70 ++++++++++++++++++++++++++++++++++++++-- routing/dht/providers.go | 16 ++++----- routing/dht/routing.go | 51 ++++++++++++++--------------- 5 files changed, 106 insertions(+), 42 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 146de751b..b0a2a0481 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -100,10 +100,11 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { func (dht *IpfsDHT) handleMessages() { u.DOut("Begin message handling routine\n") - ch := dht.network.GetChan() + errs := dht.network.GetErrChan() + dhtmes := dht.network.GetChannel(swarm.PBWrapper_DHT_MESSAGE) for { select { - case mes, ok := <-ch.Incoming: + case mes, ok := <-dhtmes: if !ok { u.DOut("handleMessages closing, bad recv on incoming\n") return @@ -147,7 +148,7 @@ func (dht *IpfsDHT) handleMessages() { u.PErr("Recieved invalid message type") } - case err := <-ch.Errors: + case err := <-errs: u.PErr("dht err: %s\n", err) case <-dht.shutdown: return diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 6296d1029..92d0931fb 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -132,8 +132,8 @@ func TestValueGetSet(t *testing.T) { dhtA.Start() dhtB.Start() - errsa := dhtA.network.GetChan().Errors - errsb := dhtB.network.GetChan().Errors + errsa := dhtA.network.GetErrChan() + errsb := dhtB.network.GetErrChan() go func() { select { case err := <-errsa: diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 8d1e74ba5..79cfd27bc 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -66,8 +66,12 @@ func (f *fauxNet) Send(mes *swarm.Message) { f.Chan.Outgoing <- mes } -func (f *fauxNet) GetChan() *swarm.Chan { - return f.Chan +func (f *fauxNet) GetErrChan() chan error { + return f.Chan.Errors +} + +func (f *fauxNet) GetChannel(t swarm.PBWrapper_MessageType) chan *swarm.Message { + return f.Chan.Incoming } func (f *fauxNet) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { @@ -167,7 +171,6 @@ func _randPeer() *peer.Peer { } func TestNotFound(t *testing.T) { - u.Debug = true fn := newFauxNet() fn.Listen() @@ -225,3 +228,64 @@ func TestNotFound(t *testing.T) { } t.Fatal("Expected to recieve an error.") } + +// If less than K nodes are in the entire network, it should fail when we make +// a GET rpc and nobody has the value +func TestLessThanKResponses(t *testing.T) { + u.Debug = false + fn := newFauxNet() + fn.Listen() + + local := new(peer.Peer) + local.ID = peer.ID("test_peer") + + d := NewDHT(local, fn) + d.Start() + + var ps []*peer.Peer + for i := 0; i < 5; i++ { + ps = append(ps, _randPeer()) + d.Update(ps[i]) + } + other := _randPeer() + + // Reply with random peers to every message + fn.AddHandler(func(mes *swarm.Message) *swarm.Message { + t.Log("Handling message...") + pmes := new(PBDHTMessage) + err := proto.Unmarshal(mes.Data, pmes) + if err != nil { + t.Fatal(err) + } + + switch pmes.GetType() { + case PBDHTMessage_GET_VALUE: + resp := Message{ + Type: pmes.GetType(), + ID: pmes.GetId(), + Response: true, + Success: false, + Peers: []*peer.Peer{other}, + } + + return swarm.NewMessage(mes.Peer, resp.ToProtobuf()) + default: + panic("Shouldnt recieve this.") + } + + }) + + _, err := d.GetValue(u.Key("hello"), time.Second*30) + if err != nil { + switch err { + case u.ErrNotFound: + //Success! + return + case u.ErrTimeout: + t.Fatal("Should not have gotten timeout!") + default: + t.Fatalf("Got unexpected error: %s", err) + } + } + t.Fatal("Expected to recieve an error.") +} diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 3dc3b7b05..fdf8d6581 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -3,24 +3,24 @@ package dht import ( "time" - u "github.com/jbenet/go-ipfs/util" peer "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" ) type ProviderManager struct { providers map[u.Key][]*providerInfo - newprovs chan *addProv - getprovs chan *getProv - halt chan struct{} + newprovs chan *addProv + getprovs chan *getProv + halt chan struct{} } type addProv struct { - k u.Key + k u.Key val *peer.Peer } type getProv struct { - k u.Key + k u.Key resp chan []*peer.Peer } @@ -55,7 +55,7 @@ func (pm *ProviderManager) run() { for k, provs := range pm.providers { var filtered []*providerInfo for _, p := range provs { - if time.Now().Sub(p.Creation) < time.Hour * 24 { + if time.Now().Sub(p.Creation) < time.Hour*24 { filtered = append(filtered, p) } } @@ -69,7 +69,7 @@ func (pm *ProviderManager) run() { func (pm *ProviderManager) AddProvider(k u.Key, val *peer.Peer) { pm.newprovs <- &addProv{ - k: k, + k: k, val: val, } } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index e3d34325d..3a4ebd33d 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -164,7 +164,8 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { case p := <-npeerChan: count++ if count >= KValue { - break + errChan <- u.ErrNotFound + return } c.Increment() @@ -172,40 +173,38 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { default: if c.Size() == 0 { errChan <- u.ErrNotFound + return } } } }() process := func() { - for { - select { - case p, ok := <-procPeer: - if !ok || p == nil { - c.Decrement() - return - } - val, peers, err := dht.getValueOrPeers(p, key, timeout/4, routeLevel) - if err != nil { - u.DErr("%v\n", err.Error()) - c.Decrement() - continue - } - if val != nil { - valChan <- val - c.Decrement() - return - } + for p := range procPeer { + if p == nil { + c.Decrement() + return + } + val, peers, err := dht.getValueOrPeers(p, key, timeout/4, routeLevel) + if err != nil { + u.DErr("%v\n", err.Error()) + c.Decrement() + continue + } + if val != nil { + valChan <- val + c.Decrement() + return + } - for _, np := range peers { - // TODO: filter out peers that arent closer - if !pset.Contains(np) && pset.Size() < KValue { - pset.Add(np) //This is racey... make a single function to do operation - npeerChan <- np - } + for _, np := range peers { + // TODO: filter out peers that arent closer + if !pset.Contains(np) && pset.Size() < KValue { + pset.Add(np) //This is racey... make a single function to do operation + npeerChan <- np } - c.Decrement() } + c.Decrement() } } From 797427ce19747093d5b3043e545e72af12a2b44d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 20 Aug 2014 16:51:03 -0700 Subject: [PATCH 0067/3817] fix swarm message type code, i beleive it works well now This commit was moved from ipfs/go-ipfs-routing@275fcaa9bcb154ad5ccb378c0754b6da29c71eaf --- routing/dht/dht.go | 25 +++++++++++++------------ routing/dht/ext_test.go | 4 ++-- routing/dht/routing.go | 6 +++--- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index b0a2a0481..4c0751aa5 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -28,6 +28,7 @@ type IpfsDHT struct { routingTables []*kb.RoutingTable network swarm.Network + netChan *swarm.Chan // Local peer (yourself) self *peer.Peer @@ -55,6 +56,7 @@ type IpfsDHT struct { func NewDHT(p *peer.Peer, net swarm.Network) *IpfsDHT { dht := new(IpfsDHT) dht.network = net + dht.netChan = net.GetChannel(swarm.PBWrapper_DHT_MESSAGE) dht.datastore = ds.NewMapDatastore() dht.self = p dht.providers = NewProviderManager() @@ -101,10 +103,9 @@ func (dht *IpfsDHT) handleMessages() { u.DOut("Begin message handling routine\n") errs := dht.network.GetErrChan() - dhtmes := dht.network.GetChannel(swarm.PBWrapper_DHT_MESSAGE) for { select { - case mes, ok := <-dhtmes: + case mes, ok := <-dht.netChan.Incoming: if !ok { u.DOut("handleMessages closing, bad recv on incoming\n") return @@ -165,7 +166,7 @@ func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) er } mes := swarm.NewMessage(p, pmes.ToProtobuf()) - dht.network.Send(mes) + dht.netChan.Outgoing <- mes return nil } @@ -225,7 +226,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { out: mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.network.Send(mes) + dht.netChan.Outgoing <- mes } // Store a value in this peer local storage @@ -247,7 +248,7 @@ func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *PBDHTMessage) { ID: pmes.GetId(), } - dht.network.Send(swarm.NewMessage(p, resp.ToProtobuf())) + dht.netChan.Outgoing <- swarm.NewMessage(p, resp.ToProtobuf()) } func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) { @@ -258,7 +259,7 @@ func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) { } defer func() { mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.network.Send(mes) + dht.netChan.Outgoing <- mes }() level := pmes.GetValue()[0] u.DOut("handleFindPeer: searching for '%s'\n", peer.ID(pmes.GetKey()).Pretty()) @@ -310,7 +311,7 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *PBDHTMessage) { } mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.network.Send(mes) + dht.netChan.Outgoing <- mes } type providerInfo struct { @@ -336,7 +337,7 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { for _, ps := range seq { mes := swarm.NewMessage(ps, pmes) - dht.network.Send(mes) + dht.netChan.Outgoing <- mes } buf := new(bytes.Buffer) @@ -372,7 +373,7 @@ out: } mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.network.Send(mes) + dht.netChan.Outgoing <- mes } func (dht *IpfsDHT) getValueOrPeers(p *peer.Peer, key u.Key, timeout time.Duration, level int) ([]byte, []*peer.Peer, error) { @@ -429,7 +430,7 @@ func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duratio mes := swarm.NewMessage(p, pmes.ToProtobuf()) t := time.Now() - dht.network.Send(mes) + dht.netChan.Outgoing <- mes // Wait for either the response or a timeout timeup := time.After(timeout) @@ -545,7 +546,7 @@ func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Durati mes := swarm.NewMessage(p, pmes.ToProtobuf()) listenChan := dht.listener.Listen(pmes.ID, 1, time.Minute) t := time.Now() - dht.network.Send(mes) + dht.netChan.Outgoing <- mes after := time.After(timeout) select { case <-after: @@ -581,7 +582,7 @@ func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, time mes := swarm.NewMessage(p, pmes.ToProtobuf()) listenChan := dht.listener.Listen(pmes.ID, 1, time.Minute) - dht.network.Send(mes) + dht.netChan.Outgoing <- mes after := time.After(timeout) select { case <-after: diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 79cfd27bc..6e034c69d 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -70,8 +70,8 @@ func (f *fauxNet) GetErrChan() chan error { return f.Chan.Errors } -func (f *fauxNet) GetChannel(t swarm.PBWrapper_MessageType) chan *swarm.Message { - return f.Chan.Incoming +func (f *fauxNet) GetChannel(t swarm.PBWrapper_MessageType) *swarm.Chan { + return f.Chan } func (f *fauxNet) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 3a4ebd33d..3c4ad9e00 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -240,7 +240,7 @@ func (dht *IpfsDHT) Provide(key u.Key) error { for _, p := range peers { mes := swarm.NewMessage(p, pbmes) - dht.network.Send(mes) + dht.netChan.Outgoing <- mes } return nil } @@ -352,7 +352,7 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { before := time.Now() responseChan := dht.listener.Listen(pmes.ID, 1, time.Minute) - dht.network.Send(mes) + dht.netChan.Outgoing <- mes tout := time.After(timeout) select { @@ -385,7 +385,7 @@ func (dht *IpfsDHT) getDiagnostic(timeout time.Duration) ([]*diagInfo, error) { pbmes := pmes.ToProtobuf() for _, p := range targets { mes := swarm.NewMessage(p, pbmes) - dht.network.Send(mes) + dht.netChan.Outgoing <- mes } var out []*diagInfo From 6c323bcb01abd2b653b0fa49778c8eac13d12b12 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 23 Aug 2014 22:21:20 -0700 Subject: [PATCH 0068/3817] refactor to allow use of mes_listener outside of dht This commit was moved from ipfs/go-ipfs-routing@318a5d4d4652778095b2d87f9145219e2a23db7e --- routing/dht/dht.go | 33 +++++---- routing/dht/ext_test.go | 2 +- routing/dht/mes_listener.go | 116 ------------------------------- routing/dht/mes_listener_test.go | 33 --------- routing/dht/providers.go | 4 ++ routing/dht/routing.go | 100 +++++--------------------- routing/dht/util.go | 71 +++++++++++++++++++ 7 files changed, 113 insertions(+), 246 deletions(-) delete mode 100644 routing/dht/mes_listener.go delete mode 100644 routing/dht/mes_listener_test.go create mode 100644 routing/dht/util.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 4c0751aa5..aa3a8da8b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -2,7 +2,7 @@ package dht import ( "bytes" - "errors" + "fmt" "sync" "time" @@ -49,7 +49,7 @@ type IpfsDHT struct { diaglock sync.Mutex // listener is a server to register to listen for responses to messages - listener *mesListener + listener *swarm.MesListener } // NewDHT creates a new DHT object with the given peer as the 'local' host @@ -66,7 +66,7 @@ func NewDHT(p *peer.Peer, net swarm.Network) *IpfsDHT { dht.routingTables[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*30) dht.routingTables[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*100) dht.routingTables[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Hour) - dht.listener = newMesListener() + dht.listener = swarm.NewMesListener() dht.birth = time.Now() return dht } @@ -89,7 +89,7 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { // NOTE: this should be done better... err = dht.Ping(npeer, time.Second*2) if err != nil { - return nil, errors.New("failed to ping newly connected peer\n") + return nil, fmt.Errorf("failed to ping newly connected peer: %s\n", err) } dht.Update(npeer) @@ -132,19 +132,19 @@ func (dht *IpfsDHT) handleMessages() { pmes.GetId(), mes.Peer.ID.Pretty()) switch pmes.GetType() { case PBDHTMessage_GET_VALUE: - dht.handleGetValue(mes.Peer, pmes) + go dht.handleGetValue(mes.Peer, pmes) case PBDHTMessage_PUT_VALUE: - dht.handlePutValue(mes.Peer, pmes) + go dht.handlePutValue(mes.Peer, pmes) case PBDHTMessage_FIND_NODE: - dht.handleFindPeer(mes.Peer, pmes) + go dht.handleFindPeer(mes.Peer, pmes) case PBDHTMessage_ADD_PROVIDER: - dht.handleAddProvider(mes.Peer, pmes) + go dht.handleAddProvider(mes.Peer, pmes) case PBDHTMessage_GET_PROVIDERS: - dht.handleGetProviders(mes.Peer, pmes) + go dht.handleGetProviders(mes.Peer, pmes) case PBDHTMessage_PING: - dht.handlePing(mes.Peer, pmes) + go dht.handlePing(mes.Peer, pmes) case PBDHTMessage_DIAGNOSTIC: - dht.handleDiagnostic(mes.Peer, pmes) + go dht.handleDiagnostic(mes.Peer, pmes) default: u.PErr("Recieved invalid message type") } @@ -162,7 +162,7 @@ func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) er Type: PBDHTMessage_PUT_VALUE, Key: key, Value: value, - ID: GenerateMessageID(), + ID: swarm.GenerateMessageID(), } mes := swarm.NewMessage(p, pmes.ToProtobuf()) @@ -242,6 +242,7 @@ func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *PBDHTMessage) { } func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *PBDHTMessage) { + u.DOut("[%s] Responding to ping from [%s]!\n", dht.self.ID.Pretty(), p.ID.Pretty()) resp := Message{ Type: pmes.GetType(), Response: true, @@ -328,6 +329,8 @@ func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *PBDHTMessage) { func (dht *IpfsDHT) Halt() { dht.shutdown <- struct{}{} dht.network.Close() + dht.providers.Halt() + dht.listener.Halt() } // NOTE: not yet finished, low priority @@ -424,7 +427,7 @@ func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duratio Type: PBDHTMessage_GET_VALUE, Key: string(key), Value: []byte{byte(level)}, - ID: GenerateMessageID(), + ID: swarm.GenerateMessageID(), } responseChan := dht.listener.Listen(pmes.ID, 1, time.Minute) @@ -539,7 +542,7 @@ func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Durati pmes := Message{ Type: PBDHTMessage_FIND_NODE, Key: string(id), - ID: GenerateMessageID(), + ID: swarm.GenerateMessageID(), Value: []byte{byte(level)}, } @@ -575,7 +578,7 @@ func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, time pmes := Message{ Type: PBDHTMessage_GET_PROVIDERS, Key: string(key), - ID: GenerateMessageID(), + ID: swarm.GenerateMessageID(), Value: []byte{byte(level)}, } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 6e034c69d..e631e27ca 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -153,7 +153,7 @@ func TestGetFailures(t *testing.T) { req := Message{ Type: PBDHTMessage_GET_VALUE, Key: "hello", - ID: GenerateMessageID(), + ID: swarm.GenerateMessageID(), Value: []byte{0}, } fn.Chan.Incoming <- swarm.NewMessage(other, req.ToProtobuf()) diff --git a/routing/dht/mes_listener.go b/routing/dht/mes_listener.go deleted file mode 100644 index 133be877a..000000000 --- a/routing/dht/mes_listener.go +++ /dev/null @@ -1,116 +0,0 @@ -package dht - -import ( - "sync" - "time" - - swarm "github.com/jbenet/go-ipfs/swarm" - u "github.com/jbenet/go-ipfs/util" -) - -type mesListener struct { - listeners map[uint64]*listenInfo - haltchan chan struct{} - unlist chan uint64 - nlist chan *listenInfo - send chan *respMes -} - -// The listen info struct holds information about a message that is being waited for -type listenInfo struct { - // Responses matching the listen ID will be sent through resp - resp chan *swarm.Message - - // count is the number of responses to listen for - count int - - // eol is the time at which this listener will expire - eol time.Time - - // sendlock is used to prevent conditions where we try to send on the resp - // channel as its being closed by a timeout in another thread - sendLock sync.Mutex - - closed bool - - id uint64 -} - -func newMesListener() *mesListener { - ml := new(mesListener) - ml.haltchan = make(chan struct{}) - ml.listeners = make(map[uint64]*listenInfo) - ml.nlist = make(chan *listenInfo, 16) - ml.send = make(chan *respMes, 16) - ml.unlist = make(chan uint64, 16) - go ml.run() - return ml -} - -func (ml *mesListener) Listen(id uint64, count int, timeout time.Duration) <-chan *swarm.Message { - li := new(listenInfo) - li.count = count - li.eol = time.Now().Add(timeout) - li.resp = make(chan *swarm.Message, count) - li.id = id - ml.nlist <- li - return li.resp -} - -func (ml *mesListener) Unlisten(id uint64) { - ml.unlist <- id -} - -type respMes struct { - id uint64 - mes *swarm.Message -} - -func (ml *mesListener) Respond(id uint64, mes *swarm.Message) { - ml.send <- &respMes{ - id: id, - mes: mes, - } -} - -func (ml *mesListener) Halt() { - ml.haltchan <- struct{}{} -} - -func (ml *mesListener) run() { - for { - select { - case <-ml.haltchan: - return - case id := <-ml.unlist: - trg, ok := ml.listeners[id] - if !ok { - continue - } - close(trg.resp) - delete(ml.listeners, id) - case li := <-ml.nlist: - ml.listeners[li.id] = li - case s := <-ml.send: - trg, ok := ml.listeners[s.id] - if !ok { - u.DOut("Send with no listener.") - continue - } - - if time.Now().After(trg.eol) { - close(trg.resp) - delete(ml.listeners, s.id) - continue - } - - trg.resp <- s.mes - trg.count-- - - if trg.count == 0 { - close(trg.resp) - delete(ml.listeners, s.id) - } - } - } -} diff --git a/routing/dht/mes_listener_test.go b/routing/dht/mes_listener_test.go deleted file mode 100644 index 8e494aabc..000000000 --- a/routing/dht/mes_listener_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package dht - -import ( - "testing" - "time" - - "github.com/jbenet/go-ipfs/peer" - "github.com/jbenet/go-ipfs/swarm" -) - -// Ensure that the Message Listeners basic functionality works -func TestMesListenerBasic(t *testing.T) { - ml := newMesListener() - a := GenerateMessageID() - resp := ml.Listen(a, 1, time.Minute) - - pmes := new(swarm.PBWrapper) - pmes.Message = []byte("Hello") - pmes.Type = new(swarm.PBWrapper_MessageType) - mes := swarm.NewMessage(new(peer.Peer), pmes) - - go ml.Respond(a, mes) - - del := time.After(time.Millisecond * 10) - select { - case get := <-resp: - if string(get.Data) != string(mes.Data) { - t.Fatal("Something got really messed up") - } - case <-del: - t.Fatal("Waiting on message response timed out.") - } -} diff --git a/routing/dht/providers.go b/routing/dht/providers.go index fdf8d6581..2e89eea4c 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -81,3 +81,7 @@ func (pm *ProviderManager) GetProviders(k u.Key) []*peer.Peer { pm.getprovs <- gp return <-gp.resp } + +func (pm *ProviderManager) Halt() { + pm.halt <- struct{}{} +} diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 3c4ad9e00..62ba4a53b 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -4,8 +4,6 @@ import ( "bytes" "encoding/json" "errors" - "math/rand" - "sync" "time" proto "code.google.com/p/goprotobuf/proto" @@ -18,21 +16,6 @@ import ( u "github.com/jbenet/go-ipfs/util" ) -// Pool size is the number of nodes used for group find/set RPC calls -var PoolSize = 6 - -// We put the 'K' in kademlia! -var KValue = 10 - -// Its in the paper, i swear -var AlphaValue = 3 - -// GenerateMessageID creates and returns a new message ID -// TODO: determine a way of creating and managing message IDs -func GenerateMessageID() uint64 { - return (uint64(rand.Uint32()) << 32) | uint64(rand.Uint32()) -} - // This file implements the Routing interface for the IpfsDHT struct. // Basic Put/Get @@ -64,60 +47,6 @@ func (dht *IpfsDHT) PutValue(key u.Key, value []byte) { } } -// A counter for incrementing a variable across multiple threads -type counter struct { - n int - mut sync.RWMutex -} - -func (c *counter) Increment() { - c.mut.Lock() - c.n++ - c.mut.Unlock() -} - -func (c *counter) Decrement() { - c.mut.Lock() - c.n-- - c.mut.Unlock() -} - -func (c *counter) Size() int { - c.mut.RLock() - defer c.mut.RUnlock() - return c.n -} - -type peerSet struct { - ps map[string]bool - lk sync.RWMutex -} - -func newPeerSet() *peerSet { - ps := new(peerSet) - ps.ps = make(map[string]bool) - return ps -} - -func (ps *peerSet) Add(p *peer.Peer) { - ps.lk.Lock() - ps.ps[string(p.ID)] = true - ps.lk.Unlock() -} - -func (ps *peerSet) Contains(p *peer.Peer) bool { - ps.lk.RLock() - _, ok := ps.ps[string(p.ID)] - ps.lk.RUnlock() - return ok -} - -func (ps *peerSet) Size() int { - ps.lk.RLock() - defer ps.lk.RUnlock() - return len(ps.ps) -} - // GetValue searches for the value corresponding to given Key. // If the search does not succeed, a multiaddr string of a closer peer is // returned along with util.ErrSearchIncomplete @@ -159,9 +88,13 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { count := 0 go func() { + defer close(procPeer) for { select { - case p := <-npeerChan: + case p, ok := <-npeerChan: + if !ok { + return + } count++ if count >= KValue { errChan <- u.ErrNotFound @@ -171,8 +104,11 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { procPeer <- p default: - if c.Size() == 0 { - errChan <- u.ErrNotFound + if c.Size() <= 0 { + select { + case errChan <- u.ErrNotFound: + default: + } return } } @@ -180,20 +116,22 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { }() process := func() { + defer c.Decrement() for p := range procPeer { if p == nil { - c.Decrement() return } val, peers, err := dht.getValueOrPeers(p, key, timeout/4, routeLevel) if err != nil { u.DErr("%v\n", err.Error()) - c.Decrement() continue } if val != nil { - valChan <- val - c.Decrement() + select { + case valChan <- val: + default: + u.DOut("Wasnt the first to return the value!") + } return } @@ -347,7 +285,7 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { // Thoughts: maybe this should accept an ID and do a peer lookup? u.DOut("Enter Ping.") - pmes := Message{ID: GenerateMessageID(), Type: PBDHTMessage_PING} + pmes := Message{ID: swarm.GenerateMessageID(), Type: PBDHTMessage_PING} mes := swarm.NewMessage(p, pmes.ToProtobuf()) before := time.Now() @@ -363,7 +301,7 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { return nil case <-tout: // Timed out, think about removing peer from network - u.DOut("Ping peer timed out.") + u.DOut("[%s] Ping peer [%s] timed out.", dht.self.ID.Pretty(), p.ID.Pretty()) dht.listener.Unlisten(pmes.ID) return u.ErrTimeout } @@ -377,7 +315,7 @@ func (dht *IpfsDHT) getDiagnostic(timeout time.Duration) ([]*diagInfo, error) { // TODO: Add timeout to this struct so nodes know when to return pmes := Message{ Type: PBDHTMessage_DIAGNOSTIC, - ID: GenerateMessageID(), + ID: swarm.GenerateMessageID(), } listenChan := dht.listener.Listen(pmes.ID, len(targets), time.Minute*2) diff --git a/routing/dht/util.go b/routing/dht/util.go new file mode 100644 index 000000000..18c4555a9 --- /dev/null +++ b/routing/dht/util.go @@ -0,0 +1,71 @@ +package dht + +import ( + "sync" + + peer "github.com/jbenet/go-ipfs/peer" +) + +// Pool size is the number of nodes used for group find/set RPC calls +var PoolSize = 6 + +// We put the 'K' in kademlia! +var KValue = 10 + +// Its in the paper, i swear +var AlphaValue = 3 + +// A counter for incrementing a variable across multiple threads +type counter struct { + n int + mut sync.Mutex +} + +func (c *counter) Increment() { + c.mut.Lock() + c.n++ + c.mut.Unlock() +} + +func (c *counter) Decrement() { + c.mut.Lock() + c.n-- + c.mut.Unlock() +} + +func (c *counter) Size() (s int) { + c.mut.Lock() + s = c.n + c.mut.Unlock() + return +} + +type peerSet struct { + ps map[string]bool + lk sync.RWMutex +} + +func newPeerSet() *peerSet { + ps := new(peerSet) + ps.ps = make(map[string]bool) + return ps +} + +func (ps *peerSet) Add(p *peer.Peer) { + ps.lk.Lock() + ps.ps[string(p.ID)] = true + ps.lk.Unlock() +} + +func (ps *peerSet) Contains(p *peer.Peer) bool { + ps.lk.RLock() + _, ok := ps.ps[string(p.ID)] + ps.lk.RUnlock() + return ok +} + +func (ps *peerSet) Size() int { + ps.lk.RLock() + defer ps.lk.RUnlock() + return len(ps.ps) +} From 9ec5168f1eb45014abc0b4a8cedf58e7e08b9619 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 24 Aug 2014 18:13:05 -0700 Subject: [PATCH 0069/3817] basic implementation of bitswap, needs testing/verification that it works This commit was moved from ipfs/go-blockservice@86681190298aa0ffbf736ce0225461a289f188f9 --- blockservice/blockservice.go | 54 ++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 blockservice/blockservice.go diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go new file mode 100644 index 000000000..0f5dbd888 --- /dev/null +++ b/blockservice/blockservice.go @@ -0,0 +1,54 @@ +package blockservice + +import ( + "fmt" + + ds "github.com/jbenet/datastore.go" + bitswap "github.com/jbenet/go-ipfs/bitswap" + blocks "github.com/jbenet/go-ipfs/blocks" + u "github.com/jbenet/go-ipfs/util" + + mh "github.com/jbenet/go-multihash" +) + +// BlockService is a block datastore. +// It uses an internal `datastore.Datastore` instance to store values. +type BlockService struct { + Datastore ds.Datastore + Remote *bitswap.BitSwap +} + +// NewBlockService creates a BlockService with given datastore instance. +func NewBlockService(d ds.Datastore) (*BlockService, error) { + if d == nil { + return nil, fmt.Errorf("BlockService requires valid datastore") + } + return &BlockService{Datastore: d}, nil +} + +// AddBlock adds a particular block to the service, Putting it into the datastore. +func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { + k := b.Key() + dsk := ds.NewKey(string(k)) + return k, s.Datastore.Put(dsk, b.Data) +} + +// GetBlock retrieves a particular block from the service, +// Getting it from the datastore using the key (hash). +func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { + dsk := ds.NewKey(string(k)) + datai, err := s.Datastore.Get(dsk) + if err != nil { + return nil, err + } + + data, ok := datai.([]byte) + if !ok { + return nil, fmt.Errorf("data associated with %s is not a []byte", k) + } + + return &blocks.Block{ + Multihash: mh.Multihash(k), + Data: data, + }, nil +} From 3de2f9aca4798a8a2c1d3e4e01e50711b929dbfa Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 24 Aug 2014 18:13:05 -0700 Subject: [PATCH 0070/3817] basic implementation of bitswap, needs testing/verification that it works This commit was moved from ipfs/go-ipfs-routing@8505073e1b7b7dbd4d2650efe602eb9243fe4d61 --- routing/dht/dht.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index aa3a8da8b..593310d64 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -603,6 +603,7 @@ func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, time } } +// TODO: Could be done async func (dht *IpfsDHT) addPeerList(key u.Key, peers []*PBDHTMessage_PBPeer) []*peer.Peer { var provArr []*peer.Peer for _, prov := range peers { From 115ed53d6cb8db7679f9c6414ea3c43d1e2fae82 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 24 Aug 2014 18:13:05 -0700 Subject: [PATCH 0071/3817] basic implementation of bitswap, needs testing/verification that it works This commit was moved from ipfs/go-merkledag@c074e6d87edebfb61897d4dd65beccd5c81e8c24 --- ipld/merkledag/merkledag.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index b906bbf89..4d1f5d4e4 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -4,6 +4,7 @@ import ( "fmt" blocks "github.com/jbenet/go-ipfs/blocks" + bserv "github.com/jbenet/go-ipfs/blockservice" u "github.com/jbenet/go-ipfs/util" mh "github.com/jbenet/go-multihash" ) @@ -93,7 +94,7 @@ func (n *Node) Key() (u.Key, error) { // - the root is virtual (like a forest) // - stores nodes' data in a BlockService type DAGService struct { - Blocks *blocks.BlockService + Blocks *bserv.BlockService } // Put adds a node to the DAGService, storing the block in the BlockService From c9ec1cc98b5d4a3bbb0f3f63b872c87309681856 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 25 Aug 2014 09:44:42 -0700 Subject: [PATCH 0072/3817] more work on bitswap and other code cleanup This commit was moved from ipfs/go-blockservice@ffc60abb306372b47deb28bb640479cae78602d0 --- blockservice/blocks_test.go | 68 +++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 blockservice/blocks_test.go diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go new file mode 100644 index 000000000..d1fe5f080 --- /dev/null +++ b/blockservice/blocks_test.go @@ -0,0 +1,68 @@ +package blockservice + +import ( + "bytes" + "fmt" + "testing" + + ds "github.com/jbenet/datastore.go" + blocks "github.com/jbenet/go-ipfs/blocks" + u "github.com/jbenet/go-ipfs/util" +) + +func TestBlocks(t *testing.T) { + + d := ds.NewMapDatastore() + bs, err := NewBlockService(d) + if err != nil { + t.Error("failed to construct block service", err) + return + } + + b, err := blocks.NewBlock([]byte("beep boop")) + if err != nil { + t.Error("failed to construct block", err) + return + } + + h, err := u.Hash([]byte("beep boop")) + if err != nil { + t.Error("failed to hash data", err) + return + } + + if !bytes.Equal(b.Multihash, h) { + t.Error("Block Multihash and data multihash not equal") + } + + if b.Key() != u.Key(h) { + t.Error("Block key and data multihash key not equal") + } + + k, err := bs.AddBlock(b) + if err != nil { + t.Error("failed to add block to BlockService", err) + return + } + + if k != b.Key() { + t.Error("returned key is not equal to block key", err) + } + + b2, err := bs.GetBlock(b.Key()) + if err != nil { + t.Error("failed to retrieve block from BlockService", err) + return + } + + if b.Key() != b2.Key() { + t.Error("Block keys not equal.") + } + + if !bytes.Equal(b.Data, b2.Data) { + t.Error("Block data is not equal.") + } + + fmt.Printf("key: %s\n", b.Key()) + fmt.Printf("data: %v\n", b.Data) +} From e661c0676bd272df4bd6739229755a164d4e99d5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 25 Aug 2014 09:44:42 -0700 Subject: [PATCH 0073/3817] more work on bitswap and other code cleanup This commit was moved from ipfs/go-ipfs-routing@5052ac4a9d4b82c1887d49d4408799af4a057931 --- routing/dht/routing.go | 56 +++++++++++++++++++++++++++++++++++++++++- routing/dht/util.go | 12 +++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 62ba4a53b..e4073c260 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -22,7 +22,7 @@ import ( // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT -func (dht *IpfsDHT) PutValue(key u.Key, value []byte) { +func (dht *IpfsDHT) PutValue(key u.Key, value []byte) error { complete := make(chan struct{}) count := 0 for _, route := range dht.routingTables { @@ -45,6 +45,7 @@ func (dht *IpfsDHT) PutValue(key u.Key, value []byte) { for i := 0; i < count; i++ { <-complete } + return nil } // GetValue searches for the value corresponding to given Key. @@ -183,6 +184,59 @@ func (dht *IpfsDHT) Provide(key u.Key) error { return nil } +func (dht *IpfsDHT) FindProvidersAsync(key u.Key, count int, timeout time.Duration) chan *peer.Peer { + peerOut := make(chan *peer.Peer, count) + go func() { + ps := newPeerSet() + provs := dht.providers.GetProviders(key) + for _, p := range provs { + count-- + // NOTE: assuming that the list of peers is unique + ps.Add(p) + peerOut <- p + if count <= 0 { + return + } + } + + peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), AlphaValue) + for _, pp := range peers { + go func() { + pmes, err := dht.findProvidersSingle(pp, key, 0, timeout) + if err != nil { + u.PErr("%v\n", err) + return + } + dht.addPeerListAsync(key, pmes.GetPeers(), ps, count, peerOut) + }() + } + + }() + return peerOut +} + +//TODO: this function could also be done asynchronously +func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*PBDHTMessage_PBPeer, ps *peerSet, count int, out chan *peer.Peer) { + for _, pbp := range peers { + maddr, err := ma.NewMultiaddr(pbp.GetAddr()) + if err != nil { + u.PErr("%v\n", err) + continue + } + p, err := dht.network.GetConnection(peer.ID(pbp.GetId()), maddr) + if err != nil { + u.PErr("%v\n", err) + continue + } + dht.providers.AddProvider(k, p) + if ps.AddIfSmallerThan(p, count) { + out <- p + } else if ps.Size() >= count { + return + } + } +} + // FindProviders searches for peers who can provide the value for given key. func (dht *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) { ll := startNewRPC("FindProviders") diff --git a/routing/dht/util.go b/routing/dht/util.go index 18c4555a9..d12f7f9df 100644 --- a/routing/dht/util.go +++ b/routing/dht/util.go @@ -40,6 +40,7 @@ func (c *counter) Size() (s int) { return } +// peerSet is a threadsafe set of peers type peerSet struct { ps map[string]bool lk sync.RWMutex @@ -69,3 +70,14 @@ func (ps *peerSet) Size() int { defer ps.lk.RUnlock() return len(ps.ps) } + +func (ps *peerSet) AddIfSmallerThan(p *peer.Peer, maxsize int) bool { + var success bool + ps.lk.Lock() + if _, ok := ps.ps[string(p.ID)]; !ok && len(ps.ps) < maxsize { + success = true + ps.ps[string(p.ID)] = true + } + ps.lk.Unlock() + return success +} From f322871ddc2f2cbf42cd096b66dd3f5766222db1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 26 Aug 2014 14:24:51 -0700 Subject: [PATCH 0074/3817] bitswap first working commit! This commit was moved from ipfs/go-blockservice@54ff6ea4b67564c7861486dc08285c398a4d90fe --- blockservice/blockservice.go | 45 ++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 0f5dbd888..a645c2917 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -2,6 +2,7 @@ package blockservice import ( "fmt" + "time" ds "github.com/jbenet/datastore.go" bitswap "github.com/jbenet/go-ipfs/bitswap" @@ -19,18 +20,27 @@ type BlockService struct { } // NewBlockService creates a BlockService with given datastore instance. -func NewBlockService(d ds.Datastore) (*BlockService, error) { +func NewBlockService(d ds.Datastore, rem *bitswap.BitSwap) (*BlockService, error) { if d == nil { return nil, fmt.Errorf("BlockService requires valid datastore") } - return &BlockService{Datastore: d}, nil + if rem == nil { + return nil, fmt.Errorf("BlockService requires a valid bitswap") + } + return &BlockService{Datastore: d, Remote: rem}, nil } // AddBlock adds a particular block to the service, Putting it into the datastore. func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { k := b.Key() dsk := ds.NewKey(string(k)) - return k, s.Datastore.Put(dsk, b.Data) + u.DOut("storing [%s] in datastore\n", k.Pretty()) + err := s.Datastore.Put(dsk, b.Data) + if err != nil { + return k, err + } + err = s.Remote.HaveBlock(b.Key()) + return k, err } // GetBlock retrieves a particular block from the service, @@ -38,17 +48,22 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { dsk := ds.NewKey(string(k)) datai, err := s.Datastore.Get(dsk) - if err != nil { - return nil, err + if err == nil { + bdata, ok := datai.([]byte) + if !ok { + return nil, fmt.Errorf("data associated with %s is not a []byte", k) + } + return &blocks.Block{ + Multihash: mh.Multihash(k), + Data: bdata, + }, nil + } else if err == ds.ErrNotFound { + blk, err := s.Remote.GetBlock(k, time.Second*5) + if err != nil { + return nil, err + } + return blk, nil + } else { + return nil, u.ErrNotFound } - - data, ok := datai.([]byte) - if !ok { - return nil, fmt.Errorf("data associated with %s is not a []byte", k) - } - - return &blocks.Block{ - Multihash: mh.Multihash(k), - Data: data, - }, nil } From 4b7945e3c69763ab4806a1d306cf6a6a0d3b8127 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 26 Aug 2014 14:24:51 -0700 Subject: [PATCH 0075/3817] bitswap first working commit! This commit was moved from ipfs/go-ipfs-routing@1ecb7edab5f1e26d965f1bcb0a1847d35a60069e --- routing/dht/dht.go | 22 ++++++++++++++-------- routing/dht/dht_test.go | 11 ++++++----- routing/dht/ext_test.go | 7 ++++--- routing/dht/routing.go | 10 ++++++++-- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 593310d64..f340bf805 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -53,11 +53,11 @@ type IpfsDHT struct { } // NewDHT creates a new DHT object with the given peer as the 'local' host -func NewDHT(p *peer.Peer, net swarm.Network) *IpfsDHT { +func NewDHT(p *peer.Peer, net swarm.Network, dstore ds.Datastore) *IpfsDHT { dht := new(IpfsDHT) dht.network = net dht.netChan = net.GetChannel(swarm.PBWrapper_DHT_MESSAGE) - dht.datastore = ds.NewMapDatastore() + dht.datastore = dstore dht.self = p dht.providers = NewProviderManager() dht.shutdown = make(chan struct{}) @@ -322,6 +322,7 @@ type providerInfo struct { func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *PBDHTMessage) { key := u.Key(pmes.GetKey()) + u.DOut("[%s] Adding [%s] as a provider for '%s'\n", dht.self.ID.Pretty(), p.ID.Pretty(), peer.ID(key).Pretty()) dht.providers.AddProvider(key, p) } @@ -615,12 +616,8 @@ func (dht *IpfsDHT) addPeerList(key u.Key, peers []*PBDHTMessage_PBPeer) []*peer p := dht.network.Find(u.Key(prov.GetId())) if p == nil { u.DOut("given provider %s was not in our network already.\n", peer.ID(prov.GetId()).Pretty()) - maddr, err := ma.NewMultiaddr(prov.GetAddr()) - if err != nil { - u.PErr("error connecting to new peer: %s\n", err) - continue - } - p, err = dht.network.GetConnection(peer.ID(prov.GetId()), maddr) + var err error + p, err = dht.peerFromInfo(prov) if err != nil { u.PErr("error connecting to new peer: %s\n", err) continue @@ -631,3 +628,12 @@ func (dht *IpfsDHT) addPeerList(key u.Key, peers []*PBDHTMessage_PBPeer) []*peer } return provArr } + +func (dht *IpfsDHT) peerFromInfo(pbp *PBDHTMessage_PBPeer) (*peer.Peer, error) { + maddr, err := ma.NewMultiaddr(pbp.GetAddr()) + if err != nil { + return nil, err + } + + return dht.network.GetConnection(peer.ID(pbp.GetId()), maddr) +} diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 92d0931fb..817ecec37 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -3,6 +3,7 @@ package dht import ( "testing" + ds "github.com/jbenet/datastore.go" peer "github.com/jbenet/go-ipfs/peer" swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" @@ -37,7 +38,7 @@ func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) if err != nil { t.Fatal(err) } - d := NewDHT(peers[i], net) + d := NewDHT(peers[i], net, ds.NewMapDatastore()) dhts = append(dhts, d) d.Start() } @@ -69,14 +70,14 @@ func TestPing(t *testing.T) { if err != nil { t.Fatal(err) } - dhtA := NewDHT(peerA, neta) + dhtA := NewDHT(peerA, neta, ds.NewMapDatastore()) netb := swarm.NewSwarm(peerB) err = netb.Listen() if err != nil { t.Fatal(err) } - dhtB := NewDHT(peerB, netb) + dhtB := NewDHT(peerB, netb, ds.NewMapDatastore()) dhtA.Start() dhtB.Start() @@ -120,14 +121,14 @@ func TestValueGetSet(t *testing.T) { if err != nil { t.Fatal(err) } - dhtA := NewDHT(peerA, neta) + dhtA := NewDHT(peerA, neta, ds.NewMapDatastore()) netb := swarm.NewSwarm(peerB) err = netb.Listen() if err != nil { t.Fatal(err) } - dhtB := NewDHT(peerB, netb) + dhtB := NewDHT(peerB, netb, ds.NewMapDatastore()) dhtA.Start() dhtB.Start() diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index e631e27ca..67b108a48 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -7,6 +7,7 @@ import ( "code.google.com/p/goprotobuf/proto" + ds "github.com/jbenet/datastore.go" peer "github.com/jbenet/go-ipfs/peer" swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" @@ -89,7 +90,7 @@ func TestGetFailures(t *testing.T) { local := new(peer.Peer) local.ID = peer.ID("test_peer") - d := NewDHT(local, fn) + d := NewDHT(local, fn, ds.NewMapDatastore()) other := &peer.Peer{ID: peer.ID("other_peer")} @@ -177,7 +178,7 @@ func TestNotFound(t *testing.T) { local := new(peer.Peer) local.ID = peer.ID("test_peer") - d := NewDHT(local, fn) + d := NewDHT(local, fn, ds.NewMapDatastore()) d.Start() var ps []*peer.Peer @@ -239,7 +240,7 @@ func TestLessThanKResponses(t *testing.T) { local := new(peer.Peer) local.ID = peer.ID("test_peer") - d := NewDHT(local, fn) + d := NewDHT(local, fn, ds.NewMapDatastore()) d.Start() var ps []*peer.Peer diff --git a/routing/dht/routing.go b/routing/dht/routing.go index e4073c260..082f737f5 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -63,7 +63,7 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { val, err := dht.getLocal(key) if err == nil { ll.Success = true - u.DOut("Found local, returning.") + u.DOut("Found local, returning.\n") return val, nil } @@ -218,6 +218,9 @@ func (dht *IpfsDHT) FindProvidersAsync(key u.Key, count int, timeout time.Durati //TODO: this function could also be done asynchronously func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*PBDHTMessage_PBPeer, ps *peerSet, count int, out chan *peer.Peer) { for _, pbp := range peers { + if peer.ID(pbp.GetId()).Equal(dht.self.ID) { + continue + } maddr, err := ma.NewMultiaddr(pbp.GetAddr()) if err != nil { u.PErr("%v\n", err) @@ -256,11 +259,14 @@ func (dht *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Pee return nil, err } if pmes.GetSuccess() { + u.DOut("Got providers back from findProviders call!\n") provs := dht.addPeerList(key, pmes.GetPeers()) ll.Success = true return provs, nil } + u.DOut("Didnt get providers, just closer peers.\n") + closer := pmes.GetPeers() if len(closer) == 0 { level++ @@ -337,7 +343,7 @@ func (dht *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, err // Ping a peer, log the time it took func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { // Thoughts: maybe this should accept an ID and do a peer lookup? - u.DOut("Enter Ping.") + u.DOut("Enter Ping.\n") pmes := Message{ID: swarm.GenerateMessageID(), Type: PBDHTMessage_PING} mes := swarm.NewMessage(p, pmes.ToProtobuf()) From 76bb6e16714c51d7124b91cb99a9f226c596d53c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Aug 2014 12:01:03 -0700 Subject: [PATCH 0076/3817] fixing up some bitswap stuff after the PR This commit was moved from ipfs/go-blockservice@732b9a7e3ab43f8f28e0b867f4831c240523ca26 --- blockservice/blocks_test.go | 7 +------ blockservice/blockservice.go | 8 +++++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index d1fe5f080..28034b711 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -2,7 +2,6 @@ package blockservice import ( "bytes" - "fmt" "testing" ds "github.com/jbenet/datastore.go" @@ -11,9 +10,8 @@ import ( ) func TestBlocks(t *testing.T) { - d := ds.NewMapDatastore() - bs, err := NewBlockService(d) + bs, err := NewBlockService(d, nil) if err != nil { t.Error("failed to construct block service", err) return @@ -62,7 +60,4 @@ func TestBlocks(t *testing.T) { if !bytes.Equal(b.Data, b2.Data) { t.Error("Block data is not equal.") } - - fmt.Printf("key: %s\n", b.Key()) - fmt.Printf("data: %v\n", b.Data) } diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index a645c2917..eb59151cd 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -25,7 +25,7 @@ func NewBlockService(d ds.Datastore, rem *bitswap.BitSwap) (*BlockService, error return nil, fmt.Errorf("BlockService requires valid datastore") } if rem == nil { - return nil, fmt.Errorf("BlockService requires a valid bitswap") + u.PErr("Caution: blockservice running in local (offline) mode.\n") } return &BlockService{Datastore: d, Remote: rem}, nil } @@ -39,7 +39,9 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { if err != nil { return k, err } - err = s.Remote.HaveBlock(b.Key()) + if s.Remote != nil { + err = s.Remote.HaveBlock(b.Key()) + } return k, err } @@ -57,7 +59,7 @@ func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { Multihash: mh.Multihash(k), Data: bdata, }, nil - } else if err == ds.ErrNotFound { + } else if err == ds.ErrNotFound && s.Remote != nil { blk, err := s.Remote.GetBlock(k, time.Second*5) if err != nil { return nil, err From 17308802c8ac91d980deb6327f8ac24da865739f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Aug 2014 12:01:03 -0700 Subject: [PATCH 0077/3817] fixing up some bitswap stuff after the PR This commit was moved from ipfs/go-ipfs-routing@3745c3277fba428a1209d9528b0cdfb513e11cd9 --- routing/dht/dht.go | 4 ++-- routing/kbucket/table_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index f340bf805..b1a6e59c9 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -49,7 +49,7 @@ type IpfsDHT struct { diaglock sync.Mutex // listener is a server to register to listen for responses to messages - listener *swarm.MesListener + listener *swarm.MessageListener } // NewDHT creates a new DHT object with the given peer as the 'local' host @@ -66,7 +66,7 @@ func NewDHT(p *peer.Peer, net swarm.Network, dstore ds.Datastore) *IpfsDHT { dht.routingTables[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*30) dht.routingTables[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*100) dht.routingTables[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Hour) - dht.listener = swarm.NewMesListener() + dht.listener = swarm.NewMessageListener() dht.birth = time.Now() return dht } diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 13a55d14c..dbb391ff3 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -78,7 +78,7 @@ func TestTableUpdate(t *testing.T) { for i := 0; i < 10000; i++ { p := rt.Update(peers[rand.Intn(len(peers))]) if p != nil { - t.Log("evicted peer.") + //t.Log("evicted peer.") } } From 322a6713c58b51f0e5328e0073430ae0c7babd78 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Aug 2014 16:48:00 -0700 Subject: [PATCH 0078/3817] rework bitswap to reflect discussion on PR #32 This commit was moved from ipfs/go-blockservice@1c51a5220b9041c5c9dfc20d9180887e9311deda --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index eb59151cd..a2924d73f 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -40,7 +40,7 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { return k, err } if s.Remote != nil { - err = s.Remote.HaveBlock(b.Key()) + err = s.Remote.HaveBlock(b) } return k, err } From 5c1236969e2cde874fa24b3c9e6bc42aedfd8324 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Aug 2014 16:48:00 -0700 Subject: [PATCH 0079/3817] rework bitswap to reflect discussion on PR #32 This commit was moved from ipfs/go-ipfs-routing@d018b754e60a76dc51a4029edf1492195d9a9992 --- routing/dht/Message.go | 2 +- routing/dht/messages.pb.go | 27 ++++++++++++--------------- routing/dht/messages.proto | 2 +- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/routing/dht/Message.go b/routing/dht/Message.go index 20c311d80..4163fb485 100644 --- a/routing/dht/Message.go +++ b/routing/dht/Message.go @@ -11,7 +11,7 @@ type Message struct { Key string Value []byte Response bool - ID uint64 + ID string Success bool Peers []*peer.Peer } diff --git a/routing/dht/messages.pb.go b/routing/dht/messages.pb.go index 7c337d306..a2452dc28 100644 --- a/routing/dht/messages.pb.go +++ b/routing/dht/messages.pb.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-gogo. +// Code generated by protoc-gen-go. // source: messages.proto // DO NOT EDIT! @@ -13,7 +13,7 @@ It has these top-level messages: */ package dht -import proto "code.google.com/p/gogoprotobuf/proto" +import proto "code.google.com/p/goprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. @@ -69,17 +69,14 @@ func (x *PBDHTMessage_MessageType) UnmarshalJSON(data []byte) error { } type PBDHTMessage struct { - Type *PBDHTMessage_MessageType `protobuf:"varint,1,req,name=type,enum=dht.PBDHTMessage_MessageType" json:"type,omitempty"` - Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` - Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` - // Unique ID of this message, used to match queries with responses - Id *uint64 `protobuf:"varint,4,req,name=id" json:"id,omitempty"` - // Signals whether or not this message is a response to another message - Response *bool `protobuf:"varint,5,opt,name=response" json:"response,omitempty"` - Success *bool `protobuf:"varint,6,opt,name=success" json:"success,omitempty"` - // Used for returning peers from queries (normally, peers closer to X) - Peers []*PBDHTMessage_PBPeer `protobuf:"bytes,7,rep,name=peers" json:"peers,omitempty"` - XXX_unrecognized []byte `json:"-"` + Type *PBDHTMessage_MessageType `protobuf:"varint,1,req,name=type,enum=dht.PBDHTMessage_MessageType" json:"type,omitempty"` + Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` + Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` + Id *string `protobuf:"bytes,4,req,name=id" json:"id,omitempty"` + Response *bool `protobuf:"varint,5,opt,name=response" json:"response,omitempty"` + Success *bool `protobuf:"varint,6,opt,name=success" json:"success,omitempty"` + Peers []*PBDHTMessage_PBPeer `protobuf:"bytes,7,rep,name=peers" json:"peers,omitempty"` + XXX_unrecognized []byte `json:"-"` } func (m *PBDHTMessage) Reset() { *m = PBDHTMessage{} } @@ -107,11 +104,11 @@ func (m *PBDHTMessage) GetValue() []byte { return nil } -func (m *PBDHTMessage) GetId() uint64 { +func (m *PBDHTMessage) GetId() string { if m != nil && m.Id != nil { return *m.Id } - return 0 + return "" } func (m *PBDHTMessage) GetResponse() bool { diff --git a/routing/dht/messages.proto b/routing/dht/messages.proto index 4d4e8c61f..c2c5cc30d 100644 --- a/routing/dht/messages.proto +++ b/routing/dht/messages.proto @@ -23,7 +23,7 @@ message PBDHTMessage { optional bytes value = 3; // Unique ID of this message, used to match queries with responses - required uint64 id = 4; + required string id = 4; // Signals whether or not this message is a response to another message optional bool response = 5; From ed91841c90f3eca3257af6c2e0c69f872e2ebe30 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 29 Aug 2014 11:34:50 -0700 Subject: [PATCH 0080/3817] integrate bitswap and blockservice into the core package This commit was moved from ipfs/go-merkledag@9dd615bb42df651bbcd9fd7a6949668d70958ef1 --- ipld/merkledag/coding.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index d87f134f6..e27f03acc 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -2,6 +2,7 @@ package merkledag import ( "fmt" + mh "github.com/jbenet/go-multihash" ) From 6acb2cb3dc0b1eaa6fe9eb89a1b893ceddd675da Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 29 Aug 2014 11:34:50 -0700 Subject: [PATCH 0081/3817] integrate bitswap and blockservice into the core package This commit was moved from ipfs/go-blockservice@3df9fe4b697b5fec2c06ee16a3ed57c3b965e54a --- blockservice/blockservice.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index a2924d73f..ceb806f9b 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -48,24 +48,29 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { + u.DOut("BlockService GetBlock: '%s'\n", k.Pretty()) dsk := ds.NewKey(string(k)) datai, err := s.Datastore.Get(dsk) if err == nil { + u.DOut("Blockservice: Got data in datastore.\n") bdata, ok := datai.([]byte) if !ok { return nil, fmt.Errorf("data associated with %s is not a []byte", k) } + u.DOut("Got data: %v\n", bdata) return &blocks.Block{ Multihash: mh.Multihash(k), Data: bdata, }, nil } else if err == ds.ErrNotFound && s.Remote != nil { + u.DOut("Blockservice: Searching bitswap.\n") blk, err := s.Remote.GetBlock(k, time.Second*5) if err != nil { return nil, err } return blk, nil } else { + u.DOut("Blockservice GetBlock: Not found.\n") return nil, u.ErrNotFound } } From bc878f3978b9a0ed8ede6ed8a67a1a92354422da Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 29 Aug 2014 11:34:50 -0700 Subject: [PATCH 0082/3817] integrate bitswap and blockservice into the core package This commit was moved from ipfs/go-path@2ef7f7b2ec05c6064764ef3da796a56cd74476f7 --- path/path.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/path/path.go b/path/path.go index 3f1c3997e..ffcfd47c2 100644 --- a/path/path.go +++ b/path/path.go @@ -2,11 +2,12 @@ package path import ( "fmt" + "path" + "strings" + merkledag "github.com/jbenet/go-ipfs/merkledag" u "github.com/jbenet/go-ipfs/util" mh "github.com/jbenet/go-multihash" - "path" - "strings" ) // Resolver provides path resolution to IPFS @@ -19,6 +20,7 @@ type Resolver struct { // path component as a hash (key) of the first node, then resolves // all other components walking the links, with ResolveLinks. func (s *Resolver) ResolvePath(fpath string) (*merkledag.Node, error) { + u.DOut("Resolve: '%s'\n", fpath) fpath = path.Clean(fpath) parts := strings.Split(fpath, "/") @@ -39,6 +41,7 @@ func (s *Resolver) ResolvePath(fpath string) (*merkledag.Node, error) { return nil, err } + u.DOut("Resolve dag get.\n") nd, err := s.DAG.Get(u.Key(h)) if err != nil { return nil, err From cc664062bd045e670fb36aa135e47ddc2faa09ea Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Sep 2014 16:09:03 -0700 Subject: [PATCH 0083/3817] add pub/priv key code to identify, not complete yet This commit was moved from ipfs/go-ipfs-routing@4e1b2d3f8f9c0642f48336bf3c2e7ed433aba90e --- routing/dht/dht.go | 8 +++++++ routing/dht/routing.go | 50 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index b1a6e59c9..3be7a2cbb 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -2,6 +2,7 @@ package dht import ( "bytes" + "crypto/rand" "fmt" "sync" "time" @@ -637,3 +638,10 @@ func (dht *IpfsDHT) peerFromInfo(pbp *PBDHTMessage_PBPeer) (*peer.Peer, error) { return dht.network.GetConnection(peer.ID(pbp.GetId()), maddr) } + +// Builds up list of peers by requesting random peer IDs +func (dht *IpfsDHT) Bootstrap() { + id := make([]byte, 16) + rand.Read(id) + dht.FindPeer(peer.ID(id), time.Second*10) +} diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 082f737f5..5ab1b65de 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -340,6 +340,56 @@ func (dht *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, err return nil, u.ErrNotFound } +func (dht *IpfsDHT) findPeerMultiple(id peer.ID, timeout time.Duration) (*peer.Peer, error) { + // Check if were already connected to them + p, _ := dht.Find(id) + if p != nil { + return p, nil + } + + routeLevel := 0 + peers := dht.routingTables[routeLevel].NearestPeers(kb.ConvertPeerID(id), AlphaValue) + if len(peers) == 0 { + return nil, kb.ErrLookupFailure + } + + found := make(chan *peer.Peer) + after := time.After(timeout) + + for _, p := range peers { + go func(p *peer.Peer) { + pmes, err := dht.findPeerSingle(p, id, timeout, routeLevel) + if err != nil { + u.DErr("getPeer error: %v\n", err) + return + } + plist := pmes.GetPeers() + if len(plist) == 0 { + routeLevel++ + } + for _, fp := range plist { + nxtp, err := dht.peerFromInfo(fp) + if err != nil { + u.DErr("findPeer error: %v\n", err) + continue + } + + if nxtp.ID.Equal(dht.self.ID) { + found <- nxtp + return + } + } + }(p) + } + + select { + case p := <-found: + return p, nil + case <-after: + return nil, u.ErrTimeout + } +} + // Ping a peer, log the time it took func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { // Thoughts: maybe this should accept an ID and do a peer lookup? From 92b0a3b2484d73076521c9c0ec997bd5424ecd86 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Sep 2014 21:55:59 -0700 Subject: [PATCH 0084/3817] fix up tests that started failing after changing identify code This commit was moved from ipfs/go-ipfs-routing@2004a97626ec2c78f805b80254ad8cdb750c831a --- routing/dht/dht_test.go | 51 ++++++++++++++++++++++++++++------------- routing/dht/routing.go | 3 ++- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 817ecec37..c8a0859d3 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -4,6 +4,7 @@ import ( "testing" ds "github.com/jbenet/datastore.go" + identify "github.com/jbenet/go-ipfs/identify" peer "github.com/jbenet/go-ipfs/peer" swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" @@ -27,7 +28,17 @@ func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) for i := 0; i < 4; i++ { p := new(peer.Peer) p.AddAddress(addrs[i]) - p.ID = peer.ID([]byte(fmt.Sprintf("peer_%d", i))) + kp, err := identify.GenKeypair(256) + if err != nil { + panic(err) + } + p.PubKey = kp.Pub + p.PrivKey = kp.Priv + id, err := kp.ID() + if err != nil { + panic(err) + } + p.ID = id peers = append(peers, p) } @@ -46,8 +57,26 @@ func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) return addrs, peers, dhts } +func makePeer(addr *ma.Multiaddr) *peer.Peer { + p := new(peer.Peer) + p.AddAddress(addr) + kp, err := identify.GenKeypair(256) + if err != nil { + panic(err) + } + p.PrivKey = kp.Priv + p.PubKey = kp.Pub + id, err := kp.ID() + if err != nil { + panic(err) + } + + p.ID = id + return p +} + func TestPing(t *testing.T) { - u.Debug = false + u.Debug = true addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/2222") if err != nil { t.Fatal(err) @@ -57,13 +86,8 @@ func TestPing(t *testing.T) { t.Fatal(err) } - peerA := new(peer.Peer) - peerA.AddAddress(addrA) - peerA.ID = peer.ID([]byte("peerA")) - - peerB := new(peer.Peer) - peerB.AddAddress(addrB) - peerB.ID = peer.ID([]byte("peerB")) + peerA := makePeer(addrA) + peerB := makePeer(addrB) neta := swarm.NewSwarm(peerA) err = neta.Listen() @@ -108,13 +132,8 @@ func TestValueGetSet(t *testing.T) { t.Fatal(err) } - peerA := new(peer.Peer) - peerA.AddAddress(addrA) - peerA.ID = peer.ID([]byte("peerA")) - - peerB := new(peer.Peer) - peerB.AddAddress(addrB) - peerB.ID = peer.ID([]byte("peerB")) + peerA := makePeer(addrA) + peerB := makePeer(addrB) neta := swarm.NewSwarm(peerA) err = neta.Listen() diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 5ab1b65de..3b3b4875b 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -315,8 +315,9 @@ func (dht *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, err for routeLevel < len(dht.routingTables) { pmes, err := dht.findPeerSingle(p, id, timeout, routeLevel) plist := pmes.GetPeers() - if len(plist) == 0 { + if plist == nil || len(plist) == 0 { routeLevel++ + continue } found := plist[0] From b2b52f1d6a5a62e2dc1ec7200ca9c93e0cb342f3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 3 Sep 2014 20:15:10 +0000 Subject: [PATCH 0085/3817] create new crypto package and make rest of repo use it This commit was moved from ipfs/go-ipfs-routing@a7105317133b7002ec8dd9eaa2d267ac9e57ee72 --- routing/dht/dht_test.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index c8a0859d3..1efb5e77b 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -4,6 +4,7 @@ import ( "testing" ds "github.com/jbenet/datastore.go" + ci "github.com/jbenet/go-ipfs/crypto" identify "github.com/jbenet/go-ipfs/identify" peer "github.com/jbenet/go-ipfs/peer" swarm "github.com/jbenet/go-ipfs/swarm" @@ -28,13 +29,13 @@ func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) for i := 0; i < 4; i++ { p := new(peer.Peer) p.AddAddress(addrs[i]) - kp, err := identify.GenKeypair(256) + sk, pk, err := ci.GenerateKeyPair(ci.RSA, 256) if err != nil { panic(err) } - p.PubKey = kp.Pub - p.PrivKey = kp.Priv - id, err := kp.ID() + p.PubKey = pk + p.PrivKey = sk + id, err := identify.IdFromPubKey(pk) if err != nil { panic(err) } @@ -60,13 +61,13 @@ func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) func makePeer(addr *ma.Multiaddr) *peer.Peer { p := new(peer.Peer) p.AddAddress(addr) - kp, err := identify.GenKeypair(256) + sk, pk, err := ci.GenerateKeyPair(ci.RSA, 256) if err != nil { panic(err) } - p.PrivKey = kp.Priv - p.PubKey = kp.Pub - id, err := kp.ID() + p.PrivKey = sk + p.PubKey = pk + id, err := identify.IdFromPubKey(pk) if err != nil { panic(err) } From 403c9aa325af386726dae21e1b809af854a09132 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 4 Sep 2014 03:37:29 +0000 Subject: [PATCH 0086/3817] fix issue with blocks not actually being stored via dagservice This commit was moved from ipfs/go-merkledag@939ac9972d5daa9b93a55b5aa133e6002ee58a71 --- ipld/merkledag/merkledag.go | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 4d1f5d4e4..0d81ea911 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -55,6 +55,7 @@ func (n *Node) AddNodeLink(name string, that *Node) error { Name: name, Size: s, Hash: h, + Node: that, }) return nil } @@ -97,8 +98,10 @@ type DAGService struct { Blocks *bserv.BlockService } -// Put adds a node to the DAGService, storing the block in the BlockService -func (n *DAGService) Put(nd *Node) (u.Key, error) { +// Add adds a node to the DAGService, storing the block in the BlockService +func (n *DAGService) Add(nd *Node) (u.Key, error) { + k, _ := nd.Key() + u.DOut("DagService Add [%s]\n", k.Pretty()) if n == nil { return "", fmt.Errorf("DAGService is nil") } @@ -116,6 +119,26 @@ func (n *DAGService) Put(nd *Node) (u.Key, error) { return n.Blocks.AddBlock(b) } +func (n *DAGService) AddRecursive(nd *Node) error { + _, err := n.Add(nd) + if err != nil { + return err + } + + for _, link := range nd.Links { + fmt.Println("Adding link.") + if link.Node == nil { + panic("Why does this node have a nil link?\n") + } + err := n.AddRecursive(link.Node) + if err != nil { + return err + } + } + + return nil +} + // Get retrieves a node from the DAGService, fetching the block in the BlockService func (n *DAGService) Get(k u.Key) (*Node, error) { if n == nil { From e68651740a71c30fba3c49f9067aa0fdbf0f7d0e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 4 Sep 2014 03:37:29 +0000 Subject: [PATCH 0087/3817] fix issue with blocks not actually being stored via dagservice This commit was moved from ipfs/go-blockservice@136d8df3f0a13367151dd7b3e4fe72713d09a56b --- blockservice/blockservice.go | 1 - 1 file changed, 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index ceb806f9b..9c114a5d6 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -57,7 +57,6 @@ func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { if !ok { return nil, fmt.Errorf("data associated with %s is not a []byte", k) } - u.DOut("Got data: %v\n", bdata) return &blocks.Block{ Multihash: mh.Multihash(k), Data: bdata, From 41297dfd60476cd14c2cf819c5900c34edfd803a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 4 Sep 2014 20:32:46 +0000 Subject: [PATCH 0088/3817] allow peers to realize that they are actually a provider for a value This commit was moved from ipfs/go-ipfs-routing@c11a55367069ba17ca3da897cd8042889c5ff9f3 --- routing/dht/dht.go | 26 +++++++++++++++++++++++++- routing/dht/providers.go | 23 ++++++++++++++++++++++- routing/dht/providers_test.go | 20 ++++++++++++++++++++ routing/dht/routing.go | 1 + 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 routing/dht/providers_test.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index b1a6e59c9..7a88bebc0 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -2,6 +2,7 @@ package dht import ( "bytes" + "crypto/rand" "fmt" "sync" "time" @@ -59,7 +60,7 @@ func NewDHT(p *peer.Peer, net swarm.Network, dstore ds.Datastore) *IpfsDHT { dht.netChan = net.GetChannel(swarm.PBWrapper_DHT_MESSAGE) dht.datastore = dstore dht.self = p - dht.providers = NewProviderManager() + dht.providers = NewProviderManager(p.ID) dht.shutdown = make(chan struct{}) dht.routingTables = make([]*kb.RoutingTable, 3) @@ -293,7 +294,15 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *PBDHTMessage) { Response: true, } + has, err := dht.datastore.Has(ds.NewKey(pmes.GetKey())) + if err != nil { + dht.netChan.Errors <- err + } + providers := dht.providers.GetProviders(u.Key(pmes.GetKey())) + if has { + providers = append(providers, dht.self) + } if providers == nil || len(providers) == 0 { level := 0 if len(pmes.GetValue()) > 0 { @@ -637,3 +646,18 @@ func (dht *IpfsDHT) peerFromInfo(pbp *PBDHTMessage_PBPeer) (*peer.Peer, error) { return dht.network.GetConnection(peer.ID(pbp.GetId()), maddr) } + +func (dht *IpfsDHT) loadProvidableKeys() error { + kl := dht.datastore.KeyList() + for _, k := range kl { + dht.providers.AddProvider(u.Key(k.Bytes()), dht.self) + } + return nil +} + +// Builds up list of peers by requesting random peer IDs +func (dht *IpfsDHT) Bootstrap() { + id := make([]byte, 16) + rand.Read(id) + dht.FindPeer(peer.ID(id), time.Second*10) +} diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 2e89eea4c..c62755cf2 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -9,9 +9,13 @@ import ( type ProviderManager struct { providers map[u.Key][]*providerInfo + local map[u.Key]struct{} + lpeer peer.ID + getlocal chan chan []u.Key newprovs chan *addProv getprovs chan *getProv halt chan struct{} + period time.Duration } type addProv struct { @@ -24,11 +28,13 @@ type getProv struct { resp chan []*peer.Peer } -func NewProviderManager() *ProviderManager { +func NewProviderManager(local peer.ID) *ProviderManager { pm := new(ProviderManager) pm.getprovs = make(chan *getProv) pm.newprovs = make(chan *addProv) pm.providers = make(map[u.Key][]*providerInfo) + pm.getlocal = make(chan chan []u.Key) + pm.local = make(map[u.Key]struct{}) pm.halt = make(chan struct{}) go pm.run() return pm @@ -39,6 +45,9 @@ func (pm *ProviderManager) run() { for { select { case np := <-pm.newprovs: + if np.val.ID.Equal(pm.lpeer) { + pm.local[np.k] = struct{}{} + } pi := new(providerInfo) pi.Creation = time.Now() pi.Value = np.val @@ -51,6 +60,12 @@ func (pm *ProviderManager) run() { parr = append(parr, p.Value) } gp.resp <- parr + case lc := <-pm.getlocal: + var keys []u.Key + for k, _ := range pm.local { + keys = append(keys, k) + } + lc <- keys case <-tick.C: for k, provs := range pm.providers { var filtered []*providerInfo @@ -82,6 +97,12 @@ func (pm *ProviderManager) GetProviders(k u.Key) []*peer.Peer { return <-gp.resp } +func (pm *ProviderManager) GetLocal() []u.Key { + resp := make(chan []u.Key) + pm.getlocal <- resp + return <-resp +} + func (pm *ProviderManager) Halt() { pm.halt <- struct{}{} } diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go new file mode 100644 index 000000000..0cdfa4fcc --- /dev/null +++ b/routing/dht/providers_test.go @@ -0,0 +1,20 @@ +package dht + +import ( + "testing" + + "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" +) + +func TestProviderManager(t *testing.T) { + mid := peer.ID("testing") + p := NewProviderManager(mid) + a := u.Key("test") + p.AddProvider(a, &peer.Peer{}) + resp := p.GetProviders(a) + if len(resp) != 1 { + t.Fatal("Could not retrieve provider.") + } + p.Halt() +} diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 082f737f5..6ceeb9a93 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -166,6 +166,7 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { // Provide makes this node announce that it can provide a value for the given key func (dht *IpfsDHT) Provide(key u.Key) error { + dht.providers.AddProvider(key, dht.self) peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), PoolSize) if len(peers) == 0 { return kb.ErrLookupFailure From 1b69661cb439af674b465cbb78f4642109c350f9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 5 Sep 2014 20:47:55 +0000 Subject: [PATCH 0089/3817] implement a basic data format for data inside dag nodes This commit was moved from ipfs/go-merkledag@24257b36490b83d1cd79df6ed32887dd2d80855c --- ipld/merkledag/Makefile | 5 +- ipld/merkledag/dagreader.go | 98 +++++++++++++++++++++++++++++++++++++ ipld/merkledag/data.pb.go | 85 ++++++++++++++++++++++++++++++++ ipld/merkledag/data.proto | 12 +++++ ipld/merkledag/merkledag.go | 43 ++++++++++++++++ 5 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 ipld/merkledag/dagreader.go create mode 100644 ipld/merkledag/data.pb.go create mode 100644 ipld/merkledag/data.proto diff --git a/ipld/merkledag/Makefile b/ipld/merkledag/Makefile index 711f34bda..2524ed3ba 100644 --- a/ipld/merkledag/Makefile +++ b/ipld/merkledag/Makefile @@ -1,8 +1,11 @@ -all: node.pb.go +all: node.pb.go data.pb.go node.pb.go: node.proto protoc --gogo_out=. --proto_path=../../../../:/usr/local/opt/protobuf/include:. $< +data.pb.go: data.proto + protoc --go_out=. data.proto + clean: rm node.pb.go diff --git a/ipld/merkledag/dagreader.go b/ipld/merkledag/dagreader.go new file mode 100644 index 000000000..c05009ded --- /dev/null +++ b/ipld/merkledag/dagreader.go @@ -0,0 +1,98 @@ +package merkledag + +import ( + "bytes" + "errors" + "io" + + "code.google.com/p/goprotobuf/proto" +) + +var ErrIsDir = errors.New("this dag node is a directory.") + +// DagReader provides a way to easily read the data contained in a dag. +type DagReader struct { + node *Node + position int + buf *bytes.Buffer + thisData []byte +} + +func NewDagReader(n *Node) (io.Reader, error) { + pb := new(PBData) + err := proto.Unmarshal(n.Data, pb) + if err != nil { + return nil, err + } + switch pb.GetType() { + case PBData_Directory: + return nil, ErrIsDir + case PBData_File: + return &DagReader{ + node: n, + thisData: pb.GetData(), + }, nil + case PBData_Raw: + return bytes.NewBuffer(pb.GetData()), nil + default: + panic("Unrecognized node type!") + } +} + +func (dr *DagReader) precalcNextBuf() error { + if dr.position >= len(dr.node.Links) { + return io.EOF + } + nxtLink := dr.node.Links[dr.position] + nxt := nxtLink.Node + if nxt == nil { + //TODO: should use dagservice or something to get needed block + return errors.New("Link to nil node! Tree not fully expanded!") + } + pb := new(PBData) + err := proto.Unmarshal(nxt.Data, pb) + if err != nil { + return err + } + dr.position++ + + // TODO: dont assume a single layer of indirection + switch pb.GetType() { + case PBData_Directory: + panic("Why is there a directory under a file?") + case PBData_File: + //TODO: maybe have a PBData_Block type for indirect blocks? + panic("Not yet handling different layers of indirection!") + case PBData_Raw: + dr.buf = bytes.NewBuffer(pb.GetData()) + return nil + default: + panic("Unrecognized node type!") + } +} + +func (dr *DagReader) Read(b []byte) (int, error) { + if dr.buf == nil { + err := dr.precalcNextBuf() + if err != nil { + return 0, err + } + } + total := 0 + for { + n, err := dr.buf.Read(b[total:]) + total += n + if err != nil { + if err != io.EOF { + return total, err + } + } + if total == len(b) { + return total, nil + } + err = dr.precalcNextBuf() + if err != nil { + return total, err + } + } +} diff --git a/ipld/merkledag/data.pb.go b/ipld/merkledag/data.pb.go new file mode 100644 index 000000000..7b85d2903 --- /dev/null +++ b/ipld/merkledag/data.pb.go @@ -0,0 +1,85 @@ +// Code generated by protoc-gen-go. +// source: data.proto +// DO NOT EDIT! + +/* +Package merkledag is a generated protocol buffer package. + +It is generated from these files: + data.proto + +It has these top-level messages: + PBData +*/ +package merkledag + +import proto "code.google.com/p/goprotobuf/proto" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = math.Inf + +type PBData_DataType int32 + +const ( + PBData_Raw PBData_DataType = 0 + PBData_Directory PBData_DataType = 1 + PBData_File PBData_DataType = 2 +) + +var PBData_DataType_name = map[int32]string{ + 0: "Raw", + 1: "Directory", + 2: "File", +} +var PBData_DataType_value = map[string]int32{ + "Raw": 0, + "Directory": 1, + "File": 2, +} + +func (x PBData_DataType) Enum() *PBData_DataType { + p := new(PBData_DataType) + *p = x + return p +} +func (x PBData_DataType) String() string { + return proto.EnumName(PBData_DataType_name, int32(x)) +} +func (x *PBData_DataType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(PBData_DataType_value, data, "PBData_DataType") + if err != nil { + return err + } + *x = PBData_DataType(value) + return nil +} + +type PBData struct { + Type *PBData_DataType `protobuf:"varint,1,req,enum=merkledag.PBData_DataType" json:"Type,omitempty"` + Data []byte `protobuf:"bytes,2,opt" json:"Data,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PBData) Reset() { *m = PBData{} } +func (m *PBData) String() string { return proto.CompactTextString(m) } +func (*PBData) ProtoMessage() {} + +func (m *PBData) GetType() PBData_DataType { + if m != nil && m.Type != nil { + return *m.Type + } + return PBData_Raw +} + +func (m *PBData) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func init() { + proto.RegisterEnum("merkledag.PBData_DataType", PBData_DataType_name, PBData_DataType_value) +} diff --git a/ipld/merkledag/data.proto b/ipld/merkledag/data.proto new file mode 100644 index 000000000..99c8a224b --- /dev/null +++ b/ipld/merkledag/data.proto @@ -0,0 +1,12 @@ +package merkledag; + +message PBData { + enum DataType { + Raw = 0; + Directory = 1; + File = 2; + } + + required DataType Type = 1; + optional bytes Data = 2; +} diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 0d81ea911..0cd72e9b0 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -3,6 +3,8 @@ package merkledag import ( "fmt" + "code.google.com/p/goprotobuf/proto" + blocks "github.com/jbenet/go-ipfs/blocks" bserv "github.com/jbenet/go-ipfs/blockservice" u "github.com/jbenet/go-ipfs/util" @@ -152,3 +154,44 @@ func (n *DAGService) Get(k u.Key) (*Node, error) { return Decoded(b.Data) } + +func FilePBData() []byte { + pbfile := new(PBData) + typ := PBData_File + pbfile.Type = &typ + + data, err := proto.Marshal(pbfile) + if err != nil { + //this really shouldnt happen, i promise + panic(err) + } + return data +} + +func FolderPBData() []byte { + pbfile := new(PBData) + typ := PBData_Directory + pbfile.Type = &typ + + data, err := proto.Marshal(pbfile) + if err != nil { + //this really shouldnt happen, i promise + panic(err) + } + return data +} + +func WrapData(b []byte) []byte { + pbdata := new(PBData) + typ := PBData_Raw + pbdata.Data = b + pbdata.Type = &typ + + out, err := proto.Marshal(pbdata) + if err != nil { + // This shouldnt happen. seriously. + panic(err) + } + + return out +} From 3af22e03fd3e24fb5725674bdfd847fb1fbc85fa Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 6 Sep 2014 22:11:44 +0000 Subject: [PATCH 0090/3817] rework dagreader to have a dagservice for node resolution This commit was moved from ipfs/go-ipfs-routing@d84b8812aab5eac0b2b09eb4ff4403d9c7de8e09 --- routing/dht/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 1565133dc..31a99def6 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -192,7 +192,7 @@ func (dht *IpfsDHT) FindProvidersAsync(key u.Key, count int, timeout time.Durati provs := dht.providers.GetProviders(key) for _, p := range provs { count-- - // NOTE: assuming that the list of peers is unique + // NOTE: assuming that this list of peers is unique ps.Add(p) peerOut <- p if count <= 0 { From a1e66ac0b47002d412c9605192514cdebd1ed9af Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 6 Sep 2014 22:11:44 +0000 Subject: [PATCH 0091/3817] rework dagreader to have a dagservice for node resolution This commit was moved from ipfs/go-merkledag@6db5ce44bfe6911997aedf17001ffbfe46a8fadc --- ipld/merkledag/dagreader.go | 40 +++++++++++++++++++++++++++++++++---- ipld/merkledag/merkledag.go | 2 ++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/ipld/merkledag/dagreader.go b/ipld/merkledag/dagreader.go index c05009ded..5fca2f9f4 100644 --- a/ipld/merkledag/dagreader.go +++ b/ipld/merkledag/dagreader.go @@ -5,20 +5,22 @@ import ( "errors" "io" - "code.google.com/p/goprotobuf/proto" + proto "code.google.com/p/goprotobuf/proto" + u "github.com/jbenet/go-ipfs/util" ) var ErrIsDir = errors.New("this dag node is a directory.") // DagReader provides a way to easily read the data contained in a dag. type DagReader struct { + serv *DAGService node *Node position int buf *bytes.Buffer thisData []byte } -func NewDagReader(n *Node) (io.Reader, error) { +func NewDagReader(n *Node, serv *DAGService) (io.Reader, error) { pb := new(PBData) err := proto.Unmarshal(n.Data, pb) if err != nil { @@ -31,6 +33,7 @@ func NewDagReader(n *Node) (io.Reader, error) { return &DagReader{ node: n, thisData: pb.GetData(), + serv: serv, }, nil case PBData_Raw: return bytes.NewBuffer(pb.GetData()), nil @@ -46,8 +49,11 @@ func (dr *DagReader) precalcNextBuf() error { nxtLink := dr.node.Links[dr.position] nxt := nxtLink.Node if nxt == nil { - //TODO: should use dagservice or something to get needed block - return errors.New("Link to nil node! Tree not fully expanded!") + nxtNode, err := dr.serv.Get(u.Key(nxtLink.Hash)) + if err != nil { + return err + } + nxt = nxtNode } pb := new(PBData) err := proto.Unmarshal(nxt.Data, pb) @@ -96,3 +102,29 @@ func (dr *DagReader) Read(b []byte) (int, error) { } } } + +/* +func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { + switch whence { + case os.SEEK_SET: + for i := 0; i < len(dr.node.Links); i++ { + nsize := dr.node.Links[i].Size - 8 + if offset > nsize { + offset -= nsize + } else { + break + } + } + dr.position = i + err := dr.precalcNextBuf() + if err != nil { + return 0, err + } + case os.SEEK_CUR: + case os.SEEK_END: + default: + return 0, errors.New("invalid whence") + } + return 0, nil +} +*/ diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 0cd72e9b0..479959aea 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -96,6 +96,8 @@ func (n *Node) Key() (u.Key, error) { // DAGService is an IPFS Merkle DAG service. // - the root is virtual (like a forest) // - stores nodes' data in a BlockService +// TODO: should cache Nodes that are in memory, and be +// able to free some of them when vm pressure is high type DAGService struct { Blocks *bserv.BlockService } From 68f50d42f908daf1817892ca6c0ac49533c105e0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 7 Sep 2014 04:25:13 +0000 Subject: [PATCH 0092/3817] clean up merge of bren2010's crypto branch and merge into master This commit was moved from ipfs/go-ipfs-routing@be98a6e4250d888df0b2a6bdeafa54ed65824e29 --- routing/dht/dht.go | 5 ++++- routing/dht/dht_test.go | 4 ++-- routing/dht/ext_test.go | 2 -- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 7a88bebc0..883fec333 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -648,7 +648,10 @@ func (dht *IpfsDHT) peerFromInfo(pbp *PBDHTMessage_PBPeer) (*peer.Peer, error) { } func (dht *IpfsDHT) loadProvidableKeys() error { - kl := dht.datastore.KeyList() + kl, err := dht.datastore.KeyList() + if err != nil { + return err + } for _, k := range kl { dht.providers.AddProvider(u.Key(k.Bytes()), dht.self) } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 1efb5e77b..2997d3797 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -29,7 +29,7 @@ func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) for i := 0; i < 4; i++ { p := new(peer.Peer) p.AddAddress(addrs[i]) - sk, pk, err := ci.GenerateKeyPair(ci.RSA, 256) + sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) if err != nil { panic(err) } @@ -61,7 +61,7 @@ func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) func makePeer(addr *ma.Multiaddr) *peer.Peer { p := new(peer.Peer) p.AddAddress(addr) - sk, pk, err := ci.GenerateKeyPair(ci.RSA, 256) + sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) if err != nil { panic(err) } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 67b108a48..2b5f3ff72 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -189,7 +189,6 @@ func TestNotFound(t *testing.T) { // Reply with random peers to every message fn.AddHandler(func(mes *swarm.Message) *swarm.Message { - t.Log("Handling message...") pmes := new(PBDHTMessage) err := proto.Unmarshal(mes.Data, pmes) if err != nil { @@ -252,7 +251,6 @@ func TestLessThanKResponses(t *testing.T) { // Reply with random peers to every message fn.AddHandler(func(mes *swarm.Message) *swarm.Message { - t.Log("Handling message...") pmes := new(PBDHTMessage) err := proto.Unmarshal(mes.Data, pmes) if err != nil { From 4d3b3120b818d68fa43f6e95d6e239a2b589d7f3 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 9 Sep 2014 22:39:42 -0700 Subject: [PATCH 0093/3817] vendor dependencies with godep dependencies are vendored into Godeps/_workspace and commit versions are recorded in Godeps.json update datastore to e89f0511 update go.crypto This commit was moved from ipfs/go-ipfs-routing@476e5f580448dd58427336226d8f77100a1347cf --- routing/dht/Message.go | 2 +- routing/dht/dht.go | 6 +++--- routing/dht/dht_test.go | 4 ++-- routing/dht/ext_test.go | 6 +++--- routing/dht/messages.pb.go | 2 +- routing/dht/routing.go | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/routing/dht/Message.go b/routing/dht/Message.go index 4163fb485..21bd26a85 100644 --- a/routing/dht/Message.go +++ b/routing/dht/Message.go @@ -1,7 +1,7 @@ package dht import ( - "code.google.com/p/goprotobuf/proto" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" peer "github.com/jbenet/go-ipfs/peer" ) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 883fec333..83962e210 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -12,11 +12,11 @@ import ( swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" - ma "github.com/jbenet/go-multiaddr" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - ds "github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" - "code.google.com/p/goprotobuf/proto" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ) // TODO. SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-dht/index.js diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 2997d3797..9e14987d8 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -3,13 +3,13 @@ package dht import ( "testing" - ds "github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ci "github.com/jbenet/go-ipfs/crypto" identify "github.com/jbenet/go-ipfs/identify" peer "github.com/jbenet/go-ipfs/peer" swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" - ma "github.com/jbenet/go-multiaddr" "fmt" "time" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 2b5f3ff72..82337bfa6 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -5,13 +5,13 @@ import ( crand "crypto/rand" - "code.google.com/p/goprotobuf/proto" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - ds "github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" peer "github.com/jbenet/go-ipfs/peer" swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" - ma "github.com/jbenet/go-multiaddr" "time" ) diff --git a/routing/dht/messages.pb.go b/routing/dht/messages.pb.go index a2452dc28..90c936eb9 100644 --- a/routing/dht/messages.pb.go +++ b/routing/dht/messages.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package dht -import proto "code.google.com/p/goprotobuf/proto" +import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 31a99def6..383c64a98 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -6,9 +6,9 @@ import ( "errors" "time" - proto "code.google.com/p/goprotobuf/proto" + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - ma "github.com/jbenet/go-multiaddr" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" peer "github.com/jbenet/go-ipfs/peer" kb "github.com/jbenet/go-ipfs/routing/kbucket" From 35914452b727e7f77aeb6cd839b6ec27dac03826 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 9 Sep 2014 22:39:42 -0700 Subject: [PATCH 0094/3817] vendor dependencies with godep dependencies are vendored into Godeps/_workspace and commit versions are recorded in Godeps.json update datastore to e89f0511 update go.crypto This commit was moved from ipfs/go-merkledag@97ff2edc0ed80a1f1b204e7609fe02e9a0b835b6 --- ipld/merkledag/coding.go | 2 +- ipld/merkledag/dagreader.go | 2 +- ipld/merkledag/data.pb.go | 2 +- ipld/merkledag/merkledag.go | 4 ++-- ipld/merkledag/node.pb.go | 6 +++--- ipld/merkledag/nodepb_test.go | 8 ++++---- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index e27f03acc..45142ac47 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -3,7 +3,7 @@ package merkledag import ( "fmt" - mh "github.com/jbenet/go-multihash" + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/dagreader.go b/ipld/merkledag/dagreader.go index 5fca2f9f4..0aa0d2606 100644 --- a/ipld/merkledag/dagreader.go +++ b/ipld/merkledag/dagreader.go @@ -5,7 +5,7 @@ import ( "errors" "io" - proto "code.google.com/p/goprotobuf/proto" + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" u "github.com/jbenet/go-ipfs/util" ) diff --git a/ipld/merkledag/data.pb.go b/ipld/merkledag/data.pb.go index 7b85d2903..d2f97d33f 100644 --- a/ipld/merkledag/data.pb.go +++ b/ipld/merkledag/data.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package merkledag -import proto "code.google.com/p/goprotobuf/proto" +import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 479959aea..accebb708 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -3,12 +3,12 @@ package merkledag import ( "fmt" - "code.google.com/p/goprotobuf/proto" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" blocks "github.com/jbenet/go-ipfs/blocks" bserv "github.com/jbenet/go-ipfs/blockservice" u "github.com/jbenet/go-ipfs/util" - mh "github.com/jbenet/go-multihash" ) // NodeMap maps u.Keys to Nodes. diff --git a/ipld/merkledag/node.pb.go b/ipld/merkledag/node.pb.go index bbfdbcdd2..f7925c9d9 100644 --- a/ipld/merkledag/node.pb.go +++ b/ipld/merkledag/node.pb.go @@ -14,14 +14,14 @@ */ package merkledag -import proto "code.google.com/p/gogoprotobuf/proto" +import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math "math" // discarding unused import gogoproto "code.google.com/p/gogoprotobuf/gogoproto/gogo.pb" import io "io" import fmt "fmt" -import code_google_com_p_gogoprotobuf_proto "code.google.com/p/gogoprotobuf/proto" +import code_google_com_p_gogoprotobuf_proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import fmt1 "fmt" import strings "strings" @@ -29,7 +29,7 @@ import reflect "reflect" import fmt2 "fmt" import strings1 "strings" -import code_google_com_p_gogoprotobuf_proto1 "code.google.com/p/gogoprotobuf/proto" +import code_google_com_p_gogoprotobuf_proto1 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import sort "sort" import strconv "strconv" import reflect1 "reflect" diff --git a/ipld/merkledag/nodepb_test.go b/ipld/merkledag/nodepb_test.go index b7ef81c3d..103ab986f 100644 --- a/ipld/merkledag/nodepb_test.go +++ b/ipld/merkledag/nodepb_test.go @@ -17,7 +17,7 @@ package merkledag import testing "testing" import math_rand "math/rand" import time "time" -import code_google_com_p_gogoprotobuf_proto "code.google.com/p/gogoprotobuf/proto" +import code_google_com_p_gogoprotobuf_proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import testing1 "testing" import math_rand1 "math/rand" import time1 "time" @@ -25,7 +25,7 @@ import encoding_json "encoding/json" import testing2 "testing" import math_rand2 "math/rand" import time2 "time" -import code_google_com_p_gogoprotobuf_proto1 "code.google.com/p/gogoprotobuf/proto" +import code_google_com_p_gogoprotobuf_proto1 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math_rand3 "math/rand" import time3 "time" import testing3 "testing" @@ -33,7 +33,7 @@ import fmt "fmt" import math_rand4 "math/rand" import time4 "time" import testing4 "testing" -import code_google_com_p_gogoprotobuf_proto2 "code.google.com/p/gogoprotobuf/proto" +import code_google_com_p_gogoprotobuf_proto2 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math_rand5 "math/rand" import time5 "time" import testing5 "testing" @@ -42,7 +42,7 @@ import go_parser "go/parser" import math_rand6 "math/rand" import time6 "time" import testing6 "testing" -import code_google_com_p_gogoprotobuf_proto3 "code.google.com/p/gogoprotobuf/proto" +import code_google_com_p_gogoprotobuf_proto3 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" func TestPBLinkProto(t *testing.T) { popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) From 7ec74b5c1afe7202ae4926263c75925fd9bf13f9 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 9 Sep 2014 22:39:42 -0700 Subject: [PATCH 0095/3817] vendor dependencies with godep dependencies are vendored into Godeps/_workspace and commit versions are recorded in Godeps.json update datastore to e89f0511 update go.crypto This commit was moved from ipfs/go-blockservice@e35b46ebfcf4f550f6ac927f606e177573d4f35e --- blockservice/blocks_test.go | 2 +- blockservice/blockservice.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index 28034b711..c610fbd2a 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -4,7 +4,7 @@ import ( "bytes" "testing" - ds "github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" blocks "github.com/jbenet/go-ipfs/blocks" u "github.com/jbenet/go-ipfs/util" ) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 9c114a5d6..8f923c76b 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -4,12 +4,12 @@ import ( "fmt" "time" - ds "github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" bitswap "github.com/jbenet/go-ipfs/bitswap" blocks "github.com/jbenet/go-ipfs/blocks" u "github.com/jbenet/go-ipfs/util" - mh "github.com/jbenet/go-multihash" + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" ) // BlockService is a block datastore. From fe65d60df329c4553c1599868045e49e167b1a41 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 9 Sep 2014 22:39:42 -0700 Subject: [PATCH 0096/3817] vendor dependencies with godep dependencies are vendored into Godeps/_workspace and commit versions are recorded in Godeps.json update datastore to e89f0511 update go.crypto This commit was moved from ipfs/go-path@59d3e5c03913e079d5457b171ef6065991a11ebc --- path/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/path.go b/path/path.go index ffcfd47c2..a06fb98cb 100644 --- a/path/path.go +++ b/path/path.go @@ -5,9 +5,9 @@ import ( "path" "strings" + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" merkledag "github.com/jbenet/go-ipfs/merkledag" u "github.com/jbenet/go-ipfs/util" - mh "github.com/jbenet/go-multihash" ) // Resolver provides path resolution to IPFS From fd85679563af5dab4a2c232e0220521767407e47 Mon Sep 17 00:00:00 2001 From: Siraj Ravel Date: Thu, 11 Sep 2014 12:25:52 -0700 Subject: [PATCH 0097/3817] golint cleanup This commit was moved from ipfs/go-ipfs-routing@9fe70ae2f948a5b0af6e41005a54c8289603ef56 --- routing/dht/dht_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 9e14987d8..3b8f7f7d1 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -35,7 +35,7 @@ func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) } p.PubKey = pk p.PrivKey = sk - id, err := identify.IdFromPubKey(pk) + id, err := identify.IDFromPubKey(pk) if err != nil { panic(err) } @@ -67,7 +67,7 @@ func makePeer(addr *ma.Multiaddr) *peer.Peer { } p.PrivKey = sk p.PubKey = pk - id, err := identify.IdFromPubKey(pk) + id, err := identify.IDFromPubKey(pk) if err != nil { panic(err) } From 71cb14ffda829be010f129794fb6cfc4ff8ca530 Mon Sep 17 00:00:00 2001 From: Siraj Ravel Date: Thu, 11 Sep 2014 13:00:56 -0700 Subject: [PATCH 0098/3817] last golint This commit was moved from ipfs/go-merkledag@a7f7f35980363eb578688b0f3853d311c001e573 --- ipld/merkledag/dagreader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/dagreader.go b/ipld/merkledag/dagreader.go index 0aa0d2606..967ec63a4 100644 --- a/ipld/merkledag/dagreader.go +++ b/ipld/merkledag/dagreader.go @@ -9,7 +9,7 @@ import ( u "github.com/jbenet/go-ipfs/util" ) -var ErrIsDir = errors.New("this dag node is a directory.") +var ErrIsDir = errors.New("this dag node is a directory") // DagReader provides a way to easily read the data contained in a dag. type DagReader struct { From 1324b9295c0f629c3f710cbc82a6f4b9ce1785b9 Mon Sep 17 00:00:00 2001 From: Siraj Ravel Date: Sun, 14 Sep 2014 20:59:09 -0700 Subject: [PATCH 0099/3817] Test for getLocal method in DHT This commit was moved from ipfs/go-ipfs-routing@eb90de892375faea55a01ec9ab254b792b0af510 --- routing/dht/dht_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 3b8f7f7d1..68812669c 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -179,6 +179,7 @@ func TestValueGetSet(t *testing.T) { if string(val) != "world" { t.Fatalf("Expected 'world' got '%s'", string(val)) } + } func TestProvides(t *testing.T) { @@ -206,6 +207,11 @@ func TestProvides(t *testing.T) { t.Fatal(err) } + _, err = dhts[3].getLocal(u.Key("hello")) + if err != nil { + t.Fatal(err) + } + err = dhts[3].Provide(u.Key("hello")) if err != nil { t.Fatal(err) From aa386b8e034a0ce06b3f84fdf97aca9612e00429 Mon Sep 17 00:00:00 2001 From: Siraj Ravel Date: Sun, 14 Sep 2014 21:44:19 -0700 Subject: [PATCH 0100/3817] checking returned value This commit was moved from ipfs/go-ipfs-routing@43c04039baf69770fd3cc31d5af5347e0c419b82 --- routing/dht/dht_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 68812669c..f53230864 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -11,6 +11,7 @@ import ( swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" + "bytes" "fmt" "time" ) @@ -207,8 +208,8 @@ func TestProvides(t *testing.T) { t.Fatal(err) } - _, err = dhts[3].getLocal(u.Key("hello")) - if err != nil { + bits, err := dhts[3].getLocal(u.Key("hello")) + if err != nil && bytes.Equal(bits, []byte("world")) { t.Fatal(err) } From ce8e719574b6ef4a9575a80fbcc6d71addbdca1f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 15 Sep 2014 05:35:31 +0000 Subject: [PATCH 0101/3817] move first data block into top level dag node This commit was moved from ipfs/go-merkledag@6d19c80d41ef82e19a837bbbc8a5bcd2a187e47a --- ipld/merkledag/dagreader.go | 1 + ipld/merkledag/merkledag.go | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/dagreader.go b/ipld/merkledag/dagreader.go index 967ec63a4..5cf4e238e 100644 --- a/ipld/merkledag/dagreader.go +++ b/ipld/merkledag/dagreader.go @@ -34,6 +34,7 @@ func NewDagReader(n *Node, serv *DAGService) (io.Reader, error) { node: n, thisData: pb.GetData(), serv: serv, + buf: bytes.NewBuffer(pb.GetData()), }, nil case PBData_Raw: return bytes.NewBuffer(pb.GetData()), nil diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index accebb708..79530df6d 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -157,10 +157,11 @@ func (n *DAGService) Get(k u.Key) (*Node, error) { return Decoded(b.Data) } -func FilePBData() []byte { +func FilePBData(data []byte) []byte { pbfile := new(PBData) typ := PBData_File pbfile.Type = &typ + pbfile.Data = data data, err := proto.Marshal(pbfile) if err != nil { From 14b0d0a1604c383d701431cb24f5874154b789a4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 15 Sep 2014 06:07:03 +0000 Subject: [PATCH 0102/3817] add basic test for blocks package #59 This commit was moved from ipfs/go-merkledag@b897d7b535da3804e1ef89a280615c1ffcf2a613 --- ipld/merkledag/dagreader.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ipld/merkledag/dagreader.go b/ipld/merkledag/dagreader.go index 5cf4e238e..1e8a0c8b9 100644 --- a/ipld/merkledag/dagreader.go +++ b/ipld/merkledag/dagreader.go @@ -17,7 +17,6 @@ type DagReader struct { node *Node position int buf *bytes.Buffer - thisData []byte } func NewDagReader(n *Node, serv *DAGService) (io.Reader, error) { @@ -31,10 +30,9 @@ func NewDagReader(n *Node, serv *DAGService) (io.Reader, error) { return nil, ErrIsDir case PBData_File: return &DagReader{ - node: n, - thisData: pb.GetData(), - serv: serv, - buf: bytes.NewBuffer(pb.GetData()), + node: n, + serv: serv, + buf: bytes.NewBuffer(pb.GetData()), }, nil case PBData_Raw: return bytes.NewBuffer(pb.GetData()), nil @@ -63,12 +61,12 @@ func (dr *DagReader) precalcNextBuf() error { } dr.position++ - // TODO: dont assume a single layer of indirection switch pb.GetType() { case PBData_Directory: panic("Why is there a directory under a file?") case PBData_File: - //TODO: maybe have a PBData_Block type for indirect blocks? + //TODO: this *should* work, needs testing first + //return NewDagReader(nxt, dr.serv) panic("Not yet handling different layers of indirection!") case PBData_Raw: dr.buf = bytes.NewBuffer(pb.GetData()) From eb4b7f068b814a559025d2c3342887bf808a0cb6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 15 Sep 2014 20:45:36 +0000 Subject: [PATCH 0103/3817] improve cleaning up in dht tests. This commit was moved from ipfs/go-ipfs-routing@129bd922379458b9a0d923329be867bc044da190 --- routing/dht/dht_test.go | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index f53230864..b2e72eded 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -107,6 +107,8 @@ func TestPing(t *testing.T) { dhtA.Start() dhtB.Start() + defer dhtA.Halt() + defer dhtB.Halt() _, err = dhtA.Connect(addrB) if err != nil { @@ -118,9 +120,6 @@ func TestPing(t *testing.T) { if err != nil { t.Fatal(err) } - - dhtA.Halt() - dhtB.Halt() } func TestValueGetSet(t *testing.T) { @@ -153,6 +152,8 @@ func TestValueGetSet(t *testing.T) { dhtA.Start() dhtB.Start() + defer dhtA.Halt() + defer dhtB.Halt() errsa := dhtA.network.GetErrChan() errsb := dhtB.network.GetErrChan() @@ -187,6 +188,11 @@ func TestProvides(t *testing.T) { u.Debug = false addrs, _, dhts := setupDHTS(4, t) + defer func() { + for i := 0; i < 4; i++ { + dhts[i].Halt() + } + }() _, err := dhts[0].Connect(addrs[1]) if err != nil { @@ -228,15 +234,16 @@ func TestProvides(t *testing.T) { if len(provs) != 1 { t.Fatal("Didnt get back providers") } - - for i := 0; i < 4; i++ { - dhts[i].Halt() - } } func TestLayeredGet(t *testing.T) { u.Debug = false addrs, _, dhts := setupDHTS(4, t) + defer func() { + for i := 0; i < 4; i++ { + dhts[i].Halt() + } + }() _, err := dhts[0].Connect(addrs[1]) if err != nil { @@ -274,15 +281,17 @@ func TestLayeredGet(t *testing.T) { t.Fatal("Got incorrect value.") } - for i := 0; i < 4; i++ { - dhts[i].Halt() - } } func TestFindPeer(t *testing.T) { u.Debug = false addrs, peers, dhts := setupDHTS(4, t) + go func() { + for i := 0; i < 4; i++ { + dhts[i].Halt() + } + }() _, err := dhts[0].Connect(addrs[1]) if err != nil { @@ -311,8 +320,4 @@ func TestFindPeer(t *testing.T) { if !p.ID.Equal(peers[2].ID) { t.Fatal("Didnt find expected peer.") } - - for i := 0; i < 4; i++ { - dhts[i].Halt() - } } From 3918c0e1182aaeac2d78de2c50f40a682dcf76b9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 18 Sep 2014 18:32:58 +0000 Subject: [PATCH 0104/3817] fix typo that caused test failure in dht_test.go This commit was moved from ipfs/go-ipfs-routing@a14e27ae2941f8a898ad17e7a97ba33831fad455 --- routing/dht/dht_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index b2e72eded..86dace2cc 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -287,7 +287,7 @@ func TestFindPeer(t *testing.T) { u.Debug = false addrs, peers, dhts := setupDHTS(4, t) - go func() { + defer func() { for i := 0; i < 4; i++ { dhts[i].Halt() } From 367312da46cd6b410238732e2ec9b7db186bd777 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 30 Aug 2014 00:00:52 -0700 Subject: [PATCH 0105/3817] Drop -> CloseConnection This commit was moved from ipfs/go-ipfs-routing@9741fa1f614bf7e1273ca43e0e8b83468a61ee56 --- routing/dht/dht.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 83962e210..f8e5e0202 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -521,7 +521,7 @@ func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { func (dht *IpfsDHT) Update(p *peer.Peer) { for _, route := range dht.routingTables { removed := route.Update(p) - // Only drop the connection if no tables refer to this peer + // Only close the connection if no tables refer to this peer if removed != nil { found := false for _, r := range dht.routingTables { @@ -531,7 +531,7 @@ func (dht *IpfsDHT) Update(p *peer.Peer) { } } if !found { - dht.network.Drop(removed) + dht.network.CloseConnection(removed) } } } From fb9739ce3aba8833defe077bfb16c7e58698e546 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 30 Aug 2014 00:24:04 -0700 Subject: [PATCH 0106/3817] network.Find -> network.GetPeer This commit was moved from ipfs/go-ipfs-routing@0761ec6c096260e73dcff68802507cd937c86786 --- routing/dht/dht.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index f8e5e0202..074815a18 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -622,7 +622,7 @@ func (dht *IpfsDHT) addPeerList(key u.Key, peers []*PBDHTMessage_PBPeer) []*peer continue } // Dont add someone who is already on the list - p := dht.network.Find(u.Key(prov.GetId())) + p := dht.network.GetPeer(u.Key(prov.GetId())) if p == nil { u.DOut("given provider %s was not in our network already.\n", peer.ID(prov.GetId()).Pretty()) var err error From 81489380f44eabff09c40e38ca9add529e4c7536 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 14 Sep 2014 01:42:00 -0700 Subject: [PATCH 0107/3817] godeps multiaddr + swarm move. This commit was moved from ipfs/go-ipfs-routing@151fab28ae0f06c3433eec9745220751824087c2 --- routing/dht/dht.go | 2 +- routing/dht/dht_test.go | 2 +- routing/dht/ext_test.go | 2 +- routing/dht/routing.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 074815a18..d78c78b54 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -7,9 +7,9 @@ import ( "sync" "time" + swarm "github.com/jbenet/go-ipfs/net/swarm" peer "github.com/jbenet/go-ipfs/peer" kb "github.com/jbenet/go-ipfs/routing/kbucket" - swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 86dace2cc..24d4d4461 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -7,8 +7,8 @@ import ( ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ci "github.com/jbenet/go-ipfs/crypto" identify "github.com/jbenet/go-ipfs/identify" + swarm "github.com/jbenet/go-ipfs/net/swarm" peer "github.com/jbenet/go-ipfs/peer" - swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" "bytes" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 82337bfa6..fe98443ad 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -9,8 +9,8 @@ import ( ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + swarm "github.com/jbenet/go-ipfs/net/swarm" peer "github.com/jbenet/go-ipfs/peer" - swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" "time" diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 383c64a98..3b8b25f5c 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -10,9 +10,9 @@ import ( ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + swarm "github.com/jbenet/go-ipfs/net/swarm" peer "github.com/jbenet/go-ipfs/peer" kb "github.com/jbenet/go-ipfs/routing/kbucket" - swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" ) From 1a70a76247835f51a4a56be53f438a3527877569 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 14 Sep 2014 04:52:08 -0700 Subject: [PATCH 0108/3817] starting to integrate new net This commit was moved from ipfs/go-ipfs-routing@b8aa0fea62c3349c0e41db3cd5f88938334e4d1c --- routing/dht/dht.go | 8 ++------ routing/dht/dht_test.go | 6 +++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index d78c78b54..788f23512 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -7,7 +7,7 @@ import ( "sync" "time" - swarm "github.com/jbenet/go-ipfs/net/swarm" + inet "github.com/jbenet/go-ipfs/net" peer "github.com/jbenet/go-ipfs/peer" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" @@ -28,8 +28,7 @@ type IpfsDHT struct { // NOTE: (currently, only a single table is used) routingTables []*kb.RoutingTable - network swarm.Network - netChan *swarm.Chan + network inet.Network // Local peer (yourself) self *peer.Peer @@ -48,9 +47,6 @@ type IpfsDHT struct { //lock to make diagnostics work better diaglock sync.Mutex - - // listener is a server to register to listen for responses to messages - listener *swarm.MessageListener } // NewDHT creates a new DHT object with the given peer as the 'local' host diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 24d4d4461..f021835e2 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -6,7 +6,7 @@ import ( ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ci "github.com/jbenet/go-ipfs/crypto" - identify "github.com/jbenet/go-ipfs/identify" + spipe "github.com/jbenet/go-ipfs/crypto/spipe" swarm "github.com/jbenet/go-ipfs/net/swarm" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" @@ -36,7 +36,7 @@ func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) } p.PubKey = pk p.PrivKey = sk - id, err := identify.IDFromPubKey(pk) + id, err := spipe.IDFromPubKey(pk) if err != nil { panic(err) } @@ -68,7 +68,7 @@ func makePeer(addr *ma.Multiaddr) *peer.Peer { } p.PrivKey = sk p.PubKey = pk - id, err := identify.IDFromPubKey(pk) + id, err := spipe.IDFromPubKey(pk) if err != nil { panic(err) } From 0e1afdd43d8c47f6ac129c1149544c7c068bcf74 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 14 Sep 2014 14:30:52 -0700 Subject: [PATCH 0109/3817] fix(bs) remove concrete refs to swarm and dht This commit was moved from ipfs/go-ipfs-routing@e11d77d51d3b325a0f2ccea02bb177fda89ad9bd --- routing/routing.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routing/routing.go b/routing/routing.go index fdf350749..c8dc2772b 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -10,6 +10,7 @@ import ( // IpfsRouting is the routing module interface // It is implemented by things like DHTs, etc. type IpfsRouting interface { + FindProvidersAsync(u.Key, int, time.Duration) <-chan *peer.Peer // Basic Put/Get From b7bac0155f8e8d8dea1f141e929d2dfd4b8fa054 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 15 Sep 2014 04:24:03 -0700 Subject: [PATCH 0110/3817] todo(blockservice, core) add notes * to wrap datastore for ease of use * to pass a non-responsive bitswap mock rather than performing nil * checks internally This commit was moved from ipfs/go-blockservice@d308efc7e9fadd455a559077ab5dd3c94e276963 --- blockservice/blockservice.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 8f923c76b..8bd61a85d 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -35,6 +35,8 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { k := b.Key() dsk := ds.NewKey(string(k)) u.DOut("storing [%s] in datastore\n", k.Pretty()) + // TODO(brian): define a block datastore with a Put method which accepts a + // block parameter err := s.Datastore.Put(dsk, b.Data) if err != nil { return k, err From 9652236eea8ae22e025aa19fcb00a6a532d2c28a Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 15 Sep 2014 18:29:42 -0700 Subject: [PATCH 0111/3817] better protobuf Makefile with wildcard. This commit was moved from ipfs/go-ipfs-routing@c109b7c034e568d96d4ef9ac8a937adc096428ae --- routing/dht/Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 routing/dht/Makefile diff --git a/routing/dht/Makefile b/routing/dht/Makefile new file mode 100644 index 000000000..563234b1d --- /dev/null +++ b/routing/dht/Makefile @@ -0,0 +1,11 @@ + +PB = $(wildcard *.proto) +GO = $(PB:.proto=.pb.go) + +all: $(GO) + +%.pb.go: %.proto + protoc --gogo_out=. --proto_path=../../../../:/usr/local/opt/protobuf/include:. $< + +clean: + rm *.pb.go From 068dd4897e19a9a0acb86dc39165495969afd73f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 15 Sep 2014 04:35:30 -0700 Subject: [PATCH 0112/3817] refactor(blockservice) use bitswap.Exchange interface This commit was moved from ipfs/go-blockservice@8362ab4e71f3733d7dc9490ce76406a22eab7a28 --- blockservice/blockservice.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 8bd61a85d..6f5bf5104 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -16,11 +16,11 @@ import ( // It uses an internal `datastore.Datastore` instance to store values. type BlockService struct { Datastore ds.Datastore - Remote *bitswap.BitSwap + Remote bitswap.Exchange } // NewBlockService creates a BlockService with given datastore instance. -func NewBlockService(d ds.Datastore, rem *bitswap.BitSwap) (*BlockService, error) { +func NewBlockService(d ds.Datastore, rem bitswap.Exchange) (*BlockService, error) { if d == nil { return nil, fmt.Errorf("BlockService requires valid datastore") } From a0caefb47643a98e92a3e61fbe446d5a8fa8683a Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 00:52:57 -0700 Subject: [PATCH 0113/3817] simpler, clearer dht message This commit was moved from ipfs/go-ipfs-routing@17616d5924a9460277271a2e28ae69d264020c43 --- routing/dht/Message.go | 44 ++++-------- routing/dht/messages.pb.go | 138 +++++++++++++++++++------------------ routing/dht/messages.proto | 28 +++++--- 3 files changed, 102 insertions(+), 108 deletions(-) diff --git a/routing/dht/Message.go b/routing/dht/Message.go index 21bd26a85..210fd6968 100644 --- a/routing/dht/Message.go +++ b/routing/dht/Message.go @@ -5,19 +5,8 @@ import ( peer "github.com/jbenet/go-ipfs/peer" ) -// Message is a a helper struct which makes working with protbuf types easier -type Message struct { - Type PBDHTMessage_MessageType - Key string - Value []byte - Response bool - ID string - Success bool - Peers []*peer.Peer -} - -func peerInfo(p *peer.Peer) *PBDHTMessage_PBPeer { - pbp := new(PBDHTMessage_PBPeer) +func peerInfo(p *peer.Peer) *Message_Peer { + pbp := new(Message_Peer) if len(p.Addresses) == 0 || p.Addresses[0] == nil { pbp.Addr = proto.String("") } else { @@ -33,23 +22,16 @@ func peerInfo(p *peer.Peer) *PBDHTMessage_PBPeer { return pbp } -// ToProtobuf takes a Message and produces a protobuf with it. -// TODO: building the protobuf message this way is a little wasteful -// Unused fields wont be omitted, find a better way to do this -func (m *Message) ToProtobuf() *PBDHTMessage { - pmes := new(PBDHTMessage) - if m.Value != nil { - pmes.Value = m.Value - } - - pmes.Type = &m.Type - pmes.Key = &m.Key - pmes.Response = &m.Response - pmes.Id = &m.ID - pmes.Success = &m.Success - for _, p := range m.Peers { - pmes.Peers = append(pmes.Peers, peerInfo(p)) - } +// GetClusterLevel gets and adjusts the cluster level on the message. +// a +/- 1 adjustment is needed to distinguish a valid first level (1) and +// default "no value" protobuf behavior (0) +func (m *Message) GetClusterLevel() int32 { + return m.GetClusterLevelRaw() - 1 +} - return pmes +// SetClusterLevel adjusts and sets the cluster level on the message. +// a +/- 1 adjustment is needed to distinguish a valid first level (1) and +// default "no value" protobuf behavior (0) +func (m *Message) SetClusterLevel(level int32) { + m.ClusterLevelRaw = &level } diff --git a/routing/dht/messages.pb.go b/routing/dht/messages.pb.go index 90c936eb9..d85e81905 100644 --- a/routing/dht/messages.pb.go +++ b/routing/dht/messages.pb.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-go. +// Code generated by protoc-gen-gogo. // source: messages.proto // DO NOT EDIT! @@ -9,30 +9,32 @@ It is generated from these files: messages.proto It has these top-level messages: - PBDHTMessage + Message */ package dht -import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" +import proto "code.google.com/p/gogoprotobuf/proto" +import json "encoding/json" import math "math" -// Reference imports to suppress errors if they are not otherwise used. +// Reference proto, json, and math imports to suppress error if they are not otherwise used. var _ = proto.Marshal +var _ = &json.SyntaxError{} var _ = math.Inf -type PBDHTMessage_MessageType int32 +type Message_MessageType int32 const ( - PBDHTMessage_PUT_VALUE PBDHTMessage_MessageType = 0 - PBDHTMessage_GET_VALUE PBDHTMessage_MessageType = 1 - PBDHTMessage_ADD_PROVIDER PBDHTMessage_MessageType = 2 - PBDHTMessage_GET_PROVIDERS PBDHTMessage_MessageType = 3 - PBDHTMessage_FIND_NODE PBDHTMessage_MessageType = 4 - PBDHTMessage_PING PBDHTMessage_MessageType = 5 - PBDHTMessage_DIAGNOSTIC PBDHTMessage_MessageType = 6 + Message_PUT_VALUE Message_MessageType = 0 + Message_GET_VALUE Message_MessageType = 1 + Message_ADD_PROVIDER Message_MessageType = 2 + Message_GET_PROVIDERS Message_MessageType = 3 + Message_FIND_NODE Message_MessageType = 4 + Message_PING Message_MessageType = 5 + Message_DIAGNOSTIC Message_MessageType = 6 ) -var PBDHTMessage_MessageType_name = map[int32]string{ +var Message_MessageType_name = map[int32]string{ 0: "PUT_VALUE", 1: "GET_VALUE", 2: "ADD_PROVIDER", @@ -41,7 +43,7 @@ var PBDHTMessage_MessageType_name = map[int32]string{ 5: "PING", 6: "DIAGNOSTIC", } -var PBDHTMessage_MessageType_value = map[string]int32{ +var Message_MessageType_value = map[string]int32{ "PUT_VALUE": 0, "GET_VALUE": 1, "ADD_PROVIDER": 2, @@ -51,105 +53,107 @@ var PBDHTMessage_MessageType_value = map[string]int32{ "DIAGNOSTIC": 6, } -func (x PBDHTMessage_MessageType) Enum() *PBDHTMessage_MessageType { - p := new(PBDHTMessage_MessageType) +func (x Message_MessageType) Enum() *Message_MessageType { + p := new(Message_MessageType) *p = x return p } -func (x PBDHTMessage_MessageType) String() string { - return proto.EnumName(PBDHTMessage_MessageType_name, int32(x)) +func (x Message_MessageType) String() string { + return proto.EnumName(Message_MessageType_name, int32(x)) } -func (x *PBDHTMessage_MessageType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(PBDHTMessage_MessageType_value, data, "PBDHTMessage_MessageType") +func (x *Message_MessageType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Message_MessageType_value, data, "Message_MessageType") if err != nil { return err } - *x = PBDHTMessage_MessageType(value) + *x = Message_MessageType(value) return nil } -type PBDHTMessage struct { - Type *PBDHTMessage_MessageType `protobuf:"varint,1,req,name=type,enum=dht.PBDHTMessage_MessageType" json:"type,omitempty"` - Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` - Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` - Id *string `protobuf:"bytes,4,req,name=id" json:"id,omitempty"` - Response *bool `protobuf:"varint,5,opt,name=response" json:"response,omitempty"` - Success *bool `protobuf:"varint,6,opt,name=success" json:"success,omitempty"` - Peers []*PBDHTMessage_PBPeer `protobuf:"bytes,7,rep,name=peers" json:"peers,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *PBDHTMessage) Reset() { *m = PBDHTMessage{} } -func (m *PBDHTMessage) String() string { return proto.CompactTextString(m) } -func (*PBDHTMessage) ProtoMessage() {} - -func (m *PBDHTMessage) GetType() PBDHTMessage_MessageType { +type Message struct { + // defines what type of message it is. + Type *Message_MessageType `protobuf:"varint,1,req,name=type,enum=dht.Message_MessageType" json:"type,omitempty"` + // defines what coral cluster level this query/response belongs to. + ClusterLevelRaw *int32 `protobuf:"varint,10,opt,name=clusterLevelRaw" json:"clusterLevelRaw,omitempty"` + // Used to specify the key associated with this message. + // PUT_VALUE, GET_VALUE, ADD_PROVIDER, GET_PROVIDERS + Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` + // Used to return a value + // PUT_VALUE, GET_VALUE + Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` + // Used to return peers closer to a key in a query + // GET_VALUE, GET_PROVIDERS, FIND_NODE + CloserPeers []*Message_Peer `protobuf:"bytes,8,rep,name=closerPeers" json:"closerPeers,omitempty"` + // Used to return Providers + // GET_VALUE, ADD_PROVIDER, GET_PROVIDERS + ProviderPeers []*Message_Peer `protobuf:"bytes,9,rep,name=providerPeers" json:"providerPeers,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Message) Reset() { *m = Message{} } +func (m *Message) String() string { return proto.CompactTextString(m) } +func (*Message) ProtoMessage() {} + +func (m *Message) GetType() Message_MessageType { if m != nil && m.Type != nil { return *m.Type } - return PBDHTMessage_PUT_VALUE + return Message_PUT_VALUE } -func (m *PBDHTMessage) GetKey() string { +func (m *Message) GetClusterLevelRaw() int32 { + if m != nil && m.ClusterLevelRaw != nil { + return *m.ClusterLevelRaw + } + return 0 +} + +func (m *Message) GetKey() string { if m != nil && m.Key != nil { return *m.Key } return "" } -func (m *PBDHTMessage) GetValue() []byte { +func (m *Message) GetValue() []byte { if m != nil { return m.Value } return nil } -func (m *PBDHTMessage) GetId() string { - if m != nil && m.Id != nil { - return *m.Id - } - return "" -} - -func (m *PBDHTMessage) GetResponse() bool { - if m != nil && m.Response != nil { - return *m.Response - } - return false -} - -func (m *PBDHTMessage) GetSuccess() bool { - if m != nil && m.Success != nil { - return *m.Success +func (m *Message) GetCloserPeers() []*Message_Peer { + if m != nil { + return m.CloserPeers } - return false + return nil } -func (m *PBDHTMessage) GetPeers() []*PBDHTMessage_PBPeer { +func (m *Message) GetProviderPeers() []*Message_Peer { if m != nil { - return m.Peers + return m.ProviderPeers } return nil } -type PBDHTMessage_PBPeer struct { +type Message_Peer struct { Id *string `protobuf:"bytes,1,req,name=id" json:"id,omitempty"` Addr *string `protobuf:"bytes,2,req,name=addr" json:"addr,omitempty"` XXX_unrecognized []byte `json:"-"` } -func (m *PBDHTMessage_PBPeer) Reset() { *m = PBDHTMessage_PBPeer{} } -func (m *PBDHTMessage_PBPeer) String() string { return proto.CompactTextString(m) } -func (*PBDHTMessage_PBPeer) ProtoMessage() {} +func (m *Message_Peer) Reset() { *m = Message_Peer{} } +func (m *Message_Peer) String() string { return proto.CompactTextString(m) } +func (*Message_Peer) ProtoMessage() {} -func (m *PBDHTMessage_PBPeer) GetId() string { +func (m *Message_Peer) GetId() string { if m != nil && m.Id != nil { return *m.Id } return "" } -func (m *PBDHTMessage_PBPeer) GetAddr() string { +func (m *Message_Peer) GetAddr() string { if m != nil && m.Addr != nil { return *m.Addr } @@ -157,5 +161,5 @@ func (m *PBDHTMessage_PBPeer) GetAddr() string { } func init() { - proto.RegisterEnum("dht.PBDHTMessage_MessageType", PBDHTMessage_MessageType_name, PBDHTMessage_MessageType_value) + proto.RegisterEnum("dht.Message_MessageType", Message_MessageType_name, Message_MessageType_value) } diff --git a/routing/dht/messages.proto b/routing/dht/messages.proto index c2c5cc30d..3c33f9382 100644 --- a/routing/dht/messages.proto +++ b/routing/dht/messages.proto @@ -2,7 +2,7 @@ package dht; //run `protoc --go_out=. *.proto` to generate -message PBDHTMessage { +message Message { enum MessageType { PUT_VALUE = 0; GET_VALUE = 1; @@ -13,22 +13,30 @@ message PBDHTMessage { DIAGNOSTIC = 6; } - message PBPeer { + message Peer { required string id = 1; required string addr = 2; } + // defines what type of message it is. required MessageType type = 1; + + // defines what coral cluster level this query/response belongs to. + optional int32 clusterLevelRaw = 10; + + // Used to specify the key associated with this message. + // PUT_VALUE, GET_VALUE, ADD_PROVIDER, GET_PROVIDERS optional string key = 2; - optional bytes value = 3; - // Unique ID of this message, used to match queries with responses - required string id = 4; + // Used to return a value + // PUT_VALUE, GET_VALUE + optional bytes value = 3; - // Signals whether or not this message is a response to another message - optional bool response = 5; - optional bool success = 6; + // Used to return peers closer to a key in a query + // GET_VALUE, GET_PROVIDERS, FIND_NODE + repeated Peer closerPeers = 8; - // Used for returning peers from queries (normally, peers closer to X) - repeated PBPeer peers = 7; + // Used to return Providers + // GET_VALUE, ADD_PROVIDER, GET_PROVIDERS + repeated Peer providerPeers = 9; } From 7f12ccf9ddd2cf2df08c39c79306350a72d5b2e5 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 00:56:40 -0700 Subject: [PATCH 0114/3817] starting on dht-- msg handler This commit was moved from ipfs/go-ipfs-routing@1b9544070173ec14afcf42a7f1689772b8134a37 --- routing/dht/dht.go | 150 +++++++++++++++++++++++++++------------------ 1 file changed, 90 insertions(+), 60 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 788f23512..5ee59cd67 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -3,18 +3,20 @@ package dht import ( "bytes" "crypto/rand" + "errors" "fmt" "sync" "time" inet "github.com/jbenet/go-ipfs/net" + msg "github.com/jbenet/go-ipfs/net/message" peer "github.com/jbenet/go-ipfs/peer" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ) @@ -28,7 +30,9 @@ type IpfsDHT struct { // NOTE: (currently, only a single table is used) routingTables []*kb.RoutingTable + // the network interface. service network inet.Network + sender inet.Sender // Local peer (yourself) self *peer.Peer @@ -50,12 +54,13 @@ type IpfsDHT struct { } // NewDHT creates a new DHT object with the given peer as the 'local' host -func NewDHT(p *peer.Peer, net swarm.Network, dstore ds.Datastore) *IpfsDHT { +func NewDHT(p *peer.Peer, net inet.Network, sender inet.Sender, dstore ds.Datastore) *IpfsDHT { dht := new(IpfsDHT) dht.network = net - dht.netChan = net.GetChannel(swarm.PBWrapper_DHT_MESSAGE) + dht.sender = sender dht.datastore = dstore dht.self = p + dht.providers = NewProviderManager(p.ID) dht.shutdown = make(chan struct{}) @@ -63,21 +68,32 @@ func NewDHT(p *peer.Peer, net swarm.Network, dstore ds.Datastore) *IpfsDHT { dht.routingTables[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*30) dht.routingTables[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*100) dht.routingTables[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Hour) - dht.listener = swarm.NewMessageListener() dht.birth = time.Now() return dht } // Start up background goroutines needed by the DHT func (dht *IpfsDHT) Start() { - go dht.handleMessages() + panic("the service is already started. rmv this method") } // Connect to a new peer at the given address, ping and add to the routing table func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { maddrstr, _ := addr.String() u.DOut("Connect to new peer: %s\n", maddrstr) - npeer, err := dht.network.ConnectNew(addr) + + // TODO(jbenet,whyrusleeping) + // + // Connect should take in a Peer (with ID). In a sense, we shouldn't be + // allowing connections to random multiaddrs without knowing who we're + // speaking to (i.e. peer.ID). In terms of moving around simple addresses + // -- instead of an (ID, Addr) pair -- we can use: + // + // /ip4/10.20.30.40/tcp/1234/ipfs/Qxhxxchxzcncxnzcnxzcxzm + // + npeer := &peer.Peer{} + npeer.AddAddress(addr) + err := dht.network.DialPeer(npeer) if err != nil { return nil, err } @@ -94,63 +110,77 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { return npeer, nil } -// Read in all messages from swarm and handle them appropriately -// NOTE: this function is just a quick sketch -func (dht *IpfsDHT) handleMessages() { - u.DOut("Begin message handling routine\n") +// HandleMessage implements the inet.Handler interface. +func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) (msg.NetMessage, error) { - errs := dht.network.GetErrChan() - for { - select { - case mes, ok := <-dht.netChan.Incoming: - if !ok { - u.DOut("handleMessages closing, bad recv on incoming\n") - return - } - pmes := new(PBDHTMessage) - err := proto.Unmarshal(mes.Data, pmes) - if err != nil { - u.PErr("Failed to decode protobuf message: %s\n", err) - continue - } + mData := mes.Data() + if mData == nil { + return nil, errors.New("message did not include Data") + } - dht.Update(mes.Peer) + mPeer := mes.Peer() + if mPeer == nil { + return nil, errors.New("message did not include a Peer") + } - // Note: not sure if this is the correct place for this - if pmes.GetResponse() { - dht.listener.Respond(pmes.GetId(), mes) - continue - } - // - - u.DOut("[peer: %s]\nGot message type: '%s' [id = %x, from = %s]\n", - dht.self.ID.Pretty(), - PBDHTMessage_MessageType_name[int32(pmes.GetType())], - pmes.GetId(), mes.Peer.ID.Pretty()) - switch pmes.GetType() { - case PBDHTMessage_GET_VALUE: - go dht.handleGetValue(mes.Peer, pmes) - case PBDHTMessage_PUT_VALUE: - go dht.handlePutValue(mes.Peer, pmes) - case PBDHTMessage_FIND_NODE: - go dht.handleFindPeer(mes.Peer, pmes) - case PBDHTMessage_ADD_PROVIDER: - go dht.handleAddProvider(mes.Peer, pmes) - case PBDHTMessage_GET_PROVIDERS: - go dht.handleGetProviders(mes.Peer, pmes) - case PBDHTMessage_PING: - go dht.handlePing(mes.Peer, pmes) - case PBDHTMessage_DIAGNOSTIC: - go dht.handleDiagnostic(mes.Peer, pmes) - default: - u.PErr("Recieved invalid message type") - } + // deserialize msg + pmes := new(Message) + err := proto.Unmarshal(mData, pmes) + if err != nil { + return nil, fmt.Errorf("Failed to decode protobuf message: %v\n", err) + } - case err := <-errs: - u.PErr("dht err: %s\n", err) - case <-dht.shutdown: - return - } + // update the peer (on valid msgs only) + dht.Update(mPeer) + + // Print out diagnostic + u.DOut("[peer: %s]\nGot message type: '%s' [from = %s]\n", + dht.self.ID.Pretty(), + Message_MessageType_name[int32(pmes.GetType())], mPeer.ID.Pretty()) + + // get handler for this msg type. + var resp *Message + handler := dht.handlerForMsgType(pmes.GetType()) + if handler == nil { + return nil, errors.New("Recieved invalid message type") + } + + // dispatch handler. + rpmes, err := handler(mPeer, pmes) + if err != nil { + return nil, err + } + + // serialize response msg + rmes, err := msg.FromObject(mPeer, rpmes) + if err != nil { + return nil, fmt.Errorf("Failed to encode protobuf message: %v\n", err) + } + + return rmes, nil +} + +// dhthandler specifies the signature of functions that handle DHT messages. +type dhtHandler func(*peer.Peer, *Message) (*Message, error) + +func (dht *IpfsDHT) handlerForMsgType(t Message_MessageType) dhtHandler { + switch t { + case Message_GET_VALUE: + return dht.handleGetValue + // case Message_PUT_VALUE: + // return dht.handlePutValue + // case Message_FIND_NODE: + // return dht.handleFindPeer + // case Message_ADD_PROVIDER: + // return dht.handleAddProvider + // case Message_GET_PROVIDERS: + // return dht.handleGetProviders + // case Message_PING: + // return dht.handlePing + // case Message_DIAGNOSTIC: + // return dht.handleDiagnostic + default: + return nil } } From 7a2294876ba6c01c712c7081a251486facb393d6 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 00:57:20 -0700 Subject: [PATCH 0115/3817] handleGetValue This commit was moved from ipfs/go-ipfs-routing@5c3a52c2044e064a5df6591d424aed1857dc1c8c --- routing/dht/dht.go | 131 ++++++++++++++++++++++++++------------------- 1 file changed, 77 insertions(+), 54 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 5ee59cd67..6d105ddfc 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -185,75 +185,98 @@ func (dht *IpfsDHT) handlerForMsgType(t Message_MessageType) dhtHandler { } func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) error { - pmes := Message{ - Type: PBDHTMessage_PUT_VALUE, - Key: key, + typ := Message_PUT_VALUE + pmes := &Message{ + Type: &typ, + Key: &key, Value: value, - ID: swarm.GenerateMessageID(), } - mes := swarm.NewMessage(p, pmes.ToProtobuf()) - dht.netChan.Outgoing <- mes - return nil + mes, err := msg.FromObject(p, pmes) + if err != nil { + return err + } + return dht.sender.SendMessage(context.TODO(), mes) } -func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { +func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error) { u.DOut("handleGetValue for key: %s\n", pmes.GetKey()) - dskey := ds.NewKey(pmes.GetKey()) + + // setup response resp := &Message{ - Response: true, - ID: pmes.GetId(), - Key: pmes.GetKey(), + Type: pmes.Type, + Key: pmes.Key, + } + + // first, is the key even a key? + key := pmes.GetKey() + if key == "" { + return nil, errors.New("handleGetValue but no key was provided.") } + + // let's first check if we have the value locally. + dskey := ds.NewKey(pmes.GetKey()) iVal, err := dht.datastore.Get(dskey) + + // if we got an unexpected error, bail. + if err != ds.ErrNotFound { + return nil, err + } + + // if we have the value, respond with it! if err == nil { u.DOut("handleGetValue success!\n") - resp.Success = true - resp.Value = iVal.([]byte) - } else if err == ds.ErrNotFound { - // Check if we know any providers for the requested value - provs := dht.providers.GetProviders(u.Key(pmes.GetKey())) - if len(provs) > 0 { - u.DOut("handleGetValue returning %d provider[s]\n", len(provs)) - resp.Peers = provs - resp.Success = true - } else { - // No providers? - // Find closest peer on given cluster to desired key and reply with that info - - level := 0 - if len(pmes.GetValue()) < 1 { - // TODO: maybe return an error? Defaulting isnt a good idea IMO - u.PErr("handleGetValue: no routing level specified, assuming 0\n") - } else { - level = int(pmes.GetValue()[0]) // Using value field to specify cluster level - } - u.DOut("handleGetValue searching level %d clusters\n", level) - - closer := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) - if closer.ID.Equal(dht.self.ID) { - u.DOut("Attempted to return self! this shouldnt happen...\n") - resp.Peers = nil - goto out - } - // If this peer is closer than the one from the table, return nil - if kb.Closer(dht.self.ID, closer.ID, u.Key(pmes.GetKey())) { - resp.Peers = nil - u.DOut("handleGetValue could not find a closer node than myself.\n") - } else { - u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty()) - resp.Peers = []*peer.Peer{closer} - } + byts, ok := iVal.([]byte) + if !ok { + return nil, fmt.Errorf("datastore had non byte-slice value for %v", dskey) } - } else { - //temp: what other errors can a datastore return? - panic(err) + + resp.Value = byts + return resp, nil } -out: - mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.netChan.Outgoing <- mes + // if we know any providers for the requested value, return those. + provs := dht.providers.GetProviders(u.Key(pmes.GetKey())) + if len(provs) > 0 { + u.DOut("handleGetValue returning %d provider[s]\n", len(provs)) + resp.ProviderPeers = provs + return resp, nil + } + + // Find closest peer on given cluster to desired key and reply with that info + // TODO: this should probably be decomposed. + + // stored levels are > 1, to distinguish missing levels. + level := pmes.GetClusterLevel() + if level < 0 { + // TODO: maybe return an error? Defaulting isnt a good idea IMO + u.PErr("handleGetValue: no routing level specified, assuming 0\n") + level = 0 + } + u.DOut("handleGetValue searching level %d clusters\n", level) + + ck := kb.ConvertKey(u.Key(pmes.GetKey())) + closer := dht.routingTables[level].NearestPeer(ck) + + // if closer peer is self, return nil + if closer.ID.Equal(dht.self.ID) { + u.DOut("Attempted to return self! this shouldnt happen...\n") + resp.CloserPeers = nil + return resp, nil + } + + // if self is closer than the one from the table, return nil + if kb.Closer(dht.self.ID, closer.ID, u.Key(pmes.GetKey())) { + u.DOut("handleGetValue could not find a closer node than myself.\n") + resp.CloserPeers = nil + return resp, nil + } + + // we got a closer peer, it seems. return it. + u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty()) + resp.CloserPeers = []*peer.Peer{closer} + return resp, nil } // Store a value in this peer local storage From c12a704e3d69d58c04b325c73ba05d717aac1a7a Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 00:57:46 -0700 Subject: [PATCH 0116/3817] refactor symbol This commit was moved from ipfs/go-ipfs-routing@7ec38e713c3b09cd065ae0806f3bff2f53462974 --- routing/dht/dht.go | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 6d105ddfc..c13ed9a98 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -280,7 +280,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error } // Store a value in this peer local storage -func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *PBDHTMessage) { +func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *Message) { dht.dslock.Lock() defer dht.dslock.Unlock() dskey := ds.NewKey(pmes.GetKey()) @@ -291,7 +291,7 @@ func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *PBDHTMessage) { } } -func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *PBDHTMessage) { +func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *Message) { u.DOut("[%s] Responding to ping from [%s]!\n", dht.self.ID.Pretty(), p.ID.Pretty()) resp := Message{ Type: pmes.GetType(), @@ -302,7 +302,7 @@ func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *PBDHTMessage) { dht.netChan.Outgoing <- swarm.NewMessage(p, resp.ToProtobuf()) } -func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) { +func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *Message) { resp := Message{ Type: pmes.GetType(), ID: pmes.GetId(), @@ -335,9 +335,9 @@ func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) { resp.Success = true } -func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *PBDHTMessage) { +func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *Message) { resp := Message{ - Type: PBDHTMessage_GET_PROVIDERS, + Type: Message_GET_PROVIDERS, Key: pmes.GetKey(), ID: pmes.GetId(), Response: true, @@ -378,7 +378,7 @@ type providerInfo struct { Value *peer.Peer } -func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *PBDHTMessage) { +func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *Message) { key := u.Key(pmes.GetKey()) u.DOut("[%s] Adding [%s] as a provider for '%s'\n", dht.self.ID.Pretty(), p.ID.Pretty(), peer.ID(key).Pretty()) dht.providers.AddProvider(key, p) @@ -393,7 +393,7 @@ func (dht *IpfsDHT) Halt() { } // NOTE: not yet finished, low priority -func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { +func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *Message) { seq := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) listenChan := dht.listener.Listen(pmes.GetId(), len(seq), time.Second*30) @@ -415,7 +415,7 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { //Timeout, return what we have goto out case reqResp := <-listenChan: - pmesOut := new(PBDHTMessage) + pmesOut := new(Message) err := proto.Unmarshal(reqResp.Data, pmesOut) if err != nil { // It broke? eh, whatever, keep going @@ -428,7 +428,7 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { out: resp := Message{ - Type: PBDHTMessage_DIAGNOSTIC, + Type: Message_DIAGNOSTIC, ID: pmes.GetId(), Value: buf.Bytes(), Response: true, @@ -481,9 +481,9 @@ func (dht *IpfsDHT) getValueOrPeers(p *peer.Peer, key u.Key, timeout time.Durati } // getValueSingle simply performs the get value RPC with the given parameters -func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duration, level int) (*PBDHTMessage, error) { +func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duration, level int) (*Message, error) { pmes := Message{ - Type: PBDHTMessage_GET_VALUE, + Type: Message_GET_VALUE, Key: string(key), Value: []byte{byte(level)}, ID: swarm.GenerateMessageID(), @@ -507,7 +507,7 @@ func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duratio } roundtrip := time.Since(t) resp.Peer.SetLatency(roundtrip) - pmesOut := new(PBDHTMessage) + pmesOut := new(Message) err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { return nil, err @@ -521,7 +521,7 @@ func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duratio // one to get the value from? Or just connect to one at a time until we get a // successful connection and request the value from it? func (dht *IpfsDHT) getFromPeerList(key u.Key, timeout time.Duration, - peerlist []*PBDHTMessage_PBPeer, level int) ([]byte, error) { + peerlist []*Message_PBPeer, level int) ([]byte, error) { for _, pinfo := range peerlist { p, _ := dht.Find(peer.ID(pinfo.GetId())) if p == nil { @@ -597,9 +597,9 @@ func (dht *IpfsDHT) Find(id peer.ID) (*peer.Peer, *kb.RoutingTable) { return nil, nil } -func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Duration, level int) (*PBDHTMessage, error) { +func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Duration, level int) (*Message, error) { pmes := Message{ - Type: PBDHTMessage_FIND_NODE, + Type: Message_FIND_NODE, Key: string(id), ID: swarm.GenerateMessageID(), Value: []byte{byte(level)}, @@ -617,7 +617,7 @@ func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Durati case resp := <-listenChan: roundtrip := time.Since(t) resp.Peer.SetLatency(roundtrip) - pmesOut := new(PBDHTMessage) + pmesOut := new(Message) err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { return nil, err @@ -633,9 +633,9 @@ func (dht *IpfsDHT) printTables() { } } -func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, timeout time.Duration) (*PBDHTMessage, error) { +func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, timeout time.Duration) (*Message, error) { pmes := Message{ - Type: PBDHTMessage_GET_PROVIDERS, + Type: Message_GET_PROVIDERS, Key: string(key), ID: swarm.GenerateMessageID(), Value: []byte{byte(level)}, @@ -652,7 +652,7 @@ func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, time return nil, u.ErrTimeout case resp := <-listenChan: u.DOut("FindProviders: got response.\n") - pmesOut := new(PBDHTMessage) + pmesOut := new(Message) err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { return nil, err @@ -663,7 +663,7 @@ func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, time } // TODO: Could be done async -func (dht *IpfsDHT) addPeerList(key u.Key, peers []*PBDHTMessage_PBPeer) []*peer.Peer { +func (dht *IpfsDHT) addPeerList(key u.Key, peers []*Message_PBPeer) []*peer.Peer { var provArr []*peer.Peer for _, prov := range peers { // Dont add outselves to the list @@ -687,7 +687,7 @@ func (dht *IpfsDHT) addPeerList(key u.Key, peers []*PBDHTMessage_PBPeer) []*peer return provArr } -func (dht *IpfsDHT) peerFromInfo(pbp *PBDHTMessage_PBPeer) (*peer.Peer, error) { +func (dht *IpfsDHT) peerFromInfo(pbp *Message_PBPeer) (*peer.Peer, error) { maddr, err := ma.NewMultiaddr(pbp.GetAddr()) if err != nil { return nil, err From 817abc8de40ce427aa98f7aa54e9a588bf4f8a70 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 00:58:08 -0700 Subject: [PATCH 0117/3817] lint nit This commit was moved from ipfs/go-ipfs-routing@56064b74c3223807dfa552170349b644c78d6212 --- routing/dht/dht.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index c13ed9a98..645ffb236 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -211,7 +211,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error // first, is the key even a key? key := pmes.GetKey() if key == "" { - return nil, errors.New("handleGetValue but no key was provided.") + return nil, errors.New("handleGetValue but no key was provided") } // let's first check if we have the value locally. From 37a746754c139a560df2b876d7387e71e14b5525 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 01:09:34 -0700 Subject: [PATCH 0118/3817] ping + find peer This commit was moved from ipfs/go-ipfs-routing@ea2170d069ad7f8abb415433878a1c58d12dcb13 --- routing/dht/Message.go | 18 ++++++++++++-- routing/dht/dht.go | 56 +++++++++++++++--------------------------- 2 files changed, 36 insertions(+), 38 deletions(-) diff --git a/routing/dht/Message.go b/routing/dht/Message.go index 210fd6968..bf592799d 100644 --- a/routing/dht/Message.go +++ b/routing/dht/Message.go @@ -3,9 +3,10 @@ package dht import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" peer "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" ) -func peerInfo(p *peer.Peer) *Message_Peer { +func peerToPBPeer(p *peer.Peer) *Message_Peer { pbp := new(Message_Peer) if len(p.Addresses) == 0 || p.Addresses[0] == nil { pbp.Addr = proto.String("") @@ -22,11 +23,24 @@ func peerInfo(p *peer.Peer) *Message_Peer { return pbp } +func peersToPBPeers(peers []*peer.Peer) []*Message_Peer { + pbpeers = make([]*Message_Peer, len(peers)) + for i, p := range peers { + pbpeers[i] = peerToPBPeer(p) + } + return pbpeers +} + // GetClusterLevel gets and adjusts the cluster level on the message. // a +/- 1 adjustment is needed to distinguish a valid first level (1) and // default "no value" protobuf behavior (0) func (m *Message) GetClusterLevel() int32 { - return m.GetClusterLevelRaw() - 1 + level := m.GetClusterLevelRaw() - 1 + if level < 0 { + u.PErr("handleGetValue: no routing level specified, assuming 0\n") + level = 0 + } + return level } // SetClusterLevel adjusts and sets the cluster level on the message. diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 645ffb236..144841442 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -169,14 +169,14 @@ func (dht *IpfsDHT) handlerForMsgType(t Message_MessageType) dhtHandler { return dht.handleGetValue // case Message_PUT_VALUE: // return dht.handlePutValue - // case Message_FIND_NODE: - // return dht.handleFindPeer + case Message_FIND_NODE: + return dht.handleFindPeer // case Message_ADD_PROVIDER: // return dht.handleAddProvider // case Message_GET_PROVIDERS: // return dht.handleGetProviders - // case Message_PING: - // return dht.handlePing + case Message_PING: + return dht.handlePing // case Message_DIAGNOSTIC: // return dht.handleDiagnostic default: @@ -240,7 +240,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error provs := dht.providers.GetProviders(u.Key(pmes.GetKey())) if len(provs) > 0 { u.DOut("handleGetValue returning %d provider[s]\n", len(provs)) - resp.ProviderPeers = provs + resp.ProviderPeers = peersToPBPeers(provs) return resp, nil } @@ -249,11 +249,6 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error // stored levels are > 1, to distinguish missing levels. level := pmes.GetClusterLevel() - if level < 0 { - // TODO: maybe return an error? Defaulting isnt a good idea IMO - u.PErr("handleGetValue: no routing level specified, assuming 0\n") - level = 0 - } u.DOut("handleGetValue searching level %d clusters\n", level) ck := kb.ConvertKey(u.Key(pmes.GetKey())) @@ -275,7 +270,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error // we got a closer peer, it seems. return it. u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty()) - resp.CloserPeers = []*peer.Peer{closer} + resp.CloserPeers = peersToPBPeers([]*peer.Peer{closer}) return resp, nil } @@ -291,48 +286,37 @@ func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *Message) { } } -func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *Message) { +func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *Message) (*Message, error) { u.DOut("[%s] Responding to ping from [%s]!\n", dht.self.ID.Pretty(), p.ID.Pretty()) - resp := Message{ - Type: pmes.GetType(), - Response: true, - ID: pmes.GetId(), - } - - dht.netChan.Outgoing <- swarm.NewMessage(p, resp.ToProtobuf()) + return &Message{Type: pmes.Type}, nil } -func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *Message) { - resp := Message{ - Type: pmes.GetType(), - ID: pmes.GetId(), - Response: true, - } - defer func() { - mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.netChan.Outgoing <- mes - }() - level := pmes.GetValue()[0] +func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *Message) (*Message, error) { + resp := &Message{Type: pmes.Type} + + level := pmes.GetClusterLevel() u.DOut("handleFindPeer: searching for '%s'\n", peer.ID(pmes.GetKey()).Pretty()) - closest := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) + + ck := kb.ConvertKey(u.Key(pmes.GetKey())) + closest := dht.routingTables[level].NearestPeer(ck) if closest == nil { u.PErr("handleFindPeer: could not find anything.\n") - return + return resp, nil } if len(closest.Addresses) == 0 { u.PErr("handleFindPeer: no addresses for connected peer...\n") - return + return resp, nil } // If the found peer further away than this peer... if kb.Closer(dht.self.ID, closest.ID, u.Key(pmes.GetKey())) { - return + return resp, nil } u.DOut("handleFindPeer: sending back '%s'\n", closest.ID.Pretty()) - resp.Peers = []*peer.Peer{closest} - resp.Success = true + resp.CloserPeers = peersToPBPeers([]*peer.Peer{closest}) + return resp, nil } func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *Message) { From 4584fdd35d357d8fcdfd63c5eaaafebe6b84718f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 01:54:53 -0700 Subject: [PATCH 0119/3817] refactor peer distance search + handleGetProviders This commit was moved from ipfs/go-ipfs-routing@194a54bfd7525ecef66e004089b32164bc3b4de7 --- routing/dht/dht.go | 113 +++++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 51 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 144841442..e0553c9a7 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -245,24 +245,8 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error } // Find closest peer on given cluster to desired key and reply with that info - // TODO: this should probably be decomposed. - - // stored levels are > 1, to distinguish missing levels. - level := pmes.GetClusterLevel() - u.DOut("handleGetValue searching level %d clusters\n", level) - - ck := kb.ConvertKey(u.Key(pmes.GetKey())) - closer := dht.routingTables[level].NearestPeer(ck) - - // if closer peer is self, return nil - if closer.ID.Equal(dht.self.ID) { - u.DOut("Attempted to return self! this shouldnt happen...\n") - resp.CloserPeers = nil - return resp, nil - } - - // if self is closer than the one from the table, return nil - if kb.Closer(dht.self.ID, closer.ID, u.Key(pmes.GetKey())) { + closer := dht.betterPeerToQuery(pmes) + if closer == nil { u.DOut("handleGetValue could not find a closer node than myself.\n") resp.CloserPeers = nil return resp, nil @@ -293,12 +277,15 @@ func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *Message) (*Message, error) { func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *Message) (*Message, error) { resp := &Message{Type: pmes.Type} + var closest *peer.Peer - level := pmes.GetClusterLevel() - u.DOut("handleFindPeer: searching for '%s'\n", peer.ID(pmes.GetKey()).Pretty()) + // if looking for self... special case where we send it on CloserPeers. + if peer.ID(pmes.GetKey()).Equal(dht.self.ID) { + closest = dht.self + } else { + closest = dht.betterPeerToQuery(pmes) + } - ck := kb.ConvertKey(u.Key(pmes.GetKey())) - closest := dht.routingTables[level].NearestPeer(ck) if closest == nil { u.PErr("handleFindPeer: could not find anything.\n") return resp, nil @@ -309,52 +296,42 @@ func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *Message) (*Message, error return resp, nil } - // If the found peer further away than this peer... - if kb.Closer(dht.self.ID, closest.ID, u.Key(pmes.GetKey())) { - return resp, nil - } - u.DOut("handleFindPeer: sending back '%s'\n", closest.ID.Pretty()) resp.CloserPeers = peersToPBPeers([]*peer.Peer{closest}) return resp, nil } -func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *Message) { - resp := Message{ - Type: Message_GET_PROVIDERS, - Key: pmes.GetKey(), - ID: pmes.GetId(), - Response: true, +func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *Message) (*Message, error) { + resp := &Message{ + Type: pmes.Type, + Key: pmes.Key, } + // check if we have this value, to add ourselves as provider. has, err := dht.datastore.Has(ds.NewKey(pmes.GetKey())) - if err != nil { - dht.netChan.Errors <- err + if err != nil && err != ds.ErrNotFound { + u.PErr("unexpected datastore error: %v\n", err) + has = false } + // setup providers providers := dht.providers.GetProviders(u.Key(pmes.GetKey())) if has { providers = append(providers, dht.self) } - if providers == nil || len(providers) == 0 { - level := 0 - if len(pmes.GetValue()) > 0 { - level = int(pmes.GetValue()[0]) - } - closer := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) - if kb.Closer(dht.self.ID, closer.ID, u.Key(pmes.GetKey())) { - resp.Peers = nil - } else { - resp.Peers = []*peer.Peer{closer} - } - } else { - resp.Peers = providers - resp.Success = true + // if we've got providers, send thos those. + if providers != nil && len(providers) > 0 { + resp.ProviderPeers = peersToPBPeers(providers) } - mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.netChan.Outgoing <- mes + // Also send closer peers. + closer := dht.betterPeerToQuery(pmes) + if closer != nil { + resp.CloserPeers = peersToPBPeers([]*peer.Peer{closer}) + } + + return resp, nil } type providerInfo struct { @@ -671,6 +648,40 @@ func (dht *IpfsDHT) addPeerList(key u.Key, peers []*Message_PBPeer) []*peer.Peer return provArr } +// nearestPeerToQuery returns the routing tables closest peers. +func (dht *IpfsDHT) nearestPeerToQuery(pmes *Message) *peer.Peer { + level := pmes.GetClusterLevel() + cluster := dht.routingTables[level] + + key := u.Key(pmes.GetKey()) + closer := cluster.NearestPeer(kb.ConvertKey(key)) + return closer +} + +// betterPeerToQuery returns nearestPeerToQuery, but iff closer than self. +func (dht *IpfsDHT) betterPeerToQuery(pmes *Message) *peer.Peer { + closer := dht.nearestPeerToQuery(pmes) + + // no node? nil + if closer == nil { + return nil + } + + // == to self? nil + if closer.ID.Equal(dht.self.ID) { + u.DOut("Attempted to return self! this shouldnt happen...\n") + return nil + } + + // self is closer? nil + if kb.Closer(dht.self.ID, closer.ID, key) { + return nil + } + + // ok seems like a closer node. + return closer +} + func (dht *IpfsDHT) peerFromInfo(pbp *Message_PBPeer) (*peer.Peer, error) { maddr, err := ma.NewMultiaddr(pbp.GetAddr()) if err != nil { From 31f2c1b6ce232f56560dd3e991675af6a419497a Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 02:04:11 -0700 Subject: [PATCH 0120/3817] comment out diagnostic it'll have to change lots since the listener is gone This commit was moved from ipfs/go-ipfs-routing@b2d8b3fc4c6159e9101400f1d7b852604258304e --- routing/dht/dht.go | 84 ++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index e0553c9a7..91ad57307 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -1,7 +1,6 @@ package dht import ( - "bytes" "crypto/rand" "errors" "fmt" @@ -341,62 +340,67 @@ type providerInfo struct { func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *Message) { key := u.Key(pmes.GetKey()) - u.DOut("[%s] Adding [%s] as a provider for '%s'\n", dht.self.ID.Pretty(), p.ID.Pretty(), peer.ID(key).Pretty()) + u.DOut("[%s] Adding [%s] as a provider for '%s'\n", + dht.self.ID.Pretty(), p.ID.Pretty(), peer.ID(key).Pretty()) dht.providers.AddProvider(key, p) } // Halt stops all communications from this peer and shut down +// TODO -- remove this in favor of context func (dht *IpfsDHT) Halt() { dht.shutdown <- struct{}{} dht.network.Close() dht.providers.Halt() - dht.listener.Halt() } // NOTE: not yet finished, low priority -func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *Message) { +func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *Message) (*Message, error) { seq := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) - listenChan := dht.listener.Listen(pmes.GetId(), len(seq), time.Second*30) for _, ps := range seq { - mes := swarm.NewMessage(ps, pmes) - dht.netChan.Outgoing <- mes - } - - buf := new(bytes.Buffer) - di := dht.getDiagInfo() - buf.Write(di.Marshal()) - - // NOTE: this shouldnt be a hardcoded value - after := time.After(time.Second * 20) - count := len(seq) - for count > 0 { - select { - case <-after: - //Timeout, return what we have - goto out - case reqResp := <-listenChan: - pmesOut := new(Message) - err := proto.Unmarshal(reqResp.Data, pmesOut) - if err != nil { - // It broke? eh, whatever, keep going - continue - } - buf.Write(reqResp.Data) - count-- + mes, err := msg.FromObject(ps, pmes) + if err != nil { + u.PErr("handleDiagnostics error creating message: %v\n", err) + continue } + // dht.sender.SendRequest(context.TODO(), mes) } + return nil, errors.New("not yet ported back") -out: - resp := Message{ - Type: Message_DIAGNOSTIC, - ID: pmes.GetId(), - Value: buf.Bytes(), - Response: true, - } - - mes := swarm.NewMessage(p, resp.ToProtobuf()) - dht.netChan.Outgoing <- mes + // buf := new(bytes.Buffer) + // di := dht.getDiagInfo() + // buf.Write(di.Marshal()) + // + // // NOTE: this shouldnt be a hardcoded value + // after := time.After(time.Second * 20) + // count := len(seq) + // for count > 0 { + // select { + // case <-after: + // //Timeout, return what we have + // goto out + // case reqResp := <-listenChan: + // pmesOut := new(Message) + // err := proto.Unmarshal(reqResp.Data, pmesOut) + // if err != nil { + // // It broke? eh, whatever, keep going + // continue + // } + // buf.Write(reqResp.Data) + // count-- + // } + // } + // + // out: + // resp := Message{ + // Type: Message_DIAGNOSTIC, + // ID: pmes.GetId(), + // Value: buf.Bytes(), + // Response: true, + // } + // + // mes := swarm.NewMessage(p, resp.ToProtobuf()) + // dht.netChan.Outgoing <- mes } func (dht *IpfsDHT) getValueOrPeers(p *peer.Peer, key u.Key, timeout time.Duration, level int) ([]byte, []*peer.Peer, error) { From 8486f72e2ecaf822e9945758d5c5a77bd340526d Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 02:07:59 -0700 Subject: [PATCH 0121/3817] moved handlers to own file This commit was moved from ipfs/go-ipfs-routing@94a3fd409d85923bd535cde26bc856a38c6e0712 --- routing/dht/dht.go | 244 ------------------------------------- routing/dht/handlers.go | 259 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 259 insertions(+), 244 deletions(-) create mode 100644 routing/dht/handlers.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 91ad57307..509076413 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -159,250 +159,6 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) (msg. return rmes, nil } -// dhthandler specifies the signature of functions that handle DHT messages. -type dhtHandler func(*peer.Peer, *Message) (*Message, error) - -func (dht *IpfsDHT) handlerForMsgType(t Message_MessageType) dhtHandler { - switch t { - case Message_GET_VALUE: - return dht.handleGetValue - // case Message_PUT_VALUE: - // return dht.handlePutValue - case Message_FIND_NODE: - return dht.handleFindPeer - // case Message_ADD_PROVIDER: - // return dht.handleAddProvider - // case Message_GET_PROVIDERS: - // return dht.handleGetProviders - case Message_PING: - return dht.handlePing - // case Message_DIAGNOSTIC: - // return dht.handleDiagnostic - default: - return nil - } -} - -func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) error { - typ := Message_PUT_VALUE - pmes := &Message{ - Type: &typ, - Key: &key, - Value: value, - } - - mes, err := msg.FromObject(p, pmes) - if err != nil { - return err - } - return dht.sender.SendMessage(context.TODO(), mes) -} - -func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error) { - u.DOut("handleGetValue for key: %s\n", pmes.GetKey()) - - // setup response - resp := &Message{ - Type: pmes.Type, - Key: pmes.Key, - } - - // first, is the key even a key? - key := pmes.GetKey() - if key == "" { - return nil, errors.New("handleGetValue but no key was provided") - } - - // let's first check if we have the value locally. - dskey := ds.NewKey(pmes.GetKey()) - iVal, err := dht.datastore.Get(dskey) - - // if we got an unexpected error, bail. - if err != ds.ErrNotFound { - return nil, err - } - - // if we have the value, respond with it! - if err == nil { - u.DOut("handleGetValue success!\n") - - byts, ok := iVal.([]byte) - if !ok { - return nil, fmt.Errorf("datastore had non byte-slice value for %v", dskey) - } - - resp.Value = byts - return resp, nil - } - - // if we know any providers for the requested value, return those. - provs := dht.providers.GetProviders(u.Key(pmes.GetKey())) - if len(provs) > 0 { - u.DOut("handleGetValue returning %d provider[s]\n", len(provs)) - resp.ProviderPeers = peersToPBPeers(provs) - return resp, nil - } - - // Find closest peer on given cluster to desired key and reply with that info - closer := dht.betterPeerToQuery(pmes) - if closer == nil { - u.DOut("handleGetValue could not find a closer node than myself.\n") - resp.CloserPeers = nil - return resp, nil - } - - // we got a closer peer, it seems. return it. - u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty()) - resp.CloserPeers = peersToPBPeers([]*peer.Peer{closer}) - return resp, nil -} - -// Store a value in this peer local storage -func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *Message) { - dht.dslock.Lock() - defer dht.dslock.Unlock() - dskey := ds.NewKey(pmes.GetKey()) - err := dht.datastore.Put(dskey, pmes.GetValue()) - if err != nil { - // For now, just panic, handle this better later maybe - panic(err) - } -} - -func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *Message) (*Message, error) { - u.DOut("[%s] Responding to ping from [%s]!\n", dht.self.ID.Pretty(), p.ID.Pretty()) - return &Message{Type: pmes.Type}, nil -} - -func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *Message) (*Message, error) { - resp := &Message{Type: pmes.Type} - var closest *peer.Peer - - // if looking for self... special case where we send it on CloserPeers. - if peer.ID(pmes.GetKey()).Equal(dht.self.ID) { - closest = dht.self - } else { - closest = dht.betterPeerToQuery(pmes) - } - - if closest == nil { - u.PErr("handleFindPeer: could not find anything.\n") - return resp, nil - } - - if len(closest.Addresses) == 0 { - u.PErr("handleFindPeer: no addresses for connected peer...\n") - return resp, nil - } - - u.DOut("handleFindPeer: sending back '%s'\n", closest.ID.Pretty()) - resp.CloserPeers = peersToPBPeers([]*peer.Peer{closest}) - return resp, nil -} - -func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *Message) (*Message, error) { - resp := &Message{ - Type: pmes.Type, - Key: pmes.Key, - } - - // check if we have this value, to add ourselves as provider. - has, err := dht.datastore.Has(ds.NewKey(pmes.GetKey())) - if err != nil && err != ds.ErrNotFound { - u.PErr("unexpected datastore error: %v\n", err) - has = false - } - - // setup providers - providers := dht.providers.GetProviders(u.Key(pmes.GetKey())) - if has { - providers = append(providers, dht.self) - } - - // if we've got providers, send thos those. - if providers != nil && len(providers) > 0 { - resp.ProviderPeers = peersToPBPeers(providers) - } - - // Also send closer peers. - closer := dht.betterPeerToQuery(pmes) - if closer != nil { - resp.CloserPeers = peersToPBPeers([]*peer.Peer{closer}) - } - - return resp, nil -} - -type providerInfo struct { - Creation time.Time - Value *peer.Peer -} - -func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *Message) { - key := u.Key(pmes.GetKey()) - u.DOut("[%s] Adding [%s] as a provider for '%s'\n", - dht.self.ID.Pretty(), p.ID.Pretty(), peer.ID(key).Pretty()) - dht.providers.AddProvider(key, p) -} - -// Halt stops all communications from this peer and shut down -// TODO -- remove this in favor of context -func (dht *IpfsDHT) Halt() { - dht.shutdown <- struct{}{} - dht.network.Close() - dht.providers.Halt() -} - -// NOTE: not yet finished, low priority -func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *Message) (*Message, error) { - seq := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) - - for _, ps := range seq { - mes, err := msg.FromObject(ps, pmes) - if err != nil { - u.PErr("handleDiagnostics error creating message: %v\n", err) - continue - } - // dht.sender.SendRequest(context.TODO(), mes) - } - return nil, errors.New("not yet ported back") - - // buf := new(bytes.Buffer) - // di := dht.getDiagInfo() - // buf.Write(di.Marshal()) - // - // // NOTE: this shouldnt be a hardcoded value - // after := time.After(time.Second * 20) - // count := len(seq) - // for count > 0 { - // select { - // case <-after: - // //Timeout, return what we have - // goto out - // case reqResp := <-listenChan: - // pmesOut := new(Message) - // err := proto.Unmarshal(reqResp.Data, pmesOut) - // if err != nil { - // // It broke? eh, whatever, keep going - // continue - // } - // buf.Write(reqResp.Data) - // count-- - // } - // } - // - // out: - // resp := Message{ - // Type: Message_DIAGNOSTIC, - // ID: pmes.GetId(), - // Value: buf.Bytes(), - // Response: true, - // } - // - // mes := swarm.NewMessage(p, resp.ToProtobuf()) - // dht.netChan.Outgoing <- mes -} - func (dht *IpfsDHT) getValueOrPeers(p *peer.Peer, key u.Key, timeout time.Duration, level int) ([]byte, []*peer.Peer, error) { pmes, err := dht.getValueSingle(p, key, timeout, level) if err != nil { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go new file mode 100644 index 000000000..86593d1bf --- /dev/null +++ b/routing/dht/handlers.go @@ -0,0 +1,259 @@ +package dht + +import ( + "errors" + "fmt" + "time" + + msg "github.com/jbenet/go-ipfs/net/message" + peer "github.com/jbenet/go-ipfs/peer" + kb "github.com/jbenet/go-ipfs/routing/kbucket" + u "github.com/jbenet/go-ipfs/util" + + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" +) + +// dhthandler specifies the signature of functions that handle DHT messages. +type dhtHandler func(*peer.Peer, *Message) (*Message, error) + +func (dht *IpfsDHT) handlerForMsgType(t Message_MessageType) dhtHandler { + switch t { + case Message_GET_VALUE: + return dht.handleGetValue + // case Message_PUT_VALUE: + // return dht.handlePutValue + case Message_FIND_NODE: + return dht.handleFindPeer + // case Message_ADD_PROVIDER: + // return dht.handleAddProvider + // case Message_GET_PROVIDERS: + // return dht.handleGetProviders + case Message_PING: + return dht.handlePing + // case Message_DIAGNOSTIC: + // return dht.handleDiagnostic + default: + return nil + } +} + +func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) error { + typ := Message_PUT_VALUE + pmes := &Message{ + Type: &typ, + Key: &key, + Value: value, + } + + mes, err := msg.FromObject(p, pmes) + if err != nil { + return err + } + return dht.sender.SendMessage(context.TODO(), mes) +} + +func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error) { + u.DOut("handleGetValue for key: %s\n", pmes.GetKey()) + + // setup response + resp := &Message{ + Type: pmes.Type, + Key: pmes.Key, + } + + // first, is the key even a key? + key := pmes.GetKey() + if key == "" { + return nil, errors.New("handleGetValue but no key was provided") + } + + // let's first check if we have the value locally. + dskey := ds.NewKey(pmes.GetKey()) + iVal, err := dht.datastore.Get(dskey) + + // if we got an unexpected error, bail. + if err != ds.ErrNotFound { + return nil, err + } + + // if we have the value, respond with it! + if err == nil { + u.DOut("handleGetValue success!\n") + + byts, ok := iVal.([]byte) + if !ok { + return nil, fmt.Errorf("datastore had non byte-slice value for %v", dskey) + } + + resp.Value = byts + return resp, nil + } + + // if we know any providers for the requested value, return those. + provs := dht.providers.GetProviders(u.Key(pmes.GetKey())) + if len(provs) > 0 { + u.DOut("handleGetValue returning %d provider[s]\n", len(provs)) + resp.ProviderPeers = peersToPBPeers(provs) + return resp, nil + } + + // Find closest peer on given cluster to desired key and reply with that info + closer := dht.betterPeerToQuery(pmes) + if closer == nil { + u.DOut("handleGetValue could not find a closer node than myself.\n") + resp.CloserPeers = nil + return resp, nil + } + + // we got a closer peer, it seems. return it. + u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty()) + resp.CloserPeers = peersToPBPeers([]*peer.Peer{closer}) + return resp, nil +} + +// Store a value in this peer local storage +func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *Message) { + dht.dslock.Lock() + defer dht.dslock.Unlock() + dskey := ds.NewKey(pmes.GetKey()) + err := dht.datastore.Put(dskey, pmes.GetValue()) + if err != nil { + // For now, just panic, handle this better later maybe + panic(err) + } +} + +func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *Message) (*Message, error) { + u.DOut("[%s] Responding to ping from [%s]!\n", dht.self.ID.Pretty(), p.ID.Pretty()) + return &Message{Type: pmes.Type}, nil +} + +func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *Message) (*Message, error) { + resp := &Message{Type: pmes.Type} + var closest *peer.Peer + + // if looking for self... special case where we send it on CloserPeers. + if peer.ID(pmes.GetKey()).Equal(dht.self.ID) { + closest = dht.self + } else { + closest = dht.betterPeerToQuery(pmes) + } + + if closest == nil { + u.PErr("handleFindPeer: could not find anything.\n") + return resp, nil + } + + if len(closest.Addresses) == 0 { + u.PErr("handleFindPeer: no addresses for connected peer...\n") + return resp, nil + } + + u.DOut("handleFindPeer: sending back '%s'\n", closest.ID.Pretty()) + resp.CloserPeers = peersToPBPeers([]*peer.Peer{closest}) + return resp, nil +} + +func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *Message) (*Message, error) { + resp := &Message{ + Type: pmes.Type, + Key: pmes.Key, + } + + // check if we have this value, to add ourselves as provider. + has, err := dht.datastore.Has(ds.NewKey(pmes.GetKey())) + if err != nil && err != ds.ErrNotFound { + u.PErr("unexpected datastore error: %v\n", err) + has = false + } + + // setup providers + providers := dht.providers.GetProviders(u.Key(pmes.GetKey())) + if has { + providers = append(providers, dht.self) + } + + // if we've got providers, send thos those. + if providers != nil && len(providers) > 0 { + resp.ProviderPeers = peersToPBPeers(providers) + } + + // Also send closer peers. + closer := dht.betterPeerToQuery(pmes) + if closer != nil { + resp.CloserPeers = peersToPBPeers([]*peer.Peer{closer}) + } + + return resp, nil +} + +type providerInfo struct { + Creation time.Time + Value *peer.Peer +} + +func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *Message) { + key := u.Key(pmes.GetKey()) + u.DOut("[%s] Adding [%s] as a provider for '%s'\n", + dht.self.ID.Pretty(), p.ID.Pretty(), peer.ID(key).Pretty()) + dht.providers.AddProvider(key, p) +} + +// Halt stops all communications from this peer and shut down +// TODO -- remove this in favor of context +func (dht *IpfsDHT) Halt() { + dht.shutdown <- struct{}{} + dht.network.Close() + dht.providers.Halt() +} + +// NOTE: not yet finished, low priority +func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *Message) (*Message, error) { + seq := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) + + for _, ps := range seq { + mes, err := msg.FromObject(ps, pmes) + if err != nil { + u.PErr("handleDiagnostics error creating message: %v\n", err) + continue + } + // dht.sender.SendRequest(context.TODO(), mes) + } + return nil, errors.New("not yet ported back") + + // buf := new(bytes.Buffer) + // di := dht.getDiagInfo() + // buf.Write(di.Marshal()) + // + // // NOTE: this shouldnt be a hardcoded value + // after := time.After(time.Second * 20) + // count := len(seq) + // for count > 0 { + // select { + // case <-after: + // //Timeout, return what we have + // goto out + // case reqResp := <-listenChan: + // pmesOut := new(Message) + // err := proto.Unmarshal(reqResp.Data, pmesOut) + // if err != nil { + // // It broke? eh, whatever, keep going + // continue + // } + // buf.Write(reqResp.Data) + // count-- + // } + // } + // + // out: + // resp := Message{ + // Type: Message_DIAGNOSTIC, + // ID: pmes.GetId(), + // Value: buf.Bytes(), + // Response: true, + // } + // + // mes := swarm.NewMessage(p, resp.ToProtobuf()) + // dht.netChan.Outgoing <- mes +} From 4240102243c29ffe4f084af04017fd5a7f4c8314 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 15 Sep 2014 04:50:56 -0700 Subject: [PATCH 0122/3817] refac(bitswap:interface) GetBlock, HaveBlock -> Block, HasBlock This commit was moved from ipfs/go-blockservice@246c55618a4836cd7e0054f098cf1cc2f5848f28 --- blockservice/blockservice.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 6f5bf5104..7008a5b2b 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -42,7 +42,7 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { return k, err } if s.Remote != nil { - err = s.Remote.HaveBlock(b) + err = s.Remote.HasBlock(b) } return k, err } @@ -65,7 +65,7 @@ func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { }, nil } else if err == ds.ErrNotFound && s.Remote != nil { u.DOut("Blockservice: Searching bitswap.\n") - blk, err := s.Remote.GetBlock(k, time.Second*5) + blk, err := s.Remote.Block(k, time.Second*5) if err != nil { return nil, err } From 23e0931992aa6776444b21f9aa3ccd46ba44bb38 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 02:16:57 -0700 Subject: [PATCH 0123/3817] uncomment all handlers This commit was moved from ipfs/go-ipfs-routing@562522951df82b96a7f77768c90989e5dd06f018 --- routing/dht/handlers.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 86593d1bf..909ec839a 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -21,18 +21,18 @@ func (dht *IpfsDHT) handlerForMsgType(t Message_MessageType) dhtHandler { switch t { case Message_GET_VALUE: return dht.handleGetValue - // case Message_PUT_VALUE: - // return dht.handlePutValue + case Message_PUT_VALUE: + return dht.handlePutValue case Message_FIND_NODE: return dht.handleFindPeer - // case Message_ADD_PROVIDER: - // return dht.handleAddProvider - // case Message_GET_PROVIDERS: - // return dht.handleGetProviders + case Message_ADD_PROVIDER: + return dht.handleAddProvider + case Message_GET_PROVIDERS: + return dht.handleGetProviders case Message_PING: return dht.handlePing - // case Message_DIAGNOSTIC: - // return dht.handleDiagnostic + case Message_DIAGNOSTIC: + return dht.handleDiagnostic default: return nil } From 650ded671547200a2d9258daf079c1242dd93b61 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 02:26:46 -0700 Subject: [PATCH 0124/3817] check type assertion `v.([]byte)` coming from a datastore can panic. `byt, ok := v.([]byte)` to be safe. @whyrusleeping This commit was moved from ipfs/go-ipfs-routing@177acbbcf0be2ca6b5c9b032deb660e350d51abf --- routing/dht/dht.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 509076413..c8b3bdaba 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -280,7 +280,12 @@ func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { if err != nil { return nil, err } - return v.([]byte), nil + + byt, ok := v.([]byte) + if !ok { + return byt, errors.New("value stored in datastore not []byte") + } + return byt, nil } func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { From c658ab0bb5f9cf3d052be47f5136e27fd2069150 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 02:43:11 -0700 Subject: [PATCH 0125/3817] getValueSingle using SendRequest This commit was moved from ipfs/go-ipfs-routing@fbdc727918223372a5aa6a3149c247534cb77269 --- routing/dht/dht.go | 71 +++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index c8b3bdaba..da58089f3 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -159,8 +159,37 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) (msg. return rmes, nil } -func (dht *IpfsDHT) getValueOrPeers(p *peer.Peer, key u.Key, timeout time.Duration, level int) ([]byte, []*peer.Peer, error) { - pmes, err := dht.getValueSingle(p, key, timeout, level) +// sendRequest sends out a request using dht.sender, but also makes sure to +// measure the RTT for latency measurements. +func (dht *IpfsDHT) sendRequest(ctx context.Context, p *peer.Peer, pmes *Message) (*Message, error) { + + mes, err := msg.FromObject(p, pmes) + if err != nil { + return nil, err + } + + start := time.Now() + + rmes, err := dht.sender.SendRequest(ctx, mes) + if err != nil { + return nil, err + } + + rtt := time.Since(start) + rmes.Peer().SetLatency(rtt) + + rpmes := new(Message) + if err := proto.Unmarshal(rmes.Data(), rpmes); err != nil { + return nil, err + } + + return rpmes, nil +} + +func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, + key u.Key, level int) ([]byte, []*peer.Peer, error) { + + pmes, err := dht.getValueSingle(ctx, p, key, level) if err != nil { return nil, nil, err } @@ -202,39 +231,15 @@ func (dht *IpfsDHT) getValueOrPeers(p *peer.Peer, key u.Key, timeout time.Durati } // getValueSingle simply performs the get value RPC with the given parameters -func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duration, level int) (*Message, error) { - pmes := Message{ - Type: Message_GET_VALUE, - Key: string(key), - Value: []byte{byte(level)}, - ID: swarm.GenerateMessageID(), - } - responseChan := dht.listener.Listen(pmes.ID, 1, time.Minute) +func (dht *IpfsDHT) getValueSingle(ctx context.Context, p *peer.Peer, + key u.Key, level int) (*Message, error) { - mes := swarm.NewMessage(p, pmes.ToProtobuf()) - t := time.Now() - dht.netChan.Outgoing <- mes + typ := Message_GET_VALUE + skey := string(key) + pmes := &Message{Type: &typ, Key: &skey} + pmes.SetClusterLevel(int32(level)) - // Wait for either the response or a timeout - timeup := time.After(timeout) - select { - case <-timeup: - dht.listener.Unlisten(pmes.ID) - return nil, u.ErrTimeout - case resp, ok := <-responseChan: - if !ok { - u.PErr("response channel closed before timeout, please investigate.\n") - return nil, u.ErrTimeout - } - roundtrip := time.Since(t) - resp.Peer.SetLatency(roundtrip) - pmesOut := new(Message) - err := proto.Unmarshal(resp.Data, pmesOut) - if err != nil { - return nil, err - } - return pmesOut, nil - } + return dht.sendRequest(ctx, p, pmes) } // TODO: Im not certain on this implementation, we get a list of peers/providers From 165372a29a254f9df63112a29a6500431509a639 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 15 Sep 2014 07:59:02 -0700 Subject: [PATCH 0126/3817] refac(bitswap:exch) HasBlock(ptr) -> HasBlock(val) This commit was moved from ipfs/go-blockservice@f6e006bd9940a509b16ba68a57b9099ebffdb184 --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 7008a5b2b..0b4f15b98 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -42,7 +42,7 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { return k, err } if s.Remote != nil { - err = s.Remote.HasBlock(b) + err = s.Remote.HasBlock(*b) } return k, err } From 1f88de77f22fe912a5822e2dcb0b42fef1a8174b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 05:05:32 -0700 Subject: [PATCH 0127/3817] Peerstore -- threadsafe collection this will later have persistent storage, but no need yet This commit was moved from ipfs/go-ipfs-routing@950f56ee148fe6454c1948e5886025348add53b3 --- routing/dht/dht.go | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index da58089f3..756c1803a 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -194,25 +194,27 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, return nil, nil, err } - if pmes.GetSuccess() { - if pmes.Value == nil { // We were given provider[s] - val, err := dht.getFromPeerList(key, timeout, pmes.GetPeers(), level) - if err != nil { - return nil, nil, err - } - return val, nil, nil - } - + if value := pmes.GetValue(); value != nil { // Success! We were given the value - return pmes.GetValue(), nil, nil + return value, nil, nil } - // We were given a closer node + // TODO decide on providers. This probably shouldn't be happening. + // if prv := pmes.GetProviderPeers(); prv != nil && len(prv) > 0 { + // val, err := dht.getFromPeerList(key, timeout,, level) + // if err != nil { + // return nil, nil, err + // } + // return val, nil, nil + // } + + // Perhaps we were given closer peers var peers []*peer.Peer - for _, pb := range pmes.GetPeers() { + for _, pb := range pmes.GetCloserPeers() { if peer.ID(pb.GetId()).Equal(dht.self.ID) { continue } + addr, err := ma.NewMultiaddr(pb.GetAddr()) if err != nil { u.PErr("%v\n", err.Error()) @@ -227,7 +229,12 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, peers = append(peers, np) } - return nil, peers, nil + + if len(peers) > 0 { + return nil, peers, nil + } + + return nil, nil, errors.New("NotFound. did not get value or closer peers.") } // getValueSingle simply performs the get value RPC with the given parameters From 56f6a0c52e19b6f82287b0a2a070f30af02f7221 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 06:10:08 -0700 Subject: [PATCH 0128/3817] godep multiaddr update This commit was moved from ipfs/go-ipfs-routing@1ef551903ba60dec76a8ed8687f32efb49d5d624 --- routing/dht/messages.pb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/messages.pb.go b/routing/dht/messages.pb.go index d85e81905..b6e9fa4f2 100644 --- a/routing/dht/messages.pb.go +++ b/routing/dht/messages.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package dht -import proto "code.google.com/p/gogoprotobuf/proto" +import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import json "encoding/json" import math "math" From fbeb862b0e7478aa2daa6afc539b0c1a5f72b6c4 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 06:18:26 -0700 Subject: [PATCH 0129/3817] add Peerstore to dht This commit was moved from ipfs/go-ipfs-routing@3aa025b23062c6a7461c39a2539c589d823bb043 --- routing/dht/dht.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 756c1803a..8e26241b1 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -36,6 +36,9 @@ type IpfsDHT struct { // Local peer (yourself) self *peer.Peer + // Other peers + peerstore peer.Peerstore + // Local data datastore ds.Datastore dslock sync.Mutex @@ -53,12 +56,13 @@ type IpfsDHT struct { } // NewDHT creates a new DHT object with the given peer as the 'local' host -func NewDHT(p *peer.Peer, net inet.Network, sender inet.Sender, dstore ds.Datastore) *IpfsDHT { +func NewDHT(p *peer.Peer, ps peer.Peerstore, net inet.Network, sender inet.Sender, dstore ds.Datastore) *IpfsDHT { dht := new(IpfsDHT) dht.network = net dht.sender = sender dht.datastore = dstore dht.self = p + dht.peerstore = ps dht.providers = NewProviderManager(p.ID) dht.shutdown = make(chan struct{}) From 8b37885f89f72fc5b47b00bf5962924f1ba40016 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 06:33:51 -0700 Subject: [PATCH 0130/3817] getFromPeerList and peerFromInfo This commit was moved from ipfs/go-ipfs-routing@e0dd642a15f5766be0460b9529d898c73951ad06 --- routing/dht/dht.go | 75 +++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 8e26241b1..596ba3dd7 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -225,13 +225,14 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, continue } - np, err := dht.network.GetConnection(peer.ID(pb.GetId()), addr) - if err != nil { - u.PErr("%v\n", err.Error()) - continue + // check if we already have this peer. + pr, _ := dht.peerstore.Get(peer.ID(pb.GetId())) + if pr == nil { + pr = &peer.Peer{ID: peer.ID(pb.GetId())} + dht.peerstore.Put(pr) } - - peers = append(peers, np) + pr.AddAddress(addr) // idempotent + peers = append(peers, pr) } if len(peers) > 0 { @@ -257,33 +258,26 @@ func (dht *IpfsDHT) getValueSingle(ctx context.Context, p *peer.Peer, // from someone what do we do with it? Connect to each of them? randomly pick // one to get the value from? Or just connect to one at a time until we get a // successful connection and request the value from it? -func (dht *IpfsDHT) getFromPeerList(key u.Key, timeout time.Duration, - peerlist []*Message_PBPeer, level int) ([]byte, error) { - for _, pinfo := range peerlist { - p, _ := dht.Find(peer.ID(pinfo.GetId())) - if p == nil { - maddr, err := ma.NewMultiaddr(pinfo.GetAddr()) - if err != nil { - u.PErr("getValue error: %s\n", err) - continue - } +func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key, + peerlist []*Message_Peer, level int) ([]byte, error) { - p, err = dht.network.GetConnection(peer.ID(pinfo.GetId()), maddr) - if err != nil { - u.PErr("getValue error: %s\n", err) - continue - } + for _, pinfo := range peerlist { + p, err := dht.peerFromInfo(pinfo) + if err != nil { + u.DErr("getFromPeers error: %s\n", err) + continue } - pmes, err := dht.getValueSingle(p, key, timeout, level) + + pmes, err := dht.getValueSingle(ctx, p, key, level) if err != nil { u.DErr("getFromPeers error: %s\n", err) continue } - dht.providers.AddProvider(key, p) - // Make sure it was a successful get - if pmes.GetSuccess() && pmes.Value != nil { - return pmes.GetValue(), nil + if value := pmes.GetValue(); value != nil { + // Success! We were given the value + dht.providers.AddProvider(key, p) + return value, nil } } return nil, u.ErrNotFound @@ -463,13 +457,32 @@ func (dht *IpfsDHT) betterPeerToQuery(pmes *Message) *peer.Peer { return closer } -func (dht *IpfsDHT) peerFromInfo(pbp *Message_PBPeer) (*peer.Peer, error) { - maddr, err := ma.NewMultiaddr(pbp.GetAddr()) - if err != nil { - return nil, err +func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (*peer.Peer, error) { + + id := peer.ID(pbp.GetId()) + p, _ := dht.peerstore.Get(id) + if p == nil { + p, _ = dht.Find(id) + if p != nil { + panic("somehow peer not getting into peerstore") + } + } + + if p == nil { + maddr, err := ma.NewMultiaddr(pbp.GetAddr()) + if err != nil { + return nil, err + } + + // create new Peer + p := &peer.Peer{ID: id} + p.AddAddress(maddr) + dht.peerstore.Put(pr) } - return dht.network.GetConnection(peer.ID(pbp.GetId()), maddr) + // dial connection + err = dht.network.Dial(p) + return p, err } func (dht *IpfsDHT) loadProvidableKeys() error { From d7d3e4232ca30e7eb18f18b8d6ca28cf07f54ce2 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 06:40:17 -0700 Subject: [PATCH 0131/3817] updated Update function This commit was moved from ipfs/go-ipfs-routing@3d3ebd99f2ce1509eb037c2b9ce0244950e85f1b --- routing/dht/dht.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 596ba3dd7..f2de949c3 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -302,24 +302,25 @@ func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { return dht.datastore.Put(ds.NewKey(string(key)), value) } -// Update TODO(chas) Document this function +// Update signals to all routingTables to Update their last-seen status +// on the given peer. func (dht *IpfsDHT) Update(p *peer.Peer) { + removedCount := 0 for _, route := range dht.routingTables { removed := route.Update(p) // Only close the connection if no tables refer to this peer if removed != nil { - found := false - for _, r := range dht.routingTables { - if r.Find(removed.ID) != nil { - found = true - break - } - } - if !found { - dht.network.CloseConnection(removed) - } + removedCount++ } } + + // Only close the connection if no tables refer to this peer + // if removedCount == len(dht.routingTables) { + // dht.network.ClosePeer(p) + // } + // ACTUALLY, no, let's not just close the connection. it may be connected + // due to other things. it seems that we just need connection timeouts + // after some deadline of inactivity. } // Find looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in. From 6fd89ec3e634defefb855ffa4ec15ceb2354a0ed Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Sep 2014 07:17:55 -0700 Subject: [PATCH 0132/3817] newMessage and more impl. This commit was moved from ipfs/go-ipfs-routing@ce3da2d129b9963dbd70dbe12af900cee15f436b --- routing/dht/Message.go | 20 ++++++-- routing/dht/dht.go | 107 +++++++++++----------------------------- routing/dht/handlers.go | 34 +++++-------- routing/dht/routing.go | 2 +- 4 files changed, 59 insertions(+), 104 deletions(-) diff --git a/routing/dht/Message.go b/routing/dht/Message.go index bf592799d..d82b3bb44 100644 --- a/routing/dht/Message.go +++ b/routing/dht/Message.go @@ -6,6 +6,15 @@ import ( u "github.com/jbenet/go-ipfs/util" ) +func newMessage(typ Message_MessageType, key string, level int) *Message { + m := &Message{ + Type: &typ, + Key: &key, + } + m.SetClusterLevel(level) + return m +} + func peerToPBPeer(p *peer.Peer) *Message_Peer { pbp := new(Message_Peer) if len(p.Addresses) == 0 || p.Addresses[0] == nil { @@ -24,7 +33,7 @@ func peerToPBPeer(p *peer.Peer) *Message_Peer { } func peersToPBPeers(peers []*peer.Peer) []*Message_Peer { - pbpeers = make([]*Message_Peer, len(peers)) + pbpeers := make([]*Message_Peer, len(peers)) for i, p := range peers { pbpeers[i] = peerToPBPeer(p) } @@ -34,18 +43,19 @@ func peersToPBPeers(peers []*peer.Peer) []*Message_Peer { // GetClusterLevel gets and adjusts the cluster level on the message. // a +/- 1 adjustment is needed to distinguish a valid first level (1) and // default "no value" protobuf behavior (0) -func (m *Message) GetClusterLevel() int32 { +func (m *Message) GetClusterLevel() int { level := m.GetClusterLevelRaw() - 1 if level < 0 { u.PErr("handleGetValue: no routing level specified, assuming 0\n") level = 0 } - return level + return int(level) } // SetClusterLevel adjusts and sets the cluster level on the message. // a +/- 1 adjustment is needed to distinguish a valid first level (1) and // default "no value" protobuf behavior (0) -func (m *Message) SetClusterLevel(level int32) { - m.ClusterLevelRaw = &level +func (m *Message) SetClusterLevel(level int) { + lvl := int32(level) + m.ClusterLevelRaw = &lvl } diff --git a/routing/dht/dht.go b/routing/dht/dht.go index f2de949c3..37205374f 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -246,11 +246,7 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, func (dht *IpfsDHT) getValueSingle(ctx context.Context, p *peer.Peer, key u.Key, level int) (*Message, error) { - typ := Message_GET_VALUE - skey := string(key) - pmes := &Message{Type: &typ, Key: &skey} - pmes.SetClusterLevel(int32(level)) - + pmes := newMessage(Message_GET_VALUE, string(key), level) return dht.sendRequest(ctx, p, pmes) } @@ -262,7 +258,7 @@ func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key, peerlist []*Message_Peer, level int) ([]byte, error) { for _, pinfo := range peerlist { - p, err := dht.peerFromInfo(pinfo) + p, err := dht.ensureConnectedToPeer(pinfo) if err != nil { u.DErr("getFromPeers error: %s\n", err) continue @@ -334,34 +330,9 @@ func (dht *IpfsDHT) Find(id peer.ID) (*peer.Peer, *kb.RoutingTable) { return nil, nil } -func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Duration, level int) (*Message, error) { - pmes := Message{ - Type: Message_FIND_NODE, - Key: string(id), - ID: swarm.GenerateMessageID(), - Value: []byte{byte(level)}, - } - - mes := swarm.NewMessage(p, pmes.ToProtobuf()) - listenChan := dht.listener.Listen(pmes.ID, 1, time.Minute) - t := time.Now() - dht.netChan.Outgoing <- mes - after := time.After(timeout) - select { - case <-after: - dht.listener.Unlisten(pmes.ID) - return nil, u.ErrTimeout - case resp := <-listenChan: - roundtrip := time.Since(t) - resp.Peer.SetLatency(roundtrip) - pmesOut := new(Message) - err := proto.Unmarshal(resp.Data, pmesOut) - if err != nil { - return nil, err - } - - return pmesOut, nil - } +func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p *peer.Peer, id peer.ID, level int) (*Message, error) { + pmes := newMessage(Message_FIND_NODE, string(id), level) + return dht.sendRequest(ctx, p, pmes) } func (dht *IpfsDHT) printTables() { @@ -370,54 +341,27 @@ func (dht *IpfsDHT) printTables() { } } -func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, timeout time.Duration) (*Message, error) { - pmes := Message{ - Type: Message_GET_PROVIDERS, - Key: string(key), - ID: swarm.GenerateMessageID(), - Value: []byte{byte(level)}, - } - - mes := swarm.NewMessage(p, pmes.ToProtobuf()) - - listenChan := dht.listener.Listen(pmes.ID, 1, time.Minute) - dht.netChan.Outgoing <- mes - after := time.After(timeout) - select { - case <-after: - dht.listener.Unlisten(pmes.ID) - return nil, u.ErrTimeout - case resp := <-listenChan: - u.DOut("FindProviders: got response.\n") - pmesOut := new(Message) - err := proto.Unmarshal(resp.Data, pmesOut) - if err != nil { - return nil, err - } - - return pmesOut, nil - } +func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p *peer.Peer, key u.Key, level int) (*Message, error) { + pmes := newMessage(Message_GET_PROVIDERS, string(key), level) + return dht.sendRequest(ctx, p, pmes) } // TODO: Could be done async -func (dht *IpfsDHT) addPeerList(key u.Key, peers []*Message_PBPeer) []*peer.Peer { +func (dht *IpfsDHT) addProviders(key u.Key, peers []*Message_Peer) []*peer.Peer { var provArr []*peer.Peer for _, prov := range peers { - // Dont add outselves to the list - if peer.ID(prov.GetId()).Equal(dht.self.ID) { + p, err := dht.peerFromInfo(prov) + if err != nil { + u.PErr("error getting peer from info: %v\n", err) continue } - // Dont add someone who is already on the list - p := dht.network.GetPeer(u.Key(prov.GetId())) - if p == nil { - u.DOut("given provider %s was not in our network already.\n", peer.ID(prov.GetId()).Pretty()) - var err error - p, err = dht.peerFromInfo(prov) - if err != nil { - u.PErr("error connecting to new peer: %s\n", err) - continue - } + + // Dont add outselves to the list + if p.ID.Equal(dht.self.ID) { + continue } + + // TODO(jbenet) ensure providers is idempotent dht.providers.AddProvider(key, p) provArr = append(provArr, p) } @@ -450,6 +394,7 @@ func (dht *IpfsDHT) betterPeerToQuery(pmes *Message) *peer.Peer { } // self is closer? nil + key := u.Key(pmes.GetKey()) if kb.Closer(dht.self.ID, closer.ID, key) { return nil } @@ -478,11 +423,19 @@ func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (*peer.Peer, error) { // create new Peer p := &peer.Peer{ID: id} p.AddAddress(maddr) - dht.peerstore.Put(pr) + dht.peerstore.Put(p) + } + return p, nil +} + +func (dht *IpfsDHT) ensureConnectedToPeer(pbp *Message_Peer) (*peer.Peer, error) { + p, err := dht.peerFromInfo(pbp) + if err != nil { + return nil, err } // dial connection - err = dht.network.Dial(p) + err = dht.network.DialPeer(p) return p, err } @@ -497,7 +450,7 @@ func (dht *IpfsDHT) loadProvidableKeys() error { return nil } -// Builds up list of peers by requesting random peer IDs +// Bootstrap builds up list of peers by requesting random peer IDs func (dht *IpfsDHT) Bootstrap() { id := make([]byte, 16) rand.Read(id) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 909ec839a..710478e45 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -40,11 +40,8 @@ func (dht *IpfsDHT) handlerForMsgType(t Message_MessageType) dhtHandler { func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) error { typ := Message_PUT_VALUE - pmes := &Message{ - Type: &typ, - Key: &key, - Value: value, - } + pmes := newMessage(Message_PUT_VALUE, string(key), 0) + pmes.Value = value mes, err := msg.FromObject(p, pmes) if err != nil { @@ -57,10 +54,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error u.DOut("handleGetValue for key: %s\n", pmes.GetKey()) // setup response - resp := &Message{ - Type: pmes.Type, - Key: pmes.Key, - } + resp := newMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) // first, is the key even a key? key := pmes.GetKey() @@ -113,24 +107,22 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error } // Store a value in this peer local storage -func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *Message) { +func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *Message) (*Message, error) { dht.dslock.Lock() defer dht.dslock.Unlock() dskey := ds.NewKey(pmes.GetKey()) err := dht.datastore.Put(dskey, pmes.GetValue()) - if err != nil { - // For now, just panic, handle this better later maybe - panic(err) - } + return nil, err } func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *Message) (*Message, error) { u.DOut("[%s] Responding to ping from [%s]!\n", dht.self.ID.Pretty(), p.ID.Pretty()) - return &Message{Type: pmes.Type}, nil + + return newMessage(pmes.GetType(), "", int(pmes.GetClusterLevel())), nil } func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *Message) (*Message, error) { - resp := &Message{Type: pmes.Type} + resp := newMessage(pmes.GetType(), "", pmes.GetClusterLevel()) var closest *peer.Peer // if looking for self... special case where we send it on CloserPeers. @@ -156,10 +148,7 @@ func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *Message) (*Message, error } func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *Message) (*Message, error) { - resp := &Message{ - Type: pmes.Type, - Key: pmes.Key, - } + resp := newMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) // check if we have this value, to add ourselves as provider. has, err := dht.datastore.Has(ds.NewKey(pmes.GetKey())) @@ -193,11 +182,14 @@ type providerInfo struct { Value *peer.Peer } -func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *Message) { +func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *Message) (*Message, error) { key := u.Key(pmes.GetKey()) + u.DOut("[%s] Adding [%s] as a provider for '%s'\n", dht.self.ID.Pretty(), p.ID.Pretty(), peer.ID(key).Pretty()) + dht.providers.AddProvider(key, p) + return nil, nil } // Halt stops all communications from this peer and shut down diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 3b8b25f5c..49fdb06ee 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -261,7 +261,7 @@ func (dht *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Pee } if pmes.GetSuccess() { u.DOut("Got providers back from findProviders call!\n") - provs := dht.addPeerList(key, pmes.GetPeers()) + provs := dht.addProviders(key, pmes.GetPeers()) ll.Success = true return provs, nil } From 789b6f1ea17f75d2f8387d3e7899b515ad865fe7 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 17 Sep 2014 01:39:08 -0700 Subject: [PATCH 0133/3817] cleaner KeySpace abstraction. This commit was moved from ipfs/go-ipfs-routing@85d17fc5e3824abb040072b4a3552f556b32f069 --- routing/dht/keyspace/keyspace.go | 95 +++++++++++++++++++++ routing/dht/keyspace/xor.go | 74 ++++++++++++++++ routing/dht/keyspace/xor_test.go | 139 +++++++++++++++++++++++++++++++ 3 files changed, 308 insertions(+) create mode 100644 routing/dht/keyspace/keyspace.go create mode 100644 routing/dht/keyspace/xor.go create mode 100644 routing/dht/keyspace/xor_test.go diff --git a/routing/dht/keyspace/keyspace.go b/routing/dht/keyspace/keyspace.go new file mode 100644 index 000000000..3d3260b26 --- /dev/null +++ b/routing/dht/keyspace/keyspace.go @@ -0,0 +1,95 @@ +package keyspace + +import ( + "sort" + + "math/big" +) + +// Key represents an identifier in a KeySpace. It holds a reference to the +// associated KeySpace, as well references to both the Original identifier, +// as well as the new, KeySpace Adjusted one. +type Key struct { + + // Space is the KeySpace this Key is related to. + Space KeySpace + + // Original is the original value of the identifier + Original []byte + + // Adjusted is the new value of the identifier, in the KeySpace. + Adjusted []byte +} + +// Equal returns whether this key is equal to another. +func (k1 Key) Equal(k2 Key) bool { + if k1.Space != k2.Space { + panic("k1 and k2 not in same key space.") + } + return k1.Space.Equal(k1, k2) +} + +// Less returns whether this key comes before another. +func (k1 Key) Less(k2 Key) bool { + if k1.Space != k2.Space { + panic("k1 and k2 not in same key space.") + } + return k1.Space.Less(k1, k2) +} + +// Distance returns this key's distance to another +func (k1 Key) Distance(k2 Key) *big.Int { + if k1.Space != k2.Space { + panic("k1 and k2 not in same key space.") + } + return k1.Space.Distance(k1, k2) +} + +// KeySpace is an object used to do math on identifiers. Each keyspace has its +// own properties and rules. See XorKeySpace. +type KeySpace interface { + + // Key converts an identifier into a Key in this space. + Key([]byte) Key + + // Equal returns whether keys are equal in this key space + Equal(Key, Key) bool + + // Distance returns the distance metric in this key space + Distance(Key, Key) *big.Int + + // Less returns whether the first key is smaller than the second. + Less(Key, Key) bool +} + +// byDistanceToCenter is a type used to sort Keys by proximity to a center. +type byDistanceToCenter struct { + Center Key + Keys []Key +} + +func (s byDistanceToCenter) Len() int { + return len(s.Keys) +} + +func (s byDistanceToCenter) Swap(i, j int) { + s.Keys[i], s.Keys[j] = s.Keys[j], s.Keys[i] +} + +func (s byDistanceToCenter) Less(i, j int) bool { + a := s.Center.Distance(s.Keys[i]) + b := s.Center.Distance(s.Keys[j]) + return a.Cmp(b) == -1 +} + +// SortByDistance takes a KeySpace, a center Key, and a list of Keys toSort. +// It returns a new list, where the Keys toSort have been sorted by their +// distance to the center Key. +func SortByDistance(sp KeySpace, center Key, toSort []Key) []Key { + bdtc := &byDistanceToCenter{ + Center: center, + Keys: toSort[:], // copy + } + sort.Sort(bdtc) + return bdtc.Keys +} diff --git a/routing/dht/keyspace/xor.go b/routing/dht/keyspace/xor.go new file mode 100644 index 000000000..8cbef30eb --- /dev/null +++ b/routing/dht/keyspace/xor.go @@ -0,0 +1,74 @@ +package keyspace + +import ( + "bytes" + "crypto/sha256" + "math/big" +) + +// XORKeySpace is a KeySpace which: +// - normalizes identifiers using a cryptographic hash (sha256) +// - measures distance by XORing keys together +var XORKeySpace = &xorKeySpace{} +var _ KeySpace = XORKeySpace // ensure it conforms + +type xorKeySpace struct{} + +// Key converts an identifier into a Key in this space. +func (s *xorKeySpace) Key(id []byte) Key { + hash := sha256.Sum256(id) + key := hash[:] + return Key{ + Space: s, + Original: id, + Adjusted: key, + } +} + +// Equal returns whether keys are equal in this key space +func (s *xorKeySpace) Equal(k1, k2 Key) bool { + return bytes.Equal(k1.Adjusted, k2.Adjusted) +} + +// Distance returns the distance metric in this key space +func (s *xorKeySpace) Distance(k1, k2 Key) *big.Int { + // XOR the keys + k3 := XOR(k1.Adjusted, k2.Adjusted) + + // interpret it as an integer + dist := big.NewInt(0).SetBytes(k3) + return dist +} + +// Less returns whether the first key is smaller than the second. +func (s *xorKeySpace) Less(k1, k2 Key) bool { + a := k1.Adjusted + b := k2.Adjusted + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return a[i] < b[i] + } + } + return true +} + +// XOR takes two byte slices, XORs them together, returns the resulting slice. +func XOR(a, b []byte) []byte { + c := make([]byte, len(a)) + for i := 0; i < len(a); i++ { + c[i] = a[i] ^ b[i] + } + return c +} + +// ZeroPrefixLen returns the number of consecutive zeroes in a byte slice. +func ZeroPrefixLen(id []byte) int { + for i := 0; i < len(id); i++ { + for j := 0; j < 8; j++ { + if (id[i]>>uint8(7-j))&0x1 != 0 { + return i*8 + j + } + } + } + return len(id) * 8 +} diff --git a/routing/dht/keyspace/xor_test.go b/routing/dht/keyspace/xor_test.go new file mode 100644 index 000000000..58987ad10 --- /dev/null +++ b/routing/dht/keyspace/xor_test.go @@ -0,0 +1,139 @@ +package keyspace + +import ( + "bytes" + "math/big" + "testing" +) + +func TestXOR(t *testing.T) { + cases := [][3][]byte{ + [3][]byte{ + []byte{0xFF, 0xFF, 0xFF}, + []byte{0xFF, 0xFF, 0xFF}, + []byte{0x00, 0x00, 0x00}, + }, + [3][]byte{ + []byte{0x00, 0xFF, 0x00}, + []byte{0xFF, 0xFF, 0xFF}, + []byte{0xFF, 0x00, 0xFF}, + }, + [3][]byte{ + []byte{0x55, 0x55, 0x55}, + []byte{0x55, 0xFF, 0xAA}, + []byte{0x00, 0xAA, 0xFF}, + }, + } + + for _, c := range cases { + r := XOR(c[0], c[1]) + if !bytes.Equal(r, c[2]) { + t.Error("XOR failed") + } + } +} + +func TestPrefixLen(t *testing.T) { + cases := [][]byte{ + []byte{0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00}, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x00, 0x58, 0xFF, 0x80, 0x00, 0x00, 0xF0}, + } + lens := []int{24, 56, 9} + + for i, c := range cases { + r := ZeroPrefixLen(c) + if r != lens[i] { + t.Errorf("ZeroPrefixLen failed: %v != %v", r, lens[i]) + } + } + +} + +func TestXorKeySpace(t *testing.T) { + + ids := [][]byte{ + []byte{0xFF, 0xFF, 0xFF, 0xFF}, + []byte{0x00, 0x00, 0x00, 0x00}, + []byte{0xFF, 0xFF, 0xFF, 0xF0}, + } + + ks := [][2]Key{ + [2]Key{XORKeySpace.Key(ids[0]), XORKeySpace.Key(ids[0])}, + [2]Key{XORKeySpace.Key(ids[1]), XORKeySpace.Key(ids[1])}, + [2]Key{XORKeySpace.Key(ids[2]), XORKeySpace.Key(ids[2])}, + } + + for i, set := range ks { + if !set[0].Equal(set[1]) { + t.Errorf("Key not eq. %v != %v", set[0], set[1]) + } + + if !bytes.Equal(set[0].Adjusted, set[1].Adjusted) { + t.Errorf("Key gen failed. %v != %v", set[0].Adjusted, set[1].Adjusted) + } + + if !bytes.Equal(set[0].Original, ids[i]) { + t.Errorf("ptrs to original. %v != %v", set[0].Original, ids[i]) + } + + if len(set[0].Adjusted) != 32 { + t.Errorf("key length incorrect. 32 != %d", len(set[0].Adjusted)) + } + } + + for i := 1; i < len(ks); i++ { + if ks[i][0].Less(ks[i-1][0]) == ks[i-1][0].Less(ks[i][0]) { + t.Errorf("less should be different.") + } + + if ks[i][0].Distance(ks[i-1][0]).Cmp(ks[i-1][0].Distance(ks[i][0])) != 0 { + t.Errorf("distance should be the same.") + } + + if ks[i][0].Equal(ks[i-1][0]) { + t.Errorf("Keys should not be eq. %v != %v", ks[i][0], ks[i-1][0]) + } + } +} + +func TestCenterSorting(t *testing.T) { + + adjs := [][]byte{ + []byte{173, 149, 19, 27, 192, 183, 153, 192, 177, 175, 71, 127, 177, 79, 207, 38, 166, 169, 247, 96, 121, 228, 139, 240, 144, 172, 183, 232, 54, 123, 253, 14}, + []byte{223, 63, 97, 152, 4, 169, 47, 219, 64, 87, 25, 45, 196, 61, 215, 72, 234, 119, 138, 220, 82, 188, 73, 140, 232, 5, 36, 192, 20, 184, 17, 25}, + []byte{73, 176, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 127}, + []byte{73, 176, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 127}, + []byte{73, 176, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 126}, + []byte{73, 0, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 127}, + } + + keys := make([]Key, len(adjs)) + for i, a := range adjs { + keys[i] = Key{Space: XORKeySpace, Adjusted: a} + } + + cmp := func(a int, b *big.Int) int { + return big.NewInt(int64(a)).Cmp(b) + } + + if 0 != cmp(0, keys[2].Distance(keys[3])) { + t.Errorf("distance calculation wrong: %v", keys[2].Distance(keys[3])) + } + + if 0 != cmp(1, keys[2].Distance(keys[4])) { + t.Errorf("distance calculation wrong: %v", keys[2].Distance(keys[4])) + } + + d1 := keys[2].Distance(keys[5]) + d2 := XOR(keys[2].Adjusted, keys[5].Adjusted) + d2 = d2[len(keys[2].Adjusted)-len(d1.Bytes()):] // skip empty space for big + if !bytes.Equal(d1.Bytes(), d2) { + t.Errorf("bytes should be the same. %v == %v", d1.Bytes(), d2) + } + + if -1 != cmp(2<<32, keys[2].Distance(keys[5])) { + t.Errorf("2<<32 should be smaller") + } + +} From 0997bcea6c2ce42119b6d4a049bb9552aa87f90b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 17 Sep 2014 01:51:03 -0700 Subject: [PATCH 0134/3817] SortByDistance copy fix This commit was moved from ipfs/go-ipfs-routing@35fcaf876a3da2d5071ffb86bd44c3328325cc94 --- routing/dht/keyspace/keyspace.go | 4 +++- routing/dht/keyspace/xor_test.go | 10 +++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/routing/dht/keyspace/keyspace.go b/routing/dht/keyspace/keyspace.go index 3d3260b26..a385e9a02 100644 --- a/routing/dht/keyspace/keyspace.go +++ b/routing/dht/keyspace/keyspace.go @@ -86,9 +86,11 @@ func (s byDistanceToCenter) Less(i, j int) bool { // It returns a new list, where the Keys toSort have been sorted by their // distance to the center Key. func SortByDistance(sp KeySpace, center Key, toSort []Key) []Key { + toSortCopy := make([]Key, len(toSort)) + copy(toSortCopy, toSort) bdtc := &byDistanceToCenter{ Center: center, - Keys: toSort[:], // copy + Keys: toSortCopy, // copy } sort.Sort(bdtc) return bdtc.Keys diff --git a/routing/dht/keyspace/xor_test.go b/routing/dht/keyspace/xor_test.go index 58987ad10..46757b7fd 100644 --- a/routing/dht/keyspace/xor_test.go +++ b/routing/dht/keyspace/xor_test.go @@ -97,7 +97,7 @@ func TestXorKeySpace(t *testing.T) { } } -func TestCenterSorting(t *testing.T) { +func TestDistancesAndCenterSorting(t *testing.T) { adjs := [][]byte{ []byte{173, 149, 19, 27, 192, 183, 153, 192, 177, 175, 71, 127, 177, 79, 207, 38, 166, 169, 247, 96, 121, 228, 139, 240, 144, 172, 183, 232, 54, 123, 253, 14}, @@ -136,4 +136,12 @@ func TestCenterSorting(t *testing.T) { t.Errorf("2<<32 should be smaller") } + keys2 := SortByDistance(XORKeySpace, keys[2], keys) + order := []int{2, 3, 4, 5, 1, 0} + for i, o := range order { + if !bytes.Equal(keys[o].Adjusted, keys2[i].Adjusted) { + t.Errorf("order is wrong. %d?? %v == %v", o, keys[o], keys2[i]) + } + } + } From 1ba133998d7702028d552f547027c4ce92f27ef9 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 17 Sep 2014 02:02:13 -0700 Subject: [PATCH 0135/3817] moved keyspace This commit was moved from ipfs/go-ipfs-routing@7cb38547369c09e15d54a87fc043268c6f756add --- routing/{dht => }/keyspace/keyspace.go | 0 routing/{dht => }/keyspace/xor.go | 0 routing/{dht => }/keyspace/xor_test.go | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename routing/{dht => }/keyspace/keyspace.go (100%) rename routing/{dht => }/keyspace/xor.go (100%) rename routing/{dht => }/keyspace/xor_test.go (100%) diff --git a/routing/dht/keyspace/keyspace.go b/routing/keyspace/keyspace.go similarity index 100% rename from routing/dht/keyspace/keyspace.go rename to routing/keyspace/keyspace.go diff --git a/routing/dht/keyspace/xor.go b/routing/keyspace/xor.go similarity index 100% rename from routing/dht/keyspace/xor.go rename to routing/keyspace/xor.go diff --git a/routing/dht/keyspace/xor_test.go b/routing/keyspace/xor_test.go similarity index 100% rename from routing/dht/keyspace/xor_test.go rename to routing/keyspace/xor_test.go From 41505dec8de3b64cd97beae7a4520f72c294255e Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 17 Sep 2014 02:02:44 -0700 Subject: [PATCH 0136/3817] kbucket use new keyspace This commit was moved from ipfs/go-ipfs-routing@5e044d90433bb129556fb4a65f7936f1229205b3 --- routing/kbucket/bucket.go | 2 +- routing/kbucket/table.go | 4 +-- routing/kbucket/table_test.go | 4 +-- routing/kbucket/util.go | 54 ++++++----------------------------- 4 files changed, 13 insertions(+), 51 deletions(-) diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index a4eb91415..3a9c71fad 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -69,7 +69,7 @@ func (b *Bucket) Split(cpl int, target ID) *Bucket { e := b.list.Front() for e != nil { peerID := ConvertPeerID(e.Value.(*peer.Peer).ID) - peerCPL := prefLen(peerID, target) + peerCPL := commonPrefixLen(peerID, target) if peerCPL > cpl { cur := e out.PushBack(e.Value) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 5f1e5c870..2a0f16d1a 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -44,7 +44,7 @@ func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer { rt.tabLock.Lock() defer rt.tabLock.Unlock() peerID := ConvertPeerID(p.ID) - cpl := xor(peerID, rt.local).commonPrefixLen() + cpl := commonPrefixLen(peerID, rt.local) bucketID := cpl if bucketID >= len(rt.Buckets) { @@ -145,7 +145,7 @@ func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer { func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { rt.tabLock.RLock() defer rt.tabLock.RUnlock() - cpl := prefLen(id, rt.local) + cpl := commonPrefixLen(id, rt.local) // Get bucket at cpl index or last bucket var bucket *Bucket diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index dbb391ff3..49be52c65 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -48,7 +48,7 @@ func TestBucket(t *testing.T) { llist := b.list for e := llist.Front(); e != nil; e = e.Next() { p := ConvertPeerID(e.Value.(*peer.Peer).ID) - cpl := xor(p, localID).commonPrefixLen() + cpl := commonPrefixLen(p, localID) if cpl > 0 { t.Fatalf("Split failed. found id with cpl > 0 in 0 bucket") } @@ -57,7 +57,7 @@ func TestBucket(t *testing.T) { rlist := spl.list for e := rlist.Front(); e != nil; e = e.Next() { p := ConvertPeerID(e.Value.(*peer.Peer).ID) - cpl := xor(p, localID).commonPrefixLen() + cpl := commonPrefixLen(p, localID) if cpl == 0 { t.Fatalf("Split failed. found id with cpl == 0 in non 0 bucket") } diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 1195022b0..ded28bb52 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -6,6 +6,7 @@ import ( "errors" peer "github.com/jbenet/go-ipfs/peer" + ks "github.com/jbenet/go-ipfs/routing/keyspace" u "github.com/jbenet/go-ipfs/util" ) @@ -13,8 +14,7 @@ import ( // behaviour var ErrLookupFailure = errors.New("failed to find any peer in table") -// ID for IpfsDHT should be a byte slice, to allow for simpler operations -// (xor). DHT ids are based on the peer.IDs. +// ID for IpfsDHT is in the XORKeySpace // // The type dht.ID signifies that its contents have been hashed from either a // peer.ID or a util.Key. This unifies the keyspace @@ -25,55 +25,17 @@ func (id ID) equal(other ID) bool { } func (id ID) less(other ID) bool { - a, b := equalizeSizes(id, other) - for i := 0; i < len(a); i++ { - if a[i] != b[i] { - return a[i] < b[i] - } - } - return len(a) < len(b) -} - -func (id ID) commonPrefixLen() int { - for i := 0; i < len(id); i++ { - for j := 0; j < 8; j++ { - if (id[i]>>uint8(7-j))&0x1 != 0 { - return i*8 + j - } - } - } - return len(id)*8 - 1 -} - -func prefLen(a, b ID) int { - return xor(a, b).commonPrefixLen() + a := ks.Key{Space: ks.XORKeySpace, Adjusted: id} + b := ks.Key{Space: ks.XORKeySpace, Adjusted: other} + return a.Less(b) } func xor(a, b ID) ID { - a, b = equalizeSizes(a, b) - - c := make(ID, len(a)) - for i := 0; i < len(a); i++ { - c[i] = a[i] ^ b[i] - } - return c + return ID(ks.XOR(a, b)) } -func equalizeSizes(a, b ID) (ID, ID) { - la := len(a) - lb := len(b) - - if la < lb { - na := make([]byte, lb) - copy(na, a) - a = na - } else if lb < la { - nb := make([]byte, la) - copy(nb, b) - b = nb - } - - return a, b +func commonPrefixLen(a, b ID) int { + return ks.ZeroPrefixLen(ks.XOR(a, b)) } // ConvertPeerID creates a DHT ID by hashing a Peer ID (Multihash) From 4a96eb4487a11989a1cd29aaccfcfc5643a2bb0f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 17 Sep 2014 02:46:54 -0700 Subject: [PATCH 0137/3817] refactored keyspace Adjusted -> Bytes This commit was moved from ipfs/go-ipfs-routing@b948f06dffe2b66d0cfbc1bf6d64127f9e8f3320 --- routing/kbucket/util.go | 4 ++-- routing/keyspace/keyspace.go | 6 +++--- routing/keyspace/xor.go | 10 +++++----- routing/keyspace/xor_test.go | 16 ++++++++-------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index ded28bb52..3aca06f6a 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -25,8 +25,8 @@ func (id ID) equal(other ID) bool { } func (id ID) less(other ID) bool { - a := ks.Key{Space: ks.XORKeySpace, Adjusted: id} - b := ks.Key{Space: ks.XORKeySpace, Adjusted: other} + a := ks.Key{Space: ks.XORKeySpace, Bytes: id} + b := ks.Key{Space: ks.XORKeySpace, Bytes: other} return a.Less(b) } diff --git a/routing/keyspace/keyspace.go b/routing/keyspace/keyspace.go index a385e9a02..e26a0e6d0 100644 --- a/routing/keyspace/keyspace.go +++ b/routing/keyspace/keyspace.go @@ -8,7 +8,7 @@ import ( // Key represents an identifier in a KeySpace. It holds a reference to the // associated KeySpace, as well references to both the Original identifier, -// as well as the new, KeySpace Adjusted one. +// as well as the new, KeySpace Bytes one. type Key struct { // Space is the KeySpace this Key is related to. @@ -17,8 +17,8 @@ type Key struct { // Original is the original value of the identifier Original []byte - // Adjusted is the new value of the identifier, in the KeySpace. - Adjusted []byte + // Bytes is the new value of the identifier, in the KeySpace. + Bytes []byte } // Equal returns whether this key is equal to another. diff --git a/routing/keyspace/xor.go b/routing/keyspace/xor.go index 8cbef30eb..dbb7c6851 100644 --- a/routing/keyspace/xor.go +++ b/routing/keyspace/xor.go @@ -21,19 +21,19 @@ func (s *xorKeySpace) Key(id []byte) Key { return Key{ Space: s, Original: id, - Adjusted: key, + Bytes: key, } } // Equal returns whether keys are equal in this key space func (s *xorKeySpace) Equal(k1, k2 Key) bool { - return bytes.Equal(k1.Adjusted, k2.Adjusted) + return bytes.Equal(k1.Bytes, k2.Bytes) } // Distance returns the distance metric in this key space func (s *xorKeySpace) Distance(k1, k2 Key) *big.Int { // XOR the keys - k3 := XOR(k1.Adjusted, k2.Adjusted) + k3 := XOR(k1.Bytes, k2.Bytes) // interpret it as an integer dist := big.NewInt(0).SetBytes(k3) @@ -42,8 +42,8 @@ func (s *xorKeySpace) Distance(k1, k2 Key) *big.Int { // Less returns whether the first key is smaller than the second. func (s *xorKeySpace) Less(k1, k2 Key) bool { - a := k1.Adjusted - b := k2.Adjusted + a := k1.Bytes + b := k2.Bytes for i := 0; i < len(a); i++ { if a[i] != b[i] { return a[i] < b[i] diff --git a/routing/keyspace/xor_test.go b/routing/keyspace/xor_test.go index 46757b7fd..d7d83afa2 100644 --- a/routing/keyspace/xor_test.go +++ b/routing/keyspace/xor_test.go @@ -69,16 +69,16 @@ func TestXorKeySpace(t *testing.T) { t.Errorf("Key not eq. %v != %v", set[0], set[1]) } - if !bytes.Equal(set[0].Adjusted, set[1].Adjusted) { - t.Errorf("Key gen failed. %v != %v", set[0].Adjusted, set[1].Adjusted) + if !bytes.Equal(set[0].Bytes, set[1].Bytes) { + t.Errorf("Key gen failed. %v != %v", set[0].Bytes, set[1].Bytes) } if !bytes.Equal(set[0].Original, ids[i]) { t.Errorf("ptrs to original. %v != %v", set[0].Original, ids[i]) } - if len(set[0].Adjusted) != 32 { - t.Errorf("key length incorrect. 32 != %d", len(set[0].Adjusted)) + if len(set[0].Bytes) != 32 { + t.Errorf("key length incorrect. 32 != %d", len(set[0].Bytes)) } } @@ -110,7 +110,7 @@ func TestDistancesAndCenterSorting(t *testing.T) { keys := make([]Key, len(adjs)) for i, a := range adjs { - keys[i] = Key{Space: XORKeySpace, Adjusted: a} + keys[i] = Key{Space: XORKeySpace, Bytes: a} } cmp := func(a int, b *big.Int) int { @@ -126,8 +126,8 @@ func TestDistancesAndCenterSorting(t *testing.T) { } d1 := keys[2].Distance(keys[5]) - d2 := XOR(keys[2].Adjusted, keys[5].Adjusted) - d2 = d2[len(keys[2].Adjusted)-len(d1.Bytes()):] // skip empty space for big + d2 := XOR(keys[2].Bytes, keys[5].Bytes) + d2 = d2[len(keys[2].Bytes)-len(d1.Bytes()):] // skip empty space for big if !bytes.Equal(d1.Bytes(), d2) { t.Errorf("bytes should be the same. %v == %v", d1.Bytes(), d2) } @@ -139,7 +139,7 @@ func TestDistancesAndCenterSorting(t *testing.T) { keys2 := SortByDistance(XORKeySpace, keys[2], keys) order := []int{2, 3, 4, 5, 1, 0} for i, o := range order { - if !bytes.Equal(keys[o].Adjusted, keys2[i].Adjusted) { + if !bytes.Equal(keys[o].Bytes, keys2[i].Bytes) { t.Errorf("order is wrong. %d?? %v == %v", o, keys[o], keys2[i]) } } From 326dbab87d03d317322fb971fd53d3bf4e5c7edf Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 17 Sep 2014 07:19:40 -0700 Subject: [PATCH 0138/3817] got everything to build This commit was moved from ipfs/go-ipfs-routing@9c06c5def265b58f67341b256b5d0dd83ee59b71 --- routing/dht/dht.go | 28 ++- routing/dht/dht_logger.go | 5 + routing/dht/handlers.go | 15 +- routing/dht/query.go | 85 +++++++++ routing/dht/routing.go | 390 +++++++++++++++----------------------- 5 files changed, 270 insertions(+), 253 deletions(-) create mode 100644 routing/dht/query.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 37205374f..d3f075762 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -142,7 +142,6 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) (msg. Message_MessageType_name[int32(pmes.GetType())], mPeer.ID.Pretty()) // get handler for this msg type. - var resp *Message handler := dht.handlerForMsgType(pmes.GetType()) if handler == nil { return nil, errors.New("Recieved invalid message type") @@ -190,6 +189,27 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p *peer.Peer, pmes *Message return rpmes, nil } +func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p *peer.Peer, key string, value []byte) error { + pmes := newMessage(Message_PUT_VALUE, string(key), 0) + pmes.Value = value + + mes, err := msg.FromObject(p, pmes) + if err != nil { + return err + } + return dht.sender.SendMessage(ctx, mes) +} + +func (dht *IpfsDHT) putProvider(ctx context.Context, p *peer.Peer, key string) error { + pmes := newMessage(Message_ADD_PROVIDER, string(key), 0) + + mes, err := msg.FromObject(p, pmes) + if err != nil { + return err + } + return dht.sender.SendMessage(ctx, mes) +} + func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, key u.Key, level int) ([]byte, []*peer.Peer, error) { @@ -406,6 +426,12 @@ func (dht *IpfsDHT) betterPeerToQuery(pmes *Message) *peer.Peer { func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (*peer.Peer, error) { id := peer.ID(pbp.GetId()) + + // continue if it's ourselves + if id.Equal(dht.self.ID) { + return nil, errors.New("found self") + } + p, _ := dht.peerstore.Get(id) if p == nil { p, _ = dht.Find(id) diff --git a/routing/dht/dht_logger.go b/routing/dht/dht_logger.go index 4a02fc304..403c2a66f 100644 --- a/routing/dht/dht_logger.go +++ b/routing/dht/dht_logger.go @@ -36,3 +36,8 @@ func (l *logDhtRPC) Print() { u.DOut(string(b)) } } + +func (l *logDhtRPC) EndAndPrint() { + l.EndLog() + l.Print() +} diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 710478e45..71e5eb037 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -10,7 +10,6 @@ import ( kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" ) @@ -38,18 +37,6 @@ func (dht *IpfsDHT) handlerForMsgType(t Message_MessageType) dhtHandler { } } -func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) error { - typ := Message_PUT_VALUE - pmes := newMessage(Message_PUT_VALUE, string(key), 0) - pmes.Value = value - - mes, err := msg.FromObject(p, pmes) - if err != nil { - return err - } - return dht.sender.SendMessage(context.TODO(), mes) -} - func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error) { u.DOut("handleGetValue for key: %s\n", pmes.GetKey()) @@ -205,7 +192,7 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *Message) (*Message, err seq := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) for _, ps := range seq { - mes, err := msg.FromObject(ps, pmes) + _, err := msg.FromObject(ps, pmes) if err != nil { u.PErr("handleDiagnostics error creating message: %v\n", err) continue diff --git a/routing/dht/query.go b/routing/dht/query.go new file mode 100644 index 000000000..efedfcd8a --- /dev/null +++ b/routing/dht/query.go @@ -0,0 +1,85 @@ +package dht + +import ( + peer "github.com/jbenet/go-ipfs/peer" + queue "github.com/jbenet/go-ipfs/peer/queue" + u "github.com/jbenet/go-ipfs/util" + + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" +) + +type dhtQuery struct { + // a PeerQueue + peers queue.PeerQueue + + // the function to execute per peer + qfunc queryFunc +} + +// QueryFunc is a function that runs a particular query with a given peer. +// It returns either: +// - the value +// - a list of peers potentially better able to serve the query +// - an error +type queryFunc func(context.Context, *peer.Peer) (interface{}, []*peer.Peer, error) + +func (q *dhtQuery) Run(ctx context.Context, concurrency int) (interface{}, error) { + // get own cancel function to signal when we've found the value + ctx, cancel := context.WithCancel(ctx) + + // the variable waiting to be populated upon success + var result interface{} + + // chanQueue is how workers receive their work + chanQueue := queue.NewChanQueue(ctx, q.peers) + + // worker + worker := func() { + for { + select { + case p := <-chanQueue.DeqChan: + + val, closer, err := q.qfunc(ctx, p) + if err != nil { + u.PErr("error running query: %v\n", err) + continue + } + + if val != nil { + result = val + cancel() // signal we're done. + return + } + + if closer != nil { + for _, p := range closer { + select { + case chanQueue.EnqChan <- p: + case <-ctx.Done(): + return + } + } + } + + case <-ctx.Done(): + return + } + } + } + + // launch all workers + for i := 0; i < concurrency; i++ { + go worker() + } + + // wait until we're done. yep. + select { + case <-ctx.Done(): + } + + if result != nil { + return result, nil + } + + return nil, ctx.Err() +} diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 49fdb06ee..bee640e8f 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -4,14 +4,13 @@ import ( "bytes" "encoding/json" "errors" + "fmt" "time" - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - - swarm "github.com/jbenet/go-ipfs/net/swarm" peer "github.com/jbenet/go-ipfs/peer" + queue "github.com/jbenet/go-ipfs/peer/queue" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" ) @@ -23,29 +22,31 @@ import ( // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT func (dht *IpfsDHT) PutValue(key u.Key, value []byte) error { - complete := make(chan struct{}) - count := 0 + ctx := context.TODO() + + query := &dhtQuery{} + query.peers = queue.NewXORDistancePQ(key) + + // get the peers we need to announce to for _, route := range dht.routingTables { peers := route.NearestPeers(kb.ConvertKey(key), KValue) for _, p := range peers { if p == nil { - dht.network.Error(kb.ErrLookupFailure) - continue + // this shouldn't be happening. + panic("p should not be nil") } - count++ - go func(sp *peer.Peer) { - err := dht.putValueToNetwork(sp, string(key), value) - if err != nil { - dht.network.Error(err) - } - complete <- struct{}{} - }(p) + + query.peers.Enqueue(p) } } - for i := 0; i < count; i++ { - <-complete + + query.qfunc = func(ctx context.Context, p *peer.Peer) (interface{}, []*peer.Peer, error) { + dht.putValueToNetwork(ctx, p, string(key), value) + return nil, nil, nil } - return nil + + _, err := query.Run(ctx, query.peers.Len()) + return err } // GetValue searches for the value corresponding to given Key. @@ -53,10 +54,9 @@ func (dht *IpfsDHT) PutValue(key u.Key, value []byte) error { // returned along with util.ErrSearchIncomplete func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { ll := startNewRPC("GET") - defer func() { - ll.EndLog() - ll.Print() - }() + defer ll.EndAndPrint() + + ctx, _ := context.WithTimeout(context.TODO(), timeout) // If we have it local, dont bother doing an RPC! // NOTE: this might not be what we want to do... @@ -67,98 +67,37 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { return val, nil } + // get closest peers in the routing tables routeLevel := 0 closest := dht.routingTables[routeLevel].NearestPeers(kb.ConvertKey(key), PoolSize) if closest == nil || len(closest) == 0 { return nil, kb.ErrLookupFailure } - valChan := make(chan []byte) - npeerChan := make(chan *peer.Peer, 30) - procPeer := make(chan *peer.Peer, 30) - errChan := make(chan error) - after := time.After(timeout) - pset := newPeerSet() + query := &dhtQuery{} + query.peers = queue.NewXORDistancePQ(key) + // get the peers we need to announce to for _, p := range closest { - pset.Add(p) - npeerChan <- p + query.peers.Enqueue(p) } - c := counter{} - - count := 0 - go func() { - defer close(procPeer) - for { - select { - case p, ok := <-npeerChan: - if !ok { - return - } - count++ - if count >= KValue { - errChan <- u.ErrNotFound - return - } - c.Increment() - - procPeer <- p - default: - if c.Size() <= 0 { - select { - case errChan <- u.ErrNotFound: - default: - } - return - } - } - } - }() - - process := func() { - defer c.Decrement() - for p := range procPeer { - if p == nil { - return - } - val, peers, err := dht.getValueOrPeers(p, key, timeout/4, routeLevel) - if err != nil { - u.DErr("%v\n", err.Error()) - continue - } - if val != nil { - select { - case valChan <- val: - default: - u.DOut("Wasnt the first to return the value!") - } - return - } - - for _, np := range peers { - // TODO: filter out peers that arent closer - if !pset.Contains(np) && pset.Size() < KValue { - pset.Add(np) //This is racey... make a single function to do operation - npeerChan <- np - } - } - c.Decrement() - } + // setup the Query Function + query.qfunc = func(ctx context.Context, p *peer.Peer) (interface{}, []*peer.Peer, error) { + return dht.getValueOrPeers(ctx, p, key, routeLevel) } - for i := 0; i < AlphaValue; i++ { - go process() + // run it! + result, err := query.Run(ctx, query.peers.Len()) + if err != nil { + return nil, err } - select { - case val := <-valChan: - return val, nil - case err := <-errChan: - return nil, err - case <-after: - return nil, u.ErrTimeout + byt, ok := result.([]byte) + if !ok { + return nil, fmt.Errorf("received non-byte slice value") } + return byt, nil } // Value provider layer of indirection. @@ -166,26 +105,27 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { // Provide makes this node announce that it can provide a value for the given key func (dht *IpfsDHT) Provide(key u.Key) error { + ctx := context.TODO() + dht.providers.AddProvider(key, dht.self) peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), PoolSize) if len(peers) == 0 { return kb.ErrLookupFailure } - pmes := Message{ - Type: PBDHTMessage_ADD_PROVIDER, - Key: string(key), - } - pbmes := pmes.ToProtobuf() - for _, p := range peers { - mes := swarm.NewMessage(p, pbmes) - dht.netChan.Outgoing <- mes + err := dht.putProvider(ctx, p, string(key)) + if err != nil { + return err + } } return nil } +// FindProvidersAsync runs FindProviders and sends back results over a channel func (dht *IpfsDHT) FindProvidersAsync(key u.Key, count int, timeout time.Duration) chan *peer.Peer { + ctx, _ := context.WithTimeout(context.TODO(), timeout) + peerOut := make(chan *peer.Peer, count) go func() { ps := newPeerSet() @@ -202,13 +142,14 @@ func (dht *IpfsDHT) FindProvidersAsync(key u.Key, count int, timeout time.Durati peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), AlphaValue) for _, pp := range peers { + ppp := pp go func() { - pmes, err := dht.findProvidersSingle(pp, key, 0, timeout) + pmes, err := dht.findProvidersSingle(ctx, ppp, key, 0) if err != nil { u.PErr("%v\n", err) return } - dht.addPeerListAsync(key, pmes.GetPeers(), ps, count, peerOut) + dht.addPeerListAsync(key, pmes.GetProviderPeers(), ps, count, peerOut) }() } @@ -217,21 +158,15 @@ func (dht *IpfsDHT) FindProvidersAsync(key u.Key, count int, timeout time.Durati } //TODO: this function could also be done asynchronously -func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*PBDHTMessage_PBPeer, ps *peerSet, count int, out chan *peer.Peer) { +func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*Message_Peer, ps *peerSet, count int, out chan *peer.Peer) { for _, pbp := range peers { - if peer.ID(pbp.GetId()).Equal(dht.self.ID) { - continue - } - maddr, err := ma.NewMultiaddr(pbp.GetAddr()) - if err != nil { - u.PErr("%v\n", err) - continue - } - p, err := dht.network.GetConnection(peer.ID(pbp.GetId()), maddr) + + // construct new peer + p, err := dht.ensureConnectedToPeer(pbp) if err != nil { - u.PErr("%v\n", err) continue } + dht.providers.AddProvider(k, p) if ps.AddIfSmallerThan(p, count) { out <- p @@ -244,10 +179,11 @@ func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*PBDHTMessage_PBPeer, ps * // FindProviders searches for peers who can provide the value for given key. func (dht *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) { ll := startNewRPC("FindProviders") - defer func() { - ll.EndLog() - ll.Print() - }() + ll.EndAndPrint() + + ctx, _ := context.WithTimeout(context.TODO(), timeout) + + // get closest peer u.DOut("Find providers for: '%s'\n", key) p := dht.routingTables[0].NearestPeer(kb.ConvertKey(key)) if p == nil { @@ -255,37 +191,30 @@ func (dht *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Pee } for level := 0; level < len(dht.routingTables); { - pmes, err := dht.findProvidersSingle(p, key, level, timeout) + + // attempt retrieving providers + pmes, err := dht.findProvidersSingle(ctx, p, key, level) if err != nil { return nil, err } - if pmes.GetSuccess() { + + // handle providers + provs := pmes.GetProviderPeers() + if provs != nil { u.DOut("Got providers back from findProviders call!\n") - provs := dht.addProviders(key, pmes.GetPeers()) - ll.Success = true - return provs, nil + return dht.addProviders(key, provs), nil } u.DOut("Didnt get providers, just closer peers.\n") - - closer := pmes.GetPeers() + closer := pmes.GetCloserPeers() if len(closer) == 0 { level++ continue } - if peer.ID(closer[0].GetId()).Equal(dht.self.ID) { - u.DOut("Got myself back as a closer peer.") - return nil, u.ErrNotFound - } - maddr, err := ma.NewMultiaddr(closer[0].GetAddr()) - if err != nil { - // ??? Move up route level??? - panic("not yet implemented") - } - np, err := dht.network.GetConnection(peer.ID(closer[0].GetId()), maddr) + np, err := dht.peerFromInfo(closer[0]) if err != nil { - u.PErr("[%s] Failed to connect to: %s\n", dht.self.ID.Pretty(), closer[0].GetAddr()) + u.DOut("no peerFromInfo") level++ continue } @@ -298,12 +227,15 @@ func (dht *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Pee // FindPeer searches for a peer with given ID. func (dht *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) { + ctx, _ := context.WithTimeout(context.TODO(), timeout) + // Check if were already connected to them p, _ := dht.Find(id) if p != nil { return p, nil } + // @whyrusleeping why is this here? doesn't the dht.Find above cover it? routeLevel := 0 p = dht.routingTables[routeLevel].NearestPeer(kb.ConvertPeerID(id)) if p == nil { @@ -314,158 +246,140 @@ func (dht *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, err } for routeLevel < len(dht.routingTables) { - pmes, err := dht.findPeerSingle(p, id, timeout, routeLevel) - plist := pmes.GetPeers() + pmes, err := dht.findPeerSingle(ctx, p, id, routeLevel) + plist := pmes.GetCloserPeers() if plist == nil || len(plist) == 0 { routeLevel++ continue } found := plist[0] - addr, err := ma.NewMultiaddr(found.GetAddr()) + nxtPeer, err := dht.ensureConnectedToPeer(found) if err != nil { - return nil, err + routeLevel++ + continue } - nxtPeer, err := dht.network.GetConnection(peer.ID(found.GetId()), addr) - if err != nil { - return nil, err - } - if pmes.GetSuccess() { - if !id.Equal(nxtPeer.ID) { - return nil, errors.New("got back invalid peer from 'successful' response") - } + if nxtPeer.ID.Equal(id) { return nxtPeer, nil } + p = nxtPeer } return nil, u.ErrNotFound } func (dht *IpfsDHT) findPeerMultiple(id peer.ID, timeout time.Duration) (*peer.Peer, error) { + ctx, _ := context.WithTimeout(context.TODO(), timeout) + // Check if were already connected to them p, _ := dht.Find(id) if p != nil { return p, nil } + query := &dhtQuery{} + query.peers = queue.NewXORDistancePQ(u.Key(id)) + + // get the peers we need to announce to routeLevel := 0 peers := dht.routingTables[routeLevel].NearestPeers(kb.ConvertPeerID(id), AlphaValue) if len(peers) == 0 { return nil, kb.ErrLookupFailure } + for _, p := range peers { + query.peers.Enqueue(p) + } + + // setup query function + query.qfunc = func(ctx context.Context, p *peer.Peer) (interface{}, []*peer.Peer, error) { + pmes, err := dht.findPeerSingle(ctx, p, id, routeLevel) + if err != nil { + u.DErr("getPeer error: %v\n", err) + return nil, nil, err + } - found := make(chan *peer.Peer) - after := time.After(timeout) + plist := pmes.GetCloserPeers() + if len(plist) == 0 { + routeLevel++ + } - for _, p := range peers { - go func(p *peer.Peer) { - pmes, err := dht.findPeerSingle(p, id, timeout, routeLevel) + nxtprs := make([]*peer.Peer, len(plist)) + for i, fp := range plist { + nxtp, err := dht.peerFromInfo(fp) if err != nil { - u.DErr("getPeer error: %v\n", err) - return - } - plist := pmes.GetPeers() - if len(plist) == 0 { - routeLevel++ + u.DErr("findPeer error: %v\n", err) + continue } - for _, fp := range plist { - nxtp, err := dht.peerFromInfo(fp) - if err != nil { - u.DErr("findPeer error: %v\n", err) - continue - } - if nxtp.ID.Equal(dht.self.ID) { - found <- nxtp - return - } + if nxtp.ID.Equal(id) { + return nxtp, nil, nil } - }(p) + + nxtprs[i] = nxtp + } + + return nil, nxtprs, nil } - select { - case p := <-found: - return p, nil - case <-after: - return nil, u.ErrTimeout + p5, err := query.Run(ctx, query.peers.Len()) + if err != nil { + return nil, err + } + + p6, ok := p5.(*peer.Peer) + if !ok { + return nil, errors.New("received non peer object") } + return p6, nil } // Ping a peer, log the time it took func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { + ctx, _ := context.WithTimeout(context.TODO(), timeout) + // Thoughts: maybe this should accept an ID and do a peer lookup? u.DOut("Enter Ping.\n") - pmes := Message{ID: swarm.GenerateMessageID(), Type: PBDHTMessage_PING} - mes := swarm.NewMessage(p, pmes.ToProtobuf()) - - before := time.Now() - responseChan := dht.listener.Listen(pmes.ID, 1, time.Minute) - dht.netChan.Outgoing <- mes - - tout := time.After(timeout) - select { - case <-responseChan: - roundtrip := time.Since(before) - p.SetLatency(roundtrip) - u.DOut("Ping took %s.\n", roundtrip.String()) - return nil - case <-tout: - // Timed out, think about removing peer from network - u.DOut("[%s] Ping peer [%s] timed out.", dht.self.ID.Pretty(), p.ID.Pretty()) - dht.listener.Unlisten(pmes.ID) - return u.ErrTimeout - } + pmes := newMessage(Message_PING, "", 0) + _, err := dht.sendRequest(ctx, p, pmes) + return err } func (dht *IpfsDHT) getDiagnostic(timeout time.Duration) ([]*diagInfo, error) { - u.DOut("Begin Diagnostic") - //Send to N closest peers - targets := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) - - // TODO: Add timeout to this struct so nodes know when to return - pmes := Message{ - Type: PBDHTMessage_DIAGNOSTIC, - ID: swarm.GenerateMessageID(), - } + ctx, _ := context.WithTimeout(context.TODO(), timeout) - listenChan := dht.listener.Listen(pmes.ID, len(targets), time.Minute*2) + u.DOut("Begin Diagnostic") + query := &dhtQuery{} + query.peers = queue.NewXORDistancePQ(u.Key(dht.self.ID)) - pbmes := pmes.ToProtobuf() + targets := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) for _, p := range targets { - mes := swarm.NewMessage(p, pbmes) - dht.netChan.Outgoing <- mes + query.peers.Enqueue(p) } var out []*diagInfo - after := time.After(timeout) - for count := len(targets); count > 0; { - select { - case <-after: - u.DOut("Diagnostic request timed out.") - return out, u.ErrTimeout - case resp := <-listenChan: - pmesOut := new(PBDHTMessage) - err := proto.Unmarshal(resp.Data, pmesOut) - if err != nil { - // NOTE: here and elsewhere, need to audit error handling, - // some errors should be continued on from - return out, err - } - dec := json.NewDecoder(bytes.NewBuffer(pmesOut.GetValue())) - for { - di := new(diagInfo) - err := dec.Decode(di) - if err != nil { - break - } + query.qfunc = func(ctx context.Context, p *peer.Peer) (interface{}, []*peer.Peer, error) { + pmes := newMessage(Message_DIAGNOSTIC, "", 0) + rpmes, err := dht.sendRequest(ctx, p, pmes) + if err != nil { + return nil, nil, err + } - out = append(out, di) + dec := json.NewDecoder(bytes.NewBuffer(rpmes.GetValue())) + for { + di := new(diagInfo) + err := dec.Decode(di) + if err != nil { + break } + + out = append(out, di) } + return nil, nil, nil } - return nil, nil + _, err := query.Run(ctx, query.peers.Len()) + return out, err } From 785a52ff039745e982bd9297a4f84fc40ebd6d3b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 17 Sep 2014 10:30:38 -0700 Subject: [PATCH 0139/3817] tests compile This commit was moved from ipfs/go-ipfs-routing@5525115e58bcd3e1ea0f3b78aea5241121f4a8d8 --- routing/dht/dht_test.go | 81 +++++++--------- routing/dht/ext_test.go | 205 +++++++++++++++++++++------------------- 2 files changed, 145 insertions(+), 141 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index f021835e2..70fbb2ba2 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -3,11 +3,16 @@ package dht import ( "testing" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + ci "github.com/jbenet/go-ipfs/crypto" spipe "github.com/jbenet/go-ipfs/crypto/spipe" - swarm "github.com/jbenet/go-ipfs/net/swarm" + inet "github.com/jbenet/go-ipfs/net" + mux "github.com/jbenet/go-ipfs/net/mux" + netservice "github.com/jbenet/go-ipfs/net/service" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" @@ -16,6 +21,30 @@ import ( "time" ) +func setupDHT(t *testing.T, p *peer.Peer) *IpfsDHT { + ctx := context.TODO() + + peerstore := peer.NewPeerstore() + + ctx, _ = context.WithCancel(ctx) + dhts := netservice.NewService(nil) // nil handler for now, need to patch it + if err := dhts.Start(ctx); err != nil { + t.Fatal(err) + } + + net, err := inet.NewIpfsNetwork(context.TODO(), p, &mux.ProtocolMap{ + mux.ProtocolID_Routing: dhts, + }) + if err != nil { + t.Fatal(err) + } + + d := NewDHT(p, peerstore, net, dhts, ds.NewMapDatastore()) + dhts.Handler = d + d.Start() + return d +} + func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) { var addrs []*ma.Multiaddr for i := 0; i < 4; i++ { @@ -46,14 +75,7 @@ func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) var dhts []*IpfsDHT for i := 0; i < 4; i++ { - net := swarm.NewSwarm(peers[i]) - err := net.Listen() - if err != nil { - t.Fatal(err) - } - d := NewDHT(peers[i], net, ds.NewMapDatastore()) - dhts = append(dhts, d) - d.Start() + dhts[i] = setupDHT(t, peers[i]) } return addrs, peers, dhts @@ -91,19 +113,8 @@ func TestPing(t *testing.T) { peerA := makePeer(addrA) peerB := makePeer(addrB) - neta := swarm.NewSwarm(peerA) - err = neta.Listen() - if err != nil { - t.Fatal(err) - } - dhtA := NewDHT(peerA, neta, ds.NewMapDatastore()) - - netb := swarm.NewSwarm(peerB) - err = netb.Listen() - if err != nil { - t.Fatal(err) - } - dhtB := NewDHT(peerB, netb, ds.NewMapDatastore()) + dhtA := setupDHT(t, peerA) + dhtB := setupDHT(t, peerB) dhtA.Start() dhtB.Start() @@ -136,36 +147,14 @@ func TestValueGetSet(t *testing.T) { peerA := makePeer(addrA) peerB := makePeer(addrB) - neta := swarm.NewSwarm(peerA) - err = neta.Listen() - if err != nil { - t.Fatal(err) - } - dhtA := NewDHT(peerA, neta, ds.NewMapDatastore()) - - netb := swarm.NewSwarm(peerB) - err = netb.Listen() - if err != nil { - t.Fatal(err) - } - dhtB := NewDHT(peerB, netb, ds.NewMapDatastore()) + dhtA := setupDHT(t, peerA) + dhtB := setupDHT(t, peerB) dhtA.Start() dhtB.Start() defer dhtA.Halt() defer dhtB.Halt() - errsa := dhtA.network.GetErrChan() - errsb := dhtB.network.GetErrChan() - go func() { - select { - case err := <-errsa: - t.Fatal(err) - case err := <-errsb: - t.Fatal(err) - } - }() - _, err = dhtA.Connect(addrB) if err != nil { t.Fatal(err) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index fe98443ad..64abb9644 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -5,11 +5,13 @@ import ( crand "crypto/rand" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - swarm "github.com/jbenet/go-ipfs/net/swarm" + msg "github.com/jbenet/go-ipfs/net/message" + mux "github.com/jbenet/go-ipfs/net/mux" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" @@ -18,79 +20,84 @@ import ( // fauxNet is a standin for a swarm.Network in order to more easily recreate // different testing scenarios -type fauxNet struct { - Chan *swarm.Chan +type fauxSender struct { handlers []mesHandleFunc - - swarm.Network } -// mesHandleFunc is a function that takes in outgoing messages -// and can respond to them, simulating other peers on the network. -// returning nil will chose not to respond and pass the message onto the -// next registered handler -type mesHandleFunc func(*swarm.Message) *swarm.Message +func (f *fauxSender) SendRequest(ctx context.Context, m msg.NetMessage) (msg.NetMessage, error) { -func newFauxNet() *fauxNet { - fn := new(fauxNet) - fn.Chan = swarm.NewChan(8) + for _, h := range f.handlers { + reply := h(m) + if reply != nil { + return reply, nil + } + } - return fn + return nil, nil } -// Instead of 'Listening' Start up a goroutine that will check -// all outgoing messages against registered message handlers, -// and reply if needed -func (f *fauxNet) Listen() error { - go func() { - for { - select { - case in := <-f.Chan.Outgoing: - for _, h := range f.handlers { - reply := h(in) - if reply != nil { - f.Chan.Incoming <- reply - break - } - } - } +func (f *fauxSender) SendMessage(ctx context.Context, m msg.NetMessage) error { + for _, h := range f.handlers { + reply := h(m) + if reply != nil { + return nil } - }() + } return nil } -func (f *fauxNet) AddHandler(fn func(*swarm.Message) *swarm.Message) { - f.handlers = append(f.handlers, fn) +// fauxNet is a standin for a swarm.Network in order to more easily recreate +// different testing scenarios +type fauxNet struct { + handlers []mesHandleFunc } -func (f *fauxNet) Send(mes *swarm.Message) { - f.Chan.Outgoing <- mes +// mesHandleFunc is a function that takes in outgoing messages +// and can respond to them, simulating other peers on the network. +// returning nil will chose not to respond and pass the message onto the +// next registered handler +type mesHandleFunc func(msg.NetMessage) msg.NetMessage + +func (f *fauxNet) AddHandler(fn func(msg.NetMessage) msg.NetMessage) { + f.handlers = append(f.handlers, fn) } -func (f *fauxNet) GetErrChan() chan error { - return f.Chan.Errors +// DialPeer attempts to establish a connection to a given peer +func (f *fauxNet) DialPeer(*peer.Peer) error { + return nil } -func (f *fauxNet) GetChannel(t swarm.PBWrapper_MessageType) *swarm.Chan { - return f.Chan +// ClosePeer connection to peer +func (f *fauxNet) ClosePeer(*peer.Peer) error { + return nil } -func (f *fauxNet) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { - return nil, nil +// IsConnected returns whether a connection to given peer exists. +func (f *fauxNet) IsConnected(*peer.Peer) (bool, error) { + return true, nil } -func (f *fauxNet) GetConnection(id peer.ID, addr *ma.Multiaddr) (*peer.Peer, error) { - return &peer.Peer{ID: id, Addresses: []*ma.Multiaddr{addr}}, nil +// GetProtocols returns the protocols registered in the network. +func (f *fauxNet) GetProtocols() *mux.ProtocolMap { return nil } + +// SendMessage sends given Message out +func (f *fauxNet) SendMessage(msg.NetMessage) error { + return nil } +// Close terminates all network operation +func (f *fauxNet) Close() error { return nil } + func TestGetFailures(t *testing.T) { - fn := newFauxNet() - fn.Listen() + ctx := context.Background() + fn := &fauxNet{} + fs := &fauxSender{} + peerstore := peer.NewPeerstore() local := new(peer.Peer) local.ID = peer.ID("test_peer") - d := NewDHT(local, fn, ds.NewMapDatastore()) + d := NewDHT(local, peerstore, fn, fs, ds.NewMapDatastore()) other := &peer.Peer{ID: peer.ID("other_peer")} @@ -109,20 +116,18 @@ func TestGetFailures(t *testing.T) { } // Reply with failures to every message - fn.AddHandler(func(mes *swarm.Message) *swarm.Message { - pmes := new(PBDHTMessage) - err := proto.Unmarshal(mes.Data, pmes) + fn.AddHandler(func(mes msg.NetMessage) msg.NetMessage { + pmes := new(Message) + err := proto.Unmarshal(mes.Data(), pmes) if err != nil { t.Fatal(err) } - resp := Message{ - Type: pmes.GetType(), - ID: pmes.GetId(), - Response: true, - Success: false, + resp := &Message{ + Type: pmes.Type, } - return swarm.NewMessage(mes.Peer, resp.ToProtobuf()) + m, err := msg.FromObject(mes.Peer(), resp) + return m }) // This one should fail with NotFound @@ -137,27 +142,34 @@ func TestGetFailures(t *testing.T) { success := make(chan struct{}) fn.handlers = nil - fn.AddHandler(func(mes *swarm.Message) *swarm.Message { - resp := new(PBDHTMessage) - err := proto.Unmarshal(mes.Data, resp) + fn.AddHandler(func(mes msg.NetMessage) msg.NetMessage { + resp := new(Message) + err := proto.Unmarshal(mes.Data(), resp) if err != nil { t.Fatal(err) } - if resp.GetSuccess() { - t.Fatal("Get returned success when it shouldnt have.") - } success <- struct{}{} return nil }) // Now we test this DHT's handleGetValue failure + typ := Message_GET_VALUE + str := "hello" req := Message{ - Type: PBDHTMessage_GET_VALUE, - Key: "hello", - ID: swarm.GenerateMessageID(), + Type: &typ, + Key: &str, Value: []byte{0}, } - fn.Chan.Incoming <- swarm.NewMessage(other, req.ToProtobuf()) + + mes, err := msg.FromObject(other, &req) + if err != nil { + t.Error(err) + } + + mes, err = fs.SendRequest(ctx, mes) + if err != nil { + t.Error(err) + } <-success } @@ -172,13 +184,14 @@ func _randPeer() *peer.Peer { } func TestNotFound(t *testing.T) { - fn := newFauxNet() - fn.Listen() + fn := &fauxNet{} + fs := &fauxSender{} local := new(peer.Peer) local.ID = peer.ID("test_peer") + peerstore := peer.NewPeerstore() - d := NewDHT(local, fn, ds.NewMapDatastore()) + d := NewDHT(local, peerstore, fn, fs, ds.NewMapDatastore()) d.Start() var ps []*peer.Peer @@ -188,26 +201,27 @@ func TestNotFound(t *testing.T) { } // Reply with random peers to every message - fn.AddHandler(func(mes *swarm.Message) *swarm.Message { - pmes := new(PBDHTMessage) - err := proto.Unmarshal(mes.Data, pmes) + fn.AddHandler(func(mes msg.NetMessage) msg.NetMessage { + pmes := new(Message) + err := proto.Unmarshal(mes.Data(), pmes) if err != nil { t.Fatal(err) } switch pmes.GetType() { - case PBDHTMessage_GET_VALUE: - resp := Message{ - Type: pmes.GetType(), - ID: pmes.GetId(), - Response: true, - Success: false, - } + case Message_GET_VALUE: + resp := &Message{Type: pmes.Type} + peers := []*peer.Peer{} for i := 0; i < 7; i++ { - resp.Peers = append(resp.Peers, _randPeer()) + peers = append(peers, _randPeer()) + } + resp.CloserPeers = peersToPBPeers(peers) + mes, err := msg.FromObject(mes.Peer(), resp) + if err != nil { + t.Error(err) } - return swarm.NewMessage(mes.Peer, resp.ToProtobuf()) + return mes default: panic("Shouldnt recieve this.") } @@ -233,13 +247,13 @@ func TestNotFound(t *testing.T) { // a GET rpc and nobody has the value func TestLessThanKResponses(t *testing.T) { u.Debug = false - fn := newFauxNet() - fn.Listen() - + fn := &fauxNet{} + fs := &fauxSender{} + peerstore := peer.NewPeerstore() local := new(peer.Peer) local.ID = peer.ID("test_peer") - d := NewDHT(local, fn, ds.NewMapDatastore()) + d := NewDHT(local, peerstore, fn, fs, ds.NewMapDatastore()) d.Start() var ps []*peer.Peer @@ -250,24 +264,25 @@ func TestLessThanKResponses(t *testing.T) { other := _randPeer() // Reply with random peers to every message - fn.AddHandler(func(mes *swarm.Message) *swarm.Message { - pmes := new(PBDHTMessage) - err := proto.Unmarshal(mes.Data, pmes) + fn.AddHandler(func(mes msg.NetMessage) msg.NetMessage { + pmes := new(Message) + err := proto.Unmarshal(mes.Data(), pmes) if err != nil { t.Fatal(err) } switch pmes.GetType() { - case PBDHTMessage_GET_VALUE: - resp := Message{ - Type: pmes.GetType(), - ID: pmes.GetId(), - Response: true, - Success: false, - Peers: []*peer.Peer{other}, + case Message_GET_VALUE: + resp := &Message{ + Type: pmes.Type, + CloserPeers: peersToPBPeers([]*peer.Peer{other}), } - return swarm.NewMessage(mes.Peer, resp.ToProtobuf()) + mes, err := msg.FromObject(mes.Peer(), resp) + if err != nil { + t.Error(err) + } + return mes default: panic("Shouldnt recieve this.") } From f960c8fd8e20d6fabc80695f7ae66b8c3b845940 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 17 Sep 2014 13:51:52 -0700 Subject: [PATCH 0140/3817] fix(routing/dht) match the routing interface the channel's "spin" is specified in the interface now =) This commit was moved from ipfs/go-ipfs-routing@4e20cc595082c961aa189a3ee72302b3f199db0d --- routing/dht/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index bee640e8f..06fe39bcb 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -123,7 +123,7 @@ func (dht *IpfsDHT) Provide(key u.Key) error { } // FindProvidersAsync runs FindProviders and sends back results over a channel -func (dht *IpfsDHT) FindProvidersAsync(key u.Key, count int, timeout time.Duration) chan *peer.Peer { +func (dht *IpfsDHT) FindProvidersAsync(key u.Key, count int, timeout time.Duration) <-chan *peer.Peer { ctx, _ := context.WithTimeout(context.TODO(), timeout) peerOut := make(chan *peer.Peer, count) From 236e065d25543a433d5cac517cb1afbe65e68f69 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 18 Sep 2014 19:30:04 -0700 Subject: [PATCH 0141/3817] better query processing (runner) This commit was moved from ipfs/go-ipfs-routing@b52d14c01e89ad7081da8d17fa3f867dc879196f --- routing/dht/dht.go | 24 +++-- routing/dht/ext_test.go | 82 +++++++------- routing/dht/handlers.go | 16 ++- routing/dht/query.go | 231 +++++++++++++++++++++++++++++++--------- routing/dht/routing.go | 108 ++++++++----------- 5 files changed, 291 insertions(+), 170 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index d3f075762..13311f614 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -177,6 +177,9 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p *peer.Peer, pmes *Message if err != nil { return nil, err } + if rmes == nil { + return nil, errors.New("no response to request") + } rtt := time.Since(start) rmes.Peer().SetLatency(rtt) @@ -218,19 +221,22 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, return nil, nil, err } + u.POut("pmes.GetValue() %v\n", pmes.GetValue()) if value := pmes.GetValue(); value != nil { // Success! We were given the value + u.POut("getValueOrPeers: got value\n") return value, nil, nil } // TODO decide on providers. This probably shouldn't be happening. - // if prv := pmes.GetProviderPeers(); prv != nil && len(prv) > 0 { - // val, err := dht.getFromPeerList(key, timeout,, level) - // if err != nil { - // return nil, nil, err - // } - // return val, nil, nil - // } + if prv := pmes.GetProviderPeers(); prv != nil && len(prv) > 0 { + val, err := dht.getFromPeerList(ctx, key, prv, level) + if err != nil { + return nil, nil, err + } + u.POut("getValueOrPeers: get from providers\n") + return val, nil, nil + } // Perhaps we were given closer peers var peers []*peer.Peer @@ -256,10 +262,12 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, } if len(peers) > 0 { + u.POut("getValueOrPeers: peers\n") return nil, peers, nil } - return nil, nil, errors.New("NotFound. did not get value or closer peers.") + u.POut("getValueOrPeers: u.ErrNotFound\n") + return nil, nil, u.ErrNotFound } // getValueSingle simply performs the get value RPC with the given parameters diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 64abb9644..47eb6429a 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -18,14 +18,23 @@ import ( "time" ) +// mesHandleFunc is a function that takes in outgoing messages +// and can respond to them, simulating other peers on the network. +// returning nil will chose not to respond and pass the message onto the +// next registered handler +type mesHandleFunc func(msg.NetMessage) msg.NetMessage + // fauxNet is a standin for a swarm.Network in order to more easily recreate // different testing scenarios type fauxSender struct { handlers []mesHandleFunc } -func (f *fauxSender) SendRequest(ctx context.Context, m msg.NetMessage) (msg.NetMessage, error) { +func (f *fauxSender) AddHandler(fn func(msg.NetMessage) msg.NetMessage) { + f.handlers = append(f.handlers, fn) +} +func (f *fauxSender) SendRequest(ctx context.Context, m msg.NetMessage) (msg.NetMessage, error) { for _, h := range f.handlers { reply := h(m) if reply != nil { @@ -33,7 +42,12 @@ func (f *fauxSender) SendRequest(ctx context.Context, m msg.NetMessage) (msg.Net } } - return nil, nil + // no reply? ok force a timeout + select { + case <-ctx.Done(): + } + + return nil, ctx.Err() } func (f *fauxSender) SendMessage(ctx context.Context, m msg.NetMessage) error { @@ -49,17 +63,6 @@ func (f *fauxSender) SendMessage(ctx context.Context, m msg.NetMessage) error { // fauxNet is a standin for a swarm.Network in order to more easily recreate // different testing scenarios type fauxNet struct { - handlers []mesHandleFunc -} - -// mesHandleFunc is a function that takes in outgoing messages -// and can respond to them, simulating other peers on the network. -// returning nil will chose not to respond and pass the message onto the -// next registered handler -type mesHandleFunc func(msg.NetMessage) msg.NetMessage - -func (f *fauxNet) AddHandler(fn func(msg.NetMessage) msg.NetMessage) { - f.handlers = append(f.handlers, fn) } // DialPeer attempts to establish a connection to a given peer @@ -98,25 +101,23 @@ func TestGetFailures(t *testing.T) { local.ID = peer.ID("test_peer") d := NewDHT(local, peerstore, fn, fs, ds.NewMapDatastore()) - other := &peer.Peer{ID: peer.ID("other_peer")} - - d.Start() - d.Update(other) // This one should time out + // u.POut("Timout Test\n") _, err := d.GetValue(u.Key("test"), time.Millisecond*10) if err != nil { - if err != u.ErrTimeout { - t.Fatal("Got different error than we expected.") + if err != context.DeadlineExceeded { + t.Fatal("Got different error than we expected", err) } } else { t.Fatal("Did not get expected error!") } + // u.POut("NotFound Test\n") // Reply with failures to every message - fn.AddHandler(func(mes msg.NetMessage) msg.NetMessage { + fs.AddHandler(func(mes msg.NetMessage) msg.NetMessage { pmes := new(Message) err := proto.Unmarshal(mes.Data(), pmes) if err != nil { @@ -140,18 +141,7 @@ func TestGetFailures(t *testing.T) { t.Fatal("expected error, got none.") } - success := make(chan struct{}) - fn.handlers = nil - fn.AddHandler(func(mes msg.NetMessage) msg.NetMessage { - resp := new(Message) - err := proto.Unmarshal(mes.Data(), resp) - if err != nil { - t.Fatal(err) - } - success <- struct{}{} - return nil - }) - + fs.handlers = nil // Now we test this DHT's handleGetValue failure typ := Message_GET_VALUE str := "hello" @@ -161,17 +151,32 @@ func TestGetFailures(t *testing.T) { Value: []byte{0}, } + // u.POut("handleGetValue Test\n") mes, err := msg.FromObject(other, &req) if err != nil { t.Error(err) } - mes, err = fs.SendRequest(ctx, mes) + mes, err = d.HandleMessage(ctx, mes) if err != nil { t.Error(err) } - <-success + pmes := new(Message) + err = proto.Unmarshal(mes.Data(), pmes) + if err != nil { + t.Fatal(err) + } + if pmes.GetValue() != nil { + t.Fatal("shouldnt have value") + } + if pmes.GetCloserPeers() != nil { + t.Fatal("shouldnt have closer peers") + } + if pmes.GetProviderPeers() != nil { + t.Fatal("shouldnt have provider peers") + } + } // TODO: Maybe put these in some sort of "ipfs_testutil" package @@ -192,7 +197,6 @@ func TestNotFound(t *testing.T) { peerstore := peer.NewPeerstore() d := NewDHT(local, peerstore, fn, fs, ds.NewMapDatastore()) - d.Start() var ps []*peer.Peer for i := 0; i < 5; i++ { @@ -201,7 +205,7 @@ func TestNotFound(t *testing.T) { } // Reply with random peers to every message - fn.AddHandler(func(mes msg.NetMessage) msg.NetMessage { + fs.AddHandler(func(mes msg.NetMessage) msg.NetMessage { pmes := new(Message) err := proto.Unmarshal(mes.Data(), pmes) if err != nil { @@ -228,7 +232,8 @@ func TestNotFound(t *testing.T) { }) - _, err := d.GetValue(u.Key("hello"), time.Second*30) + v, err := d.GetValue(u.Key("hello"), time.Second*5) + u.POut("get value got %v\n", v) if err != nil { switch err { case u.ErrNotFound: @@ -254,7 +259,6 @@ func TestLessThanKResponses(t *testing.T) { local.ID = peer.ID("test_peer") d := NewDHT(local, peerstore, fn, fs, ds.NewMapDatastore()) - d.Start() var ps []*peer.Peer for i := 0; i < 5; i++ { @@ -264,7 +268,7 @@ func TestLessThanKResponses(t *testing.T) { other := _randPeer() // Reply with random peers to every message - fn.AddHandler(func(mes msg.NetMessage) msg.NetMessage { + fs.AddHandler(func(mes msg.NetMessage) msg.NetMessage { pmes := new(Message) err := proto.Unmarshal(mes.Data(), pmes) if err != nil { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 71e5eb037..ddf76a669 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -58,7 +58,10 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error return nil, err } - // if we have the value, respond with it! + // Note: changed the behavior here to return _as much_ info as possible + // (potentially all of {value, closer peers, provider}) + + // if we have the value, send it back if err == nil { u.DOut("handleGetValue success!\n") @@ -68,7 +71,6 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error } resp.Value = byts - return resp, nil } // if we know any providers for the requested value, return those. @@ -76,20 +78,16 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error if len(provs) > 0 { u.DOut("handleGetValue returning %d provider[s]\n", len(provs)) resp.ProviderPeers = peersToPBPeers(provs) - return resp, nil } // Find closest peer on given cluster to desired key and reply with that info closer := dht.betterPeerToQuery(pmes) - if closer == nil { - u.DOut("handleGetValue could not find a closer node than myself.\n") - resp.CloserPeers = nil + if closer != nil { + u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty()) + resp.CloserPeers = peersToPBPeers([]*peer.Peer{closer}) return resp, nil } - // we got a closer peer, it seems. return it. - u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty()) - resp.CloserPeers = peersToPBPeers([]*peer.Peer{closer}) return resp, nil } diff --git a/routing/dht/query.go b/routing/dht/query.go index efedfcd8a..ecdc4c62c 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -1,19 +1,45 @@ package dht import ( + "sync" + peer "github.com/jbenet/go-ipfs/peer" queue "github.com/jbenet/go-ipfs/peer/queue" + kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" + todoctr "github.com/jbenet/go-ipfs/util/todocounter" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ) +const maxQueryConcurrency = 5 + type dhtQuery struct { - // a PeerQueue - peers queue.PeerQueue + // the key we're querying for + key u.Key // the function to execute per peer qfunc queryFunc + + // the concurrency parameter + concurrency int +} + +type dhtQueryResult struct { + value []byte // GetValue + peer *peer.Peer // FindPeer + providerPeers []*peer.Peer // GetProviders + closerPeers []*peer.Peer // * + success bool +} + +// constructs query +func newQuery(k u.Key, f queryFunc) *dhtQuery { + return &dhtQuery{ + key: k, + qfunc: f, + concurrency: maxQueryConcurrency, + } } // QueryFunc is a function that runs a particular query with a given peer. @@ -21,65 +47,170 @@ type dhtQuery struct { // - the value // - a list of peers potentially better able to serve the query // - an error -type queryFunc func(context.Context, *peer.Peer) (interface{}, []*peer.Peer, error) +type queryFunc func(context.Context, *peer.Peer) (*dhtQueryResult, error) + +// Run runs the query at hand. pass in a list of peers to use first. +func (q *dhtQuery) Run(ctx context.Context, peers []*peer.Peer) (*dhtQueryResult, error) { + runner := newQueryRunner(ctx, q) + return runner.Run(peers) +} + +type dhtQueryRunner struct { + + // the query to run + query *dhtQuery + + // peersToQuery is a list of peers remaining to query + peersToQuery *queue.ChanQueue + + // peersSeen are all the peers queried. used to prevent querying same peer 2x + peersSeen peer.Map -func (q *dhtQuery) Run(ctx context.Context, concurrency int) (interface{}, error) { - // get own cancel function to signal when we've found the value + // rateLimit is a channel used to rate limit our processing (semaphore) + rateLimit chan struct{} + + // peersRemaining is a counter of peers remaining (toQuery + processing) + peersRemaining todoctr.Counter + + // context + ctx context.Context + cancel context.CancelFunc + + // result + result *dhtQueryResult + + // result errors + errs []error + + // lock for concurrent access to fields + sync.RWMutex +} + +func newQueryRunner(ctx context.Context, q *dhtQuery) *dhtQueryRunner { ctx, cancel := context.WithCancel(ctx) - // the variable waiting to be populated upon success - var result interface{} - - // chanQueue is how workers receive their work - chanQueue := queue.NewChanQueue(ctx, q.peers) - - // worker - worker := func() { - for { - select { - case p := <-chanQueue.DeqChan: - - val, closer, err := q.qfunc(ctx, p) - if err != nil { - u.PErr("error running query: %v\n", err) - continue - } - - if val != nil { - result = val - cancel() // signal we're done. - return - } - - if closer != nil { - for _, p := range closer { - select { - case chanQueue.EnqChan <- p: - case <-ctx.Done(): - return - } - } - } - - case <-ctx.Done(): - return - } - } + return &dhtQueryRunner{ + ctx: ctx, + cancel: cancel, + query: q, + peersToQuery: queue.NewChanQueue(ctx, queue.NewXORDistancePQ(q.key)), + peersRemaining: todoctr.NewSyncCounter(), + peersSeen: peer.Map{}, + rateLimit: make(chan struct{}, q.concurrency), + } +} + +func (r *dhtQueryRunner) Run(peers []*peer.Peer) (*dhtQueryResult, error) { + // setup concurrency rate limiting + for i := 0; i < r.query.concurrency; i++ { + r.rateLimit <- struct{}{} } - // launch all workers - for i := 0; i < concurrency; i++ { - go worker() + // add all the peers we got first. + for _, p := range peers { + r.addPeerToQuery(p, nil) // don't have access to self here... } // wait until we're done. yep. select { - case <-ctx.Done(): + case <-r.peersRemaining.Done(): + r.cancel() // ran all and nothing. cancel all outstanding workers. + + r.RLock() + defer r.RUnlock() + + if len(r.errs) > 0 { + return nil, r.errs[0] + } + return nil, u.ErrNotFound + + case <-r.ctx.Done(): + r.RLock() + defer r.RUnlock() + + if r.result != nil && r.result.success { + return r.result, nil + } + return nil, r.ctx.Err() + } + +} + +func (r *dhtQueryRunner) addPeerToQuery(next *peer.Peer, benchmark *peer.Peer) { + if next == nil { + // wtf why are peers nil?!? + u.PErr("Query getting nil peers!!!\n") + return + } + + // if new peer further away than whom we got it from, bother (loops) + if benchmark != nil && kb.Closer(benchmark.ID, next.ID, r.query.key) { + return + } + + // if already seen, no need. + r.Lock() + _, found := r.peersSeen[next.Key()] + if found { + r.Unlock() + return + } + r.peersSeen[next.Key()] = next + r.Unlock() + + // do this after unlocking to prevent possible deadlocks. + r.peersRemaining.Increment(1) + select { + case r.peersToQuery.EnqChan <- next: + case <-r.ctx.Done(): } +} + +func (r *dhtQueryRunner) spawnWorkers(p *peer.Peer) { + for { + select { + case <-r.peersRemaining.Done(): + return + + case <-r.ctx.Done(): + return + + case p := <-r.peersToQuery.DeqChan: + go r.queryPeer(p) + } + } +} - if result != nil { - return result, nil +func (r *dhtQueryRunner) queryPeer(p *peer.Peer) { + // make sure we rate limit concurrency. + select { + case <-r.rateLimit: + case <-r.ctx.Done(): + r.peersRemaining.Decrement(1) + return + } + + // finally, run the query against this peer + res, err := r.query.qfunc(r.ctx, p) + + if err != nil { + r.Lock() + r.errs = append(r.errs, err) + r.Unlock() + + } else if res.success { + r.Lock() + r.result = res + r.Unlock() + r.cancel() // signal to everyone that we're done. + + } else if res.closerPeers != nil { + for _, next := range res.closerPeers { + r.addPeerToQuery(next, p) + } } - return nil, ctx.Err() + // signal we're done proccessing peer p + r.peersRemaining.Decrement(1) + r.rateLimit <- struct{}{} } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 06fe39bcb..2410dcd3a 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -3,14 +3,11 @@ package dht import ( "bytes" "encoding/json" - "errors" - "fmt" "time" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" peer "github.com/jbenet/go-ipfs/peer" - queue "github.com/jbenet/go-ipfs/peer/queue" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" ) @@ -24,28 +21,23 @@ import ( func (dht *IpfsDHT) PutValue(key u.Key, value []byte) error { ctx := context.TODO() - query := &dhtQuery{} - query.peers = queue.NewXORDistancePQ(key) + peers := []*peer.Peer{} // get the peers we need to announce to for _, route := range dht.routingTables { - peers := route.NearestPeers(kb.ConvertKey(key), KValue) - for _, p := range peers { - if p == nil { - // this shouldn't be happening. - panic("p should not be nil") - } - - query.peers.Enqueue(p) - } + npeers := route.NearestPeers(kb.ConvertKey(key), KValue) + peers = append(peers, npeers...) } - query.qfunc = func(ctx context.Context, p *peer.Peer) (interface{}, []*peer.Peer, error) { - dht.putValueToNetwork(ctx, p, string(key), value) - return nil, nil, nil - } + query := newQuery(key, func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) { + err := dht.putValueToNetwork(ctx, p, string(key), value) + if err != nil { + return nil, err + } + return &dhtQueryResult{success: true}, nil + }) - _, err := query.Run(ctx, query.peers.Len()) + _, err := query.Run(ctx, peers) return err } @@ -63,7 +55,6 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { val, err := dht.getLocal(key) if err == nil { ll.Success = true - u.DOut("Found local, returning.\n") return val, nil } @@ -74,30 +65,33 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { return nil, kb.ErrLookupFailure } - query := &dhtQuery{} - query.peers = queue.NewXORDistancePQ(key) + // setup the Query + query := newQuery(key, func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) { - // get the peers we need to announce to - for _, p := range closest { - query.peers.Enqueue(p) - } + val, peers, err := dht.getValueOrPeers(ctx, p, key, routeLevel) + if err != nil { + return nil, err + } - // setup the Query Function - query.qfunc = func(ctx context.Context, p *peer.Peer) (interface{}, []*peer.Peer, error) { - return dht.getValueOrPeers(ctx, p, key, routeLevel) - } + res := &dhtQueryResult{value: val, closerPeers: peers} + if val != nil { + res.success = true + } + + return res, nil + }) // run it! - result, err := query.Run(ctx, query.peers.Len()) + result, err := query.Run(ctx, closest) if err != nil { return nil, err } - byt, ok := result.([]byte) - if !ok { - return nil, fmt.Errorf("received non-byte slice value") + if result.value == nil { + return nil, u.ErrNotFound } - return byt, nil + + return result.value, nil } // Value provider layer of indirection. @@ -278,25 +272,19 @@ func (dht *IpfsDHT) findPeerMultiple(id peer.ID, timeout time.Duration) (*peer.P return p, nil } - query := &dhtQuery{} - query.peers = queue.NewXORDistancePQ(u.Key(id)) - // get the peers we need to announce to routeLevel := 0 peers := dht.routingTables[routeLevel].NearestPeers(kb.ConvertPeerID(id), AlphaValue) if len(peers) == 0 { return nil, kb.ErrLookupFailure } - for _, p := range peers { - query.peers.Enqueue(p) - } // setup query function - query.qfunc = func(ctx context.Context, p *peer.Peer) (interface{}, []*peer.Peer, error) { + query := newQuery(u.Key(id), func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) { pmes, err := dht.findPeerSingle(ctx, p, id, routeLevel) if err != nil { u.DErr("getPeer error: %v\n", err) - return nil, nil, err + return nil, err } plist := pmes.GetCloserPeers() @@ -313,25 +301,24 @@ func (dht *IpfsDHT) findPeerMultiple(id peer.ID, timeout time.Duration) (*peer.P } if nxtp.ID.Equal(id) { - return nxtp, nil, nil + return &dhtQueryResult{peer: nxtp, success: true}, nil } nxtprs[i] = nxtp } - return nil, nxtprs, nil - } + return &dhtQueryResult{closerPeers: nxtprs}, nil + }) - p5, err := query.Run(ctx, query.peers.Len()) + result, err := query.Run(ctx, peers) if err != nil { return nil, err } - p6, ok := p5.(*peer.Peer) - if !ok { - return nil, errors.New("received non peer object") + if result.peer == nil { + return nil, u.ErrNotFound } - return p6, nil + return result.peer, nil } // Ping a peer, log the time it took @@ -350,21 +337,14 @@ func (dht *IpfsDHT) getDiagnostic(timeout time.Duration) ([]*diagInfo, error) { ctx, _ := context.WithTimeout(context.TODO(), timeout) u.DOut("Begin Diagnostic") - query := &dhtQuery{} - query.peers = queue.NewXORDistancePQ(u.Key(dht.self.ID)) - - targets := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) - for _, p := range targets { - query.peers.Enqueue(p) - } - + peers := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) var out []*diagInfo - query.qfunc = func(ctx context.Context, p *peer.Peer) (interface{}, []*peer.Peer, error) { + query := newQuery(dht.self.Key(), func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) { pmes := newMessage(Message_DIAGNOSTIC, "", 0) rpmes, err := dht.sendRequest(ctx, p, pmes) if err != nil { - return nil, nil, err + return nil, err } dec := json.NewDecoder(bytes.NewBuffer(rpmes.GetValue())) @@ -377,9 +357,9 @@ func (dht *IpfsDHT) getDiagnostic(timeout time.Duration) ([]*diagInfo, error) { out = append(out, di) } - return nil, nil, nil - } + return &dhtQueryResult{success: true}, nil + }) - _, err := query.Run(ctx, query.peers.Len()) + _, err := query.Run(ctx, peers) return out, err } From bd8b1c54eaa1ea9b1d490da5094884b43490abf7 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 18 Sep 2014 19:41:41 -0700 Subject: [PATCH 0142/3817] added some logging This commit was moved from ipfs/go-ipfs-routing@80b92f566f6e15a5c9908140de4b591548c52d2e --- routing/dht/query.go | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/routing/dht/query.go b/routing/dht/query.go index ecdc4c62c..400259635 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -111,7 +111,12 @@ func (r *dhtQueryRunner) Run(peers []*peer.Peer) (*dhtQueryResult, error) { r.addPeerToQuery(p, nil) // don't have access to self here... } - // wait until we're done. yep. + // go do this thing. + go r.spawnWorkers() + + // so workers are working. + + // wait until they're done. select { case <-r.peersRemaining.Done(): r.cancel() // ran all and nothing. cancel all outstanding workers. @@ -158,6 +163,8 @@ func (r *dhtQueryRunner) addPeerToQuery(next *peer.Peer, benchmark *peer.Peer) { r.peersSeen[next.Key()] = next r.Unlock() + u.POut("adding peer to query: %v\n", next.ID.Pretty()) + // do this after unlocking to prevent possible deadlocks. r.peersRemaining.Increment(1) select { @@ -166,8 +173,9 @@ func (r *dhtQueryRunner) addPeerToQuery(next *peer.Peer, benchmark *peer.Peer) { } } -func (r *dhtQueryRunner) spawnWorkers(p *peer.Peer) { +func (r *dhtQueryRunner) spawnWorkers() { for { + select { case <-r.peersRemaining.Done(): return @@ -175,13 +183,19 @@ func (r *dhtQueryRunner) spawnWorkers(p *peer.Peer) { case <-r.ctx.Done(): return - case p := <-r.peersToQuery.DeqChan: + case p, more := <-r.peersToQuery.DeqChan: + if !more { + return // channel closed. + } + u.POut("spawning worker for: %v\n", p.ID.Pretty()) go r.queryPeer(p) } } } func (r *dhtQueryRunner) queryPeer(p *peer.Peer) { + u.POut("spawned worker for: %v\n", p.ID.Pretty()) + // make sure we rate limit concurrency. select { case <-r.rateLimit: @@ -190,27 +204,33 @@ func (r *dhtQueryRunner) queryPeer(p *peer.Peer) { return } + u.POut("running worker for: %v\n", p.ID.Pretty()) + // finally, run the query against this peer res, err := r.query.qfunc(r.ctx, p) if err != nil { + u.POut("ERROR worker for: %v %v\n", p.ID.Pretty(), err) r.Lock() r.errs = append(r.errs, err) r.Unlock() } else if res.success { + u.POut("SUCCESS worker for: %v\n", p.ID.Pretty(), res) r.Lock() r.result = res r.Unlock() r.cancel() // signal to everyone that we're done. } else if res.closerPeers != nil { + u.POut("PEERS CLOSER -- worker for: %v\n", p.ID.Pretty()) for _, next := range res.closerPeers { r.addPeerToQuery(next, p) } } // signal we're done proccessing peer p + u.POut("completing worker for: %v\n", p.ID.Pretty()) r.peersRemaining.Decrement(1) r.rateLimit <- struct{}{} } From a0e2d3c0cbdbf1ed644dcaa9ec07106d941f8aa1 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 18 Sep 2014 20:38:24 -0700 Subject: [PATCH 0143/3817] remove start This commit was moved from ipfs/go-ipfs-routing@a2b7b62ad72fd2bf8298c2d1672428357bf34bca --- routing/dht/dht.go | 9 --------- routing/dht/dht_test.go | 5 ----- routing/dht/handlers.go | 2 -- 3 files changed, 16 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 13311f614..d1b610c5e 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -45,9 +45,6 @@ type IpfsDHT struct { providers *ProviderManager - // Signal to shutdown dht - shutdown chan struct{} - // When this peer started up birth time.Time @@ -65,7 +62,6 @@ func NewDHT(p *peer.Peer, ps peer.Peerstore, net inet.Network, sender inet.Sende dht.peerstore = ps dht.providers = NewProviderManager(p.ID) - dht.shutdown = make(chan struct{}) dht.routingTables = make([]*kb.RoutingTable, 3) dht.routingTables[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*30) @@ -75,11 +71,6 @@ func NewDHT(p *peer.Peer, ps peer.Peerstore, net inet.Network, sender inet.Sende return dht } -// Start up background goroutines needed by the DHT -func (dht *IpfsDHT) Start() { - panic("the service is already started. rmv this method") -} - // Connect to a new peer at the given address, ping and add to the routing table func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { maddrstr, _ := addr.String() diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 70fbb2ba2..2ed917401 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -41,7 +41,6 @@ func setupDHT(t *testing.T, p *peer.Peer) *IpfsDHT { d := NewDHT(p, peerstore, net, dhts, ds.NewMapDatastore()) dhts.Handler = d - d.Start() return d } @@ -116,8 +115,6 @@ func TestPing(t *testing.T) { dhtA := setupDHT(t, peerA) dhtB := setupDHT(t, peerB) - dhtA.Start() - dhtB.Start() defer dhtA.Halt() defer dhtB.Halt() @@ -150,8 +147,6 @@ func TestValueGetSet(t *testing.T) { dhtA := setupDHT(t, peerA) dhtB := setupDHT(t, peerB) - dhtA.Start() - dhtB.Start() defer dhtA.Halt() defer dhtB.Halt() diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index ddf76a669..a12a2f3d4 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -180,8 +180,6 @@ func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *Message) (*Message, er // Halt stops all communications from this peer and shut down // TODO -- remove this in favor of context func (dht *IpfsDHT) Halt() { - dht.shutdown <- struct{}{} - dht.network.Close() dht.providers.Halt() } From 1eee20dc8060ed316cddcbc92997877a826cdab3 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 18 Sep 2014 20:58:26 -0700 Subject: [PATCH 0144/3817] comment out dht_test for now. This commit was moved from ipfs/go-ipfs-routing@74e08a362572e64fd5a4cb9f394a5bd6ef928595 --- routing/dht/dht_test.go | 610 ++++++++++++++++++++-------------------- 1 file changed, 305 insertions(+), 305 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 2ed917401..768aa4767 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -1,307 +1,307 @@ package dht -import ( - "testing" - - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - - ci "github.com/jbenet/go-ipfs/crypto" - spipe "github.com/jbenet/go-ipfs/crypto/spipe" - inet "github.com/jbenet/go-ipfs/net" - mux "github.com/jbenet/go-ipfs/net/mux" - netservice "github.com/jbenet/go-ipfs/net/service" - peer "github.com/jbenet/go-ipfs/peer" - u "github.com/jbenet/go-ipfs/util" - - "bytes" - "fmt" - "time" -) - -func setupDHT(t *testing.T, p *peer.Peer) *IpfsDHT { - ctx := context.TODO() - - peerstore := peer.NewPeerstore() - - ctx, _ = context.WithCancel(ctx) - dhts := netservice.NewService(nil) // nil handler for now, need to patch it - if err := dhts.Start(ctx); err != nil { - t.Fatal(err) - } - - net, err := inet.NewIpfsNetwork(context.TODO(), p, &mux.ProtocolMap{ - mux.ProtocolID_Routing: dhts, - }) - if err != nil { - t.Fatal(err) - } - - d := NewDHT(p, peerstore, net, dhts, ds.NewMapDatastore()) - dhts.Handler = d - return d -} - -func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) { - var addrs []*ma.Multiaddr - for i := 0; i < 4; i++ { - a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) - if err != nil { - t.Fatal(err) - } - addrs = append(addrs, a) - } - - var peers []*peer.Peer - for i := 0; i < 4; i++ { - p := new(peer.Peer) - p.AddAddress(addrs[i]) - sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) - if err != nil { - panic(err) - } - p.PubKey = pk - p.PrivKey = sk - id, err := spipe.IDFromPubKey(pk) - if err != nil { - panic(err) - } - p.ID = id - peers = append(peers, p) - } - - var dhts []*IpfsDHT - for i := 0; i < 4; i++ { - dhts[i] = setupDHT(t, peers[i]) - } - - return addrs, peers, dhts -} - -func makePeer(addr *ma.Multiaddr) *peer.Peer { - p := new(peer.Peer) - p.AddAddress(addr) - sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) - if err != nil { - panic(err) - } - p.PrivKey = sk - p.PubKey = pk - id, err := spipe.IDFromPubKey(pk) - if err != nil { - panic(err) - } - - p.ID = id - return p -} - -func TestPing(t *testing.T) { - u.Debug = true - addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/2222") - if err != nil { - t.Fatal(err) - } - addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5678") - if err != nil { - t.Fatal(err) - } - - peerA := makePeer(addrA) - peerB := makePeer(addrB) - - dhtA := setupDHT(t, peerA) - dhtB := setupDHT(t, peerB) - - defer dhtA.Halt() - defer dhtB.Halt() - - _, err = dhtA.Connect(addrB) - if err != nil { - t.Fatal(err) - } - - //Test that we can ping the node - err = dhtA.Ping(peerB, time.Second*2) - if err != nil { - t.Fatal(err) - } -} - -func TestValueGetSet(t *testing.T) { - u.Debug = false - addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") - if err != nil { - t.Fatal(err) - } - addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5679") - if err != nil { - t.Fatal(err) - } - - peerA := makePeer(addrA) - peerB := makePeer(addrB) - - dhtA := setupDHT(t, peerA) - dhtB := setupDHT(t, peerB) - - defer dhtA.Halt() - defer dhtB.Halt() - - _, err = dhtA.Connect(addrB) - if err != nil { - t.Fatal(err) - } - - dhtA.PutValue("hello", []byte("world")) - - val, err := dhtA.GetValue("hello", time.Second*2) - if err != nil { - t.Fatal(err) - } - - if string(val) != "world" { - t.Fatalf("Expected 'world' got '%s'", string(val)) - } - -} - -func TestProvides(t *testing.T) { - u.Debug = false - - addrs, _, dhts := setupDHTS(4, t) - defer func() { - for i := 0; i < 4; i++ { - dhts[i].Halt() - } - }() - - _, err := dhts[0].Connect(addrs[1]) - if err != nil { - t.Fatal(err) - } - - _, err = dhts[1].Connect(addrs[2]) - if err != nil { - t.Fatal(err) - } - - _, err = dhts[1].Connect(addrs[3]) - if err != nil { - t.Fatal(err) - } - - err = dhts[3].putLocal(u.Key("hello"), []byte("world")) - if err != nil { - t.Fatal(err) - } - - bits, err := dhts[3].getLocal(u.Key("hello")) - if err != nil && bytes.Equal(bits, []byte("world")) { - t.Fatal(err) - } - - err = dhts[3].Provide(u.Key("hello")) - if err != nil { - t.Fatal(err) - } - - time.Sleep(time.Millisecond * 60) - - provs, err := dhts[0].FindProviders(u.Key("hello"), time.Second) - if err != nil { - t.Fatal(err) - } - - if len(provs) != 1 { - t.Fatal("Didnt get back providers") - } -} - -func TestLayeredGet(t *testing.T) { - u.Debug = false - addrs, _, dhts := setupDHTS(4, t) - defer func() { - for i := 0; i < 4; i++ { - dhts[i].Halt() - } - }() - - _, err := dhts[0].Connect(addrs[1]) - if err != nil { - t.Fatalf("Failed to connect: %s", err) - } - - _, err = dhts[1].Connect(addrs[2]) - if err != nil { - t.Fatal(err) - } - - _, err = dhts[1].Connect(addrs[3]) - if err != nil { - t.Fatal(err) - } - - err = dhts[3].putLocal(u.Key("hello"), []byte("world")) - if err != nil { - t.Fatal(err) - } - - err = dhts[3].Provide(u.Key("hello")) - if err != nil { - t.Fatal(err) - } - - time.Sleep(time.Millisecond * 60) - - val, err := dhts[0].GetValue(u.Key("hello"), time.Second) - if err != nil { - t.Fatal(err) - } - - if string(val) != "world" { - t.Fatal("Got incorrect value.") - } - -} - -func TestFindPeer(t *testing.T) { - u.Debug = false - - addrs, peers, dhts := setupDHTS(4, t) - defer func() { - for i := 0; i < 4; i++ { - dhts[i].Halt() - } - }() - - _, err := dhts[0].Connect(addrs[1]) - if err != nil { - t.Fatal(err) - } - - _, err = dhts[1].Connect(addrs[2]) - if err != nil { - t.Fatal(err) - } - - _, err = dhts[1].Connect(addrs[3]) - if err != nil { - t.Fatal(err) - } - - p, err := dhts[0].FindPeer(peers[2].ID, time.Second) - if err != nil { - t.Fatal(err) - } - - if p == nil { - t.Fatal("Failed to find peer.") - } - - if !p.ID.Equal(peers[2].ID) { - t.Fatal("Didnt find expected peer.") - } -} +// import ( +// "testing" +// +// context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" +// +// ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" +// ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" +// +// ci "github.com/jbenet/go-ipfs/crypto" +// spipe "github.com/jbenet/go-ipfs/crypto/spipe" +// inet "github.com/jbenet/go-ipfs/net" +// mux "github.com/jbenet/go-ipfs/net/mux" +// netservice "github.com/jbenet/go-ipfs/net/service" +// peer "github.com/jbenet/go-ipfs/peer" +// u "github.com/jbenet/go-ipfs/util" +// +// "bytes" +// "fmt" +// "time" +// ) +// +// func setupDHT(t *testing.T, p *peer.Peer) *IpfsDHT { +// ctx := context.TODO() +// +// peerstore := peer.NewPeerstore() +// +// ctx, _ = context.WithCancel(ctx) +// dhts := netservice.NewService(nil) // nil handler for now, need to patch it +// if err := dhts.Start(ctx); err != nil { +// t.Fatal(err) +// } +// +// net, err := inet.NewIpfsNetwork(context.TODO(), p, &mux.ProtocolMap{ +// mux.ProtocolID_Routing: dhts, +// }) +// if err != nil { +// t.Fatal(err) +// } +// +// d := NewDHT(p, peerstore, net, dhts, ds.NewMapDatastore()) +// dhts.Handler = d +// return d +// } +// +// func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) { +// var addrs []*ma.Multiaddr +// for i := 0; i < 4; i++ { +// a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) +// if err != nil { +// t.Fatal(err) +// } +// addrs = append(addrs, a) +// } +// +// var peers []*peer.Peer +// for i := 0; i < 4; i++ { +// p := new(peer.Peer) +// p.AddAddress(addrs[i]) +// sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) +// if err != nil { +// panic(err) +// } +// p.PubKey = pk +// p.PrivKey = sk +// id, err := spipe.IDFromPubKey(pk) +// if err != nil { +// panic(err) +// } +// p.ID = id +// peers = append(peers, p) +// } +// +// var dhts []*IpfsDHT +// for i := 0; i < 4; i++ { +// dhts[i] = setupDHT(t, peers[i]) +// } +// +// return addrs, peers, dhts +// } +// +// func makePeer(addr *ma.Multiaddr) *peer.Peer { +// p := new(peer.Peer) +// p.AddAddress(addr) +// sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) +// if err != nil { +// panic(err) +// } +// p.PrivKey = sk +// p.PubKey = pk +// id, err := spipe.IDFromPubKey(pk) +// if err != nil { +// panic(err) +// } +// +// p.ID = id +// return p +// } +// +// func TestPing(t *testing.T) { +// u.Debug = true +// addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/2222") +// if err != nil { +// t.Fatal(err) +// } +// addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5678") +// if err != nil { +// t.Fatal(err) +// } +// +// peerA := makePeer(addrA) +// peerB := makePeer(addrB) +// +// dhtA := setupDHT(t, peerA) +// dhtB := setupDHT(t, peerB) +// +// defer dhtA.Halt() +// defer dhtB.Halt() +// +// _, err = dhtA.Connect(addrB) +// if err != nil { +// t.Fatal(err) +// } +// +// //Test that we can ping the node +// err = dhtA.Ping(peerB, time.Second*2) +// if err != nil { +// t.Fatal(err) +// } +// } +// +// func TestValueGetSet(t *testing.T) { +// u.Debug = false +// addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") +// if err != nil { +// t.Fatal(err) +// } +// addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5679") +// if err != nil { +// t.Fatal(err) +// } +// +// peerA := makePeer(addrA) +// peerB := makePeer(addrB) +// +// dhtA := setupDHT(t, peerA) +// dhtB := setupDHT(t, peerB) +// +// defer dhtA.Halt() +// defer dhtB.Halt() +// +// _, err = dhtA.Connect(addrB) +// if err != nil { +// t.Fatal(err) +// } +// +// dhtA.PutValue("hello", []byte("world")) +// +// val, err := dhtA.GetValue("hello", time.Second*2) +// if err != nil { +// t.Fatal(err) +// } +// +// if string(val) != "world" { +// t.Fatalf("Expected 'world' got '%s'", string(val)) +// } +// +// } +// +// func TestProvides(t *testing.T) { +// u.Debug = false +// +// addrs, _, dhts := setupDHTS(4, t) +// defer func() { +// for i := 0; i < 4; i++ { +// dhts[i].Halt() +// } +// }() +// +// _, err := dhts[0].Connect(addrs[1]) +// if err != nil { +// t.Fatal(err) +// } +// +// _, err = dhts[1].Connect(addrs[2]) +// if err != nil { +// t.Fatal(err) +// } +// +// _, err = dhts[1].Connect(addrs[3]) +// if err != nil { +// t.Fatal(err) +// } +// +// err = dhts[3].putLocal(u.Key("hello"), []byte("world")) +// if err != nil { +// t.Fatal(err) +// } +// +// bits, err := dhts[3].getLocal(u.Key("hello")) +// if err != nil && bytes.Equal(bits, []byte("world")) { +// t.Fatal(err) +// } +// +// err = dhts[3].Provide(u.Key("hello")) +// if err != nil { +// t.Fatal(err) +// } +// +// time.Sleep(time.Millisecond * 60) +// +// provs, err := dhts[0].FindProviders(u.Key("hello"), time.Second) +// if err != nil { +// t.Fatal(err) +// } +// +// if len(provs) != 1 { +// t.Fatal("Didnt get back providers") +// } +// } +// +// func TestLayeredGet(t *testing.T) { +// u.Debug = false +// addrs, _, dhts := setupDHTS(4, t) +// defer func() { +// for i := 0; i < 4; i++ { +// dhts[i].Halt() +// } +// }() +// +// _, err := dhts[0].Connect(addrs[1]) +// if err != nil { +// t.Fatalf("Failed to connect: %s", err) +// } +// +// _, err = dhts[1].Connect(addrs[2]) +// if err != nil { +// t.Fatal(err) +// } +// +// _, err = dhts[1].Connect(addrs[3]) +// if err != nil { +// t.Fatal(err) +// } +// +// err = dhts[3].putLocal(u.Key("hello"), []byte("world")) +// if err != nil { +// t.Fatal(err) +// } +// +// err = dhts[3].Provide(u.Key("hello")) +// if err != nil { +// t.Fatal(err) +// } +// +// time.Sleep(time.Millisecond * 60) +// +// val, err := dhts[0].GetValue(u.Key("hello"), time.Second) +// if err != nil { +// t.Fatal(err) +// } +// +// if string(val) != "world" { +// t.Fatal("Got incorrect value.") +// } +// +// } +// +// func TestFindPeer(t *testing.T) { +// u.Debug = false +// +// addrs, peers, dhts := setupDHTS(4, t) +// go func() { +// for i := 0; i < 4; i++ { +// dhts[i].Halt() +// } +// }() +// +// _, err := dhts[0].Connect(addrs[1]) +// if err != nil { +// t.Fatal(err) +// } +// +// _, err = dhts[1].Connect(addrs[2]) +// if err != nil { +// t.Fatal(err) +// } +// +// _, err = dhts[1].Connect(addrs[3]) +// if err != nil { +// t.Fatal(err) +// } +// +// p, err := dhts[0].FindPeer(peers[2].ID, time.Second) +// if err != nil { +// t.Fatal(err) +// } +// +// if p == nil { +// t.Fatal("Failed to find peer.") +// } +// +// if !p.ID.Equal(peers[2].ID) { +// t.Fatal("Didnt find expected peer.") +// } +// } From 3b3234de71c9abb48be5b82b130439c552d4f397 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 19 Sep 2014 05:01:25 -0700 Subject: [PATCH 0145/3817] dht.Connect(Peer) This commit was moved from ipfs/go-ipfs-routing@22fdbcdd850b76bdabf60b6735a4dd72fbead053 --- routing/dht/dht.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index d1b610c5e..8a8d82151 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -72,9 +72,8 @@ func NewDHT(p *peer.Peer, ps peer.Peerstore, net inet.Network, sender inet.Sende } // Connect to a new peer at the given address, ping and add to the routing table -func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { - maddrstr, _ := addr.String() - u.DOut("Connect to new peer: %s\n", maddrstr) +func (dht *IpfsDHT) Connect(npeer *peer.Peer) (*peer.Peer, error) { + u.DOut("Connect to new peer: %s\n", npeer.ID.Pretty()) // TODO(jbenet,whyrusleeping) // @@ -85,8 +84,6 @@ func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) { // // /ip4/10.20.30.40/tcp/1234/ipfs/Qxhxxchxzcncxnzcnxzcxzm // - npeer := &peer.Peer{} - npeer.AddAddress(addr) err := dht.network.DialPeer(npeer) if err != nil { return nil, err From c800867c14511d311470b47eeef8a43bfe6db2bb Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 19 Sep 2014 05:23:57 -0700 Subject: [PATCH 0146/3817] use Alpha as the concurrency. cc @whyrusleeping This commit was moved from ipfs/go-ipfs-routing@164f56cdf84ad2a43db7588c7616604ca7045fba --- routing/dht/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/query.go b/routing/dht/query.go index 400259635..f4cf6ca1a 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -12,7 +12,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ) -const maxQueryConcurrency = 5 +var maxQueryConcurrency = AlphaValue type dhtQuery struct { // the key we're querying for From e1b3a65f6f4887b6b06f2f8a0da6c5bd810ef69b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 19 Sep 2014 07:51:03 -0700 Subject: [PATCH 0147/3817] Fixed connections all over. This commit was moved from ipfs/go-ipfs-routing@aab538ffff1e4cb83bb65155622ab92778f3f402 --- routing/dht/Message.go | 2 +- routing/dht/dht.go | 22 ++- routing/dht/dht_test.go | 330 +++++++++++++++++++--------------------- routing/dht/handlers.go | 1 + routing/dht/query.go | 16 +- routing/dht/routing.go | 2 + 6 files changed, 186 insertions(+), 187 deletions(-) diff --git a/routing/dht/Message.go b/routing/dht/Message.go index d82b3bb44..ed7dc2a21 100644 --- a/routing/dht/Message.go +++ b/routing/dht/Message.go @@ -46,7 +46,7 @@ func peersToPBPeers(peers []*peer.Peer) []*Message_Peer { func (m *Message) GetClusterLevel() int { level := m.GetClusterLevelRaw() - 1 if level < 0 { - u.PErr("handleGetValue: no routing level specified, assuming 0\n") + u.PErr("GetClusterLevel: no routing level specified, assuming 0\n") level = 0 } return int(level) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 8a8d82151..9338339da 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -125,7 +125,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) (msg. dht.Update(mPeer) // Print out diagnostic - u.DOut("[peer: %s]\nGot message type: '%s' [from = %s]\n", + u.DOut("[peer: %s] Got message type: '%s' [from = %s]\n", dht.self.ID.Pretty(), Message_MessageType_name[int32(pmes.GetType())], mPeer.ID.Pretty()) @@ -141,6 +141,11 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) (msg. return nil, err } + // if nil response, return it before serializing + if rpmes == nil { + return nil, nil + } + // serialize response msg rmes, err := msg.FromObject(mPeer, rpmes) if err != nil { @@ -161,6 +166,11 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p *peer.Peer, pmes *Message start := time.Now() + // Print out diagnostic + u.DOut("[peer: %s] Sent message type: '%s' [to = %s]\n", + dht.self.ID.Pretty(), + Message_MessageType_name[int32(pmes.GetType())], p.ID.Pretty()) + rmes, err := dht.sender.SendRequest(ctx, mes) if err != nil { return nil, err @@ -209,10 +219,10 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, return nil, nil, err } - u.POut("pmes.GetValue() %v\n", pmes.GetValue()) + u.DOut("pmes.GetValue() %v\n", pmes.GetValue()) if value := pmes.GetValue(); value != nil { // Success! We were given the value - u.POut("getValueOrPeers: got value\n") + u.DOut("getValueOrPeers: got value\n") return value, nil, nil } @@ -222,7 +232,7 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, if err != nil { return nil, nil, err } - u.POut("getValueOrPeers: get from providers\n") + u.DOut("getValueOrPeers: get from providers\n") return val, nil, nil } @@ -250,11 +260,11 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, } if len(peers) > 0 { - u.POut("getValueOrPeers: peers\n") + u.DOut("getValueOrPeers: peers\n") return nil, peers, nil } - u.POut("getValueOrPeers: u.ErrNotFound\n") + u.DOut("getValueOrPeers: u.ErrNotFound\n") return nil, nil, u.ErrNotFound } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 768aa4767..6cf9c115d 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -1,194 +1,180 @@ package dht -// import ( -// "testing" -// -// context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" -// -// ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" -// ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" -// -// ci "github.com/jbenet/go-ipfs/crypto" -// spipe "github.com/jbenet/go-ipfs/crypto/spipe" -// inet "github.com/jbenet/go-ipfs/net" -// mux "github.com/jbenet/go-ipfs/net/mux" -// netservice "github.com/jbenet/go-ipfs/net/service" -// peer "github.com/jbenet/go-ipfs/peer" -// u "github.com/jbenet/go-ipfs/util" -// -// "bytes" -// "fmt" -// "time" -// ) -// -// func setupDHT(t *testing.T, p *peer.Peer) *IpfsDHT { -// ctx := context.TODO() -// -// peerstore := peer.NewPeerstore() -// -// ctx, _ = context.WithCancel(ctx) -// dhts := netservice.NewService(nil) // nil handler for now, need to patch it -// if err := dhts.Start(ctx); err != nil { -// t.Fatal(err) -// } -// -// net, err := inet.NewIpfsNetwork(context.TODO(), p, &mux.ProtocolMap{ -// mux.ProtocolID_Routing: dhts, -// }) -// if err != nil { -// t.Fatal(err) -// } -// -// d := NewDHT(p, peerstore, net, dhts, ds.NewMapDatastore()) -// dhts.Handler = d -// return d -// } -// -// func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) { -// var addrs []*ma.Multiaddr -// for i := 0; i < 4; i++ { -// a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) -// if err != nil { -// t.Fatal(err) -// } -// addrs = append(addrs, a) -// } -// -// var peers []*peer.Peer -// for i := 0; i < 4; i++ { -// p := new(peer.Peer) -// p.AddAddress(addrs[i]) -// sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) -// if err != nil { -// panic(err) -// } -// p.PubKey = pk -// p.PrivKey = sk -// id, err := spipe.IDFromPubKey(pk) -// if err != nil { -// panic(err) -// } -// p.ID = id -// peers = append(peers, p) -// } -// -// var dhts []*IpfsDHT -// for i := 0; i < 4; i++ { -// dhts[i] = setupDHT(t, peers[i]) -// } -// -// return addrs, peers, dhts -// } -// -// func makePeer(addr *ma.Multiaddr) *peer.Peer { -// p := new(peer.Peer) -// p.AddAddress(addr) -// sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) -// if err != nil { -// panic(err) -// } -// p.PrivKey = sk -// p.PubKey = pk -// id, err := spipe.IDFromPubKey(pk) -// if err != nil { -// panic(err) -// } -// -// p.ID = id -// return p -// } -// -// func TestPing(t *testing.T) { -// u.Debug = true -// addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/2222") -// if err != nil { -// t.Fatal(err) -// } -// addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5678") -// if err != nil { -// t.Fatal(err) -// } -// -// peerA := makePeer(addrA) -// peerB := makePeer(addrB) -// -// dhtA := setupDHT(t, peerA) -// dhtB := setupDHT(t, peerB) -// -// defer dhtA.Halt() -// defer dhtB.Halt() -// -// _, err = dhtA.Connect(addrB) -// if err != nil { -// t.Fatal(err) -// } -// -// //Test that we can ping the node -// err = dhtA.Ping(peerB, time.Second*2) -// if err != nil { -// t.Fatal(err) -// } -// } -// -// func TestValueGetSet(t *testing.T) { -// u.Debug = false -// addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") -// if err != nil { -// t.Fatal(err) -// } -// addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5679") -// if err != nil { -// t.Fatal(err) -// } -// -// peerA := makePeer(addrA) -// peerB := makePeer(addrB) -// -// dhtA := setupDHT(t, peerA) -// dhtB := setupDHT(t, peerB) -// -// defer dhtA.Halt() -// defer dhtB.Halt() -// -// _, err = dhtA.Connect(addrB) -// if err != nil { -// t.Fatal(err) -// } -// -// dhtA.PutValue("hello", []byte("world")) -// -// val, err := dhtA.GetValue("hello", time.Second*2) -// if err != nil { -// t.Fatal(err) -// } -// -// if string(val) != "world" { -// t.Fatalf("Expected 'world' got '%s'", string(val)) -// } -// -// } -// +import ( + "testing" + + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + + ci "github.com/jbenet/go-ipfs/crypto" + spipe "github.com/jbenet/go-ipfs/crypto/spipe" + inet "github.com/jbenet/go-ipfs/net" + mux "github.com/jbenet/go-ipfs/net/mux" + netservice "github.com/jbenet/go-ipfs/net/service" + peer "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" + + "fmt" + "time" +) + +func setupDHT(t *testing.T, p *peer.Peer) *IpfsDHT { + ctx, _ := context.WithCancel(context.TODO()) + + peerstore := peer.NewPeerstore() + + dhts := netservice.NewService(nil) // nil handler for now, need to patch it + if err := dhts.Start(ctx); err != nil { + t.Fatal(err) + } + + net, err := inet.NewIpfsNetwork(ctx, p, &mux.ProtocolMap{ + mux.ProtocolID_Routing: dhts, + }) + if err != nil { + t.Fatal(err) + } + + d := NewDHT(p, peerstore, net, dhts, ds.NewMapDatastore()) + dhts.Handler = d + return d +} + +func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) { + var addrs []*ma.Multiaddr + for i := 0; i < 4; i++ { + a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) + if err != nil { + t.Fatal(err) + } + addrs = append(addrs, a) + } + + var peers []*peer.Peer + for i := 0; i < 4; i++ { + p := makePeer(addrs[i]) + peers = append(peers, p) + } + + var dhts []*IpfsDHT + for i := 0; i < 4; i++ { + dhts[i] = setupDHT(t, peers[i]) + } + + return addrs, peers, dhts +} + +func makePeer(addr *ma.Multiaddr) *peer.Peer { + p := new(peer.Peer) + p.AddAddress(addr) + sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) + if err != nil { + panic(err) + } + p.PrivKey = sk + p.PubKey = pk + id, err := spipe.IDFromPubKey(pk) + if err != nil { + panic(err) + } + + p.ID = id + return p +} + +func TestPing(t *testing.T) { + u.Debug = true + addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/2222") + if err != nil { + t.Fatal(err) + } + addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5678") + if err != nil { + t.Fatal(err) + } + + peerA := makePeer(addrA) + peerB := makePeer(addrB) + + dhtA := setupDHT(t, peerA) + dhtB := setupDHT(t, peerB) + + defer dhtA.Halt() + defer dhtB.Halt() + + _, err = dhtA.Connect(peerB) + if err != nil { + t.Fatal(err) + } + + //Test that we can ping the node + err = dhtA.Ping(peerB, time.Second*2) + if err != nil { + t.Fatal(err) + } +} + +func TestValueGetSet(t *testing.T) { + u.Debug = false + addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") + if err != nil { + t.Fatal(err) + } + addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5679") + if err != nil { + t.Fatal(err) + } + + peerA := makePeer(addrA) + peerB := makePeer(addrB) + + dhtA := setupDHT(t, peerA) + dhtB := setupDHT(t, peerB) + + defer dhtA.Halt() + defer dhtB.Halt() + + _, err = dhtA.Connect(peerB) + if err != nil { + t.Fatal(err) + } + + dhtA.PutValue("hello", []byte("world")) + + val, err := dhtA.GetValue("hello", time.Second*2) + if err != nil { + t.Fatal(err) + } + + if string(val) != "world" { + t.Fatalf("Expected 'world' got '%s'", string(val)) + } + +} + // func TestProvides(t *testing.T) { // u.Debug = false // -// addrs, _, dhts := setupDHTS(4, t) +// _, peers, dhts := setupDHTS(4, t) // defer func() { // for i := 0; i < 4; i++ { // dhts[i].Halt() // } // }() // -// _, err := dhts[0].Connect(addrs[1]) +// _, err := dhts[0].Connect(peers[1]) // if err != nil { // t.Fatal(err) // } // -// _, err = dhts[1].Connect(addrs[2]) +// _, err = dhts[1].Connect(peers[2]) // if err != nil { // t.Fatal(err) // } // -// _, err = dhts[1].Connect(addrs[3]) +// _, err = dhts[1].Connect(peers[3]) // if err != nil { // t.Fatal(err) // } diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index a12a2f3d4..124bd76f5 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -97,6 +97,7 @@ func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *Message) (*Message, error defer dht.dslock.Unlock() dskey := ds.NewKey(pmes.GetKey()) err := dht.datastore.Put(dskey, pmes.GetValue()) + u.DOut("[%s] handlePutValue %v %v", dht.self.ID.Pretty(), dskey, pmes.GetValue()) return nil, err } diff --git a/routing/dht/query.go b/routing/dht/query.go index f4cf6ca1a..f4695845f 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -163,7 +163,7 @@ func (r *dhtQueryRunner) addPeerToQuery(next *peer.Peer, benchmark *peer.Peer) { r.peersSeen[next.Key()] = next r.Unlock() - u.POut("adding peer to query: %v\n", next.ID.Pretty()) + u.DOut("adding peer to query: %v\n", next.ID.Pretty()) // do this after unlocking to prevent possible deadlocks. r.peersRemaining.Increment(1) @@ -187,14 +187,14 @@ func (r *dhtQueryRunner) spawnWorkers() { if !more { return // channel closed. } - u.POut("spawning worker for: %v\n", p.ID.Pretty()) + u.DOut("spawning worker for: %v\n", p.ID.Pretty()) go r.queryPeer(p) } } } func (r *dhtQueryRunner) queryPeer(p *peer.Peer) { - u.POut("spawned worker for: %v\n", p.ID.Pretty()) + u.DOut("spawned worker for: %v\n", p.ID.Pretty()) // make sure we rate limit concurrency. select { @@ -204,33 +204,33 @@ func (r *dhtQueryRunner) queryPeer(p *peer.Peer) { return } - u.POut("running worker for: %v\n", p.ID.Pretty()) + u.DOut("running worker for: %v\n", p.ID.Pretty()) // finally, run the query against this peer res, err := r.query.qfunc(r.ctx, p) if err != nil { - u.POut("ERROR worker for: %v %v\n", p.ID.Pretty(), err) + u.DOut("ERROR worker for: %v %v\n", p.ID.Pretty(), err) r.Lock() r.errs = append(r.errs, err) r.Unlock() } else if res.success { - u.POut("SUCCESS worker for: %v\n", p.ID.Pretty(), res) + u.DOut("SUCCESS worker for: %v\n", p.ID.Pretty(), res) r.Lock() r.result = res r.Unlock() r.cancel() // signal to everyone that we're done. } else if res.closerPeers != nil { - u.POut("PEERS CLOSER -- worker for: %v\n", p.ID.Pretty()) + u.DOut("PEERS CLOSER -- worker for: %v\n", p.ID.Pretty()) for _, next := range res.closerPeers { r.addPeerToQuery(next, p) } } // signal we're done proccessing peer p - u.POut("completing worker for: %v\n", p.ID.Pretty()) + u.DOut("completing worker for: %v\n", p.ID.Pretty()) r.peersRemaining.Decrement(1) r.rateLimit <- struct{}{} } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 2410dcd3a..b9fdbeef4 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -30,6 +30,7 @@ func (dht *IpfsDHT) PutValue(key u.Key, value []byte) error { } query := newQuery(key, func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) { + u.DOut("[%s] PutValue qry part %v\n", dht.self.ID.Pretty(), p.ID.Pretty()) err := dht.putValueToNetwork(ctx, p, string(key), value) if err != nil { return nil, err @@ -38,6 +39,7 @@ func (dht *IpfsDHT) PutValue(key u.Key, value []byte) error { }) _, err := query.Run(ctx, peers) + u.DOut("[%s] PutValue %v %v\n", dht.self.ID.Pretty(), key, value) return err } From 14b6c92e7e03da0f169f2ab47c9ffaa209d3d14f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 19 Sep 2014 08:07:56 -0700 Subject: [PATCH 0148/3817] fixed get/put This commit was moved from ipfs/go-ipfs-routing@2641a0840eddc53dd1df984f18edbc568f6303c5 --- routing/dht/dht.go | 14 ++++++++++---- routing/dht/dht_test.go | 2 +- routing/dht/handlers.go | 13 +++++++------ routing/dht/query.go | 15 ++++++++------- routing/dht/routing.go | 2 ++ 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 9338339da..89abd093a 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -1,6 +1,7 @@ package dht import ( + "bytes" "crypto/rand" "errors" "fmt" @@ -190,15 +191,20 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p *peer.Peer, pmes *Message return rpmes, nil } -func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p *peer.Peer, key string, value []byte) error { +func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p *peer.Peer, + key string, value []byte) error { + pmes := newMessage(Message_PUT_VALUE, string(key), 0) pmes.Value = value - - mes, err := msg.FromObject(p, pmes) + rpmes, err := dht.sendRequest(ctx, p, pmes) if err != nil { return err } - return dht.sender.SendMessage(ctx, mes) + + if !bytes.Equal(rpmes.Value, pmes.Value) { + return errors.New("value not put correctly") + } + return nil } func (dht *IpfsDHT) putProvider(ctx context.Context, p *peer.Peer, key string) error { diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 6cf9c115d..b7e24b1d7 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -117,7 +117,7 @@ func TestPing(t *testing.T) { } func TestValueGetSet(t *testing.T) { - u.Debug = false + u.Debug = true addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") if err != nil { t.Fatal(err) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 124bd76f5..5320cc10a 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -38,7 +38,7 @@ func (dht *IpfsDHT) handlerForMsgType(t Message_MessageType) dhtHandler { } func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error) { - u.DOut("handleGetValue for key: %s\n", pmes.GetKey()) + u.DOut("[%s] handleGetValue for key: %s\n", dht.self.ID.Pretty(), pmes.GetKey()) // setup response resp := newMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) @@ -50,11 +50,13 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error } // let's first check if we have the value locally. + u.DOut("[%s] handleGetValue looking into ds\n", dht.self.ID.Pretty()) dskey := ds.NewKey(pmes.GetKey()) iVal, err := dht.datastore.Get(dskey) + u.DOut("[%s] handleGetValue looking into ds GOT %v\n", dht.self.ID.Pretty(), iVal) // if we got an unexpected error, bail. - if err != ds.ErrNotFound { + if err != nil && err != ds.ErrNotFound { return nil, err } @@ -63,7 +65,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error // if we have the value, send it back if err == nil { - u.DOut("handleGetValue success!\n") + u.DOut("[%s] handleGetValue success!\n", dht.self.ID.Pretty()) byts, ok := iVal.([]byte) if !ok { @@ -85,7 +87,6 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error if closer != nil { u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty()) resp.CloserPeers = peersToPBPeers([]*peer.Peer{closer}) - return resp, nil } return resp, nil @@ -97,8 +98,8 @@ func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *Message) (*Message, error defer dht.dslock.Unlock() dskey := ds.NewKey(pmes.GetKey()) err := dht.datastore.Put(dskey, pmes.GetValue()) - u.DOut("[%s] handlePutValue %v %v", dht.self.ID.Pretty(), dskey, pmes.GetValue()) - return nil, err + u.DOut("[%s] handlePutValue %v %v\n", dht.self.ID.Pretty(), dskey, pmes.GetValue()) + return pmes, err } func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *Message) (*Message, error) { diff --git a/routing/dht/query.go b/routing/dht/query.go index f4695845f..4db3f70e7 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -117,28 +117,29 @@ func (r *dhtQueryRunner) Run(peers []*peer.Peer) (*dhtQueryResult, error) { // so workers are working. // wait until they're done. + err := u.ErrNotFound + select { case <-r.peersRemaining.Done(): r.cancel() // ran all and nothing. cancel all outstanding workers. - r.RLock() defer r.RUnlock() if len(r.errs) > 0 { - return nil, r.errs[0] + err = r.errs[0] } - return nil, u.ErrNotFound case <-r.ctx.Done(): r.RLock() defer r.RUnlock() + err = r.ctx.Err() + } - if r.result != nil && r.result.success { - return r.result, nil - } - return nil, r.ctx.Err() + if r.result != nil && r.result.success { + return r.result, nil } + return nil, err } func (r *dhtQueryRunner) addPeerToQuery(next *peer.Peer, benchmark *peer.Peer) { diff --git a/routing/dht/routing.go b/routing/dht/routing.go index b9fdbeef4..4991a06f3 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -89,6 +89,8 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { return nil, err } + u.DOut("[%s] GetValue %v %v\n", dht.self.ID.Pretty(), key, result.value) + if result.value == nil { return nil, u.ErrNotFound } From ec0a2f4c17c5528d1d84c4c0f28d8c41b70f32d4 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 18 Sep 2014 17:09:22 -0700 Subject: [PATCH 0149/3817] refac(exchange) bitswap -> exchange/bitswap Move go-ipfs/bitswap package to go-ipfs/exchange/bitswap * Delineates the difference between the generic exchange interface and implementations (eg. BitSwap protocol) Thus, the bitswap protocol can be refined without having to overthink how future exchanges will work. Aspects common to BitSwap and other exchanges can be extracted out to the exchange package in piecemeal. Future exchange implementations can be placed in sibling packages next to exchange/bitswap. (eg. exchange/multilateral) This commit was moved from ipfs/go-ipfs-exchange-interface@9c8e0caebd973678ba1d24a01820e82bfe72a5dc --- exchange/interface.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 exchange/interface.go diff --git a/exchange/interface.go b/exchange/interface.go new file mode 100644 index 000000000..73c3ba603 --- /dev/null +++ b/exchange/interface.go @@ -0,0 +1,28 @@ +package bitswap + +import ( + "time" + + blocks "github.com/jbenet/go-ipfs/blocks" + peer "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" +) + +type Exchange interface { + + // Block returns the block associated with a given key. + // TODO(brian): pass a context instead of a timeout + Block(k u.Key, timeout time.Duration) (*blocks.Block, error) + + // HasBlock asserts the existence of this block + // TODO(brian): rename -> HasBlock + // TODO(brian): accept a value, not a pointer + // TODO(brian): remove error return value. Should callers be concerned with + // whether the block was made available on the network? + HasBlock(blocks.Block) error +} + +type Directory interface { + FindProvidersAsync(u.Key, int, time.Duration) <-chan *peer.Peer + Provide(key u.Key) error +} From 06477956e980efeebbb19069acbedc68c8e3aa6b Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 18 Sep 2014 17:09:22 -0700 Subject: [PATCH 0150/3817] refac(exchange) bitswap -> exchange/bitswap Move go-ipfs/bitswap package to go-ipfs/exchange/bitswap * Delineates the difference between the generic exchange interface and implementations (eg. BitSwap protocol) Thus, the bitswap protocol can be refined without having to overthink how future exchanges will work. Aspects common to BitSwap and other exchanges can be extracted out to the exchange package in piecemeal. Future exchange implementations can be placed in sibling packages next to exchange/bitswap. (eg. exchange/multilateral) This commit was moved from ipfs/go-blockservice@1382a525dc152e159316dad4dba3c74c596fe4be --- blockservice/blockservice.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 0b4f15b98..011ad0283 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -5,8 +5,8 @@ import ( "time" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" - bitswap "github.com/jbenet/go-ipfs/bitswap" blocks "github.com/jbenet/go-ipfs/blocks" + exchange "github.com/jbenet/go-ipfs/exchange" u "github.com/jbenet/go-ipfs/util" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" @@ -16,11 +16,11 @@ import ( // It uses an internal `datastore.Datastore` instance to store values. type BlockService struct { Datastore ds.Datastore - Remote bitswap.Exchange + Remote exchange.Exchange } // NewBlockService creates a BlockService with given datastore instance. -func NewBlockService(d ds.Datastore, rem bitswap.Exchange) (*BlockService, error) { +func NewBlockService(d ds.Datastore, rem exchange.Exchange) (*BlockService, error) { if d == nil { return nil, fmt.Errorf("BlockService requires valid datastore") } From a9a82e044ea5fba1d371c11fe559ffbd5249edab Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 18 Sep 2014 17:30:06 -0700 Subject: [PATCH 0151/3817] refac(exchange) rename exchange.Interface to match golang conventions examples: http://golang.org/pkg/container/heap/#Interface http://golang.org/pkg/net/#Interface http://golang.org/pkg/sort/#Interface This commit was moved from ipfs/go-ipfs-exchange-interface@1d5a8e9f1ab6d50fbd7d26aeaf26232965bce71e --- exchange/interface.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index 73c3ba603..75eca06bf 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -4,11 +4,12 @@ import ( "time" blocks "github.com/jbenet/go-ipfs/blocks" - peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" ) -type Exchange interface { +// Any type that implements exchange.Interface may be used as an IPFS block +// exchange protocol. +type Interface interface { // Block returns the block associated with a given key. // TODO(brian): pass a context instead of a timeout @@ -21,8 +22,3 @@ type Exchange interface { // whether the block was made available on the network? HasBlock(blocks.Block) error } - -type Directory interface { - FindProvidersAsync(u.Key, int, time.Duration) <-chan *peer.Peer - Provide(key u.Key) error -} From 252db7682bc9ae2496694ec14cd7cbcb572455ad Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 18 Sep 2014 17:30:06 -0700 Subject: [PATCH 0152/3817] refac(exchange) rename exchange.Interface to match golang conventions examples: http://golang.org/pkg/container/heap/#Interface http://golang.org/pkg/net/#Interface http://golang.org/pkg/sort/#Interface This commit was moved from ipfs/go-blockservice@7c577eb53e66c6284ed75b98b3d6d39fc212d024 --- blockservice/blockservice.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 011ad0283..89136edb0 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -16,11 +16,11 @@ import ( // It uses an internal `datastore.Datastore` instance to store values. type BlockService struct { Datastore ds.Datastore - Remote exchange.Exchange + Remote exchange.Interface } // NewBlockService creates a BlockService with given datastore instance. -func NewBlockService(d ds.Datastore, rem exchange.Exchange) (*BlockService, error) { +func NewBlockService(d ds.Datastore, rem exchange.Interface) (*BlockService, error) { if d == nil { return nil, fmt.Errorf("BlockService requires valid datastore") } From 713d68296f39672d551df63ef7a22f176b073190 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 18 Sep 2014 19:36:18 -0700 Subject: [PATCH 0153/3817] refac(exchange) replace timeout -> context in API This commit was moved from ipfs/go-ipfs-exchange-interface@36dca820ae9844ed84a1a47ad526aaa73c34607f --- exchange/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index 75eca06bf..7e06e1ed1 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -1,7 +1,7 @@ package bitswap import ( - "time" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" blocks "github.com/jbenet/go-ipfs/blocks" u "github.com/jbenet/go-ipfs/util" @@ -13,7 +13,7 @@ type Interface interface { // Block returns the block associated with a given key. // TODO(brian): pass a context instead of a timeout - Block(k u.Key, timeout time.Duration) (*blocks.Block, error) + Block(context.Context, u.Key) (*blocks.Block, error) // HasBlock asserts the existence of this block // TODO(brian): rename -> HasBlock From 56411d525456e5426941bae02f0163c052922998 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 18 Sep 2014 19:36:18 -0700 Subject: [PATCH 0154/3817] refac(exchange) replace timeout -> context in API This commit was moved from ipfs/go-blockservice@07b321372a091c328906e493048c707cf712d434 --- blockservice/blockservice.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 89136edb0..3018ae0d8 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -4,12 +4,13 @@ import ( "fmt" "time" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + blocks "github.com/jbenet/go-ipfs/blocks" exchange "github.com/jbenet/go-ipfs/exchange" u "github.com/jbenet/go-ipfs/util" - - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" ) // BlockService is a block datastore. @@ -65,7 +66,8 @@ func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { }, nil } else if err == ds.ErrNotFound && s.Remote != nil { u.DOut("Blockservice: Searching bitswap.\n") - blk, err := s.Remote.Block(k, time.Second*5) + ctx, _ := context.WithTimeout(context.TODO(), 5*time.Second) + blk, err := s.Remote.Block(ctx, k) if err != nil { return nil, err } From eabdda08bc95c523fe4e9355a17527c4999aed45 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 18 Sep 2014 19:15:15 -0700 Subject: [PATCH 0155/3817] refac(routing) replace timeout -> ctx @jbenet oh hai there! This commit was moved from ipfs/go-ipfs-routing@2e150db89c06b1f988cbae50da2701a26504355c --- routing/dht/routing.go | 4 +--- routing/routing.go | 4 +++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 4991a06f3..9e6c5ff29 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -121,9 +121,7 @@ func (dht *IpfsDHT) Provide(key u.Key) error { } // FindProvidersAsync runs FindProviders and sends back results over a channel -func (dht *IpfsDHT) FindProvidersAsync(key u.Key, count int, timeout time.Duration) <-chan *peer.Peer { - ctx, _ := context.WithTimeout(context.TODO(), timeout) - +func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int) <-chan *peer.Peer { peerOut := make(chan *peer.Peer, count) go func() { ps := newPeerSet() diff --git a/routing/routing.go b/routing/routing.go index c8dc2772b..872bad6f8 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -3,6 +3,8 @@ package routing import ( "time" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" ) @@ -10,7 +12,7 @@ import ( // IpfsRouting is the routing module interface // It is implemented by things like DHTs, etc. type IpfsRouting interface { - FindProvidersAsync(u.Key, int, time.Duration) <-chan *peer.Peer + FindProvidersAsync(context.Context, u.Key, int) <-chan *peer.Peer // Basic Put/Get From 022af657931e63d37036e6275d8324751a4927a3 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 18 Sep 2014 19:43:03 -0700 Subject: [PATCH 0156/3817] feat(exchange) pass ctx to exchange.HasBlock(...) This commit was moved from ipfs/go-ipfs-exchange-interface@ac8c699864f37aaea132696771f3944c06af7367 --- exchange/interface.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index 7e06e1ed1..a96094eaa 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -12,13 +12,9 @@ import ( type Interface interface { // Block returns the block associated with a given key. - // TODO(brian): pass a context instead of a timeout Block(context.Context, u.Key) (*blocks.Block, error) - // HasBlock asserts the existence of this block - // TODO(brian): rename -> HasBlock - // TODO(brian): accept a value, not a pointer - // TODO(brian): remove error return value. Should callers be concerned with - // whether the block was made available on the network? - HasBlock(blocks.Block) error + // TODO Should callers be concerned with whether the block was made + // available on the network? + HasBlock(context.Context, blocks.Block) error } From 1eb482018908a15ffd384f920305835d2c0090bb Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 18 Sep 2014 19:43:03 -0700 Subject: [PATCH 0157/3817] feat(exchange) pass ctx to exchange.HasBlock(...) This commit was moved from ipfs/go-blockservice@c5ebe0fb414b654f18e826e513dfd725e25a0017 --- blockservice/blockservice.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 3018ae0d8..e3c7402bd 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -43,7 +43,8 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { return k, err } if s.Remote != nil { - err = s.Remote.HasBlock(*b) + ctx := context.TODO() + err = s.Remote.HasBlock(ctx, *b) } return k, err } From e6f094759ea512c3d2158116007b02fbac2f8891 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 19 Sep 2014 14:31:10 -0700 Subject: [PATCH 0158/3817] provider testing This commit was moved from ipfs/go-ipfs-routing@9215b454a03ce87b1f962b4a86a6666c37a1370c --- routing/dht/dht.go | 11 ++++++++--- routing/dht/dht_test.go | 9 +++++---- routing/dht/routing.go | 3 ++- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 89abd093a..ec22da9b0 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -208,13 +208,18 @@ func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p *peer.Peer, } func (dht *IpfsDHT) putProvider(ctx context.Context, p *peer.Peer, key string) error { - pmes := newMessage(Message_ADD_PROVIDER, string(key), 0) - mes, err := msg.FromObject(p, pmes) + pmes := newMessage(Message_ADD_PROVIDER, string(key), 0) + rpmes, err := dht.sendRequest(ctx, p, pmes) if err != nil { return err } - return dht.sender.SendMessage(ctx, mes) + + if *rpmes.Key != *pmes.Key { + return errors.New("provider not added correctly") + } + + return nil } func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index b7e24b1d7..94e9ee6d3 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -1,6 +1,7 @@ package dht import ( + "bytes" "testing" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -44,7 +45,7 @@ func setupDHT(t *testing.T, p *peer.Peer) *IpfsDHT { func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) { var addrs []*ma.Multiaddr - for i := 0; i < 4; i++ { + for i := 0; i < n; i++ { a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) if err != nil { t.Fatal(err) @@ -53,13 +54,13 @@ func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) } var peers []*peer.Peer - for i := 0; i < 4; i++ { + for i := 0; i < n; i++ { p := makePeer(addrs[i]) peers = append(peers, p) } - var dhts []*IpfsDHT - for i := 0; i < 4; i++ { + dhts := make([]*IpfsDHT, n) + for i := 0; i < n; i++ { dhts[i] = setupDHT(t, peers[i]) } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 9e6c5ff29..acb4cab45 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -90,7 +90,6 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { } u.DOut("[%s] GetValue %v %v\n", dht.self.ID.Pretty(), key, result.value) - if result.value == nil { return nil, u.ErrNotFound } @@ -111,6 +110,8 @@ func (dht *IpfsDHT) Provide(key u.Key) error { return kb.ErrLookupFailure } + //TODO FIX: this doesn't work! it needs to be sent to the actual nearest peers. + // `peers` are the closest peers we have, not the ones that should get the value. for _, p := range peers { err := dht.putProvider(ctx, p, string(key)) if err != nil { From 32627aefd2b8a4204a56d28e626d23232d9d3f0e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 19 Sep 2014 15:35:06 -0700 Subject: [PATCH 0159/3817] refac(exch:offline) move offline exchange to its own package This commit was moved from ipfs/go-ipfs-exchange-offline@a9531a8171017885104c233c442ab219da0ffc15 --- exchange/offline/offline.go | 32 ++++++++++++++++++++++++++++++++ exchange/offline/offline_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 exchange/offline/offline.go create mode 100644 exchange/offline/offline_test.go diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go new file mode 100644 index 000000000..9695b0b56 --- /dev/null +++ b/exchange/offline/offline.go @@ -0,0 +1,32 @@ +package bitswap + +import ( + "errors" + + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + + blocks "github.com/jbenet/go-ipfs/blocks" + exchange "github.com/jbenet/go-ipfs/exchange" + u "github.com/jbenet/go-ipfs/util" +) + +func NewOfflineExchange() exchange.Interface { + return &offlineExchange{} +} + +// offlineExchange implements the Exchange interface but doesn't return blocks. +// For use in offline mode. +type offlineExchange struct { +} + +// Block returns nil to signal that a block could not be retrieved for the +// given key. +// NB: This function may return before the timeout expires. +func (_ *offlineExchange) Block(context.Context, u.Key) (*blocks.Block, error) { + return nil, errors.New("Block unavailable. Operating in offline mode") +} + +// HasBlock always returns nil. +func (_ *offlineExchange) HasBlock(context.Context, blocks.Block) error { + return nil +} diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go new file mode 100644 index 000000000..26821f2c8 --- /dev/null +++ b/exchange/offline/offline_test.go @@ -0,0 +1,28 @@ +package bitswap + +import ( + "testing" + + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + + u "github.com/jbenet/go-ipfs/util" + testutil "github.com/jbenet/go-ipfs/util/testutil" +) + +func TestBlockReturnsErr(t *testing.T) { + off := NewOfflineExchange() + _, err := off.Block(context.Background(), u.Key("foo")) + if err != nil { + return // as desired + } + t.Fail() +} + +func TestHasBlockReturnsNil(t *testing.T) { + off := NewOfflineExchange() + block := testutil.NewBlockOrFail(t, "data") + err := off.HasBlock(context.Background(), block) + if err != nil { + t.Fatal("") + } +} From d29247093f45253b5196ddbc8349ec2695cf13fd Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 19 Sep 2014 00:08:15 -0700 Subject: [PATCH 0160/3817] fix(exchange) package name This commit was moved from ipfs/go-ipfs-exchange-interface@8171130d624b7eee81ce830834dae68e13a698f6 --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index a96094eaa..682c98348 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -1,4 +1,4 @@ -package bitswap +package exchange import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" From 4332c069c4b8a87be20af1e9d7d7dcb012bcbef0 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 19 Sep 2014 18:11:05 -0700 Subject: [PATCH 0161/3817] dht tests pass again This commit was moved from ipfs/go-ipfs-routing@f1df72769abf9acc97a2d8d81e5904d821388c26 --- routing/dht/Message.go | 2 +- routing/dht/dht.go | 5 +- routing/dht/dht_test.go | 295 +++++++++++++++++++++------------------- routing/dht/ext_test.go | 8 +- routing/dht/handlers.go | 2 +- 5 files changed, 169 insertions(+), 143 deletions(-) diff --git a/routing/dht/Message.go b/routing/dht/Message.go index ed7dc2a21..1be9a3b80 100644 --- a/routing/dht/Message.go +++ b/routing/dht/Message.go @@ -46,7 +46,7 @@ func peersToPBPeers(peers []*peer.Peer) []*Message_Peer { func (m *Message) GetClusterLevel() int { level := m.GetClusterLevelRaw() - 1 if level < 0 { - u.PErr("GetClusterLevel: no routing level specified, assuming 0\n") + u.DErr("GetClusterLevel: no routing level specified, assuming 0\n") level = 0 } return int(level) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index ec22da9b0..148168d01 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -215,6 +215,7 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p *peer.Peer, key string) e return err } + u.DOut("[%s] putProvider: %s for %s\n", dht.self.ID.Pretty(), p.ID.Pretty(), key) if *rpmes.Key != *pmes.Key { return errors.New("provider not added correctly") } @@ -393,6 +394,8 @@ func (dht *IpfsDHT) addProviders(key u.Key, peers []*Message_Peer) []*peer.Peer continue } + u.DOut("[%s] adding provider: %s for %s", dht.self.ID.Pretty(), p, key) + // Dont add outselves to the list if p.ID.Equal(dht.self.ID) { continue @@ -464,7 +467,7 @@ func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (*peer.Peer, error) { } // create new Peer - p := &peer.Peer{ID: id} + p = &peer.Peer{ID: id} p.AddAddress(maddr) dht.peerstore.Put(p) } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 94e9ee6d3..e3f056ce2 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -86,7 +86,9 @@ func makePeer(addr *ma.Multiaddr) *peer.Peer { } func TestPing(t *testing.T) { - u.Debug = true + // t.Skip("skipping test to debug another") + + u.Debug = false addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/2222") if err != nil { t.Fatal(err) @@ -104,6 +106,8 @@ func TestPing(t *testing.T) { defer dhtA.Halt() defer dhtB.Halt() + defer dhtA.network.Close() + defer dhtB.network.Close() _, err = dhtA.Connect(peerB) if err != nil { @@ -118,7 +122,9 @@ func TestPing(t *testing.T) { } func TestValueGetSet(t *testing.T) { - u.Debug = true + // t.Skip("skipping test to debug another") + + u.Debug = false addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") if err != nil { t.Fatal(err) @@ -136,6 +142,8 @@ func TestValueGetSet(t *testing.T) { defer dhtA.Halt() defer dhtB.Halt() + defer dhtA.network.Close() + defer dhtB.network.Close() _, err = dhtA.Connect(peerB) if err != nil { @@ -155,140 +163,149 @@ func TestValueGetSet(t *testing.T) { } -// func TestProvides(t *testing.T) { -// u.Debug = false -// -// _, peers, dhts := setupDHTS(4, t) -// defer func() { -// for i := 0; i < 4; i++ { -// dhts[i].Halt() -// } -// }() -// -// _, err := dhts[0].Connect(peers[1]) -// if err != nil { -// t.Fatal(err) -// } -// -// _, err = dhts[1].Connect(peers[2]) -// if err != nil { -// t.Fatal(err) -// } -// -// _, err = dhts[1].Connect(peers[3]) -// if err != nil { -// t.Fatal(err) -// } -// -// err = dhts[3].putLocal(u.Key("hello"), []byte("world")) -// if err != nil { -// t.Fatal(err) -// } -// -// bits, err := dhts[3].getLocal(u.Key("hello")) -// if err != nil && bytes.Equal(bits, []byte("world")) { -// t.Fatal(err) -// } -// -// err = dhts[3].Provide(u.Key("hello")) -// if err != nil { -// t.Fatal(err) -// } -// -// time.Sleep(time.Millisecond * 60) -// -// provs, err := dhts[0].FindProviders(u.Key("hello"), time.Second) -// if err != nil { -// t.Fatal(err) -// } -// -// if len(provs) != 1 { -// t.Fatal("Didnt get back providers") -// } -// } -// -// func TestLayeredGet(t *testing.T) { -// u.Debug = false -// addrs, _, dhts := setupDHTS(4, t) -// defer func() { -// for i := 0; i < 4; i++ { -// dhts[i].Halt() -// } -// }() -// -// _, err := dhts[0].Connect(addrs[1]) -// if err != nil { -// t.Fatalf("Failed to connect: %s", err) -// } -// -// _, err = dhts[1].Connect(addrs[2]) -// if err != nil { -// t.Fatal(err) -// } -// -// _, err = dhts[1].Connect(addrs[3]) -// if err != nil { -// t.Fatal(err) -// } -// -// err = dhts[3].putLocal(u.Key("hello"), []byte("world")) -// if err != nil { -// t.Fatal(err) -// } -// -// err = dhts[3].Provide(u.Key("hello")) -// if err != nil { -// t.Fatal(err) -// } -// -// time.Sleep(time.Millisecond * 60) -// -// val, err := dhts[0].GetValue(u.Key("hello"), time.Second) -// if err != nil { -// t.Fatal(err) -// } -// -// if string(val) != "world" { -// t.Fatal("Got incorrect value.") -// } -// -// } -// -// func TestFindPeer(t *testing.T) { -// u.Debug = false -// -// addrs, peers, dhts := setupDHTS(4, t) -// go func() { -// for i := 0; i < 4; i++ { -// dhts[i].Halt() -// } -// }() -// -// _, err := dhts[0].Connect(addrs[1]) -// if err != nil { -// t.Fatal(err) -// } -// -// _, err = dhts[1].Connect(addrs[2]) -// if err != nil { -// t.Fatal(err) -// } -// -// _, err = dhts[1].Connect(addrs[3]) -// if err != nil { -// t.Fatal(err) -// } -// -// p, err := dhts[0].FindPeer(peers[2].ID, time.Second) -// if err != nil { -// t.Fatal(err) -// } -// -// if p == nil { -// t.Fatal("Failed to find peer.") -// } -// -// if !p.ID.Equal(peers[2].ID) { -// t.Fatal("Didnt find expected peer.") -// } -// } +func TestProvides(t *testing.T) { + // t.Skip("skipping test to debug another") + + u.Debug = false + + _, peers, dhts := setupDHTS(4, t) + defer func() { + for i := 0; i < 4; i++ { + dhts[i].Halt() + defer dhts[i].network.Close() + } + }() + + _, err := dhts[0].Connect(peers[1]) + if err != nil { + t.Fatal(err) + } + + _, err = dhts[1].Connect(peers[2]) + if err != nil { + t.Fatal(err) + } + + _, err = dhts[1].Connect(peers[3]) + if err != nil { + t.Fatal(err) + } + + err = dhts[3].putLocal(u.Key("hello"), []byte("world")) + if err != nil { + t.Fatal(err) + } + + bits, err := dhts[3].getLocal(u.Key("hello")) + if err != nil && bytes.Equal(bits, []byte("world")) { + t.Fatal(err) + } + + err = dhts[3].Provide(u.Key("hello")) + if err != nil { + t.Fatal(err) + } + + time.Sleep(time.Millisecond * 60) + + provs, err := dhts[0].FindProviders(u.Key("hello"), time.Second) + if err != nil { + t.Fatal(err) + } + + if len(provs) != 1 { + t.Fatal("Didnt get back providers") + } +} + +func TestLayeredGet(t *testing.T) { + // t.Skip("skipping test to debug another") + + u.Debug = false + _, peers, dhts := setupDHTS(4, t) + defer func() { + for i := 0; i < 4; i++ { + dhts[i].Halt() + defer dhts[i].network.Close() + } + }() + + _, err := dhts[0].Connect(peers[1]) + if err != nil { + t.Fatalf("Failed to connect: %s", err) + } + + _, err = dhts[1].Connect(peers[2]) + if err != nil { + t.Fatal(err) + } + + _, err = dhts[1].Connect(peers[3]) + if err != nil { + t.Fatal(err) + } + + err = dhts[3].putLocal(u.Key("hello"), []byte("world")) + if err != nil { + t.Fatal(err) + } + + err = dhts[3].Provide(u.Key("hello")) + if err != nil { + t.Fatal(err) + } + + time.Sleep(time.Millisecond * 60) + + val, err := dhts[0].GetValue(u.Key("hello"), time.Second) + if err != nil { + t.Fatal(err) + } + + if string(val) != "world" { + t.Fatal("Got incorrect value.") + } + +} + +func TestFindPeer(t *testing.T) { + // t.Skip("skipping test to debug another") + + u.Debug = false + + _, peers, dhts := setupDHTS(4, t) + defer func() { + for i := 0; i < 4; i++ { + dhts[i].Halt() + dhts[i].network.Close() + } + }() + + _, err := dhts[0].Connect(peers[1]) + if err != nil { + t.Fatal(err) + } + + _, err = dhts[1].Connect(peers[2]) + if err != nil { + t.Fatal(err) + } + + _, err = dhts[1].Connect(peers[3]) + if err != nil { + t.Fatal(err) + } + + p, err := dhts[0].FindPeer(peers[2].ID, time.Second) + if err != nil { + t.Fatal(err) + } + + if p == nil { + t.Fatal("Failed to find peer.") + } + + if !p.ID.Equal(peers[2].ID) { + t.Fatal("Didnt find expected peer.") + } +} diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 47eb6429a..26fbfea35 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -92,6 +92,8 @@ func (f *fauxNet) SendMessage(msg.NetMessage) error { func (f *fauxNet) Close() error { return nil } func TestGetFailures(t *testing.T) { + // t.Skip("skipping test because it makes a lot of output") + ctx := context.Background() fn := &fauxNet{} fs := &fauxSender{} @@ -189,6 +191,8 @@ func _randPeer() *peer.Peer { } func TestNotFound(t *testing.T) { + // t.Skip("skipping test because it makes a lot of output") + fn := &fauxNet{} fs := &fauxSender{} @@ -233,7 +237,7 @@ func TestNotFound(t *testing.T) { }) v, err := d.GetValue(u.Key("hello"), time.Second*5) - u.POut("get value got %v\n", v) + u.DOut("get value got %v\n", v) if err != nil { switch err { case u.ErrNotFound: @@ -251,6 +255,8 @@ func TestNotFound(t *testing.T) { // If less than K nodes are in the entire network, it should fail when we make // a GET rpc and nobody has the value func TestLessThanKResponses(t *testing.T) { + // t.Skip("skipping test because it makes a lot of output") + u.Debug = false fn := &fauxNet{} fs := &fauxSender{} diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 5320cc10a..fe22121bb 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -176,7 +176,7 @@ func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *Message) (*Message, er dht.self.ID.Pretty(), p.ID.Pretty(), peer.ID(key).Pretty()) dht.providers.AddProvider(key, p) - return nil, nil + return pmes, nil // send back same msg as confirmation. } // Halt stops all communications from this peer and shut down From b8ece7362a7e101a8a209b799e66096778c6eb94 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 20 Sep 2014 07:33:20 -0700 Subject: [PATCH 0162/3817] output + linting This commit was moved from ipfs/go-merkledag@90abcbc0069d3e50f2040fa39078342b8eccebe3 --- ipld/merkledag/merkledag.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 79530df6d..1ec5f3c5e 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -130,7 +130,6 @@ func (n *DAGService) AddRecursive(nd *Node) error { } for _, link := range nd.Links { - fmt.Println("Adding link.") if link.Node == nil { panic("Why does this node have a nil link?\n") } From 919f7bf4bd9ee255b028b6ccd81600f6226b8e53 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 20 Sep 2014 07:19:35 -0700 Subject: [PATCH 0163/3817] this warning should only print out on debug (perhaps should be logged instead) This commit was moved from ipfs/go-blockservice@29a24c8bbe4fbc0ca49a7ba1e1b44dd402aa0749 --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index e3c7402bd..1fbbfcb44 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -26,7 +26,7 @@ func NewBlockService(d ds.Datastore, rem exchange.Interface) (*BlockService, err return nil, fmt.Errorf("BlockService requires valid datastore") } if rem == nil { - u.PErr("Caution: blockservice running in local (offline) mode.\n") + u.DErr("Caution: blockservice running in local (offline) mode.\n") } return &BlockService{Datastore: d, Remote: rem}, nil } From 210aad6a57278caec98316faea637747daa3fdf1 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 21 Sep 2014 18:04:43 -0700 Subject: [PATCH 0164/3817] Routing uses context now @perfmode boom This commit was moved from ipfs/go-ipfs-routing@dd20457e1c3858af42fd965c5a8a31adf8f26092 --- routing/dht/dht.go | 8 ++--- routing/dht/dht_test.go | 74 +++++++++++++++++++++++++++++++++++++---- routing/dht/ext_test.go | 12 ++++--- routing/dht/routing.go | 29 +++++----------- routing/routing.go | 12 +++---- 5 files changed, 93 insertions(+), 42 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 148168d01..507c19c3f 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -73,7 +73,7 @@ func NewDHT(p *peer.Peer, ps peer.Peerstore, net inet.Network, sender inet.Sende } // Connect to a new peer at the given address, ping and add to the routing table -func (dht *IpfsDHT) Connect(npeer *peer.Peer) (*peer.Peer, error) { +func (dht *IpfsDHT) Connect(ctx context.Context, npeer *peer.Peer) (*peer.Peer, error) { u.DOut("Connect to new peer: %s\n", npeer.ID.Pretty()) // TODO(jbenet,whyrusleeping) @@ -92,7 +92,7 @@ func (dht *IpfsDHT) Connect(npeer *peer.Peer) (*peer.Peer, error) { // Ping new peer to register in their routing table // NOTE: this should be done better... - err = dht.Ping(npeer, time.Second*2) + err = dht.Ping(ctx, npeer) if err != nil { return nil, fmt.Errorf("failed to ping newly connected peer: %s\n", err) } @@ -497,8 +497,8 @@ func (dht *IpfsDHT) loadProvidableKeys() error { } // Bootstrap builds up list of peers by requesting random peer IDs -func (dht *IpfsDHT) Bootstrap() { +func (dht *IpfsDHT) Bootstrap(ctx context.Context) { id := make([]byte, 16) rand.Read(id) - dht.FindPeer(peer.ID(id), time.Second*10) + dht.FindPeer(ctx, peer.ID(id)) } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index e3f056ce2..675d80dde 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -22,7 +22,7 @@ import ( ) func setupDHT(t *testing.T, p *peer.Peer) *IpfsDHT { - ctx, _ := context.WithCancel(context.TODO()) + ctx := context.Background() peerstore := peer.NewPeerstore() @@ -150,9 +150,11 @@ func TestValueGetSet(t *testing.T) { t.Fatal(err) } - dhtA.PutValue("hello", []byte("world")) + ctxT, _ := context.WithTimeout(context.Background(), time.Second) + dhtA.PutValue(ctxT, "hello", []byte("world")) - val, err := dhtA.GetValue("hello", time.Second*2) + ctxT, _ = context.WithTimeout(context.Background(), time.Second*2) + val, err := dhtA.GetValue(ctxT, "hello") if err != nil { t.Fatal(err) } @@ -208,7 +210,8 @@ func TestProvides(t *testing.T) { time.Sleep(time.Millisecond * 60) - provs, err := dhts[0].FindProviders(u.Key("hello"), time.Second) + ctxT, _ := context.WithTimeout(context.Background(), time.Second) + provs, err := dhts[0].FindProviders(ctxT, u.Key("hello")) if err != nil { t.Fatal(err) } @@ -218,6 +221,63 @@ func TestProvides(t *testing.T) { } } +func TestProvidesAsync(t *testing.T) { + // t.Skip("skipping test to debug another") + + u.Debug = false + + _, peers, dhts := setupDHTS(4, t) + defer func() { + for i := 0; i < 4; i++ { + dhts[i].Halt() + defer dhts[i].network.Close() + } + }() + + _, err := dhts[0].Connect(peers[1]) + if err != nil { + t.Fatal(err) + } + + _, err = dhts[1].Connect(peers[2]) + if err != nil { + t.Fatal(err) + } + + _, err = dhts[1].Connect(peers[3]) + if err != nil { + t.Fatal(err) + } + + err = dhts[3].putLocal(u.Key("hello"), []byte("world")) + if err != nil { + t.Fatal(err) + } + + bits, err := dhts[3].getLocal(u.Key("hello")) + if err != nil && bytes.Equal(bits, []byte("world")) { + t.Fatal(err) + } + + err = dhts[3].Provide(u.Key("hello")) + if err != nil { + t.Fatal(err) + } + + time.Sleep(time.Millisecond * 60) + + ctx, _ := context.WithTimeout(context.TODO(), time.Millisecond*300) + provs := dhts[0].FindProvidersAsync(ctx, u.Key("hello"), 5) + select { + case p := <-provs: + if !p.ID.Equal(dhts[3].self.ID) { + t.Fatalf("got a provider, but not the right one. %v", p.ID.Pretty()) + } + case <-ctx.Done(): + t.Fatal("Didnt get back providers") + } +} + func TestLayeredGet(t *testing.T) { // t.Skip("skipping test to debug another") @@ -257,7 +317,8 @@ func TestLayeredGet(t *testing.T) { time.Sleep(time.Millisecond * 60) - val, err := dhts[0].GetValue(u.Key("hello"), time.Second) + ctxT, _ := context.WithTimeout(context.Background(), time.Second) + val, err := dhts[0].GetValue(ctxT, u.Key("hello")) if err != nil { t.Fatal(err) } @@ -296,7 +357,8 @@ func TestFindPeer(t *testing.T) { t.Fatal(err) } - p, err := dhts[0].FindPeer(peers[2].ID, time.Second) + ctxT, _ := context.WithTimeout(context.Background(), time.Second) + p, err := dhts[0].FindPeer(ctxT, peers[2].ID) if err != nil { t.Fatal(err) } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 26fbfea35..07999e651 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -108,7 +108,8 @@ func TestGetFailures(t *testing.T) { // This one should time out // u.POut("Timout Test\n") - _, err := d.GetValue(u.Key("test"), time.Millisecond*10) + ctx1, _ := context.WithTimeout(context.Background(), time.Second) + _, err := d.GetValue(ctx1, u.Key("test")) if err != nil { if err != context.DeadlineExceeded { t.Fatal("Got different error than we expected", err) @@ -134,7 +135,8 @@ func TestGetFailures(t *testing.T) { }) // This one should fail with NotFound - _, err = d.GetValue(u.Key("test"), time.Millisecond*1000) + ctx2, _ := context.WithTimeout(context.Background(), time.Second) + _, err = d.GetValue(ctx2, u.Key("test")) if err != nil { if err != u.ErrNotFound { t.Fatalf("Expected ErrNotFound, got: %s", err) @@ -236,7 +238,8 @@ func TestNotFound(t *testing.T) { }) - v, err := d.GetValue(u.Key("hello"), time.Second*5) + ctx, _ := context.WithTimeout(context.Background(), time.Second*5) + v, err := d.GetValue(ctx, u.Key("hello")) u.DOut("get value got %v\n", v) if err != nil { switch err { @@ -299,7 +302,8 @@ func TestLessThanKResponses(t *testing.T) { }) - _, err := d.GetValue(u.Key("hello"), time.Second*30) + ctx, _ := context.WithTimeout(context.Background(), time.Second*30) + _, err := d.GetValue(ctx, u.Key("hello")) if err != nil { switch err { case u.ErrNotFound: diff --git a/routing/dht/routing.go b/routing/dht/routing.go index acb4cab45..9f4a916e7 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -3,7 +3,6 @@ package dht import ( "bytes" "encoding/json" - "time" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -18,9 +17,7 @@ import ( // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT -func (dht *IpfsDHT) PutValue(key u.Key, value []byte) error { - ctx := context.TODO() - +func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error { peers := []*peer.Peer{} // get the peers we need to announce to @@ -46,12 +43,10 @@ func (dht *IpfsDHT) PutValue(key u.Key, value []byte) error { // GetValue searches for the value corresponding to given Key. // If the search does not succeed, a multiaddr string of a closer peer is // returned along with util.ErrSearchIncomplete -func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { +func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { ll := startNewRPC("GET") defer ll.EndAndPrint() - ctx, _ := context.WithTimeout(context.TODO(), timeout) - // If we have it local, dont bother doing an RPC! // NOTE: this might not be what we want to do... val, err := dht.getLocal(key) @@ -101,8 +96,7 @@ func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { // This is what DSHTs (Coral and MainlineDHT) do to store large values in a DHT. // Provide makes this node announce that it can provide a value for the given key -func (dht *IpfsDHT) Provide(key u.Key) error { - ctx := context.TODO() +func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { dht.providers.AddProvider(key, dht.self) peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), PoolSize) @@ -174,12 +168,10 @@ func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*Message_Peer, ps *peerSet } // FindProviders searches for peers who can provide the value for given key. -func (dht *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) { +func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]*peer.Peer, error) { ll := startNewRPC("FindProviders") ll.EndAndPrint() - ctx, _ := context.WithTimeout(context.TODO(), timeout) - // get closest peer u.DOut("Find providers for: '%s'\n", key) p := dht.routingTables[0].NearestPeer(kb.ConvertKey(key)) @@ -223,8 +215,7 @@ func (dht *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Pee // Find specific Peer // FindPeer searches for a peer with given ID. -func (dht *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) { - ctx, _ := context.WithTimeout(context.TODO(), timeout) +func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (*peer.Peer, error) { // Check if were already connected to them p, _ := dht.Find(id) @@ -266,8 +257,7 @@ func (dht *IpfsDHT) FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, err return nil, u.ErrNotFound } -func (dht *IpfsDHT) findPeerMultiple(id peer.ID, timeout time.Duration) (*peer.Peer, error) { - ctx, _ := context.WithTimeout(context.TODO(), timeout) +func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (*peer.Peer, error) { // Check if were already connected to them p, _ := dht.Find(id) @@ -325,9 +315,7 @@ func (dht *IpfsDHT) findPeerMultiple(id peer.ID, timeout time.Duration) (*peer.P } // Ping a peer, log the time it took -func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { - ctx, _ := context.WithTimeout(context.TODO(), timeout) - +func (dht *IpfsDHT) Ping(ctx context.Context, p *peer.Peer) error { // Thoughts: maybe this should accept an ID and do a peer lookup? u.DOut("Enter Ping.\n") @@ -336,8 +324,7 @@ func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { return err } -func (dht *IpfsDHT) getDiagnostic(timeout time.Duration) ([]*diagInfo, error) { - ctx, _ := context.WithTimeout(context.TODO(), timeout) +func (dht *IpfsDHT) getDiagnostic(ctx context.Context) ([]*diagInfo, error) { u.DOut("Begin Diagnostic") peers := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) diff --git a/routing/routing.go b/routing/routing.go index 872bad6f8..4669fb48c 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -1,8 +1,6 @@ package routing import ( - "time" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" peer "github.com/jbenet/go-ipfs/peer" @@ -17,22 +15,22 @@ type IpfsRouting interface { // Basic Put/Get // PutValue adds value corresponding to given Key. - PutValue(key u.Key, value []byte) error + PutValue(context.Context, u.Key, []byte) error // GetValue searches for the value corresponding to given Key. - GetValue(key u.Key, timeout time.Duration) ([]byte, error) + GetValue(context.Context, u.Key) ([]byte, error) // Value provider layer of indirection. // This is what DSHTs (Coral and MainlineDHT) do to store large values in a DHT. // Announce that this node can provide value for given key - Provide(key u.Key) error + Provide(context.Context, u.Key) error // FindProviders searches for peers who can provide the value for given key. - FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) + FindProviders(context.Context, u.Key) ([]*peer.Peer, error) // Find specific Peer // FindPeer searches for a peer with given ID. - FindPeer(id peer.ID, timeout time.Duration) (*peer.Peer, error) + FindPeer(context.Context, peer.ID) (*peer.Peer, error) } From 182d85427ed84818f9ea5ea09c2797be53bac786 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 21 Sep 2014 22:06:12 -0700 Subject: [PATCH 0165/3817] fix(routing:dht) implement FindProvidersAsync in terms of FindProviders until construction is complete on the actual async method reverts changes from ec50703395098f75946f0bad01816cc54ab18a58 https://github.com/jbenet/go-ipfs/commit/ec50703395098f75946f0bad01816cc54ab18a58 This commit was moved from ipfs/go-ipfs-routing@b20ad05062044669c196fed343941760419ed19d --- routing/dht/routing.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 9f4a916e7..762a8cfd9 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -115,8 +115,26 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { return nil } -// FindProvidersAsync runs FindProviders and sends back results over a channel +// NB: not actually async. Used to keep the interface consistent while the +// actual async method, FindProvidersAsync2 is under construction func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int) <-chan *peer.Peer { + ch := make(chan *peer.Peer) + providers, err := dht.FindProviders(ctx, key) + if err != nil { + close(ch) + return ch + } + go func() { + defer close(ch) + for _, p := range providers { + ch <- p + } + }() + return ch +} + +// FIXME: there's a bug here! +func (dht *IpfsDHT) FindProvidersAsync2(ctx context.Context, key u.Key, count int) <-chan *peer.Peer { peerOut := make(chan *peer.Peer, count) go func() { ps := newPeerSet() From 23bd11cd78c2a3cefb6880de7284b29fd419a514 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 21 Sep 2014 02:26:23 -0700 Subject: [PATCH 0166/3817] fix(exch) name the error This commit was moved from ipfs/go-ipfs-exchange-offline@392991c6227e2f292d48b9fe16ec3aef6701433c --- exchange/offline/offline.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 9695b0b56..2a7527f56 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -10,6 +10,8 @@ import ( u "github.com/jbenet/go-ipfs/util" ) +var OfflineMode = errors.New("Block unavailable. Operating in offline mode") + func NewOfflineExchange() exchange.Interface { return &offlineExchange{} } @@ -23,7 +25,7 @@ type offlineExchange struct { // given key. // NB: This function may return before the timeout expires. func (_ *offlineExchange) Block(context.Context, u.Key) (*blocks.Block, error) { - return nil, errors.New("Block unavailable. Operating in offline mode") + return nil, OfflineMode } // HasBlock always returns nil. From 95df066118f4aad8b89f2274529fd8f81663ea69 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 22 Sep 2014 03:41:56 -0700 Subject: [PATCH 0167/3817] fix(routing:dht) add ctx args This commit was moved from ipfs/go-ipfs-routing@03d6fd4a2b008fb9e6ddcfe26e937a54d8ec837b --- routing/dht/dht_test.go | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 675d80dde..7ad439ebb 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -109,13 +109,14 @@ func TestPing(t *testing.T) { defer dhtA.network.Close() defer dhtB.network.Close() - _, err = dhtA.Connect(peerB) + _, err = dhtA.Connect(context.Background(), peerB) if err != nil { t.Fatal(err) } //Test that we can ping the node - err = dhtA.Ping(peerB, time.Second*2) + ctx, _ := context.WithTimeout(context.Background(), 2*time.Millisecond) + err = dhtA.Ping(ctx, peerB) if err != nil { t.Fatal(err) } @@ -145,7 +146,7 @@ func TestValueGetSet(t *testing.T) { defer dhtA.network.Close() defer dhtB.network.Close() - _, err = dhtA.Connect(peerB) + _, err = dhtA.Connect(context.Background(), peerB) if err != nil { t.Fatal(err) } @@ -178,17 +179,17 @@ func TestProvides(t *testing.T) { } }() - _, err := dhts[0].Connect(peers[1]) + _, err := dhts[0].Connect(context.Background(), peers[1]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(peers[2]) + _, err = dhts[1].Connect(context.Background(), peers[2]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(peers[3]) + _, err = dhts[1].Connect(context.Background(), peers[3]) if err != nil { t.Fatal(err) } @@ -203,7 +204,7 @@ func TestProvides(t *testing.T) { t.Fatal(err) } - err = dhts[3].Provide(u.Key("hello")) + err = dhts[3].Provide(context.Background(), u.Key("hello")) if err != nil { t.Fatal(err) } @@ -234,17 +235,17 @@ func TestProvidesAsync(t *testing.T) { } }() - _, err := dhts[0].Connect(peers[1]) + _, err := dhts[0].Connect(context.Background(), peers[1]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(peers[2]) + _, err = dhts[1].Connect(context.Background(), peers[2]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(peers[3]) + _, err = dhts[1].Connect(context.Background(), peers[3]) if err != nil { t.Fatal(err) } @@ -259,7 +260,7 @@ func TestProvidesAsync(t *testing.T) { t.Fatal(err) } - err = dhts[3].Provide(u.Key("hello")) + err = dhts[3].Provide(context.Background(), u.Key("hello")) if err != nil { t.Fatal(err) } @@ -290,17 +291,17 @@ func TestLayeredGet(t *testing.T) { } }() - _, err := dhts[0].Connect(peers[1]) + _, err := dhts[0].Connect(context.Background(), peers[1]) if err != nil { t.Fatalf("Failed to connect: %s", err) } - _, err = dhts[1].Connect(peers[2]) + _, err = dhts[1].Connect(context.Background(), peers[2]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(peers[3]) + _, err = dhts[1].Connect(context.Background(), peers[3]) if err != nil { t.Fatal(err) } @@ -310,7 +311,7 @@ func TestLayeredGet(t *testing.T) { t.Fatal(err) } - err = dhts[3].Provide(u.Key("hello")) + err = dhts[3].Provide(context.Background(), u.Key("hello")) if err != nil { t.Fatal(err) } @@ -342,17 +343,17 @@ func TestFindPeer(t *testing.T) { } }() - _, err := dhts[0].Connect(peers[1]) + _, err := dhts[0].Connect(context.Background(), peers[1]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(peers[2]) + _, err = dhts[1].Connect(context.Background(), peers[2]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(peers[3]) + _, err = dhts[1].Connect(context.Background(), peers[3]) if err != nil { t.Fatal(err) } From 297384d5ebb24e264b60a30372af63031f8c7aad Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 22 Sep 2014 15:53:37 -0700 Subject: [PATCH 0168/3817] better logging for ping This commit was moved from ipfs/go-ipfs-routing@86e5c1448bf39a11eee2a1e6a8c34ccd1b3912ba --- routing/dht/routing.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 762a8cfd9..164e6ee3a 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -335,10 +335,11 @@ func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (*peer.Pee // Ping a peer, log the time it took func (dht *IpfsDHT) Ping(ctx context.Context, p *peer.Peer) error { // Thoughts: maybe this should accept an ID and do a peer lookup? - u.DOut("Enter Ping.\n") + u.DOut("[%s] ping %s start\n", dht.self.ID.Pretty(), p.ID.Pretty()) pmes := newMessage(Message_PING, "", 0) _, err := dht.sendRequest(ctx, p, pmes) + u.DOut("[%s] ping %s end (err = %s)\n", dht.self.ID.Pretty(), p.ID.Pretty(), err) return err } From 1a91bd8c33f85da49b891fc94024b8d4f3ef1b56 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Sep 2014 19:22:04 -0700 Subject: [PATCH 0169/3817] turn logging on by default, also make Provide not fail when no peers connected This commit was moved from ipfs/go-ipfs-routing@c32602a7632e23e724a3c915cdf412b8f63cfef3 --- routing/dht/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 164e6ee3a..a057ca828 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -194,7 +194,7 @@ func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]*peer.Peer, u.DOut("Find providers for: '%s'\n", key) p := dht.routingTables[0].NearestPeer(kb.ConvertKey(key)) if p == nil { - return nil, kb.ErrLookupFailure + return nil, nil } for level := 0; level < len(dht.routingTables); { From 4245ffe6dae6c76d36b4595acc9bc7198c416d67 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Sep 2014 22:34:30 -0700 Subject: [PATCH 0170/3817] make a few tests perform operations in two directions instead of one This commit was moved from ipfs/go-ipfs-routing@6265fa8e537c957e8c25c96fddc88d5504dc9517 --- routing/dht/dht_test.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 7ad439ebb..1f41e754a 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -115,11 +115,17 @@ func TestPing(t *testing.T) { } //Test that we can ping the node - ctx, _ := context.WithTimeout(context.Background(), 2*time.Millisecond) + ctx, _ := context.WithTimeout(context.Background(), 5*time.Millisecond) err = dhtA.Ping(ctx, peerB) if err != nil { t.Fatal(err) } + + ctx, _ = context.WithTimeout(context.Background(), 5*time.Millisecond) + err = dhtB.Ping(ctx, peerA) + if err != nil { + t.Fatal(err) + } } func TestValueGetSet(t *testing.T) { @@ -164,6 +170,15 @@ func TestValueGetSet(t *testing.T) { t.Fatalf("Expected 'world' got '%s'", string(val)) } + ctxT, _ = context.WithTimeout(context.Background(), time.Second*2) + val, err = dhtB.GetValue(ctxT, "hello") + if err != nil { + t.Fatal(err) + } + + if string(val) != "world" { + t.Fatalf("Expected 'world' got '%s'", string(val)) + } } func TestProvides(t *testing.T) { From 754b5030e8b48ea0517d3f1fd1e6bc9dee6abca4 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 23 Sep 2014 05:21:35 -0700 Subject: [PATCH 0171/3817] ping: return sme msg This fixes the broken pinging. (the issue was the cluster level, it's bein set incorrectly (off by one)) Anyway, this works now: [peer: QmfQTbC3LxfpK5WoyHW2WgnAzo6d6GePuq2wHTsJNXM5PS] Sent message type: 'PING' [to = QmNXUeFrV9gxR4aqJddEsfhWZLSJrUsfpUSeRb3R7xvSp9] [QmfQTbC3LxfpK5WoyHW2WgnAzo6d6GePuq2wHTsJNXM5PS] ping QmNXUeFrV9gxR4aqJddEsfhWZLSJrUsfpUSeRb3R7xvSp9 end (err = %!s()) cc @whyrusleeping This commit was moved from ipfs/go-ipfs-routing@1f77bb3ac6d1119315a60c4ca74d54fd30fe2aac --- routing/dht/handlers.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index fe22121bb..4301d1e4e 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -104,8 +104,7 @@ func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *Message) (*Message, error func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *Message) (*Message, error) { u.DOut("[%s] Responding to ping from [%s]!\n", dht.self.ID.Pretty(), p.ID.Pretty()) - - return newMessage(pmes.GetType(), "", int(pmes.GetClusterLevel())), nil + return pmes, nil } func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *Message) (*Message, error) { From ff3367ccbc8c56e7093e92e865d571dcbd05fb67 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Sep 2014 21:11:06 -0700 Subject: [PATCH 0172/3817] implement a mock dht for use in testing This commit was moved from ipfs/go-ipfs-routing@f91d5225d8258026b914c0512b5fccfd680c6633 --- routing/mock/routing.go | 130 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 routing/mock/routing.go diff --git a/routing/mock/routing.go b/routing/mock/routing.go new file mode 100644 index 000000000..c239c634e --- /dev/null +++ b/routing/mock/routing.go @@ -0,0 +1,130 @@ +package mockrouter + +import ( + "errors" + "math/rand" + "sync" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + peer "github.com/jbenet/go-ipfs/peer" + routing "github.com/jbenet/go-ipfs/routing" + u "github.com/jbenet/go-ipfs/util" +) + +var _ routing.IpfsRouting = &MockRouter{} + +type MockRouter struct { + datastore ds.Datastore + hashTable RoutingServer + peer *peer.Peer +} + +func NewMockRouter(local *peer.Peer, dstore ds.Datastore) *MockRouter { + return &MockRouter{ + datastore: dstore, + peer: local, + hashTable: VirtualRoutingServer(), + } +} + +func (mr *MockRouter) SetRoutingServer(rs RoutingServer) { + mr.hashTable = rs +} + +func (mr *MockRouter) PutValue(ctx context.Context, key u.Key, val []byte) error { + return mr.datastore.Put(ds.NewKey(string(key)), val) +} + +func (mr *MockRouter) GetValue(ctx context.Context, key u.Key) ([]byte, error) { + v, err := mr.datastore.Get(ds.NewKey(string(key))) + if err != nil { + return nil, err + } + + data, ok := v.([]byte) + if !ok { + return nil, errors.New("could not cast value from datastore") + } + + return data, nil +} + +func (mr *MockRouter) FindProviders(ctx context.Context, key u.Key) ([]*peer.Peer, error) { + return nil, nil +} + +func (mr *MockRouter) FindPeer(ctx context.Context, pid peer.ID) (*peer.Peer, error) { + return nil, nil +} + +func (mr *MockRouter) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-chan *peer.Peer { + out := make(chan *peer.Peer) + go func() { + defer close(out) + for i, p := range mr.hashTable.Providers(k) { + if max <= i { + return + } + select { + case out <- p: + case <-ctx.Done(): + return + } + } + }() + return out +} + +func (mr *MockRouter) Provide(_ context.Context, key u.Key) error { + return mr.hashTable.Announce(mr.peer, key) +} + +type RoutingServer interface { + Announce(*peer.Peer, u.Key) error + + Providers(u.Key) []*peer.Peer +} + +func VirtualRoutingServer() RoutingServer { + return &hashTable{ + providers: make(map[u.Key]peer.Map), + } +} + +type hashTable struct { + lock sync.RWMutex + providers map[u.Key]peer.Map +} + +func (rs *hashTable) Announce(p *peer.Peer, k u.Key) error { + rs.lock.Lock() + defer rs.lock.Unlock() + + _, ok := rs.providers[k] + if !ok { + rs.providers[k] = make(peer.Map) + } + rs.providers[k][p.Key()] = p + return nil +} + +func (rs *hashTable) Providers(k u.Key) []*peer.Peer { + rs.lock.RLock() + defer rs.lock.RUnlock() + ret := make([]*peer.Peer, 0) + peerset, ok := rs.providers[k] + if !ok { + return ret + } + for _, peer := range peerset { + ret = append(ret, peer) + } + + for i := range ret { + j := rand.Intn(i + 1) + ret[i], ret[j] = ret[j], ret[i] + } + + return ret +} From a5de1f52a12be0c76d87096da0e2aa8716cdc024 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 23 Sep 2014 11:45:02 -0700 Subject: [PATCH 0173/3817] change back to using Client method This commit was moved from ipfs/go-ipfs-routing@0febff309e1df0a737bf918caf41e89037df75cd --- routing/mock/routing.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/routing/mock/routing.go b/routing/mock/routing.go index c239c634e..43aecf269 100644 --- a/routing/mock/routing.go +++ b/routing/mock/routing.go @@ -20,7 +20,7 @@ type MockRouter struct { peer *peer.Peer } -func NewMockRouter(local *peer.Peer, dstore ds.Datastore) *MockRouter { +func NewMockRouter(local *peer.Peer, dstore ds.Datastore) routing.IpfsRouting { return &MockRouter{ datastore: dstore, peer: local, @@ -84,6 +84,8 @@ type RoutingServer interface { Announce(*peer.Peer, u.Key) error Providers(u.Key) []*peer.Peer + + Client(p *peer.Peer) routing.IpfsRouting } func VirtualRoutingServer() RoutingServer { @@ -128,3 +130,10 @@ func (rs *hashTable) Providers(k u.Key) []*peer.Peer { return ret } + +func (rs *hashTable) Client(p *peer.Peer) routing.IpfsRouting { + return &MockRouter{ + peer: p, + hashTable: rs, + } +} From 635c1e6fa252ce62bd2f4e2d40ebc0d28ba461c8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 23 Sep 2014 14:08:37 -0700 Subject: [PATCH 0174/3817] move mock routing tests to proper directory This commit was moved from ipfs/go-ipfs-routing@f386e017e2dceb5c139f45daf7fa19aa4bcfca66 --- routing/mock/routing.go | 2 +- routing/mock/routing_test.go | 154 +++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 routing/mock/routing_test.go diff --git a/routing/mock/routing.go b/routing/mock/routing.go index 43aecf269..e5fdb96fc 100644 --- a/routing/mock/routing.go +++ b/routing/mock/routing.go @@ -1,4 +1,4 @@ -package mockrouter +package mock import ( "errors" diff --git a/routing/mock/routing_test.go b/routing/mock/routing_test.go new file mode 100644 index 000000000..650f5d3d5 --- /dev/null +++ b/routing/mock/routing_test.go @@ -0,0 +1,154 @@ +package mock + +import ( + "bytes" + "testing" + + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" +) + +func TestKeyNotFound(t *testing.T) { + + vrs := VirtualRoutingServer() + empty := vrs.Providers(u.Key("not there")) + if len(empty) != 0 { + t.Fatal("should be empty") + } +} + +func TestSetAndGet(t *testing.T) { + pid := peer.ID([]byte("the peer id")) + p := &peer.Peer{ + ID: pid, + } + k := u.Key("42") + rs := VirtualRoutingServer() + err := rs.Announce(p, k) + if err != nil { + t.Fatal(err) + } + providers := rs.Providers(k) + if len(providers) != 1 { + t.Fatal("should be one") + } + for _, elem := range providers { + if bytes.Equal(elem.ID, pid) { + return + } + } + t.Fatal("ID should have matched") +} + +func TestClientFindProviders(t *testing.T) { + peer := &peer.Peer{ID: []byte("42")} + rs := VirtualRoutingServer() + client := rs.Client(peer) + + k := u.Key("hello") + err := client.Provide(context.Background(), k) + if err != nil { + t.Fatal(err) + } + max := 100 + + providersFromHashTable := rs.Providers(k) + + isInHT := false + for _, p := range providersFromHashTable { + if bytes.Equal(p.ID, peer.ID) { + isInHT = true + } + } + if !isInHT { + t.Fatal("Despite client providing key, peer wasn't in hash table as a provider") + } + providersFromClient := client.FindProvidersAsync(context.Background(), u.Key("hello"), max) + isInClient := false + for p := range providersFromClient { + if bytes.Equal(p.ID, peer.ID) { + isInClient = true + } + } + if !isInClient { + t.Fatal("Despite client providing key, client didn't receive peer when finding providers") + } +} + +func TestClientOverMax(t *testing.T) { + rs := VirtualRoutingServer() + k := u.Key("hello") + numProvidersForHelloKey := 100 + for i := 0; i < numProvidersForHelloKey; i++ { + peer := &peer.Peer{ + ID: []byte(string(i)), + } + err := rs.Announce(peer, k) + if err != nil { + t.Fatal(err) + } + } + providersFromHashTable := rs.Providers(k) + if len(providersFromHashTable) != numProvidersForHelloKey { + t.Log(1 == len(providersFromHashTable)) + t.Fatal("not all providers were returned") + } + + max := 10 + peer := &peer.Peer{ID: []byte("TODO")} + client := rs.Client(peer) + + providersFromClient := client.FindProvidersAsync(context.Background(), k, max) + i := 0 + for _ = range providersFromClient { + i++ + } + if i != max { + t.Fatal("Too many providers returned") + } +} + +// TODO does dht ensure won't receive self as a provider? probably not. +func TestCanceledContext(t *testing.T) { + rs := VirtualRoutingServer() + k := u.Key("hello") + + t.Log("async'ly announce infinite stream of providers for key") + i := 0 + go func() { // infinite stream + for { + peer := &peer.Peer{ + ID: []byte(string(i)), + } + err := rs.Announce(peer, k) + if err != nil { + t.Fatal(err) + } + i++ + } + }() + + local := &peer.Peer{ID: []byte("peer id doesn't matter")} + client := rs.Client(local) + + t.Log("warning: max is finite so this test is non-deterministic") + t.Log("context cancellation could simply take lower priority") + t.Log("and result in receiving the max number of results") + max := 1000 + + t.Log("cancel the context before consuming") + ctx, cancelFunc := context.WithCancel(context.Background()) + cancelFunc() + providers := client.FindProvidersAsync(ctx, k, max) + + numProvidersReturned := 0 + for _ = range providers { + numProvidersReturned++ + } + t.Log(numProvidersReturned) + + if numProvidersReturned == max { + t.Fatal("Context cancel had no effect") + } +} From e7b3ec8f190b8014dba4e16235b4578d3571eba9 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 22 Sep 2014 14:04:41 -0400 Subject: [PATCH 0175/3817] feat(net:service, routing) remove error return value This commit was moved from ipfs/go-ipfs-routing@ee17430ae255f391e3d490f2a7e26059d0cff669 --- routing/dht/dht.go | 24 +++++++++++++++--------- routing/dht/ext_test.go | 5 +---- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 507c19c3f..8ebecd5bd 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -103,23 +103,26 @@ func (dht *IpfsDHT) Connect(ctx context.Context, npeer *peer.Peer) (*peer.Peer, } // HandleMessage implements the inet.Handler interface. -func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) (msg.NetMessage, error) { +func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.NetMessage { mData := mes.Data() if mData == nil { - return nil, errors.New("message did not include Data") + // TODO handle/log err + return nil } mPeer := mes.Peer() if mPeer == nil { - return nil, errors.New("message did not include a Peer") + // TODO handle/log err + return nil } // deserialize msg pmes := new(Message) err := proto.Unmarshal(mData, pmes) if err != nil { - return nil, fmt.Errorf("Failed to decode protobuf message: %v\n", err) + // TODO handle/log err + return nil } // update the peer (on valid msgs only) @@ -133,27 +136,30 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) (msg. // get handler for this msg type. handler := dht.handlerForMsgType(pmes.GetType()) if handler == nil { - return nil, errors.New("Recieved invalid message type") + // TODO handle/log err + return nil } // dispatch handler. rpmes, err := handler(mPeer, pmes) if err != nil { - return nil, err + // TODO handle/log err + return nil } // if nil response, return it before serializing if rpmes == nil { - return nil, nil + return nil } // serialize response msg rmes, err := msg.FromObject(mPeer, rpmes) if err != nil { - return nil, fmt.Errorf("Failed to encode protobuf message: %v\n", err) + // TODO handle/log err + return nil } - return rmes, nil + return rmes } // sendRequest sends out a request using dht.sender, but also makes sure to diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 07999e651..f8b9293a8 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -161,10 +161,7 @@ func TestGetFailures(t *testing.T) { t.Error(err) } - mes, err = d.HandleMessage(ctx, mes) - if err != nil { - t.Error(err) - } + mes = d.HandleMessage(ctx, mes) pmes := new(Message) err = proto.Unmarshal(mes.Data(), pmes) From 19c5d9912a2a5bbd5c428081e052dc281cf54d0f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 26 Sep 2014 02:09:48 -0700 Subject: [PATCH 0176/3817] update net with peerstore This commit was moved from ipfs/go-ipfs-routing@dfb0add1faff06479aa6f96dc7f4606fc97232a5 --- routing/dht/dht_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 1f41e754a..1bbc62cdc 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -31,7 +31,7 @@ func setupDHT(t *testing.T, p *peer.Peer) *IpfsDHT { t.Fatal(err) } - net, err := inet.NewIpfsNetwork(ctx, p, &mux.ProtocolMap{ + net, err := inet.NewIpfsNetwork(ctx, p, peerstore, &mux.ProtocolMap{ mux.ProtocolID_Routing: dhts, }) if err != nil { From ab879ffdc0562a8a866df2c405ce8a91fa302cca Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 27 Sep 2014 16:02:50 -0700 Subject: [PATCH 0177/3817] udpated commands and RPC dialing to work with new configuration changes This commit was moved from ipfs/go-ipfs-routing@a4f725dea53b8b7958a771798f8cc71788b2bd70 --- routing/dht/routing.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index a057ca828..66ae09848 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -59,7 +59,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { routeLevel := 0 closest := dht.routingTables[routeLevel].NearestPeers(kb.ConvertKey(key), PoolSize) if closest == nil || len(closest) == 0 { - return nil, kb.ErrLookupFailure + return nil, nil } // setup the Query @@ -101,7 +101,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { dht.providers.AddProvider(key, dht.self) peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), PoolSize) if len(peers) == 0 { - return kb.ErrLookupFailure + return nil } //TODO FIX: this doesn't work! it needs to be sent to the actual nearest peers. @@ -245,7 +245,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (*peer.Peer, error routeLevel := 0 p = dht.routingTables[routeLevel].NearestPeer(kb.ConvertPeerID(id)) if p == nil { - return nil, kb.ErrLookupFailure + return nil, nil } if p.ID.Equal(id) { return p, nil @@ -287,7 +287,7 @@ func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (*peer.Pee routeLevel := 0 peers := dht.routingTables[routeLevel].NearestPeers(kb.ConvertPeerID(id), AlphaValue) if len(peers) == 0 { - return nil, kb.ErrLookupFailure + return nil, nil } // setup query function From 556b935c0aed82080c6d4801ec127abff021984f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 10 Sep 2014 16:55:49 +0000 Subject: [PATCH 0178/3817] implement namesys resolvers (thanks to bren2010 for dns and proquint) This commit was moved from ipfs/go-namesys@59eb2350afba56e0461dda65fd0999e7e17d513e --- namesys/dns.go | 33 +++++++++++++++++++ namesys/entry.pb.go | 48 +++++++++++++++++++++++++++ namesys/entry.proto | 6 ++++ namesys/nsresolver.go | 5 +++ namesys/proquint.go | 22 +++++++++++++ namesys/resolver.go | 21 ++++++++++++ namesys/routing.go | 77 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 212 insertions(+) create mode 100644 namesys/dns.go create mode 100644 namesys/entry.pb.go create mode 100644 namesys/entry.proto create mode 100644 namesys/nsresolver.go create mode 100644 namesys/proquint.go create mode 100644 namesys/resolver.go create mode 100644 namesys/routing.go diff --git a/namesys/dns.go b/namesys/dns.go new file mode 100644 index 000000000..b12bc0d38 --- /dev/null +++ b/namesys/dns.go @@ -0,0 +1,33 @@ +package namesys + +import ( + "net" + "strings" + + u "github.com/jbenet/go-ipfs/util" +) + +type DNSResolver struct { + // TODO: maybe some sort of caching? + // cache would need a timeout +} + +func (r *DNSResolver) Resolve(name string) (string, error) { + txt, err := net.LookupTXT(name) + if err != nil { + return "", err + } + + for _, t := range txt { + pair := strings.Split(t, "=") + if len(pair) < 2 { + // Log error? + u.DErr("Incorrectly formatted text record.") + continue + } + if pair[0] == name { + return pair[1], nil + } + } + return "", u.ErrNotFound +} diff --git a/namesys/entry.pb.go b/namesys/entry.pb.go new file mode 100644 index 000000000..c05efeb2f --- /dev/null +++ b/namesys/entry.pb.go @@ -0,0 +1,48 @@ +// Code generated by protoc-gen-go. +// source: entry.proto +// DO NOT EDIT! + +/* +Package namesys is a generated protocol buffer package. + +It is generated from these files: + entry.proto + +It has these top-level messages: + InpsEntry +*/ +package namesys + +import proto "code.google.com/p/goprotobuf/proto" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = math.Inf + +type InpsEntry struct { + Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"` + Signature []byte `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *InpsEntry) Reset() { *m = InpsEntry{} } +func (m *InpsEntry) String() string { return proto.CompactTextString(m) } +func (*InpsEntry) ProtoMessage() {} + +func (m *InpsEntry) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func (m *InpsEntry) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +func init() { +} diff --git a/namesys/entry.proto b/namesys/entry.proto new file mode 100644 index 000000000..b7b09f041 --- /dev/null +++ b/namesys/entry.proto @@ -0,0 +1,6 @@ +package namesys; + +message InpsEntry { + required bytes value = 1; + required bytes signature = 2; +} diff --git a/namesys/nsresolver.go b/namesys/nsresolver.go new file mode 100644 index 000000000..ef5bc65cb --- /dev/null +++ b/namesys/nsresolver.go @@ -0,0 +1,5 @@ +package namesys + +type NSResolver interface { + Resolve(string) (string, error) +} diff --git a/namesys/proquint.go b/namesys/proquint.go new file mode 100644 index 000000000..8583a5390 --- /dev/null +++ b/namesys/proquint.go @@ -0,0 +1,22 @@ +package namesys + +import ( + "errors" + + proquint "github.com/bren2010/proquint" +) + +var _ = proquint.Encode + +type ProquintResolver struct{} + +func (r *ProquintResolver) Resolve(name string) (string, error) { + ok, err := proquint.IsProquint(name) + if err != nil { + return "", err + } + if !ok { + return "", errors.New("not a valid proquint string") + } + return string(proquint.Decode(name)), nil +} diff --git a/namesys/resolver.go b/namesys/resolver.go new file mode 100644 index 000000000..3498cbd46 --- /dev/null +++ b/namesys/resolver.go @@ -0,0 +1,21 @@ +package namesys + +import "strings" + +type MasterResolver struct { + dns *DNSResolver + routing *RoutingResolver + pro *ProquintResolver +} + +func (mr *MasterResolver) Resolve(name string) (string, error) { + if strings.Contains(name, ".") { + return mr.dns.Resolve(name) + } + + if strings.Contains(name, "-") { + return mr.pro.Resolve(name) + } + + return mr.routing.Resolve(name) +} diff --git a/namesys/routing.go b/namesys/routing.go new file mode 100644 index 000000000..f37b6485f --- /dev/null +++ b/namesys/routing.go @@ -0,0 +1,77 @@ +package namesys + +import ( + "fmt" + "time" + + "code.google.com/p/goprotobuf/proto" + + ci "github.com/jbenet/go-ipfs/crypto" + mdag "github.com/jbenet/go-ipfs/merkledag" + "github.com/jbenet/go-ipfs/routing" + u "github.com/jbenet/go-ipfs/util" + mh "github.com/jbenet/go-multihash" +) + +// RoutingName is the de-serialized name structure that is stored (serialized) +// in the routing system. Basically, a hash + a digital signature. (serialization can be +// protobuf, or a simple binary format) +type RoutingName struct { + Hash u.Key + Signature []byte +} + +// RoutingResolver implements NSResolver for the main IPFS SFS-like naming +type RoutingResolver struct { + routing routing.IpfsRouting + dag mdag.DAGService +} + +func (r *RoutingResolver) Resolve(name string) (string, error) { + hash, err := mh.FromB58String(name) + if err != nil { + return "", err + } + // name should be a multihash. if it isn't, error out here. + + // use the routing system to get the name. + // /ipns/ + h, err := u.Hash([]byte("ipns:" + name)) + if err != nil { + return "", err + } + + inpsKey := u.Key(h) + val, err := r.routing.GetValue(inpsKey, time.Second*10) + if err != nil { + return "", err + } + + entry := new(InpsEntry) + err = proto.Unmarshal(val, entry) + if err != nil { + return "", err + } + + // name should be a public key retrievable from ipfs + // /ipfs/ + key := u.Key(hash) + node, err := r.dag.Get(key) + if err != nil { + return "", err + } + + // get PublicKey from node.Data + pk, err := ci.UnmarshalPublicKey(node.Data) + if err != nil { + return "", err + } + + // check sig with pk + if ok, err := pk.Verify(entry.GetValue(), entry.GetSignature()); err != nil && ok { + return "", fmt.Errorf("Invalid value. Not signed by PrivateKey corresponding to %v", pk) + } + + // ok sig checks out. this is a valid name. + return string(entry.GetValue()), nil +} From c8e9c15b59ee4e314307302e11a466de0aa4fc3f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 10 Sep 2014 20:56:29 +0000 Subject: [PATCH 0179/3817] fixes to make interface more usable This commit was moved from ipfs/go-ipfs-routing@6b26888c7691d2c538b5e9c11304d9dd6ce62392 --- routing/dht/routing.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 66ae09848..16f74380b 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -101,6 +101,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { dht.providers.AddProvider(key, dht.self) peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), PoolSize) if len(peers) == 0 { + // Early out for no targets return nil } From 246c5a73e80f31de6c300732265bd745af72a37d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 10 Sep 2014 20:56:29 +0000 Subject: [PATCH 0180/3817] fixes to make interface more usable This commit was moved from ipfs/go-merkledag@c30179cf5acc41c853b761e4f9dbc40f1d752709 --- ipld/merkledag/merkledag.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 1ec5f3c5e..1cc262783 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -3,7 +3,8 @@ package merkledag import ( "fmt" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" blocks "github.com/jbenet/go-ipfs/blocks" @@ -11,6 +12,8 @@ import ( u "github.com/jbenet/go-ipfs/util" ) +var log = logging.MustGetLogger("commands") + // NodeMap maps u.Keys to Nodes. // We cannot use []byte/Multihash for keys :( // so have to convert Multihash bytes to string (u.Key) @@ -105,7 +108,7 @@ type DAGService struct { // Add adds a node to the DAGService, storing the block in the BlockService func (n *DAGService) Add(nd *Node) (u.Key, error) { k, _ := nd.Key() - u.DOut("DagService Add [%s]\n", k.Pretty()) + log.Debug("DagService Add [%s]\n", k.Pretty()) if n == nil { return "", fmt.Errorf("DAGService is nil") } @@ -126,6 +129,7 @@ func (n *DAGService) Add(nd *Node) (u.Key, error) { func (n *DAGService) AddRecursive(nd *Node) error { _, err := n.Add(nd) if err != nil { + log.Info("AddRecursive Error: %s\n", err) return err } From ac74870471e322666f4f47096160e41c92760710 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 12 Sep 2014 02:41:46 +0000 Subject: [PATCH 0181/3817] implement ipns publisher code This commit was moved from ipfs/go-namesys@771520354e26eba71474f246531c131c02510943 --- namesys/entry.pb.go | 14 +++++----- namesys/entry.proto | 2 +- namesys/publisher.go | 64 ++++++++++++++++++++++++++++++++++++++++++++ namesys/resolver.go | 15 ++++++++++- namesys/routing.go | 19 +++++++------ 5 files changed, 95 insertions(+), 19 deletions(-) create mode 100644 namesys/publisher.go diff --git a/namesys/entry.pb.go b/namesys/entry.pb.go index c05efeb2f..e4420e9f9 100644 --- a/namesys/entry.pb.go +++ b/namesys/entry.pb.go @@ -9,7 +9,7 @@ It is generated from these files: entry.proto It has these top-level messages: - InpsEntry + IpnsEntry */ package namesys @@ -20,24 +20,24 @@ import math "math" var _ = proto.Marshal var _ = math.Inf -type InpsEntry struct { +type IpnsEntry struct { Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"` Signature []byte `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"` XXX_unrecognized []byte `json:"-"` } -func (m *InpsEntry) Reset() { *m = InpsEntry{} } -func (m *InpsEntry) String() string { return proto.CompactTextString(m) } -func (*InpsEntry) ProtoMessage() {} +func (m *IpnsEntry) Reset() { *m = IpnsEntry{} } +func (m *IpnsEntry) String() string { return proto.CompactTextString(m) } +func (*IpnsEntry) ProtoMessage() {} -func (m *InpsEntry) GetValue() []byte { +func (m *IpnsEntry) GetValue() []byte { if m != nil { return m.Value } return nil } -func (m *InpsEntry) GetSignature() []byte { +func (m *IpnsEntry) GetSignature() []byte { if m != nil { return m.Signature } diff --git a/namesys/entry.proto b/namesys/entry.proto index b7b09f041..fee830d7e 100644 --- a/namesys/entry.proto +++ b/namesys/entry.proto @@ -1,6 +1,6 @@ package namesys; -message InpsEntry { +message IpnsEntry { required bytes value = 1; required bytes signature = 2; } diff --git a/namesys/publisher.go b/namesys/publisher.go new file mode 100644 index 000000000..4588ccb6a --- /dev/null +++ b/namesys/publisher.go @@ -0,0 +1,64 @@ +package namesys + +import ( + "code.google.com/p/goprotobuf/proto" + + ci "github.com/jbenet/go-ipfs/crypto" + mdag "github.com/jbenet/go-ipfs/merkledag" + routing "github.com/jbenet/go-ipfs/routing" + u "github.com/jbenet/go-ipfs/util" +) + +type IpnsPublisher struct { + dag *mdag.DAGService + routing routing.IpfsRouting +} + +// Publish accepts a keypair and a value, +func (p *IpnsPublisher) Publish(k ci.PrivKey, value u.Key) error { + data, err := CreateEntryData(k, value) + if err != nil { + return err + } + pubkey := k.GetPublic() + pkbytes, err := pubkey.Bytes() + if err != nil { + return nil + } + + nameb, err := u.Hash(pkbytes) + if err != nil { + return nil + } + namekey := u.Key(nameb).Pretty() + + ipnskey, err := u.Hash([]byte("ipns:" + namekey)) + if err != nil { + return err + } + + // Store associated public key + err = p.routing.PutValue(u.Key(nameb), pkbytes) + if err != nil { + return err + } + + // Store ipns entry at h("ipns:"+b58(h(pubkey))) + err = p.routing.PutValue(u.Key(ipnskey), data) + if err != nil { + return err + } + + return nil +} + +func CreateEntryData(pk ci.PrivKey, val u.Key) ([]byte, error) { + entry := new(IpnsEntry) + sig, err := pk.Sign([]byte(val)) + if err != nil { + return nil, err + } + entry.Signature = sig + entry.Value = []byte(val) + return proto.Marshal(entry) +} diff --git a/namesys/resolver.go b/namesys/resolver.go index 3498cbd46..e440946b9 100644 --- a/namesys/resolver.go +++ b/namesys/resolver.go @@ -1,6 +1,11 @@ package namesys -import "strings" +import ( + "strings" + + mdag "github.com/jbenet/go-ipfs/merkledag" + "github.com/jbenet/go-ipfs/routing" +) type MasterResolver struct { dns *DNSResolver @@ -8,6 +13,14 @@ type MasterResolver struct { pro *ProquintResolver } +func NewMasterResolver(r routing.IpfsRouting, dag *mdag.DAGService) *MasterResolver { + mr := new(MasterResolver) + mr.dns = new(DNSResolver) + mr.pro = new(ProquintResolver) + mr.routing = NewRoutingResolver(r, dag) + return mr +} + func (mr *MasterResolver) Resolve(name string) (string, error) { if strings.Contains(name, ".") { return mr.dns.Resolve(name) diff --git a/namesys/routing.go b/namesys/routing.go index f37b6485f..ff0c2df39 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -13,18 +13,17 @@ import ( mh "github.com/jbenet/go-multihash" ) -// RoutingName is the de-serialized name structure that is stored (serialized) -// in the routing system. Basically, a hash + a digital signature. (serialization can be -// protobuf, or a simple binary format) -type RoutingName struct { - Hash u.Key - Signature []byte -} - // RoutingResolver implements NSResolver for the main IPFS SFS-like naming type RoutingResolver struct { routing routing.IpfsRouting - dag mdag.DAGService + dag *mdag.DAGService +} + +func NewRoutingResolver(route routing.IpfsRouting, dagservice *mdag.DAGService) *RoutingResolver { + return &RoutingResolver{ + routing: route, + dag: dagservice, + } } func (r *RoutingResolver) Resolve(name string) (string, error) { @@ -47,7 +46,7 @@ func (r *RoutingResolver) Resolve(name string) (string, error) { return "", err } - entry := new(InpsEntry) + entry := new(IpnsEntry) err = proto.Unmarshal(val, entry) if err != nil { return "", err From 9c0f349ae3f1491e8f1472c46e23a12b08d2017b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 11 Sep 2014 19:12:14 +0000 Subject: [PATCH 0182/3817] some bugfixes and added logging This commit was moved from ipfs/go-ipfs-routing@209a5c9b35bc05e6d6313ecfad47852ad7eb10bd --- routing/dht/routing.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 16f74380b..3d7a4bf0a 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -152,15 +152,14 @@ func (dht *IpfsDHT) FindProvidersAsync2(ctx context.Context, key u.Key, count in peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), AlphaValue) for _, pp := range peers { - ppp := pp - go func() { - pmes, err := dht.findProvidersSingle(ctx, ppp, key, 0) + go func(p *peer.Peer) { + pmes, err := dht.findProvidersSingle(p, key, 0, timeout) if err != nil { u.PErr("%v\n", err) return } - dht.addPeerListAsync(key, pmes.GetProviderPeers(), ps, count, peerOut) - }() + dht.addPeerListAsync(key, pmes.GetPeers(), ps, count, peerOut) + }(pp) } }() From 46077b36f459c5da8cba88e1db51e4e9684a8d42 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 12 Sep 2014 17:34:07 +0000 Subject: [PATCH 0183/3817] add routing resolver test This commit was moved from ipfs/go-namesys@fe8c47c89e1e8cb8c318ede9b546e250436bcdb7 --- namesys/nsresolver.go | 2 +- namesys/resolve_test.go | 66 +++++++++++++++++++++++++++++++++++++++++ namesys/routing.go | 7 +++-- 3 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 namesys/resolve_test.go diff --git a/namesys/nsresolver.go b/namesys/nsresolver.go index ef5bc65cb..5dd981330 100644 --- a/namesys/nsresolver.go +++ b/namesys/nsresolver.go @@ -1,5 +1,5 @@ package namesys -type NSResolver interface { +type Resolver interface { Resolve(string) (string, error) } diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go new file mode 100644 index 000000000..0620bd446 --- /dev/null +++ b/namesys/resolve_test.go @@ -0,0 +1,66 @@ +package namesys + +import ( + "testing" + + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + bs "github.com/jbenet/go-ipfs/blockservice" + ci "github.com/jbenet/go-ipfs/crypto" + mdag "github.com/jbenet/go-ipfs/merkledag" + "github.com/jbenet/go-ipfs/peer" + "github.com/jbenet/go-ipfs/routing/dht" + "github.com/jbenet/go-ipfs/swarm" + u "github.com/jbenet/go-ipfs/util" +) + +func TestRoutingResolve(t *testing.T) { + local := &peer.Peer{ + ID: []byte("testID"), + } + net := swarm.NewSwarm(local) + lds := ds.NewMapDatastore() + d := dht.NewDHT(local, net, lds) + + bserv, err := bs.NewBlockService(lds, nil) + if err != nil { + t.Fatal(err) + } + + dag := &mdag.DAGService{Blocks: bserv} + + resolve := NewMasterResolver(d, dag) + + pub := IpnsPublisher{ + dag: dag, + routing: d, + } + + privk, pubk, err := ci.GenerateKeyPair(ci.RSA, 512) + if err != nil { + t.Fatal(err) + } + + err = pub.Publish(privk, u.Key("Hello")) + if err != nil { + t.Fatal(err) + } + + pubkb, err := pubk.Bytes() + if err != nil { + t.Fatal(err) + } + + pkhash, err := u.Hash(pubkb) + if err != nil { + t.Fatal(err) + } + + res, err := resolve.Resolve(u.Key(pkhash).Pretty()) + if err != nil { + t.Fatal(err) + } + + if res != "Hello" { + t.Fatal("Got back incorrect value.") + } +} diff --git a/namesys/routing.go b/namesys/routing.go index ff0c2df39..e667f5b68 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -29,6 +29,7 @@ func NewRoutingResolver(route routing.IpfsRouting, dagservice *mdag.DAGService) func (r *RoutingResolver) Resolve(name string) (string, error) { hash, err := mh.FromB58String(name) if err != nil { + u.DOut("RoutingResolve: bad input hash: [%s]\n", name) return "", err } // name should be a multihash. if it isn't, error out here. @@ -43,6 +44,7 @@ func (r *RoutingResolver) Resolve(name string) (string, error) { inpsKey := u.Key(h) val, err := r.routing.GetValue(inpsKey, time.Second*10) if err != nil { + u.DOut("RoutingResolve get failed.\n") return "", err } @@ -55,13 +57,14 @@ func (r *RoutingResolver) Resolve(name string) (string, error) { // name should be a public key retrievable from ipfs // /ipfs/ key := u.Key(hash) - node, err := r.dag.Get(key) + pkval, err := r.routing.GetValue(key, time.Second*10) if err != nil { + u.DOut("RoutingResolve PubKey Get failed.\n") return "", err } // get PublicKey from node.Data - pk, err := ci.UnmarshalPublicKey(node.Data) + pk, err := ci.UnmarshalPublicKey(pkval) if err != nil { return "", err } From d8e683be9f2eab9978370800a9f5a50147731757 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 12 Sep 2014 17:34:07 +0000 Subject: [PATCH 0184/3817] add routing resolver test This commit was moved from ipfs/go-ipfs-routing@baa2eaae3558de348fdaa9793a0cb9d416f42417 --- routing/dht/routing.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 3d7a4bf0a..b2c403803 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -17,10 +17,13 @@ import ( // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT -func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error { - peers := []*peer.Peer{} +func (dht *IpfsDHT) PutValue(key u.Key, value []byte) error { + err := dht.putLocal(key, value) + if err != nil { + return err + } - // get the peers we need to announce to + var peers []*peer.Peer for _, route := range dht.routingTables { npeers := route.NearestPeers(kb.ConvertKey(key), KValue) peers = append(peers, npeers...) From 1dd5b343ae43a249c90350f3d53736e8abc7c45e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 11 Sep 2014 02:20:04 +0000 Subject: [PATCH 0185/3817] make disconnects and reconnects work a little better This commit was moved from ipfs/go-path@70cdee43132325d10366536221874a881b59d845 --- path/path.go | 1 + 1 file changed, 1 insertion(+) diff --git a/path/path.go b/path/path.go index a06fb98cb..eb5ce6660 100644 --- a/path/path.go +++ b/path/path.go @@ -38,6 +38,7 @@ func (s *Resolver) ResolvePath(fpath string) (*merkledag.Node, error) { // first element in the path is a b58 hash (for now) h, err := mh.FromB58String(parts[0]) if err != nil { + u.DOut("given path element is not a base58 string.\n") return nil, err } From 90b73a95fcebc5b724b4bd7e0309266364f541dd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 15 Sep 2014 20:32:37 +0000 Subject: [PATCH 0186/3817] address comments from the PR #45 This commit was moved from ipfs/go-namesys@17e28f4077be4d04f8cf06db1971fad2fdf32707 --- namesys/dns.go | 4 ++++ namesys/nsresolver.go | 1 + namesys/proquint.go | 5 +++++ namesys/publisher.go | 4 ++-- namesys/resolver.go | 34 +++++++++++++++++++++------------- namesys/routing.go | 13 +++++++++---- 6 files changed, 42 insertions(+), 19 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index b12bc0d38..51e99ed78 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -12,6 +12,10 @@ type DNSResolver struct { // cache would need a timeout } +func (r *DNSResolver) Matches(name string) bool { + return strings.Contains(name, ".") +} + func (r *DNSResolver) Resolve(name string) (string, error) { txt, err := net.LookupTXT(name) if err != nil { diff --git a/namesys/nsresolver.go b/namesys/nsresolver.go index 5dd981330..c8d40dcb3 100644 --- a/namesys/nsresolver.go +++ b/namesys/nsresolver.go @@ -2,4 +2,5 @@ package namesys type Resolver interface { Resolve(string) (string, error) + Matches(string) bool } diff --git a/namesys/proquint.go b/namesys/proquint.go index 8583a5390..8c4db2799 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -10,6 +10,11 @@ var _ = proquint.Encode type ProquintResolver struct{} +func (r *ProquintResolver) Matches(name string) bool { + ok, err := proquint.IsProquint(name) + return err == nil && ok +} + func (r *ProquintResolver) Resolve(name string) (string, error) { ok, err := proquint.IsProquint(name) if err != nil { diff --git a/namesys/publisher.go b/namesys/publisher.go index 4588ccb6a..d64d7954b 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -32,7 +32,7 @@ func (p *IpnsPublisher) Publish(k ci.PrivKey, value u.Key) error { } namekey := u.Key(nameb).Pretty() - ipnskey, err := u.Hash([]byte("ipns:" + namekey)) + ipnskey, err := u.Hash([]byte("/ipns/" + namekey)) if err != nil { return err } @@ -43,7 +43,7 @@ func (p *IpnsPublisher) Publish(k ci.PrivKey, value u.Key) error { return err } - // Store ipns entry at h("ipns:"+b58(h(pubkey))) + // Store ipns entry at h("/ipns/"+b58(h(pubkey))) err = p.routing.PutValue(u.Key(ipnskey), data) if err != nil { return err diff --git a/namesys/resolver.go b/namesys/resolver.go index e440946b9..2fedf210c 100644 --- a/namesys/resolver.go +++ b/namesys/resolver.go @@ -1,34 +1,42 @@ package namesys import ( - "strings" + "errors" mdag "github.com/jbenet/go-ipfs/merkledag" "github.com/jbenet/go-ipfs/routing" ) +var ErrCouldntResolve = errors.New("could not resolve name.") + type MasterResolver struct { - dns *DNSResolver - routing *RoutingResolver - pro *ProquintResolver + res []Resolver } func NewMasterResolver(r routing.IpfsRouting, dag *mdag.DAGService) *MasterResolver { mr := new(MasterResolver) - mr.dns = new(DNSResolver) - mr.pro = new(ProquintResolver) - mr.routing = NewRoutingResolver(r, dag) + mr.res = []Resolver{ + new(DNSResolver), + new(ProquintResolver), + NewRoutingResolver(r, dag), + } return mr } func (mr *MasterResolver) Resolve(name string) (string, error) { - if strings.Contains(name, ".") { - return mr.dns.Resolve(name) + for _, r := range mr.res { + if r.Matches(name) { + return r.Resolve(name) + } } + return "", ErrCouldntResolve +} - if strings.Contains(name, "-") { - return mr.pro.Resolve(name) +func (mr *MasterResolver) Matches(name string) bool { + for _, r := range mr.res { + if r.Matches(name) { + return true + } } - - return mr.routing.Resolve(name) + return false } diff --git a/namesys/routing.go b/namesys/routing.go index e667f5b68..5f85e942f 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -26,6 +26,11 @@ func NewRoutingResolver(route routing.IpfsRouting, dagservice *mdag.DAGService) } } +func (r *RoutingResolver) Matches(name string) bool { + _, err := mh.FromB58String(name) + return err == nil +} + func (r *RoutingResolver) Resolve(name string) (string, error) { hash, err := mh.FromB58String(name) if err != nil { @@ -36,13 +41,13 @@ func (r *RoutingResolver) Resolve(name string) (string, error) { // use the routing system to get the name. // /ipns/ - h, err := u.Hash([]byte("ipns:" + name)) + h, err := u.Hash([]byte("/ipns/" + name)) if err != nil { return "", err } - inpsKey := u.Key(h) - val, err := r.routing.GetValue(inpsKey, time.Second*10) + ipnsKey := u.Key(h) + val, err := r.routing.GetValue(ipnsKey, time.Second*10) if err != nil { u.DOut("RoutingResolve get failed.\n") return "", err @@ -70,7 +75,7 @@ func (r *RoutingResolver) Resolve(name string) (string, error) { } // check sig with pk - if ok, err := pk.Verify(entry.GetValue(), entry.GetSignature()); err != nil && ok { + if ok, err := pk.Verify(entry.GetValue(), entry.GetSignature()); err != nil || !ok { return "", fmt.Errorf("Invalid value. Not signed by PrivateKey corresponding to %v", pk) } From d4f1ce55641126e60c9cfd90829199cf499c2339 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 16 Sep 2014 03:44:53 +0000 Subject: [PATCH 0187/3817] Address concerns from PR This commit was moved from ipfs/go-namesys@7ef34bee76ecabd55909bbc5165bd37663fc14d1 --- namesys/proquint.go | 7 +------ namesys/resolver.go | 10 +++++----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/namesys/proquint.go b/namesys/proquint.go index 8c4db2799..9b64f3fde 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -6,8 +6,6 @@ import ( proquint "github.com/bren2010/proquint" ) -var _ = proquint.Encode - type ProquintResolver struct{} func (r *ProquintResolver) Matches(name string) bool { @@ -16,10 +14,7 @@ func (r *ProquintResolver) Matches(name string) bool { } func (r *ProquintResolver) Resolve(name string) (string, error) { - ok, err := proquint.IsProquint(name) - if err != nil { - return "", err - } + ok := r.Matches(name) if !ok { return "", errors.New("not a valid proquint string") } diff --git a/namesys/resolver.go b/namesys/resolver.go index 2fedf210c..7765a4ba0 100644 --- a/namesys/resolver.go +++ b/namesys/resolver.go @@ -9,12 +9,12 @@ import ( var ErrCouldntResolve = errors.New("could not resolve name.") -type MasterResolver struct { +type masterResolver struct { res []Resolver } -func NewMasterResolver(r routing.IpfsRouting, dag *mdag.DAGService) *MasterResolver { - mr := new(MasterResolver) +func NewMasterResolver(r routing.IpfsRouting, dag *mdag.DAGService) Resolver { + mr := new(masterResolver) mr.res = []Resolver{ new(DNSResolver), new(ProquintResolver), @@ -23,7 +23,7 @@ func NewMasterResolver(r routing.IpfsRouting, dag *mdag.DAGService) *MasterResol return mr } -func (mr *MasterResolver) Resolve(name string) (string, error) { +func (mr *masterResolver) Resolve(name string) (string, error) { for _, r := range mr.res { if r.Matches(name) { return r.Resolve(name) @@ -32,7 +32,7 @@ func (mr *MasterResolver) Resolve(name string) (string, error) { return "", ErrCouldntResolve } -func (mr *MasterResolver) Matches(name string) bool { +func (mr *masterResolver) Matches(name string) bool { for _, r := range mr.res { if r.Matches(name) { return true From f345e59c4e1f2ef87090cadc8afbdefa62df1798 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Sep 2014 15:14:19 -0700 Subject: [PATCH 0188/3817] catch ipns branch up to master and make all things compile This commit was moved from ipfs/go-ipfs-routing@01d68e36504e4152173e8330ababa31f73163d3e --- routing/dht/dht.go | 1 + routing/dht/routing.go | 16 ++++++---------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 8ebecd5bd..11712338b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -345,6 +345,7 @@ func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { // Update signals to all routingTables to Update their last-seen status // on the given peer. func (dht *IpfsDHT) Update(p *peer.Peer) { + u.DOut("updating peer: [%s] latency = %f\n", p.ID.Pretty(), p.GetLatency().Seconds()) removedCount := 0 for _, route := range dht.routingTables { removed := route.Update(p) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index b2c403803..778aaba75 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -17,7 +17,7 @@ import ( // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT -func (dht *IpfsDHT) PutValue(key u.Key, value []byte) error { +func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error { err := dht.putLocal(key, value) if err != nil { return err @@ -38,7 +38,7 @@ func (dht *IpfsDHT) PutValue(key u.Key, value []byte) error { return &dhtQueryResult{success: true}, nil }) - _, err := query.Run(ctx, peers) + _, err = query.Run(ctx, peers) u.DOut("[%s] PutValue %v %v\n", dht.self.ID.Pretty(), key, value) return err } @@ -104,8 +104,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { dht.providers.AddProvider(key, dht.self) peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), PoolSize) if len(peers) == 0 { - // Early out for no targets - return nil + return kb.ErrLookupFailure } //TODO FIX: this doesn't work! it needs to be sent to the actual nearest peers. @@ -156,12 +155,12 @@ func (dht *IpfsDHT) FindProvidersAsync2(ctx context.Context, key u.Key, count in peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), AlphaValue) for _, pp := range peers { go func(p *peer.Peer) { - pmes, err := dht.findProvidersSingle(p, key, 0, timeout) + pmes, err := dht.findProvidersSingle(ctx, p, key, 0) if err != nil { u.PErr("%v\n", err) return } - dht.addPeerListAsync(key, pmes.GetPeers(), ps, count, peerOut) + dht.addPeerListAsync(key, pmes.GetProviderPeers(), ps, count, peerOut) }(pp) } @@ -190,11 +189,8 @@ func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*Message_Peer, ps *peerSet // FindProviders searches for peers who can provide the value for given key. func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]*peer.Peer, error) { - ll := startNewRPC("FindProviders") - ll.EndAndPrint() - // get closest peer - u.DOut("Find providers for: '%s'\n", key) + u.DOut("Find providers for: '%s'\n", key.Pretty()) p := dht.routingTables[0].NearestPeer(kb.ConvertKey(key)) if p == nil { return nil, nil From 7176bb48e4e5878304380de5b67bf9dc6748ec17 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Sep 2014 15:14:19 -0700 Subject: [PATCH 0189/3817] catch ipns branch up to master and make all things compile This commit was moved from ipfs/go-namesys@eaf0d669c794dba127b64db1a8164a6888ccfd7a --- namesys/dns.go | 18 ++++++++++++------ namesys/publisher.go | 6 ++++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 16 ++++++++++------ 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 51e99ed78..55d00f945 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -4,6 +4,8 @@ import ( "net" "strings" + b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" u "github.com/jbenet/go-ipfs/util" ) @@ -16,6 +18,8 @@ func (r *DNSResolver) Matches(name string) bool { return strings.Contains(name, ".") } +// TXT records for a given domain name should contain a b58 +// encoded multihash. func (r *DNSResolver) Resolve(name string) (string, error) { txt, err := net.LookupTXT(name) if err != nil { @@ -23,15 +27,17 @@ func (r *DNSResolver) Resolve(name string) (string, error) { } for _, t := range txt { - pair := strings.Split(t, "=") - if len(pair) < 2 { - // Log error? - u.DErr("Incorrectly formatted text record.") + chk := b58.Decode(t) + if len(chk) == 0 { continue } - if pair[0] == name { - return pair[1], nil + + _, err := mh.Cast(chk) + if err != nil { + continue } + return t, nil } + return "", u.ErrNotFound } diff --git a/namesys/publisher.go b/namesys/publisher.go index d64d7954b..83f418a57 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -1,6 +1,7 @@ package namesys import ( + "code.google.com/p/go.net/context" "code.google.com/p/goprotobuf/proto" ci "github.com/jbenet/go-ipfs/crypto" @@ -16,6 +17,7 @@ type IpnsPublisher struct { // Publish accepts a keypair and a value, func (p *IpnsPublisher) Publish(k ci.PrivKey, value u.Key) error { + ctx := context.TODO() data, err := CreateEntryData(k, value) if err != nil { return err @@ -38,13 +40,13 @@ func (p *IpnsPublisher) Publish(k ci.PrivKey, value u.Key) error { } // Store associated public key - err = p.routing.PutValue(u.Key(nameb), pkbytes) + err = p.routing.PutValue(ctx, u.Key(nameb), pkbytes) if err != nil { return err } // Store ipns entry at h("/ipns/"+b58(h(pubkey))) - err = p.routing.PutValue(u.Key(ipnskey), data) + err = p.routing.PutValue(ctx, u.Key(ipnskey), data) if err != nil { return err } diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 0620bd446..32a12cf76 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -7,9 +7,9 @@ import ( bs "github.com/jbenet/go-ipfs/blockservice" ci "github.com/jbenet/go-ipfs/crypto" mdag "github.com/jbenet/go-ipfs/merkledag" + "github.com/jbenet/go-ipfs/net/swarm" "github.com/jbenet/go-ipfs/peer" "github.com/jbenet/go-ipfs/routing/dht" - "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" ) diff --git a/namesys/routing.go b/namesys/routing.go index 5f85e942f..2b1d06c80 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -2,8 +2,8 @@ package namesys import ( "fmt" - "time" + "code.google.com/p/go.net/context" "code.google.com/p/goprotobuf/proto" ci "github.com/jbenet/go-ipfs/crypto" @@ -11,8 +11,11 @@ import ( "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" mh "github.com/jbenet/go-multihash" + "github.com/op/go-logging" ) +var log = logging.MustGetLogger("namesys") + // RoutingResolver implements NSResolver for the main IPFS SFS-like naming type RoutingResolver struct { routing routing.IpfsRouting @@ -32,9 +35,10 @@ func (r *RoutingResolver) Matches(name string) bool { } func (r *RoutingResolver) Resolve(name string) (string, error) { + ctx := context.TODO() hash, err := mh.FromB58String(name) if err != nil { - u.DOut("RoutingResolve: bad input hash: [%s]\n", name) + log.Warning("RoutingResolve: bad input hash: [%s]\n", name) return "", err } // name should be a multihash. if it isn't, error out here. @@ -47,9 +51,9 @@ func (r *RoutingResolver) Resolve(name string) (string, error) { } ipnsKey := u.Key(h) - val, err := r.routing.GetValue(ipnsKey, time.Second*10) + val, err := r.routing.GetValue(ctx, ipnsKey) if err != nil { - u.DOut("RoutingResolve get failed.\n") + log.Warning("RoutingResolve get failed.") return "", err } @@ -62,9 +66,9 @@ func (r *RoutingResolver) Resolve(name string) (string, error) { // name should be a public key retrievable from ipfs // /ipfs/ key := u.Key(hash) - pkval, err := r.routing.GetValue(key, time.Second*10) + pkval, err := r.routing.GetValue(ctx, key) if err != nil { - u.DOut("RoutingResolve PubKey Get failed.\n") + log.Warning("RoutingResolve PubKey Get failed.") return "", err } From 6db5be9a3296f4b98e8f913bd9092806eb89d837 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 25 Sep 2014 15:10:57 -0700 Subject: [PATCH 0190/3817] add basic publish command, needs polish This commit was moved from ipfs/go-ipfs-routing@b30288aa3312e77ec0d3882783a6d1b16d7db722 --- routing/dht/dht.go | 3 +++ routing/dht/query.go | 7 ++++++- routing/dht/routing.go | 4 ++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 11712338b..f9f52b108 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -13,6 +13,7 @@ import ( peer "github.com/jbenet/go-ipfs/peer" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" + "github.com/op/go-logging" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" @@ -21,6 +22,8 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ) +var log = logging.MustGetLogger("dht") + // TODO. SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-dht/index.js // IpfsDHT is an implementation of Kademlia with Coral and S/Kademlia modifications. diff --git a/routing/dht/query.go b/routing/dht/query.go index 4db3f70e7..cc709d7e9 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -101,6 +101,11 @@ func newQueryRunner(ctx context.Context, q *dhtQuery) *dhtQueryRunner { } func (r *dhtQueryRunner) Run(peers []*peer.Peer) (*dhtQueryResult, error) { + log.Debug("Run query with %d peers.", len(peers)) + if len(peers) == 0 { + log.Warning("Running query with no peers!") + return nil, nil + } // setup concurrency rate limiting for i := 0; i < r.query.concurrency; i++ { r.rateLimit <- struct{}{} @@ -164,7 +169,7 @@ func (r *dhtQueryRunner) addPeerToQuery(next *peer.Peer, benchmark *peer.Peer) { r.peersSeen[next.Key()] = next r.Unlock() - u.DOut("adding peer to query: %v\n", next.ID.Pretty()) + log.Debug("adding peer to query: %v\n", next.ID.Pretty()) // do this after unlocking to prevent possible deadlocks. r.peersRemaining.Increment(1) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 778aaba75..6a2a0cdcb 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -18,6 +18,7 @@ import ( // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error { + log.Debug("[%s] PutValue %v %v", dht.self.ID.Pretty(), key, value) err := dht.putLocal(key, value) if err != nil { return err @@ -30,7 +31,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error } query := newQuery(key, func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) { - u.DOut("[%s] PutValue qry part %v\n", dht.self.ID.Pretty(), p.ID.Pretty()) + log.Debug("[%s] PutValue qry part %v", dht.self.ID.Pretty(), p.ID.Pretty()) err := dht.putValueToNetwork(ctx, p, string(key), value) if err != nil { return nil, err @@ -39,7 +40,6 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error }) _, err = query.Run(ctx, peers) - u.DOut("[%s] PutValue %v %v\n", dht.self.ID.Pretty(), key, value) return err } From 8a4afadf3af221a30bc4c9f74771e192f79d8eb6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 25 Sep 2014 18:29:05 -0700 Subject: [PATCH 0191/3817] implement initial ipns filesystem interface as well as plumbing command for publishing This commit was moved from ipfs/go-ipfs-routing@3fd024ce5b9f829b88db9c68bced77de2da96113 --- routing/dht/query.go | 1 + routing/dht/routing.go | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/routing/dht/query.go b/routing/dht/query.go index cc709d7e9..a62646f05 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -106,6 +106,7 @@ func (r *dhtQueryRunner) Run(peers []*peer.Peer) (*dhtQueryResult, error) { log.Warning("Running query with no peers!") return nil, nil } + // setup concurrency rate limiting for i := 0; i < r.query.concurrency; i++ { r.rateLimit <- struct{}{} diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 6a2a0cdcb..a62f4c01b 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -47,14 +47,13 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error // If the search does not succeed, a multiaddr string of a closer peer is // returned along with util.ErrSearchIncomplete func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { - ll := startNewRPC("GET") - defer ll.EndAndPrint() + log.Debug("Get Value [%s]", key.Pretty()) // If we have it local, dont bother doing an RPC! // NOTE: this might not be what we want to do... val, err := dht.getLocal(key) if err == nil { - ll.Success = true + log.Debug("Got value locally!") return val, nil } From fa73f16614660001b9fc5d1c4b4ea0c1ba2d921d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 25 Sep 2014 22:00:05 -0700 Subject: [PATCH 0192/3817] writes to ipns work if the top object is the written file (no directories yet!) This commit was moved from ipfs/go-ipfs-routing@2cb6d50703fad21d5ac25a68eda4f6e9a2b0d194 --- routing/dht/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index a62f4c01b..65e4e3b54 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -103,7 +103,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { dht.providers.AddProvider(key, dht.self) peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), PoolSize) if len(peers) == 0 { - return kb.ErrLookupFailure + return nil } //TODO FIX: this doesn't work! it needs to be sent to the actual nearest peers. From ede6567bfa257c4ac3b02cf71fc1b6db49acd8b5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 25 Sep 2014 15:10:57 -0700 Subject: [PATCH 0193/3817] add basic publish command, needs polish This commit was moved from ipfs/go-namesys@ee57663f4c125bac79573eead4149913ad34776b --- namesys/publisher.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 83f418a57..67830eb6b 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -1,6 +1,8 @@ package namesys import ( + "time" + "code.google.com/p/go.net/context" "code.google.com/p/goprotobuf/proto" @@ -15,8 +17,16 @@ type IpnsPublisher struct { routing routing.IpfsRouting } +func NewPublisher(dag *mdag.DAGService, route routing.IpfsRouting) *IpnsPublisher { + return &IpnsPublisher{ + dag: dag, + routing: route, + } +} + // Publish accepts a keypair and a value, func (p *IpnsPublisher) Publish(k ci.PrivKey, value u.Key) error { + log.Debug("namesys: Publish %s", value.Pretty()) ctx := context.TODO() data, err := CreateEntryData(k, value) if err != nil { @@ -40,13 +50,15 @@ func (p *IpnsPublisher) Publish(k ci.PrivKey, value u.Key) error { } // Store associated public key - err = p.routing.PutValue(ctx, u.Key(nameb), pkbytes) + timectx, _ := context.WithDeadline(ctx, time.Now().Add(time.Second*4)) + err = p.routing.PutValue(timectx, u.Key(nameb), pkbytes) if err != nil { return err } // Store ipns entry at h("/ipns/"+b58(h(pubkey))) - err = p.routing.PutValue(ctx, u.Key(ipnskey), data) + timectx, _ = context.WithDeadline(ctx, time.Now().Add(time.Second*4)) + err = p.routing.PutValue(timectx, u.Key(ipnskey), data) if err != nil { return err } From 2f47d6b99c8fc2f079041af8790ffd572573e91b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 26 Sep 2014 00:09:27 -0700 Subject: [PATCH 0194/3817] WIP: getting closer to being able to write in ipns dirs This commit was moved from ipfs/go-ipfs-routing@206251e99cfe057311c13f7e019839a697d4b50f --- routing/dht/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 65e4e3b54..e7f98ad4e 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -18,7 +18,7 @@ import ( // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error { - log.Debug("[%s] PutValue %v %v", dht.self.ID.Pretty(), key, value) + log.Debug("[%s] PutValue %v %v", dht.self.ID.Pretty(), key.Pretty(), value) err := dht.putLocal(key, value) if err != nil { return err From aa2571389c450b435f822b37ee9f35b0ed227a52 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 26 Sep 2014 12:08:00 -0700 Subject: [PATCH 0195/3817] writing files inside ipns works now! also implemented resolve cli command This commit was moved from ipfs/go-ipfs-routing@eaa11048756da415cd57e40c89370ddead0ee2bb --- routing/dht/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index e7f98ad4e..c1b85cefa 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -18,7 +18,7 @@ import ( // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error { - log.Debug("[%s] PutValue %v %v", dht.self.ID.Pretty(), key.Pretty(), value) + log.Debug("PutValue %s %v", key.Pretty(), value) err := dht.putLocal(key, value) if err != nil { return err From 8de7c55ebfa888d37f34be5be2850d7f7954529b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 26 Sep 2014 12:08:00 -0700 Subject: [PATCH 0196/3817] writing files inside ipns works now! also implemented resolve cli command This commit was moved from ipfs/go-namesys@01cb315707c7dbd7a2d5845ee5acfe30fcffef6f --- namesys/nsresolver.go | 2 ++ namesys/publisher.go | 6 +++--- namesys/resolve_test.go | 8 +++----- namesys/routing.go | 1 + 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/namesys/nsresolver.go b/namesys/nsresolver.go index c8d40dcb3..89ef9ff5a 100644 --- a/namesys/nsresolver.go +++ b/namesys/nsresolver.go @@ -1,6 +1,8 @@ package namesys type Resolver interface { + // Resolve returns a base58 encoded string Resolve(string) (string, error) + Matches(string) bool } diff --git a/namesys/publisher.go b/namesys/publisher.go index 67830eb6b..73a4197e1 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -25,8 +25,8 @@ func NewPublisher(dag *mdag.DAGService, route routing.IpfsRouting) *IpnsPublishe } // Publish accepts a keypair and a value, -func (p *IpnsPublisher) Publish(k ci.PrivKey, value u.Key) error { - log.Debug("namesys: Publish %s", value.Pretty()) +func (p *IpnsPublisher) Publish(k ci.PrivKey, value string) error { + log.Debug("namesys: Publish %s", value) ctx := context.TODO() data, err := CreateEntryData(k, value) if err != nil { @@ -66,7 +66,7 @@ func (p *IpnsPublisher) Publish(k ci.PrivKey, value u.Key) error { return nil } -func CreateEntryData(pk ci.PrivKey, val u.Key) ([]byte, error) { +func CreateEntryData(pk ci.PrivKey, val string) ([]byte, error) { entry := new(IpnsEntry) sig, err := pk.Sign([]byte(val)) if err != nil { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 32a12cf76..7cad8bef0 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -7,9 +7,8 @@ import ( bs "github.com/jbenet/go-ipfs/blockservice" ci "github.com/jbenet/go-ipfs/crypto" mdag "github.com/jbenet/go-ipfs/merkledag" - "github.com/jbenet/go-ipfs/net/swarm" "github.com/jbenet/go-ipfs/peer" - "github.com/jbenet/go-ipfs/routing/dht" + mock "github.com/jbenet/go-ipfs/routing/mock" u "github.com/jbenet/go-ipfs/util" ) @@ -17,9 +16,8 @@ func TestRoutingResolve(t *testing.T) { local := &peer.Peer{ ID: []byte("testID"), } - net := swarm.NewSwarm(local) lds := ds.NewMapDatastore() - d := dht.NewDHT(local, net, lds) + d := mock.NewMockRouter(local, lds) bserv, err := bs.NewBlockService(lds, nil) if err != nil { @@ -40,7 +38,7 @@ func TestRoutingResolve(t *testing.T) { t.Fatal(err) } - err = pub.Publish(privk, u.Key("Hello")) + err = pub.Publish(privk, "Hello") if err != nil { t.Fatal(err) } diff --git a/namesys/routing.go b/namesys/routing.go index 2b1d06c80..605458eef 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -35,6 +35,7 @@ func (r *RoutingResolver) Matches(name string) bool { } func (r *RoutingResolver) Resolve(name string) (string, error) { + log.Debug("RoutingResolve: '%s'", name) ctx := context.TODO() hash, err := mh.FromB58String(name) if err != nil { From 3d187ea04540e2c1eb5c418047e105d543594740 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 26 Sep 2014 00:09:27 -0700 Subject: [PATCH 0197/3817] WIP: getting closer to being able to write in ipns dirs This commit was moved from ipfs/go-merkledag@8dda783034bc44a562583f1cca0163a20ccb6377 --- ipld/merkledag/merkledag.go | 38 ++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 1cc262783..4c57fb95c 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -12,7 +12,7 @@ import ( u "github.com/jbenet/go-ipfs/util" ) -var log = logging.MustGetLogger("commands") +var log = logging.MustGetLogger("merkledag") // NodeMap maps u.Keys to Nodes. // We cannot use []byte/Multihash for keys :( @@ -96,6 +96,31 @@ func (n *Node) Key() (u.Key, error) { return u.Key(h), err } +// Recursively update all hash links and size values in the tree +func (n *Node) Update() error { + log.Debug("node update") + for _, l := range n.Links { + if l.Node != nil { + err := l.Node.Update() + if err != nil { + return err + } + nhash, err := l.Node.Multihash() + if err != nil { + return err + } + l.Hash = nhash + size, err := l.Node.Size() + if err != nil { + return err + } + l.Size = size + } + } + _, err := n.Encoded(true) + return err +} + // DAGService is an IPFS Merkle DAG service. // - the root is virtual (like a forest) // - stores nodes' data in a BlockService @@ -134,12 +159,11 @@ func (n *DAGService) AddRecursive(nd *Node) error { } for _, link := range nd.Links { - if link.Node == nil { - panic("Why does this node have a nil link?\n") - } - err := n.AddRecursive(link.Node) - if err != nil { - return err + if link.Node != nil { + err := n.AddRecursive(link.Node) + if err != nil { + return err + } } } From 87991e8fcc5a48777f94e238a58c1dc8fc553ede Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 26 Sep 2014 12:08:00 -0700 Subject: [PATCH 0198/3817] writing files inside ipns works now! also implemented resolve cli command This commit was moved from ipfs/go-path@ee3799867af57d1c0836ce65b9d75b3cd58198b0 --- path/path.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/path/path.go b/path/path.go index eb5ce6660..533afc7cd 100644 --- a/path/path.go +++ b/path/path.go @@ -8,8 +8,11 @@ import ( mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" merkledag "github.com/jbenet/go-ipfs/merkledag" u "github.com/jbenet/go-ipfs/util" + "github.com/op/go-logging" ) +var log = logging.MustGetLogger("path") + // Resolver provides path resolution to IPFS // It has a pointer to a DAGService, which is uses to resolve nodes. type Resolver struct { @@ -20,7 +23,7 @@ type Resolver struct { // path component as a hash (key) of the first node, then resolves // all other components walking the links, with ResolveLinks. func (s *Resolver) ResolvePath(fpath string) (*merkledag.Node, error) { - u.DOut("Resolve: '%s'\n", fpath) + log.Debug("Resolve: '%s'", fpath) fpath = path.Clean(fpath) parts := strings.Split(fpath, "/") @@ -66,10 +69,12 @@ func (s *Resolver) ResolveLinks(ndd *merkledag.Node, names []string) ( for _, name := range names { var next u.Key + var nlink *merkledag.Link // for each of the links in nd, the current object for _, link := range nd.Links { if link.Name == name { next = u.Key(link.Hash) + nlink = link break } } @@ -80,10 +85,15 @@ func (s *Resolver) ResolveLinks(ndd *merkledag.Node, names []string) ( return nil, fmt.Errorf("no link named %q under %s", name, h2) } - // fetch object for link and assign to nd - nd, err = s.DAG.Get(next) - if err != nil { - return nd, err + if nlink.Node == nil { + // fetch object for link and assign to nd + nd, err = s.DAG.Get(next) + if err != nil { + return nd, err + } + nlink.Node = nd + } else { + nd = nlink.Node } } return From 7341e5845959d7fac25df0275398b928857b8207 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 28 Sep 2014 00:13:07 -0700 Subject: [PATCH 0199/3817] update logging in multiple packages This commit was moved from ipfs/go-ipfs-routing@91341560eca12f0143101aa8c2e2a034b71a08ce --- routing/dht/dht.go | 30 +++++++++++++++--------------- routing/dht/routing.go | 10 +++++----- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index f9f52b108..4e844ecd6 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -77,7 +77,7 @@ func NewDHT(p *peer.Peer, ps peer.Peerstore, net inet.Network, sender inet.Sende // Connect to a new peer at the given address, ping and add to the routing table func (dht *IpfsDHT) Connect(ctx context.Context, npeer *peer.Peer) (*peer.Peer, error) { - u.DOut("Connect to new peer: %s\n", npeer.ID.Pretty()) + log.Debug("Connect to new peer: %s\n", npeer.ID.Pretty()) // TODO(jbenet,whyrusleeping) // @@ -132,7 +132,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N dht.Update(mPeer) // Print out diagnostic - u.DOut("[peer: %s] Got message type: '%s' [from = %s]\n", + log.Debug("[peer: %s] Got message type: '%s' [from = %s]\n", dht.self.ID.Pretty(), Message_MessageType_name[int32(pmes.GetType())], mPeer.ID.Pretty()) @@ -177,7 +177,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p *peer.Peer, pmes *Message start := time.Now() // Print out diagnostic - u.DOut("[peer: %s] Sent message type: '%s' [to = %s]\n", + log.Debug("[peer: %s] Sent message type: '%s' [to = %s]\n", dht.self.ID.Pretty(), Message_MessageType_name[int32(pmes.GetType())], p.ID.Pretty()) @@ -224,7 +224,7 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p *peer.Peer, key string) e return err } - u.DOut("[%s] putProvider: %s for %s\n", dht.self.ID.Pretty(), p.ID.Pretty(), key) + log.Debug("[%s] putProvider: %s for %s", dht.self.ID.Pretty(), p.ID.Pretty(), key) if *rpmes.Key != *pmes.Key { return errors.New("provider not added correctly") } @@ -240,10 +240,10 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, return nil, nil, err } - u.DOut("pmes.GetValue() %v\n", pmes.GetValue()) + log.Debug("pmes.GetValue() %v", pmes.GetValue()) if value := pmes.GetValue(); value != nil { // Success! We were given the value - u.DOut("getValueOrPeers: got value\n") + log.Debug("getValueOrPeers: got value") return value, nil, nil } @@ -253,7 +253,7 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, if err != nil { return nil, nil, err } - u.DOut("getValueOrPeers: get from providers\n") + log.Debug("getValueOrPeers: get from providers") return val, nil, nil } @@ -266,7 +266,7 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, addr, err := ma.NewMultiaddr(pb.GetAddr()) if err != nil { - u.PErr("%v\n", err.Error()) + log.Error("%v", err.Error()) continue } @@ -281,11 +281,11 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, } if len(peers) > 0 { - u.DOut("getValueOrPeers: peers\n") + u.DOut("getValueOrPeers: peers") return nil, peers, nil } - u.DOut("getValueOrPeers: u.ErrNotFound\n") + log.Warning("getValueOrPeers: u.ErrNotFound") return nil, nil, u.ErrNotFound } @@ -307,13 +307,13 @@ func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key, for _, pinfo := range peerlist { p, err := dht.ensureConnectedToPeer(pinfo) if err != nil { - u.DErr("getFromPeers error: %s\n", err) + log.Error("getFromPeers error: %s", err) continue } pmes, err := dht.getValueSingle(ctx, p, key, level) if err != nil { - u.DErr("getFromPeers error: %s\n", err) + log.Error("getFromPeers error: %s\n", err) continue } @@ -348,7 +348,7 @@ func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { // Update signals to all routingTables to Update their last-seen status // on the given peer. func (dht *IpfsDHT) Update(p *peer.Peer) { - u.DOut("updating peer: [%s] latency = %f\n", p.ID.Pretty(), p.GetLatency().Seconds()) + log.Debug("updating peer: [%s] latency = %f\n", p.ID.Pretty(), p.GetLatency().Seconds()) removedCount := 0 for _, route := range dht.routingTables { removed := route.Update(p) @@ -404,7 +404,7 @@ func (dht *IpfsDHT) addProviders(key u.Key, peers []*Message_Peer) []*peer.Peer continue } - u.DOut("[%s] adding provider: %s for %s", dht.self.ID.Pretty(), p, key) + log.Debug("[%s] adding provider: %s for %s", dht.self.ID.Pretty(), p, key) // Dont add outselves to the list if p.ID.Equal(dht.self.ID) { @@ -439,7 +439,7 @@ func (dht *IpfsDHT) betterPeerToQuery(pmes *Message) *peer.Peer { // == to self? nil if closer.ID.Equal(dht.self.ID) { - u.DOut("Attempted to return self! this shouldnt happen...\n") + log.Error("Attempted to return self! this shouldnt happen...") return nil } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index c1b85cefa..d1f50afb8 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -86,7 +86,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { return nil, err } - u.DOut("[%s] GetValue %v %v\n", dht.self.ID.Pretty(), key, result.value) + log.Debug("GetValue %v %v", key, result.value) if result.value == nil { return nil, u.ErrNotFound } @@ -189,7 +189,7 @@ func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*Message_Peer, ps *peerSet // FindProviders searches for peers who can provide the value for given key. func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]*peer.Peer, error) { // get closest peer - u.DOut("Find providers for: '%s'\n", key.Pretty()) + log.Debug("Find providers for: '%s'", key.Pretty()) p := dht.routingTables[0].NearestPeer(kb.ConvertKey(key)) if p == nil { return nil, nil @@ -333,17 +333,17 @@ func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (*peer.Pee // Ping a peer, log the time it took func (dht *IpfsDHT) Ping(ctx context.Context, p *peer.Peer) error { // Thoughts: maybe this should accept an ID and do a peer lookup? - u.DOut("[%s] ping %s start\n", dht.self.ID.Pretty(), p.ID.Pretty()) + log.Info("ping %s start", p.ID.Pretty()) pmes := newMessage(Message_PING, "", 0) _, err := dht.sendRequest(ctx, p, pmes) - u.DOut("[%s] ping %s end (err = %s)\n", dht.self.ID.Pretty(), p.ID.Pretty(), err) + log.Info("ping %s end (err = %s)", p.ID.Pretty(), err) return err } func (dht *IpfsDHT) getDiagnostic(ctx context.Context) ([]*diagInfo, error) { - u.DOut("Begin Diagnostic") + log.Info("Begin Diagnostic") peers := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) var out []*diagInfo From ea856c44a64c17338b10888eaeb6e7c3361d1732 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 27 Sep 2014 12:38:32 -0700 Subject: [PATCH 0200/3817] new files and directories appear to work properly This commit was moved from ipfs/go-namesys@c2c453ae44f1bd452496364f594f083d30c571c4 --- namesys/dns.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/dns.go b/namesys/dns.go index 55d00f945..1154a66fe 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -15,7 +15,7 @@ type DNSResolver struct { } func (r *DNSResolver) Matches(name string) bool { - return strings.Contains(name, ".") + return strings.Contains(name, ".") && !strings.HasPrefix(name, ".") } // TXT records for a given domain name should contain a b58 From 7cca48d11c310fb406e0050b81dbab1740dc2d5b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 28 Sep 2014 00:13:07 -0700 Subject: [PATCH 0201/3817] update logging in multiple packages This commit was moved from ipfs/go-blockservice@6f1789fd89fa90862a4e316e619c33365cea8de5 --- blockservice/blockservice.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 1fbbfcb44..51a6e0014 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -7,12 +7,15 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + "github.com/op/go-logging" blocks "github.com/jbenet/go-ipfs/blocks" exchange "github.com/jbenet/go-ipfs/exchange" u "github.com/jbenet/go-ipfs/util" ) +var log = logging.MustGetLogger("blockservice") + // BlockService is a block datastore. // It uses an internal `datastore.Datastore` instance to store values. type BlockService struct { @@ -26,7 +29,7 @@ func NewBlockService(d ds.Datastore, rem exchange.Interface) (*BlockService, err return nil, fmt.Errorf("BlockService requires valid datastore") } if rem == nil { - u.DErr("Caution: blockservice running in local (offline) mode.\n") + log.Warning("blockservice running in local (offline) mode.") } return &BlockService{Datastore: d, Remote: rem}, nil } @@ -35,7 +38,7 @@ func NewBlockService(d ds.Datastore, rem exchange.Interface) (*BlockService, err func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { k := b.Key() dsk := ds.NewKey(string(k)) - u.DOut("storing [%s] in datastore\n", k.Pretty()) + log.Debug("storing [%s] in datastore", k.Pretty()) // TODO(brian): define a block datastore with a Put method which accepts a // block parameter err := s.Datastore.Put(dsk, b.Data) @@ -52,11 +55,11 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { - u.DOut("BlockService GetBlock: '%s'\n", k.Pretty()) + log.Debug("BlockService GetBlock: '%s'", k.Pretty()) dsk := ds.NewKey(string(k)) datai, err := s.Datastore.Get(dsk) if err == nil { - u.DOut("Blockservice: Got data in datastore.\n") + log.Debug("Blockservice: Got data in datastore.") bdata, ok := datai.([]byte) if !ok { return nil, fmt.Errorf("data associated with %s is not a []byte", k) @@ -66,7 +69,7 @@ func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { Data: bdata, }, nil } else if err == ds.ErrNotFound && s.Remote != nil { - u.DOut("Blockservice: Searching bitswap.\n") + log.Debug("Blockservice: Searching bitswap.") ctx, _ := context.WithTimeout(context.TODO(), 5*time.Second) blk, err := s.Remote.Block(ctx, k) if err != nil { @@ -74,7 +77,7 @@ func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { } return blk, nil } else { - u.DOut("Blockservice GetBlock: Not found.\n") + log.Debug("Blockservice GetBlock: Not found.") return nil, u.ErrNotFound } } From 4d8af0295d53c92084eb1447a4539329825f7767 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 27 Sep 2014 12:38:32 -0700 Subject: [PATCH 0202/3817] new files and directories appear to work properly This commit was moved from ipfs/go-merkledag@60d738bccbfc466691b86597870fd9bc52bfefef --- ipld/merkledag/merkledag.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 4c57fb95c..b8e96e465 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -65,6 +65,16 @@ func (n *Node) AddNodeLink(name string, that *Node) error { return nil } +func (n *Node) RemoveNodeLink(name string) error { + for i, l := range n.Links { + if l.Name == name { + n.Links = append(n.Links[:i], n.Links[i+1:]...) + return nil + } + } + return u.ErrNotFound +} + // Size returns the total size of the data addressed by node, // including the total sizes of references. func (n *Node) Size() (uint64, error) { From d50a969a5a94c13f93d5ad9fcf4460b8d6c85884 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 29 Sep 2014 17:47:13 +0000 Subject: [PATCH 0203/3817] implement publisher for ipns to wait until moments of rapid churn die down This commit was moved from ipfs/go-ipfs-routing@04044e46d61913590424886a4763959e9d4435ec --- routing/dht/dht.go | 7 +++---- routing/dht/routing.go | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 4e844ecd6..eccfd7e45 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -177,8 +177,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p *peer.Peer, pmes *Message start := time.Now() // Print out diagnostic - log.Debug("[peer: %s] Sent message type: '%s' [to = %s]\n", - dht.self.ID.Pretty(), + log.Debug("Sent message type: '%s' [to = %s]", Message_MessageType_name[int32(pmes.GetType())], p.ID.Pretty()) rmes, err := dht.sender.SendRequest(ctx, mes) @@ -281,7 +280,7 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, } if len(peers) > 0 { - u.DOut("getValueOrPeers: peers") + log.Debug("getValueOrPeers: peers") return nil, peers, nil } @@ -400,7 +399,7 @@ func (dht *IpfsDHT) addProviders(key u.Key, peers []*Message_Peer) []*peer.Peer for _, prov := range peers { p, err := dht.peerFromInfo(prov) if err != nil { - u.PErr("error getting peer from info: %v\n", err) + log.Error("error getting peer from info: %v", err) continue } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index d1f50afb8..4fa6c8c94 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -18,7 +18,7 @@ import ( // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error { - log.Debug("PutValue %s %v", key.Pretty(), value) + log.Debug("PutValue %s", key.Pretty()) err := dht.putLocal(key, value) if err != nil { return err From 61cbda30b0b01238f818a4a7b0008ee24f187a4f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 1 Oct 2014 00:44:22 -0700 Subject: [PATCH 0204/3817] vendoring ipns things This commit was moved from ipfs/go-namesys@502ca33f131a0ed9668aa62427518576d3391eaa --- namesys/entry.pb.go | 2 +- namesys/proquint.go | 2 +- namesys/publisher.go | 4 ++-- namesys/routing.go | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/namesys/entry.pb.go b/namesys/entry.pb.go index e4420e9f9..d9dc5160b 100644 --- a/namesys/entry.pb.go +++ b/namesys/entry.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package namesys -import proto "code.google.com/p/goprotobuf/proto" +import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. diff --git a/namesys/proquint.go b/namesys/proquint.go index 9b64f3fde..bf34c3b6c 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -3,7 +3,7 @@ package namesys import ( "errors" - proquint "github.com/bren2010/proquint" + proquint "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/bren2010/proquint" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index 73a4197e1..a4292d7fe 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -3,8 +3,8 @@ package namesys import ( "time" - "code.google.com/p/go.net/context" - "code.google.com/p/goprotobuf/proto" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ci "github.com/jbenet/go-ipfs/crypto" mdag "github.com/jbenet/go-ipfs/merkledag" diff --git a/namesys/routing.go b/namesys/routing.go index 605458eef..4c2b0d816 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -3,15 +3,15 @@ package namesys import ( "fmt" - "code.google.com/p/go.net/context" - "code.google.com/p/goprotobuf/proto" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ci "github.com/jbenet/go-ipfs/crypto" mdag "github.com/jbenet/go-ipfs/merkledag" "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" - mh "github.com/jbenet/go-multihash" - "github.com/op/go-logging" + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" ) var log = logging.MustGetLogger("namesys") From f78093f8c22591388b4db486600e24275256fabc Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 1 Oct 2014 00:44:22 -0700 Subject: [PATCH 0205/3817] vendoring ipns things This commit was moved from ipfs/go-ipfs-routing@60e0ad063fb2d82da18dc5c398d501775b77b7fb --- routing/dht/dht.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index eccfd7e45..f02be4711 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -13,7 +13,7 @@ import ( peer "github.com/jbenet/go-ipfs/peer" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" - "github.com/op/go-logging" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" From 574776c9aebae9dbb24833e2dbcaaee73d5dda30 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 1 Oct 2014 00:44:22 -0700 Subject: [PATCH 0206/3817] vendoring ipns things This commit was moved from ipfs/go-blockservice@dbe004fac140bcec450b1af039c4a6f34e09c00e --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 51a6e0014..36c3ed607 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -7,7 +7,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - "github.com/op/go-logging" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" blocks "github.com/jbenet/go-ipfs/blocks" exchange "github.com/jbenet/go-ipfs/exchange" From c4b96ee05d542eb2aff5c57822b15262c5932e77 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 1 Oct 2014 00:44:22 -0700 Subject: [PATCH 0207/3817] vendoring ipns things This commit was moved from ipfs/go-path@50b3b95b4e1cfe7fd8f8065905344463e74b3823 --- path/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/path.go b/path/path.go index 533afc7cd..ebc9d81d6 100644 --- a/path/path.go +++ b/path/path.go @@ -8,7 +8,7 @@ import ( mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" merkledag "github.com/jbenet/go-ipfs/merkledag" u "github.com/jbenet/go-ipfs/util" - "github.com/op/go-logging" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" ) var log = logging.MustGetLogger("path") From 07b7038ab3fa90195158ae677e25e7f240e5a452 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 1 Oct 2014 01:36:21 -0700 Subject: [PATCH 0208/3817] IpnsPublicher -> Publisher interface This commit was moved from ipfs/go-namesys@4c087f14446f9c5b5a8a4575ab3a56b85596a81e --- namesys/publisher.go | 12 ++++++++---- namesys/resolve_test.go | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index a4292d7fe..76eb6a55b 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -12,20 +12,24 @@ import ( u "github.com/jbenet/go-ipfs/util" ) -type IpnsPublisher struct { +type ipnsPublisher struct { dag *mdag.DAGService routing routing.IpfsRouting } -func NewPublisher(dag *mdag.DAGService, route routing.IpfsRouting) *IpnsPublisher { - return &IpnsPublisher{ +type Publisher interface { + Publish(ci.PrivKey, string) error +} + +func NewPublisher(dag *mdag.DAGService, route routing.IpfsRouting) Publisher { + return &ipnsPublisher{ dag: dag, routing: route, } } // Publish accepts a keypair and a value, -func (p *IpnsPublisher) Publish(k ci.PrivKey, value string) error { +func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { log.Debug("namesys: Publish %s", value) ctx := context.TODO() data, err := CreateEntryData(k, value) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 7cad8bef0..35898b50f 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -28,7 +28,7 @@ func TestRoutingResolve(t *testing.T) { resolve := NewMasterResolver(d, dag) - pub := IpnsPublisher{ + pub := ipnsPublisher{ dag: dag, routing: d, } From b5c0812f8b5762901f4bf275573dc85771c21dfd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 3 Oct 2014 19:22:54 +0000 Subject: [PATCH 0209/3817] fixing mutability issues in ipns This commit was moved from ipfs/go-merkledag@b32dcdecf01b6c32d50d6c1ecfaa46fff52bbd02 --- ipld/merkledag/merkledag.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index b8e96e465..31f4e2937 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -75,6 +75,16 @@ func (n *Node) RemoveNodeLink(name string) error { return u.ErrNotFound } +func (n *Node) Copy() *Node { + nnode := new(Node) + nnode.Data = make([]byte, len(n.Data)) + copy(nnode.Data, n.Data) + + nnode.Links = make([]*Link, len(n.Links)) + copy(nnode.Links, n.Links) + return nnode +} + // Size returns the total size of the data addressed by node, // including the total sizes of references. func (n *Node) Size() (uint64, error) { From f0432059093fb058f61bdfac315ce66a88663c94 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 3 Oct 2014 15:34:08 -0700 Subject: [PATCH 0210/3817] use string datastore keys. This commit was moved from ipfs/go-ipfs-routing@649624f0d25346d8c87a8dc66d8778e6ec7620cb --- routing/dht/dht.go | 16 +++++++++++----- routing/dht/handlers.go | 7 ++++--- routing/mock/routing.go | 4 ++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index f02be4711..aa7361e53 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -13,11 +13,11 @@ import ( peer "github.com/jbenet/go-ipfs/peer" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ) @@ -328,7 +328,7 @@ func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key, func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { dht.dslock.Lock() defer dht.dslock.Unlock() - v, err := dht.datastore.Get(ds.NewKey(string(key))) + v, err := dht.datastore.Get(key.DsKey()) if err != nil { return nil, err } @@ -341,7 +341,7 @@ func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { } func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { - return dht.datastore.Put(ds.NewKey(string(key)), value) + return dht.datastore.Put(key.DsKey(), value) } // Update signals to all routingTables to Update their last-seen status @@ -494,13 +494,19 @@ func (dht *IpfsDHT) ensureConnectedToPeer(pbp *Message_Peer) (*peer.Peer, error) return p, err } +//TODO: this should be smarter about which keys it selects. func (dht *IpfsDHT) loadProvidableKeys() error { kl, err := dht.datastore.KeyList() if err != nil { return err } - for _, k := range kl { - dht.providers.AddProvider(u.Key(k.Bytes()), dht.self) + for _, dsk := range kl { + k := u.KeyFromDsKey(dsk) + if len(k) == 0 { + log.Error("loadProvidableKeys error: %v", dsk) + } + + dht.providers.AddProvider(k, dht.self) } return nil } diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 4301d1e4e..ac03ed3e8 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -51,7 +51,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error // let's first check if we have the value locally. u.DOut("[%s] handleGetValue looking into ds\n", dht.self.ID.Pretty()) - dskey := ds.NewKey(pmes.GetKey()) + dskey := u.Key(pmes.GetKey()).DsKey() iVal, err := dht.datastore.Get(dskey) u.DOut("[%s] handleGetValue looking into ds GOT %v\n", dht.self.ID.Pretty(), iVal) @@ -96,7 +96,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *Message) (*Message, error) { dht.dslock.Lock() defer dht.dslock.Unlock() - dskey := ds.NewKey(pmes.GetKey()) + dskey := u.Key(pmes.GetKey()).DsKey() err := dht.datastore.Put(dskey, pmes.GetValue()) u.DOut("[%s] handlePutValue %v %v\n", dht.self.ID.Pretty(), dskey, pmes.GetValue()) return pmes, err @@ -137,7 +137,8 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *Message) (*Message, e resp := newMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) // check if we have this value, to add ourselves as provider. - has, err := dht.datastore.Has(ds.NewKey(pmes.GetKey())) + dsk := u.Key(pmes.GetKey()).DsKey() + has, err := dht.datastore.Has(dsk) if err != nil && err != ds.ErrNotFound { u.PErr("unexpected datastore error: %v\n", err) has = false diff --git a/routing/mock/routing.go b/routing/mock/routing.go index e5fdb96fc..954914c3b 100644 --- a/routing/mock/routing.go +++ b/routing/mock/routing.go @@ -33,11 +33,11 @@ func (mr *MockRouter) SetRoutingServer(rs RoutingServer) { } func (mr *MockRouter) PutValue(ctx context.Context, key u.Key, val []byte) error { - return mr.datastore.Put(ds.NewKey(string(key)), val) + return mr.datastore.Put(key.DsKey(), val) } func (mr *MockRouter) GetValue(ctx context.Context, key u.Key) ([]byte, error) { - v, err := mr.datastore.Get(ds.NewKey(string(key))) + v, err := mr.datastore.Get(key.DsKey()) if err != nil { return nil, err } From 39f67332e345eced7c66dbb11982babb7778ec38 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 3 Oct 2014 15:34:08 -0700 Subject: [PATCH 0211/3817] use string datastore keys. This commit was moved from ipfs/go-blockservice@aecb4cb77199ca3fc48fed324221a751e253effe --- blockservice/blockservice.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 36c3ed607..369cacc2a 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -7,7 +7,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" + logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" blocks "github.com/jbenet/go-ipfs/blocks" exchange "github.com/jbenet/go-ipfs/exchange" @@ -37,11 +37,10 @@ func NewBlockService(d ds.Datastore, rem exchange.Interface) (*BlockService, err // AddBlock adds a particular block to the service, Putting it into the datastore. func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { k := b.Key() - dsk := ds.NewKey(string(k)) log.Debug("storing [%s] in datastore", k.Pretty()) // TODO(brian): define a block datastore with a Put method which accepts a // block parameter - err := s.Datastore.Put(dsk, b.Data) + err := s.Datastore.Put(k.DsKey(), b.Data) if err != nil { return k, err } @@ -56,8 +55,7 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { // Getting it from the datastore using the key (hash). func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { log.Debug("BlockService GetBlock: '%s'", k.Pretty()) - dsk := ds.NewKey(string(k)) - datai, err := s.Datastore.Get(dsk) + datai, err := s.Datastore.Get(k.DsKey()) if err == nil { log.Debug("Blockservice: Got data in datastore.") bdata, ok := datai.([]byte) From c3d993e9ab5a01e6963b69d729f8926ef5451746 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 3 Oct 2014 23:50:05 +0000 Subject: [PATCH 0212/3817] a little more progress... and some debugging code This commit was moved from ipfs/go-merkledag@40be065ad1a5883228f4fa11b6a01d0618d7ae53 --- ipld/merkledag/merkledag.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 31f4e2937..e51a6e3c4 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -46,6 +46,14 @@ type Link struct { // AddNodeLink adds a link to another node. func (n *Node) AddNodeLink(name string, that *Node) error { + // DEBUG CODE + for _, l := range n.Links { + if l.Name == name { + panic("Trying to add child that already exists!") + } + } + // + s, err := that.Size() if err != nil { return err From 7cedf9de8781eadadb83da8423d47c29a3077eb3 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 3 Oct 2014 18:08:21 -0700 Subject: [PATCH 0213/3817] DNSResolver: use isd.IsDomain this commit dedicated to @whyrusleeping This commit was moved from ipfs/go-namesys@e6ce1e383d8e45216a61ef65df72ad2d41da5d30 --- namesys/dns.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 1154a66fe..e9c4097d8 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -2,25 +2,29 @@ package namesys import ( "net" - "strings" b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" u "github.com/jbenet/go-ipfs/util" + isd "github.com/jbenet/go-is-domain" ) +// DNSResolver implements a Resolver on DNS domains type DNSResolver struct { // TODO: maybe some sort of caching? // cache would need a timeout } +// Matches implements Resolver func (r *DNSResolver) Matches(name string) bool { - return strings.Contains(name, ".") && !strings.HasPrefix(name, ".") + return isd.IsDomain(name) } +// Resolve implements Resolver // TXT records for a given domain name should contain a b58 // encoded multihash. func (r *DNSResolver) Resolve(name string) (string, error) { + log.Info("DNSResolver resolving %v", name) txt, err := net.LookupTXT(name) if err != nil { return "", err From bbcab0de5fc29eb5b9f1f085b8e462c5e3c3855c Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 4 Oct 2014 03:36:30 -0700 Subject: [PATCH 0214/3817] initialize loggers at ERROR This commit was moved from ipfs/go-ipfs-routing@7416ccbaaf23e6ff93ba4e2b5ee83968a4cd31ae --- routing/dht/dht.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index aa7361e53..d45c5aabf 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -22,7 +22,7 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ) -var log = logging.MustGetLogger("dht") +var log = u.Logger("dht", logging.ERROR) // TODO. SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-dht/index.js From 59ac8c3795b79a1317260cf174ad4c7c307d8e22 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 4 Oct 2014 03:36:30 -0700 Subject: [PATCH 0215/3817] initialize loggers at ERROR This commit was moved from ipfs/go-merkledag@c1ee9f4efb356cd5d1e7ac5ef84a0968ed610903 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index e51a6e3c4..c5db9e00f 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -12,7 +12,7 @@ import ( u "github.com/jbenet/go-ipfs/util" ) -var log = logging.MustGetLogger("merkledag") +var log = u.Logger("merkledag", logging.ERROR) // NodeMap maps u.Keys to Nodes. // We cannot use []byte/Multihash for keys :( From 057b0591d83e73a6518c7ff194dd9fbdfbde4a65 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 4 Oct 2014 03:36:30 -0700 Subject: [PATCH 0216/3817] initialize loggers at ERROR This commit was moved from ipfs/go-namesys@90964c306ee5d53629876d77a385800a687de521 --- namesys/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/routing.go b/namesys/routing.go index 4c2b0d816..ba7c4a723 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -14,7 +14,7 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" ) -var log = logging.MustGetLogger("namesys") +var log = u.Logger("namesys", logging.ERROR) // RoutingResolver implements NSResolver for the main IPFS SFS-like naming type RoutingResolver struct { From 2589bfab8a755014bd52c909ecfbb61dde1a46fc Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 4 Oct 2014 03:36:30 -0700 Subject: [PATCH 0217/3817] initialize loggers at ERROR This commit was moved from ipfs/go-blockservice@ad3eb746293e21bf65ecb51569cd6644f453876f --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 369cacc2a..6970f363b 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -14,7 +14,7 @@ import ( u "github.com/jbenet/go-ipfs/util" ) -var log = logging.MustGetLogger("blockservice") +var log = u.Logger("blockservice", logging.ERROR) // BlockService is a block datastore. // It uses an internal `datastore.Datastore` instance to store values. From 16e510978daa4d7aaaff961eae6c4d3e1a402962 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 4 Oct 2014 03:36:30 -0700 Subject: [PATCH 0218/3817] initialize loggers at ERROR This commit was moved from ipfs/go-path@c94c35dc884b1a103acd51c0bbd7e63a4e0d1baf --- path/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/path.go b/path/path.go index ebc9d81d6..83d1aa8b6 100644 --- a/path/path.go +++ b/path/path.go @@ -11,7 +11,7 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" ) -var log = logging.MustGetLogger("path") +var log = u.Logger("path", logging.ERROR) // Resolver provides path resolution to IPFS // It has a pointer to a DAGService, which is uses to resolve nodes. From b1172c469d9512feb9f2414a9cef2c4b9e249d5c Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 4 Oct 2014 03:53:21 -0700 Subject: [PATCH 0219/3817] loggers: set level This commit was moved from ipfs/go-ipfs-routing@c77a7071fe4301ede5b25d4eea4429ef60c131cf --- routing/dht/dht.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index d45c5aabf..8cf3a1153 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -17,12 +17,11 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ) -var log = u.Logger("dht", logging.ERROR) +var log = u.Logger("dht") // TODO. SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-dht/index.js From f7b0434ef5b5929284733e4f07496b9be4dac87a Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 4 Oct 2014 03:53:21 -0700 Subject: [PATCH 0220/3817] loggers: set level This commit was moved from ipfs/go-merkledag@e92bbfbf04d7f0e62d872538e534eaee3cf89776 --- ipld/merkledag/merkledag.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index c5db9e00f..076128155 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -4,7 +4,6 @@ import ( "fmt" proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" blocks "github.com/jbenet/go-ipfs/blocks" @@ -12,7 +11,7 @@ import ( u "github.com/jbenet/go-ipfs/util" ) -var log = u.Logger("merkledag", logging.ERROR) +var log = u.Logger("merkledag") // NodeMap maps u.Keys to Nodes. // We cannot use []byte/Multihash for keys :( From a8f0e067724734be0b3199e9ae4d308738c8233d Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 4 Oct 2014 03:53:21 -0700 Subject: [PATCH 0221/3817] loggers: set level This commit was moved from ipfs/go-namesys@ab3dab3ec3c42f9e80146e0b1385dab5c3dd7bf7 --- namesys/routing.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/namesys/routing.go b/namesys/routing.go index ba7c4a723..942263e8e 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,15 +6,14 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" ci "github.com/jbenet/go-ipfs/crypto" mdag "github.com/jbenet/go-ipfs/merkledag" "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" ) -var log = u.Logger("namesys", logging.ERROR) +var log = u.Logger("namesys") // RoutingResolver implements NSResolver for the main IPFS SFS-like naming type RoutingResolver struct { From 023d733068b1108f7b5462d68c49e7c0783d5a16 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 4 Oct 2014 03:53:21 -0700 Subject: [PATCH 0222/3817] loggers: set level This commit was moved from ipfs/go-blockservice@413d09dcad8726d913cf32efca24a2f7b01d8538 --- blockservice/blockservice.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 6970f363b..21bb75c6f 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -7,14 +7,13 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" blocks "github.com/jbenet/go-ipfs/blocks" exchange "github.com/jbenet/go-ipfs/exchange" u "github.com/jbenet/go-ipfs/util" ) -var log = u.Logger("blockservice", logging.ERROR) +var log = u.Logger("blockservice") // BlockService is a block datastore. // It uses an internal `datastore.Datastore` instance to store values. From 70467ab790a8267ad9c2472ec7a2382868e1bc26 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 4 Oct 2014 03:53:21 -0700 Subject: [PATCH 0223/3817] loggers: set level This commit was moved from ipfs/go-path@4b44c90209f34e8a24ae631a237083600df952c3 --- path/path.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/path/path.go b/path/path.go index 83d1aa8b6..239203140 100644 --- a/path/path.go +++ b/path/path.go @@ -8,10 +8,9 @@ import ( mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" merkledag "github.com/jbenet/go-ipfs/merkledag" u "github.com/jbenet/go-ipfs/util" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" ) -var log = u.Logger("path", logging.ERROR) +var log = u.Logger("path") // Resolver provides path resolution to IPFS // It has a pointer to a DAGService, which is uses to resolve nodes. From 8102d08e92d8c6605348b8e1162522030f10407b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 4 Oct 2014 19:29:08 +0000 Subject: [PATCH 0224/3817] fixed keyspace tests on 32 bit systems This commit was moved from ipfs/go-ipfs-routing@b445ef47b07684f7cf155127522cc0eeb809e3a2 --- routing/keyspace/xor_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/keyspace/xor_test.go b/routing/keyspace/xor_test.go index d7d83afa2..7963ea014 100644 --- a/routing/keyspace/xor_test.go +++ b/routing/keyspace/xor_test.go @@ -113,8 +113,8 @@ func TestDistancesAndCenterSorting(t *testing.T) { keys[i] = Key{Space: XORKeySpace, Bytes: a} } - cmp := func(a int, b *big.Int) int { - return big.NewInt(int64(a)).Cmp(b) + cmp := func(a int64, b *big.Int) int { + return big.NewInt(a).Cmp(b) } if 0 != cmp(0, keys[2].Distance(keys[3])) { From f8d4cfe066f1a39ac7c67737730133014006e8f2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 5 Oct 2014 01:22:01 +0000 Subject: [PATCH 0225/3817] fixed data size reporting This commit was moved from ipfs/go-merkledag@cc425aefc304ddd3164e66f003fa4c54745c42a4 --- ipld/merkledag/data.pb.go | 10 +++++++++- ipld/merkledag/data.proto | 1 + ipld/merkledag/merkledag.go | 23 ++++++++++++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/data.pb.go b/ipld/merkledag/data.pb.go index d2f97d33f..3ed887533 100644 --- a/ipld/merkledag/data.pb.go +++ b/ipld/merkledag/data.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package merkledag -import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" +import proto "code.google.com/p/goprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. @@ -59,6 +59,7 @@ func (x *PBData_DataType) UnmarshalJSON(data []byte) error { type PBData struct { Type *PBData_DataType `protobuf:"varint,1,req,enum=merkledag.PBData_DataType" json:"Type,omitempty"` Data []byte `protobuf:"bytes,2,opt" json:"Data,omitempty"` + Filesize *uint64 `protobuf:"varint,3,opt,name=filesize" json:"filesize,omitempty"` XXX_unrecognized []byte `json:"-"` } @@ -80,6 +81,13 @@ func (m *PBData) GetData() []byte { return nil } +func (m *PBData) GetFilesize() uint64 { + if m != nil && m.Filesize != nil { + return *m.Filesize + } + return 0 +} + func init() { proto.RegisterEnum("merkledag.PBData_DataType", PBData_DataType_name, PBData_DataType_value) } diff --git a/ipld/merkledag/data.proto b/ipld/merkledag/data.proto index 99c8a224b..043c9d1f9 100644 --- a/ipld/merkledag/data.proto +++ b/ipld/merkledag/data.proto @@ -9,4 +9,5 @@ message PBData { required DataType Type = 1; optional bytes Data = 2; + optional uint64 filesize = 3; } diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 076128155..0db22d31c 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -1,6 +1,7 @@ package merkledag import ( + "errors" "fmt" proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" @@ -107,6 +108,25 @@ func (n *Node) Size() (uint64, error) { return s, nil } +func (n *Node) DataSize() (uint64, error) { + pbdata := new(PBData) + err := proto.Unmarshal(n.Data, pbdata) + if err != nil { + return 0, err + } + + switch pbdata.GetType() { + case PBData_Directory: + return 0, errors.New("Cant get data size of directory!") + case PBData_File: + return pbdata.GetFilesize(), nil + case PBData_Raw: + return uint64(len(pbdata.GetData())), nil + default: + return 0, errors.New("Unrecognized node data type!") + } +} + // Multihash hashes the encoded data of this node. func (n *Node) Multihash() (mh.Multihash, error) { b, err := n.Encoded(false) @@ -211,11 +231,12 @@ func (n *DAGService) Get(k u.Key) (*Node, error) { return Decoded(b.Data) } -func FilePBData(data []byte) []byte { +func FilePBData(data []byte, totalsize uint64) []byte { pbfile := new(PBData) typ := PBData_File pbfile.Type = &typ pbfile.Data = data + pbfile.Filesize = proto.Uint64(totalsize) data, err := proto.Marshal(pbfile) if err != nil { From 914b19638d516b26dd9a16ea0d468f95ad892fda Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 5 Oct 2014 15:15:49 -0700 Subject: [PATCH 0226/3817] vendoring protobuf + go-is-domain This commit was moved from ipfs/go-merkledag@668fa11145ba68062d6e642babc166d856fc8f9d --- ipld/merkledag/data.pb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/data.pb.go b/ipld/merkledag/data.pb.go index 3ed887533..bd098edf2 100644 --- a/ipld/merkledag/data.pb.go +++ b/ipld/merkledag/data.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package merkledag -import proto "code.google.com/p/goprotobuf/proto" +import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. From 66e25981dfdaf71c478b9adc8e1b3d5ca4c75136 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 5 Oct 2014 15:15:49 -0700 Subject: [PATCH 0227/3817] vendoring protobuf + go-is-domain This commit was moved from ipfs/go-namesys@2fde043fb1e3275d218293b33d1a0bed79d0fa52 --- namesys/dns.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/dns.go b/namesys/dns.go index e9c4097d8..8dda6cb51 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -6,7 +6,7 @@ import ( b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" u "github.com/jbenet/go-ipfs/util" - isd "github.com/jbenet/go-is-domain" + isd "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-is-domain" ) // DNSResolver implements a Resolver on DNS domains From e16bddfd33264c0f9785225107ebea514cfe477b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 6 Oct 2014 03:42:59 +0000 Subject: [PATCH 0228/3817] working on dag modification structures, factored out the data format into an importer subpackage and added more ipns tests This commit was moved from ipfs/go-merkledag@c9bbb665bb701a842cd4b40cc53b9855b51f5090 --- ipld/merkledag/Makefile | 5 +- ipld/merkledag/dagreader.go | 17 +++--- ipld/merkledag/data.pb.go | 93 ------------------------------ ipld/merkledag/data.proto | 13 ----- ipld/merkledag/merkledag.go | 97 ++++++++------------------------ ipld/merkledag/merkledag_test.go | 3 +- 6 files changed, 37 insertions(+), 191 deletions(-) delete mode 100644 ipld/merkledag/data.pb.go delete mode 100644 ipld/merkledag/data.proto diff --git a/ipld/merkledag/Makefile b/ipld/merkledag/Makefile index 2524ed3ba..711f34bda 100644 --- a/ipld/merkledag/Makefile +++ b/ipld/merkledag/Makefile @@ -1,11 +1,8 @@ -all: node.pb.go data.pb.go +all: node.pb.go node.pb.go: node.proto protoc --gogo_out=. --proto_path=../../../../:/usr/local/opt/protobuf/include:. $< -data.pb.go: data.proto - protoc --go_out=. data.proto - clean: rm node.pb.go diff --git a/ipld/merkledag/dagreader.go b/ipld/merkledag/dagreader.go index 1e8a0c8b9..badc661fd 100644 --- a/ipld/merkledag/dagreader.go +++ b/ipld/merkledag/dagreader.go @@ -6,6 +6,7 @@ import ( "io" proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + ft "github.com/jbenet/go-ipfs/importer/format" u "github.com/jbenet/go-ipfs/util" ) @@ -20,21 +21,21 @@ type DagReader struct { } func NewDagReader(n *Node, serv *DAGService) (io.Reader, error) { - pb := new(PBData) + pb := new(ft.PBData) err := proto.Unmarshal(n.Data, pb) if err != nil { return nil, err } switch pb.GetType() { - case PBData_Directory: + case ft.PBData_Directory: return nil, ErrIsDir - case PBData_File: + case ft.PBData_File: return &DagReader{ node: n, serv: serv, buf: bytes.NewBuffer(pb.GetData()), }, nil - case PBData_Raw: + case ft.PBData_Raw: return bytes.NewBuffer(pb.GetData()), nil default: panic("Unrecognized node type!") @@ -54,7 +55,7 @@ func (dr *DagReader) precalcNextBuf() error { } nxt = nxtNode } - pb := new(PBData) + pb := new(ft.PBData) err := proto.Unmarshal(nxt.Data, pb) if err != nil { return err @@ -62,13 +63,13 @@ func (dr *DagReader) precalcNextBuf() error { dr.position++ switch pb.GetType() { - case PBData_Directory: + case ft.PBData_Directory: panic("Why is there a directory under a file?") - case PBData_File: + case ft.PBData_File: //TODO: this *should* work, needs testing first //return NewDagReader(nxt, dr.serv) panic("Not yet handling different layers of indirection!") - case PBData_Raw: + case ft.PBData_Raw: dr.buf = bytes.NewBuffer(pb.GetData()) return nil default: diff --git a/ipld/merkledag/data.pb.go b/ipld/merkledag/data.pb.go deleted file mode 100644 index 3ed887533..000000000 --- a/ipld/merkledag/data.pb.go +++ /dev/null @@ -1,93 +0,0 @@ -// Code generated by protoc-gen-go. -// source: data.proto -// DO NOT EDIT! - -/* -Package merkledag is a generated protocol buffer package. - -It is generated from these files: - data.proto - -It has these top-level messages: - PBData -*/ -package merkledag - -import proto "code.google.com/p/goprotobuf/proto" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = math.Inf - -type PBData_DataType int32 - -const ( - PBData_Raw PBData_DataType = 0 - PBData_Directory PBData_DataType = 1 - PBData_File PBData_DataType = 2 -) - -var PBData_DataType_name = map[int32]string{ - 0: "Raw", - 1: "Directory", - 2: "File", -} -var PBData_DataType_value = map[string]int32{ - "Raw": 0, - "Directory": 1, - "File": 2, -} - -func (x PBData_DataType) Enum() *PBData_DataType { - p := new(PBData_DataType) - *p = x - return p -} -func (x PBData_DataType) String() string { - return proto.EnumName(PBData_DataType_name, int32(x)) -} -func (x *PBData_DataType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(PBData_DataType_value, data, "PBData_DataType") - if err != nil { - return err - } - *x = PBData_DataType(value) - return nil -} - -type PBData struct { - Type *PBData_DataType `protobuf:"varint,1,req,enum=merkledag.PBData_DataType" json:"Type,omitempty"` - Data []byte `protobuf:"bytes,2,opt" json:"Data,omitempty"` - Filesize *uint64 `protobuf:"varint,3,opt,name=filesize" json:"filesize,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *PBData) Reset() { *m = PBData{} } -func (m *PBData) String() string { return proto.CompactTextString(m) } -func (*PBData) ProtoMessage() {} - -func (m *PBData) GetType() PBData_DataType { - if m != nil && m.Type != nil { - return *m.Type - } - return PBData_Raw -} - -func (m *PBData) GetData() []byte { - if m != nil { - return m.Data - } - return nil -} - -func (m *PBData) GetFilesize() uint64 { - if m != nil && m.Filesize != nil { - return *m.Filesize - } - return 0 -} - -func init() { - proto.RegisterEnum("merkledag.PBData_DataType", PBData_DataType_name, PBData_DataType_value) -} diff --git a/ipld/merkledag/data.proto b/ipld/merkledag/data.proto deleted file mode 100644 index 043c9d1f9..000000000 --- a/ipld/merkledag/data.proto +++ /dev/null @@ -1,13 +0,0 @@ -package merkledag; - -message PBData { - enum DataType { - Raw = 0; - Directory = 1; - File = 2; - } - - required DataType Type = 1; - optional bytes Data = 2; - optional uint64 filesize = 3; -} diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 0db22d31c..675c8ccdd 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -1,11 +1,8 @@ package merkledag import ( - "errors" "fmt" - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" blocks "github.com/jbenet/go-ipfs/blocks" bserv "github.com/jbenet/go-ipfs/blockservice" @@ -37,6 +34,9 @@ type Link struct { // cumulative size of target object Size uint64 + // cumulative size of data stored in object + DataSize uint64 + // multihash of the target object Hash mh.Multihash @@ -46,14 +46,28 @@ type Link struct { // AddNodeLink adds a link to another node. func (n *Node) AddNodeLink(name string, that *Node) error { - // DEBUG CODE - for _, l := range n.Links { - if l.Name == name { - panic("Trying to add child that already exists!") - } + s, err := that.Size() + if err != nil { + return err } - // + h, err := that.Multihash() + if err != nil { + return err + } + + n.Links = append(n.Links, &Link{ + Name: name, + Size: s, + Hash: h, + Node: that, + }) + return nil +} + +// AddNodeLink adds a link to another node. without keeping a reference to +// the child node +func (n *Node) AddNodeLinkClean(name string, that *Node) error { s, err := that.Size() if err != nil { return err @@ -68,7 +82,6 @@ func (n *Node) AddNodeLink(name string, that *Node) error { Name: name, Size: s, Hash: h, - Node: that, }) return nil } @@ -83,6 +96,8 @@ func (n *Node) RemoveNodeLink(name string) error { return u.ErrNotFound } +// Copy returns a copy of the node. +// NOTE: does not make copies of Node objects in the links. func (n *Node) Copy() *Node { nnode := new(Node) nnode.Data = make([]byte, len(n.Data)) @@ -108,25 +123,6 @@ func (n *Node) Size() (uint64, error) { return s, nil } -func (n *Node) DataSize() (uint64, error) { - pbdata := new(PBData) - err := proto.Unmarshal(n.Data, pbdata) - if err != nil { - return 0, err - } - - switch pbdata.GetType() { - case PBData_Directory: - return 0, errors.New("Cant get data size of directory!") - case PBData_File: - return pbdata.GetFilesize(), nil - case PBData_Raw: - return uint64(len(pbdata.GetData())), nil - default: - return 0, errors.New("Unrecognized node data type!") - } -} - // Multihash hashes the encoded data of this node. func (n *Node) Multihash() (mh.Multihash, error) { b, err := n.Encoded(false) @@ -230,46 +226,3 @@ func (n *DAGService) Get(k u.Key) (*Node, error) { return Decoded(b.Data) } - -func FilePBData(data []byte, totalsize uint64) []byte { - pbfile := new(PBData) - typ := PBData_File - pbfile.Type = &typ - pbfile.Data = data - pbfile.Filesize = proto.Uint64(totalsize) - - data, err := proto.Marshal(pbfile) - if err != nil { - //this really shouldnt happen, i promise - panic(err) - } - return data -} - -func FolderPBData() []byte { - pbfile := new(PBData) - typ := PBData_Directory - pbfile.Type = &typ - - data, err := proto.Marshal(pbfile) - if err != nil { - //this really shouldnt happen, i promise - panic(err) - } - return data -} - -func WrapData(b []byte) []byte { - pbdata := new(PBData) - typ := PBData_Raw - pbdata.Data = b - pbdata.Type = &typ - - out, err := proto.Marshal(pbdata) - if err != nil { - // This shouldnt happen. seriously. - panic(err) - } - - return out -} diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 7cd1649e2..2db166beb 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -2,8 +2,9 @@ package merkledag import ( "fmt" - u "github.com/jbenet/go-ipfs/util" "testing" + + u "github.com/jbenet/go-ipfs/util" ) func TestNode(t *testing.T) { From 993f49c24c7b21d0f03784d57bc0b8985edea758 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 6 Oct 2014 02:26:50 -0700 Subject: [PATCH 0229/3817] u.Hash - error the u.Hash error can be safely ignored (panic) because multihash only fails from the selection of hash function. If the fn + length are valid, it won't error. cc @whyrusleeping This commit was moved from ipfs/go-blockservice@7abc7289a26040e1856384206db29d66c7075e7e --- blockservice/blocks_test.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index c610fbd2a..bfffc37b8 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -23,12 +23,7 @@ func TestBlocks(t *testing.T) { return } - h, err := u.Hash([]byte("beep boop")) - if err != nil { - t.Error("failed to hash data", err) - return - } - + h := u.Hash([]byte("beep boop")) if !bytes.Equal(b.Multihash, h) { t.Error("Block Multihash and data multihash not equal") } From e7e9ae2c37d7688999fc6aa843fa656e2330f1d0 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 6 Oct 2014 02:26:50 -0700 Subject: [PATCH 0230/3817] u.Hash - error the u.Hash error can be safely ignored (panic) because multihash only fails from the selection of hash function. If the fn + length are valid, it won't error. cc @whyrusleeping This commit was moved from ipfs/go-merkledag@ccae0acd94a5c847099876ca18a8b0dac650200a --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 675c8ccdd..e7c13873c 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -130,7 +130,7 @@ func (n *Node) Multihash() (mh.Multihash, error) { return nil, err } - return u.Hash(b) + return u.Hash(b), nil } // Key returns the Multihash as a key, for maps. From 77520b9296cb9bcf43b50c6054785848aced4f0a Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 6 Oct 2014 02:26:50 -0700 Subject: [PATCH 0231/3817] u.Hash - error the u.Hash error can be safely ignored (panic) because multihash only fails from the selection of hash function. If the fn + length are valid, it won't error. cc @whyrusleeping This commit was moved from ipfs/go-namesys@2323ba4fb0827bbc8c5d69f732efcb4d08b4d4b4 --- namesys/publisher.go | 11 ++--------- namesys/resolve_test.go | 6 +----- namesys/routing.go | 5 +---- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 76eb6a55b..0c605301c 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -42,16 +42,9 @@ func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { return nil } - nameb, err := u.Hash(pkbytes) - if err != nil { - return nil - } + nameb := u.Hash(pkbytes) namekey := u.Key(nameb).Pretty() - - ipnskey, err := u.Hash([]byte("/ipns/" + namekey)) - if err != nil { - return err - } + ipnskey := u.Hash([]byte("/ipns/" + namekey)) // Store associated public key timectx, _ := context.WithDeadline(ctx, time.Now().Add(time.Second*4)) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 35898b50f..ff5292224 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -48,11 +48,7 @@ func TestRoutingResolve(t *testing.T) { t.Fatal(err) } - pkhash, err := u.Hash(pubkb) - if err != nil { - t.Fatal(err) - } - + pkhash := u.Hash(pubkb) res, err := resolve.Resolve(u.Key(pkhash).Pretty()) if err != nil { t.Fatal(err) diff --git a/namesys/routing.go b/namesys/routing.go index 942263e8e..abacb22d4 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -45,10 +45,7 @@ func (r *RoutingResolver) Resolve(name string) (string, error) { // use the routing system to get the name. // /ipns/ - h, err := u.Hash([]byte("/ipns/" + name)) - if err != nil { - return "", err - } + h := u.Hash([]byte("/ipns/" + name)) ipnsKey := u.Key(h) val, err := r.routing.GetValue(ctx, ipnsKey) From f904c76b7714d313583168788d05e6286cbaef05 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 6 Oct 2014 04:13:39 -0700 Subject: [PATCH 0232/3817] updated multiaddr use across codebase This commit was moved from ipfs/go-ipfs-routing@108e5e76eb278164ce4adcb416e4638f2427a1a3 --- routing/dht/Message.go | 6 +----- routing/dht/dht_test.go | 6 +++--- routing/dht/ext_test.go | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/routing/dht/Message.go b/routing/dht/Message.go index 1be9a3b80..84d323c37 100644 --- a/routing/dht/Message.go +++ b/routing/dht/Message.go @@ -20,11 +20,7 @@ func peerToPBPeer(p *peer.Peer) *Message_Peer { if len(p.Addresses) == 0 || p.Addresses[0] == nil { pbp.Addr = proto.String("") } else { - addr, err := p.Addresses[0].String() - if err != nil { - //Temp: what situations could cause this? - panic(err) - } + addr := p.Addresses[0].String() pbp.Addr = &addr } pid := string(p.ID) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 1bbc62cdc..f5d391387 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -43,8 +43,8 @@ func setupDHT(t *testing.T, p *peer.Peer) *IpfsDHT { return d } -func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) { - var addrs []*ma.Multiaddr +func setupDHTS(n int, t *testing.T) ([]ma.Multiaddr, []*peer.Peer, []*IpfsDHT) { + var addrs []ma.Multiaddr for i := 0; i < n; i++ { a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) if err != nil { @@ -67,7 +67,7 @@ func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) return addrs, peers, dhts } -func makePeer(addr *ma.Multiaddr) *peer.Peer { +func makePeer(addr ma.Multiaddr) *peer.Peer { p := new(peer.Peer) p.AddAddress(addr) sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index f8b9293a8..df8f26ff3 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -184,7 +184,7 @@ func TestGetFailures(t *testing.T) { func _randPeer() *peer.Peer { p := new(peer.Peer) p.ID = make(peer.ID, 16) - p.Addresses = []*ma.Multiaddr{nil} + p.Addresses = []ma.Multiaddr{nil} crand.Read(p.ID) return p } From a19fcfad1b83099c4f16bc054fad2d1898002a58 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 6 Oct 2014 04:23:55 -0700 Subject: [PATCH 0233/3817] Obviated need for `.ID.Pretty()` all over the place. This commit was moved from ipfs/go-blockservice@d78eeadef617990130d39aafe99ff97b30b138b6 --- blockservice/blockservice.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 21bb75c6f..9b1501424 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -36,7 +36,7 @@ func NewBlockService(d ds.Datastore, rem exchange.Interface) (*BlockService, err // AddBlock adds a particular block to the service, Putting it into the datastore. func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { k := b.Key() - log.Debug("storing [%s] in datastore", k.Pretty()) + log.Debug("storing [%s] in datastore", k) // TODO(brian): define a block datastore with a Put method which accepts a // block parameter err := s.Datastore.Put(k.DsKey(), b.Data) @@ -53,7 +53,7 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { - log.Debug("BlockService GetBlock: '%s'", k.Pretty()) + log.Debug("BlockService GetBlock: '%s'", k) datai, err := s.Datastore.Get(k.DsKey()) if err == nil { log.Debug("Blockservice: Got data in datastore.") From afd1c47924926a2225543d79dbb729289f6e4bbb Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 6 Oct 2014 04:23:55 -0700 Subject: [PATCH 0234/3817] Obviated need for `.ID.Pretty()` all over the place. This commit was moved from ipfs/go-ipfs-routing@185bd4e329ddc710c2ec1fb703ca23aa3561c060 --- routing/dht/dht.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 8cf3a1153..1f1fdd3e5 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -76,7 +76,7 @@ func NewDHT(p *peer.Peer, ps peer.Peerstore, net inet.Network, sender inet.Sende // Connect to a new peer at the given address, ping and add to the routing table func (dht *IpfsDHT) Connect(ctx context.Context, npeer *peer.Peer) (*peer.Peer, error) { - log.Debug("Connect to new peer: %s\n", npeer.ID.Pretty()) + log.Debug("Connect to new peer: %s\n", npeer) // TODO(jbenet,whyrusleeping) // @@ -132,8 +132,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N // Print out diagnostic log.Debug("[peer: %s] Got message type: '%s' [from = %s]\n", - dht.self.ID.Pretty(), - Message_MessageType_name[int32(pmes.GetType())], mPeer.ID.Pretty()) + dht.self, Message_MessageType_name[int32(pmes.GetType())], mPeer) // get handler for this msg type. handler := dht.handlerForMsgType(pmes.GetType()) @@ -177,7 +176,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p *peer.Peer, pmes *Message // Print out diagnostic log.Debug("Sent message type: '%s' [to = %s]", - Message_MessageType_name[int32(pmes.GetType())], p.ID.Pretty()) + Message_MessageType_name[int32(pmes.GetType())], p) rmes, err := dht.sender.SendRequest(ctx, mes) if err != nil { @@ -222,7 +221,7 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p *peer.Peer, key string) e return err } - log.Debug("[%s] putProvider: %s for %s", dht.self.ID.Pretty(), p.ID.Pretty(), key) + log.Debug("[%s] putProvider: %s for %s", dht.self, p, key) if *rpmes.Key != *pmes.Key { return errors.New("provider not added correctly") } @@ -346,7 +345,7 @@ func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { // Update signals to all routingTables to Update their last-seen status // on the given peer. func (dht *IpfsDHT) Update(p *peer.Peer) { - log.Debug("updating peer: [%s] latency = %f\n", p.ID.Pretty(), p.GetLatency().Seconds()) + log.Debug("updating peer: [%s] latency = %f\n", p, p.GetLatency().Seconds()) removedCount := 0 for _, route := range dht.routingTables { removed := route.Update(p) @@ -402,7 +401,7 @@ func (dht *IpfsDHT) addProviders(key u.Key, peers []*Message_Peer) []*peer.Peer continue } - log.Debug("[%s] adding provider: %s for %s", dht.self.ID.Pretty(), p, key) + log.Debug("[%s] adding provider: %s for %s", dht.self, p, key) // Dont add outselves to the list if p.ID.Equal(dht.self.ID) { From 1d15e882e7b720d6666d7b07dfc39d13bd5959a8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 6 Oct 2014 23:49:45 +0000 Subject: [PATCH 0235/3817] implement dagmodifier and tests. This commit was moved from ipfs/go-merkledag@8f57b7b8265cb4b44766bcb41a8d3958ca24dcb7 --- ipld/merkledag/merkledag.go | 47 +++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index e7c13873c..ba22f56e7 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -34,9 +34,6 @@ type Link struct { // cumulative size of target object Size uint64 - // cumulative size of data stored in object - DataSize uint64 - // multihash of the target object Hash mh.Multihash @@ -44,45 +41,45 @@ type Link struct { Node *Node } -// AddNodeLink adds a link to another node. -func (n *Node) AddNodeLink(name string, that *Node) error { - s, err := that.Size() +func MakeLink(n *Node) (*Link, error) { + s, err := n.Size() if err != nil { - return err + return nil, err } - h, err := that.Multihash() + h, err := n.Multihash() if err != nil { - return err + return nil, err } - - n.Links = append(n.Links, &Link{ - Name: name, + return &Link{ Size: s, Hash: h, - Node: that, - }) - return nil + }, nil } -// AddNodeLink adds a link to another node. without keeping a reference to -// the child node -func (n *Node) AddNodeLinkClean(name string, that *Node) error { - s, err := that.Size() +// AddNodeLink adds a link to another node. +func (n *Node) AddNodeLink(name string, that *Node) error { + lnk, err := MakeLink(that) if err != nil { return err } + lnk.Name = name + lnk.Node = that + + n.Links = append(n.Links, lnk) + return nil +} - h, err := that.Multihash() +// AddNodeLink adds a link to another node. without keeping a reference to +// the child node +func (n *Node) AddNodeLinkClean(name string, that *Node) error { + lnk, err := MakeLink(that) if err != nil { return err } + lnk.Name = name - n.Links = append(n.Links, &Link{ - Name: name, - Size: s, - Hash: h, - }) + n.Links = append(n.Links, lnk) return nil } From a0945f47630871b5058ccdba4a2b1c829eae301e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 6 Oct 2014 23:49:45 +0000 Subject: [PATCH 0236/3817] implement dagmodifier and tests. This commit was moved from ipfs/go-blockservice@bdf9a42a9f10383c561b58871b7100fe0bae6565 --- blockservice/blockservice.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 9b1501424..b8e445568 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -36,7 +36,7 @@ func NewBlockService(d ds.Datastore, rem exchange.Interface) (*BlockService, err // AddBlock adds a particular block to the service, Putting it into the datastore. func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { k := b.Key() - log.Debug("storing [%s] in datastore", k) + log.Debug("blockservice: storing [%s] in datastore", k.Pretty()) // TODO(brian): define a block datastore with a Put method which accepts a // block parameter err := s.Datastore.Put(k.DsKey(), b.Data) @@ -53,7 +53,7 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { - log.Debug("BlockService GetBlock: '%s'", k) + log.Debug("BlockService GetBlock: '%s'", k.Pretty()) datai, err := s.Datastore.Get(k.DsKey()) if err == nil { log.Debug("Blockservice: Got data in datastore.") From a319b84231a6d3b2f26b6114c70adde8cb2e0a33 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 7 Oct 2014 20:46:01 +0000 Subject: [PATCH 0237/3817] removed error from return type of blocks.NewBlock() This commit was moved from ipfs/go-merkledag@20f0643a6e71987b92564b50f63983b3ada0b408 --- ipld/merkledag/coding.go | 3 +++ ipld/merkledag/merkledag.go | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 45142ac47..1d83f32ef 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -3,6 +3,8 @@ package merkledag import ( "fmt" + u "github.com/jbenet/go-ipfs/util" + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" ) @@ -76,6 +78,7 @@ func (n *Node) Encoded(force bool) ([]byte, error) { if err != nil { return []byte{}, err } + n.cached = u.Hash(n.encoded) } return n.encoded, nil diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index ba22f56e7..05440cd6e 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -24,6 +24,8 @@ type Node struct { // cache encoded/marshaled value encoded []byte + + cached mh.Multihash } // Link represents an IPFS Merkle DAG Link between Nodes. @@ -122,12 +124,12 @@ func (n *Node) Size() (uint64, error) { // Multihash hashes the encoded data of this node. func (n *Node) Multihash() (mh.Multihash, error) { - b, err := n.Encoded(false) + _, err := n.Encoded(false) if err != nil { return nil, err } - return u.Hash(b), nil + return n.cached, nil } // Key returns the Multihash as a key, for maps. @@ -183,7 +185,9 @@ func (n *DAGService) Add(nd *Node) (u.Key, error) { return "", err } - b, err := blocks.NewBlock(d) + b := new(blocks.Block) + b.Data = d + b.Multihash, err = nd.Multihash() if err != nil { return "", err } From 6bcdfb445a0f4cf7caf32bd9e30d580696200a3c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 7 Oct 2014 20:46:01 +0000 Subject: [PATCH 0238/3817] removed error from return type of blocks.NewBlock() This commit was moved from ipfs/go-ipfs-exchange-offline@1c357a55c671d2d3b35d03afff0af50626518f93 --- exchange/offline/offline_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 26821f2c8..b759a61ca 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -5,8 +5,8 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + blocks "github.com/jbenet/go-ipfs/blocks" u "github.com/jbenet/go-ipfs/util" - testutil "github.com/jbenet/go-ipfs/util/testutil" ) func TestBlockReturnsErr(t *testing.T) { @@ -20,8 +20,8 @@ func TestBlockReturnsErr(t *testing.T) { func TestHasBlockReturnsNil(t *testing.T) { off := NewOfflineExchange() - block := testutil.NewBlockOrFail(t, "data") - err := off.HasBlock(context.Background(), block) + block := blocks.NewBlock([]byte("data")) + err := off.HasBlock(context.Background(), *block) if err != nil { t.Fatal("") } From ff453b93fb74a60e368be0b0e1410aeba9d34d3b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 8 Oct 2014 03:42:29 +0000 Subject: [PATCH 0239/3817] some performance tweaks for the dagwriter write path This commit was moved from ipfs/go-blockservice@2a7a5b63d3ae3668f7c0afefc1dc9b2ce5d82d10 --- blockservice/blockservice.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index b8e445568..dcf15ce95 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -36,7 +36,7 @@ func NewBlockService(d ds.Datastore, rem exchange.Interface) (*BlockService, err // AddBlock adds a particular block to the service, Putting it into the datastore. func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { k := b.Key() - log.Debug("blockservice: storing [%s] in datastore", k.Pretty()) + log.Debug("blockservice: storing [%s] in datastore", k) // TODO(brian): define a block datastore with a Put method which accepts a // block parameter err := s.Datastore.Put(k.DsKey(), b.Data) @@ -53,7 +53,7 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { - log.Debug("BlockService GetBlock: '%s'", k.Pretty()) + log.Debug("BlockService GetBlock: '%s'", k) datai, err := s.Datastore.Get(k.DsKey()) if err == nil { log.Debug("Blockservice: Got data in datastore.") From cbcbe38569edc2e40e63949175cbe5e3053bb1ce Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 8 Oct 2014 03:42:29 +0000 Subject: [PATCH 0240/3817] some performance tweaks for the dagwriter write path This commit was moved from ipfs/go-merkledag@1ab2c47628bd85a7889e57045916f39163a1b478 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 05440cd6e..d7e8148ed 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -175,7 +175,7 @@ type DAGService struct { // Add adds a node to the DAGService, storing the block in the BlockService func (n *DAGService) Add(nd *Node) (u.Key, error) { k, _ := nd.Key() - log.Debug("DagService Add [%s]\n", k.Pretty()) + log.Debug("DagService Add [%s]", k) if n == nil { return "", fmt.Errorf("DAGService is nil") } From 3fbd42acc69030a2881376a4d0167f2ded5e53b8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 8 Oct 2014 04:25:51 +0000 Subject: [PATCH 0241/3817] make tests pass This commit was moved from ipfs/go-blockservice@c8c176c87cb93078b8f4e0dec9cff292a522f48b --- blockservice/blocks_test.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index bfffc37b8..764d2d400 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -17,12 +17,7 @@ func TestBlocks(t *testing.T) { return } - b, err := blocks.NewBlock([]byte("beep boop")) - if err != nil { - t.Error("failed to construct block", err) - return - } - + b := blocks.NewBlock([]byte("beep boop")) h := u.Hash([]byte("beep boop")) if !bytes.Equal(b.Multihash, h) { t.Error("Block Multihash and data multihash not equal") From 79afabb0b1766d5f512dbb8bbbeb72e98d836858 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 7 Oct 2014 21:29:03 -0700 Subject: [PATCH 0242/3817] changed logging, in dht and elsewhere - use log.* instead of u.* - use automatic type conversions to .String() (Peer.String() prints nicely, and avoids calling b58 encoding until needed) This commit was moved from ipfs/go-ipfs-routing@e977a444ef5ca3be687e14927dcf6c4a29a46f65 --- routing/dht/dht.go | 6 +++--- routing/dht/dht_test.go | 2 +- routing/dht/handlers.go | 29 ++++++++++++++--------------- routing/dht/query.go | 18 +++++++++--------- routing/dht/routing.go | 12 ++++++------ routing/kbucket/table_test.go | 4 ++-- 6 files changed, 35 insertions(+), 36 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 1f1fdd3e5..cfa500ee2 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -221,7 +221,7 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p *peer.Peer, key string) e return err } - log.Debug("[%s] putProvider: %s for %s", dht.self, p, key) + log.Debug("%s putProvider: %s for %s", dht.self, p, key) if *rpmes.Key != *pmes.Key { return errors.New("provider not added correctly") } @@ -345,7 +345,7 @@ func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { // Update signals to all routingTables to Update their last-seen status // on the given peer. func (dht *IpfsDHT) Update(p *peer.Peer) { - log.Debug("updating peer: [%s] latency = %f\n", p, p.GetLatency().Seconds()) + log.Debug("updating peer: %s latency = %f\n", p, p.GetLatency().Seconds()) removedCount := 0 for _, route := range dht.routingTables { removed := route.Update(p) @@ -401,7 +401,7 @@ func (dht *IpfsDHT) addProviders(key u.Key, peers []*Message_Peer) []*peer.Peer continue } - log.Debug("[%s] adding provider: %s for %s", dht.self, p, key) + log.Debug("%s adding provider: %s for %s", dht.self, p, key) // Dont add outselves to the list if p.ID.Equal(dht.self.ID) { diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index f5d391387..23bdb88e7 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -287,7 +287,7 @@ func TestProvidesAsync(t *testing.T) { select { case p := <-provs: if !p.ID.Equal(dhts[3].self.ID) { - t.Fatalf("got a provider, but not the right one. %v", p.ID.Pretty()) + t.Fatalf("got a provider, but not the right one. %s", p) } case <-ctx.Done(): t.Fatal("Didnt get back providers") diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index ac03ed3e8..49e3eb750 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -38,7 +38,7 @@ func (dht *IpfsDHT) handlerForMsgType(t Message_MessageType) dhtHandler { } func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error) { - u.DOut("[%s] handleGetValue for key: %s\n", dht.self.ID.Pretty(), pmes.GetKey()) + log.Debug("%s handleGetValue for key: %s\n", dht.self, pmes.GetKey()) // setup response resp := newMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) @@ -50,10 +50,10 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error } // let's first check if we have the value locally. - u.DOut("[%s] handleGetValue looking into ds\n", dht.self.ID.Pretty()) + log.Debug("%s handleGetValue looking into ds\n", dht.self) dskey := u.Key(pmes.GetKey()).DsKey() iVal, err := dht.datastore.Get(dskey) - u.DOut("[%s] handleGetValue looking into ds GOT %v\n", dht.self.ID.Pretty(), iVal) + log.Debug("%s handleGetValue looking into ds GOT %v\n", dht.self, iVal) // if we got an unexpected error, bail. if err != nil && err != ds.ErrNotFound { @@ -65,7 +65,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error // if we have the value, send it back if err == nil { - u.DOut("[%s] handleGetValue success!\n", dht.self.ID.Pretty()) + log.Debug("%s handleGetValue success!\n", dht.self) byts, ok := iVal.([]byte) if !ok { @@ -78,14 +78,14 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error // if we know any providers for the requested value, return those. provs := dht.providers.GetProviders(u.Key(pmes.GetKey())) if len(provs) > 0 { - u.DOut("handleGetValue returning %d provider[s]\n", len(provs)) + log.Debug("handleGetValue returning %d provider[s]\n", len(provs)) resp.ProviderPeers = peersToPBPeers(provs) } // Find closest peer on given cluster to desired key and reply with that info closer := dht.betterPeerToQuery(pmes) if closer != nil { - u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty()) + log.Debug("handleGetValue returning a closer peer: '%s'\n", closer) resp.CloserPeers = peersToPBPeers([]*peer.Peer{closer}) } @@ -98,12 +98,12 @@ func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *Message) (*Message, error defer dht.dslock.Unlock() dskey := u.Key(pmes.GetKey()).DsKey() err := dht.datastore.Put(dskey, pmes.GetValue()) - u.DOut("[%s] handlePutValue %v %v\n", dht.self.ID.Pretty(), dskey, pmes.GetValue()) + log.Debug("%s handlePutValue %v %v\n", dht.self, dskey, pmes.GetValue()) return pmes, err } func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *Message) (*Message, error) { - u.DOut("[%s] Responding to ping from [%s]!\n", dht.self.ID.Pretty(), p.ID.Pretty()) + log.Debug("%s Responding to ping from %s!\n", dht.self, p) return pmes, nil } @@ -119,16 +119,16 @@ func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *Message) (*Message, error } if closest == nil { - u.PErr("handleFindPeer: could not find anything.\n") + log.Error("handleFindPeer: could not find anything.\n") return resp, nil } if len(closest.Addresses) == 0 { - u.PErr("handleFindPeer: no addresses for connected peer...\n") + log.Error("handleFindPeer: no addresses for connected peer...\n") return resp, nil } - u.DOut("handleFindPeer: sending back '%s'\n", closest.ID.Pretty()) + log.Debug("handleFindPeer: sending back '%s'\n", closest) resp.CloserPeers = peersToPBPeers([]*peer.Peer{closest}) return resp, nil } @@ -140,7 +140,7 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *Message) (*Message, e dsk := u.Key(pmes.GetKey()).DsKey() has, err := dht.datastore.Has(dsk) if err != nil && err != ds.ErrNotFound { - u.PErr("unexpected datastore error: %v\n", err) + log.Error("unexpected datastore error: %v\n", err) has = false } @@ -172,8 +172,7 @@ type providerInfo struct { func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *Message) (*Message, error) { key := u.Key(pmes.GetKey()) - u.DOut("[%s] Adding [%s] as a provider for '%s'\n", - dht.self.ID.Pretty(), p.ID.Pretty(), peer.ID(key).Pretty()) + log.Debug("%s adding %s as a provider for '%s'\n", dht.self, p, peer.ID(key)) dht.providers.AddProvider(key, p) return pmes, nil // send back same msg as confirmation. @@ -192,7 +191,7 @@ func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *Message) (*Message, err for _, ps := range seq { _, err := msg.FromObject(ps, pmes) if err != nil { - u.PErr("handleDiagnostics error creating message: %v\n", err) + log.Error("handleDiagnostics error creating message: %v\n", err) continue } // dht.sender.SendRequest(context.TODO(), mes) diff --git a/routing/dht/query.go b/routing/dht/query.go index a62646f05..0a9ca0bd8 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -151,7 +151,7 @@ func (r *dhtQueryRunner) Run(peers []*peer.Peer) (*dhtQueryResult, error) { func (r *dhtQueryRunner) addPeerToQuery(next *peer.Peer, benchmark *peer.Peer) { if next == nil { // wtf why are peers nil?!? - u.PErr("Query getting nil peers!!!\n") + log.Error("Query getting nil peers!!!\n") return } @@ -170,7 +170,7 @@ func (r *dhtQueryRunner) addPeerToQuery(next *peer.Peer, benchmark *peer.Peer) { r.peersSeen[next.Key()] = next r.Unlock() - log.Debug("adding peer to query: %v\n", next.ID.Pretty()) + log.Debug("adding peer to query: %v\n", next) // do this after unlocking to prevent possible deadlocks. r.peersRemaining.Increment(1) @@ -194,14 +194,14 @@ func (r *dhtQueryRunner) spawnWorkers() { if !more { return // channel closed. } - u.DOut("spawning worker for: %v\n", p.ID.Pretty()) + log.Debug("spawning worker for: %v\n", p) go r.queryPeer(p) } } } func (r *dhtQueryRunner) queryPeer(p *peer.Peer) { - u.DOut("spawned worker for: %v\n", p.ID.Pretty()) + log.Debug("spawned worker for: %v\n", p) // make sure we rate limit concurrency. select { @@ -211,33 +211,33 @@ func (r *dhtQueryRunner) queryPeer(p *peer.Peer) { return } - u.DOut("running worker for: %v\n", p.ID.Pretty()) + log.Debug("running worker for: %v\n", p) // finally, run the query against this peer res, err := r.query.qfunc(r.ctx, p) if err != nil { - u.DOut("ERROR worker for: %v %v\n", p.ID.Pretty(), err) + log.Debug("ERROR worker for: %v %v\n", p, err) r.Lock() r.errs = append(r.errs, err) r.Unlock() } else if res.success { - u.DOut("SUCCESS worker for: %v\n", p.ID.Pretty(), res) + log.Debug("SUCCESS worker for: %v\n", p, res) r.Lock() r.result = res r.Unlock() r.cancel() // signal to everyone that we're done. } else if res.closerPeers != nil { - u.DOut("PEERS CLOSER -- worker for: %v\n", p.ID.Pretty()) + log.Debug("PEERS CLOSER -- worker for: %v\n", p) for _, next := range res.closerPeers { r.addPeerToQuery(next, p) } } // signal we're done proccessing peer p - u.DOut("completing worker for: %v\n", p.ID.Pretty()) + log.Debug("completing worker for: %v\n", p) r.peersRemaining.Decrement(1) r.rateLimit <- struct{}{} } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 4fa6c8c94..25567038c 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -18,7 +18,7 @@ import ( // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error { - log.Debug("PutValue %s", key.Pretty()) + log.Debug("PutValue %s", key) err := dht.putLocal(key, value) if err != nil { return err @@ -31,7 +31,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error } query := newQuery(key, func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) { - log.Debug("[%s] PutValue qry part %v", dht.self.ID.Pretty(), p.ID.Pretty()) + log.Debug("%s PutValue qry part %v", dht.self, p) err := dht.putValueToNetwork(ctx, p, string(key), value) if err != nil { return nil, err @@ -47,7 +47,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error // If the search does not succeed, a multiaddr string of a closer peer is // returned along with util.ErrSearchIncomplete func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { - log.Debug("Get Value [%s]", key.Pretty()) + log.Debug("Get Value [%s]", key) // If we have it local, dont bother doing an RPC! // NOTE: this might not be what we want to do... @@ -189,7 +189,7 @@ func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*Message_Peer, ps *peerSet // FindProviders searches for peers who can provide the value for given key. func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]*peer.Peer, error) { // get closest peer - log.Debug("Find providers for: '%s'", key.Pretty()) + log.Debug("Find providers for: '%s'", key) p := dht.routingTables[0].NearestPeer(kb.ConvertKey(key)) if p == nil { return nil, nil @@ -333,11 +333,11 @@ func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (*peer.Pee // Ping a peer, log the time it took func (dht *IpfsDHT) Ping(ctx context.Context, p *peer.Peer) error { // Thoughts: maybe this should accept an ID and do a peer lookup? - log.Info("ping %s start", p.ID.Pretty()) + log.Info("ping %s start", p) pmes := newMessage(Message_PING, "", 0) _, err := dht.sendRequest(ctx, p, pmes) - log.Info("ping %s end (err = %s)", p.ID.Pretty(), err) + log.Info("ping %s end (err = %s)", p, err) return err } diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 49be52c65..cc1cdfba1 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -101,7 +101,7 @@ func TestTableFind(t *testing.T) { rt.Update(peers[i]) } - t.Logf("Searching for peer: '%s'", peers[2].ID.Pretty()) + t.Logf("Searching for peer: '%s'", peers[2]) found := rt.NearestPeer(ConvertPeerID(peers[2].ID)) if !found.ID.Equal(peers[2].ID) { t.Fatalf("Failed to lookup known node...") @@ -118,7 +118,7 @@ func TestTableFindMultiple(t *testing.T) { rt.Update(peers[i]) } - t.Logf("Searching for peer: '%s'", peers[2].ID.Pretty()) + t.Logf("Searching for peer: '%s'", peers[2]) found := rt.NearestPeers(ConvertPeerID(peers[2].ID), 15) if len(found) != 15 { t.Fatalf("Got back different number of peers than we expected.") From 23aba342319ae1a2621dbebc8e51cad31d00c2a1 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 8 Oct 2014 03:11:29 -0700 Subject: [PATCH 0243/3817] deprecate merkledag.Node.Update This commit was moved from ipfs/go-merkledag@372dd2265ff9e5099689fb149b4e24cd96d1bdbb --- ipld/merkledag/dagreader.go | 1 + ipld/merkledag/merkledag.go | 25 ------------------------- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/ipld/merkledag/dagreader.go b/ipld/merkledag/dagreader.go index badc661fd..bc66a6001 100644 --- a/ipld/merkledag/dagreader.go +++ b/ipld/merkledag/dagreader.go @@ -26,6 +26,7 @@ func NewDagReader(n *Node, serv *DAGService) (io.Reader, error) { if err != nil { return nil, err } + switch pb.GetType() { case ft.PBData_Directory: return nil, ErrIsDir diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index d7e8148ed..f0c93ad63 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -138,31 +138,6 @@ func (n *Node) Key() (u.Key, error) { return u.Key(h), err } -// Recursively update all hash links and size values in the tree -func (n *Node) Update() error { - log.Debug("node update") - for _, l := range n.Links { - if l.Node != nil { - err := l.Node.Update() - if err != nil { - return err - } - nhash, err := l.Node.Multihash() - if err != nil { - return err - } - l.Hash = nhash - size, err := l.Node.Size() - if err != nil { - return err - } - l.Size = size - } - } - _, err := n.Encoded(true) - return err -} - // DAGService is an IPFS Merkle DAG service. // - the root is virtual (like a forest) // - stores nodes' data in a BlockService From f82b3d880582f184fe357f17546b44aaad1db94b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 8 Oct 2014 04:14:50 -0700 Subject: [PATCH 0244/3817] New NameSystem interface type NameSystem interface { Resolver Publisher } should say it all. cc @whyrusleeping This commit was moved from ipfs/go-namesys@21228bf8b077d5c17c8f56df2ead9c329981e58b --- namesys/dns.go | 7 ++--- namesys/interface.go | 43 +++++++++++++++++++++++++++++++ namesys/namesys.go | 57 +++++++++++++++++++++++++++++++++++++++++ namesys/nsresolver.go | 8 ------ namesys/proquint.go | 6 +++-- namesys/publisher.go | 22 ++++++---------- namesys/resolve_test.go | 21 +++------------ namesys/resolver.go | 42 ------------------------------ namesys/routing.go | 24 ++++++++--------- 9 files changed, 132 insertions(+), 98 deletions(-) create mode 100644 namesys/interface.go create mode 100644 namesys/namesys.go delete mode 100644 namesys/nsresolver.go delete mode 100644 namesys/resolver.go diff --git a/namesys/dns.go b/namesys/dns.go index 8dda6cb51..66448511f 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -4,9 +4,10 @@ import ( "net" b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" + isd "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-is-domain" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + u "github.com/jbenet/go-ipfs/util" - isd "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-is-domain" ) // DNSResolver implements a Resolver on DNS domains @@ -15,8 +16,8 @@ type DNSResolver struct { // cache would need a timeout } -// Matches implements Resolver -func (r *DNSResolver) Matches(name string) bool { +// CanResolve implements Resolver +func (r *DNSResolver) CanResolve(name string) bool { return isd.IsDomain(name) } diff --git a/namesys/interface.go b/namesys/interface.go new file mode 100644 index 000000000..eef1fc32b --- /dev/null +++ b/namesys/interface.go @@ -0,0 +1,43 @@ +package namesys + +import ( + "errors" + + ci "github.com/jbenet/go-ipfs/crypto" +) + +// ErrResolveFailed signals an error when attempting to resolve. +var ErrResolveFailed = errors.New("could not resolve name.") + +// ErrPublishFailed signals an error when attempting to publish. +var ErrPublishFailed = errors.New("could not publish name.") + +// Namesys represents a cohesive name publishing and resolving system. +// +// Publishing a name is the process of establishing a mapping, a key-value +// pair, according to naming rules and databases. +// +// Resolving a name is the process of looking up the value associated with the +// key (name). +type NameSystem interface { + Resolver + Publisher +} + +// Resolver is an object capable of resolving names. +type Resolver interface { + + // Resolve looks up a name, and returns the value previously published. + Resolve(name string) (value string, err error) + + // CanResolve checks whether this Resolver can resolve a name + CanResolve(name string) bool +} + +// Publisher is an object capable of publishing particular names. +type Publisher interface { + + // Publish establishes a name-value mapping. + // TODO make this not PrivKey specific. + Publish(name ci.PrivKey, value string) error +} diff --git a/namesys/namesys.go b/namesys/namesys.go new file mode 100644 index 000000000..2ea9a30bd --- /dev/null +++ b/namesys/namesys.go @@ -0,0 +1,57 @@ +package namesys + +import ( + ci "github.com/jbenet/go-ipfs/crypto" + routing "github.com/jbenet/go-ipfs/routing" +) + +// ipnsNameSystem implements IPNS naming. +// +// Uses three Resolvers: +// (a) ipfs routing naming: SFS-like PKI names. +// (b) dns domains: resolves using links in DNS TXT records +// (c) proquints: interprets string as the raw byte data. +// +// It can only publish to: (a) ipfs routing naming. +// +type ipns struct { + resolvers []Resolver + publisher Publisher +} + +// NewNameSystem will construct the IPFS naming system based on Routing +func NewNameSystem(r routing.IpfsRouting) NameSystem { + return &ipns{ + resolvers: []Resolver{ + new(DNSResolver), + new(ProquintResolver), + NewRoutingResolver(r), + }, + publisher: NewRoutingPublisher(r), + } +} + +// Resolve implements Resolver +func (ns *ipns) Resolve(name string) (string, error) { + for _, r := range ns.resolvers { + if r.CanResolve(name) { + return r.Resolve(name) + } + } + return "", ErrResolveFailed +} + +// CanResolve implements Resolver +func (ns *ipns) CanResolve(name string) bool { + for _, r := range ns.resolvers { + if r.CanResolve(name) { + return true + } + } + return false +} + +// Publish implements Publisher +func (ns *ipns) Publish(name ci.PrivKey, value string) error { + return ns.publisher.Publish(name, value) +} diff --git a/namesys/nsresolver.go b/namesys/nsresolver.go deleted file mode 100644 index 89ef9ff5a..000000000 --- a/namesys/nsresolver.go +++ /dev/null @@ -1,8 +0,0 @@ -package namesys - -type Resolver interface { - // Resolve returns a base58 encoded string - Resolve(string) (string, error) - - Matches(string) bool -} diff --git a/namesys/proquint.go b/namesys/proquint.go index bf34c3b6c..89bbc4a44 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -8,13 +8,15 @@ import ( type ProquintResolver struct{} -func (r *ProquintResolver) Matches(name string) bool { +// CanResolve implements Resolver. Checks whether the name is a proquint string. +func (r *ProquintResolver) CanResolve(name string) bool { ok, err := proquint.IsProquint(name) return err == nil && ok } +// Resolve implements Resolver. Decodes the proquint string. func (r *ProquintResolver) Resolve(name string) (string, error) { - ok := r.Matches(name) + ok := r.CanResolve(name) if !ok { return "", errors.New("not a valid proquint string") } diff --git a/namesys/publisher.go b/namesys/publisher.go index 0c605301c..0828f5e08 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,32 +7,26 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ci "github.com/jbenet/go-ipfs/crypto" - mdag "github.com/jbenet/go-ipfs/merkledag" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" ) +// ipnsPublisher is capable of publishing and resolving names to the IPFS +// routing system. type ipnsPublisher struct { - dag *mdag.DAGService routing routing.IpfsRouting } -type Publisher interface { - Publish(ci.PrivKey, string) error +// NewRoutingPublisher constructs a publisher for the IPFS Routing name system. +func NewRoutingPublisher(route routing.IpfsRouting) Publisher { + return &ipnsPublisher{routing: route} } -func NewPublisher(dag *mdag.DAGService, route routing.IpfsRouting) Publisher { - return &ipnsPublisher{ - dag: dag, - routing: route, - } -} - -// Publish accepts a keypair and a value, +// Publish implements Publisher. Accepts a keypair and a value, func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { log.Debug("namesys: Publish %s", value) ctx := context.TODO() - data, err := CreateEntryData(k, value) + data, err := createRoutingEntryData(k, value) if err != nil { return err } @@ -63,7 +57,7 @@ func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { return nil } -func CreateEntryData(pk ci.PrivKey, val string) ([]byte, error) { +func createRoutingEntryData(pk ci.PrivKey, val string) ([]byte, error) { entry := new(IpnsEntry) sig, err := pk.Sign([]byte(val)) if err != nil { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index ff5292224..30b996647 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -4,9 +4,7 @@ import ( "testing" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" - bs "github.com/jbenet/go-ipfs/blockservice" ci "github.com/jbenet/go-ipfs/crypto" - mdag "github.com/jbenet/go-ipfs/merkledag" "github.com/jbenet/go-ipfs/peer" mock "github.com/jbenet/go-ipfs/routing/mock" u "github.com/jbenet/go-ipfs/util" @@ -19,26 +17,15 @@ func TestRoutingResolve(t *testing.T) { lds := ds.NewMapDatastore() d := mock.NewMockRouter(local, lds) - bserv, err := bs.NewBlockService(lds, nil) - if err != nil { - t.Fatal(err) - } - - dag := &mdag.DAGService{Blocks: bserv} - - resolve := NewMasterResolver(d, dag) - - pub := ipnsPublisher{ - dag: dag, - routing: d, - } + resolver := NewRoutingResolver(d) + publisher := NewRoutingPublisher(d) privk, pubk, err := ci.GenerateKeyPair(ci.RSA, 512) if err != nil { t.Fatal(err) } - err = pub.Publish(privk, "Hello") + err = publisher.Publish(privk, "Hello") if err != nil { t.Fatal(err) } @@ -49,7 +36,7 @@ func TestRoutingResolve(t *testing.T) { } pkhash := u.Hash(pubkb) - res, err := resolve.Resolve(u.Key(pkhash).Pretty()) + res, err := resolver.Resolve(u.Key(pkhash).Pretty()) if err != nil { t.Fatal(err) } diff --git a/namesys/resolver.go b/namesys/resolver.go deleted file mode 100644 index 7765a4ba0..000000000 --- a/namesys/resolver.go +++ /dev/null @@ -1,42 +0,0 @@ -package namesys - -import ( - "errors" - - mdag "github.com/jbenet/go-ipfs/merkledag" - "github.com/jbenet/go-ipfs/routing" -) - -var ErrCouldntResolve = errors.New("could not resolve name.") - -type masterResolver struct { - res []Resolver -} - -func NewMasterResolver(r routing.IpfsRouting, dag *mdag.DAGService) Resolver { - mr := new(masterResolver) - mr.res = []Resolver{ - new(DNSResolver), - new(ProquintResolver), - NewRoutingResolver(r, dag), - } - return mr -} - -func (mr *masterResolver) Resolve(name string) (string, error) { - for _, r := range mr.res { - if r.Matches(name) { - return r.Resolve(name) - } - } - return "", ErrCouldntResolve -} - -func (mr *masterResolver) Matches(name string) bool { - for _, r := range mr.res { - if r.Matches(name) { - return true - } - } - return false -} diff --git a/namesys/routing.go b/namesys/routing.go index abacb22d4..da1c05d0e 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -8,32 +8,32 @@ import ( mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" ci "github.com/jbenet/go-ipfs/crypto" - mdag "github.com/jbenet/go-ipfs/merkledag" - "github.com/jbenet/go-ipfs/routing" + routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" ) var log = u.Logger("namesys") -// RoutingResolver implements NSResolver for the main IPFS SFS-like naming -type RoutingResolver struct { +// routingResolver implements NSResolver for the main IPFS SFS-like naming +type routingResolver struct { routing routing.IpfsRouting - dag *mdag.DAGService } -func NewRoutingResolver(route routing.IpfsRouting, dagservice *mdag.DAGService) *RoutingResolver { - return &RoutingResolver{ - routing: route, - dag: dagservice, - } +// NewRoutingResolver constructs a name resolver using the IPFS Routing system +// to implement SFS-like naming on top. +func NewRoutingResolver(route routing.IpfsRouting) Resolver { + return &routingResolver{routing: route} } -func (r *RoutingResolver) Matches(name string) bool { +// CanResolve implements Resolver. Checks whether name is a b58 encoded string. +func (r *routingResolver) CanResolve(name string) bool { _, err := mh.FromB58String(name) return err == nil } -func (r *RoutingResolver) Resolve(name string) (string, error) { +// Resolve implements Resolver. Uses the IPFS routing system to resolve SFS-like +// names. +func (r *routingResolver) Resolve(name string) (string, error) { log.Debug("RoutingResolve: '%s'", name) ctx := context.TODO() hash, err := mh.FromB58String(name) From 9bc47e5ea07f5f81e4b1ab6dbb3ae7d38b456ae4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 8 Oct 2014 16:51:53 +0000 Subject: [PATCH 0245/3817] add more comments! This commit was moved from ipfs/go-merkledag@7744373c1d83afe1681052cdb2d827bd3a8beec9 --- ipld/merkledag/dagreader.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/dagreader.go b/ipld/merkledag/dagreader.go index bc66a6001..3010ca89d 100644 --- a/ipld/merkledag/dagreader.go +++ b/ipld/merkledag/dagreader.go @@ -20,6 +20,8 @@ type DagReader struct { buf *bytes.Buffer } +// NewDagReader creates a new reader object that reads the data represented by the given +// node, using the passed in DAGService for data retreival func NewDagReader(n *Node, serv *DAGService) (io.Reader, error) { pb := new(ft.PBData) err := proto.Unmarshal(n.Data, pb) @@ -29,6 +31,7 @@ func NewDagReader(n *Node, serv *DAGService) (io.Reader, error) { switch pb.GetType() { case ft.PBData_Directory: + // Dont allow reading directories return nil, ErrIsDir case ft.PBData_File: return &DagReader{ @@ -37,12 +40,15 @@ func NewDagReader(n *Node, serv *DAGService) (io.Reader, error) { buf: bytes.NewBuffer(pb.GetData()), }, nil case ft.PBData_Raw: + // Raw block will just be a single level, return a byte buffer return bytes.NewBuffer(pb.GetData()), nil default: - panic("Unrecognized node type!") + return nil, ft.ErrUnrecognizedType } } +// Follows the next link in line and loads it from the DAGService, +// setting the next buffer to read from func (dr *DagReader) precalcNextBuf() error { if dr.position >= len(dr.node.Links) { return io.EOF @@ -65,7 +71,7 @@ func (dr *DagReader) precalcNextBuf() error { switch pb.GetType() { case ft.PBData_Directory: - panic("Why is there a directory under a file?") + return ft.ErrInvalidDirLocation case ft.PBData_File: //TODO: this *should* work, needs testing first //return NewDagReader(nxt, dr.serv) @@ -74,11 +80,12 @@ func (dr *DagReader) precalcNextBuf() error { dr.buf = bytes.NewBuffer(pb.GetData()) return nil default: - panic("Unrecognized node type!") + return ft.ErrUnrecognizedType } } func (dr *DagReader) Read(b []byte) (int, error) { + // If no cached buffer, load one if dr.buf == nil { err := dr.precalcNextBuf() if err != nil { @@ -87,16 +94,22 @@ func (dr *DagReader) Read(b []byte) (int, error) { } total := 0 for { + // Attempt to fill bytes from cached buffer n, err := dr.buf.Read(b[total:]) total += n if err != nil { + // EOF is expected if err != io.EOF { return total, err } } + + // If weve read enough bytes, return if total == len(b) { return total, nil } + + // Otherwise, load up the next block err = dr.precalcNextBuf() if err != nil { return total, err From 29709bec0ed4835173b4db34aa5a9500ec0cf16d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 8 Oct 2014 21:14:18 +0000 Subject: [PATCH 0246/3817] Rework package structure for unixfs and subpackage cc @jbenet This commit was moved from ipfs/go-unixfs@26ca445614bc2b7780e5d7421b8a785ffbc2e165 --- unixfs/Makefile | 5 + unixfs/data.pb.go | 101 ++++++++++++++++++ unixfs/data.proto | 14 +++ unixfs/format.go | 119 +++++++++++++++++++++ unixfs/format_test.go | 36 +++++++ unixfs/io/dagmodifier.go | 192 ++++++++++++++++++++++++++++++++++ unixfs/io/dagmodifier_test.go | 159 ++++++++++++++++++++++++++++ unixfs/io/dagreader.go | 145 +++++++++++++++++++++++++ unixfs/io/dagwriter.go | 107 +++++++++++++++++++ unixfs/io/dagwriter_test.go | 127 ++++++++++++++++++++++ 10 files changed, 1005 insertions(+) create mode 100644 unixfs/Makefile create mode 100644 unixfs/data.pb.go create mode 100644 unixfs/data.proto create mode 100644 unixfs/format.go create mode 100644 unixfs/format_test.go create mode 100644 unixfs/io/dagmodifier.go create mode 100644 unixfs/io/dagmodifier_test.go create mode 100644 unixfs/io/dagreader.go create mode 100644 unixfs/io/dagwriter.go create mode 100644 unixfs/io/dagwriter_test.go diff --git a/unixfs/Makefile b/unixfs/Makefile new file mode 100644 index 000000000..87f182fe5 --- /dev/null +++ b/unixfs/Makefile @@ -0,0 +1,5 @@ +all: data.pb.go + +data.pb.go: data.proto + protoc --go_out=. data.proto + diff --git a/unixfs/data.pb.go b/unixfs/data.pb.go new file mode 100644 index 000000000..89e5f8084 --- /dev/null +++ b/unixfs/data.pb.go @@ -0,0 +1,101 @@ +// Code generated by protoc-gen-go. +// source: data.proto +// DO NOT EDIT! + +/* +Package unixfs is a generated protocol buffer package. + +It is generated from these files: + data.proto + +It has these top-level messages: + PBData +*/ +package unixfs + +import proto "code.google.com/p/goprotobuf/proto" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = math.Inf + +type PBData_DataType int32 + +const ( + PBData_Raw PBData_DataType = 0 + PBData_Directory PBData_DataType = 1 + PBData_File PBData_DataType = 2 +) + +var PBData_DataType_name = map[int32]string{ + 0: "Raw", + 1: "Directory", + 2: "File", +} +var PBData_DataType_value = map[string]int32{ + "Raw": 0, + "Directory": 1, + "File": 2, +} + +func (x PBData_DataType) Enum() *PBData_DataType { + p := new(PBData_DataType) + *p = x + return p +} +func (x PBData_DataType) String() string { + return proto.EnumName(PBData_DataType_name, int32(x)) +} +func (x *PBData_DataType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(PBData_DataType_value, data, "PBData_DataType") + if err != nil { + return err + } + *x = PBData_DataType(value) + return nil +} + +type PBData struct { + Type *PBData_DataType `protobuf:"varint,1,req,enum=unixfs.PBData_DataType" json:"Type,omitempty"` + Data []byte `protobuf:"bytes,2,opt" json:"Data,omitempty"` + Filesize *uint64 `protobuf:"varint,3,opt,name=filesize" json:"filesize,omitempty"` + Blocksizes []uint64 `protobuf:"varint,4,rep,name=blocksizes" json:"blocksizes,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *PBData) Reset() { *m = PBData{} } +func (m *PBData) String() string { return proto.CompactTextString(m) } +func (*PBData) ProtoMessage() {} + +func (m *PBData) GetType() PBData_DataType { + if m != nil && m.Type != nil { + return *m.Type + } + return PBData_Raw +} + +func (m *PBData) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *PBData) GetFilesize() uint64 { + if m != nil && m.Filesize != nil { + return *m.Filesize + } + return 0 +} + +func (m *PBData) GetBlocksizes() []uint64 { + if m != nil { + return m.Blocksizes + } + return nil +} + +func init() { + proto.RegisterEnum("unixfs.PBData_DataType", PBData_DataType_name, PBData_DataType_value) +} diff --git a/unixfs/data.proto b/unixfs/data.proto new file mode 100644 index 000000000..b9504b0c3 --- /dev/null +++ b/unixfs/data.proto @@ -0,0 +1,14 @@ +package unixfs; + +message PBData { + enum DataType { + Raw = 0; + Directory = 1; + File = 2; + } + + required DataType Type = 1; + optional bytes Data = 2; + optional uint64 filesize = 3; + repeated uint64 blocksizes = 4; +} diff --git a/unixfs/format.go b/unixfs/format.go new file mode 100644 index 000000000..6ba8e3aa4 --- /dev/null +++ b/unixfs/format.go @@ -0,0 +1,119 @@ +// Package format implements a data format for files in the ipfs filesystem +// It is not the only format in ipfs, but it is the one that the filesystem assumes +package unixfs + +import ( + "errors" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" +) + +var ErrMalformedFileFormat = errors.New("malformed data in file format") +var ErrInvalidDirLocation = errors.New("found directory node in unexpected place") +var ErrUnrecognizedType = errors.New("unrecognized node type") + +func FromBytes(data []byte) (*PBData, error) { + pbdata := new(PBData) + err := proto.Unmarshal(data, pbdata) + if err != nil { + return nil, err + } + return pbdata, nil +} + +func FilePBData(data []byte, totalsize uint64) []byte { + pbfile := new(PBData) + typ := PBData_File + pbfile.Type = &typ + pbfile.Data = data + pbfile.Filesize = proto.Uint64(totalsize) + + data, err := proto.Marshal(pbfile) + if err != nil { + // This really shouldnt happen, i promise + // The only failure case for marshal is if required fields + // are not filled out, and they all are. If the proto object + // gets changed and nobody updates this function, the code + // should panic due to programmer error + panic(err) + } + return data +} + +// Returns Bytes that represent a Directory +func FolderPBData() []byte { + pbfile := new(PBData) + typ := PBData_Directory + pbfile.Type = &typ + + data, err := proto.Marshal(pbfile) + if err != nil { + //this really shouldnt happen, i promise + panic(err) + } + return data +} + +func WrapData(b []byte) []byte { + pbdata := new(PBData) + typ := PBData_Raw + pbdata.Data = b + pbdata.Type = &typ + + out, err := proto.Marshal(pbdata) + if err != nil { + // This shouldnt happen. seriously. + panic(err) + } + + return out +} + +func UnwrapData(data []byte) ([]byte, error) { + pbdata := new(PBData) + err := proto.Unmarshal(data, pbdata) + if err != nil { + return nil, err + } + return pbdata.GetData(), nil +} + +func DataSize(data []byte) (uint64, error) { + pbdata := new(PBData) + err := proto.Unmarshal(data, pbdata) + if err != nil { + return 0, err + } + + switch pbdata.GetType() { + case PBData_Directory: + return 0, errors.New("Cant get data size of directory!") + case PBData_File: + return pbdata.GetFilesize(), nil + case PBData_Raw: + return uint64(len(pbdata.GetData())), nil + default: + return 0, errors.New("Unrecognized node data type!") + } +} + +type MultiBlock struct { + Data []byte + blocksizes []uint64 + subtotal uint64 +} + +func (mb *MultiBlock) AddBlockSize(s uint64) { + mb.subtotal += s + mb.blocksizes = append(mb.blocksizes, s) +} + +func (mb *MultiBlock) GetBytes() ([]byte, error) { + pbn := new(PBData) + t := PBData_File + pbn.Type = &t + pbn.Filesize = proto.Uint64(uint64(len(mb.Data)) + mb.subtotal) + pbn.Blocksizes = mb.blocksizes + pbn.Data = mb.Data + return proto.Marshal(pbn) +} diff --git a/unixfs/format_test.go b/unixfs/format_test.go new file mode 100644 index 000000000..eca926e9f --- /dev/null +++ b/unixfs/format_test.go @@ -0,0 +1,36 @@ +package unixfs + +import ( + "testing" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" +) + +func TestMultiBlock(t *testing.T) { + mbf := new(MultiBlock) + for i := 0; i < 15; i++ { + mbf.AddBlockSize(100) + } + + mbf.Data = make([]byte, 128) + + b, err := mbf.GetBytes() + if err != nil { + t.Fatal(err) + } + + pbn := new(PBData) + err = proto.Unmarshal(b, pbn) + if err != nil { + t.Fatal(err) + } + + ds, err := DataSize(b) + if err != nil { + t.Fatal(err) + } + + if ds != (100*15)+128 { + t.Fatal("Datasize calculations incorrect!") + } +} diff --git a/unixfs/io/dagmodifier.go b/unixfs/io/dagmodifier.go new file mode 100644 index 000000000..8680da46a --- /dev/null +++ b/unixfs/io/dagmodifier.go @@ -0,0 +1,192 @@ +package io + +import ( + "bytes" + "errors" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + + "github.com/jbenet/go-ipfs/importer/chunk" + mdag "github.com/jbenet/go-ipfs/merkledag" + ft "github.com/jbenet/go-ipfs/unixfs" + u "github.com/jbenet/go-ipfs/util" +) + +// DagModifier is the only struct licensed and able to correctly +// perform surgery on a DAG 'file' +// Dear god, please rename this to something more pleasant +type DagModifier struct { + dagserv *mdag.DAGService + curNode *mdag.Node + + pbdata *ft.PBData + splitter chunk.BlockSplitter +} + +func NewDagModifier(from *mdag.Node, serv *mdag.DAGService, spl chunk.BlockSplitter) (*DagModifier, error) { + pbd, err := ft.FromBytes(from.Data) + if err != nil { + return nil, err + } + + return &DagModifier{ + curNode: from.Copy(), + dagserv: serv, + pbdata: pbd, + splitter: spl, + }, nil +} + +// WriteAt will modify a dag file in place +// NOTE: it currently assumes only a single level of indirection +func (dm *DagModifier) WriteAt(b []byte, offset uint64) (int, error) { + + // Check bounds + if dm.pbdata.GetFilesize() < offset { + return 0, errors.New("Attempted to perform write starting past end of file") + } + + // First need to find where we are writing at + end := uint64(len(b)) + offset + + // This shouldnt be necessary if we do subblocks sizes properly + newsize := dm.pbdata.GetFilesize() + if end > dm.pbdata.GetFilesize() { + newsize = end + } + zeroblocklen := uint64(len(dm.pbdata.Data)) + origlen := len(b) + + if end <= zeroblocklen { + log.Debug("Writing into zero block.") + // Replacing zeroeth data block (embedded in the root node) + //TODO: check chunking here + copy(dm.pbdata.Data[offset:], b) + return len(b), nil + } + + // Find where write should start + var traversed uint64 + startsubblk := len(dm.pbdata.Blocksizes) + if offset < zeroblocklen { + dm.pbdata.Data = dm.pbdata.Data[:offset] + startsubblk = 0 + } else { + traversed = uint64(zeroblocklen) + for i, size := range dm.pbdata.Blocksizes { + if uint64(offset) < traversed+size { + log.Debug("Starting mod at block %d. [%d < %d + %d]", i, offset, traversed, size) + // Here is where we start + startsubblk = i + lnk := dm.curNode.Links[i] + node, err := dm.dagserv.Get(u.Key(lnk.Hash)) + if err != nil { + return 0, err + } + data, err := ft.UnwrapData(node.Data) + if err != nil { + return 0, err + } + + // We have to rewrite the data before our write in this block. + b = append(data[:offset-traversed], b...) + break + } + traversed += size + } + if startsubblk == len(dm.pbdata.Blocksizes) { + // TODO: Im not sure if theres any case that isnt being handled here. + // leaving this note here as a future reference in case something breaks + } + } + + // Find blocks that need to be overwritten + var changed []int + mid := -1 + var midoff uint64 + for i, size := range dm.pbdata.Blocksizes[startsubblk:] { + if end > traversed { + changed = append(changed, i+startsubblk) + } else { + break + } + traversed += size + if end < traversed { + mid = i + startsubblk + midoff = end - (traversed - size) + break + } + } + + // If our write starts in the middle of a block... + var midlnk *mdag.Link + if mid >= 0 { + midlnk = dm.curNode.Links[mid] + midnode, err := dm.dagserv.Get(u.Key(midlnk.Hash)) + if err != nil { + return 0, err + } + + // NOTE: this may have to be changed later when we have multiple + // layers of indirection + data, err := ft.UnwrapData(midnode.Data) + if err != nil { + return 0, err + } + b = append(b, data[midoff:]...) + } + + // Generate new sub-blocks, and sizes + subblocks := splitBytes(b, dm.splitter) + var links []*mdag.Link + var sizes []uint64 + for _, sb := range subblocks { + n := &mdag.Node{Data: ft.WrapData(sb)} + _, err := dm.dagserv.Add(n) + if err != nil { + log.Error("Failed adding node to DAG service: %s", err) + return 0, err + } + lnk, err := mdag.MakeLink(n) + if err != nil { + return 0, err + } + links = append(links, lnk) + sizes = append(sizes, uint64(len(sb))) + } + + // This is disgusting (and can be rewritten if performance demands) + if len(changed) > 0 { + sechalflink := append(links, dm.curNode.Links[changed[len(changed)-1]+1:]...) + dm.curNode.Links = append(dm.curNode.Links[:changed[0]], sechalflink...) + sechalfblks := append(sizes, dm.pbdata.Blocksizes[changed[len(changed)-1]+1:]...) + dm.pbdata.Blocksizes = append(dm.pbdata.Blocksizes[:changed[0]], sechalfblks...) + } else { + dm.curNode.Links = append(dm.curNode.Links, links...) + dm.pbdata.Blocksizes = append(dm.pbdata.Blocksizes, sizes...) + } + dm.pbdata.Filesize = proto.Uint64(newsize) + + return origlen, nil +} + +// splitBytes uses a splitterFunc to turn a large array of bytes +// into many smaller arrays of bytes +func splitBytes(b []byte, spl chunk.BlockSplitter) [][]byte { + out := spl.Split(bytes.NewReader(b)) + var arr [][]byte + for blk := range out { + arr = append(arr, blk) + } + return arr +} + +// GetNode gets the modified DAG Node +func (dm *DagModifier) GetNode() (*mdag.Node, error) { + b, err := proto.Marshal(dm.pbdata) + if err != nil { + return nil, err + } + dm.curNode.Data = b + return dm.curNode.Copy(), nil +} diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go new file mode 100644 index 000000000..e3ea8e4f7 --- /dev/null +++ b/unixfs/io/dagmodifier_test.go @@ -0,0 +1,159 @@ +package io + +import ( + "fmt" + "io" + "io/ioutil" + "testing" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" + bs "github.com/jbenet/go-ipfs/blockservice" + "github.com/jbenet/go-ipfs/importer/chunk" + mdag "github.com/jbenet/go-ipfs/merkledag" + ft "github.com/jbenet/go-ipfs/unixfs" + u "github.com/jbenet/go-ipfs/util" + + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" +) + +func getMockDagServ(t *testing.T) *mdag.DAGService { + dstore := ds.NewMapDatastore() + bserv, err := bs.NewBlockService(dstore, nil) + if err != nil { + t.Fatal(err) + } + return &mdag.DAGService{bserv} +} + +func getNode(t *testing.T, dserv *mdag.DAGService, size int64) ([]byte, *mdag.Node) { + dw := NewDagWriter(dserv, &chunk.SizeSplitter{500}) + + n, err := io.CopyN(dw, u.NewFastRand(), size) + if err != nil { + t.Fatal(err) + } + if n != size { + t.Fatal("Incorrect copy amount!") + } + + dw.Close() + node := dw.GetNode() + + dr, err := NewDagReader(node, dserv) + if err != nil { + t.Fatal(err) + } + + b, err := ioutil.ReadAll(dr) + if err != nil { + t.Fatal(err) + } + + return b, node +} + +func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) []byte { + newdata := make([]byte, size) + r := u.NewFastRand() + r.Read(newdata) + + if size+beg > uint64(len(orig)) { + orig = append(orig, make([]byte, (size+beg)-uint64(len(orig)))...) + } + copy(orig[beg:], newdata) + + nmod, err := dm.WriteAt(newdata, uint64(beg)) + if err != nil { + t.Fatal(err) + } + + if nmod != int(size) { + t.Fatalf("Mod length not correct! %d != %d", nmod, size) + } + + nd, err := dm.GetNode() + if err != nil { + t.Fatal(err) + } + + rd, err := NewDagReader(nd, dm.dagserv) + if err != nil { + t.Fatal(err) + } + + after, err := ioutil.ReadAll(rd) + if err != nil { + t.Fatal(err) + } + + err = arrComp(after, orig) + if err != nil { + t.Fatal(err) + } + return orig +} + +func TestDagModifierBasic(t *testing.T) { + logging.SetLevel(logging.CRITICAL, "blockservice") + logging.SetLevel(logging.CRITICAL, "merkledag") + dserv := getMockDagServ(t) + b, n := getNode(t, dserv, 50000) + + dagmod, err := NewDagModifier(n, dserv, &chunk.SizeSplitter{512}) + if err != nil { + t.Fatal(err) + } + + // Within zero block + beg := uint64(15) + length := uint64(60) + + t.Log("Testing mod within zero block") + b = testModWrite(t, beg, length, b, dagmod) + + // Within bounds of existing file + beg = 1000 + length = 4000 + t.Log("Testing mod within bounds of existing file.") + b = testModWrite(t, beg, length, b, dagmod) + + // Extend bounds + beg = 49500 + length = 4000 + + t.Log("Testing mod that extends file.") + b = testModWrite(t, beg, length, b, dagmod) + + // "Append" + beg = uint64(len(b)) + length = 3000 + b = testModWrite(t, beg, length, b, dagmod) + + // Verify reported length + node, err := dagmod.GetNode() + if err != nil { + t.Fatal(err) + } + + size, err := ft.DataSize(node.Data) + if err != nil { + t.Fatal(err) + } + + expected := uint64(50000 + 3500 + 3000) + if size != expected { + t.Fatal("Final reported size is incorrect [%d != %d]", size, expected) + } +} + +func arrComp(a, b []byte) error { + if len(a) != len(b) { + return fmt.Errorf("Arrays differ in length. %d != %d", len(a), len(b)) + } + for i, v := range a { + if v != b[i] { + return fmt.Errorf("Arrays differ at index: %d", i) + } + } + return nil +} diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go new file mode 100644 index 000000000..29196a1e3 --- /dev/null +++ b/unixfs/io/dagreader.go @@ -0,0 +1,145 @@ +package io + +import ( + "bytes" + "errors" + "io" + + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + mdag "github.com/jbenet/go-ipfs/merkledag" + ft "github.com/jbenet/go-ipfs/unixfs" + u "github.com/jbenet/go-ipfs/util" +) + +var ErrIsDir = errors.New("this dag node is a directory") + +// DagReader provides a way to easily read the data contained in a dag. +type DagReader struct { + serv *mdag.DAGService + node *mdag.Node + position int + buf *bytes.Buffer +} + +// NewDagReader creates a new reader object that reads the data represented by the given +// node, using the passed in DAGService for data retreival +func NewDagReader(n *mdag.Node, serv *mdag.DAGService) (io.Reader, error) { + pb := new(ft.PBData) + err := proto.Unmarshal(n.Data, pb) + if err != nil { + return nil, err + } + + switch pb.GetType() { + case ft.PBData_Directory: + // Dont allow reading directories + return nil, ErrIsDir + case ft.PBData_File: + return &DagReader{ + node: n, + serv: serv, + buf: bytes.NewBuffer(pb.GetData()), + }, nil + case ft.PBData_Raw: + // Raw block will just be a single level, return a byte buffer + return bytes.NewBuffer(pb.GetData()), nil + default: + return nil, ft.ErrUnrecognizedType + } +} + +// Follows the next link in line and loads it from the DAGService, +// setting the next buffer to read from +func (dr *DagReader) precalcNextBuf() error { + if dr.position >= len(dr.node.Links) { + return io.EOF + } + nxtLink := dr.node.Links[dr.position] + nxt := nxtLink.Node + if nxt == nil { + nxtNode, err := dr.serv.Get(u.Key(nxtLink.Hash)) + if err != nil { + return err + } + nxt = nxtNode + } + pb := new(ft.PBData) + err := proto.Unmarshal(nxt.Data, pb) + if err != nil { + return err + } + dr.position++ + + switch pb.GetType() { + case ft.PBData_Directory: + return ft.ErrInvalidDirLocation + case ft.PBData_File: + //TODO: this *should* work, needs testing first + //return NewDagReader(nxt, dr.serv) + panic("Not yet handling different layers of indirection!") + case ft.PBData_Raw: + dr.buf = bytes.NewBuffer(pb.GetData()) + return nil + default: + return ft.ErrUnrecognizedType + } +} + +func (dr *DagReader) Read(b []byte) (int, error) { + // If no cached buffer, load one + if dr.buf == nil { + err := dr.precalcNextBuf() + if err != nil { + return 0, err + } + } + total := 0 + for { + // Attempt to fill bytes from cached buffer + n, err := dr.buf.Read(b[total:]) + total += n + if err != nil { + // EOF is expected + if err != io.EOF { + return total, err + } + } + + // If weve read enough bytes, return + if total == len(b) { + return total, nil + } + + // Otherwise, load up the next block + err = dr.precalcNextBuf() + if err != nil { + return total, err + } + } +} + +/* +func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { + switch whence { + case os.SEEK_SET: + for i := 0; i < len(dr.node.Links); i++ { + nsize := dr.node.Links[i].Size - 8 + if offset > nsize { + offset -= nsize + } else { + break + } + } + dr.position = i + err := dr.precalcNextBuf() + if err != nil { + return 0, err + } + case os.SEEK_CUR: + case os.SEEK_END: + default: + return 0, errors.New("invalid whence") + } + return 0, nil +} +*/ diff --git a/unixfs/io/dagwriter.go b/unixfs/io/dagwriter.go new file mode 100644 index 000000000..4abb1b36c --- /dev/null +++ b/unixfs/io/dagwriter.go @@ -0,0 +1,107 @@ +package io + +import ( + "github.com/jbenet/go-ipfs/importer/chunk" + dag "github.com/jbenet/go-ipfs/merkledag" + ft "github.com/jbenet/go-ipfs/unixfs" + "github.com/jbenet/go-ipfs/util" +) + +var log = util.Logger("dagwriter") + +type DagWriter struct { + dagserv *dag.DAGService + node *dag.Node + totalSize int64 + splChan chan []byte + done chan struct{} + splitter chunk.BlockSplitter + seterr error +} + +func NewDagWriter(ds *dag.DAGService, splitter chunk.BlockSplitter) *DagWriter { + dw := new(DagWriter) + dw.dagserv = ds + dw.splChan = make(chan []byte, 8) + dw.splitter = splitter + dw.done = make(chan struct{}) + go dw.startSplitter() + return dw +} + +// startSplitter manages splitting incoming bytes and +// creating dag nodes from them. Created nodes are stored +// in the DAGService and then released to the GC. +func (dw *DagWriter) startSplitter() { + + // Since the splitter functions take a reader (and should!) + // we wrap our byte chan input in a reader + r := util.NewByteChanReader(dw.splChan) + blkchan := dw.splitter.Split(r) + + // First data block is reserved for storage in the root node + first := <-blkchan + mbf := new(ft.MultiBlock) + root := new(dag.Node) + + for blkData := range blkchan { + // Store the block size in the root node + mbf.AddBlockSize(uint64(len(blkData))) + node := &dag.Node{Data: ft.WrapData(blkData)} + _, err := dw.dagserv.Add(node) + if err != nil { + dw.seterr = err + log.Critical("Got error adding created node to dagservice: %s", err) + return + } + + // Add a link to this node without storing a reference to the memory + err = root.AddNodeLinkClean("", node) + if err != nil { + dw.seterr = err + log.Critical("Got error adding created node to root node: %s", err) + return + } + } + + // Generate the root node data + mbf.Data = first + data, err := mbf.GetBytes() + if err != nil { + dw.seterr = err + log.Critical("Failed generating bytes for multiblock file: %s", err) + return + } + root.Data = data + + // Add root node to the dagservice + _, err = dw.dagserv.Add(root) + if err != nil { + dw.seterr = err + log.Critical("Got error adding created node to dagservice: %s", err) + return + } + dw.node = root + dw.done <- struct{}{} +} + +func (dw *DagWriter) Write(b []byte) (int, error) { + if dw.seterr != nil { + return 0, dw.seterr + } + dw.splChan <- b + return len(b), nil +} + +// Close the splitters input channel and wait for it to finish +// Must be called to finish up splitting, otherwise split method +// will never halt +func (dw *DagWriter) Close() error { + close(dw.splChan) + <-dw.done + return nil +} + +func (dw *DagWriter) GetNode() *dag.Node { + return dw.node +} diff --git a/unixfs/io/dagwriter_test.go b/unixfs/io/dagwriter_test.go new file mode 100644 index 000000000..73ba5c4e9 --- /dev/null +++ b/unixfs/io/dagwriter_test.go @@ -0,0 +1,127 @@ +package io + +import ( + "testing" + + "io" + + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + bs "github.com/jbenet/go-ipfs/blockservice" + chunk "github.com/jbenet/go-ipfs/importer/chunk" + mdag "github.com/jbenet/go-ipfs/merkledag" +) + +type datasource struct { + i int +} + +func (d *datasource) Read(b []byte) (int, error) { + for i, _ := range b { + b[i] = byte(d.i % 256) + d.i++ + } + return len(b), nil +} + +func (d *datasource) Matches(t *testing.T, r io.Reader, length int) bool { + b := make([]byte, 100) + i := 0 + for { + n, err := r.Read(b) + if err != nil && err != io.EOF { + t.Fatal(err) + } + for _, v := range b[:n] { + if v != byte(i%256) { + t.Fatalf("Buffers differed at byte: %d (%d != %d)", i, v, (i % 256)) + } + i++ + } + if err == io.EOF { + break + } + } + if i != length { + t.Fatalf("Incorrect length. (%d != %d)", i, length) + } + return true +} + +func TestDagWriter(t *testing.T) { + dstore := ds.NewMapDatastore() + bserv, err := bs.NewBlockService(dstore, nil) + if err != nil { + t.Fatal(err) + } + dag := &mdag.DAGService{bserv} + dw := NewDagWriter(dag, &chunk.SizeSplitter{4096}) + + nbytes := int64(1024 * 1024 * 2) + n, err := io.CopyN(dw, &datasource{}, nbytes) + if err != nil { + t.Fatal(err) + } + + if n != nbytes { + t.Fatal("Copied incorrect amount of bytes!") + } + + dw.Close() + + node := dw.GetNode() + read, err := NewDagReader(node, dag) + if err != nil { + t.Fatal(err) + } + + d := &datasource{} + if !d.Matches(t, read, int(nbytes)) { + t.Fatal("Failed to validate!") + } +} + +func TestMassiveWrite(t *testing.T) { + t.SkipNow() + dstore := ds.NewNullDatastore() + bserv, err := bs.NewBlockService(dstore, nil) + if err != nil { + t.Fatal(err) + } + dag := &mdag.DAGService{bserv} + dw := NewDagWriter(dag, &chunk.SizeSplitter{4096}) + + nbytes := int64(1024 * 1024 * 1024 * 16) + n, err := io.CopyN(dw, &datasource{}, nbytes) + if err != nil { + t.Fatal(err) + } + if n != nbytes { + t.Fatal("Incorrect copy size.") + } + dw.Close() +} + +func BenchmarkDagWriter(b *testing.B) { + dstore := ds.NewNullDatastore() + bserv, err := bs.NewBlockService(dstore, nil) + if err != nil { + b.Fatal(err) + } + dag := &mdag.DAGService{bserv} + + b.ResetTimer() + nbytes := int64(100000) + for i := 0; i < b.N; i++ { + b.SetBytes(nbytes) + dw := NewDagWriter(dag, &chunk.SizeSplitter{4096}) + n, err := io.CopyN(dw, &datasource{}, nbytes) + if err != nil { + b.Fatal(err) + } + if n != nbytes { + b.Fatal("Incorrect copy size.") + } + dw.Close() + } + +} From a2043c7bd1552c5707965a82f00fd4d0c870fc06 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 8 Oct 2014 21:14:18 +0000 Subject: [PATCH 0247/3817] Rework package structure for unixfs and subpackage cc @jbenet This commit was moved from ipfs/go-merkledag@f7fa0f6039c0422ac6ad8e1dcae7906b587ae5db --- ipld/merkledag/dagreader.go | 144 ------------------------------------ 1 file changed, 144 deletions(-) delete mode 100644 ipld/merkledag/dagreader.go diff --git a/ipld/merkledag/dagreader.go b/ipld/merkledag/dagreader.go deleted file mode 100644 index 3010ca89d..000000000 --- a/ipld/merkledag/dagreader.go +++ /dev/null @@ -1,144 +0,0 @@ -package merkledag - -import ( - "bytes" - "errors" - "io" - - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - ft "github.com/jbenet/go-ipfs/importer/format" - u "github.com/jbenet/go-ipfs/util" -) - -var ErrIsDir = errors.New("this dag node is a directory") - -// DagReader provides a way to easily read the data contained in a dag. -type DagReader struct { - serv *DAGService - node *Node - position int - buf *bytes.Buffer -} - -// NewDagReader creates a new reader object that reads the data represented by the given -// node, using the passed in DAGService for data retreival -func NewDagReader(n *Node, serv *DAGService) (io.Reader, error) { - pb := new(ft.PBData) - err := proto.Unmarshal(n.Data, pb) - if err != nil { - return nil, err - } - - switch pb.GetType() { - case ft.PBData_Directory: - // Dont allow reading directories - return nil, ErrIsDir - case ft.PBData_File: - return &DagReader{ - node: n, - serv: serv, - buf: bytes.NewBuffer(pb.GetData()), - }, nil - case ft.PBData_Raw: - // Raw block will just be a single level, return a byte buffer - return bytes.NewBuffer(pb.GetData()), nil - default: - return nil, ft.ErrUnrecognizedType - } -} - -// Follows the next link in line and loads it from the DAGService, -// setting the next buffer to read from -func (dr *DagReader) precalcNextBuf() error { - if dr.position >= len(dr.node.Links) { - return io.EOF - } - nxtLink := dr.node.Links[dr.position] - nxt := nxtLink.Node - if nxt == nil { - nxtNode, err := dr.serv.Get(u.Key(nxtLink.Hash)) - if err != nil { - return err - } - nxt = nxtNode - } - pb := new(ft.PBData) - err := proto.Unmarshal(nxt.Data, pb) - if err != nil { - return err - } - dr.position++ - - switch pb.GetType() { - case ft.PBData_Directory: - return ft.ErrInvalidDirLocation - case ft.PBData_File: - //TODO: this *should* work, needs testing first - //return NewDagReader(nxt, dr.serv) - panic("Not yet handling different layers of indirection!") - case ft.PBData_Raw: - dr.buf = bytes.NewBuffer(pb.GetData()) - return nil - default: - return ft.ErrUnrecognizedType - } -} - -func (dr *DagReader) Read(b []byte) (int, error) { - // If no cached buffer, load one - if dr.buf == nil { - err := dr.precalcNextBuf() - if err != nil { - return 0, err - } - } - total := 0 - for { - // Attempt to fill bytes from cached buffer - n, err := dr.buf.Read(b[total:]) - total += n - if err != nil { - // EOF is expected - if err != io.EOF { - return total, err - } - } - - // If weve read enough bytes, return - if total == len(b) { - return total, nil - } - - // Otherwise, load up the next block - err = dr.precalcNextBuf() - if err != nil { - return total, err - } - } -} - -/* -func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { - switch whence { - case os.SEEK_SET: - for i := 0; i < len(dr.node.Links); i++ { - nsize := dr.node.Links[i].Size - 8 - if offset > nsize { - offset -= nsize - } else { - break - } - } - dr.position = i - err := dr.precalcNextBuf() - if err != nil { - return 0, err - } - case os.SEEK_CUR: - case os.SEEK_END: - default: - return 0, errors.New("invalid whence") - } - return 0, nil -} -*/ From 7f6d0a3fb8004ad21fa1d0f16503f7168112668b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 8 Oct 2014 21:14:18 +0000 Subject: [PATCH 0248/3817] Rework package structure for unixfs and subpackage cc @jbenet This commit was moved from ipfs/go-ipfs-chunker@50a89a045ab61e5b436fc299f101f268934f7ae8 --- chunker/rabin.go | 94 ++++++++++++++++++++++++++++++++++++++++++++ chunker/splitting.go | 45 +++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 chunker/rabin.go create mode 100644 chunker/splitting.go diff --git a/chunker/rabin.go b/chunker/rabin.go new file mode 100644 index 000000000..fbfb4cec4 --- /dev/null +++ b/chunker/rabin.go @@ -0,0 +1,94 @@ +package chunk + +import ( + "bufio" + "bytes" + "fmt" + "io" + "math" +) + +type MaybeRabin struct { + mask int + windowSize int + MinBlockSize int + MaxBlockSize int +} + +func NewMaybeRabin(avgBlkSize int) *MaybeRabin { + blkbits := uint(math.Log2(float64(avgBlkSize))) + rb := new(MaybeRabin) + rb.mask = (1 << blkbits) - 1 + rb.windowSize = 16 // probably a good number... + rb.MinBlockSize = avgBlkSize / 2 + rb.MaxBlockSize = (avgBlkSize / 2) * 3 + return rb +} + +func (mr *MaybeRabin) Split(r io.Reader) chan []byte { + out := make(chan []byte, 16) + go func() { + inbuf := bufio.NewReader(r) + blkbuf := new(bytes.Buffer) + + // some bullshit numbers i made up + a := 10 // honestly, no idea what this is + MOD := 33554383 // randomly chosen (seriously) + an := 1 + rollingHash := 0 + + // Window is a circular buffer + window := make([]byte, mr.windowSize) + push := func(i int, val byte) (outval int) { + outval = int(window[i%len(window)]) + window[i%len(window)] = val + return + } + + // Duplicate byte slice + dup := func(b []byte) []byte { + d := make([]byte, len(b)) + copy(d, b) + return d + } + + // Fill up the window + i := 0 + for ; i < mr.windowSize; i++ { + b, err := inbuf.ReadByte() + if err != nil { + fmt.Println(err) + return + } + blkbuf.WriteByte(b) + push(i, b) + rollingHash = (rollingHash*a + int(b)) % MOD + an = (an * a) % MOD + } + + for ; true; i++ { + b, err := inbuf.ReadByte() + if err != nil { + break + } + outval := push(i, b) + blkbuf.WriteByte(b) + rollingHash = (rollingHash*a + int(b) - an*outval) % MOD + if (rollingHash&mr.mask == mr.mask && blkbuf.Len() > mr.MinBlockSize) || + blkbuf.Len() >= mr.MaxBlockSize { + out <- dup(blkbuf.Bytes()) + blkbuf.Reset() + } + + // Check if there are enough remaining + peek, err := inbuf.Peek(mr.windowSize) + if err != nil || len(peek) != mr.windowSize { + break + } + } + io.Copy(blkbuf, inbuf) + out <- blkbuf.Bytes() + close(out) + }() + return out +} diff --git a/chunker/splitting.go b/chunker/splitting.go new file mode 100644 index 000000000..0b5717eaf --- /dev/null +++ b/chunker/splitting.go @@ -0,0 +1,45 @@ +package chunk + +import ( + "io" + + "github.com/jbenet/go-ipfs/util" +) + +var log = util.Logger("chunk") + +var DefaultSplitter = &SizeSplitter{1024 * 512} + +type BlockSplitter interface { + Split(r io.Reader) chan []byte +} + +type SizeSplitter struct { + Size int +} + +func (ss *SizeSplitter) Split(r io.Reader) chan []byte { + out := make(chan []byte) + go func() { + defer close(out) + for { + chunk := make([]byte, ss.Size) + nread, err := r.Read(chunk) + if err != nil { + if err == io.EOF { + if nread > 0 { + out <- chunk[:nread] + } + return + } + log.Error("Block split error: %s", err) + return + } + if nread < ss.Size { + chunk = chunk[:nread] + } + out <- chunk + } + }() + return out +} From 2c639c9fb6189d531f4f089bbe1294f952107adf Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 8 Oct 2014 14:48:32 -0700 Subject: [PATCH 0249/3817] make vendor is your friend cc @whyrusleeping This commit was moved from ipfs/go-unixfs@68523aae982f6341a9696d8257ed01249d904af6 --- unixfs/data.pb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/data.pb.go b/unixfs/data.pb.go index 89e5f8084..2efdd8a4c 100644 --- a/unixfs/data.pb.go +++ b/unixfs/data.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package unixfs -import proto "code.google.com/p/goprotobuf/proto" +import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. From 2c8268f3c05a627a3e871a644180ba31769fb82a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 8 Oct 2014 21:55:50 +0000 Subject: [PATCH 0250/3817] add in some extra debug logging, and increase routing table latencies This commit was moved from ipfs/go-ipfs-routing@936c6a022a7a7d299a0414a51009b0226bbd8612 --- routing/dht/dht.go | 4 ++-- routing/dht/handlers.go | 1 + routing/dht/routing.go | 1 + routing/kbucket/table.go | 4 ++++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index cfa500ee2..c95e07511 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -67,8 +67,8 @@ func NewDHT(p *peer.Peer, ps peer.Peerstore, net inet.Network, sender inet.Sende dht.providers = NewProviderManager(p.ID) dht.routingTables = make([]*kb.RoutingTable, 3) - dht.routingTables[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*30) - dht.routingTables[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*100) + dht.routingTables[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*1000) + dht.routingTables[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*1000) dht.routingTables[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Hour) dht.birth = time.Now() return dht diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 49e3eb750..417dd0918 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -137,6 +137,7 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *Message) (*Message, e resp := newMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) // check if we have this value, to add ourselves as provider. + log.Debug("handling GetProviders: '%s'", pmes.GetKey()) dsk := u.Key(pmes.GetKey()).DsKey() has, err := dht.datastore.Has(dsk) if err != nil && err != ds.ErrNotFound { diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 25567038c..d29a46fef 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -192,6 +192,7 @@ func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]*peer.Peer, log.Debug("Find providers for: '%s'", key) p := dht.routingTables[0].NearestPeer(kb.ConvertKey(key)) if p == nil { + log.Warning("Got no nearest peer for find providers: '%s'", key) return nil, nil } diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 2a0f16d1a..242546ba4 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -11,6 +11,8 @@ import ( u "github.com/jbenet/go-ipfs/util" ) +var log = u.Logger("table") + // RoutingTable defines the routing table. type RoutingTable struct { @@ -138,6 +140,8 @@ func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer { if len(peers) > 0 { return peers[0] } + + log.Error("NearestPeer: Returning nil, table size = %d", rt.Size()) return nil } From 394ca707d1263ab617ff489e3a1bb1f47c8bd849 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 8 Oct 2014 22:38:33 +0000 Subject: [PATCH 0251/3817] add another test to try and reproduce data loss issue This commit was moved from ipfs/go-unixfs@e46c1f5a46c7fa7931fd4b095ce388ed7fe67d75 --- unixfs/io/dagmodifier_test.go | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index e3ea8e4f7..32d9a84b5 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -146,6 +146,47 @@ func TestDagModifierBasic(t *testing.T) { } } +func TestMultiWrite(t *testing.T) { + dserv := getMockDagServ(t) + _, n := getNode(t, dserv, 0) + + dagmod, err := NewDagModifier(n, dserv, &chunk.SizeSplitter{512}) + if err != nil { + t.Fatal(err) + } + + data := make([]byte, 4000) + u.NewFastRand().Read(data) + + for i := 0; i < len(data); i++ { + n, err := dagmod.WriteAt(data[i:i+1], uint64(i)) + if err != nil { + t.Fatal(err) + } + if n != 1 { + t.Fatal("Somehow wrote the wrong number of bytes! (n != 1)") + } + } + nd, err := dagmod.GetNode() + if err != nil { + t.Fatal(err) + } + + read, err := NewDagReader(nd, dserv) + if err != nil { + t.Fatal(err) + } + rbuf, err := ioutil.ReadAll(read) + if err != nil { + t.Fatal(err) + } + + err = arrComp(rbuf, data) + if err != nil { + t.Fatal(err) + } +} + func arrComp(a, b []byte) error { if len(a) != len(b) { return fmt.Errorf("Arrays differ in length. %d != %d", len(a), len(b)) From 9e5597c1356eba12e7e9271bb89a50df4e8f2961 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 9 Oct 2014 03:39:45 -0700 Subject: [PATCH 0252/3817] ipfs name cmd improvements - cleaned up cmd help - ipfs name publish [] - ipfs name resolve [] - publish validates - both validate n args This commit was moved from ipfs/go-namesys@badb37ef0321365a23bb51138b3b7c8d34bf4cf1 --- namesys/publisher.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 0828f5e08..88533f8a0 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -1,10 +1,12 @@ package namesys import ( + "fmt" "time" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" ci "github.com/jbenet/go-ipfs/crypto" routing "github.com/jbenet/go-ipfs/routing" @@ -25,6 +27,13 @@ func NewRoutingPublisher(route routing.IpfsRouting) Publisher { // Publish implements Publisher. Accepts a keypair and a value, func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { log.Debug("namesys: Publish %s", value) + + // validate `value` is a ref (multihash) + _, err := mh.FromB58String(value) + if err != nil { + return fmt.Errorf("publish value must be str multihash. %v", err) + } + ctx := context.TODO() data, err := createRoutingEntryData(k, value) if err != nil { From 7a8a8d2a3b5518e50f08b37fa51847c2b2cbe6b8 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 9 Oct 2014 03:50:52 -0700 Subject: [PATCH 0253/3817] fixed resolver test This commit was moved from ipfs/go-namesys@fa4ebefd08a7037166b616e7c6dec0a372f30ff2 --- namesys/resolve_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 30b996647..5e652f42f 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -26,6 +26,12 @@ func TestRoutingResolve(t *testing.T) { } err = publisher.Publish(privk, "Hello") + if err == nil { + t.Fatal("should have errored out when publishing a non-multihash val") + } + + h := u.Key(u.Hash([]byte("Hello"))).Pretty() + err = publisher.Publish(privk, h) if err != nil { t.Fatal(err) } @@ -41,7 +47,7 @@ func TestRoutingResolve(t *testing.T) { t.Fatal(err) } - if res != "Hello" { + if res != h { t.Fatal("Got back incorrect value.") } } From 3030c06ef719fc490b548676be425c4198b1fd0e Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 9 Oct 2014 04:48:13 -0700 Subject: [PATCH 0254/3817] u.DOut -> log.Debug and other logging switches. I kept the u.PErr and u.POut in cli commands, as those do need to write raw output directly. This commit was moved from ipfs/go-ipfs-routing@50a7c15a0a25433a24e6c32bff6290c29f07faed --- routing/dht/Message.go | 3 +-- routing/dht/dht_logger.go | 6 ++---- routing/dht/ext_test.go | 2 +- routing/dht/routing.go | 12 ++++++------ routing/kbucket/table.go | 2 +- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/routing/dht/Message.go b/routing/dht/Message.go index 84d323c37..435a536b1 100644 --- a/routing/dht/Message.go +++ b/routing/dht/Message.go @@ -3,7 +3,6 @@ package dht import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" peer "github.com/jbenet/go-ipfs/peer" - u "github.com/jbenet/go-ipfs/util" ) func newMessage(typ Message_MessageType, key string, level int) *Message { @@ -42,7 +41,7 @@ func peersToPBPeers(peers []*peer.Peer) []*Message_Peer { func (m *Message) GetClusterLevel() int { level := m.GetClusterLevelRaw() - 1 if level < 0 { - u.DErr("GetClusterLevel: no routing level specified, assuming 0\n") + log.Error("GetClusterLevel: no routing level specified, assuming 0") level = 0 } return int(level) diff --git a/routing/dht/dht_logger.go b/routing/dht/dht_logger.go index 403c2a66f..1a0878bf7 100644 --- a/routing/dht/dht_logger.go +++ b/routing/dht/dht_logger.go @@ -3,8 +3,6 @@ package dht import ( "encoding/json" "time" - - u "github.com/jbenet/go-ipfs/util" ) type logDhtRPC struct { @@ -31,9 +29,9 @@ func (l *logDhtRPC) EndLog() { func (l *logDhtRPC) Print() { b, err := json.Marshal(l) if err != nil { - u.DOut(err.Error()) + log.Debug(err.Error()) } else { - u.DOut(string(b)) + log.Debug(string(b)) } } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index df8f26ff3..a9a9acba0 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -237,7 +237,7 @@ func TestNotFound(t *testing.T) { ctx, _ := context.WithTimeout(context.Background(), time.Second*5) v, err := d.GetValue(ctx, u.Key("hello")) - u.DOut("get value got %v\n", v) + log.Debug("get value got %v", v) if err != nil { switch err { case u.ErrNotFound: diff --git a/routing/dht/routing.go b/routing/dht/routing.go index d29a46fef..c14031ce2 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -156,7 +156,7 @@ func (dht *IpfsDHT) FindProvidersAsync2(ctx context.Context, key u.Key, count in go func(p *peer.Peer) { pmes, err := dht.findProvidersSingle(ctx, p, key, 0) if err != nil { - u.PErr("%v\n", err) + log.Error("%s", err) return } dht.addPeerListAsync(key, pmes.GetProviderPeers(), ps, count, peerOut) @@ -207,11 +207,11 @@ func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]*peer.Peer, // handle providers provs := pmes.GetProviderPeers() if provs != nil { - u.DOut("Got providers back from findProviders call!\n") + log.Debug("Got providers back from findProviders call!") return dht.addProviders(key, provs), nil } - u.DOut("Didnt get providers, just closer peers.\n") + log.Debug("Didnt get providers, just closer peers.") closer := pmes.GetCloserPeers() if len(closer) == 0 { level++ @@ -220,7 +220,7 @@ func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]*peer.Peer, np, err := dht.peerFromInfo(closer[0]) if err != nil { - u.DOut("no peerFromInfo") + log.Debug("no peerFromInfo") level++ continue } @@ -293,7 +293,7 @@ func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (*peer.Pee query := newQuery(u.Key(id), func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) { pmes, err := dht.findPeerSingle(ctx, p, id, routeLevel) if err != nil { - u.DErr("getPeer error: %v\n", err) + log.Error("%s getPeer error: %v", dht.self, err) return nil, err } @@ -306,7 +306,7 @@ func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (*peer.Pee for i, fp := range plist { nxtp, err := dht.peerFromInfo(fp) if err != nil { - u.DErr("findPeer error: %v\n", err) + log.Error("%s findPeer error: %v", dht.self, err) continue } diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 242546ba4..45ffb3cdf 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -118,7 +118,7 @@ func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) pe } peerArr = append(peerArr, &pd) if e == nil { - u.POut("list element was nil.\n") + log.Debug("list element was nil.\n") return peerArr } } From 3633b0bdd5d0e635a304901d80b8f188820ebaae Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 9 Oct 2014 04:48:13 -0700 Subject: [PATCH 0255/3817] u.DOut -> log.Debug and other logging switches. I kept the u.PErr and u.POut in cli commands, as those do need to write raw output directly. This commit was moved from ipfs/go-path@3ba12e03fd68016e12942ddb9c325352ea5e6551 --- path/path.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/path/path.go b/path/path.go index 239203140..03c1a481e 100644 --- a/path/path.go +++ b/path/path.go @@ -40,11 +40,11 @@ func (s *Resolver) ResolvePath(fpath string) (*merkledag.Node, error) { // first element in the path is a b58 hash (for now) h, err := mh.FromB58String(parts[0]) if err != nil { - u.DOut("given path element is not a base58 string.\n") + log.Debug("given path element is not a base58 string.\n") return nil, err } - u.DOut("Resolve dag get.\n") + log.Debug("Resolve dag get.\n") nd, err := s.DAG.Get(u.Key(h)) if err != nil { return nil, err From 0661a7564c62112a87c627b681f746c2c884453b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 9 Oct 2014 04:53:02 -0700 Subject: [PATCH 0256/3817] bugfixes to prev commit This commit was moved from ipfs/go-ipfs-routing@ce10b9281490715745b2412617dce4ea44a39926 --- routing/dht/Message.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/Message.go b/routing/dht/Message.go index 435a536b1..e4607f1de 100644 --- a/routing/dht/Message.go +++ b/routing/dht/Message.go @@ -41,7 +41,7 @@ func peersToPBPeers(peers []*peer.Peer) []*Message_Peer { func (m *Message) GetClusterLevel() int { level := m.GetClusterLevelRaw() - 1 if level < 0 { - log.Error("GetClusterLevel: no routing level specified, assuming 0") + log.Debug("GetClusterLevel: no routing level specified, assuming 0") level = 0 } return int(level) From 8eed10a264e0b7463bd21ef03f0f23b85de095ec Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 10 Oct 2014 14:52:59 -0700 Subject: [PATCH 0257/3817] update dht tests to new network interface This commit was moved from ipfs/go-ipfs-routing@2d707721e9203ce84250da312466a02ab13a248a --- routing/dht/ext_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index a9a9acba0..88f512378 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -88,6 +88,10 @@ func (f *fauxNet) SendMessage(msg.NetMessage) error { return nil } +func (f *fauxNet) GetPeerList() []*peer.Peer { + return nil +} + // Close terminates all network operation func (f *fauxNet) Close() error { return nil } From ea74fbd21af504ee61f2f96ffe06ee0cb082df67 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 10 Oct 2014 20:48:20 -0700 Subject: [PATCH 0258/3817] handler fixes for tests This commit was moved from ipfs/go-ipfs-routing@96e76747131e596b8943e7ee5a395d1cd0e1930c --- routing/dht/dht_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 23bdb88e7..1d7413fd5 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -39,7 +39,7 @@ func setupDHT(t *testing.T, p *peer.Peer) *IpfsDHT { } d := NewDHT(p, peerstore, net, dhts, ds.NewMapDatastore()) - dhts.Handler = d + dhts.SetHandler(d) return d } From 954cd54e49f8feb50b499f14d54a0ce13a55141b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 13 Oct 2014 02:29:33 -0700 Subject: [PATCH 0259/3817] add plumbing output + logging Fixes #157 Found #158 This commit was moved from ipfs/go-merkledag@7fe2ad6884bde93b4fd07c17dda78f89f5d4ba5a --- ipld/merkledag/merkledag.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index f0c93ad63..46b0c4089 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -124,6 +124,7 @@ func (n *Node) Size() (uint64, error) { // Multihash hashes the encoded data of this node. func (n *Node) Multihash() (mh.Multihash, error) { + // Note: Encoded generates the hash and puts it in n.cached. _, err := n.Encoded(false) if err != nil { return nil, err From 850558e8749cc498d45a88118f750a88bc98c40c Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 11 Oct 2014 06:33:57 -0700 Subject: [PATCH 0260/3817] dht handleAddProviders adds addr in msg Otherwise don't have the peer's target address. This commit was moved from ipfs/go-ipfs-routing@e7c7c714bcdf0cf1e66bf92237263b0ac7e22202 --- routing/dht/Message.go | 11 +++++++++++ routing/dht/dht.go | 4 ++++ routing/dht/handlers.go | 13 +++++++++++++ 3 files changed, 28 insertions(+) diff --git a/routing/dht/Message.go b/routing/dht/Message.go index e4607f1de..526724287 100644 --- a/routing/dht/Message.go +++ b/routing/dht/Message.go @@ -1,7 +1,10 @@ package dht import ( + "errors" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" peer "github.com/jbenet/go-ipfs/peer" ) @@ -35,6 +38,14 @@ func peersToPBPeers(peers []*peer.Peer) []*Message_Peer { return pbpeers } +// Address returns a multiaddr associated with the Message_Peer entry +func (m *Message_Peer) Address() (ma.Multiaddr, error) { + if m == nil { + return nil, errors.New("MessagePeer is nil") + } + return ma.NewMultiaddr(*m.Addr) +} + // GetClusterLevel gets and adjusts the cluster level on the message. // a +/- 1 adjustment is needed to distinguish a valid first level (1) and // default "no value" protobuf behavior (0) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index c95e07511..1c5beedac 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -216,6 +216,10 @@ func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p *peer.Peer, func (dht *IpfsDHT) putProvider(ctx context.Context, p *peer.Peer, key string) error { pmes := newMessage(Message_ADD_PROVIDER, string(key), 0) + + // add self as the provider + pmes.ProviderPeers = peersToPBPeers([]*peer.Peer{dht.self}) + rpmes, err := dht.sendRequest(ctx, p, pmes) if err != nil { return err diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 417dd0918..0c739ab17 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -175,6 +175,19 @@ func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *Message) (*Message, er log.Debug("%s adding %s as a provider for '%s'\n", dht.self, p, peer.ID(key)) + // add provider should use the address given in the message + for _, pb := range pmes.GetCloserPeers() { + if peer.ID(pb.GetId()).Equal(p.ID) { + + addr, err := pb.Address() + if err != nil { + log.Error("provider %s error with address %s", p, *pb.Addr) + continue + } + p.AddAddress(addr) + } + } + dht.providers.AddProvider(key, p) return pmes, nil // send back same msg as confirmation. } From ed257a30956f0754602fcf069016091735af063c Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 13 Oct 2014 01:31:51 -0700 Subject: [PATCH 0261/3817] logging + tweaks This commit was moved from ipfs/go-ipfs-routing@1bc91b14e9415aa7dd83cda4a56e4c41349e3b0e --- routing/dht/dht.go | 3 ++- routing/dht/handlers.go | 12 +++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 1c5beedac..6486ce40d 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -472,7 +472,7 @@ func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (*peer.Peer, error) { } if p == nil { - maddr, err := ma.NewMultiaddr(pbp.GetAddr()) + maddr, err := pbp.Address() if err != nil { return nil, err } @@ -481,6 +481,7 @@ func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (*peer.Peer, error) { p = &peer.Peer{ID: id} p.AddAddress(maddr) dht.peerstore.Put(p) + log.Info("dht found new peer: %s %s", p, maddr) } return p, nil } diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 0c739ab17..0fcbb2be6 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -176,19 +176,25 @@ func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *Message) (*Message, er log.Debug("%s adding %s as a provider for '%s'\n", dht.self, p, peer.ID(key)) // add provider should use the address given in the message - for _, pb := range pmes.GetCloserPeers() { - if peer.ID(pb.GetId()).Equal(p.ID) { + for _, pb := range pmes.GetProviderPeers() { + pid := peer.ID(pb.GetId()) + if pid.Equal(p.ID) { addr, err := pb.Address() if err != nil { log.Error("provider %s error with address %s", p, *pb.Addr) continue } + + log.Info("received provider %s %s for %s", p, addr, key) p.AddAddress(addr) + dht.providers.AddProvider(key, p) + + } else { + log.Error("handleAddProvider received provider %s from %s", pid, p) } } - dht.providers.AddProvider(key, p) return pmes, nil // send back same msg as confirmation. } From 5ecb22f87ca460adf244ba29b886b2420c33e005 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 11 Oct 2014 10:43:54 -0700 Subject: [PATCH 0262/3817] fix up FindProvidersAsync This commit was moved from ipfs/go-ipfs-routing@f477e5c1b9269f410408c86546c7bf0a53129848 --- routing/dht/dht.go | 6 ++-- routing/dht/dht_test.go | 15 +++++---- routing/dht/routing.go | 75 +++++------------------------------------ routing/routing.go | 4 --- 4 files changed, 20 insertions(+), 80 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 6486ce40d..bfaa16735 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -368,8 +368,8 @@ func (dht *IpfsDHT) Update(p *peer.Peer) { // after some deadline of inactivity. } -// Find looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in. -func (dht *IpfsDHT) Find(id peer.ID) (*peer.Peer, *kb.RoutingTable) { +// FindLocal looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in. +func (dht *IpfsDHT) FindLocal(id peer.ID) (*peer.Peer, *kb.RoutingTable) { for _, table := range dht.routingTables { p := table.Find(id) if p != nil { @@ -465,7 +465,7 @@ func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (*peer.Peer, error) { p, _ := dht.peerstore.Get(id) if p == nil { - p, _ = dht.Find(id) + p, _ = dht.FindLocal(id) if p != nil { panic("somehow peer not getting into peerstore") } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 1d7413fd5..bedda1afc 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -227,13 +227,16 @@ func TestProvides(t *testing.T) { time.Sleep(time.Millisecond * 60) ctxT, _ := context.WithTimeout(context.Background(), time.Second) - provs, err := dhts[0].FindProviders(ctxT, u.Key("hello")) - if err != nil { - t.Fatal(err) - } + provchan := dhts[0].FindProvidersAsync(ctxT, u.Key("hello"), 1) - if len(provs) != 1 { - t.Fatal("Didnt get back providers") + after := time.After(time.Second) + select { + case prov := <-provchan: + if prov == nil { + t.Fatal("Got back nil provider") + } + case <-after: + t.Fatal("Did not get a provider back.") } } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index c14031ce2..03d94d118 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -3,6 +3,7 @@ package dht import ( "bytes" "encoding/json" + "sync" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -117,26 +118,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { return nil } -// NB: not actually async. Used to keep the interface consistent while the -// actual async method, FindProvidersAsync2 is under construction func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int) <-chan *peer.Peer { - ch := make(chan *peer.Peer) - providers, err := dht.FindProviders(ctx, key) - if err != nil { - close(ch) - return ch - } - go func() { - defer close(ch) - for _, p := range providers { - ch <- p - } - }() - return ch -} - -// FIXME: there's a bug here! -func (dht *IpfsDHT) FindProvidersAsync2(ctx context.Context, key u.Key, count int) <-chan *peer.Peer { peerOut := make(chan *peer.Peer, count) go func() { ps := newPeerSet() @@ -151,9 +133,12 @@ func (dht *IpfsDHT) FindProvidersAsync2(ctx context.Context, key u.Key, count in } } + wg := new(sync.WaitGroup) peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), AlphaValue) for _, pp := range peers { + wg.Add(1) go func(p *peer.Peer) { + defer wg.Done() pmes, err := dht.findProvidersSingle(ctx, p, key, 0) if err != nil { log.Error("%s", err) @@ -162,7 +147,8 @@ func (dht *IpfsDHT) FindProvidersAsync2(ctx context.Context, key u.Key, count in dht.addPeerListAsync(key, pmes.GetProviderPeers(), ps, count, peerOut) }(pp) } - + wg.Wait() + close(peerOut) }() return peerOut } @@ -186,61 +172,16 @@ func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*Message_Peer, ps *peerSet } } -// FindProviders searches for peers who can provide the value for given key. -func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]*peer.Peer, error) { - // get closest peer - log.Debug("Find providers for: '%s'", key) - p := dht.routingTables[0].NearestPeer(kb.ConvertKey(key)) - if p == nil { - log.Warning("Got no nearest peer for find providers: '%s'", key) - return nil, nil - } - - for level := 0; level < len(dht.routingTables); { - - // attempt retrieving providers - pmes, err := dht.findProvidersSingle(ctx, p, key, level) - if err != nil { - return nil, err - } - - // handle providers - provs := pmes.GetProviderPeers() - if provs != nil { - log.Debug("Got providers back from findProviders call!") - return dht.addProviders(key, provs), nil - } - - log.Debug("Didnt get providers, just closer peers.") - closer := pmes.GetCloserPeers() - if len(closer) == 0 { - level++ - continue - } - - np, err := dht.peerFromInfo(closer[0]) - if err != nil { - log.Debug("no peerFromInfo") - level++ - continue - } - p = np - } - return nil, u.ErrNotFound -} - // Find specific Peer - // FindPeer searches for a peer with given ID. func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (*peer.Peer, error) { // Check if were already connected to them - p, _ := dht.Find(id) + p, _ := dht.FindLocal(id) if p != nil { return p, nil } - // @whyrusleeping why is this here? doesn't the dht.Find above cover it? routeLevel := 0 p = dht.routingTables[routeLevel].NearestPeer(kb.ConvertPeerID(id)) if p == nil { @@ -277,7 +218,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (*peer.Peer, error func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (*peer.Peer, error) { // Check if were already connected to them - p, _ := dht.Find(id) + p, _ := dht.FindLocal(id) if p != nil { return p, nil } diff --git a/routing/routing.go b/routing/routing.go index 4669fb48c..f3dd0c9d8 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -26,11 +26,7 @@ type IpfsRouting interface { // Announce that this node can provide value for given key Provide(context.Context, u.Key) error - // FindProviders searches for peers who can provide the value for given key. - FindProviders(context.Context, u.Key) ([]*peer.Peer, error) - // Find specific Peer - // FindPeer searches for a peer with given ID. FindPeer(context.Context, peer.ID) (*peer.Peer, error) } From 8f3c9ca3a2ce8c1274f10df25623b41b0148b46f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 12 Oct 2014 23:07:30 -0700 Subject: [PATCH 0263/3817] fix bug in diagnostics, and add more peers to closer peer responses This commit was moved from ipfs/go-ipfs-routing@5505c12e2266dc8970ad9df123905d9fe22eab30 --- routing/dht/dht.go | 93 +++++++++++++++++++++++++---------------- routing/dht/handlers.go | 36 +++++++++------- 2 files changed, 78 insertions(+), 51 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index bfaa16735..e00b82bf2 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -76,7 +76,7 @@ func NewDHT(p *peer.Peer, ps peer.Peerstore, net inet.Network, sender inet.Sende // Connect to a new peer at the given address, ping and add to the routing table func (dht *IpfsDHT) Connect(ctx context.Context, npeer *peer.Peer) (*peer.Peer, error) { - log.Debug("Connect to new peer: %s\n", npeer) + log.Debug("Connect to new peer: %s", npeer) // TODO(jbenet,whyrusleeping) // @@ -109,13 +109,13 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N mData := mes.Data() if mData == nil { - // TODO handle/log err + log.Error("Message contained nil data.") return nil } mPeer := mes.Peer() if mPeer == nil { - // TODO handle/log err + log.Error("Message contained nil peer.") return nil } @@ -123,7 +123,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N pmes := new(Message) err := proto.Unmarshal(mData, pmes) if err != nil { - // TODO handle/log err + log.Error("Error unmarshaling data") return nil } @@ -138,25 +138,27 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N handler := dht.handlerForMsgType(pmes.GetType()) if handler == nil { // TODO handle/log err + log.Error("got back nil handler from handlerForMsgType") return nil } // dispatch handler. rpmes, err := handler(mPeer, pmes) if err != nil { - // TODO handle/log err + log.Error("handle message error: %s", err) return nil } // if nil response, return it before serializing if rpmes == nil { + log.Warning("Got back nil response from request.") return nil } // serialize response msg rmes, err := msg.FromObject(mPeer, rpmes) if err != nil { - // TODO handle/log err + log.Error("serialze response error: %s", err) return nil } @@ -197,6 +199,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p *peer.Peer, pmes *Message return rpmes, nil } +// putValueToNetwork stores the given key/value pair at the peer 'p' func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p *peer.Peer, key string, value []byte) error { @@ -226,7 +229,7 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p *peer.Peer, key string) e } log.Debug("%s putProvider: %s for %s", dht.self, p, key) - if *rpmes.Key != *pmes.Key { + if rpmes.GetKey() != pmes.GetKey() { return errors.New("provider not added correctly") } @@ -261,23 +264,11 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, // Perhaps we were given closer peers var peers []*peer.Peer for _, pb := range pmes.GetCloserPeers() { - if peer.ID(pb.GetId()).Equal(dht.self.ID) { - continue - } - - addr, err := ma.NewMultiaddr(pb.GetAddr()) + pr, err := dht.addPeer(pb) if err != nil { - log.Error("%v", err.Error()) + log.Error("%s", err) continue } - - // check if we already have this peer. - pr, _ := dht.peerstore.Get(peer.ID(pb.GetId())) - if pr == nil { - pr = &peer.Peer{ID: peer.ID(pb.GetId())} - dht.peerstore.Put(pr) - } - pr.AddAddress(addr) // idempotent peers = append(peers, pr) } @@ -290,6 +281,27 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, return nil, nil, u.ErrNotFound } +func (dht *IpfsDHT) addPeer(pb *Message_Peer) (*peer.Peer, error) { + if peer.ID(pb.GetId()).Equal(dht.self.ID) { + return nil, errors.New("cannot add self as peer") + } + + addr, err := ma.NewMultiaddr(pb.GetAddr()) + if err != nil { + return nil, err + } + + // check if we already have this peer. + pr, _ := dht.peerstore.Get(peer.ID(pb.GetId())) + if pr == nil { + pr = &peer.Peer{ID: peer.ID(pb.GetId())} + dht.peerstore.Put(pr) + } + pr.AddAddress(addr) // idempotent + + return pr, nil +} + // getValueSingle simply performs the get value RPC with the given parameters func (dht *IpfsDHT) getValueSingle(ctx context.Context, p *peer.Peer, key u.Key, level int) (*Message, error) { @@ -327,6 +339,7 @@ func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key, return nil, u.ErrNotFound } +// getLocal attempts to retrieve the value from the datastore func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { dht.dslock.Lock() defer dht.dslock.Unlock() @@ -342,6 +355,7 @@ func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { return byt, nil } +// putLocal stores the key value pair in the datastore func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { return dht.datastore.Put(key.DsKey(), value) } @@ -419,39 +433,44 @@ func (dht *IpfsDHT) addProviders(key u.Key, peers []*Message_Peer) []*peer.Peer return provArr } -// nearestPeerToQuery returns the routing tables closest peers. -func (dht *IpfsDHT) nearestPeerToQuery(pmes *Message) *peer.Peer { +// nearestPeersToQuery returns the routing tables closest peers. +func (dht *IpfsDHT) nearestPeersToQuery(pmes *Message, count int) []*peer.Peer { level := pmes.GetClusterLevel() cluster := dht.routingTables[level] key := u.Key(pmes.GetKey()) - closer := cluster.NearestPeer(kb.ConvertKey(key)) + closer := cluster.NearestPeers(kb.ConvertKey(key), count) return closer } -// betterPeerToQuery returns nearestPeerToQuery, but iff closer than self. -func (dht *IpfsDHT) betterPeerToQuery(pmes *Message) *peer.Peer { - closer := dht.nearestPeerToQuery(pmes) +// betterPeerToQuery returns nearestPeersToQuery, but iff closer than self. +func (dht *IpfsDHT) betterPeersToQuery(pmes *Message, count int) []*peer.Peer { + closer := dht.nearestPeersToQuery(pmes, count) // no node? nil if closer == nil { return nil } - // == to self? nil - if closer.ID.Equal(dht.self.ID) { - log.Error("Attempted to return self! this shouldnt happen...") - return nil + // == to self? thats bad + for _, p := range closer { + if p.ID.Equal(dht.self.ID) { + log.Error("Attempted to return self! this shouldnt happen...") + return nil + } } - // self is closer? nil - key := u.Key(pmes.GetKey()) - if kb.Closer(dht.self.ID, closer.ID, key) { - return nil + var filtered []*peer.Peer + for _, p := range closer { + // must all be closer than self + key := u.Key(pmes.GetKey()) + if !kb.Closer(dht.self.ID, p.ID, key) { + filtered = append(filtered, p) + } } - // ok seems like a closer node. - return closer + // ok seems like closer nodes + return filtered } func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (*peer.Peer, error) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 0fcbb2be6..4a9de160e 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -13,6 +13,8 @@ import ( ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" ) +var CloserPeerCount = 4 + // dhthandler specifies the signature of functions that handle DHT messages. type dhtHandler func(*peer.Peer, *Message) (*Message, error) @@ -83,10 +85,12 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error } // Find closest peer on given cluster to desired key and reply with that info - closer := dht.betterPeerToQuery(pmes) + closer := dht.betterPeersToQuery(pmes, CloserPeerCount) if closer != nil { - log.Debug("handleGetValue returning a closer peer: '%s'\n", closer) - resp.CloserPeers = peersToPBPeers([]*peer.Peer{closer}) + for _, p := range closer { + log.Debug("handleGetValue returning closer peer: '%s'", p) + } + resp.CloserPeers = peersToPBPeers(closer) } return resp, nil @@ -109,27 +113,31 @@ func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *Message) (*Message, error) { func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *Message) (*Message, error) { resp := newMessage(pmes.GetType(), "", pmes.GetClusterLevel()) - var closest *peer.Peer + var closest []*peer.Peer // if looking for self... special case where we send it on CloserPeers. if peer.ID(pmes.GetKey()).Equal(dht.self.ID) { - closest = dht.self + closest = []*peer.Peer{dht.self} } else { - closest = dht.betterPeerToQuery(pmes) + closest = dht.betterPeersToQuery(pmes, CloserPeerCount) } if closest == nil { - log.Error("handleFindPeer: could not find anything.\n") + log.Error("handleFindPeer: could not find anything.") return resp, nil } - if len(closest.Addresses) == 0 { - log.Error("handleFindPeer: no addresses for connected peer...\n") - return resp, nil + var withAddresses []*peer.Peer + for _, p := range closest { + if len(p.Addresses) > 0 { + withAddresses = append(withAddresses, p) + } } - log.Debug("handleFindPeer: sending back '%s'\n", closest) - resp.CloserPeers = peersToPBPeers([]*peer.Peer{closest}) + for _, p := range withAddresses { + log.Debug("handleFindPeer: sending back '%s'", p) + } + resp.CloserPeers = peersToPBPeers(withAddresses) return resp, nil } @@ -157,9 +165,9 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *Message) (*Message, e } // Also send closer peers. - closer := dht.betterPeerToQuery(pmes) + closer := dht.betterPeersToQuery(pmes, CloserPeerCount) if closer != nil { - resp.CloserPeers = peersToPBPeers([]*peer.Peer{closer}) + resp.CloserPeers = peersToPBPeers(closer) } return resp, nil From 935d32ed387e64e7fdb11b3beded7e93c0a57ede Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 14 Oct 2014 13:40:55 -0700 Subject: [PATCH 0264/3817] Add test to test conncurrent connects between two peers This commit was moved from ipfs/go-ipfs-routing@09c0deffb50388ab491ef8649b98ba7722e85ce4 --- routing/dht/dht_test.go | 44 +++++++++++++++++++++++++++++++++++++++++ routing/dht/ext_test.go | 4 ++++ 2 files changed, 48 insertions(+) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index bedda1afc..9e8251a43 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -390,3 +390,47 @@ func TestFindPeer(t *testing.T) { t.Fatal("Didnt find expected peer.") } } + +func TestConnectCollision(t *testing.T) { + // t.Skip("skipping test to debug another") + + u.Debug = false + addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") + if err != nil { + t.Fatal(err) + } + addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5679") + if err != nil { + t.Fatal(err) + } + + peerA := makePeer(addrA) + peerB := makePeer(addrB) + + dhtA := setupDHT(t, peerA) + dhtB := setupDHT(t, peerB) + + defer dhtA.Halt() + defer dhtB.Halt() + defer dhtA.network.Close() + defer dhtB.network.Close() + + done := make(chan struct{}) + go func() { + _, err = dhtA.Connect(context.Background(), peerB) + if err != nil { + t.Fatal(err) + } + done <- struct{}{} + }() + go func() { + _, err = dhtB.Connect(context.Background(), peerA) + if err != nil { + t.Fatal(err) + } + done <- struct{}{} + }() + + <-done + <-done +} diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 88f512378..ca88a83f4 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -92,6 +92,10 @@ func (f *fauxNet) GetPeerList() []*peer.Peer { return nil } +func (f *fauxNet) GetBandwidthTotals() (uint64, uint64) { + return 0, 0 +} + // Close terminates all network operation func (f *fauxNet) Close() error { return nil } From 49ee8a5e42b4117323d96d5b24a15cedff6e26b3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 14 Oct 2014 17:46:11 -0700 Subject: [PATCH 0265/3817] make test fail instead of hang This commit was moved from ipfs/go-ipfs-routing@d8426038417a7a4dcc8391f0e5f0409809cb418b --- routing/dht/dht_test.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 9e8251a43..84f5f830e 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -431,6 +431,15 @@ func TestConnectCollision(t *testing.T) { done <- struct{}{} }() - <-done - <-done + timeout := time.After(time.Second * 5) + select { + case <-done: + case <-timeout: + t.Fatal("Timeout received!") + } + select { + case <-done: + case <-timeout: + t.Fatal("Timeout received!") + } } From eff070f32a39b10f3c45e8108dcae6180362a3ce Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 15 Oct 2014 12:30:52 -0700 Subject: [PATCH 0266/3817] some dht cleanup, and make DHTs take a master context This commit was moved from ipfs/go-ipfs-routing@e55b78bb514e3b440ff9928fcc0c116336631d98 --- routing/dht/dht.go | 31 +++++++++++++++++++--- routing/dht/dht_logger.go | 7 ++++- routing/dht/handlers.go | 54 -------------------------------------- routing/dht/messages.pb.go | 17 +++++------- routing/dht/messages.proto | 7 +++-- routing/dht/routing.go | 33 +---------------------- 6 files changed, 44 insertions(+), 105 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index e00b82bf2..22523f0bb 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -53,16 +53,19 @@ type IpfsDHT struct { //lock to make diagnostics work better diaglock sync.Mutex + + ctx context.Context } // NewDHT creates a new DHT object with the given peer as the 'local' host -func NewDHT(p *peer.Peer, ps peer.Peerstore, net inet.Network, sender inet.Sender, dstore ds.Datastore) *IpfsDHT { +func NewDHT(ctx context.Context, p *peer.Peer, ps peer.Peerstore, net inet.Network, sender inet.Sender, dstore ds.Datastore) *IpfsDHT { dht := new(IpfsDHT) dht.network = net dht.sender = sender dht.datastore = dstore dht.self = p dht.peerstore = ps + dht.ctx = ctx dht.providers = NewProviderManager(p.ID) @@ -71,6 +74,8 @@ func NewDHT(p *peer.Peer, ps peer.Peerstore, net inet.Network, sender inet.Sende dht.routingTables[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*1000) dht.routingTables[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Hour) dht.birth = time.Now() + + go dht.PingRoutine(time.Second * 10) return dht } @@ -137,7 +142,6 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N // get handler for this msg type. handler := dht.handlerForMsgType(pmes.GetType()) if handler == nil { - // TODO handle/log err log.Error("got back nil handler from handlerForMsgType") return nil } @@ -350,7 +354,7 @@ func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { byt, ok := v.([]byte) if !ok { - return byt, errors.New("value stored in datastore not []byte") + return nil, errors.New("value stored in datastore not []byte") } return byt, nil } @@ -533,6 +537,27 @@ func (dht *IpfsDHT) loadProvidableKeys() error { return nil } +func (dht *IpfsDHT) PingRoutine(t time.Duration) { + tick := time.Tick(t) + for { + select { + case <-tick: + id := make([]byte, 16) + rand.Read(id) + peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(u.Key(id)), 5) + for _, p := range peers { + ctx, _ := context.WithTimeout(dht.ctx, time.Second*5) + err := dht.Ping(ctx, p) + if err != nil { + log.Error("Ping error: %s", err) + } + } + case <-dht.ctx.Done(): + return + } + } +} + // Bootstrap builds up list of peers by requesting random peer IDs func (dht *IpfsDHT) Bootstrap(ctx context.Context) { id := make([]byte, 16) diff --git a/routing/dht/dht_logger.go b/routing/dht/dht_logger.go index 1a0878bf7..0ff012956 100644 --- a/routing/dht/dht_logger.go +++ b/routing/dht/dht_logger.go @@ -2,6 +2,7 @@ package dht import ( "encoding/json" + "fmt" "time" ) @@ -29,12 +30,16 @@ func (l *logDhtRPC) EndLog() { func (l *logDhtRPC) Print() { b, err := json.Marshal(l) if err != nil { - log.Debug(err.Error()) + log.Debug("Error marshaling logDhtRPC object: %s", err) } else { log.Debug(string(b)) } } +func (l *logDhtRPC) String() string { + return fmt.Sprintf("DHT RPC: %s took %s, success = %s", l.Type, l.Duration, l.Success) +} + func (l *logDhtRPC) EndAndPrint() { l.EndLog() l.Print() diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 4a9de160e..1046516b6 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -5,9 +5,7 @@ import ( "fmt" "time" - msg "github.com/jbenet/go-ipfs/net/message" peer "github.com/jbenet/go-ipfs/peer" - kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" @@ -32,8 +30,6 @@ func (dht *IpfsDHT) handlerForMsgType(t Message_MessageType) dhtHandler { return dht.handleGetProviders case Message_PING: return dht.handlePing - case Message_DIAGNOSTIC: - return dht.handleDiagnostic default: return nil } @@ -211,53 +207,3 @@ func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *Message) (*Message, er func (dht *IpfsDHT) Halt() { dht.providers.Halt() } - -// NOTE: not yet finished, low priority -func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *Message) (*Message, error) { - seq := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) - - for _, ps := range seq { - _, err := msg.FromObject(ps, pmes) - if err != nil { - log.Error("handleDiagnostics error creating message: %v\n", err) - continue - } - // dht.sender.SendRequest(context.TODO(), mes) - } - return nil, errors.New("not yet ported back") - - // buf := new(bytes.Buffer) - // di := dht.getDiagInfo() - // buf.Write(di.Marshal()) - // - // // NOTE: this shouldnt be a hardcoded value - // after := time.After(time.Second * 20) - // count := len(seq) - // for count > 0 { - // select { - // case <-after: - // //Timeout, return what we have - // goto out - // case reqResp := <-listenChan: - // pmesOut := new(Message) - // err := proto.Unmarshal(reqResp.Data, pmesOut) - // if err != nil { - // // It broke? eh, whatever, keep going - // continue - // } - // buf.Write(reqResp.Data) - // count-- - // } - // } - // - // out: - // resp := Message{ - // Type: Message_DIAGNOSTIC, - // ID: pmes.GetId(), - // Value: buf.Bytes(), - // Response: true, - // } - // - // mes := swarm.NewMessage(p, resp.ToProtobuf()) - // dht.netChan.Outgoing <- mes -} diff --git a/routing/dht/messages.pb.go b/routing/dht/messages.pb.go index b6e9fa4f2..f204544ea 100644 --- a/routing/dht/messages.pb.go +++ b/routing/dht/messages.pb.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-gogo. +// Code generated by protoc-gen-go. // source: messages.proto // DO NOT EDIT! @@ -13,13 +13,11 @@ It has these top-level messages: */ package dht -import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" -import json "encoding/json" +import proto "code.google.com/p/goprotobuf/proto" import math "math" -// Reference proto, json, and math imports to suppress error if they are not otherwise used. +// Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal -var _ = &json.SyntaxError{} var _ = math.Inf type Message_MessageType int32 @@ -31,7 +29,6 @@ const ( Message_GET_PROVIDERS Message_MessageType = 3 Message_FIND_NODE Message_MessageType = 4 Message_PING Message_MessageType = 5 - Message_DIAGNOSTIC Message_MessageType = 6 ) var Message_MessageType_name = map[int32]string{ @@ -41,7 +38,6 @@ var Message_MessageType_name = map[int32]string{ 3: "GET_PROVIDERS", 4: "FIND_NODE", 5: "PING", - 6: "DIAGNOSTIC", } var Message_MessageType_value = map[string]int32{ "PUT_VALUE": 0, @@ -50,7 +46,6 @@ var Message_MessageType_value = map[string]int32{ "GET_PROVIDERS": 3, "FIND_NODE": 4, "PING": 5, - "DIAGNOSTIC": 6, } func (x Message_MessageType) Enum() *Message_MessageType { @@ -72,7 +67,7 @@ func (x *Message_MessageType) UnmarshalJSON(data []byte) error { type Message struct { // defines what type of message it is. - Type *Message_MessageType `protobuf:"varint,1,req,name=type,enum=dht.Message_MessageType" json:"type,omitempty"` + Type *Message_MessageType `protobuf:"varint,1,opt,name=type,enum=dht.Message_MessageType" json:"type,omitempty"` // defines what coral cluster level this query/response belongs to. ClusterLevelRaw *int32 `protobuf:"varint,10,opt,name=clusterLevelRaw" json:"clusterLevelRaw,omitempty"` // Used to specify the key associated with this message. @@ -137,8 +132,8 @@ func (m *Message) GetProviderPeers() []*Message_Peer { } type Message_Peer struct { - Id *string `protobuf:"bytes,1,req,name=id" json:"id,omitempty"` - Addr *string `protobuf:"bytes,2,req,name=addr" json:"addr,omitempty"` + Id *string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` + Addr *string `protobuf:"bytes,2,opt,name=addr" json:"addr,omitempty"` XXX_unrecognized []byte `json:"-"` } diff --git a/routing/dht/messages.proto b/routing/dht/messages.proto index 3c33f9382..067690150 100644 --- a/routing/dht/messages.proto +++ b/routing/dht/messages.proto @@ -10,16 +10,15 @@ message Message { GET_PROVIDERS = 3; FIND_NODE = 4; PING = 5; - DIAGNOSTIC = 6; } message Peer { - required string id = 1; - required string addr = 2; + optional string id = 1; + optional string addr = 2; } // defines what type of message it is. - required MessageType type = 1; + optional MessageType type = 1; // defines what coral cluster level this query/response belongs to. optional int32 clusterLevelRaw = 10; diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 03d94d118..55ef265cb 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -1,8 +1,6 @@ package dht import ( - "bytes" - "encoding/json" "sync" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -62,6 +60,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { routeLevel := 0 closest := dht.routingTables[routeLevel].NearestPeers(kb.ConvertKey(key), PoolSize) if closest == nil || len(closest) == 0 { + log.Warning("Got no peers back from routing table!") return nil, nil } @@ -282,33 +281,3 @@ func (dht *IpfsDHT) Ping(ctx context.Context, p *peer.Peer) error { log.Info("ping %s end (err = %s)", p, err) return err } - -func (dht *IpfsDHT) getDiagnostic(ctx context.Context) ([]*diagInfo, error) { - - log.Info("Begin Diagnostic") - peers := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) - var out []*diagInfo - - query := newQuery(dht.self.Key(), func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) { - pmes := newMessage(Message_DIAGNOSTIC, "", 0) - rpmes, err := dht.sendRequest(ctx, p, pmes) - if err != nil { - return nil, err - } - - dec := json.NewDecoder(bytes.NewBuffer(rpmes.GetValue())) - for { - di := new(diagInfo) - err := dec.Decode(di) - if err != nil { - break - } - - out = append(out, di) - } - return &dhtQueryResult{success: true}, nil - }) - - _, err := query.Run(ctx, peers) - return out, err -} From c29287257cc7a747c08a4060178acae98d177e35 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 17 Oct 2014 17:54:47 -0700 Subject: [PATCH 0267/3817] small changes to auxiliary dht functions This commit was moved from ipfs/go-ipfs-routing@9aae21190ac3200218dd8066732dde52e30d64dd --- routing/dht/dht.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 22523f0bb..2d4c9852e 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -23,6 +23,8 @@ import ( var log = u.Logger("dht") +const doPinging = true + // TODO. SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-dht/index.js // IpfsDHT is an implementation of Kademlia with Coral and S/Kademlia modifications. @@ -75,7 +77,9 @@ func NewDHT(ctx context.Context, p *peer.Peer, ps peer.Peerstore, net inet.Netwo dht.routingTables[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Hour) dht.birth = time.Now() - go dht.PingRoutine(time.Second * 10) + if doPinging { + go dht.PingRoutine(time.Second * 10) + } return dht } @@ -562,5 +566,8 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { func (dht *IpfsDHT) Bootstrap(ctx context.Context) { id := make([]byte, 16) rand.Read(id) - dht.FindPeer(ctx, peer.ID(id)) + _, err := dht.FindPeer(ctx, peer.ID(id)) + if err != nil { + log.Error("Bootstrap peer error: %s", err) + } } From 4011db44c60ab07c9a129823b2b04c9f26959b4f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 18 Oct 2014 04:19:12 -0700 Subject: [PATCH 0268/3817] dht tests with context This commit was moved from ipfs/go-ipfs-routing@ec874c217b2d19aae628dfcbd5a4c589f96f1a81 --- routing/dht/dht_test.go | 102 +++++++++++++++++++++------------------- routing/dht/ext_test.go | 12 +++-- 2 files changed, 60 insertions(+), 54 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 84f5f830e..36f27a222 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -21,9 +21,7 @@ import ( "time" ) -func setupDHT(t *testing.T, p *peer.Peer) *IpfsDHT { - ctx := context.Background() - +func setupDHT(ctx context.Context, t *testing.T, p *peer.Peer) *IpfsDHT { peerstore := peer.NewPeerstore() dhts := netservice.NewService(nil) // nil handler for now, need to patch it @@ -38,12 +36,12 @@ func setupDHT(t *testing.T, p *peer.Peer) *IpfsDHT { t.Fatal(err) } - d := NewDHT(p, peerstore, net, dhts, ds.NewMapDatastore()) + d := NewDHT(ctx, p, peerstore, net, dhts, ds.NewMapDatastore()) dhts.SetHandler(d) return d } -func setupDHTS(n int, t *testing.T) ([]ma.Multiaddr, []*peer.Peer, []*IpfsDHT) { +func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []*peer.Peer, []*IpfsDHT) { var addrs []ma.Multiaddr for i := 0; i < n; i++ { a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) @@ -61,7 +59,7 @@ func setupDHTS(n int, t *testing.T) ([]ma.Multiaddr, []*peer.Peer, []*IpfsDHT) { dhts := make([]*IpfsDHT, n) for i := 0; i < n; i++ { - dhts[i] = setupDHT(t, peers[i]) + dhts[i] = setupDHT(ctx, t, peers[i]) } return addrs, peers, dhts @@ -87,7 +85,7 @@ func makePeer(addr ma.Multiaddr) *peer.Peer { func TestPing(t *testing.T) { // t.Skip("skipping test to debug another") - + ctx := context.Background() u.Debug = false addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/2222") if err != nil { @@ -101,28 +99,28 @@ func TestPing(t *testing.T) { peerA := makePeer(addrA) peerB := makePeer(addrB) - dhtA := setupDHT(t, peerA) - dhtB := setupDHT(t, peerB) + dhtA := setupDHT(ctx, t, peerA) + dhtB := setupDHT(ctx, t, peerB) defer dhtA.Halt() defer dhtB.Halt() defer dhtA.network.Close() defer dhtB.network.Close() - _, err = dhtA.Connect(context.Background(), peerB) + _, err = dhtA.Connect(ctx, peerB) if err != nil { t.Fatal(err) } //Test that we can ping the node - ctx, _ := context.WithTimeout(context.Background(), 5*time.Millisecond) - err = dhtA.Ping(ctx, peerB) + ctxT, _ := context.WithTimeout(ctx, 5*time.Millisecond) + err = dhtA.Ping(ctxT, peerB) if err != nil { t.Fatal(err) } - ctx, _ = context.WithTimeout(context.Background(), 5*time.Millisecond) - err = dhtB.Ping(ctx, peerA) + ctxT, _ = context.WithTimeout(ctx, 5*time.Millisecond) + err = dhtB.Ping(ctxT, peerA) if err != nil { t.Fatal(err) } @@ -131,6 +129,7 @@ func TestPing(t *testing.T) { func TestValueGetSet(t *testing.T) { // t.Skip("skipping test to debug another") + ctx := context.Background() u.Debug = false addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") if err != nil { @@ -144,23 +143,23 @@ func TestValueGetSet(t *testing.T) { peerA := makePeer(addrA) peerB := makePeer(addrB) - dhtA := setupDHT(t, peerA) - dhtB := setupDHT(t, peerB) + dhtA := setupDHT(ctx, t, peerA) + dhtB := setupDHT(ctx, t, peerB) defer dhtA.Halt() defer dhtB.Halt() defer dhtA.network.Close() defer dhtB.network.Close() - _, err = dhtA.Connect(context.Background(), peerB) + _, err = dhtA.Connect(ctx, peerB) if err != nil { t.Fatal(err) } - ctxT, _ := context.WithTimeout(context.Background(), time.Second) + ctxT, _ := context.WithTimeout(ctx, time.Second) dhtA.PutValue(ctxT, "hello", []byte("world")) - ctxT, _ = context.WithTimeout(context.Background(), time.Second*2) + ctxT, _ = context.WithTimeout(ctx, time.Second*2) val, err := dhtA.GetValue(ctxT, "hello") if err != nil { t.Fatal(err) @@ -170,7 +169,7 @@ func TestValueGetSet(t *testing.T) { t.Fatalf("Expected 'world' got '%s'", string(val)) } - ctxT, _ = context.WithTimeout(context.Background(), time.Second*2) + ctxT, _ = context.WithTimeout(ctx, time.Second*2) val, err = dhtB.GetValue(ctxT, "hello") if err != nil { t.Fatal(err) @@ -183,10 +182,11 @@ func TestValueGetSet(t *testing.T) { func TestProvides(t *testing.T) { // t.Skip("skipping test to debug another") + ctx := context.Background() u.Debug = false - _, peers, dhts := setupDHTS(4, t) + _, peers, dhts := setupDHTS(ctx, 4, t) defer func() { for i := 0; i < 4; i++ { dhts[i].Halt() @@ -194,17 +194,17 @@ func TestProvides(t *testing.T) { } }() - _, err := dhts[0].Connect(context.Background(), peers[1]) + _, err := dhts[0].Connect(ctx, peers[1]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(context.Background(), peers[2]) + _, err = dhts[1].Connect(ctx, peers[2]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(context.Background(), peers[3]) + _, err = dhts[1].Connect(ctx, peers[3]) if err != nil { t.Fatal(err) } @@ -219,14 +219,14 @@ func TestProvides(t *testing.T) { t.Fatal(err) } - err = dhts[3].Provide(context.Background(), u.Key("hello")) + err = dhts[3].Provide(ctx, u.Key("hello")) if err != nil { t.Fatal(err) } time.Sleep(time.Millisecond * 60) - ctxT, _ := context.WithTimeout(context.Background(), time.Second) + ctxT, _ := context.WithTimeout(ctx, time.Second) provchan := dhts[0].FindProvidersAsync(ctxT, u.Key("hello"), 1) after := time.After(time.Second) @@ -243,9 +243,10 @@ func TestProvides(t *testing.T) { func TestProvidesAsync(t *testing.T) { // t.Skip("skipping test to debug another") + ctx := context.Background() u.Debug = false - _, peers, dhts := setupDHTS(4, t) + _, peers, dhts := setupDHTS(ctx, 4, t) defer func() { for i := 0; i < 4; i++ { dhts[i].Halt() @@ -253,17 +254,17 @@ func TestProvidesAsync(t *testing.T) { } }() - _, err := dhts[0].Connect(context.Background(), peers[1]) + _, err := dhts[0].Connect(ctx, peers[1]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(context.Background(), peers[2]) + _, err = dhts[1].Connect(ctx, peers[2]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(context.Background(), peers[3]) + _, err = dhts[1].Connect(ctx, peers[3]) if err != nil { t.Fatal(err) } @@ -278,21 +279,21 @@ func TestProvidesAsync(t *testing.T) { t.Fatal(err) } - err = dhts[3].Provide(context.Background(), u.Key("hello")) + err = dhts[3].Provide(ctx, u.Key("hello")) if err != nil { t.Fatal(err) } time.Sleep(time.Millisecond * 60) - ctx, _ := context.WithTimeout(context.TODO(), time.Millisecond*300) - provs := dhts[0].FindProvidersAsync(ctx, u.Key("hello"), 5) + ctxT, _ := context.WithTimeout(ctx, time.Millisecond*300) + provs := dhts[0].FindProvidersAsync(ctxT, u.Key("hello"), 5) select { case p := <-provs: if !p.ID.Equal(dhts[3].self.ID) { t.Fatalf("got a provider, but not the right one. %s", p) } - case <-ctx.Done(): + case <-ctxT.Done(): t.Fatal("Didnt get back providers") } } @@ -300,8 +301,9 @@ func TestProvidesAsync(t *testing.T) { func TestLayeredGet(t *testing.T) { // t.Skip("skipping test to debug another") + ctx := context.Background() u.Debug = false - _, peers, dhts := setupDHTS(4, t) + _, peers, dhts := setupDHTS(ctx, 4, t) defer func() { for i := 0; i < 4; i++ { dhts[i].Halt() @@ -309,17 +311,17 @@ func TestLayeredGet(t *testing.T) { } }() - _, err := dhts[0].Connect(context.Background(), peers[1]) + _, err := dhts[0].Connect(ctx, peers[1]) if err != nil { t.Fatalf("Failed to connect: %s", err) } - _, err = dhts[1].Connect(context.Background(), peers[2]) + _, err = dhts[1].Connect(ctx, peers[2]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(context.Background(), peers[3]) + _, err = dhts[1].Connect(ctx, peers[3]) if err != nil { t.Fatal(err) } @@ -329,14 +331,14 @@ func TestLayeredGet(t *testing.T) { t.Fatal(err) } - err = dhts[3].Provide(context.Background(), u.Key("hello")) + err = dhts[3].Provide(ctx, u.Key("hello")) if err != nil { t.Fatal(err) } time.Sleep(time.Millisecond * 60) - ctxT, _ := context.WithTimeout(context.Background(), time.Second) + ctxT, _ := context.WithTimeout(ctx, time.Second) val, err := dhts[0].GetValue(ctxT, u.Key("hello")) if err != nil { t.Fatal(err) @@ -351,9 +353,10 @@ func TestLayeredGet(t *testing.T) { func TestFindPeer(t *testing.T) { // t.Skip("skipping test to debug another") + ctx := context.Background() u.Debug = false - _, peers, dhts := setupDHTS(4, t) + _, peers, dhts := setupDHTS(ctx, 4, t) defer func() { for i := 0; i < 4; i++ { dhts[i].Halt() @@ -361,22 +364,22 @@ func TestFindPeer(t *testing.T) { } }() - _, err := dhts[0].Connect(context.Background(), peers[1]) + _, err := dhts[0].Connect(ctx, peers[1]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(context.Background(), peers[2]) + _, err = dhts[1].Connect(ctx, peers[2]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(context.Background(), peers[3]) + _, err = dhts[1].Connect(ctx, peers[3]) if err != nil { t.Fatal(err) } - ctxT, _ := context.WithTimeout(context.Background(), time.Second) + ctxT, _ := context.WithTimeout(ctx, time.Second) p, err := dhts[0].FindPeer(ctxT, peers[2].ID) if err != nil { t.Fatal(err) @@ -394,6 +397,7 @@ func TestFindPeer(t *testing.T) { func TestConnectCollision(t *testing.T) { // t.Skip("skipping test to debug another") + ctx := context.Background() u.Debug = false addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") if err != nil { @@ -407,8 +411,8 @@ func TestConnectCollision(t *testing.T) { peerA := makePeer(addrA) peerB := makePeer(addrB) - dhtA := setupDHT(t, peerA) - dhtB := setupDHT(t, peerB) + dhtA := setupDHT(ctx, t, peerA) + dhtB := setupDHT(ctx, t, peerB) defer dhtA.Halt() defer dhtB.Halt() @@ -417,14 +421,14 @@ func TestConnectCollision(t *testing.T) { done := make(chan struct{}) go func() { - _, err = dhtA.Connect(context.Background(), peerB) + _, err = dhtA.Connect(ctx, peerB) if err != nil { t.Fatal(err) } done <- struct{}{} }() go func() { - _, err = dhtB.Connect(context.Background(), peerA) + _, err = dhtB.Connect(ctx, peerA) if err != nil { t.Fatal(err) } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index ca88a83f4..b5bf48772 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -110,7 +110,7 @@ func TestGetFailures(t *testing.T) { local := new(peer.Peer) local.ID = peer.ID("test_peer") - d := NewDHT(local, peerstore, fn, fs, ds.NewMapDatastore()) + d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) other := &peer.Peer{ID: peer.ID("other_peer")} d.Update(other) @@ -200,6 +200,7 @@ func _randPeer() *peer.Peer { func TestNotFound(t *testing.T) { // t.Skip("skipping test because it makes a lot of output") + ctx := context.Background() fn := &fauxNet{} fs := &fauxSender{} @@ -207,7 +208,7 @@ func TestNotFound(t *testing.T) { local.ID = peer.ID("test_peer") peerstore := peer.NewPeerstore() - d := NewDHT(local, peerstore, fn, fs, ds.NewMapDatastore()) + d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) var ps []*peer.Peer for i := 0; i < 5; i++ { @@ -243,7 +244,7 @@ func TestNotFound(t *testing.T) { }) - ctx, _ := context.WithTimeout(context.Background(), time.Second*5) + ctx, _ = context.WithTimeout(ctx, time.Second*5) v, err := d.GetValue(ctx, u.Key("hello")) log.Debug("get value got %v", v) if err != nil { @@ -265,6 +266,7 @@ func TestNotFound(t *testing.T) { func TestLessThanKResponses(t *testing.T) { // t.Skip("skipping test because it makes a lot of output") + ctx := context.Background() u.Debug = false fn := &fauxNet{} fs := &fauxSender{} @@ -272,7 +274,7 @@ func TestLessThanKResponses(t *testing.T) { local := new(peer.Peer) local.ID = peer.ID("test_peer") - d := NewDHT(local, peerstore, fn, fs, ds.NewMapDatastore()) + d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) var ps []*peer.Peer for i := 0; i < 5; i++ { @@ -307,7 +309,7 @@ func TestLessThanKResponses(t *testing.T) { }) - ctx, _ := context.WithTimeout(context.Background(), time.Second*30) + ctx, _ = context.WithTimeout(ctx, time.Second*30) _, err := d.GetValue(ctx, u.Key("hello")) if err != nil { switch err { From 7c96b81f1bebbaf46529f2379da3cb4c756e6555 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 16 Oct 2014 07:17:49 -0700 Subject: [PATCH 0269/3817] move IDFromPubKey to peer pkg This commit was moved from ipfs/go-ipfs-routing@734180186bb409ed8a91e50924bea312ed0e2487 --- routing/dht/dht_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 36f27a222..2861be73a 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -10,7 +10,6 @@ import ( ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ci "github.com/jbenet/go-ipfs/crypto" - spipe "github.com/jbenet/go-ipfs/crypto/spipe" inet "github.com/jbenet/go-ipfs/net" mux "github.com/jbenet/go-ipfs/net/mux" netservice "github.com/jbenet/go-ipfs/net/service" @@ -74,7 +73,7 @@ func makePeer(addr ma.Multiaddr) *peer.Peer { } p.PrivKey = sk p.PubKey = pk - id, err := spipe.IDFromPubKey(pk) + id, err := peer.IDFromPubKey(pk) if err != nil { panic(err) } From 1ec63750cd58a02972c52418a0ab985f7041343d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 18 Oct 2014 12:07:46 -0700 Subject: [PATCH 0270/3817] add another ipns test to simulate coalesced writes This commit was moved from ipfs/go-unixfs@82ab6d5ce56c851a71b8528b10936e8877c09f01 --- unixfs/io/dagmodifier_test.go | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index 32d9a84b5..5c96aaae4 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -187,6 +187,47 @@ func TestMultiWrite(t *testing.T) { } } +func TestMultiWriteCoal(t *testing.T) { + dserv := getMockDagServ(t) + _, n := getNode(t, dserv, 0) + + dagmod, err := NewDagModifier(n, dserv, &chunk.SizeSplitter{512}) + if err != nil { + t.Fatal(err) + } + + data := make([]byte, 4000) + u.NewFastRand().Read(data) + + for i := 0; i < len(data); i++ { + n, err := dagmod.WriteAt(data[:i+1], 0) + if err != nil { + t.Fatal(err) + } + if n != i+1 { + t.Fatal("Somehow wrote the wrong number of bytes! (n != 1)") + } + } + nd, err := dagmod.GetNode() + if err != nil { + t.Fatal(err) + } + + read, err := NewDagReader(nd, dserv) + if err != nil { + t.Fatal(err) + } + rbuf, err := ioutil.ReadAll(read) + if err != nil { + t.Fatal(err) + } + + err = arrComp(rbuf, data) + if err != nil { + t.Fatal(err) + } +} + func arrComp(a, b []byte) error { if len(a) != len(b) { return fmt.Errorf("Arrays differ in length. %d != %d", len(a), len(b)) From fd170bced3af060a554e7e47648dc55e08c441b2 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 18 Oct 2014 05:52:24 -0700 Subject: [PATCH 0271/3817] moved XOR keyspace -> util This commit was moved from ipfs/go-ipfs-routing@a0d739fa00b96367b10bbd50b0a0fe87faed02ea --- routing/keyspace/xor.go | 13 +++---------- routing/keyspace/xor_test.go | 31 +++---------------------------- 2 files changed, 6 insertions(+), 38 deletions(-) diff --git a/routing/keyspace/xor.go b/routing/keyspace/xor.go index dbb7c6851..7159f2cad 100644 --- a/routing/keyspace/xor.go +++ b/routing/keyspace/xor.go @@ -4,6 +4,8 @@ import ( "bytes" "crypto/sha256" "math/big" + + u "github.com/jbenet/go-ipfs/util" ) // XORKeySpace is a KeySpace which: @@ -33,7 +35,7 @@ func (s *xorKeySpace) Equal(k1, k2 Key) bool { // Distance returns the distance metric in this key space func (s *xorKeySpace) Distance(k1, k2 Key) *big.Int { // XOR the keys - k3 := XOR(k1.Bytes, k2.Bytes) + k3 := u.XOR(k1.Bytes, k2.Bytes) // interpret it as an integer dist := big.NewInt(0).SetBytes(k3) @@ -52,15 +54,6 @@ func (s *xorKeySpace) Less(k1, k2 Key) bool { return true } -// XOR takes two byte slices, XORs them together, returns the resulting slice. -func XOR(a, b []byte) []byte { - c := make([]byte, len(a)) - for i := 0; i < len(a); i++ { - c[i] = a[i] ^ b[i] - } - return c -} - // ZeroPrefixLen returns the number of consecutive zeroes in a byte slice. func ZeroPrefixLen(id []byte) int { for i := 0; i < len(id); i++ { diff --git a/routing/keyspace/xor_test.go b/routing/keyspace/xor_test.go index 7963ea014..8db4b926c 100644 --- a/routing/keyspace/xor_test.go +++ b/routing/keyspace/xor_test.go @@ -4,34 +4,9 @@ import ( "bytes" "math/big" "testing" -) - -func TestXOR(t *testing.T) { - cases := [][3][]byte{ - [3][]byte{ - []byte{0xFF, 0xFF, 0xFF}, - []byte{0xFF, 0xFF, 0xFF}, - []byte{0x00, 0x00, 0x00}, - }, - [3][]byte{ - []byte{0x00, 0xFF, 0x00}, - []byte{0xFF, 0xFF, 0xFF}, - []byte{0xFF, 0x00, 0xFF}, - }, - [3][]byte{ - []byte{0x55, 0x55, 0x55}, - []byte{0x55, 0xFF, 0xAA}, - []byte{0x00, 0xAA, 0xFF}, - }, - } - for _, c := range cases { - r := XOR(c[0], c[1]) - if !bytes.Equal(r, c[2]) { - t.Error("XOR failed") - } - } -} + u "github.com/jbenet/go-ipfs/util" +) func TestPrefixLen(t *testing.T) { cases := [][]byte{ @@ -126,7 +101,7 @@ func TestDistancesAndCenterSorting(t *testing.T) { } d1 := keys[2].Distance(keys[5]) - d2 := XOR(keys[2].Bytes, keys[5].Bytes) + d2 := u.XOR(keys[2].Bytes, keys[5].Bytes) d2 = d2[len(keys[2].Bytes)-len(d1.Bytes()):] // skip empty space for big if !bytes.Equal(d1.Bytes(), d2) { t.Errorf("bytes should be the same. %v == %v", d1.Bytes(), d2) From 9b97818583989a3b5541112053878ddf99f40f2e Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 18 Oct 2014 20:00:13 -0700 Subject: [PATCH 0272/3817] keyspace XOR naming This commit was moved from ipfs/go-ipfs-routing@5532288d1ac46da272db9d3f77c1b46ebcde948a --- routing/kbucket/util.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 3aca06f6a..02994230a 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -31,11 +31,11 @@ func (id ID) less(other ID) bool { } func xor(a, b ID) ID { - return ID(ks.XOR(a, b)) + return ID(u.XOR(a, b)) } func commonPrefixLen(a, b ID) int { - return ks.ZeroPrefixLen(ks.XOR(a, b)) + return ks.ZeroPrefixLen(u.XOR(a, b)) } // ConvertPeerID creates a DHT ID by hashing a Peer ID (Multihash) From 843e9fc9400c9cab06dea4dead086716f61c7504 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 18 Oct 2014 20:04:05 -0700 Subject: [PATCH 0273/3817] make vendor @whyrusleeping pre-commit hook? This commit was moved from ipfs/go-ipfs-routing@0b714cae3ac0ed6b13d9af490296f063f4d1d08f --- routing/dht/messages.pb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/messages.pb.go b/routing/dht/messages.pb.go index f204544ea..2da77e7bc 100644 --- a/routing/dht/messages.pb.go +++ b/routing/dht/messages.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package dht -import proto "code.google.com/p/goprotobuf/proto" +import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. From 591a0d98498b89139f30b76d948592e44976d37e Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 19 Oct 2014 02:05:29 -0700 Subject: [PATCH 0274/3817] fixed tests This commit was moved from ipfs/go-ipfs-routing@79e3d948085862705773442a9db91d854934966c --- routing/dht/dht_test.go | 90 ++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 2861be73a..ff28ab2ed 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -112,13 +112,13 @@ func TestPing(t *testing.T) { } //Test that we can ping the node - ctxT, _ := context.WithTimeout(ctx, 5*time.Millisecond) + ctxT, _ := context.WithTimeout(ctx, 100*time.Millisecond) err = dhtA.Ping(ctxT, peerB) if err != nil { t.Fatal(err) } - ctxT, _ = context.WithTimeout(ctx, 5*time.Millisecond) + ctxT, _ = context.WithTimeout(ctx, 100*time.Millisecond) err = dhtB.Ping(ctxT, peerA) if err != nil { t.Fatal(err) @@ -396,53 +396,61 @@ func TestFindPeer(t *testing.T) { func TestConnectCollision(t *testing.T) { // t.Skip("skipping test to debug another") - ctx := context.Background() - u.Debug = false - addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") - if err != nil { - t.Fatal(err) - } - addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5679") - if err != nil { - t.Fatal(err) - } - - peerA := makePeer(addrA) - peerB := makePeer(addrB) - - dhtA := setupDHT(ctx, t, peerA) - dhtB := setupDHT(ctx, t, peerB) + runTimes := 100 - defer dhtA.Halt() - defer dhtB.Halt() - defer dhtA.network.Close() - defer dhtB.network.Close() + for rtime := 0; rtime < runTimes; rtime++ { + log.Notice("Running Time: ", rtime) - done := make(chan struct{}) - go func() { - _, err = dhtA.Connect(ctx, peerB) + ctx := context.Background() + u.Debug = false + addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") if err != nil { t.Fatal(err) } - done <- struct{}{} - }() - go func() { - _, err = dhtB.Connect(ctx, peerA) + addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5679") if err != nil { t.Fatal(err) } - done <- struct{}{} - }() - timeout := time.After(time.Second * 5) - select { - case <-done: - case <-timeout: - t.Fatal("Timeout received!") - } - select { - case <-done: - case <-timeout: - t.Fatal("Timeout received!") + peerA := makePeer(addrA) + peerB := makePeer(addrB) + + dhtA := setupDHT(ctx, t, peerA) + dhtB := setupDHT(ctx, t, peerB) + + defer dhtA.Halt() + defer dhtB.Halt() + defer dhtA.network.Close() + defer dhtB.network.Close() + + done := make(chan struct{}) + go func() { + _, err = dhtA.Connect(ctx, peerB) + if err != nil { + t.Fatal(err) + } + done <- struct{}{} + }() + go func() { + _, err = dhtB.Connect(ctx, peerA) + if err != nil { + t.Fatal(err) + } + done <- struct{}{} + }() + + timeout := time.After(time.Second) + select { + case <-done: + case <-timeout: + t.Fatal("Timeout received!") + } + select { + case <-done: + case <-timeout: + t.Fatal("Timeout received!") + } + + <-time.After(100 * time.Millisecond) } } From 232b2bdc2bb6db422a834c9a81bd0107f402e2c6 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 19 Oct 2014 06:29:18 -0700 Subject: [PATCH 0275/3817] differentiate ports cause timing. This commit was moved from ipfs/go-ipfs-routing@08177bd39c1bd0d12c846d5897a9e5e5945c794d --- routing/dht/dht_test.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index ff28ab2ed..98b196da5 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -130,11 +130,11 @@ func TestValueGetSet(t *testing.T) { ctx := context.Background() u.Debug = false - addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") + addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/11235") if err != nil { t.Fatal(err) } - addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5679") + addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/15679") if err != nil { t.Fatal(err) } @@ -396,18 +396,18 @@ func TestFindPeer(t *testing.T) { func TestConnectCollision(t *testing.T) { // t.Skip("skipping test to debug another") - runTimes := 100 + runTimes := 10 for rtime := 0; rtime < runTimes; rtime++ { log.Notice("Running Time: ", rtime) ctx := context.Background() u.Debug = false - addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1235") + addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/11235") if err != nil { t.Fatal(err) } - addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5679") + addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/15679") if err != nil { t.Fatal(err) } @@ -418,11 +418,6 @@ func TestConnectCollision(t *testing.T) { dhtA := setupDHT(ctx, t, peerA) dhtB := setupDHT(ctx, t, peerB) - defer dhtA.Halt() - defer dhtB.Halt() - defer dhtA.network.Close() - defer dhtB.network.Close() - done := make(chan struct{}) go func() { _, err = dhtA.Connect(ctx, peerB) @@ -451,6 +446,11 @@ func TestConnectCollision(t *testing.T) { t.Fatal("Timeout received!") } - <-time.After(100 * time.Millisecond) + dhtA.Halt() + dhtB.Halt() + dhtA.network.Close() + dhtB.network.Close() + + <-time.After(200 * time.Millisecond) } } From 893f230c8a5e27063dab59147d3b748ed29da085 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 19 Oct 2014 23:40:14 -0700 Subject: [PATCH 0276/3817] peerstore constructs peers Now, all peers should be retrieved from the Peerstore, which will construct the peers accordingly. This ensures there's only one peer object per peer (opposite would be bad: things get out sync) cc @whyrusleeping This commit was moved from ipfs/go-ipfs-routing@531682c2472cdc5e17b86a0d4f32a14672d62c1e --- routing/dht/dht.go | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 2d4c9852e..683b5785b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -300,10 +300,9 @@ func (dht *IpfsDHT) addPeer(pb *Message_Peer) (*peer.Peer, error) { } // check if we already have this peer. - pr, _ := dht.peerstore.Get(peer.ID(pb.GetId())) - if pr == nil { - pr = &peer.Peer{ID: peer.ID(pb.GetId())} - dht.peerstore.Put(pr) + pr, err := dht.getPeer(peer.ID(pb.GetId())) + if err != nil { + return nil, err } pr.AddAddress(addr) // idempotent @@ -481,6 +480,16 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *Message, count int) []*peer.Peer { return filtered } +func (dht *IpfsDHT) getPeer(id peer.ID) (*peer.Peer, error) { + p, err := dht.peerstore.Get(id) + if err != nil { + err = fmt.Errorf("Failed to get peer from peerstore: %s", err) + log.Error("%s", err) + return nil, err + } + return p, nil +} + func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (*peer.Peer, error) { id := peer.ID(pbp.GetId()) @@ -490,26 +499,16 @@ func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (*peer.Peer, error) { return nil, errors.New("found self") } - p, _ := dht.peerstore.Get(id) - if p == nil { - p, _ = dht.FindLocal(id) - if p != nil { - panic("somehow peer not getting into peerstore") - } + p, err := dht.getPeer(id) + if err != nil { + return nil, err } - if p == nil { - maddr, err := pbp.Address() - if err != nil { - return nil, err - } - - // create new Peer - p = &peer.Peer{ID: id} - p.AddAddress(maddr) - dht.peerstore.Put(p) - log.Info("dht found new peer: %s %s", p, maddr) + maddr, err := pbp.Address() + if err != nil { + return nil, err } + p.AddAddress(maddr) return p, nil } @@ -541,6 +540,7 @@ func (dht *IpfsDHT) loadProvidableKeys() error { return nil } +// PingRoutine periodically pings nearest neighbors. func (dht *IpfsDHT) PingRoutine(t time.Duration) { tick := time.Tick(t) for { From 3433cce9cd41884947a6c3d7d1b9d1eb63ac5000 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 20 Oct 2014 03:26:44 -0700 Subject: [PATCH 0277/3817] peer.Peer is now an interface ![](http://m.memegen.com/77n7dk.jpg) This commit was moved from ipfs/go-ipfs-routing@be07f83e37101063df61d4429424d5fd078732de --- routing/dht/Message.go | 11 +++--- routing/dht/dht.go | 66 +++++++++++++++++------------------ routing/dht/dht_test.go | 23 +++++------- routing/dht/diag.go | 5 +-- routing/dht/ext_test.go | 39 ++++++++++----------- routing/dht/handlers.go | 28 +++++++-------- routing/dht/providers.go | 14 ++++---- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 20 +++++------ routing/dht/routing.go | 30 ++++++++-------- routing/dht/util.go | 14 ++++---- routing/kbucket/bucket.go | 10 +++--- routing/kbucket/table.go | 30 ++++++++-------- routing/kbucket/table_test.go | 49 +++++++++++++------------- routing/mock/routing.go | 26 +++++++------- routing/mock/routing_test.go | 24 +++++-------- routing/routing.go | 4 +-- 17 files changed, 191 insertions(+), 204 deletions(-) diff --git a/routing/dht/Message.go b/routing/dht/Message.go index 526724287..ae78d1f39 100644 --- a/routing/dht/Message.go +++ b/routing/dht/Message.go @@ -17,20 +17,21 @@ func newMessage(typ Message_MessageType, key string, level int) *Message { return m } -func peerToPBPeer(p *peer.Peer) *Message_Peer { +func peerToPBPeer(p peer.Peer) *Message_Peer { pbp := new(Message_Peer) - if len(p.Addresses) == 0 || p.Addresses[0] == nil { + addrs := p.Addresses() + if len(addrs) == 0 || addrs[0] == nil { pbp.Addr = proto.String("") } else { - addr := p.Addresses[0].String() + addr := addrs[0].String() pbp.Addr = &addr } - pid := string(p.ID) + pid := string(p.ID()) pbp.Id = &pid return pbp } -func peersToPBPeers(peers []*peer.Peer) []*Message_Peer { +func peersToPBPeers(peers []peer.Peer) []*Message_Peer { pbpeers := make([]*Message_Peer, len(peers)) for i, p := range peers { pbpeers[i] = peerToPBPeer(p) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 683b5785b..3617b7142 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -39,7 +39,7 @@ type IpfsDHT struct { sender inet.Sender // Local peer (yourself) - self *peer.Peer + self peer.Peer // Other peers peerstore peer.Peerstore @@ -60,7 +60,7 @@ type IpfsDHT struct { } // NewDHT creates a new DHT object with the given peer as the 'local' host -func NewDHT(ctx context.Context, p *peer.Peer, ps peer.Peerstore, net inet.Network, sender inet.Sender, dstore ds.Datastore) *IpfsDHT { +func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, net inet.Network, sender inet.Sender, dstore ds.Datastore) *IpfsDHT { dht := new(IpfsDHT) dht.network = net dht.sender = sender @@ -69,12 +69,12 @@ func NewDHT(ctx context.Context, p *peer.Peer, ps peer.Peerstore, net inet.Netwo dht.peerstore = ps dht.ctx = ctx - dht.providers = NewProviderManager(p.ID) + dht.providers = NewProviderManager(p.ID()) dht.routingTables = make([]*kb.RoutingTable, 3) - dht.routingTables[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*1000) - dht.routingTables[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*1000) - dht.routingTables[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Hour) + dht.routingTables[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID()), time.Millisecond*1000) + dht.routingTables[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID()), time.Millisecond*1000) + dht.routingTables[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID()), time.Hour) dht.birth = time.Now() if doPinging { @@ -84,7 +84,7 @@ func NewDHT(ctx context.Context, p *peer.Peer, ps peer.Peerstore, net inet.Netwo } // Connect to a new peer at the given address, ping and add to the routing table -func (dht *IpfsDHT) Connect(ctx context.Context, npeer *peer.Peer) (*peer.Peer, error) { +func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.Peer) (peer.Peer, error) { log.Debug("Connect to new peer: %s", npeer) // TODO(jbenet,whyrusleeping) @@ -175,7 +175,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N // sendRequest sends out a request using dht.sender, but also makes sure to // measure the RTT for latency measurements. -func (dht *IpfsDHT) sendRequest(ctx context.Context, p *peer.Peer, pmes *Message) (*Message, error) { +func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *Message) (*Message, error) { mes, err := msg.FromObject(p, pmes) if err != nil { @@ -208,7 +208,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p *peer.Peer, pmes *Message } // putValueToNetwork stores the given key/value pair at the peer 'p' -func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p *peer.Peer, +func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p peer.Peer, key string, value []byte) error { pmes := newMessage(Message_PUT_VALUE, string(key), 0) @@ -224,12 +224,12 @@ func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p *peer.Peer, return nil } -func (dht *IpfsDHT) putProvider(ctx context.Context, p *peer.Peer, key string) error { +func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.Peer, key string) error { pmes := newMessage(Message_ADD_PROVIDER, string(key), 0) // add self as the provider - pmes.ProviderPeers = peersToPBPeers([]*peer.Peer{dht.self}) + pmes.ProviderPeers = peersToPBPeers([]peer.Peer{dht.self}) rpmes, err := dht.sendRequest(ctx, p, pmes) if err != nil { @@ -244,8 +244,8 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p *peer.Peer, key string) e return nil } -func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, - key u.Key, level int) ([]byte, []*peer.Peer, error) { +func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, + key u.Key, level int) ([]byte, []peer.Peer, error) { pmes, err := dht.getValueSingle(ctx, p, key, level) if err != nil { @@ -270,7 +270,7 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, } // Perhaps we were given closer peers - var peers []*peer.Peer + var peers []peer.Peer for _, pb := range pmes.GetCloserPeers() { pr, err := dht.addPeer(pb) if err != nil { @@ -289,8 +289,8 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p *peer.Peer, return nil, nil, u.ErrNotFound } -func (dht *IpfsDHT) addPeer(pb *Message_Peer) (*peer.Peer, error) { - if peer.ID(pb.GetId()).Equal(dht.self.ID) { +func (dht *IpfsDHT) addPeer(pb *Message_Peer) (peer.Peer, error) { + if peer.ID(pb.GetId()).Equal(dht.self.ID()) { return nil, errors.New("cannot add self as peer") } @@ -310,7 +310,7 @@ func (dht *IpfsDHT) addPeer(pb *Message_Peer) (*peer.Peer, error) { } // getValueSingle simply performs the get value RPC with the given parameters -func (dht *IpfsDHT) getValueSingle(ctx context.Context, p *peer.Peer, +func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.Peer, key u.Key, level int) (*Message, error) { pmes := newMessage(Message_GET_VALUE, string(key), level) @@ -369,7 +369,7 @@ func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { // Update signals to all routingTables to Update their last-seen status // on the given peer. -func (dht *IpfsDHT) Update(p *peer.Peer) { +func (dht *IpfsDHT) Update(p peer.Peer) { log.Debug("updating peer: %s latency = %f\n", p, p.GetLatency().Seconds()) removedCount := 0 for _, route := range dht.routingTables { @@ -390,7 +390,7 @@ func (dht *IpfsDHT) Update(p *peer.Peer) { } // FindLocal looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in. -func (dht *IpfsDHT) FindLocal(id peer.ID) (*peer.Peer, *kb.RoutingTable) { +func (dht *IpfsDHT) FindLocal(id peer.ID) (peer.Peer, *kb.RoutingTable) { for _, table := range dht.routingTables { p := table.Find(id) if p != nil { @@ -400,7 +400,7 @@ func (dht *IpfsDHT) FindLocal(id peer.ID) (*peer.Peer, *kb.RoutingTable) { return nil, nil } -func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p *peer.Peer, id peer.ID, level int) (*Message, error) { +func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.Peer, id peer.ID, level int) (*Message, error) { pmes := newMessage(Message_FIND_NODE, string(id), level) return dht.sendRequest(ctx, p, pmes) } @@ -411,14 +411,14 @@ func (dht *IpfsDHT) printTables() { } } -func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p *peer.Peer, key u.Key, level int) (*Message, error) { +func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.Peer, key u.Key, level int) (*Message, error) { pmes := newMessage(Message_GET_PROVIDERS, string(key), level) return dht.sendRequest(ctx, p, pmes) } // TODO: Could be done async -func (dht *IpfsDHT) addProviders(key u.Key, peers []*Message_Peer) []*peer.Peer { - var provArr []*peer.Peer +func (dht *IpfsDHT) addProviders(key u.Key, peers []*Message_Peer) []peer.Peer { + var provArr []peer.Peer for _, prov := range peers { p, err := dht.peerFromInfo(prov) if err != nil { @@ -429,7 +429,7 @@ func (dht *IpfsDHT) addProviders(key u.Key, peers []*Message_Peer) []*peer.Peer log.Debug("%s adding provider: %s for %s", dht.self, p, key) // Dont add outselves to the list - if p.ID.Equal(dht.self.ID) { + if p.ID().Equal(dht.self.ID()) { continue } @@ -441,7 +441,7 @@ func (dht *IpfsDHT) addProviders(key u.Key, peers []*Message_Peer) []*peer.Peer } // nearestPeersToQuery returns the routing tables closest peers. -func (dht *IpfsDHT) nearestPeersToQuery(pmes *Message, count int) []*peer.Peer { +func (dht *IpfsDHT) nearestPeersToQuery(pmes *Message, count int) []peer.Peer { level := pmes.GetClusterLevel() cluster := dht.routingTables[level] @@ -451,7 +451,7 @@ func (dht *IpfsDHT) nearestPeersToQuery(pmes *Message, count int) []*peer.Peer { } // betterPeerToQuery returns nearestPeersToQuery, but iff closer than self. -func (dht *IpfsDHT) betterPeersToQuery(pmes *Message, count int) []*peer.Peer { +func (dht *IpfsDHT) betterPeersToQuery(pmes *Message, count int) []peer.Peer { closer := dht.nearestPeersToQuery(pmes, count) // no node? nil @@ -461,17 +461,17 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *Message, count int) []*peer.Peer { // == to self? thats bad for _, p := range closer { - if p.ID.Equal(dht.self.ID) { + if p.ID().Equal(dht.self.ID()) { log.Error("Attempted to return self! this shouldnt happen...") return nil } } - var filtered []*peer.Peer + var filtered []peer.Peer for _, p := range closer { // must all be closer than self key := u.Key(pmes.GetKey()) - if !kb.Closer(dht.self.ID, p.ID, key) { + if !kb.Closer(dht.self.ID(), p.ID(), key) { filtered = append(filtered, p) } } @@ -480,7 +480,7 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *Message, count int) []*peer.Peer { return filtered } -func (dht *IpfsDHT) getPeer(id peer.ID) (*peer.Peer, error) { +func (dht *IpfsDHT) getPeer(id peer.ID) (peer.Peer, error) { p, err := dht.peerstore.Get(id) if err != nil { err = fmt.Errorf("Failed to get peer from peerstore: %s", err) @@ -490,12 +490,12 @@ func (dht *IpfsDHT) getPeer(id peer.ID) (*peer.Peer, error) { return p, nil } -func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (*peer.Peer, error) { +func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (peer.Peer, error) { id := peer.ID(pbp.GetId()) // continue if it's ourselves - if id.Equal(dht.self.ID) { + if id.Equal(dht.self.ID()) { return nil, errors.New("found self") } @@ -512,7 +512,7 @@ func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (*peer.Peer, error) { return p, nil } -func (dht *IpfsDHT) ensureConnectedToPeer(pbp *Message_Peer) (*peer.Peer, error) { +func (dht *IpfsDHT) ensureConnectedToPeer(pbp *Message_Peer) (peer.Peer, error) { p, err := dht.peerFromInfo(pbp) if err != nil { return nil, err diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 98b196da5..23d9fcf17 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -20,7 +20,7 @@ import ( "time" ) -func setupDHT(ctx context.Context, t *testing.T, p *peer.Peer) *IpfsDHT { +func setupDHT(ctx context.Context, t *testing.T, p peer.Peer) *IpfsDHT { peerstore := peer.NewPeerstore() dhts := netservice.NewService(nil) // nil handler for now, need to patch it @@ -40,7 +40,7 @@ func setupDHT(ctx context.Context, t *testing.T, p *peer.Peer) *IpfsDHT { return d } -func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []*peer.Peer, []*IpfsDHT) { +func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []peer.Peer, []*IpfsDHT) { var addrs []ma.Multiaddr for i := 0; i < n; i++ { a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) @@ -50,7 +50,7 @@ func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []*pee addrs = append(addrs, a) } - var peers []*peer.Peer + var peers []peer.Peer for i := 0; i < n; i++ { p := makePeer(addrs[i]) peers = append(peers, p) @@ -64,21 +64,16 @@ func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []*pee return addrs, peers, dhts } -func makePeer(addr ma.Multiaddr) *peer.Peer { - p := new(peer.Peer) - p.AddAddress(addr) +func makePeer(addr ma.Multiaddr) peer.Peer { sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) if err != nil { panic(err) } - p.PrivKey = sk - p.PubKey = pk - id, err := peer.IDFromPubKey(pk) + p, err := peer.WithKeyPair(sk, pk) if err != nil { panic(err) } - - p.ID = id + p.AddAddress(addr) return p } @@ -289,7 +284,7 @@ func TestProvidesAsync(t *testing.T) { provs := dhts[0].FindProvidersAsync(ctxT, u.Key("hello"), 5) select { case p := <-provs: - if !p.ID.Equal(dhts[3].self.ID) { + if !p.ID().Equal(dhts[3].self.ID()) { t.Fatalf("got a provider, but not the right one. %s", p) } case <-ctxT.Done(): @@ -379,7 +374,7 @@ func TestFindPeer(t *testing.T) { } ctxT, _ := context.WithTimeout(ctx, time.Second) - p, err := dhts[0].FindPeer(ctxT, peers[2].ID) + p, err := dhts[0].FindPeer(ctxT, peers[2].ID()) if err != nil { t.Fatal(err) } @@ -388,7 +383,7 @@ func TestFindPeer(t *testing.T) { t.Fatal("Failed to find peer.") } - if !p.ID.Equal(peers[2].ID) { + if !p.ID().Equal(peers[2].ID()) { t.Fatal("Didnt find expected peer.") } } diff --git a/routing/dht/diag.go b/routing/dht/diag.go index 8fd581a45..e91ba9bee 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -32,12 +32,13 @@ func (di *diagInfo) Marshal() []byte { func (dht *IpfsDHT) getDiagInfo() *diagInfo { di := new(diagInfo) di.CodeVersion = "github.com/jbenet/go-ipfs" - di.ID = dht.self.ID + di.ID = dht.self.ID() di.LifeSpan = time.Since(dht.birth) di.Keys = nil // Currently no way to query datastore for _, p := range dht.routingTables[0].ListPeers() { - di.Connections = append(di.Connections, connDiagInfo{p.GetLatency(), p.ID}) + d := connDiagInfo{p.GetLatency(), p.ID()} + di.Connections = append(di.Connections, d) } return di } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index b5bf48772..c4ba09414 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -9,7 +9,6 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" msg "github.com/jbenet/go-ipfs/net/message" mux "github.com/jbenet/go-ipfs/net/mux" peer "github.com/jbenet/go-ipfs/peer" @@ -66,17 +65,17 @@ type fauxNet struct { } // DialPeer attempts to establish a connection to a given peer -func (f *fauxNet) DialPeer(*peer.Peer) error { +func (f *fauxNet) DialPeer(peer.Peer) error { return nil } // ClosePeer connection to peer -func (f *fauxNet) ClosePeer(*peer.Peer) error { +func (f *fauxNet) ClosePeer(peer.Peer) error { return nil } // IsConnected returns whether a connection to given peer exists. -func (f *fauxNet) IsConnected(*peer.Peer) (bool, error) { +func (f *fauxNet) IsConnected(peer.Peer) (bool, error) { return true, nil } @@ -88,7 +87,7 @@ func (f *fauxNet) SendMessage(msg.NetMessage) error { return nil } -func (f *fauxNet) GetPeerList() []*peer.Peer { +func (f *fauxNet) GetPeerList() []peer.Peer { return nil } @@ -107,11 +106,10 @@ func TestGetFailures(t *testing.T) { fs := &fauxSender{} peerstore := peer.NewPeerstore() - local := new(peer.Peer) - local.ID = peer.ID("test_peer") + local := peer.WithIDString("test_peer") d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) - other := &peer.Peer{ID: peer.ID("other_peer")} + other := peer.WithIDString("other_peer") d.Update(other) // This one should time out @@ -189,11 +187,10 @@ func TestGetFailures(t *testing.T) { } // TODO: Maybe put these in some sort of "ipfs_testutil" package -func _randPeer() *peer.Peer { - p := new(peer.Peer) - p.ID = make(peer.ID, 16) - p.Addresses = []ma.Multiaddr{nil} - crand.Read(p.ID) +func _randPeer() peer.Peer { + id := make(peer.ID, 16) + crand.Read(id) + p := peer.WithID(id) return p } @@ -204,13 +201,13 @@ func TestNotFound(t *testing.T) { fn := &fauxNet{} fs := &fauxSender{} - local := new(peer.Peer) - local.ID = peer.ID("test_peer") + local := peer.WithIDString("test_peer") peerstore := peer.NewPeerstore() + peerstore.Put(local) d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) - var ps []*peer.Peer + var ps []peer.Peer for i := 0; i < 5; i++ { ps = append(ps, _randPeer()) d.Update(ps[i]) @@ -228,7 +225,7 @@ func TestNotFound(t *testing.T) { case Message_GET_VALUE: resp := &Message{Type: pmes.Type} - peers := []*peer.Peer{} + peers := []peer.Peer{} for i := 0; i < 7; i++ { peers = append(peers, _randPeer()) } @@ -270,13 +267,13 @@ func TestLessThanKResponses(t *testing.T) { u.Debug = false fn := &fauxNet{} fs := &fauxSender{} + local := peer.WithIDString("test_peer") peerstore := peer.NewPeerstore() - local := new(peer.Peer) - local.ID = peer.ID("test_peer") + peerstore.Put(local) d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) - var ps []*peer.Peer + var ps []peer.Peer for i := 0; i < 5; i++ { ps = append(ps, _randPeer()) d.Update(ps[i]) @@ -295,7 +292,7 @@ func TestLessThanKResponses(t *testing.T) { case Message_GET_VALUE: resp := &Message{ Type: pmes.Type, - CloserPeers: peersToPBPeers([]*peer.Peer{other}), + CloserPeers: peersToPBPeers([]peer.Peer{other}), } mes, err := msg.FromObject(mes.Peer(), resp) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 1046516b6..44802babb 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -14,7 +14,7 @@ import ( var CloserPeerCount = 4 // dhthandler specifies the signature of functions that handle DHT messages. -type dhtHandler func(*peer.Peer, *Message) (*Message, error) +type dhtHandler func(peer.Peer, *Message) (*Message, error) func (dht *IpfsDHT) handlerForMsgType(t Message_MessageType) dhtHandler { switch t { @@ -35,7 +35,7 @@ func (dht *IpfsDHT) handlerForMsgType(t Message_MessageType) dhtHandler { } } -func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error) { +func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *Message) (*Message, error) { log.Debug("%s handleGetValue for key: %s\n", dht.self, pmes.GetKey()) // setup response @@ -93,7 +93,7 @@ func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *Message) (*Message, error } // Store a value in this peer local storage -func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *Message) (*Message, error) { +func (dht *IpfsDHT) handlePutValue(p peer.Peer, pmes *Message) (*Message, error) { dht.dslock.Lock() defer dht.dslock.Unlock() dskey := u.Key(pmes.GetKey()).DsKey() @@ -102,18 +102,18 @@ func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *Message) (*Message, error return pmes, err } -func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *Message) (*Message, error) { +func (dht *IpfsDHT) handlePing(p peer.Peer, pmes *Message) (*Message, error) { log.Debug("%s Responding to ping from %s!\n", dht.self, p) return pmes, nil } -func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *Message) (*Message, error) { +func (dht *IpfsDHT) handleFindPeer(p peer.Peer, pmes *Message) (*Message, error) { resp := newMessage(pmes.GetType(), "", pmes.GetClusterLevel()) - var closest []*peer.Peer + var closest []peer.Peer // if looking for self... special case where we send it on CloserPeers. - if peer.ID(pmes.GetKey()).Equal(dht.self.ID) { - closest = []*peer.Peer{dht.self} + if peer.ID(pmes.GetKey()).Equal(dht.self.ID()) { + closest = []peer.Peer{dht.self} } else { closest = dht.betterPeersToQuery(pmes, CloserPeerCount) } @@ -123,9 +123,9 @@ func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *Message) (*Message, error return resp, nil } - var withAddresses []*peer.Peer + var withAddresses []peer.Peer for _, p := range closest { - if len(p.Addresses) > 0 { + if len(p.Addresses()) > 0 { withAddresses = append(withAddresses, p) } } @@ -137,7 +137,7 @@ func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *Message) (*Message, error return resp, nil } -func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *Message) (*Message, error) { +func (dht *IpfsDHT) handleGetProviders(p peer.Peer, pmes *Message) (*Message, error) { resp := newMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) // check if we have this value, to add ourselves as provider. @@ -171,10 +171,10 @@ func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *Message) (*Message, e type providerInfo struct { Creation time.Time - Value *peer.Peer + Value peer.Peer } -func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *Message) (*Message, error) { +func (dht *IpfsDHT) handleAddProvider(p peer.Peer, pmes *Message) (*Message, error) { key := u.Key(pmes.GetKey()) log.Debug("%s adding %s as a provider for '%s'\n", dht.self, p, peer.ID(key)) @@ -182,7 +182,7 @@ func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *Message) (*Message, er // add provider should use the address given in the message for _, pb := range pmes.GetProviderPeers() { pid := peer.ID(pb.GetId()) - if pid.Equal(p.ID) { + if pid.Equal(p.ID()) { addr, err := pb.Address() if err != nil { diff --git a/routing/dht/providers.go b/routing/dht/providers.go index c62755cf2..204fdf7d5 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -20,12 +20,12 @@ type ProviderManager struct { type addProv struct { k u.Key - val *peer.Peer + val peer.Peer } type getProv struct { k u.Key - resp chan []*peer.Peer + resp chan []peer.Peer } func NewProviderManager(local peer.ID) *ProviderManager { @@ -45,7 +45,7 @@ func (pm *ProviderManager) run() { for { select { case np := <-pm.newprovs: - if np.val.ID.Equal(pm.lpeer) { + if np.val.ID().Equal(pm.lpeer) { pm.local[np.k] = struct{}{} } pi := new(providerInfo) @@ -54,7 +54,7 @@ func (pm *ProviderManager) run() { arr := pm.providers[np.k] pm.providers[np.k] = append(arr, pi) case gp := <-pm.getprovs: - var parr []*peer.Peer + var parr []peer.Peer provs := pm.providers[gp.k] for _, p := range provs { parr = append(parr, p.Value) @@ -82,17 +82,17 @@ func (pm *ProviderManager) run() { } } -func (pm *ProviderManager) AddProvider(k u.Key, val *peer.Peer) { +func (pm *ProviderManager) AddProvider(k u.Key, val peer.Peer) { pm.newprovs <- &addProv{ k: k, val: val, } } -func (pm *ProviderManager) GetProviders(k u.Key) []*peer.Peer { +func (pm *ProviderManager) GetProviders(k u.Key) []peer.Peer { gp := new(getProv) gp.k = k - gp.resp = make(chan []*peer.Peer) + gp.resp = make(chan []peer.Peer) pm.getprovs <- gp return <-gp.resp } diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 0cdfa4fcc..b37327d2e 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -11,7 +11,7 @@ func TestProviderManager(t *testing.T) { mid := peer.ID("testing") p := NewProviderManager(mid) a := u.Key("test") - p.AddProvider(a, &peer.Peer{}) + p.AddProvider(a, peer.WithIDString("testingprovider")) resp := p.GetProviders(a) if len(resp) != 1 { t.Fatal("Could not retrieve provider.") diff --git a/routing/dht/query.go b/routing/dht/query.go index 0a9ca0bd8..ef3670c7d 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -26,10 +26,10 @@ type dhtQuery struct { } type dhtQueryResult struct { - value []byte // GetValue - peer *peer.Peer // FindPeer - providerPeers []*peer.Peer // GetProviders - closerPeers []*peer.Peer // * + value []byte // GetValue + peer peer.Peer // FindPeer + providerPeers []peer.Peer // GetProviders + closerPeers []peer.Peer // * success bool } @@ -47,10 +47,10 @@ func newQuery(k u.Key, f queryFunc) *dhtQuery { // - the value // - a list of peers potentially better able to serve the query // - an error -type queryFunc func(context.Context, *peer.Peer) (*dhtQueryResult, error) +type queryFunc func(context.Context, peer.Peer) (*dhtQueryResult, error) // Run runs the query at hand. pass in a list of peers to use first. -func (q *dhtQuery) Run(ctx context.Context, peers []*peer.Peer) (*dhtQueryResult, error) { +func (q *dhtQuery) Run(ctx context.Context, peers []peer.Peer) (*dhtQueryResult, error) { runner := newQueryRunner(ctx, q) return runner.Run(peers) } @@ -100,7 +100,7 @@ func newQueryRunner(ctx context.Context, q *dhtQuery) *dhtQueryRunner { } } -func (r *dhtQueryRunner) Run(peers []*peer.Peer) (*dhtQueryResult, error) { +func (r *dhtQueryRunner) Run(peers []peer.Peer) (*dhtQueryResult, error) { log.Debug("Run query with %d peers.", len(peers)) if len(peers) == 0 { log.Warning("Running query with no peers!") @@ -148,7 +148,7 @@ func (r *dhtQueryRunner) Run(peers []*peer.Peer) (*dhtQueryResult, error) { return nil, err } -func (r *dhtQueryRunner) addPeerToQuery(next *peer.Peer, benchmark *peer.Peer) { +func (r *dhtQueryRunner) addPeerToQuery(next peer.Peer, benchmark peer.Peer) { if next == nil { // wtf why are peers nil?!? log.Error("Query getting nil peers!!!\n") @@ -156,7 +156,7 @@ func (r *dhtQueryRunner) addPeerToQuery(next *peer.Peer, benchmark *peer.Peer) { } // if new peer further away than whom we got it from, bother (loops) - if benchmark != nil && kb.Closer(benchmark.ID, next.ID, r.query.key) { + if benchmark != nil && kb.Closer(benchmark.ID(), next.ID(), r.query.key) { return } @@ -200,7 +200,7 @@ func (r *dhtQueryRunner) spawnWorkers() { } } -func (r *dhtQueryRunner) queryPeer(p *peer.Peer) { +func (r *dhtQueryRunner) queryPeer(p peer.Peer) { log.Debug("spawned worker for: %v\n", p) // make sure we rate limit concurrency. diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 55ef265cb..7f004dc47 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -23,13 +23,13 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error return err } - var peers []*peer.Peer + var peers []peer.Peer for _, route := range dht.routingTables { npeers := route.NearestPeers(kb.ConvertKey(key), KValue) peers = append(peers, npeers...) } - query := newQuery(key, func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) { + query := newQuery(key, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { log.Debug("%s PutValue qry part %v", dht.self, p) err := dht.putValueToNetwork(ctx, p, string(key), value) if err != nil { @@ -65,7 +65,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { } // setup the Query - query := newQuery(key, func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) { + query := newQuery(key, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { val, peers, err := dht.getValueOrPeers(ctx, p, key, routeLevel) if err != nil { @@ -117,8 +117,8 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { return nil } -func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int) <-chan *peer.Peer { - peerOut := make(chan *peer.Peer, count) +func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int) <-chan peer.Peer { + peerOut := make(chan peer.Peer, count) go func() { ps := newPeerSet() provs := dht.providers.GetProviders(key) @@ -136,7 +136,7 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), AlphaValue) for _, pp := range peers { wg.Add(1) - go func(p *peer.Peer) { + go func(p peer.Peer) { defer wg.Done() pmes, err := dht.findProvidersSingle(ctx, p, key, 0) if err != nil { @@ -153,7 +153,7 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int } //TODO: this function could also be done asynchronously -func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*Message_Peer, ps *peerSet, count int, out chan *peer.Peer) { +func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*Message_Peer, ps *peerSet, count int, out chan peer.Peer) { for _, pbp := range peers { // construct new peer @@ -173,7 +173,7 @@ func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*Message_Peer, ps *peerSet // Find specific Peer // FindPeer searches for a peer with given ID. -func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (*peer.Peer, error) { +func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) { // Check if were already connected to them p, _ := dht.FindLocal(id) @@ -186,7 +186,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (*peer.Peer, error if p == nil { return nil, nil } - if p.ID.Equal(id) { + if p.ID().Equal(id) { return p, nil } @@ -205,7 +205,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (*peer.Peer, error continue } - if nxtPeer.ID.Equal(id) { + if nxtPeer.ID().Equal(id) { return nxtPeer, nil } @@ -214,7 +214,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (*peer.Peer, error return nil, u.ErrNotFound } -func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (*peer.Peer, error) { +func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (peer.Peer, error) { // Check if were already connected to them p, _ := dht.FindLocal(id) @@ -230,7 +230,7 @@ func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (*peer.Pee } // setup query function - query := newQuery(u.Key(id), func(ctx context.Context, p *peer.Peer) (*dhtQueryResult, error) { + query := newQuery(u.Key(id), func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { pmes, err := dht.findPeerSingle(ctx, p, id, routeLevel) if err != nil { log.Error("%s getPeer error: %v", dht.self, err) @@ -242,7 +242,7 @@ func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (*peer.Pee routeLevel++ } - nxtprs := make([]*peer.Peer, len(plist)) + nxtprs := make([]peer.Peer, len(plist)) for i, fp := range plist { nxtp, err := dht.peerFromInfo(fp) if err != nil { @@ -250,7 +250,7 @@ func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (*peer.Pee continue } - if nxtp.ID.Equal(id) { + if nxtp.ID().Equal(id) { return &dhtQueryResult{peer: nxtp, success: true}, nil } @@ -272,7 +272,7 @@ func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (*peer.Pee } // Ping a peer, log the time it took -func (dht *IpfsDHT) Ping(ctx context.Context, p *peer.Peer) error { +func (dht *IpfsDHT) Ping(ctx context.Context, p peer.Peer) error { // Thoughts: maybe this should accept an ID and do a peer lookup? log.Info("ping %s start", p) diff --git a/routing/dht/util.go b/routing/dht/util.go index d12f7f9df..3cc812638 100644 --- a/routing/dht/util.go +++ b/routing/dht/util.go @@ -52,15 +52,15 @@ func newPeerSet() *peerSet { return ps } -func (ps *peerSet) Add(p *peer.Peer) { +func (ps *peerSet) Add(p peer.Peer) { ps.lk.Lock() - ps.ps[string(p.ID)] = true + ps.ps[string(p.ID())] = true ps.lk.Unlock() } -func (ps *peerSet) Contains(p *peer.Peer) bool { +func (ps *peerSet) Contains(p peer.Peer) bool { ps.lk.RLock() - _, ok := ps.ps[string(p.ID)] + _, ok := ps.ps[string(p.ID())] ps.lk.RUnlock() return ok } @@ -71,12 +71,12 @@ func (ps *peerSet) Size() int { return len(ps.ps) } -func (ps *peerSet) AddIfSmallerThan(p *peer.Peer, maxsize int) bool { +func (ps *peerSet) AddIfSmallerThan(p peer.Peer, maxsize int) bool { var success bool ps.lk.Lock() - if _, ok := ps.ps[string(p.ID)]; !ok && len(ps.ps) < maxsize { + if _, ok := ps.ps[string(p.ID())]; !ok && len(ps.ps) < maxsize { success = true - ps.ps[string(p.ID)] = true + ps.ps[string(p.ID())] = true } ps.lk.Unlock() return success diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 3a9c71fad..b114f9e21 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -23,7 +23,7 @@ func (b *Bucket) find(id peer.ID) *list.Element { b.lk.RLock() defer b.lk.RUnlock() for e := b.list.Front(); e != nil; e = e.Next() { - if e.Value.(*peer.Peer).ID.Equal(id) { + if e.Value.(peer.Peer).ID().Equal(id) { return e } } @@ -36,18 +36,18 @@ func (b *Bucket) moveToFront(e *list.Element) { b.lk.Unlock() } -func (b *Bucket) pushFront(p *peer.Peer) { +func (b *Bucket) pushFront(p peer.Peer) { b.lk.Lock() b.list.PushFront(p) b.lk.Unlock() } -func (b *Bucket) popBack() *peer.Peer { +func (b *Bucket) popBack() peer.Peer { b.lk.Lock() defer b.lk.Unlock() last := b.list.Back() b.list.Remove(last) - return last.Value.(*peer.Peer) + return last.Value.(peer.Peer) } func (b *Bucket) len() int { @@ -68,7 +68,7 @@ func (b *Bucket) Split(cpl int, target ID) *Bucket { newbuck.list = out e := b.list.Front() for e != nil { - peerID := ConvertPeerID(e.Value.(*peer.Peer).ID) + peerID := ConvertPeerID(e.Value.(peer.Peer).ID()) peerCPL := commonPrefixLen(peerID, target) if peerCPL > cpl { cur := e diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 45ffb3cdf..6f37e94de 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -42,10 +42,10 @@ func NewRoutingTable(bucketsize int, localID ID, latency time.Duration) *Routing // Update adds or moves the given peer to the front of its respective bucket // If a peer gets removed from a bucket, it is returned -func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer { +func (rt *RoutingTable) Update(p peer.Peer) peer.Peer { rt.tabLock.Lock() defer rt.tabLock.Unlock() - peerID := ConvertPeerID(p.ID) + peerID := ConvertPeerID(p.ID()) cpl := commonPrefixLen(peerID, rt.local) bucketID := cpl @@ -54,7 +54,7 @@ func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer { } bucket := rt.Buckets[bucketID] - e := bucket.find(p.ID) + e := bucket.find(p.ID()) if e == nil { // New peer, add to bucket if p.GetLatency() > rt.maxLatency { @@ -93,7 +93,7 @@ func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer { // A helper struct to sort peers by their distance to the local node type peerDistance struct { - p *peer.Peer + p peer.Peer distance ID } @@ -110,8 +110,8 @@ func (p peerSorterArr) Less(a, b int) bool { func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) peerSorterArr { for e := peerList.Front(); e != nil; e = e.Next() { - p := e.Value.(*peer.Peer) - pID := ConvertPeerID(p.ID) + p := e.Value.(peer.Peer) + pID := ConvertPeerID(p.ID()) pd := peerDistance{ p: p, distance: xor(target, pID), @@ -126,16 +126,16 @@ func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) pe } // Find a specific peer by ID or return nil -func (rt *RoutingTable) Find(id peer.ID) *peer.Peer { +func (rt *RoutingTable) Find(id peer.ID) peer.Peer { srch := rt.NearestPeers(ConvertPeerID(id), 1) - if len(srch) == 0 || !srch[0].ID.Equal(id) { + if len(srch) == 0 || !srch[0].ID().Equal(id) { return nil } return srch[0] } // NearestPeer returns a single peer that is nearest to the given ID -func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer { +func (rt *RoutingTable) NearestPeer(id ID) peer.Peer { peers := rt.NearestPeers(id, 1) if len(peers) > 0 { return peers[0] @@ -146,7 +146,7 @@ func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer { } // NearestPeers returns a list of the 'count' closest peers to the given ID -func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { +func (rt *RoutingTable) NearestPeers(id ID, count int) []peer.Peer { rt.tabLock.RLock() defer rt.tabLock.RUnlock() cpl := commonPrefixLen(id, rt.local) @@ -178,7 +178,7 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer { // Sort by distance to local peer sort.Sort(peerArr) - var out []*peer.Peer + var out []peer.Peer for i := 0; i < count && i < peerArr.Len(); i++ { out = append(out, peerArr[i].p) } @@ -197,11 +197,11 @@ func (rt *RoutingTable) Size() int { // ListPeers takes a RoutingTable and returns a list of all peers from all buckets in the table. // NOTE: This is potentially unsafe... use at your own risk -func (rt *RoutingTable) ListPeers() []*peer.Peer { - var peers []*peer.Peer +func (rt *RoutingTable) ListPeers() []peer.Peer { + var peers []peer.Peer for _, buck := range rt.Buckets { for e := buck.getIter(); e != nil; e = e.Next() { - peers = append(peers, e.Value.(*peer.Peer)) + peers = append(peers, e.Value.(peer.Peer)) } } return peers @@ -213,6 +213,6 @@ func (rt *RoutingTable) Print() { rt.tabLock.RLock() peers := rt.ListPeers() for i, p := range peers { - fmt.Printf("%d) %s %s\n", i, p.ID.Pretty(), p.GetLatency().String()) + fmt.Printf("%d) %s %s\n", i, p.ID().Pretty(), p.GetLatency().String()) } } diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index cc1cdfba1..2b45d1572 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -10,11 +10,10 @@ import ( peer "github.com/jbenet/go-ipfs/peer" ) -func _randPeer() *peer.Peer { - p := new(peer.Peer) - p.ID = make(peer.ID, 16) - crand.Read(p.ID) - return p +func _randPeer() peer.Peer { + id := make(peer.ID, 16) + crand.Read(id) + return peer.WithID(id) } func _randID() ID { @@ -29,25 +28,25 @@ func _randID() ID { func TestBucket(t *testing.T) { b := newBucket() - peers := make([]*peer.Peer, 100) + peers := make([]peer.Peer, 100) for i := 0; i < 100; i++ { peers[i] = _randPeer() b.pushFront(peers[i]) } local := _randPeer() - localID := ConvertPeerID(local.ID) + localID := ConvertPeerID(local.ID()) i := rand.Intn(len(peers)) - e := b.find(peers[i].ID) + e := b.find(peers[i].ID()) if e == nil { t.Errorf("Failed to find peer: %v", peers[i]) } - spl := b.Split(0, ConvertPeerID(local.ID)) + spl := b.Split(0, ConvertPeerID(local.ID())) llist := b.list for e := llist.Front(); e != nil; e = e.Next() { - p := ConvertPeerID(e.Value.(*peer.Peer).ID) + p := ConvertPeerID(e.Value.(peer.Peer).ID()) cpl := commonPrefixLen(p, localID) if cpl > 0 { t.Fatalf("Split failed. found id with cpl > 0 in 0 bucket") @@ -56,7 +55,7 @@ func TestBucket(t *testing.T) { rlist := spl.list for e := rlist.Front(); e != nil; e = e.Next() { - p := ConvertPeerID(e.Value.(*peer.Peer).ID) + p := ConvertPeerID(e.Value.(peer.Peer).ID()) cpl := commonPrefixLen(p, localID) if cpl == 0 { t.Fatalf("Split failed. found id with cpl == 0 in non 0 bucket") @@ -67,9 +66,9 @@ func TestBucket(t *testing.T) { // Right now, this just makes sure that it doesnt hang or crash func TestTableUpdate(t *testing.T) { local := _randPeer() - rt := NewRoutingTable(10, ConvertPeerID(local.ID), time.Hour) + rt := NewRoutingTable(10, ConvertPeerID(local.ID()), time.Hour) - peers := make([]*peer.Peer, 100) + peers := make([]peer.Peer, 100) for i := 0; i < 100; i++ { peers[i] = _randPeer() } @@ -93,33 +92,33 @@ func TestTableUpdate(t *testing.T) { func TestTableFind(t *testing.T) { local := _randPeer() - rt := NewRoutingTable(10, ConvertPeerID(local.ID), time.Hour) + rt := NewRoutingTable(10, ConvertPeerID(local.ID()), time.Hour) - peers := make([]*peer.Peer, 100) + peers := make([]peer.Peer, 100) for i := 0; i < 5; i++ { peers[i] = _randPeer() rt.Update(peers[i]) } t.Logf("Searching for peer: '%s'", peers[2]) - found := rt.NearestPeer(ConvertPeerID(peers[2].ID)) - if !found.ID.Equal(peers[2].ID) { + found := rt.NearestPeer(ConvertPeerID(peers[2].ID())) + if !found.ID().Equal(peers[2].ID()) { t.Fatalf("Failed to lookup known node...") } } func TestTableFindMultiple(t *testing.T) { local := _randPeer() - rt := NewRoutingTable(20, ConvertPeerID(local.ID), time.Hour) + rt := NewRoutingTable(20, ConvertPeerID(local.ID()), time.Hour) - peers := make([]*peer.Peer, 100) + peers := make([]peer.Peer, 100) for i := 0; i < 18; i++ { peers[i] = _randPeer() rt.Update(peers[i]) } t.Logf("Searching for peer: '%s'", peers[2]) - found := rt.NearestPeers(ConvertPeerID(peers[2].ID), 15) + found := rt.NearestPeers(ConvertPeerID(peers[2].ID()), 15) if len(found) != 15 { t.Fatalf("Got back different number of peers than we expected.") } @@ -131,7 +130,7 @@ func TestTableFindMultiple(t *testing.T) { func TestTableMultithreaded(t *testing.T) { local := peer.ID("localPeer") tab := NewRoutingTable(20, ConvertPeerID(local), time.Hour) - var peers []*peer.Peer + var peers []peer.Peer for i := 0; i < 500; i++ { peers = append(peers, _randPeer()) } @@ -156,7 +155,7 @@ func TestTableMultithreaded(t *testing.T) { go func() { for i := 0; i < 1000; i++ { n := rand.Intn(len(peers)) - tab.Find(peers[n].ID) + tab.Find(peers[n].ID()) } done <- struct{}{} }() @@ -170,7 +169,7 @@ func BenchmarkUpdates(b *testing.B) { local := ConvertKey("localKey") tab := NewRoutingTable(20, local, time.Hour) - var peers []*peer.Peer + var peers []peer.Peer for i := 0; i < b.N; i++ { peers = append(peers, _randPeer()) } @@ -186,7 +185,7 @@ func BenchmarkFinds(b *testing.B) { local := ConvertKey("localKey") tab := NewRoutingTable(20, local, time.Hour) - var peers []*peer.Peer + var peers []peer.Peer for i := 0; i < b.N; i++ { peers = append(peers, _randPeer()) tab.Update(peers[i]) @@ -194,6 +193,6 @@ func BenchmarkFinds(b *testing.B) { b.StartTimer() for i := 0; i < b.N; i++ { - tab.Find(peers[i].ID) + tab.Find(peers[i].ID()) } } diff --git a/routing/mock/routing.go b/routing/mock/routing.go index 954914c3b..caa74ffe3 100644 --- a/routing/mock/routing.go +++ b/routing/mock/routing.go @@ -17,10 +17,10 @@ var _ routing.IpfsRouting = &MockRouter{} type MockRouter struct { datastore ds.Datastore hashTable RoutingServer - peer *peer.Peer + peer peer.Peer } -func NewMockRouter(local *peer.Peer, dstore ds.Datastore) routing.IpfsRouting { +func NewMockRouter(local peer.Peer, dstore ds.Datastore) routing.IpfsRouting { return &MockRouter{ datastore: dstore, peer: local, @@ -50,16 +50,16 @@ func (mr *MockRouter) GetValue(ctx context.Context, key u.Key) ([]byte, error) { return data, nil } -func (mr *MockRouter) FindProviders(ctx context.Context, key u.Key) ([]*peer.Peer, error) { +func (mr *MockRouter) FindProviders(ctx context.Context, key u.Key) ([]peer.Peer, error) { return nil, nil } -func (mr *MockRouter) FindPeer(ctx context.Context, pid peer.ID) (*peer.Peer, error) { +func (mr *MockRouter) FindPeer(ctx context.Context, pid peer.ID) (peer.Peer, error) { return nil, nil } -func (mr *MockRouter) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-chan *peer.Peer { - out := make(chan *peer.Peer) +func (mr *MockRouter) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-chan peer.Peer { + out := make(chan peer.Peer) go func() { defer close(out) for i, p := range mr.hashTable.Providers(k) { @@ -81,11 +81,11 @@ func (mr *MockRouter) Provide(_ context.Context, key u.Key) error { } type RoutingServer interface { - Announce(*peer.Peer, u.Key) error + Announce(peer.Peer, u.Key) error - Providers(u.Key) []*peer.Peer + Providers(u.Key) []peer.Peer - Client(p *peer.Peer) routing.IpfsRouting + Client(p peer.Peer) routing.IpfsRouting } func VirtualRoutingServer() RoutingServer { @@ -99,7 +99,7 @@ type hashTable struct { providers map[u.Key]peer.Map } -func (rs *hashTable) Announce(p *peer.Peer, k u.Key) error { +func (rs *hashTable) Announce(p peer.Peer, k u.Key) error { rs.lock.Lock() defer rs.lock.Unlock() @@ -111,10 +111,10 @@ func (rs *hashTable) Announce(p *peer.Peer, k u.Key) error { return nil } -func (rs *hashTable) Providers(k u.Key) []*peer.Peer { +func (rs *hashTable) Providers(k u.Key) []peer.Peer { rs.lock.RLock() defer rs.lock.RUnlock() - ret := make([]*peer.Peer, 0) + ret := make([]peer.Peer, 0) peerset, ok := rs.providers[k] if !ok { return ret @@ -131,7 +131,7 @@ func (rs *hashTable) Providers(k u.Key) []*peer.Peer { return ret } -func (rs *hashTable) Client(p *peer.Peer) routing.IpfsRouting { +func (rs *hashTable) Client(p peer.Peer) routing.IpfsRouting { return &MockRouter{ peer: p, hashTable: rs, diff --git a/routing/mock/routing_test.go b/routing/mock/routing_test.go index 650f5d3d5..196e00b5e 100644 --- a/routing/mock/routing_test.go +++ b/routing/mock/routing_test.go @@ -20,9 +20,7 @@ func TestKeyNotFound(t *testing.T) { func TestSetAndGet(t *testing.T) { pid := peer.ID([]byte("the peer id")) - p := &peer.Peer{ - ID: pid, - } + p := peer.WithID(pid) k := u.Key("42") rs := VirtualRoutingServer() err := rs.Announce(p, k) @@ -34,7 +32,7 @@ func TestSetAndGet(t *testing.T) { t.Fatal("should be one") } for _, elem := range providers { - if bytes.Equal(elem.ID, pid) { + if bytes.Equal(elem.ID(), pid) { return } } @@ -42,7 +40,7 @@ func TestSetAndGet(t *testing.T) { } func TestClientFindProviders(t *testing.T) { - peer := &peer.Peer{ID: []byte("42")} + peer := peer.WithIDString("42") rs := VirtualRoutingServer() client := rs.Client(peer) @@ -57,7 +55,7 @@ func TestClientFindProviders(t *testing.T) { isInHT := false for _, p := range providersFromHashTable { - if bytes.Equal(p.ID, peer.ID) { + if bytes.Equal(p.ID(), peer.ID()) { isInHT = true } } @@ -67,7 +65,7 @@ func TestClientFindProviders(t *testing.T) { providersFromClient := client.FindProvidersAsync(context.Background(), u.Key("hello"), max) isInClient := false for p := range providersFromClient { - if bytes.Equal(p.ID, peer.ID) { + if bytes.Equal(p.ID(), peer.ID()) { isInClient = true } } @@ -81,9 +79,7 @@ func TestClientOverMax(t *testing.T) { k := u.Key("hello") numProvidersForHelloKey := 100 for i := 0; i < numProvidersForHelloKey; i++ { - peer := &peer.Peer{ - ID: []byte(string(i)), - } + peer := peer.WithIDString(string(i)) err := rs.Announce(peer, k) if err != nil { t.Fatal(err) @@ -96,7 +92,7 @@ func TestClientOverMax(t *testing.T) { } max := 10 - peer := &peer.Peer{ID: []byte("TODO")} + peer := peer.WithIDString("TODO") client := rs.Client(peer) providersFromClient := client.FindProvidersAsync(context.Background(), k, max) @@ -118,9 +114,7 @@ func TestCanceledContext(t *testing.T) { i := 0 go func() { // infinite stream for { - peer := &peer.Peer{ - ID: []byte(string(i)), - } + peer := peer.WithIDString(string(i)) err := rs.Announce(peer, k) if err != nil { t.Fatal(err) @@ -129,7 +123,7 @@ func TestCanceledContext(t *testing.T) { } }() - local := &peer.Peer{ID: []byte("peer id doesn't matter")} + local := peer.WithIDString("peer id doesn't matter") client := rs.Client(local) t.Log("warning: max is finite so this test is non-deterministic") diff --git a/routing/routing.go b/routing/routing.go index f3dd0c9d8..cb60e5ee8 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -10,7 +10,7 @@ import ( // IpfsRouting is the routing module interface // It is implemented by things like DHTs, etc. type IpfsRouting interface { - FindProvidersAsync(context.Context, u.Key, int) <-chan *peer.Peer + FindProvidersAsync(context.Context, u.Key, int) <-chan peer.Peer // Basic Put/Get @@ -28,5 +28,5 @@ type IpfsRouting interface { // Find specific Peer // FindPeer searches for a peer with given ID. - FindPeer(context.Context, peer.ID) (*peer.Peer, error) + FindPeer(context.Context, peer.ID) (peer.Peer, error) } From 90cc3562e2ad8f9b10051bee62cfb85f9b28879e Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 20 Oct 2014 03:26:44 -0700 Subject: [PATCH 0278/3817] peer.Peer is now an interface ![](http://m.memegen.com/77n7dk.jpg) This commit was moved from ipfs/go-namesys@4b49a77243866d5ac8fdb68245beb81b00589241 --- namesys/resolve_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 5e652f42f..c6ee63351 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -11,9 +11,7 @@ import ( ) func TestRoutingResolve(t *testing.T) { - local := &peer.Peer{ - ID: []byte("testID"), - } + local := peer.WithIDString("testID") lds := ds.NewMapDatastore() d := mock.NewMockRouter(local, lds) From ebe77f4d4426ba6cc8eb2d00a83a73067ffede85 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 20 Oct 2014 06:37:09 -0700 Subject: [PATCH 0279/3817] peerstore Put -> Add Changed lots of peer use, and changed the peerstore to ensure there is only ever one peer in use. Fixed #174 This commit was moved from ipfs/go-ipfs-routing@e90a5ad21dcb2b77cfbb11222a5de267d5fb6342 --- routing/dht/ext_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index c4ba09414..b38b12d6c 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -203,7 +203,7 @@ func TestNotFound(t *testing.T) { local := peer.WithIDString("test_peer") peerstore := peer.NewPeerstore() - peerstore.Put(local) + peerstore.Add(local) d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) @@ -269,7 +269,7 @@ func TestLessThanKResponses(t *testing.T) { fs := &fauxSender{} local := peer.WithIDString("test_peer") peerstore := peer.NewPeerstore() - peerstore.Put(local) + peerstore.Add(local) d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) From 904eced75a31a514c9303302bf8a0530d7893f03 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 21 Oct 2014 15:10:58 -0700 Subject: [PATCH 0280/3817] renamed datastore.go -> go-datastore This commit was moved from ipfs/go-ipfs-routing@edcabfa15356d57197d5158a85babe0ede6406b6 --- routing/dht/dht.go | 2 +- routing/dht/dht_test.go | 2 +- routing/dht/ext_test.go | 2 +- routing/dht/handlers.go | 2 +- routing/mock/routing.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 3617b7142..a0f14aa91 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -15,7 +15,7 @@ import ( u "github.com/jbenet/go-ipfs/util" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 23d9fcf17..590d5ead3 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -6,7 +6,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ci "github.com/jbenet/go-ipfs/crypto" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index b38b12d6c..43bd34f8a 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -8,7 +8,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" msg "github.com/jbenet/go-ipfs/net/message" mux "github.com/jbenet/go-ipfs/net/mux" peer "github.com/jbenet/go-ipfs/peer" diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 44802babb..d3c4d23e3 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -8,7 +8,7 @@ import ( peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ) var CloserPeerCount = 4 diff --git a/routing/mock/routing.go b/routing/mock/routing.go index caa74ffe3..9c6919589 100644 --- a/routing/mock/routing.go +++ b/routing/mock/routing.go @@ -6,7 +6,7 @@ import ( "sync" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" peer "github.com/jbenet/go-ipfs/peer" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" From 1ef2560705fd508d957a45487688c3db6af534af Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 21 Oct 2014 15:10:58 -0700 Subject: [PATCH 0281/3817] renamed datastore.go -> go-datastore This commit was moved from ipfs/go-namesys@52bed613052f4d8140d5675e8714560fed1f645c --- namesys/resolve_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index c6ee63351..d7d49c5a6 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -3,7 +3,7 @@ package namesys import ( "testing" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ci "github.com/jbenet/go-ipfs/crypto" "github.com/jbenet/go-ipfs/peer" mock "github.com/jbenet/go-ipfs/routing/mock" From f1b5aa263bfdcc7e0142cbfa82eae992080b1795 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 21 Oct 2014 15:10:58 -0700 Subject: [PATCH 0282/3817] renamed datastore.go -> go-datastore This commit was moved from ipfs/go-unixfs@668d7de7e64318cbf218a425d16755c487a5723a --- unixfs/io/dagmodifier_test.go | 2 +- unixfs/io/dagwriter_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index 5c96aaae4..5e9edb727 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -13,7 +13,7 @@ import ( ft "github.com/jbenet/go-ipfs/unixfs" u "github.com/jbenet/go-ipfs/util" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ) func getMockDagServ(t *testing.T) *mdag.DAGService { diff --git a/unixfs/io/dagwriter_test.go b/unixfs/io/dagwriter_test.go index 73ba5c4e9..ddf5f9d66 100644 --- a/unixfs/io/dagwriter_test.go +++ b/unixfs/io/dagwriter_test.go @@ -5,7 +5,7 @@ import ( "io" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" bs "github.com/jbenet/go-ipfs/blockservice" chunk "github.com/jbenet/go-ipfs/importer/chunk" mdag "github.com/jbenet/go-ipfs/merkledag" From 658d2c22f8f36844b5a4ed4a458d8881ce7feaf3 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 21 Oct 2014 15:10:58 -0700 Subject: [PATCH 0283/3817] renamed datastore.go -> go-datastore This commit was moved from ipfs/go-blockservice@131764a94b78a597a3eebdf79e6900a4eab7b67a --- blockservice/blocks_test.go | 2 +- blockservice/blockservice.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index 764d2d400..41eceaae6 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -4,7 +4,7 @@ import ( "bytes" "testing" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" blocks "github.com/jbenet/go-ipfs/blocks" u "github.com/jbenet/go-ipfs/util" ) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index dcf15ce95..9f914dc38 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -5,7 +5,7 @@ import ( "time" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" blocks "github.com/jbenet/go-ipfs/blocks" From 90b06f6cb8954ed2e99a0c5e9ca197dc99a3cd82 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 16 Oct 2014 00:20:46 -0700 Subject: [PATCH 0284/3817] add blockset and bloomfilter and beginnings of pinning service This commit was moved from ipfs/go-ipfs-pinner@c16410f95946003a63b5b1b1f4cff8eeb80b0928 --- pinning/pinner/pin.go | 88 ++++++++++++++++++++++++++++++++++++++++ pinning/pinner/refset.go | 40 ++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 pinning/pinner/pin.go create mode 100644 pinning/pinner/refset.go diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go new file mode 100644 index 000000000..9607d00bc --- /dev/null +++ b/pinning/pinner/pin.go @@ -0,0 +1,88 @@ +package pin + +import ( + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + "github.com/jbenet/go-ipfs/blocks/set" + mdag "github.com/jbenet/go-ipfs/merkledag" + "github.com/jbenet/go-ipfs/util" +) + +type Pinner interface { + Pin(*mdag.Node, bool) error + Unpin(util.Key, bool) error +} + +type pinner struct { + recursePin set.BlockSet + directPin set.BlockSet + indirPin set.BlockSet + dserv *mdag.DAGService +} + +func NewPinner(dstore ds.Datastore, serv *mdag.DAGService) Pinner { + return &pinner{ + recursePin: set.NewSimpleBlockSet(), + directPin: set.NewSimpleBlockSet(), + indirPin: NewRefCountBlockSet(), + dserv: serv, + } +} + +func (p *pinner) Pin(node *mdag.Node, recurse bool) error { + k, err := node.Key() + if err != nil { + return err + } + + if recurse { + if p.recursePin.HasKey(k) { + return nil + } + + p.recursePin.AddBlock(k) + + err := p.pinLinks(node) + if err != nil { + return err + } + } else { + p.directPin.AddBlock(k) + } + return nil +} + +func (p *pinner) Unpin(k util.Key, recurse bool) error { + panic("not yet implemented!") + return nil +} + +func (p *pinner) pinIndirectRecurse(node *mdag.Node) error { + k, err := node.Key() + if err != nil { + return err + } + + p.indirPin.AddBlock(k) + return p.pinLinks(node) +} + +func (p *pinner) pinLinks(node *mdag.Node) error { + for _, l := range node.Links { + subnode, err := l.GetNode(p.dserv) + if err != nil { + // TODO: Maybe just log and continue? + return err + } + err = p.pinIndirectRecurse(subnode) + if err != nil { + return err + } + } + return nil +} + +func (p *pinner) IsPinned(key util.Key) bool { + return p.recursePin.HasKey(key) || + p.directPin.HasKey(key) || + p.indirPin.HasKey(key) +} diff --git a/pinning/pinner/refset.go b/pinning/pinner/refset.go new file mode 100644 index 000000000..1756bf50a --- /dev/null +++ b/pinning/pinner/refset.go @@ -0,0 +1,40 @@ +package pin + +import ( + "github.com/jbenet/go-ipfs/blocks/bloom" + "github.com/jbenet/go-ipfs/blocks/set" + "github.com/jbenet/go-ipfs/util" +) + +type refCntBlockSet struct { + blocks map[util.Key]int +} + +func NewRefCountBlockSet() set.BlockSet { + return &refCntBlockSet{blocks: make(map[util.Key]int)} +} + +func (r *refCntBlockSet) AddBlock(k util.Key) { + r.blocks[k]++ +} + +func (r *refCntBlockSet) RemoveBlock(k util.Key) { + v, ok := r.blocks[k] + if !ok { + return + } + if v <= 1 { + delete(r.blocks, k) + } else { + r.blocks[k] = v - 1 + } +} + +func (r *refCntBlockSet) HasKey(k util.Key) bool { + _, ok := r.blocks[k] + return ok +} + +func (r *refCntBlockSet) GetBloomFilter() bloom.Filter { + return nil +} From 868cd44ce61646de6bf653167530394c1ac99ac8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 16 Oct 2014 19:20:33 -0700 Subject: [PATCH 0285/3817] implement unpin and add a datastore backed blockset This commit was moved from ipfs/go-ipfs-pinner@93a20c7f60c949cad5d711718291adbd3b13ff5b --- pinning/pinner/pin.go | 45 ++++++++++++++++++++++++++++++++++++---- pinning/pinner/refset.go | 40 ----------------------------------- 2 files changed, 41 insertions(+), 44 deletions(-) delete mode 100644 pinning/pinner/refset.go diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 9607d00bc..d7184e24f 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -17,14 +17,19 @@ type pinner struct { directPin set.BlockSet indirPin set.BlockSet dserv *mdag.DAGService + dstore ds.Datastore } func NewPinner(dstore ds.Datastore, serv *mdag.DAGService) Pinner { + rcset := set.NewDBWrapperSet(dstore, "/pinned/recurse/", set.NewSimpleBlockSet()) + dirset := set.NewDBWrapperSet(dstore, "/pinned/direct/", set.NewSimpleBlockSet()) + indset := set.NewDBWrapperSet(dstore, "/pinned/indirect/", set.NewRefCountBlockSet()) return &pinner{ - recursePin: set.NewSimpleBlockSet(), - directPin: set.NewSimpleBlockSet(), - indirPin: NewRefCountBlockSet(), + recursePin: rcset, + directPin: dirset, + indirPin: indset, dserv: serv, + dstore: dstore, } } @@ -52,7 +57,39 @@ func (p *pinner) Pin(node *mdag.Node, recurse bool) error { } func (p *pinner) Unpin(k util.Key, recurse bool) error { - panic("not yet implemented!") + if recurse { + p.recursePin.RemoveBlock(k) + node, err := p.dserv.Get(k) + if err != nil { + return err + } + + return p.unpinLinks(node) + } else { + p.directPin.RemoveBlock(k) + } + return nil +} + +func (p *pinner) unpinLinks(node *mdag.Node) error { + for _, l := range node.Links { + node, err := l.GetNode(p.dserv) + if err != nil { + return err + } + + k, err := node.Key() + if err != nil { + return err + } + + p.recursePin.RemoveBlock(k) + + err = p.unpinLinks(node) + if err != nil { + return err + } + } return nil } diff --git a/pinning/pinner/refset.go b/pinning/pinner/refset.go deleted file mode 100644 index 1756bf50a..000000000 --- a/pinning/pinner/refset.go +++ /dev/null @@ -1,40 +0,0 @@ -package pin - -import ( - "github.com/jbenet/go-ipfs/blocks/bloom" - "github.com/jbenet/go-ipfs/blocks/set" - "github.com/jbenet/go-ipfs/util" -) - -type refCntBlockSet struct { - blocks map[util.Key]int -} - -func NewRefCountBlockSet() set.BlockSet { - return &refCntBlockSet{blocks: make(map[util.Key]int)} -} - -func (r *refCntBlockSet) AddBlock(k util.Key) { - r.blocks[k]++ -} - -func (r *refCntBlockSet) RemoveBlock(k util.Key) { - v, ok := r.blocks[k] - if !ok { - return - } - if v <= 1 { - delete(r.blocks, k) - } else { - r.blocks[k] = v - 1 - } -} - -func (r *refCntBlockSet) HasKey(k util.Key) bool { - _, ok := r.blocks[k] - return ok -} - -func (r *refCntBlockSet) GetBloomFilter() bloom.Filter { - return nil -} From 2425c242e80f6c3d47dfe248b6ada1d1e5737705 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 17 Oct 2014 13:53:49 -0700 Subject: [PATCH 0286/3817] move indirect pinning to its own structure This commit was moved from ipfs/go-ipfs-pinner@bf289f5a6877d6e282e1e7b6c946bc543d9730a4 --- pinning/pinner/indirect.go | 43 ++++++++++++++++++++++++++++++++++++++ pinning/pinner/pin.go | 11 ++++++---- 2 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 pinning/pinner/indirect.go diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go new file mode 100644 index 000000000..b3b60e753 --- /dev/null +++ b/pinning/pinner/indirect.go @@ -0,0 +1,43 @@ +package pin + +import ( + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + bc "github.com/jbenet/go-ipfs/blocks/set" + "github.com/jbenet/go-ipfs/util" +) + +type indirectPin struct { + blockset bc.BlockSet + refCounts map[util.Key]int +} + +func loadBlockSet(d ds.Datastore) (bc.BlockSet, map[util.Key]int) { + panic("Not yet implemented!") + return nil, nil +} + +func newIndirectPin(d ds.Datastore) indirectPin { + // suppose the blockset actually takes blocks, not just keys + bs, rc := loadBlockSet(d) + return indirectPin{bs, rc} +} + +func (i *indirectPin) Increment(k util.Key) { + c := i.refCounts[k] + i.refCounts[k] = c + 1 + if c <= 0 { + i.blockset.AddBlock(k) + } +} + +func (i *indirectPin) Decrement(k util.Key) { + c := i.refCounts[k] - 1 + i.refCounts[k] = c + if c <= 0 { + i.blockset.RemoveBlock(k) + } +} + +func (i *indirectPin) HasKey(k util.Key) bool { + return i.blockset.HasKey(k) +} diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index d7184e24f..86fff2afb 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -15,19 +15,22 @@ type Pinner interface { type pinner struct { recursePin set.BlockSet directPin set.BlockSet - indirPin set.BlockSet + indirPin indirectPin dserv *mdag.DAGService dstore ds.Datastore } func NewPinner(dstore ds.Datastore, serv *mdag.DAGService) Pinner { + + // Load set from given datastore... rcset := set.NewDBWrapperSet(dstore, "/pinned/recurse/", set.NewSimpleBlockSet()) dirset := set.NewDBWrapperSet(dstore, "/pinned/direct/", set.NewSimpleBlockSet()) - indset := set.NewDBWrapperSet(dstore, "/pinned/indirect/", set.NewRefCountBlockSet()) + + nsdstore := dstore // WRAP IN NAMESPACE return &pinner{ recursePin: rcset, directPin: dirset, - indirPin: indset, + indirPin: newIndirectPin(nsdstore), dserv: serv, dstore: dstore, } @@ -99,7 +102,7 @@ func (p *pinner) pinIndirectRecurse(node *mdag.Node) error { return err } - p.indirPin.AddBlock(k) + p.indirPin.Increment(k) return p.pinLinks(node) } From aa6b73a9c60c18509fb2b1eab83b155a3941190c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 16 Oct 2014 00:20:46 -0700 Subject: [PATCH 0287/3817] add blockset and bloomfilter and beginnings of pinning service This commit was moved from ipfs/go-merkledag@45092e1929ccd171c151abaee19d78e87e3197ca --- ipld/merkledag/merkledag.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 46b0c4089..8ce2fed6b 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -59,6 +59,14 @@ func MakeLink(n *Node) (*Link, error) { }, nil } +func (l *Link) GetNode(serv *DAGService) (*Node, error) { + if l.Node != nil { + return l.Node, nil + } + + return serv.Get(u.Key(l.Hash)) +} + // AddNodeLink adds a link to another node. func (n *Node) AddNodeLink(name string, that *Node) error { lnk, err := MakeLink(that) From 301564baf5ba306dbdf06effc7bfa2342b281704 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 19 Oct 2014 15:28:38 -0700 Subject: [PATCH 0288/3817] flesh out pinning object, needs tests and cli wiring still This commit was moved from ipfs/go-ipfs-pinner@829b696baf1864b10755b8bd5a50ffbad99a96ba --- pinning/pinner/indirect.go | 34 +++++++++++++++------ pinning/pinner/pin.go | 62 +++++++++++++++++++++++++++++++++++--- 2 files changed, 82 insertions(+), 14 deletions(-) diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index b3b60e753..bff1e545f 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -1,25 +1,41 @@ package pin import ( + "errors" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" - bc "github.com/jbenet/go-ipfs/blocks/set" + "github.com/jbenet/go-ipfs/blocks/set" "github.com/jbenet/go-ipfs/util" ) type indirectPin struct { - blockset bc.BlockSet + blockset set.BlockSet refCounts map[util.Key]int } -func loadBlockSet(d ds.Datastore) (bc.BlockSet, map[util.Key]int) { - panic("Not yet implemented!") - return nil, nil +func NewIndirectPin(dstore ds.Datastore) *indirectPin { + return &indirectPin{ + blockset: set.NewDBWrapperSet(dstore, set.NewSimpleBlockSet()), + refCounts: make(map[util.Key]int), + } } -func newIndirectPin(d ds.Datastore) indirectPin { - // suppose the blockset actually takes blocks, not just keys - bs, rc := loadBlockSet(d) - return indirectPin{bs, rc} +func loadIndirPin(d ds.Datastore, k ds.Key) (*indirectPin, error) { + irefcnt, err := d.Get(k) + if err != nil { + return nil, err + } + refcnt, ok := irefcnt.(map[util.Key]int) + if !ok { + return nil, errors.New("invalid type from datastore") + } + + var keys []util.Key + for k, _ := range refcnt { + keys = append(keys, k) + } + + return &indirectPin{blockset: set.SimpleSetFromKeys(keys), refCounts: refcnt}, nil } func (i *indirectPin) Increment(k util.Key) { diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 86fff2afb..d6f7f0f19 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -1,21 +1,29 @@ package pin import ( + + //ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + nsds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go/namespace" "github.com/jbenet/go-ipfs/blocks/set" mdag "github.com/jbenet/go-ipfs/merkledag" "github.com/jbenet/go-ipfs/util" ) +var recursePinDatastoreKey = ds.NewKey("/local/pins/recursive/keys") +var directPinDatastoreKey = ds.NewKey("/local/pins/direct/keys") +var indirectPinDatastoreKey = ds.NewKey("/local/pins/indirect/keys") + type Pinner interface { Pin(*mdag.Node, bool) error Unpin(util.Key, bool) error + Flush() error } type pinner struct { recursePin set.BlockSet directPin set.BlockSet - indirPin indirectPin + indirPin *indirectPin dserv *mdag.DAGService dstore ds.Datastore } @@ -23,14 +31,17 @@ type pinner struct { func NewPinner(dstore ds.Datastore, serv *mdag.DAGService) Pinner { // Load set from given datastore... - rcset := set.NewDBWrapperSet(dstore, "/pinned/recurse/", set.NewSimpleBlockSet()) - dirset := set.NewDBWrapperSet(dstore, "/pinned/direct/", set.NewSimpleBlockSet()) + rcds := nsds.Wrap(dstore, recursePinDatastoreKey) + rcset := set.NewDBWrapperSet(rcds, set.NewSimpleBlockSet()) - nsdstore := dstore // WRAP IN NAMESPACE + dirds := nsds.Wrap(dstore, directPinDatastoreKey) + dirset := set.NewDBWrapperSet(dirds, set.NewSimpleBlockSet()) + + nsdstore := nsds.Wrap(dstore, indirectPinDatastoreKey) return &pinner{ recursePin: rcset, directPin: dirset, - indirPin: newIndirectPin(nsdstore), + indirPin: NewIndirectPin(nsdstore), dserv: serv, dstore: dstore, } @@ -126,3 +137,44 @@ func (p *pinner) IsPinned(key util.Key) bool { p.directPin.HasKey(key) || p.indirPin.HasKey(key) } + +func LoadPinner(d ds.Datastore) (Pinner, error) { + p := new(pinner) + + var err error + p.recursePin, err = set.SetFromDatastore(d, recursePinDatastoreKey) + if err != nil { + return nil, err + } + p.directPin, err = set.SetFromDatastore(d, directPinDatastoreKey) + if err != nil { + return nil, err + } + + p.indirPin, err = loadIndirPin(d, indirectPinDatastoreKey) + if err != nil { + return nil, err + } + + return p, nil +} + +func (p *pinner) Flush() error { + recurse := p.recursePin.GetKeys() + err := p.dstore.Put(recursePinDatastoreKey, recurse) + if err != nil { + return err + } + + direct := p.directPin.GetKeys() + err = p.dstore.Put(directPinDatastoreKey, direct) + if err != nil { + return err + } + + err = p.dstore.Put(indirectPinDatastoreKey, p.indirPin.refCounts) + if err != nil { + return err + } + return nil +} From df4981acbb0c3c61405ab31ee4f5fe9cc791027d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 19 Oct 2014 19:41:49 -0700 Subject: [PATCH 0289/3817] add testing for pins This commit was moved from ipfs/go-ipfs-pinner@73a9d9a9e056599c276dca96fc0dc77ff50a63e3 --- pinning/pinner/pin.go | 6 +- pinning/pinner/pin_test.go | 133 +++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 pinning/pinner/pin_test.go diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index d6f7f0f19..179202008 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -15,6 +15,7 @@ var directPinDatastoreKey = ds.NewKey("/local/pins/direct/keys") var indirectPinDatastoreKey = ds.NewKey("/local/pins/indirect/keys") type Pinner interface { + IsPinned(util.Key) bool Pin(*mdag.Node, bool) error Unpin(util.Key, bool) error Flush() error @@ -138,7 +139,7 @@ func (p *pinner) IsPinned(key util.Key) bool { p.indirPin.HasKey(key) } -func LoadPinner(d ds.Datastore) (Pinner, error) { +func LoadPinner(d ds.Datastore, dserv *mdag.DAGService) (Pinner, error) { p := new(pinner) var err error @@ -156,6 +157,9 @@ func LoadPinner(d ds.Datastore) (Pinner, error) { return nil, err } + p.dserv = dserv + p.dstore = d + return p, nil } diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go new file mode 100644 index 000000000..72c15d90d --- /dev/null +++ b/pinning/pinner/pin_test.go @@ -0,0 +1,133 @@ +package pin + +import ( + "testing" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + bs "github.com/jbenet/go-ipfs/blockservice" + mdag "github.com/jbenet/go-ipfs/merkledag" + "github.com/jbenet/go-ipfs/util" +) + +func randNode() (*mdag.Node, util.Key) { + nd := new(mdag.Node) + nd.Data = make([]byte, 32) + util.NewFastRand().Read(nd.Data) + k, _ := nd.Key() + return nd, k +} + +func TestPinnerBasic(t *testing.T) { + dstore := datastore.NewMapDatastore() + bserv, err := bs.NewBlockService(dstore, nil) + if err != nil { + t.Fatal(err) + } + + dserv := &mdag.DAGService{bserv} + + p := NewPinner(dstore, dserv) + + a, ak := randNode() + + // Pin A{} + err = p.Pin(a, false) + if err != nil { + t.Fatal(err) + } + + if !p.IsPinned(ak) { + t.Fatal("Failed to find key") + } + + b, _ := randNode() + err = b.AddNodeLink("child", a) + if err != nil { + t.Fatal(err) + } + + c, ck := randNode() + err = b.AddNodeLink("otherchild", c) + if err != nil { + t.Fatal(err) + } + + // recursively pin B{A,C} + err = p.Pin(b, true) + if err != nil { + t.Fatal(err) + } + + if !p.IsPinned(ck) { + t.Fatal("Child of recursively pinned node not found") + } + + bk, _ := b.Key() + if !p.IsPinned(bk) { + t.Fatal("Recursively pinned node not found..") + } + + d, _ := randNode() + d.AddNodeLink("a", a) + d.AddNodeLink("c", c) + + e, ek := randNode() + d.AddNodeLink("e", e) + + // Must be in dagserv for unpin to work + err = dserv.AddRecursive(d) + if err != nil { + t.Fatal(err) + } + + // Add D{A,C,E} + err = p.Pin(d, true) + if err != nil { + t.Fatal(err) + } + + if !p.IsPinned(ek) { + t.Fatal(err) + } + + dk, _ := d.Key() + if !p.IsPinned(dk) { + t.Fatal("pinned node not found.") + } + + // Test recursive unpin + err = p.Unpin(dk, true) + if err != nil { + t.Fatal(err) + } + + // c should still be pinned under b + if !p.IsPinned(ck) { + t.Fatal("Recursive unpin fail.") + } + + err = p.Flush() + if err != nil { + t.Fatal(err) + } + + np, err := LoadPinner(dstore, dserv) + if err != nil { + t.Fatal(err) + } + + // Test directly pinned + if !np.IsPinned(ak) { + t.Fatal("Could not find pinned node!") + } + + // Test indirectly pinned + if !np.IsPinned(ck) { + t.Fatal("could not find indirectly pinned node") + } + + // Test recursively pinned + if !np.IsPinned(bk) { + t.Fatal("could not find recursively pinned node") + } +} From 298075bb82b6cc8e1d531975d6c7e540cdd36199 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 20 Oct 2014 12:40:18 -0700 Subject: [PATCH 0290/3817] add lock to pinner and rework cli This commit was moved from ipfs/go-ipfs-pinner@3d5b74c7c946cc02393f201997a436fd44cb4824 --- pinning/pinner/pin.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 179202008..7e73b33f2 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -3,6 +3,8 @@ package pin import ( //ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + "sync" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" nsds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go/namespace" "github.com/jbenet/go-ipfs/blocks/set" @@ -22,6 +24,7 @@ type Pinner interface { } type pinner struct { + lock sync.RWMutex recursePin set.BlockSet directPin set.BlockSet indirPin *indirectPin @@ -49,6 +52,8 @@ func NewPinner(dstore ds.Datastore, serv *mdag.DAGService) Pinner { } func (p *pinner) Pin(node *mdag.Node, recurse bool) error { + p.lock.Lock() + defer p.lock.Unlock() k, err := node.Key() if err != nil { return err @@ -72,6 +77,8 @@ func (p *pinner) Pin(node *mdag.Node, recurse bool) error { } func (p *pinner) Unpin(k util.Key, recurse bool) error { + p.lock.Lock() + defer p.lock.Unlock() if recurse { p.recursePin.RemoveBlock(k) node, err := p.dserv.Get(k) @@ -134,6 +141,8 @@ func (p *pinner) pinLinks(node *mdag.Node) error { } func (p *pinner) IsPinned(key util.Key) bool { + p.lock.RLock() + defer p.lock.RUnlock() return p.recursePin.HasKey(key) || p.directPin.HasKey(key) || p.indirPin.HasKey(key) @@ -164,6 +173,8 @@ func LoadPinner(d ds.Datastore, dserv *mdag.DAGService) (Pinner, error) { } func (p *pinner) Flush() error { + p.lock.RLock() + defer p.lock.RUnlock() recurse := p.recursePin.GetKeys() err := p.dstore.Put(recursePinDatastoreKey, recurse) if err != nil { From fedb2dbe2a03303fd4a03d6d24be5be6198e368d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 21 Oct 2014 23:03:10 -0700 Subject: [PATCH 0291/3817] flush! This commit was moved from ipfs/go-ipfs-pinner@ea997ccefa6500e773990c0f18d8d175c1156ee6 --- pinning/pinner/pin.go | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 7e73b33f2..a63dcff57 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -3,6 +3,8 @@ package pin import ( //ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + "bytes" + "encoding/json" "sync" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" @@ -175,19 +177,41 @@ func LoadPinner(d ds.Datastore, dserv *mdag.DAGService) (Pinner, error) { func (p *pinner) Flush() error { p.lock.RLock() defer p.lock.RUnlock() + buf := new(bytes.Buffer) + enc := json.NewEncoder(buf) + recurse := p.recursePin.GetKeys() - err := p.dstore.Put(recursePinDatastoreKey, recurse) + err := enc.Encode(recurse) if err != nil { return err } + err = p.dstore.Put(recursePinDatastoreKey, buf.Bytes()) + if err != nil { + return err + } + + buf = new(bytes.Buffer) + enc = json.NewEncoder(buf) direct := p.directPin.GetKeys() - err = p.dstore.Put(directPinDatastoreKey, direct) + err = enc.Encode(direct) + if err != nil { + return err + } + + err = p.dstore.Put(directPinDatastoreKey, buf.Bytes()) + if err != nil { + return err + } + + buf = new(bytes.Buffer) + enc = json.NewEncoder(buf) + err = enc.Encode(p.indirPin.refCounts) if err != nil { return err } - err = p.dstore.Put(indirectPinDatastoreKey, p.indirPin.refCounts) + err = p.dstore.Put(indirectPinDatastoreKey, buf.Bytes()) if err != nil { return err } From da5ee04a3bb466c6b70be1f08bba9a3cf1726cbd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 22 Oct 2014 00:33:14 -0700 Subject: [PATCH 0292/3817] update datastore paths This commit was moved from ipfs/go-ipfs-pinner@e49d98cd91cf385185ff3c6fef6cc5650ab92ea0 --- pinning/pinner/indirect.go | 2 +- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index bff1e545f..27b11292c 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -3,7 +3,7 @@ package pin import ( "errors" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" "github.com/jbenet/go-ipfs/blocks/set" "github.com/jbenet/go-ipfs/util" ) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index a63dcff57..1c02fd038 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -7,8 +7,8 @@ import ( "encoding/json" "sync" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" - nsds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go/namespace" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + nsds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" "github.com/jbenet/go-ipfs/blocks/set" mdag "github.com/jbenet/go-ipfs/merkledag" "github.com/jbenet/go-ipfs/util" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 72c15d90d..00c9c6f3c 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -3,7 +3,7 @@ package pin import ( "testing" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" bs "github.com/jbenet/go-ipfs/blockservice" mdag "github.com/jbenet/go-ipfs/merkledag" "github.com/jbenet/go-ipfs/util" From fcc9d3b7b8adc4a8c598224ea72af1f565094a10 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 22 Oct 2014 02:55:22 -0700 Subject: [PATCH 0293/3817] fixed pin json marshal This commit was moved from ipfs/go-ipfs-pinner@f4a287b93c4053cbc687163b62608ab031822a90 --- pinning/pinner/indirect.go | 24 ++++++++----- pinning/pinner/pin.go | 71 ++++++++++++++++++++++---------------- pinning/pinner/pin_test.go | 6 ++-- 3 files changed, 60 insertions(+), 41 deletions(-) diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index 27b11292c..2eb303de2 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -1,8 +1,6 @@ package pin import ( - "errors" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" "github.com/jbenet/go-ipfs/blocks/set" "github.com/jbenet/go-ipfs/util" @@ -21,23 +19,33 @@ func NewIndirectPin(dstore ds.Datastore) *indirectPin { } func loadIndirPin(d ds.Datastore, k ds.Key) (*indirectPin, error) { - irefcnt, err := d.Get(k) + var rcStore map[string]int + err := loadSet(d, k, &rcStore) if err != nil { return nil, err } - refcnt, ok := irefcnt.(map[util.Key]int) - if !ok { - return nil, errors.New("invalid type from datastore") - } + refcnt := make(map[util.Key]int) var keys []util.Key - for k, _ := range refcnt { + for encK, v := range rcStore { + k := util.B58KeyDecode(encK) keys = append(keys, k) + refcnt[k] = v } + log.Debug("indirPin keys: %#v", keys) return &indirectPin{blockset: set.SimpleSetFromKeys(keys), refCounts: refcnt}, nil } +func storeIndirPin(d ds.Datastore, k ds.Key, p *indirectPin) error { + + rcStore := map[string]int{} + for k, v := range p.refCounts { + rcStore[util.B58KeyEncode(k)] = v + } + return storeSet(d, k, rcStore) +} + func (i *indirectPin) Increment(k util.Key) { c := i.refCounts[k] i.refCounts[k] = c + 1 diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 1c02fd038..b9c509a03 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -3,8 +3,9 @@ package pin import ( //ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" - "bytes" + "encoding/json" + "errors" "sync" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" @@ -14,6 +15,7 @@ import ( "github.com/jbenet/go-ipfs/util" ) +var log = util.Logger("pin") var recursePinDatastoreKey = ds.NewKey("/local/pins/recursive/keys") var directPinDatastoreKey = ds.NewKey("/local/pins/direct/keys") var indirectPinDatastoreKey = ds.NewKey("/local/pins/indirect/keys") @@ -89,9 +91,8 @@ func (p *pinner) Unpin(k util.Key, recurse bool) error { } return p.unpinLinks(node) - } else { - p.directPin.RemoveBlock(k) } + p.directPin.RemoveBlock(k) return nil } @@ -153,21 +154,31 @@ func (p *pinner) IsPinned(key util.Key) bool { func LoadPinner(d ds.Datastore, dserv *mdag.DAGService) (Pinner, error) { p := new(pinner) - var err error - p.recursePin, err = set.SetFromDatastore(d, recursePinDatastoreKey) - if err != nil { - return nil, err + { // load recursive set + var recurseKeys []util.Key + if err := loadSet(d, recursePinDatastoreKey, &recurseKeys); err != nil { + return nil, err + } + p.recursePin = set.SimpleSetFromKeys(recurseKeys) } - p.directPin, err = set.SetFromDatastore(d, directPinDatastoreKey) - if err != nil { - return nil, err + + { // load direct set + var directKeys []util.Key + if err := loadSet(d, directPinDatastoreKey, &directKeys); err != nil { + return nil, err + } + p.directPin = set.SimpleSetFromKeys(directKeys) } - p.indirPin, err = loadIndirPin(d, indirectPinDatastoreKey) - if err != nil { - return nil, err + { // load indirect set + var err error + p.indirPin, err = loadIndirPin(d, indirectPinDatastoreKey) + if err != nil { + return nil, err + } } + // assign services p.dserv = dserv p.dstore = d @@ -177,43 +188,43 @@ func LoadPinner(d ds.Datastore, dserv *mdag.DAGService) (Pinner, error) { func (p *pinner) Flush() error { p.lock.RLock() defer p.lock.RUnlock() - buf := new(bytes.Buffer) - enc := json.NewEncoder(buf) - recurse := p.recursePin.GetKeys() - err := enc.Encode(recurse) + err := storeSet(p.dstore, directPinDatastoreKey, p.directPin.GetKeys()) if err != nil { return err } - err = p.dstore.Put(recursePinDatastoreKey, buf.Bytes()) + err = storeSet(p.dstore, recursePinDatastoreKey, p.recursePin.GetKeys()) if err != nil { return err } - buf = new(bytes.Buffer) - enc = json.NewEncoder(buf) - direct := p.directPin.GetKeys() - err = enc.Encode(direct) + err = storeIndirPin(p.dstore, indirectPinDatastoreKey, p.indirPin) if err != nil { return err } + return nil +} - err = p.dstore.Put(directPinDatastoreKey, buf.Bytes()) +// helpers to marshal / unmarshal a pin set +func storeSet(d ds.Datastore, k ds.Key, val interface{}) error { + buf, err := json.Marshal(val) if err != nil { return err } - buf = new(bytes.Buffer) - enc = json.NewEncoder(buf) - err = enc.Encode(p.indirPin.refCounts) + return d.Put(k, buf) +} + +func loadSet(d ds.Datastore, k ds.Key, val interface{}) error { + buf, err := d.Get(k) if err != nil { return err } - err = p.dstore.Put(indirectPinDatastoreKey, buf.Bytes()) - if err != nil { - return err + bf, ok := buf.([]byte) + if !ok { + return errors.New("invalid pin set value in datastore") } - return nil + return json.Unmarshal(bf, val) } diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 00c9c6f3c..10b5862b9 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -3,7 +3,7 @@ package pin import ( "testing" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" bs "github.com/jbenet/go-ipfs/blockservice" mdag "github.com/jbenet/go-ipfs/merkledag" "github.com/jbenet/go-ipfs/util" @@ -18,7 +18,7 @@ func randNode() (*mdag.Node, util.Key) { } func TestPinnerBasic(t *testing.T) { - dstore := datastore.NewMapDatastore() + dstore := ds.NewMapDatastore() bserv, err := bs.NewBlockService(dstore, nil) if err != nil { t.Fatal(err) @@ -103,7 +103,7 @@ func TestPinnerBasic(t *testing.T) { // c should still be pinned under b if !p.IsPinned(ck) { - t.Fatal("Recursive unpin fail.") + t.Fatal("Recursive / indirect unpin fail.") } err = p.Flush() From 38b097ccd73bd14a33b7209c9aefda8fe291a217 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 20 Oct 2014 22:49:13 -0700 Subject: [PATCH 0294/3817] working on debugging dht issues This commit was moved from ipfs/go-ipfs-routing@39421de95036191f1943f120292fe31599091fb4 --- routing/dht/dht.go | 2 +- routing/dht/handlers.go | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index a0f14aa91..5068717e7 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -272,7 +272,7 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, // Perhaps we were given closer peers var peers []peer.Peer for _, pb := range pmes.GetCloserPeers() { - pr, err := dht.addPeer(pb) + pr, err := dht.ensureConnectedToPeer(pb) if err != nil { log.Error("%s", err) continue diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index d3c4d23e3..fce25454c 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -48,10 +48,10 @@ func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *Message) (*Message, error) } // let's first check if we have the value locally. - log.Debug("%s handleGetValue looking into ds\n", dht.self) + log.Debug("%s handleGetValue looking into ds", dht.self) dskey := u.Key(pmes.GetKey()).DsKey() iVal, err := dht.datastore.Get(dskey) - log.Debug("%s handleGetValue looking into ds GOT %v\n", dht.self, iVal) + log.Debug("%s handleGetValue looking into ds GOT %v", dht.self, iVal) // if we got an unexpected error, bail. if err != nil && err != ds.ErrNotFound { @@ -63,7 +63,7 @@ func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *Message) (*Message, error) // if we have the value, send it back if err == nil { - log.Debug("%s handleGetValue success!\n", dht.self) + log.Debug("%s handleGetValue success!", dht.self) byts, ok := iVal.([]byte) if !ok { @@ -85,6 +85,9 @@ func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *Message) (*Message, error) if closer != nil { for _, p := range closer { log.Debug("handleGetValue returning closer peer: '%s'", p) + if len(p.Addresses()) < 1 { + log.Error("no addresses on peer being sent!") + } } resp.CloserPeers = peersToPBPeers(closer) } From 5b7f41e8850f4672a924ed4a2369cdc9feb8d74d Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 21 Oct 2014 01:18:20 -0700 Subject: [PATCH 0295/3817] this shouldn't connect quite yet. This commit was moved from ipfs/go-ipfs-routing@ef8463bda425f468975060e051f520f3877b1f20 --- routing/dht/dht.go | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 5068717e7..1cbb73df4 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -16,7 +16,6 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ) @@ -272,7 +271,7 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, // Perhaps we were given closer peers var peers []peer.Peer for _, pb := range pmes.GetCloserPeers() { - pr, err := dht.ensureConnectedToPeer(pb) + pr, err := dht.peerFromInfo(pb) if err != nil { log.Error("%s", err) continue @@ -289,26 +288,6 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, return nil, nil, u.ErrNotFound } -func (dht *IpfsDHT) addPeer(pb *Message_Peer) (peer.Peer, error) { - if peer.ID(pb.GetId()).Equal(dht.self.ID()) { - return nil, errors.New("cannot add self as peer") - } - - addr, err := ma.NewMultiaddr(pb.GetAddr()) - if err != nil { - return nil, err - } - - // check if we already have this peer. - pr, err := dht.getPeer(peer.ID(pb.GetId())) - if err != nil { - return nil, err - } - pr.AddAddress(addr) // idempotent - - return pr, nil -} - // getValueSingle simply performs the get value RPC with the given parameters func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.Peer, key u.Key, level int) (*Message, error) { @@ -494,7 +473,8 @@ func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (peer.Peer, error) { id := peer.ID(pbp.GetId()) - // continue if it's ourselves + // bail out if it's ourselves + //TODO(jbenet) not sure this should be an error _here_ if id.Equal(dht.self.ID()) { return nil, errors.New("found self") } From 4020b1cb87829d90ad3ae2bb399640a760a13273 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 21 Oct 2014 01:18:38 -0700 Subject: [PATCH 0296/3817] query wasnt ensuring conn The query-- once it's actually attempting to connect to a peer-- should be the one connecting. This commit was moved from ipfs/go-ipfs-routing@e400dd26a4c97aa3b85a93aaca22bb1b20441dcc --- routing/dht/query.go | 41 ++++++++++++++++++++++++++++++++--------- routing/dht/routing.go | 6 +++--- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/routing/dht/query.go b/routing/dht/query.go index ef3670c7d..0019987ca 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -14,10 +14,18 @@ import ( var maxQueryConcurrency = AlphaValue +type dhtDialer interface { + // DialPeer attempts to establish a connection to a given peer + DialPeer(peer.Peer) error +} + type dhtQuery struct { // the key we're querying for key u.Key + // dialer used to ensure we're connected to peers + dialer dhtDialer + // the function to execute per peer qfunc queryFunc @@ -34,9 +42,10 @@ type dhtQueryResult struct { } // constructs query -func newQuery(k u.Key, f queryFunc) *dhtQuery { +func newQuery(k u.Key, d dhtDialer, f queryFunc) *dhtQuery { return &dhtQuery{ key: k, + dialer: d, qfunc: f, concurrency: maxQueryConcurrency, } @@ -211,19 +220,38 @@ func (r *dhtQueryRunner) queryPeer(p peer.Peer) { return } - log.Debug("running worker for: %v\n", p) + // ok let's do this! + log.Debug("running worker for: %v", p) + + // make sure we do this when we exit + defer func() { + // signal we're done proccessing peer p + log.Debug("completing worker for: %v", p) + r.peersRemaining.Decrement(1) + r.rateLimit <- struct{}{} + }() + + // make sure we're connected to the peer. + err := r.query.dialer.DialPeer(p) + if err != nil { + log.Debug("ERROR worker for: %v -- err connecting: %v", p, err) + r.Lock() + r.errs = append(r.errs, err) + r.Unlock() + return + } // finally, run the query against this peer res, err := r.query.qfunc(r.ctx, p) if err != nil { - log.Debug("ERROR worker for: %v %v\n", p, err) + log.Debug("ERROR worker for: %v %v", p, err) r.Lock() r.errs = append(r.errs, err) r.Unlock() } else if res.success { - log.Debug("SUCCESS worker for: %v\n", p, res) + log.Debug("SUCCESS worker for: %v", p, res) r.Lock() r.result = res r.Unlock() @@ -235,9 +263,4 @@ func (r *dhtQueryRunner) queryPeer(p peer.Peer) { r.addPeerToQuery(next, p) } } - - // signal we're done proccessing peer p - log.Debug("completing worker for: %v\n", p) - r.peersRemaining.Decrement(1) - r.rateLimit <- struct{}{} } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 7f004dc47..8c0d69860 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -29,7 +29,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error peers = append(peers, npeers...) } - query := newQuery(key, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(key, dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { log.Debug("%s PutValue qry part %v", dht.self, p) err := dht.putValueToNetwork(ctx, p, string(key), value) if err != nil { @@ -65,7 +65,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { } // setup the Query - query := newQuery(key, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(key, dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { val, peers, err := dht.getValueOrPeers(ctx, p, key, routeLevel) if err != nil { @@ -230,7 +230,7 @@ func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (peer.Peer } // setup query function - query := newQuery(u.Key(id), func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(u.Key(id), dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { pmes, err := dht.findPeerSingle(ctx, p, id, routeLevel) if err != nil { log.Error("%s getPeer error: %v", dht.self, err) From a1149f07bfb552947cf5358050151d168650dd32 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 21 Oct 2014 03:02:31 -0700 Subject: [PATCH 0297/3817] notes This commit was moved from ipfs/go-ipfs-routing@39aad1cc3611227709d640163c7cfe9c044d776b --- routing/dht/handlers.go | 2 +- routing/dht/query.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index fce25454c..2e5117104 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -86,7 +86,7 @@ func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *Message) (*Message, error) for _, p := range closer { log.Debug("handleGetValue returning closer peer: '%s'", p) if len(p.Addresses()) < 1 { - log.Error("no addresses on peer being sent!") + log.Critical("no addresses on peer being sent!") } } resp.CloserPeers = peersToPBPeers(closer) diff --git a/routing/dht/query.go b/routing/dht/query.go index 0019987ca..4096632b6 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -232,6 +232,7 @@ func (r *dhtQueryRunner) queryPeer(p peer.Peer) { }() // make sure we're connected to the peer. + // (Incidentally, this will add it to the peerstore too) err := r.query.dialer.DialPeer(p) if err != nil { log.Debug("ERROR worker for: %v -- err connecting: %v", p, err) From e7a22f5353498a0e03a4ba48e0241448ccc526e6 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 21 Oct 2014 03:13:10 -0700 Subject: [PATCH 0298/3817] Dialer for dht dht doesn't need the whole network interface, only needs a Dialer. (much reduced surface of possible errors) This commit was moved from ipfs/go-ipfs-routing@e0e2ef3b6f24081f65707a5a601405ee7baebf2c --- routing/dht/dht.go | 14 +++++++------- routing/dht/query.go | 10 +++------- routing/dht/routing.go | 6 +++--- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 1cbb73df4..c6079855a 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -33,9 +33,9 @@ type IpfsDHT struct { // NOTE: (currently, only a single table is used) routingTables []*kb.RoutingTable - // the network interface. service - network inet.Network - sender inet.Sender + // the network services we need + dialer inet.Dialer + sender inet.Sender // Local peer (yourself) self peer.Peer @@ -59,9 +59,9 @@ type IpfsDHT struct { } // NewDHT creates a new DHT object with the given peer as the 'local' host -func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, net inet.Network, sender inet.Sender, dstore ds.Datastore) *IpfsDHT { +func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, dialer inet.Dialer, sender inet.Sender, dstore ds.Datastore) *IpfsDHT { dht := new(IpfsDHT) - dht.network = net + dht.dialer = dialer dht.sender = sender dht.datastore = dstore dht.self = p @@ -95,7 +95,7 @@ func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.Peer) (peer.Peer, er // // /ip4/10.20.30.40/tcp/1234/ipfs/Qxhxxchxzcncxnzcnxzcxzm // - err := dht.network.DialPeer(npeer) + err := dht.dialer.DialPeer(npeer) if err != nil { return nil, err } @@ -499,7 +499,7 @@ func (dht *IpfsDHT) ensureConnectedToPeer(pbp *Message_Peer) (peer.Peer, error) } // dial connection - err = dht.network.DialPeer(p) + err = dht.dialer.DialPeer(p) return p, err } diff --git a/routing/dht/query.go b/routing/dht/query.go index 4096632b6..d15e939b7 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -3,6 +3,7 @@ package dht import ( "sync" + inet "github.com/jbenet/go-ipfs/net" peer "github.com/jbenet/go-ipfs/peer" queue "github.com/jbenet/go-ipfs/peer/queue" kb "github.com/jbenet/go-ipfs/routing/kbucket" @@ -14,17 +15,12 @@ import ( var maxQueryConcurrency = AlphaValue -type dhtDialer interface { - // DialPeer attempts to establish a connection to a given peer - DialPeer(peer.Peer) error -} - type dhtQuery struct { // the key we're querying for key u.Key // dialer used to ensure we're connected to peers - dialer dhtDialer + dialer inet.Dialer // the function to execute per peer qfunc queryFunc @@ -42,7 +38,7 @@ type dhtQueryResult struct { } // constructs query -func newQuery(k u.Key, d dhtDialer, f queryFunc) *dhtQuery { +func newQuery(k u.Key, d inet.Dialer, f queryFunc) *dhtQuery { return &dhtQuery{ key: k, dialer: d, diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 8c0d69860..b557aa76c 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -29,7 +29,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error peers = append(peers, npeers...) } - query := newQuery(key, dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(key, dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { log.Debug("%s PutValue qry part %v", dht.self, p) err := dht.putValueToNetwork(ctx, p, string(key), value) if err != nil { @@ -65,7 +65,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { } // setup the Query - query := newQuery(key, dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(key, dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { val, peers, err := dht.getValueOrPeers(ctx, p, key, routeLevel) if err != nil { @@ -230,7 +230,7 @@ func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (peer.Peer } // setup query function - query := newQuery(u.Key(id), dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(u.Key(id), dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { pmes, err := dht.findPeerSingle(ctx, p, id, routeLevel) if err != nil { log.Error("%s getPeer error: %v", dht.self, err) From 0b072311e1f194a3db4fe19004a606501f4be95b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 22 Oct 2014 05:31:49 -0700 Subject: [PATCH 0299/3817] dht test fix (net) This commit was moved from ipfs/go-ipfs-routing@a53d5e1f564b54d0fb410cbcb8e75e11719dfa4d --- routing/dht/dht_test.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 590d5ead3..136be4efe 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -98,8 +98,8 @@ func TestPing(t *testing.T) { defer dhtA.Halt() defer dhtB.Halt() - defer dhtA.network.Close() - defer dhtB.network.Close() + defer dhtA.dialer.(inet.Network).Close() + defer dhtB.dialer.(inet.Network).Close() _, err = dhtA.Connect(ctx, peerB) if err != nil { @@ -142,8 +142,8 @@ func TestValueGetSet(t *testing.T) { defer dhtA.Halt() defer dhtB.Halt() - defer dhtA.network.Close() - defer dhtB.network.Close() + defer dhtA.dialer.(inet.Network).Close() + defer dhtB.dialer.(inet.Network).Close() _, err = dhtA.Connect(ctx, peerB) if err != nil { @@ -184,7 +184,7 @@ func TestProvides(t *testing.T) { defer func() { for i := 0; i < 4; i++ { dhts[i].Halt() - defer dhts[i].network.Close() + defer dhts[i].dialer.(inet.Network).Close() } }() @@ -244,7 +244,7 @@ func TestProvidesAsync(t *testing.T) { defer func() { for i := 0; i < 4; i++ { dhts[i].Halt() - defer dhts[i].network.Close() + defer dhts[i].dialer.(inet.Network).Close() } }() @@ -301,7 +301,7 @@ func TestLayeredGet(t *testing.T) { defer func() { for i := 0; i < 4; i++ { dhts[i].Halt() - defer dhts[i].network.Close() + defer dhts[i].dialer.(inet.Network).Close() } }() @@ -354,7 +354,7 @@ func TestFindPeer(t *testing.T) { defer func() { for i := 0; i < 4; i++ { dhts[i].Halt() - dhts[i].network.Close() + dhts[i].dialer.(inet.Network).Close() } }() @@ -443,8 +443,8 @@ func TestConnectCollision(t *testing.T) { dhtA.Halt() dhtB.Halt() - dhtA.network.Close() - dhtB.network.Close() + dhtA.dialer.(inet.Network).Close() + dhtB.dialer.(inet.Network).Close() <-time.After(200 * time.Millisecond) } From e7745b2724441f0b6d03a2bf4fd8943765809db3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 22 Oct 2014 15:08:32 -0700 Subject: [PATCH 0300/3817] fix for #141, routing table segmentation This commit was moved from ipfs/go-ipfs-routing@f428f6f9f861bfcac86036ef607dbb01a38f18e7 --- routing/kbucket/table.go | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 6f37e94de..5c3e04867 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -65,18 +65,10 @@ func (rt *RoutingTable) Update(p peer.Peer) peer.Peer { // Are we past the max bucket size? if bucket.len() > rt.bucketsize { + // If this bucket is the rightmost bucket, and its full + // we need to split it and create a new bucket if bucketID == len(rt.Buckets)-1 { - newBucket := bucket.Split(bucketID, rt.local) - rt.Buckets = append(rt.Buckets, newBucket) - if newBucket.len() > rt.bucketsize { - // TODO: This is a very rare and annoying case - panic("Case not handled.") - } - - // If all elements were on left side of split... - if bucket.len() > rt.bucketsize { - return bucket.popBack() - } + return rt.nextBucket() } else { // If the bucket cant split kick out least active node return bucket.popBack() @@ -91,6 +83,22 @@ func (rt *RoutingTable) Update(p peer.Peer) peer.Peer { return nil } +func (rt *RoutingTable) nextBucket() peer.Peer { + bucket := rt.Buckets[len(rt.Buckets)-1] + newBucket := bucket.Split(len(rt.Buckets)-1, rt.local) + rt.Buckets = append(rt.Buckets, newBucket) + if newBucket.len() > rt.bucketsize { + // TODO: This is a very rare and annoying case + return rt.nextBucket() + } + + // If all elements were on left side of split... + if bucket.len() > rt.bucketsize { + return bucket.popBack() + } + return nil +} + // A helper struct to sort peers by their distance to the local node type peerDistance struct { p peer.Peer From 3f59ea70e59a1dd6a8b2d799a8b97f597c4253fb Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 22 Oct 2014 04:17:20 -0700 Subject: [PATCH 0301/3817] refactor(namesys) move proto to internal pb package This commit was moved from ipfs/go-namesys@6eefc9e8fe4dfe820c98236dc640ff4ebf529ca8 --- namesys/{ => internal/pb}/entry.pb.go | 0 namesys/{ => internal/pb}/entry.proto | 0 namesys/publisher.go | 3 ++- namesys/routing.go | 3 ++- 4 files changed, 4 insertions(+), 2 deletions(-) rename namesys/{ => internal/pb}/entry.pb.go (100%) rename namesys/{ => internal/pb}/entry.proto (100%) diff --git a/namesys/entry.pb.go b/namesys/internal/pb/entry.pb.go similarity index 100% rename from namesys/entry.pb.go rename to namesys/internal/pb/entry.pb.go diff --git a/namesys/entry.proto b/namesys/internal/pb/entry.proto similarity index 100% rename from namesys/entry.proto rename to namesys/internal/pb/entry.proto diff --git a/namesys/publisher.go b/namesys/publisher.go index 88533f8a0..7203fb1d4 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -9,6 +9,7 @@ import ( mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" ci "github.com/jbenet/go-ipfs/crypto" + pb "github.com/jbenet/go-ipfs/namesys/internal/pb" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" ) @@ -67,7 +68,7 @@ func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { } func createRoutingEntryData(pk ci.PrivKey, val string) ([]byte, error) { - entry := new(IpnsEntry) + entry := new(pb.IpnsEntry) sig, err := pk.Sign([]byte(val)) if err != nil { return nil, err diff --git a/namesys/routing.go b/namesys/routing.go index da1c05d0e..ce1755f69 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -8,6 +8,7 @@ import ( mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" ci "github.com/jbenet/go-ipfs/crypto" + pb "github.com/jbenet/go-ipfs/namesys/internal/pb" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" ) @@ -54,7 +55,7 @@ func (r *routingResolver) Resolve(name string) (string, error) { return "", err } - entry := new(IpnsEntry) + entry := new(pb.IpnsEntry) err = proto.Unmarshal(val, entry) if err != nil { return "", err From ab9e7039ccad6e60d89c81c61632cc3ddca36c77 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 22 Oct 2014 04:57:15 -0700 Subject: [PATCH 0302/3817] refactor(unixfs) move proto to pb package not internal since io needs it fix(fuse/ipns) use pb package fix(fuse) import protos from unixfs/pb package This commit was moved from ipfs/go-unixfs@a12326b80f5bea017cb3f31a8ac320b00a5ce971 --- unixfs/format.go | 33 +++++++++++++++++---------------- unixfs/format_test.go | 5 +++-- unixfs/io/dagmodifier.go | 7 ++++--- unixfs/io/dagreader.go | 17 +++++++++-------- unixfs/{ => pb}/Makefile | 0 unixfs/{ => pb}/data.pb.go | 0 unixfs/{ => pb}/data.proto | 0 7 files changed, 33 insertions(+), 29 deletions(-) rename unixfs/{ => pb}/Makefile (100%) rename unixfs/{ => pb}/data.pb.go (100%) rename unixfs/{ => pb}/data.proto (100%) diff --git a/unixfs/format.go b/unixfs/format.go index 6ba8e3aa4..d73958636 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -5,15 +5,16 @@ package unixfs import ( "errors" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + pb "github.com/jbenet/go-ipfs/unixfs/pb" ) var ErrMalformedFileFormat = errors.New("malformed data in file format") var ErrInvalidDirLocation = errors.New("found directory node in unexpected place") var ErrUnrecognizedType = errors.New("unrecognized node type") -func FromBytes(data []byte) (*PBData, error) { - pbdata := new(PBData) +func FromBytes(data []byte) (*pb.PBData, error) { + pbdata := new(pb.PBData) err := proto.Unmarshal(data, pbdata) if err != nil { return nil, err @@ -22,8 +23,8 @@ func FromBytes(data []byte) (*PBData, error) { } func FilePBData(data []byte, totalsize uint64) []byte { - pbfile := new(PBData) - typ := PBData_File + pbfile := new(pb.PBData) + typ := pb.PBData_File pbfile.Type = &typ pbfile.Data = data pbfile.Filesize = proto.Uint64(totalsize) @@ -42,8 +43,8 @@ func FilePBData(data []byte, totalsize uint64) []byte { // Returns Bytes that represent a Directory func FolderPBData() []byte { - pbfile := new(PBData) - typ := PBData_Directory + pbfile := new(pb.PBData) + typ := pb.PBData_Directory pbfile.Type = &typ data, err := proto.Marshal(pbfile) @@ -55,8 +56,8 @@ func FolderPBData() []byte { } func WrapData(b []byte) []byte { - pbdata := new(PBData) - typ := PBData_Raw + pbdata := new(pb.PBData) + typ := pb.PBData_Raw pbdata.Data = b pbdata.Type = &typ @@ -70,7 +71,7 @@ func WrapData(b []byte) []byte { } func UnwrapData(data []byte) ([]byte, error) { - pbdata := new(PBData) + pbdata := new(pb.PBData) err := proto.Unmarshal(data, pbdata) if err != nil { return nil, err @@ -79,18 +80,18 @@ func UnwrapData(data []byte) ([]byte, error) { } func DataSize(data []byte) (uint64, error) { - pbdata := new(PBData) + pbdata := new(pb.PBData) err := proto.Unmarshal(data, pbdata) if err != nil { return 0, err } switch pbdata.GetType() { - case PBData_Directory: + case pb.PBData_Directory: return 0, errors.New("Cant get data size of directory!") - case PBData_File: + case pb.PBData_File: return pbdata.GetFilesize(), nil - case PBData_Raw: + case pb.PBData_Raw: return uint64(len(pbdata.GetData())), nil default: return 0, errors.New("Unrecognized node data type!") @@ -109,8 +110,8 @@ func (mb *MultiBlock) AddBlockSize(s uint64) { } func (mb *MultiBlock) GetBytes() ([]byte, error) { - pbn := new(PBData) - t := PBData_File + pbn := new(pb.PBData) + t := pb.PBData_File pbn.Type = &t pbn.Filesize = proto.Uint64(uint64(len(mb.Data)) + mb.subtotal) pbn.Blocksizes = mb.blocksizes diff --git a/unixfs/format_test.go b/unixfs/format_test.go index eca926e9f..9dfd12cc0 100644 --- a/unixfs/format_test.go +++ b/unixfs/format_test.go @@ -3,7 +3,8 @@ package unixfs import ( "testing" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + pb "github.com/jbenet/go-ipfs/unixfs/pb" ) func TestMultiBlock(t *testing.T) { @@ -19,7 +20,7 @@ func TestMultiBlock(t *testing.T) { t.Fatal(err) } - pbn := new(PBData) + pbn := new(pb.PBData) err = proto.Unmarshal(b, pbn) if err != nil { t.Fatal(err) diff --git a/unixfs/io/dagmodifier.go b/unixfs/io/dagmodifier.go index 8680da46a..d9af9c00b 100644 --- a/unixfs/io/dagmodifier.go +++ b/unixfs/io/dagmodifier.go @@ -4,11 +4,12 @@ import ( "bytes" "errors" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - "github.com/jbenet/go-ipfs/importer/chunk" + chunk "github.com/jbenet/go-ipfs/importer/chunk" mdag "github.com/jbenet/go-ipfs/merkledag" ft "github.com/jbenet/go-ipfs/unixfs" + ftpb "github.com/jbenet/go-ipfs/unixfs/pb" u "github.com/jbenet/go-ipfs/util" ) @@ -19,7 +20,7 @@ type DagModifier struct { dagserv *mdag.DAGService curNode *mdag.Node - pbdata *ft.PBData + pbdata *ftpb.PBData splitter chunk.BlockSplitter } diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 29196a1e3..0f9b5af43 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -8,6 +8,7 @@ import ( proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" mdag "github.com/jbenet/go-ipfs/merkledag" ft "github.com/jbenet/go-ipfs/unixfs" + ftpb "github.com/jbenet/go-ipfs/unixfs/pb" u "github.com/jbenet/go-ipfs/util" ) @@ -24,23 +25,23 @@ type DagReader struct { // NewDagReader creates a new reader object that reads the data represented by the given // node, using the passed in DAGService for data retreival func NewDagReader(n *mdag.Node, serv *mdag.DAGService) (io.Reader, error) { - pb := new(ft.PBData) + pb := new(ftpb.PBData) err := proto.Unmarshal(n.Data, pb) if err != nil { return nil, err } switch pb.GetType() { - case ft.PBData_Directory: + case ftpb.PBData_Directory: // Dont allow reading directories return nil, ErrIsDir - case ft.PBData_File: + case ftpb.PBData_File: return &DagReader{ node: n, serv: serv, buf: bytes.NewBuffer(pb.GetData()), }, nil - case ft.PBData_Raw: + case ftpb.PBData_Raw: // Raw block will just be a single level, return a byte buffer return bytes.NewBuffer(pb.GetData()), nil default: @@ -63,7 +64,7 @@ func (dr *DagReader) precalcNextBuf() error { } nxt = nxtNode } - pb := new(ft.PBData) + pb := new(ftpb.PBData) err := proto.Unmarshal(nxt.Data, pb) if err != nil { return err @@ -71,13 +72,13 @@ func (dr *DagReader) precalcNextBuf() error { dr.position++ switch pb.GetType() { - case ft.PBData_Directory: + case ftpb.PBData_Directory: return ft.ErrInvalidDirLocation - case ft.PBData_File: + case ftpb.PBData_File: //TODO: this *should* work, needs testing first //return NewDagReader(nxt, dr.serv) panic("Not yet handling different layers of indirection!") - case ft.PBData_Raw: + case ftpb.PBData_Raw: dr.buf = bytes.NewBuffer(pb.GetData()) return nil default: diff --git a/unixfs/Makefile b/unixfs/pb/Makefile similarity index 100% rename from unixfs/Makefile rename to unixfs/pb/Makefile diff --git a/unixfs/data.pb.go b/unixfs/pb/data.pb.go similarity index 100% rename from unixfs/data.pb.go rename to unixfs/pb/data.pb.go diff --git a/unixfs/data.proto b/unixfs/pb/data.proto similarity index 100% rename from unixfs/data.proto rename to unixfs/pb/data.proto From 45ae01d3691da351f298a4a98a0ffc81afd435cd Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 22 Oct 2014 04:15:18 -0700 Subject: [PATCH 0303/3817] refactor(merkledag) move proto to internal pb package https://docs.google.com/document/d/1e8kOo3r51b2BWtTs_1uADIA5djfXhPT36s6eHVRIvaU/edit This commit was moved from ipfs/go-merkledag@05e10f8e710771a1fea75299a2544469db088f8b --- ipld/merkledag/coding.go | 15 ++++++++------- ipld/merkledag/{ => internal/pb}/Makefile | 0 ipld/merkledag/{ => internal/pb}/node.pb.go | 0 ipld/merkledag/{ => internal/pb}/node.proto | 0 ipld/merkledag/{ => internal/pb}/nodepb_test.go | 0 5 files changed, 8 insertions(+), 7 deletions(-) rename ipld/merkledag/{ => internal/pb}/Makefile (100%) rename ipld/merkledag/{ => internal/pb}/node.pb.go (100%) rename ipld/merkledag/{ => internal/pb}/node.proto (100%) rename ipld/merkledag/{ => internal/pb}/nodepb_test.go (100%) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 1d83f32ef..81cc1fc7c 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -3,9 +3,10 @@ package merkledag import ( "fmt" - u "github.com/jbenet/go-ipfs/util" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + + pb "github.com/jbenet/go-ipfs/merkledag/internal/pb" + u "github.com/jbenet/go-ipfs/util" ) // for now, we use a PBNode intermediate thing. @@ -14,7 +15,7 @@ import ( // Unmarshal decodes raw data into a *Node instance. // The conversion uses an intermediate PBNode. func (n *Node) Unmarshal(encoded []byte) error { - var pbn PBNode + var pbn pb.PBNode if err := pbn.Unmarshal(encoded); err != nil { return fmt.Errorf("Unmarshal failed. %v", err) } @@ -55,11 +56,11 @@ func (n *Node) Marshal() ([]byte, error) { return data, nil } -func (n *Node) getPBNode() *PBNode { - pbn := &PBNode{} - pbn.Links = make([]*PBLink, len(n.Links)) +func (n *Node) getPBNode() *pb.PBNode { + pbn := &pb.PBNode{} + pbn.Links = make([]*pb.PBLink, len(n.Links)) for i, l := range n.Links { - pbn.Links[i] = &PBLink{} + pbn.Links[i] = &pb.PBLink{} pbn.Links[i].Name = &l.Name pbn.Links[i].Tsize = &l.Size pbn.Links[i].Hash = []byte(l.Hash) diff --git a/ipld/merkledag/Makefile b/ipld/merkledag/internal/pb/Makefile similarity index 100% rename from ipld/merkledag/Makefile rename to ipld/merkledag/internal/pb/Makefile diff --git a/ipld/merkledag/node.pb.go b/ipld/merkledag/internal/pb/node.pb.go similarity index 100% rename from ipld/merkledag/node.pb.go rename to ipld/merkledag/internal/pb/node.pb.go diff --git a/ipld/merkledag/node.proto b/ipld/merkledag/internal/pb/node.proto similarity index 100% rename from ipld/merkledag/node.proto rename to ipld/merkledag/internal/pb/node.proto diff --git a/ipld/merkledag/nodepb_test.go b/ipld/merkledag/internal/pb/nodepb_test.go similarity index 100% rename from ipld/merkledag/nodepb_test.go rename to ipld/merkledag/internal/pb/nodepb_test.go From 9909d8b2a2d234a60679a443b7ac6ad03860de95 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 22 Oct 2014 05:13:48 -0700 Subject: [PATCH 0304/3817] fix(unixfs/pb) rename proto package -> unixfs_pb This commit was moved from ipfs/go-unixfs@4e5cff061dc2a2d9832fffd024f32a07a760838d --- unixfs/pb/Makefile | 11 ++++++++--- unixfs/pb/{data.pb.go => unixfs.pb.go} | 16 ++++++++-------- unixfs/pb/{data.proto => unixfs.proto} | 2 +- 3 files changed, 17 insertions(+), 12 deletions(-) rename unixfs/pb/{data.pb.go => unixfs.pb.go} (86%) rename unixfs/pb/{data.proto => unixfs.proto} (91%) diff --git a/unixfs/pb/Makefile b/unixfs/pb/Makefile index 87f182fe5..334feee74 100644 --- a/unixfs/pb/Makefile +++ b/unixfs/pb/Makefile @@ -1,5 +1,10 @@ -all: data.pb.go +PB = $(wildcard *.proto) +GO = $(PB:.proto=.pb.go) -data.pb.go: data.proto - protoc --go_out=. data.proto +all: $(GO) +%.pb.go: %.proto + protoc --gogo_out=. --proto_path=../../../../../../:/usr/local/opt/protobuf/include:. $< + +clean: + rm *.pb.go diff --git a/unixfs/pb/data.pb.go b/unixfs/pb/unixfs.pb.go similarity index 86% rename from unixfs/pb/data.pb.go rename to unixfs/pb/unixfs.pb.go index 2efdd8a4c..2ef262bd7 100644 --- a/unixfs/pb/data.pb.go +++ b/unixfs/pb/unixfs.pb.go @@ -1,19 +1,19 @@ -// Code generated by protoc-gen-go. -// source: data.proto +// Code generated by protoc-gen-gogo. +// source: unixfs.proto // DO NOT EDIT! /* -Package unixfs is a generated protocol buffer package. +Package unixfs_pb is a generated protocol buffer package. It is generated from these files: - data.proto + unixfs.proto It has these top-level messages: PBData */ -package unixfs +package unixfs_pb -import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" +import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. @@ -57,7 +57,7 @@ func (x *PBData_DataType) UnmarshalJSON(data []byte) error { } type PBData struct { - Type *PBData_DataType `protobuf:"varint,1,req,enum=unixfs.PBData_DataType" json:"Type,omitempty"` + Type *PBData_DataType `protobuf:"varint,1,req,enum=unixfs.pb.PBData_DataType" json:"Type,omitempty"` Data []byte `protobuf:"bytes,2,opt" json:"Data,omitempty"` Filesize *uint64 `protobuf:"varint,3,opt,name=filesize" json:"filesize,omitempty"` Blocksizes []uint64 `protobuf:"varint,4,rep,name=blocksizes" json:"blocksizes,omitempty"` @@ -97,5 +97,5 @@ func (m *PBData) GetBlocksizes() []uint64 { } func init() { - proto.RegisterEnum("unixfs.PBData_DataType", PBData_DataType_name, PBData_DataType_value) + proto.RegisterEnum("unixfs.pb.PBData_DataType", PBData_DataType_name, PBData_DataType_value) } diff --git a/unixfs/pb/data.proto b/unixfs/pb/unixfs.proto similarity index 91% rename from unixfs/pb/data.proto rename to unixfs/pb/unixfs.proto index b9504b0c3..68afa6681 100644 --- a/unixfs/pb/data.proto +++ b/unixfs/pb/unixfs.proto @@ -1,4 +1,4 @@ -package unixfs; +package unixfs.pb; message PBData { enum DataType { From 3882d9c85f65a1d80e87a440b9f5a3866b90c43b Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 22 Oct 2014 05:10:57 -0700 Subject: [PATCH 0305/3817] fix(merkledag/pb) rename proto package -> merkledag_pb This commit was moved from ipfs/go-merkledag@d45513e0269ced6fbc603402e5d665ec51d783fb --- ipld/merkledag/internal/pb/Makefile | 11 ++- .../pb/{node.pb.go => merkledag.pb.go} | 96 +++++++++---------- .../pb/{node.proto => merkledag.proto} | 2 +- .../{nodepb_test.go => merkledagpb_test.go} | 8 +- 4 files changed, 60 insertions(+), 57 deletions(-) rename ipld/merkledag/internal/pb/{node.pb.go => merkledag.pb.go} (84%) rename ipld/merkledag/internal/pb/{node.proto => merkledag.proto} (97%) rename ipld/merkledag/internal/pb/{nodepb_test.go => merkledagpb_test.go} (99%) diff --git a/ipld/merkledag/internal/pb/Makefile b/ipld/merkledag/internal/pb/Makefile index 711f34bda..08ac883d0 100644 --- a/ipld/merkledag/internal/pb/Makefile +++ b/ipld/merkledag/internal/pb/Makefile @@ -1,8 +1,11 @@ +PB = $(wildcard *.proto) +GO = $(PB:.proto=.pb.go) -all: node.pb.go +all: $(GO) -node.pb.go: node.proto - protoc --gogo_out=. --proto_path=../../../../:/usr/local/opt/protobuf/include:. $< +%.pb.go: %.proto + protoc --gogo_out=. --proto_path=../../../../../../:/usr/local/opt/protobuf/include:. $< clean: - rm node.pb.go + rm -f *.pb.go + rm -f *.go diff --git a/ipld/merkledag/internal/pb/node.pb.go b/ipld/merkledag/internal/pb/merkledag.pb.go similarity index 84% rename from ipld/merkledag/internal/pb/node.pb.go rename to ipld/merkledag/internal/pb/merkledag.pb.go index f7925c9d9..78d5bcb94 100644 --- a/ipld/merkledag/internal/pb/node.pb.go +++ b/ipld/merkledag/internal/pb/merkledag.pb.go @@ -1,18 +1,18 @@ // Code generated by protoc-gen-gogo. -// source: node.proto +// source: merkledag.proto // DO NOT EDIT! /* - Package merkledag is a generated protocol buffer package. + Package merkledag_pb is a generated protocol buffer package. It is generated from these files: - node.proto + merkledag.proto It has these top-level messages: PBLink PBNode */ -package merkledag +package merkledag_pb import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math "math" @@ -300,9 +300,9 @@ func (this *PBLink) String() string { return "nil" } s := strings.Join([]string{`&PBLink{`, - `Hash:` + valueToStringNode(this.Hash) + `,`, - `Name:` + valueToStringNode(this.Name) + `,`, - `Tsize:` + valueToStringNode(this.Tsize) + `,`, + `Hash:` + valueToStringMerkledag(this.Hash) + `,`, + `Name:` + valueToStringMerkledag(this.Name) + `,`, + `Tsize:` + valueToStringMerkledag(this.Tsize) + `,`, `XXX_unrecognized:` + fmt1.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") @@ -314,13 +314,13 @@ func (this *PBNode) String() string { } s := strings.Join([]string{`&PBNode{`, `Links:` + strings.Replace(fmt1.Sprintf("%v", this.Links), "PBLink", "PBLink", 1) + `,`, - `Data:` + valueToStringNode(this.Data) + `,`, + `Data:` + valueToStringMerkledag(this.Data) + `,`, `XXX_unrecognized:` + fmt1.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s } -func valueToStringNode(v interface{}) string { +func valueToStringMerkledag(v interface{}) string { rv := reflect.ValueOf(v) if rv.IsNil() { return "nil" @@ -333,14 +333,14 @@ func (m *PBLink) Size() (n int) { _ = l if m.Hash != nil { l = len(m.Hash) - n += 1 + l + sovNode(uint64(l)) + n += 1 + l + sovMerkledag(uint64(l)) } if m.Name != nil { l = len(*m.Name) - n += 1 + l + sovNode(uint64(l)) + n += 1 + l + sovMerkledag(uint64(l)) } if m.Tsize != nil { - n += 1 + sovNode(uint64(*m.Tsize)) + n += 1 + sovMerkledag(uint64(*m.Tsize)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) @@ -353,12 +353,12 @@ func (m *PBNode) Size() (n int) { if len(m.Links) > 0 { for _, e := range m.Links { l = e.Size() - n += 1 + l + sovNode(uint64(l)) + n += 1 + l + sovMerkledag(uint64(l)) } } if m.Data != nil { l = len(m.Data) - n += 1 + l + sovNode(uint64(l)) + n += 1 + l + sovMerkledag(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) @@ -366,7 +366,7 @@ func (m *PBNode) Size() (n int) { return n } -func sovNode(x uint64) (n int) { +func sovMerkledag(x uint64) (n int) { for { n++ x >>= 7 @@ -376,10 +376,10 @@ func sovNode(x uint64) (n int) { } return n } -func sozNode(x uint64) (n int) { - return sovNode(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +func sozMerkledag(x uint64) (n int) { + return sovMerkledag(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } -func NewPopulatedPBLink(r randyNode, easy bool) *PBLink { +func NewPopulatedPBLink(r randyMerkledag, easy bool) *PBLink { this := &PBLink{} if r.Intn(10) != 0 { v1 := r.Intn(100) @@ -389,7 +389,7 @@ func NewPopulatedPBLink(r randyNode, easy bool) *PBLink { } } if r.Intn(10) != 0 { - v2 := randStringNode(r) + v2 := randStringMerkledag(r) this.Name = &v2 } if r.Intn(10) != 0 { @@ -397,12 +397,12 @@ func NewPopulatedPBLink(r randyNode, easy bool) *PBLink { this.Tsize = &v3 } if !easy && r.Intn(10) != 0 { - this.XXX_unrecognized = randUnrecognizedNode(r, 4) + this.XXX_unrecognized = randUnrecognizedMerkledag(r, 4) } return this } -func NewPopulatedPBNode(r randyNode, easy bool) *PBNode { +func NewPopulatedPBNode(r randyMerkledag, easy bool) *PBNode { this := &PBNode{} if r.Intn(10) != 0 { v4 := r.Intn(10) @@ -419,12 +419,12 @@ func NewPopulatedPBNode(r randyNode, easy bool) *PBNode { } } if !easy && r.Intn(10) != 0 { - this.XXX_unrecognized = randUnrecognizedNode(r, 3) + this.XXX_unrecognized = randUnrecognizedMerkledag(r, 3) } return this } -type randyNode interface { +type randyMerkledag interface { Float32() float32 Float64() float64 Int63() int64 @@ -433,22 +433,22 @@ type randyNode interface { Intn(n int) int } -func randUTF8RuneNode(r randyNode) rune { +func randUTF8RuneMerkledag(r randyMerkledag) rune { res := rune(r.Uint32() % 1112064) if 55296 <= res { res += 2047 } return res } -func randStringNode(r randyNode) string { +func randStringMerkledag(r randyMerkledag) string { v6 := r.Intn(100) tmps := make([]rune, v6) for i := 0; i < v6; i++ { - tmps[i] = randUTF8RuneNode(r) + tmps[i] = randUTF8RuneMerkledag(r) } return string(tmps) } -func randUnrecognizedNode(r randyNode, maxFieldNumber int) (data []byte) { +func randUnrecognizedMerkledag(r randyMerkledag, maxFieldNumber int) (data []byte) { l := r.Intn(5) for i := 0; i < l; i++ { wire := r.Intn(4) @@ -456,37 +456,37 @@ func randUnrecognizedNode(r randyNode, maxFieldNumber int) (data []byte) { wire = 5 } fieldNumber := maxFieldNumber + r.Intn(100) - data = randFieldNode(data, r, fieldNumber, wire) + data = randFieldMerkledag(data, r, fieldNumber, wire) } return data } -func randFieldNode(data []byte, r randyNode, fieldNumber int, wire int) []byte { +func randFieldMerkledag(data []byte, r randyMerkledag, fieldNumber int, wire int) []byte { key := uint32(fieldNumber)<<3 | uint32(wire) switch wire { case 0: - data = encodeVarintPopulateNode(data, uint64(key)) + data = encodeVarintPopulateMerkledag(data, uint64(key)) v7 := r.Int63() if r.Intn(2) == 0 { v7 *= -1 } - data = encodeVarintPopulateNode(data, uint64(v7)) + data = encodeVarintPopulateMerkledag(data, uint64(v7)) case 1: - data = encodeVarintPopulateNode(data, uint64(key)) + data = encodeVarintPopulateMerkledag(data, uint64(key)) data = append(data, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) case 2: - data = encodeVarintPopulateNode(data, uint64(key)) + data = encodeVarintPopulateMerkledag(data, uint64(key)) ll := r.Intn(100) - data = encodeVarintPopulateNode(data, uint64(ll)) + data = encodeVarintPopulateMerkledag(data, uint64(ll)) for j := 0; j < ll; j++ { data = append(data, byte(r.Intn(256))) } default: - data = encodeVarintPopulateNode(data, uint64(key)) + data = encodeVarintPopulateMerkledag(data, uint64(key)) data = append(data, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) } return data } -func encodeVarintPopulateNode(data []byte, v uint64) []byte { +func encodeVarintPopulateMerkledag(data []byte, v uint64) []byte { for v >= 1<<7 { data = append(data, uint8(uint64(v)&0x7f|0x80)) v >>= 7 @@ -512,19 +512,19 @@ func (m *PBLink) MarshalTo(data []byte) (n int, err error) { if m.Hash != nil { data[i] = 0xa i++ - i = encodeVarintNode(data, i, uint64(len(m.Hash))) + i = encodeVarintMerkledag(data, i, uint64(len(m.Hash))) i += copy(data[i:], m.Hash) } if m.Name != nil { data[i] = 0x12 i++ - i = encodeVarintNode(data, i, uint64(len(*m.Name))) + i = encodeVarintMerkledag(data, i, uint64(len(*m.Name))) i += copy(data[i:], *m.Name) } if m.Tsize != nil { data[i] = 0x18 i++ - i = encodeVarintNode(data, i, uint64(*m.Tsize)) + i = encodeVarintMerkledag(data, i, uint64(*m.Tsize)) } if m.XXX_unrecognized != nil { i += copy(data[i:], m.XXX_unrecognized) @@ -550,7 +550,7 @@ func (m *PBNode) MarshalTo(data []byte) (n int, err error) { for _, msg := range m.Links { data[i] = 0x12 i++ - i = encodeVarintNode(data, i, uint64(msg.Size())) + i = encodeVarintMerkledag(data, i, uint64(msg.Size())) n, err := msg.MarshalTo(data[i:]) if err != nil { return 0, err @@ -561,7 +561,7 @@ func (m *PBNode) MarshalTo(data []byte) (n int, err error) { if m.Data != nil { data[i] = 0xa i++ - i = encodeVarintNode(data, i, uint64(len(m.Data))) + i = encodeVarintMerkledag(data, i, uint64(len(m.Data))) i += copy(data[i:], m.Data) } if m.XXX_unrecognized != nil { @@ -569,7 +569,7 @@ func (m *PBNode) MarshalTo(data []byte) (n int, err error) { } return i, nil } -func encodeFixed64Node(data []byte, offset int, v uint64) int { +func encodeFixed64Merkledag(data []byte, offset int, v uint64) int { data[offset] = uint8(v) data[offset+1] = uint8(v >> 8) data[offset+2] = uint8(v >> 16) @@ -580,14 +580,14 @@ func encodeFixed64Node(data []byte, offset int, v uint64) int { data[offset+7] = uint8(v >> 56) return offset + 8 } -func encodeFixed32Node(data []byte, offset int, v uint32) int { +func encodeFixed32Merkledag(data []byte, offset int, v uint32) int { data[offset] = uint8(v) data[offset+1] = uint8(v >> 8) data[offset+2] = uint8(v >> 16) data[offset+3] = uint8(v >> 24) return offset + 4 } -func encodeVarintNode(data []byte, offset int, v uint64) int { +func encodeVarintMerkledag(data []byte, offset int, v uint64) int { for v >= 1<<7 { data[offset] = uint8(v&0x7f | 0x80) v >>= 7 @@ -600,17 +600,17 @@ func (this *PBLink) GoString() string { if this == nil { return "nil" } - s := strings1.Join([]string{`&merkledag.PBLink{` + `Hash:` + valueToGoStringNode(this.Hash, "byte"), `Name:` + valueToGoStringNode(this.Name, "string"), `Tsize:` + valueToGoStringNode(this.Tsize, "uint64"), `XXX_unrecognized:` + fmt2.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") + s := strings1.Join([]string{`&merkledag_pb.PBLink{` + `Hash:` + valueToGoStringMerkledag(this.Hash, "byte"), `Name:` + valueToGoStringMerkledag(this.Name, "string"), `Tsize:` + valueToGoStringMerkledag(this.Tsize, "uint64"), `XXX_unrecognized:` + fmt2.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") return s } func (this *PBNode) GoString() string { if this == nil { return "nil" } - s := strings1.Join([]string{`&merkledag.PBNode{` + `Links:` + fmt2.Sprintf("%#v", this.Links), `Data:` + valueToGoStringNode(this.Data, "byte"), `XXX_unrecognized:` + fmt2.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") + s := strings1.Join([]string{`&merkledag_pb.PBNode{` + `Links:` + fmt2.Sprintf("%#v", this.Links), `Data:` + valueToGoStringMerkledag(this.Data, "byte"), `XXX_unrecognized:` + fmt2.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") return s } -func valueToGoStringNode(v interface{}, typ string) string { +func valueToGoStringMerkledag(v interface{}, typ string) string { rv := reflect1.ValueOf(v) if rv.IsNil() { return "nil" @@ -618,7 +618,7 @@ func valueToGoStringNode(v interface{}, typ string) string { pv := reflect1.Indirect(rv).Interface() return fmt2.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) } -func extensionToGoStringNode(e map[int32]code_google_com_p_gogoprotobuf_proto1.Extension) string { +func extensionToGoStringMerkledag(e map[int32]code_google_com_p_gogoprotobuf_proto1.Extension) string { if e == nil { return "nil" } diff --git a/ipld/merkledag/internal/pb/node.proto b/ipld/merkledag/internal/pb/merkledag.proto similarity index 97% rename from ipld/merkledag/internal/pb/node.proto rename to ipld/merkledag/internal/pb/merkledag.proto index f0f82a425..d0d47f5a3 100644 --- a/ipld/merkledag/internal/pb/node.proto +++ b/ipld/merkledag/internal/pb/merkledag.proto @@ -1,4 +1,4 @@ -package merkledag; +package merkledag.pb; import "code.google.com/p/gogoprotobuf/gogoproto/gogo.proto"; diff --git a/ipld/merkledag/internal/pb/nodepb_test.go b/ipld/merkledag/internal/pb/merkledagpb_test.go similarity index 99% rename from ipld/merkledag/internal/pb/nodepb_test.go rename to ipld/merkledag/internal/pb/merkledagpb_test.go index 103ab986f..4ed02436e 100644 --- a/ipld/merkledag/internal/pb/nodepb_test.go +++ b/ipld/merkledag/internal/pb/merkledagpb_test.go @@ -1,18 +1,18 @@ // Code generated by protoc-gen-gogo. -// source: node.proto +// source: merkledag.proto // DO NOT EDIT! /* -Package merkledag is a generated protocol buffer package. +Package merkledag_pb is a generated protocol buffer package. It is generated from these files: - node.proto + merkledag.proto It has these top-level messages: PBLink PBNode */ -package merkledag +package merkledag_pb import testing "testing" import math_rand "math/rand" From 51d954210ae275d7285df82941fe8460373e2c35 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 22 Oct 2014 05:11:28 -0700 Subject: [PATCH 0306/3817] fix(namesys/pb) rename proto package -> namesys_pb This commit was moved from ipfs/go-namesys@0a93fdf7604fec8bbf49add569c8840002c6f73a --- namesys/internal/pb/Makefile | 10 ++++++++++ namesys/internal/pb/{entry.pb.go => namesys.pb.go} | 12 ++++++------ namesys/internal/pb/{entry.proto => namesys.proto} | 2 +- 3 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 namesys/internal/pb/Makefile rename namesys/internal/pb/{entry.pb.go => namesys.pb.go} (82%) rename namesys/internal/pb/{entry.proto => namesys.proto} (80%) diff --git a/namesys/internal/pb/Makefile b/namesys/internal/pb/Makefile new file mode 100644 index 000000000..334feee74 --- /dev/null +++ b/namesys/internal/pb/Makefile @@ -0,0 +1,10 @@ +PB = $(wildcard *.proto) +GO = $(PB:.proto=.pb.go) + +all: $(GO) + +%.pb.go: %.proto + protoc --gogo_out=. --proto_path=../../../../../../:/usr/local/opt/protobuf/include:. $< + +clean: + rm *.pb.go diff --git a/namesys/internal/pb/entry.pb.go b/namesys/internal/pb/namesys.pb.go similarity index 82% rename from namesys/internal/pb/entry.pb.go rename to namesys/internal/pb/namesys.pb.go index d9dc5160b..b5d8885a2 100644 --- a/namesys/internal/pb/entry.pb.go +++ b/namesys/internal/pb/namesys.pb.go @@ -1,19 +1,19 @@ -// Code generated by protoc-gen-go. -// source: entry.proto +// Code generated by protoc-gen-gogo. +// source: namesys.proto // DO NOT EDIT! /* -Package namesys is a generated protocol buffer package. +Package namesys_pb is a generated protocol buffer package. It is generated from these files: - entry.proto + namesys.proto It has these top-level messages: IpnsEntry */ -package namesys +package namesys_pb -import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" +import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. diff --git a/namesys/internal/pb/entry.proto b/namesys/internal/pb/namesys.proto similarity index 80% rename from namesys/internal/pb/entry.proto rename to namesys/internal/pb/namesys.proto index fee830d7e..ac8a78da3 100644 --- a/namesys/internal/pb/entry.proto +++ b/namesys/internal/pb/namesys.proto @@ -1,4 +1,4 @@ -package namesys; +package namesys.pb; message IpnsEntry { required bytes value = 1; From 3b7306a5d73e12f57ed7f39f3812cba794d3f23f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 22 Oct 2014 21:48:35 -0700 Subject: [PATCH 0307/3817] refactor(unixfs/pb) mv proto PBData -> Data, etc. This commit was moved from ipfs/go-unixfs@11d6f4eff6057271327e50b2b06393c9db0bb9cc --- unixfs/format.go | 30 ++++++++++----------- unixfs/format_test.go | 2 +- unixfs/io/dagmodifier.go | 2 +- unixfs/io/dagreader.go | 16 +++++------ unixfs/pb/unixfs.pb.go | 58 ++++++++++++++++++++-------------------- unixfs/pb/unixfs.proto | 2 +- 6 files changed, 55 insertions(+), 55 deletions(-) diff --git a/unixfs/format.go b/unixfs/format.go index d73958636..0fd29d358 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -13,8 +13,8 @@ var ErrMalformedFileFormat = errors.New("malformed data in file format") var ErrInvalidDirLocation = errors.New("found directory node in unexpected place") var ErrUnrecognizedType = errors.New("unrecognized node type") -func FromBytes(data []byte) (*pb.PBData, error) { - pbdata := new(pb.PBData) +func FromBytes(data []byte) (*pb.Data, error) { + pbdata := new(pb.Data) err := proto.Unmarshal(data, pbdata) if err != nil { return nil, err @@ -23,8 +23,8 @@ func FromBytes(data []byte) (*pb.PBData, error) { } func FilePBData(data []byte, totalsize uint64) []byte { - pbfile := new(pb.PBData) - typ := pb.PBData_File + pbfile := new(pb.Data) + typ := pb.Data_File pbfile.Type = &typ pbfile.Data = data pbfile.Filesize = proto.Uint64(totalsize) @@ -43,8 +43,8 @@ func FilePBData(data []byte, totalsize uint64) []byte { // Returns Bytes that represent a Directory func FolderPBData() []byte { - pbfile := new(pb.PBData) - typ := pb.PBData_Directory + pbfile := new(pb.Data) + typ := pb.Data_Directory pbfile.Type = &typ data, err := proto.Marshal(pbfile) @@ -56,8 +56,8 @@ func FolderPBData() []byte { } func WrapData(b []byte) []byte { - pbdata := new(pb.PBData) - typ := pb.PBData_Raw + pbdata := new(pb.Data) + typ := pb.Data_Raw pbdata.Data = b pbdata.Type = &typ @@ -71,7 +71,7 @@ func WrapData(b []byte) []byte { } func UnwrapData(data []byte) ([]byte, error) { - pbdata := new(pb.PBData) + pbdata := new(pb.Data) err := proto.Unmarshal(data, pbdata) if err != nil { return nil, err @@ -80,18 +80,18 @@ func UnwrapData(data []byte) ([]byte, error) { } func DataSize(data []byte) (uint64, error) { - pbdata := new(pb.PBData) + pbdata := new(pb.Data) err := proto.Unmarshal(data, pbdata) if err != nil { return 0, err } switch pbdata.GetType() { - case pb.PBData_Directory: + case pb.Data_Directory: return 0, errors.New("Cant get data size of directory!") - case pb.PBData_File: + case pb.Data_File: return pbdata.GetFilesize(), nil - case pb.PBData_Raw: + case pb.Data_Raw: return uint64(len(pbdata.GetData())), nil default: return 0, errors.New("Unrecognized node data type!") @@ -110,8 +110,8 @@ func (mb *MultiBlock) AddBlockSize(s uint64) { } func (mb *MultiBlock) GetBytes() ([]byte, error) { - pbn := new(pb.PBData) - t := pb.PBData_File + pbn := new(pb.Data) + t := pb.Data_File pbn.Type = &t pbn.Filesize = proto.Uint64(uint64(len(mb.Data)) + mb.subtotal) pbn.Blocksizes = mb.blocksizes diff --git a/unixfs/format_test.go b/unixfs/format_test.go index 9dfd12cc0..5065c7bc5 100644 --- a/unixfs/format_test.go +++ b/unixfs/format_test.go @@ -20,7 +20,7 @@ func TestMultiBlock(t *testing.T) { t.Fatal(err) } - pbn := new(pb.PBData) + pbn := new(pb.Data) err = proto.Unmarshal(b, pbn) if err != nil { t.Fatal(err) diff --git a/unixfs/io/dagmodifier.go b/unixfs/io/dagmodifier.go index d9af9c00b..ec8f7fbeb 100644 --- a/unixfs/io/dagmodifier.go +++ b/unixfs/io/dagmodifier.go @@ -20,7 +20,7 @@ type DagModifier struct { dagserv *mdag.DAGService curNode *mdag.Node - pbdata *ftpb.PBData + pbdata *ftpb.Data splitter chunk.BlockSplitter } diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 0f9b5af43..846916103 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -25,23 +25,23 @@ type DagReader struct { // NewDagReader creates a new reader object that reads the data represented by the given // node, using the passed in DAGService for data retreival func NewDagReader(n *mdag.Node, serv *mdag.DAGService) (io.Reader, error) { - pb := new(ftpb.PBData) + pb := new(ftpb.Data) err := proto.Unmarshal(n.Data, pb) if err != nil { return nil, err } switch pb.GetType() { - case ftpb.PBData_Directory: + case ftpb.Data_Directory: // Dont allow reading directories return nil, ErrIsDir - case ftpb.PBData_File: + case ftpb.Data_File: return &DagReader{ node: n, serv: serv, buf: bytes.NewBuffer(pb.GetData()), }, nil - case ftpb.PBData_Raw: + case ftpb.Data_Raw: // Raw block will just be a single level, return a byte buffer return bytes.NewBuffer(pb.GetData()), nil default: @@ -64,7 +64,7 @@ func (dr *DagReader) precalcNextBuf() error { } nxt = nxtNode } - pb := new(ftpb.PBData) + pb := new(ftpb.Data) err := proto.Unmarshal(nxt.Data, pb) if err != nil { return err @@ -72,13 +72,13 @@ func (dr *DagReader) precalcNextBuf() error { dr.position++ switch pb.GetType() { - case ftpb.PBData_Directory: + case ftpb.Data_Directory: return ft.ErrInvalidDirLocation - case ftpb.PBData_File: + case ftpb.Data_File: //TODO: this *should* work, needs testing first //return NewDagReader(nxt, dr.serv) panic("Not yet handling different layers of indirection!") - case ftpb.PBData_Raw: + case ftpb.Data_Raw: dr.buf = bytes.NewBuffer(pb.GetData()) return nil default: diff --git a/unixfs/pb/unixfs.pb.go b/unixfs/pb/unixfs.pb.go index 2ef262bd7..999aa6d92 100644 --- a/unixfs/pb/unixfs.pb.go +++ b/unixfs/pb/unixfs.pb.go @@ -9,7 +9,7 @@ It is generated from these files: unixfs.proto It has these top-level messages: - PBData + Data */ package unixfs_pb @@ -20,76 +20,76 @@ import math "math" var _ = proto.Marshal var _ = math.Inf -type PBData_DataType int32 +type Data_DataType int32 const ( - PBData_Raw PBData_DataType = 0 - PBData_Directory PBData_DataType = 1 - PBData_File PBData_DataType = 2 + Data_Raw Data_DataType = 0 + Data_Directory Data_DataType = 1 + Data_File Data_DataType = 2 ) -var PBData_DataType_name = map[int32]string{ +var Data_DataType_name = map[int32]string{ 0: "Raw", 1: "Directory", 2: "File", } -var PBData_DataType_value = map[string]int32{ +var Data_DataType_value = map[string]int32{ "Raw": 0, "Directory": 1, "File": 2, } -func (x PBData_DataType) Enum() *PBData_DataType { - p := new(PBData_DataType) +func (x Data_DataType) Enum() *Data_DataType { + p := new(Data_DataType) *p = x return p } -func (x PBData_DataType) String() string { - return proto.EnumName(PBData_DataType_name, int32(x)) +func (x Data_DataType) String() string { + return proto.EnumName(Data_DataType_name, int32(x)) } -func (x *PBData_DataType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(PBData_DataType_value, data, "PBData_DataType") +func (x *Data_DataType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Data_DataType_value, data, "Data_DataType") if err != nil { return err } - *x = PBData_DataType(value) + *x = Data_DataType(value) return nil } -type PBData struct { - Type *PBData_DataType `protobuf:"varint,1,req,enum=unixfs.pb.PBData_DataType" json:"Type,omitempty"` - Data []byte `protobuf:"bytes,2,opt" json:"Data,omitempty"` - Filesize *uint64 `protobuf:"varint,3,opt,name=filesize" json:"filesize,omitempty"` - Blocksizes []uint64 `protobuf:"varint,4,rep,name=blocksizes" json:"blocksizes,omitempty"` - XXX_unrecognized []byte `json:"-"` +type Data struct { + Type *Data_DataType `protobuf:"varint,1,req,enum=unixfs.pb.Data_DataType" json:"Type,omitempty"` + Data []byte `protobuf:"bytes,2,opt" json:"Data,omitempty"` + Filesize *uint64 `protobuf:"varint,3,opt,name=filesize" json:"filesize,omitempty"` + Blocksizes []uint64 `protobuf:"varint,4,rep,name=blocksizes" json:"blocksizes,omitempty"` + XXX_unrecognized []byte `json:"-"` } -func (m *PBData) Reset() { *m = PBData{} } -func (m *PBData) String() string { return proto.CompactTextString(m) } -func (*PBData) ProtoMessage() {} +func (m *Data) Reset() { *m = Data{} } +func (m *Data) String() string { return proto.CompactTextString(m) } +func (*Data) ProtoMessage() {} -func (m *PBData) GetType() PBData_DataType { +func (m *Data) GetType() Data_DataType { if m != nil && m.Type != nil { return *m.Type } - return PBData_Raw + return Data_Raw } -func (m *PBData) GetData() []byte { +func (m *Data) GetData() []byte { if m != nil { return m.Data } return nil } -func (m *PBData) GetFilesize() uint64 { +func (m *Data) GetFilesize() uint64 { if m != nil && m.Filesize != nil { return *m.Filesize } return 0 } -func (m *PBData) GetBlocksizes() []uint64 { +func (m *Data) GetBlocksizes() []uint64 { if m != nil { return m.Blocksizes } @@ -97,5 +97,5 @@ func (m *PBData) GetBlocksizes() []uint64 { } func init() { - proto.RegisterEnum("unixfs.pb.PBData_DataType", PBData_DataType_name, PBData_DataType_value) + proto.RegisterEnum("unixfs.pb.Data_DataType", Data_DataType_name, Data_DataType_value) } diff --git a/unixfs/pb/unixfs.proto b/unixfs/pb/unixfs.proto index 68afa6681..618fb6159 100644 --- a/unixfs/pb/unixfs.proto +++ b/unixfs/pb/unixfs.proto @@ -1,6 +1,6 @@ package unixfs.pb; -message PBData { +message Data { enum DataType { Raw = 0; Directory = 1; From 653cc13a50c27ff7acae787cd13c49d7f2ac88e4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Oct 2014 18:32:28 -0700 Subject: [PATCH 0308/3817] rewrite findpeer and other dht tweaks This commit was moved from ipfs/go-ipfs-routing@c36e5be20338fea00afa018706a3ccf8e7980e9f --- routing/dht/dht.go | 7 --- routing/dht/dht_test.go | 8 ++- routing/dht/routing.go | 130 +++++++++++++++++---------------------- routing/kbucket/table.go | 1 - 4 files changed, 63 insertions(+), 83 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index c6079855a..118c7bb2e 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -384,18 +384,11 @@ func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.Peer, id peer.ID, return dht.sendRequest(ctx, p, pmes) } -func (dht *IpfsDHT) printTables() { - for _, route := range dht.routingTables { - route.Print() - } -} - func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.Peer, key u.Key, level int) (*Message, error) { pmes := newMessage(Message_GET_PROVIDERS, string(key), level) return dht.sendRequest(ctx, p, pmes) } -// TODO: Could be done async func (dht *IpfsDHT) addProviders(key u.Key, peers []*Message_Peer) []peer.Peer { var provArr []peer.Peer for _, prov := range peers { diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 136be4efe..507db4eec 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -283,7 +283,13 @@ func TestProvidesAsync(t *testing.T) { ctxT, _ := context.WithTimeout(ctx, time.Millisecond*300) provs := dhts[0].FindProvidersAsync(ctxT, u.Key("hello"), 5) select { - case p := <-provs: + case p, ok := <-provs: + if !ok { + t.Fatal("Provider channel was closed...") + } + if p == nil { + t.Fatal("Got back nil provider!") + } if !p.ID().Equal(dhts[3].self.ID()) { t.Fatalf("got a provider, but not the right one. %s", p) } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index b557aa76c..9ca521af7 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -61,7 +61,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { closest := dht.routingTables[routeLevel].NearestPeers(kb.ConvertKey(key), PoolSize) if closest == nil || len(closest) == 0 { log.Warning("Got no peers back from routing table!") - return nil, nil + return nil, kb.ErrLookupFailure } // setup the Query @@ -152,22 +152,32 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int return peerOut } -//TODO: this function could also be done asynchronously func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*Message_Peer, ps *peerSet, count int, out chan peer.Peer) { + done := make(chan struct{}) for _, pbp := range peers { + go func(mp *Message_Peer) { + defer func() { done <- struct{}{} }() + // construct new peer + p, err := dht.ensureConnectedToPeer(mp) + if err != nil { + log.Error("%s", err) + return + } + if p == nil { + log.Error("Got nil peer from ensureConnectedToPeer") + return + } - // construct new peer - p, err := dht.ensureConnectedToPeer(pbp) - if err != nil { - continue - } - - dht.providers.AddProvider(k, p) - if ps.AddIfSmallerThan(p, count) { - out <- p - } else if ps.Size() >= count { - return - } + dht.providers.AddProvider(k, p) + if ps.AddIfSmallerThan(p, count) { + out <- p + } else if ps.Size() >= count { + return + } + }(pbp) + } + for _ = range peers { + <-done } } @@ -182,92 +192,64 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) } routeLevel := 0 - p = dht.routingTables[routeLevel].NearestPeer(kb.ConvertPeerID(id)) - if p == nil { - return nil, nil - } - if p.ID().Equal(id) { - return p, nil + closest := dht.routingTables[routeLevel].NearestPeers(kb.ConvertPeerID(id), AlphaValue) + if closest == nil || len(closest) == 0 { + return nil, kb.ErrLookupFailure } - for routeLevel < len(dht.routingTables) { - pmes, err := dht.findPeerSingle(ctx, p, id, routeLevel) - plist := pmes.GetCloserPeers() - if plist == nil || len(plist) == 0 { - routeLevel++ - continue + // Sanity... + for _, p := range closest { + if p.ID().Equal(id) { + log.Error("Found target peer in list of closest peers...") + return p, nil } - found := plist[0] - - nxtPeer, err := dht.ensureConnectedToPeer(found) - if err != nil { - routeLevel++ - continue - } - - if nxtPeer.ID().Equal(id) { - return nxtPeer, nil - } - - p = nxtPeer - } - return nil, u.ErrNotFound -} - -func (dht *IpfsDHT) findPeerMultiple(ctx context.Context, id peer.ID) (peer.Peer, error) { - - // Check if were already connected to them - p, _ := dht.FindLocal(id) - if p != nil { - return p, nil - } - - // get the peers we need to announce to - routeLevel := 0 - peers := dht.routingTables[routeLevel].NearestPeers(kb.ConvertPeerID(id), AlphaValue) - if len(peers) == 0 { - return nil, nil } - // setup query function + // setup the Query query := newQuery(u.Key(id), dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + pmes, err := dht.findPeerSingle(ctx, p, id, routeLevel) if err != nil { - log.Error("%s getPeer error: %v", dht.self, err) return nil, err } - plist := pmes.GetCloserPeers() - if len(plist) == 0 { - routeLevel++ - } - - nxtprs := make([]peer.Peer, len(plist)) - for i, fp := range plist { - nxtp, err := dht.peerFromInfo(fp) + closer := pmes.GetCloserPeers() + var clpeers []peer.Peer + for _, pbp := range closer { + np, err := dht.getPeer(peer.ID(pbp.GetId())) if err != nil { - log.Error("%s findPeer error: %v", dht.self, err) + log.Warning("Received invalid peer from query") continue } - - if nxtp.ID().Equal(id) { - return &dhtQueryResult{peer: nxtp, success: true}, nil + ma, err := pbp.Address() + if err != nil { + log.Warning("Received peer with bad or missing address.") + continue } - - nxtprs[i] = nxtp + np.AddAddress(ma) + if pbp.GetId() == string(id) { + return &dhtQueryResult{ + peer: np, + success: true, + }, nil + } + clpeers = append(clpeers, np) } - return &dhtQueryResult{closerPeers: nxtprs}, nil + return &dhtQueryResult{closerPeers: clpeers}, nil }) - result, err := query.Run(ctx, peers) + // run it! + result, err := query.Run(ctx, closest) if err != nil { return nil, err } + log.Debug("FindPeer %v %v", id, result.success) if result.peer == nil { return nil, u.ErrNotFound } + return result.peer, nil } diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 5c3e04867..7b9d88fda 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -88,7 +88,6 @@ func (rt *RoutingTable) nextBucket() peer.Peer { newBucket := bucket.Split(len(rt.Buckets)-1, rt.local) rt.Buckets = append(rt.Buckets, newBucket) if newBucket.len() > rt.bucketsize { - // TODO: This is a very rare and annoying case return rt.nextBucket() } From f6f7c74ba00f94b2cae63b6f3e4e9b0064c40fc4 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 25 Oct 2014 02:50:13 -0700 Subject: [PATCH 0309/3817] use my go-logging fork until https://github.com/op/go-logging/pull/30 is merged This commit was moved from ipfs/go-unixfs@8e3a21011ec65d94b26af0586ff06f07c61127b1 --- unixfs/io/dagmodifier_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index 5e9edb727..22dceaf4c 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -6,7 +6,6 @@ import ( "io/ioutil" "testing" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/op/go-logging" bs "github.com/jbenet/go-ipfs/blockservice" "github.com/jbenet/go-ipfs/importer/chunk" mdag "github.com/jbenet/go-ipfs/merkledag" @@ -14,6 +13,7 @@ import ( u "github.com/jbenet/go-ipfs/util" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging" ) func getMockDagServ(t *testing.T) *mdag.DAGService { From 1b7e718ae4ba269fc28c9f4f75d1eb711aa3b839 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 25 Oct 2014 03:17:14 -0700 Subject: [PATCH 0310/3817] go-vet friendly codebase - distinguish log.Error and log.Errorf functions - Initialize structs with field names - A bit of unreachable code (defers) This commit was moved from ipfs/go-ipfs-routing@62fae319223b323c93df5d29dafb80eeb81d1510 --- routing/dht/dht.go | 34 +++++++++++++++++----------------- routing/dht/dht_logger.go | 4 ++-- routing/dht/handlers.go | 32 ++++++++++++++++---------------- routing/dht/routing.go | 16 ++++++++-------- routing/kbucket/table.go | 4 ++-- 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 118c7bb2e..60032f389 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -84,7 +84,7 @@ func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, dialer inet.Dia // Connect to a new peer at the given address, ping and add to the routing table func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.Peer) (peer.Peer, error) { - log.Debug("Connect to new peer: %s", npeer) + log.Debugf("Connect to new peer: %s", npeer) // TODO(jbenet,whyrusleeping) // @@ -139,7 +139,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N dht.Update(mPeer) // Print out diagnostic - log.Debug("[peer: %s] Got message type: '%s' [from = %s]\n", + log.Debugf("%s got message type: '%s' from %s", dht.self, Message_MessageType_name[int32(pmes.GetType())], mPeer) // get handler for this msg type. @@ -152,7 +152,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N // dispatch handler. rpmes, err := handler(mPeer, pmes) if err != nil { - log.Error("handle message error: %s", err) + log.Errorf("handle message error: %s", err) return nil } @@ -165,7 +165,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N // serialize response msg rmes, err := msg.FromObject(mPeer, rpmes) if err != nil { - log.Error("serialze response error: %s", err) + log.Errorf("serialze response error: %s", err) return nil } @@ -184,7 +184,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *Message) start := time.Now() // Print out diagnostic - log.Debug("Sent message type: '%s' [to = %s]", + log.Debugf("Sent message type: '%s' to %s", Message_MessageType_name[int32(pmes.GetType())], p) rmes, err := dht.sender.SendRequest(ctx, mes) @@ -235,7 +235,7 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.Peer, key string) er return err } - log.Debug("%s putProvider: %s for %s", dht.self, p, key) + log.Debugf("%s putProvider: %s for %s", dht.self, p, key) if rpmes.GetKey() != pmes.GetKey() { return errors.New("provider not added correctly") } @@ -251,7 +251,7 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, return nil, nil, err } - log.Debug("pmes.GetValue() %v", pmes.GetValue()) + log.Debugf("pmes.GetValue() %v", pmes.GetValue()) if value := pmes.GetValue(); value != nil { // Success! We were given the value log.Debug("getValueOrPeers: got value") @@ -273,7 +273,7 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, for _, pb := range pmes.GetCloserPeers() { pr, err := dht.peerFromInfo(pb) if err != nil { - log.Error("%s", err) + log.Error(err) continue } peers = append(peers, pr) @@ -306,13 +306,13 @@ func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key, for _, pinfo := range peerlist { p, err := dht.ensureConnectedToPeer(pinfo) if err != nil { - log.Error("getFromPeers error: %s", err) + log.Errorf("getFromPeers error: %s", err) continue } pmes, err := dht.getValueSingle(ctx, p, key, level) if err != nil { - log.Error("getFromPeers error: %s\n", err) + log.Errorf("getFromPeers error: %s\n", err) continue } @@ -349,7 +349,7 @@ func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { // Update signals to all routingTables to Update their last-seen status // on the given peer. func (dht *IpfsDHT) Update(p peer.Peer) { - log.Debug("updating peer: %s latency = %f\n", p, p.GetLatency().Seconds()) + log.Debugf("updating peer: %s latency = %f\n", p, p.GetLatency().Seconds()) removedCount := 0 for _, route := range dht.routingTables { removed := route.Update(p) @@ -394,11 +394,11 @@ func (dht *IpfsDHT) addProviders(key u.Key, peers []*Message_Peer) []peer.Peer { for _, prov := range peers { p, err := dht.peerFromInfo(prov) if err != nil { - log.Error("error getting peer from info: %v", err) + log.Errorf("error getting peer from info: %v", err) continue } - log.Debug("%s adding provider: %s for %s", dht.self, p, key) + log.Debugf("%s adding provider: %s for %s", dht.self, p, key) // Dont add outselves to the list if p.ID().Equal(dht.self.ID()) { @@ -456,7 +456,7 @@ func (dht *IpfsDHT) getPeer(id peer.ID) (peer.Peer, error) { p, err := dht.peerstore.Get(id) if err != nil { err = fmt.Errorf("Failed to get peer from peerstore: %s", err) - log.Error("%s", err) + log.Error(err) return nil, err } return p, nil @@ -505,7 +505,7 @@ func (dht *IpfsDHT) loadProvidableKeys() error { for _, dsk := range kl { k := u.KeyFromDsKey(dsk) if len(k) == 0 { - log.Error("loadProvidableKeys error: %v", dsk) + log.Errorf("loadProvidableKeys error: %v", dsk) } dht.providers.AddProvider(k, dht.self) @@ -526,7 +526,7 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { ctx, _ := context.WithTimeout(dht.ctx, time.Second*5) err := dht.Ping(ctx, p) if err != nil { - log.Error("Ping error: %s", err) + log.Errorf("Ping error: %s", err) } } case <-dht.ctx.Done(): @@ -541,6 +541,6 @@ func (dht *IpfsDHT) Bootstrap(ctx context.Context) { rand.Read(id) _, err := dht.FindPeer(ctx, peer.ID(id)) if err != nil { - log.Error("Bootstrap peer error: %s", err) + log.Errorf("Bootstrap peer error: %s", err) } } diff --git a/routing/dht/dht_logger.go b/routing/dht/dht_logger.go index 0ff012956..eea47ec1a 100644 --- a/routing/dht/dht_logger.go +++ b/routing/dht/dht_logger.go @@ -30,14 +30,14 @@ func (l *logDhtRPC) EndLog() { func (l *logDhtRPC) Print() { b, err := json.Marshal(l) if err != nil { - log.Debug("Error marshaling logDhtRPC object: %s", err) + log.Debugf("Error marshaling logDhtRPC object: %s", err) } else { log.Debug(string(b)) } } func (l *logDhtRPC) String() string { - return fmt.Sprintf("DHT RPC: %s took %s, success = %s", l.Type, l.Duration, l.Success) + return fmt.Sprintf("DHT RPC: %s took %s, success = %v", l.Type, l.Duration, l.Success) } func (l *logDhtRPC) EndAndPrint() { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 2e5117104..8aeb4251b 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -36,7 +36,7 @@ func (dht *IpfsDHT) handlerForMsgType(t Message_MessageType) dhtHandler { } func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *Message) (*Message, error) { - log.Debug("%s handleGetValue for key: %s\n", dht.self, pmes.GetKey()) + log.Debugf("%s handleGetValue for key: %s\n", dht.self, pmes.GetKey()) // setup response resp := newMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) @@ -48,10 +48,10 @@ func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *Message) (*Message, error) } // let's first check if we have the value locally. - log.Debug("%s handleGetValue looking into ds", dht.self) + log.Debugf("%s handleGetValue looking into ds", dht.self) dskey := u.Key(pmes.GetKey()).DsKey() iVal, err := dht.datastore.Get(dskey) - log.Debug("%s handleGetValue looking into ds GOT %v", dht.self, iVal) + log.Debugf("%s handleGetValue looking into ds GOT %v", dht.self, iVal) // if we got an unexpected error, bail. if err != nil && err != ds.ErrNotFound { @@ -63,7 +63,7 @@ func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *Message) (*Message, error) // if we have the value, send it back if err == nil { - log.Debug("%s handleGetValue success!", dht.self) + log.Debugf("%s handleGetValue success!", dht.self) byts, ok := iVal.([]byte) if !ok { @@ -76,7 +76,7 @@ func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *Message) (*Message, error) // if we know any providers for the requested value, return those. provs := dht.providers.GetProviders(u.Key(pmes.GetKey())) if len(provs) > 0 { - log.Debug("handleGetValue returning %d provider[s]\n", len(provs)) + log.Debugf("handleGetValue returning %d provider[s]", len(provs)) resp.ProviderPeers = peersToPBPeers(provs) } @@ -84,7 +84,7 @@ func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *Message) (*Message, error) closer := dht.betterPeersToQuery(pmes, CloserPeerCount) if closer != nil { for _, p := range closer { - log.Debug("handleGetValue returning closer peer: '%s'", p) + log.Debugf("handleGetValue returning closer peer: '%s'", p) if len(p.Addresses()) < 1 { log.Critical("no addresses on peer being sent!") } @@ -101,12 +101,12 @@ func (dht *IpfsDHT) handlePutValue(p peer.Peer, pmes *Message) (*Message, error) defer dht.dslock.Unlock() dskey := u.Key(pmes.GetKey()).DsKey() err := dht.datastore.Put(dskey, pmes.GetValue()) - log.Debug("%s handlePutValue %v %v\n", dht.self, dskey, pmes.GetValue()) + log.Debugf("%s handlePutValue %v %v\n", dht.self, dskey, pmes.GetValue()) return pmes, err } func (dht *IpfsDHT) handlePing(p peer.Peer, pmes *Message) (*Message, error) { - log.Debug("%s Responding to ping from %s!\n", dht.self, p) + log.Debugf("%s Responding to ping from %s!\n", dht.self, p) return pmes, nil } @@ -122,7 +122,7 @@ func (dht *IpfsDHT) handleFindPeer(p peer.Peer, pmes *Message) (*Message, error) } if closest == nil { - log.Error("handleFindPeer: could not find anything.") + log.Errorf("handleFindPeer: could not find anything.") return resp, nil } @@ -134,7 +134,7 @@ func (dht *IpfsDHT) handleFindPeer(p peer.Peer, pmes *Message) (*Message, error) } for _, p := range withAddresses { - log.Debug("handleFindPeer: sending back '%s'", p) + log.Debugf("handleFindPeer: sending back '%s'", p) } resp.CloserPeers = peersToPBPeers(withAddresses) return resp, nil @@ -144,11 +144,11 @@ func (dht *IpfsDHT) handleGetProviders(p peer.Peer, pmes *Message) (*Message, er resp := newMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) // check if we have this value, to add ourselves as provider. - log.Debug("handling GetProviders: '%s'", pmes.GetKey()) + log.Debugf("handling GetProviders: '%s'", pmes.GetKey()) dsk := u.Key(pmes.GetKey()).DsKey() has, err := dht.datastore.Has(dsk) if err != nil && err != ds.ErrNotFound { - log.Error("unexpected datastore error: %v\n", err) + log.Errorf("unexpected datastore error: %v\n", err) has = false } @@ -180,7 +180,7 @@ type providerInfo struct { func (dht *IpfsDHT) handleAddProvider(p peer.Peer, pmes *Message) (*Message, error) { key := u.Key(pmes.GetKey()) - log.Debug("%s adding %s as a provider for '%s'\n", dht.self, p, peer.ID(key)) + log.Debugf("%s adding %s as a provider for '%s'\n", dht.self, p, peer.ID(key)) // add provider should use the address given in the message for _, pb := range pmes.GetProviderPeers() { @@ -189,16 +189,16 @@ func (dht *IpfsDHT) handleAddProvider(p peer.Peer, pmes *Message) (*Message, err addr, err := pb.Address() if err != nil { - log.Error("provider %s error with address %s", p, *pb.Addr) + log.Errorf("provider %s error with address %s", p, *pb.Addr) continue } - log.Info("received provider %s %s for %s", p, addr, key) + log.Infof("received provider %s %s for %s", p, addr, key) p.AddAddress(addr) dht.providers.AddProvider(key, p) } else { - log.Error("handleAddProvider received provider %s from %s", pid, p) + log.Errorf("handleAddProvider received provider %s from %s", pid, p) } } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 9ca521af7..26d17cbc4 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -17,7 +17,7 @@ import ( // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error { - log.Debug("PutValue %s", key) + log.Debugf("PutValue %s", key) err := dht.putLocal(key, value) if err != nil { return err @@ -30,7 +30,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error } query := newQuery(key, dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { - log.Debug("%s PutValue qry part %v", dht.self, p) + log.Debugf("%s PutValue qry part %v", dht.self, p) err := dht.putValueToNetwork(ctx, p, string(key), value) if err != nil { return nil, err @@ -46,7 +46,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error // If the search does not succeed, a multiaddr string of a closer peer is // returned along with util.ErrSearchIncomplete func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { - log.Debug("Get Value [%s]", key) + log.Debugf("Get Value [%s]", key) // If we have it local, dont bother doing an RPC! // NOTE: this might not be what we want to do... @@ -86,7 +86,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { return nil, err } - log.Debug("GetValue %v %v", key, result.value) + log.Debugf("GetValue %v %v", key, result.value) if result.value == nil { return nil, u.ErrNotFound } @@ -140,7 +140,7 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int defer wg.Done() pmes, err := dht.findProvidersSingle(ctx, p, key, 0) if err != nil { - log.Error("%s", err) + log.Error(err) return } dht.addPeerListAsync(key, pmes.GetProviderPeers(), ps, count, peerOut) @@ -218,7 +218,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) for _, pbp := range closer { np, err := dht.getPeer(peer.ID(pbp.GetId())) if err != nil { - log.Warning("Received invalid peer from query") + log.Warningf("Received invalid peer from query: %v", err) continue } ma, err := pbp.Address() @@ -256,10 +256,10 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) // Ping a peer, log the time it took func (dht *IpfsDHT) Ping(ctx context.Context, p peer.Peer) error { // Thoughts: maybe this should accept an ID and do a peer lookup? - log.Info("ping %s start", p) + log.Infof("ping %s start", p) pmes := newMessage(Message_PING, "", 0) _, err := dht.sendRequest(ctx, p, pmes) - log.Info("ping %s end (err = %s)", p, err) + log.Infof("ping %s end (err = %s)", p, err) return err } diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 7b9d88fda..491a06c68 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -125,7 +125,7 @@ func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) pe } peerArr = append(peerArr, &pd) if e == nil { - log.Debug("list element was nil.\n") + log.Debug("list element was nil") return peerArr } } @@ -148,7 +148,7 @@ func (rt *RoutingTable) NearestPeer(id ID) peer.Peer { return peers[0] } - log.Error("NearestPeer: Returning nil, table size = %d", rt.Size()) + log.Errorf("NearestPeer: Returning nil, table size = %d", rt.Size()) return nil } From 03623919545081e7346f721c7adaad679ca18235 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 25 Oct 2014 03:17:14 -0700 Subject: [PATCH 0311/3817] go-vet friendly codebase - distinguish log.Error and log.Errorf functions - Initialize structs with field names - A bit of unreachable code (defers) This commit was moved from ipfs/go-unixfs@f7a6bca9147c5e03067271bb13534c7517120d24 --- unixfs/io/dagmodifier.go | 6 +++--- unixfs/io/dagmodifier_test.go | 12 ++++++------ unixfs/io/dagwriter_test.go | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/unixfs/io/dagmodifier.go b/unixfs/io/dagmodifier.go index ec8f7fbeb..2d5fb77d9 100644 --- a/unixfs/io/dagmodifier.go +++ b/unixfs/io/dagmodifier.go @@ -59,7 +59,7 @@ func (dm *DagModifier) WriteAt(b []byte, offset uint64) (int, error) { origlen := len(b) if end <= zeroblocklen { - log.Debug("Writing into zero block.") + log.Debug("Writing into zero block") // Replacing zeroeth data block (embedded in the root node) //TODO: check chunking here copy(dm.pbdata.Data[offset:], b) @@ -76,7 +76,7 @@ func (dm *DagModifier) WriteAt(b []byte, offset uint64) (int, error) { traversed = uint64(zeroblocklen) for i, size := range dm.pbdata.Blocksizes { if uint64(offset) < traversed+size { - log.Debug("Starting mod at block %d. [%d < %d + %d]", i, offset, traversed, size) + log.Debugf("Starting mod at block %d. [%d < %d + %d]", i, offset, traversed, size) // Here is where we start startsubblk = i lnk := dm.curNode.Links[i] @@ -145,7 +145,7 @@ func (dm *DagModifier) WriteAt(b []byte, offset uint64) (int, error) { n := &mdag.Node{Data: ft.WrapData(sb)} _, err := dm.dagserv.Add(n) if err != nil { - log.Error("Failed adding node to DAG service: %s", err) + log.Errorf("Failed adding node to DAG service: %s", err) return 0, err } lnk, err := mdag.MakeLink(n) diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index 22dceaf4c..3686ff859 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -22,11 +22,11 @@ func getMockDagServ(t *testing.T) *mdag.DAGService { if err != nil { t.Fatal(err) } - return &mdag.DAGService{bserv} + return &mdag.DAGService{Blocks: bserv} } func getNode(t *testing.T, dserv *mdag.DAGService, size int64) ([]byte, *mdag.Node) { - dw := NewDagWriter(dserv, &chunk.SizeSplitter{500}) + dw := NewDagWriter(dserv, &chunk.SizeSplitter{Size: 500}) n, err := io.CopyN(dw, u.NewFastRand(), size) if err != nil { @@ -99,7 +99,7 @@ func TestDagModifierBasic(t *testing.T) { dserv := getMockDagServ(t) b, n := getNode(t, dserv, 50000) - dagmod, err := NewDagModifier(n, dserv, &chunk.SizeSplitter{512}) + dagmod, err := NewDagModifier(n, dserv, &chunk.SizeSplitter{Size: 512}) if err != nil { t.Fatal(err) } @@ -142,7 +142,7 @@ func TestDagModifierBasic(t *testing.T) { expected := uint64(50000 + 3500 + 3000) if size != expected { - t.Fatal("Final reported size is incorrect [%d != %d]", size, expected) + t.Fatalf("Final reported size is incorrect [%d != %d]", size, expected) } } @@ -150,7 +150,7 @@ func TestMultiWrite(t *testing.T) { dserv := getMockDagServ(t) _, n := getNode(t, dserv, 0) - dagmod, err := NewDagModifier(n, dserv, &chunk.SizeSplitter{512}) + dagmod, err := NewDagModifier(n, dserv, &chunk.SizeSplitter{Size: 512}) if err != nil { t.Fatal(err) } @@ -191,7 +191,7 @@ func TestMultiWriteCoal(t *testing.T) { dserv := getMockDagServ(t) _, n := getNode(t, dserv, 0) - dagmod, err := NewDagModifier(n, dserv, &chunk.SizeSplitter{512}) + dagmod, err := NewDagModifier(n, dserv, &chunk.SizeSplitter{Size: 512}) if err != nil { t.Fatal(err) } diff --git a/unixfs/io/dagwriter_test.go b/unixfs/io/dagwriter_test.go index ddf5f9d66..d0b8f45d1 100644 --- a/unixfs/io/dagwriter_test.go +++ b/unixfs/io/dagwriter_test.go @@ -53,8 +53,8 @@ func TestDagWriter(t *testing.T) { if err != nil { t.Fatal(err) } - dag := &mdag.DAGService{bserv} - dw := NewDagWriter(dag, &chunk.SizeSplitter{4096}) + dag := &mdag.DAGService{Blocks: bserv} + dw := NewDagWriter(dag, &chunk.SizeSplitter{Size: 4096}) nbytes := int64(1024 * 1024 * 2) n, err := io.CopyN(dw, &datasource{}, nbytes) @@ -87,8 +87,8 @@ func TestMassiveWrite(t *testing.T) { if err != nil { t.Fatal(err) } - dag := &mdag.DAGService{bserv} - dw := NewDagWriter(dag, &chunk.SizeSplitter{4096}) + dag := &mdag.DAGService{Blocks: bserv} + dw := NewDagWriter(dag, &chunk.SizeSplitter{Size: 4096}) nbytes := int64(1024 * 1024 * 1024 * 16) n, err := io.CopyN(dw, &datasource{}, nbytes) @@ -107,13 +107,13 @@ func BenchmarkDagWriter(b *testing.B) { if err != nil { b.Fatal(err) } - dag := &mdag.DAGService{bserv} + dag := &mdag.DAGService{Blocks: bserv} b.ResetTimer() nbytes := int64(100000) for i := 0; i < b.N; i++ { b.SetBytes(nbytes) - dw := NewDagWriter(dag, &chunk.SizeSplitter{4096}) + dw := NewDagWriter(dag, &chunk.SizeSplitter{Size: 4096}) n, err := io.CopyN(dw, &datasource{}, nbytes) if err != nil { b.Fatal(err) From c18235b6702f9edb9bbbfb505bde2a1bd4dc4e56 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 25 Oct 2014 03:17:14 -0700 Subject: [PATCH 0312/3817] go-vet friendly codebase - distinguish log.Error and log.Errorf functions - Initialize structs with field names - A bit of unreachable code (defers) This commit was moved from ipfs/go-ipfs-pinner@8b6cef69a144c4a4be6def7ea76ccdfdea211197 --- pinning/pinner/pin_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 10b5862b9..8f6f6c343 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -24,7 +24,7 @@ func TestPinnerBasic(t *testing.T) { t.Fatal(err) } - dserv := &mdag.DAGService{bserv} + dserv := &mdag.DAGService{Blocks: bserv} p := NewPinner(dstore, dserv) From e210af5519b77d8d13574a929420590ddfb67584 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 25 Oct 2014 03:17:14 -0700 Subject: [PATCH 0313/3817] go-vet friendly codebase - distinguish log.Error and log.Errorf functions - Initialize structs with field names - A bit of unreachable code (defers) This commit was moved from ipfs/go-ipfs-chunker@a753a87fbed09f7bffff4271ae34e91e738a1ae8 --- chunker/splitting.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 0b5717eaf..f5a6f735c 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -8,7 +8,7 @@ import ( var log = util.Logger("chunk") -var DefaultSplitter = &SizeSplitter{1024 * 512} +var DefaultSplitter = &SizeSplitter{Size: 1024 * 512} type BlockSplitter interface { Split(r io.Reader) chan []byte @@ -32,7 +32,7 @@ func (ss *SizeSplitter) Split(r io.Reader) chan []byte { } return } - log.Error("Block split error: %s", err) + log.Errorf("Block split error: %s", err) return } if nread < ss.Size { From 2048a40597dde5602ef81f1af7b8b6f224eab956 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 25 Oct 2014 04:13:28 -0700 Subject: [PATCH 0314/3817] refactor(dht/pb) move proto to pb package This commit was moved from ipfs/go-ipfs-routing@05ac1e87cd24870cac6c6027488785ab8f17d9ce --- routing/dht/Makefile | 11 ----- routing/dht/dht.go | 41 +++++++++--------- routing/dht/ext_test.go | 27 ++++++------ routing/dht/handlers.go | 45 ++++++++++---------- routing/dht/pb/Makefile | 11 +++++ routing/dht/{messages.pb.go => pb/dht.pb.go} | 16 +++---- routing/dht/{messages.proto => pb/dht.proto} | 2 +- routing/dht/{Message.go => pb/message.go} | 9 ++-- routing/dht/routing.go | 7 +-- 9 files changed, 86 insertions(+), 83 deletions(-) delete mode 100644 routing/dht/Makefile create mode 100644 routing/dht/pb/Makefile rename routing/dht/{messages.pb.go => pb/dht.pb.go} (92%) rename routing/dht/{messages.proto => pb/dht.proto} (98%) rename routing/dht/{Message.go => pb/message.go} (87%) diff --git a/routing/dht/Makefile b/routing/dht/Makefile deleted file mode 100644 index 563234b1d..000000000 --- a/routing/dht/Makefile +++ /dev/null @@ -1,11 +0,0 @@ - -PB = $(wildcard *.proto) -GO = $(PB:.proto=.pb.go) - -all: $(GO) - -%.pb.go: %.proto - protoc --gogo_out=. --proto_path=../../../../:/usr/local/opt/protobuf/include:. $< - -clean: - rm *.pb.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 60032f389..52ae1f76c 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -11,6 +11,7 @@ import ( inet "github.com/jbenet/go-ipfs/net" msg "github.com/jbenet/go-ipfs/net/message" peer "github.com/jbenet/go-ipfs/peer" + pb "github.com/jbenet/go-ipfs/routing/dht/pb" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" @@ -128,7 +129,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N } // deserialize msg - pmes := new(Message) + pmes := new(pb.Message) err := proto.Unmarshal(mData, pmes) if err != nil { log.Error("Error unmarshaling data") @@ -140,7 +141,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N // Print out diagnostic log.Debugf("%s got message type: '%s' from %s", - dht.self, Message_MessageType_name[int32(pmes.GetType())], mPeer) + dht.self, pb.Message_MessageType_name[int32(pmes.GetType())], mPeer) // get handler for this msg type. handler := dht.handlerForMsgType(pmes.GetType()) @@ -174,7 +175,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N // sendRequest sends out a request using dht.sender, but also makes sure to // measure the RTT for latency measurements. -func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *Message) (*Message, error) { +func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { mes, err := msg.FromObject(p, pmes) if err != nil { @@ -185,7 +186,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *Message) // Print out diagnostic log.Debugf("Sent message type: '%s' to %s", - Message_MessageType_name[int32(pmes.GetType())], p) + pb.Message_MessageType_name[int32(pmes.GetType())], p) rmes, err := dht.sender.SendRequest(ctx, mes) if err != nil { @@ -198,7 +199,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *Message) rtt := time.Since(start) rmes.Peer().SetLatency(rtt) - rpmes := new(Message) + rpmes := new(pb.Message) if err := proto.Unmarshal(rmes.Data(), rpmes); err != nil { return nil, err } @@ -210,7 +211,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *Message) func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p peer.Peer, key string, value []byte) error { - pmes := newMessage(Message_PUT_VALUE, string(key), 0) + pmes := pb.NewMessage(pb.Message_PUT_VALUE, string(key), 0) pmes.Value = value rpmes, err := dht.sendRequest(ctx, p, pmes) if err != nil { @@ -225,10 +226,10 @@ func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p peer.Peer, func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.Peer, key string) error { - pmes := newMessage(Message_ADD_PROVIDER, string(key), 0) + pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, string(key), 0) // add self as the provider - pmes.ProviderPeers = peersToPBPeers([]peer.Peer{dht.self}) + pmes.ProviderPeers = pb.PeersToPBPeers([]peer.Peer{dht.self}) rpmes, err := dht.sendRequest(ctx, p, pmes) if err != nil { @@ -290,9 +291,9 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, // getValueSingle simply performs the get value RPC with the given parameters func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.Peer, - key u.Key, level int) (*Message, error) { + key u.Key, level int) (*pb.Message, error) { - pmes := newMessage(Message_GET_VALUE, string(key), level) + pmes := pb.NewMessage(pb.Message_GET_VALUE, string(key), level) return dht.sendRequest(ctx, p, pmes) } @@ -301,7 +302,7 @@ func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.Peer, // one to get the value from? Or just connect to one at a time until we get a // successful connection and request the value from it? func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key, - peerlist []*Message_Peer, level int) ([]byte, error) { + peerlist []*pb.Message_Peer, level int) ([]byte, error) { for _, pinfo := range peerlist { p, err := dht.ensureConnectedToPeer(pinfo) @@ -379,17 +380,17 @@ func (dht *IpfsDHT) FindLocal(id peer.ID) (peer.Peer, *kb.RoutingTable) { return nil, nil } -func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.Peer, id peer.ID, level int) (*Message, error) { - pmes := newMessage(Message_FIND_NODE, string(id), level) +func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.Peer, id peer.ID, level int) (*pb.Message, error) { + pmes := pb.NewMessage(pb.Message_FIND_NODE, string(id), level) return dht.sendRequest(ctx, p, pmes) } -func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.Peer, key u.Key, level int) (*Message, error) { - pmes := newMessage(Message_GET_PROVIDERS, string(key), level) +func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.Peer, key u.Key, level int) (*pb.Message, error) { + pmes := pb.NewMessage(pb.Message_GET_PROVIDERS, string(key), level) return dht.sendRequest(ctx, p, pmes) } -func (dht *IpfsDHT) addProviders(key u.Key, peers []*Message_Peer) []peer.Peer { +func (dht *IpfsDHT) addProviders(key u.Key, peers []*pb.Message_Peer) []peer.Peer { var provArr []peer.Peer for _, prov := range peers { p, err := dht.peerFromInfo(prov) @@ -413,7 +414,7 @@ func (dht *IpfsDHT) addProviders(key u.Key, peers []*Message_Peer) []peer.Peer { } // nearestPeersToQuery returns the routing tables closest peers. -func (dht *IpfsDHT) nearestPeersToQuery(pmes *Message, count int) []peer.Peer { +func (dht *IpfsDHT) nearestPeersToQuery(pmes *pb.Message, count int) []peer.Peer { level := pmes.GetClusterLevel() cluster := dht.routingTables[level] @@ -423,7 +424,7 @@ func (dht *IpfsDHT) nearestPeersToQuery(pmes *Message, count int) []peer.Peer { } // betterPeerToQuery returns nearestPeersToQuery, but iff closer than self. -func (dht *IpfsDHT) betterPeersToQuery(pmes *Message, count int) []peer.Peer { +func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, count int) []peer.Peer { closer := dht.nearestPeersToQuery(pmes, count) // no node? nil @@ -462,7 +463,7 @@ func (dht *IpfsDHT) getPeer(id peer.ID) (peer.Peer, error) { return p, nil } -func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (peer.Peer, error) { +func (dht *IpfsDHT) peerFromInfo(pbp *pb.Message_Peer) (peer.Peer, error) { id := peer.ID(pbp.GetId()) @@ -485,7 +486,7 @@ func (dht *IpfsDHT) peerFromInfo(pbp *Message_Peer) (peer.Peer, error) { return p, nil } -func (dht *IpfsDHT) ensureConnectedToPeer(pbp *Message_Peer) (peer.Peer, error) { +func (dht *IpfsDHT) ensureConnectedToPeer(pbp *pb.Message_Peer) (peer.Peer, error) { p, err := dht.peerFromInfo(pbp) if err != nil { return nil, err diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 43bd34f8a..be6f17d96 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -12,6 +12,7 @@ import ( msg "github.com/jbenet/go-ipfs/net/message" mux "github.com/jbenet/go-ipfs/net/mux" peer "github.com/jbenet/go-ipfs/peer" + pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" "time" @@ -127,13 +128,13 @@ func TestGetFailures(t *testing.T) { // u.POut("NotFound Test\n") // Reply with failures to every message fs.AddHandler(func(mes msg.NetMessage) msg.NetMessage { - pmes := new(Message) + pmes := new(pb.Message) err := proto.Unmarshal(mes.Data(), pmes) if err != nil { t.Fatal(err) } - resp := &Message{ + resp := &pb.Message{ Type: pmes.Type, } m, err := msg.FromObject(mes.Peer(), resp) @@ -153,9 +154,9 @@ func TestGetFailures(t *testing.T) { fs.handlers = nil // Now we test this DHT's handleGetValue failure - typ := Message_GET_VALUE + typ := pb.Message_GET_VALUE str := "hello" - req := Message{ + req := pb.Message{ Type: &typ, Key: &str, Value: []byte{0}, @@ -169,7 +170,7 @@ func TestGetFailures(t *testing.T) { mes = d.HandleMessage(ctx, mes) - pmes := new(Message) + pmes := new(pb.Message) err = proto.Unmarshal(mes.Data(), pmes) if err != nil { t.Fatal(err) @@ -215,21 +216,21 @@ func TestNotFound(t *testing.T) { // Reply with random peers to every message fs.AddHandler(func(mes msg.NetMessage) msg.NetMessage { - pmes := new(Message) + pmes := new(pb.Message) err := proto.Unmarshal(mes.Data(), pmes) if err != nil { t.Fatal(err) } switch pmes.GetType() { - case Message_GET_VALUE: - resp := &Message{Type: pmes.Type} + case pb.Message_GET_VALUE: + resp := &pb.Message{Type: pmes.Type} peers := []peer.Peer{} for i := 0; i < 7; i++ { peers = append(peers, _randPeer()) } - resp.CloserPeers = peersToPBPeers(peers) + resp.CloserPeers = pb.PeersToPBPeers(peers) mes, err := msg.FromObject(mes.Peer(), resp) if err != nil { t.Error(err) @@ -282,17 +283,17 @@ func TestLessThanKResponses(t *testing.T) { // Reply with random peers to every message fs.AddHandler(func(mes msg.NetMessage) msg.NetMessage { - pmes := new(Message) + pmes := new(pb.Message) err := proto.Unmarshal(mes.Data(), pmes) if err != nil { t.Fatal(err) } switch pmes.GetType() { - case Message_GET_VALUE: - resp := &Message{ + case pb.Message_GET_VALUE: + resp := &pb.Message{ Type: pmes.Type, - CloserPeers: peersToPBPeers([]peer.Peer{other}), + CloserPeers: pb.PeersToPBPeers([]peer.Peer{other}), } mes, err := msg.FromObject(mes.Peer(), resp) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 8aeb4251b..35355b32f 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -6,6 +6,7 @@ import ( "time" peer "github.com/jbenet/go-ipfs/peer" + pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" @@ -14,32 +15,32 @@ import ( var CloserPeerCount = 4 // dhthandler specifies the signature of functions that handle DHT messages. -type dhtHandler func(peer.Peer, *Message) (*Message, error) +type dhtHandler func(peer.Peer, *pb.Message) (*pb.Message, error) -func (dht *IpfsDHT) handlerForMsgType(t Message_MessageType) dhtHandler { +func (dht *IpfsDHT) handlerForMsgType(t pb.Message_MessageType) dhtHandler { switch t { - case Message_GET_VALUE: + case pb.Message_GET_VALUE: return dht.handleGetValue - case Message_PUT_VALUE: + case pb.Message_PUT_VALUE: return dht.handlePutValue - case Message_FIND_NODE: + case pb.Message_FIND_NODE: return dht.handleFindPeer - case Message_ADD_PROVIDER: + case pb.Message_ADD_PROVIDER: return dht.handleAddProvider - case Message_GET_PROVIDERS: + case pb.Message_GET_PROVIDERS: return dht.handleGetProviders - case Message_PING: + case pb.Message_PING: return dht.handlePing default: return nil } } -func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *Message) (*Message, error) { +func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *pb.Message) (*pb.Message, error) { log.Debugf("%s handleGetValue for key: %s\n", dht.self, pmes.GetKey()) // setup response - resp := newMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) + resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) // first, is the key even a key? key := pmes.GetKey() @@ -77,7 +78,7 @@ func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *Message) (*Message, error) provs := dht.providers.GetProviders(u.Key(pmes.GetKey())) if len(provs) > 0 { log.Debugf("handleGetValue returning %d provider[s]", len(provs)) - resp.ProviderPeers = peersToPBPeers(provs) + resp.ProviderPeers = pb.PeersToPBPeers(provs) } // Find closest peer on given cluster to desired key and reply with that info @@ -89,14 +90,14 @@ func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *Message) (*Message, error) log.Critical("no addresses on peer being sent!") } } - resp.CloserPeers = peersToPBPeers(closer) + resp.CloserPeers = pb.PeersToPBPeers(closer) } return resp, nil } // Store a value in this peer local storage -func (dht *IpfsDHT) handlePutValue(p peer.Peer, pmes *Message) (*Message, error) { +func (dht *IpfsDHT) handlePutValue(p peer.Peer, pmes *pb.Message) (*pb.Message, error) { dht.dslock.Lock() defer dht.dslock.Unlock() dskey := u.Key(pmes.GetKey()).DsKey() @@ -105,13 +106,13 @@ func (dht *IpfsDHT) handlePutValue(p peer.Peer, pmes *Message) (*Message, error) return pmes, err } -func (dht *IpfsDHT) handlePing(p peer.Peer, pmes *Message) (*Message, error) { +func (dht *IpfsDHT) handlePing(p peer.Peer, pmes *pb.Message) (*pb.Message, error) { log.Debugf("%s Responding to ping from %s!\n", dht.self, p) return pmes, nil } -func (dht *IpfsDHT) handleFindPeer(p peer.Peer, pmes *Message) (*Message, error) { - resp := newMessage(pmes.GetType(), "", pmes.GetClusterLevel()) +func (dht *IpfsDHT) handleFindPeer(p peer.Peer, pmes *pb.Message) (*pb.Message, error) { + resp := pb.NewMessage(pmes.GetType(), "", pmes.GetClusterLevel()) var closest []peer.Peer // if looking for self... special case where we send it on CloserPeers. @@ -136,12 +137,12 @@ func (dht *IpfsDHT) handleFindPeer(p peer.Peer, pmes *Message) (*Message, error) for _, p := range withAddresses { log.Debugf("handleFindPeer: sending back '%s'", p) } - resp.CloserPeers = peersToPBPeers(withAddresses) + resp.CloserPeers = pb.PeersToPBPeers(withAddresses) return resp, nil } -func (dht *IpfsDHT) handleGetProviders(p peer.Peer, pmes *Message) (*Message, error) { - resp := newMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) +func (dht *IpfsDHT) handleGetProviders(p peer.Peer, pmes *pb.Message) (*pb.Message, error) { + resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) // check if we have this value, to add ourselves as provider. log.Debugf("handling GetProviders: '%s'", pmes.GetKey()) @@ -160,13 +161,13 @@ func (dht *IpfsDHT) handleGetProviders(p peer.Peer, pmes *Message) (*Message, er // if we've got providers, send thos those. if providers != nil && len(providers) > 0 { - resp.ProviderPeers = peersToPBPeers(providers) + resp.ProviderPeers = pb.PeersToPBPeers(providers) } // Also send closer peers. closer := dht.betterPeersToQuery(pmes, CloserPeerCount) if closer != nil { - resp.CloserPeers = peersToPBPeers(closer) + resp.CloserPeers = pb.PeersToPBPeers(closer) } return resp, nil @@ -177,7 +178,7 @@ type providerInfo struct { Value peer.Peer } -func (dht *IpfsDHT) handleAddProvider(p peer.Peer, pmes *Message) (*Message, error) { +func (dht *IpfsDHT) handleAddProvider(p peer.Peer, pmes *pb.Message) (*pb.Message, error) { key := u.Key(pmes.GetKey()) log.Debugf("%s adding %s as a provider for '%s'\n", dht.self, p, peer.ID(key)) diff --git a/routing/dht/pb/Makefile b/routing/dht/pb/Makefile new file mode 100644 index 000000000..08ac883d0 --- /dev/null +++ b/routing/dht/pb/Makefile @@ -0,0 +1,11 @@ +PB = $(wildcard *.proto) +GO = $(PB:.proto=.pb.go) + +all: $(GO) + +%.pb.go: %.proto + protoc --gogo_out=. --proto_path=../../../../../../:/usr/local/opt/protobuf/include:. $< + +clean: + rm -f *.pb.go + rm -f *.go diff --git a/routing/dht/messages.pb.go b/routing/dht/pb/dht.pb.go similarity index 92% rename from routing/dht/messages.pb.go rename to routing/dht/pb/dht.pb.go index 2da77e7bc..6c488c51a 100644 --- a/routing/dht/messages.pb.go +++ b/routing/dht/pb/dht.pb.go @@ -1,19 +1,19 @@ -// Code generated by protoc-gen-go. -// source: messages.proto +// Code generated by protoc-gen-gogo. +// source: dht.proto // DO NOT EDIT! /* -Package dht is a generated protocol buffer package. +Package dht_pb is a generated protocol buffer package. It is generated from these files: - messages.proto + dht.proto It has these top-level messages: Message */ -package dht +package dht_pb -import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" +import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. @@ -67,7 +67,7 @@ func (x *Message_MessageType) UnmarshalJSON(data []byte) error { type Message struct { // defines what type of message it is. - Type *Message_MessageType `protobuf:"varint,1,opt,name=type,enum=dht.Message_MessageType" json:"type,omitempty"` + Type *Message_MessageType `protobuf:"varint,1,opt,name=type,enum=dht.pb.Message_MessageType" json:"type,omitempty"` // defines what coral cluster level this query/response belongs to. ClusterLevelRaw *int32 `protobuf:"varint,10,opt,name=clusterLevelRaw" json:"clusterLevelRaw,omitempty"` // Used to specify the key associated with this message. @@ -156,5 +156,5 @@ func (m *Message_Peer) GetAddr() string { } func init() { - proto.RegisterEnum("dht.Message_MessageType", Message_MessageType_name, Message_MessageType_value) + proto.RegisterEnum("dht.pb.Message_MessageType", Message_MessageType_name, Message_MessageType_value) } diff --git a/routing/dht/messages.proto b/routing/dht/pb/dht.proto similarity index 98% rename from routing/dht/messages.proto rename to routing/dht/pb/dht.proto index 067690150..e0696e685 100644 --- a/routing/dht/messages.proto +++ b/routing/dht/pb/dht.proto @@ -1,4 +1,4 @@ -package dht; +package dht.pb; //run `protoc --go_out=. *.proto` to generate diff --git a/routing/dht/Message.go b/routing/dht/pb/message.go similarity index 87% rename from routing/dht/Message.go rename to routing/dht/pb/message.go index ae78d1f39..a77a5b917 100644 --- a/routing/dht/Message.go +++ b/routing/dht/pb/message.go @@ -1,4 +1,4 @@ -package dht +package dht_pb import ( "errors" @@ -8,7 +8,7 @@ import ( peer "github.com/jbenet/go-ipfs/peer" ) -func newMessage(typ Message_MessageType, key string, level int) *Message { +func NewMessage(typ Message_MessageType, key string, level int) *Message { m := &Message{ Type: &typ, Key: &key, @@ -31,7 +31,7 @@ func peerToPBPeer(p peer.Peer) *Message_Peer { return pbp } -func peersToPBPeers(peers []peer.Peer) []*Message_Peer { +func PeersToPBPeers(peers []peer.Peer) []*Message_Peer { pbpeers := make([]*Message_Peer, len(peers)) for i, p := range peers { pbpeers[i] = peerToPBPeer(p) @@ -53,8 +53,7 @@ func (m *Message_Peer) Address() (ma.Multiaddr, error) { func (m *Message) GetClusterLevel() int { level := m.GetClusterLevelRaw() - 1 if level < 0 { - log.Debug("GetClusterLevel: no routing level specified, assuming 0") - level = 0 + return 0 } return int(level) } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 26d17cbc4..64a7edbd6 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -6,6 +6,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" peer "github.com/jbenet/go-ipfs/peer" + pb "github.com/jbenet/go-ipfs/routing/dht/pb" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" ) @@ -152,10 +153,10 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int return peerOut } -func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*Message_Peer, ps *peerSet, count int, out chan peer.Peer) { +func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*pb.Message_Peer, ps *peerSet, count int, out chan peer.Peer) { done := make(chan struct{}) for _, pbp := range peers { - go func(mp *Message_Peer) { + go func(mp *pb.Message_Peer) { defer func() { done <- struct{}{} }() // construct new peer p, err := dht.ensureConnectedToPeer(mp) @@ -258,7 +259,7 @@ func (dht *IpfsDHT) Ping(ctx context.Context, p peer.Peer) error { // Thoughts: maybe this should accept an ID and do a peer lookup? log.Infof("ping %s start", p) - pmes := newMessage(Message_PING, "", 0) + pmes := pb.NewMessage(pb.Message_PING, "", 0) _, err := dht.sendRequest(ctx, p, pmes) log.Infof("ping %s end (err = %s)", p, err) return err From 99198bdb3423980ede03fb620acfc28f500e2372 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 25 Oct 2014 12:38:32 -0700 Subject: [PATCH 0315/3817] add context to blockservice Get This commit was moved from ipfs/go-blockservice@a236312107aff3440f3bff7c6f32040ef9aebbf8 --- blockservice/blocks_test.go | 6 +++++- blockservice/blockservice.go | 4 +--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index 41eceaae6..69a62d322 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -3,6 +3,9 @@ package blockservice import ( "bytes" "testing" + "time" + + "code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" blocks "github.com/jbenet/go-ipfs/blocks" @@ -37,7 +40,8 @@ func TestBlocks(t *testing.T) { t.Error("returned key is not equal to block key", err) } - b2, err := bs.GetBlock(b.Key()) + ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) + b2, err := bs.GetBlock(ctx, b.Key()) if err != nil { t.Error("failed to retrieve block from BlockService", err) return diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 9f914dc38..51e9ad7d6 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -2,7 +2,6 @@ package blockservice import ( "fmt" - "time" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" @@ -52,7 +51,7 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). -func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { +func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, error) { log.Debug("BlockService GetBlock: '%s'", k) datai, err := s.Datastore.Get(k.DsKey()) if err == nil { @@ -67,7 +66,6 @@ func (s *BlockService) GetBlock(k u.Key) (*blocks.Block, error) { }, nil } else if err == ds.ErrNotFound && s.Remote != nil { log.Debug("Blockservice: Searching bitswap.") - ctx, _ := context.WithTimeout(context.TODO(), 5*time.Second) blk, err := s.Remote.Block(ctx, k) if err != nil { return nil, err From 9fc4d2b5e6c01301d71245c6b8474a950710d77f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 25 Oct 2014 12:38:32 -0700 Subject: [PATCH 0316/3817] add context to blockservice Get This commit was moved from ipfs/go-merkledag@af4ae9aeca88a603eb6e34ecf6c961962d04201b --- ipld/merkledag/merkledag.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 8ce2fed6b..a6dbc6ebf 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -2,6 +2,9 @@ package merkledag import ( "fmt" + "time" + + "code.google.com/p/go.net/context" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" blocks "github.com/jbenet/go-ipfs/blocks" @@ -204,7 +207,8 @@ func (n *DAGService) Get(k u.Key) (*Node, error) { return nil, fmt.Errorf("DAGService is nil") } - b, err := n.Blocks.GetBlock(k) + ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) + b, err := n.Blocks.GetBlock(ctx, k) if err != nil { return nil, err } From 9c3b52ac6f4b948d6e8fda78661dae28d6f3dc94 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 25 Oct 2014 03:36:00 -0700 Subject: [PATCH 0317/3817] add in dag removal This commit was moved from ipfs/go-ipfs-routing@900149aaaea40ff056b483f5015b7a24980d9b5a --- routing/dht/dht.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 52ae1f76c..b3ca010b7 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -540,7 +540,11 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { func (dht *IpfsDHT) Bootstrap(ctx context.Context) { id := make([]byte, 16) rand.Read(id) - _, err := dht.FindPeer(ctx, peer.ID(id)) + p, err := dht.FindPeer(ctx, peer.ID(id)) + if err != nil { + log.Error("Bootstrap peer error: %s", err) + } + err = dht.dialer.DialPeer(p) if err != nil { log.Errorf("Bootstrap peer error: %s", err) } From bcbc0ca01e3e95787b2530a904550d1355778dbb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 25 Oct 2014 03:36:00 -0700 Subject: [PATCH 0318/3817] add in dag removal This commit was moved from ipfs/go-blockservice@ac4c5064be6fa9a0f776b0f22f02e9d9641ad8d0 --- blockservice/blockservice.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 51e9ad7d6..0ca533b19 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -76,3 +76,7 @@ func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, er return nil, u.ErrNotFound } } + +func (s *BlockService) DeleteBlock(k u.Key) error { + return s.Datastore.Delete(k.DsKey()) +} From 57ccea36562c7b9b018fbb6b7deb702d34e5418d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 25 Oct 2014 03:36:00 -0700 Subject: [PATCH 0319/3817] add in dag removal This commit was moved from ipfs/go-merkledag@d08cbc10573120b41a7b4d34ee71a97de322175b --- ipld/merkledag/merkledag.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index a6dbc6ebf..7834677a8 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -215,3 +215,16 @@ func (n *DAGService) Get(k u.Key) (*Node, error) { return Decoded(b.Data) } + +func (n *DAGService) Remove(nd *Node) error { + for _, l := range nd.Links { + if l.Node != nil { + n.Remove(l.Node) + } + } + k, err := nd.Key() + if err != nil { + return err + } + return n.Blocks.DeleteBlock(k) +} From c73bfd454c7830b5779dd5b2476a5e42056112d4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 25 Oct 2014 14:50:22 -0700 Subject: [PATCH 0320/3817] logging, logging, and some minor logging This commit was moved from ipfs/go-ipfs-routing@5ec85163db4134fb2fe8a755026ef06fe27d39e7 --- routing/dht/dht.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index b3ca010b7..fdb9f96f2 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -23,7 +23,7 @@ import ( var log = u.Logger("dht") -const doPinging = true +const doPinging = false // TODO. SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-dht/index.js From 96c7d92809a67836747e21b2897ee612cc436adb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 26 Oct 2014 00:45:40 +0000 Subject: [PATCH 0321/3817] lots of logging This commit was moved from ipfs/go-ipfs-routing@cd9d80b305877e2d53fa1a8d9e7a46cf0241ca31 --- routing/dht/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 35355b32f..d5db8d1da 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -145,7 +145,7 @@ func (dht *IpfsDHT) handleGetProviders(p peer.Peer, pmes *pb.Message) (*pb.Messa resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) // check if we have this value, to add ourselves as provider. - log.Debugf("handling GetProviders: '%s'", pmes.GetKey()) + log.Debugf("handling GetProviders: '%s'", u.Key(pmes.GetKey())) dsk := u.Key(pmes.GetKey()).DsKey() has, err := dht.datastore.Has(dsk) if err != nil && err != ds.ErrNotFound { From 637b069f0d61bb03f7270a95545566f217ea8dac Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 26 Oct 2014 00:45:40 +0000 Subject: [PATCH 0322/3817] lots of logging This commit was moved from ipfs/go-blockservice@e94f0b549b5bd5a9174d622575d80f2207d46658 --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 0ca533b19..acb6564ed 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -52,7 +52,7 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, error) { - log.Debug("BlockService GetBlock: '%s'", k) + log.Debugf("BlockService GetBlock: '%s'", k) datai, err := s.Datastore.Get(k.DsKey()) if err == nil { log.Debug("Blockservice: Got data in datastore.") From e92f6e8c78cb7c3e9d7c6960bd537f2c5755a229 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sat, 25 Oct 2014 22:15:19 -0400 Subject: [PATCH 0323/3817] convert DAGService to an interface This commit was moved from ipfs/go-merkledag@a967e017dcab9a26756376aa872a9bd158309e00 --- ipld/merkledag/merkledag.go | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 7834677a8..0e595c9d6 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -62,7 +62,7 @@ func MakeLink(n *Node) (*Link, error) { }, nil } -func (l *Link) GetNode(serv *DAGService) (*Node, error) { +func (l *Link) GetNode(serv DAGService) (*Node, error) { if l.Node != nil { return l.Node, nil } @@ -151,20 +151,32 @@ func (n *Node) Key() (u.Key, error) { } // DAGService is an IPFS Merkle DAG service. +type DAGService interface { + Add(*Node) (u.Key, error) + AddRecursive(*Node) error + Get(u.Key) (*Node, error) + Remove(*Node) error +} + +func NewDAGService(bs *bserv.BlockService) DAGService { + return &dagService{bs} +} + +// dagService is an IPFS Merkle DAG service. // - the root is virtual (like a forest) // - stores nodes' data in a BlockService // TODO: should cache Nodes that are in memory, and be // able to free some of them when vm pressure is high -type DAGService struct { +type dagService struct { Blocks *bserv.BlockService } -// Add adds a node to the DAGService, storing the block in the BlockService -func (n *DAGService) Add(nd *Node) (u.Key, error) { +// Add adds a node to the dagService, storing the block in the BlockService +func (n *dagService) Add(nd *Node) (u.Key, error) { k, _ := nd.Key() log.Debug("DagService Add [%s]", k) if n == nil { - return "", fmt.Errorf("DAGService is nil") + return "", fmt.Errorf("dagService is nil") } d, err := nd.Encoded(false) @@ -182,7 +194,7 @@ func (n *DAGService) Add(nd *Node) (u.Key, error) { return n.Blocks.AddBlock(b) } -func (n *DAGService) AddRecursive(nd *Node) error { +func (n *dagService) AddRecursive(nd *Node) error { _, err := n.Add(nd) if err != nil { log.Info("AddRecursive Error: %s\n", err) @@ -201,10 +213,10 @@ func (n *DAGService) AddRecursive(nd *Node) error { return nil } -// Get retrieves a node from the DAGService, fetching the block in the BlockService -func (n *DAGService) Get(k u.Key) (*Node, error) { +// Get retrieves a node from the dagService, fetching the block in the BlockService +func (n *dagService) Get(k u.Key) (*Node, error) { if n == nil { - return nil, fmt.Errorf("DAGService is nil") + return nil, fmt.Errorf("dagService is nil") } ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) @@ -216,7 +228,7 @@ func (n *DAGService) Get(k u.Key) (*Node, error) { return Decoded(b.Data) } -func (n *DAGService) Remove(nd *Node) error { +func (n *dagService) Remove(nd *Node) error { for _, l := range nd.Links { if l.Node != nil { n.Remove(l.Node) From f0bae5bed96d5385ec5708010021f147a016db45 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sat, 25 Oct 2014 22:15:19 -0400 Subject: [PATCH 0324/3817] convert DAGService to an interface This commit was moved from ipfs/go-unixfs@b73ce6e1ae3ebdada7d7d195f13b20e3b611d262 --- unixfs/io/dagmodifier.go | 4 +-- unixfs/io/dagmodifier_test.go | 14 ++++++---- unixfs/io/dagreader.go | 4 +-- unixfs/io/dagwriter.go | 4 +-- unixfs/io/dagwriter_test.go | 49 ++++++++++++++++++++++++++++++++--- 5 files changed, 61 insertions(+), 14 deletions(-) diff --git a/unixfs/io/dagmodifier.go b/unixfs/io/dagmodifier.go index 2d5fb77d9..ebec24cfc 100644 --- a/unixfs/io/dagmodifier.go +++ b/unixfs/io/dagmodifier.go @@ -17,14 +17,14 @@ import ( // perform surgery on a DAG 'file' // Dear god, please rename this to something more pleasant type DagModifier struct { - dagserv *mdag.DAGService + dagserv mdag.DAGService curNode *mdag.Node pbdata *ftpb.Data splitter chunk.BlockSplitter } -func NewDagModifier(from *mdag.Node, serv *mdag.DAGService, spl chunk.BlockSplitter) (*DagModifier, error) { +func NewDagModifier(from *mdag.Node, serv mdag.DAGService, spl chunk.BlockSplitter) (*DagModifier, error) { pbd, err := ft.FromBytes(from.Data) if err != nil { return nil, err diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index 3686ff859..d45559b3a 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -16,17 +16,17 @@ import ( logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging" ) -func getMockDagServ(t *testing.T) *mdag.DAGService { +func getMockDagServ(t *testing.T) mdag.DAGService { dstore := ds.NewMapDatastore() bserv, err := bs.NewBlockService(dstore, nil) if err != nil { t.Fatal(err) } - return &mdag.DAGService{Blocks: bserv} + return mdag.NewDAGService(bserv) } -func getNode(t *testing.T, dserv *mdag.DAGService, size int64) ([]byte, *mdag.Node) { - dw := NewDagWriter(dserv, &chunk.SizeSplitter{Size: 500}) +func getNode(t *testing.T, dserv mdag.DAGService, size int64) ([]byte, *mdag.Node) { + dw := NewDagWriter(dserv, &chunk.SizeSplitter{500}) n, err := io.CopyN(dw, u.NewFastRand(), size) if err != nil { @@ -36,7 +36,11 @@ func getNode(t *testing.T, dserv *mdag.DAGService, size int64) ([]byte, *mdag.No t.Fatal("Incorrect copy amount!") } - dw.Close() + err = dw.Close() + if err != nil { + t.Fatal("DagWriter failed to close,", err) + } + node := dw.GetNode() dr, err := NewDagReader(node, dserv) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 846916103..17ad87371 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -16,7 +16,7 @@ var ErrIsDir = errors.New("this dag node is a directory") // DagReader provides a way to easily read the data contained in a dag. type DagReader struct { - serv *mdag.DAGService + serv mdag.DAGService node *mdag.Node position int buf *bytes.Buffer @@ -24,7 +24,7 @@ type DagReader struct { // NewDagReader creates a new reader object that reads the data represented by the given // node, using the passed in DAGService for data retreival -func NewDagReader(n *mdag.Node, serv *mdag.DAGService) (io.Reader, error) { +func NewDagReader(n *mdag.Node, serv mdag.DAGService) (io.Reader, error) { pb := new(ftpb.Data) err := proto.Unmarshal(n.Data, pb) if err != nil { diff --git a/unixfs/io/dagwriter.go b/unixfs/io/dagwriter.go index 4abb1b36c..c9b91cc58 100644 --- a/unixfs/io/dagwriter.go +++ b/unixfs/io/dagwriter.go @@ -10,7 +10,7 @@ import ( var log = util.Logger("dagwriter") type DagWriter struct { - dagserv *dag.DAGService + dagserv dag.DAGService node *dag.Node totalSize int64 splChan chan []byte @@ -19,7 +19,7 @@ type DagWriter struct { seterr error } -func NewDagWriter(ds *dag.DAGService, splitter chunk.BlockSplitter) *DagWriter { +func NewDagWriter(ds dag.DAGService, splitter chunk.BlockSplitter) *DagWriter { dw := new(DagWriter) dw.dagserv = ds dw.splChan = make(chan []byte, 8) diff --git a/unixfs/io/dagwriter_test.go b/unixfs/io/dagwriter_test.go index d0b8f45d1..08779e2c1 100644 --- a/unixfs/io/dagwriter_test.go +++ b/unixfs/io/dagwriter_test.go @@ -7,6 +7,7 @@ import ( ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" bs "github.com/jbenet/go-ipfs/blockservice" + "github.com/jbenet/go-ipfs/importer" chunk "github.com/jbenet/go-ipfs/importer/chunk" mdag "github.com/jbenet/go-ipfs/merkledag" ) @@ -53,7 +54,7 @@ func TestDagWriter(t *testing.T) { if err != nil { t.Fatal(err) } - dag := &mdag.DAGService{Blocks: bserv} + dag := mdag.NewDAGService(bserv) dw := NewDagWriter(dag, &chunk.SizeSplitter{Size: 4096}) nbytes := int64(1024 * 1024 * 2) @@ -87,7 +88,7 @@ func TestMassiveWrite(t *testing.T) { if err != nil { t.Fatal(err) } - dag := &mdag.DAGService{Blocks: bserv} + dag := mdag.NewDAGService(bserv) dw := NewDagWriter(dag, &chunk.SizeSplitter{Size: 4096}) nbytes := int64(1024 * 1024 * 1024 * 16) @@ -107,7 +108,7 @@ func BenchmarkDagWriter(b *testing.B) { if err != nil { b.Fatal(err) } - dag := &mdag.DAGService{Blocks: bserv} + dag := mdag.NewDAGService(bserv) b.ResetTimer() nbytes := int64(100000) @@ -125,3 +126,45 @@ func BenchmarkDagWriter(b *testing.B) { } } + +func TestAgainstImporter(t *testing.T) { + dstore := ds.NewMapDatastore() + bserv, err := bs.NewBlockService(dstore, nil) + if err != nil { + t.Fatal(err) + } + dag := mdag.NewDAGService(bserv) + + nbytes := int64(1024 * 1024 * 2) + + // DagWriter + dw := NewDagWriter(dag, &chunk.SizeSplitter{4096}) + n, err := io.CopyN(dw, &datasource{}, nbytes) + if err != nil { + t.Fatal(err) + } + if n != nbytes { + t.Fatal("Copied incorrect amount of bytes!") + } + + dw.Close() + dwNode := dw.GetNode() + dwKey, err := dwNode.Key() + if err != nil { + t.Fatal(err) + } + + // DagFromFile + rl := &io.LimitedReader{&datasource{}, nbytes} + + dffNode, err := importer.NewDagFromReaderWithSplitter(rl, &chunk.SizeSplitter{4096}) + dffKey, err := dffNode.Key() + if err != nil { + t.Fatal(err) + } + if dwKey.String() != dffKey.String() { + t.Errorf("\nDagWriter produced %s\n"+ + "DagFromReader produced %s", + dwKey, dffKey) + } +} From 71a0e94a047bbca55acf8a576753040dfbeea5e6 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sat, 25 Oct 2014 22:15:19 -0400 Subject: [PATCH 0325/3817] convert DAGService to an interface This commit was moved from ipfs/go-ipfs-pinner@9ddd1b6f52856a121d9d17a55b83a528d3c76cc3 --- pinning/pinner/pin.go | 6 +++--- pinning/pinner/pin_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index b9c509a03..a3f0e260b 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -32,11 +32,11 @@ type pinner struct { recursePin set.BlockSet directPin set.BlockSet indirPin *indirectPin - dserv *mdag.DAGService + dserv mdag.DAGService dstore ds.Datastore } -func NewPinner(dstore ds.Datastore, serv *mdag.DAGService) Pinner { +func NewPinner(dstore ds.Datastore, serv mdag.DAGService) Pinner { // Load set from given datastore... rcds := nsds.Wrap(dstore, recursePinDatastoreKey) @@ -151,7 +151,7 @@ func (p *pinner) IsPinned(key util.Key) bool { p.indirPin.HasKey(key) } -func LoadPinner(d ds.Datastore, dserv *mdag.DAGService) (Pinner, error) { +func LoadPinner(d ds.Datastore, dserv mdag.DAGService) (Pinner, error) { p := new(pinner) { // load recursive set diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 8f6f6c343..7bf0756df 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -24,7 +24,7 @@ func TestPinnerBasic(t *testing.T) { t.Fatal(err) } - dserv := &mdag.DAGService{Blocks: bserv} + dserv := mdag.NewDAGService(bserv) p := NewPinner(dstore, dserv) From faa98bc1f15b6a8eada3f84bd45c48b22ef12fd4 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sat, 25 Oct 2014 22:15:19 -0400 Subject: [PATCH 0326/3817] convert DAGService to an interface This commit was moved from ipfs/go-path@6b883e0689daa4c1ffb026db5dfe1db07a8c0d7f --- path/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/path.go b/path/path.go index 03c1a481e..cb1061d11 100644 --- a/path/path.go +++ b/path/path.go @@ -15,7 +15,7 @@ var log = u.Logger("path") // Resolver provides path resolution to IPFS // It has a pointer to a DAGService, which is uses to resolve nodes. type Resolver struct { - DAG *merkledag.DAGService + DAG merkledag.DAGService } // ResolvePath fetches the node for given path. It uses the first From 04101214bf1925a3cd9c1f36090e8b9ccb2b141d Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 25 Oct 2014 04:44:20 -0700 Subject: [PATCH 0327/3817] net/service now uses ctxcloser This commit was moved from ipfs/go-ipfs-routing@2956770389b90a53c2f4a76747893ca0a674087e --- routing/dht/dht_test.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 507db4eec..ef13f0367 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -23,11 +23,7 @@ import ( func setupDHT(ctx context.Context, t *testing.T, p peer.Peer) *IpfsDHT { peerstore := peer.NewPeerstore() - dhts := netservice.NewService(nil) // nil handler for now, need to patch it - if err := dhts.Start(ctx); err != nil { - t.Fatal(err) - } - + dhts := netservice.NewService(ctx, nil) // nil handler for now, need to patch it net, err := inet.NewIpfsNetwork(ctx, p, peerstore, &mux.ProtocolMap{ mux.ProtocolID_Routing: dhts, }) From 6c651385e47d34fa2975d8292d847df8a47b30fa Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 25 Oct 2014 07:12:01 -0700 Subject: [PATCH 0328/3817] dht ctxcloserify This commit was moved from ipfs/go-ipfs-routing@c2d23b197fef1e87f7309186bad3ae704874c633 --- routing/dht/dht.go | 15 ++++++++++----- routing/dht/dht_test.go | 20 ++++++++++---------- routing/dht/handlers.go | 6 ------ routing/dht/providers.go | 24 ++++++++++++++++-------- routing/dht/providers_test.go | 7 +++++-- 5 files changed, 41 insertions(+), 31 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index fdb9f96f2..76cde7fb5 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -14,6 +14,7 @@ import ( pb "github.com/jbenet/go-ipfs/routing/dht/pb" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" + ctxc "github.com/jbenet/go-ipfs/util/ctxcloser" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" @@ -56,7 +57,7 @@ type IpfsDHT struct { //lock to make diagnostics work better diaglock sync.Mutex - ctx context.Context + ctxc.ContextCloser } // NewDHT creates a new DHT object with the given peer as the 'local' host @@ -67,9 +68,10 @@ func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, dialer inet.Dia dht.datastore = dstore dht.self = p dht.peerstore = ps - dht.ctx = ctx + dht.ContextCloser = ctxc.NewContextCloser(ctx, nil) - dht.providers = NewProviderManager(p.ID()) + dht.providers = NewProviderManager(dht.Context(), p.ID()) + dht.AddCloserChild(dht.providers) dht.routingTables = make([]*kb.RoutingTable, 3) dht.routingTables[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID()), time.Millisecond*1000) @@ -78,6 +80,7 @@ func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, dialer inet.Dia dht.birth = time.Now() if doPinging { + dht.Children().Add(1) go dht.PingRoutine(time.Second * 10) } return dht @@ -516,6 +519,8 @@ func (dht *IpfsDHT) loadProvidableKeys() error { // PingRoutine periodically pings nearest neighbors. func (dht *IpfsDHT) PingRoutine(t time.Duration) { + defer dht.Children().Done() + tick := time.Tick(t) for { select { @@ -524,13 +529,13 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { rand.Read(id) peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(u.Key(id)), 5) for _, p := range peers { - ctx, _ := context.WithTimeout(dht.ctx, time.Second*5) + ctx, _ := context.WithTimeout(dht.Context(), time.Second*5) err := dht.Ping(ctx, p) if err != nil { log.Errorf("Ping error: %s", err) } } - case <-dht.ctx.Done(): + case <-dht.Closing(): return } } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index ef13f0367..2b8732338 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -92,8 +92,8 @@ func TestPing(t *testing.T) { dhtA := setupDHT(ctx, t, peerA) dhtB := setupDHT(ctx, t, peerB) - defer dhtA.Halt() - defer dhtB.Halt() + defer dhtA.Close() + defer dhtB.Close() defer dhtA.dialer.(inet.Network).Close() defer dhtB.dialer.(inet.Network).Close() @@ -136,8 +136,8 @@ func TestValueGetSet(t *testing.T) { dhtA := setupDHT(ctx, t, peerA) dhtB := setupDHT(ctx, t, peerB) - defer dhtA.Halt() - defer dhtB.Halt() + defer dhtA.Close() + defer dhtB.Close() defer dhtA.dialer.(inet.Network).Close() defer dhtB.dialer.(inet.Network).Close() @@ -179,7 +179,7 @@ func TestProvides(t *testing.T) { _, peers, dhts := setupDHTS(ctx, 4, t) defer func() { for i := 0; i < 4; i++ { - dhts[i].Halt() + dhts[i].Close() defer dhts[i].dialer.(inet.Network).Close() } }() @@ -239,7 +239,7 @@ func TestProvidesAsync(t *testing.T) { _, peers, dhts := setupDHTS(ctx, 4, t) defer func() { for i := 0; i < 4; i++ { - dhts[i].Halt() + dhts[i].Close() defer dhts[i].dialer.(inet.Network).Close() } }() @@ -302,7 +302,7 @@ func TestLayeredGet(t *testing.T) { _, peers, dhts := setupDHTS(ctx, 4, t) defer func() { for i := 0; i < 4; i++ { - dhts[i].Halt() + dhts[i].Close() defer dhts[i].dialer.(inet.Network).Close() } }() @@ -355,7 +355,7 @@ func TestFindPeer(t *testing.T) { _, peers, dhts := setupDHTS(ctx, 4, t) defer func() { for i := 0; i < 4; i++ { - dhts[i].Halt() + dhts[i].Close() dhts[i].dialer.(inet.Network).Close() } }() @@ -443,8 +443,8 @@ func TestConnectCollision(t *testing.T) { t.Fatal("Timeout received!") } - dhtA.Halt() - dhtB.Halt() + dhtA.Close() + dhtB.Close() dhtA.dialer.(inet.Network).Close() dhtB.dialer.(inet.Network).Close() diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index d5db8d1da..fe628eeef 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -205,9 +205,3 @@ func (dht *IpfsDHT) handleAddProvider(p peer.Peer, pmes *pb.Message) (*pb.Messag return pmes, nil // send back same msg as confirmation. } - -// Halt stops all communications from this peer and shut down -// TODO -- remove this in favor of context -func (dht *IpfsDHT) Halt() { - dht.providers.Halt() -} diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 204fdf7d5..f7d491d6a 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -5,6 +5,9 @@ import ( peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" + ctxc "github.com/jbenet/go-ipfs/util/ctxcloser" + + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ) type ProviderManager struct { @@ -14,8 +17,8 @@ type ProviderManager struct { getlocal chan chan []u.Key newprovs chan *addProv getprovs chan *getProv - halt chan struct{} period time.Duration + ctxc.ContextCloser } type addProv struct { @@ -28,19 +31,24 @@ type getProv struct { resp chan []peer.Peer } -func NewProviderManager(local peer.ID) *ProviderManager { +func NewProviderManager(ctx context.Context, local peer.ID) *ProviderManager { pm := new(ProviderManager) pm.getprovs = make(chan *getProv) pm.newprovs = make(chan *addProv) pm.providers = make(map[u.Key][]*providerInfo) pm.getlocal = make(chan chan []u.Key) pm.local = make(map[u.Key]struct{}) - pm.halt = make(chan struct{}) + pm.ContextCloser = ctxc.NewContextCloser(ctx, nil) + + pm.Children().Add(1) go pm.run() + return pm } func (pm *ProviderManager) run() { + defer pm.Children().Done() + tick := time.NewTicker(time.Hour) for { select { @@ -53,6 +61,7 @@ func (pm *ProviderManager) run() { pi.Value = np.val arr := pm.providers[np.k] pm.providers[np.k] = append(arr, pi) + case gp := <-pm.getprovs: var parr []peer.Peer provs := pm.providers[gp.k] @@ -60,12 +69,14 @@ func (pm *ProviderManager) run() { parr = append(parr, p.Value) } gp.resp <- parr + case lc := <-pm.getlocal: var keys []u.Key for k, _ := range pm.local { keys = append(keys, k) } lc <- keys + case <-tick.C: for k, provs := range pm.providers { var filtered []*providerInfo @@ -76,7 +87,8 @@ func (pm *ProviderManager) run() { } pm.providers[k] = filtered } - case <-pm.halt: + + case <-pm.Closing(): return } } @@ -102,7 +114,3 @@ func (pm *ProviderManager) GetLocal() []u.Key { pm.getlocal <- resp return <-resp } - -func (pm *ProviderManager) Halt() { - pm.halt <- struct{}{} -} diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index b37327d2e..c4ae53910 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -5,16 +5,19 @@ import ( "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" + + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ) func TestProviderManager(t *testing.T) { + ctx := context.Background() mid := peer.ID("testing") - p := NewProviderManager(mid) + p := NewProviderManager(ctx, mid) a := u.Key("test") p.AddProvider(a, peer.WithIDString("testingprovider")) resp := p.GetProviders(a) if len(resp) != 1 { t.Fatal("Could not retrieve provider.") } - p.Halt() + p.Close() } From 88d4980082556cba9950e85014137e616d05154a Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 28 Oct 2014 01:06:28 -0700 Subject: [PATCH 0329/3817] feat(routing) define routing.ErrNotFound This commit was moved from ipfs/go-ipfs-routing@a8e81896dece625084e057b479cdc43888ae0720 --- routing/routing.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/routing/routing.go b/routing/routing.go index cb60e5ee8..7f0486c76 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -1,12 +1,17 @@ package routing import ( + "errors" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" ) +// ErrNotFound is returned when a search fails to find anything +var ErrNotFound = errors.New("routing: key not found") + // IpfsRouting is the routing module interface // It is implemented by things like DHTs, etc. type IpfsRouting interface { From 370fec30b5e90a90ed549fb0e9b120ff0ec3b68b Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 28 Oct 2014 02:17:46 -0700 Subject: [PATCH 0330/3817] refactor(routing) use routing.ErrNotFound This commit was moved from ipfs/go-ipfs-routing@5d5a205f0fb9763857215d690324de507f657037 --- routing/dht/dht.go | 7 ++++--- routing/dht/ext_test.go | 7 ++++--- routing/dht/query.go | 3 ++- routing/dht/routing.go | 5 +++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 76cde7fb5..f1c422721 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -11,6 +11,7 @@ import ( inet "github.com/jbenet/go-ipfs/net" msg "github.com/jbenet/go-ipfs/net/message" peer "github.com/jbenet/go-ipfs/peer" + routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" @@ -288,8 +289,8 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, return nil, peers, nil } - log.Warning("getValueOrPeers: u.ErrNotFound") - return nil, nil, u.ErrNotFound + log.Warning("getValueOrPeers: routing.ErrNotFound") + return nil, nil, routing.ErrNotFound } // getValueSingle simply performs the get value RPC with the given parameters @@ -326,7 +327,7 @@ func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key, return value, nil } } - return nil, u.ErrNotFound + return nil, routing.ErrNotFound } // getLocal attempts to retrieve the value from the datastore diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index be6f17d96..77684db28 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -12,6 +12,7 @@ import ( msg "github.com/jbenet/go-ipfs/net/message" mux "github.com/jbenet/go-ipfs/net/mux" peer "github.com/jbenet/go-ipfs/peer" + "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" @@ -145,7 +146,7 @@ func TestGetFailures(t *testing.T) { ctx2, _ := context.WithTimeout(context.Background(), time.Second) _, err = d.GetValue(ctx2, u.Key("test")) if err != nil { - if err != u.ErrNotFound { + if err != routing.ErrNotFound { t.Fatalf("Expected ErrNotFound, got: %s", err) } } else { @@ -247,7 +248,7 @@ func TestNotFound(t *testing.T) { log.Debug("get value got %v", v) if err != nil { switch err { - case u.ErrNotFound: + case routing.ErrNotFound: //Success! return case u.ErrTimeout: @@ -311,7 +312,7 @@ func TestLessThanKResponses(t *testing.T) { _, err := d.GetValue(ctx, u.Key("hello")) if err != nil { switch err { - case u.ErrNotFound: + case routing.ErrNotFound: //Success! return case u.ErrTimeout: diff --git a/routing/dht/query.go b/routing/dht/query.go index d15e939b7..48974b8eb 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -6,6 +6,7 @@ import ( inet "github.com/jbenet/go-ipfs/net" peer "github.com/jbenet/go-ipfs/peer" queue "github.com/jbenet/go-ipfs/peer/queue" + "github.com/jbenet/go-ipfs/routing" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" todoctr "github.com/jbenet/go-ipfs/util/todocounter" @@ -128,7 +129,7 @@ func (r *dhtQueryRunner) Run(peers []peer.Peer) (*dhtQueryResult, error) { // so workers are working. // wait until they're done. - err := u.ErrNotFound + err := routing.ErrNotFound select { case <-r.peersRemaining.Done(): diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 64a7edbd6..44edd99a7 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -6,6 +6,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" peer "github.com/jbenet/go-ipfs/peer" + "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" @@ -89,7 +90,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { log.Debugf("GetValue %v %v", key, result.value) if result.value == nil { - return nil, u.ErrNotFound + return nil, routing.ErrNotFound } return result.value, nil @@ -248,7 +249,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) log.Debug("FindPeer %v %v", id, result.success) if result.peer == nil { - return nil, u.ErrNotFound + return nil, routing.ErrNotFound } return result.peer, nil From e9731cf5841ae9fa1d3350323fdc18450c0fe419 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 28 Oct 2014 05:12:54 -0700 Subject: [PATCH 0331/3817] style(routing) message This commit was moved from ipfs/go-ipfs-routing@83c4c52375a3e8f367c29ec45a2c580b7b43705f --- routing/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/routing.go b/routing/routing.go index 7f0486c76..3ef381856 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -10,7 +10,7 @@ import ( ) // ErrNotFound is returned when a search fails to find anything -var ErrNotFound = errors.New("routing: key not found") +var ErrNotFound = errors.New("routing: not found") // IpfsRouting is the routing module interface // It is implemented by things like DHTs, etc. From 5f16d340e81da472fef127f46df57668114d2031 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 28 Oct 2014 02:22:32 -0700 Subject: [PATCH 0332/3817] refactor(namesys) use one-off error This commit was moved from ipfs/go-namesys@bb2d064a7d4a964e9332b6723d411ef5b72ca9cd --- namesys/dns.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 66448511f..655e910b8 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -1,13 +1,12 @@ package namesys import ( + "fmt" "net" b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" isd "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-is-domain" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - - u "github.com/jbenet/go-ipfs/util" ) // DNSResolver implements a Resolver on DNS domains @@ -44,5 +43,5 @@ func (r *DNSResolver) Resolve(name string) (string, error) { return t, nil } - return "", u.ErrNotFound + return "", fmt.Errorf("namesys: %v not found", name) } From 0d024b055e30310dc99f881ad51e313f8a4ee99b Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 28 Oct 2014 02:21:20 -0700 Subject: [PATCH 0333/3817] refactor(merkledag) use one-off error This commit was moved from ipfs/go-merkledag@dd6d7fb64d58816329cf3abc7f3a3cefa5c47add --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 0e595c9d6..014fcec80 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -103,7 +103,7 @@ func (n *Node) RemoveNodeLink(name string) error { return nil } } - return u.ErrNotFound + return fmt.Errorf("merkledag: %s not found", name) } // Copy returns a copy of the node. From 432220b5718137eb148126a988a6a9bfd7d44ff6 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 28 Oct 2014 02:19:09 -0700 Subject: [PATCH 0334/3817] refactor(blockservice) export blockservice.ErrNotFound This commit was moved from ipfs/go-blockservice@cad00db618039b117a4291a066aedc019ad911ee --- blockservice/blockservice.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index acb6564ed..54a992cdb 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -1,6 +1,7 @@ package blockservice import ( + "errors" "fmt" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -13,6 +14,7 @@ import ( ) var log = u.Logger("blockservice") +var ErrNotFound = errors.New("blockservice: key not found") // BlockService is a block datastore. // It uses an internal `datastore.Datastore` instance to store values. @@ -73,7 +75,7 @@ func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, er return blk, nil } else { log.Debug("Blockservice GetBlock: Not found.") - return nil, u.ErrNotFound + return nil, ErrNotFound } } From 1153a69c8a0cf3f5bf10a0ea28063834da331474 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 28 Oct 2014 15:40:26 -0700 Subject: [PATCH 0335/3817] fix(namesys, merkledag) use static error This commit was moved from ipfs/go-namesys@598418c73294f9e14da6760b13d589d821afd849 --- namesys/dns.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/namesys/dns.go b/namesys/dns.go index 655e910b8..847ed0670 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -9,6 +9,8 @@ import ( mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" ) +var ErrNotFound = fmt.Errorf("namesys: name not found") + // DNSResolver implements a Resolver on DNS domains type DNSResolver struct { // TODO: maybe some sort of caching? @@ -43,5 +45,5 @@ func (r *DNSResolver) Resolve(name string) (string, error) { return t, nil } - return "", fmt.Errorf("namesys: %v not found", name) + return "", ErrNotFound } From a4bd086ea079c5e9479be5c3f25ff44c0980211c Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 28 Oct 2014 15:40:26 -0700 Subject: [PATCH 0336/3817] fix(namesys, merkledag) use static error This commit was moved from ipfs/go-merkledag@7938d0f2564375d237adb3673a6de3c6c1f9f50f --- ipld/merkledag/merkledag.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 014fcec80..c0a37bd24 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -13,6 +13,7 @@ import ( ) var log = u.Logger("merkledag") +var ErrNotFound = fmt.Errorf("merkledag: not found") // NodeMap maps u.Keys to Nodes. // We cannot use []byte/Multihash for keys :( From 7d4465f6352b0dfff2230cf327b7bdaf2aa0646e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 28 Oct 2014 15:41:49 -0700 Subject: [PATCH 0337/3817] fix(namesys) use the error that already exists This commit was moved from ipfs/go-namesys@e9ca88894c799696d19712e94231b99154c36f98 --- namesys/dns.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 847ed0670..881979930 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -1,7 +1,6 @@ package namesys import ( - "fmt" "net" b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" @@ -9,8 +8,6 @@ import ( mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" ) -var ErrNotFound = fmt.Errorf("namesys: name not found") - // DNSResolver implements a Resolver on DNS domains type DNSResolver struct { // TODO: maybe some sort of caching? @@ -45,5 +42,5 @@ func (r *DNSResolver) Resolve(name string) (string, error) { return t, nil } - return "", ErrNotFound + return "", ErrResolveFailed } From 34e35b61ab52aeb1d24be70aa933177f18e9e6bd Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 28 Oct 2014 16:10:17 -0700 Subject: [PATCH 0338/3817] fix(merkledag) return static error This commit was moved from ipfs/go-merkledag@3ab0d158c2dd8e1386b33406c937faedf627a38e --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index c0a37bd24..a7eb05d7c 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -104,7 +104,7 @@ func (n *Node) RemoveNodeLink(name string) error { return nil } } - return fmt.Errorf("merkledag: %s not found", name) + return ErrNotFound } // Copy returns a copy of the node. From 66c5bdfca6220a50a06a1a30ae85bc5619833c58 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 30 Oct 2014 03:13:08 +0000 Subject: [PATCH 0339/3817] rewrite add command to use dagwriter, moved a pinner into the dagwriter for inline pinning This commit was moved from ipfs/go-unixfs@bdef3a1fb5e91875fdf4130907594b462caa9b08 --- unixfs/io/dagwriter.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/unixfs/io/dagwriter.go b/unixfs/io/dagwriter.go index c9b91cc58..6575b1edf 100644 --- a/unixfs/io/dagwriter.go +++ b/unixfs/io/dagwriter.go @@ -3,6 +3,7 @@ package io import ( "github.com/jbenet/go-ipfs/importer/chunk" dag "github.com/jbenet/go-ipfs/merkledag" + "github.com/jbenet/go-ipfs/pin" ft "github.com/jbenet/go-ipfs/unixfs" "github.com/jbenet/go-ipfs/util" ) @@ -17,6 +18,7 @@ type DagWriter struct { done chan struct{} splitter chunk.BlockSplitter seterr error + Pinner pin.ManualPinner } func NewDagWriter(ds dag.DAGService, splitter chunk.BlockSplitter) *DagWriter { @@ -48,7 +50,10 @@ func (dw *DagWriter) startSplitter() { // Store the block size in the root node mbf.AddBlockSize(uint64(len(blkData))) node := &dag.Node{Data: ft.WrapData(blkData)} - _, err := dw.dagserv.Add(node) + nk, err := dw.dagserv.Add(node) + if dw.Pinner != nil { + dw.Pinner.PinWithMode(nk, pin.Indirect) + } if err != nil { dw.seterr = err log.Critical("Got error adding created node to dagservice: %s", err) @@ -75,12 +80,15 @@ func (dw *DagWriter) startSplitter() { root.Data = data // Add root node to the dagservice - _, err = dw.dagserv.Add(root) + rootk, err := dw.dagserv.Add(root) if err != nil { dw.seterr = err log.Critical("Got error adding created node to dagservice: %s", err) return } + if dw.Pinner != nil { + dw.Pinner.PinWithMode(rootk, pin.Recursive) + } dw.node = root dw.done <- struct{}{} } From c90ceab717535c6abc3368f074235eebc0f858dd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 30 Oct 2014 03:13:08 +0000 Subject: [PATCH 0340/3817] rewrite add command to use dagwriter, moved a pinner into the dagwriter for inline pinning This commit was moved from ipfs/go-ipfs-pinner@e3ac58db178603cd642a06f61706b1ccd6606d7c --- pinning/pinner/pin.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index a3f0e260b..dba14a977 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -20,6 +20,14 @@ var recursePinDatastoreKey = ds.NewKey("/local/pins/recursive/keys") var directPinDatastoreKey = ds.NewKey("/local/pins/direct/keys") var indirectPinDatastoreKey = ds.NewKey("/local/pins/indirect/keys") +type PinMode int + +const ( + Recursive PinMode = iota + Direct + Indirect +) + type Pinner interface { IsPinned(util.Key) bool Pin(*mdag.Node, bool) error @@ -27,6 +35,13 @@ type Pinner interface { Flush() error } +// ManualPinner is for manually editing the pin structure +// Use with care +type ManualPinner interface { + PinWithMode(util.Key, PinMode) + Pinner +} + type pinner struct { lock sync.RWMutex recursePin set.BlockSet @@ -228,3 +243,14 @@ func loadSet(d ds.Datastore, k ds.Key, val interface{}) error { } return json.Unmarshal(bf, val) } + +func (p *pinner) PinWithMode(k util.Key, mode PinMode) { + switch mode { + case Recursive: + p.recursePin.AddBlock(k) + case Direct: + p.directPin.AddBlock(k) + case Indirect: + p.indirPin.Increment(k) + } +} From caf8cc647440da014413d43239cb17599ac5e30d Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 30 Oct 2014 01:17:26 -0700 Subject: [PATCH 0341/3817] fix tests (circular import) This commit was moved from ipfs/go-unixfs@6c1301d7ddf0c565a2aefcff2fd5121c1ff36445 --- unixfs/io/dagwriter_test.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/unixfs/io/dagwriter_test.go b/unixfs/io/dagwriter_test.go index 08779e2c1..edb4a0a70 100644 --- a/unixfs/io/dagwriter_test.go +++ b/unixfs/io/dagwriter_test.go @@ -1,4 +1,4 @@ -package io +package io_test import ( "testing" @@ -7,9 +7,10 @@ import ( ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" bs "github.com/jbenet/go-ipfs/blockservice" - "github.com/jbenet/go-ipfs/importer" + importer "github.com/jbenet/go-ipfs/importer" chunk "github.com/jbenet/go-ipfs/importer/chunk" mdag "github.com/jbenet/go-ipfs/merkledag" + dagio "github.com/jbenet/go-ipfs/unixfs/io" ) type datasource struct { @@ -55,7 +56,7 @@ func TestDagWriter(t *testing.T) { t.Fatal(err) } dag := mdag.NewDAGService(bserv) - dw := NewDagWriter(dag, &chunk.SizeSplitter{Size: 4096}) + dw := dagio.NewDagWriter(dag, &chunk.SizeSplitter{Size: 4096}) nbytes := int64(1024 * 1024 * 2) n, err := io.CopyN(dw, &datasource{}, nbytes) @@ -70,7 +71,7 @@ func TestDagWriter(t *testing.T) { dw.Close() node := dw.GetNode() - read, err := NewDagReader(node, dag) + read, err := dagio.NewDagReader(node, dag) if err != nil { t.Fatal(err) } @@ -89,7 +90,7 @@ func TestMassiveWrite(t *testing.T) { t.Fatal(err) } dag := mdag.NewDAGService(bserv) - dw := NewDagWriter(dag, &chunk.SizeSplitter{Size: 4096}) + dw := dagio.NewDagWriter(dag, &chunk.SizeSplitter{Size: 4096}) nbytes := int64(1024 * 1024 * 1024 * 16) n, err := io.CopyN(dw, &datasource{}, nbytes) @@ -114,7 +115,7 @@ func BenchmarkDagWriter(b *testing.B) { nbytes := int64(100000) for i := 0; i < b.N; i++ { b.SetBytes(nbytes) - dw := NewDagWriter(dag, &chunk.SizeSplitter{Size: 4096}) + dw := dagio.NewDagWriter(dag, &chunk.SizeSplitter{Size: 4096}) n, err := io.CopyN(dw, &datasource{}, nbytes) if err != nil { b.Fatal(err) @@ -138,7 +139,7 @@ func TestAgainstImporter(t *testing.T) { nbytes := int64(1024 * 1024 * 2) // DagWriter - dw := NewDagWriter(dag, &chunk.SizeSplitter{4096}) + dw := dagio.NewDagWriter(dag, &chunk.SizeSplitter{4096}) n, err := io.CopyN(dw, &datasource{}, nbytes) if err != nil { t.Fatal(err) From 88c7308df4b47937807887cbbae20079ca2cb553 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 30 Oct 2014 01:54:50 -0700 Subject: [PATCH 0342/3817] blockservice: dont write blocks twice If the datastore has a value for the key, we already have the block. We should not write it again. This will make redundant writes much faster. At the moment, a datastore.Has on leveldb is a GetBackedHas. Track https://github.com/jbenet/go-datastore/issues/6 This commit was moved from ipfs/go-blockservice@3ba5be4871fbde34f4eb57d8c8993c44bb1c3d4a --- blockservice/blockservice.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 54a992cdb..c3c3868f2 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -40,10 +40,21 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { log.Debug("blockservice: storing [%s] in datastore", k) // TODO(brian): define a block datastore with a Put method which accepts a // block parameter - err := s.Datastore.Put(k.DsKey(), b.Data) + + // check if we have it before adding. this is an extra read, but large writes + // are more expensive. + // TODO(jbenet) cheaper has. https://github.com/jbenet/go-datastore/issues/6 + has, err := s.Datastore.Has(k.DsKey()) if err != nil { return k, err } + if !has { + err := s.Datastore.Put(k.DsKey(), b.Data) + if err != nil { + return k, err + } + } + if s.Remote != nil { ctx := context.TODO() err = s.Remote.HasBlock(ctx, *b) From 9e5b3d6ed0d4c18ad85ece3ae6ac5e9a71c51072 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 30 Oct 2014 02:01:27 -0700 Subject: [PATCH 0343/3817] blockservice: signal add optimization This commit was moved from ipfs/go-blockservice@4040913e75826f772d26ab836d247f238fc2a9a7 --- blockservice/blockservice.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index c3c3868f2..edfaa11fa 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -37,7 +37,6 @@ func NewBlockService(d ds.Datastore, rem exchange.Interface) (*BlockService, err // AddBlock adds a particular block to the service, Putting it into the datastore. func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { k := b.Key() - log.Debug("blockservice: storing [%s] in datastore", k) // TODO(brian): define a block datastore with a Put method which accepts a // block parameter @@ -48,7 +47,10 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { if err != nil { return k, err } - if !has { + if has { + log.Debugf("blockservice: storing [%s] in datastore (already stored)", k) + } else { + log.Debugf("blockservice: storing [%s] in datastore", k) err := s.Datastore.Put(k.DsKey(), b.Data) if err != nil { return k, err From 7d302de27393119f2c3929d6a0acb8d115ef64b0 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 30 Oct 2014 02:49:39 -0700 Subject: [PATCH 0344/3817] test splitting is deterministic. (it is) This commit was moved from ipfs/go-ipfs-chunker@f3c9cf6af38ecf4a9fad243cac7ca56e712cc774 --- chunker/splitting_test.go | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 chunker/splitting_test.go diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go new file mode 100644 index 000000000..0ecb143cb --- /dev/null +++ b/chunker/splitting_test.go @@ -0,0 +1,53 @@ +package chunk + +import ( + "bytes" + "crypto/rand" + "testing" +) + +func randBuf(t *testing.T, size int) []byte { + buf := make([]byte, size) + if _, err := rand.Read(buf); err != nil { + t.Fatal("failed to read enough randomness") + } + return buf +} + +func copyBuf(buf []byte) []byte { + cpy := make([]byte, len(buf)) + copy(cpy, buf) + return cpy +} + +func TestSizeSplitterIsDeterministic(t *testing.T) { + + test := func() { + bufR := randBuf(t, 10000000) // crank this up to satisfy yourself. + bufA := copyBuf(bufR) + bufB := copyBuf(bufR) + + chunksA := DefaultSplitter.Split(bytes.NewReader(bufA)) + chunksB := DefaultSplitter.Split(bytes.NewReader(bufB)) + + for n := 0; ; n++ { + a, moreA := <-chunksA + b, moreB := <-chunksB + + if !moreA { + if moreB { + t.Fatal("A ended, B didnt.") + } + return + } + + if !bytes.Equal(a, b) { + t.Fatalf("chunk %d not equal", n) + } + } + } + + for run := 0; run < 1; run++ { // crank this up to satisfy yourself. + test() + } +} From 42bafa0b2f76f3d4d1744000b4beb29cc8567b74 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 30 Oct 2014 04:14:05 -0700 Subject: [PATCH 0345/3817] util: remove broken rand This commit was moved from ipfs/go-ipfs-pinner@f0c4a2c1b6b937f3fd3df6841fa4516f4b1958cd --- pinning/pinner/pin_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 7bf0756df..1ea302823 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -12,7 +12,7 @@ import ( func randNode() (*mdag.Node, util.Key) { nd := new(mdag.Node) nd.Data = make([]byte, 32) - util.NewFastRand().Read(nd.Data) + util.NewTimeSeededRand().Read(nd.Data) k, _ := nd.Key() return nd, k } From 448bc7c685da53e88a82e1faf8e82bd6a65cb3ed Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 30 Oct 2014 04:14:05 -0700 Subject: [PATCH 0346/3817] util: remove broken rand This commit was moved from ipfs/go-unixfs@1e82bae96701443075e1e15a1b346da51d6359d6 --- unixfs/io/dagmodifier_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index d45559b3a..edb4d6f76 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -28,7 +28,7 @@ func getMockDagServ(t *testing.T) mdag.DAGService { func getNode(t *testing.T, dserv mdag.DAGService, size int64) ([]byte, *mdag.Node) { dw := NewDagWriter(dserv, &chunk.SizeSplitter{500}) - n, err := io.CopyN(dw, u.NewFastRand(), size) + n, err := io.CopyN(dw, u.NewTimeSeededRand(), size) if err != nil { t.Fatal(err) } @@ -58,7 +58,7 @@ func getNode(t *testing.T, dserv mdag.DAGService, size int64) ([]byte, *mdag.Nod func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) []byte { newdata := make([]byte, size) - r := u.NewFastRand() + r := u.NewTimeSeededRand() r.Read(newdata) if size+beg > uint64(len(orig)) { @@ -160,7 +160,7 @@ func TestMultiWrite(t *testing.T) { } data := make([]byte, 4000) - u.NewFastRand().Read(data) + u.NewTimeSeededRand().Read(data) for i := 0; i < len(data); i++ { n, err := dagmod.WriteAt(data[i:i+1], uint64(i)) @@ -201,7 +201,7 @@ func TestMultiWriteCoal(t *testing.T) { } data := make([]byte, 4000) - u.NewFastRand().Read(data) + u.NewTimeSeededRand().Read(data) for i := 0; i < len(data); i++ { n, err := dagmod.WriteAt(data[:i+1], 0) From 1a8bdd3eb21f843b0de9f73894fb1f66dd3b6ebe Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 30 Oct 2014 05:42:18 -0700 Subject: [PATCH 0347/3817] make vendor cc @whyrusleeping This commit was moved from ipfs/go-merkledag@bea70df3c176101c99cbb810d489ceaee15835a7 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index a7eb05d7c..92fa6e48e 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "code.google.com/p/go.net/context" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" blocks "github.com/jbenet/go-ipfs/blocks" From 893b01ff37f143b1ae682af0779d947a8d6c6c29 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 30 Oct 2014 05:42:18 -0700 Subject: [PATCH 0348/3817] make vendor cc @whyrusleeping This commit was moved from ipfs/go-blockservice@dfe4cc91d032c52a46001f274fb88368ddf9286f --- blockservice/blocks_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index 69a62d322..1e837eb5d 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "code.google.com/p/go.net/context" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" blocks "github.com/jbenet/go-ipfs/blocks" From 12bef3d4fe0265192e8be0f2981262362ee99b25 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 30 Oct 2014 06:35:29 -0700 Subject: [PATCH 0349/3817] fix(all) log.Debug -> log.Debugf This commit was moved from ipfs/go-ipfs-routing@140c8948cc76084a2158a53008b954883290937c --- routing/dht/ext_test.go | 2 +- routing/dht/query.go | 20 ++++++++++---------- routing/dht/routing.go | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 77684db28..19275338d 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -245,7 +245,7 @@ func TestNotFound(t *testing.T) { ctx, _ = context.WithTimeout(ctx, time.Second*5) v, err := d.GetValue(ctx, u.Key("hello")) - log.Debug("get value got %v", v) + log.Debugf("get value got %v", v) if err != nil { switch err { case routing.ErrNotFound: diff --git a/routing/dht/query.go b/routing/dht/query.go index 48974b8eb..cd9fae98c 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -107,7 +107,7 @@ func newQueryRunner(ctx context.Context, q *dhtQuery) *dhtQueryRunner { } func (r *dhtQueryRunner) Run(peers []peer.Peer) (*dhtQueryResult, error) { - log.Debug("Run query with %d peers.", len(peers)) + log.Debugf("Run query with %d peers.", len(peers)) if len(peers) == 0 { log.Warning("Running query with no peers!") return nil, nil @@ -176,7 +176,7 @@ func (r *dhtQueryRunner) addPeerToQuery(next peer.Peer, benchmark peer.Peer) { r.peersSeen[next.Key()] = next r.Unlock() - log.Debug("adding peer to query: %v\n", next) + log.Debugf("adding peer to query: %v\n", next) // do this after unlocking to prevent possible deadlocks. r.peersRemaining.Increment(1) @@ -200,14 +200,14 @@ func (r *dhtQueryRunner) spawnWorkers() { if !more { return // channel closed. } - log.Debug("spawning worker for: %v\n", p) + log.Debugf("spawning worker for: %v\n", p) go r.queryPeer(p) } } } func (r *dhtQueryRunner) queryPeer(p peer.Peer) { - log.Debug("spawned worker for: %v\n", p) + log.Debugf("spawned worker for: %v\n", p) // make sure we rate limit concurrency. select { @@ -218,12 +218,12 @@ func (r *dhtQueryRunner) queryPeer(p peer.Peer) { } // ok let's do this! - log.Debug("running worker for: %v", p) + log.Debugf("running worker for: %v", p) // make sure we do this when we exit defer func() { // signal we're done proccessing peer p - log.Debug("completing worker for: %v", p) + log.Debugf("completing worker for: %v", p) r.peersRemaining.Decrement(1) r.rateLimit <- struct{}{} }() @@ -232,7 +232,7 @@ func (r *dhtQueryRunner) queryPeer(p peer.Peer) { // (Incidentally, this will add it to the peerstore too) err := r.query.dialer.DialPeer(p) if err != nil { - log.Debug("ERROR worker for: %v -- err connecting: %v", p, err) + log.Debugf("ERROR worker for: %v -- err connecting: %v", p, err) r.Lock() r.errs = append(r.errs, err) r.Unlock() @@ -243,20 +243,20 @@ func (r *dhtQueryRunner) queryPeer(p peer.Peer) { res, err := r.query.qfunc(r.ctx, p) if err != nil { - log.Debug("ERROR worker for: %v %v", p, err) + log.Debugf("ERROR worker for: %v %v", p, err) r.Lock() r.errs = append(r.errs, err) r.Unlock() } else if res.success { - log.Debug("SUCCESS worker for: %v", p, res) + log.Debugf("SUCCESS worker for: %v", p, res) r.Lock() r.result = res r.Unlock() r.cancel() // signal to everyone that we're done. } else if res.closerPeers != nil { - log.Debug("PEERS CLOSER -- worker for: %v\n", p) + log.Debugf("PEERS CLOSER -- worker for: %v\n", p) for _, next := range res.closerPeers { r.addPeerToQuery(next, p) } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 44edd99a7..da400fee2 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -247,7 +247,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) return nil, err } - log.Debug("FindPeer %v %v", id, result.success) + log.Debugf("FindPeer %v %v", id, result.success) if result.peer == nil { return nil, routing.ErrNotFound } From 5f0d31298e80e7780eaafab87de0f21e7948fd84 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 30 Oct 2014 06:35:29 -0700 Subject: [PATCH 0350/3817] fix(all) log.Debug -> log.Debugf This commit was moved from ipfs/go-merkledag@65feb72f41546946a64dc74b7cc92198e8adeaba --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 92fa6e48e..3134899bd 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -175,7 +175,7 @@ type dagService struct { // Add adds a node to the dagService, storing the block in the BlockService func (n *dagService) Add(nd *Node) (u.Key, error) { k, _ := nd.Key() - log.Debug("DagService Add [%s]", k) + log.Debugf("DagService Add [%s]", k) if n == nil { return "", fmt.Errorf("dagService is nil") } From a5bbd9089f0258c80d6aa8beb8a163c1f0407489 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 30 Oct 2014 06:35:29 -0700 Subject: [PATCH 0351/3817] fix(all) log.Debug -> log.Debugf This commit was moved from ipfs/go-ipfs-pinner@9780fd9b9bf7c27335fae28b112d07ae6b1919b5 --- pinning/pinner/indirect.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index 2eb303de2..b15b720ee 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -32,7 +32,7 @@ func loadIndirPin(d ds.Datastore, k ds.Key) (*indirectPin, error) { keys = append(keys, k) refcnt[k] = v } - log.Debug("indirPin keys: %#v", keys) + log.Debugf("indirPin keys: %#v", keys) return &indirectPin{blockset: set.SimpleSetFromKeys(keys), refCounts: refcnt}, nil } From 1913d3bda0792e344c485b0dd0aeb9ed8b8a8eb7 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 30 Oct 2014 06:35:29 -0700 Subject: [PATCH 0352/3817] fix(all) log.Debug -> log.Debugf This commit was moved from ipfs/go-namesys@a36cbad76c634b1144c2eb5e4f8e5fc6378eb9e4 --- namesys/publisher.go | 2 +- namesys/routing.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 7203fb1d4..d95f1cbbc 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -27,7 +27,7 @@ func NewRoutingPublisher(route routing.IpfsRouting) Publisher { // Publish implements Publisher. Accepts a keypair and a value, func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { - log.Debug("namesys: Publish %s", value) + log.Debugf("namesys: Publish %s", value) // validate `value` is a ref (multihash) _, err := mh.FromB58String(value) diff --git a/namesys/routing.go b/namesys/routing.go index ce1755f69..6259705ec 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -35,7 +35,7 @@ func (r *routingResolver) CanResolve(name string) bool { // Resolve implements Resolver. Uses the IPFS routing system to resolve SFS-like // names. func (r *routingResolver) Resolve(name string) (string, error) { - log.Debug("RoutingResolve: '%s'", name) + log.Debugf("RoutingResolve: '%s'", name) ctx := context.TODO() hash, err := mh.FromB58String(name) if err != nil { From f6a3cd9101d9dd35e7b28517768b33f99d742151 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 30 Oct 2014 06:35:29 -0700 Subject: [PATCH 0353/3817] fix(all) log.Debug -> log.Debugf This commit was moved from ipfs/go-path@2aaa8dc3612cca588b3c81bb55ceadf1999eac23 --- path/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/path.go b/path/path.go index cb1061d11..8d3070a0e 100644 --- a/path/path.go +++ b/path/path.go @@ -22,7 +22,7 @@ type Resolver struct { // path component as a hash (key) of the first node, then resolves // all other components walking the links, with ResolveLinks. func (s *Resolver) ResolvePath(fpath string) (*merkledag.Node, error) { - log.Debug("Resolve: '%s'", fpath) + log.Debugf("Resolve: '%s'", fpath) fpath = path.Clean(fpath) parts := strings.Split(fpath, "/") From d63817341be4f9b753a2acac90e98fc536be5d95 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 30 Oct 2014 16:34:52 +0000 Subject: [PATCH 0354/3817] fix bug where terminal would randomly become garbled binary crap This commit was moved from ipfs/go-ipfs-routing@5eff2297985049beb39f3a1ce5856777c78f0506 --- routing/dht/dht.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index f1c422721..e76ab571c 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -240,7 +240,7 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.Peer, key string) er return err } - log.Debugf("%s putProvider: %s for %s", dht.self, p, key) + log.Debugf("%s putProvider: %s for %s", dht.self, p, u.Key(key)) if rpmes.GetKey() != pmes.GetKey() { return errors.New("provider not added correctly") } From 3504fb8ee514730ee2c5dd95a7b134d13715f665 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 31 Oct 2014 03:20:26 +0000 Subject: [PATCH 0355/3817] remove dagwriter in favor of new importer function This commit was moved from ipfs/go-ipfs-routing@b3581e8f77fa4cf5f8e3f0356005e8690684c064 --- routing/dht/query.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/query.go b/routing/dht/query.go index cd9fae98c..557af095c 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -207,7 +207,7 @@ func (r *dhtQueryRunner) spawnWorkers() { } func (r *dhtQueryRunner) queryPeer(p peer.Peer) { - log.Debugf("spawned worker for: %v\n", p) + log.Debugf("spawned worker for: %v", p) // make sure we rate limit concurrency. select { @@ -256,7 +256,7 @@ func (r *dhtQueryRunner) queryPeer(p peer.Peer) { r.cancel() // signal to everyone that we're done. } else if res.closerPeers != nil { - log.Debugf("PEERS CLOSER -- worker for: %v\n", p) + log.Debugf("PEERS CLOSER -- worker for: %v", p) for _, next := range res.closerPeers { r.addPeerToQuery(next, p) } From 41db609abf5eba8cb8b64730a54923e3cdada505 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 31 Oct 2014 03:20:26 +0000 Subject: [PATCH 0356/3817] remove dagwriter in favor of new importer function This commit was moved from ipfs/go-unixfs@31d9a0b3d3d60cee6c2da39255cf2b7b791777ad --- unixfs/io/dagmodifier.go | 2 + unixfs/io/dagmodifier_test.go | 16 +--- unixfs/io/dagwriter.go | 115 ----------------------- unixfs/io/dagwriter_test.go | 171 ---------------------------------- 4 files changed, 5 insertions(+), 299 deletions(-) delete mode 100644 unixfs/io/dagwriter.go delete mode 100644 unixfs/io/dagwriter_test.go diff --git a/unixfs/io/dagmodifier.go b/unixfs/io/dagmodifier.go index ebec24cfc..a05b9d6ed 100644 --- a/unixfs/io/dagmodifier.go +++ b/unixfs/io/dagmodifier.go @@ -13,6 +13,8 @@ import ( u "github.com/jbenet/go-ipfs/util" ) +var log = u.Logger("dagio") + // DagModifier is the only struct licensed and able to correctly // perform surgery on a DAG 'file' // Dear god, please rename this to something more pleasant diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index edb4d6f76..822c87471 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -7,6 +7,7 @@ import ( "testing" bs "github.com/jbenet/go-ipfs/blockservice" + imp "github.com/jbenet/go-ipfs/importer" "github.com/jbenet/go-ipfs/importer/chunk" mdag "github.com/jbenet/go-ipfs/merkledag" ft "github.com/jbenet/go-ipfs/unixfs" @@ -26,22 +27,11 @@ func getMockDagServ(t *testing.T) mdag.DAGService { } func getNode(t *testing.T, dserv mdag.DAGService, size int64) ([]byte, *mdag.Node) { - dw := NewDagWriter(dserv, &chunk.SizeSplitter{500}) - - n, err := io.CopyN(dw, u.NewTimeSeededRand(), size) + in := io.LimitReader(u.NewTimeSeededRand(), size) + node, err := imp.BuildDagFromReader(in, dserv, nil, &chunk.SizeSplitter{500}) if err != nil { t.Fatal(err) } - if n != size { - t.Fatal("Incorrect copy amount!") - } - - err = dw.Close() - if err != nil { - t.Fatal("DagWriter failed to close,", err) - } - - node := dw.GetNode() dr, err := NewDagReader(node, dserv) if err != nil { diff --git a/unixfs/io/dagwriter.go b/unixfs/io/dagwriter.go deleted file mode 100644 index 6575b1edf..000000000 --- a/unixfs/io/dagwriter.go +++ /dev/null @@ -1,115 +0,0 @@ -package io - -import ( - "github.com/jbenet/go-ipfs/importer/chunk" - dag "github.com/jbenet/go-ipfs/merkledag" - "github.com/jbenet/go-ipfs/pin" - ft "github.com/jbenet/go-ipfs/unixfs" - "github.com/jbenet/go-ipfs/util" -) - -var log = util.Logger("dagwriter") - -type DagWriter struct { - dagserv dag.DAGService - node *dag.Node - totalSize int64 - splChan chan []byte - done chan struct{} - splitter chunk.BlockSplitter - seterr error - Pinner pin.ManualPinner -} - -func NewDagWriter(ds dag.DAGService, splitter chunk.BlockSplitter) *DagWriter { - dw := new(DagWriter) - dw.dagserv = ds - dw.splChan = make(chan []byte, 8) - dw.splitter = splitter - dw.done = make(chan struct{}) - go dw.startSplitter() - return dw -} - -// startSplitter manages splitting incoming bytes and -// creating dag nodes from them. Created nodes are stored -// in the DAGService and then released to the GC. -func (dw *DagWriter) startSplitter() { - - // Since the splitter functions take a reader (and should!) - // we wrap our byte chan input in a reader - r := util.NewByteChanReader(dw.splChan) - blkchan := dw.splitter.Split(r) - - // First data block is reserved for storage in the root node - first := <-blkchan - mbf := new(ft.MultiBlock) - root := new(dag.Node) - - for blkData := range blkchan { - // Store the block size in the root node - mbf.AddBlockSize(uint64(len(blkData))) - node := &dag.Node{Data: ft.WrapData(blkData)} - nk, err := dw.dagserv.Add(node) - if dw.Pinner != nil { - dw.Pinner.PinWithMode(nk, pin.Indirect) - } - if err != nil { - dw.seterr = err - log.Critical("Got error adding created node to dagservice: %s", err) - return - } - - // Add a link to this node without storing a reference to the memory - err = root.AddNodeLinkClean("", node) - if err != nil { - dw.seterr = err - log.Critical("Got error adding created node to root node: %s", err) - return - } - } - - // Generate the root node data - mbf.Data = first - data, err := mbf.GetBytes() - if err != nil { - dw.seterr = err - log.Critical("Failed generating bytes for multiblock file: %s", err) - return - } - root.Data = data - - // Add root node to the dagservice - rootk, err := dw.dagserv.Add(root) - if err != nil { - dw.seterr = err - log.Critical("Got error adding created node to dagservice: %s", err) - return - } - if dw.Pinner != nil { - dw.Pinner.PinWithMode(rootk, pin.Recursive) - } - dw.node = root - dw.done <- struct{}{} -} - -func (dw *DagWriter) Write(b []byte) (int, error) { - if dw.seterr != nil { - return 0, dw.seterr - } - dw.splChan <- b - return len(b), nil -} - -// Close the splitters input channel and wait for it to finish -// Must be called to finish up splitting, otherwise split method -// will never halt -func (dw *DagWriter) Close() error { - close(dw.splChan) - <-dw.done - return nil -} - -func (dw *DagWriter) GetNode() *dag.Node { - return dw.node -} diff --git a/unixfs/io/dagwriter_test.go b/unixfs/io/dagwriter_test.go deleted file mode 100644 index edb4a0a70..000000000 --- a/unixfs/io/dagwriter_test.go +++ /dev/null @@ -1,171 +0,0 @@ -package io_test - -import ( - "testing" - - "io" - - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - bs "github.com/jbenet/go-ipfs/blockservice" - importer "github.com/jbenet/go-ipfs/importer" - chunk "github.com/jbenet/go-ipfs/importer/chunk" - mdag "github.com/jbenet/go-ipfs/merkledag" - dagio "github.com/jbenet/go-ipfs/unixfs/io" -) - -type datasource struct { - i int -} - -func (d *datasource) Read(b []byte) (int, error) { - for i, _ := range b { - b[i] = byte(d.i % 256) - d.i++ - } - return len(b), nil -} - -func (d *datasource) Matches(t *testing.T, r io.Reader, length int) bool { - b := make([]byte, 100) - i := 0 - for { - n, err := r.Read(b) - if err != nil && err != io.EOF { - t.Fatal(err) - } - for _, v := range b[:n] { - if v != byte(i%256) { - t.Fatalf("Buffers differed at byte: %d (%d != %d)", i, v, (i % 256)) - } - i++ - } - if err == io.EOF { - break - } - } - if i != length { - t.Fatalf("Incorrect length. (%d != %d)", i, length) - } - return true -} - -func TestDagWriter(t *testing.T) { - dstore := ds.NewMapDatastore() - bserv, err := bs.NewBlockService(dstore, nil) - if err != nil { - t.Fatal(err) - } - dag := mdag.NewDAGService(bserv) - dw := dagio.NewDagWriter(dag, &chunk.SizeSplitter{Size: 4096}) - - nbytes := int64(1024 * 1024 * 2) - n, err := io.CopyN(dw, &datasource{}, nbytes) - if err != nil { - t.Fatal(err) - } - - if n != nbytes { - t.Fatal("Copied incorrect amount of bytes!") - } - - dw.Close() - - node := dw.GetNode() - read, err := dagio.NewDagReader(node, dag) - if err != nil { - t.Fatal(err) - } - - d := &datasource{} - if !d.Matches(t, read, int(nbytes)) { - t.Fatal("Failed to validate!") - } -} - -func TestMassiveWrite(t *testing.T) { - t.SkipNow() - dstore := ds.NewNullDatastore() - bserv, err := bs.NewBlockService(dstore, nil) - if err != nil { - t.Fatal(err) - } - dag := mdag.NewDAGService(bserv) - dw := dagio.NewDagWriter(dag, &chunk.SizeSplitter{Size: 4096}) - - nbytes := int64(1024 * 1024 * 1024 * 16) - n, err := io.CopyN(dw, &datasource{}, nbytes) - if err != nil { - t.Fatal(err) - } - if n != nbytes { - t.Fatal("Incorrect copy size.") - } - dw.Close() -} - -func BenchmarkDagWriter(b *testing.B) { - dstore := ds.NewNullDatastore() - bserv, err := bs.NewBlockService(dstore, nil) - if err != nil { - b.Fatal(err) - } - dag := mdag.NewDAGService(bserv) - - b.ResetTimer() - nbytes := int64(100000) - for i := 0; i < b.N; i++ { - b.SetBytes(nbytes) - dw := dagio.NewDagWriter(dag, &chunk.SizeSplitter{Size: 4096}) - n, err := io.CopyN(dw, &datasource{}, nbytes) - if err != nil { - b.Fatal(err) - } - if n != nbytes { - b.Fatal("Incorrect copy size.") - } - dw.Close() - } - -} - -func TestAgainstImporter(t *testing.T) { - dstore := ds.NewMapDatastore() - bserv, err := bs.NewBlockService(dstore, nil) - if err != nil { - t.Fatal(err) - } - dag := mdag.NewDAGService(bserv) - - nbytes := int64(1024 * 1024 * 2) - - // DagWriter - dw := dagio.NewDagWriter(dag, &chunk.SizeSplitter{4096}) - n, err := io.CopyN(dw, &datasource{}, nbytes) - if err != nil { - t.Fatal(err) - } - if n != nbytes { - t.Fatal("Copied incorrect amount of bytes!") - } - - dw.Close() - dwNode := dw.GetNode() - dwKey, err := dwNode.Key() - if err != nil { - t.Fatal(err) - } - - // DagFromFile - rl := &io.LimitedReader{&datasource{}, nbytes} - - dffNode, err := importer.NewDagFromReaderWithSplitter(rl, &chunk.SizeSplitter{4096}) - dffKey, err := dffNode.Key() - if err != nil { - t.Fatal(err) - } - if dwKey.String() != dffKey.String() { - t.Errorf("\nDagWriter produced %s\n"+ - "DagFromReader produced %s", - dwKey, dffKey) - } -} From 5eb949381cbd197c476d921353b71741ab36376b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 26 Oct 2014 08:01:33 +0000 Subject: [PATCH 0357/3817] benchmark secure channel This commit was moved from ipfs/go-ipfs-routing@59cbce37dcaa767614de6f8be043b0a4d2571454 --- routing/kbucket/table_test.go | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 2b45d1572..cda69064a 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,16 +7,12 @@ import ( "testing" "time" + tu "github.com/jbenet/go-ipfs/util/testutil" + peer "github.com/jbenet/go-ipfs/peer" ) -func _randPeer() peer.Peer { - id := make(peer.ID, 16) - crand.Read(id) - return peer.WithID(id) -} - -func _randID() ID { +func RandID() ID { buf := make([]byte, 16) crand.Read(buf) @@ -30,11 +26,11 @@ func TestBucket(t *testing.T) { peers := make([]peer.Peer, 100) for i := 0; i < 100; i++ { - peers[i] = _randPeer() + peers[i] = tu.RandPeer() b.pushFront(peers[i]) } - local := _randPeer() + local := tu.RandPeer() localID := ConvertPeerID(local.ID()) i := rand.Intn(len(peers)) @@ -65,12 +61,12 @@ func TestBucket(t *testing.T) { // Right now, this just makes sure that it doesnt hang or crash func TestTableUpdate(t *testing.T) { - local := _randPeer() + local := tu.RandPeer() rt := NewRoutingTable(10, ConvertPeerID(local.ID()), time.Hour) peers := make([]peer.Peer, 100) for i := 0; i < 100; i++ { - peers[i] = _randPeer() + peers[i] = tu.RandPeer() } // Testing Update @@ -82,7 +78,7 @@ func TestTableUpdate(t *testing.T) { } for i := 0; i < 100; i++ { - id := _randID() + id := RandID() ret := rt.NearestPeers(id, 5) if len(ret) == 0 { t.Fatal("Failed to find node near ID.") @@ -91,12 +87,12 @@ func TestTableUpdate(t *testing.T) { } func TestTableFind(t *testing.T) { - local := _randPeer() + local := tu.RandPeer() rt := NewRoutingTable(10, ConvertPeerID(local.ID()), time.Hour) peers := make([]peer.Peer, 100) for i := 0; i < 5; i++ { - peers[i] = _randPeer() + peers[i] = tu.RandPeer() rt.Update(peers[i]) } @@ -108,12 +104,12 @@ func TestTableFind(t *testing.T) { } func TestTableFindMultiple(t *testing.T) { - local := _randPeer() + local := tu.RandPeer() rt := NewRoutingTable(20, ConvertPeerID(local.ID()), time.Hour) peers := make([]peer.Peer, 100) for i := 0; i < 18; i++ { - peers[i] = _randPeer() + peers[i] = tu.RandPeer() rt.Update(peers[i]) } @@ -132,7 +128,7 @@ func TestTableMultithreaded(t *testing.T) { tab := NewRoutingTable(20, ConvertPeerID(local), time.Hour) var peers []peer.Peer for i := 0; i < 500; i++ { - peers = append(peers, _randPeer()) + peers = append(peers, tu.RandPeer()) } done := make(chan struct{}) @@ -171,7 +167,7 @@ func BenchmarkUpdates(b *testing.B) { var peers []peer.Peer for i := 0; i < b.N; i++ { - peers = append(peers, _randPeer()) + peers = append(peers, tu.RandPeer()) } b.StartTimer() @@ -187,7 +183,7 @@ func BenchmarkFinds(b *testing.B) { var peers []peer.Peer for i := 0; i < b.N; i++ { - peers = append(peers, _randPeer()) + peers = append(peers, tu.RandPeer()) tab.Update(peers[i]) } From 58013e21a797de46e2149a96c0fbd919db5cba4b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 27 Oct 2014 20:23:14 +0000 Subject: [PATCH 0358/3817] msgio pooling first hack This commit was moved from ipfs/go-unixfs@c6ff34db40e9b7e9ac44f9a6614d1d872ccc9a95 --- unixfs/io/dagreader.go | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 17ad87371..a2dbeb2f2 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -9,7 +9,6 @@ import ( mdag "github.com/jbenet/go-ipfs/merkledag" ft "github.com/jbenet/go-ipfs/unixfs" ftpb "github.com/jbenet/go-ipfs/unixfs/pb" - u "github.com/jbenet/go-ipfs/util" ) var ErrIsDir = errors.New("this dag node is a directory") @@ -36,11 +35,12 @@ func NewDagReader(n *mdag.Node, serv mdag.DAGService) (io.Reader, error) { // Dont allow reading directories return nil, ErrIsDir case ftpb.Data_File: - return &DagReader{ + dr := &DagReader{ node: n, serv: serv, buf: bytes.NewBuffer(pb.GetData()), - }, nil + } + return dr, nil case ftpb.Data_Raw: // Raw block will just be a single level, return a byte buffer return bytes.NewBuffer(pb.GetData()), nil @@ -55,17 +55,12 @@ func (dr *DagReader) precalcNextBuf() error { if dr.position >= len(dr.node.Links) { return io.EOF } - nxtLink := dr.node.Links[dr.position] - nxt := nxtLink.Node - if nxt == nil { - nxtNode, err := dr.serv.Get(u.Key(nxtLink.Hash)) - if err != nil { - return err - } - nxt = nxtNode + nxt, err := dr.node.Links[dr.position].GetNode(dr.serv) + if err != nil { + return err } pb := new(ftpb.Data) - err := proto.Unmarshal(nxt.Data, pb) + err = proto.Unmarshal(nxt.Data, pb) if err != nil { return err } From 798840c3105bae8c5eabcb6f346694d0fb635529 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 27 Oct 2014 20:23:14 +0000 Subject: [PATCH 0359/3817] msgio pooling first hack This commit was moved from ipfs/go-merkledag@63e2295dd7008cacf236c5051acdfabccfaec844 --- ipld/merkledag/merkledag.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 3134899bd..19e145254 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -241,3 +241,21 @@ func (n *dagService) Remove(nd *Node) error { } return n.Blocks.DeleteBlock(k) } + +func FetchGraph(ctx context.Context, root *Node, serv *DAGService) { + for _, l := range root.Links { + go func(lnk *Link) { + select { + case <-ctx.Done(): + return + } + + nd, err := lnk.GetNode(serv) + if err != nil { + log.Error(err) + return + } + FetchGraph(ctx, nd, serv) + }(l) + } +} From c4d64e3aa2ab61fb3a36d0fb70933d4f7d40f206 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 27 Oct 2014 20:23:14 +0000 Subject: [PATCH 0360/3817] msgio pooling first hack This commit was moved from ipfs/go-ipfs-routing@3289fcf0f2898de485ff0bff4f34953af8f07ab7 --- routing/dht/pb/dht.pb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/pb/dht.pb.go b/routing/dht/pb/dht.pb.go index 6c488c51a..22c87bac9 100644 --- a/routing/dht/pb/dht.pb.go +++ b/routing/dht/pb/dht.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package dht_pb -import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import proto "code.google.com/p/gogoprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. From 2e4dcb9913f6167c6068af31e01e9107e37f330c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 30 Oct 2014 05:55:39 +0000 Subject: [PATCH 0361/3817] some small cleanup of logging This commit was moved from ipfs/go-unixfs@ab968b005bf0fa8e54f1a3d79fd9d272486805fd --- unixfs/io/dagreader.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index a2dbeb2f2..307f1d305 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -18,7 +18,7 @@ type DagReader struct { serv mdag.DAGService node *mdag.Node position int - buf *bytes.Buffer + buf io.Reader } // NewDagReader creates a new reader object that reads the data represented by the given @@ -71,8 +71,13 @@ func (dr *DagReader) precalcNextBuf() error { return ft.ErrInvalidDirLocation case ftpb.Data_File: //TODO: this *should* work, needs testing first - //return NewDagReader(nxt, dr.serv) - panic("Not yet handling different layers of indirection!") + log.Warning("Running untested code for multilayered indirect FS reads.") + subr, err := NewDagReader(nxt, dr.serv) + if err != nil { + return err + } + dr.buf = subr + return nil case ftpb.Data_Raw: dr.buf = bytes.NewBuffer(pb.GetData()) return nil From 1655ef752499bdd6004dd8bdf4917df5057e1cce Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 29 Oct 2014 21:07:17 +0000 Subject: [PATCH 0362/3817] dagservice interface fix This commit was moved from ipfs/go-merkledag@03fab00cb35fb51c977388a127d8aefc5a07b9eb --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 19e145254..d9221f0da 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -242,7 +242,7 @@ func (n *dagService) Remove(nd *Node) error { return n.Blocks.DeleteBlock(k) } -func FetchGraph(ctx context.Context, root *Node, serv *DAGService) { +func FetchGraph(ctx context.Context, root *Node, serv DAGService) { for _, l := range root.Links { go func(lnk *Link) { select { From 688f63623be2391e681465d89ec74344b61a0dc5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 31 Oct 2014 06:26:28 +0000 Subject: [PATCH 0363/3817] cleanup from CR This commit was moved from ipfs/go-ipfs-routing@5fb23b1e1d5acf72e3565b9455a95869824d3624 --- routing/dht/pb/dht.pb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/pb/dht.pb.go b/routing/dht/pb/dht.pb.go index 22c87bac9..6c488c51a 100644 --- a/routing/dht/pb/dht.pb.go +++ b/routing/dht/pb/dht.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package dht_pb -import proto "code.google.com/p/gogoprotobuf/proto" +import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. From 9492c619f7f1a3138a3120cf593df8dd733a342d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 30 Oct 2014 20:50:24 +0000 Subject: [PATCH 0364/3817] finish addressing PR concerns This commit was moved from ipfs/go-unixfs@e4475a7d95d367516ea20e6015d4088452345bc8 --- unixfs/io/dagreader.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 307f1d305..804e03438 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -35,12 +35,11 @@ func NewDagReader(n *mdag.Node, serv mdag.DAGService) (io.Reader, error) { // Dont allow reading directories return nil, ErrIsDir case ftpb.Data_File: - dr := &DagReader{ + return &DagReader{ node: n, serv: serv, buf: bytes.NewBuffer(pb.GetData()), - } - return dr, nil + }, nil case ftpb.Data_Raw: // Raw block will just be a single level, return a byte buffer return bytes.NewBuffer(pb.GetData()), nil From 31f84e1ff53e78776cec6b9bd86cc494fc8418d1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 26 Oct 2014 08:01:33 +0000 Subject: [PATCH 0365/3817] benchmark secure channel This commit was moved from ipfs/go-blockservice@a16833f9a6c71f1b83fdd97014894b106bc3612f --- blockservice/blockservice.go | 1 + 1 file changed, 1 insertion(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index edfaa11fa..3fe0465df 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -37,6 +37,7 @@ func NewBlockService(d ds.Datastore, rem exchange.Interface) (*BlockService, err // AddBlock adds a particular block to the service, Putting it into the datastore. func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { k := b.Key() + log.Debugf("blockservice: storing [%s] in datastore", k) // TODO(brian): define a block datastore with a Put method which accepts a // block parameter From b142d865bd62d445b8be96673d53d3629dd96363 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 31 Oct 2014 18:48:48 +0000 Subject: [PATCH 0366/3817] make FetchGraph waitable This commit was moved from ipfs/go-merkledag@be9e1d21e9de0f9b544bd9d25b4e5011db1ce668 --- ipld/merkledag/merkledag.go | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index d9221f0da..26431dea2 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -2,6 +2,7 @@ package merkledag import ( "fmt" + "sync" "time" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -242,9 +243,16 @@ func (n *dagService) Remove(nd *Node) error { return n.Blocks.DeleteBlock(k) } -func FetchGraph(ctx context.Context, root *Node, serv DAGService) { +func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} { + var wg sync.WaitGroup + done := make(chan struct{}) + for _, l := range root.Links { + wg.Add(1) go func(lnk *Link) { + + // Signal child is done on way out + defer wg.Done() select { case <-ctx.Done(): return @@ -255,7 +263,16 @@ func FetchGraph(ctx context.Context, root *Node, serv DAGService) { log.Error(err) return } - FetchGraph(ctx, nd, serv) + + // Wait for children to finish + <-FetchGraph(ctx, nd, serv) }(l) } + + go func() { + wg.Wait() + done <- struct{}{} + }() + + return done } From 3c86303dd9f55cd6a4990c2e47a6b83c5ae7d5e0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 28 Oct 2014 02:53:29 +0000 Subject: [PATCH 0367/3817] more memory tweaks This commit was moved from ipfs/go-ipfs-chunker@36ba109d3d2cb64a18dc15d258b2be237b016188 --- chunker/splitting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index f5a6f735c..8198999a8 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -8,7 +8,7 @@ import ( var log = util.Logger("chunk") -var DefaultSplitter = &SizeSplitter{Size: 1024 * 512} +var DefaultSplitter = &SizeSplitter{Size: 1024 * 256} type BlockSplitter interface { Split(r io.Reader) chan []byte From a91b8857313b00d7ff911364420cc08567969ff0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 3 Nov 2014 03:02:56 +0000 Subject: [PATCH 0368/3817] comment comment comment comment This commit was moved from ipfs/go-ipfs-routing@6eabeba85888a8b537ffcd98e7fd9f55500084f1 --- routing/dht/dht.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index e76ab571c..feff52706 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -228,6 +228,8 @@ func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p peer.Peer, return nil } +// putProvider sends a message to peer 'p' saying that the local node +// can provide the value of 'key' func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.Peer, key string) error { pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, string(key), 0) @@ -384,6 +386,7 @@ func (dht *IpfsDHT) FindLocal(id peer.ID) (peer.Peer, *kb.RoutingTable) { return nil, nil } +// findPeerSingle asks peer 'p' if they know where the peer with id 'id' is func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.Peer, id peer.ID, level int) (*pb.Message, error) { pmes := pb.NewMessage(pb.Message_FIND_NODE, string(id), level) return dht.sendRequest(ctx, p, pmes) @@ -457,6 +460,7 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, count int) []peer.Peer return filtered } +// getPeer searches the peerstore for a peer with the given peer ID func (dht *IpfsDHT) getPeer(id peer.ID) (peer.Peer, error) { p, err := dht.peerstore.Get(id) if err != nil { @@ -467,6 +471,8 @@ func (dht *IpfsDHT) getPeer(id peer.ID) (peer.Peer, error) { return p, nil } +// peerFromInfo returns a peer using info in the protobuf peer struct +// to lookup or create a peer func (dht *IpfsDHT) peerFromInfo(pbp *pb.Message_Peer) (peer.Peer, error) { id := peer.ID(pbp.GetId()) From 114f52ce4a8ca41a70206eb265fcc21059367d49 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 3 Nov 2014 03:02:56 +0000 Subject: [PATCH 0369/3817] comment comment comment comment This commit was moved from ipfs/go-unixfs@e4b1b49c22d0eaf7611823dc4c942f04b73c085d --- unixfs/io/dagreader.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 804e03438..ea33c3540 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -48,7 +48,7 @@ func NewDagReader(n *mdag.Node, serv mdag.DAGService) (io.Reader, error) { } } -// Follows the next link in line and loads it from the DAGService, +// precalcNextBuf follows the next link in line and loads it from the DAGService, // setting the next buffer to read from func (dr *DagReader) precalcNextBuf() error { if dr.position >= len(dr.node.Links) { @@ -67,6 +67,7 @@ func (dr *DagReader) precalcNextBuf() error { switch pb.GetType() { case ftpb.Data_Directory: + // A directory should not exist within a file return ft.ErrInvalidDirLocation case ftpb.Data_File: //TODO: this *should* work, needs testing first @@ -85,6 +86,7 @@ func (dr *DagReader) precalcNextBuf() error { } } +// Read reads data from the DAG structured file func (dr *DagReader) Read(b []byte) (int, error) { // If no cached buffer, load one if dr.buf == nil { From d8f430b955a8fb1c7fad7ec264988b182d030437 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 3 Nov 2014 03:02:56 +0000 Subject: [PATCH 0370/3817] comment comment comment comment This commit was moved from ipfs/go-blockservice@52576453352291d28d89db5dcbc7a0be49cf192e --- blockservice/blockservice.go | 1 + 1 file changed, 1 insertion(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 3fe0465df..50dc43b64 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -93,6 +93,7 @@ func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, er } } +// DeleteBlock deletes a block in the blockservice from the datastore func (s *BlockService) DeleteBlock(k u.Key) error { return s.Datastore.Delete(k.DsKey()) } From 6a1335d87817108cf6881310e7d8f82a7a77408f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 3 Nov 2014 03:53:16 +0000 Subject: [PATCH 0371/3817] a few more comments This commit was moved from ipfs/go-ipfs-pinner@136c7da2982e537b6cade0e942889d311d323ec4 --- pinning/pinner/pin.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index dba14a977..cdd40c450 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -1,9 +1,6 @@ package pin import ( - - //ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" - "encoding/json" "errors" "sync" @@ -36,12 +33,14 @@ type Pinner interface { } // ManualPinner is for manually editing the pin structure -// Use with care +// Use with care! If used improperly, garbage collection +// may not be successful type ManualPinner interface { PinWithMode(util.Key, PinMode) Pinner } +// pinner implements the Pinner interface type pinner struct { lock sync.RWMutex recursePin set.BlockSet @@ -51,6 +50,7 @@ type pinner struct { dstore ds.Datastore } +// NewPinner creates a new pinner using the given datastore as a backend func NewPinner(dstore ds.Datastore, serv mdag.DAGService) Pinner { // Load set from given datastore... @@ -70,6 +70,7 @@ func NewPinner(dstore ds.Datastore, serv mdag.DAGService) Pinner { } } +// Pin the given node, optionally recursive func (p *pinner) Pin(node *mdag.Node, recurse bool) error { p.lock.Lock() defer p.lock.Unlock() @@ -95,6 +96,7 @@ func (p *pinner) Pin(node *mdag.Node, recurse bool) error { return nil } +// Unpin a given key with optional recursive unpinning func (p *pinner) Unpin(k util.Key, recurse bool) error { p.lock.Lock() defer p.lock.Unlock() @@ -158,6 +160,7 @@ func (p *pinner) pinLinks(node *mdag.Node) error { return nil } +// IsPinned returns whether or not the given key is pinned func (p *pinner) IsPinned(key util.Key) bool { p.lock.RLock() defer p.lock.RUnlock() @@ -166,6 +169,7 @@ func (p *pinner) IsPinned(key util.Key) bool { p.indirPin.HasKey(key) } +// LoadPinner loads a pinner and its keysets from the given datastore func LoadPinner(d ds.Datastore, dserv mdag.DAGService) (Pinner, error) { p := new(pinner) @@ -200,6 +204,7 @@ func LoadPinner(d ds.Datastore, dserv mdag.DAGService) (Pinner, error) { return p, nil } +// Flush encodes and writes pinner keysets to the datastore func (p *pinner) Flush() error { p.lock.RLock() defer p.lock.RUnlock() @@ -244,6 +249,8 @@ func loadSet(d ds.Datastore, k ds.Key, val interface{}) error { return json.Unmarshal(bf, val) } +// PinWithMode is a method on ManualPinners, allowing the user to have fine +// grained control over pin counts func (p *pinner) PinWithMode(k util.Key, mode PinMode) { switch mode { case Recursive: From f030e5282eb3ee9c4360dc3d0bd31f9208410d6a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 3 Nov 2014 03:53:16 +0000 Subject: [PATCH 0372/3817] a few more comments This commit was moved from ipfs/go-merkledag@74e68092a47d750c307de8a020a04e513ae9572c --- ipld/merkledag/merkledag.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 26431dea2..79709392e 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -48,6 +48,7 @@ type Link struct { Node *Node } +// MakeLink creates a link to the given node func MakeLink(n *Node) (*Link, error) { s, err := n.Size() if err != nil { @@ -64,6 +65,7 @@ func MakeLink(n *Node) (*Link, error) { }, nil } +// GetNode returns the MDAG Node that this link points to func (l *Link) GetNode(serv DAGService) (*Node, error) { if l.Node != nil { return l.Node, nil @@ -98,6 +100,7 @@ func (n *Node) AddNodeLinkClean(name string, that *Node) error { return nil } +// Remove a link on this node by the given name func (n *Node) RemoveNodeLink(name string) error { for i, l := range n.Links { if l.Name == name { @@ -196,6 +199,7 @@ func (n *dagService) Add(nd *Node) (u.Key, error) { return n.Blocks.AddBlock(b) } +// AddRecursive adds the given node and all child nodes to the BlockService func (n *dagService) AddRecursive(nd *Node) error { _, err := n.Add(nd) if err != nil { @@ -230,6 +234,7 @@ func (n *dagService) Get(k u.Key) (*Node, error) { return Decoded(b.Data) } +// Remove deletes the given node and all of its children from the BlockService func (n *dagService) Remove(nd *Node) error { for _, l := range nd.Links { if l.Node != nil { @@ -243,6 +248,8 @@ func (n *dagService) Remove(nd *Node) error { return n.Blocks.DeleteBlock(k) } +// FetchGraph asynchronously fetches all nodes that are children of the given +// node, and returns a channel that may be waited upon for the fetch to complete func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} { var wg sync.WaitGroup done := make(chan struct{}) From 2b16571907445a62ba6c9c419858bdedd09d1f4f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 3 Nov 2014 03:53:16 +0000 Subject: [PATCH 0373/3817] a few more comments This commit was moved from ipfs/go-namesys@340d667b3eccdd5c329654a570e0a33e2ff007b1 --- namesys/publisher.go | 1 + 1 file changed, 1 insertion(+) diff --git a/namesys/publisher.go b/namesys/publisher.go index d95f1cbbc..f7bf508b6 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -26,6 +26,7 @@ func NewRoutingPublisher(route routing.IpfsRouting) Publisher { } // Publish implements Publisher. Accepts a keypair and a value, +// and publishes it out to the routing system func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { log.Debugf("namesys: Publish %s", value) From 552b01da76ddab1b0bc5ce661e9dfc1f89f56db7 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 5 Nov 2014 03:59:18 -0800 Subject: [PATCH 0374/3817] swarm + net: add explicit listen addresses This commit was moved from ipfs/go-ipfs-routing@5799dfcc9f1b98f16b6bf58f43b4d397fe223803 --- routing/dht/dht_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 2b8732338..3748e6519 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -24,7 +24,7 @@ func setupDHT(ctx context.Context, t *testing.T, p peer.Peer) *IpfsDHT { peerstore := peer.NewPeerstore() dhts := netservice.NewService(ctx, nil) // nil handler for now, need to patch it - net, err := inet.NewIpfsNetwork(ctx, p, peerstore, &mux.ProtocolMap{ + net, err := inet.NewIpfsNetwork(ctx, p.Addresses(), p, peerstore, &mux.ProtocolMap{ mux.ProtocolID_Routing: dhts, }) if err != nil { From 3de97ce96fbc367ae2deabe1b4d4dc52891d6d83 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 5 Nov 2014 08:49:55 -0800 Subject: [PATCH 0375/3817] fixed dht race #270 This commit was moved from ipfs/go-ipfs-routing@de6bd0cc750fd9c5ff14911fb34c06af0f90f587 --- routing/dht/dht_test.go | 4 ++-- routing/dht/ext_test.go | 19 +++++++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 3748e6519..ffbbef819 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -417,14 +417,14 @@ func TestConnectCollision(t *testing.T) { done := make(chan struct{}) go func() { - _, err = dhtA.Connect(ctx, peerB) + _, err := dhtA.Connect(ctx, peerB) if err != nil { t.Fatal(err) } done <- struct{}{} }() go func() { - _, err = dhtB.Connect(ctx, peerA) + _, err := dhtB.Connect(ctx, peerA) if err != nil { t.Fatal(err) } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 19275338d..a6d1d933d 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -16,6 +16,7 @@ import ( pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" + "sync" "time" ) @@ -28,15 +29,24 @@ type mesHandleFunc func(msg.NetMessage) msg.NetMessage // fauxNet is a standin for a swarm.Network in order to more easily recreate // different testing scenarios type fauxSender struct { + sync.Mutex handlers []mesHandleFunc } func (f *fauxSender) AddHandler(fn func(msg.NetMessage) msg.NetMessage) { + f.Lock() + defer f.Unlock() + f.handlers = append(f.handlers, fn) } func (f *fauxSender) SendRequest(ctx context.Context, m msg.NetMessage) (msg.NetMessage, error) { - for _, h := range f.handlers { + f.Lock() + handlers := make([]mesHandleFunc, len(f.handlers)) + copy(handlers, f.handlers) + f.Unlock() + + for _, h := range handlers { reply := h(m) if reply != nil { return reply, nil @@ -52,7 +62,12 @@ func (f *fauxSender) SendRequest(ctx context.Context, m msg.NetMessage) (msg.Net } func (f *fauxSender) SendMessage(ctx context.Context, m msg.NetMessage) error { - for _, h := range f.handlers { + f.Lock() + handlers := make([]mesHandleFunc, len(f.handlers)) + copy(handlers, f.handlers) + f.Unlock() + + for _, h := range handlers { reply := h(m) if reply != nil { return nil From 18ad1dcbcf38da545734a88f1b3b318de992dc60 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 5 Nov 2014 04:26:30 -0800 Subject: [PATCH 0376/3817] fix(net) pass contexts to dial peer This commit was moved from ipfs/go-ipfs-routing@9253f2d03f7349a1a3359a92886b407250d009f6 --- routing/dht/dht.go | 10 +++++----- routing/dht/ext_test.go | 3 +-- routing/dht/query.go | 2 +- routing/dht/routing.go | 6 +++--- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index feff52706..11764faba 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -100,7 +100,7 @@ func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.Peer) (peer.Peer, er // // /ip4/10.20.30.40/tcp/1234/ipfs/Qxhxxchxzcncxnzcnxzcxzm // - err := dht.dialer.DialPeer(npeer) + err := dht.dialer.DialPeer(ctx, npeer) if err != nil { return nil, err } @@ -311,7 +311,7 @@ func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key, peerlist []*pb.Message_Peer, level int) ([]byte, error) { for _, pinfo := range peerlist { - p, err := dht.ensureConnectedToPeer(pinfo) + p, err := dht.ensureConnectedToPeer(ctx, pinfo) if err != nil { log.Errorf("getFromPeers error: %s", err) continue @@ -496,14 +496,14 @@ func (dht *IpfsDHT) peerFromInfo(pbp *pb.Message_Peer) (peer.Peer, error) { return p, nil } -func (dht *IpfsDHT) ensureConnectedToPeer(pbp *pb.Message_Peer) (peer.Peer, error) { +func (dht *IpfsDHT) ensureConnectedToPeer(ctx context.Context, pbp *pb.Message_Peer) (peer.Peer, error) { p, err := dht.peerFromInfo(pbp) if err != nil { return nil, err } // dial connection - err = dht.dialer.DialPeer(p) + err = dht.dialer.DialPeer(ctx, p) return p, err } @@ -556,7 +556,7 @@ func (dht *IpfsDHT) Bootstrap(ctx context.Context) { if err != nil { log.Error("Bootstrap peer error: %s", err) } - err = dht.dialer.DialPeer(p) + err = dht.dialer.DialPeer(ctx, p) if err != nil { log.Errorf("Bootstrap peer error: %s", err) } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index a6d1d933d..1dabb5b64 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -4,7 +4,6 @@ import ( "testing" crand "crypto/rand" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" @@ -82,7 +81,7 @@ type fauxNet struct { } // DialPeer attempts to establish a connection to a given peer -func (f *fauxNet) DialPeer(peer.Peer) error { +func (f *fauxNet) DialPeer(context.Context, peer.Peer) error { return nil } diff --git a/routing/dht/query.go b/routing/dht/query.go index 557af095c..f0478ff29 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -230,7 +230,7 @@ func (r *dhtQueryRunner) queryPeer(p peer.Peer) { // make sure we're connected to the peer. // (Incidentally, this will add it to the peerstore too) - err := r.query.dialer.DialPeer(p) + err := r.query.dialer.DialPeer(r.ctx, p) if err != nil { log.Debugf("ERROR worker for: %v -- err connecting: %v", p, err) r.Lock() diff --git a/routing/dht/routing.go b/routing/dht/routing.go index da400fee2..c0d86e1d1 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -145,7 +145,7 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int log.Error(err) return } - dht.addPeerListAsync(key, pmes.GetProviderPeers(), ps, count, peerOut) + dht.addPeerListAsync(ctx, key, pmes.GetProviderPeers(), ps, count, peerOut) }(pp) } wg.Wait() @@ -154,13 +154,13 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int return peerOut } -func (dht *IpfsDHT) addPeerListAsync(k u.Key, peers []*pb.Message_Peer, ps *peerSet, count int, out chan peer.Peer) { +func (dht *IpfsDHT) addPeerListAsync(ctx context.Context, k u.Key, peers []*pb.Message_Peer, ps *peerSet, count int, out chan peer.Peer) { done := make(chan struct{}) for _, pbp := range peers { go func(mp *pb.Message_Peer) { defer func() { done <- struct{}{} }() // construct new peer - p, err := dht.ensureConnectedToPeer(mp) + p, err := dht.ensureConnectedToPeer(ctx, mp) if err != nil { log.Error("%s", err) return From 4bbdd5176e263a79cfc725aa6c04230c6d9d81da Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 7 Nov 2014 11:35:50 -0800 Subject: [PATCH 0377/3817] probably fix OSX ipns bug This commit was moved from ipfs/go-unixfs@1424de80e85f792ffbea42b79e51203d7738bc47 --- unixfs/io/dagmodifier.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/unixfs/io/dagmodifier.go b/unixfs/io/dagmodifier.go index a05b9d6ed..4683a157e 100644 --- a/unixfs/io/dagmodifier.go +++ b/unixfs/io/dagmodifier.go @@ -173,6 +173,13 @@ func (dm *DagModifier) WriteAt(b []byte, offset uint64) (int, error) { return origlen, nil } +func (dm *DagModifier) Size() uint64 { + if dm == nil { + return 0 + } + return dm.pbdata.GetFilesize() +} + // splitBytes uses a splitterFunc to turn a large array of bytes // into many smaller arrays of bytes func splitBytes(b []byte, spl chunk.BlockSplitter) [][]byte { From 98ccdcf345ecfcb744a4b85e76ad919e586c1da1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Nov 2014 13:43:43 -0800 Subject: [PATCH 0378/3817] write a few package doc strings to improve look of godoc This commit was moved from ipfs/go-ipfs-routing@d705f87a4c7f506dee9c6b3137bfc1f3dd29273a --- routing/dht/dht.go | 2 ++ routing/kbucket/bucket.go | 2 +- routing/kbucket/table.go | 3 ++- routing/kbucket/table_test.go | 2 +- routing/kbucket/util.go | 2 +- routing/routing.go | 1 + 6 files changed, 8 insertions(+), 4 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 11764faba..5f6184067 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -1,3 +1,5 @@ +// package dht implements a distributed hash table that satisfies the ipfs routing +// interface. This DHT is modeled after kademlia with Coral and S/Kademlia modifications. package dht import ( diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index b114f9e21..51f524971 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -1,4 +1,4 @@ -package dht +package kbucket import ( "container/list" diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 491a06c68..c144c191e 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -1,4 +1,5 @@ -package dht +// package kbucket implements a kademlia 'k-bucket' routing table. +package kbucket import ( "container/list" diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index cda69064a..85fc387e2 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -1,4 +1,4 @@ -package dht +package kbucket import ( crand "crypto/rand" diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 02994230a..4adac0405 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -1,4 +1,4 @@ -package dht +package kbucket import ( "bytes" diff --git a/routing/routing.go b/routing/routing.go index 3ef381856..09773f20b 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -1,3 +1,4 @@ +// package routing defines the interface for a routing system used by ipfs. package routing import ( From 9f8c6c40454ec6de83d431e01099365e7099436b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Nov 2014 13:43:43 -0800 Subject: [PATCH 0379/3817] write a few package doc strings to improve look of godoc This commit was moved from ipfs/go-ipfs-chunker@a897f36c1187bcaa79e2f212c89d4d6a22f77b1a --- chunker/splitting.go | 1 + 1 file changed, 1 insertion(+) diff --git a/chunker/splitting.go b/chunker/splitting.go index 8198999a8..2d92a9ead 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -1,3 +1,4 @@ +// package chunk implements streaming block splitters package chunk import ( From d807cb4ae4802e9840af3cce32986b73a215dbc0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Nov 2014 16:02:03 -0800 Subject: [PATCH 0380/3817] some more docs This commit was moved from ipfs/go-ipfs-pinner@bc47bb096f037db55e54c4300ff9bb062b09d44f --- pinning/pinner/pin.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index cdd40c450..c59574eda 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -1,3 +1,5 @@ +// package pin implemnts structures and methods to keep track of +// which objects a user wants to keep stored locally. package pin import ( From a5083ae33bd816ff5481411706195f74d3d1d749 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Nov 2014 13:43:43 -0800 Subject: [PATCH 0381/3817] write a few package doc strings to improve look of godoc This commit was moved from ipfs/go-blockservice@ad96295ee7206a3899a3e468b828f53635fbc742 --- blockservice/blockservice.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 50dc43b64..82a1b03c1 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -1,3 +1,6 @@ +// package blockservice implements a BlockService interface that provides +// a single GetBlock/AddBlock interface that seamlessly retrieves data either +// locally or from a remote peer through the exchange. package blockservice import ( From 9b72371a564a4e5605d0280b398e509e3e0377eb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Nov 2014 13:43:43 -0800 Subject: [PATCH 0382/3817] write a few package doc strings to improve look of godoc This commit was moved from ipfs/go-merkledag@642e04c7d0b7ee7d20a866246cd0351256cd6265 --- ipld/merkledag/merkledag.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 79709392e..1874e5304 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -1,3 +1,4 @@ +// package merkledag implements the ipfs Merkle DAG datastructures. package merkledag import ( From 6d8b89ba06b55c9a09bf4aff12d29a0b0123ae50 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Nov 2014 16:02:03 -0800 Subject: [PATCH 0383/3817] some more docs This commit was moved from ipfs/go-unixfs@eb2f560e4ee5f53c208d66803b1293d85570b8cb --- unixfs/io/doc.go | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 unixfs/io/doc.go diff --git a/unixfs/io/doc.go b/unixfs/io/doc.go new file mode 100644 index 000000000..b28755fc6 --- /dev/null +++ b/unixfs/io/doc.go @@ -0,0 +1,3 @@ +// package unixfs/io implements convenience objects for working with the ipfs +// unixfs data format. +package io From a37ed62c48c8f2771d6af54758c31c0b006a0b79 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Nov 2014 13:43:43 -0800 Subject: [PATCH 0384/3817] write a few package doc strings to improve look of godoc This commit was moved from ipfs/go-path@7fd00e383c33a46dc2efc729ceb4968cf77ec166 --- path/path.go | 1 + 1 file changed, 1 insertion(+) diff --git a/path/path.go b/path/path.go index 8d3070a0e..63d718656 100644 --- a/path/path.go +++ b/path/path.go @@ -1,3 +1,4 @@ +// package path implements utilities for resolving paths within ipfs. package path import ( From b8915a253bed58f5cbd3cc956d7981cbe3be7e11 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 8 Nov 2014 21:37:56 -0800 Subject: [PATCH 0385/3817] docs(exchange) This commit was moved from ipfs/go-ipfs-exchange-interface@f327e9b199779ec5cf795a43159ce499f0a64235 --- exchange/interface.go | 1 + 1 file changed, 1 insertion(+) diff --git a/exchange/interface.go b/exchange/interface.go index 682c98348..82782a046 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -1,3 +1,4 @@ +// package exchange defines the IPFS Exchange interface package exchange import ( From cb9466ef1fb3c837b05ea6a52ef377c4255e6f52 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Nov 2014 21:57:11 -0800 Subject: [PATCH 0386/3817] comments on vars in dht This commit was moved from ipfs/go-ipfs-routing@969f6af2660de98afe04731feb38d842be6bb5cc --- routing/dht/util.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/util.go b/routing/dht/util.go index 3cc812638..00ac38dbc 100644 --- a/routing/dht/util.go +++ b/routing/dht/util.go @@ -9,10 +9,10 @@ import ( // Pool size is the number of nodes used for group find/set RPC calls var PoolSize = 6 -// We put the 'K' in kademlia! +// K is the maximum number of requests to perform before returning failure. var KValue = 10 -// Its in the paper, i swear +// Alpha is the concurrency factor for asynchronous requests. var AlphaValue = 3 // A counter for incrementing a variable across multiple threads From 97d217d388a0398da089ed8cbdedfbaeef8ad979 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Nov 2014 22:44:37 -0800 Subject: [PATCH 0387/3817] more doc comments This commit was moved from ipfs/go-ipfs-routing@588abf9228573adff1bb6c97e7e60dd8b3a1874a --- routing/dht/handlers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index fe628eeef..f7b074416 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -12,6 +12,7 @@ import ( ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ) +// The number of closer peers to send on requests. var CloserPeerCount = 4 // dhthandler specifies the signature of functions that handle DHT messages. From 8d2387e01794df22844b347c376e49ef9fff8775 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Nov 2014 22:44:37 -0800 Subject: [PATCH 0388/3817] more doc comments This commit was moved from ipfs/go-ipfs-exchange-offline@7715d6378cdb18e6355a16ac20c83fe8e884895a --- exchange/offline/offline.go | 4 +++- exchange/offline/offline_test.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 2a7527f56..5f7ef8835 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -1,4 +1,6 @@ -package bitswap +// package offline implements an object that implements the exchange +// interface but returns nil values to every request. +package offline import ( "errors" diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index b759a61ca..cc3f3ec82 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -1,4 +1,4 @@ -package bitswap +package offline import ( "testing" From f7fa3c78404069a39e49e60f7cc34c6c094e2a16 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 12 Nov 2014 10:39:11 -0800 Subject: [PATCH 0389/3817] log -> logf This commit was moved from ipfs/go-ipfs-routing@35b6bce7e799a9b7cf9335aa024f6f838fd6ce08 --- routing/dht/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index c0d86e1d1..e2e5d2f37 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -162,7 +162,7 @@ func (dht *IpfsDHT) addPeerListAsync(ctx context.Context, k u.Key, peers []*pb.M // construct new peer p, err := dht.ensureConnectedToPeer(ctx, mp) if err != nil { - log.Error("%s", err) + log.Errorf("%s", err) return } if p == nil { From 1b8329b4469b206a6221dca39d40349f773e49c4 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 15 Nov 2014 00:19:47 -0800 Subject: [PATCH 0390/3817] chore(tests) add Short() -> SkipNow() to slowest tests vanilla: 21.57 real 45.14 user 8.51 sys short: 14.40 real 31.13 user 5.56 sys License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@e873abb078c9e5957863bd9d2e252d64443d822c --- routing/dht/dht_test.go | 16 ++++++++++++---- routing/dht/ext_test.go | 8 ++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index ffbbef819..133e28b58 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -231,7 +231,9 @@ func TestProvides(t *testing.T) { } func TestProvidesAsync(t *testing.T) { - // t.Skip("skipping test to debug another") + if testing.Short() { + t.SkipNow() + } ctx := context.Background() u.Debug = false @@ -295,7 +297,9 @@ func TestProvidesAsync(t *testing.T) { } func TestLayeredGet(t *testing.T) { - // t.Skip("skipping test to debug another") + if testing.Short() { + t.SkipNow() + } ctx := context.Background() u.Debug = false @@ -347,7 +351,9 @@ func TestLayeredGet(t *testing.T) { } func TestFindPeer(t *testing.T) { - // t.Skip("skipping test to debug another") + if testing.Short() { + t.SkipNow() + } ctx := context.Background() u.Debug = false @@ -391,7 +397,9 @@ func TestFindPeer(t *testing.T) { } func TestConnectCollision(t *testing.T) { - // t.Skip("skipping test to debug another") + if testing.Short() { + t.SkipNow() + } runTimes := 10 diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 1dabb5b64..6be939bed 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -115,7 +115,9 @@ func (f *fauxNet) GetBandwidthTotals() (uint64, uint64) { func (f *fauxNet) Close() error { return nil } func TestGetFailures(t *testing.T) { - // t.Skip("skipping test because it makes a lot of output") + if testing.Short() { + t.SkipNow() + } ctx := context.Background() fn := &fauxNet{} @@ -211,7 +213,9 @@ func _randPeer() peer.Peer { } func TestNotFound(t *testing.T) { - // t.Skip("skipping test because it makes a lot of output") + if testing.Short() { + t.SkipNow() + } ctx := context.Background() fn := &fauxNet{} From 54c04b17317677456b1f3e2e94ffce0087ffc81e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 15 Nov 2014 00:19:47 -0800 Subject: [PATCH 0391/3817] chore(tests) add Short() -> SkipNow() to slowest tests vanilla: 21.57 real 45.14 user 8.51 sys short: 14.40 real 31.13 user 5.56 sys License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-chunker@6f21c1f5f5ed9b5eb2b04906e42367a8786b0394 --- chunker/splitting_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index 0ecb143cb..612da4d09 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -21,6 +21,9 @@ func copyBuf(buf []byte) []byte { } func TestSizeSplitterIsDeterministic(t *testing.T) { + if testing.Short() { + t.SkipNow() + } test := func() { bufR := randBuf(t, 10000000) // crank this up to satisfy yourself. From 5d330d7ce0e6864454d600bdf030ba742ca566b2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 4 Nov 2014 10:55:51 -0800 Subject: [PATCH 0392/3817] add in a default file hash and cleaned up init functiona bit This commit was moved from ipfs/go-ipfs-pinner@c68e81cdf1f371ab7c7e029c004b4d51c5c9adf0 --- pinning/pinner/pin.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index c59574eda..60828b597 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -32,6 +32,7 @@ type Pinner interface { Pin(*mdag.Node, bool) error Unpin(util.Key, bool) error Flush() error + GetManual() ManualPinner } // ManualPinner is for manually editing the pin structure @@ -263,3 +264,7 @@ func (p *pinner) PinWithMode(k util.Key, mode PinMode) { p.indirPin.Increment(k) } } + +func (p *pinner) GetManual() ManualPinner { + return p +} From 395e8023ee0437440955e6019e6b7a61e50e1da7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 9 Nov 2014 23:45:16 -0800 Subject: [PATCH 0393/3817] switch DHT entries over to be records, test currently fail This commit was moved from ipfs/go-ipfs-routing@60f1004327d352a79e40513ff30a71304be34770 --- routing/dht/dht.go | 57 +++++++++++++++++++++++++++------ routing/dht/ext_test.go | 22 +++++++------ routing/dht/handlers.go | 27 ++++++++++++++-- routing/dht/pb/dht.pb.go | 55 +++++++++++++++++++++++++++++--- routing/dht/pb/dht.proto | 18 ++++++++++- routing/dht/records.go | 69 ++++++++++++++++++++++++++++++++++++++++ routing/dht/routing.go | 8 ++++- 7 files changed, 228 insertions(+), 28 deletions(-) create mode 100644 routing/dht/records.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 5f6184067..5d4caaa84 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -60,6 +60,9 @@ type IpfsDHT struct { //lock to make diagnostics work better diaglock sync.Mutex + // record validator funcs + Validators map[string]ValidatorFunc + ctxc.ContextCloser } @@ -81,6 +84,7 @@ func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, dialer inet.Dia dht.routingTables[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID()), time.Millisecond*1000) dht.routingTables[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID()), time.Hour) dht.birth = time.Now() + dht.Validators = make(map[string]ValidatorFunc) if doPinging { dht.Children().Add(1) @@ -215,16 +219,16 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *pb.Messa // putValueToNetwork stores the given key/value pair at the peer 'p' func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p peer.Peer, - key string, value []byte) error { + key string, rec *pb.Record) error { pmes := pb.NewMessage(pb.Message_PUT_VALUE, string(key), 0) - pmes.Value = value + pmes.Record = rec rpmes, err := dht.sendRequest(ctx, p, pmes) if err != nil { return err } - if !bytes.Equal(rpmes.Value, pmes.Value) { + if !bytes.Equal(rpmes.GetRecord().Value, pmes.GetRecord().Value) { return errors.New("value not put correctly") } return nil @@ -260,11 +264,16 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, return nil, nil, err } - log.Debugf("pmes.GetValue() %v", pmes.GetValue()) - if value := pmes.GetValue(); value != nil { + if record := pmes.GetRecord(); record != nil { // Success! We were given the value log.Debug("getValueOrPeers: got value") - return value, nil, nil + + // make sure record is still valid + err = dht.verifyRecord(record) + if err != nil { + return nil, nil, err + } + return record.GetValue(), nil, nil } // TODO decide on providers. This probably shouldn't be happening. @@ -325,10 +334,15 @@ func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key, continue } - if value := pmes.GetValue(); value != nil { + if record := pmes.GetRecord(); record != nil { // Success! We were given the value + + err := dht.verifyRecord(record) + if err != nil { + return nil, err + } dht.providers.AddProvider(key, p) - return value, nil + return record.GetValue(), nil } } return nil, routing.ErrNotFound @@ -347,12 +361,35 @@ func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { if !ok { return nil, errors.New("value stored in datastore not []byte") } - return byt, nil + rec := new(pb.Record) + err = proto.Unmarshal(byt, rec) + if err != nil { + return nil, err + } + + // TODO: 'if paranoid' + if u.Debug { + err = dht.verifyRecord(rec) + if err != nil { + return nil, err + } + } + + return rec.GetValue(), nil } // putLocal stores the key value pair in the datastore func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { - return dht.datastore.Put(key.DsKey(), value) + rec, err := dht.makePutRecord(key, value) + if err != nil { + return err + } + data, err := proto.Marshal(rec) + if err != nil { + return err + } + + return dht.datastore.Put(key.DsKey(), data) } // Update signals to all routingTables to Update their last-seen status diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 6be939bed..dcf80e4d0 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -124,10 +124,10 @@ func TestGetFailures(t *testing.T) { fs := &fauxSender{} peerstore := peer.NewPeerstore() - local := peer.WithIDString("test_peer") + local := makePeer(nil) d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) - other := peer.WithIDString("other_peer") + other := makePeer(nil) d.Update(other) // This one should time out @@ -173,10 +173,14 @@ func TestGetFailures(t *testing.T) { // Now we test this DHT's handleGetValue failure typ := pb.Message_GET_VALUE str := "hello" + rec, err := d.makePutRecord(u.Key(str), []byte("blah")) + if err != nil { + t.Fatal(err) + } req := pb.Message{ - Type: &typ, - Key: &str, - Value: []byte{0}, + Type: &typ, + Key: &str, + Record: rec, } // u.POut("handleGetValue Test\n") @@ -192,10 +196,10 @@ func TestGetFailures(t *testing.T) { if err != nil { t.Fatal(err) } - if pmes.GetValue() != nil { + if pmes.GetRecord() != nil { t.Fatal("shouldnt have value") } - if pmes.GetCloserPeers() != nil { + if len(pmes.GetCloserPeers()) > 0 { t.Fatal("shouldnt have closer peers") } if pmes.GetProviderPeers() != nil { @@ -221,7 +225,7 @@ func TestNotFound(t *testing.T) { fn := &fauxNet{} fs := &fauxSender{} - local := peer.WithIDString("test_peer") + local := makePeer(nil) peerstore := peer.NewPeerstore() peerstore.Add(local) @@ -287,7 +291,7 @@ func TestLessThanKResponses(t *testing.T) { u.Debug = false fn := &fauxNet{} fs := &fauxSender{} - local := peer.WithIDString("test_peer") + local := makePeer(nil) peerstore := peer.NewPeerstore() peerstore.Add(local) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index f7b074416..899f24292 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -5,6 +5,8 @@ import ( "fmt" "time" + "code.google.com/p/goprotobuf/proto" + peer "github.com/jbenet/go-ipfs/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" @@ -72,7 +74,14 @@ func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *pb.Message) (*pb.Message, return nil, fmt.Errorf("datastore had non byte-slice value for %v", dskey) } - resp.Value = byts + rec := new(pb.Record) + err := proto.Unmarshal(byts, rec) + if err != nil { + log.Error("Failed to unmarshal dht record from datastore") + return nil, err + } + + resp.Record = rec } // if we know any providers for the requested value, return those. @@ -102,8 +111,20 @@ func (dht *IpfsDHT) handlePutValue(p peer.Peer, pmes *pb.Message) (*pb.Message, dht.dslock.Lock() defer dht.dslock.Unlock() dskey := u.Key(pmes.GetKey()).DsKey() - err := dht.datastore.Put(dskey, pmes.GetValue()) - log.Debugf("%s handlePutValue %v %v\n", dht.self, dskey, pmes.GetValue()) + + err := dht.verifyRecord(pmes.GetRecord()) + if err != nil { + log.Error("Bad dht record in put request") + return nil, err + } + + data, err := proto.Marshal(pmes.GetRecord()) + if err != nil { + return nil, err + } + + err = dht.datastore.Put(dskey, data) + log.Debugf("%s handlePutValue %v\n", dht.self, dskey) return pmes, err } diff --git a/routing/dht/pb/dht.pb.go b/routing/dht/pb/dht.pb.go index 6c488c51a..fd7620627 100644 --- a/routing/dht/pb/dht.pb.go +++ b/routing/dht/pb/dht.pb.go @@ -10,10 +10,11 @@ It is generated from these files: It has these top-level messages: Message + Record */ package dht_pb -import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import proto "code.google.com/p/gogoprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. @@ -75,7 +76,7 @@ type Message struct { Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` // Used to return a value // PUT_VALUE, GET_VALUE - Value []byte `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` + Record *Record `protobuf:"bytes,3,opt,name=record" json:"record,omitempty"` // Used to return peers closer to a key in a query // GET_VALUE, GET_PROVIDERS, FIND_NODE CloserPeers []*Message_Peer `protobuf:"bytes,8,rep,name=closerPeers" json:"closerPeers,omitempty"` @@ -110,9 +111,9 @@ func (m *Message) GetKey() string { return "" } -func (m *Message) GetValue() []byte { +func (m *Message) GetRecord() *Record { if m != nil { - return m.Value + return m.Record } return nil } @@ -155,6 +156,52 @@ func (m *Message_Peer) GetAddr() string { return "" } +// Record represents a dht record that contains a value +// for a key value pair +type Record struct { + // The key that references this record + Key *string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` + // The actual value this record is storing + Value []byte `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` + // hash of the authors public key + Author *string `protobuf:"bytes,3,opt,name=author" json:"author,omitempty"` + // A PKI signature for the key+value+author + Signature []byte `protobuf:"bytes,4,opt,name=signature" json:"signature,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Record) Reset() { *m = Record{} } +func (m *Record) String() string { return proto.CompactTextString(m) } +func (*Record) ProtoMessage() {} + +func (m *Record) GetKey() string { + if m != nil && m.Key != nil { + return *m.Key + } + return "" +} + +func (m *Record) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func (m *Record) GetAuthor() string { + if m != nil && m.Author != nil { + return *m.Author + } + return "" +} + +func (m *Record) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + func init() { proto.RegisterEnum("dht.pb.Message_MessageType", Message_MessageType_name, Message_MessageType_value) } diff --git a/routing/dht/pb/dht.proto b/routing/dht/pb/dht.proto index e0696e685..1b49a1552 100644 --- a/routing/dht/pb/dht.proto +++ b/routing/dht/pb/dht.proto @@ -29,7 +29,7 @@ message Message { // Used to return a value // PUT_VALUE, GET_VALUE - optional bytes value = 3; + optional Record record = 3; // Used to return peers closer to a key in a query // GET_VALUE, GET_PROVIDERS, FIND_NODE @@ -39,3 +39,19 @@ message Message { // GET_VALUE, ADD_PROVIDER, GET_PROVIDERS repeated Peer providerPeers = 9; } + +// Record represents a dht record that contains a value +// for a key value pair +message Record { + // The key that references this record + optional string key = 1; + + // The actual value this record is storing + optional bytes value = 2; + + // hash of the authors public key + optional string author = 3; + + // A PKI signature for the key+value+author + optional bytes signature = 4; +} diff --git a/routing/dht/records.go b/routing/dht/records.go new file mode 100644 index 000000000..e88b18e7b --- /dev/null +++ b/routing/dht/records.go @@ -0,0 +1,69 @@ +package dht + +import ( + "bytes" + "errors" + "strings" + + "code.google.com/p/goprotobuf/proto" + "github.com/jbenet/go-ipfs/peer" + pb "github.com/jbenet/go-ipfs/routing/dht/pb" + u "github.com/jbenet/go-ipfs/util" +) + +type ValidatorFunc func(u.Key, []byte) error + +var ErrBadRecord = errors.New("bad dht record") +var ErrInvalidRecordType = errors.New("invalid record keytype") + +// creates and signs a dht record for the given key/value pair +func (dht *IpfsDHT) makePutRecord(key u.Key, value []byte) (*pb.Record, error) { + record := new(pb.Record) + + record.Key = proto.String(key.String()) + record.Value = value + record.Author = proto.String(string(dht.self.ID())) + blob := bytes.Join([][]byte{[]byte(key), value, []byte(dht.self.ID())}, []byte{}) + sig, err := dht.self.PrivKey().Sign(blob) + if err != nil { + return nil, err + } + record.Signature = sig + return record, nil +} + +func (dht *IpfsDHT) verifyRecord(r *pb.Record) error { + // First, validate the signature + p, err := dht.peerstore.Get(peer.ID(r.GetAuthor())) + if err != nil { + return err + } + + blob := bytes.Join([][]byte{[]byte(r.GetKey()), + r.GetValue(), + []byte(r.GetKey())}, []byte{}) + + ok, err := p.PubKey().Verify(blob, r.GetSignature()) + if err != nil { + return err + } + + if !ok { + return ErrBadRecord + } + + // Now, check validity func + parts := strings.Split(r.GetKey(), "/") + if len(parts) < 2 { + log.Error("Record had bad key: %s", r.GetKey()) + return ErrBadRecord + } + + fnc, ok := dht.Validators[parts[0]] + if !ok { + log.Errorf("Unrecognized key prefix: %s", parts[0]) + return ErrInvalidRecordType + } + + return fnc(u.Key(r.GetKey()), r.GetValue()) +} diff --git a/routing/dht/routing.go b/routing/dht/routing.go index e2e5d2f37..fedf281d3 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -25,6 +25,12 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error return err } + rec, err := dht.makePutRecord(key, value) + if err != nil { + log.Error("Creation of record failed!") + return err + } + var peers []peer.Peer for _, route := range dht.routingTables { npeers := route.NearestPeers(kb.ConvertKey(key), KValue) @@ -33,7 +39,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error query := newQuery(key, dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { log.Debugf("%s PutValue qry part %v", dht.self, p) - err := dht.putValueToNetwork(ctx, p, string(key), value) + err := dht.putValueToNetwork(ctx, p, string(key), rec) if err != nil { return nil, err } From 00a9753e6d592154de04cddd68735bb4c32312fc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 10 Nov 2014 14:22:56 -0800 Subject: [PATCH 0394/3817] fix validators and key prefix This commit was moved from ipfs/go-ipfs-routing@08fbaadb0fdef9d2492fd434e7e79692e8f85e23 --- routing/dht/dht_test.go | 21 +++++++++++++++------ routing/dht/ext_test.go | 4 +--- routing/dht/handlers.go | 1 + routing/dht/records.go | 16 +++++++++------- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 133e28b58..e62145d5b 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -33,6 +33,9 @@ func setupDHT(ctx context.Context, t *testing.T, p peer.Peer) *IpfsDHT { d := NewDHT(ctx, p, peerstore, net, dhts, ds.NewMapDatastore()) dhts.SetHandler(d) + d.Validators["v"] = func(u.Key, []byte) error { + return nil + } return d } @@ -136,6 +139,12 @@ func TestValueGetSet(t *testing.T) { dhtA := setupDHT(ctx, t, peerA) dhtB := setupDHT(ctx, t, peerB) + vf := func(u.Key, []byte) error { + return nil + } + dhtA.Validators["v"] = vf + dhtB.Validators["v"] = vf + defer dhtA.Close() defer dhtB.Close() defer dhtA.dialer.(inet.Network).Close() @@ -147,10 +156,10 @@ func TestValueGetSet(t *testing.T) { } ctxT, _ := context.WithTimeout(ctx, time.Second) - dhtA.PutValue(ctxT, "hello", []byte("world")) + dhtA.PutValue(ctxT, "/v/hello", []byte("world")) ctxT, _ = context.WithTimeout(ctx, time.Second*2) - val, err := dhtA.GetValue(ctxT, "hello") + val, err := dhtA.GetValue(ctxT, "/v/hello") if err != nil { t.Fatal(err) } @@ -160,7 +169,7 @@ func TestValueGetSet(t *testing.T) { } ctxT, _ = context.WithTimeout(ctx, time.Second*2) - val, err = dhtB.GetValue(ctxT, "hello") + val, err = dhtB.GetValue(ctxT, "/v/hello") if err != nil { t.Fatal(err) } @@ -326,12 +335,12 @@ func TestLayeredGet(t *testing.T) { t.Fatal(err) } - err = dhts[3].putLocal(u.Key("hello"), []byte("world")) + err = dhts[3].putLocal(u.Key("/v/hello"), []byte("world")) if err != nil { t.Fatal(err) } - err = dhts[3].Provide(ctx, u.Key("hello")) + err = dhts[3].Provide(ctx, u.Key("/v/hello")) if err != nil { t.Fatal(err) } @@ -339,7 +348,7 @@ func TestLayeredGet(t *testing.T) { time.Sleep(time.Millisecond * 60) ctxT, _ := context.WithTimeout(ctx, time.Second) - val, err := dhts[0].GetValue(ctxT, u.Key("hello")) + val, err := dhts[0].GetValue(ctxT, u.Key("/v/hello")) if err != nil { t.Fatal(err) } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index dcf80e4d0..55a68ef9e 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -4,6 +4,7 @@ import ( "testing" crand "crypto/rand" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" @@ -199,9 +200,6 @@ func TestGetFailures(t *testing.T) { if pmes.GetRecord() != nil { t.Fatal("shouldnt have value") } - if len(pmes.GetCloserPeers()) > 0 { - t.Fatal("shouldnt have closer peers") - } if pmes.GetProviderPeers() != nil { t.Fatal("shouldnt have provider peers") } diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 899f24292..cdea759da 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -114,6 +114,7 @@ func (dht *IpfsDHT) handlePutValue(p peer.Peer, pmes *pb.Message) (*pb.Message, err := dht.verifyRecord(pmes.GetRecord()) if err != nil { + fmt.Println(u.Key(pmes.GetRecord().GetAuthor())) log.Error("Bad dht record in put request") return nil, err } diff --git a/routing/dht/records.go b/routing/dht/records.go index e88b18e7b..692f04d4f 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -20,7 +20,7 @@ var ErrInvalidRecordType = errors.New("invalid record keytype") func (dht *IpfsDHT) makePutRecord(key u.Key, value []byte) (*pb.Record, error) { record := new(pb.Record) - record.Key = proto.String(key.String()) + record.Key = proto.String(string(key)) record.Value = value record.Author = proto.String(string(dht.self.ID())) blob := bytes.Join([][]byte{[]byte(key), value, []byte(dht.self.ID())}, []byte{}) @@ -38,13 +38,15 @@ func (dht *IpfsDHT) verifyRecord(r *pb.Record) error { if err != nil { return err } + k := u.Key(r.GetKey()) - blob := bytes.Join([][]byte{[]byte(r.GetKey()), + blob := bytes.Join([][]byte{[]byte(k), r.GetValue(), - []byte(r.GetKey())}, []byte{}) + []byte(r.GetAuthor())}, []byte{}) ok, err := p.PubKey().Verify(blob, r.GetSignature()) if err != nil { + log.Error("Signature verify failed.") return err } @@ -54,14 +56,14 @@ func (dht *IpfsDHT) verifyRecord(r *pb.Record) error { // Now, check validity func parts := strings.Split(r.GetKey(), "/") - if len(parts) < 2 { - log.Error("Record had bad key: %s", r.GetKey()) + if len(parts) < 3 { + log.Errorf("Record had bad key: %s", u.Key(r.GetKey())) return ErrBadRecord } - fnc, ok := dht.Validators[parts[0]] + fnc, ok := dht.Validators[parts[1]] if !ok { - log.Errorf("Unrecognized key prefix: %s", parts[0]) + log.Errorf("Unrecognized key prefix: %s", parts[1]) return ErrInvalidRecordType } From 93cb5081e6371b1defd4ef357c83fa9fdef294fa Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 9 Nov 2014 23:45:16 -0800 Subject: [PATCH 0395/3817] switch DHT entries over to be records, test currently fail This commit was moved from ipfs/go-namesys@32ab77ee7ca31e288344900d45d39408727f971a --- namesys/publisher.go | 4 ++-- namesys/routing.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index f7bf508b6..636e3fb49 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -49,7 +49,7 @@ func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { nameb := u.Hash(pkbytes) namekey := u.Key(nameb).Pretty() - ipnskey := u.Hash([]byte("/ipns/" + namekey)) + ipnskey := []byte("/ipns/" + namekey) // Store associated public key timectx, _ := context.WithDeadline(ctx, time.Now().Add(time.Second*4)) @@ -58,7 +58,7 @@ func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { return err } - // Store ipns entry at h("/ipns/"+b58(h(pubkey))) + // Store ipns entry at "/ipns/"+b58(h(pubkey)) timectx, _ = context.WithDeadline(ctx, time.Now().Add(time.Second*4)) err = p.routing.PutValue(timectx, u.Key(ipnskey), data) if err != nil { diff --git a/namesys/routing.go b/namesys/routing.go index 6259705ec..5f877bdc3 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -46,7 +46,7 @@ func (r *routingResolver) Resolve(name string) (string, error) { // use the routing system to get the name. // /ipns/ - h := u.Hash([]byte("/ipns/" + name)) + h := []byte("/ipns/" + name) ipnsKey := u.Key(h) val, err := r.routing.GetValue(ctx, ipnsKey) From 98eba0e2b3d42f206e24727908217e8be4d59360 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 10 Nov 2014 15:48:49 -0800 Subject: [PATCH 0396/3817] validator functions and ipns completion This commit was moved from ipfs/go-ipfs-routing@f90187507737016db59f67c510cd2e5337278289 --- routing/dht/dht.go | 3 +++ routing/dht/records.go | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 5d4caaa84..666387184 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -84,7 +84,10 @@ func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, dialer inet.Dia dht.routingTables[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID()), time.Millisecond*1000) dht.routingTables[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID()), time.Hour) dht.birth = time.Now() + dht.Validators = make(map[string]ValidatorFunc) + dht.Validators["ipns"] = ValidateIpnsRecord + dht.Validators["pk"] = ValidatePublicKeyRecord if doPinging { dht.Children().Add(1) diff --git a/routing/dht/records.go b/routing/dht/records.go index 692f04d4f..763b48f68 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -69,3 +69,13 @@ func (dht *IpfsDHT) verifyRecord(r *pb.Record) error { return fnc(u.Key(r.GetKey()), r.GetValue()) } + +func ValidateIpnsRecord(k u.Key, val []byte) error { + // TODO: + return nil +} + +func ValidatePublicKeyRecord(k u.Key, val []byte) error { + // TODO: + return nil +} From 2b1ba28dcc4d4e4062ef84a33d782d03ae30b93b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 10 Nov 2014 15:48:49 -0800 Subject: [PATCH 0397/3817] validator functions and ipns completion This commit was moved from ipfs/go-namesys@60a12596a7404d70404c511f057a4fcf2e3d484b --- namesys/publisher.go | 2 +- namesys/routing.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 636e3fb49..365855b1b 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -53,7 +53,7 @@ func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { // Store associated public key timectx, _ := context.WithDeadline(ctx, time.Now().Add(time.Second*4)) - err = p.routing.PutValue(timectx, u.Key(nameb), pkbytes) + err = p.routing.PutValue(timectx, u.Key("/pk/"+string(nameb)), pkbytes) if err != nil { return err } diff --git a/namesys/routing.go b/namesys/routing.go index 5f877bdc3..85eca331a 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -63,7 +63,7 @@ func (r *routingResolver) Resolve(name string) (string, error) { // name should be a public key retrievable from ipfs // /ipfs/ - key := u.Key(hash) + key := u.Key("/pk/" + string(hash)) pkval, err := r.routing.GetValue(ctx, key) if err != nil { log.Warning("RoutingResolve PubKey Get failed.") From ff3e18cd7673de3b4446a44e1d2c4b5b5cefcada Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Nov 2014 16:28:20 -0800 Subject: [PATCH 0398/3817] fix routing resolver This commit was moved from ipfs/go-ipfs-routing@8f742c12bb19b3cbb934ee6a21b68b4bfb24e79d --- routing/dht/dht.go | 3 +++ routing/dht/records.go | 36 +++++++++++++++++++++++++++++++++++- routing/mock/routing.go | 5 +++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 666387184..6b2d3f5bd 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -355,10 +355,12 @@ func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key, func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { dht.dslock.Lock() defer dht.dslock.Unlock() + log.Debug("getLocal %s", key) v, err := dht.datastore.Get(key.DsKey()) if err != nil { return nil, err } + log.Debug("found in db") byt, ok := v.([]byte) if !ok { @@ -374,6 +376,7 @@ func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { if u.Debug { err = dht.verifyRecord(rec) if err != nil { + log.Errorf("local record verify failed: %s", err) return nil, err } } diff --git a/routing/dht/records.go b/routing/dht/records.go index 763b48f68..9f3b9bdad 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -4,8 +4,11 @@ import ( "bytes" "errors" "strings" + "time" + "code.google.com/p/go.net/context" "code.google.com/p/goprotobuf/proto" + ci "github.com/jbenet/go-ipfs/crypto" "github.com/jbenet/go-ipfs/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" @@ -32,6 +35,29 @@ func (dht *IpfsDHT) makePutRecord(key u.Key, value []byte) (*pb.Record, error) { return record, nil } +func (dht *IpfsDHT) getPublicKey(pid peer.ID) (ci.PubKey, error) { + log.Debug("getPublicKey for: %s", pid) + p, err := dht.peerstore.Get(pid) + if err == nil { + return p.PubKey(), nil + } + + log.Debug("not in peerstore, searching dht.") + ctxT, _ := context.WithTimeout(dht.ContextCloser.Context(), time.Second*5) + val, err := dht.GetValue(ctxT, u.Key("/pk/"+string(pid))) + if err != nil { + log.Warning("Failed to find requested public key.") + return nil, err + } + + pubkey, err := ci.UnmarshalPublicKey(val) + if err != nil { + log.Errorf("Failed to unmarshal public key: %s", err) + return nil, err + } + return pubkey, nil +} + func (dht *IpfsDHT) verifyRecord(r *pb.Record) error { // First, validate the signature p, err := dht.peerstore.Get(peer.ID(r.GetAuthor())) @@ -76,6 +102,14 @@ func ValidateIpnsRecord(k u.Key, val []byte) error { } func ValidatePublicKeyRecord(k u.Key, val []byte) error { - // TODO: + keyparts := bytes.Split([]byte(k), []byte("/")) + if len(keyparts) < 3 { + return errors.New("invalid key") + } + + pkh := u.Hash(val) + if !bytes.Equal(keyparts[2], pkh) { + return errors.New("public key does not match storage key") + } return nil } diff --git a/routing/mock/routing.go b/routing/mock/routing.go index 9c6919589..358d57901 100644 --- a/routing/mock/routing.go +++ b/routing/mock/routing.go @@ -12,6 +12,8 @@ import ( u "github.com/jbenet/go-ipfs/util" ) +var log = u.Logger("mockrouter") + var _ routing.IpfsRouting = &MockRouter{} type MockRouter struct { @@ -33,10 +35,12 @@ func (mr *MockRouter) SetRoutingServer(rs RoutingServer) { } func (mr *MockRouter) PutValue(ctx context.Context, key u.Key, val []byte) error { + log.Debugf("PutValue: %s", key) return mr.datastore.Put(key.DsKey(), val) } func (mr *MockRouter) GetValue(ctx context.Context, key u.Key) ([]byte, error) { + log.Debugf("GetValue: %s", key) v, err := mr.datastore.Get(key.DsKey()) if err != nil { return nil, err @@ -55,6 +59,7 @@ func (mr *MockRouter) FindProviders(ctx context.Context, key u.Key) ([]peer.Peer } func (mr *MockRouter) FindPeer(ctx context.Context, pid peer.ID) (peer.Peer, error) { + log.Debug("FindPeer: %s", pid) return nil, nil } From 24452e92398428f91317251ed2f534111fe261df Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Nov 2014 16:28:20 -0800 Subject: [PATCH 0399/3817] fix routing resolver This commit was moved from ipfs/go-namesys@4361cbc77074d02d74d06a86f8dd35591f8f1fec --- namesys/publisher.go | 16 +++++++++++----- namesys/routing.go | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 365855b1b..9aaaa1cc3 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -33,34 +33,40 @@ func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { // validate `value` is a ref (multihash) _, err := mh.FromB58String(value) if err != nil { + log.Errorf("hash cast failed: %s", value) return fmt.Errorf("publish value must be str multihash. %v", err) } ctx := context.TODO() data, err := createRoutingEntryData(k, value) if err != nil { + log.Error("entry creation failed.") return err } pubkey := k.GetPublic() pkbytes, err := pubkey.Bytes() if err != nil { - return nil + log.Error("pubkey getbytes failed.") + return err } nameb := u.Hash(pkbytes) - namekey := u.Key(nameb).Pretty() - ipnskey := []byte("/ipns/" + namekey) + namekey := u.Key("/pk/" + string(nameb)) + log.Debugf("Storing pubkey at: %s", namekey) // Store associated public key timectx, _ := context.WithDeadline(ctx, time.Now().Add(time.Second*4)) - err = p.routing.PutValue(timectx, u.Key("/pk/"+string(nameb)), pkbytes) + err = p.routing.PutValue(timectx, namekey, pkbytes) if err != nil { return err } + ipnskey := u.Key("/ipns/" + string(nameb)) + + log.Debugf("Storing ipns entry at: %s", ipnskey) // Store ipns entry at "/ipns/"+b58(h(pubkey)) timectx, _ = context.WithDeadline(ctx, time.Now().Add(time.Second*4)) - err = p.routing.PutValue(timectx, u.Key(ipnskey), data) + err = p.routing.PutValue(timectx, ipnskey, data) if err != nil { return err } diff --git a/namesys/routing.go b/namesys/routing.go index 85eca331a..c956f4c44 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -46,7 +46,7 @@ func (r *routingResolver) Resolve(name string) (string, error) { // use the routing system to get the name. // /ipns/ - h := []byte("/ipns/" + name) + h := []byte("/ipns/" + string(hash)) ipnsKey := u.Key(h) val, err := r.routing.GetValue(ctx, ipnsKey) From d99e0be98b05ae6e280a0c22c884acd852598917 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Nov 2014 16:42:37 -0800 Subject: [PATCH 0400/3817] make vendor This commit was moved from ipfs/go-ipfs-routing@5c3d9fb3d11d7bad5aa74539dee886a0452c3871 --- routing/dht/handlers.go | 2 +- routing/dht/pb/dht.pb.go | 2 +- routing/dht/records.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index cdea759da..bd4b813ee 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "code.google.com/p/goprotobuf/proto" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" peer "github.com/jbenet/go-ipfs/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" diff --git a/routing/dht/pb/dht.pb.go b/routing/dht/pb/dht.pb.go index fd7620627..3e52a94ed 100644 --- a/routing/dht/pb/dht.pb.go +++ b/routing/dht/pb/dht.pb.go @@ -14,7 +14,7 @@ It has these top-level messages: */ package dht_pb -import proto "code.google.com/p/gogoprotobuf/proto" +import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. diff --git a/routing/dht/records.go b/routing/dht/records.go index 9f3b9bdad..c8397c960 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -6,8 +6,8 @@ import ( "strings" "time" - "code.google.com/p/go.net/context" - "code.google.com/p/goprotobuf/proto" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ci "github.com/jbenet/go-ipfs/crypto" "github.com/jbenet/go-ipfs/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" From 9786219362d38123f0192b8535ee9049662ac025 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Nov 2014 17:58:08 -0800 Subject: [PATCH 0401/3817] verify ipns records This commit was moved from ipfs/go-namesys@e0ce21a1ac953815de90f5fb023bfb221a7281ca --- namesys/internal/pb/namesys.pb.go | 56 ++++++++++++++++++++++++++++--- namesys/internal/pb/namesys.proto | 7 ++++ namesys/publisher.go | 49 +++++++++++++++++++++++++-- namesys/routing.go | 4 ++- 4 files changed, 109 insertions(+), 7 deletions(-) diff --git a/namesys/internal/pb/namesys.pb.go b/namesys/internal/pb/namesys.pb.go index b5d8885a2..81021b818 100644 --- a/namesys/internal/pb/namesys.pb.go +++ b/namesys/internal/pb/namesys.pb.go @@ -13,17 +13,50 @@ It has these top-level messages: */ package namesys_pb -import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import proto "code.google.com/p/gogoprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = math.Inf +type IpnsEntry_ValidityType int32 + +const ( + // setting an EOL says "this record is valid until..." + IpnsEntry_EOL IpnsEntry_ValidityType = 0 +) + +var IpnsEntry_ValidityType_name = map[int32]string{ + 0: "EOL", +} +var IpnsEntry_ValidityType_value = map[string]int32{ + "EOL": 0, +} + +func (x IpnsEntry_ValidityType) Enum() *IpnsEntry_ValidityType { + p := new(IpnsEntry_ValidityType) + *p = x + return p +} +func (x IpnsEntry_ValidityType) String() string { + return proto.EnumName(IpnsEntry_ValidityType_name, int32(x)) +} +func (x *IpnsEntry_ValidityType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(IpnsEntry_ValidityType_value, data, "IpnsEntry_ValidityType") + if err != nil { + return err + } + *x = IpnsEntry_ValidityType(value) + return nil +} + type IpnsEntry struct { - Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"` - Signature []byte `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"` - XXX_unrecognized []byte `json:"-"` + Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"` + Signature []byte `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"` + ValidityType *IpnsEntry_ValidityType `protobuf:"varint,3,opt,name=validityType,enum=namesys.pb.IpnsEntry_ValidityType" json:"validityType,omitempty"` + Validity []byte `protobuf:"bytes,4,opt,name=validity" json:"validity,omitempty"` + XXX_unrecognized []byte `json:"-"` } func (m *IpnsEntry) Reset() { *m = IpnsEntry{} } @@ -44,5 +77,20 @@ func (m *IpnsEntry) GetSignature() []byte { return nil } +func (m *IpnsEntry) GetValidityType() IpnsEntry_ValidityType { + if m != nil && m.ValidityType != nil { + return *m.ValidityType + } + return IpnsEntry_EOL +} + +func (m *IpnsEntry) GetValidity() []byte { + if m != nil { + return m.Validity + } + return nil +} + func init() { + proto.RegisterEnum("namesys.pb.IpnsEntry_ValidityType", IpnsEntry_ValidityType_name, IpnsEntry_ValidityType_value) } diff --git a/namesys/internal/pb/namesys.proto b/namesys/internal/pb/namesys.proto index ac8a78da3..4219af6bb 100644 --- a/namesys/internal/pb/namesys.proto +++ b/namesys/internal/pb/namesys.proto @@ -1,6 +1,13 @@ package namesys.pb; message IpnsEntry { + enum ValidityType { + // setting an EOL says "this record is valid until..." + EOL = 0; + } required bytes value = 1; required bytes signature = 2; + + optional ValidityType validityType = 3; + optional bytes validity = 4; } diff --git a/namesys/publisher.go b/namesys/publisher.go index 9aaaa1cc3..7123264db 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -1,6 +1,8 @@ package namesys import ( + "bytes" + "errors" "fmt" "time" @@ -14,6 +16,12 @@ import ( u "github.com/jbenet/go-ipfs/util" ) +// ErrExpiredRecord should be returned when an ipns record is +// invalid due to being too old +var ErrExpiredRecord = errors.New("expired record") + +var ErrUnrecognizedValidity = errors.New("unrecognized validity type") + // ipnsPublisher is capable of publishing and resolving names to the IPFS // routing system. type ipnsPublisher struct { @@ -76,11 +84,48 @@ func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { func createRoutingEntryData(pk ci.PrivKey, val string) ([]byte, error) { entry := new(pb.IpnsEntry) - sig, err := pk.Sign([]byte(val)) + + entry.Value = []byte(val) + typ := pb.IpnsEntry_EOL + entry.ValidityType = &typ + entry.Validity = []byte(time.Now().Add(time.Hour * 24).String()) + + sig, err := pk.Sign(ipnsEntryDataForSig(entry)) if err != nil { return nil, err } entry.Signature = sig - entry.Value = []byte(val) return proto.Marshal(entry) } + +func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte { + return bytes.Join([][]byte{ + e.Value, + e.Validity, + []byte(fmt.Sprint(e.GetValidityType())), + }, + []byte{}) +} + +func ValidateIpnsRecord(k u.Key, val []byte) error { + entry := new(pb.IpnsEntry) + err := proto.Unmarshal(val, entry) + if err != nil { + return err + } + switch entry.GetValidityType() { + case pb.IpnsEntry_EOL: + defaultTimeFormat := "2006-01-02 15:04:05.999999999 -0700 MST" + t, err := time.Parse(defaultTimeFormat, string(entry.GetValue())) + if err != nil { + log.Error("Failed parsing time for ipns record EOL") + return err + } + if time.Now().After(t) { + return ErrExpiredRecord + } + default: + return ErrUnrecognizedValidity + } + return nil +} diff --git a/namesys/routing.go b/namesys/routing.go index c956f4c44..c990b492b 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -75,9 +75,11 @@ func (r *routingResolver) Resolve(name string) (string, error) { if err != nil { return "", err } + hsh, _ := pk.Hash() + log.Debugf("pk hash = %s", u.Key(hsh)) // check sig with pk - if ok, err := pk.Verify(entry.GetValue(), entry.GetSignature()); err != nil || !ok { + if ok, err := pk.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok { return "", fmt.Errorf("Invalid value. Not signed by PrivateKey corresponding to %v", pk) } From c055343405d24bd629b6243559c28cc93de9f12d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Nov 2014 17:58:08 -0800 Subject: [PATCH 0402/3817] verify ipns records This commit was moved from ipfs/go-ipfs-routing@8a2d50b57d53a370337662f94eafd8e3c4e58019 --- routing/dht/dht.go | 1 - routing/dht/records.go | 5 ----- 2 files changed, 6 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 6b2d3f5bd..db17f9e7e 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -86,7 +86,6 @@ func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, dialer inet.Dia dht.birth = time.Now() dht.Validators = make(map[string]ValidatorFunc) - dht.Validators["ipns"] = ValidateIpnsRecord dht.Validators["pk"] = ValidatePublicKeyRecord if doPinging { diff --git a/routing/dht/records.go b/routing/dht/records.go index c8397c960..ee1257e24 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -96,11 +96,6 @@ func (dht *IpfsDHT) verifyRecord(r *pb.Record) error { return fnc(u.Key(r.GetKey()), r.GetValue()) } -func ValidateIpnsRecord(k u.Key, val []byte) error { - // TODO: - return nil -} - func ValidatePublicKeyRecord(k u.Key, val []byte) error { keyparts := bytes.Split([]byte(k), []byte("/")) if len(keyparts) < 3 { From 5fc2b91480ecf376015d56879eaf0319f2731b3e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Nov 2014 18:04:01 -0800 Subject: [PATCH 0403/3817] make vendor This commit was moved from ipfs/go-namesys@2084a38eb41ed240ba5e73dd7ccd29762f18e5ff --- namesys/internal/pb/namesys.pb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/internal/pb/namesys.pb.go b/namesys/internal/pb/namesys.pb.go index 81021b818..68b93a2c4 100644 --- a/namesys/internal/pb/namesys.pb.go +++ b/namesys/internal/pb/namesys.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package namesys_pb -import proto "code.google.com/p/gogoprotobuf/proto" +import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. From 063417cef9f732e95e7aeb82cf17b81a76833cce Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Nov 2014 19:43:53 -0800 Subject: [PATCH 0404/3817] some comments This commit was moved from ipfs/go-ipfs-routing@c0164029beaadf16af4d0aed97c796289f21367d --- routing/dht/records.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/routing/dht/records.go b/routing/dht/records.go index ee1257e24..0a3b4f4e0 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -14,9 +14,16 @@ import ( u "github.com/jbenet/go-ipfs/util" ) +// ValidatorFunc is a function that is called to validate a given +// type of DHTRecord. type ValidatorFunc func(u.Key, []byte) error +// ErrBadRecord is returned any time a dht record is found to be +// incorrectly formatted or signed. var ErrBadRecord = errors.New("bad dht record") + +// ErrInvalidRecordType is returned if a DHTRecord keys prefix +// is not found in the Validator map of the DHT. var ErrInvalidRecordType = errors.New("invalid record keytype") // creates and signs a dht record for the given key/value pair @@ -96,6 +103,9 @@ func (dht *IpfsDHT) verifyRecord(r *pb.Record) error { return fnc(u.Key(r.GetKey()), r.GetValue()) } +// ValidatePublicKeyRecord implements ValidatorFunc and +// verifies that the passed in record value is the PublicKey +// that matches the passed in key. func ValidatePublicKeyRecord(k u.Key, val []byte) error { keyparts := bytes.Split([]byte(k), []byte("/")) if len(keyparts) < 3 { From 673552c600047cffba72c24863d1187296c4af14 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Nov 2014 19:43:53 -0800 Subject: [PATCH 0405/3817] some comments This commit was moved from ipfs/go-namesys@0a01f43a023e3662b0faf55516d8ca13575c9386 --- namesys/publisher.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/namesys/publisher.go b/namesys/publisher.go index 7123264db..5b2da0c17 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -20,6 +20,8 @@ import ( // invalid due to being too old var ErrExpiredRecord = errors.New("expired record") +// ErrUnrecognizedValidity is returned when an IpnsRecord has an +// unknown validity type. var ErrUnrecognizedValidity = errors.New("unrecognized validity type") // ipnsPublisher is capable of publishing and resolving names to the IPFS @@ -107,6 +109,8 @@ func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte { []byte{}) } +// ValidateIpnsRecord implements ValidatorFunc and verifies that the +// given 'val' is an IpnsEntry and that that entry is valid. func ValidateIpnsRecord(k u.Key, val []byte) error { entry := new(pb.IpnsEntry) err := proto.Unmarshal(val, entry) From 4bb64a796e1212350ec3be81e54608c70eb638ed Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 14 Nov 2014 11:00:45 -0800 Subject: [PATCH 0406/3817] address comments from PR This commit was moved from ipfs/go-ipfs-routing@9c553435978e0381007cf5bcdad7616997cc9c38 --- routing/dht/dht.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index db17f9e7e..efe457c65 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -273,6 +273,7 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, // make sure record is still valid err = dht.verifyRecord(record) if err != nil { + log.Error("Received invalid record!") return nil, nil, err } return record.GetValue(), nil, nil From 9b084d3d5ccd57740cf3ab1bca815e54b5ad8d79 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 14 Nov 2014 11:00:45 -0800 Subject: [PATCH 0407/3817] address comments from PR This commit was moved from ipfs/go-namesys@0c6b8f23465fa4932ae7df5f8143566b2ad1ca4b --- namesys/publisher.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 5b2da0c17..a6be2a570 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -90,7 +90,7 @@ func createRoutingEntryData(pk ci.PrivKey, val string) ([]byte, error) { entry.Value = []byte(val) typ := pb.IpnsEntry_EOL entry.ValidityType = &typ - entry.Validity = []byte(time.Now().Add(time.Hour * 24).String()) + entry.Validity = []byte(u.FormatRFC3339(time.Now().Add(time.Hour * 24))) sig, err := pk.Sign(ipnsEntryDataForSig(entry)) if err != nil { @@ -119,8 +119,7 @@ func ValidateIpnsRecord(k u.Key, val []byte) error { } switch entry.GetValidityType() { case pb.IpnsEntry_EOL: - defaultTimeFormat := "2006-01-02 15:04:05.999999999 -0700 MST" - t, err := time.Parse(defaultTimeFormat, string(entry.GetValue())) + t, err := u.ParseRFC3339(string(entry.GetValue())) if err != nil { log.Error("Failed parsing time for ipns record EOL") return err From 280e19df3996730b3439e927bad55a9923876bfb Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 15 Nov 2014 18:31:06 -0800 Subject: [PATCH 0408/3817] log(dht) log a couple events to demonstrate API License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@da21d99fcc6f95db088a34c338028917c5b00758 --- routing/dht/dht.go | 5 ++++- routing/dht/pb/message.go | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index efe457c65..1af4a29bd 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -18,6 +18,7 @@ import ( kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" ctxc "github.com/jbenet/go-ipfs/util/ctxcloser" + "github.com/jbenet/go-ipfs/util/elog" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" @@ -25,7 +26,7 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ) -var log = u.Logger("dht") +var log = elog.Logger("dht") const doPinging = false @@ -152,6 +153,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N dht.Update(mPeer) // Print out diagnostic + log.Event(ctx, "foo", dht.self, mPeer, pmes) log.Debugf("%s got message type: '%s' from %s", dht.self, pb.Message_MessageType_name[int32(pmes.GetType())], mPeer) @@ -197,6 +199,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *pb.Messa start := time.Now() // Print out diagnostic + log.Event(ctx, "sentMessage", dht.self, p, pmes) log.Debugf("Sent message type: '%s' to %s", pb.Message_MessageType_name[int32(pmes.GetType())], p) diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index a77a5b917..6ea98d4cd 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -65,3 +65,11 @@ func (m *Message) SetClusterLevel(level int) { lvl := int32(level) m.ClusterLevelRaw = &lvl } + +func (m *Message) Loggable() map[string]interface{} { + return map[string]interface{}{ + "message": map[string]string{ + "type": m.Type.String(), + }, + } +} From 1aeb51f94b19aa3c6039ed73b362892c86e89e35 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 04:01:02 -0800 Subject: [PATCH 0409/3817] refactor(eventlog) elog -> eventlog License: MIT Signed-off-by: Brian Tiger Chow # TYPES # feat # fix # docs # style (formatting, missing semi colons, etc; no code change): # refactor # test (adding missing tests, refactoring tests; no production code change) # chore (updating grunt tasks etc; no production code change) Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@bae86b669ee3e7360fd32b487ada7d62b1b7f273 --- routing/dht/dht.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 1af4a29bd..f042bbd4b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -26,7 +26,7 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ) -var log = elog.Logger("dht") +var log = eventlog.Logger("dht") const doPinging = false From 80eb6d6d9c0576d490f623b309b1eec682dbd9fb Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 04:53:07 -0800 Subject: [PATCH 0410/3817] fix(imports) misc License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@310647fead1cda84c668e3f8aacced667eac8168 --- routing/dht/dht.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index f042bbd4b..7a61c75f0 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -18,7 +18,7 @@ import ( kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" ctxc "github.com/jbenet/go-ipfs/util/ctxcloser" - "github.com/jbenet/go-ipfs/util/elog" + "github.com/jbenet/go-ipfs/util/eventlog" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" From c0a44ad37fbc6b6e51456db136c4bbcd31c82768 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 07:40:05 -0800 Subject: [PATCH 0411/3817] fix(misc) address PR comments License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@ece27cae7ad9fee01c999105216d36ea2486286e --- routing/dht/dht.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 7a61c75f0..f4d2948bc 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -152,10 +152,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N // update the peer (on valid msgs only) dht.Update(mPeer) - // Print out diagnostic log.Event(ctx, "foo", dht.self, mPeer, pmes) - log.Debugf("%s got message type: '%s' from %s", - dht.self, pb.Message_MessageType_name[int32(pmes.GetType())], mPeer) // get handler for this msg type. handler := dht.handlerForMsgType(pmes.GetType()) @@ -198,10 +195,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *pb.Messa start := time.Now() - // Print out diagnostic log.Event(ctx, "sentMessage", dht.self, p, pmes) - log.Debugf("Sent message type: '%s' to %s", - pb.Message_MessageType_name[int32(pmes.GetType())], p) rmes, err := dht.sender.SendRequest(ctx, mes) if err != nil { From cdd158c69fbe0f150afcae1011cd227b664f6fa4 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 18 Nov 2014 05:48:04 -0800 Subject: [PATCH 0412/3817] SizeSplitter fix: keep-reading until chunk full if the underlying reader is buffered with a smaller buffer it would force the chunk sizes to come out smaller than intended. cc @whyrusleeping @mappum This commit was moved from ipfs/go-ipfs-chunker@6ddb6d481c46d2def1a25884cd8f22dbf83291b1 --- chunker/splitting.go | 31 +++++++++++++++++-------- chunker/splitting_test.go | 49 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 10 deletions(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 2d92a9ead..87d48be85 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -23,23 +23,34 @@ func (ss *SizeSplitter) Split(r io.Reader) chan []byte { out := make(chan []byte) go func() { defer close(out) + + // all-chunks loop (keep creating chunks) for { + // log.Infof("making chunk with size: %d", ss.Size) chunk := make([]byte, ss.Size) - nread, err := r.Read(chunk) - if err != nil { + sofar := 0 + + // this-chunk loop (keep reading until this chunk full) + for { + nread, err := r.Read(chunk[sofar:]) + sofar += nread if err == io.EOF { - if nread > 0 { - out <- chunk[:nread] + if sofar > 0 { + // log.Infof("sending out chunk with size: %d", sofar) + out <- chunk[:sofar] } return } - log.Errorf("Block split error: %s", err) - return - } - if nread < ss.Size { - chunk = chunk[:nread] + if err != nil { + log.Errorf("Block split error: %s", err) + return + } + if sofar == ss.Size { + // log.Infof("sending out chunk with size: %d", sofar) + out <- chunk[:sofar] + break // break out of this-chunk loop + } } - out <- chunk } }() return out diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index 612da4d09..1385b9069 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -3,6 +3,7 @@ package chunk import ( "bytes" "crypto/rand" + "io" "testing" ) @@ -54,3 +55,51 @@ func TestSizeSplitterIsDeterministic(t *testing.T) { test() } } + +func TestSizeSplitterFillsChunks(t *testing.T) { + if testing.Short() { + t.SkipNow() + } + + max := 10000000 + b := randBuf(t, max) + r := &clipReader{r: bytes.NewReader(b), size: 4000} + s := SizeSplitter{Size: 1024 * 256} + c := s.Split(r) + + sofar := 0 + whole := make([]byte, max) + for chunk := range c { + + bc := b[sofar : sofar+len(chunk)] + if !bytes.Equal(bc, chunk) { + t.Fatalf("chunk not correct: (sofar: %d) %d != %d, %v != %v", sofar, len(bc), len(chunk), bc[:100], chunk[:100]) + } + + copy(whole[sofar:], chunk) + + sofar += len(chunk) + if sofar != max && len(chunk) < s.Size { + t.Fatal("sizesplitter split at a smaller size") + } + } + + if !bytes.Equal(b, whole) { + t.Fatal("splitter did not split right") + } +} + +type clipReader struct { + size int + r io.Reader +} + +func (s *clipReader) Read(buf []byte) (int, error) { + + // clip the incoming buffer to produce smaller chunks + if len(buf) > s.size { + buf = buf[:s.size] + } + + return s.r.Read(buf) +} From 905713be8844039bad0a85f20104fcca68539e14 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 18 Nov 2014 23:02:22 -0800 Subject: [PATCH 0413/3817] pin: Added a Pinner#Set function to retrieve the set of pinned keys This commit was moved from ipfs/go-ipfs-pinner@d6fd1f2add1c10f69c43c442a77eed1d77364424 --- pinning/pinner/pin.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 60828b597..1d840c6bc 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -33,6 +33,7 @@ type Pinner interface { Unpin(util.Key, bool) error Flush() error GetManual() ManualPinner + Set() set.BlockSet } // ManualPinner is for manually editing the pin structure @@ -207,6 +208,11 @@ func LoadPinner(d ds.Datastore, dserv mdag.DAGService) (Pinner, error) { return p, nil } +// Set returns a blockset of directly pinned keys +func (p *pinner) Set() set.BlockSet { + return p.directPin +} + // Flush encodes and writes pinner keysets to the datastore func (p *pinner) Flush() error { p.lock.RLock() From 47b6bd49f82ed3712a476a2b79cf9e9e300d8a6a Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 19 Nov 2014 00:53:23 -0800 Subject: [PATCH 0414/3817] pin: Return copies of pinned keys, of each type (direct/indirect/recursive) This commit was moved from ipfs/go-ipfs-pinner@22ab7890cd6741d3663a286ba07eb1a7bc618308 --- pinning/pinner/indirect.go | 4 ++++ pinning/pinner/pin.go | 20 ++++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index b15b720ee..9e67bc2c9 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -65,3 +65,7 @@ func (i *indirectPin) Decrement(k util.Key) { func (i *indirectPin) HasKey(k util.Key) bool { return i.blockset.HasKey(k) } + +func (i *indirectPin) Set() set.BlockSet { + return i.blockset +} diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 1d840c6bc..371497da6 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -33,7 +33,9 @@ type Pinner interface { Unpin(util.Key, bool) error Flush() error GetManual() ManualPinner - Set() set.BlockSet + DirectKeys() []util.Key + IndirectKeys() []util.Key + RecursiveKeys() []util.Key } // ManualPinner is for manually editing the pin structure @@ -208,9 +210,19 @@ func LoadPinner(d ds.Datastore, dserv mdag.DAGService) (Pinner, error) { return p, nil } -// Set returns a blockset of directly pinned keys -func (p *pinner) Set() set.BlockSet { - return p.directPin +// DirectKeys returns a slice containing the directly pinned keys +func (p *pinner) DirectKeys() []util.Key { + return p.directPin.GetKeys() +} + +// IndirectKeys returns a slice containing the indirectly pinned keys +func (p *pinner) IndirectKeys() []util.Key { + return p.indirPin.Set().GetKeys() +} + +// RecursiveKeys returns a slice containing the recursively pinned keys +func (p *pinner) RecursiveKeys() []util.Key { + return p.recursePin.GetKeys() } // Flush encodes and writes pinner keysets to the datastore From df61fec6360470dcab7fdbc2abeceb7fc2fe0962 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 18 Nov 2014 23:03:58 -0800 Subject: [PATCH 0415/3817] importer: simplified splitter The splitter is simplified using io.ReadFull, as this function does exactly what we wanted. I believe io.ErrUnexpectedEOF should be handled as an EOF here, but please correct me if I'm wrong. This commit was moved from ipfs/go-ipfs-chunker@de56900e6d79a43be1d707c6ad3ab403b67553bd --- chunker/splitting.go | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 87d48be85..65a79d5ad 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -28,28 +28,17 @@ func (ss *SizeSplitter) Split(r io.Reader) chan []byte { for { // log.Infof("making chunk with size: %d", ss.Size) chunk := make([]byte, ss.Size) - sofar := 0 - - // this-chunk loop (keep reading until this chunk full) - for { - nread, err := r.Read(chunk[sofar:]) - sofar += nread - if err == io.EOF { - if sofar > 0 { - // log.Infof("sending out chunk with size: %d", sofar) - out <- chunk[:sofar] - } - return - } - if err != nil { - log.Errorf("Block split error: %s", err) - return - } - if sofar == ss.Size { - // log.Infof("sending out chunk with size: %d", sofar) - out <- chunk[:sofar] - break // break out of this-chunk loop - } + nread, err := io.ReadFull(r, chunk) + if nread > 0 { + // log.Infof("sending out chunk with size: %d", sofar) + out <- chunk[:nread] + } + if err == io.EOF || err == io.ErrUnexpectedEOF { + return + } + if err != nil { + log.Errorf("Block split error: %s", err) + return } } }() From 4329fae7c38f639c7fe3b2fc4109c8e2023f3bfb Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 22 Nov 2014 20:02:45 -0800 Subject: [PATCH 0416/3817] log(dht) add eventlog.Update event License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@90f57a2425e2dc879d11abc47b326af63672f5eb --- routing/dht/dht.go | 8 ++++---- routing/dht/ext_test.go | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index f4d2948bc..30fe44630 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -121,7 +121,7 @@ func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.Peer) (peer.Peer, er return nil, fmt.Errorf("failed to ping newly connected peer: %s\n", err) } - dht.Update(npeer) + dht.Update(ctx, npeer) return npeer, nil } @@ -150,7 +150,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N } // update the peer (on valid msgs only) - dht.Update(mPeer) + dht.Update(ctx, mPeer) log.Event(ctx, "foo", dht.self, mPeer, pmes) @@ -397,8 +397,8 @@ func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { // Update signals to all routingTables to Update their last-seen status // on the given peer. -func (dht *IpfsDHT) Update(p peer.Peer) { - log.Debugf("updating peer: %s latency = %f\n", p, p.GetLatency().Seconds()) +func (dht *IpfsDHT) Update(ctx context.Context, p peer.Peer) { + log.Event(ctx, "updatePeer", p) removedCount := 0 for _, route := range dht.routingTables { removed := route.Update(p) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 55a68ef9e..791c1066c 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -129,7 +129,7 @@ func TestGetFailures(t *testing.T) { d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) other := makePeer(nil) - d.Update(other) + d.Update(ctx, other) // This one should time out // u.POut("Timout Test\n") @@ -232,7 +232,7 @@ func TestNotFound(t *testing.T) { var ps []peer.Peer for i := 0; i < 5; i++ { ps = append(ps, _randPeer()) - d.Update(ps[i]) + d.Update(ctx, ps[i]) } // Reply with random peers to every message @@ -298,7 +298,7 @@ func TestLessThanKResponses(t *testing.T) { var ps []peer.Peer for i := 0; i < 5; i++ { ps = append(ps, _randPeer()) - d.Update(ps[i]) + d.Update(ctx, ps[i]) } other := _randPeer() From e0319c07b5aa67a7c85fc5e5d81cf2d982406d30 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 25 Nov 2014 04:17:37 -0800 Subject: [PATCH 0417/3817] log(dht) Event: connect License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@9f3591b60c8c518f1a9a9d0f02f6b42c73aca8c5 --- routing/dht/dht.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 30fe44630..f76ca8f59 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -98,8 +98,6 @@ func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, dialer inet.Dia // Connect to a new peer at the given address, ping and add to the routing table func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.Peer) (peer.Peer, error) { - log.Debugf("Connect to new peer: %s", npeer) - // TODO(jbenet,whyrusleeping) // // Connect should take in a Peer (with ID). In a sense, we shouldn't be @@ -120,6 +118,7 @@ func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.Peer) (peer.Peer, er if err != nil { return nil, fmt.Errorf("failed to ping newly connected peer: %s\n", err) } + log.Event(ctx, "connect", dht.self, npeer) dht.Update(ctx, npeer) From ac8f278bb1304bb0577a9a92148e0f7b3e69a7ed Mon Sep 17 00:00:00 2001 From: Simon Kirkby Date: Thu, 4 Dec 2014 20:39:35 +0800 Subject: [PATCH 0418/3817] Validity time not checked properly name publishing was failing of bad format. This commit was moved from ipfs/go-namesys@5eb019b08064a21fddbdb8040f844fde56ac7128 --- namesys/publisher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index a6be2a570..be838b2f0 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -119,7 +119,7 @@ func ValidateIpnsRecord(k u.Key, val []byte) error { } switch entry.GetValidityType() { case pb.IpnsEntry_EOL: - t, err := u.ParseRFC3339(string(entry.GetValue())) + t, err := u.ParseRFC3339(string(entry.GetValidity())) if err != nil { log.Error("Failed parsing time for ipns record EOL") return err From 6e4ad54b5c8f64c61ee843892695af5e90d7c8bc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 18 Nov 2014 21:31:00 -0800 Subject: [PATCH 0419/3817] beginnings of a bitswap refactor This commit was moved from ipfs/go-blockservice@59b4a639336cf4483b7d4f213f48319b43c50e1c --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 82a1b03c1..2eb3d695d 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -85,7 +85,7 @@ func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, er }, nil } else if err == ds.ErrNotFound && s.Remote != nil { log.Debug("Blockservice: Searching bitswap.") - blk, err := s.Remote.Block(ctx, k) + blk, err := s.Remote.GetBlock(ctx, k) if err != nil { return nil, err } From 22d98a68c43da0b0f34d0618771b895a03127e85 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 18 Nov 2014 21:31:00 -0800 Subject: [PATCH 0420/3817] beginnings of a bitswap refactor This commit was moved from ipfs/go-ipfs-exchange-offline@abc92d594bbd262d583d064a13501bcb70894f17 --- exchange/offline/offline.go | 4 ++-- exchange/offline/offline_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 5f7ef8835..37d672f73 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -23,10 +23,10 @@ func NewOfflineExchange() exchange.Interface { type offlineExchange struct { } -// Block returns nil to signal that a block could not be retrieved for the +// GetBlock returns nil to signal that a block could not be retrieved for the // given key. // NB: This function may return before the timeout expires. -func (_ *offlineExchange) Block(context.Context, u.Key) (*blocks.Block, error) { +func (_ *offlineExchange) GetBlock(context.Context, u.Key) (*blocks.Block, error) { return nil, OfflineMode } diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index cc3f3ec82..ae3fdaa0a 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -11,7 +11,7 @@ import ( func TestBlockReturnsErr(t *testing.T) { off := NewOfflineExchange() - _, err := off.Block(context.Background(), u.Key("foo")) + _, err := off.GetBlock(context.Background(), u.Key("foo")) if err != nil { return // as desired } From 633eab2003090c55635e4a4cc5986b9b50ed1ad8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 18 Nov 2014 21:31:00 -0800 Subject: [PATCH 0421/3817] beginnings of a bitswap refactor This commit was moved from ipfs/go-ipfs-exchange-interface@42d8c6a1798901ad705c4fc0a6380046305fe1f4 --- exchange/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index 82782a046..b62a47957 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -12,8 +12,8 @@ import ( // exchange protocol. type Interface interface { - // Block returns the block associated with a given key. - Block(context.Context, u.Key) (*blocks.Block, error) + // GetBlock returns the block associated with a given key. + GetBlock(context.Context, u.Key) (*blocks.Block, error) // TODO Should callers be concerned with whether the block was made // available on the network? From 33de046ee6f954bdc3c55c13fa7291f53d55ebc1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 20 Nov 2014 04:58:26 +0000 Subject: [PATCH 0422/3817] remove buffer timing in bitswap in favor of manual batching This commit was moved from ipfs/go-merkledag@ad36b64065ef7fb18621c410cb33261ed6dc3d2c --- ipld/merkledag/merkledag.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 1874e5304..d3fe79d9e 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -252,6 +252,7 @@ func (n *dagService) Remove(nd *Node) error { // FetchGraph asynchronously fetches all nodes that are children of the given // node, and returns a channel that may be waited upon for the fetch to complete func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} { + log.Warning("Untested.") var wg sync.WaitGroup done := make(chan struct{}) @@ -284,3 +285,22 @@ func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} return done } + +// Take advantage of blockservice/bitswap batched requests to fetch all +// child nodes of a given node +// TODO: finish this +func (ds *dagService) BatchFetch(ctx context.Context, root *Node) error { + var keys []u.Key + for _, lnk := range root.Links { + keys = append(keys, u.Key(lnk.Hash)) + } + + blocks, err := ds.Blocks.GetBlocks(keys) + if err != nil { + return err + } + + _ = blocks + //what do i do with blocks? + return nil +} From 6f02b1af34fed3824d4fb870898a8e6a751667ae Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 19 Nov 2014 23:32:51 +0000 Subject: [PATCH 0423/3817] move some variables into strategy This commit was moved from ipfs/go-blockservice@88d8c40cfe2599cba1a7c8517e2ed3c5eb1f04b0 --- blockservice/blockservice.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 2eb3d695d..4413eee16 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -96,6 +96,11 @@ func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, er } } +func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) (<-chan blocks.Block, error) { + // TODO: + return nil, nil +} + // DeleteBlock deletes a block in the blockservice from the datastore func (s *BlockService) DeleteBlock(k u.Key) error { return s.Datastore.Delete(k.DsKey()) From 81aa8a3687a463fc6840cb83457ac6980b26138c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 19 Nov 2014 23:34:40 +0000 Subject: [PATCH 0424/3817] fix tests halting This commit was moved from ipfs/go-ipfs-routing@0ce100ea6ae36644c09b7f5c955ba91c59ef60f4 --- routing/mock/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/mock/routing.go b/routing/mock/routing.go index 358d57901..ff83ddca3 100644 --- a/routing/mock/routing.go +++ b/routing/mock/routing.go @@ -59,7 +59,7 @@ func (mr *MockRouter) FindProviders(ctx context.Context, key u.Key) ([]peer.Peer } func (mr *MockRouter) FindPeer(ctx context.Context, pid peer.ID) (peer.Peer, error) { - log.Debug("FindPeer: %s", pid) + log.Debugf("FindPeer: %s", pid) return nil, nil } From 8cd46926b342152075335e33f95a231dc0a853ef Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 19 Nov 2014 23:11:06 -0800 Subject: [PATCH 0425/3817] fix(merkledag) missing arg License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-merkledag@2f37d28589c43e83288c9102b8e6f5461f43889b --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index d3fe79d9e..9d3e6ce95 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -295,7 +295,7 @@ func (ds *dagService) BatchFetch(ctx context.Context, root *Node) error { keys = append(keys, u.Key(lnk.Hash)) } - blocks, err := ds.Blocks.GetBlocks(keys) + blocks, err := ds.Blocks.GetBlocks(ctx, keys) if err != nil { return err } From 140d250d539b96f3ef9b5bab16876806f28933e6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 01:15:32 +0000 Subject: [PATCH 0426/3817] start working getBlocks up the call chain This commit was moved from ipfs/go-merkledag@52e9cde2193426074a81285f3b89e4c124d57d3c --- ipld/merkledag/merkledag.go | 62 ++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 9d3e6ce95..23eaf8881 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -289,18 +289,56 @@ func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} // Take advantage of blockservice/bitswap batched requests to fetch all // child nodes of a given node // TODO: finish this -func (ds *dagService) BatchFetch(ctx context.Context, root *Node) error { - var keys []u.Key - for _, lnk := range root.Links { - keys = append(keys, u.Key(lnk.Hash)) - } +func (ds *dagService) BatchFetch(ctx context.Context, root *Node) chan struct{} { + sig := make(chan struct{}) + go func() { + var keys []u.Key + for _, lnk := range root.Links { + keys = append(keys, u.Key(lnk.Hash)) + } - blocks, err := ds.Blocks.GetBlocks(ctx, keys) - if err != nil { - return err - } + blkchan := ds.Blocks.GetBlocks(ctx, keys) + + // + next := 0 + seen := make(map[int]struct{}) + // + + for blk := range blkchan { + for i, lnk := range root.Links { + + // + seen[i] = struct{}{} + // + + if u.Key(lnk.Hash) != blk.Key() { + continue + } + nd, err := Decoded(blk.Data) + if err != nil { + log.Error("Got back bad block!") + break + } + lnk.Node = nd + + // + if next == i { + sig <- struct{}{} + next++ + for { + if _, ok := seen[next]; ok { + sig <- struct{}{} + next++ + } else { + break + } + } + } + // + } + } + }() - _ = blocks - //what do i do with blocks? - return nil + // TODO: return a channel, and signal when the 'Next' readable block is available + return sig } From b7be2ab81aa10b3ca6cd30d853fd0bdc2111ab74 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 01:15:32 +0000 Subject: [PATCH 0427/3817] start working getBlocks up the call chain This commit was moved from ipfs/go-blockservice@a13559473135505e49af67fdc6d8183f8bb40b67 --- blockservice/blockservice.go | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 4413eee16..279e3ffd9 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -96,9 +96,29 @@ func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, er } } -func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) (<-chan blocks.Block, error) { - // TODO: - return nil, nil +func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks.Block { + out := make(chan *blocks.Block, 32) + go func() { + var toFetch []u.Key + for _, k := range ks { + datai, err := s.Datastore.Get(k.DsKey()) + if err == nil { + log.Debug("Blockservice: Got data in datastore.") + bdata, ok := datai.([]byte) + if !ok { + log.Criticalf("data associated with %s is not a []byte", k) + continue + } + out <- &blocks.Block{ + Multihash: mh.Multihash(k), + Data: bdata, + } + } else { + toFetch = append(toFetch, k) + } + } + }() + return out } // DeleteBlock deletes a block in the blockservice from the datastore From ab5153c734d9d3baf1525eec58413090aed928df Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 20 Nov 2014 17:27:48 -0800 Subject: [PATCH 0428/3817] refactor(blockstore) mv under blocks/ @jbenet @whyrusleeping the pyramids were built one brick at a time addresses: https://github.com/jbenet/go-ipfs/issues/370 License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-blockstore@88779b419358a94460a54fe4d3ba2732f34ae855 --- blockstore/blockstore.go | 47 +++++++++++++++++++++++++++++ blockstore/blockstore_test.go | 56 +++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 blockstore/blockstore.go create mode 100644 blockstore/blockstore_test.go diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go new file mode 100644 index 000000000..b4c0fd7fb --- /dev/null +++ b/blockstore/blockstore.go @@ -0,0 +1,47 @@ +// package blockstore implements a thin wrapper over a datastore, giving a +// clean interface for Getting and Putting block objects. +package blockstore + +import ( + "errors" + + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + blocks "github.com/jbenet/go-ipfs/blocks" + u "github.com/jbenet/go-ipfs/util" +) + +var ValueTypeMismatch = errors.New("The retrieved value is not a Block") + +type Blockstore interface { + Get(u.Key) (*blocks.Block, error) + Put(*blocks.Block) error +} + +func NewBlockstore(d ds.ThreadSafeDatastore) Blockstore { + return &blockstore{ + datastore: d, + } +} + +type blockstore struct { + datastore ds.ThreadSafeDatastore +} + +func (bs *blockstore) Get(k u.Key) (*blocks.Block, error) { + maybeData, err := bs.datastore.Get(k.DsKey()) + if err != nil { + return nil, err + } + bdata, ok := maybeData.([]byte) + if !ok { + return nil, ValueTypeMismatch + } + + return blocks.NewBlockWithHash(bdata, mh.Multihash(k)) +} + +func (bs *blockstore) Put(block *blocks.Block) error { + return bs.datastore.Put(block.Key().DsKey(), block.Data) +} diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go new file mode 100644 index 000000000..00edf61ab --- /dev/null +++ b/blockstore/blockstore_test.go @@ -0,0 +1,56 @@ +package blockstore + +import ( + "bytes" + "testing" + + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds_sync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + blocks "github.com/jbenet/go-ipfs/blocks" + u "github.com/jbenet/go-ipfs/util" +) + +// TODO(brian): TestGetReturnsNil + +func TestGetWhenKeyNotPresent(t *testing.T) { + bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) + _, err := bs.Get(u.Key("not present")) + + if err != nil { + t.Log("As expected, block is not present") + return + } + t.Fail() +} + +func TestPutThenGetBlock(t *testing.T) { + bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) + block := blocks.NewBlock([]byte("some data")) + + err := bs.Put(block) + if err != nil { + t.Fatal(err) + } + + blockFromBlockstore, err := bs.Get(block.Key()) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(block.Data, blockFromBlockstore.Data) { + t.Fail() + } +} + +func TestValueTypeMismatch(t *testing.T) { + block := blocks.NewBlock([]byte("some data")) + + datastore := ds.NewMapDatastore() + datastore.Put(block.Key().DsKey(), "data that isn't a block!") + + blockstore := NewBlockstore(ds_sync.MutexWrap(datastore)) + + _, err := blockstore.Get(block.Key()) + if err != ValueTypeMismatch { + t.Fatal(err) + } +} From 459d52130111391763305d4a8d29cc62bc9ef274 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 01:20:29 +0000 Subject: [PATCH 0429/3817] change BatchFetch to return indices This commit was moved from ipfs/go-merkledag@43c07c7eb619be28db300e153d2002f9897e3e2c --- ipld/merkledag/merkledag.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 23eaf8881..2483ce4d9 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -289,8 +289,8 @@ func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} // Take advantage of blockservice/bitswap batched requests to fetch all // child nodes of a given node // TODO: finish this -func (ds *dagService) BatchFetch(ctx context.Context, root *Node) chan struct{} { - sig := make(chan struct{}) +func (ds *dagService) BatchFetch(ctx context.Context, root *Node) <-chan int { + sig := make(chan int) go func() { var keys []u.Key for _, lnk := range root.Links { @@ -323,11 +323,11 @@ func (ds *dagService) BatchFetch(ctx context.Context, root *Node) chan struct{} // if next == i { - sig <- struct{}{} + sig <- next next++ for { if _, ok := seen[next]; ok { - sig <- struct{}{} + sig <- next next++ } else { break From 2fe1a78e2c03eeeecf908735da27e51e99e279b4 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 20 Nov 2014 18:08:43 -0800 Subject: [PATCH 0430/3817] refactor(blockstore, blockservice) use Blockstore and offline.Exchange License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-unixfs@18627ac31f739b18f422f08072887663d4f4836d --- unixfs/io/dagmodifier_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index 822c87471..d0aa83795 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -6,7 +6,10 @@ import ( "io/ioutil" "testing" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/jbenet/go-ipfs/blocks/blockstore" bs "github.com/jbenet/go-ipfs/blockservice" + "github.com/jbenet/go-ipfs/exchange/offline" imp "github.com/jbenet/go-ipfs/importer" "github.com/jbenet/go-ipfs/importer/chunk" mdag "github.com/jbenet/go-ipfs/merkledag" @@ -19,7 +22,9 @@ import ( func getMockDagServ(t *testing.T) mdag.DAGService { dstore := ds.NewMapDatastore() - bserv, err := bs.NewBlockService(dstore, nil) + tsds := sync.MutexWrap(dstore) + bstore := blockstore.NewBlockstore(tsds) + bserv, err := bs.New(bstore, offline.Exchange()) if err != nil { t.Fatal(err) } From 1411e76dbe62d56603bcb1e18c63f9f9f31d1a32 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 20 Nov 2014 17:41:47 -0800 Subject: [PATCH 0431/3817] rename exchange License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-exchange-offline@391b626cd96a80322d625c357fdd4e1724d0cc23 --- exchange/offline/offline.go | 2 +- exchange/offline/offline_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 37d672f73..fbb3485c0 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -14,7 +14,7 @@ import ( var OfflineMode = errors.New("Block unavailable. Operating in offline mode") -func NewOfflineExchange() exchange.Interface { +func Exchange() exchange.Interface { return &offlineExchange{} } diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index ae3fdaa0a..98b6e1a8c 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -10,7 +10,7 @@ import ( ) func TestBlockReturnsErr(t *testing.T) { - off := NewOfflineExchange() + off := Exchange() _, err := off.GetBlock(context.Background(), u.Key("foo")) if err != nil { return // as desired @@ -19,7 +19,7 @@ func TestBlockReturnsErr(t *testing.T) { } func TestHasBlockReturnsNil(t *testing.T) { - off := NewOfflineExchange() + off := Exchange() block := blocks.NewBlock([]byte("data")) err := off.HasBlock(context.Background(), *block) if err != nil { From 56794d07ae11c601f53896df4c1b559199131196 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 20 Nov 2014 18:08:43 -0800 Subject: [PATCH 0432/3817] refactor(blockstore, blockservice) use Blockstore and offline.Exchange License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-blockservice@7c1c42663db22e45d55e1fdcea5284b50384f1ab --- blockservice/blocks_test.go | 7 +++-- blockservice/blockservice.go | 59 +++++++++++++++--------------------- 2 files changed, 30 insertions(+), 36 deletions(-) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index 1e837eb5d..9f579c530 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -6,15 +6,18 @@ import ( "time" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" blocks "github.com/jbenet/go-ipfs/blocks" + blockstore "github.com/jbenet/go-ipfs/blocks/blockstore" + offline "github.com/jbenet/go-ipfs/exchange/offline" u "github.com/jbenet/go-ipfs/util" ) func TestBlocks(t *testing.T) { d := ds.NewMapDatastore() - bs, err := NewBlockService(d, nil) + tsds := dssync.MutexWrap(d) + bs, err := New(blockstore.NewBlockstore(tsds), offline.Exchange()) if err != nil { t.Error("failed to construct block service", err) return diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 279e3ffd9..86e0c776b 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -9,9 +9,9 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" blocks "github.com/jbenet/go-ipfs/blocks" + "github.com/jbenet/go-ipfs/blocks/blockstore" exchange "github.com/jbenet/go-ipfs/exchange" u "github.com/jbenet/go-ipfs/util" ) @@ -19,25 +19,28 @@ import ( var log = u.Logger("blockservice") var ErrNotFound = errors.New("blockservice: key not found") -// BlockService is a block datastore. +// BlockService is a hybrid block datastore. It stores data in a local +// datastore and may retrieve data from a remote Exchange. // It uses an internal `datastore.Datastore` instance to store values. type BlockService struct { - Datastore ds.Datastore - Remote exchange.Interface + // TODO don't expose underlying impl details + Blockstore blockstore.Blockstore + Remote exchange.Interface } // NewBlockService creates a BlockService with given datastore instance. -func NewBlockService(d ds.Datastore, rem exchange.Interface) (*BlockService, error) { - if d == nil { - return nil, fmt.Errorf("BlockService requires valid datastore") +func New(bs blockstore.Blockstore, rem exchange.Interface) (*BlockService, error) { + if bs == nil { + return nil, fmt.Errorf("BlockService requires valid blockstore") } if rem == nil { log.Warning("blockservice running in local (offline) mode.") } - return &BlockService{Datastore: d, Remote: rem}, nil + return &BlockService{Blockstore: bs, Remote: rem}, nil } // AddBlock adds a particular block to the service, Putting it into the datastore. +// TODO pass a context into this if the remote.HasBlock is going to remain here. func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { k := b.Key() log.Debugf("blockservice: storing [%s] in datastore", k) @@ -47,7 +50,7 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { // check if we have it before adding. this is an extra read, but large writes // are more expensive. // TODO(jbenet) cheaper has. https://github.com/jbenet/go-datastore/issues/6 - has, err := s.Datastore.Has(k.DsKey()) + has, err := s.Blockstore.Has(k) if err != nil { return k, err } @@ -55,12 +58,14 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { log.Debugf("blockservice: storing [%s] in datastore (already stored)", k) } else { log.Debugf("blockservice: storing [%s] in datastore", k) - err := s.Datastore.Put(k.DsKey(), b.Data) + err := s.Blockstore.Put(b) if err != nil { return k, err } } + // TODO this operation rate-limits blockservice operations, we should + // consider moving this to an sync process. if s.Remote != nil { ctx := context.TODO() err = s.Remote.HasBlock(ctx, *b) @@ -72,17 +77,11 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { // Getting it from the datastore using the key (hash). func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, error) { log.Debugf("BlockService GetBlock: '%s'", k) - datai, err := s.Datastore.Get(k.DsKey()) + block, err := s.Blockstore.Get(k) if err == nil { - log.Debug("Blockservice: Got data in datastore.") - bdata, ok := datai.([]byte) - if !ok { - return nil, fmt.Errorf("data associated with %s is not a []byte", k) - } - return &blocks.Block{ - Multihash: mh.Multihash(k), - Data: bdata, - }, nil + return block, nil + // TODO be careful checking ErrNotFound. If the underlying + // implementation changes, this will break. } else if err == ds.ErrNotFound && s.Remote != nil { log.Debug("Blockservice: Searching bitswap.") blk, err := s.Remote.GetBlock(ctx, k) @@ -101,21 +100,13 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks go func() { var toFetch []u.Key for _, k := range ks { - datai, err := s.Datastore.Get(k.DsKey()) - if err == nil { - log.Debug("Blockservice: Got data in datastore.") - bdata, ok := datai.([]byte) - if !ok { - log.Criticalf("data associated with %s is not a []byte", k) - continue - } - out <- &blocks.Block{ - Multihash: mh.Multihash(k), - Data: bdata, - } - } else { + block, err := s.Blockstore.Get(k) + if err != nil { toFetch = append(toFetch, k) + continue } + log.Debug("Blockservice: Got data in datastore.") + out <- block } }() return out @@ -123,5 +114,5 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks // DeleteBlock deletes a block in the blockservice from the datastore func (s *BlockService) DeleteBlock(k u.Key) error { - return s.Datastore.Delete(k.DsKey()) + return s.Blockstore.DeleteBlock(k) } From 2639d0aa45daa4cbf8f8ed9af9c16e5a2c9eba0f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 20 Nov 2014 18:08:43 -0800 Subject: [PATCH 0433/3817] refactor(blockstore, blockservice) use Blockstore and offline.Exchange License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-blockstore@75f682bea3ce0ab416308e8526661a3bfbcacb6b --- blockstore/blockstore.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index b4c0fd7fb..68ccc7c74 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -15,6 +15,8 @@ import ( var ValueTypeMismatch = errors.New("The retrieved value is not a Block") type Blockstore interface { + DeleteBlock(u.Key) error + Has(u.Key) (bool, error) Get(u.Key) (*blocks.Block, error) Put(*blocks.Block) error } @@ -45,3 +47,11 @@ func (bs *blockstore) Get(k u.Key) (*blocks.Block, error) { func (bs *blockstore) Put(block *blocks.Block) error { return bs.datastore.Put(block.Key().DsKey(), block.Data) } + +func (bs *blockstore) Has(k u.Key) (bool, error) { + return bs.datastore.Has(k.DsKey()) +} + +func (s *blockstore) DeleteBlock(k u.Key) error { + return s.datastore.Delete(k.DsKey()) +} From 7bb9f7517d296b003854cfbc6cf72b5c96179e67 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 20 Nov 2014 18:08:43 -0800 Subject: [PATCH 0434/3817] refactor(blockstore, blockservice) use Blockstore and offline.Exchange License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-pinner@1a7d93a3332c8a458c1224fbb578e8eb616aac00 --- pinning/pinner/pin_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 1ea302823..fc9dc215d 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -4,7 +4,10 @@ import ( "testing" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/jbenet/go-ipfs/blocks/blockstore" bs "github.com/jbenet/go-ipfs/blockservice" + "github.com/jbenet/go-ipfs/exchange/offline" mdag "github.com/jbenet/go-ipfs/merkledag" "github.com/jbenet/go-ipfs/util" ) @@ -19,13 +22,15 @@ func randNode() (*mdag.Node, util.Key) { func TestPinnerBasic(t *testing.T) { dstore := ds.NewMapDatastore() - bserv, err := bs.NewBlockService(dstore, nil) + bstore := blockstore.NewBlockstore(dssync.MutexWrap(dstore)) + bserv, err := bs.New(bstore, offline.Exchange()) if err != nil { t.Fatal(err) } dserv := mdag.NewDAGService(bserv) + // TODO does pinner need to share datastore with blockservice? p := NewPinner(dstore, dserv) a, ak := randNode() From 39ff4d0b23ba47267e05dcbade3ce8dc3c64e5a7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 05:38:13 +0000 Subject: [PATCH 0435/3817] revamp BatchFetch a bit This commit was moved from ipfs/go-unixfs@b8fdc22653d55354b09b4287ec2db1944572a5a2 --- unixfs/io/dagreader.go | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index ea33c3540..ec1b21bfe 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -5,6 +5,8 @@ import ( "errors" "io" + "code.google.com/p/go.net/context" + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" mdag "github.com/jbenet/go-ipfs/merkledag" ft "github.com/jbenet/go-ipfs/unixfs" @@ -15,10 +17,10 @@ var ErrIsDir = errors.New("this dag node is a directory") // DagReader provides a way to easily read the data contained in a dag. type DagReader struct { - serv mdag.DAGService - node *mdag.Node - position int - buf io.Reader + serv mdag.DAGService + node *mdag.Node + buf io.Reader + fetchChan <-chan *mdag.Node } // NewDagReader creates a new reader object that reads the data represented by the given @@ -36,9 +38,10 @@ func NewDagReader(n *mdag.Node, serv mdag.DAGService) (io.Reader, error) { return nil, ErrIsDir case ftpb.Data_File: return &DagReader{ - node: n, - serv: serv, - buf: bytes.NewBuffer(pb.GetData()), + node: n, + serv: serv, + buf: bytes.NewBuffer(pb.GetData()), + fetchChan: serv.BatchFetch(context.TODO(), n), }, nil case ftpb.Data_Raw: // Raw block will just be a single level, return a byte buffer @@ -51,19 +54,20 @@ func NewDagReader(n *mdag.Node, serv mdag.DAGService) (io.Reader, error) { // precalcNextBuf follows the next link in line and loads it from the DAGService, // setting the next buffer to read from func (dr *DagReader) precalcNextBuf() error { - if dr.position >= len(dr.node.Links) { - return io.EOF - } - nxt, err := dr.node.Links[dr.position].GetNode(dr.serv) - if err != nil { - return err + var nxt *mdag.Node + var ok bool + select { + case nxt, ok = <-dr.fetchChan: + if !ok { + return io.EOF + } } + pb := new(ftpb.Data) - err = proto.Unmarshal(nxt.Data, pb) + err := proto.Unmarshal(nxt.Data, pb) if err != nil { return err } - dr.position++ switch pb.GetType() { case ftpb.Data_Directory: From 42a6ad78764c145e03964ed298eeaeeb9f8383cd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 05:38:13 +0000 Subject: [PATCH 0436/3817] revamp BatchFetch a bit This commit was moved from ipfs/go-merkledag@24f8c9384db39477d4cf60d13cc8ff0e73d98e10 --- ipld/merkledag/merkledag.go | 71 ++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 2483ce4d9..bdb258444 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -22,6 +22,19 @@ var ErrNotFound = fmt.Errorf("merkledag: not found") // so have to convert Multihash bytes to string (u.Key) type NodeMap map[u.Key]*Node +// DAGService is an IPFS Merkle DAG service. +type DAGService interface { + Add(*Node) (u.Key, error) + AddRecursive(*Node) error + Get(u.Key) (*Node, error) + Remove(*Node) error + BatchFetch(context.Context, *Node) <-chan *Node +} + +func NewDAGService(bs *bserv.BlockService) DAGService { + return &dagService{bs} +} + // Node represents a node in the IPFS Merkle DAG. // nodes have opaque data and a set of navigable links. type Node struct { @@ -156,18 +169,6 @@ func (n *Node) Key() (u.Key, error) { return u.Key(h), err } -// DAGService is an IPFS Merkle DAG service. -type DAGService interface { - Add(*Node) (u.Key, error) - AddRecursive(*Node) error - Get(u.Key) (*Node, error) - Remove(*Node) error -} - -func NewDAGService(bs *bserv.BlockService) DAGService { - return &dagService{bs} -} - // dagService is an IPFS Merkle DAG service. // - the root is virtual (like a forest) // - stores nodes' data in a BlockService @@ -286,59 +287,55 @@ func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} return done } -// Take advantage of blockservice/bitswap batched requests to fetch all -// child nodes of a given node -// TODO: finish this -func (ds *dagService) BatchFetch(ctx context.Context, root *Node) <-chan int { - sig := make(chan int) +// BatchFetch will fill out all of the links of the given Node. +// It returns a channel of indicies, which will be returned in order +// from 0 to len(root.Links) - 1, signalling that the link specified by +// the index has been filled out. +func (ds *dagService) BatchFetch(ctx context.Context, root *Node) <-chan *Node { + sig := make(chan *Node) go func() { var keys []u.Key - for _, lnk := range root.Links { - keys = append(keys, u.Key(lnk.Hash)) - } - - blkchan := ds.Blocks.GetBlocks(ctx, keys) + nodes := make([]*Node, len(root.Links)) // next := 0 seen := make(map[int]struct{}) // + for _, lnk := range root.Links { + keys = append(keys, u.Key(lnk.Hash)) + } + + blkchan := ds.Blocks.GetBlocks(ctx, keys) + for blk := range blkchan { for i, lnk := range root.Links { + if u.Key(lnk.Hash) != blk.Key() { + continue + } // seen[i] = struct{}{} // - if u.Key(lnk.Hash) != blk.Key() { - continue - } nd, err := Decoded(blk.Data) if err != nil { log.Error("Got back bad block!") break } - lnk.Node = nd + nodes[i] = nd - // if next == i { - sig <- next + sig <- nd next++ - for { - if _, ok := seen[next]; ok { - sig <- next - next++ - } else { - break - } + for ; nodes[next] != nil; next++ { + sig <- nodes[next] } } - // } } + close(sig) }() - // TODO: return a channel, and signal when the 'Next' readable block is available return sig } From 136d53684679755c251408a5c3d6540237c18c35 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 20 Nov 2014 18:34:42 -0800 Subject: [PATCH 0437/3817] fix(exchange) allow exchange to be closed License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-exchange-offline@373e19cd2194287f5c109b03c0636044751754ec --- exchange/offline/offline.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index fbb3485c0..893f546a9 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -20,8 +20,7 @@ func Exchange() exchange.Interface { // offlineExchange implements the Exchange interface but doesn't return blocks. // For use in offline mode. -type offlineExchange struct { -} +type offlineExchange struct{} // GetBlock returns nil to signal that a block could not be retrieved for the // given key. @@ -34,3 +33,8 @@ func (_ *offlineExchange) GetBlock(context.Context, u.Key) (*blocks.Block, error func (_ *offlineExchange) HasBlock(context.Context, blocks.Block) error { return nil } + +// Close always returns nil. +func (_ *offlineExchange) Close() error { + return nil +} From 617b8c27e0a6c4b0405e721b2bbf32f5e5b373c3 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 20 Nov 2014 18:34:42 -0800 Subject: [PATCH 0438/3817] fix(exchange) allow exchange to be closed License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-exchange-interface@c6503f1678c300401195be13299f35492217c453 --- exchange/interface.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index b62a47957..1f126eed3 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -2,6 +2,8 @@ package exchange import ( + "io" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" blocks "github.com/jbenet/go-ipfs/blocks" @@ -11,11 +13,12 @@ import ( // Any type that implements exchange.Interface may be used as an IPFS block // exchange protocol. type Interface interface { - // GetBlock returns the block associated with a given key. GetBlock(context.Context, u.Key) (*blocks.Block, error) // TODO Should callers be concerned with whether the block was made // available on the network? HasBlock(context.Context, blocks.Block) error + + io.Closer } From c7020914bb0aa41ec9b925cb72ddf25edd41d125 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 06:40:34 +0000 Subject: [PATCH 0439/3817] wire GetBlocks into blockservice This commit was moved from ipfs/go-blockservice@e5247b67fe126aef42f9fc90a7522e92f4b606c2 --- blockservice/blockservice.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 86e0c776b..96234c12a 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -68,7 +68,7 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { // consider moving this to an sync process. if s.Remote != nil { ctx := context.TODO() - err = s.Remote.HasBlock(ctx, *b) + err = s.Remote.HasBlock(ctx, b) } return k, err } @@ -98,6 +98,7 @@ func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, er func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks.Block { out := make(chan *blocks.Block, 32) go func() { + defer close(out) var toFetch []u.Key for _, k := range ks { block, err := s.Blockstore.Get(k) @@ -108,6 +109,15 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks log.Debug("Blockservice: Got data in datastore.") out <- block } + + nblocks, err := s.Remote.GetBlocks(ctx, toFetch) + if err != nil { + log.Errorf("Error with GetBlocks: %s", err) + return + } + for blk := range nblocks { + out <- blk + } }() return out } From a48a65e61dac7b1a3137d399c967532d2689fe10 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 06:40:34 +0000 Subject: [PATCH 0440/3817] wire GetBlocks into blockservice This commit was moved from ipfs/go-unixfs@f62fd5b204f0818ce2ad43fdc0263adc28d66aba --- unixfs/io/dagreader.go | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index ec1b21bfe..7f8720cf1 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -17,10 +17,11 @@ var ErrIsDir = errors.New("this dag node is a directory") // DagReader provides a way to easily read the data contained in a dag. type DagReader struct { - serv mdag.DAGService - node *mdag.Node - buf io.Reader - fetchChan <-chan *mdag.Node + serv mdag.DAGService + node *mdag.Node + buf io.Reader + fetchChan <-chan *mdag.Node + linkPosition int } // NewDagReader creates a new reader object that reads the data represented by the given @@ -37,11 +38,15 @@ func NewDagReader(n *mdag.Node, serv mdag.DAGService) (io.Reader, error) { // Dont allow reading directories return nil, ErrIsDir case ftpb.Data_File: + var fetchChan <-chan *mdag.Node + if serv != nil { + fetchChan = serv.BatchFetch(context.TODO(), n) + } return &DagReader{ node: n, serv: serv, buf: bytes.NewBuffer(pb.GetData()), - fetchChan: serv.BatchFetch(context.TODO(), n), + fetchChan: fetchChan, }, nil case ftpb.Data_Raw: // Raw block will just be a single level, return a byte buffer @@ -61,6 +66,17 @@ func (dr *DagReader) precalcNextBuf() error { if !ok { return io.EOF } + default: + // Only used when fetchChan is nil, + // which only happens when passed in a nil dagservice + // TODO: this logic is hard to follow, do it better. + // NOTE: the only time this code is used, is during the + // importer tests, consider just changing those tests + if dr.linkPosition >= len(dr.node.Links) { + return io.EOF + } + nxt = dr.node.Links[dr.linkPosition].Node + dr.linkPosition++ } pb := new(ftpb.Data) From 9998cde2e607c775cc786b256a611349e01c6f95 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 08:01:34 +0000 Subject: [PATCH 0441/3817] some cleanup, and fix minor bug in dagreader from previous commit This commit was moved from ipfs/go-blockservice@0f4376e991c3dd02e36aa1d514c5a93e4faa769f --- blockservice/blockservice.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 96234c12a..97c7dec90 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -95,6 +95,9 @@ func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, er } } +// GetBlocks gets a list of blocks asynchronously and returns through +// the returned channel. +// NB: No guarantees are made about order. func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks.Block { out := make(chan *blocks.Block, 32) go func() { From aaca83e4fd507e7207d47f6b4cda201f60397b2c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 06:40:34 +0000 Subject: [PATCH 0442/3817] wire GetBlocks into blockservice This commit was moved from ipfs/go-merkledag@e7dee529c22eaf4873bce7a12dc3ebec9b0beb05 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index bdb258444..2fba2f5fa 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -328,7 +328,7 @@ func (ds *dagService) BatchFetch(ctx context.Context, root *Node) <-chan *Node { if next == i { sig <- nd next++ - for ; nodes[next] != nil; next++ { + for ; next < len(nodes) && nodes[next] != nil; next++ { sig <- nodes[next] } } From 5e280f1bbc9cd38780068aa6b223d858e1b674aa Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 08:01:34 +0000 Subject: [PATCH 0443/3817] some cleanup, and fix minor bug in dagreader from previous commit This commit was moved from ipfs/go-unixfs@fd2b8faf76f49ea8955b2ed796b1cc830869bf37 --- unixfs/io/dagreader.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 7f8720cf1..892752c91 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -61,12 +61,8 @@ func NewDagReader(n *mdag.Node, serv mdag.DAGService) (io.Reader, error) { func (dr *DagReader) precalcNextBuf() error { var nxt *mdag.Node var ok bool - select { - case nxt, ok = <-dr.fetchChan: - if !ok { - return io.EOF - } - default: + + if dr.serv == nil { // Only used when fetchChan is nil, // which only happens when passed in a nil dagservice // TODO: this logic is hard to follow, do it better. @@ -76,7 +72,18 @@ func (dr *DagReader) precalcNextBuf() error { return io.EOF } nxt = dr.node.Links[dr.linkPosition].Node + if nxt == nil { + return errors.New("Got nil node back from link! and no DAGService!") + } dr.linkPosition++ + + } else { + select { + case nxt, ok = <-dr.fetchChan: + if !ok { + return io.EOF + } + } } pb := new(ftpb.Data) From ac017be45605e4ac2a25498fd2df3ae6519fcd69 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 18:14:28 +0000 Subject: [PATCH 0444/3817] tracking down a bug dhthell found, added asserts and better logging. This commit was moved from ipfs/go-blockservice@acd2765e33922b823f3da287a62ac91042933b5c --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 97c7dec90..c4c90c88b 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -101,7 +101,6 @@ func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, er func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks.Block { out := make(chan *blocks.Block, 32) go func() { - defer close(out) var toFetch []u.Key for _, k := range ks { block, err := s.Blockstore.Get(k) @@ -121,6 +120,7 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks for blk := range nblocks { out <- blk } + close(out) }() return out } From 44542cc75c0c86b5a7932a96c84cff57d9550b30 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 08:01:34 +0000 Subject: [PATCH 0445/3817] some cleanup, and fix minor bug in dagreader from previous commit This commit was moved from ipfs/go-merkledag@d651a3764bc55dfd039abc077c3c18d7cd7e6b12 --- ipld/merkledag/merkledag.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 2fba2f5fa..453f515e5 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -288,9 +288,8 @@ func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} } // BatchFetch will fill out all of the links of the given Node. -// It returns a channel of indicies, which will be returned in order -// from 0 to len(root.Links) - 1, signalling that the link specified by -// the index has been filled out. +// It returns a channel of nodes, which the caller can receive +// all the child nodes of 'root' on, in proper order. func (ds *dagService) BatchFetch(ctx context.Context, root *Node) <-chan *Node { sig := make(chan *Node) go func() { @@ -299,7 +298,6 @@ func (ds *dagService) BatchFetch(ctx context.Context, root *Node) <-chan *Node { // next := 0 - seen := make(map[int]struct{}) // for _, lnk := range root.Links { @@ -314,10 +312,6 @@ func (ds *dagService) BatchFetch(ctx context.Context, root *Node) <-chan *Node { continue } - // - seen[i] = struct{}{} - // - nd, err := Decoded(blk.Data) if err != nil { log.Error("Got back bad block!") From 970d7cfd55b893e938bbb762d06182707ff198dd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 18:14:28 +0000 Subject: [PATCH 0446/3817] tracking down a bug dhthell found, added asserts and better logging. This commit was moved from ipfs/go-unixfs@35dc51426c070000264f89945eb81819af3dc105 --- unixfs/io/dagreader.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 892752c91..7373b94ae 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -68,6 +68,7 @@ func (dr *DagReader) precalcNextBuf() error { // TODO: this logic is hard to follow, do it better. // NOTE: the only time this code is used, is during the // importer tests, consider just changing those tests + log.Warning("Running DAGReader with nil DAGService!") if dr.linkPosition >= len(dr.node.Links) { return io.EOF } @@ -78,6 +79,9 @@ func (dr *DagReader) precalcNextBuf() error { dr.linkPosition++ } else { + if dr.fetchChan == nil { + panic("this is wrong.") + } select { case nxt, ok = <-dr.fetchChan: if !ok { From 9c2a75faaabfa3a19bc14507699f874de201e147 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 06:40:34 +0000 Subject: [PATCH 0447/3817] wire GetBlocks into blockservice This commit was moved from ipfs/go-ipfs-exchange-offline@e466b9dd25312a727fa6c25bc22ae2ffd64c693e --- exchange/offline/offline.go | 6 +++++- exchange/offline/offline_test.go | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 893f546a9..24a89e038 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -30,7 +30,7 @@ func (_ *offlineExchange) GetBlock(context.Context, u.Key) (*blocks.Block, error } // HasBlock always returns nil. -func (_ *offlineExchange) HasBlock(context.Context, blocks.Block) error { +func (_ *offlineExchange) HasBlock(context.Context, *blocks.Block) error { return nil } @@ -38,3 +38,7 @@ func (_ *offlineExchange) HasBlock(context.Context, blocks.Block) error { func (_ *offlineExchange) Close() error { return nil } + +func (_ *offlineExchange) GetBlocks(context.Context, []u.Key) (<-chan *blocks.Block, error) { + return nil, OfflineMode +} diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 98b6e1a8c..ac02d2101 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -21,7 +21,7 @@ func TestBlockReturnsErr(t *testing.T) { func TestHasBlockReturnsNil(t *testing.T) { off := Exchange() block := blocks.NewBlock([]byte("data")) - err := off.HasBlock(context.Background(), *block) + err := off.HasBlock(context.Background(), block) if err != nil { t.Fatal("") } From 09728fba212211187de5e2fb6ebe17e29b125bc4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 06:40:34 +0000 Subject: [PATCH 0448/3817] wire GetBlocks into blockservice This commit was moved from ipfs/go-ipfs-exchange-interface@35ac113f992ed6aacfe01955f0961037942945c1 --- exchange/interface.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index 1f126eed3..aa2e2431c 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -16,9 +16,11 @@ type Interface interface { // GetBlock returns the block associated with a given key. GetBlock(context.Context, u.Key) (*blocks.Block, error) + GetBlocks(context.Context, []u.Key) (<-chan *blocks.Block, error) + // TODO Should callers be concerned with whether the block was made // available on the network? - HasBlock(context.Context, blocks.Block) error + HasBlock(context.Context, *blocks.Block) error io.Closer } From 48026282d91135d5d96209b9c93d46ce959c2558 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 23:03:05 +0000 Subject: [PATCH 0449/3817] a little more correctness on the new bitswap impl This commit was moved from ipfs/go-blockservice@725e2d33baf37e49538c0c00626666df7fa281ad --- blockservice/blockservice.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index c4c90c88b..214bd49fc 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -98,7 +98,7 @@ func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, er // GetBlocks gets a list of blocks asynchronously and returns through // the returned channel. // NB: No guarantees are made about order. -func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks.Block { +func (s *BlockService) GetBlocks(parent context.Context, ks []u.Key) <-chan *blocks.Block { out := make(chan *blocks.Block, 32) go func() { var toFetch []u.Key @@ -112,11 +112,13 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks out <- block } + ctx, cancel := context.WithCancel(parent) nblocks, err := s.Remote.GetBlocks(ctx, toFetch) if err != nil { log.Errorf("Error with GetBlocks: %s", err) return } + for blk := range nblocks { out <- blk } From 28250d85525b0a4dad3ee6ed8f45558ecd024f05 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 23:33:33 +0000 Subject: [PATCH 0450/3817] use @maybebtc's ForwardBlocks function This commit was moved from ipfs/go-blockservice@dba69eb8be43784c7ce99c861882952f1841734a --- blockservice/blockservice.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 214bd49fc..07e6d1092 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -98,7 +98,7 @@ func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, er // GetBlocks gets a list of blocks asynchronously and returns through // the returned channel. // NB: No guarantees are made about order. -func (s *BlockService) GetBlocks(parent context.Context, ks []u.Key) <-chan *blocks.Block { +func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks.Block { out := make(chan *blocks.Block, 32) go func() { var toFetch []u.Key @@ -112,7 +112,6 @@ func (s *BlockService) GetBlocks(parent context.Context, ks []u.Key) <-chan *blo out <- block } - ctx, cancel := context.WithCancel(parent) nblocks, err := s.Remote.GetBlocks(ctx, toFetch) if err != nil { log.Errorf("Error with GetBlocks: %s", err) From 54954bf833cdf92eb0275115a0c32d62984a7178 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 21 Nov 2014 23:03:05 +0000 Subject: [PATCH 0451/3817] a little more correctness on the new bitswap impl This commit was moved from ipfs/go-merkledag@c52fb3bec2787075dbc6eb822e2abffe13309aaa --- ipld/merkledag/merkledag.go | 57 ++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 453f515e5..2673c59fc 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -163,6 +163,17 @@ func (n *Node) Multihash() (mh.Multihash, error) { return n.cached, nil } +// Searches this nodes links for one to the given key, +// returns the index of said link +func (n *Node) FindLink(k u.Key) (int, error) { + for i, lnk := range n.Links { + if u.Key(lnk.Hash) == k { + return i, nil + } + } + return -1, u.ErrNotFound +} + // Key returns the Multihash as a key, for maps. func (n *Node) Key() (u.Key, error) { h, err := n.Multihash() @@ -296,6 +307,10 @@ func (ds *dagService) BatchFetch(ctx context.Context, root *Node) <-chan *Node { var keys []u.Key nodes := make([]*Node, len(root.Links)) + //temp + recvd := []int{} + // + // next := 0 // @@ -306,28 +321,36 @@ func (ds *dagService) BatchFetch(ctx context.Context, root *Node) <-chan *Node { blkchan := ds.Blocks.GetBlocks(ctx, keys) + count := 0 for blk := range blkchan { - for i, lnk := range root.Links { - if u.Key(lnk.Hash) != blk.Key() { - continue - } + count++ + i, err := root.FindLink(blk.Key()) + if err != nil { + panic("Received block that wasnt in this nodes links!") + } - nd, err := Decoded(blk.Data) - if err != nil { - log.Error("Got back bad block!") - break - } - nodes[i] = nd - - if next == i { - sig <- nd - next++ - for ; next < len(nodes) && nodes[next] != nil; next++ { - sig <- nodes[next] - } + recvd = append(recvd, i) + + nd, err := Decoded(blk.Data) + if err != nil { + log.Error("Got back bad block!") + break + } + nodes[i] = nd + + if next == i { + sig <- nd + next++ + for ; next < len(nodes) && nodes[next] != nil; next++ { + sig <- nodes[next] } } } + if next < len(nodes) { + log.Errorf("count = %d, links = %d", count, len(nodes)) + log.Error(recvd) + panic("didnt receive all requested blocks!") + } close(sig) }() From 239829d7386a54bdf0c737007be3d273cb26f0bf Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 22 Nov 2014 22:27:19 +0000 Subject: [PATCH 0452/3817] ensure sending of wantlist to friendly peers This commit was moved from ipfs/go-blockservice@ded8dd609c308c4d0308ea4f07e1f39e07f884bc --- blockservice/blocks_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index 9f579c530..1779c0c83 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -10,6 +10,7 @@ import ( dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" blocks "github.com/jbenet/go-ipfs/blocks" blockstore "github.com/jbenet/go-ipfs/blocks/blockstore" + bitswap "github.com/jbenet/go-ipfs/exchange/bitswap" offline "github.com/jbenet/go-ipfs/exchange/offline" u "github.com/jbenet/go-ipfs/util" ) @@ -58,3 +59,6 @@ func TestBlocks(t *testing.T) { t.Error("Block data is not equal.") } } + +func TestGetBlocks(t *testing.T) { +} From 58c557228f8d15d7a3fa094ea768c99fde8ad770 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 23 Nov 2014 19:14:06 +0000 Subject: [PATCH 0453/3817] add a test to blockservice to demonstate GetBlocks failure. This commit was moved from ipfs/go-blockservice@0f5ebd5c1cb38feca4739dfd420548cdfa88177d --- blockservice/blocks_test.go | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index 1779c0c83..fd4ac34c3 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -11,7 +11,9 @@ import ( blocks "github.com/jbenet/go-ipfs/blocks" blockstore "github.com/jbenet/go-ipfs/blocks/blockstore" bitswap "github.com/jbenet/go-ipfs/exchange/bitswap" + tn "github.com/jbenet/go-ipfs/exchange/bitswap/testnet" offline "github.com/jbenet/go-ipfs/exchange/offline" + "github.com/jbenet/go-ipfs/routing/mock" u "github.com/jbenet/go-ipfs/util" ) @@ -61,4 +63,42 @@ func TestBlocks(t *testing.T) { } func TestGetBlocks(t *testing.T) { + net := tn.VirtualNetwork() + rs := mock.VirtualRoutingServer() + sg := bitswap.NewSessionGenerator(net, rs) + bg := bitswap.NewBlockGenerator() + + instances := sg.Instances(4) + blks := bg.Blocks(50) + // TODO: verify no duplicates + + var servs []*BlockService + for _, i := range instances { + bserv, err := New(i.Blockstore, i.Exchange) + if err != nil { + t.Fatal(err) + } + servs = append(servs, bserv) + } + + var keys []u.Key + for _, blk := range blks { + keys = append(keys, blk.Key()) + servs[0].AddBlock(blk) + } + + for i := 1; i < 4; i++ { + ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) + out := servs[i].GetBlocks(ctx, keys) + gotten := make(map[u.Key]*blocks.Block) + for blk := range out { + if _, ok := gotten[blk.Key()]; ok { + t.Fatal("Got duplicate block!") + } + gotten[blk.Key()] = blk + } + if len(gotten) != len(blks) { + t.Fatalf("Didnt get enough blocks back: %d/%d", len(gotten), len(blks)) + } + } } From 896ba6e4baed8813abb13c27a8c3cb5d958fd16d Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 23 Nov 2014 18:27:02 -0800 Subject: [PATCH 0454/3817] fix(blockservice) test License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-blockservice@10090dc756a3fe2755dc04d4dd4967de5bafe346 --- blockservice/blocks_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index fd4ac34c3..d9b2facb1 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -87,9 +87,14 @@ func TestGetBlocks(t *testing.T) { servs[0].AddBlock(blk) } + var chans []<-chan *blocks.Block for i := 1; i < 4; i++ { ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) - out := servs[i].GetBlocks(ctx, keys) + ch := servs[i].GetBlocks(ctx, keys) + chans = append(chans, ch) + } + + for _, out := range chans { gotten := make(map[u.Key]*blocks.Block) for blk := range out { if _, ok := gotten[blk.Key()]; ok { From b93dcbd5a588afe0849031fbaea93762e893086f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 23 Nov 2014 22:47:53 -0800 Subject: [PATCH 0455/3817] fix(blockservice) respect context in GetBlocks License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-blockservice@75d100016aab67e003ba034eea0c23e90f1f4eb9 --- blockservice/blockservice.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 07e6d1092..0ddec7955 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -99,29 +99,37 @@ func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, er // the returned channel. // NB: No guarantees are made about order. func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks.Block { - out := make(chan *blocks.Block, 32) + out := make(chan *blocks.Block, 0) go func() { - var toFetch []u.Key + defer close(out) + var misses []u.Key for _, k := range ks { - block, err := s.Blockstore.Get(k) + hit, err := s.Blockstore.Get(k) if err != nil { - toFetch = append(toFetch, k) + misses = append(misses, k) continue } log.Debug("Blockservice: Got data in datastore.") - out <- block + select { + case out <- hit: + case <-ctx.Done(): + return + } } - nblocks, err := s.Remote.GetBlocks(ctx, toFetch) + rblocks, err := s.Remote.GetBlocks(ctx, misses) if err != nil { log.Errorf("Error with GetBlocks: %s", err) return } - for blk := range nblocks { - out <- blk + for b := range rblocks { + select { + case out <- b: + case <-ctx.Done(): + return + } } - close(out) }() return out } From b3447aa64e1109cef665723897c723737b3e43fd Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 23 Nov 2014 22:47:06 -0800 Subject: [PATCH 0456/3817] fix(bitswap/testutils) vendor License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-unixfs@aa935e76a1b86b58e92f5c57df5f63a416cc5342 --- unixfs/io/dagreader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 7373b94ae..55e677386 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -5,7 +5,7 @@ import ( "errors" "io" - "code.google.com/p/go.net/context" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" mdag "github.com/jbenet/go-ipfs/merkledag" From 9c0565b1e77ba5018f4fd3230ce13121da867be6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 24 Nov 2014 08:28:48 +0000 Subject: [PATCH 0457/3817] fix issues in merkledag This commit was moved from ipfs/go-merkledag@eba3c6bd4c775d59a5bc13c4167e6d41da12b394 --- ipld/merkledag/merkledag.go | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 2673c59fc..06381bacf 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -163,17 +163,6 @@ func (n *Node) Multihash() (mh.Multihash, error) { return n.cached, nil } -// Searches this nodes links for one to the given key, -// returns the index of said link -func (n *Node) FindLink(k u.Key) (int, error) { - for i, lnk := range n.Links { - if u.Key(lnk.Hash) == k { - return i, nil - } - } - return -1, u.ErrNotFound -} - // Key returns the Multihash as a key, for maps. func (n *Node) Key() (u.Key, error) { h, err := n.Multihash() @@ -298,6 +287,17 @@ func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} return done } +// Searches this nodes links for one to the given key, +// returns the index of said link +func FindLink(n *Node, k u.Key, found []*Node) (int, error) { + for i, lnk := range n.Links { + if u.Key(lnk.Hash) == k && found[i] == nil { + return i, nil + } + } + return -1, u.ErrNotFound +} + // BatchFetch will fill out all of the links of the given Node. // It returns a channel of nodes, which the caller can receive // all the child nodes of 'root' on, in proper order. @@ -324,7 +324,7 @@ func (ds *dagService) BatchFetch(ctx context.Context, root *Node) <-chan *Node { count := 0 for blk := range blkchan { count++ - i, err := root.FindLink(blk.Key()) + i, err := FindLink(root, blk.Key(), nodes) if err != nil { panic("Received block that wasnt in this nodes links!") } @@ -356,3 +356,14 @@ func (ds *dagService) BatchFetch(ctx context.Context, root *Node) <-chan *Node { return sig } + +func checkForDupes(ks []u.Key) bool { + seen := make(map[u.Key]struct{}) + for _, k := range ks { + if _, ok := seen[k]; ok { + return true + } + seen[k] = struct{}{} + } + return false +} From d3d5d3e938a237492a6a0346c33ae3c2baac97b8 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 23 Nov 2014 22:59:22 -0800 Subject: [PATCH 0458/3817] reset test to the way it ways before @whyrusleeping License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-blockservice@0966ac421c315c6888bdb3afdca4888e96dcc70e --- blockservice/blocks_test.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index d9b2facb1..9fd85725e 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -62,7 +62,7 @@ func TestBlocks(t *testing.T) { } } -func TestGetBlocks(t *testing.T) { +func TestGetBlocksSequential(t *testing.T) { net := tn.VirtualNetwork() rs := mock.VirtualRoutingServer() sg := bitswap.NewSessionGenerator(net, rs) @@ -87,14 +87,11 @@ func TestGetBlocks(t *testing.T) { servs[0].AddBlock(blk) } - var chans []<-chan *blocks.Block - for i := 1; i < 4; i++ { - ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) - ch := servs[i].GetBlocks(ctx, keys) - chans = append(chans, ch) - } + t.Log("one instance at a time, get blocks concurrently") - for _, out := range chans { + for i := 1; i < len(instances); i++ { + ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) + out := servs[i].GetBlocks(ctx, keys) gotten := make(map[u.Key]*blocks.Block) for blk := range out { if _, ok := gotten[blk.Key()]; ok { From b785d5fe1093ff5ab61a5bec49317bafa8adc81d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 25 Nov 2014 18:45:44 +0000 Subject: [PATCH 0459/3817] add a test in merkledag to exercise GetBlocks This commit was moved from ipfs/go-merkledag@b6ffb6a374e29597424a55d37c9b28e53733fd7c --- ipld/merkledag/merkledag_test.go | 94 +++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 2db166beb..0f628e6c1 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -1,9 +1,20 @@ -package merkledag +package merkledag_test import ( + "bytes" "fmt" + "io" + "io/ioutil" "testing" + bserv "github.com/jbenet/go-ipfs/blockservice" + bs "github.com/jbenet/go-ipfs/exchange/bitswap" + tn "github.com/jbenet/go-ipfs/exchange/bitswap/testnet" + imp "github.com/jbenet/go-ipfs/importer" + chunk "github.com/jbenet/go-ipfs/importer/chunk" + . "github.com/jbenet/go-ipfs/merkledag" + "github.com/jbenet/go-ipfs/routing/mock" + uio "github.com/jbenet/go-ipfs/unixfs/io" u "github.com/jbenet/go-ipfs/util" ) @@ -56,3 +67,84 @@ func TestNode(t *testing.T) { printn("boop", n2) printn("beep boop", n3) } + +func makeTestDag(t *testing.T) *Node { + read := io.LimitReader(u.NewTimeSeededRand(), 1024*32) + spl := &chunk.SizeSplitter{512} + root, err := imp.NewDagFromReaderWithSplitter(read, spl) + if err != nil { + t.Fatal(err) + } + return root +} + +func TestBatchFetch(t *testing.T) { + net := tn.VirtualNetwork() + rs := mock.VirtualRoutingServer() + sg := bs.NewSessionGenerator(net, rs) + + instances := sg.Instances(5) + + var servs []*bserv.BlockService + var dagservs []DAGService + for _, i := range instances { + bsi, err := bserv.New(i.Blockstore, i.Exchange) + if err != nil { + t.Fatal(err) + } + servs = append(servs, bsi) + dagservs = append(dagservs, NewDAGService(bsi)) + } + t.Log("finished setup.") + + root := makeTestDag(t) + read, err := uio.NewDagReader(root, nil) + if err != nil { + t.Fatal(err) + } + expected, err := ioutil.ReadAll(read) + if err != nil { + t.Fatal(err) + } + + err = dagservs[0].AddRecursive(root) + if err != nil { + t.Fatal(err) + } + + t.Log("Added file to first node.") + + k, err := root.Key() + if err != nil { + t.Fatal(err) + } + + done := make(chan struct{}) + for i := 1; i < len(dagservs); i++ { + go func(i int) { + first, err := dagservs[i].Get(k) + if err != nil { + t.Fatal(err) + } + fmt.Println("Got first node back.") + + read, err := uio.NewDagReader(first, dagservs[i]) + if err != nil { + t.Fatal(err) + } + datagot, err := ioutil.ReadAll(read) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(datagot, expected) { + t.Fatal("Got bad data back!") + } + done <- struct{}{} + }(i) + } + + for i := 1; i < len(dagservs); i++ { + <-done + } +} From 434b23afd0adf44c5751a0c5a6e49e598690fa46 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 26 Nov 2014 12:08:40 -0800 Subject: [PATCH 0460/3817] refactor(util) move block generator @whyrusleeping @jbenet Putting the block generator in a util dir until blocks. Can't put it in util/testutil because the util/testutil/dag-generator imports blockservice and blockservice uses the generator. Tough problem. This'll do for now. License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-blockservice@a7bfc2f2024fe3c130e415e35f89e480881473b6 --- blockservice/blocks_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index 9fd85725e..1a75723e2 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -10,6 +10,7 @@ import ( dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" blocks "github.com/jbenet/go-ipfs/blocks" blockstore "github.com/jbenet/go-ipfs/blocks/blockstore" + blocksutil "github.com/jbenet/go-ipfs/blocks/blocksutil" bitswap "github.com/jbenet/go-ipfs/exchange/bitswap" tn "github.com/jbenet/go-ipfs/exchange/bitswap/testnet" offline "github.com/jbenet/go-ipfs/exchange/offline" @@ -66,7 +67,7 @@ func TestGetBlocksSequential(t *testing.T) { net := tn.VirtualNetwork() rs := mock.VirtualRoutingServer() sg := bitswap.NewSessionGenerator(net, rs) - bg := bitswap.NewBlockGenerator() + bg := blocksutil.NewBlockGenerator() instances := sg.Instances(4) blks := bg.Blocks(50) From 56e804e619b15cfb1ae5a2cd1cb851d923eb6c44 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 26 Nov 2014 22:50:41 +0000 Subject: [PATCH 0461/3817] some bitswap cleanup This commit was moved from ipfs/go-unixfs@1e14bec0fe4d31080d33a1d713f59fa791e4c09d --- unixfs/io/dagreader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 55e677386..b41ac3daa 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -40,7 +40,7 @@ func NewDagReader(n *mdag.Node, serv mdag.DAGService) (io.Reader, error) { case ftpb.Data_File: var fetchChan <-chan *mdag.Node if serv != nil { - fetchChan = serv.BatchFetch(context.TODO(), n) + fetchChan = serv.GetKeysAsync(context.TODO(), n) } return &DagReader{ node: n, From 55ffc462f47160c42ab329702dacef8300059b15 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 26 Nov 2014 22:50:41 +0000 Subject: [PATCH 0462/3817] some bitswap cleanup This commit was moved from ipfs/go-merkledag@f3ed34edc5ac66d1aa71235c59806b178d484cf1 --- ipld/merkledag/merkledag.go | 38 +++++++++---------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 06381bacf..7dadf722d 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -28,7 +28,7 @@ type DAGService interface { AddRecursive(*Node) error Get(u.Key) (*Node, error) Remove(*Node) error - BatchFetch(context.Context, *Node) <-chan *Node + GetKeysAsync(context.Context, *Node) <-chan *Node } func NewDAGService(bs *bserv.BlockService) DAGService { @@ -298,41 +298,33 @@ func FindLink(n *Node, k u.Key, found []*Node) (int, error) { return -1, u.ErrNotFound } -// BatchFetch will fill out all of the links of the given Node. +// GetKeysAsync will fill out all of the links of the given Node. // It returns a channel of nodes, which the caller can receive // all the child nodes of 'root' on, in proper order. -func (ds *dagService) BatchFetch(ctx context.Context, root *Node) <-chan *Node { +func (ds *dagService) GetKeysAsync(ctx context.Context, root *Node) <-chan *Node { sig := make(chan *Node) go func() { var keys []u.Key nodes := make([]*Node, len(root.Links)) - //temp - recvd := []int{} - // - - // - next := 0 - // - for _, lnk := range root.Links { keys = append(keys, u.Key(lnk.Hash)) } blkchan := ds.Blocks.GetBlocks(ctx, keys) - count := 0 + next := 0 for blk := range blkchan { - count++ i, err := FindLink(root, blk.Key(), nodes) if err != nil { + // NB: can only occur as a result of programmer error panic("Received block that wasnt in this nodes links!") } - recvd = append(recvd, i) - nd, err := Decoded(blk.Data) if err != nil { + // NB: can occur in normal situations, with improperly formatted + // input data log.Error("Got back bad block!") break } @@ -347,23 +339,11 @@ func (ds *dagService) BatchFetch(ctx context.Context, root *Node) <-chan *Node { } } if next < len(nodes) { - log.Errorf("count = %d, links = %d", count, len(nodes)) - log.Error(recvd) - panic("didnt receive all requested blocks!") + // TODO: bubble errors back up. + log.Errorf("Did not receive correct number of nodes!") } close(sig) }() return sig } - -func checkForDupes(ks []u.Key) bool { - seen := make(map[u.Key]struct{}) - for _, k := range ks { - if _, ok := seen[k]; ok { - return true - } - seen[k] = struct{}{} - } - return false -} From 1aa63332c9fac39ddd960352472453ab4fcef069 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Dec 2014 21:38:16 +0000 Subject: [PATCH 0463/3817] switch over to using sendMessage vs sendRequest This commit was moved from ipfs/go-ipfs-routing@471e5f1f168cd3ef722f95508f9c22d32ab5d958 --- routing/dht/routing.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index fedf281d3..b1644d116 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -126,6 +126,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { } func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int) <-chan peer.Peer { + log.Debug("Find Providers: %s", key) peerOut := make(chan peer.Peer, count) go func() { ps := newPeerSet() From 4bfd707659835ee5412002ff9c806eafeeaf6f4a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Dec 2014 02:15:04 +0000 Subject: [PATCH 0464/3817] cleanup, use a workgroup over channels This commit was moved from ipfs/go-unixfs@e754f7a562316056069ac283199762e459b3e958 --- unixfs/io/dagreader.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index b41ac3daa..f4290dd4b 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -40,7 +40,7 @@ func NewDagReader(n *mdag.Node, serv mdag.DAGService) (io.Reader, error) { case ftpb.Data_File: var fetchChan <-chan *mdag.Node if serv != nil { - fetchChan = serv.GetKeysAsync(context.TODO(), n) + fetchChan = serv.GetDAG(context.TODO(), n) } return &DagReader{ node: n, @@ -62,6 +62,7 @@ func (dr *DagReader) precalcNextBuf() error { var nxt *mdag.Node var ok bool + // TODO: require non-nil dagservice, use offline bitswap exchange if dr.serv == nil { // Only used when fetchChan is nil, // which only happens when passed in a nil dagservice From ed6a65644fd1075a51b92b2b164a4a3a1c6d63e2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Dec 2014 02:15:04 +0000 Subject: [PATCH 0465/3817] cleanup, use a workgroup over channels This commit was moved from ipfs/go-merkledag@3a1aa2faae4953d303d86d693a158a5a1a238f13 --- ipld/merkledag/merkledag.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 7dadf722d..fbb07c9ee 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -28,7 +28,10 @@ type DAGService interface { AddRecursive(*Node) error Get(u.Key) (*Node, error) Remove(*Node) error - GetKeysAsync(context.Context, *Node) <-chan *Node + + // GetDAG returns, in order, all the single leve child + // nodes of the passed in node. + GetDAG(context.Context, *Node) <-chan *Node } func NewDAGService(bs *bserv.BlockService) DAGService { @@ -298,10 +301,10 @@ func FindLink(n *Node, k u.Key, found []*Node) (int, error) { return -1, u.ErrNotFound } -// GetKeysAsync will fill out all of the links of the given Node. +// GetDAG will fill out all of the links of the given Node. // It returns a channel of nodes, which the caller can receive // all the child nodes of 'root' on, in proper order. -func (ds *dagService) GetKeysAsync(ctx context.Context, root *Node) <-chan *Node { +func (ds *dagService) GetDAG(ctx context.Context, root *Node) <-chan *Node { sig := make(chan *Node) go func() { var keys []u.Key From 76b39882005f7d542e18a869c9931cfbe843ced9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 2 Dec 2014 07:34:39 +0000 Subject: [PATCH 0466/3817] make bitswap sub-RPC's timeout (slowly for now) This commit was moved from ipfs/go-ipfs-routing@6d770c18706092342016dd611c2ec5be93893412 --- routing/dht/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index b1644d116..f504b9bb4 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -126,7 +126,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { } func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int) <-chan peer.Peer { - log.Debug("Find Providers: %s", key) + log.Debugf("Find Providers: %s", key) peerOut := make(chan peer.Peer, count) go func() { ps := newPeerSet() From e190bd2efb81791060f38cce0383d56e3a9cf8f0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 3 Dec 2014 19:46:01 +0000 Subject: [PATCH 0467/3817] add readme for bitswap This commit was moved from ipfs/go-ipfs-routing@b7361bc8b406e35e70db9bd62242921e63fb2f3b --- routing/dht/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index f504b9bb4..102acd5a4 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -126,7 +126,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { } func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int) <-chan peer.Peer { - log.Debugf("Find Providers: %s", key) + log.Event(ctx, "findProviders", key) peerOut := make(chan peer.Peer, count) go func() { ps := newPeerSet() From a704ce153ab434540eb069ea64d72e34016839f4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 3 Dec 2014 19:51:17 +0000 Subject: [PATCH 0468/3817] util keys need to be pointers for loggable This commit was moved from ipfs/go-ipfs-routing@ba7bd235a2437cb62ea6b0ba9b5eeb779bb4a7c0 --- routing/dht/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 102acd5a4..f0bfbe485 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -126,7 +126,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { } func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int) <-chan peer.Peer { - log.Event(ctx, "findProviders", key) + log.Event(ctx, "findProviders", &key) peerOut := make(chan peer.Peer, count) go func() { ps := newPeerSet() From a37a06d7caf905953ec9ca14e264c9cf8d0a1538 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 2 Dec 2014 00:19:22 -0800 Subject: [PATCH 0469/3817] fix(routing/dht) _always_ close chan on exit of FindProvidersAsync the important change here is that within FindProvidersAsync, the channel is closed using a `defer`. This ensures the channel is always closed, regardless of the path taken to exit. + misc cleanup cc @whyrusleeping @jbenet License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@6007d1dcf0400e6544f28c9230ef6ba48e59793b --- routing/dht/dht.go | 9 +++------ routing/dht/routing.go | 30 +++++++++++++++++++----------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index f76ca8f59..127cfacc5 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -194,24 +194,21 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *pb.Messa start := time.Now() - log.Event(ctx, "sentMessage", dht.self, p, pmes) - - rmes, err := dht.sender.SendRequest(ctx, mes) + rmes, err := dht.sender.SendRequest(ctx, mes) // respect? if err != nil { return nil, err } if rmes == nil { return nil, errors.New("no response to request") } + log.Event(ctx, "sentMessage", dht.self, p, pmes) - rtt := time.Since(start) - rmes.Peer().SetLatency(rtt) + rmes.Peer().SetLatency(time.Since(start)) rpmes := new(pb.Message) if err := proto.Unmarshal(rmes.Data(), rpmes); err != nil { return nil, err } - return rpmes, nil } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index f0bfbe485..b154b270e 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -129,21 +129,27 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int log.Event(ctx, "findProviders", &key) peerOut := make(chan peer.Peer, count) go func() { + defer close(peerOut) + ps := newPeerSet() + // TODO may want to make this function async to hide latency provs := dht.providers.GetProviders(key) for _, p := range provs { count-- // NOTE: assuming that this list of peers is unique ps.Add(p) - peerOut <- p + select { + case peerOut <- p: + case <-ctx.Done(): + return + } if count <= 0 { return } } - wg := new(sync.WaitGroup) - peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), AlphaValue) - for _, pp := range peers { + var wg sync.WaitGroup + for _, pp := range dht.routingTables[0].NearestPeers(kb.ConvertKey(key), AlphaValue) { wg.Add(1) go func(p peer.Peer) { defer wg.Done() @@ -156,16 +162,16 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int }(pp) } wg.Wait() - close(peerOut) }() return peerOut } func (dht *IpfsDHT) addPeerListAsync(ctx context.Context, k u.Key, peers []*pb.Message_Peer, ps *peerSet, count int, out chan peer.Peer) { - done := make(chan struct{}) + var wg sync.WaitGroup for _, pbp := range peers { + wg.Add(1) go func(mp *pb.Message_Peer) { - defer func() { done <- struct{}{} }() + defer wg.Done() // construct new peer p, err := dht.ensureConnectedToPeer(ctx, mp) if err != nil { @@ -179,15 +185,17 @@ func (dht *IpfsDHT) addPeerListAsync(ctx context.Context, k u.Key, peers []*pb.M dht.providers.AddProvider(k, p) if ps.AddIfSmallerThan(p, count) { - out <- p + select { + case out <- p: + case <-ctx.Done(): + return + } } else if ps.Size() >= count { return } }(pbp) } - for _ = range peers { - <-done - } + wg.Wait() } // Find specific Peer From 0673c3a2e0c9b604df77e3ae85502ff50b7627ee Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 2 Dec 2014 00:40:50 -0800 Subject: [PATCH 0470/3817] fix(dht/routing) make GetProviders respect context This commit makes GetProviders (sync) respect the request context. It also amends all of GetProviders' callsites to pass a context in. This meant changing the signature of the dht's handlerfunc. I think I'll start referring to the request context as Vito Corleone. cc @whyrusleeping @jbenet License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@cf3a347b18491063d9ce0aaaafdf582ff9fd30d3 --- routing/dht/dht.go | 2 +- routing/dht/handlers.go | 25 ++++++++++++------------- routing/dht/providers.go | 10 +++++++--- routing/dht/providers_test.go | 2 +- routing/dht/routing.go | 2 +- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 127cfacc5..0277f644b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -161,7 +161,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N } // dispatch handler. - rpmes, err := handler(mPeer, pmes) + rpmes, err := handler(ctx, mPeer, pmes) if err != nil { log.Errorf("handle message error: %s", err) return nil diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index bd4b813ee..07f21f18a 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -5,20 +5,19 @@ import ( "fmt" "time" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" peer "github.com/jbenet/go-ipfs/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" - - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ) // The number of closer peers to send on requests. var CloserPeerCount = 4 // dhthandler specifies the signature of functions that handle DHT messages. -type dhtHandler func(peer.Peer, *pb.Message) (*pb.Message, error) +type dhtHandler func(context.Context, peer.Peer, *pb.Message) (*pb.Message, error) func (dht *IpfsDHT) handlerForMsgType(t pb.Message_MessageType) dhtHandler { switch t { @@ -39,7 +38,7 @@ func (dht *IpfsDHT) handlerForMsgType(t pb.Message_MessageType) dhtHandler { } } -func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *pb.Message) (*pb.Message, error) { +func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { log.Debugf("%s handleGetValue for key: %s\n", dht.self, pmes.GetKey()) // setup response @@ -85,7 +84,7 @@ func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *pb.Message) (*pb.Message, } // if we know any providers for the requested value, return those. - provs := dht.providers.GetProviders(u.Key(pmes.GetKey())) + provs := dht.providers.GetProviders(ctx, u.Key(pmes.GetKey())) if len(provs) > 0 { log.Debugf("handleGetValue returning %d provider[s]", len(provs)) resp.ProviderPeers = pb.PeersToPBPeers(provs) @@ -107,7 +106,7 @@ func (dht *IpfsDHT) handleGetValue(p peer.Peer, pmes *pb.Message) (*pb.Message, } // Store a value in this peer local storage -func (dht *IpfsDHT) handlePutValue(p peer.Peer, pmes *pb.Message) (*pb.Message, error) { +func (dht *IpfsDHT) handlePutValue(ctx context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { dht.dslock.Lock() defer dht.dslock.Unlock() dskey := u.Key(pmes.GetKey()).DsKey() @@ -129,12 +128,12 @@ func (dht *IpfsDHT) handlePutValue(p peer.Peer, pmes *pb.Message) (*pb.Message, return pmes, err } -func (dht *IpfsDHT) handlePing(p peer.Peer, pmes *pb.Message) (*pb.Message, error) { +func (dht *IpfsDHT) handlePing(_ context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { log.Debugf("%s Responding to ping from %s!\n", dht.self, p) return pmes, nil } -func (dht *IpfsDHT) handleFindPeer(p peer.Peer, pmes *pb.Message) (*pb.Message, error) { +func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { resp := pb.NewMessage(pmes.GetType(), "", pmes.GetClusterLevel()) var closest []peer.Peer @@ -164,7 +163,7 @@ func (dht *IpfsDHT) handleFindPeer(p peer.Peer, pmes *pb.Message) (*pb.Message, return resp, nil } -func (dht *IpfsDHT) handleGetProviders(p peer.Peer, pmes *pb.Message) (*pb.Message, error) { +func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) // check if we have this value, to add ourselves as provider. @@ -177,7 +176,7 @@ func (dht *IpfsDHT) handleGetProviders(p peer.Peer, pmes *pb.Message) (*pb.Messa } // setup providers - providers := dht.providers.GetProviders(u.Key(pmes.GetKey())) + providers := dht.providers.GetProviders(ctx, u.Key(pmes.GetKey())) if has { providers = append(providers, dht.self) } @@ -201,7 +200,7 @@ type providerInfo struct { Value peer.Peer } -func (dht *IpfsDHT) handleAddProvider(p peer.Peer, pmes *pb.Message) (*pb.Message, error) { +func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { key := u.Key(pmes.GetKey()) log.Debugf("%s adding %s as a provider for '%s'\n", dht.self, p, peer.ID(key)) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index f7d491d6a..2adc20860 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -101,12 +101,16 @@ func (pm *ProviderManager) AddProvider(k u.Key, val peer.Peer) { } } -func (pm *ProviderManager) GetProviders(k u.Key) []peer.Peer { +func (pm *ProviderManager) GetProviders(ctx context.Context, k u.Key) []peer.Peer { gp := new(getProv) gp.k = k gp.resp = make(chan []peer.Peer) - pm.getprovs <- gp - return <-gp.resp + select { + case pm.getprovs <- gp: + return <-gp.resp + case <-ctx.Done(): + return nil + } } func (pm *ProviderManager) GetLocal() []u.Key { diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index c4ae53910..1ae85fbc4 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -15,7 +15,7 @@ func TestProviderManager(t *testing.T) { p := NewProviderManager(ctx, mid) a := u.Key("test") p.AddProvider(a, peer.WithIDString("testingprovider")) - resp := p.GetProviders(a) + resp := p.GetProviders(ctx, a) if len(resp) != 1 { t.Fatal("Could not retrieve provider.") } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index b154b270e..5db218ff6 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -133,7 +133,7 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int ps := newPeerSet() // TODO may want to make this function async to hide latency - provs := dht.providers.GetProviders(key) + provs := dht.providers.GetProviders(ctx, key) for _, p := range provs { count-- // NOTE: assuming that this list of peers is unique From 9e3a9b46765b640aed4fb81216b490a025c19e88 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 2 Dec 2014 00:55:07 -0800 Subject: [PATCH 0471/3817] fix(dht/routing) buffer promise response to prevent resource leak When performing this "promise" pattern, it is important to provide a channel with space for one value. Otherwise the sender may block forever in the case of a receiver that decides to abandon the request. A subtle detail, but one that is important for avoiding leaked goroutines. cc @whyrusleeping @jbenet License: MIT Signed-off-by: Brian Tiger Chow License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@cbfe43713fd6b707b440aeb3aee6edf705c9d9d2 --- routing/dht/providers.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 2adc20860..7f70056d3 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -102,9 +102,10 @@ func (pm *ProviderManager) AddProvider(k u.Key, val peer.Peer) { } func (pm *ProviderManager) GetProviders(ctx context.Context, k u.Key) []peer.Peer { - gp := new(getProv) - gp.k = k - gp.resp = make(chan []peer.Peer) + gp := &getProv{ + k: k, + resp: make(chan []peer.Peer, 1), // buffered to prevent sender from blocking + } select { case pm.getprovs <- gp: return <-gp.resp From fb585139d0e4abc584cce5dd71075bcf429961d3 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 5 Dec 2014 20:46:15 -0800 Subject: [PATCH 0472/3817] style: readability @jbenet License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@87de882875fc081c8d09776923ede826412ac1a9 --- routing/dht/routing.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 5db218ff6..134e54ccb 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -149,7 +149,8 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int } var wg sync.WaitGroup - for _, pp := range dht.routingTables[0].NearestPeers(kb.ConvertKey(key), AlphaValue) { + peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), AlphaValue) + for _, pp := range peers { wg.Add(1) go func(p peer.Peer) { defer wg.Done() From c68c1cb9df860b8559a33ced54862a39575ff264 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 5 Dec 2014 22:54:16 -0800 Subject: [PATCH 0473/3817] fix: respect ctx on receive @jbenet License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@37bc902f6e5f18ae94a08f4c02875a7a4dc123aa --- routing/dht/providers.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 7f70056d3..0deea6324 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -107,10 +107,15 @@ func (pm *ProviderManager) GetProviders(ctx context.Context, k u.Key) []peer.Pee resp: make(chan []peer.Peer, 1), // buffered to prevent sender from blocking } select { + case <-ctx.Done(): + return nil case pm.getprovs <- gp: - return <-gp.resp + } + select { case <-ctx.Done(): return nil + case peers := <-gp.resp: + return peers } } From 064f8cddd7960b1010ea53ee5fd1a3f5c14ee8b6 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 26 Nov 2014 17:01:40 -0800 Subject: [PATCH 0474/3817] style(blockservice) s/Remote/Exchange License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-blockservice@ec4d644783c33bf4b038c9dd5644ca1a42d53751 --- blockservice/blockservice.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 0ddec7955..0ebe30a4d 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -25,7 +25,7 @@ var ErrNotFound = errors.New("blockservice: key not found") type BlockService struct { // TODO don't expose underlying impl details Blockstore blockstore.Blockstore - Remote exchange.Interface + Exchange exchange.Interface } // NewBlockService creates a BlockService with given datastore instance. @@ -36,7 +36,7 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) (*BlockService, error if rem == nil { log.Warning("blockservice running in local (offline) mode.") } - return &BlockService{Blockstore: bs, Remote: rem}, nil + return &BlockService{Blockstore: bs, Exchange: rem}, nil } // AddBlock adds a particular block to the service, Putting it into the datastore. @@ -66,9 +66,9 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { // TODO this operation rate-limits blockservice operations, we should // consider moving this to an sync process. - if s.Remote != nil { + if s.Exchange != nil { ctx := context.TODO() - err = s.Remote.HasBlock(ctx, b) + err = s.Exchange.HasBlock(ctx, b) } return k, err } @@ -82,9 +82,9 @@ func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, er return block, nil // TODO be careful checking ErrNotFound. If the underlying // implementation changes, this will break. - } else if err == ds.ErrNotFound && s.Remote != nil { + } else if err == ds.ErrNotFound && s.Exchange != nil { log.Debug("Blockservice: Searching bitswap.") - blk, err := s.Remote.GetBlock(ctx, k) + blk, err := s.Exchange.GetBlock(ctx, k) if err != nil { return nil, err } @@ -117,7 +117,7 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks } } - rblocks, err := s.Remote.GetBlocks(ctx, misses) + rblocks, err := s.Exchange.GetBlocks(ctx, misses) if err != nil { log.Errorf("Error with GetBlocks: %s", err) return From 521867d2e4d8729d4e70e705e44f27a197e786fc Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 30 Nov 2014 21:25:46 -0800 Subject: [PATCH 0475/3817] feat(blockstore) write cache vendors hashicorp/golang-lru dependency. (Mozilla Public License, version 2.0) License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-blockstore@68d0beb81163f415006c8b5e9e9a389e18558a73 --- blockstore/write_cache.go | 45 +++++++++++++++++ blockstore/write_cache_test.go | 88 ++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 blockstore/write_cache.go create mode 100644 blockstore/write_cache_test.go diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go new file mode 100644 index 000000000..b46d05846 --- /dev/null +++ b/blockstore/write_cache.go @@ -0,0 +1,45 @@ +package blockstore + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" + "github.com/jbenet/go-ipfs/blocks" + u "github.com/jbenet/go-ipfs/util" +) + +// WriteCached returns a blockstore that caches up to |size| unique writes (bs.Put). +func WriteCached(bs Blockstore, size int) (Blockstore, error) { + c, err := lru.New(size) + if err != nil { + return nil, err + } + return &writecache{blockstore: bs, cache: c}, nil +} + +type writecache struct { + cache *lru.Cache // pointer b/c Cache contains a Mutex as value (complicates copying) + blockstore Blockstore +} + +func (w *writecache) DeleteBlock(k u.Key) error { + w.cache.Remove(k) + return w.blockstore.DeleteBlock(k) +} + +func (w *writecache) Has(k u.Key) (bool, error) { + if _, ok := w.cache.Get(k); ok { + return true, nil + } + return w.blockstore.Has(k) +} + +func (w *writecache) Get(k u.Key) (*blocks.Block, error) { + return w.blockstore.Get(k) +} + +func (w *writecache) Put(b *blocks.Block) error { + if _, ok := w.cache.Get(b.Key()); ok { + return nil + } + w.cache.Add(b.Key(), struct{}{}) + return w.blockstore.Put(b) +} diff --git a/blockstore/write_cache_test.go b/blockstore/write_cache_test.go new file mode 100644 index 000000000..2be865903 --- /dev/null +++ b/blockstore/write_cache_test.go @@ -0,0 +1,88 @@ +package blockstore + +import ( + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + syncds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/jbenet/go-ipfs/blocks" + "testing" +) + +func TestReturnsErrorWhenSizeNegative(t *testing.T) { + bs := NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore())) + _, err := WriteCached(bs, -1) + if err != nil { + return + } + t.Fail() +} + +func TestRemoveCacheEntryOnDelete(t *testing.T) { + b := blocks.NewBlock([]byte("foo")) + cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} + bs := NewBlockstore(syncds.MutexWrap(cd)) + cachedbs, err := WriteCached(bs, 1) + if err != nil { + t.Fatal(err) + } + cachedbs.Put(b) + + writeHitTheDatastore := false + cd.SetFunc(func() { + writeHitTheDatastore = true + }) + + cachedbs.DeleteBlock(b.Key()) + cachedbs.Put(b) + if !writeHitTheDatastore { + t.Fail() + } +} + +func TestElideDuplicateWrite(t *testing.T) { + cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} + bs := NewBlockstore(syncds.MutexWrap(cd)) + cachedbs, err := WriteCached(bs, 1) + if err != nil { + t.Fatal(err) + } + + b1 := blocks.NewBlock([]byte("foo")) + + cachedbs.Put(b1) + cd.SetFunc(func() { + t.Fatal("write hit the datastore") + }) + cachedbs.Put(b1) +} + +type callbackDatastore struct { + f func() + ds ds.Datastore +} + +func (c *callbackDatastore) SetFunc(f func()) { c.f = f } + +func (c *callbackDatastore) Put(key ds.Key, value interface{}) (err error) { + c.f() + return c.ds.Put(key, value) +} + +func (c *callbackDatastore) Get(key ds.Key) (value interface{}, err error) { + c.f() + return c.ds.Get(key) +} + +func (c *callbackDatastore) Has(key ds.Key) (exists bool, err error) { + c.f() + return c.ds.Has(key) +} + +func (c *callbackDatastore) Delete(key ds.Key) (err error) { + c.f() + return c.ds.Delete(key) +} + +func (c *callbackDatastore) KeyList() ([]ds.Key, error) { + c.f() + return c.ds.KeyList() +} From 7011a42aa8fb993af52511461c4f586e3cd6ac85 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 26 Nov 2014 16:55:28 -0800 Subject: [PATCH 0476/3817] feat(bitswap) make offline exchange query datastore License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-blockservice@2f1cf9bf3317df11940d47ae32dd09441246016c --- blockservice/blocks_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index 1a75723e2..2645b2024 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -19,9 +19,8 @@ import ( ) func TestBlocks(t *testing.T) { - d := ds.NewMapDatastore() - tsds := dssync.MutexWrap(d) - bs, err := New(blockstore.NewBlockstore(tsds), offline.Exchange()) + bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) + bs, err := New(bstore, offline.Exchange(bstore)) if err != nil { t.Error("failed to construct block service", err) return From f1379f4cbaa0d1324c93e39e2dfdfa0219cb1471 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 26 Nov 2014 16:55:28 -0800 Subject: [PATCH 0477/3817] feat(bitswap) make offline exchange query datastore License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-unixfs@b3ee4c0f87f2c32ed6236d70ca38dfb7c6989de1 --- unixfs/io/dagmodifier_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index d0aa83795..ed5b10d69 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -24,7 +24,7 @@ func getMockDagServ(t *testing.T) mdag.DAGService { dstore := ds.NewMapDatastore() tsds := sync.MutexWrap(dstore) bstore := blockstore.NewBlockstore(tsds) - bserv, err := bs.New(bstore, offline.Exchange()) + bserv, err := bs.New(bstore, offline.Exchange(bstore)) if err != nil { t.Fatal(err) } From bdf75bb5476ec337061148d65cec0c44d70de21f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 26 Nov 2014 16:55:28 -0800 Subject: [PATCH 0478/3817] feat(bitswap) make offline exchange query datastore License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-pinner@ab9ce81f6cabbe6dbc7b12faa42513774dc367b9 --- pinning/pinner/pin_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index fc9dc215d..dada99803 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -23,7 +23,7 @@ func randNode() (*mdag.Node, util.Key) { func TestPinnerBasic(t *testing.T) { dstore := ds.NewMapDatastore() bstore := blockstore.NewBlockstore(dssync.MutexWrap(dstore)) - bserv, err := bs.New(bstore, offline.Exchange()) + bserv, err := bs.New(bstore, offline.Exchange(bstore)) if err != nil { t.Fatal(err) } From 76c3cdd82cda9a50e626d87f17087a727569b803 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 26 Nov 2014 16:55:28 -0800 Subject: [PATCH 0479/3817] feat(bitswap) make offline exchange query datastore License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-exchange-offline@e7e4c6c5f249281a657f5c2f0db34a4a48b3f5fe --- exchange/offline/offline.go | 52 ++++++++++++++++++++-------- exchange/offline/offline_test.go | 59 +++++++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 19 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 24a89e038..f1a6aaa61 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -3,42 +3,66 @@ package offline import ( - "errors" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - blocks "github.com/jbenet/go-ipfs/blocks" + "github.com/jbenet/go-ipfs/blocks/blockstore" exchange "github.com/jbenet/go-ipfs/exchange" u "github.com/jbenet/go-ipfs/util" ) -var OfflineMode = errors.New("Block unavailable. Operating in offline mode") - -func Exchange() exchange.Interface { - return &offlineExchange{} +func Exchange(bs blockstore.Blockstore) exchange.Interface { + return &offlineExchange{bs: bs} } // offlineExchange implements the Exchange interface but doesn't return blocks. // For use in offline mode. -type offlineExchange struct{} +type offlineExchange struct { + bs blockstore.Blockstore +} // GetBlock returns nil to signal that a block could not be retrieved for the // given key. // NB: This function may return before the timeout expires. -func (_ *offlineExchange) GetBlock(context.Context, u.Key) (*blocks.Block, error) { - return nil, OfflineMode +func (e *offlineExchange) GetBlock(_ context.Context, k u.Key) (*blocks.Block, error) { + return e.bs.Get(k) } // HasBlock always returns nil. -func (_ *offlineExchange) HasBlock(context.Context, *blocks.Block) error { - return nil +func (e *offlineExchange) HasBlock(_ context.Context, b *blocks.Block) error { + return e.bs.Put(b) } // Close always returns nil. func (_ *offlineExchange) Close() error { + // NB: exchange doesn't own the blockstore's underlying datastore, so it is + // not responsible for closing it. return nil } -func (_ *offlineExchange) GetBlocks(context.Context, []u.Key) (<-chan *blocks.Block, error) { - return nil, OfflineMode +func (e *offlineExchange) GetBlocks(ctx context.Context, ks []u.Key) (<-chan *blocks.Block, error) { + out := make(chan *blocks.Block, 0) + go func() { + defer close(out) + var misses []u.Key + for _, k := range ks { + hit, err := e.bs.Get(k) + if err != nil { + misses = append(misses, k) + // a long line of misses should abort when context is cancelled. + select { + // TODO case send misses down channel + case <-ctx.Done(): + return + default: + continue + } + } + select { + case out <- hit: + case <-ctx.Done(): + return + } + } + }() + return out, nil } diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index ac02d2101..d32f336d0 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -4,13 +4,16 @@ import ( "testing" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds_sync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" blocks "github.com/jbenet/go-ipfs/blocks" + "github.com/jbenet/go-ipfs/blocks/blockstore" + "github.com/jbenet/go-ipfs/blocks/blocksutil" u "github.com/jbenet/go-ipfs/util" ) func TestBlockReturnsErr(t *testing.T) { - off := Exchange() + off := Exchange(bstore()) _, err := off.GetBlock(context.Background(), u.Key("foo")) if err != nil { return // as desired @@ -19,10 +22,56 @@ func TestBlockReturnsErr(t *testing.T) { } func TestHasBlockReturnsNil(t *testing.T) { - off := Exchange() + store := bstore() + ex := Exchange(store) block := blocks.NewBlock([]byte("data")) - err := off.HasBlock(context.Background(), block) + + err := ex.HasBlock(context.Background(), block) if err != nil { - t.Fatal("") + t.Fail() + } + + if _, err := store.Get(block.Key()); err != nil { + t.Fatal(err) + } +} + +func TestGetBlocks(t *testing.T) { + store := bstore() + ex := Exchange(store) + g := blocksutil.NewBlockGenerator() + + expected := g.Blocks(2) + + for _, b := range expected { + if err := ex.HasBlock(context.Background(), b); err != nil { + t.Fail() + } } + + request := func() []u.Key { + var ks []u.Key + + for _, b := range expected { + ks = append(ks, b.Key()) + } + return ks + }() + + received, err := ex.GetBlocks(context.Background(), request) + if err != nil { + t.Fatal(err) + } + + var count int + for _ = range received { + count++ + } + if len(expected) != count { + t.Fail() + } +} + +func bstore() blockstore.Blockstore { + return blockstore.NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) } From bd4dfd37f82f266282f5e28a53791de416e3999d Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 6 Dec 2014 13:39:03 -0800 Subject: [PATCH 0480/3817] blockstore: Put checks Has first. This commit was moved from ipfs/go-ipfs-blockstore@cf153d24b2666016d4fea60f4545ca6705ab6030 --- blockstore/blockstore.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 68ccc7c74..3fe742ef8 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -45,7 +45,13 @@ func (bs *blockstore) Get(k u.Key) (*blocks.Block, error) { } func (bs *blockstore) Put(block *blocks.Block) error { - return bs.datastore.Put(block.Key().DsKey(), block.Data) + // Has is cheaper than + k := block.Key().DsKey() + exists, err := bs.datastore.Has(k) + if err != nil && exists { + return nil // already stored. + } + return bs.datastore.Put(k, block.Data) } func (bs *blockstore) Has(k u.Key) (bool, error) { From 66ca90a118ed713ead703c3131648be9d13f6408 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 6 Dec 2014 15:18:16 -0800 Subject: [PATCH 0481/3817] rm redundant Has License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-blockservice@de8b2c68f2ad628e52127c74e7cd4a066e2d599b --- blockservice/blockservice.go | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 0ebe30a4d..f44eaa0f5 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -44,25 +44,10 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) (*BlockService, error func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { k := b.Key() log.Debugf("blockservice: storing [%s] in datastore", k) - // TODO(brian): define a block datastore with a Put method which accepts a - // block parameter - - // check if we have it before adding. this is an extra read, but large writes - // are more expensive. - // TODO(jbenet) cheaper has. https://github.com/jbenet/go-datastore/issues/6 - has, err := s.Blockstore.Has(k) + err := s.Blockstore.Put(b) if err != nil { return k, err } - if has { - log.Debugf("blockservice: storing [%s] in datastore (already stored)", k) - } else { - log.Debugf("blockservice: storing [%s] in datastore", k) - err := s.Blockstore.Put(b) - if err != nil { - return k, err - } - } // TODO this operation rate-limits blockservice operations, we should // consider moving this to an sync process. From 29baf9325caa673e995494a1c9f7f0a8f71fe082 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 8 Dec 2014 01:09:37 -0800 Subject: [PATCH 0482/3817] refactor(peerstore) s/Get/FindOrCreate License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@f6c0f472ab48929391840ad8aadcd8198e58dbf2 --- routing/dht/dht.go | 2 +- routing/dht/records.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 0277f644b..1d9c3a47a 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -500,7 +500,7 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, count int) []peer.Peer // getPeer searches the peerstore for a peer with the given peer ID func (dht *IpfsDHT) getPeer(id peer.ID) (peer.Peer, error) { - p, err := dht.peerstore.Get(id) + p, err := dht.peerstore.FindOrCreate(id) if err != nil { err = fmt.Errorf("Failed to get peer from peerstore: %s", err) log.Error(err) diff --git a/routing/dht/records.go b/routing/dht/records.go index 0a3b4f4e0..0ea455a17 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -44,7 +44,7 @@ func (dht *IpfsDHT) makePutRecord(key u.Key, value []byte) (*pb.Record, error) { func (dht *IpfsDHT) getPublicKey(pid peer.ID) (ci.PubKey, error) { log.Debug("getPublicKey for: %s", pid) - p, err := dht.peerstore.Get(pid) + p, err := dht.peerstore.FindOrCreate(pid) if err == nil { return p.PubKey(), nil } @@ -67,7 +67,7 @@ func (dht *IpfsDHT) getPublicKey(pid peer.ID) (ci.PubKey, error) { func (dht *IpfsDHT) verifyRecord(r *pb.Record) error { // First, validate the signature - p, err := dht.peerstore.Get(peer.ID(r.GetAuthor())) + p, err := dht.peerstore.FindOrCreate(peer.ID(r.GetAuthor())) if err != nil { return err } From 842fa71aa9f7baddba60de389a81448b53c42930 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 8 Dec 2014 01:40:07 -0800 Subject: [PATCH 0483/3817] refactor(peer): create peer through peerstore for safety! use mockpeer.WithID methods to create peers in tests License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@3067a8a060696c4f9c33db916436274623324267 --- routing/dht/dht_test.go | 3 ++- routing/dht/ext_test.go | 3 ++- routing/dht/providers_test.go | 3 ++- routing/mock/routing_test.go | 13 +++++++------ 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index e62145d5b..df16ea878 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -14,6 +14,7 @@ import ( mux "github.com/jbenet/go-ipfs/net/mux" netservice "github.com/jbenet/go-ipfs/net/service" peer "github.com/jbenet/go-ipfs/peer" + "github.com/jbenet/go-ipfs/peer/mock" u "github.com/jbenet/go-ipfs/util" "fmt" @@ -68,7 +69,7 @@ func makePeer(addr ma.Multiaddr) peer.Peer { if err != nil { panic(err) } - p, err := peer.WithKeyPair(sk, pk) + p, err := mockpeer.WithKeyPair(sk, pk) if err != nil { panic(err) } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 791c1066c..1f30ff4fa 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -12,6 +12,7 @@ import ( msg "github.com/jbenet/go-ipfs/net/message" mux "github.com/jbenet/go-ipfs/net/mux" peer "github.com/jbenet/go-ipfs/peer" + "github.com/jbenet/go-ipfs/peer/mock" "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" @@ -210,7 +211,7 @@ func TestGetFailures(t *testing.T) { func _randPeer() peer.Peer { id := make(peer.ID, 16) crand.Read(id) - p := peer.WithID(id) + p := mockpeer.WithID(id) return p } diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 1ae85fbc4..f22c09a7b 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/jbenet/go-ipfs/peer" + "github.com/jbenet/go-ipfs/peer/mock" u "github.com/jbenet/go-ipfs/util" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -14,7 +15,7 @@ func TestProviderManager(t *testing.T) { mid := peer.ID("testing") p := NewProviderManager(ctx, mid) a := u.Key("test") - p.AddProvider(a, peer.WithIDString("testingprovider")) + p.AddProvider(a, mockpeer.WithIDString("testingprovider")) resp := p.GetProviders(ctx, a) if len(resp) != 1 { t.Fatal("Could not retrieve provider.") diff --git a/routing/mock/routing_test.go b/routing/mock/routing_test.go index 196e00b5e..ca9c845d0 100644 --- a/routing/mock/routing_test.go +++ b/routing/mock/routing_test.go @@ -6,6 +6,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/peer" + "github.com/jbenet/go-ipfs/peer/mock" u "github.com/jbenet/go-ipfs/util" ) @@ -20,7 +21,7 @@ func TestKeyNotFound(t *testing.T) { func TestSetAndGet(t *testing.T) { pid := peer.ID([]byte("the peer id")) - p := peer.WithID(pid) + p := mockpeer.WithID(pid) k := u.Key("42") rs := VirtualRoutingServer() err := rs.Announce(p, k) @@ -40,7 +41,7 @@ func TestSetAndGet(t *testing.T) { } func TestClientFindProviders(t *testing.T) { - peer := peer.WithIDString("42") + peer := mockpeer.WithIDString("42") rs := VirtualRoutingServer() client := rs.Client(peer) @@ -79,7 +80,7 @@ func TestClientOverMax(t *testing.T) { k := u.Key("hello") numProvidersForHelloKey := 100 for i := 0; i < numProvidersForHelloKey; i++ { - peer := peer.WithIDString(string(i)) + peer := mockpeer.WithIDString(string(i)) err := rs.Announce(peer, k) if err != nil { t.Fatal(err) @@ -92,7 +93,7 @@ func TestClientOverMax(t *testing.T) { } max := 10 - peer := peer.WithIDString("TODO") + peer := mockpeer.WithIDString("TODO") client := rs.Client(peer) providersFromClient := client.FindProvidersAsync(context.Background(), k, max) @@ -114,7 +115,7 @@ func TestCanceledContext(t *testing.T) { i := 0 go func() { // infinite stream for { - peer := peer.WithIDString(string(i)) + peer := mockpeer.WithIDString(string(i)) err := rs.Announce(peer, k) if err != nil { t.Fatal(err) @@ -123,7 +124,7 @@ func TestCanceledContext(t *testing.T) { } }() - local := peer.WithIDString("peer id doesn't matter") + local := mockpeer.WithIDString("peer id doesn't matter") client := rs.Client(local) t.Log("warning: max is finite so this test is non-deterministic") From dd5e6fd27337cfd2c1ab1ab98e5a6622385f2d7f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 8 Dec 2014 01:40:07 -0800 Subject: [PATCH 0484/3817] refactor(peer): create peer through peerstore for safety! use mockpeer.WithID methods to create peers in tests License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-namesys@c982a506c5e87b8ec0c0a819e4975c8abfadc359 --- namesys/resolve_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index d7d49c5a6..35fa49254 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -5,13 +5,13 @@ import ( ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ci "github.com/jbenet/go-ipfs/crypto" - "github.com/jbenet/go-ipfs/peer" + "github.com/jbenet/go-ipfs/peer/mock" mock "github.com/jbenet/go-ipfs/routing/mock" u "github.com/jbenet/go-ipfs/util" ) func TestRoutingResolve(t *testing.T) { - local := peer.WithIDString("testID") + local := mockpeer.WithIDString("testID") lds := ds.NewMapDatastore() d := mock.NewMockRouter(local, lds) From e5988b9776aef03d0fa5c7d2f155a0abbe57bd9b Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 8 Dec 2014 01:40:07 -0800 Subject: [PATCH 0485/3817] refactor(peer): create peer through peerstore for safety! use mockpeer.WithID methods to create peers in tests License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-blockstore@c5a445d762d44c47fd175c0364bc0235e73889c2 --- blockstore/write_cache_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/blockstore/write_cache_test.go b/blockstore/write_cache_test.go index 2be865903..c2175a1fc 100644 --- a/blockstore/write_cache_test.go +++ b/blockstore/write_cache_test.go @@ -1,10 +1,11 @@ package blockstore import ( + "testing" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" syncds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" "github.com/jbenet/go-ipfs/blocks" - "testing" ) func TestReturnsErrorWhenSizeNegative(t *testing.T) { From 4ab1342d5822d5d297fd7460f95dba5c81e27070 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 8 Dec 2014 14:32:52 -0800 Subject: [PATCH 0486/3817] fix(core, peer) helpers to testutil, err handling License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@776fe95d24ca0ee9fb1706e87f7cc499183430eb --- routing/dht/dht_test.go | 4 ++-- routing/dht/ext_test.go | 7 +++---- routing/dht/providers_test.go | 4 ++-- routing/mock/routing_test.go | 14 +++++++------- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index df16ea878..30ef2d3aa 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -14,8 +14,8 @@ import ( mux "github.com/jbenet/go-ipfs/net/mux" netservice "github.com/jbenet/go-ipfs/net/service" peer "github.com/jbenet/go-ipfs/peer" - "github.com/jbenet/go-ipfs/peer/mock" u "github.com/jbenet/go-ipfs/util" + testutil "github.com/jbenet/go-ipfs/util/testutil" "fmt" "time" @@ -69,7 +69,7 @@ func makePeer(addr ma.Multiaddr) peer.Peer { if err != nil { panic(err) } - p, err := mockpeer.WithKeyPair(sk, pk) + p, err := testutil.NewPeerWithKeyPair(sk, pk) if err != nil { panic(err) } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 1f30ff4fa..fa536edd4 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -7,15 +7,14 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" msg "github.com/jbenet/go-ipfs/net/message" mux "github.com/jbenet/go-ipfs/net/mux" peer "github.com/jbenet/go-ipfs/peer" - "github.com/jbenet/go-ipfs/peer/mock" - "github.com/jbenet/go-ipfs/routing" + routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" + testutil "github.com/jbenet/go-ipfs/util/testutil" "sync" "time" @@ -211,7 +210,7 @@ func TestGetFailures(t *testing.T) { func _randPeer() peer.Peer { id := make(peer.ID, 16) crand.Read(id) - p := mockpeer.WithID(id) + p := testutil.NewPeerWithID(id) return p } diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index f22c09a7b..7d8aaa304 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -4,8 +4,8 @@ import ( "testing" "github.com/jbenet/go-ipfs/peer" - "github.com/jbenet/go-ipfs/peer/mock" u "github.com/jbenet/go-ipfs/util" + testutil "github.com/jbenet/go-ipfs/util/testutil" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ) @@ -15,7 +15,7 @@ func TestProviderManager(t *testing.T) { mid := peer.ID("testing") p := NewProviderManager(ctx, mid) a := u.Key("test") - p.AddProvider(a, mockpeer.WithIDString("testingprovider")) + p.AddProvider(a, testutil.NewPeerWithIDString("testingprovider")) resp := p.GetProviders(ctx, a) if len(resp) != 1 { t.Fatal("Could not retrieve provider.") diff --git a/routing/mock/routing_test.go b/routing/mock/routing_test.go index ca9c845d0..536d7b018 100644 --- a/routing/mock/routing_test.go +++ b/routing/mock/routing_test.go @@ -6,8 +6,8 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/peer" - "github.com/jbenet/go-ipfs/peer/mock" u "github.com/jbenet/go-ipfs/util" + testutil "github.com/jbenet/go-ipfs/util/testutil" ) func TestKeyNotFound(t *testing.T) { @@ -21,7 +21,7 @@ func TestKeyNotFound(t *testing.T) { func TestSetAndGet(t *testing.T) { pid := peer.ID([]byte("the peer id")) - p := mockpeer.WithID(pid) + p := testutil.NewPeerWithID(pid) k := u.Key("42") rs := VirtualRoutingServer() err := rs.Announce(p, k) @@ -41,7 +41,7 @@ func TestSetAndGet(t *testing.T) { } func TestClientFindProviders(t *testing.T) { - peer := mockpeer.WithIDString("42") + peer := testutil.NewPeerWithIDString("42") rs := VirtualRoutingServer() client := rs.Client(peer) @@ -80,7 +80,7 @@ func TestClientOverMax(t *testing.T) { k := u.Key("hello") numProvidersForHelloKey := 100 for i := 0; i < numProvidersForHelloKey; i++ { - peer := mockpeer.WithIDString(string(i)) + peer := testutil.NewPeerWithIDString(string(i)) err := rs.Announce(peer, k) if err != nil { t.Fatal(err) @@ -93,7 +93,7 @@ func TestClientOverMax(t *testing.T) { } max := 10 - peer := mockpeer.WithIDString("TODO") + peer := testutil.NewPeerWithIDString("TODO") client := rs.Client(peer) providersFromClient := client.FindProvidersAsync(context.Background(), k, max) @@ -115,7 +115,7 @@ func TestCanceledContext(t *testing.T) { i := 0 go func() { // infinite stream for { - peer := mockpeer.WithIDString(string(i)) + peer := testutil.NewPeerWithIDString(string(i)) err := rs.Announce(peer, k) if err != nil { t.Fatal(err) @@ -124,7 +124,7 @@ func TestCanceledContext(t *testing.T) { } }() - local := mockpeer.WithIDString("peer id doesn't matter") + local := testutil.NewPeerWithIDString("peer id doesn't matter") client := rs.Client(local) t.Log("warning: max is finite so this test is non-deterministic") From 317b72c0dc185b78908afa5d6e084bc6031ae103 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 8 Dec 2014 14:32:52 -0800 Subject: [PATCH 0487/3817] fix(core, peer) helpers to testutil, err handling License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-namesys@fec652f8dcf1e3849c227c67ec908261090f0a92 --- namesys/resolve_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 35fa49254..eef5e6825 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -5,13 +5,13 @@ import ( ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ci "github.com/jbenet/go-ipfs/crypto" - "github.com/jbenet/go-ipfs/peer/mock" mock "github.com/jbenet/go-ipfs/routing/mock" u "github.com/jbenet/go-ipfs/util" + testutil "github.com/jbenet/go-ipfs/util/testutil" ) func TestRoutingResolve(t *testing.T) { - local := mockpeer.WithIDString("testID") + local := testutil.NewPeerWithIDString("testID") lds := ds.NewMapDatastore() d := mock.NewMockRouter(local, lds) From 5d19ab18626ecc27bd4547b830229e2c57457284 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 8 Dec 2014 02:55:33 -0800 Subject: [PATCH 0488/3817] silence verbose output for higher SnR at IPFS_LOGGING=info License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@d023ec2221e69d76110591a0132ea7f15d99b946 --- routing/dht/routing.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 134e54ccb..47a64d3fd 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -274,10 +274,10 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) // Ping a peer, log the time it took func (dht *IpfsDHT) Ping(ctx context.Context, p peer.Peer) error { // Thoughts: maybe this should accept an ID and do a peer lookup? - log.Infof("ping %s start", p) + log.Debugf("ping %s start", p) pmes := pb.NewMessage(pb.Message_PING, "", 0) _, err := dht.sendRequest(ctx, p, pmes) - log.Infof("ping %s end (err = %s)", p, err) + log.Debugf("ping %s end (err = %s)", p, err) return err } From 66d2f2d7d539b32895a7a2b53abbf9c1844fec41 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 8 Dec 2014 04:07:52 -0800 Subject: [PATCH 0489/3817] refactor(dht) remove extraneous return value License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@4775502bac51f8563df1557ffb41599e2f6f1f95 --- routing/dht/dht.go | 17 ++++------------- routing/dht/dht_test.go | 32 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 1d9c3a47a..5aa0e8d46 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -97,32 +97,23 @@ func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, dialer inet.Dia } // Connect to a new peer at the given address, ping and add to the routing table -func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.Peer) (peer.Peer, error) { - // TODO(jbenet,whyrusleeping) - // - // Connect should take in a Peer (with ID). In a sense, we shouldn't be - // allowing connections to random multiaddrs without knowing who we're - // speaking to (i.e. peer.ID). In terms of moving around simple addresses - // -- instead of an (ID, Addr) pair -- we can use: - // - // /ip4/10.20.30.40/tcp/1234/ipfs/Qxhxxchxzcncxnzcnxzcxzm - // +func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.Peer) error { err := dht.dialer.DialPeer(ctx, npeer) if err != nil { - return nil, err + return err } // Ping new peer to register in their routing table // NOTE: this should be done better... err = dht.Ping(ctx, npeer) if err != nil { - return nil, fmt.Errorf("failed to ping newly connected peer: %s\n", err) + return fmt.Errorf("failed to ping newly connected peer: %s\n", err) } log.Event(ctx, "connect", dht.self, npeer) dht.Update(ctx, npeer) - return npeer, nil + return nil } // HandleMessage implements the inet.Handler interface. diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 30ef2d3aa..e440964dc 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -101,7 +101,7 @@ func TestPing(t *testing.T) { defer dhtA.dialer.(inet.Network).Close() defer dhtB.dialer.(inet.Network).Close() - _, err = dhtA.Connect(ctx, peerB) + err = dhtA.Connect(ctx, peerB) if err != nil { t.Fatal(err) } @@ -151,7 +151,7 @@ func TestValueGetSet(t *testing.T) { defer dhtA.dialer.(inet.Network).Close() defer dhtB.dialer.(inet.Network).Close() - _, err = dhtA.Connect(ctx, peerB) + err = dhtA.Connect(ctx, peerB) if err != nil { t.Fatal(err) } @@ -194,17 +194,17 @@ func TestProvides(t *testing.T) { } }() - _, err := dhts[0].Connect(ctx, peers[1]) + err := dhts[0].Connect(ctx, peers[1]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(ctx, peers[2]) + err = dhts[1].Connect(ctx, peers[2]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(ctx, peers[3]) + err = dhts[1].Connect(ctx, peers[3]) if err != nil { t.Fatal(err) } @@ -256,17 +256,17 @@ func TestProvidesAsync(t *testing.T) { } }() - _, err := dhts[0].Connect(ctx, peers[1]) + err := dhts[0].Connect(ctx, peers[1]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(ctx, peers[2]) + err = dhts[1].Connect(ctx, peers[2]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(ctx, peers[3]) + err = dhts[1].Connect(ctx, peers[3]) if err != nil { t.Fatal(err) } @@ -321,17 +321,17 @@ func TestLayeredGet(t *testing.T) { } }() - _, err := dhts[0].Connect(ctx, peers[1]) + err := dhts[0].Connect(ctx, peers[1]) if err != nil { t.Fatalf("Failed to connect: %s", err) } - _, err = dhts[1].Connect(ctx, peers[2]) + err = dhts[1].Connect(ctx, peers[2]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(ctx, peers[3]) + err = dhts[1].Connect(ctx, peers[3]) if err != nil { t.Fatal(err) } @@ -376,17 +376,17 @@ func TestFindPeer(t *testing.T) { } }() - _, err := dhts[0].Connect(ctx, peers[1]) + err := dhts[0].Connect(ctx, peers[1]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(ctx, peers[2]) + err = dhts[1].Connect(ctx, peers[2]) if err != nil { t.Fatal(err) } - _, err = dhts[1].Connect(ctx, peers[3]) + err = dhts[1].Connect(ctx, peers[3]) if err != nil { t.Fatal(err) } @@ -435,14 +435,14 @@ func TestConnectCollision(t *testing.T) { done := make(chan struct{}) go func() { - _, err := dhtA.Connect(ctx, peerB) + err := dhtA.Connect(ctx, peerB) if err != nil { t.Fatal(err) } done <- struct{}{} }() go func() { - _, err := dhtB.Connect(ctx, peerA) + err := dhtB.Connect(ctx, peerA) if err != nil { t.Fatal(err) } From 9991b9c1ce10b6c020c21bdc1b3032be7c5b69dd Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 20 Nov 2014 10:46:19 -0800 Subject: [PATCH 0490/3817] dht: linting This commit was moved from ipfs/go-ipfs-routing@b51893055d5ab538170522f96927169163222c5a --- routing/dht/dht.go | 4 ++-- routing/dht/pb/message.go | 2 ++ routing/dht/routing.go | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 5aa0e8d46..162f60c56 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -1,4 +1,4 @@ -// package dht implements a distributed hash table that satisfies the ipfs routing +// Package dht implements a distributed hash table that satisfies the ipfs routing // interface. This DHT is modeled after kademlia with Coral and S/Kademlia modifications. package dht @@ -583,7 +583,7 @@ func (dht *IpfsDHT) Bootstrap(ctx context.Context) { rand.Read(id) p, err := dht.FindPeer(ctx, peer.ID(id)) if err != nil { - log.Error("Bootstrap peer error: %s", err) + log.Errorf("Bootstrap peer error: %s", err) } err = dht.dialer.DialPeer(ctx, p) if err != nil { diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 6ea98d4cd..f8001c5f7 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -31,6 +31,8 @@ func peerToPBPeer(p peer.Peer) *Message_Peer { return pbp } +// PeersToPBPeers converts a slice of Peers into a slice of *Message_Peers, +// ready to go out on the wire. func PeersToPBPeers(peers []peer.Peer) []*Message_Peer { pbpeers := make([]*Message_Peer, len(peers)) for i, p := range peers { diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 47a64d3fd..4e2dc3745 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -125,6 +125,9 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { return nil } +// FindProvidersAsync is the same thing as FindProviders, but returns a channel. +// Peers will be returned on the channel as soon as they are found, even before +// the search query completes. func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int) <-chan peer.Peer { log.Event(ctx, "findProviders", &key) peerOut := make(chan peer.Peer, count) @@ -199,7 +202,6 @@ func (dht *IpfsDHT) addPeerListAsync(ctx context.Context, k u.Key, peers []*pb.M wg.Wait() } -// Find specific Peer // FindPeer searches for a peer with given ID. func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) { From 08ad5f4f50682905fdb538e7ee6e4c5628d0459d Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 20 Nov 2014 10:46:56 -0800 Subject: [PATCH 0491/3817] dht: changed msgs, include multiple addrs + conn type See https://github.com/jbenet/go-ipfs/issues/153#issuecomment-63350535 This commit was moved from ipfs/go-ipfs-routing@80effbe51cdcf8d29457bff0cc1a3e26adf4691b --- routing/dht/dht.go | 7 ++-- routing/dht/handlers.go | 10 +++--- routing/dht/pb/dht.pb.go | 74 ++++++++++++++++++++++++++++++++++----- routing/dht/pb/dht.proto | 23 +++++++++++- routing/dht/pb/message.go | 27 ++++++++------ routing/dht/routing.go | 11 ++++-- 6 files changed, 124 insertions(+), 28 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 162f60c56..d5aca0d7f 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -517,11 +517,14 @@ func (dht *IpfsDHT) peerFromInfo(pbp *pb.Message_Peer) (peer.Peer, error) { return nil, err } - maddr, err := pbp.Address() + // add addresses we've just discovered + maddrs, err := pbp.Addresses() if err != nil { return nil, err } - p.AddAddress(maddr) + for _, maddr := range maddrs { + p.AddAddress(maddr) + } return p, nil } diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 07f21f18a..41013a633 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -210,14 +210,16 @@ func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.Peer, pmes *pb pid := peer.ID(pb.GetId()) if pid.Equal(p.ID()) { - addr, err := pb.Address() + maddrs, err := pb.Addresses() if err != nil { - log.Errorf("provider %s error with address %s", p, *pb.Addr) + log.Errorf("provider %s error with addresses %s", p, pb.Addrs) continue } - log.Infof("received provider %s %s for %s", p, addr, key) - p.AddAddress(addr) + log.Infof("received provider %s %s for %s", p, maddrs, key) + for _, maddr := range maddrs { + p.AddAddress(maddr) + } dht.providers.AddProvider(key, p) } else { diff --git a/routing/dht/pb/dht.pb.go b/routing/dht/pb/dht.pb.go index 3e52a94ed..e102ef7d3 100644 --- a/routing/dht/pb/dht.pb.go +++ b/routing/dht/pb/dht.pb.go @@ -15,10 +15,12 @@ It has these top-level messages: package dht_pb import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import json "encoding/json" import math "math" -// Reference imports to suppress errors if they are not otherwise used. +// Reference proto, json, and math imports to suppress error if they are not otherwise used. var _ = proto.Marshal +var _ = &json.SyntaxError{} var _ = math.Inf type Message_MessageType int32 @@ -66,6 +68,50 @@ func (x *Message_MessageType) UnmarshalJSON(data []byte) error { return nil } +type Message_ConnectionType int32 + +const ( + // sender does not have a connection to peer, and no extra information (default) + Message_NOT_CONNECTED Message_ConnectionType = 0 + // sender has a live connection to peer + Message_CONNECTED Message_ConnectionType = 1 + // sender recently connected to peer + Message_CAN_CONNECT Message_ConnectionType = 2 + // sender recently tried to connect to peer repeatedly but failed to connect + // ("try" here is loose, but this should signal "made strong effort, failed") + Message_CANNOT_CONNECT Message_ConnectionType = 3 +) + +var Message_ConnectionType_name = map[int32]string{ + 0: "NOT_CONNECTED", + 1: "CONNECTED", + 2: "CAN_CONNECT", + 3: "CANNOT_CONNECT", +} +var Message_ConnectionType_value = map[string]int32{ + "NOT_CONNECTED": 0, + "CONNECTED": 1, + "CAN_CONNECT": 2, + "CANNOT_CONNECT": 3, +} + +func (x Message_ConnectionType) Enum() *Message_ConnectionType { + p := new(Message_ConnectionType) + *p = x + return p +} +func (x Message_ConnectionType) String() string { + return proto.EnumName(Message_ConnectionType_name, int32(x)) +} +func (x *Message_ConnectionType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Message_ConnectionType_value, data, "Message_ConnectionType") + if err != nil { + return err + } + *x = Message_ConnectionType(value) + return nil +} + type Message struct { // defines what type of message it is. Type *Message_MessageType `protobuf:"varint,1,opt,name=type,enum=dht.pb.Message_MessageType" json:"type,omitempty"` @@ -133,9 +179,13 @@ func (m *Message) GetProviderPeers() []*Message_Peer { } type Message_Peer struct { - Id *string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` - Addr *string `protobuf:"bytes,2,opt,name=addr" json:"addr,omitempty"` - XXX_unrecognized []byte `json:"-"` + // ID of a given peer. + Id *string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` + // multiaddrs for a given peer + Addrs []string `protobuf:"bytes,2,rep,name=addrs" json:"addrs,omitempty"` + // used to signal the sender's connection capabilities to the peer + Connection *Message_ConnectionType `protobuf:"varint,3,opt,name=connection,enum=dht.pb.Message_ConnectionType" json:"connection,omitempty"` + XXX_unrecognized []byte `json:"-"` } func (m *Message_Peer) Reset() { *m = Message_Peer{} } @@ -149,11 +199,18 @@ func (m *Message_Peer) GetId() string { return "" } -func (m *Message_Peer) GetAddr() string { - if m != nil && m.Addr != nil { - return *m.Addr +func (m *Message_Peer) GetAddrs() []string { + if m != nil { + return m.Addrs } - return "" + return nil +} + +func (m *Message_Peer) GetConnection() Message_ConnectionType { + if m != nil && m.Connection != nil { + return *m.Connection + } + return Message_NOT_CONNECTED } // Record represents a dht record that contains a value @@ -204,4 +261,5 @@ func (m *Record) GetSignature() []byte { func init() { proto.RegisterEnum("dht.pb.Message_MessageType", Message_MessageType_name, Message_MessageType_value) + proto.RegisterEnum("dht.pb.Message_ConnectionType", Message_ConnectionType_name, Message_ConnectionType_value) } diff --git a/routing/dht/pb/dht.proto b/routing/dht/pb/dht.proto index 1b49a1552..6f31dd5e3 100644 --- a/routing/dht/pb/dht.proto +++ b/routing/dht/pb/dht.proto @@ -12,9 +12,30 @@ message Message { PING = 5; } + enum ConnectionType { + // sender does not have a connection to peer, and no extra information (default) + NOT_CONNECTED = 0; + + // sender has a live connection to peer + CONNECTED = 1; + + // sender recently connected to peer + CAN_CONNECT = 2; + + // sender recently tried to connect to peer repeatedly but failed to connect + // ("try" here is loose, but this should signal "made strong effort, failed") + CANNOT_CONNECT = 3; + } + message Peer { + // ID of a given peer. optional string id = 1; - optional string addr = 2; + + // multiaddrs for a given peer + repeated string addrs = 2; + + // used to signal the sender's connection capabilities to the peer + optional ConnectionType connection = 3; } // defines what type of message it is. diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index f8001c5f7..a7cc28b04 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -3,7 +3,6 @@ package dht_pb import ( "errors" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" peer "github.com/jbenet/go-ipfs/peer" ) @@ -19,12 +18,11 @@ func NewMessage(typ Message_MessageType, key string, level int) *Message { func peerToPBPeer(p peer.Peer) *Message_Peer { pbp := new(Message_Peer) - addrs := p.Addresses() - if len(addrs) == 0 || addrs[0] == nil { - pbp.Addr = proto.String("") - } else { - addr := addrs[0].String() - pbp.Addr = &addr + + maddrs := p.Addresses() + pbp.Addrs = make([]string, len(maddrs)) + for i, maddr := range maddrs { + pbp.Addrs[i] = maddr.String() } pid := string(p.ID()) pbp.Id = &pid @@ -41,12 +39,21 @@ func PeersToPBPeers(peers []peer.Peer) []*Message_Peer { return pbpeers } -// Address returns a multiaddr associated with the Message_Peer entry -func (m *Message_Peer) Address() (ma.Multiaddr, error) { +// Addresses returns a multiaddr associated with the Message_Peer entry +func (m *Message_Peer) Addresses() ([]ma.Multiaddr, error) { if m == nil { return nil, errors.New("MessagePeer is nil") } - return ma.NewMultiaddr(*m.Addr) + + var err error + maddrs := make([]ma.Multiaddr, len(m.Addrs)) + for i, addr := range m.Addrs { + maddrs[i], err = ma.NewMultiaddr(addr) + if err != nil { + return nil, err + } + } + return maddrs, nil } // GetClusterLevel gets and adjusts the cluster level on the message. diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 4e2dc3745..1074b23ec 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -241,12 +241,17 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) log.Warningf("Received invalid peer from query: %v", err) continue } - ma, err := pbp.Address() + + // add addresses + maddrs, err := pbp.Addresses() if err != nil { - log.Warning("Received peer with bad or missing address.") + log.Warning("Received peer with bad or missing addresses: %s", pbp.Addrs) continue } - np.AddAddress(ma) + for _, maddr := range maddrs { + np.AddAddress(maddr) + } + if pbp.GetId() == string(id) { return &dhtQueryResult{ peer: np, From 7a17d6b7a0f4bc9cb8a0bc6e00252715f789c8ec Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 20 Nov 2014 11:02:13 -0800 Subject: [PATCH 0492/3817] dht tests: dont introduce nil multiaddr this is the type of assumption we shouldn't violate. This commit was moved from ipfs/go-ipfs-routing@32cb5e94b06176982e983829a84ca505f77cd258 --- routing/dht/dht_test.go | 8 ++++++++ routing/dht/ext_test.go | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index e440964dc..524ebc763 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -64,6 +64,14 @@ func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []peer return addrs, peers, dhts } +func makePeerString(t *testing.T, addr string) peer.Peer { + maddr, err := ma.NewMultiaddr(addr) + if err != nil { + t.Fatal(err) + } + return makePeer(maddr) +} + func makePeer(addr ma.Multiaddr) peer.Peer { sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) if err != nil { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index fa536edd4..e0cae2a88 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -125,10 +125,10 @@ func TestGetFailures(t *testing.T) { fs := &fauxSender{} peerstore := peer.NewPeerstore() - local := makePeer(nil) + local := makePeerString(t, "") d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) - other := makePeer(nil) + other := makePeerString(t, "") d.Update(ctx, other) // This one should time out @@ -223,7 +223,7 @@ func TestNotFound(t *testing.T) { fn := &fauxNet{} fs := &fauxSender{} - local := makePeer(nil) + local := makePeerString(t, "") peerstore := peer.NewPeerstore() peerstore.Add(local) @@ -289,7 +289,7 @@ func TestLessThanKResponses(t *testing.T) { u.Debug = false fn := &fauxNet{} fs := &fauxSender{} - local := makePeer(nil) + local := makePeerString(t, "") peerstore := peer.NewPeerstore() peerstore.Add(local) From f30253a620e2b90982319dfbd7148933e31844d1 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 20 Nov 2014 18:45:05 -0800 Subject: [PATCH 0493/3817] net: add Connectedness var. This commit was moved from ipfs/go-ipfs-routing@edde08f4dae931c29dde6e6dd3aaf2efac5f95af --- routing/dht/ext_test.go | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index e0cae2a88..3e5a842be 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -8,6 +8,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + inet "github.com/jbenet/go-ipfs/net" msg "github.com/jbenet/go-ipfs/net/message" mux "github.com/jbenet/go-ipfs/net/mux" peer "github.com/jbenet/go-ipfs/peer" @@ -79,6 +80,7 @@ func (f *fauxSender) SendMessage(ctx context.Context, m msg.NetMessage) error { // fauxNet is a standin for a swarm.Network in order to more easily recreate // different testing scenarios type fauxNet struct { + local peer.Peer } // DialPeer attempts to establish a connection to a given peer @@ -86,6 +88,10 @@ func (f *fauxNet) DialPeer(context.Context, peer.Peer) error { return nil } +func (f *fauxNet) LocalPeer() peer.Peer { + return f.local +} + // ClosePeer connection to peer func (f *fauxNet) ClosePeer(peer.Peer) error { return nil @@ -96,6 +102,11 @@ func (f *fauxNet) IsConnected(peer.Peer) (bool, error) { return true, nil } +// Connectedness returns whether a connection to given peer exists. +func (f *fauxNet) Connectedness(peer.Peer) inet.Connectedness { + return inet.Connected +} + // GetProtocols returns the protocols registered in the network. func (f *fauxNet) GetProtocols() *mux.ProtocolMap { return nil } @@ -120,13 +131,13 @@ func TestGetFailures(t *testing.T) { t.SkipNow() } - ctx := context.Background() - fn := &fauxNet{} - fs := &fauxSender{} - peerstore := peer.NewPeerstore() local := makePeerString(t, "") + ctx := context.Background() + fn := &fauxNet{local} + fs := &fauxSender{} + d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) other := makePeerString(t, "") d.Update(ctx, other) @@ -219,14 +230,14 @@ func TestNotFound(t *testing.T) { t.SkipNow() } - ctx := context.Background() - fn := &fauxNet{} - fs := &fauxSender{} - local := makePeerString(t, "") peerstore := peer.NewPeerstore() peerstore.Add(local) + ctx := context.Background() + fn := &fauxNet{local} + fs := &fauxSender{} + d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) var ps []peer.Peer @@ -285,14 +296,15 @@ func TestNotFound(t *testing.T) { func TestLessThanKResponses(t *testing.T) { // t.Skip("skipping test because it makes a lot of output") - ctx := context.Background() - u.Debug = false - fn := &fauxNet{} - fs := &fauxSender{} local := makePeerString(t, "") peerstore := peer.NewPeerstore() peerstore.Add(local) + ctx := context.Background() + u.Debug = false + fn := &fauxNet{local} + fs := &fauxSender{} + d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) var ps []peer.Peer From e09a00993b66bc9c3dc7fcc9a1e3131c3923d9ee Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 21 Nov 2014 03:45:03 -0800 Subject: [PATCH 0494/3817] dht/pb: changed PeersToPBPeers to set ConnectionType Uses an inet.Dialer This commit was moved from ipfs/go-ipfs-routing@cbb356959d142607ab3d6eee07349d813a432af9 --- routing/dht/dht.go | 2 +- routing/dht/ext_test.go | 4 +-- routing/dht/handlers.go | 10 +++---- routing/dht/pb/message.go | 55 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 61 insertions(+), 10 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index d5aca0d7f..fff833e58 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -227,7 +227,7 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.Peer, key string) er pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, string(key), 0) // add self as the provider - pmes.ProviderPeers = pb.PeersToPBPeers([]peer.Peer{dht.self}) + pmes.ProviderPeers = pb.PeersToPBPeers(dht.dialer, []peer.Peer{dht.self}) rpmes, err := dht.sendRequest(ctx, p, pmes) if err != nil { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 3e5a842be..b2d72043d 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -262,7 +262,7 @@ func TestNotFound(t *testing.T) { for i := 0; i < 7; i++ { peers = append(peers, _randPeer()) } - resp.CloserPeers = pb.PeersToPBPeers(peers) + resp.CloserPeers = pb.PeersToPBPeers(d.dialer, peers) mes, err := msg.FromObject(mes.Peer(), resp) if err != nil { t.Error(err) @@ -326,7 +326,7 @@ func TestLessThanKResponses(t *testing.T) { case pb.Message_GET_VALUE: resp := &pb.Message{ Type: pmes.Type, - CloserPeers: pb.PeersToPBPeers([]peer.Peer{other}), + CloserPeers: pb.PeersToPBPeers(d.dialer, []peer.Peer{other}), } mes, err := msg.FromObject(mes.Peer(), resp) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 41013a633..a37091349 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -87,7 +87,7 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.Peer, pmes *pb.Me provs := dht.providers.GetProviders(ctx, u.Key(pmes.GetKey())) if len(provs) > 0 { log.Debugf("handleGetValue returning %d provider[s]", len(provs)) - resp.ProviderPeers = pb.PeersToPBPeers(provs) + resp.ProviderPeers = pb.PeersToPBPeers(dht.dialer, provs) } // Find closest peer on given cluster to desired key and reply with that info @@ -99,7 +99,7 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.Peer, pmes *pb.Me log.Critical("no addresses on peer being sent!") } } - resp.CloserPeers = pb.PeersToPBPeers(closer) + resp.CloserPeers = pb.PeersToPBPeers(dht.dialer, closer) } return resp, nil @@ -159,7 +159,7 @@ func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.Peer, pmes *pb.Me for _, p := range withAddresses { log.Debugf("handleFindPeer: sending back '%s'", p) } - resp.CloserPeers = pb.PeersToPBPeers(withAddresses) + resp.CloserPeers = pb.PeersToPBPeers(dht.dialer, withAddresses) return resp, nil } @@ -183,13 +183,13 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.Peer, pmes *p // if we've got providers, send thos those. if providers != nil && len(providers) > 0 { - resp.ProviderPeers = pb.PeersToPBPeers(providers) + resp.ProviderPeers = pb.PeersToPBPeers(dht.dialer, providers) } // Also send closer peers. closer := dht.betterPeersToQuery(pmes, CloserPeerCount) if closer != nil { - resp.CloserPeers = pb.PeersToPBPeers(closer) + resp.CloserPeers = pb.PeersToPBPeers(dht.dialer, closer) } return resp, nil diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index a7cc28b04..8f3b06c01 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,9 +4,12 @@ import ( "errors" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + + inet "github.com/jbenet/go-ipfs/net" peer "github.com/jbenet/go-ipfs/peer" ) +// NewMessage constructs a new dht message with given type, key, and level func NewMessage(typ Message_MessageType, key string, level int) *Message { m := &Message{ Type: &typ, @@ -29,9 +32,9 @@ func peerToPBPeer(p peer.Peer) *Message_Peer { return pbp } -// PeersToPBPeers converts a slice of Peers into a slice of *Message_Peers, +// RawPeersToPBPeers converts a slice of Peers into a slice of *Message_Peers, // ready to go out on the wire. -func PeersToPBPeers(peers []peer.Peer) []*Message_Peer { +func RawPeersToPBPeers(peers []peer.Peer) []*Message_Peer { pbpeers := make([]*Message_Peer, len(peers)) for i, p := range peers { pbpeers[i] = peerToPBPeer(p) @@ -39,6 +42,19 @@ func PeersToPBPeers(peers []peer.Peer) []*Message_Peer { return pbpeers } +// PeersToPBPeers converts given []peer.Peer into a set of []*Message_Peer, +// which can be written to a message and sent out. the key thing this function +// does (in addition to PeersToPBPeers) is set the ConnectionType with +// information from the given inet.Dialer. +func PeersToPBPeers(d inet.Dialer, peers []peer.Peer) []*Message_Peer { + pbps := RawPeersToPBPeers(peers) + for i, pbp := range pbps { + c := ConnectionType(d.Connectedness(peers[i])) + pbp.Connection = &c + } + return pbps +} + // Addresses returns a multiaddr associated with the Message_Peer entry func (m *Message_Peer) Addresses() ([]ma.Multiaddr, error) { if m == nil { @@ -75,6 +91,7 @@ func (m *Message) SetClusterLevel(level int) { m.ClusterLevelRaw = &lvl } +// Loggable turns a Message into machine-readable log output func (m *Message) Loggable() map[string]interface{} { return map[string]interface{}{ "message": map[string]string{ @@ -82,3 +99,37 @@ func (m *Message) Loggable() map[string]interface{} { }, } } + +// ConnectionType returns a Message_ConnectionType associated with the +// inet.Connectedness. +func ConnectionType(c inet.Connectedness) Message_ConnectionType { + switch c { + default: + return Message_NOT_CONNECTED + case inet.NotConnected: + return Message_NOT_CONNECTED + case inet.Connected: + return Message_CONNECTED + case inet.CanConnect: + return Message_CAN_CONNECT + case inet.CannotConnect: + return Message_CANNOT_CONNECT + } +} + +// Connectedness returns an inet.Connectedness associated with the +// Message_ConnectionType. +func Connectedness(c Message_ConnectionType) inet.Connectedness { + switch c { + default: + return inet.NotConnected + case Message_NOT_CONNECTED: + return inet.NotConnected + case Message_CONNECTED: + return inet.Connected + case Message_CAN_CONNECT: + return inet.CanConnect + case Message_CANNOT_CONNECT: + return inet.CannotConnect + } +} From beaad76970514a2c653f598cae205cde7498c213 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 21 Nov 2014 08:03:11 -0800 Subject: [PATCH 0495/3817] dht: update to use net.LocalPeer This commit was moved from ipfs/go-ipfs-routing@e2c0f9f68669da25901d37db77a1c033b9000cc3 --- routing/dht/dht.go | 57 ++++++++++----------------------------- routing/dht/pb/message.go | 37 +++++++++++++++++++++++++ routing/dht/query.go | 7 ++++- routing/dht/routing.go | 24 +++++------------ 4 files changed, 64 insertions(+), 61 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index fff833e58..6f3a846d3 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -274,14 +274,11 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, } // Perhaps we were given closer peers - var peers []peer.Peer - for _, pb := range pmes.GetCloserPeers() { - pr, err := dht.peerFromInfo(pb) + peers, errs := pb.PBPeersToPeers(dht.peerstore, pmes.GetCloserPeers()) + for _, err := range errs { if err != nil { log.Error(err) - continue } - peers = append(peers, pr) } if len(peers) > 0 { @@ -426,22 +423,20 @@ func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.Peer, key u. return dht.sendRequest(ctx, p, pmes) } -func (dht *IpfsDHT) addProviders(key u.Key, peers []*pb.Message_Peer) []peer.Peer { - var provArr []peer.Peer - for _, prov := range peers { - p, err := dht.peerFromInfo(prov) - if err != nil { - log.Errorf("error getting peer from info: %v", err) - continue - } - - log.Debugf("%s adding provider: %s for %s", dht.self, p, key) +func (dht *IpfsDHT) addProviders(key u.Key, pbps []*pb.Message_Peer) []peer.Peer { + peers, errs := pb.PBPeersToPeers(dht.peerstore, pbps) + for _, err := range errs { + log.Errorf("error converting peer: %v", err) + } + var provArr []peer.Peer + for _, p := range peers { // Dont add outselves to the list if p.ID().Equal(dht.self.ID()) { continue } + log.Debugf("%s adding provider: %s for %s", dht.self, p, key) // TODO(jbenet) ensure providers is idempotent dht.providers.AddProvider(key, p) provArr = append(provArr, p) @@ -500,38 +495,14 @@ func (dht *IpfsDHT) getPeer(id peer.ID) (peer.Peer, error) { return p, nil } -// peerFromInfo returns a peer using info in the protobuf peer struct -// to lookup or create a peer -func (dht *IpfsDHT) peerFromInfo(pbp *pb.Message_Peer) (peer.Peer, error) { - - id := peer.ID(pbp.GetId()) - - // bail out if it's ourselves - //TODO(jbenet) not sure this should be an error _here_ - if id.Equal(dht.self.ID()) { - return nil, errors.New("found self") - } - - p, err := dht.getPeer(id) - if err != nil { - return nil, err - } - - // add addresses we've just discovered - maddrs, err := pbp.Addresses() +func (dht *IpfsDHT) ensureConnectedToPeer(ctx context.Context, pbp *pb.Message_Peer) (peer.Peer, error) { + p, err := pb.PBPeerToPeer(dht.peerstore, pbp) if err != nil { return nil, err } - for _, maddr := range maddrs { - p.AddAddress(maddr) - } - return p, nil -} -func (dht *IpfsDHT) ensureConnectedToPeer(ctx context.Context, pbp *pb.Message_Peer) (peer.Peer, error) { - p, err := dht.peerFromInfo(pbp) - if err != nil { - return nil, err + if dht.dialer.LocalPeer().ID().Equal(p.ID()) { + return nil, errors.New("attempting to ensure connection to self") } // dial connection diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 8f3b06c01..82230422a 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -2,6 +2,7 @@ package dht_pb import ( "errors" + "fmt" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" @@ -32,6 +33,24 @@ func peerToPBPeer(p peer.Peer) *Message_Peer { return pbp } +// PBPeerToPeer turns a *Message_Peer into its peer.Peer counterpart +func PBPeerToPeer(ps peer.Peerstore, pbp *Message_Peer) (peer.Peer, error) { + p, err := ps.FindOrCreate(peer.ID(pbp.GetId())) + if err != nil { + return nil, fmt.Errorf("Failed to get peer from peerstore: %s", err) + } + + // add addresses + maddrs, err := pbp.Addresses() + if err != nil { + return nil, fmt.Errorf("Received peer with bad or missing addresses: %s", pbp.Addrs) + } + for _, maddr := range maddrs { + p.AddAddress(maddr) + } + return p, nil +} + // RawPeersToPBPeers converts a slice of Peers into a slice of *Message_Peers, // ready to go out on the wire. func RawPeersToPBPeers(peers []peer.Peer) []*Message_Peer { @@ -55,6 +74,24 @@ func PeersToPBPeers(d inet.Dialer, peers []peer.Peer) []*Message_Peer { return pbps } +// PBPeersToPeers converts given []*Message_Peer into a set of []peer.Peer +// Returns two slices, one of peers, and one of errors. The slice of peers +// will ONLY contain successfully converted peers. The slice of errors contains +// whether each input Message_Peer was successfully converted. +func PBPeersToPeers(ps peer.Peerstore, pbps []*Message_Peer) ([]peer.Peer, []error) { + errs := make([]error, len(pbps)) + peers := make([]peer.Peer, 0, len(pbps)) + for i, pbp := range pbps { + p, err := PBPeerToPeer(ps, pbp) + if err != nil { + errs[i] = err + } else { + peers = append(peers, p) + } + } + return peers, errs +} + // Addresses returns a multiaddr associated with the Message_Peer entry func (m *Message_Peer) Addresses() ([]ma.Multiaddr, error) { if m == nil { diff --git a/routing/dht/query.go b/routing/dht/query.go index f0478ff29..f4e43132d 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -161,7 +161,12 @@ func (r *dhtQueryRunner) addPeerToQuery(next peer.Peer, benchmark peer.Peer) { return } - // if new peer further away than whom we got it from, bother (loops) + // if new peer is ourselves... + if next.ID().Equal(r.query.dialer.LocalPeer().ID()) { + return + } + + // if new peer further away than whom we got it from, don't bother (loops) if benchmark != nil && kb.Closer(benchmark.ID(), next.ID(), r.query.key) { return } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 1074b23ec..7de0e1140 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -234,31 +234,21 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) } closer := pmes.GetCloserPeers() - var clpeers []peer.Peer - for _, pbp := range closer { - np, err := dht.getPeer(peer.ID(pbp.GetId())) + clpeers, errs := pb.PBPeersToPeers(dht.peerstore, closer) + for _, err := range errs { if err != nil { - log.Warningf("Received invalid peer from query: %v", err) - continue - } - - // add addresses - maddrs, err := pbp.Addresses() - if err != nil { - log.Warning("Received peer with bad or missing addresses: %s", pbp.Addrs) - continue - } - for _, maddr := range maddrs { - np.AddAddress(maddr) + log.Warning(err) } + } - if pbp.GetId() == string(id) { + // see it we got the peer here + for _, np := range clpeers { + if string(np.ID()) == string(id) { return &dhtQueryResult{ peer: np, success: true, }, nil } - clpeers = append(clpeers, np) } return &dhtQueryResult{closerPeers: clpeers}, nil From a1a31cfb29189b68b7ba83559fd5017eb2c31d21 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 24 Nov 2014 14:58:51 -0500 Subject: [PATCH 0496/3817] dht: FindPeersConnectedToPeer This commit was moved from ipfs/go-ipfs-routing@656a1b263ce3fad41222d56c2ba73c56391c44d4 --- routing/dht/dht_test.go | 95 +++++++++++++++++++++++++++++++++++++++++ routing/dht/handlers.go | 1 + routing/dht/routing.go | 70 ++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 524ebc763..71d5525b0 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -2,6 +2,7 @@ package dht import ( "bytes" + "sort" "testing" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -414,6 +415,100 @@ func TestFindPeer(t *testing.T) { } } +func TestFindPeersConnectedToPeer(t *testing.T) { + if testing.Short() { + t.SkipNow() + } + + ctx := context.Background() + u.Debug = false + + _, peers, dhts := setupDHTS(ctx, 4, t) + defer func() { + for i := 0; i < 4; i++ { + dhts[i].Close() + dhts[i].dialer.(inet.Network).Close() + } + }() + + // topology: + // 0-1, 1-2, 1-3, 2-3 + err := dhts[0].Connect(ctx, peers[1]) + if err != nil { + t.Fatal(err) + } + + err = dhts[1].Connect(ctx, peers[2]) + if err != nil { + t.Fatal(err) + } + + err = dhts[1].Connect(ctx, peers[3]) + if err != nil { + t.Fatal(err) + } + + err = dhts[2].Connect(ctx, peers[3]) + if err != nil { + t.Fatal(err) + } + + // fmt.Println("0 is", peers[0]) + // fmt.Println("1 is", peers[1]) + // fmt.Println("2 is", peers[2]) + // fmt.Println("3 is", peers[3]) + + ctxT, _ := context.WithTimeout(ctx, time.Second) + pchan, err := dhts[0].FindPeersConnectedToPeer(ctxT, peers[2].ID()) + if err != nil { + t.Fatal(err) + } + + // shouldFind := []peer.Peer{peers[1], peers[3]} + found := []peer.Peer{} + for nextp := range pchan { + found = append(found, nextp) + } + + // fmt.Printf("querying 0 (%s) FindPeersConnectedToPeer 2 (%s)\n", peers[0], peers[2]) + // fmt.Println("should find 1, 3", shouldFind) + // fmt.Println("found", found) + + // testPeerListsMatch(t, shouldFind, found) + + log.Warning("TestFindPeersConnectedToPeer is not quite correct") + if len(found) == 0 { + t.Fatal("didn't find any peers.") + } +} + +func testPeerListsMatch(t *testing.T, p1, p2 []peer.Peer) { + + if len(p1) != len(p2) { + t.Fatal("did not find as many peers as should have", p1, p2) + } + + ids1 := make([]string, len(p1)) + ids2 := make([]string, len(p2)) + + for i, p := range p1 { + ids1[i] = p.ID().Pretty() + } + + for i, p := range p2 { + ids2[i] = p.ID().Pretty() + } + + sort.Sort(sort.StringSlice(ids1)) + sort.Sort(sort.StringSlice(ids2)) + + for i := range ids1 { + if ids1[i] != ids2[i] { + t.Fatal("Didnt find expected peer", ids1[i], ids2) + } + } +} + func TestConnectCollision(t *testing.T) { if testing.Short() { t.SkipNow() diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index a37091349..f7e8073da 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -159,6 +159,7 @@ func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.Peer, pmes *pb.Me for _, p := range withAddresses { log.Debugf("handleFindPeer: sending back '%s'", p) } + resp.CloserPeers = pb.PeersToPBPeers(dht.dialer, withAddresses) return resp, nil } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 7de0e1140..f6442b1f1 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -5,6 +5,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + inet "github.com/jbenet/go-ipfs/net" peer "github.com/jbenet/go-ipfs/peer" "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" @@ -268,6 +269,75 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) return result.peer, nil } +// FindPeersConnectedToPeer searches for peers directly connected to a given peer. +func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (<-chan peer.Peer, error) { + + peerchan := make(chan peer.Peer, 10) + peersSeen := map[string]peer.Peer{} + + routeLevel := 0 + closest := dht.routingTables[routeLevel].NearestPeers(kb.ConvertPeerID(id), AlphaValue) + if closest == nil || len(closest) == 0 { + return nil, kb.ErrLookupFailure + } + + // setup the Query + query := newQuery(u.Key(id), dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + + pmes, err := dht.findPeerSingle(ctx, p, id, routeLevel) + if err != nil { + return nil, err + } + + var clpeers []peer.Peer + closer := pmes.GetCloserPeers() + for _, pbp := range closer { + // skip peers already seen + if _, found := peersSeen[string(pbp.GetId())]; found { + continue + } + + // skip peers that fail to unmarshal + p, err := pb.PBPeerToPeer(dht.peerstore, pbp) + if err != nil { + log.Warning(err) + continue + } + + // if peer is connected, send it to our client. + if pb.Connectedness(*pbp.Connection) == inet.Connected { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case peerchan <- p: + } + } + + peersSeen[string(p.ID())] = p + + // if peer is the peer we're looking for, don't bother querying it. + if pb.Connectedness(*pbp.Connection) != inet.Connected { + clpeers = append(clpeers, p) + } + } + + return &dhtQueryResult{closerPeers: clpeers}, nil + }) + + // run it! run it asynchronously to gen peers as results are found. + // this does no error checking + go func() { + if _, err := query.Run(ctx, closest); err != nil { + log.Error(err) + } + + // close the peerchan channel when done. + close(peerchan) + }() + + return peerchan, nil +} + // Ping a peer, log the time it took func (dht *IpfsDHT) Ping(ctx context.Context, p peer.Peer) error { // Thoughts: maybe this should accept an ID and do a peer lookup? From e4f54aaf9158f8cdbd09f32e46f2cde90286c812 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 8 Dec 2014 21:55:51 -0800 Subject: [PATCH 0497/3817] dht: comment for asyncQueryBuffer This commit was moved from ipfs/go-ipfs-routing@3363963b76ef6b8bdefe7c9d2940df9b203db7c3 --- routing/dht/routing.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index f6442b1f1..aeced86b1 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -13,6 +13,12 @@ import ( u "github.com/jbenet/go-ipfs/util" ) +// asyncQueryBuffer is the size of buffered channels in async queries. This +// buffer allows multiple queries to execute simultaneously, return their +// results and continue querying closer peers. Note that different query +// results will wait for the channel to drain. +var asyncQueryBuffer = 10 + // This file implements the Routing interface for the IpfsDHT struct. // Basic Put/Get @@ -272,7 +278,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) // FindPeersConnectedToPeer searches for peers directly connected to a given peer. func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (<-chan peer.Peer, error) { - peerchan := make(chan peer.Peer, 10) + peerchan := make(chan peer.Peer, asyncQueryBuffer) peersSeen := map[string]peer.Peer{} routeLevel := 0 From ef738955d1cf9b9c20b4ff45e299f6daec31825e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 11 Dec 2014 05:08:39 +0000 Subject: [PATCH 0498/3817] rewrite FindProvidersAsync This commit was moved from ipfs/go-ipfs-routing@8ce74b81af0b4a21ff2688340bd760c165085c96 --- routing/dht/handlers.go | 1 - routing/dht/routing.go | 95 ++++++++++++++++++++++++++++------------- 2 files changed, 65 insertions(+), 31 deletions(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index f7e8073da..5045a421e 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -182,7 +182,6 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.Peer, pmes *p providers = append(providers, dht.self) } - // if we've got providers, send thos those. if providers != nil && len(providers) > 0 { resp.ProviderPeers = pb.PeersToPBPeers(dht.dialer, providers) } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index aeced86b1..6b6b547f5 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -138,43 +138,78 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int) <-chan peer.Peer { log.Event(ctx, "findProviders", &key) peerOut := make(chan peer.Peer, count) - go func() { - defer close(peerOut) - - ps := newPeerSet() - // TODO may want to make this function async to hide latency - provs := dht.providers.GetProviders(ctx, key) - for _, p := range provs { - count-- - // NOTE: assuming that this list of peers is unique - ps.Add(p) + go dht.findProvidersAsyncRoutine(ctx, key, count, peerOut) + return peerOut +} + +func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, count int, peerOut chan peer.Peer) { + defer close(peerOut) + + ps := newPeerSet() + provs := dht.providers.GetProviders(ctx, key) + for _, p := range provs { + count-- + // NOTE: assuming that this list of peers is unique + ps.Add(p) + select { + case peerOut <- p: + case <-ctx.Done(): + return + } + if count <= 0 { + return + } + } + + // setup the Query + query := newQuery(key, dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + + pmes, err := dht.findProvidersSingle(ctx, p, key, 0) + if err != nil { + return nil, err + } + + provs, errs := pb.PBPeersToPeers(dht.peerstore, pmes.GetProviderPeers()) + for _, err := range errs { + if err != nil { + log.Warning(err) + } + } + + // Add unique providers from request, up to 'count' + for _, prov := range provs { + if ps.Contains(prov) { + continue + } select { - case peerOut <- p: + case peerOut <- prov: case <-ctx.Done(): - return + log.Error("Context timed out sending more providers") + return nil, ctx.Err() } - if count <= 0 { - return + ps.Add(prov) + if ps.Size() >= count { + return &dhtQueryResult{success: true}, nil } } - var wg sync.WaitGroup - peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), AlphaValue) - for _, pp := range peers { - wg.Add(1) - go func(p peer.Peer) { - defer wg.Done() - pmes, err := dht.findProvidersSingle(ctx, p, key, 0) - if err != nil { - log.Error(err) - return - } - dht.addPeerListAsync(ctx, key, pmes.GetProviderPeers(), ps, count, peerOut) - }(pp) + // Give closer peers back to the query to be queried + closer := pmes.GetCloserPeers() + clpeers, errs := pb.PBPeersToPeers(dht.peerstore, closer) + for _, err := range errs { + if err != nil { + log.Warning(err) + } } - wg.Wait() - }() - return peerOut + + return &dhtQueryResult{closerPeers: clpeers}, nil + }) + + peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), AlphaValue) + _, err := query.Run(ctx, peers) + if err != nil { + log.Errorf("FindProviders Query error: %s", err) + } } func (dht *IpfsDHT) addPeerListAsync(ctx context.Context, k u.Key, peers []*pb.Message_Peer, ps *peerSet, count int, out chan peer.Peer) { From e8599b7bb084e23b930ce4992cdad8b3a1ad985f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 11 Dec 2014 05:42:05 +0000 Subject: [PATCH 0499/3817] changes from PR This commit was moved from ipfs/go-ipfs-routing@d49387f3fd822b9b2d545fd693a52668902416f8 --- routing/dht/routing.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 6b6b547f5..fbdc7293a 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -148,15 +148,17 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co ps := newPeerSet() provs := dht.providers.GetProviders(ctx, key) for _, p := range provs { - count-- // NOTE: assuming that this list of peers is unique - ps.Add(p) - select { - case peerOut <- p: - case <-ctx.Done(): - return + if ps.AddIfSmallerThan(p, count) { + select { + case peerOut <- p: + case <-ctx.Done(): + return + } } - if count <= 0 { + + // If we have enough peers locally, dont bother with remote RPC + if ps.Size() >= count { return } } @@ -178,16 +180,14 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co // Add unique providers from request, up to 'count' for _, prov := range provs { - if ps.Contains(prov) { - continue - } - select { - case peerOut <- prov: - case <-ctx.Done(): - log.Error("Context timed out sending more providers") - return nil, ctx.Err() + if ps.AddIfSmallerThan(prov, count) { + select { + case peerOut <- prov: + case <-ctx.Done(): + log.Error("Context timed out sending more providers") + return nil, ctx.Err() + } } - ps.Add(prov) if ps.Size() >= count { return &dhtQueryResult{success: true}, nil } From 7a691f1cf5c457805bd5fe8a8d392378eb29781c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 11 Dec 2014 06:08:53 +0000 Subject: [PATCH 0500/3817] remove multilayered routing table from the DHT (for now) This commit was moved from ipfs/go-ipfs-routing@25824cf54ed1c6e23abc0d71bed492ce13cb2610 --- routing/dht/dht.go | 63 ++++++++++++++---------------------------- routing/dht/diag.go | 2 +- routing/dht/routing.go | 29 ++++++++----------- 3 files changed, 32 insertions(+), 62 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 6f3a846d3..caf6d8c9d 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -37,7 +37,7 @@ const doPinging = false type IpfsDHT struct { // Array of routing tables for differently distanced nodes // NOTE: (currently, only a single table is used) - routingTables []*kb.RoutingTable + routingTable *kb.RoutingTable // the network services we need dialer inet.Dialer @@ -80,10 +80,7 @@ func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, dialer inet.Dia dht.providers = NewProviderManager(dht.Context(), p.ID()) dht.AddCloserChild(dht.providers) - dht.routingTables = make([]*kb.RoutingTable, 3) - dht.routingTables[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID()), time.Millisecond*1000) - dht.routingTables[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID()), time.Millisecond*1000) - dht.routingTables[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID()), time.Hour) + dht.routingTable = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID()), time.Minute) dht.birth = time.Now() dht.Validators = make(map[string]ValidatorFunc) @@ -243,9 +240,9 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.Peer, key string) er } func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, - key u.Key, level int) ([]byte, []peer.Peer, error) { + key u.Key) ([]byte, []peer.Peer, error) { - pmes, err := dht.getValueSingle(ctx, p, key, level) + pmes, err := dht.getValueSingle(ctx, p, key) if err != nil { return nil, nil, err } @@ -265,7 +262,7 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, // TODO decide on providers. This probably shouldn't be happening. if prv := pmes.GetProviderPeers(); prv != nil && len(prv) > 0 { - val, err := dht.getFromPeerList(ctx, key, prv, level) + val, err := dht.getFromPeerList(ctx, key, prv) if err != nil { return nil, nil, err } @@ -292,9 +289,9 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, // getValueSingle simply performs the get value RPC with the given parameters func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.Peer, - key u.Key, level int) (*pb.Message, error) { + key u.Key) (*pb.Message, error) { - pmes := pb.NewMessage(pb.Message_GET_VALUE, string(key), level) + pmes := pb.NewMessage(pb.Message_GET_VALUE, string(key), 0) return dht.sendRequest(ctx, p, pmes) } @@ -303,7 +300,7 @@ func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.Peer, // one to get the value from? Or just connect to one at a time until we get a // successful connection and request the value from it? func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key, - peerlist []*pb.Message_Peer, level int) ([]byte, error) { + peerlist []*pb.Message_Peer) ([]byte, error) { for _, pinfo := range peerlist { p, err := dht.ensureConnectedToPeer(ctx, pinfo) @@ -312,7 +309,7 @@ func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key, continue } - pmes, err := dht.getValueSingle(ctx, p, key, level) + pmes, err := dht.getValueSingle(ctx, p, key) if err != nil { log.Errorf("getFromPeers error: %s\n", err) continue @@ -379,47 +376,30 @@ func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { return dht.datastore.Put(key.DsKey(), data) } -// Update signals to all routingTables to Update their last-seen status +// Update signals the routingTable to Update its last-seen status // on the given peer. func (dht *IpfsDHT) Update(ctx context.Context, p peer.Peer) { log.Event(ctx, "updatePeer", p) - removedCount := 0 - for _, route := range dht.routingTables { - removed := route.Update(p) - // Only close the connection if no tables refer to this peer - if removed != nil { - removedCount++ - } - } - - // Only close the connection if no tables refer to this peer - // if removedCount == len(dht.routingTables) { - // dht.network.ClosePeer(p) - // } - // ACTUALLY, no, let's not just close the connection. it may be connected - // due to other things. it seems that we just need connection timeouts - // after some deadline of inactivity. + dht.routingTable.Update(p) } // FindLocal looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in. func (dht *IpfsDHT) FindLocal(id peer.ID) (peer.Peer, *kb.RoutingTable) { - for _, table := range dht.routingTables { - p := table.Find(id) - if p != nil { - return p, table - } + p := dht.routingTable.Find(id) + if p != nil { + return p, dht.routingTable } return nil, nil } // findPeerSingle asks peer 'p' if they know where the peer with id 'id' is -func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.Peer, id peer.ID, level int) (*pb.Message, error) { - pmes := pb.NewMessage(pb.Message_FIND_NODE, string(id), level) +func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.Peer, id peer.ID) (*pb.Message, error) { + pmes := pb.NewMessage(pb.Message_FIND_NODE, string(id), 0) return dht.sendRequest(ctx, p, pmes) } -func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.Peer, key u.Key, level int) (*pb.Message, error) { - pmes := pb.NewMessage(pb.Message_GET_PROVIDERS, string(key), level) +func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.Peer, key u.Key) (*pb.Message, error) { + pmes := pb.NewMessage(pb.Message_GET_PROVIDERS, string(key), 0) return dht.sendRequest(ctx, p, pmes) } @@ -446,11 +426,8 @@ func (dht *IpfsDHT) addProviders(key u.Key, pbps []*pb.Message_Peer) []peer.Peer // nearestPeersToQuery returns the routing tables closest peers. func (dht *IpfsDHT) nearestPeersToQuery(pmes *pb.Message, count int) []peer.Peer { - level := pmes.GetClusterLevel() - cluster := dht.routingTables[level] - key := u.Key(pmes.GetKey()) - closer := cluster.NearestPeers(kb.ConvertKey(key), count) + closer := dht.routingTable.NearestPeers(kb.ConvertKey(key), count) return closer } @@ -537,7 +514,7 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { case <-tick: id := make([]byte, 16) rand.Read(id) - peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(u.Key(id)), 5) + peers := dht.routingTable.NearestPeers(kb.ConvertKey(u.Key(id)), 5) for _, p := range peers { ctx, _ := context.WithTimeout(dht.Context(), time.Second*5) err := dht.Ping(ctx, p) diff --git a/routing/dht/diag.go b/routing/dht/diag.go index e91ba9bee..82316e2e3 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -36,7 +36,7 @@ func (dht *IpfsDHT) getDiagInfo() *diagInfo { di.LifeSpan = time.Since(dht.birth) di.Keys = nil // Currently no way to query datastore - for _, p := range dht.routingTables[0].ListPeers() { + for _, p := range dht.routingTable.ListPeers() { d := connDiagInfo{p.GetLatency(), p.ID()} di.Connections = append(di.Connections, d) } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index fbdc7293a..3148e1589 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -38,11 +38,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error return err } - var peers []peer.Peer - for _, route := range dht.routingTables { - npeers := route.NearestPeers(kb.ConvertKey(key), KValue) - peers = append(peers, npeers...) - } + peers := dht.routingTable.NearestPeers(kb.ConvertKey(key), KValue) query := newQuery(key, dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { log.Debugf("%s PutValue qry part %v", dht.self, p) @@ -71,9 +67,8 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { return val, nil } - // get closest peers in the routing tables - routeLevel := 0 - closest := dht.routingTables[routeLevel].NearestPeers(kb.ConvertKey(key), PoolSize) + // get closest peers in the routing table + closest := dht.routingTable.NearestPeers(kb.ConvertKey(key), PoolSize) if closest == nil || len(closest) == 0 { log.Warning("Got no peers back from routing table!") return nil, kb.ErrLookupFailure @@ -82,7 +77,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { // setup the Query query := newQuery(key, dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { - val, peers, err := dht.getValueOrPeers(ctx, p, key, routeLevel) + val, peers, err := dht.getValueOrPeers(ctx, p, key) if err != nil { return nil, err } @@ -116,7 +111,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { dht.providers.AddProvider(key, dht.self) - peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), PoolSize) + peers := dht.routingTable.NearestPeers(kb.ConvertKey(key), PoolSize) if len(peers) == 0 { return nil } @@ -166,7 +161,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co // setup the Query query := newQuery(key, dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { - pmes, err := dht.findProvidersSingle(ctx, p, key, 0) + pmes, err := dht.findProvidersSingle(ctx, p, key) if err != nil { return nil, err } @@ -205,7 +200,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co return &dhtQueryResult{closerPeers: clpeers}, nil }) - peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), AlphaValue) + peers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) _, err := query.Run(ctx, peers) if err != nil { log.Errorf("FindProviders Query error: %s", err) @@ -253,8 +248,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) return p, nil } - routeLevel := 0 - closest := dht.routingTables[routeLevel].NearestPeers(kb.ConvertPeerID(id), AlphaValue) + closest := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue) if closest == nil || len(closest) == 0 { return nil, kb.ErrLookupFailure } @@ -270,7 +264,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) // setup the Query query := newQuery(u.Key(id), dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { - pmes, err := dht.findPeerSingle(ctx, p, id, routeLevel) + pmes, err := dht.findPeerSingle(ctx, p, id) if err != nil { return nil, err } @@ -316,8 +310,7 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< peerchan := make(chan peer.Peer, asyncQueryBuffer) peersSeen := map[string]peer.Peer{} - routeLevel := 0 - closest := dht.routingTables[routeLevel].NearestPeers(kb.ConvertPeerID(id), AlphaValue) + closest := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue) if closest == nil || len(closest) == 0 { return nil, kb.ErrLookupFailure } @@ -325,7 +318,7 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< // setup the Query query := newQuery(u.Key(id), dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { - pmes, err := dht.findPeerSingle(ctx, p, id, routeLevel) + pmes, err := dht.findPeerSingle(ctx, p, id) if err != nil { return nil, err } From 168dad0d2bfd06b53a9f79365fb8656ac5164aba Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 12 Dec 2014 20:05:54 -0800 Subject: [PATCH 0501/3817] refactor(mdag, bserv, bs) mocks, etc. License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-blockservice@0c7694cb42bb42118fbcce58f292d2c04a6a13dd --- blockservice/blocks_test.go | 21 ++------------------- blockservice/mock.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 19 deletions(-) create mode 100644 blockservice/mock.go diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index 2645b2024..e7966729f 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -11,10 +11,7 @@ import ( blocks "github.com/jbenet/go-ipfs/blocks" blockstore "github.com/jbenet/go-ipfs/blocks/blockstore" blocksutil "github.com/jbenet/go-ipfs/blocks/blocksutil" - bitswap "github.com/jbenet/go-ipfs/exchange/bitswap" - tn "github.com/jbenet/go-ipfs/exchange/bitswap/testnet" offline "github.com/jbenet/go-ipfs/exchange/offline" - "github.com/jbenet/go-ipfs/routing/mock" u "github.com/jbenet/go-ipfs/util" ) @@ -63,23 +60,9 @@ func TestBlocks(t *testing.T) { } func TestGetBlocksSequential(t *testing.T) { - net := tn.VirtualNetwork() - rs := mock.VirtualRoutingServer() - sg := bitswap.NewSessionGenerator(net, rs) + var servs = Mocks(t, 4) bg := blocksutil.NewBlockGenerator() - - instances := sg.Instances(4) blks := bg.Blocks(50) - // TODO: verify no duplicates - - var servs []*BlockService - for _, i := range instances { - bserv, err := New(i.Blockstore, i.Exchange) - if err != nil { - t.Fatal(err) - } - servs = append(servs, bserv) - } var keys []u.Key for _, blk := range blks { @@ -89,7 +72,7 @@ func TestGetBlocksSequential(t *testing.T) { t.Log("one instance at a time, get blocks concurrently") - for i := 1; i < len(instances); i++ { + for i := 1; i < len(servs); i++ { ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) out := servs[i].GetBlocks(ctx, keys) gotten := make(map[u.Key]*blocks.Block) diff --git a/blockservice/mock.go b/blockservice/mock.go new file mode 100644 index 000000000..0305c92fb --- /dev/null +++ b/blockservice/mock.go @@ -0,0 +1,28 @@ +package blockservice + +import ( + "testing" + + bitswap "github.com/jbenet/go-ipfs/exchange/bitswap" + tn "github.com/jbenet/go-ipfs/exchange/bitswap/testnet" + "github.com/jbenet/go-ipfs/routing/mock" +) + +// Mocks returns |n| connected mock Blockservices +func Mocks(t *testing.T, n int) []*BlockService { + net := tn.VirtualNetwork() + rs := mock.VirtualRoutingServer() + sg := bitswap.NewSessionGenerator(net, rs) + + instances := sg.Instances(n) + + var servs []*BlockService + for _, i := range instances { + bserv, err := New(i.Blockstore(), i.Exchange) + if err != nil { + t.Fatal(err) + } + servs = append(servs, bserv) + } + return servs +} From 913a39ec0134585122d1572d6fa3dc7a4adff3bd Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 12 Dec 2014 20:05:54 -0800 Subject: [PATCH 0502/3817] refactor(mdag, bserv, bs) mocks, etc. License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@3aaf646376076bbff69726333703c566a5ebbb9d --- routing/mock/routing.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/routing/mock/routing.go b/routing/mock/routing.go index ff83ddca3..23fe36644 100644 --- a/routing/mock/routing.go +++ b/routing/mock/routing.go @@ -30,10 +30,6 @@ func NewMockRouter(local peer.Peer, dstore ds.Datastore) routing.IpfsRouting { } } -func (mr *MockRouter) SetRoutingServer(rs RoutingServer) { - mr.hashTable = rs -} - func (mr *MockRouter) PutValue(ctx context.Context, key u.Key, val []byte) error { log.Debugf("PutValue: %s", key) return mr.datastore.Put(key.DsKey(), val) @@ -119,7 +115,8 @@ func (rs *hashTable) Announce(p peer.Peer, k u.Key) error { func (rs *hashTable) Providers(k u.Key) []peer.Peer { rs.lock.RLock() defer rs.lock.RUnlock() - ret := make([]peer.Peer, 0) + + var ret []peer.Peer peerset, ok := rs.providers[k] if !ok { return ret From b6e86a0841a2db06940f2fbebc912e7d04490337 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 12 Dec 2014 22:28:24 -0800 Subject: [PATCH 0503/3817] feat(bs/testnet) use delay in virtual network License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-blockservice@5cf1ea2db081e9b63f04f8616c61d8a94094c22e --- blockservice/mock.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/blockservice/mock.go b/blockservice/mock.go index 0305c92fb..2b646386c 100644 --- a/blockservice/mock.go +++ b/blockservice/mock.go @@ -5,12 +5,13 @@ import ( bitswap "github.com/jbenet/go-ipfs/exchange/bitswap" tn "github.com/jbenet/go-ipfs/exchange/bitswap/testnet" - "github.com/jbenet/go-ipfs/routing/mock" + mock "github.com/jbenet/go-ipfs/routing/mock" + delay "github.com/jbenet/go-ipfs/util/delay" ) // Mocks returns |n| connected mock Blockservices func Mocks(t *testing.T, n int) []*BlockService { - net := tn.VirtualNetwork() + net := tn.VirtualNetwork(delay.Fixed(0)) rs := mock.VirtualRoutingServer() sg := bitswap.NewSessionGenerator(net, rs) From 047a404620733c1fa099ef8d8ccd4d2652635ecf Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 12 Dec 2014 20:05:54 -0800 Subject: [PATCH 0504/3817] refactor(mdag, bserv, bs) mocks, etc. License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-merkledag@5284b7cc8c8c749e5b3c61dce7ac6308bee1427b --- ipld/merkledag/merkledag_test.go | 19 ++----------------- ipld/merkledag/mock.go | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 17 deletions(-) create mode 100644 ipld/merkledag/mock.go diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 0f628e6c1..b5f170c24 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -7,13 +7,10 @@ import ( "io/ioutil" "testing" - bserv "github.com/jbenet/go-ipfs/blockservice" - bs "github.com/jbenet/go-ipfs/exchange/bitswap" - tn "github.com/jbenet/go-ipfs/exchange/bitswap/testnet" + blockservice "github.com/jbenet/go-ipfs/blockservice" imp "github.com/jbenet/go-ipfs/importer" chunk "github.com/jbenet/go-ipfs/importer/chunk" . "github.com/jbenet/go-ipfs/merkledag" - "github.com/jbenet/go-ipfs/routing/mock" uio "github.com/jbenet/go-ipfs/unixfs/io" u "github.com/jbenet/go-ipfs/util" ) @@ -79,20 +76,8 @@ func makeTestDag(t *testing.T) *Node { } func TestBatchFetch(t *testing.T) { - net := tn.VirtualNetwork() - rs := mock.VirtualRoutingServer() - sg := bs.NewSessionGenerator(net, rs) - - instances := sg.Instances(5) - - var servs []*bserv.BlockService var dagservs []DAGService - for _, i := range instances { - bsi, err := bserv.New(i.Blockstore, i.Exchange) - if err != nil { - t.Fatal(err) - } - servs = append(servs, bsi) + for _, bsi := range blockservice.Mocks(t, 5) { dagservs = append(dagservs, NewDAGService(bsi)) } t.Log("finished setup.") diff --git a/ipld/merkledag/mock.go b/ipld/merkledag/mock.go new file mode 100644 index 000000000..ea3737f58 --- /dev/null +++ b/ipld/merkledag/mock.go @@ -0,0 +1,20 @@ +package merkledag + +import ( + "testing" + + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/jbenet/go-ipfs/blocks/blockstore" + bsrv "github.com/jbenet/go-ipfs/blockservice" + "github.com/jbenet/go-ipfs/exchange/offline" +) + +func Mock(t testing.TB) DAGService { + bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) + bserv, err := bsrv.New(bstore, offline.Exchange(bstore)) + if err != nil { + t.Fatal(err) + } + return NewDAGService(bserv) +} From 0dc5e6e178a7722be3d40504886a618145cc8596 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 12 Dec 2014 20:00:23 -0800 Subject: [PATCH 0505/3817] misc(blockstore) comment License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-blockstore@9a4a84b76a97b6dac3b0f010f21a030257b9d71f --- blockstore/blockstore.go | 1 + 1 file changed, 1 insertion(+) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 3fe742ef8..d4849cb43 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -14,6 +14,7 @@ import ( var ValueTypeMismatch = errors.New("The retrieved value is not a Block") +// Blockstore wraps a ThreadSafeDatastore type Blockstore interface { DeleteBlock(u.Key) error Has(u.Key) (bool, error) From a994d53d3d26321fc938994137ec480fd9d8b292 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 12 Dec 2014 22:56:36 -0800 Subject: [PATCH 0506/3817] refactor(mockrouting) misc License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@07e7f9a2aa3a91b38dcb31daf50e765d45597dc6 --- routing/mock/client.go | 74 +++++++++ routing/mock/interface.go | 40 +++++ .../{routing_test.go => mockrouting_test.go} | 54 +++---- routing/mock/routing.go | 141 ------------------ routing/mock/server.go | 76 ++++++++++ 5 files changed, 208 insertions(+), 177 deletions(-) create mode 100644 routing/mock/client.go create mode 100644 routing/mock/interface.go rename routing/mock/{routing_test.go => mockrouting_test.go} (74%) delete mode 100644 routing/mock/routing.go create mode 100644 routing/mock/server.go diff --git a/routing/mock/client.go b/routing/mock/client.go new file mode 100644 index 000000000..f4702aae6 --- /dev/null +++ b/routing/mock/client.go @@ -0,0 +1,74 @@ +package mockrouting + +import ( + "errors" + + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + peer "github.com/jbenet/go-ipfs/peer" + routing "github.com/jbenet/go-ipfs/routing" + u "github.com/jbenet/go-ipfs/util" +) + +var log = u.Logger("mockrouter") + +type client struct { + datastore ds.Datastore + server server + peer peer.Peer +} + +// FIXME(brian): is this method meant to simulate putting a value into the network? +func (c *client) PutValue(ctx context.Context, key u.Key, val []byte) error { + log.Debugf("PutValue: %s", key) + return c.datastore.Put(key.DsKey(), val) +} + +// FIXME(brian): is this method meant to simulate getting a value from the network? +func (c *client) GetValue(ctx context.Context, key u.Key) ([]byte, error) { + log.Debugf("GetValue: %s", key) + v, err := c.datastore.Get(key.DsKey()) + if err != nil { + return nil, err + } + + data, ok := v.([]byte) + if !ok { + return nil, errors.New("could not cast value from datastore") + } + + return data, nil +} + +func (c *client) FindProviders(ctx context.Context, key u.Key) ([]peer.Peer, error) { + return c.server.Providers(key), nil +} + +func (c *client) FindPeer(ctx context.Context, pid peer.ID) (peer.Peer, error) { + log.Debugf("FindPeer: %s", pid) + return nil, nil +} + +func (c *client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-chan peer.Peer { + out := make(chan peer.Peer) + go func() { + defer close(out) + for i, p := range c.server.Providers(k) { + if max <= i { + return + } + select { + case out <- p: + case <-ctx.Done(): + return + } + } + }() + return out +} + +func (c *client) Provide(_ context.Context, key u.Key) error { + return c.server.Announce(c.peer, key) +} + +var _ routing.IpfsRouting = &client{} diff --git a/routing/mock/interface.go b/routing/mock/interface.go new file mode 100644 index 000000000..e84a9ba5a --- /dev/null +++ b/routing/mock/interface.go @@ -0,0 +1,40 @@ +// Package mock provides a virtual routing server. To use it, create a virtual +// routing server and use the Client() method to get a routing client +// (IpfsRouting). The server quacks like a DHT but is really a local in-memory +// hash table. +package mockrouting + +import ( + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + peer "github.com/jbenet/go-ipfs/peer" + routing "github.com/jbenet/go-ipfs/routing" + u "github.com/jbenet/go-ipfs/util" + delay "github.com/jbenet/go-ipfs/util/delay" +) + +// Server provides mockrouting Clients +type Server interface { + Client(p peer.Peer) Client + ClientWithDatastore(peer.Peer, ds.Datastore) Client +} + +// Client implements IpfsRouting +type Client interface { + FindProviders(context.Context, u.Key) ([]peer.Peer, error) + + routing.IpfsRouting +} + +// NewServer returns a mockrouting Server +func NewServer() Server { + return NewServerWithDelay(delay.Fixed(0)) +} + +// NewServerWithDelay returns a mockrouting Server with a delay! +func NewServerWithDelay(d delay.D) Server { + return &s{ + providers: make(map[u.Key]peer.Map), + delay: d, + } +} diff --git a/routing/mock/routing_test.go b/routing/mock/mockrouting_test.go similarity index 74% rename from routing/mock/routing_test.go rename to routing/mock/mockrouting_test.go index 536d7b018..3f9bfab6c 100644 --- a/routing/mock/routing_test.go +++ b/routing/mock/mockrouting_test.go @@ -1,4 +1,4 @@ -package mock +package mockrouting import ( "bytes" @@ -12,37 +12,21 @@ import ( func TestKeyNotFound(t *testing.T) { - vrs := VirtualRoutingServer() - empty := vrs.Providers(u.Key("not there")) - if len(empty) != 0 { - t.Fatal("should be empty") - } -} + var peer = testutil.NewPeerWithID(peer.ID([]byte("the peer id"))) + var key = u.Key("mock key") + var ctx = context.Background() -func TestSetAndGet(t *testing.T) { - pid := peer.ID([]byte("the peer id")) - p := testutil.NewPeerWithID(pid) - k := u.Key("42") - rs := VirtualRoutingServer() - err := rs.Announce(p, k) - if err != nil { - t.Fatal(err) - } - providers := rs.Providers(k) - if len(providers) != 1 { - t.Fatal("should be one") + rs := NewServer() + providers := rs.Client(peer).FindProvidersAsync(ctx, key, 10) + _, ok := <-providers + if ok { + t.Fatal("should be closed") } - for _, elem := range providers { - if bytes.Equal(elem.ID(), pid) { - return - } - } - t.Fatal("ID should have matched") } func TestClientFindProviders(t *testing.T) { peer := testutil.NewPeerWithIDString("42") - rs := VirtualRoutingServer() + rs := NewServer() client := rs.Client(peer) k := u.Key("hello") @@ -52,7 +36,10 @@ func TestClientFindProviders(t *testing.T) { } max := 100 - providersFromHashTable := rs.Providers(k) + providersFromHashTable, err := rs.Client(peer).FindProviders(context.Background(), k) + if err != nil { + t.Fatal(err) + } isInHT := false for _, p := range providersFromHashTable { @@ -76,21 +63,16 @@ func TestClientFindProviders(t *testing.T) { } func TestClientOverMax(t *testing.T) { - rs := VirtualRoutingServer() + rs := NewServer() k := u.Key("hello") numProvidersForHelloKey := 100 for i := 0; i < numProvidersForHelloKey; i++ { peer := testutil.NewPeerWithIDString(string(i)) - err := rs.Announce(peer, k) + err := rs.Client(peer).Provide(context.Background(), k) if err != nil { t.Fatal(err) } } - providersFromHashTable := rs.Providers(k) - if len(providersFromHashTable) != numProvidersForHelloKey { - t.Log(1 == len(providersFromHashTable)) - t.Fatal("not all providers were returned") - } max := 10 peer := testutil.NewPeerWithIDString("TODO") @@ -108,7 +90,7 @@ func TestClientOverMax(t *testing.T) { // TODO does dht ensure won't receive self as a provider? probably not. func TestCanceledContext(t *testing.T) { - rs := VirtualRoutingServer() + rs := NewServer() k := u.Key("hello") t.Log("async'ly announce infinite stream of providers for key") @@ -116,7 +98,7 @@ func TestCanceledContext(t *testing.T) { go func() { // infinite stream for { peer := testutil.NewPeerWithIDString(string(i)) - err := rs.Announce(peer, k) + err := rs.Client(peer).Provide(context.Background(), k) if err != nil { t.Fatal(err) } diff --git a/routing/mock/routing.go b/routing/mock/routing.go deleted file mode 100644 index 23fe36644..000000000 --- a/routing/mock/routing.go +++ /dev/null @@ -1,141 +0,0 @@ -package mock - -import ( - "errors" - "math/rand" - "sync" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - peer "github.com/jbenet/go-ipfs/peer" - routing "github.com/jbenet/go-ipfs/routing" - u "github.com/jbenet/go-ipfs/util" -) - -var log = u.Logger("mockrouter") - -var _ routing.IpfsRouting = &MockRouter{} - -type MockRouter struct { - datastore ds.Datastore - hashTable RoutingServer - peer peer.Peer -} - -func NewMockRouter(local peer.Peer, dstore ds.Datastore) routing.IpfsRouting { - return &MockRouter{ - datastore: dstore, - peer: local, - hashTable: VirtualRoutingServer(), - } -} - -func (mr *MockRouter) PutValue(ctx context.Context, key u.Key, val []byte) error { - log.Debugf("PutValue: %s", key) - return mr.datastore.Put(key.DsKey(), val) -} - -func (mr *MockRouter) GetValue(ctx context.Context, key u.Key) ([]byte, error) { - log.Debugf("GetValue: %s", key) - v, err := mr.datastore.Get(key.DsKey()) - if err != nil { - return nil, err - } - - data, ok := v.([]byte) - if !ok { - return nil, errors.New("could not cast value from datastore") - } - - return data, nil -} - -func (mr *MockRouter) FindProviders(ctx context.Context, key u.Key) ([]peer.Peer, error) { - return nil, nil -} - -func (mr *MockRouter) FindPeer(ctx context.Context, pid peer.ID) (peer.Peer, error) { - log.Debugf("FindPeer: %s", pid) - return nil, nil -} - -func (mr *MockRouter) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-chan peer.Peer { - out := make(chan peer.Peer) - go func() { - defer close(out) - for i, p := range mr.hashTable.Providers(k) { - if max <= i { - return - } - select { - case out <- p: - case <-ctx.Done(): - return - } - } - }() - return out -} - -func (mr *MockRouter) Provide(_ context.Context, key u.Key) error { - return mr.hashTable.Announce(mr.peer, key) -} - -type RoutingServer interface { - Announce(peer.Peer, u.Key) error - - Providers(u.Key) []peer.Peer - - Client(p peer.Peer) routing.IpfsRouting -} - -func VirtualRoutingServer() RoutingServer { - return &hashTable{ - providers: make(map[u.Key]peer.Map), - } -} - -type hashTable struct { - lock sync.RWMutex - providers map[u.Key]peer.Map -} - -func (rs *hashTable) Announce(p peer.Peer, k u.Key) error { - rs.lock.Lock() - defer rs.lock.Unlock() - - _, ok := rs.providers[k] - if !ok { - rs.providers[k] = make(peer.Map) - } - rs.providers[k][p.Key()] = p - return nil -} - -func (rs *hashTable) Providers(k u.Key) []peer.Peer { - rs.lock.RLock() - defer rs.lock.RUnlock() - - var ret []peer.Peer - peerset, ok := rs.providers[k] - if !ok { - return ret - } - for _, peer := range peerset { - ret = append(ret, peer) - } - - for i := range ret { - j := rand.Intn(i + 1) - ret[i], ret[j] = ret[j], ret[i] - } - - return ret -} - -func (rs *hashTable) Client(p peer.Peer) routing.IpfsRouting { - return &MockRouter{ - peer: p, - hashTable: rs, - } -} diff --git a/routing/mock/server.go b/routing/mock/server.go new file mode 100644 index 000000000..3e189d954 --- /dev/null +++ b/routing/mock/server.go @@ -0,0 +1,76 @@ +package mockrouting + +import ( + "math/rand" + "sync" + + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + peer "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" + delay "github.com/jbenet/go-ipfs/util/delay" +) + +// server is the mockrouting.Client's private interface to the routing server +type server interface { + Announce(peer.Peer, u.Key) error + Providers(u.Key) []peer.Peer + + Server +} + +// s is an implementation of the private server interface +type s struct { + delay delay.D + + lock sync.RWMutex + providers map[u.Key]peer.Map +} + +func (rs *s) Announce(p peer.Peer, k u.Key) error { + rs.delay.Wait() // before locking + + rs.lock.Lock() + defer rs.lock.Unlock() + + _, ok := rs.providers[k] + if !ok { + rs.providers[k] = make(peer.Map) + } + rs.providers[k][p.Key()] = p + return nil +} + +func (rs *s) Providers(k u.Key) []peer.Peer { + rs.delay.Wait() // before locking + + rs.lock.RLock() + defer rs.lock.RUnlock() + + var ret []peer.Peer + peerset, ok := rs.providers[k] + if !ok { + return ret + } + for _, peer := range peerset { + ret = append(ret, peer) + } + + for i := range ret { + j := rand.Intn(i + 1) + ret[i], ret[j] = ret[j], ret[i] + } + + return ret +} + +func (rs *s) Client(p peer.Peer) Client { + return rs.ClientWithDatastore(p, ds.NewMapDatastore()) +} + +func (rs *s) ClientWithDatastore(p peer.Peer, datastore ds.Datastore) Client { + return &client{ + peer: p, + datastore: ds.NewMapDatastore(), + server: rs, + } +} From 096171199f25b964d2c416eb0e3d2ec309fa34a9 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 12 Dec 2014 22:56:36 -0800 Subject: [PATCH 0507/3817] refactor(mockrouting) misc License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-blockservice@cb2398d0e5e017d827b7d9aa724d3f271b3b6839 --- blockservice/mock.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/mock.go b/blockservice/mock.go index 2b646386c..277519746 100644 --- a/blockservice/mock.go +++ b/blockservice/mock.go @@ -5,14 +5,14 @@ import ( bitswap "github.com/jbenet/go-ipfs/exchange/bitswap" tn "github.com/jbenet/go-ipfs/exchange/bitswap/testnet" - mock "github.com/jbenet/go-ipfs/routing/mock" + mockrouting "github.com/jbenet/go-ipfs/routing/mock" delay "github.com/jbenet/go-ipfs/util/delay" ) // Mocks returns |n| connected mock Blockservices func Mocks(t *testing.T, n int) []*BlockService { net := tn.VirtualNetwork(delay.Fixed(0)) - rs := mock.VirtualRoutingServer() + rs := mockrouting.NewServer() sg := bitswap.NewSessionGenerator(net, rs) instances := sg.Instances(n) From 6b328ff05b2e9a4bbe446f2914f18fab3d172e74 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 12 Dec 2014 22:56:36 -0800 Subject: [PATCH 0508/3817] refactor(mockrouting) misc License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-namesys@ff4233077e87ad23126c3a6d9ac1f6d7180296c1 --- namesys/resolve_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index eef5e6825..1d487f9a7 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -3,17 +3,15 @@ package namesys import ( "testing" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ci "github.com/jbenet/go-ipfs/crypto" - mock "github.com/jbenet/go-ipfs/routing/mock" + mockrouting "github.com/jbenet/go-ipfs/routing/mock" u "github.com/jbenet/go-ipfs/util" testutil "github.com/jbenet/go-ipfs/util/testutil" ) func TestRoutingResolve(t *testing.T) { local := testutil.NewPeerWithIDString("testID") - lds := ds.NewMapDatastore() - d := mock.NewMockRouter(local, lds) + d := mockrouting.NewServer().Client(local) resolver := NewRoutingResolver(d) publisher := NewRoutingPublisher(d) From 0553b653d6a56be4e8ac7d7fbb90fd8dad8f408f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 14 Dec 2014 23:55:54 +0000 Subject: [PATCH 0509/3817] fix bug where a file containing duplicate blocks would fail to be read properly This commit was moved from ipfs/go-merkledag@a604200d4bdd3b6b1f668070a8a02aadcf6c345c --- ipld/merkledag/merkledag.go | 7 +++ ipld/merkledag/merkledag_test.go | 78 ++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index fbb07c9ee..ef66a9f2e 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -332,6 +332,13 @@ func (ds *dagService) GetDAG(ctx context.Context, root *Node) <-chan *Node { break } nodes[i] = nd + for { //Check for duplicate links + ni, err := FindLink(root, blk.Key(), nodes) + if err != nil { + break + } + nodes[ni] = nd + } if next == i { sig <- nd diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index b5f170c24..621378964 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -75,6 +75,25 @@ func makeTestDag(t *testing.T) *Node { return root } +type devZero struct{} + +func (_ devZero) Read(b []byte) (int, error) { + for i, _ := range b { + b[i] = 0 + } + return len(b), nil +} + +func makeZeroDag(t *testing.T) *Node { + read := io.LimitReader(devZero{}, 1024*32) + spl := &chunk.SizeSplitter{512} + root, err := imp.NewDagFromReaderWithSplitter(read, spl) + if err != nil { + t.Fatal(err) + } + return root +} + func TestBatchFetch(t *testing.T) { var dagservs []DAGService for _, bsi := range blockservice.Mocks(t, 5) { @@ -133,3 +152,62 @@ func TestBatchFetch(t *testing.T) { <-done } } + +func TestBatchFetchDupBlock(t *testing.T) { + var dagservs []DAGService + for _, bsi := range blockservice.Mocks(t, 5) { + dagservs = append(dagservs, NewDAGService(bsi)) + } + t.Log("finished setup.") + + root := makeZeroDag(t) + read, err := uio.NewDagReader(root, nil) + if err != nil { + t.Fatal(err) + } + expected, err := ioutil.ReadAll(read) + if err != nil { + t.Fatal(err) + } + + err = dagservs[0].AddRecursive(root) + if err != nil { + t.Fatal(err) + } + + t.Log("Added file to first node.") + + k, err := root.Key() + if err != nil { + t.Fatal(err) + } + + done := make(chan struct{}) + for i := 1; i < len(dagservs); i++ { + go func(i int) { + first, err := dagservs[i].Get(k) + if err != nil { + t.Fatal(err) + } + fmt.Println("Got first node back.") + + read, err := uio.NewDagReader(first, dagservs[i]) + if err != nil { + t.Fatal(err) + } + datagot, err := ioutil.ReadAll(read) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(datagot, expected) { + t.Fatal("Got bad data back!") + } + done <- struct{}{} + }(i) + } + + for i := 1; i < len(dagservs); i++ { + <-done + } +} From 5fe401e7f140c68ad122adb2b921f9db8af7ae68 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 14 Dec 2014 16:05:39 -0800 Subject: [PATCH 0510/3817] style(merkle): move var dec closer to use This commit was moved from ipfs/go-merkledag@c5d14421c0fbc18ebe9065c3ffe8df4e0572e180 --- ipld/merkledag/merkledag.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index ef66a9f2e..91062683e 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -308,14 +308,12 @@ func (ds *dagService) GetDAG(ctx context.Context, root *Node) <-chan *Node { sig := make(chan *Node) go func() { var keys []u.Key - nodes := make([]*Node, len(root.Links)) - for _, lnk := range root.Links { keys = append(keys, u.Key(lnk.Hash)) } - blkchan := ds.Blocks.GetBlocks(ctx, keys) + nodes := make([]*Node, len(root.Links)) next := 0 for blk := range blkchan { i, err := FindLink(root, blk.Key(), nodes) From f86aca7a88cc17973ba37435791ae12b5b8697c9 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 14 Dec 2014 16:06:53 -0800 Subject: [PATCH 0511/3817] fix(merkle) use defer This commit was moved from ipfs/go-merkledag@e55ff35fb4b3256010cda8238a66a7ac131a35fd --- ipld/merkledag/merkledag.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 91062683e..2fec1081a 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -307,6 +307,8 @@ func FindLink(n *Node, k u.Key, found []*Node) (int, error) { func (ds *dagService) GetDAG(ctx context.Context, root *Node) <-chan *Node { sig := make(chan *Node) go func() { + defer close(sig) + var keys []u.Key for _, lnk := range root.Links { keys = append(keys, u.Key(lnk.Hash)) @@ -350,7 +352,6 @@ func (ds *dagService) GetDAG(ctx context.Context, root *Node) <-chan *Node { // TODO: bubble errors back up. log.Errorf("Did not receive correct number of nodes!") } - close(sig) }() return sig From 2603aab3d2cb711ba5de201f93db0da2910f8d10 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 14 Dec 2014 16:07:55 -0800 Subject: [PATCH 0512/3817] Update merkledag.go This commit was moved from ipfs/go-merkledag@21f3add54656e1de423bbc841f18ee6033f7a0cf --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 2fec1081a..96ba514e5 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -327,7 +327,7 @@ func (ds *dagService) GetDAG(ctx context.Context, root *Node) <-chan *Node { nd, err := Decoded(blk.Data) if err != nil { // NB: can occur in normal situations, with improperly formatted - // input data + // input data log.Error("Got back bad block!") break } From ba8846bccfa288aa68c6cd6b2c9441e9ce0db1e5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 15 Dec 2014 01:03:53 +0000 Subject: [PATCH 0513/3817] change FindLink to FindLinks cc @maybebtc This commit was moved from ipfs/go-merkledag@d5fcd17e0cf128a8f373bd355bff18cf07f7cef5 --- ipld/merkledag/merkledag.go | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 96ba514e5..4f2efd81d 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -292,13 +292,14 @@ func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} // Searches this nodes links for one to the given key, // returns the index of said link -func FindLink(n *Node, k u.Key, found []*Node) (int, error) { +func FindLinks(n *Node, k u.Key) []int { + var out []int for i, lnk := range n.Links { - if u.Key(lnk.Hash) == k && found[i] == nil { - return i, nil + if u.Key(lnk.Hash) == k { + out = append(out, i) } } - return -1, u.ErrNotFound + return out } // GetDAG will fill out all of the links of the given Node. @@ -318,12 +319,6 @@ func (ds *dagService) GetDAG(ctx context.Context, root *Node) <-chan *Node { nodes := make([]*Node, len(root.Links)) next := 0 for blk := range blkchan { - i, err := FindLink(root, blk.Key(), nodes) - if err != nil { - // NB: can only occur as a result of programmer error - panic("Received block that wasnt in this nodes links!") - } - nd, err := Decoded(blk.Data) if err != nil { // NB: can occur in normal situations, with improperly formatted @@ -331,16 +326,12 @@ func (ds *dagService) GetDAG(ctx context.Context, root *Node) <-chan *Node { log.Error("Got back bad block!") break } - nodes[i] = nd - for { //Check for duplicate links - ni, err := FindLink(root, blk.Key(), nodes) - if err != nil { - break - } - nodes[ni] = nd + is := FindLinks(root, blk.Key()) + for _, i := range is { + nodes[i] = nd } - if next == i { + if next == is[0] { sig <- nd next++ for ; next < len(nodes) && nodes[next] != nil; next++ { From afb4bc3861ef4ac445cd2e059158ca0ee7bbc2fb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 15 Dec 2014 04:58:07 +0000 Subject: [PATCH 0514/3817] some cleanup, use WaitGroup over channel ugliness This commit was moved from ipfs/go-merkledag@0b6524cfa336da6a9a675650989ff0adbfc97b73 --- ipld/merkledag/merkledag_test.go | 72 +++++--------------------------- 1 file changed, 11 insertions(+), 61 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 621378964..de887e5a8 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "io/ioutil" + "sync" "testing" blockservice "github.com/jbenet/go-ipfs/blockservice" @@ -95,72 +96,22 @@ func makeZeroDag(t *testing.T) *Node { } func TestBatchFetch(t *testing.T) { - var dagservs []DAGService - for _, bsi := range blockservice.Mocks(t, 5) { - dagservs = append(dagservs, NewDAGService(bsi)) - } - t.Log("finished setup.") - root := makeTestDag(t) - read, err := uio.NewDagReader(root, nil) - if err != nil { - t.Fatal(err) - } - expected, err := ioutil.ReadAll(read) - if err != nil { - t.Fatal(err) - } - - err = dagservs[0].AddRecursive(root) - if err != nil { - t.Fatal(err) - } - - t.Log("Added file to first node.") - - k, err := root.Key() - if err != nil { - t.Fatal(err) - } - - done := make(chan struct{}) - for i := 1; i < len(dagservs); i++ { - go func(i int) { - first, err := dagservs[i].Get(k) - if err != nil { - t.Fatal(err) - } - fmt.Println("Got first node back.") - - read, err := uio.NewDagReader(first, dagservs[i]) - if err != nil { - t.Fatal(err) - } - datagot, err := ioutil.ReadAll(read) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(datagot, expected) { - t.Fatal("Got bad data back!") - } - done <- struct{}{} - }(i) - } - - for i := 1; i < len(dagservs); i++ { - <-done - } + runBatchFetchTest(t, root) } func TestBatchFetchDupBlock(t *testing.T) { + root := makeZeroDag(t) + runBatchFetchTest(t, root) +} + +func runBatchFetchTest(t *testing.T, root *Node) { var dagservs []DAGService for _, bsi := range blockservice.Mocks(t, 5) { dagservs = append(dagservs, NewDAGService(bsi)) } t.Log("finished setup.") - root := makeZeroDag(t) read, err := uio.NewDagReader(root, nil) if err != nil { t.Fatal(err) @@ -182,9 +133,11 @@ func TestBatchFetchDupBlock(t *testing.T) { t.Fatal(err) } - done := make(chan struct{}) + wg := sync.WaitGroup{} for i := 1; i < len(dagservs); i++ { + wg.Add(1) go func(i int) { + defer wg.Done() first, err := dagservs[i].Get(k) if err != nil { t.Fatal(err) @@ -203,11 +156,8 @@ func TestBatchFetchDupBlock(t *testing.T) { if !bytes.Equal(datagot, expected) { t.Fatal("Got bad data back!") } - done <- struct{}{} }(i) } - for i := 1; i < len(dagservs); i++ { - <-done - } + wg.Done() } From 459d0705fe274f33e32cf45b4e11233ccccbe500 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 15 Dec 2014 05:04:29 +0000 Subject: [PATCH 0515/3817] fix FindLinks comment This commit was moved from ipfs/go-merkledag@74915efd4b0ca5f20dd692c0e23bd5885c46bc31 --- ipld/merkledag/merkledag.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 4f2efd81d..2b2272cf8 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -290,8 +290,8 @@ func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} return done } -// Searches this nodes links for one to the given key, -// returns the index of said link +// FindLinks searches this nodes links for the given key, +// returns the indexes of any links pointing to it func FindLinks(n *Node, k u.Key) []int { var out []int for i, lnk := range n.Links { From b36ba80b51a45427a4291c5e2473edc6887238c9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 15 Dec 2014 05:16:15 +0000 Subject: [PATCH 0516/3817] cleanup from CR This commit was moved from ipfs/go-merkledag@b1e1ba8a90b30786cb09cab287384a393d2cbd3f --- ipld/merkledag/merkledag.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 2b2272cf8..d22e3a396 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -331,12 +331,8 @@ func (ds *dagService) GetDAG(ctx context.Context, root *Node) <-chan *Node { nodes[i] = nd } - if next == is[0] { - sig <- nd - next++ - for ; next < len(nodes) && nodes[next] != nil; next++ { - sig <- nodes[next] - } + for ; next < len(nodes) && nodes[next] != nil; next++ { + sig <- nodes[next] } } if next < len(nodes) { From 8ef550d77fb19a60b60d05b94f21b9eb25741e59 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 15 Dec 2014 20:30:18 -0800 Subject: [PATCH 0517/3817] fix: routing mock accuracy routing interface doesn't wait for value to appear in network, but value doesn't appear in network until time as passed License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@88a9bf04fd822f27aba685c0a34c3afae8579ae1 --- routing/mock/client.go | 2 ++ routing/mock/interface.go | 20 +++++++++++++---- routing/mock/mockrouting_test.go | 37 +++++++++++++++++++++++++++++++- routing/mock/server.go | 30 ++++++++++++++++---------- 4 files changed, 73 insertions(+), 16 deletions(-) diff --git a/routing/mock/client.go b/routing/mock/client.go index f4702aae6..444a4b960 100644 --- a/routing/mock/client.go +++ b/routing/mock/client.go @@ -67,6 +67,8 @@ func (c *client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-cha return out } +// Provide returns once the message is on the network. Value is not necessarily +// visible yet. func (c *client) Provide(_ context.Context, key u.Key) error { return c.server.Announce(c.peer, key) } diff --git a/routing/mock/interface.go b/routing/mock/interface.go index e84a9ba5a..639736292 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -28,13 +28,25 @@ type Client interface { // NewServer returns a mockrouting Server func NewServer() Server { - return NewServerWithDelay(delay.Fixed(0)) + return NewServerWithDelay(DelayConfig{ + ValueVisibility: delay.Fixed(0), + Query: delay.Fixed(0), + }) } // NewServerWithDelay returns a mockrouting Server with a delay! -func NewServerWithDelay(d delay.D) Server { +func NewServerWithDelay(conf DelayConfig) Server { return &s{ - providers: make(map[u.Key]peer.Map), - delay: d, + providers: make(map[u.Key]map[u.Key]providerRecord), + delayConf: conf, } } + +type DelayConfig struct { + // ValueVisibility is the time it takes for a value to be visible in the network + // FIXME there _must_ be a better term for this + ValueVisibility delay.D + + // Query is the time it takes to receive a response from a routing query + Query delay.D +} diff --git a/routing/mock/mockrouting_test.go b/routing/mock/mockrouting_test.go index 3f9bfab6c..6700cd8ed 100644 --- a/routing/mock/mockrouting_test.go +++ b/routing/mock/mockrouting_test.go @@ -3,10 +3,12 @@ package mockrouting import ( "bytes" "testing" + "time" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" + delay "github.com/jbenet/go-ipfs/util/delay" testutil "github.com/jbenet/go-ipfs/util/testutil" ) @@ -129,3 +131,36 @@ func TestCanceledContext(t *testing.T) { t.Fatal("Context cancel had no effect") } } + +func TestValidAfter(t *testing.T) { + + var p = testutil.NewPeerWithID(peer.ID([]byte("the peer id"))) + var key = u.Key("mock key") + var ctx = context.Background() + conf := DelayConfig{ + ValueVisibility: delay.Fixed(1 * time.Hour), + Query: delay.Fixed(0), + } + + rs := NewServerWithDelay(conf) + + rs.Client(p).Provide(ctx, key) + + var providers []peer.Peer + providers, err := rs.Client(p).FindProviders(ctx, key) + if err != nil { + t.Fatal(err) + } + if len(providers) > 0 { + t.Fail() + } + + conf.ValueVisibility.Set(0) + providers, err = rs.Client(p).FindProviders(ctx, key) + if err != nil { + t.Fatal(err) + } + if len(providers) != 1 { + t.Fail() + } +} diff --git a/routing/mock/server.go b/routing/mock/server.go index 3e189d954..e176c7aeb 100644 --- a/routing/mock/server.go +++ b/routing/mock/server.go @@ -3,11 +3,11 @@ package mockrouting import ( "math/rand" "sync" + "time" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" - delay "github.com/jbenet/go-ipfs/util/delay" ) // server is the mockrouting.Client's private interface to the routing server @@ -20,39 +20,47 @@ type server interface { // s is an implementation of the private server interface type s struct { - delay delay.D + delayConf DelayConfig lock sync.RWMutex - providers map[u.Key]peer.Map + providers map[u.Key]map[u.Key]providerRecord } -func (rs *s) Announce(p peer.Peer, k u.Key) error { - rs.delay.Wait() // before locking +type providerRecord struct { + Peer peer.Peer + Created time.Time +} +func (rs *s) Announce(p peer.Peer, k u.Key) error { rs.lock.Lock() defer rs.lock.Unlock() _, ok := rs.providers[k] if !ok { - rs.providers[k] = make(peer.Map) + rs.providers[k] = make(map[u.Key]providerRecord) + } + rs.providers[k][p.Key()] = providerRecord{ + Created: time.Now(), + Peer: p, } - rs.providers[k][p.Key()] = p return nil } func (rs *s) Providers(k u.Key) []peer.Peer { - rs.delay.Wait() // before locking + rs.delayConf.Query.Wait() // before locking rs.lock.RLock() defer rs.lock.RUnlock() var ret []peer.Peer - peerset, ok := rs.providers[k] + records, ok := rs.providers[k] if !ok { return ret } - for _, peer := range peerset { - ret = append(ret, peer) + for _, r := range records { + if time.Now().Sub(r.Created) > rs.delayConf.ValueVisibility.Get() { + ret = append(ret, r.Peer) + } } for i := range ret { From f7aa21e6a9ab1680f3d86c9d9ab84ec67bede0a5 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Dec 2014 08:55:46 -0800 Subject: [PATCH 0518/3817] Integrated new network into ipfs This commit was moved from ipfs/go-ipfs-routing@05158e5b41dd04def8f4cfbb7a6c8980442f5a56 --- routing/dht/dht.go | 144 ++++++-------------------------------- routing/dht/dht_test.go | 34 ++++----- routing/dht/ext_test.go | 2 - routing/dht/handlers.go | 10 +-- routing/dht/pb/message.go | 2 +- routing/dht/providers.go | 6 +- routing/dht/records.go | 2 +- routing/dht/routing.go | 10 +-- 8 files changed, 51 insertions(+), 159 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index caf6d8c9d..f85889afd 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -11,19 +11,17 @@ import ( "time" inet "github.com/jbenet/go-ipfs/net" - msg "github.com/jbenet/go-ipfs/net/message" peer "github.com/jbenet/go-ipfs/peer" routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" - ctxc "github.com/jbenet/go-ipfs/util/ctxcloser" "github.com/jbenet/go-ipfs/util/eventlog" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ) var log = eventlog.Logger("dht") @@ -35,50 +33,37 @@ const doPinging = false // IpfsDHT is an implementation of Kademlia with Coral and S/Kademlia modifications. // It is used to implement the base IpfsRouting module. type IpfsDHT struct { - // Array of routing tables for differently distanced nodes - // NOTE: (currently, only a single table is used) - routingTable *kb.RoutingTable - - // the network services we need - dialer inet.Dialer - sender inet.Sender + network inet.Network // the network services we need + self peer.Peer // Local peer (yourself) + peerstore peer.Peerstore // Other peers - // Local peer (yourself) - self peer.Peer - - // Other peers - peerstore peer.Peerstore - - // Local data - datastore ds.Datastore + datastore ds.Datastore // Local data dslock sync.Mutex - providers *ProviderManager - - // When this peer started up - birth time.Time + routingTable *kb.RoutingTable // Array of routing tables for differently distanced nodes + providers *ProviderManager - //lock to make diagnostics work better - diaglock sync.Mutex + birth time.Time // When this peer started up + diaglock sync.Mutex // lock to make diagnostics work better // record validator funcs Validators map[string]ValidatorFunc - ctxc.ContextCloser + ctxgroup.ContextGroup } // NewDHT creates a new DHT object with the given peer as the 'local' host -func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, dialer inet.Dialer, sender inet.Sender, dstore ds.Datastore) *IpfsDHT { +func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, n inet.Network, dstore ds.Datastore) *IpfsDHT { dht := new(IpfsDHT) - dht.dialer = dialer - dht.sender = sender dht.datastore = dstore dht.self = p dht.peerstore = ps - dht.ContextCloser = ctxc.NewContextCloser(ctx, nil) + dht.ContextGroup = ctxgroup.WithContext(ctx) + dht.network = n + n.SetHandler(inet.ProtocolDHT, dht.handleNewStream) dht.providers = NewProviderManager(dht.Context(), p.ID()) - dht.AddCloserChild(dht.providers) + dht.AddChildGroup(dht.providers) dht.routingTable = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID()), time.Minute) dht.birth = time.Now() @@ -95,7 +80,7 @@ func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, dialer inet.Dia // Connect to a new peer at the given address, ping and add to the routing table func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.Peer) error { - err := dht.dialer.DialPeer(ctx, npeer) + err := dht.network.DialPeer(ctx, npeer) if err != nil { return err } @@ -113,93 +98,6 @@ func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.Peer) error { return nil } -// HandleMessage implements the inet.Handler interface. -func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.NetMessage { - - mData := mes.Data() - if mData == nil { - log.Error("Message contained nil data.") - return nil - } - - mPeer := mes.Peer() - if mPeer == nil { - log.Error("Message contained nil peer.") - return nil - } - - // deserialize msg - pmes := new(pb.Message) - err := proto.Unmarshal(mData, pmes) - if err != nil { - log.Error("Error unmarshaling data") - return nil - } - - // update the peer (on valid msgs only) - dht.Update(ctx, mPeer) - - log.Event(ctx, "foo", dht.self, mPeer, pmes) - - // get handler for this msg type. - handler := dht.handlerForMsgType(pmes.GetType()) - if handler == nil { - log.Error("got back nil handler from handlerForMsgType") - return nil - } - - // dispatch handler. - rpmes, err := handler(ctx, mPeer, pmes) - if err != nil { - log.Errorf("handle message error: %s", err) - return nil - } - - // if nil response, return it before serializing - if rpmes == nil { - log.Warning("Got back nil response from request.") - return nil - } - - // serialize response msg - rmes, err := msg.FromObject(mPeer, rpmes) - if err != nil { - log.Errorf("serialze response error: %s", err) - return nil - } - - return rmes -} - -// sendRequest sends out a request using dht.sender, but also makes sure to -// measure the RTT for latency measurements. -func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { - - mes, err := msg.FromObject(p, pmes) - if err != nil { - return nil, err - } - - start := time.Now() - - rmes, err := dht.sender.SendRequest(ctx, mes) // respect? - if err != nil { - return nil, err - } - if rmes == nil { - return nil, errors.New("no response to request") - } - log.Event(ctx, "sentMessage", dht.self, p, pmes) - - rmes.Peer().SetLatency(time.Since(start)) - - rpmes := new(pb.Message) - if err := proto.Unmarshal(rmes.Data(), rpmes); err != nil { - return nil, err - } - return rpmes, nil -} - // putValueToNetwork stores the given key/value pair at the peer 'p' func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p peer.Peer, key string, rec *pb.Record) error { @@ -224,7 +122,7 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.Peer, key string) er pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, string(key), 0) // add self as the provider - pmes.ProviderPeers = pb.PeersToPBPeers(dht.dialer, []peer.Peer{dht.self}) + pmes.ProviderPeers = pb.PeersToPBPeers(dht.network, []peer.Peer{dht.self}) rpmes, err := dht.sendRequest(ctx, p, pmes) if err != nil { @@ -478,12 +376,12 @@ func (dht *IpfsDHT) ensureConnectedToPeer(ctx context.Context, pbp *pb.Message_P return nil, err } - if dht.dialer.LocalPeer().ID().Equal(p.ID()) { + if dht.self.ID().Equal(p.ID()) { return nil, errors.New("attempting to ensure connection to self") } // dial connection - err = dht.dialer.DialPeer(ctx, p) + err = dht.network.DialPeer(ctx, p) return p, err } @@ -536,7 +434,7 @@ func (dht *IpfsDHT) Bootstrap(ctx context.Context) { if err != nil { log.Errorf("Bootstrap peer error: %s", err) } - err = dht.dialer.DialPeer(ctx, p) + err = dht.network.DialPeer(ctx, p) if err != nil { log.Errorf("Bootstrap peer error: %s", err) } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 71d5525b0..a955290f9 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -12,8 +12,6 @@ import ( ci "github.com/jbenet/go-ipfs/crypto" inet "github.com/jbenet/go-ipfs/net" - mux "github.com/jbenet/go-ipfs/net/mux" - netservice "github.com/jbenet/go-ipfs/net/service" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" testutil "github.com/jbenet/go-ipfs/util/testutil" @@ -25,16 +23,14 @@ import ( func setupDHT(ctx context.Context, t *testing.T, p peer.Peer) *IpfsDHT { peerstore := peer.NewPeerstore() - dhts := netservice.NewService(ctx, nil) // nil handler for now, need to patch it - net, err := inet.NewIpfsNetwork(ctx, p.Addresses(), p, peerstore, &mux.ProtocolMap{ - mux.ProtocolID_Routing: dhts, - }) + n, err := inet.NewNetwork(ctx, p.Addresses(), p, peerstore) if err != nil { t.Fatal(err) } - d := NewDHT(ctx, p, peerstore, net, dhts, ds.NewMapDatastore()) - dhts.SetHandler(d) + d := NewDHT(ctx, p, peerstore, n, ds.NewMapDatastore()) + d.network.SetHandler(inet.ProtocolDHT, d.handleNewStream) + d.Validators["v"] = func(u.Key, []byte) error { return nil } @@ -107,8 +103,8 @@ func TestPing(t *testing.T) { defer dhtA.Close() defer dhtB.Close() - defer dhtA.dialer.(inet.Network).Close() - defer dhtB.dialer.(inet.Network).Close() + defer dhtA.network.Close() + defer dhtB.network.Close() err = dhtA.Connect(ctx, peerB) if err != nil { @@ -157,8 +153,8 @@ func TestValueGetSet(t *testing.T) { defer dhtA.Close() defer dhtB.Close() - defer dhtA.dialer.(inet.Network).Close() - defer dhtB.dialer.(inet.Network).Close() + defer dhtA.network.Close() + defer dhtB.network.Close() err = dhtA.Connect(ctx, peerB) if err != nil { @@ -199,7 +195,7 @@ func TestProvides(t *testing.T) { defer func() { for i := 0; i < 4; i++ { dhts[i].Close() - defer dhts[i].dialer.(inet.Network).Close() + defer dhts[i].network.Close() } }() @@ -261,7 +257,7 @@ func TestProvidesAsync(t *testing.T) { defer func() { for i := 0; i < 4; i++ { dhts[i].Close() - defer dhts[i].dialer.(inet.Network).Close() + defer dhts[i].network.Close() } }() @@ -326,7 +322,7 @@ func TestLayeredGet(t *testing.T) { defer func() { for i := 0; i < 4; i++ { dhts[i].Close() - defer dhts[i].dialer.(inet.Network).Close() + defer dhts[i].network.Close() } }() @@ -381,7 +377,7 @@ func TestFindPeer(t *testing.T) { defer func() { for i := 0; i < 4; i++ { dhts[i].Close() - dhts[i].dialer.(inet.Network).Close() + dhts[i].network.Close() } }() @@ -427,7 +423,7 @@ func TestFindPeersConnectedToPeer(t *testing.T) { defer func() { for i := 0; i < 4; i++ { dhts[i].Close() - dhts[i].dialer.(inet.Network).Close() + dhts[i].network.Close() } }() @@ -566,8 +562,8 @@ func TestConnectCollision(t *testing.T) { dhtA.Close() dhtB.Close() - dhtA.dialer.(inet.Network).Close() - dhtB.dialer.(inet.Network).Close() + dhtA.network.Close() + dhtB.network.Close() <-time.After(200 * time.Millisecond) } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index b2d72043d..f69d4d018 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -9,8 +9,6 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" inet "github.com/jbenet/go-ipfs/net" - msg "github.com/jbenet/go-ipfs/net/message" - mux "github.com/jbenet/go-ipfs/net/mux" peer "github.com/jbenet/go-ipfs/peer" routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 5045a421e..4319ef019 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -87,7 +87,7 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.Peer, pmes *pb.Me provs := dht.providers.GetProviders(ctx, u.Key(pmes.GetKey())) if len(provs) > 0 { log.Debugf("handleGetValue returning %d provider[s]", len(provs)) - resp.ProviderPeers = pb.PeersToPBPeers(dht.dialer, provs) + resp.ProviderPeers = pb.PeersToPBPeers(dht.network, provs) } // Find closest peer on given cluster to desired key and reply with that info @@ -99,7 +99,7 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.Peer, pmes *pb.Me log.Critical("no addresses on peer being sent!") } } - resp.CloserPeers = pb.PeersToPBPeers(dht.dialer, closer) + resp.CloserPeers = pb.PeersToPBPeers(dht.network, closer) } return resp, nil @@ -160,7 +160,7 @@ func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.Peer, pmes *pb.Me log.Debugf("handleFindPeer: sending back '%s'", p) } - resp.CloserPeers = pb.PeersToPBPeers(dht.dialer, withAddresses) + resp.CloserPeers = pb.PeersToPBPeers(dht.network, withAddresses) return resp, nil } @@ -183,13 +183,13 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.Peer, pmes *p } if providers != nil && len(providers) > 0 { - resp.ProviderPeers = pb.PeersToPBPeers(dht.dialer, providers) + resp.ProviderPeers = pb.PeersToPBPeers(dht.network, providers) } // Also send closer peers. closer := dht.betterPeersToQuery(pmes, CloserPeerCount) if closer != nil { - resp.CloserPeers = pb.PeersToPBPeers(dht.dialer, closer) + resp.CloserPeers = pb.PeersToPBPeers(dht.network, closer) } return resp, nil diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 82230422a..c5c4afea7 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -65,7 +65,7 @@ func RawPeersToPBPeers(peers []peer.Peer) []*Message_Peer { // which can be written to a message and sent out. the key thing this function // does (in addition to PeersToPBPeers) is set the ConnectionType with // information from the given inet.Dialer. -func PeersToPBPeers(d inet.Dialer, peers []peer.Peer) []*Message_Peer { +func PeersToPBPeers(d inet.Network, peers []peer.Peer) []*Message_Peer { pbps := RawPeersToPBPeers(peers) for i, pbp := range pbps { c := ConnectionType(d.Connectedness(peers[i])) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 0deea6324..928b3fa32 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -3,9 +3,9 @@ package dht import ( "time" + ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" - ctxc "github.com/jbenet/go-ipfs/util/ctxcloser" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ) @@ -18,7 +18,7 @@ type ProviderManager struct { newprovs chan *addProv getprovs chan *getProv period time.Duration - ctxc.ContextCloser + ctxgroup.ContextGroup } type addProv struct { @@ -38,7 +38,7 @@ func NewProviderManager(ctx context.Context, local peer.ID) *ProviderManager { pm.providers = make(map[u.Key][]*providerInfo) pm.getlocal = make(chan chan []u.Key) pm.local = make(map[u.Key]struct{}) - pm.ContextCloser = ctxc.NewContextCloser(ctx, nil) + pm.ContextGroup = ctxgroup.WithContext(ctx) pm.Children().Add(1) go pm.run() diff --git a/routing/dht/records.go b/routing/dht/records.go index 0ea455a17..1f284ed99 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -50,7 +50,7 @@ func (dht *IpfsDHT) getPublicKey(pid peer.ID) (ci.PubKey, error) { } log.Debug("not in peerstore, searching dht.") - ctxT, _ := context.WithTimeout(dht.ContextCloser.Context(), time.Second*5) + ctxT, _ := context.WithTimeout(dht.ContextGroup.Context(), time.Second*5) val, err := dht.GetValue(ctxT, u.Key("/pk/"+string(pid))) if err != nil { log.Warning("Failed to find requested public key.") diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 3148e1589..76260e710 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -40,7 +40,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error peers := dht.routingTable.NearestPeers(kb.ConvertKey(key), KValue) - query := newQuery(key, dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(key, dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { log.Debugf("%s PutValue qry part %v", dht.self, p) err := dht.putValueToNetwork(ctx, p, string(key), rec) if err != nil { @@ -75,7 +75,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { } // setup the Query - query := newQuery(key, dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(key, dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { val, peers, err := dht.getValueOrPeers(ctx, p, key) if err != nil { @@ -159,7 +159,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co } // setup the Query - query := newQuery(key, dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(key, dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { pmes, err := dht.findProvidersSingle(ctx, p, key) if err != nil { @@ -262,7 +262,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) } // setup the Query - query := newQuery(u.Key(id), dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(u.Key(id), dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { pmes, err := dht.findPeerSingle(ctx, p, id) if err != nil { @@ -316,7 +316,7 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< } // setup the Query - query := newQuery(u.Key(id), dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(u.Key(id), dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { pmes, err := dht.findPeerSingle(ctx, p, id) if err != nil { From bef2c622e502dd86db475d140e7f2b3e9ac64ecc Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Dec 2014 14:35:52 -0800 Subject: [PATCH 0519/3817] Lots of fixes. DHT tests pass This commit was moved from ipfs/go-ipfs-routing@153774b603f3eb4c12333854b76fb704aae326d7 --- routing/dht/dht.go | 8 +- routing/dht/dht_net.go | 104 ++++++++++++ routing/dht/dht_test.go | 75 ++++----- routing/dht/ext_test.go | 358 ++++++++++++++++------------------------ 4 files changed, 280 insertions(+), 265 deletions(-) create mode 100644 routing/dht/dht_net.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index f85889afd..5a68bd759 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -80,21 +80,17 @@ func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, n inet.Network, // Connect to a new peer at the given address, ping and add to the routing table func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.Peer) error { - err := dht.network.DialPeer(ctx, npeer) - if err != nil { + if err := dht.network.DialPeer(ctx, npeer); err != nil { return err } // Ping new peer to register in their routing table // NOTE: this should be done better... - err = dht.Ping(ctx, npeer) - if err != nil { + if err := dht.Ping(ctx, npeer); err != nil { return fmt.Errorf("failed to ping newly connected peer: %s\n", err) } log.Event(ctx, "connect", dht.self, npeer) - dht.Update(ctx, npeer) - return nil } diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go new file mode 100644 index 000000000..e31a52da7 --- /dev/null +++ b/routing/dht/dht_net.go @@ -0,0 +1,104 @@ +package dht + +import ( + "errors" + "time" + + inet "github.com/jbenet/go-ipfs/net" + peer "github.com/jbenet/go-ipfs/peer" + pb "github.com/jbenet/go-ipfs/routing/dht/pb" + + ggio "code.google.com/p/gogoprotobuf/io" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" +) + +// handleNewStream implements the inet.StreamHandler +func (dht *IpfsDHT) handleNewStream(s inet.Stream) { + go dht.handleNewMessage(s) +} + +func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { + defer s.Close() + + ctx := dht.Context() + r := ggio.NewDelimitedReader(s, inet.MessageSizeMax) + w := ggio.NewDelimitedWriter(s) + mPeer := s.Conn().RemotePeer() + + // receive msg + pmes := new(pb.Message) + if err := r.ReadMsg(pmes); err != nil { + log.Error("Error unmarshaling data") + return + } + // update the peer (on valid msgs only) + dht.Update(ctx, mPeer) + + log.Event(ctx, "foo", dht.self, mPeer, pmes) + + // get handler for this msg type. + handler := dht.handlerForMsgType(pmes.GetType()) + if handler == nil { + log.Error("got back nil handler from handlerForMsgType") + return + } + + // dispatch handler. + rpmes, err := handler(ctx, mPeer, pmes) + if err != nil { + log.Errorf("handle message error: %s", err) + return + } + + // if nil response, return it before serializing + if rpmes == nil { + log.Warning("Got back nil response from request.") + return + } + + // send out response msg + if err := w.WriteMsg(rpmes); err != nil { + log.Errorf("send response error: %s", err) + return + } + + return +} + +// sendRequest sends out a request, but also makes sure to +// measure the RTT for latency measurements. +func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { + + log.Debugf("%s dht starting stream", dht.self) + s, err := dht.network.NewStream(inet.ProtocolDHT, p) + if err != nil { + return nil, err + } + defer s.Close() + + r := ggio.NewDelimitedReader(s, inet.MessageSizeMax) + w := ggio.NewDelimitedWriter(s) + + start := time.Now() + + log.Debugf("%s writing", dht.self) + if err := w.WriteMsg(pmes); err != nil { + return nil, err + } + log.Event(ctx, "dhtSentMessage", dht.self, p, pmes) + + log.Debugf("%s reading", dht.self) + defer log.Debugf("%s done", dht.self) + + rpmes := new(pb.Message) + if err := r.ReadMsg(rpmes); err != nil { + return nil, err + } + if rpmes == nil { + return nil, errors.New("no response to request") + } + + p.SetLatency(time.Since(start)) + log.Event(ctx, "dhtReceivedMessage", dht.self, p, rpmes) + return rpmes, nil +} diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index a955290f9..50ec76792 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -2,6 +2,7 @@ package dht import ( "bytes" + "math/rand" "sort" "testing" @@ -20,6 +21,16 @@ import ( "time" ) +func randMultiaddr(t *testing.T) ma.Multiaddr { + + s := fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 10000+rand.Intn(40000)) + a, err := ma.NewMultiaddr(s) + if err != nil { + t.Fatal(err) + } + return a +} + func setupDHT(ctx context.Context, t *testing.T, p peer.Peer) *IpfsDHT { peerstore := peer.NewPeerstore() @@ -29,7 +40,6 @@ func setupDHT(ctx context.Context, t *testing.T, p peer.Peer) *IpfsDHT { } d := NewDHT(ctx, p, peerstore, n, ds.NewMapDatastore()) - d.network.SetHandler(inet.ProtocolDHT, d.handleNewStream) d.Validators["v"] = func(u.Key, []byte) error { return nil @@ -40,7 +50,8 @@ func setupDHT(ctx context.Context, t *testing.T, p peer.Peer) *IpfsDHT { func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []peer.Peer, []*IpfsDHT) { var addrs []ma.Multiaddr for i := 0; i < n; i++ { - a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 5000+i)) + r := rand.Intn(40000) + a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 10000+r)) if err != nil { t.Fatal(err) } @@ -85,15 +96,9 @@ func makePeer(addr ma.Multiaddr) peer.Peer { func TestPing(t *testing.T) { // t.Skip("skipping test to debug another") ctx := context.Background() - u.Debug = false - addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/2222") - if err != nil { - t.Fatal(err) - } - addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5678") - if err != nil { - t.Fatal(err) - } + + addrA := randMultiaddr(t) + addrB := randMultiaddr(t) peerA := makePeer(addrA) peerB := makePeer(addrB) @@ -106,21 +111,22 @@ func TestPing(t *testing.T) { defer dhtA.network.Close() defer dhtB.network.Close() - err = dhtA.Connect(ctx, peerB) - if err != nil { + if err := dhtA.Connect(ctx, peerB); err != nil { t.Fatal(err) } + // if err := dhtB.Connect(ctx, peerA); err != nil { + // t.Fatal(err) + // } + //Test that we can ping the node ctxT, _ := context.WithTimeout(ctx, 100*time.Millisecond) - err = dhtA.Ping(ctxT, peerB) - if err != nil { + if err := dhtA.Ping(ctxT, peerB); err != nil { t.Fatal(err) } ctxT, _ = context.WithTimeout(ctx, 100*time.Millisecond) - err = dhtB.Ping(ctxT, peerA) - if err != nil { + if err := dhtB.Ping(ctxT, peerA); err != nil { t.Fatal(err) } } @@ -129,15 +135,9 @@ func TestValueGetSet(t *testing.T) { // t.Skip("skipping test to debug another") ctx := context.Background() - u.Debug = false - addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/11235") - if err != nil { - t.Fatal(err) - } - addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/15679") - if err != nil { - t.Fatal(err) - } + + addrA := randMultiaddr(t) + addrB := randMultiaddr(t) peerA := makePeer(addrA) peerB := makePeer(addrB) @@ -156,7 +156,7 @@ func TestValueGetSet(t *testing.T) { defer dhtA.network.Close() defer dhtB.network.Close() - err = dhtA.Connect(ctx, peerB) + err := dhtA.Connect(ctx, peerB) if err != nil { t.Fatal(err) } @@ -189,8 +189,6 @@ func TestProvides(t *testing.T) { // t.Skip("skipping test to debug another") ctx := context.Background() - u.Debug = false - _, peers, dhts := setupDHTS(ctx, 4, t) defer func() { for i := 0; i < 4; i++ { @@ -251,7 +249,6 @@ func TestProvidesAsync(t *testing.T) { } ctx := context.Background() - u.Debug = false _, peers, dhts := setupDHTS(ctx, 4, t) defer func() { @@ -317,7 +314,7 @@ func TestLayeredGet(t *testing.T) { } ctx := context.Background() - u.Debug = false + _, peers, dhts := setupDHTS(ctx, 4, t) defer func() { for i := 0; i < 4; i++ { @@ -371,7 +368,6 @@ func TestFindPeer(t *testing.T) { } ctx := context.Background() - u.Debug = false _, peers, dhts := setupDHTS(ctx, 4, t) defer func() { @@ -412,12 +408,13 @@ func TestFindPeer(t *testing.T) { } func TestFindPeersConnectedToPeer(t *testing.T) { + t.Skip("not quite correct (see note)") + if testing.Short() { t.SkipNow() } ctx := context.Background() - u.Debug = false _, peers, dhts := setupDHTS(ctx, 4, t) defer func() { @@ -516,15 +513,9 @@ func TestConnectCollision(t *testing.T) { log.Notice("Running Time: ", rtime) ctx := context.Background() - u.Debug = false - addrA, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/11235") - if err != nil { - t.Fatal(err) - } - addrB, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/15679") - if err != nil { - t.Fatal(err) - } + + addrA := randMultiaddr(t) + addrB := randMultiaddr(t) peerA := makePeer(addrA) peerB := makePeer(addrB) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index f69d4d018..1fb46b521 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -1,150 +1,48 @@ package dht import ( + "math/rand" "testing" crand "crypto/rand" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" inet "github.com/jbenet/go-ipfs/net" + mocknet "github.com/jbenet/go-ipfs/net/mock" peer "github.com/jbenet/go-ipfs/peer" routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" testutil "github.com/jbenet/go-ipfs/util/testutil" - "sync" + ggio "code.google.com/p/gogoprotobuf/io" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + "time" ) -// mesHandleFunc is a function that takes in outgoing messages -// and can respond to them, simulating other peers on the network. -// returning nil will chose not to respond and pass the message onto the -// next registered handler -type mesHandleFunc func(msg.NetMessage) msg.NetMessage - -// fauxNet is a standin for a swarm.Network in order to more easily recreate -// different testing scenarios -type fauxSender struct { - sync.Mutex - handlers []mesHandleFunc -} - -func (f *fauxSender) AddHandler(fn func(msg.NetMessage) msg.NetMessage) { - f.Lock() - defer f.Unlock() - - f.handlers = append(f.handlers, fn) -} - -func (f *fauxSender) SendRequest(ctx context.Context, m msg.NetMessage) (msg.NetMessage, error) { - f.Lock() - handlers := make([]mesHandleFunc, len(f.handlers)) - copy(handlers, f.handlers) - f.Unlock() - - for _, h := range handlers { - reply := h(m) - if reply != nil { - return reply, nil - } - } - - // no reply? ok force a timeout - select { - case <-ctx.Done(): - } - - return nil, ctx.Err() -} - -func (f *fauxSender) SendMessage(ctx context.Context, m msg.NetMessage) error { - f.Lock() - handlers := make([]mesHandleFunc, len(f.handlers)) - copy(handlers, f.handlers) - f.Unlock() - - for _, h := range handlers { - reply := h(m) - if reply != nil { - return nil - } - } - return nil -} - -// fauxNet is a standin for a swarm.Network in order to more easily recreate -// different testing scenarios -type fauxNet struct { - local peer.Peer -} - -// DialPeer attempts to establish a connection to a given peer -func (f *fauxNet) DialPeer(context.Context, peer.Peer) error { - return nil -} - -func (f *fauxNet) LocalPeer() peer.Peer { - return f.local -} - -// ClosePeer connection to peer -func (f *fauxNet) ClosePeer(peer.Peer) error { - return nil -} - -// IsConnected returns whether a connection to given peer exists. -func (f *fauxNet) IsConnected(peer.Peer) (bool, error) { - return true, nil -} - -// Connectedness returns whether a connection to given peer exists. -func (f *fauxNet) Connectedness(peer.Peer) inet.Connectedness { - return inet.Connected -} - -// GetProtocols returns the protocols registered in the network. -func (f *fauxNet) GetProtocols() *mux.ProtocolMap { return nil } - -// SendMessage sends given Message out -func (f *fauxNet) SendMessage(msg.NetMessage) error { - return nil -} - -func (f *fauxNet) GetPeerList() []peer.Peer { - return nil -} - -func (f *fauxNet) GetBandwidthTotals() (uint64, uint64) { - return 0, 0 -} - -// Close terminates all network operation -func (f *fauxNet) Close() error { return nil } - func TestGetFailures(t *testing.T) { if testing.Short() { t.SkipNow() } + ctx := context.Background() peerstore := peer.NewPeerstore() local := makePeerString(t, "") + peers := []peer.Peer{local, testutil.RandPeer()} - ctx := context.Background() - fn := &fauxNet{local} - fs := &fauxSender{} + nets, err := mocknet.MakeNetworks(ctx, peers) + if err != nil { + t.Fatal(err) + } - d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) - other := makePeerString(t, "") - d.Update(ctx, other) + d := NewDHT(ctx, peers[0], peerstore, nets[0], ds.NewMapDatastore()) + d.Update(ctx, peers[1]) // This one should time out // u.POut("Timout Test\n") ctx1, _ := context.WithTimeout(context.Background(), time.Second) - _, err := d.GetValue(ctx1, u.Key("test")) - if err != nil { + if _, err := d.GetValue(ctx1, u.Key("test")); err != nil { if err != context.DeadlineExceeded { t.Fatal("Got different error than we expected", err) } @@ -152,20 +50,29 @@ func TestGetFailures(t *testing.T) { t.Fatal("Did not get expected error!") } + msgs := make(chan *pb.Message, 100) + // u.POut("NotFound Test\n") // Reply with failures to every message - fs.AddHandler(func(mes msg.NetMessage) msg.NetMessage { + nets[1].SetHandler(inet.ProtocolDHT, func(s inet.Stream) { + defer s.Close() + + pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) + pbw := ggio.NewDelimitedWriter(s) + pmes := new(pb.Message) - err := proto.Unmarshal(mes.Data(), pmes) - if err != nil { - t.Fatal(err) + if err := pbr.ReadMsg(pmes); err != nil { + panic(err) } resp := &pb.Message{ Type: pmes.Type, } - m, err := msg.FromObject(mes.Peer(), resp) - return m + if err := pbw.WriteMsg(resp); err != nil { + panic(err) + } + + msgs <- resp }) // This one should fail with NotFound @@ -179,40 +86,45 @@ func TestGetFailures(t *testing.T) { t.Fatal("expected error, got none.") } - fs.handlers = nil // Now we test this DHT's handleGetValue failure - typ := pb.Message_GET_VALUE - str := "hello" - rec, err := d.makePutRecord(u.Key(str), []byte("blah")) - if err != nil { - t.Fatal(err) - } - req := pb.Message{ - Type: &typ, - Key: &str, - Record: rec, - } + { + typ := pb.Message_GET_VALUE + str := "hello" + rec, err := d.makePutRecord(u.Key(str), []byte("blah")) + if err != nil { + t.Fatal(err) + } + req := pb.Message{ + Type: &typ, + Key: &str, + Record: rec, + } - // u.POut("handleGetValue Test\n") - mes, err := msg.FromObject(other, &req) - if err != nil { - t.Error(err) - } + // u.POut("handleGetValue Test\n") + s, err := nets[1].NewStream(inet.ProtocolDHT, peers[0]) + if err != nil { + t.Fatal(err) + } + defer s.Close() - mes = d.HandleMessage(ctx, mes) + pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) + pbw := ggio.NewDelimitedWriter(s) - pmes := new(pb.Message) - err = proto.Unmarshal(mes.Data(), pmes) - if err != nil { - t.Fatal(err) - } - if pmes.GetRecord() != nil { - t.Fatal("shouldnt have value") - } - if pmes.GetProviderPeers() != nil { - t.Fatal("shouldnt have provider peers") - } + if err := pbw.WriteMsg(&req); err != nil { + t.Fatal(err) + } + pmes := new(pb.Message) + if err := pbr.ReadMsg(pmes); err != nil { + t.Fatal(err) + } + if pmes.GetRecord() != nil { + t.Fatal("shouldnt have value") + } + if pmes.GetProviderPeers() != nil { + t.Fatal("shouldnt have provider peers") + } + } } // TODO: Maybe put these in some sort of "ipfs_testutil" package @@ -228,49 +140,57 @@ func TestNotFound(t *testing.T) { t.SkipNow() } - local := makePeerString(t, "") + ctx := context.Background() peerstore := peer.NewPeerstore() - peerstore.Add(local) - ctx := context.Background() - fn := &fauxNet{local} - fs := &fauxSender{} + var peers []peer.Peer + for i := 0; i < 16; i++ { + peers = append(peers, testutil.RandPeer()) + } - d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) + nets, err := mocknet.MakeNetworks(ctx, peers) + if err != nil { + t.Fatal(err) + } + + d := NewDHT(ctx, peers[0], peerstore, nets[0], ds.NewMapDatastore()) - var ps []peer.Peer - for i := 0; i < 5; i++ { - ps = append(ps, _randPeer()) - d.Update(ctx, ps[i]) + for _, p := range peers { + d.Update(ctx, p) } // Reply with random peers to every message - fs.AddHandler(func(mes msg.NetMessage) msg.NetMessage { - pmes := new(pb.Message) - err := proto.Unmarshal(mes.Data(), pmes) - if err != nil { - t.Fatal(err) - } + for _, neti := range nets { + neti.SetHandler(inet.ProtocolDHT, func(s inet.Stream) { + defer s.Close() - switch pmes.GetType() { - case pb.Message_GET_VALUE: - resp := &pb.Message{Type: pmes.Type} + pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) + pbw := ggio.NewDelimitedWriter(s) - peers := []peer.Peer{} - for i := 0; i < 7; i++ { - peers = append(peers, _randPeer()) - } - resp.CloserPeers = pb.PeersToPBPeers(d.dialer, peers) - mes, err := msg.FromObject(mes.Peer(), resp) - if err != nil { - t.Error(err) + pmes := new(pb.Message) + if err := pbr.ReadMsg(pmes); err != nil { + panic(err) } - return mes - default: - panic("Shouldnt recieve this.") - } - }) + switch pmes.GetType() { + case pb.Message_GET_VALUE: + resp := &pb.Message{Type: pmes.Type} + + ps := []peer.Peer{} + for i := 0; i < 7; i++ { + ps = append(ps, peers[rand.Intn(len(peers))]) + } + + resp.CloserPeers = pb.PeersToPBPeers(d.network, peers) + if err := pbw.WriteMsg(resp); err != nil { + panic(err) + } + + default: + panic("Shouldnt recieve this.") + } + }) + } ctx, _ = context.WithTimeout(ctx, time.Second*5) v, err := d.GetValue(ctx, u.Key("hello")) @@ -294,53 +214,57 @@ func TestNotFound(t *testing.T) { func TestLessThanKResponses(t *testing.T) { // t.Skip("skipping test because it makes a lot of output") - local := makePeerString(t, "") + ctx := context.Background() peerstore := peer.NewPeerstore() - peerstore.Add(local) - ctx := context.Background() - u.Debug = false - fn := &fauxNet{local} - fs := &fauxSender{} + var peers []peer.Peer + for i := 0; i < 6; i++ { + peers = append(peers, testutil.RandPeer()) + } + + nets, err := mocknet.MakeNetworks(ctx, peers) + if err != nil { + t.Fatal(err) + } - d := NewDHT(ctx, local, peerstore, fn, fs, ds.NewMapDatastore()) + d := NewDHT(ctx, peers[0], peerstore, nets[0], ds.NewMapDatastore()) - var ps []peer.Peer - for i := 0; i < 5; i++ { - ps = append(ps, _randPeer()) - d.Update(ctx, ps[i]) + for i := 1; i < 5; i++ { + d.Update(ctx, peers[i]) } - other := _randPeer() // Reply with random peers to every message - fs.AddHandler(func(mes msg.NetMessage) msg.NetMessage { - pmes := new(pb.Message) - err := proto.Unmarshal(mes.Data(), pmes) - if err != nil { - t.Fatal(err) - } + for _, neti := range nets { + neti.SetHandler(inet.ProtocolDHT, func(s inet.Stream) { + defer s.Close() + + pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) + pbw := ggio.NewDelimitedWriter(s) - switch pmes.GetType() { - case pb.Message_GET_VALUE: - resp := &pb.Message{ - Type: pmes.Type, - CloserPeers: pb.PeersToPBPeers(d.dialer, []peer.Peer{other}), + pmes := new(pb.Message) + if err := pbr.ReadMsg(pmes); err != nil { + panic(err) } - mes, err := msg.FromObject(mes.Peer(), resp) - if err != nil { - t.Error(err) + switch pmes.GetType() { + case pb.Message_GET_VALUE: + resp := &pb.Message{ + Type: pmes.Type, + CloserPeers: pb.PeersToPBPeers(d.network, []peer.Peer{peers[1]}), + } + + if err := pbw.WriteMsg(resp); err != nil { + panic(err) + } + default: + panic("Shouldnt recieve this.") } - return mes - default: - panic("Shouldnt recieve this.") - } - }) + }) + } ctx, _ = context.WithTimeout(ctx, time.Second*30) - _, err := d.GetValue(ctx, u.Key("hello")) - if err != nil { + if _, err := d.GetValue(ctx, u.Key("hello")); err != nil { switch err { case routing.ErrNotFound: //Success! From d1dd4f8ec01fa1a714563cd1dfcc746a9e6a97ae Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 16 Dec 2014 14:53:02 -0800 Subject: [PATCH 0520/3817] make vendor This commit was moved from ipfs/go-ipfs-routing@15c6bc15b1bd45fd2dc2b2adfb7b108c8bd2f1c8 --- routing/dht/dht_net.go | 2 +- routing/dht/ext_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index e31a52da7..d1898f15c 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -8,7 +8,7 @@ import ( peer "github.com/jbenet/go-ipfs/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" - ggio "code.google.com/p/gogoprotobuf/io" + ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 1fb46b521..018669608 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -14,7 +14,7 @@ import ( u "github.com/jbenet/go-ipfs/util" testutil "github.com/jbenet/go-ipfs/util/testutil" - ggio "code.google.com/p/gogoprotobuf/io" + ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" From 0a9007ddb2ac44219c1c759a5aa28f9cd3eef72f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 17 Dec 2014 08:02:59 -0800 Subject: [PATCH 0521/3817] transition dht to mock2 This commit was moved from ipfs/go-ipfs-routing@6bb28d6ad2b819afc720daedab1368afda9d2b9b --- routing/dht/ext_test.go | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 018669608..ace195c29 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -7,15 +7,15 @@ import ( crand "crypto/rand" inet "github.com/jbenet/go-ipfs/net" - mocknet "github.com/jbenet/go-ipfs/net/mock" + mocknet "github.com/jbenet/go-ipfs/net/mock2" peer "github.com/jbenet/go-ipfs/peer" routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" testutil "github.com/jbenet/go-ipfs/util/testutil" - ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" "time" @@ -27,16 +27,15 @@ func TestGetFailures(t *testing.T) { } ctx := context.Background() - peerstore := peer.NewPeerstore() - local := makePeerString(t, "") - peers := []peer.Peer{local, testutil.RandPeer()} - - nets, err := mocknet.MakeNetworks(ctx, peers) + mn, err := mocknet.FullMeshConnected(ctx, 2) if err != nil { t.Fatal(err) } + nets := mn.Nets() + peers := mn.Peers() - d := NewDHT(ctx, peers[0], peerstore, nets[0], ds.NewMapDatastore()) + ps := peer.NewPeerstore() + d := NewDHT(ctx, peers[0], ps, nets[0], ds.NewMapDatastore()) d.Update(ctx, peers[1]) // This one should time out @@ -141,17 +140,13 @@ func TestNotFound(t *testing.T) { } ctx := context.Background() - peerstore := peer.NewPeerstore() - - var peers []peer.Peer - for i := 0; i < 16; i++ { - peers = append(peers, testutil.RandPeer()) - } - - nets, err := mocknet.MakeNetworks(ctx, peers) + mn, err := mocknet.FullMeshConnected(ctx, 16) if err != nil { t.Fatal(err) } + nets := mn.Nets() + peers := mn.Peers() + peerstore := peer.NewPeerstore() d := NewDHT(ctx, peers[0], peerstore, nets[0], ds.NewMapDatastore()) @@ -215,17 +210,13 @@ func TestLessThanKResponses(t *testing.T) { // t.Skip("skipping test because it makes a lot of output") ctx := context.Background() - peerstore := peer.NewPeerstore() - - var peers []peer.Peer - for i := 0; i < 6; i++ { - peers = append(peers, testutil.RandPeer()) - } - - nets, err := mocknet.MakeNetworks(ctx, peers) + mn, err := mocknet.FullMeshConnected(ctx, 6) if err != nil { t.Fatal(err) } + nets := mn.Nets() + peers := mn.Peers() + peerstore := peer.NewPeerstore() d := NewDHT(ctx, peers[0], peerstore, nets[0], ds.NewMapDatastore()) From 9c6725ae3be682bd21a0a9e5e2c4ae7c9c4474bf Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 17 Dec 2014 08:04:02 -0800 Subject: [PATCH 0522/3817] mv net/mock2 -> net/mock This commit was moved from ipfs/go-ipfs-routing@50926368e886602a2507c5cf42951fa51be9c6dc --- routing/dht/ext_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index ace195c29..c7315d538 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -7,7 +7,7 @@ import ( crand "crypto/rand" inet "github.com/jbenet/go-ipfs/net" - mocknet "github.com/jbenet/go-ipfs/net/mock2" + mocknet "github.com/jbenet/go-ipfs/net/mock" peer "github.com/jbenet/go-ipfs/peer" routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" From 95b694458b3c333a1bad37192e15a5267d3f4d3f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 15 Dec 2014 01:33:04 +0000 Subject: [PATCH 0523/3817] rewrite sendWantlistToProviders This commit was moved from ipfs/go-ipfs-routing@7cb304d457940363e22361e055cebcde03a8c2a7 --- routing/dht/routing.go | 5 +++-- routing/dht/util.go | 44 ------------------------------------------ 2 files changed, 3 insertions(+), 46 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 76260e710..f8036ca38 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -11,6 +11,7 @@ import ( pb "github.com/jbenet/go-ipfs/routing/dht/pb" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" + pset "github.com/jbenet/go-ipfs/util/peerset" ) // asyncQueryBuffer is the size of buffered channels in async queries. This @@ -140,7 +141,7 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, count int, peerOut chan peer.Peer) { defer close(peerOut) - ps := newPeerSet() + ps := pset.NewPeerSet() provs := dht.providers.GetProviders(ctx, key) for _, p := range provs { // NOTE: assuming that this list of peers is unique @@ -207,7 +208,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co } } -func (dht *IpfsDHT) addPeerListAsync(ctx context.Context, k u.Key, peers []*pb.Message_Peer, ps *peerSet, count int, out chan peer.Peer) { +func (dht *IpfsDHT) addPeerListAsync(ctx context.Context, k u.Key, peers []*pb.Message_Peer, ps *pset.PeerSet, count int, out chan peer.Peer) { var wg sync.WaitGroup for _, pbp := range peers { wg.Add(1) diff --git a/routing/dht/util.go b/routing/dht/util.go index 00ac38dbc..2b0c1e2a2 100644 --- a/routing/dht/util.go +++ b/routing/dht/util.go @@ -2,8 +2,6 @@ package dht import ( "sync" - - peer "github.com/jbenet/go-ipfs/peer" ) // Pool size is the number of nodes used for group find/set RPC calls @@ -39,45 +37,3 @@ func (c *counter) Size() (s int) { c.mut.Unlock() return } - -// peerSet is a threadsafe set of peers -type peerSet struct { - ps map[string]bool - lk sync.RWMutex -} - -func newPeerSet() *peerSet { - ps := new(peerSet) - ps.ps = make(map[string]bool) - return ps -} - -func (ps *peerSet) Add(p peer.Peer) { - ps.lk.Lock() - ps.ps[string(p.ID())] = true - ps.lk.Unlock() -} - -func (ps *peerSet) Contains(p peer.Peer) bool { - ps.lk.RLock() - _, ok := ps.ps[string(p.ID())] - ps.lk.RUnlock() - return ok -} - -func (ps *peerSet) Size() int { - ps.lk.RLock() - defer ps.lk.RUnlock() - return len(ps.ps) -} - -func (ps *peerSet) AddIfSmallerThan(p peer.Peer, maxsize int) bool { - var success bool - ps.lk.Lock() - if _, ok := ps.ps[string(p.ID())]; !ok && len(ps.ps) < maxsize { - success = true - ps.ps[string(p.ID())] = true - } - ps.lk.Unlock() - return success -} From cc88460d295b5bc23d5e3569a400c23291388c28 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 10 Dec 2014 23:01:56 +0000 Subject: [PATCH 0524/3817] blockstore.ErrNotFound, and proper wantlist sorting This commit was moved from ipfs/go-blockservice@d63258a4c3d5f7fed2a93a615caa182570ab6d61 --- blockservice/blockservice.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index f44eaa0f5..9014cab46 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -8,8 +8,6 @@ import ( "fmt" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - blocks "github.com/jbenet/go-ipfs/blocks" "github.com/jbenet/go-ipfs/blocks/blockstore" exchange "github.com/jbenet/go-ipfs/exchange" @@ -67,7 +65,7 @@ func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, er return block, nil // TODO be careful checking ErrNotFound. If the underlying // implementation changes, this will break. - } else if err == ds.ErrNotFound && s.Exchange != nil { + } else if err == blockstore.ErrNotFound && s.Exchange != nil { log.Debug("Blockservice: Searching bitswap.") blk, err := s.Exchange.GetBlock(ctx, k) if err != nil { From 95cea3009bdf8d4f4381062bebfef46072138faf Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 10 Dec 2014 23:01:56 +0000 Subject: [PATCH 0525/3817] blockstore.ErrNotFound, and proper wantlist sorting This commit was moved from ipfs/go-ipfs-blockstore@bc4a13fd3309c14318e86e7ad5e973bb5e66633e --- blockstore/blockstore.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index d4849cb43..e203ffc50 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -6,14 +6,16 @@ import ( "errors" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + blocks "github.com/jbenet/go-ipfs/blocks" u "github.com/jbenet/go-ipfs/util" ) var ValueTypeMismatch = errors.New("The retrieved value is not a Block") +var ErrNotFound = errors.New("blockstore: block not found") + // Blockstore wraps a ThreadSafeDatastore type Blockstore interface { DeleteBlock(u.Key) error @@ -34,6 +36,9 @@ type blockstore struct { func (bs *blockstore) Get(k u.Key) (*blocks.Block, error) { maybeData, err := bs.datastore.Get(k.DsKey()) + if err == ds.ErrNotFound { + return nil, ErrNotFound + } if err != nil { return nil, err } From b2343c744294c66c048660ebb7e4d411c2cc88b6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 16 Dec 2014 04:01:15 +0000 Subject: [PATCH 0526/3817] change Provide RPC to not wait for an ACK, improves performance of 'Add' operations This commit was moved from ipfs/go-ipfs-routing@e838fd78f494692b9f8001bbd694b56e4c36eaad --- routing/dht/dht.go | 5 +---- routing/dht/dht_net.go | 23 ++++++++++++++++++++++- routing/mock/mockrouting_test.go | 4 ++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 5a68bd759..6e49c84cf 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -120,15 +120,12 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.Peer, key string) er // add self as the provider pmes.ProviderPeers = pb.PeersToPBPeers(dht.network, []peer.Peer{dht.self}) - rpmes, err := dht.sendRequest(ctx, p, pmes) + err := dht.sendMessage(ctx, p, pmes) if err != nil { return err } log.Debugf("%s putProvider: %s for %s", dht.self, p, u.Key(key)) - if rpmes.GetKey() != pmes.GetKey() { - return errors.New("provider not added correctly") - } return nil } diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index d1898f15c..6e46b4de6 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -8,8 +8,8 @@ import ( peer "github.com/jbenet/go-ipfs/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" - ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" ) // handleNewStream implements the inet.StreamHandler @@ -102,3 +102,24 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *pb.Messa log.Event(ctx, "dhtReceivedMessage", dht.self, p, rpmes) return rpmes, nil } + +// sendMessage sends out a message +func (dht *IpfsDHT) sendMessage(ctx context.Context, p peer.Peer, pmes *pb.Message) error { + + log.Debugf("%s dht starting stream", dht.self) + s, err := dht.network.NewStream(inet.ProtocolDHT, p) + if err != nil { + return err + } + defer s.Close() + + w := ggio.NewDelimitedWriter(s) + + log.Debugf("%s writing", dht.self) + if err := w.WriteMsg(pmes); err != nil { + return err + } + log.Event(ctx, "dhtSentMessage", dht.self, p, pmes) + log.Debugf("%s done", dht.self) + return nil +} diff --git a/routing/mock/mockrouting_test.go b/routing/mock/mockrouting_test.go index 6700cd8ed..44b1b52bd 100644 --- a/routing/mock/mockrouting_test.go +++ b/routing/mock/mockrouting_test.go @@ -36,6 +36,9 @@ func TestClientFindProviders(t *testing.T) { if err != nil { t.Fatal(err) } + + // This is bad... but simulating networks is hard + time.Sleep(time.Millisecond * 300) max := 100 providersFromHashTable, err := rs.Client(peer).FindProviders(context.Background(), k) @@ -160,6 +163,7 @@ func TestValidAfter(t *testing.T) { if err != nil { t.Fatal(err) } + t.Log("providers", providers) if len(providers) != 1 { t.Fail() } From 0527a1fbef2fd44fe539a21679d3de70c5ff575a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 16 Dec 2014 04:01:15 +0000 Subject: [PATCH 0527/3817] change Provide RPC to not wait for an ACK, improves performance of 'Add' operations This commit was moved from ipfs/go-merkledag@6fec6bba365ab5a4b8533882a9a20676ac662ec7 --- ipld/merkledag/merkledag.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index d22e3a396..3d8916b03 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -2,6 +2,7 @@ package merkledag import ( + "bytes" "fmt" "sync" "time" @@ -294,8 +295,9 @@ func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} // returns the indexes of any links pointing to it func FindLinks(n *Node, k u.Key) []int { var out []int + keybytes := []byte(k) for i, lnk := range n.Links { - if u.Key(lnk.Hash) == k { + if bytes.Equal([]byte(lnk.Hash), keybytes) { out = append(out, i) } } From 6965d9606f17d860b16eba7fad61b2f653e1006b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 16 Dec 2014 18:33:36 +0000 Subject: [PATCH 0528/3817] refactor peerSet This commit was moved from ipfs/go-ipfs-routing@352eec315e9f6d0c0171fa10edbe101ed7c1fda6 --- routing/dht/routing.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index f8036ca38..bfe38e200 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -141,11 +141,11 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, count int, peerOut chan peer.Peer) { defer close(peerOut) - ps := pset.NewPeerSet() + ps := pset.NewLimitedPeerSet(count) provs := dht.providers.GetProviders(ctx, key) for _, p := range provs { // NOTE: assuming that this list of peers is unique - if ps.AddIfSmallerThan(p, count) { + if ps.TryAdd(p) { select { case peerOut <- p: case <-ctx.Done(): @@ -176,7 +176,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co // Add unique providers from request, up to 'count' for _, prov := range provs { - if ps.AddIfSmallerThan(prov, count) { + if ps.TryAdd(prov) { select { case peerOut <- prov: case <-ctx.Done(): @@ -226,7 +226,7 @@ func (dht *IpfsDHT) addPeerListAsync(ctx context.Context, k u.Key, peers []*pb.M } dht.providers.AddProvider(k, p) - if ps.AddIfSmallerThan(p, count) { + if ps.TryAdd(p) { select { case out <- p: case <-ctx.Done(): From 258ad30cf86d429542fa9f9f7bf0552b04d923f7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 17 Dec 2014 19:27:41 +0000 Subject: [PATCH 0529/3817] clean peerset constructor names This commit was moved from ipfs/go-ipfs-routing@54d693e1da3034ba510a56de912d5c6cf4173d5b --- routing/dht/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index bfe38e200..51f15ff21 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -141,7 +141,7 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, count int, peerOut chan peer.Peer) { defer close(peerOut) - ps := pset.NewLimitedPeerSet(count) + ps := pset.NewLimited(count) provs := dht.providers.GetProviders(ctx, key) for _, p := range provs { // NOTE: assuming that this list of peers is unique From 8ddd3b49aee5c2ec6911544fcf89e232cb41c3ea Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 19 Dec 2014 12:19:56 -0800 Subject: [PATCH 0530/3817] peer change: peer.Peer -> peer.ID this is a major refactor of the entire codebase it changes the monolithic peer.Peer into using a peer.ID and a peer.Peerstore. Other changes: - removed handshake3. - testutil vastly simplified peer - secio bugfix + debugging logs - testutil: RandKeyPair - backpressure bugfix: w.o.w. - peer: added hex enc/dec - peer: added a PeerInfo struct PeerInfo is a small struct used to pass around a peer with a set of addresses and keys. This is not meant to be a complete view of the system, but rather to model updates to the peerstore. It is used by things like the routing system. - updated peer/queue + peerset - latency metrics - testutil: use crand for PeerID gen RandPeerID generates random "valid" peer IDs. it does not NEED to generate keys because it is as if we lost the key right away. fine to read some randomness and hash it. to generate proper keys and an ID, use: sk, pk, _ := testutil.RandKeyPair() id, _ := peer.IDFromPublicKey(pk) Also added RandPeerIDFatal helper - removed old spipe - updated seccat - core: cleanup initIdentity - removed old getFromPeerList This commit was moved from ipfs/go-ipfs-routing@59fe3ced9e2b4ec1da1fe1f2d45f96458a2b3ace --- routing/dht/dht.go | 188 +++++++------------ routing/dht/dht_net.go | 6 +- routing/dht/dht_test.go | 297 +++++++++++-------------------- routing/dht/diag.go | 4 +- routing/dht/ext_test.go | 38 ++-- routing/dht/handlers.go | 106 +++++------ routing/dht/pb/dht.pb.go | 4 +- routing/dht/pb/dht.proto | 2 +- routing/dht/pb/message.go | 86 ++++----- routing/dht/providers.go | 14 +- routing/dht/providers_test.go | 5 +- routing/dht/query.go | 48 ++--- routing/dht/records.go | 145 ++++++++++++--- routing/dht/routing.go | 120 +++++-------- routing/kbucket/bucket.go | 10 +- routing/kbucket/table.go | 52 +++--- routing/kbucket/table_test.go | 88 +++++---- routing/kbucket/util.go | 2 +- routing/mock/client.go | 12 +- routing/mock/interface.go | 8 +- routing/mock/mockrouting_test.go | 46 +++-- routing/mock/server.go | 22 +-- routing/routing.go | 7 +- 23 files changed, 613 insertions(+), 697 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 6e49c84cf..2cb680140 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -34,11 +34,10 @@ const doPinging = false // It is used to implement the base IpfsRouting module. type IpfsDHT struct { network inet.Network // the network services we need - self peer.Peer // Local peer (yourself) - peerstore peer.Peerstore // Other peers + self peer.ID // Local peer (yourself) + peerstore peer.Peerstore // Peer Registry - datastore ds.Datastore // Local data - dslock sync.Mutex + datastore ds.ThreadSafeDatastore // Local data routingTable *kb.RoutingTable // Array of routing tables for differently distanced nodes providers *ProviderManager @@ -53,19 +52,19 @@ type IpfsDHT struct { } // NewDHT creates a new DHT object with the given peer as the 'local' host -func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, n inet.Network, dstore ds.Datastore) *IpfsDHT { +func NewDHT(ctx context.Context, p peer.ID, n inet.Network, dstore ds.ThreadSafeDatastore) *IpfsDHT { dht := new(IpfsDHT) dht.datastore = dstore dht.self = p - dht.peerstore = ps + dht.peerstore = n.Peerstore() dht.ContextGroup = ctxgroup.WithContext(ctx) dht.network = n n.SetHandler(inet.ProtocolDHT, dht.handleNewStream) - dht.providers = NewProviderManager(dht.Context(), p.ID()) + dht.providers = NewProviderManager(dht.Context(), p) dht.AddChildGroup(dht.providers) - dht.routingTable = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID()), time.Minute) + dht.routingTable = kb.NewRoutingTable(20, kb.ConvertPeerID(p), time.Minute, dht.peerstore) dht.birth = time.Now() dht.Validators = make(map[string]ValidatorFunc) @@ -79,7 +78,7 @@ func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, n inet.Network, } // Connect to a new peer at the given address, ping and add to the routing table -func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.Peer) error { +func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.ID) error { if err := dht.network.DialPeer(ctx, npeer); err != nil { return err } @@ -95,7 +94,8 @@ func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.Peer) error { } // putValueToNetwork stores the given key/value pair at the peer 'p' -func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p peer.Peer, +// meaning: it sends a PUT_VALUE message to p +func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p peer.ID, key string, rec *pb.Record) error { pmes := pb.NewMessage(pb.Message_PUT_VALUE, string(key), 0) @@ -113,12 +113,13 @@ func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p peer.Peer, // putProvider sends a message to peer 'p' saying that the local node // can provide the value of 'key' -func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.Peer, key string) error { +func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, key string) error { pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, string(key), 0) // add self as the provider - pmes.ProviderPeers = pb.PeersToPBPeers(dht.network, []peer.Peer{dht.self}) + pi := dht.peerstore.PeerInfo(dht.self) + pmes.ProviderPeers = pb.PeerInfosToPBPeers(dht.network, []peer.PeerInfo{pi}) err := dht.sendMessage(ctx, p, pmes) if err != nil { @@ -130,8 +131,12 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.Peer, key string) er return nil } -func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, - key u.Key) ([]byte, []peer.Peer, error) { +// getValueOrPeers queries a particular peer p for the value for +// key. It returns either the value or a list of closer peers. +// NOTE: it will update the dht's peerstore with any new addresses +// it finds for the given peer. +func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.ID, + key u.Key) ([]byte, []peer.PeerInfo, error) { pmes, err := dht.getValueSingle(ctx, p, key) if err != nil { @@ -142,8 +147,8 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, // Success! We were given the value log.Debug("getValueOrPeers: got value") - // make sure record is still valid - err = dht.verifyRecord(record) + // make sure record is valid. + err = dht.verifyRecordOnline(ctx, record) if err != nil { log.Error("Received invalid record!") return nil, nil, err @@ -151,24 +156,8 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, return record.GetValue(), nil, nil } - // TODO decide on providers. This probably shouldn't be happening. - if prv := pmes.GetProviderPeers(); prv != nil && len(prv) > 0 { - val, err := dht.getFromPeerList(ctx, key, prv) - if err != nil { - return nil, nil, err - } - log.Debug("getValueOrPeers: get from providers") - return val, nil, nil - } - // Perhaps we were given closer peers - peers, errs := pb.PBPeersToPeers(dht.peerstore, pmes.GetCloserPeers()) - for _, err := range errs { - if err != nil { - log.Error(err) - } - } - + peers := pb.PBPeersToPeerInfos(pmes.GetCloserPeers()) if len(peers) > 0 { log.Debug("getValueOrPeers: peers") return nil, peers, nil @@ -179,51 +168,16 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer, } // getValueSingle simply performs the get value RPC with the given parameters -func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.Peer, +func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.ID, key u.Key) (*pb.Message, error) { pmes := pb.NewMessage(pb.Message_GET_VALUE, string(key), 0) return dht.sendRequest(ctx, p, pmes) } -// TODO: Im not certain on this implementation, we get a list of peers/providers -// from someone what do we do with it? Connect to each of them? randomly pick -// one to get the value from? Or just connect to one at a time until we get a -// successful connection and request the value from it? -func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key, - peerlist []*pb.Message_Peer) ([]byte, error) { - - for _, pinfo := range peerlist { - p, err := dht.ensureConnectedToPeer(ctx, pinfo) - if err != nil { - log.Errorf("getFromPeers error: %s", err) - continue - } - - pmes, err := dht.getValueSingle(ctx, p, key) - if err != nil { - log.Errorf("getFromPeers error: %s\n", err) - continue - } - - if record := pmes.GetRecord(); record != nil { - // Success! We were given the value - - err := dht.verifyRecord(record) - if err != nil { - return nil, err - } - dht.providers.AddProvider(key, p) - return record.GetValue(), nil - } - } - return nil, routing.ErrNotFound -} - // getLocal attempts to retrieve the value from the datastore func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { - dht.dslock.Lock() - defer dht.dslock.Unlock() + log.Debug("getLocal %s", key) v, err := dht.datastore.Get(key.DsKey()) if err != nil { @@ -243,7 +197,7 @@ func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { // TODO: 'if paranoid' if u.Debug { - err = dht.verifyRecord(rec) + err = dht.verifyRecordLocally(rec) if err != nil { log.Errorf("local record verify failed: %s", err) return nil, err @@ -269,41 +223,40 @@ func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { // Update signals the routingTable to Update its last-seen status // on the given peer. -func (dht *IpfsDHT) Update(ctx context.Context, p peer.Peer) { +func (dht *IpfsDHT) Update(ctx context.Context, p peer.ID) { log.Event(ctx, "updatePeer", p) dht.routingTable.Update(p) } // FindLocal looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in. -func (dht *IpfsDHT) FindLocal(id peer.ID) (peer.Peer, *kb.RoutingTable) { +func (dht *IpfsDHT) FindLocal(id peer.ID) (peer.PeerInfo, *kb.RoutingTable) { p := dht.routingTable.Find(id) - if p != nil { - return p, dht.routingTable + if p != "" { + return dht.peerstore.PeerInfo(p), dht.routingTable } - return nil, nil + return peer.PeerInfo{}, nil } // findPeerSingle asks peer 'p' if they know where the peer with id 'id' is -func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.Peer, id peer.ID) (*pb.Message, error) { +func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.ID, id peer.ID) (*pb.Message, error) { pmes := pb.NewMessage(pb.Message_FIND_NODE, string(id), 0) return dht.sendRequest(ctx, p, pmes) } -func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.Peer, key u.Key) (*pb.Message, error) { +func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.ID, key u.Key) (*pb.Message, error) { pmes := pb.NewMessage(pb.Message_GET_PROVIDERS, string(key), 0) return dht.sendRequest(ctx, p, pmes) } -func (dht *IpfsDHT) addProviders(key u.Key, pbps []*pb.Message_Peer) []peer.Peer { - peers, errs := pb.PBPeersToPeers(dht.peerstore, pbps) - for _, err := range errs { - log.Errorf("error converting peer: %v", err) - } +func (dht *IpfsDHT) addProviders(key u.Key, pbps []*pb.Message_Peer) []peer.ID { + peers := pb.PBPeersToPeerInfos(pbps) + + var provArr []peer.ID + for _, pi := range peers { + p := pi.ID - var provArr []peer.Peer - for _, p := range peers { // Dont add outselves to the list - if p.ID().Equal(dht.self.ID()) { + if p == dht.self { continue } @@ -316,14 +269,14 @@ func (dht *IpfsDHT) addProviders(key u.Key, pbps []*pb.Message_Peer) []peer.Peer } // nearestPeersToQuery returns the routing tables closest peers. -func (dht *IpfsDHT) nearestPeersToQuery(pmes *pb.Message, count int) []peer.Peer { +func (dht *IpfsDHT) nearestPeersToQuery(pmes *pb.Message, count int) []peer.ID { key := u.Key(pmes.GetKey()) closer := dht.routingTable.NearestPeers(kb.ConvertKey(key), count) return closer } // betterPeerToQuery returns nearestPeersToQuery, but iff closer than self. -func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, count int) []peer.Peer { +func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, count int) []peer.ID { closer := dht.nearestPeersToQuery(pmes, count) // no node? nil @@ -333,17 +286,17 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, count int) []peer.Peer // == to self? thats bad for _, p := range closer { - if p.ID().Equal(dht.self.ID()) { + if p == dht.self { log.Error("Attempted to return self! this shouldnt happen...") return nil } } - var filtered []peer.Peer + var filtered []peer.ID for _, p := range closer { // must all be closer than self key := u.Key(pmes.GetKey()) - if !kb.Closer(dht.self.ID(), p.ID(), key) { + if !kb.Closer(dht.self, p, key) { filtered = append(filtered, p) } } @@ -352,30 +305,13 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, count int) []peer.Peer return filtered } -// getPeer searches the peerstore for a peer with the given peer ID -func (dht *IpfsDHT) getPeer(id peer.ID) (peer.Peer, error) { - p, err := dht.peerstore.FindOrCreate(id) - if err != nil { - err = fmt.Errorf("Failed to get peer from peerstore: %s", err) - log.Error(err) - return nil, err - } - return p, nil -} - -func (dht *IpfsDHT) ensureConnectedToPeer(ctx context.Context, pbp *pb.Message_Peer) (peer.Peer, error) { - p, err := pb.PBPeerToPeer(dht.peerstore, pbp) - if err != nil { - return nil, err - } - - if dht.self.ID().Equal(p.ID()) { - return nil, errors.New("attempting to ensure connection to self") +func (dht *IpfsDHT) ensureConnectedToPeer(ctx context.Context, p peer.ID) error { + if p == dht.self { + return errors.New("attempting to ensure connection to self") } // dial connection - err = dht.network.DialPeer(ctx, p) - return p, err + return dht.network.DialPeer(ctx, p) } //TODO: this should be smarter about which keys it selects. @@ -421,14 +357,24 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { // Bootstrap builds up list of peers by requesting random peer IDs func (dht *IpfsDHT) Bootstrap(ctx context.Context) { - id := make([]byte, 16) - rand.Read(id) - p, err := dht.FindPeer(ctx, peer.ID(id)) - if err != nil { - log.Errorf("Bootstrap peer error: %s", err) - } - err = dht.network.DialPeer(ctx, p) - if err != nil { - log.Errorf("Bootstrap peer error: %s", err) + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + id := make([]byte, 16) + rand.Read(id) + pi, err := dht.FindPeer(ctx, peer.ID(id)) + if err != nil { + // NOTE: this is not an error. this is expected! + log.Errorf("Bootstrap peer error: %s", err) + } + + // woah, we got a peer under a random id? it _cannot_ be valid. + log.Errorf("dht seemingly found a peer at a random bootstrap id (%s)...", pi) + }() } + wg.Wait() } diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 6e46b4de6..a91e0f53c 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -67,7 +67,7 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { // sendRequest sends out a request, but also makes sure to // measure the RTT for latency measurements. -func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { +func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { log.Debugf("%s dht starting stream", dht.self) s, err := dht.network.NewStream(inet.ProtocolDHT, p) @@ -98,13 +98,13 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *pb.Messa return nil, errors.New("no response to request") } - p.SetLatency(time.Since(start)) + dht.peerstore.RecordLatency(p, time.Since(start)) log.Event(ctx, "dhtReceivedMessage", dht.self, p, rpmes) return rpmes, nil } // sendMessage sends out a message -func (dht *IpfsDHT) sendMessage(ctx context.Context, p peer.Peer, pmes *pb.Message) error { +func (dht *IpfsDHT) sendMessage(ctx context.Context, p peer.ID, pmes *pb.Message) error { log.Debugf("%s dht starting stream", dht.self) s, err := dht.network.NewStream(inet.ProtocolDHT, p) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 50ec76792..b378675c6 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -2,44 +2,47 @@ package dht import ( "bytes" - "math/rand" "sort" "testing" + "time" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - ci "github.com/jbenet/go-ipfs/crypto" + // ci "github.com/jbenet/go-ipfs/crypto" inet "github.com/jbenet/go-ipfs/net" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" testutil "github.com/jbenet/go-ipfs/util/testutil" - - "fmt" - "time" ) -func randMultiaddr(t *testing.T) ma.Multiaddr { +func setupDHT(ctx context.Context, t *testing.T, addr ma.Multiaddr) *IpfsDHT { - s := fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 10000+rand.Intn(40000)) - a, err := ma.NewMultiaddr(s) + sk, pk, err := testutil.RandKeyPair(512) + if err != nil { + t.Fatal(err) + } + + p, err := peer.IDFromPublicKey(pk) if err != nil { t.Fatal(err) } - return a -} -func setupDHT(ctx context.Context, t *testing.T, p peer.Peer) *IpfsDHT { peerstore := peer.NewPeerstore() + peerstore.AddPrivKey(p, sk) + peerstore.AddPubKey(p, pk) + peerstore.AddAddress(p, addr) - n, err := inet.NewNetwork(ctx, p.Addresses(), p, peerstore) + n, err := inet.NewNetwork(ctx, []ma.Multiaddr{addr}, p, peerstore) if err != nil { t.Fatal(err) } - d := NewDHT(ctx, p, peerstore, n, ds.NewMapDatastore()) + dss := dssync.MutexWrap(ds.NewMapDatastore()) + d := NewDHT(ctx, p, n, dss) d.Validators["v"] = func(u.Key, []byte) error { return nil @@ -47,77 +50,53 @@ func setupDHT(ctx context.Context, t *testing.T, p peer.Peer) *IpfsDHT { return d } -func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []peer.Peer, []*IpfsDHT) { - var addrs []ma.Multiaddr - for i := 0; i < n; i++ { - r := rand.Intn(40000) - a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 10000+r)) - if err != nil { - t.Fatal(err) - } - addrs = append(addrs, a) - } - - var peers []peer.Peer - for i := 0; i < n; i++ { - p := makePeer(addrs[i]) - peers = append(peers, p) - } - +func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []peer.ID, []*IpfsDHT) { + addrs := make([]ma.Multiaddr, n) dhts := make([]*IpfsDHT, n) + peers := make([]peer.ID, n) + for i := 0; i < n; i++ { - dhts[i] = setupDHT(ctx, t, peers[i]) + addrs[i] = testutil.RandLocalTCPAddress() + dhts[i] = setupDHT(ctx, t, addrs[i]) + peers[i] = dhts[i].self } return addrs, peers, dhts } -func makePeerString(t *testing.T, addr string) peer.Peer { - maddr, err := ma.NewMultiaddr(addr) - if err != nil { - t.Fatal(err) - } - return makePeer(maddr) -} +func connect(t *testing.T, ctx context.Context, a, b *IpfsDHT) { -func makePeer(addr ma.Multiaddr) peer.Peer { - sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) - if err != nil { - panic(err) + idB := b.self + addrB := b.peerstore.Addresses(idB) + if len(addrB) == 0 { + t.Fatal("peers setup incorrectly: no local address") } - p, err := testutil.NewPeerWithKeyPair(sk, pk) - if err != nil { - panic(err) + + a.peerstore.AddAddresses(idB, addrB) + if err := a.Connect(ctx, idB); err != nil { + t.Fatal(err) } - p.AddAddress(addr) - return p } func TestPing(t *testing.T) { // t.Skip("skipping test to debug another") ctx := context.Background() - addrA := randMultiaddr(t) - addrB := randMultiaddr(t) + addrA := testutil.RandLocalTCPAddress() + addrB := testutil.RandLocalTCPAddress() - peerA := makePeer(addrA) - peerB := makePeer(addrB) + dhtA := setupDHT(ctx, t, addrA) + dhtB := setupDHT(ctx, t, addrB) - dhtA := setupDHT(ctx, t, peerA) - dhtB := setupDHT(ctx, t, peerB) + peerA := dhtA.self + peerB := dhtB.self defer dhtA.Close() defer dhtB.Close() defer dhtA.network.Close() defer dhtB.network.Close() - if err := dhtA.Connect(ctx, peerB); err != nil { - t.Fatal(err) - } - - // if err := dhtB.Connect(ctx, peerA); err != nil { - // t.Fatal(err) - // } + connect(t, ctx, dhtA, dhtB) //Test that we can ping the node ctxT, _ := context.WithTimeout(ctx, 100*time.Millisecond) @@ -136,14 +115,16 @@ func TestValueGetSet(t *testing.T) { ctx := context.Background() - addrA := randMultiaddr(t) - addrB := randMultiaddr(t) + addrA := testutil.RandLocalTCPAddress() + addrB := testutil.RandLocalTCPAddress() - peerA := makePeer(addrA) - peerB := makePeer(addrB) + dhtA := setupDHT(ctx, t, addrA) + dhtB := setupDHT(ctx, t, addrB) - dhtA := setupDHT(ctx, t, peerA) - dhtB := setupDHT(ctx, t, peerB) + defer dhtA.Close() + defer dhtB.Close() + defer dhtA.network.Close() + defer dhtB.network.Close() vf := func(u.Key, []byte) error { return nil @@ -151,15 +132,7 @@ func TestValueGetSet(t *testing.T) { dhtA.Validators["v"] = vf dhtB.Validators["v"] = vf - defer dhtA.Close() - defer dhtB.Close() - defer dhtA.network.Close() - defer dhtB.network.Close() - - err := dhtA.Connect(ctx, peerB) - if err != nil { - t.Fatal(err) - } + connect(t, ctx, dhtA, dhtB) ctxT, _ := context.WithTimeout(ctx, time.Second) dhtA.PutValue(ctxT, "/v/hello", []byte("world")) @@ -189,7 +162,7 @@ func TestProvides(t *testing.T) { // t.Skip("skipping test to debug another") ctx := context.Background() - _, peers, dhts := setupDHTS(ctx, 4, t) + _, _, dhts := setupDHTS(ctx, 4, t) defer func() { for i := 0; i < 4; i++ { dhts[i].Close() @@ -197,22 +170,11 @@ func TestProvides(t *testing.T) { } }() - err := dhts[0].Connect(ctx, peers[1]) - if err != nil { - t.Fatal(err) - } + connect(t, ctx, dhts[0], dhts[1]) + connect(t, ctx, dhts[1], dhts[2]) + connect(t, ctx, dhts[1], dhts[3]) - err = dhts[1].Connect(ctx, peers[2]) - if err != nil { - t.Fatal(err) - } - - err = dhts[1].Connect(ctx, peers[3]) - if err != nil { - t.Fatal(err) - } - - err = dhts[3].putLocal(u.Key("hello"), []byte("world")) + err := dhts[3].putLocal(u.Key("hello"), []byte("world")) if err != nil { t.Fatal(err) } @@ -227,18 +189,21 @@ func TestProvides(t *testing.T) { t.Fatal(err) } - time.Sleep(time.Millisecond * 60) + // what is this timeout for? was 60ms before. + time.Sleep(time.Millisecond * 6) ctxT, _ := context.WithTimeout(ctx, time.Second) provchan := dhts[0].FindProvidersAsync(ctxT, u.Key("hello"), 1) - after := time.After(time.Second) select { case prov := <-provchan: - if prov == nil { + if prov.ID == "" { t.Fatal("Got back nil provider") } - case <-after: + if prov.ID != dhts[3].self { + t.Fatal("Got back nil provider") + } + case <-ctxT.Done(): t.Fatal("Did not get a provider back.") } } @@ -250,7 +215,7 @@ func TestProvidesAsync(t *testing.T) { ctx := context.Background() - _, peers, dhts := setupDHTS(ctx, 4, t) + _, _, dhts := setupDHTS(ctx, 4, t) defer func() { for i := 0; i < 4; i++ { dhts[i].Close() @@ -258,22 +223,11 @@ func TestProvidesAsync(t *testing.T) { } }() - err := dhts[0].Connect(ctx, peers[1]) - if err != nil { - t.Fatal(err) - } - - err = dhts[1].Connect(ctx, peers[2]) - if err != nil { - t.Fatal(err) - } - - err = dhts[1].Connect(ctx, peers[3]) - if err != nil { - t.Fatal(err) - } + connect(t, ctx, dhts[0], dhts[1]) + connect(t, ctx, dhts[1], dhts[2]) + connect(t, ctx, dhts[1], dhts[3]) - err = dhts[3].putLocal(u.Key("hello"), []byte("world")) + err := dhts[3].putLocal(u.Key("hello"), []byte("world")) if err != nil { t.Fatal(err) } @@ -297,10 +251,10 @@ func TestProvidesAsync(t *testing.T) { if !ok { t.Fatal("Provider channel was closed...") } - if p == nil { + if p.ID == "" { t.Fatal("Got back nil provider!") } - if !p.ID().Equal(dhts[3].self.ID()) { + if p.ID != dhts[3].self { t.Fatalf("got a provider, but not the right one. %s", p) } case <-ctxT.Done(): @@ -315,7 +269,7 @@ func TestLayeredGet(t *testing.T) { ctx := context.Background() - _, peers, dhts := setupDHTS(ctx, 4, t) + _, _, dhts := setupDHTS(ctx, 4, t) defer func() { for i := 0; i < 4; i++ { dhts[i].Close() @@ -323,22 +277,11 @@ func TestLayeredGet(t *testing.T) { } }() - err := dhts[0].Connect(ctx, peers[1]) - if err != nil { - t.Fatalf("Failed to connect: %s", err) - } + connect(t, ctx, dhts[0], dhts[1]) + connect(t, ctx, dhts[1], dhts[2]) + connect(t, ctx, dhts[1], dhts[3]) - err = dhts[1].Connect(ctx, peers[2]) - if err != nil { - t.Fatal(err) - } - - err = dhts[1].Connect(ctx, peers[3]) - if err != nil { - t.Fatal(err) - } - - err = dhts[3].putLocal(u.Key("/v/hello"), []byte("world")) + err := dhts[3].putLocal(u.Key("/v/hello"), []byte("world")) if err != nil { t.Fatal(err) } @@ -377,32 +320,21 @@ func TestFindPeer(t *testing.T) { } }() - err := dhts[0].Connect(ctx, peers[1]) - if err != nil { - t.Fatal(err) - } - - err = dhts[1].Connect(ctx, peers[2]) - if err != nil { - t.Fatal(err) - } - - err = dhts[1].Connect(ctx, peers[3]) - if err != nil { - t.Fatal(err) - } + connect(t, ctx, dhts[0], dhts[1]) + connect(t, ctx, dhts[1], dhts[2]) + connect(t, ctx, dhts[1], dhts[3]) ctxT, _ := context.WithTimeout(ctx, time.Second) - p, err := dhts[0].FindPeer(ctxT, peers[2].ID()) + p, err := dhts[0].FindPeer(ctxT, peers[2]) if err != nil { t.Fatal(err) } - if p == nil { + if p.ID == "" { t.Fatal("Failed to find peer.") } - if !p.ID().Equal(peers[2].ID()) { + if p.ID != peers[2] { t.Fatal("Didnt find expected peer.") } } @@ -426,25 +358,10 @@ func TestFindPeersConnectedToPeer(t *testing.T) { // topology: // 0-1, 1-2, 1-3, 2-3 - err := dhts[0].Connect(ctx, peers[1]) - if err != nil { - t.Fatal(err) - } - - err = dhts[1].Connect(ctx, peers[2]) - if err != nil { - t.Fatal(err) - } - - err = dhts[1].Connect(ctx, peers[3]) - if err != nil { - t.Fatal(err) - } - - err = dhts[2].Connect(ctx, peers[3]) - if err != nil { - t.Fatal(err) - } + connect(t, ctx, dhts[0], dhts[1]) + connect(t, ctx, dhts[1], dhts[2]) + connect(t, ctx, dhts[1], dhts[3]) + connect(t, ctx, dhts[2], dhts[3]) // fmt.Println("0 is", peers[0]) // fmt.Println("1 is", peers[1]) @@ -452,13 +369,13 @@ func TestFindPeersConnectedToPeer(t *testing.T) { // fmt.Println("3 is", peers[3]) ctxT, _ := context.WithTimeout(ctx, time.Second) - pchan, err := dhts[0].FindPeersConnectedToPeer(ctxT, peers[2].ID()) + pchan, err := dhts[0].FindPeersConnectedToPeer(ctxT, peers[2]) if err != nil { t.Fatal(err) } - // shouldFind := []peer.Peer{peers[1], peers[3]} - found := []peer.Peer{} + // shouldFind := []peer.ID{peers[1], peers[3]} + found := []peer.PeerInfo{} for nextp := range pchan { found = append(found, nextp) } @@ -475,7 +392,7 @@ func TestFindPeersConnectedToPeer(t *testing.T) { } } -func testPeerListsMatch(t *testing.T, p1, p2 []peer.Peer) { +func testPeerListsMatch(t *testing.T, p1, p2 []peer.ID) { if len(p1) != len(p2) { t.Fatal("did not find as many peers as should have", p1, p2) @@ -485,11 +402,11 @@ func testPeerListsMatch(t *testing.T, p1, p2 []peer.Peer) { ids2 := make([]string, len(p2)) for i, p := range p1 { - ids1[i] = p.ID().Pretty() + ids1[i] = string(p) } for i, p := range p2 { - ids2[i] = p.ID().Pretty() + ids2[i] = string(p) } sort.Sort(sort.StringSlice(ids1)) @@ -514,39 +431,41 @@ func TestConnectCollision(t *testing.T) { ctx := context.Background() - addrA := randMultiaddr(t) - addrB := randMultiaddr(t) + addrA := testutil.RandLocalTCPAddress() + addrB := testutil.RandLocalTCPAddress() - peerA := makePeer(addrA) - peerB := makePeer(addrB) + dhtA := setupDHT(ctx, t, addrA) + dhtB := setupDHT(ctx, t, addrB) - dhtA := setupDHT(ctx, t, peerA) - dhtB := setupDHT(ctx, t, peerB) + peerA := dhtA.self + peerB := dhtB.self - done := make(chan struct{}) + errs := make(chan error) go func() { + dhtA.peerstore.AddAddress(peerB, addrB) err := dhtA.Connect(ctx, peerB) - if err != nil { - t.Fatal(err) - } - done <- struct{}{} + errs <- err }() go func() { + dhtB.peerstore.AddAddress(peerA, addrA) err := dhtB.Connect(ctx, peerA) - if err != nil { - t.Fatal(err) - } - done <- struct{}{} + errs <- err }() timeout := time.After(time.Second) select { - case <-done: + case e := <-errs: + if e != nil { + t.Fatal(e) + } case <-timeout: t.Fatal("Timeout received!") } select { - case <-done: + case e := <-errs: + if e != nil { + t.Fatal(e) + } case <-timeout: t.Fatal("Timeout received!") } @@ -555,7 +474,5 @@ func TestConnectCollision(t *testing.T) { dhtB.Close() dhtA.network.Close() dhtB.network.Close() - - <-time.After(200 * time.Millisecond) } } diff --git a/routing/dht/diag.go b/routing/dht/diag.go index 82316e2e3..96d2b1a01 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -32,12 +32,12 @@ func (di *diagInfo) Marshal() []byte { func (dht *IpfsDHT) getDiagInfo() *diagInfo { di := new(diagInfo) di.CodeVersion = "github.com/jbenet/go-ipfs" - di.ID = dht.self.ID() + di.ID = dht.self di.LifeSpan = time.Since(dht.birth) di.Keys = nil // Currently no way to query datastore for _, p := range dht.routingTable.ListPeers() { - d := connDiagInfo{p.GetLatency(), p.ID()} + d := connDiagInfo{dht.peerstore.LatencyEWMA(p), p} di.Connections = append(di.Connections, d) } return di diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index c7315d538..04f5111a9 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -4,19 +4,17 @@ import ( "math/rand" "testing" - crand "crypto/rand" - inet "github.com/jbenet/go-ipfs/net" mocknet "github.com/jbenet/go-ipfs/net/mock" peer "github.com/jbenet/go-ipfs/peer" routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" - testutil "github.com/jbenet/go-ipfs/util/testutil" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" "time" ) @@ -34,8 +32,8 @@ func TestGetFailures(t *testing.T) { nets := mn.Nets() peers := mn.Peers() - ps := peer.NewPeerstore() - d := NewDHT(ctx, peers[0], ps, nets[0], ds.NewMapDatastore()) + tsds := dssync.MutexWrap(ds.NewMapDatastore()) + d := NewDHT(ctx, peers[0], nets[0], tsds) d.Update(ctx, peers[1]) // This one should time out @@ -126,14 +124,6 @@ func TestGetFailures(t *testing.T) { } } -// TODO: Maybe put these in some sort of "ipfs_testutil" package -func _randPeer() peer.Peer { - id := make(peer.ID, 16) - crand.Read(id) - p := testutil.NewPeerWithID(id) - return p -} - func TestNotFound(t *testing.T) { if testing.Short() { t.SkipNow() @@ -146,9 +136,8 @@ func TestNotFound(t *testing.T) { } nets := mn.Nets() peers := mn.Peers() - peerstore := peer.NewPeerstore() - - d := NewDHT(ctx, peers[0], peerstore, nets[0], ds.NewMapDatastore()) + tsds := dssync.MutexWrap(ds.NewMapDatastore()) + d := NewDHT(ctx, peers[0], nets[0], tsds) for _, p := range peers { d.Update(ctx, p) @@ -156,6 +145,7 @@ func TestNotFound(t *testing.T) { // Reply with random peers to every message for _, neti := range nets { + neti := neti // shadow loop var neti.SetHandler(inet.ProtocolDHT, func(s inet.Stream) { defer s.Close() @@ -171,12 +161,14 @@ func TestNotFound(t *testing.T) { case pb.Message_GET_VALUE: resp := &pb.Message{Type: pmes.Type} - ps := []peer.Peer{} + ps := []peer.PeerInfo{} for i := 0; i < 7; i++ { - ps = append(ps, peers[rand.Intn(len(peers))]) + p := peers[rand.Intn(len(peers))] + pi := neti.Peerstore().PeerInfo(p) + ps = append(ps, pi) } - resp.CloserPeers = pb.PeersToPBPeers(d.network, peers) + resp.CloserPeers = pb.PeerInfosToPBPeers(d.network, ps) if err := pbw.WriteMsg(resp); err != nil { panic(err) } @@ -216,9 +208,9 @@ func TestLessThanKResponses(t *testing.T) { } nets := mn.Nets() peers := mn.Peers() - peerstore := peer.NewPeerstore() - d := NewDHT(ctx, peers[0], peerstore, nets[0], ds.NewMapDatastore()) + tsds := dssync.MutexWrap(ds.NewMapDatastore()) + d := NewDHT(ctx, peers[0], nets[0], tsds) for i := 1; i < 5; i++ { d.Update(ctx, peers[i]) @@ -226,6 +218,7 @@ func TestLessThanKResponses(t *testing.T) { // Reply with random peers to every message for _, neti := range nets { + neti := neti // shadow loop var neti.SetHandler(inet.ProtocolDHT, func(s inet.Stream) { defer s.Close() @@ -239,9 +232,10 @@ func TestLessThanKResponses(t *testing.T) { switch pmes.GetType() { case pb.Message_GET_VALUE: + pi := neti.Peerstore().PeerInfo(peers[1]) resp := &pb.Message{ Type: pmes.Type, - CloserPeers: pb.PeersToPBPeers(d.network, []peer.Peer{peers[1]}), + CloserPeers: pb.PeerInfosToPBPeers(d.network, []peer.PeerInfo{pi}), } if err := pbw.WriteMsg(resp); err != nil { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 4319ef019..e9ffd7d7f 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -17,7 +17,7 @@ import ( var CloserPeerCount = 4 // dhthandler specifies the signature of functions that handle DHT messages. -type dhtHandler func(context.Context, peer.Peer, *pb.Message) (*pb.Message, error) +type dhtHandler func(context.Context, peer.ID, *pb.Message) (*pb.Message, error) func (dht *IpfsDHT) handlerForMsgType(t pb.Message_MessageType) dhtHandler { switch t { @@ -38,16 +38,17 @@ func (dht *IpfsDHT) handlerForMsgType(t pb.Message_MessageType) dhtHandler { } } -func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { +func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { log.Debugf("%s handleGetValue for key: %s\n", dht.self, pmes.GetKey()) // setup response resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) - // first, is the key even a key? + // first, is there even a key? key := pmes.GetKey() if key == "" { return nil, errors.New("handleGetValue but no key was provided") + // TODO: send back an error response? could be bad, but the other node's hanging. } // let's first check if we have the value locally. @@ -85,36 +86,38 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.Peer, pmes *pb.Me // if we know any providers for the requested value, return those. provs := dht.providers.GetProviders(ctx, u.Key(pmes.GetKey())) + provinfos := peer.PeerInfos(dht.peerstore, provs) if len(provs) > 0 { log.Debugf("handleGetValue returning %d provider[s]", len(provs)) - resp.ProviderPeers = pb.PeersToPBPeers(dht.network, provs) + resp.ProviderPeers = pb.PeerInfosToPBPeers(dht.network, provinfos) } // Find closest peer on given cluster to desired key and reply with that info closer := dht.betterPeersToQuery(pmes, CloserPeerCount) + closerinfos := peer.PeerInfos(dht.peerstore, closer) if closer != nil { - for _, p := range closer { - log.Debugf("handleGetValue returning closer peer: '%s'", p) - if len(p.Addresses()) < 1 { - log.Critical("no addresses on peer being sent!") + for _, pi := range closerinfos { + log.Debugf("handleGetValue returning closer peer: '%s'", pi.ID) + if len(pi.Addrs) < 1 { + log.Criticalf(`no addresses on peer being sent! + [local:%s] + [sending:%s] + [remote:%s]`, dht.self, pi.ID, p) } } - resp.CloserPeers = pb.PeersToPBPeers(dht.network, closer) + + resp.CloserPeers = pb.PeerInfosToPBPeers(dht.network, closerinfos) } return resp, nil } // Store a value in this peer local storage -func (dht *IpfsDHT) handlePutValue(ctx context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { - dht.dslock.Lock() - defer dht.dslock.Unlock() +func (dht *IpfsDHT) handlePutValue(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { dskey := u.Key(pmes.GetKey()).DsKey() - err := dht.verifyRecord(pmes.GetRecord()) - if err != nil { - fmt.Println(u.Key(pmes.GetRecord().GetAuthor())) - log.Error("Bad dht record in put request") + if err := dht.verifyRecordLocally(pmes.GetRecord()); err != nil { + log.Errorf("Bad dht record in PUT from: %s. %s", u.Key(pmes.GetRecord().GetAuthor()), err) return nil, err } @@ -128,18 +131,18 @@ func (dht *IpfsDHT) handlePutValue(ctx context.Context, p peer.Peer, pmes *pb.Me return pmes, err } -func (dht *IpfsDHT) handlePing(_ context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { +func (dht *IpfsDHT) handlePing(_ context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { log.Debugf("%s Responding to ping from %s!\n", dht.self, p) return pmes, nil } -func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { +func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { resp := pb.NewMessage(pmes.GetType(), "", pmes.GetClusterLevel()) - var closest []peer.Peer + var closest []peer.ID // if looking for self... special case where we send it on CloserPeers. - if peer.ID(pmes.GetKey()).Equal(dht.self.ID()) { - closest = []peer.Peer{dht.self} + if peer.ID(pmes.GetKey()) == dht.self { + closest = []peer.ID{dht.self} } else { closest = dht.betterPeersToQuery(pmes, CloserPeerCount) } @@ -149,22 +152,20 @@ func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.Peer, pmes *pb.Me return resp, nil } - var withAddresses []peer.Peer - for _, p := range closest { - if len(p.Addresses()) > 0 { - withAddresses = append(withAddresses, p) + var withAddresses []peer.PeerInfo + closestinfos := peer.PeerInfos(dht.peerstore, closest) + for _, pi := range closestinfos { + if len(pi.Addrs) > 0 { + withAddresses = append(withAddresses, pi) + log.Debugf("handleFindPeer: sending back '%s'", pi.ID) } } - for _, p := range withAddresses { - log.Debugf("handleFindPeer: sending back '%s'", p) - } - - resp.CloserPeers = pb.PeersToPBPeers(dht.network, withAddresses) + resp.CloserPeers = pb.PeerInfosToPBPeers(dht.network, withAddresses) return resp, nil } -func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { +func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) // check if we have this value, to add ourselves as provider. @@ -183,13 +184,15 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.Peer, pmes *p } if providers != nil && len(providers) > 0 { - resp.ProviderPeers = pb.PeersToPBPeers(dht.network, providers) + infos := peer.PeerInfos(dht.peerstore, providers) + resp.ProviderPeers = pb.PeerInfosToPBPeers(dht.network, infos) } // Also send closer peers. closer := dht.betterPeersToQuery(pmes, CloserPeerCount) if closer != nil { - resp.CloserPeers = pb.PeersToPBPeers(dht.network, closer) + infos := peer.PeerInfos(dht.peerstore, providers) + resp.CloserPeers = pb.PeerInfosToPBPeers(dht.network, infos) } return resp, nil @@ -197,34 +200,35 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.Peer, pmes *p type providerInfo struct { Creation time.Time - Value peer.Peer + Value peer.ID } -func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) { +func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { key := u.Key(pmes.GetKey()) log.Debugf("%s adding %s as a provider for '%s'\n", dht.self, p, peer.ID(key)) // add provider should use the address given in the message - for _, pb := range pmes.GetProviderPeers() { - pid := peer.ID(pb.GetId()) - if pid.Equal(p.ID()) { - - maddrs, err := pb.Addresses() - if err != nil { - log.Errorf("provider %s error with addresses %s", p, pb.Addrs) - continue - } + pinfos := pb.PBPeersToPeerInfos(pmes.GetProviderPeers()) + for _, pi := range pinfos { + if pi.ID != p { + // we should ignore this provider reccord! not from originator. + // (we chould sign them and check signature later...) + log.Errorf("handleAddProvider received provider %s from %s. Ignore.", pi.ID, p) + continue + } - log.Infof("received provider %s %s for %s", p, maddrs, key) - for _, maddr := range maddrs { - p.AddAddress(maddr) - } - dht.providers.AddProvider(key, p) + if len(pi.Addrs) < 1 { + log.Errorf("got no valid addresses for provider %s. Ignore.", p) + continue + } - } else { - log.Errorf("handleAddProvider received provider %s from %s", pid, p) + log.Infof("received provider %s for %s (addrs: %s)", p, key, pi.Addrs) + for _, maddr := range pi.Addrs { + // add the received addresses to our peerstore. + dht.peerstore.AddAddress(p, maddr) } + dht.providers.AddProvider(key, p) } return pmes, nil // send back same msg as confirmation. diff --git a/routing/dht/pb/dht.pb.go b/routing/dht/pb/dht.pb.go index e102ef7d3..09db3d5f9 100644 --- a/routing/dht/pb/dht.pb.go +++ b/routing/dht/pb/dht.pb.go @@ -182,7 +182,7 @@ type Message_Peer struct { // ID of a given peer. Id *string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` // multiaddrs for a given peer - Addrs []string `protobuf:"bytes,2,rep,name=addrs" json:"addrs,omitempty"` + Addrs [][]byte `protobuf:"bytes,2,rep,name=addrs" json:"addrs,omitempty"` // used to signal the sender's connection capabilities to the peer Connection *Message_ConnectionType `protobuf:"varint,3,opt,name=connection,enum=dht.pb.Message_ConnectionType" json:"connection,omitempty"` XXX_unrecognized []byte `json:"-"` @@ -199,7 +199,7 @@ func (m *Message_Peer) GetId() string { return "" } -func (m *Message_Peer) GetAddrs() []string { +func (m *Message_Peer) GetAddrs() [][]byte { if m != nil { return m.Addrs } diff --git a/routing/dht/pb/dht.proto b/routing/dht/pb/dht.proto index 6f31dd5e3..91c8d8e04 100644 --- a/routing/dht/pb/dht.proto +++ b/routing/dht/pb/dht.proto @@ -32,7 +32,7 @@ message Message { optional string id = 1; // multiaddrs for a given peer - repeated string addrs = 2; + repeated bytes addrs = 2; // used to signal the sender's connection capabilities to the peer optional ConnectionType connection = 3; diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index c5c4afea7..570c7cf18 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -1,15 +1,15 @@ package dht_pb import ( - "errors" - "fmt" - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" inet "github.com/jbenet/go-ipfs/net" peer "github.com/jbenet/go-ipfs/peer" + eventlog "github.com/jbenet/go-ipfs/util/eventlog" ) +var log = eventlog.Logger("dht.pb") + // NewMessage constructs a new dht message with given type, key, and level func NewMessage(typ Message_MessageType, key string, level int) *Message { m := &Message{ @@ -20,43 +20,32 @@ func NewMessage(typ Message_MessageType, key string, level int) *Message { return m } -func peerToPBPeer(p peer.Peer) *Message_Peer { +func peerInfoToPBPeer(p peer.PeerInfo) *Message_Peer { pbp := new(Message_Peer) - maddrs := p.Addresses() - pbp.Addrs = make([]string, len(maddrs)) - for i, maddr := range maddrs { - pbp.Addrs[i] = maddr.String() + pbp.Addrs = make([][]byte, len(p.Addrs)) + for i, maddr := range p.Addrs { + pbp.Addrs[i] = maddr.Bytes() // Bytes, not String. Compressed. } - pid := string(p.ID()) - pbp.Id = &pid + s := string(p.ID) + pbp.Id = &s return pbp } -// PBPeerToPeer turns a *Message_Peer into its peer.Peer counterpart -func PBPeerToPeer(ps peer.Peerstore, pbp *Message_Peer) (peer.Peer, error) { - p, err := ps.FindOrCreate(peer.ID(pbp.GetId())) - if err != nil { - return nil, fmt.Errorf("Failed to get peer from peerstore: %s", err) - } - - // add addresses - maddrs, err := pbp.Addresses() - if err != nil { - return nil, fmt.Errorf("Received peer with bad or missing addresses: %s", pbp.Addrs) +// PBPeerToPeer turns a *Message_Peer into its peer.PeerInfo counterpart +func PBPeerToPeerInfo(pbp *Message_Peer) peer.PeerInfo { + return peer.PeerInfo{ + ID: peer.ID(pbp.GetId()), + Addrs: pbp.Addresses(), } - for _, maddr := range maddrs { - p.AddAddress(maddr) - } - return p, nil } -// RawPeersToPBPeers converts a slice of Peers into a slice of *Message_Peers, +// RawPeerInfosToPBPeers converts a slice of Peers into a slice of *Message_Peers, // ready to go out on the wire. -func RawPeersToPBPeers(peers []peer.Peer) []*Message_Peer { +func RawPeerInfosToPBPeers(peers []peer.PeerInfo) []*Message_Peer { pbpeers := make([]*Message_Peer, len(peers)) for i, p := range peers { - pbpeers[i] = peerToPBPeer(p) + pbpeers[i] = peerInfoToPBPeer(p) } return pbpeers } @@ -64,49 +53,42 @@ func RawPeersToPBPeers(peers []peer.Peer) []*Message_Peer { // PeersToPBPeers converts given []peer.Peer into a set of []*Message_Peer, // which can be written to a message and sent out. the key thing this function // does (in addition to PeersToPBPeers) is set the ConnectionType with -// information from the given inet.Dialer. -func PeersToPBPeers(d inet.Network, peers []peer.Peer) []*Message_Peer { - pbps := RawPeersToPBPeers(peers) +// information from the given inet.Network. +func PeerInfosToPBPeers(n inet.Network, peers []peer.PeerInfo) []*Message_Peer { + pbps := RawPeerInfosToPBPeers(peers) for i, pbp := range pbps { - c := ConnectionType(d.Connectedness(peers[i])) + c := ConnectionType(n.Connectedness(peers[i].ID)) pbp.Connection = &c } return pbps } -// PBPeersToPeers converts given []*Message_Peer into a set of []peer.Peer -// Returns two slices, one of peers, and one of errors. The slice of peers -// will ONLY contain successfully converted peers. The slice of errors contains -// whether each input Message_Peer was successfully converted. -func PBPeersToPeers(ps peer.Peerstore, pbps []*Message_Peer) ([]peer.Peer, []error) { - errs := make([]error, len(pbps)) - peers := make([]peer.Peer, 0, len(pbps)) - for i, pbp := range pbps { - p, err := PBPeerToPeer(ps, pbp) - if err != nil { - errs[i] = err - } else { - peers = append(peers, p) - } +// PBPeersToPeerInfos converts given []*Message_Peer into []peer.PeerInfo +// Invalid addresses will be silently omitted. +func PBPeersToPeerInfos(pbps []*Message_Peer) []peer.PeerInfo { + peers := make([]peer.PeerInfo, 0, len(pbps)) + for _, pbp := range pbps { + peers = append(peers, PBPeerToPeerInfo(pbp)) } - return peers, errs + return peers } // Addresses returns a multiaddr associated with the Message_Peer entry -func (m *Message_Peer) Addresses() ([]ma.Multiaddr, error) { +func (m *Message_Peer) Addresses() []ma.Multiaddr { if m == nil { - return nil, errors.New("MessagePeer is nil") + return nil } var err error maddrs := make([]ma.Multiaddr, len(m.Addrs)) for i, addr := range m.Addrs { - maddrs[i], err = ma.NewMultiaddr(addr) + maddrs[i], err = ma.NewMultiaddrBytes(addr) if err != nil { - return nil, err + log.Error("error decoding Multiaddr for peer: %s", m.GetId()) + continue } } - return maddrs, nil + return maddrs } // GetClusterLevel gets and adjusts the cluster level on the message. diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 928b3fa32..861c25f0c 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -23,12 +23,12 @@ type ProviderManager struct { type addProv struct { k u.Key - val peer.Peer + val peer.ID } type getProv struct { k u.Key - resp chan []peer.Peer + resp chan []peer.ID } func NewProviderManager(ctx context.Context, local peer.ID) *ProviderManager { @@ -53,7 +53,7 @@ func (pm *ProviderManager) run() { for { select { case np := <-pm.newprovs: - if np.val.ID().Equal(pm.lpeer) { + if np.val == pm.lpeer { pm.local[np.k] = struct{}{} } pi := new(providerInfo) @@ -63,7 +63,7 @@ func (pm *ProviderManager) run() { pm.providers[np.k] = append(arr, pi) case gp := <-pm.getprovs: - var parr []peer.Peer + var parr []peer.ID provs := pm.providers[gp.k] for _, p := range provs { parr = append(parr, p.Value) @@ -94,17 +94,17 @@ func (pm *ProviderManager) run() { } } -func (pm *ProviderManager) AddProvider(k u.Key, val peer.Peer) { +func (pm *ProviderManager) AddProvider(k u.Key, val peer.ID) { pm.newprovs <- &addProv{ k: k, val: val, } } -func (pm *ProviderManager) GetProviders(ctx context.Context, k u.Key) []peer.Peer { +func (pm *ProviderManager) GetProviders(ctx context.Context, k u.Key) []peer.ID { gp := &getProv{ k: k, - resp: make(chan []peer.Peer, 1), // buffered to prevent sender from blocking + resp: make(chan []peer.ID, 1), // buffered to prevent sender from blocking } select { case <-ctx.Done(): diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 7d8aaa304..35ff92dfe 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -3,9 +3,8 @@ package dht import ( "testing" - "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" - testutil "github.com/jbenet/go-ipfs/util/testutil" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ) @@ -15,7 +14,7 @@ func TestProviderManager(t *testing.T) { mid := peer.ID("testing") p := NewProviderManager(ctx, mid) a := u.Key("test") - p.AddProvider(a, testutil.NewPeerWithIDString("testingprovider")) + p.AddProvider(a, peer.ID("testingprovider")) resp := p.GetProviders(ctx, a) if len(resp) != 1 { t.Fatal("Could not retrieve provider.") diff --git a/routing/dht/query.go b/routing/dht/query.go index f4e43132d..1321b5193 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -31,10 +31,10 @@ type dhtQuery struct { } type dhtQueryResult struct { - value []byte // GetValue - peer peer.Peer // FindPeer - providerPeers []peer.Peer // GetProviders - closerPeers []peer.Peer // * + value []byte // GetValue + peer peer.PeerInfo // FindPeer + providerPeers []peer.PeerInfo // GetProviders + closerPeers []peer.PeerInfo // * success bool } @@ -53,10 +53,10 @@ func newQuery(k u.Key, d inet.Dialer, f queryFunc) *dhtQuery { // - the value // - a list of peers potentially better able to serve the query // - an error -type queryFunc func(context.Context, peer.Peer) (*dhtQueryResult, error) +type queryFunc func(context.Context, peer.ID) (*dhtQueryResult, error) // Run runs the query at hand. pass in a list of peers to use first. -func (q *dhtQuery) Run(ctx context.Context, peers []peer.Peer) (*dhtQueryResult, error) { +func (q *dhtQuery) Run(ctx context.Context, peers []peer.ID) (*dhtQueryResult, error) { runner := newQueryRunner(ctx, q) return runner.Run(peers) } @@ -70,7 +70,7 @@ type dhtQueryRunner struct { peersToQuery *queue.ChanQueue // peersSeen are all the peers queried. used to prevent querying same peer 2x - peersSeen peer.Map + peersSeen peer.Set // rateLimit is a channel used to rate limit our processing (semaphore) rateLimit chan struct{} @@ -101,12 +101,12 @@ func newQueryRunner(ctx context.Context, q *dhtQuery) *dhtQueryRunner { query: q, peersToQuery: queue.NewChanQueue(ctx, queue.NewXORDistancePQ(q.key)), peersRemaining: todoctr.NewSyncCounter(), - peersSeen: peer.Map{}, + peersSeen: peer.Set{}, rateLimit: make(chan struct{}, q.concurrency), } } -func (r *dhtQueryRunner) Run(peers []peer.Peer) (*dhtQueryResult, error) { +func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { log.Debugf("Run query with %d peers.", len(peers)) if len(peers) == 0 { log.Warning("Running query with no peers!") @@ -120,7 +120,7 @@ func (r *dhtQueryRunner) Run(peers []peer.Peer) (*dhtQueryResult, error) { // add all the peers we got first. for _, p := range peers { - r.addPeerToQuery(p, nil) // don't have access to self here... + r.addPeerToQuery(p, "") // don't have access to self here... } // go do this thing. @@ -154,31 +154,30 @@ func (r *dhtQueryRunner) Run(peers []peer.Peer) (*dhtQueryResult, error) { return nil, err } -func (r *dhtQueryRunner) addPeerToQuery(next peer.Peer, benchmark peer.Peer) { - if next == nil { - // wtf why are peers nil?!? - log.Error("Query getting nil peers!!!\n") - return - } - +func (r *dhtQueryRunner) addPeerToQuery(next peer.ID, benchmark peer.ID) { // if new peer is ourselves... - if next.ID().Equal(r.query.dialer.LocalPeer().ID()) { + if next == r.query.dialer.LocalPeer() { return } // if new peer further away than whom we got it from, don't bother (loops) - if benchmark != nil && kb.Closer(benchmark.ID(), next.ID(), r.query.key) { + // TODO----------- this benchmark should be replaced by a heap: + // we should be doing the s/kademlia "continue to search" + // (i.e. put all of them in a heap sorted by dht distance and then just + // pull from the the top until a) you exhaust all peers you get, + // b) you succeed, c) your context expires. + if benchmark != "" && kb.Closer(benchmark, next, r.query.key) { return } // if already seen, no need. r.Lock() - _, found := r.peersSeen[next.Key()] + _, found := r.peersSeen[next] if found { r.Unlock() return } - r.peersSeen[next.Key()] = next + r.peersSeen[next] = struct{}{} r.Unlock() log.Debugf("adding peer to query: %v\n", next) @@ -211,7 +210,7 @@ func (r *dhtQueryRunner) spawnWorkers() { } } -func (r *dhtQueryRunner) queryPeer(p peer.Peer) { +func (r *dhtQueryRunner) queryPeer(p peer.ID) { log.Debugf("spawned worker for: %v", p) // make sure we rate limit concurrency. @@ -234,7 +233,6 @@ func (r *dhtQueryRunner) queryPeer(p peer.Peer) { }() // make sure we're connected to the peer. - // (Incidentally, this will add it to the peerstore too) err := r.query.dialer.DialPeer(r.ctx, p) if err != nil { log.Debugf("ERROR worker for: %v -- err connecting: %v", p, err) @@ -263,7 +261,9 @@ func (r *dhtQueryRunner) queryPeer(p peer.Peer) { } else if res.closerPeers != nil { log.Debugf("PEERS CLOSER -- worker for: %v", p) for _, next := range res.closerPeers { - r.addPeerToQuery(next, p) + // add their addresses to the dialer's peerstore + r.query.dialer.Peerstore().AddAddresses(next.ID, next.Addrs) + r.addPeerToQuery(next.ID, p) } } } diff --git a/routing/dht/records.go b/routing/dht/records.go index 1f284ed99..cf383916b 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -3,15 +3,17 @@ package dht import ( "bytes" "errors" + "fmt" "strings" - "time" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + ci "github.com/jbenet/go-ipfs/crypto" "github.com/jbenet/go-ipfs/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" + ctxutil "github.com/jbenet/go-ipfs/util/ctx" ) // ValidatorFunc is a function that is called to validate a given @@ -26,64 +28,163 @@ var ErrBadRecord = errors.New("bad dht record") // is not found in the Validator map of the DHT. var ErrInvalidRecordType = errors.New("invalid record keytype") +// KeyForPublicKey returns the key used to retrieve public keys +// from the dht. +func KeyForPublicKey(id peer.ID) u.Key { + return u.Key("/pk/" + string(id)) +} + +// RecordBlobForSig returns the blob protected by the record signature +func RecordBlobForSig(r *pb.Record) []byte { + k := []byte(r.GetKey()) + v := []byte(r.GetValue()) + a := []byte(r.GetAuthor()) + return bytes.Join([][]byte{k, v, a}, []byte{}) +} + // creates and signs a dht record for the given key/value pair func (dht *IpfsDHT) makePutRecord(key u.Key, value []byte) (*pb.Record, error) { record := new(pb.Record) record.Key = proto.String(string(key)) record.Value = value - record.Author = proto.String(string(dht.self.ID())) - blob := bytes.Join([][]byte{[]byte(key), value, []byte(dht.self.ID())}, []byte{}) - sig, err := dht.self.PrivKey().Sign(blob) + record.Author = proto.String(string(dht.self)) + blob := RecordBlobForSig(record) + + sk := dht.peerstore.PrivKey(dht.self) + if sk == nil { + log.Errorf("%s dht cannot get own private key!", dht.self) + return nil, fmt.Errorf("cannot get private key to sign record!") + } + + sig, err := sk.Sign(blob) if err != nil { return nil, err } + record.Signature = sig return record, nil } -func (dht *IpfsDHT) getPublicKey(pid peer.ID) (ci.PubKey, error) { - log.Debug("getPublicKey for: %s", pid) - p, err := dht.peerstore.FindOrCreate(pid) - if err == nil { - return p.PubKey(), nil +func (dht *IpfsDHT) getPublicKeyOnline(ctx context.Context, p peer.ID) (ci.PubKey, error) { + log.Debugf("getPublicKey for: %s", p) + + // check locally. + pk := dht.peerstore.PubKey(p) + if pk != nil { + return pk, nil + } + + // ok, try the node itself. if they're overwhelmed or slow we can move on. + ctxT, _ := ctxutil.WithDeadlineFraction(ctx, 0.3) + if pk, err := dht.getPublicKeyFromNode(ctx, p); err == nil { + return pk, nil } - log.Debug("not in peerstore, searching dht.") - ctxT, _ := context.WithTimeout(dht.ContextGroup.Context(), time.Second*5) - val, err := dht.GetValue(ctxT, u.Key("/pk/"+string(pid))) + // last ditch effort: let's try the dht. + log.Debugf("pk for %s not in peerstore, and peer failed. trying dht.", p) + pkkey := KeyForPublicKey(p) + + // ok, try the node itself. if they're overwhelmed or slow we can move on. + val, err := dht.GetValue(ctxT, pkkey) if err != nil { log.Warning("Failed to find requested public key.") return nil, err } - pubkey, err := ci.UnmarshalPublicKey(val) + pk, err = ci.UnmarshalPublicKey(val) if err != nil { log.Errorf("Failed to unmarshal public key: %s", err) return nil, err } - return pubkey, nil + return pk, nil } -func (dht *IpfsDHT) verifyRecord(r *pb.Record) error { +func (dht *IpfsDHT) getPublicKeyFromNode(ctx context.Context, p peer.ID) (ci.PubKey, error) { + + // check locally, just in case... + pk := dht.peerstore.PubKey(p) + if pk != nil { + return pk, nil + } + + pkkey := KeyForPublicKey(p) + pmes, err := dht.getValueSingle(ctx, p, pkkey) + if err != nil { + return nil, err + } + + // node doesn't have key :( + record := pmes.GetRecord() + if record == nil { + return nil, fmt.Errorf("node not responding with its public key: %s", p) + } + + // Success! We were given the value. we don't need to check + // validity because a) we can't. b) we know the hash of the + // key we're looking for. + val := record.GetValue() + log.Debug("dht got a value from other peer.") + + pk, err = ci.UnmarshalPublicKey(val) + if err != nil { + return nil, err + } + + id, err := peer.IDFromPublicKey(pk) + if err != nil { + return nil, err + } + if id != p { + return nil, fmt.Errorf("public key does not match id: %s", p) + } + + // ok! it's valid. we got it! + log.Debugf("dht got public key from node itself.") + return pk, nil +} + +// verifyRecordLocally attempts to verify a record. if we do not have the public +// key, we fail. we do not search the dht. +func (dht *IpfsDHT) verifyRecordLocally(r *pb.Record) error { + // First, validate the signature - p, err := dht.peerstore.FindOrCreate(peer.ID(r.GetAuthor())) + p := peer.ID(r.GetAuthor()) + pk := dht.peerstore.PubKey(p) + if pk == nil { + return fmt.Errorf("do not have public key for %s", p) + } + + return dht.verifyRecord(r, pk) +} + +// verifyRecordOnline verifies a record, searching the DHT for the public key +// if necessary. The reason there is a distinction in the functions is that +// retrieving arbitrary public keys from the DHT as a result of passively +// receiving records (e.g. through a PUT_VALUE or ADD_PROVIDER) can cause a +// massive amplification attack on the dht. Use with care. +func (dht *IpfsDHT) verifyRecordOnline(ctx context.Context, r *pb.Record) error { + + // get the public key, search for it if necessary. + p := peer.ID(r.GetAuthor()) + pk, err := dht.getPublicKeyOnline(ctx, p) if err != nil { return err } - k := u.Key(r.GetKey()) - blob := bytes.Join([][]byte{[]byte(k), - r.GetValue(), - []byte(r.GetAuthor())}, []byte{}) + return dht.verifyRecord(r, pk) +} - ok, err := p.PubKey().Verify(blob, r.GetSignature()) +func (dht *IpfsDHT) verifyRecord(r *pb.Record, pk ci.PubKey) error { + // First, validate the signature + blob := RecordBlobForSig(r) + ok, err := pk.Verify(blob, r.GetSignature()) if err != nil { log.Error("Signature verify failed.") return err } - if !ok { + log.Error("dht found a forged record! (ignored)") return ErrBadRecord } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 51f15ff21..9a0620581 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -41,7 +41,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error peers := dht.routingTable.NearestPeers(kb.ConvertKey(key), KValue) - query := newQuery(key, dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(key, dht.network, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { log.Debugf("%s PutValue qry part %v", dht.self, p) err := dht.putValueToNetwork(ctx, p, string(key), rec) if err != nil { @@ -61,7 +61,6 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { log.Debugf("Get Value [%s]", key) // If we have it local, dont bother doing an RPC! - // NOTE: this might not be what we want to do... val, err := dht.getLocal(key) if err == nil { log.Debug("Got value locally!") @@ -76,7 +75,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { } // setup the Query - query := newQuery(key, dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(key, dht.network, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { val, peers, err := dht.getValueOrPeers(ctx, p, key) if err != nil { @@ -131,14 +130,14 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { // FindProvidersAsync is the same thing as FindProviders, but returns a channel. // Peers will be returned on the channel as soon as they are found, even before // the search query completes. -func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int) <-chan peer.Peer { +func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int) <-chan peer.PeerInfo { log.Event(ctx, "findProviders", &key) - peerOut := make(chan peer.Peer, count) + peerOut := make(chan peer.PeerInfo, count) go dht.findProvidersAsyncRoutine(ctx, key, count, peerOut) return peerOut } -func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, count int, peerOut chan peer.Peer) { +func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, count int, peerOut chan peer.PeerInfo) { defer close(peerOut) ps := pset.NewLimited(count) @@ -147,7 +146,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co // NOTE: assuming that this list of peers is unique if ps.TryAdd(p) { select { - case peerOut <- p: + case peerOut <- dht.peerstore.PeerInfo(p): case <-ctx.Done(): return } @@ -160,23 +159,18 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co } // setup the Query - query := newQuery(key, dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(key, dht.network, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { pmes, err := dht.findProvidersSingle(ctx, p, key) if err != nil { return nil, err } - provs, errs := pb.PBPeersToPeers(dht.peerstore, pmes.GetProviderPeers()) - for _, err := range errs { - if err != nil { - log.Warning(err) - } - } + provs := pb.PBPeersToPeerInfos(pmes.GetProviderPeers()) // Add unique providers from request, up to 'count' for _, prov := range provs { - if ps.TryAdd(prov) { + if ps.TryAdd(prov.ID) { select { case peerOut <- prov: case <-ctx.Done(): @@ -191,13 +185,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co // Give closer peers back to the query to be queried closer := pmes.GetCloserPeers() - clpeers, errs := pb.PBPeersToPeers(dht.peerstore, closer) - for _, err := range errs { - if err != nil { - log.Warning(err) - } - } - + clpeers := pb.PBPeersToPeerInfos(closer) return &dhtQueryResult{closerPeers: clpeers}, nil }) @@ -208,62 +196,58 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co } } -func (dht *IpfsDHT) addPeerListAsync(ctx context.Context, k u.Key, peers []*pb.Message_Peer, ps *pset.PeerSet, count int, out chan peer.Peer) { +func (dht *IpfsDHT) addPeerListAsync(ctx context.Context, k u.Key, peers []*pb.Message_Peer, ps *pset.PeerSet, count int, out chan peer.PeerInfo) { var wg sync.WaitGroup - for _, pbp := range peers { + peerInfos := pb.PBPeersToPeerInfos(peers) + for _, pi := range peerInfos { wg.Add(1) - go func(mp *pb.Message_Peer) { + go func(pi peer.PeerInfo) { defer wg.Done() - // construct new peer - p, err := dht.ensureConnectedToPeer(ctx, mp) - if err != nil { + + p := pi.ID + if err := dht.ensureConnectedToPeer(ctx, p); err != nil { log.Errorf("%s", err) return } - if p == nil { - log.Error("Got nil peer from ensureConnectedToPeer") - return - } dht.providers.AddProvider(k, p) if ps.TryAdd(p) { select { - case out <- p: + case out <- pi: case <-ctx.Done(): return } } else if ps.Size() >= count { return } - }(pbp) + }(pi) } wg.Wait() } // FindPeer searches for a peer with given ID. -func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) { +func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, error) { // Check if were already connected to them - p, _ := dht.FindLocal(id) - if p != nil { - return p, nil + if pi, _ := dht.FindLocal(id); pi.ID != "" { + return pi, nil } closest := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue) if closest == nil || len(closest) == 0 { - return nil, kb.ErrLookupFailure + return peer.PeerInfo{}, kb.ErrLookupFailure } // Sanity... for _, p := range closest { - if p.ID().Equal(id) { + if p == id { log.Error("Found target peer in list of closest peers...") - return p, nil + return dht.peerstore.PeerInfo(p), nil } } // setup the Query - query := newQuery(u.Key(id), dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(u.Key(id), dht.network, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { pmes, err := dht.findPeerSingle(ctx, p, id) if err != nil { @@ -271,45 +255,40 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) } closer := pmes.GetCloserPeers() - clpeers, errs := pb.PBPeersToPeers(dht.peerstore, closer) - for _, err := range errs { - if err != nil { - log.Warning(err) - } - } + clpeerInfos := pb.PBPeersToPeerInfos(closer) // see it we got the peer here - for _, np := range clpeers { - if string(np.ID()) == string(id) { + for _, npi := range clpeerInfos { + if npi.ID == id { return &dhtQueryResult{ - peer: np, + peer: npi, success: true, }, nil } } - return &dhtQueryResult{closerPeers: clpeers}, nil + return &dhtQueryResult{closerPeers: clpeerInfos}, nil }) // run it! result, err := query.Run(ctx, closest) if err != nil { - return nil, err + return peer.PeerInfo{}, err } log.Debugf("FindPeer %v %v", id, result.success) - if result.peer == nil { - return nil, routing.ErrNotFound + if result.peer.ID == "" { + return peer.PeerInfo{}, routing.ErrNotFound } return result.peer, nil } // FindPeersConnectedToPeer searches for peers directly connected to a given peer. -func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (<-chan peer.Peer, error) { +func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (<-chan peer.PeerInfo, error) { - peerchan := make(chan peer.Peer, asyncQueryBuffer) - peersSeen := map[string]peer.Peer{} + peerchan := make(chan peer.PeerInfo, asyncQueryBuffer) + peersSeen := peer.Set{} closest := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue) if closest == nil || len(closest) == 0 { @@ -317,42 +296,37 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< } // setup the Query - query := newQuery(u.Key(id), dht.network, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) { + query := newQuery(u.Key(id), dht.network, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { pmes, err := dht.findPeerSingle(ctx, p, id) if err != nil { return nil, err } - var clpeers []peer.Peer + var clpeers []peer.PeerInfo closer := pmes.GetCloserPeers() for _, pbp := range closer { - // skip peers already seen - if _, found := peersSeen[string(pbp.GetId())]; found { - continue - } + pi := pb.PBPeerToPeerInfo(pbp) - // skip peers that fail to unmarshal - p, err := pb.PBPeerToPeer(dht.peerstore, pbp) - if err != nil { - log.Warning(err) + // skip peers already seen + if _, found := peersSeen[pi.ID]; found { continue } + peersSeen[pi.ID] = struct{}{} // if peer is connected, send it to our client. if pb.Connectedness(*pbp.Connection) == inet.Connected { select { case <-ctx.Done(): return nil, ctx.Err() - case peerchan <- p: + case peerchan <- pi: } } - peersSeen[string(p.ID())] = p - // if peer is the peer we're looking for, don't bother querying it. + // TODO maybe query it? if pb.Connectedness(*pbp.Connection) != inet.Connected { - clpeers = append(clpeers, p) + clpeers = append(clpeers, pi) } } @@ -374,7 +348,7 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< } // Ping a peer, log the time it took -func (dht *IpfsDHT) Ping(ctx context.Context, p peer.Peer) error { +func (dht *IpfsDHT) Ping(ctx context.Context, p peer.ID) error { // Thoughts: maybe this should accept an ID and do a peer lookup? log.Debugf("ping %s start", p) diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 51f524971..2fa5586db 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -23,7 +23,7 @@ func (b *Bucket) find(id peer.ID) *list.Element { b.lk.RLock() defer b.lk.RUnlock() for e := b.list.Front(); e != nil; e = e.Next() { - if e.Value.(peer.Peer).ID().Equal(id) { + if e.Value.(peer.ID) == id { return e } } @@ -36,18 +36,18 @@ func (b *Bucket) moveToFront(e *list.Element) { b.lk.Unlock() } -func (b *Bucket) pushFront(p peer.Peer) { +func (b *Bucket) pushFront(p peer.ID) { b.lk.Lock() b.list.PushFront(p) b.lk.Unlock() } -func (b *Bucket) popBack() peer.Peer { +func (b *Bucket) popBack() peer.ID { b.lk.Lock() defer b.lk.Unlock() last := b.list.Back() b.list.Remove(last) - return last.Value.(peer.Peer) + return last.Value.(peer.ID) } func (b *Bucket) len() int { @@ -68,7 +68,7 @@ func (b *Bucket) Split(cpl int, target ID) *Bucket { newbuck.list = out e := b.list.Front() for e != nil { - peerID := ConvertPeerID(e.Value.(peer.Peer).ID()) + peerID := ConvertPeerID(e.Value.(peer.ID)) peerCPL := commonPrefixLen(peerID, target) if peerCPL > cpl { cur := e diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index c144c191e..da4c6e720 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -23,6 +23,9 @@ type RoutingTable struct { // Blanket lock, refine later for better performance tabLock sync.RWMutex + // latency metrics + metrics peer.Metrics + // Maximum acceptable latency for peers in this cluster maxLatency time.Duration @@ -32,21 +35,22 @@ type RoutingTable struct { } // NewRoutingTable creates a new routing table with a given bucketsize, local ID, and latency tolerance. -func NewRoutingTable(bucketsize int, localID ID, latency time.Duration) *RoutingTable { +func NewRoutingTable(bucketsize int, localID ID, latency time.Duration, m peer.Metrics) *RoutingTable { rt := new(RoutingTable) rt.Buckets = []*Bucket{newBucket()} rt.bucketsize = bucketsize rt.local = localID rt.maxLatency = latency + rt.metrics = m return rt } // Update adds or moves the given peer to the front of its respective bucket // If a peer gets removed from a bucket, it is returned -func (rt *RoutingTable) Update(p peer.Peer) peer.Peer { +func (rt *RoutingTable) Update(p peer.ID) peer.ID { rt.tabLock.Lock() defer rt.tabLock.Unlock() - peerID := ConvertPeerID(p.ID()) + peerID := ConvertPeerID(p) cpl := commonPrefixLen(peerID, rt.local) bucketID := cpl @@ -55,12 +59,12 @@ func (rt *RoutingTable) Update(p peer.Peer) peer.Peer { } bucket := rt.Buckets[bucketID] - e := bucket.find(p.ID()) + e := bucket.find(p) if e == nil { // New peer, add to bucket - if p.GetLatency() > rt.maxLatency { + if rt.metrics.LatencyEWMA(p) > rt.maxLatency { // Connection doesnt meet requirements, skip! - return nil + return "" } bucket.pushFront(p) @@ -75,16 +79,16 @@ func (rt *RoutingTable) Update(p peer.Peer) peer.Peer { return bucket.popBack() } } - return nil + return "" } // If the peer is already in the table, move it to the front. // This signifies that it it "more active" and the less active nodes // Will as a result tend towards the back of the list bucket.moveToFront(e) - return nil + return "" } -func (rt *RoutingTable) nextBucket() peer.Peer { +func (rt *RoutingTable) nextBucket() peer.ID { bucket := rt.Buckets[len(rt.Buckets)-1] newBucket := bucket.Split(len(rt.Buckets)-1, rt.local) rt.Buckets = append(rt.Buckets, newBucket) @@ -96,12 +100,12 @@ func (rt *RoutingTable) nextBucket() peer.Peer { if bucket.len() > rt.bucketsize { return bucket.popBack() } - return nil + return "" } // A helper struct to sort peers by their distance to the local node type peerDistance struct { - p peer.Peer + p peer.ID distance ID } @@ -118,8 +122,8 @@ func (p peerSorterArr) Less(a, b int) bool { func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) peerSorterArr { for e := peerList.Front(); e != nil; e = e.Next() { - p := e.Value.(peer.Peer) - pID := ConvertPeerID(p.ID()) + p := e.Value.(peer.ID) + pID := ConvertPeerID(p) pd := peerDistance{ p: p, distance: xor(target, pID), @@ -134,27 +138,27 @@ func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) pe } // Find a specific peer by ID or return nil -func (rt *RoutingTable) Find(id peer.ID) peer.Peer { +func (rt *RoutingTable) Find(id peer.ID) peer.ID { srch := rt.NearestPeers(ConvertPeerID(id), 1) - if len(srch) == 0 || !srch[0].ID().Equal(id) { - return nil + if len(srch) == 0 || srch[0] != id { + return "" } return srch[0] } // NearestPeer returns a single peer that is nearest to the given ID -func (rt *RoutingTable) NearestPeer(id ID) peer.Peer { +func (rt *RoutingTable) NearestPeer(id ID) peer.ID { peers := rt.NearestPeers(id, 1) if len(peers) > 0 { return peers[0] } log.Errorf("NearestPeer: Returning nil, table size = %d", rt.Size()) - return nil + return "" } // NearestPeers returns a list of the 'count' closest peers to the given ID -func (rt *RoutingTable) NearestPeers(id ID, count int) []peer.Peer { +func (rt *RoutingTable) NearestPeers(id ID, count int) []peer.ID { rt.tabLock.RLock() defer rt.tabLock.RUnlock() cpl := commonPrefixLen(id, rt.local) @@ -186,7 +190,7 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []peer.Peer { // Sort by distance to local peer sort.Sort(peerArr) - var out []peer.Peer + var out []peer.ID for i := 0; i < count && i < peerArr.Len(); i++ { out = append(out, peerArr[i].p) } @@ -205,11 +209,11 @@ func (rt *RoutingTable) Size() int { // ListPeers takes a RoutingTable and returns a list of all peers from all buckets in the table. // NOTE: This is potentially unsafe... use at your own risk -func (rt *RoutingTable) ListPeers() []peer.Peer { - var peers []peer.Peer +func (rt *RoutingTable) ListPeers() []peer.ID { + var peers []peer.ID for _, buck := range rt.Buckets { for e := buck.getIter(); e != nil; e = e.Next() { - peers = append(peers, e.Value.(peer.Peer)) + peers = append(peers, e.Value.(peer.ID)) } } return peers @@ -221,6 +225,6 @@ func (rt *RoutingTable) Print() { rt.tabLock.RLock() peers := rt.ListPeers() for i, p := range peers { - fmt.Printf("%d) %s %s\n", i, p.ID().Pretty(), p.GetLatency().String()) + fmt.Printf("%d) %s %s\n", i, p.Pretty(), rt.metrics.LatencyEWMA(p).String()) } } diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 85fc387e2..db93ddf86 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -1,8 +1,6 @@ package kbucket import ( - crand "crypto/rand" - "crypto/sha256" "math/rand" "testing" "time" @@ -12,37 +10,29 @@ import ( peer "github.com/jbenet/go-ipfs/peer" ) -func RandID() ID { - buf := make([]byte, 16) - crand.Read(buf) - - hash := sha256.Sum256(buf) - return ID(hash[:]) -} - // Test basic features of the bucket struct func TestBucket(t *testing.T) { b := newBucket() - peers := make([]peer.Peer, 100) + peers := make([]peer.ID, 100) for i := 0; i < 100; i++ { - peers[i] = tu.RandPeer() + peers[i] = tu.RandPeerIDFatal(t) b.pushFront(peers[i]) } - local := tu.RandPeer() - localID := ConvertPeerID(local.ID()) + local := tu.RandPeerIDFatal(t) + localID := ConvertPeerID(local) i := rand.Intn(len(peers)) - e := b.find(peers[i].ID()) + e := b.find(peers[i]) if e == nil { t.Errorf("Failed to find peer: %v", peers[i]) } - spl := b.Split(0, ConvertPeerID(local.ID())) + spl := b.Split(0, ConvertPeerID(local)) llist := b.list for e := llist.Front(); e != nil; e = e.Next() { - p := ConvertPeerID(e.Value.(peer.Peer).ID()) + p := ConvertPeerID(e.Value.(peer.ID)) cpl := commonPrefixLen(p, localID) if cpl > 0 { t.Fatalf("Split failed. found id with cpl > 0 in 0 bucket") @@ -51,7 +41,7 @@ func TestBucket(t *testing.T) { rlist := spl.list for e := rlist.Front(); e != nil; e = e.Next() { - p := ConvertPeerID(e.Value.(peer.Peer).ID()) + p := ConvertPeerID(e.Value.(peer.ID)) cpl := commonPrefixLen(p, localID) if cpl == 0 { t.Fatalf("Split failed. found id with cpl == 0 in non 0 bucket") @@ -61,24 +51,25 @@ func TestBucket(t *testing.T) { // Right now, this just makes sure that it doesnt hang or crash func TestTableUpdate(t *testing.T) { - local := tu.RandPeer() - rt := NewRoutingTable(10, ConvertPeerID(local.ID()), time.Hour) + local := tu.RandPeerIDFatal(t) + m := peer.NewMetrics() + rt := NewRoutingTable(10, ConvertPeerID(local), time.Hour, m) - peers := make([]peer.Peer, 100) + peers := make([]peer.ID, 100) for i := 0; i < 100; i++ { - peers[i] = tu.RandPeer() + peers[i] = tu.RandPeerIDFatal(t) } // Testing Update for i := 0; i < 10000; i++ { p := rt.Update(peers[rand.Intn(len(peers))]) - if p != nil { + if p != "" { //t.Log("evicted peer.") } } for i := 0; i < 100; i++ { - id := RandID() + id := ConvertPeerID(tu.RandPeerIDFatal(t)) ret := rt.NearestPeers(id, 5) if len(ret) == 0 { t.Fatal("Failed to find node near ID.") @@ -87,34 +78,36 @@ func TestTableUpdate(t *testing.T) { } func TestTableFind(t *testing.T) { - local := tu.RandPeer() - rt := NewRoutingTable(10, ConvertPeerID(local.ID()), time.Hour) + local := tu.RandPeerIDFatal(t) + m := peer.NewMetrics() + rt := NewRoutingTable(10, ConvertPeerID(local), time.Hour, m) - peers := make([]peer.Peer, 100) + peers := make([]peer.ID, 100) for i := 0; i < 5; i++ { - peers[i] = tu.RandPeer() + peers[i] = tu.RandPeerIDFatal(t) rt.Update(peers[i]) } t.Logf("Searching for peer: '%s'", peers[2]) - found := rt.NearestPeer(ConvertPeerID(peers[2].ID())) - if !found.ID().Equal(peers[2].ID()) { + found := rt.NearestPeer(ConvertPeerID(peers[2])) + if !(found == peers[2]) { t.Fatalf("Failed to lookup known node...") } } func TestTableFindMultiple(t *testing.T) { - local := tu.RandPeer() - rt := NewRoutingTable(20, ConvertPeerID(local.ID()), time.Hour) + local := tu.RandPeerIDFatal(t) + m := peer.NewMetrics() + rt := NewRoutingTable(20, ConvertPeerID(local), time.Hour, m) - peers := make([]peer.Peer, 100) + peers := make([]peer.ID, 100) for i := 0; i < 18; i++ { - peers[i] = tu.RandPeer() + peers[i] = tu.RandPeerIDFatal(t) rt.Update(peers[i]) } t.Logf("Searching for peer: '%s'", peers[2]) - found := rt.NearestPeers(ConvertPeerID(peers[2].ID()), 15) + found := rt.NearestPeers(ConvertPeerID(peers[2]), 15) if len(found) != 15 { t.Fatalf("Got back different number of peers than we expected.") } @@ -125,10 +118,11 @@ func TestTableFindMultiple(t *testing.T) { // and set GOMAXPROCS above 1 func TestTableMultithreaded(t *testing.T) { local := peer.ID("localPeer") - tab := NewRoutingTable(20, ConvertPeerID(local), time.Hour) - var peers []peer.Peer + m := peer.NewMetrics() + tab := NewRoutingTable(20, ConvertPeerID(local), time.Hour, m) + var peers []peer.ID for i := 0; i < 500; i++ { - peers = append(peers, tu.RandPeer()) + peers = append(peers, tu.RandPeerIDFatal(t)) } done := make(chan struct{}) @@ -151,7 +145,7 @@ func TestTableMultithreaded(t *testing.T) { go func() { for i := 0; i < 1000; i++ { n := rand.Intn(len(peers)) - tab.Find(peers[n].ID()) + tab.Find(peers[n]) } done <- struct{}{} }() @@ -163,11 +157,12 @@ func TestTableMultithreaded(t *testing.T) { func BenchmarkUpdates(b *testing.B) { b.StopTimer() local := ConvertKey("localKey") - tab := NewRoutingTable(20, local, time.Hour) + m := peer.NewMetrics() + tab := NewRoutingTable(20, local, time.Hour, m) - var peers []peer.Peer + var peers []peer.ID for i := 0; i < b.N; i++ { - peers = append(peers, tu.RandPeer()) + peers = append(peers, tu.RandPeerIDFatal(b)) } b.StartTimer() @@ -179,16 +174,17 @@ func BenchmarkUpdates(b *testing.B) { func BenchmarkFinds(b *testing.B) { b.StopTimer() local := ConvertKey("localKey") - tab := NewRoutingTable(20, local, time.Hour) + m := peer.NewMetrics() + tab := NewRoutingTable(20, local, time.Hour, m) - var peers []peer.Peer + var peers []peer.ID for i := 0; i < b.N; i++ { - peers = append(peers, tu.RandPeer()) + peers = append(peers, tu.RandPeerIDFatal(b)) tab.Update(peers[i]) } b.StartTimer() for i := 0; i < b.N; i++ { - tab.Find(peers[i].ID()) + tab.Find(peers[i]) } } diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 4adac0405..2d06b5f08 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -40,7 +40,7 @@ func commonPrefixLen(a, b ID) int { // ConvertPeerID creates a DHT ID by hashing a Peer ID (Multihash) func ConvertPeerID(id peer.ID) ID { - hash := sha256.Sum256(id) + hash := sha256.Sum256([]byte(id)) return hash[:] } diff --git a/routing/mock/client.go b/routing/mock/client.go index 444a4b960..9be43b653 100644 --- a/routing/mock/client.go +++ b/routing/mock/client.go @@ -15,7 +15,7 @@ var log = u.Logger("mockrouter") type client struct { datastore ds.Datastore server server - peer peer.Peer + peer peer.PeerInfo } // FIXME(brian): is this method meant to simulate putting a value into the network? @@ -40,17 +40,17 @@ func (c *client) GetValue(ctx context.Context, key u.Key) ([]byte, error) { return data, nil } -func (c *client) FindProviders(ctx context.Context, key u.Key) ([]peer.Peer, error) { +func (c *client) FindProviders(ctx context.Context, key u.Key) ([]peer.PeerInfo, error) { return c.server.Providers(key), nil } -func (c *client) FindPeer(ctx context.Context, pid peer.ID) (peer.Peer, error) { +func (c *client) FindPeer(ctx context.Context, pid peer.ID) (peer.PeerInfo, error) { log.Debugf("FindPeer: %s", pid) - return nil, nil + return peer.PeerInfo{}, nil } -func (c *client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-chan peer.Peer { - out := make(chan peer.Peer) +func (c *client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-chan peer.PeerInfo { + out := make(chan peer.PeerInfo) go func() { defer close(out) for i, p := range c.server.Providers(k) { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 639736292..abb869eb4 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -15,13 +15,13 @@ import ( // Server provides mockrouting Clients type Server interface { - Client(p peer.Peer) Client - ClientWithDatastore(peer.Peer, ds.Datastore) Client + Client(p peer.PeerInfo) Client + ClientWithDatastore(peer.PeerInfo, ds.Datastore) Client } // Client implements IpfsRouting type Client interface { - FindProviders(context.Context, u.Key) ([]peer.Peer, error) + FindProviders(context.Context, u.Key) ([]peer.PeerInfo, error) routing.IpfsRouting } @@ -37,7 +37,7 @@ func NewServer() Server { // NewServerWithDelay returns a mockrouting Server with a delay! func NewServerWithDelay(conf DelayConfig) Server { return &s{ - providers: make(map[u.Key]map[u.Key]providerRecord), + providers: make(map[u.Key]map[peer.ID]providerRecord), delayConf: conf, } } diff --git a/routing/mock/mockrouting_test.go b/routing/mock/mockrouting_test.go index 44b1b52bd..64540a3bc 100644 --- a/routing/mock/mockrouting_test.go +++ b/routing/mock/mockrouting_test.go @@ -1,7 +1,6 @@ package mockrouting import ( - "bytes" "testing" "time" @@ -9,17 +8,16 @@ import ( peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" delay "github.com/jbenet/go-ipfs/util/delay" - testutil "github.com/jbenet/go-ipfs/util/testutil" ) func TestKeyNotFound(t *testing.T) { - var peer = testutil.NewPeerWithID(peer.ID([]byte("the peer id"))) + var pi = peer.PeerInfo{ID: peer.ID("the peer id")} var key = u.Key("mock key") var ctx = context.Background() rs := NewServer() - providers := rs.Client(peer).FindProvidersAsync(ctx, key, 10) + providers := rs.Client(pi).FindProvidersAsync(ctx, key, 10) _, ok := <-providers if ok { t.Fatal("should be closed") @@ -27,9 +25,9 @@ func TestKeyNotFound(t *testing.T) { } func TestClientFindProviders(t *testing.T) { - peer := testutil.NewPeerWithIDString("42") + pi := peer.PeerInfo{ID: peer.ID("42")} rs := NewServer() - client := rs.Client(peer) + client := rs.Client(pi) k := u.Key("hello") err := client.Provide(context.Background(), k) @@ -41,14 +39,14 @@ func TestClientFindProviders(t *testing.T) { time.Sleep(time.Millisecond * 300) max := 100 - providersFromHashTable, err := rs.Client(peer).FindProviders(context.Background(), k) + providersFromHashTable, err := rs.Client(pi).FindProviders(context.Background(), k) if err != nil { t.Fatal(err) } isInHT := false - for _, p := range providersFromHashTable { - if bytes.Equal(p.ID(), peer.ID()) { + for _, pi := range providersFromHashTable { + if pi.ID == pi.ID { isInHT = true } } @@ -57,8 +55,8 @@ func TestClientFindProviders(t *testing.T) { } providersFromClient := client.FindProvidersAsync(context.Background(), u.Key("hello"), max) isInClient := false - for p := range providersFromClient { - if bytes.Equal(p.ID(), peer.ID()) { + for pi := range providersFromClient { + if pi.ID == pi.ID { isInClient = true } } @@ -72,16 +70,16 @@ func TestClientOverMax(t *testing.T) { k := u.Key("hello") numProvidersForHelloKey := 100 for i := 0; i < numProvidersForHelloKey; i++ { - peer := testutil.NewPeerWithIDString(string(i)) - err := rs.Client(peer).Provide(context.Background(), k) + pi := peer.PeerInfo{ID: peer.ID(i)} + err := rs.Client(pi).Provide(context.Background(), k) if err != nil { t.Fatal(err) } } max := 10 - peer := testutil.NewPeerWithIDString("TODO") - client := rs.Client(peer) + pi := peer.PeerInfo{ID: peer.ID("TODO")} + client := rs.Client(pi) providersFromClient := client.FindProvidersAsync(context.Background(), k, max) i := 0 @@ -102,16 +100,16 @@ func TestCanceledContext(t *testing.T) { i := 0 go func() { // infinite stream for { - peer := testutil.NewPeerWithIDString(string(i)) - err := rs.Client(peer).Provide(context.Background(), k) + pi := peer.PeerInfo{ID: peer.ID(i)} + err := rs.Client(pi).Provide(context.Background(), k) if err != nil { - t.Fatal(err) + t.Error(err) } i++ } }() - local := testutil.NewPeerWithIDString("peer id doesn't matter") + local := peer.PeerInfo{ID: peer.ID("peer id doesn't matter")} client := rs.Client(local) t.Log("warning: max is finite so this test is non-deterministic") @@ -137,7 +135,7 @@ func TestCanceledContext(t *testing.T) { func TestValidAfter(t *testing.T) { - var p = testutil.NewPeerWithID(peer.ID([]byte("the peer id"))) + var pi = peer.PeerInfo{ID: peer.ID("the peer id")} var key = u.Key("mock key") var ctx = context.Background() conf := DelayConfig{ @@ -147,10 +145,10 @@ func TestValidAfter(t *testing.T) { rs := NewServerWithDelay(conf) - rs.Client(p).Provide(ctx, key) + rs.Client(pi).Provide(ctx, key) - var providers []peer.Peer - providers, err := rs.Client(p).FindProviders(ctx, key) + var providers []peer.PeerInfo + providers, err := rs.Client(pi).FindProviders(ctx, key) if err != nil { t.Fatal(err) } @@ -159,7 +157,7 @@ func TestValidAfter(t *testing.T) { } conf.ValueVisibility.Set(0) - providers, err = rs.Client(p).FindProviders(ctx, key) + providers, err = rs.Client(pi).FindProviders(ctx, key) if err != nil { t.Fatal(err) } diff --git a/routing/mock/server.go b/routing/mock/server.go index e176c7aeb..31ae4b730 100644 --- a/routing/mock/server.go +++ b/routing/mock/server.go @@ -12,8 +12,8 @@ import ( // server is the mockrouting.Client's private interface to the routing server type server interface { - Announce(peer.Peer, u.Key) error - Providers(u.Key) []peer.Peer + Announce(peer.PeerInfo, u.Key) error + Providers(u.Key) []peer.PeerInfo Server } @@ -23,36 +23,36 @@ type s struct { delayConf DelayConfig lock sync.RWMutex - providers map[u.Key]map[u.Key]providerRecord + providers map[u.Key]map[peer.ID]providerRecord } type providerRecord struct { - Peer peer.Peer + Peer peer.PeerInfo Created time.Time } -func (rs *s) Announce(p peer.Peer, k u.Key) error { +func (rs *s) Announce(p peer.PeerInfo, k u.Key) error { rs.lock.Lock() defer rs.lock.Unlock() _, ok := rs.providers[k] if !ok { - rs.providers[k] = make(map[u.Key]providerRecord) + rs.providers[k] = make(map[peer.ID]providerRecord) } - rs.providers[k][p.Key()] = providerRecord{ + rs.providers[k][p.ID] = providerRecord{ Created: time.Now(), Peer: p, } return nil } -func (rs *s) Providers(k u.Key) []peer.Peer { +func (rs *s) Providers(k u.Key) []peer.PeerInfo { rs.delayConf.Query.Wait() // before locking rs.lock.RLock() defer rs.lock.RUnlock() - var ret []peer.Peer + var ret []peer.PeerInfo records, ok := rs.providers[k] if !ok { return ret @@ -71,11 +71,11 @@ func (rs *s) Providers(k u.Key) []peer.Peer { return ret } -func (rs *s) Client(p peer.Peer) Client { +func (rs *s) Client(p peer.PeerInfo) Client { return rs.ClientWithDatastore(p, ds.NewMapDatastore()) } -func (rs *s) ClientWithDatastore(p peer.Peer, datastore ds.Datastore) Client { +func (rs *s) ClientWithDatastore(p peer.PeerInfo, datastore ds.Datastore) Client { return &client{ peer: p, datastore: ds.NewMapDatastore(), diff --git a/routing/routing.go b/routing/routing.go index 09773f20b..ae9acad44 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -16,7 +16,7 @@ var ErrNotFound = errors.New("routing: not found") // IpfsRouting is the routing module interface // It is implemented by things like DHTs, etc. type IpfsRouting interface { - FindProvidersAsync(context.Context, u.Key, int) <-chan peer.Peer + FindProvidersAsync(context.Context, u.Key, int) <-chan peer.PeerInfo // Basic Put/Get @@ -33,6 +33,7 @@ type IpfsRouting interface { Provide(context.Context, u.Key) error // Find specific Peer - // FindPeer searches for a peer with given ID. - FindPeer(context.Context, peer.ID) (peer.Peer, error) + // FindPeer searches for a peer with given ID, returns a peer.PeerInfo + // with relevant addresses. + FindPeer(context.Context, peer.ID) (peer.PeerInfo, error) } From 979cfecd448373696d0475f1a3b3e05c424d2a28 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 19 Dec 2014 12:19:56 -0800 Subject: [PATCH 0531/3817] peer change: peer.Peer -> peer.ID this is a major refactor of the entire codebase it changes the monolithic peer.Peer into using a peer.ID and a peer.Peerstore. Other changes: - removed handshake3. - testutil vastly simplified peer - secio bugfix + debugging logs - testutil: RandKeyPair - backpressure bugfix: w.o.w. - peer: added hex enc/dec - peer: added a PeerInfo struct PeerInfo is a small struct used to pass around a peer with a set of addresses and keys. This is not meant to be a complete view of the system, but rather to model updates to the peerstore. It is used by things like the routing system. - updated peer/queue + peerset - latency metrics - testutil: use crand for PeerID gen RandPeerID generates random "valid" peer IDs. it does not NEED to generate keys because it is as if we lost the key right away. fine to read some randomness and hash it. to generate proper keys and an ID, use: sk, pk, _ := testutil.RandKeyPair() id, _ := peer.IDFromPublicKey(pk) Also added RandPeerIDFatal helper - removed old spipe - updated seccat - core: cleanup initIdentity - removed old getFromPeerList This commit was moved from ipfs/go-namesys@3a3248e68708568c5d6f61c2b8b98c0ba948205c --- namesys/resolve_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 1d487f9a7..fb29490f3 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -4,14 +4,18 @@ import ( "testing" ci "github.com/jbenet/go-ipfs/crypto" + peer "github.com/jbenet/go-ipfs/peer" mockrouting "github.com/jbenet/go-ipfs/routing/mock" u "github.com/jbenet/go-ipfs/util" testutil "github.com/jbenet/go-ipfs/util/testutil" ) func TestRoutingResolve(t *testing.T) { - local := testutil.NewPeerWithIDString("testID") - d := mockrouting.NewServer().Client(local) + local, err := testutil.RandPeerID() + if err != nil { + t.Fatal(err) + } + d := mockrouting.NewServer().Client(peer.PeerInfo{ID: local}) resolver := NewRoutingResolver(d) publisher := NewRoutingPublisher(d) From 6123cdca614dff0d02d8ec0871b67985c6721760 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 22 Dec 2014 15:11:17 -0800 Subject: [PATCH 0532/3817] routing/mock test: kill leaked goroutine This commit was moved from ipfs/go-ipfs-routing@67ea1c81195361fed3bff7b458c8df6ecbd5e8a2 --- routing/mock/mockrouting_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/routing/mock/mockrouting_test.go b/routing/mock/mockrouting_test.go index 64540a3bc..739edbc63 100644 --- a/routing/mock/mockrouting_test.go +++ b/routing/mock/mockrouting_test.go @@ -96,10 +96,23 @@ func TestCanceledContext(t *testing.T) { rs := NewServer() k := u.Key("hello") + // avoid leaking goroutine, without using the context to signal + // (we want the goroutine to keep trying to publish on a + // cancelled context until we've tested it doesnt do anything.) + done := make(chan struct{}) + defer func() { done <- struct{}{} }() + t.Log("async'ly announce infinite stream of providers for key") i := 0 go func() { // infinite stream for { + select { + case <-done: + t.Log("exiting async worker") + return + default: + } + pi := peer.PeerInfo{ID: peer.ID(i)} err := rs.Client(pi).Provide(context.Background(), k) if err != nil { From e07aa39cb7ccd4335760bcdf79cb068fdb535dfd Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 22 Dec 2014 23:52:21 -0800 Subject: [PATCH 0533/3817] dht: bit nicer logging This commit was moved from ipfs/go-ipfs-routing@59856daf57f9a809481c056046216eda7d9ff7de --- routing/dht/dht.go | 2 +- routing/dht/handlers.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 2cb680140..fcc0f3bf0 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -126,7 +126,7 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, key string) erro return err } - log.Debugf("%s putProvider: %s for %s", dht.self, p, u.Key(key)) + log.Debugf("%s putProvider: %s for %s (%s)", dht.self, p, u.Key(key), pi.Addrs) return nil } diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index e9ffd7d7f..070f320a9 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -219,7 +219,7 @@ func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.M } if len(pi.Addrs) < 1 { - log.Errorf("got no valid addresses for provider %s. Ignore.", p) + log.Errorf("%s got no valid addresses for provider %s. Ignore.", dht.self, p) continue } From e82d58dc6bdb1ac2eb6e09ed71631ddf0a00bd7b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 18 Dec 2014 12:23:09 -0800 Subject: [PATCH 0534/3817] added bootstrap logging This commit was moved from ipfs/go-ipfs-routing@aba3dab59925db98333c6b723e005782391db046 --- routing/dht/dht.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index fcc0f3bf0..a893e62b8 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -77,6 +77,11 @@ func NewDHT(ctx context.Context, p peer.ID, n inet.Network, dstore ds.ThreadSafe return dht } +// LocalPeer returns the peer.Peer of the dht. +func (dht *IpfsDHT) LocalPeer() peer.ID { + return dht.self +} + // Connect to a new peer at the given address, ping and add to the routing table func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.ID) error { if err := dht.network.DialPeer(ctx, npeer); err != nil { From 0646ebd712b6fe777e270e5cecf4f6109e43a2c2 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 18 Dec 2014 18:50:13 -0500 Subject: [PATCH 0535/3817] remote low SnR debug statement cc @jbenet @whyrusleeping License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-merkledag@b07aac01231f444c1881fde4a80ca210f4f27661 --- ipld/merkledag/merkledag.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 3d8916b03..9a638ca2a 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -184,9 +184,7 @@ type dagService struct { // Add adds a node to the dagService, storing the block in the BlockService func (n *dagService) Add(nd *Node) (u.Key, error) { - k, _ := nd.Key() - log.Debugf("DagService Add [%s]", k) - if n == nil { + if n == nil { // FIXME remove this assertion. protect with constructor invariant return "", fmt.Errorf("dagService is nil") } From 51c142378bdc1fb8bfd440d46945825807431248 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 18 Dec 2014 18:51:29 -0500 Subject: [PATCH 0536/3817] rm low SnR debug statement "Get" is still fairly useful. Leaving it there. License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-blockservice@da268f18d7c917c9363435e653ceb0f0ac8da67d --- blockservice/blockservice.go | 1 - 1 file changed, 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 9014cab46..1a7ea6b7a 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -41,7 +41,6 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) (*BlockService, error // TODO pass a context into this if the remote.HasBlock is going to remain here. func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { k := b.Key() - log.Debugf("blockservice: storing [%s] in datastore", k) err := s.Blockstore.Put(b) if err != nil { return k, err From 334c6d8e1531e1eac94adf5a68d6235dc8826eb9 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 23 Dec 2014 03:14:30 -0800 Subject: [PATCH 0537/3817] dht: helpful debugging for no closer peers This commit was moved from ipfs/go-ipfs-routing@280c31591d11a36d2b7ec4a9dde0f04e4217805d --- routing/dht/query.go | 7 +++++-- routing/dht/routing.go | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/routing/dht/query.go b/routing/dht/query.go index 1321b5193..c45fa239f 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -258,12 +258,15 @@ func (r *dhtQueryRunner) queryPeer(p peer.ID) { r.Unlock() r.cancel() // signal to everyone that we're done. - } else if res.closerPeers != nil { - log.Debugf("PEERS CLOSER -- worker for: %v", p) + } else if len(res.closerPeers) > 0 { + log.Debugf("PEERS CLOSER -- worker for: %v (%d closer peers)", p, len(res.closerPeers)) for _, next := range res.closerPeers { // add their addresses to the dialer's peerstore r.query.dialer.Peerstore().AddAddresses(next.ID, next.Addrs) r.addPeerToQuery(next.ID, p) + log.Debugf("PEERS CLOSER -- worker for: %v added %v (%v)", p, next.ID, next.Addrs) } + } else { + log.Debugf("QUERY worker for: %v - not found, and no closer peers.", p) } } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 9a0620581..c515324c5 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -139,6 +139,7 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, count int, peerOut chan peer.PeerInfo) { defer close(peerOut) + log.Debugf("%s FindProviders %s", dht.self, key) ps := pset.NewLimited(count) provs := dht.providers.GetProviders(ctx, key) From 44517471217b198cbe5f9a93fecbad451cb183a5 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 23 Dec 2014 08:16:05 -0500 Subject: [PATCH 0538/3817] refactor(bitswap) bitswap.Network now abstracts ipfs.Network + ipfs.Routing @jbenet @whyrusleeping the next commit will change bitswap.Network.FindProviders to only deal with IDs This commit was moved from ipfs/go-blockservice@805558624e70a265c90d9db16334e70fe393d7a9 --- blockservice/mock.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/blockservice/mock.go b/blockservice/mock.go index 277519746..57432178e 100644 --- a/blockservice/mock.go +++ b/blockservice/mock.go @@ -11,9 +11,8 @@ import ( // Mocks returns |n| connected mock Blockservices func Mocks(t *testing.T, n int) []*BlockService { - net := tn.VirtualNetwork(delay.Fixed(0)) - rs := mockrouting.NewServer() - sg := bitswap.NewSessionGenerator(net, rs) + net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(0)) + sg := bitswap.NewSessionGenerator(net) instances := sg.Instances(n) From 05653e2366c3991bc1f09adc7b3238601b1464a0 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 23 Dec 2014 18:40:19 -0800 Subject: [PATCH 0539/3817] dht_test large providers test This commit was moved from ipfs/go-ipfs-routing@0a4b46927f414517533698484ac0e5f394405f9e --- routing/dht/dht_test.go | 158 +++++++++++++++++++++++++++++++++++----- 1 file changed, 139 insertions(+), 19 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index b378675c6..3e17c71cd 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -2,7 +2,9 @@ package dht import ( "bytes" + "fmt" "sort" + "sync" "testing" "time" @@ -19,6 +21,17 @@ import ( testutil "github.com/jbenet/go-ipfs/util/testutil" ) +var testCaseValues = map[u.Key][]byte{} + +func init() { + testCaseValues["hello"] = []byte("world") + for i := 0; i < 100; i++ { + k := fmt.Sprintf("%d -- key", i) + v := fmt.Sprintf("%d -- value", i) + testCaseValues[u.Key(k)] = []byte(v) + } +} + func setupDHT(ctx context.Context, t *testing.T, addr ma.Multiaddr) *IpfsDHT { sk, pk, err := testutil.RandKeyPair(512) @@ -174,37 +187,144 @@ func TestProvides(t *testing.T) { connect(t, ctx, dhts[1], dhts[2]) connect(t, ctx, dhts[1], dhts[3]) - err := dhts[3].putLocal(u.Key("hello"), []byte("world")) - if err != nil { - t.Fatal(err) + for k, v := range testCaseValues { + t.Logf("adding local values for %s = %s", k, v) + err := dhts[3].putLocal(k, v) + if err != nil { + t.Fatal(err) + } + + bits, err := dhts[3].getLocal(k) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(bits, v) { + t.Fatal("didn't store the right bits (%s, %s)", k, v) + } } - bits, err := dhts[3].getLocal(u.Key("hello")) - if err != nil && bytes.Equal(bits, []byte("world")) { - t.Fatal(err) + for k, _ := range testCaseValues { + t.Logf("announcing provider for %s", k) + if err := dhts[3].Provide(ctx, k); err != nil { + t.Fatal(err) + } } - err = dhts[3].Provide(ctx, u.Key("hello")) - if err != nil { - t.Fatal(err) + // what is this timeout for? was 60ms before. + time.Sleep(time.Millisecond * 6) + + n := 0 + for k, _ := range testCaseValues { + n = (n + 1) % 3 + + t.Logf("getting providers for %s from %d", k, n) + ctxT, _ := context.WithTimeout(ctx, time.Second) + provchan := dhts[n].FindProvidersAsync(ctxT, k, 1) + + select { + case prov := <-provchan: + if prov.ID == "" { + t.Fatal("Got back nil provider") + } + if prov.ID != dhts[3].self { + t.Fatal("Got back wrong provider") + } + case <-ctxT.Done(): + t.Fatal("Did not get a provider back.") + } + } +} + +func TestProvidesMany(t *testing.T) { + t.Skip("this test doesn't work") + ctx := context.Background() + + nDHTs := 40 + _, _, dhts := setupDHTS(ctx, nDHTs, t) + defer func() { + for i := 0; i < nDHTs; i++ { + dhts[i].Close() + defer dhts[i].network.Close() + } + }() + + t.Logf("connecting %d dhts in a ring", nDHTs) + for i := 0; i < nDHTs; i++ { + connect(t, ctx, dhts[i], dhts[(i+1)%len(dhts)]) + } + + // t.Logf("bootstrapping them so they find each other", nDHTs) + // for _, dht := range dhts { + // bootstrap(t, ctx, dht) + // } + + d := 0 + for k, v := range testCaseValues { + d = (d + 1) % len(dhts) + dht := dhts[d] + + t.Logf("adding local values for %s = %s (on %s)", k, v, dht.self) + err := dht.putLocal(k, v) + if err != nil { + t.Fatal(err) + } + + bits, err := dht.getLocal(k) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(bits, v) { + t.Fatal("didn't store the right bits (%s, %s)", k, v) + } + + t.Logf("announcing provider for %s", k) + if err := dht.Provide(ctx, k); err != nil { + t.Fatal(err) + } } // what is this timeout for? was 60ms before. time.Sleep(time.Millisecond * 6) - ctxT, _ := context.WithTimeout(ctx, time.Second) - provchan := dhts[0].FindProvidersAsync(ctxT, u.Key("hello"), 1) + errchan := make(chan error) - select { - case prov := <-provchan: - if prov.ID == "" { - t.Fatal("Got back nil provider") + ctxT, _ := context.WithTimeout(ctx, 5*time.Second) + + var wg sync.WaitGroup + getProvider := func(dht *IpfsDHT, k u.Key) { + defer wg.Done() + + provchan := dht.FindProvidersAsync(ctxT, k, 1) + select { + case prov := <-provchan: + if prov.ID == "" { + errchan <- fmt.Errorf("Got back nil provider (%s at %s)", k, dht.self) + } else if prov.ID != dhts[3].self { + errchan <- fmt.Errorf("Got back wrong provider (%s at %s)", k, dht.self) + } + case <-ctxT.Done(): + errchan <- fmt.Errorf("Did not get a provider back (%s at %s)", k, dht.self) } - if prov.ID != dhts[3].self { - t.Fatal("Got back nil provider") + } + + for k, _ := range testCaseValues { + // everyone should be able to find it... + for _, dht := range dhts { + t.Logf("getting providers for %s at %s", k, dht.self) + wg.Add(1) + go getProvider(dht, k) } - case <-ctxT.Done(): - t.Fatal("Did not get a provider back.") + } + + // we need this because of printing errors + go func() { + wg.Wait() + close(errchan) + }() + + t.Logf("looking through errors") + for err := range errchan { + t.Error(err) } } From cf8b35aa616ebfe7e8d7b517cbb74617861caa97 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 23 Dec 2014 18:54:23 -0800 Subject: [PATCH 0540/3817] dht bugfix: unlock on print This commit was moved from ipfs/go-ipfs-routing@22e9835052225f4171182740354a4a914659d338 --- routing/kbucket/table.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index da4c6e720..aaaa57372 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -227,4 +227,5 @@ func (rt *RoutingTable) Print() { for i, p := range peers { fmt.Printf("%d) %s %s\n", i, p.Pretty(), rt.metrics.LatencyEWMA(p).String()) } + rt.tabLock.RUnlock() } From 3805290ca95f72794447c28be37abe7df5708215 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 23 Dec 2014 22:05:55 -0800 Subject: [PATCH 0541/3817] routing table: better printing (see bkts) This commit was moved from ipfs/go-ipfs-routing@6509dc170508aae656bec27795f9c130a5bc462c --- routing/kbucket/table.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index aaaa57372..bed7447a5 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -223,9 +223,16 @@ func (rt *RoutingTable) ListPeers() []peer.ID { func (rt *RoutingTable) Print() { fmt.Printf("Routing Table, bs = %d, Max latency = %d\n", rt.bucketsize, rt.maxLatency) rt.tabLock.RLock() - peers := rt.ListPeers() - for i, p := range peers { - fmt.Printf("%d) %s %s\n", i, p.Pretty(), rt.metrics.LatencyEWMA(p).String()) + + for i, b := range rt.Buckets { + fmt.Printf("\tbucket: %d\n", i) + + b.lk.RLock() + for e := b.list.Front(); e != nil; e = e.Next() { + p := e.Value.(peer.ID) + fmt.Printf("\t\t- %s %s\n", p.Pretty(), rt.metrics.LatencyEWMA(p).String()) + } + b.lk.RUnlock() } rt.tabLock.RUnlock() } From b63d63c8b2e9144256169546302109bd034cba48 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 23 Dec 2014 19:02:03 -0800 Subject: [PATCH 0542/3817] dht bootstrap err check fix + logging This commit was moved from ipfs/go-ipfs-routing@4d81d5b2452f92c01cd2ca9f8894ac9cb8fbb89a --- routing/dht/dht.go | 11 ++++++----- routing/dht/handlers.go | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index a893e62b8..0898fb3d3 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -372,13 +372,14 @@ func (dht *IpfsDHT) Bootstrap(ctx context.Context) { id := make([]byte, 16) rand.Read(id) pi, err := dht.FindPeer(ctx, peer.ID(id)) - if err != nil { - // NOTE: this is not an error. this is expected! + if err == routing.ErrNotFound { + // this isn't an error. this is precisely what we expect. + } else if err != nil { log.Errorf("Bootstrap peer error: %s", err) + } else { + // woah, we got a peer under a random id? it _cannot_ be valid. + log.Errorf("dht seemingly found a peer at a random bootstrap id (%s)...", pi) } - - // woah, we got a peer under a random id? it _cannot_ be valid. - log.Errorf("dht seemingly found a peer at a random bootstrap id (%s)...", pi) }() } wg.Wait() diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 070f320a9..5aec6c2ff 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -148,7 +148,7 @@ func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.ID, pmes *pb.Mess } if closest == nil { - log.Errorf("handleFindPeer: could not find anything.") + log.Debugf("handleFindPeer: could not find anything.") return resp, nil } From 39854f319b7a588d4bd16681d50863899434346b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 24 Dec 2014 02:13:38 -0800 Subject: [PATCH 0543/3817] dht/query: fix important panic Withe queries (particularly providers), it was possible to exit the query runner's Run BEFORE all its children were done, because the runner itself only listened to the context. This introduced the possibility of a panic (you can go check it out by running the TestProvidersMany test on dht_test in commits before this one). Thankfully, ctxgroup saved the day with almost _zero_ changes to the sync flow, and now we have the guarantee that the query runner will only exit if all its children are done. :heart: Conflicts: routing/dht/query.go This commit was moved from ipfs/go-ipfs-routing@a19b41b2e168311a294a374bf36e576be143a444 --- routing/dht/dht_net.go | 16 ++++++++---- routing/dht/query.go | 58 +++++++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index a91e0f53c..cfbc812a3 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -7,6 +7,7 @@ import ( inet "github.com/jbenet/go-ipfs/net" peer "github.com/jbenet/go-ipfs/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" + ctxutil "github.com/jbenet/go-ipfs/util/ctx" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" @@ -21,8 +22,10 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { defer s.Close() ctx := dht.Context() - r := ggio.NewDelimitedReader(s, inet.MessageSizeMax) - w := ggio.NewDelimitedWriter(s) + cr := ctxutil.NewReader(ctx, s) // ok to use. we defer close stream in this func + cw := ctxutil.NewWriter(ctx, s) // ok to use. we defer close stream in this func + r := ggio.NewDelimitedReader(cr, inet.MessageSizeMax) + w := ggio.NewDelimitedWriter(cw) mPeer := s.Conn().RemotePeer() // receive msg @@ -76,8 +79,10 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message } defer s.Close() - r := ggio.NewDelimitedReader(s, inet.MessageSizeMax) - w := ggio.NewDelimitedWriter(s) + cr := ctxutil.NewReader(ctx, s) // ok to use. we defer close stream in this func + cw := ctxutil.NewWriter(ctx, s) // ok to use. we defer close stream in this func + r := ggio.NewDelimitedReader(cr, inet.MessageSizeMax) + w := ggio.NewDelimitedWriter(cw) start := time.Now() @@ -113,7 +118,8 @@ func (dht *IpfsDHT) sendMessage(ctx context.Context, p peer.ID, pmes *pb.Message } defer s.Close() - w := ggio.NewDelimitedWriter(s) + cw := ctxutil.NewWriter(ctx, s) // ok to use. we defer close stream in this func + w := ggio.NewDelimitedWriter(cw) log.Debugf("%s writing", dht.self) if err := w.WriteMsg(pmes); err != nil { diff --git a/routing/dht/query.go b/routing/dht/query.go index c45fa239f..3d3f94091 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -12,6 +12,7 @@ import ( todoctr "github.com/jbenet/go-ipfs/util/todocounter" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" ) var maxQueryConcurrency = AlphaValue @@ -78,9 +79,8 @@ type dhtQueryRunner struct { // peersRemaining is a counter of peers remaining (toQuery + processing) peersRemaining todoctr.Counter - // context - ctx context.Context - cancel context.CancelFunc + // context group + cg ctxgroup.ContextGroup // result result *dhtQueryResult @@ -93,16 +93,13 @@ type dhtQueryRunner struct { } func newQueryRunner(ctx context.Context, q *dhtQuery) *dhtQueryRunner { - ctx, cancel := context.WithCancel(ctx) - return &dhtQueryRunner{ - ctx: ctx, - cancel: cancel, query: q, peersToQuery: queue.NewChanQueue(ctx, queue.NewXORDistancePQ(q.key)), peersRemaining: todoctr.NewSyncCounter(), peersSeen: peer.Set{}, rateLimit: make(chan struct{}, q.concurrency), + cg: ctxgroup.WithContext(ctx), } } @@ -120,11 +117,13 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { // add all the peers we got first. for _, p := range peers { - r.addPeerToQuery(p, "") // don't have access to self here... + r.addPeerToQuery(r.cg.Context(), p, "") // don't have access to self here... } // go do this thing. - go r.spawnWorkers() + // do it as a child func to make sure Run exits + // ONLY AFTER spawn workers has exited. + r.cg.AddChildFunc(r.spawnWorkers) // so workers are working. @@ -133,7 +132,7 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { select { case <-r.peersRemaining.Done(): - r.cancel() // ran all and nothing. cancel all outstanding workers. + r.cg.Close() r.RLock() defer r.RUnlock() @@ -141,10 +140,10 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { err = r.errs[0] } - case <-r.ctx.Done(): + case <-r.cg.Closed(): r.RLock() defer r.RUnlock() - err = r.ctx.Err() + err = r.cg.Context().Err() // collect the error. } if r.result != nil && r.result.success { @@ -154,7 +153,7 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { return nil, err } -func (r *dhtQueryRunner) addPeerToQuery(next peer.ID, benchmark peer.ID) { +func (r *dhtQueryRunner) addPeerToQuery(ctx context.Context, next peer.ID, benchmark peer.ID) { // if new peer is ourselves... if next == r.query.dialer.LocalPeer() { return @@ -186,37 +185,42 @@ func (r *dhtQueryRunner) addPeerToQuery(next peer.ID, benchmark peer.ID) { r.peersRemaining.Increment(1) select { case r.peersToQuery.EnqChan <- next: - case <-r.ctx.Done(): + case <-ctx.Done(): } } -func (r *dhtQueryRunner) spawnWorkers() { +func (r *dhtQueryRunner) spawnWorkers(parent ctxgroup.ContextGroup) { for { select { case <-r.peersRemaining.Done(): return - case <-r.ctx.Done(): + case <-r.cg.Closing(): return case p, more := <-r.peersToQuery.DeqChan: if !more { return // channel closed. } - log.Debugf("spawning worker for: %v\n", p) - go r.queryPeer(p) + log.Debugf("spawning worker for: %v", p) + + // do it as a child func to make sure Run exits + // ONLY AFTER spawn workers has exited. + parent.AddChildFunc(func(cg ctxgroup.ContextGroup) { + r.queryPeer(cg, p) + }) } } } -func (r *dhtQueryRunner) queryPeer(p peer.ID) { +func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { log.Debugf("spawned worker for: %v", p) // make sure we rate limit concurrency. select { case <-r.rateLimit: - case <-r.ctx.Done(): + case <-cg.Closing(): r.peersRemaining.Decrement(1) return } @@ -233,7 +237,7 @@ func (r *dhtQueryRunner) queryPeer(p peer.ID) { }() // make sure we're connected to the peer. - err := r.query.dialer.DialPeer(r.ctx, p) + err := r.query.dialer.DialPeer(cg.Context(), p) if err != nil { log.Debugf("ERROR worker for: %v -- err connecting: %v", p, err) r.Lock() @@ -243,7 +247,7 @@ func (r *dhtQueryRunner) queryPeer(p peer.ID) { } // finally, run the query against this peer - res, err := r.query.qfunc(r.ctx, p) + res, err := r.query.qfunc(cg.Context(), p) if err != nil { log.Debugf("ERROR worker for: %v %v", p, err) @@ -256,14 +260,20 @@ func (r *dhtQueryRunner) queryPeer(p peer.ID) { r.Lock() r.result = res r.Unlock() - r.cancel() // signal to everyone that we're done. + go r.cg.Close() // signal to everyone that we're done. + // must be async, as we're one of the children, and Close blocks. } else if len(res.closerPeers) > 0 { log.Debugf("PEERS CLOSER -- worker for: %v (%d closer peers)", p, len(res.closerPeers)) for _, next := range res.closerPeers { // add their addresses to the dialer's peerstore + conns := r.query.dialer.ConnsToPeer(next.ID) + if len(conns) == 0 { + log.Infof("PEERS CLOSER -- worker for %v FOUND NEW PEER: %s %s", p, next.ID, next.Addrs) + } + r.query.dialer.Peerstore().AddAddresses(next.ID, next.Addrs) - r.addPeerToQuery(next.ID, p) + r.addPeerToQuery(cg.Context(), next.ID, p) log.Debugf("PEERS CLOSER -- worker for: %v added %v (%v)", p, next.ID, next.Addrs) } } else { From bbf759b80a68d438a218a7fa07be51d7b8b91d15 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 23 Dec 2014 19:20:42 -0800 Subject: [PATCH 0544/3817] dht: update on every received message i made a separate function because we may want to update our routing table based on "closer peers". maybe not-- these could all be lies. This commit was moved from ipfs/go-ipfs-routing@d62fdf5fddea0fcaf0f805edcc98e1279ba15739 --- routing/dht/dht_net.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index cfbc812a3..caf0518c2 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -34,8 +34,9 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { log.Error("Error unmarshaling data") return } + // update the peer (on valid msgs only) - dht.Update(ctx, mPeer) + dht.updateFromMessage(ctx, mPeer, pmes) log.Event(ctx, "foo", dht.self, mPeer, pmes) @@ -103,6 +104,9 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message return nil, errors.New("no response to request") } + // update the peer (on valid msgs only) + dht.updateFromMessage(ctx, p, rpmes) + dht.peerstore.RecordLatency(p, time.Since(start)) log.Event(ctx, "dhtReceivedMessage", dht.self, p, rpmes) return rpmes, nil @@ -129,3 +133,8 @@ func (dht *IpfsDHT) sendMessage(ctx context.Context, p peer.ID, pmes *pb.Message log.Debugf("%s done", dht.self) return nil } + +func (dht *IpfsDHT) updateFromMessage(ctx context.Context, p peer.ID, mes *pb.Message) error { + dht.Update(ctx, p) + return nil +} From 22281e147eb14d030a5f5e4ada987478fd66fd5d Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 23 Dec 2014 19:05:41 -0800 Subject: [PATCH 0545/3817] bootstrap test This commit was moved from ipfs/go-ipfs-routing@4296ca59ab5838511e7b07bba29610133d352efb --- routing/dht/dht_test.go | 48 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 3e17c71cd..02950e084 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -91,6 +91,18 @@ func connect(t *testing.T, ctx context.Context, a, b *IpfsDHT) { } } +func bootstrap(t *testing.T, ctx context.Context, dhts []*IpfsDHT) { + var wg sync.WaitGroup + for _, dht := range dhts { + wg.Add(1) + go func() { + defer wg.Done() + dht.Bootstrap(ctx) + }() + } + wg.Wait() +} + func TestPing(t *testing.T) { // t.Skip("skipping test to debug another") ctx := context.Background() @@ -235,8 +247,38 @@ func TestProvides(t *testing.T) { } } +func TestBootstrap(t *testing.T) { + ctx := context.Background() + + nDHTs := 40 + _, _, dhts := setupDHTS(ctx, nDHTs, t) + defer func() { + for i := 0; i < nDHTs; i++ { + dhts[i].Close() + defer dhts[i].network.Close() + } + }() + + t.Logf("connecting %d dhts in a ring", nDHTs) + for i := 0; i < nDHTs; i++ { + connect(t, ctx, dhts[i], dhts[(i+1)%len(dhts)]) + } + + t.Logf("bootstrapping them so they find each other", nDHTs) + bootstrap(t, ctx, dhts) + + // the routing tables should be full now. let's inspect them. + t.Logf("checking routing table of %d", nDHTs) + for _, dht := range dhts { + fmt.Printf("checking routing table of %s\n", dht.self) + dht.routingTable.Print() + fmt.Println("") + } +} + func TestProvidesMany(t *testing.T) { t.Skip("this test doesn't work") + // t.Skip("skipping test to debug another") ctx := context.Background() nDHTs := 40 @@ -253,10 +295,8 @@ func TestProvidesMany(t *testing.T) { connect(t, ctx, dhts[i], dhts[(i+1)%len(dhts)]) } - // t.Logf("bootstrapping them so they find each other", nDHTs) - // for _, dht := range dhts { - // bootstrap(t, ctx, dht) - // } + t.Logf("bootstrapping them so they find each other", nDHTs) + bootstrap(t, ctx, dhts) d := 0 for k, v := range testCaseValues { From 31eb5af868972177227a6b46b612ebd629373fc6 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 24 Dec 2014 03:24:28 -0800 Subject: [PATCH 0546/3817] respect don contexteone This commit was moved from ipfs/go-ipfs-routing@1a344cc5bd97deb1395d923c5f6f18281ad8eb0d --- routing/dht/dht_net.go | 2 +- routing/dht/dht_test.go | 3 ++- routing/dht/ext_test.go | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index caf0518c2..d247cf3af 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -31,7 +31,7 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { // receive msg pmes := new(pb.Message) if err := r.ReadMsg(pmes); err != nil { - log.Error("Error unmarshaling data") + log.Errorf("Error unmarshaling data: %s", err) return } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 02950e084..f2ff099df 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -265,7 +265,8 @@ func TestBootstrap(t *testing.T) { } t.Logf("bootstrapping them so they find each other", nDHTs) - bootstrap(t, ctx, dhts) + ctxT, _ := context.WithTimeout(ctx, 5*time.Second) + bootstrap(t, ctxT, dhts) // the routing tables should be full now. let's inspect them. t.Logf("checking routing table of %d", nDHTs) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 04f5111a9..b4b1158d7 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -73,7 +73,7 @@ func TestGetFailures(t *testing.T) { }) // This one should fail with NotFound - ctx2, _ := context.WithTimeout(context.Background(), time.Second) + ctx2, _ := context.WithTimeout(context.Background(), 3*time.Second) _, err = d.GetValue(ctx2, u.Key("test")) if err != nil { if err != routing.ErrNotFound { From d4a24062aba5828bb6fc4b2320f014f4ff5765b4 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 23 Dec 2014 19:21:35 -0800 Subject: [PATCH 0547/3817] dht bootstrap test: rounds. do nothing odd behavior: only one dht (the last one) is seeing changes to its routing table. This commit was moved from ipfs/go-ipfs-routing@76efc958ff6b819c550bd6dbc400547cc883d34f --- routing/dht/dht_test.go | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index f2ff099df..3c3ce9954 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -92,15 +92,23 @@ func connect(t *testing.T, ctx context.Context, a, b *IpfsDHT) { } func bootstrap(t *testing.T, ctx context.Context, dhts []*IpfsDHT) { - var wg sync.WaitGroup - for _, dht := range dhts { - wg.Add(1) - go func() { - defer wg.Done() - dht.Bootstrap(ctx) - }() + + // try multiple rounds... + rounds := 5 + for i := 0; i < rounds; i++ { + fmt.Printf("bootstrapping round %d/%d\n", i, rounds) + + var wg sync.WaitGroup + for _, dht := range dhts { + wg.Add(1) + go func() { + defer wg.Done() + dht.Bootstrap(ctx) + }() + } + wg.Wait() + } - wg.Wait() } func TestPing(t *testing.T) { @@ -264,6 +272,8 @@ func TestBootstrap(t *testing.T) { connect(t, ctx, dhts[i], dhts[(i+1)%len(dhts)]) } + <-time.After(100 * time.Millisecond) + t.Logf("bootstrapping them so they find each other", nDHTs) ctxT, _ := context.WithTimeout(ctx, 5*time.Second) bootstrap(t, ctxT, dhts) From 4d622b307554c6312c9a67cad336e2c18c5a1865 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 23 Dec 2014 22:05:29 -0800 Subject: [PATCH 0548/3817] dht_test: better bootstrapping logging This commit was moved from ipfs/go-ipfs-routing@fb8a936f44418ed360852dd8c9a7def95fd544f3 --- routing/dht/dht_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 3c3ce9954..d3041f364 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -94,17 +94,19 @@ func connect(t *testing.T, ctx context.Context, a, b *IpfsDHT) { func bootstrap(t *testing.T, ctx context.Context, dhts []*IpfsDHT) { // try multiple rounds... - rounds := 5 + rounds := 1 for i := 0; i < rounds; i++ { fmt.Printf("bootstrapping round %d/%d\n", i, rounds) var wg sync.WaitGroup for _, dht := range dhts { wg.Add(1) - go func() { + go func(i int) { defer wg.Done() + <-time.After(time.Duration(i) * time.Millisecond) // stagger them to avoid overwhelming + fmt.Printf("bootstrapping round %d/%d -- %s\n", i, rounds, dht.self) dht.Bootstrap(ctx) - }() + }(i) } wg.Wait() From 8d3b524e8bc67f611fc497c4ed62022c3ba25c4c Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 23 Dec 2014 22:01:57 -0800 Subject: [PATCH 0549/3817] dht: removing extra newlines This commit was moved from ipfs/go-ipfs-routing@c3eaf2dda8241490fb2aa9fb79f8004497c20d7e --- routing/dht/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/query.go b/routing/dht/query.go index 3d3f94091..aff724bc9 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -179,7 +179,7 @@ func (r *dhtQueryRunner) addPeerToQuery(ctx context.Context, next peer.ID, bench r.peersSeen[next] = struct{}{} r.Unlock() - log.Debugf("adding peer to query: %v\n", next) + log.Debugf("adding peer to query: %v", next) // do this after unlocking to prevent possible deadlocks. r.peersRemaining.Increment(1) From e9caf488d172d45145ca3fb3bb95878689bd9944 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 23 Dec 2014 22:04:21 -0800 Subject: [PATCH 0550/3817] dht: bootstrap query constants This commit was moved from ipfs/go-ipfs-routing@bc828f753bb6389dc169822647de822ac7325503 --- routing/dht/dht.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 0898fb3d3..fdb5170f7 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -28,6 +28,10 @@ var log = eventlog.Logger("dht") const doPinging = false +// NumBootstrapQueries defines the number of random dht queries to do to +// collect members of the routing table. +const NumBootstrapQueries = 5 + // TODO. SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-dht/index.js // IpfsDHT is an implementation of Kademlia with Coral and S/Kademlia modifications. @@ -364,7 +368,7 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { func (dht *IpfsDHT) Bootstrap(ctx context.Context) { var wg sync.WaitGroup - for i := 0; i < 10; i++ { + for i := 0; i < NumBootstrapQueries; i++ { wg.Add(1) go func() { defer wg.Done() From c1017863f9d5ffb0baa32cd3b338c0959c9b5e76 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 23 Dec 2014 22:06:45 -0800 Subject: [PATCH 0551/3817] dht/query: log when dialing a closerpeer This commit was moved from ipfs/go-ipfs-routing@8b4b0b8456434f5b327f052b1af244cab35df361 --- routing/dht/query.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/routing/dht/query.go b/routing/dht/query.go index aff724bc9..6a7bb687d 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -237,13 +237,18 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { }() // make sure we're connected to the peer. - err := r.query.dialer.DialPeer(cg.Context(), p) - if err != nil { - log.Debugf("ERROR worker for: %v -- err connecting: %v", p, err) - r.Lock() - r.errs = append(r.errs, err) - r.Unlock() - return + if conns := r.query.dialer.ConnsToPeer(p); len(conns) == 0 { + log.Infof("worker for: %v -- not connected. dial start", p) + + if err := r.query.dialer.DialPeer(cg.Context(), p); err != nil { + log.Debugf("ERROR worker for: %v -- err connecting: %v", p, err) + r.Lock() + r.errs = append(r.errs, err) + r.Unlock() + return + } + + log.Infof("worker for: %v -- not connected. dial success!", p) } // finally, run the query against this peer From ca7af7bcbbd18d5150587d4654eeca185c23d576 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 23 Dec 2014 23:12:22 -0800 Subject: [PATCH 0552/3817] dht/dht_test: bootstrap synchronously. fares better. This commit was moved from ipfs/go-ipfs-routing@4a051a54d3a6ab5ca9a33c6de44e22ba2254b27a --- routing/dht/dht.go | 32 +++++++++++++------------------- routing/dht/dht_test.go | 39 ++++++++++++++++++++++++--------------- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index fdb5170f7..4cbf68e43 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -365,26 +365,20 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { } // Bootstrap builds up list of peers by requesting random peer IDs -func (dht *IpfsDHT) Bootstrap(ctx context.Context) { +func (dht *IpfsDHT) Bootstrap(ctx context.Context, queries int) { - var wg sync.WaitGroup + // bootstrap sequentially, as results will compound for i := 0; i < NumBootstrapQueries; i++ { - wg.Add(1) - go func() { - defer wg.Done() - - id := make([]byte, 16) - rand.Read(id) - pi, err := dht.FindPeer(ctx, peer.ID(id)) - if err == routing.ErrNotFound { - // this isn't an error. this is precisely what we expect. - } else if err != nil { - log.Errorf("Bootstrap peer error: %s", err) - } else { - // woah, we got a peer under a random id? it _cannot_ be valid. - log.Errorf("dht seemingly found a peer at a random bootstrap id (%s)...", pi) - } - }() + id := make([]byte, 16) + rand.Read(id) + pi, err := dht.FindPeer(ctx, peer.ID(id)) + if err == routing.ErrNotFound { + // this isn't an error. this is precisely what we expect. + } else if err != nil { + log.Errorf("Bootstrap peer error: %s", err) + } else { + // woah, we got a peer under a random id? it _cannot_ be valid. + log.Errorf("dht seemingly found a peer at a random bootstrap id (%s)...", pi) + } } - wg.Wait() } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index d3041f364..f37656c2e 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -93,24 +93,23 @@ func connect(t *testing.T, ctx context.Context, a, b *IpfsDHT) { func bootstrap(t *testing.T, ctx context.Context, dhts []*IpfsDHT) { - // try multiple rounds... + ctx, cancel := context.WithCancel(ctx) + rounds := 1 for i := 0; i < rounds; i++ { fmt.Printf("bootstrapping round %d/%d\n", i, rounds) - var wg sync.WaitGroup + // tried async. sequential fares much better. compare: + // 100 async https://gist.github.com/jbenet/56d12f0578d5f34810b2 + // 100 sync https://gist.github.com/jbenet/6c59e7c15426e48aaedd + // probably because results compound for _, dht := range dhts { - wg.Add(1) - go func(i int) { - defer wg.Done() - <-time.After(time.Duration(i) * time.Millisecond) // stagger them to avoid overwhelming - fmt.Printf("bootstrapping round %d/%d -- %s\n", i, rounds, dht.self) - dht.Bootstrap(ctx) - }(i) + fmt.Printf("bootstrapping round %d/%d -- %s\n", i, rounds, dht.self) + dht.Bootstrap(ctx, 3) } - wg.Wait() - } + + cancel() } func TestPing(t *testing.T) { @@ -260,7 +259,7 @@ func TestProvides(t *testing.T) { func TestBootstrap(t *testing.T) { ctx := context.Background() - nDHTs := 40 + nDHTs := 10 _, _, dhts := setupDHTS(ctx, nDHTs, t) defer func() { for i := 0; i < nDHTs; i++ { @@ -275,7 +274,6 @@ func TestBootstrap(t *testing.T) { } <-time.After(100 * time.Millisecond) - t.Logf("bootstrapping them so they find each other", nDHTs) ctxT, _ := context.WithTimeout(ctx, 5*time.Second) bootstrap(t, ctxT, dhts) @@ -308,8 +306,19 @@ func TestProvidesMany(t *testing.T) { connect(t, ctx, dhts[i], dhts[(i+1)%len(dhts)]) } + <-time.After(100 * time.Millisecond) t.Logf("bootstrapping them so they find each other", nDHTs) - bootstrap(t, ctx, dhts) + ctxT, _ := context.WithTimeout(ctx, 5*time.Second) + bootstrap(t, ctxT, dhts) + + <-time.After(5 * time.Second) + // the routing tables should be full now. let's inspect them. + t.Logf("checking routing table of %d", nDHTs) + for _, dht := range dhts { + fmt.Printf("checking routing table of %s\n", dht.self) + dht.routingTable.Print() + fmt.Println("") + } d := 0 for k, v := range testCaseValues { @@ -341,7 +350,7 @@ func TestProvidesMany(t *testing.T) { errchan := make(chan error) - ctxT, _ := context.WithTimeout(ctx, 5*time.Second) + ctxT, _ = context.WithTimeout(ctx, 5*time.Second) var wg sync.WaitGroup getProvider := func(dht *IpfsDHT, k u.Key) { From cc014723f576a0275237a362a99d675608bd02ed Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 24 Dec 2014 01:33:52 -0800 Subject: [PATCH 0553/3817] dht: cleaned up dht_test. TestProversMany still fails This commit was moved from ipfs/go-ipfs-routing@c29267e03c3e9711a19646a87f86fb24ee361d82 --- routing/dht/dht_test.go | 69 +++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index f37656c2e..438791fe2 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -17,6 +17,7 @@ import ( // ci "github.com/jbenet/go-ipfs/crypto" inet "github.com/jbenet/go-ipfs/net" peer "github.com/jbenet/go-ipfs/peer" + routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" testutil "github.com/jbenet/go-ipfs/util/testutil" ) @@ -97,14 +98,14 @@ func bootstrap(t *testing.T, ctx context.Context, dhts []*IpfsDHT) { rounds := 1 for i := 0; i < rounds; i++ { - fmt.Printf("bootstrapping round %d/%d\n", i, rounds) + log.Debugf("bootstrapping round %d/%d\n", i, rounds) // tried async. sequential fares much better. compare: // 100 async https://gist.github.com/jbenet/56d12f0578d5f34810b2 // 100 sync https://gist.github.com/jbenet/6c59e7c15426e48aaedd // probably because results compound for _, dht := range dhts { - fmt.Printf("bootstrapping round %d/%d -- %s\n", i, rounds, dht.self) + log.Debugf("bootstrapping round %d/%d -- %s\n", i, rounds, dht.self) dht.Bootstrap(ctx, 3) } } @@ -209,7 +210,7 @@ func TestProvides(t *testing.T) { connect(t, ctx, dhts[1], dhts[3]) for k, v := range testCaseValues { - t.Logf("adding local values for %s = %s", k, v) + log.Debugf("adding local values for %s = %s", k, v) err := dhts[3].putLocal(k, v) if err != nil { t.Fatal(err) @@ -225,7 +226,7 @@ func TestProvides(t *testing.T) { } for k, _ := range testCaseValues { - t.Logf("announcing provider for %s", k) + log.Debugf("announcing provider for %s", k) if err := dhts[3].Provide(ctx, k); err != nil { t.Fatal(err) } @@ -238,7 +239,7 @@ func TestProvides(t *testing.T) { for k, _ := range testCaseValues { n = (n + 1) % 3 - t.Logf("getting providers for %s from %d", k, n) + log.Debugf("getting providers for %s from %d", k, n) ctxT, _ := context.WithTimeout(ctx, time.Second) provchan := dhts[n].FindProvidersAsync(ctxT, k, 1) @@ -259,7 +260,7 @@ func TestProvides(t *testing.T) { func TestBootstrap(t *testing.T) { ctx := context.Background() - nDHTs := 10 + nDHTs := 15 _, _, dhts := setupDHTS(ctx, nDHTs, t) defer func() { for i := 0; i < nDHTs; i++ { @@ -278,12 +279,23 @@ func TestBootstrap(t *testing.T) { ctxT, _ := context.WithTimeout(ctx, 5*time.Second) bootstrap(t, ctxT, dhts) - // the routing tables should be full now. let's inspect them. - t.Logf("checking routing table of %d", nDHTs) + if u.Debug { + // the routing tables should be full now. let's inspect them. + <-time.After(5 * time.Second) + t.Logf("checking routing table of %d", nDHTs) + for _, dht := range dhts { + fmt.Printf("checking routing table of %s\n", dht.self) + dht.routingTable.Print() + fmt.Println("") + } + } + + // test "well-formed-ness" (>= 3 peers in every routing table) for _, dht := range dhts { - fmt.Printf("checking routing table of %s\n", dht.self) - dht.routingTable.Print() - fmt.Println("") + rtlen := dht.routingTable.Size() + if rtlen < 4 { + t.Errorf("routing table for %s only has %d peers", dht.self, rtlen) + } } } @@ -311,13 +323,15 @@ func TestProvidesMany(t *testing.T) { ctxT, _ := context.WithTimeout(ctx, 5*time.Second) bootstrap(t, ctxT, dhts) - <-time.After(5 * time.Second) - // the routing tables should be full now. let's inspect them. - t.Logf("checking routing table of %d", nDHTs) - for _, dht := range dhts { - fmt.Printf("checking routing table of %s\n", dht.self) - dht.routingTable.Print() - fmt.Println("") + if u.Debug { + // the routing tables should be full now. let's inspect them. + <-time.After(5 * time.Second) + t.Logf("checking routing table of %d", nDHTs) + for _, dht := range dhts { + fmt.Printf("checking routing table of %s\n", dht.self) + dht.routingTable.Print() + fmt.Println("") + } } d := 0 @@ -372,7 +386,7 @@ func TestProvidesMany(t *testing.T) { for k, _ := range testCaseValues { // everyone should be able to find it... for _, dht := range dhts { - t.Logf("getting providers for %s at %s", k, dht.self) + log.Debugf("getting providers for %s at %s", k, dht.self) wg.Add(1) go getProvider(dht, k) } @@ -384,7 +398,6 @@ func TestProvidesMany(t *testing.T) { close(errchan) }() - t.Logf("looking through errors") for err := range errchan { t.Error(err) } @@ -473,18 +486,20 @@ func TestLayeredGet(t *testing.T) { t.Fatal(err) } - time.Sleep(time.Millisecond * 60) + time.Sleep(time.Millisecond * 6) + t.Log("interface was changed. GetValue should not use providers.") ctxT, _ := context.WithTimeout(ctx, time.Second) val, err := dhts[0].GetValue(ctxT, u.Key("/v/hello")) - if err != nil { - t.Fatal(err) + if err != routing.ErrNotFound { + t.Error(err) } - - if string(val) != "world" { - t.Fatal("Got incorrect value.") + if string(val) == "world" { + t.Error("should not get value.") + } + if len(val) > 0 && string(val) != "world" { + t.Error("worse, there's a value and its not even the right one.") } - } func TestFindPeer(t *testing.T) { From c8fa6330e5ffbb1685c7396435f5e6cac726ad13 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 24 Dec 2014 04:23:15 -0800 Subject: [PATCH 0554/3817] dht/test: providers test id compare This commit was moved from ipfs/go-ipfs-routing@14ca849f95645b75ba56cf6454338164fde93305 --- routing/dht/dht_test.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 438791fe2..ddf3909c8 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -334,10 +334,13 @@ func TestProvidesMany(t *testing.T) { } } + var providers = map[u.Key]peer.ID{} + d := 0 for k, v := range testCaseValues { d = (d + 1) % len(dhts) dht := dhts[d] + providers[k] = dht.self t.Logf("adding local values for %s = %s (on %s)", k, v, dht.self) err := dht.putLocal(k, v) @@ -370,13 +373,17 @@ func TestProvidesMany(t *testing.T) { getProvider := func(dht *IpfsDHT, k u.Key) { defer wg.Done() + expected := providers[k] + provchan := dht.FindProvidersAsync(ctxT, k, 1) select { case prov := <-provchan: - if prov.ID == "" { + actual := prov.ID + if actual == "" { errchan <- fmt.Errorf("Got back nil provider (%s at %s)", k, dht.self) - } else if prov.ID != dhts[3].self { - errchan <- fmt.Errorf("Got back wrong provider (%s at %s)", k, dht.self) + } else if actual != expected { + errchan <- fmt.Errorf("Got back wrong provider (%s != %s) (%s at %s)", + expected, actual, k, dht.self) } case <-ctxT.Done(): errchan <- fmt.Errorf("Did not get a provider back (%s at %s)", k, dht.self) From bf609052a3d6b2fc4082af6a0a40b706a7b2b00c Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 24 Dec 2014 05:39:48 -0800 Subject: [PATCH 0555/3817] dht/test skip bootstrap test when short This commit was moved from ipfs/go-ipfs-routing@37d2cf1d49ce6a2a9452bdad2c94baeeac685467 --- routing/dht/dht_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index ddf3909c8..5603c4d5c 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -258,6 +258,10 @@ func TestProvides(t *testing.T) { } func TestBootstrap(t *testing.T) { + if testing.Short() { + t.SkipNow() + } + ctx := context.Background() nDHTs := 15 From c6e5ddae65dff582ccc0c619c98a40761ca483bc Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 23 Dec 2014 21:25:29 -0500 Subject: [PATCH 0556/3817] refactor(routing/mock) move files This commit was moved from ipfs/go-ipfs-routing@ea3d2cf60ce24bf731f014f730e9b0485d1907fd --- routing/mock/{client.go => centralized_client.go} | 0 routing/mock/{server.go => centralized_server.go} | 0 routing/mock/{mockrouting_test.go => centralized_test.go} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename routing/mock/{client.go => centralized_client.go} (100%) rename routing/mock/{server.go => centralized_server.go} (100%) rename routing/mock/{mockrouting_test.go => centralized_test.go} (100%) diff --git a/routing/mock/client.go b/routing/mock/centralized_client.go similarity index 100% rename from routing/mock/client.go rename to routing/mock/centralized_client.go diff --git a/routing/mock/server.go b/routing/mock/centralized_server.go similarity index 100% rename from routing/mock/server.go rename to routing/mock/centralized_server.go diff --git a/routing/mock/mockrouting_test.go b/routing/mock/centralized_test.go similarity index 100% rename from routing/mock/mockrouting_test.go rename to routing/mock/centralized_test.go From f5357e9bb80f37c39e75ca9deb3815a74d95a1b3 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 23 Dec 2014 21:36:01 -0500 Subject: [PATCH 0557/3817] fix(merkledag/test) panic! https://travis-ci.org/jbenet/go-ipfs/jobs/45000756 cc @whyrusleeping @jbenet lol this is starting to happen pretty often This commit was moved from ipfs/go-merkledag@2c739b6c0fc67f7acaae697b9cfb27d683b9b645 --- ipld/merkledag/merkledag_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index de887e5a8..55107f08b 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -159,5 +159,5 @@ func runBatchFetchTest(t *testing.T, root *Node) { }(i) } - wg.Done() + wg.Wait() } From 6e44106d9ce7ad4a3f3faa2e28a0f02d5f0919cf Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 17 Dec 2014 10:02:19 -0800 Subject: [PATCH 0558/3817] wip with DHT @whyrusleeping @jbenet this is a WIP with the DHT. wip License: MIT Signed-off-by: Brian Tiger Chow Conflicts: epictest/addcat_test.go exchange/bitswap/testnet/peernet.go exchange/bitswap/testutils.go routing/mock/centralized_server.go routing/mock/centralized_test.go routing/mock/interface.go fix(routing/mock) fill in function definition This commit was moved from ipfs/go-ipfs-routing@544e4796ce46f0c821357d89c5bb34026c00cbaf --- routing/dht/routing.go | 10 ++++++++ routing/mock/centralized_client.go | 10 ++++++-- routing/mock/centralized_server.go | 8 ++++--- routing/mock/centralized_test.go | 34 ++++++++++---------------- routing/mock/interface.go | 6 ++--- routing/mock/server2.go | 38 ++++++++++++++++++++++++++++++ 6 files changed, 76 insertions(+), 30 deletions(-) create mode 100644 routing/mock/server2.go diff --git a/routing/dht/routing.go b/routing/dht/routing.go index c515324c5..34108f076 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -1,6 +1,7 @@ package dht import ( + "math" "sync" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -127,6 +128,15 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { return nil } +// FindProviders searches until the context expires. +func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]peer.PeerInfo, error) { + var providers []peer.PeerInfo + for p := range dht.FindProvidersAsync(ctx, key, math.MaxInt32) { + providers = append(providers, p) + } + return providers, nil +} + // FindProvidersAsync is the same thing as FindProviders, but returns a channel. // Peers will be returned on the channel as soon as they are found, even before // the search query completes. diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 9be43b653..0ba4be538 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -5,9 +5,11 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" peer "github.com/jbenet/go-ipfs/peer" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" + "github.com/jbenet/go-ipfs/util/testutil" ) var log = u.Logger("mockrouter") @@ -15,7 +17,7 @@ var log = u.Logger("mockrouter") type client struct { datastore ds.Datastore server server - peer peer.PeerInfo + peer testutil.Peer } // FIXME(brian): is this method meant to simulate putting a value into the network? @@ -70,7 +72,11 @@ func (c *client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-cha // Provide returns once the message is on the network. Value is not necessarily // visible yet. func (c *client) Provide(_ context.Context, key u.Key) error { - return c.server.Announce(c.peer, key) + info := peer.PeerInfo{ + ID: c.peer.ID(), + Addrs: []ma.Multiaddr{c.peer.Address()}, + } + return c.server.Announce(info, key) } var _ routing.IpfsRouting = &client{} diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 31ae4b730..10f81eb2c 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -5,9 +5,11 @@ import ( "sync" "time" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" + "github.com/jbenet/go-ipfs/util/testutil" ) // server is the mockrouting.Client's private interface to the routing server @@ -71,11 +73,11 @@ func (rs *s) Providers(k u.Key) []peer.PeerInfo { return ret } -func (rs *s) Client(p peer.PeerInfo) Client { - return rs.ClientWithDatastore(p, ds.NewMapDatastore()) +func (rs *s) Client(p testutil.Peer) Client { + return rs.ClientWithDatastore(context.Background(), p, ds.NewMapDatastore()) } -func (rs *s) ClientWithDatastore(p peer.PeerInfo, datastore ds.Datastore) Client { +func (rs *s) ClientWithDatastore(_ context.Context, p testutil.Peer, datastore ds.Datastore) Client { return &client{ peer: p, datastore: ds.NewMapDatastore(), diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 739edbc63..bda7ac004 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,11 +8,12 @@ import ( peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" delay "github.com/jbenet/go-ipfs/util/delay" + "github.com/jbenet/go-ipfs/util/testutil" ) func TestKeyNotFound(t *testing.T) { - var pi = peer.PeerInfo{ID: peer.ID("the peer id")} + var pi = testutil.RandPeerOrFatal(t) var key = u.Key("mock key") var ctx = context.Background() @@ -25,7 +26,7 @@ func TestKeyNotFound(t *testing.T) { } func TestClientFindProviders(t *testing.T) { - pi := peer.PeerInfo{ID: peer.ID("42")} + pi := testutil.RandPeerOrFatal(t) rs := NewServer() client := rs.Client(pi) @@ -39,20 +40,6 @@ func TestClientFindProviders(t *testing.T) { time.Sleep(time.Millisecond * 300) max := 100 - providersFromHashTable, err := rs.Client(pi).FindProviders(context.Background(), k) - if err != nil { - t.Fatal(err) - } - - isInHT := false - for _, pi := range providersFromHashTable { - if pi.ID == pi.ID { - isInHT = true - } - } - if !isInHT { - t.Fatal("Despite client providing key, peer wasn't in hash table as a provider") - } providersFromClient := client.FindProvidersAsync(context.Background(), u.Key("hello"), max) isInClient := false for pi := range providersFromClient { @@ -70,7 +57,7 @@ func TestClientOverMax(t *testing.T) { k := u.Key("hello") numProvidersForHelloKey := 100 for i := 0; i < numProvidersForHelloKey; i++ { - pi := peer.PeerInfo{ID: peer.ID(i)} + pi := testutil.RandPeerOrFatal(t) err := rs.Client(pi).Provide(context.Background(), k) if err != nil { t.Fatal(err) @@ -78,7 +65,7 @@ func TestClientOverMax(t *testing.T) { } max := 10 - pi := peer.PeerInfo{ID: peer.ID("TODO")} + pi := testutil.RandPeerOrFatal(t) client := rs.Client(pi) providersFromClient := client.FindProvidersAsync(context.Background(), k, max) @@ -113,8 +100,11 @@ func TestCanceledContext(t *testing.T) { default: } - pi := peer.PeerInfo{ID: peer.ID(i)} - err := rs.Client(pi).Provide(context.Background(), k) + pi, err := testutil.RandPeer() + if err != nil { + t.Error(err) + } + err = rs.Client(pi).Provide(context.Background(), k) if err != nil { t.Error(err) } @@ -122,7 +112,7 @@ func TestCanceledContext(t *testing.T) { } }() - local := peer.PeerInfo{ID: peer.ID("peer id doesn't matter")} + local := testutil.RandPeerOrFatal(t) client := rs.Client(local) t.Log("warning: max is finite so this test is non-deterministic") @@ -148,7 +138,7 @@ func TestCanceledContext(t *testing.T) { func TestValidAfter(t *testing.T) { - var pi = peer.PeerInfo{ID: peer.ID("the peer id")} + pi := testutil.RandPeerOrFatal(t) var key = u.Key("mock key") var ctx = context.Background() conf := DelayConfig{ diff --git a/routing/mock/interface.go b/routing/mock/interface.go index abb869eb4..3ff1ca059 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -11,18 +11,18 @@ import ( routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" delay "github.com/jbenet/go-ipfs/util/delay" + "github.com/jbenet/go-ipfs/util/testutil" ) // Server provides mockrouting Clients type Server interface { - Client(p peer.PeerInfo) Client - ClientWithDatastore(peer.PeerInfo, ds.Datastore) Client + Client(p testutil.Peer) Client + ClientWithDatastore(context.Context, testutil.Peer, ds.Datastore) Client } // Client implements IpfsRouting type Client interface { FindProviders(context.Context, u.Key) ([]peer.PeerInfo, error) - routing.IpfsRouting } diff --git a/routing/mock/server2.go b/routing/mock/server2.go new file mode 100644 index 000000000..dc3dccdfa --- /dev/null +++ b/routing/mock/server2.go @@ -0,0 +1,38 @@ +package mockrouting + +import ( + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + sync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + mocknet "github.com/jbenet/go-ipfs/net/mock" + dht "github.com/jbenet/go-ipfs/routing/dht" + "github.com/jbenet/go-ipfs/util/testutil" +) + +type mocknetserver struct { + mn mocknet.Mocknet +} + +func NewDHTNetwork(mn mocknet.Mocknet) Server { + return &mocknetserver{ + mn: mn, + } +} + +func (rs *mocknetserver) Client(p testutil.Peer) Client { + return rs.ClientWithDatastore(context.TODO(), p, ds.NewMapDatastore()) +} + +func (rs *mocknetserver) ClientWithDatastore(ctx context.Context, p testutil.Peer, ds ds.Datastore) Client { + + // FIXME AddPeer doesn't appear to be idempotent + + net, err := rs.mn.AddPeer(p.PrivateKey(), p.Address()) + if err != nil { + panic("FIXME") + // return nil, debugerror.Wrap(err) + } + return dht.NewDHT(ctx, p.ID(), net, sync.MutexWrap(ds)) +} + +var _ Server = &mocknetserver{} From a329f346cbc55a4356f295a36ee8b78e596ee796 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 17 Dec 2014 10:02:19 -0800 Subject: [PATCH 0559/3817] wip with DHT @whyrusleeping @jbenet this is a WIP with the DHT. wip License: MIT Signed-off-by: Brian Tiger Chow Conflicts: epictest/addcat_test.go exchange/bitswap/testnet/peernet.go exchange/bitswap/testutils.go routing/mock/centralized_server.go routing/mock/centralized_test.go routing/mock/interface.go fix(routing/mock) fill in function definition This commit was moved from ipfs/go-namesys@78638f20dbf5eb3ab660e7ec5f34b0db97cc2574 --- namesys/resolve_test.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index fb29490f3..74fb08982 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -4,18 +4,13 @@ import ( "testing" ci "github.com/jbenet/go-ipfs/crypto" - peer "github.com/jbenet/go-ipfs/peer" mockrouting "github.com/jbenet/go-ipfs/routing/mock" u "github.com/jbenet/go-ipfs/util" testutil "github.com/jbenet/go-ipfs/util/testutil" ) func TestRoutingResolve(t *testing.T) { - local, err := testutil.RandPeerID() - if err != nil { - t.Fatal(err) - } - d := mockrouting.NewServer().Client(peer.PeerInfo{ID: local}) + d := mockrouting.NewServer().Client(testutil.RandPeerOrFatal(t)) resolver := NewRoutingResolver(d) publisher := NewRoutingPublisher(d) From 8dc1bee3df4f1cf95c54275a2e14688f86d1e31d Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 24 Dec 2014 09:38:51 -0500 Subject: [PATCH 0560/3817] rename to dht This commit was moved from ipfs/go-ipfs-routing@536262cae3d1e00325591b3a167a56415ddf1f91 --- routing/mock/{server2.go => dht.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename routing/mock/{server2.go => dht.go} (100%) diff --git a/routing/mock/server2.go b/routing/mock/dht.go similarity index 100% rename from routing/mock/server2.go rename to routing/mock/dht.go From fdd8003e78ef390538b84a2e455cbe752d6524ff Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 24 Dec 2014 09:53:18 -0500 Subject: [PATCH 0561/3817] style(testutil) rename testutil.Peer -> testutil.Identity cc @jbenet This commit was moved from ipfs/go-ipfs-routing@9b3a1679f8b9d0423062ede291375c4965ccd8d9 --- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 4 ++-- routing/mock/centralized_test.go | 14 +++++++------- routing/mock/dht.go | 4 ++-- routing/mock/interface.go | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 0ba4be538..6b5a455a7 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -17,7 +17,7 @@ var log = u.Logger("mockrouter") type client struct { datastore ds.Datastore server server - peer testutil.Peer + peer testutil.Identity } // FIXME(brian): is this method meant to simulate putting a value into the network? diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 10f81eb2c..030227b1b 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -73,11 +73,11 @@ func (rs *s) Providers(k u.Key) []peer.PeerInfo { return ret } -func (rs *s) Client(p testutil.Peer) Client { +func (rs *s) Client(p testutil.Identity) Client { return rs.ClientWithDatastore(context.Background(), p, ds.NewMapDatastore()) } -func (rs *s) ClientWithDatastore(_ context.Context, p testutil.Peer, datastore ds.Datastore) Client { +func (rs *s) ClientWithDatastore(_ context.Context, p testutil.Identity, datastore ds.Datastore) Client { return &client{ peer: p, datastore: ds.NewMapDatastore(), diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index bda7ac004..dcaf165b1 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -13,7 +13,7 @@ import ( func TestKeyNotFound(t *testing.T) { - var pi = testutil.RandPeerOrFatal(t) + var pi = testutil.RandIdentityOrFatal(t) var key = u.Key("mock key") var ctx = context.Background() @@ -26,7 +26,7 @@ func TestKeyNotFound(t *testing.T) { } func TestClientFindProviders(t *testing.T) { - pi := testutil.RandPeerOrFatal(t) + pi := testutil.RandIdentityOrFatal(t) rs := NewServer() client := rs.Client(pi) @@ -57,7 +57,7 @@ func TestClientOverMax(t *testing.T) { k := u.Key("hello") numProvidersForHelloKey := 100 for i := 0; i < numProvidersForHelloKey; i++ { - pi := testutil.RandPeerOrFatal(t) + pi := testutil.RandIdentityOrFatal(t) err := rs.Client(pi).Provide(context.Background(), k) if err != nil { t.Fatal(err) @@ -65,7 +65,7 @@ func TestClientOverMax(t *testing.T) { } max := 10 - pi := testutil.RandPeerOrFatal(t) + pi := testutil.RandIdentityOrFatal(t) client := rs.Client(pi) providersFromClient := client.FindProvidersAsync(context.Background(), k, max) @@ -100,7 +100,7 @@ func TestCanceledContext(t *testing.T) { default: } - pi, err := testutil.RandPeer() + pi, err := testutil.RandIdentity() if err != nil { t.Error(err) } @@ -112,7 +112,7 @@ func TestCanceledContext(t *testing.T) { } }() - local := testutil.RandPeerOrFatal(t) + local := testutil.RandIdentityOrFatal(t) client := rs.Client(local) t.Log("warning: max is finite so this test is non-deterministic") @@ -138,7 +138,7 @@ func TestCanceledContext(t *testing.T) { func TestValidAfter(t *testing.T) { - pi := testutil.RandPeerOrFatal(t) + pi := testutil.RandIdentityOrFatal(t) var key = u.Key("mock key") var ctx = context.Background() conf := DelayConfig{ diff --git a/routing/mock/dht.go b/routing/mock/dht.go index dc3dccdfa..1dfa415e0 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -19,11 +19,11 @@ func NewDHTNetwork(mn mocknet.Mocknet) Server { } } -func (rs *mocknetserver) Client(p testutil.Peer) Client { +func (rs *mocknetserver) Client(p testutil.Identity) Client { return rs.ClientWithDatastore(context.TODO(), p, ds.NewMapDatastore()) } -func (rs *mocknetserver) ClientWithDatastore(ctx context.Context, p testutil.Peer, ds ds.Datastore) Client { +func (rs *mocknetserver) ClientWithDatastore(ctx context.Context, p testutil.Identity, ds ds.Datastore) Client { // FIXME AddPeer doesn't appear to be idempotent diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 3ff1ca059..0bb54f365 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -16,8 +16,8 @@ import ( // Server provides mockrouting Clients type Server interface { - Client(p testutil.Peer) Client - ClientWithDatastore(context.Context, testutil.Peer, ds.Datastore) Client + Client(p testutil.Identity) Client + ClientWithDatastore(context.Context, testutil.Identity, ds.Datastore) Client } // Client implements IpfsRouting From 3d3d978bb9890f9da3ac6110842d13a4b65263f6 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 24 Dec 2014 09:53:18 -0500 Subject: [PATCH 0562/3817] style(testutil) rename testutil.Peer -> testutil.Identity cc @jbenet This commit was moved from ipfs/go-namesys@2c0dec26f4e0bff6a1c1579ccff0dde5a599f705 --- namesys/resolve_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 74fb08982..84e4f1cb6 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -10,7 +10,7 @@ import ( ) func TestRoutingResolve(t *testing.T) { - d := mockrouting.NewServer().Client(testutil.RandPeerOrFatal(t)) + d := mockrouting.NewServer().Client(testutil.RandIdentityOrFatal(t)) resolver := NewRoutingResolver(d) publisher := NewRoutingPublisher(d) From 8d2411f03af2b33347b353f4571e7650510b8a67 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 14 Dec 2014 00:50:49 +0000 Subject: [PATCH 0563/3817] rewrite of provides to better select peers to send RPCs to refactor test peer creation to be deterministic and reliable a bit of cleanup trying to figure out TestGetFailure add test to verify deterministic peer creation switch put RPC over to use getClosestPeers rm 0xDEADC0DE fix queries not searching peer if its not actually closer This commit was moved from ipfs/go-ipfs-routing@04e9ae3375837ed683cadba30ca47cabff5fa932 --- routing/dht/dht.go | 33 +++------- routing/dht/dht_test.go | 19 +++--- routing/dht/ext_test.go | 7 +-- routing/dht/handlers.go | 11 ++-- routing/dht/query.go | 31 +++------ routing/dht/routing.go | 126 +++++++++++++++++++++++++++++++------ routing/kbucket/sorting.go | 59 +++++++++++++++++ routing/kbucket/table.go | 35 ----------- 8 files changed, 203 insertions(+), 118 deletions(-) create mode 100644 routing/kbucket/sorting.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 4cbf68e43..1573f3477 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -103,9 +103,8 @@ func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.ID) error { } // putValueToNetwork stores the given key/value pair at the peer 'p' -// meaning: it sends a PUT_VALUE message to p func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p peer.ID, - key string, rec *pb.Record) error { + key u.Key, rec *pb.Record) error { pmes := pb.NewMessage(pb.Message_PUT_VALUE, string(key), 0) pmes.Record = rec @@ -285,7 +284,7 @@ func (dht *IpfsDHT) nearestPeersToQuery(pmes *pb.Message, count int) []peer.ID { } // betterPeerToQuery returns nearestPeersToQuery, but iff closer than self. -func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, count int) []peer.ID { +func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, p peer.ID, count int) []peer.ID { closer := dht.nearestPeersToQuery(pmes, count) // no node? nil @@ -302,11 +301,16 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, count int) []peer.ID { } var filtered []peer.ID - for _, p := range closer { + for _, clp := range closer { + // Dont send a peer back themselves + if p == clp { + continue + } + // must all be closer than self key := u.Key(pmes.GetKey()) - if !kb.Closer(dht.self, p, key) { - filtered = append(filtered, p) + if !kb.Closer(dht.self, clp, key) { + filtered = append(filtered, clp) } } @@ -323,23 +327,6 @@ func (dht *IpfsDHT) ensureConnectedToPeer(ctx context.Context, p peer.ID) error return dht.network.DialPeer(ctx, p) } -//TODO: this should be smarter about which keys it selects. -func (dht *IpfsDHT) loadProvidableKeys() error { - kl, err := dht.datastore.KeyList() - if err != nil { - return err - } - for _, dsk := range kl { - k := u.KeyFromDsKey(dsk) - if len(k) == 0 { - log.Errorf("loadProvidableKeys error: %v", dsk) - } - - dht.providers.AddProvider(k, dht.self) - } - return nil -} - // PingRoutine periodically pings nearest neighbors. func (dht *IpfsDHT) PingRoutine(t time.Duration) { defer dht.Children().Done() diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 5603c4d5c..bbc7b9692 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -14,7 +14,6 @@ import ( dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - // ci "github.com/jbenet/go-ipfs/crypto" inet "github.com/jbenet/go-ipfs/net" peer "github.com/jbenet/go-ipfs/peer" routing "github.com/jbenet/go-ipfs/routing" @@ -33,9 +32,9 @@ func init() { } } -func setupDHT(ctx context.Context, t *testing.T, addr ma.Multiaddr) *IpfsDHT { +func setupDHT(ctx context.Context, t *testing.T, addr ma.Multiaddr, seed int64) *IpfsDHT { - sk, pk, err := testutil.RandKeyPair(512) + sk, pk, err := testutil.SeededKeyPair(512, seed) if err != nil { t.Fatal(err) } @@ -71,7 +70,7 @@ func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []peer for i := 0; i < n; i++ { addrs[i] = testutil.RandLocalTCPAddress() - dhts[i] = setupDHT(ctx, t, addrs[i]) + dhts[i] = setupDHT(ctx, t, addrs[i], int64(i)) peers[i] = dhts[i].self } @@ -120,8 +119,8 @@ func TestPing(t *testing.T) { addrA := testutil.RandLocalTCPAddress() addrB := testutil.RandLocalTCPAddress() - dhtA := setupDHT(ctx, t, addrA) - dhtB := setupDHT(ctx, t, addrB) + dhtA := setupDHT(ctx, t, addrA, 1) + dhtB := setupDHT(ctx, t, addrB, 2) peerA := dhtA.self peerB := dhtB.self @@ -153,8 +152,8 @@ func TestValueGetSet(t *testing.T) { addrA := testutil.RandLocalTCPAddress() addrB := testutil.RandLocalTCPAddress() - dhtA := setupDHT(ctx, t, addrA) - dhtB := setupDHT(ctx, t, addrB) + dhtA := setupDHT(ctx, t, addrA, 1) + dhtB := setupDHT(ctx, t, addrB, 2) defer dhtA.Close() defer dhtB.Close() @@ -642,8 +641,8 @@ func TestConnectCollision(t *testing.T) { addrA := testutil.RandLocalTCPAddress() addrB := testutil.RandLocalTCPAddress() - dhtA := setupDHT(ctx, t, addrA) - dhtB := setupDHT(ctx, t, addrB) + dhtA := setupDHT(ctx, t, addrA, int64((rtime*2)+1)) + dhtB := setupDHT(ctx, t, addrB, int64((rtime*2)+2)) peerA := dhtA.self peerB := dhtB.self diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index b4b1158d7..8441c1f72 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -47,9 +47,8 @@ func TestGetFailures(t *testing.T) { t.Fatal("Did not get expected error!") } - msgs := make(chan *pb.Message, 100) + t.Log("Timeout test passed.") - // u.POut("NotFound Test\n") // Reply with failures to every message nets[1].SetHandler(inet.ProtocolDHT, func(s inet.Stream) { defer s.Close() @@ -68,8 +67,6 @@ func TestGetFailures(t *testing.T) { if err := pbw.WriteMsg(resp); err != nil { panic(err) } - - msgs <- resp }) // This one should fail with NotFound @@ -83,6 +80,8 @@ func TestGetFailures(t *testing.T) { t.Fatal("expected error, got none.") } + t.Log("ErrNotFound check passed!") + // Now we test this DHT's handleGetValue failure { typ := pb.Message_GET_VALUE diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 5aec6c2ff..e8edaa5eb 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -93,7 +93,7 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Mess } // Find closest peer on given cluster to desired key and reply with that info - closer := dht.betterPeersToQuery(pmes, CloserPeerCount) + closer := dht.betterPeersToQuery(pmes, p, CloserPeerCount) closerinfos := peer.PeerInfos(dht.peerstore, closer) if closer != nil { for _, pi := range closerinfos { @@ -137,6 +137,9 @@ func (dht *IpfsDHT) handlePing(_ context.Context, p peer.ID, pmes *pb.Message) ( } func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { + log.Errorf("handle find peer %s start", p) + defer log.Errorf("handle find peer %s end", p) + resp := pb.NewMessage(pmes.GetType(), "", pmes.GetClusterLevel()) var closest []peer.ID @@ -144,11 +147,11 @@ func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.ID, pmes *pb.Mess if peer.ID(pmes.GetKey()) == dht.self { closest = []peer.ID{dht.self} } else { - closest = dht.betterPeersToQuery(pmes, CloserPeerCount) + closest = dht.betterPeersToQuery(pmes, p, CloserPeerCount) } if closest == nil { - log.Debugf("handleFindPeer: could not find anything.") + log.Warningf("handleFindPeer: could not find anything.") return resp, nil } @@ -189,7 +192,7 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb. } // Also send closer peers. - closer := dht.betterPeersToQuery(pmes, CloserPeerCount) + closer := dht.betterPeersToQuery(pmes, p, CloserPeerCount) if closer != nil { infos := peer.PeerInfos(dht.peerstore, providers) resp.CloserPeers = pb.PeerInfosToPBPeers(dht.network, infos) diff --git a/routing/dht/query.go b/routing/dht/query.go index 6a7bb687d..4790e814c 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -7,8 +7,8 @@ import ( peer "github.com/jbenet/go-ipfs/peer" queue "github.com/jbenet/go-ipfs/peer/queue" "github.com/jbenet/go-ipfs/routing" - kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" + pset "github.com/jbenet/go-ipfs/util/peerset" todoctr "github.com/jbenet/go-ipfs/util/todocounter" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -71,7 +71,7 @@ type dhtQueryRunner struct { peersToQuery *queue.ChanQueue // peersSeen are all the peers queried. used to prevent querying same peer 2x - peersSeen peer.Set + peersSeen *pset.PeerSet // rateLimit is a channel used to rate limit our processing (semaphore) rateLimit chan struct{} @@ -97,7 +97,7 @@ func newQueryRunner(ctx context.Context, q *dhtQuery) *dhtQueryRunner { query: q, peersToQuery: queue.NewChanQueue(ctx, queue.NewXORDistancePQ(q.key)), peersRemaining: todoctr.NewSyncCounter(), - peersSeen: peer.Set{}, + peersSeen: pset.New(), rateLimit: make(chan struct{}, q.concurrency), cg: ctxgroup.WithContext(ctx), } @@ -117,7 +117,7 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { // add all the peers we got first. for _, p := range peers { - r.addPeerToQuery(r.cg.Context(), p, "") // don't have access to self here... + r.addPeerToQuery(r.cg.Context(), p) } // go do this thing. @@ -153,32 +153,17 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { return nil, err } -func (r *dhtQueryRunner) addPeerToQuery(ctx context.Context, next peer.ID, benchmark peer.ID) { +func (r *dhtQueryRunner) addPeerToQuery(ctx context.Context, next peer.ID) { // if new peer is ourselves... if next == r.query.dialer.LocalPeer() { return } - // if new peer further away than whom we got it from, don't bother (loops) - // TODO----------- this benchmark should be replaced by a heap: - // we should be doing the s/kademlia "continue to search" - // (i.e. put all of them in a heap sorted by dht distance and then just - // pull from the the top until a) you exhaust all peers you get, - // b) you succeed, c) your context expires. - if benchmark != "" && kb.Closer(benchmark, next, r.query.key) { + if !r.peersSeen.TryAdd(next) { + log.Debug("query peer was already seen") return } - // if already seen, no need. - r.Lock() - _, found := r.peersSeen[next] - if found { - r.Unlock() - return - } - r.peersSeen[next] = struct{}{} - r.Unlock() - log.Debugf("adding peer to query: %v", next) // do this after unlocking to prevent possible deadlocks. @@ -278,7 +263,7 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { } r.query.dialer.Peerstore().AddAddresses(next.ID, next.Addrs) - r.addPeerToQuery(cg.Context(), next.ID, p) + r.addPeerToQuery(cg.Context(), next.ID) log.Debugf("PEERS CLOSER -- worker for: %v added %v (%v)", p, next.ID, next.Addrs) } } else { diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 34108f076..0cd5751a1 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -40,19 +40,24 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error return err } - peers := dht.routingTable.NearestPeers(kb.ConvertKey(key), KValue) - - query := newQuery(key, dht.network, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { - log.Debugf("%s PutValue qry part %v", dht.self, p) - err := dht.putValueToNetwork(ctx, p, string(key), rec) - if err != nil { - return nil, err - } - return &dhtQueryResult{success: true}, nil - }) + pchan, err := dht.getClosestPeers(ctx, key, KValue) + if err != nil { + return err + } - _, err = query.Run(ctx, peers) - return err + wg := sync.WaitGroup{} + for p := range pchan { + wg.Add(1) + go func(p peer.ID) { + defer wg.Done() + err := dht.putValueToNetwork(ctx, p, key, rec) + if err != nil { + log.Errorf("failed putting value to peer: %s", err) + } + }(p) + } + wg.Wait() + return nil } // GetValue searches for the value corresponding to given Key. @@ -111,18 +116,19 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { // Provide makes this node announce that it can provide a value for the given key func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { + log.Event(ctx, "Provide Value start", &key) + defer log.Event(ctx, "Provide Value end", &key) dht.providers.AddProvider(key, dht.self) - peers := dht.routingTable.NearestPeers(kb.ConvertKey(key), PoolSize) - if len(peers) == 0 { - return nil + + peers, err := dht.getClosestPeers(ctx, key, KValue) + if err != nil { + return err } - //TODO FIX: this doesn't work! it needs to be sent to the actual nearest peers. - // `peers` are the closest peers we have, not the ones that should get the value. - for _, p := range peers { + for p := range peers { err := dht.putProvider(ctx, p, string(key)) if err != nil { - return err + log.Error(err) } } return nil @@ -137,6 +143,87 @@ func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]peer.PeerIn return providers, nil } +func (dht *IpfsDHT) getClosestPeers(ctx context.Context, key u.Key, count int) (<-chan peer.ID, error) { + log.Error("Get Closest Peers") + tablepeers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) + if len(tablepeers) == 0 { + return nil, kb.ErrLookupFailure + } + + out := make(chan peer.ID, count) + peerset := pset.NewLimited(count) + + for _, p := range tablepeers { + out <- p + peerset.Add(p) + } + + wg := sync.WaitGroup{} + for _, p := range tablepeers { + wg.Add(1) + go func(p peer.ID) { + dht.getClosestPeersRecurse(ctx, key, p, peerset, out) + wg.Done() + }(p) + } + + go func() { + wg.Wait() + close(out) + log.Error("Closing closest peer chan") + }() + + return out, nil +} + +func (dht *IpfsDHT) getClosestPeersRecurse(ctx context.Context, key u.Key, p peer.ID, peers *pset.PeerSet, peerOut chan<- peer.ID) { + log.Error("closest peers recurse") + defer log.Error("closest peers recurse end") + closer, err := dht.closerPeersSingle(ctx, key, p) + if err != nil { + log.Errorf("error getting closer peers: %s", err) + return + } + + wg := sync.WaitGroup{} + for _, p := range closer { + if kb.Closer(p, dht.self, key) && peers.TryAdd(p) { + select { + case peerOut <- p: + case <-ctx.Done(): + return + } + wg.Add(1) + go func(p peer.ID) { + dht.getClosestPeersRecurse(ctx, key, p, peers, peerOut) + wg.Done() + }(p) + } + } + wg.Wait() +} + +func (dht *IpfsDHT) closerPeersSingle(ctx context.Context, key u.Key, p peer.ID) ([]peer.ID, error) { + log.Errorf("closest peers single %s %s", p, key) + defer log.Errorf("closest peers single end %s %s", p, key) + pmes, err := dht.findPeerSingle(ctx, p, peer.ID(key)) + if err != nil { + return nil, err + } + + var out []peer.ID + for _, pbp := range pmes.GetCloserPeers() { + pid := peer.ID(pbp.GetId()) + dht.peerstore.AddAddresses(pid, pbp.Addresses()) + err := dht.ensureConnectedToPeer(ctx, pid) + if err != nil { + return nil, err + } + out = append(out, pid) + } + return out, nil +} + // FindProvidersAsync is the same thing as FindProviders, but returns a channel. // Peers will be returned on the channel as soon as they are found, even before // the search query completes. @@ -182,6 +269,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co // Add unique providers from request, up to 'count' for _, prov := range provs { if ps.TryAdd(prov.ID) { + dht.peerstore.AddAddresses(prov.ID, prov.Addrs) select { case peerOut <- prov: case <-ctx.Done(): diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go new file mode 100644 index 000000000..a3a68767b --- /dev/null +++ b/routing/kbucket/sorting.go @@ -0,0 +1,59 @@ +package kbucket + +import ( + "container/list" + peer "github.com/jbenet/go-ipfs/peer" + "sort" +) + +// A helper struct to sort peers by their distance to the local node +type peerDistance struct { + p peer.ID + distance ID +} + +// peerSorterArr implements sort.Interface to sort peers by xor distance +type peerSorterArr []*peerDistance + +func (p peerSorterArr) Len() int { return len(p) } +func (p peerSorterArr) Swap(a, b int) { p[a], p[b] = p[b], p[a] } +func (p peerSorterArr) Less(a, b int) bool { + return p[a].distance.less(p[b].distance) +} + +// + +func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) peerSorterArr { + for e := peerList.Front(); e != nil; e = e.Next() { + p := e.Value.(peer.ID) + pID := ConvertPeerID(p) + pd := peerDistance{ + p: p, + distance: xor(target, pID), + } + peerArr = append(peerArr, &pd) + if e == nil { + log.Debug("list element was nil") + return peerArr + } + } + return peerArr +} + +func SortClosestPeers(peers []peer.ID, target ID) []peer.ID { + var psarr peerSorterArr + for _, p := range peers { + pID := ConvertPeerID(p) + pd := &peerDistance{ + p: p, + distance: xor(target, pID), + } + psarr = append(psarr, pd) + } + sort.Sort(psarr) + var out []peer.ID + for _, p := range psarr { + out = append(out, p.p) + } + return out +} diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index bed7447a5..90ba65530 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -2,7 +2,6 @@ package kbucket import ( - "container/list" "fmt" "sort" "sync" @@ -103,40 +102,6 @@ func (rt *RoutingTable) nextBucket() peer.ID { return "" } -// A helper struct to sort peers by their distance to the local node -type peerDistance struct { - p peer.ID - distance ID -} - -// peerSorterArr implements sort.Interface to sort peers by xor distance -type peerSorterArr []*peerDistance - -func (p peerSorterArr) Len() int { return len(p) } -func (p peerSorterArr) Swap(a, b int) { p[a], p[b] = p[b], p[a] } -func (p peerSorterArr) Less(a, b int) bool { - return p[a].distance.less(p[b].distance) -} - -// - -func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) peerSorterArr { - for e := peerList.Front(); e != nil; e = e.Next() { - p := e.Value.(peer.ID) - pID := ConvertPeerID(p) - pd := peerDistance{ - p: p, - distance: xor(target, pID), - } - peerArr = append(peerArr, &pd) - if e == nil { - log.Debug("list element was nil") - return peerArr - } - } - return peerArr -} - // Find a specific peer by ID or return nil func (rt *RoutingTable) Find(id peer.ID) peer.ID { srch := rt.NearestPeers(ConvertPeerID(id), 1) From ae786f92e7e17f7fdb5d9131ca3f01398248f972 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 14 Dec 2014 00:50:49 +0000 Subject: [PATCH 0564/3817] rewrite of provides to better select peers to send RPCs to refactor test peer creation to be deterministic and reliable a bit of cleanup trying to figure out TestGetFailure add test to verify deterministic peer creation switch put RPC over to use getClosestPeers rm 0xDEADC0DE fix queries not searching peer if its not actually closer This commit was moved from ipfs/go-namesys@6693d80a2d2d6dc58260f5fc16160c8ee2218e38 --- namesys/resolve_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 84e4f1cb6..592c344d7 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -15,7 +15,7 @@ func TestRoutingResolve(t *testing.T) { resolver := NewRoutingResolver(d) publisher := NewRoutingPublisher(d) - privk, pubk, err := ci.GenerateKeyPair(ci.RSA, 512) + privk, pubk, err := ci.GenerateKeyPair(ci.RSA, 512, u.NewTimeSeededRand()) if err != nil { t.Fatal(err) } From 06477b48cf05f9118137551666e8bf3a6460d439 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 24 Dec 2014 19:42:37 +0000 Subject: [PATCH 0565/3817] a couple small fixes This commit was moved from ipfs/go-ipfs-routing@a8ef90cf86d26f23c2de3763867a93d224075f1b --- routing/dht/routing.go | 1 - 1 file changed, 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 0cd5751a1..a0334451f 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -269,7 +269,6 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co // Add unique providers from request, up to 'count' for _, prov := range provs { if ps.TryAdd(prov.ID) { - dht.peerstore.AddAddresses(prov.ID, prov.Addrs) select { case peerOut <- prov: case <-ctx.Done(): From 89393c586d53d071e029ce861c8d2c1853664176 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 28 Dec 2014 15:46:41 -0800 Subject: [PATCH 0566/3817] dht: fix TestLayeredGet The test was occasionally passing because: - it called `putLocal(key, val)` - GetValue calls `getLocal(key)` optimistically. cc @whyrusleeping This commit was moved from ipfs/go-ipfs-routing@cda86e983924bee75e0ce73e4f94813ab8112a40 --- routing/dht/dht_test.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index bbc7b9692..547e88a9c 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -486,12 +486,7 @@ func TestLayeredGet(t *testing.T) { connect(t, ctx, dhts[1], dhts[2]) connect(t, ctx, dhts[1], dhts[3]) - err := dhts[3].putLocal(u.Key("/v/hello"), []byte("world")) - if err != nil { - t.Fatal(err) - } - - err = dhts[3].Provide(ctx, u.Key("/v/hello")) + err := dhts[3].Provide(ctx, u.Key("/v/hello")) if err != nil { t.Fatal(err) } From d6c0095914fbb19bfc94b83b8dcfab2b2965d021 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 28 Dec 2014 23:46:25 +0000 Subject: [PATCH 0567/3817] some better logging and cleanup This commit was moved from ipfs/go-ipfs-routing@903a3095d03f0c6ae8ae97392a2b08cd76760716 --- routing/dht/dht.go | 30 ++++------------------- routing/dht/handlers.go | 7 ++---- routing/dht/routing.go | 54 ++++++++++------------------------------- 3 files changed, 20 insertions(+), 71 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 1573f3477..fb7c5a49b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -102,8 +102,8 @@ func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.ID) error { return nil } -// putValueToNetwork stores the given key/value pair at the peer 'p' -func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p peer.ID, +// putValueToPeer stores the given key/value pair at the peer 'p' +func (dht *IpfsDHT) putValueToPeer(ctx context.Context, p peer.ID, key u.Key, rec *pb.Record) error { pmes := pb.NewMessage(pb.Message_PUT_VALUE, string(key), 0) @@ -237,12 +237,12 @@ func (dht *IpfsDHT) Update(ctx context.Context, p peer.ID) { } // FindLocal looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in. -func (dht *IpfsDHT) FindLocal(id peer.ID) (peer.PeerInfo, *kb.RoutingTable) { +func (dht *IpfsDHT) FindLocal(id peer.ID) peer.PeerInfo { p := dht.routingTable.Find(id) if p != "" { - return dht.peerstore.PeerInfo(p), dht.routingTable + return dht.peerstore.PeerInfo(p) } - return peer.PeerInfo{}, nil + return peer.PeerInfo{} } // findPeerSingle asks peer 'p' if they know where the peer with id 'id' is @@ -256,26 +256,6 @@ func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.ID, key u.Ke return dht.sendRequest(ctx, p, pmes) } -func (dht *IpfsDHT) addProviders(key u.Key, pbps []*pb.Message_Peer) []peer.ID { - peers := pb.PBPeersToPeerInfos(pbps) - - var provArr []peer.ID - for _, pi := range peers { - p := pi.ID - - // Dont add outselves to the list - if p == dht.self { - continue - } - - log.Debugf("%s adding provider: %s for %s", dht.self, p, key) - // TODO(jbenet) ensure providers is idempotent - dht.providers.AddProvider(key, p) - provArr = append(provArr, p) - } - return provArr -} - // nearestPeersToQuery returns the routing tables closest peers. func (dht *IpfsDHT) nearestPeersToQuery(pmes *pb.Message, count int) []peer.ID { key := u.Key(pmes.GetKey()) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index e8edaa5eb..acb052248 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -39,7 +39,7 @@ func (dht *IpfsDHT) handlerForMsgType(t pb.Message_MessageType) dhtHandler { } func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { - log.Debugf("%s handleGetValue for key: %s\n", dht.self, pmes.GetKey()) + log.Debugf("%s handleGetValue for key: %s", dht.self, pmes.GetKey()) // setup response resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) @@ -127,7 +127,7 @@ func (dht *IpfsDHT) handlePutValue(ctx context.Context, p peer.ID, pmes *pb.Mess } err = dht.datastore.Put(dskey, data) - log.Debugf("%s handlePutValue %v\n", dht.self, dskey) + log.Debugf("%s handlePutValue %v", dht.self, dskey) return pmes, err } @@ -137,9 +137,6 @@ func (dht *IpfsDHT) handlePing(_ context.Context, p peer.ID, pmes *pb.Message) ( } func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { - log.Errorf("handle find peer %s start", p) - defer log.Errorf("handle find peer %s end", p) - resp := pb.NewMessage(pmes.GetType(), "", pmes.GetClusterLevel()) var closest []peer.ID diff --git a/routing/dht/routing.go b/routing/dht/routing.go index a0334451f..8b0bb1670 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -50,7 +50,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error wg.Add(1) go func(p peer.ID) { defer wg.Done() - err := dht.putValueToNetwork(ctx, p, key, rec) + err := dht.putValueToPeer(ctx, p, key, rec) if err != nil { log.Errorf("failed putting value to peer: %s", err) } @@ -125,12 +125,18 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { return err } + wg := sync.WaitGroup{} for p := range peers { - err := dht.putProvider(ctx, p, string(key)) - if err != nil { - log.Error(err) - } + wg.Add(1) + go func(p peer.ID) { + defer wg.Done() + err := dht.putProvider(ctx, p, string(key)) + if err != nil { + log.Error(err) + } + }(p) } + wg.Wait() return nil } @@ -144,7 +150,6 @@ func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]peer.PeerIn } func (dht *IpfsDHT) getClosestPeers(ctx context.Context, key u.Key, count int) (<-chan peer.ID, error) { - log.Error("Get Closest Peers") tablepeers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) if len(tablepeers) == 0 { return nil, kb.ErrLookupFailure @@ -170,15 +175,12 @@ func (dht *IpfsDHT) getClosestPeers(ctx context.Context, key u.Key, count int) ( go func() { wg.Wait() close(out) - log.Error("Closing closest peer chan") }() return out, nil } func (dht *IpfsDHT) getClosestPeersRecurse(ctx context.Context, key u.Key, p peer.ID, peers *pset.PeerSet, peerOut chan<- peer.ID) { - log.Error("closest peers recurse") - defer log.Error("closest peers recurse end") closer, err := dht.closerPeersSingle(ctx, key, p) if err != nil { log.Errorf("error getting closer peers: %s", err) @@ -204,8 +206,6 @@ func (dht *IpfsDHT) getClosestPeersRecurse(ctx context.Context, key u.Key, p pee } func (dht *IpfsDHT) closerPeersSingle(ctx context.Context, key u.Key, p peer.ID) ([]peer.ID, error) { - log.Errorf("closest peers single %s %s", p, key) - defer log.Errorf("closest peers single end %s %s", p, key) pmes, err := dht.findPeerSingle(ctx, p, peer.ID(key)) if err != nil { return nil, err @@ -236,6 +236,7 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, count int, peerOut chan peer.PeerInfo) { defer close(peerOut) + defer log.Event(ctx, "findProviders end", &key) log.Debugf("%s FindProviders %s", dht.self, key) ps := pset.NewLimited(count) @@ -294,40 +295,11 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co } } -func (dht *IpfsDHT) addPeerListAsync(ctx context.Context, k u.Key, peers []*pb.Message_Peer, ps *pset.PeerSet, count int, out chan peer.PeerInfo) { - var wg sync.WaitGroup - peerInfos := pb.PBPeersToPeerInfos(peers) - for _, pi := range peerInfos { - wg.Add(1) - go func(pi peer.PeerInfo) { - defer wg.Done() - - p := pi.ID - if err := dht.ensureConnectedToPeer(ctx, p); err != nil { - log.Errorf("%s", err) - return - } - - dht.providers.AddProvider(k, p) - if ps.TryAdd(p) { - select { - case out <- pi: - case <-ctx.Done(): - return - } - } else if ps.Size() >= count { - return - } - }(pi) - } - wg.Wait() -} - // FindPeer searches for a peer with given ID. func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, error) { // Check if were already connected to them - if pi, _ := dht.FindLocal(id); pi.ID != "" { + if pi := dht.FindLocal(id); pi.ID != "" { return pi, nil } From f51ebae060d486ab1dd8915b1ac3a13f46b3e3aa Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 29 Dec 2014 06:32:27 +0000 Subject: [PATCH 0568/3817] use query for getClosestPeers This commit was moved from ipfs/go-ipfs-routing@a55bf913544d4976c87fcfc0130b6a077272be6a --- routing/dht/routing.go | 68 ++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 8b0bb1670..98c4ce3d4 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -159,52 +159,48 @@ func (dht *IpfsDHT) getClosestPeers(ctx context.Context, key u.Key, count int) ( peerset := pset.NewLimited(count) for _, p := range tablepeers { - out <- p + select { + case out <- p: + case <-ctx.Done(): + return nil, ctx.Err() + } peerset.Add(p) } - wg := sync.WaitGroup{} - for _, p := range tablepeers { - wg.Add(1) - go func(p peer.ID) { - dht.getClosestPeersRecurse(ctx, key, p, peerset, out) - wg.Done() - }(p) - } + query := newQuery(key, dht.network, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { + closer, err := dht.closerPeersSingle(ctx, key, p) + if err != nil { + log.Errorf("error getting closer peers: %s", err) + return nil, err + } + + var filtered []peer.PeerInfo + for _, p := range closer { + if kb.Closer(p, dht.self, key) && peerset.TryAdd(p) { + select { + case out <- p: + case <-ctx.Done(): + return nil, ctx.Err() + } + filtered = append(filtered, dht.peerstore.PeerInfo(p)) + } + } + + return &dhtQueryResult{closerPeers: filtered}, nil + }) go func() { - wg.Wait() - close(out) + defer close(out) + // run it! + _, err := query.Run(ctx, tablepeers) + if err != nil { + log.Errorf("closestPeers query run error: %s", err) + } }() return out, nil } -func (dht *IpfsDHT) getClosestPeersRecurse(ctx context.Context, key u.Key, p peer.ID, peers *pset.PeerSet, peerOut chan<- peer.ID) { - closer, err := dht.closerPeersSingle(ctx, key, p) - if err != nil { - log.Errorf("error getting closer peers: %s", err) - return - } - - wg := sync.WaitGroup{} - for _, p := range closer { - if kb.Closer(p, dht.self, key) && peers.TryAdd(p) { - select { - case peerOut <- p: - case <-ctx.Done(): - return - } - wg.Add(1) - go func(p peer.ID) { - dht.getClosestPeersRecurse(ctx, key, p, peers, peerOut) - wg.Done() - }(p) - } - } - wg.Wait() -} - func (dht *IpfsDHT) closerPeersSingle(ctx context.Context, key u.Key, p peer.ID) ([]peer.ID, error) { pmes, err := dht.findPeerSingle(ctx, p, peer.ID(key)) if err != nil { From 398e3f81aed4c765d2bc55f1d15ba300eeee5881 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 29 Dec 2014 02:29:55 +0000 Subject: [PATCH 0569/3817] address comments from PR This commit was moved from ipfs/go-namesys@4baa9877a58d7e215a3c8e5f5e76e9278ef3daa5 --- namesys/resolve_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 592c344d7..35851fc32 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -3,7 +3,6 @@ package namesys import ( "testing" - ci "github.com/jbenet/go-ipfs/crypto" mockrouting "github.com/jbenet/go-ipfs/routing/mock" u "github.com/jbenet/go-ipfs/util" testutil "github.com/jbenet/go-ipfs/util/testutil" @@ -15,7 +14,7 @@ func TestRoutingResolve(t *testing.T) { resolver := NewRoutingResolver(d) publisher := NewRoutingPublisher(d) - privk, pubk, err := ci.GenerateKeyPair(ci.RSA, 512, u.NewTimeSeededRand()) + privk, pubk, err := testutil.RandKeyPair(512) if err != nil { t.Fatal(err) } From b79faa434305ba81c37521797a81522df174e903 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 29 Dec 2014 18:22:16 +0000 Subject: [PATCH 0570/3817] Improve readability of getClosestPeers method. Also remove older useless code. This commit was moved from ipfs/go-ipfs-routing@ec5d9c78ec9808d4ffd1fd20925eb7fe4b6bdd48 --- routing/dht/routing.go | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 98c4ce3d4..36e281cc9 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -40,7 +40,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error return err } - pchan, err := dht.getClosestPeers(ctx, key, KValue) + pchan, err := dht.getClosestPeers(ctx, key) if err != nil { return err } @@ -116,11 +116,11 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { // Provide makes this node announce that it can provide a value for the given key func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { - log.Event(ctx, "Provide Value start", &key) - defer log.Event(ctx, "Provide Value end", &key) + log.Event(ctx, "provideBegin", &key) + defer log.Event(ctx, "provideEnd", &key) dht.providers.AddProvider(key, dht.self) - peers, err := dht.getClosestPeers(ctx, key, KValue) + peers, err := dht.getClosestPeers(ctx, key) if err != nil { return err } @@ -149,14 +149,16 @@ func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]peer.PeerIn return providers, nil } -func (dht *IpfsDHT) getClosestPeers(ctx context.Context, key u.Key, count int) (<-chan peer.ID, error) { +// Kademlia 'node lookup' operation. Returns a channel of the K closest peers +// to the given key +func (dht *IpfsDHT) getClosestPeers(ctx context.Context, key u.Key) (<-chan peer.ID, error) { tablepeers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) if len(tablepeers) == 0 { return nil, kb.ErrLookupFailure } - out := make(chan peer.ID, count) - peerset := pset.NewLimited(count) + out := make(chan peer.ID, KValue) + peerset := pset.NewLimited(KValue) for _, p := range tablepeers { select { @@ -211,10 +213,6 @@ func (dht *IpfsDHT) closerPeersSingle(ctx context.Context, key u.Key, p peer.ID) for _, pbp := range pmes.GetCloserPeers() { pid := peer.ID(pbp.GetId()) dht.peerstore.AddAddresses(pid, pbp.Addresses()) - err := dht.ensureConnectedToPeer(ctx, pid) - if err != nil { - return nil, err - } out = append(out, pid) } return out, nil From bde8466a5a521474582fdbcfa611bc7ae2955085 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 2 Jan 2015 08:33:42 +0000 Subject: [PATCH 0571/3817] clean up test setup interface This commit was moved from ipfs/go-ipfs-routing@a0ccd2d992853eb16f4e01f0d50c3ebfea849338 --- routing/dht/dht_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 547e88a9c..5eeb3a2bc 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -32,9 +32,9 @@ func init() { } } -func setupDHT(ctx context.Context, t *testing.T, addr ma.Multiaddr, seed int64) *IpfsDHT { +func setupDHT(ctx context.Context, t *testing.T, addr ma.Multiaddr) *IpfsDHT { - sk, pk, err := testutil.SeededKeyPair(512, seed) + sk, pk, err := testutil.SeededKeyPair(time.Now().UnixNano()) if err != nil { t.Fatal(err) } @@ -70,7 +70,7 @@ func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []peer for i := 0; i < n; i++ { addrs[i] = testutil.RandLocalTCPAddress() - dhts[i] = setupDHT(ctx, t, addrs[i], int64(i)) + dhts[i] = setupDHT(ctx, t, addrs[i]) peers[i] = dhts[i].self } @@ -119,8 +119,8 @@ func TestPing(t *testing.T) { addrA := testutil.RandLocalTCPAddress() addrB := testutil.RandLocalTCPAddress() - dhtA := setupDHT(ctx, t, addrA, 1) - dhtB := setupDHT(ctx, t, addrB, 2) + dhtA := setupDHT(ctx, t, addrA) + dhtB := setupDHT(ctx, t, addrB) peerA := dhtA.self peerB := dhtB.self @@ -152,8 +152,8 @@ func TestValueGetSet(t *testing.T) { addrA := testutil.RandLocalTCPAddress() addrB := testutil.RandLocalTCPAddress() - dhtA := setupDHT(ctx, t, addrA, 1) - dhtB := setupDHT(ctx, t, addrB, 2) + dhtA := setupDHT(ctx, t, addrA) + dhtB := setupDHT(ctx, t, addrB) defer dhtA.Close() defer dhtB.Close() @@ -636,8 +636,8 @@ func TestConnectCollision(t *testing.T) { addrA := testutil.RandLocalTCPAddress() addrB := testutil.RandLocalTCPAddress() - dhtA := setupDHT(ctx, t, addrA, int64((rtime*2)+1)) - dhtB := setupDHT(ctx, t, addrB, int64((rtime*2)+2)) + dhtA := setupDHT(ctx, t, addrA) + dhtB := setupDHT(ctx, t, addrB) peerA := dhtA.self peerB := dhtB.self From 1fbb90d39f6700f45126747f5c495a923f4bab43 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 2 Jan 2015 08:36:32 -0800 Subject: [PATCH 0572/3817] routing: use debugerror This commit was moved from ipfs/go-ipfs-routing@f72cf145534e417be5d8048b5136795e63c19c55 --- routing/dht/routing.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 36e281cc9..2a948f4be 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,6 +12,7 @@ import ( pb "github.com/jbenet/go-ipfs/routing/dht/pb" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" + errors "github.com/jbenet/go-ipfs/util/debugerror" pset "github.com/jbenet/go-ipfs/util/peerset" ) @@ -77,7 +78,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { closest := dht.routingTable.NearestPeers(kb.ConvertKey(key), PoolSize) if closest == nil || len(closest) == 0 { log.Warning("Got no peers back from routing table!") - return nil, kb.ErrLookupFailure + return nil, errors.Wrap(kb.ErrLookupFailure) } // setup the Query @@ -154,7 +155,7 @@ func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]peer.PeerIn func (dht *IpfsDHT) getClosestPeers(ctx context.Context, key u.Key) (<-chan peer.ID, error) { tablepeers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) if len(tablepeers) == 0 { - return nil, kb.ErrLookupFailure + return nil, errors.Wrap(kb.ErrLookupFailure) } out := make(chan peer.ID, KValue) @@ -299,7 +300,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, er closest := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue) if closest == nil || len(closest) == 0 { - return peer.PeerInfo{}, kb.ErrLookupFailure + return peer.PeerInfo{}, errors.Wrap(kb.ErrLookupFailure) } // Sanity... @@ -356,7 +357,7 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< closest := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue) if closest == nil || len(closest) == 0 { - return nil, kb.ErrLookupFailure + return nil, errors.Wrap(kb.ErrLookupFailure) } // setup the Query From e8fbf1483909f772a437281307bf7946506f0b07 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 2 Jan 2015 08:36:36 -0800 Subject: [PATCH 0573/3817] blockstore: suppress exchange error This commit was moved from ipfs/go-blockservice@dfa7749ee22dde7fcdf2d582e6aece63060a63c7 --- blockservice/blockservice.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 1a7ea6b7a..db126f344 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -50,9 +50,13 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { // consider moving this to an sync process. if s.Exchange != nil { ctx := context.TODO() - err = s.Exchange.HasBlock(ctx, b) + if err := s.Exchange.HasBlock(ctx, b); err != nil { + // suppress error, as the client shouldn't care about bitswap. + // the client only cares about the blockstore.Put. + log.Errorf("Exchange.HasBlock error: %s", err) + } } - return k, err + return k, nil } // GetBlock retrieves a particular block from the service, From 9fb71b284d1bdf1fc7dc1a9d5d189078c36c2966 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 24 Dec 2014 10:17:26 -0800 Subject: [PATCH 0574/3817] net: move Network implementation to own pkg I needed the network implementation in its own package, because I'll be writing several services that will plug into _it_ that shouldn't be part of the core net package. and then there were dependency conflicts. yay. mux + identify are good examples of what i mean. This commit was moved from ipfs/go-ipfs-routing@b39f91ee89dbed2444b72dbc1d205270b972b3d0 --- routing/dht/dht_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 5eeb3a2bc..4e49b8f96 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -14,7 +14,7 @@ import ( dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - inet "github.com/jbenet/go-ipfs/net" + ipfsnet "github.com/jbenet/go-ipfs/net/ipfsnet" peer "github.com/jbenet/go-ipfs/peer" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" @@ -49,7 +49,7 @@ func setupDHT(ctx context.Context, t *testing.T, addr ma.Multiaddr) *IpfsDHT { peerstore.AddPubKey(p, pk) peerstore.AddAddress(p, addr) - n, err := inet.NewNetwork(ctx, []ma.Multiaddr{addr}, p, peerstore) + n, err := ipfsnet.NewNetwork(ctx, []ma.Multiaddr{addr}, p, peerstore) if err != nil { t.Fatal(err) } From e1c8556a39ace32c368abca31c3dbc4a9f46004f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 28 Dec 2014 06:25:45 -0800 Subject: [PATCH 0575/3817] ipfsnet -> swarmnet swarmnet is a better name for the package, because it's just a Network implemented with a Swarm. (ipfsnet will be something slightly different). This commit was moved from ipfs/go-ipfs-routing@49257c52f07c360e6fa7a9bf6e277398af035037 --- routing/dht/dht_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 4e49b8f96..088dff217 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -14,7 +14,7 @@ import ( dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - ipfsnet "github.com/jbenet/go-ipfs/net/ipfsnet" + swarmnet "github.com/jbenet/go-ipfs/net/swarmnet" peer "github.com/jbenet/go-ipfs/peer" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" @@ -49,7 +49,7 @@ func setupDHT(ctx context.Context, t *testing.T, addr ma.Multiaddr) *IpfsDHT { peerstore.AddPubKey(p, pk) peerstore.AddAddress(p, addr) - n, err := ipfsnet.NewNetwork(ctx, []ma.Multiaddr{addr}, p, peerstore) + n, err := swarmnet.NewNetwork(ctx, []ma.Multiaddr{addr}, p, peerstore) if err != nil { t.Fatal(err) } From 74a43c62a5eecc0170e1dd666934510dfec31563 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 29 Dec 2014 05:43:56 -0800 Subject: [PATCH 0576/3817] introducing p2p pkg I think it's time to move a lot of the peer-to-peer networking but-not-ipfs-specific things into its own package: p2p. This could in the future be split off into its own library. The first thing to go is the peer. This commit was moved from ipfs/go-ipfs-routing@888ed12fbc61fcc99e49620d6074f38b99b3ed7e --- routing/dht/dht.go | 2 +- routing/dht/dht_net.go | 2 +- routing/dht/dht_test.go | 2 +- routing/dht/diag.go | 2 +- routing/dht/ext_test.go | 2 +- routing/dht/handlers.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/providers.go | 2 +- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 4 ++-- routing/dht/records.go | 2 +- routing/dht/routing.go | 2 +- routing/kbucket/bucket.go | 2 +- routing/kbucket/table.go | 2 +- routing/kbucket/table_test.go | 2 +- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/interface.go | 2 +- routing/routing.go | 2 +- 21 files changed, 22 insertions(+), 22 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index fb7c5a49b..ed9858c7a 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -11,7 +11,7 @@ import ( "time" inet "github.com/jbenet/go-ipfs/net" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" kb "github.com/jbenet/go-ipfs/routing/kbucket" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index d247cf3af..3eea25d9e 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -5,7 +5,7 @@ import ( "time" inet "github.com/jbenet/go-ipfs/net" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" ctxutil "github.com/jbenet/go-ipfs/util/ctx" diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 088dff217..f4f2a5414 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -15,7 +15,7 @@ import ( ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" swarmnet "github.com/jbenet/go-ipfs/net/swarmnet" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" testutil "github.com/jbenet/go-ipfs/util/testutil" diff --git a/routing/dht/diag.go b/routing/dht/diag.go index 96d2b1a01..79b4709e9 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" ) type connDiagInfo struct { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 8441c1f72..168a2d8ed 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -6,7 +6,7 @@ import ( inet "github.com/jbenet/go-ipfs/net" mocknet "github.com/jbenet/go-ipfs/net/mock" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index acb052248..a02eb024d 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -8,7 +8,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" ) diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 570c7cf18..87b0b1f4c 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,7 +4,7 @@ import ( ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" inet "github.com/jbenet/go-ipfs/net" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" eventlog "github.com/jbenet/go-ipfs/util/eventlog" ) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 861c25f0c..9e96eff36 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -4,7 +4,7 @@ import ( "time" ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" u "github.com/jbenet/go-ipfs/util" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 35ff92dfe..2781a3c59 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -3,7 +3,7 @@ package dht import ( "testing" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" u "github.com/jbenet/go-ipfs/util" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" diff --git a/routing/dht/query.go b/routing/dht/query.go index 4790e814c..95e3c3c90 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -4,8 +4,8 @@ import ( "sync" inet "github.com/jbenet/go-ipfs/net" - peer "github.com/jbenet/go-ipfs/peer" - queue "github.com/jbenet/go-ipfs/peer/queue" + peer "github.com/jbenet/go-ipfs/p2p/peer" + queue "github.com/jbenet/go-ipfs/p2p/peer/queue" "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" pset "github.com/jbenet/go-ipfs/util/peerset" diff --git a/routing/dht/records.go b/routing/dht/records.go index cf383916b..0d7e91c6f 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -10,7 +10,7 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ci "github.com/jbenet/go-ipfs/crypto" - "github.com/jbenet/go-ipfs/peer" + "github.com/jbenet/go-ipfs/p2p/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" ctxutil "github.com/jbenet/go-ipfs/util/ctx" diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 2a948f4be..ee71f7156 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -7,7 +7,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" inet "github.com/jbenet/go-ipfs/net" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" kb "github.com/jbenet/go-ipfs/routing/kbucket" diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 2fa5586db..e158f70f9 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 90ba65530..62bfa0646 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,7 +7,7 @@ import ( "sync" "time" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" u "github.com/jbenet/go-ipfs/util" ) diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index db93ddf86..3e44cf66a 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,7 +7,7 @@ import ( tu "github.com/jbenet/go-ipfs/util/testutil" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 2d06b5f08..80c08de9e 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -5,7 +5,7 @@ import ( "crypto/sha256" "errors" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" ks "github.com/jbenet/go-ipfs/routing/keyspace" u "github.com/jbenet/go-ipfs/util" ) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 6b5a455a7..2aeafe026 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -6,7 +6,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" "github.com/jbenet/go-ipfs/util/testutil" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 030227b1b..dc462797a 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -7,7 +7,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" u "github.com/jbenet/go-ipfs/util" "github.com/jbenet/go-ipfs/util/testutil" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index dcaf165b1..526d63c68 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -5,7 +5,7 @@ import ( "time" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" u "github.com/jbenet/go-ipfs/util" delay "github.com/jbenet/go-ipfs/util/delay" "github.com/jbenet/go-ipfs/util/testutil" diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 0bb54f365..d7dca8348 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -7,7 +7,7 @@ package mockrouting import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" delay "github.com/jbenet/go-ipfs/util/delay" diff --git a/routing/routing.go b/routing/routing.go index ae9acad44..1fbd79d25 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -6,7 +6,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" u "github.com/jbenet/go-ipfs/util" ) From f22a08626f7e78f6867d1670b4b2fa46f503a6b0 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 29 Dec 2014 05:45:55 -0800 Subject: [PATCH 0577/3817] crypto -> p2p/crypto The crypto package moves into p2p. Nothing in it so far is ipfs specific; everything is p2p-general. This commit was moved from ipfs/go-ipfs-routing@77884ea075efd40f7fb22bbe67abd3a5f23b5cb7 --- routing/dht/records.go | 2 +- routing/kbucket/sorting.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/records.go b/routing/dht/records.go index 0d7e91c6f..0791f80a3 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -9,7 +9,7 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - ci "github.com/jbenet/go-ipfs/crypto" + ci "github.com/jbenet/go-ipfs/p2p/crypto" "github.com/jbenet/go-ipfs/p2p/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index a3a68767b..7995b39ed 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "github.com/jbenet/go-ipfs/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" "sort" ) From 37c003b961c1ccbfc209ae32a8f60ca36d08aaf6 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 29 Dec 2014 05:48:21 -0800 Subject: [PATCH 0578/3817] net -> p2p/net The net package is the next to move. It will be massaged a bit still to fix the Network / "NetworkBackend" conflict. This commit was moved from ipfs/go-ipfs-routing@bf57b51b6c58ca12b93ebb42d6db6611966a4d49 --- routing/dht/dht.go | 2 +- routing/dht/dht_net.go | 2 +- routing/dht/dht_test.go | 2 +- routing/dht/ext_test.go | 4 ++-- routing/dht/pb/message.go | 2 +- routing/dht/query.go | 2 +- routing/dht/routing.go | 2 +- routing/mock/dht.go | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index ed9858c7a..4e9e670d8 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -10,7 +10,7 @@ import ( "sync" "time" - inet "github.com/jbenet/go-ipfs/net" + inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 3eea25d9e..3d9bbd93f 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -4,7 +4,7 @@ import ( "errors" "time" - inet "github.com/jbenet/go-ipfs/net" + inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" ctxutil "github.com/jbenet/go-ipfs/util/ctx" diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index f4f2a5414..18fd74274 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -14,7 +14,7 @@ import ( dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - swarmnet "github.com/jbenet/go-ipfs/net/swarmnet" + swarmnet "github.com/jbenet/go-ipfs/p2p/net/swarmnet" peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 168a2d8ed..f76f5bddc 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -4,8 +4,8 @@ import ( "math/rand" "testing" - inet "github.com/jbenet/go-ipfs/net" - mocknet "github.com/jbenet/go-ipfs/net/mock" + inet "github.com/jbenet/go-ipfs/p2p/net" + mocknet "github.com/jbenet/go-ipfs/p2p/net/mock" peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 87b0b1f4c..61bf41ebb 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -3,7 +3,7 @@ package dht_pb import ( ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - inet "github.com/jbenet/go-ipfs/net" + inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" eventlog "github.com/jbenet/go-ipfs/util/eventlog" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index 95e3c3c90..5b62a8f4c 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -3,7 +3,7 @@ package dht import ( "sync" - inet "github.com/jbenet/go-ipfs/net" + inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" queue "github.com/jbenet/go-ipfs/p2p/peer/queue" "github.com/jbenet/go-ipfs/routing" diff --git a/routing/dht/routing.go b/routing/dht/routing.go index ee71f7156..4c3cf160b 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -6,7 +6,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - inet "github.com/jbenet/go-ipfs/net" + inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 1dfa415e0..1f0340ebb 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -4,7 +4,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" sync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - mocknet "github.com/jbenet/go-ipfs/net/mock" + mocknet "github.com/jbenet/go-ipfs/p2p/net/mock" dht "github.com/jbenet/go-ipfs/routing/dht" "github.com/jbenet/go-ipfs/util/testutil" ) From 967b1e3cd5c7e8d6595d3b26dcb5fdf8cc734315 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 29 Dec 2014 05:45:55 -0800 Subject: [PATCH 0579/3817] crypto -> p2p/crypto The crypto package moves into p2p. Nothing in it so far is ipfs specific; everything is p2p-general. This commit was moved from ipfs/go-namesys@7d4d71075156bf923395e335212dc09bbcd4a4d5 --- namesys/interface.go | 2 +- namesys/namesys.go | 2 +- namesys/publisher.go | 2 +- namesys/routing.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index eef1fc32b..c2e39afec 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -3,7 +3,7 @@ package namesys import ( "errors" - ci "github.com/jbenet/go-ipfs/crypto" + ci "github.com/jbenet/go-ipfs/p2p/crypto" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/namesys.go b/namesys/namesys.go index 2ea9a30bd..cc11d9ddc 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -1,7 +1,7 @@ package namesys import ( - ci "github.com/jbenet/go-ipfs/crypto" + ci "github.com/jbenet/go-ipfs/p2p/crypto" routing "github.com/jbenet/go-ipfs/routing" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index be838b2f0..75cccf9e4 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -10,8 +10,8 @@ import ( proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - ci "github.com/jbenet/go-ipfs/crypto" pb "github.com/jbenet/go-ipfs/namesys/internal/pb" + ci "github.com/jbenet/go-ipfs/p2p/crypto" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" ) diff --git a/namesys/routing.go b/namesys/routing.go index c990b492b..709f9424c 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -7,8 +7,8 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - ci "github.com/jbenet/go-ipfs/crypto" pb "github.com/jbenet/go-ipfs/namesys/internal/pb" + ci "github.com/jbenet/go-ipfs/p2p/crypto" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" ) From 370aa61e14cee1c3cf3c86067eec341859e10f23 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 1 Jan 2015 12:45:39 -0800 Subject: [PATCH 0580/3817] swap net2 -> net This commit was moved from ipfs/go-ipfs-routing@0c8144f5ff3033c80e98ec7d5f7fd34e4f937d1e --- routing/dht/dht.go | 28 ++++++++------- routing/dht/dht_net.go | 4 +-- routing/dht/dht_test.go | 80 ++++++++++++++--------------------------- routing/dht/ext_test.go | 45 +++++++++++++---------- routing/dht/handlers.go | 10 +++--- routing/dht/query.go | 34 ++++++------------ routing/dht/routing.go | 10 +++--- routing/mock/dht.go | 4 +-- 8 files changed, 95 insertions(+), 120 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 4e9e670d8..2a576629a 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -10,8 +10,9 @@ import ( "sync" "time" - inet "github.com/jbenet/go-ipfs/p2p/net" + host "github.com/jbenet/go-ipfs/p2p/host" peer "github.com/jbenet/go-ipfs/p2p/peer" + protocol "github.com/jbenet/go-ipfs/p2p/protocol" routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" kb "github.com/jbenet/go-ipfs/routing/kbucket" @@ -26,6 +27,8 @@ import ( var log = eventlog.Logger("dht") +var ProtocolDHT protocol.ID = "/ipfs/dht" + const doPinging = false // NumBootstrapQueries defines the number of random dht queries to do to @@ -37,7 +40,7 @@ const NumBootstrapQueries = 5 // IpfsDHT is an implementation of Kademlia with Coral and S/Kademlia modifications. // It is used to implement the base IpfsRouting module. type IpfsDHT struct { - network inet.Network // the network services we need + host host.Host // the network services we need self peer.ID // Local peer (yourself) peerstore peer.Peerstore // Peer Registry @@ -56,19 +59,19 @@ type IpfsDHT struct { } // NewDHT creates a new DHT object with the given peer as the 'local' host -func NewDHT(ctx context.Context, p peer.ID, n inet.Network, dstore ds.ThreadSafeDatastore) *IpfsDHT { +func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *IpfsDHT { dht := new(IpfsDHT) dht.datastore = dstore - dht.self = p - dht.peerstore = n.Peerstore() + dht.self = h.ID() + dht.peerstore = h.Peerstore() dht.ContextGroup = ctxgroup.WithContext(ctx) - dht.network = n - n.SetHandler(inet.ProtocolDHT, dht.handleNewStream) + dht.host = h + h.SetStreamHandler(ProtocolDHT, dht.handleNewStream) - dht.providers = NewProviderManager(dht.Context(), p) + dht.providers = NewProviderManager(dht.Context(), dht.self) dht.AddChildGroup(dht.providers) - dht.routingTable = kb.NewRoutingTable(20, kb.ConvertPeerID(p), time.Minute, dht.peerstore) + dht.routingTable = kb.NewRoutingTable(20, kb.ConvertPeerID(dht.self), time.Minute, dht.peerstore) dht.birth = time.Now() dht.Validators = make(map[string]ValidatorFunc) @@ -88,7 +91,8 @@ func (dht *IpfsDHT) LocalPeer() peer.ID { // Connect to a new peer at the given address, ping and add to the routing table func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.ID) error { - if err := dht.network.DialPeer(ctx, npeer); err != nil { + // TODO: change interface to accept a PeerInfo as well. + if err := dht.host.Connect(ctx, peer.PeerInfo{ID: npeer}); err != nil { return err } @@ -127,7 +131,7 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, key string) erro // add self as the provider pi := dht.peerstore.PeerInfo(dht.self) - pmes.ProviderPeers = pb.PeerInfosToPBPeers(dht.network, []peer.PeerInfo{pi}) + pmes.ProviderPeers = pb.PeerInfosToPBPeers(dht.host.Network(), []peer.PeerInfo{pi}) err := dht.sendMessage(ctx, p, pmes) if err != nil { @@ -304,7 +308,7 @@ func (dht *IpfsDHT) ensureConnectedToPeer(ctx context.Context, p peer.ID) error } // dial connection - return dht.network.DialPeer(ctx, p) + return dht.host.Connect(ctx, peer.PeerInfo{ID: p}) } // PingRoutine periodically pings nearest neighbors. diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 3d9bbd93f..fd088e02c 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -74,7 +74,7 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { log.Debugf("%s dht starting stream", dht.self) - s, err := dht.network.NewStream(inet.ProtocolDHT, p) + s, err := dht.host.NewStream(ProtocolDHT, p) if err != nil { return nil, err } @@ -116,7 +116,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message func (dht *IpfsDHT) sendMessage(ctx context.Context, p peer.ID, pmes *pb.Message) error { log.Debugf("%s dht starting stream", dht.self) - s, err := dht.network.NewStream(inet.ProtocolDHT, p) + s, err := dht.host.NewStream(ProtocolDHT, p) if err != nil { return err } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 18fd74274..133f7a27c 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -14,11 +14,10 @@ import ( dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - swarmnet "github.com/jbenet/go-ipfs/p2p/net/swarmnet" peer "github.com/jbenet/go-ipfs/p2p/peer" + netutil "github.com/jbenet/go-ipfs/p2p/test/util" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" - testutil "github.com/jbenet/go-ipfs/util/testutil" ) var testCaseValues = map[u.Key][]byte{} @@ -32,30 +31,11 @@ func init() { } } -func setupDHT(ctx context.Context, t *testing.T, addr ma.Multiaddr) *IpfsDHT { - - sk, pk, err := testutil.SeededKeyPair(time.Now().UnixNano()) - if err != nil { - t.Fatal(err) - } - - p, err := peer.IDFromPublicKey(pk) - if err != nil { - t.Fatal(err) - } - - peerstore := peer.NewPeerstore() - peerstore.AddPrivKey(p, sk) - peerstore.AddPubKey(p, pk) - peerstore.AddAddress(p, addr) - - n, err := swarmnet.NewNetwork(ctx, []ma.Multiaddr{addr}, p, peerstore) - if err != nil { - t.Fatal(err) - } +func setupDHT(ctx context.Context, t *testing.T) *IpfsDHT { + h := netutil.GenHostSwarm(t, ctx) dss := dssync.MutexWrap(ds.NewMapDatastore()) - d := NewDHT(ctx, p, n, dss) + d := NewDHT(ctx, h, dss) d.Validators["v"] = func(u.Key, []byte) error { return nil @@ -69,9 +49,9 @@ func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []peer peers := make([]peer.ID, n) for i := 0; i < n; i++ { - addrs[i] = testutil.RandLocalTCPAddress() - dhts[i] = setupDHT(ctx, t, addrs[i]) + dhts[i] = setupDHT(ctx, t) peers[i] = dhts[i].self + addrs[i] = dhts[i].peerstore.Addresses(dhts[i].self)[0] } return addrs, peers, dhts @@ -116,19 +96,16 @@ func TestPing(t *testing.T) { // t.Skip("skipping test to debug another") ctx := context.Background() - addrA := testutil.RandLocalTCPAddress() - addrB := testutil.RandLocalTCPAddress() - - dhtA := setupDHT(ctx, t, addrA) - dhtB := setupDHT(ctx, t, addrB) + dhtA := setupDHT(ctx, t) + dhtB := setupDHT(ctx, t) peerA := dhtA.self peerB := dhtB.self defer dhtA.Close() defer dhtB.Close() - defer dhtA.network.Close() - defer dhtB.network.Close() + defer dhtA.host.Close() + defer dhtB.host.Close() connect(t, ctx, dhtA, dhtB) @@ -149,16 +126,13 @@ func TestValueGetSet(t *testing.T) { ctx := context.Background() - addrA := testutil.RandLocalTCPAddress() - addrB := testutil.RandLocalTCPAddress() - - dhtA := setupDHT(ctx, t, addrA) - dhtB := setupDHT(ctx, t, addrB) + dhtA := setupDHT(ctx, t) + dhtB := setupDHT(ctx, t) defer dhtA.Close() defer dhtB.Close() - defer dhtA.network.Close() - defer dhtB.network.Close() + defer dhtA.host.Close() + defer dhtB.host.Close() vf := func(u.Key, []byte) error { return nil @@ -200,7 +174,7 @@ func TestProvides(t *testing.T) { defer func() { for i := 0; i < 4; i++ { dhts[i].Close() - defer dhts[i].network.Close() + defer dhts[i].host.Close() } }() @@ -268,7 +242,7 @@ func TestBootstrap(t *testing.T) { defer func() { for i := 0; i < nDHTs; i++ { dhts[i].Close() - defer dhts[i].network.Close() + defer dhts[i].host.Close() } }() @@ -312,7 +286,7 @@ func TestProvidesMany(t *testing.T) { defer func() { for i := 0; i < nDHTs; i++ { dhts[i].Close() - defer dhts[i].network.Close() + defer dhts[i].host.Close() } }() @@ -424,7 +398,7 @@ func TestProvidesAsync(t *testing.T) { defer func() { for i := 0; i < 4; i++ { dhts[i].Close() - defer dhts[i].network.Close() + defer dhts[i].host.Close() } }() @@ -478,7 +452,7 @@ func TestLayeredGet(t *testing.T) { defer func() { for i := 0; i < 4; i++ { dhts[i].Close() - defer dhts[i].network.Close() + defer dhts[i].host.Close() } }() @@ -518,7 +492,7 @@ func TestFindPeer(t *testing.T) { defer func() { for i := 0; i < 4; i++ { dhts[i].Close() - dhts[i].network.Close() + dhts[i].host.Close() } }() @@ -554,7 +528,7 @@ func TestFindPeersConnectedToPeer(t *testing.T) { defer func() { for i := 0; i < 4; i++ { dhts[i].Close() - dhts[i].network.Close() + dhts[i].host.Close() } }() @@ -633,11 +607,11 @@ func TestConnectCollision(t *testing.T) { ctx := context.Background() - addrA := testutil.RandLocalTCPAddress() - addrB := testutil.RandLocalTCPAddress() + dhtA := setupDHT(ctx, t) + dhtB := setupDHT(ctx, t) - dhtA := setupDHT(ctx, t, addrA) - dhtB := setupDHT(ctx, t, addrB) + addrA := dhtA.peerstore.Addresses(dhtA.self)[0] + addrB := dhtB.peerstore.Addresses(dhtB.self)[0] peerA := dhtA.self peerB := dhtB.self @@ -674,7 +648,7 @@ func TestConnectCollision(t *testing.T) { dhtA.Close() dhtB.Close() - dhtA.network.Close() - dhtB.network.Close() + dhtA.host.Close() + dhtB.host.Close() } } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index f76f5bddc..2be8127c7 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -1,6 +1,8 @@ package dht import ( + "io" + "io/ioutil" "math/rand" "testing" @@ -29,13 +31,20 @@ func TestGetFailures(t *testing.T) { if err != nil { t.Fatal(err) } - nets := mn.Nets() + hosts := mn.Hosts() peers := mn.Peers() tsds := dssync.MutexWrap(ds.NewMapDatastore()) - d := NewDHT(ctx, peers[0], nets[0], tsds) + d := NewDHT(ctx, hosts[0], tsds) d.Update(ctx, peers[1]) + // u.POut("NotFound Test\n") + // Reply with failures to every message + hosts[1].SetStreamHandler(ProtocolDHT, func(s inet.Stream) { + defer s.Close() + io.Copy(ioutil.Discard, s) + }) + // This one should time out // u.POut("Timout Test\n") ctx1, _ := context.WithTimeout(context.Background(), time.Second) @@ -50,7 +59,7 @@ func TestGetFailures(t *testing.T) { t.Log("Timeout test passed.") // Reply with failures to every message - nets[1].SetHandler(inet.ProtocolDHT, func(s inet.Stream) { + hosts[1].SetStreamHandler(ProtocolDHT, func(s inet.Stream) { defer s.Close() pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) @@ -97,7 +106,7 @@ func TestGetFailures(t *testing.T) { } // u.POut("handleGetValue Test\n") - s, err := nets[1].NewStream(inet.ProtocolDHT, peers[0]) + s, err := hosts[1].NewStream(ProtocolDHT, hosts[0].ID()) if err != nil { t.Fatal(err) } @@ -133,19 +142,19 @@ func TestNotFound(t *testing.T) { if err != nil { t.Fatal(err) } - nets := mn.Nets() + hosts := mn.Hosts() peers := mn.Peers() tsds := dssync.MutexWrap(ds.NewMapDatastore()) - d := NewDHT(ctx, peers[0], nets[0], tsds) + d := NewDHT(ctx, hosts[0], tsds) for _, p := range peers { d.Update(ctx, p) } // Reply with random peers to every message - for _, neti := range nets { - neti := neti // shadow loop var - neti.SetHandler(inet.ProtocolDHT, func(s inet.Stream) { + for _, host := range hosts { + host := host // shadow loop var + host.SetStreamHandler(ProtocolDHT, func(s inet.Stream) { defer s.Close() pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) @@ -163,11 +172,11 @@ func TestNotFound(t *testing.T) { ps := []peer.PeerInfo{} for i := 0; i < 7; i++ { p := peers[rand.Intn(len(peers))] - pi := neti.Peerstore().PeerInfo(p) + pi := host.Peerstore().PeerInfo(p) ps = append(ps, pi) } - resp.CloserPeers = pb.PeerInfosToPBPeers(d.network, ps) + resp.CloserPeers = pb.PeerInfosToPBPeers(d.host.Network(), ps) if err := pbw.WriteMsg(resp); err != nil { panic(err) } @@ -205,20 +214,20 @@ func TestLessThanKResponses(t *testing.T) { if err != nil { t.Fatal(err) } - nets := mn.Nets() + hosts := mn.Hosts() peers := mn.Peers() tsds := dssync.MutexWrap(ds.NewMapDatastore()) - d := NewDHT(ctx, peers[0], nets[0], tsds) + d := NewDHT(ctx, hosts[0], tsds) for i := 1; i < 5; i++ { d.Update(ctx, peers[i]) } // Reply with random peers to every message - for _, neti := range nets { - neti := neti // shadow loop var - neti.SetHandler(inet.ProtocolDHT, func(s inet.Stream) { + for _, host := range hosts { + host := host // shadow loop var + host.SetStreamHandler(ProtocolDHT, func(s inet.Stream) { defer s.Close() pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) @@ -231,10 +240,10 @@ func TestLessThanKResponses(t *testing.T) { switch pmes.GetType() { case pb.Message_GET_VALUE: - pi := neti.Peerstore().PeerInfo(peers[1]) + pi := host.Peerstore().PeerInfo(peers[1]) resp := &pb.Message{ Type: pmes.Type, - CloserPeers: pb.PeerInfosToPBPeers(d.network, []peer.PeerInfo{pi}), + CloserPeers: pb.PeerInfosToPBPeers(d.host.Network(), []peer.PeerInfo{pi}), } if err := pbw.WriteMsg(resp); err != nil { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index a02eb024d..3670c570d 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -89,7 +89,7 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Mess provinfos := peer.PeerInfos(dht.peerstore, provs) if len(provs) > 0 { log.Debugf("handleGetValue returning %d provider[s]", len(provs)) - resp.ProviderPeers = pb.PeerInfosToPBPeers(dht.network, provinfos) + resp.ProviderPeers = pb.PeerInfosToPBPeers(dht.host.Network(), provinfos) } // Find closest peer on given cluster to desired key and reply with that info @@ -106,7 +106,7 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Mess } } - resp.CloserPeers = pb.PeerInfosToPBPeers(dht.network, closerinfos) + resp.CloserPeers = pb.PeerInfosToPBPeers(dht.host.Network(), closerinfos) } return resp, nil @@ -161,7 +161,7 @@ func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.ID, pmes *pb.Mess } } - resp.CloserPeers = pb.PeerInfosToPBPeers(dht.network, withAddresses) + resp.CloserPeers = pb.PeerInfosToPBPeers(dht.host.Network(), withAddresses) return resp, nil } @@ -185,14 +185,14 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb. if providers != nil && len(providers) > 0 { infos := peer.PeerInfos(dht.peerstore, providers) - resp.ProviderPeers = pb.PeerInfosToPBPeers(dht.network, infos) + resp.ProviderPeers = pb.PeerInfosToPBPeers(dht.host.Network(), infos) } // Also send closer peers. closer := dht.betterPeersToQuery(pmes, p, CloserPeerCount) if closer != nil { infos := peer.PeerInfos(dht.peerstore, providers) - resp.CloserPeers = pb.PeerInfosToPBPeers(dht.network, infos) + resp.CloserPeers = pb.PeerInfosToPBPeers(dht.host.Network(), infos) } return resp, nil diff --git a/routing/dht/query.go b/routing/dht/query.go index 5b62a8f4c..0056bee1d 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -3,7 +3,6 @@ package dht import ( "sync" - inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" queue "github.com/jbenet/go-ipfs/p2p/peer/queue" "github.com/jbenet/go-ipfs/routing" @@ -18,17 +17,10 @@ import ( var maxQueryConcurrency = AlphaValue type dhtQuery struct { - // the key we're querying for - key u.Key - - // dialer used to ensure we're connected to peers - dialer inet.Dialer - - // the function to execute per peer - qfunc queryFunc - - // the concurrency parameter - concurrency int + dht *IpfsDHT + key u.Key // the key we're querying for + qfunc queryFunc // the function to execute per peer + concurrency int // the concurrency parameter } type dhtQueryResult struct { @@ -40,10 +32,10 @@ type dhtQueryResult struct { } // constructs query -func newQuery(k u.Key, d inet.Dialer, f queryFunc) *dhtQuery { +func (dht *IpfsDHT) newQuery(k u.Key, f queryFunc) *dhtQuery { return &dhtQuery{ key: k, - dialer: d, + dht: dht, qfunc: f, concurrency: maxQueryConcurrency, } @@ -155,7 +147,7 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { func (r *dhtQueryRunner) addPeerToQuery(ctx context.Context, next peer.ID) { // if new peer is ourselves... - if next == r.query.dialer.LocalPeer() { + if next == r.query.dht.self { return } @@ -222,10 +214,11 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { }() // make sure we're connected to the peer. - if conns := r.query.dialer.ConnsToPeer(p); len(conns) == 0 { + if conns := r.query.dht.host.Network().ConnsToPeer(p); len(conns) == 0 { log.Infof("worker for: %v -- not connected. dial start", p) - if err := r.query.dialer.DialPeer(cg.Context(), p); err != nil { + pi := peer.PeerInfo{ID: p} + if err := r.query.dht.host.Connect(cg.Context(), pi); err != nil { log.Debugf("ERROR worker for: %v -- err connecting: %v", p, err) r.Lock() r.errs = append(r.errs, err) @@ -257,12 +250,7 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { log.Debugf("PEERS CLOSER -- worker for: %v (%d closer peers)", p, len(res.closerPeers)) for _, next := range res.closerPeers { // add their addresses to the dialer's peerstore - conns := r.query.dialer.ConnsToPeer(next.ID) - if len(conns) == 0 { - log.Infof("PEERS CLOSER -- worker for %v FOUND NEW PEER: %s %s", p, next.ID, next.Addrs) - } - - r.query.dialer.Peerstore().AddAddresses(next.ID, next.Addrs) + r.query.dht.peerstore.AddPeerInfo(next) r.addPeerToQuery(cg.Context(), next.ID) log.Debugf("PEERS CLOSER -- worker for: %v added %v (%v)", p, next.ID, next.Addrs) } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 4c3cf160b..2f00929b6 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -82,7 +82,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { } // setup the Query - query := newQuery(key, dht.network, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { + query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { val, peers, err := dht.getValueOrPeers(ctx, p, key) if err != nil { @@ -170,7 +170,7 @@ func (dht *IpfsDHT) getClosestPeers(ctx context.Context, key u.Key) (<-chan peer peerset.Add(p) } - query := newQuery(key, dht.network, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { + query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { closer, err := dht.closerPeersSingle(ctx, key, p) if err != nil { log.Errorf("error getting closer peers: %s", err) @@ -253,7 +253,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co } // setup the Query - query := newQuery(key, dht.network, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { + query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { pmes, err := dht.findProvidersSingle(ctx, p, key) if err != nil { @@ -312,7 +312,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, er } // setup the Query - query := newQuery(u.Key(id), dht.network, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { + query := dht.newQuery(u.Key(id), func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { pmes, err := dht.findPeerSingle(ctx, p, id) if err != nil { @@ -361,7 +361,7 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< } // setup the Query - query := newQuery(u.Key(id), dht.network, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { + query := dht.newQuery(u.Key(id), func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { pmes, err := dht.findPeerSingle(ctx, p, id) if err != nil { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 1f0340ebb..2235970f5 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -27,12 +27,12 @@ func (rs *mocknetserver) ClientWithDatastore(ctx context.Context, p testutil.Ide // FIXME AddPeer doesn't appear to be idempotent - net, err := rs.mn.AddPeer(p.PrivateKey(), p.Address()) + host, err := rs.mn.AddPeer(p.PrivateKey(), p.Address()) if err != nil { panic("FIXME") // return nil, debugerror.Wrap(err) } - return dht.NewDHT(ctx, p.ID(), net, sync.MutexWrap(ds)) + return dht.NewDHT(ctx, host, sync.MutexWrap(ds)) } var _ Server = &mocknetserver{} From 7dc49e50e4294b5f8c8ed3f944e99044a46743f7 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 2 Jan 2015 05:40:38 -0800 Subject: [PATCH 0581/3817] core: rearranged initialization a bit This commit was moved from ipfs/go-namesys@066e93c490f47971584dfebb2ef85ff5366d9e9f --- namesys/routing.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/namesys/routing.go b/namesys/routing.go index 709f9424c..b57d2c601 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -23,6 +23,10 @@ type routingResolver struct { // NewRoutingResolver constructs a name resolver using the IPFS Routing system // to implement SFS-like naming on top. func NewRoutingResolver(route routing.IpfsRouting) Resolver { + if route == nil { + panic("attempt to create resolver with nil routing system") + } + return &routingResolver{routing: route} } From cccc64d90ae3a4c3a547c39d7ac591555070cff2 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 3 Jan 2015 00:29:49 -0800 Subject: [PATCH 0582/3817] dht: debug dont cast Key as peer.ID This commit was moved from ipfs/go-ipfs-routing@5e76c5327fd995c211d167249808b7e1b6cc54d5 --- routing/dht/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 3670c570d..546939ca0 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -206,7 +206,7 @@ type providerInfo struct { func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { key := u.Key(pmes.GetKey()) - log.Debugf("%s adding %s as a provider for '%s'\n", dht.self, p, peer.ID(key)) + log.Debugf("%s adding %s as a provider for '%s'\n", dht.self, p, key) // add provider should use the address given in the message pinfos := pb.PBPeersToPeerInfos(pmes.GetProviderPeers()) From 6ca81f657c96b6221e5867f760ddc74b9f67b93b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 3 Jan 2015 00:56:27 -0800 Subject: [PATCH 0583/3817] dht: some provider debug logging This commit was moved from ipfs/go-ipfs-routing@c4b467a71b11b94f2090566c01960d12cc267ccc --- routing/dht/handlers.go | 15 +++++++++++---- routing/dht/routing.go | 11 +++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 546939ca0..491176550 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -167,25 +167,31 @@ func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.ID, pmes *pb.Mess func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) + key := u.Key(pmes.GetKey()) + + // debug logging niceness. + reqDesc := fmt.Sprintf("%s handleGetProviders(%s, %s): ", dht.self, p, key) + log.Debugf("%s begin", reqDesc) + defer log.Debugf("%s end", reqDesc) // check if we have this value, to add ourselves as provider. - log.Debugf("handling GetProviders: '%s'", u.Key(pmes.GetKey())) - dsk := u.Key(pmes.GetKey()).DsKey() - has, err := dht.datastore.Has(dsk) + has, err := dht.datastore.Has(key.DsKey()) if err != nil && err != ds.ErrNotFound { log.Errorf("unexpected datastore error: %v\n", err) has = false } // setup providers - providers := dht.providers.GetProviders(ctx, u.Key(pmes.GetKey())) + providers := dht.providers.GetProviders(ctx, key) if has { providers = append(providers, dht.self) + log.Debugf("%s have the value. added self as provider", reqDesc) } if providers != nil && len(providers) > 0 { infos := peer.PeerInfos(dht.peerstore, providers) resp.ProviderPeers = pb.PeerInfosToPBPeers(dht.host.Network(), infos) + log.Debugf("%s have %d providers: %s", reqDesc, len(providers), infos) } // Also send closer peers. @@ -193,6 +199,7 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb. if closer != nil { infos := peer.PeerInfos(dht.peerstore, providers) resp.CloserPeers = pb.PeerInfosToPBPeers(dht.host.Network(), infos) + log.Debugf("%s have %d closer peers: %s", reqDesc, len(closer), infos) } return resp, nil diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 2f00929b6..ec414de13 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -1,6 +1,7 @@ package dht import ( + "fmt" "math" "sync" @@ -255,16 +256,24 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co // setup the Query query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { + reqDesc := fmt.Sprintf("%s findProviders(%s).Query(%s): ", dht.self, key, p) + log.Debugf("%s begin", reqDesc) + defer log.Debugf("%s end", reqDesc) + pmes, err := dht.findProvidersSingle(ctx, p, key) if err != nil { return nil, err } + log.Debugf("%s got %d provider entries", reqDesc, len(pmes.GetProviderPeers())) provs := pb.PBPeersToPeerInfos(pmes.GetProviderPeers()) + log.Debugf("%s got %d provider entries decoded", reqDesc, len(provs)) // Add unique providers from request, up to 'count' for _, prov := range provs { + log.Debugf("%s got provider: %s", reqDesc, prov) if ps.TryAdd(prov.ID) { + log.Debugf("%s using provider: %s", reqDesc, prov) select { case peerOut <- prov: case <-ctx.Done(): @@ -273,6 +282,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co } } if ps.Size() >= count { + log.Debugf("%s got enough providers (%d/%d)", reqDesc, ps.Size(), count) return &dhtQueryResult{success: true}, nil } } @@ -280,6 +290,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co // Give closer peers back to the query to be queried closer := pmes.GetCloserPeers() clpeers := pb.PBPeersToPeerInfos(closer) + log.Debugf("%s got closer peers: %s", reqDesc, clpeers) return &dhtQueryResult{closerPeers: clpeers}, nil }) From 8931276762fd4ab29e450816188ce588a14079c5 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 3 Jan 2015 06:15:50 -0800 Subject: [PATCH 0584/3817] bitswap debug logging This commit was moved from ipfs/go-ipfs-routing@872485c82ecbe1eeb2092b60cfb5d007fa9a328d --- routing/dht/dht_net.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index fd088e02c..2b857ce2b 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -87,15 +87,11 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message start := time.Now() - log.Debugf("%s writing", dht.self) if err := w.WriteMsg(pmes); err != nil { return nil, err } log.Event(ctx, "dhtSentMessage", dht.self, p, pmes) - log.Debugf("%s reading", dht.self) - defer log.Debugf("%s done", dht.self) - rpmes := new(pb.Message) if err := r.ReadMsg(rpmes); err != nil { return nil, err @@ -125,12 +121,10 @@ func (dht *IpfsDHT) sendMessage(ctx context.Context, p peer.ID, pmes *pb.Message cw := ctxutil.NewWriter(ctx, s) // ok to use. we defer close stream in this func w := ggio.NewDelimitedWriter(cw) - log.Debugf("%s writing", dht.self) if err := w.WriteMsg(pmes); err != nil { return err } log.Event(ctx, "dhtSentMessage", dht.self, p, pmes) - log.Debugf("%s done", dht.self) return nil } From dc4bc8e849e61115cb44f5e4ad8afa2bc7db31ac Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 3 Jan 2015 08:54:36 -0800 Subject: [PATCH 0585/3817] bitswap and dht: lots of debugging logs This commit was moved from ipfs/go-ipfs-routing@06d45460b5d06453d56e4d0ff9e4f2f172cc597c --- routing/dht/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 491176550..8f66afbf6 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -148,7 +148,7 @@ func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.ID, pmes *pb.Mess } if closest == nil { - log.Warningf("handleFindPeer: could not find anything.") + log.Warningf("%s handleFindPeer %s: could not find anything.", dht.self, p) return resp, nil } From fdad1332db86b57fc4ef14c155db32265a5994cf Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 3 Jan 2015 06:16:19 -0800 Subject: [PATCH 0586/3817] merkledag: LONG timeout on Get we shouldn't use an arbitrary timeout here. since Get doesnt take in a context yet, we give a large upper bound. think of an http request. we want it to go on as long as the client requests it. This commit was moved from ipfs/go-merkledag@339d2a983d4e0fa4b39a308373afebf11ccbd64d --- ipld/merkledag/merkledag.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 9a638ca2a..c9ea00ad2 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -229,7 +229,11 @@ func (n *dagService) Get(k u.Key) (*Node, error) { return nil, fmt.Errorf("dagService is nil") } - ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) + ctx, _ := context.WithTimeout(context.TODO(), time.Minute) + // we shouldn't use an arbitrary timeout here. + // since Get doesnt take in a context yet, we give a large upper bound. + // think of an http request. we want it to go on as long as the client requests it. + b, err := n.Blocks.GetBlock(ctx, k) if err != nil { return nil, err From ee25dde6dfcbef941d40d0bd179787a984d3332e Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 4 Jan 2015 18:18:16 -0800 Subject: [PATCH 0587/3817] dht: extend duration of TestGetFailures TestGetFailures may just be operating very slowly, instead of completely failing. Right now it gets caught on travis often. not sure if its actually wrong. This commit was moved from ipfs/go-ipfs-routing@8087d59bfe31e0cdcd69f8e4595cf29ee90e6e06 --- routing/dht/ext_test.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 2be8127c7..3da0dfa3a 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -47,7 +47,7 @@ func TestGetFailures(t *testing.T) { // This one should time out // u.POut("Timout Test\n") - ctx1, _ := context.WithTimeout(context.Background(), time.Second) + ctx1, _ := context.WithTimeout(context.Background(), 200*time.Millisecond) if _, err := d.GetValue(ctx1, u.Key("test")); err != nil { if err != context.DeadlineExceeded { t.Fatal("Got different error than we expected", err) @@ -78,8 +78,12 @@ func TestGetFailures(t *testing.T) { } }) - // This one should fail with NotFound - ctx2, _ := context.WithTimeout(context.Background(), 3*time.Second) + // This one should fail with NotFound. + // long context timeout to ensure we dont end too early. + // the dht should be exhausting its query and returning not found. + // (was 3 seconds before which should be _plenty_ of time, but maybe + // travis machines really have a hard time...) + ctx2, _ := context.WithTimeout(context.Background(), 20*time.Second) _, err = d.GetValue(ctx2, u.Key("test")) if err != nil { if err != routing.ErrNotFound { @@ -187,7 +191,8 @@ func TestNotFound(t *testing.T) { }) } - ctx, _ = context.WithTimeout(ctx, time.Second*5) + // long timeout to ensure timing is not at play. + ctx, _ = context.WithTimeout(ctx, time.Second*20) v, err := d.GetValue(ctx, u.Key("hello")) log.Debugf("get value got %v", v) if err != nil { From 6650d59f8968ddd1a57d3136c54e248e0fcf8a8f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 5 Jan 2015 04:36:27 -0800 Subject: [PATCH 0588/3817] ext_test: bitten by mocknet ordering mocknet indeterminism screwed this test up. that's twice it's bitten us. let's not let it do it a third time. cc @briantigerchow omg. This commit was moved from ipfs/go-ipfs-routing@e9d3734a97b023dfbc2b1973379f4424db34952a --- routing/dht/ext_test.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 3da0dfa3a..77ea54c96 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -32,11 +32,10 @@ func TestGetFailures(t *testing.T) { t.Fatal(err) } hosts := mn.Hosts() - peers := mn.Peers() tsds := dssync.MutexWrap(ds.NewMapDatastore()) d := NewDHT(ctx, hosts[0], tsds) - d.Update(ctx, peers[1]) + d.Update(ctx, hosts[1].ID()) // u.POut("NotFound Test\n") // Reply with failures to every message @@ -147,12 +146,11 @@ func TestNotFound(t *testing.T) { t.Fatal(err) } hosts := mn.Hosts() - peers := mn.Peers() tsds := dssync.MutexWrap(ds.NewMapDatastore()) d := NewDHT(ctx, hosts[0], tsds) - for _, p := range peers { - d.Update(ctx, p) + for _, p := range hosts { + d.Update(ctx, p.ID()) } // Reply with random peers to every message @@ -175,7 +173,7 @@ func TestNotFound(t *testing.T) { ps := []peer.PeerInfo{} for i := 0; i < 7; i++ { - p := peers[rand.Intn(len(peers))] + p := hosts[rand.Intn(len(hosts))].ID() pi := host.Peerstore().PeerInfo(p) ps = append(ps, pi) } @@ -220,13 +218,12 @@ func TestLessThanKResponses(t *testing.T) { t.Fatal(err) } hosts := mn.Hosts() - peers := mn.Peers() tsds := dssync.MutexWrap(ds.NewMapDatastore()) d := NewDHT(ctx, hosts[0], tsds) for i := 1; i < 5; i++ { - d.Update(ctx, peers[i]) + d.Update(ctx, hosts[i].ID()) } // Reply with random peers to every message @@ -245,7 +242,7 @@ func TestLessThanKResponses(t *testing.T) { switch pmes.GetType() { case pb.Message_GET_VALUE: - pi := host.Peerstore().PeerInfo(peers[1]) + pi := host.Peerstore().PeerInfo(hosts[1].ID()) resp := &pb.Message{ Type: pmes.Type, CloserPeers: pb.PeerInfosToPBPeers(d.host.Network(), []peer.PeerInfo{pi}), From 184ad5a8bcfd7d205c602d7354ffb8953a15e8a0 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 5 Jan 2015 04:48:50 -0800 Subject: [PATCH 0589/3817] dht: key without record validator func This is causing test failures because tests don't usually have "/-/-" format. we can decide whether or not to allow keys without validators, but for now removing. cc @whyrusleeping This commit was moved from ipfs/go-ipfs-routing@58aaa6a60d202a0b1e30f7abf0b0960baf395890 --- routing/dht/records.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/records.go b/routing/dht/records.go index 0791f80a3..083eeb26e 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -191,8 +191,8 @@ func (dht *IpfsDHT) verifyRecord(r *pb.Record, pk ci.PubKey) error { // Now, check validity func parts := strings.Split(r.GetKey(), "/") if len(parts) < 3 { - log.Errorf("Record had bad key: %s", u.Key(r.GetKey())) - return ErrBadRecord + log.Infof("Record key does not have validator: %s", u.Key(r.GetKey())) + return nil } fnc, ok := dht.Validators[parts[1]] From a855bf709a87a652caa4f50ba1a9f4cdc784481e Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 5 Jan 2015 04:35:54 -0800 Subject: [PATCH 0590/3817] dht: even more logging. This commit was moved from ipfs/go-ipfs-routing@577fc6fae10fe60dd93da392ff7064743c346296 --- routing/dht/dht.go | 5 +++ routing/dht/query.go | 73 ++++++++++++++++++++++-------------------- routing/dht/routing.go | 46 +++++++++++++++----------- 3 files changed, 72 insertions(+), 52 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 2a576629a..17d300d87 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -89,6 +89,11 @@ func (dht *IpfsDHT) LocalPeer() peer.ID { return dht.self } +// log returns the dht's logger +func (dht *IpfsDHT) log() eventlog.EventLogger { + return log.Prefix("dht(%s)", dht.self) +} + // Connect to a new peer at the given address, ping and add to the routing table func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.ID) error { // TODO: change interface to accept a PeerInfo as well. diff --git a/routing/dht/query.go b/routing/dht/query.go index 0056bee1d..44dc49926 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -7,6 +7,7 @@ import ( queue "github.com/jbenet/go-ipfs/p2p/peer/queue" "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" + eventlog "github.com/jbenet/go-ipfs/util/eventlog" pset "github.com/jbenet/go-ipfs/util/peerset" todoctr "github.com/jbenet/go-ipfs/util/todocounter" @@ -55,32 +56,18 @@ func (q *dhtQuery) Run(ctx context.Context, peers []peer.ID) (*dhtQueryResult, e } type dhtQueryRunner struct { + query *dhtQuery // query to run + peersSeen *pset.PeerSet // all peers queried. prevent querying same peer 2x + peersToQuery *queue.ChanQueue // peers remaining to be queried + peersRemaining todoctr.Counter // peersToQuery + currently processing - // the query to run - query *dhtQuery + result *dhtQueryResult // query result + errs []error // result errors. maybe should be a map[peer.ID]error - // peersToQuery is a list of peers remaining to query - peersToQuery *queue.ChanQueue + rateLimit chan struct{} // processing semaphore + log eventlog.EventLogger - // peersSeen are all the peers queried. used to prevent querying same peer 2x - peersSeen *pset.PeerSet - - // rateLimit is a channel used to rate limit our processing (semaphore) - rateLimit chan struct{} - - // peersRemaining is a counter of peers remaining (toQuery + processing) - peersRemaining todoctr.Counter - - // context group cg ctxgroup.ContextGroup - - // result - result *dhtQueryResult - - // result errors - errs []error - - // lock for concurrent access to fields sync.RWMutex } @@ -96,6 +83,11 @@ func newQueryRunner(ctx context.Context, q *dhtQuery) *dhtQueryRunner { } func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { + log := log.Prefix("dht(%s).Query(%s).Run(%d)", r.query.dht.self, r.query.key, len(peers)) + r.log = log + log.Debug("enter") + defer log.Debug("end") + log.Debugf("Run query with %d peers.", len(peers)) if len(peers) == 0 { log.Warning("Running query with no peers!") @@ -115,6 +107,7 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { // go do this thing. // do it as a child func to make sure Run exits // ONLY AFTER spawn workers has exited. + log.Debugf("go spawn workers") r.cg.AddChildFunc(r.spawnWorkers) // so workers are working. @@ -124,41 +117,45 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { select { case <-r.peersRemaining.Done(): + log.Debug("all peers ended") r.cg.Close() r.RLock() defer r.RUnlock() if len(r.errs) > 0 { - err = r.errs[0] + err = r.errs[0] // take the first? } case <-r.cg.Closed(): + log.Debug("r.cg.Closed()") + r.RLock() defer r.RUnlock() err = r.cg.Context().Err() // collect the error. } if r.result != nil && r.result.success { + log.Debug("success: %s", r.result) return r.result, nil } + log.Debug("failure: %s", err) return nil, err } func (r *dhtQueryRunner) addPeerToQuery(ctx context.Context, next peer.ID) { // if new peer is ourselves... if next == r.query.dht.self { + r.log.Debug("addPeerToQuery skip self") return } if !r.peersSeen.TryAdd(next) { - log.Debug("query peer was already seen") + r.log.Debugf("addPeerToQuery skip seen %s", next) return } - log.Debugf("adding peer to query: %v", next) - - // do this after unlocking to prevent possible deadlocks. + r.log.Debugf("addPeerToQuery adding %s", next) r.peersRemaining.Increment(1) select { case r.peersToQuery.EnqChan <- next: @@ -167,6 +164,10 @@ func (r *dhtQueryRunner) addPeerToQuery(ctx context.Context, next peer.ID) { } func (r *dhtQueryRunner) spawnWorkers(parent ctxgroup.ContextGroup) { + log := r.log.Prefix("spawnWorkers") + log.Debugf("begin") + defer log.Debugf("end") + for { select { @@ -192,7 +193,9 @@ func (r *dhtQueryRunner) spawnWorkers(parent ctxgroup.ContextGroup) { } func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { - log.Debugf("spawned worker for: %v", p) + log := r.log.Prefix("queryPeer(%s)", p) + log.Debugf("spawned") + defer log.Debugf("finished") // make sure we rate limit concurrency. select { @@ -203,34 +206,36 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { } // ok let's do this! - log.Debugf("running worker for: %v", p) + log.Debugf("running") // make sure we do this when we exit defer func() { // signal we're done proccessing peer p - log.Debugf("completing worker for: %v", p) + log.Debugf("completed") r.peersRemaining.Decrement(1) r.rateLimit <- struct{}{} }() // make sure we're connected to the peer. if conns := r.query.dht.host.Network().ConnsToPeer(p); len(conns) == 0 { - log.Infof("worker for: %v -- not connected. dial start", p) + log.Infof("not connected. dialing.") pi := peer.PeerInfo{ID: p} if err := r.query.dht.host.Connect(cg.Context(), pi); err != nil { - log.Debugf("ERROR worker for: %v -- err connecting: %v", p, err) + log.Debugf("Error connecting: %s", err) r.Lock() r.errs = append(r.errs, err) r.Unlock() return } - log.Infof("worker for: %v -- not connected. dial success!", p) + log.Debugf("connected. dial success.") } // finally, run the query against this peer + log.Debugf("query running") res, err := r.query.qfunc(cg.Context(), p) + log.Debugf("query finished") if err != nil { log.Debugf("ERROR worker for: %v %v", p, err) @@ -239,7 +244,7 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { r.Unlock() } else if res.success { - log.Debugf("SUCCESS worker for: %v", p, res) + log.Debugf("SUCCESS worker for: %v %s", p, res) r.Lock() r.result = res r.Unlock() diff --git a/routing/dht/routing.go b/routing/dht/routing.go index ec414de13..5978a9a80 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -1,7 +1,6 @@ package dht import ( - "fmt" "math" "sync" @@ -66,25 +65,29 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error // If the search does not succeed, a multiaddr string of a closer peer is // returned along with util.ErrSearchIncomplete func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { - log.Debugf("Get Value [%s]", key) + log := dht.log().Prefix("GetValue(%s)", key) + log.Debugf("start") + defer log.Debugf("end") // If we have it local, dont bother doing an RPC! val, err := dht.getLocal(key) if err == nil { - log.Debug("Got value locally!") + log.Debug("have it locally") return val, nil } // get closest peers in the routing table + rtp := dht.routingTable.ListPeers() + log.Debugf("peers in rt: %s", len(rtp), rtp) + closest := dht.routingTable.NearestPeers(kb.ConvertKey(key), PoolSize) if closest == nil || len(closest) == 0 { - log.Warning("Got no peers back from routing table!") + log.Warning("No peers from routing table!") return nil, errors.Wrap(kb.ErrLookupFailure) } // setup the Query query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { - val, peers, err := dht.getValueOrPeers(ctx, p, key) if err != nil { return nil, err @@ -117,9 +120,13 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { // Provide makes this node announce that it can provide a value for the given key func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { - + log := dht.log().Prefix("Provide(%s)", key) + log.Debugf("start", key) log.Event(ctx, "provideBegin", &key) + defer log.Debugf("end", key) defer log.Event(ctx, "provideEnd", &key) + + // add self locally dht.providers.AddProvider(key, dht.self) peers, err := dht.getClosestPeers(ctx, key) @@ -132,6 +139,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { wg.Add(1) go func(p peer.ID) { defer wg.Done() + log.Debugf("putProvider(%s, %s)", key, p) err := dht.putProvider(ctx, p, string(key)) if err != nil { log.Error(err) @@ -231,9 +239,12 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int } func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, count int, peerOut chan peer.PeerInfo) { + log := dht.log().Prefix("FindProviders(%s)", key) + defer close(peerOut) defer log.Event(ctx, "findProviders end", &key) - log.Debugf("%s FindProviders %s", dht.self, key) + log.Debug("begin") + defer log.Debug("begin") ps := pset.NewLimited(count) provs := dht.providers.GetProviders(ctx, key) @@ -255,25 +266,24 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co // setup the Query query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { - - reqDesc := fmt.Sprintf("%s findProviders(%s).Query(%s): ", dht.self, key, p) - log.Debugf("%s begin", reqDesc) - defer log.Debugf("%s end", reqDesc) + log := log.Prefix("Query(%s)", p) + log.Debugf("begin") + defer log.Debugf("end") pmes, err := dht.findProvidersSingle(ctx, p, key) if err != nil { return nil, err } - log.Debugf("%s got %d provider entries", reqDesc, len(pmes.GetProviderPeers())) + log.Debugf("%d provider entries", len(pmes.GetProviderPeers())) provs := pb.PBPeersToPeerInfos(pmes.GetProviderPeers()) - log.Debugf("%s got %d provider entries decoded", reqDesc, len(provs)) + log.Debugf("%d provider entries decoded", len(provs)) // Add unique providers from request, up to 'count' for _, prov := range provs { - log.Debugf("%s got provider: %s", reqDesc, prov) + log.Debugf("got provider: %s", prov) if ps.TryAdd(prov.ID) { - log.Debugf("%s using provider: %s", reqDesc, prov) + log.Debugf("using provider: %s", prov) select { case peerOut <- prov: case <-ctx.Done(): @@ -282,7 +292,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co } } if ps.Size() >= count { - log.Debugf("%s got enough providers (%d/%d)", reqDesc, ps.Size(), count) + log.Debugf("got enough providers (%d/%d)", ps.Size(), count) return &dhtQueryResult{success: true}, nil } } @@ -290,14 +300,14 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co // Give closer peers back to the query to be queried closer := pmes.GetCloserPeers() clpeers := pb.PBPeersToPeerInfos(closer) - log.Debugf("%s got closer peers: %s", reqDesc, clpeers) + log.Debugf("got closer peers: %d %s", len(clpeers), clpeers) return &dhtQueryResult{closerPeers: clpeers}, nil }) peers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) _, err := query.Run(ctx, peers) if err != nil { - log.Errorf("FindProviders Query error: %s", err) + log.Errorf("Query error: %s", err) } } From 76ca1a513def5442d56d55538a230e2a857a9fc2 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 5 Jan 2015 04:48:37 -0800 Subject: [PATCH 0591/3817] dht test skips This commit was moved from ipfs/go-ipfs-routing@9fbf3ebd32d33151bb320e7b1c90d3554764755b --- routing/dht/dht_test.go | 5 +++++ routing/dht/ext_test.go | 2 ++ 2 files changed, 7 insertions(+) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 133f7a27c..147970695 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -231,6 +231,7 @@ func TestProvides(t *testing.T) { } func TestBootstrap(t *testing.T) { + // t.Skip("skipping test to debug another") if testing.Short() { t.SkipNow() } @@ -388,6 +389,7 @@ func TestProvidesMany(t *testing.T) { } func TestProvidesAsync(t *testing.T) { + // t.Skip("skipping test to debug another") if testing.Short() { t.SkipNow() } @@ -442,6 +444,7 @@ func TestProvidesAsync(t *testing.T) { } func TestLayeredGet(t *testing.T) { + // t.Skip("skipping test to debug another") if testing.Short() { t.SkipNow() } @@ -482,6 +485,7 @@ func TestLayeredGet(t *testing.T) { } func TestFindPeer(t *testing.T) { + // t.Skip("skipping test to debug another") if testing.Short() { t.SkipNow() } @@ -596,6 +600,7 @@ func testPeerListsMatch(t *testing.T, p1, p2 []peer.ID) { } func TestConnectCollision(t *testing.T) { + // t.Skip("skipping test to debug another") if testing.Short() { t.SkipNow() } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 77ea54c96..6f12c3113 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -136,6 +136,7 @@ func TestGetFailures(t *testing.T) { } func TestNotFound(t *testing.T) { + // t.Skip("skipping test to debug another") if testing.Short() { t.SkipNow() } @@ -210,6 +211,7 @@ func TestNotFound(t *testing.T) { // If less than K nodes are in the entire network, it should fail when we make // a GET rpc and nobody has the value func TestLessThanKResponses(t *testing.T) { + // t.Skip("skipping test to debug another") // t.Skip("skipping test because it makes a lot of output") ctx := context.Background() From c5cd0f1e26097686d5d8211eb367e4faa34cd7d7 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 4 Jan 2015 14:06:53 -0800 Subject: [PATCH 0592/3817] testutil: obvious names for seeded key pairs This commit was moved from ipfs/go-namesys@ea639d5d9d50f7cbfa9e9bc13a1170ccbc050790 --- namesys/resolve_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 35851fc32..8e3214dfe 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -14,7 +14,7 @@ func TestRoutingResolve(t *testing.T) { resolver := NewRoutingResolver(d) publisher := NewRoutingPublisher(d) - privk, pubk, err := testutil.RandKeyPair(512) + privk, pubk, err := testutil.RandTestKeyPair(512) if err != nil { t.Fatal(err) } From cff2f3ebdb0607cc567776de406befe753e2e04b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 5 Jan 2015 05:21:05 -0800 Subject: [PATCH 0593/3817] p2p/test: bogus key pair for faster tests This commit was moved from ipfs/go-blockservice@c356c710a6c4c79f45315a3ddd136c90769af594 --- blockservice/mock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/mock.go b/blockservice/mock.go index 57432178e..73fcdf2fc 100644 --- a/blockservice/mock.go +++ b/blockservice/mock.go @@ -12,7 +12,7 @@ import ( // Mocks returns |n| connected mock Blockservices func Mocks(t *testing.T, n int) []*BlockService { net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(0)) - sg := bitswap.NewSessionGenerator(net) + sg := bitswap.NewTestSessionGenerator(net) instances := sg.Instances(n) From f013aec51c7ff39ff02cca805fd2d3cdf436fb14 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 8 Dec 2014 18:21:57 -0800 Subject: [PATCH 0594/3817] feat(core) dht.Bootstrap License: MIT Signed-off-by: Brian Tiger Chow This commit was moved from ipfs/go-ipfs-routing@b715725d85b210514d302176af588b4fc61b58f9 --- routing/dht/dht.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 17d300d87..3fdd327f9 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -341,7 +341,7 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { } // Bootstrap builds up list of peers by requesting random peer IDs -func (dht *IpfsDHT) Bootstrap(ctx context.Context, queries int) { +func (dht *IpfsDHT) Bootstrap(ctx context.Context, queries int) error { // bootstrap sequentially, as results will compound for i := 0; i < NumBootstrapQueries; i++ { @@ -357,4 +357,5 @@ func (dht *IpfsDHT) Bootstrap(ctx context.Context, queries int) { log.Errorf("dht seemingly found a peer at a random bootstrap id (%s)...", pi) } } + return nil } From cde36c392052cf1e6775afaaf293a36ec91567d8 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 9 Dec 2014 11:22:00 -0800 Subject: [PATCH 0595/3817] dht: bootstrap query logging This commit was moved from ipfs/go-ipfs-routing@8c83c2431484be4e58e14074998cd10895b7ba5e --- routing/dht/dht.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 3fdd327f9..4d87ebbd8 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -343,18 +343,26 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { // Bootstrap builds up list of peers by requesting random peer IDs func (dht *IpfsDHT) Bootstrap(ctx context.Context, queries int) error { - // bootstrap sequentially, as results will compound - for i := 0; i < NumBootstrapQueries; i++ { + randomID := func() peer.ID { + // 16 random bytes is not a valid peer id. it may be fine becuase + // the dht will rehash to its own keyspace anyway. id := make([]byte, 16) rand.Read(id) - pi, err := dht.FindPeer(ctx, peer.ID(id)) + return peer.ID(id) + } + + // bootstrap sequentially, as results will compound + for i := 0; i < queries; i++ { + id := randomID() + log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i, queries, id) + p, err := dht.FindPeer(ctx, id) if err == routing.ErrNotFound { // this isn't an error. this is precisely what we expect. } else if err != nil { log.Errorf("Bootstrap peer error: %s", err) } else { // woah, we got a peer under a random id? it _cannot_ be valid. - log.Errorf("dht seemingly found a peer at a random bootstrap id (%s)...", pi) + log.Errorf("dht seemingly found a peer at a random bootstrap id (%s)...", p) } } return nil From 94bdaf6d9d2463cfcce5df4c027e7dbe60ee6d6d Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 9 Dec 2014 12:04:25 -0800 Subject: [PATCH 0596/3817] dht/bootstrap: (optional) parallelism + error on peer This also makes it an Error to find a peer. This commit was moved from ipfs/go-ipfs-routing@da1c77f841ff30282788d6ed5cd7ff749f78d3c2 --- routing/dht/dht.go | 47 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 4d87ebbd8..d6a6d8770 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -342,6 +342,7 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { // Bootstrap builds up list of peers by requesting random peer IDs func (dht *IpfsDHT) Bootstrap(ctx context.Context, queries int) error { + var merr u.MultiErr randomID := func() peer.ID { // 16 random bytes is not a valid peer id. it may be fine becuase @@ -352,18 +353,52 @@ func (dht *IpfsDHT) Bootstrap(ctx context.Context, queries int) error { } // bootstrap sequentially, as results will compound - for i := 0; i < queries; i++ { - id := randomID() - log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i, queries, id) + runQuery := func(ctx context.Context, id peer.ID) { p, err := dht.FindPeer(ctx, id) if err == routing.ErrNotFound { // this isn't an error. this is precisely what we expect. } else if err != nil { - log.Errorf("Bootstrap peer error: %s", err) + merr = append(merr, err) } else { - // woah, we got a peer under a random id? it _cannot_ be valid. - log.Errorf("dht seemingly found a peer at a random bootstrap id (%s)...", p) + // woah, actually found a peer with that ID? this shouldn't happen normally + // (as the ID we use is not a real ID). this is an odd error worth logging. + err := fmt.Errorf("Bootstrap peer error: Actually FOUND peer. (%s, %s)", id, p) + log.Errorf("%s", err) + merr = append(merr, err) } } + + sequential := true + if sequential { + // these should be parallel normally. but can make them sequential for debugging. + // note that the core/bootstrap context deadline should be extended too for that. + for i := 0; i < queries; i++ { + id := randomID() + log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, queries, id) + runQuery(ctx, id) + } + + } else { + // note on parallelism here: the context is passed in to the queries, so they + // **should** exit when it exceeds, making this function exit on ctx cancel. + // normally, we should be selecting on ctx.Done() here too, but this gets + // complicated to do with WaitGroup, and doesnt wait for the children to exit. + var wg sync.WaitGroup + for i := 0; i < queries; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + id := randomID() + log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, queries, id) + runQuery(ctx, id) + }() + } + wg.Wait() + } + + if len(merr) > 0 { + return merr + } return nil } From 6ceb717573dacf9db8b719d9f284b88e8fa05221 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 5 Jan 2015 07:00:49 -0800 Subject: [PATCH 0597/3817] bootstrap: not error to not have enough bootstrap peers use dht bootstrap. there is an edge case where the dht is tiny (1?) and we have 0 bootstrap peers. we should probably _inform_ the user, but this may be more a webui or command thing. This commit was moved from ipfs/go-ipfs-routing@0d0cf4e52947950d42c2369456d987eee38dacd0 --- routing/dht/dht_test.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 147970695..d2341a1bd 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -238,7 +238,7 @@ func TestBootstrap(t *testing.T) { ctx := context.Background() - nDHTs := 15 + nDHTs := 30 _, _, dhts := setupDHTS(ctx, nDHTs, t) defer func() { for i := 0; i < nDHTs; i++ { @@ -269,12 +269,23 @@ func TestBootstrap(t *testing.T) { } // test "well-formed-ness" (>= 3 peers in every routing table) + avgsize := 0 for _, dht := range dhts { rtlen := dht.routingTable.Size() + avgsize += rtlen + t.Logf("routing table for %s has %d peers", dht.self, rtlen) if rtlen < 4 { - t.Errorf("routing table for %s only has %d peers", dht.self, rtlen) + // currently, we dont have good bootstrapping guarantees. + // t.Errorf("routing table for %s only has %d peers", dht.self, rtlen) } } + avgsize = avgsize / len(dhts) + avgsizeExpected := 6 + + t.Logf("avg rt size: %d", avgsize) + if avgsize < avgsizeExpected { + t.Errorf("avg rt size: %d < %d", avgsize, avgsizeExpected) + } } func TestProvidesMany(t *testing.T) { From 1e9e302d2442ebc27af6181b9dbad318e5f7ab87 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 5 Jan 2015 07:19:07 -0800 Subject: [PATCH 0598/3817] dht/bootstrap/test: longer timeout, less bias This commit was moved from ipfs/go-ipfs-routing@74e55ed63caa421431baec55533a8f923fe57b2f --- routing/dht/dht_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index d2341a1bd..4d63c0e44 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -3,6 +3,7 @@ package dht import ( "bytes" "fmt" + "math/rand" "sort" "sync" "testing" @@ -76,6 +77,7 @@ func bootstrap(t *testing.T, ctx context.Context, dhts []*IpfsDHT) { ctx, cancel := context.WithCancel(ctx) rounds := 1 + for i := 0; i < rounds; i++ { log.Debugf("bootstrapping round %d/%d\n", i, rounds) @@ -83,7 +85,10 @@ func bootstrap(t *testing.T, ctx context.Context, dhts []*IpfsDHT) { // 100 async https://gist.github.com/jbenet/56d12f0578d5f34810b2 // 100 sync https://gist.github.com/jbenet/6c59e7c15426e48aaedd // probably because results compound - for _, dht := range dhts { + + start := rand.Intn(len(dhts)) // randomize to decrease bias. + for i := range dhts { + dht := dhts[(start+i)%len(dhts)] log.Debugf("bootstrapping round %d/%d -- %s\n", i, rounds, dht.self) dht.Bootstrap(ctx, 3) } @@ -309,7 +314,7 @@ func TestProvidesMany(t *testing.T) { <-time.After(100 * time.Millisecond) t.Logf("bootstrapping them so they find each other", nDHTs) - ctxT, _ := context.WithTimeout(ctx, 5*time.Second) + ctxT, _ := context.WithTimeout(ctx, 20*time.Second) bootstrap(t, ctxT, dhts) if u.Debug { From a66000c9295fa112a178eb7bf4de9300343a3471 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 6 Jan 2015 08:45:17 -0800 Subject: [PATCH 0599/3817] blockservice: async HasBlock with ratelimit This commit was moved from ipfs/go-blockservice@face34bdbd98706398f774203e6ec18227fb39b0 --- blockservice/blockservice.go | 42 ++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index db126f344..a2a001418 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -8,6 +8,8 @@ import ( "fmt" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + process "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" + procrl "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit" blocks "github.com/jbenet/go-ipfs/blocks" "github.com/jbenet/go-ipfs/blocks/blockstore" exchange "github.com/jbenet/go-ipfs/exchange" @@ -17,6 +19,9 @@ import ( var log = u.Logger("blockservice") var ErrNotFound = errors.New("blockservice: key not found") +// MaxExchangeAddWorkers rate limits the number of exchange workers +var MaxExchangeAddWorkers = 100 + // BlockService is a hybrid block datastore. It stores data in a local // datastore and may retrieve data from a remote Exchange. // It uses an internal `datastore.Datastore` instance to store values. @@ -24,6 +29,9 @@ type BlockService struct { // TODO don't expose underlying impl details Blockstore blockstore.Blockstore Exchange exchange.Interface + + rateLimiter *procrl.RateLimiter + exchangeAdd chan blocks.Block } // NewBlockService creates a BlockService with given datastore instance. @@ -34,7 +42,17 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) (*BlockService, error if rem == nil { log.Warning("blockservice running in local (offline) mode.") } - return &BlockService{Blockstore: bs, Exchange: rem}, nil + + // exchangeAdd is a channel for async workers to add to the exchange. + // 100 blocks buffer. not clear what this number should be + exchangeAdd := make(chan blocks.Block, 100) + + return &BlockService{ + Blockstore: bs, + Exchange: rem, + exchangeAdd: exchangeAdd, + rateLimiter: procrl.NewRateLimiter(process.Background(), MaxExchangeAddWorkers), + }, nil } // AddBlock adds a particular block to the service, Putting it into the datastore. @@ -46,15 +64,21 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { return k, err } - // TODO this operation rate-limits blockservice operations, we should - // consider moving this to an sync process. + // this operation rate-limits blockservice operations, so it is + // now an async process. if s.Exchange != nil { - ctx := context.TODO() - if err := s.Exchange.HasBlock(ctx, b); err != nil { - // suppress error, as the client shouldn't care about bitswap. - // the client only cares about the blockstore.Put. - log.Errorf("Exchange.HasBlock error: %s", err) - } + + // LimitedGo will spawn a goroutine but provide proper backpressure. + // it will not spawn the goroutine until the ratelimiter's work load + // is under the threshold. + s.rateLimiter.LimitedGo(func(worker process.Process) { + ctx := context.TODO() + if err := s.Exchange.HasBlock(ctx, b); err != nil { + // suppress error, as the client shouldn't care about bitswap. + // the client only cares about the blockstore.Put. + log.Errorf("Exchange.HasBlock error: %s", err) + } + }) } return k, nil } From 894185ee0f861e08bef3351075c5a3170566d94a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 17 Dec 2014 19:07:54 +0000 Subject: [PATCH 0600/3817] implement recursive indirect blocks improve efficiency of multilayered indirect blocks clean up tests panic cleanup clean up logic, improve readability add final root node to the dagservice upon creation importer: simplified dag generation test: updated hashes using latest code @whyrusleeping this is why the sharness tests were failing: the hashes are added manually to make sure our generation doesn't change. cleanup after CR fix merkledag tests fix small block generation (no subblocks!) This commit was moved from ipfs/go-merkledag@a32fc7ffda9ce2a443a3718acbd663e16ba3764e --- ipld/merkledag/merkledag.go | 8 ++-- ipld/merkledag/merkledag_test.go | 69 +++++++++++++++++++------------- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index c9ea00ad2..007b5d055 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -295,12 +295,12 @@ func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} // FindLinks searches this nodes links for the given key, // returns the indexes of any links pointing to it -func FindLinks(n *Node, k u.Key) []int { +func FindLinks(n *Node, k u.Key, start int) []int { var out []int keybytes := []byte(k) - for i, lnk := range n.Links { + for i, lnk := range n.Links[start:] { if bytes.Equal([]byte(lnk.Hash), keybytes) { - out = append(out, i) + out = append(out, i+start) } } return out @@ -330,7 +330,7 @@ func (ds *dagService) GetDAG(ctx context.Context, root *Node) <-chan *Node { log.Error("Got back bad block!") break } - is := FindLinks(root, blk.Key()) + is := FindLinks(root, blk.Key(), next) for _, i := range is { nodes[i] = nd } diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 55107f08b..0c5bf71a8 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -8,14 +8,40 @@ import ( "sync" "testing" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + bstore "github.com/jbenet/go-ipfs/blocks/blockstore" blockservice "github.com/jbenet/go-ipfs/blockservice" + bserv "github.com/jbenet/go-ipfs/blockservice" + offline "github.com/jbenet/go-ipfs/exchange/offline" imp "github.com/jbenet/go-ipfs/importer" chunk "github.com/jbenet/go-ipfs/importer/chunk" . "github.com/jbenet/go-ipfs/merkledag" + "github.com/jbenet/go-ipfs/pin" uio "github.com/jbenet/go-ipfs/unixfs/io" u "github.com/jbenet/go-ipfs/util" ) +type dagservAndPinner struct { + ds DAGService + mp pin.ManualPinner +} + +func getDagservAndPinner(t *testing.T) dagservAndPinner { + db := ds.NewMapDatastore() + bs := bstore.NewBlockstore(dssync.MutexWrap(db)) + blockserv, err := bserv.New(bs, offline.Exchange(bs)) + if err != nil { + t.Fatal(err) + } + dserv := NewDAGService(blockserv) + mpin := pin.NewPinner(db, dserv).GetManual() + return dagservAndPinner{ + ds: dserv, + mp: mpin, + } +} + func TestNode(t *testing.T) { n1 := &Node{Data: []byte("beep")} @@ -66,16 +92,6 @@ func TestNode(t *testing.T) { printn("beep boop", n3) } -func makeTestDag(t *testing.T) *Node { - read := io.LimitReader(u.NewTimeSeededRand(), 1024*32) - spl := &chunk.SizeSplitter{512} - root, err := imp.NewDagFromReaderWithSplitter(read, spl) - if err != nil { - t.Fatal(err) - } - return root -} - type devZero struct{} func (_ devZero) Read(b []byte) (int, error) { @@ -85,38 +101,37 @@ func (_ devZero) Read(b []byte) (int, error) { return len(b), nil } -func makeZeroDag(t *testing.T) *Node { - read := io.LimitReader(devZero{}, 1024*32) - spl := &chunk.SizeSplitter{512} - root, err := imp.NewDagFromReaderWithSplitter(read, spl) - if err != nil { - t.Fatal(err) - } - return root -} - func TestBatchFetch(t *testing.T) { - root := makeTestDag(t) - runBatchFetchTest(t, root) + read := io.LimitReader(u.NewTimeSeededRand(), 1024*32) + runBatchFetchTest(t, read) } func TestBatchFetchDupBlock(t *testing.T) { - root := makeZeroDag(t) - runBatchFetchTest(t, root) + read := io.LimitReader(devZero{}, 1024*32) + runBatchFetchTest(t, read) } -func runBatchFetchTest(t *testing.T, root *Node) { +func runBatchFetchTest(t *testing.T, read io.Reader) { var dagservs []DAGService for _, bsi := range blockservice.Mocks(t, 5) { dagservs = append(dagservs, NewDAGService(bsi)) } + + spl := &chunk.SizeSplitter{512} + + root, err := imp.BuildDagFromReader(read, dagservs[0], nil, spl) + if err != nil { + t.Fatal(err) + } + t.Log("finished setup.") - read, err := uio.NewDagReader(root, nil) + dagr, err := uio.NewDagReader(root, dagservs[0]) if err != nil { t.Fatal(err) } - expected, err := ioutil.ReadAll(read) + + expected, err := ioutil.ReadAll(dagr) if err != nil { t.Fatal(err) } From 60bb0ed3d79ad2f21f51fb19ccfaf8f59d35f6c9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 17 Dec 2014 19:07:54 +0000 Subject: [PATCH 0601/3817] implement recursive indirect blocks improve efficiency of multilayered indirect blocks clean up tests panic cleanup clean up logic, improve readability add final root node to the dagservice upon creation importer: simplified dag generation test: updated hashes using latest code @whyrusleeping this is why the sharness tests were failing: the hashes are added manually to make sure our generation doesn't change. cleanup after CR fix merkledag tests fix small block generation (no subblocks!) This commit was moved from ipfs/go-unixfs@39d1685993e8a4def4d2cd37f2f9bdb8be2d5dca --- unixfs/format.go | 8 +++++++ unixfs/io/dagmodifier_test.go | 1 + unixfs/io/dagreader.go | 39 +++++++++-------------------------- 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/unixfs/format.go b/unixfs/format.go index 0fd29d358..845f1da30 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -118,3 +118,11 @@ func (mb *MultiBlock) GetBytes() ([]byte, error) { pbn.Data = mb.Data return proto.Marshal(pbn) } + +func (mb *MultiBlock) FileSize() uint64 { + return uint64(len(mb.Data)) + mb.subtotal +} + +func (mb *MultiBlock) NumChildren() int { + return len(mb.blocksizes) +} diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index ed5b10d69..e4020c64a 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -187,6 +187,7 @@ func TestMultiWrite(t *testing.T) { } func TestMultiWriteCoal(t *testing.T) { + t.Skip("Skipping test until DagModifier is fixed") dserv := getMockDagServ(t) _, n := getNode(t, dserv, 0) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index f4290dd4b..ab28dc8ae 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -38,10 +38,7 @@ func NewDagReader(n *mdag.Node, serv mdag.DAGService) (io.Reader, error) { // Dont allow reading directories return nil, ErrIsDir case ftpb.Data_File: - var fetchChan <-chan *mdag.Node - if serv != nil { - fetchChan = serv.GetDAG(context.TODO(), n) - } + fetchChan := serv.GetDAG(context.TODO(), n) return &DagReader{ node: n, serv: serv, @@ -62,33 +59,17 @@ func (dr *DagReader) precalcNextBuf() error { var nxt *mdag.Node var ok bool - // TODO: require non-nil dagservice, use offline bitswap exchange - if dr.serv == nil { - // Only used when fetchChan is nil, - // which only happens when passed in a nil dagservice - // TODO: this logic is hard to follow, do it better. - // NOTE: the only time this code is used, is during the - // importer tests, consider just changing those tests - log.Warning("Running DAGReader with nil DAGService!") - if dr.linkPosition >= len(dr.node.Links) { + if dr.fetchChan == nil { + // This panic is appropriate because the select statement + // will not panic if you try and read from a nil channel + // it will simply hang. + panic("fetchChan should NOT be nil") + } + select { + case nxt, ok = <-dr.fetchChan: + if !ok { return io.EOF } - nxt = dr.node.Links[dr.linkPosition].Node - if nxt == nil { - return errors.New("Got nil node back from link! and no DAGService!") - } - dr.linkPosition++ - - } else { - if dr.fetchChan == nil { - panic("this is wrong.") - } - select { - case nxt, ok = <-dr.fetchChan: - if !ok { - return io.EOF - } - } } pb := new(ftpb.Data) From 71967c59559f4b9c4d154b2c8e8a880e7f4db1d2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 17 Dec 2014 19:07:54 +0000 Subject: [PATCH 0602/3817] implement recursive indirect blocks improve efficiency of multilayered indirect blocks clean up tests panic cleanup clean up logic, improve readability add final root node to the dagservice upon creation importer: simplified dag generation test: updated hashes using latest code @whyrusleeping this is why the sharness tests were failing: the hashes are added manually to make sure our generation doesn't change. cleanup after CR fix merkledag tests fix small block generation (no subblocks!) This commit was moved from ipfs/go-ipfs-chunker@31b5f255bf9b5432ebdbe6759e621de1be733601 --- chunker/splitting.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 65a79d5ad..40597a064 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -9,7 +9,8 @@ import ( var log = util.Logger("chunk") -var DefaultSplitter = &SizeSplitter{Size: 1024 * 256} +var DefaultBlockSize = 1024 * 256 +var DefaultSplitter = &SizeSplitter{Size: DefaultBlockSize} type BlockSplitter interface { Split(r io.Reader) chan []byte From ad6308fd2c4f6cdce948f797a3e24cd530359755 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 6 Jan 2015 13:07:28 -0800 Subject: [PATCH 0603/3817] merkledag: keep links sorted by name May not be necessary to sort when adding each link-- doing so would be unnecessarily expensive O(n^2) when constructing nodes -- though n wont be big. This commit was moved from ipfs/go-merkledag@1a5c8cc40a0d9723655e86368e889775d531d0d9 --- ipld/merkledag/coding.go | 5 +++++ ipld/merkledag/merkledag.go | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 81cc1fc7c..cbd2de74a 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -2,6 +2,7 @@ package merkledag import ( "fmt" + "sort" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" @@ -30,6 +31,7 @@ func (n *Node) Unmarshal(encoded []byte) error { } n.Links[i].Hash = h } + sort.Stable(LinkSlice(n.Links)) // keep links sorted n.Data = pbn.GetData() return nil @@ -59,6 +61,8 @@ func (n *Node) Marshal() ([]byte, error) { func (n *Node) getPBNode() *pb.PBNode { pbn := &pb.PBNode{} pbn.Links = make([]*pb.PBLink, len(n.Links)) + + sort.Stable(LinkSlice(n.Links)) // keep links sorted for i, l := range n.Links { pbn.Links[i] = &pb.PBLink{} pbn.Links[i].Name = &l.Name @@ -73,6 +77,7 @@ func (n *Node) getPBNode() *pb.PBNode { // Encoded returns the encoded raw data version of a Node instance. // It may use a cached encoded version, unless the force flag is given. func (n *Node) Encoded(force bool) ([]byte, error) { + sort.Stable(LinkSlice(n.Links)) // keep links sorted if n.encoded == nil || force { var err error n.encoded, err = n.Marshal() diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 007b5d055..cd50d9e5b 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -66,6 +66,12 @@ type Link struct { Node *Node } +type LinkSlice []*Link + +func (ls LinkSlice) Len() int { return len(ls) } +func (ls LinkSlice) Swap(a, b int) { ls[a], ls[b] = ls[b], ls[a] } +func (ls LinkSlice) Less(a, b int) bool { return ls[a].Name < ls[b].Name } + // MakeLink creates a link to the given node func MakeLink(n *Node) (*Link, error) { s, err := n.Size() From 441c69b4ce82e8d79a6d1849abe952f6b45efefc Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 7 Jan 2015 02:13:44 -0800 Subject: [PATCH 0604/3817] merkledag: add NodeStat object This commit was moved from ipfs/go-merkledag@43f8f6010c1e32a66a95b85371c8a209be9033d3 --- ipld/merkledag/merkledag.go | 35 +++++++++++++++++++++++++++++++ ipld/merkledag/merkledag_test.go | 36 ++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index cd50d9e5b..bfb7f3ccf 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -51,6 +51,20 @@ type Node struct { cached mh.Multihash } +// NodeStat is a statistics object for a Node. Mostly sizes. +type NodeStat struct { + NumLinks int // number of links in link table + BlockSize int // size of the raw data + LinksSize int // size of the links segment + DataSize int // size of the data segment + CumulativeSize int // cumulatie size of object + all it references +} + +func (ns NodeStat) String() string { + f := "NodeStat{NumLinks: %d, BlockSize: %d, LinksSize: %d, DataSize: %d, CumulativeSize: %d}" + return fmt.Sprintf(f, ns.NumLinks, ns.BlockSize, ns.LinksSize, ns.DataSize, ns.CumulativeSize) +} + // Link represents an IPFS Merkle DAG Link between Nodes. type Link struct { // utf string name. should be unique per object @@ -162,6 +176,27 @@ func (n *Node) Size() (uint64, error) { return s, nil } +// Stat returns statistics on the node. +func (n *Node) Stat() (NodeStat, error) { + enc, err := n.Encoded(false) + if err != nil { + return NodeStat{}, err + } + + cumSize, err := n.Size() + if err != nil { + return NodeStat{}, err + } + + return NodeStat{ + NumLinks: len(n.Links), + BlockSize: len(enc), + LinksSize: len(enc) - len(n.Data), // includes framing. + DataSize: len(n.Data), + CumulativeSize: int(cumSize), + }, nil +} + // Multihash hashes the encoded data of this node. func (n *Node) Multihash() (mh.Multihash, error) { // Note: Encoded generates the hash and puts it in n.cached. diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 0c5bf71a8..e44870b8c 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -85,6 +85,8 @@ func TestNode(t *testing.T) { } else { fmt.Println("key: ", k) } + + SubtestNodeStat(t, n) } printn("beep", n1) @@ -92,6 +94,40 @@ func TestNode(t *testing.T) { printn("beep boop", n3) } +func SubtestNodeStat(t *testing.T, n *Node) { + enc, err := n.Encoded(true) + if err != nil { + t.Error("n.Encoded(true) failed") + return + } + + cumSize, err := n.Size() + if err != nil { + t.Error("n.Size() failed") + return + } + + expected := NodeStat{ + NumLinks: len(n.Links), + BlockSize: len(enc), + LinksSize: len(enc) - len(n.Data), // includes framing. + DataSize: len(n.Data), + CumulativeSize: int(cumSize), + } + + actual, err := n.Stat() + if err != nil { + t.Error("n.Stat() failed") + return + } + + if expected != actual { + t.Error("n.Stat incorrect.\nexpect: %s\nactual: %s", expected, actual) + } else { + fmt.Printf("n.Stat correct: %s\n", actual) + } +} + type devZero struct{} func (_ devZero) Read(b []byte) (int, error) { From 3aaab8c38d4c0965f495ae3ddf78c130bc9463ac Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 7 Jan 2015 02:15:53 -0800 Subject: [PATCH 0605/3817] merkledag: split off node.go This commit was moved from ipfs/go-merkledag@f93ea20ce7394d20c6e626be225c454ffba2e64c --- ipld/merkledag/merkledag.go | 181 ---------------------------------- ipld/merkledag/node.go | 188 ++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 181 deletions(-) create mode 100644 ipld/merkledag/node.go diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index bfb7f3ccf..16427c484 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -9,7 +9,6 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" blocks "github.com/jbenet/go-ipfs/blocks" bserv "github.com/jbenet/go-ipfs/blockservice" u "github.com/jbenet/go-ipfs/util" @@ -18,11 +17,6 @@ import ( var log = u.Logger("merkledag") var ErrNotFound = fmt.Errorf("merkledag: not found") -// NodeMap maps u.Keys to Nodes. -// We cannot use []byte/Multihash for keys :( -// so have to convert Multihash bytes to string (u.Key) -type NodeMap map[u.Key]*Node - // DAGService is an IPFS Merkle DAG service. type DAGService interface { Add(*Node) (u.Key, error) @@ -39,181 +33,6 @@ func NewDAGService(bs *bserv.BlockService) DAGService { return &dagService{bs} } -// Node represents a node in the IPFS Merkle DAG. -// nodes have opaque data and a set of navigable links. -type Node struct { - Links []*Link - Data []byte - - // cache encoded/marshaled value - encoded []byte - - cached mh.Multihash -} - -// NodeStat is a statistics object for a Node. Mostly sizes. -type NodeStat struct { - NumLinks int // number of links in link table - BlockSize int // size of the raw data - LinksSize int // size of the links segment - DataSize int // size of the data segment - CumulativeSize int // cumulatie size of object + all it references -} - -func (ns NodeStat) String() string { - f := "NodeStat{NumLinks: %d, BlockSize: %d, LinksSize: %d, DataSize: %d, CumulativeSize: %d}" - return fmt.Sprintf(f, ns.NumLinks, ns.BlockSize, ns.LinksSize, ns.DataSize, ns.CumulativeSize) -} - -// Link represents an IPFS Merkle DAG Link between Nodes. -type Link struct { - // utf string name. should be unique per object - Name string // utf8 - - // cumulative size of target object - Size uint64 - - // multihash of the target object - Hash mh.Multihash - - // a ptr to the actual node for graph manipulation - Node *Node -} - -type LinkSlice []*Link - -func (ls LinkSlice) Len() int { return len(ls) } -func (ls LinkSlice) Swap(a, b int) { ls[a], ls[b] = ls[b], ls[a] } -func (ls LinkSlice) Less(a, b int) bool { return ls[a].Name < ls[b].Name } - -// MakeLink creates a link to the given node -func MakeLink(n *Node) (*Link, error) { - s, err := n.Size() - if err != nil { - return nil, err - } - - h, err := n.Multihash() - if err != nil { - return nil, err - } - return &Link{ - Size: s, - Hash: h, - }, nil -} - -// GetNode returns the MDAG Node that this link points to -func (l *Link) GetNode(serv DAGService) (*Node, error) { - if l.Node != nil { - return l.Node, nil - } - - return serv.Get(u.Key(l.Hash)) -} - -// AddNodeLink adds a link to another node. -func (n *Node) AddNodeLink(name string, that *Node) error { - lnk, err := MakeLink(that) - if err != nil { - return err - } - lnk.Name = name - lnk.Node = that - - n.Links = append(n.Links, lnk) - return nil -} - -// AddNodeLink adds a link to another node. without keeping a reference to -// the child node -func (n *Node) AddNodeLinkClean(name string, that *Node) error { - lnk, err := MakeLink(that) - if err != nil { - return err - } - lnk.Name = name - - n.Links = append(n.Links, lnk) - return nil -} - -// Remove a link on this node by the given name -func (n *Node) RemoveNodeLink(name string) error { - for i, l := range n.Links { - if l.Name == name { - n.Links = append(n.Links[:i], n.Links[i+1:]...) - return nil - } - } - return ErrNotFound -} - -// Copy returns a copy of the node. -// NOTE: does not make copies of Node objects in the links. -func (n *Node) Copy() *Node { - nnode := new(Node) - nnode.Data = make([]byte, len(n.Data)) - copy(nnode.Data, n.Data) - - nnode.Links = make([]*Link, len(n.Links)) - copy(nnode.Links, n.Links) - return nnode -} - -// Size returns the total size of the data addressed by node, -// including the total sizes of references. -func (n *Node) Size() (uint64, error) { - b, err := n.Encoded(false) - if err != nil { - return 0, err - } - - s := uint64(len(b)) - for _, l := range n.Links { - s += l.Size - } - return s, nil -} - -// Stat returns statistics on the node. -func (n *Node) Stat() (NodeStat, error) { - enc, err := n.Encoded(false) - if err != nil { - return NodeStat{}, err - } - - cumSize, err := n.Size() - if err != nil { - return NodeStat{}, err - } - - return NodeStat{ - NumLinks: len(n.Links), - BlockSize: len(enc), - LinksSize: len(enc) - len(n.Data), // includes framing. - DataSize: len(n.Data), - CumulativeSize: int(cumSize), - }, nil -} - -// Multihash hashes the encoded data of this node. -func (n *Node) Multihash() (mh.Multihash, error) { - // Note: Encoded generates the hash and puts it in n.cached. - _, err := n.Encoded(false) - if err != nil { - return nil, err - } - - return n.cached, nil -} - -// Key returns the Multihash as a key, for maps. -func (n *Node) Key() (u.Key, error) { - h, err := n.Multihash() - return u.Key(h), err -} - // dagService is an IPFS Merkle DAG service. // - the root is virtual (like a forest) // - stores nodes' data in a BlockService diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go new file mode 100644 index 000000000..2f522ce54 --- /dev/null +++ b/ipld/merkledag/node.go @@ -0,0 +1,188 @@ +package merkledag + +import ( + "fmt" + + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + u "github.com/jbenet/go-ipfs/util" +) + +// NodeMap maps u.Keys to Nodes. +// We cannot use []byte/Multihash for keys :( +// so have to convert Multihash bytes to string (u.Key) +type NodeMap map[u.Key]*Node + +// Node represents a node in the IPFS Merkle DAG. +// nodes have opaque data and a set of navigable links. +type Node struct { + Links []*Link + Data []byte + + // cache encoded/marshaled value + encoded []byte + + cached mh.Multihash +} + +// NodeStat is a statistics object for a Node. Mostly sizes. +type NodeStat struct { + NumLinks int // number of links in link table + BlockSize int // size of the raw data + LinksSize int // size of the links segment + DataSize int // size of the data segment + CumulativeSize int // cumulatie size of object + all it references +} + +func (ns NodeStat) String() string { + f := "NodeStat{NumLinks: %d, BlockSize: %d, LinksSize: %d, DataSize: %d, CumulativeSize: %d}" + return fmt.Sprintf(f, ns.NumLinks, ns.BlockSize, ns.LinksSize, ns.DataSize, ns.CumulativeSize) +} + +// Link represents an IPFS Merkle DAG Link between Nodes. +type Link struct { + // utf string name. should be unique per object + Name string // utf8 + + // cumulative size of target object + Size uint64 + + // multihash of the target object + Hash mh.Multihash + + // a ptr to the actual node for graph manipulation + Node *Node +} + +type LinkSlice []*Link + +func (ls LinkSlice) Len() int { return len(ls) } +func (ls LinkSlice) Swap(a, b int) { ls[a], ls[b] = ls[b], ls[a] } +func (ls LinkSlice) Less(a, b int) bool { return ls[a].Name < ls[b].Name } + +// MakeLink creates a link to the given node +func MakeLink(n *Node) (*Link, error) { + s, err := n.Size() + if err != nil { + return nil, err + } + + h, err := n.Multihash() + if err != nil { + return nil, err + } + return &Link{ + Size: s, + Hash: h, + }, nil +} + +// GetNode returns the MDAG Node that this link points to +func (l *Link) GetNode(serv DAGService) (*Node, error) { + if l.Node != nil { + return l.Node, nil + } + + return serv.Get(u.Key(l.Hash)) +} + +// AddNodeLink adds a link to another node. +func (n *Node) AddNodeLink(name string, that *Node) error { + lnk, err := MakeLink(that) + if err != nil { + return err + } + lnk.Name = name + lnk.Node = that + + n.Links = append(n.Links, lnk) + return nil +} + +// AddNodeLink adds a link to another node. without keeping a reference to +// the child node +func (n *Node) AddNodeLinkClean(name string, that *Node) error { + lnk, err := MakeLink(that) + if err != nil { + return err + } + lnk.Name = name + + n.Links = append(n.Links, lnk) + return nil +} + +// Remove a link on this node by the given name +func (n *Node) RemoveNodeLink(name string) error { + for i, l := range n.Links { + if l.Name == name { + n.Links = append(n.Links[:i], n.Links[i+1:]...) + return nil + } + } + return ErrNotFound +} + +// Copy returns a copy of the node. +// NOTE: does not make copies of Node objects in the links. +func (n *Node) Copy() *Node { + nnode := new(Node) + nnode.Data = make([]byte, len(n.Data)) + copy(nnode.Data, n.Data) + + nnode.Links = make([]*Link, len(n.Links)) + copy(nnode.Links, n.Links) + return nnode +} + +// Size returns the total size of the data addressed by node, +// including the total sizes of references. +func (n *Node) Size() (uint64, error) { + b, err := n.Encoded(false) + if err != nil { + return 0, err + } + + s := uint64(len(b)) + for _, l := range n.Links { + s += l.Size + } + return s, nil +} + +// Stat returns statistics on the node. +func (n *Node) Stat() (NodeStat, error) { + enc, err := n.Encoded(false) + if err != nil { + return NodeStat{}, err + } + + cumSize, err := n.Size() + if err != nil { + return NodeStat{}, err + } + + return NodeStat{ + NumLinks: len(n.Links), + BlockSize: len(enc), + LinksSize: len(enc) - len(n.Data), // includes framing. + DataSize: len(n.Data), + CumulativeSize: int(cumSize), + }, nil +} + +// Multihash hashes the encoded data of this node. +func (n *Node) Multihash() (mh.Multihash, error) { + // Note: Encoded generates the hash and puts it in n.cached. + _, err := n.Encoded(false) + if err != nil { + return nil, err + } + + return n.cached, nil +} + +// Key returns the Multihash as a key, for maps. +func (n *Node) Key() (u.Key, error) { + h, err := n.Multihash() + return u.Key(h), err +} From 582b0044963a2c63372c889b43e8c4ee4212d754 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 7 Jan 2015 02:30:02 -0800 Subject: [PATCH 0606/3817] ipfs object has learned stat 'ipfs object stat' is a plumbing command to print DAG node statistics. is a base58 encoded multihash. It outputs to stdout: NumLinks int number of links in link table BlockSize int size of the raw, encoded data LinksSize int size of the links segment DataSize int size of the data segment CumulativeSize int cumulative size of object and references This commit was moved from ipfs/go-merkledag@60d2be9fafc41f96ff032808ee6dd3711fefda9b --- ipld/merkledag/node.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 2f522ce54..016f4353b 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -27,10 +27,10 @@ type Node struct { // NodeStat is a statistics object for a Node. Mostly sizes. type NodeStat struct { NumLinks int // number of links in link table - BlockSize int // size of the raw data + BlockSize int // size of the raw, encoded data LinksSize int // size of the links segment DataSize int // size of the data segment - CumulativeSize int // cumulatie size of object + all it references + CumulativeSize int // cumulative size of object and its references } func (ns NodeStat) String() string { From 9c00c6d77b71413e212050208d78844e9b6276a8 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 7 Jan 2015 11:04:35 -0800 Subject: [PATCH 0607/3817] merkledag traversal This commit was moved from ipfs/go-merkledag@cdd736c91c769ed32bb72009e2b4d16ed55e9602 --- ipld/merkledag/traverse/traverse.go | 226 +++++++++++++ ipld/merkledag/traverse/traverse_test.go | 397 +++++++++++++++++++++++ 2 files changed, 623 insertions(+) create mode 100644 ipld/merkledag/traverse/traverse.go create mode 100644 ipld/merkledag/traverse/traverse_test.go diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go new file mode 100644 index 000000000..688c409f2 --- /dev/null +++ b/ipld/merkledag/traverse/traverse.go @@ -0,0 +1,226 @@ +// Package traverse provides merkledag traversal functions +package traverse + +import ( + "errors" + + mdag "github.com/jbenet/go-ipfs/merkledag" +) + +// Order is an identifier for traversal algorithm orders +type Order int + +const ( + DFSPre Order = iota // depth-first pre-order + DFSPost // depth-first post-order + BFS // breadth-first +) + +// Options specifies a series of traversal options +type Options struct { + DAG mdag.DAGService // the dagservice to fetch nodes + Order Order // what order to traverse in + Func Func // the function to perform at each step + ErrFunc ErrFunc // see ErrFunc. Optional + + SkipDuplicates bool // whether to skip duplicate nodes +} + +// State is a current traversal state +type State struct { + Node *mdag.Node + Depth int +} + +type traversal struct { + opts Options + seen map[string]struct{} +} + +func (t *traversal) shouldSkip(n *mdag.Node) (bool, error) { + if t.opts.SkipDuplicates { + k, err := n.Key() + if err != nil { + return true, err + } + + if _, found := t.seen[string(k)]; found { + return true, nil + } + t.seen[string(k)] = struct{}{} + } + + return false, nil +} + +func (t *traversal) callFunc(next State) error { + return t.opts.Func(next) +} + +// getNode returns the node for link. If it return an error, +// stop processing. if it returns a nil node, just skip it. +// +// the error handling is a little complicated. +func (t *traversal) getNode(link *mdag.Link) (*mdag.Node, error) { + + getNode := func(l *mdag.Link) (*mdag.Node, error) { + next, err := l.GetNode(t.opts.DAG) + if err != nil { + return nil, err + } + + skip, err := t.shouldSkip(next) + if skip { + next = nil + } + return next, err + } + + next, err := getNode(link) + if err != nil && t.opts.ErrFunc != nil { // attempt recovery. + err = t.opts.ErrFunc(err) + next = nil // skip regardless + } + return next, err +} + +// Func is the type of the function called for each dag.Node visited by Traverse. +// The traversal argument contains the current traversal state. +// If an error is returned, processing stops. +type Func func(current State) error + +// If there is a problem walking to the Node, and ErrFunc is provided, Traverse +// will call ErrFunc with the error encountered. ErrFunc can decide how to handle +// that error, and return an error back to Traversal with how to proceed: +// * nil - skip the Node and its children, but continue processing +// * all other errors halt processing immediately. +// +// If ErrFunc is nil, Traversal will stop, as if: +// +// opts.ErrFunc = func(err error) { return err } +// +type ErrFunc func(err error) error + +func Traverse(root *mdag.Node, o Options) error { + t := traversal{ + opts: o, + seen: map[string]struct{}{}, + } + + state := State{ + Node: root, + Depth: 0, + } + + switch o.Order { + default: + return dfsPreTraverse(state, &t) + case DFSPre: + return dfsPreTraverse(state, &t) + case DFSPost: + return dfsPostTraverse(state, &t) + case BFS: + return bfsTraverse(state, &t) + } +} + +type dfsFunc func(state State, t *traversal) error + +func dfsPreTraverse(state State, t *traversal) error { + if err := t.callFunc(state); err != nil { + return err + } + if err := dfsDescend(dfsPreTraverse, state, t); err != nil { + return err + } + return nil +} + +func dfsPostTraverse(state State, t *traversal) error { + if err := dfsDescend(dfsPostTraverse, state, t); err != nil { + return err + } + if err := t.callFunc(state); err != nil { + return err + } + return nil +} + +func dfsDescend(df dfsFunc, curr State, t *traversal) error { + for _, l := range curr.Node.Links { + node, err := t.getNode(l) + if err != nil { + return err + } + if node == nil { // skip + continue + } + + next := State{ + Node: node, + Depth: curr.Depth + 1, + } + if err := df(next, t); err != nil { + return err + } + } + return nil +} + +func bfsTraverse(root State, t *traversal) error { + + if skip, err := t.shouldSkip(root.Node); skip || err != nil { + return err + } + + var q queue + q.enq(root) + for q.len() > 0 { + curr := q.deq() + if curr.Node == nil { + return errors.New("failed to dequeue though queue not empty") + } + + // call user's func + if err := t.callFunc(curr); err != nil { + return err + } + + for _, l := range curr.Node.Links { + node, err := t.getNode(l) + if err != nil { + return err + } + if node == nil { // skip + continue + } + + q.enq(State{ + Node: node, + Depth: curr.Depth + 1, + }) + } + } + return nil +} + +type queue struct { + s []State +} + +func (q *queue) enq(n State) { + q.s = append(q.s, n) +} + +func (q *queue) deq() State { + if len(q.s) < 1 { + return State{} + } + n := q.s[0] + q.s = q.s[1:] + return n +} + +func (q *queue) len() int { + return len(q.s) +} diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go new file mode 100644 index 000000000..912ce34d7 --- /dev/null +++ b/ipld/merkledag/traverse/traverse_test.go @@ -0,0 +1,397 @@ +package traverse + +import ( + "bytes" + "fmt" + "testing" + + mdag "github.com/jbenet/go-ipfs/merkledag" +) + +func TestDFSPreNoSkip(t *testing.T) { + opts := Options{Order: DFSPre} + + testWalkOutputs(t, newFan(t), opts, []byte(` +0 /a +1 /a/aa +1 /a/ab +1 /a/ac +1 /a/ad +`)) + + testWalkOutputs(t, newLinkedList(t), opts, []byte(` +0 /a +1 /a/aa +2 /a/aa/aaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +`)) + + testWalkOutputs(t, newBinaryTree(t), opts, []byte(` +0 /a +1 /a/aa +2 /a/aa/aaa +2 /a/aa/aab +1 /a/ab +2 /a/ab/aba +2 /a/ab/abb +`)) + + testWalkOutputs(t, newBinaryDAG(t), opts, []byte(` +0 /a +1 /a/aa +2 /a/aa/aaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +2 /a/aa/aaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +1 /a/aa +2 /a/aa/aaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +2 /a/aa/aaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +`)) +} + +func TestDFSPreSkip(t *testing.T) { + opts := Options{Order: DFSPre, SkipDuplicates: true} + + testWalkOutputs(t, newFan(t), opts, []byte(` +0 /a +1 /a/aa +1 /a/ab +1 /a/ac +1 /a/ad +`)) + + testWalkOutputs(t, newLinkedList(t), opts, []byte(` +0 /a +1 /a/aa +2 /a/aa/aaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +`)) + + testWalkOutputs(t, newBinaryTree(t), opts, []byte(` +0 /a +1 /a/aa +2 /a/aa/aaa +2 /a/aa/aab +1 /a/ab +2 /a/ab/aba +2 /a/ab/abb +`)) + + testWalkOutputs(t, newBinaryDAG(t), opts, []byte(` +0 /a +1 /a/aa +2 /a/aa/aaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +`)) +} + +func TestDFSPostNoSkip(t *testing.T) { + opts := Options{Order: DFSPost} + + testWalkOutputs(t, newFan(t), opts, []byte(` +1 /a/aa +1 /a/ab +1 /a/ac +1 /a/ad +0 /a +`)) + + testWalkOutputs(t, newLinkedList(t), opts, []byte(` +4 /a/aa/aaa/aaaa/aaaaa +3 /a/aa/aaa/aaaa +2 /a/aa/aaa +1 /a/aa +0 /a +`)) + + testWalkOutputs(t, newBinaryTree(t), opts, []byte(` +2 /a/aa/aaa +2 /a/aa/aab +1 /a/aa +2 /a/ab/aba +2 /a/ab/abb +1 /a/ab +0 /a +`)) + + testWalkOutputs(t, newBinaryDAG(t), opts, []byte(` +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +3 /a/aa/aaa/aaaa +2 /a/aa/aaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +3 /a/aa/aaa/aaaa +2 /a/aa/aaa +1 /a/aa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +3 /a/aa/aaa/aaaa +2 /a/aa/aaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +3 /a/aa/aaa/aaaa +2 /a/aa/aaa +1 /a/aa +0 /a +`)) +} + +func TestDFSPostSkip(t *testing.T) { + opts := Options{Order: DFSPost, SkipDuplicates: true} + + testWalkOutputs(t, newFan(t), opts, []byte(` +1 /a/aa +1 /a/ab +1 /a/ac +1 /a/ad +0 /a +`)) + + testWalkOutputs(t, newLinkedList(t), opts, []byte(` +4 /a/aa/aaa/aaaa/aaaaa +3 /a/aa/aaa/aaaa +2 /a/aa/aaa +1 /a/aa +0 /a +`)) + + testWalkOutputs(t, newBinaryTree(t), opts, []byte(` +2 /a/aa/aaa +2 /a/aa/aab +1 /a/aa +2 /a/ab/aba +2 /a/ab/abb +1 /a/ab +0 /a +`)) + + testWalkOutputs(t, newBinaryDAG(t), opts, []byte(` +4 /a/aa/aaa/aaaa/aaaaa +3 /a/aa/aaa/aaaa +2 /a/aa/aaa +1 /a/aa +0 /a +`)) +} + +func TestBFSNoSkip(t *testing.T) { + opts := Options{Order: BFS} + + testWalkOutputs(t, newFan(t), opts, []byte(` +0 /a +1 /a/aa +1 /a/ab +1 /a/ac +1 /a/ad +`)) + + testWalkOutputs(t, newLinkedList(t), opts, []byte(` +0 /a +1 /a/aa +2 /a/aa/aaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +`)) + + testWalkOutputs(t, newBinaryTree(t), opts, []byte(` +0 /a +1 /a/aa +1 /a/ab +2 /a/aa/aaa +2 /a/aa/aab +2 /a/ab/aba +2 /a/ab/abb +`)) + + testWalkOutputs(t, newBinaryDAG(t), opts, []byte(` +0 /a +1 /a/aa +1 /a/aa +2 /a/aa/aaa +2 /a/aa/aaa +2 /a/aa/aaa +2 /a/aa/aaa +3 /a/aa/aaa/aaaa +3 /a/aa/aaa/aaaa +3 /a/aa/aaa/aaaa +3 /a/aa/aaa/aaaa +3 /a/aa/aaa/aaaa +3 /a/aa/aaa/aaaa +3 /a/aa/aaa/aaaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +4 /a/aa/aaa/aaaa/aaaaa +`)) +} + +func TestBFSSkip(t *testing.T) { + opts := Options{Order: BFS, SkipDuplicates: true} + + testWalkOutputs(t, newFan(t), opts, []byte(` +0 /a +1 /a/aa +1 /a/ab +1 /a/ac +1 /a/ad +`)) + + testWalkOutputs(t, newLinkedList(t), opts, []byte(` +0 /a +1 /a/aa +2 /a/aa/aaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +`)) + + testWalkOutputs(t, newBinaryTree(t), opts, []byte(` +0 /a +1 /a/aa +1 /a/ab +2 /a/aa/aaa +2 /a/aa/aab +2 /a/ab/aba +2 /a/ab/abb +`)) + + testWalkOutputs(t, newBinaryDAG(t), opts, []byte(` +0 /a +1 /a/aa +2 /a/aa/aaa +3 /a/aa/aaa/aaaa +4 /a/aa/aaa/aaaa/aaaaa +`)) +} + +func testWalkOutputs(t *testing.T, root *mdag.Node, opts Options, expect []byte) { + expect = bytes.TrimLeft(expect, "\n") + + var buf bytes.Buffer + walk := func(current State) error { + s := fmt.Sprintf("%d %s\n", current.Depth, current.Node.Data) + t.Logf("walk: %s", s) + buf.Write([]byte(s)) + return nil + } + + opts.Func = walk + if err := Traverse(root, opts); err != nil { + t.Error(err) + return + } + + actual := buf.Bytes() + if !bytes.Equal(actual, expect) { + t.Error("error: outputs differ") + t.Logf("expect:\n%s", expect) + t.Logf("actual:\n%s", actual) + } else { + t.Logf("expect matches actual:\n%s", expect) + } +} + +func newFan(t *testing.T) *mdag.Node { + a := &mdag.Node{Data: []byte("/a")} + addChild(t, a, "aa") + addChild(t, a, "ab") + addChild(t, a, "ac") + addChild(t, a, "ad") + return a +} + +func newLinkedList(t *testing.T) *mdag.Node { + a := &mdag.Node{Data: []byte("/a")} + aa := addChild(t, a, "aa") + aaa := addChild(t, aa, "aaa") + aaaa := addChild(t, aaa, "aaaa") + addChild(t, aaaa, "aaaaa") + return a +} + +func newBinaryTree(t *testing.T) *mdag.Node { + a := &mdag.Node{Data: []byte("/a")} + aa := addChild(t, a, "aa") + ab := addChild(t, a, "ab") + addChild(t, aa, "aaa") + addChild(t, aa, "aab") + addChild(t, ab, "aba") + addChild(t, ab, "abb") + return a +} + +func newBinaryDAG(t *testing.T) *mdag.Node { + a := &mdag.Node{Data: []byte("/a")} + aa := addChild(t, a, "aa") + aaa := addChild(t, aa, "aaa") + aaaa := addChild(t, aaa, "aaaa") + aaaaa := addChild(t, aaaa, "aaaaa") + addLink(t, a, aa) + addLink(t, aa, aaa) + addLink(t, aaa, aaaa) + addLink(t, aaaa, aaaaa) + return a +} + +func addLink(t *testing.T, a, b *mdag.Node) { + to := string(a.Data) + "2" + string(b.Data) + if err := a.AddNodeLink(to, b); err != nil { + t.Error(err) + } +} + +func addChild(t *testing.T, a *mdag.Node, name string) *mdag.Node { + c := &mdag.Node{Data: []byte(string(a.Data) + "/" + name)} + addLink(t, a, c) + return c +} From f5a76acd6323cd472daac0ba210af1ae16496c07 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 8 Jan 2015 16:52:23 -0800 Subject: [PATCH 0608/3817] path: ignore prefix /ipfs/ This commit was moved from ipfs/go-path@b70a21e5faf7714c04a487d731befd1d818dbbd1 --- path/path.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/path/path.go b/path/path.go index 63d718656..35ea36705 100644 --- a/path/path.go +++ b/path/path.go @@ -26,6 +26,10 @@ func (s *Resolver) ResolvePath(fpath string) (*merkledag.Node, error) { log.Debugf("Resolve: '%s'", fpath) fpath = path.Clean(fpath) + if strings.HasPrefix(fpath, "/ipfs/") { + fpath = fpath[6:] + } + parts := strings.Split(fpath, "/") // skip over empty first elem From e04825e86536198ef5d27d7c51d1daacdc4634d6 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 4 Dec 2014 21:31:38 -0800 Subject: [PATCH 0609/3817] ping WIP License: MIT Signed-off-by: Brian Tiger Chow Conflicts: core/commands/root.go begin ping command, WIP finish initial ping implementation This commit was moved from ipfs/go-ipfs-routing@6ba5352373cbb3823a0d142f6103fc22e1498835 --- routing/mock/centralized_client.go | 4 ++++ routing/routing.go | 3 +++ 2 files changed, 7 insertions(+) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 2aeafe026..da8e39692 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -79,4 +79,8 @@ func (c *client) Provide(_ context.Context, key u.Key) error { return c.server.Announce(info, key) } +func (c *client) Ping(ctx context.Context, p peer.ID) error { + return nil +} + var _ routing.IpfsRouting = &client{} diff --git a/routing/routing.go b/routing/routing.go index 1fbd79d25..934a00c41 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -36,4 +36,7 @@ type IpfsRouting interface { // FindPeer searches for a peer with given ID, returns a peer.PeerInfo // with relevant addresses. FindPeer(context.Context, peer.ID) (peer.PeerInfo, error) + + // Ping a peer, log the time it took + Ping(context.Context, peer.ID) error } From 82bdc4a1ba52267e7001342992c3645e24a70ddf Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 9 Jan 2015 19:04:13 +0000 Subject: [PATCH 0610/3817] Address PR comments and add in more user feedback This commit was moved from ipfs/go-ipfs-routing@025a8f9daef47de30c662ade37321929b9204c86 --- routing/dht/dht.go | 6 +++--- routing/dht/routing.go | 7 +++++-- routing/mock/centralized_client.go | 5 +++-- routing/routing.go | 3 ++- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index d6a6d8770..78fef653b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -103,8 +103,8 @@ func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.ID) error { // Ping new peer to register in their routing table // NOTE: this should be done better... - if err := dht.Ping(ctx, npeer); err != nil { - return fmt.Errorf("failed to ping newly connected peer: %s\n", err) + if _, err := dht.Ping(ctx, npeer); err != nil { + return fmt.Errorf("failed to ping newly connected peer: %s", err) } log.Event(ctx, "connect", dht.self, npeer) dht.Update(ctx, npeer) @@ -329,7 +329,7 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { peers := dht.routingTable.NearestPeers(kb.ConvertKey(u.Key(id)), 5) for _, p := range peers { ctx, _ := context.WithTimeout(dht.Context(), time.Second*5) - err := dht.Ping(ctx, p) + _, err := dht.Ping(ctx, p) if err != nil { log.Errorf("Ping error: %s", err) } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 5978a9a80..cd929c255 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -3,6 +3,7 @@ package dht import ( "math" "sync" + "time" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -434,12 +435,14 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< } // Ping a peer, log the time it took -func (dht *IpfsDHT) Ping(ctx context.Context, p peer.ID) error { +func (dht *IpfsDHT) Ping(ctx context.Context, p peer.ID) (time.Duration, error) { // Thoughts: maybe this should accept an ID and do a peer lookup? log.Debugf("ping %s start", p) + before := time.Now() pmes := pb.NewMessage(pb.Message_PING, "", 0) _, err := dht.sendRequest(ctx, p, pmes) log.Debugf("ping %s end (err = %s)", p, err) - return err + + return time.Now().Sub(before), err } diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index da8e39692..4a9b63b01 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -2,6 +2,7 @@ package mockrouting import ( "errors" + "time" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" @@ -79,8 +80,8 @@ func (c *client) Provide(_ context.Context, key u.Key) error { return c.server.Announce(info, key) } -func (c *client) Ping(ctx context.Context, p peer.ID) error { - return nil +func (c *client) Ping(ctx context.Context, p peer.ID) (time.Duration, error) { + return 0, nil } var _ routing.IpfsRouting = &client{} diff --git a/routing/routing.go b/routing/routing.go index 934a00c41..8238aa45c 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -3,6 +3,7 @@ package routing import ( "errors" + "time" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -38,5 +39,5 @@ type IpfsRouting interface { FindPeer(context.Context, peer.ID) (peer.PeerInfo, error) // Ping a peer, log the time it took - Ping(context.Context, peer.ID) error + Ping(context.Context, peer.ID) (time.Duration, error) } From 9febcc7fb2d0370edd8257602fbb13e575455075 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 9 Jan 2015 22:37:13 +0000 Subject: [PATCH 0611/3817] add peer info after FindPeer RPC fix ping test This commit was moved from ipfs/go-ipfs-routing@deb6748fe26819edf1490f1933118756bb7ed572 --- routing/dht/dht_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 4d63c0e44..818fd5911 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -116,12 +116,12 @@ func TestPing(t *testing.T) { //Test that we can ping the node ctxT, _ := context.WithTimeout(ctx, 100*time.Millisecond) - if err := dhtA.Ping(ctxT, peerB); err != nil { + if _, err := dhtA.Ping(ctxT, peerB); err != nil { t.Fatal(err) } ctxT, _ = context.WithTimeout(ctx, 100*time.Millisecond) - if err := dhtB.Ping(ctxT, peerA); err != nil { + if _, err := dhtB.Ping(ctxT, peerA); err != nil { t.Fatal(err) } } From 78497d220f7dace51cfa965c91793a107dc9616a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 10 Jan 2015 08:00:03 +0000 Subject: [PATCH 0612/3817] mark ipns as readonly This commit was moved from ipfs/go-unixfs@22252f25d663f5bb4da4a79efc0e72f405ae8eb4 --- unixfs/io/dagmodifier_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index e4020c64a..43e7fcb08 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -93,6 +93,7 @@ func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) } func TestDagModifierBasic(t *testing.T) { + t.Skip("DAGModifier needs to be fixed to work with indirect blocks.") logging.SetLevel(logging.CRITICAL, "blockservice") logging.SetLevel(logging.CRITICAL, "merkledag") dserv := getMockDagServ(t) @@ -146,6 +147,7 @@ func TestDagModifierBasic(t *testing.T) { } func TestMultiWrite(t *testing.T) { + t.Skip("DAGModifier needs to be fixed to work with indirect blocks.") dserv := getMockDagServ(t) _, n := getNode(t, dserv, 0) From 25c4c807a8bf95e92a6c29c847ff1e4efb66f414 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 9 Jan 2015 16:37:20 -0800 Subject: [PATCH 0613/3817] updated datastore (Query) This commit was moved from ipfs/go-ipfs-blockstore@0907fa92f739473174677e08104b04c0404f5f1f --- blockstore/write_cache_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/blockstore/write_cache_test.go b/blockstore/write_cache_test.go index c2175a1fc..1e072e95e 100644 --- a/blockstore/write_cache_test.go +++ b/blockstore/write_cache_test.go @@ -4,6 +4,7 @@ import ( "testing" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dsq "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" syncds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" "github.com/jbenet/go-ipfs/blocks" ) @@ -83,7 +84,7 @@ func (c *callbackDatastore) Delete(key ds.Key) (err error) { return c.ds.Delete(key) } -func (c *callbackDatastore) KeyList() ([]ds.Key, error) { +func (c *callbackDatastore) Query(q dsq.Query) (*dsq.Results, error) { c.f() - return c.ds.KeyList() + return c.ds.Query(q) } From 8a37c349094800c1bf91aa10db2b14467c551b53 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 9 Jan 2015 17:39:56 -0800 Subject: [PATCH 0614/3817] blocks: AllKeys + tests This commit was moved from ipfs/go-ipfs-blockstore@8ecb97e32932546fde655a91c6deb4edca7b25ff --- blockstore/blockstore.go | 35 +++++++++++++++++++-- blockstore/blockstore_test.go | 59 ++++++++++++++++++++++++++++++++++- blockstore/write_cache.go | 4 +++ 3 files changed, 95 insertions(+), 3 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index e203ffc50..6132d155e 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -6,12 +6,17 @@ import ( "errors" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dsns "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" + dsq "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" blocks "github.com/jbenet/go-ipfs/blocks" u "github.com/jbenet/go-ipfs/util" ) +// BlockPrefix namespaces blockstore datastores +var BlockPrefix = ds.NewKey("blocks") + var ValueTypeMismatch = errors.New("The retrieved value is not a Block") var ErrNotFound = errors.New("blockstore: block not found") @@ -22,16 +27,20 @@ type Blockstore interface { Has(u.Key) (bool, error) Get(u.Key) (*blocks.Block, error) Put(*blocks.Block) error + AllKeys(offset int, limit int) ([]u.Key, error) } func NewBlockstore(d ds.ThreadSafeDatastore) Blockstore { + dd := dsns.Wrap(d, BlockPrefix) return &blockstore{ - datastore: d, + datastore: dd, } } type blockstore struct { - datastore ds.ThreadSafeDatastore + datastore ds.Datastore + // cant be ThreadSafeDatastore cause namespace.Datastore doesnt support it. + // we do check it on `NewBlockstore` though. } func (bs *blockstore) Get(k u.Key) (*blocks.Block, error) { @@ -67,3 +76,25 @@ func (bs *blockstore) Has(k u.Key) (bool, error) { func (s *blockstore) DeleteBlock(k u.Key) error { return s.datastore.Delete(k.DsKey()) } + +// AllKeys runs a query for keys from the blockstore. +// this is very simplistic, in the future, take dsq.Query as a param? +// if offset and limit are 0, they are ignored. +func (bs *blockstore) AllKeys(offset int, limit int) ([]u.Key, error) { + var keys []u.Key + + // TODO make async inside ds/leveldb.Query + // KeysOnly, because that would be _a lot_ of data. + q := dsq.Query{KeysOnly: true, Offset: offset, Limit: limit} + res, err := bs.datastore.Query(q) + if err != nil { + return nil, err + } + + for e := range res.Entries() { + // need to convert to u.Key using u.KeyFromDsKey. + k := u.KeyFromDsKey(ds.NewKey(e.Key)) + keys = append(keys, k) + } + return keys, nil +} diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 00edf61ab..a80ce8337 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -2,6 +2,7 @@ package blockstore import ( "bytes" + "fmt" "testing" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" @@ -41,11 +42,49 @@ func TestPutThenGetBlock(t *testing.T) { } } +func TestAllKeys(t *testing.T) { + bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) + N := 100 + + keys := make([]u.Key, N) + for i := 0; i < N; i++ { + block := blocks.NewBlock([]byte(fmt.Sprintf("some data %d", i))) + err := bs.Put(block) + if err != nil { + t.Fatal(err) + } + keys[i] = block.Key() + } + + keys2, err := bs.AllKeys(0, 0) + if err != nil { + t.Fatal(err) + } + // for _, k2 := range keys2 { + // t.Log("found ", k2.Pretty()) + // } + + expectMatches(t, keys, keys2) + + keys3, err := bs.AllKeys(N/3, N/3) + if err != nil { + t.Fatal(err) + } + for _, k3 := range keys3 { + t.Log("found ", k3.Pretty()) + } + if len(keys3) != N/3 { + t.Errorf("keys3 should be: %d != %d", N/3, len(keys3)) + } + +} + func TestValueTypeMismatch(t *testing.T) { block := blocks.NewBlock([]byte("some data")) datastore := ds.NewMapDatastore() - datastore.Put(block.Key().DsKey(), "data that isn't a block!") + k := BlockPrefix.Child(block.Key().DsKey()) + datastore.Put(k, "data that isn't a block!") blockstore := NewBlockstore(ds_sync.MutexWrap(datastore)) @@ -54,3 +93,21 @@ func TestValueTypeMismatch(t *testing.T) { t.Fatal(err) } } + +func expectMatches(t *testing.T, expect, actual []u.Key) { + + if len(expect) != len(actual) { + t.Errorf("expect and actual differ: %d != %d", len(expect), len(actual)) + } + for _, ek := range expect { + found := false + for _, ak := range actual { + if ek == ak { + found = true + } + } + if !found { + t.Error("expected key not found: ", ek) + } + } +} diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index b46d05846..da9a0a01d 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -43,3 +43,7 @@ func (w *writecache) Put(b *blocks.Block) error { w.cache.Add(b.Key(), struct{}{}) return w.blockstore.Put(b) } + +func (w *writecache) AllKeys(offset int, limit int) ([]u.Key, error) { + return w.blockstore.AllKeys(offset, limit) +} From ca46205133c919c29becb62539bd39dcac3ff607 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 10 Jan 2015 14:31:40 -0800 Subject: [PATCH 0615/3817] updated datastore for proper query handling Queries now can be cancelled and the resources collected This commit was moved from ipfs/go-ipfs-blockstore@083e0167a740d2f42f2efec3351a132d2df40b5f --- blockstore/blockstore.go | 77 +++++++++++++-- blockstore/blockstore_test.go | 165 ++++++++++++++++++++++++++++++++- blockstore/write_cache.go | 10 +- blockstore/write_cache_test.go | 2 +- 4 files changed, 238 insertions(+), 16 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 6132d155e..63d5df54b 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -5,6 +5,7 @@ package blockstore import ( "errors" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" dsns "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" dsq "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" @@ -12,8 +13,11 @@ import ( blocks "github.com/jbenet/go-ipfs/blocks" u "github.com/jbenet/go-ipfs/util" + eventlog "github.com/jbenet/go-ipfs/util/eventlog" ) +var log = eventlog.Logger("blockstore") + // BlockPrefix namespaces blockstore datastores var BlockPrefix = ds.NewKey("blocks") @@ -27,7 +31,9 @@ type Blockstore interface { Has(u.Key) (bool, error) Get(u.Key) (*blocks.Block, error) Put(*blocks.Block) error - AllKeys(offset int, limit int) ([]u.Key, error) + + AllKeys(ctx context.Context, offset int, limit int) ([]u.Key, error) + AllKeysChan(ctx context.Context, offset int, limit int) (<-chan u.Key, error) } func NewBlockstore(d ds.ThreadSafeDatastore) Blockstore { @@ -80,10 +86,29 @@ func (s *blockstore) DeleteBlock(k u.Key) error { // AllKeys runs a query for keys from the blockstore. // this is very simplistic, in the future, take dsq.Query as a param? // if offset and limit are 0, they are ignored. -func (bs *blockstore) AllKeys(offset int, limit int) ([]u.Key, error) { +// +// AllKeys respects context +func (bs *blockstore) AllKeys(ctx context.Context, offset int, limit int) ([]u.Key, error) { + + ch, err := bs.AllKeysChan(ctx, offset, limit) + if err != nil { + return nil, err + } + var keys []u.Key + for k := range ch { + keys = append(keys, k) + } + return keys, nil +} + +// AllKeys runs a query for keys from the blockstore. +// this is very simplistic, in the future, take dsq.Query as a param? +// if offset and limit are 0, they are ignored. +// +// AllKeys respects context +func (bs *blockstore) AllKeysChan(ctx context.Context, offset int, limit int) (<-chan u.Key, error) { - // TODO make async inside ds/leveldb.Query // KeysOnly, because that would be _a lot_ of data. q := dsq.Query{KeysOnly: true, Offset: offset, Limit: limit} res, err := bs.datastore.Query(q) @@ -91,10 +116,46 @@ func (bs *blockstore) AllKeys(offset int, limit int) ([]u.Key, error) { return nil, err } - for e := range res.Entries() { - // need to convert to u.Key using u.KeyFromDsKey. - k := u.KeyFromDsKey(ds.NewKey(e.Key)) - keys = append(keys, k) + // this function is here to compartmentalize + get := func() (k u.Key, ok bool) { + select { + case <-ctx.Done(): + return k, false + case e, more := <-res.Next(): + if !more { + return k, false + } + if e.Error != nil { + log.Debug("blockstore.AllKeysChan got err:", e.Error) + return k, false + } + + // need to convert to u.Key using u.KeyFromDsKey. + k = u.KeyFromDsKey(ds.NewKey(e.Key)) + return k, true + } } - return keys, nil + + output := make(chan u.Key) + go func() { + defer func() { + res.Process().Close() // ensure exit (signals early exit, too) + close(output) + }() + + for { + k, ok := get() + if !ok { + return + } + + select { + case <-ctx.Done(): + return + case output <- k: + } + } + }() + + return output, nil } diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index a80ce8337..de74d6d83 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -5,8 +5,11 @@ import ( "fmt" "testing" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dsq "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" ds_sync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + blocks "github.com/jbenet/go-ipfs/blocks" u "github.com/jbenet/go-ipfs/util" ) @@ -42,9 +45,11 @@ func TestPutThenGetBlock(t *testing.T) { } } -func TestAllKeys(t *testing.T) { - bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) - N := 100 +func newBlockStoreWithKeys(t *testing.T, d ds.Datastore, N int) (Blockstore, []u.Key) { + if d == nil { + d = ds.NewMapDatastore() + } + bs := NewBlockstore(ds_sync.MutexWrap(d)) keys := make([]u.Key, N) for i := 0; i < N; i++ { @@ -55,8 +60,14 @@ func TestAllKeys(t *testing.T) { } keys[i] = block.Key() } + return bs, keys +} + +func TestAllKeysSimple(t *testing.T) { + bs, keys := newBlockStoreWithKeys(t, nil, 100) - keys2, err := bs.AllKeys(0, 0) + ctx := context.Background() + keys2, err := bs.AllKeys(ctx, 0, 0) if err != nil { t.Fatal(err) } @@ -65,8 +76,14 @@ func TestAllKeys(t *testing.T) { // } expectMatches(t, keys, keys2) +} - keys3, err := bs.AllKeys(N/3, N/3) +func TestAllKeysOffsetAndLimit(t *testing.T) { + N := 30 + bs, _ := newBlockStoreWithKeys(t, nil, N) + + ctx := context.Background() + keys3, err := bs.AllKeys(ctx, N/3, N/3) if err != nil { t.Fatal(err) } @@ -76,6 +93,114 @@ func TestAllKeys(t *testing.T) { if len(keys3) != N/3 { t.Errorf("keys3 should be: %d != %d", N/3, len(keys3)) } +} + +func TestAllKeysRespectsContext(t *testing.T) { + N := 100 + + d := &queryTestDS{ds: ds.NewMapDatastore()} + bs, _ := newBlockStoreWithKeys(t, d, N) + + started := make(chan struct{}, 1) + done := make(chan struct{}, 1) + errors := make(chan error, 100) + + getKeys := func(ctx context.Context) { + started <- struct{}{} + _, err := bs.AllKeys(ctx, 0, 0) // once without cancelling + if err != nil { + errors <- err + } + done <- struct{}{} + errors <- nil // a nil one to signal break + } + + // Once without context, to make sure it all works + { + var results dsq.Results + resultChan := make(chan dsq.Result) + d.SetFunc(func(q dsq.Query) (dsq.Results, error) { + results = dsq.ResultsWithChan(q, resultChan) + return results, nil + }) + + go getKeys(context.Background()) + + // make sure it's waiting. + <-started + select { + case <-done: + t.Fatal("sync is wrong") + case <-results.Process().Closing(): + t.Fatal("should not be closing") + case <-results.Process().Closed(): + t.Fatal("should not be closed") + default: + } + + e := dsq.Entry{Key: BlockPrefix.ChildString("foo").String()} + resultChan <- dsq.Result{Entry: e} // let it go. + close(resultChan) + <-done // should be done now. + <-results.Process().Closed() // should be closed now + + // print any errors + for err := range errors { + if err == nil { + break + } + t.Error(err) + } + } + + // Once with + { + var results dsq.Results + resultChan := make(chan dsq.Result) + d.SetFunc(func(q dsq.Query) (dsq.Results, error) { + results = dsq.ResultsWithChan(q, resultChan) + return results, nil + }) + + ctx, cancel := context.WithCancel(context.Background()) + go getKeys(ctx) + + // make sure it's waiting. + <-started + select { + case <-done: + t.Fatal("sync is wrong") + case <-results.Process().Closing(): + t.Fatal("should not be closing") + case <-results.Process().Closed(): + t.Fatal("should not be closed") + default: + } + + cancel() // let it go. + + select { + case <-done: + t.Fatal("sync is wrong") + case <-results.Process().Closed(): + t.Fatal("should not be closed") // should not be closed yet. + case <-results.Process().Closing(): + // should be closing now! + t.Log("closing correctly at this point.") + } + + close(resultChan) + <-done // should be done now. + <-results.Process().Closed() // should be closed now + + // print any errors + for err := range errors { + if err == nil { + break + } + t.Error(err) + } + } } @@ -111,3 +236,33 @@ func expectMatches(t *testing.T, expect, actual []u.Key) { } } } + +type queryTestDS struct { + cb func(q dsq.Query) (dsq.Results, error) + ds ds.Datastore +} + +func (c *queryTestDS) SetFunc(f func(dsq.Query) (dsq.Results, error)) { c.cb = f } + +func (c *queryTestDS) Put(key ds.Key, value interface{}) (err error) { + return c.ds.Put(key, value) +} + +func (c *queryTestDS) Get(key ds.Key) (value interface{}, err error) { + return c.ds.Get(key) +} + +func (c *queryTestDS) Has(key ds.Key) (exists bool, err error) { + return c.ds.Has(key) +} + +func (c *queryTestDS) Delete(key ds.Key) (err error) { + return c.ds.Delete(key) +} + +func (c *queryTestDS) Query(q dsq.Query) (dsq.Results, error) { + if c.cb != nil { + return c.cb(q) + } + return c.ds.Query(q) +} diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index da9a0a01d..377ce629d 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -1,7 +1,9 @@ package blockstore import ( + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" + "github.com/jbenet/go-ipfs/blocks" u "github.com/jbenet/go-ipfs/util" ) @@ -44,6 +46,10 @@ func (w *writecache) Put(b *blocks.Block) error { return w.blockstore.Put(b) } -func (w *writecache) AllKeys(offset int, limit int) ([]u.Key, error) { - return w.blockstore.AllKeys(offset, limit) +func (w *writecache) AllKeys(ctx context.Context, offset int, limit int) ([]u.Key, error) { + return w.blockstore.AllKeys(ctx, offset, limit) +} + +func (w *writecache) AllKeysChan(ctx context.Context, offset int, limit int) (<-chan u.Key, error) { + return w.blockstore.AllKeysChan(ctx, offset, limit) } diff --git a/blockstore/write_cache_test.go b/blockstore/write_cache_test.go index 1e072e95e..b20188e29 100644 --- a/blockstore/write_cache_test.go +++ b/blockstore/write_cache_test.go @@ -84,7 +84,7 @@ func (c *callbackDatastore) Delete(key ds.Key) (err error) { return c.ds.Delete(key) } -func (c *callbackDatastore) Query(q dsq.Query) (*dsq.Results, error) { +func (c *callbackDatastore) Query(q dsq.Query) (dsq.Results, error) { c.f() return c.ds.Query(q) } From e0ab059e0036507d8ea291e8645ead819ae02f7e Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 10 Jan 2015 20:44:51 -0800 Subject: [PATCH 0616/3817] refs: tie the contexts together This commit was moved from ipfs/go-ipfs-blockstore@b92267ea7eb938c1354698be06572feb36dc96c7 --- blockstore/blockstore.go | 1 + 1 file changed, 1 insertion(+) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 63d5df54b..484e15154 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -132,6 +132,7 @@ func (bs *blockstore) AllKeysChan(ctx context.Context, offset int, limit int) (< // need to convert to u.Key using u.KeyFromDsKey. k = u.KeyFromDsKey(ds.NewKey(e.Key)) + log.Debug("blockstore: query got key", k) return k, true } } From 164413edffee322418502e3ec079cbb0fc558341 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 11 Jan 2015 19:33:37 -0800 Subject: [PATCH 0617/3817] change block datastore prefix to "/b" the prefix should be as short as possible, as this is a per-block overhead. This commit was moved from ipfs/go-ipfs-blockstore@50d8212ea993dae0357cca689ba9de2f6f8960e0 --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 484e15154..ed24326c9 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -19,7 +19,7 @@ import ( var log = eventlog.Logger("blockstore") // BlockPrefix namespaces blockstore datastores -var BlockPrefix = ds.NewKey("blocks") +var BlockPrefix = ds.NewKey("b") var ValueTypeMismatch = errors.New("The retrieved value is not a Block") From badbfae9fa44a96e04b48ac578fa72f9c7e0a395 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 11 Jan 2015 21:19:42 -0800 Subject: [PATCH 0618/3817] blockstore Allkeys: ignore non multihash keys This commit was moved from ipfs/go-ipfs-blockstore@145c531c16ea52f5abd175bef5066df57a89ca5f --- blockstore/blockstore.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index ed24326c9..e4a9f7745 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -133,6 +133,13 @@ func (bs *blockstore) AllKeysChan(ctx context.Context, offset int, limit int) (< // need to convert to u.Key using u.KeyFromDsKey. k = u.KeyFromDsKey(ds.NewKey(e.Key)) log.Debug("blockstore: query got key", k) + + // key must be a multihash. else ignore it. + _, err := mh.Cast([]byte(k)) + if err != nil { + return "", true + } + return k, true } } @@ -149,6 +156,9 @@ func (bs *blockstore) AllKeysChan(ctx context.Context, offset int, limit int) (< if !ok { return } + if k == "" { + continue + } select { case <-ctx.Done(): From 4bf3d23fd2d367915e6a5eb18330f9915114aff4 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 12 Jan 2015 12:21:04 -0800 Subject: [PATCH 0619/3817] p2p/net/swarm: do not usre link local addrs This commit was moved from ipfs/go-ipfs-routing@831115dff5747d31d22b088ef083b2ceffe869b6 --- routing/dht/dht.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 78fef653b..9b1279f10 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -66,8 +66,13 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip dht.peerstore = h.Peerstore() dht.ContextGroup = ctxgroup.WithContext(ctx) dht.host = h - h.SetStreamHandler(ProtocolDHT, dht.handleNewStream) + // sanity check. this should **never** happen + if len(dht.peerstore.Addresses(dht.self)) < 1 { + panic("attempt to initialize dht without addresses for self") + } + + h.SetStreamHandler(ProtocolDHT, dht.handleNewStream) dht.providers = NewProviderManager(dht.Context(), dht.self) dht.AddChildGroup(dht.providers) @@ -132,19 +137,23 @@ func (dht *IpfsDHT) putValueToPeer(ctx context.Context, p peer.ID, // can provide the value of 'key' func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, key string) error { - pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, string(key), 0) - // add self as the provider pi := dht.peerstore.PeerInfo(dht.self) - pmes.ProviderPeers = pb.PeerInfosToPBPeers(dht.host.Network(), []peer.PeerInfo{pi}) + // // only share WAN-friendly addresses ?? + // pi.Addrs = addrutil.WANShareableAddrs(pi.Addrs) + if len(pi.Addrs) < 1 { + log.Errorf("%s putProvider: %s for %s error: no wan-friendly addresses", dht.self, p, u.Key(key), pi.Addrs) + return fmt.Errorf("no known addresses for self. cannot put provider.") + } + pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, string(key), 0) + pmes.ProviderPeers = pb.PeerInfosToPBPeers(dht.host.Network(), []peer.PeerInfo{pi}) err := dht.sendMessage(ctx, p, pmes) if err != nil { return err } log.Debugf("%s putProvider: %s for %s (%s)", dht.self, p, u.Key(key), pi.Addrs) - return nil } From e9a918ac6f9e63e81c877545412f7de4d9b49ce0 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 11 Jan 2015 21:31:19 -0800 Subject: [PATCH 0620/3817] race fix: pinner loads with a threadsafe datastore All the datastores used by pinners and so on should be mutex wrapped. One issue with changing all of them from ds.Datastore -> ds.ThreadSafeDatastore is that we wrap the incoming ds.ThreadSafeDatastore with other datastores, which do not implement the interface. Re-wrapping again causes double locking. (which may be ok..., but...) any ideas? This commit was moved from ipfs/go-ipfs-pinner@5c2d22bc2705bd88dc02ba1d688704da478d29f4 --- pinning/pinner/pin.go | 6 +++--- pinning/pinner/pin_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 371497da6..32b85a9d7 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -53,11 +53,11 @@ type pinner struct { directPin set.BlockSet indirPin *indirectPin dserv mdag.DAGService - dstore ds.Datastore + dstore ds.ThreadSafeDatastore } // NewPinner creates a new pinner using the given datastore as a backend -func NewPinner(dstore ds.Datastore, serv mdag.DAGService) Pinner { +func NewPinner(dstore ds.ThreadSafeDatastore, serv mdag.DAGService) Pinner { // Load set from given datastore... rcds := nsds.Wrap(dstore, recursePinDatastoreKey) @@ -176,7 +176,7 @@ func (p *pinner) IsPinned(key util.Key) bool { } // LoadPinner loads a pinner and its keysets from the given datastore -func LoadPinner(d ds.Datastore, dserv mdag.DAGService) (Pinner, error) { +func LoadPinner(d ds.ThreadSafeDatastore, dserv mdag.DAGService) (Pinner, error) { p := new(pinner) { // load recursive set diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index dada99803..623983a34 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -21,8 +21,8 @@ func randNode() (*mdag.Node, util.Key) { } func TestPinnerBasic(t *testing.T) { - dstore := ds.NewMapDatastore() - bstore := blockstore.NewBlockstore(dssync.MutexWrap(dstore)) + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) bserv, err := bs.New(bstore, offline.Exchange(bstore)) if err != nil { t.Fatal(err) From 657f3342470cd0e563ca6ba131e1383f2fa5753e Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 11 Jan 2015 21:31:19 -0800 Subject: [PATCH 0621/3817] race fix: pinner loads with a threadsafe datastore All the datastores used by pinners and so on should be mutex wrapped. One issue with changing all of them from ds.Datastore -> ds.ThreadSafeDatastore is that we wrap the incoming ds.ThreadSafeDatastore with other datastores, which do not implement the interface. Re-wrapping again causes double locking. (which may be ok..., but...) any ideas? This commit was moved from ipfs/go-merkledag@559d20fd985ee5ea4f5cc51c76406e03179f3b9b --- ipld/merkledag/merkledag_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index e44870b8c..9dbfad454 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -28,8 +28,8 @@ type dagservAndPinner struct { } func getDagservAndPinner(t *testing.T) dagservAndPinner { - db := ds.NewMapDatastore() - bs := bstore.NewBlockstore(dssync.MutexWrap(db)) + db := dssync.MutexWrap(ds.NewMapDatastore()) + bs := bstore.NewBlockstore(db) blockserv, err := bserv.New(bs, offline.Exchange(bs)) if err != nil { t.Fatal(err) From e2e0f31119cec08c285df47b81f07fca14eaa072 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 15 Jan 2015 04:17:17 +0000 Subject: [PATCH 0622/3817] starting to move important events over to EventBegin/Done This commit was moved from ipfs/go-ipfs-routing@2dfdf35070e9845ab53654561346cf04c836f150 --- routing/dht/dht.go | 8 ++++++++ routing/dht/routing.go | 15 ++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 9b1279f10..c0b27ea90 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -196,6 +196,8 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.ID, // getValueSingle simply performs the get value RPC with the given parameters func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.ID, key u.Key) (*pb.Message, error) { + e := log.EventBegin(ctx, "getValueSingle", p, &key) + defer e.Done() pmes := pb.NewMessage(pb.Message_GET_VALUE, string(key), 0) return dht.sendRequest(ctx, p, pmes) @@ -265,11 +267,17 @@ func (dht *IpfsDHT) FindLocal(id peer.ID) peer.PeerInfo { // findPeerSingle asks peer 'p' if they know where the peer with id 'id' is func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.ID, id peer.ID) (*pb.Message, error) { + e := log.EventBegin(ctx, "findPeerSingle", p, id) + defer e.Done() + pmes := pb.NewMessage(pb.Message_FIND_NODE, string(id), 0) return dht.sendRequest(ctx, p, pmes) } func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.ID, key u.Key) (*pb.Message, error) { + e := log.EventBegin(ctx, "findProvidersSingle", p, &key) + defer e.Done() + pmes := pb.NewMessage(pb.Message_GET_PROVIDERS, string(key), 0) return dht.sendRequest(ctx, p, pmes) } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index cd929c255..3ce5d5ec6 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -122,10 +122,12 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { // Provide makes this node announce that it can provide a value for the given key func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { log := dht.log().Prefix("Provide(%s)", key) + log.Debugf("start", key) - log.Event(ctx, "provideBegin", &key) defer log.Debugf("end", key) - defer log.Event(ctx, "provideEnd", &key) + + e := log.EventBegin(ctx, "provide", &key) + defer e.Done() // add self locally dht.providers.AddProvider(key, dht.self) @@ -163,6 +165,7 @@ func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]peer.PeerIn // Kademlia 'node lookup' operation. Returns a channel of the K closest peers // to the given key func (dht *IpfsDHT) getClosestPeers(ctx context.Context, key u.Key) (<-chan peer.ID, error) { + e := log.EventBegin(ctx, "getClosestPeers", &key) tablepeers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) if len(tablepeers) == 0 { return nil, errors.Wrap(kb.ErrLookupFailure) @@ -204,6 +207,7 @@ func (dht *IpfsDHT) getClosestPeers(ctx context.Context, key u.Key) (<-chan peer go func() { defer close(out) + defer e.Done() // run it! _, err := query.Run(ctx, tablepeers) if err != nil { @@ -242,10 +246,9 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, count int, peerOut chan peer.PeerInfo) { log := dht.log().Prefix("FindProviders(%s)", key) + e := log.EventBegin(ctx, "findProvidersAsync", &key) + defer e.Done() defer close(peerOut) - defer log.Event(ctx, "findProviders end", &key) - log.Debug("begin") - defer log.Debug("begin") ps := pset.NewLimited(count) provs := dht.providers.GetProviders(ctx, key) @@ -314,6 +317,8 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co // FindPeer searches for a peer with given ID. func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, error) { + e := log.EventBegin(ctx, "FindPeer", id) + defer e.Done() // Check if were already connected to them if pi := dht.FindLocal(id); pi.ID != "" { From d045cc40dc84548bfbe14ade74fe38207896e94c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 15 Jan 2015 04:45:34 +0000 Subject: [PATCH 0623/3817] rewrite as single line defer logs This commit was moved from ipfs/go-ipfs-routing@90fae2b441b2495014e6e670b311d41e35d2ded5 --- routing/dht/dht.go | 9 +++------ routing/dht/routing.go | 9 +++------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index c0b27ea90..7befb6597 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -196,8 +196,7 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.ID, // getValueSingle simply performs the get value RPC with the given parameters func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.ID, key u.Key) (*pb.Message, error) { - e := log.EventBegin(ctx, "getValueSingle", p, &key) - defer e.Done() + defer log.EventBegin(ctx, "getValueSingle", p, &key).Done() pmes := pb.NewMessage(pb.Message_GET_VALUE, string(key), 0) return dht.sendRequest(ctx, p, pmes) @@ -267,16 +266,14 @@ func (dht *IpfsDHT) FindLocal(id peer.ID) peer.PeerInfo { // findPeerSingle asks peer 'p' if they know where the peer with id 'id' is func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.ID, id peer.ID) (*pb.Message, error) { - e := log.EventBegin(ctx, "findPeerSingle", p, id) - defer e.Done() + defer log.EventBegin(ctx, "findPeerSingle", p, id).Done() pmes := pb.NewMessage(pb.Message_FIND_NODE, string(id), 0) return dht.sendRequest(ctx, p, pmes) } func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.ID, key u.Key) (*pb.Message, error) { - e := log.EventBegin(ctx, "findProvidersSingle", p, &key) - defer e.Done() + defer log.EventBegin(ctx, "findProvidersSingle", p, &key).Done() pmes := pb.NewMessage(pb.Message_GET_PROVIDERS, string(key), 0) return dht.sendRequest(ctx, p, pmes) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 3ce5d5ec6..05fa028a8 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -126,8 +126,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { log.Debugf("start", key) defer log.Debugf("end", key) - e := log.EventBegin(ctx, "provide", &key) - defer e.Done() + defer log.EventBegin(ctx, "provide", &key).Done() // add self locally dht.providers.AddProvider(key, dht.self) @@ -246,8 +245,7 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, count int, peerOut chan peer.PeerInfo) { log := dht.log().Prefix("FindProviders(%s)", key) - e := log.EventBegin(ctx, "findProvidersAsync", &key) - defer e.Done() + defer log.EventBegin(ctx, "findProvidersAsync", &key).Done() defer close(peerOut) ps := pset.NewLimited(count) @@ -317,8 +315,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co // FindPeer searches for a peer with given ID. func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, error) { - e := log.EventBegin(ctx, "FindPeer", id) - defer e.Done() + defer log.EventBegin(ctx, "FindPeer", id).Done() // Check if were already connected to them if pi := dht.FindLocal(id); pi.ID != "" { From 532e32af34a7bcd62d0f828fa38e48d869edaabc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 15 Jan 2015 17:47:36 +0000 Subject: [PATCH 0624/3817] add events for handlers This commit was moved from ipfs/go-ipfs-routing@d63da196b330a5978a50dd66a27de99aeb4eed7a --- routing/dht/handlers.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 8f66afbf6..59e30d398 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -39,6 +39,7 @@ func (dht *IpfsDHT) handlerForMsgType(t pb.Message_MessageType) dhtHandler { } func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { + defer log.EventBegin(ctx, "handleGetValue", p).Done() log.Debugf("%s handleGetValue for key: %s", dht.self, pmes.GetKey()) // setup response @@ -114,6 +115,7 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Mess // Store a value in this peer local storage func (dht *IpfsDHT) handlePutValue(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { + defer log.EventBegin(ctx, "handlePutValue", p).Done() dskey := u.Key(pmes.GetKey()).DsKey() if err := dht.verifyRecordLocally(pmes.GetRecord()); err != nil { @@ -137,6 +139,7 @@ func (dht *IpfsDHT) handlePing(_ context.Context, p peer.ID, pmes *pb.Message) ( } func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { + defer log.EventBegin(ctx, "handleFindPeer", p).Done() resp := pb.NewMessage(pmes.GetType(), "", pmes.GetClusterLevel()) var closest []peer.ID @@ -166,6 +169,7 @@ func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.ID, pmes *pb.Mess } func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { + defer log.EventBegin(ctx, "handleGetProviders", p).Done() resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) key := u.Key(pmes.GetKey()) @@ -211,6 +215,7 @@ type providerInfo struct { } func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { + defer log.EventBegin(ctx, "handleAddProvider", p).Done() key := u.Key(pmes.GetKey()) log.Debugf("%s adding %s as a provider for '%s'\n", dht.self, p, key) From fd36a6fc2158af6d25cd48bca703ebdeb2be8f49 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 15 Jan 2015 22:01:33 +0000 Subject: [PATCH 0625/3817] remove low signal log messages This commit was moved from ipfs/go-ipfs-routing@0c77275472ac09d008153b8c0e9e804a94708d02 --- routing/dht/routing.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 05fa028a8..4a2cc3518 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -123,9 +123,6 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { log := dht.log().Prefix("Provide(%s)", key) - log.Debugf("start", key) - defer log.Debugf("end", key) - defer log.EventBegin(ctx, "provide", &key).Done() // add self locally From faf353f61e8ebaa61f1b2c37c6d4574643ca45a6 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 16 Jan 2015 02:13:00 -0800 Subject: [PATCH 0626/3817] addr-explosion mitigated adding mitigated adding our own addresses where received from peers see #573 This commit was moved from ipfs/go-ipfs-routing@ee38367c198fab08025b3dd811163cd6571389fd --- routing/dht/handlers.go | 4 ++-- routing/dht/query.go | 5 +++++ routing/dht/routing.go | 6 ++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 59e30d398..28df3aea6 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -236,9 +236,9 @@ func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.M } log.Infof("received provider %s for %s (addrs: %s)", p, key, pi.Addrs) - for _, maddr := range pi.Addrs { + if pi.ID != dht.self { // dont add own addrs. // add the received addresses to our peerstore. - dht.peerstore.AddAddress(p, maddr) + dht.peerstore.AddPeerInfo(pi) } dht.providers.AddProvider(key, p) } diff --git a/routing/dht/query.go b/routing/dht/query.go index 44dc49926..6ac8fefe8 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -254,6 +254,11 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { } else if len(res.closerPeers) > 0 { log.Debugf("PEERS CLOSER -- worker for: %v (%d closer peers)", p, len(res.closerPeers)) for _, next := range res.closerPeers { + if next.ID == r.query.dht.self { // dont add self. + log.Debugf("PEERS CLOSER -- worker for: %v found self", p) + continue + } + // add their addresses to the dialer's peerstore r.query.dht.peerstore.AddPeerInfo(next) r.addPeerToQuery(cg.Context(), next.ID) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 4a2cc3518..3c72da494 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -223,8 +223,10 @@ func (dht *IpfsDHT) closerPeersSingle(ctx context.Context, key u.Key, p peer.ID) var out []peer.ID for _, pbp := range pmes.GetCloserPeers() { pid := peer.ID(pbp.GetId()) - dht.peerstore.AddAddresses(pid, pbp.Addresses()) - out = append(out, pid) + if pid != dht.self { // dont add self + dht.peerstore.AddAddresses(pid, pbp.Addresses()) + out = append(out, pid) + } } return out, nil } From 72fa79418c0aba45c5c6e565c9c7f9efed4fb9bf Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 18 Jan 2015 12:31:12 -0800 Subject: [PATCH 0627/3817] move generic packages to thirdparty (see thirdparty/README.md) This commit was moved from ipfs/go-ipfs-routing@70af83b45e473b84aef6edc2c741a981f92615d1 --- routing/dht/dht.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/query.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/interface.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 7befb6597..9f4860880 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -16,8 +16,8 @@ import ( routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" kb "github.com/jbenet/go-ipfs/routing/kbucket" + "github.com/jbenet/go-ipfs/thirdparty/eventlog" u "github.com/jbenet/go-ipfs/util" - "github.com/jbenet/go-ipfs/util/eventlog" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 61bf41ebb..680234102 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -5,7 +5,7 @@ import ( inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" - eventlog "github.com/jbenet/go-ipfs/util/eventlog" + eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" ) var log = eventlog.Logger("dht.pb") diff --git a/routing/dht/query.go b/routing/dht/query.go index 6ac8fefe8..53c232323 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -6,8 +6,8 @@ import ( peer "github.com/jbenet/go-ipfs/p2p/peer" queue "github.com/jbenet/go-ipfs/p2p/peer/queue" "github.com/jbenet/go-ipfs/routing" + eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" u "github.com/jbenet/go-ipfs/util" - eventlog "github.com/jbenet/go-ipfs/util/eventlog" pset "github.com/jbenet/go-ipfs/util/peerset" todoctr "github.com/jbenet/go-ipfs/util/todocounter" diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 526d63c68..56f61c076 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -6,8 +6,8 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" peer "github.com/jbenet/go-ipfs/p2p/peer" + delay "github.com/jbenet/go-ipfs/thirdparty/delay" u "github.com/jbenet/go-ipfs/util" - delay "github.com/jbenet/go-ipfs/util/delay" "github.com/jbenet/go-ipfs/util/testutil" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index d7dca8348..f8b034098 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -9,8 +9,8 @@ import ( ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" + delay "github.com/jbenet/go-ipfs/thirdparty/delay" u "github.com/jbenet/go-ipfs/util" - delay "github.com/jbenet/go-ipfs/util/delay" "github.com/jbenet/go-ipfs/util/testutil" ) From c5db930f038c8c039c4ce76d0bd32bb2516da05a Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 18 Jan 2015 12:31:12 -0800 Subject: [PATCH 0628/3817] move generic packages to thirdparty (see thirdparty/README.md) This commit was moved from ipfs/go-ipfs-blockstore@c637d7339b5cab26e37e8999ca07b021ffcc881f --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index e4a9f7745..3c98b0735 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -12,8 +12,8 @@ import ( mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" blocks "github.com/jbenet/go-ipfs/blocks" + eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" u "github.com/jbenet/go-ipfs/util" - eventlog "github.com/jbenet/go-ipfs/util/eventlog" ) var log = eventlog.Logger("blockstore") From 40c75ce173de2bd99dc5d7a1a36d61a6477c32bb Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 18 Jan 2015 12:31:12 -0800 Subject: [PATCH 0629/3817] move generic packages to thirdparty (see thirdparty/README.md) This commit was moved from ipfs/go-blockservice@4e4f388e59fe6ed323f58d24c1287ae4c25a2175 --- blockservice/mock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/mock.go b/blockservice/mock.go index 73fcdf2fc..541efe696 100644 --- a/blockservice/mock.go +++ b/blockservice/mock.go @@ -6,7 +6,7 @@ import ( bitswap "github.com/jbenet/go-ipfs/exchange/bitswap" tn "github.com/jbenet/go-ipfs/exchange/bitswap/testnet" mockrouting "github.com/jbenet/go-ipfs/routing/mock" - delay "github.com/jbenet/go-ipfs/util/delay" + delay "github.com/jbenet/go-ipfs/thirdparty/delay" ) // Mocks returns |n| connected mock Blockservices From 89589a3c18cc70ba09475f6368eb0837004c5d10 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 16 Jan 2015 22:46:44 +0000 Subject: [PATCH 0630/3817] fix fuse mounting issues this time, without loading the private key on every startup This commit was moved from ipfs/go-ipfs-routing@1241f4f11da894779464af81b2945fa226c4cd6e --- routing/dht/dht.go | 17 +++++++- routing/dht/ext_test.go | 8 +++- routing/dht/records.go | 14 +++--- routing/dht/routing.go | 9 +++- routing/offline/offline.go | 89 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 routing/offline/offline.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 9f4860880..e4a285528 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -10,6 +10,7 @@ import ( "sync" "time" + ci "github.com/jbenet/go-ipfs/p2p/crypto" host "github.com/jbenet/go-ipfs/p2p/host" peer "github.com/jbenet/go-ipfs/p2p/peer" protocol "github.com/jbenet/go-ipfs/p2p/protocol" @@ -234,9 +235,23 @@ func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { return rec.GetValue(), nil } +func (dht *IpfsDHT) getOwnPrivateKey() (ci.PrivKey, error) { + sk := dht.peerstore.PrivKey(dht.self) + if sk == nil { + log.Errorf("%s dht cannot get own private key!", dht.self) + return nil, fmt.Errorf("cannot get private key to sign record!") + } + return sk, nil +} + // putLocal stores the key value pair in the datastore func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { - rec, err := dht.makePutRecord(key, value) + sk, err := dht.getOwnPrivateKey() + if err != nil { + return err + } + + rec, err := MakePutRecord(sk, key, value) if err != nil { return err } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 6f12c3113..808e27b10 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -98,7 +98,13 @@ func TestGetFailures(t *testing.T) { { typ := pb.Message_GET_VALUE str := "hello" - rec, err := d.makePutRecord(u.Key(str), []byte("blah")) + + sk, err := d.getOwnPrivateKey() + if err != nil { + t.Fatal(err) + } + + rec, err := MakePutRecord(sk, u.Key(str), []byte("blah")) if err != nil { t.Fatal(err) } diff --git a/routing/dht/records.go b/routing/dht/records.go index 083eeb26e..5dbcccaaa 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -43,20 +43,20 @@ func RecordBlobForSig(r *pb.Record) []byte { } // creates and signs a dht record for the given key/value pair -func (dht *IpfsDHT) makePutRecord(key u.Key, value []byte) (*pb.Record, error) { +func MakePutRecord(sk ci.PrivKey, key u.Key, value []byte) (*pb.Record, error) { record := new(pb.Record) record.Key = proto.String(string(key)) record.Value = value - record.Author = proto.String(string(dht.self)) - blob := RecordBlobForSig(record) - sk := dht.peerstore.PrivKey(dht.self) - if sk == nil { - log.Errorf("%s dht cannot get own private key!", dht.self) - return nil, fmt.Errorf("cannot get private key to sign record!") + pkh, err := sk.GetPublic().Hash() + if err != nil { + return nil, err } + record.Author = proto.String(string(pkh)) + blob := RecordBlobForSig(record) + sig, err := sk.Sign(blob) if err != nil { return nil, err diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 3c72da494..aea4406f1 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -36,7 +36,12 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error return err } - rec, err := dht.makePutRecord(key, value) + sk, err := dht.getOwnPrivateKey() + if err != nil { + return err + } + + rec, err := MakePutRecord(sk, key, value) if err != nil { log.Error("Creation of record failed!") return err @@ -75,6 +80,8 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { if err == nil { log.Debug("have it locally") return val, nil + } else { + log.Debug("failed to get value locally: %s", err) } // get closest peers in the routing table diff --git a/routing/offline/offline.go b/routing/offline/offline.go new file mode 100644 index 000000000..7109c6abe --- /dev/null +++ b/routing/offline/offline.go @@ -0,0 +1,89 @@ +package offline + +import ( + "errors" + "time" + + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ci "github.com/jbenet/go-ipfs/p2p/crypto" + "github.com/jbenet/go-ipfs/p2p/peer" + routing "github.com/jbenet/go-ipfs/routing" + dht "github.com/jbenet/go-ipfs/routing/dht" + pb "github.com/jbenet/go-ipfs/routing/dht/pb" + eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" + u "github.com/jbenet/go-ipfs/util" +) + +var log = eventlog.Logger("offlinerouting") + +var ErrOffline = errors.New("routing system in offline mode") + +func NewOfflineRouter(dstore ds.Datastore, privkey ci.PrivKey) routing.IpfsRouting { + return &offlineRouting{ + datastore: dstore, + sk: privkey, + } +} + +type offlineRouting struct { + datastore ds.Datastore + sk ci.PrivKey +} + +func (c *offlineRouting) PutValue(ctx context.Context, key u.Key, val []byte) error { + rec, err := dht.MakePutRecord(c.sk, key, val) + if err != nil { + return err + } + data, err := proto.Marshal(rec) + if err != nil { + return err + } + + return c.datastore.Put(key.DsKey(), data) +} + +func (c *offlineRouting) GetValue(ctx context.Context, key u.Key) ([]byte, error) { + v, err := c.datastore.Get(key.DsKey()) + if err != nil { + return nil, err + } + + byt, ok := v.([]byte) + if !ok { + return nil, errors.New("value stored in datastore not []byte") + } + rec := new(pb.Record) + err = proto.Unmarshal(byt, rec) + if err != nil { + return nil, err + } + + return rec.GetValue(), nil +} + +func (c *offlineRouting) FindProviders(ctx context.Context, key u.Key) ([]peer.PeerInfo, error) { + return nil, ErrOffline +} + +func (c *offlineRouting) FindPeer(ctx context.Context, pid peer.ID) (peer.PeerInfo, error) { + return peer.PeerInfo{}, ErrOffline +} + +func (c *offlineRouting) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-chan peer.PeerInfo { + out := make(chan peer.PeerInfo) + close(out) + return out +} + +func (c *offlineRouting) Provide(_ context.Context, key u.Key) error { + return ErrOffline +} + +func (c *offlineRouting) Ping(ctx context.Context, p peer.ID) (time.Duration, error) { + return 0, ErrOffline +} + +var _ routing.IpfsRouting = &offlineRouting{} From 2e3e4cf52a745202bafe68b82c06c519f7939d1a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 16 Jan 2015 23:53:56 +0000 Subject: [PATCH 0631/3817] some comments This commit was moved from ipfs/go-ipfs-routing@2327b3c05493e7d453fcf610b31eb330bc9acb8e --- routing/dht/dht.go | 2 ++ routing/dht/records.go | 4 +++- routing/offline/offline.go | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index e4a285528..344092057 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -235,6 +235,8 @@ func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { return rec.GetValue(), nil } +// getOwnPrivateKey attempts to load the local peers private +// key from the peerstore. func (dht *IpfsDHT) getOwnPrivateKey() (ci.PrivKey, error) { sk := dht.peerstore.PrivKey(dht.self) if sk == nil { diff --git a/routing/dht/records.go b/routing/dht/records.go index 5dbcccaaa..2dc9644cf 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -42,7 +42,7 @@ func RecordBlobForSig(r *pb.Record) []byte { return bytes.Join([][]byte{k, v, a}, []byte{}) } -// creates and signs a dht record for the given key/value pair +// MakePutRecord creates and signs a dht record for the given key/value pair func MakePutRecord(sk ci.PrivKey, key u.Key, value []byte) (*pb.Record, error) { record := new(pb.Record) @@ -175,6 +175,8 @@ func (dht *IpfsDHT) verifyRecordOnline(ctx context.Context, r *pb.Record) error return dht.verifyRecord(r, pk) } +// TODO: make this an independent exported function. +// it might be useful for users to have access to. func (dht *IpfsDHT) verifyRecord(r *pb.Record, pk ci.PubKey) error { // First, validate the signature blob := RecordBlobForSig(r) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 7109c6abe..41baf50d2 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -27,6 +27,9 @@ func NewOfflineRouter(dstore ds.Datastore, privkey ci.PrivKey) routing.IpfsRouti } } +// offlineRouting implements the IpfsRouting interface, +// but only provides the capability to Put and Get signed dht +// records to and from the local datastore. type offlineRouting struct { datastore ds.Datastore sk ci.PrivKey @@ -86,4 +89,5 @@ func (c *offlineRouting) Ping(ctx context.Context, p peer.ID) (time.Duration, er return 0, ErrOffline } +// ensure offlineRouting matches the IpfsRouting interface var _ routing.IpfsRouting = &offlineRouting{} From 91eab6319421f7f035677082bc557306d572cdf8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 17 Jan 2015 02:50:10 +0000 Subject: [PATCH 0632/3817] move dht record code into new package This commit was moved from ipfs/go-ipfs-routing@eb96dbd7e438b8e5d5b94a1260638a9abdf74f16 --- routing/dht/dht.go | 3 ++- routing/dht/ext_test.go | 3 ++- routing/dht/records.go | 36 ++------------------------------ routing/dht/routing.go | 3 ++- routing/offline/offline.go | 4 ++-- routing/record/record.go | 42 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 52 insertions(+), 39 deletions(-) create mode 100644 routing/record/record.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 344092057..25be47fac 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -17,6 +17,7 @@ import ( routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" kb "github.com/jbenet/go-ipfs/routing/kbucket" + record "github.com/jbenet/go-ipfs/routing/record" "github.com/jbenet/go-ipfs/thirdparty/eventlog" u "github.com/jbenet/go-ipfs/util" @@ -253,7 +254,7 @@ func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { return err } - rec, err := MakePutRecord(sk, key, value) + rec, err := record.MakePutRecord(sk, key, value) if err != nil { return err } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 808e27b10..e151af5d2 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -11,6 +11,7 @@ import ( peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" + record "github.com/jbenet/go-ipfs/routing/record" u "github.com/jbenet/go-ipfs/util" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -104,7 +105,7 @@ func TestGetFailures(t *testing.T) { t.Fatal(err) } - rec, err := MakePutRecord(sk, u.Key(str), []byte("blah")) + rec, err := record.MakePutRecord(sk, u.Key(str), []byte("blah")) if err != nil { t.Fatal(err) } diff --git a/routing/dht/records.go b/routing/dht/records.go index 2dc9644cf..71511b978 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -7,11 +7,11 @@ import ( "strings" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ci "github.com/jbenet/go-ipfs/p2p/crypto" "github.com/jbenet/go-ipfs/p2p/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" + record "github.com/jbenet/go-ipfs/routing/record" u "github.com/jbenet/go-ipfs/util" ctxutil "github.com/jbenet/go-ipfs/util/ctx" ) @@ -34,38 +34,6 @@ func KeyForPublicKey(id peer.ID) u.Key { return u.Key("/pk/" + string(id)) } -// RecordBlobForSig returns the blob protected by the record signature -func RecordBlobForSig(r *pb.Record) []byte { - k := []byte(r.GetKey()) - v := []byte(r.GetValue()) - a := []byte(r.GetAuthor()) - return bytes.Join([][]byte{k, v, a}, []byte{}) -} - -// MakePutRecord creates and signs a dht record for the given key/value pair -func MakePutRecord(sk ci.PrivKey, key u.Key, value []byte) (*pb.Record, error) { - record := new(pb.Record) - - record.Key = proto.String(string(key)) - record.Value = value - - pkh, err := sk.GetPublic().Hash() - if err != nil { - return nil, err - } - - record.Author = proto.String(string(pkh)) - blob := RecordBlobForSig(record) - - sig, err := sk.Sign(blob) - if err != nil { - return nil, err - } - - record.Signature = sig - return record, nil -} - func (dht *IpfsDHT) getPublicKeyOnline(ctx context.Context, p peer.ID) (ci.PubKey, error) { log.Debugf("getPublicKey for: %s", p) @@ -179,7 +147,7 @@ func (dht *IpfsDHT) verifyRecordOnline(ctx context.Context, r *pb.Record) error // it might be useful for users to have access to. func (dht *IpfsDHT) verifyRecord(r *pb.Record, pk ci.PubKey) error { // First, validate the signature - blob := RecordBlobForSig(r) + blob := record.RecordBlobForSig(r) ok, err := pk.Verify(blob, r.GetSignature()) if err != nil { log.Error("Signature verify failed.") diff --git a/routing/dht/routing.go b/routing/dht/routing.go index aea4406f1..35d804650 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,6 +12,7 @@ import ( "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" kb "github.com/jbenet/go-ipfs/routing/kbucket" + record "github.com/jbenet/go-ipfs/routing/record" u "github.com/jbenet/go-ipfs/util" errors "github.com/jbenet/go-ipfs/util/debugerror" pset "github.com/jbenet/go-ipfs/util/peerset" @@ -41,7 +42,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error return err } - rec, err := MakePutRecord(sk, key, value) + rec, err := record.MakePutRecord(sk, key, value) if err != nil { log.Error("Creation of record failed!") return err diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 41baf50d2..63fb14441 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -10,8 +10,8 @@ import ( ci "github.com/jbenet/go-ipfs/p2p/crypto" "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" - dht "github.com/jbenet/go-ipfs/routing/dht" pb "github.com/jbenet/go-ipfs/routing/dht/pb" + record "github.com/jbenet/go-ipfs/routing/record" eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" u "github.com/jbenet/go-ipfs/util" ) @@ -36,7 +36,7 @@ type offlineRouting struct { } func (c *offlineRouting) PutValue(ctx context.Context, key u.Key, val []byte) error { - rec, err := dht.MakePutRecord(c.sk, key, val) + rec, err := record.MakePutRecord(c.sk, key, val) if err != nil { return err } diff --git a/routing/record/record.go b/routing/record/record.go new file mode 100644 index 000000000..602159694 --- /dev/null +++ b/routing/record/record.go @@ -0,0 +1,42 @@ +package record + +import ( + "bytes" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + + ci "github.com/jbenet/go-ipfs/p2p/crypto" + pb "github.com/jbenet/go-ipfs/routing/dht/pb" + u "github.com/jbenet/go-ipfs/util" +) + +// MakePutRecord creates and signs a dht record for the given key/value pair +func MakePutRecord(sk ci.PrivKey, key u.Key, value []byte) (*pb.Record, error) { + record := new(pb.Record) + + record.Key = proto.String(string(key)) + record.Value = value + + pkh, err := sk.GetPublic().Hash() + if err != nil { + return nil, err + } + + record.Author = proto.String(string(pkh)) + blob := RecordBlobForSig(record) + + sig, err := sk.Sign(blob) + if err != nil { + return nil, err + } + + record.Signature = sig + return record, nil +} + +// RecordBlobForSig returns the blob protected by the record signature +func RecordBlobForSig(r *pb.Record) []byte { + k := []byte(r.GetKey()) + v := []byte(r.GetValue()) + a := []byte(r.GetAuthor()) + return bytes.Join([][]byte{k, v, a}, []byte{}) +} From bcff55d6dfa65c418a87d497578cb079b5e13ec8 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 17 Jan 2015 03:52:40 -0800 Subject: [PATCH 0633/3817] routing: record validation into record/ This commit moves the record validation/verification from dht/ into the new record/ packaage. Validator object -- which is merely a map of ValidatorFuncs -- with a VerifyRecord cc @whyrusleeping This commit was moved from ipfs/go-ipfs-routing@2218364fbaedc8a45b8d95e277bd3c893b305957 --- routing/dht/dht.go | 7 ++-- routing/dht/dht_test.go | 6 +-- routing/dht/records.go | 69 ++------------------------------- routing/record/record.go | 4 ++ routing/record/validation.go | 75 ++++++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 73 deletions(-) create mode 100644 routing/record/validation.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 25be47fac..923c8c69b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -54,8 +54,7 @@ type IpfsDHT struct { birth time.Time // When this peer started up diaglock sync.Mutex // lock to make diagnostics work better - // record validator funcs - Validators map[string]ValidatorFunc + Validator record.Validator // record validator funcs ctxgroup.ContextGroup } @@ -81,8 +80,8 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip dht.routingTable = kb.NewRoutingTable(20, kb.ConvertPeerID(dht.self), time.Minute, dht.peerstore) dht.birth = time.Now() - dht.Validators = make(map[string]ValidatorFunc) - dht.Validators["pk"] = ValidatePublicKeyRecord + dht.Validator = make(record.Validator) + dht.Validator["pk"] = record.ValidatePublicKeyRecord if doPinging { dht.Children().Add(1) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 818fd5911..07211f5fe 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -38,7 +38,7 @@ func setupDHT(ctx context.Context, t *testing.T) *IpfsDHT { dss := dssync.MutexWrap(ds.NewMapDatastore()) d := NewDHT(ctx, h, dss) - d.Validators["v"] = func(u.Key, []byte) error { + d.Validator["v"] = func(u.Key, []byte) error { return nil } return d @@ -142,8 +142,8 @@ func TestValueGetSet(t *testing.T) { vf := func(u.Key, []byte) error { return nil } - dhtA.Validators["v"] = vf - dhtB.Validators["v"] = vf + dhtA.Validator["v"] = vf + dhtB.Validator["v"] = vf connect(t, ctx, dhtA, dhtB) diff --git a/routing/dht/records.go b/routing/dht/records.go index 71511b978..14d73b6c6 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -1,33 +1,17 @@ package dht import ( - "bytes" - "errors" "fmt" - "strings" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ci "github.com/jbenet/go-ipfs/p2p/crypto" - "github.com/jbenet/go-ipfs/p2p/peer" + peer "github.com/jbenet/go-ipfs/p2p/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" - record "github.com/jbenet/go-ipfs/routing/record" u "github.com/jbenet/go-ipfs/util" ctxutil "github.com/jbenet/go-ipfs/util/ctx" ) -// ValidatorFunc is a function that is called to validate a given -// type of DHTRecord. -type ValidatorFunc func(u.Key, []byte) error - -// ErrBadRecord is returned any time a dht record is found to be -// incorrectly formatted or signed. -var ErrBadRecord = errors.New("bad dht record") - -// ErrInvalidRecordType is returned if a DHTRecord keys prefix -// is not found in the Validator map of the DHT. -var ErrInvalidRecordType = errors.New("invalid record keytype") - // KeyForPublicKey returns the key used to retrieve public keys // from the dht. func KeyForPublicKey(id peer.ID) u.Key { @@ -123,7 +107,7 @@ func (dht *IpfsDHT) verifyRecordLocally(r *pb.Record) error { return fmt.Errorf("do not have public key for %s", p) } - return dht.verifyRecord(r, pk) + return dht.Validator.VerifyRecord(r, pk) } // verifyRecordOnline verifies a record, searching the DHT for the public key @@ -140,52 +124,5 @@ func (dht *IpfsDHT) verifyRecordOnline(ctx context.Context, r *pb.Record) error return err } - return dht.verifyRecord(r, pk) -} - -// TODO: make this an independent exported function. -// it might be useful for users to have access to. -func (dht *IpfsDHT) verifyRecord(r *pb.Record, pk ci.PubKey) error { - // First, validate the signature - blob := record.RecordBlobForSig(r) - ok, err := pk.Verify(blob, r.GetSignature()) - if err != nil { - log.Error("Signature verify failed.") - return err - } - if !ok { - log.Error("dht found a forged record! (ignored)") - return ErrBadRecord - } - - // Now, check validity func - parts := strings.Split(r.GetKey(), "/") - if len(parts) < 3 { - log.Infof("Record key does not have validator: %s", u.Key(r.GetKey())) - return nil - } - - fnc, ok := dht.Validators[parts[1]] - if !ok { - log.Errorf("Unrecognized key prefix: %s", parts[1]) - return ErrInvalidRecordType - } - - return fnc(u.Key(r.GetKey()), r.GetValue()) -} - -// ValidatePublicKeyRecord implements ValidatorFunc and -// verifies that the passed in record value is the PublicKey -// that matches the passed in key. -func ValidatePublicKeyRecord(k u.Key, val []byte) error { - keyparts := bytes.Split([]byte(k), []byte("/")) - if len(keyparts) < 3 { - return errors.New("invalid key") - } - - pkh := u.Hash(val) - if !bytes.Equal(keyparts[2], pkh) { - return errors.New("public key does not match storage key") - } - return nil + return dht.Validator.VerifyRecord(r, pk) } diff --git a/routing/record/record.go b/routing/record/record.go index 602159694..e41de94ae 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -2,13 +2,17 @@ package record import ( "bytes" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ci "github.com/jbenet/go-ipfs/p2p/crypto" pb "github.com/jbenet/go-ipfs/routing/dht/pb" + eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" u "github.com/jbenet/go-ipfs/util" ) +var log = eventlog.Logger("routing/record") + // MakePutRecord creates and signs a dht record for the given key/value pair func MakePutRecord(sk ci.PrivKey, key u.Key, value []byte) (*pb.Record, error) { record := new(pb.Record) diff --git a/routing/record/validation.go b/routing/record/validation.go new file mode 100644 index 000000000..bd0913525 --- /dev/null +++ b/routing/record/validation.go @@ -0,0 +1,75 @@ +package record + +import ( + "bytes" + "errors" + "strings" + + ci "github.com/jbenet/go-ipfs/p2p/crypto" + pb "github.com/jbenet/go-ipfs/routing/dht/pb" + u "github.com/jbenet/go-ipfs/util" +) + +// ValidatorFunc is a function that is called to validate a given +// type of DHTRecord. +type ValidatorFunc func(u.Key, []byte) error + +// ErrBadRecord is returned any time a dht record is found to be +// incorrectly formatted or signed. +var ErrBadRecord = errors.New("bad dht record") + +// ErrInvalidRecordType is returned if a DHTRecord keys prefix +// is not found in the Validator map of the DHT. +var ErrInvalidRecordType = errors.New("invalid record keytype") + +// Validator is an object that helps ensure routing records are valid. +// It is a collection of validator functions, each of which implements +// its own notion of validity. +type Validator map[string]ValidatorFunc + +// VerifyRecord checks a record and ensures it is still valid. +// It runs needed validators +func (v Validator) VerifyRecord(r *pb.Record, pk ci.PubKey) error { + // First, validate the signature + blob := RecordBlobForSig(r) + ok, err := pk.Verify(blob, r.GetSignature()) + if err != nil { + log.Error("Signature verify failed.") + return err + } + if !ok { + log.Error("dht found a forged record! (ignored)") + return ErrBadRecord + } + + // Now, check validity func + parts := strings.Split(r.GetKey(), "/") + if len(parts) < 3 { + log.Infof("Record key does not have validator: %s", u.Key(r.GetKey())) + return nil + } + + fnc, ok := v[parts[1]] + if !ok { + log.Errorf("Unrecognized key prefix: %s", parts[1]) + return ErrInvalidRecordType + } + + return fnc(u.Key(r.GetKey()), r.GetValue()) +} + +// ValidatePublicKeyRecord implements ValidatorFunc and +// verifies that the passed in record value is the PublicKey +// that matches the passed in key. +func ValidatePublicKeyRecord(k u.Key, val []byte) error { + keyparts := bytes.Split([]byte(k), []byte("/")) + if len(keyparts) < 3 { + return errors.New("invalid key") + } + + pkh := u.Hash(val) + if !bytes.Equal(keyparts[2], pkh) { + return errors.New("public key does not match storage key") + } + return nil +} From 603b00182f54ee7b8b3358dd0003c39a03d8de53 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 20 Jan 2015 09:27:47 -0800 Subject: [PATCH 0634/3817] blockstore: fixed data race This commit was moved from ipfs/go-ipfs-blockstore@64d89ae7fee700459b2bc79c3c41fe8609e3c5fb --- blockstore/blockstore_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index de74d6d83..44f5964e8 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -118,9 +118,11 @@ func TestAllKeysRespectsContext(t *testing.T) { // Once without context, to make sure it all works { var results dsq.Results + var resultsmu = make(chan struct{}) resultChan := make(chan dsq.Result) d.SetFunc(func(q dsq.Query) (dsq.Results, error) { results = dsq.ResultsWithChan(q, resultChan) + resultsmu <- struct{}{} return results, nil }) @@ -128,6 +130,7 @@ func TestAllKeysRespectsContext(t *testing.T) { // make sure it's waiting. <-started + <-resultsmu select { case <-done: t.Fatal("sync is wrong") @@ -156,9 +159,11 @@ func TestAllKeysRespectsContext(t *testing.T) { // Once with { var results dsq.Results + var resultsmu = make(chan struct{}) resultChan := make(chan dsq.Result) d.SetFunc(func(q dsq.Query) (dsq.Results, error) { results = dsq.ResultsWithChan(q, resultChan) + resultsmu <- struct{}{} return results, nil }) @@ -167,6 +172,7 @@ func TestAllKeysRespectsContext(t *testing.T) { // make sure it's waiting. <-started + <-resultsmu select { case <-done: t.Fatal("sync is wrong") From 1caff9faf2d1fca40d60f7aeaa7fc962b27263e6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 19 Jan 2015 00:26:11 +0000 Subject: [PATCH 0635/3817] update pinning to new semantics, and fix a couple bugs This commit was moved from ipfs/go-ipfs-pinner@8d22c94831f7e7e994caca4e8adb173f366bc0e6 --- pinning/pinner/pin.go | 25 +++++++++++++++++++------ pinning/pinner/pin_test.go | 4 ++-- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 32b85a9d7..0ce754103 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -25,12 +25,13 @@ const ( Recursive PinMode = iota Direct Indirect + NotPinned ) type Pinner interface { IsPinned(util.Key) bool Pin(*mdag.Node, bool) error - Unpin(util.Key, bool) error + Unpin(util.Key) error Flush() error GetManual() ManualPinner DirectKeys() []util.Key @@ -90,6 +91,10 @@ func (p *pinner) Pin(node *mdag.Node, recurse bool) error { return nil } + if p.directPin.HasKey(k) { + p.directPin.RemoveBlock(k) + } + p.recursePin.AddBlock(k) err := p.pinLinks(node) @@ -97,16 +102,19 @@ func (p *pinner) Pin(node *mdag.Node, recurse bool) error { return err } } else { + if p.recursePin.HasKey(k) { + return errors.New("Key already pinned recursively.") + } p.directPin.AddBlock(k) } return nil } -// Unpin a given key with optional recursive unpinning -func (p *pinner) Unpin(k util.Key, recurse bool) error { +// Unpin a given key +func (p *pinner) Unpin(k util.Key) error { p.lock.Lock() defer p.lock.Unlock() - if recurse { + if p.recursePin.HasKey(k) { p.recursePin.RemoveBlock(k) node, err := p.dserv.Get(k) if err != nil { @@ -114,9 +122,14 @@ func (p *pinner) Unpin(k util.Key, recurse bool) error { } return p.unpinLinks(node) + } else if p.directPin.HasKey(k) { + p.directPin.RemoveBlock(k) + return nil + } else if p.indirPin.HasKey(k) { + return errors.New("Cannot unpin indirectly pinned block.") + } else { + return errors.New("Given key was not pinned.") } - p.directPin.RemoveBlock(k) - return nil } func (p *pinner) unpinLinks(node *mdag.Node) error { diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 623983a34..3a93bdf74 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -100,8 +100,8 @@ func TestPinnerBasic(t *testing.T) { t.Fatal("pinned node not found.") } - // Test recursive unpin - err = p.Unpin(dk, true) + // Test unpin + err = p.Unpin(dk) if err != nil { t.Fatal(err) } From a35e45dc920bdd85c91c63cadeac4d9bf5d97bf9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 20 Jan 2015 03:47:37 +0000 Subject: [PATCH 0636/3817] fix pinning UX, and add tests to match This commit was moved from ipfs/go-ipfs-pinner@27e8b51e6f38e2e5d1713635cf8a9171210dc31b --- pinning/pinner/pin.go | 20 ++++++++++++-------- pinning/pinner/pin_test.go | 4 ++-- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 0ce754103..ed3598e4d 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -31,7 +31,7 @@ const ( type Pinner interface { IsPinned(util.Key) bool Pin(*mdag.Node, bool) error - Unpin(util.Key) error + Unpin(util.Key, bool) error Flush() error GetManual() ManualPinner DirectKeys() []util.Key @@ -111,17 +111,21 @@ func (p *pinner) Pin(node *mdag.Node, recurse bool) error { } // Unpin a given key -func (p *pinner) Unpin(k util.Key) error { +func (p *pinner) Unpin(k util.Key, recursive bool) error { p.lock.Lock() defer p.lock.Unlock() if p.recursePin.HasKey(k) { - p.recursePin.RemoveBlock(k) - node, err := p.dserv.Get(k) - if err != nil { - return err + if recursive { + p.recursePin.RemoveBlock(k) + node, err := p.dserv.Get(k) + if err != nil { + return err + } + + return p.unpinLinks(node) + } else { + return errors.New("Key pinned recursively.") } - - return p.unpinLinks(node) } else if p.directPin.HasKey(k) { p.directPin.RemoveBlock(k) return nil diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 3a93bdf74..623983a34 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -100,8 +100,8 @@ func TestPinnerBasic(t *testing.T) { t.Fatal("pinned node not found.") } - // Test unpin - err = p.Unpin(dk) + // Test recursive unpin + err = p.Unpin(dk, true) if err != nil { t.Fatal(err) } From d1d9d6143b4b1ceb8462264df73b9fe8a54d65bf Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 20 Jan 2015 04:52:27 +0000 Subject: [PATCH 0637/3817] address concerns from PR This commit was moved from ipfs/go-ipfs-pinner@be994ddf361b5498c12ff1844b25c75d3f240c4d --- pinning/pinner/pin.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index ed3598e4d..466dfba41 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -5,6 +5,7 @@ package pin import ( "encoding/json" "errors" + "fmt" "sync" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" @@ -103,7 +104,7 @@ func (p *pinner) Pin(node *mdag.Node, recurse bool) error { } } else { if p.recursePin.HasKey(k) { - return errors.New("Key already pinned recursively.") + return fmt.Errorf("%s already pinned recursively", k.B58String()) } p.directPin.AddBlock(k) } @@ -124,15 +125,15 @@ func (p *pinner) Unpin(k util.Key, recursive bool) error { return p.unpinLinks(node) } else { - return errors.New("Key pinned recursively.") + return fmt.Errorf("%s is pinned recursively", k) } } else if p.directPin.HasKey(k) { p.directPin.RemoveBlock(k) return nil } else if p.indirPin.HasKey(k) { - return errors.New("Cannot unpin indirectly pinned block.") + return fmt.Errorf("%s is pinned indirectly. indirect pins cannot be removed directly", k) } else { - return errors.New("Given key was not pinned.") + return fmt.Errorf("%s is not pinned", k) } } From b6d54aec717f24e575d0170c1e08166352f579b9 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 19 Jan 2015 21:27:06 -0800 Subject: [PATCH 0638/3817] fix(blockservice) fully async exchange.HasBlock This commit was moved from ipfs/go-blockservice@c68129e9b6be50215ea8224c98d71b7a2bbe345d --- blockservice/blocks_test.go | 6 +- blockservice/blockservice.go | 60 ++++---- blockservice/worker/bench/main.go | 82 +++++++++++ blockservice/worker/bench_worker_test.go | 42 ++++++ blockservice/worker/worker.go | 169 +++++++++++++++++++++++ blockservice/worker/worker_test.go | 14 ++ 6 files changed, 341 insertions(+), 32 deletions(-) create mode 100644 blockservice/worker/bench/main.go create mode 100644 blockservice/worker/bench_worker_test.go create mode 100644 blockservice/worker/worker.go create mode 100644 blockservice/worker/worker_test.go diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index e7966729f..8f3d89c7b 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -22,6 +22,7 @@ func TestBlocks(t *testing.T) { t.Error("failed to construct block service", err) return } + defer bs.Close() b := blocks.NewBlock([]byte("beep boop")) h := u.Hash([]byte("beep boop")) @@ -61,6 +62,9 @@ func TestBlocks(t *testing.T) { func TestGetBlocksSequential(t *testing.T) { var servs = Mocks(t, 4) + for _, s := range servs { + defer s.Close() + } bg := blocksutil.NewBlockGenerator() blks := bg.Blocks(50) @@ -73,7 +77,7 @@ func TestGetBlocksSequential(t *testing.T) { t.Log("one instance at a time, get blocks concurrently") for i := 1; i < len(servs); i++ { - ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) + ctx, _ := context.WithTimeout(context.TODO(), time.Second*50) out := servs[i].GetBlocks(ctx, keys) gotten := make(map[u.Key]*blocks.Block) for blk := range out { diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index a2a001418..2ce230452 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -8,20 +8,33 @@ import ( "fmt" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - process "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" - procrl "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit" blocks "github.com/jbenet/go-ipfs/blocks" "github.com/jbenet/go-ipfs/blocks/blockstore" + worker "github.com/jbenet/go-ipfs/blockservice/worker" exchange "github.com/jbenet/go-ipfs/exchange" u "github.com/jbenet/go-ipfs/util" ) +var wc = worker.Config{ + // When running on a single core, NumWorkers has a harsh negative effect on + // throughput. (-80% when < 25) + // Running a lot more workers appears to have very little effect on both + // single and multicore configurations. + NumWorkers: 25, + + // These have no effect on when running on multiple cores, but harsh + // negative effect on throughput when running on a single core + // On multicore configurations these buffers have little effect on + // throughput. + // On single core configurations, larger buffers have severe adverse + // effects on throughput. + ClientBufferSize: 0, + WorkerBufferSize: 0, +} + var log = u.Logger("blockservice") var ErrNotFound = errors.New("blockservice: key not found") -// MaxExchangeAddWorkers rate limits the number of exchange workers -var MaxExchangeAddWorkers = 100 - // BlockService is a hybrid block datastore. It stores data in a local // datastore and may retrieve data from a remote Exchange. // It uses an internal `datastore.Datastore` instance to store values. @@ -30,8 +43,7 @@ type BlockService struct { Blockstore blockstore.Blockstore Exchange exchange.Interface - rateLimiter *procrl.RateLimiter - exchangeAdd chan blocks.Block + worker *worker.Worker } // NewBlockService creates a BlockService with given datastore instance. @@ -43,15 +55,10 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) (*BlockService, error log.Warning("blockservice running in local (offline) mode.") } - // exchangeAdd is a channel for async workers to add to the exchange. - // 100 blocks buffer. not clear what this number should be - exchangeAdd := make(chan blocks.Block, 100) - return &BlockService{ - Blockstore: bs, - Exchange: rem, - exchangeAdd: exchangeAdd, - rateLimiter: procrl.NewRateLimiter(process.Background(), MaxExchangeAddWorkers), + Blockstore: bs, + Exchange: rem, + worker: worker.NewWorker(rem, wc), }, nil } @@ -63,22 +70,8 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { if err != nil { return k, err } - - // this operation rate-limits blockservice operations, so it is - // now an async process. - if s.Exchange != nil { - - // LimitedGo will spawn a goroutine but provide proper backpressure. - // it will not spawn the goroutine until the ratelimiter's work load - // is under the threshold. - s.rateLimiter.LimitedGo(func(worker process.Process) { - ctx := context.TODO() - if err := s.Exchange.HasBlock(ctx, b); err != nil { - // suppress error, as the client shouldn't care about bitswap. - // the client only cares about the blockstore.Put. - log.Errorf("Exchange.HasBlock error: %s", err) - } - }) + if err := s.worker.HasBlock(b); err != nil { + return "", errors.New("blockservice is closed") } return k, nil } @@ -148,3 +141,8 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks func (s *BlockService) DeleteBlock(k u.Key) error { return s.Blockstore.DeleteBlock(k) } + +func (s *BlockService) Close() error { + log.Debug("blockservice is shutting down...") + return s.worker.Close() +} diff --git a/blockservice/worker/bench/main.go b/blockservice/worker/bench/main.go new file mode 100644 index 000000000..5d85de27f --- /dev/null +++ b/blockservice/worker/bench/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "log" + "math" + "testing" + "time" + + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds_sync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + blocks "github.com/jbenet/go-ipfs/blocks" + blockstore "github.com/jbenet/go-ipfs/blocks/blockstore" + worker "github.com/jbenet/go-ipfs/blockservice/worker" + "github.com/jbenet/go-ipfs/exchange/offline" + "github.com/jbenet/go-ipfs/thirdparty/delay" + "github.com/jbenet/go-ipfs/util/datastore2" +) + +const kEstRoutingDelay = time.Second + +const kBlocksPerOp = 100 + +func main() { + var bestConfig worker.Config + var quickestNsPerOp int64 = math.MaxInt64 + for NumWorkers := 1; NumWorkers < 10; NumWorkers++ { + for ClientBufferSize := 0; ClientBufferSize < 10; ClientBufferSize++ { + for WorkerBufferSize := 0; WorkerBufferSize < 10; WorkerBufferSize++ { + c := worker.Config{ + NumWorkers: NumWorkers, + ClientBufferSize: ClientBufferSize, + WorkerBufferSize: WorkerBufferSize, + } + result := testing.Benchmark(BenchmarkWithConfig(c)) + if result.NsPerOp() < quickestNsPerOp { + bestConfig = c + quickestNsPerOp = result.NsPerOp() + } + log.Printf("benched %+v \t result: %+v", c, result) + } + } + } + log.Println(bestConfig) +} + +func BenchmarkWithConfig(c worker.Config) func(b *testing.B) { + return func(b *testing.B) { + + routingDelay := delay.Fixed(0) // during setup + + dstore := ds_sync.MutexWrap(datastore2.WithDelay(ds.NewMapDatastore(), routingDelay)) + bstore := blockstore.NewBlockstore(dstore) + var testdata []*blocks.Block + var i int64 + for i = 0; i < kBlocksPerOp; i++ { + testdata = append(testdata, blocks.NewBlock([]byte(string(i)))) + } + b.ResetTimer() + b.SetBytes(kBlocksPerOp) + for i := 0; i < b.N; i++ { + + b.StopTimer() + w := worker.NewWorker(offline.Exchange(bstore), c) + b.StartTimer() + + prev := routingDelay.Set(kEstRoutingDelay) // during measured section + + for _, block := range testdata { + if err := w.HasBlock(block); err != nil { + b.Fatal(err) + } + } + + routingDelay.Set(prev) // to hasten the unmeasured close period + + b.StopTimer() + w.Close() + b.StartTimer() + + } + } +} diff --git a/blockservice/worker/bench_worker_test.go b/blockservice/worker/bench_worker_test.go new file mode 100644 index 000000000..aab910b64 --- /dev/null +++ b/blockservice/worker/bench_worker_test.go @@ -0,0 +1,42 @@ +package worker + +import ( + "testing" + + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + blocks "github.com/jbenet/go-ipfs/blocks" + blockstore "github.com/jbenet/go-ipfs/blocks/blockstore" + "github.com/jbenet/go-ipfs/exchange/offline" +) + +func BenchmarkHandle10KBlocks(b *testing.B) { + bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) + var testdata []*blocks.Block + for i := 0; i < 10000; i++ { + testdata = append(testdata, blocks.NewBlock([]byte(string(i)))) + } + b.ResetTimer() + b.SetBytes(10000) + for i := 0; i < b.N; i++ { + + b.StopTimer() + w := NewWorker(offline.Exchange(bstore), Config{ + NumWorkers: 1, + ClientBufferSize: 0, + WorkerBufferSize: 0, + }) + b.StartTimer() + + for _, block := range testdata { + if err := w.HasBlock(block); err != nil { + b.Fatal(err) + } + } + + b.StopTimer() + w.Close() + b.StartTimer() + + } +} diff --git a/blockservice/worker/worker.go b/blockservice/worker/worker.go new file mode 100644 index 000000000..0da792719 --- /dev/null +++ b/blockservice/worker/worker.go @@ -0,0 +1,169 @@ +// TODO FIXME name me +package worker + +import ( + "container/list" + "errors" + "time" + + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + process "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" + blocks "github.com/jbenet/go-ipfs/blocks" + exchange "github.com/jbenet/go-ipfs/exchange" + util "github.com/jbenet/go-ipfs/util" +) + +var log = util.Logger("blockservice") + +var DefaultConfig = Config{ + NumWorkers: 1, + ClientBufferSize: 0, + WorkerBufferSize: 0, +} + +type Config struct { + // NumWorkers sets the number of background workers that provide blocks to + // the exchange. + NumWorkers int + + // ClientBufferSize allows clients of HasBlock to send up to + // |ClientBufferSize| blocks without blocking. + ClientBufferSize int + + // WorkerBufferSize can be used in conjunction with NumWorkers to reduce + // communication-coordination within the worker. + WorkerBufferSize int +} + +// TODO FIXME name me +type Worker struct { + // added accepts blocks from client + added chan *blocks.Block + exchange exchange.Interface + + // workQueue is owned by the client worker + // process manages life-cycle + process process.Process +} + +func NewWorker(e exchange.Interface, c Config) *Worker { + if c.NumWorkers < 1 { + c.NumWorkers = 1 // provide a sane default + } + w := &Worker{ + exchange: e, + added: make(chan *blocks.Block, c.ClientBufferSize), + process: process.WithParent(process.Background()), // internal management + } + w.start(c) + return w +} + +func (w *Worker) HasBlock(b *blocks.Block) error { + select { + case <-w.process.Closed(): + return errors.New("blockservice worker is closed") + case w.added <- b: + return nil + } +} + +func (w *Worker) Close() error { + log.Debug("blockservice provide worker is shutting down...") + return w.process.Close() +} + +func (w *Worker) start(c Config) { + + workerChan := make(chan *blocks.Block, c.WorkerBufferSize) + + // clientWorker handles incoming blocks from |w.added| and sends to + // |workerChan|. This will never block the client. + w.process.Go(func(proc process.Process) { + defer close(workerChan) + + var workQueue BlockList + for { + + // take advantage of the fact that sending on nil channel always + // blocks so that a message is only sent if a block exists + sendToWorker := workerChan + nextBlock := workQueue.Pop() + if nextBlock == nil { + sendToWorker = nil + } + + select { + + // if worker is ready and there's a block to process, send the + // block + case sendToWorker <- nextBlock: + case <-time.Tick(5 * time.Second): + if workQueue.Len() > 0 { + log.Debugf("%d blocks in blockservice provide queue...", workQueue.Len()) + } + case block := <-w.added: + if nextBlock != nil { + workQueue.Push(nextBlock) // missed the chance to send it + } + // if the client sends another block, add it to the queue. + workQueue.Push(block) + case <-proc.Closing(): + return + } + } + }) + + for i := 0; i < c.NumWorkers; i++ { + // reads from |workerChan| until process closes + w.process.Go(func(proc process.Process) { + ctx, cancel := context.WithCancel(context.Background()) + + // shuts down an in-progress HasBlock operation + proc.Go(func(proc process.Process) { + <-proc.Closing() + cancel() + }) + + for { + select { + case <-proc.Closing(): + return + case block, ok := <-workerChan: + if !ok { + return + } + if err := w.exchange.HasBlock(ctx, block); err != nil { + // TODO log event? + } + } + } + }) + } +} + +type BlockList struct { + list list.List +} + +func (s *BlockList) PushFront(b *blocks.Block) { + // FIXME find figures + s.list.PushFront(b) +} + +func (s *BlockList) Push(b *blocks.Block) { + s.list.PushBack(b) +} + +func (s *BlockList) Pop() *blocks.Block { + if s.list.Len() == 0 { + return nil + } + e := s.list.Front() + s.list.Remove(e) + return e.Value.(*blocks.Block) +} + +func (s *BlockList) Len() int { + return s.list.Len() +} diff --git a/blockservice/worker/worker_test.go b/blockservice/worker/worker_test.go new file mode 100644 index 000000000..9c1158df7 --- /dev/null +++ b/blockservice/worker/worker_test.go @@ -0,0 +1,14 @@ +package worker + +import "testing" + +func TestStartClose(t *testing.T) { + numRuns := 50 + if testing.Short() { + numRuns = 5 + } + for i := 0; i < numRuns; i++ { + w := NewWorker(nil, DefaultConfig) + w.Close() + } +} From 106a2eb0be3a8f73dd8f5baa18a1cea93f65ee01 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 20 Jan 2015 16:51:45 -0800 Subject: [PATCH 0639/3817] one worker This commit was moved from ipfs/go-blockservice@d630e9834e02fd92b957b647b24ef19de539c08f --- blockservice/worker/worker.go | 44 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/blockservice/worker/worker.go b/blockservice/worker/worker.go index 0da792719..a3d55a155 100644 --- a/blockservice/worker/worker.go +++ b/blockservice/worker/worker.go @@ -114,32 +114,30 @@ func (w *Worker) start(c Config) { } }) - for i := 0; i < c.NumWorkers; i++ { - // reads from |workerChan| until process closes - w.process.Go(func(proc process.Process) { - ctx, cancel := context.WithCancel(context.Background()) - - // shuts down an in-progress HasBlock operation - proc.Go(func(proc process.Process) { - <-proc.Closing() - cancel() - }) - - for { - select { - case <-proc.Closing(): + // reads from |workerChan| until process closes + w.process.Go(func(proc process.Process) { + ctx, cancel := context.WithCancel(context.Background()) + + // shuts down an in-progress HasBlock operation + proc.Go(func(proc process.Process) { + <-proc.Closing() + cancel() + }) + + for { + select { + case <-proc.Closing(): + return + case block, ok := <-workerChan: + if !ok { return - case block, ok := <-workerChan: - if !ok { - return - } - if err := w.exchange.HasBlock(ctx, block); err != nil { - // TODO log event? - } + } + if err := w.exchange.HasBlock(ctx, block); err != nil { + // TODO log event? } } - }) - } + } + }) } type BlockList struct { From 923626a19c45bfb21e721afe0758b2754e3011b6 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 20 Jan 2015 16:54:58 -0800 Subject: [PATCH 0640/3817] use rate-limiter @jbenet This commit was moved from ipfs/go-blockservice@4332ac4c14d385f70ee61686e0f4449a7f8004d8 --- blockservice/worker/worker.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/blockservice/worker/worker.go b/blockservice/worker/worker.go index a3d55a155..a52c3d708 100644 --- a/blockservice/worker/worker.go +++ b/blockservice/worker/worker.go @@ -8,6 +8,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" process "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" + ratelimit "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit" blocks "github.com/jbenet/go-ipfs/blocks" exchange "github.com/jbenet/go-ipfs/exchange" util "github.com/jbenet/go-ipfs/util" @@ -124,6 +125,7 @@ func (w *Worker) start(c Config) { cancel() }) + limiter := ratelimit.NewRateLimiter(proc, c.NumWorkers) for { select { case <-proc.Closing(): @@ -132,9 +134,11 @@ func (w *Worker) start(c Config) { if !ok { return } - if err := w.exchange.HasBlock(ctx, block); err != nil { - // TODO log event? - } + limiter.LimitedGo(func(proc process.Process) { + if err := w.exchange.HasBlock(ctx, block); err != nil { + // TODO log event? + } + }) } } }) From 2054465c7089768171f05ccf3e8c987a62cf7745 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 20 Jan 2015 23:22:43 -0800 Subject: [PATCH 0641/3817] extract context func @jbenet would like it to work this way This commit was moved from ipfs/go-blockservice@686220bf7cf4212d0c4188c0eef55916d8ff0154 --- blockservice/worker/worker.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/blockservice/worker/worker.go b/blockservice/worker/worker.go index a52c3d708..dc570f3ee 100644 --- a/blockservice/worker/worker.go +++ b/blockservice/worker/worker.go @@ -117,14 +117,7 @@ func (w *Worker) start(c Config) { // reads from |workerChan| until process closes w.process.Go(func(proc process.Process) { - ctx, cancel := context.WithCancel(context.Background()) - - // shuts down an in-progress HasBlock operation - proc.Go(func(proc process.Process) { - <-proc.Closing() - cancel() - }) - + ctx := childContext(proc) // shut down in-progress HasBlock when time to die limiter := ratelimit.NewRateLimiter(proc, c.NumWorkers) for { select { @@ -169,3 +162,18 @@ func (s *BlockList) Pop() *blocks.Block { func (s *BlockList) Len() int { return s.list.Len() } + +// TODO extract +type waitable interface { + Closing() <-chan struct{} +} + +// TODO extract +func childContext(w waitable) context.Context { + ctx, cancel := context.WithCancel(context.Background()) + go func() { + <-w.Closing() + cancel() + }() + return ctx +} From d0ca4221411ad33347dfaaf5e47bde4705472d82 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 20 Jan 2015 23:31:48 -0800 Subject: [PATCH 0642/3817] fix(blockservice/worker) replace time.Tick with a timer we can stop This commit was moved from ipfs/go-blockservice@90d665fb74f7b5977b9303f995b1707b6c996675 --- blockservice/worker/worker.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/blockservice/worker/worker.go b/blockservice/worker/worker.go index dc570f3ee..077f366e7 100644 --- a/blockservice/worker/worker.go +++ b/blockservice/worker/worker.go @@ -84,6 +84,8 @@ func (w *Worker) start(c Config) { defer close(workerChan) var workQueue BlockList + debugInfo := time.NewTicker(5 * time.Second) + defer debugInfo.Stop() for { // take advantage of the fact that sending on nil channel always @@ -99,7 +101,7 @@ func (w *Worker) start(c Config) { // if worker is ready and there's a block to process, send the // block case sendToWorker <- nextBlock: - case <-time.Tick(5 * time.Second): + case <-debugInfo.C: if workQueue.Len() > 0 { log.Debugf("%d blocks in blockservice provide queue...", workQueue.Len()) } From acd0fe3095001d49ea52581e5e032b0590787d6f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 20 Jan 2015 00:04:52 -0800 Subject: [PATCH 0643/3817] demote dht logs This commit was moved from ipfs/go-ipfs-routing@2be84c9b0a79e25a271c84621c29eb82d2feffce --- routing/dht/dht_net.go | 2 -- routing/dht/routing.go | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 2b857ce2b..c8b6b66eb 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -38,8 +38,6 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { // update the peer (on valid msgs only) dht.updateFromMessage(ctx, mPeer, pmes) - log.Event(ctx, "foo", dht.self, mPeer, pmes) - // get handler for this msg type. handler := dht.handlerForMsgType(pmes.GetType()) if handler == nil { diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 35d804650..07d9ddc43 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -215,7 +215,7 @@ func (dht *IpfsDHT) getClosestPeers(ctx context.Context, key u.Key) (<-chan peer // run it! _, err := query.Run(ctx, tablepeers) if err != nil { - log.Errorf("closestPeers query run error: %s", err) + log.Debugf("closestPeers query run error: %s", err) } }() From 6d1b8180c67c93488b8f2c9bebfdabec921dbc3e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 20 Jan 2015 23:44:07 -0800 Subject: [PATCH 0644/3817] log err This commit was moved from ipfs/go-blockservice@81b761d56ced09e576573277aadc509b2f2dae1c --- blockservice/worker/worker.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/blockservice/worker/worker.go b/blockservice/worker/worker.go index 077f366e7..429f67982 100644 --- a/blockservice/worker/worker.go +++ b/blockservice/worker/worker.go @@ -131,7 +131,7 @@ func (w *Worker) start(c Config) { } limiter.LimitedGo(func(proc process.Process) { if err := w.exchange.HasBlock(ctx, block); err != nil { - // TODO log event? + log.Infof("blockservice worker error: %s", err) } }) } @@ -144,7 +144,6 @@ type BlockList struct { } func (s *BlockList) PushFront(b *blocks.Block) { - // FIXME find figures s.list.PushFront(b) } From 163f13b84544b16b22121fee0ac2441a89e7f06b Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 21 Jan 2015 14:58:33 -0800 Subject: [PATCH 0645/3817] deduplicate blocks in queue This commit was moved from ipfs/go-blockservice@7da1938df4910e312ec724f8432b60c57f602e7c --- blockservice/worker/worker.go | 25 ++++++++++++--- blockservice/worker/worker_test.go | 51 +++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/blockservice/worker/worker.go b/blockservice/worker/worker.go index 429f67982..be46c45c8 100644 --- a/blockservice/worker/worker.go +++ b/blockservice/worker/worker.go @@ -140,15 +140,30 @@ func (w *Worker) start(c Config) { } type BlockList struct { - list list.List + list list.List + uniques map[util.Key]*list.Element } func (s *BlockList) PushFront(b *blocks.Block) { - s.list.PushFront(b) + if s.uniques == nil { + s.uniques = make(map[util.Key]*list.Element) + } + _, ok := s.uniques[b.Key()] + if !ok { + e := s.list.PushFront(b) + s.uniques[b.Key()] = e + } } func (s *BlockList) Push(b *blocks.Block) { - s.list.PushBack(b) + if s.uniques == nil { + s.uniques = make(map[util.Key]*list.Element) + } + _, ok := s.uniques[b.Key()] + if !ok { + e := s.list.PushBack(b) + s.uniques[b.Key()] = e + } } func (s *BlockList) Pop() *blocks.Block { @@ -157,7 +172,9 @@ func (s *BlockList) Pop() *blocks.Block { } e := s.list.Front() s.list.Remove(e) - return e.Value.(*blocks.Block) + b := e.Value.(*blocks.Block) + delete(s.uniques, b.Key()) + return b } func (s *BlockList) Len() int { diff --git a/blockservice/worker/worker_test.go b/blockservice/worker/worker_test.go index 9c1158df7..4cbc9b2cc 100644 --- a/blockservice/worker/worker_test.go +++ b/blockservice/worker/worker_test.go @@ -1,6 +1,9 @@ package worker -import "testing" +import ( + blocks "github.com/jbenet/go-ipfs/blocks" + "testing" +) func TestStartClose(t *testing.T) { numRuns := 50 @@ -12,3 +15,49 @@ func TestStartClose(t *testing.T) { w.Close() } } + +func TestQueueDeduplication(t *testing.T) { + numUniqBlocks := 5 // arbitrary + + var firstBatch []*blocks.Block + for i := 0; i < numUniqBlocks; i++ { + firstBatch = append(firstBatch, blockFromInt(i)) + } + + // to get different pointer values and prevent the implementation from + // cheating. The impl must check equality using Key. + var secondBatch []*blocks.Block + for i := 0; i < numUniqBlocks; i++ { + secondBatch = append(secondBatch, blockFromInt(i)) + } + var workQueue BlockList + + for _, b := range append(firstBatch, secondBatch...) { + workQueue.Push(b) + } + for i := 0; i < numUniqBlocks; i++ { + b := workQueue.Pop() + if b.Key() != firstBatch[i].Key() { + t.Fatal("list is not FIFO") + } + } + if b := workQueue.Pop(); b != nil { + t.Fatal("the workQueue did not de-duplicate the blocks") + } +} + +func TestPushPopPushPop(t *testing.T) { + var workQueue BlockList + orig := blockFromInt(1) + dup := blockFromInt(1) + workQueue.PushFront(orig) + workQueue.Pop() + workQueue.Push(dup) + if workQueue.Len() != 1 { + t.Fatal("the block list's internal state is corrupt") + } +} + +func blockFromInt(i int) *blocks.Block { + return blocks.NewBlock([]byte(string(i))) +} From 8f154feb7dde0de3b0acdec0fbadb6796c37920e Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 16 Jan 2015 12:52:12 -0800 Subject: [PATCH 0646/3817] routing/dht: periodic bootstrapping #572 This commit was moved from ipfs/go-ipfs-routing@656354eeba62fb2d03925c598de6dd6d14b734d4 --- routing/dht/dht.go | 63 ------------ routing/dht/dht_bootstrap.go | 181 ++++++++++++++++++++++++++++++++++ routing/dht/dht_test.go | 184 +++++++++++++++++++++++++++-------- 3 files changed, 327 insertions(+), 101 deletions(-) create mode 100644 routing/dht/dht_bootstrap.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 923c8c69b..0fd5177a2 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -370,66 +370,3 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { } } } - -// Bootstrap builds up list of peers by requesting random peer IDs -func (dht *IpfsDHT) Bootstrap(ctx context.Context, queries int) error { - var merr u.MultiErr - - randomID := func() peer.ID { - // 16 random bytes is not a valid peer id. it may be fine becuase - // the dht will rehash to its own keyspace anyway. - id := make([]byte, 16) - rand.Read(id) - return peer.ID(id) - } - - // bootstrap sequentially, as results will compound - runQuery := func(ctx context.Context, id peer.ID) { - p, err := dht.FindPeer(ctx, id) - if err == routing.ErrNotFound { - // this isn't an error. this is precisely what we expect. - } else if err != nil { - merr = append(merr, err) - } else { - // woah, actually found a peer with that ID? this shouldn't happen normally - // (as the ID we use is not a real ID). this is an odd error worth logging. - err := fmt.Errorf("Bootstrap peer error: Actually FOUND peer. (%s, %s)", id, p) - log.Errorf("%s", err) - merr = append(merr, err) - } - } - - sequential := true - if sequential { - // these should be parallel normally. but can make them sequential for debugging. - // note that the core/bootstrap context deadline should be extended too for that. - for i := 0; i < queries; i++ { - id := randomID() - log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, queries, id) - runQuery(ctx, id) - } - - } else { - // note on parallelism here: the context is passed in to the queries, so they - // **should** exit when it exceeds, making this function exit on ctx cancel. - // normally, we should be selecting on ctx.Done() here too, but this gets - // complicated to do with WaitGroup, and doesnt wait for the children to exit. - var wg sync.WaitGroup - for i := 0; i < queries; i++ { - wg.Add(1) - go func() { - defer wg.Done() - - id := randomID() - log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, queries, id) - runQuery(ctx, id) - }() - } - wg.Wait() - } - - if len(merr) > 0 { - return merr - } - return nil -} diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go new file mode 100644 index 000000000..271fa7474 --- /dev/null +++ b/routing/dht/dht_bootstrap.go @@ -0,0 +1,181 @@ +// Package dht implements a distributed hash table that satisfies the ipfs routing +// interface. This DHT is modeled after kademlia with Coral and S/Kademlia modifications. +package dht + +import ( + "crypto/rand" + "fmt" + "sync" + "time" + + peer "github.com/jbenet/go-ipfs/p2p/peer" + routing "github.com/jbenet/go-ipfs/routing" + u "github.com/jbenet/go-ipfs/util" + + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + goprocess "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" +) + +// DefaultBootstrapQueries specifies how many queries to run, +// if the user does not specify a different number as an option. +// +// For now, this is set to 16 queries, which is an aggressive number. +// We are currently more interested in ensuring we have a properly formed +// DHT than making sure our dht minimizes traffic. Once we are more certain +// of our implementation's robustness, we should lower this down to 8 or 4. +// +// Note there is also a tradeoff between the bootstrap period and the number +// of queries. We could support a higher period with a smaller number of +// queries +const DefaultBootstrapQueries = 16 + +// DefaultBootstrapPeriod specifies how often to periodically run bootstrap, +// if the user does not specify a different number as an option. +// +// For now, this is set to 10 seconds, which is an aggressive period. We are +// We are currently more interested in ensuring we have a properly formed +// DHT than making sure our dht minimizes traffic. Once we are more certain +// implementation's robustness, we should lower this down to 30s or 1m. +// +// Note there is also a tradeoff between the bootstrap period and the number +// of queries. We could support a higher period with a smaller number of +// queries +const DefaultBootstrapPeriod = time.Duration(10 * time.Second) + +// Bootstrap runs bootstrapping once, then calls SignalBootstrap with default +// parameters: DefaultBootstrapQueries and DefaultBootstrapPeriod. This allows +// the user to catch an error off the bat if the connections are faulty. It also +// allows BootstrapOnSignal not to run bootstrap at the beginning, which is useful +// for instrumenting it on tests, or delaying bootstrap until the network is online +// and connected to at least a few nodes. +// +// Like PeriodicBootstrap, Bootstrap returns a process, so the user can stop it. +func (dht *IpfsDHT) Bootstrap() (goprocess.Process, error) { + + if err := dht.runBootstrap(dht.Context(), DefaultBootstrapQueries); err != nil { + return nil, err + } + + sig := time.Tick(DefaultBootstrapPeriod) + return dht.BootstrapOnSignal(DefaultBootstrapQueries, sig) +} + +// SignalBootstrap ensures the dht routing table remains healthy as peers come and go. +// it builds up a list of peers by requesting random peer IDs. The Bootstrap +// process will run a number of queries each time, and run every time signal fires. +// These parameters are configurable. +// +// SignalBootstrap returns a process, so the user can stop it. +func (dht *IpfsDHT) BootstrapOnSignal(queries int, signal <-chan time.Time) (goprocess.Process, error) { + if queries <= 0 { + return nil, fmt.Errorf("invalid number of queries: %d", queries) + } + + if signal == nil { + return nil, fmt.Errorf("invalid signal: %v", signal) + } + + proc := goprocess.Go(func(worker goprocess.Process) { + for { + select { + case <-worker.Closing(): + log.Debug("dht bootstrapper shutting down") + return + + case <-signal: + // it would be useful to be able to send out signals of when we bootstrap, too... + // maybe this is a good case for whole module event pub/sub? + + ctx := dht.Context() + if err := dht.runBootstrap(ctx, queries); err != nil { + log.Error(err) + // A bootstrapping error is important to notice but not fatal. + // maybe the client should be able to consume these errors, + // though I dont have a clear use case in mind-- what **could** + // the client do if one of the bootstrap calls fails? + // + // This is also related to the core's bootstrap failures. + // superviseConnections should perhaps allow clients to detect + // bootstrapping problems. + // + // Anyway, passing errors could be done with a bootstrapper object. + // this would imply the client should be able to consume a lot of + // other non-fatal dht errors too. providing this functionality + // should be done correctly DHT-wide. + // NB: whatever the design, clients must ensure they drain errors! + // This pattern is common to many things, perhaps long-running services + // should have something like an ErrStream that allows clients to consume + // periodic errors and take action. It should allow the user to also + // ignore all errors with something like an ErrStreamDiscard. We should + // study what other systems do for ideas. + } + } + } + }) + + return proc, nil +} + +// runBootstrap builds up list of peers by requesting random peer IDs +func (dht *IpfsDHT) runBootstrap(ctx context.Context, queries int) error { + + var merr u.MultiErr + + randomID := func() peer.ID { + // 16 random bytes is not a valid peer id. it may be fine becuase + // the dht will rehash to its own keyspace anyway. + id := make([]byte, 16) + rand.Read(id) + return peer.ID(id) + } + + // bootstrap sequentially, as results will compound + runQuery := func(ctx context.Context, id peer.ID) { + p, err := dht.FindPeer(ctx, id) + if err == routing.ErrNotFound { + // this isn't an error. this is precisely what we expect. + } else if err != nil { + merr = append(merr, err) + } else { + // woah, actually found a peer with that ID? this shouldn't happen normally + // (as the ID we use is not a real ID). this is an odd error worth logging. + err := fmt.Errorf("Bootstrap peer error: Actually FOUND peer. (%s, %s)", id, p) + log.Errorf("%s", err) + merr = append(merr, err) + } + } + + sequential := true + if sequential { + // these should be parallel normally. but can make them sequential for debugging. + // note that the core/bootstrap context deadline should be extended too for that. + for i := 0; i < queries; i++ { + id := randomID() + log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, queries, id) + runQuery(ctx, id) + } + + } else { + // note on parallelism here: the context is passed in to the queries, so they + // **should** exit when it exceeds, making this function exit on ctx cancel. + // normally, we should be selecting on ctx.Done() here too, but this gets + // complicated to do with WaitGroup, and doesnt wait for the children to exit. + var wg sync.WaitGroup + for i := 0; i < queries; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + id := randomID() + log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, queries, id) + runQuery(ctx, id) + }() + } + wg.Wait() + } + + if len(merr) > 0 { + return merr + } + return nil +} diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 07211f5fe..afc5756e8 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -75,25 +75,20 @@ func connect(t *testing.T, ctx context.Context, a, b *IpfsDHT) { func bootstrap(t *testing.T, ctx context.Context, dhts []*IpfsDHT) { ctx, cancel := context.WithCancel(ctx) + log.Error("hmm") + defer log.Error("hmm end") + log.Debugf("bootstrapping dhts...") - rounds := 1 + // tried async. sequential fares much better. compare: + // 100 async https://gist.github.com/jbenet/56d12f0578d5f34810b2 + // 100 sync https://gist.github.com/jbenet/6c59e7c15426e48aaedd + // probably because results compound - for i := 0; i < rounds; i++ { - log.Debugf("bootstrapping round %d/%d\n", i, rounds) - - // tried async. sequential fares much better. compare: - // 100 async https://gist.github.com/jbenet/56d12f0578d5f34810b2 - // 100 sync https://gist.github.com/jbenet/6c59e7c15426e48aaedd - // probably because results compound - - start := rand.Intn(len(dhts)) // randomize to decrease bias. - for i := range dhts { - dht := dhts[(start+i)%len(dhts)] - log.Debugf("bootstrapping round %d/%d -- %s\n", i, rounds, dht.self) - dht.Bootstrap(ctx, 3) - } + start := rand.Intn(len(dhts)) // randomize to decrease bias. + for i := range dhts { + dht := dhts[(start+i)%len(dhts)] + dht.runBootstrap(ctx, 3) } - cancel() } @@ -235,6 +230,53 @@ func TestProvides(t *testing.T) { } } +// if minPeers or avgPeers is 0, dont test for it. +func waitForWellFormedTables(t *testing.T, dhts []*IpfsDHT, minPeers, avgPeers int, timeout time.Duration) bool { + // test "well-formed-ness" (>= minPeers peers in every routing table) + + checkTables := func() bool { + totalPeers := 0 + for _, dht := range dhts { + rtlen := dht.routingTable.Size() + totalPeers += rtlen + if minPeers > 0 && rtlen < minPeers { + t.Logf("routing table for %s only has %d peers (should have >%d)", dht.self, rtlen, minPeers) + return false + } + } + actualAvgPeers := totalPeers / len(dhts) + t.Logf("avg rt size: %d", actualAvgPeers) + if avgPeers > 0 && actualAvgPeers < avgPeers { + t.Logf("avg rt size: %d < %d", actualAvgPeers, avgPeers) + return false + } + return true + } + + timeoutA := time.After(timeout) + for { + select { + case <-timeoutA: + log.Error("did not reach well-formed routing tables by %s", timeout) + return false // failed + case <-time.After(5 * time.Millisecond): + if checkTables() { + return true // succeeded + } + } + } +} + +func printRoutingTables(dhts []*IpfsDHT) { + // the routing tables should be full now. let's inspect them. + fmt.Println("checking routing table of %d", len(dhts)) + for _, dht := range dhts { + fmt.Printf("checking routing table of %s\n", dht.self) + dht.routingTable.Print() + fmt.Println("") + } +} + func TestBootstrap(t *testing.T) { // t.Skip("skipping test to debug another") if testing.Short() { @@ -258,38 +300,105 @@ func TestBootstrap(t *testing.T) { } <-time.After(100 * time.Millisecond) - t.Logf("bootstrapping them so they find each other", nDHTs) - ctxT, _ := context.WithTimeout(ctx, 5*time.Second) - bootstrap(t, ctxT, dhts) + // bootstrap a few times until we get good tables. + stop := make(chan struct{}) + go func() { + for { + t.Logf("bootstrapping them so they find each other", nDHTs) + ctxT, _ := context.WithTimeout(ctx, 5*time.Second) + bootstrap(t, ctxT, dhts) + + select { + case <-time.After(50 * time.Millisecond): + continue // being explicit + case <-stop: + return + } + } + }() + + waitForWellFormedTables(t, dhts, 7, 10, 5*time.Second) + close(stop) if u.Debug { // the routing tables should be full now. let's inspect them. - <-time.After(5 * time.Second) - t.Logf("checking routing table of %d", nDHTs) - for _, dht := range dhts { - fmt.Printf("checking routing table of %s\n", dht.self) - dht.routingTable.Print() - fmt.Println("") + printRoutingTables(dhts) + } +} + +func TestPeriodicBootstrap(t *testing.T) { + // t.Skip("skipping test to debug another") + if testing.Short() { + t.SkipNow() + } + + ctx := context.Background() + + nDHTs := 30 + _, _, dhts := setupDHTS(ctx, nDHTs, t) + defer func() { + for i := 0; i < nDHTs; i++ { + dhts[i].Close() + defer dhts[i].host.Close() + } + }() + + // signal amplifier + amplify := func(signal chan time.Time, other []chan time.Time) { + for t := range signal { + for _, s := range other { + s <- t + } + } + for _, s := range other { + close(s) } } - // test "well-formed-ness" (>= 3 peers in every routing table) - avgsize := 0 + signal := make(chan time.Time) + allSignals := []chan time.Time{} + + // kick off periodic bootstrappers with instrumented signals. + for _, dht := range dhts { + s := make(chan time.Time) + allSignals = append(allSignals, s) + dht.BootstrapOnSignal(5, s) + } + go amplify(signal, allSignals) + + t.Logf("dhts are not connected.", nDHTs) + for _, dht := range dhts { + rtlen := dht.routingTable.Size() + if rtlen > 0 { + t.Errorf("routing table for %s should have 0 peers. has %d", dht.self, rtlen) + } + } + + for i := 0; i < nDHTs; i++ { + connect(t, ctx, dhts[i], dhts[(i+1)%len(dhts)]) + } + + t.Logf("dhts are now connected to 1-2 others.", nDHTs) for _, dht := range dhts { rtlen := dht.routingTable.Size() - avgsize += rtlen - t.Logf("routing table for %s has %d peers", dht.self, rtlen) - if rtlen < 4 { - // currently, we dont have good bootstrapping guarantees. - // t.Errorf("routing table for %s only has %d peers", dht.self, rtlen) + if rtlen > 2 { + t.Errorf("routing table for %s should have at most 2 peers. has %d", dht.self, rtlen) } } - avgsize = avgsize / len(dhts) - avgsizeExpected := 6 - t.Logf("avg rt size: %d", avgsize) - if avgsize < avgsizeExpected { - t.Errorf("avg rt size: %d < %d", avgsize, avgsizeExpected) + if u.Debug { + printRoutingTables(dhts) + } + + t.Logf("bootstrapping them so they find each other", nDHTs) + signal <- time.Now() + + // this is async, and we dont know when it's finished with one cycle, so keep checking + // until the routing tables look better, or some long timeout for the failure case. + waitForWellFormedTables(t, dhts, 7, 10, 5*time.Second) + + if u.Debug { + printRoutingTables(dhts) } } @@ -319,7 +428,6 @@ func TestProvidesMany(t *testing.T) { if u.Debug { // the routing tables should be full now. let's inspect them. - <-time.After(5 * time.Second) t.Logf("checking routing table of %d", nDHTs) for _, dht := range dhts { fmt.Printf("checking routing table of %s\n", dht.self) From b8f594646e5abb25949a14e7dde9d99c8a53eb62 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 17 Jan 2015 20:02:58 -0800 Subject: [PATCH 0647/3817] try less aggressive bootstrap This commit was moved from ipfs/go-ipfs-routing@3088cdac9095cb908a4c1dd4847120083bff88ad --- routing/dht/dht_bootstrap.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 271fa7474..6efd53d3a 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -27,7 +27,7 @@ import ( // Note there is also a tradeoff between the bootstrap period and the number // of queries. We could support a higher period with a smaller number of // queries -const DefaultBootstrapQueries = 16 +const DefaultBootstrapQueries = 1 // DefaultBootstrapPeriod specifies how often to periodically run bootstrap, // if the user does not specify a different number as an option. From bf898d76e9161e7b298837b0c4ff5372c7c5221b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 18 Jan 2015 00:58:34 -0800 Subject: [PATCH 0648/3817] dht/bootstrap: logging This commit was moved from ipfs/go-ipfs-routing@f005c35b39851e3a51df351accbf672f3d7a33b2 --- routing/dht/dht_bootstrap.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 6efd53d3a..7ee82fbef 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -42,6 +42,10 @@ const DefaultBootstrapQueries = 1 // queries const DefaultBootstrapPeriod = time.Duration(10 * time.Second) +// DefaultBootstrapTimeout specifies how long to wait for a bootstrap query +// to run. +const DefaultBootstrapTimeout = time.Duration(10 * time.Second) + // Bootstrap runs bootstrapping once, then calls SignalBootstrap with default // parameters: DefaultBootstrapQueries and DefaultBootstrapPeriod. This allows // the user to catch an error off the bat if the connections are faulty. It also @@ -76,10 +80,10 @@ func (dht *IpfsDHT) BootstrapOnSignal(queries int, signal <-chan time.Time) (gop } proc := goprocess.Go(func(worker goprocess.Process) { + defer log.Debug("dht bootstrapper shutting down") for { select { case <-worker.Closing(): - log.Debug("dht bootstrapper shutting down") return case <-signal: @@ -118,6 +122,12 @@ func (dht *IpfsDHT) BootstrapOnSignal(queries int, signal <-chan time.Time) (gop // runBootstrap builds up list of peers by requesting random peer IDs func (dht *IpfsDHT) runBootstrap(ctx context.Context, queries int) error { + bslog := func(msg string) { + log.Debugf("DHT %s dhtRunBootstrap %s -- routing table size: %d", dht.self, msg, dht.routingTable.Size()) + } + bslog("start") + defer bslog("end") + defer log.EventBegin(ctx, "dhtRunBootstrap").Done() var merr u.MultiErr From 46c8ca3c51a1391d816dfa4b069dcf2ced0966c8 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 18 Jan 2015 00:58:56 -0800 Subject: [PATCH 0649/3817] dht/bootstrap: timeout queries This commit was moved from ipfs/go-ipfs-routing@ac8c43512a2f1a481ce3f8531214ed2f50afb31f --- routing/dht/dht_bootstrap.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 7ee82fbef..095c194d6 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -140,6 +140,8 @@ func (dht *IpfsDHT) runBootstrap(ctx context.Context, queries int) error { } // bootstrap sequentially, as results will compound + ctx, cancel := context.WithTimeout(ctx, DefaultBootstrapTimeout) + defer cancel() runQuery := func(ctx context.Context, id peer.ID) { p, err := dht.FindPeer(ctx, id) if err == routing.ErrNotFound { From cf504c64f20a9e8a6e370067327b3d69d1b3bca3 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 18 Jan 2015 00:59:22 -0800 Subject: [PATCH 0650/3817] dht/query: err return NotFound case When some queries finished, but we got no result, it should be a simple NotFoundError. Only when every single query ended in error do we externalize those to the client, in case something major is going wrong This commit was moved from ipfs/go-ipfs-routing@f69b922e4b7a2ab6113dbee14be404521179fd44 --- routing/dht/ext_test.go | 10 ++++++++++ routing/dht/query.go | 10 +++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index e151af5d2..ab756b5e4 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -49,6 +49,10 @@ func TestGetFailures(t *testing.T) { // u.POut("Timout Test\n") ctx1, _ := context.WithTimeout(context.Background(), 200*time.Millisecond) if _, err := d.GetValue(ctx1, u.Key("test")); err != nil { + if merr, ok := err.(u.MultiErr); ok && len(merr) > 0 { + err = merr[0] + } + if err != context.DeadlineExceeded { t.Fatal("Got different error than we expected", err) } @@ -86,6 +90,9 @@ func TestGetFailures(t *testing.T) { ctx2, _ := context.WithTimeout(context.Background(), 20*time.Second) _, err = d.GetValue(ctx2, u.Key("test")) if err != nil { + if merr, ok := err.(u.MultiErr); ok && len(merr) > 0 { + err = merr[0] + } if err != routing.ErrNotFound { t.Fatalf("Expected ErrNotFound, got: %s", err) } @@ -202,6 +209,9 @@ func TestNotFound(t *testing.T) { v, err := d.GetValue(ctx, u.Key("hello")) log.Debugf("get value got %v", v) if err != nil { + if merr, ok := err.(u.MultiErr); ok && len(merr) > 0 { + err = merr[0] + } switch err { case routing.ErrNotFound: //Success! diff --git a/routing/dht/query.go b/routing/dht/query.go index 53c232323..687d2621f 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -62,7 +62,7 @@ type dhtQueryRunner struct { peersRemaining todoctr.Counter // peersToQuery + currently processing result *dhtQueryResult // query result - errs []error // result errors. maybe should be a map[peer.ID]error + errs u.MultiErr // result errors. maybe should be a map[peer.ID]error rateLimit chan struct{} // processing semaphore log eventlog.EventLogger @@ -122,8 +122,12 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { r.RLock() defer r.RUnlock() - if len(r.errs) > 0 { - err = r.errs[0] // take the first? + err = routing.ErrNotFound + + // if every query to every peer failed, something must be very wrong. + if len(r.errs) > 0 && len(r.errs) == r.peersSeen.Size() { + log.Debugf("query errs: %s", r.errs) + err = r.errs[0] } case <-r.cg.Closed(): From 3c9da35a43a59fbd600c1769f80f9110e7df2929 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 18 Jan 2015 01:00:25 -0800 Subject: [PATCH 0651/3817] dht: kick off all the queries wit every node in our rt s/kademlia calls for makign sure to query all peers we have in our routing table, not just those closest. this helps ensure most queries resolve properly. This commit was moved from ipfs/go-ipfs-routing@05322a80aecf0db6085695e2ce86d7100a928b23 --- routing/dht/routing.go | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 07d9ddc43..2054e03fd 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -88,9 +88,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { // get closest peers in the routing table rtp := dht.routingTable.ListPeers() log.Debugf("peers in rt: %s", len(rtp), rtp) - - closest := dht.routingTable.NearestPeers(kb.ConvertKey(key), PoolSize) - if closest == nil || len(closest) == 0 { + if len(rtp) == 0 { log.Warning("No peers from routing table!") return nil, errors.Wrap(kb.ErrLookupFailure) } @@ -111,7 +109,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { }) // run it! - result, err := query.Run(ctx, closest) + result, err := query.Run(ctx, rtp) if err != nil { return nil, err } @@ -170,7 +168,7 @@ func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]peer.PeerIn // to the given key func (dht *IpfsDHT) getClosestPeers(ctx context.Context, key u.Key) (<-chan peer.ID, error) { e := log.EventBegin(ctx, "getClosestPeers", &key) - tablepeers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) + tablepeers := dht.routingTable.ListPeers() if len(tablepeers) == 0 { return nil, errors.Wrap(kb.ErrLookupFailure) } @@ -313,7 +311,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co return &dhtQueryResult{closerPeers: clpeers}, nil }) - peers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) + peers := dht.routingTable.ListPeers() _, err := query.Run(ctx, peers) if err != nil { log.Errorf("Query error: %s", err) @@ -329,13 +327,13 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, er return pi, nil } - closest := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue) - if closest == nil || len(closest) == 0 { + peers := dht.routingTable.ListPeers() + if len(peers) == 0 { return peer.PeerInfo{}, errors.Wrap(kb.ErrLookupFailure) } // Sanity... - for _, p := range closest { + for _, p := range peers { if p == id { log.Error("Found target peer in list of closest peers...") return dht.peerstore.PeerInfo(p), nil @@ -367,7 +365,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, er }) // run it! - result, err := query.Run(ctx, closest) + result, err := query.Run(ctx, peers) if err != nil { return peer.PeerInfo{}, err } @@ -386,8 +384,8 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< peerchan := make(chan peer.PeerInfo, asyncQueryBuffer) peersSeen := peer.Set{} - closest := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue) - if closest == nil || len(closest) == 0 { + peers := dht.routingTable.ListPeers() + if len(peers) == 0 { return nil, errors.Wrap(kb.ErrLookupFailure) } @@ -432,7 +430,7 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< // run it! run it asynchronously to gen peers as results are found. // this does no error checking go func() { - if _, err := query.Run(ctx, closest); err != nil { + if _, err := query.Run(ctx, peers); err != nil { log.Error(err) } From b01579e09aec2fba742cc4d8459b130ffa7f2ad8 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 20 Jan 2015 07:38:20 -0800 Subject: [PATCH 0652/3817] core/bootstrap: cleaned up bootstrapping Moved it to its own package to isolate scope. This commit was moved from ipfs/go-ipfs-routing@f634db0929f93ba1a4241951663290805903a728 --- routing/dht/dht_bootstrap.go | 66 ++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 095c194d6..c3991972c 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -14,6 +14,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" goprocess "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" + periodicproc "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/periodic" ) // DefaultBootstrapQueries specifies how many queries to run, @@ -54,9 +55,9 @@ const DefaultBootstrapTimeout = time.Duration(10 * time.Second) // and connected to at least a few nodes. // // Like PeriodicBootstrap, Bootstrap returns a process, so the user can stop it. -func (dht *IpfsDHT) Bootstrap() (goprocess.Process, error) { +func (dht *IpfsDHT) Bootstrap(ctx context.Context) (goprocess.Process, error) { - if err := dht.runBootstrap(dht.Context(), DefaultBootstrapQueries); err != nil { + if err := dht.runBootstrap(ctx, DefaultBootstrapQueries); err != nil { return nil, err } @@ -79,41 +80,32 @@ func (dht *IpfsDHT) BootstrapOnSignal(queries int, signal <-chan time.Time) (gop return nil, fmt.Errorf("invalid signal: %v", signal) } - proc := goprocess.Go(func(worker goprocess.Process) { - defer log.Debug("dht bootstrapper shutting down") - for { - select { - case <-worker.Closing(): - return - - case <-signal: - // it would be useful to be able to send out signals of when we bootstrap, too... - // maybe this is a good case for whole module event pub/sub? - - ctx := dht.Context() - if err := dht.runBootstrap(ctx, queries); err != nil { - log.Error(err) - // A bootstrapping error is important to notice but not fatal. - // maybe the client should be able to consume these errors, - // though I dont have a clear use case in mind-- what **could** - // the client do if one of the bootstrap calls fails? - // - // This is also related to the core's bootstrap failures. - // superviseConnections should perhaps allow clients to detect - // bootstrapping problems. - // - // Anyway, passing errors could be done with a bootstrapper object. - // this would imply the client should be able to consume a lot of - // other non-fatal dht errors too. providing this functionality - // should be done correctly DHT-wide. - // NB: whatever the design, clients must ensure they drain errors! - // This pattern is common to many things, perhaps long-running services - // should have something like an ErrStream that allows clients to consume - // periodic errors and take action. It should allow the user to also - // ignore all errors with something like an ErrStreamDiscard. We should - // study what other systems do for ideas. - } - } + proc := periodicproc.Ticker(signal, func(worker goprocess.Process) { + // it would be useful to be able to send out signals of when we bootstrap, too... + // maybe this is a good case for whole module event pub/sub? + + ctx := dht.Context() + if err := dht.runBootstrap(ctx, queries); err != nil { + log.Error(err) + // A bootstrapping error is important to notice but not fatal. + // maybe the client should be able to consume these errors, + // though I dont have a clear use case in mind-- what **could** + // the client do if one of the bootstrap calls fails? + // + // This is also related to the core's bootstrap failures. + // superviseConnections should perhaps allow clients to detect + // bootstrapping problems. + // + // Anyway, passing errors could be done with a bootstrapper object. + // this would imply the client should be able to consume a lot of + // other non-fatal dht errors too. providing this functionality + // should be done correctly DHT-wide. + // NB: whatever the design, clients must ensure they drain errors! + // This pattern is common to many things, perhaps long-running services + // should have something like an ErrStream that allows clients to consume + // periodic errors and take action. It should allow the user to also + // ignore all errors with something like an ErrStreamDiscard. We should + // study what other systems do for ideas. } }) From 9f181a8b796fbee1f0b6accb91119e5374e79fd0 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 20 Jan 2015 17:22:14 -0800 Subject: [PATCH 0653/3817] core/bootstrap: CR comments This commit was moved from ipfs/go-ipfs-routing@d771d4b8214b98b99d897d2b7ea6181ee1c17423 --- routing/dht/dht_bootstrap.go | 1 + routing/dht/dht_test.go | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index c3991972c..588bcfd75 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -128,6 +128,7 @@ func (dht *IpfsDHT) runBootstrap(ctx context.Context, queries int) error { // the dht will rehash to its own keyspace anyway. id := make([]byte, 16) rand.Read(id) + id = u.Hash(id) return peer.ID(id) } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index afc5756e8..2e1e1129f 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -75,8 +75,6 @@ func connect(t *testing.T, ctx context.Context, a, b *IpfsDHT) { func bootstrap(t *testing.T, ctx context.Context, dhts []*IpfsDHT) { ctx, cancel := context.WithCancel(ctx) - log.Error("hmm") - defer log.Error("hmm end") log.Debugf("bootstrapping dhts...") // tried async. sequential fares much better. compare: From f52295b2e9dfeab4ec8e0873b947d49071df27cc Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 23 Jan 2015 04:36:18 -0800 Subject: [PATCH 0654/3817] core: cleaned up bootstrap process This commit was moved from ipfs/go-ipfs-routing@6423b2e4227f3200f944727baa8aad1a20bfb70d --- routing/dht/dht_bootstrap.go | 112 +++++++++++++---------------------- 1 file changed, 42 insertions(+), 70 deletions(-) diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 588bcfd75..c91df05e5 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -17,52 +17,42 @@ import ( periodicproc "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/periodic" ) -// DefaultBootstrapQueries specifies how many queries to run, -// if the user does not specify a different number as an option. +// BootstrapConfig specifies parameters used bootstrapping the DHT. // -// For now, this is set to 16 queries, which is an aggressive number. -// We are currently more interested in ensuring we have a properly formed -// DHT than making sure our dht minimizes traffic. Once we are more certain -// of our implementation's robustness, we should lower this down to 8 or 4. -// -// Note there is also a tradeoff between the bootstrap period and the number -// of queries. We could support a higher period with a smaller number of -// queries -const DefaultBootstrapQueries = 1 +// Note there is a tradeoff between the bootstrap period and the +// number of queries. We could support a higher period with less +// queries. +type BootstrapConfig struct { + Queries int // how many queries to run per period + Period time.Duration // how often to run periodi cbootstrap. + Timeout time.Duration // how long to wait for a bootstrao query to run +} -// DefaultBootstrapPeriod specifies how often to periodically run bootstrap, -// if the user does not specify a different number as an option. -// -// For now, this is set to 10 seconds, which is an aggressive period. We are -// We are currently more interested in ensuring we have a properly formed -// DHT than making sure our dht minimizes traffic. Once we are more certain -// implementation's robustness, we should lower this down to 30s or 1m. -// -// Note there is also a tradeoff between the bootstrap period and the number -// of queries. We could support a higher period with a smaller number of -// queries -const DefaultBootstrapPeriod = time.Duration(10 * time.Second) - -// DefaultBootstrapTimeout specifies how long to wait for a bootstrap query -// to run. -const DefaultBootstrapTimeout = time.Duration(10 * time.Second) - -// Bootstrap runs bootstrapping once, then calls SignalBootstrap with default -// parameters: DefaultBootstrapQueries and DefaultBootstrapPeriod. This allows -// the user to catch an error off the bat if the connections are faulty. It also -// allows BootstrapOnSignal not to run bootstrap at the beginning, which is useful -// for instrumenting it on tests, or delaying bootstrap until the network is online -// and connected to at least a few nodes. -// -// Like PeriodicBootstrap, Bootstrap returns a process, so the user can stop it. -func (dht *IpfsDHT) Bootstrap(ctx context.Context) (goprocess.Process, error) { +var DefaultBootstrapConfig = BootstrapConfig{ + // For now, this is set to 1 query. + // We are currently more interested in ensuring we have a properly formed + // DHT than making sure our dht minimizes traffic. Once we are more certain + // of our implementation's robustness, we should lower this down to 8 or 4. + Queries: 1, - if err := dht.runBootstrap(ctx, DefaultBootstrapQueries); err != nil { - return nil, err - } + // For now, this is set to 10 seconds, which is an aggressive period. We are + // We are currently more interested in ensuring we have a properly formed + // DHT than making sure our dht minimizes traffic. Once we are more certain + // implementation's robustness, we should lower this down to 30s or 1m. + Period: time.Duration(20 * time.Second), - sig := time.Tick(DefaultBootstrapPeriod) - return dht.BootstrapOnSignal(DefaultBootstrapQueries, sig) + Timeout: time.Duration(20 * time.Second), +} + +// Bootstrap ensures the dht routing table remains healthy as peers come and go. +// it builds up a list of peers by requesting random peer IDs. The Bootstrap +// process will run a number of queries each time, and run every time signal fires. +// These parameters are configurable. +// +// Bootstrap returns a process, so the user can stop it. +func (dht *IpfsDHT) Bootstrap(config BootstrapConfig) (goprocess.Process, error) { + sig := time.Tick(config.Period) + return dht.BootstrapOnSignal(config, sig) } // SignalBootstrap ensures the dht routing table remains healthy as peers come and go. @@ -71,9 +61,9 @@ func (dht *IpfsDHT) Bootstrap(ctx context.Context) (goprocess.Process, error) { // These parameters are configurable. // // SignalBootstrap returns a process, so the user can stop it. -func (dht *IpfsDHT) BootstrapOnSignal(queries int, signal <-chan time.Time) (goprocess.Process, error) { - if queries <= 0 { - return nil, fmt.Errorf("invalid number of queries: %d", queries) +func (dht *IpfsDHT) BootstrapOnSignal(cfg BootstrapConfig, signal <-chan time.Time) (goprocess.Process, error) { + if cfg.Queries <= 0 { + return nil, fmt.Errorf("invalid number of queries: %d", cfg.Queries) } if signal == nil { @@ -85,27 +75,9 @@ func (dht *IpfsDHT) BootstrapOnSignal(queries int, signal <-chan time.Time) (gop // maybe this is a good case for whole module event pub/sub? ctx := dht.Context() - if err := dht.runBootstrap(ctx, queries); err != nil { + if err := dht.runBootstrap(ctx, cfg); err != nil { log.Error(err) // A bootstrapping error is important to notice but not fatal. - // maybe the client should be able to consume these errors, - // though I dont have a clear use case in mind-- what **could** - // the client do if one of the bootstrap calls fails? - // - // This is also related to the core's bootstrap failures. - // superviseConnections should perhaps allow clients to detect - // bootstrapping problems. - // - // Anyway, passing errors could be done with a bootstrapper object. - // this would imply the client should be able to consume a lot of - // other non-fatal dht errors too. providing this functionality - // should be done correctly DHT-wide. - // NB: whatever the design, clients must ensure they drain errors! - // This pattern is common to many things, perhaps long-running services - // should have something like an ErrStream that allows clients to consume - // periodic errors and take action. It should allow the user to also - // ignore all errors with something like an ErrStreamDiscard. We should - // study what other systems do for ideas. } }) @@ -113,7 +85,7 @@ func (dht *IpfsDHT) BootstrapOnSignal(queries int, signal <-chan time.Time) (gop } // runBootstrap builds up list of peers by requesting random peer IDs -func (dht *IpfsDHT) runBootstrap(ctx context.Context, queries int) error { +func (dht *IpfsDHT) runBootstrap(ctx context.Context, cfg BootstrapConfig) error { bslog := func(msg string) { log.Debugf("DHT %s dhtRunBootstrap %s -- routing table size: %d", dht.self, msg, dht.routingTable.Size()) } @@ -133,7 +105,7 @@ func (dht *IpfsDHT) runBootstrap(ctx context.Context, queries int) error { } // bootstrap sequentially, as results will compound - ctx, cancel := context.WithTimeout(ctx, DefaultBootstrapTimeout) + ctx, cancel := context.WithTimeout(ctx, cfg.Timeout) defer cancel() runQuery := func(ctx context.Context, id peer.ID) { p, err := dht.FindPeer(ctx, id) @@ -154,9 +126,9 @@ func (dht *IpfsDHT) runBootstrap(ctx context.Context, queries int) error { if sequential { // these should be parallel normally. but can make them sequential for debugging. // note that the core/bootstrap context deadline should be extended too for that. - for i := 0; i < queries; i++ { + for i := 0; i < cfg.Queries; i++ { id := randomID() - log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, queries, id) + log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, cfg.Queries, id) runQuery(ctx, id) } @@ -166,13 +138,13 @@ func (dht *IpfsDHT) runBootstrap(ctx context.Context, queries int) error { // normally, we should be selecting on ctx.Done() here too, but this gets // complicated to do with WaitGroup, and doesnt wait for the children to exit. var wg sync.WaitGroup - for i := 0; i < queries; i++ { + for i := 0; i < cfg.Queries; i++ { wg.Add(1) go func() { defer wg.Done() id := randomID() - log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, queries, id) + log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, cfg.Queries, id) runQuery(ctx, id) }() } From b9bc6f98876e83714b6ee4305cb279eef05a9616 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 23 Jan 2015 04:36:32 -0800 Subject: [PATCH 0655/3817] reprovide: wait a minute before reproviding Many times, a node will start up only to shut down immediately. In these cases, reproviding is costly to both the node, and the rest of the network. Also note: the probability of a node being up another minute increases with uptime. TODO: maybe this should be 5 * time.Minute This commit was moved from ipfs/go-ipfs-routing@e07559b808ad344d426b348cd9c2b0d4057f3ce6 --- routing/dht/dht_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 2e1e1129f..b7b9faf71 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -82,10 +82,14 @@ func bootstrap(t *testing.T, ctx context.Context, dhts []*IpfsDHT) { // 100 sync https://gist.github.com/jbenet/6c59e7c15426e48aaedd // probably because results compound + var cfg BootstrapConfig + cfg = DefaultBootstrapConfig + cfg.Queries = 3 + start := rand.Intn(len(dhts)) // randomize to decrease bias. for i := range dhts { dht := dhts[(start+i)%len(dhts)] - dht.runBootstrap(ctx, 3) + dht.runBootstrap(ctx, cfg) } cancel() } @@ -356,11 +360,15 @@ func TestPeriodicBootstrap(t *testing.T) { signal := make(chan time.Time) allSignals := []chan time.Time{} + var cfg BootstrapConfig + cfg = DefaultBootstrapConfig + cfg.Queries = 5 + // kick off periodic bootstrappers with instrumented signals. for _, dht := range dhts { s := make(chan time.Time) allSignals = append(allSignals, s) - dht.BootstrapOnSignal(5, s) + dht.BootstrapOnSignal(cfg, s) } go amplify(signal, allSignals) From 23b66777f659adcd55643f4c095ee488ac4b0cf5 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 23 Jan 2015 04:36:32 -0800 Subject: [PATCH 0656/3817] reprovide: wait a minute before reproviding Many times, a node will start up only to shut down immediately. In these cases, reproviding is costly to both the node, and the rest of the network. Also note: the probability of a node being up another minute increases with uptime. TODO: maybe this should be 5 * time.Minute This commit was moved from ipfs/go-ipfs-pinner@8a3c8f7530932d1d3081c254f51a1498fa28a769 --- pinning/pinner/indirect.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index 9e67bc2c9..09decbb25 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -32,7 +32,7 @@ func loadIndirPin(d ds.Datastore, k ds.Key) (*indirectPin, error) { keys = append(keys, k) refcnt[k] = v } - log.Debugf("indirPin keys: %#v", keys) + // log.Debugf("indirPin keys: %#v", keys) return &indirectPin{blockset: set.SimpleSetFromKeys(keys), refCounts: refcnt}, nil } From a6b25bfe493808efe3b1743a0f1a48a1c79b5d1d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 22 Jan 2015 07:59:57 +0000 Subject: [PATCH 0657/3817] really ugly impl of 'ipfs dht query' command This commit was moved from ipfs/go-ipfs-routing@5eb4c50fcee38cc331cff7dd04ea397df6db2a3e --- routing/dht/lookup.go | 156 +++++++++++++++++++++++++++++++++++++++++ routing/dht/routing.go | 77 +------------------- 2 files changed, 158 insertions(+), 75 deletions(-) create mode 100644 routing/dht/lookup.go diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go new file mode 100644 index 000000000..fe746498a --- /dev/null +++ b/routing/dht/lookup.go @@ -0,0 +1,156 @@ +package dht + +import ( + "encoding/json" + + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + + peer "github.com/jbenet/go-ipfs/p2p/peer" + kb "github.com/jbenet/go-ipfs/routing/kbucket" + u "github.com/jbenet/go-ipfs/util" + errors "github.com/jbenet/go-ipfs/util/debugerror" + pset "github.com/jbenet/go-ipfs/util/peerset" +) + +type QueryEventType int + +const ( + SendingQuery QueryEventType = iota + PeerResponse + FinalPeer +) + +type QueryEvent struct { + ID peer.ID + Type QueryEventType + Responses []*peer.PeerInfo +} + +func pointerizePeerInfos(pis []peer.PeerInfo) []*peer.PeerInfo { + out := make([]*peer.PeerInfo, len(pis)) + for i, p := range pis { + np := p + out[i] = &np + } + return out +} + +// Kademlia 'node lookup' operation. Returns a channel of the K closest peers +// to the given key +func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key u.Key, events chan<- *QueryEvent) (<-chan peer.ID, error) { + e := log.EventBegin(ctx, "getClosestPeers", &key) + tablepeers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) + if len(tablepeers) == 0 { + return nil, errors.Wrap(kb.ErrLookupFailure) + } + + out := make(chan peer.ID, KValue) + peerset := pset.NewLimited(KValue) + + for _, p := range tablepeers { + select { + case out <- p: + case <-ctx.Done(): + return nil, ctx.Err() + } + peerset.Add(p) + } + + query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { + // For DHT query command + select { + case events <- &QueryEvent{ + Type: SendingQuery, + ID: p, + }: + } + + closer, err := dht.closerPeersSingle(ctx, key, p) + if err != nil { + log.Errorf("error getting closer peers: %s", err) + return nil, err + } + + var filtered []peer.PeerInfo + for _, clp := range closer { + if kb.Closer(clp, dht.self, key) && peerset.TryAdd(clp) { + select { + case out <- clp: + log.Error("Sending out peer: %s", clp.Pretty()) + case <-ctx.Done(): + return nil, ctx.Err() + } + filtered = append(filtered, dht.peerstore.PeerInfo(clp)) + } + } + log.Errorf("filtered: %v", filtered) + + // For DHT query command + select { + case events <- &QueryEvent{ + Type: PeerResponse, + ID: p, + Responses: pointerizePeerInfos(filtered), + }: + } + + return &dhtQueryResult{closerPeers: filtered}, nil + }) + + go func() { + defer close(out) + defer e.Done() + // run it! + _, err := query.Run(ctx, tablepeers) + if err != nil { + log.Debugf("closestPeers query run error: %s", err) + } + }() + + return out, nil +} + +func (dht *IpfsDHT) closerPeersSingle(ctx context.Context, key u.Key, p peer.ID) ([]peer.ID, error) { + pmes, err := dht.findPeerSingle(ctx, p, peer.ID(key)) + if err != nil { + return nil, err + } + + var out []peer.ID + for _, pbp := range pmes.GetCloserPeers() { + pid := peer.ID(pbp.GetId()) + if pid != dht.self { // dont add self + dht.peerstore.AddAddresses(pid, pbp.Addresses()) + out = append(out, pid) + } + } + return out, nil +} + +func (qe *QueryEvent) MarshalJSON() ([]byte, error) { + out := make(map[string]interface{}) + out["ID"] = peer.IDB58Encode(qe.ID) + out["Type"] = int(qe.Type) + out["Responses"] = qe.Responses + return json.Marshal(out) +} + +func (qe *QueryEvent) UnmarshalJSON(b []byte) error { + temp := struct { + ID string + Type int + Responses []*peer.PeerInfo + }{} + err := json.Unmarshal(b, &temp) + if err != nil { + return err + } + pid, err := peer.IDB58Decode(temp.ID) + if err != nil { + return err + } + qe.ID = pid + qe.Type = QueryEventType(temp.Type) + qe.Responses = temp.Responses + return nil +} diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 2054e03fd..3eaedc619 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -48,7 +48,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error return err } - pchan, err := dht.getClosestPeers(ctx, key) + pchan, err := dht.GetClosestPeers(ctx, key, nil) if err != nil { return err } @@ -134,7 +134,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { // add self locally dht.providers.AddProvider(key, dht.self) - peers, err := dht.getClosestPeers(ctx, key) + peers, err := dht.GetClosestPeers(ctx, key, nil) if err != nil { return err } @@ -164,79 +164,6 @@ func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]peer.PeerIn return providers, nil } -// Kademlia 'node lookup' operation. Returns a channel of the K closest peers -// to the given key -func (dht *IpfsDHT) getClosestPeers(ctx context.Context, key u.Key) (<-chan peer.ID, error) { - e := log.EventBegin(ctx, "getClosestPeers", &key) - tablepeers := dht.routingTable.ListPeers() - if len(tablepeers) == 0 { - return nil, errors.Wrap(kb.ErrLookupFailure) - } - - out := make(chan peer.ID, KValue) - peerset := pset.NewLimited(KValue) - - for _, p := range tablepeers { - select { - case out <- p: - case <-ctx.Done(): - return nil, ctx.Err() - } - peerset.Add(p) - } - - query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { - closer, err := dht.closerPeersSingle(ctx, key, p) - if err != nil { - log.Errorf("error getting closer peers: %s", err) - return nil, err - } - - var filtered []peer.PeerInfo - for _, p := range closer { - if kb.Closer(p, dht.self, key) && peerset.TryAdd(p) { - select { - case out <- p: - case <-ctx.Done(): - return nil, ctx.Err() - } - filtered = append(filtered, dht.peerstore.PeerInfo(p)) - } - } - - return &dhtQueryResult{closerPeers: filtered}, nil - }) - - go func() { - defer close(out) - defer e.Done() - // run it! - _, err := query.Run(ctx, tablepeers) - if err != nil { - log.Debugf("closestPeers query run error: %s", err) - } - }() - - return out, nil -} - -func (dht *IpfsDHT) closerPeersSingle(ctx context.Context, key u.Key, p peer.ID) ([]peer.ID, error) { - pmes, err := dht.findPeerSingle(ctx, p, peer.ID(key)) - if err != nil { - return nil, err - } - - var out []peer.ID - for _, pbp := range pmes.GetCloserPeers() { - pid := peer.ID(pbp.GetId()) - if pid != dht.self { // dont add self - dht.peerstore.AddAddresses(pid, pbp.Addresses()) - out = append(out, pid) - } - } - return out, nil -} - // FindProvidersAsync is the same thing as FindProviders, but returns a channel. // Peers will be returned on the channel as soon as they are found, even before // the search query completes. From d965d81d62477a93f39d4c0e9c2659f8b466c19a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 22 Jan 2015 18:18:41 +0000 Subject: [PATCH 0658/3817] use a notification type strategy for the query events This commit was moved from ipfs/go-ipfs-routing@f985252841743a55b48e622042d51f0d79ef7a17 --- routing/dht/lookup.go | 64 ++++++------------------------------------ routing/dht/routing.go | 4 +-- 2 files changed, 11 insertions(+), 57 deletions(-) diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index fe746498a..c0be519b2 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -1,10 +1,9 @@ package dht import ( - "encoding/json" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + notif "github.com/jbenet/go-ipfs/notifications" peer "github.com/jbenet/go-ipfs/p2p/peer" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" @@ -12,20 +11,7 @@ import ( pset "github.com/jbenet/go-ipfs/util/peerset" ) -type QueryEventType int - -const ( - SendingQuery QueryEventType = iota - PeerResponse - FinalPeer -) - -type QueryEvent struct { - ID peer.ID - Type QueryEventType - Responses []*peer.PeerInfo -} - +// Required in order for proper JSON marshaling func pointerizePeerInfos(pis []peer.PeerInfo) []*peer.PeerInfo { out := make([]*peer.PeerInfo, len(pis)) for i, p := range pis { @@ -37,7 +23,7 @@ func pointerizePeerInfos(pis []peer.PeerInfo) []*peer.PeerInfo { // Kademlia 'node lookup' operation. Returns a channel of the K closest peers // to the given key -func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key u.Key, events chan<- *QueryEvent) (<-chan peer.ID, error) { +func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key u.Key) (<-chan peer.ID, error) { e := log.EventBegin(ctx, "getClosestPeers", &key) tablepeers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) if len(tablepeers) == 0 { @@ -58,12 +44,10 @@ func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key u.Key, events chan< query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { // For DHT query command - select { - case events <- &QueryEvent{ - Type: SendingQuery, + notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + Type: notif.SendingQuery, ID: p, - }: - } + }) closer, err := dht.closerPeersSingle(ctx, key, p) if err != nil { @@ -86,13 +70,11 @@ func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key u.Key, events chan< log.Errorf("filtered: %v", filtered) // For DHT query command - select { - case events <- &QueryEvent{ - Type: PeerResponse, + notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + Type: notif.PeerResponse, ID: p, Responses: pointerizePeerInfos(filtered), - }: - } + }) return &dhtQueryResult{closerPeers: filtered}, nil }) @@ -126,31 +108,3 @@ func (dht *IpfsDHT) closerPeersSingle(ctx context.Context, key u.Key, p peer.ID) } return out, nil } - -func (qe *QueryEvent) MarshalJSON() ([]byte, error) { - out := make(map[string]interface{}) - out["ID"] = peer.IDB58Encode(qe.ID) - out["Type"] = int(qe.Type) - out["Responses"] = qe.Responses - return json.Marshal(out) -} - -func (qe *QueryEvent) UnmarshalJSON(b []byte) error { - temp := struct { - ID string - Type int - Responses []*peer.PeerInfo - }{} - err := json.Unmarshal(b, &temp) - if err != nil { - return err - } - pid, err := peer.IDB58Decode(temp.ID) - if err != nil { - return err - } - qe.ID = pid - qe.Type = QueryEventType(temp.Type) - qe.Responses = temp.Responses - return nil -} diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 3eaedc619..c1911ccda 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -48,7 +48,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error return err } - pchan, err := dht.GetClosestPeers(ctx, key, nil) + pchan, err := dht.GetClosestPeers(ctx, key) if err != nil { return err } @@ -134,7 +134,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { // add self locally dht.providers.AddProvider(key, dht.self) - peers, err := dht.GetClosestPeers(ctx, key, nil) + peers, err := dht.GetClosestPeers(ctx, key) if err != nil { return err } From efb14cc8ff1a9513daed02efed2c9544311b6c9b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 22 Jan 2015 07:34:26 -0800 Subject: [PATCH 0659/3817] dont rate limit query during dials This commit was moved from ipfs/go-ipfs-routing@1233183ef266dd9cea88eea3ddf0dbe5c35b15f1 --- routing/dht/query.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/routing/dht/query.go b/routing/dht/query.go index 687d2621f..f4150d82e 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -223,6 +223,9 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { // make sure we're connected to the peer. if conns := r.query.dht.host.Network().ConnsToPeer(p); len(conns) == 0 { log.Infof("not connected. dialing.") + // while we dial, we do not take up a rate limit. this is to allow + // forward progress during potentially very high latency dials. + r.rateLimit <- struct{}{} pi := peer.PeerInfo{ID: p} if err := r.query.dht.host.Connect(cg.Context(), pi); err != nil { @@ -230,9 +233,10 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { r.Lock() r.errs = append(r.errs, err) r.Unlock() + <-r.rateLimit // need to grab it again, as we deferred. return } - + <-r.rateLimit // need to grab it again, as we deferred. log.Debugf("connected. dial success.") } From 0f4cf52453dc62ced46d74e767b315e18d793a79 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 22 Jan 2015 22:18:36 +0000 Subject: [PATCH 0660/3817] implement dht findprovs and add error output to dht query This commit was moved from ipfs/go-ipfs-routing@1dd19b84016af3bf8a4ce70c2e79e2746dfbeca7 --- routing/dht/lookup.go | 1 - routing/dht/query.go | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index c0be519b2..c97e70fb1 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -67,7 +67,6 @@ func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key u.Key) (<-chan peer filtered = append(filtered, dht.peerstore.PeerInfo(clp)) } } - log.Errorf("filtered: %v", filtered) // For DHT query command notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ diff --git a/routing/dht/query.go b/routing/dht/query.go index f4150d82e..dfaecef98 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -3,6 +3,7 @@ package dht import ( "sync" + notif "github.com/jbenet/go-ipfs/notifications" peer "github.com/jbenet/go-ipfs/p2p/peer" queue "github.com/jbenet/go-ipfs/p2p/peer/queue" "github.com/jbenet/go-ipfs/routing" @@ -230,6 +231,12 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { pi := peer.PeerInfo{ID: p} if err := r.query.dht.host.Connect(cg.Context(), pi); err != nil { log.Debugf("Error connecting: %s", err) + + notif.PublishQueryEvent(cg.Context(), ¬if.QueryEvent{ + Type: notif.QueryError, + Extra: err.Error(), + }) + r.Lock() r.errs = append(r.errs, err) r.Unlock() From 4fa9ba043005742653e958b5c718aaa337efdc2d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 24 Jan 2015 00:59:47 +0000 Subject: [PATCH 0661/3817] respect verbose option a bit, and show query events for other commands This commit was moved from ipfs/go-ipfs-routing@b13eb0c3151e867aaff89a94791a46e5502477a4 --- routing/dht/lookup.go | 1 - routing/dht/routing.go | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index c97e70fb1..ea1552ba7 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -60,7 +60,6 @@ func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key u.Key) (<-chan peer if kb.Closer(clp, dht.self, key) && peerset.TryAdd(clp) { select { case out <- clp: - log.Error("Sending out peer: %s", clp.Pretty()) case <-ctx.Done(): return nil, ctx.Err() } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index c1911ccda..39002fe64 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -7,6 +7,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + notif "github.com/jbenet/go-ipfs/notifications" inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" "github.com/jbenet/go-ipfs/routing" @@ -242,6 +243,10 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co _, err := query.Run(ctx, peers) if err != nil { log.Errorf("Query error: %s", err) + notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + Type: notif.QueryError, + Extra: err.Error(), + }) } } @@ -269,6 +274,10 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, er // setup the Query query := dht.newQuery(u.Key(id), func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { + notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + Type: notif.SendingQuery, + ID: p, + }) pmes, err := dht.findPeerSingle(ctx, p, id) if err != nil { @@ -288,6 +297,11 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, er } } + notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + Type: notif.PeerResponse, + Responses: pointerizePeerInfos(clpeerInfos), + }) + return &dhtQueryResult{closerPeers: clpeerInfos}, nil }) From a1c5b912feaa0d5d65ee2921ee3df29aad234f1e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 24 Jan 2015 00:24:44 -0800 Subject: [PATCH 0662/3817] remove prefix logger This commit was moved from ipfs/go-ipfs-routing@fbb5c35bf7420560d99604c63e710990fd613261 --- routing/dht/dht.go | 2 +- routing/dht/query.go | 9 --------- routing/dht/routing.go | 12 ------------ 3 files changed, 1 insertion(+), 22 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 0fd5177a2..5f7512393 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -97,7 +97,7 @@ func (dht *IpfsDHT) LocalPeer() peer.ID { // log returns the dht's logger func (dht *IpfsDHT) log() eventlog.EventLogger { - return log.Prefix("dht(%s)", dht.self) + return log // TODO rm } // Connect to a new peer at the given address, ping and add to the routing table diff --git a/routing/dht/query.go b/routing/dht/query.go index dfaecef98..14a23de6f 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -84,7 +84,6 @@ func newQueryRunner(ctx context.Context, q *dhtQuery) *dhtQueryRunner { } func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { - log := log.Prefix("dht(%s).Query(%s).Run(%d)", r.query.dht.self, r.query.key, len(peers)) r.log = log log.Debug("enter") defer log.Debug("end") @@ -169,10 +168,6 @@ func (r *dhtQueryRunner) addPeerToQuery(ctx context.Context, next peer.ID) { } func (r *dhtQueryRunner) spawnWorkers(parent ctxgroup.ContextGroup) { - log := r.log.Prefix("spawnWorkers") - log.Debugf("begin") - defer log.Debugf("end") - for { select { @@ -198,10 +193,6 @@ func (r *dhtQueryRunner) spawnWorkers(parent ctxgroup.ContextGroup) { } func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { - log := r.log.Prefix("queryPeer(%s)", p) - log.Debugf("spawned") - defer log.Debugf("finished") - // make sure we rate limit concurrency. select { case <-r.rateLimit: diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 39002fe64..0de059eec 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -73,10 +73,6 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error // If the search does not succeed, a multiaddr string of a closer peer is // returned along with util.ErrSearchIncomplete func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { - log := dht.log().Prefix("GetValue(%s)", key) - log.Debugf("start") - defer log.Debugf("end") - // If we have it local, dont bother doing an RPC! val, err := dht.getLocal(key) if err == nil { @@ -128,8 +124,6 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { // Provide makes this node announce that it can provide a value for the given key func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { - log := dht.log().Prefix("Provide(%s)", key) - defer log.EventBegin(ctx, "provide", &key).Done() // add self locally @@ -176,8 +170,6 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int } func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, count int, peerOut chan peer.PeerInfo) { - log := dht.log().Prefix("FindProviders(%s)", key) - defer log.EventBegin(ctx, "findProvidersAsync", &key).Done() defer close(peerOut) @@ -201,10 +193,6 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co // setup the Query query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { - log := log.Prefix("Query(%s)", p) - log.Debugf("begin") - defer log.Debugf("end") - pmes, err := dht.findProvidersSingle(ctx, p, key) if err != nil { return nil, err From 162326e45e1a32cc587b0f2a1da844fab310735c Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 23 Jan 2015 22:03:05 -0800 Subject: [PATCH 0663/3817] provide simple wrapper methods for AllKeysRange @jbenet @whyrusleeping was the 1<<16 intentional? replaced the raw methods with wrappers. This commit was moved from ipfs/go-ipfs-blockstore@0a26e549358ce74b68a0ecdd4da12563d5274ec8 --- blockstore/blockstore.go | 29 ++++++++++++++++++++--------- blockstore/blockstore_test.go | 6 +++--- blockstore/write_cache.go | 16 ++++++++++++---- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 3c98b0735..70a705884 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -32,8 +32,11 @@ type Blockstore interface { Get(u.Key) (*blocks.Block, error) Put(*blocks.Block) error - AllKeys(ctx context.Context, offset int, limit int) ([]u.Key, error) - AllKeysChan(ctx context.Context, offset int, limit int) (<-chan u.Key, error) + AllKeys(ctx context.Context) ([]u.Key, error) + AllKeysChan(ctx context.Context) (<-chan u.Key, error) + + AllKeysRange(ctx context.Context, offset int, limit int) ([]u.Key, error) + AllKeysRangeChan(ctx context.Context, offset int, limit int) (<-chan u.Key, error) } func NewBlockstore(d ds.ThreadSafeDatastore) Blockstore { @@ -83,14 +86,22 @@ func (s *blockstore) DeleteBlock(k u.Key) error { return s.datastore.Delete(k.DsKey()) } -// AllKeys runs a query for keys from the blockstore. +func (bs *blockstore) AllKeys(ctx context.Context) ([]u.Key, error) { + return bs.AllKeysRange(ctx, 0, 0) +} + +func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan u.Key, error) { + return bs.AllKeysRangeChan(ctx, 0, 0) +} + +// AllKeysRange runs a query for keys from the blockstore. // this is very simplistic, in the future, take dsq.Query as a param? // if offset and limit are 0, they are ignored. // -// AllKeys respects context -func (bs *blockstore) AllKeys(ctx context.Context, offset int, limit int) ([]u.Key, error) { +// AllKeysRange respects context +func (bs *blockstore) AllKeysRange(ctx context.Context, offset int, limit int) ([]u.Key, error) { - ch, err := bs.AllKeysChan(ctx, offset, limit) + ch, err := bs.AllKeysRangeChan(ctx, offset, limit) if err != nil { return nil, err } @@ -102,12 +113,12 @@ func (bs *blockstore) AllKeys(ctx context.Context, offset int, limit int) ([]u.K return keys, nil } -// AllKeys runs a query for keys from the blockstore. +// AllKeysRangeChan runs a query for keys from the blockstore. // this is very simplistic, in the future, take dsq.Query as a param? // if offset and limit are 0, they are ignored. // -// AllKeys respects context -func (bs *blockstore) AllKeysChan(ctx context.Context, offset int, limit int) (<-chan u.Key, error) { +// AllKeysRangeChan respects context +func (bs *blockstore) AllKeysRangeChan(ctx context.Context, offset int, limit int) (<-chan u.Key, error) { // KeysOnly, because that would be _a lot_ of data. q := dsq.Query{KeysOnly: true, Offset: offset, Limit: limit} diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 44f5964e8..2280f78f8 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -67,7 +67,7 @@ func TestAllKeysSimple(t *testing.T) { bs, keys := newBlockStoreWithKeys(t, nil, 100) ctx := context.Background() - keys2, err := bs.AllKeys(ctx, 0, 0) + keys2, err := bs.AllKeys(ctx) if err != nil { t.Fatal(err) } @@ -83,7 +83,7 @@ func TestAllKeysOffsetAndLimit(t *testing.T) { bs, _ := newBlockStoreWithKeys(t, nil, N) ctx := context.Background() - keys3, err := bs.AllKeys(ctx, N/3, N/3) + keys3, err := bs.AllKeysRange(ctx, N/3, N/3) if err != nil { t.Fatal(err) } @@ -107,7 +107,7 @@ func TestAllKeysRespectsContext(t *testing.T) { getKeys := func(ctx context.Context) { started <- struct{}{} - _, err := bs.AllKeys(ctx, 0, 0) // once without cancelling + _, err := bs.AllKeys(ctx) // once without cancelling if err != nil { errors <- err } diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index 377ce629d..487899597 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -46,10 +46,18 @@ func (w *writecache) Put(b *blocks.Block) error { return w.blockstore.Put(b) } -func (w *writecache) AllKeys(ctx context.Context, offset int, limit int) ([]u.Key, error) { - return w.blockstore.AllKeys(ctx, offset, limit) +func (w *writecache) AllKeys(ctx context.Context) ([]u.Key, error) { + return w.blockstore.AllKeysRange(ctx, 0, 0) } -func (w *writecache) AllKeysChan(ctx context.Context, offset int, limit int) (<-chan u.Key, error) { - return w.blockstore.AllKeysChan(ctx, offset, limit) +func (w *writecache) AllKeysChan(ctx context.Context) (<-chan u.Key, error) { + return w.blockstore.AllKeysRangeChan(ctx, 0, 0) +} + +func (w *writecache) AllKeysRange(ctx context.Context, offset int, limit int) ([]u.Key, error) { + return w.blockstore.AllKeysRange(ctx, offset, limit) +} + +func (w *writecache) AllKeysRangeChan(ctx context.Context, offset int, limit int) (<-chan u.Key, error) { + return w.blockstore.AllKeysRangeChan(ctx, offset, limit) } From ec2df3f5e0c108f6adb0736a9a45533ccf509d95 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 24 Jan 2015 00:26:42 -0800 Subject: [PATCH 0664/3817] Extracted TAR archive building/reading code out of 'ipfs get' This commit was moved from ipfs/go-unixfs@d38b695ea75af3316df4fb226cffa29b941bbbce --- unixfs/tar/reader.go | 200 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 unixfs/tar/reader.go diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go new file mode 100644 index 000000000..725e6867f --- /dev/null +++ b/unixfs/tar/reader.go @@ -0,0 +1,200 @@ +package tar + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "io" + p "path" + + mdag "github.com/jbenet/go-ipfs/merkledag" + path "github.com/jbenet/go-ipfs/path" + uio "github.com/jbenet/go-ipfs/unixfs/io" + upb "github.com/jbenet/go-ipfs/unixfs/pb" + + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" +) + +type Reader struct { + buf bytes.Buffer + closed bool + signalChan chan struct{} + dag mdag.DAGService + resolver *path.Resolver + writer *tar.Writer + gzipWriter *gzip.Writer + err error +} + +func NewReader(path string, dag mdag.DAGService, resolver *path.Resolver, compression int) (*Reader, error) { + reader := &Reader{ + signalChan: make(chan struct{}), + dag: dag, + resolver: resolver, + } + + var err error + if compression != gzip.NoCompression { + reader.gzipWriter, err = gzip.NewWriterLevel(&reader.buf, compression) + if err != nil { + return nil, err + } + reader.writer = tar.NewWriter(reader.gzipWriter) + } else { + reader.writer = tar.NewWriter(&reader.buf) + } + + dagnode, err := resolver.ResolvePath(path) + if err != nil { + return nil, err + } + + // writeToBuf will write the data to the buffer, and will signal when there + // is new data to read + go reader.writeToBuf(dagnode, path, 0) + + return reader, nil +} + +func (i *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { + pb := new(upb.Data) + err := proto.Unmarshal(dagnode.Data, pb) + if err != nil { + i.emitError(err) + return + } + + if depth == 0 { + defer i.close() + } + + if pb.GetType() == upb.Data_Directory { + err = i.writer.WriteHeader(&tar.Header{ + Name: path, + Typeflag: tar.TypeDir, + Mode: 0777, + // TODO: set mode, dates, etc. when added to unixFS + }) + if err != nil { + i.emitError(err) + return + } + + for _, link := range dagnode.Links { + childNode, err := link.GetNode(i.dag) + if err != nil { + i.emitError(err) + return + } + i.writeToBuf(childNode, p.Join(path, link.Name), depth+1) + } + return + } + + err = i.writer.WriteHeader(&tar.Header{ + Name: path, + Size: int64(pb.GetFilesize()), + Typeflag: tar.TypeReg, + Mode: 0644, + // TODO: set mode, dates, etc. when added to unixFS + }) + if err != nil { + i.emitError(err) + return + } + + reader, err := uio.NewDagReader(dagnode, i.dag) + if err != nil { + i.emitError(err) + return + } + + err = i.syncCopy(reader) + if err != nil { + i.emitError(err) + return + } +} + +func (i *Reader) Read(p []byte) (int, error) { + // wait for the goroutine that is writing data to the buffer to tell us + // there is something to read + if !i.closed { + <-i.signalChan + } + + if i.err != nil { + return 0, i.err + } + + if !i.closed { + defer i.signal() + } + + if i.buf.Len() == 0 { + if i.closed { + return 0, io.EOF + } + return 0, nil + } + + n, err := i.buf.Read(p) + if err == io.EOF && !i.closed || i.buf.Len() > 0 { + return n, nil + } + + return n, err +} + +func (i *Reader) signal() { + i.signalChan <- struct{}{} +} + +func (i *Reader) emitError(err error) { + i.err = err + i.signal() +} + +func (i *Reader) close() { + i.closed = true + i.flush() +} + +func (i *Reader) flush() { + defer i.signal() + err := i.writer.Close() + if err != nil { + i.emitError(err) + return + } + if i.gzipWriter != nil { + err = i.gzipWriter.Close() + if err != nil { + i.emitError(err) + return + } + } +} + +func (i *Reader) syncCopy(reader io.Reader) error { + buf := make([]byte, 32*1024) + for { + nr, err := reader.Read(buf) + if nr > 0 { + _, err := i.writer.Write(buf[:nr]) + if err != nil { + return err + } + i.signal() + // wait for Read to finish reading + <-i.signalChan + } + if err == io.EOF { + break + } + if err != nil { + return err + } + } + return nil +} From 4550b3a9cb3d007a1af3f13adefaa68fb2ecc7a5 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 24 Jan 2015 05:35:05 -0800 Subject: [PATCH 0665/3817] unixfs/tar: Fixed reader not properly buffering headers This commit was moved from ipfs/go-unixfs@04f921181b9a7045ef15185711b852a4cba4371c --- unixfs/tar/reader.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index 725e6867f..081d816a2 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -79,6 +79,7 @@ func (i *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { i.emitError(err) return } + i.flush() for _, link := range dagnode.Links { childNode, err := link.GetNode(i.dag) @@ -102,6 +103,7 @@ func (i *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { i.emitError(err) return } + i.flush() reader, err := uio.NewDagReader(dagnode, i.dag) if err != nil { @@ -150,6 +152,11 @@ func (i *Reader) signal() { i.signalChan <- struct{}{} } +func (i *Reader) flush() { + i.signal() + <-i.signalChan +} + func (i *Reader) emitError(err error) { i.err = err i.signal() @@ -157,10 +164,6 @@ func (i *Reader) emitError(err error) { func (i *Reader) close() { i.closed = true - i.flush() -} - -func (i *Reader) flush() { defer i.signal() err := i.writer.Close() if err != nil { @@ -185,9 +188,7 @@ func (i *Reader) syncCopy(reader io.Reader) error { if err != nil { return err } - i.signal() - // wait for Read to finish reading - <-i.signalChan + i.flush() } if err == io.EOF { break From 76f0073b7d91379c873ae97a3c172c648d340b39 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 24 Jan 2015 08:52:01 -0800 Subject: [PATCH 0666/3817] routing/dht: adjust routing table on peer conn/disc This commit was moved from ipfs/go-ipfs-routing@261de3076ef7d3f1f066183241840168732450e2 --- routing/dht/dht.go | 10 +++++++++- routing/dht/notif.go | 33 +++++++++++++++++++++++++++++++++ routing/kbucket/bucket.go | 10 ++++++++++ routing/kbucket/table.go | 17 +++++++++++++++++ 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 routing/dht/notif.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 5f7512393..e66517072 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -65,9 +65,17 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip dht.datastore = dstore dht.self = h.ID() dht.peerstore = h.Peerstore() - dht.ContextGroup = ctxgroup.WithContext(ctx) dht.host = h + // register for network notifs. + dht.host.Network().Notify((*netNotifiee)(dht)) + + dht.ContextGroup = ctxgroup.WithContextAndTeardown(ctx, func() error { + // remove ourselves from network notifs. + dht.host.Network().StopNotify((*netNotifiee)(dht)) + return nil + }) + // sanity check. this should **never** happen if len(dht.peerstore.Addresses(dht.self)) < 1 { panic("attempt to initialize dht without addresses for self") diff --git a/routing/dht/notif.go b/routing/dht/notif.go new file mode 100644 index 000000000..318db12ea --- /dev/null +++ b/routing/dht/notif.go @@ -0,0 +1,33 @@ +package dht + +import ( + inet "github.com/jbenet/go-ipfs/p2p/net" +) + +// netNotifiee defines methods to be used with the IpfsDHT +type netNotifiee IpfsDHT + +func (nn *netNotifiee) DHT() *IpfsDHT { + return (*IpfsDHT)(nn) +} + +func (nn *netNotifiee) Connected(n inet.Network, v inet.Conn) { + dht := nn.DHT() + select { + case <-dht.Closing(): + return + } + dht.Update(dht.Context(), v.RemotePeer()) +} + +func (nn *netNotifiee) Disconnected(n inet.Network, v inet.Conn) { + dht := nn.DHT() + select { + case <-dht.Closing(): + return + } + dht.routingTable.Remove(v.RemotePeer()) +} + +func (nn *netNotifiee) OpenedStream(n inet.Network, v inet.Stream) {} +func (nn *netNotifiee) ClosedStream(n inet.Network, v inet.Stream) {} diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index e158f70f9..7d4f87c2e 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -30,6 +30,16 @@ func (b *Bucket) find(id peer.ID) *list.Element { return nil } +func (b *Bucket) remove(id peer.ID) { + b.lk.RLock() + defer b.lk.RUnlock() + for e := b.list.Front(); e != nil; e = e.Next() { + if e.Value.(peer.ID) == id { + b.list.Remove(e) + } + } +} + func (b *Bucket) moveToFront(e *list.Element) { b.lk.Lock() b.list.MoveToFront(e) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 62bfa0646..59b81282d 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -87,6 +87,23 @@ func (rt *RoutingTable) Update(p peer.ID) peer.ID { return "" } +// Remove deletes a peer from the routing table. This is to be used +// when we are sure a node has disconnected completely. +func (rt *RoutingTable) Remove(p peer.ID) { + rt.tabLock.Lock() + defer rt.tabLock.Unlock() + peerID := ConvertPeerID(p) + cpl := commonPrefixLen(peerID, rt.local) + + bucketID := cpl + if bucketID >= len(rt.Buckets) { + bucketID = len(rt.Buckets) - 1 + } + + bucket := rt.Buckets[bucketID] + bucket.remove(p) +} + func (rt *RoutingTable) nextBucket() peer.ID { bucket := rt.Buckets[len(rt.Buckets)-1] newBucket := bucket.Split(len(rt.Buckets)-1, rt.local) From 1fe66d6d6344496d1488c1878d2e4bf911911c31 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 24 Jan 2015 09:45:07 -0800 Subject: [PATCH 0667/3817] dht/kbucket: race condition fix This commit was moved from ipfs/go-ipfs-routing@a4bf6b56c6ac2424c3b9716690176a222b8aa083 --- routing/kbucket/bucket.go | 15 +++++++++++---- routing/kbucket/table.go | 6 +++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index e158f70f9..32bc8631e 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -19,6 +19,17 @@ func newBucket() *Bucket { return b } +func (b *Bucket) Peers() []peer.ID { + b.lk.RLock() + defer b.lk.RUnlock() + ps := make([]peer.ID, 0, b.list.Len()) + for e := b.list.Front(); e != nil; e = e.Next() { + id := e.Value.(peer.ID) + ps = append(ps, id) + } + return ps +} + func (b *Bucket) find(id peer.ID) *list.Element { b.lk.RLock() defer b.lk.RUnlock() @@ -81,7 +92,3 @@ func (b *Bucket) Split(cpl int, target ID) *Bucket { } return newbuck } - -func (b *Bucket) getIter() *list.Element { - return b.list.Front() -} diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 62bfa0646..4ec35b5d3 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -176,11 +176,11 @@ func (rt *RoutingTable) Size() int { // NOTE: This is potentially unsafe... use at your own risk func (rt *RoutingTable) ListPeers() []peer.ID { var peers []peer.ID + rt.tabLock.RLock() for _, buck := range rt.Buckets { - for e := buck.getIter(); e != nil; e = e.Next() { - peers = append(peers, e.Value.(peer.ID)) - } + peers = append(peers, buck.Peers()...) } + rt.tabLock.RUnlock() return peers } From 760d294dabc192c712d73c0f937bf25e6868421b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 24 Jan 2015 10:30:15 -0800 Subject: [PATCH 0668/3817] disable dht TestPeriodicBootstrap on CI This commit was moved from ipfs/go-ipfs-routing@94c4656bc1314fdce6e1474a522a7453d0703aea --- routing/dht/dht_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index b7b9faf71..1b274395a 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -19,6 +19,7 @@ import ( netutil "github.com/jbenet/go-ipfs/p2p/test/util" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" + ci "github.com/jbenet/go-ipfs/util/testutil/ci" ) var testCaseValues = map[u.Key][]byte{} @@ -330,6 +331,9 @@ func TestBootstrap(t *testing.T) { func TestPeriodicBootstrap(t *testing.T) { // t.Skip("skipping test to debug another") + if ci.IsRunning() { + t.Skip("skipping on CI. highly timing dependent") + } if testing.Short() { t.SkipNow() } From 5180925494e33abc6545f7d1a5084c6918766465 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 24 Jan 2015 10:34:26 -0800 Subject: [PATCH 0669/3817] dht: TestConnectCollision skip in Travis + longer timeout This commit was moved from ipfs/go-ipfs-routing@65224292879e73f104df66b498402d06c93fa842 --- routing/dht/dht_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 1b274395a..2d8494b65 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -19,7 +19,9 @@ import ( netutil "github.com/jbenet/go-ipfs/p2p/test/util" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" + ci "github.com/jbenet/go-ipfs/util/testutil/ci" + travisci "github.com/jbenet/go-ipfs/util/testutil/ci/travis" ) var testCaseValues = map[u.Key][]byte{} @@ -738,6 +740,9 @@ func TestConnectCollision(t *testing.T) { if testing.Short() { t.SkipNow() } + if travisci.IsRunning() { + t.Skip("Skipping on Travis-CI.") + } runTimes := 10 @@ -767,7 +772,7 @@ func TestConnectCollision(t *testing.T) { errs <- err }() - timeout := time.After(time.Second) + timeout := time.After(5 * time.Second) select { case e := <-errs: if e != nil { From fae004b522a345cc596f8856155ff607a1850e46 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 25 Jan 2015 01:41:06 +0000 Subject: [PATCH 0670/3817] correct notifications for findProviders This commit was moved from ipfs/go-ipfs-routing@f3b051ffd90772d28310472fc3e8730155b70b0c --- routing/dht/routing.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 0de059eec..e04803ed2 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -193,6 +193,10 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co // setup the Query query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { + notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + Type: notif.SendingQuery, + ID: p, + }) pmes, err := dht.findProvidersSingle(ctx, p, key) if err != nil { return nil, err @@ -224,6 +228,12 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co closer := pmes.GetCloserPeers() clpeers := pb.PBPeersToPeerInfos(closer) log.Debugf("got closer peers: %d %s", len(clpeers), clpeers) + + notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + Type: notif.PeerResponse, + ID: p, + Responses: pointerizePeerInfos(clpeers), + }) return &dhtQueryResult{closerPeers: clpeers}, nil }) From b22819d3434a8fbf43f55a10de97ef93ec21ab72 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 24 Jan 2015 13:46:08 -0800 Subject: [PATCH 0671/3817] core/commands: get: Place files at root of TAR when using a multi-element ipfs path This commit was moved from ipfs/go-unixfs@165f19fa4337815405452798e3d7f9de1eff6e65 --- unixfs/tar/reader.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index 081d816a2..6ec333928 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -51,7 +51,8 @@ func NewReader(path string, dag mdag.DAGService, resolver *path.Resolver, compre // writeToBuf will write the data to the buffer, and will signal when there // is new data to read - go reader.writeToBuf(dagnode, path, 0) + _, filename := p.Split(path) + go reader.writeToBuf(dagnode, filename, 0) return reader, nil } From ba46e7e591d1e4a9a0ff139077423a5859a69dae Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 24 Jan 2015 15:04:54 -0800 Subject: [PATCH 0672/3817] unixfs/tar: Ignore /ipfs/ in path This commit was moved from ipfs/go-unixfs@c9ea7960d95f7e0fb08cf6f229fb4063ad0f285b --- unixfs/tar/reader.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index 6ec333928..a64a963eb 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -6,6 +6,7 @@ import ( "compress/gzip" "io" p "path" + "strings" mdag "github.com/jbenet/go-ipfs/merkledag" path "github.com/jbenet/go-ipfs/path" @@ -27,6 +28,10 @@ type Reader struct { } func NewReader(path string, dag mdag.DAGService, resolver *path.Resolver, compression int) (*Reader, error) { + if strings.HasPrefix(path, "/ipfs/") { + path = path[6:] + } + reader := &Reader{ signalChan: make(chan struct{}), dag: dag, From b32b99d62edc8ca40ab6f1f2387f8f9493bfc12e Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 26 Jan 2015 16:57:55 -0800 Subject: [PATCH 0673/3817] unixfs/tar: Use current date for file timestamps This commit was moved from ipfs/go-unixfs@5478c3c47c14c8f094a1cd28d9c9ef3d00230389 --- unixfs/tar/reader.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index a64a963eb..193f50333 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -7,6 +7,7 @@ import ( "io" p "path" "strings" + "time" mdag "github.com/jbenet/go-ipfs/merkledag" path "github.com/jbenet/go-ipfs/path" @@ -79,6 +80,7 @@ func (i *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { Name: path, Typeflag: tar.TypeDir, Mode: 0777, + ModTime: time.Now(), // TODO: set mode, dates, etc. when added to unixFS }) if err != nil { @@ -103,6 +105,7 @@ func (i *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { Size: int64(pb.GetFilesize()), Typeflag: tar.TypeReg, Mode: 0644, + ModTime: time.Now(), // TODO: set mode, dates, etc. when added to unixFS }) if err != nil { From 65be7279dcbdd4f2130e0b2b11d4503249d35e96 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 26 Jan 2015 17:02:48 -0800 Subject: [PATCH 0674/3817] unixfs/tar: Rename p to gopath This commit was moved from ipfs/go-unixfs@4cfcdc644f2c3dd6eaa97a90e8143e121c0540a0 --- unixfs/tar/reader.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index 193f50333..de4589f94 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -5,7 +5,7 @@ import ( "bytes" "compress/gzip" "io" - p "path" + gopath "path" "strings" "time" @@ -57,7 +57,7 @@ func NewReader(path string, dag mdag.DAGService, resolver *path.Resolver, compre // writeToBuf will write the data to the buffer, and will signal when there // is new data to read - _, filename := p.Split(path) + _, filename := gopath.Split(path) go reader.writeToBuf(dagnode, filename, 0) return reader, nil @@ -95,7 +95,7 @@ func (i *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { i.emitError(err) return } - i.writeToBuf(childNode, p.Join(path, link.Name), depth+1) + i.writeToBuf(childNode, gopath.Join(path, link.Name), depth+1) } return } From 38a22fbe774294d6b63adfcdce18dfeeec5df66a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 25 Jan 2015 08:54:33 +0000 Subject: [PATCH 0675/3817] implement seeking in the dagreader This commit was moved from ipfs/go-merkledag@50433b18f5f1d0570a0cd7f4b336e16a5b223352 --- ipld/merkledag/merkledag.go | 75 +++++++++++++++++++------------- ipld/merkledag/merkledag_test.go | 5 ++- 2 files changed, 47 insertions(+), 33 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 16427c484..1685695df 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -2,7 +2,6 @@ package merkledag import ( - "bytes" "fmt" "sync" "time" @@ -27,6 +26,7 @@ type DAGService interface { // GetDAG returns, in order, all the single leve child // nodes of the passed in node. GetDAG(context.Context, *Node) <-chan *Node + GetNodes(context.Context, []u.Key) <-chan *Node } func NewDAGService(bs *bserv.BlockService) DAGService { @@ -155,11 +155,10 @@ func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} // FindLinks searches this nodes links for the given key, // returns the indexes of any links pointing to it -func FindLinks(n *Node, k u.Key, start int) []int { +func FindLinks(links []u.Key, k u.Key, start int) []int { var out []int - keybytes := []byte(k) - for i, lnk := range n.Links[start:] { - if bytes.Equal([]byte(lnk.Hash), keybytes) { + for i, lnk_k := range links[start:] { + if k == lnk_k { out = append(out, i+start) } } @@ -170,40 +169,54 @@ func FindLinks(n *Node, k u.Key, start int) []int { // It returns a channel of nodes, which the caller can receive // all the child nodes of 'root' on, in proper order. func (ds *dagService) GetDAG(ctx context.Context, root *Node) <-chan *Node { + var keys []u.Key + for _, lnk := range root.Links { + keys = append(keys, u.Key(lnk.Hash)) + } + + return ds.GetNodes(ctx, keys) +} + +func (ds *dagService) GetNodes(ctx context.Context, keys []u.Key) <-chan *Node { sig := make(chan *Node) go func() { defer close(sig) - - var keys []u.Key - for _, lnk := range root.Links { - keys = append(keys, u.Key(lnk.Hash)) - } blkchan := ds.Blocks.GetBlocks(ctx, keys) - nodes := make([]*Node, len(root.Links)) + nodes := make([]*Node, len(keys)) next := 0 - for blk := range blkchan { - nd, err := Decoded(blk.Data) - if err != nil { - // NB: can occur in normal situations, with improperly formatted - // input data - log.Error("Got back bad block!") - break - } - is := FindLinks(root, blk.Key(), next) - for _, i := range is { - nodes[i] = nd - } - - for ; next < len(nodes) && nodes[next] != nil; next++ { - sig <- nodes[next] + for { + select { + case blk, ok := <-blkchan: + if !ok { + if next < len(nodes) { + log.Errorf("Did not receive correct number of nodes!") + } + return + } + nd, err := Decoded(blk.Data) + if err != nil { + // NB: can occur in normal situations, with improperly formatted + // input data + log.Error("Got back bad block!") + break + } + is := FindLinks(keys, blk.Key(), next) + for _, i := range is { + nodes[i] = nd + } + + for ; next < len(nodes) && nodes[next] != nil; next++ { + select { + case sig <- nodes[next]: + case <-ctx.Done(): + return + } + } + case <-ctx.Done(): + return } } - if next < len(nodes) { - // TODO: bubble errors back up. - log.Errorf("Did not receive correct number of nodes!") - } }() - return sig } diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 9dbfad454..b66b085e8 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -8,6 +8,7 @@ import ( "sync" "testing" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" bstore "github.com/jbenet/go-ipfs/blocks/blockstore" @@ -162,7 +163,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { t.Log("finished setup.") - dagr, err := uio.NewDagReader(root, dagservs[0]) + dagr, err := uio.NewDagReader(context.TODO(), root, dagservs[0]) if err != nil { t.Fatal(err) } @@ -195,7 +196,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { } fmt.Println("Got first node back.") - read, err := uio.NewDagReader(first, dagservs[i]) + read, err := uio.NewDagReader(context.TODO(), first, dagservs[i]) if err != nil { t.Fatal(err) } From bc425f7124ca58240f650e166200e762c7065ddf Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 25 Jan 2015 08:54:33 +0000 Subject: [PATCH 0676/3817] implement seeking in the dagreader This commit was moved from ipfs/go-unixfs@5b4f82f25ca34c910ffe2a5cd23533c4fbfb245f --- unixfs/io/dagmodifier_test.go | 9 ++- unixfs/io/dagreader.go | 136 ++++++++++++++++++++++++++++++---- unixfs/tar/reader.go | 3 +- 3 files changed, 127 insertions(+), 21 deletions(-) diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index 43e7fcb08..52ff5853b 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "testing" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" "github.com/jbenet/go-ipfs/blocks/blockstore" bs "github.com/jbenet/go-ipfs/blockservice" @@ -38,7 +39,7 @@ func getNode(t *testing.T, dserv mdag.DAGService, size int64) ([]byte, *mdag.Nod t.Fatal(err) } - dr, err := NewDagReader(node, dserv) + dr, err := NewDagReader(context.TODO(), node, dserv) if err != nil { t.Fatal(err) } @@ -75,7 +76,7 @@ func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) t.Fatal(err) } - rd, err := NewDagReader(nd, dm.dagserv) + rd, err := NewDagReader(context.TODO(), nd, dm.dagserv) if err != nil { t.Fatal(err) } @@ -173,7 +174,7 @@ func TestMultiWrite(t *testing.T) { t.Fatal(err) } - read, err := NewDagReader(nd, dserv) + read, err := NewDagReader(context.TODO(), nd, dserv) if err != nil { t.Fatal(err) } @@ -215,7 +216,7 @@ func TestMultiWriteCoal(t *testing.T) { t.Fatal(err) } - read, err := NewDagReader(nd, dserv) + read, err := NewDagReader(context.TODO(), nd, dserv) if err != nil { t.Fatal(err) } diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index ab28dc8ae..17dbfb41b 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -3,7 +3,10 @@ package io import ( "bytes" "errors" + "fmt" "io" + "io/ioutil" + "os" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -11,6 +14,7 @@ import ( mdag "github.com/jbenet/go-ipfs/merkledag" ft "github.com/jbenet/go-ipfs/unixfs" ftpb "github.com/jbenet/go-ipfs/unixfs/pb" + u "github.com/jbenet/go-ipfs/util" ) var ErrIsDir = errors.New("this dag node is a directory") @@ -19,14 +23,28 @@ var ErrIsDir = errors.New("this dag node is a directory") type DagReader struct { serv mdag.DAGService node *mdag.Node - buf io.Reader + buf ReadSeekCloser fetchChan <-chan *mdag.Node linkPosition int + offset int64 + + // Our context + ctx context.Context + + // Context for children + fctx context.Context + cancel func() +} + +type ReadSeekCloser interface { + io.Reader + io.Seeker + io.Closer } // NewDagReader creates a new reader object that reads the data represented by the given // node, using the passed in DAGService for data retreival -func NewDagReader(n *mdag.Node, serv mdag.DAGService) (io.Reader, error) { +func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (ReadSeekCloser, error) { pb := new(ftpb.Data) err := proto.Unmarshal(n.Data, pb) if err != nil { @@ -38,16 +56,20 @@ func NewDagReader(n *mdag.Node, serv mdag.DAGService) (io.Reader, error) { // Dont allow reading directories return nil, ErrIsDir case ftpb.Data_File: - fetchChan := serv.GetDAG(context.TODO(), n) + fctx, cancel := context.WithCancel(ctx) + fetchChan := serv.GetDAG(fctx, n) return &DagReader{ node: n, serv: serv, - buf: bytes.NewBuffer(pb.GetData()), + buf: NewRSNCFromBytes(pb.GetData()), fetchChan: fetchChan, + ctx: ctx, + fctx: fctx, + cancel: cancel, }, nil case ftpb.Data_Raw: // Raw block will just be a single level, return a byte buffer - return bytes.NewBuffer(pb.GetData()), nil + return NewRSNCFromBytes(pb.GetData()), nil default: return nil, ft.ErrUnrecognizedType } @@ -70,6 +92,8 @@ func (dr *DagReader) precalcNextBuf() error { if !ok { return io.EOF } + case <-dr.ctx.Done(): + return dr.ctx.Err() } pb := new(ftpb.Data) @@ -85,20 +109,37 @@ func (dr *DagReader) precalcNextBuf() error { case ftpb.Data_File: //TODO: this *should* work, needs testing first log.Warning("Running untested code for multilayered indirect FS reads.") - subr, err := NewDagReader(nxt, dr.serv) + subr, err := NewDagReader(dr.fctx, nxt, dr.serv) if err != nil { return err } dr.buf = subr return nil case ftpb.Data_Raw: - dr.buf = bytes.NewBuffer(pb.GetData()) + dr.buf = NewRSNCFromBytes(pb.GetData()) return nil default: return ft.ErrUnrecognizedType } } +func (dr *DagReader) resetBlockFetch(nlinkpos int) { + dr.cancel() + dr.fetchChan = nil + dr.linkPosition = nlinkpos + + var keys []u.Key + for _, lnk := range dr.node.Links[dr.linkPosition:] { + keys = append(keys, u.Key(lnk.Hash)) + } + + fctx, cancel := context.WithCancel(dr.ctx) + dr.cancel = cancel + dr.fctx = fctx + fch := dr.serv.GetNodes(fctx, keys) + dr.fetchChan = fch +} + // Read reads data from the DAG structured file func (dr *DagReader) Read(b []byte) (int, error) { // If no cached buffer, load one @@ -113,6 +154,7 @@ func (dr *DagReader) Read(b []byte) (int, error) { // Attempt to fill bytes from cached buffer n, err := dr.buf.Read(b[total:]) total += n + dr.offset += int64(n) if err != nil { // EOF is expected if err != io.EOF { @@ -133,28 +175,90 @@ func (dr *DagReader) Read(b []byte) (int, error) { } } -/* +func (dr *DagReader) Close() error { + if dr.fctx != nil { + dr.cancel() + } + return nil +} + func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { switch whence { case os.SEEK_SET: - for i := 0; i < len(dr.node.Links); i++ { - nsize := dr.node.Links[i].Size - 8 - if offset > nsize { - offset -= nsize - } else { + if offset < 0 { + return -1, errors.New("Invalid offset") + } + //TODO: this pb should be cached + pb := new(ftpb.Data) + err := proto.Unmarshal(dr.node.Data, pb) + if err != nil { + return -1, err + } + + if offset == 0 { + dr.resetBlockFetch(0) + dr.buf = NewRSNCFromBytes(pb.GetData()) + return 0, nil + } + + left := offset + if int64(len(pb.Data)) > offset { + dr.buf = NewRSNCFromBytes(pb.GetData()[offset:]) + dr.linkPosition = 0 + dr.offset = offset + return offset, nil + } else { + left -= int64(len(pb.Data)) + } + + i := 0 + for ; i < len(pb.Blocksizes); i++ { + if pb.Blocksizes[i] > uint64(left) { break + } else { + left -= int64(pb.Blocksizes[i]) } } - dr.position = i - err := dr.precalcNextBuf() + dr.resetBlockFetch(i) + err = dr.precalcNextBuf() if err != nil { return 0, err } + + n, err := io.CopyN(ioutil.Discard, dr.buf, left) + if err != nil { + fmt.Printf("the copy failed: %s - [%d]\n", err, n) + return -1, err + } + left -= n + if left != 0 { + return -1, errors.New("failed to seek properly") + } + dr.offset = offset + return offset, nil case os.SEEK_CUR: + noffset := dr.offset + offset + return dr.Seek(noffset, os.SEEK_SET) case os.SEEK_END: + pb := new(ftpb.Data) + err := proto.Unmarshal(dr.node.Data, pb) + if err != nil { + return -1, err + } + noffset := int64(pb.GetFilesize()) - offset + return dr.Seek(noffset, os.SEEK_SET) default: return 0, errors.New("invalid whence") } return 0, nil } -*/ + +type readSeekNopCloser struct { + *bytes.Reader +} + +func NewRSNCFromBytes(b []byte) ReadSeekCloser { + return &readSeekNopCloser{bytes.NewReader(b)} +} + +func (r *readSeekNopCloser) Close() error { return nil } diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index de4589f94..65493f11f 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -4,6 +4,7 @@ import ( "archive/tar" "bytes" "compress/gzip" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "io" gopath "path" "strings" @@ -114,7 +115,7 @@ func (i *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { } i.flush() - reader, err := uio.NewDagReader(dagnode, i.dag) + reader, err := uio.NewDagReader(context.TODO(), dagnode, i.dag) if err != nil { i.emitError(err) return From 1d0b7e4e54a69fba3108f1888db5d9a6d4e5d9fa Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 26 Jan 2015 01:32:26 +0000 Subject: [PATCH 0677/3817] refactor and clean up dagreader This commit was moved from ipfs/go-merkledag@b00063cb2b4abf93045113c966c4f428340d18cb --- ipld/merkledag/merkledag.go | 73 ++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 1685695df..b05b309b7 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -25,8 +25,8 @@ type DAGService interface { // GetDAG returns, in order, all the single leve child // nodes of the passed in node. - GetDAG(context.Context, *Node) <-chan *Node - GetNodes(context.Context, []u.Key) <-chan *Node + GetDAG(context.Context, *Node) []NodeGetter + GetNodes(context.Context, []u.Key) []NodeGetter } func NewDAGService(bs *bserv.BlockService) DAGService { @@ -168,7 +168,7 @@ func FindLinks(links []u.Key, k u.Key, start int) []int { // GetDAG will fill out all of the links of the given Node. // It returns a channel of nodes, which the caller can receive // all the child nodes of 'root' on, in proper order. -func (ds *dagService) GetDAG(ctx context.Context, root *Node) <-chan *Node { +func (ds *dagService) GetDAG(ctx context.Context, root *Node) []NodeGetter { var keys []u.Key for _, lnk := range root.Links { keys = append(keys, u.Key(lnk.Hash)) @@ -177,46 +177,69 @@ func (ds *dagService) GetDAG(ctx context.Context, root *Node) <-chan *Node { return ds.GetNodes(ctx, keys) } -func (ds *dagService) GetNodes(ctx context.Context, keys []u.Key) <-chan *Node { - sig := make(chan *Node) +func (ds *dagService) GetNodes(ctx context.Context, keys []u.Key) []NodeGetter { + promises := make([]NodeGetter, len(keys)) + sendChans := make([]chan<- *Node, len(keys)) + for i, _ := range keys { + promises[i], sendChans[i] = newNodePromise(ctx) + } + go func() { - defer close(sig) blkchan := ds.Blocks.GetBlocks(ctx, keys) - nodes := make([]*Node, len(keys)) - next := 0 for { select { case blk, ok := <-blkchan: if !ok { - if next < len(nodes) { - log.Errorf("Did not receive correct number of nodes!") - } return } + nd, err := Decoded(blk.Data) if err != nil { - // NB: can occur in normal situations, with improperly formatted - // input data + // NB: can happen with improperly formatted input data log.Error("Got back bad block!") - break + return } - is := FindLinks(keys, blk.Key(), next) + is := FindLinks(keys, blk.Key(), 0) for _, i := range is { - nodes[i] = nd - } - - for ; next < len(nodes) && nodes[next] != nil; next++ { - select { - case sig <- nodes[next]: - case <-ctx.Done(): - return - } + sendChans[i] <- nd } case <-ctx.Done(): return } } }() - return sig + return promises +} + +func newNodePromise(ctx context.Context) (NodeGetter, chan<- *Node) { + ch := make(chan *Node, 1) + return &nodePromise{ + recv: ch, + ctx: ctx, + }, ch +} + +type nodePromise struct { + cache *Node + recv <-chan *Node + ctx context.Context +} + +type NodeGetter interface { + Get() (*Node, error) +} + +func (np *nodePromise) Get() (*Node, error) { + if np.cache != nil { + return np.cache, nil + } + + select { + case blk := <-np.recv: + np.cache = blk + case <-np.ctx.Done(): + return nil, np.ctx.Err() + } + return np.cache, nil } From d8c9389fc8e0136cdf0a786c47376fff290dcb3b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 26 Jan 2015 01:32:26 +0000 Subject: [PATCH 0678/3817] refactor and clean up dagreader This commit was moved from ipfs/go-unixfs@6f736b95241da7e450d4522ecd95a719c2246fc3 --- unixfs/io/dagreader.go | 110 +++++++++++------------------------------ 1 file changed, 29 insertions(+), 81 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 17dbfb41b..300eb7016 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -3,9 +3,7 @@ package io import ( "bytes" "errors" - "fmt" "io" - "io/ioutil" "os" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -14,7 +12,6 @@ import ( mdag "github.com/jbenet/go-ipfs/merkledag" ft "github.com/jbenet/go-ipfs/unixfs" ftpb "github.com/jbenet/go-ipfs/unixfs/pb" - u "github.com/jbenet/go-ipfs/util" ) var ErrIsDir = errors.New("this dag node is a directory") @@ -23,8 +20,9 @@ var ErrIsDir = errors.New("this dag node is a directory") type DagReader struct { serv mdag.DAGService node *mdag.Node + pbdata *ftpb.Data buf ReadSeekCloser - fetchChan <-chan *mdag.Node + promises []mdag.NodeGetter linkPosition int offset int64 @@ -57,15 +55,15 @@ func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (Read return nil, ErrIsDir case ftpb.Data_File: fctx, cancel := context.WithCancel(ctx) - fetchChan := serv.GetDAG(fctx, n) + promises := serv.GetDAG(fctx, n) return &DagReader{ - node: n, - serv: serv, - buf: NewRSNCFromBytes(pb.GetData()), - fetchChan: fetchChan, - ctx: ctx, - fctx: fctx, - cancel: cancel, + node: n, + serv: serv, + buf: NewRSNCFromBytes(pb.GetData()), + promises: promises, + ctx: fctx, + cancel: cancel, + pbdata: pb, }, nil case ftpb.Data_Raw: // Raw block will just be a single level, return a byte buffer @@ -78,26 +76,18 @@ func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (Read // precalcNextBuf follows the next link in line and loads it from the DAGService, // setting the next buffer to read from func (dr *DagReader) precalcNextBuf() error { - var nxt *mdag.Node - var ok bool - - if dr.fetchChan == nil { - // This panic is appropriate because the select statement - // will not panic if you try and read from a nil channel - // it will simply hang. - panic("fetchChan should NOT be nil") + dr.buf.Close() // Just to make sure + if dr.linkPosition >= len(dr.promises) { + return io.EOF } - select { - case nxt, ok = <-dr.fetchChan: - if !ok { - return io.EOF - } - case <-dr.ctx.Done(): - return dr.ctx.Err() + nxt, err := dr.promises[dr.linkPosition].Get() + if err != nil { + return err } + dr.linkPosition++ pb := new(ftpb.Data) - err := proto.Unmarshal(nxt.Data, pb) + err = proto.Unmarshal(nxt.Data, pb) if err != nil { return err } @@ -107,9 +97,7 @@ func (dr *DagReader) precalcNextBuf() error { // A directory should not exist within a file return ft.ErrInvalidDirLocation case ftpb.Data_File: - //TODO: this *should* work, needs testing first - log.Warning("Running untested code for multilayered indirect FS reads.") - subr, err := NewDagReader(dr.fctx, nxt, dr.serv) + subr, err := NewDagReader(dr.ctx, nxt, dr.serv) if err != nil { return err } @@ -123,32 +111,9 @@ func (dr *DagReader) precalcNextBuf() error { } } -func (dr *DagReader) resetBlockFetch(nlinkpos int) { - dr.cancel() - dr.fetchChan = nil - dr.linkPosition = nlinkpos - - var keys []u.Key - for _, lnk := range dr.node.Links[dr.linkPosition:] { - keys = append(keys, u.Key(lnk.Hash)) - } - - fctx, cancel := context.WithCancel(dr.ctx) - dr.cancel = cancel - dr.fctx = fctx - fch := dr.serv.GetNodes(fctx, keys) - dr.fetchChan = fch -} - // Read reads data from the DAG structured file func (dr *DagReader) Read(b []byte) (int, error) { // If no cached buffer, load one - if dr.buf == nil { - err := dr.precalcNextBuf() - if err != nil { - return 0, err - } - } total := 0 for { // Attempt to fill bytes from cached buffer @@ -176,9 +141,7 @@ func (dr *DagReader) Read(b []byte) (int, error) { } func (dr *DagReader) Close() error { - if dr.fctx != nil { - dr.cancel() - } + dr.cancel() return nil } @@ -188,21 +151,11 @@ func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { if offset < 0 { return -1, errors.New("Invalid offset") } - //TODO: this pb should be cached - pb := new(ftpb.Data) - err := proto.Unmarshal(dr.node.Data, pb) - if err != nil { - return -1, err - } - - if offset == 0 { - dr.resetBlockFetch(0) - dr.buf = NewRSNCFromBytes(pb.GetData()) - return 0, nil - } + pb := dr.pbdata left := offset if int64(len(pb.Data)) > offset { + dr.buf.Close() dr.buf = NewRSNCFromBytes(pb.GetData()[offset:]) dr.linkPosition = 0 dr.offset = offset @@ -211,23 +164,22 @@ func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { left -= int64(len(pb.Data)) } - i := 0 - for ; i < len(pb.Blocksizes); i++ { + for i := 0; i < len(pb.Blocksizes); i++ { if pb.Blocksizes[i] > uint64(left) { + dr.linkPosition = i break } else { left -= int64(pb.Blocksizes[i]) } } - dr.resetBlockFetch(i) - err = dr.precalcNextBuf() + + err := dr.precalcNextBuf() if err != nil { return 0, err } - n, err := io.CopyN(ioutil.Discard, dr.buf, left) + n, err := dr.buf.Seek(left, os.SEEK_SET) if err != nil { - fmt.Printf("the copy failed: %s - [%d]\n", err, n) return -1, err } left -= n @@ -237,15 +189,11 @@ func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { dr.offset = offset return offset, nil case os.SEEK_CUR: + // TODO: be smarter here noffset := dr.offset + offset return dr.Seek(noffset, os.SEEK_SET) case os.SEEK_END: - pb := new(ftpb.Data) - err := proto.Unmarshal(dr.node.Data, pb) - if err != nil { - return -1, err - } - noffset := int64(pb.GetFilesize()) - offset + noffset := int64(dr.pbdata.GetFilesize()) - offset return dr.Seek(noffset, os.SEEK_SET) default: return 0, errors.New("invalid whence") From ef65413f6047121a85b79fa20c1d3c76df83856b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 27 Jan 2015 01:28:41 +0000 Subject: [PATCH 0679/3817] address concerns from PR This commit was moved from ipfs/go-merkledag@ceafd080c1bb511e25d73854bf16892e521813da --- ipld/merkledag/merkledag.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index b05b309b7..5a68b12d2 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -177,6 +177,8 @@ func (ds *dagService) GetDAG(ctx context.Context, root *Node) []NodeGetter { return ds.GetNodes(ctx, keys) } +// GetNodes returns an array of 'NodeGetter' promises, with each corresponding +// to the key with the same index as the passed in keys func (ds *dagService) GetNodes(ctx context.Context, keys []u.Key) []NodeGetter { promises := make([]NodeGetter, len(keys)) sendChans := make([]chan<- *Node, len(keys)) From d1b32ff30b99f13fcb133bfad340b29fe803dcf5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 27 Jan 2015 01:28:41 +0000 Subject: [PATCH 0680/3817] address concerns from PR This commit was moved from ipfs/go-unixfs@7c05bd61c2b26075e7ee351e4ca2328d32d221b6 --- unixfs/io/dagmodifier_test.go | 8 +++---- unixfs/io/dagreader.go | 43 ++++++++++++++++++++++++++++------- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index 52ff5853b..23f2a0afa 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -39,7 +39,7 @@ func getNode(t *testing.T, dserv mdag.DAGService, size int64) ([]byte, *mdag.Nod t.Fatal(err) } - dr, err := NewDagReader(context.TODO(), node, dserv) + dr, err := NewDagReader(context.Background(), node, dserv) if err != nil { t.Fatal(err) } @@ -76,7 +76,7 @@ func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) t.Fatal(err) } - rd, err := NewDagReader(context.TODO(), nd, dm.dagserv) + rd, err := NewDagReader(context.Background(), nd, dm.dagserv) if err != nil { t.Fatal(err) } @@ -174,7 +174,7 @@ func TestMultiWrite(t *testing.T) { t.Fatal(err) } - read, err := NewDagReader(context.TODO(), nd, dserv) + read, err := NewDagReader(context.Background(), nd, dserv) if err != nil { t.Fatal(err) } @@ -216,7 +216,7 @@ func TestMultiWriteCoal(t *testing.T) { t.Fatal(err) } - read, err := NewDagReader(context.TODO(), nd, dserv) + read, err := NewDagReader(context.Background(), nd, dserv) if err != nil { t.Fatal(err) } diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 300eb7016..8d1c87507 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -18,19 +18,31 @@ var ErrIsDir = errors.New("this dag node is a directory") // DagReader provides a way to easily read the data contained in a dag. type DagReader struct { - serv mdag.DAGService - node *mdag.Node - pbdata *ftpb.Data - buf ReadSeekCloser - promises []mdag.NodeGetter + serv mdag.DAGService + + // the node being read + node *mdag.Node + + // cached protobuf structure from node.Data + pbdata *ftpb.Data + + // the current data buffer to be read from + // will either be a bytes.Reader or a child DagReader + buf ReadSeekCloser + + // NodeGetters for each of 'nodes' child links + promises []mdag.NodeGetter + + // the index of the child link currently being read from linkPosition int - offset int64 + + // current offset for the read head within the 'file' + offset int64 // Our context ctx context.Context - // Context for children - fctx context.Context + // context cancel for children cancel func() } @@ -145,6 +157,8 @@ func (dr *DagReader) Close() error { return nil } +// Seek implements io.Seeker, and will seek to a given offset in the file +// interface matches standard unix seek func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { switch whence { case os.SEEK_SET: @@ -152,18 +166,26 @@ func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { return -1, errors.New("Invalid offset") } + // Grab cached protobuf object (solely to make code look cleaner) pb := dr.pbdata + + // left represents the number of bytes remaining to seek to (from beginning) left := offset if int64(len(pb.Data)) > offset { + // Close current buf to close potential child dagreader dr.buf.Close() dr.buf = NewRSNCFromBytes(pb.GetData()[offset:]) + + // start reading links from the beginning dr.linkPosition = 0 dr.offset = offset return offset, nil } else { + // skip past root block data left -= int64(len(pb.Data)) } + // iterate through links and find where we need to be for i := 0; i < len(pb.Blocksizes); i++ { if pb.Blocksizes[i] > uint64(left) { dr.linkPosition = i @@ -173,15 +195,19 @@ func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { } } + // start sub-block request err := dr.precalcNextBuf() if err != nil { return 0, err } + // set proper offset within child readseeker n, err := dr.buf.Seek(left, os.SEEK_SET) if err != nil { return -1, err } + + // sanity left -= n if left != 0 { return -1, errors.New("failed to seek properly") @@ -201,6 +227,7 @@ func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { return 0, nil } +// readSeekNopCloser wraps a bytes.Reader to implement ReadSeekCloser type readSeekNopCloser struct { *bytes.Reader } From fabb9a99e0ffe4f03a5d0a9cf6c54419fdded45f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 26 Jan 2015 19:12:12 -0800 Subject: [PATCH 0681/3817] dropped down log.Errors This commit was moved from ipfs/go-unixfs@6e12a49162f6bae7dabd39b0640aa5e2a730f569 --- unixfs/io/dagmodifier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/io/dagmodifier.go b/unixfs/io/dagmodifier.go index 4683a157e..e155d8b38 100644 --- a/unixfs/io/dagmodifier.go +++ b/unixfs/io/dagmodifier.go @@ -147,7 +147,7 @@ func (dm *DagModifier) WriteAt(b []byte, offset uint64) (int, error) { n := &mdag.Node{Data: ft.WrapData(sb)} _, err := dm.dagserv.Add(n) if err != nil { - log.Errorf("Failed adding node to DAG service: %s", err) + log.Warningf("Failed adding node to DAG service: %s", err) return 0, err } lnk, err := mdag.MakeLink(n) From 68cf821460d524f846122c7e5fb0644bc4bdad60 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 26 Jan 2015 19:12:12 -0800 Subject: [PATCH 0682/3817] dropped down log.Errors This commit was moved from ipfs/go-ipfs-routing@de9e2da32d77d2b3c8ff5aedae4493954582a572 --- routing/dht/dht.go | 12 ++++++------ routing/dht/dht_bootstrap.go | 4 ++-- routing/dht/dht_net.go | 8 ++++---- routing/dht/dht_test.go | 2 +- routing/dht/handlers.go | 10 +++++----- routing/dht/lookup.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/records.go | 2 +- routing/dht/routing.go | 14 +++++++------- routing/kbucket/table.go | 2 +- routing/record/validation.go | 6 +++--- 11 files changed, 32 insertions(+), 32 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index e66517072..42e0cd7b7 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -151,7 +151,7 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, key string) erro // // only share WAN-friendly addresses ?? // pi.Addrs = addrutil.WANShareableAddrs(pi.Addrs) if len(pi.Addrs) < 1 { - log.Errorf("%s putProvider: %s for %s error: no wan-friendly addresses", dht.self, p, u.Key(key), pi.Addrs) + log.Infof("%s putProvider: %s for %s error: no wan-friendly addresses", dht.self, p, u.Key(key), pi.Addrs) return fmt.Errorf("no known addresses for self. cannot put provider.") } @@ -185,7 +185,7 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.ID, // make sure record is valid. err = dht.verifyRecordOnline(ctx, record) if err != nil { - log.Error("Received invalid record!") + log.Info("Received invalid record! (discarded)") return nil, nil, err } return record.GetValue(), nil, nil @@ -235,7 +235,7 @@ func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { if u.Debug { err = dht.verifyRecordLocally(rec) if err != nil { - log.Errorf("local record verify failed: %s", err) + log.Debugf("local record verify failed: %s (discarded)", err) return nil, err } } @@ -248,7 +248,7 @@ func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { func (dht *IpfsDHT) getOwnPrivateKey() (ci.PrivKey, error) { sk := dht.peerstore.PrivKey(dht.self) if sk == nil { - log.Errorf("%s dht cannot get own private key!", dht.self) + log.Warningf("%s dht cannot get own private key!", dht.self) return nil, fmt.Errorf("cannot get private key to sign record!") } return sk, nil @@ -323,7 +323,7 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, p peer.ID, count int) [ // == to self? thats bad for _, p := range closer { if p == dht.self { - log.Error("Attempted to return self! this shouldnt happen...") + log.Debug("Attempted to return self! this shouldnt happen...") return nil } } @@ -370,7 +370,7 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { ctx, _ := context.WithTimeout(dht.Context(), time.Second*5) _, err := dht.Ping(ctx, p) if err != nil { - log.Errorf("Ping error: %s", err) + log.Debugf("Ping error: %s", err) } } case <-dht.Closing(): diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index c91df05e5..f2cc50f9a 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -76,7 +76,7 @@ func (dht *IpfsDHT) BootstrapOnSignal(cfg BootstrapConfig, signal <-chan time.Ti ctx := dht.Context() if err := dht.runBootstrap(ctx, cfg); err != nil { - log.Error(err) + log.Warning(err) // A bootstrapping error is important to notice but not fatal. } }) @@ -117,7 +117,7 @@ func (dht *IpfsDHT) runBootstrap(ctx context.Context, cfg BootstrapConfig) error // woah, actually found a peer with that ID? this shouldn't happen normally // (as the ID we use is not a real ID). this is an odd error worth logging. err := fmt.Errorf("Bootstrap peer error: Actually FOUND peer. (%s, %s)", id, p) - log.Errorf("%s", err) + log.Warningf("%s", err) merr = append(merr, err) } } diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index c8b6b66eb..15b0b63d8 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -31,7 +31,7 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { // receive msg pmes := new(pb.Message) if err := r.ReadMsg(pmes); err != nil { - log.Errorf("Error unmarshaling data: %s", err) + log.Debugf("Error unmarshaling data: %s", err) return } @@ -41,14 +41,14 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { // get handler for this msg type. handler := dht.handlerForMsgType(pmes.GetType()) if handler == nil { - log.Error("got back nil handler from handlerForMsgType") + log.Debug("got back nil handler from handlerForMsgType") return } // dispatch handler. rpmes, err := handler(ctx, mPeer, pmes) if err != nil { - log.Errorf("handle message error: %s", err) + log.Debugf("handle message error: %s", err) return } @@ -60,7 +60,7 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { // send out response msg if err := w.WriteMsg(rpmes); err != nil { - log.Errorf("send response error: %s", err) + log.Debugf("send response error: %s", err) return } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 2d8494b65..00597b016 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -262,7 +262,7 @@ func waitForWellFormedTables(t *testing.T, dhts []*IpfsDHT, minPeers, avgPeers i for { select { case <-timeoutA: - log.Error("did not reach well-formed routing tables by %s", timeout) + log.Errorf("did not reach well-formed routing tables by %s", timeout) return false // failed case <-time.After(5 * time.Millisecond): if checkTables() { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 28df3aea6..6376dbcba 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -78,7 +78,7 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Mess rec := new(pb.Record) err := proto.Unmarshal(byts, rec) if err != nil { - log.Error("Failed to unmarshal dht record from datastore") + log.Debug("Failed to unmarshal dht record from datastore") return nil, err } @@ -119,7 +119,7 @@ func (dht *IpfsDHT) handlePutValue(ctx context.Context, p peer.ID, pmes *pb.Mess dskey := u.Key(pmes.GetKey()).DsKey() if err := dht.verifyRecordLocally(pmes.GetRecord()); err != nil { - log.Errorf("Bad dht record in PUT from: %s. %s", u.Key(pmes.GetRecord().GetAuthor()), err) + log.Debugf("Bad dht record in PUT from: %s. %s", u.Key(pmes.GetRecord().GetAuthor()), err) return nil, err } @@ -181,7 +181,7 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb. // check if we have this value, to add ourselves as provider. has, err := dht.datastore.Has(key.DsKey()) if err != nil && err != ds.ErrNotFound { - log.Errorf("unexpected datastore error: %v\n", err) + log.Debugf("unexpected datastore error: %v\n", err) has = false } @@ -226,12 +226,12 @@ func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.M if pi.ID != p { // we should ignore this provider reccord! not from originator. // (we chould sign them and check signature later...) - log.Errorf("handleAddProvider received provider %s from %s. Ignore.", pi.ID, p) + log.Debugf("handleAddProvider received provider %s from %s. Ignore.", pi.ID, p) continue } if len(pi.Addrs) < 1 { - log.Errorf("%s got no valid addresses for provider %s. Ignore.", dht.self, p) + log.Debugf("%s got no valid addresses for provider %s. Ignore.", dht.self, p) continue } diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index ea1552ba7..6e0acd9fa 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -51,7 +51,7 @@ func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key u.Key) (<-chan peer closer, err := dht.closerPeersSingle(ctx, key, p) if err != nil { - log.Errorf("error getting closer peers: %s", err) + log.Debugf("error getting closer peers: %s", err) return nil, err } diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 680234102..efa72f6ee 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -84,7 +84,7 @@ func (m *Message_Peer) Addresses() []ma.Multiaddr { for i, addr := range m.Addrs { maddrs[i], err = ma.NewMultiaddrBytes(addr) if err != nil { - log.Error("error decoding Multiaddr for peer: %s", m.GetId()) + log.Debugf("error decoding Multiaddr for peer: %s", m.GetId()) continue } } diff --git a/routing/dht/records.go b/routing/dht/records.go index 14d73b6c6..0bc701153 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -46,7 +46,7 @@ func (dht *IpfsDHT) getPublicKeyOnline(ctx context.Context, p peer.ID) (ci.PubKe pk, err = ci.UnmarshalPublicKey(val) if err != nil { - log.Errorf("Failed to unmarshal public key: %s", err) + log.Debugf("Failed to unmarshal public key: %s", err) return nil, err } return pk, nil diff --git a/routing/dht/routing.go b/routing/dht/routing.go index e04803ed2..ade41b82e 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -45,7 +45,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error rec, err := record.MakePutRecord(sk, key, value) if err != nil { - log.Error("Creation of record failed!") + log.Debug("Creation of record failed!") return err } @@ -61,7 +61,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error defer wg.Done() err := dht.putValueToPeer(ctx, p, key, rec) if err != nil { - log.Errorf("failed putting value to peer: %s", err) + log.Debugf("failed putting value to peer: %s", err) } }(p) } @@ -142,7 +142,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { log.Debugf("putProvider(%s, %s)", key, p) err := dht.putProvider(ctx, p, string(key)) if err != nil { - log.Error(err) + log.Debug(err) } }(p) } @@ -214,7 +214,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co select { case peerOut <- prov: case <-ctx.Done(): - log.Error("Context timed out sending more providers") + log.Debug("Context timed out sending more providers") return nil, ctx.Err() } } @@ -240,7 +240,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co peers := dht.routingTable.ListPeers() _, err := query.Run(ctx, peers) if err != nil { - log.Errorf("Query error: %s", err) + log.Debugf("Query error: %s", err) notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ Type: notif.QueryError, Extra: err.Error(), @@ -265,7 +265,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, er // Sanity... for _, p := range peers { if p == id { - log.Error("Found target peer in list of closest peers...") + log.Debug("Found target peer in list of closest peers...") return dht.peerstore.PeerInfo(p), nil } } @@ -370,7 +370,7 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< // this does no error checking go func() { if _, err := query.Run(ctx, peers); err != nil { - log.Error(err) + log.Debug(err) } // close the peerchan channel when done. diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 8bc825f95..dc5fb3d6f 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -135,7 +135,7 @@ func (rt *RoutingTable) NearestPeer(id ID) peer.ID { return peers[0] } - log.Errorf("NearestPeer: Returning nil, table size = %d", rt.Size()) + log.Debugf("NearestPeer: Returning nil, table size = %d", rt.Size()) return "" } diff --git a/routing/record/validation.go b/routing/record/validation.go index bd0913525..9519ebd3f 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -34,11 +34,11 @@ func (v Validator) VerifyRecord(r *pb.Record, pk ci.PubKey) error { blob := RecordBlobForSig(r) ok, err := pk.Verify(blob, r.GetSignature()) if err != nil { - log.Error("Signature verify failed.") + log.Info("Signature verify failed. (ignored)") return err } if !ok { - log.Error("dht found a forged record! (ignored)") + log.Info("dht found a forged record! (ignored)") return ErrBadRecord } @@ -51,7 +51,7 @@ func (v Validator) VerifyRecord(r *pb.Record, pk ci.PubKey) error { fnc, ok := v[parts[1]] if !ok { - log.Errorf("Unrecognized key prefix: %s", parts[1]) + log.Infof("Unrecognized key prefix: %s", parts[1]) return ErrInvalidRecordType } From 50d238f19240e20d445d774d965583d718e67305 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 27 Jan 2015 03:29:10 +0000 Subject: [PATCH 0683/3817] comment NodeGetter This commit was moved from ipfs/go-merkledag@5f130e41defd189a5bbd313cb6ad7311711d7f15 --- ipld/merkledag/merkledag.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 5a68b12d2..e07ff2c35 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -228,6 +228,10 @@ type nodePromise struct { ctx context.Context } +// NodeGetter provides a promise like interface for a dag Node +// the first call to Get will block until the Node is received +// from its internal channels, subsequent calls will return the +// cached node. type NodeGetter interface { Get() (*Node, error) } From f99b907949b0019b221c1496aed50e80d3000017 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 27 Jan 2015 07:41:51 +0000 Subject: [PATCH 0684/3817] off by one error seeking to end of single block file This commit was moved from ipfs/go-unixfs@f0f00316759829fd97a3f5a8a7e7a8bea79471d4 --- unixfs/io/dagreader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 8d1c87507..15e1b6f6e 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -171,7 +171,7 @@ func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { // left represents the number of bytes remaining to seek to (from beginning) left := offset - if int64(len(pb.Data)) > offset { + if int64(len(pb.Data)) >= offset { // Close current buf to close potential child dagreader dr.buf.Close() dr.buf = NewRSNCFromBytes(pb.GetData()[offset:]) From a71018f40d38747f1fe0ad6c1eac38827cb1b5eb Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 26 Jan 2015 20:48:00 -0500 Subject: [PATCH 0685/3817] feat(routing.grandcentral): skeleton fixes breakages: - peer.Peer -> peer.ID - peer.X -> peer.PeerInfo - netmsg -> p2p streams This commit was moved from ipfs/go-ipfs-routing@c46e89cc2fffc619758573809ab07b9a18cc59ef --- routing/grandcentral/client.go | 121 +++++++++++++++++++++ routing/grandcentral/proxy/loopback.go | 53 ++++++++++ routing/grandcentral/proxy/standard.go | 73 +++++++++++++ routing/grandcentral/server.go | 140 +++++++++++++++++++++++++ 4 files changed, 387 insertions(+) create mode 100644 routing/grandcentral/client.go create mode 100644 routing/grandcentral/proxy/loopback.go create mode 100644 routing/grandcentral/proxy/standard.go create mode 100644 routing/grandcentral/server.go diff --git a/routing/grandcentral/client.go b/routing/grandcentral/client.go new file mode 100644 index 000000000..ccf0bd199 --- /dev/null +++ b/routing/grandcentral/client.go @@ -0,0 +1,121 @@ +package grandcentral + +import ( + "bytes" + "time" + + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + inet "github.com/jbenet/go-ipfs/p2p/net" + peer "github.com/jbenet/go-ipfs/p2p/peer" + routing "github.com/jbenet/go-ipfs/routing" + pb "github.com/jbenet/go-ipfs/routing/dht/pb" + proxy "github.com/jbenet/go-ipfs/routing/grandcentral/proxy" + eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" + u "github.com/jbenet/go-ipfs/util" + errors "github.com/jbenet/go-ipfs/util/debugerror" +) + +var log = eventlog.Logger("grandcentral") + +var ErrTODO = errors.New("TODO") + +type Client struct { + peerstore peer.Peerstore + proxy proxy.Proxy + dialer inet.Network + local peer.ID +} + +// TODO take in datastore/cache +func NewClient(d inet.Network, px proxy.Proxy, ps peer.Peerstore, local peer.ID) (*Client, error) { + return &Client{ + dialer: d, + proxy: px, + local: local, + peerstore: ps, + }, nil +} + +func (c *Client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-chan peer.PeerInfo { + ch := make(chan peer.PeerInfo) + go func() { + defer close(ch) + request := pb.NewMessage(pb.Message_GET_PROVIDERS, string(k), 0) + response, err := c.proxy.SendRequest(ctx, request) + if err != nil { + log.Error(errors.Wrap(err)) + return + } + for _, p := range pb.PBPeersToPeerInfos(response.GetProviderPeers()) { + select { + case <-ctx.Done(): + log.Error(errors.Wrap(ctx.Err())) + return + case ch <- p: + } + } + }() + return ch +} + +func (c *Client) PutValue(ctx context.Context, k u.Key, v []byte) error { + r, err := makeRecord(c.peerstore, c.local, k, v) + if err != nil { + return err + } + pmes := pb.NewMessage(pb.Message_PUT_VALUE, string(k), 0) + pmes.Record = r + return c.proxy.SendMessage(ctx, pmes) // wrap to hide the remote +} + +func (c *Client) GetValue(ctx context.Context, k u.Key) ([]byte, error) { + msg := pb.NewMessage(pb.Message_GET_VALUE, string(k), 0) + response, err := c.proxy.SendRequest(ctx, msg) // TODO wrap to hide the remote + if err != nil { + return nil, errors.Wrap(err) + } + return response.Record.GetValue(), nil +} + +func (c *Client) Provide(ctx context.Context, k u.Key) error { + msg := pb.NewMessage(pb.Message_ADD_PROVIDER, string(k), 0) + // TODO wrap this to hide the dialer and the local/remote peers + msg.ProviderPeers = pb.PeerInfosToPBPeers(c.dialer, []peer.PeerInfo{peer.PeerInfo{ID: c.local}}) // FIXME how is connectedness defined for the local node + return c.proxy.SendMessage(ctx, msg) // TODO wrap to hide remote +} + +func (c *Client) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, error) { + request := pb.NewMessage(pb.Message_FIND_NODE, string(id), 0) + response, err := c.proxy.SendRequest(ctx, request) // hide remote + if err != nil { + return peer.PeerInfo{}, errors.Wrap(err) + } + for _, p := range pb.PBPeersToPeerInfos(response.GetCloserPeers()) { + if p.ID == id { + return p, nil + } + } + return peer.PeerInfo{}, errors.New("could not find peer") +} + +// creates and signs a record for the given key/value pair +func makeRecord(ps peer.Peerstore, p peer.ID, k u.Key, v []byte) (*pb.Record, error) { + blob := bytes.Join([][]byte{[]byte(k), v, []byte(p)}, []byte{}) + sig, err := ps.PrivKey(p).Sign(blob) + if err != nil { + return nil, err + } + return &pb.Record{ + Key: proto.String(string(k)), + Value: v, + Author: proto.String(string(p)), + Signature: sig, + }, nil +} + +func (c *Client) Ping(ctx context.Context, id peer.ID) (time.Duration, error) { + return time.Nanosecond, errors.New("grandcentral routing does not support the ping method") +} + +var _ routing.IpfsRouting = &Client{} diff --git a/routing/grandcentral/proxy/loopback.go b/routing/grandcentral/proxy/loopback.go new file mode 100644 index 000000000..59a414df0 --- /dev/null +++ b/routing/grandcentral/proxy/loopback.go @@ -0,0 +1,53 @@ +package proxy + +import ( + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + inet "github.com/jbenet/go-ipfs/p2p/net" + peer "github.com/jbenet/go-ipfs/p2p/peer" + ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" + dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" + errors "github.com/jbenet/go-ipfs/util/debugerror" +) + +// RequestHandler handles routing requests locally +type RequestHandler interface { + HandleRequest(ctx context.Context, p peer.ID, m *dhtpb.Message) *dhtpb.Message +} + +// Loopback forwards requests to a local handler +type Loopback struct { + Handler RequestHandler + Local peer.ID +} + +// SendMessage intercepts local requests, forwarding them to a local handler +func (lb *Loopback) SendMessage(ctx context.Context, m *dhtpb.Message) error { + response := lb.Handler.HandleRequest(ctx, lb.Local, m) + if response != nil { + log.Warning("loopback handler returned unexpected message") + } + return nil +} + +// SendRequest intercepts local requests, forwarding them to a local handler +func (lb *Loopback) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) { + return lb.Handler.HandleRequest(ctx, lb.Local, m), nil +} + +func (lb *Loopback) handleNewStream(s inet.Stream) { + defer s.Close() + pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) + var incoming dhtpb.Message + if err := pbr.ReadMsg(&incoming); err != nil { + log.Error(errors.Wrap(err)) + return + } + ctx := context.TODO() + outgoing := lb.Handler.HandleRequest(ctx, s.Conn().RemotePeer(), &incoming) + + pbw := ggio.NewDelimitedWriter(s) + + if err := pbw.WriteMsg(outgoing); err != nil { + return // TODO logerr + } +} diff --git a/routing/grandcentral/proxy/standard.go b/routing/grandcentral/proxy/standard.go new file mode 100644 index 000000000..629f61916 --- /dev/null +++ b/routing/grandcentral/proxy/standard.go @@ -0,0 +1,73 @@ +package proxy + +import ( + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + host "github.com/jbenet/go-ipfs/p2p/host" + ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" + inet "github.com/jbenet/go-ipfs/p2p/net" + peer "github.com/jbenet/go-ipfs/p2p/peer" + dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" + errors "github.com/jbenet/go-ipfs/util/debugerror" + eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" +) + +var log = eventlog.Logger("proxy") + +type Proxy interface { + SendMessage(ctx context.Context, m *dhtpb.Message) error + SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) +} + +type standard struct { + Host host.Host + Remote peer.ID +} + +func Standard(h host.Host, remote peer.ID) Proxy { + return &standard{h, remote} +} + +const ProtocolGCR = "/ipfs/grandcentral" + +func (px *standard) SendMessage(ctx context.Context, m *dhtpb.Message) error { + if err := px.Host.Connect(ctx, peer.PeerInfo{ID: px.Remote}); err != nil { + return err + } + s, err := px.Host.NewStream(ProtocolGCR, px.Remote) + if err != nil { + return err + } + defer s.Close() + pbw := ggio.NewDelimitedWriter(s) + if err := pbw.WriteMsg(m); err != nil { + return errors.Wrap(err) + } + return nil +} + +func (px *standard) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) { + if err := px.Host.Connect(ctx, peer.PeerInfo{ID: px.Remote}); err != nil { + return nil, err + } + s, err := px.Host.NewStream(ProtocolGCR, px.Remote) + if err != nil { + return nil, err + } + defer s.Close() + r := ggio.NewDelimitedReader(s, inet.MessageSizeMax) + w := ggio.NewDelimitedWriter(s) + if err := w.WriteMsg(m); err != nil { + return nil, err + } + + var reply dhtpb.Message + if err := r.ReadMsg(&reply); err != nil { + return nil, err + } + // need ctx expiration? + if &reply == nil { + return nil, errors.New("no response to request") + } + return &reply, nil +} + diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go new file mode 100644 index 000000000..f51b71917 --- /dev/null +++ b/routing/grandcentral/server.go @@ -0,0 +1,140 @@ +package grandcentral + +import ( + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + datastore "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + inet "github.com/jbenet/go-ipfs/p2p/net" + peer "github.com/jbenet/go-ipfs/p2p/peer" + dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" + proxy "github.com/jbenet/go-ipfs/routing/grandcentral/proxy" + util "github.com/jbenet/go-ipfs/util" + errors "github.com/jbenet/go-ipfs/util/debugerror" +) + +// Server handles routing queries using a database backend +type Server struct { + local peer.ID + datastore datastore.ThreadSafeDatastore + dialer inet.Network + peerstore peer.Peerstore + *proxy.Loopback // so server can be injected into client +} + +// NewServer creates a new GrandCentral routing Server +func NewServer(ds datastore.ThreadSafeDatastore, d inet.Network, ps peer.Peerstore, local peer.ID) (*Server, error) { + s := &Server{local, ds, d, ps, nil} + s.Loopback = &proxy.Loopback{ + Handler: s, + Local: local, + } + return s, nil +} + +// HandleLocalRequest implements the proxy.RequestHandler interface. This is +// where requests are received from the outside world. +func (s *Server) HandleRequest(ctx context.Context, p peer.ID, req *dhtpb.Message) *dhtpb.Message { + _, response := s.handleMessage(ctx, p, req) // ignore response peer. it's local. + return response +} + +// TODO extract backend. backend can be implemented with whatever database we desire +func (s *Server) handleMessage( + ctx context.Context, p peer.ID, req *dhtpb.Message) (peer.ID, *dhtpb.Message) { + + // FIXME threw everything into this switch statement to get things going. + // Once each operation is well-defined, extract pluggable backend so any + // database may be used. + + var response = dhtpb.NewMessage(req.GetType(), req.GetKey(), req.GetClusterLevel()) + switch req.GetType() { + + case dhtpb.Message_GET_VALUE: + dskey := util.Key(req.GetKey()).DsKey() + val, err := s.datastore.Get(dskey) + if err != nil { + log.Error(errors.Wrap(err)) + return "", nil + } + rawRecord, ok := val.([]byte) + if !ok { + log.Errorf("datastore had non byte-slice value for %v", dskey) + return "", nil + } + if err := proto.Unmarshal(rawRecord, response.Record); err != nil { + log.Error("failed to unmarshal dht record from datastore") + return "", nil + } + // TODO before merging: if we know any providers for the requested value, return those. + return p, response + + case dhtpb.Message_PUT_VALUE: + // TODO before merging: verifyRecord(req.GetRecord()) + data, err := proto.Marshal(req.GetRecord()) + if err != nil { + log.Error(err) + return "", nil + } + dskey := util.Key(req.GetKey()).DsKey() + if err := s.datastore.Put(dskey, data); err != nil { + log.Error(err) + return "", nil + } + return p, req // TODO before merging: verify that we should return record + + case dhtpb.Message_FIND_NODE: + p := s.peerstore.PeerInfo(peer.ID(req.GetKey())) + response.CloserPeers = dhtpb.PeerInfosToPBPeers(s.dialer, []peer.PeerInfo{p}) + return p.ID, response + + case dhtpb.Message_ADD_PROVIDER: + for _, provider := range req.GetProviderPeers() { + providerID := peer.ID(provider.GetId()) + if providerID != p { + log.Errorf("provider message came from third-party %s", p) + continue + } + for _, maddr := range provider.Addresses() { + // FIXME do we actually want to store to peerstore + s.peerstore.AddAddress(p, maddr) + } + } + var providers []dhtpb.Message_Peer + pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", req.GetKey()}) + if v, err := s.datastore.Get(pkey); err == nil { + if protopeers, ok := v.([]dhtpb.Message_Peer); ok { + providers = append(providers, protopeers...) + } + } + if err := s.datastore.Put(pkey, providers); err != nil { + log.Error(err) + return "", nil + } + return "", nil + + case dhtpb.Message_GET_PROVIDERS: + dskey := util.Key(req.GetKey()).DsKey() + exists, err := s.datastore.Has(dskey) + if err == nil && exists { + response.ProviderPeers = append(response.ProviderPeers, dhtpb.PeerInfosToPBPeers(s.dialer, []peer.PeerInfo{peer.PeerInfo{ID: s.local}})...) + } + // FIXME(btc) is this how we want to persist this data? + pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", req.GetKey()}) + if v, err := s.datastore.Get(pkey); err == nil { + if protopeers, ok := v.([]dhtpb.Message_Peer); ok { + for _, p := range protopeers { + response.ProviderPeers = append(response.ProviderPeers, &p) + } + } + } + return p, response + + case dhtpb.Message_PING: + return p, req + default: + } + return "", nil +} + +var _ proxy.RequestHandler = &Server{} +var _ proxy.Proxy = &Server{} From 141e8034753159f34fc04e7f032eb4eb443411a0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 26 Jan 2015 20:13:11 +0000 Subject: [PATCH 0686/3817] change ipns resolve/publish to store raw keys, not b58 encoded This commit was moved from ipfs/go-namesys@3173118e99cb0332d57ddd3df403e3632284916f --- namesys/dns.go | 7 +++++-- namesys/interface.go | 6 ++++-- namesys/namesys.go | 10 ++++++---- namesys/proquint.go | 6 ++++-- namesys/publisher.go | 11 +++++------ namesys/resolve_test.go | 9 +++++---- namesys/routing.go | 5 ++--- 7 files changed, 31 insertions(+), 23 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 881979930..2fb477930 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -3,9 +3,12 @@ package namesys import ( "net" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" isd "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-is-domain" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + + u "github.com/jbenet/go-ipfs/util" ) // DNSResolver implements a Resolver on DNS domains @@ -22,7 +25,7 @@ func (r *DNSResolver) CanResolve(name string) bool { // Resolve implements Resolver // TXT records for a given domain name should contain a b58 // encoded multihash. -func (r *DNSResolver) Resolve(name string) (string, error) { +func (r *DNSResolver) Resolve(ctx context.Context, name string) (u.Key, error) { log.Info("DNSResolver resolving %v", name) txt, err := net.LookupTXT(name) if err != nil { @@ -39,7 +42,7 @@ func (r *DNSResolver) Resolve(name string) (string, error) { if err != nil { continue } - return t, nil + return u.Key(chk), nil } return "", ErrResolveFailed diff --git a/namesys/interface.go b/namesys/interface.go index c2e39afec..f2e4f104d 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -3,7 +3,9 @@ package namesys import ( "errors" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ci "github.com/jbenet/go-ipfs/p2p/crypto" + u "github.com/jbenet/go-ipfs/util" ) // ErrResolveFailed signals an error when attempting to resolve. @@ -28,7 +30,7 @@ type NameSystem interface { type Resolver interface { // Resolve looks up a name, and returns the value previously published. - Resolve(name string) (value string, err error) + Resolve(ctx context.Context, name string) (value u.Key, err error) // CanResolve checks whether this Resolver can resolve a name CanResolve(name string) bool @@ -39,5 +41,5 @@ type Publisher interface { // Publish establishes a name-value mapping. // TODO make this not PrivKey specific. - Publish(name ci.PrivKey, value string) error + Publish(ctx context.Context, name ci.PrivKey, value u.Key) error } diff --git a/namesys/namesys.go b/namesys/namesys.go index cc11d9ddc..eddae747d 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -1,8 +1,10 @@ package namesys import ( + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ci "github.com/jbenet/go-ipfs/p2p/crypto" routing "github.com/jbenet/go-ipfs/routing" + u "github.com/jbenet/go-ipfs/util" ) // ipnsNameSystem implements IPNS naming. @@ -32,10 +34,10 @@ func NewNameSystem(r routing.IpfsRouting) NameSystem { } // Resolve implements Resolver -func (ns *ipns) Resolve(name string) (string, error) { +func (ns *ipns) Resolve(ctx context.Context, name string) (u.Key, error) { for _, r := range ns.resolvers { if r.CanResolve(name) { - return r.Resolve(name) + return r.Resolve(ctx, name) } } return "", ErrResolveFailed @@ -52,6 +54,6 @@ func (ns *ipns) CanResolve(name string) bool { } // Publish implements Publisher -func (ns *ipns) Publish(name ci.PrivKey, value string) error { - return ns.publisher.Publish(name, value) +func (ns *ipns) Publish(ctx context.Context, name ci.PrivKey, value u.Key) error { + return ns.publisher.Publish(ctx, name, value) } diff --git a/namesys/proquint.go b/namesys/proquint.go index 89bbc4a44..24e97529d 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -3,7 +3,9 @@ package namesys import ( "errors" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" proquint "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/bren2010/proquint" + u "github.com/jbenet/go-ipfs/util" ) type ProquintResolver struct{} @@ -15,10 +17,10 @@ func (r *ProquintResolver) CanResolve(name string) bool { } // Resolve implements Resolver. Decodes the proquint string. -func (r *ProquintResolver) Resolve(name string) (string, error) { +func (r *ProquintResolver) Resolve(ctx context.Context, name string) (u.Key, error) { ok := r.CanResolve(name) if !ok { return "", errors.New("not a valid proquint string") } - return string(proquint.Decode(name)), nil + return u.Key(proquint.Decode(name)), nil } diff --git a/namesys/publisher.go b/namesys/publisher.go index 75cccf9e4..8a82c7947 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -37,17 +37,16 @@ func NewRoutingPublisher(route routing.IpfsRouting) Publisher { // Publish implements Publisher. Accepts a keypair and a value, // and publishes it out to the routing system -func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { +func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value u.Key) error { log.Debugf("namesys: Publish %s", value) // validate `value` is a ref (multihash) - _, err := mh.FromB58String(value) + _, err := mh.FromB58String(value.Pretty()) if err != nil { log.Errorf("hash cast failed: %s", value) return fmt.Errorf("publish value must be str multihash. %v", err) } - ctx := context.TODO() data, err := createRoutingEntryData(k, value) if err != nil { log.Error("entry creation failed.") @@ -65,7 +64,7 @@ func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { log.Debugf("Storing pubkey at: %s", namekey) // Store associated public key - timectx, _ := context.WithDeadline(ctx, time.Now().Add(time.Second*4)) + timectx, _ := context.WithDeadline(ctx, time.Now().Add(time.Second*10)) err = p.routing.PutValue(timectx, namekey, pkbytes) if err != nil { return err @@ -75,7 +74,7 @@ func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { log.Debugf("Storing ipns entry at: %s", ipnskey) // Store ipns entry at "/ipns/"+b58(h(pubkey)) - timectx, _ = context.WithDeadline(ctx, time.Now().Add(time.Second*4)) + timectx, _ = context.WithDeadline(ctx, time.Now().Add(time.Second*10)) err = p.routing.PutValue(timectx, ipnskey, data) if err != nil { return err @@ -84,7 +83,7 @@ func (p *ipnsPublisher) Publish(k ci.PrivKey, value string) error { return nil } -func createRoutingEntryData(pk ci.PrivKey, val string) ([]byte, error) { +func createRoutingEntryData(pk ci.PrivKey, val u.Key) ([]byte, error) { entry := new(pb.IpnsEntry) entry.Value = []byte(val) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 8e3214dfe..e349a0aa9 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -1,6 +1,7 @@ package namesys import ( + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "testing" mockrouting "github.com/jbenet/go-ipfs/routing/mock" @@ -19,13 +20,13 @@ func TestRoutingResolve(t *testing.T) { t.Fatal(err) } - err = publisher.Publish(privk, "Hello") + err = publisher.Publish(context.Background(), privk, "Hello") if err == nil { t.Fatal("should have errored out when publishing a non-multihash val") } - h := u.Key(u.Hash([]byte("Hello"))).Pretty() - err = publisher.Publish(privk, h) + h := u.Key(u.Hash([]byte("Hello"))) + err = publisher.Publish(context.Background(), privk, h) if err != nil { t.Fatal(err) } @@ -36,7 +37,7 @@ func TestRoutingResolve(t *testing.T) { } pkhash := u.Hash(pubkb) - res, err := resolver.Resolve(u.Key(pkhash).Pretty()) + res, err := resolver.Resolve(context.Background(), u.Key(pkhash).Pretty()) if err != nil { t.Fatal(err) } diff --git a/namesys/routing.go b/namesys/routing.go index b57d2c601..a66565db6 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -38,9 +38,8 @@ func (r *routingResolver) CanResolve(name string) bool { // Resolve implements Resolver. Uses the IPFS routing system to resolve SFS-like // names. -func (r *routingResolver) Resolve(name string) (string, error) { +func (r *routingResolver) Resolve(ctx context.Context, name string) (u.Key, error) { log.Debugf("RoutingResolve: '%s'", name) - ctx := context.TODO() hash, err := mh.FromB58String(name) if err != nil { log.Warning("RoutingResolve: bad input hash: [%s]\n", name) @@ -88,5 +87,5 @@ func (r *routingResolver) Resolve(name string) (string, error) { } // ok sig checks out. this is a valid name. - return string(entry.GetValue()), nil + return u.Key(entry.GetValue()), nil } From 181f1c21fccfb7957ceb54dca2c192d82ecf347d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 28 Jan 2015 05:18:39 +0000 Subject: [PATCH 0687/3817] implement path type This commit was moved from ipfs/go-path@06d427101bccc0ebf66ecbb11495dfa24034cdba --- path/path.go | 102 ++++++----------------------------------------- path/resolver.go | 95 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 90 deletions(-) create mode 100644 path/resolver.go diff --git a/path/path.go b/path/path.go index 35ea36705..bfbef7234 100644 --- a/path/path.go +++ b/path/path.go @@ -1,104 +1,26 @@ -// package path implements utilities for resolving paths within ipfs. package path import ( - "fmt" "path" "strings" - - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - merkledag "github.com/jbenet/go-ipfs/merkledag" - u "github.com/jbenet/go-ipfs/util" ) -var log = u.Logger("path") - -// Resolver provides path resolution to IPFS -// It has a pointer to a DAGService, which is uses to resolve nodes. -type Resolver struct { - DAG merkledag.DAGService -} - -// ResolvePath fetches the node for given path. It uses the first -// path component as a hash (key) of the first node, then resolves -// all other components walking the links, with ResolveLinks. -func (s *Resolver) ResolvePath(fpath string) (*merkledag.Node, error) { - log.Debugf("Resolve: '%s'", fpath) - fpath = path.Clean(fpath) - - if strings.HasPrefix(fpath, "/ipfs/") { - fpath = fpath[6:] - } - - parts := strings.Split(fpath, "/") - - // skip over empty first elem - if len(parts[0]) == 0 { - parts = parts[1:] - } - - // if nothing, bail. - if len(parts) == 0 { - return nil, fmt.Errorf("ipfs path must contain at least one component") - } +// TODO: debate making this a private struct wrapped in a public interface +// would allow us to control creation, and cache segments. +type Path string - // first element in the path is a b58 hash (for now) - h, err := mh.FromB58String(parts[0]) - if err != nil { - log.Debug("given path element is not a base58 string.\n") - return nil, err - } +func (p Path) Segments() []string { + cleaned := path.Clean(string(p)) + segments := strings.Split(cleaned, "/") - log.Debug("Resolve dag get.\n") - nd, err := s.DAG.Get(u.Key(h)) - if err != nil { - return nil, err + // Ignore leading slash + if len(segments[0]) == 0 { + segments = segments[1:] } - return s.ResolveLinks(nd, parts[1:]) + return segments } -// ResolveLinks iteratively resolves names by walking the link hierarchy. -// Every node is fetched from the DAGService, resolving the next name. -// Returns the last node found. -// -// ResolveLinks(nd, []string{"foo", "bar", "baz"}) -// would retrieve "baz" in ("bar" in ("foo" in nd.Links).Links).Links -func (s *Resolver) ResolveLinks(ndd *merkledag.Node, names []string) ( - nd *merkledag.Node, err error) { - - nd = ndd // dup arg workaround - - // for each of the path components - for _, name := range names { - - var next u.Key - var nlink *merkledag.Link - // for each of the links in nd, the current object - for _, link := range nd.Links { - if link.Name == name { - next = u.Key(link.Hash) - nlink = link - break - } - } - - if next == "" { - h1, _ := nd.Multihash() - h2 := h1.B58String() - return nil, fmt.Errorf("no link named %q under %s", name, h2) - } - - if nlink.Node == nil { - // fetch object for link and assign to nd - nd, err = s.DAG.Get(next) - if err != nil { - return nd, err - } - nlink.Node = nd - } else { - nd = nlink.Node - } - } - return +func (p Path) String() string { + return string(p) } diff --git a/path/resolver.go b/path/resolver.go new file mode 100644 index 000000000..6c9366124 --- /dev/null +++ b/path/resolver.go @@ -0,0 +1,95 @@ +// package path implements utilities for resolving paths within ipfs. +package path + +import ( + "fmt" + + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + merkledag "github.com/jbenet/go-ipfs/merkledag" + u "github.com/jbenet/go-ipfs/util" +) + +var log = u.Logger("path") + +// Resolver provides path resolution to IPFS +// It has a pointer to a DAGService, which is uses to resolve nodes. +type Resolver struct { + DAG merkledag.DAGService +} + +// ResolvePath fetches the node for given path. It uses the first +// path component as a hash (key) of the first node, then resolves +// all other components walking the links, with ResolveLinks. +func (s *Resolver) ResolvePath(fpath Path) (*merkledag.Node, error) { + log.Debugf("Resolve: '%s'", fpath) + + parts := fpath.Segments() + if parts[0] == "ipfs" { + parts = parts[1:] + } + + // if nothing, bail. + if len(parts) == 0 { + return nil, fmt.Errorf("ipfs path must contain at least one component") + } + + // first element in the path is a b58 hash (for now) + h, err := mh.FromB58String(parts[0]) + if err != nil { + log.Debug("given path element is not a base58 string.\n") + return nil, err + } + + log.Debug("Resolve dag get.\n") + nd, err := s.DAG.Get(u.Key(h)) + if err != nil { + return nil, err + } + + return s.ResolveLinks(nd, parts[1:]) +} + +// ResolveLinks iteratively resolves names by walking the link hierarchy. +// Every node is fetched from the DAGService, resolving the next name. +// Returns the last node found. +// +// ResolveLinks(nd, []string{"foo", "bar", "baz"}) +// would retrieve "baz" in ("bar" in ("foo" in nd.Links).Links).Links +func (s *Resolver) ResolveLinks(ndd *merkledag.Node, names []string) ( + nd *merkledag.Node, err error) { + + nd = ndd // dup arg workaround + + // for each of the path components + for _, name := range names { + + var next u.Key + var nlink *merkledag.Link + // for each of the links in nd, the current object + for _, link := range nd.Links { + if link.Name == name { + next = u.Key(link.Hash) + nlink = link + break + } + } + + if next == "" { + h1, _ := nd.Multihash() + h2 := h1.B58String() + return nil, fmt.Errorf("no link named %q under %s", name, h2) + } + + if nlink.Node == nil { + // fetch object for link and assign to nd + nd, err = s.DAG.Get(next) + if err != nil { + return nd, err + } + nlink.Node = nd + } else { + nd = nlink.Node + } + } + return +} From bbf3011febff04c6dc5fcebc95466d6fa42d7926 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 28 Jan 2015 05:18:39 +0000 Subject: [PATCH 0688/3817] implement path type This commit was moved from ipfs/go-unixfs@72525912eb70aaba00668b01010c69935746c2c0 --- unixfs/tar/reader.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index 65493f11f..e27f6af41 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -7,7 +7,6 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "io" gopath "path" - "strings" "time" mdag "github.com/jbenet/go-ipfs/merkledag" @@ -29,10 +28,7 @@ type Reader struct { err error } -func NewReader(path string, dag mdag.DAGService, resolver *path.Resolver, compression int) (*Reader, error) { - if strings.HasPrefix(path, "/ipfs/") { - path = path[6:] - } +func NewReader(path path.Path, dag mdag.DAGService, resolver *path.Resolver, compression int) (*Reader, error) { reader := &Reader{ signalChan: make(chan struct{}), @@ -58,7 +54,7 @@ func NewReader(path string, dag mdag.DAGService, resolver *path.Resolver, compre // writeToBuf will write the data to the buffer, and will signal when there // is new data to read - _, filename := gopath.Split(path) + _, filename := gopath.Split(path.String()) go reader.writeToBuf(dagnode, filename, 0) return reader, nil From 1be52783179679fd38376f273fb1a70cbca2d414 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 20 Jan 2015 03:05:30 -0800 Subject: [PATCH 0689/3817] log(dht): remove lots of query debug logs the debug log is flooded with pages upon pages of... we've gotta be more judicious with our use of console logs. i'm sure there's interesting actionable information in here. let's use the console logging more like a sniper rifle and less like birdshot. feel free to revert if there are specific critical statements in this changeset 03:05:24.096 DEBUG dht: dht().Query(QmXvrpUZXCYaCkf1jfaQTJASS91xd47Yih2rnVC5YbFAAK).Run(3) queryPeer() QUERY worker for: - not found, and no closer peers. prefixlog.go:107 03:05:24.096 DEBUG dht: dht().Query(QmXvrpUZXCYaCkf1jfaQTJASS91xd47Yih2rnVC5YbFAAK).Run(3) queryPeer() completed prefixlog.go:107 03:05:24.096 DEBUG dht: dht().Query(QmXvrpUZXCYaCkf1jfaQTJASS91xd47Yih2rnVC5YbFAAK).Run(3) queryPeer() finished prefixlog.go:107 03:05:24.096 DEBUG dht: dht() FindProviders(QmXvrpUZXCYaCkf1jfaQTJASS91xd47Yih2rnVC5YbFAAK) Query() 0 provider entries prefixlog.go:107 03:05:24.096 DEBUG dht: dht() FindProviders(QmXvrpUZXCYaCkf1jfaQTJASS91xd47Yih2rnVC5YbFAAK) Query() 0 provider entries decoded prefixlog.go:107 03:05:24.096 DEBUG dht: dht() FindProviders(QmXvrpUZXCYaCkf1jfaQTJASS91xd47Yih2rnVC5YbFAAK) Query() got closer peers: 0 [] prefixlog.go:107 03:05:24.097 DEBUG dht: dht() FindProviders(QmXvrpUZXCYaCkf1jfaQTJASS91xd47Yih2rnVC5YbFAAK) Query() end prefixlog.go:107 03:05:24.097 DEBUG dht: dht().Query(QmXvrpUZXCYaCkf1jfaQTJASS91xd47Yih2rnVC5YbFAAK).Run(3) queryPeer() query finished prefixlog.go:107 03:05:24.097 DEBUG dht: dht().Query(QmXvrpUZXCYaCkf1jfaQTJASS91xd47Yih2rnVC5YbFAAK).Run(3) queryPeer() QUERY worker for: - not found, and no closer peers. prefixlog.go:107 03:05:24.097 DEBUG dht: dht().Query(QmXvrpUZXCYaCkf1jfaQTJASS91xd47Yih2rnVC5YbFAAK).Run(3) queryPeer() completed prefixlog.go:107 03:05:24.097 DEBUG dht: dht().Query(QmXvrpUZXCYaCkf1jfaQTJASS91xd47Yih2rnVC5YbFAAK).Run(3) queryPeer() finished prefixlog.go:107 03:05:24.097 DEBUG dht: dht().Query(QmXvrpUZXCYaCkf1jfaQTJASS91xd47Yih2rnVC5YbFAAK).Run(3) all peers ended prefixlog.go:107 03:05:24.097 DEBUG dht: dht().Query(QmXvrpUZXCYaCkf1jfaQTJASS91xd47Yih2rnVC5YbFAAK).Run(3) spawnWorkers end prefixlog.go:107 03:05:24.097 DEBUG dht: dht().Query(QmXvrpUZXCYaCkf1jfaQTJASS91xd47Yih2rnVC5YbFAAK).Run(3) failure: %s routing: not found prefixlog.go:107 03:05:24.097 DEBUG dht: dht().Query(QmXvrpUZXCYaCkf1jfaQTJASS91xd47Yih2rnVC5YbFAAK).Run(3) end prefixlog.go:107 This commit was moved from ipfs/go-ipfs-routing@d24f129d5d655326ad6d0c1f80f8e8d2ef55174a --- routing/dht/query.go | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/routing/dht/query.go b/routing/dht/query.go index 14a23de6f..8d6505b88 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -85,10 +85,7 @@ func newQueryRunner(ctx context.Context, q *dhtQuery) *dhtQueryRunner { func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { r.log = log - log.Debug("enter") - defer log.Debug("end") - log.Debugf("Run query with %d peers.", len(peers)) if len(peers) == 0 { log.Warning("Running query with no peers!") return nil, nil @@ -107,7 +104,6 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { // go do this thing. // do it as a child func to make sure Run exits // ONLY AFTER spawn workers has exited. - log.Debugf("go spawn workers") r.cg.AddChildFunc(r.spawnWorkers) // so workers are working. @@ -117,7 +113,6 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { select { case <-r.peersRemaining.Done(): - log.Debug("all peers ended") r.cg.Close() r.RLock() defer r.RUnlock() @@ -139,11 +134,9 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { } if r.result != nil && r.result.success { - log.Debug("success: %s", r.result) return r.result, nil } - log.Debug("failure: %s", err) return nil, err } @@ -155,11 +148,9 @@ func (r *dhtQueryRunner) addPeerToQuery(ctx context.Context, next peer.ID) { } if !r.peersSeen.TryAdd(next) { - r.log.Debugf("addPeerToQuery skip seen %s", next) return } - r.log.Debugf("addPeerToQuery adding %s", next) r.peersRemaining.Increment(1) select { case r.peersToQuery.EnqChan <- next: @@ -181,7 +172,6 @@ func (r *dhtQueryRunner) spawnWorkers(parent ctxgroup.ContextGroup) { if !more { return // channel closed. } - log.Debugf("spawning worker for: %v", p) // do it as a child func to make sure Run exits // ONLY AFTER spawn workers has exited. @@ -202,17 +192,16 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { } // ok let's do this! - log.Debugf("running") // make sure we do this when we exit defer func() { // signal we're done proccessing peer p - log.Debugf("completed") r.peersRemaining.Decrement(1) r.rateLimit <- struct{}{} }() // make sure we're connected to the peer. + // FIXME abstract away into the network layer if conns := r.query.dht.host.Network().ConnsToPeer(p); len(conns) == 0 { log.Infof("not connected. dialing.") // while we dial, we do not take up a rate limit. this is to allow @@ -239,9 +228,7 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { } // finally, run the query against this peer - log.Debugf("query running") res, err := r.query.qfunc(cg.Context(), p) - log.Debugf("query finished") if err != nil { log.Debugf("ERROR worker for: %v %v", p, err) From 4e5fead489c4f2378a17b952f0130dff7d123d90 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 30 Jan 2015 19:55:38 +0000 Subject: [PATCH 0690/3817] address concerns about user interface with new Path type This commit was moved from ipfs/go-path@00e226d9d4bf206d0c273ac59588d9d846095a67 --- path/path.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/path/path.go b/path/path.go index bfbef7234..c76844b1e 100644 --- a/path/path.go +++ b/path/path.go @@ -3,12 +3,24 @@ package path import ( "path" "strings" + + u "github.com/jbenet/go-ipfs/util" ) // TODO: debate making this a private struct wrapped in a public interface // would allow us to control creation, and cache segments. type Path string +// FromString safely converts a string type to a Path type +func FromString(s string) Path { + return Path(s) +} + +// FromKey safely converts a Key type to a Path type +func FromKey(k u.Key) Path { + return Path(k.String()) +} + func (p Path) Segments() []string { cleaned := path.Clean(string(p)) segments := strings.Split(cleaned, "/") From 5b062f3cd9dc222d49c43eb0d42346952c9d91b9 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 30 Jan 2015 20:17:55 -0800 Subject: [PATCH 0691/3817] p2p/net: notify on listens Network now signals when it successfully listens on some address or when an address shuts down. This will be used to establish and close nat port mappings. It could also be used to notify peers of address changes. This commit was moved from ipfs/go-ipfs-routing@fa1378dae370b5c3ac9d7f359a0cdbef9cf899b9 --- routing/dht/notif.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 318db12ea..82e097753 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -1,6 +1,8 @@ package dht import ( + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + inet "github.com/jbenet/go-ipfs/p2p/net" ) @@ -31,3 +33,5 @@ func (nn *netNotifiee) Disconnected(n inet.Network, v inet.Conn) { func (nn *netNotifiee) OpenedStream(n inet.Network, v inet.Stream) {} func (nn *netNotifiee) ClosedStream(n inet.Network, v inet.Stream) {} +func (nn *netNotifiee) Listen(n inet.Network, a ma.Multiaddr) {} +func (nn *netNotifiee) ListenClose(n inet.Network, a ma.Multiaddr) {} From 27e3ed4f09f707cc1a5f1d643e62e224c1b2c1fe Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 30 Jan 2015 20:19:48 -0800 Subject: [PATCH 0692/3817] dht: removing addrs sanity check About to allow dht to start without local addresses. this is so that we can initialize the dht and sign it up to listen on the muxer, before our node starts accepting incoming connections. otherwise, we lose some (we're observing this happening already). I looked through the dht's use of the peerstore, and the check here doesnt seem to be as important as the panic implies. I believe the panic was used for debugging weird "dont have any address" conditions we had earlier. This commit was moved from ipfs/go-ipfs-routing@199d5b62191e8821666dd8e21c571fef3f102409 --- routing/dht/dht.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 42e0cd7b7..d34ac720b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -76,11 +76,6 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip return nil }) - // sanity check. this should **never** happen - if len(dht.peerstore.Addresses(dht.self)) < 1 { - panic("attempt to initialize dht without addresses for self") - } - h.SetStreamHandler(ProtocolDHT, dht.handleNewStream) dht.providers = NewProviderManager(dht.Context(), dht.self) dht.AddChildGroup(dht.providers) From 742a25b99d84ec53bb237751894933fe3b97db67 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 19 Jan 2015 17:26:58 -0800 Subject: [PATCH 0693/3817] init docs: go generated welcome dir + files updated sharness hashes This commit was moved from ipfs/go-unixfs@767e9462d6b6ef7a555f96cf352f694a9ac0fcb9 --- unixfs/io/dirbuilder.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 unixfs/io/dirbuilder.go diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go new file mode 100644 index 000000000..9597db3d1 --- /dev/null +++ b/unixfs/io/dirbuilder.go @@ -0,0 +1,38 @@ +package io + +import ( + mdag "github.com/jbenet/go-ipfs/merkledag" + format "github.com/jbenet/go-ipfs/unixfs" + u "github.com/jbenet/go-ipfs/util" +) + +type directoryBuilder struct { + dserv mdag.DAGService + dirnode *mdag.Node +} + +func NewDirectory(dserv mdag.DAGService) *directoryBuilder { + db := new(directoryBuilder) + db.dserv = dserv + db.dirnode = new(mdag.Node) + db.dirnode.Data = format.FolderPBData() + return db +} + +func (d *directoryBuilder) AddChild(name string, k u.Key) error { + cnode, err := d.dserv.Get(k) + if err != nil { + return err + } + + err = d.dirnode.AddNodeLinkClean(name, cnode) + if err != nil { + return err + } + + return nil +} + +func (d *directoryBuilder) GetNode() *mdag.Node { + return d.dirnode +} From 812bea7bea06311750b1f616efb411a54ba76900 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Wed, 21 Jan 2015 15:51:28 +0100 Subject: [PATCH 0694/3817] HTTP: add handlers to allow object creation and modification This commit was moved from ipfs/go-merkledag@09e47db61379f5fa7713ceed7e889d51f1ba7857 --- ipld/merkledag/node.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 016f4353b..637563290 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -134,6 +134,16 @@ func (n *Node) Copy() *Node { return nnode } +// UpdateNodeLink return a copy of the node with the link name set to point to +// that. If a link of the same name existed, it is removed. +func (n *Node) UpdateNodeLink(name string, that *Node) (*Node, error) { + newnode := n.Copy() + err := newnode.RemoveNodeLink(name) + err = nil // ignore error + err = newnode.AddNodeLink(name, that) + return newnode, err +} + // Size returns the total size of the data addressed by node, // including the total sizes of references. func (n *Node) Size() (uint64, error) { From 5c4b68c68b2c3c257675a8b0498be4d3ebaf806f Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Wed, 21 Jan 2015 15:51:28 +0100 Subject: [PATCH 0695/3817] HTTP: add handlers to allow object creation and modification This commit was moved from ipfs/go-path@a84f11bd6dd7c35a221fa48830ff18c789002bd5 --- path/resolver.go | 63 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index 6c9366124..863ae9d3c 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -11,16 +11,26 @@ import ( var log = u.Logger("path") +// ErrNoLink is returned when a link is not found in a path +type ErrNoLink struct { + name string + node mh.Multihash +} + +func (e ErrNoLink) Error() string { + return fmt.Sprintf("no link named %q under %s", e.name, e.node.B58String()) +} + // Resolver provides path resolution to IPFS // It has a pointer to a DAGService, which is uses to resolve nodes. type Resolver struct { DAG merkledag.DAGService } -// ResolvePath fetches the node for given path. It uses the first -// path component as a hash (key) of the first node, then resolves -// all other components walking the links, with ResolveLinks. -func (s *Resolver) ResolvePath(fpath Path) (*merkledag.Node, error) { +// SplitAbsPath clean up and split fpath. It extracts the first component (which +// must be a Multihash) and return it separately. +func SplitAbsPath(fpath Path) (mh.Multihash, []string, error) { + log.Debugf("Resolve: '%s'", fpath) parts := fpath.Segments() @@ -30,13 +40,36 @@ func (s *Resolver) ResolvePath(fpath Path) (*merkledag.Node, error) { // if nothing, bail. if len(parts) == 0 { - return nil, fmt.Errorf("ipfs path must contain at least one component") + return nil, nil, fmt.Errorf("ipfs path must contain at least one component") } // first element in the path is a b58 hash (for now) h, err := mh.FromB58String(parts[0]) if err != nil { log.Debug("given path element is not a base58 string.\n") + return nil, nil, err + } + + return h, parts[1:], nil +} + +// ResolvePath fetches the node for given path. It returns the last item +// returned by ResolvePathComponents. +func (s *Resolver) ResolvePath(fpath Path) (*merkledag.Node, error) { + nodes, err := s.ResolvePathComponents(fpath) + if err != nil || nodes == nil { + return nil, err + } else { + return nodes[len(nodes)-1], err + } +} + +// ResolvePathComponents fetches the nodes for each segment of the given path. +// It uses the first path component as a hash (key) of the first node, then +// resolves all other components walking the links, with ResolveLinks. +func (s *Resolver) ResolvePathComponents(fpath Path) ([]*merkledag.Node, error) { + h, parts, err := SplitAbsPath(fpath) + if err != nil { return nil, err } @@ -46,19 +79,22 @@ func (s *Resolver) ResolvePath(fpath Path) (*merkledag.Node, error) { return nil, err } - return s.ResolveLinks(nd, parts[1:]) + return s.ResolveLinks(nd, parts) } // ResolveLinks iteratively resolves names by walking the link hierarchy. // Every node is fetched from the DAGService, resolving the next name. -// Returns the last node found. +// Returns the list of nodes forming the path, starting with ndd. This list is +// guaranteed never to be empty. // // ResolveLinks(nd, []string{"foo", "bar", "baz"}) // would retrieve "baz" in ("bar" in ("foo" in nd.Links).Links).Links func (s *Resolver) ResolveLinks(ndd *merkledag.Node, names []string) ( - nd *merkledag.Node, err error) { + result []*merkledag.Node, err error) { - nd = ndd // dup arg workaround + result = make([]*merkledag.Node, 0, len(names)+1) + result = append(result, ndd) + nd := ndd // dup arg workaround // for each of the path components for _, name := range names { @@ -75,21 +111,22 @@ func (s *Resolver) ResolveLinks(ndd *merkledag.Node, names []string) ( } if next == "" { - h1, _ := nd.Multihash() - h2 := h1.B58String() - return nil, fmt.Errorf("no link named %q under %s", name, h2) + n, _ := nd.Multihash() + return result, ErrNoLink{name: name, node: n} } if nlink.Node == nil { // fetch object for link and assign to nd nd, err = s.DAG.Get(next) if err != nil { - return nd, err + return append(result, nd), err } nlink.Node = nd } else { nd = nlink.Node } + + result = append(result, nlink.Node) } return } From a285a2c2453b2edbb2e3a877fdf4275e1f4dd8b6 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 31 Jan 2015 17:08:47 -0800 Subject: [PATCH 0696/3817] blockservice/worker: fix proc/limiter sync see: https://gist.github.com/jbenet/6b8b45bde9d9fce17d57 I want to make the goprocess API nicer so it doesnt lead users into this problem. any ideas? This commit was moved from ipfs/go-blockservice@f4fbf264e11e0e40b2e29464687c894ea57102f0 --- blockservice/worker/worker.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/blockservice/worker/worker.go b/blockservice/worker/worker.go index be46c45c8..eb5b1fc17 100644 --- a/blockservice/worker/worker.go +++ b/blockservice/worker/worker.go @@ -120,7 +120,8 @@ func (w *Worker) start(c Config) { // reads from |workerChan| until process closes w.process.Go(func(proc process.Process) { ctx := childContext(proc) // shut down in-progress HasBlock when time to die - limiter := ratelimit.NewRateLimiter(proc, c.NumWorkers) + limiter := ratelimit.NewRateLimiter(process.Background(), c.NumWorkers) + defer limiter.Close() for { select { case <-proc.Closing(): From 451f407be4e38d8afecc87beeb92d3e5cc83931f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 1 Feb 2015 01:43:58 -0800 Subject: [PATCH 0697/3817] dht/notif: bugfix in hanging connects http://gifs.gifbin.com/012011/1295375531_cat-jump-fail.gif This commit was moved from ipfs/go-ipfs-routing@4f0abd70c35c34c4f01fcfdee3ecf61923c6de3d --- routing/dht/notif.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 82e097753..4af2fc978 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -18,6 +18,7 @@ func (nn *netNotifiee) Connected(n inet.Network, v inet.Conn) { select { case <-dht.Closing(): return + default: } dht.Update(dht.Context(), v.RemotePeer()) } @@ -27,6 +28,7 @@ func (nn *netNotifiee) Disconnected(n inet.Network, v inet.Conn) { select { case <-dht.Closing(): return + default: } dht.routingTable.Remove(v.RemotePeer()) } From de9c153726b21b7b9633b86167c039f6a48c69dd Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 1 Feb 2015 07:20:09 -0800 Subject: [PATCH 0698/3817] NewDagReader: return DagReader for more functions Since the DagReader has lots of useful information (like sizes etc) it's good to be able to use it. This commit was moved from ipfs/go-unixfs@030291dc740c92947f3e40b7037c2030875d45bb --- unixfs/io/dagreader.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 15e1b6f6e..7262daf68 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -54,7 +54,7 @@ type ReadSeekCloser interface { // NewDagReader creates a new reader object that reads the data represented by the given // node, using the passed in DAGService for data retreival -func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (ReadSeekCloser, error) { +func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (*DagReader, error) { pb := new(ftpb.Data) err := proto.Unmarshal(n.Data, pb) if err != nil { @@ -65,6 +65,8 @@ func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (Read case ftpb.Data_Directory: // Dont allow reading directories return nil, ErrIsDir + case ftpb.Data_Raw: + fallthrough case ftpb.Data_File: fctx, cancel := context.WithCancel(ctx) promises := serv.GetDAG(fctx, n) @@ -77,9 +79,6 @@ func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (Read cancel: cancel, pbdata: pb, }, nil - case ftpb.Data_Raw: - // Raw block will just be a single level, return a byte buffer - return NewRSNCFromBytes(pb.GetData()), nil default: return nil, ft.ErrUnrecognizedType } @@ -123,6 +122,11 @@ func (dr *DagReader) precalcNextBuf() error { } } +// Size return the total length of the data from the DAG structured file. +func (dr *DagReader) Size() int64 { + return int64(dr.pbdata.GetFilesize()) +} + // Read reads data from the DAG structured file func (dr *DagReader) Read(b []byte) (int, error) { // If no cached buffer, load one From e725d341553d985e9b0dae82e9dbaa5c1e697dea Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 27 Jan 2015 22:57:39 -0800 Subject: [PATCH 0699/3817] feat(dht/message) add PeerRoutingInfo This commit was moved from ipfs/go-ipfs-routing@bb60979719f917678d951a4f922b2e25b6cc841c --- routing/dht/pb/message.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index efa72f6ee..676038d2d 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -10,6 +10,11 @@ import ( var log = eventlog.Logger("dht.pb") +type PeerRoutingInfo struct { + peer.PeerInfo + inet.Connectedness +} + // NewMessage constructs a new dht message with given type, key, and level func NewMessage(typ Message_MessageType, key string, level int) *Message { m := &Message{ @@ -20,6 +25,20 @@ func NewMessage(typ Message_MessageType, key string, level int) *Message { return m } +func peerRoutingInfoToPBPeer(p PeerRoutingInfo) *Message_Peer { + pbp := new(Message_Peer) + + pbp.Addrs = make([][]byte, len(p.Addrs)) + for i, maddr := range p.Addrs { + pbp.Addrs[i] = maddr.Bytes() // Bytes, not String. Compressed. + } + s := string(p.ID) + pbp.Id = &s + c := ConnectionType(p.Connectedness) + pbp.Connection = &c + return pbp +} + func peerInfoToPBPeer(p peer.PeerInfo) *Message_Peer { pbp := new(Message_Peer) @@ -63,6 +82,14 @@ func PeerInfosToPBPeers(n inet.Network, peers []peer.PeerInfo) []*Message_Peer { return pbps } +func PeerRoutingInfosToPBPeers(peers []PeerRoutingInfo) []*Message_Peer { + pbpeers := make([]*Message_Peer, len(peers)) + for i, p := range peers { + pbpeers[i] = peerRoutingInfoToPBPeer(p) + } + return pbpeers +} + // PBPeersToPeerInfos converts given []*Message_Peer into []peer.PeerInfo // Invalid addresses will be silently omitted. func PBPeersToPeerInfos(pbps []*Message_Peer) []peer.PeerInfo { From ae43b9c561801cddbc6028bd6e86f1f0814767cf Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 30 Jan 2015 00:08:46 -0800 Subject: [PATCH 0700/3817] refac(blockservice) extract waitable This commit was moved from ipfs/go-blockservice@6d19450b27f0c28c52b01e6ef7c14a138c52cf86 --- blockservice/worker/worker.go | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/blockservice/worker/worker.go b/blockservice/worker/worker.go index eb5b1fc17..77097bef6 100644 --- a/blockservice/worker/worker.go +++ b/blockservice/worker/worker.go @@ -6,11 +6,11 @@ import ( "errors" "time" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" process "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" ratelimit "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit" blocks "github.com/jbenet/go-ipfs/blocks" exchange "github.com/jbenet/go-ipfs/exchange" + waitable "github.com/jbenet/go-ipfs/thirdparty/waitable" util "github.com/jbenet/go-ipfs/util" ) @@ -119,7 +119,7 @@ func (w *Worker) start(c Config) { // reads from |workerChan| until process closes w.process.Go(func(proc process.Process) { - ctx := childContext(proc) // shut down in-progress HasBlock when time to die + ctx := waitable.Context(proc) // shut down in-progress HasBlock when time to die limiter := ratelimit.NewRateLimiter(process.Background(), c.NumWorkers) defer limiter.Close() for { @@ -181,18 +181,3 @@ func (s *BlockList) Pop() *blocks.Block { func (s *BlockList) Len() int { return s.list.Len() } - -// TODO extract -type waitable interface { - Closing() <-chan struct{} -} - -// TODO extract -func childContext(w waitable) context.Context { - ctx, cancel := context.WithCancel(context.Background()) - go func() { - <-w.Closing() - cancel() - }() - return ctx -} From bbfbcb2b1457b3e6d88625a2716ed854cfcff239 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 30 Jan 2015 23:15:20 -0800 Subject: [PATCH 0701/3817] log(dht/pb) include key in dht message loggable This commit was moved from ipfs/go-ipfs-routing@2caaf12e6438d11eb0f313ab9805a4fb4ad737b9 --- routing/dht/pb/message.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 676038d2d..ce6af1459 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -6,6 +6,7 @@ import ( inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" + util "github.com/jbenet/go-ipfs/util" ) var log = eventlog.Logger("dht.pb") @@ -142,6 +143,7 @@ func (m *Message) Loggable() map[string]interface{} { return map[string]interface{}{ "message": map[string]string{ "type": m.Type.String(), + "key": util.Key(m.GetKey()).Pretty(), }, } } From b33e5d1defa388ebeb4a8c97a5b80f998b2ccb2a Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 2 Feb 2015 08:21:45 -0800 Subject: [PATCH 0702/3817] dht: use our most recent Addrs This commit was moved from ipfs/go-ipfs-routing@ef1817f8001c9648186847d776d6ada71b677d3c --- routing/dht/dht.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index d34ac720b..edd18ff11 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -142,16 +142,20 @@ func (dht *IpfsDHT) putValueToPeer(ctx context.Context, p peer.ID, func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, key string) error { // add self as the provider - pi := dht.peerstore.PeerInfo(dht.self) + pi := peer.PeerInfo{ + ID: dht.self, + Addrs: dht.host.Addrs(), + } + // // only share WAN-friendly addresses ?? // pi.Addrs = addrutil.WANShareableAddrs(pi.Addrs) if len(pi.Addrs) < 1 { - log.Infof("%s putProvider: %s for %s error: no wan-friendly addresses", dht.self, p, u.Key(key), pi.Addrs) + // log.Infof("%s putProvider: %s for %s error: no wan-friendly addresses", dht.self, p, u.Key(key), pi.Addrs) return fmt.Errorf("no known addresses for self. cannot put provider.") } pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, string(key), 0) - pmes.ProviderPeers = pb.PeerInfosToPBPeers(dht.host.Network(), []peer.PeerInfo{pi}) + pmes.ProviderPeers = pb.RawPeerInfosToPBPeers([]peer.PeerInfo{pi}) err := dht.sendMessage(ctx, p, pmes) if err != nil { return err From 1e109278d65fb47fb684d2bee3f836af0dd710b9 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 2 Feb 2015 11:30:00 -0800 Subject: [PATCH 0703/3817] AddrManager: use addr manager with smarter TTLs This addr manager should seriously help with the addrsplosion problem. This commit was moved from ipfs/go-ipfs-routing@c96b9c189203f5c6f7337ef1165a3b4ed146ea05 --- routing/dht/dht_test.go | 14 +++++++------- routing/dht/handlers.go | 2 +- routing/dht/lookup.go | 2 +- routing/dht/query.go | 2 +- routing/grandcentral/server.go | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 00597b016..9d65a6fac 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -55,7 +55,7 @@ func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []peer for i := 0; i < n; i++ { dhts[i] = setupDHT(ctx, t) peers[i] = dhts[i].self - addrs[i] = dhts[i].peerstore.Addresses(dhts[i].self)[0] + addrs[i] = dhts[i].peerstore.Addrs(dhts[i].self)[0] } return addrs, peers, dhts @@ -64,12 +64,12 @@ func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []peer func connect(t *testing.T, ctx context.Context, a, b *IpfsDHT) { idB := b.self - addrB := b.peerstore.Addresses(idB) + addrB := b.peerstore.Addrs(idB) if len(addrB) == 0 { t.Fatal("peers setup incorrectly: no local address") } - a.peerstore.AddAddresses(idB, addrB) + a.peerstore.AddAddrs(idB, addrB, peer.TempAddrTTL) if err := a.Connect(ctx, idB); err != nil { t.Fatal(err) } @@ -754,20 +754,20 @@ func TestConnectCollision(t *testing.T) { dhtA := setupDHT(ctx, t) dhtB := setupDHT(ctx, t) - addrA := dhtA.peerstore.Addresses(dhtA.self)[0] - addrB := dhtB.peerstore.Addresses(dhtB.self)[0] + addrA := dhtA.peerstore.Addrs(dhtA.self)[0] + addrB := dhtB.peerstore.Addrs(dhtB.self)[0] peerA := dhtA.self peerB := dhtB.self errs := make(chan error) go func() { - dhtA.peerstore.AddAddress(peerB, addrB) + dhtA.peerstore.AddAddr(peerB, addrB, peer.TempAddrTTL) err := dhtA.Connect(ctx, peerB) errs <- err }() go func() { - dhtB.peerstore.AddAddress(peerA, addrA) + dhtB.peerstore.AddAddr(peerA, addrA, peer.TempAddrTTL) err := dhtB.Connect(ctx, peerA) errs <- err }() diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 6376dbcba..6974ad08d 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -238,7 +238,7 @@ func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.M log.Infof("received provider %s for %s (addrs: %s)", p, key, pi.Addrs) if pi.ID != dht.self { // dont add own addrs. // add the received addresses to our peerstore. - dht.peerstore.AddPeerInfo(pi) + dht.peerstore.AddAddrs(pi.ID, pi.Addrs, peer.ProviderAddrTTL) } dht.providers.AddProvider(key, p) } diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 6e0acd9fa..a713e553d 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -100,7 +100,7 @@ func (dht *IpfsDHT) closerPeersSingle(ctx context.Context, key u.Key, p peer.ID) for _, pbp := range pmes.GetCloserPeers() { pid := peer.ID(pbp.GetId()) if pid != dht.self { // dont add self - dht.peerstore.AddAddresses(pid, pbp.Addresses()) + dht.peerstore.AddAddrs(pid, pbp.Addresses(), peer.TempAddrTTL) out = append(out, pid) } } diff --git a/routing/dht/query.go b/routing/dht/query.go index 8d6505b88..293c0ddd9 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -253,7 +253,7 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { } // add their addresses to the dialer's peerstore - r.query.dht.peerstore.AddPeerInfo(next) + r.query.dht.peerstore.AddAddrs(next.ID, next.Addrs, peer.TempAddrTTL) r.addPeerToQuery(cg.Context(), next.ID) log.Debugf("PEERS CLOSER -- worker for: %v added %v (%v)", p, next.ID, next.Addrs) } diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index f51b71917..179b15b3e 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -96,7 +96,7 @@ func (s *Server) handleMessage( } for _, maddr := range provider.Addresses() { // FIXME do we actually want to store to peerstore - s.peerstore.AddAddress(p, maddr) + s.peerstore.AddAddr(p, maddr, peer.TempAddrTTL) } } var providers []dhtpb.Message_Peer From 911caaa934aa31f8d04d458d98831cbea261b4d2 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 3 Feb 2015 01:06:07 -0800 Subject: [PATCH 0704/3817] logs: removed all log.Errors unhelpful to users Let's save log.Error for things the user can take action on. Moved all our diagnostics to log.Debug. We can ideally reduce them even further. This commit was moved from ipfs/go-ipfs-routing@7c546db422f8fb030cffe18f3ee0d4fafc2f8e72 --- routing/dht/dht_test.go | 6 +++--- routing/grandcentral/client.go | 4 ++-- routing/grandcentral/proxy/loopback.go | 4 ++-- routing/grandcentral/server.go | 14 +++++++------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 9d65a6fac..b57caaccc 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -262,7 +262,7 @@ func waitForWellFormedTables(t *testing.T, dhts []*IpfsDHT, minPeers, avgPeers i for { select { case <-timeoutA: - log.Errorf("did not reach well-formed routing tables by %s", timeout) + log.Debugf("did not reach well-formed routing tables by %s", timeout) return false // failed case <-time.After(5 * time.Millisecond): if checkTables() { @@ -322,7 +322,7 @@ func TestBootstrap(t *testing.T) { } }() - waitForWellFormedTables(t, dhts, 7, 10, 5*time.Second) + waitForWellFormedTables(t, dhts, 7, 10, 20*time.Second) close(stop) if u.Debug { @@ -407,7 +407,7 @@ func TestPeriodicBootstrap(t *testing.T) { // this is async, and we dont know when it's finished with one cycle, so keep checking // until the routing tables look better, or some long timeout for the failure case. - waitForWellFormedTables(t, dhts, 7, 10, 5*time.Second) + waitForWellFormedTables(t, dhts, 7, 10, 20*time.Second) if u.Debug { printRoutingTables(dhts) diff --git a/routing/grandcentral/client.go b/routing/grandcentral/client.go index ccf0bd199..bc783af76 100644 --- a/routing/grandcentral/client.go +++ b/routing/grandcentral/client.go @@ -44,13 +44,13 @@ func (c *Client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-cha request := pb.NewMessage(pb.Message_GET_PROVIDERS, string(k), 0) response, err := c.proxy.SendRequest(ctx, request) if err != nil { - log.Error(errors.Wrap(err)) + log.Debug(errors.Wrap(err)) return } for _, p := range pb.PBPeersToPeerInfos(response.GetProviderPeers()) { select { case <-ctx.Done(): - log.Error(errors.Wrap(ctx.Err())) + log.Debug(errors.Wrap(ctx.Err())) return case ch <- p: } diff --git a/routing/grandcentral/proxy/loopback.go b/routing/grandcentral/proxy/loopback.go index 59a414df0..ba598c3ea 100644 --- a/routing/grandcentral/proxy/loopback.go +++ b/routing/grandcentral/proxy/loopback.go @@ -2,9 +2,9 @@ package proxy import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" - ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" errors "github.com/jbenet/go-ipfs/util/debugerror" ) @@ -39,7 +39,7 @@ func (lb *Loopback) handleNewStream(s inet.Stream) { pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) var incoming dhtpb.Message if err := pbr.ReadMsg(&incoming); err != nil { - log.Error(errors.Wrap(err)) + log.Debug(errors.Wrap(err)) return } ctx := context.TODO() diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index 179b15b3e..767d7e760 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -53,16 +53,16 @@ func (s *Server) handleMessage( dskey := util.Key(req.GetKey()).DsKey() val, err := s.datastore.Get(dskey) if err != nil { - log.Error(errors.Wrap(err)) + log.Debug(errors.Wrap(err)) return "", nil } rawRecord, ok := val.([]byte) if !ok { - log.Errorf("datastore had non byte-slice value for %v", dskey) + log.Debugf("datastore had non byte-slice value for %v", dskey) return "", nil } if err := proto.Unmarshal(rawRecord, response.Record); err != nil { - log.Error("failed to unmarshal dht record from datastore") + log.Debug("failed to unmarshal dht record from datastore") return "", nil } // TODO before merging: if we know any providers for the requested value, return those. @@ -72,12 +72,12 @@ func (s *Server) handleMessage( // TODO before merging: verifyRecord(req.GetRecord()) data, err := proto.Marshal(req.GetRecord()) if err != nil { - log.Error(err) + log.Debug(err) return "", nil } dskey := util.Key(req.GetKey()).DsKey() if err := s.datastore.Put(dskey, data); err != nil { - log.Error(err) + log.Debug(err) return "", nil } return p, req // TODO before merging: verify that we should return record @@ -91,7 +91,7 @@ func (s *Server) handleMessage( for _, provider := range req.GetProviderPeers() { providerID := peer.ID(provider.GetId()) if providerID != p { - log.Errorf("provider message came from third-party %s", p) + log.Debugf("provider message came from third-party %s", p) continue } for _, maddr := range provider.Addresses() { @@ -107,7 +107,7 @@ func (s *Server) handleMessage( } } if err := s.datastore.Put(pkey, providers); err != nil { - log.Error(err) + log.Debug(err) return "", nil } return "", nil From f1200906f85812f31825260e126db3210202a198 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 3 Feb 2015 01:06:07 -0800 Subject: [PATCH 0705/3817] logs: removed all log.Errors unhelpful to users Let's save log.Error for things the user can take action on. Moved all our diagnostics to log.Debug. We can ideally reduce them even further. This commit was moved from ipfs/go-merkledag@7a150b00c9d8eb08dabee89bc92df6c3a776a0c9 --- ipld/merkledag/merkledag.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index e07ff2c35..db48d3b34 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -136,7 +136,7 @@ func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} nd, err := lnk.GetNode(serv) if err != nil { - log.Error(err) + log.Debug(err) return } @@ -199,7 +199,7 @@ func (ds *dagService) GetNodes(ctx context.Context, keys []u.Key) []NodeGetter { nd, err := Decoded(blk.Data) if err != nil { // NB: can happen with improperly formatted input data - log.Error("Got back bad block!") + log.Debug("Got back bad block!") return } is := FindLinks(keys, blk.Key(), 0) From 7d8a5650eded5977b0a948bc0d09dc42ea1366e8 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 3 Feb 2015 01:06:07 -0800 Subject: [PATCH 0706/3817] logs: removed all log.Errors unhelpful to users Let's save log.Error for things the user can take action on. Moved all our diagnostics to log.Debug. We can ideally reduce them even further. This commit was moved from ipfs/go-blockservice@5846e989855e0fc1090ce4ab21f5c2a28e9ca1f4 --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 2ce230452..0d4dcfa05 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -122,7 +122,7 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks rblocks, err := s.Exchange.GetBlocks(ctx, misses) if err != nil { - log.Errorf("Error with GetBlocks: %s", err) + log.Debugf("Error with GetBlocks: %s", err) return } From 0e6b2e6a393bb23cc211f41203128f2dde42b45c Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 3 Feb 2015 01:06:07 -0800 Subject: [PATCH 0707/3817] logs: removed all log.Errors unhelpful to users Let's save log.Error for things the user can take action on. Moved all our diagnostics to log.Debug. We can ideally reduce them even further. This commit was moved from ipfs/go-namesys@5f659f8bdcc6700ba42cc1b498c344759bba9016 --- namesys/publisher.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 8a82c7947..3b209cd26 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -43,19 +43,16 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value u.Key) // validate `value` is a ref (multihash) _, err := mh.FromB58String(value.Pretty()) if err != nil { - log.Errorf("hash cast failed: %s", value) return fmt.Errorf("publish value must be str multihash. %v", err) } data, err := createRoutingEntryData(k, value) if err != nil { - log.Error("entry creation failed.") return err } pubkey := k.GetPublic() pkbytes, err := pubkey.Bytes() if err != nil { - log.Error("pubkey getbytes failed.") return err } @@ -120,7 +117,7 @@ func ValidateIpnsRecord(k u.Key, val []byte) error { case pb.IpnsEntry_EOL: t, err := u.ParseRFC3339(string(entry.GetValidity())) if err != nil { - log.Error("Failed parsing time for ipns record EOL") + log.Debug("Failed parsing time for ipns record EOL") return err } if time.Now().After(t) { From b33ff400ea547a59eaf50ace02118e3eb390ad4f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 3 Feb 2015 01:06:07 -0800 Subject: [PATCH 0708/3817] logs: removed all log.Errors unhelpful to users Let's save log.Error for things the user can take action on. Moved all our diagnostics to log.Debug. We can ideally reduce them even further. This commit was moved from ipfs/go-ipfs-chunker@e4011410906ffd153b6b12cb4b5aaa4e9d6ebe62 --- chunker/splitting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 40597a064..73fe49db1 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -38,7 +38,7 @@ func (ss *SizeSplitter) Split(r io.Reader) chan []byte { return } if err != nil { - log.Errorf("Block split error: %s", err) + log.Debugf("Block split error: %s", err) return } } From 1a7112e7db6a4266563a4d904ddbeaa7d2fbd08c Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 3 Feb 2015 12:18:30 -0800 Subject: [PATCH 0709/3817] dht/query: make sure to cancel all contexts. We are leaking peer queues: http://gateway.ipfs.io/ipfs/QmQxVA48CzVwwNYExUiFe56VrUBn8u368ZfchnCLoc7fSC/moriarty This commit was moved from ipfs/go-ipfs-routing@3f7679c8a79e4679ae691dcdd69b7a5f8c867be9 --- routing/dht/query.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/routing/dht/query.go b/routing/dht/query.go index 293c0ddd9..7a32b45bb 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -52,6 +52,9 @@ type queryFunc func(context.Context, peer.ID) (*dhtQueryResult, error) // Run runs the query at hand. pass in a list of peers to use first. func (q *dhtQuery) Run(ctx context.Context, peers []peer.ID) (*dhtQueryResult, error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + runner := newQueryRunner(ctx, q) return runner.Run(peers) } From 8e06571a2ae45381d2248c3433624dccb2f97b73 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 4 Feb 2015 19:43:49 +0000 Subject: [PATCH 0710/3817] clean up benchmarks, implement WriterTo on DAGReader, and optimize DagReader This commit was moved from ipfs/go-unixfs@72795d7feb3d4f81b2910219c5dcca7c7f1c04f5 --- unixfs/io/dagreader.go | 60 +++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 7262daf68..4d3f37b7e 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -50,6 +50,7 @@ type ReadSeekCloser interface { io.Reader io.Seeker io.Closer + io.WriterTo } // NewDagReader creates a new reader object that reads the data represented by the given @@ -68,22 +69,26 @@ func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (*Dag case ftpb.Data_Raw: fallthrough case ftpb.Data_File: - fctx, cancel := context.WithCancel(ctx) - promises := serv.GetDAG(fctx, n) - return &DagReader{ - node: n, - serv: serv, - buf: NewRSNCFromBytes(pb.GetData()), - promises: promises, - ctx: fctx, - cancel: cancel, - pbdata: pb, - }, nil + return newDataFileReader(ctx, n, pb, serv), nil default: return nil, ft.ErrUnrecognizedType } } +func newDataFileReader(ctx context.Context, n *mdag.Node, pb *ftpb.Data, serv mdag.DAGService) *DagReader { + fctx, cancel := context.WithCancel(ctx) + promises := serv.GetDAG(fctx, n) + return &DagReader{ + node: n, + serv: serv, + buf: NewRSNCFromBytes(pb.GetData()), + promises: promises, + ctx: fctx, + cancel: cancel, + pbdata: pb, + } +} + // precalcNextBuf follows the next link in line and loads it from the DAGService, // setting the next buffer to read from func (dr *DagReader) precalcNextBuf() error { @@ -108,11 +113,7 @@ func (dr *DagReader) precalcNextBuf() error { // A directory should not exist within a file return ft.ErrInvalidDirLocation case ftpb.Data_File: - subr, err := NewDagReader(dr.ctx, nxt, dr.serv) - if err != nil { - return err - } - dr.buf = subr + dr.buf = newDataFileReader(dr.ctx, nxt, pb, dr.serv) return nil case ftpb.Data_Raw: dr.buf = NewRSNCFromBytes(pb.GetData()) @@ -156,6 +157,31 @@ func (dr *DagReader) Read(b []byte) (int, error) { } } +func (dr *DagReader) WriteTo(w io.Writer) (int64, error) { + // If no cached buffer, load one + total := int64(0) + for { + // Attempt to write bytes from cached buffer + n, err := dr.buf.WriteTo(w) + total += n + dr.offset += n + if err != nil { + if err != io.EOF { + return total, err + } + } + + // Otherwise, load up the next block + err = dr.precalcNextBuf() + if err != nil { + if err == io.EOF { + return total, nil + } + return total, err + } + } +} + func (dr *DagReader) Close() error { dr.cancel() return nil @@ -163,6 +189,8 @@ func (dr *DagReader) Close() error { // Seek implements io.Seeker, and will seek to a given offset in the file // interface matches standard unix seek +// TODO: check if we can do relative seeks, to reduce the amount of dagreader +// recreations that need to happen. func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { switch whence { case os.SEEK_SET: From 7112bb9d21269bd04d78e721c1aae21df150015e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 4 Feb 2015 03:53:10 +0000 Subject: [PATCH 0711/3817] refactor importer package with trickle and balanced dag generation This commit was moved from ipfs/go-merkledag@74d015bd1832b4374d8b1d1f642ae4e0ca43d8dd --- ipld/merkledag/{mock.go => test/utils.go} | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) rename ipld/merkledag/{mock.go => test/utils.go} (79%) diff --git a/ipld/merkledag/mock.go b/ipld/merkledag/test/utils.go similarity index 79% rename from ipld/merkledag/mock.go rename to ipld/merkledag/test/utils.go index ea3737f58..cc373a8fd 100644 --- a/ipld/merkledag/mock.go +++ b/ipld/merkledag/test/utils.go @@ -1,4 +1,4 @@ -package merkledag +package mdutils import ( "testing" @@ -8,13 +8,14 @@ import ( "github.com/jbenet/go-ipfs/blocks/blockstore" bsrv "github.com/jbenet/go-ipfs/blockservice" "github.com/jbenet/go-ipfs/exchange/offline" + dag "github.com/jbenet/go-ipfs/merkledag" ) -func Mock(t testing.TB) DAGService { +func Mock(t testing.TB) dag.DAGService { bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) bserv, err := bsrv.New(bstore, offline.Exchange(bstore)) if err != nil { t.Fatal(err) } - return NewDAGService(bserv) + return dag.NewDAGService(bserv) } From e6699bbc8b4bc83ad274b569ee4592568d7b039c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 2 Feb 2015 22:46:59 +0000 Subject: [PATCH 0712/3817] implement metadata node for unixfs and other This commit was moved from ipfs/go-unixfs@737609a09782d9a2f893140b64db6523826f4f36 --- unixfs/format.go | 45 ++++++++++++++++++++++++++++++++++++++++++ unixfs/io/dagreader.go | 11 +++++++++++ unixfs/pb/unixfs.pb.go | 22 ++++++++++++++++++++- unixfs/pb/unixfs.proto | 5 +++++ 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/unixfs/format.go b/unixfs/format.go index 845f1da30..5e12d52ac 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -126,3 +126,48 @@ func (mb *MultiBlock) FileSize() uint64 { func (mb *MultiBlock) NumChildren() int { return len(mb.blocksizes) } + +type Metadata struct { + MimeType string + Size uint64 +} + +func MetadataFromBytes(b []byte) (*Metadata, error) { + pbd := new(pb.Data) + err := proto.Unmarshal(b, pbd) + if err != nil { + return nil, err + } + if pbd.GetType() != pb.Data_Metadata { + return nil, errors.New("incorrect node type") + } + + pbm := new(pb.Metadata) + err = proto.Unmarshal(pbd.Data, pbm) + if err != nil { + return nil, err + } + md := new(Metadata) + md.MimeType = pbm.GetMimeType() + return md, nil +} + +func (m *Metadata) Bytes() ([]byte, error) { + pbm := new(pb.Metadata) + pbm.MimeType = &m.MimeType + return proto.Marshal(pbm) +} + +func BytesForMetadata(m *Metadata) ([]byte, error) { + pbd := new(pb.Data) + pbd.Filesize = proto.Uint64(m.Size) + typ := pb.Data_Metadata + pbd.Type = &typ + mdd, err := m.Bytes() + if err != nil { + return nil, err + } + + pbd.Data = mdd + return proto.Marshal(pbd) +} diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 4d3f37b7e..d363d22d1 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -70,6 +70,15 @@ func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (*Dag fallthrough case ftpb.Data_File: return newDataFileReader(ctx, n, pb, serv), nil + case ftpb.Data_Metadata: + if len(n.Links) == 0 { + return nil, errors.New("incorrectly formatted metadata object") + } + child, err := n.Links[0].GetNode(serv) + if err != nil { + return nil, err + } + return NewDagReader(ctx, child, serv) default: return nil, ft.ErrUnrecognizedType } @@ -118,6 +127,8 @@ func (dr *DagReader) precalcNextBuf() error { case ftpb.Data_Raw: dr.buf = NewRSNCFromBytes(pb.GetData()) return nil + case ftpb.Data_Metadata: + return errors.New("Shouldnt have had metadata object inside file") default: return ft.ErrUnrecognizedType } diff --git a/unixfs/pb/unixfs.pb.go b/unixfs/pb/unixfs.pb.go index 999aa6d92..6e442456f 100644 --- a/unixfs/pb/unixfs.pb.go +++ b/unixfs/pb/unixfs.pb.go @@ -10,10 +10,11 @@ It is generated from these files: It has these top-level messages: Data + Metadata */ package unixfs_pb -import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import proto "code.google.com/p/gogoprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. @@ -26,17 +27,20 @@ const ( Data_Raw Data_DataType = 0 Data_Directory Data_DataType = 1 Data_File Data_DataType = 2 + Data_Metadata Data_DataType = 3 ) var Data_DataType_name = map[int32]string{ 0: "Raw", 1: "Directory", 2: "File", + 3: "Metadata", } var Data_DataType_value = map[string]int32{ "Raw": 0, "Directory": 1, "File": 2, + "Metadata": 3, } func (x Data_DataType) Enum() *Data_DataType { @@ -96,6 +100,22 @@ func (m *Data) GetBlocksizes() []uint64 { return nil } +type Metadata struct { + MimeType *string `protobuf:"bytes,1,req" json:"MimeType,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Metadata) Reset() { *m = Metadata{} } +func (m *Metadata) String() string { return proto.CompactTextString(m) } +func (*Metadata) ProtoMessage() {} + +func (m *Metadata) GetMimeType() string { + if m != nil && m.MimeType != nil { + return *m.MimeType + } + return "" +} + func init() { proto.RegisterEnum("unixfs.pb.Data_DataType", Data_DataType_name, Data_DataType_value) } diff --git a/unixfs/pb/unixfs.proto b/unixfs/pb/unixfs.proto index 618fb6159..1450809e4 100644 --- a/unixfs/pb/unixfs.proto +++ b/unixfs/pb/unixfs.proto @@ -5,6 +5,7 @@ message Data { Raw = 0; Directory = 1; File = 2; + Metadata = 3; } required DataType Type = 1; @@ -12,3 +13,7 @@ message Data { optional uint64 filesize = 3; repeated uint64 blocksizes = 4; } + +message Metadata { + required string MimeType = 1; +} From 0270c4fdb98b120104ebedcb5350aef2d79f3b8e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 4 Feb 2015 15:32:21 -0800 Subject: [PATCH 0713/3817] fix: vendor This commit was moved from ipfs/go-unixfs@7e7fb827388562fd2b0088740ee28305984846e8 --- unixfs/pb/unixfs.pb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/pb/unixfs.pb.go b/unixfs/pb/unixfs.pb.go index 6e442456f..19eb9d8ee 100644 --- a/unixfs/pb/unixfs.pb.go +++ b/unixfs/pb/unixfs.pb.go @@ -14,7 +14,7 @@ It has these top-level messages: */ package unixfs_pb -import proto "code.google.com/p/gogoprotobuf/proto" +import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. From 1728ba26000adc35beedd469c4acedc12145affa Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 5 Feb 2015 04:53:23 -0800 Subject: [PATCH 0714/3817] kbucket: fix data race This commit was moved from ipfs/go-ipfs-routing@f082e3d35b502059b8263d1a3fc08a30bac7aa3a --- routing/kbucket/bucket.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index f6fcc0b94..a8c6a07bc 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -42,8 +42,8 @@ func (b *Bucket) find(id peer.ID) *list.Element { } func (b *Bucket) remove(id peer.ID) { - b.lk.RLock() - defer b.lk.RUnlock() + b.lk.Lock() + defer b.lk.Unlock() for e := b.list.Front(); e != nil; e = e.Next() { if e.Value.(peer.ID) == id { b.list.Remove(e) From 24dc5a8d58ea5c3a9e405dfdae9dc9916377077f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 5 Feb 2015 06:22:44 -0800 Subject: [PATCH 0715/3817] routing/kbucket: cleaner "public" interface for bucket This commit was moved from ipfs/go-ipfs-routing@f47f09d0d5893ca914c4f10e4efc7acaa57f969e --- routing/kbucket/bucket.go | 24 +++++++------ routing/kbucket/table.go | 66 ++++++++++++++++++----------------- routing/kbucket/table_test.go | 10 ++---- 3 files changed, 51 insertions(+), 49 deletions(-) diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index a8c6a07bc..d551cf819 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -30,18 +30,18 @@ func (b *Bucket) Peers() []peer.ID { return ps } -func (b *Bucket) find(id peer.ID) *list.Element { +func (b *Bucket) Has(id peer.ID) bool { b.lk.RLock() defer b.lk.RUnlock() for e := b.list.Front(); e != nil; e = e.Next() { if e.Value.(peer.ID) == id { - return e + return true } } - return nil + return false } -func (b *Bucket) remove(id peer.ID) { +func (b *Bucket) Remove(id peer.ID) { b.lk.Lock() defer b.lk.Unlock() for e := b.list.Front(); e != nil; e = e.Next() { @@ -51,19 +51,23 @@ func (b *Bucket) remove(id peer.ID) { } } -func (b *Bucket) moveToFront(e *list.Element) { +func (b *Bucket) MoveToFront(id peer.ID) { b.lk.Lock() - b.list.MoveToFront(e) - b.lk.Unlock() + defer b.lk.Unlock() + for e := b.list.Front(); e != nil; e = e.Next() { + if e.Value.(peer.ID) == id { + b.list.MoveToFront(e) + } + } } -func (b *Bucket) pushFront(p peer.ID) { +func (b *Bucket) PushFront(p peer.ID) { b.lk.Lock() b.list.PushFront(p) b.lk.Unlock() } -func (b *Bucket) popBack() peer.ID { +func (b *Bucket) PopBack() peer.ID { b.lk.Lock() defer b.lk.Unlock() last := b.list.Back() @@ -71,7 +75,7 @@ func (b *Bucket) popBack() peer.ID { return last.Value.(peer.ID) } -func (b *Bucket) len() int { +func (b *Bucket) Len() int { b.lk.RLock() defer b.lk.RUnlock() return b.list.Len() diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index dc5fb3d6f..a785bf8b5 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -46,7 +46,7 @@ func NewRoutingTable(bucketsize int, localID ID, latency time.Duration, m peer.M // Update adds or moves the given peer to the front of its respective bucket // If a peer gets removed from a bucket, it is returned -func (rt *RoutingTable) Update(p peer.ID) peer.ID { +func (rt *RoutingTable) Update(p peer.ID) { rt.tabLock.Lock() defer rt.tabLock.Unlock() peerID := ConvertPeerID(p) @@ -58,33 +58,35 @@ func (rt *RoutingTable) Update(p peer.ID) peer.ID { } bucket := rt.Buckets[bucketID] - e := bucket.find(p) - if e == nil { - // New peer, add to bucket - if rt.metrics.LatencyEWMA(p) > rt.maxLatency { - // Connection doesnt meet requirements, skip! - return "" - } - bucket.pushFront(p) - - // Are we past the max bucket size? - if bucket.len() > rt.bucketsize { - // If this bucket is the rightmost bucket, and its full - // we need to split it and create a new bucket - if bucketID == len(rt.Buckets)-1 { - return rt.nextBucket() - } else { - // If the bucket cant split kick out least active node - return bucket.popBack() - } + if bucket.Has(p) { + // If the peer is already in the table, move it to the front. + // This signifies that it it "more active" and the less active nodes + // Will as a result tend towards the back of the list + bucket.MoveToFront(p) + return + } + + if rt.metrics.LatencyEWMA(p) > rt.maxLatency { + // Connection doesnt meet requirements, skip! + return + } + + // New peer, add to bucket + bucket.PushFront(p) + + // Are we past the max bucket size? + if bucket.Len() > rt.bucketsize { + // If this bucket is the rightmost bucket, and its full + // we need to split it and create a new bucket + if bucketID == len(rt.Buckets)-1 { + rt.nextBucket() + return + } else { + // If the bucket cant split kick out least active node + bucket.PopBack() + return } - return "" } - // If the peer is already in the table, move it to the front. - // This signifies that it it "more active" and the less active nodes - // Will as a result tend towards the back of the list - bucket.moveToFront(e) - return "" } // Remove deletes a peer from the routing table. This is to be used @@ -101,20 +103,20 @@ func (rt *RoutingTable) Remove(p peer.ID) { } bucket := rt.Buckets[bucketID] - bucket.remove(p) + bucket.Remove(p) } func (rt *RoutingTable) nextBucket() peer.ID { bucket := rt.Buckets[len(rt.Buckets)-1] newBucket := bucket.Split(len(rt.Buckets)-1, rt.local) rt.Buckets = append(rt.Buckets, newBucket) - if newBucket.len() > rt.bucketsize { + if newBucket.Len() > rt.bucketsize { return rt.nextBucket() } // If all elements were on left side of split... - if bucket.len() > rt.bucketsize { - return bucket.popBack() + if bucket.Len() > rt.bucketsize { + return bucket.PopBack() } return "" } @@ -153,7 +155,7 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []peer.ID { bucket = rt.Buckets[cpl] var peerArr peerSorterArr - if bucket.len() == 0 { + if bucket.Len() == 0 { // In the case of an unusual split, one bucket may be empty. // if this happens, search both surrounding buckets for nearest peer if cpl > 0 { @@ -184,7 +186,7 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []peer.ID { func (rt *RoutingTable) Size() int { var tot int for _, buck := range rt.Buckets { - tot += buck.len() + tot += buck.Len() } return tot } diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 3e44cf66a..670342b67 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -17,15 +17,14 @@ func TestBucket(t *testing.T) { peers := make([]peer.ID, 100) for i := 0; i < 100; i++ { peers[i] = tu.RandPeerIDFatal(t) - b.pushFront(peers[i]) + b.PushFront(peers[i]) } local := tu.RandPeerIDFatal(t) localID := ConvertPeerID(local) i := rand.Intn(len(peers)) - e := b.find(peers[i]) - if e == nil { + if !b.Has(peers[i]) { t.Errorf("Failed to find peer: %v", peers[i]) } @@ -62,10 +61,7 @@ func TestTableUpdate(t *testing.T) { // Testing Update for i := 0; i < 10000; i++ { - p := rt.Update(peers[rand.Intn(len(peers))]) - if p != "" { - //t.Log("evicted peer.") - } + rt.Update(peers[rand.Intn(len(peers))]) } for i := 0; i < 100; i++ { From 3eb8766bacbd22491cd94c7b6129c755ba876ea2 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 6 Feb 2015 10:59:03 -0800 Subject: [PATCH 0716/3817] ratelimiter: fixing rate limiter use Use of the ratelimiter should be conscious of the ratelimiter's potential closing. any loops that add work to ratelimiter should (a) only do so if the rate limiter is not closed, or (b) prevent limiter while work is added (i.e. use limiter.Go(addWorkHere)) This commit was moved from ipfs/go-blockservice@8a93edef9e4ed83e373f84adb2fc1735e699a163 --- blockservice/worker/worker.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/blockservice/worker/worker.go b/blockservice/worker/worker.go index 77097bef6..ee45d32ad 100644 --- a/blockservice/worker/worker.go +++ b/blockservice/worker/worker.go @@ -117,11 +117,10 @@ func (w *Worker) start(c Config) { } }) - // reads from |workerChan| until process closes - w.process.Go(func(proc process.Process) { + // reads from |workerChan| until w.process closes + limiter := ratelimit.NewRateLimiter(w.process, c.NumWorkers) + limiter.Go(func(proc process.Process) { ctx := waitable.Context(proc) // shut down in-progress HasBlock when time to die - limiter := ratelimit.NewRateLimiter(process.Background(), c.NumWorkers) - defer limiter.Close() for { select { case <-proc.Closing(): From dfaaee946c37f82ad567f02517a673843ca35202 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 10 Feb 2015 22:59:10 +0000 Subject: [PATCH 0717/3817] document some packages This commit was moved from ipfs/go-namesys@96bdf67e6183202083ef0fb524e0b28cca7b0af1 --- namesys/interface.go | 1 + 1 file changed, 1 insertion(+) diff --git a/namesys/interface.go b/namesys/interface.go index f2e4f104d..6faaeaf6a 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -1,3 +1,4 @@ +// package namesys implements various functionality for the ipns naming system. package namesys import ( From 032104f16579183d162a0c97984ee6ab3796d260 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 13 Feb 2015 08:08:30 +0000 Subject: [PATCH 0718/3817] this might solve all our problems This commit was moved from ipfs/go-ipfs-routing@70e025ed2065eebda4353728314d70c66cae803e --- routing/dht/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 6974ad08d..9f11eb6d1 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -201,7 +201,7 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb. // Also send closer peers. closer := dht.betterPeersToQuery(pmes, p, CloserPeerCount) if closer != nil { - infos := peer.PeerInfos(dht.peerstore, providers) + infos := peer.PeerInfos(dht.peerstore, closer) resp.CloserPeers = pb.PeerInfosToPBPeers(dht.host.Network(), infos) log.Debugf("%s have %d closer peers: %s", reqDesc, len(closer), infos) } From 9bc6186c0ea0aff13c730663f6117f5ede0774b6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 13 Feb 2015 08:29:10 +0000 Subject: [PATCH 0719/3817] a few more cleanup changes to handlers This commit was moved from ipfs/go-ipfs-routing@5a2c0293b18b07a4af89061056c9eb7992f772f9 --- routing/dht/handlers.go | 19 +------------------ routing/dht/providers.go | 5 +++++ 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 9f11eb6d1..03c3eda3c 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -3,7 +3,6 @@ package dht import ( "errors" "fmt" - "time" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" @@ -63,9 +62,6 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Mess return nil, err } - // Note: changed the behavior here to return _as much_ info as possible - // (potentially all of {value, closer peers, provider}) - // if we have the value, send it back if err == nil { log.Debugf("%s handleGetValue success!", dht.self) @@ -85,18 +81,10 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Mess resp.Record = rec } - // if we know any providers for the requested value, return those. - provs := dht.providers.GetProviders(ctx, u.Key(pmes.GetKey())) - provinfos := peer.PeerInfos(dht.peerstore, provs) - if len(provs) > 0 { - log.Debugf("handleGetValue returning %d provider[s]", len(provs)) - resp.ProviderPeers = pb.PeerInfosToPBPeers(dht.host.Network(), provinfos) - } - // Find closest peer on given cluster to desired key and reply with that info closer := dht.betterPeersToQuery(pmes, p, CloserPeerCount) - closerinfos := peer.PeerInfos(dht.peerstore, closer) if closer != nil { + closerinfos := peer.PeerInfos(dht.peerstore, closer) for _, pi := range closerinfos { log.Debugf("handleGetValue returning closer peer: '%s'", pi.ID) if len(pi.Addrs) < 1 { @@ -209,11 +197,6 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb. return resp, nil } -type providerInfo struct { - Creation time.Time - Value peer.ID -} - func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { defer log.EventBegin(ctx, "handleAddProvider", p).Done() key := u.Key(pmes.GetKey()) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 9e96eff36..f4f7514fb 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -10,6 +10,11 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ) +type providerInfo struct { + Creation time.Time + Value peer.ID +} + type ProviderManager struct { providers map[u.Key][]*providerInfo local map[u.Key]struct{} From f5fa9813442a4a30c19cda6389158263433a3442 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 30 Jan 2015 08:19:33 -0800 Subject: [PATCH 0720/3817] refac(gcr/s,c) use PeerRoutingInfo This commit was moved from ipfs/go-ipfs-routing@26109c0ea55cbb4287cd29d9f563712698cabc0e --- routing/grandcentral/client.go | 13 ++++++++++--- routing/grandcentral/server.go | 16 ++++++++++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/routing/grandcentral/client.go b/routing/grandcentral/client.go index bc783af76..828653eca 100644 --- a/routing/grandcentral/client.go +++ b/routing/grandcentral/client.go @@ -80,9 +80,16 @@ func (c *Client) GetValue(ctx context.Context, k u.Key) ([]byte, error) { func (c *Client) Provide(ctx context.Context, k u.Key) error { msg := pb.NewMessage(pb.Message_ADD_PROVIDER, string(k), 0) - // TODO wrap this to hide the dialer and the local/remote peers - msg.ProviderPeers = pb.PeerInfosToPBPeers(c.dialer, []peer.PeerInfo{peer.PeerInfo{ID: c.local}}) // FIXME how is connectedness defined for the local node - return c.proxy.SendMessage(ctx, msg) // TODO wrap to hide remote + // FIXME how is connectedness defined for the local node + pri := []pb.PeerRoutingInfo{ + pb.PeerRoutingInfo{ + PeerInfo: peer.PeerInfo{ + ID: c.local, + }, + }, + } + msg.ProviderPeers = pb.PeerRoutingInfosToPBPeers(pri) + return c.proxy.SendMessage(ctx, msg) // TODO wrap to hide remote } func (c *Client) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, error) { diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index 767d7e760..8bb8e0d03 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -84,7 +84,13 @@ func (s *Server) handleMessage( case dhtpb.Message_FIND_NODE: p := s.peerstore.PeerInfo(peer.ID(req.GetKey())) - response.CloserPeers = dhtpb.PeerInfosToPBPeers(s.dialer, []peer.PeerInfo{p}) + pri := []dhtpb.PeerRoutingInfo{ + dhtpb.PeerRoutingInfo{ + PeerInfo: p, + // Connectedness: TODO + }, + } + response.CloserPeers = dhtpb.PeerRoutingInfosToPBPeers(pri) return p.ID, response case dhtpb.Message_ADD_PROVIDER: @@ -116,7 +122,13 @@ func (s *Server) handleMessage( dskey := util.Key(req.GetKey()).DsKey() exists, err := s.datastore.Has(dskey) if err == nil && exists { - response.ProviderPeers = append(response.ProviderPeers, dhtpb.PeerInfosToPBPeers(s.dialer, []peer.PeerInfo{peer.PeerInfo{ID: s.local}})...) + pri := []dhtpb.PeerRoutingInfo{ + dhtpb.PeerRoutingInfo{ + // Connectedness: TODO how is connectedness defined for the local node + PeerInfo: peer.PeerInfo{ID: s.local}, + }, + } + response.ProviderPeers = append(response.ProviderPeers, dhtpb.PeerRoutingInfosToPBPeers(pri)...) } // FIXME(btc) is this how we want to persist this data? pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", req.GetKey()}) From ef1281466e07f51802b52b40b814096fd813ced0 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 27 Jan 2015 23:51:15 -0800 Subject: [PATCH 0721/3817] refac(gcr/s,c) remove network/dialer remove dialer from GCR client This commit was moved from ipfs/go-ipfs-routing@e57a9ba4fee56a3b0b1fc3a55c40afcfe4ed57b5 --- routing/grandcentral/client.go | 5 +---- routing/grandcentral/server.go | 6 ++---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/routing/grandcentral/client.go b/routing/grandcentral/client.go index 828653eca..c9acf61a8 100644 --- a/routing/grandcentral/client.go +++ b/routing/grandcentral/client.go @@ -6,7 +6,6 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" @@ -23,14 +22,12 @@ var ErrTODO = errors.New("TODO") type Client struct { peerstore peer.Peerstore proxy proxy.Proxy - dialer inet.Network local peer.ID } // TODO take in datastore/cache -func NewClient(d inet.Network, px proxy.Proxy, ps peer.Peerstore, local peer.ID) (*Client, error) { +func NewClient(px proxy.Proxy, ps peer.Peerstore, local peer.ID) (*Client, error) { return &Client{ - dialer: d, proxy: px, local: local, peerstore: ps, diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index 8bb8e0d03..a855ca661 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -4,7 +4,6 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" datastore "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" proxy "github.com/jbenet/go-ipfs/routing/grandcentral/proxy" @@ -16,14 +15,13 @@ import ( type Server struct { local peer.ID datastore datastore.ThreadSafeDatastore - dialer inet.Network peerstore peer.Peerstore *proxy.Loopback // so server can be injected into client } // NewServer creates a new GrandCentral routing Server -func NewServer(ds datastore.ThreadSafeDatastore, d inet.Network, ps peer.Peerstore, local peer.ID) (*Server, error) { - s := &Server{local, ds, d, ps, nil} +func NewServer(ds datastore.ThreadSafeDatastore, ps peer.Peerstore, local peer.ID) (*Server, error) { + s := &Server{local, ds, ps, nil} s.Loopback = &proxy.Loopback{ Handler: s, Local: local, From 07d781b54808ce48178f993754a6785aa0e45b82 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 30 Jan 2015 08:22:32 -0800 Subject: [PATCH 0722/3817] fix(gcr/s) proto marshaling bugs This commit was moved from ipfs/go-ipfs-routing@d0ff30bae18a1c48fd84639912d4201aca4815a9 --- routing/grandcentral/server.go | 164 +++++++++++++++++++++------------ 1 file changed, 103 insertions(+), 61 deletions(-) diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index a855ca661..309433eff 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -1,6 +1,8 @@ package grandcentral import ( + "fmt" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" datastore "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" @@ -48,36 +50,17 @@ func (s *Server) handleMessage( switch req.GetType() { case dhtpb.Message_GET_VALUE: - dskey := util.Key(req.GetKey()).DsKey() - val, err := s.datastore.Get(dskey) + rawRecord, err := getRoutingRecord(s.datastore, util.Key(req.GetKey())) if err != nil { - log.Debug(errors.Wrap(err)) - return "", nil - } - rawRecord, ok := val.([]byte) - if !ok { - log.Debugf("datastore had non byte-slice value for %v", dskey) - return "", nil - } - if err := proto.Unmarshal(rawRecord, response.Record); err != nil { - log.Debug("failed to unmarshal dht record from datastore") return "", nil } + response.Record = rawRecord // TODO before merging: if we know any providers for the requested value, return those. return p, response case dhtpb.Message_PUT_VALUE: // TODO before merging: verifyRecord(req.GetRecord()) - data, err := proto.Marshal(req.GetRecord()) - if err != nil { - log.Debug(err) - return "", nil - } - dskey := util.Key(req.GetKey()).DsKey() - if err := s.datastore.Put(dskey, data); err != nil { - log.Debug(err) - return "", nil - } + putRoutingRecord(s.datastore, util.Key(req.GetKey()), req.GetRecord()) return p, req // TODO before merging: verify that we should return record case dhtpb.Message_FIND_NODE: @@ -92,51 +75,19 @@ func (s *Server) handleMessage( return p.ID, response case dhtpb.Message_ADD_PROVIDER: - for _, provider := range req.GetProviderPeers() { - providerID := peer.ID(provider.GetId()) - if providerID != p { - log.Debugf("provider message came from third-party %s", p) - continue - } - for _, maddr := range provider.Addresses() { - // FIXME do we actually want to store to peerstore - s.peerstore.AddAddr(p, maddr, peer.TempAddrTTL) - } - } - var providers []dhtpb.Message_Peer - pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", req.GetKey()}) - if v, err := s.datastore.Get(pkey); err == nil { - if protopeers, ok := v.([]dhtpb.Message_Peer); ok { - providers = append(providers, protopeers...) - } - } - if err := s.datastore.Put(pkey, providers); err != nil { - log.Debug(err) + storeProvidersToPeerstore(s.peerstore, p, req.GetProviderPeers()) + + if err := putRoutingProviders(s.datastore, util.Key(req.GetKey()), req.GetProviderPeers()); err != nil { return "", nil } return "", nil case dhtpb.Message_GET_PROVIDERS: - dskey := util.Key(req.GetKey()).DsKey() - exists, err := s.datastore.Has(dskey) - if err == nil && exists { - pri := []dhtpb.PeerRoutingInfo{ - dhtpb.PeerRoutingInfo{ - // Connectedness: TODO how is connectedness defined for the local node - PeerInfo: peer.PeerInfo{ID: s.local}, - }, - } - response.ProviderPeers = append(response.ProviderPeers, dhtpb.PeerRoutingInfosToPBPeers(pri)...) - } - // FIXME(btc) is this how we want to persist this data? - pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", req.GetKey()}) - if v, err := s.datastore.Get(pkey); err == nil { - if protopeers, ok := v.([]dhtpb.Message_Peer); ok { - for _, p := range protopeers { - response.ProviderPeers = append(response.ProviderPeers, &p) - } - } + providers, err := getRoutingProviders(s.local, s.datastore, util.Key(req.GetKey())) + if err != nil { + return "", nil } + response.ProviderPeers = providers return p, response case dhtpb.Message_PING: @@ -148,3 +99,94 @@ func (s *Server) handleMessage( var _ proxy.RequestHandler = &Server{} var _ proxy.Proxy = &Server{} + +func getRoutingRecord(ds datastore.Datastore, k util.Key) (*dhtpb.Record, error) { + dskey := k.DsKey() + val, err := ds.Get(dskey) + if err != nil { + return nil, errors.Wrap(err) + } + recordBytes, ok := val.([]byte) + if !ok { + return nil, fmt.Errorf("datastore had non byte-slice value for %v", dskey) + } + var record dhtpb.Record + if err := proto.Unmarshal(recordBytes, &record); err != nil { + return nil, errors.New("failed to unmarshal dht record from datastore") + } + return &record, nil +} + +func putRoutingRecord(ds datastore.Datastore, k util.Key, value *dhtpb.Record) error { + data, err := proto.Marshal(value) + if err != nil { + return err + } + dskey := k.DsKey() + // TODO namespace + if err := ds.Put(dskey, data); err != nil { + return err + } + return nil +} + +func putRoutingProviders(ds datastore.Datastore, k util.Key, providers []*dhtpb.Message_Peer) error { + pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) + if v, err := ds.Get(pkey); err == nil { + if msg, ok := v.([]byte); ok { + var protomsg dhtpb.Message + if err := proto.Unmarshal(msg, &protomsg); err != nil { + log.Error("failed to unmarshal routing provider record. programmer error") + } else { + providers = append(providers, protomsg.ProviderPeers...) + } + } + } + var protomsg dhtpb.Message + protomsg.ProviderPeers = providers + data, err := proto.Marshal(&protomsg) + if err != nil { + return err + } + return ds.Put(pkey, data) +} + +func storeProvidersToPeerstore(ps peer.Peerstore, p peer.ID, providers []*dhtpb.Message_Peer) { + for _, provider := range providers { + providerID := peer.ID(provider.GetId()) + if providerID != p { + log.Errorf("provider message came from third-party %s", p) + continue + } + for _, maddr := range provider.Addresses() { + // as a router, we want to store addresses for peers who have provided + ps.AddAddr(p, maddr, peer.AddressTTL) + } + } +} + +func getRoutingProviders(local peer.ID, ds datastore.Datastore, k util.Key) ([]*dhtpb.Message_Peer, error) { + var providers []*dhtpb.Message_Peer + exists, err := ds.Has(k.DsKey()) // TODO store values in a local datastore? + if err == nil && exists { + pri := []dhtpb.PeerRoutingInfo{ + dhtpb.PeerRoutingInfo{ + // Connectedness: TODO how is connectedness defined for the local node + PeerInfo: peer.PeerInfo{ID: local}, + }, + } + providers = append(providers, dhtpb.PeerRoutingInfosToPBPeers(pri)...) + } + + pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) // TODO key fmt + if v, err := ds.Get(pkey); err == nil { + if data, ok := v.([]byte); ok { + var msg dhtpb.Message + if err := proto.Unmarshal(data, &msg); err != nil { + return nil, err + } + providers = append(providers, msg.GetProviderPeers()...) + } + } + return providers, nil +} From c3f7439b937b3f464dd0c51e2664724cc02600c1 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 30 Jan 2015 08:21:08 -0800 Subject: [PATCH 0723/3817] misc(gcr/c) rm TODO This commit was moved from ipfs/go-ipfs-routing@8e7501da9f110bbd46c85afe1ebf4a2a58d21e83 --- routing/grandcentral/client.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/routing/grandcentral/client.go b/routing/grandcentral/client.go index c9acf61a8..b7caa2795 100644 --- a/routing/grandcentral/client.go +++ b/routing/grandcentral/client.go @@ -17,8 +17,6 @@ import ( var log = eventlog.Logger("grandcentral") -var ErrTODO = errors.New("TODO") - type Client struct { peerstore peer.Peerstore proxy proxy.Proxy From 855a79721102f861e17023b4ba1b5c3eb94ed3d5 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 28 Jan 2015 05:20:20 -0800 Subject: [PATCH 0724/3817] feat(gcr/c) add support for multiple servers This commit was moved from ipfs/go-ipfs-routing@cb2e956fbcd65dd4db4cc9003e1ce2ba17d6d3e9 --- routing/grandcentral/proxy/standard.go | 45 +++++++++++++++++++------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/routing/grandcentral/proxy/standard.go b/routing/grandcentral/proxy/standard.go index 629f61916..b3d1fc5d8 100644 --- a/routing/grandcentral/proxy/standard.go +++ b/routing/grandcentral/proxy/standard.go @@ -2,13 +2,13 @@ package proxy import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - host "github.com/jbenet/go-ipfs/p2p/host" ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" + host "github.com/jbenet/go-ipfs/p2p/host" inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" - errors "github.com/jbenet/go-ipfs/util/debugerror" eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" + errors "github.com/jbenet/go-ipfs/util/debugerror" ) var log = eventlog.Logger("proxy") @@ -19,21 +19,32 @@ type Proxy interface { } type standard struct { - Host host.Host - Remote peer.ID + Host host.Host + Remotes []peer.ID } -func Standard(h host.Host, remote peer.ID) Proxy { - return &standard{h, remote} +func Standard(h host.Host, remotes []peer.ID) Proxy { + return &standard{h, remotes} } const ProtocolGCR = "/ipfs/grandcentral" func (px *standard) SendMessage(ctx context.Context, m *dhtpb.Message) error { - if err := px.Host.Connect(ctx, peer.PeerInfo{ID: px.Remote}); err != nil { + var err error + for _, remote := range px.Remotes { + if err = px.sendMessage(ctx, m, remote); err != nil { // careful don't re-declare err! + continue + } + return nil // success + } + return err // NB: returns the last error +} + +func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote peer.ID) error { + if err := px.Host.Connect(ctx, peer.PeerInfo{ID: remote}); err != nil { return err } - s, err := px.Host.NewStream(ProtocolGCR, px.Remote) + s, err := px.Host.NewStream(ProtocolGCR, remote) if err != nil { return err } @@ -46,10 +57,23 @@ func (px *standard) SendMessage(ctx context.Context, m *dhtpb.Message) error { } func (px *standard) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) { - if err := px.Host.Connect(ctx, peer.PeerInfo{ID: px.Remote}); err != nil { + var err error + for _, remote := range px.Remotes { + var reply *dhtpb.Message + reply, err = px.sendRequest(ctx, m, remote) // careful don't redeclare err! + if err != nil { + continue + } + return reply, nil // success + } + return nil, err // NB: returns the last error +} + +func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote peer.ID) (*dhtpb.Message, error) { + if err := px.Host.Connect(ctx, peer.PeerInfo{ID: remote}); err != nil { return nil, err } - s, err := px.Host.NewStream(ProtocolGCR, px.Remote) + s, err := px.Host.NewStream(ProtocolGCR, remote) if err != nil { return nil, err } @@ -70,4 +94,3 @@ func (px *standard) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.M } return &reply, nil } - From 717a86209b5d4d57d8621794488bf76ddb8a5e92 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 30 Jan 2015 08:02:43 -0800 Subject: [PATCH 0725/3817] fix(gcr/s,c) register stream handlers This commit was moved from ipfs/go-ipfs-routing@22d8d5c77e18151f2ce6e78f5a83e7a7f2620a56 --- routing/grandcentral/proxy/loopback.go | 2 +- routing/grandcentral/proxy/standard.go | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/routing/grandcentral/proxy/loopback.go b/routing/grandcentral/proxy/loopback.go index ba598c3ea..e5fa39deb 100644 --- a/routing/grandcentral/proxy/loopback.go +++ b/routing/grandcentral/proxy/loopback.go @@ -34,7 +34,7 @@ func (lb *Loopback) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.M return lb.Handler.HandleRequest(ctx, lb.Local, m), nil } -func (lb *Loopback) handleNewStream(s inet.Stream) { +func (lb *Loopback) HandleStream(s inet.Stream) { defer s.Close() pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) var incoming dhtpb.Message diff --git a/routing/grandcentral/proxy/standard.go b/routing/grandcentral/proxy/standard.go index b3d1fc5d8..996f2f5e7 100644 --- a/routing/grandcentral/proxy/standard.go +++ b/routing/grandcentral/proxy/standard.go @@ -13,7 +13,10 @@ import ( var log = eventlog.Logger("proxy") +const ProtocolGCR = "/ipfs/grandcentral" + type Proxy interface { + HandleStream(inet.Stream) SendMessage(ctx context.Context, m *dhtpb.Message) error SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) } @@ -27,7 +30,9 @@ func Standard(h host.Host, remotes []peer.ID) Proxy { return &standard{h, remotes} } -const ProtocolGCR = "/ipfs/grandcentral" +func (p *standard) HandleStream(s inet.Stream) { + panic("client received a GCR message") +} func (px *standard) SendMessage(ctx context.Context, m *dhtpb.Message) error { var err error From 26e90774416b564b210a9434305647727db004eb Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 28 Jan 2015 20:41:12 -0800 Subject: [PATCH 0726/3817] feat(gcr/c) randomize order of remotes This commit was moved from ipfs/go-ipfs-routing@2f0a7ef8fcda721f4c8abb4500934d455e30855b --- routing/grandcentral/proxy/standard.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/routing/grandcentral/proxy/standard.go b/routing/grandcentral/proxy/standard.go index 996f2f5e7..373501ea1 100644 --- a/routing/grandcentral/proxy/standard.go +++ b/routing/grandcentral/proxy/standard.go @@ -1,6 +1,8 @@ package proxy import ( + "math/rand" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" host "github.com/jbenet/go-ipfs/p2p/host" @@ -36,7 +38,8 @@ func (p *standard) HandleStream(s inet.Stream) { func (px *standard) SendMessage(ctx context.Context, m *dhtpb.Message) error { var err error - for _, remote := range px.Remotes { + for _, i := range rand.Perm(len(px.Remotes)) { + remote := px.Remotes[i] if err = px.sendMessage(ctx, m, remote); err != nil { // careful don't re-declare err! continue } @@ -63,7 +66,8 @@ func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote pe func (px *standard) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) { var err error - for _, remote := range px.Remotes { + for _, i := range rand.Perm(len(px.Remotes)) { + remote := px.Remotes[i] var reply *dhtpb.Message reply, err = px.sendRequest(ctx, m, remote) // careful don't redeclare err! if err != nil { From 6a97328c761a30b23147a6a86845eca621847fb0 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 30 Jan 2015 08:05:01 -0800 Subject: [PATCH 0727/3817] refactor(gcr/c) pass host.Host into GCR client This commit was moved from ipfs/go-ipfs-routing@63164978f35332c18beb272676de1cce3d7b68a4 --- routing/grandcentral/client.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/routing/grandcentral/client.go b/routing/grandcentral/client.go index b7caa2795..8b67276b7 100644 --- a/routing/grandcentral/client.go +++ b/routing/grandcentral/client.go @@ -6,6 +6,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + "github.com/jbenet/go-ipfs/p2p/host" peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" @@ -18,17 +19,19 @@ import ( var log = eventlog.Logger("grandcentral") type Client struct { + peerhost host.Host peerstore peer.Peerstore proxy proxy.Proxy local peer.ID } // TODO take in datastore/cache -func NewClient(px proxy.Proxy, ps peer.Peerstore, local peer.ID) (*Client, error) { +func NewClient(px proxy.Proxy, h host.Host, ps peer.Peerstore, local peer.ID) (*Client, error) { return &Client{ proxy: px, local: local, peerstore: ps, + peerhost: h, }, nil } @@ -79,7 +82,8 @@ func (c *Client) Provide(ctx context.Context, k u.Key) error { pri := []pb.PeerRoutingInfo{ pb.PeerRoutingInfo{ PeerInfo: peer.PeerInfo{ - ID: c.local, + ID: c.local, + Addrs: c.peerhost.Addrs(), }, }, } From 12c76fa3238f669dca74fb5c2f9f754346f637ba Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 28 Jan 2015 08:06:53 -0800 Subject: [PATCH 0728/3817] feat(gcr/s) add eventlogs This commit was moved from ipfs/go-ipfs-routing@f7dad1aaa9bc0e8ce0c51dc720a508b254ddf466 --- routing/grandcentral/client.go | 6 ++++++ routing/grandcentral/proxy/standard.go | 30 +++++++++++++++++++------- routing/grandcentral/server.go | 4 ++++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/routing/grandcentral/client.go b/routing/grandcentral/client.go index 8b67276b7..405a7750d 100644 --- a/routing/grandcentral/client.go +++ b/routing/grandcentral/client.go @@ -36,6 +36,7 @@ func NewClient(px proxy.Proxy, h host.Host, ps peer.Peerstore, local peer.ID) (* } func (c *Client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-chan peer.PeerInfo { + defer log.EventBegin(ctx, "findProviders", &k).Done() ch := make(chan peer.PeerInfo) go func() { defer close(ch) @@ -58,6 +59,7 @@ func (c *Client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-cha } func (c *Client) PutValue(ctx context.Context, k u.Key, v []byte) error { + defer log.EventBegin(ctx, "putValue", &k).Done() r, err := makeRecord(c.peerstore, c.local, k, v) if err != nil { return err @@ -68,6 +70,7 @@ func (c *Client) PutValue(ctx context.Context, k u.Key, v []byte) error { } func (c *Client) GetValue(ctx context.Context, k u.Key) ([]byte, error) { + defer log.EventBegin(ctx, "getValue", &k).Done() msg := pb.NewMessage(pb.Message_GET_VALUE, string(k), 0) response, err := c.proxy.SendRequest(ctx, msg) // TODO wrap to hide the remote if err != nil { @@ -77,6 +80,7 @@ func (c *Client) GetValue(ctx context.Context, k u.Key) ([]byte, error) { } func (c *Client) Provide(ctx context.Context, k u.Key) error { + defer log.EventBegin(ctx, "provide", &k).Done() msg := pb.NewMessage(pb.Message_ADD_PROVIDER, string(k), 0) // FIXME how is connectedness defined for the local node pri := []pb.PeerRoutingInfo{ @@ -92,6 +96,7 @@ func (c *Client) Provide(ctx context.Context, k u.Key) error { } func (c *Client) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, error) { + defer log.EventBegin(ctx, "findPeer", id).Done() request := pb.NewMessage(pb.Message_FIND_NODE, string(id), 0) response, err := c.proxy.SendRequest(ctx, request) // hide remote if err != nil { @@ -121,6 +126,7 @@ func makeRecord(ps peer.Peerstore, p peer.ID, k u.Key, v []byte) (*pb.Record, er } func (c *Client) Ping(ctx context.Context, id peer.ID) (time.Duration, error) { + defer log.EventBegin(ctx, "ping", id).Done() return time.Nanosecond, errors.New("grandcentral routing does not support the ping method") } diff --git a/routing/grandcentral/proxy/standard.go b/routing/grandcentral/proxy/standard.go index 373501ea1..36da88cbf 100644 --- a/routing/grandcentral/proxy/standard.go +++ b/routing/grandcentral/proxy/standard.go @@ -13,10 +13,10 @@ import ( errors "github.com/jbenet/go-ipfs/util/debugerror" ) -var log = eventlog.Logger("proxy") - const ProtocolGCR = "/ipfs/grandcentral" +var log = eventlog.Logger("grandcentral/proxy") + type Proxy interface { HandleStream(inet.Stream) SendMessage(ctx context.Context, m *dhtpb.Message) error @@ -48,8 +48,15 @@ func (px *standard) SendMessage(ctx context.Context, m *dhtpb.Message) error { return err // NB: returns the last error } -func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote peer.ID) error { - if err := px.Host.Connect(ctx, peer.PeerInfo{ID: remote}); err != nil { +func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote peer.ID) (err error) { + e := log.EventBegin(ctx, "sendRoutingMessage", px.Host.ID(), remote, m) + defer func() { + if err != nil { + e.SetError(err) + } + e.Done() + }() + if err = px.Host.Connect(ctx, peer.PeerInfo{ID: remote}); err != nil { return err } s, err := px.Host.NewStream(ProtocolGCR, remote) @@ -78,8 +85,15 @@ func (px *standard) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.M return nil, err // NB: returns the last error } -func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote peer.ID) (*dhtpb.Message, error) { - if err := px.Host.Connect(ctx, peer.PeerInfo{ID: remote}); err != nil { +func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote peer.ID) (_ *dhtpb.Message, err error) { + e := log.EventBegin(ctx, "sendRoutingRequest", px.Host.ID(), remote, m) + defer func() { + if err != nil { + e.SetError(err) + } + e.Done() + }() + if err = px.Host.Connect(ctx, peer.PeerInfo{ID: remote}); err != nil { return nil, err } s, err := px.Host.NewStream(ProtocolGCR, remote) @@ -89,12 +103,12 @@ func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote pe defer s.Close() r := ggio.NewDelimitedReader(s, inet.MessageSizeMax) w := ggio.NewDelimitedWriter(s) - if err := w.WriteMsg(m); err != nil { + if err = w.WriteMsg(m); err != nil { return nil, err } var reply dhtpb.Message - if err := r.ReadMsg(&reply); err != nil { + if err = r.ReadMsg(&reply); err != nil { return nil, err } // need ctx expiration? diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index 309433eff..62198f8cb 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -42,6 +42,8 @@ func (s *Server) HandleRequest(ctx context.Context, p peer.ID, req *dhtpb.Messag func (s *Server) handleMessage( ctx context.Context, p peer.ID, req *dhtpb.Message) (peer.ID, *dhtpb.Message) { + log.EventBegin(ctx, "routingMessageReceived", req, p, s.local).Done() // TODO may need to differentiate between local and remote + // FIXME threw everything into this switch statement to get things going. // Once each operation is well-defined, extract pluggable backend so any // database may be used. @@ -131,6 +133,7 @@ func putRoutingRecord(ds datastore.Datastore, k util.Key, value *dhtpb.Record) e } func putRoutingProviders(ds datastore.Datastore, k util.Key, providers []*dhtpb.Message_Peer) error { + log.Event(context.Background(), "putRoutingProviders", &k) pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) if v, err := ds.Get(pkey); err == nil { if msg, ok := v.([]byte); ok { @@ -166,6 +169,7 @@ func storeProvidersToPeerstore(ps peer.Peerstore, p peer.ID, providers []*dhtpb. } func getRoutingProviders(local peer.ID, ds datastore.Datastore, k util.Key) ([]*dhtpb.Message_Peer, error) { + log.Event(context.Background(), "getProviders", local, &k) var providers []*dhtpb.Message_Peer exists, err := ds.Has(k.DsKey()) // TODO store values in a local datastore? if err == nil && exists { From 19948119b9f34228718773655075ba4cbb97b9c7 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 29 Jan 2015 23:47:12 -0800 Subject: [PATCH 0729/3817] fix(gcr/c) print a loud error when clients receive routing messages, but don't panic This is an unhandled case. Right now, we close the stream without reading. Should clients be able to satisfy routing requests? @jbenet @whyrusleeping This commit was moved from ipfs/go-ipfs-routing@8dd6c4f7b2ad8e9086965352f4c056c4ef36efe0 --- routing/grandcentral/proxy/standard.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routing/grandcentral/proxy/standard.go b/routing/grandcentral/proxy/standard.go index 36da88cbf..393f9d303 100644 --- a/routing/grandcentral/proxy/standard.go +++ b/routing/grandcentral/proxy/standard.go @@ -33,7 +33,9 @@ func Standard(h host.Host, remotes []peer.ID) Proxy { } func (p *standard) HandleStream(s inet.Stream) { - panic("client received a GCR message") + // TODO(brian): Should clients be able to satisfy requests? + log.Error("grandcentral client received (dropped) a routing message from", s.Conn().RemotePeer()) + s.Close() } func (px *standard) SendMessage(ctx context.Context, m *dhtpb.Message) error { From 10c0244f017395d0bfeb6a151675d80f37c44d79 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 1 Feb 2015 22:49:14 -0800 Subject: [PATCH 0730/3817] log(gcr/s,c) add addtional eventlogs This commit was moved from ipfs/go-ipfs-routing@7fdd65ab0ff278b647b1e6dd49aca0f26fa2b042 --- routing/grandcentral/client.go | 1 + routing/grandcentral/proxy/standard.go | 31 ++++++++++++++------------ routing/grandcentral/server.go | 3 ++- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/routing/grandcentral/client.go b/routing/grandcentral/client.go index 405a7750d..99a507983 100644 --- a/routing/grandcentral/client.go +++ b/routing/grandcentral/client.go @@ -36,6 +36,7 @@ func NewClient(px proxy.Proxy, h host.Host, ps peer.Peerstore, local peer.ID) (* } func (c *Client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-chan peer.PeerInfo { + ctx = eventlog.ContextWithLoggable(ctx, eventlog.Uuid("findProviders")) defer log.EventBegin(ctx, "findProviders", &k).Done() ch := make(chan peer.PeerInfo) go func() { diff --git a/routing/grandcentral/proxy/standard.go b/routing/grandcentral/proxy/standard.go index 393f9d303..3fe33616f 100644 --- a/routing/grandcentral/proxy/standard.go +++ b/routing/grandcentral/proxy/standard.go @@ -87,35 +87,38 @@ func (px *standard) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.M return nil, err // NB: returns the last error } -func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote peer.ID) (_ *dhtpb.Message, err error) { - e := log.EventBegin(ctx, "sendRoutingRequest", px.Host.ID(), remote, m) - defer func() { - if err != nil { - e.SetError(err) - } - e.Done() - }() - if err = px.Host.Connect(ctx, peer.PeerInfo{ID: remote}); err != nil { +func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote peer.ID) (*dhtpb.Message, error) { + e := log.EventBegin(ctx, "sendRoutingRequest", px.Host.ID(), remote, eventlog.Pair("request", m)) + defer e.Done() + if err := px.Host.Connect(ctx, peer.PeerInfo{ID: remote}); err != nil { + e.SetError(err) return nil, err } s, err := px.Host.NewStream(ProtocolGCR, remote) if err != nil { + e.SetError(err) return nil, err } defer s.Close() r := ggio.NewDelimitedReader(s, inet.MessageSizeMax) w := ggio.NewDelimitedWriter(s) if err = w.WriteMsg(m); err != nil { + e.SetError(err) return nil, err } - var reply dhtpb.Message - if err = r.ReadMsg(&reply); err != nil { + response := &dhtpb.Message{} + if err = r.ReadMsg(response); err != nil { + e.SetError(err) return nil, err } // need ctx expiration? - if &reply == nil { - return nil, errors.New("no response to request") + if response == nil { + err := errors.New("no response to request") + e.SetError(err) + return nil, err } - return &reply, nil + e.Append(eventlog.Pair("response", response)) + e.Append(eventlog.Pair("uuid", eventlog.Uuid("foo"))) + return response, nil } diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index 62198f8cb..d35ad97bd 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -169,7 +169,8 @@ func storeProvidersToPeerstore(ps peer.Peerstore, p peer.ID, providers []*dhtpb. } func getRoutingProviders(local peer.ID, ds datastore.Datastore, k util.Key) ([]*dhtpb.Message_Peer, error) { - log.Event(context.Background(), "getProviders", local, &k) + e := log.EventBegin(context.Background(), "getProviders", &k) + defer e.Done() var providers []*dhtpb.Message_Peer exists, err := ds.Has(k.DsKey()) // TODO store values in a local datastore? if err == nil && exists { From c5f29457c66869aea7cf758f871177784ba3321b Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 2 Feb 2015 01:49:55 -0800 Subject: [PATCH 0731/3817] fix(gcr/s) rename datastore to routing backend This commit was moved from ipfs/go-ipfs-routing@b9e3535741bc11842fb19df50a9266b0a1bb6a7f --- routing/grandcentral/server.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index d35ad97bd..fdeef62d0 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -16,7 +16,7 @@ import ( // Server handles routing queries using a database backend type Server struct { local peer.ID - datastore datastore.ThreadSafeDatastore + routingBackend datastore.ThreadSafeDatastore peerstore peer.Peerstore *proxy.Loopback // so server can be injected into client } @@ -52,7 +52,7 @@ func (s *Server) handleMessage( switch req.GetType() { case dhtpb.Message_GET_VALUE: - rawRecord, err := getRoutingRecord(s.datastore, util.Key(req.GetKey())) + rawRecord, err := getRoutingRecord(s.routingBackend, util.Key(req.GetKey())) if err != nil { return "", nil } @@ -62,7 +62,7 @@ func (s *Server) handleMessage( case dhtpb.Message_PUT_VALUE: // TODO before merging: verifyRecord(req.GetRecord()) - putRoutingRecord(s.datastore, util.Key(req.GetKey()), req.GetRecord()) + putRoutingRecord(s.routingBackend, util.Key(req.GetKey()), req.GetRecord()) return p, req // TODO before merging: verify that we should return record case dhtpb.Message_FIND_NODE: @@ -79,13 +79,13 @@ func (s *Server) handleMessage( case dhtpb.Message_ADD_PROVIDER: storeProvidersToPeerstore(s.peerstore, p, req.GetProviderPeers()) - if err := putRoutingProviders(s.datastore, util.Key(req.GetKey()), req.GetProviderPeers()); err != nil { + if err := putRoutingProviders(s.routingBackend, util.Key(req.GetKey()), req.GetProviderPeers()); err != nil { return "", nil } return "", nil case dhtpb.Message_GET_PROVIDERS: - providers, err := getRoutingProviders(s.local, s.datastore, util.Key(req.GetKey())) + providers, err := getRoutingProviders(s.local, s.routingBackend, util.Key(req.GetKey())) if err != nil { return "", nil } From c041a05bef4bf10870387707f6ef20097b0bc4ee Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 2 Feb 2015 01:52:53 -0800 Subject: [PATCH 0732/3817] misc(gcr/s) rm TODO This commit was moved from ipfs/go-ipfs-routing@0785ef75a1910f376907cf4832bf3c813f29ba1c --- routing/grandcentral/server.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index fdeef62d0..663f2562a 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -44,10 +44,6 @@ func (s *Server) handleMessage( log.EventBegin(ctx, "routingMessageReceived", req, p, s.local).Done() // TODO may need to differentiate between local and remote - // FIXME threw everything into this switch statement to get things going. - // Once each operation is well-defined, extract pluggable backend so any - // database may be used. - var response = dhtpb.NewMessage(req.GetType(), req.GetKey(), req.GetClusterLevel()) switch req.GetType() { From 4733b4a4bcf28b0659e578cab9e18333897b99b5 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 2 Feb 2015 02:05:40 -0800 Subject: [PATCH 0733/3817] fix: don't check routingbackend for value This commit was moved from ipfs/go-ipfs-routing@082ebd2a0ef8e7cfc6b839eb57098cfef9eb3990 --- routing/grandcentral/server.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index 663f2562a..19facfaa2 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -168,17 +168,6 @@ func getRoutingProviders(local peer.ID, ds datastore.Datastore, k util.Key) ([]* e := log.EventBegin(context.Background(), "getProviders", &k) defer e.Done() var providers []*dhtpb.Message_Peer - exists, err := ds.Has(k.DsKey()) // TODO store values in a local datastore? - if err == nil && exists { - pri := []dhtpb.PeerRoutingInfo{ - dhtpb.PeerRoutingInfo{ - // Connectedness: TODO how is connectedness defined for the local node - PeerInfo: peer.PeerInfo{ID: local}, - }, - } - providers = append(providers, dhtpb.PeerRoutingInfosToPBPeers(pri)...) - } - pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) // TODO key fmt if v, err := ds.Get(pkey); err == nil { if data, ok := v.([]byte); ok { From 41abe43e99c4873ea6568e3b10dfc8503f389ff7 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 2 Feb 2015 02:08:14 -0800 Subject: [PATCH 0734/3817] misc(gcr/s) add doc This commit was moved from ipfs/go-ipfs-routing@32f3101360e996752452c5739c38adaf78db5871 --- routing/grandcentral/server.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index 19facfaa2..5af3b431d 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -73,6 +73,9 @@ func (s *Server) handleMessage( return p.ID, response case dhtpb.Message_ADD_PROVIDER: + // FIXME(btc): do we want to store these locally? I think the + // storeProvidersToPeerstore behavior is straight from the DHT message + // handler. storeProvidersToPeerstore(s.peerstore, p, req.GetProviderPeers()) if err := putRoutingProviders(s.routingBackend, util.Key(req.GetKey()), req.GetProviderPeers()); err != nil { From d7ab668a53a7d02306c59fc0eee1ab5eeaa95b2b Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 2 Feb 2015 02:10:50 -0800 Subject: [PATCH 0735/3817] misc(gcr/s) rm unused param This commit was moved from ipfs/go-ipfs-routing@9eddd786dfd7104767cf1fbf1778f16c1ef8f37e --- routing/grandcentral/server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index 5af3b431d..6d0a0b0f2 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -84,7 +84,7 @@ func (s *Server) handleMessage( return "", nil case dhtpb.Message_GET_PROVIDERS: - providers, err := getRoutingProviders(s.local, s.routingBackend, util.Key(req.GetKey())) + providers, err := getRoutingProviders(s.routingBackend, util.Key(req.GetKey())) if err != nil { return "", nil } @@ -167,7 +167,7 @@ func storeProvidersToPeerstore(ps peer.Peerstore, p peer.ID, providers []*dhtpb. } } -func getRoutingProviders(local peer.ID, ds datastore.Datastore, k util.Key) ([]*dhtpb.Message_Peer, error) { +func getRoutingProviders(ds datastore.Datastore, k util.Key) ([]*dhtpb.Message_Peer, error) { e := log.EventBegin(context.Background(), "getProviders", &k) defer e.Done() var providers []*dhtpb.Message_Peer From e1a8e413316d004091b1b07c8c47934af09ecd30 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 2 Feb 2015 02:24:38 -0800 Subject: [PATCH 0736/3817] refactor(gcr/s) re-use code from get This commit was moved from ipfs/go-ipfs-routing@be74d28b4cb2c2833ca559e4a469b8575ec0e04f --- routing/grandcentral/server.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index 6d0a0b0f2..f1c8f8d41 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -134,16 +134,11 @@ func putRoutingRecord(ds datastore.Datastore, k util.Key, value *dhtpb.Record) e func putRoutingProviders(ds datastore.Datastore, k util.Key, providers []*dhtpb.Message_Peer) error { log.Event(context.Background(), "putRoutingProviders", &k) pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) - if v, err := ds.Get(pkey); err == nil { - if msg, ok := v.([]byte); ok { - var protomsg dhtpb.Message - if err := proto.Unmarshal(msg, &protomsg); err != nil { - log.Error("failed to unmarshal routing provider record. programmer error") - } else { - providers = append(providers, protomsg.ProviderPeers...) - } - } + old, err := getRoutingProviders(ds, k) + if err != nil { + return err } + providers = append(providers, old...) var protomsg dhtpb.Message protomsg.ProviderPeers = providers data, err := proto.Marshal(&protomsg) From 33bd2724e5ae4ea24486379ef31ecda2494f0b3e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 2 Feb 2015 02:44:45 -0800 Subject: [PATCH 0737/3817] fix(gcr/s) de-duplicate routing records This commit was moved from ipfs/go-ipfs-routing@1a8177b50e8f211423065473a1bb567a127852db --- routing/grandcentral/server.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index f1c8f8d41..4dbe79feb 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -131,16 +131,25 @@ func putRoutingRecord(ds datastore.Datastore, k util.Key, value *dhtpb.Record) e return nil } -func putRoutingProviders(ds datastore.Datastore, k util.Key, providers []*dhtpb.Message_Peer) error { +func putRoutingProviders(ds datastore.Datastore, k util.Key, newRecords []*dhtpb.Message_Peer) error { log.Event(context.Background(), "putRoutingProviders", &k) pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) - old, err := getRoutingProviders(ds, k) + oldRecords, err := getRoutingProviders(ds, k) if err != nil { return err } - providers = append(providers, old...) + mergedRecords := make(map[string]*dhtpb.Message_Peer) + for _, provider := range oldRecords { + mergedRecords[provider.GetId()] = provider // add original records + } + for _, provider := range newRecords { + mergedRecords[provider.GetId()] = provider // overwrite old record if new exists + } var protomsg dhtpb.Message - protomsg.ProviderPeers = providers + protomsg.ProviderPeers = make([]*dhtpb.Message_Peer, 0, len(mergedRecords)) + for _, provider := range mergedRecords { + protomsg.ProviderPeers = append(protomsg.ProviderPeers, provider) + } data, err := proto.Marshal(&protomsg) if err != nil { return err From 471d082b2a2db4e69986f25995874f15ca4eee59 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 2 Feb 2015 02:45:28 -0800 Subject: [PATCH 0738/3817] refactor(gcr/s) move declaration This commit was moved from ipfs/go-ipfs-routing@d9e4555231d784209ad2eb3a02f58974d43d88e6 --- routing/grandcentral/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index 4dbe79feb..d8ad538ed 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -133,7 +133,6 @@ func putRoutingRecord(ds datastore.Datastore, k util.Key, value *dhtpb.Record) e func putRoutingProviders(ds datastore.Datastore, k util.Key, newRecords []*dhtpb.Message_Peer) error { log.Event(context.Background(), "putRoutingProviders", &k) - pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) oldRecords, err := getRoutingProviders(ds, k) if err != nil { return err @@ -154,6 +153,7 @@ func putRoutingProviders(ds datastore.Datastore, k util.Key, newRecords []*dhtpb if err != nil { return err } + pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) return ds.Put(pkey, data) } From 47dd319538c3997ca2b1720fdf8c1389a7b501da Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 2 Feb 2015 02:48:01 -0800 Subject: [PATCH 0739/3817] refactor(gcr/s) extract provider key This commit was moved from ipfs/go-ipfs-routing@2ea9449484045bab0408e4062473914005e6a609 --- routing/grandcentral/server.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index d8ad538ed..55eb703ea 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -153,8 +153,7 @@ func putRoutingProviders(ds datastore.Datastore, k util.Key, newRecords []*dhtpb if err != nil { return err } - pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) - return ds.Put(pkey, data) + return ds.Put(providerKey(k), data) } func storeProvidersToPeerstore(ps peer.Peerstore, p peer.ID, providers []*dhtpb.Message_Peer) { @@ -175,8 +174,7 @@ func getRoutingProviders(ds datastore.Datastore, k util.Key) ([]*dhtpb.Message_P e := log.EventBegin(context.Background(), "getProviders", &k) defer e.Done() var providers []*dhtpb.Message_Peer - pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) // TODO key fmt - if v, err := ds.Get(pkey); err == nil { + if v, err := ds.Get(providerKey(k)); err == nil { if data, ok := v.([]byte); ok { var msg dhtpb.Message if err := proto.Unmarshal(data, &msg); err != nil { @@ -187,3 +185,7 @@ func getRoutingProviders(ds datastore.Datastore, k util.Key) ([]*dhtpb.Message_P } return providers, nil } + +func providerKey(k util.Key) datastore.Key { + return datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) +} From 3930d690461ab61a1d184f1afc09d36491bd7e06 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 2 Feb 2015 06:08:20 -0800 Subject: [PATCH 0740/3817] doc(gcr/c) comment methods This commit was moved from ipfs/go-ipfs-routing@cb6b1d41da7ab38f62a00b08114b1707e49a37c9 --- routing/grandcentral/proxy/standard.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/routing/grandcentral/proxy/standard.go b/routing/grandcentral/proxy/standard.go index 3fe33616f..f32b58800 100644 --- a/routing/grandcentral/proxy/standard.go +++ b/routing/grandcentral/proxy/standard.go @@ -38,6 +38,9 @@ func (p *standard) HandleStream(s inet.Stream) { s.Close() } +// SendMessage sends message to each remote sequentially (randomized order), +// stopping after the first successful response. If all fail, returns the last +// error. func (px *standard) SendMessage(ctx context.Context, m *dhtpb.Message) error { var err error for _, i := range rand.Perm(len(px.Remotes)) { @@ -73,6 +76,9 @@ func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote pe return nil } +// SendRequest sends the request to each remote sequentially (randomized order), +// stopping after the first successful response. If all fail, returns the last +// error. func (px *standard) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) { var err error for _, i := range rand.Perm(len(px.Remotes)) { From 71918965441c372a931d246ed9810fe66972acbc Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 2 Feb 2015 06:09:21 -0800 Subject: [PATCH 0741/3817] fix(gcr/s) defer log event This commit was moved from ipfs/go-ipfs-routing@a5ba41507016fb190ca41409e5809b480c282198 --- routing/grandcentral/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index 55eb703ea..ebec06203 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -42,7 +42,7 @@ func (s *Server) HandleRequest(ctx context.Context, p peer.ID, req *dhtpb.Messag func (s *Server) handleMessage( ctx context.Context, p peer.ID, req *dhtpb.Message) (peer.ID, *dhtpb.Message) { - log.EventBegin(ctx, "routingMessageReceived", req, p, s.local).Done() // TODO may need to differentiate between local and remote + defer log.EventBegin(ctx, "routingMessageReceived", req, p, s.local).Done() // TODO may need to differentiate between local and remote var response = dhtpb.NewMessage(req.GetType(), req.GetKey(), req.GetClusterLevel()) switch req.GetType() { From f35f17be55a8af86550ade12a071400b7bae7a57 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 3 Feb 2015 14:25:22 -0800 Subject: [PATCH 0742/3817] misc(gcr/s) rm TODO This commit was moved from ipfs/go-ipfs-routing@53d41d96eba5072a5b73b78b6e264124fc3a1a1e --- routing/grandcentral/server.go | 1 - 1 file changed, 1 deletion(-) diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index ebec06203..8e11a667d 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -38,7 +38,6 @@ func (s *Server) HandleRequest(ctx context.Context, p peer.ID, req *dhtpb.Messag return response } -// TODO extract backend. backend can be implemented with whatever database we desire func (s *Server) handleMessage( ctx context.Context, p peer.ID, req *dhtpb.Message) (peer.ID, *dhtpb.Message) { From 0178377af9e7ea490e929b0ecca393721e939ccb Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 3 Feb 2015 14:25:52 -0800 Subject: [PATCH 0743/3817] log(gcr/s) remove local peer in message-received event This commit was moved from ipfs/go-ipfs-routing@10b4313b8e7348a8c63d649cc6b4cbece5c52567 --- routing/grandcentral/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index 8e11a667d..fafa5014a 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -41,7 +41,7 @@ func (s *Server) HandleRequest(ctx context.Context, p peer.ID, req *dhtpb.Messag func (s *Server) handleMessage( ctx context.Context, p peer.ID, req *dhtpb.Message) (peer.ID, *dhtpb.Message) { - defer log.EventBegin(ctx, "routingMessageReceived", req, p, s.local).Done() // TODO may need to differentiate between local and remote + defer log.EventBegin(ctx, "routingMessageReceived", req, p).Done() var response = dhtpb.NewMessage(req.GetType(), req.GetKey(), req.GetClusterLevel()) switch req.GetType() { From 4e4ab8e7de8188bd2d0299d1996cf2a7b18ea8ec Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 3 Feb 2015 14:29:28 -0800 Subject: [PATCH 0744/3817] remove TODO @jbenet when returning values for records, when would it make sense to also return providers for the records? This commit was moved from ipfs/go-ipfs-routing@f1f8d4c75d077b6391615af018e37ba32619a655 --- routing/grandcentral/server.go | 1 - 1 file changed, 1 deletion(-) diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index fafa5014a..c8a9707b4 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -52,7 +52,6 @@ func (s *Server) handleMessage( return "", nil } response.Record = rawRecord - // TODO before merging: if we know any providers for the requested value, return those. return p, response case dhtpb.Message_PUT_VALUE: From 5acfd7321e71ed8c2dc5a7d3cba3e2111ab48e77 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 3 Feb 2015 14:30:58 -0800 Subject: [PATCH 0745/3817] rm TODO (there's still one for verifying records) This commit was moved from ipfs/go-ipfs-routing@da8ed1c73adb6541d670efcaedeb2118282480a3 --- routing/grandcentral/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/grandcentral/server.go b/routing/grandcentral/server.go index c8a9707b4..6d18968d4 100644 --- a/routing/grandcentral/server.go +++ b/routing/grandcentral/server.go @@ -57,7 +57,7 @@ func (s *Server) handleMessage( case dhtpb.Message_PUT_VALUE: // TODO before merging: verifyRecord(req.GetRecord()) putRoutingRecord(s.routingBackend, util.Key(req.GetKey()), req.GetRecord()) - return p, req // TODO before merging: verify that we should return record + return p, req case dhtpb.Message_FIND_NODE: p := s.peerstore.PeerInfo(peer.ID(req.GetKey())) From c9151f0f17240e0b9c439e5020d5414f023f1269 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 4 Feb 2015 04:07:30 -0800 Subject: [PATCH 0746/3817] refactor(routing) rename grandcentral to supernode thanks @mappum remove .go-ipfs This commit was moved from ipfs/go-ipfs-routing@bb047389741c40e6d9ce4687cffcd0f2732ebcaa --- routing/{grandcentral => supernode}/client.go | 8 ++++---- routing/{grandcentral => supernode}/proxy/loopback.go | 0 routing/{grandcentral => supernode}/proxy/standard.go | 10 +++++----- routing/{grandcentral => supernode}/server.go | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) rename routing/{grandcentral => supernode}/client.go (94%) rename routing/{grandcentral => supernode}/proxy/loopback.go (100%) rename routing/{grandcentral => supernode}/proxy/standard.go (92%) rename routing/{grandcentral => supernode}/server.go (97%) diff --git a/routing/grandcentral/client.go b/routing/supernode/client.go similarity index 94% rename from routing/grandcentral/client.go rename to routing/supernode/client.go index 99a507983..96b58681e 100644 --- a/routing/grandcentral/client.go +++ b/routing/supernode/client.go @@ -1,4 +1,4 @@ -package grandcentral +package supernode import ( "bytes" @@ -10,13 +10,13 @@ import ( peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" pb "github.com/jbenet/go-ipfs/routing/dht/pb" - proxy "github.com/jbenet/go-ipfs/routing/grandcentral/proxy" + proxy "github.com/jbenet/go-ipfs/routing/supernode/proxy" eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" u "github.com/jbenet/go-ipfs/util" errors "github.com/jbenet/go-ipfs/util/debugerror" ) -var log = eventlog.Logger("grandcentral") +var log = eventlog.Logger("supernode") type Client struct { peerhost host.Host @@ -128,7 +128,7 @@ func makeRecord(ps peer.Peerstore, p peer.ID, k u.Key, v []byte) (*pb.Record, er func (c *Client) Ping(ctx context.Context, id peer.ID) (time.Duration, error) { defer log.EventBegin(ctx, "ping", id).Done() - return time.Nanosecond, errors.New("grandcentral routing does not support the ping method") + return time.Nanosecond, errors.New("supernode routing does not support the ping method") } var _ routing.IpfsRouting = &Client{} diff --git a/routing/grandcentral/proxy/loopback.go b/routing/supernode/proxy/loopback.go similarity index 100% rename from routing/grandcentral/proxy/loopback.go rename to routing/supernode/proxy/loopback.go diff --git a/routing/grandcentral/proxy/standard.go b/routing/supernode/proxy/standard.go similarity index 92% rename from routing/grandcentral/proxy/standard.go rename to routing/supernode/proxy/standard.go index f32b58800..1918e344a 100644 --- a/routing/grandcentral/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -13,9 +13,9 @@ import ( errors "github.com/jbenet/go-ipfs/util/debugerror" ) -const ProtocolGCR = "/ipfs/grandcentral" +const ProtocolSNR = "/ipfs/supernoderouting" -var log = eventlog.Logger("grandcentral/proxy") +var log = eventlog.Logger("supernode/proxy") type Proxy interface { HandleStream(inet.Stream) @@ -34,7 +34,7 @@ func Standard(h host.Host, remotes []peer.ID) Proxy { func (p *standard) HandleStream(s inet.Stream) { // TODO(brian): Should clients be able to satisfy requests? - log.Error("grandcentral client received (dropped) a routing message from", s.Conn().RemotePeer()) + log.Error("supernode client received (dropped) a routing message from", s.Conn().RemotePeer()) s.Close() } @@ -64,7 +64,7 @@ func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote pe if err = px.Host.Connect(ctx, peer.PeerInfo{ID: remote}); err != nil { return err } - s, err := px.Host.NewStream(ProtocolGCR, remote) + s, err := px.Host.NewStream(ProtocolSNR, remote) if err != nil { return err } @@ -100,7 +100,7 @@ func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote pe e.SetError(err) return nil, err } - s, err := px.Host.NewStream(ProtocolGCR, remote) + s, err := px.Host.NewStream(ProtocolSNR, remote) if err != nil { e.SetError(err) return nil, err diff --git a/routing/grandcentral/server.go b/routing/supernode/server.go similarity index 97% rename from routing/grandcentral/server.go rename to routing/supernode/server.go index 6d18968d4..f5efb77ef 100644 --- a/routing/grandcentral/server.go +++ b/routing/supernode/server.go @@ -1,4 +1,4 @@ -package grandcentral +package supernode import ( "fmt" @@ -8,7 +8,7 @@ import ( datastore "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" peer "github.com/jbenet/go-ipfs/p2p/peer" dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" - proxy "github.com/jbenet/go-ipfs/routing/grandcentral/proxy" + proxy "github.com/jbenet/go-ipfs/routing/supernode/proxy" util "github.com/jbenet/go-ipfs/util" errors "github.com/jbenet/go-ipfs/util/debugerror" ) @@ -21,7 +21,7 @@ type Server struct { *proxy.Loopback // so server can be injected into client } -// NewServer creates a new GrandCentral routing Server +// NewServer creates a new Supernode routing Server func NewServer(ds datastore.ThreadSafeDatastore, ps peer.Peerstore, local peer.ID) (*Server, error) { s := &Server{local, ds, ps, nil} s.Loopback = &proxy.Loopback{ From ccbbbfc894afd0015ae95f909faf0e15c8a8e622 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 4 Feb 2015 04:20:33 -0800 Subject: [PATCH 0747/3817] remove todo this functionality is here as an optimization This commit was moved from ipfs/go-ipfs-routing@f55e55078318e99f8427f840e81153916dafd09e --- routing/supernode/server.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index f5efb77ef..ee6ae678f 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -71,9 +71,6 @@ func (s *Server) handleMessage( return p.ID, response case dhtpb.Message_ADD_PROVIDER: - // FIXME(btc): do we want to store these locally? I think the - // storeProvidersToPeerstore behavior is straight from the DHT message - // handler. storeProvidersToPeerstore(s.peerstore, p, req.GetProviderPeers()) if err := putRoutingProviders(s.routingBackend, util.Key(req.GetKey()), req.GetProviderPeers()); err != nil { From 00904ed13362994db8d55ba83aa1f5447dbb4bd6 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 4 Feb 2015 04:27:14 -0800 Subject: [PATCH 0748/3817] ensure we only accept AddProvider records if the peer is the sender This commit was moved from ipfs/go-ipfs-routing@835438e88223e4768631c90946042e05f88615b0 --- routing/supernode/server.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index ee6ae678f..cbf240a19 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -71,10 +71,17 @@ func (s *Server) handleMessage( return p.ID, response case dhtpb.Message_ADD_PROVIDER: - storeProvidersToPeerstore(s.peerstore, p, req.GetProviderPeers()) - - if err := putRoutingProviders(s.routingBackend, util.Key(req.GetKey()), req.GetProviderPeers()); err != nil { - return "", nil + for _, provider := range req.GetProviderPeers() { + providerID := peer.ID(provider.GetId()) + if providerID == p { + store := []*dhtpb.Message_Peer{provider} + storeProvidersToPeerstore(s.peerstore, p, store) + if err := putRoutingProviders(s.routingBackend, util.Key(req.GetKey()), store); err != nil { + return "", nil + } + } else { + log.Event(ctx, "addProviderBadRequest", p, req) + } } return "", nil From a568030f81d39e9ef0b744ce4957eec978b23b12 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 4 Feb 2015 15:17:42 -0800 Subject: [PATCH 0749/3817] test GetPutRecord validate doesn't work. the peer's public key is not present in the peerstore. This commit was moved from ipfs/go-ipfs-routing@1396c1ab9ea1db1dc182e897599c572ec6b9c6df --- routing/supernode/server.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index cbf240a19..78a686ebe 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,6 +8,7 @@ import ( datastore "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" peer "github.com/jbenet/go-ipfs/p2p/peer" dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" + record "github.com/jbenet/go-ipfs/routing/record" proxy "github.com/jbenet/go-ipfs/routing/supernode/proxy" util "github.com/jbenet/go-ipfs/util" errors "github.com/jbenet/go-ipfs/util/debugerror" @@ -55,7 +56,12 @@ func (s *Server) handleMessage( return p, response case dhtpb.Message_PUT_VALUE: - // TODO before merging: verifyRecord(req.GetRecord()) + // FIXME: verify complains that the peer's ID is not present in the + // peerstore. Mocknet problem? + // if err := verify(s.peerstore, req.GetRecord()); err != nil { + // log.Event(ctx, "validationFailed", req, p) + // return "", nil + // } putRoutingRecord(s.routingBackend, util.Key(req.GetKey()), req.GetRecord()) return p, req @@ -191,3 +197,17 @@ func getRoutingProviders(ds datastore.Datastore, k util.Key) ([]*dhtpb.Message_P func providerKey(k util.Key) datastore.Key { return datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) } + +func verify(ps peer.Peerstore, r *dhtpb.Record) error { + v := make(record.Validator) + v["pk"] = record.ValidatePublicKeyRecord + p := peer.ID(r.GetAuthor()) + pk := ps.PubKey(p) + if pk == nil { + return fmt.Errorf("do not have public key for %s", p) + } + if err := v.VerifyRecord(r, pk); err != nil { + return err + } + return nil +} From 1081c7c2837cd8c6b12fc37d220411decf3f1078 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 2 Feb 2015 07:31:53 -0800 Subject: [PATCH 0750/3817] fix(grc) move Bootstrap method onto routing interface This commit was moved from ipfs/go-ipfs-routing@d4c2e06461d8c63984ea781fa124c88f513996f0 --- routing/supernode/client.go | 4 ++++ routing/supernode/proxy/loopback.go | 5 +++++ routing/supernode/proxy/standard.go | 18 ++++++++++++++---- routing/supernode/server.go | 4 ++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 96b58681e..14a264524 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -131,4 +131,8 @@ func (c *Client) Ping(ctx context.Context, id peer.ID) (time.Duration, error) { return time.Nanosecond, errors.New("supernode routing does not support the ping method") } +func (c *Client) Bootstrap(ctx context.Context) error { + return c.proxy.Bootstrap(ctx) +} + var _ routing.IpfsRouting = &Client{} diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index e5fa39deb..01aeeac3c 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -20,6 +20,11 @@ type Loopback struct { Local peer.ID } +func (_ *Loopback) Bootstrap(ctx context.Context) error { + return nil +} + + // SendMessage intercepts local requests, forwarding them to a local handler func (lb *Loopback) SendMessage(ctx context.Context, m *dhtpb.Message) error { response := lb.Handler.HandleRequest(ctx, lb.Local, m) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 1918e344a..1a095cd64 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -18,6 +18,7 @@ const ProtocolSNR = "/ipfs/supernoderouting" var log = eventlog.Logger("supernode/proxy") type Proxy interface { + Bootstrap(context.Context) error HandleStream(inet.Stream) SendMessage(ctx context.Context, m *dhtpb.Message) error SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) @@ -25,13 +26,22 @@ type Proxy interface { type standard struct { Host host.Host - Remotes []peer.ID + Remotes []peer.PeerInfo } -func Standard(h host.Host, remotes []peer.ID) Proxy { +func Standard(h host.Host, remotes []peer.PeerInfo) Proxy { return &standard{h, remotes} } +func (px *standard) Bootstrap(ctx context.Context) error { + for _, info := range px.Remotes { + if err := px.Host.Connect(ctx, info); err != nil { + return err // TODO + } + } + return nil +} + func (p *standard) HandleStream(s inet.Stream) { // TODO(brian): Should clients be able to satisfy requests? log.Error("supernode client received (dropped) a routing message from", s.Conn().RemotePeer()) @@ -44,7 +54,7 @@ func (p *standard) HandleStream(s inet.Stream) { func (px *standard) SendMessage(ctx context.Context, m *dhtpb.Message) error { var err error for _, i := range rand.Perm(len(px.Remotes)) { - remote := px.Remotes[i] + remote := px.Remotes[i].ID if err = px.sendMessage(ctx, m, remote); err != nil { // careful don't re-declare err! continue } @@ -82,7 +92,7 @@ func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote pe func (px *standard) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) { var err error for _, i := range rand.Perm(len(px.Remotes)) { - remote := px.Remotes[i] + remote := px.Remotes[i].ID var reply *dhtpb.Message reply, err = px.sendRequest(ctx, m, remote) // careful don't redeclare err! if err != nil { diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 78a686ebe..5aca4c4d0 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -32,6 +32,10 @@ func NewServer(ds datastore.ThreadSafeDatastore, ps peer.Peerstore, local peer.I return s, nil } +func (_ *Server) Bootstrap(ctx context.Context) error { + return nil +} + // HandleLocalRequest implements the proxy.RequestHandler interface. This is // where requests are received from the outside world. func (s *Server) HandleRequest(ctx context.Context, p peer.ID, req *dhtpb.Message) *dhtpb.Message { From a0fcbdc7a7d455e69104a5039e9a09d2f273758e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 3 Feb 2015 13:50:49 -0800 Subject: [PATCH 0751/3817] refactor(routing) expose Bootstrap() error on routing interface This commit was moved from ipfs/go-ipfs-routing@acff69286e5b4da6b4da3369e892743d9855b058 --- routing/dht/dht_bootstrap.go | 8 +++++++- routing/mock/centralized_client.go | 4 ++++ routing/offline/offline.go | 4 ++++ routing/routing.go | 6 ++++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index f2cc50f9a..18451f1c3 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -4,6 +4,7 @@ package dht import ( "crypto/rand" + "errors" "fmt" "sync" "time" @@ -44,13 +45,18 @@ var DefaultBootstrapConfig = BootstrapConfig{ Timeout: time.Duration(20 * time.Second), } +func (dht *IpfsDHT) Bootstrap(context.Context) error { + // Bootstrap satisfies the routing interface + return errors.New("TODO: perform DHT bootstrap") +} + // Bootstrap ensures the dht routing table remains healthy as peers come and go. // it builds up a list of peers by requesting random peer IDs. The Bootstrap // process will run a number of queries each time, and run every time signal fires. // These parameters are configurable. // // Bootstrap returns a process, so the user can stop it. -func (dht *IpfsDHT) Bootstrap(config BootstrapConfig) (goprocess.Process, error) { +func (dht *IpfsDHT) BootstrapWithConfig(config BootstrapConfig) (goprocess.Process, error) { sig := time.Tick(config.Period) return dht.BootstrapOnSignal(config, sig) } diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 4a9b63b01..6d358f52b 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -84,4 +84,8 @@ func (c *client) Ping(ctx context.Context, p peer.ID) (time.Duration, error) { return 0, nil } +func (c *client) Bootstrap(context.Context) error { + return nil +} + var _ routing.IpfsRouting = &client{} diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 63fb14441..c73d73391 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -89,5 +89,9 @@ func (c *offlineRouting) Ping(ctx context.Context, p peer.ID) (time.Duration, er return 0, ErrOffline } +func (c *offlineRouting) Bootstrap(context.Context) (error) { + return nil +} + // ensure offlineRouting matches the IpfsRouting interface var _ routing.IpfsRouting = &offlineRouting{} diff --git a/routing/routing.go b/routing/routing.go index 8238aa45c..3dea2feb6 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -40,4 +40,10 @@ type IpfsRouting interface { // Ping a peer, log the time it took Ping(context.Context, peer.ID) (time.Duration, error) + + // Bootstrap allows callers to hint to the routing system to get into a + // Boostrapped state + Bootstrap(context.Context) error + + // TODO expose io.Closer or plain-old Close error } From 208533fad96a34f8eab8ea64a193eb8ada60191a Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 5 Feb 2015 07:53:53 -0800 Subject: [PATCH 0752/3817] bootstrap: update bootstrapping process. Note: the dht-specific part of the bootstrap function was only there to make sure to call `dht.Update(ctx, npeer)`. This already happens on all new connections made by the network, as the dht is signed up for notifications. This commit was moved from ipfs/go-ipfs-routing@b078ad2a000e879ef3fad89c5b3e60c5a97b0500 --- routing/dht/dht_bootstrap.go | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 18451f1c3..7eedb4821 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -4,7 +4,6 @@ package dht import ( "crypto/rand" - "errors" "fmt" "sync" "time" @@ -45,17 +44,37 @@ var DefaultBootstrapConfig = BootstrapConfig{ Timeout: time.Duration(20 * time.Second), } -func (dht *IpfsDHT) Bootstrap(context.Context) error { - // Bootstrap satisfies the routing interface - return errors.New("TODO: perform DHT bootstrap") +// Bootstrap ensures the dht routing table remains healthy as peers come and go. +// it builds up a list of peers by requesting random peer IDs. The Bootstrap +// process will run a number of queries each time, and run every time signal fires. +// These parameters are configurable. +// +// As opposed to BootstrapWithConfig, Bootstrap satisfies the routing interface +func (dht *IpfsDHT) Bootstrap(ctx context.Context) error { + proc, err := dht.BootstrapWithConfig(DefaultBootstrapConfig) + if err != nil { + return err + } + + // wait till ctx or dht.Context exits. + // we have to do it this way to satisfy the Routing interface (contexts) + go func() { + defer proc.Close() + select { + case <-ctx.Done(): + case <-dht.Context().Done(): + } + }() + + return nil } -// Bootstrap ensures the dht routing table remains healthy as peers come and go. +// BootstrapWithConfig ensures the dht routing table remains healthy as peers come and go. // it builds up a list of peers by requesting random peer IDs. The Bootstrap // process will run a number of queries each time, and run every time signal fires. // These parameters are configurable. // -// Bootstrap returns a process, so the user can stop it. +// BootstrapWithConfig returns a process, so the user can stop it. func (dht *IpfsDHT) BootstrapWithConfig(config BootstrapConfig) (goprocess.Process, error) { sig := time.Tick(config.Period) return dht.BootstrapOnSignal(config, sig) From a72ce773cdff7044b2714305bd40e2e2f170d3a6 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 2 Feb 2015 04:31:48 -0800 Subject: [PATCH 0753/3817] test(snr/s) put fix This commit was moved from ipfs/go-ipfs-routing@4d33dce3b758134da1badd0db1aaa4369a2878de --- routing/supernode/server_test.go | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 routing/supernode/server_test.go diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go new file mode 100644 index 000000000..0d2d00318 --- /dev/null +++ b/routing/supernode/server_test.go @@ -0,0 +1,40 @@ +package supernode + +import ( + "testing" + + datastore "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" + "github.com/jbenet/go-ipfs/util" +) + +func TestPutProviderDoesntResultInDuplicates(t *testing.T) { + routingBackend := datastore.NewMapDatastore() + k := util.Key("foo") + put := []*dhtpb.Message_Peer{ + convPeer("bob", "127.0.0.1/tcp/4001"), + convPeer("alice", "10.0.0.10/tcp/4001"), + } + if err := putRoutingProviders(routingBackend, k, put); err != nil { + t.Fatal(err) + } + if err := putRoutingProviders(routingBackend, k, put); err != nil { + t.Fatal(err) + } + + got, err := getRoutingProviders(routingBackend, k) + if err != nil { + t.Fatal(err) + } + if len(got) != 2 { + t.Fatal("should be 2 values, but there are", len(got)) + } +} + +func convPeer(name string, addrs ...string) *dhtpb.Message_Peer { + var rawAddrs [][]byte + for _, addr := range addrs { + rawAddrs = append(rawAddrs, []byte(addr)) + } + return &dhtpb.Message_Peer{Id: &name, Addrs: rawAddrs} +} From 50457ef5886a8aac97836bef19105dfd492d7f1f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 6 Feb 2015 10:49:52 -0700 Subject: [PATCH 0754/3817] style(routing/supernode/client) fix indent This commit was moved from ipfs/go-ipfs-routing@3fe1c7072eeb75bc2a27393b939e5b7c2a383d64 --- routing/supernode/proxy/standard.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 1a095cd64..999029e9f 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -34,11 +34,11 @@ func Standard(h host.Host, remotes []peer.PeerInfo) Proxy { } func (px *standard) Bootstrap(ctx context.Context) error { - for _, info := range px.Remotes { - if err := px.Host.Connect(ctx, info); err != nil { - return err // TODO - } + for _, info := range px.Remotes { + if err := px.Host.Connect(ctx, info); err != nil { + return err // TODO } + } return nil } From 4869f7fc136917acbba5d0a5bbe455bc42c2f3ab Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 6 Feb 2015 11:24:49 -0700 Subject: [PATCH 0755/3817] log(routing) report boostrap result to user This commit was moved from ipfs/go-ipfs-routing@fed010e39a2b34341316369d22cf4a28ced86beb --- routing/supernode/proxy/standard.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 999029e9f..039997a6f 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -34,10 +34,17 @@ func Standard(h host.Host, remotes []peer.PeerInfo) Proxy { } func (px *standard) Bootstrap(ctx context.Context) error { + var cxns []peer.PeerInfo for _, info := range px.Remotes { if err := px.Host.Connect(ctx, info); err != nil { - return err // TODO + continue } + cxns = append(cxns, info) + } + if len(cxns) == 0 { + log.Critical("unable to bootstrap to any supernode routers") + } else { + log.Info("bootstrapped to %d supernode routers: %s", len(cxns), cxns) } return nil } From c24be77811a314c7d71d2a0f477e0e0c2272b9f4 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 6 Feb 2015 14:03:33 -0700 Subject: [PATCH 0756/3817] fix log This commit was moved from ipfs/go-ipfs-routing@e84a97079599d9a1c59e4fce14382a1b20e5d550 --- routing/supernode/proxy/standard.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 039997a6f..cb458acc2 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -44,7 +44,7 @@ func (px *standard) Bootstrap(ctx context.Context) error { if len(cxns) == 0 { log.Critical("unable to bootstrap to any supernode routers") } else { - log.Info("bootstrapped to %d supernode routers: %s", len(cxns), cxns) + log.Infof("bootstrapped to %d supernode routers: %s", len(cxns), cxns) } return nil } From fb0f350fc0b8bc0bb23ad5379555ceab7b381105 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 12 Feb 2015 09:51:56 -0800 Subject: [PATCH 0757/3817] feat(snrouting): pick remote based on XOR distance metric This commit was moved from ipfs/go-ipfs-routing@4487bfd5c13e216ce19c7a344adc08e6f6d7cc8c --- routing/supernode/proxy/standard.go | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index cb458acc2..3e4e2291c 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -1,15 +1,15 @@ package proxy import ( - "math/rand" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" host "github.com/jbenet/go-ipfs/p2p/host" inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" + kbucket "github.com/jbenet/go-ipfs/routing/kbucket" eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" + "github.com/jbenet/go-ipfs/util" errors "github.com/jbenet/go-ipfs/util/debugerror" ) @@ -25,17 +25,23 @@ type Proxy interface { } type standard struct { - Host host.Host - Remotes []peer.PeerInfo + Host host.Host + + remoteInfos []peer.PeerInfo // addr required for bootstrapping + remoteIDs []peer.ID // []ID is required for each req. here, cached for performance. } func Standard(h host.Host, remotes []peer.PeerInfo) Proxy { - return &standard{h, remotes} + var ids []peer.ID + for _, remote := range remotes { + ids = append(ids, remote.ID) + } + return &standard{h, remotes, ids} } func (px *standard) Bootstrap(ctx context.Context) error { var cxns []peer.PeerInfo - for _, info := range px.Remotes { + for _, info := range px.remoteInfos { if err := px.Host.Connect(ctx, info); err != nil { continue } @@ -60,8 +66,7 @@ func (p *standard) HandleStream(s inet.Stream) { // error. func (px *standard) SendMessage(ctx context.Context, m *dhtpb.Message) error { var err error - for _, i := range rand.Perm(len(px.Remotes)) { - remote := px.Remotes[i].ID + for _, remote := range sortedByKey(px.remoteIDs, m.GetKey()) { if err = px.sendMessage(ctx, m, remote); err != nil { // careful don't re-declare err! continue } @@ -98,8 +103,7 @@ func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote pe // error. func (px *standard) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) { var err error - for _, i := range rand.Perm(len(px.Remotes)) { - remote := px.Remotes[i].ID + for _, remote := range sortedByKey(px.remoteIDs, m.GetKey()) { var reply *dhtpb.Message reply, err = px.sendRequest(ctx, m, remote) // careful don't redeclare err! if err != nil { @@ -145,3 +149,8 @@ func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote pe e.Append(eventlog.Pair("uuid", eventlog.Uuid("foo"))) return response, nil } + +func sortedByKey(peers []peer.ID, key string) []peer.ID { + target := kbucket.ConvertKey(util.Key(key)) + return kbucket.SortClosestPeers(peers, target) +} From 63c1ddb6064a519163836107f21b7ee5d3a8eef9 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 12 Feb 2015 13:56:27 -0800 Subject: [PATCH 0758/3817] feat(snrouting) replicate Provider, PutValue to multiple remotes This commit was moved from ipfs/go-ipfs-routing@2af3b166685b397c5278d53d23cf462226707b82 --- routing/supernode/proxy/standard.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 3e4e2291c..e45bfb72b 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -61,15 +61,25 @@ func (p *standard) HandleStream(s inet.Stream) { s.Close() } +const replicationFactor = 2 + // SendMessage sends message to each remote sequentially (randomized order), // stopping after the first successful response. If all fail, returns the last // error. func (px *standard) SendMessage(ctx context.Context, m *dhtpb.Message) error { var err error + var numSuccesses int for _, remote := range sortedByKey(px.remoteIDs, m.GetKey()) { if err = px.sendMessage(ctx, m, remote); err != nil { // careful don't re-declare err! continue } + numSuccesses++ + switch m.GetType() { + case dhtpb.Message_ADD_PROVIDER, dhtpb.Message_PUT_VALUE: + if numSuccesses < replicationFactor { + continue + } + } return nil // success } return err // NB: returns the last error From 0cfd591d63ccc518a6a5de7acfc35f30be85ae50 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 18 Feb 2015 08:33:26 +0000 Subject: [PATCH 0759/3817] teach pinning how to use GetBlocks This commit was moved from ipfs/go-ipfs-pinner@3bd4a4b0416a7ac03ed6b2310e60a4ea659ee561 --- pinning/pinner/pin.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 466dfba41..666e87b6f 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -7,7 +7,9 @@ import ( "errors" "fmt" "sync" + "time" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" nsds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" "github.com/jbenet/go-ipfs/blocks/set" @@ -170,8 +172,10 @@ func (p *pinner) pinIndirectRecurse(node *mdag.Node) error { } func (p *pinner) pinLinks(node *mdag.Node) error { - for _, l := range node.Links { - subnode, err := l.GetNode(p.dserv) + ctx, _ := context.WithTimeout(context.Background(), time.Second*60) + for _, ng := range p.dserv.GetDAG(ctx, node) { + subnode, err := ng.Get() + //subnode, err := l.GetNode(p.dserv) if err != nil { // TODO: Maybe just log and continue? return err From 359c35e62e697c3a530df0bce6e39fe372d9d0bd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 18 Feb 2015 08:44:44 +0000 Subject: [PATCH 0760/3817] teach unixfs/tar (aka ipfs get) how to use GetBlocks This commit was moved from ipfs/go-unixfs@dcb7a5312f02d6924bd3ef51dc3325eb0eeb8174 --- unixfs/tar/reader.go | 96 ++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index e27f6af41..4d4f20c88 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -60,20 +60,20 @@ func NewReader(path path.Path, dag mdag.DAGService, resolver *path.Resolver, com return reader, nil } -func (i *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { +func (r *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { pb := new(upb.Data) err := proto.Unmarshal(dagnode.Data, pb) if err != nil { - i.emitError(err) + r.emitError(err) return } if depth == 0 { - defer i.close() + defer r.close() } if pb.GetType() == upb.Data_Directory { - err = i.writer.WriteHeader(&tar.Header{ + err = r.writer.WriteHeader(&tar.Header{ Name: path, Typeflag: tar.TypeDir, Mode: 0777, @@ -81,23 +81,25 @@ func (i *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { // TODO: set mode, dates, etc. when added to unixFS }) if err != nil { - i.emitError(err) + r.emitError(err) return } - i.flush() + r.flush() - for _, link := range dagnode.Links { - childNode, err := link.GetNode(i.dag) + ctx, _ := context.WithTimeout(context.TODO(), time.Second*60) + + for i, ng := range r.dag.GetDAG(ctx, dagnode) { + childNode, err := ng.Get() if err != nil { - i.emitError(err) + r.emitError(err) return } - i.writeToBuf(childNode, gopath.Join(path, link.Name), depth+1) + r.writeToBuf(childNode, gopath.Join(path, dagnode.Links[i].Name), depth+1) } return } - err = i.writer.WriteHeader(&tar.Header{ + err = r.writer.WriteHeader(&tar.Header{ Name: path, Size: int64(pb.GetFilesize()), Typeflag: tar.TypeReg, @@ -106,95 +108,95 @@ func (i *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { // TODO: set mode, dates, etc. when added to unixFS }) if err != nil { - i.emitError(err) + r.emitError(err) return } - i.flush() + r.flush() - reader, err := uio.NewDagReader(context.TODO(), dagnode, i.dag) + reader, err := uio.NewDagReader(context.TODO(), dagnode, r.dag) if err != nil { - i.emitError(err) + r.emitError(err) return } - err = i.syncCopy(reader) + err = r.syncCopy(reader) if err != nil { - i.emitError(err) + r.emitError(err) return } } -func (i *Reader) Read(p []byte) (int, error) { +func (r *Reader) Read(p []byte) (int, error) { // wait for the goroutine that is writing data to the buffer to tell us // there is something to read - if !i.closed { - <-i.signalChan + if !r.closed { + <-r.signalChan } - if i.err != nil { - return 0, i.err + if r.err != nil { + return 0, r.err } - if !i.closed { - defer i.signal() + if !r.closed { + defer r.signal() } - if i.buf.Len() == 0 { - if i.closed { + if r.buf.Len() == 0 { + if r.closed { return 0, io.EOF } return 0, nil } - n, err := i.buf.Read(p) - if err == io.EOF && !i.closed || i.buf.Len() > 0 { + n, err := r.buf.Read(p) + if err == io.EOF && !r.closed || r.buf.Len() > 0 { return n, nil } return n, err } -func (i *Reader) signal() { - i.signalChan <- struct{}{} +func (r *Reader) signal() { + r.signalChan <- struct{}{} } -func (i *Reader) flush() { - i.signal() - <-i.signalChan +func (r *Reader) flush() { + r.signal() + <-r.signalChan } -func (i *Reader) emitError(err error) { - i.err = err - i.signal() +func (r *Reader) emitError(err error) { + r.err = err + r.signal() } -func (i *Reader) close() { - i.closed = true - defer i.signal() - err := i.writer.Close() +func (r *Reader) close() { + r.closed = true + defer r.signal() + err := r.writer.Close() if err != nil { - i.emitError(err) + r.emitError(err) return } - if i.gzipWriter != nil { - err = i.gzipWriter.Close() + if r.gzipWriter != nil { + err = r.gzipWriter.Close() if err != nil { - i.emitError(err) + r.emitError(err) return } } } -func (i *Reader) syncCopy(reader io.Reader) error { +func (r *Reader) syncCopy(reader io.Reader) error { buf := make([]byte, 32*1024) for { nr, err := reader.Read(buf) if nr > 0 { - _, err := i.writer.Write(buf[:nr]) + _, err := r.writer.Write(buf[:nr]) if err != nil { return err } - i.flush() + r.flush() } if err == io.EOF { break From 0488a69e1867738a80611fa94501a134d3d0bca0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 18 Feb 2015 09:29:29 +0000 Subject: [PATCH 0761/3817] pinner now requires all nodes exist in blockstore This commit was moved from ipfs/go-ipfs-pinner@a12db134a717a1d416db2de3d08e308c3966d600 --- pinning/pinner/pin.go | 1 - pinning/pinner/pin_test.go | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 666e87b6f..25edd5832 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -175,7 +175,6 @@ func (p *pinner) pinLinks(node *mdag.Node) error { ctx, _ := context.WithTimeout(context.Background(), time.Second*60) for _, ng := range p.dserv.GetDAG(ctx, node) { subnode, err := ng.Get() - //subnode, err := l.GetNode(p.dserv) if err != nil { // TODO: Maybe just log and continue? return err diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 623983a34..c8d18f027 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -34,6 +34,10 @@ func TestPinnerBasic(t *testing.T) { p := NewPinner(dstore, dserv) a, ak := randNode() + _, err = dserv.Add(a) + if err != nil { + t.Fatal(err) + } // Pin A{} err = p.Pin(a, false) @@ -45,18 +49,30 @@ func TestPinnerBasic(t *testing.T) { t.Fatal("Failed to find key") } + // create new node c, to be indirectly pinned through b + c, ck := randNode() + _, err = dserv.Add(c) + if err != nil { + t.Fatal(err) + } + + // Create new node b, to be parent to a and c b, _ := randNode() err = b.AddNodeLink("child", a) if err != nil { t.Fatal(err) } - c, ck := randNode() err = b.AddNodeLink("otherchild", c) if err != nil { t.Fatal(err) } + _, err = dserv.Add(b) + if err != nil { + t.Fatal(err) + } + // recursively pin B{A,C} err = p.Pin(b, true) if err != nil { From 3472bb12e152a05262ecfcbed2d4e0779edc893c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 19 Feb 2015 00:31:10 +0000 Subject: [PATCH 0762/3817] move blocking calls out of single threaded loops, cancel contexts ASAP This commit was moved from ipfs/go-merkledag@0c12b7a06a6614026c626584bf1e42a4078e09b5 --- ipld/merkledag/merkledag.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index db48d3b34..cb35f0919 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -187,9 +187,12 @@ func (ds *dagService) GetNodes(ctx context.Context, keys []u.Key) []NodeGetter { } go func() { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + blkchan := ds.Blocks.GetBlocks(ctx, keys) - for { + for count := 0; count < len(keys); { select { case blk, ok := <-blkchan: if !ok { @@ -205,6 +208,7 @@ func (ds *dagService) GetNodes(ctx context.Context, keys []u.Key) []NodeGetter { is := FindLinks(keys, blk.Key(), 0) for _, i := range is { sendChans[i] <- nd + count++ } case <-ctx.Done(): return From 0da2f30ccd2bda5c0bb1889ab0b525bb38210535 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 19 Feb 2015 08:42:28 +0000 Subject: [PATCH 0763/3817] hotfix: duplicate blocks werent being counted properly, deduped key list before requesting This commit was moved from ipfs/go-merkledag@b7d8fdef119dcb5bd135fe5e4bfbb75cc16dc689 --- ipld/merkledag/merkledag.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index cb35f0919..57a5b97f2 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -180,17 +180,24 @@ func (ds *dagService) GetDAG(ctx context.Context, root *Node) []NodeGetter { // GetNodes returns an array of 'NodeGetter' promises, with each corresponding // to the key with the same index as the passed in keys func (ds *dagService) GetNodes(ctx context.Context, keys []u.Key) []NodeGetter { + + // Early out if no work to do + if len(keys) == 0 { + return nil + } + promises := make([]NodeGetter, len(keys)) sendChans := make([]chan<- *Node, len(keys)) for i, _ := range keys { promises[i], sendChans[i] = newNodePromise(ctx) } + dedupedKeys := dedupeKeys(keys) go func() { ctx, cancel := context.WithCancel(ctx) defer cancel() - blkchan := ds.Blocks.GetBlocks(ctx, keys) + blkchan := ds.Blocks.GetBlocks(ctx, dedupedKeys) for count := 0; count < len(keys); { select { @@ -207,8 +214,8 @@ func (ds *dagService) GetNodes(ctx context.Context, keys []u.Key) []NodeGetter { } is := FindLinks(keys, blk.Key(), 0) for _, i := range is { - sendChans[i] <- nd count++ + sendChans[i] <- nd } case <-ctx.Done(): return @@ -218,6 +225,19 @@ func (ds *dagService) GetNodes(ctx context.Context, keys []u.Key) []NodeGetter { return promises } +// Remove duplicates from a list of keys +func dedupeKeys(ks []u.Key) []u.Key { + kmap := make(map[u.Key]struct{}) + var out []u.Key + for _, k := range ks { + if _, ok := kmap[k]; !ok { + kmap[k] = struct{}{} + out = append(out, k) + } + } + return out +} + func newNodePromise(ctx context.Context) (NodeGetter, chan<- *Node) { ch := make(chan *Node, 1) return &nodePromise{ From a15d1229f7922ed232a65ec2a3337edaf8b9a9c8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 21 Feb 2015 16:20:28 -0800 Subject: [PATCH 0764/3817] add put and get dht commands to cli This commit was moved from ipfs/go-ipfs-routing@f7a86f2f0285aa11f1c2c1c4e076b6df4cd76c13 --- routing/dht/dht.go | 2 +- routing/dht/handlers.go | 2 +- routing/dht/routing.go | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index edd18ff11..e3d054925 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -322,7 +322,7 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, p peer.ID, count int) [ // == to self? thats bad for _, p := range closer { if p == dht.self { - log.Debug("Attempted to return self! this shouldnt happen...") + log.Error("Attempted to return self! this shouldnt happen...") return nil } } diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 03c3eda3c..defb7e488 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -83,7 +83,7 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Mess // Find closest peer on given cluster to desired key and reply with that info closer := dht.betterPeersToQuery(pmes, p, CloserPeerCount) - if closer != nil { + if len(closer) > 0 { closerinfos := peer.PeerInfos(dht.peerstore, closer) for _, pi := range closerinfos { log.Debugf("handleGetValue returning closer peer: '%s'", pi.ID) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index ade41b82e..49449eda7 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -59,6 +59,11 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error wg.Add(1) go func(p peer.ID) { defer wg.Done() + notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + Type: notif.Value, + ID: p, + }) + err := dht.putValueToPeer(ctx, p, key, rec) if err != nil { log.Debugf("failed putting value to peer: %s", err) @@ -92,6 +97,11 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { // setup the Query query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { + notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + Type: notif.SendingQuery, + ID: p, + }) + val, peers, err := dht.getValueOrPeers(ctx, p, key) if err != nil { return nil, err @@ -102,6 +112,12 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { res.success = true } + notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + Type: notif.PeerResponse, + ID: p, + Responses: pointerizePeerInfos(peers), + }) + return res, nil }) From 9fd3e1aec07e87a474a5dfe9349a82d4b37e496f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 21 Feb 2015 19:26:58 -0800 Subject: [PATCH 0765/3817] dont potentially kill our memory This commit was moved from ipfs/go-ipfs-routing@066fdbcfe269888c3213885e4c23c99123bbc77b --- routing/dht/routing.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 49449eda7..99df4423c 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -1,7 +1,6 @@ package dht import ( - "math" "sync" "time" @@ -89,7 +88,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { // get closest peers in the routing table rtp := dht.routingTable.ListPeers() - log.Debugf("peers in rt: %s", len(rtp), rtp) + log.Errorf("peers in rt: %s", len(rtp), rtp) if len(rtp) == 0 { log.Warning("No peers from routing table!") return nil, errors.Wrap(kb.ErrLookupFailure) @@ -169,7 +168,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { // FindProviders searches until the context expires. func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]peer.PeerInfo, error) { var providers []peer.PeerInfo - for p := range dht.FindProvidersAsync(ctx, key, math.MaxInt32) { + for p := range dht.FindProvidersAsync(ctx, key, KValue) { providers = append(providers, p) } return providers, nil From e83778d12e0ff252c20d2b008fedb5d06d5b65db Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 23 Feb 2015 11:22:45 -0800 Subject: [PATCH 0766/3817] error -> debug This commit was moved from ipfs/go-ipfs-routing@a062e1f2707d840befab0da22d4ef1ca2ad8410f --- routing/dht/dht.go | 2 +- routing/dht/routing.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index e3d054925..1c5c921f3 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -322,7 +322,7 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, p peer.ID, count int) [ // == to self? thats bad for _, p := range closer { if p == dht.self { - log.Error("Attempted to return self! this shouldnt happen...") + log.Info("Attempted to return self! this shouldnt happen...") return nil } } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 99df4423c..e5652dc8d 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -88,7 +88,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { // get closest peers in the routing table rtp := dht.routingTable.ListPeers() - log.Errorf("peers in rt: %s", len(rtp), rtp) + log.Debugf("peers in rt: %s", len(rtp), rtp) if len(rtp) == 0 { log.Warning("No peers from routing table!") return nil, errors.Wrap(kb.ErrLookupFailure) From 78f8a7367aa0c51826603ecfe007ae1afda6ebc4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 19 Feb 2015 07:40:37 +0000 Subject: [PATCH 0767/3817] fix panic in offline calls of 'ipfs object stat' This commit was moved from ipfs/go-merkledag@6b082dc09b8e83171e25ba149f3e5c1a8f9c57ea --- ipld/merkledag/merkledag_test.go | 2 +- ipld/merkledag/node.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index b66b085e8..83d70ff4c 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -122,7 +122,7 @@ func SubtestNodeStat(t *testing.T, n *Node) { return } - if expected != actual { + if expected != *actual { t.Error("n.Stat incorrect.\nexpect: %s\nactual: %s", expected, actual) } else { fmt.Printf("n.Stat correct: %s\n", actual) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 637563290..2848cdd3a 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -160,18 +160,18 @@ func (n *Node) Size() (uint64, error) { } // Stat returns statistics on the node. -func (n *Node) Stat() (NodeStat, error) { +func (n *Node) Stat() (*NodeStat, error) { enc, err := n.Encoded(false) if err != nil { - return NodeStat{}, err + return nil, err } cumSize, err := n.Size() if err != nil { - return NodeStat{}, err + return nil, err } - return NodeStat{ + return &NodeStat{ NumLinks: len(n.Links), BlockSize: len(enc), LinksSize: len(enc) - len(n.Data), // includes framing. From b3e47532af5bdf7ffa1e514943cc225c084435d7 Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 23 Feb 2015 16:51:09 +0100 Subject: [PATCH 0768/3817] rewrote import paths of go.net/context to use golang.org/x/context - updated go-ctxgroup and goprocess ctxgroup: AddChildGroup was changed to AddChild. Used in two files: - p2p/net/mock/mock_net.go - routing/dht/dht.go - updated context from hg repo to git prev. commit in hg was ad01a6fcc8a19d3a4478c836895ffe883bd2ceab. (context: make parentCancelCtx iterative) represents commit 84f8955a887232b6308d79c68b8db44f64df455c in git repo - updated context to master (b6fdb7d8a4ccefede406f8fe0f017fb58265054c) Aaron Jacobs (2): net/context: Don't accept a context in the DoSomethingSlow example. context: Be clear that users must cancel the result of WithCancel. Andrew Gerrand (1): go.net: use golang.org/x/... import paths Bryan C. Mills (1): net/context: Don't leak goroutines in Done example. Damien Neil (1): context: fix removal of cancelled timer contexts from parent David Symonds (2): context: Fix WithValue example code. net: add import comments. Sameer Ajmani (1): context: fix TestAllocs to account for ints in interfaces This commit was moved from ipfs/go-ipfs-routing@0c4de74b408c1bf1c6b2984e63baed28b08f9c32 --- routing/dht/dht.go | 4 ++-- routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 2 +- routing/dht/dht_test.go | 3 +-- routing/dht/ext_test.go | 5 ++--- routing/dht/handlers.go | 2 +- routing/dht/lookup.go | 3 +-- routing/dht/providers.go | 2 +- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 2 +- routing/dht/records.go | 3 +-- routing/dht/routing.go | 3 +-- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 2 +- routing/offline/offline.go | 2 +- routing/routing.go | 3 +-- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 3 +-- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- 23 files changed, 25 insertions(+), 32 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index edd18ff11..1294d9c4a 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -21,10 +21,10 @@ import ( "github.com/jbenet/go-ipfs/thirdparty/eventlog" u "github.com/jbenet/go-ipfs/util" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) var log = eventlog.Logger("dht") @@ -78,7 +78,7 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip h.SetStreamHandler(ProtocolDHT, dht.handleNewStream) dht.providers = NewProviderManager(dht.Context(), dht.self) - dht.AddChildGroup(dht.providers) + dht.AddChild(dht.providers) dht.routingTable = kb.NewRoutingTable(20, kb.ConvertPeerID(dht.self), time.Minute, dht.peerstore) dht.birth = time.Now() diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 7eedb4821..79dcb4d64 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -12,9 +12,9 @@ import ( routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" goprocess "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" periodicproc "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/periodic" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) // BootstrapConfig specifies parameters used bootstrapping the DHT. diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 15b0b63d8..d84e8e008 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -9,8 +9,8 @@ import ( pb "github.com/jbenet/go-ipfs/routing/dht/pb" ctxutil "github.com/jbenet/go-ipfs/util/ctx" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) // handleNewStream implements the inet.StreamHandler diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index b57caaccc..54a644877 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -9,11 +9,10 @@ import ( "testing" "time" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" peer "github.com/jbenet/go-ipfs/p2p/peer" netutil "github.com/jbenet/go-ipfs/p2p/test/util" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index ab756b5e4..ec5fffac3 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "math/rand" "testing" + "time" inet "github.com/jbenet/go-ipfs/p2p/net" mocknet "github.com/jbenet/go-ipfs/p2p/net/mock" @@ -14,12 +15,10 @@ import ( record "github.com/jbenet/go-ipfs/routing/record" u "github.com/jbenet/go-ipfs/util" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - - "time" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 03c3eda3c..c1e96e402 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -4,9 +4,9 @@ import ( "errors" "fmt" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" peer "github.com/jbenet/go-ipfs/p2p/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" u "github.com/jbenet/go-ipfs/util" diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index a713e553d..59ef3911f 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -1,8 +1,7 @@ package dht import ( - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" notif "github.com/jbenet/go-ipfs/notifications" peer "github.com/jbenet/go-ipfs/p2p/peer" kb "github.com/jbenet/go-ipfs/routing/kbucket" diff --git a/routing/dht/providers.go b/routing/dht/providers.go index f4f7514fb..1f95bdf82 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -7,7 +7,7 @@ import ( peer "github.com/jbenet/go-ipfs/p2p/peer" u "github.com/jbenet/go-ipfs/util" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) type providerInfo struct { diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 2781a3c59..22990d580 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -6,7 +6,7 @@ import ( peer "github.com/jbenet/go-ipfs/p2p/peer" u "github.com/jbenet/go-ipfs/util" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) func TestProviderManager(t *testing.T) { diff --git a/routing/dht/query.go b/routing/dht/query.go index 7a32b45bb..aacab106f 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -12,8 +12,8 @@ import ( pset "github.com/jbenet/go-ipfs/util/peerset" todoctr "github.com/jbenet/go-ipfs/util/todocounter" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) var maxQueryConcurrency = AlphaValue diff --git a/routing/dht/records.go b/routing/dht/records.go index 0bc701153..c6baf4999 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -3,8 +3,7 @@ package dht import ( "fmt" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ci "github.com/jbenet/go-ipfs/p2p/crypto" peer "github.com/jbenet/go-ipfs/p2p/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" diff --git a/routing/dht/routing.go b/routing/dht/routing.go index ade41b82e..7aa423285 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -5,8 +5,7 @@ import ( "sync" "time" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" notif "github.com/jbenet/go-ipfs/notifications" inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 6d358f52b..6a550bfaa 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -4,9 +4,9 @@ import ( "errors" "time" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index dc462797a..a0ee67e6d 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -5,8 +5,8 @@ import ( "sync" "time" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" peer "github.com/jbenet/go-ipfs/p2p/peer" u "github.com/jbenet/go-ipfs/util" "github.com/jbenet/go-ipfs/util/testutil" diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 56f61c076..d77144a73 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" peer "github.com/jbenet/go-ipfs/p2p/peer" delay "github.com/jbenet/go-ipfs/thirdparty/delay" u "github.com/jbenet/go-ipfs/util" diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 2235970f5..0fe30b119 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -1,9 +1,9 @@ package mockrouting import ( - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" sync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" mocknet "github.com/jbenet/go-ipfs/p2p/net/mock" dht "github.com/jbenet/go-ipfs/routing/dht" "github.com/jbenet/go-ipfs/util/testutil" diff --git a/routing/mock/interface.go b/routing/mock/interface.go index f8b034098..34092dfda 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -5,8 +5,8 @@ package mockrouting import ( - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" delay "github.com/jbenet/go-ipfs/thirdparty/delay" diff --git a/routing/offline/offline.go b/routing/offline/offline.go index c73d73391..324e438be 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -4,9 +4,9 @@ import ( "errors" "time" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ci "github.com/jbenet/go-ipfs/p2p/crypto" "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" diff --git a/routing/routing.go b/routing/routing.go index 3dea2feb6..be400a520 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -5,8 +5,7 @@ import ( "errors" "time" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" peer "github.com/jbenet/go-ipfs/p2p/peer" u "github.com/jbenet/go-ipfs/util" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 14a264524..09fa90ef6 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -4,8 +4,8 @@ import ( "bytes" "time" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" "github.com/jbenet/go-ipfs/p2p/host" peer "github.com/jbenet/go-ipfs/p2p/peer" routing "github.com/jbenet/go-ipfs/routing" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 01aeeac3c..df9dc1d72 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -1,8 +1,8 @@ package proxy import ( - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" @@ -24,7 +24,6 @@ func (_ *Loopback) Bootstrap(ctx context.Context) error { return nil } - // SendMessage intercepts local requests, forwarding them to a local handler func (lb *Loopback) SendMessage(ctx context.Context, m *dhtpb.Message) error { response := lb.Handler.HandleRequest(ctx, lb.Local, m) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index e45bfb72b..92a6d9f01 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -1,8 +1,8 @@ package proxy import ( - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" host "github.com/jbenet/go-ipfs/p2p/host" inet "github.com/jbenet/go-ipfs/p2p/net" peer "github.com/jbenet/go-ipfs/p2p/peer" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 5aca4c4d0..be587f749 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -3,9 +3,9 @@ package supernode import ( "fmt" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" datastore "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" peer "github.com/jbenet/go-ipfs/p2p/peer" dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" record "github.com/jbenet/go-ipfs/routing/record" From 2def74454e2277dbd86e3b06324df4ac4aa5fcad Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 23 Feb 2015 16:51:09 +0100 Subject: [PATCH 0769/3817] rewrote import paths of go.net/context to use golang.org/x/context - updated go-ctxgroup and goprocess ctxgroup: AddChildGroup was changed to AddChild. Used in two files: - p2p/net/mock/mock_net.go - routing/dht/dht.go - updated context from hg repo to git prev. commit in hg was ad01a6fcc8a19d3a4478c836895ffe883bd2ceab. (context: make parentCancelCtx iterative) represents commit 84f8955a887232b6308d79c68b8db44f64df455c in git repo - updated context to master (b6fdb7d8a4ccefede406f8fe0f017fb58265054c) Aaron Jacobs (2): net/context: Don't accept a context in the DoSomethingSlow example. context: Be clear that users must cancel the result of WithCancel. Andrew Gerrand (1): go.net: use golang.org/x/... import paths Bryan C. Mills (1): net/context: Don't leak goroutines in Done example. Damien Neil (1): context: fix removal of cancelled timer contexts from parent David Symonds (2): context: Fix WithValue example code. net: add import comments. Sameer Ajmani (1): context: fix TestAllocs to account for ints in interfaces This commit was moved from ipfs/go-merkledag@7659d38821e0acef1d22c50d0b3fde337790621f --- ipld/merkledag/merkledag.go | 3 +-- ipld/merkledag/merkledag_test.go | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 57a5b97f2..6e833056d 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -6,8 +6,7 @@ import ( "sync" "time" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/jbenet/go-ipfs/blocks" bserv "github.com/jbenet/go-ipfs/blockservice" u "github.com/jbenet/go-ipfs/util" diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index b66b085e8..efb7c77dd 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -8,9 +8,9 @@ import ( "sync" "testing" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" bstore "github.com/jbenet/go-ipfs/blocks/blockstore" blockservice "github.com/jbenet/go-ipfs/blockservice" bserv "github.com/jbenet/go-ipfs/blockservice" From 1c70848c78a4af649af77dad06ae08e6833baef8 Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 23 Feb 2015 16:51:09 +0100 Subject: [PATCH 0770/3817] rewrote import paths of go.net/context to use golang.org/x/context - updated go-ctxgroup and goprocess ctxgroup: AddChildGroup was changed to AddChild. Used in two files: - p2p/net/mock/mock_net.go - routing/dht/dht.go - updated context from hg repo to git prev. commit in hg was ad01a6fcc8a19d3a4478c836895ffe883bd2ceab. (context: make parentCancelCtx iterative) represents commit 84f8955a887232b6308d79c68b8db44f64df455c in git repo - updated context to master (b6fdb7d8a4ccefede406f8fe0f017fb58265054c) Aaron Jacobs (2): net/context: Don't accept a context in the DoSomethingSlow example. context: Be clear that users must cancel the result of WithCancel. Andrew Gerrand (1): go.net: use golang.org/x/... import paths Bryan C. Mills (1): net/context: Don't leak goroutines in Done example. Damien Neil (1): context: fix removal of cancelled timer contexts from parent David Symonds (2): context: Fix WithValue example code. net: add import comments. Sameer Ajmani (1): context: fix TestAllocs to account for ints in interfaces This commit was moved from ipfs/go-namesys@543c7420ead4d6a099ec40f96c4a87c21ae871ce --- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/namesys.go | 2 +- namesys/proquint.go | 2 +- namesys/publisher.go | 2 +- namesys/resolve_test.go | 2 +- namesys/routing.go | 3 +-- 7 files changed, 7 insertions(+), 8 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 2fb477930..9efc72348 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -3,10 +3,10 @@ package namesys import ( "net" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" isd "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-is-domain" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" u "github.com/jbenet/go-ipfs/util" ) diff --git a/namesys/interface.go b/namesys/interface.go index 6faaeaf6a..10e4fb89f 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -4,7 +4,7 @@ package namesys import ( "errors" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ci "github.com/jbenet/go-ipfs/p2p/crypto" u "github.com/jbenet/go-ipfs/util" ) diff --git a/namesys/namesys.go b/namesys/namesys.go index eddae747d..d4cc03964 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -1,7 +1,7 @@ package namesys import ( - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ci "github.com/jbenet/go-ipfs/p2p/crypto" routing "github.com/jbenet/go-ipfs/routing" u "github.com/jbenet/go-ipfs/util" diff --git a/namesys/proquint.go b/namesys/proquint.go index 24e97529d..b43f7a2a6 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -3,8 +3,8 @@ package namesys import ( "errors" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" proquint "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/bren2010/proquint" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" u "github.com/jbenet/go-ipfs/util" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 3b209cd26..656730b40 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -6,9 +6,9 @@ import ( "fmt" "time" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" pb "github.com/jbenet/go-ipfs/namesys/internal/pb" ci "github.com/jbenet/go-ipfs/p2p/crypto" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index e349a0aa9..3b8bb7072 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -1,9 +1,9 @@ package namesys import ( - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "testing" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" mockrouting "github.com/jbenet/go-ipfs/routing/mock" u "github.com/jbenet/go-ipfs/util" testutil "github.com/jbenet/go-ipfs/util/testutil" diff --git a/namesys/routing.go b/namesys/routing.go index a66565db6..ed8690079 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -3,10 +3,9 @@ package namesys import ( "fmt" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" pb "github.com/jbenet/go-ipfs/namesys/internal/pb" ci "github.com/jbenet/go-ipfs/p2p/crypto" routing "github.com/jbenet/go-ipfs/routing" From b066986818c1237c4f04a6850cdda85b66a5fa5e Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 23 Feb 2015 16:51:09 +0100 Subject: [PATCH 0771/3817] rewrote import paths of go.net/context to use golang.org/x/context - updated go-ctxgroup and goprocess ctxgroup: AddChildGroup was changed to AddChild. Used in two files: - p2p/net/mock/mock_net.go - routing/dht/dht.go - updated context from hg repo to git prev. commit in hg was ad01a6fcc8a19d3a4478c836895ffe883bd2ceab. (context: make parentCancelCtx iterative) represents commit 84f8955a887232b6308d79c68b8db44f64df455c in git repo - updated context to master (b6fdb7d8a4ccefede406f8fe0f017fb58265054c) Aaron Jacobs (2): net/context: Don't accept a context in the DoSomethingSlow example. context: Be clear that users must cancel the result of WithCancel. Andrew Gerrand (1): go.net: use golang.org/x/... import paths Bryan C. Mills (1): net/context: Don't leak goroutines in Done example. Damien Neil (1): context: fix removal of cancelled timer contexts from parent David Symonds (2): context: Fix WithValue example code. net: add import comments. Sameer Ajmani (1): context: fix TestAllocs to account for ints in interfaces This commit was moved from ipfs/go-ipfs-exchange-offline@87ee004fff66ff18385a3ece4ef8173f54ed806f --- exchange/offline/offline.go | 2 +- exchange/offline/offline_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index f1a6aaa61..fe8fa723c 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -3,7 +3,7 @@ package offline import ( - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/jbenet/go-ipfs/blocks" "github.com/jbenet/go-ipfs/blocks/blockstore" exchange "github.com/jbenet/go-ipfs/exchange" diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index d32f336d0..20588bde8 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -3,9 +3,9 @@ package offline import ( "testing" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ds_sync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/jbenet/go-ipfs/blocks" "github.com/jbenet/go-ipfs/blocks/blockstore" "github.com/jbenet/go-ipfs/blocks/blocksutil" From bbf3dd84e9951a7e2538b6a07a92252515655b21 Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 23 Feb 2015 16:51:09 +0100 Subject: [PATCH 0772/3817] rewrote import paths of go.net/context to use golang.org/x/context - updated go-ctxgroup and goprocess ctxgroup: AddChildGroup was changed to AddChild. Used in two files: - p2p/net/mock/mock_net.go - routing/dht/dht.go - updated context from hg repo to git prev. commit in hg was ad01a6fcc8a19d3a4478c836895ffe883bd2ceab. (context: make parentCancelCtx iterative) represents commit 84f8955a887232b6308d79c68b8db44f64df455c in git repo - updated context to master (b6fdb7d8a4ccefede406f8fe0f017fb58265054c) Aaron Jacobs (2): net/context: Don't accept a context in the DoSomethingSlow example. context: Be clear that users must cancel the result of WithCancel. Andrew Gerrand (1): go.net: use golang.org/x/... import paths Bryan C. Mills (1): net/context: Don't leak goroutines in Done example. Damien Neil (1): context: fix removal of cancelled timer contexts from parent David Symonds (2): context: Fix WithValue example code. net: add import comments. Sameer Ajmani (1): context: fix TestAllocs to account for ints in interfaces This commit was moved from ipfs/go-unixfs@a6f72a5360dd437ad07b73c7b42102f4fb956a11 --- unixfs/io/dagmodifier_test.go | 2 +- unixfs/io/dagreader.go | 3 +-- unixfs/tar/reader.go | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index 23f2a0afa..844393950 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -6,7 +6,6 @@ import ( "io/ioutil" "testing" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" "github.com/jbenet/go-ipfs/blocks/blockstore" bs "github.com/jbenet/go-ipfs/blockservice" @@ -19,6 +18,7 @@ import ( ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) func getMockDagServ(t *testing.T) mdag.DAGService { diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index d363d22d1..0af49e9ee 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -6,9 +6,8 @@ import ( "io" "os" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" mdag "github.com/jbenet/go-ipfs/merkledag" ft "github.com/jbenet/go-ipfs/unixfs" ftpb "github.com/jbenet/go-ipfs/unixfs/pb" diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index 4d4f20c88..73f3ea8cf 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -4,11 +4,11 @@ import ( "archive/tar" "bytes" "compress/gzip" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "io" gopath "path" "time" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" mdag "github.com/jbenet/go-ipfs/merkledag" path "github.com/jbenet/go-ipfs/path" uio "github.com/jbenet/go-ipfs/unixfs/io" From d68c24b8f95951b74caa67824a39c442cee740bc Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 23 Feb 2015 16:51:09 +0100 Subject: [PATCH 0773/3817] rewrote import paths of go.net/context to use golang.org/x/context - updated go-ctxgroup and goprocess ctxgroup: AddChildGroup was changed to AddChild. Used in two files: - p2p/net/mock/mock_net.go - routing/dht/dht.go - updated context from hg repo to git prev. commit in hg was ad01a6fcc8a19d3a4478c836895ffe883bd2ceab. (context: make parentCancelCtx iterative) represents commit 84f8955a887232b6308d79c68b8db44f64df455c in git repo - updated context to master (b6fdb7d8a4ccefede406f8fe0f017fb58265054c) Aaron Jacobs (2): net/context: Don't accept a context in the DoSomethingSlow example. context: Be clear that users must cancel the result of WithCancel. Andrew Gerrand (1): go.net: use golang.org/x/... import paths Bryan C. Mills (1): net/context: Don't leak goroutines in Done example. Damien Neil (1): context: fix removal of cancelled timer contexts from parent David Symonds (2): context: Fix WithValue example code. net: add import comments. Sameer Ajmani (1): context: fix TestAllocs to account for ints in interfaces This commit was moved from ipfs/go-ipfs-pinner@89fcdcda57429cdf932f51faf8b432a59e1e00d8 --- pinning/pinner/pin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 25edd5832..c53e17f1c 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -9,9 +9,9 @@ import ( "sync" "time" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" nsds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" "github.com/jbenet/go-ipfs/blocks/set" mdag "github.com/jbenet/go-ipfs/merkledag" "github.com/jbenet/go-ipfs/util" From b81b0cb94ea9460cacd83af595e60aba5ad749ec Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 23 Feb 2015 16:51:09 +0100 Subject: [PATCH 0774/3817] rewrote import paths of go.net/context to use golang.org/x/context - updated go-ctxgroup and goprocess ctxgroup: AddChildGroup was changed to AddChild. Used in two files: - p2p/net/mock/mock_net.go - routing/dht/dht.go - updated context from hg repo to git prev. commit in hg was ad01a6fcc8a19d3a4478c836895ffe883bd2ceab. (context: make parentCancelCtx iterative) represents commit 84f8955a887232b6308d79c68b8db44f64df455c in git repo - updated context to master (b6fdb7d8a4ccefede406f8fe0f017fb58265054c) Aaron Jacobs (2): net/context: Don't accept a context in the DoSomethingSlow example. context: Be clear that users must cancel the result of WithCancel. Andrew Gerrand (1): go.net: use golang.org/x/... import paths Bryan C. Mills (1): net/context: Don't leak goroutines in Done example. Damien Neil (1): context: fix removal of cancelled timer contexts from parent David Symonds (2): context: Fix WithValue example code. net: add import comments. Sameer Ajmani (1): context: fix TestAllocs to account for ints in interfaces This commit was moved from ipfs/go-ipfs-blockstore@e878e5c92fe2b68b0f32fcee4dae7c60cce778bb --- blockstore/blockstore.go | 3 +-- blockstore/blockstore_test.go | 2 +- blockstore/write_cache.go | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 70a705884..7c7e7ed2d 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -5,12 +5,11 @@ package blockstore import ( "errors" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" dsns "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" dsq "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/jbenet/go-ipfs/blocks" eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" u "github.com/jbenet/go-ipfs/util" diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 2280f78f8..0601773a0 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" dsq "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" ds_sync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/jbenet/go-ipfs/blocks" u "github.com/jbenet/go-ipfs/util" diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index 487899597..d082e0cdc 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -1,9 +1,8 @@ package blockstore import ( - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" - + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" "github.com/jbenet/go-ipfs/blocks" u "github.com/jbenet/go-ipfs/util" ) From 1dbe6e7ccf99071b120c066409635ee40736f6d4 Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 23 Feb 2015 16:51:09 +0100 Subject: [PATCH 0775/3817] rewrote import paths of go.net/context to use golang.org/x/context - updated go-ctxgroup and goprocess ctxgroup: AddChildGroup was changed to AddChild. Used in two files: - p2p/net/mock/mock_net.go - routing/dht/dht.go - updated context from hg repo to git prev. commit in hg was ad01a6fcc8a19d3a4478c836895ffe883bd2ceab. (context: make parentCancelCtx iterative) represents commit 84f8955a887232b6308d79c68b8db44f64df455c in git repo - updated context to master (b6fdb7d8a4ccefede406f8fe0f017fb58265054c) Aaron Jacobs (2): net/context: Don't accept a context in the DoSomethingSlow example. context: Be clear that users must cancel the result of WithCancel. Andrew Gerrand (1): go.net: use golang.org/x/... import paths Bryan C. Mills (1): net/context: Don't leak goroutines in Done example. Damien Neil (1): context: fix removal of cancelled timer contexts from parent David Symonds (2): context: Fix WithValue example code. net: add import comments. Sameer Ajmani (1): context: fix TestAllocs to account for ints in interfaces This commit was moved from ipfs/go-blockservice@c5d1cdd43b17c099066f0121b87d46c7d0310133 --- blockservice/blocks_test.go | 2 +- blockservice/blockservice.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index 8f3d89c7b..be1ca6059 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -5,9 +5,9 @@ import ( "testing" "time" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/jbenet/go-ipfs/blocks" blockstore "github.com/jbenet/go-ipfs/blocks/blockstore" blocksutil "github.com/jbenet/go-ipfs/blocks/blocksutil" diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 0d4dcfa05..ee84d79d6 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -7,7 +7,7 @@ import ( "errors" "fmt" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/jbenet/go-ipfs/blocks" "github.com/jbenet/go-ipfs/blocks/blockstore" worker "github.com/jbenet/go-ipfs/blockservice/worker" From b566cc30c28c3a3693bece32dc3ebc68e8db0f1f Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 23 Feb 2015 16:51:09 +0100 Subject: [PATCH 0776/3817] rewrote import paths of go.net/context to use golang.org/x/context - updated go-ctxgroup and goprocess ctxgroup: AddChildGroup was changed to AddChild. Used in two files: - p2p/net/mock/mock_net.go - routing/dht/dht.go - updated context from hg repo to git prev. commit in hg was ad01a6fcc8a19d3a4478c836895ffe883bd2ceab. (context: make parentCancelCtx iterative) represents commit 84f8955a887232b6308d79c68b8db44f64df455c in git repo - updated context to master (b6fdb7d8a4ccefede406f8fe0f017fb58265054c) Aaron Jacobs (2): net/context: Don't accept a context in the DoSomethingSlow example. context: Be clear that users must cancel the result of WithCancel. Andrew Gerrand (1): go.net: use golang.org/x/... import paths Bryan C. Mills (1): net/context: Don't leak goroutines in Done example. Damien Neil (1): context: fix removal of cancelled timer contexts from parent David Symonds (2): context: Fix WithValue example code. net: add import comments. Sameer Ajmani (1): context: fix TestAllocs to account for ints in interfaces This commit was moved from ipfs/go-ipfs-exchange-interface@5982c5b1cea284bf3db7cbd85e4bcab3a908d78b --- exchange/interface.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index aa2e2431c..c07d2a471 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -4,8 +4,7 @@ package exchange import ( "io" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/jbenet/go-ipfs/blocks" u "github.com/jbenet/go-ipfs/util" ) From c2081c1f32876b64be1ea1214fde42b647fedf91 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 26 Feb 2015 16:44:36 -0800 Subject: [PATCH 0777/3817] make the providers manager respect contexts This commit was moved from ipfs/go-ipfs-routing@55a1b13df004373a42febbce200b2936382dcaf7 --- routing/dht/handlers.go | 2 +- routing/dht/providers.go | 8 ++++++-- routing/dht/providers_test.go | 2 +- routing/dht/routing.go | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index c46c711a1..62b22c5ca 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -223,7 +223,7 @@ func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.M // add the received addresses to our peerstore. dht.peerstore.AddAddrs(pi.ID, pi.Addrs, peer.ProviderAddrTTL) } - dht.providers.AddProvider(key, p) + dht.providers.AddProvider(ctx, key, p) } return pmes, nil // send back same msg as confirmation. diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 1f95bdf82..d8e0d910d 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -99,11 +99,15 @@ func (pm *ProviderManager) run() { } } -func (pm *ProviderManager) AddProvider(k u.Key, val peer.ID) { - pm.newprovs <- &addProv{ +func (pm *ProviderManager) AddProvider(ctx context.Context, k u.Key, val peer.ID) { + prov := &addProv{ k: k, val: val, } + select { + case pm.newprovs <- prov: + case <-ctx.Done(): + } } func (pm *ProviderManager) GetProviders(ctx context.Context, k u.Key) []peer.ID { diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 22990d580..121992ede 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -14,7 +14,7 @@ func TestProviderManager(t *testing.T) { mid := peer.ID("testing") p := NewProviderManager(ctx, mid) a := u.Key("test") - p.AddProvider(a, peer.ID("testingprovider")) + p.AddProvider(ctx, a, peer.ID("testingprovider")) resp := p.GetProviders(ctx, a) if len(resp) != 1 { t.Fatal("Could not retrieve provider.") diff --git a/routing/dht/routing.go b/routing/dht/routing.go index b52fa0059..8cf487063 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -141,7 +141,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { defer log.EventBegin(ctx, "provide", &key).Done() // add self locally - dht.providers.AddProvider(key, dht.self) + dht.providers.AddProvider(ctx, key, dht.self) peers, err := dht.GetClosestPeers(ctx, key) if err != nil { From 6bf86e2dd42e9bf4c7465cda5a2a068c5cf40ff9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 26 Feb 2015 14:16:31 -0800 Subject: [PATCH 0778/3817] more understandable errors from merkledag decoding This commit was moved from ipfs/go-merkledag@7e655ddf31d99068404c92abf2bc2788efa8e312 --- ipld/merkledag/coding.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index cbd2de74a..9a1c23152 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -92,7 +92,10 @@ func (n *Node) Encoded(force bool) ([]byte, error) { // Decoded decodes raw data and returns a new Node instance. func Decoded(encoded []byte) (*Node, error) { - n := &Node{} + n := new(Node) err := n.Unmarshal(encoded) - return n, err + if err != nil { + return nil, fmt.Errorf("incorrectly formatted merkledag node: %s", err) + } + return n, nil } From 0bab0a7117e91e13e93da9b0466fcbedab8aa706 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 23 Feb 2015 00:25:20 -0800 Subject: [PATCH 0779/3817] make signing dht put records optional This commit was moved from ipfs/go-ipfs-routing@05c71b819ef150942c4d837f0d5a7cde635960df --- routing/dht/dht.go | 11 +-------- routing/dht/dht_test.go | 32 ++++++++++++++++++++----- routing/dht/ext_test.go | 2 +- routing/dht/records.go | 38 ++++++++++++++++++++---------- routing/dht/routing.go | 10 ++++---- routing/mock/centralized_client.go | 2 +- routing/offline/offline.go | 6 ++--- routing/record/record.go | 16 +++++++------ routing/record/validation.go | 26 ++++++++++---------- routing/routing.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/server.go | 5 +++- 12 files changed, 91 insertions(+), 61 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 1b318e4ee..6e5e6456e 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -254,16 +254,7 @@ func (dht *IpfsDHT) getOwnPrivateKey() (ci.PrivKey, error) { } // putLocal stores the key value pair in the datastore -func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error { - sk, err := dht.getOwnPrivateKey() - if err != nil { - return err - } - - rec, err := record.MakePutRecord(sk, key, value) - if err != nil { - return err - } +func (dht *IpfsDHT) putLocal(key u.Key, rec *pb.Record) error { data, err := proto.Marshal(rec) if err != nil { return err diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 54a644877..862693418 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -17,6 +17,7 @@ import ( peer "github.com/jbenet/go-ipfs/p2p/peer" netutil "github.com/jbenet/go-ipfs/p2p/test/util" routing "github.com/jbenet/go-ipfs/routing" + record "github.com/jbenet/go-ipfs/routing/record" u "github.com/jbenet/go-ipfs/util" ci "github.com/jbenet/go-ipfs/util/testutil/ci" @@ -147,7 +148,7 @@ func TestValueGetSet(t *testing.T) { connect(t, ctx, dhtA, dhtB) ctxT, _ := context.WithTimeout(ctx, time.Second) - dhtA.PutValue(ctxT, "/v/hello", []byte("world")) + dhtA.PutValue(ctxT, "/v/hello", []byte("world"), false) ctxT, _ = context.WithTimeout(ctx, time.Second*2) val, err := dhtA.GetValue(ctxT, "/v/hello") @@ -188,7 +189,13 @@ func TestProvides(t *testing.T) { for k, v := range testCaseValues { log.Debugf("adding local values for %s = %s", k, v) - err := dhts[3].putLocal(k, v) + sk := dhts[3].peerstore.PrivKey(dhts[3].self) + rec, err := record.MakePutRecord(sk, k, v, false) + if err != nil { + t.Fatal(err) + } + + err = dhts[3].putLocal(k, rec) if err != nil { t.Fatal(err) } @@ -456,7 +463,12 @@ func TestProvidesMany(t *testing.T) { providers[k] = dht.self t.Logf("adding local values for %s = %s (on %s)", k, v, dht.self) - err := dht.putLocal(k, v) + rec, err := record.MakePutRecord(nil, k, v, false) + if err != nil { + t.Fatal(err) + } + + err = dht.putLocal(k, rec) if err != nil { t.Fatal(err) } @@ -543,13 +555,21 @@ func TestProvidesAsync(t *testing.T) { connect(t, ctx, dhts[1], dhts[2]) connect(t, ctx, dhts[1], dhts[3]) - err := dhts[3].putLocal(u.Key("hello"), []byte("world")) + k := u.Key("hello") + val := []byte("world") + sk := dhts[3].peerstore.PrivKey(dhts[3].self) + rec, err := record.MakePutRecord(sk, k, val, false) + if err != nil { + t.Fatal(err) + } + + err = dhts[3].putLocal(k, rec) if err != nil { t.Fatal(err) } - bits, err := dhts[3].getLocal(u.Key("hello")) - if err != nil && bytes.Equal(bits, []byte("world")) { + bits, err := dhts[3].getLocal(k) + if err != nil && bytes.Equal(bits, val) { t.Fatal(err) } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index ec5fffac3..772724956 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -111,7 +111,7 @@ func TestGetFailures(t *testing.T) { t.Fatal(err) } - rec, err := record.MakePutRecord(sk, u.Key(str), []byte("blah")) + rec, err := record.MakePutRecord(sk, u.Key(str), []byte("blah"), true) if err != nil { t.Fatal(err) } diff --git a/routing/dht/records.go b/routing/dht/records.go index c6baf4999..e575c33ad 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -7,6 +7,7 @@ import ( ci "github.com/jbenet/go-ipfs/p2p/crypto" peer "github.com/jbenet/go-ipfs/p2p/peer" pb "github.com/jbenet/go-ipfs/routing/dht/pb" + record "github.com/jbenet/go-ipfs/routing/record" u "github.com/jbenet/go-ipfs/util" ctxutil "github.com/jbenet/go-ipfs/util/ctx" ) @@ -99,14 +100,20 @@ func (dht *IpfsDHT) getPublicKeyFromNode(ctx context.Context, p peer.ID) (ci.Pub // key, we fail. we do not search the dht. func (dht *IpfsDHT) verifyRecordLocally(r *pb.Record) error { - // First, validate the signature - p := peer.ID(r.GetAuthor()) - pk := dht.peerstore.PubKey(p) - if pk == nil { - return fmt.Errorf("do not have public key for %s", p) + if len(r.Signature) > 0 { + // First, validate the signature + p := peer.ID(r.GetAuthor()) + pk := dht.peerstore.PubKey(p) + if pk == nil { + return fmt.Errorf("do not have public key for %s", p) + } + + if err := record.CheckRecordSig(r, pk); err != nil { + return err + } } - return dht.Validator.VerifyRecord(r, pk) + return dht.Validator.VerifyRecord(r) } // verifyRecordOnline verifies a record, searching the DHT for the public key @@ -116,12 +123,19 @@ func (dht *IpfsDHT) verifyRecordLocally(r *pb.Record) error { // massive amplification attack on the dht. Use with care. func (dht *IpfsDHT) verifyRecordOnline(ctx context.Context, r *pb.Record) error { - // get the public key, search for it if necessary. - p := peer.ID(r.GetAuthor()) - pk, err := dht.getPublicKeyOnline(ctx, p) - if err != nil { - return err + if len(r.Signature) > 0 { + // get the public key, search for it if necessary. + p := peer.ID(r.GetAuthor()) + pk, err := dht.getPublicKeyOnline(ctx, p) + if err != nil { + return err + } + + err = record.CheckRecordSig(r, pk) + if err != nil { + return err + } } - return dht.Validator.VerifyRecord(r, pk) + return dht.Validator.VerifyRecord(r) } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 8cf487063..3f548e21d 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -29,21 +29,21 @@ var asyncQueryBuffer = 10 // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT -func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error { +func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte, sign bool) error { log.Debugf("PutValue %s", key) - err := dht.putLocal(key, value) + sk, err := dht.getOwnPrivateKey() if err != nil { return err } - sk, err := dht.getOwnPrivateKey() + rec, err := record.MakePutRecord(sk, key, value, sign) if err != nil { + log.Debug("Creation of record failed!") return err } - rec, err := record.MakePutRecord(sk, key, value) + err = dht.putLocal(key, rec) if err != nil { - log.Debug("Creation of record failed!") return err } diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 6a550bfaa..cbcff3ab0 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -22,7 +22,7 @@ type client struct { } // FIXME(brian): is this method meant to simulate putting a value into the network? -func (c *client) PutValue(ctx context.Context, key u.Key, val []byte) error { +func (c *client) PutValue(ctx context.Context, key u.Key, val []byte, sign bool) error { log.Debugf("PutValue: %s", key) return c.datastore.Put(key.DsKey(), val) } diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 324e438be..33c57d336 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -35,8 +35,8 @@ type offlineRouting struct { sk ci.PrivKey } -func (c *offlineRouting) PutValue(ctx context.Context, key u.Key, val []byte) error { - rec, err := record.MakePutRecord(c.sk, key, val) +func (c *offlineRouting) PutValue(ctx context.Context, key u.Key, val []byte, sign bool) error { + rec, err := record.MakePutRecord(c.sk, key, val, sign) if err != nil { return err } @@ -89,7 +89,7 @@ func (c *offlineRouting) Ping(ctx context.Context, p peer.ID) (time.Duration, er return 0, ErrOffline } -func (c *offlineRouting) Bootstrap(context.Context) (error) { +func (c *offlineRouting) Bootstrap(context.Context) error { return nil } diff --git a/routing/record/record.go b/routing/record/record.go index e41de94ae..c5575a86f 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -14,7 +14,7 @@ import ( var log = eventlog.Logger("routing/record") // MakePutRecord creates and signs a dht record for the given key/value pair -func MakePutRecord(sk ci.PrivKey, key u.Key, value []byte) (*pb.Record, error) { +func MakePutRecord(sk ci.PrivKey, key u.Key, value []byte, sign bool) (*pb.Record, error) { record := new(pb.Record) record.Key = proto.String(string(key)) @@ -26,14 +26,16 @@ func MakePutRecord(sk ci.PrivKey, key u.Key, value []byte) (*pb.Record, error) { } record.Author = proto.String(string(pkh)) - blob := RecordBlobForSig(record) + if sign { + blob := RecordBlobForSig(record) - sig, err := sk.Sign(blob) - if err != nil { - return nil, err - } + sig, err := sk.Sign(blob) + if err != nil { + return nil, err + } - record.Signature = sig + record.Signature = sig + } return record, nil } diff --git a/routing/record/validation.go b/routing/record/validation.go index 9519ebd3f..4c6db584f 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -29,19 +29,7 @@ type Validator map[string]ValidatorFunc // VerifyRecord checks a record and ensures it is still valid. // It runs needed validators -func (v Validator) VerifyRecord(r *pb.Record, pk ci.PubKey) error { - // First, validate the signature - blob := RecordBlobForSig(r) - ok, err := pk.Verify(blob, r.GetSignature()) - if err != nil { - log.Info("Signature verify failed. (ignored)") - return err - } - if !ok { - log.Info("dht found a forged record! (ignored)") - return ErrBadRecord - } - +func (v Validator) VerifyRecord(r *pb.Record) error { // Now, check validity func parts := strings.Split(r.GetKey(), "/") if len(parts) < 3 { @@ -73,3 +61,15 @@ func ValidatePublicKeyRecord(k u.Key, val []byte) error { } return nil } + +func CheckRecordSig(r *pb.Record, pk ci.PubKey) error { + blob := RecordBlobForSig(r) + good, err := pk.Verify(blob, r.Signature) + if err != nil { + return nil + } + if !good { + return errors.New("invalid record signature") + } + return nil +} diff --git a/routing/routing.go b/routing/routing.go index be400a520..d0acbb077 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -21,7 +21,7 @@ type IpfsRouting interface { // Basic Put/Get // PutValue adds value corresponding to given Key. - PutValue(context.Context, u.Key, []byte) error + PutValue(context.Context, u.Key, []byte, bool) error // GetValue searches for the value corresponding to given Key. GetValue(context.Context, u.Key) ([]byte, error) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 09fa90ef6..fbba8dfee 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -59,7 +59,7 @@ func (c *Client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-cha return ch } -func (c *Client) PutValue(ctx context.Context, k u.Key, v []byte) error { +func (c *Client) PutValue(ctx context.Context, k u.Key, v []byte, sign bool) error { defer log.EventBegin(ctx, "putValue", &k).Done() r, err := makeRecord(c.peerstore, c.local, k, v) if err != nil { diff --git a/routing/supernode/server.go b/routing/supernode/server.go index be587f749..315730858 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -210,7 +210,10 @@ func verify(ps peer.Peerstore, r *dhtpb.Record) error { if pk == nil { return fmt.Errorf("do not have public key for %s", p) } - if err := v.VerifyRecord(r, pk); err != nil { + if err := record.CheckRecordSig(r, pk); err != nil { + return err + } + if err := v.VerifyRecord(r); err != nil { return err } return nil From 97a15304d5986b67196ff794d244ac1a8629f534 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 23 Feb 2015 00:25:20 -0800 Subject: [PATCH 0780/3817] make signing dht put records optional This commit was moved from ipfs/go-namesys@2de3180d5d0c6dc2727dc8011c7cb16112568b0e --- namesys/publisher.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 656730b40..126069d96 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -62,7 +62,7 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value u.Key) log.Debugf("Storing pubkey at: %s", namekey) // Store associated public key timectx, _ := context.WithDeadline(ctx, time.Now().Add(time.Second*10)) - err = p.routing.PutValue(timectx, namekey, pkbytes) + err = p.routing.PutValue(timectx, namekey, pkbytes, false) if err != nil { return err } @@ -72,7 +72,7 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value u.Key) log.Debugf("Storing ipns entry at: %s", ipnskey) // Store ipns entry at "/ipns/"+b58(h(pubkey)) timectx, _ = context.WithDeadline(ctx, time.Now().Add(time.Second*10)) - err = p.routing.PutValue(timectx, ipnskey, data) + err = p.routing.PutValue(timectx, ipnskey, data, true) if err != nil { return err } From b88c409743b7ed718f53552a27df535eae407cf7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 25 Feb 2015 14:56:26 -0800 Subject: [PATCH 0781/3817] move signing options into a validation checker struct This commit was moved from ipfs/go-ipfs-routing@9e75edaf7d19594df304d3536cc6615f480c13c1 --- routing/dht/dht.go | 2 +- routing/dht/dht_test.go | 16 ++++++++++----- routing/dht/routing.go | 7 ++++++- routing/mock/centralized_client.go | 2 +- routing/offline/offline.go | 4 ++-- routing/record/validation.go | 33 +++++++++++++++++++++++++++--- routing/routing.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/server.go | 2 +- 9 files changed, 54 insertions(+), 16 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 6e5e6456e..324b80fb6 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -84,7 +84,7 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip dht.birth = time.Now() dht.Validator = make(record.Validator) - dht.Validator["pk"] = record.ValidatePublicKeyRecord + dht.Validator["pk"] = record.PublicKeyValidator if doPinging { dht.Children().Add(1) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 862693418..4b48ccc65 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -41,8 +41,11 @@ func setupDHT(ctx context.Context, t *testing.T) *IpfsDHT { dss := dssync.MutexWrap(ds.NewMapDatastore()) d := NewDHT(ctx, h, dss) - d.Validator["v"] = func(u.Key, []byte) error { - return nil + d.Validator["v"] = &record.ValidChecker{ + Func: func(u.Key, []byte) error { + return nil + }, + Sign: false, } return d } @@ -139,8 +142,11 @@ func TestValueGetSet(t *testing.T) { defer dhtA.host.Close() defer dhtB.host.Close() - vf := func(u.Key, []byte) error { - return nil + vf := &record.ValidChecker{ + Func: func(u.Key, []byte) error { + return nil + }, + Sign: false, } dhtA.Validator["v"] = vf dhtB.Validator["v"] = vf @@ -148,7 +154,7 @@ func TestValueGetSet(t *testing.T) { connect(t, ctx, dhtA, dhtB) ctxT, _ := context.WithTimeout(ctx, time.Second) - dhtA.PutValue(ctxT, "/v/hello", []byte("world"), false) + dhtA.PutValue(ctxT, "/v/hello", []byte("world")) ctxT, _ = context.WithTimeout(ctx, time.Second*2) val, err := dhtA.GetValue(ctxT, "/v/hello") diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 3f548e21d..28ee8f03a 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -29,13 +29,18 @@ var asyncQueryBuffer = 10 // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT -func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte, sign bool) error { +func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error { log.Debugf("PutValue %s", key) sk, err := dht.getOwnPrivateKey() if err != nil { return err } + sign, err := dht.Validator.IsSigned(key) + if err != nil { + return err + } + rec, err := record.MakePutRecord(sk, key, value, sign) if err != nil { log.Debug("Creation of record failed!") diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index cbcff3ab0..6a550bfaa 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -22,7 +22,7 @@ type client struct { } // FIXME(brian): is this method meant to simulate putting a value into the network? -func (c *client) PutValue(ctx context.Context, key u.Key, val []byte, sign bool) error { +func (c *client) PutValue(ctx context.Context, key u.Key, val []byte) error { log.Debugf("PutValue: %s", key) return c.datastore.Put(key.DsKey(), val) } diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 33c57d336..15049f16d 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -35,8 +35,8 @@ type offlineRouting struct { sk ci.PrivKey } -func (c *offlineRouting) PutValue(ctx context.Context, key u.Key, val []byte, sign bool) error { - rec, err := record.MakePutRecord(c.sk, key, val, sign) +func (c *offlineRouting) PutValue(ctx context.Context, key u.Key, val []byte) error { + rec, err := record.MakePutRecord(c.sk, key, val, false) if err != nil { return err } diff --git a/routing/record/validation.go b/routing/record/validation.go index 4c6db584f..380bdea4c 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -25,7 +25,12 @@ var ErrInvalidRecordType = errors.New("invalid record keytype") // Validator is an object that helps ensure routing records are valid. // It is a collection of validator functions, each of which implements // its own notion of validity. -type Validator map[string]ValidatorFunc +type Validator map[string]*ValidChecker + +type ValidChecker struct { + Func ValidatorFunc + Sign bool +} // VerifyRecord checks a record and ensures it is still valid. // It runs needed validators @@ -37,13 +42,30 @@ func (v Validator) VerifyRecord(r *pb.Record) error { return nil } - fnc, ok := v[parts[1]] + val, ok := v[parts[1]] if !ok { log.Infof("Unrecognized key prefix: %s", parts[1]) return ErrInvalidRecordType } - return fnc(u.Key(r.GetKey()), r.GetValue()) + return val.Func(u.Key(r.GetKey()), r.GetValue()) +} + +func (v Validator) IsSigned(k u.Key) (bool, error) { + // Now, check validity func + parts := strings.Split(string(k), "/") + if len(parts) < 3 { + log.Infof("Record key does not have validator: %s", k) + return false, nil + } + + val, ok := v[parts[1]] + if !ok { + log.Infof("Unrecognized key prefix: %s", parts[1]) + return false, ErrInvalidRecordType + } + + return val.Sign, nil } // ValidatePublicKeyRecord implements ValidatorFunc and @@ -62,6 +84,11 @@ func ValidatePublicKeyRecord(k u.Key, val []byte) error { return nil } +var PublicKeyValidator = &ValidChecker{ + Func: ValidatePublicKeyRecord, + Sign: false, +} + func CheckRecordSig(r *pb.Record, pk ci.PubKey) error { blob := RecordBlobForSig(r) good, err := pk.Verify(blob, r.Signature) diff --git a/routing/routing.go b/routing/routing.go index d0acbb077..be400a520 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -21,7 +21,7 @@ type IpfsRouting interface { // Basic Put/Get // PutValue adds value corresponding to given Key. - PutValue(context.Context, u.Key, []byte, bool) error + PutValue(context.Context, u.Key, []byte) error // GetValue searches for the value corresponding to given Key. GetValue(context.Context, u.Key) ([]byte, error) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index fbba8dfee..09fa90ef6 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -59,7 +59,7 @@ func (c *Client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-cha return ch } -func (c *Client) PutValue(ctx context.Context, k u.Key, v []byte, sign bool) error { +func (c *Client) PutValue(ctx context.Context, k u.Key, v []byte) error { defer log.EventBegin(ctx, "putValue", &k).Done() r, err := makeRecord(c.peerstore, c.local, k, v) if err != nil { diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 315730858..1132039a1 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -204,7 +204,7 @@ func providerKey(k util.Key) datastore.Key { func verify(ps peer.Peerstore, r *dhtpb.Record) error { v := make(record.Validator) - v["pk"] = record.ValidatePublicKeyRecord + v["pk"] = record.PublicKeyValidator p := peer.ID(r.GetAuthor()) pk := ps.PubKey(p) if pk == nil { From 62d3b0b2634fa0fe66e772767eb082f5a439524a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 25 Feb 2015 14:56:26 -0800 Subject: [PATCH 0782/3817] move signing options into a validation checker struct This commit was moved from ipfs/go-namesys@d56e307ea149c453aac7b6f2c651678b836cd1d8 --- namesys/publisher.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 126069d96..cb7456cb9 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -13,6 +13,7 @@ import ( pb "github.com/jbenet/go-ipfs/namesys/internal/pb" ci "github.com/jbenet/go-ipfs/p2p/crypto" routing "github.com/jbenet/go-ipfs/routing" + record "github.com/jbenet/go-ipfs/routing/record" u "github.com/jbenet/go-ipfs/util" ) @@ -62,7 +63,7 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value u.Key) log.Debugf("Storing pubkey at: %s", namekey) // Store associated public key timectx, _ := context.WithDeadline(ctx, time.Now().Add(time.Second*10)) - err = p.routing.PutValue(timectx, namekey, pkbytes, false) + err = p.routing.PutValue(timectx, namekey, pkbytes) if err != nil { return err } @@ -72,7 +73,7 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value u.Key) log.Debugf("Storing ipns entry at: %s", ipnskey) // Store ipns entry at "/ipns/"+b58(h(pubkey)) timectx, _ = context.WithDeadline(ctx, time.Now().Add(time.Second*10)) - err = p.routing.PutValue(timectx, ipnskey, data, true) + err = p.routing.PutValue(timectx, ipnskey, data) if err != nil { return err } @@ -105,6 +106,11 @@ func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte { []byte{}) } +var IpnsRecordValidator = &record.ValidChecker{ + Func: ValidateIpnsRecord, + Sign: true, +} + // ValidateIpnsRecord implements ValidatorFunc and verifies that the // given 'val' is an IpnsEntry and that that entry is valid. func ValidateIpnsRecord(k u.Key, val []byte) error { From f99a04a24e7da8c5cc286ecd67000ed4d32177d1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 26 Feb 2015 16:13:33 -0800 Subject: [PATCH 0783/3817] error -> debug This commit was moved from ipfs/go-ipfs-routing@7f2386b9a806829c8453182c397e1a525b8053bf --- routing/dht/dht.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 324b80fb6..14211cc81 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -313,7 +313,7 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, p peer.ID, count int) [ // == to self? thats bad for _, p := range closer { if p == dht.self { - log.Info("Attempted to return self! this shouldnt happen...") + log.Debug("Attempted to return self! this shouldnt happen...") return nil } } From 7d04c746ac1eeafb22bde26aae88e5c4cec55532 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 2 Feb 2015 02:48:12 +0000 Subject: [PATCH 0784/3817] implement a simple wantlist command to allow the user to view their wantlist This commit was moved from ipfs/go-ipfs-exchange-offline@cc7ab064d4968d6497eb6c380aa2707025653da3 --- exchange/offline/offline.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index fe8fa723c..e3e50bbf4 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -66,3 +66,8 @@ func (e *offlineExchange) GetBlocks(ctx context.Context, ks []u.Key) (<-chan *bl }() return out, nil } + +// implement Exchange +func (e *offlineExchange) GetWantlist() []u.Key { + return nil +} From f22be215d2f0008ce6377795a66f10ec3ba360cf Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 23 Feb 2015 11:47:49 -0800 Subject: [PATCH 0785/3817] dont put wantlist getter in exchange interface This commit was moved from ipfs/go-ipfs-exchange-offline@85b2f60fe8ea627fcdcc5830277ab74317a904f4 --- exchange/offline/offline.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index e3e50bbf4..fe8fa723c 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -66,8 +66,3 @@ func (e *offlineExchange) GetBlocks(ctx context.Context, ks []u.Key) (<-chan *bl }() return out, nil } - -// implement Exchange -func (e *offlineExchange) GetWantlist() []u.Key { - return nil -} From 6780d1fcb41a90e6dda8f804c9622a1fc469adc7 Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 27 Feb 2015 14:19:08 +0100 Subject: [PATCH 0786/3817] don't depend on go-logging - use it through util instead. This commit was moved from ipfs/go-unixfs@a3c28a1a3d563e9ab17f0ad561edc92c69e653cf --- unixfs/io/dagmodifier_test.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go index 844393950..ca9c42004 100644 --- a/unixfs/io/dagmodifier_test.go +++ b/unixfs/io/dagmodifier_test.go @@ -17,7 +17,6 @@ import ( u "github.com/jbenet/go-ipfs/util" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) @@ -95,8 +94,12 @@ func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) func TestDagModifierBasic(t *testing.T) { t.Skip("DAGModifier needs to be fixed to work with indirect blocks.") - logging.SetLevel(logging.CRITICAL, "blockservice") - logging.SetLevel(logging.CRITICAL, "merkledag") + if err := u.SetLogLevel("blockservice", "critical"); err != nil { + t.Fatalf("testlog prepare failed: %s", err) + } + if err := u.SetLogLevel("merkledag", "critical"); err != nil { + t.Fatalf("testlog prepare failed: %s", err) + } dserv := getMockDagServ(t) b, n := getNode(t, dserv, 50000) From 2f312b000cd2d5976ebeff78417b64fc4d527b88 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 4 Mar 2015 08:28:46 -0800 Subject: [PATCH 0787/3817] fixed dht kbucket race closes #836 This commit was moved from ipfs/go-ipfs-routing@5f313283847fb2bdb624b890e81785d369caf9cf --- routing/kbucket/table.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index a785bf8b5..7b10d8daf 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -185,9 +185,11 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []peer.ID { // Size returns the total number of peers in the routing table func (rt *RoutingTable) Size() int { var tot int + rt.tabLock.RLock() for _, buck := range rt.Buckets { tot += buck.Len() } + rt.tabLock.RUnlock() return tot } From 6c5ed6bc3e70db7f933ee390d1893a6792c2ee44 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 6 Mar 2015 11:20:11 -0800 Subject: [PATCH 0788/3817] refactoring unixfs node and importer/helpers to match This commit was moved from ipfs/go-unixfs@3831428ed524c01258cfee2449d6a9bca92caa46 --- unixfs/format.go | 66 ++++++++++++++++++++++++++++++++----------- unixfs/format_test.go | 11 ++++---- 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/unixfs/format.go b/unixfs/format.go index 5e12d52ac..61bb2ec9e 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -9,6 +9,13 @@ import ( pb "github.com/jbenet/go-ipfs/unixfs/pb" ) +const ( + TRaw = pb.Data_Raw + TFile = pb.Data_File + TDirectory = pb.Data_Directory + TMetadata = pb.Data_Metadata +) + var ErrMalformedFileFormat = errors.New("malformed data in file format") var ErrInvalidDirLocation = errors.New("found directory node in unexpected place") var ErrUnrecognizedType = errors.New("unrecognized node type") @@ -98,33 +105,60 @@ func DataSize(data []byte) (uint64, error) { } } -type MultiBlock struct { - Data []byte +type FSNode struct { + Data []byte + + // total data size for each child blocksizes []uint64 - subtotal uint64 + + // running sum of blocksizes + subtotal uint64 + + // node type of this node + Type pb.Data_DataType +} + +func FSNodeFromBytes(b []byte) (*FSNode, error) { + pbn := new(pb.Data) + err := proto.Unmarshal(b, pbn) + if err != nil { + return nil, err + } + + n := new(FSNode) + n.Data = pbn.Data + n.blocksizes = pbn.Blocksizes + n.subtotal = pbn.GetFilesize() - uint64(len(n.Data)) + n.Type = pbn.GetType() + return n, nil +} + +// AddBlockSize adds the size of the next child block of this node +func (n *FSNode) AddBlockSize(s uint64) { + n.subtotal += s + n.blocksizes = append(n.blocksizes, s) } -func (mb *MultiBlock) AddBlockSize(s uint64) { - mb.subtotal += s - mb.blocksizes = append(mb.blocksizes, s) +func (n *FSNode) RemoveBlockSize(i int) { + n.subtotal -= n.blocksizes[i] + n.blocksizes = append(n.blocksizes[:i], n.blocksizes[i+1:]...) } -func (mb *MultiBlock) GetBytes() ([]byte, error) { +func (n *FSNode) GetBytes() ([]byte, error) { pbn := new(pb.Data) - t := pb.Data_File - pbn.Type = &t - pbn.Filesize = proto.Uint64(uint64(len(mb.Data)) + mb.subtotal) - pbn.Blocksizes = mb.blocksizes - pbn.Data = mb.Data + pbn.Type = &n.Type + pbn.Filesize = proto.Uint64(uint64(len(n.Data)) + n.subtotal) + pbn.Blocksizes = n.blocksizes + pbn.Data = n.Data return proto.Marshal(pbn) } -func (mb *MultiBlock) FileSize() uint64 { - return uint64(len(mb.Data)) + mb.subtotal +func (n *FSNode) FileSize() uint64 { + return uint64(len(n.Data)) + n.subtotal } -func (mb *MultiBlock) NumChildren() int { - return len(mb.blocksizes) +func (n *FSNode) NumChildren() int { + return len(n.blocksizes) } type Metadata struct { diff --git a/unixfs/format_test.go b/unixfs/format_test.go index 5065c7bc5..b15ed0789 100644 --- a/unixfs/format_test.go +++ b/unixfs/format_test.go @@ -7,15 +7,16 @@ import ( pb "github.com/jbenet/go-ipfs/unixfs/pb" ) -func TestMultiBlock(t *testing.T) { - mbf := new(MultiBlock) +func TestFSNode(t *testing.T) { + fsn := new(FSNode) + fsn.Type = TFile for i := 0; i < 15; i++ { - mbf.AddBlockSize(100) + fsn.AddBlockSize(100) } - mbf.Data = make([]byte, 128) + fsn.Data = make([]byte, 128) - b, err := mbf.GetBytes() + b, err := fsn.GetBytes() if err != nil { t.Fatal(err) } From c2dfd658553dfd4c23c205b385dfec31642e8e8b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 7 Mar 2015 02:44:20 -0800 Subject: [PATCH 0789/3817] query: fixed race condition This commit was moved from ipfs/go-ipfs-routing@159785fb1d5d4ed59a470bdbb5307cfa2e5bc1da --- routing/dht/query.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/routing/dht/query.go b/routing/dht/query.go index aacab106f..888fdd65d 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -52,6 +52,12 @@ type queryFunc func(context.Context, peer.ID) (*dhtQueryResult, error) // Run runs the query at hand. pass in a list of peers to use first. func (q *dhtQuery) Run(ctx context.Context, peers []peer.ID) (*dhtQueryResult, error) { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -104,6 +110,15 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { r.addPeerToQuery(r.cg.Context(), p) } + // may be closed already. this caused an odd race (where we attempt to + // add a child to an already closed ctxgroup). this is a temp workaround + // as we'll switch to using a proc here soon. + select { + case <-r.cg.Closed(): + return nil, r.cg.Context().Err() + default: + } + // go do this thing. // do it as a child func to make sure Run exits // ONLY AFTER spawn workers has exited. From 2d919f0f4784d9c749098f88ca4bed561ec0ccaa Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 7 Mar 2015 03:20:29 -0800 Subject: [PATCH 0790/3817] query: fix race condition: redux this time just move to goprocess This commit was moved from ipfs/go-ipfs-routing@39421d0f2be633f7cb395fbb2e7fe95de9751dd2 --- routing/dht/ext_test.go | 2 +- routing/dht/query.go | 78 ++++++++++++++++++++++------------------- 2 files changed, 42 insertions(+), 38 deletions(-) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 772724956..539d55cca 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -52,7 +52,7 @@ func TestGetFailures(t *testing.T) { err = merr[0] } - if err != context.DeadlineExceeded { + if err != context.DeadlineExceeded && err != context.Canceled { t.Fatal("Got different error than we expected", err) } } else { diff --git a/routing/dht/query.go b/routing/dht/query.go index 888fdd65d..3687bc859 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -12,7 +12,8 @@ import ( pset "github.com/jbenet/go-ipfs/util/peerset" todoctr "github.com/jbenet/go-ipfs/util/todocounter" - ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" + process "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" + ctxproc "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) @@ -61,8 +62,8 @@ func (q *dhtQuery) Run(ctx context.Context, peers []peer.ID) (*dhtQueryResult, e ctx, cancel := context.WithCancel(ctx) defer cancel() - runner := newQueryRunner(ctx, q) - return runner.Run(peers) + runner := newQueryRunner(q) + return runner.Run(ctx, peers) } type dhtQueryRunner struct { @@ -77,22 +78,24 @@ type dhtQueryRunner struct { rateLimit chan struct{} // processing semaphore log eventlog.EventLogger - cg ctxgroup.ContextGroup + proc process.Process sync.RWMutex } -func newQueryRunner(ctx context.Context, q *dhtQuery) *dhtQueryRunner { +func newQueryRunner(q *dhtQuery) *dhtQueryRunner { + proc := process.WithParent(process.Background()) + ctx := ctxproc.WithProcessClosing(context.Background(), proc) return &dhtQueryRunner{ query: q, peersToQuery: queue.NewChanQueue(ctx, queue.NewXORDistancePQ(q.key)), peersRemaining: todoctr.NewSyncCounter(), peersSeen: pset.New(), rateLimit: make(chan struct{}, q.concurrency), - cg: ctxgroup.WithContext(ctx), + proc: proc, } } -func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { +func (r *dhtQueryRunner) Run(ctx context.Context, peers []peer.ID) (*dhtQueryResult, error) { r.log = log if len(peers) == 0 { @@ -107,31 +110,30 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { // add all the peers we got first. for _, p := range peers { - r.addPeerToQuery(r.cg.Context(), p) - } - - // may be closed already. this caused an odd race (where we attempt to - // add a child to an already closed ctxgroup). this is a temp workaround - // as we'll switch to using a proc here soon. - select { - case <-r.cg.Closed(): - return nil, r.cg.Context().Err() - default: + r.addPeerToQuery(p) } // go do this thing. - // do it as a child func to make sure Run exits + // do it as a child proc to make sure Run exits // ONLY AFTER spawn workers has exited. - r.cg.AddChildFunc(r.spawnWorkers) + r.proc.Go(r.spawnWorkers) // so workers are working. // wait until they're done. err := routing.ErrNotFound + // now, if the context finishes, close the proc. + // we have to do it here because the logic before is setup, which + // should run without closing the proc. + go func() { + <-ctx.Done() + r.proc.Close() + }() + select { case <-r.peersRemaining.Done(): - r.cg.Close() + r.proc.Close() r.RLock() defer r.RUnlock() @@ -143,12 +145,10 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { err = r.errs[0] } - case <-r.cg.Closed(): - log.Debug("r.cg.Closed()") - + case <-r.proc.Closed(): r.RLock() defer r.RUnlock() - err = r.cg.Context().Err() // collect the error. + err = context.DeadlineExceeded } if r.result != nil && r.result.success { @@ -158,7 +158,7 @@ func (r *dhtQueryRunner) Run(peers []peer.ID) (*dhtQueryResult, error) { return nil, err } -func (r *dhtQueryRunner) addPeerToQuery(ctx context.Context, next peer.ID) { +func (r *dhtQueryRunner) addPeerToQuery(next peer.ID) { // if new peer is ourselves... if next == r.query.dht.self { r.log.Debug("addPeerToQuery skip self") @@ -172,18 +172,18 @@ func (r *dhtQueryRunner) addPeerToQuery(ctx context.Context, next peer.ID) { r.peersRemaining.Increment(1) select { case r.peersToQuery.EnqChan <- next: - case <-ctx.Done(): + case <-r.proc.Closing(): } } -func (r *dhtQueryRunner) spawnWorkers(parent ctxgroup.ContextGroup) { +func (r *dhtQueryRunner) spawnWorkers(proc process.Process) { for { select { case <-r.peersRemaining.Done(): return - case <-r.cg.Closing(): + case <-r.proc.Closing(): return case p, more := <-r.peersToQuery.DeqChan: @@ -193,24 +193,27 @@ func (r *dhtQueryRunner) spawnWorkers(parent ctxgroup.ContextGroup) { // do it as a child func to make sure Run exits // ONLY AFTER spawn workers has exited. - parent.AddChildFunc(func(cg ctxgroup.ContextGroup) { - r.queryPeer(cg, p) + proc.Go(func(proc process.Process) { + r.queryPeer(proc, p) }) } } } -func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { +func (r *dhtQueryRunner) queryPeer(proc process.Process, p peer.ID) { // make sure we rate limit concurrency. select { case <-r.rateLimit: - case <-cg.Closing(): + case <-proc.Closing(): r.peersRemaining.Decrement(1) return } // ok let's do this! + // create a context from our proc. + ctx := ctxproc.WithProcessClosing(context.Background(), proc) + // make sure we do this when we exit defer func() { // signal we're done proccessing peer p @@ -227,10 +230,11 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { r.rateLimit <- struct{}{} pi := peer.PeerInfo{ID: p} - if err := r.query.dht.host.Connect(cg.Context(), pi); err != nil { + + if err := r.query.dht.host.Connect(ctx, pi); err != nil { log.Debugf("Error connecting: %s", err) - notif.PublishQueryEvent(cg.Context(), ¬if.QueryEvent{ + notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ Type: notif.QueryError, Extra: err.Error(), }) @@ -246,7 +250,7 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { } // finally, run the query against this peer - res, err := r.query.qfunc(cg.Context(), p) + res, err := r.query.qfunc(ctx, p) if err != nil { log.Debugf("ERROR worker for: %v %v", p, err) @@ -259,7 +263,7 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { r.Lock() r.result = res r.Unlock() - go r.cg.Close() // signal to everyone that we're done. + go r.proc.Close() // signal to everyone that we're done. // must be async, as we're one of the children, and Close blocks. } else if len(res.closerPeers) > 0 { @@ -272,7 +276,7 @@ func (r *dhtQueryRunner) queryPeer(cg ctxgroup.ContextGroup, p peer.ID) { // add their addresses to the dialer's peerstore r.query.dht.peerstore.AddAddrs(next.ID, next.Addrs, peer.TempAddrTTL) - r.addPeerToQuery(cg.Context(), next.ID) + r.addPeerToQuery(next.ID) log.Debugf("PEERS CLOSER -- worker for: %v added %v (%v)", p, next.ID, next.Addrs) } } else { From dbbb7ff1a843f36a657ee33518f0462f08605d76 Mon Sep 17 00:00:00 2001 From: Henry Date: Wed, 25 Feb 2015 14:39:56 +0100 Subject: [PATCH 0791/3817] don't ignore returned cancelFunc() This commit was moved from ipfs/go-ipfs-routing@9a913bbf8a57bf8edc028f0448b16810e19ebdcf --- routing/dht/records.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/routing/dht/records.go b/routing/dht/records.go index e575c33ad..e327ed171 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -28,7 +28,8 @@ func (dht *IpfsDHT) getPublicKeyOnline(ctx context.Context, p peer.ID) (ci.PubKe } // ok, try the node itself. if they're overwhelmed or slow we can move on. - ctxT, _ := ctxutil.WithDeadlineFraction(ctx, 0.3) + ctxT, cancelFunc := ctxutil.WithDeadlineFraction(ctx, 0.3) + defer cancelFunc() if pk, err := dht.getPublicKeyFromNode(ctx, p); err == nil { return pk, nil } From daed88d8413822cea56502c6368df68706bfa383 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 7 Mar 2015 09:31:46 -0800 Subject: [PATCH 0792/3817] added cancel func calls previously ignored This commit was moved from ipfs/go-unixfs@5ac81bcd70fab0a720650bd948c525c6f82ddb9f --- unixfs/tar/reader.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index 73f3ea8cf..aa15c823a 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -86,7 +86,8 @@ func (r *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { } r.flush() - ctx, _ := context.WithTimeout(context.TODO(), time.Second*60) + ctx, cancel := context.WithTimeout(context.TODO(), time.Second*60) + defer cancel() for i, ng := range r.dag.GetDAG(ctx, dagnode) { childNode, err := ng.Get() From 1b1d6bf1d751f38b24c71f3973e88056612734fa Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 7 Mar 2015 09:31:46 -0800 Subject: [PATCH 0793/3817] added cancel func calls previously ignored This commit was moved from ipfs/go-ipfs-pinner@437724c9145284c9f513060c05617127917059cc --- pinning/pinner/pin.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index c53e17f1c..68e627c1e 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -172,7 +172,9 @@ func (p *pinner) pinIndirectRecurse(node *mdag.Node) error { } func (p *pinner) pinLinks(node *mdag.Node) error { - ctx, _ := context.WithTimeout(context.Background(), time.Second*60) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*60) + defer cancel() + for _, ng := range p.dserv.GetDAG(ctx, node) { subnode, err := ng.Get() if err != nil { From e23c43d4ff7487b40fdde2771b37044d9e4b6b3e Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 7 Mar 2015 09:31:46 -0800 Subject: [PATCH 0794/3817] added cancel func calls previously ignored This commit was moved from ipfs/go-namesys@19cf7a4c6a79a6b1a9308c376aa7754731f5ecdf --- namesys/publisher.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index cb7456cb9..d786c210b 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -60,9 +60,11 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value u.Key) nameb := u.Hash(pkbytes) namekey := u.Key("/pk/" + string(nameb)) + timectx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Second*10)) + defer cancel() + log.Debugf("Storing pubkey at: %s", namekey) // Store associated public key - timectx, _ := context.WithDeadline(ctx, time.Now().Add(time.Second*10)) err = p.routing.PutValue(timectx, namekey, pkbytes) if err != nil { return err From 54529e6301b008ca47118cb04b3c4e7ae928d941 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 7 Mar 2015 09:31:46 -0800 Subject: [PATCH 0795/3817] added cancel func calls previously ignored This commit was moved from ipfs/go-ipfs-routing@cf7c31b410a3d371703f144c99207478f1d0fe1b --- routing/dht/dht.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 14211cc81..974d87b58 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -357,11 +357,12 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { rand.Read(id) peers := dht.routingTable.NearestPeers(kb.ConvertKey(u.Key(id)), 5) for _, p := range peers { - ctx, _ := context.WithTimeout(dht.Context(), time.Second*5) + ctx, cancel := context.WithTimeout(dht.Context(), time.Second*5) _, err := dht.Ping(ctx, p) if err != nil { log.Debugf("Ping error: %s", err) } + cancel() } case <-dht.Closing(): return From 25a2eed27e4afeeb5f8d1fc36804d2fcbad04c8d Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sat, 7 Mar 2015 09:31:46 -0800 Subject: [PATCH 0796/3817] added cancel func calls previously ignored This commit was moved from ipfs/go-merkledag@d54bc701afea2b482ca3dbd4f13613ab771ccfd3 --- ipld/merkledag/merkledag.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 6e833056d..923a3d715 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -88,7 +88,8 @@ func (n *dagService) Get(k u.Key) (*Node, error) { return nil, fmt.Errorf("dagService is nil") } - ctx, _ := context.WithTimeout(context.TODO(), time.Minute) + ctx, cancel := context.WithTimeout(context.TODO(), time.Minute) + defer cancel() // we shouldn't use an arbitrary timeout here. // since Get doesnt take in a context yet, we give a large upper bound. // think of an http request. we want it to go on as long as the client requests it. From 9378c49b288db9cc622fc5ccccf373257b08299b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 7 Mar 2015 00:58:04 -0800 Subject: [PATCH 0797/3817] refactor dagmodifier to work with trickledag format This commit was moved from ipfs/go-unixfs@01c574f2f092f0ca4283c3896311457b3531d078 --- unixfs/io/dagmodifier.go | 202 -------------- unixfs/io/dagmodifier_test.go | 247 ------------------ unixfs/mod/dagmodifier.go | 450 ++++++++++++++++++++++++++++++++ unixfs/mod/dagmodifier_test.go | 464 +++++++++++++++++++++++++++++++++ 4 files changed, 914 insertions(+), 449 deletions(-) delete mode 100644 unixfs/io/dagmodifier.go delete mode 100644 unixfs/io/dagmodifier_test.go create mode 100644 unixfs/mod/dagmodifier.go create mode 100644 unixfs/mod/dagmodifier_test.go diff --git a/unixfs/io/dagmodifier.go b/unixfs/io/dagmodifier.go deleted file mode 100644 index e155d8b38..000000000 --- a/unixfs/io/dagmodifier.go +++ /dev/null @@ -1,202 +0,0 @@ -package io - -import ( - "bytes" - "errors" - - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - - chunk "github.com/jbenet/go-ipfs/importer/chunk" - mdag "github.com/jbenet/go-ipfs/merkledag" - ft "github.com/jbenet/go-ipfs/unixfs" - ftpb "github.com/jbenet/go-ipfs/unixfs/pb" - u "github.com/jbenet/go-ipfs/util" -) - -var log = u.Logger("dagio") - -// DagModifier is the only struct licensed and able to correctly -// perform surgery on a DAG 'file' -// Dear god, please rename this to something more pleasant -type DagModifier struct { - dagserv mdag.DAGService - curNode *mdag.Node - - pbdata *ftpb.Data - splitter chunk.BlockSplitter -} - -func NewDagModifier(from *mdag.Node, serv mdag.DAGService, spl chunk.BlockSplitter) (*DagModifier, error) { - pbd, err := ft.FromBytes(from.Data) - if err != nil { - return nil, err - } - - return &DagModifier{ - curNode: from.Copy(), - dagserv: serv, - pbdata: pbd, - splitter: spl, - }, nil -} - -// WriteAt will modify a dag file in place -// NOTE: it currently assumes only a single level of indirection -func (dm *DagModifier) WriteAt(b []byte, offset uint64) (int, error) { - - // Check bounds - if dm.pbdata.GetFilesize() < offset { - return 0, errors.New("Attempted to perform write starting past end of file") - } - - // First need to find where we are writing at - end := uint64(len(b)) + offset - - // This shouldnt be necessary if we do subblocks sizes properly - newsize := dm.pbdata.GetFilesize() - if end > dm.pbdata.GetFilesize() { - newsize = end - } - zeroblocklen := uint64(len(dm.pbdata.Data)) - origlen := len(b) - - if end <= zeroblocklen { - log.Debug("Writing into zero block") - // Replacing zeroeth data block (embedded in the root node) - //TODO: check chunking here - copy(dm.pbdata.Data[offset:], b) - return len(b), nil - } - - // Find where write should start - var traversed uint64 - startsubblk := len(dm.pbdata.Blocksizes) - if offset < zeroblocklen { - dm.pbdata.Data = dm.pbdata.Data[:offset] - startsubblk = 0 - } else { - traversed = uint64(zeroblocklen) - for i, size := range dm.pbdata.Blocksizes { - if uint64(offset) < traversed+size { - log.Debugf("Starting mod at block %d. [%d < %d + %d]", i, offset, traversed, size) - // Here is where we start - startsubblk = i - lnk := dm.curNode.Links[i] - node, err := dm.dagserv.Get(u.Key(lnk.Hash)) - if err != nil { - return 0, err - } - data, err := ft.UnwrapData(node.Data) - if err != nil { - return 0, err - } - - // We have to rewrite the data before our write in this block. - b = append(data[:offset-traversed], b...) - break - } - traversed += size - } - if startsubblk == len(dm.pbdata.Blocksizes) { - // TODO: Im not sure if theres any case that isnt being handled here. - // leaving this note here as a future reference in case something breaks - } - } - - // Find blocks that need to be overwritten - var changed []int - mid := -1 - var midoff uint64 - for i, size := range dm.pbdata.Blocksizes[startsubblk:] { - if end > traversed { - changed = append(changed, i+startsubblk) - } else { - break - } - traversed += size - if end < traversed { - mid = i + startsubblk - midoff = end - (traversed - size) - break - } - } - - // If our write starts in the middle of a block... - var midlnk *mdag.Link - if mid >= 0 { - midlnk = dm.curNode.Links[mid] - midnode, err := dm.dagserv.Get(u.Key(midlnk.Hash)) - if err != nil { - return 0, err - } - - // NOTE: this may have to be changed later when we have multiple - // layers of indirection - data, err := ft.UnwrapData(midnode.Data) - if err != nil { - return 0, err - } - b = append(b, data[midoff:]...) - } - - // Generate new sub-blocks, and sizes - subblocks := splitBytes(b, dm.splitter) - var links []*mdag.Link - var sizes []uint64 - for _, sb := range subblocks { - n := &mdag.Node{Data: ft.WrapData(sb)} - _, err := dm.dagserv.Add(n) - if err != nil { - log.Warningf("Failed adding node to DAG service: %s", err) - return 0, err - } - lnk, err := mdag.MakeLink(n) - if err != nil { - return 0, err - } - links = append(links, lnk) - sizes = append(sizes, uint64(len(sb))) - } - - // This is disgusting (and can be rewritten if performance demands) - if len(changed) > 0 { - sechalflink := append(links, dm.curNode.Links[changed[len(changed)-1]+1:]...) - dm.curNode.Links = append(dm.curNode.Links[:changed[0]], sechalflink...) - sechalfblks := append(sizes, dm.pbdata.Blocksizes[changed[len(changed)-1]+1:]...) - dm.pbdata.Blocksizes = append(dm.pbdata.Blocksizes[:changed[0]], sechalfblks...) - } else { - dm.curNode.Links = append(dm.curNode.Links, links...) - dm.pbdata.Blocksizes = append(dm.pbdata.Blocksizes, sizes...) - } - dm.pbdata.Filesize = proto.Uint64(newsize) - - return origlen, nil -} - -func (dm *DagModifier) Size() uint64 { - if dm == nil { - return 0 - } - return dm.pbdata.GetFilesize() -} - -// splitBytes uses a splitterFunc to turn a large array of bytes -// into many smaller arrays of bytes -func splitBytes(b []byte, spl chunk.BlockSplitter) [][]byte { - out := spl.Split(bytes.NewReader(b)) - var arr [][]byte - for blk := range out { - arr = append(arr, blk) - } - return arr -} - -// GetNode gets the modified DAG Node -func (dm *DagModifier) GetNode() (*mdag.Node, error) { - b, err := proto.Marshal(dm.pbdata) - if err != nil { - return nil, err - } - dm.curNode.Data = b - return dm.curNode.Copy(), nil -} diff --git a/unixfs/io/dagmodifier_test.go b/unixfs/io/dagmodifier_test.go deleted file mode 100644 index ca9c42004..000000000 --- a/unixfs/io/dagmodifier_test.go +++ /dev/null @@ -1,247 +0,0 @@ -package io - -import ( - "fmt" - "io" - "io/ioutil" - "testing" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - "github.com/jbenet/go-ipfs/blocks/blockstore" - bs "github.com/jbenet/go-ipfs/blockservice" - "github.com/jbenet/go-ipfs/exchange/offline" - imp "github.com/jbenet/go-ipfs/importer" - "github.com/jbenet/go-ipfs/importer/chunk" - mdag "github.com/jbenet/go-ipfs/merkledag" - ft "github.com/jbenet/go-ipfs/unixfs" - u "github.com/jbenet/go-ipfs/util" - - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" -) - -func getMockDagServ(t *testing.T) mdag.DAGService { - dstore := ds.NewMapDatastore() - tsds := sync.MutexWrap(dstore) - bstore := blockstore.NewBlockstore(tsds) - bserv, err := bs.New(bstore, offline.Exchange(bstore)) - if err != nil { - t.Fatal(err) - } - return mdag.NewDAGService(bserv) -} - -func getNode(t *testing.T, dserv mdag.DAGService, size int64) ([]byte, *mdag.Node) { - in := io.LimitReader(u.NewTimeSeededRand(), size) - node, err := imp.BuildDagFromReader(in, dserv, nil, &chunk.SizeSplitter{500}) - if err != nil { - t.Fatal(err) - } - - dr, err := NewDagReader(context.Background(), node, dserv) - if err != nil { - t.Fatal(err) - } - - b, err := ioutil.ReadAll(dr) - if err != nil { - t.Fatal(err) - } - - return b, node -} - -func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) []byte { - newdata := make([]byte, size) - r := u.NewTimeSeededRand() - r.Read(newdata) - - if size+beg > uint64(len(orig)) { - orig = append(orig, make([]byte, (size+beg)-uint64(len(orig)))...) - } - copy(orig[beg:], newdata) - - nmod, err := dm.WriteAt(newdata, uint64(beg)) - if err != nil { - t.Fatal(err) - } - - if nmod != int(size) { - t.Fatalf("Mod length not correct! %d != %d", nmod, size) - } - - nd, err := dm.GetNode() - if err != nil { - t.Fatal(err) - } - - rd, err := NewDagReader(context.Background(), nd, dm.dagserv) - if err != nil { - t.Fatal(err) - } - - after, err := ioutil.ReadAll(rd) - if err != nil { - t.Fatal(err) - } - - err = arrComp(after, orig) - if err != nil { - t.Fatal(err) - } - return orig -} - -func TestDagModifierBasic(t *testing.T) { - t.Skip("DAGModifier needs to be fixed to work with indirect blocks.") - if err := u.SetLogLevel("blockservice", "critical"); err != nil { - t.Fatalf("testlog prepare failed: %s", err) - } - if err := u.SetLogLevel("merkledag", "critical"); err != nil { - t.Fatalf("testlog prepare failed: %s", err) - } - dserv := getMockDagServ(t) - b, n := getNode(t, dserv, 50000) - - dagmod, err := NewDagModifier(n, dserv, &chunk.SizeSplitter{Size: 512}) - if err != nil { - t.Fatal(err) - } - - // Within zero block - beg := uint64(15) - length := uint64(60) - - t.Log("Testing mod within zero block") - b = testModWrite(t, beg, length, b, dagmod) - - // Within bounds of existing file - beg = 1000 - length = 4000 - t.Log("Testing mod within bounds of existing file.") - b = testModWrite(t, beg, length, b, dagmod) - - // Extend bounds - beg = 49500 - length = 4000 - - t.Log("Testing mod that extends file.") - b = testModWrite(t, beg, length, b, dagmod) - - // "Append" - beg = uint64(len(b)) - length = 3000 - b = testModWrite(t, beg, length, b, dagmod) - - // Verify reported length - node, err := dagmod.GetNode() - if err != nil { - t.Fatal(err) - } - - size, err := ft.DataSize(node.Data) - if err != nil { - t.Fatal(err) - } - - expected := uint64(50000 + 3500 + 3000) - if size != expected { - t.Fatalf("Final reported size is incorrect [%d != %d]", size, expected) - } -} - -func TestMultiWrite(t *testing.T) { - t.Skip("DAGModifier needs to be fixed to work with indirect blocks.") - dserv := getMockDagServ(t) - _, n := getNode(t, dserv, 0) - - dagmod, err := NewDagModifier(n, dserv, &chunk.SizeSplitter{Size: 512}) - if err != nil { - t.Fatal(err) - } - - data := make([]byte, 4000) - u.NewTimeSeededRand().Read(data) - - for i := 0; i < len(data); i++ { - n, err := dagmod.WriteAt(data[i:i+1], uint64(i)) - if err != nil { - t.Fatal(err) - } - if n != 1 { - t.Fatal("Somehow wrote the wrong number of bytes! (n != 1)") - } - } - nd, err := dagmod.GetNode() - if err != nil { - t.Fatal(err) - } - - read, err := NewDagReader(context.Background(), nd, dserv) - if err != nil { - t.Fatal(err) - } - rbuf, err := ioutil.ReadAll(read) - if err != nil { - t.Fatal(err) - } - - err = arrComp(rbuf, data) - if err != nil { - t.Fatal(err) - } -} - -func TestMultiWriteCoal(t *testing.T) { - t.Skip("Skipping test until DagModifier is fixed") - dserv := getMockDagServ(t) - _, n := getNode(t, dserv, 0) - - dagmod, err := NewDagModifier(n, dserv, &chunk.SizeSplitter{Size: 512}) - if err != nil { - t.Fatal(err) - } - - data := make([]byte, 4000) - u.NewTimeSeededRand().Read(data) - - for i := 0; i < len(data); i++ { - n, err := dagmod.WriteAt(data[:i+1], 0) - if err != nil { - t.Fatal(err) - } - if n != i+1 { - t.Fatal("Somehow wrote the wrong number of bytes! (n != 1)") - } - } - nd, err := dagmod.GetNode() - if err != nil { - t.Fatal(err) - } - - read, err := NewDagReader(context.Background(), nd, dserv) - if err != nil { - t.Fatal(err) - } - rbuf, err := ioutil.ReadAll(read) - if err != nil { - t.Fatal(err) - } - - err = arrComp(rbuf, data) - if err != nil { - t.Fatal(err) - } -} - -func arrComp(a, b []byte) error { - if len(a) != len(b) { - return fmt.Errorf("Arrays differ in length. %d != %d", len(a), len(b)) - } - for i, v := range a { - if v != b[i] { - return fmt.Errorf("Arrays differ at index: %d", i) - } - } - return nil -} diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go new file mode 100644 index 000000000..4e5b31088 --- /dev/null +++ b/unixfs/mod/dagmodifier.go @@ -0,0 +1,450 @@ +package mod + +import ( + "bytes" + "errors" + "io" + "os" + + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + + chunk "github.com/jbenet/go-ipfs/importer/chunk" + help "github.com/jbenet/go-ipfs/importer/helpers" + trickle "github.com/jbenet/go-ipfs/importer/trickle" + mdag "github.com/jbenet/go-ipfs/merkledag" + pin "github.com/jbenet/go-ipfs/pin" + ft "github.com/jbenet/go-ipfs/unixfs" + uio "github.com/jbenet/go-ipfs/unixfs/io" + ftpb "github.com/jbenet/go-ipfs/unixfs/pb" + u "github.com/jbenet/go-ipfs/util" +) + +// 2MB +var writebufferSize = 1 << 21 + +var log = u.Logger("dagio") + +// DagModifier is the only struct licensed and able to correctly +// perform surgery on a DAG 'file' +// Dear god, please rename this to something more pleasant +type DagModifier struct { + dagserv mdag.DAGService + curNode *mdag.Node + mp pin.ManualPinner + + splitter chunk.BlockSplitter + ctx context.Context + readCancel func() + + writeStart uint64 + curWrOff uint64 + wrBuf *bytes.Buffer + + read *uio.DagReader +} + +func NewDagModifier(ctx context.Context, from *mdag.Node, serv mdag.DAGService, mp pin.ManualPinner, spl chunk.BlockSplitter) (*DagModifier, error) { + return &DagModifier{ + curNode: from.Copy(), + dagserv: serv, + splitter: spl, + ctx: ctx, + mp: mp, + }, nil +} + +// WriteAt will modify a dag file in place +// NOTE: it currently assumes only a single level of indirection +func (dm *DagModifier) WriteAt(b []byte, offset int64) (int, error) { + // TODO: this is currently VERY inneficient + if uint64(offset) != dm.curWrOff { + size, err := dm.Size() + if err != nil { + return 0, err + } + if offset > size { + err := dm.expandSparse(offset - size) + if err != nil { + return 0, err + } + } + + err = dm.Flush() + if err != nil { + return 0, err + } + dm.writeStart = uint64(offset) + } + + return dm.Write(b) +} + +// A reader that just returns zeros +type zeroReader struct{} + +func (zr zeroReader) Read(b []byte) (int, error) { + for i, _ := range b { + b[i] = 0 + } + return len(b), nil +} + +func (dm *DagModifier) expandSparse(size int64) error { + spl := chunk.SizeSplitter{4096} + r := io.LimitReader(zeroReader{}, size) + blks := spl.Split(r) + nnode, err := dm.appendData(dm.curNode, blks) + if err != nil { + return err + } + _, err = dm.dagserv.Add(nnode) + if err != nil { + return err + } + dm.curNode = nnode + return nil +} + +func (dm *DagModifier) Write(b []byte) (int, error) { + if dm.read != nil { + dm.read = nil + } + if dm.wrBuf == nil { + dm.wrBuf = new(bytes.Buffer) + } + n, err := dm.wrBuf.Write(b) + if err != nil { + return n, err + } + dm.curWrOff += uint64(n) + if dm.wrBuf.Len() > writebufferSize { + err := dm.Flush() + if err != nil { + return n, err + } + } + return n, nil +} + +func (dm *DagModifier) Size() (int64, error) { + // TODO: compute size without flushing, should be easy + err := dm.Flush() + if err != nil { + return 0, err + } + + pbn, err := ft.FromBytes(dm.curNode.Data) + if err != nil { + return 0, err + } + + return int64(pbn.GetFilesize()), nil +} + +func (dm *DagModifier) Flush() error { + if dm.wrBuf == nil { + return nil + } + + // If we have an active reader, kill it + if dm.read != nil { + dm.read = nil + dm.readCancel() + } + + buflen := dm.wrBuf.Len() + + k, _, done, err := dm.modifyDag(dm.curNode, dm.writeStart, dm.wrBuf) + if err != nil { + return err + } + + nd, err := dm.dagserv.Get(k) + if err != nil { + return err + } + + dm.curNode = nd + + if !done { + blks := dm.splitter.Split(dm.wrBuf) + nd, err = dm.appendData(dm.curNode, blks) + if err != nil { + return err + } + + _, err := dm.dagserv.Add(nd) + if err != nil { + return err + } + + dm.curNode = nd + } + + dm.writeStart += uint64(buflen) + + dm.wrBuf = nil + return nil +} + +func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) (u.Key, int, bool, error) { + f, err := ft.FromBytes(node.Data) + if err != nil { + return "", 0, false, err + } + + if len(node.Links) == 0 && (f.GetType() == ftpb.Data_Raw || f.GetType() == ftpb.Data_File) { + n, err := data.Read(f.Data[offset:]) + if err != nil && err != io.EOF { + return "", 0, false, err + } + + // Update newly written node.. + b, err := proto.Marshal(f) + if err != nil { + return "", 0, false, err + } + + nd := &mdag.Node{Data: b} + k, err := dm.dagserv.Add(nd) + if err != nil { + return "", 0, false, err + } + + // Hey look! we're done! + var done bool + if n < len(f.Data) { + done = true + } + + return k, n, done, nil + } + + var cur uint64 + var done bool + var totread int + for i, bs := range f.GetBlocksizes() { + if cur+bs > offset { + child, err := node.Links[i].GetNode(dm.dagserv) + if err != nil { + return "", 0, false, err + } + k, nread, sdone, err := dm.modifyDag(child, offset-cur, data) + if err != nil { + return "", 0, false, err + } + totread += nread + + offset += bs + node.Links[i].Hash = mh.Multihash(k) + + if sdone { + done = true + break + } + } + cur += bs + } + + k, err := dm.dagserv.Add(node) + return k, totread, done, err +} + +func (dm *DagModifier) appendData(node *mdag.Node, blks <-chan []byte) (*mdag.Node, error) { + dbp := &help.DagBuilderParams{ + Dagserv: dm.dagserv, + Maxlinks: help.DefaultLinksPerBlock, + Pinner: dm.mp, + } + + return trickle.TrickleAppend(node, dbp.New(blks)) +} + +func (dm *DagModifier) Read(b []byte) (int, error) { + err := dm.Flush() + if err != nil { + return 0, err + } + + if dm.read == nil { + dr, err := uio.NewDagReader(dm.ctx, dm.curNode, dm.dagserv) + if err != nil { + return 0, err + } + + i, err := dr.Seek(int64(dm.curWrOff), os.SEEK_SET) + if err != nil { + return 0, err + } + + if i != int64(dm.curWrOff) { + return 0, errors.New("failed to seek properly") + } + + dm.read = dr + } + + n, err := dm.read.Read(b) + dm.curWrOff += uint64(n) + return n, err +} + +// splitBytes uses a splitterFunc to turn a large array of bytes +// into many smaller arrays of bytes +func (dm *DagModifier) splitBytes(in io.Reader) ([]u.Key, error) { + var out []u.Key + blks := dm.splitter.Split(in) + for blk := range blks { + nd := help.NewUnixfsNode() + nd.SetData(blk) + dagnd, err := nd.GetDagNode() + if err != nil { + return nil, err + } + + k, err := dm.dagserv.Add(dagnd) + if err != nil { + return nil, err + } + out = append(out, k) + } + return out, nil +} + +// GetNode gets the modified DAG Node +func (dm *DagModifier) GetNode() (*mdag.Node, error) { + err := dm.Flush() + if err != nil { + return nil, err + } + return dm.curNode.Copy(), nil +} + +func (dm *DagModifier) HasChanges() bool { + return dm.wrBuf != nil +} + +func (dm *DagModifier) Seek(offset int64, whence int) (int64, error) { + err := dm.Flush() + if err != nil { + return 0, err + } + + switch whence { + case os.SEEK_CUR: + dm.curWrOff += uint64(offset) + dm.writeStart = dm.curWrOff + case os.SEEK_SET: + dm.curWrOff = uint64(offset) + dm.writeStart = uint64(offset) + case os.SEEK_END: + return 0, errors.New("SEEK_END currently not implemented") + default: + return 0, errors.New("unrecognized whence") + } + + if dm.read != nil { + _, err = dm.read.Seek(offset, whence) + if err != nil { + return 0, err + } + } + + return int64(dm.curWrOff), nil +} + +func (dm *DagModifier) Truncate(size int64) error { + err := dm.Flush() + if err != nil { + return err + } + + realSize, err := dm.Size() + if err != nil { + return err + } + + if size > int64(realSize) { + return errors.New("Cannot extend file through truncate") + } + + nnode, err := dagTruncate(dm.curNode, uint64(size), dm.dagserv) + if err != nil { + return err + } + + _, err = dm.dagserv.Add(nnode) + if err != nil { + return err + } + + dm.curNode = nnode + return nil +} + +func dagTruncate(nd *mdag.Node, size uint64, ds mdag.DAGService) (*mdag.Node, error) { + if len(nd.Links) == 0 { + // TODO: this can likely be done without marshaling and remarshaling + pbn, err := ft.FromBytes(nd.Data) + if err != nil { + return nil, err + } + + nd.Data = ft.WrapData(pbn.Data[:size]) + return nd, nil + } + + var cur uint64 + end := 0 + var modified *mdag.Node + ndata := new(ft.FSNode) + for i, lnk := range nd.Links { + child, err := lnk.GetNode(ds) + if err != nil { + return nil, err + } + + childsize, err := ft.DataSize(child.Data) + if err != nil { + return nil, err + } + + if size < cur+childsize { + nchild, err := dagTruncate(child, size-cur, ds) + if err != nil { + return nil, err + } + + // TODO: sanity check size of truncated block + ndata.AddBlockSize(size - cur) + + modified = nchild + end = i + break + } + cur += childsize + ndata.AddBlockSize(childsize) + } + + _, err := ds.Add(modified) + if err != nil { + return nil, err + } + + nd.Links = nd.Links[:end] + err = nd.AddNodeLinkClean("", modified) + if err != nil { + return nil, err + } + + d, err := ndata.GetBytes() + if err != nil { + return nil, err + } + + nd.Data = d + + return nd, nil +} diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go new file mode 100644 index 000000000..ac4e6f507 --- /dev/null +++ b/unixfs/mod/dagmodifier_test.go @@ -0,0 +1,464 @@ +package mod + +import ( + "fmt" + "io" + "io/ioutil" + "testing" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/jbenet/go-ipfs/blocks/blockstore" + bs "github.com/jbenet/go-ipfs/blockservice" + "github.com/jbenet/go-ipfs/exchange/offline" + imp "github.com/jbenet/go-ipfs/importer" + "github.com/jbenet/go-ipfs/importer/chunk" + h "github.com/jbenet/go-ipfs/importer/helpers" + trickle "github.com/jbenet/go-ipfs/importer/trickle" + mdag "github.com/jbenet/go-ipfs/merkledag" + ft "github.com/jbenet/go-ipfs/unixfs" + uio "github.com/jbenet/go-ipfs/unixfs/io" + u "github.com/jbenet/go-ipfs/util" + + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" +) + +func getMockDagServ(t *testing.T) mdag.DAGService { + dstore := ds.NewMapDatastore() + tsds := sync.MutexWrap(dstore) + bstore := blockstore.NewBlockstore(tsds) + bserv, err := bs.New(bstore, offline.Exchange(bstore)) + if err != nil { + t.Fatal(err) + } + return mdag.NewDAGService(bserv) +} + +func getNode(t *testing.T, dserv mdag.DAGService, size int64) ([]byte, *mdag.Node) { + in := io.LimitReader(u.NewTimeSeededRand(), size) + node, err := imp.BuildTrickleDagFromReader(in, dserv, nil, &chunk.SizeSplitter{500}) + if err != nil { + t.Fatal(err) + } + + dr, err := uio.NewDagReader(context.Background(), node, dserv) + if err != nil { + t.Fatal(err) + } + + b, err := ioutil.ReadAll(dr) + if err != nil { + t.Fatal(err) + } + + return b, node +} + +func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) []byte { + newdata := make([]byte, size) + r := u.NewTimeSeededRand() + r.Read(newdata) + + if size+beg > uint64(len(orig)) { + orig = append(orig, make([]byte, (size+beg)-uint64(len(orig)))...) + } + copy(orig[beg:], newdata) + + nmod, err := dm.WriteAt(newdata, int64(beg)) + if err != nil { + t.Fatal(err) + } + + if nmod != int(size) { + t.Fatalf("Mod length not correct! %d != %d", nmod, size) + } + + nd, err := dm.GetNode() + if err != nil { + t.Fatal(err) + } + + err = trickle.VerifyTrickleDagStructure(nd, dm.dagserv, h.DefaultLinksPerBlock, 4) + if err != nil { + t.Fatal(err) + } + + rd, err := uio.NewDagReader(context.Background(), nd, dm.dagserv) + if err != nil { + t.Fatal(err) + } + + after, err := ioutil.ReadAll(rd) + if err != nil { + t.Fatal(err) + } + + err = arrComp(after, orig) + if err != nil { + t.Fatal(err) + } + return orig +} + +func TestDagModifierBasic(t *testing.T) { + dserv := getMockDagServ(t) + b, n := getNode(t, dserv, 50000) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + if err != nil { + t.Fatal(err) + } + + // Within zero block + beg := uint64(15) + length := uint64(60) + + t.Log("Testing mod within zero block") + b = testModWrite(t, beg, length, b, dagmod) + + // Within bounds of existing file + beg = 1000 + length = 4000 + t.Log("Testing mod within bounds of existing multiblock file.") + b = testModWrite(t, beg, length, b, dagmod) + + // Extend bounds + beg = 49500 + length = 4000 + + t.Log("Testing mod that extends file.") + b = testModWrite(t, beg, length, b, dagmod) + + // "Append" + beg = uint64(len(b)) + length = 3000 + t.Log("Testing pure append") + b = testModWrite(t, beg, length, b, dagmod) + + // Verify reported length + node, err := dagmod.GetNode() + if err != nil { + t.Fatal(err) + } + + size, err := ft.DataSize(node.Data) + if err != nil { + t.Fatal(err) + } + + expected := uint64(50000 + 3500 + 3000) + if size != expected { + t.Fatalf("Final reported size is incorrect [%d != %d]", size, expected) + } +} + +func TestMultiWrite(t *testing.T) { + dserv := getMockDagServ(t) + _, n := getNode(t, dserv, 0) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + if err != nil { + t.Fatal(err) + } + + data := make([]byte, 4000) + u.NewTimeSeededRand().Read(data) + + for i := 0; i < len(data); i++ { + n, err := dagmod.WriteAt(data[i:i+1], int64(i)) + if err != nil { + t.Fatal(err) + } + if n != 1 { + t.Fatal("Somehow wrote the wrong number of bytes! (n != 1)") + } + } + nd, err := dagmod.GetNode() + if err != nil { + t.Fatal(err) + } + + read, err := uio.NewDagReader(context.Background(), nd, dserv) + if err != nil { + t.Fatal(err) + } + rbuf, err := ioutil.ReadAll(read) + if err != nil { + t.Fatal(err) + } + + err = arrComp(rbuf, data) + if err != nil { + t.Fatal(err) + } +} + +func TestMultiWriteAndFlush(t *testing.T) { + dserv := getMockDagServ(t) + _, n := getNode(t, dserv, 0) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + if err != nil { + t.Fatal(err) + } + + data := make([]byte, 20) + u.NewTimeSeededRand().Read(data) + + for i := 0; i < len(data); i++ { + n, err := dagmod.WriteAt(data[i:i+1], int64(i)) + if err != nil { + t.Fatal(err) + } + if n != 1 { + t.Fatal("Somehow wrote the wrong number of bytes! (n != 1)") + } + err = dagmod.Flush() + if err != nil { + t.Fatal(err) + } + } + nd, err := dagmod.GetNode() + if err != nil { + t.Fatal(err) + } + + read, err := uio.NewDagReader(context.Background(), nd, dserv) + if err != nil { + t.Fatal(err) + } + rbuf, err := ioutil.ReadAll(read) + if err != nil { + t.Fatal(err) + } + + err = arrComp(rbuf, data) + if err != nil { + t.Fatal(err) + } +} + +func TestWriteNewFile(t *testing.T) { + dserv := getMockDagServ(t) + _, n := getNode(t, dserv, 0) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + if err != nil { + t.Fatal(err) + } + + towrite := make([]byte, 2000) + u.NewTimeSeededRand().Read(towrite) + + nw, err := dagmod.Write(towrite) + if err != nil { + t.Fatal(err) + } + if nw != len(towrite) { + t.Fatal("Wrote wrong amount") + } + + nd, err := dagmod.GetNode() + if err != nil { + t.Fatal(err) + } + + read, err := uio.NewDagReader(ctx, nd, dserv) + if err != nil { + t.Fatal(err) + } + + data, err := ioutil.ReadAll(read) + if err != nil { + t.Fatal(err) + } + + if err := arrComp(data, towrite); err != nil { + t.Fatal(err) + } +} + +func TestMultiWriteCoal(t *testing.T) { + dserv := getMockDagServ(t) + _, n := getNode(t, dserv, 0) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + if err != nil { + t.Fatal(err) + } + + data := make([]byte, 1000) + u.NewTimeSeededRand().Read(data) + + for i := 0; i < len(data); i++ { + n, err := dagmod.WriteAt(data[:i+1], 0) + if err != nil { + fmt.Println("FAIL AT ", i) + t.Fatal(err) + } + if n != i+1 { + t.Fatal("Somehow wrote the wrong number of bytes! (n != 1)") + } + + // TEMP + nn, err := dagmod.GetNode() + if err != nil { + t.Fatal(err) + } + + r, err := uio.NewDagReader(ctx, nn, dserv) + if err != nil { + t.Fatal(err) + } + + out, err := ioutil.ReadAll(r) + if err != nil { + t.Fatal(err) + } + + if err := arrComp(out, data[:i+1]); err != nil { + fmt.Println("A ", len(out)) + fmt.Println(out) + fmt.Println(data[:i+1]) + t.Fatal(err) + } + // + } + nd, err := dagmod.GetNode() + if err != nil { + t.Fatal(err) + } + + read, err := uio.NewDagReader(context.Background(), nd, dserv) + if err != nil { + t.Fatal(err) + } + rbuf, err := ioutil.ReadAll(read) + if err != nil { + t.Fatal(err) + } + + err = arrComp(rbuf, data) + if err != nil { + t.Fatal(err) + } +} + +func TestLargeWriteChunks(t *testing.T) { + dserv := getMockDagServ(t) + _, n := getNode(t, dserv, 0) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + if err != nil { + t.Fatal(err) + } + + wrsize := 1000 + datasize := 10000000 + data := make([]byte, datasize) + + u.NewTimeSeededRand().Read(data) + + for i := 0; i < datasize/wrsize; i++ { + n, err := dagmod.WriteAt(data[i*wrsize:(i+1)*wrsize], int64(i*wrsize)) + if err != nil { + t.Fatal(err) + } + if n != wrsize { + t.Fatal("failed to write buffer") + } + } + + out, err := ioutil.ReadAll(dagmod) + if err != nil { + t.Fatal(err) + } + + if err = arrComp(out, data); err != nil { + t.Fatal(err) + } + +} + +func TestDagTruncate(t *testing.T) { + dserv := getMockDagServ(t) + b, n := getNode(t, dserv, 50000) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + if err != nil { + t.Fatal(err) + } + + err = dagmod.Truncate(12345) + if err != nil { + t.Fatal(err) + } + + out, err := ioutil.ReadAll(dagmod) + if err != nil { + t.Fatal(err) + } + + if err = arrComp(out, b[:12345]); err != nil { + t.Fatal(err) + } +} + +func arrComp(a, b []byte) error { + if len(a) != len(b) { + return fmt.Errorf("Arrays differ in length. %d != %d", len(a), len(b)) + } + for i, v := range a { + if v != b[i] { + return fmt.Errorf("Arrays differ at index: %d", i) + } + } + return nil +} + +func printDag(nd *mdag.Node, ds mdag.DAGService, indent int) { + pbd, err := ft.FromBytes(nd.Data) + if err != nil { + panic(err) + } + + for i := 0; i < indent; i++ { + fmt.Print(" ") + } + fmt.Printf("{size = %d, type = %s, children = %d", pbd.GetFilesize(), pbd.GetType().String(), len(pbd.GetBlocksizes())) + if len(nd.Links) > 0 { + fmt.Println() + } + for _, lnk := range nd.Links { + child, err := lnk.GetNode(ds) + if err != nil { + panic(err) + } + printDag(child, ds, indent+1) + } + if len(nd.Links) > 0 { + for i := 0; i < indent; i++ { + fmt.Print(" ") + } + } + fmt.Println("}") +} From fd4600bd16259ec65ea37ed28bcdbdadd2b1d21c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 7 Mar 2015 19:08:39 -0800 Subject: [PATCH 0798/3817] better error message from dagreader with bad protofbuf This commit was moved from ipfs/go-unixfs@e6119c6495268fbb4c566d226750b819120cce0b --- unixfs/io/dagreader.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 0af49e9ee..64dfff127 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -3,6 +3,7 @@ package io import ( "bytes" "errors" + "fmt" "io" "os" @@ -113,7 +114,7 @@ func (dr *DagReader) precalcNextBuf() error { pb := new(ftpb.Data) err = proto.Unmarshal(nxt.Data, pb) if err != nil { - return err + return fmt.Errorf("incorrectly formatted protobuf: %s", err) } switch pb.GetType() { From 43499b008ed89c6f8054f49b2cc5addfa81f4b4a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 9 Mar 2015 14:53:21 -0700 Subject: [PATCH 0799/3817] Code cleanup This commit was moved from ipfs/go-unixfs@d7f1750f7a95d82bb3974458f5075e35c0ddcd81 --- unixfs/mod/dagmodifier.go | 48 +++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 4e5b31088..1c234945e 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -17,7 +17,6 @@ import ( pin "github.com/jbenet/go-ipfs/pin" ft "github.com/jbenet/go-ipfs/unixfs" uio "github.com/jbenet/go-ipfs/unixfs/io" - ftpb "github.com/jbenet/go-ipfs/unixfs/pb" u "github.com/jbenet/go-ipfs/util" ) @@ -56,9 +55,10 @@ func NewDagModifier(ctx context.Context, from *mdag.Node, serv mdag.DAGService, } // WriteAt will modify a dag file in place -// NOTE: it currently assumes only a single level of indirection func (dm *DagModifier) WriteAt(b []byte, offset int64) (int, error) { // TODO: this is currently VERY inneficient + // each write that happens at an offset other than the current one causes a + // flush to disk, and dag rewrite if uint64(offset) != dm.curWrOff { size, err := dm.Size() if err != nil { @@ -91,6 +91,8 @@ func (zr zeroReader) Read(b []byte) (int, error) { return len(b), nil } +// expandSparse grows the file with zero blocks of 4096 +// A small blocksize is chosen to aid in deduplication func (dm *DagModifier) expandSparse(size int64) error { spl := chunk.SizeSplitter{4096} r := io.LimitReader(zeroReader{}, size) @@ -107,6 +109,7 @@ func (dm *DagModifier) expandSparse(size int64) error { return nil } +// Write continues writing to the dag at the current offset func (dm *DagModifier) Write(b []byte) (int, error) { if dm.read != nil { dm.read = nil @@ -114,6 +117,7 @@ func (dm *DagModifier) Write(b []byte) (int, error) { if dm.wrBuf == nil { dm.wrBuf = new(bytes.Buffer) } + n, err := dm.wrBuf.Write(b) if err != nil { return n, err @@ -143,7 +147,9 @@ func (dm *DagModifier) Size() (int64, error) { return int64(pbn.GetFilesize()), nil } +// Flush writes changes to this dag to disk func (dm *DagModifier) Flush() error { + // No buffer? Nothing to do if dm.wrBuf == nil { return nil } @@ -154,9 +160,11 @@ func (dm *DagModifier) Flush() error { dm.readCancel() } + // Number of bytes we're going to write buflen := dm.wrBuf.Len() - k, _, done, err := dm.modifyDag(dm.curNode, dm.writeStart, dm.wrBuf) + // overwrite existing dag nodes + k, done, err := dm.modifyDag(dm.curNode, dm.writeStart, dm.wrBuf) if err != nil { return err } @@ -168,6 +176,7 @@ func (dm *DagModifier) Flush() error { dm.curNode = nd + // need to write past end of current dag if !done { blks := dm.splitter.Split(dm.wrBuf) nd, err = dm.appendData(dm.curNode, blks) @@ -189,28 +198,30 @@ func (dm *DagModifier) Flush() error { return nil } -func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) (u.Key, int, bool, error) { +// modifyDag writes the data in 'data' over the data in 'node' starting at 'offset' +func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) (u.Key, bool, error) { f, err := ft.FromBytes(node.Data) if err != nil { - return "", 0, false, err + return "", false, err } - if len(node.Links) == 0 && (f.GetType() == ftpb.Data_Raw || f.GetType() == ftpb.Data_File) { + // If we've reached a leaf node. + if len(node.Links) == 0 { n, err := data.Read(f.Data[offset:]) if err != nil && err != io.EOF { - return "", 0, false, err + return "", false, err } // Update newly written node.. b, err := proto.Marshal(f) if err != nil { - return "", 0, false, err + return "", false, err } nd := &mdag.Node{Data: b} k, err := dm.dagserv.Add(nd) if err != nil { - return "", 0, false, err + return "", false, err } // Hey look! we're done! @@ -219,23 +230,21 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) done = true } - return k, n, done, nil + return k, done, nil } var cur uint64 var done bool - var totread int for i, bs := range f.GetBlocksizes() { if cur+bs > offset { child, err := node.Links[i].GetNode(dm.dagserv) if err != nil { - return "", 0, false, err + return "", false, err } - k, nread, sdone, err := dm.modifyDag(child, offset-cur, data) + k, sdone, err := dm.modifyDag(child, offset-cur, data) if err != nil { - return "", 0, false, err + return "", false, err } - totread += nread offset += bs node.Links[i].Hash = mh.Multihash(k) @@ -249,9 +258,10 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) } k, err := dm.dagserv.Add(node) - return k, totread, done, err + return k, done, err } +// appendData appends the blocks from the given chan to the end of this dag func (dm *DagModifier) appendData(node *mdag.Node, blks <-chan []byte) (*mdag.Node, error) { dbp := &help.DagBuilderParams{ Dagserv: dm.dagserv, @@ -262,6 +272,7 @@ func (dm *DagModifier) appendData(node *mdag.Node, blks <-chan []byte) (*mdag.No return trickle.TrickleAppend(node, dbp.New(blks)) } +// Read data from this dag starting at the current offset func (dm *DagModifier) Read(b []byte) (int, error) { err := dm.Flush() if err != nil { @@ -322,6 +333,7 @@ func (dm *DagModifier) GetNode() (*mdag.Node, error) { return dm.curNode.Copy(), nil } +// HasChanges returned whether or not there are unflushed changes to this dag func (dm *DagModifier) HasChanges() bool { return dm.wrBuf != nil } @@ -366,8 +378,9 @@ func (dm *DagModifier) Truncate(size int64) error { return err } + // Truncate can also be used to expand the file if size > int64(realSize) { - return errors.New("Cannot extend file through truncate") + return dm.expandSparse(int64(size) - realSize) } nnode, err := dagTruncate(dm.curNode, uint64(size), dm.dagserv) @@ -384,6 +397,7 @@ func (dm *DagModifier) Truncate(size int64) error { return nil } +// dagTruncate truncates the given node to 'size' and returns the modified Node func dagTruncate(nd *mdag.Node, size uint64, ds mdag.DAGService) (*mdag.Node, error) { if len(nd.Links) == 0 { // TODO: this can likely be done without marshaling and remarshaling From 906905b6b8d35c33f3ccfce2d3e1892ba55b4cea Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 9 Mar 2015 16:37:28 -0700 Subject: [PATCH 0800/3817] add benchmark This commit was moved from ipfs/go-unixfs@00efa963cc355e1182be6c691c171bc607450984 --- unixfs/mod/dagmodifier_test.go | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index ac4e6f507..d384c5ccc 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -23,7 +23,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) -func getMockDagServ(t *testing.T) mdag.DAGService { +func getMockDagServ(t testing.TB) mdag.DAGService { dstore := ds.NewMapDatastore() tsds := sync.MutexWrap(dstore) bstore := blockstore.NewBlockstore(tsds) @@ -34,7 +34,7 @@ func getMockDagServ(t *testing.T) mdag.DAGService { return mdag.NewDAGService(bserv) } -func getNode(t *testing.T, dserv mdag.DAGService, size int64) ([]byte, *mdag.Node) { +func getNode(t testing.TB, dserv mdag.DAGService, size int64) ([]byte, *mdag.Node) { in := io.LimitReader(u.NewTimeSeededRand(), size) node, err := imp.BuildTrickleDagFromReader(in, dserv, nil, &chunk.SizeSplitter{500}) if err != nil { @@ -423,6 +423,35 @@ func TestDagTruncate(t *testing.T) { } } +func BenchmarkDagmodWrite(b *testing.B) { + b.StopTimer() + dserv := getMockDagServ(b) + _, n := getNode(b, dserv, 0) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + wrsize := 4096 + + dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + if err != nil { + b.Fatal(err) + } + + buf := make([]byte, b.N*wrsize) + u.NewTimeSeededRand().Read(buf) + b.StartTimer() + b.SetBytes(int64(wrsize)) + for i := 0; i < b.N; i++ { + n, err := dagmod.Write(buf[i*wrsize : (i+1)*wrsize]) + if err != nil { + b.Fatal(err) + } + if n != wrsize { + b.Fatal("Wrote bad size") + } + } +} + func arrComp(a, b []byte) error { if len(a) != len(b) { return fmt.Errorf("Arrays differ in length. %d != %d", len(a), len(b)) From 8075c534419ba4d5217245d143eaedc5aa703ad5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 9 Mar 2015 16:51:58 -0700 Subject: [PATCH 0801/3817] address comments from @cryptix in PR This commit was moved from ipfs/go-unixfs@f68d79f51ed01bf5777849ca34efb21ecfaa4089 --- unixfs/mod/dagmodifier.go | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 1c234945e..c9bb17d35 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -20,6 +20,10 @@ import ( u "github.com/jbenet/go-ipfs/util" ) +var ErrSeekFail = errors.New("failed to seek properly") +var ErrSeekEndNotImpl = errors.New("SEEK_END currently not implemented") +var ErrUnrecognizedWhence = errors.New("unrecognized whence") + // 2MB var writebufferSize = 1 << 21 @@ -199,6 +203,8 @@ func (dm *DagModifier) Flush() error { } // modifyDag writes the data in 'data' over the data in 'node' starting at 'offset' +// returns the new key of the passed in node and whether or not all the data in the reader +// has been consumed. func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) (u.Key, bool, error) { f, err := ft.FromBytes(node.Data) if err != nil { @@ -291,7 +297,7 @@ func (dm *DagModifier) Read(b []byte) (int, error) { } if i != int64(dm.curWrOff) { - return 0, errors.New("failed to seek properly") + return 0, ErrSeekFail } dm.read = dr @@ -302,28 +308,6 @@ func (dm *DagModifier) Read(b []byte) (int, error) { return n, err } -// splitBytes uses a splitterFunc to turn a large array of bytes -// into many smaller arrays of bytes -func (dm *DagModifier) splitBytes(in io.Reader) ([]u.Key, error) { - var out []u.Key - blks := dm.splitter.Split(in) - for blk := range blks { - nd := help.NewUnixfsNode() - nd.SetData(blk) - dagnd, err := nd.GetDagNode() - if err != nil { - return nil, err - } - - k, err := dm.dagserv.Add(dagnd) - if err != nil { - return nil, err - } - out = append(out, k) - } - return out, nil -} - // GetNode gets the modified DAG Node func (dm *DagModifier) GetNode() (*mdag.Node, error) { err := dm.Flush() @@ -352,9 +336,9 @@ func (dm *DagModifier) Seek(offset int64, whence int) (int64, error) { dm.curWrOff = uint64(offset) dm.writeStart = uint64(offset) case os.SEEK_END: - return 0, errors.New("SEEK_END currently not implemented") + return 0, ErrSeekEndNotImpl default: - return 0, errors.New("unrecognized whence") + return 0, ErrUnrecognizedWhence } if dm.read != nil { @@ -425,6 +409,7 @@ func dagTruncate(nd *mdag.Node, size uint64, ds mdag.DAGService) (*mdag.Node, er return nil, err } + // found the child we want to cut if size < cur+childsize { nchild, err := dagTruncate(child, size-cur, ds) if err != nil { From fff62d89646c3f94d9828e1c24e16dcb4bc54113 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 9 Mar 2015 18:21:10 -0700 Subject: [PATCH 0802/3817] remove pointless TODO This commit was moved from ipfs/go-unixfs@7201c98adcb71115da325793f3145cab63044453 --- unixfs/mod/dagmodifier.go | 1 - 1 file changed, 1 deletion(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index c9bb17d35..ee6e3d9c9 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -416,7 +416,6 @@ func dagTruncate(nd *mdag.Node, size uint64, ds mdag.DAGService) (*mdag.Node, er return nil, err } - // TODO: sanity check size of truncated block ndata.AddBlockSize(size - cur) modified = nchild From fd44da575159166101ca2a103beb3f78bbf71fcb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 10 Mar 2015 00:27:08 -0700 Subject: [PATCH 0803/3817] remove temp code in Coal test, and make Size not have to flush This commit was moved from ipfs/go-unixfs@0d88fa496b0294b170d0d6690feb3459586a6ad7 --- unixfs/mod/dagmodifier.go | 10 +++++----- unixfs/mod/dagmodifier_test.go | 33 ++++++++++----------------------- 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index ee6e3d9c9..eddf221f4 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -137,15 +137,15 @@ func (dm *DagModifier) Write(b []byte) (int, error) { } func (dm *DagModifier) Size() (int64, error) { - // TODO: compute size without flushing, should be easy - err := dm.Flush() + pbn, err := ft.FromBytes(dm.curNode.Data) if err != nil { return 0, err } - pbn, err := ft.FromBytes(dm.curNode.Data) - if err != nil { - return 0, err + if dm.wrBuf != nil { + if uint64(dm.wrBuf.Len())+dm.writeStart > pbn.GetFilesize() { + return int64(dm.wrBuf.Len()) + int64(dm.writeStart), nil + } } return int64(pbn.GetFilesize()), nil diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index d384c5ccc..e6b51315f 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -177,6 +177,15 @@ func TestMultiWrite(t *testing.T) { if n != 1 { t.Fatal("Somehow wrote the wrong number of bytes! (n != 1)") } + + size, err := dagmod.Size() + if err != nil { + t.Fatal(err) + } + + if size != int64(i+1) { + t.Fatal("Size was reported incorrectly") + } } nd, err := dagmod.GetNode() if err != nil { @@ -305,6 +314,7 @@ func TestMultiWriteCoal(t *testing.T) { u.NewTimeSeededRand().Read(data) for i := 0; i < len(data); i++ { + log.Error(i) n, err := dagmod.WriteAt(data[:i+1], 0) if err != nil { fmt.Println("FAIL AT ", i) @@ -314,29 +324,6 @@ func TestMultiWriteCoal(t *testing.T) { t.Fatal("Somehow wrote the wrong number of bytes! (n != 1)") } - // TEMP - nn, err := dagmod.GetNode() - if err != nil { - t.Fatal(err) - } - - r, err := uio.NewDagReader(ctx, nn, dserv) - if err != nil { - t.Fatal(err) - } - - out, err := ioutil.ReadAll(r) - if err != nil { - t.Fatal(err) - } - - if err := arrComp(out, data[:i+1]); err != nil { - fmt.Println("A ", len(out)) - fmt.Println(out) - fmt.Println(data[:i+1]) - t.Fatal(err) - } - // } nd, err := dagmod.GetNode() if err != nil { From 8fafb1c96c03c628a373eda36b7f08e8869dc094 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 10 Mar 2015 00:31:06 -0700 Subject: [PATCH 0804/3817] overwrite optimization for dagmodifier This commit was moved from ipfs/go-unixfs@5fc12a98404020062a2bf4166160c9c8ffbc3550 --- unixfs/mod/dagmodifier.go | 9 ++++++++- unixfs/mod/dagmodifier_test.go | 1 - 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index eddf221f4..61f480e9e 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -63,7 +63,12 @@ func (dm *DagModifier) WriteAt(b []byte, offset int64) (int, error) { // TODO: this is currently VERY inneficient // each write that happens at an offset other than the current one causes a // flush to disk, and dag rewrite - if uint64(offset) != dm.curWrOff { + if offset == int64(dm.writeStart) && dm.wrBuf != nil { + // If we would overwrite the previous write + if len(b) >= dm.wrBuf.Len() { + dm.wrBuf.Reset() + } + } else if uint64(offset) != dm.curWrOff { size, err := dm.Size() if err != nil { return 0, err @@ -242,6 +247,7 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) var cur uint64 var done bool for i, bs := range f.GetBlocksizes() { + // We found the correct child to write into if cur+bs > offset { child, err := node.Links[i].GetNode(dm.dagserv) if err != nil { @@ -256,6 +262,7 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) node.Links[i].Hash = mh.Multihash(k) if sdone { + // No more bytes to write! done = true break } diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index e6b51315f..90c6d99e5 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -314,7 +314,6 @@ func TestMultiWriteCoal(t *testing.T) { u.NewTimeSeededRand().Read(data) for i := 0; i < len(data); i++ { - log.Error(i) n, err := dagmod.WriteAt(data[:i+1], 0) if err != nil { fmt.Println("FAIL AT ", i) From 6c56ec8814b93965fabc1abd9df50c99fd54b7da Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 10 Mar 2015 12:57:29 -0700 Subject: [PATCH 0805/3817] Correct pinning for dagmodifier, and a bunch more tests This commit was moved from ipfs/go-ipfs-pinner@d19f2effe2ce49872b5f938cbefa93996979d7f6 --- pinning/pinner/pin.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 68e627c1e..7ae36f607 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -47,6 +47,7 @@ type Pinner interface { // may not be successful type ManualPinner interface { PinWithMode(util.Key, PinMode) + RemovePinWithMode(util.Key, PinMode) Pinner } @@ -198,6 +199,20 @@ func (p *pinner) IsPinned(key util.Key) bool { p.indirPin.HasKey(key) } +func (p *pinner) RemovePinWithMode(key util.Key, mode PinMode) { + switch mode { + case Direct: + p.directPin.RemoveBlock(key) + case Indirect: + p.indirPin.Decrement(key) + case Recursive: + p.recursePin.RemoveBlock(key) + default: + // programmer error, panic OK + panic("unrecognized pin type") + } +} + // LoadPinner loads a pinner and its keysets from the given datastore func LoadPinner(d ds.ThreadSafeDatastore, dserv mdag.DAGService) (Pinner, error) { p := new(pinner) From 20301074593bb0baaa2f4ab2ab73dc19e7a9a1eb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 10 Mar 2015 12:57:29 -0700 Subject: [PATCH 0806/3817] Correct pinning for dagmodifier, and a bunch more tests This commit was moved from ipfs/go-unixfs@7147a88ffb00c714f99322fbd2753c188ff5eb2e --- unixfs/mod/dagmodifier.go | 46 ++++++- unixfs/mod/dagmodifier_test.go | 235 +++++++++++++++++++++++++++++---- 2 files changed, 248 insertions(+), 33 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 61f480e9e..e08c3bf86 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -172,13 +172,19 @@ func (dm *DagModifier) Flush() error { // Number of bytes we're going to write buflen := dm.wrBuf.Len() + // Grab key for unpinning after mod operation + curk, err := dm.curNode.Key() + if err != nil { + return err + } + // overwrite existing dag nodes - k, done, err := dm.modifyDag(dm.curNode, dm.writeStart, dm.wrBuf) + thisk, done, err := dm.modifyDag(dm.curNode, dm.writeStart, dm.wrBuf) if err != nil { return err } - nd, err := dm.dagserv.Get(k) + nd, err := dm.dagserv.Get(thisk) if err != nil { return err } @@ -193,7 +199,7 @@ func (dm *DagModifier) Flush() error { return err } - _, err := dm.dagserv.Add(nd) + thisk, err = dm.dagserv.Add(nd) if err != nil { return err } @@ -201,6 +207,14 @@ func (dm *DagModifier) Flush() error { dm.curNode = nd } + // Finalize correct pinning, and flush pinner + dm.mp.PinWithMode(thisk, pin.Recursive) + dm.mp.RemovePinWithMode(curk, pin.Recursive) + err = dm.mp.Flush() + if err != nil { + return err + } + dm.writeStart += uint64(buflen) dm.wrBuf = nil @@ -237,7 +251,7 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) // Hey look! we're done! var done bool - if n < len(f.Data) { + if n < len(f.Data[offset:]) { done = true } @@ -249,6 +263,10 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) for i, bs := range f.GetBlocksizes() { // We found the correct child to write into if cur+bs > offset { + // Unpin block + ckey := u.Key(node.Links[i].Hash) + dm.mp.RemovePinWithMode(ckey, pin.Indirect) + child, err := node.Links[i].GetNode(dm.dagserv) if err != nil { return "", false, err @@ -258,14 +276,24 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) return "", false, err } + // pin the new node + dm.mp.PinWithMode(k, pin.Indirect) + offset += bs node.Links[i].Hash = mh.Multihash(k) + // Recache serialized node + _, err = node.Encoded(true) + if err != nil { + return "", false, err + } + if sdone { // No more bytes to write! done = true break } + offset = cur + bs } cur += bs } @@ -293,7 +321,8 @@ func (dm *DagModifier) Read(b []byte) (int, error) { } if dm.read == nil { - dr, err := uio.NewDagReader(dm.ctx, dm.curNode, dm.dagserv) + ctx, cancel := context.WithCancel(dm.ctx) + dr, err := uio.NewDagReader(ctx, dm.curNode, dm.dagserv) if err != nil { return 0, err } @@ -307,6 +336,7 @@ func (dm *DagModifier) Read(b []byte) (int, error) { return 0, ErrSeekFail } + dm.readCancel = cancel dm.read = dr } @@ -451,5 +481,11 @@ func dagTruncate(nd *mdag.Node, size uint64, ds mdag.DAGService) (*mdag.Node, er nd.Data = d + // invalidate cache and recompute serialized data + _, err = nd.Encoded(true) + if err != nil { + return nil, err + } + return nd, nil } diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 90c6d99e5..d5ae29d7d 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -4,6 +4,8 @@ import ( "fmt" "io" "io/ioutil" + "math/rand" + "os" "testing" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" @@ -15,6 +17,7 @@ import ( h "github.com/jbenet/go-ipfs/importer/helpers" trickle "github.com/jbenet/go-ipfs/importer/trickle" mdag "github.com/jbenet/go-ipfs/merkledag" + pin "github.com/jbenet/go-ipfs/pin" ft "github.com/jbenet/go-ipfs/unixfs" uio "github.com/jbenet/go-ipfs/unixfs/io" u "github.com/jbenet/go-ipfs/util" @@ -23,7 +26,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) -func getMockDagServ(t testing.TB) mdag.DAGService { +func getMockDagServ(t testing.TB) (mdag.DAGService, pin.ManualPinner) { dstore := ds.NewMapDatastore() tsds := sync.MutexWrap(dstore) bstore := blockstore.NewBlockstore(tsds) @@ -31,12 +34,25 @@ func getMockDagServ(t testing.TB) mdag.DAGService { if err != nil { t.Fatal(err) } - return mdag.NewDAGService(bserv) + dserv := mdag.NewDAGService(bserv) + return dserv, pin.NewPinner(tsds, dserv).GetManual() } -func getNode(t testing.TB, dserv mdag.DAGService, size int64) ([]byte, *mdag.Node) { +func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.Blockstore, pin.ManualPinner) { + dstore := ds.NewMapDatastore() + tsds := sync.MutexWrap(dstore) + bstore := blockstore.NewBlockstore(tsds) + bserv, err := bs.New(bstore, offline.Exchange(bstore)) + if err != nil { + t.Fatal(err) + } + dserv := mdag.NewDAGService(bserv) + return dserv, bstore, pin.NewPinner(tsds, dserv).GetManual() +} + +func getNode(t testing.TB, dserv mdag.DAGService, size int64, pinner pin.ManualPinner) ([]byte, *mdag.Node) { in := io.LimitReader(u.NewTimeSeededRand(), size) - node, err := imp.BuildTrickleDagFromReader(in, dserv, nil, &chunk.SizeSplitter{500}) + node, err := imp.BuildTrickleDagFromReader(in, dserv, pinner, &chunk.SizeSplitter{500}) if err != nil { t.Fatal(err) } @@ -101,12 +117,12 @@ func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) } func TestDagModifierBasic(t *testing.T) { - dserv := getMockDagServ(t) - b, n := getNode(t, dserv, 50000) + dserv, pin := getMockDagServ(t) + b, n := getNode(t, dserv, 50000, pin) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pin, &chunk.SizeSplitter{Size: 512}) if err != nil { t.Fatal(err) } @@ -155,13 +171,13 @@ func TestDagModifierBasic(t *testing.T) { } func TestMultiWrite(t *testing.T) { - dserv := getMockDagServ(t) - _, n := getNode(t, dserv, 0) + dserv, pins := getMockDagServ(t) + _, n := getNode(t, dserv, 0, pins) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) if err != nil { t.Fatal(err) } @@ -208,13 +224,13 @@ func TestMultiWrite(t *testing.T) { } func TestMultiWriteAndFlush(t *testing.T) { - dserv := getMockDagServ(t) - _, n := getNode(t, dserv, 0) + dserv, pins := getMockDagServ(t) + _, n := getNode(t, dserv, 0, pins) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) if err != nil { t.Fatal(err) } @@ -256,13 +272,13 @@ func TestMultiWriteAndFlush(t *testing.T) { } func TestWriteNewFile(t *testing.T) { - dserv := getMockDagServ(t) - _, n := getNode(t, dserv, 0) + dserv, pins := getMockDagServ(t) + _, n := getNode(t, dserv, 0, pins) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) if err != nil { t.Fatal(err) } @@ -299,13 +315,13 @@ func TestWriteNewFile(t *testing.T) { } func TestMultiWriteCoal(t *testing.T) { - dserv := getMockDagServ(t) - _, n := getNode(t, dserv, 0) + dserv, pins := getMockDagServ(t) + _, n := getNode(t, dserv, 0, pins) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) if err != nil { t.Fatal(err) } @@ -345,13 +361,13 @@ func TestMultiWriteCoal(t *testing.T) { } func TestLargeWriteChunks(t *testing.T) { - dserv := getMockDagServ(t) - _, n := getNode(t, dserv, 0) + dserv, pins := getMockDagServ(t) + _, n := getNode(t, dserv, 0, pins) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) if err != nil { t.Fatal(err) } @@ -384,12 +400,12 @@ func TestLargeWriteChunks(t *testing.T) { } func TestDagTruncate(t *testing.T) { - dserv := getMockDagServ(t) - b, n := getNode(t, dserv, 50000) + dserv, pins := getMockDagServ(t) + b, n := getNode(t, dserv, 50000, pins) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) if err != nil { t.Fatal(err) } @@ -399,6 +415,11 @@ func TestDagTruncate(t *testing.T) { t.Fatal(err) } + _, err = dagmod.Seek(0, os.SEEK_SET) + if err != nil { + t.Fatal(err) + } + out, err := ioutil.ReadAll(dagmod) if err != nil { t.Fatal(err) @@ -409,16 +430,174 @@ func TestDagTruncate(t *testing.T) { } } +func TestSparseWrite(t *testing.T) { + dserv, pins := getMockDagServ(t) + _, n := getNode(t, dserv, 0, pins) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) + if err != nil { + t.Fatal(err) + } + + buf := make([]byte, 5000) + u.NewTimeSeededRand().Read(buf[2500:]) + + wrote, err := dagmod.WriteAt(buf[2500:], 2500) + if err != nil { + t.Fatal(err) + } + + if wrote != 2500 { + t.Fatal("incorrect write amount") + } + + _, err = dagmod.Seek(0, os.SEEK_SET) + if err != nil { + t.Fatal(err) + } + + out, err := ioutil.ReadAll(dagmod) + if err != nil { + t.Fatal(err) + } + + if err = arrComp(out, buf); err != nil { + t.Fatal(err) + } +} + +func basicGC(t *testing.T, bs blockstore.Blockstore, pins pin.ManualPinner) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() // in case error occurs during operation + keychan, err := bs.AllKeysChan(ctx) + if err != nil { + t.Fatal(err) + } + for k := range keychan { // rely on AllKeysChan to close chan + if !pins.IsPinned(k) { + err := bs.DeleteBlock(k) + if err != nil { + t.Fatal(err) + } + } + } +} +func TestCorrectPinning(t *testing.T) { + dserv, bstore, pins := getMockDagServAndBstore(t) + b, n := getNode(t, dserv, 50000, pins) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) + if err != nil { + t.Fatal(err) + } + + buf := make([]byte, 1024) + for i := 0; i < 100; i++ { + size, err := dagmod.Size() + if err != nil { + t.Fatal(err) + } + offset := rand.Intn(int(size)) + u.NewTimeSeededRand().Read(buf) + + if offset+len(buf) > int(size) { + b = append(b[:offset], buf...) + } else { + copy(b[offset:], buf) + } + + n, err := dagmod.WriteAt(buf, int64(offset)) + if err != nil { + t.Fatal(err) + } + if n != len(buf) { + t.Fatal("wrote incorrect number of bytes") + } + } + + fisize, err := dagmod.Size() + if err != nil { + t.Fatal(err) + } + + if int(fisize) != len(b) { + t.Fatal("reported filesize incorrect", fisize, len(b)) + } + + // Run a GC, then ensure we can still read the file correctly + basicGC(t, bstore, pins) + + nd, err := dagmod.GetNode() + if err != nil { + t.Fatal(err) + } + read, err := uio.NewDagReader(context.Background(), nd, dserv) + if err != nil { + t.Fatal(err) + } + + out, err := ioutil.ReadAll(read) + if err != nil { + t.Fatal(err) + } + + if err = arrComp(out, b); err != nil { + t.Fatal(err) + } + + rootk, err := nd.Key() + if err != nil { + t.Fatal(err) + } + + // Verify only one recursive pin + recpins := pins.RecursiveKeys() + if len(recpins) != 1 { + t.Fatal("Incorrect number of pinned entries") + } + + // verify the correct node is pinned + if recpins[0] != rootk { + t.Fatal("Incorrect node recursively pinned") + } + + indirpins := pins.IndirectKeys() + children := enumerateChildren(t, nd, dserv) + if len(indirpins) != len(children) { + t.Log(len(indirpins), len(children)) + t.Fatal("Incorrect number of indirectly pinned blocks") + } + +} + +func enumerateChildren(t *testing.T, nd *mdag.Node, ds mdag.DAGService) []u.Key { + var out []u.Key + for _, lnk := range nd.Links { + out = append(out, u.Key(lnk.Hash)) + child, err := lnk.GetNode(ds) + if err != nil { + t.Fatal(err) + } + children := enumerateChildren(t, child, ds) + out = append(out, children...) + } + return out +} + func BenchmarkDagmodWrite(b *testing.B) { b.StopTimer() - dserv := getMockDagServ(b) - _, n := getNode(b, dserv, 0) + dserv, pins := getMockDagServ(b) + _, n := getNode(b, dserv, 0, pins) ctx, cancel := context.WithCancel(context.Background()) defer cancel() wrsize := 4096 - dagmod, err := NewDagModifier(ctx, n, dserv, nil, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) if err != nil { b.Fatal(err) } From 9c98b11f2f28b16488d957c6af3c051fc41221a1 Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Wed, 11 Mar 2015 17:24:04 -0700 Subject: [PATCH 0807/3817] Dead code cleanup: remove Range support from blockstore Nothing uses it, and offset+limit is a bad query mechanism for mutating data. This commit was moved from ipfs/go-ipfs-blockstore@0ef353be8b1f84a2d68e1bbd686e71bf37f0960a --- blockstore/blockstore.go | 29 ++++++++--------------------- blockstore/blockstore_test.go | 17 ----------------- blockstore/write_cache.go | 12 ++---------- 3 files changed, 10 insertions(+), 48 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 7c7e7ed2d..dc94dbc6f 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -33,9 +33,6 @@ type Blockstore interface { AllKeys(ctx context.Context) ([]u.Key, error) AllKeysChan(ctx context.Context) (<-chan u.Key, error) - - AllKeysRange(ctx context.Context, offset int, limit int) ([]u.Key, error) - AllKeysRangeChan(ctx context.Context, offset int, limit int) (<-chan u.Key, error) } func NewBlockstore(d ds.ThreadSafeDatastore) Blockstore { @@ -85,22 +82,13 @@ func (s *blockstore) DeleteBlock(k u.Key) error { return s.datastore.Delete(k.DsKey()) } -func (bs *blockstore) AllKeys(ctx context.Context) ([]u.Key, error) { - return bs.AllKeysRange(ctx, 0, 0) -} - -func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan u.Key, error) { - return bs.AllKeysRangeChan(ctx, 0, 0) -} - -// AllKeysRange runs a query for keys from the blockstore. +// AllKeys runs a query for keys from the blockstore. // this is very simplistic, in the future, take dsq.Query as a param? -// if offset and limit are 0, they are ignored. // -// AllKeysRange respects context -func (bs *blockstore) AllKeysRange(ctx context.Context, offset int, limit int) ([]u.Key, error) { +// AllKeys respects context +func (bs *blockstore) AllKeys(ctx context.Context) ([]u.Key, error) { - ch, err := bs.AllKeysRangeChan(ctx, offset, limit) + ch, err := bs.AllKeysChan(ctx) if err != nil { return nil, err } @@ -112,15 +100,14 @@ func (bs *blockstore) AllKeysRange(ctx context.Context, offset int, limit int) ( return keys, nil } -// AllKeysRangeChan runs a query for keys from the blockstore. +// AllKeysChan runs a query for keys from the blockstore. // this is very simplistic, in the future, take dsq.Query as a param? -// if offset and limit are 0, they are ignored. // -// AllKeysRangeChan respects context -func (bs *blockstore) AllKeysRangeChan(ctx context.Context, offset int, limit int) (<-chan u.Key, error) { +// AllKeysChan respects context +func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan u.Key, error) { // KeysOnly, because that would be _a lot_ of data. - q := dsq.Query{KeysOnly: true, Offset: offset, Limit: limit} + q := dsq.Query{KeysOnly: true} res, err := bs.datastore.Query(q) if err != nil { return nil, err diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 0601773a0..4daed126d 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -78,23 +78,6 @@ func TestAllKeysSimple(t *testing.T) { expectMatches(t, keys, keys2) } -func TestAllKeysOffsetAndLimit(t *testing.T) { - N := 30 - bs, _ := newBlockStoreWithKeys(t, nil, N) - - ctx := context.Background() - keys3, err := bs.AllKeysRange(ctx, N/3, N/3) - if err != nil { - t.Fatal(err) - } - for _, k3 := range keys3 { - t.Log("found ", k3.Pretty()) - } - if len(keys3) != N/3 { - t.Errorf("keys3 should be: %d != %d", N/3, len(keys3)) - } -} - func TestAllKeysRespectsContext(t *testing.T) { N := 100 diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index d082e0cdc..b60a4d2c2 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -46,17 +46,9 @@ func (w *writecache) Put(b *blocks.Block) error { } func (w *writecache) AllKeys(ctx context.Context) ([]u.Key, error) { - return w.blockstore.AllKeysRange(ctx, 0, 0) + return w.blockstore.AllKeys(ctx) } func (w *writecache) AllKeysChan(ctx context.Context) (<-chan u.Key, error) { - return w.blockstore.AllKeysRangeChan(ctx, 0, 0) -} - -func (w *writecache) AllKeysRange(ctx context.Context, offset int, limit int) ([]u.Key, error) { - return w.blockstore.AllKeysRange(ctx, offset, limit) -} - -func (w *writecache) AllKeysRangeChan(ctx context.Context, offset int, limit int) (<-chan u.Key, error) { - return w.blockstore.AllKeysRangeChan(ctx, offset, limit) + return w.blockstore.AllKeysChan(ctx) } From 005dfe5729904b81fb3239764b513e0d0fbaa0bf Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Wed, 11 Mar 2015 17:29:20 -0700 Subject: [PATCH 0808/3817] Dead code cleanup: remove AllKeys from blockstore Nothing uses it. This commit was moved from ipfs/go-ipfs-blockstore@4f7282235f8fad65fcf1fd88653fb670b6b77504 --- blockstore/blockstore.go | 19 ------------------- blockstore/blockstore_test.go | 15 +++++++++++++-- blockstore/write_cache.go | 4 ---- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index dc94dbc6f..7e929af10 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -31,7 +31,6 @@ type Blockstore interface { Get(u.Key) (*blocks.Block, error) Put(*blocks.Block) error - AllKeys(ctx context.Context) ([]u.Key, error) AllKeysChan(ctx context.Context) (<-chan u.Key, error) } @@ -82,24 +81,6 @@ func (s *blockstore) DeleteBlock(k u.Key) error { return s.datastore.Delete(k.DsKey()) } -// AllKeys runs a query for keys from the blockstore. -// this is very simplistic, in the future, take dsq.Query as a param? -// -// AllKeys respects context -func (bs *blockstore) AllKeys(ctx context.Context) ([]u.Key, error) { - - ch, err := bs.AllKeysChan(ctx) - if err != nil { - return nil, err - } - - var keys []u.Key - for k := range ch { - keys = append(keys, k) - } - return keys, nil -} - // AllKeysChan runs a query for keys from the blockstore. // this is very simplistic, in the future, take dsq.Query as a param? // diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 4daed126d..51f5aad11 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -63,14 +63,24 @@ func newBlockStoreWithKeys(t *testing.T, d ds.Datastore, N int) (Blockstore, []u return bs, keys } +func collect(ch <-chan u.Key) []u.Key { + var keys []u.Key + for k := range ch { + keys = append(keys, k) + } + return keys +} + func TestAllKeysSimple(t *testing.T) { bs, keys := newBlockStoreWithKeys(t, nil, 100) ctx := context.Background() - keys2, err := bs.AllKeys(ctx) + ch, err := bs.AllKeysChan(ctx) if err != nil { t.Fatal(err) } + keys2 := collect(ch) + // for _, k2 := range keys2 { // t.Log("found ", k2.Pretty()) // } @@ -90,10 +100,11 @@ func TestAllKeysRespectsContext(t *testing.T) { getKeys := func(ctx context.Context) { started <- struct{}{} - _, err := bs.AllKeys(ctx) // once without cancelling + ch, err := bs.AllKeysChan(ctx) // once without cancelling if err != nil { errors <- err } + _ = collect(ch) done <- struct{}{} errors <- nil // a nil one to signal break } diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index b60a4d2c2..a1399fcc6 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -45,10 +45,6 @@ func (w *writecache) Put(b *blocks.Block) error { return w.blockstore.Put(b) } -func (w *writecache) AllKeys(ctx context.Context) ([]u.Key, error) { - return w.blockstore.AllKeys(ctx) -} - func (w *writecache) AllKeysChan(ctx context.Context) (<-chan u.Key, error) { return w.blockstore.AllKeysChan(ctx) } From 9d6b4900c0533a3e6301fdf51d18b5091848e1ae Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 17 Mar 2015 14:51:26 -0700 Subject: [PATCH 0809/3817] fix locking in the pinner This commit was moved from ipfs/go-ipfs-pinner@ae27f8ad7e5a8dae4206bd80f06a1144d90d7d80 --- pinning/pinner/pin.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 7ae36f607..5f726a457 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -200,6 +200,8 @@ func (p *pinner) IsPinned(key util.Key) bool { } func (p *pinner) RemovePinWithMode(key util.Key, mode PinMode) { + p.lock.Lock() + defer p.lock.Unlock() switch mode { case Direct: p.directPin.RemoveBlock(key) @@ -265,8 +267,8 @@ func (p *pinner) RecursiveKeys() []util.Key { // Flush encodes and writes pinner keysets to the datastore func (p *pinner) Flush() error { - p.lock.RLock() - defer p.lock.RUnlock() + p.lock.Lock() + defer p.lock.Unlock() err := storeSet(p.dstore, directPinDatastoreKey, p.directPin.GetKeys()) if err != nil { @@ -311,6 +313,8 @@ func loadSet(d ds.Datastore, k ds.Key, val interface{}) error { // PinWithMode is a method on ManualPinners, allowing the user to have fine // grained control over pin counts func (p *pinner) PinWithMode(k util.Key, mode PinMode) { + p.lock.Lock() + defer p.lock.Unlock() switch mode { case Recursive: p.recursePin.AddBlock(k) From ded939ff67dafc841a8f4f070da57ea791698ae2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 17 Mar 2015 15:39:00 -0700 Subject: [PATCH 0810/3817] move keyspace init function into namesys This commit was moved from ipfs/go-namesys@a4feb39f6b378c6a2bcee5c32935b9ba37b98650 --- namesys/publisher.go | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index d786c210b..5d763f490 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -10,10 +10,13 @@ import ( mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + dag "github.com/jbenet/go-ipfs/merkledag" pb "github.com/jbenet/go-ipfs/namesys/internal/pb" ci "github.com/jbenet/go-ipfs/p2p/crypto" + pin "github.com/jbenet/go-ipfs/pin" routing "github.com/jbenet/go-ipfs/routing" record "github.com/jbenet/go-ipfs/routing/record" + ft "github.com/jbenet/go-ipfs/unixfs" u "github.com/jbenet/go-ipfs/util" ) @@ -60,11 +63,9 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value u.Key) nameb := u.Hash(pkbytes) namekey := u.Key("/pk/" + string(nameb)) - timectx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Second*10)) - defer cancel() - log.Debugf("Storing pubkey at: %s", namekey) // Store associated public key + timectx, _ := context.WithDeadline(ctx, time.Now().Add(time.Second*10)) err = p.routing.PutValue(timectx, namekey, pkbytes) if err != nil { return err @@ -136,3 +137,31 @@ func ValidateIpnsRecord(k u.Key, val []byte) error { } return nil } + +// InitializeKeyspace sets the ipns record for the given key to +// point to an empty directory. +// TODO: this doesnt feel like it belongs here +func InitializeKeyspace(ctx context.Context, ds dag.DAGService, pub Publisher, pins pin.Pinner, key ci.PrivKey) error { + emptyDir := &dag.Node{Data: ft.FolderPBData()} + nodek, err := ds.Add(emptyDir) + if err != nil { + return err + } + + err = pins.Pin(emptyDir, false) + if err != nil { + return err + } + + err = pins.Flush() + if err != nil { + return err + } + + err = pub.Publish(ctx, key, nodek) + if err != nil { + return err + } + + return nil +} From cba06a49f0e3ff372af4f5b7ab6e5613f4d2933f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 17 Mar 2015 21:03:37 -0700 Subject: [PATCH 0811/3817] ignore bootstrap failures in namesys initialization This commit was moved from ipfs/go-ipfs-routing@a10aa37778f73512284c89e6b284d80be5af2616 --- routing/dht/lookup.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 59ef3911f..1f01a082a 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -6,7 +6,6 @@ import ( peer "github.com/jbenet/go-ipfs/p2p/peer" kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" - errors "github.com/jbenet/go-ipfs/util/debugerror" pset "github.com/jbenet/go-ipfs/util/peerset" ) @@ -26,7 +25,7 @@ func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key u.Key) (<-chan peer e := log.EventBegin(ctx, "getClosestPeers", &key) tablepeers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) if len(tablepeers) == 0 { - return nil, errors.Wrap(kb.ErrLookupFailure) + return nil, kb.ErrLookupFailure } out := make(chan peer.ID, KValue) From 745e36eef654aea5dc4f68e68e5c86e7039447ff Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 18 Mar 2015 15:52:09 -0700 Subject: [PATCH 0812/3817] fix for weird repo init issue This commit was moved from ipfs/go-namesys@2ba44b0b5a05c4da935aa2118964f18a60495d77 --- namesys/publisher.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 5d763f490..c96915307 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -148,7 +148,9 @@ func InitializeKeyspace(ctx context.Context, ds dag.DAGService, pub Publisher, p return err } - err = pins.Pin(emptyDir, false) + // pin recursively because this might already be pinned + // and doing a direct pin would throw an error in that case + err = pins.Pin(emptyDir, true) if err != nil { return err } From ba4ca232ce989f27a4e127bff4577955b0f1f273 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 18 Mar 2015 16:48:06 -0700 Subject: [PATCH 0813/3817] test for pinning semantics This commit was moved from ipfs/go-ipfs-pinner@1b4817eaca49c51c37b8301de56f90710d363cb7 --- pinning/pinner/pin_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index c8d18f027..12db39b29 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -152,3 +152,41 @@ func TestPinnerBasic(t *testing.T) { t.Fatal("could not find recursively pinned node") } } + +func TestDuplicateSemantics(t *testing.T) { + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv, err := bs.New(bstore, offline.Exchange(bstore)) + if err != nil { + t.Fatal(err) + } + + dserv := mdag.NewDAGService(bserv) + + // TODO does pinner need to share datastore with blockservice? + p := NewPinner(dstore, dserv) + + a, _ := randNode() + _, err = dserv.Add(a) + if err != nil { + t.Fatal(err) + } + + // pin is recursively + err = p.Pin(a, true) + if err != nil { + t.Fatal(err) + } + + // pinning directly should fail + err = p.Pin(a, false) + if err == nil { + t.Fatal("expected direct pin to fail") + } + + // pinning recursively again should succeed + err = p.Pin(a, true) + if err != nil { + t.Fatal(err) + } +} From 474e3df37e61b412107f632a3fd75cd7bd28a5b1 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 19 Mar 2015 04:01:15 -0700 Subject: [PATCH 0814/3817] dht: tone down dht bootstrap move to a less aggressive period. 5m instead of 20s This commit was moved from ipfs/go-ipfs-routing@809b09ef8be91dd67809fbe0ae53cfebf82fb4e1 --- routing/dht/dht_bootstrap.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 79dcb4d64..d5bbfa860 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -35,13 +35,12 @@ var DefaultBootstrapConfig = BootstrapConfig{ // of our implementation's robustness, we should lower this down to 8 or 4. Queries: 1, - // For now, this is set to 10 seconds, which is an aggressive period. We are + // For now, this is set to 1 minute, which is a medium period. We are // We are currently more interested in ensuring we have a properly formed - // DHT than making sure our dht minimizes traffic. Once we are more certain - // implementation's robustness, we should lower this down to 30s or 1m. - Period: time.Duration(20 * time.Second), + // DHT than making sure our dht minimizes traffic. + Period: time.Duration(5 * time.Minute), - Timeout: time.Duration(20 * time.Second), + Timeout: time.Duration(10 * time.Second), } // Bootstrap ensures the dht routing table remains healthy as peers come and go. From f051bc9242028b02cde7c71190ea8e7daf104618 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 19 Mar 2015 22:50:28 -0700 Subject: [PATCH 0815/3817] invalidate merkledag cache when modifying children This commit was moved from ipfs/go-merkledag@a83cbb99da466a4b5b6b8caf7db6cb9de342fff8 --- ipld/merkledag/node.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 2848cdd3a..848b228dc 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -87,6 +87,7 @@ func (l *Link) GetNode(serv DAGService) (*Node, error) { // AddNodeLink adds a link to another node. func (n *Node) AddNodeLink(name string, that *Node) error { + n.encoded = nil lnk, err := MakeLink(that) if err != nil { return err @@ -101,6 +102,7 @@ func (n *Node) AddNodeLink(name string, that *Node) error { // AddNodeLink adds a link to another node. without keeping a reference to // the child node func (n *Node) AddNodeLinkClean(name string, that *Node) error { + n.encoded = nil lnk, err := MakeLink(that) if err != nil { return err @@ -113,6 +115,7 @@ func (n *Node) AddNodeLinkClean(name string, that *Node) error { // Remove a link on this node by the given name func (n *Node) RemoveNodeLink(name string) error { + n.encoded = nil for i, l := range n.Links { if l.Name == name { n.Links = append(n.Links[:i], n.Links[i+1:]...) From c9d0407ea4efe72f2b9d92c2fc7bdbc01ac7274b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 18 Mar 2015 21:48:52 -0700 Subject: [PATCH 0816/3817] code cleanup and better naming of methods This commit was moved from ipfs/go-unixfs@554e71fda8618a54326f04c7715cdb3bc47fc96f --- unixfs/mod/dagmodifier.go | 16 ++++++++-------- unixfs/mod/dagmodifier_test.go | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index e08c3bf86..cd207ed91 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -80,7 +80,7 @@ func (dm *DagModifier) WriteAt(b []byte, offset int64) (int, error) { } } - err = dm.Flush() + err = dm.Sync() if err != nil { return 0, err } @@ -133,7 +133,7 @@ func (dm *DagModifier) Write(b []byte) (int, error) { } dm.curWrOff += uint64(n) if dm.wrBuf.Len() > writebufferSize { - err := dm.Flush() + err := dm.Sync() if err != nil { return n, err } @@ -156,8 +156,8 @@ func (dm *DagModifier) Size() (int64, error) { return int64(pbn.GetFilesize()), nil } -// Flush writes changes to this dag to disk -func (dm *DagModifier) Flush() error { +// Sync writes changes to this dag to disk +func (dm *DagModifier) Sync() error { // No buffer? Nothing to do if dm.wrBuf == nil { return nil @@ -315,7 +315,7 @@ func (dm *DagModifier) appendData(node *mdag.Node, blks <-chan []byte) (*mdag.No // Read data from this dag starting at the current offset func (dm *DagModifier) Read(b []byte) (int, error) { - err := dm.Flush() + err := dm.Sync() if err != nil { return 0, err } @@ -347,7 +347,7 @@ func (dm *DagModifier) Read(b []byte) (int, error) { // GetNode gets the modified DAG Node func (dm *DagModifier) GetNode() (*mdag.Node, error) { - err := dm.Flush() + err := dm.Sync() if err != nil { return nil, err } @@ -360,7 +360,7 @@ func (dm *DagModifier) HasChanges() bool { } func (dm *DagModifier) Seek(offset int64, whence int) (int64, error) { - err := dm.Flush() + err := dm.Sync() if err != nil { return 0, err } @@ -389,7 +389,7 @@ func (dm *DagModifier) Seek(offset int64, whence int) (int64, error) { } func (dm *DagModifier) Truncate(size int64) error { - err := dm.Flush() + err := dm.Sync() if err != nil { return err } diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index d5ae29d7d..9f8050972 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -246,7 +246,7 @@ func TestMultiWriteAndFlush(t *testing.T) { if n != 1 { t.Fatal("Somehow wrote the wrong number of bytes! (n != 1)") } - err = dagmod.Flush() + err = dagmod.Sync() if err != nil { t.Fatal(err) } From f36cd2aa6bbe43b34bd816fe408b712a215270d9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 23 Mar 2015 14:01:42 -0700 Subject: [PATCH 0817/3817] fix context respect through fuse reading This commit was moved from ipfs/go-merkledag@513740f9aced79fa0a4e378992880c88cf0fcde5 --- ipld/merkledag/merkledag.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 923a3d715..2084c200e 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -257,10 +257,10 @@ type nodePromise struct { // from its internal channels, subsequent calls will return the // cached node. type NodeGetter interface { - Get() (*Node, error) + Get(context.Context) (*Node, error) } -func (np *nodePromise) Get() (*Node, error) { +func (np *nodePromise) Get(ctx context.Context) (*Node, error) { if np.cache != nil { return np.cache, nil } @@ -270,6 +270,8 @@ func (np *nodePromise) Get() (*Node, error) { np.cache = blk case <-np.ctx.Done(): return nil, np.ctx.Err() + case <-ctx.Done(): + return nil, ctx.Err() } return np.cache, nil } From 0a9440d3e581ee06d77c42de4099d1a9c7313d9f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 23 Mar 2015 14:01:42 -0700 Subject: [PATCH 0818/3817] fix context respect through fuse reading This commit was moved from ipfs/go-unixfs@c10d3ecb851ce30e3745b1cf0fd030eafbc64d01 --- unixfs/io/dagreader.go | 16 +++++++++++----- unixfs/mod/dagmodifier.go | 31 ++++++++++++++++++++++++++----- unixfs/tar/reader.go | 2 +- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 64dfff127..6bb9eb406 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -100,12 +100,13 @@ func newDataFileReader(ctx context.Context, n *mdag.Node, pb *ftpb.Data, serv md // precalcNextBuf follows the next link in line and loads it from the DAGService, // setting the next buffer to read from -func (dr *DagReader) precalcNextBuf() error { +func (dr *DagReader) precalcNextBuf(ctx context.Context) error { dr.buf.Close() // Just to make sure if dr.linkPosition >= len(dr.promises) { return io.EOF } - nxt, err := dr.promises[dr.linkPosition].Get() + + nxt, err := dr.promises[dr.linkPosition].Get(ctx) if err != nil { return err } @@ -141,6 +142,11 @@ func (dr *DagReader) Size() int64 { // Read reads data from the DAG structured file func (dr *DagReader) Read(b []byte) (int, error) { + return dr.CtxReadFull(dr.ctx, b) +} + +// CtxReadFull reads data from the DAG structured file +func (dr *DagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { // If no cached buffer, load one total := 0 for { @@ -161,7 +167,7 @@ func (dr *DagReader) Read(b []byte) (int, error) { } // Otherwise, load up the next block - err = dr.precalcNextBuf() + err = dr.precalcNextBuf(ctx) if err != nil { return total, err } @@ -183,7 +189,7 @@ func (dr *DagReader) WriteTo(w io.Writer) (int64, error) { } // Otherwise, load up the next block - err = dr.precalcNextBuf() + err = dr.precalcNextBuf(dr.ctx) if err != nil { if err == io.EOF { return total, nil @@ -239,7 +245,7 @@ func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { } // start sub-block request - err := dr.precalcNextBuf() + err := dr.precalcNextBuf(dr.ctx) if err != nil { return 0, err } diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index cd207ed91..133af2227 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -315,32 +315,53 @@ func (dm *DagModifier) appendData(node *mdag.Node, blks <-chan []byte) (*mdag.No // Read data from this dag starting at the current offset func (dm *DagModifier) Read(b []byte) (int, error) { - err := dm.Sync() + err := dm.readPrep() if err != nil { return 0, err } + n, err := dm.read.Read(b) + dm.curWrOff += uint64(n) + return n, err +} + +func (dm *DagModifier) readPrep() error { + err := dm.Sync() + if err != nil { + return err + } + if dm.read == nil { ctx, cancel := context.WithCancel(dm.ctx) dr, err := uio.NewDagReader(ctx, dm.curNode, dm.dagserv) if err != nil { - return 0, err + return err } i, err := dr.Seek(int64(dm.curWrOff), os.SEEK_SET) if err != nil { - return 0, err + return err } if i != int64(dm.curWrOff) { - return 0, ErrSeekFail + return ErrSeekFail } dm.readCancel = cancel dm.read = dr } - n, err := dm.read.Read(b) + return nil +} + +// Read data from this dag starting at the current offset +func (dm *DagModifier) CtxReadFull(ctx context.Context, b []byte) (int, error) { + err := dm.readPrep() + if err != nil { + return 0, err + } + + n, err := dm.read.CtxReadFull(ctx, b) dm.curWrOff += uint64(n) return n, err } diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index aa15c823a..26aa772ce 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -90,7 +90,7 @@ func (r *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { defer cancel() for i, ng := range r.dag.GetDAG(ctx, dagnode) { - childNode, err := ng.Get() + childNode, err := ng.Get(ctx) if err != nil { r.emitError(err) return From 2fd43020760efcd53ebf7280efae7b4344ac9ec9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 23 Mar 2015 14:01:42 -0700 Subject: [PATCH 0819/3817] fix context respect through fuse reading This commit was moved from ipfs/go-ipfs-pinner@9a408897768570663f8239392aa1e1fcae3b000d --- pinning/pinner/pin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 5f726a457..6ec299388 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -177,7 +177,7 @@ func (p *pinner) pinLinks(node *mdag.Node) error { defer cancel() for _, ng := range p.dserv.GetDAG(ctx, node) { - subnode, err := ng.Get() + subnode, err := ng.Get(ctx) if err != nil { // TODO: Maybe just log and continue? return err From 98f0b71ac64f2ef5539425f0d779e6d6ee484e98 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 30 Mar 2015 07:53:14 -0700 Subject: [PATCH 0820/3817] reduce dht bandwidth consumption Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@5617e89d16e2a32c3c852df4520bb5236953c6a3 --- routing/dht/handlers.go | 2 +- routing/dht/records.go | 9 +++++++-- routing/dht/routing.go | 8 ++++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 62b22c5ca..550825245 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -226,5 +226,5 @@ func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.M dht.providers.AddProvider(ctx, key, p) } - return pmes, nil // send back same msg as confirmation. + return nil, nil } diff --git a/routing/dht/records.go b/routing/dht/records.go index e327ed171..9c90b9b7d 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -31,6 +31,10 @@ func (dht *IpfsDHT) getPublicKeyOnline(ctx context.Context, p peer.ID) (ci.PubKe ctxT, cancelFunc := ctxutil.WithDeadlineFraction(ctx, 0.3) defer cancelFunc() if pk, err := dht.getPublicKeyFromNode(ctx, p); err == nil { + err := dht.peerstore.AddPubKey(p, pk) + if err != nil { + return pk, err + } return pk, nil } @@ -38,7 +42,7 @@ func (dht *IpfsDHT) getPublicKeyOnline(ctx context.Context, p peer.ID) (ci.PubKe log.Debugf("pk for %s not in peerstore, and peer failed. trying dht.", p) pkkey := KeyForPublicKey(p) - // ok, try the node itself. if they're overwhelmed or slow we can move on. + // ok, now try the dht. Anyone who has previously fetched the key should have it val, err := dht.GetValue(ctxT, pkkey) if err != nil { log.Warning("Failed to find requested public key.") @@ -50,7 +54,8 @@ func (dht *IpfsDHT) getPublicKeyOnline(ctx context.Context, p peer.ID) (ci.PubKe log.Debugf("Failed to unmarshal public key: %s", err) return nil, err } - return pk, nil + + return pk, dht.peerstore.AddPubKey(p, pk) } func (dht *IpfsDHT) getPublicKeyFromNode(ctx context.Context, p peer.ID) (ci.PubKey, error) { diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 28ee8f03a..5ff279104 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -91,7 +91,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { } // get closest peers in the routing table - rtp := dht.routingTable.ListPeers() + rtp := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) log.Debugf("peers in rt: %s", len(rtp), rtp) if len(rtp) == 0 { log.Warning("No peers from routing table!") @@ -256,7 +256,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, co return &dhtQueryResult{closerPeers: clpeers}, nil }) - peers := dht.routingTable.ListPeers() + peers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) _, err := query.Run(ctx, peers) if err != nil { log.Debugf("Query error: %s", err) @@ -276,7 +276,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, er return pi, nil } - peers := dht.routingTable.ListPeers() + peers := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue) if len(peers) == 0 { return peer.PeerInfo{}, errors.Wrap(kb.ErrLookupFailure) } @@ -342,7 +342,7 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< peerchan := make(chan peer.PeerInfo, asyncQueryBuffer) peersSeen := peer.Set{} - peers := dht.routingTable.ListPeers() + peers := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue) if len(peers) == 0 { return nil, errors.Wrap(kb.ErrLookupFailure) } From 9d4619d0b0ebe4b53b4755fc33d6ddf1b18eb667 Mon Sep 17 00:00:00 2001 From: Ho-Sheng Hsiao Date: Mon, 30 Mar 2015 20:04:32 -0700 Subject: [PATCH 0821/3817] Reorged imports from jbenet/go-ipfs to ipfs/go-ipfs - Modified Godeps/Godeps.json by hand - [TEST] Updated welcome docs hash to sharness - [TEST] Updated contact doc - [TEST] disabled breaking test (t0080-repo refs local) This commit was moved from ipfs/go-ipfs-routing@a1c3b44afad271d35e7c27687a7b7a7f36bd7d89 --- routing/dht/dht.go | 30 ++++++++++++++--------------- routing/dht/dht_bootstrap.go | 12 ++++++------ routing/dht/dht_net.go | 12 ++++++------ routing/dht/dht_test.go | 26 ++++++++++++------------- routing/dht/diag.go | 4 ++-- routing/dht/ext_test.go | 24 +++++++++++------------ routing/dht/handlers.go | 12 ++++++------ routing/dht/lookup.go | 12 ++++++------ routing/dht/notif.go | 4 ++-- routing/dht/pb/dht.pb.go | 2 +- routing/dht/pb/message.go | 10 +++++----- routing/dht/providers.go | 8 ++++---- routing/dht/providers_test.go | 6 +++--- routing/dht/query.go | 24 +++++++++++------------ routing/dht/records.go | 14 +++++++------- routing/dht/routing.go | 22 ++++++++++----------- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 4 ++-- routing/kbucket/table_test.go | 4 ++-- routing/kbucket/util.go | 6 +++--- routing/keyspace/xor.go | 2 +- routing/keyspace/xor_test.go | 2 +- routing/mock/centralized_client.go | 14 +++++++------- routing/mock/centralized_server.go | 10 +++++----- routing/mock/centralized_test.go | 10 +++++----- routing/mock/dht.go | 12 ++++++------ routing/mock/interface.go | 14 +++++++------- routing/offline/offline.go | 20 +++++++++---------- routing/record/record.go | 10 +++++----- routing/record/validation.go | 6 +++--- routing/routing.go | 6 +++--- routing/supernode/client.go | 20 +++++++++---------- routing/supernode/proxy/loopback.go | 12 ++++++------ routing/supernode/proxy/standard.go | 20 +++++++++---------- routing/supernode/server.go | 18 ++++++++--------- routing/supernode/server_test.go | 6 +++--- 37 files changed, 211 insertions(+), 211 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 974d87b58..d34f37a56 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -10,21 +10,21 @@ import ( "sync" "time" - ci "github.com/jbenet/go-ipfs/p2p/crypto" - host "github.com/jbenet/go-ipfs/p2p/host" - peer "github.com/jbenet/go-ipfs/p2p/peer" - protocol "github.com/jbenet/go-ipfs/p2p/protocol" - routing "github.com/jbenet/go-ipfs/routing" - pb "github.com/jbenet/go-ipfs/routing/dht/pb" - kb "github.com/jbenet/go-ipfs/routing/kbucket" - record "github.com/jbenet/go-ipfs/routing/record" - "github.com/jbenet/go-ipfs/thirdparty/eventlog" - u "github.com/jbenet/go-ipfs/util" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + ci "github.com/ipfs/go-ipfs/p2p/crypto" + host "github.com/ipfs/go-ipfs/p2p/host" + peer "github.com/ipfs/go-ipfs/p2p/peer" + protocol "github.com/ipfs/go-ipfs/p2p/protocol" + routing "github.com/ipfs/go-ipfs/routing" + pb "github.com/ipfs/go-ipfs/routing/dht/pb" + kb "github.com/ipfs/go-ipfs/routing/kbucket" + record "github.com/ipfs/go-ipfs/routing/record" + "github.com/ipfs/go-ipfs/thirdparty/eventlog" + u "github.com/ipfs/go-ipfs/util" + + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + ctxgroup "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) var log = eventlog.Logger("dht") diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index d5bbfa860..4a07d9f0b 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -8,13 +8,13 @@ import ( "sync" "time" - peer "github.com/jbenet/go-ipfs/p2p/peer" - routing "github.com/jbenet/go-ipfs/routing" - u "github.com/jbenet/go-ipfs/util" + peer "github.com/ipfs/go-ipfs/p2p/peer" + routing "github.com/ipfs/go-ipfs/routing" + u "github.com/ipfs/go-ipfs/util" - goprocess "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" - periodicproc "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/periodic" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" + periodicproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/periodic" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) // BootstrapConfig specifies parameters used bootstrapping the DHT. diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index d84e8e008..8bba4c41d 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -4,13 +4,13 @@ import ( "errors" "time" - inet "github.com/jbenet/go-ipfs/p2p/net" - peer "github.com/jbenet/go-ipfs/p2p/peer" - pb "github.com/jbenet/go-ipfs/routing/dht/pb" - ctxutil "github.com/jbenet/go-ipfs/util/ctx" + inet "github.com/ipfs/go-ipfs/p2p/net" + peer "github.com/ipfs/go-ipfs/p2p/peer" + pb "github.com/ipfs/go-ipfs/routing/dht/pb" + ctxutil "github.com/ipfs/go-ipfs/util/ctx" - ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) // handleNewStream implements the inet.StreamHandler diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 4b48ccc65..fdd334d59 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -9,19 +9,19 @@ import ( "testing" "time" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - - peer "github.com/jbenet/go-ipfs/p2p/peer" - netutil "github.com/jbenet/go-ipfs/p2p/test/util" - routing "github.com/jbenet/go-ipfs/routing" - record "github.com/jbenet/go-ipfs/routing/record" - u "github.com/jbenet/go-ipfs/util" - - ci "github.com/jbenet/go-ipfs/util/testutil/ci" - travisci "github.com/jbenet/go-ipfs/util/testutil/ci/travis" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + + peer "github.com/ipfs/go-ipfs/p2p/peer" + netutil "github.com/ipfs/go-ipfs/p2p/test/util" + routing "github.com/ipfs/go-ipfs/routing" + record "github.com/ipfs/go-ipfs/routing/record" + u "github.com/ipfs/go-ipfs/util" + + ci "github.com/ipfs/go-ipfs/util/testutil/ci" + travisci "github.com/ipfs/go-ipfs/util/testutil/ci/travis" ) var testCaseValues = map[u.Key][]byte{} diff --git a/routing/dht/diag.go b/routing/dht/diag.go index 79b4709e9..a7a632c3e 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - peer "github.com/jbenet/go-ipfs/p2p/peer" + peer "github.com/ipfs/go-ipfs/p2p/peer" ) type connDiagInfo struct { @@ -31,7 +31,7 @@ func (di *diagInfo) Marshal() []byte { func (dht *IpfsDHT) getDiagInfo() *diagInfo { di := new(diagInfo) - di.CodeVersion = "github.com/jbenet/go-ipfs" + di.CodeVersion = "github.com/ipfs/go-ipfs" di.ID = dht.self di.LifeSpan = time.Since(dht.birth) di.Keys = nil // Currently no way to query datastore diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 539d55cca..efe62cd7c 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -7,18 +7,18 @@ import ( "testing" "time" - inet "github.com/jbenet/go-ipfs/p2p/net" - mocknet "github.com/jbenet/go-ipfs/p2p/net/mock" - peer "github.com/jbenet/go-ipfs/p2p/peer" - routing "github.com/jbenet/go-ipfs/routing" - pb "github.com/jbenet/go-ipfs/routing/dht/pb" - record "github.com/jbenet/go-ipfs/routing/record" - u "github.com/jbenet/go-ipfs/util" - - ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + inet "github.com/ipfs/go-ipfs/p2p/net" + mocknet "github.com/ipfs/go-ipfs/p2p/net/mock" + peer "github.com/ipfs/go-ipfs/p2p/peer" + routing "github.com/ipfs/go-ipfs/routing" + pb "github.com/ipfs/go-ipfs/routing/dht/pb" + record "github.com/ipfs/go-ipfs/routing/record" + u "github.com/ipfs/go-ipfs/util" + + ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 62b22c5ca..ca81552af 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -4,12 +4,12 @@ import ( "errors" "fmt" - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - peer "github.com/jbenet/go-ipfs/p2p/peer" - pb "github.com/jbenet/go-ipfs/routing/dht/pb" - u "github.com/jbenet/go-ipfs/util" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + peer "github.com/ipfs/go-ipfs/p2p/peer" + pb "github.com/ipfs/go-ipfs/routing/dht/pb" + u "github.com/ipfs/go-ipfs/util" ) // The number of closer peers to send on requests. diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 1f01a082a..76671657a 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -1,12 +1,12 @@ package dht import ( - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - notif "github.com/jbenet/go-ipfs/notifications" - peer "github.com/jbenet/go-ipfs/p2p/peer" - kb "github.com/jbenet/go-ipfs/routing/kbucket" - u "github.com/jbenet/go-ipfs/util" - pset "github.com/jbenet/go-ipfs/util/peerset" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + notif "github.com/ipfs/go-ipfs/notifications" + peer "github.com/ipfs/go-ipfs/p2p/peer" + kb "github.com/ipfs/go-ipfs/routing/kbucket" + u "github.com/ipfs/go-ipfs/util" + pset "github.com/ipfs/go-ipfs/util/peerset" ) // Required in order for proper JSON marshaling diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 4af2fc978..70144481a 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -1,9 +1,9 @@ package dht import ( - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - inet "github.com/jbenet/go-ipfs/p2p/net" + inet "github.com/ipfs/go-ipfs/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/dht.pb.go b/routing/dht/pb/dht.pb.go index 09db3d5f9..78532e95b 100644 --- a/routing/dht/pb/dht.pb.go +++ b/routing/dht/pb/dht.pb.go @@ -14,7 +14,7 @@ It has these top-level messages: */ package dht_pb -import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import json "encoding/json" import math "math" diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index ce6af1459..10279abd2 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -1,12 +1,12 @@ package dht_pb import ( - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - inet "github.com/jbenet/go-ipfs/p2p/net" - peer "github.com/jbenet/go-ipfs/p2p/peer" - eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" - util "github.com/jbenet/go-ipfs/util" + inet "github.com/ipfs/go-ipfs/p2p/net" + peer "github.com/ipfs/go-ipfs/p2p/peer" + eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" + util "github.com/ipfs/go-ipfs/util" ) var log = eventlog.Logger("dht.pb") diff --git a/routing/dht/providers.go b/routing/dht/providers.go index d8e0d910d..c62aee97c 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -3,11 +3,11 @@ package dht import ( "time" - ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" - peer "github.com/jbenet/go-ipfs/p2p/peer" - u "github.com/jbenet/go-ipfs/util" + ctxgroup "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" + peer "github.com/ipfs/go-ipfs/p2p/peer" + u "github.com/ipfs/go-ipfs/util" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) type providerInfo struct { diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 121992ede..159634a80 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -3,10 +3,10 @@ package dht import ( "testing" - peer "github.com/jbenet/go-ipfs/p2p/peer" - u "github.com/jbenet/go-ipfs/util" + peer "github.com/ipfs/go-ipfs/p2p/peer" + u "github.com/ipfs/go-ipfs/util" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) func TestProviderManager(t *testing.T) { diff --git a/routing/dht/query.go b/routing/dht/query.go index 3687bc859..d833b126c 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -3,18 +3,18 @@ package dht import ( "sync" - notif "github.com/jbenet/go-ipfs/notifications" - peer "github.com/jbenet/go-ipfs/p2p/peer" - queue "github.com/jbenet/go-ipfs/p2p/peer/queue" - "github.com/jbenet/go-ipfs/routing" - eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" - u "github.com/jbenet/go-ipfs/util" - pset "github.com/jbenet/go-ipfs/util/peerset" - todoctr "github.com/jbenet/go-ipfs/util/todocounter" - - process "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" - ctxproc "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + notif "github.com/ipfs/go-ipfs/notifications" + peer "github.com/ipfs/go-ipfs/p2p/peer" + queue "github.com/ipfs/go-ipfs/p2p/peer/queue" + "github.com/ipfs/go-ipfs/routing" + eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" + u "github.com/ipfs/go-ipfs/util" + pset "github.com/ipfs/go-ipfs/util/peerset" + todoctr "github.com/ipfs/go-ipfs/util/todocounter" + + process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" + ctxproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) var maxQueryConcurrency = AlphaValue diff --git a/routing/dht/records.go b/routing/dht/records.go index e327ed171..cbe8c5803 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -3,13 +3,13 @@ package dht import ( "fmt" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - ci "github.com/jbenet/go-ipfs/p2p/crypto" - peer "github.com/jbenet/go-ipfs/p2p/peer" - pb "github.com/jbenet/go-ipfs/routing/dht/pb" - record "github.com/jbenet/go-ipfs/routing/record" - u "github.com/jbenet/go-ipfs/util" - ctxutil "github.com/jbenet/go-ipfs/util/ctx" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + ci "github.com/ipfs/go-ipfs/p2p/crypto" + peer "github.com/ipfs/go-ipfs/p2p/peer" + pb "github.com/ipfs/go-ipfs/routing/dht/pb" + record "github.com/ipfs/go-ipfs/routing/record" + u "github.com/ipfs/go-ipfs/util" + ctxutil "github.com/ipfs/go-ipfs/util/ctx" ) // KeyForPublicKey returns the key used to retrieve public keys diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 28ee8f03a..ab493696b 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -4,17 +4,17 @@ import ( "sync" "time" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - notif "github.com/jbenet/go-ipfs/notifications" - inet "github.com/jbenet/go-ipfs/p2p/net" - peer "github.com/jbenet/go-ipfs/p2p/peer" - "github.com/jbenet/go-ipfs/routing" - pb "github.com/jbenet/go-ipfs/routing/dht/pb" - kb "github.com/jbenet/go-ipfs/routing/kbucket" - record "github.com/jbenet/go-ipfs/routing/record" - u "github.com/jbenet/go-ipfs/util" - errors "github.com/jbenet/go-ipfs/util/debugerror" - pset "github.com/jbenet/go-ipfs/util/peerset" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + notif "github.com/ipfs/go-ipfs/notifications" + inet "github.com/ipfs/go-ipfs/p2p/net" + peer "github.com/ipfs/go-ipfs/p2p/peer" + "github.com/ipfs/go-ipfs/routing" + pb "github.com/ipfs/go-ipfs/routing/dht/pb" + kb "github.com/ipfs/go-ipfs/routing/kbucket" + record "github.com/ipfs/go-ipfs/routing/record" + u "github.com/ipfs/go-ipfs/util" + errors "github.com/ipfs/go-ipfs/util/debugerror" + pset "github.com/ipfs/go-ipfs/util/peerset" ) // asyncQueryBuffer is the size of buffered channels in async queries. This diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index d551cf819..35ceed385 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "github.com/jbenet/go-ipfs/p2p/peer" + peer "github.com/ipfs/go-ipfs/p2p/peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index 7995b39ed..31c64591a 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "github.com/jbenet/go-ipfs/p2p/peer" + peer "github.com/ipfs/go-ipfs/p2p/peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 7b10d8daf..87d9c9e3f 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,8 +7,8 @@ import ( "sync" "time" - peer "github.com/jbenet/go-ipfs/p2p/peer" - u "github.com/jbenet/go-ipfs/util" + peer "github.com/ipfs/go-ipfs/p2p/peer" + u "github.com/ipfs/go-ipfs/util" ) var log = u.Logger("table") diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 670342b67..e5b01cc72 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -5,9 +5,9 @@ import ( "testing" "time" - tu "github.com/jbenet/go-ipfs/util/testutil" + tu "github.com/ipfs/go-ipfs/util/testutil" - peer "github.com/jbenet/go-ipfs/p2p/peer" + peer "github.com/ipfs/go-ipfs/p2p/peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 80c08de9e..e7c56f868 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -5,9 +5,9 @@ import ( "crypto/sha256" "errors" - peer "github.com/jbenet/go-ipfs/p2p/peer" - ks "github.com/jbenet/go-ipfs/routing/keyspace" - u "github.com/jbenet/go-ipfs/util" + peer "github.com/ipfs/go-ipfs/p2p/peer" + ks "github.com/ipfs/go-ipfs/routing/keyspace" + u "github.com/ipfs/go-ipfs/util" ) // Returned if a routing table query returns no results. This is NOT expected diff --git a/routing/keyspace/xor.go b/routing/keyspace/xor.go index 7159f2cad..8fae7744f 100644 --- a/routing/keyspace/xor.go +++ b/routing/keyspace/xor.go @@ -5,7 +5,7 @@ import ( "crypto/sha256" "math/big" - u "github.com/jbenet/go-ipfs/util" + u "github.com/ipfs/go-ipfs/util" ) // XORKeySpace is a KeySpace which: diff --git a/routing/keyspace/xor_test.go b/routing/keyspace/xor_test.go index 8db4b926c..f90e8a5f9 100644 --- a/routing/keyspace/xor_test.go +++ b/routing/keyspace/xor_test.go @@ -5,7 +5,7 @@ import ( "math/big" "testing" - u "github.com/jbenet/go-ipfs/util" + u "github.com/ipfs/go-ipfs/util" ) func TestPrefixLen(t *testing.T) { diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 6a550bfaa..7b04c9762 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -4,13 +4,13 @@ import ( "errors" "time" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - peer "github.com/jbenet/go-ipfs/p2p/peer" - routing "github.com/jbenet/go-ipfs/routing" - u "github.com/jbenet/go-ipfs/util" - "github.com/jbenet/go-ipfs/util/testutil" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + peer "github.com/ipfs/go-ipfs/p2p/peer" + routing "github.com/ipfs/go-ipfs/routing" + u "github.com/ipfs/go-ipfs/util" + "github.com/ipfs/go-ipfs/util/testutil" ) var log = u.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index a0ee67e6d..da2cedf48 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -5,11 +5,11 @@ import ( "sync" "time" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - peer "github.com/jbenet/go-ipfs/p2p/peer" - u "github.com/jbenet/go-ipfs/util" - "github.com/jbenet/go-ipfs/util/testutil" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + peer "github.com/ipfs/go-ipfs/p2p/peer" + u "github.com/ipfs/go-ipfs/util" + "github.com/ipfs/go-ipfs/util/testutil" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index d77144a73..38b58cb64 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -4,11 +4,11 @@ import ( "testing" "time" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - peer "github.com/jbenet/go-ipfs/p2p/peer" - delay "github.com/jbenet/go-ipfs/thirdparty/delay" - u "github.com/jbenet/go-ipfs/util" - "github.com/jbenet/go-ipfs/util/testutil" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + peer "github.com/ipfs/go-ipfs/p2p/peer" + delay "github.com/ipfs/go-ipfs/thirdparty/delay" + u "github.com/ipfs/go-ipfs/util" + "github.com/ipfs/go-ipfs/util/testutil" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 0fe30b119..f4b2b4900 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -1,12 +1,12 @@ package mockrouting import ( - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - sync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - mocknet "github.com/jbenet/go-ipfs/p2p/net/mock" - dht "github.com/jbenet/go-ipfs/routing/dht" - "github.com/jbenet/go-ipfs/util/testutil" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + mocknet "github.com/ipfs/go-ipfs/p2p/net/mock" + dht "github.com/ipfs/go-ipfs/routing/dht" + "github.com/ipfs/go-ipfs/util/testutil" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 34092dfda..df29fdbb9 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -5,13 +5,13 @@ package mockrouting import ( - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - peer "github.com/jbenet/go-ipfs/p2p/peer" - routing "github.com/jbenet/go-ipfs/routing" - delay "github.com/jbenet/go-ipfs/thirdparty/delay" - u "github.com/jbenet/go-ipfs/util" - "github.com/jbenet/go-ipfs/util/testutil" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + peer "github.com/ipfs/go-ipfs/p2p/peer" + routing "github.com/ipfs/go-ipfs/routing" + delay "github.com/ipfs/go-ipfs/thirdparty/delay" + u "github.com/ipfs/go-ipfs/util" + "github.com/ipfs/go-ipfs/util/testutil" ) // Server provides mockrouting Clients diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 15049f16d..0e5bed248 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -4,16 +4,16 @@ import ( "errors" "time" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - ci "github.com/jbenet/go-ipfs/p2p/crypto" - "github.com/jbenet/go-ipfs/p2p/peer" - routing "github.com/jbenet/go-ipfs/routing" - pb "github.com/jbenet/go-ipfs/routing/dht/pb" - record "github.com/jbenet/go-ipfs/routing/record" - eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" - u "github.com/jbenet/go-ipfs/util" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + ci "github.com/ipfs/go-ipfs/p2p/crypto" + "github.com/ipfs/go-ipfs/p2p/peer" + routing "github.com/ipfs/go-ipfs/routing" + pb "github.com/ipfs/go-ipfs/routing/dht/pb" + record "github.com/ipfs/go-ipfs/routing/record" + eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" + u "github.com/ipfs/go-ipfs/util" ) var log = eventlog.Logger("offlinerouting") diff --git a/routing/record/record.go b/routing/record/record.go index c5575a86f..2d9ab18e2 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -3,12 +3,12 @@ package record import ( "bytes" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - ci "github.com/jbenet/go-ipfs/p2p/crypto" - pb "github.com/jbenet/go-ipfs/routing/dht/pb" - eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" - u "github.com/jbenet/go-ipfs/util" + ci "github.com/ipfs/go-ipfs/p2p/crypto" + pb "github.com/ipfs/go-ipfs/routing/dht/pb" + eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" + u "github.com/ipfs/go-ipfs/util" ) var log = eventlog.Logger("routing/record") diff --git a/routing/record/validation.go b/routing/record/validation.go index 380bdea4c..8d2657fe2 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -5,9 +5,9 @@ import ( "errors" "strings" - ci "github.com/jbenet/go-ipfs/p2p/crypto" - pb "github.com/jbenet/go-ipfs/routing/dht/pb" - u "github.com/jbenet/go-ipfs/util" + ci "github.com/ipfs/go-ipfs/p2p/crypto" + pb "github.com/ipfs/go-ipfs/routing/dht/pb" + u "github.com/ipfs/go-ipfs/util" ) // ValidatorFunc is a function that is called to validate a given diff --git a/routing/routing.go b/routing/routing.go index be400a520..bb11265e2 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -5,9 +5,9 @@ import ( "errors" "time" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - peer "github.com/jbenet/go-ipfs/p2p/peer" - u "github.com/jbenet/go-ipfs/util" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + peer "github.com/ipfs/go-ipfs/p2p/peer" + u "github.com/ipfs/go-ipfs/util" ) // ErrNotFound is returned when a search fails to find anything diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 09fa90ef6..fa47e2e80 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -4,16 +4,16 @@ import ( "bytes" "time" - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - "github.com/jbenet/go-ipfs/p2p/host" - peer "github.com/jbenet/go-ipfs/p2p/peer" - routing "github.com/jbenet/go-ipfs/routing" - pb "github.com/jbenet/go-ipfs/routing/dht/pb" - proxy "github.com/jbenet/go-ipfs/routing/supernode/proxy" - eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" - u "github.com/jbenet/go-ipfs/util" - errors "github.com/jbenet/go-ipfs/util/debugerror" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "github.com/ipfs/go-ipfs/p2p/host" + peer "github.com/ipfs/go-ipfs/p2p/peer" + routing "github.com/ipfs/go-ipfs/routing" + pb "github.com/ipfs/go-ipfs/routing/dht/pb" + proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" + eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" + u "github.com/ipfs/go-ipfs/util" + errors "github.com/ipfs/go-ipfs/util/debugerror" ) var log = eventlog.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index df9dc1d72..5d46fb4e1 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -1,12 +1,12 @@ package proxy import ( - ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - inet "github.com/jbenet/go-ipfs/p2p/net" - peer "github.com/jbenet/go-ipfs/p2p/peer" - dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" - errors "github.com/jbenet/go-ipfs/util/debugerror" + ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + inet "github.com/ipfs/go-ipfs/p2p/net" + peer "github.com/ipfs/go-ipfs/p2p/peer" + dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" + errors "github.com/ipfs/go-ipfs/util/debugerror" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 92a6d9f01..0a3d9e1b7 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -1,16 +1,16 @@ package proxy import ( - ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - host "github.com/jbenet/go-ipfs/p2p/host" - inet "github.com/jbenet/go-ipfs/p2p/net" - peer "github.com/jbenet/go-ipfs/p2p/peer" - dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" - kbucket "github.com/jbenet/go-ipfs/routing/kbucket" - eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" - "github.com/jbenet/go-ipfs/util" - errors "github.com/jbenet/go-ipfs/util/debugerror" + ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + host "github.com/ipfs/go-ipfs/p2p/host" + inet "github.com/ipfs/go-ipfs/p2p/net" + peer "github.com/ipfs/go-ipfs/p2p/peer" + dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" + kbucket "github.com/ipfs/go-ipfs/routing/kbucket" + eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" + "github.com/ipfs/go-ipfs/util" + errors "github.com/ipfs/go-ipfs/util/debugerror" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 1132039a1..44ef349d4 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -3,15 +3,15 @@ package supernode import ( "fmt" - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - datastore "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - peer "github.com/jbenet/go-ipfs/p2p/peer" - dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" - record "github.com/jbenet/go-ipfs/routing/record" - proxy "github.com/jbenet/go-ipfs/routing/supernode/proxy" - util "github.com/jbenet/go-ipfs/util" - errors "github.com/jbenet/go-ipfs/util/debugerror" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + peer "github.com/ipfs/go-ipfs/p2p/peer" + dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" + record "github.com/ipfs/go-ipfs/routing/record" + proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" + util "github.com/ipfs/go-ipfs/util" + errors "github.com/ipfs/go-ipfs/util/debugerror" ) // Server handles routing queries using a database backend diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 0d2d00318..2bd0fa15b 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,9 +3,9 @@ package supernode import ( "testing" - datastore "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb" - "github.com/jbenet/go-ipfs/util" + datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" + "github.com/ipfs/go-ipfs/util" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From d700d615bc1de3e55614df6b050aa3b42a60acbf Mon Sep 17 00:00:00 2001 From: Ho-Sheng Hsiao Date: Mon, 30 Mar 2015 20:04:32 -0700 Subject: [PATCH 0822/3817] Reorged imports from jbenet/go-ipfs to ipfs/go-ipfs - Modified Godeps/Godeps.json by hand - [TEST] Updated welcome docs hash to sharness - [TEST] Updated contact doc - [TEST] disabled breaking test (t0080-repo refs local) This commit was moved from ipfs/go-namesys@8dceca380deb818da346cc61dca2a74e1f893085 --- namesys/dns.go | 10 +++++----- namesys/interface.go | 6 +++--- namesys/internal/pb/namesys.pb.go | 2 +- namesys/namesys.go | 8 ++++---- namesys/proquint.go | 6 +++--- namesys/publisher.go | 24 ++++++++++++------------ namesys/resolve_test.go | 8 ++++---- namesys/routing.go | 14 +++++++------- 8 files changed, 39 insertions(+), 39 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 9efc72348..003e6f0f0 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -3,12 +3,12 @@ package namesys import ( "net" - b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" - isd "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-is-domain" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" + isd "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-is-domain" + mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - u "github.com/jbenet/go-ipfs/util" + u "github.com/ipfs/go-ipfs/util" ) // DNSResolver implements a Resolver on DNS domains diff --git a/namesys/interface.go b/namesys/interface.go index 10e4fb89f..39a5c6e73 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -4,9 +4,9 @@ package namesys import ( "errors" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - ci "github.com/jbenet/go-ipfs/p2p/crypto" - u "github.com/jbenet/go-ipfs/util" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + ci "github.com/ipfs/go-ipfs/p2p/crypto" + u "github.com/ipfs/go-ipfs/util" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/internal/pb/namesys.pb.go b/namesys/internal/pb/namesys.pb.go index 68b93a2c4..637d02306 100644 --- a/namesys/internal/pb/namesys.pb.go +++ b/namesys/internal/pb/namesys.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package namesys_pb -import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. diff --git a/namesys/namesys.go b/namesys/namesys.go index d4cc03964..ed2ccb255 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -1,10 +1,10 @@ package namesys import ( - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - ci "github.com/jbenet/go-ipfs/p2p/crypto" - routing "github.com/jbenet/go-ipfs/routing" - u "github.com/jbenet/go-ipfs/util" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + ci "github.com/ipfs/go-ipfs/p2p/crypto" + routing "github.com/ipfs/go-ipfs/routing" + u "github.com/ipfs/go-ipfs/util" ) // ipnsNameSystem implements IPNS naming. diff --git a/namesys/proquint.go b/namesys/proquint.go index b43f7a2a6..e3e2cc281 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -3,9 +3,9 @@ package namesys import ( "errors" - proquint "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/bren2010/proquint" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - u "github.com/jbenet/go-ipfs/util" + proquint "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/bren2010/proquint" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + u "github.com/ipfs/go-ipfs/util" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index c96915307..eb3838eef 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -6,18 +6,18 @@ import ( "fmt" "time" - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - - dag "github.com/jbenet/go-ipfs/merkledag" - pb "github.com/jbenet/go-ipfs/namesys/internal/pb" - ci "github.com/jbenet/go-ipfs/p2p/crypto" - pin "github.com/jbenet/go-ipfs/pin" - routing "github.com/jbenet/go-ipfs/routing" - record "github.com/jbenet/go-ipfs/routing/record" - ft "github.com/jbenet/go-ipfs/unixfs" - u "github.com/jbenet/go-ipfs/util" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + + dag "github.com/ipfs/go-ipfs/merkledag" + pb "github.com/ipfs/go-ipfs/namesys/internal/pb" + ci "github.com/ipfs/go-ipfs/p2p/crypto" + pin "github.com/ipfs/go-ipfs/pin" + routing "github.com/ipfs/go-ipfs/routing" + record "github.com/ipfs/go-ipfs/routing/record" + ft "github.com/ipfs/go-ipfs/unixfs" + u "github.com/ipfs/go-ipfs/util" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 3b8bb7072..e9cd01760 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -3,10 +3,10 @@ package namesys import ( "testing" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - mockrouting "github.com/jbenet/go-ipfs/routing/mock" - u "github.com/jbenet/go-ipfs/util" - testutil "github.com/jbenet/go-ipfs/util/testutil" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + mockrouting "github.com/ipfs/go-ipfs/routing/mock" + u "github.com/ipfs/go-ipfs/util" + testutil "github.com/ipfs/go-ipfs/util/testutil" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index ed8690079..476303cbf 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -3,13 +3,13 @@ package namesys import ( "fmt" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - pb "github.com/jbenet/go-ipfs/namesys/internal/pb" - ci "github.com/jbenet/go-ipfs/p2p/crypto" - routing "github.com/jbenet/go-ipfs/routing" - u "github.com/jbenet/go-ipfs/util" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + pb "github.com/ipfs/go-ipfs/namesys/internal/pb" + ci "github.com/ipfs/go-ipfs/p2p/crypto" + routing "github.com/ipfs/go-ipfs/routing" + u "github.com/ipfs/go-ipfs/util" ) var log = u.Logger("namesys") From 2b3aa7cb917d6fd18f0dcbccba8879b1b552df1d Mon Sep 17 00:00:00 2001 From: Ho-Sheng Hsiao Date: Mon, 30 Mar 2015 20:04:32 -0700 Subject: [PATCH 0823/3817] Reorged imports from jbenet/go-ipfs to ipfs/go-ipfs - Modified Godeps/Godeps.json by hand - [TEST] Updated welcome docs hash to sharness - [TEST] Updated contact doc - [TEST] disabled breaking test (t0080-repo refs local) This commit was moved from ipfs/go-merkledag@c31a3536651245f5ed23de1726357c3dcdd9c5e5 --- ipld/merkledag/coding.go | 6 ++--- ipld/merkledag/internal/pb/merkledag.pb.go | 6 ++--- .../merkledag/internal/pb/merkledagpb_test.go | 8 +++--- ipld/merkledag/merkledag.go | 8 +++--- ipld/merkledag/merkledag_test.go | 26 +++++++++---------- ipld/merkledag/node.go | 4 +-- ipld/merkledag/test/utils.go | 12 ++++----- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/traverse/traverse_test.go | 2 +- 9 files changed, 37 insertions(+), 37 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 9a1c23152..6e108c2cf 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -4,10 +4,10 @@ import ( "fmt" "sort" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - pb "github.com/jbenet/go-ipfs/merkledag/internal/pb" - u "github.com/jbenet/go-ipfs/util" + pb "github.com/ipfs/go-ipfs/merkledag/internal/pb" + u "github.com/ipfs/go-ipfs/util" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/internal/pb/merkledag.pb.go b/ipld/merkledag/internal/pb/merkledag.pb.go index 78d5bcb94..18e480897 100644 --- a/ipld/merkledag/internal/pb/merkledag.pb.go +++ b/ipld/merkledag/internal/pb/merkledag.pb.go @@ -14,14 +14,14 @@ */ package merkledag_pb -import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math "math" // discarding unused import gogoproto "code.google.com/p/gogoprotobuf/gogoproto/gogo.pb" import io "io" import fmt "fmt" -import code_google_com_p_gogoprotobuf_proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import code_google_com_p_gogoprotobuf_proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import fmt1 "fmt" import strings "strings" @@ -29,7 +29,7 @@ import reflect "reflect" import fmt2 "fmt" import strings1 "strings" -import code_google_com_p_gogoprotobuf_proto1 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import code_google_com_p_gogoprotobuf_proto1 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import sort "sort" import strconv "strconv" import reflect1 "reflect" diff --git a/ipld/merkledag/internal/pb/merkledagpb_test.go b/ipld/merkledag/internal/pb/merkledagpb_test.go index 4ed02436e..00c05e4c9 100644 --- a/ipld/merkledag/internal/pb/merkledagpb_test.go +++ b/ipld/merkledag/internal/pb/merkledagpb_test.go @@ -17,7 +17,7 @@ package merkledag_pb import testing "testing" import math_rand "math/rand" import time "time" -import code_google_com_p_gogoprotobuf_proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import code_google_com_p_gogoprotobuf_proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import testing1 "testing" import math_rand1 "math/rand" import time1 "time" @@ -25,7 +25,7 @@ import encoding_json "encoding/json" import testing2 "testing" import math_rand2 "math/rand" import time2 "time" -import code_google_com_p_gogoprotobuf_proto1 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import code_google_com_p_gogoprotobuf_proto1 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math_rand3 "math/rand" import time3 "time" import testing3 "testing" @@ -33,7 +33,7 @@ import fmt "fmt" import math_rand4 "math/rand" import time4 "time" import testing4 "testing" -import code_google_com_p_gogoprotobuf_proto2 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import code_google_com_p_gogoprotobuf_proto2 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math_rand5 "math/rand" import time5 "time" import testing5 "testing" @@ -42,7 +42,7 @@ import go_parser "go/parser" import math_rand6 "math/rand" import time6 "time" import testing6 "testing" -import code_google_com_p_gogoprotobuf_proto3 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import code_google_com_p_gogoprotobuf_proto3 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" func TestPBLinkProto(t *testing.T) { popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 2084c200e..c085e782e 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -6,10 +6,10 @@ import ( "sync" "time" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - blocks "github.com/jbenet/go-ipfs/blocks" - bserv "github.com/jbenet/go-ipfs/blockservice" - u "github.com/jbenet/go-ipfs/util" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + blocks "github.com/ipfs/go-ipfs/blocks" + bserv "github.com/ipfs/go-ipfs/blockservice" + u "github.com/ipfs/go-ipfs/util" ) var log = u.Logger("merkledag") diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 4d13e19e0..f46698223 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -8,19 +8,19 @@ import ( "sync" "testing" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - bstore "github.com/jbenet/go-ipfs/blocks/blockstore" - blockservice "github.com/jbenet/go-ipfs/blockservice" - bserv "github.com/jbenet/go-ipfs/blockservice" - offline "github.com/jbenet/go-ipfs/exchange/offline" - imp "github.com/jbenet/go-ipfs/importer" - chunk "github.com/jbenet/go-ipfs/importer/chunk" - . "github.com/jbenet/go-ipfs/merkledag" - "github.com/jbenet/go-ipfs/pin" - uio "github.com/jbenet/go-ipfs/unixfs/io" - u "github.com/jbenet/go-ipfs/util" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + bstore "github.com/ipfs/go-ipfs/blocks/blockstore" + blockservice "github.com/ipfs/go-ipfs/blockservice" + bserv "github.com/ipfs/go-ipfs/blockservice" + offline "github.com/ipfs/go-ipfs/exchange/offline" + imp "github.com/ipfs/go-ipfs/importer" + chunk "github.com/ipfs/go-ipfs/importer/chunk" + . "github.com/ipfs/go-ipfs/merkledag" + "github.com/ipfs/go-ipfs/pin" + uio "github.com/ipfs/go-ipfs/unixfs/io" + u "github.com/ipfs/go-ipfs/util" ) type dagservAndPinner struct { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 848b228dc..4aab2420b 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -3,8 +3,8 @@ package merkledag import ( "fmt" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - u "github.com/jbenet/go-ipfs/util" + mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + u "github.com/ipfs/go-ipfs/util" ) // NodeMap maps u.Keys to Nodes. diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index cc373a8fd..07a1bd1ca 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -3,12 +3,12 @@ package mdutils import ( "testing" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - "github.com/jbenet/go-ipfs/blocks/blockstore" - bsrv "github.com/jbenet/go-ipfs/blockservice" - "github.com/jbenet/go-ipfs/exchange/offline" - dag "github.com/jbenet/go-ipfs/merkledag" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/ipfs/go-ipfs/blocks/blockstore" + bsrv "github.com/ipfs/go-ipfs/blockservice" + "github.com/ipfs/go-ipfs/exchange/offline" + dag "github.com/ipfs/go-ipfs/merkledag" ) func Mock(t testing.TB) dag.DAGService { diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index 688c409f2..73f032319 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -4,7 +4,7 @@ package traverse import ( "errors" - mdag "github.com/jbenet/go-ipfs/merkledag" + mdag "github.com/ipfs/go-ipfs/merkledag" ) // Order is an identifier for traversal algorithm orders diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 912ce34d7..12aa9fd21 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - mdag "github.com/jbenet/go-ipfs/merkledag" + mdag "github.com/ipfs/go-ipfs/merkledag" ) func TestDFSPreNoSkip(t *testing.T) { From b17de07f0fa7d1cae6a8090ff346fa56744d673e Mon Sep 17 00:00:00 2001 From: Ho-Sheng Hsiao Date: Mon, 30 Mar 2015 20:04:32 -0700 Subject: [PATCH 0824/3817] Reorged imports from jbenet/go-ipfs to ipfs/go-ipfs - Modified Godeps/Godeps.json by hand - [TEST] Updated welcome docs hash to sharness - [TEST] Updated contact doc - [TEST] disabled breaking test (t0080-repo refs local) This commit was moved from ipfs/go-path@3e65a45a48f2e12446261b65d0962496d1145b19 --- path/path.go | 2 +- path/resolver.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/path/path.go b/path/path.go index c76844b1e..ea83cd12c 100644 --- a/path/path.go +++ b/path/path.go @@ -4,7 +4,7 @@ import ( "path" "strings" - u "github.com/jbenet/go-ipfs/util" + u "github.com/ipfs/go-ipfs/util" ) // TODO: debate making this a private struct wrapped in a public interface diff --git a/path/resolver.go b/path/resolver.go index 863ae9d3c..f329ddebd 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -4,9 +4,9 @@ package path import ( "fmt" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - merkledag "github.com/jbenet/go-ipfs/merkledag" - u "github.com/jbenet/go-ipfs/util" + mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + merkledag "github.com/ipfs/go-ipfs/merkledag" + u "github.com/ipfs/go-ipfs/util" ) var log = u.Logger("path") From ded381838a9ef0b21211bc81da2121109fca201c Mon Sep 17 00:00:00 2001 From: Ho-Sheng Hsiao Date: Mon, 30 Mar 2015 20:04:32 -0700 Subject: [PATCH 0825/3817] Reorged imports from jbenet/go-ipfs to ipfs/go-ipfs - Modified Godeps/Godeps.json by hand - [TEST] Updated welcome docs hash to sharness - [TEST] Updated contact doc - [TEST] disabled breaking test (t0080-repo refs local) This commit was moved from ipfs/go-unixfs@f95a76d681e4f93a558b5eccd66bc4f7f5c267e9 --- unixfs/format.go | 4 ++-- unixfs/format_test.go | 4 ++-- unixfs/io/dagreader.go | 10 +++++----- unixfs/io/dirbuilder.go | 6 +++--- unixfs/mod/dagmodifier.go | 24 ++++++++++++------------ unixfs/mod/dagmodifier_test.go | 32 ++++++++++++++++---------------- unixfs/pb/unixfs.pb.go | 2 +- unixfs/tar/reader.go | 12 ++++++------ 8 files changed, 47 insertions(+), 47 deletions(-) diff --git a/unixfs/format.go b/unixfs/format.go index 61bb2ec9e..21ba46f74 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -5,8 +5,8 @@ package unixfs import ( "errors" - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - pb "github.com/jbenet/go-ipfs/unixfs/pb" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + pb "github.com/ipfs/go-ipfs/unixfs/pb" ) const ( diff --git a/unixfs/format_test.go b/unixfs/format_test.go index b15ed0789..4d4175545 100644 --- a/unixfs/format_test.go +++ b/unixfs/format_test.go @@ -3,8 +3,8 @@ package unixfs import ( "testing" - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - pb "github.com/jbenet/go-ipfs/unixfs/pb" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + pb "github.com/ipfs/go-ipfs/unixfs/pb" ) func TestFSNode(t *testing.T) { diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 6bb9eb406..da9b3ee24 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -7,11 +7,11 @@ import ( "io" "os" - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - mdag "github.com/jbenet/go-ipfs/merkledag" - ft "github.com/jbenet/go-ipfs/unixfs" - ftpb "github.com/jbenet/go-ipfs/unixfs/pb" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + mdag "github.com/ipfs/go-ipfs/merkledag" + ft "github.com/ipfs/go-ipfs/unixfs" + ftpb "github.com/ipfs/go-ipfs/unixfs/pb" ) var ErrIsDir = errors.New("this dag node is a directory") diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 9597db3d1..ecdbfc623 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -1,9 +1,9 @@ package io import ( - mdag "github.com/jbenet/go-ipfs/merkledag" - format "github.com/jbenet/go-ipfs/unixfs" - u "github.com/jbenet/go-ipfs/util" + mdag "github.com/ipfs/go-ipfs/merkledag" + format "github.com/ipfs/go-ipfs/unixfs" + u "github.com/ipfs/go-ipfs/util" ) type directoryBuilder struct { diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 133af2227..fe04ece20 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -6,18 +6,18 @@ import ( "io" "os" - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - - chunk "github.com/jbenet/go-ipfs/importer/chunk" - help "github.com/jbenet/go-ipfs/importer/helpers" - trickle "github.com/jbenet/go-ipfs/importer/trickle" - mdag "github.com/jbenet/go-ipfs/merkledag" - pin "github.com/jbenet/go-ipfs/pin" - ft "github.com/jbenet/go-ipfs/unixfs" - uio "github.com/jbenet/go-ipfs/unixfs/io" - u "github.com/jbenet/go-ipfs/util" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + + chunk "github.com/ipfs/go-ipfs/importer/chunk" + help "github.com/ipfs/go-ipfs/importer/helpers" + trickle "github.com/ipfs/go-ipfs/importer/trickle" + mdag "github.com/ipfs/go-ipfs/merkledag" + pin "github.com/ipfs/go-ipfs/pin" + ft "github.com/ipfs/go-ipfs/unixfs" + uio "github.com/ipfs/go-ipfs/unixfs/io" + u "github.com/ipfs/go-ipfs/util" ) var ErrSeekFail = errors.New("failed to seek properly") diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 9f8050972..2eaa04265 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -8,22 +8,22 @@ import ( "os" "testing" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - "github.com/jbenet/go-ipfs/blocks/blockstore" - bs "github.com/jbenet/go-ipfs/blockservice" - "github.com/jbenet/go-ipfs/exchange/offline" - imp "github.com/jbenet/go-ipfs/importer" - "github.com/jbenet/go-ipfs/importer/chunk" - h "github.com/jbenet/go-ipfs/importer/helpers" - trickle "github.com/jbenet/go-ipfs/importer/trickle" - mdag "github.com/jbenet/go-ipfs/merkledag" - pin "github.com/jbenet/go-ipfs/pin" - ft "github.com/jbenet/go-ipfs/unixfs" - uio "github.com/jbenet/go-ipfs/unixfs/io" - u "github.com/jbenet/go-ipfs/util" - - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/ipfs/go-ipfs/blocks/blockstore" + bs "github.com/ipfs/go-ipfs/blockservice" + "github.com/ipfs/go-ipfs/exchange/offline" + imp "github.com/ipfs/go-ipfs/importer" + "github.com/ipfs/go-ipfs/importer/chunk" + h "github.com/ipfs/go-ipfs/importer/helpers" + trickle "github.com/ipfs/go-ipfs/importer/trickle" + mdag "github.com/ipfs/go-ipfs/merkledag" + pin "github.com/ipfs/go-ipfs/pin" + ft "github.com/ipfs/go-ipfs/unixfs" + uio "github.com/ipfs/go-ipfs/unixfs/io" + u "github.com/ipfs/go-ipfs/util" + + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) func getMockDagServ(t testing.TB) (mdag.DAGService, pin.ManualPinner) { diff --git a/unixfs/pb/unixfs.pb.go b/unixfs/pb/unixfs.pb.go index 19eb9d8ee..aac37040e 100644 --- a/unixfs/pb/unixfs.pb.go +++ b/unixfs/pb/unixfs.pb.go @@ -14,7 +14,7 @@ It has these top-level messages: */ package unixfs_pb -import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index 26aa772ce..9509b2d6d 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -8,13 +8,13 @@ import ( gopath "path" "time" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - mdag "github.com/jbenet/go-ipfs/merkledag" - path "github.com/jbenet/go-ipfs/path" - uio "github.com/jbenet/go-ipfs/unixfs/io" - upb "github.com/jbenet/go-ipfs/unixfs/pb" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + mdag "github.com/ipfs/go-ipfs/merkledag" + path "github.com/ipfs/go-ipfs/path" + uio "github.com/ipfs/go-ipfs/unixfs/io" + upb "github.com/ipfs/go-ipfs/unixfs/pb" - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ) type Reader struct { From ebbc4dffcb479727ab738e9a132ecc056ade5059 Mon Sep 17 00:00:00 2001 From: Ho-Sheng Hsiao Date: Mon, 30 Mar 2015 20:04:32 -0700 Subject: [PATCH 0826/3817] Reorged imports from jbenet/go-ipfs to ipfs/go-ipfs - Modified Godeps/Godeps.json by hand - [TEST] Updated welcome docs hash to sharness - [TEST] Updated contact doc - [TEST] disabled breaking test (t0080-repo refs local) This commit was moved from ipfs/go-ipfs-pinner@5f805306f75bfee35634324c14935178921e3c36 --- pinning/pinner/indirect.go | 6 +++--- pinning/pinner/pin.go | 12 ++++++------ pinning/pinner/pin_test.go | 14 +++++++------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index 09decbb25..46350a4e0 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -1,9 +1,9 @@ package pin import ( - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - "github.com/jbenet/go-ipfs/blocks/set" - "github.com/jbenet/go-ipfs/util" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + "github.com/ipfs/go-ipfs/blocks/set" + "github.com/ipfs/go-ipfs/util" ) type indirectPin struct { diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 6ec299388..49a587133 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -9,12 +9,12 @@ import ( "sync" "time" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - nsds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - "github.com/jbenet/go-ipfs/blocks/set" - mdag "github.com/jbenet/go-ipfs/merkledag" - "github.com/jbenet/go-ipfs/util" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + nsds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "github.com/ipfs/go-ipfs/blocks/set" + mdag "github.com/ipfs/go-ipfs/merkledag" + "github.com/ipfs/go-ipfs/util" ) var log = util.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 12db39b29..f31e1fef9 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -3,13 +3,13 @@ package pin import ( "testing" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - "github.com/jbenet/go-ipfs/blocks/blockstore" - bs "github.com/jbenet/go-ipfs/blockservice" - "github.com/jbenet/go-ipfs/exchange/offline" - mdag "github.com/jbenet/go-ipfs/merkledag" - "github.com/jbenet/go-ipfs/util" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/ipfs/go-ipfs/blocks/blockstore" + bs "github.com/ipfs/go-ipfs/blockservice" + "github.com/ipfs/go-ipfs/exchange/offline" + mdag "github.com/ipfs/go-ipfs/merkledag" + "github.com/ipfs/go-ipfs/util" ) func randNode() (*mdag.Node, util.Key) { From cc79c7904b7772f050c596eeb554f270d33ec0e7 Mon Sep 17 00:00:00 2001 From: Ho-Sheng Hsiao Date: Mon, 30 Mar 2015 20:04:32 -0700 Subject: [PATCH 0827/3817] Reorged imports from jbenet/go-ipfs to ipfs/go-ipfs - Modified Godeps/Godeps.json by hand - [TEST] Updated welcome docs hash to sharness - [TEST] Updated contact doc - [TEST] disabled breaking test (t0080-repo refs local) This commit was moved from ipfs/go-ipfs-blockstore@f5caff3e9e5ee7a061693014f528891b60632284 --- blockstore/blockstore.go | 16 ++++++++-------- blockstore/blockstore_test.go | 12 ++++++------ blockstore/write_cache.go | 8 ++++---- blockstore/write_cache_test.go | 8 ++++---- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 7e929af10..7f3d4d7c8 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -5,14 +5,14 @@ package blockstore import ( "errors" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dsns "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" - dsq "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - blocks "github.com/jbenet/go-ipfs/blocks" - eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog" - u "github.com/jbenet/go-ipfs/util" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dsns "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" + dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" + mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + blocks "github.com/ipfs/go-ipfs/blocks" + eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" + u "github.com/ipfs/go-ipfs/util" ) var log = eventlog.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 51f5aad11..10844354a 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -5,13 +5,13 @@ import ( "fmt" "testing" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dsq "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" - ds_sync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" + ds_sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - blocks "github.com/jbenet/go-ipfs/blocks" - u "github.com/jbenet/go-ipfs/util" + blocks "github.com/ipfs/go-ipfs/blocks" + u "github.com/ipfs/go-ipfs/util" ) // TODO(brian): TestGetReturnsNil diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index a1399fcc6..b0ea4abc5 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -1,10 +1,10 @@ package blockstore import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - "github.com/jbenet/go-ipfs/blocks" - u "github.com/jbenet/go-ipfs/util" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "github.com/ipfs/go-ipfs/blocks" + u "github.com/ipfs/go-ipfs/util" ) // WriteCached returns a blockstore that caches up to |size| unique writes (bs.Put). diff --git a/blockstore/write_cache_test.go b/blockstore/write_cache_test.go index b20188e29..cf8150ba6 100644 --- a/blockstore/write_cache_test.go +++ b/blockstore/write_cache_test.go @@ -3,10 +3,10 @@ package blockstore import ( "testing" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dsq "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" - syncds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - "github.com/jbenet/go-ipfs/blocks" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" + syncds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/ipfs/go-ipfs/blocks" ) func TestReturnsErrorWhenSizeNegative(t *testing.T) { From 2b73cdd1ee9985126481877ead65c15d40da50f3 Mon Sep 17 00:00:00 2001 From: Ho-Sheng Hsiao Date: Mon, 30 Mar 2015 20:04:32 -0700 Subject: [PATCH 0828/3817] Reorged imports from jbenet/go-ipfs to ipfs/go-ipfs - Modified Godeps/Godeps.json by hand - [TEST] Updated welcome docs hash to sharness - [TEST] Updated contact doc - [TEST] disabled breaking test (t0080-repo refs local) This commit was moved from ipfs/go-blockservice@3ae534c87da6d837832772212e5881c6a643d0b3 --- blockservice/blocks_test.go | 16 ++++++++-------- blockservice/blockservice.go | 12 ++++++------ blockservice/mock.go | 8 ++++---- blockservice/worker/bench/main.go | 16 ++++++++-------- blockservice/worker/bench_worker_test.go | 10 +++++----- blockservice/worker/worker.go | 12 ++++++------ blockservice/worker/worker_test.go | 2 +- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index be1ca6059..b38d52af9 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -5,14 +5,14 @@ import ( "testing" "time" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - blocks "github.com/jbenet/go-ipfs/blocks" - blockstore "github.com/jbenet/go-ipfs/blocks/blockstore" - blocksutil "github.com/jbenet/go-ipfs/blocks/blocksutil" - offline "github.com/jbenet/go-ipfs/exchange/offline" - u "github.com/jbenet/go-ipfs/util" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + blocks "github.com/ipfs/go-ipfs/blocks" + blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" + blocksutil "github.com/ipfs/go-ipfs/blocks/blocksutil" + offline "github.com/ipfs/go-ipfs/exchange/offline" + u "github.com/ipfs/go-ipfs/util" ) func TestBlocks(t *testing.T) { diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index ee84d79d6..46612be26 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -7,12 +7,12 @@ import ( "errors" "fmt" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - blocks "github.com/jbenet/go-ipfs/blocks" - "github.com/jbenet/go-ipfs/blocks/blockstore" - worker "github.com/jbenet/go-ipfs/blockservice/worker" - exchange "github.com/jbenet/go-ipfs/exchange" - u "github.com/jbenet/go-ipfs/util" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + blocks "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-ipfs/blocks/blockstore" + worker "github.com/ipfs/go-ipfs/blockservice/worker" + exchange "github.com/ipfs/go-ipfs/exchange" + u "github.com/ipfs/go-ipfs/util" ) var wc = worker.Config{ diff --git a/blockservice/mock.go b/blockservice/mock.go index 541efe696..45b440b3e 100644 --- a/blockservice/mock.go +++ b/blockservice/mock.go @@ -3,10 +3,10 @@ package blockservice import ( "testing" - bitswap "github.com/jbenet/go-ipfs/exchange/bitswap" - tn "github.com/jbenet/go-ipfs/exchange/bitswap/testnet" - mockrouting "github.com/jbenet/go-ipfs/routing/mock" - delay "github.com/jbenet/go-ipfs/thirdparty/delay" + bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" + tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" + mockrouting "github.com/ipfs/go-ipfs/routing/mock" + delay "github.com/ipfs/go-ipfs/thirdparty/delay" ) // Mocks returns |n| connected mock Blockservices diff --git a/blockservice/worker/bench/main.go b/blockservice/worker/bench/main.go index 5d85de27f..82c3dee13 100644 --- a/blockservice/worker/bench/main.go +++ b/blockservice/worker/bench/main.go @@ -6,14 +6,14 @@ import ( "testing" "time" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - ds_sync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - blocks "github.com/jbenet/go-ipfs/blocks" - blockstore "github.com/jbenet/go-ipfs/blocks/blockstore" - worker "github.com/jbenet/go-ipfs/blockservice/worker" - "github.com/jbenet/go-ipfs/exchange/offline" - "github.com/jbenet/go-ipfs/thirdparty/delay" - "github.com/jbenet/go-ipfs/util/datastore2" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds_sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + blocks "github.com/ipfs/go-ipfs/blocks" + blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" + worker "github.com/ipfs/go-ipfs/blockservice/worker" + "github.com/ipfs/go-ipfs/exchange/offline" + "github.com/ipfs/go-ipfs/thirdparty/delay" + "github.com/ipfs/go-ipfs/util/datastore2" ) const kEstRoutingDelay = time.Second diff --git a/blockservice/worker/bench_worker_test.go b/blockservice/worker/bench_worker_test.go index aab910b64..a5e34a107 100644 --- a/blockservice/worker/bench_worker_test.go +++ b/blockservice/worker/bench_worker_test.go @@ -3,11 +3,11 @@ package worker import ( "testing" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - blocks "github.com/jbenet/go-ipfs/blocks" - blockstore "github.com/jbenet/go-ipfs/blocks/blockstore" - "github.com/jbenet/go-ipfs/exchange/offline" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + blocks "github.com/ipfs/go-ipfs/blocks" + blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" + "github.com/ipfs/go-ipfs/exchange/offline" ) func BenchmarkHandle10KBlocks(b *testing.B) { diff --git a/blockservice/worker/worker.go b/blockservice/worker/worker.go index ee45d32ad..5e57d8429 100644 --- a/blockservice/worker/worker.go +++ b/blockservice/worker/worker.go @@ -6,12 +6,12 @@ import ( "errors" "time" - process "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" - ratelimit "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit" - blocks "github.com/jbenet/go-ipfs/blocks" - exchange "github.com/jbenet/go-ipfs/exchange" - waitable "github.com/jbenet/go-ipfs/thirdparty/waitable" - util "github.com/jbenet/go-ipfs/util" + process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" + ratelimit "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit" + blocks "github.com/ipfs/go-ipfs/blocks" + exchange "github.com/ipfs/go-ipfs/exchange" + waitable "github.com/ipfs/go-ipfs/thirdparty/waitable" + util "github.com/ipfs/go-ipfs/util" ) var log = util.Logger("blockservice") diff --git a/blockservice/worker/worker_test.go b/blockservice/worker/worker_test.go index 4cbc9b2cc..2b6a2d16f 100644 --- a/blockservice/worker/worker_test.go +++ b/blockservice/worker/worker_test.go @@ -1,7 +1,7 @@ package worker import ( - blocks "github.com/jbenet/go-ipfs/blocks" + blocks "github.com/ipfs/go-ipfs/blocks" "testing" ) From 510cb9910313855d06cdd1307c76cb77970a05c3 Mon Sep 17 00:00:00 2001 From: Ho-Sheng Hsiao Date: Mon, 30 Mar 2015 20:04:32 -0700 Subject: [PATCH 0829/3817] Reorged imports from jbenet/go-ipfs to ipfs/go-ipfs - Modified Godeps/Godeps.json by hand - [TEST] Updated welcome docs hash to sharness - [TEST] Updated contact doc - [TEST] disabled breaking test (t0080-repo refs local) This commit was moved from ipfs/go-ipfs-exchange-offline@b1ec37fcd5148bc8f88a24665722e5a515dd100c --- exchange/offline/offline.go | 10 +++++----- exchange/offline/offline_test.go | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index fe8fa723c..6b6ffc838 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -3,11 +3,11 @@ package offline import ( - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - blocks "github.com/jbenet/go-ipfs/blocks" - "github.com/jbenet/go-ipfs/blocks/blockstore" - exchange "github.com/jbenet/go-ipfs/exchange" - u "github.com/jbenet/go-ipfs/util" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + blocks "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-ipfs/blocks/blockstore" + exchange "github.com/ipfs/go-ipfs/exchange" + u "github.com/ipfs/go-ipfs/util" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 20588bde8..1bbbf3f10 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -3,13 +3,13 @@ package offline import ( "testing" - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - ds_sync "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - blocks "github.com/jbenet/go-ipfs/blocks" - "github.com/jbenet/go-ipfs/blocks/blockstore" - "github.com/jbenet/go-ipfs/blocks/blocksutil" - u "github.com/jbenet/go-ipfs/util" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds_sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + blocks "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-ipfs/blocks/blockstore" + "github.com/ipfs/go-ipfs/blocks/blocksutil" + u "github.com/ipfs/go-ipfs/util" ) func TestBlockReturnsErr(t *testing.T) { From 8369c0acec49767695ee0d12402131604fafa994 Mon Sep 17 00:00:00 2001 From: Ho-Sheng Hsiao Date: Mon, 30 Mar 2015 20:04:32 -0700 Subject: [PATCH 0830/3817] Reorged imports from jbenet/go-ipfs to ipfs/go-ipfs - Modified Godeps/Godeps.json by hand - [TEST] Updated welcome docs hash to sharness - [TEST] Updated contact doc - [TEST] disabled breaking test (t0080-repo refs local) This commit was moved from ipfs/go-ipfs-exchange-interface@f190e6acb7c313eff6ac060b2b69b9bb639bfa22 --- exchange/interface.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index c07d2a471..3ccda263c 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -4,9 +4,9 @@ package exchange import ( "io" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - blocks "github.com/jbenet/go-ipfs/blocks" - u "github.com/jbenet/go-ipfs/util" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + blocks "github.com/ipfs/go-ipfs/blocks" + u "github.com/ipfs/go-ipfs/util" ) // Any type that implements exchange.Interface may be used as an IPFS block From 76d8561da5263e5f56108e15bee12f79c2d96315 Mon Sep 17 00:00:00 2001 From: Ho-Sheng Hsiao Date: Mon, 30 Mar 2015 20:04:32 -0700 Subject: [PATCH 0831/3817] Reorged imports from jbenet/go-ipfs to ipfs/go-ipfs - Modified Godeps/Godeps.json by hand - [TEST] Updated welcome docs hash to sharness - [TEST] Updated contact doc - [TEST] disabled breaking test (t0080-repo refs local) This commit was moved from ipfs/go-ipfs-chunker@1cfd9abd7be865cf1bb84c46065d6d25ef0a0390 --- chunker/splitting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 73fe49db1..999ed367f 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -4,7 +4,7 @@ package chunk import ( "io" - "github.com/jbenet/go-ipfs/util" + "github.com/ipfs/go-ipfs/util" ) var log = util.Logger("chunk") From 8b4ab284f5ecc95073c84093c2baca554889b5e0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 30 Mar 2015 10:48:29 -0700 Subject: [PATCH 0832/3817] cache public keys and use better method for fetching This commit was moved from ipfs/go-ipfs-routing@fdf7dbf691802a2781bfea4a263cde68aa210bbf --- routing/dht/records.go | 5 ++--- routing/routing.go | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/routing/dht/records.go b/routing/dht/records.go index b1a66299a..3d563ab57 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -18,7 +18,7 @@ func KeyForPublicKey(id peer.ID) u.Key { return u.Key("/pk/" + string(id)) } -func (dht *IpfsDHT) getPublicKeyOnline(ctx context.Context, p peer.ID) (ci.PubKey, error) { +func (dht *IpfsDHT) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey, error) { log.Debugf("getPublicKey for: %s", p) // check locally. @@ -42,7 +42,6 @@ func (dht *IpfsDHT) getPublicKeyOnline(ctx context.Context, p peer.ID) (ci.PubKe log.Debugf("pk for %s not in peerstore, and peer failed. trying dht.", p) pkkey := KeyForPublicKey(p) - // ok, now try the dht. Anyone who has previously fetched the key should have it val, err := dht.GetValue(ctxT, pkkey) if err != nil { log.Warning("Failed to find requested public key.") @@ -132,7 +131,7 @@ func (dht *IpfsDHT) verifyRecordOnline(ctx context.Context, r *pb.Record) error if len(r.Signature) > 0 { // get the public key, search for it if necessary. p := peer.ID(r.GetAuthor()) - pk, err := dht.getPublicKeyOnline(ctx, p) + pk, err := dht.GetPublicKey(ctx, p) if err != nil { return err } diff --git a/routing/routing.go b/routing/routing.go index bb11265e2..b85b25e22 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -6,6 +6,7 @@ import ( "time" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + ci "github.com/ipfs/go-ipfs/p2p/crypto" peer "github.com/ipfs/go-ipfs/p2p/peer" u "github.com/ipfs/go-ipfs/util" ) @@ -46,3 +47,7 @@ type IpfsRouting interface { // TODO expose io.Closer or plain-old Close error } + +type PubKeyFetcher interface { + GetPublicKey(context.Context, peer.ID) (ci.PubKey, error) +} From 70c5898809dffd9407e25bd5d324510ff3ccf0a6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 30 Mar 2015 10:48:29 -0700 Subject: [PATCH 0833/3817] cache public keys and use better method for fetching This commit was moved from ipfs/go-namesys@fe3edb2b000ab4e8ea2b90fba00f83e7f6fd5bd2 --- namesys/routing.go | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/namesys/routing.go b/namesys/routing.go index 476303cbf..67d96f1f6 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -8,6 +8,7 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" pb "github.com/ipfs/go-ipfs/namesys/internal/pb" ci "github.com/ipfs/go-ipfs/p2p/crypto" + peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" ) @@ -65,24 +66,38 @@ func (r *routingResolver) Resolve(ctx context.Context, name string) (u.Key, erro // name should be a public key retrievable from ipfs // /ipfs/ - key := u.Key("/pk/" + string(hash)) - pkval, err := r.routing.GetValue(ctx, key) - if err != nil { - log.Warning("RoutingResolve PubKey Get failed.") - return "", err - } + var pubkey ci.PubKey + if dht, ok := r.routing.(routing.PubKeyFetcher); ok { + // If we have a DHT as our routing system, use optimized fetcher + pk, err := dht.GetPublicKey(ctx, peer.ID(hash)) + if err != nil { + log.Warning("RoutingResolve PubKey Get failed.") + return "", err + } + pubkey = pk + } else { + key := u.Key("/pk/" + string(hash)) + pkval, err := r.routing.GetValue(ctx, key) + if err != nil { + log.Warning("RoutingResolve PubKey Get failed.") + return "", err + } - // get PublicKey from node.Data - pk, err := ci.UnmarshalPublicKey(pkval) - if err != nil { - return "", err + // get PublicKey from node.Data + pk, err := ci.UnmarshalPublicKey(pkval) + if err != nil { + return "", err + } + + pubkey = pk } - hsh, _ := pk.Hash() + + hsh, _ := pubkey.Hash() log.Debugf("pk hash = %s", u.Key(hsh)) // check sig with pk - if ok, err := pk.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok { - return "", fmt.Errorf("Invalid value. Not signed by PrivateKey corresponding to %v", pk) + if ok, err := pubkey.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok { + return "", fmt.Errorf("Invalid value. Not signed by PrivateKey corresponding to %v", pubkey) } // ok sig checks out. this is a valid name. From b2e37220b21a91b757ce896c9046c8c398becf43 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 31 Mar 2015 14:41:53 -0700 Subject: [PATCH 0834/3817] Address comments from PR This commit was moved from ipfs/go-ipfs-routing@0bd6ae459ad7b3966928649ccc4ed48e6e3a5016 --- routing/dht/records.go | 12 +++--------- routing/routing.go | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/routing/dht/records.go b/routing/dht/records.go index 3d563ab57..973ceca96 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -6,18 +6,12 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ci "github.com/ipfs/go-ipfs/p2p/crypto" peer "github.com/ipfs/go-ipfs/p2p/peer" + routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - u "github.com/ipfs/go-ipfs/util" ctxutil "github.com/ipfs/go-ipfs/util/ctx" ) -// KeyForPublicKey returns the key used to retrieve public keys -// from the dht. -func KeyForPublicKey(id peer.ID) u.Key { - return u.Key("/pk/" + string(id)) -} - func (dht *IpfsDHT) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey, error) { log.Debugf("getPublicKey for: %s", p) @@ -40,7 +34,7 @@ func (dht *IpfsDHT) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey, err // last ditch effort: let's try the dht. log.Debugf("pk for %s not in peerstore, and peer failed. trying dht.", p) - pkkey := KeyForPublicKey(p) + pkkey := routing.KeyForPublicKey(p) val, err := dht.GetValue(ctxT, pkkey) if err != nil { @@ -65,7 +59,7 @@ func (dht *IpfsDHT) getPublicKeyFromNode(ctx context.Context, p peer.ID) (ci.Pub return pk, nil } - pkkey := KeyForPublicKey(p) + pkkey := routing.KeyForPublicKey(p) pmes, err := dht.getValueSingle(ctx, p, pkkey) if err != nil { return nil, err diff --git a/routing/routing.go b/routing/routing.go index b85b25e22..c94d813ae 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -51,3 +51,25 @@ type IpfsRouting interface { type PubKeyFetcher interface { GetPublicKey(context.Context, peer.ID) (ci.PubKey, error) } + +// KeyForPublicKey returns the key used to retrieve public keys +// from the dht. +func KeyForPublicKey(id peer.ID) u.Key { + return u.Key("/pk/" + string(id)) +} + +func GetPublicKey(r IpfsRouting, ctx context.Context, pkhash []byte) (ci.PubKey, error) { + if dht, ok := r.(PubKeyFetcher); ok { + // If we have a DHT as our routing system, use optimized fetcher + return dht.GetPublicKey(ctx, peer.ID(pkhash)) + } else { + key := u.Key("/pk/" + string(pkhash)) + pkval, err := r.GetValue(ctx, key) + if err != nil { + return nil, err + } + + // get PublicKey from node.Data + return ci.UnmarshalPublicKey(pkval) + } +} From 61f08d3d8cfae53d303c383df454b0de491e507a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 31 Mar 2015 14:41:53 -0700 Subject: [PATCH 0835/3817] Address comments from PR This commit was moved from ipfs/go-namesys@3ec169a26cac0c5fedd8a0b0ac90ad1c07b1e8a6 --- namesys/routing.go | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/namesys/routing.go b/namesys/routing.go index 67d96f1f6..4a9756d00 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -7,8 +7,6 @@ import ( mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" pb "github.com/ipfs/go-ipfs/namesys/internal/pb" - ci "github.com/ipfs/go-ipfs/p2p/crypto" - peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" ) @@ -65,31 +63,9 @@ func (r *routingResolver) Resolve(ctx context.Context, name string) (u.Key, erro } // name should be a public key retrievable from ipfs - // /ipfs/ - var pubkey ci.PubKey - if dht, ok := r.routing.(routing.PubKeyFetcher); ok { - // If we have a DHT as our routing system, use optimized fetcher - pk, err := dht.GetPublicKey(ctx, peer.ID(hash)) - if err != nil { - log.Warning("RoutingResolve PubKey Get failed.") - return "", err - } - pubkey = pk - } else { - key := u.Key("/pk/" + string(hash)) - pkval, err := r.routing.GetValue(ctx, key) - if err != nil { - log.Warning("RoutingResolve PubKey Get failed.") - return "", err - } - - // get PublicKey from node.Data - pk, err := ci.UnmarshalPublicKey(pkval) - if err != nil { - return "", err - } - - pubkey = pk + pubkey, err := routing.GetPublicKey(r.routing, ctx, hash) + if err != nil { + return "", err } hsh, _ := pubkey.Hash() From ed7a93f2fe91685f1fef159174fadc03208a08fc Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 2 Apr 2015 03:02:12 -0700 Subject: [PATCH 0836/3817] dht-handlers-log-keys This commit was moved from ipfs/go-ipfs-routing@15854ee4ee12b5d3860b23a113cae38319d349a6 --- routing/dht/handlers.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 95404efb1..926dc75f7 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -10,6 +10,7 @@ import ( peer "github.com/ipfs/go-ipfs/p2p/peer" pb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" + lgbl "github.com/ipfs/go-ipfs/util/eventlog/loggables" ) // The number of closer peers to send on requests. @@ -157,9 +158,13 @@ func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.ID, pmes *pb.Mess } func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { - defer log.EventBegin(ctx, "handleGetProviders", p).Done() + lm := make(lgbl.DeferredMap) + lm["peer"] = func() interface{} { return p.Pretty() } + defer log.EventBegin(ctx, "handleGetProviders", lm).Done() + resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) key := u.Key(pmes.GetKey()) + lm["key"] = func() interface{} { return key.Pretty() } // debug logging niceness. reqDesc := fmt.Sprintf("%s handleGetProviders(%s, %s): ", dht.self, p, key) @@ -198,8 +203,12 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb. } func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { - defer log.EventBegin(ctx, "handleAddProvider", p).Done() + lm := make(lgbl.DeferredMap) + lm["peer"] = func() interface{} { return p.Pretty() } + + defer log.EventBegin(ctx, "handleAddProvider", lm).Done() key := u.Key(pmes.GetKey()) + lm["key"] = func() interface{} { return key.Pretty() } log.Debugf("%s adding %s as a provider for '%s'\n", dht.self, p, key) From d4a41309f31521f015637929deb4ec07be08227c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 13 Apr 2015 19:48:55 -0700 Subject: [PATCH 0837/3817] move log messages out of warning level This commit was moved from ipfs/go-ipfs-routing@b29cf0c3806847d08d48b307ce0fbad06bf16ec8 --- routing/dht/dht_net.go | 2 +- routing/dht/handlers.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 8bba4c41d..92fec8ec6 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -54,7 +54,7 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { // if nil response, return it before serializing if rpmes == nil { - log.Warning("Got back nil response from request.") + log.Debug("Got back nil response from request.") return } diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 926dc75f7..279ac82e4 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -140,7 +140,7 @@ func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.ID, pmes *pb.Mess } if closest == nil { - log.Warningf("%s handleFindPeer %s: could not find anything.", dht.self, p) + log.Infof("%s handleFindPeer %s: could not find anything.", dht.self, p) return resp, nil } From 4e2143c6402932da02c6d3f79a8d07654bfe2b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Sun, 5 Apr 2015 04:05:40 +0200 Subject: [PATCH 0838/3817] Add additional link manipulation functions // AddRawLink adds a link to this node AddRawLink(name string, lnk *Link) error // Return a copy of the link with given name GetNodeLink(name string) (*Link, error) This commit was moved from ipfs/go-merkledag@b5b61991e996d3f20c491fd111d9d3d7da51f16c --- ipld/merkledag/node.go | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 4aab2420b..34eaf2c18 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -88,18 +88,21 @@ func (l *Link) GetNode(serv DAGService) (*Node, error) { // AddNodeLink adds a link to another node. func (n *Node) AddNodeLink(name string, that *Node) error { n.encoded = nil + lnk, err := MakeLink(that) + + lnk.Name = name + lnk.Node = that if err != nil { return err } - lnk.Name = name - lnk.Node = that - n.Links = append(n.Links, lnk) + n.AddRawLink(name, lnk) + return nil } -// AddNodeLink adds a link to another node. without keeping a reference to +// AddNodeLinkClean adds a link to another node. without keeping a reference to // the child node func (n *Node) AddNodeLinkClean(name string, that *Node) error { n.encoded = nil @@ -107,9 +110,21 @@ func (n *Node) AddNodeLinkClean(name string, that *Node) error { if err != nil { return err } - lnk.Name = name + n.AddRawLink(name, lnk) + + return nil +} + +// AddRawLink adds a copy of a link to this node +func (n *Node) AddRawLink(name string, l *Link) error { + n.encoded = nil + n.Links = append(n.Links, &Link{ + Name: name, + Size: l.Size, + Hash: l.Hash, + Node: l.Node, + }) - n.Links = append(n.Links, lnk) return nil } @@ -125,6 +140,21 @@ func (n *Node) RemoveNodeLink(name string) error { return ErrNotFound } +// Return a copy of the link with given name +func (n *Node) GetNodeLink(name string) (*Link, error) { + for _, l := range n.Links { + if l.Name == name { + return &Link{ + Name: l.Name, + Size: l.Size, + Hash: l.Hash, + Node: l.Node, + }, nil + } + } + return nil, ErrNotFound +} + // Copy returns a copy of the node. // NOTE: does not make copies of Node objects in the links. func (n *Node) Copy() *Node { From 9f3d713284ce745046df8b94a8152dc023dcd952 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 3 Apr 2015 17:40:03 -0700 Subject: [PATCH 0839/3817] fix for #1008 and other pinning fixes This commit adds a new set of sharness tests for pinning, and addresses bugs that were pointed out by said tests. test/sharness: added more pinning tests Pinning is currently broken. See issue #1051. This commit introduces a few more pinning tests. These are by no means exhaustive, but definitely surface the present problems going on. I believe these tests are correct, but not sure. Pushing them as failing so that pinning is fixed in this PR. make pinning and merkledag.Get take contexts improve 'add' commands usage of pinning FIXUP: fix 'pin lists look good' ipfs-pin-stat simple script to help check pinning This is a simple shell script to help check pinning. We ought to strive towards making adding commands this easy. The http api is great and powerful, but our setup right now gets in the way. Perhaps we can clean up that area. updated t0081-repo-pinning - fixed a couple bugs with the tests - made it a bit clearer (still a lot going on) - the remaining tests are correct and highlight a problem with pinning. Namely, that recursive pinning is buggy. At least: towards the end of the test, $HASH_DIR4 and $HASH_FILE4 should be pinned indirectly, but they're not. And thus get gc-ed out. There may be other problems too. cc @whyrusleeping fix grep params for context deadline check fix bugs in pin and pin tests check for block local before checking recursive pin This commit was moved from ipfs/go-path@63b4a056ef4c981c1487d3df80be1472c148a59d --- path/resolver.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index f329ddebd..27aa2a0eb 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -3,8 +3,10 @@ package path import ( "fmt" + "time" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" merkledag "github.com/ipfs/go-ipfs/merkledag" u "github.com/ipfs/go-ipfs/util" ) @@ -74,7 +76,9 @@ func (s *Resolver) ResolvePathComponents(fpath Path) ([]*merkledag.Node, error) } log.Debug("Resolve dag get.\n") - nd, err := s.DAG.Get(u.Key(h)) + ctx, cancel := context.WithTimeout(context.TODO(), time.Minute) + defer cancel() + nd, err := s.DAG.Get(ctx, u.Key(h)) if err != nil { return nil, err } @@ -117,7 +121,9 @@ func (s *Resolver) ResolveLinks(ndd *merkledag.Node, names []string) ( if nlink.Node == nil { // fetch object for link and assign to nd - nd, err = s.DAG.Get(next) + ctx, cancel := context.WithTimeout(context.TODO(), time.Minute) + defer cancel() + nd, err = s.DAG.Get(ctx, next) if err != nil { return append(result, nd), err } From 644283f36f127a9e8458b65f210781121f502ea6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 3 Apr 2015 17:40:03 -0700 Subject: [PATCH 0840/3817] fix for #1008 and other pinning fixes This commit adds a new set of sharness tests for pinning, and addresses bugs that were pointed out by said tests. test/sharness: added more pinning tests Pinning is currently broken. See issue #1051. This commit introduces a few more pinning tests. These are by no means exhaustive, but definitely surface the present problems going on. I believe these tests are correct, but not sure. Pushing them as failing so that pinning is fixed in this PR. make pinning and merkledag.Get take contexts improve 'add' commands usage of pinning FIXUP: fix 'pin lists look good' ipfs-pin-stat simple script to help check pinning This is a simple shell script to help check pinning. We ought to strive towards making adding commands this easy. The http api is great and powerful, but our setup right now gets in the way. Perhaps we can clean up that area. updated t0081-repo-pinning - fixed a couple bugs with the tests - made it a bit clearer (still a lot going on) - the remaining tests are correct and highlight a problem with pinning. Namely, that recursive pinning is buggy. At least: towards the end of the test, $HASH_DIR4 and $HASH_FILE4 should be pinned indirectly, but they're not. And thus get gc-ed out. There may be other problems too. cc @whyrusleeping fix grep params for context deadline check fix bugs in pin and pin tests check for block local before checking recursive pin This commit was moved from ipfs/go-unixfs@fa99d7e3ab6d633c474bfa1d7099ebe70d2521fd --- unixfs/io/dagreader.go | 2 +- unixfs/io/dirbuilder.go | 9 ++++++++- unixfs/mod/dagmodifier.go | 10 +++++++--- unixfs/mod/dagmodifier_test.go | 4 ++-- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index da9b3ee24..2f33d337f 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -74,7 +74,7 @@ func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (*Dag if len(n.Links) == 0 { return nil, errors.New("incorrectly formatted metadata object") } - child, err := n.Links[0].GetNode(serv) + child, err := n.Links[0].GetNode(ctx, serv) if err != nil { return nil, err } diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index ecdbfc623..b30d9ea3a 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -1,6 +1,10 @@ package io import ( + "time" + + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" u "github.com/ipfs/go-ipfs/util" @@ -20,7 +24,10 @@ func NewDirectory(dserv mdag.DAGService) *directoryBuilder { } func (d *directoryBuilder) AddChild(name string, k u.Key) error { - cnode, err := d.dserv.Get(k) + ctx, cancel := context.WithTimeout(context.TODO(), time.Minute) + defer cancel() + + cnode, err := d.dserv.Get(ctx, k) if err != nil { return err } diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index fe04ece20..90118559b 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -5,6 +5,7 @@ import ( "errors" "io" "os" + "time" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" @@ -184,7 +185,7 @@ func (dm *DagModifier) Sync() error { return err } - nd, err := dm.dagserv.Get(thisk) + nd, err := dm.dagserv.Get(dm.ctx, thisk) if err != nil { return err } @@ -267,7 +268,7 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) ckey := u.Key(node.Links[i].Hash) dm.mp.RemovePinWithMode(ckey, pin.Indirect) - child, err := node.Links[i].GetNode(dm.dagserv) + child, err := node.Links[i].GetNode(dm.ctx, dm.dagserv) if err != nil { return "", false, err } @@ -457,7 +458,10 @@ func dagTruncate(nd *mdag.Node, size uint64, ds mdag.DAGService) (*mdag.Node, er var modified *mdag.Node ndata := new(ft.FSNode) for i, lnk := range nd.Links { - child, err := lnk.GetNode(ds) + ctx, cancel := context.WithTimeout(context.TODO(), time.Minute) + defer cancel() + + child, err := lnk.GetNode(ctx, ds) if err != nil { return nil, err } diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 2eaa04265..abc8268e3 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -578,7 +578,7 @@ func enumerateChildren(t *testing.T, nd *mdag.Node, ds mdag.DAGService) []u.Key var out []u.Key for _, lnk := range nd.Links { out = append(out, u.Key(lnk.Hash)) - child, err := lnk.GetNode(ds) + child, err := lnk.GetNode(context.Background(), ds) if err != nil { t.Fatal(err) } @@ -643,7 +643,7 @@ func printDag(nd *mdag.Node, ds mdag.DAGService, indent int) { fmt.Println() } for _, lnk := range nd.Links { - child, err := lnk.GetNode(ds) + child, err := lnk.GetNode(context.Background(), ds) if err != nil { panic(err) } From 41158c49081aec5bc5d940b59b007c0a538467d1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 3 Apr 2015 17:40:03 -0700 Subject: [PATCH 0841/3817] fix for #1008 and other pinning fixes This commit adds a new set of sharness tests for pinning, and addresses bugs that were pointed out by said tests. test/sharness: added more pinning tests Pinning is currently broken. See issue #1051. This commit introduces a few more pinning tests. These are by no means exhaustive, but definitely surface the present problems going on. I believe these tests are correct, but not sure. Pushing them as failing so that pinning is fixed in this PR. make pinning and merkledag.Get take contexts improve 'add' commands usage of pinning FIXUP: fix 'pin lists look good' ipfs-pin-stat simple script to help check pinning This is a simple shell script to help check pinning. We ought to strive towards making adding commands this easy. The http api is great and powerful, but our setup right now gets in the way. Perhaps we can clean up that area. updated t0081-repo-pinning - fixed a couple bugs with the tests - made it a bit clearer (still a lot going on) - the remaining tests are correct and highlight a problem with pinning. Namely, that recursive pinning is buggy. At least: towards the end of the test, $HASH_DIR4 and $HASH_FILE4 should be pinned indirectly, but they're not. And thus get gc-ed out. There may be other problems too. cc @whyrusleeping fix grep params for context deadline check fix bugs in pin and pin tests check for block local before checking recursive pin This commit was moved from ipfs/go-namesys@b355d6d8e784ac392042935bdfa9a7f0e638fd26 --- namesys/publisher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index eb3838eef..9ffd72618 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -150,7 +150,7 @@ func InitializeKeyspace(ctx context.Context, ds dag.DAGService, pub Publisher, p // pin recursively because this might already be pinned // and doing a direct pin would throw an error in that case - err = pins.Pin(emptyDir, true) + err = pins.Pin(ctx, emptyDir, true) if err != nil { return err } From ba7ac74edf9795046b9dc842c4baa34c6076a801 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 3 Apr 2015 17:40:03 -0700 Subject: [PATCH 0842/3817] fix for #1008 and other pinning fixes This commit adds a new set of sharness tests for pinning, and addresses bugs that were pointed out by said tests. test/sharness: added more pinning tests Pinning is currently broken. See issue #1051. This commit introduces a few more pinning tests. These are by no means exhaustive, but definitely surface the present problems going on. I believe these tests are correct, but not sure. Pushing them as failing so that pinning is fixed in this PR. make pinning and merkledag.Get take contexts improve 'add' commands usage of pinning FIXUP: fix 'pin lists look good' ipfs-pin-stat simple script to help check pinning This is a simple shell script to help check pinning. We ought to strive towards making adding commands this easy. The http api is great and powerful, but our setup right now gets in the way. Perhaps we can clean up that area. updated t0081-repo-pinning - fixed a couple bugs with the tests - made it a bit clearer (still a lot going on) - the remaining tests are correct and highlight a problem with pinning. Namely, that recursive pinning is buggy. At least: towards the end of the test, $HASH_DIR4 and $HASH_FILE4 should be pinned indirectly, but they're not. And thus get gc-ed out. There may be other problems too. cc @whyrusleeping fix grep params for context deadline check fix bugs in pin and pin tests check for block local before checking recursive pin This commit was moved from ipfs/go-merkledag@99bdf1979fe88d2b9780e1e2abb79e24788fa96f --- ipld/merkledag/merkledag.go | 13 +++---------- ipld/merkledag/merkledag_test.go | 2 +- ipld/merkledag/node.go | 6 ++++-- ipld/merkledag/traverse/traverse.go | 8 +++++++- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index c085e782e..be8753f71 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -4,7 +4,6 @@ package merkledag import ( "fmt" "sync" - "time" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" @@ -19,7 +18,7 @@ var ErrNotFound = fmt.Errorf("merkledag: not found") type DAGService interface { Add(*Node) (u.Key, error) AddRecursive(*Node) error - Get(u.Key) (*Node, error) + Get(context.Context, u.Key) (*Node, error) Remove(*Node) error // GetDAG returns, in order, all the single leve child @@ -83,17 +82,11 @@ func (n *dagService) AddRecursive(nd *Node) error { } // Get retrieves a node from the dagService, fetching the block in the BlockService -func (n *dagService) Get(k u.Key) (*Node, error) { +func (n *dagService) Get(ctx context.Context, k u.Key) (*Node, error) { if n == nil { return nil, fmt.Errorf("dagService is nil") } - ctx, cancel := context.WithTimeout(context.TODO(), time.Minute) - defer cancel() - // we shouldn't use an arbitrary timeout here. - // since Get doesnt take in a context yet, we give a large upper bound. - // think of an http request. we want it to go on as long as the client requests it. - b, err := n.Blocks.GetBlock(ctx, k) if err != nil { return nil, err @@ -134,7 +127,7 @@ func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} return } - nd, err := lnk.GetNode(serv) + nd, err := lnk.GetNode(ctx, serv) if err != nil { log.Debug(err) return diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index f46698223..1c5f18a26 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -190,7 +190,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { wg.Add(1) go func(i int) { defer wg.Done() - first, err := dagservs[i].Get(k) + first, err := dagservs[i].Get(context.Background(), k) if err != nil { t.Fatal(err) } diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 34eaf2c18..5fbffbcf0 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -3,6 +3,8 @@ package merkledag import ( "fmt" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" u "github.com/ipfs/go-ipfs/util" ) @@ -77,12 +79,12 @@ func MakeLink(n *Node) (*Link, error) { } // GetNode returns the MDAG Node that this link points to -func (l *Link) GetNode(serv DAGService) (*Node, error) { +func (l *Link) GetNode(ctx context.Context, serv DAGService) (*Node, error) { if l.Node != nil { return l.Node, nil } - return serv.Get(u.Key(l.Hash)) + return serv.Get(ctx, u.Key(l.Hash)) } // AddNodeLink adds a link to another node. diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index 73f032319..b00307364 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -3,6 +3,9 @@ package traverse import ( "errors" + "time" + + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" mdag "github.com/ipfs/go-ipfs/merkledag" ) @@ -64,7 +67,10 @@ func (t *traversal) callFunc(next State) error { func (t *traversal) getNode(link *mdag.Link) (*mdag.Node, error) { getNode := func(l *mdag.Link) (*mdag.Node, error) { - next, err := l.GetNode(t.opts.DAG) + ctx, cancel := context.WithTimeout(context.TODO(), time.Minute) + defer cancel() + + next, err := l.GetNode(ctx, t.opts.DAG) if err != nil { return nil, err } From 03d2bb8261204b37ce2a57954a557369a24da1fc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 3 Apr 2015 17:40:03 -0700 Subject: [PATCH 0843/3817] fix for #1008 and other pinning fixes This commit adds a new set of sharness tests for pinning, and addresses bugs that were pointed out by said tests. test/sharness: added more pinning tests Pinning is currently broken. See issue #1051. This commit introduces a few more pinning tests. These are by no means exhaustive, but definitely surface the present problems going on. I believe these tests are correct, but not sure. Pushing them as failing so that pinning is fixed in this PR. make pinning and merkledag.Get take contexts improve 'add' commands usage of pinning FIXUP: fix 'pin lists look good' ipfs-pin-stat simple script to help check pinning This is a simple shell script to help check pinning. We ought to strive towards making adding commands this easy. The http api is great and powerful, but our setup right now gets in the way. Perhaps we can clean up that area. updated t0081-repo-pinning - fixed a couple bugs with the tests - made it a bit clearer (still a lot going on) - the remaining tests are correct and highlight a problem with pinning. Namely, that recursive pinning is buggy. At least: towards the end of the test, $HASH_DIR4 and $HASH_FILE4 should be pinned indirectly, but they're not. And thus get gc-ed out. There may be other problems too. cc @whyrusleeping fix grep params for context deadline check fix bugs in pin and pin tests check for block local before checking recursive pin This commit was moved from ipfs/go-ipfs-pinner@c177cd36c51882408d9e861077241c9ac694a259 --- pinning/pinner/indirect.go | 13 +++++++-- pinning/pinner/pin.go | 50 ++++++++++++++++--------------- pinning/pinner/pin_test.go | 60 +++++++++++++++++++++++++++++++++----- 3 files changed, 89 insertions(+), 34 deletions(-) diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index 46350a4e0..deed1f5ff 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -28,9 +28,11 @@ func loadIndirPin(d ds.Datastore, k ds.Key) (*indirectPin, error) { refcnt := make(map[util.Key]int) var keys []util.Key for encK, v := range rcStore { - k := util.B58KeyDecode(encK) - keys = append(keys, k) - refcnt[k] = v + if v > 0 { + k := util.B58KeyDecode(encK) + keys = append(keys, k) + refcnt[k] = v + } } // log.Debugf("indirPin keys: %#v", keys) @@ -59,6 +61,7 @@ func (i *indirectPin) Decrement(k util.Key) { i.refCounts[k] = c if c <= 0 { i.blockset.RemoveBlock(k) + delete(i.refCounts, k) } } @@ -69,3 +72,7 @@ func (i *indirectPin) HasKey(k util.Key) bool { func (i *indirectPin) Set() set.BlockSet { return i.blockset } + +func (i *indirectPin) GetRefs() map[util.Key]int { + return i.refCounts +} diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 49a587133..553593fc8 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "sync" - "time" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" nsds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" @@ -33,12 +32,12 @@ const ( type Pinner interface { IsPinned(util.Key) bool - Pin(*mdag.Node, bool) error - Unpin(util.Key, bool) error + Pin(context.Context, *mdag.Node, bool) error + Unpin(context.Context, util.Key, bool) error Flush() error GetManual() ManualPinner DirectKeys() []util.Key - IndirectKeys() []util.Key + IndirectKeys() map[util.Key]int RecursiveKeys() []util.Key } @@ -82,7 +81,7 @@ func NewPinner(dstore ds.ThreadSafeDatastore, serv mdag.DAGService) Pinner { } // Pin the given node, optionally recursive -func (p *pinner) Pin(node *mdag.Node, recurse bool) error { +func (p *pinner) Pin(ctx context.Context, node *mdag.Node, recurse bool) error { p.lock.Lock() defer p.lock.Unlock() k, err := node.Key() @@ -99,34 +98,40 @@ func (p *pinner) Pin(node *mdag.Node, recurse bool) error { p.directPin.RemoveBlock(k) } - p.recursePin.AddBlock(k) - - err := p.pinLinks(node) + err := p.pinLinks(ctx, node) if err != nil { return err } + + p.recursePin.AddBlock(k) } else { + _, err := p.dserv.Get(ctx, k) + if err != nil { + return err + } + if p.recursePin.HasKey(k) { return fmt.Errorf("%s already pinned recursively", k.B58String()) } + p.directPin.AddBlock(k) } return nil } // Unpin a given key -func (p *pinner) Unpin(k util.Key, recursive bool) error { +func (p *pinner) Unpin(ctx context.Context, k util.Key, recursive bool) error { p.lock.Lock() defer p.lock.Unlock() if p.recursePin.HasKey(k) { if recursive { p.recursePin.RemoveBlock(k) - node, err := p.dserv.Get(k) + node, err := p.dserv.Get(ctx, k) if err != nil { return err } - return p.unpinLinks(node) + return p.unpinLinks(ctx, node) } else { return fmt.Errorf("%s is pinned recursively", k) } @@ -140,9 +145,9 @@ func (p *pinner) Unpin(k util.Key, recursive bool) error { } } -func (p *pinner) unpinLinks(node *mdag.Node) error { +func (p *pinner) unpinLinks(ctx context.Context, node *mdag.Node) error { for _, l := range node.Links { - node, err := l.GetNode(p.dserv) + node, err := l.GetNode(ctx, p.dserv) if err != nil { return err } @@ -152,9 +157,9 @@ func (p *pinner) unpinLinks(node *mdag.Node) error { return err } - p.recursePin.RemoveBlock(k) + p.indirPin.Decrement(k) - err = p.unpinLinks(node) + err = p.unpinLinks(ctx, node) if err != nil { return err } @@ -162,27 +167,24 @@ func (p *pinner) unpinLinks(node *mdag.Node) error { return nil } -func (p *pinner) pinIndirectRecurse(node *mdag.Node) error { +func (p *pinner) pinIndirectRecurse(ctx context.Context, node *mdag.Node) error { k, err := node.Key() if err != nil { return err } p.indirPin.Increment(k) - return p.pinLinks(node) + return p.pinLinks(ctx, node) } -func (p *pinner) pinLinks(node *mdag.Node) error { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*60) - defer cancel() - +func (p *pinner) pinLinks(ctx context.Context, node *mdag.Node) error { for _, ng := range p.dserv.GetDAG(ctx, node) { subnode, err := ng.Get(ctx) if err != nil { // TODO: Maybe just log and continue? return err } - err = p.pinIndirectRecurse(subnode) + err = p.pinIndirectRecurse(ctx, subnode) if err != nil { return err } @@ -256,8 +258,8 @@ func (p *pinner) DirectKeys() []util.Key { } // IndirectKeys returns a slice containing the indirectly pinned keys -func (p *pinner) IndirectKeys() []util.Key { - return p.indirPin.Set().GetKeys() +func (p *pinner) IndirectKeys() map[util.Key]int { + return p.indirPin.GetRefs() } // RecursiveKeys returns a slice containing the recursively pinned keys diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index f31e1fef9..b79232570 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -2,6 +2,9 @@ package pin import ( "testing" + "time" + + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" @@ -21,6 +24,8 @@ func randNode() (*mdag.Node, util.Key) { } func TestPinnerBasic(t *testing.T) { + ctx := context.Background() + dstore := dssync.MutexWrap(ds.NewMapDatastore()) bstore := blockstore.NewBlockstore(dstore) bserv, err := bs.New(bstore, offline.Exchange(bstore)) @@ -40,7 +45,7 @@ func TestPinnerBasic(t *testing.T) { } // Pin A{} - err = p.Pin(a, false) + err = p.Pin(ctx, a, false) if err != nil { t.Fatal(err) } @@ -74,7 +79,7 @@ func TestPinnerBasic(t *testing.T) { } // recursively pin B{A,C} - err = p.Pin(b, true) + err = p.Pin(ctx, b, true) if err != nil { t.Fatal(err) } @@ -102,7 +107,7 @@ func TestPinnerBasic(t *testing.T) { } // Add D{A,C,E} - err = p.Pin(d, true) + err = p.Pin(ctx, d, true) if err != nil { t.Fatal(err) } @@ -117,7 +122,7 @@ func TestPinnerBasic(t *testing.T) { } // Test recursive unpin - err = p.Unpin(dk, true) + err = p.Unpin(ctx, dk, true) if err != nil { t.Fatal(err) } @@ -154,6 +159,7 @@ func TestPinnerBasic(t *testing.T) { } func TestDuplicateSemantics(t *testing.T) { + ctx := context.Background() dstore := dssync.MutexWrap(ds.NewMapDatastore()) bstore := blockstore.NewBlockstore(dstore) bserv, err := bs.New(bstore, offline.Exchange(bstore)) @@ -173,19 +179,59 @@ func TestDuplicateSemantics(t *testing.T) { } // pin is recursively - err = p.Pin(a, true) + err = p.Pin(ctx, a, true) if err != nil { t.Fatal(err) } // pinning directly should fail - err = p.Pin(a, false) + err = p.Pin(ctx, a, false) if err == nil { t.Fatal("expected direct pin to fail") } // pinning recursively again should succeed - err = p.Pin(a, true) + err = p.Pin(ctx, a, true) + if err != nil { + t.Fatal(err) + } +} + +func TestPinRecursiveFail(t *testing.T) { + ctx := context.Background() + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv, err := bs.New(bstore, offline.Exchange(bstore)) + if err != nil { + t.Fatal(err) + } + + dserv := mdag.NewDAGService(bserv) + + p := NewPinner(dstore, dserv) + + a, _ := randNode() + b, _ := randNode() + err = a.AddNodeLinkClean("child", b) + if err != nil { + t.Fatal(err) + } + + // Note: this isnt a time based test, we expect the pin to fail + mctx, _ := context.WithTimeout(ctx, time.Millisecond) + err = p.Pin(mctx, a, true) + if err == nil { + t.Fatal("should have failed to pin here") + } + + _, err = dserv.Add(b) + if err != nil { + t.Fatal(err) + } + + // this one is time based... but shouldnt cause any issues + mctx, _ = context.WithTimeout(ctx, time.Second) + err = p.Pin(mctx, a, true) if err != nil { t.Fatal(err) } From 456ec1cee3f20c1f616cc4b0377dfe33af1828ff Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 20 Apr 2015 00:15:34 -0700 Subject: [PATCH 0844/3817] remove debugerrors We now consider debugerrors harmful: we've run into cases where debugerror.Wrap() hid valuable error information (err == io.EOF?). I've removed them from the main code, but left them in some tests. Go errors are lacking, but unfortunately, this isn't the solution. It is possible that debugerros.New or debugerrors.Errorf should remain still (i.e. only remove debugerrors.Wrap) but we don't use these errors often enough to keep. This commit was moved from ipfs/go-ipfs-routing@720c8b55d99ac0d741cfe64393b980db2def2466 --- routing/dht/routing.go | 7 +++---- routing/mock/dht.go | 1 - routing/supernode/client.go | 10 +++++----- routing/supernode/proxy/loopback.go | 3 +-- routing/supernode/proxy/standard.go | 5 +++-- routing/supernode/server.go | 4 ++-- 6 files changed, 14 insertions(+), 16 deletions(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 28967d200..47e892414 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -13,7 +13,6 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" u "github.com/ipfs/go-ipfs/util" - errors "github.com/ipfs/go-ipfs/util/debugerror" pset "github.com/ipfs/go-ipfs/util/peerset" ) @@ -95,7 +94,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { log.Debugf("peers in rt: %s", len(rtp), rtp) if len(rtp) == 0 { log.Warning("No peers from routing table!") - return nil, errors.Wrap(kb.ErrLookupFailure) + return nil, kb.ErrLookupFailure } // setup the Query @@ -278,7 +277,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, er peers := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue) if len(peers) == 0 { - return peer.PeerInfo{}, errors.Wrap(kb.ErrLookupFailure) + return peer.PeerInfo{}, kb.ErrLookupFailure } // Sanity... @@ -344,7 +343,7 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< peers := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue) if len(peers) == 0 { - return nil, errors.Wrap(kb.ErrLookupFailure) + return nil, kb.ErrLookupFailure } // setup the Query diff --git a/routing/mock/dht.go b/routing/mock/dht.go index f4b2b4900..df8d7cdfc 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -30,7 +30,6 @@ func (rs *mocknetserver) ClientWithDatastore(ctx context.Context, p testutil.Ide host, err := rs.mn.AddPeer(p.PrivateKey(), p.Address()) if err != nil { panic("FIXME") - // return nil, debugerror.Wrap(err) } return dht.NewDHT(ctx, host, sync.MutexWrap(ds)) } diff --git a/routing/supernode/client.go b/routing/supernode/client.go index fa47e2e80..13f845abe 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -2,6 +2,7 @@ package supernode import ( "bytes" + "errors" "time" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" @@ -13,7 +14,6 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" u "github.com/ipfs/go-ipfs/util" - errors "github.com/ipfs/go-ipfs/util/debugerror" ) var log = eventlog.Logger("supernode") @@ -44,13 +44,13 @@ func (c *Client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-cha request := pb.NewMessage(pb.Message_GET_PROVIDERS, string(k), 0) response, err := c.proxy.SendRequest(ctx, request) if err != nil { - log.Debug(errors.Wrap(err)) + log.Debug(err) return } for _, p := range pb.PBPeersToPeerInfos(response.GetProviderPeers()) { select { case <-ctx.Done(): - log.Debug(errors.Wrap(ctx.Err())) + log.Debug(ctx.Err()) return case ch <- p: } @@ -75,7 +75,7 @@ func (c *Client) GetValue(ctx context.Context, k u.Key) ([]byte, error) { msg := pb.NewMessage(pb.Message_GET_VALUE, string(k), 0) response, err := c.proxy.SendRequest(ctx, msg) // TODO wrap to hide the remote if err != nil { - return nil, errors.Wrap(err) + return nil, err } return response.Record.GetValue(), nil } @@ -101,7 +101,7 @@ func (c *Client) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, error request := pb.NewMessage(pb.Message_FIND_NODE, string(id), 0) response, err := c.proxy.SendRequest(ctx, request) // hide remote if err != nil { - return peer.PeerInfo{}, errors.Wrap(err) + return peer.PeerInfo{}, err } for _, p := range pb.PBPeersToPeerInfos(response.GetCloserPeers()) { if p.ID == id { diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 5d46fb4e1..06e91707c 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -6,7 +6,6 @@ import ( inet "github.com/ipfs/go-ipfs/p2p/net" peer "github.com/ipfs/go-ipfs/p2p/peer" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - errors "github.com/ipfs/go-ipfs/util/debugerror" ) // RequestHandler handles routing requests locally @@ -43,7 +42,7 @@ func (lb *Loopback) HandleStream(s inet.Stream) { pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) var incoming dhtpb.Message if err := pbr.ReadMsg(&incoming); err != nil { - log.Debug(errors.Wrap(err)) + log.Debug(err) return } ctx := context.TODO() diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 0a3d9e1b7..7f4d38faa 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -1,6 +1,8 @@ package proxy import ( + "errors" + ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" host "github.com/ipfs/go-ipfs/p2p/host" @@ -10,7 +12,6 @@ import ( kbucket "github.com/ipfs/go-ipfs/routing/kbucket" eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" "github.com/ipfs/go-ipfs/util" - errors "github.com/ipfs/go-ipfs/util/debugerror" ) const ProtocolSNR = "/ipfs/supernoderouting" @@ -103,7 +104,7 @@ func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote pe defer s.Close() pbw := ggio.NewDelimitedWriter(s) if err := pbw.WriteMsg(m); err != nil { - return errors.Wrap(err) + return err } return nil } diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 44ef349d4..fb077e882 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -1,6 +1,7 @@ package supernode import ( + "errors" "fmt" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" @@ -11,7 +12,6 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" util "github.com/ipfs/go-ipfs/util" - errors "github.com/ipfs/go-ipfs/util/debugerror" ) // Server handles routing queries using a database backend @@ -117,7 +117,7 @@ func getRoutingRecord(ds datastore.Datastore, k util.Key) (*dhtpb.Record, error) dskey := k.DsKey() val, err := ds.Get(dskey) if err != nil { - return nil, errors.Wrap(err) + return nil, err } recordBytes, ok := val.([]byte) if !ok { From c4d46e1dbb6368c0c2dfb1eaac8c8becb9fc84f2 Mon Sep 17 00:00:00 2001 From: gatesvp Date: Mon, 20 Apr 2015 00:37:17 -0700 Subject: [PATCH 0845/3817] Move IPNS resolutions into the core library Move IPNS resolutions into the core library via the pathresolver.go file. Fix the CLI commands to leverage this core component. This commit was moved from ipfs/go-path@c880a2a1eed71f6916f6ca51207dbb87c97a50db --- path/path.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/path/path.go b/path/path.go index ea83cd12c..7820880c1 100644 --- a/path/path.go +++ b/path/path.go @@ -1,10 +1,9 @@ package path import ( + u "github.com/ipfs/go-ipfs/util" "path" "strings" - - u "github.com/ipfs/go-ipfs/util" ) // TODO: debate making this a private struct wrapped in a public interface @@ -36,3 +35,7 @@ func (p Path) Segments() []string { func (p Path) String() string { return string(p) } + +func FromSegments(seg ...string) Path { + return Path(strings.Join(seg, "/")) +} From 37ac594834c7bb9e66f7300034d860968fefc2af Mon Sep 17 00:00:00 2001 From: gatesvp Date: Mon, 20 Apr 2015 00:37:17 -0700 Subject: [PATCH 0846/3817] Move IPNS resolutions into the core library Move IPNS resolutions into the core library via the pathresolver.go file. Fix the CLI commands to leverage this core component. This commit was moved from ipfs/go-unixfs@671dfd042178615d85205190a7f6d306f0470ab7 --- unixfs/tar/reader.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index 9509b2d6d..405a0eb18 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -28,12 +28,11 @@ type Reader struct { err error } -func NewReader(path path.Path, dag mdag.DAGService, resolver *path.Resolver, compression int) (*Reader, error) { +func NewReader(path path.Path, dag mdag.DAGService, dagnode *mdag.Node, compression int) (*Reader, error) { reader := &Reader{ signalChan: make(chan struct{}), dag: dag, - resolver: resolver, } var err error @@ -47,11 +46,6 @@ func NewReader(path path.Path, dag mdag.DAGService, resolver *path.Resolver, com reader.writer = tar.NewWriter(&reader.buf) } - dagnode, err := resolver.ResolvePath(path) - if err != nil { - return nil, err - } - // writeToBuf will write the data to the buffer, and will signal when there // is new data to read _, filename := gopath.Split(path.String()) From a040804e61c1ecc8c503d3ce87afd86bcc6b8109 Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Mon, 16 Mar 2015 14:03:28 -0700 Subject: [PATCH 0847/3817] Use flatfs to store objects under /blocks outside of LevelDB WARNING: No migration performed! That needs to come in a separate commit, perhaps amended into this one. Migration must move keyspace "/b" from leveldb to the flatfs subdir, while removing the "b" prefix (keys should start with just "/"). This commit was moved from ipfs/go-ipfs-blockstore@ed982777b038825394b05007e503a129c4d1716a --- blockstore/blockstore.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 7f3d4d7c8..ccc7e8fc5 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -18,7 +18,7 @@ import ( var log = eventlog.Logger("blockstore") // BlockPrefix namespaces blockstore datastores -var BlockPrefix = ds.NewKey("b") +var BlockPrefix = ds.NewKey("blocks") var ValueTypeMismatch = errors.New("The retrieved value is not a Block") @@ -89,6 +89,8 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan u.Key, error) { // KeysOnly, because that would be _a lot_ of data. q := dsq.Query{KeysOnly: true} + // datastore/namespace does *NOT* fix up Query.Prefix + q.Prefix = BlockPrefix.String() res, err := bs.datastore.Query(q) if err != nil { return nil, err From 552e83c6088794a33759a83b2cd4f5983361b9cb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 17 Apr 2015 21:14:25 -0700 Subject: [PATCH 0848/3817] refactored ipns records to point to paths Also changed the ipns dns resolution to use the "dnslink" format This commit was moved from ipfs/go-path@7058d559f87f9444650b0274c48d3ec4c2adce27 --- path/path.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/path/path.go b/path/path.go index 7820880c1..f7d4e43ff 100644 --- a/path/path.go +++ b/path/path.go @@ -1,11 +1,19 @@ package path import ( - u "github.com/ipfs/go-ipfs/util" + "errors" "path" "strings" + + u "github.com/ipfs/go-ipfs/util" + + b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" + mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" ) +// ErrBadPath is returned when a given path is incorrectly formatted +var ErrBadPath = errors.New("invalid ipfs ref path") + // TODO: debate making this a private struct wrapped in a public interface // would allow us to control creation, and cache segments. type Path string @@ -17,7 +25,7 @@ func FromString(s string) Path { // FromKey safely converts a Key type to a Path type func FromKey(k u.Key) Path { - return Path(k.String()) + return Path("/ipfs/" + k.String()) } func (p Path) Segments() []string { @@ -39,3 +47,42 @@ func (p Path) String() string { func FromSegments(seg ...string) Path { return Path(strings.Join(seg, "/")) } + +func ParsePath(txt string) (Path, error) { + kp, err := ParseKeyToPath(txt) + if err == nil { + return kp, nil + } + parts := strings.Split(txt, "/") + if len(parts) < 3 { + return "", ErrBadPath + } + + if parts[0] != "" { + return "", ErrBadPath + } + + if parts[1] != "ipfs" && parts[1] != "ipns" { + return "", ErrBadPath + } + + _, err = ParseKeyToPath(parts[2]) + if err != nil { + return "", err + } + + return Path(txt), nil +} + +func ParseKeyToPath(txt string) (Path, error) { + chk := b58.Decode(txt) + if len(chk) == 0 { + return "", errors.New("not a key") + } + + _, err := mh.Cast(chk) + if err != nil { + return "", err + } + return FromKey(u.Key(chk)), nil +} From d86225ad4de999032eb018f485f9029709f54811 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 19 Apr 2015 11:17:06 -0700 Subject: [PATCH 0849/3817] address comments from CR This commit was moved from ipfs/go-path@f7108d98b59fec9b2e62f5c701dfaedbaf17ab01 --- path/path.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/path/path.go b/path/path.go index f7d4e43ff..e61a06193 100644 --- a/path/path.go +++ b/path/path.go @@ -49,11 +49,13 @@ func FromSegments(seg ...string) Path { } func ParsePath(txt string) (Path, error) { - kp, err := ParseKeyToPath(txt) - if err == nil { - return kp, nil - } parts := strings.Split(txt, "/") + if len(parts) == 1 { + kp, err := ParseKeyToPath(txt) + if err == nil { + return kp, nil + } + } if len(parts) < 3 { return "", ErrBadPath } @@ -66,7 +68,7 @@ func ParsePath(txt string) (Path, error) { return "", ErrBadPath } - _, err = ParseKeyToPath(parts[2]) + _, err := ParseKeyToPath(parts[2]) if err != nil { return "", err } @@ -86,3 +88,8 @@ func ParseKeyToPath(txt string) (Path, error) { } return FromKey(u.Key(chk)), nil } + +func (p *Path) IsValid() error { + _, err := ParsePath(p.String()) + return err +} From e62855b59292242e3777809149253233e83e3e13 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 22 Apr 2015 23:20:22 -0700 Subject: [PATCH 0850/3817] fix up core.Resolve a bit This commit was moved from ipfs/go-path@a3af1211d2e2cf490f67017523075ec1fceaf313 --- path/path.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/path/path.go b/path/path.go index e61a06193..15c1b4591 100644 --- a/path/path.go +++ b/path/path.go @@ -45,7 +45,11 @@ func (p Path) String() string { } func FromSegments(seg ...string) Path { - return Path(strings.Join(seg, "/")) + var pref string + if seg[0] == "ipfs" || seg[0] == "ipns" { + pref = "/" + } + return Path(pref + strings.Join(seg, "/")) } func ParsePath(txt string) (Path, error) { From 40a52e4c6b8c5f995b81febd0acc322cdf4ab1ce Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 17 Apr 2015 21:14:25 -0700 Subject: [PATCH 0851/3817] refactored ipns records to point to paths Also changed the ipns dns resolution to use the "dnslink" format This commit was moved from ipfs/go-namesys@ef61b7d31108790d3d9c3ca80b8ea0bb0bc67dea --- namesys/dns.go | 38 ++++++++++++++++++++++++------------- namesys/dns_test.go | 42 +++++++++++++++++++++++++++++++++++++++++ namesys/interface.go | 6 +++--- namesys/namesys.go | 6 +++--- namesys/proquint.go | 6 +++--- namesys/publisher.go | 14 ++++---------- namesys/resolve_test.go | 8 ++------ namesys/routing.go | 15 +++++++++++++-- 8 files changed, 95 insertions(+), 40 deletions(-) create mode 100644 namesys/dns_test.go diff --git a/namesys/dns.go b/namesys/dns.go index 003e6f0f0..086adee9e 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -1,14 +1,14 @@ package namesys import ( + "errors" "net" + "strings" - b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" isd "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-is-domain" - mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - u "github.com/ipfs/go-ipfs/util" + path "github.com/ipfs/go-ipfs/path" ) // DNSResolver implements a Resolver on DNS domains @@ -25,7 +25,7 @@ func (r *DNSResolver) CanResolve(name string) bool { // Resolve implements Resolver // TXT records for a given domain name should contain a b58 // encoded multihash. -func (r *DNSResolver) Resolve(ctx context.Context, name string) (u.Key, error) { +func (r *DNSResolver) Resolve(ctx context.Context, name string) (path.Path, error) { log.Info("DNSResolver resolving %v", name) txt, err := net.LookupTXT(name) if err != nil { @@ -33,17 +33,29 @@ func (r *DNSResolver) Resolve(ctx context.Context, name string) (u.Key, error) { } for _, t := range txt { - chk := b58.Decode(t) - if len(chk) == 0 { - continue + p, err := parseEntry(t) + if err == nil { + return p, nil } - - _, err := mh.Cast(chk) - if err != nil { - continue - } - return u.Key(chk), nil } return "", ErrResolveFailed } + +func parseEntry(txt string) (path.Path, error) { + p, err := path.ParseKeyToPath(txt) + if err == nil { + return p, nil + } + + return tryParseDnsLink(txt) +} + +func tryParseDnsLink(txt string) (path.Path, error) { + parts := strings.Split(txt, "=") + if len(parts) == 1 || parts[0] != "dnslink" { + return "", errors.New("not a valid dnslink entry") + } + + return path.ParsePath(parts[1]) +} diff --git a/namesys/dns_test.go b/namesys/dns_test.go new file mode 100644 index 000000000..402156add --- /dev/null +++ b/namesys/dns_test.go @@ -0,0 +1,42 @@ +package namesys + +import ( + "testing" +) + +func TestDnsEntryParsing(t *testing.T) { + goodEntries := []string{ + "QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", + "dnslink=/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/foo", + "dnslink=/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/bar", + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/foo/bar/baz", + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", + } + + badEntries := []string{ + "QmYhE8xgFCjGcz6PHgnvJz5NOTCORRECT", + "quux=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", + "dnslink=", + "dnslink=/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/foo", + "dnslink=ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/bar", + } + + for _, e := range goodEntries { + _, err := parseEntry(e) + if err != nil { + t.Log("expected entry to parse correctly!") + t.Log(e) + t.Fatal(err) + } + } + + for _, e := range badEntries { + _, err := parseEntry(e) + if err == nil { + t.Log("expected entry parse to fail!") + t.Fatal(err) + } + } +} diff --git a/namesys/interface.go b/namesys/interface.go index 39a5c6e73..4ceb3b9d9 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -6,7 +6,7 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ci "github.com/ipfs/go-ipfs/p2p/crypto" - u "github.com/ipfs/go-ipfs/util" + path "github.com/ipfs/go-ipfs/path" ) // ErrResolveFailed signals an error when attempting to resolve. @@ -31,7 +31,7 @@ type NameSystem interface { type Resolver interface { // Resolve looks up a name, and returns the value previously published. - Resolve(ctx context.Context, name string) (value u.Key, err error) + Resolve(ctx context.Context, name string) (value path.Path, err error) // CanResolve checks whether this Resolver can resolve a name CanResolve(name string) bool @@ -42,5 +42,5 @@ type Publisher interface { // Publish establishes a name-value mapping. // TODO make this not PrivKey specific. - Publish(ctx context.Context, name ci.PrivKey, value u.Key) error + Publish(ctx context.Context, name ci.PrivKey, value path.Path) error } diff --git a/namesys/namesys.go b/namesys/namesys.go index ed2ccb255..655307723 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -3,8 +3,8 @@ package namesys import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ci "github.com/ipfs/go-ipfs/p2p/crypto" + path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - u "github.com/ipfs/go-ipfs/util" ) // ipnsNameSystem implements IPNS naming. @@ -34,7 +34,7 @@ func NewNameSystem(r routing.IpfsRouting) NameSystem { } // Resolve implements Resolver -func (ns *ipns) Resolve(ctx context.Context, name string) (u.Key, error) { +func (ns *ipns) Resolve(ctx context.Context, name string) (path.Path, error) { for _, r := range ns.resolvers { if r.CanResolve(name) { return r.Resolve(ctx, name) @@ -54,6 +54,6 @@ func (ns *ipns) CanResolve(name string) bool { } // Publish implements Publisher -func (ns *ipns) Publish(ctx context.Context, name ci.PrivKey, value u.Key) error { +func (ns *ipns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error { return ns.publisher.Publish(ctx, name, value) } diff --git a/namesys/proquint.go b/namesys/proquint.go index e3e2cc281..66bd54e24 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -5,7 +5,7 @@ import ( proquint "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/bren2010/proquint" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - u "github.com/ipfs/go-ipfs/util" + path "github.com/ipfs/go-ipfs/path" ) type ProquintResolver struct{} @@ -17,10 +17,10 @@ func (r *ProquintResolver) CanResolve(name string) bool { } // Resolve implements Resolver. Decodes the proquint string. -func (r *ProquintResolver) Resolve(ctx context.Context, name string) (u.Key, error) { +func (r *ProquintResolver) Resolve(ctx context.Context, name string) (path.Path, error) { ok := r.CanResolve(name) if !ok { return "", errors.New("not a valid proquint string") } - return u.Key(proquint.Decode(name)), nil + return path.FromString(string(proquint.Decode(name))), nil } diff --git a/namesys/publisher.go b/namesys/publisher.go index 9ffd72618..23e15ca71 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,12 +7,12 @@ import ( "time" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" - mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" dag "github.com/ipfs/go-ipfs/merkledag" pb "github.com/ipfs/go-ipfs/namesys/internal/pb" ci "github.com/ipfs/go-ipfs/p2p/crypto" + path "github.com/ipfs/go-ipfs/path" pin "github.com/ipfs/go-ipfs/pin" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" @@ -41,15 +41,9 @@ func NewRoutingPublisher(route routing.IpfsRouting) Publisher { // Publish implements Publisher. Accepts a keypair and a value, // and publishes it out to the routing system -func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value u.Key) error { +func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error { log.Debugf("namesys: Publish %s", value) - // validate `value` is a ref (multihash) - _, err := mh.FromB58String(value.Pretty()) - if err != nil { - return fmt.Errorf("publish value must be str multihash. %v", err) - } - data, err := createRoutingEntryData(k, value) if err != nil { return err @@ -84,7 +78,7 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value u.Key) return nil } -func createRoutingEntryData(pk ci.PrivKey, val u.Key) ([]byte, error) { +func createRoutingEntryData(pk ci.PrivKey, val path.Path) ([]byte, error) { entry := new(pb.IpnsEntry) entry.Value = []byte(val) @@ -160,7 +154,7 @@ func InitializeKeyspace(ctx context.Context, ds dag.DAGService, pub Publisher, p return err } - err = pub.Publish(ctx, key, nodek) + err = pub.Publish(ctx, key, path.FromKey(nodek)) if err != nil { return err } diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index e9cd01760..ce28b1d6b 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -4,6 +4,7 @@ import ( "testing" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" u "github.com/ipfs/go-ipfs/util" testutil "github.com/ipfs/go-ipfs/util/testutil" @@ -20,12 +21,7 @@ func TestRoutingResolve(t *testing.T) { t.Fatal(err) } - err = publisher.Publish(context.Background(), privk, "Hello") - if err == nil { - t.Fatal("should have errored out when publishing a non-multihash val") - } - - h := u.Key(u.Hash([]byte("Hello"))) + h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN") err = publisher.Publish(context.Background(), privk, h) if err != nil { t.Fatal(err) diff --git a/namesys/routing.go b/namesys/routing.go index 4a9756d00..5e0cf1a96 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -7,6 +7,7 @@ import ( mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" pb "github.com/ipfs/go-ipfs/namesys/internal/pb" + path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" ) @@ -36,7 +37,7 @@ func (r *routingResolver) CanResolve(name string) bool { // Resolve implements Resolver. Uses the IPFS routing system to resolve SFS-like // names. -func (r *routingResolver) Resolve(ctx context.Context, name string) (u.Key, error) { +func (r *routingResolver) Resolve(ctx context.Context, name string) (path.Path, error) { log.Debugf("RoutingResolve: '%s'", name) hash, err := mh.FromB58String(name) if err != nil { @@ -77,5 +78,15 @@ func (r *routingResolver) Resolve(ctx context.Context, name string) (u.Key, erro } // ok sig checks out. this is a valid name. - return u.Key(entry.GetValue()), nil + + // check for old style record: + valh, err := mh.Cast(entry.GetValue()) + if err != nil { + // Not a multihash, probably a new record + return path.ParsePath(string(entry.GetValue())) + } else { + // Its an old style multihash record + log.Warning("Detected old style multihash record") + return path.FromKey(u.Key(valh)), nil + } } From 7ffec5aeddb572dd13e1ca3967fdb344e0ff4e56 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 23 Apr 2015 22:02:33 -0700 Subject: [PATCH 0852/3817] address comments from CR This commit was moved from ipfs/go-path@10ad47cafa603f87bb3032ecb94c41f6397384f1 --- path/path.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/path/path.go b/path/path.go index 15c1b4591..0ab15e5c9 100644 --- a/path/path.go +++ b/path/path.go @@ -44,12 +44,12 @@ func (p Path) String() string { return string(p) } -func FromSegments(seg ...string) Path { +func FromSegments(seg ...string) (Path, error) { var pref string if seg[0] == "ipfs" || seg[0] == "ipns" { pref = "/" } - return Path(pref + strings.Join(seg, "/")) + return ParsePath(pref + strings.Join(seg, "/")) } func ParsePath(txt string) (Path, error) { From 26b0be009e435d1d62f5ea1619a88fd6429e3f40 Mon Sep 17 00:00:00 2001 From: Henry Date: Tue, 28 Apr 2015 12:33:02 +0200 Subject: [PATCH 0853/3817] godeps: move (go)goprotobuf to github location This commit was moved from ipfs/go-unixfs@0f1dc11a77774130c6be8b72da2a89817157e193 --- unixfs/format.go | 2 +- unixfs/format_test.go | 3 ++- unixfs/io/dagreader.go | 3 ++- unixfs/mod/dagmodifier.go | 2 +- unixfs/pb/unixfs.pb.go | 2 +- unixfs/tar/reader.go | 4 ++-- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/unixfs/format.go b/unixfs/format.go index 21ba46f74..b8c0abeb1 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -5,7 +5,7 @@ package unixfs import ( "errors" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" pb "github.com/ipfs/go-ipfs/unixfs/pb" ) diff --git a/unixfs/format_test.go b/unixfs/format_test.go index 4d4175545..f178b5615 100644 --- a/unixfs/format_test.go +++ b/unixfs/format_test.go @@ -3,7 +3,8 @@ package unixfs import ( "testing" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + pb "github.com/ipfs/go-ipfs/unixfs/pb" ) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 2f33d337f..def8c1501 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -7,8 +7,9 @@ import ( "io" "os" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 90118559b..6cca0c007 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -7,7 +7,7 @@ import ( "os" "time" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" diff --git a/unixfs/pb/unixfs.pb.go b/unixfs/pb/unixfs.pb.go index aac37040e..c11ffd4d0 100644 --- a/unixfs/pb/unixfs.pb.go +++ b/unixfs/pb/unixfs.pb.go @@ -14,7 +14,7 @@ It has these top-level messages: */ package unixfs_pb -import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index 405a0eb18..20e18fe11 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -8,13 +8,13 @@ import ( gopath "path" "time" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + mdag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" uio "github.com/ipfs/go-ipfs/unixfs/io" upb "github.com/ipfs/go-ipfs/unixfs/pb" - - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ) type Reader struct { From 44194a91e6cde05ef1875dbf988902e84572058f Mon Sep 17 00:00:00 2001 From: Henry Date: Tue, 28 Apr 2015 12:33:02 +0200 Subject: [PATCH 0854/3817] godeps: move (go)goprotobuf to github location This commit was moved from ipfs/go-merkledag@1e0db6dab82bdeda6aaa3bdb490d9a80b3499aee --- ipld/merkledag/internal/pb/merkledag.pb.go | 96 +++++------ .../merkledag/internal/pb/merkledagpb_test.go | 154 ++++++++---------- 2 files changed, 114 insertions(+), 136 deletions(-) diff --git a/ipld/merkledag/internal/pb/merkledag.pb.go b/ipld/merkledag/internal/pb/merkledag.pb.go index 18e480897..a0dfa91f8 100644 --- a/ipld/merkledag/internal/pb/merkledag.pb.go +++ b/ipld/merkledag/internal/pb/merkledag.pb.go @@ -14,27 +14,21 @@ */ package merkledag_pb -import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" import math "math" // discarding unused import gogoproto "code.google.com/p/gogoprotobuf/gogoproto/gogo.pb" import io "io" import fmt "fmt" -import code_google_com_p_gogoprotobuf_proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import github_com_gogo_protobuf_proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" -import fmt1 "fmt" import strings "strings" import reflect "reflect" -import fmt2 "fmt" -import strings1 "strings" -import code_google_com_p_gogoprotobuf_proto1 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" import sort "sort" import strconv "strconv" -import reflect1 "reflect" -import fmt3 "fmt" import bytes "bytes" // Reference imports to suppress errors if they are not otherwise used. @@ -143,7 +137,7 @@ func (m *PBLink) Unmarshal(data []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Hash = append(m.Hash, data[index:postIndex]...) + m.Hash = append([]byte{}, data[index:postIndex]...) index = postIndex case 2: if wireType != 2 { @@ -195,7 +189,7 @@ func (m *PBLink) Unmarshal(data []byte) error { } } index -= sizeOfWire - skippy, err := code_google_com_p_gogoprotobuf_proto.Skip(data[index:]) + skippy, err := github_com_gogo_protobuf_proto.Skip(data[index:]) if err != nil { return err } @@ -270,7 +264,7 @@ func (m *PBNode) Unmarshal(data []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Data = append(m.Data, data[index:postIndex]...) + m.Data = append([]byte{}, data[index:postIndex]...) index = postIndex default: var sizeOfWire int @@ -282,7 +276,7 @@ func (m *PBNode) Unmarshal(data []byte) error { } } index -= sizeOfWire - skippy, err := code_google_com_p_gogoprotobuf_proto.Skip(data[index:]) + skippy, err := github_com_gogo_protobuf_proto.Skip(data[index:]) if err != nil { return err } @@ -303,7 +297,7 @@ func (this *PBLink) String() string { `Hash:` + valueToStringMerkledag(this.Hash) + `,`, `Name:` + valueToStringMerkledag(this.Name) + `,`, `Tsize:` + valueToStringMerkledag(this.Tsize) + `,`, - `XXX_unrecognized:` + fmt1.Sprintf("%v", this.XXX_unrecognized) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -313,9 +307,9 @@ func (this *PBNode) String() string { return "nil" } s := strings.Join([]string{`&PBNode{`, - `Links:` + strings.Replace(fmt1.Sprintf("%v", this.Links), "PBLink", "PBLink", 1) + `,`, + `Links:` + strings.Replace(fmt.Sprintf("%v", this.Links), "PBLink", "PBLink", 1) + `,`, `Data:` + valueToStringMerkledag(this.Data) + `,`, - `XXX_unrecognized:` + fmt1.Sprintf("%v", this.XXX_unrecognized) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -326,7 +320,7 @@ func valueToStringMerkledag(v interface{}) string { return "nil" } pv := reflect.Indirect(rv).Interface() - return fmt1.Sprintf("*%v", pv) + return fmt.Sprintf("*%v", pv) } func (m *PBLink) Size() (n int) { var l int @@ -347,6 +341,7 @@ func (m *PBLink) Size() (n int) { } return n } + func (m *PBNode) Size() (n int) { var l int _ = l @@ -434,11 +429,7 @@ type randyMerkledag interface { } func randUTF8RuneMerkledag(r randyMerkledag) rune { - res := rune(r.Uint32() % 1112064) - if 55296 <= res { - res += 2047 - } - return res + return rune(r.Intn(126-43) + 43) } func randStringMerkledag(r randyMerkledag) string { v6 := r.Intn(100) @@ -531,6 +522,7 @@ func (m *PBLink) MarshalTo(data []byte) (n int, err error) { } return i, nil } + func (m *PBNode) Marshal() (data []byte, err error) { size := m.Size() data = make([]byte, size) @@ -569,6 +561,7 @@ func (m *PBNode) MarshalTo(data []byte) (n int, err error) { } return i, nil } + func encodeFixed64Merkledag(data []byte, offset int, v uint64) int { data[offset] = uint8(v) data[offset+1] = uint8(v >> 8) @@ -600,25 +593,32 @@ func (this *PBLink) GoString() string { if this == nil { return "nil" } - s := strings1.Join([]string{`&merkledag_pb.PBLink{` + `Hash:` + valueToGoStringMerkledag(this.Hash, "byte"), `Name:` + valueToGoStringMerkledag(this.Name, "string"), `Tsize:` + valueToGoStringMerkledag(this.Tsize, "uint64"), `XXX_unrecognized:` + fmt2.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") + s := strings.Join([]string{`&merkledag_pb.PBLink{` + + `Hash:` + valueToGoStringMerkledag(this.Hash, "byte"), + `Name:` + valueToGoStringMerkledag(this.Name, "string"), + `Tsize:` + valueToGoStringMerkledag(this.Tsize, "uint64"), + `XXX_unrecognized:` + fmt.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") return s } func (this *PBNode) GoString() string { if this == nil { return "nil" } - s := strings1.Join([]string{`&merkledag_pb.PBNode{` + `Links:` + fmt2.Sprintf("%#v", this.Links), `Data:` + valueToGoStringMerkledag(this.Data, "byte"), `XXX_unrecognized:` + fmt2.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") + s := strings.Join([]string{`&merkledag_pb.PBNode{` + + `Links:` + fmt.Sprintf("%#v", this.Links), + `Data:` + valueToGoStringMerkledag(this.Data, "byte"), + `XXX_unrecognized:` + fmt.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") return s } func valueToGoStringMerkledag(v interface{}, typ string) string { - rv := reflect1.ValueOf(v) + rv := reflect.ValueOf(v) if rv.IsNil() { return "nil" } - pv := reflect1.Indirect(rv).Interface() - return fmt2.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) } -func extensionToGoStringMerkledag(e map[int32]code_google_com_p_gogoprotobuf_proto1.Extension) string { +func extensionToGoStringMerkledag(e map[int32]github_com_gogo_protobuf_proto.Extension) string { if e == nil { return "nil" } @@ -632,7 +632,7 @@ func extensionToGoStringMerkledag(e map[int32]code_google_com_p_gogoprotobuf_pro for _, k := range keys { ss = append(ss, strconv.Itoa(k)+": "+e[int32(k)].GoString()) } - s += strings1.Join(ss, ",") + "}" + s += strings.Join(ss, ",") + "}" return s } func (this *PBLink) VerboseEqual(that interface{}) error { @@ -640,44 +640,44 @@ func (this *PBLink) VerboseEqual(that interface{}) error { if this == nil { return nil } - return fmt3.Errorf("that == nil && this != nil") + return fmt.Errorf("that == nil && this != nil") } that1, ok := that.(*PBLink) if !ok { - return fmt3.Errorf("that is not of type *PBLink") + return fmt.Errorf("that is not of type *PBLink") } if that1 == nil { if this == nil { return nil } - return fmt3.Errorf("that is type *PBLink but is nil && this != nil") + return fmt.Errorf("that is type *PBLink but is nil && this != nil") } else if this == nil { - return fmt3.Errorf("that is type *PBLinkbut is not nil && this == nil") + return fmt.Errorf("that is type *PBLinkbut is not nil && this == nil") } if !bytes.Equal(this.Hash, that1.Hash) { - return fmt3.Errorf("Hash this(%v) Not Equal that(%v)", this.Hash, that1.Hash) + return fmt.Errorf("Hash this(%v) Not Equal that(%v)", this.Hash, that1.Hash) } if this.Name != nil && that1.Name != nil { if *this.Name != *that1.Name { - return fmt3.Errorf("Name this(%v) Not Equal that(%v)", *this.Name, *that1.Name) + return fmt.Errorf("Name this(%v) Not Equal that(%v)", *this.Name, *that1.Name) } } else if this.Name != nil { - return fmt3.Errorf("this.Name == nil && that.Name != nil") + return fmt.Errorf("this.Name == nil && that.Name != nil") } else if that1.Name != nil { - return fmt3.Errorf("Name this(%v) Not Equal that(%v)", this.Name, that1.Name) + return fmt.Errorf("Name this(%v) Not Equal that(%v)", this.Name, that1.Name) } if this.Tsize != nil && that1.Tsize != nil { if *this.Tsize != *that1.Tsize { - return fmt3.Errorf("Tsize this(%v) Not Equal that(%v)", *this.Tsize, *that1.Tsize) + return fmt.Errorf("Tsize this(%v) Not Equal that(%v)", *this.Tsize, *that1.Tsize) } } else if this.Tsize != nil { - return fmt3.Errorf("this.Tsize == nil && that.Tsize != nil") + return fmt.Errorf("this.Tsize == nil && that.Tsize != nil") } else if that1.Tsize != nil { - return fmt3.Errorf("Tsize this(%v) Not Equal that(%v)", this.Tsize, that1.Tsize) + return fmt.Errorf("Tsize this(%v) Not Equal that(%v)", this.Tsize, that1.Tsize) } if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { - return fmt3.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) + return fmt.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) } return nil } @@ -732,34 +732,34 @@ func (this *PBNode) VerboseEqual(that interface{}) error { if this == nil { return nil } - return fmt3.Errorf("that == nil && this != nil") + return fmt.Errorf("that == nil && this != nil") } that1, ok := that.(*PBNode) if !ok { - return fmt3.Errorf("that is not of type *PBNode") + return fmt.Errorf("that is not of type *PBNode") } if that1 == nil { if this == nil { return nil } - return fmt3.Errorf("that is type *PBNode but is nil && this != nil") + return fmt.Errorf("that is type *PBNode but is nil && this != nil") } else if this == nil { - return fmt3.Errorf("that is type *PBNodebut is not nil && this == nil") + return fmt.Errorf("that is type *PBNodebut is not nil && this == nil") } if len(this.Links) != len(that1.Links) { - return fmt3.Errorf("Links this(%v) Not Equal that(%v)", len(this.Links), len(that1.Links)) + return fmt.Errorf("Links this(%v) Not Equal that(%v)", len(this.Links), len(that1.Links)) } for i := range this.Links { if !this.Links[i].Equal(that1.Links[i]) { - return fmt3.Errorf("Links this[%v](%v) Not Equal that[%v](%v)", i, this.Links[i], i, that1.Links[i]) + return fmt.Errorf("Links this[%v](%v) Not Equal that[%v](%v)", i, this.Links[i], i, that1.Links[i]) } } if !bytes.Equal(this.Data, that1.Data) { - return fmt3.Errorf("Data this(%v) Not Equal that(%v)", this.Data, that1.Data) + return fmt.Errorf("Data this(%v) Not Equal that(%v)", this.Data, that1.Data) } if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { - return fmt3.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) + return fmt.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) } return nil } diff --git a/ipld/merkledag/internal/pb/merkledagpb_test.go b/ipld/merkledag/internal/pb/merkledagpb_test.go index 00c05e4c9..dd55e2230 100644 --- a/ipld/merkledag/internal/pb/merkledagpb_test.go +++ b/ipld/merkledag/internal/pb/merkledagpb_test.go @@ -17,42 +17,20 @@ package merkledag_pb import testing "testing" import math_rand "math/rand" import time "time" -import code_google_com_p_gogoprotobuf_proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" -import testing1 "testing" -import math_rand1 "math/rand" -import time1 "time" +import github_com_gogo_protobuf_proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" import encoding_json "encoding/json" -import testing2 "testing" -import math_rand2 "math/rand" -import time2 "time" -import code_google_com_p_gogoprotobuf_proto1 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" -import math_rand3 "math/rand" -import time3 "time" -import testing3 "testing" import fmt "fmt" -import math_rand4 "math/rand" -import time4 "time" -import testing4 "testing" -import code_google_com_p_gogoprotobuf_proto2 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" -import math_rand5 "math/rand" -import time5 "time" -import testing5 "testing" -import fmt1 "fmt" import go_parser "go/parser" -import math_rand6 "math/rand" -import time6 "time" -import testing6 "testing" -import code_google_com_p_gogoprotobuf_proto3 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" func TestPBLinkProto(t *testing.T) { popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBLink(popr, false) - data, err := code_google_com_p_gogoprotobuf_proto.Marshal(p) + data, err := github_com_gogo_protobuf_proto.Marshal(p) if err != nil { panic(err) } msg := &PBLink{} - if err := code_google_com_p_gogoprotobuf_proto.Unmarshal(data, msg); err != nil { + if err := github_com_gogo_protobuf_proto.Unmarshal(data, msg); err != nil { panic(err) } for i := range data { @@ -79,7 +57,7 @@ func TestPBLinkMarshalTo(t *testing.T) { panic(err) } msg := &PBLink{} - if err := code_google_com_p_gogoprotobuf_proto.Unmarshal(data, msg); err != nil { + if err := github_com_gogo_protobuf_proto.Unmarshal(data, msg); err != nil { panic(err) } for i := range data { @@ -102,7 +80,7 @@ func BenchmarkPBLinkProtoMarshal(b *testing.B) { } b.ResetTimer() for i := 0; i < b.N; i++ { - data, err := code_google_com_p_gogoprotobuf_proto.Marshal(pops[i%10000]) + data, err := github_com_gogo_protobuf_proto.Marshal(pops[i%10000]) if err != nil { panic(err) } @@ -116,7 +94,7 @@ func BenchmarkPBLinkProtoUnmarshal(b *testing.B) { total := 0 datas := make([][]byte, 10000) for i := 0; i < 10000; i++ { - data, err := code_google_com_p_gogoprotobuf_proto.Marshal(NewPopulatedPBLink(popr, false)) + data, err := github_com_gogo_protobuf_proto.Marshal(NewPopulatedPBLink(popr, false)) if err != nil { panic(err) } @@ -126,7 +104,7 @@ func BenchmarkPBLinkProtoUnmarshal(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { total += len(datas[i%10000]) - if err := code_google_com_p_gogoprotobuf_proto.Unmarshal(datas[i%10000], msg); err != nil { + if err := github_com_gogo_protobuf_proto.Unmarshal(datas[i%10000], msg); err != nil { panic(err) } } @@ -136,12 +114,12 @@ func BenchmarkPBLinkProtoUnmarshal(b *testing.B) { func TestPBNodeProto(t *testing.T) { popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBNode(popr, false) - data, err := code_google_com_p_gogoprotobuf_proto.Marshal(p) + data, err := github_com_gogo_protobuf_proto.Marshal(p) if err != nil { panic(err) } msg := &PBNode{} - if err := code_google_com_p_gogoprotobuf_proto.Unmarshal(data, msg); err != nil { + if err := github_com_gogo_protobuf_proto.Unmarshal(data, msg); err != nil { panic(err) } for i := range data { @@ -168,7 +146,7 @@ func TestPBNodeMarshalTo(t *testing.T) { panic(err) } msg := &PBNode{} - if err := code_google_com_p_gogoprotobuf_proto.Unmarshal(data, msg); err != nil { + if err := github_com_gogo_protobuf_proto.Unmarshal(data, msg); err != nil { panic(err) } for i := range data { @@ -191,7 +169,7 @@ func BenchmarkPBNodeProtoMarshal(b *testing.B) { } b.ResetTimer() for i := 0; i < b.N; i++ { - data, err := code_google_com_p_gogoprotobuf_proto.Marshal(pops[i%10000]) + data, err := github_com_gogo_protobuf_proto.Marshal(pops[i%10000]) if err != nil { panic(err) } @@ -205,7 +183,7 @@ func BenchmarkPBNodeProtoUnmarshal(b *testing.B) { total := 0 datas := make([][]byte, 10000) for i := 0; i < 10000; i++ { - data, err := code_google_com_p_gogoprotobuf_proto.Marshal(NewPopulatedPBNode(popr, false)) + data, err := github_com_gogo_protobuf_proto.Marshal(NewPopulatedPBNode(popr, false)) if err != nil { panic(err) } @@ -215,15 +193,15 @@ func BenchmarkPBNodeProtoUnmarshal(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { total += len(datas[i%10000]) - if err := code_google_com_p_gogoprotobuf_proto.Unmarshal(datas[i%10000], msg); err != nil { + if err := github_com_gogo_protobuf_proto.Unmarshal(datas[i%10000], msg); err != nil { panic(err) } } b.SetBytes(int64(total / b.N)) } -func TestPBLinkJSON(t *testing1.T) { - popr := math_rand1.New(math_rand1.NewSource(time1.Now().UnixNano())) +func TestPBLinkJSON(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBLink(popr, true) jsondata, err := encoding_json.Marshal(p) if err != nil { @@ -241,8 +219,8 @@ func TestPBLinkJSON(t *testing1.T) { t.Fatalf("%#v !Json Equal %#v", msg, p) } } -func TestPBNodeJSON(t *testing1.T) { - popr := math_rand1.New(math_rand1.NewSource(time1.Now().UnixNano())) +func TestPBNodeJSON(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBNode(popr, true) jsondata, err := encoding_json.Marshal(p) if err != nil { @@ -260,12 +238,12 @@ func TestPBNodeJSON(t *testing1.T) { t.Fatalf("%#v !Json Equal %#v", msg, p) } } -func TestPBLinkProtoText(t *testing2.T) { - popr := math_rand2.New(math_rand2.NewSource(time2.Now().UnixNano())) +func TestPBLinkProtoText(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBLink(popr, true) - data := code_google_com_p_gogoprotobuf_proto1.MarshalTextString(p) + data := github_com_gogo_protobuf_proto.MarshalTextString(p) msg := &PBLink{} - if err := code_google_com_p_gogoprotobuf_proto1.UnmarshalText(data, msg); err != nil { + if err := github_com_gogo_protobuf_proto.UnmarshalText(data, msg); err != nil { panic(err) } if err := p.VerboseEqual(msg); err != nil { @@ -276,12 +254,12 @@ func TestPBLinkProtoText(t *testing2.T) { } } -func TestPBLinkProtoCompactText(t *testing2.T) { - popr := math_rand2.New(math_rand2.NewSource(time2.Now().UnixNano())) +func TestPBLinkProtoCompactText(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBLink(popr, true) - data := code_google_com_p_gogoprotobuf_proto1.CompactTextString(p) + data := github_com_gogo_protobuf_proto.CompactTextString(p) msg := &PBLink{} - if err := code_google_com_p_gogoprotobuf_proto1.UnmarshalText(data, msg); err != nil { + if err := github_com_gogo_protobuf_proto.UnmarshalText(data, msg); err != nil { panic(err) } if err := p.VerboseEqual(msg); err != nil { @@ -292,12 +270,12 @@ func TestPBLinkProtoCompactText(t *testing2.T) { } } -func TestPBNodeProtoText(t *testing2.T) { - popr := math_rand2.New(math_rand2.NewSource(time2.Now().UnixNano())) +func TestPBNodeProtoText(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBNode(popr, true) - data := code_google_com_p_gogoprotobuf_proto1.MarshalTextString(p) + data := github_com_gogo_protobuf_proto.MarshalTextString(p) msg := &PBNode{} - if err := code_google_com_p_gogoprotobuf_proto1.UnmarshalText(data, msg); err != nil { + if err := github_com_gogo_protobuf_proto.UnmarshalText(data, msg); err != nil { panic(err) } if err := p.VerboseEqual(msg); err != nil { @@ -308,12 +286,12 @@ func TestPBNodeProtoText(t *testing2.T) { } } -func TestPBNodeProtoCompactText(t *testing2.T) { - popr := math_rand2.New(math_rand2.NewSource(time2.Now().UnixNano())) +func TestPBNodeProtoCompactText(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBNode(popr, true) - data := code_google_com_p_gogoprotobuf_proto1.CompactTextString(p) + data := github_com_gogo_protobuf_proto.CompactTextString(p) msg := &PBNode{} - if err := code_google_com_p_gogoprotobuf_proto1.UnmarshalText(data, msg); err != nil { + if err := github_com_gogo_protobuf_proto.UnmarshalText(data, msg); err != nil { panic(err) } if err := p.VerboseEqual(msg); err != nil { @@ -324,8 +302,8 @@ func TestPBNodeProtoCompactText(t *testing2.T) { } } -func TestPBLinkStringer(t *testing3.T) { - popr := math_rand3.New(math_rand3.NewSource(time3.Now().UnixNano())) +func TestPBLinkStringer(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBLink(popr, false) s1 := p.String() s2 := fmt.Sprintf("%v", p) @@ -333,8 +311,8 @@ func TestPBLinkStringer(t *testing3.T) { t.Fatalf("String want %v got %v", s1, s2) } } -func TestPBNodeStringer(t *testing3.T) { - popr := math_rand3.New(math_rand3.NewSource(time3.Now().UnixNano())) +func TestPBNodeStringer(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBNode(popr, false) s1 := p.String() s2 := fmt.Sprintf("%v", p) @@ -342,11 +320,11 @@ func TestPBNodeStringer(t *testing3.T) { t.Fatalf("String want %v got %v", s1, s2) } } -func TestPBLinkSize(t *testing4.T) { - popr := math_rand4.New(math_rand4.NewSource(time4.Now().UnixNano())) +func TestPBLinkSize(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBLink(popr, true) - size2 := code_google_com_p_gogoprotobuf_proto2.Size(p) - data, err := code_google_com_p_gogoprotobuf_proto2.Marshal(p) + size2 := github_com_gogo_protobuf_proto.Size(p) + data, err := github_com_gogo_protobuf_proto.Marshal(p) if err != nil { panic(err) } @@ -357,14 +335,14 @@ func TestPBLinkSize(t *testing4.T) { if size2 != size { t.Fatalf("size %v != before marshal proto.Size %v", size, size2) } - size3 := code_google_com_p_gogoprotobuf_proto2.Size(p) + size3 := github_com_gogo_protobuf_proto.Size(p) if size3 != size { t.Fatalf("size %v != after marshal proto.Size %v", size, size3) } } -func BenchmarkPBLinkSize(b *testing4.B) { - popr := math_rand4.New(math_rand4.NewSource(616)) +func BenchmarkPBLinkSize(b *testing.B) { + popr := math_rand.New(math_rand.NewSource(616)) total := 0 pops := make([]*PBLink, 1000) for i := 0; i < 1000; i++ { @@ -377,11 +355,11 @@ func BenchmarkPBLinkSize(b *testing4.B) { b.SetBytes(int64(total / b.N)) } -func TestPBNodeSize(t *testing4.T) { - popr := math_rand4.New(math_rand4.NewSource(time4.Now().UnixNano())) +func TestPBNodeSize(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBNode(popr, true) - size2 := code_google_com_p_gogoprotobuf_proto2.Size(p) - data, err := code_google_com_p_gogoprotobuf_proto2.Marshal(p) + size2 := github_com_gogo_protobuf_proto.Size(p) + data, err := github_com_gogo_protobuf_proto.Marshal(p) if err != nil { panic(err) } @@ -392,14 +370,14 @@ func TestPBNodeSize(t *testing4.T) { if size2 != size { t.Fatalf("size %v != before marshal proto.Size %v", size, size2) } - size3 := code_google_com_p_gogoprotobuf_proto2.Size(p) + size3 := github_com_gogo_protobuf_proto.Size(p) if size3 != size { t.Fatalf("size %v != after marshal proto.Size %v", size, size3) } } -func BenchmarkPBNodeSize(b *testing4.B) { - popr := math_rand4.New(math_rand4.NewSource(616)) +func BenchmarkPBNodeSize(b *testing.B) { + popr := math_rand.New(math_rand.NewSource(616)) total := 0 pops := make([]*PBNode, 1000) for i := 0; i < 1000; i++ { @@ -412,11 +390,11 @@ func BenchmarkPBNodeSize(b *testing4.B) { b.SetBytes(int64(total / b.N)) } -func TestPBLinkGoString(t *testing5.T) { - popr := math_rand5.New(math_rand5.NewSource(time5.Now().UnixNano())) +func TestPBLinkGoString(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBLink(popr, false) s1 := p.GoString() - s2 := fmt1.Sprintf("%#v", p) + s2 := fmt.Sprintf("%#v", p) if s1 != s2 { t.Fatalf("GoString want %v got %v", s1, s2) } @@ -425,11 +403,11 @@ func TestPBLinkGoString(t *testing5.T) { panic(err) } } -func TestPBNodeGoString(t *testing5.T) { - popr := math_rand5.New(math_rand5.NewSource(time5.Now().UnixNano())) +func TestPBNodeGoString(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBNode(popr, false) s1 := p.GoString() - s2 := fmt1.Sprintf("%#v", p) + s2 := fmt.Sprintf("%#v", p) if s1 != s2 { t.Fatalf("GoString want %v got %v", s1, s2) } @@ -438,30 +416,30 @@ func TestPBNodeGoString(t *testing5.T) { panic(err) } } -func TestPBLinkVerboseEqual(t *testing6.T) { - popr := math_rand6.New(math_rand6.NewSource(time6.Now().UnixNano())) +func TestPBLinkVerboseEqual(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBLink(popr, false) - data, err := code_google_com_p_gogoprotobuf_proto3.Marshal(p) + data, err := github_com_gogo_protobuf_proto.Marshal(p) if err != nil { panic(err) } msg := &PBLink{} - if err := code_google_com_p_gogoprotobuf_proto3.Unmarshal(data, msg); err != nil { + if err := github_com_gogo_protobuf_proto.Unmarshal(data, msg); err != nil { panic(err) } if err := p.VerboseEqual(msg); err != nil { t.Fatalf("%#v !VerboseEqual %#v, since %v", msg, p, err) } } -func TestPBNodeVerboseEqual(t *testing6.T) { - popr := math_rand6.New(math_rand6.NewSource(time6.Now().UnixNano())) +func TestPBNodeVerboseEqual(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBNode(popr, false) - data, err := code_google_com_p_gogoprotobuf_proto3.Marshal(p) + data, err := github_com_gogo_protobuf_proto.Marshal(p) if err != nil { panic(err) } msg := &PBNode{} - if err := code_google_com_p_gogoprotobuf_proto3.Unmarshal(data, msg); err != nil { + if err := github_com_gogo_protobuf_proto.Unmarshal(data, msg); err != nil { panic(err) } if err := p.VerboseEqual(msg); err != nil { @@ -469,4 +447,4 @@ func TestPBNodeVerboseEqual(t *testing6.T) { } } -//These tests are generated by code.google.com/p/gogoprotobuf/plugin/testgen +//These tests are generated by github.com/gogo/protobuf/plugin/testgen From a87156067cb9a31e34985ec87837d4f7fc9cefe7 Mon Sep 17 00:00:00 2001 From: Henry Date: Tue, 28 Apr 2015 12:33:02 +0200 Subject: [PATCH 0855/3817] godeps: move (go)goprotobuf to github location This commit was moved from ipfs/go-ipfs-routing@ca42d82f2660f846bee3bd03c8774aa64120b3b3 --- routing/dht/dht.go | 2 +- routing/dht/dht_net.go | 2 +- routing/dht/ext_test.go | 10 +++++----- routing/dht/handlers.go | 2 +- routing/dht/pb/dht.pb.go | 6 ++---- routing/offline/offline.go | 2 +- routing/record/record.go | 2 +- routing/supernode/client.go | 3 ++- routing/supernode/proxy/loopback.go | 3 ++- routing/supernode/proxy/standard.go | 5 +++-- routing/supernode/server.go | 3 ++- 11 files changed, 21 insertions(+), 19 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index d34f37a56..8c5ceaa61 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -21,7 +21,7 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/eventlog" u "github.com/ipfs/go-ipfs/util" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ctxgroup "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 92fec8ec6..44767fbe4 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -9,7 +9,7 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" ctxutil "github.com/ipfs/go-ipfs/util/ctx" - ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" + ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index efe62cd7c..5ac342e3a 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -7,6 +7,11 @@ import ( "testing" "time" + ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + inet "github.com/ipfs/go-ipfs/p2p/net" mocknet "github.com/ipfs/go-ipfs/p2p/net/mock" peer "github.com/ipfs/go-ipfs/p2p/peer" @@ -14,11 +19,6 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" u "github.com/ipfs/go-ipfs/util" - - ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 279ac82e4..5449cad43 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" peer "github.com/ipfs/go-ipfs/p2p/peer" diff --git a/routing/dht/pb/dht.pb.go b/routing/dht/pb/dht.pb.go index 78532e95b..9a313a897 100644 --- a/routing/dht/pb/dht.pb.go +++ b/routing/dht/pb/dht.pb.go @@ -14,13 +14,11 @@ It has these top-level messages: */ package dht_pb -import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" -import json "encoding/json" +import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" import math "math" -// Reference proto, json, and math imports to suppress error if they are not otherwise used. +// Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal -var _ = &json.SyntaxError{} var _ = math.Inf type Message_MessageType int32 diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 0e5bed248..a94e0c3c7 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -4,7 +4,7 @@ import ( "errors" "time" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ci "github.com/ipfs/go-ipfs/p2p/crypto" diff --git a/routing/record/record.go b/routing/record/record.go index 2d9ab18e2..ae423a172 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -3,7 +3,7 @@ package record import ( "bytes" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ci "github.com/ipfs/go-ipfs/p2p/crypto" pb "github.com/ipfs/go-ipfs/routing/dht/pb" diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 13f845abe..15c3a4086 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -5,8 +5,9 @@ import ( "errors" "time" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "github.com/ipfs/go-ipfs/p2p/host" peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 06e91707c..1437b574a 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -1,8 +1,9 @@ package proxy import ( - ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" + ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + inet "github.com/ipfs/go-ipfs/p2p/net" peer "github.com/ipfs/go-ipfs/p2p/peer" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 7f4d38faa..10625f180 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -3,15 +3,16 @@ package proxy import ( "errors" - ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io" + ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + host "github.com/ipfs/go-ipfs/p2p/host" inet "github.com/ipfs/go-ipfs/p2p/net" peer "github.com/ipfs/go-ipfs/p2p/peer" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" - "github.com/ipfs/go-ipfs/util" + util "github.com/ipfs/go-ipfs/util" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index fb077e882..46205d0e4 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -4,9 +4,10 @@ import ( "errors" "fmt" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + peer "github.com/ipfs/go-ipfs/p2p/peer" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" From 4bc752e36401d1d4c3776648a57590b55b2703a3 Mon Sep 17 00:00:00 2001 From: Henry Date: Tue, 28 Apr 2015 12:33:02 +0200 Subject: [PATCH 0856/3817] godeps: move (go)goprotobuf to github location This commit was moved from ipfs/go-namesys@a5e860a79458dd35152027c62598d354ae5c7074 --- namesys/internal/pb/namesys.pb.go | 2 +- namesys/publisher.go | 2 +- namesys/routing.go | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/namesys/internal/pb/namesys.pb.go b/namesys/internal/pb/namesys.pb.go index 637d02306..97e25a855 100644 --- a/namesys/internal/pb/namesys.pb.go +++ b/namesys/internal/pb/namesys.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package namesys_pb -import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto" +import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. diff --git a/namesys/publisher.go b/namesys/publisher.go index 23e15ca71..38dd8d082 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -6,7 +6,7 @@ import ( "fmt" "time" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" dag "github.com/ipfs/go-ipfs/merkledag" diff --git a/namesys/routing.go b/namesys/routing.go index 5e0cf1a96..38cb250d0 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -3,9 +3,10 @@ package namesys import ( "fmt" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + pb "github.com/ipfs/go-ipfs/namesys/internal/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" From 3defe1d67e3da282e85d76a977f5c4a5ebc04874 Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Tue, 28 Apr 2015 16:05:52 -0700 Subject: [PATCH 0857/3817] blocks: Don't re-Put blocks we already have Commit 1192be196b3d0acca2e2dce5ffd5d12a924fdc5a tried to do this, but had a simple mistake. Functions returning `bool, error` pretty much never return `true, anError`, so that branch was never taken. Also fix the partial sentence in the This commit was moved from ipfs/go-ipfs-blockstore@eb50103bfcd9481e75d8452e1ee97ba47c0f0d50 --- blockstore/blockstore.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index ccc7e8fc5..244d4578a 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -64,10 +64,11 @@ func (bs *blockstore) Get(k u.Key) (*blocks.Block, error) { } func (bs *blockstore) Put(block *blocks.Block) error { - // Has is cheaper than k := block.Key().DsKey() + + // Has is cheaper than Put, so see if we already have it exists, err := bs.datastore.Has(k) - if err != nil && exists { + if err == nil && exists { return nil // already stored. } return bs.datastore.Put(k, block.Data) From 48fd4263b292eb71ea057553e2450e2c91b951ec Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 30 Apr 2015 21:33:43 -0700 Subject: [PATCH 0858/3817] blockservice/worker/bench/main: Add a package comment This commit was moved from ipfs/go-blockservice@7b2d310c2567433daa462c6d8f4e722a88dc4fb8 --- blockservice/worker/bench/main.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/blockservice/worker/bench/main.go b/blockservice/worker/bench/main.go index 82c3dee13..c23770f78 100644 --- a/blockservice/worker/bench/main.go +++ b/blockservice/worker/bench/main.go @@ -1,3 +1,12 @@ +/* +Benchmark github.com/ipfs/go-ipfs/blockservice/worker. + +Loop over a range of workers and buffer sizes and measure the time it +per block-transfer operation for each value. Run with: + + $ go run "${GOPATH}/src/github.com/ipfs/go-ipfs/blockservice/worker/bench/main.go" +*/ + package main import ( From 6c02420d9e6b8ff06274220b1a8ff6c2bc01eba6 Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 1 May 2015 17:33:24 +0200 Subject: [PATCH 0859/3817] core: add context.Context param to core.Resolve() commands/object: remove objectData() and objectLinks() helpers resolver: added context parameters sharness: $HASH carried the \r from the http protocol with sharness: write curl output to individual files http gw: break PUT handler until PR#1191 This commit was moved from ipfs/go-path@992c6a5e7251f2d2b44133ac6afed7c8bff19188 --- path/resolver.go | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index 27aa2a0eb..b24f45f21 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -1,4 +1,4 @@ -// package path implements utilities for resolving paths within ipfs. +// Package path implements utilities for resolving paths within ipfs. package path import ( @@ -7,6 +7,7 @@ import ( mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + merkledag "github.com/ipfs/go-ipfs/merkledag" u "github.com/ipfs/go-ipfs/util" ) @@ -57,33 +58,32 @@ func SplitAbsPath(fpath Path) (mh.Multihash, []string, error) { // ResolvePath fetches the node for given path. It returns the last item // returned by ResolvePathComponents. -func (s *Resolver) ResolvePath(fpath Path) (*merkledag.Node, error) { - nodes, err := s.ResolvePathComponents(fpath) +func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (*merkledag.Node, error) { + nodes, err := s.ResolvePathComponents(ctx, fpath) if err != nil || nodes == nil { return nil, err - } else { - return nodes[len(nodes)-1], err } + return nodes[len(nodes)-1], err } // ResolvePathComponents fetches the nodes for each segment of the given path. // It uses the first path component as a hash (key) of the first node, then // resolves all other components walking the links, with ResolveLinks. -func (s *Resolver) ResolvePathComponents(fpath Path) ([]*merkledag.Node, error) { +func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]*merkledag.Node, error) { h, parts, err := SplitAbsPath(fpath) if err != nil { return nil, err } - log.Debug("Resolve dag get.\n") - ctx, cancel := context.WithTimeout(context.TODO(), time.Minute) + log.Debug("Resolve dag get.") + ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() nd, err := s.DAG.Get(ctx, u.Key(h)) if err != nil { return nil, err } - return s.ResolveLinks(nd, parts) + return s.ResolveLinks(ctx, nd, parts) } // ResolveLinks iteratively resolves names by walking the link hierarchy. @@ -93,10 +93,9 @@ func (s *Resolver) ResolvePathComponents(fpath Path) ([]*merkledag.Node, error) // // ResolveLinks(nd, []string{"foo", "bar", "baz"}) // would retrieve "baz" in ("bar" in ("foo" in nd.Links).Links).Links -func (s *Resolver) ResolveLinks(ndd *merkledag.Node, names []string) ( - result []*merkledag.Node, err error) { +func (s *Resolver) ResolveLinks(ctx context.Context, ndd *merkledag.Node, names []string) ([]*merkledag.Node, error) { - result = make([]*merkledag.Node, 0, len(names)+1) + result := make([]*merkledag.Node, 0, len(names)+1) result = append(result, ndd) nd := ndd // dup arg workaround @@ -121,9 +120,9 @@ func (s *Resolver) ResolveLinks(ndd *merkledag.Node, names []string) ( if nlink.Node == nil { // fetch object for link and assign to nd - ctx, cancel := context.WithTimeout(context.TODO(), time.Minute) + ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() - nd, err = s.DAG.Get(ctx, next) + nd, err := s.DAG.Get(ctx, next) if err != nil { return append(result, nd), err } @@ -134,5 +133,5 @@ func (s *Resolver) ResolveLinks(ndd *merkledag.Node, names []string) ( result = append(result, nlink.Node) } - return + return result, nil } From b13a5f9e8c8076ed7eab0f22443ffc8839bde73e Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 8 May 2015 16:07:01 -0700 Subject: [PATCH 0860/3817] path/resolver: Fix recursive path resolution I'm not entirely clear on Go's scoping (there's some text I can't quite parse here [1]), but it seems like the := version (because this is the first time we use 'err') was masking the function-level 'nd' just for this if block. That means that after we get out of the if block and return to the start of the for-loop for the next pass, nd.Links would still be pointing at the original object's links. This commit drops the :=, which fixes the earlier: $ ipfs ls QmXX7YRpU7nNBKfw75VG7Y1c3GwpSAGHRev67XVPgZFv9R/static/css Error: no link named "css" under QmXX7YRpU7nNBKfw75VG7Y1c3GwpSAGHRev67XVPgZFv9R so we get the intended: $ ipfs ls QmXX7YRpU7nNBKfw75VG7Y1c3GwpSAGHRev67XVPgZFv9R/static/css Qme4r3eA4h1revFBgCEv1HF1U7sLL4vvAyzRLWJhCFhwg2 7051 style.css It also means we're probably missing (or are unreliably using) a multi-level-path-resolving test. [1]: https://golang.org/ref/spec#Declarations_and_scope This commit was moved from ipfs/go-path@05aceed0788e7fae65fd02c20bc0da58dd1376c4 --- path/resolver.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/path/resolver.go b/path/resolver.go index b24f45f21..a08df8741 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -122,7 +122,8 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd *merkledag.Node, names // fetch object for link and assign to nd ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() - nd, err := s.DAG.Get(ctx, next) + var err error + nd, err = s.DAG.Get(ctx, next) if err != nil { return append(result, nd), err } From 4c095e5d63a3ef264aa9de0d34c8be8dd170f46d Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 8 May 2015 21:43:43 -0700 Subject: [PATCH 0861/3817] path/resolver_test: Test recursive Link resolution Setup a three-level graph: a -(child)-> b -(grandchild)-> c and then try and resolve: /ipfs//child/grandchild Before 10669e8b (path/resolver: Fix recursive path resolution, 2015-05-08) this failed with: resolver_test.go:71: no link named "grandchild" under QmSomeRandomHash The boilerplate for this test is from pin/pin_test.go, and I make no claims that it's the best way to setup the test graph ;). This commit was moved from ipfs/go-path@33a4dc08cc26702e87aba7d81bededa3585d54bb --- path/resolver_test.go | 83 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 path/resolver_test.go diff --git a/path/resolver_test.go b/path/resolver_test.go new file mode 100644 index 000000000..3772f1b9b --- /dev/null +++ b/path/resolver_test.go @@ -0,0 +1,83 @@ +package path_test + +import ( + "fmt" + "testing" + + datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + + blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" + blockservice "github.com/ipfs/go-ipfs/blockservice" + offline "github.com/ipfs/go-ipfs/exchange/offline" + merkledag "github.com/ipfs/go-ipfs/merkledag" + path "github.com/ipfs/go-ipfs/path" + util "github.com/ipfs/go-ipfs/util" +) + +func randNode() (*merkledag.Node, util.Key) { + node := new(merkledag.Node) + node.Data = make([]byte, 32) + util.NewTimeSeededRand().Read(node.Data) + k, _ := node.Key() + return node, k +} + +func TestRecurivePathResolution(t *testing.T) { + ctx := context.Background() + dstore := sync.MutexWrap(datastore.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv, err := blockservice.New(bstore, offline.Exchange(bstore)) + if err != nil { + t.Fatal(err) + } + + dagService := merkledag.NewDAGService(bserv) + + a, _ := randNode() + b, _ := randNode() + c, cKey := randNode() + + err = b.AddNodeLink("grandchild", c) + if err != nil { + t.Fatal(err) + } + + err = a.AddNodeLink("child", b) + if err != nil { + t.Fatal(err) + } + + err = dagService.AddRecursive(a) + if err != nil { + t.Fatal(err) + } + + aKey, err := a.Key() + if err != nil { + t.Fatal(err) + } + + segments := []string{"", "ipfs", aKey.String(), "child", "grandchild"} + p, err := path.FromSegments(segments...) + if err != nil { + t.Fatal(err) + } + + resolver := &path.Resolver{DAG: dagService} + node, err := resolver.ResolvePath(ctx, p) + if err != nil { + t.Fatal(err) + } + + key, err := node.Key() + if err != nil { + t.Fatal(err) + } + if key.String() != cKey.String() { + t.Fatal(fmt.Errorf( + "recursive path resolution failed for %s: %s != %s", + p.String(), key.String(), cKey.String())) + } +} From 8628c947b07f590c5034d4db3929120280cae8d7 Mon Sep 17 00:00:00 2001 From: Henry Date: Sat, 9 May 2015 11:49:02 +0200 Subject: [PATCH 0862/3817] unixfs/io: added NewEmptyDirectory() some golinting along the way This commit was moved from ipfs/go-unixfs@ea030b67b3cce7d11ebdd95776c59a7d4d52ca3d --- unixfs/io/dirbuilder.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index b30d9ea3a..ef74f3de0 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -15,15 +15,22 @@ type directoryBuilder struct { dirnode *mdag.Node } +// NewEmptyDirectory returns an empty merkledag Node with a folder Data chunk +func NewEmptyDirectory() *mdag.Node { + return &mdag.Node{Data: format.FolderPBData()} +} + +// NewDirectory returns a directoryBuilder. It needs a DAGService to add the Children func NewDirectory(dserv mdag.DAGService) *directoryBuilder { db := new(directoryBuilder) db.dserv = dserv - db.dirnode = new(mdag.Node) - db.dirnode.Data = format.FolderPBData() + db.dirnode = NewEmptyDirectory() return db } +// AddChild adds a (name, key)-pair to the root node. func (d *directoryBuilder) AddChild(name string, k u.Key) error { + // TODO(cryptix): consolidate context managment ctx, cancel := context.WithTimeout(context.TODO(), time.Minute) defer cancel() @@ -40,6 +47,7 @@ func (d *directoryBuilder) AddChild(name string, k u.Key) error { return nil } +// GetNode returns the root of this directoryBuilder func (d *directoryBuilder) GetNode() *mdag.Node { return d.dirnode } From 2d48da1392b2a5851743f6e2ab6bc35ede1732d3 Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 19 May 2015 00:42:21 +0700 Subject: [PATCH 0863/3817] Run 'gofmt -s -w' on these files This commit was moved from ipfs/go-unixfs@36d50ca7f58037286a9a9c5f664f2f6b8a6aedb4 --- unixfs/mod/dagmodifier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 6cca0c007..e0e09f711 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -95,7 +95,7 @@ func (dm *DagModifier) WriteAt(b []byte, offset int64) (int, error) { type zeroReader struct{} func (zr zeroReader) Read(b []byte) (int, error) { - for i, _ := range b { + for i := range b { b[i] = 0 } return len(b), nil From 94553ba6b13ec3651f9cab223a51d9140da44d17 Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 19 May 2015 00:42:21 +0700 Subject: [PATCH 0864/3817] Run 'gofmt -s -w' on these files This commit was moved from ipfs/go-merkledag@93ab652f17c1d0429591007f0c5a9c03c2c2d704 --- ipld/merkledag/merkledag.go | 2 +- ipld/merkledag/merkledag_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index be8753f71..2ca5552f1 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -181,7 +181,7 @@ func (ds *dagService) GetNodes(ctx context.Context, keys []u.Key) []NodeGetter { promises := make([]NodeGetter, len(keys)) sendChans := make([]chan<- *Node, len(keys)) - for i, _ := range keys { + for i := range keys { promises[i], sendChans[i] = newNodePromise(ctx) } diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 1c5f18a26..07525b891 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -132,7 +132,7 @@ func SubtestNodeStat(t *testing.T, n *Node) { type devZero struct{} func (_ devZero) Read(b []byte) (int, error) { - for i, _ := range b { + for i := range b { b[i] = 0 } return len(b), nil From e8bd35c95f9f35b6af6e0a39c60081e6aa5ee53d Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 19 May 2015 00:42:21 +0700 Subject: [PATCH 0865/3817] Run 'gofmt -s -w' on these files This commit was moved from ipfs/go-ipfs-routing@c56a25e2dc4611d0b9bd4c9c8395dc5b01aee027 --- routing/dht/dht_test.go | 6 +++--- routing/dht/providers.go | 2 +- routing/keyspace/xor_test.go | 30 +++++++++++++++--------------- routing/supernode/client.go | 2 +- routing/supernode/server.go | 2 +- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index fdd334d59..29b57816f 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -215,7 +215,7 @@ func TestProvides(t *testing.T) { } } - for k, _ := range testCaseValues { + for k := range testCaseValues { log.Debugf("announcing provider for %s", k) if err := dhts[3].Provide(ctx, k); err != nil { t.Fatal(err) @@ -226,7 +226,7 @@ func TestProvides(t *testing.T) { time.Sleep(time.Millisecond * 6) n := 0 - for k, _ := range testCaseValues { + for k := range testCaseValues { n = (n + 1) % 3 log.Debugf("getting providers for %s from %d", k, n) @@ -521,7 +521,7 @@ func TestProvidesMany(t *testing.T) { } } - for k, _ := range testCaseValues { + for k := range testCaseValues { // everyone should be able to find it... for _, dht := range dhts { log.Debugf("getting providers for %s at %s", k, dht.self) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index c62aee97c..e803398be 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -77,7 +77,7 @@ func (pm *ProviderManager) run() { case lc := <-pm.getlocal: var keys []u.Key - for k, _ := range pm.local { + for k := range pm.local { keys = append(keys, k) } lc <- keys diff --git a/routing/keyspace/xor_test.go b/routing/keyspace/xor_test.go index f90e8a5f9..cac274278 100644 --- a/routing/keyspace/xor_test.go +++ b/routing/keyspace/xor_test.go @@ -10,9 +10,9 @@ import ( func TestPrefixLen(t *testing.T) { cases := [][]byte{ - []byte{0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00}, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x00, 0x58, 0xFF, 0x80, 0x00, 0x00, 0xF0}, + {0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x58, 0xFF, 0x80, 0x00, 0x00, 0xF0}, } lens := []int{24, 56, 9} @@ -28,15 +28,15 @@ func TestPrefixLen(t *testing.T) { func TestXorKeySpace(t *testing.T) { ids := [][]byte{ - []byte{0xFF, 0xFF, 0xFF, 0xFF}, - []byte{0x00, 0x00, 0x00, 0x00}, - []byte{0xFF, 0xFF, 0xFF, 0xF0}, + {0xFF, 0xFF, 0xFF, 0xFF}, + {0x00, 0x00, 0x00, 0x00}, + {0xFF, 0xFF, 0xFF, 0xF0}, } ks := [][2]Key{ - [2]Key{XORKeySpace.Key(ids[0]), XORKeySpace.Key(ids[0])}, - [2]Key{XORKeySpace.Key(ids[1]), XORKeySpace.Key(ids[1])}, - [2]Key{XORKeySpace.Key(ids[2]), XORKeySpace.Key(ids[2])}, + {XORKeySpace.Key(ids[0]), XORKeySpace.Key(ids[0])}, + {XORKeySpace.Key(ids[1]), XORKeySpace.Key(ids[1])}, + {XORKeySpace.Key(ids[2]), XORKeySpace.Key(ids[2])}, } for i, set := range ks { @@ -75,12 +75,12 @@ func TestXorKeySpace(t *testing.T) { func TestDistancesAndCenterSorting(t *testing.T) { adjs := [][]byte{ - []byte{173, 149, 19, 27, 192, 183, 153, 192, 177, 175, 71, 127, 177, 79, 207, 38, 166, 169, 247, 96, 121, 228, 139, 240, 144, 172, 183, 232, 54, 123, 253, 14}, - []byte{223, 63, 97, 152, 4, 169, 47, 219, 64, 87, 25, 45, 196, 61, 215, 72, 234, 119, 138, 220, 82, 188, 73, 140, 232, 5, 36, 192, 20, 184, 17, 25}, - []byte{73, 176, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 127}, - []byte{73, 176, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 127}, - []byte{73, 176, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 126}, - []byte{73, 0, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 127}, + {173, 149, 19, 27, 192, 183, 153, 192, 177, 175, 71, 127, 177, 79, 207, 38, 166, 169, 247, 96, 121, 228, 139, 240, 144, 172, 183, 232, 54, 123, 253, 14}, + {223, 63, 97, 152, 4, 169, 47, 219, 64, 87, 25, 45, 196, 61, 215, 72, 234, 119, 138, 220, 82, 188, 73, 140, 232, 5, 36, 192, 20, 184, 17, 25}, + {73, 176, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 127}, + {73, 176, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 127}, + {73, 176, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 126}, + {73, 0, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 127}, } keys := make([]Key, len(adjs)) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 15c3a4086..14f6a4db5 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -86,7 +86,7 @@ func (c *Client) Provide(ctx context.Context, k u.Key) error { msg := pb.NewMessage(pb.Message_ADD_PROVIDER, string(k), 0) // FIXME how is connectedness defined for the local node pri := []pb.PeerRoutingInfo{ - pb.PeerRoutingInfo{ + { PeerInfo: peer.PeerInfo{ ID: c.local, Addrs: c.peerhost.Addrs(), diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 46205d0e4..95d6a0def 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -73,7 +73,7 @@ func (s *Server) handleMessage( case dhtpb.Message_FIND_NODE: p := s.peerstore.PeerInfo(peer.ID(req.GetKey())) pri := []dhtpb.PeerRoutingInfo{ - dhtpb.PeerRoutingInfo{ + { PeerInfo: p, // Connectedness: TODO }, From b59382894c68c37800c27e5b13108022d3a0d2b2 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 7 May 2015 15:08:09 -0700 Subject: [PATCH 0866/3817] namesys/publisher: Drop the 'namesys: ' prefix for the Publish log This is already handled by setup in namesys/routing.go: var log = u.Logger("namesys") This commit was moved from ipfs/go-namesys@76cbab47fcb3cbd5190a5995b40a2c141341da9b --- namesys/publisher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 38dd8d082..b20a47bea 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -42,7 +42,7 @@ func NewRoutingPublisher(route routing.IpfsRouting) Publisher { // Publish implements Publisher. Accepts a keypair and a value, // and publishes it out to the routing system func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error { - log.Debugf("namesys: Publish %s", value) + log.Debugf("Publish %s", value) data, err := createRoutingEntryData(k, value) if err != nil { From ee29b9ba2c23007d498b666f622287156e406778 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 17 May 2015 11:49:30 -0700 Subject: [PATCH 0867/3817] namesys/interface: Expand package docs to discuss mutable names What they are, why you'd use them, and which command-line tools you can use to access this functionality. This commit was moved from ipfs/go-namesys@01e04b16ce4975312cd999806a4e6ddb9590d013 --- namesys/interface.go | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/namesys/interface.go b/namesys/interface.go index 4ceb3b9d9..74f686128 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -1,4 +1,32 @@ -// package namesys implements various functionality for the ipns naming system. +/* +Package namesys implements resolvers and publishers for the IPFS +naming system (IPNS). + +The core of IPFS is an immutable, content-addressable Merkle graph. +That works well for many use cases, but doesn't allow you to answer +questions like "what is Alice's current homepage?". The mutable name +system allows Alice to publish information like: + + The current homepage for alice.example.com is + /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj + +or: + + The current homepage for node + QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + is + /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj + +The mutable name system also allows users to resolve those references +to find the immutable IPFS object currently referenced by a given +mutable name. + +For command-line bindings to this functionality, see: + + ipfs name + ipfs dns + ipfs resolve +*/ package namesys import ( From 0f7117854c9c92674c08277c25e59c56daad7ff6 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 16 May 2015 09:34:08 -0700 Subject: [PATCH 0868/3817] namesys/dns: Use SplitN to find dnslink references RFC 6763 requires printable ASCII except '=' for the key [1], but allows any character including '=' in the value [2]. This patch adjusts our parsing to avoid splitting on '=' in the value, and then ignoring anything after that split. [1]: https://tools.ietf.org/html/rfc6763#section-6.4 [2]: https://tools.ietf.org/html/rfc6763#section-6.5 This commit was moved from ipfs/go-namesys@79909f40b3b7295b3a353f0f4a3f59e65a801dc4 --- namesys/dns.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 086adee9e..3e703d420 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -52,10 +52,10 @@ func parseEntry(txt string) (path.Path, error) { } func tryParseDnsLink(txt string) (path.Path, error) { - parts := strings.Split(txt, "=") - if len(parts) == 1 || parts[0] != "dnslink" { - return "", errors.New("not a valid dnslink entry") + parts := strings.SplitN(txt, "=", 2) + if len(parts) == 2 && parts[0] == "dnslink" { + return path.ParsePath(parts[1]) } - return path.ParsePath(parts[1]) + return "", errors.New("not a valid dnslink entry") } From a50c1dad42e2dab5f086ab359237d9c5c8ec0120 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 7 May 2015 14:31:14 -0700 Subject: [PATCH 0869/3817] namesys: Add recursive resolution This allows direct access to the earlier protocol-specific Resolve implementations. The guts of each protocol-specific resolver are in the internal resolveOnce method, and we've added a new: ResolveN(ctx, name, depth) method to the public interface. There's also: Resolve(ctx, name) which wraps ResolveN using DefaultDepthLimit. The extra API endpoint is intended to reduce the likelyhood of clients accidentally calling the more dangerous ResolveN with a nonsensically high or infinite depth. On IRC on 2015-05-17, Juan said: 15:34 If 90% of uses is the reduced API with no chance to screw it up, that's a huge win. 15:34 Why would those 90% not just set depth=0 or depth=1, depending on which they need? 15:34 Because people will start writing `r.Resolve(ctx, name, d)` where d is a variable. 15:35 And then accidentally set that variable to some huge number? 15:35 Grom experience, i've seen this happen _dozens_ of times. people screw trivial things up. 15:35 Why won't those same people be using ResolveN? 15:36 Because almost every example they see will tell them to use Resolve(), and they will mostly stay away from ResolveN. The per-prodocol versions also resolve recursively within their protocol. For example: DNSResolver.Resolve(ctx, "ipfs.io", 0) will recursively resolve DNS links until the referenced value is no longer a DNS link. I also renamed the multi-protocol ipfs NameSystem (defined in namesys/namesys.go) to 'mpns' (for Multi-Protocol Name System), because I wasn't clear on whether IPNS applied to the whole system or just to to the DHT-based system. The new name is unambiguously multi-protocol, which is good. It would be nice to have a distinct name for the DHT-based link system. Now that resolver output is always prefixed with a namespace and unprefixed mpns resolver input is interpreted as /ipfs/, core/corehttp/ipns_hostname.go can dispense with it's old manual /ipfs/ injection. Now that the Resolver interface handles recursion, we don't need the resolveRecurse helper in core/pathresolver.go. The pathresolver cleanup also called for an adjustment to FromSegments to more easily get slash-prefixed paths. Now that recursive resolution with the namesys/namesys.go composite resolver always gets you to an /ipfs/... path, there's no need for the /ipns/ special case in fuse/ipns/ipns_unix.go. Now that DNS links can be things other than /ipfs/ or DHT-link references (e.g. they could be /ipns/ references) I've also loosened the ParsePath logic to only attempt multihash validation on IPFS paths. It checks to ensure that other paths have a known-protocol prefix, but otherwise leaves them alone. I also changed some key-stringification from .Pretty() to .String() following the potential deprecation mentioned in util/key.go. This commit was moved from ipfs/go-namesys@41bdb138ff766cae2ef467c2c9743e420c279a79 --- namesys/base.go | 54 +++++++++++++++++++++++++++++++ namesys/dns.go | 23 ++++++++++---- namesys/interface.go | 40 +++++++++++++++++++++-- namesys/namesys.go | 76 +++++++++++++++++++++++++++++--------------- namesys/proquint.go | 20 +++++++----- namesys/routing.go | 25 +++++++++++---- 6 files changed, 188 insertions(+), 50 deletions(-) create mode 100644 namesys/base.go diff --git a/namesys/base.go b/namesys/base.go new file mode 100644 index 000000000..e552fce46 --- /dev/null +++ b/namesys/base.go @@ -0,0 +1,54 @@ +package namesys + +import ( + "strings" + + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + + path "github.com/ipfs/go-ipfs/path" +) + +type resolver interface { + // resolveOnce looks up a name once (without recursion). + resolveOnce(ctx context.Context, name string) (value path.Path, err error) +} + +// resolve is a helper for implementing Resolver.ResolveN using resolveOnce. +func resolve(ctx context.Context, r resolver, name string, depth int, prefixes ...string) (path.Path, error) { + for { + p, err := r.resolveOnce(ctx, name) + if err != nil { + log.Warningf("Could not resolve %s", name) + return "", err + } + log.Debugf("Resolved %s to %s", name, p.String()) + + if strings.HasPrefix(p.String(), "/ipfs/") { + // we've bottomed out with an IPFS path + return p, nil + } + + if depth == 1 { + return p, ErrResolveRecursion + } + + matched := false + for _, prefix := range prefixes { + if strings.HasPrefix(p.String(), prefix) { + matched = true + if len(prefixes) == 1 { + name = strings.TrimPrefix(p.String(), prefix) + } + break + } + } + + if !matched { + return p, nil + } + + if depth > 1 { + depth-- + } + } +} diff --git a/namesys/dns.go b/namesys/dns.go index 3e703d420..f57ddce59 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -17,16 +17,25 @@ type DNSResolver struct { // cache would need a timeout } -// CanResolve implements Resolver -func (r *DNSResolver) CanResolve(name string) bool { - return isd.IsDomain(name) +// Resolve implements Resolver. +func (r *DNSResolver) Resolve(ctx context.Context, name string) (path.Path, error) { + return r.ResolveN(ctx, name, DefaultDepthLimit) +} + +// ResolveN implements Resolver. +func (r *DNSResolver) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) { + return resolve(ctx, r, name, depth, "/ipns/") } -// Resolve implements Resolver +// resolveOnce implements resolver. // TXT records for a given domain name should contain a b58 // encoded multihash. -func (r *DNSResolver) Resolve(ctx context.Context, name string) (path.Path, error) { - log.Info("DNSResolver resolving %v", name) +func (r *DNSResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) { + if !isd.IsDomain(name) { + return "", errors.New("not a valid domain name") + } + + log.Infof("DNSResolver resolving %s", name) txt, err := net.LookupTXT(name) if err != nil { return "", err @@ -43,7 +52,7 @@ func (r *DNSResolver) Resolve(ctx context.Context, name string) (path.Path, erro } func parseEntry(txt string) (path.Path, error) { - p, err := path.ParseKeyToPath(txt) + p, err := path.ParseKeyToPath(txt) // bare IPFS multihashes if err == nil { return p, nil } diff --git a/namesys/interface.go b/namesys/interface.go index 74f686128..5903c78a3 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -37,9 +37,24 @@ import ( path "github.com/ipfs/go-ipfs/path" ) +const ( + // DefaultDepthLimit is the default depth limit used by Resolve. + DefaultDepthLimit = 32 + + // UnlimitedDepth allows infinite recursion in ResolveN. You + // probably don't want to use this, but it's here if you absolutely + // trust resolution to eventually complete and can't put an upper + // limit on how many steps it will take. + UnlimitedDepth = 0 +) + // ErrResolveFailed signals an error when attempting to resolve. var ErrResolveFailed = errors.New("could not resolve name.") +// ErrResolveRecursion signals a recursion-depth limit. +var ErrResolveRecursion = errors.New( + "could not resolve name (recursion limit exceeded).") + // ErrPublishFailed signals an error when attempting to publish. var ErrPublishFailed = errors.New("could not publish name.") @@ -58,11 +73,30 @@ type NameSystem interface { // Resolver is an object capable of resolving names. type Resolver interface { - // Resolve looks up a name, and returns the value previously published. + // Resolve performs a recursive lookup, returning the dereferenced + // path. For example, if ipfs.io has a DNS TXT record pointing to + // /ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + // and there is a DHT IPNS entry for + // QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + // -> /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj + // then + // Resolve(ctx, "/ipns/ipfs.io") + // will resolve both names, returning + // /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj + // + // There is a default depth-limit to avoid infinite recursion. Most + // users will be fine with this default limit, but if you need to + // adjust the limit you can use ResolveN. Resolve(ctx context.Context, name string) (value path.Path, err error) - // CanResolve checks whether this Resolver can resolve a name - CanResolve(name string) bool + // ResolveN performs a recursive lookup, returning the dereferenced + // path. The only difference from Resolve is that the depth limit + // is configurable. You can use DefaultDepthLimit, UnlimitedDepth, + // or a depth limit of your own choosing. + // + // Most users should use Resolve, since the default limit works well + // in most real-world situations. + ResolveN(ctx context.Context, name string, depth int) (value path.Path, err error) } // Publisher is an object capable of publishing particular names. diff --git a/namesys/namesys.go b/namesys/namesys.go index 655307723..0f5b853be 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -1,59 +1,83 @@ package namesys import ( + "strings" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ci "github.com/ipfs/go-ipfs/p2p/crypto" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" ) -// ipnsNameSystem implements IPNS naming. +// mpns (a multi-protocol NameSystem) implements generic IPFS naming. // -// Uses three Resolvers: +// Uses several Resolvers: // (a) ipfs routing naming: SFS-like PKI names. // (b) dns domains: resolves using links in DNS TXT records // (c) proquints: interprets string as the raw byte data. // // It can only publish to: (a) ipfs routing naming. // -type ipns struct { - resolvers []Resolver - publisher Publisher +type mpns struct { + resolvers map[string]resolver + publishers map[string]Publisher } // NewNameSystem will construct the IPFS naming system based on Routing func NewNameSystem(r routing.IpfsRouting) NameSystem { - return &ipns{ - resolvers: []Resolver{ - new(DNSResolver), - new(ProquintResolver), - NewRoutingResolver(r), + return &mpns{ + resolvers: map[string]resolver{ + "dns": new(DNSResolver), + "proquint": new(ProquintResolver), + "dht": newRoutingResolver(r), + }, + publishers: map[string]Publisher{ + "/ipns/": NewRoutingPublisher(r), }, - publisher: NewRoutingPublisher(r), } } -// Resolve implements Resolver -func (ns *ipns) Resolve(ctx context.Context, name string) (path.Path, error) { - for _, r := range ns.resolvers { - if r.CanResolve(name) { - return r.Resolve(ctx, name) - } +// Resolve implements Resolver. +func (ns *mpns) Resolve(ctx context.Context, name string) (path.Path, error) { + return ns.ResolveN(ctx, name, DefaultDepthLimit) +} + +// ResolveN implements Resolver. +func (ns *mpns) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) { + if strings.HasPrefix(name, "/ipfs/") { + return path.ParsePath(name) } - return "", ErrResolveFailed + + if !strings.HasPrefix(name, "/") { + return path.ParsePath("/ipfs/" + name) + } + + return resolve(ctx, ns, name, depth, "/ipns/") } -// CanResolve implements Resolver -func (ns *ipns) CanResolve(name string) bool { - for _, r := range ns.resolvers { - if r.CanResolve(name) { - return true +// resolveOnce implements resolver. +func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) { + if !strings.HasPrefix(name, "/ipns/") { + name = "/ipns/" + name + } + segments := strings.SplitN(name, "/", 3) + if len(segments) < 3 || segments[0] != "" { + log.Warningf("Invalid name syntax for %s", name) + return "", ErrResolveFailed + } + + for protocol, resolver := range ns.resolvers { + log.Debugf("Attempting to resolve %s with %s", name, protocol) + p, err := resolver.resolveOnce(ctx, segments[2]) + if err == nil { + return p, err } } - return false + log.Warningf("No resolver found for %s", name) + return "", ErrResolveFailed } // Publish implements Publisher -func (ns *ipns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error { - return ns.publisher.Publish(ctx, name, value) +func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error { + return ns.publishers["/ipns/"].Publish(ctx, name, value) } diff --git a/namesys/proquint.go b/namesys/proquint.go index 66bd54e24..2ad3275a4 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -10,16 +10,20 @@ import ( type ProquintResolver struct{} -// CanResolve implements Resolver. Checks whether the name is a proquint string. -func (r *ProquintResolver) CanResolve(name string) bool { - ok, err := proquint.IsProquint(name) - return err == nil && ok +// Resolve implements Resolver. +func (r *ProquintResolver) Resolve(ctx context.Context, name string) (path.Path, error) { + return r.ResolveN(ctx, name, DefaultDepthLimit) } -// Resolve implements Resolver. Decodes the proquint string. -func (r *ProquintResolver) Resolve(ctx context.Context, name string) (path.Path, error) { - ok := r.CanResolve(name) - if !ok { +// ResolveN implements Resolver. +func (r *ProquintResolver) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) { + return resolve(ctx, r, name, depth, "/ipns/") +} + +// resolveOnce implements resolver. Decodes the proquint string. +func (r *ProquintResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) { + ok, err := proquint.IsProquint(name) + if err != nil || !ok { return "", errors.New("not a valid proquint string") } return path.FromString(string(proquint.Decode(name))), nil diff --git a/namesys/routing.go b/namesys/routing.go index 38cb250d0..290c06cb2 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -30,15 +30,28 @@ func NewRoutingResolver(route routing.IpfsRouting) Resolver { return &routingResolver{routing: route} } -// CanResolve implements Resolver. Checks whether name is a b58 encoded string. -func (r *routingResolver) CanResolve(name string) bool { - _, err := mh.FromB58String(name) - return err == nil +// newRoutingResolver returns a resolver instead of a Resolver. +func newRoutingResolver(route routing.IpfsRouting) resolver { + if route == nil { + panic("attempt to create resolver with nil routing system") + } + + return &routingResolver{routing: route} } -// Resolve implements Resolver. Uses the IPFS routing system to resolve SFS-like -// names. +// Resolve implements Resolver. func (r *routingResolver) Resolve(ctx context.Context, name string) (path.Path, error) { + return r.ResolveN(ctx, name, DefaultDepthLimit) +} + +// ResolveN implements Resolver. +func (r *routingResolver) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) { + return resolve(ctx, r, name, depth, "/ipns/") +} + +// resolveOnce implements resolver. Uses the IPFS routing system to +// resolve SFS-like names. +func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) { log.Debugf("RoutingResolve: '%s'", name) hash, err := mh.FromB58String(name) if err != nil { From e5694c4b5433824ed6b0da95bd8333f7fc05e464 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 7 May 2015 14:31:14 -0700 Subject: [PATCH 0870/3817] namesys: Add recursive resolution This allows direct access to the earlier protocol-specific Resolve implementations. The guts of each protocol-specific resolver are in the internal resolveOnce method, and we've added a new: ResolveN(ctx, name, depth) method to the public interface. There's also: Resolve(ctx, name) which wraps ResolveN using DefaultDepthLimit. The extra API endpoint is intended to reduce the likelyhood of clients accidentally calling the more dangerous ResolveN with a nonsensically high or infinite depth. On IRC on 2015-05-17, Juan said: 15:34 If 90% of uses is the reduced API with no chance to screw it up, that's a huge win. 15:34 Why would those 90% not just set depth=0 or depth=1, depending on which they need? 15:34 Because people will start writing `r.Resolve(ctx, name, d)` where d is a variable. 15:35 And then accidentally set that variable to some huge number? 15:35 Grom experience, i've seen this happen _dozens_ of times. people screw trivial things up. 15:35 Why won't those same people be using ResolveN? 15:36 Because almost every example they see will tell them to use Resolve(), and they will mostly stay away from ResolveN. The per-prodocol versions also resolve recursively within their protocol. For example: DNSResolver.Resolve(ctx, "ipfs.io", 0) will recursively resolve DNS links until the referenced value is no longer a DNS link. I also renamed the multi-protocol ipfs NameSystem (defined in namesys/namesys.go) to 'mpns' (for Multi-Protocol Name System), because I wasn't clear on whether IPNS applied to the whole system or just to to the DHT-based system. The new name is unambiguously multi-protocol, which is good. It would be nice to have a distinct name for the DHT-based link system. Now that resolver output is always prefixed with a namespace and unprefixed mpns resolver input is interpreted as /ipfs/, core/corehttp/ipns_hostname.go can dispense with it's old manual /ipfs/ injection. Now that the Resolver interface handles recursion, we don't need the resolveRecurse helper in core/pathresolver.go. The pathresolver cleanup also called for an adjustment to FromSegments to more easily get slash-prefixed paths. Now that recursive resolution with the namesys/namesys.go composite resolver always gets you to an /ipfs/... path, there's no need for the /ipns/ special case in fuse/ipns/ipns_unix.go. Now that DNS links can be things other than /ipfs/ or DHT-link references (e.g. they could be /ipns/ references) I've also loosened the ParsePath logic to only attempt multihash validation on IPFS paths. It checks to ensure that other paths have a known-protocol prefix, but otherwise leaves them alone. I also changed some key-stringification from .Pretty() to .String() following the potential deprecation mentioned in util/key.go. This commit was moved from ipfs/go-path@461b0414442036b3dd195e363edfd2937ad05476 --- path/path.go | 20 ++++++++------------ path/resolver_test.go | 4 ++-- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/path/path.go b/path/path.go index 0ab15e5c9..ba75810c8 100644 --- a/path/path.go +++ b/path/path.go @@ -44,12 +44,8 @@ func (p Path) String() string { return string(p) } -func FromSegments(seg ...string) (Path, error) { - var pref string - if seg[0] == "ipfs" || seg[0] == "ipns" { - pref = "/" - } - return ParsePath(pref + strings.Join(seg, "/")) +func FromSegments(prefix string, seg ...string) (Path, error) { + return ParsePath(prefix + strings.Join(seg, "/")) } func ParsePath(txt string) (Path, error) { @@ -68,15 +64,15 @@ func ParsePath(txt string) (Path, error) { return "", ErrBadPath } - if parts[1] != "ipfs" && parts[1] != "ipns" { + if parts[1] == "ipfs" { + _, err := ParseKeyToPath(parts[2]) + if err != nil { + return "", err + } + } else if parts[1] != "ipns" { return "", ErrBadPath } - _, err := ParseKeyToPath(parts[2]) - if err != nil { - return "", err - } - return Path(txt), nil } diff --git a/path/resolver_test.go b/path/resolver_test.go index 3772f1b9b..88fcb7433 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -59,8 +59,8 @@ func TestRecurivePathResolution(t *testing.T) { t.Fatal(err) } - segments := []string{"", "ipfs", aKey.String(), "child", "grandchild"} - p, err := path.FromSegments(segments...) + segments := []string{aKey.String(), "child", "grandchild"} + p, err := path.FromSegments("/ipfs/", segments...) if err != nil { t.Fatal(err) } From eba868f22897333cb1e634c57fe3e7707c7448f1 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 16 May 2015 09:33:22 -0700 Subject: [PATCH 0871/3817] namesys/dns: Pluggable lookupTXT field So we can attach a mock lookup function for testing. This commit was moved from ipfs/go-namesys@3e0d4ad3e1e0960210a3010e6dfec93c40758269 --- namesys/dns.go | 16 +++++++++++++++- namesys/namesys.go | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index f57ddce59..3703bd8d0 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -11,12 +11,26 @@ import ( path "github.com/ipfs/go-ipfs/path" ) +type LookupTXTFunc func(name string) (txt []string, err error) + // DNSResolver implements a Resolver on DNS domains type DNSResolver struct { + lookupTXT LookupTXTFunc // TODO: maybe some sort of caching? // cache would need a timeout } +// NewDNSResolver constructs a name resolver using DNS TXT records. +func NewDNSResolver() Resolver { + return &DNSResolver{lookupTXT: net.LookupTXT} +} + +// newDNSResolver constructs a name resolver using DNS TXT records, +// returning a resolver instead of NewDNSResolver's Resolver. +func newDNSResolver() resolver { + return &DNSResolver{lookupTXT: net.LookupTXT} +} + // Resolve implements Resolver. func (r *DNSResolver) Resolve(ctx context.Context, name string) (path.Path, error) { return r.ResolveN(ctx, name, DefaultDepthLimit) @@ -36,7 +50,7 @@ func (r *DNSResolver) resolveOnce(ctx context.Context, name string) (path.Path, } log.Infof("DNSResolver resolving %s", name) - txt, err := net.LookupTXT(name) + txt, err := r.lookupTXT(name) if err != nil { return "", err } diff --git a/namesys/namesys.go b/namesys/namesys.go index 0f5b853be..7fe317b66 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -27,7 +27,7 @@ type mpns struct { func NewNameSystem(r routing.IpfsRouting) NameSystem { return &mpns{ resolvers: map[string]resolver{ - "dns": new(DNSResolver), + "dns": newDNSResolver(), "proquint": new(ProquintResolver), "dht": newRoutingResolver(r), }, From ac419d2ac93fb5206f0aa6cf0b8984ee69bdceb1 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 16 May 2015 09:54:06 -0700 Subject: [PATCH 0872/3817] namesys/dns_test: Add DNS resolution tests with a mock resolver This commit was moved from ipfs/go-namesys@949935808ae36f987d61f021106802decab41a76 --- namesys/dns_test.go | 86 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 402156add..6bb75ff9f 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -1,9 +1,24 @@ package namesys import ( + "fmt" "testing" + + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) +type mockDNS struct { + entries map[string][]string +} + +func (m *mockDNS) lookupTXT(name string) (txt []string, err error) { + txt, ok := m.entries[name] + if !ok { + return nil, fmt.Errorf("No TXT entry for %s", name) + } + return txt, nil +} + func TestDnsEntryParsing(t *testing.T) { goodEntries := []string{ "QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", @@ -40,3 +55,74 @@ func TestDnsEntryParsing(t *testing.T) { } } } + +func newMockDNS() *mockDNS { + return &mockDNS{ + entries: map[string][]string{ + "multihash.example.com": []string{ + "dnslink=QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", + }, + "ipfs.example.com": []string{ + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", + }, + "dns1.example.com": []string{ + "dnslink=/ipns/ipfs.example.com", + }, + "dns2.example.com": []string{ + "dnslink=/ipns/dns1.example.com", + }, + "multi.example.com": []string{ + "some stuff", + "dnslink=/ipns/dns1.example.com", + "masked dnslink=/ipns/example.invalid", + }, + "equals.example.com": []string{ + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/=equals", + }, + "loop1.example.com": []string{ + "dnslink=/ipns/loop2.example.com", + }, + "loop2.example.com": []string{ + "dnslink=/ipns/loop1.example.com", + }, + "bad.example.com": []string{ + "dnslink=", + }, + }, + } +} + +func testResolution(t *testing.T, resolver Resolver, name string, depth int, expected string, expError error) { + p, err := resolver.ResolveN(context.Background(), name, depth) + if err != expError { + t.Fatal(fmt.Errorf( + "Expected %s with a depth of %d to have a '%s' error, but got '%s'", + name, depth, expError, err)) + } + if p.String() != expected { + t.Fatal(fmt.Errorf( + "%s with depth %d resolved to %s != %s", + name, depth, p.String(), expected)) + } +} + +func TestDNSResolution(t *testing.T) { + mock := newMockDNS() + r := &DNSResolver{lookupTXT: mock.lookupTXT} + testResolution(t, r, "multihash.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "ipfs.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "dns1.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "dns1.example.com", 1, "/ipns/ipfs.example.com", ErrResolveRecursion) + testResolution(t, r, "dns2.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "dns2.example.com", 1, "/ipns/dns1.example.com", ErrResolveRecursion) + testResolution(t, r, "dns2.example.com", 2, "/ipns/ipfs.example.com", ErrResolveRecursion) + testResolution(t, r, "multi.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "multi.example.com", 1, "/ipns/dns1.example.com", ErrResolveRecursion) + testResolution(t, r, "multi.example.com", 2, "/ipns/ipfs.example.com", ErrResolveRecursion) + testResolution(t, r, "equals.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/=equals", nil) + testResolution(t, r, "loop1.example.com", 1, "/ipns/loop2.example.com", ErrResolveRecursion) + testResolution(t, r, "loop1.example.com", 2, "/ipns/loop1.example.com", ErrResolveRecursion) + testResolution(t, r, "loop1.example.com", 3, "/ipns/loop2.example.com", ErrResolveRecursion) + testResolution(t, r, "loop1.example.com", DefaultDepthLimit, "/ipns/loop1.example.com", ErrResolveRecursion) + testResolution(t, r, "bad.example.com", DefaultDepthLimit, "", ErrResolveFailed) +} From 5fa31613ca74402163530b126f2673875d505305 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 19 May 2015 12:48:32 -0700 Subject: [PATCH 0873/3817] namesys/namesys_test: Excercise mpns.ResolveN Shifting the generic testResolution helper from the protocol-specific dns_test.go to the generic namesys_test.go. This commit was moved from ipfs/go-namesys@58d3f58507989276b86cb888b332094a2791fdaf --- namesys/dns_test.go | 16 ---------- namesys/namesys_test.go | 71 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 16 deletions(-) create mode 100644 namesys/namesys_test.go diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 6bb75ff9f..40bf702c3 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -3,8 +3,6 @@ package namesys import ( "fmt" "testing" - - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) type mockDNS struct { @@ -92,20 +90,6 @@ func newMockDNS() *mockDNS { } } -func testResolution(t *testing.T, resolver Resolver, name string, depth int, expected string, expError error) { - p, err := resolver.ResolveN(context.Background(), name, depth) - if err != expError { - t.Fatal(fmt.Errorf( - "Expected %s with a depth of %d to have a '%s' error, but got '%s'", - name, depth, expError, err)) - } - if p.String() != expected { - t.Fatal(fmt.Errorf( - "%s with depth %d resolved to %s != %s", - name, depth, p.String(), expected)) - } -} - func TestDNSResolution(t *testing.T) { mock := newMockDNS() r := &DNSResolver{lookupTXT: mock.lookupTXT} diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go new file mode 100644 index 000000000..256228c3e --- /dev/null +++ b/namesys/namesys_test.go @@ -0,0 +1,71 @@ +package namesys + +import ( + "fmt" + "testing" + + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + + path "github.com/ipfs/go-ipfs/path" +) + +type mockResolver struct { + entries map[string]string +} + +func testResolution(t *testing.T, resolver Resolver, name string, depth int, expected string, expError error) { + p, err := resolver.ResolveN(context.Background(), name, depth) + if err != expError { + t.Fatal(fmt.Errorf( + "Expected %s with a depth of %d to have a '%s' error, but got '%s'", + name, depth, expError, err)) + } + if p.String() != expected { + t.Fatal(fmt.Errorf( + "%s with depth %d resolved to %s != %s", + name, depth, p.String(), expected)) + } +} + +func (r *mockResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) { + return path.ParsePath(r.entries[name]) +} + +func mockResolverOne() *mockResolver { + return &mockResolver{ + entries: map[string]string{ + "QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy": "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", + "QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n": "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", + "QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD": "/ipns/ipfs.io", + }, + } +} + +func mockResolverTwo() *mockResolver { + return &mockResolver{ + entries: map[string]string{ + "ipfs.io": "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", + }, + } +} + +func TestNamesysResolution(t *testing.T) { + r := &mpns{ + resolvers: map[string]resolver{ + "one": mockResolverOne(), + "two": mockResolverTwo(), + }, + } + + testResolution(t, r, "Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) + testResolution(t, r, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) + testResolution(t, r, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) + testResolution(t, r, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", 1, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", ErrResolveRecursion) + testResolution(t, r, "/ipns/ipfs.io", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) + testResolution(t, r, "/ipns/ipfs.io", 1, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", ErrResolveRecursion) + testResolution(t, r, "/ipns/ipfs.io", 2, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", ErrResolveRecursion) + testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) + testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 1, "/ipns/ipfs.io", ErrResolveRecursion) + testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 2, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", ErrResolveRecursion) + testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 3, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", ErrResolveRecursion) +} From c175b946e5e9c831788644d281b13de0fffaabca Mon Sep 17 00:00:00 2001 From: Travis Person Date: Fri, 22 May 2015 09:18:49 -0700 Subject: [PATCH 0874/3817] Named error for `no components` Update the previous `invalid path` error to match the error returned from `SplitAbsPath`. This commit was moved from ipfs/go-path@032c997e06d465d2a5d08bab85b8266d88b6f282 --- path/resolver.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/path/resolver.go b/path/resolver.go index a08df8741..b4d6239dd 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -4,6 +4,7 @@ package path import ( "fmt" "time" + "errors" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" @@ -14,6 +15,10 @@ import ( var log = u.Logger("path") +// Paths after a protocol must contain at least one component +var ErrNoComponents = errors.New( + "path must contain at least one component") + // ErrNoLink is returned when a link is not found in a path type ErrNoLink struct { name string @@ -43,7 +48,7 @@ func SplitAbsPath(fpath Path) (mh.Multihash, []string, error) { // if nothing, bail. if len(parts) == 0 { - return nil, nil, fmt.Errorf("ipfs path must contain at least one component") + return nil, nil, ErrNoComponents } // first element in the path is a b58 hash (for now) From 719f20df8baccb7599b8ee85610a51ead5eb83b0 Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 26 May 2015 23:18:04 +0700 Subject: [PATCH 0875/3817] Replace 'var * bytes.Buffer' with '\1 := new(bytes.Buffer)' This commit was moved from ipfs/go-merkledag@d00305a4ca0d26fcc71f767258b3e484f5cc36ed --- ipld/merkledag/traverse/traverse_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 12aa9fd21..ff57909a3 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -317,7 +317,7 @@ func TestBFSSkip(t *testing.T) { func testWalkOutputs(t *testing.T, root *mdag.Node, opts Options, expect []byte) { expect = bytes.TrimLeft(expect, "\n") - var buf bytes.Buffer + buf := new(bytes.Buffer) walk := func(current State) error { s := fmt.Sprintf("%d %s\n", current.Depth, current.Node.Data) t.Logf("walk: %s", s) From 687caccb7b6541804f0e81dbb317494e0749d465 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 19 May 2015 08:52:30 -0700 Subject: [PATCH 0876/3817] change pinning to happen in a callback This commit was moved from ipfs/go-unixfs@da1062c936ccbad850b9738e8e2319b2bec5c3c8 --- unixfs/mod/dagmodifier.go | 3 ++- unixfs/mod/dagmodifier_test.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index e0e09f711..78f7282fb 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -11,6 +11,7 @@ import ( mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + imp "github.com/ipfs/go-ipfs/importer" chunk "github.com/ipfs/go-ipfs/importer/chunk" help "github.com/ipfs/go-ipfs/importer/helpers" trickle "github.com/ipfs/go-ipfs/importer/trickle" @@ -308,7 +309,7 @@ func (dm *DagModifier) appendData(node *mdag.Node, blks <-chan []byte) (*mdag.No dbp := &help.DagBuilderParams{ Dagserv: dm.dagserv, Maxlinks: help.DefaultLinksPerBlock, - Pinner: dm.mp, + BlockCB: imp.BasicPinnerCB(dm.mp), } return trickle.TrickleAppend(node, dbp.New(blks)) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index abc8268e3..3e2bea6cb 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -52,7 +52,7 @@ func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.Blocksto func getNode(t testing.TB, dserv mdag.DAGService, size int64, pinner pin.ManualPinner) ([]byte, *mdag.Node) { in := io.LimitReader(u.NewTimeSeededRand(), size) - node, err := imp.BuildTrickleDagFromReader(in, dserv, pinner, &chunk.SizeSplitter{500}) + node, err := imp.BuildTrickleDagFromReader(in, dserv, &chunk.SizeSplitter{500}, imp.BasicPinnerCB(pinner)) if err != nil { t.Fatal(err) } From f65c844c7bfb7bbd684898192046976bbaf377dc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 19 May 2015 08:52:30 -0700 Subject: [PATCH 0877/3817] change pinning to happen in a callback This commit was moved from ipfs/go-merkledag@df52581658b866730410c0c0d5ea43c8dc47a2fe --- ipld/merkledag/merkledag_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 07525b891..28b58f883 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -156,7 +156,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { spl := &chunk.SizeSplitter{512} - root, err := imp.BuildDagFromReader(read, dagservs[0], nil, spl) + root, err := imp.BuildDagFromReader(read, dagservs[0], spl, nil) if err != nil { t.Fatal(err) } From 72054f7a9c291fd33013aae2760f4c7cded3bb62 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 May 2015 08:28:59 -0700 Subject: [PATCH 0878/3817] make callback take a node instead of a key This commit was moved from ipfs/go-unixfs@0c7270ffb65c686f9b9116f303d808d3e487d70f --- unixfs/mod/dagmodifier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 78f7282fb..bba3139cb 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -309,7 +309,7 @@ func (dm *DagModifier) appendData(node *mdag.Node, blks <-chan []byte) (*mdag.No dbp := &help.DagBuilderParams{ Dagserv: dm.dagserv, Maxlinks: help.DefaultLinksPerBlock, - BlockCB: imp.BasicPinnerCB(dm.mp), + NodeCB: imp.BasicPinnerCB(dm.mp), } return trickle.TrickleAppend(node, dbp.New(blks)) From ef8a490e3b9a3dd8c4c431d26ae92c3c87750fa8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 20 May 2015 22:32:56 -0700 Subject: [PATCH 0879/3817] remove testing imports from non testing code rename bserv mock to mock_test swap out testing.T for an interface This commit was moved from ipfs/go-blockservice@bb4eca868ae1bea8c6752eacf18f40fd67d5a62b --- blockservice/mock.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/blockservice/mock.go b/blockservice/mock.go index 45b440b3e..293d11f16 100644 --- a/blockservice/mock.go +++ b/blockservice/mock.go @@ -1,16 +1,18 @@ package blockservice import ( - "testing" - bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" mockrouting "github.com/ipfs/go-ipfs/routing/mock" delay "github.com/ipfs/go-ipfs/thirdparty/delay" ) +type fataler interface { + Fatal(args ...interface{}) +} + // Mocks returns |n| connected mock Blockservices -func Mocks(t *testing.T, n int) []*BlockService { +func Mocks(t fataler, n int) []*BlockService { net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(0)) sg := bitswap.NewTestSessionGenerator(net) From ee36df77e8fcda8f32997286f265017205e7b74d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Jun 2015 16:10:08 -0700 Subject: [PATCH 0880/3817] move util.Key into its own package under blocks This commit was moved from ipfs/go-ipfs-routing@274b8f77a3cab6d1df6a2d6f1271088f7b30ba43 --- routing/dht/dht.go | 27 ++++++++++++++------------- routing/dht/dht_test.go | 23 ++++++++++++----------- routing/dht/ext_test.go | 14 ++++++-------- routing/dht/handlers.go | 16 ++++++++-------- routing/dht/lookup.go | 6 +++--- routing/dht/pb/message.go | 4 ++-- routing/dht/providers.go | 28 ++++++++++++++-------------- routing/dht/providers_test.go | 4 ++-- routing/dht/query.go | 5 +++-- routing/dht/routing.go | 18 +++++++++--------- routing/kbucket/util.go | 5 +++-- routing/mock/centralized_client.go | 11 ++++++----- routing/mock/centralized_server.go | 12 ++++++------ routing/mock/centralized_test.go | 14 +++++++------- routing/mock/interface.go | 6 +++--- routing/offline/offline.go | 12 ++++++------ routing/record/record.go | 4 ++-- routing/record/validation.go | 11 ++++++----- routing/routing.go | 16 ++++++++-------- routing/supernode/client.go | 12 ++++++------ routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 20 ++++++++++---------- routing/supernode/server_test.go | 4 ++-- 23 files changed, 141 insertions(+), 137 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 8c5ceaa61..b1b13985b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -10,6 +10,7 @@ import ( "sync" "time" + key "github.com/ipfs/go-ipfs/blocks/key" ci "github.com/ipfs/go-ipfs/p2p/crypto" host "github.com/ipfs/go-ipfs/p2p/host" peer "github.com/ipfs/go-ipfs/p2p/peer" @@ -122,7 +123,7 @@ func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.ID) error { // putValueToPeer stores the given key/value pair at the peer 'p' func (dht *IpfsDHT) putValueToPeer(ctx context.Context, p peer.ID, - key u.Key, rec *pb.Record) error { + key key.Key, rec *pb.Record) error { pmes := pb.NewMessage(pb.Message_PUT_VALUE, string(key), 0) pmes.Record = rec @@ -139,7 +140,7 @@ func (dht *IpfsDHT) putValueToPeer(ctx context.Context, p peer.ID, // putProvider sends a message to peer 'p' saying that the local node // can provide the value of 'key' -func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, key string) error { +func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, skey string) error { // add self as the provider pi := peer.PeerInfo{ @@ -150,18 +151,18 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, key string) erro // // only share WAN-friendly addresses ?? // pi.Addrs = addrutil.WANShareableAddrs(pi.Addrs) if len(pi.Addrs) < 1 { - // log.Infof("%s putProvider: %s for %s error: no wan-friendly addresses", dht.self, p, u.Key(key), pi.Addrs) + // log.Infof("%s putProvider: %s for %s error: no wan-friendly addresses", dht.self, p, key.Key(key), pi.Addrs) return fmt.Errorf("no known addresses for self. cannot put provider.") } - pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, string(key), 0) + pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, skey, 0) pmes.ProviderPeers = pb.RawPeerInfosToPBPeers([]peer.PeerInfo{pi}) err := dht.sendMessage(ctx, p, pmes) if err != nil { return err } - log.Debugf("%s putProvider: %s for %s (%s)", dht.self, p, u.Key(key), pi.Addrs) + log.Debugf("%s putProvider: %s for %s (%s)", dht.self, p, key.Key(skey), pi.Addrs) return nil } @@ -170,7 +171,7 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, key string) erro // NOTE: it will update the dht's peerstore with any new addresses // it finds for the given peer. func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.ID, - key u.Key) ([]byte, []peer.PeerInfo, error) { + key key.Key) ([]byte, []peer.PeerInfo, error) { pmes, err := dht.getValueSingle(ctx, p, key) if err != nil { @@ -203,7 +204,7 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.ID, // getValueSingle simply performs the get value RPC with the given parameters func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.ID, - key u.Key) (*pb.Message, error) { + key key.Key) (*pb.Message, error) { defer log.EventBegin(ctx, "getValueSingle", p, &key).Done() pmes := pb.NewMessage(pb.Message_GET_VALUE, string(key), 0) @@ -211,7 +212,7 @@ func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.ID, } // getLocal attempts to retrieve the value from the datastore -func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) { +func (dht *IpfsDHT) getLocal(key key.Key) ([]byte, error) { log.Debug("getLocal %s", key) v, err := dht.datastore.Get(key.DsKey()) @@ -254,7 +255,7 @@ func (dht *IpfsDHT) getOwnPrivateKey() (ci.PrivKey, error) { } // putLocal stores the key value pair in the datastore -func (dht *IpfsDHT) putLocal(key u.Key, rec *pb.Record) error { +func (dht *IpfsDHT) putLocal(key key.Key, rec *pb.Record) error { data, err := proto.Marshal(rec) if err != nil { return err @@ -287,7 +288,7 @@ func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.ID, id peer.ID) ( return dht.sendRequest(ctx, p, pmes) } -func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.ID, key u.Key) (*pb.Message, error) { +func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.ID, key key.Key) (*pb.Message, error) { defer log.EventBegin(ctx, "findProvidersSingle", p, &key).Done() pmes := pb.NewMessage(pb.Message_GET_PROVIDERS, string(key), 0) @@ -296,7 +297,7 @@ func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.ID, key u.Ke // nearestPeersToQuery returns the routing tables closest peers. func (dht *IpfsDHT) nearestPeersToQuery(pmes *pb.Message, count int) []peer.ID { - key := u.Key(pmes.GetKey()) + key := key.Key(pmes.GetKey()) closer := dht.routingTable.NearestPeers(kb.ConvertKey(key), count) return closer } @@ -326,7 +327,7 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, p peer.ID, count int) [ } // must all be closer than self - key := u.Key(pmes.GetKey()) + key := key.Key(pmes.GetKey()) if !kb.Closer(dht.self, clp, key) { filtered = append(filtered, clp) } @@ -355,7 +356,7 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { case <-tick: id := make([]byte, 16) rand.Read(id) - peers := dht.routingTable.NearestPeers(kb.ConvertKey(u.Key(id)), 5) + peers := dht.routingTable.NearestPeers(kb.ConvertKey(key.Key(id)), 5) for _, p := range peers { ctx, cancel := context.WithTimeout(dht.Context(), time.Second*5) _, err := dht.Ping(ctx, p) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 29b57816f..a6eb41a77 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -14,6 +14,7 @@ import ( ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" netutil "github.com/ipfs/go-ipfs/p2p/test/util" routing "github.com/ipfs/go-ipfs/routing" @@ -24,14 +25,14 @@ import ( travisci "github.com/ipfs/go-ipfs/util/testutil/ci/travis" ) -var testCaseValues = map[u.Key][]byte{} +var testCaseValues = map[key.Key][]byte{} func init() { testCaseValues["hello"] = []byte("world") for i := 0; i < 100; i++ { k := fmt.Sprintf("%d -- key", i) v := fmt.Sprintf("%d -- value", i) - testCaseValues[u.Key(k)] = []byte(v) + testCaseValues[key.Key(k)] = []byte(v) } } @@ -42,7 +43,7 @@ func setupDHT(ctx context.Context, t *testing.T) *IpfsDHT { d := NewDHT(ctx, h, dss) d.Validator["v"] = &record.ValidChecker{ - Func: func(u.Key, []byte) error { + Func: func(key.Key, []byte) error { return nil }, Sign: false, @@ -143,7 +144,7 @@ func TestValueGetSet(t *testing.T) { defer dhtB.host.Close() vf := &record.ValidChecker{ - Func: func(u.Key, []byte) error { + Func: func(key.Key, []byte) error { return nil }, Sign: false, @@ -460,7 +461,7 @@ func TestProvidesMany(t *testing.T) { } } - var providers = map[u.Key]peer.ID{} + var providers = map[key.Key]peer.ID{} d := 0 for k, v := range testCaseValues { @@ -501,7 +502,7 @@ func TestProvidesMany(t *testing.T) { ctxT, _ = context.WithTimeout(ctx, 5*time.Second) var wg sync.WaitGroup - getProvider := func(dht *IpfsDHT, k u.Key) { + getProvider := func(dht *IpfsDHT, k key.Key) { defer wg.Done() expected := providers[k] @@ -561,7 +562,7 @@ func TestProvidesAsync(t *testing.T) { connect(t, ctx, dhts[1], dhts[2]) connect(t, ctx, dhts[1], dhts[3]) - k := u.Key("hello") + k := key.Key("hello") val := []byte("world") sk := dhts[3].peerstore.PrivKey(dhts[3].self) rec, err := record.MakePutRecord(sk, k, val, false) @@ -579,7 +580,7 @@ func TestProvidesAsync(t *testing.T) { t.Fatal(err) } - err = dhts[3].Provide(ctx, u.Key("hello")) + err = dhts[3].Provide(ctx, key.Key("hello")) if err != nil { t.Fatal(err) } @@ -587,7 +588,7 @@ func TestProvidesAsync(t *testing.T) { time.Sleep(time.Millisecond * 60) ctxT, _ := context.WithTimeout(ctx, time.Millisecond*300) - provs := dhts[0].FindProvidersAsync(ctxT, u.Key("hello"), 5) + provs := dhts[0].FindProvidersAsync(ctxT, key.Key("hello"), 5) select { case p, ok := <-provs: if !ok { @@ -624,7 +625,7 @@ func TestLayeredGet(t *testing.T) { connect(t, ctx, dhts[1], dhts[2]) connect(t, ctx, dhts[1], dhts[3]) - err := dhts[3].Provide(ctx, u.Key("/v/hello")) + err := dhts[3].Provide(ctx, key.Key("/v/hello")) if err != nil { t.Fatal(err) } @@ -633,7 +634,7 @@ func TestLayeredGet(t *testing.T) { t.Log("interface was changed. GetValue should not use providers.") ctxT, _ := context.WithTimeout(ctx, time.Second) - val, err := dhts[0].GetValue(ctxT, u.Key("/v/hello")) + val, err := dhts[0].GetValue(ctxT, key.Key("/v/hello")) if err != routing.ErrNotFound { t.Error(err) } diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 5ac342e3a..c77116578 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -12,6 +12,7 @@ import ( dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" inet "github.com/ipfs/go-ipfs/p2p/net" mocknet "github.com/ipfs/go-ipfs/p2p/net/mock" peer "github.com/ipfs/go-ipfs/p2p/peer" @@ -37,7 +38,6 @@ func TestGetFailures(t *testing.T) { d := NewDHT(ctx, hosts[0], tsds) d.Update(ctx, hosts[1].ID()) - // u.POut("NotFound Test\n") // Reply with failures to every message hosts[1].SetStreamHandler(ProtocolDHT, func(s inet.Stream) { defer s.Close() @@ -45,9 +45,8 @@ func TestGetFailures(t *testing.T) { }) // This one should time out - // u.POut("Timout Test\n") ctx1, _ := context.WithTimeout(context.Background(), 200*time.Millisecond) - if _, err := d.GetValue(ctx1, u.Key("test")); err != nil { + if _, err := d.GetValue(ctx1, key.Key("test")); err != nil { if merr, ok := err.(u.MultiErr); ok && len(merr) > 0 { err = merr[0] } @@ -87,7 +86,7 @@ func TestGetFailures(t *testing.T) { // (was 3 seconds before which should be _plenty_ of time, but maybe // travis machines really have a hard time...) ctx2, _ := context.WithTimeout(context.Background(), 20*time.Second) - _, err = d.GetValue(ctx2, u.Key("test")) + _, err = d.GetValue(ctx2, key.Key("test")) if err != nil { if merr, ok := err.(u.MultiErr); ok && len(merr) > 0 { err = merr[0] @@ -111,7 +110,7 @@ func TestGetFailures(t *testing.T) { t.Fatal(err) } - rec, err := record.MakePutRecord(sk, u.Key(str), []byte("blah"), true) + rec, err := record.MakePutRecord(sk, key.Key(str), []byte("blah"), true) if err != nil { t.Fatal(err) } @@ -121,7 +120,6 @@ func TestGetFailures(t *testing.T) { Record: rec, } - // u.POut("handleGetValue Test\n") s, err := hosts[1].NewStream(ProtocolDHT, hosts[0].ID()) if err != nil { t.Fatal(err) @@ -205,7 +203,7 @@ func TestNotFound(t *testing.T) { // long timeout to ensure timing is not at play. ctx, _ = context.WithTimeout(ctx, time.Second*20) - v, err := d.GetValue(ctx, u.Key("hello")) + v, err := d.GetValue(ctx, key.Key("hello")) log.Debugf("get value got %v", v) if err != nil { if merr, ok := err.(u.MultiErr); ok && len(merr) > 0 { @@ -277,7 +275,7 @@ func TestLessThanKResponses(t *testing.T) { } ctx, _ = context.WithTimeout(ctx, time.Second*30) - if _, err := d.GetValue(ctx, u.Key("hello")); err != nil { + if _, err := d.GetValue(ctx, key.Key("hello")); err != nil { switch err { case routing.ErrNotFound: //Success! diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 5449cad43..b3db5d87e 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -7,9 +7,9 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - u "github.com/ipfs/go-ipfs/util" lgbl "github.com/ipfs/go-ipfs/util/eventlog/loggables" ) @@ -46,15 +46,15 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Mess resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) // first, is there even a key? - key := pmes.GetKey() - if key == "" { + k := pmes.GetKey() + if k == "" { return nil, errors.New("handleGetValue but no key was provided") // TODO: send back an error response? could be bad, but the other node's hanging. } // let's first check if we have the value locally. log.Debugf("%s handleGetValue looking into ds", dht.self) - dskey := u.Key(pmes.GetKey()).DsKey() + dskey := key.Key(k).DsKey() iVal, err := dht.datastore.Get(dskey) log.Debugf("%s handleGetValue looking into ds GOT %v", dht.self, iVal) @@ -105,10 +105,10 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Mess // Store a value in this peer local storage func (dht *IpfsDHT) handlePutValue(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { defer log.EventBegin(ctx, "handlePutValue", p).Done() - dskey := u.Key(pmes.GetKey()).DsKey() + dskey := key.Key(pmes.GetKey()).DsKey() if err := dht.verifyRecordLocally(pmes.GetRecord()); err != nil { - log.Debugf("Bad dht record in PUT from: %s. %s", u.Key(pmes.GetRecord().GetAuthor()), err) + log.Debugf("Bad dht record in PUT from: %s. %s", key.Key(pmes.GetRecord().GetAuthor()), err) return nil, err } @@ -163,7 +163,7 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb. defer log.EventBegin(ctx, "handleGetProviders", lm).Done() resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) - key := u.Key(pmes.GetKey()) + key := key.Key(pmes.GetKey()) lm["key"] = func() interface{} { return key.Pretty() } // debug logging niceness. @@ -207,7 +207,7 @@ func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.M lm["peer"] = func() interface{} { return p.Pretty() } defer log.EventBegin(ctx, "handleAddProvider", lm).Done() - key := u.Key(pmes.GetKey()) + key := key.Key(pmes.GetKey()) lm["key"] = func() interface{} { return key.Pretty() } log.Debugf("%s adding %s as a provider for '%s'\n", dht.self, p, key) diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 76671657a..a10073640 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -2,10 +2,10 @@ package dht import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" notif "github.com/ipfs/go-ipfs/notifications" peer "github.com/ipfs/go-ipfs/p2p/peer" kb "github.com/ipfs/go-ipfs/routing/kbucket" - u "github.com/ipfs/go-ipfs/util" pset "github.com/ipfs/go-ipfs/util/peerset" ) @@ -21,7 +21,7 @@ func pointerizePeerInfos(pis []peer.PeerInfo) []*peer.PeerInfo { // Kademlia 'node lookup' operation. Returns a channel of the K closest peers // to the given key -func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key u.Key) (<-chan peer.ID, error) { +func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key key.Key) (<-chan peer.ID, error) { e := log.EventBegin(ctx, "getClosestPeers", &key) tablepeers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) if len(tablepeers) == 0 { @@ -88,7 +88,7 @@ func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key u.Key) (<-chan peer return out, nil } -func (dht *IpfsDHT) closerPeersSingle(ctx context.Context, key u.Key, p peer.ID) ([]peer.ID, error) { +func (dht *IpfsDHT) closerPeersSingle(ctx context.Context, key key.Key, p peer.ID) ([]peer.ID, error) { pmes, err := dht.findPeerSingle(ctx, p, peer.ID(key)) if err != nil { return nil, err diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 10279abd2..24ad1d6f0 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -3,10 +3,10 @@ package dht_pb import ( ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + key "github.com/ipfs/go-ipfs/blocks/key" inet "github.com/ipfs/go-ipfs/p2p/net" peer "github.com/ipfs/go-ipfs/p2p/peer" eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" - util "github.com/ipfs/go-ipfs/util" ) var log = eventlog.Logger("dht.pb") @@ -143,7 +143,7 @@ func (m *Message) Loggable() map[string]interface{} { return map[string]interface{}{ "message": map[string]string{ "type": m.Type.String(), - "key": util.Key(m.GetKey()).Pretty(), + "key": key.Key(m.GetKey()).Pretty(), }, } } diff --git a/routing/dht/providers.go b/routing/dht/providers.go index e803398be..2b7fa2cbd 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -4,8 +4,8 @@ import ( "time" ctxgroup "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" + key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" - u "github.com/ipfs/go-ipfs/util" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) @@ -16,10 +16,10 @@ type providerInfo struct { } type ProviderManager struct { - providers map[u.Key][]*providerInfo - local map[u.Key]struct{} + providers map[key.Key][]*providerInfo + local map[key.Key]struct{} lpeer peer.ID - getlocal chan chan []u.Key + getlocal chan chan []key.Key newprovs chan *addProv getprovs chan *getProv period time.Duration @@ -27,12 +27,12 @@ type ProviderManager struct { } type addProv struct { - k u.Key + k key.Key val peer.ID } type getProv struct { - k u.Key + k key.Key resp chan []peer.ID } @@ -40,9 +40,9 @@ func NewProviderManager(ctx context.Context, local peer.ID) *ProviderManager { pm := new(ProviderManager) pm.getprovs = make(chan *getProv) pm.newprovs = make(chan *addProv) - pm.providers = make(map[u.Key][]*providerInfo) - pm.getlocal = make(chan chan []u.Key) - pm.local = make(map[u.Key]struct{}) + pm.providers = make(map[key.Key][]*providerInfo) + pm.getlocal = make(chan chan []key.Key) + pm.local = make(map[key.Key]struct{}) pm.ContextGroup = ctxgroup.WithContext(ctx) pm.Children().Add(1) @@ -76,7 +76,7 @@ func (pm *ProviderManager) run() { gp.resp <- parr case lc := <-pm.getlocal: - var keys []u.Key + var keys []key.Key for k := range pm.local { keys = append(keys, k) } @@ -99,7 +99,7 @@ func (pm *ProviderManager) run() { } } -func (pm *ProviderManager) AddProvider(ctx context.Context, k u.Key, val peer.ID) { +func (pm *ProviderManager) AddProvider(ctx context.Context, k key.Key, val peer.ID) { prov := &addProv{ k: k, val: val, @@ -110,7 +110,7 @@ func (pm *ProviderManager) AddProvider(ctx context.Context, k u.Key, val peer.ID } } -func (pm *ProviderManager) GetProviders(ctx context.Context, k u.Key) []peer.ID { +func (pm *ProviderManager) GetProviders(ctx context.Context, k key.Key) []peer.ID { gp := &getProv{ k: k, resp: make(chan []peer.ID, 1), // buffered to prevent sender from blocking @@ -128,8 +128,8 @@ func (pm *ProviderManager) GetProviders(ctx context.Context, k u.Key) []peer.ID } } -func (pm *ProviderManager) GetLocal() []u.Key { - resp := make(chan []u.Key) +func (pm *ProviderManager) GetLocal() []key.Key { + resp := make(chan []key.Key) pm.getlocal <- resp return <-resp } diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 159634a80..ecf937962 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -3,8 +3,8 @@ package dht import ( "testing" + key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" - u "github.com/ipfs/go-ipfs/util" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) @@ -13,7 +13,7 @@ func TestProviderManager(t *testing.T) { ctx := context.Background() mid := peer.ID("testing") p := NewProviderManager(ctx, mid) - a := u.Key("test") + a := key.Key("test") p.AddProvider(ctx, a, peer.ID("testingprovider")) resp := p.GetProviders(ctx, a) if len(resp) != 1 { diff --git a/routing/dht/query.go b/routing/dht/query.go index d833b126c..c69437f49 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -3,6 +3,7 @@ package dht import ( "sync" + key "github.com/ipfs/go-ipfs/blocks/key" notif "github.com/ipfs/go-ipfs/notifications" peer "github.com/ipfs/go-ipfs/p2p/peer" queue "github.com/ipfs/go-ipfs/p2p/peer/queue" @@ -21,7 +22,7 @@ var maxQueryConcurrency = AlphaValue type dhtQuery struct { dht *IpfsDHT - key u.Key // the key we're querying for + key key.Key // the key we're querying for qfunc queryFunc // the function to execute per peer concurrency int // the concurrency parameter } @@ -35,7 +36,7 @@ type dhtQueryResult struct { } // constructs query -func (dht *IpfsDHT) newQuery(k u.Key, f queryFunc) *dhtQuery { +func (dht *IpfsDHT) newQuery(k key.Key, f queryFunc) *dhtQuery { return &dhtQuery{ key: k, dht: dht, diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 47e892414..c4dc76ac4 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -5,6 +5,7 @@ import ( "time" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" notif "github.com/ipfs/go-ipfs/notifications" inet "github.com/ipfs/go-ipfs/p2p/net" peer "github.com/ipfs/go-ipfs/p2p/peer" @@ -12,7 +13,6 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - u "github.com/ipfs/go-ipfs/util" pset "github.com/ipfs/go-ipfs/util/peerset" ) @@ -28,7 +28,7 @@ var asyncQueryBuffer = 10 // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT -func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error { +func (dht *IpfsDHT) PutValue(ctx context.Context, key key.Key, value []byte) error { log.Debugf("PutValue %s", key) sk, err := dht.getOwnPrivateKey() if err != nil { @@ -79,7 +79,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error // GetValue searches for the value corresponding to given Key. // If the search does not succeed, a multiaddr string of a closer peer is // returned along with util.ErrSearchIncomplete -func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { +func (dht *IpfsDHT) GetValue(ctx context.Context, key key.Key) ([]byte, error) { // If we have it local, dont bother doing an RPC! val, err := dht.getLocal(key) if err == nil { @@ -141,7 +141,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) { // This is what DSHTs (Coral and MainlineDHT) do to store large values in a DHT. // Provide makes this node announce that it can provide a value for the given key -func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { +func (dht *IpfsDHT) Provide(ctx context.Context, key key.Key) error { defer log.EventBegin(ctx, "provide", &key).Done() // add self locally @@ -169,7 +169,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error { } // FindProviders searches until the context expires. -func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]peer.PeerInfo, error) { +func (dht *IpfsDHT) FindProviders(ctx context.Context, key key.Key) ([]peer.PeerInfo, error) { var providers []peer.PeerInfo for p := range dht.FindProvidersAsync(ctx, key, KValue) { providers = append(providers, p) @@ -180,14 +180,14 @@ func (dht *IpfsDHT) FindProviders(ctx context.Context, key u.Key) ([]peer.PeerIn // FindProvidersAsync is the same thing as FindProviders, but returns a channel. // Peers will be returned on the channel as soon as they are found, even before // the search query completes. -func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int) <-chan peer.PeerInfo { +func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key key.Key, count int) <-chan peer.PeerInfo { log.Event(ctx, "findProviders", &key) peerOut := make(chan peer.PeerInfo, count) go dht.findProvidersAsyncRoutine(ctx, key, count, peerOut) return peerOut } -func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key u.Key, count int, peerOut chan peer.PeerInfo) { +func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key key.Key, count int, peerOut chan peer.PeerInfo) { defer log.EventBegin(ctx, "findProvidersAsync", &key).Done() defer close(peerOut) @@ -289,7 +289,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, er } // setup the Query - query := dht.newQuery(u.Key(id), func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { + query := dht.newQuery(key.Key(id), func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ Type: notif.SendingQuery, ID: p, @@ -347,7 +347,7 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< } // setup the Query - query := dht.newQuery(u.Key(id), func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { + query := dht.newQuery(key.Key(id), func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { pmes, err := dht.findPeerSingle(ctx, p, id) if err != nil { diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index e7c56f868..e37a70183 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "errors" + key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" ks "github.com/ipfs/go-ipfs/routing/keyspace" u "github.com/ipfs/go-ipfs/util" @@ -45,13 +46,13 @@ func ConvertPeerID(id peer.ID) ID { } // ConvertKey creates a DHT ID by hashing a local key (String) -func ConvertKey(id u.Key) ID { +func ConvertKey(id key.Key) ID { hash := sha256.Sum256([]byte(id)) return hash[:] } // Closer returns true if a is closer to key than b is -func Closer(a, b peer.ID, key u.Key) bool { +func Closer(a, b peer.ID, key key.Key) bool { aid := ConvertPeerID(a) bid := ConvertPeerID(b) tgt := ConvertKey(key) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 7b04c9762..6085b69be 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -7,6 +7,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" @@ -22,13 +23,13 @@ type client struct { } // FIXME(brian): is this method meant to simulate putting a value into the network? -func (c *client) PutValue(ctx context.Context, key u.Key, val []byte) error { +func (c *client) PutValue(ctx context.Context, key key.Key, val []byte) error { log.Debugf("PutValue: %s", key) return c.datastore.Put(key.DsKey(), val) } // FIXME(brian): is this method meant to simulate getting a value from the network? -func (c *client) GetValue(ctx context.Context, key u.Key) ([]byte, error) { +func (c *client) GetValue(ctx context.Context, key key.Key) ([]byte, error) { log.Debugf("GetValue: %s", key) v, err := c.datastore.Get(key.DsKey()) if err != nil { @@ -43,7 +44,7 @@ func (c *client) GetValue(ctx context.Context, key u.Key) ([]byte, error) { return data, nil } -func (c *client) FindProviders(ctx context.Context, key u.Key) ([]peer.PeerInfo, error) { +func (c *client) FindProviders(ctx context.Context, key key.Key) ([]peer.PeerInfo, error) { return c.server.Providers(key), nil } @@ -52,7 +53,7 @@ func (c *client) FindPeer(ctx context.Context, pid peer.ID) (peer.PeerInfo, erro return peer.PeerInfo{}, nil } -func (c *client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-chan peer.PeerInfo { +func (c *client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan peer.PeerInfo { out := make(chan peer.PeerInfo) go func() { defer close(out) @@ -72,7 +73,7 @@ func (c *client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-cha // Provide returns once the message is on the network. Value is not necessarily // visible yet. -func (c *client) Provide(_ context.Context, key u.Key) error { +func (c *client) Provide(_ context.Context, key key.Key) error { info := peer.PeerInfo{ ID: c.peer.ID(), Addrs: []ma.Multiaddr{c.peer.Address()}, diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index da2cedf48..c7bd239ed 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -7,15 +7,15 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" - u "github.com/ipfs/go-ipfs/util" "github.com/ipfs/go-ipfs/util/testutil" ) // server is the mockrouting.Client's private interface to the routing server type server interface { - Announce(peer.PeerInfo, u.Key) error - Providers(u.Key) []peer.PeerInfo + Announce(peer.PeerInfo, key.Key) error + Providers(key.Key) []peer.PeerInfo Server } @@ -25,7 +25,7 @@ type s struct { delayConf DelayConfig lock sync.RWMutex - providers map[u.Key]map[peer.ID]providerRecord + providers map[key.Key]map[peer.ID]providerRecord } type providerRecord struct { @@ -33,7 +33,7 @@ type providerRecord struct { Created time.Time } -func (rs *s) Announce(p peer.PeerInfo, k u.Key) error { +func (rs *s) Announce(p peer.PeerInfo, k key.Key) error { rs.lock.Lock() defer rs.lock.Unlock() @@ -48,7 +48,7 @@ func (rs *s) Announce(p peer.PeerInfo, k u.Key) error { return nil } -func (rs *s) Providers(k u.Key) []peer.PeerInfo { +func (rs *s) Providers(k key.Key) []peer.PeerInfo { rs.delayConf.Query.Wait() // before locking rs.lock.RLock() diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 38b58cb64..a719570aa 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -5,16 +5,16 @@ import ( "time" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - u "github.com/ipfs/go-ipfs/util" "github.com/ipfs/go-ipfs/util/testutil" ) func TestKeyNotFound(t *testing.T) { var pi = testutil.RandIdentityOrFatal(t) - var key = u.Key("mock key") + var key = key.Key("mock key") var ctx = context.Background() rs := NewServer() @@ -30,7 +30,7 @@ func TestClientFindProviders(t *testing.T) { rs := NewServer() client := rs.Client(pi) - k := u.Key("hello") + k := key.Key("hello") err := client.Provide(context.Background(), k) if err != nil { t.Fatal(err) @@ -40,7 +40,7 @@ func TestClientFindProviders(t *testing.T) { time.Sleep(time.Millisecond * 300) max := 100 - providersFromClient := client.FindProvidersAsync(context.Background(), u.Key("hello"), max) + providersFromClient := client.FindProvidersAsync(context.Background(), key.Key("hello"), max) isInClient := false for pi := range providersFromClient { if pi.ID == pi.ID { @@ -54,7 +54,7 @@ func TestClientFindProviders(t *testing.T) { func TestClientOverMax(t *testing.T) { rs := NewServer() - k := u.Key("hello") + k := key.Key("hello") numProvidersForHelloKey := 100 for i := 0; i < numProvidersForHelloKey; i++ { pi := testutil.RandIdentityOrFatal(t) @@ -81,7 +81,7 @@ func TestClientOverMax(t *testing.T) { // TODO does dht ensure won't receive self as a provider? probably not. func TestCanceledContext(t *testing.T) { rs := NewServer() - k := u.Key("hello") + k := key.Key("hello") // avoid leaking goroutine, without using the context to signal // (we want the goroutine to keep trying to publish on a @@ -139,7 +139,7 @@ func TestCanceledContext(t *testing.T) { func TestValidAfter(t *testing.T) { pi := testutil.RandIdentityOrFatal(t) - var key = u.Key("mock key") + var key = key.Key("mock key") var ctx = context.Background() conf := DelayConfig{ ValueVisibility: delay.Fixed(1 * time.Hour), diff --git a/routing/mock/interface.go b/routing/mock/interface.go index df29fdbb9..f18e387d8 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -7,10 +7,10 @@ package mockrouting import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - u "github.com/ipfs/go-ipfs/util" "github.com/ipfs/go-ipfs/util/testutil" ) @@ -22,7 +22,7 @@ type Server interface { // Client implements IpfsRouting type Client interface { - FindProviders(context.Context, u.Key) ([]peer.PeerInfo, error) + FindProviders(context.Context, key.Key) ([]peer.PeerInfo, error) routing.IpfsRouting } @@ -37,7 +37,7 @@ func NewServer() Server { // NewServerWithDelay returns a mockrouting Server with a delay! func NewServerWithDelay(conf DelayConfig) Server { return &s{ - providers: make(map[u.Key]map[peer.ID]providerRecord), + providers: make(map[key.Key]map[peer.ID]providerRecord), delayConf: conf, } } diff --git a/routing/offline/offline.go b/routing/offline/offline.go index a94e0c3c7..2ef4e5633 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,13 +7,13 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" ci "github.com/ipfs/go-ipfs/p2p/crypto" "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" - u "github.com/ipfs/go-ipfs/util" ) var log = eventlog.Logger("offlinerouting") @@ -35,7 +35,7 @@ type offlineRouting struct { sk ci.PrivKey } -func (c *offlineRouting) PutValue(ctx context.Context, key u.Key, val []byte) error { +func (c *offlineRouting) PutValue(ctx context.Context, key key.Key, val []byte) error { rec, err := record.MakePutRecord(c.sk, key, val, false) if err != nil { return err @@ -48,7 +48,7 @@ func (c *offlineRouting) PutValue(ctx context.Context, key u.Key, val []byte) er return c.datastore.Put(key.DsKey(), data) } -func (c *offlineRouting) GetValue(ctx context.Context, key u.Key) ([]byte, error) { +func (c *offlineRouting) GetValue(ctx context.Context, key key.Key) ([]byte, error) { v, err := c.datastore.Get(key.DsKey()) if err != nil { return nil, err @@ -67,7 +67,7 @@ func (c *offlineRouting) GetValue(ctx context.Context, key u.Key) ([]byte, error return rec.GetValue(), nil } -func (c *offlineRouting) FindProviders(ctx context.Context, key u.Key) ([]peer.PeerInfo, error) { +func (c *offlineRouting) FindProviders(ctx context.Context, key key.Key) ([]peer.PeerInfo, error) { return nil, ErrOffline } @@ -75,13 +75,13 @@ func (c *offlineRouting) FindPeer(ctx context.Context, pid peer.ID) (peer.PeerIn return peer.PeerInfo{}, ErrOffline } -func (c *offlineRouting) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-chan peer.PeerInfo { +func (c *offlineRouting) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan peer.PeerInfo { out := make(chan peer.PeerInfo) close(out) return out } -func (c *offlineRouting) Provide(_ context.Context, key u.Key) error { +func (c *offlineRouting) Provide(_ context.Context, key key.Key) error { return ErrOffline } diff --git a/routing/record/record.go b/routing/record/record.go index ae423a172..4e1d54a4d 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -5,16 +5,16 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + key "github.com/ipfs/go-ipfs/blocks/key" ci "github.com/ipfs/go-ipfs/p2p/crypto" pb "github.com/ipfs/go-ipfs/routing/dht/pb" eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" - u "github.com/ipfs/go-ipfs/util" ) var log = eventlog.Logger("routing/record") // MakePutRecord creates and signs a dht record for the given key/value pair -func MakePutRecord(sk ci.PrivKey, key u.Key, value []byte, sign bool) (*pb.Record, error) { +func MakePutRecord(sk ci.PrivKey, key key.Key, value []byte, sign bool) (*pb.Record, error) { record := new(pb.Record) record.Key = proto.String(string(key)) diff --git a/routing/record/validation.go b/routing/record/validation.go index 8d2657fe2..f186bea90 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -5,6 +5,7 @@ import ( "errors" "strings" + key "github.com/ipfs/go-ipfs/blocks/key" ci "github.com/ipfs/go-ipfs/p2p/crypto" pb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" @@ -12,7 +13,7 @@ import ( // ValidatorFunc is a function that is called to validate a given // type of DHTRecord. -type ValidatorFunc func(u.Key, []byte) error +type ValidatorFunc func(key.Key, []byte) error // ErrBadRecord is returned any time a dht record is found to be // incorrectly formatted or signed. @@ -38,7 +39,7 @@ func (v Validator) VerifyRecord(r *pb.Record) error { // Now, check validity func parts := strings.Split(r.GetKey(), "/") if len(parts) < 3 { - log.Infof("Record key does not have validator: %s", u.Key(r.GetKey())) + log.Infof("Record key does not have validator: %s", key.Key(r.GetKey())) return nil } @@ -48,10 +49,10 @@ func (v Validator) VerifyRecord(r *pb.Record) error { return ErrInvalidRecordType } - return val.Func(u.Key(r.GetKey()), r.GetValue()) + return val.Func(key.Key(r.GetKey()), r.GetValue()) } -func (v Validator) IsSigned(k u.Key) (bool, error) { +func (v Validator) IsSigned(k key.Key) (bool, error) { // Now, check validity func parts := strings.Split(string(k), "/") if len(parts) < 3 { @@ -71,7 +72,7 @@ func (v Validator) IsSigned(k u.Key) (bool, error) { // ValidatePublicKeyRecord implements ValidatorFunc and // verifies that the passed in record value is the PublicKey // that matches the passed in key. -func ValidatePublicKeyRecord(k u.Key, val []byte) error { +func ValidatePublicKeyRecord(k key.Key, val []byte) error { keyparts := bytes.Split([]byte(k), []byte("/")) if len(keyparts) < 3 { return errors.New("invalid key") diff --git a/routing/routing.go b/routing/routing.go index c94d813ae..31be8f3f8 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -6,9 +6,9 @@ import ( "time" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" ci "github.com/ipfs/go-ipfs/p2p/crypto" peer "github.com/ipfs/go-ipfs/p2p/peer" - u "github.com/ipfs/go-ipfs/util" ) // ErrNotFound is returned when a search fails to find anything @@ -17,21 +17,21 @@ var ErrNotFound = errors.New("routing: not found") // IpfsRouting is the routing module interface // It is implemented by things like DHTs, etc. type IpfsRouting interface { - FindProvidersAsync(context.Context, u.Key, int) <-chan peer.PeerInfo + FindProvidersAsync(context.Context, key.Key, int) <-chan peer.PeerInfo // Basic Put/Get // PutValue adds value corresponding to given Key. - PutValue(context.Context, u.Key, []byte) error + PutValue(context.Context, key.Key, []byte) error // GetValue searches for the value corresponding to given Key. - GetValue(context.Context, u.Key) ([]byte, error) + GetValue(context.Context, key.Key) ([]byte, error) // Value provider layer of indirection. // This is what DSHTs (Coral and MainlineDHT) do to store large values in a DHT. // Announce that this node can provide value for given key - Provide(context.Context, u.Key) error + Provide(context.Context, key.Key) error // Find specific Peer // FindPeer searches for a peer with given ID, returns a peer.PeerInfo @@ -54,8 +54,8 @@ type PubKeyFetcher interface { // KeyForPublicKey returns the key used to retrieve public keys // from the dht. -func KeyForPublicKey(id peer.ID) u.Key { - return u.Key("/pk/" + string(id)) +func KeyForPublicKey(id peer.ID) key.Key { + return key.Key("/pk/" + string(id)) } func GetPublicKey(r IpfsRouting, ctx context.Context, pkhash []byte) (ci.PubKey, error) { @@ -63,7 +63,7 @@ func GetPublicKey(r IpfsRouting, ctx context.Context, pkhash []byte) (ci.PubKey, // If we have a DHT as our routing system, use optimized fetcher return dht.GetPublicKey(ctx, peer.ID(pkhash)) } else { - key := u.Key("/pk/" + string(pkhash)) + key := key.Key("/pk/" + string(pkhash)) pkval, err := r.GetValue(ctx, key) if err != nil { return nil, err diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 14f6a4db5..5269be51b 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,13 +8,13 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/p2p/host" peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" - u "github.com/ipfs/go-ipfs/util" ) var log = eventlog.Logger("supernode") @@ -36,7 +36,7 @@ func NewClient(px proxy.Proxy, h host.Host, ps peer.Peerstore, local peer.ID) (* }, nil } -func (c *Client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-chan peer.PeerInfo { +func (c *Client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan peer.PeerInfo { ctx = eventlog.ContextWithLoggable(ctx, eventlog.Uuid("findProviders")) defer log.EventBegin(ctx, "findProviders", &k).Done() ch := make(chan peer.PeerInfo) @@ -60,7 +60,7 @@ func (c *Client) FindProvidersAsync(ctx context.Context, k u.Key, max int) <-cha return ch } -func (c *Client) PutValue(ctx context.Context, k u.Key, v []byte) error { +func (c *Client) PutValue(ctx context.Context, k key.Key, v []byte) error { defer log.EventBegin(ctx, "putValue", &k).Done() r, err := makeRecord(c.peerstore, c.local, k, v) if err != nil { @@ -71,7 +71,7 @@ func (c *Client) PutValue(ctx context.Context, k u.Key, v []byte) error { return c.proxy.SendMessage(ctx, pmes) // wrap to hide the remote } -func (c *Client) GetValue(ctx context.Context, k u.Key) ([]byte, error) { +func (c *Client) GetValue(ctx context.Context, k key.Key) ([]byte, error) { defer log.EventBegin(ctx, "getValue", &k).Done() msg := pb.NewMessage(pb.Message_GET_VALUE, string(k), 0) response, err := c.proxy.SendRequest(ctx, msg) // TODO wrap to hide the remote @@ -81,7 +81,7 @@ func (c *Client) GetValue(ctx context.Context, k u.Key) ([]byte, error) { return response.Record.GetValue(), nil } -func (c *Client) Provide(ctx context.Context, k u.Key) error { +func (c *Client) Provide(ctx context.Context, k key.Key) error { defer log.EventBegin(ctx, "provide", &k).Done() msg := pb.NewMessage(pb.Message_ADD_PROVIDER, string(k), 0) // FIXME how is connectedness defined for the local node @@ -113,7 +113,7 @@ func (c *Client) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, error } // creates and signs a record for the given key/value pair -func makeRecord(ps peer.Peerstore, p peer.ID, k u.Key, v []byte) (*pb.Record, error) { +func makeRecord(ps peer.Peerstore, p peer.ID, k key.Key, v []byte) (*pb.Record, error) { blob := bytes.Join([][]byte{[]byte(k), v, []byte(p)}, []byte{}) sig, err := ps.PrivKey(p).Sign(blob) if err != nil { diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 10625f180..b85b053e5 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -6,13 +6,13 @@ import ( ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" host "github.com/ipfs/go-ipfs/p2p/host" inet "github.com/ipfs/go-ipfs/p2p/net" peer "github.com/ipfs/go-ipfs/p2p/peer" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" - util "github.com/ipfs/go-ipfs/util" ) const ProtocolSNR = "/ipfs/supernoderouting" @@ -162,7 +162,7 @@ func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote pe return response, nil } -func sortedByKey(peers []peer.ID, key string) []peer.ID { - target := kbucket.ConvertKey(util.Key(key)) +func sortedByKey(peers []peer.ID, skey string) []peer.ID { + target := kbucket.ConvertKey(key.Key(skey)) return kbucket.SortClosestPeers(peers, target) } diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 95d6a0def..97a5c832d 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,11 +8,11 @@ import ( datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - util "github.com/ipfs/go-ipfs/util" ) // Server handles routing queries using a database backend @@ -53,7 +53,7 @@ func (s *Server) handleMessage( switch req.GetType() { case dhtpb.Message_GET_VALUE: - rawRecord, err := getRoutingRecord(s.routingBackend, util.Key(req.GetKey())) + rawRecord, err := getRoutingRecord(s.routingBackend, key.Key(req.GetKey())) if err != nil { return "", nil } @@ -67,7 +67,7 @@ func (s *Server) handleMessage( // log.Event(ctx, "validationFailed", req, p) // return "", nil // } - putRoutingRecord(s.routingBackend, util.Key(req.GetKey()), req.GetRecord()) + putRoutingRecord(s.routingBackend, key.Key(req.GetKey()), req.GetRecord()) return p, req case dhtpb.Message_FIND_NODE: @@ -87,7 +87,7 @@ func (s *Server) handleMessage( if providerID == p { store := []*dhtpb.Message_Peer{provider} storeProvidersToPeerstore(s.peerstore, p, store) - if err := putRoutingProviders(s.routingBackend, util.Key(req.GetKey()), store); err != nil { + if err := putRoutingProviders(s.routingBackend, key.Key(req.GetKey()), store); err != nil { return "", nil } } else { @@ -97,7 +97,7 @@ func (s *Server) handleMessage( return "", nil case dhtpb.Message_GET_PROVIDERS: - providers, err := getRoutingProviders(s.routingBackend, util.Key(req.GetKey())) + providers, err := getRoutingProviders(s.routingBackend, key.Key(req.GetKey())) if err != nil { return "", nil } @@ -114,7 +114,7 @@ func (s *Server) handleMessage( var _ proxy.RequestHandler = &Server{} var _ proxy.Proxy = &Server{} -func getRoutingRecord(ds datastore.Datastore, k util.Key) (*dhtpb.Record, error) { +func getRoutingRecord(ds datastore.Datastore, k key.Key) (*dhtpb.Record, error) { dskey := k.DsKey() val, err := ds.Get(dskey) if err != nil { @@ -131,7 +131,7 @@ func getRoutingRecord(ds datastore.Datastore, k util.Key) (*dhtpb.Record, error) return &record, nil } -func putRoutingRecord(ds datastore.Datastore, k util.Key, value *dhtpb.Record) error { +func putRoutingRecord(ds datastore.Datastore, k key.Key, value *dhtpb.Record) error { data, err := proto.Marshal(value) if err != nil { return err @@ -144,7 +144,7 @@ func putRoutingRecord(ds datastore.Datastore, k util.Key, value *dhtpb.Record) e return nil } -func putRoutingProviders(ds datastore.Datastore, k util.Key, newRecords []*dhtpb.Message_Peer) error { +func putRoutingProviders(ds datastore.Datastore, k key.Key, newRecords []*dhtpb.Message_Peer) error { log.Event(context.Background(), "putRoutingProviders", &k) oldRecords, err := getRoutingProviders(ds, k) if err != nil { @@ -183,7 +183,7 @@ func storeProvidersToPeerstore(ps peer.Peerstore, p peer.ID, providers []*dhtpb. } } -func getRoutingProviders(ds datastore.Datastore, k util.Key) ([]*dhtpb.Message_Peer, error) { +func getRoutingProviders(ds datastore.Datastore, k key.Key) ([]*dhtpb.Message_Peer, error) { e := log.EventBegin(context.Background(), "getProviders", &k) defer e.Done() var providers []*dhtpb.Message_Peer @@ -199,7 +199,7 @@ func getRoutingProviders(ds datastore.Datastore, k util.Key) ([]*dhtpb.Message_P return providers, nil } -func providerKey(k util.Key) datastore.Key { +func providerKey(k key.Key) datastore.Key { return datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) } diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 2bd0fa15b..d8ea8ea4e 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -4,13 +4,13 @@ import ( "testing" datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - "github.com/ipfs/go-ipfs/util" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { routingBackend := datastore.NewMapDatastore() - k := util.Key("foo") + k := key.Key("foo") put := []*dhtpb.Message_Peer{ convPeer("bob", "127.0.0.1/tcp/4001"), convPeer("alice", "10.0.0.10/tcp/4001"), From a1aa3d06b74d0edf9fe3319f1228fe61170b712a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Jun 2015 16:10:08 -0700 Subject: [PATCH 0881/3817] move util.Key into its own package under blocks This commit was moved from ipfs/go-blockservice@59e07036f96d6dddedfe95f01e53dbd50fe54f63 --- blockservice/blocks_test.go | 7 ++++--- blockservice/blockservice.go | 11 ++++++----- blockservice/worker/worker.go | 7 ++++--- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/blockservice/blocks_test.go b/blockservice/blocks_test.go index b38d52af9..a703cfc72 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/blocks_test.go @@ -11,6 +11,7 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" blocksutil "github.com/ipfs/go-ipfs/blocks/blocksutil" + key "github.com/ipfs/go-ipfs/blocks/key" offline "github.com/ipfs/go-ipfs/exchange/offline" u "github.com/ipfs/go-ipfs/util" ) @@ -30,7 +31,7 @@ func TestBlocks(t *testing.T) { t.Error("Block Multihash and data multihash not equal") } - if b.Key() != u.Key(h) { + if b.Key() != key.Key(h) { t.Error("Block key and data multihash key not equal") } @@ -68,7 +69,7 @@ func TestGetBlocksSequential(t *testing.T) { bg := blocksutil.NewBlockGenerator() blks := bg.Blocks(50) - var keys []u.Key + var keys []key.Key for _, blk := range blks { keys = append(keys, blk.Key()) servs[0].AddBlock(blk) @@ -79,7 +80,7 @@ func TestGetBlocksSequential(t *testing.T) { for i := 1; i < len(servs); i++ { ctx, _ := context.WithTimeout(context.TODO(), time.Second*50) out := servs[i].GetBlocks(ctx, keys) - gotten := make(map[u.Key]*blocks.Block) + gotten := make(map[key.Key]*blocks.Block) for blk := range out { if _, ok := gotten[blk.Key()]; ok { t.Fatal("Got duplicate block!") diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 46612be26..bd7b44789 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -10,6 +10,7 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" + key "github.com/ipfs/go-ipfs/blocks/key" worker "github.com/ipfs/go-ipfs/blockservice/worker" exchange "github.com/ipfs/go-ipfs/exchange" u "github.com/ipfs/go-ipfs/util" @@ -64,7 +65,7 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) (*BlockService, error // AddBlock adds a particular block to the service, Putting it into the datastore. // TODO pass a context into this if the remote.HasBlock is going to remain here. -func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { +func (s *BlockService) AddBlock(b *blocks.Block) (key.Key, error) { k := b.Key() err := s.Blockstore.Put(b) if err != nil { @@ -78,7 +79,7 @@ func (s *BlockService) AddBlock(b *blocks.Block) (u.Key, error) { // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). -func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, error) { +func (s *BlockService) GetBlock(ctx context.Context, k key.Key) (*blocks.Block, error) { log.Debugf("BlockService GetBlock: '%s'", k) block, err := s.Blockstore.Get(k) if err == nil { @@ -101,11 +102,11 @@ func (s *BlockService) GetBlock(ctx context.Context, k u.Key) (*blocks.Block, er // GetBlocks gets a list of blocks asynchronously and returns through // the returned channel. // NB: No guarantees are made about order. -func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks.Block { +func (s *BlockService) GetBlocks(ctx context.Context, ks []key.Key) <-chan *blocks.Block { out := make(chan *blocks.Block, 0) go func() { defer close(out) - var misses []u.Key + var misses []key.Key for _, k := range ks { hit, err := s.Blockstore.Get(k) if err != nil { @@ -138,7 +139,7 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []u.Key) <-chan *blocks } // DeleteBlock deletes a block in the blockservice from the datastore -func (s *BlockService) DeleteBlock(k u.Key) error { +func (s *BlockService) DeleteBlock(k key.Key) error { return s.Blockstore.DeleteBlock(k) } diff --git a/blockservice/worker/worker.go b/blockservice/worker/worker.go index 5e57d8429..88cf4c326 100644 --- a/blockservice/worker/worker.go +++ b/blockservice/worker/worker.go @@ -9,6 +9,7 @@ import ( process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" ratelimit "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit" blocks "github.com/ipfs/go-ipfs/blocks" + key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" waitable "github.com/ipfs/go-ipfs/thirdparty/waitable" util "github.com/ipfs/go-ipfs/util" @@ -141,12 +142,12 @@ func (w *Worker) start(c Config) { type BlockList struct { list list.List - uniques map[util.Key]*list.Element + uniques map[key.Key]*list.Element } func (s *BlockList) PushFront(b *blocks.Block) { if s.uniques == nil { - s.uniques = make(map[util.Key]*list.Element) + s.uniques = make(map[key.Key]*list.Element) } _, ok := s.uniques[b.Key()] if !ok { @@ -157,7 +158,7 @@ func (s *BlockList) PushFront(b *blocks.Block) { func (s *BlockList) Push(b *blocks.Block) { if s.uniques == nil { - s.uniques = make(map[util.Key]*list.Element) + s.uniques = make(map[key.Key]*list.Element) } _, ok := s.uniques[b.Key()] if !ok { From b620c4c0e6b1f9773df1e7f7397e7e22c566f31e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Jun 2015 16:10:08 -0700 Subject: [PATCH 0882/3817] move util.Key into its own package under blocks This commit was moved from ipfs/go-merkledag@20ee0c205d91d3a7601d325691f24df460bc5b72 --- ipld/merkledag/merkledag.go | 25 +++++++++++++------------ ipld/merkledag/merkledag_test.go | 3 ++- ipld/merkledag/node.go | 13 ++++--------- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 2ca5552f1..2ab3df6cf 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -7,6 +7,7 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" + key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" u "github.com/ipfs/go-ipfs/util" ) @@ -16,15 +17,15 @@ var ErrNotFound = fmt.Errorf("merkledag: not found") // DAGService is an IPFS Merkle DAG service. type DAGService interface { - Add(*Node) (u.Key, error) + Add(*Node) (key.Key, error) AddRecursive(*Node) error - Get(context.Context, u.Key) (*Node, error) + Get(context.Context, key.Key) (*Node, error) Remove(*Node) error // GetDAG returns, in order, all the single leve child // nodes of the passed in node. GetDAG(context.Context, *Node) []NodeGetter - GetNodes(context.Context, []u.Key) []NodeGetter + GetNodes(context.Context, []key.Key) []NodeGetter } func NewDAGService(bs *bserv.BlockService) DAGService { @@ -41,7 +42,7 @@ type dagService struct { } // Add adds a node to the dagService, storing the block in the BlockService -func (n *dagService) Add(nd *Node) (u.Key, error) { +func (n *dagService) Add(nd *Node) (key.Key, error) { if n == nil { // FIXME remove this assertion. protect with constructor invariant return "", fmt.Errorf("dagService is nil") } @@ -82,7 +83,7 @@ func (n *dagService) AddRecursive(nd *Node) error { } // Get retrieves a node from the dagService, fetching the block in the BlockService -func (n *dagService) Get(ctx context.Context, k u.Key) (*Node, error) { +func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) { if n == nil { return nil, fmt.Errorf("dagService is nil") } @@ -148,7 +149,7 @@ func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} // FindLinks searches this nodes links for the given key, // returns the indexes of any links pointing to it -func FindLinks(links []u.Key, k u.Key, start int) []int { +func FindLinks(links []key.Key, k key.Key, start int) []int { var out []int for i, lnk_k := range links[start:] { if k == lnk_k { @@ -162,9 +163,9 @@ func FindLinks(links []u.Key, k u.Key, start int) []int { // It returns a channel of nodes, which the caller can receive // all the child nodes of 'root' on, in proper order. func (ds *dagService) GetDAG(ctx context.Context, root *Node) []NodeGetter { - var keys []u.Key + var keys []key.Key for _, lnk := range root.Links { - keys = append(keys, u.Key(lnk.Hash)) + keys = append(keys, key.Key(lnk.Hash)) } return ds.GetNodes(ctx, keys) @@ -172,7 +173,7 @@ func (ds *dagService) GetDAG(ctx context.Context, root *Node) []NodeGetter { // GetNodes returns an array of 'NodeGetter' promises, with each corresponding // to the key with the same index as the passed in keys -func (ds *dagService) GetNodes(ctx context.Context, keys []u.Key) []NodeGetter { +func (ds *dagService) GetNodes(ctx context.Context, keys []key.Key) []NodeGetter { // Early out if no work to do if len(keys) == 0 { @@ -219,9 +220,9 @@ func (ds *dagService) GetNodes(ctx context.Context, keys []u.Key) []NodeGetter { } // Remove duplicates from a list of keys -func dedupeKeys(ks []u.Key) []u.Key { - kmap := make(map[u.Key]struct{}) - var out []u.Key +func dedupeKeys(ks []key.Key) []key.Key { + kmap := make(map[key.Key]struct{}) + var out []key.Key for _, k := range ks { if _, ok := kmap[k]; !ok { kmap[k] = struct{}{} diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 28b58f883..795cce2f6 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -12,6 +12,7 @@ import ( dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" + key "github.com/ipfs/go-ipfs/blocks/key" blockservice "github.com/ipfs/go-ipfs/blockservice" bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" @@ -81,7 +82,7 @@ func TestNode(t *testing.T) { k, err := n.Key() if err != nil { t.Error(err) - } else if k != u.Key(h) { + } else if k != key.Key(h) { t.Error("Key is not equivalent to multihash") } else { fmt.Println("key: ", k) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 5fbffbcf0..e61503ba2 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -6,14 +6,9 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - u "github.com/ipfs/go-ipfs/util" + key "github.com/ipfs/go-ipfs/blocks/key" ) -// NodeMap maps u.Keys to Nodes. -// We cannot use []byte/Multihash for keys :( -// so have to convert Multihash bytes to string (u.Key) -type NodeMap map[u.Key]*Node - // Node represents a node in the IPFS Merkle DAG. // nodes have opaque data and a set of navigable links. type Node struct { @@ -84,7 +79,7 @@ func (l *Link) GetNode(ctx context.Context, serv DAGService) (*Node, error) { return l.Node, nil } - return serv.Get(ctx, u.Key(l.Hash)) + return serv.Get(ctx, key.Key(l.Hash)) } // AddNodeLink adds a link to another node. @@ -227,7 +222,7 @@ func (n *Node) Multihash() (mh.Multihash, error) { } // Key returns the Multihash as a key, for maps. -func (n *Node) Key() (u.Key, error) { +func (n *Node) Key() (key.Key, error) { h, err := n.Multihash() - return u.Key(h), err + return key.Key(h), err } From 97232921f9044c203782ff0b491b72be5afc804a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Jun 2015 16:10:08 -0700 Subject: [PATCH 0883/3817] move util.Key into its own package under blocks This commit was moved from ipfs/go-path@5c2276a9fedaea266d4fd1fb4bb33a0803854e7f --- path/path.go | 6 +++--- path/resolver.go | 9 +++++---- path/resolver_test.go | 3 ++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/path/path.go b/path/path.go index ba75810c8..f98f2cd13 100644 --- a/path/path.go +++ b/path/path.go @@ -5,7 +5,7 @@ import ( "path" "strings" - u "github.com/ipfs/go-ipfs/util" + key "github.com/ipfs/go-ipfs/blocks/key" b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" @@ -24,7 +24,7 @@ func FromString(s string) Path { } // FromKey safely converts a Key type to a Path type -func FromKey(k u.Key) Path { +func FromKey(k key.Key) Path { return Path("/ipfs/" + k.String()) } @@ -86,7 +86,7 @@ func ParseKeyToPath(txt string) (Path, error) { if err != nil { return "", err } - return FromKey(u.Key(chk)), nil + return FromKey(key.Key(chk)), nil } func (p *Path) IsValid() error { diff --git a/path/resolver.go b/path/resolver.go index b4d6239dd..ce1f64a7e 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -2,13 +2,14 @@ package path import ( + "errors" "fmt" "time" - "errors" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" merkledag "github.com/ipfs/go-ipfs/merkledag" u "github.com/ipfs/go-ipfs/util" ) @@ -83,7 +84,7 @@ func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]*me log.Debug("Resolve dag get.") ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() - nd, err := s.DAG.Get(ctx, u.Key(h)) + nd, err := s.DAG.Get(ctx, key.Key(h)) if err != nil { return nil, err } @@ -107,12 +108,12 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd *merkledag.Node, names // for each of the path components for _, name := range names { - var next u.Key + var next key.Key var nlink *merkledag.Link // for each of the links in nd, the current object for _, link := range nd.Links { if link.Name == name { - next = u.Key(link.Hash) + next = key.Key(link.Hash) nlink = link break } diff --git a/path/resolver_test.go b/path/resolver_test.go index 88fcb7433..cb99703a0 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -9,6 +9,7 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" + key "github.com/ipfs/go-ipfs/blocks/key" blockservice "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" merkledag "github.com/ipfs/go-ipfs/merkledag" @@ -16,7 +17,7 @@ import ( util "github.com/ipfs/go-ipfs/util" ) -func randNode() (*merkledag.Node, util.Key) { +func randNode() (*merkledag.Node, key.Key) { node := new(merkledag.Node) node.Data = make([]byte, 32) util.NewTimeSeededRand().Read(node.Data) From 446a74b8935a969ab914de3baed0f7231883348d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Jun 2015 16:10:08 -0700 Subject: [PATCH 0884/3817] move util.Key into its own package under blocks This commit was moved from ipfs/go-ipfs-blockstore@67f1e9320d7b5d5a7cef6efe87bb580885034276 --- blockstore/blockstore.go | 26 +++++++++++++------------- blockstore/blockstore_test.go | 14 +++++++------- blockstore/write_cache.go | 10 +++++----- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 244d4578a..8521fa137 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -11,8 +11,8 @@ import ( mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" + key "github.com/ipfs/go-ipfs/blocks/key" eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" - u "github.com/ipfs/go-ipfs/util" ) var log = eventlog.Logger("blockstore") @@ -26,12 +26,12 @@ var ErrNotFound = errors.New("blockstore: block not found") // Blockstore wraps a ThreadSafeDatastore type Blockstore interface { - DeleteBlock(u.Key) error - Has(u.Key) (bool, error) - Get(u.Key) (*blocks.Block, error) + DeleteBlock(key.Key) error + Has(key.Key) (bool, error) + Get(key.Key) (*blocks.Block, error) Put(*blocks.Block) error - AllKeysChan(ctx context.Context) (<-chan u.Key, error) + AllKeysChan(ctx context.Context) (<-chan key.Key, error) } func NewBlockstore(d ds.ThreadSafeDatastore) Blockstore { @@ -47,7 +47,7 @@ type blockstore struct { // we do check it on `NewBlockstore` though. } -func (bs *blockstore) Get(k u.Key) (*blocks.Block, error) { +func (bs *blockstore) Get(k key.Key) (*blocks.Block, error) { maybeData, err := bs.datastore.Get(k.DsKey()) if err == ds.ErrNotFound { return nil, ErrNotFound @@ -74,11 +74,11 @@ func (bs *blockstore) Put(block *blocks.Block) error { return bs.datastore.Put(k, block.Data) } -func (bs *blockstore) Has(k u.Key) (bool, error) { +func (bs *blockstore) Has(k key.Key) (bool, error) { return bs.datastore.Has(k.DsKey()) } -func (s *blockstore) DeleteBlock(k u.Key) error { +func (s *blockstore) DeleteBlock(k key.Key) error { return s.datastore.Delete(k.DsKey()) } @@ -86,7 +86,7 @@ func (s *blockstore) DeleteBlock(k u.Key) error { // this is very simplistic, in the future, take dsq.Query as a param? // // AllKeysChan respects context -func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan u.Key, error) { +func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { // KeysOnly, because that would be _a lot_ of data. q := dsq.Query{KeysOnly: true} @@ -98,7 +98,7 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan u.Key, error) { } // this function is here to compartmentalize - get := func() (k u.Key, ok bool) { + get := func() (k key.Key, ok bool) { select { case <-ctx.Done(): return k, false @@ -111,8 +111,8 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan u.Key, error) { return k, false } - // need to convert to u.Key using u.KeyFromDsKey. - k = u.KeyFromDsKey(ds.NewKey(e.Key)) + // need to convert to key.Key using key.KeyFromDsKey. + k = key.KeyFromDsKey(ds.NewKey(e.Key)) log.Debug("blockstore: query got key", k) // key must be a multihash. else ignore it. @@ -125,7 +125,7 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan u.Key, error) { } } - output := make(chan u.Key) + output := make(chan key.Key) go func() { defer func() { res.Process().Close() // ensure exit (signals early exit, too) diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 10844354a..ee49b260c 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -11,14 +11,14 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" - u "github.com/ipfs/go-ipfs/util" + key "github.com/ipfs/go-ipfs/blocks/key" ) // TODO(brian): TestGetReturnsNil func TestGetWhenKeyNotPresent(t *testing.T) { bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) - _, err := bs.Get(u.Key("not present")) + _, err := bs.Get(key.Key("not present")) if err != nil { t.Log("As expected, block is not present") @@ -45,13 +45,13 @@ func TestPutThenGetBlock(t *testing.T) { } } -func newBlockStoreWithKeys(t *testing.T, d ds.Datastore, N int) (Blockstore, []u.Key) { +func newBlockStoreWithKeys(t *testing.T, d ds.Datastore, N int) (Blockstore, []key.Key) { if d == nil { d = ds.NewMapDatastore() } bs := NewBlockstore(ds_sync.MutexWrap(d)) - keys := make([]u.Key, N) + keys := make([]key.Key, N) for i := 0; i < N; i++ { block := blocks.NewBlock([]byte(fmt.Sprintf("some data %d", i))) err := bs.Put(block) @@ -63,8 +63,8 @@ func newBlockStoreWithKeys(t *testing.T, d ds.Datastore, N int) (Blockstore, []u return bs, keys } -func collect(ch <-chan u.Key) []u.Key { - var keys []u.Key +func collect(ch <-chan key.Key) []key.Key { + var keys []key.Key for k := range ch { keys = append(keys, k) } @@ -219,7 +219,7 @@ func TestValueTypeMismatch(t *testing.T) { } } -func expectMatches(t *testing.T, expect, actual []u.Key) { +func expectMatches(t *testing.T, expect, actual []key.Key) { if len(expect) != len(actual) { t.Errorf("expect and actual differ: %d != %d", len(expect), len(actual)) diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index b0ea4abc5..fd7fd5d0e 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -4,7 +4,7 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" "github.com/ipfs/go-ipfs/blocks" - u "github.com/ipfs/go-ipfs/util" + key "github.com/ipfs/go-ipfs/blocks/key" ) // WriteCached returns a blockstore that caches up to |size| unique writes (bs.Put). @@ -21,19 +21,19 @@ type writecache struct { blockstore Blockstore } -func (w *writecache) DeleteBlock(k u.Key) error { +func (w *writecache) DeleteBlock(k key.Key) error { w.cache.Remove(k) return w.blockstore.DeleteBlock(k) } -func (w *writecache) Has(k u.Key) (bool, error) { +func (w *writecache) Has(k key.Key) (bool, error) { if _, ok := w.cache.Get(k); ok { return true, nil } return w.blockstore.Has(k) } -func (w *writecache) Get(k u.Key) (*blocks.Block, error) { +func (w *writecache) Get(k key.Key) (*blocks.Block, error) { return w.blockstore.Get(k) } @@ -45,6 +45,6 @@ func (w *writecache) Put(b *blocks.Block) error { return w.blockstore.Put(b) } -func (w *writecache) AllKeysChan(ctx context.Context) (<-chan u.Key, error) { +func (w *writecache) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { return w.blockstore.AllKeysChan(ctx) } From aca0fb51f556f37b52e2f8b37d57db2827d970f3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Jun 2015 16:10:08 -0700 Subject: [PATCH 0885/3817] move util.Key into its own package under blocks This commit was moved from ipfs/go-namesys@a95c01287dc0f31bc94b165501d6822cfb3a03b5 --- namesys/publisher.go | 7 ++++--- namesys/resolve_test.go | 3 ++- namesys/routing.go | 7 ++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index b20a47bea..f20009b7d 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -9,6 +9,7 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" pb "github.com/ipfs/go-ipfs/namesys/internal/pb" ci "github.com/ipfs/go-ipfs/p2p/crypto" @@ -55,7 +56,7 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Pa } nameb := u.Hash(pkbytes) - namekey := u.Key("/pk/" + string(nameb)) + namekey := key.Key("/pk/" + string(nameb)) log.Debugf("Storing pubkey at: %s", namekey) // Store associated public key @@ -65,7 +66,7 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Pa return err } - ipnskey := u.Key("/ipns/" + string(nameb)) + ipnskey := key.Key("/ipns/" + string(nameb)) log.Debugf("Storing ipns entry at: %s", ipnskey) // Store ipns entry at "/ipns/"+b58(h(pubkey)) @@ -110,7 +111,7 @@ var IpnsRecordValidator = &record.ValidChecker{ // ValidateIpnsRecord implements ValidatorFunc and verifies that the // given 'val' is an IpnsEntry and that that entry is valid. -func ValidateIpnsRecord(k u.Key, val []byte) error { +func ValidateIpnsRecord(k key.Key, val []byte) error { entry := new(pb.IpnsEntry) err := proto.Unmarshal(val, entry) if err != nil { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index ce28b1d6b..3dde211ad 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -4,6 +4,7 @@ import ( "testing" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" u "github.com/ipfs/go-ipfs/util" @@ -33,7 +34,7 @@ func TestRoutingResolve(t *testing.T) { } pkhash := u.Hash(pubkb) - res, err := resolver.Resolve(context.Background(), u.Key(pkhash).Pretty()) + res, err := resolver.Resolve(context.Background(), key.Key(pkhash).Pretty()) if err != nil { t.Fatal(err) } diff --git a/namesys/routing.go b/namesys/routing.go index 290c06cb2..40acf38bd 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -7,6 +7,7 @@ import ( mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/namesys/internal/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" @@ -64,7 +65,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa // /ipns/ h := []byte("/ipns/" + string(hash)) - ipnsKey := u.Key(h) + ipnsKey := key.Key(h) val, err := r.routing.GetValue(ctx, ipnsKey) if err != nil { log.Warning("RoutingResolve get failed.") @@ -84,7 +85,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa } hsh, _ := pubkey.Hash() - log.Debugf("pk hash = %s", u.Key(hsh)) + log.Debugf("pk hash = %s", key.Key(hsh)) // check sig with pk if ok, err := pubkey.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok { @@ -101,6 +102,6 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa } else { // Its an old style multihash record log.Warning("Detected old style multihash record") - return path.FromKey(u.Key(valh)), nil + return path.FromKey(key.Key(valh)), nil } } From 2ab9c6d66b0fd9126efd8a3bf0f656f25cbb8fcf Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Jun 2015 16:10:08 -0700 Subject: [PATCH 0886/3817] move util.Key into its own package under blocks This commit was moved from ipfs/go-unixfs@c5c7005633fcfb13d39408ffa35bc851899ced16 --- unixfs/io/dirbuilder.go | 4 ++-- unixfs/mod/dagmodifier.go | 5 +++-- unixfs/mod/dagmodifier_test.go | 7 ++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index ef74f3de0..d1b67c758 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -5,9 +5,9 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" - u "github.com/ipfs/go-ipfs/util" ) type directoryBuilder struct { @@ -29,7 +29,7 @@ func NewDirectory(dserv mdag.DAGService) *directoryBuilder { } // AddChild adds a (name, key)-pair to the root node. -func (d *directoryBuilder) AddChild(name string, k u.Key) error { +func (d *directoryBuilder) AddChild(name string, k key.Key) error { // TODO(cryptix): consolidate context managment ctx, cancel := context.WithTimeout(context.TODO(), time.Minute) defer cancel() diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index bba3139cb..48374c10b 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -11,6 +11,7 @@ import ( mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" imp "github.com/ipfs/go-ipfs/importer" chunk "github.com/ipfs/go-ipfs/importer/chunk" help "github.com/ipfs/go-ipfs/importer/helpers" @@ -226,7 +227,7 @@ func (dm *DagModifier) Sync() error { // modifyDag writes the data in 'data' over the data in 'node' starting at 'offset' // returns the new key of the passed in node and whether or not all the data in the reader // has been consumed. -func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) (u.Key, bool, error) { +func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) (key.Key, bool, error) { f, err := ft.FromBytes(node.Data) if err != nil { return "", false, err @@ -266,7 +267,7 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) // We found the correct child to write into if cur+bs > offset { // Unpin block - ckey := u.Key(node.Links[i].Hash) + ckey := key.Key(node.Links[i].Hash) dm.mp.RemovePinWithMode(ckey, pin.Indirect) child, err := node.Links[i].GetNode(dm.ctx, dm.dagserv) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 3e2bea6cb..e7db0d97d 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -10,6 +10,7 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks/blockstore" + key "github.com/ipfs/go-ipfs/blocks/key" bs "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" imp "github.com/ipfs/go-ipfs/importer" @@ -574,10 +575,10 @@ func TestCorrectPinning(t *testing.T) { } -func enumerateChildren(t *testing.T, nd *mdag.Node, ds mdag.DAGService) []u.Key { - var out []u.Key +func enumerateChildren(t *testing.T, nd *mdag.Node, ds mdag.DAGService) []key.Key { + var out []key.Key for _, lnk := range nd.Links { - out = append(out, u.Key(lnk.Hash)) + out = append(out, key.Key(lnk.Hash)) child, err := lnk.GetNode(context.Background(), ds) if err != nil { t.Fatal(err) From 1354f22aeb7990808353e537a349bff020f9e7d5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Jun 2015 16:10:08 -0700 Subject: [PATCH 0887/3817] move util.Key into its own package under blocks This commit was moved from ipfs/go-ipfs-pinner@cc2a44406a17b18925b1b2dd945bab28403f02d6 --- pinning/pinner/indirect.go | 22 +++++++++++----------- pinning/pinner/pin.go | 33 +++++++++++++++++---------------- pinning/pinner/pin_test.go | 3 ++- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index deed1f5ff..dca99600f 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -2,19 +2,19 @@ package pin import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blocks/set" - "github.com/ipfs/go-ipfs/util" ) type indirectPin struct { blockset set.BlockSet - refCounts map[util.Key]int + refCounts map[key.Key]int } func NewIndirectPin(dstore ds.Datastore) *indirectPin { return &indirectPin{ blockset: set.NewDBWrapperSet(dstore, set.NewSimpleBlockSet()), - refCounts: make(map[util.Key]int), + refCounts: make(map[key.Key]int), } } @@ -25,11 +25,11 @@ func loadIndirPin(d ds.Datastore, k ds.Key) (*indirectPin, error) { return nil, err } - refcnt := make(map[util.Key]int) - var keys []util.Key + refcnt := make(map[key.Key]int) + var keys []key.Key for encK, v := range rcStore { if v > 0 { - k := util.B58KeyDecode(encK) + k := key.B58KeyDecode(encK) keys = append(keys, k) refcnt[k] = v } @@ -43,12 +43,12 @@ func storeIndirPin(d ds.Datastore, k ds.Key, p *indirectPin) error { rcStore := map[string]int{} for k, v := range p.refCounts { - rcStore[util.B58KeyEncode(k)] = v + rcStore[key.B58KeyEncode(k)] = v } return storeSet(d, k, rcStore) } -func (i *indirectPin) Increment(k util.Key) { +func (i *indirectPin) Increment(k key.Key) { c := i.refCounts[k] i.refCounts[k] = c + 1 if c <= 0 { @@ -56,7 +56,7 @@ func (i *indirectPin) Increment(k util.Key) { } } -func (i *indirectPin) Decrement(k util.Key) { +func (i *indirectPin) Decrement(k key.Key) { c := i.refCounts[k] - 1 i.refCounts[k] = c if c <= 0 { @@ -65,7 +65,7 @@ func (i *indirectPin) Decrement(k util.Key) { } } -func (i *indirectPin) HasKey(k util.Key) bool { +func (i *indirectPin) HasKey(k key.Key) bool { return i.blockset.HasKey(k) } @@ -73,6 +73,6 @@ func (i *indirectPin) Set() set.BlockSet { return i.blockset } -func (i *indirectPin) GetRefs() map[util.Key]int { +func (i *indirectPin) GetRefs() map[key.Key]int { return i.refCounts } diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 553593fc8..8f2d4b820 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -11,6 +11,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" nsds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/util" @@ -31,22 +32,22 @@ const ( ) type Pinner interface { - IsPinned(util.Key) bool + IsPinned(key.Key) bool Pin(context.Context, *mdag.Node, bool) error - Unpin(context.Context, util.Key, bool) error + Unpin(context.Context, key.Key, bool) error Flush() error GetManual() ManualPinner - DirectKeys() []util.Key - IndirectKeys() map[util.Key]int - RecursiveKeys() []util.Key + DirectKeys() []key.Key + IndirectKeys() map[key.Key]int + RecursiveKeys() []key.Key } // ManualPinner is for manually editing the pin structure // Use with care! If used improperly, garbage collection // may not be successful type ManualPinner interface { - PinWithMode(util.Key, PinMode) - RemovePinWithMode(util.Key, PinMode) + PinWithMode(key.Key, PinMode) + RemovePinWithMode(key.Key, PinMode) Pinner } @@ -120,7 +121,7 @@ func (p *pinner) Pin(ctx context.Context, node *mdag.Node, recurse bool) error { } // Unpin a given key -func (p *pinner) Unpin(ctx context.Context, k util.Key, recursive bool) error { +func (p *pinner) Unpin(ctx context.Context, k key.Key, recursive bool) error { p.lock.Lock() defer p.lock.Unlock() if p.recursePin.HasKey(k) { @@ -193,7 +194,7 @@ func (p *pinner) pinLinks(ctx context.Context, node *mdag.Node) error { } // IsPinned returns whether or not the given key is pinned -func (p *pinner) IsPinned(key util.Key) bool { +func (p *pinner) IsPinned(key key.Key) bool { p.lock.RLock() defer p.lock.RUnlock() return p.recursePin.HasKey(key) || @@ -201,7 +202,7 @@ func (p *pinner) IsPinned(key util.Key) bool { p.indirPin.HasKey(key) } -func (p *pinner) RemovePinWithMode(key util.Key, mode PinMode) { +func (p *pinner) RemovePinWithMode(key key.Key, mode PinMode) { p.lock.Lock() defer p.lock.Unlock() switch mode { @@ -222,7 +223,7 @@ func LoadPinner(d ds.ThreadSafeDatastore, dserv mdag.DAGService) (Pinner, error) p := new(pinner) { // load recursive set - var recurseKeys []util.Key + var recurseKeys []key.Key if err := loadSet(d, recursePinDatastoreKey, &recurseKeys); err != nil { return nil, err } @@ -230,7 +231,7 @@ func LoadPinner(d ds.ThreadSafeDatastore, dserv mdag.DAGService) (Pinner, error) } { // load direct set - var directKeys []util.Key + var directKeys []key.Key if err := loadSet(d, directPinDatastoreKey, &directKeys); err != nil { return nil, err } @@ -253,17 +254,17 @@ func LoadPinner(d ds.ThreadSafeDatastore, dserv mdag.DAGService) (Pinner, error) } // DirectKeys returns a slice containing the directly pinned keys -func (p *pinner) DirectKeys() []util.Key { +func (p *pinner) DirectKeys() []key.Key { return p.directPin.GetKeys() } // IndirectKeys returns a slice containing the indirectly pinned keys -func (p *pinner) IndirectKeys() map[util.Key]int { +func (p *pinner) IndirectKeys() map[key.Key]int { return p.indirPin.GetRefs() } // RecursiveKeys returns a slice containing the recursively pinned keys -func (p *pinner) RecursiveKeys() []util.Key { +func (p *pinner) RecursiveKeys() []key.Key { return p.recursePin.GetKeys() } @@ -314,7 +315,7 @@ func loadSet(d ds.Datastore, k ds.Key, val interface{}) error { // PinWithMode is a method on ManualPinners, allowing the user to have fine // grained control over pin counts -func (p *pinner) PinWithMode(k util.Key, mode PinMode) { +func (p *pinner) PinWithMode(k key.Key, mode PinMode) { p.lock.Lock() defer p.lock.Unlock() switch mode { diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index b79232570..3f6c67b54 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -9,13 +9,14 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks/blockstore" + key "github.com/ipfs/go-ipfs/blocks/key" bs "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/util" ) -func randNode() (*mdag.Node, util.Key) { +func randNode() (*mdag.Node, key.Key) { nd := new(mdag.Node) nd.Data = make([]byte, 32) util.NewTimeSeededRand().Read(nd.Data) From f1a875a9aeb1bb58bacadcb5597952a84624a0bd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Jun 2015 16:10:08 -0700 Subject: [PATCH 0888/3817] move util.Key into its own package under blocks This commit was moved from ipfs/go-ipfs-exchange-offline@fe186f37a8146c718ba3be9e04a24cb1887f130f --- exchange/offline/offline.go | 8 ++++---- exchange/offline/offline_test.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 6b6ffc838..9cf125ce0 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -6,8 +6,8 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" + key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" - u "github.com/ipfs/go-ipfs/util" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { @@ -23,7 +23,7 @@ type offlineExchange struct { // GetBlock returns nil to signal that a block could not be retrieved for the // given key. // NB: This function may return before the timeout expires. -func (e *offlineExchange) GetBlock(_ context.Context, k u.Key) (*blocks.Block, error) { +func (e *offlineExchange) GetBlock(_ context.Context, k key.Key) (*blocks.Block, error) { return e.bs.Get(k) } @@ -39,11 +39,11 @@ func (_ *offlineExchange) Close() error { return nil } -func (e *offlineExchange) GetBlocks(ctx context.Context, ks []u.Key) (<-chan *blocks.Block, error) { +func (e *offlineExchange) GetBlocks(ctx context.Context, ks []key.Key) (<-chan *blocks.Block, error) { out := make(chan *blocks.Block, 0) go func() { defer close(out) - var misses []u.Key + var misses []key.Key for _, k := range ks { hit, err := e.bs.Get(k) if err != nil { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 1bbbf3f10..41e8bb216 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -9,12 +9,12 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" - u "github.com/ipfs/go-ipfs/util" + key "github.com/ipfs/go-ipfs/blocks/key" ) func TestBlockReturnsErr(t *testing.T) { off := Exchange(bstore()) - _, err := off.GetBlock(context.Background(), u.Key("foo")) + _, err := off.GetBlock(context.Background(), key.Key("foo")) if err != nil { return // as desired } @@ -49,8 +49,8 @@ func TestGetBlocks(t *testing.T) { } } - request := func() []u.Key { - var ks []u.Key + request := func() []key.Key { + var ks []key.Key for _, b := range expected { ks = append(ks, b.Key()) From 9eb4f18bb043deeb6d9b29603b66ccef5004d16d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Jun 2015 16:10:08 -0700 Subject: [PATCH 0889/3817] move util.Key into its own package under blocks This commit was moved from ipfs/go-ipfs-exchange-interface@1729a4c601c00e4fd8ffce7774449bc970152d0f --- exchange/interface.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index 3ccda263c..81ae3483a 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -6,16 +6,16 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" - u "github.com/ipfs/go-ipfs/util" + key "github.com/ipfs/go-ipfs/blocks/key" ) // Any type that implements exchange.Interface may be used as an IPFS block // exchange protocol. type Interface interface { // GetBlock returns the block associated with a given key. - GetBlock(context.Context, u.Key) (*blocks.Block, error) + GetBlock(context.Context, key.Key) (*blocks.Block, error) - GetBlocks(context.Context, []u.Key) (<-chan *blocks.Block, error) + GetBlocks(context.Context, []key.Key) (<-chan *blocks.Block, error) // TODO Should callers be concerned with whether the block was made // available on the network? From 7fd722e165b61fe532a6ed9582d0c1feeae0d5cc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Jun 2015 08:08:51 -0700 Subject: [PATCH 0890/3817] fix rampant memory leak in providers records storage address comments from CR use map and array combo for better perf This commit was moved from ipfs/go-ipfs-routing@bcb5803a90e3fa8bf73a15b3dd7c22d5e1b3acb0 --- routing/dht/providers.go | 70 ++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 2b7fa2cbd..74c79b8e9 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -10,22 +10,25 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) -type providerInfo struct { - Creation time.Time - Value peer.ID -} - type ProviderManager struct { - providers map[key.Key][]*providerInfo + // all non channel fields are meant to be accessed only within + // the run method + providers map[key.Key]*providerSet local map[key.Key]struct{} lpeer peer.ID - getlocal chan chan []key.Key - newprovs chan *addProv - getprovs chan *getProv - period time.Duration + + getlocal chan chan []key.Key + newprovs chan *addProv + getprovs chan *getProv + period time.Duration ctxgroup.ContextGroup } +type providerSet struct { + providers []peer.ID + set map[peer.ID]time.Time +} + type addProv struct { k key.Key val peer.ID @@ -40,7 +43,7 @@ func NewProviderManager(ctx context.Context, local peer.ID) *ProviderManager { pm := new(ProviderManager) pm.getprovs = make(chan *getProv) pm.newprovs = make(chan *addProv) - pm.providers = make(map[key.Key][]*providerInfo) + pm.providers = make(map[key.Key]*providerSet) pm.getlocal = make(chan chan []key.Key) pm.local = make(map[key.Key]struct{}) pm.ContextGroup = ctxgroup.WithContext(ctx) @@ -61,18 +64,20 @@ func (pm *ProviderManager) run() { if np.val == pm.lpeer { pm.local[np.k] = struct{}{} } - pi := new(providerInfo) - pi.Creation = time.Now() - pi.Value = np.val - arr := pm.providers[np.k] - pm.providers[np.k] = append(arr, pi) + provs, ok := pm.providers[np.k] + if !ok { + provs = newProviderSet() + pm.providers[np.k] = provs + } + provs.Add(np.val) case gp := <-pm.getprovs: var parr []peer.ID - provs := pm.providers[gp.k] - for _, p := range provs { - parr = append(parr, p.Value) + provs, ok := pm.providers[gp.k] + if ok { + parr = provs.providers } + gp.resp <- parr case lc := <-pm.getlocal: @@ -83,14 +88,16 @@ func (pm *ProviderManager) run() { lc <- keys case <-tick.C: - for k, provs := range pm.providers { - var filtered []*providerInfo - for _, p := range provs { - if time.Now().Sub(p.Creation) < time.Hour*24 { + for _, provs := range pm.providers { + var filtered []peer.ID + for p, t := range provs.set { + if time.Now().Sub(t) > time.Hour*24 { + delete(provs.set, p) + } else { filtered = append(filtered, p) } } - pm.providers[k] = filtered + provs.providers = filtered } case <-pm.Closing(): @@ -133,3 +140,18 @@ func (pm *ProviderManager) GetLocal() []key.Key { pm.getlocal <- resp return <-resp } + +func newProviderSet() *providerSet { + return &providerSet{ + set: make(map[peer.ID]time.Time), + } +} + +func (ps *providerSet) Add(p peer.ID) { + _, found := ps.set[p] + if !found { + ps.providers = append(ps.providers, p) + } + + ps.set[p] = time.Now() +} From 0d10db32517534746ee7c2f53b3ac6a423f177a6 Mon Sep 17 00:00:00 2001 From: rht Date: Wed, 3 Jun 2015 18:12:34 +0700 Subject: [PATCH 0891/3817] Swap all 'crypto/rand' rng in tests with 'math/rand' This commit was moved from ipfs/go-ipfs-chunker@620aaba51829a4cd8ba89014e18bab8cc6db2689 --- chunker/splitting_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index 1385b9069..232b4fde9 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -2,14 +2,15 @@ package chunk import ( "bytes" - "crypto/rand" "io" "testing" + + u "github.com/ipfs/go-ipfs/util" ) func randBuf(t *testing.T, size int) []byte { buf := make([]byte, size) - if _, err := rand.Read(buf); err != nil { + if _, err := u.NewTimeSeededRand().Read(buf); err != nil { t.Fatal("failed to read enough randomness") } return buf From dc352f76e8f4927ff885cffaa1b979b568fbfbe2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 3 Jun 2015 14:27:31 -0700 Subject: [PATCH 0892/3817] fix up some dependencies to avoid circular record deps This commit was moved from ipfs/go-blockservice@db38dad045710157d5e467dab2e638365e6c213a --- blockservice/{ => test}/blocks_test.go | 3 ++- blockservice/{ => test}/mock.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) rename blockservice/{ => test}/blocks_test.go (97%) rename blockservice/{ => test}/mock.go (92%) diff --git a/blockservice/blocks_test.go b/blockservice/test/blocks_test.go similarity index 97% rename from blockservice/blocks_test.go rename to blockservice/test/blocks_test.go index a703cfc72..cb7f665ac 100644 --- a/blockservice/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -1,4 +1,4 @@ -package blockservice +package bstest import ( "bytes" @@ -12,6 +12,7 @@ import ( blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" blocksutil "github.com/ipfs/go-ipfs/blocks/blocksutil" key "github.com/ipfs/go-ipfs/blocks/key" + . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" u "github.com/ipfs/go-ipfs/util" ) diff --git a/blockservice/mock.go b/blockservice/test/mock.go similarity index 92% rename from blockservice/mock.go rename to blockservice/test/mock.go index 293d11f16..4c4009de2 100644 --- a/blockservice/mock.go +++ b/blockservice/test/mock.go @@ -1,6 +1,7 @@ -package blockservice +package bstest import ( + . "github.com/ipfs/go-ipfs/blockservice" bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" mockrouting "github.com/ipfs/go-ipfs/routing/mock" From 94d5edcddf3dfbe0ffe78435c02657a437bd5e26 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 3 Jun 2015 14:27:31 -0700 Subject: [PATCH 0893/3817] fix up some dependencies to avoid circular record deps This commit was moved from ipfs/go-merkledag@306c9d1ff7388eb8f05a7b7a80d2b1bcb184cdc4 --- ipld/merkledag/merkledag_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 795cce2f6..8ab41ca87 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -13,8 +13,8 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" - blockservice "github.com/ipfs/go-ipfs/blockservice" bserv "github.com/ipfs/go-ipfs/blockservice" + bstest "github.com/ipfs/go-ipfs/blockservice/test" offline "github.com/ipfs/go-ipfs/exchange/offline" imp "github.com/ipfs/go-ipfs/importer" chunk "github.com/ipfs/go-ipfs/importer/chunk" @@ -151,7 +151,7 @@ func TestBatchFetchDupBlock(t *testing.T) { func runBatchFetchTest(t *testing.T, read io.Reader) { var dagservs []DAGService - for _, bsi := range blockservice.Mocks(t, 5) { + for _, bsi := range bstest.Mocks(t, 5) { dagservs = append(dagservs, NewDAGService(bsi)) } From b9e325481085aaa7a38f5fabd9fff9a2de798503 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 May 2015 13:53:11 -0700 Subject: [PATCH 0894/3817] implement an ipfs patch command for modifying merkledag objects WIP: object creator command better docs move patch command into object namespace dont ignore cancel funcs addressing comment from CR add two new subcommands to object patch and clean up main Run func cancel contexts in early returns switch to util.Key This commit was moved from ipfs/go-ipfs-routing@57474f176cfba73a2ab86fa888dfc1271cf5f683 --- routing/record/record.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/routing/record/record.go b/routing/record/record.go index 4e1d54a4d..8db5758e0 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -6,11 +6,13 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" key "github.com/ipfs/go-ipfs/blocks/key" + dag "github.com/ipfs/go-ipfs/merkledag" ci "github.com/ipfs/go-ipfs/p2p/crypto" pb "github.com/ipfs/go-ipfs/routing/dht/pb" eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" ) +var _ = dag.FetchGraph var log = eventlog.Logger("routing/record") // MakePutRecord creates and signs a dht record for the given key/value pair From 32513ce4e2d529f3ffdb96fc931e2e82445e8818 Mon Sep 17 00:00:00 2001 From: rht Date: Fri, 12 Jun 2015 04:36:25 +0700 Subject: [PATCH 0895/3817] Replace Critical{,f} with Error{,f} Except when there is an explicit os.Exit(1) after the Critical line, then replace with Fatal{,f}. golang's log and logrus already call os.Exit(1) by default with Fatal. License: MIT Signed-off-by: rht This commit was moved from ipfs/go-ipfs-routing@356d51c34f464d5bb1f01efc666d29555d4ce77b --- routing/dht/handlers.go | 2 +- routing/supernode/proxy/standard.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index b3db5d87e..36a92a251 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -89,7 +89,7 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Mess for _, pi := range closerinfos { log.Debugf("handleGetValue returning closer peer: '%s'", pi.ID) if len(pi.Addrs) < 1 { - log.Criticalf(`no addresses on peer being sent! + log.Errorf(`no addresses on peer being sent! [local:%s] [sending:%s] [remote:%s]`, dht.self, pi.ID, p) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index b85b053e5..fe723409e 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -50,7 +50,7 @@ func (px *standard) Bootstrap(ctx context.Context) error { cxns = append(cxns, info) } if len(cxns) == 0 { - log.Critical("unable to bootstrap to any supernode routers") + log.Error("unable to bootstrap to any supernode routers") } else { log.Infof("bootstrapped to %d supernode routers: %s", len(cxns), cxns) } From 8794096716adb98c8c0f585c5a5f10c7c3780674 Mon Sep 17 00:00:00 2001 From: rht Date: Fri, 12 Jun 2015 04:48:27 +0700 Subject: [PATCH 0896/3817] Remove Notice{,f} logging interface And substitute the lines using Notice{,f} with Info{,f} License: MIT Signed-off-by: rht This commit was moved from ipfs/go-ipfs-routing@3305304c97624a7f0ac55f8065a48f6ae87cc3e3 --- routing/dht/dht_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index a6eb41a77..83c3b2b20 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -773,7 +773,7 @@ func TestConnectCollision(t *testing.T) { runTimes := 10 for rtime := 0; rtime < runTimes; rtime++ { - log.Notice("Running Time: ", rtime) + log.Info("Running Time: ", rtime) ctx := context.Background() From 3d183af45e3de3cd78517030d8987c51574dcf95 Mon Sep 17 00:00:00 2001 From: rht Date: Fri, 3 Jul 2015 17:31:46 +0700 Subject: [PATCH 0897/3817] Add path validation in Resolver.ResolvePath Add ErrNoComponents in ParsePath validation & remove redundant path validation. Any lines using core.Resolve & Resolver.ResolvePath will have their path validated. License: MIT Signed-off-by: rht This commit was moved from ipfs/go-path@2f07b0e127840688f68d93c7cd73582238d4f43e --- path/path.go | 16 +++++++++++----- path/resolver.go | 5 +++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/path/path.go b/path/path.go index f98f2cd13..6fe75adbe 100644 --- a/path/path.go +++ b/path/path.go @@ -61,12 +61,15 @@ func ParsePath(txt string) (Path, error) { } if parts[0] != "" { - return "", ErrBadPath + if _, err := ParseKeyToPath(parts[0]); err != nil { + return "", ErrBadPath + } + // The case when the path starts with hash without a protocol prefix + return Path("/ipfs/" + txt), nil } if parts[1] == "ipfs" { - _, err := ParseKeyToPath(parts[2]) - if err != nil { + if _, err := ParseKeyToPath(parts[2]); err != nil { return "", err } } else if parts[1] != "ipns" { @@ -77,13 +80,16 @@ func ParsePath(txt string) (Path, error) { } func ParseKeyToPath(txt string) (Path, error) { + if txt == "" { + return "", ErrNoComponents + } + chk := b58.Decode(txt) if len(chk) == 0 { return "", errors.New("not a key") } - _, err := mh.Cast(chk) - if err != nil { + if _, err := mh.Cast(chk); err != nil { return "", err } return FromKey(key.Key(chk)), nil diff --git a/path/resolver.go b/path/resolver.go index ce1f64a7e..288075000 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -65,6 +65,11 @@ func SplitAbsPath(fpath Path) (mh.Multihash, []string, error) { // ResolvePath fetches the node for given path. It returns the last item // returned by ResolvePathComponents. func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (*merkledag.Node, error) { + // validate path + if err := fpath.IsValid(); err != nil { + return nil, err + } + nodes, err := s.ResolvePathComponents(ctx, fpath) if err != nil || nodes == nil { return nil, err From 2fbecb774932a42d82fcf68658a05c96fa382b8b Mon Sep 17 00:00:00 2001 From: rht Date: Wed, 17 Jun 2015 19:18:52 +0700 Subject: [PATCH 0898/3817] Replace ctxgroup.ContextGroup -> goprocess.Process License: MIT Signed-off-by: rht This commit was moved from ipfs/go-ipfs-routing@fb7fd0365c77af9877a04c65708726fc2e85ac6f --- routing/dht/dht.go | 20 +++++++++++--------- routing/dht/providers.go | 13 +++++-------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index b1b13985b..fcc14273f 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -23,8 +23,9 @@ import ( u "github.com/ipfs/go-ipfs/util" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" - ctxgroup "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" + goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) @@ -57,7 +58,8 @@ type IpfsDHT struct { Validator record.Validator // record validator funcs - ctxgroup.ContextGroup + Context context.Context + goprocess.Process } // NewDHT creates a new DHT object with the given peer as the 'local' host @@ -71,14 +73,17 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip // register for network notifs. dht.host.Network().Notify((*netNotifiee)(dht)) - dht.ContextGroup = ctxgroup.WithContextAndTeardown(ctx, func() error { + procctx = goprocessctx.WithContext(ctx) + procctx.SetTeardown(func() error { // remove ourselves from network notifs. dht.host.Network().StopNotify((*netNotifiee)(dht)) return nil }) + dht.Process = procctx + dht.Context = ctx h.SetStreamHandler(ProtocolDHT, dht.handleNewStream) - dht.providers = NewProviderManager(dht.Context(), dht.self) + dht.providers = NewProviderManager(dht.Context, dht.self) dht.AddChild(dht.providers) dht.routingTable = kb.NewRoutingTable(20, kb.ConvertPeerID(dht.self), time.Minute, dht.peerstore) @@ -88,8 +93,7 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip dht.Validator["pk"] = record.PublicKeyValidator if doPinging { - dht.Children().Add(1) - go dht.PingRoutine(time.Second * 10) + dht.Go(func() { dht.PingRoutine(time.Second * 10) }) } return dht } @@ -348,8 +352,6 @@ func (dht *IpfsDHT) ensureConnectedToPeer(ctx context.Context, p peer.ID) error // PingRoutine periodically pings nearest neighbors. func (dht *IpfsDHT) PingRoutine(t time.Duration) { - defer dht.Children().Done() - tick := time.Tick(t) for { select { @@ -358,7 +360,7 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { rand.Read(id) peers := dht.routingTable.NearestPeers(kb.ConvertKey(key.Key(id)), 5) for _, p := range peers { - ctx, cancel := context.WithTimeout(dht.Context(), time.Second*5) + ctx, cancel := context.WithTimeout(dht.Context, time.Second*5) _, err := dht.Ping(ctx, p) if err != nil { log.Debugf("Ping error: %s", err) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 74c79b8e9..46675604a 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -3,7 +3,8 @@ package dht import ( "time" - ctxgroup "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" + goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" @@ -21,7 +22,7 @@ type ProviderManager struct { newprovs chan *addProv getprovs chan *getProv period time.Duration - ctxgroup.ContextGroup + goprocess.Process } type providerSet struct { @@ -46,17 +47,13 @@ func NewProviderManager(ctx context.Context, local peer.ID) *ProviderManager { pm.providers = make(map[key.Key]*providerSet) pm.getlocal = make(chan chan []key.Key) pm.local = make(map[key.Key]struct{}) - pm.ContextGroup = ctxgroup.WithContext(ctx) - - pm.Children().Add(1) - go pm.run() + pm.Process = goprocessctx.WithContext(ctx) + pm.Go(pm.run) return pm } func (pm *ProviderManager) run() { - defer pm.Children().Done() - tick := time.NewTicker(time.Hour) for { select { From bf647de8e206f8f46706fe8ed31163eb0e7cba3a Mon Sep 17 00:00:00 2001 From: rht Date: Thu, 18 Jun 2015 17:17:38 +0700 Subject: [PATCH 0899/3817] Change Process interface into object variable License: MIT Signed-off-by: rht This commit was moved from ipfs/go-ipfs-routing@50d59a421aa2ddd8e7259dd9178febfc6457dd9c --- routing/dht/dht.go | 39 +++++++++++++++++++++++++---------- routing/dht/notif.go | 4 ++-- routing/dht/providers.go | 8 +++---- routing/dht/providers_test.go | 2 +- 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index fcc14273f..d6a07073e 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -58,8 +58,8 @@ type IpfsDHT struct { Validator record.Validator // record validator funcs - Context context.Context - goprocess.Process + ctx context.Context + proc goprocess.Process } // NewDHT creates a new DHT object with the given peer as the 'local' host @@ -73,18 +73,18 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip // register for network notifs. dht.host.Network().Notify((*netNotifiee)(dht)) - procctx = goprocessctx.WithContext(ctx) - procctx.SetTeardown(func() error { + proc := goprocessctx.WithContext(ctx) + proc.SetTeardown(func() error { // remove ourselves from network notifs. dht.host.Network().StopNotify((*netNotifiee)(dht)) return nil }) - dht.Process = procctx - dht.Context = ctx + dht.proc = proc + dht.ctx = ctx h.SetStreamHandler(ProtocolDHT, dht.handleNewStream) - dht.providers = NewProviderManager(dht.Context, dht.self) - dht.AddChild(dht.providers) + dht.providers = NewProviderManager(dht.ctx, dht.self) + dht.proc.AddChild(dht.providers.proc) dht.routingTable = kb.NewRoutingTable(20, kb.ConvertPeerID(dht.self), time.Minute, dht.peerstore) dht.birth = time.Now() @@ -93,7 +93,9 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip dht.Validator["pk"] = record.PublicKeyValidator if doPinging { - dht.Go(func() { dht.PingRoutine(time.Second * 10) }) + dht.proc.Go(func(p goprocess.Process) { + dht.PingRoutine(time.Second * 10) + }) } return dht } @@ -360,15 +362,30 @@ func (dht *IpfsDHT) PingRoutine(t time.Duration) { rand.Read(id) peers := dht.routingTable.NearestPeers(kb.ConvertKey(key.Key(id)), 5) for _, p := range peers { - ctx, cancel := context.WithTimeout(dht.Context, time.Second*5) + ctx, cancel := context.WithTimeout(dht.Context(), time.Second*5) _, err := dht.Ping(ctx, p) if err != nil { log.Debugf("Ping error: %s", err) } cancel() } - case <-dht.Closing(): + case <-dht.proc.Closing(): return } } } + +// Context return dht's context +func (dht *IpfsDHT) Context() context.Context { + return dht.ctx +} + +// Process return dht's process +func (dht *IpfsDHT) Process() goprocess.Process { + return dht.proc +} + +// Close calls Process Close +func (dht *IpfsDHT) Close() error { + return dht.proc.Close() +} diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 70144481a..cfe411c38 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -16,7 +16,7 @@ func (nn *netNotifiee) DHT() *IpfsDHT { func (nn *netNotifiee) Connected(n inet.Network, v inet.Conn) { dht := nn.DHT() select { - case <-dht.Closing(): + case <-dht.Process().Closing(): return default: } @@ -26,7 +26,7 @@ func (nn *netNotifiee) Connected(n inet.Network, v inet.Conn) { func (nn *netNotifiee) Disconnected(n inet.Network, v inet.Conn) { dht := nn.DHT() select { - case <-dht.Closing(): + case <-dht.Process().Closing(): return default: } diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 46675604a..17455b336 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -22,7 +22,7 @@ type ProviderManager struct { newprovs chan *addProv getprovs chan *getProv period time.Duration - goprocess.Process + proc goprocess.Process } type providerSet struct { @@ -47,8 +47,8 @@ func NewProviderManager(ctx context.Context, local peer.ID) *ProviderManager { pm.providers = make(map[key.Key]*providerSet) pm.getlocal = make(chan chan []key.Key) pm.local = make(map[key.Key]struct{}) - pm.Process = goprocessctx.WithContext(ctx) - pm.Go(pm.run) + pm.proc = goprocessctx.WithContext(ctx) + pm.proc.Go(func(p goprocess.Process) { pm.run() }) return pm } @@ -97,7 +97,7 @@ func (pm *ProviderManager) run() { provs.providers = filtered } - case <-pm.Closing(): + case <-pm.proc.Closing(): return } } diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index ecf937962..7e2e47d93 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -19,5 +19,5 @@ func TestProviderManager(t *testing.T) { if len(resp) != 1 { t.Fatal("Could not retrieve provider.") } - p.Close() + p.proc.Close() } From d641888bc159575f9f964064c321357b39befe02 Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 22 Jun 2015 21:57:14 +0700 Subject: [PATCH 0900/3817] Use WithContextAndTeardown whenever possible License: MIT Signed-off-by: rht This commit was moved from ipfs/go-ipfs-routing@d3b46b8db72775d1346cc1d5628a7aed68528c9e --- routing/dht/dht.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index d6a07073e..205e6d980 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -73,13 +73,12 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip // register for network notifs. dht.host.Network().Notify((*netNotifiee)(dht)) - proc := goprocessctx.WithContext(ctx) - proc.SetTeardown(func() error { + dht.proc = goprocessctx.WithContextAndTeardown(ctx, func() error { // remove ourselves from network notifs. dht.host.Network().StopNotify((*netNotifiee)(dht)) return nil }) - dht.proc = proc + dht.ctx = ctx h.SetStreamHandler(ProtocolDHT, dht.handleNewStream) From 8e1a03e112b3de36047b6900e1ec16d07babfea2 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 5 Jul 2015 09:35:06 +0700 Subject: [PATCH 0901/3817] Make sure process context is set last License: MIT Signed-off-by: rht This commit was moved from ipfs/go-ipfs-routing@b3235e16796636365e6995f647c34251d04e4bd9 --- routing/dht/dht.go | 3 ++- routing/dht/query.go | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 205e6d980..9dc74daeb 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -73,7 +73,7 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip // register for network notifs. dht.host.Network().Notify((*netNotifiee)(dht)) - dht.proc = goprocessctx.WithContextAndTeardown(ctx, func() error { + dht.proc = goprocess.WithTeardown(func() error { // remove ourselves from network notifs. dht.host.Network().StopNotify((*netNotifiee)(dht)) return nil @@ -84,6 +84,7 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip h.SetStreamHandler(ProtocolDHT, dht.handleNewStream) dht.providers = NewProviderManager(dht.ctx, dht.self) dht.proc.AddChild(dht.providers.proc) + goprocessctx.CloseAfterContext(dht.proc, ctx) dht.routingTable = kb.NewRoutingTable(20, kb.ConvertPeerID(dht.self), time.Minute, dht.peerstore) dht.birth = time.Now() diff --git a/routing/dht/query.go b/routing/dht/query.go index c69437f49..a6c8a14b3 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -127,10 +127,7 @@ func (r *dhtQueryRunner) Run(ctx context.Context, peers []peer.ID) (*dhtQueryRes // now, if the context finishes, close the proc. // we have to do it here because the logic before is setup, which // should run without closing the proc. - go func() { - <-ctx.Done() - r.proc.Close() - }() + ctxproc.CloseAfterContext(r.proc, ctx) select { case <-r.peersRemaining.Done(): From ef498d24487773f83f93e963fc2ec54299e6a1b8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 26 Jun 2015 09:44:00 -0700 Subject: [PATCH 0902/3817] use batching transaction interface from datastore License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@767a36340c8a138014864863af1b572c61d03e05 --- ipld/merkledag/merkledag.go | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 2ab3df6cf..bf8dc1310 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -26,6 +26,8 @@ type DAGService interface { // nodes of the passed in node. GetDAG(context.Context, *Node) []NodeGetter GetNodes(context.Context, []key.Key) []NodeGetter + + Batch() *Batch } func NewDAGService(bs *bserv.BlockService) DAGService { @@ -62,6 +64,10 @@ func (n *dagService) Add(nd *Node) (key.Key, error) { return n.Blocks.AddBlock(b) } +func (n *dagService) Batch() *Batch { + return &Batch{ds: n, MaxSize: 8 * 1024 * 1024} +} + // AddRecursive adds the given node and all child nodes to the BlockService func (n *dagService) AddRecursive(nd *Node) error { _, err := n.Add(nd) @@ -269,3 +275,41 @@ func (np *nodePromise) Get(ctx context.Context) (*Node, error) { } return np.cache, nil } + +type Batch struct { + ds *dagService + + blocks []*blocks.Block + size int + MaxSize int +} + +func (t *Batch) Add(nd *Node) (key.Key, error) { + d, err := nd.Encoded(false) + if err != nil { + return "", err + } + + b := new(blocks.Block) + b.Data = d + b.Multihash, err = nd.Multihash() + if err != nil { + return "", err + } + + k := key.Key(b.Multihash) + + t.blocks = append(t.blocks, b) + t.size += len(b.Data) + if t.size > t.MaxSize { + return k, t.Commit() + } + return k, nil +} + +func (t *Batch) Commit() error { + _, err := t.ds.Blocks.AddBlocks(t.blocks) + t.blocks = nil + t.size = 0 + return err +} From 5ed8375b160883c46976f773c67d7afe770f9599 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 26 Jun 2015 09:44:00 -0700 Subject: [PATCH 0903/3817] use batching transaction interface from datastore License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@034441cfa5c3179ef8f52963810166060886dfb5 --- blockservice/blockservice.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index bd7b44789..eec292b21 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -77,6 +77,22 @@ func (s *BlockService) AddBlock(b *blocks.Block) (key.Key, error) { return k, nil } +func (s *BlockService) AddBlocks(bs []*blocks.Block) ([]key.Key, error) { + err := s.Blockstore.PutMany(bs) + if err != nil { + return nil, err + } + + var ks []key.Key + for _, b := range bs { + if err := s.worker.HasBlock(b); err != nil { + return nil, errors.New("blockservice is closed") + } + ks = append(ks, b.Key()) + } + return ks, nil +} + // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). func (s *BlockService) GetBlock(ctx context.Context, k key.Key) (*blocks.Block, error) { From 67314abf5f541d2ad6c3b65232b6ddce23ed0393 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 26 Jun 2015 09:44:00 -0700 Subject: [PATCH 0904/3817] use batching transaction interface from datastore License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@7e356be6b49c16778545013f80d97e18cfe7fd64 --- blockstore/blockstore.go | 23 ++++++++++++++++++++++- blockstore/blockstore_test.go | 4 ++++ blockstore/write_cache.go | 10 ++++++++++ blockstore/write_cache_test.go | 4 ++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 8521fa137..63fa7f9eb 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -30,6 +30,7 @@ type Blockstore interface { Has(key.Key) (bool, error) Get(key.Key) (*blocks.Block, error) Put(*blocks.Block) error + PutMany([]*blocks.Block) error AllKeysChan(ctx context.Context) (<-chan key.Key, error) } @@ -42,7 +43,7 @@ func NewBlockstore(d ds.ThreadSafeDatastore) Blockstore { } type blockstore struct { - datastore ds.Datastore + datastore ds.BatchingDatastore // cant be ThreadSafeDatastore cause namespace.Datastore doesnt support it. // we do check it on `NewBlockstore` though. } @@ -74,6 +75,26 @@ func (bs *blockstore) Put(block *blocks.Block) error { return bs.datastore.Put(k, block.Data) } +func (bs *blockstore) PutMany(blocks []*blocks.Block) error { + t, err := bs.datastore.Batch() + if err != nil { + return err + } + for _, b := range blocks { + k := b.Key().DsKey() + exists, err := bs.datastore.Has(k) + if err == nil && exists { + continue + } + + err = t.Put(k, b.Data) + if err != nil { + return err + } + } + return t.Commit() +} + func (bs *blockstore) Has(k key.Key) (bool, error) { return bs.datastore.Has(k.DsKey()) } diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index ee49b260c..934c7933e 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -266,3 +266,7 @@ func (c *queryTestDS) Query(q dsq.Query) (dsq.Results, error) { } return c.ds.Query(q) } + +func (c *queryTestDS) Batch() (ds.Batch, error) { + return ds.NewBasicBatch(c), nil +} diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index fd7fd5d0e..5b2f55a2a 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -45,6 +45,16 @@ func (w *writecache) Put(b *blocks.Block) error { return w.blockstore.Put(b) } +func (w *writecache) PutMany(bs []*blocks.Block) error { + var good []*blocks.Block + for _, b := range bs { + if _, ok := w.cache.Get(b.Key()); !ok { + good = append(good, b) + } + } + return w.blockstore.PutMany(good) +} + func (w *writecache) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { return w.blockstore.AllKeysChan(ctx) } diff --git a/blockstore/write_cache_test.go b/blockstore/write_cache_test.go index cf8150ba6..a51d2f7c6 100644 --- a/blockstore/write_cache_test.go +++ b/blockstore/write_cache_test.go @@ -88,3 +88,7 @@ func (c *callbackDatastore) Query(q dsq.Query) (dsq.Results, error) { c.f() return c.ds.Query(q) } + +func (c *callbackDatastore) Batch() (ds.Batch, error) { + return ds.NewBasicBatch(c), nil +} From 2852fb03018cba48983832f316f17bd54144d8ac Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 9 Jul 2015 16:34:16 -0700 Subject: [PATCH 0905/3817] expose internal/pb packages. we shouldn't use internal packages. License: MIT Signed-off-by: Juan Batiz-Benet This commit was moved from ipfs/go-merkledag@26c3616adddc90656cf7640ac163768226479bd8 --- ipld/merkledag/coding.go | 2 +- ipld/merkledag/{internal => }/pb/Makefile | 0 ipld/merkledag/{internal => }/pb/merkledag.pb.go | 0 ipld/merkledag/{internal => }/pb/merkledag.proto | 0 ipld/merkledag/{internal => }/pb/merkledagpb_test.go | 0 5 files changed, 1 insertion(+), 1 deletion(-) rename ipld/merkledag/{internal => }/pb/Makefile (100%) rename ipld/merkledag/{internal => }/pb/merkledag.pb.go (100%) rename ipld/merkledag/{internal => }/pb/merkledag.proto (100%) rename ipld/merkledag/{internal => }/pb/merkledagpb_test.go (100%) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 6e108c2cf..f8cc326a4 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -6,7 +6,7 @@ import ( mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - pb "github.com/ipfs/go-ipfs/merkledag/internal/pb" + pb "github.com/ipfs/go-ipfs/merkledag/pb" u "github.com/ipfs/go-ipfs/util" ) diff --git a/ipld/merkledag/internal/pb/Makefile b/ipld/merkledag/pb/Makefile similarity index 100% rename from ipld/merkledag/internal/pb/Makefile rename to ipld/merkledag/pb/Makefile diff --git a/ipld/merkledag/internal/pb/merkledag.pb.go b/ipld/merkledag/pb/merkledag.pb.go similarity index 100% rename from ipld/merkledag/internal/pb/merkledag.pb.go rename to ipld/merkledag/pb/merkledag.pb.go diff --git a/ipld/merkledag/internal/pb/merkledag.proto b/ipld/merkledag/pb/merkledag.proto similarity index 100% rename from ipld/merkledag/internal/pb/merkledag.proto rename to ipld/merkledag/pb/merkledag.proto diff --git a/ipld/merkledag/internal/pb/merkledagpb_test.go b/ipld/merkledag/pb/merkledagpb_test.go similarity index 100% rename from ipld/merkledag/internal/pb/merkledagpb_test.go rename to ipld/merkledag/pb/merkledagpb_test.go From e10316d09192d7b336024eb0e08c4337885ebda5 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 9 Jul 2015 16:34:16 -0700 Subject: [PATCH 0906/3817] expose internal/pb packages. we shouldn't use internal packages. License: MIT Signed-off-by: Juan Batiz-Benet This commit was moved from ipfs/go-namesys@0321fe902530db3ee024b3b56c7ef927dc941ef9 --- namesys/{internal => }/pb/Makefile | 0 namesys/{internal => }/pb/namesys.pb.go | 0 namesys/{internal => }/pb/namesys.proto | 0 namesys/publisher.go | 2 +- namesys/routing.go | 2 +- 5 files changed, 2 insertions(+), 2 deletions(-) rename namesys/{internal => }/pb/Makefile (100%) rename namesys/{internal => }/pb/namesys.pb.go (100%) rename namesys/{internal => }/pb/namesys.proto (100%) diff --git a/namesys/internal/pb/Makefile b/namesys/pb/Makefile similarity index 100% rename from namesys/internal/pb/Makefile rename to namesys/pb/Makefile diff --git a/namesys/internal/pb/namesys.pb.go b/namesys/pb/namesys.pb.go similarity index 100% rename from namesys/internal/pb/namesys.pb.go rename to namesys/pb/namesys.pb.go diff --git a/namesys/internal/pb/namesys.proto b/namesys/pb/namesys.proto similarity index 100% rename from namesys/internal/pb/namesys.proto rename to namesys/pb/namesys.proto diff --git a/namesys/publisher.go b/namesys/publisher.go index f20009b7d..3f5e15ae5 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -11,7 +11,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" - pb "github.com/ipfs/go-ipfs/namesys/internal/pb" + pb "github.com/ipfs/go-ipfs/namesys/pb" ci "github.com/ipfs/go-ipfs/p2p/crypto" path "github.com/ipfs/go-ipfs/path" pin "github.com/ipfs/go-ipfs/pin" diff --git a/namesys/routing.go b/namesys/routing.go index 40acf38bd..9ff2a62f6 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -8,7 +8,7 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" - pb "github.com/ipfs/go-ipfs/namesys/internal/pb" + pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" From dc09b2f8eb0c40a70500376b15fd3d007b66645e Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 10 Jul 2015 17:48:54 -0700 Subject: [PATCH 0907/3817] moved util/ctx to github.com/jbenet/go-context License: MIT Signed-off-by: Juan Batiz-Benet This commit was moved from ipfs/go-ipfs-routing@d25524a4f36fab82a2870afc69e99e9e3f36d728 --- routing/dht/dht_net.go | 17 ++++++++--------- routing/dht/records.go | 4 ++-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 44767fbe4..722ece7ea 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -4,13 +4,12 @@ import ( "errors" "time" + ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" + ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" inet "github.com/ipfs/go-ipfs/p2p/net" peer "github.com/ipfs/go-ipfs/p2p/peer" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ctxutil "github.com/ipfs/go-ipfs/util/ctx" - - ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) // handleNewStream implements the inet.StreamHandler @@ -22,8 +21,8 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { defer s.Close() ctx := dht.Context() - cr := ctxutil.NewReader(ctx, s) // ok to use. we defer close stream in this func - cw := ctxutil.NewWriter(ctx, s) // ok to use. we defer close stream in this func + cr := ctxio.NewReader(ctx, s) // ok to use. we defer close stream in this func + cw := ctxio.NewWriter(ctx, s) // ok to use. we defer close stream in this func r := ggio.NewDelimitedReader(cr, inet.MessageSizeMax) w := ggio.NewDelimitedWriter(cw) mPeer := s.Conn().RemotePeer() @@ -78,8 +77,8 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message } defer s.Close() - cr := ctxutil.NewReader(ctx, s) // ok to use. we defer close stream in this func - cw := ctxutil.NewWriter(ctx, s) // ok to use. we defer close stream in this func + cr := ctxio.NewReader(ctx, s) // ok to use. we defer close stream in this func + cw := ctxio.NewWriter(ctx, s) // ok to use. we defer close stream in this func r := ggio.NewDelimitedReader(cr, inet.MessageSizeMax) w := ggio.NewDelimitedWriter(cw) @@ -116,7 +115,7 @@ func (dht *IpfsDHT) sendMessage(ctx context.Context, p peer.ID, pmes *pb.Message } defer s.Close() - cw := ctxutil.NewWriter(ctx, s) // ok to use. we defer close stream in this func + cw := ctxio.NewWriter(ctx, s) // ok to use. we defer close stream in this func w := ggio.NewDelimitedWriter(cw) if err := w.WriteMsg(pmes); err != nil { diff --git a/routing/dht/records.go b/routing/dht/records.go index 973ceca96..3c7d1d176 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -3,13 +3,13 @@ package dht import ( "fmt" + ctxfrac "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/frac" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ci "github.com/ipfs/go-ipfs/p2p/crypto" peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ctxutil "github.com/ipfs/go-ipfs/util/ctx" ) func (dht *IpfsDHT) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey, error) { @@ -22,7 +22,7 @@ func (dht *IpfsDHT) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey, err } // ok, try the node itself. if they're overwhelmed or slow we can move on. - ctxT, cancelFunc := ctxutil.WithDeadlineFraction(ctx, 0.3) + ctxT, cancelFunc := ctxfrac.WithDeadlineFraction(ctx, 0.3) defer cancelFunc() if pk, err := dht.getPublicKeyFromNode(ctx, p); err == nil { err := dht.peerstore.AddPubKey(p, pk) From 8e301c93637cf3b2ae085b1279327a57fcbf7691 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 13 Jul 2015 15:53:58 -0700 Subject: [PATCH 0908/3817] clean up unused dht methods License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@d1c04c9be239c8f549900c6b135f9d989db8975e --- routing/dht/dht.go | 57 ----------------------------------------- routing/dht/dht_test.go | 9 ++++--- 2 files changed, 6 insertions(+), 60 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 9dc74daeb..50c4df14c 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -4,7 +4,6 @@ package dht import ( "bytes" - "crypto/rand" "errors" "fmt" "sync" @@ -33,8 +32,6 @@ var log = eventlog.Logger("dht") var ProtocolDHT protocol.ID = "/ipfs/dht" -const doPinging = false - // NumBootstrapQueries defines the number of random dht queries to do to // collect members of the routing table. const NumBootstrapQueries = 5 @@ -92,11 +89,6 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip dht.Validator = make(record.Validator) dht.Validator["pk"] = record.PublicKeyValidator - if doPinging { - dht.proc.Go(func(p goprocess.Process) { - dht.PingRoutine(time.Second * 10) - }) - } return dht } @@ -110,23 +102,6 @@ func (dht *IpfsDHT) log() eventlog.EventLogger { return log // TODO rm } -// Connect to a new peer at the given address, ping and add to the routing table -func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.ID) error { - // TODO: change interface to accept a PeerInfo as well. - if err := dht.host.Connect(ctx, peer.PeerInfo{ID: npeer}); err != nil { - return err - } - - // Ping new peer to register in their routing table - // NOTE: this should be done better... - if _, err := dht.Ping(ctx, npeer); err != nil { - return fmt.Errorf("failed to ping newly connected peer: %s", err) - } - log.Event(ctx, "connect", dht.self, npeer) - dht.Update(ctx, npeer) - return nil -} - // putValueToPeer stores the given key/value pair at the peer 'p' func (dht *IpfsDHT) putValueToPeer(ctx context.Context, p peer.ID, key key.Key, rec *pb.Record) error { @@ -343,38 +318,6 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, p peer.ID, count int) [ return filtered } -func (dht *IpfsDHT) ensureConnectedToPeer(ctx context.Context, p peer.ID) error { - if p == dht.self { - return errors.New("attempting to ensure connection to self") - } - - // dial connection - return dht.host.Connect(ctx, peer.PeerInfo{ID: p}) -} - -// PingRoutine periodically pings nearest neighbors. -func (dht *IpfsDHT) PingRoutine(t time.Duration) { - tick := time.Tick(t) - for { - select { - case <-tick: - id := make([]byte, 16) - rand.Read(id) - peers := dht.routingTable.NearestPeers(kb.ConvertKey(key.Key(id)), 5) - for _, p := range peers { - ctx, cancel := context.WithTimeout(dht.Context(), time.Second*5) - _, err := dht.Ping(ctx, p) - if err != nil { - log.Debugf("Ping error: %s", err) - } - cancel() - } - case <-dht.proc.Closing(): - return - } - } -} - // Context return dht's context func (dht *IpfsDHT) Context() context.Context { return dht.ctx diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 83c3b2b20..edfffcebf 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -74,7 +74,8 @@ func connect(t *testing.T, ctx context.Context, a, b *IpfsDHT) { } a.peerstore.AddAddrs(idB, addrB, peer.TempAddrTTL) - if err := a.Connect(ctx, idB); err != nil { + pi := peer.PeerInfo{ID: idB} + if err := a.host.Connect(ctx, pi); err != nil { t.Fatal(err) } } @@ -789,12 +790,14 @@ func TestConnectCollision(t *testing.T) { errs := make(chan error) go func() { dhtA.peerstore.AddAddr(peerB, addrB, peer.TempAddrTTL) - err := dhtA.Connect(ctx, peerB) + pi := peer.PeerInfo{ID: peerB} + err := dhtA.host.Connect(ctx, pi) errs <- err }() go func() { dhtB.peerstore.AddAddr(peerA, addrA, peer.TempAddrTTL) - err := dhtB.Connect(ctx, peerA) + pi := peer.PeerInfo{ID: peerA} + err := dhtB.host.Connect(ctx, pi) errs <- err }() From e8f572739c4348134ba9ac37c5d9dfced2a2db21 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 13 Jul 2015 17:29:55 -0700 Subject: [PATCH 0909/3817] make ping its own protocol License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@d38793fc0d7c142a549c2be2cc60bbf18d18182f --- routing/dht/dht_test.go | 29 ----------------------------- routing/dht/routing.go | 14 -------------- routing/routing.go | 4 ---- 3 files changed, 47 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index edfffcebf..1358903a9 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -102,35 +102,6 @@ func bootstrap(t *testing.T, ctx context.Context, dhts []*IpfsDHT) { cancel() } -func TestPing(t *testing.T) { - // t.Skip("skipping test to debug another") - ctx := context.Background() - - dhtA := setupDHT(ctx, t) - dhtB := setupDHT(ctx, t) - - peerA := dhtA.self - peerB := dhtB.self - - defer dhtA.Close() - defer dhtB.Close() - defer dhtA.host.Close() - defer dhtB.host.Close() - - connect(t, ctx, dhtA, dhtB) - - //Test that we can ping the node - ctxT, _ := context.WithTimeout(ctx, 100*time.Millisecond) - if _, err := dhtA.Ping(ctxT, peerB); err != nil { - t.Fatal(err) - } - - ctxT, _ = context.WithTimeout(ctx, 100*time.Millisecond) - if _, err := dhtB.Ping(ctxT, peerA); err != nil { - t.Fatal(err) - } -} - func TestValueGetSet(t *testing.T) { // t.Skip("skipping test to debug another") diff --git a/routing/dht/routing.go b/routing/dht/routing.go index c4dc76ac4..190e50285 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -2,7 +2,6 @@ package dht import ( "sync" - "time" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" @@ -397,16 +396,3 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< return peerchan, nil } - -// Ping a peer, log the time it took -func (dht *IpfsDHT) Ping(ctx context.Context, p peer.ID) (time.Duration, error) { - // Thoughts: maybe this should accept an ID and do a peer lookup? - log.Debugf("ping %s start", p) - before := time.Now() - - pmes := pb.NewMessage(pb.Message_PING, "", 0) - _, err := dht.sendRequest(ctx, p, pmes) - log.Debugf("ping %s end (err = %s)", p, err) - - return time.Now().Sub(before), err -} diff --git a/routing/routing.go b/routing/routing.go index 31be8f3f8..db9b49dcd 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -3,7 +3,6 @@ package routing import ( "errors" - "time" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" @@ -38,9 +37,6 @@ type IpfsRouting interface { // with relevant addresses. FindPeer(context.Context, peer.ID) (peer.PeerInfo, error) - // Ping a peer, log the time it took - Ping(context.Context, peer.ID) (time.Duration, error) - // Bootstrap allows callers to hint to the routing system to get into a // Boostrapped state Bootstrap(context.Context) error From 209781856b6d24a2efeba362d3458628a34e787d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 14 Jul 2015 12:01:01 -0700 Subject: [PATCH 0910/3817] fix parsing for paths of format /path License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@21baa8a3489a0bf41608dd0a9d975ba07d3dde52 --- path/path.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/path/path.go b/path/path.go index 6fe75adbe..e865ba287 100644 --- a/path/path.go +++ b/path/path.go @@ -56,10 +56,9 @@ func ParsePath(txt string) (Path, error) { return kp, nil } } - if len(parts) < 3 { - return "", ErrBadPath - } + // if the path doesnt being with a '/' + // we expect this to start with a hash, and be an 'ipfs' path if parts[0] != "" { if _, err := ParseKeyToPath(parts[0]); err != nil { return "", ErrBadPath @@ -68,6 +67,10 @@ func ParsePath(txt string) (Path, error) { return Path("/ipfs/" + txt), nil } + if len(parts) < 3 { + return "", ErrBadPath + } + if parts[1] == "ipfs" { if _, err := ParseKeyToPath(parts[2]); err != nil { return "", err From b706aeffd89ed47bdd022ee6443b4f681ec9b35f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 14 Jul 2015 12:16:10 -0700 Subject: [PATCH 0911/3817] add tests for path parsing License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@d04dbfaade853ff7c9c6e68f4ef517ef73ebba3d --- path/path_test.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 path/path_test.go diff --git a/path/path_test.go b/path/path_test.go new file mode 100644 index 000000000..f800e19e7 --- /dev/null +++ b/path/path_test.go @@ -0,0 +1,30 @@ +package path + +import ( + "testing" +) + +func TestPathParsing(t *testing.T) { + cases := map[string]bool{ + "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true, + "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": true, + "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b/c/d/e/f": true, + "/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b/c/d/e/f": true, + "/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true, + "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b/c/d/e/f": true, + "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true, + "/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": false, + "/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": false, + "/ipfs/": false, + "ipfs/": false, + "ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": false, + } + + for p, expected := range cases { + _, err := ParsePath(p) + valid := (err == nil) + if valid != expected { + t.Fatalf("expected %s to have valid == %s", p, expected) + } + } +} From 69dcba83a336777cab77d057e031640835fd14dc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 17 Jul 2015 17:28:44 -0700 Subject: [PATCH 0912/3817] mark other nodes in routing table on test-connect License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@9d3d61d3e9e383c30a491e76357015ca87259908 --- routing/dht/dht_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 1358903a9..6baedfbd1 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -78,6 +78,14 @@ func connect(t *testing.T, ctx context.Context, a, b *IpfsDHT) { if err := a.host.Connect(ctx, pi); err != nil { t.Fatal(err) } + + for a.routingTable.Find(b.self) == "" { + time.Sleep(time.Millisecond * 5) + } + + for b.routingTable.Find(a.self) == "" { + time.Sleep(time.Millisecond * 5) + } } func bootstrap(t *testing.T, ctx context.Context, dhts []*IpfsDHT) { From 0b3a6169c64184106cee06a1e56d0ca0db5e49df Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 19 Jul 2015 16:32:50 -0700 Subject: [PATCH 0913/3817] comment for future @jbenet and @whyrusleeping's to understand reasoning License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@11649ed7e207f1e22c15588a28c2f9c2d4064153 --- routing/dht/dht_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 6baedfbd1..2e63e438e 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -79,6 +79,8 @@ func connect(t *testing.T, ctx context.Context, a, b *IpfsDHT) { t.Fatal(err) } + // loop until connection notification has been received. + // under high load, this may not happen as immediately as we would like. for a.routingTable.Find(b.self) == "" { time.Sleep(time.Millisecond * 5) } From 70c010ec906d5324c6c87d487c3cee63c925e417 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 21 Jul 2015 07:55:58 -0700 Subject: [PATCH 0914/3817] include hash of resolved object in object stat output License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@e40c4df3de11c647cb1c23a1cc334c6e4ba4db62 --- ipld/merkledag/merkledag_test.go | 9 ++++++++- ipld/merkledag/node.go | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 8ab41ca87..d2961d3ad 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -109,12 +109,19 @@ func SubtestNodeStat(t *testing.T, n *Node) { return } + k, err := n.Key() + if err != nil { + t.Error("n.Key() failed") + return + } + expected := NodeStat{ NumLinks: len(n.Links), BlockSize: len(enc), LinksSize: len(enc) - len(n.Data), // includes framing. DataSize: len(n.Data), CumulativeSize: int(cumSize), + Hash: k.B58String(), } actual, err := n.Stat() @@ -124,7 +131,7 @@ func SubtestNodeStat(t *testing.T, n *Node) { } if expected != *actual { - t.Error("n.Stat incorrect.\nexpect: %s\nactual: %s", expected, actual) + t.Errorf("n.Stat incorrect.\nexpect: %s\nactual: %s", expected, actual) } else { fmt.Printf("n.Stat correct: %s\n", actual) } diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index e61503ba2..71a5b5b32 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -23,6 +23,7 @@ type Node struct { // NodeStat is a statistics object for a Node. Mostly sizes. type NodeStat struct { + Hash string NumLinks int // number of links in link table BlockSize int // size of the raw, encoded data LinksSize int // size of the links segment @@ -201,7 +202,13 @@ func (n *Node) Stat() (*NodeStat, error) { return nil, err } + key, err := n.Key() + if err != nil { + return nil, err + } + return &NodeStat{ + Hash: key.B58String(), NumLinks: len(n.Links), BlockSize: len(enc), LinksSize: len(enc) - len(n.Data), // includes framing. From 9e5e1e17915c96b527dd0cc009f06cd7c0cbab3a Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 24 Jul 2015 14:43:17 -0700 Subject: [PATCH 0915/3817] cmds/get: fix context timeout problem Get had a random timeout of 60s. This commit fixes that, wiring up our contexts correctly. License: MIT Signed-off-by: Juan Batiz-Benet This commit was moved from ipfs/go-unixfs@1abe7ad833b2d8a09cd309f97fa603167642f6b3 --- unixfs/tar/reader.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index 20e18fe11..1fac41922 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -9,7 +9,7 @@ import ( "time" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + cxt "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" mdag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" @@ -28,7 +28,7 @@ type Reader struct { err error } -func NewReader(path path.Path, dag mdag.DAGService, dagnode *mdag.Node, compression int) (*Reader, error) { +func NewReader(ctx cxt.Context, path path.Path, dag mdag.DAGService, dagnode *mdag.Node, compression int) (*Reader, error) { reader := &Reader{ signalChan: make(chan struct{}), @@ -49,12 +49,11 @@ func NewReader(path path.Path, dag mdag.DAGService, dagnode *mdag.Node, compress // writeToBuf will write the data to the buffer, and will signal when there // is new data to read _, filename := gopath.Split(path.String()) - go reader.writeToBuf(dagnode, filename, 0) - + go reader.writeToBuf(ctx, dagnode, filename, 0) return reader, nil } -func (r *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { +func (r *Reader) writeToBuf(ctx cxt.Context, dagnode *mdag.Node, path string, depth int) { pb := new(upb.Data) err := proto.Unmarshal(dagnode.Data, pb) if err != nil { @@ -80,16 +79,13 @@ func (r *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { } r.flush() - ctx, cancel := context.WithTimeout(context.TODO(), time.Second*60) - defer cancel() - for i, ng := range r.dag.GetDAG(ctx, dagnode) { childNode, err := ng.Get(ctx) if err != nil { r.emitError(err) return } - r.writeToBuf(childNode, gopath.Join(path, dagnode.Links[i].Name), depth+1) + r.writeToBuf(ctx, childNode, gopath.Join(path, dagnode.Links[i].Name), depth+1) } return } @@ -108,7 +104,7 @@ func (r *Reader) writeToBuf(dagnode *mdag.Node, path string, depth int) { } r.flush() - reader, err := uio.NewDagReader(context.TODO(), dagnode, r.dag) + reader, err := uio.NewDagReader(ctx, dagnode, r.dag) if err != nil { r.emitError(err) return From ac5d6e7c29d99df7119f9570f3f10a933ddce681 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Jul 2015 12:45:33 -0700 Subject: [PATCH 0916/3817] break merkledag utils into its own package License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@00f5bdf1d39274091f2de6b69616a56c36aad6f4 --- ipld/merkledag/node.go | 9 +++ ipld/merkledag/utils/utils.go | 113 +++++++++++++++++++++++++++ ipld/merkledag/utils/utils_test.go | 118 +++++++++++++++++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 ipld/merkledag/utils/utils.go create mode 100644 ipld/merkledag/utils/utils_test.go diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 71a5b5b32..e90ac95e8 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -153,6 +153,15 @@ func (n *Node) GetNodeLink(name string) (*Link, error) { return nil, ErrNotFound } +func (n *Node) GetLinkedNode(ctx context.Context, ds DAGService, name string) (*Node, error) { + lnk, err := n.GetNodeLink(name) + if err != nil { + return nil, err + } + + return lnk.GetNode(ctx, ds) +} + // Copy returns a copy of the node. // NOTE: does not make copies of Node objects in the links. func (n *Node) Copy() *Node { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go new file mode 100644 index 000000000..e82d00229 --- /dev/null +++ b/ipld/merkledag/utils/utils.go @@ -0,0 +1,113 @@ +package dagutils + +import ( + "errors" + "time" + + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + + key "github.com/ipfs/go-ipfs/blocks/key" + dag "github.com/ipfs/go-ipfs/merkledag" + ft "github.com/ipfs/go-ipfs/unixfs" +) + +func AddLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childk key.Key) (*dag.Node, error) { + if childname == "" { + return nil, errors.New("cannot create link with no name!") + } + + ctx, cancel := context.WithTimeout(ctx, time.Second*30) + defer cancel() + childnd, err := ds.Get(ctx, childk) + if err != nil { + return nil, err + } + + // ensure no link with that name already exists + _ = root.RemoveNodeLink(childname) // ignore error, only option is ErrNotFound + + err = root.AddNodeLinkClean(childname, childnd) + if err != nil { + return nil, err + } + + _, err = ds.Add(root) + if err != nil { + return nil, err + } + return root, nil +} + +func InsertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert key.Key, create bool) (*dag.Node, error) { + if len(path) == 1 { + return AddLink(ctx, ds, root, path[0], toinsert) + } + + nd, err := root.GetLinkedNode(ctx, ds, path[0]) + if err != nil { + // if 'create' is true, we create directories on the way down as needed + if err == dag.ErrNotFound && create { + nd = &dag.Node{Data: ft.FolderPBData()} + } else { + return nil, err + } + } + + ndprime, err := InsertNodeAtPath(ctx, ds, nd, path[1:], toinsert, create) + if err != nil { + return nil, err + } + + _ = root.RemoveNodeLink(path[0]) + err = root.AddNodeLinkClean(path[0], ndprime) + if err != nil { + return nil, err + } + + _, err = ds.Add(root) + if err != nil { + return nil, err + } + + return root, nil +} + +func RmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string) (*dag.Node, error) { + if len(path) == 1 { + // base case, remove node in question + err := root.RemoveNodeLink(path[0]) + if err != nil { + return nil, err + } + + _, err = ds.Add(root) + if err != nil { + return nil, err + } + + return root, nil + } + + nd, err := root.GetLinkedNode(ctx, ds, path[0]) + if err != nil { + return nil, err + } + + nnode, err := RmLink(ctx, ds, nd, path[1:]) + if err != nil { + return nil, err + } + + _ = root.RemoveNodeLink(path[0]) + err = root.AddNodeLinkClean(path[0], nnode) + if err != nil { + return nil, err + } + + _, err = ds.Add(root) + if err != nil { + return nil, err + } + + return root, nil +} diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go new file mode 100644 index 000000000..36da81687 --- /dev/null +++ b/ipld/merkledag/utils/utils_test.go @@ -0,0 +1,118 @@ +package dagutils + +import ( + "testing" + + key "github.com/ipfs/go-ipfs/blocks/key" + dag "github.com/ipfs/go-ipfs/merkledag" + mdtest "github.com/ipfs/go-ipfs/merkledag/test" + + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" +) + +func TestAddLink(t *testing.T) { + ds := mdtest.Mock(t) + fishnode := &dag.Node{ + Data: []byte("fishcakes!"), + } + + fk, err := ds.Add(fishnode) + if err != nil { + t.Fatal(err) + } + + nd := new(dag.Node) + nnode, err := AddLink(context.Background(), ds, nd, "fish", fk) + if err != nil { + t.Fatal(err) + } + + fnprime, err := nnode.GetLinkedNode(context.Background(), ds, "fish") + if err != nil { + t.Fatal(err) + } + + fnpkey, err := fnprime.Key() + if err != nil { + t.Fatal(err) + } + + if fnpkey != fk { + t.Fatal("wrong child node found!") + } +} + +func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path []string, exp key.Key) { + cur := root + for _, e := range path { + nxt, err := cur.GetLinkedNode(context.Background(), ds, e) + if err != nil { + t.Fatal(err) + } + + cur = nxt + } + + curk, err := cur.Key() + if err != nil { + t.Fatal(err) + } + + if curk != exp { + t.Fatal("node not as expected at end of path") + } +} + +func TestInsertNode(t *testing.T) { + ds := mdtest.Mock(t) + root := new(dag.Node) + + childa := &dag.Node{ + Data: []byte("This is child A"), + } + ak, err := ds.Add(childa) + if err != nil { + t.Fatal(err) + } + + path := []string{"a", "b", "c", "d"} + root_a, err := InsertNodeAtPath(context.Background(), ds, root, path, ak, true) + if err != nil { + t.Fatal(err) + } + assertNodeAtPath(t, ds, root_a, path, ak) + + childb := &dag.Node{Data: []byte("this is the second child")} + bk, err := ds.Add(childb) + if err != nil { + t.Fatal(err) + } + + // this one should fail, we are specifying a non-existant path + // with create == false + path2 := []string{"a", "b", "e", "f"} + _, err = InsertNodeAtPath(context.Background(), ds, root_a, path2, bk, false) + if err == nil { + t.Fatal("that shouldnt have worked") + } + if err != dag.ErrNotFound { + t.Fatal("expected this to fail with 'not found'") + } + + // inserting a path of length one should work with create == false + path3 := []string{"x"} + root_b, err := InsertNodeAtPath(context.Background(), ds, root_a, path3, bk, false) + if err != nil { + t.Fatal(err) + } + + assertNodeAtPath(t, ds, root_b, path3, bk) + + // now try overwriting a path + root_c, err := InsertNodeAtPath(context.Background(), ds, root_b, path, bk, false) + if err != nil { + t.Fatal(err) + } + + assertNodeAtPath(t, ds, root_c, path, bk) +} From ce33502562015dd11e91228053147bd4566dc956 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 28 Jul 2015 14:12:01 -0700 Subject: [PATCH 0917/3817] implement 'editor' abstraction License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@fea3a0bbdce5f7fa2dfd92d0487ac4800c083672 --- ipld/merkledag/node.go | 20 ++++++--- ipld/merkledag/utils/utils.go | 66 ++++++++++++++++++++++----- ipld/merkledag/utils/utils_test.go | 72 ++++++++++++++++-------------- 3 files changed, 108 insertions(+), 50 deletions(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index e90ac95e8..8d06077c0 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -129,13 +129,23 @@ func (n *Node) AddRawLink(name string, l *Link) error { // Remove a link on this node by the given name func (n *Node) RemoveNodeLink(name string) error { n.encoded = nil - for i, l := range n.Links { - if l.Name == name { - n.Links = append(n.Links[:i], n.Links[i+1:]...) - return nil + good := make([]*Link, 0, len(n.Links)) + var found bool + + for _, l := range n.Links { + if l.Name != name { + good = append(good, l) + } else { + found = true } } - return ErrNotFound + n.Links = good + + if !found { + return ErrNotFound + } + + return nil } // Return a copy of the link with given name diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index e82d00229..6ab612c17 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -2,22 +2,44 @@ package dagutils import ( "errors" - "time" + "strings" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" - ft "github.com/ipfs/go-ipfs/unixfs" ) -func AddLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childk key.Key) (*dag.Node, error) { +type Editor struct { + root *dag.Node + ds dag.DAGService +} + +func NewDagEditor(ds dag.DAGService, root *dag.Node) *Editor { + return &Editor{ + root: root, + ds: ds, + } +} + +func (e *Editor) GetNode() *dag.Node { + return e.root.Copy() +} + +func (e *Editor) AddLink(ctx context.Context, childname string, childk key.Key) error { + nd, err := addLink(ctx, e.ds, e.root, childname, childk) + if err != nil { + return err + } + e.root = nd + return nil +} + +func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childk key.Key) (*dag.Node, error) { if childname == "" { return nil, errors.New("cannot create link with no name!") } - ctx, cancel := context.WithTimeout(ctx, time.Second*30) - defer cancel() childnd, err := ds.Get(ctx, childk) if err != nil { return nil, err @@ -38,22 +60,32 @@ func AddLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname s return root, nil } -func InsertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert key.Key, create bool) (*dag.Node, error) { +func (e *Editor) InsertNodeAtPath(ctx context.Context, path string, toinsert key.Key, create func() *dag.Node) error { + splpath := strings.Split(path, "/") + nd, err := insertNodeAtPath(ctx, e.ds, e.root, splpath, toinsert, create) + if err != nil { + return err + } + e.root = nd + return nil +} + +func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert key.Key, create func() *dag.Node) (*dag.Node, error) { if len(path) == 1 { - return AddLink(ctx, ds, root, path[0], toinsert) + return addLink(ctx, ds, root, path[0], toinsert) } nd, err := root.GetLinkedNode(ctx, ds, path[0]) if err != nil { // if 'create' is true, we create directories on the way down as needed - if err == dag.ErrNotFound && create { - nd = &dag.Node{Data: ft.FolderPBData()} + if err == dag.ErrNotFound && create != nil { + nd = create() } else { return nil, err } } - ndprime, err := InsertNodeAtPath(ctx, ds, nd, path[1:], toinsert, create) + ndprime, err := insertNodeAtPath(ctx, ds, nd, path[1:], toinsert, create) if err != nil { return nil, err } @@ -72,7 +104,17 @@ func InsertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, pa return root, nil } -func RmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string) (*dag.Node, error) { +func (e *Editor) RmLink(ctx context.Context, path string) error { + splpath := strings.Split(path, "/") + nd, err := rmLink(ctx, e.ds, e.root, splpath) + if err != nil { + return err + } + e.root = nd + return nil +} + +func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string) (*dag.Node, error) { if len(path) == 1 { // base case, remove node in question err := root.RemoveNodeLink(path[0]) @@ -93,7 +135,7 @@ func RmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []strin return nil, err } - nnode, err := RmLink(ctx, ds, nd, path[1:]) + nnode, err := rmLink(ctx, ds, nd, path[1:]) if err != nil { return nil, err } diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 36da81687..39b1a519d 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -1,6 +1,7 @@ package dagutils import ( + "strings" "testing" key "github.com/ipfs/go-ipfs/blocks/key" @@ -22,7 +23,7 @@ func TestAddLink(t *testing.T) { } nd := new(dag.Node) - nnode, err := AddLink(context.Background(), ds, nd, "fish", fk) + nnode, err := addLink(context.Background(), ds, nd, "fish", fk) if err != nil { t.Fatal(err) } @@ -42,9 +43,10 @@ func TestAddLink(t *testing.T) { } } -func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path []string, exp key.Key) { +func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path string, exp key.Key) { + parts := strings.Split(path, "/") cur := root - for _, e := range path { + for _, e := range parts { nxt, err := cur.GetLinkedNode(context.Background(), ds, e) if err != nil { t.Fatal(err) @@ -66,53 +68,57 @@ func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path []st func TestInsertNode(t *testing.T) { ds := mdtest.Mock(t) root := new(dag.Node) + e := NewDagEditor(ds, root) - childa := &dag.Node{ - Data: []byte("This is child A"), - } - ak, err := ds.Add(childa) + testInsert(t, e, "a", "anodefortesting", false, "") + testInsert(t, e, "a/b", "data", false, "") + testInsert(t, e, "a/b/c/d/e", "blah", false, "merkledag: not found") + testInsert(t, e, "a/b/c/d/e", "foo", true, "") + testInsert(t, e, "a/b/c/d/f", "baz", true, "") + testInsert(t, e, "a/b/c/d/f", "bar", true, "") + + testInsert(t, e, "", "bar", true, "cannot create link with no name!") + testInsert(t, e, "////", "slashes", true, "cannot create link with no name!") + + k, err := e.GetNode().Key() if err != nil { t.Fatal(err) } - path := []string{"a", "b", "c", "d"} - root_a, err := InsertNodeAtPath(context.Background(), ds, root, path, ak, true) - if err != nil { - t.Fatal(err) + if k.B58String() != "QmThorWojP6YzLJwDukxiYCoKQSwyrMCvdt4WZ6rPm221t" { + t.Fatal("output was different than expected") } - assertNodeAtPath(t, ds, root_a, path, ak) +} - childb := &dag.Node{Data: []byte("this is the second child")} - bk, err := ds.Add(childb) +func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr string) { + child := &dag.Node{Data: []byte(data)} + ck, err := e.ds.Add(child) if err != nil { t.Fatal(err) } - // this one should fail, we are specifying a non-existant path - // with create == false - path2 := []string{"a", "b", "e", "f"} - _, err = InsertNodeAtPath(context.Background(), ds, root_a, path2, bk, false) - if err == nil { - t.Fatal("that shouldnt have worked") - } - if err != dag.ErrNotFound { - t.Fatal("expected this to fail with 'not found'") + var c func() *dag.Node + if create { + c = func() *dag.Node { + return &dag.Node{} + } } - // inserting a path of length one should work with create == false - path3 := []string{"x"} - root_b, err := InsertNodeAtPath(context.Background(), ds, root_a, path3, bk, false) - if err != nil { - t.Fatal(err) + err = e.InsertNodeAtPath(context.TODO(), path, ck, c) + if experr != "" { + var got string + if err != nil { + got = err.Error() + } + if got != experr { + t.Fatalf("expected '%s' but got '%s'", experr, got) + } + return } - assertNodeAtPath(t, ds, root_b, path3, bk) - - // now try overwriting a path - root_c, err := InsertNodeAtPath(context.Background(), ds, root_b, path, bk, false) if err != nil { t.Fatal(err) } - assertNodeAtPath(t, ds, root_c, path, bk) + assertNodeAtPath(t, e.ds, e.root, path, ck) } From f37093e56779ab317091f395ad36dd822681b0fb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 28 Jul 2015 21:15:35 -0700 Subject: [PATCH 0918/3817] a little more test coverage on merkledag License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@d28e7cb77ba5a43327fdadf2309ebf9ce7ca2c49 --- ipld/merkledag/coding.go | 12 +----- ipld/merkledag/merkledag_test.go | 68 ++++++++++++++++++++++++++++++++ ipld/merkledag/node_test.go | 54 +++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 ipld/merkledag/node_test.go diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index f8cc326a4..7baf863c8 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -37,16 +37,6 @@ func (n *Node) Unmarshal(encoded []byte) error { return nil } -// MarshalTo encodes a *Node instance into a given byte slice. -// The conversion uses an intermediate PBNode. -func (n *Node) MarshalTo(encoded []byte) error { - pbn := n.getPBNode() - if _, err := pbn.MarshalTo(encoded); err != nil { - return fmt.Errorf("Marshal failed. %v", err) - } - return nil -} - // Marshal encodes a *Node instance into a new byte slice. // The conversion uses an intermediate PBNode. func (n *Node) Marshal() ([]byte, error) { @@ -82,7 +72,7 @@ func (n *Node) Encoded(force bool) ([]byte, error) { var err error n.encoded, err = n.Marshal() if err != nil { - return []byte{}, err + return nil, err } n.cached = u.Hash(n.encoded) } diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index d2961d3ad..fc110bfd7 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "io/ioutil" + "strings" "sync" "testing" @@ -221,3 +222,70 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { wg.Wait() } + +func TestRecursiveAdd(t *testing.T) { + a := &Node{Data: []byte("A")} + b := &Node{Data: []byte("B")} + c := &Node{Data: []byte("C")} + d := &Node{Data: []byte("D")} + e := &Node{Data: []byte("E")} + + err := a.AddNodeLink("blah", b) + if err != nil { + t.Fatal(err) + } + + err = b.AddNodeLink("foo", c) + if err != nil { + t.Fatal(err) + } + + err = b.AddNodeLink("bar", d) + if err != nil { + t.Fatal(err) + } + + err = d.AddNodeLink("baz", e) + if err != nil { + t.Fatal(err) + } + + dsp := getDagservAndPinner(t) + err = dsp.ds.AddRecursive(a) + if err != nil { + t.Fatal(err) + } + + assertCanGet(t, dsp.ds, a) + assertCanGet(t, dsp.ds, b) + assertCanGet(t, dsp.ds, c) + assertCanGet(t, dsp.ds, d) + assertCanGet(t, dsp.ds, e) +} + +func assertCanGet(t *testing.T, ds DAGService, n *Node) { + k, err := n.Key() + if err != nil { + t.Fatal(err) + } + + _, err = ds.Get(context.TODO(), k) + if err != nil { + t.Fatal(err) + } +} + +func TestCantGet(t *testing.T) { + dsp := getDagservAndPinner(t) + a := &Node{Data: []byte("A")} + + k, err := a.Key() + if err != nil { + t.Fatal(err) + } + + _, err = dsp.ds.Get(context.TODO(), k) + if !strings.Contains(err.Error(), "not found") { + t.Fatal("expected err not found, got: ", err) + } +} diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go new file mode 100644 index 000000000..75aa4c988 --- /dev/null +++ b/ipld/merkledag/node_test.go @@ -0,0 +1,54 @@ +package merkledag + +import ( + "testing" +) + +func TestRemoveLink(t *testing.T) { + nd := &Node{ + Links: []*Link{ + &Link{Name: "a"}, + &Link{Name: "b"}, + &Link{Name: "a"}, + &Link{Name: "a"}, + &Link{Name: "c"}, + &Link{Name: "a"}, + }, + } + + err := nd.RemoveNodeLink("a") + if err != nil { + t.Fatal(err) + } + + if len(nd.Links) != 2 { + t.Fatal("number of links incorrect") + } + + if nd.Links[0].Name != "b" { + t.Fatal("link order wrong") + } + + if nd.Links[1].Name != "c" { + t.Fatal("link order wrong") + } + + // should fail + err = nd.RemoveNodeLink("a") + if err != ErrNotFound { + t.Fatal("should have failed to remove link") + } + + // ensure nothing else got touched + if len(nd.Links) != 2 { + t.Fatal("number of links incorrect") + } + + if nd.Links[0].Name != "b" { + t.Fatal("link order wrong") + } + + if nd.Links[1].Name != "c" { + t.Fatal("link order wrong") + } +} From b00b6e51cde8dfff3d3a09bdf2468e3a8bb11090 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Mon, 3 Aug 2015 16:30:23 +0200 Subject: [PATCH 0919/3817] unixfs/tar: cleaned up reader code License: MIT Signed-off-by: Juan Batiz-Benet This commit was moved from ipfs/go-unixfs@ff5195ac74392251af2c59847f8de19048697fd5 --- unixfs/tar/reader.go | 117 +++++++++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 48 deletions(-) diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go index 1fac41922..2c55b1bbc 100644 --- a/unixfs/tar/reader.go +++ b/unixfs/tar/reader.go @@ -4,6 +4,7 @@ import ( "archive/tar" "bytes" "compress/gzip" + "fmt" "io" gopath "path" "time" @@ -49,71 +50,70 @@ func NewReader(ctx cxt.Context, path path.Path, dag mdag.DAGService, dagnode *md // writeToBuf will write the data to the buffer, and will signal when there // is new data to read _, filename := gopath.Split(path.String()) - go reader.writeToBuf(ctx, dagnode, filename, 0) + go func() { + if err := reader.writeNodeToBuf(ctx, dagnode, filename, 0); err != nil { + reader.emitError(err) + } + }() return reader, nil } -func (r *Reader) writeToBuf(ctx cxt.Context, dagnode *mdag.Node, path string, depth int) { - pb := new(upb.Data) - err := proto.Unmarshal(dagnode.Data, pb) - if err != nil { - r.emitError(err) - return - } - - if depth == 0 { - defer r.close() +func (r *Reader) writeDirToBuf(ctx cxt.Context, nd *mdag.Node, path string, depth int) error { + if err := writeDirHeader(r.writer, path); err != nil { + return err } + r.flush() - if pb.GetType() == upb.Data_Directory { - err = r.writer.WriteHeader(&tar.Header{ - Name: path, - Typeflag: tar.TypeDir, - Mode: 0777, - ModTime: time.Now(), - // TODO: set mode, dates, etc. when added to unixFS - }) + for i, ng := range r.dag.GetDAG(ctx, nd) { + child, err := ng.Get(ctx) if err != nil { - r.emitError(err) - return + return err } - r.flush() - for i, ng := range r.dag.GetDAG(ctx, dagnode) { - childNode, err := ng.Get(ctx) - if err != nil { - r.emitError(err) - return - } - r.writeToBuf(ctx, childNode, gopath.Join(path, dagnode.Links[i].Name), depth+1) + npath := gopath.Join(path, nd.Links[i].Name) + if err := r.writeNodeToBuf(ctx, child, npath, depth+1); err != nil { + return err } - return } - err = r.writer.WriteHeader(&tar.Header{ - Name: path, - Size: int64(pb.GetFilesize()), - Typeflag: tar.TypeReg, - Mode: 0644, - ModTime: time.Now(), - // TODO: set mode, dates, etc. when added to unixFS - }) - if err != nil { - r.emitError(err) - return + return nil +} + +func (r *Reader) writeFileToBuf(ctx cxt.Context, nd *mdag.Node, pb *upb.Data, path string, depth int) error { + if err := writeFileHeader(r.writer, path, pb.GetFilesize()); err != nil { + return err } r.flush() - reader, err := uio.NewDagReader(ctx, dagnode, r.dag) + reader, err := uio.NewDagReader(ctx, nd, r.dag) if err != nil { - r.emitError(err) - return + return err } - err = r.syncCopy(reader) - if err != nil { - r.emitError(err) - return + if err := r.syncCopy(reader); err != nil { + return err + } + + return nil +} + +func (r *Reader) writeNodeToBuf(ctx cxt.Context, nd *mdag.Node, path string, depth int) error { + pb := new(upb.Data) + if err := proto.Unmarshal(nd.Data, pb); err != nil { + return err + } + + if depth == 0 { + defer r.close() + } + + switch pb.GetType() { + case upb.Data_Directory: + return r.writeDirToBuf(ctx, nd, path, depth) + case upb.Data_File: + return r.writeFileToBuf(ctx, nd, pb, path, depth) + default: + return fmt.Errorf("unixfs type not supported: %s", pb.GetType()) } } @@ -198,3 +198,24 @@ func (r *Reader) syncCopy(reader io.Reader) error { } return nil } + +func writeDirHeader(w *tar.Writer, path string) error { + return w.WriteHeader(&tar.Header{ + Name: path, + Typeflag: tar.TypeDir, + Mode: 0777, + ModTime: time.Now(), + // TODO: set mode, dates, etc. when added to unixFS + }) +} + +func writeFileHeader(w *tar.Writer, path string, size uint64) error { + return w.WriteHeader(&tar.Header{ + Name: path, + Size: int64(size), + Typeflag: tar.TypeReg, + Mode: 0644, + ModTime: time.Now(), + // TODO: set mode, dates, etc. when added to unixFS + }) +} From 07878391f3c8d6d7c46e03e5516d2b68fbb49784 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 4 Aug 2015 12:14:58 +0200 Subject: [PATCH 0920/3817] get: fix bug + improvements up until now there has been a very annoying bug with get, we would get halting behavior. I'm not 100% sure this commit fixes it, but it should. It certainly fixes others found in the process of digging into the get / tar extractor code. (wish we could repro the bug reliably enough to make a test case). This is a much cleaner tar writer. the ad-hoc, error-prone synch for the tar reader is gone (with i believe was incorrect). it is replaced with a simple pipe and bufio. The tar logic is now in tar.Writer, which writes unixfs dag nodes into a tar archive (no need for synch here). And get's reader is constructed with DagArchive which sets up the pipe + bufio. NOTE: this commit also changes this behavior of `get`: When retrieving a single file, if the file exists, get would fail. this emulated the behavior of wget by default, which (without opts) does not overwrite if the file is there. This change makes get fail if the file is available locally. This seems more intuitive to me as expected from a unix tool-- though perhaps it should be discussed more before adopting. Everything seems to work fine, and i have not been able to reproduce the get halt bug. License: MIT Signed-off-by: Juan Batiz-Benet This commit was moved from ipfs/go-unixfs@3e90d66e463c7392ed80a75941d23ffe04e97b90 --- unixfs/tar/reader.go | 221 ------------------------------------------- unixfs/tar/writer.go | 170 +++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 221 deletions(-) delete mode 100644 unixfs/tar/reader.go create mode 100644 unixfs/tar/writer.go diff --git a/unixfs/tar/reader.go b/unixfs/tar/reader.go deleted file mode 100644 index 2c55b1bbc..000000000 --- a/unixfs/tar/reader.go +++ /dev/null @@ -1,221 +0,0 @@ -package tar - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "fmt" - "io" - gopath "path" - "time" - - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" - cxt "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - - mdag "github.com/ipfs/go-ipfs/merkledag" - path "github.com/ipfs/go-ipfs/path" - uio "github.com/ipfs/go-ipfs/unixfs/io" - upb "github.com/ipfs/go-ipfs/unixfs/pb" -) - -type Reader struct { - buf bytes.Buffer - closed bool - signalChan chan struct{} - dag mdag.DAGService - resolver *path.Resolver - writer *tar.Writer - gzipWriter *gzip.Writer - err error -} - -func NewReader(ctx cxt.Context, path path.Path, dag mdag.DAGService, dagnode *mdag.Node, compression int) (*Reader, error) { - - reader := &Reader{ - signalChan: make(chan struct{}), - dag: dag, - } - - var err error - if compression != gzip.NoCompression { - reader.gzipWriter, err = gzip.NewWriterLevel(&reader.buf, compression) - if err != nil { - return nil, err - } - reader.writer = tar.NewWriter(reader.gzipWriter) - } else { - reader.writer = tar.NewWriter(&reader.buf) - } - - // writeToBuf will write the data to the buffer, and will signal when there - // is new data to read - _, filename := gopath.Split(path.String()) - go func() { - if err := reader.writeNodeToBuf(ctx, dagnode, filename, 0); err != nil { - reader.emitError(err) - } - }() - return reader, nil -} - -func (r *Reader) writeDirToBuf(ctx cxt.Context, nd *mdag.Node, path string, depth int) error { - if err := writeDirHeader(r.writer, path); err != nil { - return err - } - r.flush() - - for i, ng := range r.dag.GetDAG(ctx, nd) { - child, err := ng.Get(ctx) - if err != nil { - return err - } - - npath := gopath.Join(path, nd.Links[i].Name) - if err := r.writeNodeToBuf(ctx, child, npath, depth+1); err != nil { - return err - } - } - - return nil -} - -func (r *Reader) writeFileToBuf(ctx cxt.Context, nd *mdag.Node, pb *upb.Data, path string, depth int) error { - if err := writeFileHeader(r.writer, path, pb.GetFilesize()); err != nil { - return err - } - r.flush() - - reader, err := uio.NewDagReader(ctx, nd, r.dag) - if err != nil { - return err - } - - if err := r.syncCopy(reader); err != nil { - return err - } - - return nil -} - -func (r *Reader) writeNodeToBuf(ctx cxt.Context, nd *mdag.Node, path string, depth int) error { - pb := new(upb.Data) - if err := proto.Unmarshal(nd.Data, pb); err != nil { - return err - } - - if depth == 0 { - defer r.close() - } - - switch pb.GetType() { - case upb.Data_Directory: - return r.writeDirToBuf(ctx, nd, path, depth) - case upb.Data_File: - return r.writeFileToBuf(ctx, nd, pb, path, depth) - default: - return fmt.Errorf("unixfs type not supported: %s", pb.GetType()) - } -} - -func (r *Reader) Read(p []byte) (int, error) { - // wait for the goroutine that is writing data to the buffer to tell us - // there is something to read - if !r.closed { - <-r.signalChan - } - - if r.err != nil { - return 0, r.err - } - - if !r.closed { - defer r.signal() - } - - if r.buf.Len() == 0 { - if r.closed { - return 0, io.EOF - } - return 0, nil - } - - n, err := r.buf.Read(p) - if err == io.EOF && !r.closed || r.buf.Len() > 0 { - return n, nil - } - - return n, err -} - -func (r *Reader) signal() { - r.signalChan <- struct{}{} -} - -func (r *Reader) flush() { - r.signal() - <-r.signalChan -} - -func (r *Reader) emitError(err error) { - r.err = err - r.signal() -} - -func (r *Reader) close() { - r.closed = true - defer r.signal() - err := r.writer.Close() - if err != nil { - r.emitError(err) - return - } - if r.gzipWriter != nil { - err = r.gzipWriter.Close() - if err != nil { - r.emitError(err) - return - } - } -} - -func (r *Reader) syncCopy(reader io.Reader) error { - buf := make([]byte, 32*1024) - for { - nr, err := reader.Read(buf) - if nr > 0 { - _, err := r.writer.Write(buf[:nr]) - if err != nil { - return err - } - r.flush() - } - if err == io.EOF { - break - } - if err != nil { - return err - } - } - return nil -} - -func writeDirHeader(w *tar.Writer, path string) error { - return w.WriteHeader(&tar.Header{ - Name: path, - Typeflag: tar.TypeDir, - Mode: 0777, - ModTime: time.Now(), - // TODO: set mode, dates, etc. when added to unixFS - }) -} - -func writeFileHeader(w *tar.Writer, path string, size uint64) error { - return w.WriteHeader(&tar.Header{ - Name: path, - Size: int64(size), - Typeflag: tar.TypeReg, - Mode: 0644, - ModTime: time.Now(), - // TODO: set mode, dates, etc. when added to unixFS - }) -} diff --git a/unixfs/tar/writer.go b/unixfs/tar/writer.go new file mode 100644 index 000000000..125beed96 --- /dev/null +++ b/unixfs/tar/writer.go @@ -0,0 +1,170 @@ +package tar + +import ( + "archive/tar" + "bufio" + "compress/gzip" + "fmt" + "io" + "path" + "time" + + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + cxt "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + + mdag "github.com/ipfs/go-ipfs/merkledag" + uio "github.com/ipfs/go-ipfs/unixfs/io" + upb "github.com/ipfs/go-ipfs/unixfs/pb" +) + +// DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. +// TODO: does this need to be configurable? +var DefaultBufSize = 1048576 + +func DagArchive(ctx cxt.Context, nd *mdag.Node, name string, dag mdag.DAGService, compression int) (io.Reader, error) { + + _, filename := path.Split(name) + + // need to connect a writer to a reader + piper, pipew := io.Pipe() + + // use a buffered writer to parallelize task + bufw := bufio.NewWriterSize(pipew, DefaultBufSize) + + // construct the tar writer + w, err := NewWriter(bufw, dag, compression) + if err != nil { + return nil, err + } + + // write all the nodes recursively + go func() { + if err := w.WriteNode(ctx, nd, filename); err != nil { + pipew.CloseWithError(err) + return + } + + if err := bufw.Flush(); err != nil { + pipew.CloseWithError(err) + return + } + + pipew.Close() // everything seems to be ok. + }() + + return piper, nil +} + +// Writer is a utility structure that helps to write +// unixfs merkledag nodes as a tar archive format. +// It wraps any io.Writer. +type Writer struct { + Dag mdag.DAGService + TarW *tar.Writer +} + +// NewWriter wraps given io.Writer. +// compression determines whether to use gzip compression. +func NewWriter(w io.Writer, dag mdag.DAGService, compression int) (*Writer, error) { + + if compression != gzip.NoCompression { + var err error + w, err = gzip.NewWriterLevel(w, compression) + if err != nil { + return nil, err + } + } + + return &Writer{ + Dag: dag, + TarW: tar.NewWriter(w), + }, nil +} + +func (w *Writer) WriteDir(ctx cxt.Context, nd *mdag.Node, fpath string) error { + if err := writeDirHeader(w.TarW, fpath); err != nil { + return err + } + + for i, ng := range w.Dag.GetDAG(ctx, nd) { + child, err := ng.Get(ctx) + if err != nil { + return err + } + + npath := path.Join(fpath, nd.Links[i].Name) + if err := w.WriteNode(ctx, child, npath); err != nil { + return err + } + } + + return nil +} + +func (w *Writer) WriteFile(ctx cxt.Context, nd *mdag.Node, fpath string) error { + pb := new(upb.Data) + if err := proto.Unmarshal(nd.Data, pb); err != nil { + return err + } + + return w.writeFile(ctx, nd, pb, fpath) +} + +func (w *Writer) writeFile(ctx cxt.Context, nd *mdag.Node, pb *upb.Data, fpath string) error { + if err := writeFileHeader(w.TarW, fpath, pb.GetFilesize()); err != nil { + return err + } + + dagr, err := uio.NewDagReader(ctx, nd, w.Dag) + if err != nil { + return err + } + + _, err = io.Copy(w.TarW, dagr) + if err != nil && err != io.EOF { + return err + } + + return nil +} + +func (w *Writer) WriteNode(ctx cxt.Context, nd *mdag.Node, fpath string) error { + pb := new(upb.Data) + if err := proto.Unmarshal(nd.Data, pb); err != nil { + return err + } + + switch pb.GetType() { + case upb.Data_Directory: + return w.WriteDir(ctx, nd, fpath) + case upb.Data_File: + return w.writeFile(ctx, nd, pb, fpath) + default: + return fmt.Errorf("unixfs type not supported: %s", pb.GetType()) + } +} + +func (w *Writer) Close() error { + return w.TarW.Close() +} + +func writeDirHeader(w *tar.Writer, fpath string) error { + return w.WriteHeader(&tar.Header{ + Name: fpath, + Typeflag: tar.TypeDir, + Mode: 0777, + ModTime: time.Now(), + // TODO: set mode, dates, etc. when added to unixFS + }) +} + +func writeFileHeader(w *tar.Writer, fpath string, size uint64) error { + return w.WriteHeader(&tar.Header{ + Name: fpath, + Size: int64(size), + Typeflag: tar.TypeReg, + Mode: 0644, + ModTime: time.Now(), + // TODO: set mode, dates, etc. when added to unixFS + }) +} From 27190f63b4e952eb7aa57561fc9c8f6ff65ddaf3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 29 Jul 2015 13:08:37 -0700 Subject: [PATCH 0921/3817] use rabin fingerprinting for a chunker License: MIT Signed-off-by: Jeromy implement rabin fingerprinting as a chunker for ipfs License: MIT Signed-off-by: Jeromy vendor correctly License: MIT Signed-off-by: Jeromy refactor chunking interface a little License: MIT Signed-off-by: Jeromy work chunking interface changes up into importer License: MIT Signed-off-by: Jeromy move chunker type parsing into its own file in chunk License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-chunker@1ff7a98492ada726d7693c6eb898732e61d19808 --- chunker/parse.go | 76 ++++++++++++++++++++++++++++ chunker/rabin.go | 103 +++++++++----------------------------- chunker/rabin_test.go | 84 +++++++++++++++++++++++++++++++ chunker/splitting.go | 70 +++++++++++++++++++------- chunker/splitting_test.go | 10 ++-- 5 files changed, 240 insertions(+), 103 deletions(-) create mode 100644 chunker/parse.go create mode 100644 chunker/rabin_test.go diff --git a/chunker/parse.go b/chunker/parse.go new file mode 100644 index 000000000..55e96cc04 --- /dev/null +++ b/chunker/parse.go @@ -0,0 +1,76 @@ +package chunk + +import ( + "errors" + "fmt" + "io" + "strconv" + "strings" +) + +func FromString(r io.Reader, chunker string) (Splitter, error) { + switch { + case chunker == "" || chunker == "default": + return NewSizeSplitter(r, DefaultBlockSize), nil + + case strings.HasPrefix(chunker, "size-"): + sizeStr := strings.Split(chunker, "-")[1] + size, err := strconv.Atoi(sizeStr) + if err != nil { + return nil, err + } + return NewSizeSplitter(r, int64(size)), nil + + case strings.HasPrefix(chunker, "rabin"): + return parseRabinString(r, chunker) + + default: + return nil, fmt.Errorf("unrecognized chunker option: %s", chunker) + } +} + +func parseRabinString(r io.Reader, chunker string) (Splitter, error) { + parts := strings.Split(chunker, "-") + switch len(parts) { + case 1: + return NewRabin(r, uint64(DefaultBlockSize)), nil + case 2: + size, err := strconv.Atoi(parts[1]) + if err != nil { + return nil, err + } + return NewRabin(r, uint64(size)), nil + case 4: + sub := strings.Split(parts[1], ":") + if len(sub) > 1 && sub[0] != "min" { + return nil, errors.New("first label must be min") + } + min, err := strconv.Atoi(sub[len(sub)-1]) + if err != nil { + return nil, err + } + + sub = strings.Split(parts[2], ":") + if len(sub) > 1 && sub[0] != "avg" { + log.Error("sub == ", sub) + return nil, errors.New("second label must be avg") + } + avg, err := strconv.Atoi(sub[len(sub)-1]) + if err != nil { + return nil, err + } + + sub = strings.Split(parts[3], ":") + if len(sub) > 1 && sub[0] != "max" { + return nil, errors.New("final label must be max") + } + max, err := strconv.Atoi(sub[len(sub)-1]) + if err != nil { + return nil, err + } + + return NewRabinMinMax(r, uint64(min), uint64(avg), uint64(max)), nil + default: + return nil, errors.New("incorrect format (expected 'rabin' 'rabin-[avg]' or 'rabin-[min]-[avg]-[max]'") + } +} diff --git a/chunker/rabin.go b/chunker/rabin.go index fbfb4cec4..de68ae079 100644 --- a/chunker/rabin.go +++ b/chunker/rabin.go @@ -1,94 +1,39 @@ package chunk import ( - "bufio" - "bytes" - "fmt" + "hash/fnv" "io" - "math" + + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/chunker" ) -type MaybeRabin struct { - mask int - windowSize int - MinBlockSize int - MaxBlockSize int -} +var IpfsRabinPoly = chunker.Pol(17437180132763653) -func NewMaybeRabin(avgBlkSize int) *MaybeRabin { - blkbits := uint(math.Log2(float64(avgBlkSize))) - rb := new(MaybeRabin) - rb.mask = (1 << blkbits) - 1 - rb.windowSize = 16 // probably a good number... - rb.MinBlockSize = avgBlkSize / 2 - rb.MaxBlockSize = (avgBlkSize / 2) * 3 - return rb +type Rabin struct { + r *chunker.Chunker } -func (mr *MaybeRabin) Split(r io.Reader) chan []byte { - out := make(chan []byte, 16) - go func() { - inbuf := bufio.NewReader(r) - blkbuf := new(bytes.Buffer) - - // some bullshit numbers i made up - a := 10 // honestly, no idea what this is - MOD := 33554383 // randomly chosen (seriously) - an := 1 - rollingHash := 0 +func NewRabin(r io.Reader, avgBlkSize uint64) *Rabin { + min := avgBlkSize / 3 + max := avgBlkSize + (avgBlkSize / 2) - // Window is a circular buffer - window := make([]byte, mr.windowSize) - push := func(i int, val byte) (outval int) { - outval = int(window[i%len(window)]) - window[i%len(window)] = val - return - } + return NewRabinMinMax(r, avgBlkSize, min, max) +} - // Duplicate byte slice - dup := func(b []byte) []byte { - d := make([]byte, len(b)) - copy(d, b) - return d - } +func NewRabinMinMax(r io.Reader, min, avg, max uint64) *Rabin { + h := fnv.New32a() + ch := chunker.New(r, IpfsRabinPoly, h, avg, min, max) - // Fill up the window - i := 0 - for ; i < mr.windowSize; i++ { - b, err := inbuf.ReadByte() - if err != nil { - fmt.Println(err) - return - } - blkbuf.WriteByte(b) - push(i, b) - rollingHash = (rollingHash*a + int(b)) % MOD - an = (an * a) % MOD - } + return &Rabin{ + r: ch, + } +} - for ; true; i++ { - b, err := inbuf.ReadByte() - if err != nil { - break - } - outval := push(i, b) - blkbuf.WriteByte(b) - rollingHash = (rollingHash*a + int(b) - an*outval) % MOD - if (rollingHash&mr.mask == mr.mask && blkbuf.Len() > mr.MinBlockSize) || - blkbuf.Len() >= mr.MaxBlockSize { - out <- dup(blkbuf.Bytes()) - blkbuf.Reset() - } +func (r *Rabin) NextBytes() ([]byte, error) { + ch, err := r.r.Next() + if err != nil { + return nil, err + } - // Check if there are enough remaining - peek, err := inbuf.Peek(mr.windowSize) - if err != nil || len(peek) != mr.windowSize { - break - } - } - io.Copy(blkbuf, inbuf) - out <- blkbuf.Bytes() - close(out) - }() - return out + return ch.Data, nil } diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go new file mode 100644 index 000000000..596f2f63e --- /dev/null +++ b/chunker/rabin_test.go @@ -0,0 +1,84 @@ +package chunk + +import ( + "bytes" + "fmt" + "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-ipfs/blocks/key" + "github.com/ipfs/go-ipfs/util" + "io" + "testing" +) + +func TestRabinChunking(t *testing.T) { + data := make([]byte, 1024*1024*16) + util.NewTimeSeededRand().Read(data) + + r := NewRabin(bytes.NewReader(data), 1024*256) + + var chunks [][]byte + + for { + chunk, err := r.NextBytes() + if err != nil { + if err == io.EOF { + break + } + t.Fatal(err) + } + + chunks = append(chunks, chunk) + } + + fmt.Printf("average block size: %d\n", len(data)/len(chunks)) + + unchunked := bytes.Join(chunks, nil) + if !bytes.Equal(unchunked, data) { + fmt.Printf("%d %d\n", len(unchunked), len(data)) + t.Fatal("data was chunked incorrectly") + } +} + +func chunkData(t *testing.T, data []byte) map[key.Key]*blocks.Block { + r := NewRabin(bytes.NewReader(data), 1024*256) + + blkmap := make(map[key.Key]*blocks.Block) + + for { + blk, err := r.NextBytes() + if err != nil { + if err == io.EOF { + break + } + t.Fatal(err) + } + + b := blocks.NewBlock(blk) + blkmap[b.Key()] = b + } + + return blkmap +} + +func TestRabinChunkReuse(t *testing.T) { + data := make([]byte, 1024*1024*16) + util.NewTimeSeededRand().Read(data) + + ch1 := chunkData(t, data[1000:]) + ch2 := chunkData(t, data) + + var extra int + for k, _ := range ch2 { + _, ok := ch1[k] + if !ok { + extra++ + } + } + + if extra > 2 { + t.Fatal("too many spare chunks made") + } + if extra == 2 { + t.Log("why did we get two extra blocks?") + } +} diff --git a/chunker/splitting.go b/chunker/splitting.go index 999ed367f..960947245 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -9,39 +9,71 @@ import ( var log = util.Logger("chunk") -var DefaultBlockSize = 1024 * 256 -var DefaultSplitter = &SizeSplitter{Size: DefaultBlockSize} +var DefaultBlockSize int64 = 1024 * 256 -type BlockSplitter interface { - Split(r io.Reader) chan []byte +type Splitter interface { + NextBytes() ([]byte, error) } -type SizeSplitter struct { - Size int +type SplitterGen func(r io.Reader) Splitter + +func DefaultSplitter(r io.Reader) Splitter { + return NewSizeSplitter(r, DefaultBlockSize) +} + +func SizeSplitterGen(size int64) SplitterGen { + return func(r io.Reader) Splitter { + return NewSizeSplitter(r, size) + } } -func (ss *SizeSplitter) Split(r io.Reader) chan []byte { +func Chan(s Splitter) (<-chan []byte, <-chan error) { out := make(chan []byte) + errs := make(chan error, 1) go func() { defer close(out) + defer close(errs) // all-chunks loop (keep creating chunks) for { - // log.Infof("making chunk with size: %d", ss.Size) - chunk := make([]byte, ss.Size) - nread, err := io.ReadFull(r, chunk) - if nread > 0 { - // log.Infof("sending out chunk with size: %d", sofar) - out <- chunk[:nread] - } - if err == io.EOF || err == io.ErrUnexpectedEOF { - return - } + b, err := s.NextBytes() if err != nil { - log.Debugf("Block split error: %s", err) + errs <- err return } + + out <- b } }() - return out + return out, errs +} + +type sizeSplitterv2 struct { + r io.Reader + size int64 + err error +} + +func NewSizeSplitter(r io.Reader, size int64) Splitter { + return &sizeSplitterv2{ + r: r, + size: size, + } +} + +func (ss *sizeSplitterv2) NextBytes() ([]byte, error) { + if ss.err != nil { + return nil, ss.err + } + buf := make([]byte, ss.size) + n, err := io.ReadFull(ss.r, buf) + if err == io.ErrUnexpectedEOF { + ss.err = io.EOF + err = nil + } + if err != nil { + return nil, err + } + + return buf[:n], nil } diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index 232b4fde9..27b2a7b7a 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -32,8 +32,8 @@ func TestSizeSplitterIsDeterministic(t *testing.T) { bufA := copyBuf(bufR) bufB := copyBuf(bufR) - chunksA := DefaultSplitter.Split(bytes.NewReader(bufA)) - chunksB := DefaultSplitter.Split(bytes.NewReader(bufB)) + chunksA, _ := Chan(DefaultSplitter(bytes.NewReader(bufA))) + chunksB, _ := Chan(DefaultSplitter(bytes.NewReader(bufB))) for n := 0; ; n++ { a, moreA := <-chunksA @@ -65,8 +65,8 @@ func TestSizeSplitterFillsChunks(t *testing.T) { max := 10000000 b := randBuf(t, max) r := &clipReader{r: bytes.NewReader(b), size: 4000} - s := SizeSplitter{Size: 1024 * 256} - c := s.Split(r) + chunksize := int64(1024 * 256) + c, _ := Chan(NewSizeSplitter(r, chunksize)) sofar := 0 whole := make([]byte, max) @@ -80,7 +80,7 @@ func TestSizeSplitterFillsChunks(t *testing.T) { copy(whole[sofar:], chunk) sofar += len(chunk) - if sofar != max && len(chunk) < s.Size { + if sofar != max && len(chunk) < int(chunksize) { t.Fatal("sizesplitter split at a smaller size") } } From b40b083fa959b5ba39a481532cf1aefaa747f1b3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 29 Jul 2015 13:08:37 -0700 Subject: [PATCH 0922/3817] use rabin fingerprinting for a chunker License: MIT Signed-off-by: Jeromy implement rabin fingerprinting as a chunker for ipfs License: MIT Signed-off-by: Jeromy vendor correctly License: MIT Signed-off-by: Jeromy refactor chunking interface a little License: MIT Signed-off-by: Jeromy work chunking interface changes up into importer License: MIT Signed-off-by: Jeromy move chunker type parsing into its own file in chunk License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@ca37d6f156114b95cdae3d9884b22fd46df4f848 --- ipld/merkledag/merkledag_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index fc110bfd7..4a87f6d84 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -163,9 +163,9 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { dagservs = append(dagservs, NewDAGService(bsi)) } - spl := &chunk.SizeSplitter{512} + spl := chunk.NewSizeSplitter(read, 512) - root, err := imp.BuildDagFromReader(read, dagservs[0], spl, nil) + root, err := imp.BuildDagFromReader(dagservs[0], spl, nil) if err != nil { t.Fatal(err) } From c5f82b9c71e99012a366618296f1caa7a57a39c0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 29 Jul 2015 13:08:37 -0700 Subject: [PATCH 0923/3817] use rabin fingerprinting for a chunker License: MIT Signed-off-by: Jeromy implement rabin fingerprinting as a chunker for ipfs License: MIT Signed-off-by: Jeromy vendor correctly License: MIT Signed-off-by: Jeromy refactor chunking interface a little License: MIT Signed-off-by: Jeromy work chunking interface changes up into importer License: MIT Signed-off-by: Jeromy move chunker type parsing into its own file in chunk License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@96b1fb1dfaa01bab57b0654e1026d78ed6368d23 --- unixfs/mod/dagmodifier.go | 18 +++++++++--------- unixfs/mod/dagmodifier_test.go | 28 +++++++++++++++++----------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 48374c10b..be7d92248 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -40,7 +40,7 @@ type DagModifier struct { curNode *mdag.Node mp pin.ManualPinner - splitter chunk.BlockSplitter + splitter chunk.SplitterGen ctx context.Context readCancel func() @@ -51,7 +51,7 @@ type DagModifier struct { read *uio.DagReader } -func NewDagModifier(ctx context.Context, from *mdag.Node, serv mdag.DAGService, mp pin.ManualPinner, spl chunk.BlockSplitter) (*DagModifier, error) { +func NewDagModifier(ctx context.Context, from *mdag.Node, serv mdag.DAGService, mp pin.ManualPinner, spl chunk.SplitterGen) (*DagModifier, error) { return &DagModifier{ curNode: from.Copy(), dagserv: serv, @@ -106,10 +106,10 @@ func (zr zeroReader) Read(b []byte) (int, error) { // expandSparse grows the file with zero blocks of 4096 // A small blocksize is chosen to aid in deduplication func (dm *DagModifier) expandSparse(size int64) error { - spl := chunk.SizeSplitter{4096} r := io.LimitReader(zeroReader{}, size) - blks := spl.Split(r) - nnode, err := dm.appendData(dm.curNode, blks) + spl := chunk.NewSizeSplitter(r, 4096) + blks, errs := chunk.Chan(spl) + nnode, err := dm.appendData(dm.curNode, blks, errs) if err != nil { return err } @@ -196,8 +196,8 @@ func (dm *DagModifier) Sync() error { // need to write past end of current dag if !done { - blks := dm.splitter.Split(dm.wrBuf) - nd, err = dm.appendData(dm.curNode, blks) + blks, errs := chunk.Chan(dm.splitter(dm.wrBuf)) + nd, err = dm.appendData(dm.curNode, blks, errs) if err != nil { return err } @@ -306,14 +306,14 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) } // appendData appends the blocks from the given chan to the end of this dag -func (dm *DagModifier) appendData(node *mdag.Node, blks <-chan []byte) (*mdag.Node, error) { +func (dm *DagModifier) appendData(node *mdag.Node, blks <-chan []byte, errs <-chan error) (*mdag.Node, error) { dbp := &help.DagBuilderParams{ Dagserv: dm.dagserv, Maxlinks: help.DefaultLinksPerBlock, NodeCB: imp.BasicPinnerCB(dm.mp), } - return trickle.TrickleAppend(node, dbp.New(blks)) + return trickle.TrickleAppend(node, dbp.New(blks, errs)) } // Read data from this dag starting at the current offset diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index e7db0d97d..b4a501dd4 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -53,7 +53,7 @@ func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.Blocksto func getNode(t testing.TB, dserv mdag.DAGService, size int64, pinner pin.ManualPinner) ([]byte, *mdag.Node) { in := io.LimitReader(u.NewTimeSeededRand(), size) - node, err := imp.BuildTrickleDagFromReader(in, dserv, &chunk.SizeSplitter{500}, imp.BasicPinnerCB(pinner)) + node, err := imp.BuildTrickleDagFromReader(dserv, sizeSplitterGen(500)(in), imp.BasicPinnerCB(pinner)) if err != nil { t.Fatal(err) } @@ -117,13 +117,19 @@ func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) return orig } +func sizeSplitterGen(size int64) chunk.SplitterGen { + return func(r io.Reader) chunk.Splitter { + return chunk.NewSizeSplitter(r, size) + } +} + func TestDagModifierBasic(t *testing.T) { dserv, pin := getMockDagServ(t) b, n := getNode(t, dserv, 50000, pin) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pin, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pin, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -178,7 +184,7 @@ func TestMultiWrite(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -231,7 +237,7 @@ func TestMultiWriteAndFlush(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -279,7 +285,7 @@ func TestWriteNewFile(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -322,7 +328,7 @@ func TestMultiWriteCoal(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -368,7 +374,7 @@ func TestLargeWriteChunks(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -406,7 +412,7 @@ func TestDagTruncate(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -437,7 +443,7 @@ func TestSparseWrite(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -491,7 +497,7 @@ func TestCorrectPinning(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -598,7 +604,7 @@ func BenchmarkDagmodWrite(b *testing.B) { wrsize := 4096 - dagmod, err := NewDagModifier(ctx, n, dserv, pins, &chunk.SizeSplitter{Size: 512}) + dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) if err != nil { b.Fatal(err) } From 1f3102fd4af219293690446403e3fbcc63cc572b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 10 Aug 2015 14:44:06 -0700 Subject: [PATCH 0924/3817] randomly getting a bad data layout shouldnt fail the tests License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-chunker@95d023a0b7649622ebddba289ea96b5c62fe00f1 --- chunker/rabin_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 596f2f63e..b4e1b2dc4 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -76,9 +76,6 @@ func TestRabinChunkReuse(t *testing.T) { } if extra > 2 { - t.Fatal("too many spare chunks made") - } - if extra == 2 { - t.Log("why did we get two extra blocks?") + t.Log("too many spare chunks made") } } From e46f8d2b41bb774205cbcdda8367ea3d63be82b5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 10 Aug 2015 17:47:08 -0700 Subject: [PATCH 0925/3817] implement a basic DAG diffing algorithm License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@5af10f580ff477252c4952dbb62be129e3fddfa8 --- ipld/merkledag/utils/diff.go | 152 +++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 ipld/merkledag/utils/diff.go diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go new file mode 100644 index 000000000..7bde3f1f3 --- /dev/null +++ b/ipld/merkledag/utils/diff.go @@ -0,0 +1,152 @@ +package dagutils + +import ( + "bytes" + "fmt" + "path" + + key "github.com/ipfs/go-ipfs/blocks/key" + dag "github.com/ipfs/go-ipfs/merkledag" + context "golang.org/x/net/context" +) + +const ( + Add = iota + Remove + Mod +) + +type Change struct { + Type int + Path string + Before key.Key + After key.Key +} + +func (c *Change) String() string { + switch c.Type { + case Add: + return fmt.Sprintf("Added %s at %s", c.After.B58String()[:6], c.Path) + case Remove: + return fmt.Sprintf("Removed %s from %s", c.Before.B58String()[:6], c.Path) + case Mod: + return fmt.Sprintf("Changed %s to %s at %s", c.Before.B58String()[:6], c.After.B58String()[:6], c.Path) + default: + panic("nope") + } +} + +func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.Node, cs []*Change) (*dag.Node, error) { + e := NewDagEditor(ds, nd) + for _, c := range cs { + switch c.Type { + case Add: + err := e.InsertNodeAtPath(ctx, c.Path, c.After, nil) + if err != nil { + return nil, err + } + + case Remove: + err := e.RmLink(ctx, c.Path) + if err != nil { + return nil, err + } + + case Mod: + err := e.RmLink(ctx, c.Path) + if err != nil { + return nil, err + } + err = e.InsertNodeAtPath(ctx, c.Path, c.After, nil) + if err != nil { + return nil, err + } + } + } + return e.GetNode(), nil +} + +func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) []*Change { + if len(a.Links) == 0 && len(b.Links) == 0 { + ak, _ := a.Key() + bk, _ := b.Key() + return []*Change{ + &Change{ + Type: Mod, + Before: ak, + After: bk, + }, + } + } + + var out []*Change + clean_a := a.Copy() + clean_b := b.Copy() + + // strip out unchanged stuff + for _, lnk := range a.Links { + l, err := b.GetNodeLink(lnk.Name) + if err == nil { + if bytes.Equal(l.Hash, lnk.Hash) { + // no change... ignore it + } else { + anode, _ := lnk.GetNode(ctx, ds) + bnode, _ := l.GetNode(ctx, ds) + sub := Diff(ctx, ds, anode, bnode) + + for _, subc := range sub { + subc.Path = path.Join(lnk.Name, subc.Path) + out = append(out, subc) + } + } + clean_a.RemoveNodeLink(l.Name) + clean_b.RemoveNodeLink(l.Name) + } + } + + for _, lnk := range clean_a.Links { + out = append(out, &Change{ + Type: Remove, + Path: lnk.Name, + Before: key.Key(lnk.Hash), + }) + } + for _, lnk := range clean_b.Links { + out = append(out, &Change{ + Type: Add, + Path: lnk.Name, + After: key.Key(lnk.Hash), + }) + } + + return out +} + +type Conflict struct { + A *Change + B *Change +} + +func MergeDiffs(a, b []*Change) ([]*Change, []Conflict) { + var out []*Change + var conflicts []Conflict + paths := make(map[string]*Change) + for _, c := range a { + paths[c.Path] = c + } + + for _, c := range b { + if ca, ok := paths[c.Path]; ok { + conflicts = append(conflicts, Conflict{ + A: ca, + B: c, + }) + } else { + out = append(out, c) + } + } + for _, c := range paths { + out = append(out, c) + } + return out, conflicts +} From 0832da163ad118c32c566b571fb3aa90bd04711b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 12 Aug 2015 16:08:57 -0700 Subject: [PATCH 0926/3817] use correct context for dht notifs License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@8947f91a963cd8adeb6d3f3e69c02c164fe67a1d --- routing/dht/lookup.go | 7 +++++-- routing/dht/routing.go | 15 +++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index a10073640..76173a615 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -40,9 +40,12 @@ func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key key.Key) (<-chan pe peerset.Add(p) } + // since the query doesnt actually pass our context down + // we have to hack this here. whyrusleeping isnt a huge fan of goprocess + parent := ctx query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { // For DHT query command - notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + notif.PublishQueryEvent(parent, ¬if.QueryEvent{ Type: notif.SendingQuery, ID: p, }) @@ -66,7 +69,7 @@ func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key key.Key) (<-chan pe } // For DHT query command - notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + notif.PublishQueryEvent(parent, ¬if.QueryEvent{ Type: notif.PeerResponse, ID: p, Responses: pointerizePeerInfos(filtered), diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 190e50285..80652f6ad 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -97,8 +97,9 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key key.Key) ([]byte, error) { } // setup the Query + parent := ctx query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { - notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + notif.PublishQueryEvent(parent, ¬if.QueryEvent{ Type: notif.SendingQuery, ID: p, }) @@ -113,7 +114,7 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key key.Key) ([]byte, error) { res.success = true } - notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + notif.PublishQueryEvent(parent, ¬if.QueryEvent{ Type: notif.PeerResponse, ID: p, Responses: pointerizePeerInfos(peers), @@ -209,8 +210,9 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key key.Key, } // setup the Query + parent := ctx query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { - notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + notif.PublishQueryEvent(parent, ¬if.QueryEvent{ Type: notif.SendingQuery, ID: p, }) @@ -246,7 +248,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key key.Key, clpeers := pb.PBPeersToPeerInfos(closer) log.Debugf("got closer peers: %d %s", len(clpeers), clpeers) - notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + notif.PublishQueryEvent(parent, ¬if.QueryEvent{ Type: notif.PeerResponse, ID: p, Responses: pointerizePeerInfos(clpeers), @@ -288,8 +290,9 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, er } // setup the Query + parent := ctx query := dht.newQuery(key.Key(id), func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { - notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + notif.PublishQueryEvent(parent, ¬if.QueryEvent{ Type: notif.SendingQuery, ID: p, }) @@ -312,7 +315,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, er } } - notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + notif.PublishQueryEvent(parent, ¬if.QueryEvent{ Type: notif.PeerResponse, Responses: pointerizePeerInfos(clpeerInfos), }) From ec08f42ec09f6ab5aafb2ec51ec69be739d5fd2a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 14 Aug 2015 16:25:51 -0700 Subject: [PATCH 0927/3817] blockservice.New doesnt need to return an error License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@8c589a001ac660457351a94f4827e0e533dd7710 --- ipld/merkledag/merkledag_test.go | 7 ++----- ipld/merkledag/test/utils.go | 9 ++------- ipld/merkledag/utils/utils_test.go | 4 ++-- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 4a87f6d84..788041646 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -33,10 +33,7 @@ type dagservAndPinner struct { func getDagservAndPinner(t *testing.T) dagservAndPinner { db := dssync.MutexWrap(ds.NewMapDatastore()) bs := bstore.NewBlockstore(db) - blockserv, err := bserv.New(bs, offline.Exchange(bs)) - if err != nil { - t.Fatal(err) - } + blockserv := bserv.New(bs, offline.Exchange(bs)) dserv := NewDAGService(blockserv) mpin := pin.NewPinner(db, dserv).GetManual() return dagservAndPinner{ @@ -159,7 +156,7 @@ func TestBatchFetchDupBlock(t *testing.T) { func runBatchFetchTest(t *testing.T, read io.Reader) { var dagservs []DAGService - for _, bsi := range bstest.Mocks(t, 5) { + for _, bsi := range bstest.Mocks(5) { dagservs = append(dagservs, NewDAGService(bsi)) } diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index 07a1bd1ca..066516e52 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -1,8 +1,6 @@ package mdutils import ( - "testing" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks/blockstore" @@ -11,11 +9,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ) -func Mock(t testing.TB) dag.DAGService { +func Mock() dag.DAGService { bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) - bserv, err := bsrv.New(bstore, offline.Exchange(bstore)) - if err != nil { - t.Fatal(err) - } + bserv := bsrv.New(bstore, offline.Exchange(bstore)) return dag.NewDAGService(bserv) } diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 39b1a519d..b49958d15 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -12,7 +12,7 @@ import ( ) func TestAddLink(t *testing.T) { - ds := mdtest.Mock(t) + ds := mdtest.Mock() fishnode := &dag.Node{ Data: []byte("fishcakes!"), } @@ -66,7 +66,7 @@ func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path stri } func TestInsertNode(t *testing.T) { - ds := mdtest.Mock(t) + ds := mdtest.Mock() root := new(dag.Node) e := NewDagEditor(ds, root) From 783c7aa523dee0b6395e0610a9bea131912f40f3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 14 Aug 2015 16:25:51 -0700 Subject: [PATCH 0928/3817] blockservice.New doesnt need to return an error License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@8f44f5611f9dab8759401b1eebfaaa22ce1223c5 --- unixfs/mod/dagmodifier_test.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index b4a501dd4..475e7c6c4 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -31,10 +31,7 @@ func getMockDagServ(t testing.TB) (mdag.DAGService, pin.ManualPinner) { dstore := ds.NewMapDatastore() tsds := sync.MutexWrap(dstore) bstore := blockstore.NewBlockstore(tsds) - bserv, err := bs.New(bstore, offline.Exchange(bstore)) - if err != nil { - t.Fatal(err) - } + bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) return dserv, pin.NewPinner(tsds, dserv).GetManual() } @@ -43,10 +40,7 @@ func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.Blocksto dstore := ds.NewMapDatastore() tsds := sync.MutexWrap(dstore) bstore := blockstore.NewBlockstore(tsds) - bserv, err := bs.New(bstore, offline.Exchange(bstore)) - if err != nil { - t.Fatal(err) - } + bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) return dserv, bstore, pin.NewPinner(tsds, dserv).GetManual() } From 89f2a9a5e4f9430aec383be9ee3f02d9b754760d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 14 Aug 2015 16:25:51 -0700 Subject: [PATCH 0929/3817] blockservice.New doesnt need to return an error License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@323a2d5474b40c98f4e282e6e8ca79611f332e30 --- blockservice/blockservice.go | 8 ++------ blockservice/test/blocks_test.go | 8 ++------ blockservice/test/mock.go | 12 ++---------- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index eec292b21..bfc6394f1 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -5,7 +5,6 @@ package blockservice import ( "errors" - "fmt" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" @@ -48,10 +47,7 @@ type BlockService struct { } // NewBlockService creates a BlockService with given datastore instance. -func New(bs blockstore.Blockstore, rem exchange.Interface) (*BlockService, error) { - if bs == nil { - return nil, fmt.Errorf("BlockService requires valid blockstore") - } +func New(bs blockstore.Blockstore, rem exchange.Interface) *BlockService { if rem == nil { log.Warning("blockservice running in local (offline) mode.") } @@ -60,7 +56,7 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) (*BlockService, error Blockstore: bs, Exchange: rem, worker: worker.NewWorker(rem, wc), - }, nil + } } // AddBlock adds a particular block to the service, Putting it into the datastore. diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index cb7f665ac..dbbc51562 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -19,11 +19,7 @@ import ( func TestBlocks(t *testing.T) { bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) - bs, err := New(bstore, offline.Exchange(bstore)) - if err != nil { - t.Error("failed to construct block service", err) - return - } + bs := New(bstore, offline.Exchange(bstore)) defer bs.Close() b := blocks.NewBlock([]byte("beep boop")) @@ -63,7 +59,7 @@ func TestBlocks(t *testing.T) { } func TestGetBlocksSequential(t *testing.T) { - var servs = Mocks(t, 4) + var servs = Mocks(4) for _, s := range servs { defer s.Close() } diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index 4c4009de2..28e3a4e99 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -8,12 +8,8 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" ) -type fataler interface { - Fatal(args ...interface{}) -} - // Mocks returns |n| connected mock Blockservices -func Mocks(t fataler, n int) []*BlockService { +func Mocks(n int) []*BlockService { net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(0)) sg := bitswap.NewTestSessionGenerator(net) @@ -21,11 +17,7 @@ func Mocks(t fataler, n int) []*BlockService { var servs []*BlockService for _, i := range instances { - bserv, err := New(i.Blockstore(), i.Exchange) - if err != nil { - t.Fatal(err) - } - servs = append(servs, bserv) + servs = append(servs, New(i.Blockstore(), i.Exchange)) } return servs } From a6739712e3dc4f68a829b3f051031183112cf5f8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 14 Aug 2015 16:25:51 -0700 Subject: [PATCH 0930/3817] blockservice.New doesnt need to return an error License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@5b224ae2c119daafc092824fc7c004f6a81fac61 --- pinning/pinner/pin_test.go | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 3f6c67b54..223beb03e 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -29,10 +29,7 @@ func TestPinnerBasic(t *testing.T) { dstore := dssync.MutexWrap(ds.NewMapDatastore()) bstore := blockstore.NewBlockstore(dstore) - bserv, err := bs.New(bstore, offline.Exchange(bstore)) - if err != nil { - t.Fatal(err) - } + bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) @@ -40,7 +37,7 @@ func TestPinnerBasic(t *testing.T) { p := NewPinner(dstore, dserv) a, ak := randNode() - _, err = dserv.Add(a) + _, err := dserv.Add(a) if err != nil { t.Fatal(err) } @@ -163,10 +160,7 @@ func TestDuplicateSemantics(t *testing.T) { ctx := context.Background() dstore := dssync.MutexWrap(ds.NewMapDatastore()) bstore := blockstore.NewBlockstore(dstore) - bserv, err := bs.New(bstore, offline.Exchange(bstore)) - if err != nil { - t.Fatal(err) - } + bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) @@ -174,7 +168,7 @@ func TestDuplicateSemantics(t *testing.T) { p := NewPinner(dstore, dserv) a, _ := randNode() - _, err = dserv.Add(a) + _, err := dserv.Add(a) if err != nil { t.Fatal(err) } @@ -202,10 +196,7 @@ func TestPinRecursiveFail(t *testing.T) { ctx := context.Background() dstore := dssync.MutexWrap(ds.NewMapDatastore()) bstore := blockstore.NewBlockstore(dstore) - bserv, err := bs.New(bstore, offline.Exchange(bstore)) - if err != nil { - t.Fatal(err) - } + bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) @@ -213,7 +204,7 @@ func TestPinRecursiveFail(t *testing.T) { a, _ := randNode() b, _ := randNode() - err = a.AddNodeLinkClean("child", b) + err := a.AddNodeLinkClean("child", b) if err != nil { t.Fatal(err) } From b1fee816a81183b1a15a0f9f77b47807af16d754 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 14 Aug 2015 16:25:51 -0700 Subject: [PATCH 0931/3817] blockservice.New doesnt need to return an error License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@7f04ac922f4c55bd52b55ce7e1586d35834401e0 --- path/resolver_test.go | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/path/resolver_test.go b/path/resolver_test.go index cb99703a0..c0342fd62 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -4,15 +4,11 @@ import ( "fmt" "testing" - datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" - blockservice "github.com/ipfs/go-ipfs/blockservice" - offline "github.com/ipfs/go-ipfs/exchange/offline" merkledag "github.com/ipfs/go-ipfs/merkledag" + dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" util "github.com/ipfs/go-ipfs/util" ) @@ -27,20 +23,13 @@ func randNode() (*merkledag.Node, key.Key) { func TestRecurivePathResolution(t *testing.T) { ctx := context.Background() - dstore := sync.MutexWrap(datastore.NewMapDatastore()) - bstore := blockstore.NewBlockstore(dstore) - bserv, err := blockservice.New(bstore, offline.Exchange(bstore)) - if err != nil { - t.Fatal(err) - } - - dagService := merkledag.NewDAGService(bserv) + dagService := dagmock.Mock() a, _ := randNode() b, _ := randNode() c, cKey := randNode() - err = b.AddNodeLink("grandchild", c) + err := b.AddNodeLink("grandchild", c) if err != nil { t.Fatal(err) } From 64a293d21cb04ac9cd66231371c711d03b3caeba Mon Sep 17 00:00:00 2001 From: Karthik Bala Date: Fri, 14 Aug 2015 20:02:16 -0700 Subject: [PATCH 0932/3817] Add router that does nothing for bitswap_wo_routing test License: MIT Signed-off-by: Karthik Bala This commit was moved from ipfs/go-ipfs-routing@da5ce28e5c67900d7a2d4b85e426b0f74c6d021f --- routing/none/none_client.go | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 routing/none/none_client.go diff --git a/routing/none/none_client.go b/routing/none/none_client.go new file mode 100644 index 000000000..ce50d7357 --- /dev/null +++ b/routing/none/none_client.go @@ -0,0 +1,51 @@ +package nilrouting + +import ( + "errors" + + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + key "github.com/ipfs/go-ipfs/blocks/key" + p2phost "github.com/ipfs/go-ipfs/p2p/host" + peer "github.com/ipfs/go-ipfs/p2p/peer" + routing "github.com/ipfs/go-ipfs/routing" + u "github.com/ipfs/go-ipfs/util" +) + +var log = u.Logger("mockrouter") + +type nilclient struct { +} + +func (c *nilclient) PutValue(_ context.Context, _ key.Key, _ []byte) error { + return nil +} + +func (c *nilclient) GetValue(_ context.Context, _ key.Key) ([]byte, error) { + return nil, errors.New("Tried GetValue from nil routing.") +} + +func (c *nilclient) FindPeer(_ context.Context, _ peer.ID) (peer.PeerInfo, error) { + return peer.PeerInfo{}, nil +} + +func (c *nilclient) FindProvidersAsync(_ context.Context, _ key.Key, _ int) <-chan peer.PeerInfo { + out := make(chan peer.PeerInfo) + defer close(out) + return out +} + +func (c *nilclient) Provide(_ context.Context, _ key.Key) error { + return nil +} + +func (c *nilclient) Bootstrap(_ context.Context) error { + return nil +} + +func ConstructNilRouting(_ context.Context, _ p2phost.Host, _ ds.ThreadSafeDatastore) (routing.IpfsRouting, error) { + return &nilclient{}, nil +} + +// ensure nilclient satisfies interface +var _ routing.IpfsRouting = &nilclient{} From f3dfce5aa15f1e29ba8b35aa3b38e4839832528b Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 10 Aug 2015 04:05:41 +0700 Subject: [PATCH 0933/3817] Refactor ipfs get License: MIT Signed-off-by: rht This commit was moved from ipfs/go-unixfs@c0c8f9c3a5567564ef3e1284dc615a45e65b5437 --- unixfs/io/dagreader.go | 13 +++--- unixfs/tar/writer.go | 101 ++++++++++++++++++++++------------------- 2 files changed, 61 insertions(+), 53 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index def8c1501..1426f10cc 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -58,8 +58,7 @@ type ReadSeekCloser interface { // node, using the passed in DAGService for data retreival func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (*DagReader, error) { pb := new(ftpb.Data) - err := proto.Unmarshal(n.Data, pb) - if err != nil { + if err := proto.Unmarshal(n.Data, pb); err != nil { return nil, err } @@ -70,7 +69,7 @@ func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (*Dag case ftpb.Data_Raw: fallthrough case ftpb.Data_File: - return newDataFileReader(ctx, n, pb, serv), nil + return NewDataFileReader(ctx, n, pb, serv), nil case ftpb.Data_Metadata: if len(n.Links) == 0 { return nil, errors.New("incorrectly formatted metadata object") @@ -85,7 +84,7 @@ func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (*Dag } } -func newDataFileReader(ctx context.Context, n *mdag.Node, pb *ftpb.Data, serv mdag.DAGService) *DagReader { +func NewDataFileReader(ctx context.Context, n *mdag.Node, pb *ftpb.Data, serv mdag.DAGService) *DagReader { fctx, cancel := context.WithCancel(ctx) promises := serv.GetDAG(fctx, n) return &DagReader{ @@ -124,7 +123,7 @@ func (dr *DagReader) precalcNextBuf(ctx context.Context) error { // A directory should not exist within a file return ft.ErrInvalidDirLocation case ftpb.Data_File: - dr.buf = newDataFileReader(dr.ctx, nxt, pb, dr.serv) + dr.buf = NewDataFileReader(dr.ctx, nxt, pb, dr.serv) return nil case ftpb.Data_Raw: dr.buf = NewRSNCFromBytes(pb.GetData()) @@ -137,8 +136,8 @@ func (dr *DagReader) precalcNextBuf(ctx context.Context) error { } // Size return the total length of the data from the DAG structured file. -func (dr *DagReader) Size() int64 { - return int64(dr.pbdata.GetFilesize()) +func (dr *DagReader) Size() uint64 { + return dr.pbdata.GetFilesize() } // Read reads data from the DAG structured file diff --git a/unixfs/tar/writer.go b/unixfs/tar/writer.go index 125beed96..9e519b368 100644 --- a/unixfs/tar/writer.go +++ b/unixfs/tar/writer.go @@ -4,7 +4,6 @@ import ( "archive/tar" "bufio" "compress/gzip" - "fmt" "io" "path" "time" @@ -13,6 +12,7 @@ import ( cxt "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" mdag "github.com/ipfs/go-ipfs/merkledag" + ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" upb "github.com/ipfs/go-ipfs/unixfs/pb" ) @@ -21,7 +21,8 @@ import ( // TODO: does this need to be configurable? var DefaultBufSize = 1048576 -func DagArchive(ctx cxt.Context, nd *mdag.Node, name string, dag mdag.DAGService, compression int) (io.Reader, error) { +// DagArchive is equivalent to `ipfs getdag $hash | maybe_tar | maybe_gzip` +func DagArchive(ctx cxt.Context, nd *mdag.Node, name string, dag mdag.DAGService, archive bool, compression int) (io.Reader, error) { _, filename := path.Split(name) @@ -31,17 +32,44 @@ func DagArchive(ctx cxt.Context, nd *mdag.Node, name string, dag mdag.DAGService // use a buffered writer to parallelize task bufw := bufio.NewWriterSize(pipew, DefaultBufSize) + // compression determines whether to use gzip compression. + var maybeGzw io.Writer + if compression != gzip.NoCompression { + var err error + maybeGzw, err = gzip.NewWriterLevel(bufw, compression) + if err != nil { + return nil, err + } + } else { + maybeGzw = bufw + } + // construct the tar writer - w, err := NewWriter(bufw, dag, compression) + w, err := NewWriter(ctx, dag, archive, compression, maybeGzw) if err != nil { return nil, err } // write all the nodes recursively go func() { - if err := w.WriteNode(ctx, nd, filename); err != nil { - pipew.CloseWithError(err) - return + if !archive && compression != gzip.NoCompression { + // the case when the node is a file + dagr, err := uio.NewDagReader(w.ctx, nd, w.Dag) + if err != nil { + pipew.CloseWithError(err) + return + } + + if _, err := dagr.WriteTo(maybeGzw); err != nil { + pipew.CloseWithError(err) + return + } + } else { + // the case for 1. archive, and 2. not archived and not compressed, in which tar is used anyway as a transport format + if err := w.WriteNode(nd, filename); err != nil { + pipew.CloseWithError(err) + return + } } if err := bufw.Flush(); err != nil { @@ -49,6 +77,7 @@ func DagArchive(ctx cxt.Context, nd *mdag.Node, name string, dag mdag.DAGService return } + w.Close() pipew.Close() // everything seems to be ok. }() @@ -61,39 +90,32 @@ func DagArchive(ctx cxt.Context, nd *mdag.Node, name string, dag mdag.DAGService type Writer struct { Dag mdag.DAGService TarW *tar.Writer + + ctx cxt.Context } // NewWriter wraps given io.Writer. -// compression determines whether to use gzip compression. -func NewWriter(w io.Writer, dag mdag.DAGService, compression int) (*Writer, error) { - - if compression != gzip.NoCompression { - var err error - w, err = gzip.NewWriterLevel(w, compression) - if err != nil { - return nil, err - } - } - +func NewWriter(ctx cxt.Context, dag mdag.DAGService, archive bool, compression int, w io.Writer) (*Writer, error) { return &Writer{ Dag: dag, TarW: tar.NewWriter(w), + ctx: ctx, }, nil } -func (w *Writer) WriteDir(ctx cxt.Context, nd *mdag.Node, fpath string) error { +func (w *Writer) writeDir(nd *mdag.Node, fpath string) error { if err := writeDirHeader(w.TarW, fpath); err != nil { return err } - for i, ng := range w.Dag.GetDAG(ctx, nd) { - child, err := ng.Get(ctx) + for i, ng := range w.Dag.GetDAG(w.ctx, nd) { + child, err := ng.Get(w.ctx) if err != nil { return err } npath := path.Join(fpath, nd.Links[i].Name) - if err := w.WriteNode(ctx, child, npath); err != nil { + if err := w.WriteNode(child, npath); err != nil { return err } } @@ -101,46 +123,33 @@ func (w *Writer) WriteDir(ctx cxt.Context, nd *mdag.Node, fpath string) error { return nil } -func (w *Writer) WriteFile(ctx cxt.Context, nd *mdag.Node, fpath string) error { - pb := new(upb.Data) - if err := proto.Unmarshal(nd.Data, pb); err != nil { - return err - } - - return w.writeFile(ctx, nd, pb, fpath) -} - -func (w *Writer) writeFile(ctx cxt.Context, nd *mdag.Node, pb *upb.Data, fpath string) error { +func (w *Writer) writeFile(nd *mdag.Node, pb *upb.Data, fpath string) error { if err := writeFileHeader(w.TarW, fpath, pb.GetFilesize()); err != nil { return err } - dagr, err := uio.NewDagReader(ctx, nd, w.Dag) - if err != nil { - return err - } - - _, err = io.Copy(w.TarW, dagr) - if err != nil && err != io.EOF { - return err - } - - return nil + dagr := uio.NewDataFileReader(w.ctx, nd, pb, w.Dag) + _, err := dagr.WriteTo(w.TarW) + return err } -func (w *Writer) WriteNode(ctx cxt.Context, nd *mdag.Node, fpath string) error { +func (w *Writer) WriteNode(nd *mdag.Node, fpath string) error { pb := new(upb.Data) if err := proto.Unmarshal(nd.Data, pb); err != nil { return err } switch pb.GetType() { + case upb.Data_Metadata: + fallthrough case upb.Data_Directory: - return w.WriteDir(ctx, nd, fpath) + return w.writeDir(nd, fpath) + case upb.Data_Raw: + fallthrough case upb.Data_File: - return w.writeFile(ctx, nd, pb, fpath) + return w.writeFile(nd, pb, fpath) default: - return fmt.Errorf("unixfs type not supported: %s", pb.GetType()) + return ft.ErrUnrecognizedType } } From e10d297e14c5afeb187d100d8db3ab7c4c040330 Mon Sep 17 00:00:00 2001 From: rht Date: Thu, 20 Aug 2015 14:53:50 +0700 Subject: [PATCH 0934/3817] Decompose DagArchive from unixfs tar License: MIT Signed-off-by: rht This commit was moved from ipfs/go-unixfs@c3db1bdef60f0e5ee16253540e94dd3092c9c950 --- unixfs/archive/archive.go | 83 ++++++++++++++++++++++++++++++ unixfs/{ => archive}/tar/writer.go | 69 ------------------------- 2 files changed, 83 insertions(+), 69 deletions(-) create mode 100644 unixfs/archive/archive.go rename unixfs/{ => archive}/tar/writer.go (58%) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go new file mode 100644 index 000000000..d530461e7 --- /dev/null +++ b/unixfs/archive/archive.go @@ -0,0 +1,83 @@ +package archive + +import ( + "bufio" + "compress/gzip" + "io" + "path" + + cxt "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + + mdag "github.com/ipfs/go-ipfs/merkledag" + tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" + uio "github.com/ipfs/go-ipfs/unixfs/io" +) + +// DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. +// TODO: does this need to be configurable? +var DefaultBufSize = 1048576 + +// DagArchive is equivalent to `ipfs getdag $hash | maybe_tar | maybe_gzip` +func DagArchive(ctx cxt.Context, nd *mdag.Node, name string, dag mdag.DAGService, archive bool, compression int) (io.Reader, error) { + + _, filename := path.Split(name) + + // need to connect a writer to a reader + piper, pipew := io.Pipe() + + // use a buffered writer to parallelize task + bufw := bufio.NewWriterSize(pipew, DefaultBufSize) + + // compression determines whether to use gzip compression. + var maybeGzw io.Writer + if compression != gzip.NoCompression { + var err error + maybeGzw, err = gzip.NewWriterLevel(bufw, compression) + if err != nil { + return nil, err + } + } else { + maybeGzw = bufw + } + + if !archive && compression != gzip.NoCompression { + // the case when the node is a file + dagr, err := uio.NewDagReader(ctx, nd, dag) + if err != nil { + pipew.CloseWithError(err) + return nil, err + } + + go func() { + if _, err := dagr.WriteTo(maybeGzw); err != nil { + pipew.CloseWithError(err) + return + } + pipew.Close() // everything seems to be ok. + }() + } else { + // the case for 1. archive, and 2. not archived and not compressed, in which tar is used anyway as a transport format + + // construct the tar writer + w, err := tar.NewWriter(ctx, dag, archive, compression, maybeGzw) + if err != nil { + return nil, err + } + + go func() { + // write all the nodes recursively + if err := w.WriteNode(nd, filename); err != nil { + pipew.CloseWithError(err) + return + } + if err := bufw.Flush(); err != nil { + pipew.CloseWithError(err) + return + } + w.Close() + pipew.Close() // everything seems to be ok. + }() + } + + return piper, nil +} diff --git a/unixfs/tar/writer.go b/unixfs/archive/tar/writer.go similarity index 58% rename from unixfs/tar/writer.go rename to unixfs/archive/tar/writer.go index 9e519b368..73aeafa4b 100644 --- a/unixfs/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -2,8 +2,6 @@ package tar import ( "archive/tar" - "bufio" - "compress/gzip" "io" "path" "time" @@ -17,73 +15,6 @@ import ( upb "github.com/ipfs/go-ipfs/unixfs/pb" ) -// DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. -// TODO: does this need to be configurable? -var DefaultBufSize = 1048576 - -// DagArchive is equivalent to `ipfs getdag $hash | maybe_tar | maybe_gzip` -func DagArchive(ctx cxt.Context, nd *mdag.Node, name string, dag mdag.DAGService, archive bool, compression int) (io.Reader, error) { - - _, filename := path.Split(name) - - // need to connect a writer to a reader - piper, pipew := io.Pipe() - - // use a buffered writer to parallelize task - bufw := bufio.NewWriterSize(pipew, DefaultBufSize) - - // compression determines whether to use gzip compression. - var maybeGzw io.Writer - if compression != gzip.NoCompression { - var err error - maybeGzw, err = gzip.NewWriterLevel(bufw, compression) - if err != nil { - return nil, err - } - } else { - maybeGzw = bufw - } - - // construct the tar writer - w, err := NewWriter(ctx, dag, archive, compression, maybeGzw) - if err != nil { - return nil, err - } - - // write all the nodes recursively - go func() { - if !archive && compression != gzip.NoCompression { - // the case when the node is a file - dagr, err := uio.NewDagReader(w.ctx, nd, w.Dag) - if err != nil { - pipew.CloseWithError(err) - return - } - - if _, err := dagr.WriteTo(maybeGzw); err != nil { - pipew.CloseWithError(err) - return - } - } else { - // the case for 1. archive, and 2. not archived and not compressed, in which tar is used anyway as a transport format - if err := w.WriteNode(nd, filename); err != nil { - pipew.CloseWithError(err) - return - } - } - - if err := bufw.Flush(); err != nil { - pipew.CloseWithError(err) - return - } - - w.Close() - pipew.Close() // everything seems to be ok. - }() - - return piper, nil -} - // Writer is a utility structure that helps to write // unixfs merkledag nodes as a tar archive format. // It wraps any io.Writer. From b311f3a53972261bdcd6117246041346a7201b8b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 20 Aug 2015 18:15:59 +0200 Subject: [PATCH 0935/3817] fix master: make vendor blame: @whyrusleeping on ed4274c9b75b513672fa674884a5df2cdb45276c License: MIT Signed-off-by: Juan Batiz-Benet This commit was moved from ipfs/go-merkledag@1912a50a8388c7864ed1f752afb864a678fb3764 --- ipld/merkledag/utils/diff.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 7bde3f1f3..e013becab 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -5,9 +5,9 @@ import ( "fmt" "path" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" - context "golang.org/x/net/context" ) const ( From cff37d5ae90e3cc242e0b42a7eb0201618f6c999 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 16 Aug 2015 18:22:40 +0700 Subject: [PATCH 0936/3817] Make sure ctx in commands are derived from req.Context License: MIT Signed-off-by: rht This commit was moved from ipfs/go-unixfs@cd2ac441342cf67a5c25146592affdf3f5f6e97b --- unixfs/io/dirbuilder.go | 15 ++------------- unixfs/mod/dagmodifier.go | 8 ++++---- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index d1b67c758..6fdef9ffb 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -1,8 +1,6 @@ package io import ( - "time" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" @@ -29,22 +27,13 @@ func NewDirectory(dserv mdag.DAGService) *directoryBuilder { } // AddChild adds a (name, key)-pair to the root node. -func (d *directoryBuilder) AddChild(name string, k key.Key) error { - // TODO(cryptix): consolidate context managment - ctx, cancel := context.WithTimeout(context.TODO(), time.Minute) - defer cancel() - +func (d *directoryBuilder) AddChild(ctx context.Context, name string, k key.Key) error { cnode, err := d.dserv.Get(ctx, k) if err != nil { return err } - err = d.dirnode.AddNodeLinkClean(name, cnode) - if err != nil { - return err - } - - return nil + return d.dirnode.AddNodeLinkClean(name, cnode) } // GetNode returns the root of this directoryBuilder diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index be7d92248..0d0ae7fbe 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -428,7 +428,7 @@ func (dm *DagModifier) Truncate(size int64) error { return dm.expandSparse(int64(size) - realSize) } - nnode, err := dagTruncate(dm.curNode, uint64(size), dm.dagserv) + nnode, err := dagTruncate(dm.ctx, dm.curNode, uint64(size), dm.dagserv) if err != nil { return err } @@ -443,7 +443,7 @@ func (dm *DagModifier) Truncate(size int64) error { } // dagTruncate truncates the given node to 'size' and returns the modified Node -func dagTruncate(nd *mdag.Node, size uint64, ds mdag.DAGService) (*mdag.Node, error) { +func dagTruncate(ctx context.Context, nd *mdag.Node, size uint64, ds mdag.DAGService) (*mdag.Node, error) { if len(nd.Links) == 0 { // TODO: this can likely be done without marshaling and remarshaling pbn, err := ft.FromBytes(nd.Data) @@ -460,7 +460,7 @@ func dagTruncate(nd *mdag.Node, size uint64, ds mdag.DAGService) (*mdag.Node, er var modified *mdag.Node ndata := new(ft.FSNode) for i, lnk := range nd.Links { - ctx, cancel := context.WithTimeout(context.TODO(), time.Minute) + _ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() child, err := lnk.GetNode(ctx, ds) @@ -475,7 +475,7 @@ func dagTruncate(nd *mdag.Node, size uint64, ds mdag.DAGService) (*mdag.Node, er // found the child we want to cut if size < cur+childsize { - nchild, err := dagTruncate(child, size-cur, ds) + nchild, err := dagTruncate(_ctx, child, size-cur, ds) if err != nil { return nil, err } From f67fb97a10123890608d5fb6bfd1f41a009e795b Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 17 Aug 2015 15:40:48 +0700 Subject: [PATCH 0937/3817] Replace WithTimeout with WithCancel whenever possible License: MIT Signed-off-by: rht This commit was moved from ipfs/go-unixfs@4023331d8fd688cdb31020069112f9f6a031178d --- unixfs/mod/dagmodifier.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 0d0ae7fbe..2ed5f9d27 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -5,7 +5,6 @@ import ( "errors" "io" "os" - "time" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" @@ -460,7 +459,7 @@ func dagTruncate(ctx context.Context, nd *mdag.Node, size uint64, ds mdag.DAGSer var modified *mdag.Node ndata := new(ft.FSNode) for i, lnk := range nd.Links { - _ctx, cancel := context.WithTimeout(ctx, time.Minute) + _ctx, cancel := context.WithCancel(ctx) defer cancel() child, err := lnk.GetNode(ctx, ds) From 9f77f01c8295cc03ef27b8cef9cf7c6d30582564 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 16 Aug 2015 18:22:40 +0700 Subject: [PATCH 0938/3817] Make sure ctx in commands are derived from req.Context License: MIT Signed-off-by: rht This commit was moved from ipfs/go-merkledag@44cd2f798293580dbd0c4a637583287ff4f20476 --- ipld/merkledag/utils/utils.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 6ab612c17..a7e87f052 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -48,8 +48,7 @@ func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname s // ensure no link with that name already exists _ = root.RemoveNodeLink(childname) // ignore error, only option is ErrNotFound - err = root.AddNodeLinkClean(childname, childnd) - if err != nil { + if err := root.AddNodeLinkClean(childname, childnd); err != nil { return nil, err } From 4fc790765097c445e2983d976fc7261650706a3d Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 23 Aug 2015 19:33:53 +0700 Subject: [PATCH 0939/3817] Fix 'ctx, _' to have explicit cancel License: MIT Signed-off-by: rht This commit was moved from ipfs/go-blockservice@31d8527e2bd4f930800e0fbb93a0c4b1d040bb60 --- blockservice/test/blocks_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index dbbc51562..c94a2357e 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -42,7 +42,8 @@ func TestBlocks(t *testing.T) { t.Error("returned key is not equal to block key", err) } - ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) + ctx, cancel := context.WithTimeout(context.TODO(), time.Second*5) + defer cancel() b2, err := bs.GetBlock(ctx, b.Key()) if err != nil { t.Error("failed to retrieve block from BlockService", err) @@ -75,7 +76,8 @@ func TestGetBlocksSequential(t *testing.T) { t.Log("one instance at a time, get blocks concurrently") for i := 1; i < len(servs); i++ { - ctx, _ := context.WithTimeout(context.TODO(), time.Second*50) + ctx, cancel := context.WithTimeout(context.TODO(), time.Second*50) + defer cancel() out := servs[i].GetBlocks(ctx, keys) gotten := make(map[key.Key]*blocks.Block) for blk := range out { From 07c94c89dfb4ceb2ed712a399af89e0d725d97ce Mon Sep 17 00:00:00 2001 From: rht Date: Thu, 20 Aug 2015 07:59:52 +0700 Subject: [PATCH 0940/3817] Wire a context down to (n *helpers.UnixfsNode) GetChild License: MIT Signed-off-by: rht This commit was moved from ipfs/go-unixfs@8f176c05ca8478b45e6c00d4e0d29ce59cce9566 --- unixfs/mod/dagmodifier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 2ed5f9d27..6ea761989 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -312,7 +312,7 @@ func (dm *DagModifier) appendData(node *mdag.Node, blks <-chan []byte, errs <-ch NodeCB: imp.BasicPinnerCB(dm.mp), } - return trickle.TrickleAppend(node, dbp.New(blks, errs)) + return trickle.TrickleAppend(dm.ctx, node, dbp.New(blks, errs)) } // Read data from this dag starting at the current offset From 2fe3525158f71046578ca7f1925453ea90645d76 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 23 Aug 2015 19:33:53 +0700 Subject: [PATCH 0941/3817] Fix 'ctx, _' to have explicit cancel License: MIT Signed-off-by: rht This commit was moved from ipfs/go-ipfs-pinner@518bb43f8e21f46209fd415c5cf93a753e478f29 --- pinning/pinner/pin_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 223beb03e..d3947254d 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -210,21 +210,21 @@ func TestPinRecursiveFail(t *testing.T) { } // Note: this isnt a time based test, we expect the pin to fail - mctx, _ := context.WithTimeout(ctx, time.Millisecond) + mctx, cancel := context.WithTimeout(ctx, time.Millisecond) + defer cancel() err = p.Pin(mctx, a, true) if err == nil { t.Fatal("should have failed to pin here") } - _, err = dserv.Add(b) - if err != nil { + if _, err := dserv.Add(b); err != nil { t.Fatal(err) } // this one is time based... but shouldnt cause any issues - mctx, _ = context.WithTimeout(ctx, time.Second) - err = p.Pin(mctx, a, true) - if err != nil { + mctx, cancel = context.WithTimeout(ctx, time.Second) + defer cancel() + if err := p.Pin(mctx, a, true); err != nil { t.Fatal(err) } } From 7fcd12ab610398a8b66dab4894930b0d2eff7bcb Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 17 Aug 2015 15:40:48 +0700 Subject: [PATCH 0942/3817] Replace WithTimeout with WithCancel whenever possible License: MIT Signed-off-by: rht This commit was moved from ipfs/go-path@4e1d0aa2ab7fda8a54a7c02ea1e48c48e40afbf2 --- path/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/resolver.go b/path/resolver.go index 288075000..5740e829e 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -87,7 +87,7 @@ func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]*me } log.Debug("Resolve dag get.") - ctx, cancel := context.WithTimeout(ctx, time.Minute) + ctx, cancel := context.WithCancel(ctx) defer cancel() nd, err := s.DAG.Get(ctx, key.Key(h)) if err != nil { From 55a02fd14319653a2ccd26463bb2b42a6a2fd9f7 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 23 Aug 2015 19:33:53 +0700 Subject: [PATCH 0943/3817] Fix 'ctx, _' to have explicit cancel License: MIT Signed-off-by: rht This commit was moved from ipfs/go-ipfs-routing@d21309d12b9aa34d7572277f856248c510d26b4a --- routing/dht/ext_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index c77116578..75219da5c 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -202,7 +202,8 @@ func TestNotFound(t *testing.T) { } // long timeout to ensure timing is not at play. - ctx, _ = context.WithTimeout(ctx, time.Second*20) + ctx, cancel := context.WithTimeout(ctx, time.Second*20) + defer cancel() v, err := d.GetValue(ctx, key.Key("hello")) log.Debugf("get value got %v", v) if err != nil { @@ -274,7 +275,8 @@ func TestLessThanKResponses(t *testing.T) { }) } - ctx, _ = context.WithTimeout(ctx, time.Second*30) + ctx, cancel := context.WithTimeout(ctx, time.Second*30) + defer cancel() if _, err := d.GetValue(ctx, key.Key("hello")); err != nil { switch err { case routing.ErrNotFound: From 3a4059aae49e643679275cd3a7bf078f56022e98 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 23 Aug 2015 19:33:53 +0700 Subject: [PATCH 0944/3817] Fix 'ctx, _' to have explicit cancel License: MIT Signed-off-by: rht This commit was moved from ipfs/go-namesys@1a54d24b1c0d639636b52272182d2bedc3ca4ab5 --- namesys/publisher.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 3f5e15ae5..e3dd1d81b 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -60,7 +60,8 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Pa log.Debugf("Storing pubkey at: %s", namekey) // Store associated public key - timectx, _ := context.WithDeadline(ctx, time.Now().Add(time.Second*10)) + timectx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Second*10)) + defer cancel() err = p.routing.PutValue(timectx, namekey, pkbytes) if err != nil { return err @@ -70,9 +71,9 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Pa log.Debugf("Storing ipns entry at: %s", ipnskey) // Store ipns entry at "/ipns/"+b58(h(pubkey)) - timectx, _ = context.WithDeadline(ctx, time.Now().Add(time.Second*10)) - err = p.routing.PutValue(timectx, ipnskey, data) - if err != nil { + timectx, cancel = context.WithDeadline(ctx, time.Now().Add(time.Second*10)) + defer cancel() + if err := p.routing.PutValue(timectx, ipnskey, data); err != nil { return err } From 8d52fec1d95df6899f4b2310ae354be10b911fdb Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 23 Aug 2015 19:55:45 +0700 Subject: [PATCH 0945/3817] Replace context.TODO in test files with context.Background License: MIT Signed-off-by: rht This commit was moved from ipfs/go-merkledag@7993a94b6789e563240bae18546ecf9a3b646068 --- ipld/merkledag/merkledag_test.go | 12 ++++++------ ipld/merkledag/utils/utils_test.go | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 788041646..40bc45740 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -155,6 +155,7 @@ func TestBatchFetchDupBlock(t *testing.T) { } func runBatchFetchTest(t *testing.T, read io.Reader) { + ctx := context.Background() var dagservs []DAGService for _, bsi := range bstest.Mocks(5) { dagservs = append(dagservs, NewDAGService(bsi)) @@ -169,7 +170,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { t.Log("finished setup.") - dagr, err := uio.NewDagReader(context.TODO(), root, dagservs[0]) + dagr, err := uio.NewDagReader(ctx, root, dagservs[0]) if err != nil { t.Fatal(err) } @@ -196,13 +197,13 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { wg.Add(1) go func(i int) { defer wg.Done() - first, err := dagservs[i].Get(context.Background(), k) + first, err := dagservs[i].Get(ctx, k) if err != nil { t.Fatal(err) } fmt.Println("Got first node back.") - read, err := uio.NewDagReader(context.TODO(), first, dagservs[i]) + read, err := uio.NewDagReader(ctx, first, dagservs[i]) if err != nil { t.Fatal(err) } @@ -266,8 +267,7 @@ func assertCanGet(t *testing.T, ds DAGService, n *Node) { t.Fatal(err) } - _, err = ds.Get(context.TODO(), k) - if err != nil { + if _, err := ds.Get(context.Background(), k); err != nil { t.Fatal(err) } } @@ -281,7 +281,7 @@ func TestCantGet(t *testing.T) { t.Fatal(err) } - _, err = dsp.ds.Get(context.TODO(), k) + _, err = dsp.ds.Get(context.Background(), k) if !strings.Contains(err.Error(), "not found") { t.Fatal("expected err not found, got: ", err) } diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index b49958d15..b91641267 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -104,7 +104,7 @@ func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr } } - err = e.InsertNodeAtPath(context.TODO(), path, ck, c) + err = e.InsertNodeAtPath(context.Background(), path, ck, c) if experr != "" { var got string if err != nil { From b891ed1fe8deeeead7264f8e50bee00a5a149608 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 23 Aug 2015 19:55:45 +0700 Subject: [PATCH 0946/3817] Replace context.TODO in test files with context.Background License: MIT Signed-off-by: rht This commit was moved from ipfs/go-blockservice@75d6f9ce7aed1ae38a77b599a618aa57b29c6bd8 --- blockservice/test/blocks_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index c94a2357e..6ba5eb40f 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -42,7 +42,7 @@ func TestBlocks(t *testing.T) { t.Error("returned key is not equal to block key", err) } - ctx, cancel := context.WithTimeout(context.TODO(), time.Second*5) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() b2, err := bs.GetBlock(ctx, b.Key()) if err != nil { @@ -76,7 +76,7 @@ func TestGetBlocksSequential(t *testing.T) { t.Log("one instance at a time, get blocks concurrently") for i := 1; i < len(servs); i++ { - ctx, cancel := context.WithTimeout(context.TODO(), time.Second*50) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*50) defer cancel() out := servs[i].GetBlocks(ctx, keys) gotten := make(map[key.Key]*blocks.Block) From a8797639b0ed08de1277786c897351378eb22fca Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 23 Aug 2015 22:12:23 +0700 Subject: [PATCH 0947/3817] Localize the scope of context.WithCancel for every DAG.Get Instead put it inside of DAG.Get. The fix is applied only in the case when the context.WithCancel before a DAG.Get is also used later on in the scope. License: MIT Signed-off-by: rht This commit was moved from ipfs/go-unixfs@ac3a64d8f6f99e23e3041691c5883343bc8ba6ff --- unixfs/mod/dagmodifier.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 6ea761989..8d97bddfc 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -459,9 +459,6 @@ func dagTruncate(ctx context.Context, nd *mdag.Node, size uint64, ds mdag.DAGSer var modified *mdag.Node ndata := new(ft.FSNode) for i, lnk := range nd.Links { - _ctx, cancel := context.WithCancel(ctx) - defer cancel() - child, err := lnk.GetNode(ctx, ds) if err != nil { return nil, err @@ -474,7 +471,7 @@ func dagTruncate(ctx context.Context, nd *mdag.Node, size uint64, ds mdag.DAGSer // found the child we want to cut if size < cur+childsize { - nchild, err := dagTruncate(_ctx, child, size-cur, ds) + nchild, err := dagTruncate(ctx, child, size-cur, ds) if err != nil { return nil, err } From 51d0cdad6461b79578d5ae5246d485425b1ca3b3 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 23 Aug 2015 22:12:23 +0700 Subject: [PATCH 0948/3817] Localize the scope of context.WithCancel for every DAG.Get Instead put it inside of DAG.Get. The fix is applied only in the case when the context.WithCancel before a DAG.Get is also used later on in the scope. License: MIT Signed-off-by: rht This commit was moved from ipfs/go-merkledag@dbcf68e922d78ad43e786216559f87f15298f703 --- ipld/merkledag/merkledag.go | 2 ++ ipld/merkledag/traverse/traverse.go | 6 +----- ipld/merkledag/utils/utils.go | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index bf8dc1310..c93b5a23b 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -93,6 +93,8 @@ func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) { if n == nil { return nil, fmt.Errorf("dagService is nil") } + ctx, cancel := context.WithCancel(ctx) + defer cancel() b, err := n.Blocks.GetBlock(ctx, k) if err != nil { diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index b00307364..aa71ad2f2 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -3,7 +3,6 @@ package traverse import ( "errors" - "time" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" @@ -67,10 +66,7 @@ func (t *traversal) callFunc(next State) error { func (t *traversal) getNode(link *mdag.Link) (*mdag.Node, error) { getNode := func(l *mdag.Link) (*mdag.Node, error) { - ctx, cancel := context.WithTimeout(context.TODO(), time.Minute) - defer cancel() - - next, err := l.GetNode(ctx, t.opts.DAG) + next, err := l.GetNode(context.TODO(), t.opts.DAG) if err != nil { return nil, err } diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index a7e87f052..b073d4bf7 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -52,8 +52,7 @@ func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname s return nil, err } - _, err = ds.Add(root) - if err != nil { + if _, err := ds.Add(root); err != nil { return nil, err } return root, nil From 951bf87090f828039e7dbc52b93c59ca95f39627 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 23 Aug 2015 22:12:23 +0700 Subject: [PATCH 0949/3817] Localize the scope of context.WithCancel for every DAG.Get Instead put it inside of DAG.Get. The fix is applied only in the case when the context.WithCancel before a DAG.Get is also used later on in the scope. License: MIT Signed-off-by: rht This commit was moved from ipfs/go-ipfs-pinner@b1e47218ea129e225f5473f9928e814e24112f01 --- pinning/pinner/pin.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 8f2d4b820..3d51b7a06 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -106,8 +106,7 @@ func (p *pinner) Pin(ctx context.Context, node *mdag.Node, recurse bool) error { p.recursePin.AddBlock(k) } else { - _, err := p.dserv.Get(ctx, k) - if err != nil { + if _, err := p.dserv.Get(ctx, k); err != nil { return err } From e4a4624f0ebcb3990d6195f5bae412d109426909 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 23 Aug 2015 22:12:23 +0700 Subject: [PATCH 0950/3817] Localize the scope of context.WithCancel for every DAG.Get Instead put it inside of DAG.Get. The fix is applied only in the case when the context.WithCancel before a DAG.Get is also used later on in the scope. License: MIT Signed-off-by: rht This commit was moved from ipfs/go-path@30635a80a7c4888d9124e2d623c1b20d0d46f4d7 --- path/resolver.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index 5740e829e..275d2b7d4 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -87,8 +87,6 @@ func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]*me } log.Debug("Resolve dag get.") - ctx, cancel := context.WithCancel(ctx) - defer cancel() nd, err := s.DAG.Get(ctx, key.Key(h)) if err != nil { return nil, err From b105ea43f5d42a6e9bade3b767d24a1347165e37 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 12 Aug 2015 12:17:52 -0700 Subject: [PATCH 0951/3817] implement symlinks in unixfs, first draft License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@7fd695066806137ea299a22d3cb02a42476924ad --- unixfs/format.go | 14 ++++++++++++++ unixfs/io/dagreader.go | 6 ++++++ unixfs/pb/unixfs.pb.go | 5 ++++- unixfs/pb/unixfs.proto | 1 + 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/unixfs/format.go b/unixfs/format.go index b8c0abeb1..c1f82a485 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -77,6 +77,20 @@ func WrapData(b []byte) []byte { return out } +func SymlinkData(path string) []byte { + pbdata := new(pb.Data) + typ := pb.Data_Symlink + pbdata.Data = []byte(path) + pbdata.Type = &typ + + out, err := proto.Marshal(pbdata) + if err != nil { + panic(err) + } + + return out +} + func UnwrapData(data []byte) ([]byte, error) { pbdata := new(pb.Data) err := proto.Unmarshal(data, pbdata) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 1426f10cc..646a69a40 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -17,6 +17,8 @@ import ( var ErrIsDir = errors.New("this dag node is a directory") +var ErrCantReadSymlinks = errors.New("cannot currently read symlinks") + // DagReader provides a way to easily read the data contained in a dag. type DagReader struct { serv mdag.DAGService @@ -79,6 +81,8 @@ func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (*Dag return nil, err } return NewDagReader(ctx, child, serv) + case ftpb.Data_Symlink: + return nil, ErrCantReadSymlinks default: return nil, ft.ErrUnrecognizedType } @@ -130,6 +134,8 @@ func (dr *DagReader) precalcNextBuf(ctx context.Context) error { return nil case ftpb.Data_Metadata: return errors.New("Shouldnt have had metadata object inside file") + case ftpb.Data_Symlink: + return errors.New("shouldnt have had symlink inside file") default: return ft.ErrUnrecognizedType } diff --git a/unixfs/pb/unixfs.pb.go b/unixfs/pb/unixfs.pb.go index c11ffd4d0..074a32998 100644 --- a/unixfs/pb/unixfs.pb.go +++ b/unixfs/pb/unixfs.pb.go @@ -14,7 +14,7 @@ It has these top-level messages: */ package unixfs_pb -import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" +import proto "github.com/gogo/protobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. @@ -28,6 +28,7 @@ const ( Data_Directory Data_DataType = 1 Data_File Data_DataType = 2 Data_Metadata Data_DataType = 3 + Data_Symlink Data_DataType = 4 ) var Data_DataType_name = map[int32]string{ @@ -35,12 +36,14 @@ var Data_DataType_name = map[int32]string{ 1: "Directory", 2: "File", 3: "Metadata", + 4: "Symlink", } var Data_DataType_value = map[string]int32{ "Raw": 0, "Directory": 1, "File": 2, "Metadata": 3, + "Symlink": 4, } func (x Data_DataType) Enum() *Data_DataType { diff --git a/unixfs/pb/unixfs.proto b/unixfs/pb/unixfs.proto index 1450809e4..4a52c3af5 100644 --- a/unixfs/pb/unixfs.proto +++ b/unixfs/pb/unixfs.proto @@ -6,6 +6,7 @@ message Data { Directory = 1; File = 2; Metadata = 3; + Symlink = 4; } required DataType Type = 1; From c4fb4e8f1c970197837a024076286e4214d0b4c1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 30 Aug 2015 22:14:31 -0700 Subject: [PATCH 0952/3817] give ipfs get symlink support License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@c40e6b586a92b9d2e72cbf33925a7378b24559c6 --- unixfs/archive/tar/writer.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 73aeafa4b..4953be90e 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -79,6 +79,8 @@ func (w *Writer) WriteNode(nd *mdag.Node, fpath string) error { fallthrough case upb.Data_File: return w.writeFile(nd, pb, fpath) + case upb.Data_Symlink: + return writeSymlinkHeader(w.TarW, string(pb.GetData()), fpath) default: return ft.ErrUnrecognizedType } @@ -108,3 +110,12 @@ func writeFileHeader(w *tar.Writer, fpath string, size uint64) error { // TODO: set mode, dates, etc. when added to unixFS }) } + +func writeSymlinkHeader(w *tar.Writer, target, fpath string) error { + return w.WriteHeader(&tar.Header{ + Name: fpath, + Linkname: target, + Mode: 0777, + Typeflag: tar.TypeSymlink, + }) +} From 1e23a49bcb98c93709001bfa5be9f81a8726d31d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 2 Sep 2015 10:57:26 -0700 Subject: [PATCH 0953/3817] rm panic License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@c502cd7f99065ac9ad136e90a77de6608f67abe5 --- unixfs/format.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unixfs/format.go b/unixfs/format.go index c1f82a485..9193ddede 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -77,7 +77,7 @@ func WrapData(b []byte) []byte { return out } -func SymlinkData(path string) []byte { +func SymlinkData(path string) ([]byte, error) { pbdata := new(pb.Data) typ := pb.Data_Symlink pbdata.Data = []byte(path) @@ -85,10 +85,10 @@ func SymlinkData(path string) []byte { out, err := proto.Marshal(pbdata) if err != nil { - panic(err) + return nil, err } - return out + return out, nil } func UnwrapData(data []byte) ([]byte, error) { From 44af61eb2921e012053979096a209e6d41a797ec Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 3 Sep 2015 12:54:10 -0700 Subject: [PATCH 0954/3817] fix import path of generated proto files License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@a73473188af3c10a2f217ecf8d4814ee9734398e --- unixfs/pb/unixfs.pb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/pb/unixfs.pb.go b/unixfs/pb/unixfs.pb.go index 074a32998..e89fca29a 100644 --- a/unixfs/pb/unixfs.pb.go +++ b/unixfs/pb/unixfs.pb.go @@ -14,7 +14,7 @@ It has these top-level messages: */ package unixfs_pb -import proto "github.com/gogo/protobuf/proto" +import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. From 7469af0e832a8bb6ad2a55d0f7b2183cb6481761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Thu, 3 Sep 2015 22:01:01 +0200 Subject: [PATCH 0955/3817] fix swaped argument in rabin.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Michael Muré This commit was moved from ipfs/go-ipfs-chunker@5c4dcacd1e5341fcd14bb228cfb67fe6215a6ca8 --- chunker/rabin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/rabin.go b/chunker/rabin.go index de68ae079..ce9b5fc56 100644 --- a/chunker/rabin.go +++ b/chunker/rabin.go @@ -17,7 +17,7 @@ func NewRabin(r io.Reader, avgBlkSize uint64) *Rabin { min := avgBlkSize / 3 max := avgBlkSize + (avgBlkSize / 2) - return NewRabinMinMax(r, avgBlkSize, min, max) + return NewRabinMinMax(r, min, avgBlkSize, max) } func NewRabinMinMax(r io.Reader, min, avg, max uint64) *Rabin { From e5ba2b7d5db45ed26ba7c3074944bd08e3a0058e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 4 Sep 2015 15:41:15 -0700 Subject: [PATCH 0956/3817] fix blockservice error ignorance License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@f819c6c532d679968b67ac6562d95aa78ffb48eb --- blockservice/blockservice.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index bfc6394f1..0b00b4e8c 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -96,19 +96,25 @@ func (s *BlockService) GetBlock(ctx context.Context, k key.Key) (*blocks.Block, block, err := s.Blockstore.Get(k) if err == nil { return block, nil + } + + if err == blockstore.ErrNotFound && s.Exchange != nil { // TODO be careful checking ErrNotFound. If the underlying // implementation changes, this will break. - } else if err == blockstore.ErrNotFound && s.Exchange != nil { log.Debug("Blockservice: Searching bitswap.") blk, err := s.Exchange.GetBlock(ctx, k) if err != nil { return nil, err } return blk, nil - } else { - log.Debug("Blockservice GetBlock: Not found.") + } + + log.Debug("Blockservice GetBlock: Not found.") + if err == blockstore.ErrNotFound { return nil, ErrNotFound } + + return nil, err } // GetBlocks gets a list of blocks asynchronously and returns through From 74427d958143d11bf4a51e6bb9384b109f8044f3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 12 Aug 2015 13:44:23 -0700 Subject: [PATCH 0957/3817] an attempt at making the editor more efficient License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@b7715c8a685636ddfbc04deb6eba360bf7fc8d73 --- ipld/merkledag/utils/utils.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index b073d4bf7..7985edcc0 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -151,3 +151,32 @@ func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []strin return root, nil } + +func (e *Editor) WriteOutputTo(ds dag.DAGService) error { + return copyDag(e.GetNode(), e.ds, ds) +} + +func copyDag(nd *dag.Node, from, to dag.DAGService) error { + _, err := to.Add(nd) + if err != nil { + return err + } + + for _, lnk := range nd.Links { + child, err := lnk.GetNode(context.Background(), from) + if err != nil { + if err == dag.ErrNotFound { + // not found means we didnt modify it, and it should + // already be in the target datastore + continue + } + return err + } + + err = copyDag(child, from, to) + if err != nil { + return err + } + } + return nil +} From bd8788ffef0024df92c4285a4fd0d557d4f5dae1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 14 Aug 2015 15:12:57 -0700 Subject: [PATCH 0958/3817] add-only-hash no longer stores entirety of everything in memory License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@ae63887c3b7e6047de3f867d68e790f85ad83c2d --- ipld/merkledag/utils/utils.go | 19 +++++++------------ ipld/merkledag/utils/utils_test.go | 4 ++-- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 7985edcc0..b8dde47e7 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -6,7 +6,6 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" ) @@ -26,21 +25,17 @@ func (e *Editor) GetNode() *dag.Node { return e.root.Copy() } -func (e *Editor) AddLink(ctx context.Context, childname string, childk key.Key) error { - nd, err := addLink(ctx, e.ds, e.root, childname, childk) - if err != nil { - return err - } - e.root = nd - return nil +func (e *Editor) GetDagService() dag.DAGService { + return e.ds } -func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childk key.Key) (*dag.Node, error) { +func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childnd *dag.Node) (*dag.Node, error) { if childname == "" { return nil, errors.New("cannot create link with no name!") } - childnd, err := ds.Get(ctx, childk) + // ensure that the node we are adding is in the dagservice + _, err := ds.Add(childnd) if err != nil { return nil, err } @@ -58,7 +53,7 @@ func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname s return root, nil } -func (e *Editor) InsertNodeAtPath(ctx context.Context, path string, toinsert key.Key, create func() *dag.Node) error { +func (e *Editor) InsertNodeAtPath(ctx context.Context, path string, toinsert *dag.Node, create func() *dag.Node) error { splpath := strings.Split(path, "/") nd, err := insertNodeAtPath(ctx, e.ds, e.root, splpath, toinsert, create) if err != nil { @@ -68,7 +63,7 @@ func (e *Editor) InsertNodeAtPath(ctx context.Context, path string, toinsert key return nil } -func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert key.Key, create func() *dag.Node) (*dag.Node, error) { +func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert *dag.Node, create func() *dag.Node) (*dag.Node, error) { if len(path) == 1 { return addLink(ctx, ds, root, path[0], toinsert) } diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index b91641267..f41427cf2 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -23,7 +23,7 @@ func TestAddLink(t *testing.T) { } nd := new(dag.Node) - nnode, err := addLink(context.Background(), ds, nd, "fish", fk) + nnode, err := addLink(context.Background(), ds, nd, "fish", fishnode) if err != nil { t.Fatal(err) } @@ -104,7 +104,7 @@ func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr } } - err = e.InsertNodeAtPath(context.Background(), path, ck, c) + err = e.InsertNodeAtPath(context.Background(), path, child, c) if experr != "" { var got string if err != nil { From 4045a662b750e46f15200444cdb4efcabb8978cd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 17 Aug 2015 10:21:18 -0700 Subject: [PATCH 0959/3817] move mem-dag construction to its own function, and actually call WriteOutputTo License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@e2f87ba07426d6bbf829161558d3f60f41b5c2b1 --- blockservice/blockservice.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 0b00b4e8c..40197c5ba 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -104,6 +104,9 @@ func (s *BlockService) GetBlock(ctx context.Context, k key.Key) (*blocks.Block, log.Debug("Blockservice: Searching bitswap.") blk, err := s.Exchange.GetBlock(ctx, k) if err != nil { + if err == blockstore.ErrNotFound { + return nil, ErrNotFound + } return nil, err } return blk, nil From 0cefad138cd49e42e9bf660f9d9e9957a606dc5b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 17 Aug 2015 10:21:18 -0700 Subject: [PATCH 0960/3817] move mem-dag construction to its own function, and actually call WriteOutputTo License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@2ea717433662411088303bfb0554cd29240897cf --- ipld/merkledag/merkledag.go | 3 +++ ipld/merkledag/utils/diff.go | 12 ++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index c93b5a23b..92fc00f92 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -98,6 +98,9 @@ func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) { b, err := n.Blocks.GetBlock(ctx, k) if err != nil { + if err == bserv.ErrNotFound { + return nil, ErrNotFound + } return nil, err } diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index e013becab..47ca5124f 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -41,7 +41,11 @@ func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.Node, cs []*Cha for _, c := range cs { switch c.Type { case Add: - err := e.InsertNodeAtPath(ctx, c.Path, c.After, nil) + child, err := ds.Get(ctx, c.After) + if err != nil { + return nil, err + } + err = e.InsertNodeAtPath(ctx, c.Path, child, nil) if err != nil { return nil, err } @@ -57,7 +61,11 @@ func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.Node, cs []*Cha if err != nil { return nil, err } - err = e.InsertNodeAtPath(ctx, c.Path, c.After, nil) + child, err := ds.Get(ctx, c.After) + if err != nil { + return nil, err + } + err = e.InsertNodeAtPath(ctx, c.Path, child, nil) if err != nil { return nil, err } From d5a27b9ee887f52f2b543b76fc1b3c1264db536b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 8 Sep 2015 21:15:53 -0700 Subject: [PATCH 0961/3817] use new methods from goprocess/context, remove thirdparty/waitable License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@bfa72bb64bd592a9e0c5f2491a81b500e337faa9 --- blockservice/worker/worker.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/worker/worker.go b/blockservice/worker/worker.go index 88cf4c326..88149add3 100644 --- a/blockservice/worker/worker.go +++ b/blockservice/worker/worker.go @@ -7,11 +7,11 @@ import ( "time" process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" + procctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" ratelimit "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" - waitable "github.com/ipfs/go-ipfs/thirdparty/waitable" util "github.com/ipfs/go-ipfs/util" ) @@ -121,7 +121,7 @@ func (w *Worker) start(c Config) { // reads from |workerChan| until w.process closes limiter := ratelimit.NewRateLimiter(w.process, c.NumWorkers) limiter.Go(func(proc process.Process) { - ctx := waitable.Context(proc) // shut down in-progress HasBlock when time to die + ctx := procctx.OnClosingContext(proc) // shut down in-progress HasBlock when time to die for { select { case <-proc.Closing(): From b1f7dcf44c0c55395e07deee0d85b7fef1557403 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 8 Sep 2015 21:15:53 -0700 Subject: [PATCH 0962/3817] use new methods from goprocess/context, remove thirdparty/waitable License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@172cd15e815cdd6c48aea2f98bc3a4b91b1c6e58 --- routing/dht/ext_test.go | 2 +- routing/dht/query.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 75219da5c..710a9afca 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -51,7 +51,7 @@ func TestGetFailures(t *testing.T) { err = merr[0] } - if err != context.DeadlineExceeded && err != context.Canceled { + if err.Error() != "process closing" { t.Fatal("Got different error than we expected", err) } } else { diff --git a/routing/dht/query.go b/routing/dht/query.go index a6c8a14b3..9906e5189 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -85,7 +85,7 @@ type dhtQueryRunner struct { func newQueryRunner(q *dhtQuery) *dhtQueryRunner { proc := process.WithParent(process.Background()) - ctx := ctxproc.WithProcessClosing(context.Background(), proc) + ctx := ctxproc.OnClosingContext(proc) return &dhtQueryRunner{ query: q, peersToQuery: queue.NewChanQueue(ctx, queue.NewXORDistancePQ(q.key)), @@ -210,7 +210,7 @@ func (r *dhtQueryRunner) queryPeer(proc process.Process, p peer.ID) { // ok let's do this! // create a context from our proc. - ctx := ctxproc.WithProcessClosing(context.Background(), proc) + ctx := ctxproc.OnClosingContext(proc) // make sure we do this when we exit defer func() { From f4dc1b43cd0ebec6262cae1fc0329c4901a64847 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 13 Sep 2015 15:41:38 +0700 Subject: [PATCH 0963/3817] Fix t0090 tar&gz unexpected EOF error License: MIT Signed-off-by: rht This commit was moved from ipfs/go-unixfs@b7016fd1d8ac44a19fe3e85d00e64f281525c920 --- unixfs/archive/archive.go | 27 +++++++++++++++++++++++---- unixfs/archive/tar/writer.go | 7 +++++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index d530461e7..e1faf5a33 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -17,6 +17,18 @@ import ( // TODO: does this need to be configurable? var DefaultBufSize = 1048576 +type identityWriteCloser struct { + w io.Writer +} + +func (i *identityWriteCloser) Write(p []byte) (int, error) { + return i.w.Write(p) +} + +func (i *identityWriteCloser) Close() error { + return nil +} + // DagArchive is equivalent to `ipfs getdag $hash | maybe_tar | maybe_gzip` func DagArchive(ctx cxt.Context, nd *mdag.Node, name string, dag mdag.DAGService, archive bool, compression int) (io.Reader, error) { @@ -29,15 +41,16 @@ func DagArchive(ctx cxt.Context, nd *mdag.Node, name string, dag mdag.DAGService bufw := bufio.NewWriterSize(pipew, DefaultBufSize) // compression determines whether to use gzip compression. - var maybeGzw io.Writer + var maybeGzw io.WriteCloser + var err error if compression != gzip.NoCompression { - var err error maybeGzw, err = gzip.NewWriterLevel(bufw, compression) if err != nil { + pipew.CloseWithError(err) return nil, err } } else { - maybeGzw = bufw + maybeGzw = &identityWriteCloser{bufw} } if !archive && compression != gzip.NoCompression { @@ -53,6 +66,11 @@ func DagArchive(ctx cxt.Context, nd *mdag.Node, name string, dag mdag.DAGService pipew.CloseWithError(err) return } + maybeGzw.Close() + if err := bufw.Flush(); err != nil { + pipew.CloseWithError(err) + return + } pipew.Close() // everything seems to be ok. }() } else { @@ -70,11 +88,12 @@ func DagArchive(ctx cxt.Context, nd *mdag.Node, name string, dag mdag.DAGService pipew.CloseWithError(err) return } + w.Close() + maybeGzw.Close() if err := bufw.Flush(); err != nil { pipew.CloseWithError(err) return } - w.Close() pipew.Close() // everything seems to be ok. }() } diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 4953be90e..6536e443e 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -60,8 +60,11 @@ func (w *Writer) writeFile(nd *mdag.Node, pb *upb.Data, fpath string) error { } dagr := uio.NewDataFileReader(w.ctx, nd, pb, w.Dag) - _, err := dagr.WriteTo(w.TarW) - return err + if _, err := dagr.WriteTo(w.TarW); err != nil { + return err + } + w.TarW.Flush() + return nil } func (w *Writer) WriteNode(nd *mdag.Node, fpath string) error { From 8061255874c914c1cd1ba3d5ca2906ecc72bc083 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 14 Sep 2015 17:33:03 -0700 Subject: [PATCH 0964/3817] extract logging License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@8d4960b199f66c42c36d3e055d565c1dc1019813 --- blockstore/blockstore.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 63fa7f9eb..90a3b9264 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -12,10 +12,10 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" - eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) -var log = eventlog.Logger("blockstore") +var log = logging.Logger("blockstore") // BlockPrefix namespaces blockstore datastores var BlockPrefix = ds.NewKey("blocks") From 48f73405a205a2b7e30208644ae2021e3d3ea232 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 14 Sep 2015 17:33:03 -0700 Subject: [PATCH 0965/3817] extract logging License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@99499a8ee424b7a154537bfd3ef42538db90dd52 --- blockservice/blockservice.go | 4 ++-- blockservice/worker/worker.go | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 40197c5ba..3ab6bc1dc 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -12,7 +12,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" worker "github.com/ipfs/go-ipfs/blockservice/worker" exchange "github.com/ipfs/go-ipfs/exchange" - u "github.com/ipfs/go-ipfs/util" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) var wc = worker.Config{ @@ -32,7 +32,7 @@ var wc = worker.Config{ WorkerBufferSize: 0, } -var log = u.Logger("blockservice") +var log = logging.Logger("blockservice") var ErrNotFound = errors.New("blockservice: key not found") // BlockService is a hybrid block datastore. It stores data in a local diff --git a/blockservice/worker/worker.go b/blockservice/worker/worker.go index 88149add3..3b4df2d73 100644 --- a/blockservice/worker/worker.go +++ b/blockservice/worker/worker.go @@ -12,10 +12,11 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" - util "github.com/ipfs/go-ipfs/util" + + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) -var log = util.Logger("blockservice") +var log = logging.Logger("blockservice") var DefaultConfig = Config{ NumWorkers: 1, From ec8b0fb5eb27322f9dc417c67b068be5ffbf2927 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 14 Sep 2015 17:33:03 -0700 Subject: [PATCH 0966/3817] extract logging License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@d8d7cc75847d0aa45da91bad8559346343150cb5 --- routing/dht/dht.go | 6 +++--- routing/dht/pb/message.go | 4 ++-- routing/dht/query.go | 4 ++-- routing/kbucket/table.go | 4 ++-- routing/mock/centralized_client.go | 4 ++-- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 4 ++-- routing/record/record.go | 4 ++-- routing/supernode/client.go | 6 +++--- routing/supernode/proxy/standard.go | 10 +++++----- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 50c4df14c..b5d6d1611 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -18,8 +18,8 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - "github.com/ipfs/go-ipfs/thirdparty/eventlog" u "github.com/ipfs/go-ipfs/util" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" @@ -28,7 +28,7 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) -var log = eventlog.Logger("dht") +var log = logging.Logger("dht") var ProtocolDHT protocol.ID = "/ipfs/dht" @@ -98,7 +98,7 @@ func (dht *IpfsDHT) LocalPeer() peer.ID { } // log returns the dht's logger -func (dht *IpfsDHT) log() eventlog.EventLogger { +func (dht *IpfsDHT) log() logging.EventLogger { return log // TODO rm } diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 24ad1d6f0..d9f6a2073 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -6,10 +6,10 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" inet "github.com/ipfs/go-ipfs/p2p/net" peer "github.com/ipfs/go-ipfs/p2p/peer" - eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) -var log = eventlog.Logger("dht.pb") +var log = logging.Logger("dht.pb") type PeerRoutingInfo struct { peer.PeerInfo diff --git a/routing/dht/query.go b/routing/dht/query.go index 9906e5189..ab4f28492 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -8,10 +8,10 @@ import ( peer "github.com/ipfs/go-ipfs/p2p/peer" queue "github.com/ipfs/go-ipfs/p2p/peer/queue" "github.com/ipfs/go-ipfs/routing" - eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" u "github.com/ipfs/go-ipfs/util" pset "github.com/ipfs/go-ipfs/util/peerset" todoctr "github.com/ipfs/go-ipfs/util/todocounter" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" ctxproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" @@ -77,7 +77,7 @@ type dhtQueryRunner struct { errs u.MultiErr // result errors. maybe should be a map[peer.ID]error rateLimit chan struct{} // processing semaphore - log eventlog.EventLogger + log logging.EventLogger proc process.Process sync.RWMutex diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 87d9c9e3f..45e438635 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -8,10 +8,10 @@ import ( "time" peer "github.com/ipfs/go-ipfs/p2p/peer" - u "github.com/ipfs/go-ipfs/util" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) -var log = u.Logger("table") +var log = logging.Logger("table") // RoutingTable defines the routing table. type RoutingTable struct { diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 6085b69be..9d577c4a3 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -10,11 +10,11 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" - u "github.com/ipfs/go-ipfs/util" "github.com/ipfs/go-ipfs/util/testutil" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) -var log = u.Logger("mockrouter") +var log = logging.Logger("mockrouter") type client struct { datastore ds.Datastore diff --git a/routing/none/none_client.go b/routing/none/none_client.go index ce50d7357..8400e6a3b 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -9,10 +9,10 @@ import ( p2phost "github.com/ipfs/go-ipfs/p2p/host" peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" - u "github.com/ipfs/go-ipfs/util" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) -var log = u.Logger("mockrouter") +var log = logging.Logger("mockrouter") type nilclient struct { } diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 2ef4e5633..7ead5d305 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -13,10 +13,10 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) -var log = eventlog.Logger("offlinerouting") +var log = logging.Logger("offlinerouting") var ErrOffline = errors.New("routing system in offline mode") diff --git a/routing/record/record.go b/routing/record/record.go index 8db5758e0..80da08581 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -9,11 +9,11 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ci "github.com/ipfs/go-ipfs/p2p/crypto" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) var _ = dag.FetchGraph -var log = eventlog.Logger("routing/record") +var log = logging.Logger("routing/record") // MakePutRecord creates and signs a dht record for the given key/value pair func MakePutRecord(sk ci.PrivKey, key key.Key, value []byte, sign bool) (*pb.Record, error) { diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 5269be51b..97d3d70c7 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -14,10 +14,10 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) -var log = eventlog.Logger("supernode") +var log = logging.Logger("supernode") type Client struct { peerhost host.Host @@ -37,7 +37,7 @@ func NewClient(px proxy.Proxy, h host.Host, ps peer.Peerstore, local peer.ID) (* } func (c *Client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan peer.PeerInfo { - ctx = eventlog.ContextWithLoggable(ctx, eventlog.Uuid("findProviders")) + ctx = logging.ContextWithLoggable(ctx, logging.Uuid("findProviders")) defer log.EventBegin(ctx, "findProviders", &k).Done() ch := make(chan peer.PeerInfo) go func() { diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index fe723409e..cba08c2ea 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -12,12 +12,12 @@ import ( peer "github.com/ipfs/go-ipfs/p2p/peer" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" - eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) const ProtocolSNR = "/ipfs/supernoderouting" -var log = eventlog.Logger("supernode/proxy") +var log = logging.Logger("supernode/proxy") type Proxy interface { Bootstrap(context.Context) error @@ -127,7 +127,7 @@ func (px *standard) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.M } func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote peer.ID) (*dhtpb.Message, error) { - e := log.EventBegin(ctx, "sendRoutingRequest", px.Host.ID(), remote, eventlog.Pair("request", m)) + e := log.EventBegin(ctx, "sendRoutingRequest", px.Host.ID(), remote, logging.Pair("request", m)) defer e.Done() if err := px.Host.Connect(ctx, peer.PeerInfo{ID: remote}); err != nil { e.SetError(err) @@ -157,8 +157,8 @@ func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote pe e.SetError(err) return nil, err } - e.Append(eventlog.Pair("response", response)) - e.Append(eventlog.Pair("uuid", eventlog.Uuid("foo"))) + e.Append(logging.Pair("response", response)) + e.Append(logging.Pair("uuid", logging.Uuid("foo"))) return response, nil } From 435c3a8c207b9a0e608a404bdb329e236f175fdc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 14 Sep 2015 17:33:03 -0700 Subject: [PATCH 0967/3817] extract logging License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@d727813200d61a96db438ca77c3b714ba201a600 --- namesys/routing.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/routing.go b/namesys/routing.go index 9ff2a62f6..9946ed560 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -11,10 +11,10 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - u "github.com/ipfs/go-ipfs/util" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) -var log = u.Logger("namesys") +var log = logging.Logger("namesys") // routingResolver implements NSResolver for the main IPFS SFS-like naming type routingResolver struct { From 270cc9d80909aafe8958412f68d179da21df6cf3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 14 Sep 2015 17:33:03 -0700 Subject: [PATCH 0968/3817] extract logging License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@5bffe51eaf52da0185b430bf7a48d5782e9269e0 --- ipld/merkledag/merkledag.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 92fc00f92..4eae02290 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -9,10 +9,10 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" - u "github.com/ipfs/go-ipfs/util" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) -var log = u.Logger("merkledag") +var log = logging.Logger("merkledag") var ErrNotFound = fmt.Errorf("merkledag: not found") // DAGService is an IPFS Merkle DAG service. From 5929397fbc48e56a3c0ba00bb9cb2a2c05f4a09e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 14 Sep 2015 17:33:03 -0700 Subject: [PATCH 0969/3817] extract logging License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@533f3c94bd983cda8cbfe71baeb613ae7ce9eaae --- pinning/pinner/pin.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 3d51b7a06..a82b93f82 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -14,10 +14,10 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" - "github.com/ipfs/go-ipfs/util" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) -var log = util.Logger("pin") +var log = logging.Logger("pin") var recursePinDatastoreKey = ds.NewKey("/local/pins/recursive/keys") var directPinDatastoreKey = ds.NewKey("/local/pins/direct/keys") var indirectPinDatastoreKey = ds.NewKey("/local/pins/indirect/keys") From b08197413f1eabfb72569219fdd0c3d0682df915 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 14 Sep 2015 17:33:03 -0700 Subject: [PATCH 0970/3817] extract logging License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-chunker@0575a776ab025b83ae248b6b5b0bafbe4d715ca6 --- chunker/splitting.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 960947245..31b5c03e5 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -4,10 +4,10 @@ package chunk import ( "io" - "github.com/ipfs/go-ipfs/util" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) -var log = util.Logger("chunk") +var log = logging.Logger("chunk") var DefaultBlockSize int64 = 1024 * 256 From 5809b1a6cd60273b2e9a2c4fc6cc105192d90284 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 14 Sep 2015 17:33:03 -0700 Subject: [PATCH 0971/3817] extract logging License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@7da593b685ad8247f66d4113fd1cdf06ece77bfd --- path/resolver.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index 275d2b7d4..6c3ae8a97 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -11,10 +11,10 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" merkledag "github.com/ipfs/go-ipfs/merkledag" - u "github.com/ipfs/go-ipfs/util" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) -var log = u.Logger("path") +var log = logging.Logger("path") // Paths after a protocol must contain at least one component var ErrNoComponents = errors.New( From ce001c13f0d235b607ed069fb254c17bc4a06fce Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 14 Sep 2015 17:33:03 -0700 Subject: [PATCH 0972/3817] extract logging License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@78d19b2f8462c805d6e0d3d1b8d79e2d21b113fc --- unixfs/mod/dagmodifier.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 8d97bddfc..c39714398 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -19,7 +19,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - u "github.com/ipfs/go-ipfs/util" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) var ErrSeekFail = errors.New("failed to seek properly") @@ -29,7 +29,7 @@ var ErrUnrecognizedWhence = errors.New("unrecognized whence") // 2MB var writebufferSize = 1 << 21 -var log = u.Logger("dagio") +var log = logging.Logger("dagio") // DagModifier is the only struct licensed and able to correctly // perform surgery on a DAG 'file' From 52c3ad958f36dc97dad84737c61448819c99d42b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 15 Sep 2015 17:15:29 -0700 Subject: [PATCH 0973/3817] update go-datastore to latest License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@75385f222d2ee272285637d105ea55da893620d9 --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 90a3b9264..5558bf3b2 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -43,7 +43,7 @@ func NewBlockstore(d ds.ThreadSafeDatastore) Blockstore { } type blockstore struct { - datastore ds.BatchingDatastore + datastore ds.Batching // cant be ThreadSafeDatastore cause namespace.Datastore doesnt support it. // we do check it on `NewBlockstore` though. } From c3fec930813e94038a22f9eb786b6934a514ec8c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 28 Aug 2015 14:20:37 -0700 Subject: [PATCH 0974/3817] dont need blockservice workers anymore License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@418787d00cfda152388894182f5abc7bf8186acf --- blockservice/blockservice.go | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 3ab6bc1dc..7cd8f2875 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -10,29 +10,12 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" - worker "github.com/ipfs/go-ipfs/blockservice/worker" exchange "github.com/ipfs/go-ipfs/exchange" logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) -var wc = worker.Config{ - // When running on a single core, NumWorkers has a harsh negative effect on - // throughput. (-80% when < 25) - // Running a lot more workers appears to have very little effect on both - // single and multicore configurations. - NumWorkers: 25, - - // These have no effect on when running on multiple cores, but harsh - // negative effect on throughput when running on a single core - // On multicore configurations these buffers have little effect on - // throughput. - // On single core configurations, larger buffers have severe adverse - // effects on throughput. - ClientBufferSize: 0, - WorkerBufferSize: 0, -} - var log = logging.Logger("blockservice") + var ErrNotFound = errors.New("blockservice: key not found") // BlockService is a hybrid block datastore. It stores data in a local @@ -42,8 +25,6 @@ type BlockService struct { // TODO don't expose underlying impl details Blockstore blockstore.Blockstore Exchange exchange.Interface - - worker *worker.Worker } // NewBlockService creates a BlockService with given datastore instance. @@ -55,7 +36,6 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) *BlockService { return &BlockService{ Blockstore: bs, Exchange: rem, - worker: worker.NewWorker(rem, wc), } } @@ -67,7 +47,7 @@ func (s *BlockService) AddBlock(b *blocks.Block) (key.Key, error) { if err != nil { return k, err } - if err := s.worker.HasBlock(b); err != nil { + if err := s.Exchange.HasBlock(context.TODO(), b); err != nil { return "", errors.New("blockservice is closed") } return k, nil @@ -81,7 +61,7 @@ func (s *BlockService) AddBlocks(bs []*blocks.Block) ([]key.Key, error) { var ks []key.Key for _, b := range bs { - if err := s.worker.HasBlock(b); err != nil { + if err := s.Exchange.HasBlock(context.TODO(), b); err != nil { return nil, errors.New("blockservice is closed") } ks = append(ks, b.Key()) @@ -166,5 +146,5 @@ func (s *BlockService) DeleteBlock(k key.Key) error { func (s *BlockService) Close() error { log.Debug("blockservice is shutting down...") - return s.worker.Close() + return s.Exchange.Close() } From d72ee3e6128dcc35fd0eb5bc98dac8ed333a6b7c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 2 Sep 2015 14:44:04 -0700 Subject: [PATCH 0975/3817] remove context from HasBlock, use bitswap process instead License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@63a223c1bb558f5302f4af7de695db4a5f51aad5 --- blockservice/blockservice.go | 4 +- blockservice/worker/bench/main.go | 91 ----------- blockservice/worker/bench_worker_test.go | 42 ------ blockservice/worker/worker.go | 184 ----------------------- blockservice/worker/worker_test.go | 63 -------- 5 files changed, 2 insertions(+), 382 deletions(-) delete mode 100644 blockservice/worker/bench/main.go delete mode 100644 blockservice/worker/bench_worker_test.go delete mode 100644 blockservice/worker/worker.go delete mode 100644 blockservice/worker/worker_test.go diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 7cd8f2875..f13c090e4 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -47,7 +47,7 @@ func (s *BlockService) AddBlock(b *blocks.Block) (key.Key, error) { if err != nil { return k, err } - if err := s.Exchange.HasBlock(context.TODO(), b); err != nil { + if err := s.Exchange.HasBlock(b); err != nil { return "", errors.New("blockservice is closed") } return k, nil @@ -61,7 +61,7 @@ func (s *BlockService) AddBlocks(bs []*blocks.Block) ([]key.Key, error) { var ks []key.Key for _, b := range bs { - if err := s.Exchange.HasBlock(context.TODO(), b); err != nil { + if err := s.Exchange.HasBlock(b); err != nil { return nil, errors.New("blockservice is closed") } ks = append(ks, b.Key()) diff --git a/blockservice/worker/bench/main.go b/blockservice/worker/bench/main.go deleted file mode 100644 index c23770f78..000000000 --- a/blockservice/worker/bench/main.go +++ /dev/null @@ -1,91 +0,0 @@ -/* -Benchmark github.com/ipfs/go-ipfs/blockservice/worker. - -Loop over a range of workers and buffer sizes and measure the time it -per block-transfer operation for each value. Run with: - - $ go run "${GOPATH}/src/github.com/ipfs/go-ipfs/blockservice/worker/bench/main.go" -*/ - -package main - -import ( - "log" - "math" - "testing" - "time" - - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - ds_sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - blocks "github.com/ipfs/go-ipfs/blocks" - blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" - worker "github.com/ipfs/go-ipfs/blockservice/worker" - "github.com/ipfs/go-ipfs/exchange/offline" - "github.com/ipfs/go-ipfs/thirdparty/delay" - "github.com/ipfs/go-ipfs/util/datastore2" -) - -const kEstRoutingDelay = time.Second - -const kBlocksPerOp = 100 - -func main() { - var bestConfig worker.Config - var quickestNsPerOp int64 = math.MaxInt64 - for NumWorkers := 1; NumWorkers < 10; NumWorkers++ { - for ClientBufferSize := 0; ClientBufferSize < 10; ClientBufferSize++ { - for WorkerBufferSize := 0; WorkerBufferSize < 10; WorkerBufferSize++ { - c := worker.Config{ - NumWorkers: NumWorkers, - ClientBufferSize: ClientBufferSize, - WorkerBufferSize: WorkerBufferSize, - } - result := testing.Benchmark(BenchmarkWithConfig(c)) - if result.NsPerOp() < quickestNsPerOp { - bestConfig = c - quickestNsPerOp = result.NsPerOp() - } - log.Printf("benched %+v \t result: %+v", c, result) - } - } - } - log.Println(bestConfig) -} - -func BenchmarkWithConfig(c worker.Config) func(b *testing.B) { - return func(b *testing.B) { - - routingDelay := delay.Fixed(0) // during setup - - dstore := ds_sync.MutexWrap(datastore2.WithDelay(ds.NewMapDatastore(), routingDelay)) - bstore := blockstore.NewBlockstore(dstore) - var testdata []*blocks.Block - var i int64 - for i = 0; i < kBlocksPerOp; i++ { - testdata = append(testdata, blocks.NewBlock([]byte(string(i)))) - } - b.ResetTimer() - b.SetBytes(kBlocksPerOp) - for i := 0; i < b.N; i++ { - - b.StopTimer() - w := worker.NewWorker(offline.Exchange(bstore), c) - b.StartTimer() - - prev := routingDelay.Set(kEstRoutingDelay) // during measured section - - for _, block := range testdata { - if err := w.HasBlock(block); err != nil { - b.Fatal(err) - } - } - - routingDelay.Set(prev) // to hasten the unmeasured close period - - b.StopTimer() - w.Close() - b.StartTimer() - - } - } -} diff --git a/blockservice/worker/bench_worker_test.go b/blockservice/worker/bench_worker_test.go deleted file mode 100644 index a5e34a107..000000000 --- a/blockservice/worker/bench_worker_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package worker - -import ( - "testing" - - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" - blocks "github.com/ipfs/go-ipfs/blocks" - blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" - "github.com/ipfs/go-ipfs/exchange/offline" -) - -func BenchmarkHandle10KBlocks(b *testing.B) { - bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) - var testdata []*blocks.Block - for i := 0; i < 10000; i++ { - testdata = append(testdata, blocks.NewBlock([]byte(string(i)))) - } - b.ResetTimer() - b.SetBytes(10000) - for i := 0; i < b.N; i++ { - - b.StopTimer() - w := NewWorker(offline.Exchange(bstore), Config{ - NumWorkers: 1, - ClientBufferSize: 0, - WorkerBufferSize: 0, - }) - b.StartTimer() - - for _, block := range testdata { - if err := w.HasBlock(block); err != nil { - b.Fatal(err) - } - } - - b.StopTimer() - w.Close() - b.StartTimer() - - } -} diff --git a/blockservice/worker/worker.go b/blockservice/worker/worker.go deleted file mode 100644 index 3b4df2d73..000000000 --- a/blockservice/worker/worker.go +++ /dev/null @@ -1,184 +0,0 @@ -// TODO FIXME name me -package worker - -import ( - "container/list" - "errors" - "time" - - process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" - procctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" - ratelimit "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit" - blocks "github.com/ipfs/go-ipfs/blocks" - key "github.com/ipfs/go-ipfs/blocks/key" - exchange "github.com/ipfs/go-ipfs/exchange" - - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" -) - -var log = logging.Logger("blockservice") - -var DefaultConfig = Config{ - NumWorkers: 1, - ClientBufferSize: 0, - WorkerBufferSize: 0, -} - -type Config struct { - // NumWorkers sets the number of background workers that provide blocks to - // the exchange. - NumWorkers int - - // ClientBufferSize allows clients of HasBlock to send up to - // |ClientBufferSize| blocks without blocking. - ClientBufferSize int - - // WorkerBufferSize can be used in conjunction with NumWorkers to reduce - // communication-coordination within the worker. - WorkerBufferSize int -} - -// TODO FIXME name me -type Worker struct { - // added accepts blocks from client - added chan *blocks.Block - exchange exchange.Interface - - // workQueue is owned by the client worker - // process manages life-cycle - process process.Process -} - -func NewWorker(e exchange.Interface, c Config) *Worker { - if c.NumWorkers < 1 { - c.NumWorkers = 1 // provide a sane default - } - w := &Worker{ - exchange: e, - added: make(chan *blocks.Block, c.ClientBufferSize), - process: process.WithParent(process.Background()), // internal management - } - w.start(c) - return w -} - -func (w *Worker) HasBlock(b *blocks.Block) error { - select { - case <-w.process.Closed(): - return errors.New("blockservice worker is closed") - case w.added <- b: - return nil - } -} - -func (w *Worker) Close() error { - log.Debug("blockservice provide worker is shutting down...") - return w.process.Close() -} - -func (w *Worker) start(c Config) { - - workerChan := make(chan *blocks.Block, c.WorkerBufferSize) - - // clientWorker handles incoming blocks from |w.added| and sends to - // |workerChan|. This will never block the client. - w.process.Go(func(proc process.Process) { - defer close(workerChan) - - var workQueue BlockList - debugInfo := time.NewTicker(5 * time.Second) - defer debugInfo.Stop() - for { - - // take advantage of the fact that sending on nil channel always - // blocks so that a message is only sent if a block exists - sendToWorker := workerChan - nextBlock := workQueue.Pop() - if nextBlock == nil { - sendToWorker = nil - } - - select { - - // if worker is ready and there's a block to process, send the - // block - case sendToWorker <- nextBlock: - case <-debugInfo.C: - if workQueue.Len() > 0 { - log.Debugf("%d blocks in blockservice provide queue...", workQueue.Len()) - } - case block := <-w.added: - if nextBlock != nil { - workQueue.Push(nextBlock) // missed the chance to send it - } - // if the client sends another block, add it to the queue. - workQueue.Push(block) - case <-proc.Closing(): - return - } - } - }) - - // reads from |workerChan| until w.process closes - limiter := ratelimit.NewRateLimiter(w.process, c.NumWorkers) - limiter.Go(func(proc process.Process) { - ctx := procctx.OnClosingContext(proc) // shut down in-progress HasBlock when time to die - for { - select { - case <-proc.Closing(): - return - case block, ok := <-workerChan: - if !ok { - return - } - limiter.LimitedGo(func(proc process.Process) { - if err := w.exchange.HasBlock(ctx, block); err != nil { - log.Infof("blockservice worker error: %s", err) - } - }) - } - } - }) -} - -type BlockList struct { - list list.List - uniques map[key.Key]*list.Element -} - -func (s *BlockList) PushFront(b *blocks.Block) { - if s.uniques == nil { - s.uniques = make(map[key.Key]*list.Element) - } - _, ok := s.uniques[b.Key()] - if !ok { - e := s.list.PushFront(b) - s.uniques[b.Key()] = e - } -} - -func (s *BlockList) Push(b *blocks.Block) { - if s.uniques == nil { - s.uniques = make(map[key.Key]*list.Element) - } - _, ok := s.uniques[b.Key()] - if !ok { - e := s.list.PushBack(b) - s.uniques[b.Key()] = e - } -} - -func (s *BlockList) Pop() *blocks.Block { - if s.list.Len() == 0 { - return nil - } - e := s.list.Front() - s.list.Remove(e) - b := e.Value.(*blocks.Block) - delete(s.uniques, b.Key()) - return b -} - -func (s *BlockList) Len() int { - return s.list.Len() -} diff --git a/blockservice/worker/worker_test.go b/blockservice/worker/worker_test.go deleted file mode 100644 index 2b6a2d16f..000000000 --- a/blockservice/worker/worker_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package worker - -import ( - blocks "github.com/ipfs/go-ipfs/blocks" - "testing" -) - -func TestStartClose(t *testing.T) { - numRuns := 50 - if testing.Short() { - numRuns = 5 - } - for i := 0; i < numRuns; i++ { - w := NewWorker(nil, DefaultConfig) - w.Close() - } -} - -func TestQueueDeduplication(t *testing.T) { - numUniqBlocks := 5 // arbitrary - - var firstBatch []*blocks.Block - for i := 0; i < numUniqBlocks; i++ { - firstBatch = append(firstBatch, blockFromInt(i)) - } - - // to get different pointer values and prevent the implementation from - // cheating. The impl must check equality using Key. - var secondBatch []*blocks.Block - for i := 0; i < numUniqBlocks; i++ { - secondBatch = append(secondBatch, blockFromInt(i)) - } - var workQueue BlockList - - for _, b := range append(firstBatch, secondBatch...) { - workQueue.Push(b) - } - for i := 0; i < numUniqBlocks; i++ { - b := workQueue.Pop() - if b.Key() != firstBatch[i].Key() { - t.Fatal("list is not FIFO") - } - } - if b := workQueue.Pop(); b != nil { - t.Fatal("the workQueue did not de-duplicate the blocks") - } -} - -func TestPushPopPushPop(t *testing.T) { - var workQueue BlockList - orig := blockFromInt(1) - dup := blockFromInt(1) - workQueue.PushFront(orig) - workQueue.Pop() - workQueue.Push(dup) - if workQueue.Len() != 1 { - t.Fatal("the block list's internal state is corrupt") - } -} - -func blockFromInt(i int) *blocks.Block { - return blocks.NewBlock([]byte(string(i))) -} From 6afcf195ded403e383e3047be41cefb5404079b3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 2 Sep 2015 14:44:04 -0700 Subject: [PATCH 0976/3817] remove context from HasBlock, use bitswap process instead License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@91e4d27b8584e59ee551c541b32ac80bef321209 --- exchange/offline/offline.go | 2 +- exchange/offline/offline_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 9cf125ce0..9a448906e 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -28,7 +28,7 @@ func (e *offlineExchange) GetBlock(_ context.Context, k key.Key) (*blocks.Block, } // HasBlock always returns nil. -func (e *offlineExchange) HasBlock(_ context.Context, b *blocks.Block) error { +func (e *offlineExchange) HasBlock(b *blocks.Block) error { return e.bs.Put(b) } diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 41e8bb216..dc0071606 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -26,7 +26,7 @@ func TestHasBlockReturnsNil(t *testing.T) { ex := Exchange(store) block := blocks.NewBlock([]byte("data")) - err := ex.HasBlock(context.Background(), block) + err := ex.HasBlock(block) if err != nil { t.Fail() } @@ -44,7 +44,7 @@ func TestGetBlocks(t *testing.T) { expected := g.Blocks(2) for _, b := range expected { - if err := ex.HasBlock(context.Background(), b); err != nil { + if err := ex.HasBlock(b); err != nil { t.Fail() } } From 39fa1d2d02a7dbf73e8993070ca856c923983322 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 2 Sep 2015 14:44:04 -0700 Subject: [PATCH 0977/3817] remove context from HasBlock, use bitswap process instead License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-interface@83459c0f9b4bae2f3c72b7201a2bf4f2006f22e6 --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index 81ae3483a..1a149ed9d 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -19,7 +19,7 @@ type Interface interface { // TODO Should callers be concerned with whether the block was made // available on the network? - HasBlock(context.Context, *blocks.Block) error + HasBlock(*blocks.Block) error io.Closer } From 16a6f04f7985597878a4f37d154b4a3e726450a9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 18 Sep 2015 10:27:55 -0700 Subject: [PATCH 0978/3817] ipns record selection via sequence numbers This commit adds a sequence number to the IpnsEntry protobuf that is used to determine which among a set of entries for the same key is the 'most correct'. GetValues has been added to the routing interface to retrieve a set of records from the dht, for the caller to select from. GetValue (singular) will call GetValues, select the 'best' record, and then update that record to peers we received outdated records from. This will help keep the dht consistent. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@b47ab797fa01ed79aaa0180afbfc8cec944fb8da --- routing/dht/dht.go | 17 +++-- routing/dht/dht_test.go | 12 +++- routing/dht/handlers.go | 2 +- routing/dht/routing.go | 102 ++++++++++++++++++++++++----- routing/mock/centralized_client.go | 15 +++++ routing/none/none_client.go | 4 ++ routing/offline/offline.go | 21 ++++++ routing/record/record.go | 2 - routing/record/selection.go | 40 +++++++++++ routing/routing.go | 19 ++++++ routing/supernode/client.go | 16 +++++ 11 files changed, 220 insertions(+), 30 deletions(-) create mode 100644 routing/record/selection.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index b5d6d1611..64f28c74e 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -54,6 +54,7 @@ type IpfsDHT struct { diaglock sync.Mutex // lock to make diagnostics work better Validator record.Validator // record validator funcs + Selector record.Selector // record selection funcs ctx context.Context proc goprocess.Process @@ -89,6 +90,9 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip dht.Validator = make(record.Validator) dht.Validator["pk"] = record.PublicKeyValidator + dht.Selector = make(record.Selector) + dht.Selector["pk"] = record.PublicKeySelector + return dht } @@ -152,13 +156,16 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, skey string) err // NOTE: it will update the dht's peerstore with any new addresses // it finds for the given peer. func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.ID, - key key.Key) ([]byte, []peer.PeerInfo, error) { + key key.Key) (*pb.Record, []peer.PeerInfo, error) { pmes, err := dht.getValueSingle(ctx, p, key) if err != nil { return nil, nil, err } + // Perhaps we were given closer peers + peers := pb.PBPeersToPeerInfos(pmes.GetCloserPeers()) + if record := pmes.GetRecord(); record != nil { // Success! We were given the value log.Debug("getValueOrPeers: got value") @@ -169,11 +176,9 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.ID, log.Info("Received invalid record! (discarded)") return nil, nil, err } - return record.GetValue(), nil, nil + return record, peers, nil } - // Perhaps we were given closer peers - peers := pb.PBPeersToPeerInfos(pmes.GetCloserPeers()) if len(peers) > 0 { log.Debug("getValueOrPeers: peers") return nil, peers, nil @@ -193,7 +198,7 @@ func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.ID, } // getLocal attempts to retrieve the value from the datastore -func (dht *IpfsDHT) getLocal(key key.Key) ([]byte, error) { +func (dht *IpfsDHT) getLocal(key key.Key) (*pb.Record, error) { log.Debug("getLocal %s", key) v, err := dht.datastore.Get(key.DsKey()) @@ -221,7 +226,7 @@ func (dht *IpfsDHT) getLocal(key key.Key) ([]byte, error) { } } - return rec.GetValue(), nil + return rec, nil } // getOwnPrivateKey attempts to load the local peers private diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 2e63e438e..c09871610 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -131,8 +131,14 @@ func TestValueGetSet(t *testing.T) { }, Sign: false, } + nulsel := func(_ key.Key, bs [][]byte) (int, error) { + return 0, nil + } + dhtA.Validator["v"] = vf dhtB.Validator["v"] = vf + dhtA.Selector["v"] = nulsel + dhtB.Selector["v"] = nulsel connect(t, ctx, dhtA, dhtB) @@ -193,7 +199,7 @@ func TestProvides(t *testing.T) { if err != nil { t.Fatal(err) } - if !bytes.Equal(bits, v) { + if !bytes.Equal(bits.GetValue(), v) { t.Fatal("didn't store the right bits (%s, %s)", k, v) } } @@ -466,7 +472,7 @@ func TestProvidesMany(t *testing.T) { if err != nil { t.Fatal(err) } - if !bytes.Equal(bits, v) { + if !bytes.Equal(bits.GetValue(), v) { t.Fatal("didn't store the right bits (%s, %s)", k, v) } @@ -558,7 +564,7 @@ func TestProvidesAsync(t *testing.T) { } bits, err := dhts[3].getLocal(k) - if err != nil && bytes.Equal(bits, val) { + if err != nil && bytes.Equal(bits.GetValue(), val) { t.Fatal(err) } diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 36a92a251..1c5d2b1c1 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -108,7 +108,7 @@ func (dht *IpfsDHT) handlePutValue(ctx context.Context, p peer.ID, pmes *pb.Mess dskey := key.Key(pmes.GetKey()).DsKey() if err := dht.verifyRecordLocally(pmes.GetRecord()); err != nil { - log.Debugf("Bad dht record in PUT from: %s. %s", key.Key(pmes.GetRecord().GetAuthor()), err) + log.Warningf("Bad dht record in PUT from: %s. %s", key.Key(pmes.GetRecord().GetAuthor()), err) return nil, err } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 80652f6ad..d5854155f 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -1,6 +1,7 @@ package dht import ( + "bytes" "sync" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" @@ -76,16 +77,71 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key key.Key, value []byte) err } // GetValue searches for the value corresponding to given Key. -// If the search does not succeed, a multiaddr string of a closer peer is -// returned along with util.ErrSearchIncomplete func (dht *IpfsDHT) GetValue(ctx context.Context, key key.Key) ([]byte, error) { + vals, err := dht.GetValues(ctx, key, 3) + if err != nil { + return nil, err + } + + var recs [][]byte + for _, v := range vals { + recs = append(recs, v.Val) + } + + i, err := dht.Selector.BestRecord(key, recs) + if err != nil { + return nil, err + } + + best := recs[i] + log.Debugf("GetValue %v %v", key, best) + if best == nil { + log.Errorf("GetValue yielded correct record with nil value.") + return nil, routing.ErrNotFound + } + + fixupRec, err := record.MakePutRecord(dht.peerstore.PrivKey(dht.self), key, best, true) + if err != nil { + // probably shouldnt actually 'error' here as we have found a value we like, + // but this call failing probably isnt something we want to ignore + return nil, err + } + + for _, v := range vals { + // if someone sent us a different 'less-valid' record, lets correct them + if !bytes.Equal(v.Val, best) { + go func(v routing.RecvdVal) { + err := dht.putValueToPeer(ctx, v.From, key, fixupRec) + if err != nil { + log.Error("Error correcting DHT entry: ", err) + } + }(v) + } + } + + return best, nil +} + +func (dht *IpfsDHT) GetValues(ctx context.Context, key key.Key, nvals int) ([]routing.RecvdVal, error) { + var vals []routing.RecvdVal + var valslock sync.Mutex + // If we have it local, dont bother doing an RPC! - val, err := dht.getLocal(key) + lrec, err := dht.getLocal(key) if err == nil { + // TODO: this is tricky, we dont always want to trust our own value + // what if the authoritative source updated it? log.Debug("have it locally") - return val, nil - } else { - log.Debug("failed to get value locally: %s", err) + vals = append(vals, routing.RecvdVal{ + Val: lrec.GetValue(), + From: dht.self, + }) + + if nvals <= 1 { + return vals, nil + } + } else if nvals == 0 { + return nil, err } // get closest peers in the routing table @@ -104,14 +160,26 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key key.Key) ([]byte, error) { ID: p, }) - val, peers, err := dht.getValueOrPeers(ctx, p, key) + rec, peers, err := dht.getValueOrPeers(ctx, p, key) if err != nil { return nil, err } - res := &dhtQueryResult{value: val, closerPeers: peers} - if val != nil { - res.success = true + res := &dhtQueryResult{closerPeers: peers} + + if rec.GetValue() != nil { + rv := routing.RecvdVal{ + Val: rec.GetValue(), + From: p, + } + valslock.Lock() + vals = append(vals, rv) + + // If weve collected enough records, we're done + if len(vals) >= nvals { + res.success = true + } + valslock.Unlock() } notif.PublishQueryEvent(parent, ¬if.QueryEvent{ @@ -124,17 +192,15 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key key.Key) ([]byte, error) { }) // run it! - result, err := query.Run(ctx, rtp) - if err != nil { - return nil, err + _, err = query.Run(ctx, rtp) + if len(vals) == 0 { + if err != nil { + return nil, err + } } - log.Debugf("GetValue %v %v", key, result.value) - if result.value == nil { - return nil, routing.ErrNotFound - } + return vals, nil - return result.value, nil } // Value provider layer of indirection. diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 9d577c4a3..09f17e61e 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -44,6 +44,21 @@ func (c *client) GetValue(ctx context.Context, key key.Key) ([]byte, error) { return data, nil } +func (c *client) GetValues(ctx context.Context, key key.Key, count int) ([]routing.RecvdVal, error) { + log.Debugf("GetValue: %s", key) + v, err := c.datastore.Get(key.DsKey()) + if err != nil { + return nil, err + } + + data, ok := v.([]byte) + if !ok { + return nil, errors.New("could not cast value from datastore") + } + + return []routing.RecvdVal{{Val: data, From: c.peer.ID()}}, nil +} + func (c *client) FindProviders(ctx context.Context, key key.Key) ([]peer.PeerInfo, error) { return c.server.Providers(key), nil } diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 8400e6a3b..49df5870a 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -25,6 +25,10 @@ func (c *nilclient) GetValue(_ context.Context, _ key.Key) ([]byte, error) { return nil, errors.New("Tried GetValue from nil routing.") } +func (c *nilclient) GetValues(_ context.Context, _ key.Key, _ int) ([]routing.RecvdVal, error) { + return nil, errors.New("Tried GetValues from nil routing.") +} + func (c *nilclient) FindPeer(_ context.Context, _ peer.ID) (peer.PeerInfo, error) { return peer.PeerInfo{}, nil } diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 7ead5d305..22aef75b3 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -67,6 +67,27 @@ func (c *offlineRouting) GetValue(ctx context.Context, key key.Key) ([]byte, err return rec.GetValue(), nil } +func (c *offlineRouting) GetValues(ctx context.Context, key key.Key, _ int) ([]routing.RecvdVal, error) { + v, err := c.datastore.Get(key.DsKey()) + if err != nil { + return nil, err + } + + byt, ok := v.([]byte) + if !ok { + return nil, errors.New("value stored in datastore not []byte") + } + rec := new(pb.Record) + err = proto.Unmarshal(byt, rec) + if err != nil { + return nil, err + } + + return []routing.RecvdVal{ + {Val: rec.GetValue()}, + }, nil +} + func (c *offlineRouting) FindProviders(ctx context.Context, key key.Key) ([]peer.PeerInfo, error) { return nil, ErrOffline } diff --git a/routing/record/record.go b/routing/record/record.go index 80da08581..61dd995ac 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -6,13 +6,11 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" key "github.com/ipfs/go-ipfs/blocks/key" - dag "github.com/ipfs/go-ipfs/merkledag" ci "github.com/ipfs/go-ipfs/p2p/crypto" pb "github.com/ipfs/go-ipfs/routing/dht/pb" logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) -var _ = dag.FetchGraph var log = logging.Logger("routing/record") // MakePutRecord creates and signs a dht record for the given key/value pair diff --git a/routing/record/selection.go b/routing/record/selection.go new file mode 100644 index 000000000..e90ebcd39 --- /dev/null +++ b/routing/record/selection.go @@ -0,0 +1,40 @@ +package record + +import ( + "errors" + "strings" + + key "github.com/ipfs/go-ipfs/blocks/key" +) + +// A SelectorFunc selects the best value for the given key from +// a slice of possible values and returns the index of the chosen one +type SelectorFunc func(key.Key, [][]byte) (int, error) + +type Selector map[string]SelectorFunc + +func (s Selector) BestRecord(k key.Key, recs [][]byte) (int, error) { + if len(recs) == 0 { + return 0, errors.New("no records given!") + } + + parts := strings.Split(string(k), "/") + if len(parts) < 3 { + log.Infof("Record key does not have selectorfunc: %s", k) + return 0, errors.New("record key does not have selectorfunc") + } + + sel, ok := s[parts[1]] + if !ok { + log.Infof("Unrecognized key prefix: %s", parts[1]) + return 0, ErrInvalidRecordType + } + + return sel(k, recs) +} + +// PublicKeySelector just selects the first entry. +// All valid public key records will be equivalent. +func PublicKeySelector(k key.Key, vals [][]byte) (int, error) { + return 0, nil +} diff --git a/routing/routing.go b/routing/routing.go index db9b49dcd..1c799b984 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -26,6 +26,18 @@ type IpfsRouting interface { // GetValue searches for the value corresponding to given Key. GetValue(context.Context, key.Key) ([]byte, error) + // GetValues searches for values corresponding to given Key. + // + // Passing a value of '0' for the count argument will cause the + // routing interface to return values only from cached or local storage + // and return an error if no cached value is found. + // + // Passing a value of '1' will return a local value if found, and query + // the network for the first value it finds otherwise. + // As a result, a value of '1' is mostly useful for cases where the record + // in question has only one valid value (such as public keys) + GetValues(c context.Context, k key.Key, count int) ([]RecvdVal, error) + // Value provider layer of indirection. // This is what DSHTs (Coral and MainlineDHT) do to store large values in a DHT. @@ -44,6 +56,13 @@ type IpfsRouting interface { // TODO expose io.Closer or plain-old Close error } +// RecvdVal represents a dht value record that has been received from a given peer +// it is used to track peers with expired records in order to correct them. +type RecvdVal struct { + From peer.ID + Val []byte +} + type PubKeyFetcher interface { GetPublicKey(context.Context, peer.ID) (ci.PubKey, error) } diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 97d3d70c7..923d530eb 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -81,6 +81,22 @@ func (c *Client) GetValue(ctx context.Context, k key.Key) ([]byte, error) { return response.Record.GetValue(), nil } +func (c *Client) GetValues(ctx context.Context, k key.Key, _ int) ([]routing.RecvdVal, error) { + defer log.EventBegin(ctx, "getValue", &k).Done() + msg := pb.NewMessage(pb.Message_GET_VALUE, string(k), 0) + response, err := c.proxy.SendRequest(ctx, msg) // TODO wrap to hide the remote + if err != nil { + return nil, err + } + + return []routing.RecvdVal{ + { + Val: response.Record.GetValue(), + From: c.local, + }, + }, nil +} + func (c *Client) Provide(ctx context.Context, k key.Key) error { defer log.EventBegin(ctx, "provide", &k).Done() msg := pb.NewMessage(pb.Message_ADD_PROVIDER, string(k), 0) From 34b054f5e0de53fe2a2329c26f4861236870bd8b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 18 Sep 2015 10:27:55 -0700 Subject: [PATCH 0979/3817] ipns record selection via sequence numbers This commit adds a sequence number to the IpnsEntry protobuf that is used to determine which among a set of entries for the same key is the 'most correct'. GetValues has been added to the routing interface to retrieve a set of records from the dht, for the caller to select from. GetValue (singular) will call GetValues, select the 'best' record, and then update that record to peers we received outdated records from. This will help keep the dht consistent. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@38e95d2d4e58d0e6040678dcc26e0142246cf4f3 --- namesys/pb/namesys.pb.go | 8 +++++ namesys/pb/namesys.proto | 2 ++ namesys/publisher.go | 77 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 80 insertions(+), 7 deletions(-) diff --git a/namesys/pb/namesys.pb.go b/namesys/pb/namesys.pb.go index 97e25a855..4d99a5e0a 100644 --- a/namesys/pb/namesys.pb.go +++ b/namesys/pb/namesys.pb.go @@ -56,6 +56,7 @@ type IpnsEntry struct { Signature []byte `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"` ValidityType *IpnsEntry_ValidityType `protobuf:"varint,3,opt,name=validityType,enum=namesys.pb.IpnsEntry_ValidityType" json:"validityType,omitempty"` Validity []byte `protobuf:"bytes,4,opt,name=validity" json:"validity,omitempty"` + Sequence *uint64 `protobuf:"varint,5,opt,name=sequence" json:"sequence,omitempty"` XXX_unrecognized []byte `json:"-"` } @@ -91,6 +92,13 @@ func (m *IpnsEntry) GetValidity() []byte { return nil } +func (m *IpnsEntry) GetSequence() uint64 { + if m != nil && m.Sequence != nil { + return *m.Sequence + } + return 0 +} + func init() { proto.RegisterEnum("namesys.pb.IpnsEntry_ValidityType", IpnsEntry_ValidityType_name, IpnsEntry_ValidityType_value) } diff --git a/namesys/pb/namesys.proto b/namesys/pb/namesys.proto index 4219af6bb..242f77bf2 100644 --- a/namesys/pb/namesys.proto +++ b/namesys/pb/namesys.proto @@ -10,4 +10,6 @@ message IpnsEntry { optional ValidityType validityType = 3; optional bytes validity = 4; + + optional uint64 sequence = 5; } diff --git a/namesys/publisher.go b/namesys/publisher.go index e3dd1d81b..a37a39fbe 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,6 +7,7 @@ import ( "time" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" @@ -45,10 +46,6 @@ func NewRoutingPublisher(route routing.IpfsRouting) Publisher { func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error { log.Debugf("Publish %s", value) - data, err := createRoutingEntryData(k, value) - if err != nil { - return err - } pubkey := k.GetPublic() pkbytes, err := pubkey.Bytes() if err != nil { @@ -57,6 +54,27 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Pa nameb := u.Hash(pkbytes) namekey := key.Key("/pk/" + string(nameb)) + ipnskey := key.Key("/ipns/" + string(nameb)) + + // get previous records sequence number, and add one to it + var seqnum uint64 + prevrec, err := p.routing.GetValues(ctx, ipnskey, 0) + if err == nil { + e := new(pb.IpnsEntry) + err := proto.Unmarshal(prevrec[0].Val, e) + if err != nil { + return err + } + + seqnum = e.GetSequence() + 1 + } else if err != ds.ErrNotFound { + return err + } + + data, err := createRoutingEntryData(k, value, seqnum) + if err != nil { + return err + } log.Debugf("Storing pubkey at: %s", namekey) // Store associated public key @@ -67,8 +85,6 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Pa return err } - ipnskey := key.Key("/ipns/" + string(nameb)) - log.Debugf("Storing ipns entry at: %s", ipnskey) // Store ipns entry at "/ipns/"+b58(h(pubkey)) timectx, cancel = context.WithDeadline(ctx, time.Now().Add(time.Second*10)) @@ -80,12 +96,13 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Pa return nil } -func createRoutingEntryData(pk ci.PrivKey, val path.Path) ([]byte, error) { +func createRoutingEntryData(pk ci.PrivKey, val path.Path, seq uint64) ([]byte, error) { entry := new(pb.IpnsEntry) entry.Value = []byte(val) typ := pb.IpnsEntry_EOL entry.ValidityType = &typ + entry.Sequence = proto.Uint64(seq) entry.Validity = []byte(u.FormatRFC3339(time.Now().Add(time.Hour * 24))) sig, err := pk.Sign(ipnsEntryDataForSig(entry)) @@ -110,6 +127,52 @@ var IpnsRecordValidator = &record.ValidChecker{ Sign: true, } +func IpnsSelectorFunc(k key.Key, vals [][]byte) (int, error) { + var recs []*pb.IpnsEntry + for _, v := range vals { + e := new(pb.IpnsEntry) + err := proto.Unmarshal(v, e) + if err == nil { + recs = append(recs, e) + } else { + recs = append(recs, nil) + } + } + + var best_seq uint64 + best_i := -1 + + for i, r := range recs { + if r == nil { + continue + } + if best_i == -1 || r.GetSequence() > best_seq { + best_seq = r.GetSequence() + best_i = i + } else if r.GetSequence() == best_seq { + rt, err := u.ParseRFC3339(string(r.GetValidity())) + if err != nil { + continue + } + + bestt, err := u.ParseRFC3339(string(recs[best_i].GetValidity())) + if err != nil { + continue + } + + if rt.After(bestt) { + best_seq = r.GetSequence() + best_i = i + } + } + } + if best_i == -1 { + return 0, errors.New("no usable records in given set") + } + + return best_i, nil +} + // ValidateIpnsRecord implements ValidatorFunc and verifies that the // given 'val' is an IpnsEntry and that that entry is valid. func ValidateIpnsRecord(k key.Key, val []byte) error { From 1593d020fd6566eff172847d93b4254bbc703e7a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 21 Sep 2015 09:55:25 -0700 Subject: [PATCH 0980/3817] Fix dht queries Queries previously would sometimes only query three (alpha value) peers before halting the operation. This PR changes the number of peers grabbed from the routing table to start a query to K. Dht nodes would also not respond with enough peers, as per the kademlia paper, this has been changed to from 4 to 'K'. The query mechanism itself also was flawed in that it would pull all the peers it had yet to query out of the queue and 'start' the query for them. The concurrency rate limiting was done inside the 'queryPeer' method after the goroutine was spawned. This did not allow for peers receiver from query replies to be properly queried in order of distance. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@e26360b596419c895f24be9ba8ec9ec7e2f2d2d6 --- routing/dht/dht.go | 6 +----- routing/dht/handlers.go | 2 +- routing/dht/lookup.go | 2 +- routing/dht/query.go | 33 ++++++++++++++++----------------- routing/dht/routing.go | 8 ++++---- 5 files changed, 23 insertions(+), 28 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 64f28c74e..2f9a6ebe6 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -312,11 +312,7 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, p peer.ID, count int) [ continue } - // must all be closer than self - key := key.Key(pmes.GetKey()) - if !kb.Closer(dht.self, clp, key) { - filtered = append(filtered, clp) - } + filtered = append(filtered, clp) } // ok seems like closer nodes diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 1c5d2b1c1..137995bed 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -14,7 +14,7 @@ import ( ) // The number of closer peers to send on requests. -var CloserPeerCount = 4 +var CloserPeerCount = KValue // dhthandler specifies the signature of functions that handle DHT messages. type dhtHandler func(context.Context, peer.ID, *pb.Message) (*pb.Message, error) diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 76173a615..dc377e8b7 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -23,7 +23,7 @@ func pointerizePeerInfos(pis []peer.PeerInfo) []*peer.PeerInfo { // to the given key func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key key.Key) (<-chan peer.ID, error) { e := log.EventBegin(ctx, "getClosestPeers", &key) - tablepeers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) + tablepeers := dht.routingTable.NearestPeers(kb.ConvertKey(key), KValue) if len(tablepeers) == 0 { return nil, kb.ErrLookupFailure } diff --git a/routing/dht/query.go b/routing/dht/query.go index ab4f28492..b1bec20bb 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -184,29 +184,28 @@ func (r *dhtQueryRunner) spawnWorkers(proc process.Process) { case <-r.proc.Closing(): return - case p, more := <-r.peersToQuery.DeqChan: - if !more { - return // channel closed. + case <-r.rateLimit: + select { + case p, more := <-r.peersToQuery.DeqChan: + if !more { + return // channel closed. + } + + // do it as a child func to make sure Run exits + // ONLY AFTER spawn workers has exited. + proc.Go(func(proc process.Process) { + r.queryPeer(proc, p) + }) + case <-r.proc.Closing(): + return + case <-r.peersRemaining.Done(): + return } - - // do it as a child func to make sure Run exits - // ONLY AFTER spawn workers has exited. - proc.Go(func(proc process.Process) { - r.queryPeer(proc, p) - }) } } } func (r *dhtQueryRunner) queryPeer(proc process.Process, p peer.ID) { - // make sure we rate limit concurrency. - select { - case <-r.rateLimit: - case <-proc.Closing(): - r.peersRemaining.Decrement(1) - return - } - // ok let's do this! // create a context from our proc. diff --git a/routing/dht/routing.go b/routing/dht/routing.go index d5854155f..57341e69c 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -145,7 +145,7 @@ func (dht *IpfsDHT) GetValues(ctx context.Context, key key.Key, nvals int) ([]ro } // get closest peers in the routing table - rtp := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) + rtp := dht.routingTable.NearestPeers(kb.ConvertKey(key), KValue) log.Debugf("peers in rt: %s", len(rtp), rtp) if len(rtp) == 0 { log.Warning("No peers from routing table!") @@ -322,7 +322,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key key.Key, return &dhtQueryResult{closerPeers: clpeers}, nil }) - peers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue) + peers := dht.routingTable.NearestPeers(kb.ConvertKey(key), KValue) _, err := query.Run(ctx, peers) if err != nil { log.Debugf("Query error: %s", err) @@ -342,7 +342,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, er return pi, nil } - peers := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue) + peers := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), KValue) if len(peers) == 0 { return peer.PeerInfo{}, kb.ErrLookupFailure } @@ -409,7 +409,7 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< peerchan := make(chan peer.PeerInfo, asyncQueryBuffer) peersSeen := peer.Set{} - peers := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue) + peers := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), KValue) if len(peers) == 0 { return nil, kb.ErrLookupFailure } From bb07db083ca07b9ce14d51368b9e17593e2bb085 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 21 Sep 2015 16:35:33 -0700 Subject: [PATCH 0981/3817] Implement ipns republisher This commit adds a very basic process that will periodically go through a list of given ids and republish the values for their ipns entries. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@452d98fc93c78ae95a4c838d0641eae3a1702112 --- namesys/publisher.go | 74 ++++++++++++---- namesys/republisher/repub.go | 142 ++++++++++++++++++++++++++++++ namesys/republisher/repub_test.go | 120 +++++++++++++++++++++++++ 3 files changed, 320 insertions(+), 16 deletions(-) create mode 100644 namesys/republisher/repub.go create mode 100644 namesys/republisher/repub_test.go diff --git a/namesys/publisher.go b/namesys/publisher.go index a37a39fbe..33c7a49cc 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -14,6 +14,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" pb "github.com/ipfs/go-ipfs/namesys/pb" ci "github.com/ipfs/go-ipfs/p2p/crypto" + peer "github.com/ipfs/go-ipfs/p2p/peer" path "github.com/ipfs/go-ipfs/path" pin "github.com/ipfs/go-ipfs/pin" routing "github.com/ipfs/go-ipfs/routing" @@ -30,6 +31,8 @@ var ErrExpiredRecord = errors.New("expired record") // unknown validity type. var ErrUnrecognizedValidity = errors.New("unrecognized validity type") +var PublishPutValTimeout = time.Minute + // ipnsPublisher is capable of publishing and resolving names to the IPFS // routing system. type ipnsPublisher struct { @@ -37,7 +40,7 @@ type ipnsPublisher struct { } // NewRoutingPublisher constructs a publisher for the IPFS Routing name system. -func NewRoutingPublisher(route routing.IpfsRouting) Publisher { +func NewRoutingPublisher(route routing.IpfsRouting) *ipnsPublisher { return &ipnsPublisher{routing: route} } @@ -45,16 +48,19 @@ func NewRoutingPublisher(route routing.IpfsRouting) Publisher { // and publishes it out to the routing system func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error { log.Debugf("Publish %s", value) + return p.PublishWithEOL(ctx, k, value, time.Now().Add(time.Hour*24)) +} - pubkey := k.GetPublic() - pkbytes, err := pubkey.Bytes() +// PublishWithEOL is a temporary stand in for the ipns records implementation +// see here for more details: https://github.com/ipfs/specs/tree/master/records +func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error { + + id, err := peer.IDFromPrivateKey(k) if err != nil { return err } - nameb := u.Hash(pkbytes) - namekey := key.Key("/pk/" + string(nameb)) - ipnskey := key.Key("/ipns/" + string(nameb)) + namekey, ipnskey := IpnsKeysForID(id) // get previous records sequence number, and add one to it var seqnum uint64 @@ -71,46 +77,75 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Pa return err } - data, err := createRoutingEntryData(k, value, seqnum) + entry, err := CreateRoutingEntryData(k, value, seqnum, eol) + if err != nil { + return err + } + + err = PublishEntry(ctx, p.routing, ipnskey, entry) + if err != nil { + return err + } + + err = PublishPublicKey(ctx, p.routing, namekey, k.GetPublic()) + if err != nil { + return err + } + + return nil +} + +func PublishPublicKey(ctx context.Context, r routing.IpfsRouting, k key.Key, pubk ci.PubKey) error { + log.Debugf("Storing pubkey at: %s", k) + pkbytes, err := pubk.Bytes() if err != nil { return err } - log.Debugf("Storing pubkey at: %s", namekey) // Store associated public key - timectx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Second*10)) + timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout) defer cancel() - err = p.routing.PutValue(timectx, namekey, pkbytes) + err = r.PutValue(timectx, k, pkbytes) + if err != nil { + return err + } + + return nil +} + +func PublishEntry(ctx context.Context, r routing.IpfsRouting, ipnskey key.Key, rec *pb.IpnsEntry) error { + timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout) + defer cancel() + + data, err := proto.Marshal(rec) if err != nil { return err } log.Debugf("Storing ipns entry at: %s", ipnskey) // Store ipns entry at "/ipns/"+b58(h(pubkey)) - timectx, cancel = context.WithDeadline(ctx, time.Now().Add(time.Second*10)) - defer cancel() - if err := p.routing.PutValue(timectx, ipnskey, data); err != nil { + if err := r.PutValue(timectx, ipnskey, data); err != nil { return err } return nil } -func createRoutingEntryData(pk ci.PrivKey, val path.Path, seq uint64) ([]byte, error) { +func CreateRoutingEntryData(pk ci.PrivKey, val path.Path, seq uint64, eol time.Time) (*pb.IpnsEntry, error) { entry := new(pb.IpnsEntry) entry.Value = []byte(val) typ := pb.IpnsEntry_EOL entry.ValidityType = &typ entry.Sequence = proto.Uint64(seq) - entry.Validity = []byte(u.FormatRFC3339(time.Now().Add(time.Hour * 24))) + entry.Validity = []byte(u.FormatRFC3339(eol)) sig, err := pk.Sign(ipnsEntryDataForSig(entry)) if err != nil { return nil, err } entry.Signature = sig - return proto.Marshal(entry) + return entry, nil } func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte { @@ -226,3 +261,10 @@ func InitializeKeyspace(ctx context.Context, ds dag.DAGService, pub Publisher, p return nil } + +func IpnsKeysForID(id peer.ID) (name, ipns key.Key) { + namekey := key.Key("/pk/" + id) + ipnskey := key.Key("/ipns/" + id) + + return namekey, ipnskey +} diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go new file mode 100644 index 000000000..9dd95f563 --- /dev/null +++ b/namesys/republisher/repub.go @@ -0,0 +1,142 @@ +package republisher + +import ( + "errors" + "sync" + "time" + + key "github.com/ipfs/go-ipfs/blocks/key" + namesys "github.com/ipfs/go-ipfs/namesys" + pb "github.com/ipfs/go-ipfs/namesys/pb" + peer "github.com/ipfs/go-ipfs/p2p/peer" + path "github.com/ipfs/go-ipfs/path" + "github.com/ipfs/go-ipfs/routing" + dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" + + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" + gpctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" +) + +var errNoEntry = errors.New("no previous entry") + +var log = logging.Logger("ipns-repub") + +var DefaultRebroadcastInterval = time.Hour * 4 + +const DefaultRecordLifetime = time.Hour * 24 + +type Republisher struct { + r routing.IpfsRouting + ds ds.Datastore + ps peer.Peerstore + + Interval time.Duration + + // how long records that are republished should be valid for + RecordLifetime time.Duration + + entrylock sync.Mutex + entries map[peer.ID]struct{} +} + +func NewRepublisher(r routing.IpfsRouting, ds ds.Datastore, ps peer.Peerstore) *Republisher { + return &Republisher{ + r: r, + ps: ps, + ds: ds, + entries: make(map[peer.ID]struct{}), + Interval: DefaultRebroadcastInterval, + RecordLifetime: DefaultRecordLifetime, + } +} + +func (rp *Republisher) AddName(id peer.ID) { + rp.entrylock.Lock() + defer rp.entrylock.Unlock() + rp.entries[id] = struct{}{} +} + +func (rp *Republisher) Run(proc goprocess.Process) { + tick := time.NewTicker(rp.Interval) + defer tick.Stop() + + for { + select { + case <-tick.C: + err := rp.republishEntries(proc) + if err != nil { + log.Error(err) + } + case <-proc.Closing(): + return + } + } +} + +func (rp *Republisher) republishEntries(p goprocess.Process) error { + ctx, cancel := context.WithCancel(gpctx.OnClosingContext(p)) + defer cancel() + + for id, _ := range rp.entries { + log.Debugf("republishing ipns entry for %s", id) + priv := rp.ps.PrivKey(id) + + // Look for it locally only + namekey, ipnskey := namesys.IpnsKeysForID(id) + p, seq, err := rp.getLastVal(ipnskey) + if err != nil { + if err == errNoEntry { + continue + } + return err + } + + // update record with same sequence number + eol := time.Now().Add(rp.RecordLifetime) + entry, err := namesys.CreateRoutingEntryData(priv, p, seq, eol) + if err != nil { + return err + } + + // republish public key + err = namesys.PublishPublicKey(ctx, rp.r, namekey, priv.GetPublic()) + if err != nil { + return err + } + + // republish ipns entry + err = namesys.PublishEntry(ctx, rp.r, ipnskey, entry) + if err != nil { + return err + } + } + + return nil +} + +func (rp *Republisher) getLastVal(k key.Key) (path.Path, uint64, error) { + ival, err := rp.ds.Get(k.DsKey()) + if err != nil { + // not found means we dont have a previously published entry + return "", 0, errNoEntry + } + + val := ival.([]byte) + dhtrec := new(dhtpb.Record) + err = proto.Unmarshal(val, dhtrec) + if err != nil { + return "", 0, err + } + + // extract published data from record + e := new(pb.IpnsEntry) + err = proto.Unmarshal(dhtrec.GetValue(), e) + if err != nil { + return "", 0, err + } + return path.Path(e.Value), e.GetSequence(), nil +} diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go new file mode 100644 index 000000000..66d137a70 --- /dev/null +++ b/namesys/republisher/repub_test.go @@ -0,0 +1,120 @@ +package republisher_test + +import ( + "errors" + "testing" + "time" + + goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + + "github.com/ipfs/go-ipfs/core" + mock "github.com/ipfs/go-ipfs/core/mock" + namesys "github.com/ipfs/go-ipfs/namesys" + . "github.com/ipfs/go-ipfs/namesys/republisher" + mocknet "github.com/ipfs/go-ipfs/p2p/net/mock" + peer "github.com/ipfs/go-ipfs/p2p/peer" + path "github.com/ipfs/go-ipfs/path" +) + +func TestRepublish(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // create network + mn := mocknet.New(ctx) + + var nodes []*core.IpfsNode + for i := 0; i < 10; i++ { + nd, err := core.NewNode(ctx, &core.BuildCfg{ + Online: true, + Host: mock.MockHostOption(mn), + }) + if err != nil { + t.Fatal(err) + } + + nodes = append(nodes, nd) + } + + mn.LinkAll() + + bsinf := core.BootstrapConfigWithPeers( + []peer.PeerInfo{ + nodes[0].Peerstore.PeerInfo(nodes[0].Identity), + }, + ) + + for _, n := range nodes[1:] { + if err := n.Bootstrap(bsinf); err != nil { + t.Fatal(err) + } + } + + // have one node publish a record that is valid for 1 second + publisher := nodes[3] + p := path.FromString("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") // does not need to be valid + rp := namesys.NewRoutingPublisher(publisher.Routing) + err := rp.PublishWithEOL(ctx, publisher.PrivateKey, p, time.Now().Add(time.Second)) + if err != nil { + t.Fatal(err) + } + + name := "/ipns/" + publisher.Identity.Pretty() + if err := verifyResolution(nodes, name, p); err != nil { + t.Fatal(err) + } + + // Now wait a second, the records will be invalid and we should fail to resolve + time.Sleep(time.Second) + if err := verifyResolutionFails(nodes, name); err != nil { + t.Fatal(err) + } + + // The republishers that are contained within the nodes have their timeout set + // to 12 hours. Instead of trying to tweak those, we're just going to pretend + // they dont exist and make our own. + repub := NewRepublisher(publisher.Routing, publisher.Repo.Datastore(), publisher.Peerstore) + repub.Interval = time.Second + repub.RecordLifetime = time.Second * 5 + repub.AddName(publisher.Identity) + + proc := goprocess.Go(repub.Run) + defer proc.Close() + + // now wait a couple seconds for it to fire + time.Sleep(time.Second * 2) + + // we should be able to resolve them now + if err := verifyResolution(nodes, name, p); err != nil { + t.Fatal(err) + } +} + +func verifyResolution(nodes []*core.IpfsNode, key string, exp path.Path) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + for _, n := range nodes { + val, err := n.Namesys.Resolve(ctx, key) + if err != nil { + return err + } + + if val != exp { + return errors.New("resolved wrong record") + } + } + return nil +} + +func verifyResolutionFails(nodes []*core.IpfsNode, key string) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + for _, n := range nodes { + _, err := n.Namesys.Resolve(ctx, key) + if err == nil { + return errors.New("expected resolution to fail") + } + } + return nil +} From 1b408c7d4f88fa279b0da6161f1f15ccab31d25f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 21 Sep 2015 16:35:33 -0700 Subject: [PATCH 0982/3817] Implement ipns republisher This commit adds a very basic process that will periodically go through a list of given ids and republish the values for their ipns entries. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@aea26285fdea79835535822136cd34ccdadb79ef --- routing/dht/dht.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 2f9a6ebe6..5de2d21da 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -18,7 +18,6 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - u "github.com/ipfs/go-ipfs/util" logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" @@ -217,13 +216,10 @@ func (dht *IpfsDHT) getLocal(key key.Key) (*pb.Record, error) { return nil, err } - // TODO: 'if paranoid' - if u.Debug { - err = dht.verifyRecordLocally(rec) - if err != nil { - log.Debugf("local record verify failed: %s (discarded)", err) - return nil, err - } + err = dht.verifyRecordLocally(rec) + if err != nil { + log.Debugf("local record verify failed: %s (discarded)", err) + return nil, err } return rec, nil From 4fc02fd10583930d8f17d52e33638fc01e570fd9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 30 Sep 2015 11:03:44 -0700 Subject: [PATCH 0983/3817] make publish more configurable and add test for repub License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@af676f7be1e9ce6ae54435408d5e5b723d4d3a4e --- namesys/interface.go | 5 +++++ namesys/namesys.go | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/namesys/interface.go b/namesys/interface.go index 5903c78a3..09c296c23 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -31,6 +31,7 @@ package namesys import ( "errors" + "time" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ci "github.com/ipfs/go-ipfs/p2p/crypto" @@ -105,4 +106,8 @@ type Publisher interface { // Publish establishes a name-value mapping. // TODO make this not PrivKey specific. Publish(ctx context.Context, name ci.PrivKey, value path.Path) error + + // TODO: to be replaced by a more generic 'PublishWithValidity' type + // call once the records spec is implemented + PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error } diff --git a/namesys/namesys.go b/namesys/namesys.go index 7fe317b66..ed03b4cc2 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -2,6 +2,7 @@ package namesys import ( "strings" + "time" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ci "github.com/ipfs/go-ipfs/p2p/crypto" @@ -81,3 +82,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error { return ns.publishers["/ipns/"].Publish(ctx, name, value) } + +func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, val path.Path, eol time.Time) error { + return ns.publishers["/ipns/"].PublishWithEOL(ctx, name, val, eol) +} From 7dd91b521c26479bdf8b006cf8c3ce837e9b5554 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 30 Sep 2015 10:59:12 -0700 Subject: [PATCH 0984/3817] Addressing comments from CR License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@9fb7bdffd8a3289619db03a3c2ad498798bc17b5 --- routing/dht/routing.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 57341e69c..df93396ce 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -3,6 +3,7 @@ package dht import ( "bytes" "sync" + "time" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" @@ -60,6 +61,8 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key key.Key, value []byte) err for p := range pchan { wg.Add(1) go func(p peer.ID) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() defer wg.Done() notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ Type: notif.Value, @@ -78,7 +81,10 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key key.Key, value []byte) err // GetValue searches for the value corresponding to given Key. func (dht *IpfsDHT) GetValue(ctx context.Context, key key.Key) ([]byte, error) { - vals, err := dht.GetValues(ctx, key, 3) + ctx, cancel := context.WithTimeout(ctx, time.Minute) + defer cancel() + + vals, err := dht.GetValues(ctx, key, 16) if err != nil { return nil, err } @@ -111,6 +117,8 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key key.Key) ([]byte, error) { // if someone sent us a different 'less-valid' record, lets correct them if !bytes.Equal(v.Val, best) { go func(v routing.RecvdVal) { + ctx, cancel := context.WithTimeout(dht.Context(), time.Second*30) + defer cancel() err := dht.putValueToPeer(ctx, v.From, key, fixupRec) if err != nil { log.Error("Error correcting DHT entry: ", err) From 52e070010aa2de055582d26d8401f2e6699e9618 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 30 Sep 2015 10:59:12 -0700 Subject: [PATCH 0985/3817] Addressing comments from CR License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@38d88f11b818ad8d8766e3d5ccd5c75855b7549f --- namesys/ipns_select_test.go | 127 +++++++++++++++++++++++++++++++++++ namesys/publisher.go | 23 +++++-- namesys/republisher/repub.go | 18 +---- 3 files changed, 148 insertions(+), 20 deletions(-) create mode 100644 namesys/ipns_select_test.go diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go new file mode 100644 index 000000000..ebd81e86d --- /dev/null +++ b/namesys/ipns_select_test.go @@ -0,0 +1,127 @@ +package namesys + +import ( + "fmt" + "math/rand" + "testing" + "time" + + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + + pb "github.com/ipfs/go-ipfs/namesys/pb" + ci "github.com/ipfs/go-ipfs/p2p/crypto" + path "github.com/ipfs/go-ipfs/path" + u "github.com/ipfs/go-ipfs/util" +) + +func shuffle(a []*pb.IpnsEntry) { + for n := 0; n < 5; n++ { + for i, _ := range a { + j := rand.Intn(len(a)) + a[i], a[j] = a[j], a[i] + } + } +} + +func AssertSelected(r *pb.IpnsEntry, from ...*pb.IpnsEntry) error { + shuffle(from) + var vals [][]byte + for _, r := range from { + data, err := proto.Marshal(r) + if err != nil { + return err + } + vals = append(vals, data) + } + + i, err := selectRecord(from, vals) + if err != nil { + return err + } + + if from[i] != r { + return fmt.Errorf("selected incorrect record %d", i) + } + + return nil +} + +func TestOrdering(t *testing.T) { + // select timestamp so selection is deterministic + ts := time.Unix(1000000, 0) + + // generate a key for signing the records + r := u.NewSeededRand(15) // generate deterministic keypair + priv, _, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, r) + if err != nil { + t.Fatal(err) + } + + e1, err := CreateRoutingEntryData(priv, path.Path("foo"), 1, ts.Add(time.Hour)) + if err != nil { + t.Fatal(err) + } + + e2, err := CreateRoutingEntryData(priv, path.Path("bar"), 2, ts.Add(time.Hour)) + if err != nil { + t.Fatal(err) + } + + e3, err := CreateRoutingEntryData(priv, path.Path("baz"), 3, ts.Add(time.Hour)) + if err != nil { + t.Fatal(err) + } + + e4, err := CreateRoutingEntryData(priv, path.Path("cat"), 3, ts.Add(time.Hour*2)) + if err != nil { + t.Fatal(err) + } + + e5, err := CreateRoutingEntryData(priv, path.Path("dog"), 4, ts.Add(time.Hour*3)) + if err != nil { + t.Fatal(err) + } + + e6, err := CreateRoutingEntryData(priv, path.Path("fish"), 4, ts.Add(time.Hour*3)) + if err != nil { + t.Fatal(err) + } + + // e1 is the only record, i hope it gets this right + err = AssertSelected(e1, e1) + if err != nil { + t.Fatal(err) + } + + // e2 has the highest sequence number + err = AssertSelected(e2, e1, e2) + if err != nil { + t.Fatal(err) + } + + // e3 has the highest sequence number + err = AssertSelected(e3, e1, e2, e3) + if err != nil { + t.Fatal(err) + } + + // e4 has a higher timeout + err = AssertSelected(e4, e1, e2, e3, e4) + if err != nil { + t.Fatal(err) + } + + // e5 has the highest sequence number + err = AssertSelected(e5, e1, e2, e3, e4, e5) + if err != nil { + t.Fatal(err) + } + + // e6 should be selected as its signauture will win in the comparison + err = AssertSelected(e6, e1, e2, e3, e4, e5, e6) + if err != nil { + t.Fatal(err) + } + + _ = []interface{}{e1, e2, e3, e4, e5, e6} +} diff --git a/namesys/publisher.go b/namesys/publisher.go index 33c7a49cc..521ce4cc8 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -60,7 +60,7 @@ func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value return err } - namekey, ipnskey := IpnsKeysForID(id) + _, ipnskey := IpnsKeysForID(id) // get previous records sequence number, and add one to it var seqnum uint64 @@ -77,17 +77,22 @@ func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value return err } + return PutRecordToRouting(ctx, k, value, seqnum, eol, p.routing, id) +} + +func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.IpfsRouting, id peer.ID) error { + namekey, ipnskey := IpnsKeysForID(id) entry, err := CreateRoutingEntryData(k, value, seqnum, eol) if err != nil { return err } - err = PublishEntry(ctx, p.routing, ipnskey, entry) + err = PublishEntry(ctx, r, ipnskey, entry) if err != nil { return err } - err = PublishPublicKey(ctx, p.routing, namekey, k.GetPublic()) + err = PublishPublicKey(ctx, r, namekey, k.GetPublic()) if err != nil { return err } @@ -174,13 +179,18 @@ func IpnsSelectorFunc(k key.Key, vals [][]byte) (int, error) { } } + return selectRecord(recs, vals) +} + +func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) { var best_seq uint64 best_i := -1 for i, r := range recs { - if r == nil { + if r == nil || r.GetSequence() < best_seq { continue } + if best_i == -1 || r.GetSequence() > best_seq { best_seq = r.GetSequence() best_i = i @@ -196,8 +206,11 @@ func IpnsSelectorFunc(k key.Key, vals [][]byte) (int, error) { } if rt.After(bestt) { - best_seq = r.GetSequence() best_i = i + } else if rt == bestt { + if bytes.Compare(vals[i], vals[best_i]) > 0 { + best_i = i + } } } } diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 9dd95f563..4fece5721 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -69,7 +69,7 @@ func (rp *Republisher) Run(proc goprocess.Process) { case <-tick.C: err := rp.republishEntries(proc) if err != nil { - log.Error(err) + log.Error("Republisher failed to republish: ", err) } case <-proc.Closing(): return @@ -86,7 +86,7 @@ func (rp *Republisher) republishEntries(p goprocess.Process) error { priv := rp.ps.PrivKey(id) // Look for it locally only - namekey, ipnskey := namesys.IpnsKeysForID(id) + _, ipnskey := namesys.IpnsKeysForID(id) p, seq, err := rp.getLastVal(ipnskey) if err != nil { if err == errNoEntry { @@ -97,19 +97,7 @@ func (rp *Republisher) republishEntries(p goprocess.Process) error { // update record with same sequence number eol := time.Now().Add(rp.RecordLifetime) - entry, err := namesys.CreateRoutingEntryData(priv, p, seq, eol) - if err != nil { - return err - } - - // republish public key - err = namesys.PublishPublicKey(ctx, rp.r, namekey, priv.GetPublic()) - if err != nil { - return err - } - - // republish ipns entry - err = namesys.PublishEntry(ctx, rp.r, ipnskey, entry) + err = namesys.PutRecordToRouting(ctx, priv, p, seq, eol, rp.r, id) if err != nil { return err } From 270a8bc4f49630dbd3fb6b0b5c52e441e9214457 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Oct 2015 15:02:22 -0700 Subject: [PATCH 0986/3817] fix publish fail on prexisting bad record dont error out if prexisting record is bad, just grab its sequence number and continue on with the publish. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@c6ef238a2ce839021a2afbfad7674d79d8d1ca45 --- routing/dht/handlers.go | 104 +++++++++++++++++++++-------- routing/dht/pb/dht.pb.go | 15 ++++- routing/dht/pb/dht.proto | 3 + routing/dht/records.go | 9 +++ routing/mock/centralized_client.go | 31 ++++++--- routing/mock/centralized_server.go | 2 +- 6 files changed, 122 insertions(+), 42 deletions(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 137995bed..6fa4d3f9b 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -3,6 +3,7 @@ package dht import ( "errors" "fmt" + "time" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" @@ -10,6 +11,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" pb "github.com/ipfs/go-ipfs/routing/dht/pb" + u "github.com/ipfs/go-ipfs/util" lgbl "github.com/ipfs/go-ipfs/util/eventlog/loggables" ) @@ -46,41 +48,17 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Mess resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) // first, is there even a key? - k := pmes.GetKey() + k := key.Key(pmes.GetKey()) if k == "" { return nil, errors.New("handleGetValue but no key was provided") // TODO: send back an error response? could be bad, but the other node's hanging. } - // let's first check if we have the value locally. - log.Debugf("%s handleGetValue looking into ds", dht.self) - dskey := key.Key(k).DsKey() - iVal, err := dht.datastore.Get(dskey) - log.Debugf("%s handleGetValue looking into ds GOT %v", dht.self, iVal) - - // if we got an unexpected error, bail. - if err != nil && err != ds.ErrNotFound { + rec, err := dht.checkLocalDatastore(k) + if err != nil { return nil, err } - - // if we have the value, send it back - if err == nil { - log.Debugf("%s handleGetValue success!", dht.self) - - byts, ok := iVal.([]byte) - if !ok { - return nil, fmt.Errorf("datastore had non byte-slice value for %v", dskey) - } - - rec := new(pb.Record) - err := proto.Unmarshal(byts, rec) - if err != nil { - log.Debug("Failed to unmarshal dht record from datastore") - return nil, err - } - - resp.Record = rec - } + resp.Record = rec // Find closest peer on given cluster to desired key and reply with that info closer := dht.betterPeersToQuery(pmes, p, CloserPeerCount) @@ -102,6 +80,69 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Mess return resp, nil } +func (dht *IpfsDHT) checkLocalDatastore(k key.Key) (*pb.Record, error) { + log.Debugf("%s handleGetValue looking into ds", dht.self) + dskey := k.DsKey() + iVal, err := dht.datastore.Get(dskey) + log.Debugf("%s handleGetValue looking into ds GOT %v", dht.self, iVal) + + if err == ds.ErrNotFound { + return nil, nil + } + + // if we got an unexpected error, bail. + if err != nil { + return nil, err + } + + // if we have the value, send it back + log.Debugf("%s handleGetValue success!", dht.self) + + byts, ok := iVal.([]byte) + if !ok { + return nil, fmt.Errorf("datastore had non byte-slice value for %v", dskey) + } + + rec := new(pb.Record) + err = proto.Unmarshal(byts, rec) + if err != nil { + log.Debug("Failed to unmarshal dht record from datastore") + return nil, err + } + + // if its our record, dont bother checking the times on it + if peer.ID(rec.GetAuthor()) == dht.self { + return rec, nil + } + + var recordIsBad bool + recvtime, err := u.ParseRFC3339(rec.GetTimeReceived()) + if err != nil { + log.Info("either no receive time set on record, or it was invalid: ", err) + recordIsBad = true + } + + if time.Now().Sub(recvtime) > MaxRecordAge { + log.Debug("old record found, tossing.") + recordIsBad = true + } + + // NOTE: we do not verify the record here beyond checking these timestamps. + // we put the burden of checking the records on the requester as checking a record + // may be computationally expensive + + if recordIsBad { + err := dht.datastore.Delete(dskey) + if err != nil { + log.Error("Failed to delete bad record from datastore: ", err) + } + + return nil, nil // can treat this as not having the record at all + } + + return rec, nil +} + // Store a value in this peer local storage func (dht *IpfsDHT) handlePutValue(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { defer log.EventBegin(ctx, "handlePutValue", p).Done() @@ -112,7 +153,12 @@ func (dht *IpfsDHT) handlePutValue(ctx context.Context, p peer.ID, pmes *pb.Mess return nil, err } - data, err := proto.Marshal(pmes.GetRecord()) + rec := pmes.GetRecord() + + // record the time we receive every record + rec.TimeReceived = proto.String(u.FormatRFC3339(time.Now())) + + data, err := proto.Marshal(rec) if err != nil { return nil, err } diff --git a/routing/dht/pb/dht.pb.go b/routing/dht/pb/dht.pb.go index 9a313a897..4b8501180 100644 --- a/routing/dht/pb/dht.pb.go +++ b/routing/dht/pb/dht.pb.go @@ -14,7 +14,7 @@ It has these top-level messages: */ package dht_pb -import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" +import proto "github.com/gogo/protobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. @@ -221,8 +221,10 @@ type Record struct { // hash of the authors public key Author *string `protobuf:"bytes,3,opt,name=author" json:"author,omitempty"` // A PKI signature for the key+value+author - Signature []byte `protobuf:"bytes,4,opt,name=signature" json:"signature,omitempty"` - XXX_unrecognized []byte `json:"-"` + Signature []byte `protobuf:"bytes,4,opt,name=signature" json:"signature,omitempty"` + // Time the record was received, set by receiver + TimeReceived *string `protobuf:"bytes,5,opt,name=timeReceived" json:"timeReceived,omitempty"` + XXX_unrecognized []byte `json:"-"` } func (m *Record) Reset() { *m = Record{} } @@ -257,6 +259,13 @@ func (m *Record) GetSignature() []byte { return nil } +func (m *Record) GetTimeReceived() string { + if m != nil && m.TimeReceived != nil { + return *m.TimeReceived + } + return "" +} + func init() { proto.RegisterEnum("dht.pb.Message_MessageType", Message_MessageType_name, Message_MessageType_value) proto.RegisterEnum("dht.pb.Message_ConnectionType", Message_ConnectionType_name, Message_ConnectionType_value) diff --git a/routing/dht/pb/dht.proto b/routing/dht/pb/dht.proto index 91c8d8e04..de88c3451 100644 --- a/routing/dht/pb/dht.proto +++ b/routing/dht/pb/dht.proto @@ -75,4 +75,7 @@ message Record { // A PKI signature for the key+value+author optional bytes signature = 4; + + // Time the record was received, set by receiver + optional string timeReceived = 5; } diff --git a/routing/dht/records.go b/routing/dht/records.go index 3c7d1d176..49a06d557 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -2,6 +2,7 @@ package dht import ( "fmt" + "time" ctxfrac "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/frac" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" @@ -12,6 +13,14 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" ) +// MaxRecordAge specifies the maximum time that any node will hold onto a record +// from the time its received. This does not apply to any other forms of validity that +// the record may contain. +// For example, a record may contain an ipns entry with an EOL saying its valid +// until the year 2020 (a great time in the future). For that record to stick around +// it must be rebroadcasted more frequently than once every 'MaxRecordAge' +const MaxRecordAge = time.Hour * 36 + func (dht *IpfsDHT) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey, error) { log.Debugf("getPublicKey for: %s", p) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 09f17e61e..9d9a1f6da 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -4,12 +4,15 @@ import ( "errors" "time" + proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" + dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" + u "github.com/ipfs/go-ipfs/util" "github.com/ipfs/go-ipfs/util/testutil" logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" ) @@ -25,7 +28,16 @@ type client struct { // FIXME(brian): is this method meant to simulate putting a value into the network? func (c *client) PutValue(ctx context.Context, key key.Key, val []byte) error { log.Debugf("PutValue: %s", key) - return c.datastore.Put(key.DsKey(), val) + rec := new(dhtpb.Record) + rec.Value = val + rec.Key = proto.String(string(key)) + rec.TimeReceived = proto.String(u.FormatRFC3339(time.Now())) + data, err := proto.Marshal(rec) + if err != nil { + return err + } + + return c.datastore.Put(key.DsKey(), data) } // FIXME(brian): is this method meant to simulate getting a value from the network? @@ -41,21 +53,22 @@ func (c *client) GetValue(ctx context.Context, key key.Key) ([]byte, error) { return nil, errors.New("could not cast value from datastore") } - return data, nil + rec := new(dhtpb.Record) + err = proto.Unmarshal(data, rec) + if err != nil { + return nil, err + } + + return rec.GetValue(), nil } func (c *client) GetValues(ctx context.Context, key key.Key, count int) ([]routing.RecvdVal, error) { - log.Debugf("GetValue: %s", key) - v, err := c.datastore.Get(key.DsKey()) + log.Debugf("GetValues: %s", key) + data, err := c.GetValue(ctx, key) if err != nil { return nil, err } - data, ok := v.([]byte) - if !ok { - return nil, errors.New("could not cast value from datastore") - } - return []routing.RecvdVal{{Val: data, From: c.peer.ID()}}, nil } diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index c7bd239ed..a62f64f8d 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -80,7 +80,7 @@ func (rs *s) Client(p testutil.Identity) Client { func (rs *s) ClientWithDatastore(_ context.Context, p testutil.Identity, datastore ds.Datastore) Client { return &client{ peer: p, - datastore: ds.NewMapDatastore(), + datastore: datastore, server: rs, } } From 23111ccfb35548b87aac0db814578d562e11cb2f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Oct 2015 15:02:22 -0700 Subject: [PATCH 0987/3817] fix publish fail on prexisting bad record dont error out if prexisting record is bad, just grab its sequence number and continue on with the publish. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@7a56cf96849c1120a3ac6bdf6a34cb69f6a4d22d --- namesys/namesys.go | 5 +- namesys/publisher.go | 65 +++++++++++++++++---- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 94 ++++++++++++++++++++++++++++++- 4 files changed, 150 insertions(+), 16 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index ed03b4cc2..12b73218b 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -4,6 +4,7 @@ import ( "strings" "time" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ci "github.com/ipfs/go-ipfs/p2p/crypto" path "github.com/ipfs/go-ipfs/path" @@ -25,7 +26,7 @@ type mpns struct { } // NewNameSystem will construct the IPFS naming system based on Routing -func NewNameSystem(r routing.IpfsRouting) NameSystem { +func NewNameSystem(r routing.IpfsRouting, ds ds.Datastore) NameSystem { return &mpns{ resolvers: map[string]resolver{ "dns": newDNSResolver(), @@ -33,7 +34,7 @@ func NewNameSystem(r routing.IpfsRouting) NameSystem { "dht": newRoutingResolver(r), }, publishers: map[string]Publisher{ - "/ipns/": NewRoutingPublisher(r), + "/ipns/": NewRoutingPublisher(r, ds), }, } } diff --git a/namesys/publisher.go b/namesys/publisher.go index 521ce4cc8..e31217dff 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -18,6 +18,7 @@ import ( path "github.com/ipfs/go-ipfs/path" pin "github.com/ipfs/go-ipfs/pin" routing "github.com/ipfs/go-ipfs/routing" + dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" u "github.com/ipfs/go-ipfs/util" @@ -37,11 +38,15 @@ var PublishPutValTimeout = time.Minute // routing system. type ipnsPublisher struct { routing routing.IpfsRouting + ds ds.Datastore } // NewRoutingPublisher constructs a publisher for the IPFS Routing name system. -func NewRoutingPublisher(route routing.IpfsRouting) *ipnsPublisher { - return &ipnsPublisher{routing: route} +func NewRoutingPublisher(route routing.IpfsRouting, ds ds.Datastore) *ipnsPublisher { + if ds == nil { + panic("nil datastore") + } + return &ipnsPublisher{routing: route, ds: ds} } // Publish implements Publisher. Accepts a keypair and a value, @@ -62,22 +67,58 @@ func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value _, ipnskey := IpnsKeysForID(id) - // get previous records sequence number, and add one to it - var seqnum uint64 - prevrec, err := p.routing.GetValues(ctx, ipnskey, 0) + // get previous records sequence number + seqnum, err := p.getPreviousSeqNo(ctx, ipnskey) + if err != nil { + return err + } + + // increment it + seqnum++ + + return PutRecordToRouting(ctx, k, value, seqnum, eol, p.routing, id) +} + +func (p *ipnsPublisher) getPreviousSeqNo(ctx context.Context, ipnskey key.Key) (uint64, error) { + prevrec, err := p.ds.Get(ipnskey.DsKey()) + if err != nil && err != ds.ErrNotFound { + // None found, lets start at zero! + return 0, err + } + var val []byte if err == nil { - e := new(pb.IpnsEntry) - err := proto.Unmarshal(prevrec[0].Val, e) + prbytes, ok := prevrec.([]byte) + if !ok { + return 0, fmt.Errorf("unexpected type returned from datastore: %#v", prevrec) + } + dhtrec := new(dhtpb.Record) + err := proto.Unmarshal(prbytes, dhtrec) if err != nil { - return err + return 0, err } - seqnum = e.GetSequence() + 1 - } else if err != ds.ErrNotFound { - return err + val = dhtrec.GetValue() + } else { + // try and check the dht for a record + ctx, cancel := context.WithTimeout(ctx, time.Second*30) + defer cancel() + + rv, err := p.routing.GetValue(ctx, ipnskey) + if err != nil { + // no such record found, start at zero! + return 0, nil + } + + val = rv } - return PutRecordToRouting(ctx, k, value, seqnum, eol, p.routing, id) + e := new(pb.IpnsEntry) + err = proto.Unmarshal(val, e) + if err != nil { + return 0, err + } + + return e.GetSequence(), nil } func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.IpfsRouting, id peer.ID) error { diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 66d137a70..ef7fcf7e3 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -54,7 +54,7 @@ func TestRepublish(t *testing.T) { // have one node publish a record that is valid for 1 second publisher := nodes[3] p := path.FromString("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") // does not need to be valid - rp := namesys.NewRoutingPublisher(publisher.Routing) + rp := namesys.NewRoutingPublisher(publisher.Routing, publisher.Repo.Datastore()) err := rp.PublishWithEOL(ctx, publisher.PrivateKey, p, time.Now().Add(time.Second)) if err != nil { t.Fatal(err) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 3dde211ad..4d81751f1 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -1,10 +1,14 @@ package namesys import ( + "errors" "testing" + "time" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" + peer "github.com/ipfs/go-ipfs/p2p/peer" path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" u "github.com/ipfs/go-ipfs/util" @@ -13,9 +17,10 @@ import ( func TestRoutingResolve(t *testing.T) { d := mockrouting.NewServer().Client(testutil.RandIdentityOrFatal(t)) + dstore := ds.NewMapDatastore() resolver := NewRoutingResolver(d) - publisher := NewRoutingPublisher(d) + publisher := NewRoutingPublisher(d, dstore) privk, pubk, err := testutil.RandTestKeyPair(512) if err != nil { @@ -43,3 +48,90 @@ func TestRoutingResolve(t *testing.T) { t.Fatal("Got back incorrect value.") } } + +func TestPrexistingExpiredRecord(t *testing.T) { + dstore := ds.NewMapDatastore() + d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore) + + resolver := NewRoutingResolver(d) + publisher := NewRoutingPublisher(d, dstore) + + privk, pubk, err := testutil.RandTestKeyPair(512) + if err != nil { + t.Fatal(err) + } + + id, err := peer.IDFromPublicKey(pubk) + if err != nil { + t.Fatal(err) + } + + // Make an expired record and put it in the datastore + h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN") + eol := time.Now().Add(time.Hour * -1) + err = PutRecordToRouting(context.Background(), privk, h, 0, eol, d, id) + if err != nil { + t.Fatal(err) + } + + // Now, with an old record in the system already, try and publish a new one + err = publisher.Publish(context.Background(), privk, h) + if err != nil { + t.Fatal(err) + } + + err = verifyCanResolve(resolver, id.Pretty(), h) + if err != nil { + t.Fatal(err) + } +} + +func TestPrexistingRecord(t *testing.T) { + dstore := ds.NewMapDatastore() + d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore) + + resolver := NewRoutingResolver(d) + publisher := NewRoutingPublisher(d, dstore) + + privk, pubk, err := testutil.RandTestKeyPair(512) + if err != nil { + t.Fatal(err) + } + + id, err := peer.IDFromPublicKey(pubk) + if err != nil { + t.Fatal(err) + } + + // Make a good record and put it in the datastore + h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN") + eol := time.Now().Add(time.Hour) + err = PutRecordToRouting(context.Background(), privk, h, 0, eol, d, id) + if err != nil { + t.Fatal(err) + } + + // Now, with an old record in the system already, try and publish a new one + err = publisher.Publish(context.Background(), privk, h) + if err != nil { + t.Fatal(err) + } + + err = verifyCanResolve(resolver, id.Pretty(), h) + if err != nil { + t.Fatal(err) + } +} + +func verifyCanResolve(r Resolver, name string, exp path.Path) error { + res, err := r.Resolve(context.Background(), name) + if err != nil { + return err + } + + if res != exp { + return errors.New("got back wrong record!") + } + + return nil +} From 088995057c82e8d78e7b69a4723f98e27d0928cd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Oct 2015 11:22:28 -0700 Subject: [PATCH 0988/3817] replace imports with absolute path instead of using symlink License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@43380f1573ac394d7fb7e060cd4d150e117e4ff0 --- routing/dht/dht.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/query.go | 2 +- routing/kbucket/table.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/none/none_client.go | 2 +- routing/offline/offline.go | 2 +- routing/record/record.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/standard.go | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 5de2d21da..7b8b449ae 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -18,7 +18,7 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index d9f6a2073..3fd63902f 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -6,7 +6,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" inet "github.com/ipfs/go-ipfs/p2p/net" peer "github.com/ipfs/go-ipfs/p2p/peer" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/query.go b/routing/dht/query.go index b1bec20bb..3c89b3fe2 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -11,7 +11,7 @@ import ( u "github.com/ipfs/go-ipfs/util" pset "github.com/ipfs/go-ipfs/util/peerset" todoctr "github.com/ipfs/go-ipfs/util/todocounter" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" ctxproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 45e438635..ed3b6d350 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -8,7 +8,7 @@ import ( "time" peer "github.com/ipfs/go-ipfs/p2p/peer" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var log = logging.Logger("table") diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 9d9a1f6da..61005aa9f 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -14,7 +14,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" "github.com/ipfs/go-ipfs/util/testutil" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var log = logging.Logger("mockrouter") diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 49df5870a..ca2eafeec 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -9,7 +9,7 @@ import ( p2phost "github.com/ipfs/go-ipfs/p2p/host" peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 22aef75b3..b2b636b77 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -13,7 +13,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var log = logging.Logger("offlinerouting") diff --git a/routing/record/record.go b/routing/record/record.go index 61dd995ac..2de5f0856 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ci "github.com/ipfs/go-ipfs/p2p/crypto" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var log = logging.Logger("routing/record") diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 923d530eb..c22b55b16 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -14,7 +14,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index cba08c2ea..7213ef436 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -12,7 +12,7 @@ import ( peer "github.com/ipfs/go-ipfs/p2p/peer" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) const ProtocolSNR = "/ipfs/supernoderouting" From 3a20deeabf872c37c7a3550a7a4391e49e12b5ef Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Oct 2015 11:22:28 -0700 Subject: [PATCH 0989/3817] replace imports with absolute path instead of using symlink License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@40707144ea9add435acffb9bcd62e0afdeb3f8c0 --- unixfs/mod/dagmodifier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index c39714398..4b0ff4720 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -19,7 +19,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var ErrSeekFail = errors.New("failed to seek properly") From fc63ad9c93931bb6dc222f2fcdb1787359d0f810 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Oct 2015 11:22:28 -0700 Subject: [PATCH 0990/3817] replace imports with absolute path instead of using symlink License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@0a3f52e5d2ae24a0204d213d83a6f52512f94412 --- namesys/republisher/repub.go | 2 +- namesys/routing.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 4fece5721..cf1d5a218 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -18,7 +18,7 @@ import ( goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" gpctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/routing.go b/namesys/routing.go index 9946ed560..3d49c564c 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -11,7 +11,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var log = logging.Logger("namesys") From f87c4cc499aa9432103bdffe2f72fdcbc1f047cd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Oct 2015 11:22:28 -0700 Subject: [PATCH 0991/3817] replace imports with absolute path instead of using symlink License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@ec17d2268a9335db00eeab9089a9ecacd28a2f3e --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 4eae02290..e9e50a554 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -9,7 +9,7 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var log = logging.Logger("merkledag") From 05c2d5fab3042d65c75be117ceb91dd0077ff428 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Oct 2015 11:22:28 -0700 Subject: [PATCH 0992/3817] replace imports with absolute path instead of using symlink License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@79880d628448ac20ef0a9ea49d5600514a38add2 --- pinning/pinner/pin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index a82b93f82..5f6f46679 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -14,7 +14,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var log = logging.Logger("pin") From ba2417b1adef0463db0a89d31b50757c757f86d8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Oct 2015 11:22:28 -0700 Subject: [PATCH 0993/3817] replace imports with absolute path instead of using symlink License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@3a1ca24b72a12a0a1af28d982b5f465f0d0c604a --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 5558bf3b2..cb910ff8c 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -12,7 +12,7 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var log = logging.Logger("blockstore") From e3eeec383770381de5572ec7fb67c340624dc4e7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Oct 2015 11:22:28 -0700 Subject: [PATCH 0994/3817] replace imports with absolute path instead of using symlink License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@047a080b409214aa2508e8116290b3f2d3414226 --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index f13c090e4..ee2973f25 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -11,7 +11,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var log = logging.Logger("blockservice") From bbe3cbbe568eaf52ce2af6e608daf8fa1881ea9f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Oct 2015 11:22:28 -0700 Subject: [PATCH 0995/3817] replace imports with absolute path instead of using symlink License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-chunker@1b4493df5078ab9e63bcab18acc5a1bd1787ac33 --- chunker/splitting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 31b5c03e5..47cc679aa 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -4,7 +4,7 @@ package chunk import ( "io" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var log = logging.Logger("chunk") From 8f6a09bfea3700bbfb8f2b40311f1f01eec20a57 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Oct 2015 11:22:28 -0700 Subject: [PATCH 0996/3817] replace imports with absolute path instead of using symlink License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@e4b7ad717295da6bcc960fdce8f37f252d7f8fb7 --- path/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/resolver.go b/path/resolver.go index 6c3ae8a97..4f4db6dce 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -11,7 +11,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" merkledag "github.com/ipfs/go-ipfs/merkledag" - logging "github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0" + logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" ) var log = logging.Logger("path") From dfae38a81ec323f0823d2d8638f5830179761b37 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 4 Oct 2015 02:59:23 -0400 Subject: [PATCH 0997/3817] fix vendor path. We need to have a test case that fails when any dep path is not vendored. (until we use gx fully that is, and vendor everything with it) License: MIT Signed-off-by: Juan Batiz-Benet This commit was moved from ipfs/go-ipfs-routing@da928b58df930ffcf54c68d04f0818d70ece8039 --- routing/dht/pb/dht.pb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/pb/dht.pb.go b/routing/dht/pb/dht.pb.go index 4b8501180..2d7ad1d9d 100644 --- a/routing/dht/pb/dht.pb.go +++ b/routing/dht/pb/dht.pb.go @@ -14,7 +14,7 @@ It has these top-level messages: */ package dht_pb -import proto "github.com/gogo/protobuf/proto" +import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. From 3a853d8902634822b9ab2bc6c874eb0616364c52 Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 14 Sep 2015 06:52:47 +0700 Subject: [PATCH 0998/3817] Decompose maybeGzwriter License: MIT Signed-off-by: rht This commit was moved from ipfs/go-unixfs@291d62bbce567ed4a85c00ac5afbe5631bc6c668 --- unixfs/archive/archive.go | 62 +++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index e1faf5a33..1fbd5ccd9 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -36,67 +36,71 @@ func DagArchive(ctx cxt.Context, nd *mdag.Node, name string, dag mdag.DAGService // need to connect a writer to a reader piper, pipew := io.Pipe() + checkErrAndClosePipe := func(err error) bool { + if err != nil { + pipew.CloseWithError(err) + return true + } + return false + } // use a buffered writer to parallelize task bufw := bufio.NewWriterSize(pipew, DefaultBufSize) // compression determines whether to use gzip compression. - var maybeGzw io.WriteCloser - var err error - if compression != gzip.NoCompression { - maybeGzw, err = gzip.NewWriterLevel(bufw, compression) - if err != nil { - pipew.CloseWithError(err) - return nil, err + maybeGzw, err := newMaybeGzWriter(bufw, compression) + if checkErrAndClosePipe(err) { + return nil, err + } + + closeGzwAndPipe := func() { + if err := maybeGzw.Close(); checkErrAndClosePipe(err) { + return } - } else { - maybeGzw = &identityWriteCloser{bufw} + if err := bufw.Flush(); checkErrAndClosePipe(err) { + return + } + pipew.Close() // everything seems to be ok. } if !archive && compression != gzip.NoCompression { // the case when the node is a file dagr, err := uio.NewDagReader(ctx, nd, dag) - if err != nil { - pipew.CloseWithError(err) + if checkErrAndClosePipe(err) { return nil, err } go func() { - if _, err := dagr.WriteTo(maybeGzw); err != nil { - pipew.CloseWithError(err) - return - } - maybeGzw.Close() - if err := bufw.Flush(); err != nil { - pipew.CloseWithError(err) + if _, err := dagr.WriteTo(maybeGzw); checkErrAndClosePipe(err) { return } - pipew.Close() // everything seems to be ok. + closeGzwAndPipe() // everything seems to be ok }() } else { // the case for 1. archive, and 2. not archived and not compressed, in which tar is used anyway as a transport format // construct the tar writer w, err := tar.NewWriter(ctx, dag, archive, compression, maybeGzw) - if err != nil { + if checkErrAndClosePipe(err) { return nil, err } go func() { // write all the nodes recursively - if err := w.WriteNode(nd, filename); err != nil { - pipew.CloseWithError(err) + if err := w.WriteNode(nd, filename); checkErrAndClosePipe(err) { return } - w.Close() - maybeGzw.Close() - if err := bufw.Flush(); err != nil { - pipew.CloseWithError(err) - return - } - pipew.Close() // everything seems to be ok. + w.Close() // close tar writer + closeGzwAndPipe() // everything seems to be ok }() } return piper, nil } + +func newMaybeGzWriter(w io.Writer, compression int) (io.WriteCloser, error) { + if compression != gzip.NoCompression { + return gzip.NewWriterLevel(w, compression) + } + return &identityWriteCloser{w}, nil +} From 923ea5208c6363aabea907b4817cf17758f8c6f5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 27 Oct 2015 10:47:32 -0700 Subject: [PATCH 0999/3817] update code to use new logging changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@a8fe4e87c2889f35f1b0f558486bfb7e0cbdd0c9 --- path/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/resolver.go b/path/resolver.go index 4f4db6dce..e58749cfe 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -11,7 +11,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" merkledag "github.com/ipfs/go-ipfs/merkledag" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var log = logging.Logger("path") From 7b15f8fdbcdf15489734485aab1894a8e6eaa426 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 27 Oct 2015 10:47:32 -0700 Subject: [PATCH 1000/3817] update code to use new logging changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@1e2e6c27dfd2e902a202a2c92495961f4fe87073 --- namesys/republisher/repub.go | 2 +- namesys/routing.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index cf1d5a218..1dadd3731 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -18,7 +18,7 @@ import ( goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" gpctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/routing.go b/namesys/routing.go index 3d49c564c..63a323cae 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -11,7 +11,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var log = logging.Logger("namesys") From 5318ff274e9221ebeb9a77906e2fa4928aaa904a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 27 Oct 2015 10:47:32 -0700 Subject: [PATCH 1001/3817] update code to use new logging changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@6ccb17a41574fbe8a9e3b914ef362a7fec3401fe --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index e9e50a554..32e666fef 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -9,7 +9,7 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var log = logging.Logger("merkledag") From fc7089743d1a909023a74df536e6b44ff89deeeb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 27 Oct 2015 10:47:32 -0700 Subject: [PATCH 1002/3817] update code to use new logging changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@bcaf4e73b078478163973099ae8fdf0e1fb071e9 --- pinning/pinner/pin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 5f6f46679..89e617a45 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -14,7 +14,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var log = logging.Logger("pin") From 82fc7d6c5683623e81572eadf2df21d86ac19f6e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 27 Oct 2015 10:47:32 -0700 Subject: [PATCH 1003/3817] update code to use new logging changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@46bbbfc36fcdf1691df0da78ae7fddaac85029f8 --- unixfs/mod/dagmodifier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 4b0ff4720..a249a515a 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -19,7 +19,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var ErrSeekFail = errors.New("failed to seek properly") From 8e619672595e9e8ae77d5b3a87a05f0b33352c31 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 27 Oct 2015 10:47:32 -0700 Subject: [PATCH 1004/3817] update code to use new logging changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@8bf7981fa789773c4436c20566fa51a202dbcf35 --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index cb910ff8c..c747ce4c8 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -12,7 +12,7 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var log = logging.Logger("blockstore") From 48e7905624d13ff8ba40b951f27b5fe6f6f5990d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 27 Oct 2015 10:47:32 -0700 Subject: [PATCH 1005/3817] update code to use new logging changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@c20e26d47948275c2aaf47433ef0002145ca5b11 --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index ee2973f25..62efa1418 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -11,7 +11,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var log = logging.Logger("blockservice") From 96ad58e35a0c3d86d5bcf5118027fa21cbdb0d14 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 27 Oct 2015 10:47:32 -0700 Subject: [PATCH 1006/3817] update code to use new logging changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-chunker@4e51253ee701b57e2f2fd2f3808f1a6da9c61eb2 --- chunker/splitting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 47cc679aa..c10d8d6ff 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -4,7 +4,7 @@ package chunk import ( "io" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var log = logging.Logger("chunk") From f5eb891968fbb0d7793a782bd70c790bae6d3a35 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 27 Oct 2015 10:47:32 -0700 Subject: [PATCH 1007/3817] update code to use new logging changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@4e65f19582ffe35509f73e4245776d019f0590e0 --- routing/dht/dht.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/query.go | 2 +- routing/kbucket/table.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/none/none_client.go | 2 +- routing/offline/offline.go | 2 +- routing/record/record.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/standard.go | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 7b8b449ae..c085616bd 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -18,7 +18,7 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 3fd63902f..87896a419 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -6,7 +6,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" inet "github.com/ipfs/go-ipfs/p2p/net" peer "github.com/ipfs/go-ipfs/p2p/peer" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/query.go b/routing/dht/query.go index 3c89b3fe2..5318897ee 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -11,7 +11,7 @@ import ( u "github.com/ipfs/go-ipfs/util" pset "github.com/ipfs/go-ipfs/util/peerset" todoctr "github.com/ipfs/go-ipfs/util/todocounter" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" ctxproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index ed3b6d350..cbfddcfb7 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -8,7 +8,7 @@ import ( "time" peer "github.com/ipfs/go-ipfs/p2p/peer" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var log = logging.Logger("table") diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 61005aa9f..14202c03b 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -14,7 +14,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" "github.com/ipfs/go-ipfs/util/testutil" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var log = logging.Logger("mockrouter") diff --git a/routing/none/none_client.go b/routing/none/none_client.go index ca2eafeec..3bccc5873 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -9,7 +9,7 @@ import ( p2phost "github.com/ipfs/go-ipfs/p2p/host" peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index b2b636b77..24a7a1ec1 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -13,7 +13,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var log = logging.Logger("offlinerouting") diff --git a/routing/record/record.go b/routing/record/record.go index 2de5f0856..055729df7 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ci "github.com/ipfs/go-ipfs/p2p/crypto" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var log = logging.Logger("routing/record") diff --git a/routing/supernode/client.go b/routing/supernode/client.go index c22b55b16..cc131ca64 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -14,7 +14,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 7213ef436..81b42cc5a 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -12,7 +12,7 @@ import ( peer "github.com/ipfs/go-ipfs/p2p/peer" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" - logging "github.com/ipfs/go-ipfs/vendor/QmXJkcEXB6C9h6Ytb6rrUTFU56Ro62zxgrbxTT3dgjQGA8/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" ) const ProtocolSNR = "/ipfs/supernoderouting" From 9d0b66c607244ba494e8d12d98e1a71150025c76 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 22 Oct 2015 23:32:57 +0200 Subject: [PATCH 1008/3817] fixing putHandler for --writable http gateway I disabled this a long time ago and never refactored it. About time. License: MIT Signed-off-by: Henry This commit was moved from ipfs/go-path@47ddd064a51ae60c309f29c94e9d750517865ffc --- path/resolver.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index 4f4db6dce..6390bdeca 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -22,12 +22,12 @@ var ErrNoComponents = errors.New( // ErrNoLink is returned when a link is not found in a path type ErrNoLink struct { - name string - node mh.Multihash + Name string + Node mh.Multihash } func (e ErrNoLink) Error() string { - return fmt.Sprintf("no link named %q under %s", e.name, e.node.B58String()) + return fmt.Sprintf("no link named %q under %s", e.Name, e.Node.B58String()) } // Resolver provides path resolution to IPFS @@ -124,7 +124,7 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd *merkledag.Node, names if next == "" { n, _ := nd.Multihash() - return result, ErrNoLink{name: name, node: n} + return result, ErrNoLink{Name: name, Node: n} } if nlink.Node == nil { From 6700847fac72b1ec4e66d1e0034826878f65889e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 22 Oct 2015 16:27:31 -0700 Subject: [PATCH 1009/3817] cache ipns entries to speed things up a little License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@c885d48fd05ef25ff1b463b557b1876fb635948c --- namesys/namesys.go | 6 +- namesys/pb/namesys.pb.go | 8 +++ namesys/pb/namesys.proto | 2 + namesys/publisher.go | 18 +++++ namesys/republisher/repub_test.go | 4 ++ namesys/resolve_test.go | 6 +- namesys/routing.go | 110 ++++++++++++++++++++++++++---- 7 files changed, 137 insertions(+), 17 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 12b73218b..c61d3496b 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -26,12 +26,12 @@ type mpns struct { } // NewNameSystem will construct the IPFS naming system based on Routing -func NewNameSystem(r routing.IpfsRouting, ds ds.Datastore) NameSystem { +func NewNameSystem(r routing.IpfsRouting, ds ds.Datastore, cachesize int) NameSystem { return &mpns{ resolvers: map[string]resolver{ "dns": newDNSResolver(), "proquint": new(ProquintResolver), - "dht": newRoutingResolver(r), + "dht": NewRoutingResolver(r, cachesize), }, publishers: map[string]Publisher{ "/ipns/": NewRoutingPublisher(r, ds), @@ -39,6 +39,8 @@ func NewNameSystem(r routing.IpfsRouting, ds ds.Datastore) NameSystem { } } +const DefaultResolverCacheTTL = time.Minute + // Resolve implements Resolver. func (ns *mpns) Resolve(ctx context.Context, name string) (path.Path, error) { return ns.ResolveN(ctx, name, DefaultDepthLimit) diff --git a/namesys/pb/namesys.pb.go b/namesys/pb/namesys.pb.go index 4d99a5e0a..0508e772d 100644 --- a/namesys/pb/namesys.pb.go +++ b/namesys/pb/namesys.pb.go @@ -57,6 +57,7 @@ type IpnsEntry struct { ValidityType *IpnsEntry_ValidityType `protobuf:"varint,3,opt,name=validityType,enum=namesys.pb.IpnsEntry_ValidityType" json:"validityType,omitempty"` Validity []byte `protobuf:"bytes,4,opt,name=validity" json:"validity,omitempty"` Sequence *uint64 `protobuf:"varint,5,opt,name=sequence" json:"sequence,omitempty"` + Ttl *uint64 `protobuf:"varint,6,opt,name=ttl" json:"ttl,omitempty"` XXX_unrecognized []byte `json:"-"` } @@ -99,6 +100,13 @@ func (m *IpnsEntry) GetSequence() uint64 { return 0 } +func (m *IpnsEntry) GetTtl() uint64 { + if m != nil && m.Ttl != nil { + return *m.Ttl + } + return 0 +} + func init() { proto.RegisterEnum("namesys.pb.IpnsEntry_ValidityType", IpnsEntry_ValidityType_name, IpnsEntry_ValidityType_value) } diff --git a/namesys/pb/namesys.proto b/namesys/pb/namesys.proto index 242f77bf2..d6eaf3243 100644 --- a/namesys/pb/namesys.proto +++ b/namesys/pb/namesys.proto @@ -12,4 +12,6 @@ message IpnsEntry { optional bytes validity = 4; optional uint64 sequence = 5; + + optional uint64 ttl = 6; } diff --git a/namesys/publisher.go b/namesys/publisher.go index e31217dff..78d7bb37c 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -121,6 +121,19 @@ func (p *ipnsPublisher) getPreviousSeqNo(ctx context.Context, ipnskey key.Key) ( return e.GetSequence(), nil } +// setting the TTL on published records is an experimental feature. +// as such, i'm using the context to wire it through to avoid changing too +// much code along the way. +func checkCtxTTL(ctx context.Context) (time.Duration, bool) { + v := ctx.Value("ipns-publish-ttl") + if v == nil { + return 0, false + } + + d, ok := v.(time.Duration) + return d, ok +} + func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.IpfsRouting, id peer.ID) error { namekey, ipnskey := IpnsKeysForID(id) entry, err := CreateRoutingEntryData(k, value, seqnum, eol) @@ -128,6 +141,11 @@ func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqn return err } + ttl, ok := checkCtxTTL(ctx) + if ok { + entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds())) + } + err = PublishEntry(ctx, r, ipnskey, entry) if err != nil { return err diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index ef7fcf7e3..92c224a73 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -18,6 +18,8 @@ import ( ) func TestRepublish(t *testing.T) { + // set cache life to zero for testing low-period repubs + ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -34,6 +36,8 @@ func TestRepublish(t *testing.T) { t.Fatal(err) } + nd.Namesys = namesys.NewNameSystem(nd.Routing, nd.Repo.Datastore(), 0) + nodes = append(nodes, nd) } diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 4d81751f1..11145ff01 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -19,7 +19,7 @@ func TestRoutingResolve(t *testing.T) { d := mockrouting.NewServer().Client(testutil.RandIdentityOrFatal(t)) dstore := ds.NewMapDatastore() - resolver := NewRoutingResolver(d) + resolver := NewRoutingResolver(d, 0) publisher := NewRoutingPublisher(d, dstore) privk, pubk, err := testutil.RandTestKeyPair(512) @@ -53,7 +53,7 @@ func TestPrexistingExpiredRecord(t *testing.T) { dstore := ds.NewMapDatastore() d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore) - resolver := NewRoutingResolver(d) + resolver := NewRoutingResolver(d, 0) publisher := NewRoutingPublisher(d, dstore) privk, pubk, err := testutil.RandTestKeyPair(512) @@ -90,7 +90,7 @@ func TestPrexistingRecord(t *testing.T) { dstore := ds.NewMapDatastore() d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore) - resolver := NewRoutingResolver(d) + resolver := NewRoutingResolver(d, 0) publisher := NewRoutingPublisher(d, dstore) privk, pubk, err := testutil.RandTestKeyPair(512) diff --git a/namesys/routing.go b/namesys/routing.go index 63a323cae..a25fa19f6 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -2,16 +2,19 @@ package namesys import ( "fmt" + "time" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + lru "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + u "github.com/ipfs/go-ipfs/util" ) var log = logging.Logger("namesys") @@ -19,25 +22,84 @@ var log = logging.Logger("namesys") // routingResolver implements NSResolver for the main IPFS SFS-like naming type routingResolver struct { routing routing.IpfsRouting + + cache *lru.Cache } -// NewRoutingResolver constructs a name resolver using the IPFS Routing system -// to implement SFS-like naming on top. -func NewRoutingResolver(route routing.IpfsRouting) Resolver { - if route == nil { - panic("attempt to create resolver with nil routing system") +func (r *routingResolver) cacheGet(name string) (path.Path, bool) { + if r.cache == nil { + return "", false + } + + ientry, ok := r.cache.Get(name) + if !ok { + return "", false } - return &routingResolver{routing: route} + entry, ok := ientry.(cacheEntry) + if !ok { + // should never happen, purely for sanity + log.Panicf("unexpected type %T in cache for %q.", ientry, name) + } + + if time.Now().Before(entry.eol) { + return entry.val, true + } + + r.cache.Remove(name) + + return "", false } -// newRoutingResolver returns a resolver instead of a Resolver. -func newRoutingResolver(route routing.IpfsRouting) resolver { +func (r *routingResolver) cacheSet(name string, val path.Path, rec *pb.IpnsEntry) { + if r.cache == nil { + return + } + + // if completely unspecified, just use one minute + ttl := DefaultResolverCacheTTL + if rec.Ttl != nil { + recttl := time.Duration(rec.GetTtl()) + if recttl >= 0 { + ttl = recttl + } + } + + cacheTil := time.Now().Add(ttl) + eol, ok := checkEOL(rec) + if ok && eol.Before(cacheTil) { + cacheTil = eol + } + + r.cache.Add(name, cacheEntry{ + val: val, + eol: cacheTil, + }) +} + +type cacheEntry struct { + val path.Path + eol time.Time +} + +// NewRoutingResolver constructs a name resolver using the IPFS Routing system +// to implement SFS-like naming on top. +// cachesize is the limit of the number of entries in the lru cache. Setting it +// to '0' will disable caching. +func NewRoutingResolver(route routing.IpfsRouting, cachesize int) *routingResolver { if route == nil { panic("attempt to create resolver with nil routing system") } - return &routingResolver{routing: route} + var cache *lru.Cache + if cachesize > 0 { + cache, _ = lru.New(cachesize) + } + + return &routingResolver{ + routing: route, + cache: cache, + } } // Resolve implements Resolver. @@ -54,6 +116,11 @@ func (r *routingResolver) ResolveN(ctx context.Context, name string, depth int) // resolve SFS-like names. func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) { log.Debugf("RoutingResolve: '%s'", name) + cached, ok := r.cacheGet(name) + if ok { + return cached, nil + } + hash, err := mh.FromB58String(name) if err != nil { log.Warning("RoutingResolve: bad input hash: [%s]\n", name) @@ -98,10 +165,29 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa valh, err := mh.Cast(entry.GetValue()) if err != nil { // Not a multihash, probably a new record - return path.ParsePath(string(entry.GetValue())) + p, err := path.ParsePath(string(entry.GetValue())) + if err != nil { + return "", err + } + + r.cacheSet(name, p, entry) + return p, nil } else { // Its an old style multihash record log.Warning("Detected old style multihash record") - return path.FromKey(key.Key(valh)), nil + p := path.FromKey(key.Key(valh)) + r.cacheSet(name, p, entry) + return p, nil + } +} + +func checkEOL(e *pb.IpnsEntry) (time.Time, bool) { + if e.GetValidityType() == pb.IpnsEntry_EOL { + eol, err := u.ParseRFC3339(string(e.GetValidity())) + if err != nil { + return time.Time{}, false + } + return eol, true } + return time.Time{}, false } From b2ba9c20f0e66aab9b1bb39c00429d4188662616 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 2 Nov 2015 23:19:01 -0800 Subject: [PATCH 1010/3817] set data and links nil if not present License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@b22eae6bfbe083a9184fba2a4e57187d9d2d313e --- ipld/merkledag/coding.go | 8 ++++++-- ipld/merkledag/node.go | 12 ++++++++---- ipld/merkledag/utils/utils_test.go | 4 ++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 7baf863c8..b678c3b44 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -50,7 +50,9 @@ func (n *Node) Marshal() ([]byte, error) { func (n *Node) getPBNode() *pb.PBNode { pbn := &pb.PBNode{} - pbn.Links = make([]*pb.PBLink, len(n.Links)) + if len(n.Links) > 0 { + pbn.Links = make([]*pb.PBLink, len(n.Links)) + } sort.Stable(LinkSlice(n.Links)) // keep links sorted for i, l := range n.Links { @@ -60,7 +62,9 @@ func (n *Node) getPBNode() *pb.PBNode { pbn.Links[i].Hash = []byte(l.Hash) } - pbn.Data = n.Data + if len(n.Data) > 0 { + pbn.Data = n.Data + } return pbn } diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 8d06077c0..f84695f91 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -176,11 +176,15 @@ func (n *Node) GetLinkedNode(ctx context.Context, ds DAGService, name string) (* // NOTE: does not make copies of Node objects in the links. func (n *Node) Copy() *Node { nnode := new(Node) - nnode.Data = make([]byte, len(n.Data)) - copy(nnode.Data, n.Data) + if len(n.Data) > 0 { + nnode.Data = make([]byte, len(n.Data)) + copy(nnode.Data, n.Data) + } - nnode.Links = make([]*Link, len(n.Links)) - copy(nnode.Links, n.Links) + if len(n.Links) > 0 { + nnode.Links = make([]*Link, len(n.Links)) + copy(nnode.Links, n.Links) + } return nnode } diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index f41427cf2..18839bf8f 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -85,8 +85,8 @@ func TestInsertNode(t *testing.T) { t.Fatal(err) } - if k.B58String() != "QmThorWojP6YzLJwDukxiYCoKQSwyrMCvdt4WZ6rPm221t" { - t.Fatal("output was different than expected") + if k.B58String() != "QmZ8yeT9uD6ouJPNAYt62XffYuXBT6b4mP4obRSE9cJrSt" { + t.Fatal("output was different than expected: ", k) } } From c50621ab73a836fa9d50636846368368c68322e3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 29 Oct 2015 21:22:53 -0700 Subject: [PATCH 1011/3817] vendor logging lib update License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@51d6f29fc88a4d9ec2ce5d2304f08b99d06791aa --- routing/dht/dht.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/query.go | 2 +- routing/kbucket/table.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/none/none_client.go | 2 +- routing/offline/offline.go | 2 +- routing/record/record.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/standard.go | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index c085616bd..3f50652fd 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -18,7 +18,7 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 87896a419..f780b1b05 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -6,7 +6,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" inet "github.com/ipfs/go-ipfs/p2p/net" peer "github.com/ipfs/go-ipfs/p2p/peer" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/query.go b/routing/dht/query.go index 5318897ee..86b33a8db 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -11,7 +11,7 @@ import ( u "github.com/ipfs/go-ipfs/util" pset "github.com/ipfs/go-ipfs/util/peerset" todoctr "github.com/ipfs/go-ipfs/util/todocounter" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" ctxproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index cbfddcfb7..044d3a2c2 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -8,7 +8,7 @@ import ( "time" peer "github.com/ipfs/go-ipfs/p2p/peer" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var log = logging.Logger("table") diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 14202c03b..e7aa44968 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -14,7 +14,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" "github.com/ipfs/go-ipfs/util/testutil" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var log = logging.Logger("mockrouter") diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 3bccc5873..efa0b8a99 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -9,7 +9,7 @@ import ( p2phost "github.com/ipfs/go-ipfs/p2p/host" peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 24a7a1ec1..54f2bb87f 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -13,7 +13,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var log = logging.Logger("offlinerouting") diff --git a/routing/record/record.go b/routing/record/record.go index 055729df7..944f615d0 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ci "github.com/ipfs/go-ipfs/p2p/crypto" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var log = logging.Logger("routing/record") diff --git a/routing/supernode/client.go b/routing/supernode/client.go index cc131ca64..5b7c4a306 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -14,7 +14,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 81b42cc5a..279cbe7de 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -12,7 +12,7 @@ import ( peer "github.com/ipfs/go-ipfs/p2p/peer" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) const ProtocolSNR = "/ipfs/supernoderouting" From 20c8a19bd99c2f4bce92de78530c06d3fbf94d29 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 29 Oct 2015 21:22:53 -0700 Subject: [PATCH 1012/3817] vendor logging lib update License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@548ae27c5af99409a62904d0cdcf80d7d4efd5fa --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 32e666fef..da921ed09 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -9,7 +9,7 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var log = logging.Logger("merkledag") From 768361581e61fb856669fedb2a7f56dd88b0ae68 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 29 Oct 2015 21:22:53 -0700 Subject: [PATCH 1013/3817] vendor logging lib update License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@d1c4983b31cbc99cad261879d15197ca42d6917f --- namesys/republisher/repub.go | 2 +- namesys/routing.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 1dadd3731..b633f454c 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -18,7 +18,7 @@ import ( goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" gpctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/routing.go b/namesys/routing.go index a25fa19f6..85ef498e7 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -8,13 +8,13 @@ import ( lru "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var log = logging.Logger("namesys") From a08f1c93d21bd3b861049a5336dd5d3cb8a92b86 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 29 Oct 2015 21:22:53 -0700 Subject: [PATCH 1014/3817] vendor logging lib update License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@e6efb04837f48008fb9f807c05aaa7910b8496ed --- pinning/pinner/pin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 89e617a45..53d965e9b 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -14,7 +14,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var log = logging.Logger("pin") From 0bd89e2ded50a47f62b5f0cc4750c7d47a1b85b0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 29 Oct 2015 21:22:53 -0700 Subject: [PATCH 1015/3817] vendor logging lib update License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@0282215421c030b204097d0e0728093fdbdbdf1c --- unixfs/mod/dagmodifier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index a249a515a..5f5eddc90 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -19,7 +19,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var ErrSeekFail = errors.New("failed to seek properly") From e1963bec267a05f8f9f03797df222db219955022 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 29 Oct 2015 21:22:53 -0700 Subject: [PATCH 1016/3817] vendor logging lib update License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@9ccb34d29697ef57aeb9e0e1a3c48c7659a7212b --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index c747ce4c8..c4eefaddf 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -12,7 +12,7 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var log = logging.Logger("blockstore") From 3021d17163d034d322c3b1e7f387f1e8119dcd58 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 29 Oct 2015 21:22:53 -0700 Subject: [PATCH 1017/3817] vendor logging lib update License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@7cc2792ee1c93b0e1b750cc3635f9ccbca611c83 --- path/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/resolver.go b/path/resolver.go index 62ca5cac1..4ed7b67e9 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -11,7 +11,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" merkledag "github.com/ipfs/go-ipfs/merkledag" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var log = logging.Logger("path") From 6c285f51de6b1a9d4fcf49a273b416132e0e7340 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 29 Oct 2015 21:22:53 -0700 Subject: [PATCH 1018/3817] vendor logging lib update License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@07f41b86928d2c08e729dfa3a4b8f75a61212f7a --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 62efa1418..9c5cc00e5 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -11,7 +11,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var log = logging.Logger("blockservice") From cd358a5c0e759ed89311a7d37c1be760c2270cda Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 29 Oct 2015 21:22:53 -0700 Subject: [PATCH 1019/3817] vendor logging lib update License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-chunker@5dcffac54f543a30c3e536d766b2598ac25d6ece --- chunker/splitting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index c10d8d6ff..a2f1dac4f 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -4,7 +4,7 @@ package chunk import ( "io" - logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) var log = logging.Logger("chunk") From 53316c3ce2aa86de021bfc670860e755e7f74ca9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 4 Nov 2015 21:49:20 -0800 Subject: [PATCH 1020/3817] Add in some more notifications to help profile queries License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@30b4c9e6affcdeeb42cb3b25e7d9e270cc59a72d --- routing/dht/query.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/routing/dht/query.go b/routing/dht/query.go index 5318897ee..8afaaa7e5 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -79,6 +79,8 @@ type dhtQueryRunner struct { rateLimit chan struct{} // processing semaphore log logging.EventLogger + runCtx context.Context + proc process.Process sync.RWMutex } @@ -98,6 +100,7 @@ func newQueryRunner(q *dhtQuery) *dhtQueryRunner { func (r *dhtQueryRunner) Run(ctx context.Context, peers []peer.ID) (*dhtQueryResult, error) { r.log = log + r.runCtx = ctx if len(peers) == 0 { log.Warning("Running query with no peers!") @@ -167,6 +170,11 @@ func (r *dhtQueryRunner) addPeerToQuery(next peer.ID) { return } + notif.PublishQueryEvent(r.runCtx, ¬if.QueryEvent{ + Type: notif.AddingPeer, + ID: next, + }) + r.peersRemaining.Increment(1) select { case r.peersToQuery.EnqChan <- next: @@ -221,7 +229,12 @@ func (r *dhtQueryRunner) queryPeer(proc process.Process, p peer.ID) { // make sure we're connected to the peer. // FIXME abstract away into the network layer if conns := r.query.dht.host.Network().ConnsToPeer(p); len(conns) == 0 { - log.Infof("not connected. dialing.") + log.Error("not connected. dialing.") + + notif.PublishQueryEvent(r.runCtx, ¬if.QueryEvent{ + Type: notif.DialingPeer, + ID: p, + }) // while we dial, we do not take up a rate limit. this is to allow // forward progress during potentially very high latency dials. r.rateLimit <- struct{}{} @@ -231,9 +244,10 @@ func (r *dhtQueryRunner) queryPeer(proc process.Process, p peer.ID) { if err := r.query.dht.host.Connect(ctx, pi); err != nil { log.Debugf("Error connecting: %s", err) - notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + notif.PublishQueryEvent(r.runCtx, ¬if.QueryEvent{ Type: notif.QueryError, Extra: err.Error(), + ID: p, }) r.Lock() From ac71d825b6e907612873b6fd9e611bb51209df8b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 9 Nov 2015 23:18:38 -0800 Subject: [PATCH 1021/3817] drop error log down to debug License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@e045974814dab7a0506689fed37bf8b311ebef66 --- routing/dht/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/query.go b/routing/dht/query.go index 666a95878..d64e432ea 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -229,7 +229,7 @@ func (r *dhtQueryRunner) queryPeer(proc process.Process, p peer.ID) { // make sure we're connected to the peer. // FIXME abstract away into the network layer if conns := r.query.dht.host.Network().ConnsToPeer(p); len(conns) == 0 { - log.Error("not connected. dialing.") + log.Debug("not connected. dialing.") notif.PublishQueryEvent(r.runCtx, ¬if.QueryEvent{ Type: notif.DialingPeer, From 30a17b08374e1d1bbabf89e980b74ce358d8d07d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 15 Nov 2015 20:20:54 -0800 Subject: [PATCH 1022/3817] import from go-ipfs This commit was moved from ipfs/go-ipfs-util@ee302b211dd9abdbca5de9f96aee10d502a5ad49 --- util/file.go | 11 ++++ util/file_test.go | 10 ++++ util/time.go | 17 ++++++ util/time_test.go | 16 +++++ util/util.go | 148 ++++++++++++++++++++++++++++++++++++++++++++++ util/util_test.go | 63 ++++++++++++++++++++ 6 files changed, 265 insertions(+) create mode 100644 util/file.go create mode 100644 util/file_test.go create mode 100644 util/time.go create mode 100644 util/time_test.go create mode 100644 util/util.go create mode 100644 util/util_test.go diff --git a/util/file.go b/util/file.go new file mode 100644 index 000000000..e3bd49d71 --- /dev/null +++ b/util/file.go @@ -0,0 +1,11 @@ +package util + +import "os" + +func FileExists(filename string) bool { + fi, err := os.Lstat(filename) + if fi != nil || (err != nil && !os.IsNotExist(err)) { + return true + } + return false +} diff --git a/util/file_test.go b/util/file_test.go new file mode 100644 index 000000000..040b22927 --- /dev/null +++ b/util/file_test.go @@ -0,0 +1,10 @@ +package util + +import "testing" + +func TestFileDoesNotExist(t *testing.T) { + t.Parallel() + if FileExists("i would be surprised to discover that this file exists") { + t.Fail() + } +} diff --git a/util/time.go b/util/time.go new file mode 100644 index 000000000..5fc6ec66d --- /dev/null +++ b/util/time.go @@ -0,0 +1,17 @@ +package util + +import "time" + +var TimeFormatIpfs = time.RFC3339Nano + +func ParseRFC3339(s string) (time.Time, error) { + t, err := time.Parse(TimeFormatIpfs, s) + if err != nil { + return time.Time{}, err + } + return t.UTC(), nil +} + +func FormatRFC3339(t time.Time) string { + return t.UTC().Format(TimeFormatIpfs) +} diff --git a/util/time_test.go b/util/time_test.go new file mode 100644 index 000000000..b5a98caa6 --- /dev/null +++ b/util/time_test.go @@ -0,0 +1,16 @@ +package util + +import ( + "testing" + "time" +) + +func TestTimeFormatParseInversion(t *testing.T) { + v, err := ParseRFC3339(FormatRFC3339(time.Now())) + if err != nil { + t.Fatal(err) + } + if v.Location() != time.UTC { + t.Fatal("Time should be UTC") + } +} diff --git a/util/util.go b/util/util.go new file mode 100644 index 000000000..1ce3a19b4 --- /dev/null +++ b/util/util.go @@ -0,0 +1,148 @@ +// Package util implements various utility functions used within ipfs +// that do not currently have a better place to live. +package util + +import ( + "errors" + "io" + "math/rand" + "os" + "path/filepath" + "runtime/debug" + "strings" + "time" + + b58 "github.com/jbenet/go-base58" + mh "github.com/jbenet/go-multihash" +) + +// Debug is a global flag for debugging. +var Debug bool + +// ErrNotImplemented signifies a function has not been implemented yet. +var ErrNotImplemented = errors.New("Error: not implemented yet.") + +// ErrTimeout implies that a timeout has been triggered +var ErrTimeout = errors.New("Error: Call timed out.") + +// ErrSeErrSearchIncomplete implies that a search type operation didnt +// find the expected node, but did find 'a' node. +var ErrSearchIncomplete = errors.New("Error: Search Incomplete.") + +// ErrCast is returned when a cast fails AND the program should not panic. +func ErrCast() error { + debug.PrintStack() + return errCast +} + +var errCast = errors.New("cast error") + +// ExpandPathnames takes a set of paths and turns them into absolute paths +func ExpandPathnames(paths []string) ([]string, error) { + var out []string + for _, p := range paths { + abspath, err := filepath.Abs(p) + if err != nil { + return nil, err + } + out = append(out, abspath) + } + return out, nil +} + +type randGen struct { + rand.Rand +} + +func NewTimeSeededRand() io.Reader { + src := rand.NewSource(time.Now().UnixNano()) + return &randGen{ + Rand: *rand.New(src), + } +} + +func NewSeededRand(seed int64) io.Reader { + src := rand.NewSource(seed) + return &randGen{ + Rand: *rand.New(src), + } +} + +func (r *randGen) Read(p []byte) (n int, err error) { + for i := 0; i < len(p); i++ { + p[i] = byte(r.Rand.Intn(255)) + } + return len(p), nil +} + +// GetenvBool is the way to check an env var as a boolean +func GetenvBool(name string) bool { + v := strings.ToLower(os.Getenv(name)) + return v == "true" || v == "t" || v == "1" +} + +// MultiErr is a util to return multiple errors +type MultiErr []error + +func (m MultiErr) Error() string { + if len(m) == 0 { + return "no errors" + } + + s := "Multiple errors: " + for i, e := range m { + if i != 0 { + s += ", " + } + s += e.Error() + } + return s +} + +func Partition(subject string, sep string) (string, string, string) { + if i := strings.Index(subject, sep); i != -1 { + return subject[:i], subject[i : i+len(sep)], subject[i+len(sep):] + } + return subject, "", "" +} + +func RPartition(subject string, sep string) (string, string, string) { + if i := strings.LastIndex(subject, sep); i != -1 { + return subject[:i], subject[i : i+len(sep)], subject[i+len(sep):] + } + return subject, "", "" +} + +// Hash is the global IPFS hash function. uses multihash SHA2_256, 256 bits +func Hash(data []byte) mh.Multihash { + h, err := mh.Sum(data, mh.SHA2_256, -1) + if err != nil { + // this error can be safely ignored (panic) because multihash only fails + // from the selection of hash function. If the fn + length are valid, it + // won't error. + panic("multihash failed to hash using SHA2_256.") + } + return h +} + +// IsValidHash checks whether a given hash is valid (b58 decodable, len > 0) +func IsValidHash(s string) bool { + out := b58.Decode(s) + if out == nil || len(out) == 0 { + return false + } + _, err := mh.Cast(out) + if err != nil { + return false + } + return true +} + +// XOR takes two byte slices, XORs them together, returns the resulting slice. +func XOR(a, b []byte) []byte { + c := make([]byte, len(a)) + for i := 0; i < len(a); i++ { + c[i] = a[i] ^ b[i] + } + return c +} diff --git a/util/util_test.go b/util/util_test.go new file mode 100644 index 000000000..70747ad90 --- /dev/null +++ b/util/util_test.go @@ -0,0 +1,63 @@ +package util + +import ( + "bytes" + "testing" +) + +func TestXOR(t *testing.T) { + cases := [][3][]byte{ + { + {0xFF, 0xFF, 0xFF}, + {0xFF, 0xFF, 0xFF}, + {0x00, 0x00, 0x00}, + }, + { + {0x00, 0xFF, 0x00}, + {0xFF, 0xFF, 0xFF}, + {0xFF, 0x00, 0xFF}, + }, + { + {0x55, 0x55, 0x55}, + {0x55, 0xFF, 0xAA}, + {0x00, 0xAA, 0xFF}, + }, + } + + for _, c := range cases { + r := XOR(c[0], c[1]) + if !bytes.Equal(r, c[2]) { + t.Error("XOR failed") + } + } +} + +func BenchmarkHash256K(b *testing.B) { + buf := make([]byte, 256*1024) + NewTimeSeededRand().Read(buf) + b.SetBytes(int64(256 * 1024)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Hash(buf) + } +} + +func BenchmarkHash512K(b *testing.B) { + buf := make([]byte, 512*1024) + NewTimeSeededRand().Read(buf) + b.SetBytes(int64(512 * 1024)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Hash(buf) + } +} + +func BenchmarkHash1M(b *testing.B) { + buf := make([]byte, 1024*1024) + NewTimeSeededRand().Read(buf) + b.SetBytes(int64(1024 * 1024)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Hash(buf) + } +} From 030c323664d2512f2f43c17c0420539621bf0d84 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 15 Nov 2015 21:05:44 -0800 Subject: [PATCH 1023/3817] gx-ify This commit was moved from ipfs/go-ipfs-util@8b6cc6f134aa799cc9b939c0707a31cd7fe3e533 --- util/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 util/.gitignore diff --git a/util/.gitignore b/util/.gitignore new file mode 100644 index 000000000..1377554eb --- /dev/null +++ b/util/.gitignore @@ -0,0 +1 @@ +*.swp From d1dede505981a71454897776a41baac20dc9acb5 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 12 Dec 2015 21:28:00 +0100 Subject: [PATCH 1024/3817] merkledag/merkledag_test: fix t.Fatal in a goroutine License: MIT Signed-off-by: Christian Couder This commit was moved from ipfs/go-merkledag@95afd3c24f10c0087555f3a78de883abcdf1d910 --- ipld/merkledag/merkledag_test.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 40bc45740..d81cdc003 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -2,6 +2,7 @@ package merkledag_test import ( "bytes" + "errors" "fmt" "io" "io/ioutil" @@ -193,32 +194,43 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { } wg := sync.WaitGroup{} + errs := make(chan error) + for i := 1; i < len(dagservs); i++ { wg.Add(1) go func(i int) { defer wg.Done() first, err := dagservs[i].Get(ctx, k) if err != nil { - t.Fatal(err) + errs <- err } fmt.Println("Got first node back.") read, err := uio.NewDagReader(ctx, first, dagservs[i]) if err != nil { - t.Fatal(err) + errs <- err } datagot, err := ioutil.ReadAll(read) if err != nil { - t.Fatal(err) + errs <- err } if !bytes.Equal(datagot, expected) { - t.Fatal("Got bad data back!") + errs <- errors.New("Got bad data back!") } }(i) } - wg.Wait() + go func() { + wg.Wait() + close(errs) + }() + + for err := range errs { + if err != nil { + t.Fatal(err) + } + } } func TestRecursiveAdd(t *testing.T) { From 537fa4afe943f9e51595b24c9db3126106f213a7 Mon Sep 17 00:00:00 2001 From: "Jakub (Kubuxu) Sztandera" Date: Sat, 2 Jan 2016 00:24:54 +0100 Subject: [PATCH 1025/3817] namesys: Make paths with multiple segemnts work. Fixes #2059 Also fixes non-recursive resolve erring instead showing one step. The patch of core/commands/resolve.go could be done better but I don't know how to get access to ErrResolveRecursion. It allows for dnslinks into sub-segments. So for example hosting multiple blogs on just domains from one pubkey. Fixes #2059 Add tests and fix case when dnslinks references dnslink License: MIT Signed-off-by: Jakub (Kubuxu) Sztandera This commit was moved from ipfs/go-namesys@3e2eb50f68f61cb9c695b4a6be8355798ce4318b --- namesys/dns.go | 11 ++++++++--- namesys/dns_test.go | 9 +++++++++ namesys/namesys.go | 10 +++++++--- namesys/routing.go | 2 +- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 3703bd8d0..d74213e93 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -45,12 +45,14 @@ func (r *DNSResolver) ResolveN(ctx context.Context, name string, depth int) (pat // TXT records for a given domain name should contain a b58 // encoded multihash. func (r *DNSResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) { - if !isd.IsDomain(name) { + segments := strings.SplitN(name, "/", 2) + + if !isd.IsDomain(segments[0]) { return "", errors.New("not a valid domain name") } - log.Infof("DNSResolver resolving %s", name) - txt, err := r.lookupTXT(name) + log.Infof("DNSResolver resolving %s", segments[0]) + txt, err := r.lookupTXT(segments[0]) if err != nil { return "", err } @@ -58,6 +60,9 @@ func (r *DNSResolver) resolveOnce(ctx context.Context, name string) (path.Path, for _, t := range txt { p, err := parseEntry(t) if err == nil { + if len(segments) > 1 { + return path.FromSegments(p.String() + "/", segments[1]) + } return p, nil } } diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 40bf702c3..a1e4ce442 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -18,6 +18,7 @@ func (m *mockDNS) lookupTXT(name string) (txt []string, err error) { } func TestDnsEntryParsing(t *testing.T) { + goodEntries := []string{ "QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", @@ -86,6 +87,12 @@ func newMockDNS() *mockDNS { "bad.example.com": []string{ "dnslink=", }, + "withsegment.example.com": []string{ + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment", + }, + "withrecsegment.example.com": []string{ + "dnslink=/ipns/withsegment.example.com/subsub", + }, }, } } @@ -109,4 +116,6 @@ func TestDNSResolution(t *testing.T) { testResolution(t, r, "loop1.example.com", 3, "/ipns/loop2.example.com", ErrResolveRecursion) testResolution(t, r, "loop1.example.com", DefaultDepthLimit, "/ipns/loop1.example.com", ErrResolveRecursion) testResolution(t, r, "bad.example.com", DefaultDepthLimit, "", ErrResolveFailed) + testResolution(t, r, "withsegment.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment", nil) + testResolution(t, r, "withrecsegment.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub", nil) } diff --git a/namesys/namesys.go b/namesys/namesys.go index c61d3496b..b375a5c04 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -64,17 +64,21 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) if !strings.HasPrefix(name, "/ipns/") { name = "/ipns/" + name } - segments := strings.SplitN(name, "/", 3) + segments := strings.SplitN(name, "/", 4) if len(segments) < 3 || segments[0] != "" { log.Warningf("Invalid name syntax for %s", name) return "", ErrResolveFailed } for protocol, resolver := range ns.resolvers { - log.Debugf("Attempting to resolve %s with %s", name, protocol) + log.Debugf("Attempting to resolve %s with %s", segments[2], protocol) p, err := resolver.resolveOnce(ctx, segments[2]) if err == nil { - return p, err + if len(segments) > 3 { + return path.FromSegments(p.String() + "/", segments[3]) + } else { + return p, err + } } } log.Warningf("No resolver found for %s", name) diff --git a/namesys/routing.go b/namesys/routing.go index 85ef498e7..5f9e3bc87 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -123,7 +123,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa hash, err := mh.FromB58String(name) if err != nil { - log.Warning("RoutingResolve: bad input hash: [%s]\n", name) + log.Warningf("RoutingResolve: bad input hash: [%s]\n", name) return "", err } // name should be a multihash. if it isn't, error out here. From d4df5d5ea702af86fb53b96aa51c9fbdb461a7c9 Mon Sep 17 00:00:00 2001 From: "Jakub (Kubuxu) Sztandera" Date: Tue, 5 Jan 2016 18:13:43 +0100 Subject: [PATCH 1026/3817] Included more namesys tests. Fixed some issues with trailing slashes. License: MIT Signed-off-by: Jakub (Kubuxu) Sztandera This commit was moved from ipfs/go-namesys@ecb67ec4c53a709495c84730cf8a28a50293109c --- namesys/dns.go | 2 +- namesys/dns_test.go | 11 +++++++++++ namesys/namesys.go | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index d74213e93..96147534a 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -61,7 +61,7 @@ func (r *DNSResolver) resolveOnce(ctx context.Context, name string) (path.Path, p, err := parseEntry(t) if err == nil { if len(segments) > 1 { - return path.FromSegments(p.String() + "/", segments[1]) + return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[1]) } return p, nil } diff --git a/namesys/dns_test.go b/namesys/dns_test.go index a1e4ce442..27b3883db 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -26,6 +26,7 @@ func TestDnsEntryParsing(t *testing.T) { "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/foo", "dnslink=/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/bar", "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/foo/bar/baz", + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/foo/bar/baz/", "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", } @@ -93,6 +94,12 @@ func newMockDNS() *mockDNS { "withrecsegment.example.com": []string{ "dnslink=/ipns/withsegment.example.com/subsub", }, + "withtrailing.example.com": []string{ + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/", + }, + "withtrailingrec.example.com": []string{ + "dnslink=/ipns/withtrailing.example.com/segment/", + }, }, } } @@ -118,4 +125,8 @@ func TestDNSResolution(t *testing.T) { testResolution(t, r, "bad.example.com", DefaultDepthLimit, "", ErrResolveFailed) testResolution(t, r, "withsegment.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment", nil) testResolution(t, r, "withrecsegment.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub", nil) + testResolution(t, r, "withsegment.example.com/test1", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/test1", nil) + testResolution(t, r, "withrecsegment.example.com/test2", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub/test2", nil) + testResolution(t, r, "withrecsegment.example.com/test3/", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub/test3/", nil) + testResolution(t, r, "withtrailingrec.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/", nil) } diff --git a/namesys/namesys.go b/namesys/namesys.go index b375a5c04..4c9868b57 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -75,7 +75,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) p, err := resolver.resolveOnce(ctx, segments[2]) if err == nil { if len(segments) > 3 { - return path.FromSegments(p.String() + "/", segments[3]) + return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) } else { return p, err } From b52ba7f68861ace966c30fec268cf089d8a74eee Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Sun, 31 May 2015 15:47:36 -0700 Subject: [PATCH 1027/3817] pin: Guard against callers causing refcount underflow This used to lead to large refcount numbers, causing Flush to create a lot of IPFS objects, and merkledag to consume tens of gigabytes of RAM. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@2250faf505b099775030a2b95dfe0c5cf6b66e8b --- pinning/pinner/indirect.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index dca99600f..e5ed5dcb6 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -57,6 +57,10 @@ func (i *indirectPin) Increment(k key.Key) { } func (i *indirectPin) Decrement(k key.Key) { + if i.refCounts[k] == 0 { + log.Warningf("pinning: bad call: asked to unpin nonexistent indirect key: %v", k) + return + } c := i.refCounts[k] - 1 i.refCounts[k] = c if c <= 0 { From b5768935343f8322714cbc4be51739a5d52b9569 Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Wed, 6 May 2015 16:17:13 -0700 Subject: [PATCH 1028/3817] pin: unexport NewIndirectPin, it's not useful and not used elsewhere License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@28647540e2b3483a1331b635dbe4222f04cfeaf0 --- pinning/pinner/indirect.go | 2 +- pinning/pinner/pin.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index e5ed5dcb6..1ca8c4bed 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -11,7 +11,7 @@ type indirectPin struct { refCounts map[key.Key]int } -func NewIndirectPin(dstore ds.Datastore) *indirectPin { +func newIndirectPin(dstore ds.Datastore) *indirectPin { return &indirectPin{ blockset: set.NewDBWrapperSet(dstore, set.NewSimpleBlockSet()), refCounts: make(map[key.Key]int), diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 53d965e9b..31f2afe0f 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -75,7 +75,7 @@ func NewPinner(dstore ds.ThreadSafeDatastore, serv mdag.DAGService) Pinner { return &pinner{ recursePin: rcset, directPin: dirset, - indirPin: NewIndirectPin(nsdstore), + indirPin: newIndirectPin(nsdstore), dserv: serv, dstore: dstore, } From e3cc8c322e4054c34350adefb6bb1f6c56acba14 Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Fri, 8 May 2015 10:29:00 -0700 Subject: [PATCH 1029/3817] pin: Remove code shadowing pins as datastore keys These secondary copies were never actually queried, and didn't contain the indirect refcounts so they couldn't become the authoritative source anyway as is. New goal is to move pinning into IPFS objects. A migration will be needed to remove the old data from the datastore. This can happen at any time after this commit. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@c705cd501a43f050cee7e60eac3b4dd84398b0e8 --- pinning/pinner/indirect.go | 4 ++-- pinning/pinner/pin.go | 10 +++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index 1ca8c4bed..1a1070ee2 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -11,9 +11,9 @@ type indirectPin struct { refCounts map[key.Key]int } -func newIndirectPin(dstore ds.Datastore) *indirectPin { +func newIndirectPin() *indirectPin { return &indirectPin{ - blockset: set.NewDBWrapperSet(dstore, set.NewSimpleBlockSet()), + blockset: set.NewSimpleBlockSet(), refCounts: make(map[key.Key]int), } } diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 31f2afe0f..ee27252c3 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -9,7 +9,6 @@ import ( "sync" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - nsds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blocks/set" @@ -65,17 +64,14 @@ type pinner struct { func NewPinner(dstore ds.ThreadSafeDatastore, serv mdag.DAGService) Pinner { // Load set from given datastore... - rcds := nsds.Wrap(dstore, recursePinDatastoreKey) - rcset := set.NewDBWrapperSet(rcds, set.NewSimpleBlockSet()) + rcset := set.NewSimpleBlockSet() - dirds := nsds.Wrap(dstore, directPinDatastoreKey) - dirset := set.NewDBWrapperSet(dirds, set.NewSimpleBlockSet()) + dirset := set.NewSimpleBlockSet() - nsdstore := nsds.Wrap(dstore, indirectPinDatastoreKey) return &pinner{ recursePin: rcset, directPin: dirset, - indirPin: newIndirectPin(nsdstore), + indirPin: newIndirectPin(), dserv: serv, dstore: dstore, } From 95226f03d9661cccc355154ef6075c37ab26e95e Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Fri, 8 May 2015 11:00:55 -0700 Subject: [PATCH 1030/3817] Simplify Pinner interface by folding ManualPinner into Pinner Pinner had method GetManual that returned a ManualPinner, so every Pinner had to implement ManualPinner anyway. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@2464e11491b4fae23ca0e822515c1fcf3cf0919e --- pinning/pinner/pin.go | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index ee27252c3..2db6a9b81 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -34,22 +34,22 @@ type Pinner interface { IsPinned(key.Key) bool Pin(context.Context, *mdag.Node, bool) error Unpin(context.Context, key.Key, bool) error + + // PinWithMode is for manually editing the pin structure. Use with + // care! If used improperly, garbage collection may not be + // successful. + PinWithMode(key.Key, PinMode) + // RemovePinWithMode is for manually editing the pin structure. + // Use with care! If used improperly, garbage collection may not + // be successful. + RemovePinWithMode(key.Key, PinMode) + Flush() error - GetManual() ManualPinner DirectKeys() []key.Key IndirectKeys() map[key.Key]int RecursiveKeys() []key.Key } -// ManualPinner is for manually editing the pin structure -// Use with care! If used improperly, garbage collection -// may not be successful -type ManualPinner interface { - PinWithMode(key.Key, PinMode) - RemovePinWithMode(key.Key, PinMode) - Pinner -} - // pinner implements the Pinner interface type pinner struct { lock sync.RWMutex @@ -308,8 +308,8 @@ func loadSet(d ds.Datastore, k ds.Key, val interface{}) error { return json.Unmarshal(bf, val) } -// PinWithMode is a method on ManualPinners, allowing the user to have fine -// grained control over pin counts +// PinWithMode allows the user to have fine grained control over pin +// counts func (p *pinner) PinWithMode(k key.Key, mode PinMode) { p.lock.Lock() defer p.lock.Unlock() @@ -322,7 +322,3 @@ func (p *pinner) PinWithMode(k key.Key, mode PinMode) { p.indirPin.Increment(k) } } - -func (p *pinner) GetManual() ManualPinner { - return p -} From 045f1d534ee1539031ba171c7809b97b92c801aa Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Fri, 8 May 2015 17:00:20 -0700 Subject: [PATCH 1031/3817] pin: Remove dead code License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@d1669f6939ae12c494ef6fe466c63e9880288521 --- pinning/pinner/indirect.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index 1a1070ee2..734387bd5 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -73,10 +73,6 @@ func (i *indirectPin) HasKey(k key.Key) bool { return i.blockset.HasKey(k) } -func (i *indirectPin) Set() set.BlockSet { - return i.blockset -} - func (i *indirectPin) GetRefs() map[key.Key]int { return i.refCounts } From 2940feafa19caec82190500ffd644902537b4eed Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Fri, 8 May 2015 17:10:46 -0700 Subject: [PATCH 1032/3817] pin: Remove double bookkeeping of refcount keys License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@0ea108445058f82b655683b675129e4c1a58ba50 --- pinning/pinner/indirect.go | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index 734387bd5..6043a97f7 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -3,17 +3,14 @@ package pin import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" - "github.com/ipfs/go-ipfs/blocks/set" ) type indirectPin struct { - blockset set.BlockSet refCounts map[key.Key]int } func newIndirectPin() *indirectPin { return &indirectPin{ - blockset: set.NewSimpleBlockSet(), refCounts: make(map[key.Key]int), } } @@ -36,7 +33,7 @@ func loadIndirPin(d ds.Datastore, k ds.Key) (*indirectPin, error) { } // log.Debugf("indirPin keys: %#v", keys) - return &indirectPin{blockset: set.SimpleSetFromKeys(keys), refCounts: refcnt}, nil + return &indirectPin{refCounts: refcnt}, nil } func storeIndirPin(d ds.Datastore, k ds.Key, p *indirectPin) error { @@ -49,11 +46,7 @@ func storeIndirPin(d ds.Datastore, k ds.Key, p *indirectPin) error { } func (i *indirectPin) Increment(k key.Key) { - c := i.refCounts[k] - i.refCounts[k] = c + 1 - if c <= 0 { - i.blockset.AddBlock(k) - } + i.refCounts[k]++ } func (i *indirectPin) Decrement(k key.Key) { @@ -61,16 +54,15 @@ func (i *indirectPin) Decrement(k key.Key) { log.Warningf("pinning: bad call: asked to unpin nonexistent indirect key: %v", k) return } - c := i.refCounts[k] - 1 - i.refCounts[k] = c - if c <= 0 { - i.blockset.RemoveBlock(k) + i.refCounts[k]-- + if i.refCounts[k] == 0 { delete(i.refCounts, k) } } func (i *indirectPin) HasKey(k key.Key) bool { - return i.blockset.HasKey(k) + _, found := i.refCounts[k] + return found } func (i *indirectPin) GetRefs() map[key.Key]int { From 55265e5c5a8c41c055e12b6d550121475e0f3f7a Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Fri, 8 May 2015 17:17:09 -0700 Subject: [PATCH 1033/3817] Use uint64 for indirect pin refcounts Platform-dependent behavior is not nice, and negative refcounts are not very useful. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@1f4ff89a606788b4f4abb35a915808da2baa7687 --- pinning/pinner/indirect.go | 12 ++++++------ pinning/pinner/pin.go | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index 6043a97f7..a89c2caf0 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -6,23 +6,23 @@ import ( ) type indirectPin struct { - refCounts map[key.Key]int + refCounts map[key.Key]uint64 } func newIndirectPin() *indirectPin { return &indirectPin{ - refCounts: make(map[key.Key]int), + refCounts: make(map[key.Key]uint64), } } func loadIndirPin(d ds.Datastore, k ds.Key) (*indirectPin, error) { - var rcStore map[string]int + var rcStore map[string]uint64 err := loadSet(d, k, &rcStore) if err != nil { return nil, err } - refcnt := make(map[key.Key]int) + refcnt := make(map[key.Key]uint64) var keys []key.Key for encK, v := range rcStore { if v > 0 { @@ -38,7 +38,7 @@ func loadIndirPin(d ds.Datastore, k ds.Key) (*indirectPin, error) { func storeIndirPin(d ds.Datastore, k ds.Key, p *indirectPin) error { - rcStore := map[string]int{} + rcStore := map[string]uint64{} for k, v := range p.refCounts { rcStore[key.B58KeyEncode(k)] = v } @@ -65,6 +65,6 @@ func (i *indirectPin) HasKey(k key.Key) bool { return found } -func (i *indirectPin) GetRefs() map[key.Key]int { +func (i *indirectPin) GetRefs() map[key.Key]uint64 { return i.refCounts } diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 2db6a9b81..6740869d2 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -46,7 +46,7 @@ type Pinner interface { Flush() error DirectKeys() []key.Key - IndirectKeys() map[key.Key]int + IndirectKeys() map[key.Key]uint64 RecursiveKeys() []key.Key } @@ -254,7 +254,7 @@ func (p *pinner) DirectKeys() []key.Key { } // IndirectKeys returns a slice containing the indirectly pinned keys -func (p *pinner) IndirectKeys() map[key.Key]int { +func (p *pinner) IndirectKeys() map[key.Key]uint64 { return p.indirPin.GetRefs() } From 3d2158a8783ea954187287cafe5a62ffc63e13bd Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Fri, 8 May 2015 20:13:30 -0700 Subject: [PATCH 1034/3817] Typo License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@226231164b34b249b7f8838b2bfbb0916249cc4e --- pinning/pinner/pin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 6740869d2..b719f188e 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -1,4 +1,4 @@ -// package pin implemnts structures and methods to keep track of +// package pin implements structures and methods to keep track of // which objects a user wants to keep stored locally. package pin From 2d0d611eb5981c8167d524cc620f16a32c6b9e7a Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Fri, 8 May 2015 11:00:55 -0700 Subject: [PATCH 1035/3817] Simplify Pinner interface by folding ManualPinner into Pinner Pinner had method GetManual that returned a ManualPinner, so every Pinner had to implement ManualPinner anyway. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@27f6f389e44ac660abb389d4696b9d2bbbebf81f --- ipld/merkledag/merkledag_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index d81cdc003..dda4a976e 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -28,7 +28,7 @@ import ( type dagservAndPinner struct { ds DAGService - mp pin.ManualPinner + mp pin.Pinner } func getDagservAndPinner(t *testing.T) dagservAndPinner { @@ -36,7 +36,7 @@ func getDagservAndPinner(t *testing.T) dagservAndPinner { bs := bstore.NewBlockstore(db) blockserv := bserv.New(bs, offline.Exchange(bs)) dserv := NewDAGService(blockserv) - mpin := pin.NewPinner(db, dserv).GetManual() + mpin := pin.NewPinner(db, dserv) return dagservAndPinner{ ds: dserv, mp: mpin, From 8c3d57e28540b965758f138fb0aa98b316968ff4 Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Fri, 8 May 2015 11:00:55 -0700 Subject: [PATCH 1036/3817] Simplify Pinner interface by folding ManualPinner into Pinner Pinner had method GetManual that returned a ManualPinner, so every Pinner had to implement ManualPinner anyway. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@1497d51f7b578605195c3dfdc4c585217c9d0d4e --- unixfs/mod/dagmodifier.go | 4 ++-- unixfs/mod/dagmodifier_test.go | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 5f5eddc90..bb22f289f 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -37,7 +37,7 @@ var log = logging.Logger("dagio") type DagModifier struct { dagserv mdag.DAGService curNode *mdag.Node - mp pin.ManualPinner + mp pin.Pinner splitter chunk.SplitterGen ctx context.Context @@ -50,7 +50,7 @@ type DagModifier struct { read *uio.DagReader } -func NewDagModifier(ctx context.Context, from *mdag.Node, serv mdag.DAGService, mp pin.ManualPinner, spl chunk.SplitterGen) (*DagModifier, error) { +func NewDagModifier(ctx context.Context, from *mdag.Node, serv mdag.DAGService, mp pin.Pinner, spl chunk.SplitterGen) (*DagModifier, error) { return &DagModifier{ curNode: from.Copy(), dagserv: serv, diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 475e7c6c4..25caadfb0 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -27,25 +27,25 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) -func getMockDagServ(t testing.TB) (mdag.DAGService, pin.ManualPinner) { +func getMockDagServ(t testing.TB) (mdag.DAGService, pin.Pinner) { dstore := ds.NewMapDatastore() tsds := sync.MutexWrap(dstore) bstore := blockstore.NewBlockstore(tsds) bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) - return dserv, pin.NewPinner(tsds, dserv).GetManual() + return dserv, pin.NewPinner(tsds, dserv) } -func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.Blockstore, pin.ManualPinner) { +func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.Blockstore, pin.Pinner) { dstore := ds.NewMapDatastore() tsds := sync.MutexWrap(dstore) bstore := blockstore.NewBlockstore(tsds) bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) - return dserv, bstore, pin.NewPinner(tsds, dserv).GetManual() + return dserv, bstore, pin.NewPinner(tsds, dserv) } -func getNode(t testing.TB, dserv mdag.DAGService, size int64, pinner pin.ManualPinner) ([]byte, *mdag.Node) { +func getNode(t testing.TB, dserv mdag.DAGService, size int64, pinner pin.Pinner) ([]byte, *mdag.Node) { in := io.LimitReader(u.NewTimeSeededRand(), size) node, err := imp.BuildTrickleDagFromReader(dserv, sizeSplitterGen(500)(in), imp.BasicPinnerCB(pinner)) if err != nil { @@ -469,7 +469,7 @@ func TestSparseWrite(t *testing.T) { } } -func basicGC(t *testing.T, bs blockstore.Blockstore, pins pin.ManualPinner) { +func basicGC(t *testing.T, bs blockstore.Blockstore, pins pin.Pinner) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // in case error occurs during operation keychan, err := bs.AllKeysChan(ctx) From 6923d18119adb26293a294396d0bf229ba85b7ae Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Mon, 11 May 2015 11:19:36 -0700 Subject: [PATCH 1037/3817] pin: Rewrite to store pins in IPFS objects WARNING: No migration performed! That needs to come in a separate commit, perhaps amended into this one. This is the minimal rewrite, only changing the storage from JSON(+extra keys) in Datastore to IPFS objects. All of the pinning state is still loaded in memory, and written from scratch on Flush. To do more would require API changes, e.g. adding error returns. Set/Multiset is not cleanly separated into a library, yet, as it's API is expected to change radically. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@8e0e03bebb62c8894ca5078784b566ecbe120db6 --- pinning/pinner/indirect.go | 31 --- pinning/pinner/internal/pb/doc.go | 6 + pinning/pinner/internal/pb/header.pb.go | 59 +++++ pinning/pinner/internal/pb/header.proto | 14 + pinning/pinner/pin.go | 136 +++++++--- pinning/pinner/set.go | 338 ++++++++++++++++++++++++ 6 files changed, 510 insertions(+), 74 deletions(-) create mode 100644 pinning/pinner/internal/pb/doc.go create mode 100644 pinning/pinner/internal/pb/header.pb.go create mode 100644 pinning/pinner/internal/pb/header.proto create mode 100644 pinning/pinner/set.go diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index a89c2caf0..22e3a1fb4 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -1,7 +1,6 @@ package pin import ( - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" ) @@ -15,36 +14,6 @@ func newIndirectPin() *indirectPin { } } -func loadIndirPin(d ds.Datastore, k ds.Key) (*indirectPin, error) { - var rcStore map[string]uint64 - err := loadSet(d, k, &rcStore) - if err != nil { - return nil, err - } - - refcnt := make(map[key.Key]uint64) - var keys []key.Key - for encK, v := range rcStore { - if v > 0 { - k := key.B58KeyDecode(encK) - keys = append(keys, k) - refcnt[k] = v - } - } - // log.Debugf("indirPin keys: %#v", keys) - - return &indirectPin{refCounts: refcnt}, nil -} - -func storeIndirPin(d ds.Datastore, k ds.Key, p *indirectPin) error { - - rcStore := map[string]uint64{} - for k, v := range p.refCounts { - rcStore[key.B58KeyEncode(k)] = v - } - return storeSet(d, k, rcStore) -} - func (i *indirectPin) Increment(k key.Key) { i.refCounts[k]++ } diff --git a/pinning/pinner/internal/pb/doc.go b/pinning/pinner/internal/pb/doc.go new file mode 100644 index 000000000..1143a4d83 --- /dev/null +++ b/pinning/pinner/internal/pb/doc.go @@ -0,0 +1,6 @@ +package pb + +//go:generate protoc --gogo_out=. header.proto + +// kludge to get vendoring right in protobuf output +//go:generate sed -i s,github.com/,github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/,g header.pb.go diff --git a/pinning/pinner/internal/pb/header.pb.go b/pinning/pinner/internal/pb/header.pb.go new file mode 100644 index 000000000..eafb246e7 --- /dev/null +++ b/pinning/pinner/internal/pb/header.pb.go @@ -0,0 +1,59 @@ +// Code generated by protoc-gen-gogo. +// source: header.proto +// DO NOT EDIT! + +/* +Package pb is a generated protocol buffer package. + +It is generated from these files: + header.proto + +It has these top-level messages: + Set +*/ +package pb + +import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = math.Inf + +type Set struct { + // 1 for now, library will refuse to handle entries with an unrecognized version. + Version *uint32 `protobuf:"varint,1,opt,name=version" json:"version,omitempty"` + // how many of the links are subtrees + Fanout *uint32 `protobuf:"varint,2,opt,name=fanout" json:"fanout,omitempty"` + // hash seed for subtree selection, a random number + Seed *uint32 `protobuf:"fixed32,3,opt,name=seed" json:"seed,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Set) Reset() { *m = Set{} } +func (m *Set) String() string { return proto.CompactTextString(m) } +func (*Set) ProtoMessage() {} + +func (m *Set) GetVersion() uint32 { + if m != nil && m.Version != nil { + return *m.Version + } + return 0 +} + +func (m *Set) GetFanout() uint32 { + if m != nil && m.Fanout != nil { + return *m.Fanout + } + return 0 +} + +func (m *Set) GetSeed() uint32 { + if m != nil && m.Seed != nil { + return *m.Seed + } + return 0 +} + +func init() { +} diff --git a/pinning/pinner/internal/pb/header.proto b/pinning/pinner/internal/pb/header.proto new file mode 100644 index 000000000..36b32b36d --- /dev/null +++ b/pinning/pinner/internal/pb/header.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + +package ipfs.pin; + +option go_package = "pb"; + +message Set { + // 1 for now, library will refuse to handle entries with an unrecognized version. + optional uint32 version = 1; + // how many of the links are subtrees + optional uint32 fanout = 2; + // hash seed for subtree selection, a random number + optional fixed32 seed = 3; +} diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index b719f188e..726c62729 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -3,8 +3,6 @@ package pin import ( - "encoding/json" - "errors" "fmt" "sync" @@ -17,9 +15,16 @@ import ( ) var log = logging.Logger("pin") -var recursePinDatastoreKey = ds.NewKey("/local/pins/recursive/keys") -var directPinDatastoreKey = ds.NewKey("/local/pins/direct/keys") -var indirectPinDatastoreKey = ds.NewKey("/local/pins/indirect/keys") + +var pinDatastoreKey = ds.NewKey("/local/pins") + +var emptyKey = key.B58KeyDecode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n") + +const ( + linkDirect = "direct" + linkRecursive = "recursive" + linkIndirect = "indirect" +) type PinMode int @@ -56,8 +61,11 @@ type pinner struct { recursePin set.BlockSet directPin set.BlockSet indirPin *indirectPin - dserv mdag.DAGService - dstore ds.ThreadSafeDatastore + // Track the keys used for storing the pinning state, so gc does + // not delete them. + internalPin map[key.Key]struct{} + dserv mdag.DAGService + dstore ds.ThreadSafeDatastore } // NewPinner creates a new pinner using the given datastore as a backend @@ -188,13 +196,19 @@ func (p *pinner) pinLinks(ctx context.Context, node *mdag.Node) error { return nil } +func (p *pinner) isInternalPin(key key.Key) bool { + _, ok := p.internalPin[key] + return ok +} + // IsPinned returns whether or not the given key is pinned func (p *pinner) IsPinned(key key.Key) bool { p.lock.RLock() defer p.lock.RUnlock() return p.recursePin.HasKey(key) || p.directPin.HasKey(key) || - p.indirPin.HasKey(key) + p.indirPin.HasKey(key) || + p.isInternalPin(key) } func (p *pinner) RemovePinWithMode(key key.Key, mode PinMode) { @@ -217,30 +231,56 @@ func (p *pinner) RemovePinWithMode(key key.Key, mode PinMode) { func LoadPinner(d ds.ThreadSafeDatastore, dserv mdag.DAGService) (Pinner, error) { p := new(pinner) + rootKeyI, err := d.Get(pinDatastoreKey) + if err != nil { + return nil, fmt.Errorf("cannot load pin state: %v", err) + } + rootKeyBytes, ok := rootKeyI.([]byte) + if !ok { + return nil, fmt.Errorf("cannot load pin state: %s was not bytes", pinDatastoreKey) + } + + rootKey := key.Key(rootKeyBytes) + + ctx := context.TODO() + root, err := dserv.Get(ctx, rootKey) + if err != nil { + return nil, fmt.Errorf("cannot find pinning root object: %v", err) + } + + internalPin := map[key.Key]struct{}{ + rootKey: struct{}{}, + } + recordInternal := func(k key.Key) { + internalPin[k] = struct{}{} + } + { // load recursive set - var recurseKeys []key.Key - if err := loadSet(d, recursePinDatastoreKey, &recurseKeys); err != nil { - return nil, err + recurseKeys, err := loadSet(ctx, dserv, root, linkRecursive, recordInternal) + if err != nil { + return nil, fmt.Errorf("cannot load recursive pins: %v", err) } p.recursePin = set.SimpleSetFromKeys(recurseKeys) } { // load direct set - var directKeys []key.Key - if err := loadSet(d, directPinDatastoreKey, &directKeys); err != nil { - return nil, err + directKeys, err := loadSet(ctx, dserv, root, linkDirect, recordInternal) + if err != nil { + return nil, fmt.Errorf("cannot load direct pins: %v", err) } p.directPin = set.SimpleSetFromKeys(directKeys) } { // load indirect set - var err error - p.indirPin, err = loadIndirPin(d, indirectPinDatastoreKey) + refcnt, err := loadMultiset(ctx, dserv, root, linkIndirect, recordInternal) if err != nil { - return nil, err + return nil, fmt.Errorf("cannot load indirect pins: %v", err) } + p.indirPin = &indirectPin{refCounts: refcnt} } + p.internalPin = internalPin + // assign services p.dserv = dserv p.dstore = d @@ -268,44 +308,54 @@ func (p *pinner) Flush() error { p.lock.Lock() defer p.lock.Unlock() - err := storeSet(p.dstore, directPinDatastoreKey, p.directPin.GetKeys()) - if err != nil { - return err - } + ctx := context.TODO() - err = storeSet(p.dstore, recursePinDatastoreKey, p.recursePin.GetKeys()) - if err != nil { - return err + internalPin := make(map[key.Key]struct{}) + recordInternal := func(k key.Key) { + internalPin[k] = struct{}{} } - err = storeIndirPin(p.dstore, indirectPinDatastoreKey, p.indirPin) - if err != nil { - return err + root := &mdag.Node{} + { + n, err := storeSet(ctx, p.dserv, p.directPin.GetKeys(), recordInternal) + if err != nil { + return err + } + if err := root.AddNodeLink(linkDirect, n); err != nil { + return err + } } - return nil -} -// helpers to marshal / unmarshal a pin set -func storeSet(d ds.Datastore, k ds.Key, val interface{}) error { - buf, err := json.Marshal(val) - if err != nil { - return err + { + n, err := storeSet(ctx, p.dserv, p.recursePin.GetKeys(), recordInternal) + if err != nil { + return err + } + if err := root.AddNodeLink(linkRecursive, n); err != nil { + return err + } } - return d.Put(k, buf) -} + { + n, err := storeMultiset(ctx, p.dserv, p.indirPin.GetRefs(), recordInternal) + if err != nil { + return err + } + if err := root.AddNodeLink(linkIndirect, n); err != nil { + return err + } + } -func loadSet(d ds.Datastore, k ds.Key, val interface{}) error { - buf, err := d.Get(k) + k, err := p.dserv.Add(root) if err != nil { return err } - - bf, ok := buf.([]byte) - if !ok { - return errors.New("invalid pin set value in datastore") + internalPin[k] = struct{}{} + if err := p.dstore.Put(pinDatastoreKey, []byte(k)); err != nil { + return fmt.Errorf("cannot store pin state: %v", err) } - return json.Unmarshal(bf, val) + p.internalPin = internalPin + return nil } // PinWithMode allows the user to have fine grained control over pin diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go new file mode 100644 index 000000000..02619bf20 --- /dev/null +++ b/pinning/pinner/set.go @@ -0,0 +1,338 @@ +package pin + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "errors" + "fmt" + "hash/fnv" + "io" + "sort" + "unsafe" + + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "github.com/ipfs/go-ipfs/blocks/key" + "github.com/ipfs/go-ipfs/merkledag" + "github.com/ipfs/go-ipfs/pin/internal/pb" +) + +const ( + defaultFanout = 256 + maxItems = 8192 +) + +func randomSeed() (uint32, error) { + var buf [4]byte + if _, err := rand.Read(buf[:]); err != nil { + return 0, err + } + return binary.LittleEndian.Uint32(buf[:]), nil +} + +func hash(seed uint32, k key.Key) uint32 { + var buf [4]byte + binary.LittleEndian.PutUint32(buf[:], seed) + h := fnv.New32a() + _, _ = h.Write(buf[:]) + _, _ = io.WriteString(h, string(k)) + return h.Sum32() +} + +type itemIterator func() (k key.Key, data []byte, ok bool) + +type keyObserver func(key.Key) + +type refcount uint8 + +func (r refcount) Bytes() []byte { + // refcount size can change in later versions; this may need + // encoding/binary + return []byte{byte(r)} +} + +type sortByHash struct { + links []*merkledag.Link + data []byte +} + +func (s sortByHash) Len() int { + return len(s.links) +} + +func (s sortByHash) Less(a, b int) bool { + return bytes.Compare(s.links[a].Hash, s.links[b].Hash) == -1 +} + +func (s sortByHash) Swap(a, b int) { + s.links[a], s.links[b] = s.links[b], s.links[a] + if len(s.data) != 0 { + const n = int(unsafe.Sizeof(refcount(0))) + tmp := make([]byte, n) + copy(tmp, s.data[a:a+n]) + copy(s.data[a:a+n], s.data[b:b+n]) + copy(s.data[b:b+n], tmp) + } +} + +func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint64, iter itemIterator, internalKeys keyObserver) (*merkledag.Node, error) { + seed, err := randomSeed() + if err != nil { + return nil, err + } + n := &merkledag.Node{ + Links: make([]*merkledag.Link, 0, defaultFanout+maxItems), + } + for i := 0; i < defaultFanout; i++ { + n.Links = append(n.Links, &merkledag.Link{Hash: emptyKey.ToMultihash()}) + } + internalKeys(emptyKey) + hdr := &pb.Set{ + Version: proto.Uint32(1), + Fanout: proto.Uint32(defaultFanout), + Seed: proto.Uint32(seed), + } + if err := writeHdr(n, hdr); err != nil { + return nil, err + } + hdrLen := len(n.Data) + + if estimatedLen < maxItems { + // it'll probably fit + for i := 0; i < maxItems; i++ { + k, data, ok := iter() + if !ok { + // all done + break + } + n.Links = append(n.Links, &merkledag.Link{Hash: k.ToMultihash()}) + n.Data = append(n.Data, data...) + } + // sort by hash, also swap item Data + s := sortByHash{ + links: n.Links[defaultFanout:], + data: n.Data[hdrLen:], + } + sort.Stable(s) + } + + // wasteful but simple + type item struct { + k key.Key + data []byte + } + hashed := make(map[uint32][]item) + for { + k, data, ok := iter() + if !ok { + break + } + h := hash(seed, k) + hashed[h] = append(hashed[h], item{k, data}) + } + for h, items := range hashed { + childIter := func() (k key.Key, data []byte, ok bool) { + if len(items) == 0 { + return "", nil, false + } + first := items[0] + items = items[1:] + return first.k, first.data, true + } + child, err := storeItems(ctx, dag, uint64(len(items)), childIter, internalKeys) + if err != nil { + return nil, err + } + size, err := child.Size() + if err != nil { + return nil, err + } + childKey, err := dag.Add(child) + if err != nil { + return nil, err + } + internalKeys(childKey) + l := &merkledag.Link{ + Name: "", + Hash: childKey.ToMultihash(), + Size: size, + Node: child, + } + n.Links[int(h%defaultFanout)] = l + } + return n, nil +} + +func readHdr(n *merkledag.Node) (*pb.Set, []byte, error) { + hdrLenRaw, consumed := binary.Uvarint(n.Data) + if consumed <= 0 { + return nil, nil, errors.New("invalid Set header length") + } + buf := n.Data[consumed:] + if hdrLenRaw > uint64(len(buf)) { + return nil, nil, errors.New("impossibly large Set header length") + } + // as hdrLenRaw was <= an int, we now know it fits in an int + hdrLen := int(hdrLenRaw) + var hdr pb.Set + if err := proto.Unmarshal(buf[:hdrLen], &hdr); err != nil { + return nil, nil, err + } + buf = buf[hdrLen:] + + if v := hdr.GetVersion(); v != 1 { + return nil, nil, fmt.Errorf("unsupported Set version: %d", v) + } + if uint64(hdr.GetFanout()) > uint64(len(n.Links)) { + return nil, nil, errors.New("impossibly large Fanout") + } + return &hdr, buf, nil +} + +func writeHdr(n *merkledag.Node, hdr *pb.Set) error { + hdrData, err := proto.Marshal(hdr) + if err != nil { + return err + } + n.Data = make([]byte, binary.MaxVarintLen64, binary.MaxVarintLen64+len(hdrData)) + written := binary.PutUvarint(n.Data, uint64(len(hdrData))) + n.Data = n.Data[:written] + n.Data = append(n.Data, hdrData...) + return nil +} + +type walkerFunc func(buf []byte, idx int, link *merkledag.Link) error + +func walkItems(ctx context.Context, dag merkledag.DAGService, n *merkledag.Node, fn walkerFunc, children keyObserver) error { + hdr, buf, err := readHdr(n) + if err != nil { + return err + } + // readHdr guarantees fanout is a safe value + fanout := hdr.GetFanout() + for i, l := range n.Links[fanout:] { + if err := fn(buf, i, l); err != nil { + return err + } + } + for _, l := range n.Links[:fanout] { + children(key.Key(l.Hash)) + if key.Key(l.Hash) == emptyKey { + continue + } + subtree, err := l.GetNode(ctx, dag) + if err != nil { + return err + } + if err := walkItems(ctx, dag, subtree, fn, children); err != nil { + return err + } + } + return nil +} + +func loadSet(ctx context.Context, dag merkledag.DAGService, root *merkledag.Node, name string, internalKeys keyObserver) ([]key.Key, error) { + l, err := root.GetNodeLink(name) + if err != nil { + return nil, err + } + internalKeys(key.Key(l.Hash)) + n, err := l.GetNode(ctx, dag) + if err != nil { + return nil, err + } + + var res []key.Key + walk := func(buf []byte, idx int, link *merkledag.Link) error { + res = append(res, key.Key(link.Hash)) + return nil + } + if err := walkItems(ctx, dag, n, walk, internalKeys); err != nil { + return nil, err + } + return res, nil +} + +func loadMultiset(ctx context.Context, dag merkledag.DAGService, root *merkledag.Node, name string, internalKeys keyObserver) (map[key.Key]uint64, error) { + l, err := root.GetNodeLink(name) + if err != nil { + return nil, err + } + internalKeys(key.Key(l.Hash)) + n, err := l.GetNode(ctx, dag) + if err != nil { + return nil, err + } + + refcounts := make(map[key.Key]uint64) + walk := func(buf []byte, idx int, link *merkledag.Link) error { + refcounts[key.Key(link.Hash)] += uint64(buf[idx]) + return nil + } + if err := walkItems(ctx, dag, n, walk, internalKeys); err != nil { + return nil, err + } + return refcounts, nil +} + +func storeSet(ctx context.Context, dag merkledag.DAGService, keys []key.Key, internalKeys keyObserver) (*merkledag.Node, error) { + iter := func() (k key.Key, data []byte, ok bool) { + if len(keys) == 0 { + return "", nil, false + } + first := keys[0] + keys = keys[1:] + return first, nil, true + } + n, err := storeItems(ctx, dag, uint64(len(keys)), iter, internalKeys) + if err != nil { + return nil, err + } + k, err := dag.Add(n) + if err != nil { + return nil, err + } + internalKeys(k) + return n, nil +} + +func storeMultiset(ctx context.Context, dag merkledag.DAGService, refcounts map[key.Key]uint64, internalKeys keyObserver) (*merkledag.Node, error) { + iter := func() (k key.Key, data []byte, ok bool) { + // Every call of this function returns the next refcount item. + // + // This function splits out the uint64 reference counts as + // smaller increments, as fits in type refcount. Most of the + // time the refcount will fit inside just one, so this saves + // space. + // + // We use range here to pick an arbitrary item in the map, but + // not really iterate the map. + for k, refs := range refcounts { + // Max value a single multiset item can store + num := ^refcount(0) + if refs <= uint64(num) { + // Remaining count fits in a single item; remove the + // key from the map. + num = refcount(refs) + delete(refcounts, k) + } else { + // Count is too large to fit in one item, the key will + // repeat in some later call. + refcounts[k] -= uint64(num) + } + return k, num.Bytes(), true + } + return "", nil, false + } + n, err := storeItems(ctx, dag, uint64(len(refcounts)), iter, internalKeys) + if err != nil { + return nil, err + } + k, err := dag.Add(n) + if err != nil { + return nil, err + } + internalKeys(k) + return n, nil +} From 8eebb2bdbe8caea5daf1626506630e1e7bba3788 Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Mon, 18 May 2015 14:01:07 -0700 Subject: [PATCH 1038/3817] pin: Future-proof against refcount marshaled size changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@a61d377e5c036fb8b68a127d540e9af2f875947f --- pinning/pinner/set.go | 29 ++++++++++--- pinning/pinner/set_test.go | 85 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 pinning/pinner/set_test.go diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 02619bf20..4b6edc2ed 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -44,14 +44,29 @@ type itemIterator func() (k key.Key, data []byte, ok bool) type keyObserver func(key.Key) +// refcount is the marshaled format of refcounts. It may change +// between versions; this is valid for version 1. Changing it may +// become desirable if there are many links with refcount > 255. +// +// There are two guarantees that need to be preserved, if this is +// changed: +// +// - the marshaled format is of fixed size, matching +// unsafe.Sizeof(refcount(0)) +// - methods of refcount handle endianness, and may +// in later versions need encoding/binary. type refcount uint8 func (r refcount) Bytes() []byte { - // refcount size can change in later versions; this may need - // encoding/binary return []byte{byte(r)} } +// readRefcount returns the idx'th refcount in []byte, which is +// assumed to be a sequence of refcount.Bytes results. +func (r *refcount) ReadFromIdx(buf []byte, idx int) { + *r = refcount(buf[idx]) +} + type sortByHash struct { links []*merkledag.Link data []byte @@ -70,9 +85,9 @@ func (s sortByHash) Swap(a, b int) { if len(s.data) != 0 { const n = int(unsafe.Sizeof(refcount(0))) tmp := make([]byte, n) - copy(tmp, s.data[a:a+n]) - copy(s.data[a:a+n], s.data[b:b+n]) - copy(s.data[b:b+n], tmp) + copy(tmp, s.data[a*n:a*n+n]) + copy(s.data[a*n:a*n+n], s.data[b*n:b*n+n]) + copy(s.data[b*n:b*n+n], tmp) } } @@ -267,7 +282,9 @@ func loadMultiset(ctx context.Context, dag merkledag.DAGService, root *merkledag refcounts := make(map[key.Key]uint64) walk := func(buf []byte, idx int, link *merkledag.Link) error { - refcounts[key.Key(link.Hash)] += uint64(buf[idx]) + var r refcount + r.ReadFromIdx(buf, idx) + refcounts[key.Key(link.Hash)] += uint64(r) return nil } if err := walkItems(ctx, dag, n, walk, internalKeys); err != nil { diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go new file mode 100644 index 000000000..ce15df0f7 --- /dev/null +++ b/pinning/pinner/set_test.go @@ -0,0 +1,85 @@ +package pin + +import ( + "testing" + "testing/quick" + + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/ipfs/go-ipfs/blocks/blockstore" + "github.com/ipfs/go-ipfs/blocks/key" + "github.com/ipfs/go-ipfs/blockservice" + "github.com/ipfs/go-ipfs/exchange/offline" + "github.com/ipfs/go-ipfs/merkledag" + "golang.org/x/net/context" +) + +func ignoreKeys(key.Key) {} + +func copyMap(m map[key.Key]uint16) map[key.Key]uint64 { + c := make(map[key.Key]uint64, len(m)) + for k, v := range m { + c[k] = uint64(v) + } + return c +} + +func TestMultisetRoundtrip(t *testing.T) { + dstore := dssync.MutexWrap(datastore.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv, err := blockservice.New(bstore, offline.Exchange(bstore)) + if err != nil { + t.Fatal(err) + } + dag := merkledag.NewDAGService(bserv) + + fn := func(m map[key.Key]uint16) bool { + // Generate a smaller range for refcounts than full uint64, as + // otherwise this just becomes overly cpu heavy, splitting it + // out into too many items. That means we need to convert to + // the right kind of map. As storeMultiset mutates the map as + // part of its bookkeeping, this is actually good. + refcounts := copyMap(m) + + ctx := context.Background() + n, err := storeMultiset(ctx, dag, refcounts, ignoreKeys) + if err != nil { + t.Fatalf("storing multiset: %v", err) + } + root := &merkledag.Node{} + const linkName = "dummylink" + if err := root.AddNodeLink(linkName, n); err != nil { + t.Fatalf("adding link to root node: %v", err) + } + + roundtrip, err := loadMultiset(ctx, dag, root, linkName, ignoreKeys) + if err != nil { + t.Fatalf("loading multiset: %v", err) + } + + orig := copyMap(m) + success := true + for k, want := range orig { + if got, ok := roundtrip[k]; ok { + if got != want { + success = false + t.Logf("refcount changed: %v -> %v for %q", want, got, k) + } + delete(orig, k) + delete(roundtrip, k) + } + } + for k, v := range orig { + success = false + t.Logf("refcount missing: %v for %q", v, k) + } + for k, v := range roundtrip { + success = false + t.Logf("refcount extra: %v for %q", v, k) + } + return success + } + if err := quick.Check(fn, nil); err != nil { + t.Fatal(err) + } +} From 5997913443bfd3673aad47e515914b343e480349 Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Mon, 8 Jun 2015 21:42:04 -0700 Subject: [PATCH 1039/3817] pin: Do not accidentally delete indirect pins on Flush License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@49637f631ea0f06a815d3c0d3f7df77e6b9f9668 --- pinning/pinner/pin_test.go | 21 +++++++++++++++++++++ pinning/pinner/set.go | 11 +++++++++++ 2 files changed, 32 insertions(+) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index d3947254d..e96adb292 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -192,6 +192,27 @@ func TestDuplicateSemantics(t *testing.T) { } } +func TestFlush(t *testing.T) { + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv, err := bs.New(bstore, offline.Exchange(bstore)) + if err != nil { + t.Fatal(err) + } + + dserv := mdag.NewDAGService(bserv) + p := NewPinner(dstore, dserv) + _, k := randNode() + + p.PinWithMode(k, Indirect) + if err := p.Flush(); err != nil { + t.Fatal(err) + } + if !p.IsPinned(k) { + t.Fatal("expected key to still be pinned") + } +} + func TestPinRecursiveFail(t *testing.T) { ctx := context.Background() dstore := dssync.MutexWrap(ds.NewMapDatastore()) diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 4b6edc2ed..71851af6e 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -314,7 +314,18 @@ func storeSet(ctx context.Context, dag merkledag.DAGService, keys []key.Key, int return n, nil } +func copyRefcounts(orig map[key.Key]uint64) map[key.Key]uint64 { + r := make(map[key.Key]uint64, len(orig)) + for k, v := range orig { + r[k] = v + } + return r +} + func storeMultiset(ctx context.Context, dag merkledag.DAGService, refcounts map[key.Key]uint64, internalKeys keyObserver) (*merkledag.Node, error) { + // make a working copy of the refcounts + refcounts = copyRefcounts(refcounts) + iter := func() (k key.Key, data []byte, ok bool) { // Every call of this function returns the next refcount item. // From af1a89accb7e5c5fd51744a5c1f36e0e7f478fa3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 17 Jun 2015 09:19:05 -0700 Subject: [PATCH 1040/3817] using multistream muxer * ID service stream * make the relay service use msmux * fix nc tests Note from jbenet: Maybe we should remove the old protocol/muxer and see what breaks. It shouldn't be used by anything now. License: MIT Signed-off-by: Jeromy Signed-off-by: Juan Batiz-Benet This commit was moved from ipfs/go-ipfs-pinner@725c73e3f319589e434c508c001fed35bbb0aed5 --- pinning/pinner/pin.go | 5 ++++- pinning/pinner/set_test.go | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 726c62729..4d17138ab 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -5,6 +5,7 @@ package pin import ( "fmt" "sync" + "time" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" @@ -242,7 +243,9 @@ func LoadPinner(d ds.ThreadSafeDatastore, dserv mdag.DAGService) (Pinner, error) rootKey := key.Key(rootKeyBytes) - ctx := context.TODO() + ctx, cancel := context.WithTimeout(context.TODO(), time.Second*5) + defer cancel() + root, err := dserv.Get(ctx, rootKey) if err != nil { return nil, fmt.Errorf("cannot find pinning root object: %v", err) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index ce15df0f7..83af07780 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -6,12 +6,12 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" "github.com/ipfs/go-ipfs/merkledag" - "golang.org/x/net/context" ) func ignoreKeys(key.Key) {} From 095afb13d325df28ce3046edb50240cddd7a04bd Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 9 Jul 2015 05:57:21 -0700 Subject: [PATCH 1041/3817] renamed {R,}Lock -> {Pin,GC}Lock License: MIT Signed-off-by: Juan Batiz-Benet This commit was moved from ipfs/go-ipfs-pinner@370c62c1f2c65790566a65713e9fe5d55d39ded1 --- pinning/pinner/pin_test.go | 5 +---- pinning/pinner/set_test.go | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index e96adb292..69f84f531 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -195,10 +195,7 @@ func TestDuplicateSemantics(t *testing.T) { func TestFlush(t *testing.T) { dstore := dssync.MutexWrap(ds.NewMapDatastore()) bstore := blockstore.NewBlockstore(dstore) - bserv, err := bs.New(bstore, offline.Exchange(bstore)) - if err != nil { - t.Fatal(err) - } + bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) p := NewPinner(dstore, dserv) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 83af07780..a48744939 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -27,10 +27,7 @@ func copyMap(m map[key.Key]uint16) map[key.Key]uint64 { func TestMultisetRoundtrip(t *testing.T) { dstore := dssync.MutexWrap(datastore.NewMapDatastore()) bstore := blockstore.NewBlockstore(dstore) - bserv, err := blockservice.New(bstore, offline.Exchange(bstore)) - if err != nil { - t.Fatal(err) - } + bserv := blockservice.New(bstore, offline.Exchange(bstore)) dag := merkledag.NewDAGService(bserv) fn := func(m map[key.Key]uint16) bool { From 93ebb47f23731159c8b2b36fb070f309ba536126 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 23 Jun 2015 16:01:32 -0700 Subject: [PATCH 1042/3817] implement mark and sweep GC License: MIT Signed-off-by: Jeromy dont GC blocks used by pinner License: MIT Signed-off-by: Jeromy comment GC algo License: MIT Signed-off-by: Jeromy add lock to blockstore to prevent GC from eating wanted blocks License: MIT Signed-off-by: Jeromy improve FetchGraph License: MIT Signed-off-by: Jeromy separate interfaces for blockstore and GCBlockstore License: MIT Signed-off-by: Jeromy reintroduce indirect pinning, add enumerateChildren dag method License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@1d2607cd168343cbe0568ccef8dd96199726e431 --- pinning/pinner/gc/gc.go | 99 ++++++++++++++++++++++++++++++++++ pinning/pinner/pin.go | 107 +++++++------------------------------ pinning/pinner/pin_test.go | 24 ++------- 3 files changed, 122 insertions(+), 108 deletions(-) create mode 100644 pinning/pinner/gc/gc.go diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go new file mode 100644 index 000000000..3e2b85049 --- /dev/null +++ b/pinning/pinner/gc/gc.go @@ -0,0 +1,99 @@ +package gc + +import ( + bstore "github.com/ipfs/go-ipfs/blocks/blockstore" + key "github.com/ipfs/go-ipfs/blocks/key" + bserv "github.com/ipfs/go-ipfs/blockservice" + offline "github.com/ipfs/go-ipfs/exchange/offline" + dag "github.com/ipfs/go-ipfs/merkledag" + pin "github.com/ipfs/go-ipfs/pin" + + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" +) + +var log = logging.Logger("gc") + +// GC performs a mark and sweep garbage collection of the blocks in the blockstore +// first, it creates a 'marked' set and adds to it the following: +// - all recursively pinned blocks, plus all of their descendants (recursively) +// - all directly pinned blocks +// - all blocks utilized internally by the pinner +// +// The routine then iterates over every block in the blockstore and +// deletes any block that is not found in the marked set. +func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner) (<-chan key.Key, error) { + unlock := bs.GCLock() + defer unlock() + + bsrv := bserv.New(bs, offline.Exchange(bs)) + ds := dag.NewDAGService(bsrv) + + // KeySet currently implemented in memory, in the future, may be bloom filter or + // disk backed to conserve memory. + gcs := key.NewKeySet() + for _, k := range pn.RecursiveKeys() { + gcs.Add(k) + nd, err := ds.Get(ctx, k) + if err != nil { + return nil, err + } + + // EnumerateChildren recursively walks the dag and adds the keys to the given set + err = dag.EnumerateChildren(ctx, ds, nd, gcs) + if err != nil { + return nil, err + } + } + for _, k := range pn.DirectKeys() { + gcs.Add(k) + } + for _, k := range pn.InternalPins() { + gcs.Add(k) + + nd, err := ds.Get(ctx, k) + if err != nil { + return nil, err + } + + // EnumerateChildren recursively walks the dag and adds the keys to the given set + err = dag.EnumerateChildren(ctx, ds, nd, gcs) + if err != nil { + return nil, err + } + } + + keychan, err := bs.AllKeysChan(ctx) + if err != nil { + return nil, err + } + + output := make(chan key.Key) + go func() { + defer close(output) + for { + select { + case k, ok := <-keychan: + if !ok { + return + } + if !gcs.Has(k) { + err := bs.DeleteBlock(k) + if err != nil { + log.Debugf("Error removing key from blockstore: %s", err) + return + } + select { + case output <- k: + case <-ctx.Done(): + return + } + } + case <-ctx.Done(): + return + } + } + }() + + return output, nil +} diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 4d17138ab..4221fae59 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -24,7 +24,6 @@ var emptyKey = key.B58KeyDecode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" const ( linkDirect = "direct" linkRecursive = "recursive" - linkIndirect = "indirect" ) type PinMode int @@ -32,7 +31,6 @@ type PinMode int const ( Recursive PinMode = iota Direct - Indirect NotPinned ) @@ -52,8 +50,8 @@ type Pinner interface { Flush() error DirectKeys() []key.Key - IndirectKeys() map[key.Key]uint64 RecursiveKeys() []key.Key + InternalPins() []key.Key } // pinner implements the Pinner interface @@ -61,7 +59,7 @@ type pinner struct { lock sync.RWMutex recursePin set.BlockSet directPin set.BlockSet - indirPin *indirectPin + // Track the keys used for storing the pinning state, so gc does // not delete them. internalPin map[key.Key]struct{} @@ -80,7 +78,6 @@ func NewPinner(dstore ds.ThreadSafeDatastore, serv mdag.DAGService) Pinner { return &pinner{ recursePin: rcset, directPin: dirset, - indirPin: newIndirectPin(), dserv: serv, dstore: dstore, } @@ -104,7 +101,8 @@ func (p *pinner) Pin(ctx context.Context, node *mdag.Node, recurse bool) error { p.directPin.RemoveBlock(k) } - err := p.pinLinks(ctx, node) + // fetch entire graph + err := mdag.FetchGraph(ctx, node, p.dserv) if err != nil { return err } @@ -131,72 +129,18 @@ func (p *pinner) Unpin(ctx context.Context, k key.Key, recursive bool) error { if p.recursePin.HasKey(k) { if recursive { p.recursePin.RemoveBlock(k) - node, err := p.dserv.Get(ctx, k) - if err != nil { - return err - } - - return p.unpinLinks(ctx, node) + return nil } else { return fmt.Errorf("%s is pinned recursively", k) } } else if p.directPin.HasKey(k) { p.directPin.RemoveBlock(k) return nil - } else if p.indirPin.HasKey(k) { - return fmt.Errorf("%s is pinned indirectly. indirect pins cannot be removed directly", k) } else { return fmt.Errorf("%s is not pinned", k) } } -func (p *pinner) unpinLinks(ctx context.Context, node *mdag.Node) error { - for _, l := range node.Links { - node, err := l.GetNode(ctx, p.dserv) - if err != nil { - return err - } - - k, err := node.Key() - if err != nil { - return err - } - - p.indirPin.Decrement(k) - - err = p.unpinLinks(ctx, node) - if err != nil { - return err - } - } - return nil -} - -func (p *pinner) pinIndirectRecurse(ctx context.Context, node *mdag.Node) error { - k, err := node.Key() - if err != nil { - return err - } - - p.indirPin.Increment(k) - return p.pinLinks(ctx, node) -} - -func (p *pinner) pinLinks(ctx context.Context, node *mdag.Node) error { - for _, ng := range p.dserv.GetDAG(ctx, node) { - subnode, err := ng.Get(ctx) - if err != nil { - // TODO: Maybe just log and continue? - return err - } - err = p.pinIndirectRecurse(ctx, subnode) - if err != nil { - return err - } - } - return nil -} - func (p *pinner) isInternalPin(key key.Key) bool { _, ok := p.internalPin[key] return ok @@ -208,7 +152,6 @@ func (p *pinner) IsPinned(key key.Key) bool { defer p.lock.RUnlock() return p.recursePin.HasKey(key) || p.directPin.HasKey(key) || - p.indirPin.HasKey(key) || p.isInternalPin(key) } @@ -218,8 +161,6 @@ func (p *pinner) RemovePinWithMode(key key.Key, mode PinMode) { switch mode { case Direct: p.directPin.RemoveBlock(key) - case Indirect: - p.indirPin.Decrement(key) case Recursive: p.recursePin.RemoveBlock(key) default: @@ -274,14 +215,6 @@ func LoadPinner(d ds.ThreadSafeDatastore, dserv mdag.DAGService) (Pinner, error) p.directPin = set.SimpleSetFromKeys(directKeys) } - { // load indirect set - refcnt, err := loadMultiset(ctx, dserv, root, linkIndirect, recordInternal) - if err != nil { - return nil, fmt.Errorf("cannot load indirect pins: %v", err) - } - p.indirPin = &indirectPin{refCounts: refcnt} - } - p.internalPin = internalPin // assign services @@ -296,11 +229,6 @@ func (p *pinner) DirectKeys() []key.Key { return p.directPin.GetKeys() } -// IndirectKeys returns a slice containing the indirectly pinned keys -func (p *pinner) IndirectKeys() map[key.Key]uint64 { - return p.indirPin.GetRefs() -} - // RecursiveKeys returns a slice containing the recursively pinned keys func (p *pinner) RecursiveKeys() []key.Key { return p.recursePin.GetKeys() @@ -339,20 +267,17 @@ func (p *pinner) Flush() error { } } - { - n, err := storeMultiset(ctx, p.dserv, p.indirPin.GetRefs(), recordInternal) - if err != nil { - return err - } - if err := root.AddNodeLink(linkIndirect, n); err != nil { - return err - } + // add the empty node, its referenced by the pin sets but never created + _, err := p.dserv.Add(new(mdag.Node)) + if err != nil { + return err } k, err := p.dserv.Add(root) if err != nil { return err } + internalPin[k] = struct{}{} if err := p.dstore.Put(pinDatastoreKey, []byte(k)); err != nil { return fmt.Errorf("cannot store pin state: %v", err) @@ -361,6 +286,16 @@ func (p *pinner) Flush() error { return nil } +func (p *pinner) InternalPins() []key.Key { + p.lock.Lock() + defer p.lock.Unlock() + var out []key.Key + for k, _ := range p.internalPin { + out = append(out, k) + } + return out +} + // PinWithMode allows the user to have fine grained control over pin // counts func (p *pinner) PinWithMode(k key.Key, mode PinMode) { @@ -371,7 +306,5 @@ func (p *pinner) PinWithMode(k key.Key, mode PinMode) { p.recursePin.AddBlock(k) case Direct: p.directPin.AddBlock(k) - case Indirect: - p.indirPin.Increment(k) } } diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 69f84f531..15fd0a2f9 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -53,7 +53,7 @@ func TestPinnerBasic(t *testing.T) { } // create new node c, to be indirectly pinned through b - c, ck := randNode() + c, _ := randNode() _, err = dserv.Add(c) if err != nil { t.Fatal(err) @@ -82,10 +82,6 @@ func TestPinnerBasic(t *testing.T) { t.Fatal(err) } - if !p.IsPinned(ck) { - t.Fatal("Child of recursively pinned node not found") - } - bk, _ := b.Key() if !p.IsPinned(bk) { t.Fatal("Recursively pinned node not found..") @@ -95,7 +91,7 @@ func TestPinnerBasic(t *testing.T) { d.AddNodeLink("a", a) d.AddNodeLink("c", c) - e, ek := randNode() + e, _ := randNode() d.AddNodeLink("e", e) // Must be in dagserv for unpin to work @@ -110,10 +106,6 @@ func TestPinnerBasic(t *testing.T) { t.Fatal(err) } - if !p.IsPinned(ek) { - t.Fatal(err) - } - dk, _ := d.Key() if !p.IsPinned(dk) { t.Fatal("pinned node not found.") @@ -125,11 +117,6 @@ func TestPinnerBasic(t *testing.T) { t.Fatal(err) } - // c should still be pinned under b - if !p.IsPinned(ck) { - t.Fatal("Recursive / indirect unpin fail.") - } - err = p.Flush() if err != nil { t.Fatal(err) @@ -145,11 +132,6 @@ func TestPinnerBasic(t *testing.T) { t.Fatal("Could not find pinned node!") } - // Test indirectly pinned - if !np.IsPinned(ck) { - t.Fatal("could not find indirectly pinned node") - } - // Test recursively pinned if !np.IsPinned(bk) { t.Fatal("could not find recursively pinned node") @@ -201,7 +183,7 @@ func TestFlush(t *testing.T) { p := NewPinner(dstore, dserv) _, k := randNode() - p.PinWithMode(k, Indirect) + p.PinWithMode(k, Recursive) if err := p.Flush(); err != nil { t.Fatal(err) } From b121e522b61be44a9fb244da3f4d676600a07e0d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 7 Jul 2015 09:04:03 -0700 Subject: [PATCH 1043/3817] merkledag FetchGraph and EnumerateChildren This commit improves (fixes) the FetchGraph call for recursively fetching every descendant node of a given merkledag node. This operation should be the simplest way of ensuring that you have replicated a dag locally. This commit also implements a method in the merkledag package called EnumerateChildren, this method is used to get a set of the keys of every descendant node of the given node. All keys found are noted in the passed in KeySet, which may in the future be implemented on disk to avoid excessive memory consumption. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@b6915124fa6a2dd0897967b7ebea48011c6114ba --- ipld/merkledag/merkledag.go | 119 ++++++++++++++++++++++++------- ipld/merkledag/merkledag_test.go | 79 +++++++++++++++++++- 2 files changed, 169 insertions(+), 29 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index da921ed09..5158c42aa 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -3,7 +3,6 @@ package merkledag import ( "fmt" - "sync" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" @@ -121,41 +120,86 @@ func (n *dagService) Remove(nd *Node) error { return n.Blocks.DeleteBlock(k) } -// FetchGraph asynchronously fetches all nodes that are children of the given -// node, and returns a channel that may be waited upon for the fetch to complete -func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} { - log.Warning("Untested.") - var wg sync.WaitGroup - done := make(chan struct{}) +// FetchGraph fetches all nodes that are children of the given node +func FetchGraph(ctx context.Context, root *Node, serv DAGService) error { + toprocess := make(chan []key.Key, 8) + nodes := make(chan *Node, 8) + errs := make(chan error, 1) - for _, l := range root.Links { - wg.Add(1) - go func(lnk *Link) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + defer close(toprocess) - // Signal child is done on way out - defer wg.Done() - select { - case <-ctx.Done(): - return + go fetchNodes(ctx, serv, toprocess, nodes, errs) + + nodes <- root + live := 1 + + for { + select { + case nd, ok := <-nodes: + if !ok { + return nil } - nd, err := lnk.GetNode(ctx, serv) - if err != nil { - log.Debug(err) - return + var keys []key.Key + for _, lnk := range nd.Links { + keys = append(keys, key.Key(lnk.Hash)) } + keys = dedupeKeys(keys) - // Wait for children to finish - <-FetchGraph(ctx, nd, serv) - }(l) + // keep track of open request, when zero, we're done + live += len(keys) - 1 + + if live == 0 { + return nil + } + + if len(keys) > 0 { + select { + case toprocess <- keys: + case <-ctx.Done(): + return ctx.Err() + } + } + case err := <-errs: + return err + case <-ctx.Done(): + return ctx.Err() + } } +} - go func() { - wg.Wait() - done <- struct{}{} - }() +func fetchNodes(ctx context.Context, ds DAGService, in <-chan []key.Key, out chan<- *Node, errs chan<- error) { + defer close(out) + for { + select { + case ks, ok := <-in: + if !ok { + return + } - return done + ng := ds.GetNodes(ctx, ks) + for _, g := range ng { + go func(g NodeGetter) { + nd, err := g.Get(ctx) + if err != nil { + select { + case errs <- err: + case <-ctx.Done(): + } + return + } + + select { + case out <- nd: + case <-ctx.Done(): + return + } + }(g) + } + } + } } // FindLinks searches this nodes links for the given key, @@ -318,3 +362,24 @@ func (t *Batch) Commit() error { t.size = 0 return err } + +// EnumerateChildren will walk the dag below the given root node and add all +// unseen children to the passed in set. +// TODO: parallelize to avoid disk latency perf hits? +func EnumerateChildren(ctx context.Context, ds DAGService, root *Node, set key.KeySet) error { + for _, lnk := range root.Links { + k := key.Key(lnk.Hash) + if !set.Has(k) { + set.Add(k) + child, err := ds.Get(ctx, k) + if err != nil { + return err + } + err = EnumerateChildren(ctx, ds, child, set) + if err != nil { + return err + } + } + } + return nil +} diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index dda4a976e..3e316b083 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -130,7 +130,7 @@ func SubtestNodeStat(t *testing.T, n *Node) { } if expected != *actual { - t.Errorf("n.Stat incorrect.\nexpect: %s\nactual: %s", expected, actual) + t.Error("n.Stat incorrect.\nexpect: %s\nactual: %s", expected, actual) } else { fmt.Printf("n.Stat correct: %s\n", actual) } @@ -232,7 +232,6 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { } } } - func TestRecursiveAdd(t *testing.T) { a := &Node{Data: []byte("A")} b := &Node{Data: []byte("B")} @@ -298,3 +297,79 @@ func TestCantGet(t *testing.T) { t.Fatal("expected err not found, got: ", err) } } + +func TestFetchGraph(t *testing.T) { + bsi := bstest.Mocks(t, 1)[0] + ds := NewDAGService(bsi) + + read := io.LimitReader(u.NewTimeSeededRand(), 1024*32) + spl := &chunk.SizeSplitter{512} + + root, err := imp.BuildDagFromReader(read, ds, spl, nil) + if err != nil { + t.Fatal(err) + } + + err = FetchGraph(context.TODO(), root, ds) + if err != nil { + t.Fatal(err) + } +} + +func TestFetchGraphOther(t *testing.T) { + var dservs []DAGService + for _, bsi := range bstest.Mocks(t, 2) { + dservs = append(dservs, NewDAGService(bsi)) + } + + read := io.LimitReader(u.NewTimeSeededRand(), 1024*32) + spl := &chunk.SizeSplitter{512} + + root, err := imp.BuildDagFromReader(read, dservs[0], spl, nil) + if err != nil { + t.Fatal(err) + } + + err = FetchGraph(context.TODO(), root, dservs[1]) + if err != nil { + t.Fatal(err) + } +} + +func TestEnumerateChildren(t *testing.T) { + bsi := bstest.Mocks(t, 1) + ds := NewDAGService(bsi[0]) + + spl := &chunk.SizeSplitter{512} + + read := io.LimitReader(u.NewTimeSeededRand(), 1024*1024) + + root, err := imp.BuildDagFromReader(read, ds, spl, nil) + if err != nil { + t.Fatal(err) + } + + ks := key.NewKeySet() + err = EnumerateChildren(context.Background(), ds, root, ks) + if err != nil { + t.Fatal(err) + } + + var traverse func(n *Node) + traverse = func(n *Node) { + // traverse dag and check + for _, lnk := range n.Links { + k := key.Key(lnk.Hash) + if !ks.Has(k) { + t.Fatal("missing key in set!") + } + child, err := ds.Get(context.Background(), k) + if err != nil { + t.Fatal(err) + } + traverse(child) + } + } + + traverse(root) +} From 9a81202be305ca971c4be1cd35fea42caf1a0eb7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 9 Jul 2015 16:03:48 -0700 Subject: [PATCH 1044/3817] break up GC logic License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@ecda80aebc3d42d96a01eca768e6723bce38b4f9 --- pinning/pinner/gc/gc.go | 74 +++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 3e2b85049..f435959b9 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -29,38 +29,9 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner) (<-chan key. bsrv := bserv.New(bs, offline.Exchange(bs)) ds := dag.NewDAGService(bsrv) - // KeySet currently implemented in memory, in the future, may be bloom filter or - // disk backed to conserve memory. - gcs := key.NewKeySet() - for _, k := range pn.RecursiveKeys() { - gcs.Add(k) - nd, err := ds.Get(ctx, k) - if err != nil { - return nil, err - } - - // EnumerateChildren recursively walks the dag and adds the keys to the given set - err = dag.EnumerateChildren(ctx, ds, nd, gcs) - if err != nil { - return nil, err - } - } - for _, k := range pn.DirectKeys() { - gcs.Add(k) - } - for _, k := range pn.InternalPins() { - gcs.Add(k) - - nd, err := ds.Get(ctx, k) - if err != nil { - return nil, err - } - - // EnumerateChildren recursively walks the dag and adds the keys to the given set - err = dag.EnumerateChildren(ctx, ds, nd, gcs) - if err != nil { - return nil, err - } + gcs, err := ColoredSet(pn, ds) + if err != nil { + return nil, err } keychan, err := bs.AllKeysChan(ctx) @@ -97,3 +68,42 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner) (<-chan key. return output, nil } + +func Descendants(ds dag.DAGService, set key.KeySet, roots []key.Key) error { + for _, k := range roots { + set.Add(k) + nd, err := ds.Get(context.Background(), k) + if err != nil { + return err + } + + // EnumerateChildren recursively walks the dag and adds the keys to the given set + err = dag.EnumerateChildren(context.Background(), ds, nd, set) + if err != nil { + return err + } + } + + return nil +} + +func ColoredSet(pn pin.Pinner, ds dag.DAGService) (key.KeySet, error) { + // KeySet currently implemented in memory, in the future, may be bloom filter or + // disk backed to conserve memory. + gcs := key.NewKeySet() + err := Descendants(ds, gcs, pn.RecursiveKeys()) + if err != nil { + return nil, err + } + + for _, k := range pn.DirectKeys() { + gcs.Add(k) + } + + err = Color(ds, gcs, pn.InternalPins()) + if err != nil { + return nil, err + } + + return gcs, nil +} From 4c6495c1d72aca88a52e83c3ff9f8c60825ecc47 Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Mon, 8 Jun 2015 21:43:11 -0700 Subject: [PATCH 1045/3817] dagmodifier: Don't lose pin if old and new key happen to be equal License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@96bed09b4f0d8ba002c0047510c75b8ba5658a2b --- unixfs/mod/dagmodifier.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index bb22f289f..df1abe0b6 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -209,9 +209,10 @@ func (dm *DagModifier) Sync() error { dm.curNode = nd } - // Finalize correct pinning, and flush pinner - dm.mp.PinWithMode(thisk, pin.Recursive) + // Finalize correct pinning, and flush pinner. + // Be careful about the order, as curk might equal thisk. dm.mp.RemovePinWithMode(curk, pin.Recursive) + dm.mp.PinWithMode(thisk, pin.Recursive) err = dm.mp.Flush() if err != nil { return err From abf4c52d66885411c2b040f9ba80e768a1d1ed9f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 8 Jul 2015 08:48:18 -0700 Subject: [PATCH 1046/3817] address concerns from PR License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@10935680eae76478c24a1bfaf061c6229abf7c2d --- ipld/merkledag/merkledag.go | 159 ++++++++++++++++--------------- ipld/merkledag/merkledag_test.go | 29 +++--- 2 files changed, 94 insertions(+), 94 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 5158c42aa..a6c6633f0 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -122,84 +122,7 @@ func (n *dagService) Remove(nd *Node) error { // FetchGraph fetches all nodes that are children of the given node func FetchGraph(ctx context.Context, root *Node, serv DAGService) error { - toprocess := make(chan []key.Key, 8) - nodes := make(chan *Node, 8) - errs := make(chan error, 1) - - ctx, cancel := context.WithCancel(ctx) - defer cancel() - defer close(toprocess) - - go fetchNodes(ctx, serv, toprocess, nodes, errs) - - nodes <- root - live := 1 - - for { - select { - case nd, ok := <-nodes: - if !ok { - return nil - } - - var keys []key.Key - for _, lnk := range nd.Links { - keys = append(keys, key.Key(lnk.Hash)) - } - keys = dedupeKeys(keys) - - // keep track of open request, when zero, we're done - live += len(keys) - 1 - - if live == 0 { - return nil - } - - if len(keys) > 0 { - select { - case toprocess <- keys: - case <-ctx.Done(): - return ctx.Err() - } - } - case err := <-errs: - return err - case <-ctx.Done(): - return ctx.Err() - } - } -} - -func fetchNodes(ctx context.Context, ds DAGService, in <-chan []key.Key, out chan<- *Node, errs chan<- error) { - defer close(out) - for { - select { - case ks, ok := <-in: - if !ok { - return - } - - ng := ds.GetNodes(ctx, ks) - for _, g := range ng { - go func(g NodeGetter) { - nd, err := g.Get(ctx) - if err != nil { - select { - case errs <- err: - case <-ctx.Done(): - } - return - } - - select { - case out <- nd: - case <-ctx.Done(): - return - } - }(g) - } - } - } + return EnumerateChildrenAsync(ctx, serv, root, key.NewKeySet()) } // FindLinks searches this nodes links for the given key, @@ -383,3 +306,83 @@ func EnumerateChildren(ctx context.Context, ds DAGService, root *Node, set key.K } return nil } + +func EnumerateChildrenAsync(ctx context.Context, ds DAGService, root *Node, set key.KeySet) error { + toprocess := make(chan []key.Key, 8) + nodes := make(chan *Node, 8) + errs := make(chan error, 1) + + ctx, cancel := context.WithCancel(ctx) + defer cancel() + defer close(toprocess) + + go fetchNodes(ctx, ds, toprocess, nodes, errs) + + nodes <- root + live := 1 + + for { + select { + case nd, ok := <-nodes: + if !ok { + return nil + } + // a node has been fetched + live-- + + var keys []key.Key + for _, lnk := range nd.Links { + k := key.Key(lnk.Hash) + if !set.Has(k) { + set.Add(k) + live++ + keys = append(keys, k) + } + } + + if live == 0 { + return nil + } + + if len(keys) > 0 { + select { + case toprocess <- keys: + case <-ctx.Done(): + return ctx.Err() + } + } + case err := <-errs: + return err + case <-ctx.Done(): + return ctx.Err() + } + } +} + +func fetchNodes(ctx context.Context, ds DAGService, in <-chan []key.Key, out chan<- *Node, errs chan<- error) { + defer close(out) + + get := func(g NodeGetter) { + nd, err := g.Get(ctx) + if err != nil { + select { + case errs <- err: + case <-ctx.Done(): + } + return + } + + select { + case out <- nd: + case <-ctx.Done(): + return + } + } + + for ks := range in { + ng := ds.GetNodes(ctx, ks) + for _, g := range ng { + go get(g) + } + } +} diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 3e316b083..674df6d53 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -299,38 +299,35 @@ func TestCantGet(t *testing.T) { } func TestFetchGraph(t *testing.T) { - bsi := bstest.Mocks(t, 1)[0] - ds := NewDAGService(bsi) + var dservs []DAGService + bsis := bstest.Mocks(t, 2) + for _, bsi := range bsis { + dservs = append(dservs, NewDAGService(bsi)) + } read := io.LimitReader(u.NewTimeSeededRand(), 1024*32) spl := &chunk.SizeSplitter{512} - root, err := imp.BuildDagFromReader(read, ds, spl, nil) + root, err := imp.BuildDagFromReader(read, dservs[0], spl, nil) if err != nil { t.Fatal(err) } - err = FetchGraph(context.TODO(), root, ds) + err = FetchGraph(context.TODO(), root, dservs[1]) if err != nil { t.Fatal(err) } -} - -func TestFetchGraphOther(t *testing.T) { - var dservs []DAGService - for _, bsi := range bstest.Mocks(t, 2) { - dservs = append(dservs, NewDAGService(bsi)) - } - - read := io.LimitReader(u.NewTimeSeededRand(), 1024*32) - spl := &chunk.SizeSplitter{512} - root, err := imp.BuildDagFromReader(read, dservs[0], spl, nil) + // create an offline dagstore and ensure all blocks were fetched + bs, err := bserv.New(bsis[1].Blockstore, offline.Exchange(bsis[1].Blockstore)) if err != nil { t.Fatal(err) } - err = FetchGraph(context.TODO(), root, dservs[1]) + offline_ds := NewDAGService(bs) + ks := key.NewKeySet() + + err = EnumerateChildren(context.Background(), offline_ds, root, ks) if err != nil { t.Fatal(err) } From c879078b2bbe2ec1f6336d00f39ee95e6e789cdc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 10 Jul 2015 10:49:19 -0700 Subject: [PATCH 1047/3817] addressing comments from CR License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@96547c99ef13e73283836741d548b23cb0837761 --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin.go | 60 ++++++++++++++++++++++++++--- pinning/pinner/pin_test.go | 77 +++++++++++++++++++++++++++++--------- 3 files changed, 115 insertions(+), 24 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index f435959b9..ec61f816a 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -100,7 +100,7 @@ func ColoredSet(pn pin.Pinner, ds dag.DAGService) (key.KeySet, error) { gcs.Add(k) } - err = Color(ds, gcs, pn.InternalPins()) + err = Descendants(ds, gcs, pn.InternalPins()) if err != nil { return nil, err } diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 4221fae59..8905293ed 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -35,7 +35,7 @@ const ( ) type Pinner interface { - IsPinned(key.Key) bool + IsPinned(key.Key) (string, bool, error) Pin(context.Context, *mdag.Node, bool) error Unpin(context.Context, key.Key, bool) error @@ -147,12 +147,38 @@ func (p *pinner) isInternalPin(key key.Key) bool { } // IsPinned returns whether or not the given key is pinned -func (p *pinner) IsPinned(key key.Key) bool { +// and an explanation of why its pinned +func (p *pinner) IsPinned(k key.Key) (string, bool, error) { p.lock.RLock() defer p.lock.RUnlock() - return p.recursePin.HasKey(key) || - p.directPin.HasKey(key) || - p.isInternalPin(key) + if p.recursePin.HasKey(k) { + return "recursive", true, nil + } + if p.directPin.HasKey(k) { + return "direct", true, nil + } + if p.isInternalPin(k) { + return "internal", true, nil + } + + for _, rk := range p.recursePin.GetKeys() { + ss := &searchSet{target: k} + + rnd, err := p.dserv.Get(context.Background(), rk) + if err != nil { + return "", false, err + } + + err = mdag.EnumerateChildren(context.Background(), p.dserv, rnd, ss) + if err != nil { + return "", false, err + } + + if ss.found { + return rk.B58String(), true, nil + } + } + return "", false, nil } func (p *pinner) RemovePinWithMode(key key.Key, mode PinMode) { @@ -308,3 +334,27 @@ func (p *pinner) PinWithMode(k key.Key, mode PinMode) { p.directPin.AddBlock(k) } } + +// searchSet implements key.KeySet in +type searchSet struct { + target key.Key + found bool +} + +func (ss *searchSet) Add(k key.Key) { + if ss.target == k { + ss.found = true + } +} + +func (ss *searchSet) Has(k key.Key) bool { + // returning true to all Has queries will cause EnumerateChildren to return + // almost immediately + return ss.found +} + +func (ss *searchSet) Keys() []key.Key { + return nil +} + +func (ss *searchSet) Remove(key.Key) {} diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 15fd0a2f9..d681bb8df 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -24,6 +24,17 @@ func randNode() (*mdag.Node, key.Key) { return nd, k } +func assertPinned(t *testing.T, p Pinner, k key.Key, failmsg string) { + _, pinned, err := p.IsPinned(k) + if err != nil { + t.Fatal(err) + } + + if !pinned { + t.Fatal(failmsg) + } +} + func TestPinnerBasic(t *testing.T) { ctx := context.Background() @@ -48,13 +59,11 @@ func TestPinnerBasic(t *testing.T) { t.Fatal(err) } - if !p.IsPinned(ak) { - t.Fatal("Failed to find key") - } + assertPinned(t, p, ak, "Failed to find key") // create new node c, to be indirectly pinned through b c, _ := randNode() - _, err = dserv.Add(c) + ck, err := dserv.Add(c) if err != nil { t.Fatal(err) } @@ -82,10 +91,10 @@ func TestPinnerBasic(t *testing.T) { t.Fatal(err) } + assertPinned(t, p, ck, "child of recursively pinned node not found") + bk, _ := b.Key() - if !p.IsPinned(bk) { - t.Fatal("Recursively pinned node not found..") - } + assertPinned(t, p, bk, "Recursively pinned node not found..") d, _ := randNode() d.AddNodeLink("a", a) @@ -107,9 +116,7 @@ func TestPinnerBasic(t *testing.T) { } dk, _ := d.Key() - if !p.IsPinned(dk) { - t.Fatal("pinned node not found.") - } + assertPinned(t, p, dk, "pinned node not found.") // Test recursive unpin err = p.Unpin(ctx, dk, true) @@ -128,14 +135,10 @@ func TestPinnerBasic(t *testing.T) { } // Test directly pinned - if !np.IsPinned(ak) { - t.Fatal("Could not find pinned node!") - } + assertPinned(t, np, ak, "Could not find pinned node!") // Test recursively pinned - if !np.IsPinned(bk) { - t.Fatal("could not find recursively pinned node") - } + assertPinned(t, np, bk, "could not find recursively pinned node") } func TestDuplicateSemantics(t *testing.T) { @@ -187,8 +190,46 @@ func TestFlush(t *testing.T) { if err := p.Flush(); err != nil { t.Fatal(err) } - if !p.IsPinned(k) { - t.Fatal("expected key to still be pinned") + assertPinned(t, p, k, "expected key to still be pinned") +} + +func TestPinRecursiveFail(t *testing.T) { + ctx := context.Background() + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv, err := bs.New(bstore, offline.Exchange(bstore)) + if err != nil { + t.Fatal(err) + } + + dserv := mdag.NewDAGService(bserv) + + p := NewPinner(dstore, dserv) + + a, _ := randNode() + b, _ := randNode() + err = a.AddNodeLinkClean("child", b) + if err != nil { + t.Fatal(err) + } + + // Note: this isnt a time based test, we expect the pin to fail + mctx, _ := context.WithTimeout(ctx, time.Millisecond) + err = p.Pin(mctx, a, true) + if err == nil { + t.Fatal("should have failed to pin here") + } + + _, err = dserv.Add(b) + if err != nil { + t.Fatal(err) + } + + // this one is time based... but shouldnt cause any issues + mctx, _ = context.WithTimeout(ctx, time.Second) + err = p.Pin(mctx, a, true) + if err != nil { + t.Fatal(err) } } From 0a1fbf5c0bca78da66e779c01a005c924c1f35e2 Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Mon, 8 Jun 2015 21:43:40 -0700 Subject: [PATCH 1048/3817] dagmodifier test: Add TODO note about how bad luck can cause test failure License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@bf9c085882a43ff5a8ff7e87bd479cc566a033ae --- unixfs/mod/dagmodifier_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 25caadfb0..98393b377 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -568,6 +568,7 @@ func TestCorrectPinning(t *testing.T) { indirpins := pins.IndirectKeys() children := enumerateChildren(t, nd, dserv) + // TODO this is not true if the contents happen to be identical if len(indirpins) != len(children) { t.Log(len(indirpins), len(children)) t.Fatal("Incorrect number of indirectly pinned blocks") From 865c0e8d56e8e90e62cfd91e6bad47fbbd759d27 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 9 Jul 2015 05:57:21 -0700 Subject: [PATCH 1049/3817] renamed {R,}Lock -> {Pin,GC}Lock License: MIT Signed-off-by: Juan Batiz-Benet This commit was moved from ipfs/go-merkledag@bd20a39b60da5bc0ace13a585553cdac006099de --- ipld/merkledag/merkledag_test.go | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 674df6d53..59e94069d 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -300,15 +300,13 @@ func TestCantGet(t *testing.T) { func TestFetchGraph(t *testing.T) { var dservs []DAGService - bsis := bstest.Mocks(t, 2) + bsis := bstest.Mocks(2) for _, bsi := range bsis { dservs = append(dservs, NewDAGService(bsi)) } read := io.LimitReader(u.NewTimeSeededRand(), 1024*32) - spl := &chunk.SizeSplitter{512} - - root, err := imp.BuildDagFromReader(read, dservs[0], spl, nil) + root, err := imp.BuildDagFromReader(dservs[0], chunk.NewSizeSplitter(read, 512), nil) if err != nil { t.Fatal(err) } @@ -319,10 +317,7 @@ func TestFetchGraph(t *testing.T) { } // create an offline dagstore and ensure all blocks were fetched - bs, err := bserv.New(bsis[1].Blockstore, offline.Exchange(bsis[1].Blockstore)) - if err != nil { - t.Fatal(err) - } + bs := bserv.New(bsis[1].Blockstore, offline.Exchange(bsis[1].Blockstore)) offline_ds := NewDAGService(bs) ks := key.NewKeySet() @@ -334,14 +329,11 @@ func TestFetchGraph(t *testing.T) { } func TestEnumerateChildren(t *testing.T) { - bsi := bstest.Mocks(t, 1) + bsi := bstest.Mocks(1) ds := NewDAGService(bsi[0]) - spl := &chunk.SizeSplitter{512} - read := io.LimitReader(u.NewTimeSeededRand(), 1024*1024) - - root, err := imp.BuildDagFromReader(read, ds, spl, nil) + root, err := imp.BuildDagFromReader(ds, chunk.NewSizeSplitter(read, 512), nil) if err != nil { t.Fatal(err) } From 5abf9381edac5834d14ed9592c9db624b278df0c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 7 Jul 2015 08:56:05 -0700 Subject: [PATCH 1050/3817] Add locking interface to blockstore The addition of a locking interface to the blockstore allows us to perform atomic operations on the underlying datastore without having to worry about different operations happening in the background, such as garbage collection. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@96b2995d0ee979b1c9778bb8fbce160b37503674 --- blockstore/blockstore.go | 22 +++++++++++++++++++++- blockstore/write_cache.go | 10 +++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index c4eefaddf..1a56313be 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -4,6 +4,7 @@ package blockstore import ( "errors" + "sync" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" dsns "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" @@ -35,7 +36,14 @@ type Blockstore interface { AllKeysChan(ctx context.Context) (<-chan key.Key, error) } -func NewBlockstore(d ds.ThreadSafeDatastore) Blockstore { +type GCBlockstore interface { + Blockstore + + Lock() func() + RLock() func() +} + +func NewBlockstore(d ds.ThreadSafeDatastore) *blockstore { dd := dsns.Wrap(d, BlockPrefix) return &blockstore{ datastore: dd, @@ -46,6 +54,8 @@ type blockstore struct { datastore ds.Batching // cant be ThreadSafeDatastore cause namespace.Datastore doesnt support it. // we do check it on `NewBlockstore` though. + + lk sync.RWMutex } func (bs *blockstore) Get(k key.Key) (*blocks.Block, error) { @@ -172,3 +182,13 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { return output, nil } + +func (bs *blockstore) Lock() func() { + bs.lk.Lock() + return bs.lk.Unlock +} + +func (bs *blockstore) RLock() func() { + bs.lk.RLock() + return bs.lk.RUnlock +} diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index 5b2f55a2a..54cdfd6eb 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -8,7 +8,7 @@ import ( ) // WriteCached returns a blockstore that caches up to |size| unique writes (bs.Put). -func WriteCached(bs Blockstore, size int) (Blockstore, error) { +func WriteCached(bs Blockstore, size int) (*writecache, error) { c, err := lru.New(size) if err != nil { return nil, err @@ -58,3 +58,11 @@ func (w *writecache) PutMany(bs []*blocks.Block) error { func (w *writecache) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { return w.blockstore.AllKeysChan(ctx) } + +func (w *writecache) Lock() func() { + return w.blockstore.(GCBlockstore).Lock() +} + +func (w *writecache) RLock() func() { + return w.blockstore.(GCBlockstore).RLock() +} From 048b68435df0bd02e1e349150704779aa78b4178 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 10 Jul 2015 11:03:15 -0700 Subject: [PATCH 1051/3817] pin rm fails appropriately for indirect pins License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@484d981e7c8fd3c2c10a15742c51fd25a74c5e68 --- pinning/pinner/pin.go | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 8905293ed..ffdb90a6c 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -126,18 +126,26 @@ func (p *pinner) Pin(ctx context.Context, node *mdag.Node, recurse bool) error { func (p *pinner) Unpin(ctx context.Context, k key.Key, recursive bool) error { p.lock.Lock() defer p.lock.Unlock() - if p.recursePin.HasKey(k) { + reason, pinned, err := p.isPinned(k) + if err != nil { + return err + } + if !pinned { + return fmt.Errorf("%s is not pinned", k) + } + switch reason { + case "recursive": if recursive { p.recursePin.RemoveBlock(k) return nil } else { return fmt.Errorf("%s is pinned recursively", k) } - } else if p.directPin.HasKey(k) { + case "direct": p.directPin.RemoveBlock(k) return nil - } else { - return fmt.Errorf("%s is not pinned", k) + default: + return fmt.Errorf("%s is pinned indirectly under %s", k, reason) } } @@ -151,6 +159,12 @@ func (p *pinner) isInternalPin(key key.Key) bool { func (p *pinner) IsPinned(k key.Key) (string, bool, error) { p.lock.RLock() defer p.lock.RUnlock() + return p.isPinned(k) +} + +// isPinned is the implementation of IsPinned that does not lock. +// intended for use by other pinned methods that already take locks +func (p *pinner) isPinned(k key.Key) (string, bool, error) { if p.recursePin.HasKey(k) { return "recursive", true, nil } From 30aedab933de4446723fb8af2e32102155b34070 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 23 Jun 2015 16:01:32 -0700 Subject: [PATCH 1052/3817] implement mark and sweep GC License: MIT Signed-off-by: Jeromy dont GC blocks used by pinner License: MIT Signed-off-by: Jeromy comment GC algo License: MIT Signed-off-by: Jeromy add lock to blockstore to prevent GC from eating wanted blocks License: MIT Signed-off-by: Jeromy improve FetchGraph License: MIT Signed-off-by: Jeromy separate interfaces for blockstore and GCBlockstore License: MIT Signed-off-by: Jeromy reintroduce indirect pinning, add enumerateChildren dag method License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@622095d132fef7bd95b0e9eabde482f2c1b92fdd --- unixfs/mod/dagmodifier.go | 9 --------- unixfs/mod/dagmodifier_test.go | 26 +++++++------------------- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index df1abe0b6..481005c2f 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -11,7 +11,6 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" - imp "github.com/ipfs/go-ipfs/importer" chunk "github.com/ipfs/go-ipfs/importer/chunk" help "github.com/ipfs/go-ipfs/importer/helpers" trickle "github.com/ipfs/go-ipfs/importer/trickle" @@ -266,10 +265,6 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) for i, bs := range f.GetBlocksizes() { // We found the correct child to write into if cur+bs > offset { - // Unpin block - ckey := key.Key(node.Links[i].Hash) - dm.mp.RemovePinWithMode(ckey, pin.Indirect) - child, err := node.Links[i].GetNode(dm.ctx, dm.dagserv) if err != nil { return "", false, err @@ -279,9 +274,6 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) return "", false, err } - // pin the new node - dm.mp.PinWithMode(k, pin.Indirect) - offset += bs node.Links[i].Hash = mh.Multihash(k) @@ -310,7 +302,6 @@ func (dm *DagModifier) appendData(node *mdag.Node, blks <-chan []byte, errs <-ch dbp := &help.DagBuilderParams{ Dagserv: dm.dagserv, Maxlinks: help.DefaultLinksPerBlock, - NodeCB: imp.BasicPinnerCB(dm.mp), } return trickle.TrickleAppend(dm.ctx, node, dbp.New(blks, errs)) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 98393b377..75638a7bf 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -19,6 +19,7 @@ import ( trickle "github.com/ipfs/go-ipfs/importer/trickle" mdag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" + gc "github.com/ipfs/go-ipfs/pin/gc" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" u "github.com/ipfs/go-ipfs/util" @@ -36,7 +37,7 @@ func getMockDagServ(t testing.TB) (mdag.DAGService, pin.Pinner) { return dserv, pin.NewPinner(tsds, dserv) } -func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.Blockstore, pin.Pinner) { +func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.GCBlockstore, pin.Pinner) { dstore := ds.NewMapDatastore() tsds := sync.MutexWrap(dstore) bstore := blockstore.NewBlockstore(tsds) @@ -47,7 +48,7 @@ func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.Blocksto func getNode(t testing.TB, dserv mdag.DAGService, size int64, pinner pin.Pinner) ([]byte, *mdag.Node) { in := io.LimitReader(u.NewTimeSeededRand(), size) - node, err := imp.BuildTrickleDagFromReader(dserv, sizeSplitterGen(500)(in), imp.BasicPinnerCB(pinner)) + node, err := imp.BuildTrickleDagFromReader(dserv, sizeSplitterGen(500)(in)) if err != nil { t.Fatal(err) } @@ -469,22 +470,17 @@ func TestSparseWrite(t *testing.T) { } } -func basicGC(t *testing.T, bs blockstore.Blockstore, pins pin.Pinner) { +func basicGC(t *testing.T, bs blockstore.GCBlockstore, pins pin.Pinner) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // in case error occurs during operation - keychan, err := bs.AllKeysChan(ctx) + out, err := gc.GC(ctx, bs, pins) if err != nil { t.Fatal(err) } - for k := range keychan { // rely on AllKeysChan to close chan - if !pins.IsPinned(k) { - err := bs.DeleteBlock(k) - if err != nil { - t.Fatal(err) - } - } + for range out { } } + func TestCorrectPinning(t *testing.T) { dserv, bstore, pins := getMockDagServAndBstore(t) b, n := getNode(t, dserv, 50000, pins) @@ -566,14 +562,6 @@ func TestCorrectPinning(t *testing.T) { t.Fatal("Incorrect node recursively pinned") } - indirpins := pins.IndirectKeys() - children := enumerateChildren(t, nd, dserv) - // TODO this is not true if the contents happen to be identical - if len(indirpins) != len(children) { - t.Log(len(indirpins), len(children)) - t.Fatal("Incorrect number of indirectly pinned blocks") - } - } func enumerateChildren(t *testing.T, nd *mdag.Node, ds mdag.DAGService) []key.Key { From ef575227736eef57f1765871ec3a2eb99fab6264 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 23 Jun 2015 16:01:32 -0700 Subject: [PATCH 1053/3817] implement mark and sweep GC License: MIT Signed-off-by: Jeromy dont GC blocks used by pinner License: MIT Signed-off-by: Jeromy comment GC algo License: MIT Signed-off-by: Jeromy add lock to blockstore to prevent GC from eating wanted blocks License: MIT Signed-off-by: Jeromy improve FetchGraph License: MIT Signed-off-by: Jeromy separate interfaces for blockstore and GCBlockstore License: MIT Signed-off-by: Jeromy reintroduce indirect pinning, add enumerateChildren dag method License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@64ec8e46e2c4ed235c3ad24598de2592bffaf120 --- ipld/merkledag/merkledag_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 59e94069d..28ec79343 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -164,7 +164,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { spl := chunk.NewSizeSplitter(read, 512) - root, err := imp.BuildDagFromReader(dagservs[0], spl, nil) + root, err := imp.BuildDagFromReader(dagservs[0], spl) if err != nil { t.Fatal(err) } @@ -306,7 +306,7 @@ func TestFetchGraph(t *testing.T) { } read := io.LimitReader(u.NewTimeSeededRand(), 1024*32) - root, err := imp.BuildDagFromReader(dservs[0], chunk.NewSizeSplitter(read, 512), nil) + root, err := imp.BuildDagFromReader(dservs[0], chunk.NewSizeSplitter(read, 512)) if err != nil { t.Fatal(err) } @@ -333,7 +333,7 @@ func TestEnumerateChildren(t *testing.T) { ds := NewDAGService(bsi[0]) read := io.LimitReader(u.NewTimeSeededRand(), 1024*1024) - root, err := imp.BuildDagFromReader(ds, chunk.NewSizeSplitter(read, 512), nil) + root, err := imp.BuildDagFromReader(ds, chunk.NewSizeSplitter(read, 512)) if err != nil { t.Fatal(err) } From f963d82e93275f8a8a2f5241710e865aa9247779 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 9 Jul 2015 05:57:21 -0700 Subject: [PATCH 1054/3817] renamed {R,}Lock -> {Pin,GC}Lock License: MIT Signed-off-by: Juan Batiz-Benet This commit was moved from ipfs/go-ipfs-blockstore@ef7f864c79e3cf8b1178279449ce6fde628808b4 --- blockstore/blockstore.go | 16 ++++++++++++---- blockstore/write_cache.go | 8 ++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 1a56313be..f2eec8cfe 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -39,8 +39,16 @@ type Blockstore interface { type GCBlockstore interface { Blockstore - Lock() func() - RLock() func() + // GCLock locks the blockstore for garbage collection. No operations + // that expect to finish with a pin should ocurr simultaneously. + // Reading during GC is safe, and requires no lock. + GCLock() func() + + // PinLock locks the blockstore for sequences of puts expected to finish + // with a pin (before GC). Multiple put->pin sequences can write through + // at the same time, but no GC should not happen simulatenously. + // Reading during Pinning is safe, and requires no lock. + PinLock() func() } func NewBlockstore(d ds.ThreadSafeDatastore) *blockstore { @@ -183,12 +191,12 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { return output, nil } -func (bs *blockstore) Lock() func() { +func (bs *blockstore) GCLock() func() { bs.lk.Lock() return bs.lk.Unlock } -func (bs *blockstore) RLock() func() { +func (bs *blockstore) PinLock() func() { bs.lk.RLock() return bs.lk.RUnlock } diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index 54cdfd6eb..52af696e4 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -59,10 +59,10 @@ func (w *writecache) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { return w.blockstore.AllKeysChan(ctx) } -func (w *writecache) Lock() func() { - return w.blockstore.(GCBlockstore).Lock() +func (w *writecache) GCLock() func() { + return w.blockstore.(GCBlockstore).GCLock() } -func (w *writecache) RLock() func() { - return w.blockstore.(GCBlockstore).RLock() +func (w *writecache) PinLock() func() { + return w.blockstore.(GCBlockstore).PinLock() } From 8ea7de1a0217d9c85cea6fc9f8a9125f06b5128f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 10 Jul 2015 11:34:29 -0700 Subject: [PATCH 1055/3817] dont use searchset for indirect pin checking License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@9b7197d224e168afb5b33cfc1932b0bdebf7bc1b --- pinning/pinner/pin.go | 45 ++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index ffdb90a6c..80c11d698 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -176,19 +176,16 @@ func (p *pinner) isPinned(k key.Key) (string, bool, error) { } for _, rk := range p.recursePin.GetKeys() { - ss := &searchSet{target: k} - rnd, err := p.dserv.Get(context.Background(), rk) if err != nil { return "", false, err } - err = mdag.EnumerateChildren(context.Background(), p.dserv, rnd, ss) + has, err := hasChild(p.dserv, rnd, k) if err != nil { return "", false, err } - - if ss.found { + if has { return rk.B58String(), true, nil } } @@ -349,26 +346,26 @@ func (p *pinner) PinWithMode(k key.Key, mode PinMode) { } } -// searchSet implements key.KeySet in -type searchSet struct { - target key.Key - found bool -} +func hasChild(ds mdag.DAGService, root *mdag.Node, child key.Key) (bool, error) { + for _, lnk := range root.Links { + k := key.Key(lnk.Hash) + if k == child { + return true, nil + } -func (ss *searchSet) Add(k key.Key) { - if ss.target == k { - ss.found = true - } -} + nd, err := ds.Get(context.Background(), k) + if err != nil { + return false, err + } -func (ss *searchSet) Has(k key.Key) bool { - // returning true to all Has queries will cause EnumerateChildren to return - // almost immediately - return ss.found -} + has, err := hasChild(ds, nd, child) + if err != nil { + return false, err + } -func (ss *searchSet) Keys() []key.Key { - return nil + if has { + return has, nil + } + } + return false, nil } - -func (ss *searchSet) Remove(key.Key) {} From 423728c30211fb24e09b62199d8fdf9942cecf8f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 10 Jul 2015 10:49:19 -0700 Subject: [PATCH 1056/3817] addressing comments from CR License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@07b2cea0654d4d60fed4b20d50a2aafbee251573 --- unixfs/mod/dagmodifier_test.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 75638a7bf..48be0545e 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -10,7 +10,6 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks/blockstore" - key "github.com/ipfs/go-ipfs/blocks/key" bs "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" imp "github.com/ipfs/go-ipfs/importer" @@ -564,20 +563,6 @@ func TestCorrectPinning(t *testing.T) { } -func enumerateChildren(t *testing.T, nd *mdag.Node, ds mdag.DAGService) []key.Key { - var out []key.Key - for _, lnk := range nd.Links { - out = append(out, key.Key(lnk.Hash)) - child, err := lnk.GetNode(context.Background(), ds) - if err != nil { - t.Fatal(err) - } - children := enumerateChildren(t, child, ds) - out = append(out, children...) - } - return out -} - func BenchmarkDagmodWrite(b *testing.B) { b.StopTimer() dserv, pins := getMockDagServ(b) From 78e705c7f35002f3d2388147c290203799189a7b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 9 Sep 2015 15:02:46 -0700 Subject: [PATCH 1057/3817] Refactor ipnsfs into a more generic and well tested mfs License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@5a486a1714ab34f9a1747af8f145c23fa65c38b4 --- mfs/dir.go | 313 ++++++++++++++++++++++++++++++ mfs/file.go | 145 ++++++++++++++ mfs/mfs_test.go | 476 ++++++++++++++++++++++++++++++++++++++++++++++ mfs/ops.go | 43 +++++ mfs/repub_test.go | 78 ++++++++ mfs/system.go | 237 +++++++++++++++++++++++ 6 files changed, 1292 insertions(+) create mode 100644 mfs/dir.go create mode 100644 mfs/file.go create mode 100644 mfs/mfs_test.go create mode 100644 mfs/ops.go create mode 100644 mfs/repub_test.go create mode 100644 mfs/system.go diff --git a/mfs/dir.go b/mfs/dir.go new file mode 100644 index 000000000..c33032baf --- /dev/null +++ b/mfs/dir.go @@ -0,0 +1,313 @@ +package mfs + +import ( + "errors" + "fmt" + "os" + "sync" + + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + + dag "github.com/ipfs/go-ipfs/merkledag" + ft "github.com/ipfs/go-ipfs/unixfs" + ufspb "github.com/ipfs/go-ipfs/unixfs/pb" +) + +var ErrNotYetImplemented = errors.New("not yet implemented") +var ErrInvalidChild = errors.New("invalid child node") +var ErrDirExists = errors.New("directory already has entry by that name") + +type Directory struct { + dserv dag.DAGService + parent childCloser + + childDirs map[string]*Directory + files map[string]*File + + lock sync.Mutex + node *dag.Node + ctx context.Context + + name string +} + +func NewDirectory(ctx context.Context, name string, node *dag.Node, parent childCloser, dserv dag.DAGService) *Directory { + return &Directory{ + dserv: dserv, + ctx: ctx, + name: name, + node: node, + parent: parent, + childDirs: make(map[string]*Directory), + files: make(map[string]*File), + } +} + +// closeChild updates the child by the given name to the dag node 'nd' +// and changes its own dag node, then propogates the changes upward +func (d *Directory) closeChild(name string, nd *dag.Node) error { + _, err := d.dserv.Add(nd) + if err != nil { + return err + } + + d.lock.Lock() + defer d.lock.Unlock() + err = d.node.RemoveNodeLink(name) + if err != nil && err != dag.ErrNotFound { + return err + } + + err = d.node.AddNodeLinkClean(name, nd) + if err != nil { + return err + } + + return d.parent.closeChild(d.name, d.node) +} + +func (d *Directory) Type() NodeType { + return TDir +} + +// childFile returns a file under this directory by the given name if it exists +func (d *Directory) childFile(name string) (*File, error) { + fi, ok := d.files[name] + if ok { + return fi, nil + } + + nd, err := d.childFromDag(name) + if err != nil { + return nil, err + } + i, err := ft.FromBytes(nd.Data) + if err != nil { + return nil, err + } + + switch i.GetType() { + case ufspb.Data_Directory: + return nil, ErrIsDirectory + case ufspb.Data_File: + nfi, err := NewFile(name, nd, d, d.dserv) + if err != nil { + return nil, err + } + d.files[name] = nfi + return nfi, nil + case ufspb.Data_Metadata: + return nil, ErrNotYetImplemented + default: + return nil, ErrInvalidChild + } +} + +// childDir returns a directory under this directory by the given name if it +// exists. +func (d *Directory) childDir(name string) (*Directory, error) { + dir, ok := d.childDirs[name] + if ok { + return dir, nil + } + + nd, err := d.childFromDag(name) + if err != nil { + return nil, err + } + + i, err := ft.FromBytes(nd.Data) + if err != nil { + return nil, err + } + + switch i.GetType() { + case ufspb.Data_Directory: + ndir := NewDirectory(d.ctx, name, nd, d, d.dserv) + d.childDirs[name] = ndir + return ndir, nil + case ufspb.Data_File: + return nil, fmt.Errorf("%s is not a directory", name) + case ufspb.Data_Metadata: + return nil, ErrNotYetImplemented + default: + return nil, ErrInvalidChild + } +} + +// childFromDag searches through this directories dag node for a child link +// with the given name +func (d *Directory) childFromDag(name string) (*dag.Node, error) { + for _, lnk := range d.node.Links { + if lnk.Name == name { + return lnk.GetNode(d.ctx, d.dserv) + } + } + + return nil, os.ErrNotExist +} + +// Child returns the child of this directory by the given name +func (d *Directory) Child(name string) (FSNode, error) { + d.lock.Lock() + defer d.lock.Unlock() + return d.childUnsync(name) +} + +// childUnsync returns the child under this directory by the given name +// without locking, useful for operations which already hold a lock +func (d *Directory) childUnsync(name string) (FSNode, error) { + + dir, err := d.childDir(name) + if err == nil { + return dir, nil + } + fi, err := d.childFile(name) + if err == nil { + return fi, nil + } + + return nil, os.ErrNotExist +} + +type NodeListing struct { + Name string + Type int + Size int64 + Hash string +} + +func (d *Directory) List() ([]NodeListing, error) { + d.lock.Lock() + defer d.lock.Unlock() + + var out []NodeListing + for _, l := range d.node.Links { + child := NodeListing{} + child.Name = l.Name + + c, err := d.childUnsync(l.Name) + if err != nil { + return nil, err + } + + child.Type = int(c.Type()) + if c, ok := c.(*File); ok { + size, err := c.Size() + if err != nil { + return nil, err + } + child.Size = size + } + nd, err := c.GetNode() + if err != nil { + return nil, err + } + + k, err := nd.Key() + if err != nil { + return nil, err + } + + child.Hash = k.B58String() + + out = append(out, child) + } + + return out, nil +} + +func (d *Directory) Mkdir(name string) (*Directory, error) { + d.lock.Lock() + defer d.lock.Unlock() + + _, err := d.childDir(name) + if err == nil { + return nil, os.ErrExist + } + _, err = d.childFile(name) + if err == nil { + return nil, os.ErrExist + } + + ndir := &dag.Node{Data: ft.FolderPBData()} + + _, err = d.dserv.Add(ndir) + if err != nil { + return nil, err + } + + err = d.node.AddNodeLinkClean(name, ndir) + if err != nil { + return nil, err + } + + err = d.parent.closeChild(d.name, d.node) + if err != nil { + return nil, err + } + + return d.childDir(name) +} + +func (d *Directory) Unlink(name string) error { + d.lock.Lock() + defer d.lock.Unlock() + + delete(d.childDirs, name) + delete(d.files, name) + + err := d.node.RemoveNodeLink(name) + if err != nil { + return err + } + + return d.parent.closeChild(d.name, d.node) +} + +// AddChild adds the node 'nd' under this directory giving it the name 'name' +func (d *Directory) AddChild(name string, nd *dag.Node) error { + d.Lock() + defer d.Unlock() + + pbn, err := ft.FromBytes(nd.Data) + if err != nil { + return err + } + + _, err = d.childUnsync(name) + if err == nil { + return ErrDirExists + } + + err = d.node.AddNodeLinkClean(name, nd) + if err != nil { + return err + } + + switch pbn.GetType() { + case ft.TDirectory: + d.childDirs[name] = NewDirectory(d.ctx, name, nd, d, d.dserv) + case ft.TFile, ft.TMetadata, ft.TRaw: + nfi, err := NewFile(name, nd, d, d.dserv) + if err != nil { + return err + } + d.files[name] = nfi + default: + return ErrInvalidChild + } + return d.parent.closeChild(d.name, d.node) +} + +func (d *Directory) GetNode() (*dag.Node, error) { + return d.node, nil +} + +func (d *Directory) Lock() { + d.lock.Lock() +} + +func (d *Directory) Unlock() { + d.lock.Unlock() +} diff --git a/mfs/file.go b/mfs/file.go new file mode 100644 index 000000000..fea1112dc --- /dev/null +++ b/mfs/file.go @@ -0,0 +1,145 @@ +package mfs + +import ( + "sync" + + chunk "github.com/ipfs/go-ipfs/importer/chunk" + dag "github.com/ipfs/go-ipfs/merkledag" + mod "github.com/ipfs/go-ipfs/unixfs/mod" + + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" +) + +type File struct { + parent childCloser + + name string + hasChanges bool + + mod *mod.DagModifier + lock sync.Mutex +} + +// NewFile returns a NewFile object with the given parameters +func NewFile(name string, node *dag.Node, parent childCloser, dserv dag.DAGService) (*File, error) { + dmod, err := mod.NewDagModifier(context.Background(), node, dserv, chunk.DefaultSplitter) + if err != nil { + return nil, err + } + + return &File{ + parent: parent, + name: name, + mod: dmod, + }, nil +} + +// Write writes the given data to the file at its current offset +func (fi *File) Write(b []byte) (int, error) { + fi.Lock() + defer fi.Unlock() + fi.hasChanges = true + return fi.mod.Write(b) +} + +// Read reads into the given buffer from the current offset +func (fi *File) Read(b []byte) (int, error) { + fi.Lock() + defer fi.Unlock() + return fi.mod.Read(b) +} + +// Read reads into the given buffer from the current offset +func (fi *File) CtxReadFull(ctx context.Context, b []byte) (int, error) { + fi.Lock() + defer fi.Unlock() + return fi.mod.CtxReadFull(ctx, b) +} + +// Close flushes, then propogates the modified dag node up the directory structure +// and signals a republish to occur +func (fi *File) Close() error { + fi.Lock() + defer fi.Unlock() + if fi.hasChanges { + err := fi.mod.Sync() + if err != nil { + return err + } + + nd, err := fi.mod.GetNode() + if err != nil { + return err + } + + fi.Unlock() + err = fi.parent.closeChild(fi.name, nd) + fi.Lock() + if err != nil { + return err + } + + fi.hasChanges = false + } + + return nil +} + +// Sync flushes the changes in the file to disk +func (fi *File) Sync() error { + fi.Lock() + defer fi.Unlock() + return fi.mod.Sync() +} + +// Seek implements io.Seeker +func (fi *File) Seek(offset int64, whence int) (int64, error) { + fi.Lock() + defer fi.Unlock() + return fi.mod.Seek(offset, whence) +} + +// Write At writes the given bytes at the offset 'at' +func (fi *File) WriteAt(b []byte, at int64) (int, error) { + fi.Lock() + defer fi.Unlock() + fi.hasChanges = true + return fi.mod.WriteAt(b, at) +} + +// Size returns the size of this file +func (fi *File) Size() (int64, error) { + fi.Lock() + defer fi.Unlock() + return fi.mod.Size() +} + +// GetNode returns the dag node associated with this file +func (fi *File) GetNode() (*dag.Node, error) { + fi.Lock() + defer fi.Unlock() + return fi.mod.GetNode() +} + +// Truncate truncates the file to size +func (fi *File) Truncate(size int64) error { + fi.Lock() + defer fi.Unlock() + fi.hasChanges = true + return fi.mod.Truncate(size) +} + +// Type returns the type FSNode this is +func (fi *File) Type() NodeType { + return TFile +} + +// Lock the file +func (fi *File) Lock() { + fi.lock.Lock() +} + +// Unlock the file +func (fi *File) Unlock() { + fi.lock.Unlock() +} diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go new file mode 100644 index 000000000..609d81a29 --- /dev/null +++ b/mfs/mfs_test.go @@ -0,0 +1,476 @@ +package mfs + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "sort" + "strings" + "testing" + + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + + bstore "github.com/ipfs/go-ipfs/blocks/blockstore" + key "github.com/ipfs/go-ipfs/blocks/key" + bserv "github.com/ipfs/go-ipfs/blockservice" + offline "github.com/ipfs/go-ipfs/exchange/offline" + importer "github.com/ipfs/go-ipfs/importer" + chunk "github.com/ipfs/go-ipfs/importer/chunk" + dag "github.com/ipfs/go-ipfs/merkledag" + ft "github.com/ipfs/go-ipfs/unixfs" + uio "github.com/ipfs/go-ipfs/unixfs/io" + u "github.com/ipfs/go-ipfs/util" +) + +func getDagserv(t *testing.T) dag.DAGService { + db := dssync.MutexWrap(ds.NewMapDatastore()) + bs := bstore.NewBlockstore(db) + blockserv := bserv.New(bs, offline.Exchange(bs)) + return dag.NewDAGService(blockserv) +} + +func getRandFile(t *testing.T, ds dag.DAGService, size int64) *dag.Node { + r := io.LimitReader(u.NewTimeSeededRand(), size) + nd, err := importer.BuildDagFromReader(ds, chunk.DefaultSplitter(r)) + if err != nil { + t.Fatal(err) + } + return nd +} + +func mkdirP(t *testing.T, root *Directory, path string) *Directory { + dirs := strings.Split(path, "/") + cur := root + for _, d := range dirs { + n, err := cur.Mkdir(d) + if err != nil && err != os.ErrExist { + t.Fatal(err) + } + if err == os.ErrExist { + fsn, err := cur.Child(d) + if err != nil { + t.Fatal(err) + } + switch fsn := fsn.(type) { + case *Directory: + n = fsn + case *File: + t.Fatal("tried to make a directory where a file already exists") + } + } + + cur = n + } + return cur +} + +func assertDirAtPath(root *Directory, path string, children []string) error { + fsn, err := DirLookup(root, path) + if err != nil { + return err + } + + dir, ok := fsn.(*Directory) + if !ok { + return fmt.Errorf("%s was not a directory", path) + } + + listing, err := dir.List() + if err != nil { + return err + } + + var names []string + for _, d := range listing { + names = append(names, d.Name) + } + + sort.Strings(children) + sort.Strings(names) + if !compStrArrs(children, names) { + return errors.New("directories children did not match!") + } + + return nil +} + +func compStrArrs(a, b []string) bool { + if len(a) != len(b) { + return false + } + + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return false + } + } + + return true +} + +func assertFileAtPath(ds dag.DAGService, root *Directory, exp *dag.Node, path string) error { + parts := strings.Split(path, "/") + cur := root + for i, d := range parts[:len(parts)-1] { + next, err := cur.Child(d) + if err != nil { + return fmt.Errorf("looking for %s failed: %s", path, err) + } + + nextDir, ok := next.(*Directory) + if !ok { + return fmt.Errorf("%s points to a non-directory", parts[:i+1]) + } + + cur = nextDir + } + + last := parts[len(parts)-1] + finaln, err := cur.Child(last) + if err != nil { + return err + } + + file, ok := finaln.(*File) + if !ok { + return fmt.Errorf("%s was not a file!", path) + } + + out, err := ioutil.ReadAll(file) + if err != nil { + return err + } + + expbytes, err := catNode(ds, exp) + if err != nil { + return err + } + + if !bytes.Equal(out, expbytes) { + return fmt.Errorf("Incorrect data at path!") + } + return nil +} + +func catNode(ds dag.DAGService, nd *dag.Node) ([]byte, error) { + r, err := uio.NewDagReader(context.TODO(), nd, ds) + if err != nil { + return nil, err + } + defer r.Close() + + return ioutil.ReadAll(r) +} + +func setupRoot(ctx context.Context, t *testing.T) (dag.DAGService, *Root) { + ds := getDagserv(t) + + root := &dag.Node{Data: ft.FolderPBData()} + rt, err := NewRoot(ctx, ds, root, func(ctx context.Context, k key.Key) error { + fmt.Println("PUBLISHED: ", k) + return nil + }) + + if err != nil { + t.Fatal(err) + } + + return ds, rt +} + +func TestBasic(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ds, rt := setupRoot(ctx, t) + + rootdir := rt.GetValue().(*Directory) + + // test making a basic dir + _, err := rootdir.Mkdir("a") + if err != nil { + t.Fatal(err) + } + + path := "a/b/c/d/e/f/g" + d := mkdirP(t, rootdir, path) + + fi := getRandFile(t, ds, 1000) + + // test inserting that file + err = d.AddChild("afile", fi) + if err != nil { + t.Fatal(err) + } + + err = assertFileAtPath(ds, rootdir, fi, "a/b/c/d/e/f/g/afile") + if err != nil { + t.Fatal(err) + } +} + +func TestMkdir(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + _, rt := setupRoot(ctx, t) + + rootdir := rt.GetValue().(*Directory) + + dirsToMake := []string{"a", "B", "foo", "bar", "cats", "fish"} + sort.Strings(dirsToMake) // sort for easy comparing later + + for _, d := range dirsToMake { + _, err := rootdir.Mkdir(d) + if err != nil { + t.Fatal(err) + } + } + + err := assertDirAtPath(rootdir, "/", dirsToMake) + if err != nil { + t.Fatal(err) + } + + for _, d := range dirsToMake { + mkdirP(t, rootdir, "a/"+d) + } + + err = assertDirAtPath(rootdir, "/a", dirsToMake) + if err != nil { + t.Fatal(err) + } + + // mkdir over existing dir should fail + _, err = rootdir.Mkdir("a") + if err == nil { + t.Fatal("should have failed!") + } +} + +func TestDirectoryLoadFromDag(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ds, rt := setupRoot(ctx, t) + + rootdir := rt.GetValue().(*Directory) + + nd := getRandFile(t, ds, 1000) + _, err := ds.Add(nd) + if err != nil { + t.Fatal(err) + } + + fihash, err := nd.Multihash() + if err != nil { + t.Fatal(err) + } + + dir := &dag.Node{Data: ft.FolderPBData()} + _, err = ds.Add(dir) + if err != nil { + t.Fatal(err) + } + + dirhash, err := dir.Multihash() + if err != nil { + t.Fatal(err) + } + + top := &dag.Node{ + Data: ft.FolderPBData(), + Links: []*dag.Link{ + &dag.Link{ + Name: "a", + Hash: fihash, + }, + &dag.Link{ + Name: "b", + Hash: dirhash, + }, + }, + } + + err = rootdir.AddChild("foo", top) + if err != nil { + t.Fatal(err) + } + + // get this dir + topi, err := rootdir.Child("foo") + if err != nil { + t.Fatal(err) + } + + topd := topi.(*Directory) + + // mkdir over existing but unloaded child file should fail + _, err = topd.Mkdir("a") + if err == nil { + t.Fatal("expected to fail!") + } + + // mkdir over existing but unloaded child dir should fail + _, err = topd.Mkdir("b") + if err == nil { + t.Fatal("expected to fail!") + } + + // adding a child over an existing path fails + err = topd.AddChild("b", nd) + if err == nil { + t.Fatal("expected to fail!") + } + + err = assertFileAtPath(ds, rootdir, nd, "foo/a") + if err != nil { + t.Fatal(err) + } + + err = assertDirAtPath(rootdir, "foo/b", nil) + if err != nil { + t.Fatal(err) + } + + err = rootdir.Unlink("foo") + if err != nil { + t.Fatal(err) + } + + err = assertDirAtPath(rootdir, "", nil) + if err != nil { + t.Fatal(err) + } +} + +func TestMfsFile(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ds, rt := setupRoot(ctx, t) + + rootdir := rt.GetValue().(*Directory) + + fisize := 1000 + nd := getRandFile(t, ds, 1000) + + err := rootdir.AddChild("file", nd) + if err != nil { + t.Fatal(err) + } + + fsn, err := rootdir.Child("file") + if err != nil { + t.Fatal(err) + } + + fi := fsn.(*File) + + if fi.Type() != TFile { + t.Fatal("some is seriously wrong here") + } + + // assert size is as expected + size, err := fi.Size() + if size != int64(fisize) { + t.Fatal("size isnt correct") + } + + // write to beginning of file + b := []byte("THIS IS A TEST") + n, err := fi.Write(b) + if err != nil { + t.Fatal(err) + } + + if n != len(b) { + t.Fatal("didnt write correct number of bytes") + } + + // sync file + err = fi.Sync() + if err != nil { + t.Fatal(err) + } + + // make sure size hasnt changed + size, err = fi.Size() + if size != int64(fisize) { + t.Fatal("size isnt correct") + } + + // seek back to beginning + ns, err := fi.Seek(0, os.SEEK_SET) + if err != nil { + t.Fatal(err) + } + + if ns != 0 { + t.Fatal("didnt seek to beginning") + } + + // read back bytes we wrote + buf := make([]byte, len(b)) + n, err = fi.Read(buf) + if err != nil { + t.Fatal(err) + } + + if n != len(buf) { + t.Fatal("didnt read enough") + } + + if !bytes.Equal(buf, b) { + t.Fatal("data read was different than data written") + } + + // truncate file to ten bytes + err = fi.Truncate(10) + if err != nil { + t.Fatal(err) + } + + size, err = fi.Size() + if err != nil { + t.Fatal(err) + } + + if size != 10 { + t.Fatal("size was incorrect: ", size) + } + + // 'writeAt' to extend it + data := []byte("this is a test foo foo foo") + nwa, err := fi.WriteAt(data, 5) + if err != nil { + t.Fatal(err) + } + + if nwa != len(data) { + t.Fatal(err) + } + + // assert size once more + size, err = fi.Size() + if err != nil { + t.Fatal(err) + } + + if size != int64(5+len(data)) { + t.Fatal("size was incorrect") + } + + // make sure we can get node. TODO: verify it later + _, err = fi.GetNode() + if err != nil { + t.Fatal(err) + } + + // close it out! + err = fi.Close() + if err != nil { + t.Fatal(err) + } +} diff --git a/mfs/ops.go b/mfs/ops.go new file mode 100644 index 000000000..75f187f52 --- /dev/null +++ b/mfs/ops.go @@ -0,0 +1,43 @@ +package mfs + +import ( + "errors" + "fmt" + "strings" +) + +func rootLookup(r *Root, path string) (FSNode, error) { + dir, ok := r.GetValue().(*Directory) + if !ok { + return nil, errors.New("root was not a directory") + } + + return DirLookup(dir, path) +} + +// DirLookup will look up a file or directory at the given path +// under the directory 'd' +func DirLookup(d *Directory, path string) (FSNode, error) { + path = strings.Trim(path, "/") + parts := strings.Split(path, "/") + if len(parts) == 1 && parts[0] == "" { + return d, nil + } + + var cur FSNode + cur = d + for i, p := range parts { + chdir, ok := cur.(*Directory) + if !ok { + return nil, fmt.Errorf("cannot access %s: Not a directory", strings.Join(parts[:i+1], "/")) + } + + child, err := chdir.Child(p) + if err != nil { + return nil, err + } + + cur = child + } + return cur, nil +} diff --git a/mfs/repub_test.go b/mfs/repub_test.go new file mode 100644 index 000000000..36db90e80 --- /dev/null +++ b/mfs/repub_test.go @@ -0,0 +1,78 @@ +package mfs + +import ( + "testing" + "time" + + key "github.com/ipfs/go-ipfs/blocks/key" + ci "github.com/ipfs/go-ipfs/util/testutil/ci" + + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" +) + +func TestRepublisher(t *testing.T) { + if ci.IsRunning() { + t.Skip("dont run timing tests in CI") + } + + ctx := context.TODO() + + pub := make(chan struct{}) + + pf := func(ctx context.Context, k key.Key) error { + pub <- struct{}{} + return nil + } + + tshort := time.Millisecond * 50 + tlong := time.Second / 2 + + rp := NewRepublisher(ctx, pf, tshort, tlong) + go rp.Run() + + rp.Update("test") + + // should hit short timeout + select { + case <-time.After(tshort * 2): + t.Fatal("publish didnt happen in time") + case <-pub: + } + + cctx, cancel := context.WithCancel(context.Background()) + + go func() { + for { + rp.Update("a") + time.Sleep(time.Millisecond * 10) + select { + case <-cctx.Done(): + return + default: + } + } + }() + + select { + case <-pub: + t.Fatal("shouldnt have received publish yet!") + case <-time.After((tlong * 9) / 10): + } + select { + case <-pub: + case <-time.After(tlong / 2): + t.Fatal("waited too long for pub!") + } + + cancel() + + go func() { + err := rp.Close() + if err != nil { + t.Fatal(err) + } + }() + + // final pub from closing + <-pub +} diff --git a/mfs/system.go b/mfs/system.go new file mode 100644 index 000000000..d2819479f --- /dev/null +++ b/mfs/system.go @@ -0,0 +1,237 @@ +// package mfs implements an in memory model of a mutable ipfs filesystem. +// +// It consists of four main structs: +// 1) The Filesystem +// The filesystem serves as a container and entry point for various mfs filesystems +// 2) Root +// Root represents an individual filesystem mounted within the mfs system as a whole +// 3) Directories +// 4) Files +package mfs + +import ( + "errors" + "sync" + "time" + + key "github.com/ipfs/go-ipfs/blocks/key" + dag "github.com/ipfs/go-ipfs/merkledag" + ft "github.com/ipfs/go-ipfs/unixfs" + + context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" +) + +var ErrNotExist = errors.New("no such rootfs") + +var log = logging.Logger("mfs") + +var ErrIsDirectory = errors.New("error: is a directory") + +type childCloser interface { + closeChild(string, *dag.Node) error +} + +type NodeType int + +const ( + TFile NodeType = iota + TDir +) + +// FSNode represents any node (directory, root, or file) in the ipns filesystem +type FSNode interface { + GetNode() (*dag.Node, error) + Type() NodeType + Lock() + Unlock() +} + +// Root represents the root of a filesystem tree pointed to by a given keypair +type Root struct { + // node is the merkledag node pointed to by this keypair + node *dag.Node + + // val represents the node pointed to by this key. It can either be a File or a Directory + val FSNode + + repub *Republisher + + dserv dag.DAGService + + Type string +} + +type PubFunc func(context.Context, key.Key) error + +// newRoot creates a new Root for the given key, and starts up a republisher routine +// for it +func NewRoot(parent context.Context, ds dag.DAGService, node *dag.Node, pf PubFunc) (*Root, error) { + ndk, err := node.Key() + if err != nil { + return nil, err + } + + root := &Root{ + node: node, + repub: NewRepublisher(parent, pf, time.Millisecond*300, time.Second*3), + dserv: ds, + } + + root.repub.setVal(ndk) + go root.repub.Run() + + pbn, err := ft.FromBytes(node.Data) + if err != nil { + log.Error("IPNS pointer was not unixfs node") + return nil, err + } + + switch pbn.GetType() { + case ft.TDirectory: + root.val = NewDirectory(parent, ndk.String(), node, root, ds) + case ft.TFile, ft.TMetadata, ft.TRaw: + fi, err := NewFile(ndk.String(), node, root, ds) + if err != nil { + return nil, err + } + root.val = fi + default: + panic("unrecognized! (NYI)") + } + return root, nil +} + +func (kr *Root) GetValue() FSNode { + return kr.val +} + +// closeChild implements the childCloser interface, and signals to the publisher that +// there are changes ready to be published +func (kr *Root) closeChild(name string, nd *dag.Node) error { + k, err := kr.dserv.Add(nd) + if err != nil { + return err + } + + kr.repub.Update(k) + return nil +} + +func (kr *Root) Close() error { + return kr.repub.Close() +} + +// Republisher manages when to publish the ipns entry associated with a given key +type Republisher struct { + TimeoutLong time.Duration + TimeoutShort time.Duration + Publish chan struct{} + pubfunc PubFunc + pubnowch chan struct{} + + ctx context.Context + cancel func() + + lk sync.Mutex + val key.Key + lastpub key.Key +} + +func (rp *Republisher) getVal() key.Key { + rp.lk.Lock() + defer rp.lk.Unlock() + return rp.val +} + +// NewRepublisher creates a new Republisher object to republish the given keyroot +// using the given short and long time intervals +func NewRepublisher(ctx context.Context, pf PubFunc, tshort, tlong time.Duration) *Republisher { + ctx, cancel := context.WithCancel(ctx) + return &Republisher{ + TimeoutShort: tshort, + TimeoutLong: tlong, + Publish: make(chan struct{}, 1), + pubfunc: pf, + pubnowch: make(chan struct{}), + ctx: ctx, + cancel: cancel, + } +} + +func (p *Republisher) setVal(k key.Key) { + p.lk.Lock() + defer p.lk.Unlock() + p.val = k +} + +func (p *Republisher) pubNow() { + select { + case p.pubnowch <- struct{}{}: + default: + } +} + +func (p *Republisher) Close() error { + err := p.publish(p.ctx) + p.cancel() + return err +} + +// Touch signals that an update has occurred since the last publish. +// Multiple consecutive touches may extend the time period before +// the next Publish occurs in order to more efficiently batch updates +func (np *Republisher) Update(k key.Key) { + np.setVal(k) + select { + case np.Publish <- struct{}{}: + default: + } +} + +// Run is the main republisher loop +func (np *Republisher) Run() { + for { + select { + case <-np.Publish: + quick := time.After(np.TimeoutShort) + longer := time.After(np.TimeoutLong) + + wait: + select { + case <-np.ctx.Done(): + return + case <-np.Publish: + quick = time.After(np.TimeoutShort) + goto wait + case <-quick: + case <-longer: + case <-np.pubnowch: + } + + err := np.publish(np.ctx) + if err != nil { + log.Error("republishRoot error: %s", err) + } + + case <-np.ctx.Done(): + return + } + } +} + +func (np *Republisher) publish(ctx context.Context) error { + np.lk.Lock() + topub := np.val + np.lk.Unlock() + + log.Info("Publishing Changes!") + err := np.pubfunc(ctx, topub) + if err != nil { + return err + } + np.lk.Lock() + np.lastpub = topub + np.lk.Unlock() + return nil +} From 0c3a47d60dcc1c201123970c9e5c22b9903edf78 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 21 Sep 2015 18:07:36 -0700 Subject: [PATCH 1058/3817] fixup comments License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@90014fc4728e3efaa35fc9bd23ecdd84be7f98ba --- mfs/system.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/mfs/system.go b/mfs/system.go index d2819479f..22ef63cd4 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -39,7 +39,7 @@ const ( TDir ) -// FSNode represents any node (directory, root, or file) in the ipns filesystem +// FSNode represents any node (directory, root, or file) in the mfs filesystem type FSNode interface { GetNode() (*dag.Node, error) Type() NodeType @@ -47,12 +47,12 @@ type FSNode interface { Unlock() } -// Root represents the root of a filesystem tree pointed to by a given keypair +// Root represents the root of a filesystem tree type Root struct { - // node is the merkledag node pointed to by this keypair + // node is the merkledag root node *dag.Node - // val represents the node pointed to by this key. It can either be a File or a Directory + // val represents the node. It can either be a File or a Directory val FSNode repub *Republisher @@ -64,8 +64,7 @@ type Root struct { type PubFunc func(context.Context, key.Key) error -// newRoot creates a new Root for the given key, and starts up a republisher routine -// for it +// newRoot creates a new Root and starts up a republisher routine for it func NewRoot(parent context.Context, ds dag.DAGService, node *dag.Node, pf PubFunc) (*Root, error) { ndk, err := node.Key() if err != nil { @@ -122,7 +121,7 @@ func (kr *Root) Close() error { return kr.repub.Close() } -// Republisher manages when to publish the ipns entry associated with a given key +// Republisher manages when to publish a given entry type Republisher struct { TimeoutLong time.Duration TimeoutShort time.Duration @@ -144,7 +143,7 @@ func (rp *Republisher) getVal() key.Key { return rp.val } -// NewRepublisher creates a new Republisher object to republish the given keyroot +// NewRepublisher creates a new Republisher object to republish the given root // using the given short and long time intervals func NewRepublisher(ctx context.Context, pf PubFunc, tshort, tlong time.Duration) *Republisher { ctx, cancel := context.WithCancel(ctx) From d4e83e4a5d38267f537291dd056db7d3463bc847 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 29 Sep 2015 21:31:18 -0700 Subject: [PATCH 1059/3817] implement ipfs files command License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@191c539b888b3995085c655cec17cc2bd2c17bcc --- mfs/dir.go | 5 +++ mfs/ops.go | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 1 deletion(-) diff --git a/mfs/dir.go b/mfs/dir.go index c33032baf..264dea4a0 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -280,6 +280,11 @@ func (d *Directory) AddChild(name string, nd *dag.Node) error { return ErrDirExists } + _, err = d.dserv.Add(nd) + if err != nil { + return err + } + err = d.node.AddNodeLinkClean(name, nd) if err != nil { return err diff --git a/mfs/ops.go b/mfs/ops.go index 75f187f52..397aea65a 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -3,10 +3,117 @@ package mfs import ( "errors" "fmt" + "os" + gopath "path" "strings" + + dag "github.com/ipfs/go-ipfs/merkledag" ) -func rootLookup(r *Root, path string) (FSNode, error) { +// Mv moves the file or directory at 'src' to 'dst' +func Mv(r *Root, src, dst string) error { + srcDir, srcFname := gopath.Split(src) + + srcObj, err := Lookup(r, src) + if err != nil { + return err + } + + var dstDirStr string + var filename string + if dst[len(dst)-1] == '/' { + dstDirStr = dst + filename = srcFname + } else { + dstDirStr, filename = gopath.Split(dst) + } + + dstDiri, err := Lookup(r, dstDirStr) + if err != nil { + return err + } + + dstDir := dstDiri.(*Directory) + nd, err := srcObj.GetNode() + if err != nil { + return err + } + + err = dstDir.AddChild(filename, nd) + if err != nil { + return err + } + + srcDirObji, err := Lookup(r, srcDir) + if err != nil { + return err + } + + srcDirObj := srcDirObji.(*Directory) + err = srcDirObj.Unlink(srcFname) + if err != nil { + return err + } + + return nil +} + +// PutNode inserts 'nd' at 'path' in the given mfs +func PutNode(r *Root, path string, nd *dag.Node) error { + dirp, filename := gopath.Split(path) + + parent, err := Lookup(r, dirp) + if err != nil { + return fmt.Errorf("lookup '%s' failed: %s", dirp, err) + } + + pdir, ok := parent.(*Directory) + if !ok { + return fmt.Errorf("%s did not point to directory", dirp) + } + + return pdir.AddChild(filename, nd) +} + +// Mkdir creates a directory at 'path' under the directory 'd', creating +// intermediary directories as needed if 'parents' is set to true +func Mkdir(r *Root, path string, parents bool) error { + parts := strings.Split(path, "/") + if parts[0] == "" { + parts = parts[1:] + } + + cur := r.GetValue().(*Directory) + for i, d := range parts[:len(parts)-1] { + fsn, err := cur.Child(d) + if err != nil { + if err == os.ErrNotExist && parents { + mkd, err := cur.Mkdir(d) + if err != nil { + return err + } + fsn = mkd + } + } + + next, ok := fsn.(*Directory) + if !ok { + return fmt.Errorf("%s was not a directory", strings.Join(parts[:i], "/")) + } + cur = next + } + + _, err := cur.Mkdir(parts[len(parts)-1]) + if err != nil { + if !parents || err != os.ErrExist { + return err + } + } + + return nil +} + +func Lookup(r *Root, path string) (FSNode, error) { dir, ok := r.GetValue().(*Directory) if !ok { return nil, errors.New("root was not a directory") From bfc91be56173c80f924b33284c22823d3aa3ce79 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 30 Sep 2015 17:12:51 -0700 Subject: [PATCH 1060/3817] address comments from CR License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@0f90282fe6f737030f9ed82130eeb682acf43a7e --- mfs/ops.go | 82 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/mfs/ops.go b/mfs/ops.go index 397aea65a..33514fc67 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -14,11 +14,6 @@ import ( func Mv(r *Root, src, dst string) error { srcDir, srcFname := gopath.Split(src) - srcObj, err := Lookup(r, src) - if err != nil { - return err - } - var dstDirStr string var filename string if dst[len(dst)-1] == '/' { @@ -28,28 +23,46 @@ func Mv(r *Root, src, dst string) error { dstDirStr, filename = gopath.Split(dst) } - dstDiri, err := Lookup(r, dstDirStr) + // get parent directories of both src and dest first + dstDir, err := lookupDir(r, dstDirStr) if err != nil { return err } - dstDir := dstDiri.(*Directory) - nd, err := srcObj.GetNode() + srcDirObj, err := lookupDir(r, srcDir) if err != nil { return err } - err = dstDir.AddChild(filename, nd) + srcObj, err := srcDirObj.Child(srcFname) if err != nil { return err } - srcDirObji, err := Lookup(r, srcDir) + nd, err := srcObj.GetNode() + if err != nil { + return err + } + + fsn, err := dstDir.Child(filename) + if err == nil { + switch n := fsn.(type) { + case *File: + _ = dstDir.Unlink(filename) + case *Directory: + dstDir = n + default: + return fmt.Errorf("unexpected type at path: %s", dst) + } + } else if err != os.ErrNotExist { + return err + } + + err = dstDir.AddChild(filename, nd) if err != nil { return err } - srcDirObj := srcDirObji.(*Directory) err = srcDirObj.Unlink(srcFname) if err != nil { return err @@ -58,18 +71,27 @@ func Mv(r *Root, src, dst string) error { return nil } +func lookupDir(r *Root, path string) (*Directory, error) { + di, err := Lookup(r, path) + if err != nil { + return nil, err + } + + d, ok := di.(*Directory) + if !ok { + return nil, fmt.Errorf("%s is not a directory", path) + } + + return d, nil +} + // PutNode inserts 'nd' at 'path' in the given mfs func PutNode(r *Root, path string, nd *dag.Node) error { dirp, filename := gopath.Split(path) - parent, err := Lookup(r, dirp) + pdir, err := lookupDir(r, dirp) if err != nil { - return fmt.Errorf("lookup '%s' failed: %s", dirp, err) - } - - pdir, ok := parent.(*Directory) - if !ok { - return fmt.Errorf("%s did not point to directory", dirp) + return err } return pdir.AddChild(filename, nd) @@ -83,17 +105,27 @@ func Mkdir(r *Root, path string, parents bool) error { parts = parts[1:] } + // allow 'mkdir /a/b/c/' to create c + if parts[len(parts)-1] == "" { + parts = parts[:len(parts)-1] + } + + if len(parts) == 0 { + // this will only happen on 'mkdir /' + return fmt.Errorf("cannot mkdir '%s'", path) + } + cur := r.GetValue().(*Directory) for i, d := range parts[:len(parts)-1] { fsn, err := cur.Child(d) - if err != nil { - if err == os.ErrNotExist && parents { - mkd, err := cur.Mkdir(d) - if err != nil { - return err - } - fsn = mkd + if err == os.ErrNotExist && parents { + mkd, err := cur.Mkdir(d) + if err != nil { + return err } + fsn = mkd + } else if err != nil { + return err } next, ok := fsn.(*Directory) From 2d8f9a3f5c59ee2d3752e2ffd2f63c925e1ebc31 Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 17 Nov 2015 15:36:48 +0700 Subject: [PATCH 1061/3817] Replace strings.Join(elms, "/") with path.Join(elms) License: MIT Signed-off-by: rht This commit was moved from ipfs/go-mfs@4b035d7f87d659d0deded786b9db1a8d439039aa --- mfs/ops.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/mfs/ops.go b/mfs/ops.go index 33514fc67..9e8ec1674 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -8,6 +8,7 @@ import ( "strings" dag "github.com/ipfs/go-ipfs/merkledag" + path "github.com/ipfs/go-ipfs/path" ) // Mv moves the file or directory at 'src' to 'dst' @@ -99,8 +100,8 @@ func PutNode(r *Root, path string, nd *dag.Node) error { // Mkdir creates a directory at 'path' under the directory 'd', creating // intermediary directories as needed if 'parents' is set to true -func Mkdir(r *Root, path string, parents bool) error { - parts := strings.Split(path, "/") +func Mkdir(r *Root, pth string, parents bool) error { + parts := strings.Split(pth, "/") if parts[0] == "" { parts = parts[1:] } @@ -112,7 +113,7 @@ func Mkdir(r *Root, path string, parents bool) error { if len(parts) == 0 { // this will only happen on 'mkdir /' - return fmt.Errorf("cannot mkdir '%s'", path) + return fmt.Errorf("cannot mkdir '%s'", pth) } cur := r.GetValue().(*Directory) @@ -130,7 +131,7 @@ func Mkdir(r *Root, path string, parents bool) error { next, ok := fsn.(*Directory) if !ok { - return fmt.Errorf("%s was not a directory", strings.Join(parts[:i], "/")) + return fmt.Errorf("%s was not a directory", path.Join(parts[:i])) } cur = next } @@ -156,9 +157,9 @@ func Lookup(r *Root, path string) (FSNode, error) { // DirLookup will look up a file or directory at the given path // under the directory 'd' -func DirLookup(d *Directory, path string) (FSNode, error) { - path = strings.Trim(path, "/") - parts := strings.Split(path, "/") +func DirLookup(d *Directory, pth string) (FSNode, error) { + pth = strings.Trim(pth, "/") + parts := strings.Split(pth, "/") if len(parts) == 1 && parts[0] == "" { return d, nil } @@ -168,7 +169,7 @@ func DirLookup(d *Directory, path string) (FSNode, error) { for i, p := range parts { chdir, ok := cur.(*Directory) if !ok { - return nil, fmt.Errorf("cannot access %s: Not a directory", strings.Join(parts[:i+1], "/")) + return nil, fmt.Errorf("cannot access %s: Not a directory", path.Join(parts[:i+1])) } child, err := chdir.Child(p) From 978769ba13cc487bab0a1ca9fcb76d2811d477d2 Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 24 Nov 2015 13:59:34 +0700 Subject: [PATCH 1062/3817] strings.Split -> path.SplitList License: MIT Signed-off-by: rht This commit was moved from ipfs/go-mfs@6bb4f0eebea862f465c66d1ada77e985aa7de1e4 --- mfs/mfs_test.go | 20 ++++++++++---------- mfs/ops.go | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 609d81a29..13797c460 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -8,12 +8,12 @@ import ( "io/ioutil" "os" "sort" - "strings" "testing" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "github.com/ipfs/go-ipfs/path" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" @@ -43,8 +43,8 @@ func getRandFile(t *testing.T, ds dag.DAGService, size int64) *dag.Node { return nd } -func mkdirP(t *testing.T, root *Directory, path string) *Directory { - dirs := strings.Split(path, "/") +func mkdirP(t *testing.T, root *Directory, pth string) *Directory { + dirs := path.SplitList(pth) cur := root for _, d := range dirs { n, err := cur.Mkdir(d) @@ -69,15 +69,15 @@ func mkdirP(t *testing.T, root *Directory, path string) *Directory { return cur } -func assertDirAtPath(root *Directory, path string, children []string) error { - fsn, err := DirLookup(root, path) +func assertDirAtPath(root *Directory, pth string, children []string) error { + fsn, err := DirLookup(root, pth) if err != nil { return err } dir, ok := fsn.(*Directory) if !ok { - return fmt.Errorf("%s was not a directory", path) + return fmt.Errorf("%s was not a directory", pth) } listing, err := dir.List() @@ -113,13 +113,13 @@ func compStrArrs(a, b []string) bool { return true } -func assertFileAtPath(ds dag.DAGService, root *Directory, exp *dag.Node, path string) error { - parts := strings.Split(path, "/") +func assertFileAtPath(ds dag.DAGService, root *Directory, exp *dag.Node, pth string) error { + parts := path.SplitList(pth) cur := root for i, d := range parts[:len(parts)-1] { next, err := cur.Child(d) if err != nil { - return fmt.Errorf("looking for %s failed: %s", path, err) + return fmt.Errorf("looking for %s failed: %s", pth, err) } nextDir, ok := next.(*Directory) @@ -138,7 +138,7 @@ func assertFileAtPath(ds dag.DAGService, root *Directory, exp *dag.Node, path st file, ok := finaln.(*File) if !ok { - return fmt.Errorf("%s was not a file!", path) + return fmt.Errorf("%s was not a file!", pth) } out, err := ioutil.ReadAll(file) diff --git a/mfs/ops.go b/mfs/ops.go index 9e8ec1674..c7309a31d 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -101,7 +101,7 @@ func PutNode(r *Root, path string, nd *dag.Node) error { // Mkdir creates a directory at 'path' under the directory 'd', creating // intermediary directories as needed if 'parents' is set to true func Mkdir(r *Root, pth string, parents bool) error { - parts := strings.Split(pth, "/") + parts := path.SplitList(pth) if parts[0] == "" { parts = parts[1:] } @@ -159,7 +159,7 @@ func Lookup(r *Root, path string) (FSNode, error) { // under the directory 'd' func DirLookup(d *Directory, pth string) (FSNode, error) { pth = strings.Trim(pth, "/") - parts := strings.Split(pth, "/") + parts := path.SplitList(pth) if len(parts) == 1 && parts[0] == "" { return d, nil } From 5c9747679f6e1e2224b1459e995716196d26b4cf Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 2 Dec 2015 00:22:37 -0800 Subject: [PATCH 1063/3817] add option to disable flushing files structure on writes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@5683f81e4027ca6113a825387350b65f9895b7c9 --- mfs/dir.go | 121 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 91 insertions(+), 30 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 264dea4a0..b86c98d77 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -53,7 +53,16 @@ func (d *Directory) closeChild(name string, nd *dag.Node) error { d.lock.Lock() defer d.lock.Unlock() - err = d.node.RemoveNodeLink(name) + err = d.updateChild(name, nd) + if err != nil { + return err + } + + return d.parent.closeChild(d.name, d.node) +} + +func (d *Directory) updateChild(name string, nd *dag.Node) error { + err := d.node.RemoveNodeLink(name) if err != nil && err != dag.ErrNotFound { return err } @@ -63,7 +72,7 @@ func (d *Directory) closeChild(name string, nd *dag.Node) error { return err } - return d.parent.closeChild(d.name, d.node) + return nil } func (d *Directory) Type() NodeType { @@ -77,30 +86,16 @@ func (d *Directory) childFile(name string) (*File, error) { return fi, nil } - nd, err := d.childFromDag(name) - if err != nil { - return nil, err - } - i, err := ft.FromBytes(nd.Data) + fsn, err := d.childNode(name) if err != nil { return nil, err } - switch i.GetType() { - case ufspb.Data_Directory: - return nil, ErrIsDirectory - case ufspb.Data_File: - nfi, err := NewFile(name, nd, d, d.dserv) - if err != nil { - return nil, err - } - d.files[name] = nfi - return nfi, nil - case ufspb.Data_Metadata: - return nil, ErrNotYetImplemented - default: - return nil, ErrInvalidChild + if fi, ok := fsn.(*File); ok { + return fi, nil } + + return nil, fmt.Errorf("%s is not a file", name) } // childDir returns a directory under this directory by the given name if it @@ -111,6 +106,21 @@ func (d *Directory) childDir(name string) (*Directory, error) { return dir, nil } + fsn, err := d.childNode(name) + if err != nil { + return nil, err + } + + if dir, ok := fsn.(*Directory); ok { + return dir, nil + } + + return nil, fmt.Errorf("%s is not a directory", name) +} + +// childNode returns a FSNode under this directory by the given name if it exists. +// it does *not* check the cached dirs and files +func (d *Directory) childNode(name string) (FSNode, error) { nd, err := d.childFromDag(name) if err != nil { return nil, err @@ -127,7 +137,12 @@ func (d *Directory) childDir(name string) (*Directory, error) { d.childDirs[name] = ndir return ndir, nil case ufspb.Data_File: - return nil, fmt.Errorf("%s is not a directory", name) + nfi, err := NewFile(name, nd, d, d.dserv) + if err != nil { + return nil, err + } + d.files[name] = nfi + return nfi, nil case ufspb.Data_Metadata: return nil, ErrNotYetImplemented default: @@ -157,17 +172,17 @@ func (d *Directory) Child(name string) (FSNode, error) { // childUnsync returns the child under this directory by the given name // without locking, useful for operations which already hold a lock func (d *Directory) childUnsync(name string) (FSNode, error) { - - dir, err := d.childDir(name) - if err == nil { - return dir, nil + cdir, ok := d.childDirs[name] + if ok { + return cdir, nil } - fi, err := d.childFile(name) - if err == nil { - return fi, nil + + cfile, ok := d.files[name] + if ok { + return cfile, nil } - return nil, os.ErrNotExist + return d.childNode(name) } type NodeListing struct { @@ -305,7 +320,53 @@ func (d *Directory) AddChild(name string, nd *dag.Node) error { return d.parent.closeChild(d.name, d.node) } +func (d *Directory) sync() error { + for name, dir := range d.childDirs { + nd, err := dir.GetNode() + if err != nil { + return err + } + + _, err = d.dserv.Add(nd) + if err != nil { + return err + } + + err = d.updateChild(name, nd) + if err != nil { + return err + } + } + + for name, file := range d.files { + nd, err := file.GetNode() + if err != nil { + return err + } + + _, err = d.dserv.Add(nd) + if err != nil { + return err + } + + err = d.updateChild(name, nd) + if err != nil { + return err + } + } + + return nil +} + func (d *Directory) GetNode() (*dag.Node, error) { + d.Lock() + defer d.Unlock() + + err := d.sync() + if err != nil { + return nil, err + } + return d.node, nil } From 353512a42ecc6a3559ceb41a9d534f3ca9133ccf Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 4 Dec 2015 14:25:13 -0800 Subject: [PATCH 1064/3817] use mfs for adds License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@9f9a8af3f559cbfc85abdd60b2a46cd80a7cb638 --- mfs/system.go | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/mfs/system.go b/mfs/system.go index 22ef63cd4..a7aeb2b20 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -71,15 +71,19 @@ func NewRoot(parent context.Context, ds dag.DAGService, node *dag.Node, pf PubFu return nil, err } + var repub *Republisher + if pf != nil { + repub = NewRepublisher(parent, pf, time.Millisecond*300, time.Second*3) + repub.setVal(ndk) + go repub.Run() + } + root := &Root{ node: node, - repub: NewRepublisher(parent, pf, time.Millisecond*300, time.Second*3), + repub: repub, dserv: ds, } - root.repub.setVal(ndk) - go root.repub.Run() - pbn, err := ft.FromBytes(node.Data) if err != nil { log.Error("IPNS pointer was not unixfs node") @@ -113,12 +117,17 @@ func (kr *Root) closeChild(name string, nd *dag.Node) error { return err } - kr.repub.Update(k) + if kr.repub != nil { + kr.repub.Update(k) + } return nil } func (kr *Root) Close() error { - return kr.repub.Close() + if kr.repub != nil { + return kr.repub.Close() + } + return nil } // Republisher manages when to publish a given entry From 48f5b210d332d8db6c734b862fc98d6ab637c523 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 4 Dec 2015 15:17:31 -0800 Subject: [PATCH 1065/3817] enfastify mfs License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@fb88ae2964f36027b0a8c8244fad8ba9a8e54ad5 --- mfs/dir.go | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index b86c98d77..ece79adeb 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "sync" + "time" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" @@ -28,6 +29,8 @@ type Directory struct { node *dag.Node ctx context.Context + modTime time.Time + name string } @@ -40,6 +43,7 @@ func NewDirectory(ctx context.Context, name string, node *dag.Node, parent child parent: parent, childDirs: make(map[string]*Directory), files: make(map[string]*File), + modTime: time.Now(), } } @@ -72,6 +76,8 @@ func (d *Directory) updateChild(name string, nd *dag.Node) error { return err } + d.modTime = time.Now() + return nil } @@ -285,12 +291,7 @@ func (d *Directory) AddChild(name string, nd *dag.Node) error { d.Lock() defer d.Unlock() - pbn, err := ft.FromBytes(nd.Data) - if err != nil { - return err - } - - _, err = d.childUnsync(name) + _, err := d.childUnsync(name) if err == nil { return ErrDirExists } @@ -305,18 +306,8 @@ func (d *Directory) AddChild(name string, nd *dag.Node) error { return err } - switch pbn.GetType() { - case ft.TDirectory: - d.childDirs[name] = NewDirectory(d.ctx, name, nd, d, d.dserv) - case ft.TFile, ft.TMetadata, ft.TRaw: - nfi, err := NewFile(name, nd, d, d.dserv) - if err != nil { - return err - } - d.files[name] = nfi - default: - return ErrInvalidChild - } + d.modTime = time.Now() + return d.parent.closeChild(d.name, d.node) } From 7b76562a2d1a27af7457b742096ebe3ec45a2770 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 4 Dec 2015 17:18:16 -0800 Subject: [PATCH 1066/3817] fix some tests License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@eab5476628f6c9307fce843a6ed4b944f96d2c45 --- mfs/dir.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mfs/dir.go b/mfs/dir.go index ece79adeb..43271fe49 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -308,7 +308,8 @@ func (d *Directory) AddChild(name string, nd *dag.Node) error { d.modTime = time.Now() - return d.parent.closeChild(d.name, d.node) + //return d.parent.closeChild(d.name, d.node) + return nil } func (d *Directory) sync() error { From e1529ba3b605f3caf95e6fc9b8f13a6236f987a6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 4 Dec 2015 21:09:26 -0800 Subject: [PATCH 1067/3817] fixify tests License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@e752f895d908b8f2a15acfb7acc2e62f3e9d87a2 --- mfs/ops.go | 3 +++ mfs/system.go | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/mfs/ops.go b/mfs/ops.go index c7309a31d..ebb1932ed 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -101,6 +101,9 @@ func PutNode(r *Root, path string, nd *dag.Node) error { // Mkdir creates a directory at 'path' under the directory 'd', creating // intermediary directories as needed if 'parents' is set to true func Mkdir(r *Root, pth string, parents bool) error { + if pth == "" { + panic("empty path") + } parts := path.SplitList(pth) if parts[0] == "" { parts = parts[1:] diff --git a/mfs/system.go b/mfs/system.go index a7aeb2b20..2cfc4e201 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -124,9 +124,21 @@ func (kr *Root) closeChild(name string, nd *dag.Node) error { } func (kr *Root) Close() error { + nd, err := kr.GetValue().GetNode() + if err != nil { + return err + } + + k, err := kr.dserv.Add(nd) + if err != nil { + return err + } + if kr.repub != nil { + kr.repub.Update(k) return kr.repub.Close() } + return nil } From 7af94661e25ec380ca10f1bf36cc05bfacf2d9ac Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Wed, 20 May 2015 08:50:36 -0700 Subject: [PATCH 1068/3817] fsrepo: Refactor to extract datastore internals License: MIT Signed-off-by: Tommi Virtanen This commit was moved from ipfs/go-ipfs-routing@929e4cb9bd1d6631e0398eb50a73b93f6ea3d8f3 --- routing/dht/dht.go | 4 ++-- routing/none/none_client.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 3f50652fd..42a68fa59 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -44,7 +44,7 @@ type IpfsDHT struct { self peer.ID // Local peer (yourself) peerstore peer.Peerstore // Peer Registry - datastore ds.ThreadSafeDatastore // Local data + datastore ds.Datastore // Local data routingTable *kb.RoutingTable // Array of routing tables for differently distanced nodes providers *ProviderManager @@ -60,7 +60,7 @@ type IpfsDHT struct { } // NewDHT creates a new DHT object with the given peer as the 'local' host -func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *IpfsDHT { +func NewDHT(ctx context.Context, h host.Host, dstore ds.Datastore) *IpfsDHT { dht := new(IpfsDHT) dht.datastore = dstore dht.self = h.ID() diff --git a/routing/none/none_client.go b/routing/none/none_client.go index efa0b8a99..4326eb5cc 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -47,7 +47,7 @@ func (c *nilclient) Bootstrap(_ context.Context) error { return nil } -func ConstructNilRouting(_ context.Context, _ p2phost.Host, _ ds.ThreadSafeDatastore) (routing.IpfsRouting, error) { +func ConstructNilRouting(_ context.Context, _ p2phost.Host, _ ds.Datastore) (routing.IpfsRouting, error) { return &nilclient{}, nil } From 5217a5f83817ae1045476ad2a50eb33265a6b699 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 5 Dec 2015 19:20:15 -0800 Subject: [PATCH 1069/3817] Flatten multipart file transfers License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@2768a1103ac87e5899ef71cb96be33042deb4dc5 --- mfs/ops.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/ops.go b/mfs/ops.go index ebb1932ed..fc36b2256 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -102,7 +102,7 @@ func PutNode(r *Root, path string, nd *dag.Node) error { // intermediary directories as needed if 'parents' is set to true func Mkdir(r *Root, pth string, parents bool) error { if pth == "" { - panic("empty path") + return nil } parts := path.SplitList(pth) if parts[0] == "" { From 6ec1eba06f0fa3072d1e30ea66b9ef746c1e130a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 16 Jul 2015 11:32:41 -0700 Subject: [PATCH 1070/3817] fixup datastore interfaces License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@7efab0d2ae34c2248dbf01260299bf9d89c9557d --- routing/none/none_client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 4326eb5cc..6d16a88bf 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -3,11 +3,11 @@ package nilrouting import ( "errors" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" p2phost "github.com/ipfs/go-ipfs/p2p/host" peer "github.com/ipfs/go-ipfs/p2p/peer" + repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" ) @@ -47,7 +47,7 @@ func (c *nilclient) Bootstrap(_ context.Context) error { return nil } -func ConstructNilRouting(_ context.Context, _ p2phost.Host, _ ds.Datastore) (routing.IpfsRouting, error) { +func ConstructNilRouting(_ context.Context, _ p2phost.Host, _ repo.Datastore) (routing.IpfsRouting, error) { return &nilclient{}, nil } From f8f37e3742f3af9d5cd157ad52d67e8079ff901f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Dec 2015 07:56:19 -0800 Subject: [PATCH 1071/3817] PutNode creates intermediary nodes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@268978fe7f1a528f97b02e8f3f8f40314bed5253 --- mfs/dir.go | 4 +++- mfs/ops.go | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 43271fe49..946d9e9a4 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -268,7 +268,9 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { return nil, err } - return d.childDir(name) + dirobj := NewDirectory(d.ctx, name, ndir, d, d.dserv) + d.childDirs[name] = dirobj + return dirobj, nil } func (d *Directory) Unlink(name string) error { diff --git a/mfs/ops.go b/mfs/ops.go index fc36b2256..59c6e239b 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -116,7 +116,10 @@ func Mkdir(r *Root, pth string, parents bool) error { if len(parts) == 0 { // this will only happen on 'mkdir /' - return fmt.Errorf("cannot mkdir '%s'", pth) + if parents { + return nil + } + return fmt.Errorf("cannot create directory '/': Already exists") } cur := r.GetValue().(*Directory) From 8128a9ec6ba0500f2cbfaa70b665859413eea56a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 13 Nov 2015 09:55:42 -0800 Subject: [PATCH 1072/3817] improves memory usage of add License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@b54c4fa0f154a4053bb3b6519c275c0c858ef75f --- ipld/merkledag/merkledag.go | 13 +++++++++++-- ipld/merkledag/utils/utils.go | 6 ++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index a6c6633f0..b84327dfd 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -20,6 +20,7 @@ type DAGService interface { AddRecursive(*Node) error Get(context.Context, key.Key) (*Node, error) Remove(*Node) error + RemoveRecursive(*Node) error // GetDAG returns, in order, all the single leve child // nodes of the passed in node. @@ -107,10 +108,10 @@ func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) { } // Remove deletes the given node and all of its children from the BlockService -func (n *dagService) Remove(nd *Node) error { +func (n *dagService) RemoveRecursive(nd *Node) error { for _, l := range nd.Links { if l.Node != nil { - n.Remove(l.Node) + n.RemoveRecursive(l.Node) } } k, err := nd.Key() @@ -120,6 +121,14 @@ func (n *dagService) Remove(nd *Node) error { return n.Blocks.DeleteBlock(k) } +func (n *dagService) Remove(nd *Node) error { + k, err := nd.Key() + if err != nil { + return err + } + return n.Blocks.DeleteBlock(k) +} + // FetchGraph fetches all nodes that are children of the given node func FetchGraph(ctx context.Context, root *Node, serv DAGService) error { return EnumerateChildrenAsync(ctx, serv, root, key.NewKeySet()) diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index b8dde47e7..35730f48d 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -40,6 +40,8 @@ func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname s return nil, err } + _ = ds.Remove(root) + // ensure no link with that name already exists _ = root.RemoveNodeLink(childname) // ignore error, only option is ErrNotFound @@ -83,6 +85,8 @@ func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, pa return nil, err } + _ = ds.Remove(root) + _ = root.RemoveNodeLink(path[0]) err = root.AddNodeLinkClean(path[0], ndprime) if err != nil { @@ -133,6 +137,8 @@ func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []strin return nil, err } + _ = ds.Remove(root) + _ = root.RemoveNodeLink(path[0]) err = root.AddNodeLinkClean(path[0], nnode) if err != nil { From 83fe6a81b9b996d1cd7d9030dfb83cc2a80a029c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 17 Jul 2015 10:12:27 -0700 Subject: [PATCH 1073/3817] comments from CR License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@09254bf861abd403788eabaee266e5376809fa27 --- routing/supernode/server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 97a5c832d..ab82ab5f1 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -18,13 +18,13 @@ import ( // Server handles routing queries using a database backend type Server struct { local peer.ID - routingBackend datastore.ThreadSafeDatastore + routingBackend datastore.Datastore peerstore peer.Peerstore *proxy.Loopback // so server can be injected into client } // NewServer creates a new Supernode routing Server -func NewServer(ds datastore.ThreadSafeDatastore, ps peer.Peerstore, local peer.ID) (*Server, error) { +func NewServer(ds datastore.Datastore, ps peer.Peerstore, local peer.ID) (*Server, error) { s := &Server{local, ds, ps, nil} s.Loopback = &proxy.Loopback{ Handler: s, From a0e02547a833fde8e43071778ebf095c8adbdfec Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 2 Jan 2016 17:56:42 -0800 Subject: [PATCH 1074/3817] vendor in new go-datastore License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@2d8f6d6e6af5cd14ca6b26f1ba904cc56364d67a --- mfs/mfs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 13797c460..62f0d0836 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -10,8 +10,8 @@ import ( "sort" "testing" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" "github.com/ipfs/go-ipfs/path" From b29a32d0911eba8ddb0b5bd183802b0879d22879 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 13 Nov 2015 10:19:47 -0800 Subject: [PATCH 1075/3817] rework editor creation and finalization License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@7cb580adae838c3bf2ff98774f88dd55d6599121 --- ipld/merkledag/node.go | 4 +- ipld/merkledag/utils/diff.go | 5 +- ipld/merkledag/utils/utils.go | 76 +++++++++++++++++++++--------- ipld/merkledag/utils/utils_test.go | 11 ++--- 4 files changed, 65 insertions(+), 31 deletions(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index f84695f91..b644cae12 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -9,6 +9,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ) +var ErrLinkNotFound = fmt.Errorf("no link by that name") + // Node represents a node in the IPFS Merkle DAG. // nodes have opaque data and a set of navigable links. type Node struct { @@ -160,7 +162,7 @@ func (n *Node) GetNodeLink(name string) (*Link, error) { }, nil } } - return nil, ErrNotFound + return nil, ErrLinkNotFound } func (n *Node) GetLinkedNode(ctx context.Context, ds DAGService, name string) (*Node, error) { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 47ca5124f..8ee50819c 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -37,7 +37,7 @@ func (c *Change) String() string { } func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.Node, cs []*Change) (*dag.Node, error) { - e := NewDagEditor(ds, nd) + e := NewDagEditor(nd, ds) for _, c := range cs { switch c.Type { case Add: @@ -71,7 +71,8 @@ func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.Node, cs []*Cha } } } - return e.GetNode(), nil + + return e.Finalize(ds) } func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) []*Change { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 35730f48d..9d6aac031 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -4,20 +4,41 @@ import ( "errors" "strings" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + syncds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + bstore "github.com/ipfs/go-ipfs/blocks/blockstore" + bserv "github.com/ipfs/go-ipfs/blockservice" + offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" ) type Editor struct { root *dag.Node - ds dag.DAGService + + // tmp is a temporary in memory (for now) dagstore for all of the + // intermediary nodes to be stored in + tmp dag.DAGService + + // src is the dagstore with *all* of the data on it, it is used to pull + // nodes from for modification (nil is a valid value) + src dag.DAGService +} + +func NewMemoryDagService() dag.DAGService { + // build mem-datastore for editor's intermediary nodes + bs := bstore.NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore())) + bsrv := bserv.New(bs, offline.Exchange(bs)) + return dag.NewDAGService(bsrv) } -func NewDagEditor(ds dag.DAGService, root *dag.Node) *Editor { +// root is the node to be modified, source is the dagstore to pull nodes from (optional) +func NewDagEditor(root *dag.Node, source dag.DAGService) *Editor { return &Editor{ root: root, - ds: ds, + tmp: NewMemoryDagService(), + src: source, } } @@ -26,7 +47,7 @@ func (e *Editor) GetNode() *dag.Node { } func (e *Editor) GetDagService() dag.DAGService { - return e.ds + return e.tmp } func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childnd *dag.Node) (*dag.Node, error) { @@ -57,7 +78,7 @@ func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname s func (e *Editor) InsertNodeAtPath(ctx context.Context, path string, toinsert *dag.Node, create func() *dag.Node) error { splpath := strings.Split(path, "/") - nd, err := insertNodeAtPath(ctx, e.ds, e.root, splpath, toinsert, create) + nd, err := e.insertNodeAtPath(ctx, e.root, splpath, toinsert, create) if err != nil { return err } @@ -65,27 +86,32 @@ func (e *Editor) InsertNodeAtPath(ctx context.Context, path string, toinsert *da return nil } -func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert *dag.Node, create func() *dag.Node) (*dag.Node, error) { +func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.Node, path []string, toinsert *dag.Node, create func() *dag.Node) (*dag.Node, error) { if len(path) == 1 { - return addLink(ctx, ds, root, path[0], toinsert) + return addLink(ctx, e.tmp, root, path[0], toinsert) } - nd, err := root.GetLinkedNode(ctx, ds, path[0]) + nd, err := root.GetLinkedNode(ctx, e.tmp, path[0]) if err != nil { // if 'create' is true, we create directories on the way down as needed - if err == dag.ErrNotFound && create != nil { + if err == dag.ErrLinkNotFound && create != nil { nd = create() - } else { + err = nil // no longer an error case + } else if err == dag.ErrNotFound { + nd, err = root.GetLinkedNode(ctx, e.src, path[0]) + } + + if err != nil { return nil, err } } - ndprime, err := insertNodeAtPath(ctx, ds, nd, path[1:], toinsert, create) + ndprime, err := e.insertNodeAtPath(ctx, nd, path[1:], toinsert, create) if err != nil { return nil, err } - _ = ds.Remove(root) + _ = e.tmp.Remove(root) _ = root.RemoveNodeLink(path[0]) err = root.AddNodeLinkClean(path[0], ndprime) @@ -93,7 +119,7 @@ func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, pa return nil, err } - _, err = ds.Add(root) + _, err = e.tmp.Add(root) if err != nil { return nil, err } @@ -103,7 +129,7 @@ func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, pa func (e *Editor) RmLink(ctx context.Context, path string) error { splpath := strings.Split(path, "/") - nd, err := rmLink(ctx, e.ds, e.root, splpath) + nd, err := e.rmLink(ctx, e.root, splpath) if err != nil { return err } @@ -111,7 +137,7 @@ func (e *Editor) RmLink(ctx context.Context, path string) error { return nil } -func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string) (*dag.Node, error) { +func (e *Editor) rmLink(ctx context.Context, root *dag.Node, path []string) (*dag.Node, error) { if len(path) == 1 { // base case, remove node in question err := root.RemoveNodeLink(path[0]) @@ -119,7 +145,7 @@ func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []strin return nil, err } - _, err = ds.Add(root) + _, err = e.tmp.Add(root) if err != nil { return nil, err } @@ -127,17 +153,21 @@ func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []strin return root, nil } - nd, err := root.GetLinkedNode(ctx, ds, path[0]) + nd, err := root.GetLinkedNode(ctx, e.tmp, path[0]) + if err == dag.ErrNotFound { + nd, err = root.GetLinkedNode(ctx, e.src, path[0]) + } + if err != nil { return nil, err } - nnode, err := rmLink(ctx, ds, nd, path[1:]) + nnode, err := e.rmLink(ctx, nd, path[1:]) if err != nil { return nil, err } - _ = ds.Remove(root) + _ = e.tmp.Remove(root) _ = root.RemoveNodeLink(path[0]) err = root.AddNodeLinkClean(path[0], nnode) @@ -145,7 +175,7 @@ func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []strin return nil, err } - _, err = ds.Add(root) + _, err = e.tmp.Add(root) if err != nil { return nil, err } @@ -153,8 +183,10 @@ func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []strin return root, nil } -func (e *Editor) WriteOutputTo(ds dag.DAGService) error { - return copyDag(e.GetNode(), e.ds, ds) +func (e *Editor) Finalize(ds dag.DAGService) (*dag.Node, error) { + nd := e.GetNode() + err := copyDag(nd, e.tmp, ds) + return nd, err } func copyDag(nd *dag.Node, from, to dag.DAGService) error { diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 18839bf8f..498f676b2 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -66,13 +66,12 @@ func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path stri } func TestInsertNode(t *testing.T) { - ds := mdtest.Mock() root := new(dag.Node) - e := NewDagEditor(ds, root) + e := NewDagEditor(root, nil) testInsert(t, e, "a", "anodefortesting", false, "") testInsert(t, e, "a/b", "data", false, "") - testInsert(t, e, "a/b/c/d/e", "blah", false, "merkledag: not found") + testInsert(t, e, "a/b/c/d/e", "blah", false, "no link by that name") testInsert(t, e, "a/b/c/d/e", "foo", true, "") testInsert(t, e, "a/b/c/d/f", "baz", true, "") testInsert(t, e, "a/b/c/d/f", "bar", true, "") @@ -92,7 +91,7 @@ func TestInsertNode(t *testing.T) { func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr string) { child := &dag.Node{Data: []byte(data)} - ck, err := e.ds.Add(child) + ck, err := e.tmp.Add(child) if err != nil { t.Fatal(err) } @@ -117,8 +116,8 @@ func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr } if err != nil { - t.Fatal(err) + t.Fatal(err, path, data, create, experr) } - assertNodeAtPath(t, e.ds, e.root, path, ck) + assertNodeAtPath(t, e.tmp, e.root, path, ck) } From 0a5a624b6349823a383c4ec39c5e15ab8e582fb4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 13 Nov 2015 14:36:13 -0800 Subject: [PATCH 1076/3817] if bucket doesnt have enough peers, grab more elsewhere License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@65f87fd76a53a22391b91be03904311662871dda --- routing/kbucket/sorting.go | 4 ---- routing/kbucket/table.go | 9 ++++----- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index 31c64591a..875b82261 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -32,10 +32,6 @@ func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) pe distance: xor(target, pID), } peerArr = append(peerArr, &pd) - if e == nil { - log.Debug("list element was nil") - return peerArr - } } return peerArr } diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 044d3a2c2..d4cf051f3 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -155,9 +155,10 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []peer.ID { bucket = rt.Buckets[cpl] var peerArr peerSorterArr - if bucket.Len() == 0 { - // In the case of an unusual split, one bucket may be empty. - // if this happens, search both surrounding buckets for nearest peer + peerArr = copyPeersFromList(id, peerArr, bucket.list) + if len(peerArr) < count { + // In the case of an unusual split, one bucket may be short or empty. + // if this happens, search both surrounding buckets for nearby peers if cpl > 0 { plist := rt.Buckets[cpl-1].list peerArr = copyPeersFromList(id, peerArr, plist) @@ -167,8 +168,6 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []peer.ID { plist := rt.Buckets[cpl+1].list peerArr = copyPeersFromList(id, peerArr, plist) } - } else { - peerArr = copyPeersFromList(id, peerArr, bucket.list) } // Sort by distance to local peer From 72e8cb24c68108d0e5e068fa410b06323d1cc155 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 18 Dec 2015 21:59:21 -0800 Subject: [PATCH 1077/3817] do not hold locks for multiple filesystem nodes at the same time License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@e6bc6244063328e284e5aa92e061219b21e00a87 --- mfs/dir.go | 39 +++++++++++++++++++++++++++++---------- mfs/file.go | 44 ++++++++++++++++++++++++++++++-------------- mfs/system.go | 17 +++++++++++++++++ 3 files changed, 76 insertions(+), 24 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 946d9e9a4..8ca79e74a 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -50,19 +50,34 @@ func NewDirectory(ctx context.Context, name string, node *dag.Node, parent child // closeChild updates the child by the given name to the dag node 'nd' // and changes its own dag node, then propogates the changes upward func (d *Directory) closeChild(name string, nd *dag.Node) error { - _, err := d.dserv.Add(nd) + mynd, err := d.closeChildUpdate(name, nd) if err != nil { return err } + return d.parent.closeChild(d.name, mynd) +} + +// closeChildUpdate is the portion of closeChild that needs to be locked around +func (d *Directory) closeChildUpdate(name string, nd *dag.Node) (*dag.Node, error) { d.lock.Lock() defer d.lock.Unlock() - err = d.updateChild(name, nd) + + err := d.updateChild(name, nd) if err != nil { - return err + return nil, err } - return d.parent.closeChild(d.name, d.node) + return d.flushCurrentNode() +} + +func (d *Directory) flushCurrentNode() (*dag.Node, error) { + _, err := d.dserv.Add(d.node) + if err != nil { + return nil, err + } + + return d.node.Copy(), nil } func (d *Directory) updateChild(name string, nd *dag.Node) error { @@ -263,7 +278,7 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { return nil, err } - err = d.parent.closeChild(d.name, d.node) + err = d.flushUp() if err != nil { return nil, err } @@ -285,13 +300,18 @@ func (d *Directory) Unlink(name string) error { return err } + return d.flushUp() +} + +func (d *Directory) flushUp() error { + return d.parent.closeChild(d.name, d.node) } // AddChild adds the node 'nd' under this directory giving it the name 'name' func (d *Directory) AddChild(name string, nd *dag.Node) error { - d.Lock() - defer d.Unlock() + d.lock.Lock() + defer d.lock.Unlock() _, err := d.childUnsync(name) if err == nil { @@ -310,7 +330,6 @@ func (d *Directory) AddChild(name string, nd *dag.Node) error { d.modTime = time.Now() - //return d.parent.closeChild(d.name, d.node) return nil } @@ -353,8 +372,8 @@ func (d *Directory) sync() error { } func (d *Directory) GetNode() (*dag.Node, error) { - d.Lock() - defer d.Unlock() + d.lock.Lock() + defer d.lock.Unlock() err := d.sync() if err != nil { diff --git a/mfs/file.go b/mfs/file.go index fea1112dc..8539a253f 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -16,8 +16,9 @@ type File struct { name string hasChanges bool - mod *mod.DagModifier - lock sync.Mutex + dserv dag.DAGService + mod *mod.DagModifier + lock sync.Mutex } // NewFile returns a NewFile object with the given parameters @@ -28,6 +29,7 @@ func NewFile(name string, node *dag.Node, parent childCloser, dserv dag.DAGServi } return &File{ + dserv: dserv, parent: parent, name: name, mod: dmod, @@ -60,29 +62,43 @@ func (fi *File) CtxReadFull(ctx context.Context, b []byte) (int, error) { // and signals a republish to occur func (fi *File) Close() error { fi.Lock() - defer fi.Unlock() if fi.hasChanges { err := fi.mod.Sync() if err != nil { return err } - nd, err := fi.mod.GetNode() - if err != nil { - return err - } + fi.hasChanges = false + + // explicitly stay locked for flushUp call, + // it will manage the lock for us + return fi.flushUp() + } + + return nil +} +// flushUp syncs the file and adds it to the dagservice +// it *must* be called with the File's lock taken +func (fi *File) flushUp() error { + nd, err := fi.mod.GetNode() + if err != nil { fi.Unlock() - err = fi.parent.closeChild(fi.name, nd) - fi.Lock() - if err != nil { - return err - } + return err + } - fi.hasChanges = false + _, err = fi.dserv.Add(nd) + if err != nil { + fi.Unlock() + return err } - return nil + name := fi.name + parent := fi.parent + + // explicit unlock *only* before closeChild call + fi.Unlock() + return parent.closeChild(name, nd) } // Sync flushes the changes in the file to disk diff --git a/mfs/system.go b/mfs/system.go index 2cfc4e201..d3e705273 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -109,6 +109,23 @@ func (kr *Root) GetValue() FSNode { return kr.val } +func (kr *Root) Flush() error { + nd, err := kr.GetValue().GetNode() + if err != nil { + return err + } + + k, err := kr.dserv.Add(nd) + if err != nil { + return err + } + + if kr.repub != nil { + kr.repub.Update(k) + } + return nil +} + // closeChild implements the childCloser interface, and signals to the publisher that // there are changes ready to be published func (kr *Root) closeChild(name string, nd *dag.Node) error { From b6ded68091419a07eb54bda63cfbc2dab59cece5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 17 Nov 2015 10:17:26 -0800 Subject: [PATCH 1078/3817] comment multiple dagstore error checking License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@ef322f3cd48e28310350f964744caccaba45275b --- ipld/merkledag/utils/utils.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 9d6aac031..1f19e3380 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -98,9 +98,12 @@ func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.Node, path []st nd = create() err = nil // no longer an error case } else if err == dag.ErrNotFound { + // try finding it in our source dagstore nd, err = root.GetLinkedNode(ctx, e.src, path[0]) } + // if we receive an ErrNotFound, then our second 'GetLinkedNode' call + // also fails, we want to error out if err != nil { return nil, err } @@ -153,6 +156,7 @@ func (e *Editor) rmLink(ctx context.Context, root *dag.Node, path []string) (*da return root, nil } + // search for node in both tmp dagstore and source dagstore nd, err := root.GetLinkedNode(ctx, e.tmp, path[0]) if err == dag.ErrNotFound { nd, err = root.GetLinkedNode(ctx, e.src, path[0]) From 75201ab8397807e16ec9f1d80026f9b586f00323 Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Wed, 20 May 2015 08:50:36 -0700 Subject: [PATCH 1079/3817] fsrepo: Refactor to extract datastore internals License: MIT Signed-off-by: Tommi Virtanen This commit was moved from ipfs/go-ipfs-blockstore@8aee0f54c8eb21c11872091dbf9f7076589a156e --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index f2eec8cfe..4f6d89f70 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -51,7 +51,7 @@ type GCBlockstore interface { PinLock() func() } -func NewBlockstore(d ds.ThreadSafeDatastore) *blockstore { +func NewBlockstore(d ds.Datastore) *blockstore { dd := dsns.Wrap(d, BlockPrefix) return &blockstore{ datastore: dd, From 41413f005883579887ebdf9e3f7c2d5804c3dfe7 Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Wed, 15 Jul 2015 08:36:48 -0700 Subject: [PATCH 1080/3817] gofmt generated assets The generated file went through some changes because of differing go-bindata versions. License: MIT Signed-off-by: Tommi Virtanen This commit was moved from ipfs/go-ipfs-pinner@930fa6c24d2b4d45cc4ff12c7b3bdd4cfc7196ee --- pinning/pinner/pin_test.go | 45 ++------------------------------------ 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index d681bb8df..818a414ab 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -197,18 +197,14 @@ func TestPinRecursiveFail(t *testing.T) { ctx := context.Background() dstore := dssync.MutexWrap(ds.NewMapDatastore()) bstore := blockstore.NewBlockstore(dstore) - bserv, err := bs.New(bstore, offline.Exchange(bstore)) - if err != nil { - t.Fatal(err) - } - + bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) p := NewPinner(dstore, dserv) a, _ := randNode() b, _ := randNode() - err = a.AddNodeLinkClean("child", b) + err := a.AddNodeLinkClean("child", b) if err != nil { t.Fatal(err) } @@ -232,40 +228,3 @@ func TestPinRecursiveFail(t *testing.T) { t.Fatal(err) } } - -func TestPinRecursiveFail(t *testing.T) { - ctx := context.Background() - dstore := dssync.MutexWrap(ds.NewMapDatastore()) - bstore := blockstore.NewBlockstore(dstore) - bserv := bs.New(bstore, offline.Exchange(bstore)) - - dserv := mdag.NewDAGService(bserv) - - p := NewPinner(dstore, dserv) - - a, _ := randNode() - b, _ := randNode() - err := a.AddNodeLinkClean("child", b) - if err != nil { - t.Fatal(err) - } - - // Note: this isnt a time based test, we expect the pin to fail - mctx, cancel := context.WithTimeout(ctx, time.Millisecond) - defer cancel() - err = p.Pin(mctx, a, true) - if err == nil { - t.Fatal("should have failed to pin here") - } - - if _, err := dserv.Add(b); err != nil { - t.Fatal(err) - } - - // this one is time based... but shouldnt cause any issues - mctx, cancel = context.WithTimeout(ctx, time.Second) - defer cancel() - if err := p.Pin(mctx, a, true); err != nil { - t.Fatal(err) - } -} From 8723b7f3df662599739e860260cb4a3bf74436dd Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 24 Nov 2015 13:59:34 +0700 Subject: [PATCH 1081/3817] strings.Split -> path.SplitList License: MIT Signed-off-by: rht This commit was moved from ipfs/go-ipfs-routing@f1397e318370cb237bc35c5224ad8f2bee7e8cf3 --- routing/record/selection.go | 4 ++-- routing/record/validation.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/routing/record/selection.go b/routing/record/selection.go index e90ebcd39..8e68006c1 100644 --- a/routing/record/selection.go +++ b/routing/record/selection.go @@ -2,9 +2,9 @@ package record import ( "errors" - "strings" key "github.com/ipfs/go-ipfs/blocks/key" + path "github.com/ipfs/go-ipfs/path" ) // A SelectorFunc selects the best value for the given key from @@ -18,7 +18,7 @@ func (s Selector) BestRecord(k key.Key, recs [][]byte) (int, error) { return 0, errors.New("no records given!") } - parts := strings.Split(string(k), "/") + parts := path.SplitList(string(k)) if len(parts) < 3 { log.Infof("Record key does not have selectorfunc: %s", k) return 0, errors.New("record key does not have selectorfunc") diff --git a/routing/record/validation.go b/routing/record/validation.go index f186bea90..a2afc0dfa 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -3,10 +3,10 @@ package record import ( "bytes" "errors" - "strings" key "github.com/ipfs/go-ipfs/blocks/key" ci "github.com/ipfs/go-ipfs/p2p/crypto" + path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" ) @@ -37,7 +37,7 @@ type ValidChecker struct { // It runs needed validators func (v Validator) VerifyRecord(r *pb.Record) error { // Now, check validity func - parts := strings.Split(r.GetKey(), "/") + parts := path.SplitList(r.GetKey()) if len(parts) < 3 { log.Infof("Record key does not have validator: %s", key.Key(r.GetKey())) return nil @@ -54,7 +54,7 @@ func (v Validator) VerifyRecord(r *pb.Record) error { func (v Validator) IsSigned(k key.Key) (bool, error) { // Now, check validity func - parts := strings.Split(string(k), "/") + parts := path.SplitList(string(k)) if len(parts) < 3 { log.Infof("Record key does not have validator: %s", k) return false, nil From 62683a2784525f5fe424a9ed758c5570f0ae7bc6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 18 Dec 2015 22:12:39 -0800 Subject: [PATCH 1082/3817] just flush dir in mkdir flush, not whole tree License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@846759c26cdc41ab262ab01199bda136bc2ccff4 --- mfs/dir.go | 20 ++++++++++++-------- mfs/ops.go | 11 +++++++++-- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 8ca79e74a..3ec39bf7d 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -278,11 +278,6 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { return nil, err } - err = d.flushUp() - if err != nil { - return nil, err - } - dirobj := NewDirectory(d.ctx, name, ndir, d, d.dserv) d.childDirs[name] = dirobj return dirobj, nil @@ -300,12 +295,21 @@ func (d *Directory) Unlink(name string) error { return err } - return d.flushUp() + _, err = d.dserv.Add(d.node) + if err != nil { + return err + } + + return d.parent.closeChild(d.name, d.node) } -func (d *Directory) flushUp() error { +func (d *Directory) Flush() error { + nd, err := d.flushCurrentNode() + if err != nil { + return err + } - return d.parent.closeChild(d.name, d.node) + return d.parent.closeChild(d.name, nd) } // AddChild adds the node 'nd' under this directory giving it the name 'name' diff --git a/mfs/ops.go b/mfs/ops.go index 59c6e239b..d21f71770 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -100,7 +100,7 @@ func PutNode(r *Root, path string, nd *dag.Node) error { // Mkdir creates a directory at 'path' under the directory 'd', creating // intermediary directories as needed if 'parents' is set to true -func Mkdir(r *Root, pth string, parents bool) error { +func Mkdir(r *Root, pth string, parents bool, flush bool) error { if pth == "" { return nil } @@ -142,13 +142,20 @@ func Mkdir(r *Root, pth string, parents bool) error { cur = next } - _, err := cur.Mkdir(parts[len(parts)-1]) + final, err := cur.Mkdir(parts[len(parts)-1]) if err != nil { if !parents || err != os.ErrExist { return err } } + if flush { + err := final.Flush() + if err != nil { + return err + } + } + return nil } From 831933f1d11b8cf3c3002ca5412e9d518ca2f29d Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 24 Nov 2015 13:59:34 +0700 Subject: [PATCH 1083/3817] strings.Split -> path.SplitList License: MIT Signed-off-by: rht This commit was moved from ipfs/go-merkledag@68870d0377c59a6793c8acb40af0ee0bc85ea115 --- ipld/merkledag/utils/utils.go | 10 +++++----- ipld/merkledag/utils/utils_test.go | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 1f19e3380..97e2ebb4e 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -2,7 +2,6 @@ package dagutils import ( "errors" - "strings" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" syncds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" @@ -12,6 +11,7 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" + path "github.com/ipfs/go-ipfs/path" ) type Editor struct { @@ -76,8 +76,8 @@ func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname s return root, nil } -func (e *Editor) InsertNodeAtPath(ctx context.Context, path string, toinsert *dag.Node, create func() *dag.Node) error { - splpath := strings.Split(path, "/") +func (e *Editor) InsertNodeAtPath(ctx context.Context, pth string, toinsert *dag.Node, create func() *dag.Node) error { + splpath := path.SplitList(pth) nd, err := e.insertNodeAtPath(ctx, e.root, splpath, toinsert, create) if err != nil { return err @@ -130,8 +130,8 @@ func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.Node, path []st return root, nil } -func (e *Editor) RmLink(ctx context.Context, path string) error { - splpath := strings.Split(path, "/") +func (e *Editor) RmLink(ctx context.Context, pth string) error { + splpath := path.SplitList(pth) nd, err := e.rmLink(ctx, e.root, splpath) if err != nil { return err diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 498f676b2..d4b2af5f3 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -1,12 +1,12 @@ package dagutils import ( - "strings" "testing" key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" + path "github.com/ipfs/go-ipfs/path" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) @@ -43,8 +43,8 @@ func TestAddLink(t *testing.T) { } } -func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path string, exp key.Key) { - parts := strings.Split(path, "/") +func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, pth string, exp key.Key) { + parts := path.SplitList(pth) cur := root for _, e := range parts { nxt, err := cur.GetLinkedNode(context.Background(), ds, e) From 77647e15f2c1a152d5b9b396df91b2122a362504 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 16 Jul 2015 11:32:41 -0700 Subject: [PATCH 1084/3817] fixup datastore interfaces License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@1c25f6a282b9c70e28d5f239fc1c15ea7aa1558f --- blockstore/blockstore.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 4f6d89f70..e6a13cda6 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -25,7 +25,7 @@ var ValueTypeMismatch = errors.New("The retrieved value is not a Block") var ErrNotFound = errors.New("blockstore: block not found") -// Blockstore wraps a ThreadSafeDatastore +// Blockstore wraps a Datastore type Blockstore interface { DeleteBlock(key.Key) error Has(key.Key) (bool, error) @@ -51,7 +51,7 @@ type GCBlockstore interface { PinLock() func() } -func NewBlockstore(d ds.Datastore) *blockstore { +func NewBlockstore(d ds.Batching) *blockstore { dd := dsns.Wrap(d, BlockPrefix) return &blockstore{ datastore: dd, @@ -60,8 +60,6 @@ func NewBlockstore(d ds.Datastore) *blockstore { type blockstore struct { datastore ds.Batching - // cant be ThreadSafeDatastore cause namespace.Datastore doesnt support it. - // we do check it on `NewBlockstore` though. lk sync.RWMutex } From 8d31d7556d57f5aa9ca388effca6db1c4d86f16c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 9 Sep 2015 15:02:46 -0700 Subject: [PATCH 1085/3817] Refactor ipnsfs into a more generic and well tested mfs License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@428e8f9d82305118bce843a39b31974f09e659a8 --- unixfs/format.go | 1 + unixfs/mod/dagmodifier.go | 16 +-- unixfs/mod/dagmodifier_test.go | 180 ++++++++++----------------------- 3 files changed, 55 insertions(+), 142 deletions(-) diff --git a/unixfs/format.go b/unixfs/format.go index 9193ddede..472a575e7 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -67,6 +67,7 @@ func WrapData(b []byte) []byte { typ := pb.Data_Raw pbdata.Data = b pbdata.Type = &typ + pbdata.Filesize = proto.Uint64(uint64(len(b))) out, err := proto.Marshal(pbdata) if err != nil { diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 481005c2f..3c6a110f6 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -15,7 +15,6 @@ import ( help "github.com/ipfs/go-ipfs/importer/helpers" trickle "github.com/ipfs/go-ipfs/importer/trickle" mdag "github.com/ipfs/go-ipfs/merkledag" - pin "github.com/ipfs/go-ipfs/pin" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" @@ -36,7 +35,6 @@ var log = logging.Logger("dagio") type DagModifier struct { dagserv mdag.DAGService curNode *mdag.Node - mp pin.Pinner splitter chunk.SplitterGen ctx context.Context @@ -49,13 +47,12 @@ type DagModifier struct { read *uio.DagReader } -func NewDagModifier(ctx context.Context, from *mdag.Node, serv mdag.DAGService, mp pin.Pinner, spl chunk.SplitterGen) (*DagModifier, error) { +func NewDagModifier(ctx context.Context, from *mdag.Node, serv mdag.DAGService, spl chunk.SplitterGen) (*DagModifier, error) { return &DagModifier{ curNode: from.Copy(), dagserv: serv, splitter: spl, ctx: ctx, - mp: mp, }, nil } @@ -174,7 +171,7 @@ func (dm *DagModifier) Sync() error { buflen := dm.wrBuf.Len() // Grab key for unpinning after mod operation - curk, err := dm.curNode.Key() + _, err := dm.curNode.Key() if err != nil { return err } @@ -208,15 +205,6 @@ func (dm *DagModifier) Sync() error { dm.curNode = nd } - // Finalize correct pinning, and flush pinner. - // Be careful about the order, as curk might equal thisk. - dm.mp.RemovePinWithMode(curk, pin.Recursive) - dm.mp.PinWithMode(thisk, pin.Recursive) - err = dm.mp.Flush() - if err != nil { - return err - } - dm.writeStart += uint64(buflen) dm.wrBuf = nil diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 48be0545e..6f53a90d1 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -4,7 +4,6 @@ import ( "fmt" "io" "io/ioutil" - "math/rand" "os" "testing" @@ -17,8 +16,6 @@ import ( h "github.com/ipfs/go-ipfs/importer/helpers" trickle "github.com/ipfs/go-ipfs/importer/trickle" mdag "github.com/ipfs/go-ipfs/merkledag" - pin "github.com/ipfs/go-ipfs/pin" - gc "github.com/ipfs/go-ipfs/pin/gc" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" u "github.com/ipfs/go-ipfs/util" @@ -27,25 +24,24 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) -func getMockDagServ(t testing.TB) (mdag.DAGService, pin.Pinner) { +func getMockDagServ(t testing.TB) mdag.DAGService { dstore := ds.NewMapDatastore() tsds := sync.MutexWrap(dstore) bstore := blockstore.NewBlockstore(tsds) bserv := bs.New(bstore, offline.Exchange(bstore)) - dserv := mdag.NewDAGService(bserv) - return dserv, pin.NewPinner(tsds, dserv) + return mdag.NewDAGService(bserv) } -func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.GCBlockstore, pin.Pinner) { +func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.GCBlockstore) { dstore := ds.NewMapDatastore() tsds := sync.MutexWrap(dstore) bstore := blockstore.NewBlockstore(tsds) bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) - return dserv, bstore, pin.NewPinner(tsds, dserv) + return dserv, bstore } -func getNode(t testing.TB, dserv mdag.DAGService, size int64, pinner pin.Pinner) ([]byte, *mdag.Node) { +func getNode(t testing.TB, dserv mdag.DAGService, size int64) ([]byte, *mdag.Node) { in := io.LimitReader(u.NewTimeSeededRand(), size) node, err := imp.BuildTrickleDagFromReader(dserv, sizeSplitterGen(500)(in)) if err != nil { @@ -118,12 +114,12 @@ func sizeSplitterGen(size int64) chunk.SplitterGen { } func TestDagModifierBasic(t *testing.T) { - dserv, pin := getMockDagServ(t) - b, n := getNode(t, dserv, 50000, pin) + dserv := getMockDagServ(t) + b, n := getNode(t, dserv, 50000) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pin, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -172,13 +168,13 @@ func TestDagModifierBasic(t *testing.T) { } func TestMultiWrite(t *testing.T) { - dserv, pins := getMockDagServ(t) - _, n := getNode(t, dserv, 0, pins) + dserv := getMockDagServ(t) + _, n := getNode(t, dserv, 0) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -225,13 +221,13 @@ func TestMultiWrite(t *testing.T) { } func TestMultiWriteAndFlush(t *testing.T) { - dserv, pins := getMockDagServ(t) - _, n := getNode(t, dserv, 0, pins) + dserv := getMockDagServ(t) + _, n := getNode(t, dserv, 0) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -273,13 +269,13 @@ func TestMultiWriteAndFlush(t *testing.T) { } func TestWriteNewFile(t *testing.T) { - dserv, pins := getMockDagServ(t) - _, n := getNode(t, dserv, 0, pins) + dserv := getMockDagServ(t) + _, n := getNode(t, dserv, 0) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -316,13 +312,13 @@ func TestWriteNewFile(t *testing.T) { } func TestMultiWriteCoal(t *testing.T) { - dserv, pins := getMockDagServ(t) - _, n := getNode(t, dserv, 0, pins) + dserv := getMockDagServ(t) + _, n := getNode(t, dserv, 0) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -362,13 +358,13 @@ func TestMultiWriteCoal(t *testing.T) { } func TestLargeWriteChunks(t *testing.T) { - dserv, pins := getMockDagServ(t) - _, n := getNode(t, dserv, 0, pins) + dserv := getMockDagServ(t) + _, n := getNode(t, dserv, 0) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -401,12 +397,12 @@ func TestLargeWriteChunks(t *testing.T) { } func TestDagTruncate(t *testing.T) { - dserv, pins := getMockDagServ(t) - b, n := getNode(t, dserv, 50000, pins) + dserv := getMockDagServ(t) + b, n := getNode(t, dserv, 50000) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -415,164 +411,92 @@ func TestDagTruncate(t *testing.T) { if err != nil { t.Fatal(err) } - - _, err = dagmod.Seek(0, os.SEEK_SET) + size, err := dagmod.Size() if err != nil { t.Fatal(err) } - out, err := ioutil.ReadAll(dagmod) - if err != nil { - t.Fatal(err) - } - - if err = arrComp(out, b[:12345]); err != nil { - t.Fatal(err) + if size != 12345 { + t.Fatal("size was incorrect!") } -} -func TestSparseWrite(t *testing.T) { - dserv, pins := getMockDagServ(t) - _, n := getNode(t, dserv, 0, pins) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) + _, err = dagmod.Seek(0, os.SEEK_SET) if err != nil { t.Fatal(err) } - buf := make([]byte, 5000) - u.NewTimeSeededRand().Read(buf[2500:]) - - wrote, err := dagmod.WriteAt(buf[2500:], 2500) + out, err := ioutil.ReadAll(dagmod) if err != nil { t.Fatal(err) } - if wrote != 2500 { - t.Fatal("incorrect write amount") - } - - _, err = dagmod.Seek(0, os.SEEK_SET) - if err != nil { + if err = arrComp(out, b[:12345]); err != nil { t.Fatal(err) } - out, err := ioutil.ReadAll(dagmod) + err = dagmod.Truncate(10) if err != nil { t.Fatal(err) } - if err = arrComp(out, buf); err != nil { - t.Fatal(err) - } -} - -func basicGC(t *testing.T, bs blockstore.GCBlockstore, pins pin.Pinner) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() // in case error occurs during operation - out, err := gc.GC(ctx, bs, pins) + size, err = dagmod.Size() if err != nil { t.Fatal(err) } - for range out { + + if size != 10 { + t.Fatal("size was incorrect!") } } -func TestCorrectPinning(t *testing.T) { - dserv, bstore, pins := getMockDagServAndBstore(t) - b, n := getNode(t, dserv, 50000, pins) +func TestSparseWrite(t *testing.T) { + dserv := getMockDagServ(t) + _, n := getNode(t, dserv, 0) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) if err != nil { t.Fatal(err) } - buf := make([]byte, 1024) - for i := 0; i < 100; i++ { - size, err := dagmod.Size() - if err != nil { - t.Fatal(err) - } - offset := rand.Intn(int(size)) - u.NewTimeSeededRand().Read(buf) - - if offset+len(buf) > int(size) { - b = append(b[:offset], buf...) - } else { - copy(b[offset:], buf) - } - - n, err := dagmod.WriteAt(buf, int64(offset)) - if err != nil { - t.Fatal(err) - } - if n != len(buf) { - t.Fatal("wrote incorrect number of bytes") - } - } + buf := make([]byte, 5000) + u.NewTimeSeededRand().Read(buf[2500:]) - fisize, err := dagmod.Size() + wrote, err := dagmod.WriteAt(buf[2500:], 2500) if err != nil { t.Fatal(err) } - if int(fisize) != len(b) { - t.Fatal("reported filesize incorrect", fisize, len(b)) + if wrote != 2500 { + t.Fatal("incorrect write amount") } - // Run a GC, then ensure we can still read the file correctly - basicGC(t, bstore, pins) - - nd, err := dagmod.GetNode() - if err != nil { - t.Fatal(err) - } - read, err := uio.NewDagReader(context.Background(), nd, dserv) + _, err = dagmod.Seek(0, os.SEEK_SET) if err != nil { t.Fatal(err) } - out, err := ioutil.ReadAll(read) + out, err := ioutil.ReadAll(dagmod) if err != nil { t.Fatal(err) } - if err = arrComp(out, b); err != nil { - t.Fatal(err) - } - - rootk, err := nd.Key() - if err != nil { + if err = arrComp(out, buf); err != nil { t.Fatal(err) } - - // Verify only one recursive pin - recpins := pins.RecursiveKeys() - if len(recpins) != 1 { - t.Fatal("Incorrect number of pinned entries") - } - - // verify the correct node is pinned - if recpins[0] != rootk { - t.Fatal("Incorrect node recursively pinned") - } - } func BenchmarkDagmodWrite(b *testing.B) { b.StopTimer() - dserv, pins := getMockDagServ(b) - _, n := getNode(b, dserv, 0, pins) + dserv := getMockDagServ(b) + _, n := getNode(b, dserv, 0) ctx, cancel := context.WithCancel(context.Background()) defer cancel() wrsize := 4096 - dagmod, err := NewDagModifier(ctx, n, dserv, pins, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) if err != nil { b.Fatal(err) } From 30d6c12edd3deda5bf680d2f81c41ffa19558a74 Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Wed, 20 May 2015 08:50:36 -0700 Subject: [PATCH 1086/3817] fsrepo: Refactor to extract datastore internals License: MIT Signed-off-by: Tommi Virtanen This commit was moved from ipfs/go-ipfs-pinner@59393aecc30d93681735f58c4b75145394925c36 --- pinning/pinner/pin.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 80c11d698..41d97a142 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -64,11 +64,11 @@ type pinner struct { // not delete them. internalPin map[key.Key]struct{} dserv mdag.DAGService - dstore ds.ThreadSafeDatastore + dstore ds.Datastore } // NewPinner creates a new pinner using the given datastore as a backend -func NewPinner(dstore ds.ThreadSafeDatastore, serv mdag.DAGService) Pinner { +func NewPinner(dstore ds.Datastore, serv mdag.DAGService) Pinner { // Load set from given datastore... rcset := set.NewSimpleBlockSet() @@ -207,7 +207,7 @@ func (p *pinner) RemovePinWithMode(key key.Key, mode PinMode) { } // LoadPinner loads a pinner and its keysets from the given datastore -func LoadPinner(d ds.ThreadSafeDatastore, dserv mdag.DAGService) (Pinner, error) { +func LoadPinner(d ds.Datastore, dserv mdag.DAGService) (Pinner, error) { p := new(pinner) rootKeyI, err := d.Get(pinDatastoreKey) From dc45cea51aaf805d4c675d39cc19a3d946cc7ae5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 19 Nov 2015 11:24:59 -0800 Subject: [PATCH 1087/3817] send record fixes to peers who send outdated records License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@3cfc215b266d93a1142da36af395a8fcb7df869d --- routing/dht/dht.go | 4 +++- routing/dht/routing.go | 12 +++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 42a68fa59..c0b7970be 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -173,7 +173,9 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.ID, err = dht.verifyRecordOnline(ctx, record) if err != nil { log.Info("Received invalid record! (discarded)") - return nil, nil, err + // still return a non-nil record to signify that we received + // a bad record from this peer + record = new(pb.Record) } return record, peers, nil } diff --git a/routing/dht/routing.go b/routing/dht/routing.go index df93396ce..0f6d50d1a 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -91,7 +91,9 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key key.Key) ([]byte, error) { var recs [][]byte for _, v := range vals { - recs = append(recs, v.Val) + if v.Val != nil { + recs = append(recs, v.Val) + } } i, err := dht.Selector.BestRecord(key, recs) @@ -170,6 +172,14 @@ func (dht *IpfsDHT) GetValues(ctx context.Context, key key.Key, nvals int) ([]ro rec, peers, err := dht.getValueOrPeers(ctx, p, key) if err != nil { + if err == routing.ErrNotFound { + // in this case, they responded with nothing, + // still send a notification + notif.PublishQueryEvent(parent, ¬if.QueryEvent{ + Type: notif.PeerResponse, + ID: p, + }) + } return nil, err } From b1f13b981ac61940b1e3a32db3cb304394f41ace Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 26 Dec 2015 17:24:31 -0800 Subject: [PATCH 1088/3817] add test and locking fix License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@6b725109f49241d3fb1b7ab7c7106772ccb480b0 --- mfs/dir.go | 15 ++++- mfs/mfs_test.go | 145 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 2 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 3ec39bf7d..b714cb093 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "os" + "path" "sync" "time" @@ -48,7 +49,7 @@ func NewDirectory(ctx context.Context, name string, node *dag.Node, parent child } // closeChild updates the child by the given name to the dag node 'nd' -// and changes its own dag node, then propogates the changes upward +// and changes its own dag node func (d *Directory) closeChild(name string, nd *dag.Node) error { mynd, err := d.closeChildUpdate(name, nd) if err != nil { @@ -300,7 +301,7 @@ func (d *Directory) Unlink(name string) error { return err } - return d.parent.closeChild(d.name, d.node) + return nil } func (d *Directory) Flush() error { @@ -375,6 +376,16 @@ func (d *Directory) sync() error { return nil } +func (d *Directory) Path() string { + cur := d + var out string + for cur != nil { + out = path.Join(cur.name, out) + cur = cur.parent.(*Directory) + } + return out +} + func (d *Directory) GetNode() (*dag.Node, error) { d.lock.Lock() defer d.lock.Unlock() diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 62f0d0836..65e1e1a84 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -6,10 +6,12 @@ import ( "fmt" "io" "io/ioutil" + "math/rand" "os" "sort" "testing" + randbo "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/dustin/randbo" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" @@ -474,3 +476,146 @@ func TestMfsFile(t *testing.T) { t.Fatal(err) } } + +func randomWalk(d *Directory, n int) (*Directory, error) { + for i := 0; i < n; i++ { + dirents, err := d.List() + if err != nil { + return nil, err + } + + var childdirs []NodeListing + for _, child := range dirents { + if child.Type == int(TDir) { + childdirs = append(childdirs, child) + } + } + if len(childdirs) == 0 { + return d, nil + } + + next := childdirs[rand.Intn(len(childdirs))].Name + + nextD, err := d.Child(next) + if err != nil { + return nil, err + } + + d = nextD.(*Directory) + } + return d, nil +} + +func randomName() string { + set := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_" + length := rand.Intn(10) + 2 + var out string + for i := 0; i < length; i++ { + j := rand.Intn(len(set)) + out += set[j : j+1] + } + return out +} + +func actorMakeFile(d *Directory) error { + d, err := randomWalk(d, rand.Intn(7)) + if err != nil { + return err + } + + name := randomName() + f, err := NewFile(name, &dag.Node{Data: ft.FilePBData(nil, 0)}, d, d.dserv) + if err != nil { + return err + } + + r := io.LimitReader(randbo.New(), int64(77*rand.Intn(123))) + _, err = io.Copy(f, r) + if err != nil { + return err + } + + err = f.Close() + if err != nil { + return err + } + + return nil +} +func actorMkdir(d *Directory) error { + d, err := randomWalk(d, rand.Intn(7)) + if err != nil { + return err + } + + _, err = d.Mkdir(randomName()) + if err != nil { + return err + } + + return nil +} + +func actorRemoveFile(d *Directory) error { + d, err := randomWalk(d, rand.Intn(7)) + if err != nil { + return err + } + + ents, err := d.List() + if err != nil { + return err + } + + if len(ents) == 0 { + return nil + } + + re := ents[rand.Intn(len(ents))] + + return d.Unlink(re.Name) +} + +func testActor(rt *Root, iterations int, errs chan error) { + d := rt.GetValue().(*Directory) + for i := 0; i < iterations; i++ { + switch rand.Intn(4) { + case 0: + if err := actorMkdir(d); err != nil { + errs <- err + return + } + case 1, 2: + if err := actorMakeFile(d); err != nil { + errs <- err + return + } + case 3: + if err := actorRemoveFile(d); err != nil { + errs <- err + return + } + } + } + errs <- nil +} + +func TestMfsStress(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + _, rt := setupRoot(ctx, t) + + numroutines := 2 + + errs := make(chan error) + for i := 0; i < numroutines; i++ { + go testActor(rt, 50, errs) + } + + for i := 0; i < numroutines; i++ { + err := <-errs + if err != nil { + t.Fatal(err) + } + } +} From 6d7211ff89d3c524838d19332377cc9321b32e41 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 4 Dec 2015 14:25:13 -0800 Subject: [PATCH 1089/3817] use mfs for adds License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@8da09c77783a2516cfaa179ed2fa5aa5fb3f9d65 --- ipld/merkledag/merkledag.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index b84327dfd..0486e3321 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -3,6 +3,7 @@ package merkledag import ( "fmt" + "time" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" @@ -48,6 +49,14 @@ func (n *dagService) Add(nd *Node) (key.Key, error) { if n == nil { // FIXME remove this assertion. protect with constructor invariant return "", fmt.Errorf("dagService is nil") } + /* + start := time.Now() + defer func() { + took := time.Now().Sub(start) + log.Error("add took: %s", took) + }() + */ + _ = time.Saturday d, err := nd.Encoded(false) if err != nil { From 1d3a6c86b6013ef39d3c7de03059bdf47eae86cd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 17 Jul 2015 10:12:27 -0700 Subject: [PATCH 1090/3817] comments from CR License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@9f9c117252549e39850ae69459c6ff63921b79d2 --- blockstore/blockstore.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index e6a13cda6..bc000df93 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -52,9 +52,11 @@ type GCBlockstore interface { } func NewBlockstore(d ds.Batching) *blockstore { + var dsb ds.Batching dd := dsns.Wrap(d, BlockPrefix) + dsb = dd return &blockstore{ - datastore: dd, + datastore: dsb, } } From bf307d126bbf2f6708e1932c47597c165dfb9abc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 30 Sep 2015 17:12:51 -0700 Subject: [PATCH 1091/3817] address comments from CR License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@90f6f5f07669a871a585de7be87e7927d4212305 --- unixfs/mod/dagmodifier.go | 20 ++++++++++++--- unixfs/mod/dagmodifier_test.go | 47 ++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 3c6a110f6..aa4de8caf 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -368,19 +368,31 @@ func (dm *DagModifier) Seek(offset int64, whence int) (int64, error) { return 0, err } + fisize, err := dm.Size() + if err != nil { + return 0, err + } + + var newoffset uint64 switch whence { case os.SEEK_CUR: - dm.curWrOff += uint64(offset) - dm.writeStart = dm.curWrOff + newoffset = dm.curWrOff + uint64(offset) case os.SEEK_SET: - dm.curWrOff = uint64(offset) - dm.writeStart = uint64(offset) + newoffset = uint64(offset) case os.SEEK_END: return 0, ErrSeekEndNotImpl default: return 0, ErrUnrecognizedWhence } + if offset > fisize { + if err := dm.expandSparse(offset - fisize); err != nil { + return 0, err + } + } + dm.curWrOff = newoffset + dm.writeStart = newoffset + if dm.read != nil { _, err = dm.read.Seek(offset, whence) if err != nil { diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 6f53a90d1..f3341690c 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -487,6 +487,53 @@ func TestSparseWrite(t *testing.T) { } } +func TestSeekPastEndWrite(t *testing.T) { + dserv := getMockDagServ(t) + _, n := getNode(t, dserv, 0) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + if err != nil { + t.Fatal(err) + } + + buf := make([]byte, 5000) + u.NewTimeSeededRand().Read(buf[2500:]) + + nseek, err := dagmod.Seek(2500, os.SEEK_SET) + if err != nil { + t.Fatal(err) + } + + if nseek != 2500 { + t.Fatal("failed to seek") + } + + wrote, err := dagmod.Write(buf[2500:]) + if err != nil { + t.Fatal(err) + } + + if wrote != 2500 { + t.Fatal("incorrect write amount") + } + + _, err = dagmod.Seek(0, os.SEEK_SET) + if err != nil { + t.Fatal(err) + } + + out, err := ioutil.ReadAll(dagmod) + if err != nil { + t.Fatal(err) + } + + if err = arrComp(out, buf); err != nil { + t.Fatal(err) + } +} + func BenchmarkDagmodWrite(b *testing.B) { b.StopTimer() dserv := getMockDagServ(b) From 2c7d246e5e39583903ed1bc38534d74ebb56b50c Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 17 Nov 2015 15:36:48 +0700 Subject: [PATCH 1092/3817] Replace strings.Join(elms, "/") with path.Join(elms) License: MIT Signed-off-by: rht This commit was moved from ipfs/go-path@3c97f83b1ab4187301825513c4accf2a41722689 --- path/path.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/path/path.go b/path/path.go index e865ba287..b6aa187b9 100644 --- a/path/path.go +++ b/path/path.go @@ -102,3 +102,7 @@ func (p *Path) IsValid() error { _, err := ParsePath(p.String()) return err } + +func Join(pths []string) string { + return strings.Join(pths, "/") +} From cb0149c2071c690535ffc5552b66c0e5f5de9d37 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 5 Dec 2015 20:31:25 -0800 Subject: [PATCH 1093/3817] Allow for gc during adds License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@13ffb8d488b23b1c10f376407cfe390c148cd451 --- pinning/pinner/gc/gc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index ec61f816a..df9ddedc6 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -24,7 +24,6 @@ var log = logging.Logger("gc") // deletes any block that is not found in the marked set. func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner) (<-chan key.Key, error) { unlock := bs.GCLock() - defer unlock() bsrv := bserv.New(bs, offline.Exchange(bs)) ds := dag.NewDAGService(bsrv) @@ -42,6 +41,7 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner) (<-chan key. output := make(chan key.Key) go func() { defer close(output) + defer unlock() for { select { case k, ok := <-keychan: From eadca4f99956a3f247733ab4070199e6cc499d5a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 20 Nov 2015 11:12:14 -0800 Subject: [PATCH 1094/3817] return sentinel error for invalid records License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@dc331e4c635216a289dfd5e69df182b0c3942b14 --- routing/dht/dht.go | 8 +++++--- routing/dht/routing.go | 25 +++++++++++++++---------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index c0b7970be..015b77805 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -150,6 +150,8 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, skey string) err return nil } +var errInvalidRecord = errors.New("received invalid record") + // getValueOrPeers queries a particular peer p for the value for // key. It returns either the value or a list of closer peers. // NOTE: it will update the dht's peerstore with any new addresses @@ -173,11 +175,11 @@ func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.ID, err = dht.verifyRecordOnline(ctx, record) if err != nil { log.Info("Received invalid record! (discarded)") - // still return a non-nil record to signify that we received - // a bad record from this peer + // return a sentinal to signify an invalid record was received + err = errInvalidRecord record = new(pb.Record) } - return record, peers, nil + return record, peers, err } if len(peers) > 0 { diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 0f6d50d1a..627c93607 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -171,21 +171,26 @@ func (dht *IpfsDHT) GetValues(ctx context.Context, key key.Key, nvals int) ([]ro }) rec, peers, err := dht.getValueOrPeers(ctx, p, key) - if err != nil { - if err == routing.ErrNotFound { - // in this case, they responded with nothing, - // still send a notification - notif.PublishQueryEvent(parent, ¬if.QueryEvent{ - Type: notif.PeerResponse, - ID: p, - }) - } + switch err { + case routing.ErrNotFound: + // in this case, they responded with nothing, + // still send a notification so listeners can know the + // request has completed 'successfully' + notif.PublishQueryEvent(parent, ¬if.QueryEvent{ + Type: notif.PeerResponse, + ID: p, + }) + return nil, err + default: return nil, err + + case nil, errInvalidRecord: + // in either of these cases, we want to keep going } res := &dhtQueryResult{closerPeers: peers} - if rec.GetValue() != nil { + if rec.GetValue() != nil || err == errInvalidRecord { rv := routing.RecvdVal{ Val: rec.GetValue(), From: p, From 87ef7c12bb1430ef3ba7a665cc2aad78b56ea1c5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 2 Jan 2016 13:26:33 -0800 Subject: [PATCH 1095/3817] fix shared node reference issue License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@d004e9fa8df6f3a14ce7beb042bbc8430a5052c2 --- mfs/dir.go | 6 ++--- mfs/file.go | 9 +++++--- mfs/mfs_test.go | 60 +++++++++++++++++++++++++++++++++++++++++++++++-- mfs/ops.go | 10 ++++----- 4 files changed, 72 insertions(+), 13 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index b714cb093..649bcb88d 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -258,9 +258,9 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { d.lock.Lock() defer d.lock.Unlock() - _, err := d.childDir(name) + child, err := d.childDir(name) if err == nil { - return nil, os.ErrExist + return child, os.ErrExist } _, err = d.childFile(name) if err == nil { @@ -395,7 +395,7 @@ func (d *Directory) GetNode() (*dag.Node, error) { return nil, err } - return d.node, nil + return d.node.Copy(), nil } func (d *Directory) Lock() { diff --git a/mfs/file.go b/mfs/file.go index 8539a253f..15aecb805 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -65,6 +65,7 @@ func (fi *File) Close() error { if fi.hasChanges { err := fi.mod.Sync() if err != nil { + fi.Unlock() return err } @@ -74,6 +75,7 @@ func (fi *File) Close() error { // it will manage the lock for us return fi.flushUp() } + fi.Unlock() return nil } @@ -93,12 +95,13 @@ func (fi *File) flushUp() error { return err } - name := fi.name - parent := fi.parent + //name := fi.name + //parent := fi.parent // explicit unlock *only* before closeChild call fi.Unlock() - return parent.closeChild(name, nd) + return nil + //return parent.closeChild(name, nd) } // Sync flushes the changes in the file to disk diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 65e1e1a84..ff6c9d03c 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -576,10 +576,56 @@ func actorRemoveFile(d *Directory) error { return d.Unlink(re.Name) } +func actorReadFile(d *Directory) error { + d, err := randomWalk(d, rand.Intn(6)) + if err != nil { + return err + } + + ents, err := d.List() + if err != nil { + return err + } + + var files []string + for _, e := range ents { + if e.Type == int(TFile) { + files = append(files, e.Name) + } + } + + if len(files) == 0 { + return nil + } + + fname := files[rand.Intn(len(files))] + fsn, err := d.Child(fname) + if err != nil { + return err + } + + fi, ok := fsn.(*File) + if !ok { + return errors.New("file wasnt a file, race?") + } + + _, err = fi.Size() + if err != nil { + return err + } + + _, err = ioutil.ReadAll(fi) + if err != nil { + return err + } + + return fi.Close() +} + func testActor(rt *Root, iterations int, errs chan error) { d := rt.GetValue().(*Directory) for i := 0; i < iterations; i++ { - switch rand.Intn(4) { + switch rand.Intn(5) { case 0: if err := actorMkdir(d); err != nil { errs <- err @@ -591,10 +637,20 @@ func testActor(rt *Root, iterations int, errs chan error) { return } case 3: + continue + // randomly deleting things + // doesnt really give us any sort of useful test results. + // you will never have this in a real environment where + // you expect anything productive to happen... if err := actorRemoveFile(d); err != nil { errs <- err return } + case 4: + if err := actorReadFile(d); err != nil { + errs <- err + return + } } } errs <- nil @@ -605,7 +661,7 @@ func TestMfsStress(t *testing.T) { defer cancel() _, rt := setupRoot(ctx, t) - numroutines := 2 + numroutines := 10 errs := make(chan error) for i := 0; i < numroutines; i++ { diff --git a/mfs/ops.go b/mfs/ops.go index d21f71770..75c5d6a84 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -99,8 +99,8 @@ func PutNode(r *Root, path string, nd *dag.Node) error { } // Mkdir creates a directory at 'path' under the directory 'd', creating -// intermediary directories as needed if 'parents' is set to true -func Mkdir(r *Root, pth string, parents bool, flush bool) error { +// intermediary directories as needed if 'mkparents' is set to true +func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { if pth == "" { return nil } @@ -116,7 +116,7 @@ func Mkdir(r *Root, pth string, parents bool, flush bool) error { if len(parts) == 0 { // this will only happen on 'mkdir /' - if parents { + if mkparents { return nil } return fmt.Errorf("cannot create directory '/': Already exists") @@ -125,7 +125,7 @@ func Mkdir(r *Root, pth string, parents bool, flush bool) error { cur := r.GetValue().(*Directory) for i, d := range parts[:len(parts)-1] { fsn, err := cur.Child(d) - if err == os.ErrNotExist && parents { + if err == os.ErrNotExist && mkparents { mkd, err := cur.Mkdir(d) if err != nil { return err @@ -144,7 +144,7 @@ func Mkdir(r *Root, pth string, parents bool, flush bool) error { final, err := cur.Mkdir(parts[len(parts)-1]) if err != nil { - if !parents || err != os.ErrExist { + if !mkparents || err != os.ErrExist || final == nil { return err } } From 722fb22e34337c93102b1ca101bf28836dabee57 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 4 Dec 2015 17:44:08 -0800 Subject: [PATCH 1096/3817] slight cleanup License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@36c9cf5bf3471d2fcbbd39e5b8e7f41195f7f012 --- ipld/merkledag/merkledag.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 0486e3321..b84327dfd 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -3,7 +3,6 @@ package merkledag import ( "fmt" - "time" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" @@ -49,14 +48,6 @@ func (n *dagService) Add(nd *Node) (key.Key, error) { if n == nil { // FIXME remove this assertion. protect with constructor invariant return "", fmt.Errorf("dagService is nil") } - /* - start := time.Now() - defer func() { - took := time.Now().Sub(start) - log.Error("add took: %s", took) - }() - */ - _ = time.Saturday d, err := nd.Encoded(false) if err != nil { From 546cfe3e05d1804017da36b696fc162103503799 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 5 Dec 2015 20:31:25 -0800 Subject: [PATCH 1097/3817] Allow for gc during adds License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@05437d567d4c32daa9f78e53dcb5bef3d58a0dcd --- blockstore/blockstore.go | 15 ++++++++++++++- blockstore/write_cache.go | 4 ++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index bc000df93..59f0f2c72 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -5,6 +5,7 @@ package blockstore import ( "errors" "sync" + "sync/atomic" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" dsns "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" @@ -49,6 +50,10 @@ type GCBlockstore interface { // at the same time, but no GC should not happen simulatenously. // Reading during Pinning is safe, and requires no lock. PinLock() func() + + // GcRequested returns true if GCLock has been called and is waiting to + // take the lock + GCRequested() bool } func NewBlockstore(d ds.Batching) *blockstore { @@ -63,7 +68,9 @@ func NewBlockstore(d ds.Batching) *blockstore { type blockstore struct { datastore ds.Batching - lk sync.RWMutex + lk sync.RWMutex + gcreq int32 + gcreqlk sync.Mutex } func (bs *blockstore) Get(k key.Key) (*blocks.Block, error) { @@ -192,7 +199,9 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { } func (bs *blockstore) GCLock() func() { + atomic.AddInt32(&bs.gcreq, 1) bs.lk.Lock() + atomic.AddInt32(&bs.gcreq, -1) return bs.lk.Unlock } @@ -200,3 +209,7 @@ func (bs *blockstore) PinLock() func() { bs.lk.RLock() return bs.lk.RUnlock } + +func (bs *blockstore) GCRequested() bool { + return atomic.LoadInt32(&bs.gcreq) > 0 +} diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index 52af696e4..73a7813f5 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -66,3 +66,7 @@ func (w *writecache) GCLock() func() { func (w *writecache) PinLock() func() { return w.blockstore.(GCBlockstore).PinLock() } + +func (w *writecache) GCRequested() bool { + return w.blockstore.(GCBlockstore).GCRequested() +} From 636da38b157e991293639f11fe96525f72774b3a Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 15 Nov 2015 18:50:05 +0700 Subject: [PATCH 1098/3817] Remove chunk channels License: MIT Signed-off-by: rht This commit was moved from ipfs/go-unixfs@222445c94734cc698b0e817ceeaa1d415106a7bb --- unixfs/mod/dagmodifier.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index aa4de8caf..197e330a9 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -103,8 +103,7 @@ func (zr zeroReader) Read(b []byte) (int, error) { func (dm *DagModifier) expandSparse(size int64) error { r := io.LimitReader(zeroReader{}, size) spl := chunk.NewSizeSplitter(r, 4096) - blks, errs := chunk.Chan(spl) - nnode, err := dm.appendData(dm.curNode, blks, errs) + nnode, err := dm.appendData(dm.curNode, spl) if err != nil { return err } @@ -191,8 +190,7 @@ func (dm *DagModifier) Sync() error { // need to write past end of current dag if !done { - blks, errs := chunk.Chan(dm.splitter(dm.wrBuf)) - nd, err = dm.appendData(dm.curNode, blks, errs) + nd, err = dm.appendData(dm.curNode, dm.splitter(dm.wrBuf)) if err != nil { return err } @@ -286,13 +284,13 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) } // appendData appends the blocks from the given chan to the end of this dag -func (dm *DagModifier) appendData(node *mdag.Node, blks <-chan []byte, errs <-chan error) (*mdag.Node, error) { +func (dm *DagModifier) appendData(node *mdag.Node, spl chunk.Splitter) (*mdag.Node, error) { dbp := &help.DagBuilderParams{ Dagserv: dm.dagserv, Maxlinks: help.DefaultLinksPerBlock, } - return trickle.TrickleAppend(dm.ctx, node, dbp.New(blks, errs)) + return trickle.TrickleAppend(dm.ctx, node, dbp.New(spl)) } // Read data from this dag starting at the current offset From 3c23819650308e75105898b8a9697ef0ed8c6aad Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 2 Jan 2016 17:56:42 -0800 Subject: [PATCH 1099/3817] vendor in new go-datastore License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@a70bed6d2168493510593987506475d1ddfb55bf --- namesys/namesys.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/resolve_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 4c9868b57..6dea9864e 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -4,7 +4,7 @@ import ( "strings" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ci "github.com/ipfs/go-ipfs/p2p/crypto" path "github.com/ipfs/go-ipfs/path" diff --git a/namesys/publisher.go b/namesys/publisher.go index 78d7bb37c..1197d7217 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,7 +7,7 @@ import ( "time" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index b633f454c..11b47d0f1 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -14,7 +14,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" gpctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 11145ff01..219efda0f 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" From 1cc66466500b7a48af5e4cecc070f3a446a14c9a Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 24 Nov 2015 13:59:34 +0700 Subject: [PATCH 1100/3817] strings.Split -> path.SplitList License: MIT Signed-off-by: rht This commit was moved from ipfs/go-path@4db917b82da5715df10d32eced55a3068806ff77 --- path/path.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/path/path.go b/path/path.go index b6aa187b9..6f14f9016 100644 --- a/path/path.go +++ b/path/path.go @@ -106,3 +106,7 @@ func (p *Path) IsValid() error { func Join(pths []string) string { return strings.Join(pths, "/") } + +func SplitList(pth string) []string { + return strings.Split(pth, "/") +} From 94f9bdde45acd92fb85a05cd878eb4f29ea8b537 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 2 Jan 2016 17:56:42 -0800 Subject: [PATCH 1101/3817] vendor in new go-datastore License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@c6b8993011c9b2c258925fe10cd7c7d8aba1f7b1 --- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 41d97a142..4cb2b2c68 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -7,7 +7,7 @@ import ( "sync" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blocks/set" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 818a414ab..9356d3101 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -6,8 +6,8 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" bs "github.com/ipfs/go-ipfs/blockservice" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index a48744939..b076c4146 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -4,8 +4,8 @@ import ( "testing" "testing/quick" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/key" From 82885735d3320ff349eaf513f76a35057ddf6153 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 2 Jan 2016 17:56:42 -0800 Subject: [PATCH 1102/3817] vendor in new go-datastore License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@2b2d7ad3258acbb95bf381180549611e57739de6 --- routing/dht/dht.go | 2 +- routing/dht/dht_test.go | 4 ++-- routing/dht/ext_test.go | 4 ++-- routing/dht/handlers.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/dht.go | 4 ++-- routing/mock/interface.go | 2 +- routing/offline/offline.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 015b77805..31979aa8b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -21,7 +21,7 @@ import ( logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index c09871610..32560c59f 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 710a9afca..a770a0962 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -8,8 +8,8 @@ import ( "time" ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 6fa4d3f9b..121f7623b 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -6,7 +6,7 @@ import ( "time" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index e7aa44968..f360f9a8a 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -5,7 +5,7 @@ import ( "time" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index a62f64f8d..075750c3a 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -5,7 +5,7 @@ import ( "sync" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" diff --git a/routing/mock/dht.go b/routing/mock/dht.go index df8d7cdfc..fc3b876e7 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -1,8 +1,8 @@ package mockrouting import ( - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" mocknet "github.com/ipfs/go-ipfs/p2p/net/mock" dht "github.com/ipfs/go-ipfs/routing/dht" diff --git a/routing/mock/interface.go b/routing/mock/interface.go index f18e387d8..b16b99046 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -5,7 +5,7 @@ package mockrouting import ( - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" peer "github.com/ipfs/go-ipfs/p2p/peer" diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 54f2bb87f..83775566c 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -5,7 +5,7 @@ import ( "time" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" ci "github.com/ipfs/go-ipfs/p2p/crypto" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index ab82ab5f1..32a69ead5 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -5,7 +5,7 @@ import ( "fmt" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" - datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index d8ea8ea4e..ea3ead0c2 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,7 +3,7 @@ package supernode import ( "testing" - datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" ) From 9f2e80098ad3396e2b9848d239f0385237b375da Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 2 Jan 2016 17:56:42 -0800 Subject: [PATCH 1103/3817] vendor in new go-datastore License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@9af99e6a443adf8beb10a31990c44079e3f23d28 --- blockservice/test/blocks_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 6ba5eb40f..8b56753ad 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" From d5edab62ed63b8425a2f24bb3082a342dc056696 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 11 Jan 2016 04:15:25 -0800 Subject: [PATCH 1104/3817] a small amount of cleanup in mfs dir License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@b712a6fe7e95f9965aa3d37d29a3dcfe8cdefd15 --- mfs/dir.go | 68 +++++++++++++----------------------------------------- 1 file changed, 16 insertions(+), 52 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 649bcb88d..15b4ea777 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -101,45 +101,6 @@ func (d *Directory) Type() NodeType { return TDir } -// childFile returns a file under this directory by the given name if it exists -func (d *Directory) childFile(name string) (*File, error) { - fi, ok := d.files[name] - if ok { - return fi, nil - } - - fsn, err := d.childNode(name) - if err != nil { - return nil, err - } - - if fi, ok := fsn.(*File); ok { - return fi, nil - } - - return nil, fmt.Errorf("%s is not a file", name) -} - -// childDir returns a directory under this directory by the given name if it -// exists. -func (d *Directory) childDir(name string) (*Directory, error) { - dir, ok := d.childDirs[name] - if ok { - return dir, nil - } - - fsn, err := d.childNode(name) - if err != nil { - return nil, err - } - - if dir, ok := fsn.(*Directory); ok { - return dir, nil - } - - return nil, fmt.Errorf("%s is not a directory", name) -} - // childNode returns a FSNode under this directory by the given name if it exists. // it does *not* check the cached dirs and files func (d *Directory) childNode(name string) (FSNode, error) { @@ -172,6 +133,13 @@ func (d *Directory) childNode(name string) (FSNode, error) { } } +// Child returns the child of this directory by the given name +func (d *Directory) Child(name string) (FSNode, error) { + d.lock.Lock() + defer d.lock.Unlock() + return d.childUnsync(name) +} + // childFromDag searches through this directories dag node for a child link // with the given name func (d *Directory) childFromDag(name string) (*dag.Node, error) { @@ -184,13 +152,6 @@ func (d *Directory) childFromDag(name string) (*dag.Node, error) { return nil, os.ErrNotExist } -// Child returns the child of this directory by the given name -func (d *Directory) Child(name string) (FSNode, error) { - d.lock.Lock() - defer d.lock.Unlock() - return d.childUnsync(name) -} - // childUnsync returns the child under this directory by the given name // without locking, useful for operations which already hold a lock func (d *Directory) childUnsync(name string) (FSNode, error) { @@ -258,13 +219,16 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { d.lock.Lock() defer d.lock.Unlock() - child, err := d.childDir(name) - if err == nil { - return child, os.ErrExist - } - _, err = d.childFile(name) + fsn, err := d.childUnsync(name) if err == nil { - return nil, os.ErrExist + switch fsn := fsn.(type) { + case *Directory: + return fsn, os.ErrExist + case *File: + return nil, os.ErrExist + default: + return nil, fmt.Errorf("unrecognized type: %#v", fsn) + } } ndir := &dag.Node{Data: ft.FolderPBData()} From 419d5b094f77403dabc8443154d20d58ecc1e463 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 2 Jan 2016 17:56:42 -0800 Subject: [PATCH 1105/3817] vendor in new go-datastore License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@3e9025f65404683bc714fdfe8fc1f478516fc628 --- ipld/merkledag/merkledag_test.go | 4 ++-- ipld/merkledag/test/utils.go | 4 ++-- ipld/merkledag/utils/utils.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 28ec79343..1df3182d4 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -10,8 +10,8 @@ import ( "sync" "testing" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index 066516e52..1e96569ad 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -1,8 +1,8 @@ package mdutils import ( - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks/blockstore" bsrv "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 97e2ebb4e..3536e35cc 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -3,8 +3,8 @@ package dagutils import ( "errors" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - syncds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + syncds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" From 12a27d8c16d7df59de55a3ab7f07197bb687b5a7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 2 Jan 2016 17:56:42 -0800 Subject: [PATCH 1106/3817] vendor in new go-datastore License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@00b55617867f5655b3fcf54a080f6d6ce43de5d6 --- exchange/offline/offline_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index dc0071606..0a4787f07 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -3,8 +3,8 @@ package offline import ( "testing" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - ds_sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + ds_sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" From a448b2d693e60c2e302374ecee47096f2a202aa7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 2 Jan 2016 17:56:42 -0800 Subject: [PATCH 1107/3817] vendor in new go-datastore License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@a2b4f8b19f5c3568878f18197b14ab422435b2bd --- blockstore/blockstore.go | 6 +++--- blockstore/blockstore_test.go | 6 +++--- blockstore/write_cache_test.go | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 59f0f2c72..342bbc72d 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -7,9 +7,9 @@ import ( "sync" "sync/atomic" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dsns "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace" - dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + dsns "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/namespace" + dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/query" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 934c7933e..9c535b9d8 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -5,9 +5,9 @@ import ( "fmt" "testing" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" - ds_sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/query" + ds_sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" blocks "github.com/ipfs/go-ipfs/blocks" diff --git a/blockstore/write_cache_test.go b/blockstore/write_cache_test.go index a51d2f7c6..97bf86b12 100644 --- a/blockstore/write_cache_test.go +++ b/blockstore/write_cache_test.go @@ -3,9 +3,9 @@ package blockstore import ( "testing" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" - dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query" - syncds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/query" + syncds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks" ) From eb213d26d470419e3dd9ec65ac24180624f9a9c5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 2 Jan 2016 17:56:42 -0800 Subject: [PATCH 1108/3817] vendor in new go-datastore License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@02b3c19108b43f177000cd8d08db69b02176c0e2 --- unixfs/mod/dagmodifier_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index f3341690c..16f7dca33 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -7,7 +7,7 @@ import ( "os" "testing" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync" + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks/blockstore" bs "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" @@ -20,7 +20,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" u "github.com/ipfs/go-ipfs/util" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" + ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" ) From 66d84e35733c0248ed365b68a32492d4d77dbfda Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 13 Jan 2016 11:03:09 -0800 Subject: [PATCH 1109/3817] do resolve operations concurrently License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@a1e80e7e955284328ad2f46d44f57b8a83a7225b --- namesys/routing.go | 51 +++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/namesys/routing.go b/namesys/routing.go index 5f9e3bc87..a288f7557 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -11,6 +11,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/namesys/pb" + ci "github.com/ipfs/go-ipfs/p2p/crypto" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" @@ -123,32 +124,50 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa hash, err := mh.FromB58String(name) if err != nil { + // name should be a multihash. if it isn't, error out here. log.Warningf("RoutingResolve: bad input hash: [%s]\n", name) return "", err } - // name should be a multihash. if it isn't, error out here. // use the routing system to get the name. // /ipns/ h := []byte("/ipns/" + string(hash)) - ipnsKey := key.Key(h) - val, err := r.routing.GetValue(ctx, ipnsKey) - if err != nil { - log.Warning("RoutingResolve get failed.") - return "", err - } + var entry *pb.IpnsEntry + var pubkey ci.PubKey - entry := new(pb.IpnsEntry) - err = proto.Unmarshal(val, entry) - if err != nil { - return "", err - } + resp := make(chan error, 2) + go func() { + ipnsKey := key.Key(h) + val, err := r.routing.GetValue(ctx, ipnsKey) + if err != nil { + log.Warning("RoutingResolve get failed.") + resp <- err + } - // name should be a public key retrievable from ipfs - pubkey, err := routing.GetPublicKey(r.routing, ctx, hash) - if err != nil { - return "", err + entry = new(pb.IpnsEntry) + err = proto.Unmarshal(val, entry) + if err != nil { + resp <- err + } + resp <- nil + }() + + go func() { + // name should be a public key retrievable from ipfs + pubk, err := routing.GetPublicKey(r.routing, ctx, hash) + if err != nil { + resp <- err + } + pubkey = pubk + resp <- nil + }() + + for i := 0; i < 2; i++ { + err = <-resp + if err != nil { + return "", err + } } hsh, _ := pubkey.Hash() From f2885663bc60f2e120ecae96353d1f0bf20cac1f Mon Sep 17 00:00:00 2001 From: Stephen Whitmore Date: Sat, 16 Jan 2016 03:10:28 +0100 Subject: [PATCH 1110/3817] Implements path.IsJustAKey(). License: MIT Signed-off-by: Stephen Whitmore This commit was moved from ipfs/go-path@1de11497ec351d161ec064f9ea349608895fe653 --- path/path.go | 6 ++++++ path/path_test.go | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/path/path.go b/path/path.go index 6f14f9016..dc2d5d1de 100644 --- a/path/path.go +++ b/path/path.go @@ -44,6 +44,12 @@ func (p Path) String() string { return string(p) } +// IsJustAKey returns true if the path is of the form or /ipfs/. +func (p Path) IsJustAKey() bool { + parts := p.Segments() + return (len(parts) == 2 && parts[0] == "ipfs") +} + func FromSegments(prefix string, seg ...string) (Path, error) { return ParsePath(prefix + strings.Join(seg, "/")) } diff --git a/path/path_test.go b/path/path_test.go index f800e19e7..464cd419a 100644 --- a/path/path_test.go +++ b/path/path_test.go @@ -28,3 +28,23 @@ func TestPathParsing(t *testing.T) { } } } + +func TestIsJustAKey(t *testing.T) { + cases := map[string]bool{ + "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true, + "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true, + "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": false, + "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b": false, + } + + for p, expected := range cases { + path, err := ParsePath(p) + if err != nil { + t.Fatalf("ParsePath failed to parse \"%s\", but should have succeeded", p) + } + result := path.IsJustAKey() + if result != expected { + t.Fatalf("expected IsJustAKey(%s) to return %v, not %v", p, expected, result) + } + } +} From f06f1e8883a0ac8a3754bcdfe0c146f0d73e350b Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 16 Jan 2016 14:37:04 +0100 Subject: [PATCH 1111/3817] pin/pin: replace isPinned() with isPinnedWithType() It is more generic to be able to pass a pin type argument. License: MIT Signed-off-by: Christian Couder This commit was moved from ipfs/go-ipfs-pinner@cd06a2f13a7ae9e1cfc9509b3d4703e0faf6571d --- pinning/pinner/pin.go | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 4cb2b2c68..86b0d58da 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -36,6 +36,7 @@ const ( type Pinner interface { IsPinned(key.Key) (string, bool, error) + IsPinnedWithType(key.Key, string) (string, bool, error) Pin(context.Context, *mdag.Node, bool) error Unpin(context.Context, key.Key, bool) error @@ -126,7 +127,7 @@ func (p *pinner) Pin(ctx context.Context, node *mdag.Node, recurse bool) error { func (p *pinner) Unpin(ctx context.Context, k key.Key, recursive bool) error { p.lock.Lock() defer p.lock.Unlock() - reason, pinned, err := p.isPinned(k) + reason, pinned, err := p.isPinnedWithType(k, "all") if err != nil { return err } @@ -159,22 +160,46 @@ func (p *pinner) isInternalPin(key key.Key) bool { func (p *pinner) IsPinned(k key.Key) (string, bool, error) { p.lock.RLock() defer p.lock.RUnlock() - return p.isPinned(k) + return p.isPinnedWithType(k, "all") } -// isPinned is the implementation of IsPinned that does not lock. +func (p *pinner) IsPinnedWithType(k key.Key, typeStr string) (string, bool, error) { + p.lock.RLock() + defer p.lock.RUnlock() + return p.isPinnedWithType(k, typeStr) +} + +// isPinnedWithType is the implementation of IsPinnedWithType that does not lock. // intended for use by other pinned methods that already take locks -func (p *pinner) isPinned(k key.Key) (string, bool, error) { - if p.recursePin.HasKey(k) { +func (p *pinner) isPinnedWithType(k key.Key, typeStr string) (string, bool, error) { + switch typeStr { + case "all", "direct", "indirect", "recursive", "internal": + default: + err := fmt.Errorf("Invalid type '%s', must be one of {direct, indirect, recursive, internal, all}", typeStr) + return "", false, err + } + if (typeStr == "recursive" || typeStr == "all") && p.recursePin.HasKey(k) { return "recursive", true, nil } - if p.directPin.HasKey(k) { + if typeStr == "recursive" { + return "", false, nil + } + + if (typeStr == "direct" || typeStr == "all") && p.directPin.HasKey(k) { return "direct", true, nil } - if p.isInternalPin(k) { + if typeStr == "direct" { + return "", false, nil + } + + if (typeStr == "internal" || typeStr == "all") && p.isInternalPin(k) { return "internal", true, nil } + if typeStr == "internal" { + return "", false, nil + } + // Default is "indirect" for _, rk := range p.recursePin.GetKeys() { rnd, err := p.dserv.Get(context.Background(), rk) if err != nil { From 5ce8b9402afe50fca8f8e423c309a2da35febd02 Mon Sep 17 00:00:00 2001 From: Stephen Whitmore Date: Thu, 21 Jan 2016 16:38:16 +0100 Subject: [PATCH 1112/3817] wip License: MIT Signed-off-by: Stephen Whitmore This commit was moved from ipfs/go-path@376fb073f33f409f2755f8736b35c2c54e44024d --- path/path_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/path/path_test.go b/path/path_test.go index 464cd419a..33b93a3a0 100644 --- a/path/path_test.go +++ b/path/path_test.go @@ -35,6 +35,7 @@ func TestIsJustAKey(t *testing.T) { "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true, "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": false, "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b": false, + "/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": false, } for p, expected := range cases { From 6213c349215b2ab5c7f80c11e1c85da2da1947cc Mon Sep 17 00:00:00 2001 From: Stephen Whitmore Date: Sun, 24 Jan 2016 23:29:41 -0800 Subject: [PATCH 1113/3817] Implements Path.PopLastSegment(). This allows a path (/ipfs/foo/bar) to be separated between its head (/ipfs/foo) and its tail (bar). License: MIT Signed-off-by: Stephen Whitmore This commit was moved from ipfs/go-path@b38fdfea328e74fc610275f2e09c3ab4a3db25a2 --- path/path.go | 18 ++++++++++++++++++ path/path_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/path/path.go b/path/path.go index dc2d5d1de..0891e8466 100644 --- a/path/path.go +++ b/path/path.go @@ -50,6 +50,24 @@ func (p Path) IsJustAKey() bool { return (len(parts) == 2 && parts[0] == "ipfs") } +// PopLastSegment returns a new Path without its final segment, and the final +// segment, separately. If there is no more to pop (the path is just a key), +// the original path is returned. +func (p Path) PopLastSegment() (Path, string, error) { + + if p.IsJustAKey() { + return p, "", nil + } + + segs := p.Segments() + newPath, err := ParsePath("/" + strings.Join(segs[:len(segs)-1], "/")) + if err != nil { + return "", "", err + } + + return newPath, segs[len(segs)-1], nil +} + func FromSegments(prefix string, seg ...string) (Path, error) { return ParsePath(prefix + strings.Join(seg, "/")) } diff --git a/path/path_test.go b/path/path_test.go index 33b93a3a0..a718bd81f 100644 --- a/path/path_test.go +++ b/path/path_test.go @@ -49,3 +49,31 @@ func TestIsJustAKey(t *testing.T) { } } } + +func TestPopLastSegment(t *testing.T) { + cases := map[string][]string{ + "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": []string{"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n", ""}, + "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": []string{"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n", ""}, + "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": []string{"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n", "a"}, + "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b": []string{"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a", "b"}, + "/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y/z": []string{"/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y", "z"}, + } + + for p, expected := range cases { + path, err := ParsePath(p) + if err != nil { + t.Fatalf("ParsePath failed to parse \"%s\", but should have succeeded", p) + } + head, tail, err := path.PopLastSegment() + if err != nil { + t.Fatalf("PopLastSegment failed, but should have succeeded: %s", err) + } + headStr := head.String() + if headStr != expected[0] { + t.Fatalf("expected head of PopLastSegment(%s) to return %v, not %v", p, expected[0], headStr) + } + if tail != expected[1] { + t.Fatalf("expected tail of PopLastSegment(%s) to return %v, not %v", p, expected[1], tail) + } + } +} From 1dc3403c0dfd04e8e86ab085f6eadaf983499058 Mon Sep 17 00:00:00 2001 From: Kubuxu Date: Mon, 11 Jan 2016 16:01:09 +0100 Subject: [PATCH 1114/3817] Make dns resolve paths under _dnslink. Thus allowing to CNAME main site entry to gateway and stil specify dnslink. License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@df674f0d508fa9446cffb7b2ac476f0ca0502db5 --- namesys/dns.go | 63 ++++++++++++++++++++++++++++++++++++++------- namesys/dns_test.go | 28 ++++++++++++++++++++ 2 files changed, 81 insertions(+), 10 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 96147534a..a02a73ad8 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -41,33 +41,76 @@ func (r *DNSResolver) ResolveN(ctx context.Context, name string, depth int) (pat return resolve(ctx, r, name, depth, "/ipns/") } +type lookupRes struct { + path path.Path + error error +} + // resolveOnce implements resolver. // TXT records for a given domain name should contain a b58 // encoded multihash. func (r *DNSResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) { segments := strings.SplitN(name, "/", 2) + domain := segments[0] - if !isd.IsDomain(segments[0]) { + if !isd.IsDomain(domain) { return "", errors.New("not a valid domain name") } + log.Infof("DNSResolver resolving %s", domain) + + rootChan := make(chan lookupRes, 1) + go workDomain(r, domain, rootChan) + + subChan := make(chan lookupRes, 1) + go workDomain(r, "_dnslink."+domain, subChan) + + var subRes lookupRes + select { + case subRes = <-subChan: + case <-ctx.Done(): + return "", ctx.Err() + } + + var p path.Path + if subRes.error == nil { + p = subRes.path + } else { + var rootRes lookupRes + select { + case rootRes = <-rootChan: + case <-ctx.Done(): + return "", ctx.Err() + } + if rootRes.error == nil { + p = rootRes.path + } else { + return "", ErrResolveFailed + } + } + if len(segments) > 1 { + return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[1]) + } else { + return p, nil + } +} + +func workDomain(r *DNSResolver, name string, res chan lookupRes) { + txt, err := r.lookupTXT(name) - log.Infof("DNSResolver resolving %s", segments[0]) - txt, err := r.lookupTXT(segments[0]) if err != nil { - return "", err + // Error is != nil + res <- lookupRes{"", err} + return } for _, t := range txt { p, err := parseEntry(t) if err == nil { - if len(segments) > 1 { - return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[1]) - } - return p, nil + res <- lookupRes{p, nil} + return } } - - return "", ErrResolveFailed + res <- lookupRes{"", ErrResolveFailed} } func parseEntry(txt string) (path.Path, error) { diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 27b3883db..9b11845ac 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -65,6 +65,9 @@ func newMockDNS() *mockDNS { "ipfs.example.com": []string{ "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", }, + "_dnslink.dipfs.example.com": []string{ + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", + }, "dns1.example.com": []string{ "dnslink=/ipns/ipfs.example.com", }, @@ -85,6 +88,12 @@ func newMockDNS() *mockDNS { "loop2.example.com": []string{ "dnslink=/ipns/loop1.example.com", }, + "_dnslink.dloop1.example.com": []string{ + "dnslink=/ipns/loop2.example.com", + }, + "_dnslink.dloop2.example.com": []string{ + "dnslink=/ipns/loop1.example.com", + }, "bad.example.com": []string{ "dnslink=", }, @@ -100,6 +109,18 @@ func newMockDNS() *mockDNS { "withtrailingrec.example.com": []string{ "dnslink=/ipns/withtrailing.example.com/segment/", }, + "double.example.com": []string{ + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", + }, + "_dnslink.double.example.com": []string{ + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", + }, + "double.conflict.com": []string{ + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", + }, + "_dnslink.conflict.example.com": []string{ + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjE", + }, }, } } @@ -109,6 +130,7 @@ func TestDNSResolution(t *testing.T) { r := &DNSResolver{lookupTXT: mock.lookupTXT} testResolution(t, r, "multihash.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) testResolution(t, r, "ipfs.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "dipfs.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) testResolution(t, r, "dns1.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) testResolution(t, r, "dns1.example.com", 1, "/ipns/ipfs.example.com", ErrResolveRecursion) testResolution(t, r, "dns2.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) @@ -122,6 +144,10 @@ func TestDNSResolution(t *testing.T) { testResolution(t, r, "loop1.example.com", 2, "/ipns/loop1.example.com", ErrResolveRecursion) testResolution(t, r, "loop1.example.com", 3, "/ipns/loop2.example.com", ErrResolveRecursion) testResolution(t, r, "loop1.example.com", DefaultDepthLimit, "/ipns/loop1.example.com", ErrResolveRecursion) + testResolution(t, r, "dloop1.example.com", 1, "/ipns/loop2.example.com", ErrResolveRecursion) + testResolution(t, r, "dloop1.example.com", 2, "/ipns/loop1.example.com", ErrResolveRecursion) + testResolution(t, r, "dloop1.example.com", 3, "/ipns/loop2.example.com", ErrResolveRecursion) + testResolution(t, r, "dloop1.example.com", DefaultDepthLimit, "/ipns/loop1.example.com", ErrResolveRecursion) testResolution(t, r, "bad.example.com", DefaultDepthLimit, "", ErrResolveFailed) testResolution(t, r, "withsegment.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment", nil) testResolution(t, r, "withrecsegment.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub", nil) @@ -129,4 +155,6 @@ func TestDNSResolution(t *testing.T) { testResolution(t, r, "withrecsegment.example.com/test2", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub/test2", nil) testResolution(t, r, "withrecsegment.example.com/test3/", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub/test3/", nil) testResolution(t, r, "withtrailingrec.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/", nil) + testResolution(t, r, "double.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "conflict.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjE", nil) } From b5aa5583f8ed8b43a7a523c418261fead1c821ef Mon Sep 17 00:00:00 2001 From: rht Date: Thu, 21 Jan 2016 14:40:15 +0700 Subject: [PATCH 1115/3817] Wire ctx to getdag operations in gc.GC License: MIT Signed-off-by: rht This commit was moved from ipfs/go-ipfs-pinner@51084f5ddca1274e0f2d0c4c3d6dff76e5921649 --- pinning/pinner/gc/gc.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index df9ddedc6..5cf35fb7e 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -28,7 +28,7 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner) (<-chan key. bsrv := bserv.New(bs, offline.Exchange(bs)) ds := dag.NewDAGService(bsrv) - gcs, err := ColoredSet(pn, ds) + gcs, err := ColoredSet(ctx, pn, ds) if err != nil { return nil, err } @@ -69,16 +69,16 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner) (<-chan key. return output, nil } -func Descendants(ds dag.DAGService, set key.KeySet, roots []key.Key) error { +func Descendants(ctx context.Context, ds dag.DAGService, set key.KeySet, roots []key.Key) error { for _, k := range roots { set.Add(k) - nd, err := ds.Get(context.Background(), k) + nd, err := ds.Get(ctx, k) if err != nil { return err } // EnumerateChildren recursively walks the dag and adds the keys to the given set - err = dag.EnumerateChildren(context.Background(), ds, nd, set) + err = dag.EnumerateChildren(ctx, ds, nd, set) if err != nil { return err } @@ -87,11 +87,11 @@ func Descendants(ds dag.DAGService, set key.KeySet, roots []key.Key) error { return nil } -func ColoredSet(pn pin.Pinner, ds dag.DAGService) (key.KeySet, error) { +func ColoredSet(ctx context.Context, pn pin.Pinner, ds dag.DAGService) (key.KeySet, error) { // KeySet currently implemented in memory, in the future, may be bloom filter or // disk backed to conserve memory. gcs := key.NewKeySet() - err := Descendants(ds, gcs, pn.RecursiveKeys()) + err := Descendants(ctx, ds, gcs, pn.RecursiveKeys()) if err != nil { return nil, err } @@ -100,7 +100,7 @@ func ColoredSet(pn pin.Pinner, ds dag.DAGService) (key.KeySet, error) { gcs.Add(k) } - err = Descendants(ds, gcs, pn.InternalPins()) + err = Descendants(ctx, ds, gcs, pn.InternalPins()) if err != nil { return nil, err } From f0eb4aa81ce6357faa79778cb2dadbf40f6def3c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 27 Jan 2016 14:28:34 -0800 Subject: [PATCH 1116/3817] initial vendoring of libp2p outside of the repo with gx License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@20a6e66f4b5fdfd6ecc7f896aa6ff218bc0c8eb3 --- namesys/base.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 4 ++-- namesys/ipns_select_test.go | 2 +- namesys/namesys.go | 4 ++-- namesys/namesys_test.go | 2 +- namesys/proquint.go | 2 +- namesys/publisher.go | 6 +++--- namesys/republisher/repub.go | 6 +++--- namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 4 ++-- namesys/routing.go | 6 +++--- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index e552fce46..569cb4bb3 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -3,7 +3,7 @@ package namesys import ( "strings" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" path "github.com/ipfs/go-ipfs/path" ) diff --git a/namesys/dns.go b/namesys/dns.go index 96147534a..5ddd57f4e 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -6,7 +6,7 @@ import ( "strings" isd "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-is-domain" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" path "github.com/ipfs/go-ipfs/path" ) diff --git a/namesys/interface.go b/namesys/interface.go index 09c296c23..404274bfa 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -33,9 +33,9 @@ import ( "errors" "time" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - ci "github.com/ipfs/go-ipfs/p2p/crypto" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" path "github.com/ipfs/go-ipfs/path" + ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" ) const ( diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index ebd81e86d..5ce96cb14 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -9,9 +9,9 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" pb "github.com/ipfs/go-ipfs/namesys/pb" - ci "github.com/ipfs/go-ipfs/p2p/crypto" path "github.com/ipfs/go-ipfs/path" u "github.com/ipfs/go-ipfs/util" + ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" ) func shuffle(a []*pb.IpnsEntry) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 6dea9864e..a89abdcdf 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,10 +5,10 @@ import ( "time" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - ci "github.com/ipfs/go-ipfs/p2p/crypto" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" + ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 256228c3e..f44f17ef5 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" path "github.com/ipfs/go-ipfs/path" ) diff --git a/namesys/proquint.go b/namesys/proquint.go index 2ad3275a4..8dbf4a19a 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,7 +4,7 @@ import ( "errors" proquint "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/bren2010/proquint" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" path "github.com/ipfs/go-ipfs/path" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 1197d7217..aac6a9cee 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -8,13 +8,11 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" pb "github.com/ipfs/go-ipfs/namesys/pb" - ci "github.com/ipfs/go-ipfs/p2p/crypto" - peer "github.com/ipfs/go-ipfs/p2p/peer" path "github.com/ipfs/go-ipfs/path" pin "github.com/ipfs/go-ipfs/pin" routing "github.com/ipfs/go-ipfs/routing" @@ -22,6 +20,8 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" u "github.com/ipfs/go-ipfs/util" + ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 11b47d0f1..70961de8e 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -8,17 +8,17 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" namesys "github.com/ipfs/go-ipfs/namesys" pb "github.com/ipfs/go-ipfs/namesys/pb" - peer "github.com/ipfs/go-ipfs/p2p/peer" path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" gpctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 92c224a73..a4fde300f 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -6,15 +6,15 @@ import ( "time" goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" "github.com/ipfs/go-ipfs/core" mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - mocknet "github.com/ipfs/go-ipfs/p2p/net/mock" - peer "github.com/ipfs/go-ipfs/p2p/peer" path "github.com/ipfs/go-ipfs/path" + mocknet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 219efda0f..cd22eecc2 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,13 +6,13 @@ import ( "time" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" - peer "github.com/ipfs/go-ipfs/p2p/peer" path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" u "github.com/ipfs/go-ipfs/util" testutil "github.com/ipfs/go-ipfs/util/testutil" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index a288f7557..40cf658d4 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -7,15 +7,15 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" lru "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/namesys/pb" - ci "github.com/ipfs/go-ipfs/p2p/crypto" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" ) var log = logging.Logger("namesys") From 3cba9c02bb9deadd81ed8418341bf741359719eb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 27 Jan 2016 14:28:34 -0800 Subject: [PATCH 1117/3817] initial vendoring of libp2p outside of the repo with gx License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@cd393ab5952ca88a6307f3e13886b13982c0b59a --- routing/dht/dht.go | 12 ++++++------ routing/dht/dht_bootstrap.go | 4 ++-- routing/dht/dht_net.go | 10 +++++----- routing/dht/dht_test.go | 8 ++++---- routing/dht/diag.go | 2 +- routing/dht/ext_test.go | 10 +++++----- routing/dht/handlers.go | 4 ++-- routing/dht/lookup.go | 4 ++-- routing/dht/notif.go | 4 ++-- routing/dht/pb/message.go | 8 ++++---- routing/dht/providers.go | 4 ++-- routing/dht/providers_test.go | 4 ++-- routing/dht/query.go | 10 +++++----- routing/dht/records.go | 6 +++--- routing/dht/routing.go | 6 +++--- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 4 ++-- routing/kbucket/table_test.go | 2 +- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 8 ++++---- routing/mock/centralized_server.go | 4 ++-- routing/mock/centralized_test.go | 4 ++-- routing/mock/dht.go | 4 ++-- routing/mock/interface.go | 4 ++-- routing/none/none_client.go | 8 ++++---- routing/offline/offline.go | 8 ++++---- routing/record/record.go | 4 ++-- routing/record/validation.go | 2 +- routing/routing.go | 6 +++--- routing/supernode/client.go | 8 ++++---- routing/supernode/proxy/loopback.go | 6 +++--- routing/supernode/proxy/standard.go | 14 +++++++------- routing/supernode/server.go | 4 ++-- 34 files changed, 96 insertions(+), 96 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 31979aa8b..2a349652d 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -10,21 +10,21 @@ import ( "time" key "github.com/ipfs/go-ipfs/blocks/key" - ci "github.com/ipfs/go-ipfs/p2p/crypto" - host "github.com/ipfs/go-ipfs/p2p/host" - peer "github.com/ipfs/go-ipfs/p2p/peer" - protocol "github.com/ipfs/go-ipfs/p2p/protocol" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" + host "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/host" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + protocol "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/protocol" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) var log = logging.Logger("dht") diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 4a07d9f0b..d9b62c36e 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -8,13 +8,13 @@ import ( "sync" "time" - peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" periodicproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/periodic" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) // BootstrapConfig specifies parameters used bootstrapping the DHT. diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 722ece7ea..aa0499311 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,10 +6,10 @@ import ( ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - inet "github.com/ipfs/go-ipfs/p2p/net" - peer "github.com/ipfs/go-ipfs/p2p/peer" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" pb "github.com/ipfs/go-ipfs/routing/dht/pb" + inet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) // handleNewStream implements the inet.StreamHandler @@ -71,7 +71,7 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { log.Debugf("%s dht starting stream", dht.self) - s, err := dht.host.NewStream(ProtocolDHT, p) + s, err := dht.host.NewStream(ctx, ProtocolDHT, p) if err != nil { return nil, err } @@ -109,7 +109,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message func (dht *IpfsDHT) sendMessage(ctx context.Context, p peer.ID, pmes *pb.Message) error { log.Debugf("%s dht starting stream", dht.self) - s, err := dht.host.NewStream(ProtocolDHT, p) + s, err := dht.host.NewStream(ctx, ProtocolDHT, p) if err != nil { return err } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 32560c59f..b5499d0bf 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -11,15 +11,15 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" - peer "github.com/ipfs/go-ipfs/p2p/peer" - netutil "github.com/ipfs/go-ipfs/p2p/test/util" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" u "github.com/ipfs/go-ipfs/util" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + netutil "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/test/util" ci "github.com/ipfs/go-ipfs/util/testutil/ci" travisci "github.com/ipfs/go-ipfs/util/testutil/ci/travis" diff --git a/routing/dht/diag.go b/routing/dht/diag.go index a7a632c3e..3a466ed96 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - peer "github.com/ipfs/go-ipfs/p2p/peer" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) type connDiagInfo struct { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index a770a0962..75ef4800e 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -10,16 +10,16 @@ import ( ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" - inet "github.com/ipfs/go-ipfs/p2p/net" - mocknet "github.com/ipfs/go-ipfs/p2p/net/mock" - peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" u "github.com/ipfs/go-ipfs/util" + inet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) func TestGetFailures(t *testing.T) { @@ -120,7 +120,7 @@ func TestGetFailures(t *testing.T) { Record: rec, } - s, err := hosts[1].NewStream(ProtocolDHT, hosts[0].ID()) + s, err := hosts[1].NewStream(context.Background(), ProtocolDHT, hosts[0].ID()) if err != nil { t.Fatal(err) } diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 121f7623b..da122bd28 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -7,12 +7,12 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" - peer "github.com/ipfs/go-ipfs/p2p/peer" pb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" lgbl "github.com/ipfs/go-ipfs/util/eventlog/loggables" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) // The number of closer peers to send on requests. diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index dc377e8b7..01dd89964 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -1,12 +1,12 @@ package dht import ( - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" notif "github.com/ipfs/go-ipfs/notifications" - peer "github.com/ipfs/go-ipfs/p2p/peer" kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/util/peerset" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) // Required in order for proper JSON marshaling diff --git a/routing/dht/notif.go b/routing/dht/notif.go index cfe411c38..00089f00a 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -1,9 +1,9 @@ package dht import ( - ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" - inet "github.com/ipfs/go-ipfs/p2p/net" + inet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index f780b1b05..c13a5cf3a 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -1,12 +1,12 @@ package dht_pb import ( - ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" - inet "github.com/ipfs/go-ipfs/p2p/net" - peer "github.com/ipfs/go-ipfs/p2p/peer" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + inet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 17455b336..25bb967bd 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -6,9 +6,9 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" key "github.com/ipfs/go-ipfs/blocks/key" - peer "github.com/ipfs/go-ipfs/p2p/peer" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) type ProviderManager struct { diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 7e2e47d93..7b16fc807 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -4,9 +4,9 @@ import ( "testing" key "github.com/ipfs/go-ipfs/blocks/key" - peer "github.com/ipfs/go-ipfs/p2p/peer" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) func TestProviderManager(t *testing.T) { diff --git a/routing/dht/query.go b/routing/dht/query.go index d64e432ea..70765b694 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -5,17 +5,17 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" notif "github.com/ipfs/go-ipfs/notifications" - peer "github.com/ipfs/go-ipfs/p2p/peer" - queue "github.com/ipfs/go-ipfs/p2p/peer/queue" "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" pset "github.com/ipfs/go-ipfs/util/peerset" todoctr "github.com/ipfs/go-ipfs/util/todocounter" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + queue "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer/queue" process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" ctxproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) var maxQueryConcurrency = AlphaValue @@ -90,7 +90,7 @@ func newQueryRunner(q *dhtQuery) *dhtQueryRunner { ctx := ctxproc.OnClosingContext(proc) return &dhtQueryRunner{ query: q, - peersToQuery: queue.NewChanQueue(ctx, queue.NewXORDistancePQ(q.key)), + peersToQuery: queue.NewChanQueue(ctx, queue.NewXORDistancePQ(string(q.key))), peersRemaining: todoctr.NewSyncCounter(), peersSeen: pset.New(), rateLimit: make(chan struct{}, q.concurrency), diff --git a/routing/dht/records.go b/routing/dht/records.go index 49a06d557..18c17dec9 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -5,12 +5,12 @@ import ( "time" ctxfrac "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/frac" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - ci "github.com/ipfs/go-ipfs/p2p/crypto" - peer "github.com/ipfs/go-ipfs/p2p/peer" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" + ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) // MaxRecordAge specifies the maximum time that any node will hold onto a record diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 627c93607..85d4638de 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -5,16 +5,16 @@ import ( "sync" "time" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" notif "github.com/ipfs/go-ipfs/notifications" - inet "github.com/ipfs/go-ipfs/p2p/net" - peer "github.com/ipfs/go-ipfs/p2p/peer" "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/util/peerset" + inet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) // asyncQueryBuffer is the size of buffered channels in async queries. This diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 35ceed385..df8f92e9f 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "github.com/ipfs/go-ipfs/p2p/peer" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index 875b82261..0daae3b44 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "github.com/ipfs/go-ipfs/p2p/peer" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index d4cf051f3..8dd3ff3af 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,8 +7,8 @@ import ( "sync" "time" - peer "github.com/ipfs/go-ipfs/p2p/peer" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) var log = logging.Logger("table") diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index e5b01cc72..eb16167f5 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,7 +7,7 @@ import ( tu "github.com/ipfs/go-ipfs/util/testutil" - peer "github.com/ipfs/go-ipfs/p2p/peer" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index e37a70183..be477ff48 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -6,9 +6,9 @@ import ( "errors" key "github.com/ipfs/go-ipfs/blocks/key" - peer "github.com/ipfs/go-ipfs/p2p/peer" ks "github.com/ipfs/go-ipfs/routing/keyspace" u "github.com/ipfs/go-ipfs/util" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) // Returned if a routing table query returns no results. This is NOT expected diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index f360f9a8a..463c216b5 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -6,15 +6,15 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" - peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" "github.com/ipfs/go-ipfs/util/testutil" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 075750c3a..1757536ac 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -6,10 +6,10 @@ import ( "time" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" - peer "github.com/ipfs/go-ipfs/p2p/peer" "github.com/ipfs/go-ipfs/util/testutil" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index a719570aa..a71580a23 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -4,11 +4,11 @@ import ( "testing" "time" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" - peer "github.com/ipfs/go-ipfs/p2p/peer" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/util/testutil" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index fc3b876e7..716f23c1b 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - mocknet "github.com/ipfs/go-ipfs/p2p/net/mock" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/util/testutil" + mocknet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index b16b99046..6e29bad8a 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -6,12 +6,12 @@ package mockrouting import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" - peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/util/testutil" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) // Server provides mockrouting Clients diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 6d16a88bf..0caa78c45 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -3,13 +3,13 @@ package nilrouting import ( "errors" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" - p2phost "github.com/ipfs/go-ipfs/p2p/host" - peer "github.com/ipfs/go-ipfs/p2p/peer" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + p2phost "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/host" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 83775566c..88fedb9cb 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -6,14 +6,14 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" - ci "github.com/ipfs/go-ipfs/p2p/crypto" - "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" + "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) var log = logging.Logger("offlinerouting") diff --git a/routing/record/record.go b/routing/record/record.go index 944f615d0..f3868a79a 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -6,9 +6,9 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" key "github.com/ipfs/go-ipfs/blocks/key" - ci "github.com/ipfs/go-ipfs/p2p/crypto" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" ) var log = logging.Logger("routing/record") diff --git a/routing/record/validation.go b/routing/record/validation.go index a2afc0dfa..0a25c30e7 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -5,10 +5,10 @@ import ( "errors" key "github.com/ipfs/go-ipfs/blocks/key" - ci "github.com/ipfs/go-ipfs/p2p/crypto" path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" + ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" ) // ValidatorFunc is a function that is called to validate a given diff --git a/routing/routing.go b/routing/routing.go index 1c799b984..9894d9953 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -4,10 +4,10 @@ package routing import ( "errors" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" - ci "github.com/ipfs/go-ipfs/p2p/crypto" - peer "github.com/ipfs/go-ipfs/p2p/peer" + ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) // ErrNotFound is returned when a search fails to find anything diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 5b7c4a306..b0a62aae8 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -6,15 +6,15 @@ import ( "time" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" - "github.com/ipfs/go-ipfs/p2p/host" - peer "github.com/ipfs/go-ipfs/p2p/peer" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/host" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 1437b574a..80f010c4f 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,11 +2,11 @@ package proxy import ( ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - inet "github.com/ipfs/go-ipfs/p2p/net" - peer "github.com/ipfs/go-ipfs/p2p/peer" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" + inet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 279cbe7de..f89995110 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,15 +4,15 @@ import ( "errors" ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" - host "github.com/ipfs/go-ipfs/p2p/host" - inet "github.com/ipfs/go-ipfs/p2p/net" - peer "github.com/ipfs/go-ipfs/p2p/peer" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + host "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/host" + inet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) const ProtocolSNR = "/ipfs/supernoderouting" @@ -98,7 +98,7 @@ func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote pe if err = px.Host.Connect(ctx, peer.PeerInfo{ID: remote}); err != nil { return err } - s, err := px.Host.NewStream(ProtocolSNR, remote) + s, err := px.Host.NewStream(ctx, ProtocolSNR, remote) if err != nil { return err } @@ -133,7 +133,7 @@ func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote pe e.SetError(err) return nil, err } - s, err := px.Host.NewStream(ProtocolSNR, remote) + s, err := px.Host.NewStream(ctx, ProtocolSNR, remote) if err != nil { e.SetError(err) return nil, err diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 32a69ead5..f4111abad 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -6,13 +6,13 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" - peer "github.com/ipfs/go-ipfs/p2p/peer" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" + peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) // Server handles routing queries using a database backend From 0404df8cf02e3875eed43c47e3f0fafa225954fa Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 27 Jan 2016 14:28:34 -0800 Subject: [PATCH 1118/3817] initial vendoring of libp2p outside of the repo with gx License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@510524a21d88d69992502c85796e7e789d54937b --- blockservice/blockservice.go | 4 ++-- blockservice/test/blocks_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 9c5cc00e5..b9f616c05 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -6,12 +6,12 @@ package blockservice import ( "errors" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" ) var log = logging.Logger("blockservice") diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 8b56753ad..4c6dca16c 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -7,7 +7,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" blocksutil "github.com/ipfs/go-ipfs/blocks/blocksutil" From c75557f15673ee4badc0b82a28e06dd90f828c21 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 27 Jan 2016 14:28:34 -0800 Subject: [PATCH 1119/3817] initial vendoring of libp2p outside of the repo with gx License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@452278a79cb2f1b9fedfdf7547ec4ffb0e2bd935 --- unixfs/archive/archive.go | 2 +- unixfs/archive/tar/writer.go | 2 +- unixfs/io/dagreader.go | 2 +- unixfs/io/dirbuilder.go | 2 +- unixfs/mod/dagmodifier.go | 4 ++-- unixfs/mod/dagmodifier_test.go | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index 1fbd5ccd9..1ec1760f0 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -6,7 +6,7 @@ import ( "io" "path" - cxt "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + cxt "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" mdag "github.com/ipfs/go-ipfs/merkledag" tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 6536e443e..2d470b667 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -7,7 +7,7 @@ import ( "time" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" - cxt "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + cxt "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 646a69a40..647c1572e 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -8,7 +8,7 @@ import ( "os" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 6fdef9ffb..8dad9a16d 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -1,7 +1,7 @@ package io import ( - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" mdag "github.com/ipfs/go-ipfs/merkledag" diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 197e330a9..a344ff398 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -8,7 +8,7 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" chunk "github.com/ipfs/go-ipfs/importer/chunk" @@ -17,7 +17,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" ) var ErrSeekFail = errors.New("failed to seek properly") diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 16f7dca33..0cd4a2f10 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -21,7 +21,7 @@ import ( u "github.com/ipfs/go-ipfs/util" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) func getMockDagServ(t testing.TB) mdag.DAGService { From 58b614b8d386f3cca3327caeee9455dfe8526642 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 27 Jan 2016 14:28:34 -0800 Subject: [PATCH 1120/3817] initial vendoring of libp2p outside of the repo with gx License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@a91825094db96b824029d285e748f488fc381ac3 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 2 +- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index df9ddedc6..daa082f91 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 86b0d58da..cd17daba7 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -8,11 +8,11 @@ import ( "time" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 9356d3101..5dd9c45cf 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 71851af6e..9188f4d56 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -12,7 +12,7 @@ import ( "unsafe" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index b076c4146..eb796d919 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blockservice" From cb7d7473ae9e83b6d035e600cd254baf34c8d35f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 27 Jan 2016 14:28:34 -0800 Subject: [PATCH 1121/3817] initial vendoring of libp2p outside of the repo with gx License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-interface@915d7a0820a5fc4490b59b0ba198d5748f52a04f --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index 1a149ed9d..0f38f86b3 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -4,7 +4,7 @@ package exchange import ( "io" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" ) From 59b50fbc54fd74bbbcb6af6d018dff0426ff6d58 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 27 Jan 2016 14:28:34 -0800 Subject: [PATCH 1122/3817] initial vendoring of libp2p outside of the repo with gx License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@39f5dc4e02e2f3e0b3717daa3a326f3a24cbddec --- mfs/dir.go | 2 +- mfs/file.go | 2 +- mfs/mfs_test.go | 2 +- mfs/repub_test.go | 2 +- mfs/system.go | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 15b4ea777..28d9f7306 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -8,7 +8,7 @@ import ( "sync" "time" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" diff --git a/mfs/file.go b/mfs/file.go index 15aecb805..da4737140 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -7,7 +7,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" mod "github.com/ipfs/go-ipfs/unixfs/mod" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index ff6c9d03c..0cf8b639e 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,7 +14,7 @@ import ( randbo "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/dustin/randbo" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" "github.com/ipfs/go-ipfs/path" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 36db90e80..4ba7bae4f 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ci "github.com/ipfs/go-ipfs/util/testutil/ci" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index d3e705273..4b9afed7d 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -18,8 +18,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" ) var ErrNotExist = errors.New("no such rootfs") From 801f10f1ba2689206cff30c85159be4dc7fdb96b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 27 Jan 2016 14:28:34 -0800 Subject: [PATCH 1123/3817] initial vendoring of libp2p outside of the repo with gx License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@805dfd2b24a7bd529d6c012fdd3b73b107fbf8d3 --- blockstore/blockstore.go | 4 ++-- blockstore/blockstore_test.go | 2 +- blockstore/write_cache.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 342bbc72d..f7bbbc8aa 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -11,10 +11,10 @@ import ( dsns "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/namespace" dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/query" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 9c535b9d8..685745f00 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -8,7 +8,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/query" ds_sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index 73a7813f5..55ff4a1d9 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -2,7 +2,7 @@ package blockstore import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" ) From bb3bc3e9c24ea7401dfa049cca31fc1648a9b505 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 27 Jan 2016 14:28:34 -0800 Subject: [PATCH 1124/3817] initial vendoring of libp2p outside of the repo with gx License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@d5f589550a074b222d42395a4fd6ec2bd7ae466d --- ipld/merkledag/merkledag.go | 4 ++-- ipld/merkledag/merkledag_test.go | 2 +- ipld/merkledag/node.go | 2 +- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/utils/diff.go | 2 +- ipld/merkledag/utils/utils.go | 2 +- ipld/merkledag/utils/utils_test.go | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index b84327dfd..21ce1422a 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -4,11 +4,11 @@ package merkledag import ( "fmt" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" ) var log = logging.Logger("merkledag") diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 1df3182d4..58d8eadc8 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -12,7 +12,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index b644cae12..c5e1c4e33 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -3,7 +3,7 @@ package merkledag import ( "fmt" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index aa71ad2f2..d07354617 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -4,7 +4,7 @@ package traverse import ( "errors" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" mdag "github.com/ipfs/go-ipfs/merkledag" ) diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 8ee50819c..58eeef398 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -5,7 +5,7 @@ import ( "fmt" "path" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" ) diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 3536e35cc..231397fe3 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -5,7 +5,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" bserv "github.com/ipfs/go-ipfs/blockservice" diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index d4b2af5f3..b225a3dff 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -8,7 +8,7 @@ import ( mdtest "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) func TestAddLink(t *testing.T) { From 1db8396406aa6c8fd7a29bc3aaad18eb861d0edf Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 27 Jan 2016 14:28:34 -0800 Subject: [PATCH 1125/3817] initial vendoring of libp2p outside of the repo with gx License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@a15c85a271ecb9fd0d2bad356d9918bd5278425b --- path/resolver.go | 4 ++-- path/resolver_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index 4ed7b67e9..d4e68a1bf 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -7,11 +7,11 @@ import ( "time" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" merkledag "github.com/ipfs/go-ipfs/merkledag" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" ) var log = logging.Logger("path") diff --git a/path/resolver_test.go b/path/resolver_test.go index c0342fd62..9ebb3f7a9 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" merkledag "github.com/ipfs/go-ipfs/merkledag" From ec991c76e3c79bf5a02921430d201fa05bc65b2f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 27 Jan 2016 14:28:34 -0800 Subject: [PATCH 1126/3817] initial vendoring of libp2p outside of the repo with gx License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-chunker@3fdc04167ac0048296e3b3829a47e14f63d44bc6 --- chunker/splitting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index a2f1dac4f..84545cdfc 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -4,7 +4,7 @@ package chunk import ( "io" - logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" ) var log = logging.Logger("chunk") From f616658cd3a5e33062762e65013c146ca3ce0730 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 27 Jan 2016 14:28:34 -0800 Subject: [PATCH 1127/3817] initial vendoring of libp2p outside of the repo with gx License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@2fec5b54d1f9ba33ac5d57b97a8e30f744733a18 --- exchange/offline/offline.go | 2 +- exchange/offline/offline_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 9a448906e..47e555e26 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -3,7 +3,7 @@ package offline import ( - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 0a4787f07..407865703 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -5,7 +5,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" ds_sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" From 65fc69b3d7587a220c12db894db3ea529ceef152 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Jan 2016 09:43:06 -0800 Subject: [PATCH 1128/3817] go-keyspace dep from libp2p added License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@767b845830e83e06469e87bca8c2c37f5761c037 --- namesys/interface.go | 4 ++-- namesys/ipns_select_test.go | 2 +- namesys/namesys.go | 4 ++-- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 4 ++-- namesys/routing.go | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index 404274bfa..4917370b4 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -33,9 +33,9 @@ import ( "errors" "time" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) const ( diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index 5ce96cb14..856d0b4d9 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -11,7 +11,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" u "github.com/ipfs/go-ipfs/util" - ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" ) func shuffle(a []*pb.IpnsEntry) { diff --git a/namesys/namesys.go b/namesys/namesys.go index a89abdcdf..b25828909 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,10 +5,10 @@ import ( "time" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/publisher.go b/namesys/publisher.go index aac6a9cee..513a45e61 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -20,8 +20,8 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" u "github.com/ipfs/go-ipfs/util" - ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 70961de8e..cf2608651 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,7 +11,7 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index a4fde300f..4b546fa68 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + mocknet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index cd22eecc2..8b9e955f0 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,13 +6,13 @@ import ( "time" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" u "github.com/ipfs/go-ipfs/util" testutil "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index 40cf658d4..23334e48a 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -14,8 +14,8 @@ import ( path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" + ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" - ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" ) var log = logging.Logger("namesys") From 3559d16deabcef34a5a9d5a413856593edc2fef5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Jan 2016 09:43:06 -0800 Subject: [PATCH 1129/3817] go-keyspace dep from libp2p added License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@6a37a454c18b3124451a1d0b6a8dea6acbbca844 --- routing/dht/dht.go | 8 ++++---- routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 6 +++--- routing/dht/dht_test.go | 6 +++--- routing/dht/diag.go | 2 +- routing/dht/ext_test.go | 6 +++--- routing/dht/handlers.go | 4 ++-- routing/dht/lookup.go | 4 ++-- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 4 ++-- routing/dht/providers.go | 2 +- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 4 ++-- routing/dht/records.go | 6 +++--- routing/dht/routing.go | 6 +++--- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 2 +- routing/kbucket/table_test.go | 2 +- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 6 +++--- routing/mock/centralized_server.go | 4 ++-- routing/mock/centralized_test.go | 4 ++-- routing/mock/dht.go | 4 ++-- routing/mock/interface.go | 4 ++-- routing/none/none_client.go | 6 +++--- routing/offline/offline.go | 6 +++--- routing/record/record.go | 2 +- routing/record/validation.go | 2 +- routing/routing.go | 4 ++-- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 2 +- 34 files changed, 66 insertions(+), 66 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 2a349652d..8c9060a0d 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -14,11 +14,11 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" + ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" + host "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/host" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + protocol "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/protocol" logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" - ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" - host "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/host" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" - protocol "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/protocol" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index d9b62c36e..4cc49c6ad 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -10,7 +10,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" periodicproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/periodic" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index aa0499311..b2a5b669f 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,10 +6,10 @@ import ( ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) // handleNewStream implements the inet.StreamHandler diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index b5499d0bf..70d469259 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -11,15 +11,15 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" u "github.com/ipfs/go-ipfs/util" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" - netutil "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/test/util" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + netutil "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/test/util" ci "github.com/ipfs/go-ipfs/util/testutil/ci" travisci "github.com/ipfs/go-ipfs/util/testutil/ci/travis" diff --git a/routing/dht/diag.go b/routing/dht/diag.go index 3a466ed96..2a183a34e 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" ) type connDiagInfo struct { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 75ef4800e..c74dba02d 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -17,9 +17,9 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" u "github.com/ipfs/go-ipfs/util" - inet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index da122bd28..92ba7469b 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -7,12 +7,12 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" lgbl "github.com/ipfs/go-ipfs/util/eventlog/loggables" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) // The number of closer peers to send on requests. diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 01dd89964..3d8f400c9 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -1,12 +1,12 @@ package dht import ( - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" notif "github.com/ipfs/go-ipfs/notifications" kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/util/peerset" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) // Required in order for proper JSON marshaling diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 00089f00a..9df5abdee 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" - inet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net" + inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index c13a5cf3a..177134991 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,9 +4,9 @@ import ( ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" + inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" - inet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 25bb967bd..601a578ed 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 7b16fc807..1f92713e3 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -4,7 +4,7 @@ import ( "testing" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index 70765b694..c520e738d 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -9,9 +9,9 @@ import ( u "github.com/ipfs/go-ipfs/util" pset "github.com/ipfs/go-ipfs/util/peerset" todoctr "github.com/ipfs/go-ipfs/util/todocounter" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + queue "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer/queue" logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" - queue "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer/queue" process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" ctxproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" diff --git a/routing/dht/records.go b/routing/dht/records.go index 18c17dec9..82aeb66bb 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -5,12 +5,12 @@ import ( "time" ctxfrac "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/frac" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) // MaxRecordAge specifies the maximum time that any node will hold onto a record diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 85d4638de..fa099e0ca 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -5,7 +5,6 @@ import ( "sync" "time" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" notif "github.com/ipfs/go-ipfs/notifications" "github.com/ipfs/go-ipfs/routing" @@ -13,8 +12,9 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/util/peerset" - inet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) // asyncQueryBuffer is the size of buffered channels in async queries. This diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index df8f92e9f..77fd6dbfb 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index 0daae3b44..33b6a440f 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 8dd3ff3af..2386fd103 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,8 +7,8 @@ import ( "sync" "time" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) var log = logging.Logger("table") diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index eb16167f5..1c9db10a1 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,7 +7,7 @@ import ( tu "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index be477ff48..a0c634781 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" u "github.com/ipfs/go-ipfs/util" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" ) // Returned if a routing table query returns no results. This is NOT expected diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 463c216b5..1a572e303 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -6,15 +6,15 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" "github.com/ipfs/go-ipfs/util/testutil" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 1757536ac..3f05d5432 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -6,10 +6,10 @@ import ( "time" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index a71580a23..69df8bbc8 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -4,11 +4,11 @@ import ( "testing" "time" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 716f23c1b..66a9d169a 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/util/testutil" - mocknet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net/mock" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 6e29bad8a..6fc9fa910 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -6,12 +6,12 @@ package mockrouting import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) // Server provides mockrouting Clients diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 0caa78c45..172daf90a 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -3,13 +3,13 @@ package nilrouting import ( "errors" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" + p2phost "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/host" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" - p2phost "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/host" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 88fedb9cb..20f770d16 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -6,14 +6,14 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" + ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" + "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" - ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" - "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) var log = logging.Logger("offlinerouting") diff --git a/routing/record/record.go b/routing/record/record.go index f3868a79a..2d424b276 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" + ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" - ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" ) var log = logging.Logger("routing/record") diff --git a/routing/record/validation.go b/routing/record/validation.go index 0a25c30e7..2179d1b2d 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" - ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" ) // ValidatorFunc is a function that is called to validate a given diff --git a/routing/routing.go b/routing/routing.go index 9894d9953..80d6e3236 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -5,8 +5,8 @@ import ( "errors" key "github.com/ipfs/go-ipfs/blocks/key" - ci "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index b0a62aae8..90f4744c5 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -12,9 +12,9 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" + "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/host" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" - "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/host" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 80f010c4f..2600a6a6a 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index f89995110..3b57ad625 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -9,10 +9,10 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" + host "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/host" + inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" - host "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/host" - inet "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/net" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index f4111abad..0eaeb7fd9 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -12,7 +12,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - peer "gx/ipfs/QmY3NAw959vbE1oJooP9HchcRdBsbxhgQsEZTRhKgvoSuC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" ) // Server handles routing queries using a database backend From 276d61440cf783e044593e9b84e8c0043f44e264 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Jan 2016 09:43:06 -0800 Subject: [PATCH 1130/3817] go-keyspace dep from libp2p added License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@8c596d25a2d3a7bbb6919003c3b7271478fa5c62 --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index b9f616c05..3f17e249a 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -6,11 +6,11 @@ package blockservice import ( "errors" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" ) From c6ed4716c00af45aadfdcd9c1a7802c5eac8f6c2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Jan 2016 10:07:26 -0800 Subject: [PATCH 1131/3817] correct go-log dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@bf709dda438b0062e87296c65a709224fad96bf5 --- namesys/proquint.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/routing.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/namesys/proquint.go b/namesys/proquint.go index 8dbf4a19a..ce17181e8 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,8 +4,8 @@ import ( "errors" proquint "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/bren2010/proquint" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" path "github.com/ipfs/go-ipfs/path" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) type ProquintResolver struct{} diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index cf2608651..60ce8cd0d 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -18,7 +18,7 @@ import ( goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" gpctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/routing.go b/namesys/routing.go index 23334e48a..f814ef165 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -15,7 +15,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("namesys") From f2787c806bcee11e5f2c6a4a9e7b9ff6f77ceba2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Jan 2016 10:07:26 -0800 Subject: [PATCH 1132/3817] correct go-log dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@feb5a23da2fd426adeb7ee9c51f5488540b13fda --- routing/dht/dht.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/query.go | 2 +- routing/kbucket/table.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/none/none_client.go | 2 +- routing/offline/offline.go | 2 +- routing/record/record.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/standard.go | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 8c9060a0d..1742e58fc 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -18,7 +18,7 @@ import ( host "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/host" peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" protocol "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/protocol" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 177134991..ae2cbb398 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -6,7 +6,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/query.go b/routing/dht/query.go index c520e738d..c7c5b2f0e 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -11,7 +11,7 @@ import ( todoctr "github.com/ipfs/go-ipfs/util/todocounter" peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" queue "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer/queue" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" ctxproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 2386fd103..e849ed38e 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -8,7 +8,7 @@ import ( "time" peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("table") diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 1a572e303..a15405a3f 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -14,7 +14,7 @@ import ( ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("mockrouter") diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 172daf90a..4ae1c2c89 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -9,7 +9,7 @@ import ( p2phost "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/host" peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 20f770d16..980cb1f9a 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -13,7 +13,7 @@ import ( ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("offlinerouting") diff --git a/routing/record/record.go b/routing/record/record.go index 2d424b276..ca4a7b456 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("routing/record") diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 90f4744c5..72b9347f3 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -14,7 +14,7 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/host" peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 3b57ad625..b08494c57 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -12,7 +12,7 @@ import ( host "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/host" inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) const ProtocolSNR = "/ipfs/supernoderouting" From 305f452eb731e3103adde5de0b4798185450de8c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Jan 2016 10:07:26 -0800 Subject: [PATCH 1133/3817] correct go-log dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@05022b0ed34e74c9db7d4651718e015ae30479b2 --- unixfs/mod/dagmodifier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index a344ff398..e9dbe40a0 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -17,7 +17,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var ErrSeekFail = errors.New("failed to seek properly") From 17af64cc6266ad63c7335c03e849da867e77f45d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Jan 2016 10:07:26 -0800 Subject: [PATCH 1134/3817] correct go-log dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@ae987a615a7e99139bef2abb3185d1f79e089772 --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index daa082f91..8586e2b9b 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -9,7 +9,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index cd17daba7..fb6269d3e 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -8,11 +8,11 @@ import ( "time" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 9188f4d56..a07762a31 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -12,10 +12,10 @@ import ( "unsafe" "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index eb796d919..3ef7ce51b 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -6,12 +6,12 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" "github.com/ipfs/go-ipfs/merkledag" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) func ignoreKeys(key.Key) {} From 642ad8b499d5bbc12c94dd08f651565e1b21c8f0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Jan 2016 10:07:26 -0800 Subject: [PATCH 1135/3817] correct go-log dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-interface@d63fbc7e4334cb75b7c308109a88e820d51b6307 --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index 0f38f86b3..32954b862 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -4,9 +4,9 @@ package exchange import ( "io" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) // Any type that implements exchange.Interface may be used as an IPFS block From 03cb18ff5d1c60d7f648039059b36ad345ee932b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Jan 2016 10:07:26 -0800 Subject: [PATCH 1136/3817] correct go-log dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@facd6b3c1d6fb2cd1a5fbe900be2c55fe5c2ca14 --- mfs/mfs_test.go | 2 +- mfs/system.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 0cf8b639e..917845f5a 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,8 +14,8 @@ import ( randbo "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/dustin/randbo" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" "github.com/ipfs/go-ipfs/path" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/mfs/system.go b/mfs/system.go index 4b9afed7d..c059bf5ce 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -19,7 +19,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var ErrNotExist = errors.New("no such rootfs") From a13bf8f01b5653a8d558ac0d86eba12c7a0eb3f8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Jan 2016 10:07:26 -0800 Subject: [PATCH 1137/3817] correct go-log dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@d14e7c79ca6280018f3597acceff45ad43f5a0c5 --- blockstore/blockstore.go | 4 ++-- blockstore/write_cache.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index f7bbbc8aa..8221ec4a5 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -11,10 +11,10 @@ import ( dsns "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/namespace" dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/query" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("blockstore") diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index 55ff4a1d9..90109e8a2 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -2,9 +2,9 @@ package blockstore import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) // WriteCached returns a blockstore that caches up to |size| unique writes (bs.Put). From 5dc632c797b898f30fcb30f1ddf2d27d7f83cd7b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Jan 2016 10:07:26 -0800 Subject: [PATCH 1138/3817] correct go-log dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@9f902763acb42a0ea546569580b047f17778d3bc --- ipld/merkledag/merkledag.go | 4 ++-- ipld/merkledag/merkledag_test.go | 2 +- ipld/merkledag/utils/diff.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 21ce1422a..e324ceb88 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -4,11 +4,11 @@ package merkledag import ( "fmt" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("merkledag") diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 58d8eadc8..d9622082e 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -12,7 +12,6 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" @@ -24,6 +23,7 @@ import ( "github.com/ipfs/go-ipfs/pin" uio "github.com/ipfs/go-ipfs/unixfs/io" u "github.com/ipfs/go-ipfs/util" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) type dagservAndPinner struct { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 58eeef398..3237ad913 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -5,9 +5,9 @@ import ( "fmt" "path" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) const ( From 118dc6418df0a45c49b84bcdda47ea4676f32140 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Jan 2016 10:07:26 -0800 Subject: [PATCH 1139/3817] correct go-log dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@f5bfff1912893b33495ca78902e9566463d202c4 --- path/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/resolver.go b/path/resolver.go index d4e68a1bf..569a4d1be 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -11,7 +11,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" merkledag "github.com/ipfs/go-ipfs/merkledag" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("path") From 0d2222ee64d1cd18bbc08ba74bb28ab99f790230 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Jan 2016 10:07:26 -0800 Subject: [PATCH 1140/3817] correct go-log dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@c1b893699bb1159d9855410b8f328c5ec593d24e --- blockservice/blockservice.go | 2 +- blockservice/test/blocks_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 3f17e249a..21af30dfb 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -11,7 +11,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("blockservice") diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 4c6dca16c..8cd5e6dfb 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -7,7 +7,6 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" blocksutil "github.com/ipfs/go-ipfs/blocks/blocksutil" @@ -15,6 +14,7 @@ import ( . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" u "github.com/ipfs/go-ipfs/util" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) func TestBlocks(t *testing.T) { From fba5b00664191d0a52164d07855f4b9600a9eaa0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Jan 2016 10:07:26 -0800 Subject: [PATCH 1141/3817] correct go-log dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-chunker@48f7f0637e2db5d7d6778653e9e6541b04decc1f --- chunker/splitting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 84545cdfc..3b539fe7b 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -4,7 +4,7 @@ package chunk import ( "io" - logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log" + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("chunk") From af0810c60c566a996569355bdb8a8c1d27b2060b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 28 Jan 2016 10:07:26 -0800 Subject: [PATCH 1142/3817] correct go-log dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@17a6633c8ee46e9933d2c6a4ffcc5c4c3a1b6aac --- exchange/offline/offline.go | 2 +- exchange/offline/offline_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 47e555e26..8f857d933 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -3,11 +3,11 @@ package offline import ( - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 407865703..d7d17341e 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -5,11 +5,11 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" ds_sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" key "github.com/ipfs/go-ipfs/blocks/key" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) func TestBlockReturnsErr(t *testing.T) { From f583c5534e3556ae8a99036a1ecf1b75aac8a4d0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 31 Jan 2016 10:19:50 -0800 Subject: [PATCH 1143/3817] update libp2p dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@03d76dd20414f04c01ac0adef1a2ba35ab8686af --- namesys/interface.go | 2 +- namesys/ipns_select_test.go | 2 +- namesys/namesys.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index 4917370b4..adce88024 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -34,7 +34,7 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index 856d0b4d9..78cbb1c5f 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -11,7 +11,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" u "github.com/ipfs/go-ipfs/util" - ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" ) func shuffle(a []*pb.IpnsEntry) { diff --git a/namesys/namesys.go b/namesys/namesys.go index b25828909..9df56da8c 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,7 +7,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 513a45e61..71349e528 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -20,8 +20,8 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" u "github.com/ipfs/go-ipfs/util" - ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 60ce8cd0d..e57c6f6ea 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,7 +11,7 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 4b546fa68..8303a36af 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + mocknet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 8b9e955f0..6ed7b8870 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -11,7 +11,7 @@ import ( mockrouting "github.com/ipfs/go-ipfs/routing/mock" u "github.com/ipfs/go-ipfs/util" testutil "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/routing.go b/namesys/routing.go index f814ef165..1293721a2 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -14,7 +14,7 @@ import ( path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" - ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) From d9275b47a04776a965049ca4fa4b175b5247db1c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 31 Jan 2016 10:19:50 -0800 Subject: [PATCH 1144/3817] update libp2p dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@d2e33cfa5be5a6dd6bfcd157bcf4dce8054bade7 --- routing/dht/dht.go | 8 ++++---- routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 4 ++-- routing/dht/dht_test.go | 4 ++-- routing/dht/diag.go | 2 +- routing/dht/ext_test.go | 6 +++--- routing/dht/handlers.go | 2 +- routing/dht/lookup.go | 2 +- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 4 ++-- routing/dht/providers.go | 2 +- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 4 ++-- routing/dht/records.go | 4 ++-- routing/dht/routing.go | 4 ++-- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 2 +- routing/kbucket/table_test.go | 2 +- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 2 +- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 4 ++-- routing/record/record.go | 2 +- routing/record/validation.go | 2 +- routing/routing.go | 4 ++-- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 2 +- 34 files changed, 52 insertions(+), 52 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 1742e58fc..cd51f2be3 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -14,10 +14,10 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" - host "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/host" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" - protocol "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/protocol" + ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" + host "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/host" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + protocol "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/protocol" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 4cc49c6ad..8e94e9295 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -10,7 +10,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" periodicproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/periodic" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index b2a5b669f..add91f0ca 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -7,8 +7,8 @@ import ( ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + inet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 70d469259..de7256512 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -18,8 +18,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" u "github.com/ipfs/go-ipfs/util" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" - netutil "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/test/util" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + netutil "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/test/util" ci "github.com/ipfs/go-ipfs/util/testutil/ci" travisci "github.com/ipfs/go-ipfs/util/testutil/ci/travis" diff --git a/routing/dht/diag.go b/routing/dht/diag.go index 2a183a34e..674a1f44c 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" ) type connDiagInfo struct { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index c74dba02d..e431c183a 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -17,9 +17,9 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" u "github.com/ipfs/go-ipfs/util" - inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + inet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 92ba7469b..95df157f4 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -11,7 +11,7 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" lgbl "github.com/ipfs/go-ipfs/util/eventlog/loggables" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 3d8f400c9..b8e0c0c5d 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -5,7 +5,7 @@ import ( notif "github.com/ipfs/go-ipfs/notifications" kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/util/peerset" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 9df5abdee..92c39c06e 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" - inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" + inet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index ae2cbb398..bb7276312 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,8 +4,8 @@ import ( ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" - inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + inet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 601a578ed..a76bf6727 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 1f92713e3..2087ee636 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -4,7 +4,7 @@ import ( "testing" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index c7c5b2f0e..832389c32 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -9,8 +9,8 @@ import ( u "github.com/ipfs/go-ipfs/util" pset "github.com/ipfs/go-ipfs/util/peerset" todoctr "github.com/ipfs/go-ipfs/util/todocounter" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" - queue "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer/queue" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + queue "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer/queue" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" diff --git a/routing/dht/records.go b/routing/dht/records.go index 82aeb66bb..c84e52451 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -8,8 +8,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index fa099e0ca..035e60899 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,8 +12,8 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/util/peerset" - inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + inet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 77fd6dbfb..494f448a7 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index 33b6a440f..ae3d13667 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index e849ed38e..2464251e2 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,7 +7,7 @@ import ( "sync" "time" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 1c9db10a1..083e17287 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,7 +7,7 @@ import ( tu "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index a0c634781..50c17c3eb 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" u "github.com/ipfs/go-ipfs/util" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" ) // Returned if a routing table query returns no results. This is NOT expected diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index a15405a3f..eb769308f 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -11,8 +11,8 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" "github.com/ipfs/go-ipfs/util/testutil" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 3f05d5432..6331e7964 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -8,7 +8,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 69df8bbc8..d1a55e86c 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 66a9d169a..64fa620a9 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,7 +5,7 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/util/testutil" - mocknet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 6fc9fa910..1f21d0b4e 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,7 +10,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 4ae1c2c89..f6be10151 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,8 +6,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" - p2phost "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/host" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + p2phost "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/host" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 980cb1f9a..36a8e1d8e 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -10,8 +10,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" - "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" + "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/record/record.go b/routing/record/record.go index ca4a7b456..96d9f69da 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/record/validation.go b/routing/record/validation.go index 2179d1b2d..f1cfb2bfb 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" - ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" ) // ValidatorFunc is a function that is called to validate a given diff --git a/routing/routing.go b/routing/routing.go index 80d6e3236..b737c27c2 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -5,8 +5,8 @@ import ( "errors" key "github.com/ipfs/go-ipfs/blocks/key" - ci "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 72b9347f3..40d6e79b3 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -12,8 +12,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/host" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/host" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 2600a6a6a..58f11a73a 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + inet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index b08494c57..487cade62 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -9,9 +9,9 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" - host "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/host" - inet "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + host "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/host" + inet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 0eaeb7fd9..7a7c78bc3 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -12,7 +12,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer" + peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" ) // Server handles routing queries using a database backend From 36b3b43bb1d0c45ef658714733aaf0a02fb58bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Fri, 23 Oct 2015 21:57:29 +0200 Subject: [PATCH 1145/3817] Add log events when blocks are added/removed from the blockstore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Michael Muré This commit was moved from ipfs/go-ipfs-blockstore@68d6c7eb55da7560cb66ba3b07f624ee830d56d5 --- blockstore/write_cache.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index 73a7813f5..ecb8933bc 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -22,6 +22,7 @@ type writecache struct { } func (w *writecache) DeleteBlock(k key.Key) error { + defer log.EventBegin(context.TODO(), "writecache.BlockRemoved", &k).Done() w.cache.Remove(k) return w.blockstore.DeleteBlock(k) } @@ -38,9 +39,12 @@ func (w *writecache) Get(k key.Key) (*blocks.Block, error) { } func (w *writecache) Put(b *blocks.Block) error { - if _, ok := w.cache.Get(b.Key()); ok { + k := b.Key() + if _, ok := w.cache.Get(k); ok { return nil } + defer log.EventBegin(context.TODO(), "writecache.BlockAdded", &k).Done() + w.cache.Add(b.Key(), struct{}{}) return w.blockstore.Put(b) } @@ -50,6 +54,8 @@ func (w *writecache) PutMany(bs []*blocks.Block) error { for _, b := range bs { if _, ok := w.cache.Get(b.Key()); !ok { good = append(good, b) + k := b.Key() + defer log.EventBegin(context.TODO(), "writecache.BlockAdded", &k).Done() } } return w.blockstore.PutMany(good) From 44366170a7914eb4f474da6f246dfcffb2f8c0b4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 31 Jan 2016 15:37:39 -0800 Subject: [PATCH 1146/3817] do that last thing again License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@522df738c30edb10c0590aa0a5b16fd81047c662 --- namesys/interface.go | 2 +- namesys/ipns_select_test.go | 2 +- namesys/namesys.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index adce88024..68933bfe0 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -34,7 +34,7 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index 78cbb1c5f..beeb0ac7c 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -11,7 +11,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" u "github.com/ipfs/go-ipfs/util" - ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" ) func shuffle(a []*pb.IpnsEntry) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 9df56da8c..b9c753881 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,7 +7,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 71349e528..981814f52 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -20,8 +20,8 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" u "github.com/ipfs/go-ipfs/util" - ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index e57c6f6ea..37d4d19e2 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,7 +11,7 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 8303a36af..a56111874 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + mocknet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 6ed7b8870..ff1b27f54 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -11,7 +11,7 @@ import ( mockrouting "github.com/ipfs/go-ipfs/routing/mock" u "github.com/ipfs/go-ipfs/util" testutil "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/routing.go b/namesys/routing.go index 1293721a2..933bdd041 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -14,7 +14,7 @@ import ( path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" - ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) From 5cdee8eedfe00f5266b38832f8ba39d8eca1b242 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 31 Jan 2016 15:37:39 -0800 Subject: [PATCH 1147/3817] do that last thing again License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@e6a40c1ab9a508fc276da2df39f6dc9b4f77ad38 --- routing/dht/dht.go | 8 ++++---- routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 4 ++-- routing/dht/dht_test.go | 4 ++-- routing/dht/diag.go | 2 +- routing/dht/ext_test.go | 6 +++--- routing/dht/handlers.go | 2 +- routing/dht/lookup.go | 2 +- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 4 ++-- routing/dht/providers.go | 2 +- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 4 ++-- routing/dht/records.go | 4 ++-- routing/dht/routing.go | 4 ++-- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 2 +- routing/kbucket/table_test.go | 2 +- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 2 +- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 4 ++-- routing/record/record.go | 2 +- routing/record/validation.go | 2 +- routing/routing.go | 4 ++-- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 2 +- 34 files changed, 52 insertions(+), 52 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index cd51f2be3..eadd5b4be 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -14,10 +14,10 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" - host "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/host" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" - protocol "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/protocol" + ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" + host "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/host" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + protocol "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/protocol" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 8e94e9295..3865bca13 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -10,7 +10,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" u "github.com/ipfs/go-ipfs/util" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" periodicproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/periodic" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index add91f0ca..e88a94dc6 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -7,8 +7,8 @@ import ( ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index de7256512..55ed11c55 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -18,8 +18,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" u "github.com/ipfs/go-ipfs/util" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" - netutil "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/test/util" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + netutil "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/test/util" ci "github.com/ipfs/go-ipfs/util/testutil/ci" travisci "github.com/ipfs/go-ipfs/util/testutil/ci/travis" diff --git a/routing/dht/diag.go b/routing/dht/diag.go index 674a1f44c..3f8a7a929 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" ) type connDiagInfo struct { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index e431c183a..adff49c90 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -17,9 +17,9 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" u "github.com/ipfs/go-ipfs/util" - inet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 95df157f4..265ff49f9 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -11,7 +11,7 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" lgbl "github.com/ipfs/go-ipfs/util/eventlog/loggables" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index b8e0c0c5d..140fcae21 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -5,7 +5,7 @@ import ( notif "github.com/ipfs/go-ipfs/notifications" kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/util/peerset" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 92c39c06e..2fe6cce40 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" - inet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net" + inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index bb7276312..a1c4887e1 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,8 +4,8 @@ import ( ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" - inet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index a76bf6727..c0c16c54e 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 2087ee636..ec3b7a5d1 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -4,7 +4,7 @@ import ( "testing" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index 832389c32..53e75fecb 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -9,8 +9,8 @@ import ( u "github.com/ipfs/go-ipfs/util" pset "github.com/ipfs/go-ipfs/util/peerset" todoctr "github.com/ipfs/go-ipfs/util/todocounter" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" - queue "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer/queue" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + queue "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer/queue" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" diff --git a/routing/dht/records.go b/routing/dht/records.go index c84e52451..06a4e70b1 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -8,8 +8,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 035e60899..e0feda4ec 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,8 +12,8 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/util/peerset" - inet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 494f448a7..8924a63d4 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index ae3d13667..e1e0a1bbf 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 2464251e2..5128b7821 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,7 +7,7 @@ import ( "sync" "time" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 083e17287..13f02df89 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,7 +7,7 @@ import ( tu "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 50c17c3eb..73602d185 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" u "github.com/ipfs/go-ipfs/util" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" ) // Returned if a routing table query returns no results. This is NOT expected diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index eb769308f..407f0c094 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -11,8 +11,8 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 6331e7964..8e8d5d0b2 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -8,7 +8,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index d1a55e86c..d71b24c0b 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 64fa620a9..99ef8ba88 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,7 +5,7 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/util/testutil" - mocknet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 1f21d0b4e..75daee9e9 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,7 +10,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/util/testutil" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index f6be10151..1f7c9749f 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,8 +6,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" - p2phost "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/host" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + p2phost "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/host" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 36a8e1d8e..7e5f80275 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -10,8 +10,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" - "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" + "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/record/record.go b/routing/record/record.go index 96d9f69da..2adcc21bc 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/record/validation.go b/routing/record/validation.go index f1cfb2bfb..32c33e966 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" u "github.com/ipfs/go-ipfs/util" - ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" ) // ValidatorFunc is a function that is called to validate a given diff --git a/routing/routing.go b/routing/routing.go index b737c27c2..5acce6f54 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -5,8 +5,8 @@ import ( "errors" key "github.com/ipfs/go-ipfs/blocks/key" - ci "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 40d6e79b3..8838b7aeb 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -12,8 +12,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/host" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/host" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 58f11a73a..c067f634b 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 487cade62..00892de88 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -9,9 +9,9 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" - host "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/host" - inet "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/net" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + host "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/host" + inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 7a7c78bc3..46caf03ea 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -12,7 +12,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - peer "gx/ipfs/QmQQCBoWhMZtStYuAAo2uDNNLit9n7yX5ANBecfjKq4XBn/go-libp2p/p2p/peer" + peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" ) // Server handles routing queries using a database backend From f20e2806b84e74a42e0d2896cf6b76fc794410d1 Mon Sep 17 00:00:00 2001 From: Thomas Gardner Date: Sun, 24 Jan 2016 14:18:03 +1000 Subject: [PATCH 1148/3817] trivial: various superficial fixes misc/completion/ipfs-completion.bash: add `ipfs stats` to BASH completion core/commands/mount_unix.go: ensure error is not nil before printing it contribute.md: fix bibliography indexing in example core/commands/swarm.go: change tabs to spaces in USAGE message *: 80-column readability improvements License: MIT Signed-off-by: Thomas Gardner This commit was moved from ipfs/go-unixfs@527151a784fecc880591a8a6d771a1685e66781c --- unixfs/format.go | 5 +++-- unixfs/io/dagreader.go | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/unixfs/format.go b/unixfs/format.go index 472a575e7..0bf569438 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -1,5 +1,6 @@ -// Package format implements a data format for files in the ipfs filesystem -// It is not the only format in ipfs, but it is the one that the filesystem assumes +// Package format implements a data format for files in the ipfs filesystem It +// is not the only format in ipfs, but it is the one that the filesystem +// assumes package unixfs import ( diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 646a69a40..4cc522025 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -56,8 +56,8 @@ type ReadSeekCloser interface { io.WriterTo } -// NewDagReader creates a new reader object that reads the data represented by the given -// node, using the passed in DAGService for data retreival +// NewDagReader creates a new reader object that reads the data represented by +// the given node, using the passed in DAGService for data retreival func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (*DagReader, error) { pb := new(ftpb.Data) if err := proto.Unmarshal(n.Data, pb); err != nil { @@ -102,8 +102,8 @@ func NewDataFileReader(ctx context.Context, n *mdag.Node, pb *ftpb.Data, serv md } } -// precalcNextBuf follows the next link in line and loads it from the DAGService, -// setting the next buffer to read from +// precalcNextBuf follows the next link in line and loads it from the +// DAGService, setting the next buffer to read from func (dr *DagReader) precalcNextBuf(ctx context.Context) error { dr.buf.Close() // Just to make sure if dr.linkPosition >= len(dr.promises) { From dc753b913ecbfaaa4643456b2c72766929393563 Mon Sep 17 00:00:00 2001 From: Thomas Gardner Date: Sun, 24 Jan 2016 14:18:03 +1000 Subject: [PATCH 1149/3817] trivial: various superficial fixes misc/completion/ipfs-completion.bash: add `ipfs stats` to BASH completion core/commands/mount_unix.go: ensure error is not nil before printing it contribute.md: fix bibliography indexing in example core/commands/swarm.go: change tabs to spaces in USAGE message *: 80-column readability improvements License: MIT Signed-off-by: Thomas Gardner This commit was moved from ipfs/go-ipfs-exchange-interface@5916b728a63371e5afb3c4d6d23e0548c5103197 --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index 1a149ed9d..05f52a64b 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -11,7 +11,7 @@ import ( // Any type that implements exchange.Interface may be used as an IPFS block // exchange protocol. -type Interface interface { +type Interface interface { // type Exchanger interface // GetBlock returns the block associated with a given key. GetBlock(context.Context, key.Key) (*blocks.Block, error) From af7fbeb68e497194fbed0c6af16857bf0f0df551 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 11 Jan 2016 03:20:41 -0800 Subject: [PATCH 1150/3817] flushing and shallow list names License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@2edcf0e284bf7f4af0e2519b14dc8291de739853 --- mfs/dir.go | 24 +++++++++++++++++++ mfs/mfs_test.go | 50 ++++++++++++++++++++++++++++++++++++++++ mfs/ops.go | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) diff --git a/mfs/dir.go b/mfs/dir.go index 28d9f7306..f4147dd2a 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -175,6 +175,30 @@ type NodeListing struct { Hash string } +func (d *Directory) ListNames() []string { + d.Lock() + defer d.Unlock() + + names := make(map[string]struct{}) + for n, _ := range d.childDirs { + names[n] = struct{}{} + } + for n, _ := range d.files { + names[n] = struct{}{} + } + + for _, l := range d.node.Links { + names[l.Name] = struct{}{} + } + + var out []string + for n, _ := range names { + out = append(out, n) + } + + return out +} + func (d *Directory) List() ([]NodeListing, error) { d.lock.Lock() defer d.lock.Unlock() diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 917845f5a..161a8945a 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -675,3 +675,53 @@ func TestMfsStress(t *testing.T) { } } } + +func TestFlushing(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + _, rt := setupRoot(ctx, t) + + dir := rt.GetValue().(*Directory) + c := mkdirP(t, dir, "a/b/c") + d := mkdirP(t, dir, "a/b/d") + e := mkdirP(t, dir, "a/b/e") + + data := []byte("this is a test\n") + nd1 := &dag.Node{Data: ft.FilePBData(data, uint64(len(data)))} + + if err := c.AddChild("TEST", nd1); err != nil { + t.Fatal(err) + } + if err := d.AddChild("TEST", nd1); err != nil { + t.Fatal(err) + } + if err := e.AddChild("TEST", nd1); err != nil { + t.Fatal(err) + } + + if err := FlushPath(rt, "/a/b/c/TEST"); err != nil { + t.Fatal(err) + } + + if err := FlushPath(rt, "/a/b/d/TEST"); err != nil { + t.Fatal(err) + } + + if err := FlushPath(rt, "/a/b/e/TEST"); err != nil { + t.Fatal(err) + } + + rnd, err := dir.GetNode() + if err != nil { + t.Fatal(err) + } + + rnk, err := rnd.Key() + if err != nil { + t.Fatal(err) + } + + if rnk.B58String() != "QmWcvrHUFk7LQRrA4WqKjqy7ZyRGFLVagtgNxbEodTEzQ4" { + t.Fatal("dag looks wrong") + } +} diff --git a/mfs/ops.go b/mfs/ops.go index 75c5d6a84..6edc9dd76 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -194,3 +194,64 @@ func DirLookup(d *Directory, pth string) (FSNode, error) { } return cur, nil } + +func FlushPath(r *Root, pth string) error { + parts := path.SplitList(strings.Trim(pth, "/")) + + d, ok := r.GetValue().(*Directory) + if !ok { + return errors.New("mfs root somehow didnt point to a directory") + } + + nd, err := flushPathRec(d, parts) + if err != nil { + return err + } + + k, err := nd.Key() + if err != nil { + return err + } + + r.repub.Update(k) + return nil +} + +func flushPathRec(d *Directory, parts []string) (*dag.Node, error) { + if len(parts) == 0 { + return d.GetNode() + } + + d.Lock() + defer d.Unlock() + + next, err := d.childUnsync(parts[0]) + if err != nil { + log.Errorf("childnode: %q %q", parts[0], err) + return nil, err + } + + switch next := next.(type) { + case *Directory: + nd, err := flushPathRec(next, parts[1:]) + if err != nil { + return nil, err + } + + newnode, err := d.node.UpdateNodeLink(parts[0], nd) + if err != nil { + return nil, err + } + + d.node = newnode + return newnode, nil + case *File: + if len(parts) > 1 { + return nil, fmt.Errorf("%s is a file, not a directory", parts[0]) + } + + return next.GetNode() + default: + return nil, fmt.Errorf("unrecognized FSNode type: %#v", next) + } +} From ecdb1b5762e5c19fc42968d7a8f79d72d087430d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 11 Jan 2016 05:37:34 -0800 Subject: [PATCH 1151/3817] flush pinning improvements License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@12a3d99b70a7bf2ee650cc266f20f879ab1dbb5e --- mfs/ops.go | 22 +++++++++++++++++++++- mfs/system.go | 19 +++++++++++++++---- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/mfs/ops.go b/mfs/ops.go index 6edc9dd76..3348d4522 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -197,6 +197,9 @@ func DirLookup(d *Directory, pth string) (FSNode, error) { func FlushPath(r *Root, pth string) error { parts := path.SplitList(strings.Trim(pth, "/")) + if len(parts) == 1 && parts[0] == "" { + parts = nil + } d, ok := r.GetValue().(*Directory) if !ok { @@ -214,12 +217,24 @@ func FlushPath(r *Root, pth string) error { } r.repub.Update(k) + r.repub.WaitPub() + return nil } func flushPathRec(d *Directory, parts []string) (*dag.Node, error) { if len(parts) == 0 { - return d.GetNode() + nd, err := d.GetNode() + if err != nil { + return nil, err + } + + _, err = d.dserv.Add(nd) + if err != nil { + return nil, err + } + + return nd, nil } d.Lock() @@ -243,6 +258,11 @@ func flushPathRec(d *Directory, parts []string) (*dag.Node, error) { return nil, err } + _, err = d.dserv.Add(newnode) + if err != nil { + return nil, err + } + d.node = newnode return newnode, nil case *File: diff --git a/mfs/system.go b/mfs/system.go index c059bf5ce..b5fe38768 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -165,7 +165,7 @@ type Republisher struct { TimeoutShort time.Duration Publish chan struct{} pubfunc PubFunc - pubnowch chan struct{} + pubnowch chan chan struct{} ctx context.Context cancel func() @@ -190,7 +190,7 @@ func NewRepublisher(ctx context.Context, pf PubFunc, tshort, tlong time.Duration TimeoutLong: tlong, Publish: make(chan struct{}, 1), pubfunc: pf, - pubnowch: make(chan struct{}), + pubnowch: make(chan chan struct{}), ctx: ctx, cancel: cancel, } @@ -204,11 +204,17 @@ func (p *Republisher) setVal(k key.Key) { func (p *Republisher) pubNow() { select { - case p.pubnowch <- struct{}{}: + case p.pubnowch <- nil: default: } } +func (p *Republisher) WaitPub() { + wait := make(chan struct{}) + p.pubnowch <- wait + <-wait +} + func (p *Republisher) Close() error { err := p.publish(p.ctx) p.cancel() @@ -235,6 +241,8 @@ func (np *Republisher) Run() { longer := time.After(np.TimeoutLong) wait: + var pubnowresp chan struct{} + select { case <-np.ctx.Done(): return @@ -243,10 +251,13 @@ func (np *Republisher) Run() { goto wait case <-quick: case <-longer: - case <-np.pubnowch: + case pubnowresp = <-np.pubnowch: } err := np.publish(np.ctx) + if pubnowresp != nil { + pubnowresp <- struct{}{} + } if err != nil { log.Error("republishRoot error: %s", err) } From 78a5e1d3572f0bcfcdf82c91eff0944f49fea0a6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 11 Jan 2016 06:04:04 -0800 Subject: [PATCH 1152/3817] use correct context in pubfunc pinning License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@f180e18d6cdf1b14770580e1293f3e589d006dcd --- mfs/ops.go | 1 + 1 file changed, 1 insertion(+) diff --git a/mfs/ops.go b/mfs/ops.go index 3348d4522..f12dfa743 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -162,6 +162,7 @@ func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { func Lookup(r *Root, path string) (FSNode, error) { dir, ok := r.GetValue().(*Directory) if !ok { + log.Error("root not a dir: %#v", r.GetValue()) return nil, errors.New("root was not a directory") } From 0dbaeae6e13642a046b2245ff3c768bc7705aba2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 11 Jan 2016 06:06:33 -0800 Subject: [PATCH 1153/3817] sort ListNames output License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@dc6d031c980f8077e9d1062d51a511affed0c678 --- mfs/dir.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mfs/dir.go b/mfs/dir.go index f4147dd2a..c70555bb7 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path" + "sort" "sync" "time" @@ -195,6 +196,7 @@ func (d *Directory) ListNames() []string { for n, _ := range names { out = append(out, n) } + sort.Strings(out) return out } From 1e023678666c65eb96bc5bd00a6cf22816f71496 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 15 Jan 2016 15:14:29 -0800 Subject: [PATCH 1154/3817] blockstore locks return unlocker object now License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@0e72f107dd9ca58828b9f1fd46f907c1909d86fd --- mfs/dir.go | 36 +++++++++++---------- mfs/file.go | 14 ++++----- mfs/mfs_test.go | 83 ++++++++++++++++++++++++++++++++++++++++--------- mfs/ops.go | 38 ++++++++++++---------- mfs/system.go | 8 ++--- 5 files changed, 120 insertions(+), 59 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index c70555bb7..d437b28d7 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -51,17 +51,20 @@ func NewDirectory(ctx context.Context, name string, node *dag.Node, parent child // closeChild updates the child by the given name to the dag node 'nd' // and changes its own dag node -func (d *Directory) closeChild(name string, nd *dag.Node) error { - mynd, err := d.closeChildUpdate(name, nd) +func (d *Directory) closeChild(name string, nd *dag.Node, sync bool) error { + mynd, err := d.closeChildUpdate(name, nd, sync) if err != nil { return err } - return d.parent.closeChild(d.name, mynd) + if sync { + return d.parent.closeChild(d.name, mynd, true) + } + return nil } // closeChildUpdate is the portion of closeChild that needs to be locked around -func (d *Directory) closeChildUpdate(name string, nd *dag.Node) (*dag.Node, error) { +func (d *Directory) closeChildUpdate(name string, nd *dag.Node, sync bool) (*dag.Node, error) { d.lock.Lock() defer d.lock.Unlock() @@ -70,7 +73,10 @@ func (d *Directory) closeChildUpdate(name string, nd *dag.Node) (*dag.Node, erro return nil, err } - return d.flushCurrentNode() + if sync { + return d.flushCurrentNode() + } + return nil, nil } func (d *Directory) flushCurrentNode() (*dag.Node, error) { @@ -295,12 +301,15 @@ func (d *Directory) Unlink(name string) error { } func (d *Directory) Flush() error { + d.lock.Lock() nd, err := d.flushCurrentNode() if err != nil { + d.lock.Unlock() return err } + d.lock.Unlock() - return d.parent.closeChild(d.name, nd) + return d.parent.closeChild(d.name, nd, true) } // AddChild adds the node 'nd' under this directory giving it the name 'name' @@ -335,11 +344,6 @@ func (d *Directory) sync() error { return err } - _, err = d.dserv.Add(nd) - if err != nil { - return err - } - err = d.updateChild(name, nd) if err != nil { return err @@ -352,11 +356,6 @@ func (d *Directory) sync() error { return err } - _, err = d.dserv.Add(nd) - if err != nil { - return err - } - err = d.updateChild(name, nd) if err != nil { return err @@ -385,6 +384,11 @@ func (d *Directory) GetNode() (*dag.Node, error) { return nil, err } + _, err = d.dserv.Add(d.node) + if err != nil { + return nil, err + } + return d.node.Copy(), nil } diff --git a/mfs/file.go b/mfs/file.go index da4737140..99dfff0df 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -73,7 +73,7 @@ func (fi *File) Close() error { // explicitly stay locked for flushUp call, // it will manage the lock for us - return fi.flushUp() + return fi.flushUp(true) } fi.Unlock() @@ -82,7 +82,7 @@ func (fi *File) Close() error { // flushUp syncs the file and adds it to the dagservice // it *must* be called with the File's lock taken -func (fi *File) flushUp() error { +func (fi *File) flushUp(fullsync bool) error { nd, err := fi.mod.GetNode() if err != nil { fi.Unlock() @@ -95,20 +95,18 @@ func (fi *File) flushUp() error { return err } - //name := fi.name - //parent := fi.parent + name := fi.name + parent := fi.parent // explicit unlock *only* before closeChild call fi.Unlock() - return nil - //return parent.closeChild(name, nd) + return parent.closeChild(name, nd, fullsync) } // Sync flushes the changes in the file to disk func (fi *File) Sync() error { fi.Lock() - defer fi.Unlock() - return fi.mod.Sync() + return fi.flushUp(false) } // Seek implements io.Seeker diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 161a8945a..38237c218 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -576,15 +576,15 @@ func actorRemoveFile(d *Directory) error { return d.Unlink(re.Name) } -func actorReadFile(d *Directory) error { +func randomFile(d *Directory) (*File, error) { d, err := randomWalk(d, rand.Intn(6)) if err != nil { - return err + return nil, err } ents, err := d.List() if err != nil { - return err + return nil, err } var files []string @@ -595,18 +595,61 @@ func actorReadFile(d *Directory) error { } if len(files) == 0 { - return nil + return nil, nil } fname := files[rand.Intn(len(files))] fsn, err := d.Child(fname) if err != nil { - return err + return nil, err } fi, ok := fsn.(*File) if !ok { - return errors.New("file wasnt a file, race?") + return nil, errors.New("file wasnt a file, race?") + } + + return fi, nil +} + +func actorWriteFile(d *Directory) error { + fi, err := randomFile(d) + if err != nil { + return err + } + if fi == nil { + return nil + } + + size := rand.Intn(1024) + buf := make([]byte, size) + randbo.New().Read(buf) + + s, err := fi.Size() + if err != nil { + return err + } + + offset := rand.Int63n(s) + + n, err := fi.WriteAt(buf, offset) + if err != nil { + return err + } + if n != size { + return fmt.Errorf("didnt write enough") + } + + return fi.Close() +} + +func actorReadFile(d *Directory) error { + fi, err := randomFile(d) + if err != nil { + return err + } + if fi == nil { + return nil } _, err = fi.Size() @@ -637,12 +680,7 @@ func testActor(rt *Root, iterations int, errs chan error) { return } case 3: - continue - // randomly deleting things - // doesnt really give us any sort of useful test results. - // you will never have this in a real environment where - // you expect anything productive to happen... - if err := actorRemoveFile(d); err != nil { + if err := actorWriteFile(d); err != nil { errs <- err return } @@ -698,6 +736,9 @@ func TestFlushing(t *testing.T) { if err := e.AddChild("TEST", nd1); err != nil { t.Fatal(err) } + if err := dir.AddChild("FILE", nd1); err != nil { + t.Fatal(err) + } if err := FlushPath(rt, "/a/b/c/TEST"); err != nil { t.Fatal(err) @@ -711,17 +752,31 @@ func TestFlushing(t *testing.T) { t.Fatal(err) } + if err := FlushPath(rt, "/FILE"); err != nil { + t.Fatal(err) + } + rnd, err := dir.GetNode() if err != nil { t.Fatal(err) } + fsnode, err := ft.FSNodeFromBytes(rnd.Data) + if err != nil { + t.Fatal(err) + } + + if fsnode.Type != ft.TDirectory { + t.Fatal("root wasnt a directory") + } + rnk, err := rnd.Key() if err != nil { t.Fatal(err) } - if rnk.B58String() != "QmWcvrHUFk7LQRrA4WqKjqy7ZyRGFLVagtgNxbEodTEzQ4" { - t.Fatal("dag looks wrong") + exp := "QmWMVyhTuyxUrXX3ynz171jq76yY3PktfY9Bxiph7b9ikr" + if rnk.B58String() != exp { + t.Fatalf("dag looks wrong, expected %s, but got %s", exp, rnk.B58String()) } } diff --git a/mfs/ops.go b/mfs/ops.go index f12dfa743..9bc59994c 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -230,11 +230,6 @@ func flushPathRec(d *Directory, parts []string) (*dag.Node, error) { return nil, err } - _, err = d.dserv.Add(nd) - if err != nil { - return nil, err - } - return nd, nil } @@ -247,6 +242,7 @@ func flushPathRec(d *Directory, parts []string) (*dag.Node, error) { return nil, err } + var ndagnode *dag.Node switch next := next.(type) { case *Directory: nd, err := flushPathRec(next, parts[1:]) @@ -254,25 +250,33 @@ func flushPathRec(d *Directory, parts []string) (*dag.Node, error) { return nil, err } - newnode, err := d.node.UpdateNodeLink(parts[0], nd) - if err != nil { - return nil, err - } - - _, err = d.dserv.Add(newnode) - if err != nil { - return nil, err - } + ndagnode = nd - d.node = newnode - return newnode, nil case *File: if len(parts) > 1 { return nil, fmt.Errorf("%s is a file, not a directory", parts[0]) } - return next.GetNode() + child, err := next.GetNode() + if err != nil { + return nil, err + } + + ndagnode = child default: return nil, fmt.Errorf("unrecognized FSNode type: %#v", next) } + + newnode, err := d.node.UpdateNodeLink(parts[0], ndagnode) + if err != nil { + return nil, err + } + + _, err = d.dserv.Add(newnode) + if err != nil { + return nil, err + } + + d.node = newnode + return newnode, nil } diff --git a/mfs/system.go b/mfs/system.go index b5fe38768..454577c45 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -29,7 +29,7 @@ var log = logging.Logger("mfs") var ErrIsDirectory = errors.New("error: is a directory") type childCloser interface { - closeChild(string, *dag.Node) error + closeChild(string, *dag.Node, bool) error } type NodeType int @@ -115,7 +115,7 @@ func (kr *Root) Flush() error { return err } - k, err := kr.dserv.Add(nd) + k, err := nd.Key() if err != nil { return err } @@ -128,7 +128,7 @@ func (kr *Root) Flush() error { // closeChild implements the childCloser interface, and signals to the publisher that // there are changes ready to be published -func (kr *Root) closeChild(name string, nd *dag.Node) error { +func (kr *Root) closeChild(name string, nd *dag.Node, sync bool) error { k, err := kr.dserv.Add(nd) if err != nil { return err @@ -146,7 +146,7 @@ func (kr *Root) Close() error { return err } - k, err := kr.dserv.Add(nd) + k, err := nd.Key() if err != nil { return err } From 64cf9fefd56de64974d3f6a5b4289177ea71709f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 15 Jan 2016 15:14:29 -0800 Subject: [PATCH 1155/3817] blockstore locks return unlocker object now License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@a85166021a651da1dd72d33873dc61fc8dffc91c --- unixfs/mod/dagmodifier.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index e9dbe40a0..5306399f6 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -169,12 +169,6 @@ func (dm *DagModifier) Sync() error { // Number of bytes we're going to write buflen := dm.wrBuf.Len() - // Grab key for unpinning after mod operation - _, err := dm.curNode.Key() - if err != nil { - return err - } - // overwrite existing dag nodes thisk, done, err := dm.modifyDag(dm.curNode, dm.writeStart, dm.wrBuf) if err != nil { From 888a6640c3f8bce10ffd36da18748922f2ca0b37 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 15 Jan 2016 15:14:29 -0800 Subject: [PATCH 1156/3817] blockstore locks return unlocker object now License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@478ebe0ca768d14cc8001acf665fe537476ebb29 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 695da62ec..0aad6c03f 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -23,7 +23,7 @@ var log = logging.Logger("gc") // The routine then iterates over every block in the blockstore and // deletes any block that is not found in the marked set. func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner) (<-chan key.Key, error) { - unlock := bs.GCLock() + unlocker := bs.GCLock() bsrv := bserv.New(bs, offline.Exchange(bs)) ds := dag.NewDAGService(bsrv) @@ -41,7 +41,7 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner) (<-chan key. output := make(chan key.Key) go func() { defer close(output) - defer unlock() + defer unlocker.Unlock() for { select { case k, ok := <-keychan: diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index fb6269d3e..a7f62417f 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -123,6 +123,8 @@ func (p *pinner) Pin(ctx context.Context, node *mdag.Node, recurse bool) error { return nil } +var ErrNotPinned = fmt.Errorf("not pinned") + // Unpin a given key func (p *pinner) Unpin(ctx context.Context, k key.Key, recursive bool) error { p.lock.Lock() @@ -132,7 +134,7 @@ func (p *pinner) Unpin(ctx context.Context, k key.Key, recursive bool) error { return err } if !pinned { - return fmt.Errorf("%s is not pinned", k) + return ErrNotPinned } switch reason { case "recursive": From bcced0c184bcf7cb43ef95a16f42f7843f863ca4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 15 Jan 2016 15:14:29 -0800 Subject: [PATCH 1157/3817] blockstore locks return unlocker object now License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@dfdb1014a5fa19f8200db4c6d3d4324822b8b271 --- blockstore/blockstore.go | 25 +++++++++++++++++++------ blockstore/write_cache.go | 4 ++-- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 8221ec4a5..51979e653 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -43,13 +43,13 @@ type GCBlockstore interface { // GCLock locks the blockstore for garbage collection. No operations // that expect to finish with a pin should ocurr simultaneously. // Reading during GC is safe, and requires no lock. - GCLock() func() + GCLock() Unlocker // PinLock locks the blockstore for sequences of puts expected to finish // with a pin (before GC). Multiple put->pin sequences can write through // at the same time, but no GC should not happen simulatenously. // Reading during Pinning is safe, and requires no lock. - PinLock() func() + PinLock() Unlocker // GcRequested returns true if GCLock has been called and is waiting to // take the lock @@ -198,16 +198,29 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { return output, nil } -func (bs *blockstore) GCLock() func() { +type Unlocker interface { + Unlock() +} + +type unlocker struct { + unlock func() +} + +func (u *unlocker) Unlock() { + u.unlock() + u.unlock = nil // ensure its not called twice +} + +func (bs *blockstore) GCLock() Unlocker { atomic.AddInt32(&bs.gcreq, 1) bs.lk.Lock() atomic.AddInt32(&bs.gcreq, -1) - return bs.lk.Unlock + return &unlocker{bs.lk.Unlock} } -func (bs *blockstore) PinLock() func() { +func (bs *blockstore) PinLock() Unlocker { bs.lk.RLock() - return bs.lk.RUnlock + return &unlocker{bs.lk.RUnlock} } func (bs *blockstore) GCRequested() bool { diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index 90109e8a2..2567a7216 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -59,11 +59,11 @@ func (w *writecache) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { return w.blockstore.AllKeysChan(ctx) } -func (w *writecache) GCLock() func() { +func (w *writecache) GCLock() Unlocker { return w.blockstore.(GCBlockstore).GCLock() } -func (w *writecache) PinLock() func() { +func (w *writecache) PinLock() Unlocker { return w.blockstore.(GCBlockstore).PinLock() } From 2a3fd08f2ca65bba2955d36d351431b6b07d31a7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 3 Feb 2016 20:45:12 -0800 Subject: [PATCH 1158/3817] introduce concept of filedescriptors to mfs, adjust fuse code to use them License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@3dc110f8742708a8040a847933525cbf18a79ed0 --- mfs/dir.go | 12 +-- mfs/fd.go | 151 ++++++++++++++++++++++++++++ mfs/file.go | 174 ++++++++++++--------------------- mfs/mfs_test.go | 255 ++++++++++++++++++++++++++++++++++++++++++++---- mfs/ops.go | 78 +-------------- mfs/system.go | 11 ++- 6 files changed, 462 insertions(+), 219 deletions(-) create mode 100644 mfs/fd.go diff --git a/mfs/dir.go b/mfs/dir.go index d437b28d7..fc949621a 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -183,8 +183,8 @@ type NodeListing struct { } func (d *Directory) ListNames() []string { - d.Lock() - defer d.Unlock() + d.lock.Lock() + defer d.lock.Unlock() names := make(map[string]struct{}) for n, _ := range d.childDirs { @@ -391,11 +391,3 @@ func (d *Directory) GetNode() (*dag.Node, error) { return d.node.Copy(), nil } - -func (d *Directory) Lock() { - d.lock.Lock() -} - -func (d *Directory) Unlock() { - d.lock.Unlock() -} diff --git a/mfs/fd.go b/mfs/fd.go new file mode 100644 index 000000000..2d3f2f3d0 --- /dev/null +++ b/mfs/fd.go @@ -0,0 +1,151 @@ +package mfs + +import ( + "fmt" + "io" + + mod "github.com/ipfs/go-ipfs/unixfs/mod" + + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" +) + +type FileDescriptor interface { + io.Reader + CtxReadFull(context.Context, []byte) (int, error) + + io.Writer + io.WriterAt + + io.Closer + io.Seeker + + Truncate(int64) error + Size() (int64, error) + Sync() error + Flush() error +} + +type fileDescriptor struct { + inode *File + mod *mod.DagModifier + perms int + sync bool + hasChanges bool + + closed bool +} + +// Size returns the size of the file referred to by this descriptor +func (fi *fileDescriptor) Size() (int64, error) { + return fi.mod.Size() +} + +// Truncate truncates the file to size +func (fi *fileDescriptor) Truncate(size int64) error { + if fi.perms == OpenReadOnly { + return fmt.Errorf("cannot call truncate on readonly file descriptor") + } + fi.hasChanges = true + return fi.mod.Truncate(size) +} + +// Write writes the given data to the file at its current offset +func (fi *fileDescriptor) Write(b []byte) (int, error) { + if fi.perms == OpenReadOnly { + return 0, fmt.Errorf("cannot write on not writeable descriptor") + } + fi.hasChanges = true + return fi.mod.Write(b) +} + +// Read reads into the given buffer from the current offset +func (fi *fileDescriptor) Read(b []byte) (int, error) { + if fi.perms == OpenWriteOnly { + return 0, fmt.Errorf("cannot read on write-only descriptor") + } + return fi.mod.Read(b) +} + +// Read reads into the given buffer from the current offset +func (fi *fileDescriptor) CtxReadFull(ctx context.Context, b []byte) (int, error) { + if fi.perms == OpenWriteOnly { + return 0, fmt.Errorf("cannot read on write-only descriptor") + } + return fi.mod.CtxReadFull(ctx, b) +} + +// Close flushes, then propogates the modified dag node up the directory structure +// and signals a republish to occur +func (fi *fileDescriptor) Close() error { + defer func() { + switch fi.perms { + case OpenReadOnly: + fi.inode.desclock.RUnlock() + case OpenWriteOnly, OpenReadWrite: + fi.inode.desclock.Unlock() + } + }() + + if fi.closed { + panic("attempted to close file descriptor twice!") + } + + if fi.hasChanges { + err := fi.mod.Sync() + if err != nil { + return err + } + + fi.hasChanges = false + + // explicitly stay locked for flushUp call, + // it will manage the lock for us + return fi.flushUp(fi.sync) + } + + return nil +} + +func (fi *fileDescriptor) Sync() error { + return fi.flushUp(false) +} + +func (fi *fileDescriptor) Flush() error { + return fi.flushUp(true) +} + +// flushUp syncs the file and adds it to the dagservice +// it *must* be called with the File's lock taken +func (fi *fileDescriptor) flushUp(fullsync bool) error { + nd, err := fi.mod.GetNode() + if err != nil { + return err + } + + _, err = fi.inode.dserv.Add(nd) + if err != nil { + return err + } + + fi.inode.nodelk.Lock() + fi.inode.node = nd + name := fi.inode.name + parent := fi.inode.parent + fi.inode.nodelk.Unlock() + + return parent.closeChild(name, nd, fullsync) +} + +// Seek implements io.Seeker +func (fi *fileDescriptor) Seek(offset int64, whence int) (int64, error) { + return fi.mod.Seek(offset, whence) +} + +// Write At writes the given bytes at the offset 'at' +func (fi *fileDescriptor) WriteAt(b []byte, at int64) (int, error) { + if fi.perms == OpenReadOnly { + return 0, fmt.Errorf("cannot write on not writeable descriptor") + } + fi.hasChanges = true + return fi.mod.WriteAt(b, at) +} diff --git a/mfs/file.go b/mfs/file.go index 99dfff0df..578da98f6 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -1,10 +1,12 @@ package mfs import ( + "fmt" "sync" chunk "github.com/ipfs/go-ipfs/importer/chunk" dag "github.com/ipfs/go-ipfs/merkledag" + ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" @@ -13,150 +15,98 @@ import ( type File struct { parent childCloser - name string - hasChanges bool + name string - dserv dag.DAGService - mod *mod.DagModifier - lock sync.Mutex + desclock sync.RWMutex + + dserv dag.DAGService + node *dag.Node + nodelk sync.Mutex } // NewFile returns a NewFile object with the given parameters func NewFile(name string, node *dag.Node, parent childCloser, dserv dag.DAGService) (*File, error) { - dmod, err := mod.NewDagModifier(context.Background(), node, dserv, chunk.DefaultSplitter) - if err != nil { - return nil, err - } - return &File{ dserv: dserv, parent: parent, name: name, - mod: dmod, + node: node, }, nil } -// Write writes the given data to the file at its current offset -func (fi *File) Write(b []byte) (int, error) { - fi.Lock() - defer fi.Unlock() - fi.hasChanges = true - return fi.mod.Write(b) -} - -// Read reads into the given buffer from the current offset -func (fi *File) Read(b []byte) (int, error) { - fi.Lock() - defer fi.Unlock() - return fi.mod.Read(b) -} - -// Read reads into the given buffer from the current offset -func (fi *File) CtxReadFull(ctx context.Context, b []byte) (int, error) { - fi.Lock() - defer fi.Unlock() - return fi.mod.CtxReadFull(ctx, b) -} +const ( + OpenReadOnly = iota + OpenWriteOnly + OpenReadWrite +) -// Close flushes, then propogates the modified dag node up the directory structure -// and signals a republish to occur -func (fi *File) Close() error { - fi.Lock() - if fi.hasChanges { - err := fi.mod.Sync() - if err != nil { - fi.Unlock() - return err - } - - fi.hasChanges = false - - // explicitly stay locked for flushUp call, - // it will manage the lock for us - return fi.flushUp(true) +func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { + fi.nodelk.Lock() + node := fi.node + fi.nodelk.Unlock() + + switch flags { + case OpenReadOnly: + fi.desclock.RLock() + case OpenWriteOnly, OpenReadWrite: + fi.desclock.Lock() + default: + // TODO: support other modes + return nil, fmt.Errorf("mode not supported") } - fi.Unlock() - return nil -} - -// flushUp syncs the file and adds it to the dagservice -// it *must* be called with the File's lock taken -func (fi *File) flushUp(fullsync bool) error { - nd, err := fi.mod.GetNode() + dmod, err := mod.NewDagModifier(context.TODO(), node, fi.dserv, chunk.DefaultSplitter) if err != nil { - fi.Unlock() - return err + return nil, err } - _, err = fi.dserv.Add(nd) + return &fileDescriptor{ + inode: fi, + perms: flags, + sync: sync, + mod: dmod, + }, nil +} + +// Size returns the size of this file +func (fi *File) Size() (int64, error) { + fi.nodelk.Lock() + defer fi.nodelk.Unlock() + pbd, err := ft.FromBytes(fi.node.Data) if err != nil { - fi.Unlock() - return err + return 0, err } - name := fi.name - parent := fi.parent - - // explicit unlock *only* before closeChild call - fi.Unlock() - return parent.closeChild(name, nd, fullsync) + return int64(pbd.GetFilesize()), nil } -// Sync flushes the changes in the file to disk -func (fi *File) Sync() error { - fi.Lock() - return fi.flushUp(false) +// GetNode returns the dag node associated with this file +func (fi *File) GetNode() (*dag.Node, error) { + fi.nodelk.Lock() + defer fi.nodelk.Unlock() + return fi.node, nil } -// Seek implements io.Seeker -func (fi *File) Seek(offset int64, whence int) (int64, error) { - fi.Lock() - defer fi.Unlock() - return fi.mod.Seek(offset, whence) -} +func (fi *File) Flush() error { + // open the file in fullsync mode + fd, err := fi.Open(OpenWriteOnly, true) + if err != nil { + return err + } -// Write At writes the given bytes at the offset 'at' -func (fi *File) WriteAt(b []byte, at int64) (int, error) { - fi.Lock() - defer fi.Unlock() - fi.hasChanges = true - return fi.mod.WriteAt(b, at) -} + defer fd.Close() -// Size returns the size of this file -func (fi *File) Size() (int64, error) { - fi.Lock() - defer fi.Unlock() - return fi.mod.Size() + return fd.Flush() } -// GetNode returns the dag node associated with this file -func (fi *File) GetNode() (*dag.Node, error) { - fi.Lock() - defer fi.Unlock() - return fi.mod.GetNode() -} - -// Truncate truncates the file to size -func (fi *File) Truncate(size int64) error { - fi.Lock() - defer fi.Unlock() - fi.hasChanges = true - return fi.mod.Truncate(size) +func (fi *File) Sync() error { + // just being able to take the writelock means the descriptor is synced + fi.desclock.Lock() + fi.desclock.Unlock() + return nil } // Type returns the type FSNode this is func (fi *File) Type() NodeType { return TFile } - -// Lock the file -func (fi *File) Lock() { - fi.lock.Lock() -} - -// Unlock the file -func (fi *File) Unlock() { - fi.lock.Unlock() -} diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 38237c218..b8ba320ce 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -9,7 +9,9 @@ import ( "math/rand" "os" "sort" + "sync" "testing" + "time" randbo "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/dustin/randbo" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" @@ -38,6 +40,10 @@ func getDagserv(t *testing.T) dag.DAGService { func getRandFile(t *testing.T, ds dag.DAGService, size int64) *dag.Node { r := io.LimitReader(u.NewTimeSeededRand(), size) + return fileNodeFromReader(t, ds, r) +} + +func fileNodeFromReader(t *testing.T, ds dag.DAGService, r io.Reader) *dag.Node { nd, err := importer.BuildDagFromReader(ds, chunk.DefaultSplitter(r)) if err != nil { t.Fatal(err) @@ -143,7 +149,12 @@ func assertFileAtPath(ds dag.DAGService, root *Directory, exp *dag.Node, pth str return fmt.Errorf("%s was not a file!", pth) } - out, err := ioutil.ReadAll(file) + rfd, err := file.Open(OpenReadOnly, false) + if err != nil { + return err + } + + out, err := ioutil.ReadAll(rfd) if err != nil { return err } @@ -374,6 +385,11 @@ func TestMfsFile(t *testing.T) { t.Fatal("some is seriously wrong here") } + wfd, err := fi.Open(OpenReadWrite, true) + if err != nil { + t.Fatal(err) + } + // assert size is as expected size, err := fi.Size() if size != int64(fisize) { @@ -382,7 +398,7 @@ func TestMfsFile(t *testing.T) { // write to beginning of file b := []byte("THIS IS A TEST") - n, err := fi.Write(b) + n, err := wfd.Write(b) if err != nil { t.Fatal(err) } @@ -392,19 +408,19 @@ func TestMfsFile(t *testing.T) { } // sync file - err = fi.Sync() + err = wfd.Sync() if err != nil { t.Fatal(err) } // make sure size hasnt changed - size, err = fi.Size() + size, err = wfd.Size() if size != int64(fisize) { t.Fatal("size isnt correct") } // seek back to beginning - ns, err := fi.Seek(0, os.SEEK_SET) + ns, err := wfd.Seek(0, os.SEEK_SET) if err != nil { t.Fatal(err) } @@ -415,7 +431,7 @@ func TestMfsFile(t *testing.T) { // read back bytes we wrote buf := make([]byte, len(b)) - n, err = fi.Read(buf) + n, err = wfd.Read(buf) if err != nil { t.Fatal(err) } @@ -429,12 +445,12 @@ func TestMfsFile(t *testing.T) { } // truncate file to ten bytes - err = fi.Truncate(10) + err = wfd.Truncate(10) if err != nil { t.Fatal(err) } - size, err = fi.Size() + size, err = wfd.Size() if err != nil { t.Fatal(err) } @@ -445,7 +461,7 @@ func TestMfsFile(t *testing.T) { // 'writeAt' to extend it data := []byte("this is a test foo foo foo") - nwa, err := fi.WriteAt(data, 5) + nwa, err := wfd.WriteAt(data, 5) if err != nil { t.Fatal(err) } @@ -455,7 +471,7 @@ func TestMfsFile(t *testing.T) { } // assert size once more - size, err = fi.Size() + size, err = wfd.Size() if err != nil { t.Fatal(err) } @@ -464,14 +480,14 @@ func TestMfsFile(t *testing.T) { t.Fatal("size was incorrect") } - // make sure we can get node. TODO: verify it later - _, err = fi.GetNode() + // close it out! + err = wfd.Close() if err != nil { t.Fatal(err) } - // close it out! - err = fi.Close() + // make sure we can get node. TODO: verify it later + _, err = fi.GetNode() if err != nil { t.Fatal(err) } @@ -529,13 +545,18 @@ func actorMakeFile(d *Directory) error { return err } + wfd, err := f.Open(OpenWriteOnly, true) + if err != nil { + return err + } + r := io.LimitReader(randbo.New(), int64(77*rand.Intn(123))) - _, err = io.Copy(f, r) + _, err = io.Copy(wfd, r) if err != nil { return err } - err = f.Close() + err = wfd.Close() if err != nil { return err } @@ -630,9 +651,14 @@ func actorWriteFile(d *Directory) error { return err } + wfd, err := fi.Open(OpenWriteOnly, true) + if err != nil { + return err + } + offset := rand.Int63n(s) - n, err := fi.WriteAt(buf, offset) + n, err := wfd.WriteAt(buf, offset) if err != nil { return err } @@ -640,7 +666,7 @@ func actorWriteFile(d *Directory) error { return fmt.Errorf("didnt write enough") } - return fi.Close() + return wfd.Close() } func actorReadFile(d *Directory) error { @@ -657,12 +683,17 @@ func actorReadFile(d *Directory) error { return err } - _, err = ioutil.ReadAll(fi) + rfd, err := fi.Open(OpenReadOnly, false) if err != nil { return err } - return fi.Close() + _, err = ioutil.ReadAll(rfd) + if err != nil { + return err + } + + return rfd.Close() } func testActor(rt *Root, iterations int, errs chan error) { @@ -780,3 +811,187 @@ func TestFlushing(t *testing.T) { t.Fatalf("dag looks wrong, expected %s, but got %s", exp, rnk.B58String()) } } + +func readFile(rt *Root, path string, offset int64, buf []byte) error { + n, err := Lookup(rt, path) + if err != nil { + return err + } + + fi, ok := n.(*File) + if !ok { + return fmt.Errorf("%s was not a file", path) + } + + fd, err := fi.Open(OpenReadOnly, false) + if err != nil { + return err + } + + _, err = fd.Seek(offset, os.SEEK_SET) + if err != nil { + return err + } + + nread, err := fd.Read(buf) + if err != nil { + return err + } + if nread != len(buf) { + return fmt.Errorf("didnt read enough!") + } + + return fd.Close() +} + +func TestConcurrentReads(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ds, rt := setupRoot(ctx, t) + + rootdir := rt.GetValue().(*Directory) + + path := "a/b/c" + d := mkdirP(t, rootdir, path) + + buf := make([]byte, 2048) + randbo.New().Read(buf) + + fi := fileNodeFromReader(t, ds, bytes.NewReader(buf)) + err := d.AddChild("afile", fi) + if err != nil { + t.Fatal(err) + } + + var wg sync.WaitGroup + nloops := 100 + for i := 0; i < 10; i++ { + wg.Add(1) + go func(me int) { + defer wg.Done() + mybuf := make([]byte, len(buf)) + for j := 0; j < nloops; j++ { + offset := rand.Intn(len(buf)) + length := rand.Intn(len(buf) - offset) + + err := readFile(rt, "/a/b/c/afile", int64(offset), mybuf[:length]) + if err != nil { + t.Error("readfile failed: ", err) + return + } + + if !bytes.Equal(mybuf[:length], buf[offset:offset+length]) { + t.Error("incorrect read!") + } + } + }(i) + } + wg.Wait() +} + +func TestFileDescriptors(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ds, rt := setupRoot(ctx, t) + dir := rt.GetValue().(*Directory) + + nd := &dag.Node{Data: ft.FilePBData(nil, 0)} + fi, err := NewFile("test", nd, dir, ds) + if err != nil { + t.Fatal(err) + } + + // test read only + rfd1, err := fi.Open(OpenReadOnly, false) + if err != nil { + t.Fatal(err) + } + + err = rfd1.Truncate(0) + if err == nil { + t.Fatal("shouldnt be able to truncate readonly fd") + } + + _, err = rfd1.Write([]byte{}) + if err == nil { + t.Fatal("shouldnt be able to write to readonly fd") + } + + _, err = rfd1.Read([]byte{}) + if err != nil { + t.Fatalf("expected to be able to read from file: %s", err) + } + + done := make(chan struct{}) + go func() { + defer close(done) + // can open second readonly file descriptor + rfd2, err := fi.Open(OpenReadOnly, false) + if err != nil { + t.Error(err) + return + } + + rfd2.Close() + }() + + select { + case <-time.After(time.Second): + t.Fatal("open second file descriptor failed") + case <-done: + } + + if t.Failed() { + return + } + + // test not being able to open for write until reader are closed + done = make(chan struct{}) + go func() { + defer close(done) + wfd1, err := fi.Open(OpenWriteOnly, true) + if err != nil { + t.Error(err) + } + + wfd1.Close() + }() + + select { + case <-time.After(time.Millisecond * 200): + case <-done: + if t.Failed() { + return + } + + t.Fatal("shouldnt have been able to open file for writing") + } + + err = rfd1.Close() + if err != nil { + t.Fatal(err) + } + + select { + case <-time.After(time.Second): + t.Fatal("should have been able to open write fd after closing read fd") + case <-done: + } + + wfd, err := fi.Open(OpenWriteOnly, true) + if err != nil { + t.Fatal(err) + } + + _, err = wfd.Read([]byte{}) + if err == nil { + t.Fatal("shouldnt have been able to read from write only filedescriptor") + } + + _, err = wfd.Write([]byte{}) + if err != nil { + t.Fatal(err) + } +} diff --git a/mfs/ops.go b/mfs/ops.go index 9bc59994c..b02d64fd1 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -196,87 +196,17 @@ func DirLookup(d *Directory, pth string) (FSNode, error) { return cur, nil } -func FlushPath(r *Root, pth string) error { - parts := path.SplitList(strings.Trim(pth, "/")) - if len(parts) == 1 && parts[0] == "" { - parts = nil - } - - d, ok := r.GetValue().(*Directory) - if !ok { - return errors.New("mfs root somehow didnt point to a directory") - } - - nd, err := flushPathRec(d, parts) +func FlushPath(rt *Root, pth string) error { + nd, err := Lookup(rt, pth) if err != nil { return err } - k, err := nd.Key() + err = nd.Flush() if err != nil { return err } - r.repub.Update(k) - r.repub.WaitPub() - + rt.repub.WaitPub() return nil } - -func flushPathRec(d *Directory, parts []string) (*dag.Node, error) { - if len(parts) == 0 { - nd, err := d.GetNode() - if err != nil { - return nil, err - } - - return nd, nil - } - - d.Lock() - defer d.Unlock() - - next, err := d.childUnsync(parts[0]) - if err != nil { - log.Errorf("childnode: %q %q", parts[0], err) - return nil, err - } - - var ndagnode *dag.Node - switch next := next.(type) { - case *Directory: - nd, err := flushPathRec(next, parts[1:]) - if err != nil { - return nil, err - } - - ndagnode = nd - - case *File: - if len(parts) > 1 { - return nil, fmt.Errorf("%s is a file, not a directory", parts[0]) - } - - child, err := next.GetNode() - if err != nil { - return nil, err - } - - ndagnode = child - default: - return nil, fmt.Errorf("unrecognized FSNode type: %#v", next) - } - - newnode, err := d.node.UpdateNodeLink(parts[0], ndagnode) - if err != nil { - return nil, err - } - - _, err = d.dserv.Add(newnode) - if err != nil { - return nil, err - } - - d.node = newnode - return newnode, nil -} diff --git a/mfs/system.go b/mfs/system.go index 454577c45..2ccc6650c 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -42,9 +42,8 @@ const ( // FSNode represents any node (directory, root, or file) in the mfs filesystem type FSNode interface { GetNode() (*dag.Node, error) + Flush() error Type() NodeType - Lock() - Unlock() } // Root represents the root of a filesystem tree @@ -210,6 +209,13 @@ func (p *Republisher) pubNow() { } func (p *Republisher) WaitPub() { + p.lk.Lock() + consistent := p.lastpub == p.val + p.lk.Unlock() + if consistent { + return + } + wait := make(chan struct{}) p.pubnowch <- wait <-wait @@ -273,7 +279,6 @@ func (np *Republisher) publish(ctx context.Context) error { topub := np.val np.lk.Unlock() - log.Info("Publishing Changes!") err := np.pubfunc(ctx, topub) if err != nil { return err From ccb3f15863c0a8af115258eedd9b07020a9a6d4a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 3 Feb 2016 20:45:12 -0800 Subject: [PATCH 1159/3817] introduce concept of filedescriptors to mfs, adjust fuse code to use them License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@ff4ffe58fd28133e68c0b228a53ab47c3b57897d --- unixfs/mod/dagmodifier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 5306399f6..0f5793867 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -372,7 +372,7 @@ func (dm *DagModifier) Seek(offset int64, whence int) (int64, error) { case os.SEEK_SET: newoffset = uint64(offset) case os.SEEK_END: - return 0, ErrSeekEndNotImpl + newoffset = uint64(fisize) - uint64(offset) default: return 0, ErrUnrecognizedWhence } From 089c37c4b4ff1df73c0483b78c9db9a1653eb764 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 10 Feb 2016 16:07:06 -0800 Subject: [PATCH 1160/3817] bump kvalue from 10 to 20 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@b2c9738c80cea987ccac102e943654637875b8c9 --- routing/dht/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/util.go b/routing/dht/util.go index 2b0c1e2a2..a605759a9 100644 --- a/routing/dht/util.go +++ b/routing/dht/util.go @@ -8,7 +8,7 @@ import ( var PoolSize = 6 // K is the maximum number of requests to perform before returning failure. -var KValue = 10 +var KValue = 20 // Alpha is the concurrency factor for asynchronous requests. var AlphaValue = 3 From 9a83304f20da9025baf9eed95819fa36e0e940bc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 10 Feb 2016 17:03:22 -0800 Subject: [PATCH 1161/3817] put pubkey and ipns entry to dht in parallel License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@d4d2314f568c089aec770742a38a50b5495a05c3 --- namesys/publisher.go | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 981814f52..67fcb26ef 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -146,12 +146,22 @@ func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqn entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds())) } - err = PublishEntry(ctx, r, ipnskey, entry) + errs := make(chan error) + + go func() { + errs <- PublishEntry(ctx, r, ipnskey, entry) + }() + + go func() { + errs <- PublishPublicKey(ctx, r, namekey, k.GetPublic()) + }() + + err = waitOnErrChan(ctx, errs) if err != nil { return err } - err = PublishPublicKey(ctx, r, namekey, k.GetPublic()) + err = waitOnErrChan(ctx, errs) if err != nil { return err } @@ -159,6 +169,19 @@ func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqn return nil } +func waitOnErrChan(ctx context.Context, errs chan error) error { + select { + case err := <-errs: + if err != nil { + return err + } + case <-ctx.Done(): + return ctx.Err() + } + + return nil +} + func PublishPublicKey(ctx context.Context, r routing.IpfsRouting, k key.Key, pubk ci.PubKey) error { log.Debugf("Storing pubkey at: %s", k) pkbytes, err := pubk.Bytes() From 6f6b622d1119d56b1b5c2206ea402d864787f6fd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 10 Feb 2016 22:09:43 -0800 Subject: [PATCH 1162/3817] cleanup waitfunc License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@bb93660f70ad592352959ad4eddb29d29e4a7eb8 --- namesys/publisher.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 67fcb26ef..8cf44c9d7 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -172,14 +172,10 @@ func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqn func waitOnErrChan(ctx context.Context, errs chan error) error { select { case err := <-errs: - if err != nil { - return err - } + return err case <-ctx.Done(): return ctx.Err() } - - return nil } func PublishPublicKey(ctx context.Context, r routing.IpfsRouting, k key.Key, pubk ci.PubKey) error { From c0ee5062d4d747c24f21c11e248f18a2d986a774 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Thu, 11 Feb 2016 13:03:32 -0500 Subject: [PATCH 1163/3817] Capitalized could License: MIT Signed-off-by: Richard Littauer This commit was moved from ipfs/go-namesys@dc13affa13edcaf81162dde045ea403f090256ca --- namesys/interface.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index 68933bfe0..7978d74dd 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -50,14 +50,14 @@ const ( ) // ErrResolveFailed signals an error when attempting to resolve. -var ErrResolveFailed = errors.New("could not resolve name.") +var ErrResolveFailed = errors.New("Could not resolve name.") // ErrResolveRecursion signals a recursion-depth limit. var ErrResolveRecursion = errors.New( - "could not resolve name (recursion limit exceeded).") + "Could not resolve name (recursion limit exceeded).") // ErrPublishFailed signals an error when attempting to publish. -var ErrPublishFailed = errors.New("could not publish name.") +var ErrPublishFailed = errors.New("Could not publish name.") // Namesys represents a cohesive name publishing and resolving system. // From 66c56b923a1f57c8f75184f0c85f6a4c5f2dffef Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 11 Feb 2016 11:09:09 -0800 Subject: [PATCH 1164/3817] fix race conditions in tests License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@1553868c9917d849bc4fd8aa00cc2080816364f0 --- namesys/resolve_test.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index ff1b27f54..555b3055a 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,6 +6,7 @@ import ( "time" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" @@ -16,8 +17,10 @@ import ( ) func TestRoutingResolve(t *testing.T) { - d := mockrouting.NewServer().Client(testutil.RandIdentityOrFatal(t)) - dstore := ds.NewMapDatastore() + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + serv := mockrouting.NewServer() + id := testutil.RandIdentityOrFatal(t) + d := serv.ClientWithDatastore(context.Background(), id, dstore) resolver := NewRoutingResolver(d, 0) publisher := NewRoutingPublisher(d, dstore) @@ -50,7 +53,7 @@ func TestRoutingResolve(t *testing.T) { } func TestPrexistingExpiredRecord(t *testing.T) { - dstore := ds.NewMapDatastore() + dstore := dssync.MutexWrap(ds.NewMapDatastore()) d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore) resolver := NewRoutingResolver(d, 0) @@ -87,7 +90,7 @@ func TestPrexistingExpiredRecord(t *testing.T) { } func TestPrexistingRecord(t *testing.T) { - dstore := ds.NewMapDatastore() + dstore := dssync.MutexWrap(ds.NewMapDatastore()) d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore) resolver := NewRoutingResolver(d, 0) From 39bc0f01cf078d9b3c62f2a4af5ce8f66765b936 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 11 Feb 2016 11:09:09 -0800 Subject: [PATCH 1165/3817] fix race conditions in tests License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@ab366112d87ec1aeabcb2857ae8a0c60528ccf14 --- routing/mock/centralized_server.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 8e8d5d0b2..c05ca9460 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -6,6 +6,7 @@ import ( "time" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/util/testutil" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" @@ -74,7 +75,7 @@ func (rs *s) Providers(k key.Key) []peer.PeerInfo { } func (rs *s) Client(p testutil.Identity) Client { - return rs.ClientWithDatastore(context.Background(), p, ds.NewMapDatastore()) + return rs.ClientWithDatastore(context.Background(), p, dssync.MutexWrap(ds.NewMapDatastore())) } func (rs *s) ClientWithDatastore(_ context.Context, p testutil.Identity, datastore ds.Datastore) Client { From bd5e193a183d3e1584c64641c62b8fbee8738f5b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 8 Feb 2016 16:45:15 -0800 Subject: [PATCH 1166/3817] remove goprocess from godeps, use gx vendored one License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@66086e9b7df9ab974d1e932c5b67f19b4da3f242 --- routing/dht/dht.go | 4 ++-- routing/dht/dht_bootstrap.go | 4 ++-- routing/dht/providers.go | 4 ++-- routing/dht/query.go | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index eadd5b4be..ddab5044f 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -22,8 +22,8 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" - goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" + goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" + goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 3865bca13..b059c11d6 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -12,8 +12,8 @@ import ( u "github.com/ipfs/go-ipfs/util" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" - goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" - periodicproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/periodic" + goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" + periodicproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/periodic" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index c0c16c54e..2f0a8e64d 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -3,9 +3,9 @@ package dht import ( "time" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" - goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" key "github.com/ipfs/go-ipfs/blocks/key" + goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" + goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/routing/dht/query.go b/routing/dht/query.go index 53e75fecb..e7dc7a8e1 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -13,8 +13,8 @@ import ( queue "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer/queue" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" - process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" - ctxproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" + process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" + ctxproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From 99d14e234327575081ee2921b36ad5d47f564643 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 8 Feb 2016 16:45:15 -0800 Subject: [PATCH 1167/3817] remove goprocess from godeps, use gx vendored one License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@356ca49837876b87be8bf7973b2fb7a1870aa99a --- namesys/republisher/repub.go | 4 ++-- namesys/republisher/repub_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 37d4d19e2..4c4ca9386 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -15,8 +15,8 @@ import ( proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" - gpctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context" + goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" + gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index a56111874..31195892a 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" + goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" "github.com/ipfs/go-ipfs/core" From 73509d6d6dd21b76e0d8507c5fd07688a24feefe Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 8 Feb 2016 23:10:19 -0800 Subject: [PATCH 1168/3817] remove randbo from godeps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@f97b811bc529af2fa02daec4ba43d64d43e6f2c2 --- mfs/mfs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index b8ba320ce..060f8083c 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -13,10 +13,10 @@ import ( "testing" "time" - randbo "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/dustin/randbo" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/path" + randbo "gx/ipfs/QmYvsG72GsfLgUeSojXArjnU6L4Wmwk7wuAxtNLuyXcc1T/randbo" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" From 6e6af6ddef736fb2bffaefb79ff3a39ce626fe29 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 9 Feb 2016 10:07:20 -0800 Subject: [PATCH 1169/3817] remove gogo-protobuf from godeps, use gx vendored License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@fba232f88149839ab994eb4fb321c65f98f67b40 --- routing/dht/dht.go | 2 +- routing/dht/dht_net.go | 2 +- routing/dht/ext_test.go | 2 +- routing/dht/handlers.go | 2 +- routing/dht/pb/dht.pb.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/offline/offline.go | 2 +- routing/record/record.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index ddab5044f..aad30fa51 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -20,7 +20,7 @@ import ( protocol "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/protocol" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index e88a94dc6..d54c678ef 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -4,7 +4,7 @@ import ( "errors" "time" - ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" + ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" pb "github.com/ipfs/go-ipfs/routing/dht/pb" inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index adff49c90..dad0ac645 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" + ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 265ff49f9..84320ab5d 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" diff --git a/routing/dht/pb/dht.pb.go b/routing/dht/pb/dht.pb.go index 2d7ad1d9d..24dc2e5be 100644 --- a/routing/dht/pb/dht.pb.go +++ b/routing/dht/pb/dht.pb.go @@ -14,7 +14,7 @@ It has these top-level messages: */ package dht_pb -import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" +import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 407f0c094..15e7eebeb 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -4,7 +4,7 @@ import ( "errors" "time" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 7e5f80275..5dd6ceb87 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -4,7 +4,7 @@ import ( "errors" "time" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" diff --git a/routing/record/record.go b/routing/record/record.go index 2adcc21bc..78051e696 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -3,7 +3,7 @@ package record import ( "bytes" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 8838b7aeb..8f7d063b6 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -5,7 +5,7 @@ import ( "errors" "time" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index c067f634b..4fc7b7f04 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -1,7 +1,7 @@ package proxy import ( - ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" + ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 00892de88..e8ec2f434 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -3,7 +3,7 @@ package proxy import ( "errors" - ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" + ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 46caf03ea..8c79d2172 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" From 67c2a3a248fc1a970e3fff2a24e321507d25e620 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 9 Feb 2016 10:07:20 -0800 Subject: [PATCH 1170/3817] remove gogo-protobuf from godeps, use gx vendored License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@bfefbe90a672edf3f08f1de346b133001e3af7b9 --- ipld/merkledag/pb/merkledag.pb.go | 4 ++-- ipld/merkledag/pb/merkledagpb_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/pb/merkledag.pb.go b/ipld/merkledag/pb/merkledag.pb.go index a0dfa91f8..a4c73580f 100644 --- a/ipld/merkledag/pb/merkledag.pb.go +++ b/ipld/merkledag/pb/merkledag.pb.go @@ -14,14 +14,14 @@ */ package merkledag_pb -import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" +import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" import math "math" // discarding unused import gogoproto "code.google.com/p/gogoprotobuf/gogoproto/gogo.pb" import io "io" import fmt "fmt" -import github_com_gogo_protobuf_proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" +import github_com_gogo_protobuf_proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" import strings "strings" import reflect "reflect" diff --git a/ipld/merkledag/pb/merkledagpb_test.go b/ipld/merkledag/pb/merkledagpb_test.go index dd55e2230..b59fca7fa 100644 --- a/ipld/merkledag/pb/merkledagpb_test.go +++ b/ipld/merkledag/pb/merkledagpb_test.go @@ -17,7 +17,7 @@ package merkledag_pb import testing "testing" import math_rand "math/rand" import time "time" -import github_com_gogo_protobuf_proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" +import github_com_gogo_protobuf_proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" import encoding_json "encoding/json" import fmt "fmt" import go_parser "go/parser" From 3158488783e4d37758a33ce1fcd16bfc171abc08 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 9 Feb 2016 10:07:20 -0800 Subject: [PATCH 1171/3817] remove gogo-protobuf from godeps, use gx vendored License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@714fcc1714c62394a4516c9b4015097c74adf666 --- unixfs/archive/tar/writer.go | 2 +- unixfs/format.go | 2 +- unixfs/format_test.go | 2 +- unixfs/io/dagreader.go | 2 +- unixfs/mod/dagmodifier.go | 2 +- unixfs/pb/unixfs.pb.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 2d470b667..32596618a 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -6,7 +6,7 @@ import ( "path" "time" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" cxt "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" mdag "github.com/ipfs/go-ipfs/merkledag" diff --git a/unixfs/format.go b/unixfs/format.go index 0bf569438..af62f994f 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -6,7 +6,7 @@ package unixfs import ( "errors" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pb "github.com/ipfs/go-ipfs/unixfs/pb" ) diff --git a/unixfs/format_test.go b/unixfs/format_test.go index f178b5615..ac35db56f 100644 --- a/unixfs/format_test.go +++ b/unixfs/format_test.go @@ -3,7 +3,7 @@ package unixfs import ( "testing" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pb "github.com/ipfs/go-ipfs/unixfs/pb" ) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index a1a104e44..5b2a8b112 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -7,7 +7,7 @@ import ( "io" "os" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" mdag "github.com/ipfs/go-ipfs/merkledag" diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 0f5793867..38d4459d9 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -6,7 +6,7 @@ import ( "io" "os" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/unixfs/pb/unixfs.pb.go b/unixfs/pb/unixfs.pb.go index e89fca29a..55348ad76 100644 --- a/unixfs/pb/unixfs.pb.go +++ b/unixfs/pb/unixfs.pb.go @@ -14,7 +14,7 @@ It has these top-level messages: */ package unixfs_pb -import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" +import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. From 65e7875382410d6032d59fe0491040c969882e7b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 9 Feb 2016 10:07:20 -0800 Subject: [PATCH 1172/3817] remove gogo-protobuf from godeps, use gx vendored License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@75d030b04a946c17cc17bee92020de231aca99f6 --- pinning/pinner/internal/pb/header.pb.go | 2 +- pinning/pinner/set.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/internal/pb/header.pb.go b/pinning/pinner/internal/pb/header.pb.go index eafb246e7..b77d37743 100644 --- a/pinning/pinner/internal/pb/header.pb.go +++ b/pinning/pinner/internal/pb/header.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package pb -import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" +import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index a07762a31..f3d825818 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -11,7 +11,7 @@ import ( "sort" "unsafe" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" From cf737bc2b8230856be788fb42e5860bb409b114a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 9 Feb 2016 10:07:20 -0800 Subject: [PATCH 1173/3817] remove gogo-protobuf from godeps, use gx vendored License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@51213941ef479fe74f22796739d8aaee51c6d2bb --- namesys/ipns_select_test.go | 2 +- namesys/pb/namesys.pb.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/routing.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index beeb0ac7c..fb171e901 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" diff --git a/namesys/pb/namesys.pb.go b/namesys/pb/namesys.pb.go index 0508e772d..31e6355d7 100644 --- a/namesys/pb/namesys.pb.go +++ b/namesys/pb/namesys.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package namesys_pb -import proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" +import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. diff --git a/namesys/publisher.go b/namesys/publisher.go index 8cf44c9d7..241b40d3a 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -6,7 +6,7 @@ import ( "fmt" "time" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 4c4ca9386..2647a3954 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -13,7 +13,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" diff --git a/namesys/routing.go b/namesys/routing.go index 933bdd041..7d499fe61 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" lru "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" From 652c3e009050c19ff82dce107879f45b05489708 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 9 Feb 2016 10:56:19 -0800 Subject: [PATCH 1174/3817] Use gx vendored go-ipfs-utils where possible For the rest of the packages in util, move them to thirdparty and update the references. util is gone! License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@ca32b60f28bd64be70c755842a1b2cb9fda39aab --- mfs/mfs_test.go | 2 +- mfs/repub_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 060f8083c..a56b68939 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -28,7 +28,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - u "github.com/ipfs/go-ipfs/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) func getDagserv(t *testing.T) dag.DAGService { diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 4ba7bae4f..32e0b2b27 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -5,7 +5,7 @@ import ( "time" key "github.com/ipfs/go-ipfs/blocks/key" - ci "github.com/ipfs/go-ipfs/util/testutil/ci" + ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From 1510b18838e7841b0e56c2ec445cb89bccd30f03 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 9 Feb 2016 10:56:19 -0800 Subject: [PATCH 1175/3817] Use gx vendored go-ipfs-utils where possible For the rest of the packages in util, move them to thirdparty and update the references. util is gone! License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@52fd0b26429e2fbff2511227a991c9f7f981ef17 --- routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_test.go | 6 +++--- routing/dht/ext_test.go | 2 +- routing/dht/handlers.go | 6 +++--- routing/dht/lookup.go | 2 +- routing/dht/query.go | 6 +++--- routing/dht/routing.go | 2 +- routing/kbucket/table_test.go | 2 +- routing/kbucket/util.go | 2 +- routing/keyspace/xor.go | 2 +- routing/keyspace/xor_test.go | 2 +- routing/mock/centralized_client.go | 6 +++--- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 2 +- routing/record/validation.go | 2 +- 17 files changed, 25 insertions(+), 25 deletions(-) diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index b059c11d6..b544884fc 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -9,7 +9,7 @@ import ( "time" routing "github.com/ipfs/go-ipfs/routing" - u "github.com/ipfs/go-ipfs/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 55ed11c55..3834c75e7 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -17,12 +17,12 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" - u "github.com/ipfs/go-ipfs/util" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" netutil "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/test/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - ci "github.com/ipfs/go-ipfs/util/testutil/ci" - travisci "github.com/ipfs/go-ipfs/util/testutil/ci/travis" + ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" + travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" ) var testCaseValues = map[key.Key][]byte{} diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index dad0ac645..41048df2a 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -16,7 +16,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - u "github.com/ipfs/go-ipfs/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" mocknet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net/mock" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 84320ab5d..ea8996416 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -5,13 +5,13 @@ import ( "fmt" "time" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - u "github.com/ipfs/go-ipfs/util" - lgbl "github.com/ipfs/go-ipfs/util/eventlog/loggables" + lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 140fcae21..2def4046b 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -4,7 +4,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" notif "github.com/ipfs/go-ipfs/notifications" kb "github.com/ipfs/go-ipfs/routing/kbucket" - pset "github.com/ipfs/go-ipfs/util/peerset" + pset "github.com/ipfs/go-ipfs/thirdparty/peerset" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index e7dc7a8e1..518f1e11f 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -6,11 +6,11 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" notif "github.com/ipfs/go-ipfs/notifications" "github.com/ipfs/go-ipfs/routing" - u "github.com/ipfs/go-ipfs/util" - pset "github.com/ipfs/go-ipfs/util/peerset" - todoctr "github.com/ipfs/go-ipfs/util/todocounter" + pset "github.com/ipfs/go-ipfs/thirdparty/peerset" + todoctr "github.com/ipfs/go-ipfs/thirdparty/todocounter" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" queue "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer/queue" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/routing/dht/routing.go b/routing/dht/routing.go index e0feda4ec..5a7430122 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -11,7 +11,7 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - pset "github.com/ipfs/go-ipfs/util/peerset" + pset "github.com/ipfs/go-ipfs/thirdparty/peerset" inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 13f02df89..a023dba4e 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - tu "github.com/ipfs/go-ipfs/util/testutil" + tu "github.com/ipfs/go-ipfs/thirdparty/testutil" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" ) diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 73602d185..df3237822 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" - u "github.com/ipfs/go-ipfs/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" ) diff --git a/routing/keyspace/xor.go b/routing/keyspace/xor.go index 8fae7744f..fd96fd65b 100644 --- a/routing/keyspace/xor.go +++ b/routing/keyspace/xor.go @@ -5,7 +5,7 @@ import ( "crypto/sha256" "math/big" - u "github.com/ipfs/go-ipfs/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) // XORKeySpace is a KeySpace which: diff --git a/routing/keyspace/xor_test.go b/routing/keyspace/xor_test.go index cac274278..a461c094e 100644 --- a/routing/keyspace/xor_test.go +++ b/routing/keyspace/xor_test.go @@ -5,7 +5,7 @@ import ( "math/big" "testing" - u "github.com/ipfs/go-ipfs/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) func TestPrefixLen(t *testing.T) { diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 15e7eebeb..4f29a5776 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -4,15 +4,15 @@ import ( "errors" "time" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - u "github.com/ipfs/go-ipfs/util" - "github.com/ipfs/go-ipfs/util/testutil" + "github.com/ipfs/go-ipfs/thirdparty/testutil" ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index c05ca9460..cc57e6a07 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -8,7 +8,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" - "github.com/ipfs/go-ipfs/util/testutil" + "github.com/ipfs/go-ipfs/thirdparty/testutil" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index d71b24c0b..0bafda9b2 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -6,7 +6,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "github.com/ipfs/go-ipfs/util/testutil" + "github.com/ipfs/go-ipfs/thirdparty/testutil" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 99ef8ba88..96be2a489 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -4,7 +4,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" - "github.com/ipfs/go-ipfs/util/testutil" + "github.com/ipfs/go-ipfs/thirdparty/testutil" mocknet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 75daee9e9..69035c232 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -9,7 +9,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "github.com/ipfs/go-ipfs/util/testutil" + "github.com/ipfs/go-ipfs/thirdparty/testutil" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/record/validation.go b/routing/record/validation.go index 32c33e966..a898259c6 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - u "github.com/ipfs/go-ipfs/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" ) From c5b1e8fab59eede5cfad34f9df205d10d6917814 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 9 Feb 2016 10:56:19 -0800 Subject: [PATCH 1176/3817] Use gx vendored go-ipfs-utils where possible For the rest of the packages in util, move them to thirdparty and update the references. util is gone! License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@f87eb679cae3afc8152889759fc8c233ecf31197 --- ipld/merkledag/coding.go | 4 ++-- ipld/merkledag/merkledag_test.go | 2 +- ipld/merkledag/node.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index b678c3b44..1c57125fb 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -4,10 +4,10 @@ import ( "fmt" "sort" - mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" pb "github.com/ipfs/go-ipfs/merkledag/pb" - u "github.com/ipfs/go-ipfs/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index d9622082e..91bc2c0f7 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -22,7 +22,7 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin" uio "github.com/ipfs/go-ipfs/unixfs/io" - u "github.com/ipfs/go-ipfs/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index c5e1c4e33..b5e95f81b 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -5,7 +5,7 @@ import ( "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" key "github.com/ipfs/go-ipfs/blocks/key" ) From c87778d31ec54e17497294fb8a763b0730b3a01c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 9 Feb 2016 10:56:19 -0800 Subject: [PATCH 1177/3817] Use gx vendored go-ipfs-utils where possible For the rest of the packages in util, move them to thirdparty and update the references. util is gone! License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@40e7d74e907795995026f02b2eb2ae6acd484fe1 --- unixfs/mod/dagmodifier.go | 2 +- unixfs/mod/dagmodifier_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 38d4459d9..ec7072597 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -7,7 +7,7 @@ import ( "os" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 0cd4a2f10..a5ed5dc1b 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -18,7 +18,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - u "github.com/ipfs/go-ipfs/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" From cea66196a47e5bd754af140ddbdb5e89440dcc62 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 9 Feb 2016 10:56:19 -0800 Subject: [PATCH 1178/3817] Use gx vendored go-ipfs-utils where possible For the rest of the packages in util, move them to thirdparty and update the references. util is gone! License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@a25e4c47c28950f3eeade963f8d523a2ac928a59 --- path/path.go | 2 +- path/resolver.go | 2 +- path/resolver_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/path/path.go b/path/path.go index 0891e8466..c06973417 100644 --- a/path/path.go +++ b/path/path.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" - mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" ) // ErrBadPath is returned when a given path is incorrectly formatted diff --git a/path/resolver.go b/path/resolver.go index 569a4d1be..10368bbfd 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -6,7 +6,7 @@ import ( "fmt" "time" - mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/path/resolver_test.go b/path/resolver_test.go index 9ebb3f7a9..7f5f756c4 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -10,7 +10,7 @@ import ( merkledag "github.com/ipfs/go-ipfs/merkledag" dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - util "github.com/ipfs/go-ipfs/util" + util "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) func randNode() (*merkledag.Node, key.Key) { From c19b983a1c250f45a07dd8a3aaf14d64f185a033 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 9 Feb 2016 10:56:19 -0800 Subject: [PATCH 1179/3817] Use gx vendored go-ipfs-utils where possible For the rest of the packages in util, move them to thirdparty and update the references. util is gone! License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@9a1422d23616dd751c3d56a20d124550cf457798 --- pinning/pinner/pin_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 5dd9c45cf..9eb61acef 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -13,7 +13,7 @@ import ( bs "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" - "github.com/ipfs/go-ipfs/util" + "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) func randNode() (*mdag.Node, key.Key) { From 43cc40861b1c13736ad7b8da4c8201ea87816a74 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 9 Feb 2016 10:56:19 -0800 Subject: [PATCH 1180/3817] Use gx vendored go-ipfs-utils where possible For the rest of the packages in util, move them to thirdparty and update the references. util is gone! License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@08cbf9077d7d0cffc167a98f00665f930f38e677 --- namesys/ipns_select_test.go | 2 +- namesys/publisher.go | 2 +- namesys/resolve_test.go | 4 ++-- namesys/routing.go | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index fb171e901..b6ffe1c40 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -10,7 +10,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - u "github.com/ipfs/go-ipfs/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 241b40d3a..4f3cfaf2e 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -19,7 +19,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" - u "github.com/ipfs/go-ipfs/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" ) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 555b3055a..69f03f035 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -10,9 +10,9 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" - u "github.com/ipfs/go-ipfs/util" - testutil "github.com/ipfs/go-ipfs/util/testutil" + testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/routing.go b/namesys/routing.go index 7d499fe61..b10c22490 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,14 +6,14 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" lru "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" - mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - u "github.com/ipfs/go-ipfs/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) From 9e2fdbcba9b3fbd7f25884666984bfe9bb77b7b0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 9 Feb 2016 10:56:19 -0800 Subject: [PATCH 1181/3817] Use gx vendored go-ipfs-utils where possible For the rest of the packages in util, move them to thirdparty and update the references. util is gone! License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@88ccc80a73026e6daf9da65c385290341f1467a2 --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 51979e653..42c83b64b 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -10,9 +10,9 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dsns "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/namespace" dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/query" - mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" + mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) From 1dd13ced0239300aa76ebf9be14f36f10acbac89 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 9 Feb 2016 10:56:19 -0800 Subject: [PATCH 1182/3817] Use gx vendored go-ipfs-utils where possible For the rest of the packages in util, move them to thirdparty and update the references. util is gone! License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@7cf3183239b5ba5a5269d305cfaa43510d5d2047 --- blockservice/test/blocks_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 8cd5e6dfb..ab6a476aa 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -13,7 +13,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - u "github.com/ipfs/go-ipfs/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From 6f98c452ba721acc901f42d1072b3f50a7883abd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 9 Feb 2016 10:56:19 -0800 Subject: [PATCH 1183/3817] Use gx vendored go-ipfs-utils where possible For the rest of the packages in util, move them to thirdparty and update the references. util is gone! License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-chunker@55f8309bfefb04bce470aea77271578c8ae225cc --- chunker/rabin_test.go | 2 +- chunker/splitting_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index b4e1b2dc4..7702d3e76 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/key" - "github.com/ipfs/go-ipfs/util" + "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "io" "testing" ) diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index 27b2a7b7a..83dcaadba 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -5,7 +5,7 @@ import ( "io" "testing" - u "github.com/ipfs/go-ipfs/util" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) func randBuf(t *testing.T, size int) []byte { From e5cb566fbcd0c5dd811a9f445f6bce06c3673112 Mon Sep 17 00:00:00 2001 From: Stephen Whitmore Date: Thu, 18 Feb 2016 13:40:03 -0800 Subject: [PATCH 1184/3817] Fixes range error by using > 0 length buffer. License: MIT Signed-off-by: Stephen Whitmore This commit was moved from ipfs/go-mfs@6a77a95b450801f4612956b3de8bc85693c0567b --- mfs/mfs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index a56b68939..927a20f86 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -642,7 +642,7 @@ func actorWriteFile(d *Directory) error { return nil } - size := rand.Intn(1024) + size := rand.Intn(1024) + 1 buf := make([]byte, size) randbo.New().Read(buf) From cc7740d2db723ca755e7b4c8be2aa59de0d7fbc2 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Thu, 18 Feb 2016 17:39:26 -0500 Subject: [PATCH 1185/3817] Capitalized DHT License: MIT Signed-off-by: Richard Littauer This commit was moved from ipfs/go-ipfs-routing@da75b0f0811264e5b2b10d044dfed8baf22c485f --- routing/dht/dht_net.go | 4 ++-- routing/dht/dht_test.go | 4 ++-- routing/dht/handlers.go | 2 +- routing/dht/records.go | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index d54c678ef..8fec44685 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -70,7 +70,7 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { // measure the RTT for latency measurements. func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { - log.Debugf("%s dht starting stream", dht.self) + log.Debugf("%s DHT starting stream", dht.self) s, err := dht.host.NewStream(ctx, ProtocolDHT, p) if err != nil { return nil, err @@ -108,7 +108,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message // sendMessage sends out a message func (dht *IpfsDHT) sendMessage(ctx context.Context, p peer.ID, pmes *pb.Message) error { - log.Debugf("%s dht starting stream", dht.self) + log.Debugf("%s DHT starting stream", dht.self) s, err := dht.host.NewStream(ctx, ProtocolDHT, p) if err != nil { return err diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 3834c75e7..cdd1c5e51 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -93,7 +93,7 @@ func connect(t *testing.T, ctx context.Context, a, b *IpfsDHT) { func bootstrap(t *testing.T, ctx context.Context, dhts []*IpfsDHT) { ctx, cancel := context.WithCancel(ctx) - log.Debugf("bootstrapping dhts...") + log.Debugf("Bootstrapping DHTs...") // tried async. sequential fares much better. compare: // 100 async https://gist.github.com/jbenet/56d12f0578d5f34810b2 @@ -391,7 +391,7 @@ func TestPeriodicBootstrap(t *testing.T) { connect(t, ctx, dhts[i], dhts[(i+1)%len(dhts)]) } - t.Logf("dhts are now connected to 1-2 others.", nDHTs) + t.Logf("DHTs are now connected to 1-2 others.", nDHTs) for _, dht := range dhts { rtlen := dht.routingTable.Size() if rtlen > 2 { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index ea8996416..9c9598ffc 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -106,7 +106,7 @@ func (dht *IpfsDHT) checkLocalDatastore(k key.Key) (*pb.Record, error) { rec := new(pb.Record) err = proto.Unmarshal(byts, rec) if err != nil { - log.Debug("Failed to unmarshal dht record from datastore") + log.Debug("Failed to unmarshal DHT record from datastore.") return nil, err } diff --git a/routing/dht/records.go b/routing/dht/records.go index 06a4e70b1..3c9587dfa 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -42,7 +42,7 @@ func (dht *IpfsDHT) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey, err } // last ditch effort: let's try the dht. - log.Debugf("pk for %s not in peerstore, and peer failed. trying dht.", p) + log.Debugf("pk for %s not in peerstore, and peer failed. Trying DHT.", p) pkkey := routing.KeyForPublicKey(p) val, err := dht.GetValue(ctxT, pkkey) @@ -77,14 +77,14 @@ func (dht *IpfsDHT) getPublicKeyFromNode(ctx context.Context, p peer.ID) (ci.Pub // node doesn't have key :( record := pmes.GetRecord() if record == nil { - return nil, fmt.Errorf("node not responding with its public key: %s", p) + return nil, fmt.Errorf("Node not responding with its public key: %s", p) } // Success! We were given the value. we don't need to check // validity because a) we can't. b) we know the hash of the // key we're looking for. val := record.GetValue() - log.Debug("dht got a value from other peer.") + log.Debug("DHT got a value from other peer.") pk, err = ci.UnmarshalPublicKey(val) if err != nil { @@ -100,7 +100,7 @@ func (dht *IpfsDHT) getPublicKeyFromNode(ctx context.Context, p peer.ID) (ci.Pub } // ok! it's valid. we got it! - log.Debugf("dht got public key from node itself.") + log.Debugf("DHT got public key from node itself.") return pk, nil } From dd4510967818ab02794ce8d677ef7d7846b2e258 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 19 Feb 2016 18:57:41 -0800 Subject: [PATCH 1186/3817] fix minor mfs truncate bug License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@aee8b331ad392947b4c4c34283cf61c30a8ed1fd --- unixfs/mod/dagmodifier.go | 2 +- unixfs/mod/dagmodifier_test.go | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index ec7072597..33f082417 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -6,8 +6,8 @@ import ( "io" "os" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index a5ed5dc1b..fc3810f3f 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -447,6 +447,20 @@ func TestDagTruncate(t *testing.T) { if size != 10 { t.Fatal("size was incorrect!") } + + err = dagmod.Truncate(0) + if err != nil { + t.Fatal(err) + } + + size, err = dagmod.Size() + if err != nil { + t.Fatal(err) + } + + if size != 0 { + t.Fatal("size was incorrect!") + } } func TestSparseWrite(t *testing.T) { From 257f619f5819acb5e2daa2265cbe2e5349710eed Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 19 Feb 2016 18:57:41 -0800 Subject: [PATCH 1187/3817] fix minor mfs truncate bug License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@080e046ca1cf21e463e8f4eb9ed99d9a699fa609 --- mfs/dir.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/dir.go b/mfs/dir.go index fc949621a..b73d8ad7c 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -126,7 +126,7 @@ func (d *Directory) childNode(name string) (FSNode, error) { ndir := NewDirectory(d.ctx, name, nd, d, d.dserv) d.childDirs[name] = ndir return ndir, nil - case ufspb.Data_File: + case ufspb.Data_File, ufspb.Data_Raw: nfi, err := NewFile(name, nd, d, d.dserv) if err != nil { return nil, err From f9500471d84852e4f892db970c9583f954337f06 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 20 Feb 2016 10:27:07 -0800 Subject: [PATCH 1188/3817] change batch fetching methods of dagserv License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@172550b48d86c1c370848ec065f0ce356d991175 --- ipld/merkledag/merkledag.go | 60 +++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index e324ceb88..a311b396c 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -24,8 +24,7 @@ type DAGService interface { // GetDAG returns, in order, all the single leve child // nodes of the passed in node. - GetDAG(context.Context, *Node) []NodeGetter - GetNodes(context.Context, []key.Key) []NodeGetter + GetMany(context.Context, []key.Key) (<-chan *Node, <-chan error) Batch() *Batch } @@ -146,21 +145,52 @@ func FindLinks(links []key.Key, k key.Key, start int) []int { return out } +func (ds *dagService) GetMany(ctx context.Context, keys []key.Key) (<-chan *Node, <-chan error) { + out := make(chan *Node) + errs := make(chan error, 1) + blocks := ds.Blocks.GetBlocks(ctx, keys) + go func() { + defer close(out) + defer close(errs) + for { + select { + case b, ok := <-blocks: + if !ok { + return + } + nd, err := Decoded(b.Data) + if err != nil { + errs <- err + return + } + select { + case out <- nd: + case <-ctx.Done(): + return + } + case <-ctx.Done(): + return + } + } + }() + return out, errs +} + // GetDAG will fill out all of the links of the given Node. // It returns a channel of nodes, which the caller can receive // all the child nodes of 'root' on, in proper order. -func (ds *dagService) GetDAG(ctx context.Context, root *Node) []NodeGetter { +func GetDAG(ctx context.Context, ds DAGService, root *Node) []NodeGetter { var keys []key.Key for _, lnk := range root.Links { keys = append(keys, key.Key(lnk.Hash)) } - return ds.GetNodes(ctx, keys) + return GetNodes(ctx, ds, keys) } // GetNodes returns an array of 'NodeGetter' promises, with each corresponding // to the key with the same index as the passed in keys -func (ds *dagService) GetNodes(ctx context.Context, keys []key.Key) []NodeGetter { +func GetNodes(ctx context.Context, ds DAGService, keys []key.Key) []NodeGetter { // Early out if no work to do if len(keys) == 0 { @@ -178,26 +208,29 @@ func (ds *dagService) GetNodes(ctx context.Context, keys []key.Key) []NodeGetter ctx, cancel := context.WithCancel(ctx) defer cancel() - blkchan := ds.Blocks.GetBlocks(ctx, dedupedKeys) + nodechan, errchan := ds.GetMany(ctx, dedupedKeys) for count := 0; count < len(keys); { select { - case blk, ok := <-blkchan: + case nd, ok := <-nodechan: if !ok { return } - nd, err := Decoded(blk.Data) + k, err := nd.Key() if err != nil { - // NB: can happen with improperly formatted input data - log.Debug("Got back bad block!") - return + log.Error("Failed to get node key: ", err) + continue } - is := FindLinks(keys, blk.Key(), 0) + + is := FindLinks(keys, k, 0) for _, i := range is { count++ sendChans[i] <- nd } + case err := <-errchan: + log.Error("error fetching: ", err) + return case <-ctx.Done(): return } @@ -389,9 +422,10 @@ func fetchNodes(ctx context.Context, ds DAGService, in <-chan []key.Key, out cha } for ks := range in { - ng := ds.GetNodes(ctx, ks) + ng := GetNodes(ctx, ds, ks) for _, g := range ng { go get(g) } } + } From 993fdae1b8620c36fa7c66fea067201c007aa357 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 20 Feb 2016 10:27:07 -0800 Subject: [PATCH 1189/3817] change batch fetching methods of dagserv License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@90961cf508c1d4436a1e7b2da1403f2d3e83182f --- unixfs/archive/tar/writer.go | 2 +- unixfs/io/dagreader.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 32596618a..ad151016a 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -39,7 +39,7 @@ func (w *Writer) writeDir(nd *mdag.Node, fpath string) error { return err } - for i, ng := range w.Dag.GetDAG(w.ctx, nd) { + for i, ng := range mdag.GetDAG(w.ctx, w.Dag, nd) { child, err := ng.Get(w.ctx) if err != nil { return err diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 5b2a8b112..3c68ad896 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -90,7 +90,7 @@ func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (*Dag func NewDataFileReader(ctx context.Context, n *mdag.Node, pb *ftpb.Data, serv mdag.DAGService) *DagReader { fctx, cancel := context.WithCancel(ctx) - promises := serv.GetDAG(fctx, n) + promises := mdag.GetDAG(fctx, serv, n) return &DagReader{ node: n, serv: serv, From b0a5a598653094d585f21829ae54ba32705d0365 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 20 Feb 2016 11:04:21 -0800 Subject: [PATCH 1190/3817] rework FetchGraph to be less of a memory hog License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@f81083941ae3a02f58ea9829c950ca0d121ab416 --- ipld/merkledag/merkledag.go | 39 +++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index a311b396c..dc02b92f8 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -149,6 +149,8 @@ func (ds *dagService) GetMany(ctx context.Context, keys []key.Key) (<-chan *Node out := make(chan *Node) errs := make(chan error, 1) blocks := ds.Blocks.GetBlocks(ctx, keys) + var count int + go func() { defer close(out) defer close(errs) @@ -156,6 +158,9 @@ func (ds *dagService) GetMany(ctx context.Context, keys []key.Key) (<-chan *Node select { case b, ok := <-blocks: if !ok { + if count != len(keys) { + errs <- fmt.Errorf("failed to fetch all nodes") + } return } nd, err := Decoded(b.Data) @@ -165,6 +170,7 @@ func (ds *dagService) GetMany(ctx context.Context, keys []key.Key) (<-chan *Node } select { case out <- nd: + count++ case <-ctx.Done(): return } @@ -404,28 +410,27 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, root *Node, set func fetchNodes(ctx context.Context, ds DAGService, in <-chan []key.Key, out chan<- *Node, errs chan<- error) { defer close(out) - get := func(g NodeGetter) { - nd, err := g.Get(ctx) - if err != nil { + get := func(ks []key.Key) { + nodes, errch := ds.GetMany(ctx, ks) + for { select { - case errs <- err: - case <-ctx.Done(): + case nd, ok := <-nodes: + if !ok { + return + } + select { + case out <- nd: + case <-ctx.Done(): + return + } + case err := <-errch: + errs <- err + return } - return - } - - select { - case out <- nd: - case <-ctx.Done(): - return } } for ks := range in { - ng := GetNodes(ctx, ds, ks) - for _, g := range ng { - go get(g) - } + go get(ks) } - } From 9cf0a0deb5f05efc1b49594eb26994de710bbbcf Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 24 Feb 2016 10:06:07 -0800 Subject: [PATCH 1191/3817] fixes from review License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@b88c9c043bd7f58c23dae7ab77a590c5518802b3 --- ipld/merkledag/merkledag.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index dc02b92f8..552fc068d 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -146,14 +146,13 @@ func FindLinks(links []key.Key, k key.Key, start int) []int { } func (ds *dagService) GetMany(ctx context.Context, keys []key.Key) (<-chan *Node, <-chan error) { - out := make(chan *Node) + out := make(chan *Node, len(keys)) errs := make(chan error, 1) blocks := ds.Blocks.GetBlocks(ctx, keys) var count int go func() { defer close(out) - defer close(errs) for { select { case b, ok := <-blocks: @@ -168,13 +167,13 @@ func (ds *dagService) GetMany(ctx context.Context, keys []key.Key) (<-chan *Node errs <- err return } - select { - case out <- nd: - count++ - case <-ctx.Done(): - return - } + + // buffered, no need to select + out <- nd + count++ + case <-ctx.Done(): + errs <- ctx.Err() return } } From 6406900a32ac347e213b7961247e60d70ac5efa2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 24 Feb 2016 11:38:44 -0800 Subject: [PATCH 1192/3817] use an option type to simplify concurrency License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@bfdc7966d677cfca19065f4fb4b8a271116e9dd5 --- ipld/merkledag/merkledag.go | 84 +++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 552fc068d..aebc370ad 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -3,6 +3,7 @@ package merkledag import ( "fmt" + "sync" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" @@ -24,7 +25,7 @@ type DAGService interface { // GetDAG returns, in order, all the single leve child // nodes of the passed in node. - GetMany(context.Context, []key.Key) (<-chan *Node, <-chan error) + GetMany(context.Context, []key.Key) <-chan *NodeOption Batch() *Batch } @@ -145,9 +146,13 @@ func FindLinks(links []key.Key, k key.Key, start int) []int { return out } -func (ds *dagService) GetMany(ctx context.Context, keys []key.Key) (<-chan *Node, <-chan error) { - out := make(chan *Node, len(keys)) - errs := make(chan error, 1) +type NodeOption struct { + Node *Node + Err error +} + +func (ds *dagService) GetMany(ctx context.Context, keys []key.Key) <-chan *NodeOption { + out := make(chan *NodeOption, len(keys)) blocks := ds.Blocks.GetBlocks(ctx, keys) var count int @@ -158,27 +163,27 @@ func (ds *dagService) GetMany(ctx context.Context, keys []key.Key) (<-chan *Node case b, ok := <-blocks: if !ok { if count != len(keys) { - errs <- fmt.Errorf("failed to fetch all nodes") + out <- &NodeOption{Err: fmt.Errorf("failed to fetch all nodes")} } return } nd, err := Decoded(b.Data) if err != nil { - errs <- err + out <- &NodeOption{Err: err} return } // buffered, no need to select - out <- nd + out <- &NodeOption{Node: nd} count++ case <-ctx.Done(): - errs <- ctx.Err() + out <- &NodeOption{Err: ctx.Err()} return } } }() - return out, errs + return out } // GetDAG will fill out all of the links of the given Node. @@ -213,15 +218,22 @@ func GetNodes(ctx context.Context, ds DAGService, keys []key.Key) []NodeGetter { ctx, cancel := context.WithCancel(ctx) defer cancel() - nodechan, errchan := ds.GetMany(ctx, dedupedKeys) + nodechan := ds.GetMany(ctx, dedupedKeys) for count := 0; count < len(keys); { select { - case nd, ok := <-nodechan: + case opt, ok := <-nodechan: if !ok { return } + if opt.Err != nil { + log.Error("error fetching: ", opt.Err) + return + } + + nd := opt.Node + k, err := nd.Key() if err != nil { log.Error("Failed to get node key: ", err) @@ -233,9 +245,6 @@ func GetNodes(ctx context.Context, ds DAGService, keys []key.Key) []NodeGetter { count++ sendChans[i] <- nd } - case err := <-errchan: - log.Error("error fetching: ", err) - return case <-ctx.Done(): return } @@ -356,24 +365,30 @@ func EnumerateChildren(ctx context.Context, ds DAGService, root *Node, set key.K func EnumerateChildrenAsync(ctx context.Context, ds DAGService, root *Node, set key.KeySet) error { toprocess := make(chan []key.Key, 8) - nodes := make(chan *Node, 8) - errs := make(chan error, 1) + nodes := make(chan *NodeOption, 8) ctx, cancel := context.WithCancel(ctx) defer cancel() defer close(toprocess) - go fetchNodes(ctx, ds, toprocess, nodes, errs) + go fetchNodes(ctx, ds, toprocess, nodes) - nodes <- root + nodes <- &NodeOption{Node: root} live := 1 for { select { - case nd, ok := <-nodes: + case opt, ok := <-nodes: if !ok { return nil } + + if opt.Err != nil { + return opt.Err + } + + nd := opt.Node + // a node has been fetched live-- @@ -398,38 +413,35 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, root *Node, set return ctx.Err() } } - case err := <-errs: - return err case <-ctx.Done(): return ctx.Err() } } } -func fetchNodes(ctx context.Context, ds DAGService, in <-chan []key.Key, out chan<- *Node, errs chan<- error) { - defer close(out) +func fetchNodes(ctx context.Context, ds DAGService, in <-chan []key.Key, out chan<- *NodeOption) { + var wg sync.WaitGroup + defer func() { + // wait for all 'get' calls to complete so we don't accidentally send + // on a closed channel + wg.Wait() + close(out) + }() get := func(ks []key.Key) { - nodes, errch := ds.GetMany(ctx, ks) - for { + defer wg.Done() + nodes := ds.GetMany(ctx, ks) + for opt := range nodes { select { - case nd, ok := <-nodes: - if !ok { - return - } - select { - case out <- nd: - case <-ctx.Done(): - return - } - case err := <-errch: - errs <- err + case out <- opt: + case <-ctx.Done(): return } } } for ks := range in { + wg.Add(1) go get(ks) } } From 52ff7db5aa1ed5418ae44a850b890be54f3f0cec Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Mon, 26 Oct 2015 17:13:44 +0100 Subject: [PATCH 1193/3817] Remove usage of merkledag.Link.Node pointer outside of merkledag This prepares for inclusion of IPLD where the Node pointer won't be there. License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-merkledag@fb646c2c7a05371c4e42bd44d80f676903140463 --- ipld/merkledag/node.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index b5e95f81b..ee21e5180 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -2,6 +2,7 @@ package merkledag import ( "fmt" + "time" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" @@ -85,6 +86,26 @@ func (l *Link) GetNode(ctx context.Context, serv DAGService) (*Node, error) { return serv.Get(ctx, key.Key(l.Hash)) } +// GetNodeAndCache return the MDAG Node that the link points to and store a +// pointer to that node along with the link to speed up further retrivals. A +// timeout is to be specified to avoid taking too much time. +func (l *Link) GetNodeAndCache(ctx context.Context, serv DAGService, timeout time.Duration) (*Node, error) { + if l.Node == nil { + if timeout != 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, time.Minute) + defer cancel() + } + nd, err := serv.Get(ctx, key.Key(l.Hash)) + if err != nil { + return nil, err + } + l.Node = nd + } + + return l.Node, nil +} + // AddNodeLink adds a link to another node. func (n *Node) AddNodeLink(name string, that *Node) error { n.encoded = nil From cc3f97ffe7dd887c7b33981638d8f15d6d73799e Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Mon, 26 Oct 2015 22:47:04 +0100 Subject: [PATCH 1194/3817] path/resolver.go: Handle timeout here License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-merkledag@f63bfe0a9620d6abbb8f74f05f1a89f48d1c9a45 --- ipld/merkledag/node.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index ee21e5180..ed503d312 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -2,7 +2,6 @@ package merkledag import ( "fmt" - "time" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" @@ -77,6 +76,11 @@ func MakeLink(n *Node) (*Link, error) { }, nil } +// GetCachedNode returns the MDAG Node that was cached, or nil +func (l *Link) GetCachedNode() *Node { + return l.Node +} + // GetNode returns the MDAG Node that this link points to func (l *Link) GetNode(ctx context.Context, serv DAGService) (*Node, error) { if l.Node != nil { @@ -89,13 +93,8 @@ func (l *Link) GetNode(ctx context.Context, serv DAGService) (*Node, error) { // GetNodeAndCache return the MDAG Node that the link points to and store a // pointer to that node along with the link to speed up further retrivals. A // timeout is to be specified to avoid taking too much time. -func (l *Link) GetNodeAndCache(ctx context.Context, serv DAGService, timeout time.Duration) (*Node, error) { +func (l *Link) GetNodeAndCache(ctx context.Context, serv DAGService) (*Node, error) { if l.Node == nil { - if timeout != 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, time.Minute) - defer cancel() - } nd, err := serv.Get(ctx, key.Key(l.Hash)) if err != nil { return nil, err From 401e50350db4d04cccaca47fcfb2ee4b320bdd6f Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Mon, 26 Oct 2015 17:13:44 +0100 Subject: [PATCH 1195/3817] Remove usage of merkledag.Link.Node pointer outside of merkledag This prepares for inclusion of IPLD where the Node pointer won't be there. License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-path@6ffa40e3b9ae633d0a085626b044d05d97eb0b02 --- path/resolver.go | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index 10368bbfd..d5a6745e1 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -111,37 +111,27 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd *merkledag.Node, names // for each of the path components for _, name := range names { - var next key.Key var nlink *merkledag.Link // for each of the links in nd, the current object for _, link := range nd.Links { if link.Name == name { - next = key.Key(link.Hash) nlink = link break } } - if next == "" { + if nlink == nil || len(nlink.Hash) == 0 { n, _ := nd.Multihash() return result, ErrNoLink{Name: name, Node: n} } - if nlink.Node == nil { - // fetch object for link and assign to nd - ctx, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() - var err error - nd, err = s.DAG.Get(ctx, next) - if err != nil { - return append(result, nd), err - } - nlink.Node = nd - } else { - nd = nlink.Node + var err error + nd, err = nlink.GetNodeAndCache(ctx, s.DAG, time.Minute) + if err != nil { + return append(result, nd), err } - result = append(result, nlink.Node) + result = append(result, nd) } return result, nil } From c7e13c7a5cbdd37003a01e3f03aba56d3ba447c3 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Fri, 27 Nov 2015 20:22:09 +0100 Subject: [PATCH 1196/3817] merkledag: Make Node.Unmarshal() private License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-merkledag@77c80001611f737b2d852803040d86e91b3121ab --- ipld/merkledag/coding.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 1c57125fb..302e75151 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -13,9 +13,9 @@ import ( // for now, we use a PBNode intermediate thing. // because native go objects are nice. -// Unmarshal decodes raw data into a *Node instance. +// unmarshal decodes raw data into a *Node instance. // The conversion uses an intermediate PBNode. -func (n *Node) Unmarshal(encoded []byte) error { +func (n *Node) unmarshal(encoded []byte) error { var pbn pb.PBNode if err := pbn.Unmarshal(encoded); err != nil { return fmt.Errorf("Unmarshal failed. %v", err) @@ -87,7 +87,7 @@ func (n *Node) Encoded(force bool) ([]byte, error) { // Decoded decodes raw data and returns a new Node instance. func Decoded(encoded []byte) (*Node, error) { n := new(Node) - err := n.Unmarshal(encoded) + err := n.unmarshal(encoded) if err != nil { return nil, fmt.Errorf("incorrectly formatted merkledag node: %s", err) } From e55e2c8431ddccef4aadf9581600f6d1eea30e39 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Mon, 26 Oct 2015 22:47:04 +0100 Subject: [PATCH 1197/3817] path/resolver.go: Handle timeout here License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-path@4f4b6d19670930729150ffe295db6afc466fb26b --- path/resolver.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/path/resolver.go b/path/resolver.go index d5a6745e1..4bb11ecf0 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -125,8 +125,14 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd *merkledag.Node, names return result, ErrNoLink{Name: name, Node: n} } + if nlink.GetCachedNode() == nil { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, time.Minute) + defer cancel() + } + var err error - nd, err = nlink.GetNodeAndCache(ctx, s.DAG, time.Minute) + nd, err = nlink.GetNodeAndCache(ctx, s.DAG) if err != nil { return append(result, nd), err } From f0aeb42f8d60c72043f552888065709faefb1055 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Fri, 5 Feb 2016 23:31:37 +0100 Subject: [PATCH 1198/3817] Rename Decoded into DecodeProtobuf This function work only with protocol buffer encoding. To make this clear, rename the function. License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-merkledag@ec4cec0cb1fa7f86412f46440f6100f7d1b87027 --- ipld/merkledag/coding.go | 2 +- ipld/merkledag/merkledag.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 302e75151..884b0277c 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -85,7 +85,7 @@ func (n *Node) Encoded(force bool) ([]byte, error) { } // Decoded decodes raw data and returns a new Node instance. -func Decoded(encoded []byte) (*Node, error) { +func DecodeProtobuf(encoded []byte) (*Node, error) { n := new(Node) err := n.unmarshal(encoded) if err != nil { diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index aebc370ad..0152a5afc 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -104,7 +104,7 @@ func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) { return nil, err } - return Decoded(b.Data) + return DecodeProtobuf(b.Data) } // Remove deletes the given node and all of its children from the BlockService @@ -167,7 +167,7 @@ func (ds *dagService) GetMany(ctx context.Context, keys []key.Key) <-chan *NodeO } return } - nd, err := Decoded(b.Data) + nd, err := DecodeProtobuf(b.Data) if err != nil { out <- &NodeOption{Err: err} return From fd354fe70696d3d40a6ceef82339e08a37ca995b Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Wed, 24 Feb 2016 08:34:32 +0100 Subject: [PATCH 1199/3817] merkledag: make Link.Node (the node cache) a private field License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-merkledag@da9cdfdc1dcd5f6d32f28a1925360b6d98b74307 --- ipld/merkledag/merkledag.go | 8 ++++---- ipld/merkledag/node.go | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 0152a5afc..83d363677 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -77,8 +77,8 @@ func (n *dagService) AddRecursive(nd *Node) error { } for _, link := range nd.Links { - if link.Node != nil { - err := n.AddRecursive(link.Node) + if link.node != nil { + err := n.AddRecursive(link.node) if err != nil { return err } @@ -110,8 +110,8 @@ func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) { // Remove deletes the given node and all of its children from the BlockService func (n *dagService) RemoveRecursive(nd *Node) error { for _, l := range nd.Links { - if l.Node != nil { - n.RemoveRecursive(l.Node) + if l.node != nil { + n.RemoveRecursive(l.node) } } k, err := nd.Key() diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index ed503d312..e0e282dba 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -5,8 +5,8 @@ import ( "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" key "github.com/ipfs/go-ipfs/blocks/key" + mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" ) var ErrLinkNotFound = fmt.Errorf("no link by that name") @@ -50,7 +50,7 @@ type Link struct { Hash mh.Multihash // a ptr to the actual node for graph manipulation - Node *Node + node *Node } type LinkSlice []*Link @@ -78,13 +78,13 @@ func MakeLink(n *Node) (*Link, error) { // GetCachedNode returns the MDAG Node that was cached, or nil func (l *Link) GetCachedNode() *Node { - return l.Node + return l.node } // GetNode returns the MDAG Node that this link points to func (l *Link) GetNode(ctx context.Context, serv DAGService) (*Node, error) { - if l.Node != nil { - return l.Node, nil + if l.node != nil { + return l.node, nil } return serv.Get(ctx, key.Key(l.Hash)) @@ -94,15 +94,15 @@ func (l *Link) GetNode(ctx context.Context, serv DAGService) (*Node, error) { // pointer to that node along with the link to speed up further retrivals. A // timeout is to be specified to avoid taking too much time. func (l *Link) GetNodeAndCache(ctx context.Context, serv DAGService) (*Node, error) { - if l.Node == nil { + if l.node == nil { nd, err := serv.Get(ctx, key.Key(l.Hash)) if err != nil { return nil, err } - l.Node = nd + l.node = nd } - return l.Node, nil + return l.node, nil } // AddNodeLink adds a link to another node. @@ -112,7 +112,7 @@ func (n *Node) AddNodeLink(name string, that *Node) error { lnk, err := MakeLink(that) lnk.Name = name - lnk.Node = that + lnk.node = that if err != nil { return err } @@ -142,7 +142,7 @@ func (n *Node) AddRawLink(name string, l *Link) error { Name: name, Size: l.Size, Hash: l.Hash, - Node: l.Node, + node: l.node, }) return nil @@ -178,7 +178,7 @@ func (n *Node) GetNodeLink(name string) (*Link, error) { Name: l.Name, Size: l.Size, Hash: l.Hash, - Node: l.Node, + node: l.node, }, nil } } From 2786ed06da4b860a488970b073d793045af9c49a Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Wed, 24 Feb 2016 08:41:55 +0100 Subject: [PATCH 1200/3817] path/resolver.go: simplify ResolveLinks() License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-merkledag@7ab5f1f66ecb969580776e4c93a56d1bd246826f --- ipld/merkledag/node.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index e0e282dba..b07d64b5b 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -90,21 +90,6 @@ func (l *Link) GetNode(ctx context.Context, serv DAGService) (*Node, error) { return serv.Get(ctx, key.Key(l.Hash)) } -// GetNodeAndCache return the MDAG Node that the link points to and store a -// pointer to that node along with the link to speed up further retrivals. A -// timeout is to be specified to avoid taking too much time. -func (l *Link) GetNodeAndCache(ctx context.Context, serv DAGService) (*Node, error) { - if l.node == nil { - nd, err := serv.Get(ctx, key.Key(l.Hash)) - if err != nil { - return nil, err - } - l.node = nd - } - - return l.node, nil -} - // AddNodeLink adds a link to another node. func (n *Node) AddNodeLink(name string, that *Node) error { n.encoded = nil From 44294f07ff2580a23975da02a50d165c8b62acbe Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Thu, 25 Feb 2016 07:34:56 +0100 Subject: [PATCH 1201/3817] Remove GetCachedNode() License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-merkledag@00d312eaab99e23ea62797a4d46799cfd645dc81 --- ipld/merkledag/node.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index b07d64b5b..0a17ccca5 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -76,11 +76,6 @@ func MakeLink(n *Node) (*Link, error) { }, nil } -// GetCachedNode returns the MDAG Node that was cached, or nil -func (l *Link) GetCachedNode() *Node { - return l.node -} - // GetNode returns the MDAG Node that this link points to func (l *Link) GetNode(ctx context.Context, serv DAGService) (*Node, error) { if l.node != nil { From 4cb965a1d56136e490f5b5a5b7f48183deb8dab0 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Thu, 25 Feb 2016 07:35:28 +0100 Subject: [PATCH 1202/3817] Rename Encoded() to EncodeProtobuf() License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-merkledag@e02443c316bf3911d3e4ecfa7af5839c356e65c7 --- ipld/merkledag/coding.go | 4 ++-- ipld/merkledag/merkledag.go | 4 ++-- ipld/merkledag/merkledag_test.go | 6 +++--- ipld/merkledag/node.go | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 884b0277c..3d7b8381c 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -68,9 +68,9 @@ func (n *Node) getPBNode() *pb.PBNode { return pbn } -// Encoded returns the encoded raw data version of a Node instance. +// EncodeProtobuf returns the encoded raw data version of a Node instance. // It may use a cached encoded version, unless the force flag is given. -func (n *Node) Encoded(force bool) ([]byte, error) { +func (n *Node) EncodeProtobuf(force bool) ([]byte, error) { sort.Stable(LinkSlice(n.Links)) // keep links sorted if n.encoded == nil || force { var err error diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 83d363677..792194002 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -49,7 +49,7 @@ func (n *dagService) Add(nd *Node) (key.Key, error) { return "", fmt.Errorf("dagService is nil") } - d, err := nd.Encoded(false) + d, err := nd.EncodeProtobuf(false) if err != nil { return "", err } @@ -313,7 +313,7 @@ type Batch struct { } func (t *Batch) Add(nd *Node) (key.Key, error) { - d, err := nd.Encoded(false) + d, err := nd.EncodeProtobuf(false) if err != nil { return "", err } diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 91bc2c0f7..6816aad52 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -64,7 +64,7 @@ func TestNode(t *testing.T) { fmt.Println("-", l.Name, l.Size, l.Hash) } - e, err := n.Encoded(false) + e, err := n.EncodeProtobuf(false) if err != nil { t.Error(err) } else { @@ -96,9 +96,9 @@ func TestNode(t *testing.T) { } func SubtestNodeStat(t *testing.T, n *Node) { - enc, err := n.Encoded(true) + enc, err := n.EncodeProtobuf(true) if err != nil { - t.Error("n.Encoded(true) failed") + t.Error("n.EncodeProtobuf(true) failed") return } diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 0a17ccca5..5fa05b41a 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -203,7 +203,7 @@ func (n *Node) UpdateNodeLink(name string, that *Node) (*Node, error) { // Size returns the total size of the data addressed by node, // including the total sizes of references. func (n *Node) Size() (uint64, error) { - b, err := n.Encoded(false) + b, err := n.EncodeProtobuf(false) if err != nil { return 0, err } @@ -217,7 +217,7 @@ func (n *Node) Size() (uint64, error) { // Stat returns statistics on the node. func (n *Node) Stat() (*NodeStat, error) { - enc, err := n.Encoded(false) + enc, err := n.EncodeProtobuf(false) if err != nil { return nil, err } @@ -244,8 +244,8 @@ func (n *Node) Stat() (*NodeStat, error) { // Multihash hashes the encoded data of this node. func (n *Node) Multihash() (mh.Multihash, error) { - // Note: Encoded generates the hash and puts it in n.cached. - _, err := n.Encoded(false) + // Note: EncodeProtobuf generates the hash and puts it in n.cached. + _, err := n.EncodeProtobuf(false) if err != nil { return nil, err } From 4f85034cfbb187c64e4750fe39c17e8ffe35f63b Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Wed, 24 Feb 2016 08:34:32 +0100 Subject: [PATCH 1203/3817] merkledag: make Link.Node (the node cache) a private field License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-ipfs-pinner@4ee875a57dda07a989071172d00f4c0ea150b18b --- pinning/pinner/set.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index f3d825818..669fa7a60 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -11,10 +11,10 @@ import ( "sort" "unsafe" - "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" + "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) @@ -172,7 +172,6 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint Name: "", Hash: childKey.ToMultihash(), Size: size, - Node: child, } n.Links[int(h%defaultFanout)] = l } From 5a16de4a1d0354cfbe32649eefbcfa69c75f7cf7 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Wed, 24 Feb 2016 08:41:55 +0100 Subject: [PATCH 1204/3817] path/resolver.go: simplify ResolveLinks() License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-path@1cc3164818770bb9da2436f0b48742b5491cfdc1 --- path/resolver.go | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index 4bb11ecf0..3eaf345ff 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -111,33 +111,20 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd *merkledag.Node, names // for each of the path components for _, name := range names { - var nlink *merkledag.Link - // for each of the links in nd, the current object - for _, link := range nd.Links { - if link.Name == name { - nlink = link - break - } - } + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, time.Minute) + defer cancel() - if nlink == nil || len(nlink.Hash) == 0 { + nextnode, err := nd.GetLinkedNode(ctx, s.DAG, name) + if err == merkledag.ErrLinkNotFound { n, _ := nd.Multihash() return result, ErrNoLink{Name: name, Node: n} + } else if err != nil { + return append(result, nextnode), err } - if nlink.GetCachedNode() == nil { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, time.Minute) - defer cancel() - } - - var err error - nd, err = nlink.GetNodeAndCache(ctx, s.DAG) - if err != nil { - return append(result, nd), err - } - - result = append(result, nd) + nd = nextnode + result = append(result, nextnode) } return result, nil } From c358779c99831c6db8213f612b286ffa49ec43ea Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Thu, 25 Feb 2016 07:35:28 +0100 Subject: [PATCH 1205/3817] Rename Encoded() to EncodeProtobuf() License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-unixfs@b3a517a2c125bad1b462edda6c253203469cc6a6 --- unixfs/mod/dagmodifier.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 33f082417..0f5866716 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -258,7 +258,7 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) node.Links[i].Hash = mh.Multihash(k) // Recache serialized node - _, err = node.Encoded(true) + _, err = node.EncodeProtobuf(true) if err != nil { return "", false, err } @@ -489,7 +489,7 @@ func dagTruncate(ctx context.Context, nd *mdag.Node, size uint64, ds mdag.DAGSer nd.Data = d // invalidate cache and recompute serialized data - _, err = nd.Encoded(true) + _, err = nd.EncodeProtobuf(true) if err != nil { return nil, err } From cefb2aa078ec66fff5c2f771dae901071d9a3d57 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Sun, 28 Feb 2016 11:30:26 +0100 Subject: [PATCH 1206/3817] merkledag: Remove unused AddRecursive and RemoveRecursive License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-merkledag@8561017a487a989852e75dd5a78640110cc7c67c --- ipld/merkledag/merkledag.go | 36 ---------------------------- ipld/merkledag/merkledag_test.go | 41 +------------------------------- 2 files changed, 1 insertion(+), 76 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 792194002..3466aafc2 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -18,10 +18,8 @@ var ErrNotFound = fmt.Errorf("merkledag: not found") // DAGService is an IPFS Merkle DAG service. type DAGService interface { Add(*Node) (key.Key, error) - AddRecursive(*Node) error Get(context.Context, key.Key) (*Node, error) Remove(*Node) error - RemoveRecursive(*Node) error // GetDAG returns, in order, all the single leve child // nodes of the passed in node. @@ -68,26 +66,6 @@ func (n *dagService) Batch() *Batch { return &Batch{ds: n, MaxSize: 8 * 1024 * 1024} } -// AddRecursive adds the given node and all child nodes to the BlockService -func (n *dagService) AddRecursive(nd *Node) error { - _, err := n.Add(nd) - if err != nil { - log.Info("AddRecursive Error: %s\n", err) - return err - } - - for _, link := range nd.Links { - if link.node != nil { - err := n.AddRecursive(link.node) - if err != nil { - return err - } - } - } - - return nil -} - // Get retrieves a node from the dagService, fetching the block in the BlockService func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) { if n == nil { @@ -107,20 +85,6 @@ func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) { return DecodeProtobuf(b.Data) } -// Remove deletes the given node and all of its children from the BlockService -func (n *dagService) RemoveRecursive(nd *Node) error { - for _, l := range nd.Links { - if l.node != nil { - n.RemoveRecursive(l.node) - } - } - k, err := nd.Key() - if err != nil { - return err - } - return n.Blocks.DeleteBlock(k) -} - func (n *dagService) Remove(nd *Node) error { k, err := nd.Key() if err != nil { diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 6816aad52..8137496d8 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -181,7 +181,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { t.Fatal(err) } - err = dagservs[0].AddRecursive(root) + _, err = dagservs[0].Add(root) if err != nil { t.Fatal(err) } @@ -232,45 +232,6 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { } } } -func TestRecursiveAdd(t *testing.T) { - a := &Node{Data: []byte("A")} - b := &Node{Data: []byte("B")} - c := &Node{Data: []byte("C")} - d := &Node{Data: []byte("D")} - e := &Node{Data: []byte("E")} - - err := a.AddNodeLink("blah", b) - if err != nil { - t.Fatal(err) - } - - err = b.AddNodeLink("foo", c) - if err != nil { - t.Fatal(err) - } - - err = b.AddNodeLink("bar", d) - if err != nil { - t.Fatal(err) - } - - err = d.AddNodeLink("baz", e) - if err != nil { - t.Fatal(err) - } - - dsp := getDagservAndPinner(t) - err = dsp.ds.AddRecursive(a) - if err != nil { - t.Fatal(err) - } - - assertCanGet(t, dsp.ds, a) - assertCanGet(t, dsp.ds, b) - assertCanGet(t, dsp.ds, c) - assertCanGet(t, dsp.ds, d) - assertCanGet(t, dsp.ds, e) -} func assertCanGet(t *testing.T, ds DAGService, n *Node) { k, err := n.Key() From 299d60103e47ec4335ed37c01b9d0c435edee913 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Sun, 28 Feb 2016 11:40:08 +0100 Subject: [PATCH 1207/3817] merkledag: Remove cached Node.node License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-merkledag@cbc9a085265290961fa4c605f1b61975a5211cbf --- ipld/merkledag/node.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 5fa05b41a..d44285159 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -48,9 +48,6 @@ type Link struct { // multihash of the target object Hash mh.Multihash - - // a ptr to the actual node for graph manipulation - node *Node } type LinkSlice []*Link @@ -78,10 +75,6 @@ func MakeLink(n *Node) (*Link, error) { // GetNode returns the MDAG Node that this link points to func (l *Link) GetNode(ctx context.Context, serv DAGService) (*Node, error) { - if l.node != nil { - return l.node, nil - } - return serv.Get(ctx, key.Key(l.Hash)) } @@ -92,7 +85,6 @@ func (n *Node) AddNodeLink(name string, that *Node) error { lnk, err := MakeLink(that) lnk.Name = name - lnk.node = that if err != nil { return err } @@ -122,7 +114,6 @@ func (n *Node) AddRawLink(name string, l *Link) error { Name: name, Size: l.Size, Hash: l.Hash, - node: l.node, }) return nil @@ -158,7 +149,6 @@ func (n *Node) GetNodeLink(name string) (*Link, error) { Name: l.Name, Size: l.Size, Hash: l.Hash, - node: l.node, }, nil } } From 0927249d3c1a8fb85495a8f6ebe68dd16fdaf51a Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Sun, 28 Feb 2016 11:30:26 +0100 Subject: [PATCH 1208/3817] merkledag: Remove unused AddRecursive and RemoveRecursive License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-ipfs-pinner@104b9f6f818dac05d8d24e9b1dc24ce8534efb8a --- pinning/pinner/pin_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 9eb61acef..09371fc6e 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -104,7 +104,11 @@ func TestPinnerBasic(t *testing.T) { d.AddNodeLink("e", e) // Must be in dagserv for unpin to work - err = dserv.AddRecursive(d) + _, err = dserv.Add(e) + if err != nil { + t.Fatal(err) + } + _, err = dserv.Add(d) if err != nil { t.Fatal(err) } From 7a1462457b4040f1ad85a3b52ca78147634975a1 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Wed, 2 Mar 2016 09:54:42 +0100 Subject: [PATCH 1209/3817] Improve error reporting and fix pin/set_test.go License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-merkledag@29695e9e27b66c0242129d4276b6b310adb6ccdc --- ipld/merkledag/coding.go | 2 +- ipld/merkledag/merkledag.go | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 3d7b8381c..10c30727a 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -27,7 +27,7 @@ func (n *Node) unmarshal(encoded []byte) error { n.Links[i] = &Link{Name: l.GetName(), Size: l.GetTsize()} h, err := mh.Cast(l.GetHash()) if err != nil { - return fmt.Errorf("Link hash is not valid multihash. %v", err) + return fmt.Errorf("Link hash #%d is not valid multihash. %v", i, err) } n.Links[i].Hash = h } diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 3466aafc2..df6fa4187 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -79,10 +79,14 @@ func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) { if err == bserv.ErrNotFound { return nil, ErrNotFound } - return nil, err + return nil, fmt.Errorf("Failed to get block for %s: %v", k.B58String(), err) } - return DecodeProtobuf(b.Data) + res, err := DecodeProtobuf(b.Data) + if err != nil { + return nil, fmt.Errorf("Failed to decode Protocol Buffers: %v", err) + } + return res, nil } func (n *dagService) Remove(nd *Node) error { From 1fb33c024ff438dc2f480b962d3dd6389ab8b93b Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Sun, 28 Feb 2016 11:30:26 +0100 Subject: [PATCH 1210/3817] merkledag: Remove unused AddRecursive and RemoveRecursive License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-path@aa614ccddb36ba6679dd917f6a57d965b3855efb --- path/resolver_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/path/resolver_test.go b/path/resolver_test.go index 7f5f756c4..fe8155a85 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -39,9 +39,11 @@ func TestRecurivePathResolution(t *testing.T) { t.Fatal(err) } - err = dagService.AddRecursive(a) - if err != nil { - t.Fatal(err) + for _, n := range []*merkledag.Node{a, b, c} { + _, err = dagService.Add(n) + if err != nil { + t.Fatal(err) + } } aKey, err := a.Key() From e702c8f88d5402c20ba731d71d5efddcfb5b7cc9 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Wed, 2 Mar 2016 09:54:42 +0100 Subject: [PATCH 1211/3817] Improve error reporting and fix pin/set_test.go License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-ipfs-pinner@8b350306ea958e8f2b29369790d4a7ee5e88fc8e --- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 669fa7a60..fec38e254 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -271,12 +271,12 @@ func loadSet(ctx context.Context, dag merkledag.DAGService, root *merkledag.Node func loadMultiset(ctx context.Context, dag merkledag.DAGService, root *merkledag.Node, name string, internalKeys keyObserver) (map[key.Key]uint64, error) { l, err := root.GetNodeLink(name) if err != nil { - return nil, err + return nil, fmt.Errorf("Failed to get link %s: %v", name, err) } internalKeys(key.Key(l.Hash)) n, err := l.GetNode(ctx, dag) if err != nil { - return nil, err + return nil, fmt.Errorf("Failed to get node from link %s: %v", name, err) } refcounts := make(map[key.Key]uint64) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 3ef7ce51b..b25f91a96 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -11,6 +11,8 @@ import ( "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" "github.com/ipfs/go-ipfs/merkledag" + mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) @@ -31,6 +33,14 @@ func TestMultisetRoundtrip(t *testing.T) { dag := merkledag.NewDAGService(bserv) fn := func(m map[key.Key]uint16) bool { + // Convert invalid multihash from input to valid ones + for k, v := range m { + if _, err := mh.Cast([]byte(k)); err != nil { + delete(m, k) + m[key.Key(u.Hash([]byte(k)))] = v + } + } + // Generate a smaller range for refcounts than full uint64, as // otherwise this just becomes overly cpu heavy, splitting it // out into too many items. That means we need to convert to @@ -43,6 +53,17 @@ func TestMultisetRoundtrip(t *testing.T) { if err != nil { t.Fatalf("storing multiset: %v", err) } + + // Check that the node n is in the DAG + k, err := n.Key() + if err != nil { + t.Fatalf("Could not get key: %v", err) + } + _, err = dag.Get(ctx, k) + if err != nil { + t.Fatalf("Could not get node: %v", err) + } + root := &merkledag.Node{} const linkName = "dummylink" if err := root.AddNodeLink(linkName, n); err != nil { From 752a9c77dd6295523c397ed32d1a93cea3ca9d92 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Wed, 2 Mar 2016 22:32:21 +0100 Subject: [PATCH 1212/3817] merkledag/traverse: Fix tests after node pointer removal License: MIT Signed-off-by: Mildred Ki'Lya This commit was moved from ipfs/go-merkledag@7df6213e0591ebf3d493bf89e739f10e09cb4989 --- ipld/merkledag/traverse/traverse_test.go | 140 +++++++++++++---------- 1 file changed, 79 insertions(+), 61 deletions(-) diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index ff57909a3..5ca906a51 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -6,12 +6,14 @@ import ( "testing" mdag "github.com/ipfs/go-ipfs/merkledag" + mdagtest "github.com/ipfs/go-ipfs/merkledag/test" ) func TestDFSPreNoSkip(t *testing.T) { - opts := Options{Order: DFSPre} + ds := mdagtest.Mock() + opts := Options{Order: DFSPre, DAG: ds} - testWalkOutputs(t, newFan(t), opts, []byte(` + testWalkOutputs(t, newFan(t, ds), opts, []byte(` 0 /a 1 /a/aa 1 /a/ab @@ -19,7 +21,7 @@ func TestDFSPreNoSkip(t *testing.T) { 1 /a/ad `)) - testWalkOutputs(t, newLinkedList(t), opts, []byte(` + testWalkOutputs(t, newLinkedList(t, ds), opts, []byte(` 0 /a 1 /a/aa 2 /a/aa/aaa @@ -27,7 +29,7 @@ func TestDFSPreNoSkip(t *testing.T) { 4 /a/aa/aaa/aaaa/aaaaa `)) - testWalkOutputs(t, newBinaryTree(t), opts, []byte(` + testWalkOutputs(t, newBinaryTree(t, ds), opts, []byte(` 0 /a 1 /a/aa 2 /a/aa/aaa @@ -37,7 +39,7 @@ func TestDFSPreNoSkip(t *testing.T) { 2 /a/ab/abb `)) - testWalkOutputs(t, newBinaryDAG(t), opts, []byte(` + testWalkOutputs(t, newBinaryDAG(t, ds), opts, []byte(` 0 /a 1 /a/aa 2 /a/aa/aaa @@ -73,9 +75,10 @@ func TestDFSPreNoSkip(t *testing.T) { } func TestDFSPreSkip(t *testing.T) { - opts := Options{Order: DFSPre, SkipDuplicates: true} + ds := mdagtest.Mock() + opts := Options{Order: DFSPre, SkipDuplicates: true, DAG: ds} - testWalkOutputs(t, newFan(t), opts, []byte(` + testWalkOutputs(t, newFan(t, ds), opts, []byte(` 0 /a 1 /a/aa 1 /a/ab @@ -83,7 +86,7 @@ func TestDFSPreSkip(t *testing.T) { 1 /a/ad `)) - testWalkOutputs(t, newLinkedList(t), opts, []byte(` + testWalkOutputs(t, newLinkedList(t, ds), opts, []byte(` 0 /a 1 /a/aa 2 /a/aa/aaa @@ -91,7 +94,7 @@ func TestDFSPreSkip(t *testing.T) { 4 /a/aa/aaa/aaaa/aaaaa `)) - testWalkOutputs(t, newBinaryTree(t), opts, []byte(` + testWalkOutputs(t, newBinaryTree(t, ds), opts, []byte(` 0 /a 1 /a/aa 2 /a/aa/aaa @@ -101,7 +104,7 @@ func TestDFSPreSkip(t *testing.T) { 2 /a/ab/abb `)) - testWalkOutputs(t, newBinaryDAG(t), opts, []byte(` + testWalkOutputs(t, newBinaryDAG(t, ds), opts, []byte(` 0 /a 1 /a/aa 2 /a/aa/aaa @@ -111,9 +114,10 @@ func TestDFSPreSkip(t *testing.T) { } func TestDFSPostNoSkip(t *testing.T) { - opts := Options{Order: DFSPost} + ds := mdagtest.Mock() + opts := Options{Order: DFSPost, DAG: ds} - testWalkOutputs(t, newFan(t), opts, []byte(` + testWalkOutputs(t, newFan(t, ds), opts, []byte(` 1 /a/aa 1 /a/ab 1 /a/ac @@ -121,7 +125,7 @@ func TestDFSPostNoSkip(t *testing.T) { 0 /a `)) - testWalkOutputs(t, newLinkedList(t), opts, []byte(` + testWalkOutputs(t, newLinkedList(t, ds), opts, []byte(` 4 /a/aa/aaa/aaaa/aaaaa 3 /a/aa/aaa/aaaa 2 /a/aa/aaa @@ -129,7 +133,7 @@ func TestDFSPostNoSkip(t *testing.T) { 0 /a `)) - testWalkOutputs(t, newBinaryTree(t), opts, []byte(` + testWalkOutputs(t, newBinaryTree(t, ds), opts, []byte(` 2 /a/aa/aaa 2 /a/aa/aab 1 /a/aa @@ -139,7 +143,7 @@ func TestDFSPostNoSkip(t *testing.T) { 0 /a `)) - testWalkOutputs(t, newBinaryDAG(t), opts, []byte(` + testWalkOutputs(t, newBinaryDAG(t, ds), opts, []byte(` 4 /a/aa/aaa/aaaa/aaaaa 4 /a/aa/aaa/aaaa/aaaaa 3 /a/aa/aaa/aaaa @@ -175,9 +179,10 @@ func TestDFSPostNoSkip(t *testing.T) { } func TestDFSPostSkip(t *testing.T) { - opts := Options{Order: DFSPost, SkipDuplicates: true} + ds := mdagtest.Mock() + opts := Options{Order: DFSPost, SkipDuplicates: true, DAG: ds} - testWalkOutputs(t, newFan(t), opts, []byte(` + testWalkOutputs(t, newFan(t, ds), opts, []byte(` 1 /a/aa 1 /a/ab 1 /a/ac @@ -185,7 +190,7 @@ func TestDFSPostSkip(t *testing.T) { 0 /a `)) - testWalkOutputs(t, newLinkedList(t), opts, []byte(` + testWalkOutputs(t, newLinkedList(t, ds), opts, []byte(` 4 /a/aa/aaa/aaaa/aaaaa 3 /a/aa/aaa/aaaa 2 /a/aa/aaa @@ -193,7 +198,7 @@ func TestDFSPostSkip(t *testing.T) { 0 /a `)) - testWalkOutputs(t, newBinaryTree(t), opts, []byte(` + testWalkOutputs(t, newBinaryTree(t, ds), opts, []byte(` 2 /a/aa/aaa 2 /a/aa/aab 1 /a/aa @@ -203,7 +208,7 @@ func TestDFSPostSkip(t *testing.T) { 0 /a `)) - testWalkOutputs(t, newBinaryDAG(t), opts, []byte(` + testWalkOutputs(t, newBinaryDAG(t, ds), opts, []byte(` 4 /a/aa/aaa/aaaa/aaaaa 3 /a/aa/aaa/aaaa 2 /a/aa/aaa @@ -213,9 +218,10 @@ func TestDFSPostSkip(t *testing.T) { } func TestBFSNoSkip(t *testing.T) { - opts := Options{Order: BFS} + ds := mdagtest.Mock() + opts := Options{Order: BFS, DAG: ds} - testWalkOutputs(t, newFan(t), opts, []byte(` + testWalkOutputs(t, newFan(t, ds), opts, []byte(` 0 /a 1 /a/aa 1 /a/ab @@ -223,7 +229,7 @@ func TestBFSNoSkip(t *testing.T) { 1 /a/ad `)) - testWalkOutputs(t, newLinkedList(t), opts, []byte(` + testWalkOutputs(t, newLinkedList(t, ds), opts, []byte(` 0 /a 1 /a/aa 2 /a/aa/aaa @@ -231,7 +237,7 @@ func TestBFSNoSkip(t *testing.T) { 4 /a/aa/aaa/aaaa/aaaaa `)) - testWalkOutputs(t, newBinaryTree(t), opts, []byte(` + testWalkOutputs(t, newBinaryTree(t, ds), opts, []byte(` 0 /a 1 /a/aa 1 /a/ab @@ -241,7 +247,7 @@ func TestBFSNoSkip(t *testing.T) { 2 /a/ab/abb `)) - testWalkOutputs(t, newBinaryDAG(t), opts, []byte(` + testWalkOutputs(t, newBinaryDAG(t, ds), opts, []byte(` 0 /a 1 /a/aa 1 /a/aa @@ -277,9 +283,10 @@ func TestBFSNoSkip(t *testing.T) { } func TestBFSSkip(t *testing.T) { - opts := Options{Order: BFS, SkipDuplicates: true} + ds := mdagtest.Mock() + opts := Options{Order: BFS, SkipDuplicates: true, DAG: ds} - testWalkOutputs(t, newFan(t), opts, []byte(` + testWalkOutputs(t, newFan(t, ds), opts, []byte(` 0 /a 1 /a/aa 1 /a/ab @@ -287,7 +294,7 @@ func TestBFSSkip(t *testing.T) { 1 /a/ad `)) - testWalkOutputs(t, newLinkedList(t), opts, []byte(` + testWalkOutputs(t, newLinkedList(t, ds), opts, []byte(` 0 /a 1 /a/aa 2 /a/aa/aaa @@ -295,7 +302,7 @@ func TestBFSSkip(t *testing.T) { 4 /a/aa/aaa/aaaa/aaaaa `)) - testWalkOutputs(t, newBinaryTree(t), opts, []byte(` + testWalkOutputs(t, newBinaryTree(t, ds), opts, []byte(` 0 /a 1 /a/aa 1 /a/ab @@ -305,7 +312,7 @@ func TestBFSSkip(t *testing.T) { 2 /a/ab/abb `)) - testWalkOutputs(t, newBinaryDAG(t), opts, []byte(` + testWalkOutputs(t, newBinaryDAG(t, ds), opts, []byte(` 0 /a 1 /a/aa 2 /a/aa/aaa @@ -341,57 +348,68 @@ func testWalkOutputs(t *testing.T, root *mdag.Node, opts Options, expect []byte) } } -func newFan(t *testing.T) *mdag.Node { +func newFan(t *testing.T, ds mdag.DAGService) *mdag.Node { a := &mdag.Node{Data: []byte("/a")} - addChild(t, a, "aa") - addChild(t, a, "ab") - addChild(t, a, "ac") - addChild(t, a, "ad") + addLink(t, ds, a, child(t, ds, a, "aa")) + addLink(t, ds, a, child(t, ds, a, "ab")) + addLink(t, ds, a, child(t, ds, a, "ac")) + addLink(t, ds, a, child(t, ds, a, "ad")) return a } -func newLinkedList(t *testing.T) *mdag.Node { +func newLinkedList(t *testing.T, ds mdag.DAGService) *mdag.Node { a := &mdag.Node{Data: []byte("/a")} - aa := addChild(t, a, "aa") - aaa := addChild(t, aa, "aaa") - aaaa := addChild(t, aaa, "aaaa") - addChild(t, aaaa, "aaaaa") + aa := child(t, ds, a, "aa") + aaa := child(t, ds, aa, "aaa") + aaaa := child(t, ds, aaa, "aaaa") + aaaaa := child(t, ds, aaaa, "aaaaa") + addLink(t, ds, aaaa, aaaaa) + addLink(t, ds, aaa, aaaa) + addLink(t, ds, aa, aaa) + addLink(t, ds, a, aa) return a } -func newBinaryTree(t *testing.T) *mdag.Node { +func newBinaryTree(t *testing.T, ds mdag.DAGService) *mdag.Node { a := &mdag.Node{Data: []byte("/a")} - aa := addChild(t, a, "aa") - ab := addChild(t, a, "ab") - addChild(t, aa, "aaa") - addChild(t, aa, "aab") - addChild(t, ab, "aba") - addChild(t, ab, "abb") + aa := child(t, ds, a, "aa") + ab := child(t, ds, a, "ab") + addLink(t, ds, aa, child(t, ds, aa, "aaa")) + addLink(t, ds, aa, child(t, ds, aa, "aab")) + addLink(t, ds, ab, child(t, ds, ab, "aba")) + addLink(t, ds, ab, child(t, ds, ab, "abb")) + addLink(t, ds, a, aa) + addLink(t, ds, a, ab) return a } -func newBinaryDAG(t *testing.T) *mdag.Node { +func newBinaryDAG(t *testing.T, ds mdag.DAGService) *mdag.Node { a := &mdag.Node{Data: []byte("/a")} - aa := addChild(t, a, "aa") - aaa := addChild(t, aa, "aaa") - aaaa := addChild(t, aaa, "aaaa") - aaaaa := addChild(t, aaaa, "aaaaa") - addLink(t, a, aa) - addLink(t, aa, aaa) - addLink(t, aaa, aaaa) - addLink(t, aaaa, aaaaa) + aa := child(t, ds, a, "aa") + aaa := child(t, ds, aa, "aaa") + aaaa := child(t, ds, aaa, "aaaa") + aaaaa := child(t, ds, aaaa, "aaaaa") + addLink(t, ds, aaaa, aaaaa) + addLink(t, ds, aaaa, aaaaa) + addLink(t, ds, aaa, aaaa) + addLink(t, ds, aaa, aaaa) + addLink(t, ds, aa, aaa) + addLink(t, ds, aa, aaa) + addLink(t, ds, a, aa) + addLink(t, ds, a, aa) return a } -func addLink(t *testing.T, a, b *mdag.Node) { +func addLink(t *testing.T, ds mdag.DAGService, a, b *mdag.Node) { to := string(a.Data) + "2" + string(b.Data) + if _, err := ds.Add(b); err != nil { + t.Error(err) + } if err := a.AddNodeLink(to, b); err != nil { t.Error(err) } } -func addChild(t *testing.T, a *mdag.Node, name string) *mdag.Node { - c := &mdag.Node{Data: []byte(string(a.Data) + "/" + name)} - addLink(t, a, c) - return c +func child(t *testing.T, ds mdag.DAGService, a *mdag.Node, name string) *mdag.Node { + return &mdag.Node{Data: []byte(string(a.Data) + "/" + name)} } From e7e6ca871d130c88fa5c780dbe332f5d96e7421d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 9 Mar 2016 09:53:19 -0800 Subject: [PATCH 1213/3817] update libp2p dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@6159b3f1358ce1cc4a2bc7f2ddb4b5eed3257012 --- namesys/interface.go | 2 +- namesys/ipns_select_test.go | 2 +- namesys/namesys.go | 2 +- namesys/publisher.go | 6 +++--- namesys/republisher/repub.go | 4 ++-- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 4 ++-- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index 7978d74dd..67f52f891 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -34,7 +34,7 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index b6ffe1c40..80aa83070 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -10,8 +10,8 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" + ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" ) func shuffle(a []*pb.IpnsEntry) { diff --git a/namesys/namesys.go b/namesys/namesys.go index b9c753881..a3820ddae 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,7 +7,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 4f3cfaf2e..186573a27 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -6,8 +6,8 @@ import ( "fmt" "time" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" @@ -19,9 +19,9 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" + ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 2647a3954..e73279c13 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,12 +11,12 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 31195892a..1e7df8dbd 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + mocknet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 69f03f035..9518552d4 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -11,7 +11,7 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/routing.go b/namesys/routing.go index b10c22490..e37dde5e5 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -4,17 +4,17 @@ import ( "fmt" "time" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" lru "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" + ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) From 2d315d86daa512c1715569e363773daa6a6f2962 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 9 Mar 2016 09:53:19 -0800 Subject: [PATCH 1214/3817] update libp2p dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@a72cf948d2f3dd53d70a9402c3eca5108e6f0b0d --- routing/dht/dht.go | 10 +++++----- routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 6 +++--- routing/dht/dht_test.go | 6 +++--- routing/dht/diag.go | 2 +- routing/dht/ext_test.go | 8 ++++---- routing/dht/handlers.go | 2 +- routing/dht/lookup.go | 2 +- routing/dht/notif.go | 4 ++-- routing/dht/pb/message.go | 6 +++--- routing/dht/providers.go | 2 +- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 4 ++-- routing/dht/records.go | 4 ++-- routing/dht/routing.go | 4 ++-- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 2 +- routing/kbucket/table_test.go | 2 +- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 4 ++-- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 2 +- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 6 +++--- routing/record/record.go | 2 +- routing/record/validation.go | 2 +- routing/routing.go | 4 ++-- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 4 ++-- 34 files changed, 61 insertions(+), 61 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index aad30fa51..53ed4d3ef 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -14,16 +14,16 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" - host "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/host" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" - protocol "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/protocol" + ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" + host "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/host" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + protocol "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/protocol" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index b544884fc..4cbfb3c4e 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -9,8 +9,8 @@ import ( "time" routing "github.com/ipfs/go-ipfs/routing" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" periodicproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/periodic" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 8fec44685..3af84e3ec 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -4,11 +4,11 @@ import ( "errors" "time" - ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + inet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index cdd1c5e51..4bb339061 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -11,14 +11,14 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" - netutil "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/test/util" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + netutil "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/test/util" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" diff --git a/routing/dht/diag.go b/routing/dht/diag.go index 3f8a7a929..555f2ed62 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" ) type connDiagInfo struct { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 41048df2a..64745be79 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -7,19 +7,19 @@ import ( "testing" "time" - ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" + inet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 9c9598ffc..ceecfa0ef 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -9,7 +9,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 2def4046b..076f3f532 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -5,7 +5,7 @@ import ( notif "github.com/ipfs/go-ipfs/notifications" kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 2fe6cce40..224fecb9e 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -1,9 +1,9 @@ package dht import ( - ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" + ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" - inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" + inet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index a1c4887e1..d0ff6fa95 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -1,11 +1,11 @@ package dht_pb import ( - ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" + ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" - inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + inet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 2f0a8e64d..94ec0e840 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -4,9 +4,9 @@ import ( "time" key "github.com/ipfs/go-ipfs/blocks/key" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index ec3b7a5d1..b711246c9 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -4,7 +4,7 @@ import ( "testing" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index 518f1e11f..419245c27 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -8,8 +8,8 @@ import ( "github.com/ipfs/go-ipfs/routing" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" todoctr "github.com/ipfs/go-ipfs/thirdparty/todocounter" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" - queue "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer/queue" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + queue "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer/queue" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" diff --git a/routing/dht/records.go b/routing/dht/records.go index 3c9587dfa..f3def809d 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -8,8 +8,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 5a7430122..33bf6a2ac 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,8 +12,8 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + inet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 8924a63d4..e12fe56c2 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index e1e0a1bbf..f96170e37 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 5128b7821..b627d681d 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,7 +7,7 @@ import ( "sync" "time" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index a023dba4e..9be348c53 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,7 +7,7 @@ import ( tu "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index df3237822..8b005315a 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" ) // Returned if a routing table query returns no results. This is NOT expected diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 4f29a5776..134f1477d 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -9,12 +9,12 @@ import ( routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index cc57e6a07..1a131d01a 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -9,7 +9,7 @@ import ( dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 0bafda9b2..d875c9492 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 96be2a489..77603c81c 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,7 +5,7 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 69035c232..9d8e51423 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,7 +10,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 1f7c9749f..be540f754 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,8 +6,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" - p2phost "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/host" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + p2phost "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/host" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 5dd6ceb87..6ce6571c4 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -4,14 +4,14 @@ import ( "errors" "time" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" - "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" + "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/record/record.go b/routing/record/record.go index 78051e696..f17ae9c61 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/record/validation.go b/routing/record/validation.go index a898259c6..bab827da5 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" + ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" ) // ValidatorFunc is a function that is called to validate a given diff --git a/routing/routing.go b/routing/routing.go index 5acce6f54..c039b8c39 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -5,8 +5,8 @@ import ( "errors" key "github.com/ipfs/go-ipfs/blocks/key" - ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 8f7d063b6..d1485f170 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -12,8 +12,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/host" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/host" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 4fc7b7f04..50b741552 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + inet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index e8ec2f434..659f4b186 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -9,9 +9,9 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" - host "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/host" - inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + host "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/host" + inet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 8c79d2172..45374f56b 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -4,15 +4,15 @@ import ( "errors" "fmt" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer" + peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" ) // Server handles routing queries using a database backend From 771e98c53d79d64ba131779e7a29193203f0f19b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 9 Mar 2016 09:53:19 -0800 Subject: [PATCH 1215/3817] update libp2p dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@13358c2e1cd8faa51103505c7cd1293ab350f2c7 --- unixfs/format.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/format.go b/unixfs/format.go index af62f994f..6acb41050 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -6,8 +6,8 @@ package unixfs import ( "errors" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pb "github.com/ipfs/go-ipfs/unixfs/pb" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) const ( From dcad7d641c9456a9edb8ae2b5cdf8700c90c1ff0 Mon Sep 17 00:00:00 2001 From: Chris P Date: Wed, 16 Mar 2016 18:51:15 +0100 Subject: [PATCH 1216/3817] util: Add DefaultIpfsHash constant for programtically accessing the current default. This commit was moved from ipfs/go-ipfs-util@9814eaec2c59803d85016286c561010345e98f05 --- util/util.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/util/util.go b/util/util.go index 1ce3a19b4..019d0420c 100644 --- a/util/util.go +++ b/util/util.go @@ -16,6 +16,9 @@ import ( mh "github.com/jbenet/go-multihash" ) +// DefaultIpfsHash is the current default hash function used by IPFS. +const DefaultIpfsHash = mh.SHA2_256 + // Debug is a global flag for debugging. var Debug bool @@ -115,7 +118,7 @@ func RPartition(subject string, sep string) (string, string, string) { // Hash is the global IPFS hash function. uses multihash SHA2_256, 256 bits func Hash(data []byte) mh.Multihash { - h, err := mh.Sum(data, mh.SHA2_256, -1) + h, err := mh.Sum(data, DefaultIpfsHash, -1) if err != nil { // this error can be safely ignored (panic) because multihash only fails // from the selection of hash function. If the fn + length are valid, it From d7c8aa7989ab98151e7457115564fb50271979c7 Mon Sep 17 00:00:00 2001 From: jbenet Date: Tue, 22 Mar 2016 09:02:29 -0400 Subject: [PATCH 1217/3817] license This commit was moved from ipfs/go-ipfs-util@d6aa22b9506c5587fc5c20489c0d96463569f8d0 --- util/LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 util/LICENSE diff --git a/util/LICENSE b/util/LICENSE new file mode 100644 index 000000000..c7386b3c9 --- /dev/null +++ b/util/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Juan Batiz-Benet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. From b7b8ab23cf3b8d7c28ae246430f9f61018b597c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Sun, 20 Mar 2016 17:07:25 +0100 Subject: [PATCH 1218/3817] clean deprecated Key.Pretty() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Michael Muré This commit was moved from ipfs/go-namesys@f647d44ebf098c094b885fb28020d450365b0a05 --- namesys/resolve_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 9518552d4..742923568 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -42,7 +42,7 @@ func TestRoutingResolve(t *testing.T) { } pkhash := u.Hash(pubkb) - res, err := resolver.Resolve(context.Background(), key.Key(pkhash).Pretty()) + res, err := resolver.Resolve(context.Background(), key.Key(pkhash).B58String()) if err != nil { t.Fatal(err) } From 9f2bab9a18aaa53173af58aa3b771757bef927d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Sun, 20 Mar 2016 17:07:25 +0100 Subject: [PATCH 1219/3817] clean deprecated Key.Pretty() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Michael Muré This commit was moved from ipfs/go-ipfs-routing@521e05f4f4b40acab876c3ce40c8b4a1034c4aeb --- routing/dht/handlers.go | 4 ++-- routing/dht/pb/message.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index ceecfa0ef..bc74af005 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -210,7 +210,7 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb. resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) key := key.Key(pmes.GetKey()) - lm["key"] = func() interface{} { return key.Pretty() } + lm["key"] = func() interface{} { return key.B58String() } // debug logging niceness. reqDesc := fmt.Sprintf("%s handleGetProviders(%s, %s): ", dht.self, p, key) @@ -254,7 +254,7 @@ func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.M defer log.EventBegin(ctx, "handleAddProvider", lm).Done() key := key.Key(pmes.GetKey()) - lm["key"] = func() interface{} { return key.Pretty() } + lm["key"] = func() interface{} { return key.B58String() } log.Debugf("%s adding %s as a provider for '%s'\n", dht.self, p, key) diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index d0ff6fa95..5b9fb4309 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -143,7 +143,7 @@ func (m *Message) Loggable() map[string]interface{} { return map[string]interface{}{ "message": map[string]string{ "type": m.Type.String(), - "key": key.Key(m.GetKey()).Pretty(), + "key": key.Key(m.GetKey()).B58String(), }, } } From 2655942d672d2d5393d1060bcfadf1a9da2fcc50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Sun, 20 Mar 2016 17:07:25 +0100 Subject: [PATCH 1220/3817] clean deprecated Key.Pretty() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Michael Muré This commit was moved from ipfs/go-ipfs-blockstore@f097ef90837f60360c19a5c949d9146591646e90 --- blockstore/blockstore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 685745f00..4987f9670 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -82,7 +82,7 @@ func TestAllKeysSimple(t *testing.T) { keys2 := collect(ch) // for _, k2 := range keys2 { - // t.Log("found ", k2.Pretty()) + // t.Log("found ", k2.B58String()) // } expectMatches(t, keys, keys2) From 07548b24a9322b18bcdbf02de1e33fb0546725da Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 29 Mar 2016 19:18:14 -0700 Subject: [PATCH 1221/3817] update utp and cleanup more godeps along the way License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@746d9328fecf865702a16a90b37cd7acff7bbd37 --- namesys/interface.go | 2 +- namesys/ipns_select_test.go | 2 +- namesys/namesys.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index 67f52f891..fa4bf3b35 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -34,7 +34,7 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index 80aa83070..f6247df1c 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -10,7 +10,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/namesys/namesys.go b/namesys/namesys.go index a3820ddae..1a2fa1849 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,7 +7,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 186573a27..d7875ce27 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -19,8 +19,8 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" - ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index e73279c13..7c31ae36e 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,7 +11,7 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 1e7df8dbd..3bfc6711d 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + mocknet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 742923568..a3ed3d3e2 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -11,7 +11,7 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/routing.go b/namesys/routing.go index e37dde5e5..5a9dd8510 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -13,7 +13,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) From 31f40ca08e935da4c24655539eec8d75875f265c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 29 Mar 2016 19:18:14 -0700 Subject: [PATCH 1222/3817] update utp and cleanup more godeps along the way License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@d5f3462a862e30a70af82b776b7bda04b4c4a0ed --- routing/dht/dht.go | 8 ++++---- routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 4 ++-- routing/dht/dht_test.go | 4 ++-- routing/dht/diag.go | 2 +- routing/dht/ext_test.go | 6 +++--- routing/dht/handlers.go | 2 +- routing/dht/lookup.go | 2 +- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 4 ++-- routing/dht/providers.go | 2 +- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 4 ++-- routing/dht/records.go | 4 ++-- routing/dht/routing.go | 4 ++-- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 2 +- routing/kbucket/table_test.go | 2 +- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 2 +- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 4 ++-- routing/record/record.go | 2 +- routing/record/validation.go | 2 +- routing/routing.go | 4 ++-- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 2 +- 34 files changed, 52 insertions(+), 52 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 53ed4d3ef..2cd9a9c54 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -14,10 +14,10 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" - host "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/host" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" - protocol "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/protocol" + ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" + host "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/host" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + protocol "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/protocol" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 4cbfb3c4e..32e09a5cd 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -9,7 +9,7 @@ import ( "time" routing "github.com/ipfs/go-ipfs/routing" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 3af84e3ec..2d3de7e9d 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,8 +6,8 @@ import ( ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + inet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 4bb339061..a43c118ab 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -17,8 +17,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" - netutil "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/test/util" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + netutil "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/test/util" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" diff --git a/routing/dht/diag.go b/routing/dht/diag.go index 555f2ed62..a43bd2b57 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" ) type connDiagInfo struct { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 64745be79..6656a6db0 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -16,9 +16,9 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - inet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + inet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index bc74af005..d8141e71e 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -9,7 +9,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 076f3f532..d5fbd667a 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -5,7 +5,7 @@ import ( notif "github.com/ipfs/go-ipfs/notifications" kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 224fecb9e..74a31c8f0 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" - inet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net" + inet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 5b9fb4309..7a01ff8b5 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,8 +4,8 @@ import ( ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" - inet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + inet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 94ec0e840..3592c1734 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -4,9 +4,9 @@ import ( "time" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index b711246c9..8c65fa97f 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -4,7 +4,7 @@ import ( "testing" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index 419245c27..d70e80e65 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -8,8 +8,8 @@ import ( "github.com/ipfs/go-ipfs/routing" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" todoctr "github.com/ipfs/go-ipfs/thirdparty/todocounter" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" - queue "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer/queue" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + queue "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer/queue" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" diff --git a/routing/dht/records.go b/routing/dht/records.go index f3def809d..7376a1abe 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -8,8 +8,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 33bf6a2ac..28daaf174 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,8 +12,8 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - inet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + inet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index e12fe56c2..7ce83a0f8 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index f96170e37..a190120b0 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index b627d681d..15b0ed7df 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,7 +7,7 @@ import ( "sync" "time" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 9be348c53..ce11e673c 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,7 +7,7 @@ import ( tu "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 8b005315a..2b055e794 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 134f1477d..fd6442d41 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -9,7 +9,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 1a131d01a..12f2c8fac 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -9,7 +9,7 @@ import ( dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index d875c9492..0ded9e7f5 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 77603c81c..62da12d4e 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,7 +5,7 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 9d8e51423..89425f82e 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,7 +10,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index be540f754..470a0e7f5 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,8 +6,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" - p2phost "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/host" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + p2phost "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/host" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 6ce6571c4..c55b2b7f8 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -9,8 +9,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" - "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" + "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" diff --git a/routing/record/record.go b/routing/record/record.go index f17ae9c61..09bdda4f6 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/record/validation.go b/routing/record/validation.go index bab827da5..9ed37766d 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/routing/routing.go b/routing/routing.go index c039b8c39..eb25d9429 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -5,8 +5,8 @@ import ( "errors" key "github.com/ipfs/go-ipfs/blocks/key" - ci "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index d1485f170..becc4e752 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -12,8 +12,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/host" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/host" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 50b741552..62b018285 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + inet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 659f4b186..8fea1123c 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -9,9 +9,9 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" - host "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/host" - inet "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/net" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + host "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/host" + inet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 45374f56b..7040243b3 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -12,7 +12,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - peer "gx/ipfs/QmNefBbWHR9JEiP3KDVqZsBLQVRmH3GBG2D2Ke24SsFqfW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" ) // Server handles routing queries using a database backend From 3b7b02e2b5a231ca8866d858e89cdaaf0be14b5f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 6 Apr 2016 15:42:06 -0700 Subject: [PATCH 1223/3817] switch to new libp2p with mss crypto License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@e33d8b243f57053130776240800419665860264e --- namesys/interface.go | 2 +- namesys/ipns_select_test.go | 2 +- namesys/namesys.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index fa4bf3b35..e29d3740b 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -34,7 +34,7 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index f6247df1c..2be0381f4 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -10,7 +10,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/namesys/namesys.go b/namesys/namesys.go index 1a2fa1849..e5d6a7f64 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,7 +7,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index d7875ce27..6b5433561 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -19,8 +19,8 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" - ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 7c31ae36e..0b5024476 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,7 +11,7 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 3bfc6711d..c67cdb0a5 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + mocknet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index a3ed3d3e2..b5af6de2d 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -11,7 +11,7 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/routing.go b/namesys/routing.go index 5a9dd8510..cadaa092e 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -13,7 +13,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) From db46906ea35a2e6f17312d6efcf9fe70e492ae86 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 6 Apr 2016 15:42:06 -0700 Subject: [PATCH 1224/3817] switch to new libp2p with mss crypto License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@0a26936a720619152dd49ee636d32cbdd37214ea --- routing/dht/dht.go | 8 ++++---- routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 4 ++-- routing/dht/dht_test.go | 4 ++-- routing/dht/diag.go | 2 +- routing/dht/ext_test.go | 6 +++--- routing/dht/handlers.go | 2 +- routing/dht/lookup.go | 2 +- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 4 ++-- routing/dht/providers.go | 2 +- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 4 ++-- routing/dht/records.go | 4 ++-- routing/dht/routing.go | 4 ++-- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 2 +- routing/kbucket/table_test.go | 2 +- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 2 +- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 4 ++-- routing/record/record.go | 2 +- routing/record/validation.go | 2 +- routing/routing.go | 4 ++-- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 2 +- 34 files changed, 52 insertions(+), 52 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 2cd9a9c54..99b073033 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -14,10 +14,10 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" - host "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/host" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" - protocol "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/protocol" + ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" + host "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/host" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" + protocol "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/protocol" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 32e09a5cd..584f8b5a6 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -9,7 +9,7 @@ import ( "time" routing "github.com/ipfs/go-ipfs/routing" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 2d3de7e9d..3eb6ee371 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,9 +6,9 @@ import ( ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" + inet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index a43c118ab..c8c60d63d 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -17,8 +17,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" - netutil "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/test/util" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" + netutil "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/test/util" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" diff --git a/routing/dht/diag.go b/routing/dht/diag.go index a43bd2b57..c69ada553 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" ) type connDiagInfo struct { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 6656a6db0..e08307c8f 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -16,9 +16,9 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - inet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + inet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index d8141e71e..b8c51aae0 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -9,8 +9,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index d5fbd667a..99c9b45de 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -5,7 +5,7 @@ import ( notif "github.com/ipfs/go-ipfs/notifications" kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 74a31c8f0..69f603a99 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" - inet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net" + inet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 7a01ff8b5..2290de103 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,8 +4,8 @@ import ( ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" - inet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + inet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 3592c1734..e16400291 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -6,7 +6,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 8c65fa97f..3353db301 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -4,7 +4,7 @@ import ( "testing" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index d70e80e65..34a5ca9b8 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -8,8 +8,8 @@ import ( "github.com/ipfs/go-ipfs/routing" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" todoctr "github.com/ipfs/go-ipfs/thirdparty/todocounter" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" - queue "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer/queue" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" + queue "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer/queue" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" diff --git a/routing/dht/records.go b/routing/dht/records.go index 7376a1abe..0dd575607 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -8,8 +8,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 28daaf174..b9d31cda5 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,8 +12,8 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - inet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + inet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 7ce83a0f8..b061a67a0 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index a190120b0..8b1dada12 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 15b0ed7df..e0e398e4d 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,7 +7,7 @@ import ( "sync" "time" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index ce11e673c..4c5057754 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,7 +7,7 @@ import ( tu "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 2b055e794..1d6fe05a3 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index fd6442d41..3824a2b9c 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -9,8 +9,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 12f2c8fac..ac23d4480 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -9,7 +9,7 @@ import ( dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 0ded9e7f5..adf63ae5c 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 62da12d4e..41fc4fe9e 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,7 +5,7 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 89425f82e..894c25f8e 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,7 +10,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 470a0e7f5..4484c0f1c 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,8 +6,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" - p2phost "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/host" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + p2phost "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/host" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index c55b2b7f8..0dc281f1d 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -9,9 +9,9 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" - "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" + "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/record/record.go b/routing/record/record.go index 09bdda4f6..41c408f91 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/record/validation.go b/routing/record/validation.go index 9ed37766d..29e796f3d 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/routing/routing.go b/routing/routing.go index eb25d9429..dc2e6efc5 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -5,8 +5,8 @@ import ( "errors" key "github.com/ipfs/go-ipfs/blocks/key" - ci "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index becc4e752..e712a3251 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -12,8 +12,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/host" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/host" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 62b018285..7f9e0be1d 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + inet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 8fea1123c..5f1197308 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -9,9 +9,9 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" - host "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/host" - inet "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/net" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + host "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/host" + inet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 7040243b3..0993b55ea 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -12,7 +12,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - peer "gx/ipfs/QmSN2ELGRp4T9kjqiSsSNJRUeR9JKXzQEgwe1HH3tdSGbC/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" ) // Server handles routing queries using a database backend From 37d9a65a8289d734e1f1ceecd0fce99e8031473c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 14 Feb 2016 23:58:45 -0800 Subject: [PATCH 1225/3817] fix dht command key escaping License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@0ad133af6d998128a8dbc496d7793245a3c7dd41 --- namesys/publisher.go | 1 + 1 file changed, 1 insertion(+) diff --git a/namesys/publisher.go b/namesys/publisher.go index 6b5433561..ba7353f6a 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -141,6 +141,7 @@ func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqn return err } + log.Error("KEY: ", []byte(namekey)) ttl, ok := checkCtxTTL(ctx) if ok { entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds())) From 98dc01f731b82cb0e27d5d8949077d280f586ec8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 15 Feb 2016 00:49:06 -0800 Subject: [PATCH 1226/3817] Remove debug log License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@da22186d9637f25bf084283b69f7ceac03009b5a --- namesys/publisher.go | 1 - 1 file changed, 1 deletion(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index ba7353f6a..6b5433561 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -141,7 +141,6 @@ func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqn return err } - log.Error("KEY: ", []byte(namekey)) ttl, ok := checkCtxTTL(ctx) if ok { entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds())) From f2acf889fe058a835d52592654bd05619305e68a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 27 Jan 2016 18:55:53 -0800 Subject: [PATCH 1227/3817] allow promises to fail License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@1535a6a9e574f7c3c56f9b518e842333959d786c --- ipld/merkledag/merkledag.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index df6fa4187..3761096cc 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -192,6 +192,9 @@ func GetNodes(ctx context.Context, ds DAGService, keys []key.Key) []NodeGetter { select { case opt, ok := <-nodechan: if !ok { + for _, p := range promises { + p.Fail(ErrNotFound) + } return } @@ -239,6 +242,7 @@ func newNodePromise(ctx context.Context) (NodeGetter, chan<- *Node) { return &nodePromise{ recv: ch, ctx: ctx, + err: make(chan error, 1), }, ch } @@ -246,6 +250,7 @@ type nodePromise struct { cache *Node recv <-chan *Node ctx context.Context + err chan error } // NodeGetter provides a promise like interface for a dag Node @@ -254,6 +259,11 @@ type nodePromise struct { // cached node. type NodeGetter interface { Get(context.Context) (*Node, error) + Fail(err error) +} + +func (np *nodePromise) Fail(err error) { + np.err <- err } func (np *nodePromise) Get(ctx context.Context) (*Node, error) { @@ -268,6 +278,8 @@ func (np *nodePromise) Get(ctx context.Context) (*Node, error) { return nil, np.ctx.Err() case <-ctx.Done(): return nil, ctx.Err() + case err := <-np.err: + return nil, err } return np.cache, nil } From 1e9171a2594a671fdf5c598699426cfa252912dd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 11 Apr 2016 12:52:54 -0700 Subject: [PATCH 1228/3817] update libp2p dep to fix hanging listeners problem License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@a2a78b3f8a846401b2d74499b052efc26b7d378e --- routing/dht/dht.go | 8 ++++---- routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 4 ++-- routing/dht/dht_test.go | 4 ++-- routing/dht/diag.go | 2 +- routing/dht/ext_test.go | 6 +++--- routing/dht/handlers.go | 2 +- routing/dht/lookup.go | 2 +- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 4 ++-- routing/dht/providers.go | 2 +- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 4 ++-- routing/dht/records.go | 4 ++-- routing/dht/routing.go | 4 ++-- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 2 +- routing/kbucket/table_test.go | 2 +- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 2 +- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 4 ++-- routing/record/record.go | 2 +- routing/record/validation.go | 2 +- routing/routing.go | 4 ++-- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 2 +- 34 files changed, 52 insertions(+), 52 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 99b073033..10f6efefc 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -14,11 +14,11 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" - host "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/host" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" - protocol "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/protocol" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" + host "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/host" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" + protocol "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/protocol" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 584f8b5a6..4427134df 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -9,8 +9,8 @@ import ( "time" routing "github.com/ipfs/go-ipfs/routing" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" periodicproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/periodic" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 3eb6ee371..75525a8a8 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -7,9 +7,9 @@ import ( ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" pb "github.com/ipfs/go-ipfs/routing/dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - inet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + inet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // handleNewStream implements the inet.StreamHandler diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index c8c60d63d..3f3280c26 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -17,9 +17,9 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" - netutil "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/test/util" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" + netutil "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/test/util" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" diff --git a/routing/dht/diag.go b/routing/dht/diag.go index c69ada553..0a3679c05 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) type connDiagInfo struct { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index e08307c8f..4c1af12eb 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -16,10 +16,10 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - inet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + inet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index b8c51aae0..a1e133646 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -10,9 +10,9 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // The number of closer peers to send on requests. diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 99c9b45de..ccbae2318 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -5,8 +5,8 @@ import ( notif "github.com/ipfs/go-ipfs/notifications" kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // Required in order for proper JSON marshaling diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 69f603a99..3e39c12a7 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" - inet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net" + inet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 2290de103..7219bd3ed 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,9 +4,9 @@ import ( ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" - inet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + inet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/providers.go b/routing/dht/providers.go index e16400291..be84a7207 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -6,7 +6,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 3353db301..7729bb889 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -4,7 +4,7 @@ import ( "testing" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index 34a5ca9b8..85e80e42c 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -8,10 +8,10 @@ import ( "github.com/ipfs/go-ipfs/routing" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" todoctr "github.com/ipfs/go-ipfs/thirdparty/todocounter" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" - queue "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer/queue" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" + queue "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer/queue" process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" ctxproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" diff --git a/routing/dht/records.go b/routing/dht/records.go index 0dd575607..504b0f2db 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -8,9 +8,9 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // MaxRecordAge specifies the maximum time that any node will hold onto a record diff --git a/routing/dht/routing.go b/routing/dht/routing.go index b9d31cda5..af1f725e8 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,9 +12,9 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - inet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + inet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // asyncQueryBuffer is the size of buffered channels in async queries. This diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index b061a67a0..aebb4e5f5 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index 8b1dada12..d188554d7 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index e0e398e4d..336fc3de1 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,8 +7,8 @@ import ( "sync" "time" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) var log = logging.Logger("table") diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 4c5057754..90f178186 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,7 +7,7 @@ import ( tu "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 1d6fe05a3..b81c4c05b 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // Returned if a routing table query returns no results. This is NOT expected diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 3824a2b9c..71720e254 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -10,10 +10,10 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" "github.com/ipfs/go-ipfs/thirdparty/testutil" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" ) diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index ac23d4480..015f07490 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -9,8 +9,8 @@ import ( dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index adf63ae5c..515ad3db3 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 41fc4fe9e..95f5e2ab0 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,8 +5,8 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + mocknet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 894c25f8e..50a01c326 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,8 +10,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // Server provides mockrouting Clients diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 4484c0f1c..a065c0727 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,10 +6,10 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" - p2phost "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/host" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + p2phost "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/host" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 0dc281f1d..0e5582a4d 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -10,10 +10,10 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" - "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" + "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) var log = logging.Logger("offlinerouting") diff --git a/routing/record/record.go b/routing/record/record.go index 41c408f91..d20c3f213 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" ) var log = logging.Logger("routing/record") diff --git a/routing/record/validation.go b/routing/record/validation.go index 29e796f3d..71d3a01d9 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" ) // ValidatorFunc is a function that is called to validate a given diff --git a/routing/routing.go b/routing/routing.go index dc2e6efc5..8305ca185 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -5,9 +5,9 @@ import ( "errors" key "github.com/ipfs/go-ipfs/blocks/key" - ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // ErrNotFound is returned when a search fails to find anything diff --git a/routing/supernode/client.go b/routing/supernode/client.go index e712a3251..7a46b90d7 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -12,9 +12,9 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/host" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/host" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 7f9e0be1d..0985fbf83 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" + inet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 5f1197308..0be8ccaef 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -9,10 +9,10 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" - host "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/host" - inet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + host "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/host" + inet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 0993b55ea..81cd1b22f 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -12,7 +12,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // Server handles routing queries using a database backend From 9e46c96c41445c8a281ba1e2c288b3959a70808a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 11 Apr 2016 12:52:54 -0700 Subject: [PATCH 1229/3817] update libp2p dep to fix hanging listeners problem License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@9c73fc24d373a2415a0a12d67bda1fcbb0b93110 --- namesys/interface.go | 2 +- namesys/ipns_select_test.go | 2 +- namesys/namesys.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index e29d3740b..b492bc93e 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -34,8 +34,8 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" ) const ( diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index 2be0381f4..b62b1c98a 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -10,8 +10,8 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" ) func shuffle(a []*pb.IpnsEntry) { diff --git a/namesys/namesys.go b/namesys/namesys.go index e5d6a7f64..62d21072b 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,8 +7,8 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/publisher.go b/namesys/publisher.go index 6b5433561..aa97dd73c 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -19,9 +19,9 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" - ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 0b5024476..e22031528 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,7 +11,7 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index c67cdb0a5..4b281b0d2 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" + mocknet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index b5af6de2d..6550849aa 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -11,9 +11,9 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index cadaa092e..1520bfb68 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -13,9 +13,9 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ci "gx/ipfs/QmZMehXD2w81qeVJP6r1mmocxwsD7kqAvuzGm2QWDw1H88/go-libp2p/p2p/crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" ) var log = logging.Logger("namesys") From 1031fda3618895899f4b04bb3d73209f3fe76760 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 10 Feb 2016 21:42:17 -0800 Subject: [PATCH 1230/3817] don't fail promises that already succeeded License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@70e5c88f8d6aff655c0dbbbc27af90539c3b6941 --- ipld/merkledag/merkledag.go | 57 ++++++++++++++++++++++++-------- ipld/merkledag/merkledag_test.go | 44 ++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 14 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 3761096cc..6a6ad0ecd 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -176,9 +176,8 @@ func GetNodes(ctx context.Context, ds DAGService, keys []key.Key) []NodeGetter { } promises := make([]NodeGetter, len(keys)) - sendChans := make([]chan<- *Node, len(keys)) for i := range keys { - promises[i], sendChans[i] = newNodePromise(ctx) + promises[i] = newNodePromise(ctx) } dedupedKeys := dedupeKeys(keys) @@ -199,7 +198,9 @@ func GetNodes(ctx context.Context, ds DAGService, keys []key.Key) []NodeGetter { } if opt.Err != nil { - log.Error("error fetching: ", opt.Err) + for _, p := range promises { + p.Fail(opt.Err) + } return } @@ -214,7 +215,7 @@ func GetNodes(ctx context.Context, ds DAGService, keys []key.Key) []NodeGetter { is := FindLinks(keys, k, 0) for _, i := range is { count++ - sendChans[i] <- nd + promises[i].Send(nd) } case <-ctx.Done(): return @@ -237,18 +238,18 @@ func dedupeKeys(ks []key.Key) []key.Key { return out } -func newNodePromise(ctx context.Context) (NodeGetter, chan<- *Node) { - ch := make(chan *Node, 1) +func newNodePromise(ctx context.Context) NodeGetter { return &nodePromise{ - recv: ch, + recv: make(chan *Node, 1), ctx: ctx, err: make(chan error, 1), - }, ch + } } type nodePromise struct { cache *Node - recv <-chan *Node + clk sync.Mutex + recv chan *Node ctx context.Context err chan error } @@ -260,20 +261,49 @@ type nodePromise struct { type NodeGetter interface { Get(context.Context) (*Node, error) Fail(err error) + Send(*Node) } func (np *nodePromise) Fail(err error) { + np.clk.Lock() + v := np.cache + np.clk.Unlock() + + // if promise has a value, don't fail it + if v != nil { + return + } + np.err <- err } -func (np *nodePromise) Get(ctx context.Context) (*Node, error) { +func (np *nodePromise) Send(nd *Node) { + var already bool + np.clk.Lock() if np.cache != nil { - return np.cache, nil + already = true + } + np.cache = nd + np.clk.Unlock() + + if already { + panic("sending twice to the same promise is an error!") + } + + np.recv <- nd +} + +func (np *nodePromise) Get(ctx context.Context) (*Node, error) { + np.clk.Lock() + c := np.cache + np.clk.Unlock() + if c != nil { + return c, nil } select { - case blk := <-np.recv: - np.cache = blk + case nd := <-np.recv: + return nd, nil case <-np.ctx.Done(): return nil, np.ctx.Err() case <-ctx.Done(): @@ -281,7 +311,6 @@ func (np *nodePromise) Get(ctx context.Context) (*Node, error) { case err := <-np.err: return nil, err } - return np.cache, nil } type Batch struct { diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 8137496d8..e475fa680 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -20,6 +20,7 @@ import ( imp "github.com/ipfs/go-ipfs/importer" chunk "github.com/ipfs/go-ipfs/importer/chunk" . "github.com/ipfs/go-ipfs/merkledag" + dstest "github.com/ipfs/go-ipfs/merkledag/test" "github.com/ipfs/go-ipfs/pin" uio "github.com/ipfs/go-ipfs/unixfs/io" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" @@ -323,3 +324,46 @@ func TestEnumerateChildren(t *testing.T) { traverse(root) } + +func TestFetchFailure(t *testing.T) { + ds := dstest.Mock() + ds_bad := dstest.Mock() + + top := new(Node) + for i := 0; i < 10; i++ { + nd := &Node{Data: []byte{byte('a' + i)}} + _, err := ds.Add(nd) + if err != nil { + t.Fatal(err) + } + + err = top.AddNodeLinkClean(fmt.Sprintf("AA%d", i), nd) + if err != nil { + t.Fatal(err) + } + } + + for i := 0; i < 10; i++ { + nd := &Node{Data: []byte{'f', 'a' + byte(i)}} + _, err := ds_bad.Add(nd) + if err != nil { + t.Fatal(err) + } + + err = top.AddNodeLinkClean(fmt.Sprintf("BB%d", i), nd) + if err != nil { + t.Fatal(err) + } + } + + getters := GetDAG(context.Background(), ds, top) + for i, getter := range getters { + _, err := getter.Get(context.Background()) + if err != nil && i < 10 { + t.Fatal(err) + } + if err == nil && i >= 10 { + t.Fatal("should have failed request") + } + } +} From 1074a079b4f9bba2e6470b8185a77d698342dfcd Mon Sep 17 00:00:00 2001 From: Stephen Whitmore Date: Wed, 6 Apr 2016 12:31:06 -0700 Subject: [PATCH 1231/3817] mfs.Mkdir returns the final Directory it creates License: MIT Signed-off-by: Stephen Whitmore This commit was moved from ipfs/go-mfs@a40b1ab3793a8a38cc5c141306c1ac40d28e7c88 --- mfs/ops.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mfs/ops.go b/mfs/ops.go index b02d64fd1..e5c36206a 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -100,9 +100,9 @@ func PutNode(r *Root, path string, nd *dag.Node) error { // Mkdir creates a directory at 'path' under the directory 'd', creating // intermediary directories as needed if 'mkparents' is set to true -func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { +func Mkdir(r *Root, pth string, mkparents bool, flush bool) (*Directory, error) { if pth == "" { - return nil + return nil, nil } parts := path.SplitList(pth) if parts[0] == "" { @@ -117,9 +117,9 @@ func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { if len(parts) == 0 { // this will only happen on 'mkdir /' if mkparents { - return nil + return nil, nil } - return fmt.Errorf("cannot create directory '/': Already exists") + return nil, fmt.Errorf("cannot create directory '/': Already exists") } cur := r.GetValue().(*Directory) @@ -128,16 +128,16 @@ func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { if err == os.ErrNotExist && mkparents { mkd, err := cur.Mkdir(d) if err != nil { - return err + return nil, err } fsn = mkd } else if err != nil { - return err + return nil, err } next, ok := fsn.(*Directory) if !ok { - return fmt.Errorf("%s was not a directory", path.Join(parts[:i])) + return nil, fmt.Errorf("%s was not a directory", path.Join(parts[:i])) } cur = next } @@ -145,18 +145,18 @@ func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { final, err := cur.Mkdir(parts[len(parts)-1]) if err != nil { if !mkparents || err != os.ErrExist || final == nil { - return err + return nil, err } } if flush { err := final.Flush() if err != nil { - return err + return nil, err } } - return nil + return final, nil } func Lookup(r *Root, path string) (FSNode, error) { From b7a5e248126a57ff2c8a3cacc4af37ee3c0f2044 Mon Sep 17 00:00:00 2001 From: Stephen Whitmore Date: Wed, 6 Apr 2016 19:25:17 -0700 Subject: [PATCH 1232/3817] Cache files/dirs when added. License: MIT Signed-off-by: Stephen Whitmore This commit was moved from ipfs/go-mfs@8b1e520cc2418cddd2fcad19cb9c0fbb1d871910 --- mfs/dir.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/mfs/dir.go b/mfs/dir.go index b73d8ad7c..ba14464ae 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -116,6 +116,11 @@ func (d *Directory) childNode(name string) (FSNode, error) { return nil, err } + return d.cacheNode(name, nd) +} + +// cacheNode caches a node into d.childDirs or d.files and returns the FSNode. +func (d *Directory) cacheNode(name string, nd *dag.Node) (FSNode, error) { i, err := ft.FromBytes(nd.Data) if err != nil { return nil, err @@ -334,6 +339,17 @@ func (d *Directory) AddChild(name string, nd *dag.Node) error { d.modTime = time.Now() + if len(nd.Links) == 0 { + nfi, err := NewFile(name, nd, d, d.dserv) + if err != nil { + return err + } + d.files[name] = nfi + } else { + ndir := NewDirectory(d.ctx, name, nd, d, d.dserv) + d.childDirs[name] = ndir + } + return nil } From 8f514f18ea11f96bc311b643d4bd16087a75cd53 Mon Sep 17 00:00:00 2001 From: Stephen Whitmore Date: Fri, 8 Apr 2016 14:33:38 -0700 Subject: [PATCH 1233/3817] More thorough error checking. License: MIT Signed-off-by: Stephen Whitmore This commit was moved from ipfs/go-mfs@edc2a19a9eb8d129b81bca689e8201648dbb6c1d --- mfs/ops.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/ops.go b/mfs/ops.go index e5c36206a..e45c367d7 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -102,7 +102,7 @@ func PutNode(r *Root, path string, nd *dag.Node) error { // intermediary directories as needed if 'mkparents' is set to true func Mkdir(r *Root, pth string, mkparents bool, flush bool) (*Directory, error) { if pth == "" { - return nil, nil + return nil, fmt.Errorf("no path given to Mkdir") } parts := path.SplitList(pth) if parts[0] == "" { From 2b076bf6b8ef213795d52e2698f5b5ac9c55ec20 Mon Sep 17 00:00:00 2001 From: Stephen Whitmore Date: Tue, 12 Apr 2016 13:30:09 -0700 Subject: [PATCH 1234/3817] Revert "mfs.Mkdir returns the final Directory it creates" This reverts commit dfd98f27b25868c770cb1d50c3a3a82e5f53453d. License: MIT Signed-off-by: Stephen Whitmore This commit was moved from ipfs/go-mfs@f8a6382480a92a89e8cbf7c9a5f6786afc23b430 --- mfs/ops.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mfs/ops.go b/mfs/ops.go index e45c367d7..950552f1b 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -100,9 +100,9 @@ func PutNode(r *Root, path string, nd *dag.Node) error { // Mkdir creates a directory at 'path' under the directory 'd', creating // intermediary directories as needed if 'mkparents' is set to true -func Mkdir(r *Root, pth string, mkparents bool, flush bool) (*Directory, error) { +func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { if pth == "" { - return nil, fmt.Errorf("no path given to Mkdir") + return fmt.Errorf("no path given to Mkdir") } parts := path.SplitList(pth) if parts[0] == "" { @@ -117,9 +117,9 @@ func Mkdir(r *Root, pth string, mkparents bool, flush bool) (*Directory, error) if len(parts) == 0 { // this will only happen on 'mkdir /' if mkparents { - return nil, nil + return nil } - return nil, fmt.Errorf("cannot create directory '/': Already exists") + return fmt.Errorf("cannot create directory '/': Already exists") } cur := r.GetValue().(*Directory) @@ -128,16 +128,16 @@ func Mkdir(r *Root, pth string, mkparents bool, flush bool) (*Directory, error) if err == os.ErrNotExist && mkparents { mkd, err := cur.Mkdir(d) if err != nil { - return nil, err + return err } fsn = mkd } else if err != nil { - return nil, err + return err } next, ok := fsn.(*Directory) if !ok { - return nil, fmt.Errorf("%s was not a directory", path.Join(parts[:i])) + return fmt.Errorf("%s was not a directory", path.Join(parts[:i])) } cur = next } @@ -145,18 +145,18 @@ func Mkdir(r *Root, pth string, mkparents bool, flush bool) (*Directory, error) final, err := cur.Mkdir(parts[len(parts)-1]) if err != nil { if !mkparents || err != os.ErrExist || final == nil { - return nil, err + return err } } if flush { err := final.Flush() if err != nil { - return nil, err + return err } } - return final, nil + return nil } func Lookup(r *Root, path string) (FSNode, error) { From 856c400ec88bddef16e48c4ba58921d893e22f8e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 13 Apr 2016 11:04:36 -0700 Subject: [PATCH 1235/3817] remove a ton of unused godeps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@c55cb13464aaf203a74771b8d1f6e0dfb45cae22 --- path/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/path.go b/path/path.go index c06973417..790168de0 100644 --- a/path/path.go +++ b/path/path.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" - b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" + b58 "gx/ipfs/QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf/go-base58" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" ) From 4e2337b34893d5db6b241da171a0ed6711bedb0d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 14 Apr 2016 13:06:51 -0700 Subject: [PATCH 1236/3817] basic implementation of object diff License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@868b4b061576b5aede20468bc91db20ad63cd674 --- ipld/merkledag/utils/diff.go | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 3237ad913..493394437 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -75,17 +75,25 @@ func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.Node, cs []*Cha return e.Finalize(ds) } -func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) []*Change { +func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) ([]*Change, error) { if len(a.Links) == 0 && len(b.Links) == 0 { - ak, _ := a.Key() - bk, _ := b.Key() + ak, err := a.Key() + if err != nil { + return nil, err + } + + bk, err := b.Key() + if err != nil { + return nil, err + } + return []*Change{ &Change{ Type: Mod, Before: ak, After: bk, }, - } + }, nil } var out []*Change @@ -99,9 +107,20 @@ func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) []*Change { if bytes.Equal(l.Hash, lnk.Hash) { // no change... ignore it } else { - anode, _ := lnk.GetNode(ctx, ds) - bnode, _ := l.GetNode(ctx, ds) - sub := Diff(ctx, ds, anode, bnode) + anode, err := lnk.GetNode(ctx, ds) + if err != nil { + return nil, err + } + + bnode, err := l.GetNode(ctx, ds) + if err != nil { + return nil, err + } + + sub, err := Diff(ctx, ds, anode, bnode) + if err != nil { + return nil, err + } for _, subc := range sub { subc.Path = path.Join(lnk.Name, subc.Path) @@ -128,7 +147,7 @@ func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) []*Change { }) } - return out + return out, nil } type Conflict struct { From 4703f6a43037116b88a58bf185cecb6eb1e39a78 Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Sat, 16 Apr 2016 21:23:47 -0700 Subject: [PATCH 1237/3817] Update go-libp2p License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/go-namesys@7928016566bf85277c05a00e9443e1a01c464dd9 --- namesys/interface.go | 2 +- namesys/ipns_select_test.go | 2 +- namesys/namesys.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index b492bc93e..52f934efb 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -34,8 +34,8 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" + ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" ) const ( diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index b62b1c98a..0ad5632e0 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -10,8 +10,8 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" + ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" ) func shuffle(a []*pb.IpnsEntry) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 62d21072b..ac0efd8d9 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,8 +7,8 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" + ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/publisher.go b/namesys/publisher.go index aa97dd73c..5cd03c741 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -19,9 +19,9 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" + ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index e22031528..8f0184cb9 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,7 +11,7 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 4b281b0d2..cad47d478 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" + mocknet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 6550849aa..ffdbac4f1 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -11,9 +11,9 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index 1520bfb68..1c91230d6 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -13,9 +13,9 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" + ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" - ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" ) var log = logging.Logger("namesys") From 5d954c3c5fe43dab4bb1fa51c45c8a32d1369dab Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Sat, 16 Apr 2016 21:23:47 -0700 Subject: [PATCH 1238/3817] Update go-libp2p License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/go-ipfs-routing@279515968ce93fe8f7cba095605470ae9782165d --- routing/dht/dht.go | 8 ++++---- routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 4 ++-- routing/dht/dht_test.go | 4 ++-- routing/dht/diag.go | 2 +- routing/dht/ext_test.go | 6 +++--- routing/dht/handlers.go | 2 +- routing/dht/lookup.go | 2 +- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 4 ++-- routing/dht/providers.go | 2 +- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 4 ++-- routing/dht/records.go | 4 ++-- routing/dht/routing.go | 4 ++-- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 2 +- routing/kbucket/table_test.go | 2 +- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 2 +- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 4 ++-- routing/record/record.go | 2 +- routing/record/validation.go | 2 +- routing/routing.go | 4 ++-- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 2 +- 34 files changed, 52 insertions(+), 52 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 10f6efefc..2f834ccf7 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -14,11 +14,11 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" + ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" + host "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/host" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + protocol "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/protocol" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" - ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" - host "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/host" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" - protocol "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/protocol" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 4427134df..d660153e7 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -9,8 +9,8 @@ import ( "time" routing "github.com/ipfs/go-ipfs/routing" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" periodicproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/periodic" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 75525a8a8..9a085c17b 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,10 +6,10 @@ import ( ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" pb "github.com/ipfs/go-ipfs/routing/dht/pb" + inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - inet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // handleNewStream implements the inet.StreamHandler diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 3f3280c26..3e369f1d6 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -17,9 +17,9 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + netutil "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/test/util" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" - netutil "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/test/util" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" diff --git a/routing/dht/diag.go b/routing/dht/diag.go index 0a3679c05..c451a470d 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" ) type connDiagInfo struct { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 4c1af12eb..54904291d 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -16,10 +16,10 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" + inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - inet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index a1e133646..474cd82d0 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -9,10 +9,10 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // The number of closer peers to send on requests. diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index ccbae2318..ba08ea3ae 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -5,8 +5,8 @@ import ( notif "github.com/ipfs/go-ipfs/notifications" kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // Required in order for proper JSON marshaling diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 3e39c12a7..baf5f81e8 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" - inet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net" + inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 7219bd3ed..d329ef44a 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,9 +4,9 @@ import ( ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" + inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" - inet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/providers.go b/routing/dht/providers.go index be84a7207..0851319ea 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -6,7 +6,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 7729bb889..5fa0aa754 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -4,7 +4,7 @@ import ( "testing" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index 85e80e42c..5174370bf 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -8,10 +8,10 @@ import ( "github.com/ipfs/go-ipfs/routing" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" todoctr "github.com/ipfs/go-ipfs/thirdparty/todocounter" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + queue "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer/queue" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" - queue "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer/queue" process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" ctxproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" diff --git a/routing/dht/records.go b/routing/dht/records.go index 504b0f2db..05f628c47 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -8,9 +8,9 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" + ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // MaxRecordAge specifies the maximum time that any node will hold onto a record diff --git a/routing/dht/routing.go b/routing/dht/routing.go index af1f725e8..46c7de416 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,9 +12,9 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" + inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - inet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // asyncQueryBuffer is the size of buffered channels in async queries. This diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index aebb4e5f5..6ec1fcd75 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index d188554d7..c23b49e6c 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 336fc3de1..c6cce4c88 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,8 +7,8 @@ import ( "sync" "time" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) var log = logging.Logger("table") diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 90f178186..b6b44ed03 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,7 +7,7 @@ import ( tu "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index b81c4c05b..4588702ce 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // Returned if a routing table query returns no results. This is NOT expected diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 71720e254..574085bc7 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -9,11 +9,11 @@ import ( routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" "github.com/ipfs/go-ipfs/thirdparty/testutil" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" ) diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 015f07490..b809de1dd 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -9,8 +9,8 @@ import ( dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 515ad3db3..8cdc8e9e1 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 95f5e2ab0..c276f97c9 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,8 +5,8 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" + mocknet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - mocknet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 50a01c326..25f95d72c 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,8 +10,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // Server provides mockrouting Clients diff --git a/routing/none/none_client.go b/routing/none/none_client.go index a065c0727..f414dfdae 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,10 +6,10 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" + p2phost "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/host" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" - p2phost "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/host" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 0e5582a4d..4a2bb7fe8 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -9,11 +9,11 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" + ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" + "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" - ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" - "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) var log = logging.Logger("offlinerouting") diff --git a/routing/record/record.go b/routing/record/record.go index d20c3f213..ca3084017 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" + ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" - ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" ) var log = logging.Logger("routing/record") diff --git a/routing/record/validation.go b/routing/record/validation.go index 71d3a01d9..c0d9ce198 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" + ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" ) // ValidatorFunc is a function that is called to validate a given diff --git a/routing/routing.go b/routing/routing.go index 8305ca185..34200a1ae 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -5,9 +5,9 @@ import ( "errors" key "github.com/ipfs/go-ipfs/blocks/key" + ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ci "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) // ErrNotFound is returned when a search fails to find anything diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 7a46b90d7..7590fd47a 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -12,9 +12,9 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" + "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/host" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" - "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/host" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 0985fbf83..66f24dc86 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" + inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 0be8ccaef..dc0df97dd 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -9,10 +9,10 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" + host "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/host" + inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" - host "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/host" - inet "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/net" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 81cd1b22f..ca7d387a6 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -12,7 +12,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - peer "gx/ipfs/QmccGfZs3rzku8Bv6sTPH3bMUKD1EVod8srgRjt5csdmva/go-libp2p/p2p/peer" + peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" ) // Server handles routing queries using a database backend From 0c2f7cda4ed296d9964dd89525ba7d399fd61bb4 Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Sat, 16 Apr 2016 21:38:22 -0700 Subject: [PATCH 1239/3817] Use extracted go-libp2p-crypto, -secio, -peer packages License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/go-namesys@8bc828e524d160f37362e253c6f1bbe38b15db0a --- namesys/interface.go | 2 +- namesys/ipns_select_test.go | 2 +- namesys/namesys.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index 52f934efb..c70bb86f2 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -34,7 +34,7 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index 0ad5632e0..aadaa9795 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -10,7 +10,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/namesys/namesys.go b/namesys/namesys.go index ac0efd8d9..727aa71ce 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,7 +7,7 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 5cd03c741..970472546 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -19,9 +19,9 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" - ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 8f0184cb9..c84c30d2e 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,7 +11,7 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index cad47d478..6fa925266 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" mocknet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index ffdbac4f1..995e80ee2 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -11,8 +11,8 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/routing.go b/namesys/routing.go index 1c91230d6..e3c6cc610 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -13,7 +13,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) From 3f872ef7f2255abbe500b3cfa5cb9561cc3ccc16 Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Sat, 16 Apr 2016 21:38:22 -0700 Subject: [PATCH 1240/3817] Use extracted go-libp2p-crypto, -secio, -peer packages License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/go-ipfs-routing@148d097451c87fec529336a799c68c372420b8a6 --- routing/dht/dht.go | 4 ++-- routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 2 +- routing/dht/dht_test.go | 2 +- routing/dht/diag.go | 2 +- routing/dht/ext_test.go | 2 +- routing/dht/handlers.go | 2 +- routing/dht/lookup.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/providers.go | 2 +- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 4 ++-- routing/dht/records.go | 4 ++-- routing/dht/routing.go | 2 +- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 2 +- routing/kbucket/table_test.go | 2 +- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/interface.go | 2 +- routing/none/none_client.go | 2 +- routing/offline/offline.go | 4 ++-- routing/record/record.go | 2 +- routing/record/validation.go | 2 +- routing/routing.go | 4 ++-- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- 32 files changed, 37 insertions(+), 37 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 2f834ccf7..e0010bf5f 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -14,10 +14,10 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" host "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/host" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" protocol "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/protocol" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index d660153e7..bfe5cc091 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -9,8 +9,8 @@ import ( "time" routing "github.com/ipfs/go-ipfs/routing" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" periodicproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/periodic" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 9a085c17b..a0d898972 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -7,8 +7,8 @@ import ( ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" pb "github.com/ipfs/go-ipfs/routing/dht/pb" inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 3e369f1d6..c3ff566bf 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -17,9 +17,9 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" netutil "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/test/util" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" diff --git a/routing/dht/diag.go b/routing/dht/diag.go index c451a470d..e87464b71 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" ) type connDiagInfo struct { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 54904291d..ac9b01749 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -18,8 +18,8 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" mocknet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 474cd82d0..c439834cc 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -9,9 +9,9 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index ba08ea3ae..975967ff2 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -5,7 +5,7 @@ import ( notif "github.com/ipfs/go-ipfs/notifications" kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index d329ef44a..64f671a36 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -5,7 +5,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 0851319ea..2656e0284 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -6,7 +6,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 5fa0aa754..e88b982f1 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -4,7 +4,7 @@ import ( "testing" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index 5174370bf..4a733ca9b 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -8,9 +8,9 @@ import ( "github.com/ipfs/go-ipfs/routing" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" todoctr "github.com/ipfs/go-ipfs/thirdparty/todocounter" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" - queue "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer/queue" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + queue "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer/queue" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/routing/dht/records.go b/routing/dht/records.go index 05f628c47..2d74be310 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -8,8 +8,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 46c7de416..5fdbae82b 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -13,7 +13,7 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 6ec1fcd75..30126d35d 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index c23b49e6c..1ff7acc25 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index c6cce4c88..d8dd4344b 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,7 +7,7 @@ import ( "sync" "time" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index b6b44ed03..caa999757 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,7 +7,7 @@ import ( tu "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 4588702ce..e2c610f16 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" ) // Returned if a routing table query returns no results. This is NOT expected diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 574085bc7..dc92325e3 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -9,9 +9,9 @@ import ( routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index b809de1dd..3baf01a28 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -9,7 +9,7 @@ import ( dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 8cdc8e9e1..09d96eeef 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 25f95d72c..a3a027a4b 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,7 +10,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index f414dfdae..f47e0ada9 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -7,7 +7,7 @@ import ( repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" p2phost "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/host" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 4a2bb7fe8..aae149218 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -9,9 +9,9 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" - "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/record/record.go b/routing/record/record.go index ca3084017..7cecc8d70 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/record/validation.go b/routing/record/validation.go index c0d9ce198..e9a35cf54 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" + ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/routing/routing.go b/routing/routing.go index 34200a1ae..4ccaa5582 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -5,8 +5,8 @@ import ( "errors" key "github.com/ipfs/go-ipfs/blocks/key" - ci "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/crypto" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 7590fd47a..2d1d3c410 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -13,7 +13,7 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/host" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 66f24dc86..4b8cdc27d 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -6,7 +6,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index dc0df97dd..9a4f4fda5 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -11,7 +11,7 @@ import ( kbucket "github.com/ipfs/go-ipfs/routing/kbucket" host "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/host" inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index ca7d387a6..e3d1a9284 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -12,7 +12,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - peer "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/peer" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" ) // Server handles routing queries using a database backend From bc2dd479305dc7d62934d9660b73425c003689bf Mon Sep 17 00:00:00 2001 From: Michael Pfister Date: Mon, 18 Apr 2016 13:32:03 -0700 Subject: [PATCH 1241/3817] ipfs name resolve --local fixed multihash error resolveOnce should remove '/ipns/' prefix before using multihash functions. Fixes #2527 License: MIT Signed-off-by: Mike Pfister This commit was moved from ipfs/go-namesys@aac00c6dc9819e89f4e27be26337835eb7ea6534 --- namesys/routing.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/namesys/routing.go b/namesys/routing.go index 1520bfb68..6db13dfb0 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -2,6 +2,7 @@ package namesys import ( "fmt" + "strings" "time" lru "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" @@ -122,6 +123,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa return cached, nil } + name = strings.TrimPrefix(name, "/ipns/") hash, err := mh.FromB58String(name) if err != nil { // name should be a multihash. if it isn't, error out here. From de77a29dfcd4a97b52d97f5dcbcd4d4d64b91f8c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 20 Nov 2015 15:24:14 -0800 Subject: [PATCH 1242/3817] wire contexts into bitswap requests more deeply License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@d493d2d99105ef070fa474239cfde23ecf8686d1 --- blockservice/blockservice.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 21af30dfb..0b0397b5b 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -122,6 +122,10 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []key.Key) <-chan *bloc } } + if len(misses) == 0 { + return + } + rblocks, err := s.Exchange.GetBlocks(ctx, misses) if err != nil { log.Debugf("Error with GetBlocks: %s", err) From 991f9577a8cffc62cd500abefaec7403588c02e2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 27 Apr 2016 10:39:48 -0700 Subject: [PATCH 1243/3817] update libp2p with utp dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@cee268be625c07fec64f02250fbfafca55f6417c --- routing/dht/dht.go | 4 ++-- routing/dht/dht_net.go | 2 +- routing/dht/dht_test.go | 2 +- routing/dht/ext_test.go | 4 ++-- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/routing.go | 2 +- routing/mock/dht.go | 2 +- routing/none/none_client.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 4 ++-- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index e0010bf5f..d90e96c87 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -15,8 +15,8 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - host "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/host" - protocol "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/protocol" + host "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/host" + protocol "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/protocol" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index a0d898972..40ceb29aa 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,7 +6,7 @@ import ( ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" + inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index c3ff566bf..586b9e56f 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -17,7 +17,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" - netutil "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/test/util" + netutil "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/test/util" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index ac9b01749..359ee32ce 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -16,8 +16,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net/mock" + inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net/mock" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" ) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index baf5f81e8..e14d52044 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" - inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" + inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 64f671a36..54af0db4a 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,7 +4,7 @@ import ( ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" - inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" + inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 5fdbae82b..b2c0f9fd9 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,7 +12,7 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" + inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index c276f97c9..a137b3393 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,7 +5,7 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index f47e0ada9..828ac3a64 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,7 +6,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" - p2phost "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/host" + p2phost "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/host" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 2d1d3c410..845643d36 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -12,7 +12,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/host" + "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/host" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 4b8cdc27d..25ae21109 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,7 +5,7 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" + inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 9a4f4fda5..4d98b6193 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -9,8 +9,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" - host "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/host" - inet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net" + host "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/host" + inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) From e3c40948d4aef9fc1f0d4b80782bb350fd611cd7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 27 Apr 2016 10:39:48 -0700 Subject: [PATCH 1244/3817] update libp2p with utp dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@8cb65a84ebc3587eb8cf225ff84c3094df902417 --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 6fa925266..40365ec20 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,7 +13,7 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmYgaiNVVL7f2nydijAwpDRunRkmxfu3PoK87Y3pH84uAW/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net/mock" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" ) From d441d92dde6a521dcd14c30ee8ab0a3bb2ab37ec Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Fri, 29 Apr 2016 16:57:19 -0400 Subject: [PATCH 1245/3817] Capitalized `NOTE`, first letter of following word License: MIT Signed-off-by: Richard Littauer This commit was moved from ipfs/go-ipfs-routing@73ff11cc3068a343e8bce452f1c5bb5ec5df3361 --- routing/dht/dht.go | 2 +- routing/dht/handlers.go | 2 +- routing/dht/routing.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index d90e96c87..d8552d977 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -154,7 +154,7 @@ var errInvalidRecord = errors.New("received invalid record") // getValueOrPeers queries a particular peer p for the value for // key. It returns either the value or a list of closer peers. -// NOTE: it will update the dht's peerstore with any new addresses +// NOTE: It will update the dht's peerstore with any new addresses // it finds for the given peer. func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.ID, key key.Key) (*pb.Record, []peer.PeerInfo, error) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index c439834cc..a295b927b 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -127,7 +127,7 @@ func (dht *IpfsDHT) checkLocalDatastore(k key.Key) (*pb.Record, error) { recordIsBad = true } - // NOTE: we do not verify the record here beyond checking these timestamps. + // NOTE: We do not verify the record here beyond checking these timestamps. // we put the burden of checking the records on the requester as checking a record // may be computationally expensive diff --git a/routing/dht/routing.go b/routing/dht/routing.go index b2c0f9fd9..3663dc49e 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -283,7 +283,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key key.Key, ps := pset.NewLimited(count) provs := dht.providers.GetProviders(ctx, key) for _, p := range provs { - // NOTE: assuming that this list of peers is unique + // NOTE: Assuming that this list of peers is unique if ps.TryAdd(p) { select { case peerOut <- dht.peerstore.PeerInfo(p): From d5bdc21fb5e6e208349b6b6234c7398115ebac8c Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Fri, 29 Apr 2016 16:57:19 -0400 Subject: [PATCH 1246/3817] Capitalized `NOTE`, first letter of following word License: MIT Signed-off-by: Richard Littauer This commit was moved from ipfs/go-merkledag@27ad379689aac3543549138fc9f6107ecda24f58 --- ipld/merkledag/node.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index d44285159..36479fb75 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -165,7 +165,7 @@ func (n *Node) GetLinkedNode(ctx context.Context, ds DAGService, name string) (* } // Copy returns a copy of the node. -// NOTE: does not make copies of Node objects in the links. +// NOTE: Does not make copies of Node objects in the links. func (n *Node) Copy() *Node { nnode := new(Node) if len(n.Data) > 0 { @@ -234,7 +234,7 @@ func (n *Node) Stat() (*NodeStat, error) { // Multihash hashes the encoded data of this node. func (n *Node) Multihash() (mh.Multihash, error) { - // Note: EncodeProtobuf generates the hash and puts it in n.cached. + // NOTE: EncodeProtobuf generates the hash and puts it in n.cached. _, err := n.EncodeProtobuf(false) if err != nil { return nil, err From 6308abbf2454db1703ae209cc1d93da3665fc7f4 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Fri, 29 Apr 2016 16:57:19 -0400 Subject: [PATCH 1247/3817] Capitalized `NOTE`, first letter of following word License: MIT Signed-off-by: Richard Littauer This commit was moved from ipfs/go-ipfs-pinner@0b8bb0494df428f52ea02d670a97b6d7f9e171aa --- pinning/pinner/pin_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 09371fc6e..c8859660a 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -213,7 +213,7 @@ func TestPinRecursiveFail(t *testing.T) { t.Fatal(err) } - // Note: this isnt a time based test, we expect the pin to fail + // NOTE: This isnt a time based test, we expect the pin to fail mctx, _ := context.WithTimeout(ctx, time.Millisecond) err = p.Pin(mctx, a, true) if err == nil { From 4d4e54d12ef23afaac1acaec90c5ed9f18e492fc Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 4 May 2016 22:56:39 +0200 Subject: [PATCH 1248/3817] Update go-log to 1.1.0 and fix calls to go-log.Uuid License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-routing@d01180a6bd8ee82a3f9baa3703dabe89649b12bd --- routing/dht/dht.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/query.go | 2 +- routing/kbucket/table.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/none/none_client.go | 2 +- routing/offline/offline.go | 2 +- routing/record/record.go | 2 +- routing/supernode/client.go | 8 ++++---- routing/supernode/proxy/standard.go | 10 +++++----- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index d8552d977..e5959e70a 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -18,7 +18,7 @@ import ( host "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/host" protocol "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/protocol" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 54af0db4a..7dc7e8f28 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -6,7 +6,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/query.go b/routing/dht/query.go index 4a733ca9b..a89ce2b80 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -11,7 +11,7 @@ import ( u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" queue "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer/queue" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" ctxproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index d8dd4344b..1d41da7b1 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -8,7 +8,7 @@ import ( "time" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("table") diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index dc92325e3..93f4d5acc 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -13,7 +13,7 @@ import ( u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 828ac3a64..b8d2877b3 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -9,7 +9,7 @@ import ( p2phost "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/host" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index aae149218..eca941655 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -13,7 +13,7 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("offlinerouting") diff --git a/routing/record/record.go b/routing/record/record.go index 7cecc8d70..41071e6c0 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("routing/record") diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 845643d36..6330cc2eb 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,13 +8,14 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/host" + peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/host" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" ) var log = logging.Logger("supernode") @@ -37,7 +38,6 @@ func NewClient(px proxy.Proxy, h host.Host, ps peer.Peerstore, local peer.ID) (* } func (c *Client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan peer.PeerInfo { - ctx = logging.ContextWithLoggable(ctx, logging.Uuid("findProviders")) defer log.EventBegin(ctx, "findProviders", &k).Done() ch := make(chan peer.PeerInfo) go func() { diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 4d98b6193..1e5906ada 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -6,13 +6,14 @@ import ( ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - key "github.com/ipfs/go-ipfs/blocks/key" - dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - kbucket "github.com/ipfs/go-ipfs/routing/kbucket" host "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/host" inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + + key "github.com/ipfs/go-ipfs/blocks/key" + dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" + kbucket "github.com/ipfs/go-ipfs/routing/kbucket" ) const ProtocolSNR = "/ipfs/supernoderouting" @@ -158,7 +159,6 @@ func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote pe return nil, err } e.Append(logging.Pair("response", response)) - e.Append(logging.Pair("uuid", logging.Uuid("foo"))) return response, nil } From c885d7f22bce1ac1af5426dc0ab72814748e72e7 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 4 May 2016 22:56:39 +0200 Subject: [PATCH 1249/3817] Update go-log to 1.1.0 and fix calls to go-log.Uuid License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@0a178a492bd72a1f4a359496cf5fee745b5b8e14 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 6a6ad0ecd..b67723d58 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -9,7 +9,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("merkledag") From 60e252f294440cb9d252039c133a0345cf6420c4 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 4 May 2016 22:56:39 +0200 Subject: [PATCH 1250/3817] Update go-log to 1.1.0 and fix calls to go-log.Uuid License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-blockstore@0b8ac0fc671155184c56e92018018526d1eadc16 --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 42c83b64b..f8c086cc2 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -14,7 +14,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("blockstore") From 8021a5d059236151446ab581c321b50fef38c5e8 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 4 May 2016 22:56:39 +0200 Subject: [PATCH 1251/3817] Update go-log to 1.1.0 and fix calls to go-log.Uuid License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-blockservice@364267d13702e925c0a2253b5137237aafc6eb6b --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 0b0397b5b..802f493d1 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -11,7 +11,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("blockservice") From b994de89344cbc159a175bde9fd466ef24eef9e1 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 4 May 2016 22:56:39 +0200 Subject: [PATCH 1252/3817] Update go-log to 1.1.0 and fix calls to go-log.Uuid License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-chunker@64cb0cf21198515d1fdefb1b3127e75c1c925966 --- chunker/splitting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 3b539fe7b..6b82a8c87 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -4,7 +4,7 @@ package chunk import ( "io" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("chunk") From 40dfdca9a75f54e6e6274ee6f3b3f36f1ffbfc42 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 4 May 2016 22:56:39 +0200 Subject: [PATCH 1253/3817] Update go-log to 1.1.0 and fix calls to go-log.Uuid License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-namesys@2c686c4d6b8a97001a3e3b1657c26fd64739ee87 --- namesys/republisher/repub.go | 2 +- namesys/routing.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index c84c30d2e..52004fbfb 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -18,7 +18,7 @@ import ( gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/routing.go b/namesys/routing.go index bbfd0ae15..174e3506b 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -16,7 +16,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("namesys") From 002e65175ddca7baaeb362bba9fc2f4fe6cba55c Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 4 May 2016 22:56:39 +0200 Subject: [PATCH 1254/3817] Update go-log to 1.1.0 and fix calls to go-log.Uuid License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-pinner@b21fb9fe219fdba4d383f023f02a582dcdc0633a --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 0aad6c03f..425fe1383 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -9,7 +9,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index a7f62417f..64c1bf264 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -12,7 +12,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("pin") From 082d62ace6f1c5fcb198bef273a02f698954e5c4 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 4 May 2016 22:56:39 +0200 Subject: [PATCH 1255/3817] Update go-log to 1.1.0 and fix calls to go-log.Uuid License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-mfs@8f33a2deb32ddc1f336c4c242303c3e9762035e8 --- mfs/system.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/system.go b/mfs/system.go index 2ccc6650c..19f90a40d 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -19,7 +19,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var ErrNotExist = errors.New("no such rootfs") From a9c3d34493c845d327fd08d79c070099814434bb Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 4 May 2016 22:56:39 +0200 Subject: [PATCH 1256/3817] Update go-log to 1.1.0 and fix calls to go-log.Uuid License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@4d071e23b88b547dc02b4e0c3beac15cb15ee475 --- unixfs/mod/dagmodifier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 0f5866716..ba266f396 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -17,7 +17,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var ErrSeekFail = errors.New("failed to seek properly") From 9ae5edea482a830f462fddcb1d904fe2be6e4953 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 4 May 2016 22:56:39 +0200 Subject: [PATCH 1257/3817] Update go-log to 1.1.0 and fix calls to go-log.Uuid License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-path@171db441fd1cbcb3b2625027ebee85b290b6de0c --- path/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/resolver.go b/path/resolver.go index 3eaf345ff..fb95b4f52 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -11,7 +11,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" merkledag "github.com/ipfs/go-ipfs/merkledag" - logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("path") From f6ce5ea30753430892772bd8a48287fdd9cf60be Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 5 May 2016 00:54:20 +0200 Subject: [PATCH 1258/3817] Restore go-log.Uuid() calls as loggables.Uuid() calls License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-routing@2c964c34e000172c58d342e25811272c7314d6fe --- routing/supernode/client.go | 2 ++ routing/supernode/proxy/standard.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 6330cc2eb..acb471058 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -16,6 +16,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" + loggables "github.com/ipfs/go-ipfs/thirdparty/loggables" ) var log = logging.Logger("supernode") @@ -38,6 +39,7 @@ func NewClient(px proxy.Proxy, h host.Host, ps peer.Peerstore, local peer.ID) (* } func (c *Client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan peer.PeerInfo { + logging.ContextWithLoggable(ctx, loggables.Uuid("findProviders")) defer log.EventBegin(ctx, "findProviders", &k).Done() ch := make(chan peer.PeerInfo) go func() { diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 1e5906ada..2c0da5adc 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -14,6 +14,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" + loggables "github.com/ipfs/go-ipfs/thirdparty/loggables" ) const ProtocolSNR = "/ipfs/supernoderouting" @@ -159,6 +160,7 @@ func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote pe return nil, err } e.Append(logging.Pair("response", response)) + e.Append(logging.Pair("uuid", loggables.Uuid("foo"))) return response, nil } From 93423d21271dc6db2e799289bfaa288ef78151b2 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 5 May 2016 18:00:43 -0400 Subject: [PATCH 1259/3817] Make blocks.Block an interface. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@c5917bc1bfe740dd2f816bede9a7a569c10dee33 --- ipld/merkledag/merkledag.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index b67723d58..938b71308 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -52,13 +52,13 @@ func (n *dagService) Add(nd *Node) (key.Key, error) { return "", err } - b := new(blocks.Block) - b.Data = d - b.Multihash, err = nd.Multihash() + mh, err := nd.Multihash() if err != nil { return "", err } + b, _ := blocks.NewBlockWithHash(d, mh) + return n.Blocks.AddBlock(b) } @@ -82,7 +82,7 @@ func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) { return nil, fmt.Errorf("Failed to get block for %s: %v", k.B58String(), err) } - res, err := DecodeProtobuf(b.Data) + res, err := DecodeProtobuf(b.Data()) if err != nil { return nil, fmt.Errorf("Failed to decode Protocol Buffers: %v", err) } @@ -135,7 +135,7 @@ func (ds *dagService) GetMany(ctx context.Context, keys []key.Key) <-chan *NodeO } return } - nd, err := DecodeProtobuf(b.Data) + nd, err := DecodeProtobuf(b.Data()) if err != nil { out <- &NodeOption{Err: err} return @@ -316,7 +316,7 @@ func (np *nodePromise) Get(ctx context.Context) (*Node, error) { type Batch struct { ds *dagService - blocks []*blocks.Block + blocks []blocks.Block size int MaxSize int } @@ -327,17 +327,17 @@ func (t *Batch) Add(nd *Node) (key.Key, error) { return "", err } - b := new(blocks.Block) - b.Data = d - b.Multihash, err = nd.Multihash() + mh, err := nd.Multihash() if err != nil { return "", err } - k := key.Key(b.Multihash) + b, _ := blocks.NewBlockWithHash(d, mh) + + k := key.Key(mh) t.blocks = append(t.blocks, b) - t.size += len(b.Data) + t.size += len(b.Data()) if t.size > t.MaxSize { return k, t.Commit() } From c98fef86577b2e619edc3b5cb4bbc1086f26ff89 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 5 May 2016 18:00:43 -0400 Subject: [PATCH 1260/3817] Make blocks.Block an interface. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-blockstore@cc91da6feace4e695fa944f16bb9386abc19ecaf --- blockstore/blockstore.go | 16 ++++++++-------- blockstore/blockstore_test.go | 2 +- blockstore/write_cache.go | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index f8c086cc2..671ae2c25 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -30,9 +30,9 @@ var ErrNotFound = errors.New("blockstore: block not found") type Blockstore interface { DeleteBlock(key.Key) error Has(key.Key) (bool, error) - Get(key.Key) (*blocks.Block, error) - Put(*blocks.Block) error - PutMany([]*blocks.Block) error + Get(key.Key) (blocks.Block, error) + Put(blocks.Block) error + PutMany([]blocks.Block) error AllKeysChan(ctx context.Context) (<-chan key.Key, error) } @@ -73,7 +73,7 @@ type blockstore struct { gcreqlk sync.Mutex } -func (bs *blockstore) Get(k key.Key) (*blocks.Block, error) { +func (bs *blockstore) Get(k key.Key) (blocks.Block, error) { maybeData, err := bs.datastore.Get(k.DsKey()) if err == ds.ErrNotFound { return nil, ErrNotFound @@ -89,7 +89,7 @@ func (bs *blockstore) Get(k key.Key) (*blocks.Block, error) { return blocks.NewBlockWithHash(bdata, mh.Multihash(k)) } -func (bs *blockstore) Put(block *blocks.Block) error { +func (bs *blockstore) Put(block blocks.Block) error { k := block.Key().DsKey() // Has is cheaper than Put, so see if we already have it @@ -97,10 +97,10 @@ func (bs *blockstore) Put(block *blocks.Block) error { if err == nil && exists { return nil // already stored. } - return bs.datastore.Put(k, block.Data) + return bs.datastore.Put(k, block.Data()) } -func (bs *blockstore) PutMany(blocks []*blocks.Block) error { +func (bs *blockstore) PutMany(blocks []blocks.Block) error { t, err := bs.datastore.Batch() if err != nil { return err @@ -112,7 +112,7 @@ func (bs *blockstore) PutMany(blocks []*blocks.Block) error { continue } - err = t.Put(k, b.Data) + err = t.Put(k, b.Data()) if err != nil { return err } diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 4987f9670..446d4b776 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -40,7 +40,7 @@ func TestPutThenGetBlock(t *testing.T) { if err != nil { t.Fatal(err) } - if !bytes.Equal(block.Data, blockFromBlockstore.Data) { + if !bytes.Equal(block.Data(), blockFromBlockstore.Data()) { t.Fail() } } diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index 9084b1a67..f7c2caf45 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -34,11 +34,11 @@ func (w *writecache) Has(k key.Key) (bool, error) { return w.blockstore.Has(k) } -func (w *writecache) Get(k key.Key) (*blocks.Block, error) { +func (w *writecache) Get(k key.Key) (blocks.Block, error) { return w.blockstore.Get(k) } -func (w *writecache) Put(b *blocks.Block) error { +func (w *writecache) Put(b blocks.Block) error { k := b.Key() if _, ok := w.cache.Get(k); ok { return nil @@ -49,8 +49,8 @@ func (w *writecache) Put(b *blocks.Block) error { return w.blockstore.Put(b) } -func (w *writecache) PutMany(bs []*blocks.Block) error { - var good []*blocks.Block +func (w *writecache) PutMany(bs []blocks.Block) error { + var good []blocks.Block for _, b := range bs { if _, ok := w.cache.Get(b.Key()); !ok { good = append(good, b) From a375343ba729d31edec655f181b853be7ce3fdf0 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 5 May 2016 18:00:43 -0400 Subject: [PATCH 1261/3817] Make blocks.Block an interface. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-blockservice@a4fea7bb531bd4dc5a3ecf10ea56b2fdb1bda694 --- blockservice/blockservice.go | 10 +++++----- blockservice/test/blocks_test.go | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 802f493d1..78838757a 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -41,7 +41,7 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) *BlockService { // AddBlock adds a particular block to the service, Putting it into the datastore. // TODO pass a context into this if the remote.HasBlock is going to remain here. -func (s *BlockService) AddBlock(b *blocks.Block) (key.Key, error) { +func (s *BlockService) AddBlock(b blocks.Block) (key.Key, error) { k := b.Key() err := s.Blockstore.Put(b) if err != nil { @@ -53,7 +53,7 @@ func (s *BlockService) AddBlock(b *blocks.Block) (key.Key, error) { return k, nil } -func (s *BlockService) AddBlocks(bs []*blocks.Block) ([]key.Key, error) { +func (s *BlockService) AddBlocks(bs []blocks.Block) ([]key.Key, error) { err := s.Blockstore.PutMany(bs) if err != nil { return nil, err @@ -71,7 +71,7 @@ func (s *BlockService) AddBlocks(bs []*blocks.Block) ([]key.Key, error) { // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). -func (s *BlockService) GetBlock(ctx context.Context, k key.Key) (*blocks.Block, error) { +func (s *BlockService) GetBlock(ctx context.Context, k key.Key) (blocks.Block, error) { log.Debugf("BlockService GetBlock: '%s'", k) block, err := s.Blockstore.Get(k) if err == nil { @@ -103,8 +103,8 @@ func (s *BlockService) GetBlock(ctx context.Context, k key.Key) (*blocks.Block, // GetBlocks gets a list of blocks asynchronously and returns through // the returned channel. // NB: No guarantees are made about order. -func (s *BlockService) GetBlocks(ctx context.Context, ks []key.Key) <-chan *blocks.Block { - out := make(chan *blocks.Block, 0) +func (s *BlockService) GetBlocks(ctx context.Context, ks []key.Key) <-chan blocks.Block { + out := make(chan blocks.Block, 0) go func() { defer close(out) var misses []key.Key diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index ab6a476aa..584505b21 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -24,7 +24,7 @@ func TestBlocks(t *testing.T) { b := blocks.NewBlock([]byte("beep boop")) h := u.Hash([]byte("beep boop")) - if !bytes.Equal(b.Multihash, h) { + if !bytes.Equal(b.Multihash(), h) { t.Error("Block Multihash and data multihash not equal") } @@ -54,7 +54,7 @@ func TestBlocks(t *testing.T) { t.Error("Block keys not equal.") } - if !bytes.Equal(b.Data, b2.Data) { + if !bytes.Equal(b.Data(), b2.Data()) { t.Error("Block data is not equal.") } } @@ -79,7 +79,7 @@ func TestGetBlocksSequential(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*50) defer cancel() out := servs[i].GetBlocks(ctx, keys) - gotten := make(map[key.Key]*blocks.Block) + gotten := make(map[key.Key]blocks.Block) for blk := range out { if _, ok := gotten[blk.Key()]; ok { t.Fatal("Got duplicate block!") From c2176c681912628bd3e2827cf1d85c1e5e2537a0 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 5 May 2016 18:00:43 -0400 Subject: [PATCH 1262/3817] Make blocks.Block an interface. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-chunker@f5c45f3a3e8e1186039cbc9f9d73cacaccc1bbef --- chunker/rabin_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 7702d3e76..9b9cfce8f 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -39,10 +39,10 @@ func TestRabinChunking(t *testing.T) { } } -func chunkData(t *testing.T, data []byte) map[key.Key]*blocks.Block { +func chunkData(t *testing.T, data []byte) map[key.Key]blocks.Block { r := NewRabin(bytes.NewReader(data), 1024*256) - blkmap := make(map[key.Key]*blocks.Block) + blkmap := make(map[key.Key]blocks.Block) for { blk, err := r.NextBytes() From 3d7122a77c2e9a9946bc77d6f4e8fe59ac247ee2 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 5 May 2016 18:00:43 -0400 Subject: [PATCH 1263/3817] Make blocks.Block an interface. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-exchange-offline@9e9cf60bd2b08383a11a51ed29a8b2da905efcdf --- exchange/offline/offline.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 8f857d933..d2ee4fbaa 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -23,12 +23,12 @@ type offlineExchange struct { // GetBlock returns nil to signal that a block could not be retrieved for the // given key. // NB: This function may return before the timeout expires. -func (e *offlineExchange) GetBlock(_ context.Context, k key.Key) (*blocks.Block, error) { +func (e *offlineExchange) GetBlock(_ context.Context, k key.Key) (blocks.Block, error) { return e.bs.Get(k) } // HasBlock always returns nil. -func (e *offlineExchange) HasBlock(b *blocks.Block) error { +func (e *offlineExchange) HasBlock(b blocks.Block) error { return e.bs.Put(b) } @@ -39,8 +39,8 @@ func (_ *offlineExchange) Close() error { return nil } -func (e *offlineExchange) GetBlocks(ctx context.Context, ks []key.Key) (<-chan *blocks.Block, error) { - out := make(chan *blocks.Block, 0) +func (e *offlineExchange) GetBlocks(ctx context.Context, ks []key.Key) (<-chan blocks.Block, error) { + out := make(chan blocks.Block, 0) go func() { defer close(out) var misses []key.Key From 4f1aeff4a0c23a5cf088cb5368f3d21e4b74b609 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 5 May 2016 18:00:43 -0400 Subject: [PATCH 1264/3817] Make blocks.Block an interface. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-exchange-interface@631c4132a2d6119fdcf41f8158a9cb328afa668a --- exchange/interface.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index dbc66e3b6..6db476d9e 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -13,13 +13,13 @@ import ( // exchange protocol. type Interface interface { // type Exchanger interface // GetBlock returns the block associated with a given key. - GetBlock(context.Context, key.Key) (*blocks.Block, error) + GetBlock(context.Context, key.Key) (blocks.Block, error) - GetBlocks(context.Context, []key.Key) (<-chan *blocks.Block, error) + GetBlocks(context.Context, []key.Key) (<-chan blocks.Block, error) // TODO Should callers be concerned with whether the block was made // available on the network? - HasBlock(*blocks.Block) error + HasBlock(blocks.Block) error io.Closer } From f22917bda9ae382574dc9c2c6c4aab6a2c6a6617 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 10 May 2016 16:06:28 -0700 Subject: [PATCH 1265/3817] update libp2p with go-multiaddr and go-stream-muxer updates License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@803748f00a8f0f4712dfce88c626204aea043f14 --- routing/dht/dht.go | 6 +++--- routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 4 ++-- routing/dht/dht_test.go | 6 +++--- routing/dht/diag.go | 2 +- routing/dht/ext_test.go | 6 +++--- routing/dht/handlers.go | 2 +- routing/dht/lookup.go | 2 +- routing/dht/notif.go | 4 ++-- routing/dht/pb/message.go | 6 +++--- routing/dht/providers.go | 2 +- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 4 ++-- routing/dht/records.go | 2 +- routing/dht/routing.go | 4 ++-- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 2 +- routing/kbucket/table_test.go | 2 +- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 4 ++-- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 2 +- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 2 +- routing/routing.go | 2 +- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 2 +- 32 files changed, 50 insertions(+), 50 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index e5959e70a..77a6c8cab 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -15,10 +15,10 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - host "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/host" - protocol "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/protocol" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + host "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/host" + protocol "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/protocol" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index bfe5cc091..5fddf954e 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -10,7 +10,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" periodicproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/periodic" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 40ceb29aa..8dec0fe83 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,10 +6,10 @@ import ( ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + inet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net" ) // handleNewStream implements the inet.StreamHandler diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 586b9e56f..1234be143 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -11,15 +11,15 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" - netutil "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/test/util" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + netutil "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/test/util" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" diff --git a/routing/dht/diag.go b/routing/dht/diag.go index e87464b71..cf30393f1 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" ) type connDiagInfo struct { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 359ee32ce..0c43b3b2b 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -16,10 +16,10 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net/mock" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + inet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net/mock" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index a295b927b..15fd25679 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -11,7 +11,7 @@ import ( lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 975967ff2..ad523d6d9 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -5,7 +5,7 @@ import ( notif "github.com/ipfs/go-ipfs/notifications" kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index e14d52044..a64eb4204 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -1,9 +1,9 @@ package dht import ( - ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" + ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" + inet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 7dc7e8f28..75a138ed2 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -1,12 +1,12 @@ package dht_pb import ( - ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" + ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" - inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + inet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 2656e0284..df16a5c15 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -6,7 +6,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index e88b982f1..9d4861943 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -4,7 +4,7 @@ import ( "testing" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index a89ce2b80..872e9f947 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -9,8 +9,8 @@ import ( pset "github.com/ipfs/go-ipfs/thirdparty/peerset" todoctr "github.com/ipfs/go-ipfs/thirdparty/todocounter" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" - queue "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer/queue" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + queue "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer/queue" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/routing/dht/records.go b/routing/dht/records.go index 2d74be310..fdfb12d2d 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -9,7 +9,7 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 3663dc49e..9182c0873 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,9 +12,9 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + inet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net" ) // asyncQueryBuffer is the size of buffered channels in async queries. This diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 30126d35d..37b7cb8c8 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index 1ff7acc25..6d282e0f0 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 1d41da7b1..7c22fec7d 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,7 +7,7 @@ import ( "sync" "time" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index caa999757..4ac3a2af0 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,7 +7,7 @@ import ( tu "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index e2c610f16..8653c7e6a 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" ) // Returned if a routing table query returns no results. This is NOT expected diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 93f4d5acc..5230fba83 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -9,12 +9,12 @@ import ( routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" "github.com/ipfs/go-ipfs/thirdparty/testutil" + ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" - ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 3baf01a28..13ae8e262 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -9,7 +9,7 @@ import ( dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 09d96eeef..1d8862306 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index a137b3393..3f2d01272 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,8 +5,8 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + mocknet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index a3a027a4b..25069071b 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,7 +10,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index b8d2877b3..d40eb30fb 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,10 +6,10 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" - p2phost "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/host" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + p2phost "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/host" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index eca941655..4942b27c6 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -11,7 +11,7 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) diff --git a/routing/routing.go b/routing/routing.go index 4ccaa5582..3b1ab799c 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -6,7 +6,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index acb471058..034c223d9 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,9 +8,9 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/host" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/host" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 25ae21109..3fc8690c8 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + inet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 2c0da5adc..c97248df6 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -6,10 +6,10 @@ import ( ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - host "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/host" - inet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + host "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/host" + inet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index e3d1a9284..815e53e1b 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -12,7 +12,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" ) // Server handles routing queries using a database backend From cae6bf0b1a90c6577382d14ec88c86d6e7c1270b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 10 May 2016 16:06:28 -0700 Subject: [PATCH 1266/3817] update libp2p with go-multiaddr and go-stream-muxer updates License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@b5f18e1fb46745ffbac21bab3f6a2317ee042c27 --- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 970472546..17bc7d736 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -21,7 +21,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 52004fbfb..dc0645898 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,7 +11,7 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 40365ec20..81f0495a2 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmXDvxcXUYn2DDnGKJwdQPxkJgG83jBTp5UmmNzeHzqbj5/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + mocknet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 995e80ee2..4d638735f 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -12,7 +12,7 @@ import ( mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer" + peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From 76bb21e8d296f7f00c497b2d0eb7a394b69689d3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 13 May 2016 13:42:46 -0700 Subject: [PATCH 1267/3817] update deps to introduce yamux hang fix License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@139f9f0b453e2eb02883f1832bd68ebf2aba6be1 --- routing/dht/dht.go | 4 ++-- routing/dht/dht_net.go | 2 +- routing/dht/dht_test.go | 2 +- routing/dht/ext_test.go | 4 ++-- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/routing.go | 2 +- routing/mock/dht.go | 2 +- routing/none/none_client.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 4 ++-- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 77a6c8cab..149000603 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -16,9 +16,9 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + host "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/host" + protocol "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/protocol" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" - host "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/host" - protocol "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/protocol" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 8dec0fe83..415ab9d42 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -8,8 +8,8 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + inet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - inet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net" ) // handleNewStream implements the inet.StreamHandler diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 1234be143..5d53e1833 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -19,7 +19,7 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - netutil "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/test/util" + netutil "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/test/util" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 0c43b3b2b..dfdf99929 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -18,8 +18,8 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - inet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net/mock" + inet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net/mock" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/notif.go b/routing/dht/notif.go index a64eb4204..ed312b928 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - inet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net" + inet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 75a138ed2..ec69c3512 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -5,8 +5,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + inet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" - inet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 9182c0873..63163d688 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -13,8 +13,8 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + inet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - inet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net" ) // asyncQueryBuffer is the size of buffered channels in async queries. This diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 3f2d01272..a5a0bef25 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,8 +5,8 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" + mocknet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - mocknet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/none/none_client.go b/routing/none/none_client.go index d40eb30fb..887c593f4 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -7,9 +7,9 @@ import ( repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + p2phost "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/host" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" - p2phost "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/host" ) var log = logging.Logger("mockrouter") diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 034c223d9..def431954 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -9,8 +9,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/host" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" - "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/host" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 3fc8690c8..3a2b08157 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -6,7 +6,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - inet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net" + inet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index c97248df6..04706a55d 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -7,9 +7,9 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + host "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/host" + inet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" - host "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/host" - inet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" From fadb803fede1823eb13c0a65be4a453b27acf172 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 13 May 2016 13:42:46 -0700 Subject: [PATCH 1268/3817] update deps to introduce yamux hang fix License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@31cf8180275458066ba4d3c298e115130c71ac5e --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 81f0495a2..872e92d3a 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - mocknet "gx/ipfs/QmcQTVCQWCN2MYgBHpFXE5S56rcg2mRsxaRgMYmA1UWgA8/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { From b2d05625c11ff1ea305287e1cfffea15da21183b Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 15 May 2016 18:15:28 +0200 Subject: [PATCH 1269/3817] pin: add missing consts and convertion functions License: MIT Signed-off-by: Christian Couder This commit was moved from ipfs/go-ipfs-pinner@a54f592f0908a246a01d7c38aed3049d7ae8fa42 --- pinning/pinner/pin.go | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 64c1bf264..df7440fac 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -22,8 +22,13 @@ var pinDatastoreKey = ds.NewKey("/local/pins") var emptyKey = key.B58KeyDecode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n") const ( - linkDirect = "direct" linkRecursive = "recursive" + linkDirect = "direct" + linkIndirect = "indirect" + linkInternal = "internal" + linkNotPinned = "not pinned" + linkAny = "any" + linkAll = "all" ) type PinMode int @@ -31,9 +36,39 @@ type PinMode int const ( Recursive PinMode = iota Direct + Indirect + Internal NotPinned + Any ) +func PinModeToString(mode PinMode) (string, bool) { + m := map[PinMode]string{ + Recursive: linkRecursive, + Direct: linkDirect, + Indirect: linkIndirect, + Internal: linkInternal, + NotPinned: linkNotPinned, + Any: linkAny, + } + s, ok := m[mode] + return s, ok +} + +func StringToPinMode(s string) (PinMode, bool) { + m := map[string]PinMode{ + linkRecursive: Recursive, + linkDirect: Direct, + linkIndirect: Indirect, + linkInternal: Internal, + linkNotPinned: NotPinned, + linkAny: Any, + linkAll: Any, // "all" and "any" means the same thing + } + mode, ok := m[s] + return mode, ok +} + type Pinner interface { IsPinned(key.Key) (string, bool, error) IsPinnedWithType(key.Key, string) (string, bool, error) From 1d461b7e9818e76b9293c0c933651ce8ffbc494e Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 15 May 2016 17:13:55 +0200 Subject: [PATCH 1270/3817] pin: use new constants instead of literal values License: MIT Signed-off-by: Christian Couder This commit was moved from ipfs/go-ipfs-pinner@262c7287ad9438cbd2822b8cc35bc0471d2ed1d1 --- pinning/pinner/pin.go | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index df7440fac..0696e7984 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -71,7 +71,7 @@ func StringToPinMode(s string) (PinMode, bool) { type Pinner interface { IsPinned(key.Key) (string, bool, error) - IsPinnedWithType(key.Key, string) (string, bool, error) + IsPinnedWithType(key.Key, PinMode) (string, bool, error) Pin(context.Context, *mdag.Node, bool) error Unpin(context.Context, key.Key, bool) error @@ -164,7 +164,7 @@ var ErrNotPinned = fmt.Errorf("not pinned") func (p *pinner) Unpin(ctx context.Context, k key.Key, recursive bool) error { p.lock.Lock() defer p.lock.Unlock() - reason, pinned, err := p.isPinnedWithType(k, "all") + reason, pinned, err := p.isPinnedWithType(k, Any) if err != nil { return err } @@ -197,46 +197,47 @@ func (p *pinner) isInternalPin(key key.Key) bool { func (p *pinner) IsPinned(k key.Key) (string, bool, error) { p.lock.RLock() defer p.lock.RUnlock() - return p.isPinnedWithType(k, "all") + return p.isPinnedWithType(k, Any) } -func (p *pinner) IsPinnedWithType(k key.Key, typeStr string) (string, bool, error) { +func (p *pinner) IsPinnedWithType(k key.Key, mode PinMode) (string, bool, error) { p.lock.RLock() defer p.lock.RUnlock() - return p.isPinnedWithType(k, typeStr) + return p.isPinnedWithType(k, mode) } // isPinnedWithType is the implementation of IsPinnedWithType that does not lock. // intended for use by other pinned methods that already take locks -func (p *pinner) isPinnedWithType(k key.Key, typeStr string) (string, bool, error) { - switch typeStr { - case "all", "direct", "indirect", "recursive", "internal": +func (p *pinner) isPinnedWithType(k key.Key, mode PinMode) (string, bool, error) { + switch mode { + case Any, Direct, Indirect, Recursive, Internal: default: - err := fmt.Errorf("Invalid type '%s', must be one of {direct, indirect, recursive, internal, all}", typeStr) + err := fmt.Errorf("Invalid Pin Mode '%d', must be one of {%d, %d, %d, %d, %d}", + mode, Direct, Indirect, Recursive, Internal, Any) return "", false, err } - if (typeStr == "recursive" || typeStr == "all") && p.recursePin.HasKey(k) { - return "recursive", true, nil + if (mode == Recursive || mode == Any) && p.recursePin.HasKey(k) { + return linkRecursive, true, nil } - if typeStr == "recursive" { + if mode == Recursive { return "", false, nil } - if (typeStr == "direct" || typeStr == "all") && p.directPin.HasKey(k) { - return "direct", true, nil + if (mode == Direct || mode == Any) && p.directPin.HasKey(k) { + return linkDirect, true, nil } - if typeStr == "direct" { + if mode == Direct { return "", false, nil } - if (typeStr == "internal" || typeStr == "all") && p.isInternalPin(k) { - return "internal", true, nil + if (mode == Internal || mode == Any) && p.isInternalPin(k) { + return linkInternal, true, nil } - if typeStr == "internal" { + if mode == Internal { return "", false, nil } - // Default is "indirect" + // Default is Indirect for _, rk := range p.recursePin.GetKeys() { rnd, err := p.dserv.Get(context.Background(), rk) if err != nil { From 27106f3f3f38768d38822577f94493e94e59d9f1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 16 May 2016 11:22:36 -0700 Subject: [PATCH 1271/3817] update libp2p to v3.2.1 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@7553bcb1d057c80b798d6b526b629eab0a84712d --- routing/dht/dht.go | 4 ++-- routing/dht/dht_net.go | 2 +- routing/dht/dht_test.go | 2 +- routing/dht/ext_test.go | 4 ++-- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/routing.go | 2 +- routing/mock/dht.go | 2 +- routing/none/none_client.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 4 ++-- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 149000603..deef86022 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -15,9 +15,9 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + host "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/host" + protocol "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/protocol" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - host "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/host" - protocol "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/protocol" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 415ab9d42..2fe516cec 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,9 +6,9 @@ import ( ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" pb "github.com/ipfs/go-ipfs/routing/dht/pb" + inet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - inet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 5d53e1833..0c66a5502 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -17,9 +17,9 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" + netutil "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/test/util" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - netutil "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/test/util" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index dfdf99929..7c5446172 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -16,10 +16,10 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" + inet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net/mock" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - inet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net/mock" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/notif.go b/routing/dht/notif.go index ed312b928..fb7c2ee14 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - inet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net" + inet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index ec69c3512..919841f04 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,8 +4,8 @@ import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" + inet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - inet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 63163d688..d91478101 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,8 +12,8 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" + inet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - inet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index a5a0bef25..cb4987cfc 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,7 +5,7 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 887c593f4..f549efca6 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,8 +6,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" + p2phost "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/host" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - p2phost "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/host" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index def431954..4a362693c 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,8 +8,8 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/host" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/host" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 3a2b08157..0ecd65b60 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" + inet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - inet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 04706a55d..6d242ad43 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -6,9 +6,9 @@ import ( ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + host "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/host" + inet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - host "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/host" - inet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" key "github.com/ipfs/go-ipfs/blocks/key" From cd807dd35ef53a5082cc6820a154af5da8a4d2b7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 16 May 2016 11:22:36 -0700 Subject: [PATCH 1272/3817] update libp2p to v3.2.1 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@80cc569b51b8db54089fd62acbb489c5472cd9ef --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 872e92d3a..4d838d33f 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" + mocknet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net/mock" peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - mocknet "gx/ipfs/QmZpVD1kkRwoC67vNknvCrY72pjmVdtZ7txSk8mtCbuwd3/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { From 03a0bde418657e2185cf972e56161f0cd4d0e867 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 16 May 2016 17:09:54 -0700 Subject: [PATCH 1273/3817] don't return nil multiaddrs from dht messages License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@d76463e795c5a8d97b0eae8e012979640c26ba7a --- routing/dht/pb/message.go | 11 ++++++----- routing/dht/pb/message_test.go | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 routing/dht/pb/message_test.go diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 919841f04..ba3104ca2 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -107,14 +107,15 @@ func (m *Message_Peer) Addresses() []ma.Multiaddr { return nil } - var err error - maddrs := make([]ma.Multiaddr, len(m.Addrs)) - for i, addr := range m.Addrs { - maddrs[i], err = ma.NewMultiaddrBytes(addr) + maddrs := make([]ma.Multiaddr, 0, len(m.Addrs)) + for _, addr := range m.Addrs { + maddr, err := ma.NewMultiaddrBytes(addr) if err != nil { - log.Debugf("error decoding Multiaddr for peer: %s", m.GetId()) + log.Warningf("error decoding Multiaddr for peer: %s", m.GetId()) continue } + + maddrs = append(maddrs, maddr) } return maddrs } diff --git a/routing/dht/pb/message_test.go b/routing/dht/pb/message_test.go new file mode 100644 index 000000000..71f4abdc5 --- /dev/null +++ b/routing/dht/pb/message_test.go @@ -0,0 +1,15 @@ +package dht_pb + +import ( + "testing" +) + +func TestBadAddrsDontReturnNil(t *testing.T) { + mp := new(Message_Peer) + mp.Addrs = [][]byte{[]byte("NOT A VALID MULTIADDR")} + + addrs := mp.Addresses() + if len(addrs) > 0 { + t.Fatal("shouldnt have any multiaddrs") + } +} From e8b80379da46d57329daad393c128b3b0cd1b4e6 Mon Sep 17 00:00:00 2001 From: jbenet Date: Mon, 16 May 2016 22:39:39 -0700 Subject: [PATCH 1274/3817] add error checking for nil keys Checks in: - blockstore - blockservice - dagservice - bitswap Do not anger the pokemans #2715 License: MIT Signed-off-by: Juan Benet This commit was moved from ipfs/go-merkledag@b26887e5f8aa166c9dcde8a2fb446693b6be1abf --- ipld/merkledag/merkledag.go | 3 +++ ipld/merkledag/merkledag_test.go | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 938b71308..b98fdafe6 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -68,6 +68,9 @@ func (n *dagService) Batch() *Batch { // Get retrieves a node from the dagService, fetching the block in the BlockService func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) { + if k == "" { + return nil, ErrNotFound + } if n == nil { return nil, fmt.Errorf("dagService is nil") } diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index e475fa680..2739e2195 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -32,6 +32,13 @@ type dagservAndPinner struct { mp pin.Pinner } +func getDagserv(t *testing.T) DAGService { + db := dssync.MutexWrap(ds.NewMapDatastore()) + bs := bstore.NewBlockstore(db) + blockserv := bserv.New(bs, offline.Exchange(bs)) + return NewDAGService(blockserv) +} + func getDagservAndPinner(t *testing.T) dagservAndPinner { db := dssync.MutexWrap(ds.NewMapDatastore()) bs := bstore.NewBlockstore(db) @@ -245,6 +252,14 @@ func assertCanGet(t *testing.T, ds DAGService, n *Node) { } } +func TestEmptyKey(t *testing.T) { + ds := getDagserv(t) + _, err := ds.Get(context.Background(), key.Key("")) + if err != ErrNotFound { + t.Error("dag service should error when key is nil", err) + } +} + func TestCantGet(t *testing.T) { dsp := getDagservAndPinner(t) a := &Node{Data: []byte("A")} From 48e48404d4a5b2bb9ee90c3ff3ec9cbdfa84a154 Mon Sep 17 00:00:00 2001 From: jbenet Date: Mon, 16 May 2016 22:39:39 -0700 Subject: [PATCH 1275/3817] add error checking for nil keys Checks in: - blockstore - blockservice - dagservice - bitswap Do not anger the pokemans #2715 License: MIT Signed-off-by: Juan Benet This commit was moved from ipfs/go-ipfs-blockstore@7faf20ac0452de7192ca4f15c24476ca874b75ae --- blockstore/blockstore.go | 4 ++++ blockstore/blockstore_test.go | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 671ae2c25..d3a9b1aa1 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -74,6 +74,10 @@ type blockstore struct { } func (bs *blockstore) Get(k key.Key) (blocks.Block, error) { + if k == "" { + return nil, ErrNotFound + } + maybeData, err := bs.datastore.Get(k.DsKey()) if err == ds.ErrNotFound { return nil, ErrNotFound diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 446d4b776..8b0609f1f 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -27,6 +27,14 @@ func TestGetWhenKeyNotPresent(t *testing.T) { t.Fail() } +func TestGetWhenKeyIsEmptyString(t *testing.T) { + bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) + _, err := bs.Get(key.Key("")) + if err != ErrNotFound { + t.Fail() + } +} + func TestPutThenGetBlock(t *testing.T) { bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) block := blocks.NewBlock([]byte("some data")) From 964076e0fd6e9ae530a58bf6d4066e4507cb12d7 Mon Sep 17 00:00:00 2001 From: jbenet Date: Mon, 16 May 2016 22:39:39 -0700 Subject: [PATCH 1276/3817] add error checking for nil keys Checks in: - blockstore - blockservice - dagservice - bitswap Do not anger the pokemans #2715 License: MIT Signed-off-by: Juan Benet This commit was moved from ipfs/go-blockservice@f576ac716a224aa871751ff9775362d3a73fc48d --- blockservice/blockservice.go | 5 +++++ blockservice/test/blocks_test.go | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 78838757a..945f60ae6 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -72,6 +72,11 @@ func (s *BlockService) AddBlocks(bs []blocks.Block) ([]key.Key, error) { // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). func (s *BlockService) GetBlock(ctx context.Context, k key.Key) (blocks.Block, error) { + if k == "" { + log.Debug("BlockService GetBlock: Nil Key") + return nil, ErrNotFound + } + log.Debugf("BlockService GetBlock: '%s'", k) block, err := s.Blockstore.Get(k) if err == nil { diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 584505b21..ed61dad59 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -22,6 +22,11 @@ func TestBlocks(t *testing.T) { bs := New(bstore, offline.Exchange(bstore)) defer bs.Close() + _, err := bs.GetBlock(context.Background(), key.Key("")) + if err != ErrNotFound { + t.Error("Empty String Key should error", err) + } + b := blocks.NewBlock([]byte("beep boop")) h := u.Hash([]byte("beep boop")) if !bytes.Equal(b.Multihash(), h) { From c6368d9ebf264270fc5edf3191453d36bda84668 Mon Sep 17 00:00:00 2001 From: Juan Benet Date: Tue, 17 May 2016 00:22:19 -0700 Subject: [PATCH 1277/3817] address CR - use dstest.Mock License: MIT Signed-off-by: Juan Benet This commit was moved from ipfs/go-merkledag@173940b46671fbd040308375304f6f72ec9c37e2 --- ipld/merkledag/merkledag_test.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 2739e2195..430e31f7a 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -32,13 +32,6 @@ type dagservAndPinner struct { mp pin.Pinner } -func getDagserv(t *testing.T) DAGService { - db := dssync.MutexWrap(ds.NewMapDatastore()) - bs := bstore.NewBlockstore(db) - blockserv := bserv.New(bs, offline.Exchange(bs)) - return NewDAGService(blockserv) -} - func getDagservAndPinner(t *testing.T) dagservAndPinner { db := dssync.MutexWrap(ds.NewMapDatastore()) bs := bstore.NewBlockstore(db) @@ -253,7 +246,7 @@ func assertCanGet(t *testing.T, ds DAGService, n *Node) { } func TestEmptyKey(t *testing.T) { - ds := getDagserv(t) + ds := dstest.Mock() _, err := ds.Get(context.Background(), key.Key("")) if err != ErrNotFound { t.Error("dag service should error when key is nil", err) From c9aeb17bbf5b88c179b32cfda207f4cae56c1ca2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 17 May 2016 10:23:10 -0700 Subject: [PATCH 1278/3817] update go-libp2p 3.2.2, nil maddr fixes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@66ac0a21e97787f8412f80879b1d67299162e914 --- routing/dht/dht.go | 6 +++--- routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 4 ++-- routing/dht/dht_test.go | 4 ++-- routing/dht/diag.go | 2 +- routing/dht/ext_test.go | 6 +++--- routing/dht/handlers.go | 2 +- routing/dht/lookup.go | 2 +- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 4 ++-- routing/dht/providers.go | 2 +- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 4 ++-- routing/dht/records.go | 2 +- routing/dht/routing.go | 4 ++-- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 2 +- routing/kbucket/table_test.go | 2 +- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 2 +- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 2 +- routing/routing.go | 2 +- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 2 +- 32 files changed, 46 insertions(+), 46 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index deef86022..0d9be4a55 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -15,10 +15,10 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - host "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/host" - protocol "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/protocol" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + host "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/host" + protocol "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/protocol" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 5fddf954e..01d23a7f0 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -10,7 +10,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" periodicproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/periodic" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 2fe516cec..94b5a435d 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,10 +6,10 @@ import ( ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net" + inet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // handleNewStream implements the inet.StreamHandler diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 0c66a5502..760871d32 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -17,9 +17,9 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" - netutil "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/test/util" + netutil "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/test/util" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" diff --git a/routing/dht/diag.go b/routing/dht/diag.go index cf30393f1..59ceae87e 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) type connDiagInfo struct { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 7c5446172..de94f4413 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -16,10 +16,10 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - inet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net/mock" + inet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net/mock" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 15fd25679..47c03f01e 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -11,8 +11,8 @@ import ( lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // The number of closer peers to send on requests. diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index ad523d6d9..9c1ebc22f 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -5,8 +5,8 @@ import ( notif "github.com/ipfs/go-ipfs/notifications" kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // Required in order for proper JSON marshaling diff --git a/routing/dht/notif.go b/routing/dht/notif.go index fb7c2ee14..235f0f933 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - inet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net" + inet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index ba3104ca2..93815e3aa 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,9 +4,9 @@ import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" - inet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + inet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/providers.go b/routing/dht/providers.go index df16a5c15..ecfd691ae 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -6,7 +6,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 9d4861943..0e9cfd835 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -4,7 +4,7 @@ import ( "testing" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index 872e9f947..b2d853b85 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -9,9 +9,9 @@ import ( pset "github.com/ipfs/go-ipfs/thirdparty/peerset" todoctr "github.com/ipfs/go-ipfs/thirdparty/todocounter" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" - queue "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer/queue" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" + queue "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer/queue" process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" ctxproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" diff --git a/routing/dht/records.go b/routing/dht/records.go index fdfb12d2d..23b01410b 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -9,8 +9,8 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // MaxRecordAge specifies the maximum time that any node will hold onto a record diff --git a/routing/dht/routing.go b/routing/dht/routing.go index d91478101..040b5d56b 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,9 +12,9 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - inet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + inet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // asyncQueryBuffer is the size of buffered channels in async queries. This diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 37b7cb8c8..96c0a6808 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index 6d282e0f0..5d0c834e0 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 7c22fec7d..378741586 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,8 +7,8 @@ import ( "sync" "time" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) var log = logging.Logger("table") diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 4ac3a2af0..85c579c80 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,7 +7,7 @@ import ( tu "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 8653c7e6a..107dbf473 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // Returned if a routing table query returns no results. This is NOT expected diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 5230fba83..e48cb679b 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -12,9 +12,9 @@ import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 13ae8e262..5af2b92ff 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -9,8 +9,8 @@ import ( dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 1d8862306..6bbbc1fbf 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index cb4987cfc..6cb63fa51 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,7 +5,7 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 25069071b..66d43d0bc 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,8 +10,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // Server provides mockrouting Clients diff --git a/routing/none/none_client.go b/routing/none/none_client.go index f549efca6..58f397577 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,10 +6,10 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" - p2phost "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/host" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + p2phost "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/host" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 4942b27c6..398e5b07b 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -11,9 +11,9 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) var log = logging.Logger("offlinerouting") diff --git a/routing/routing.go b/routing/routing.go index 3b1ab799c..bedcabd53 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -6,8 +6,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // ErrNotFound is returned when a search fails to find anything diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 4a362693c..d8ee83f6b 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,9 +8,9 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/host" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/host" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 0ecd65b60..5bbecbcf8 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + inet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 6d242ad43..983bf79ee 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -6,10 +6,10 @@ import ( ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - host "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/host" - inet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + host "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/host" + inet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 815e53e1b..64b0ef87b 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -12,7 +12,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // Server handles routing queries using a database backend From 0da4509f489dd139d84a64ebd9ef67a39b80ef78 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 17 May 2016 10:23:10 -0700 Subject: [PATCH 1279/3817] update go-libp2p 3.2.2, nil maddr fixes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@15822a89018331dd2ed96b3589cb1ca219063fe1 --- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 17bc7d736..d8ca95623 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -21,7 +21,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index dc0645898..3071b9bb8 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,7 +11,7 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 4d838d33f..93539c7f0 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmUHrgorZ1F9yGkgF2His5fsQ9xtCzjdsPGjizmcEW94i5/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" + mocknet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net/mock" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 4d638735f..95060e903 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -12,8 +12,8 @@ import ( mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmZpD74pUj6vuxTp1o6LhA3JavC2Bvh9fsWPPVvHnD9sE7/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) func TestRoutingResolve(t *testing.T) { From 07ceaaae018bc55b5f31bac7a49c755df22d1021 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 19 May 2016 12:33:04 +0200 Subject: [PATCH 1280/3817] Move proquint from Godeps to gx License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@a5c9e238483d0ea9f7b81e62cf7b94cb017a1edb --- namesys/proquint.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/proquint.go b/namesys/proquint.go index ce17181e8..f90a4c8a1 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -3,8 +3,8 @@ package namesys import ( "errors" - proquint "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/bren2010/proquint" path "github.com/ipfs/go-ipfs/path" + proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From b38bfc8161dd96f80ba41be461c5500e89932f5c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 30 May 2016 22:14:21 -0700 Subject: [PATCH 1281/3817] update libp2p to v3.2.3 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@6704479321aeb8c391d4af61a0f5d67525e20caa --- routing/dht/dht.go | 4 ++-- routing/dht/dht_net.go | 2 +- routing/dht/dht_test.go | 2 +- routing/dht/ext_test.go | 4 ++-- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/routing.go | 2 +- routing/mock/dht.go | 2 +- routing/none/none_client.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 4 ++-- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 0d9be4a55..70d597d4b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -14,9 +14,9 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" + host "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/host" + protocol "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/protocol" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - host "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/host" - protocol "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/protocol" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 94b5a435d..ded60c18b 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,7 +6,7 @@ import ( ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net" + inet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 760871d32..e281563b2 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -17,7 +17,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" - netutil "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/test/util" + netutil "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/test/util" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index de94f4413..f64a72539 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -16,8 +16,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - inet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net/mock" + inet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net/mock" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 235f0f933..c7c6399b9 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - inet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net" + inet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 93815e3aa..9733c38b7 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,7 +4,7 @@ import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" - inet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net" + inet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 040b5d56b..413847e00 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,7 +12,7 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - inet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net" + inet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 6cb63fa51..d5d2d40d8 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,7 +5,7 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 58f397577..fa80f1e63 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,7 +6,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" - p2phost "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/host" + p2phost "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/host" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" diff --git a/routing/supernode/client.go b/routing/supernode/client.go index d8ee83f6b..2a7131961 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,7 +8,7 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/host" + "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/host" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 5bbecbcf8..88041a1af 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,7 +5,7 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net" + inet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net" peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 983bf79ee..7e3ea1ee8 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -6,8 +6,8 @@ import ( ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - host "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/host" - inet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net" + host "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/host" + inet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" From 2aeefcf9f3381aad29ad93156262d3e9a2a9d1cc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 30 May 2016 22:14:21 -0700 Subject: [PATCH 1282/3817] update libp2p to v3.2.3 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@1f36f2a43db0533673414e91ded6a735401727f3 --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 93539c7f0..44c362256 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,7 +13,7 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmVL44QeoQDTYK8RVdpkyja7uYcK3WDNoBNHVLonf9YDtm/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net/mock" peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) From 189763dd7bd4ea467e05a6c65205a9ecb9613d78 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 24 May 2016 21:05:15 +0200 Subject: [PATCH 1283/3817] Move go-context to gx License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-routing@cb083ee28b9f5dce25b7e03db928b789a03a3904 --- routing/dht/dht_net.go | 2 +- routing/dht/records.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index ded60c18b..221c60dc1 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -4,9 +4,9 @@ import ( "errors" "time" - ctxio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/io" pb "github.com/ipfs/go-ipfs/routing/dht/pb" inet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net" + ctxio "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/io" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" diff --git a/routing/dht/records.go b/routing/dht/records.go index 23b01410b..9d2eab248 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -4,11 +4,11 @@ import ( "fmt" "time" - ctxfrac "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/frac" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + ctxfrac "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/frac" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) From 86c4958210b672a94517b1752d997ec91465adad Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 1 Jun 2016 12:18:59 -0700 Subject: [PATCH 1284/3817] buffer error chan to prevent dead goro buildup License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@f14ee339e4513c19fdfb03b395ea502d3b8a8c1b --- namesys/publisher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index d8ca95623..5388583a5 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -146,7 +146,7 @@ func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqn entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds())) } - errs := make(chan error) + errs := make(chan error, 2) go func() { errs <- PublishEntry(ctx, r, ipnskey, entry) From a92b4b41d07baa304c5ee31a4d176c37fcd84840 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 1 Jun 2016 12:20:04 -0700 Subject: [PATCH 1285/3817] localize context cancellation in PutRecordToRouting License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@f71ea1933d1239dd2cdef55837ed54737f8c0b00 --- namesys/publisher.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/namesys/publisher.go b/namesys/publisher.go index 5388583a5..5fdbb725a 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -135,6 +135,9 @@ func checkCtxTTL(ctx context.Context) (time.Duration, bool) { } func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.IpfsRouting, id peer.ID) error { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + namekey, ipnskey := IpnsKeysForID(id) entry, err := CreateRoutingEntryData(k, value, seqnum, eol) if err != nil { From 7b2b834523457fc5657ba7e689ea4e92aa8bdb83 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 1 Jun 2016 15:51:39 -0700 Subject: [PATCH 1286/3817] update libp2p to v3.3.1 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@462beabf988d6297c1f680cf72bdd1a8c9a466a1 --- routing/dht/dht.go | 27 ++++++++++++------------ routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 4 ++-- routing/dht/dht_test.go | 28 ++++++++++++------------- routing/dht/diag.go | 2 +- routing/dht/ext_test.go | 16 +++++++-------- routing/dht/handlers.go | 16 ++++++++------- routing/dht/lookup.go | 12 ++++++----- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 25 +++++++++++----------- routing/dht/providers.go | 2 +- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 21 ++++++++++--------- routing/dht/records.go | 2 +- routing/dht/routing.go | 32 +++++++++++++++-------------- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 7 ++++--- routing/kbucket/table_test.go | 15 +++++++------- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 16 ++++++++------- routing/mock/centralized_server.go | 16 ++++++++------- routing/mock/centralized_test.go | 5 +++-- routing/mock/dht.go | 2 +- routing/mock/interface.go | 5 +++-- routing/none/none_client.go | 13 ++++++------ routing/offline/offline.go | 14 +++++++------ routing/routing.go | 9 ++++---- routing/supernode/client.go | 32 ++++++++++++++--------------- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 19 +++++++++-------- routing/supernode/server.go | 19 +++++++++-------- 32 files changed, 199 insertions(+), 176 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 70d597d4b..6ddd4325e 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -14,17 +14,18 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - host "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/host" - protocol "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/protocol" - ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + host "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/host" + protocol "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/protocol" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("dht") @@ -40,9 +41,9 @@ const NumBootstrapQueries = 5 // IpfsDHT is an implementation of Kademlia with Coral and S/Kademlia modifications. // It is used to implement the base IpfsRouting module. type IpfsDHT struct { - host host.Host // the network services we need - self peer.ID // Local peer (yourself) - peerstore peer.Peerstore // Peer Registry + host host.Host // the network services we need + self peer.ID // Local peer (yourself) + peerstore pstore.Peerstore // Peer Registry datastore ds.Datastore // Local data @@ -127,7 +128,7 @@ func (dht *IpfsDHT) putValueToPeer(ctx context.Context, p peer.ID, func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, skey string) error { // add self as the provider - pi := peer.PeerInfo{ + pi := pstore.PeerInfo{ ID: dht.self, Addrs: dht.host.Addrs(), } @@ -140,7 +141,7 @@ func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, skey string) err } pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, skey, 0) - pmes.ProviderPeers = pb.RawPeerInfosToPBPeers([]peer.PeerInfo{pi}) + pmes.ProviderPeers = pb.RawPeerInfosToPBPeers([]pstore.PeerInfo{pi}) err := dht.sendMessage(ctx, p, pmes) if err != nil { return err @@ -157,7 +158,7 @@ var errInvalidRecord = errors.New("received invalid record") // NOTE: It will update the dht's peerstore with any new addresses // it finds for the given peer. func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.ID, - key key.Key) (*pb.Record, []peer.PeerInfo, error) { + key key.Key) (*pb.Record, []pstore.PeerInfo, error) { pmes, err := dht.getValueSingle(ctx, p, key) if err != nil { @@ -258,12 +259,12 @@ func (dht *IpfsDHT) Update(ctx context.Context, p peer.ID) { } // FindLocal looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in. -func (dht *IpfsDHT) FindLocal(id peer.ID) peer.PeerInfo { +func (dht *IpfsDHT) FindLocal(id peer.ID) pstore.PeerInfo { p := dht.routingTable.Find(id) if p != "" { return dht.peerstore.PeerInfo(p) } - return peer.PeerInfo{} + return pstore.PeerInfo{} } // findPeerSingle asks peer 'p' if they know where the peer with id 'id' is diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 01d23a7f0..774e632de 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -9,8 +9,8 @@ import ( "time" routing "github.com/ipfs/go-ipfs/routing" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" periodicproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/periodic" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 221c60dc1..4ebd6e3f6 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -5,11 +5,11 @@ import ( "time" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + inet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net" ctxio "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/io" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // handleNewStream implements the inet.StreamHandler diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index e281563b2..f4bc863a6 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -11,18 +11,18 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" - netutil "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/test/util" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" - ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" + + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + netutil "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/test/util" + ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) var testCaseValues = map[key.Key][]byte{} @@ -73,8 +73,8 @@ func connect(t *testing.T, ctx context.Context, a, b *IpfsDHT) { t.Fatal("peers setup incorrectly: no local address") } - a.peerstore.AddAddrs(idB, addrB, peer.TempAddrTTL) - pi := peer.PeerInfo{ID: idB} + a.peerstore.AddAddrs(idB, addrB, pstore.TempAddrTTL) + pi := pstore.PeerInfo{ID: idB} if err := a.host.Connect(ctx, pi); err != nil { t.Fatal(err) } @@ -705,7 +705,7 @@ func TestFindPeersConnectedToPeer(t *testing.T) { } // shouldFind := []peer.ID{peers[1], peers[3]} - found := []peer.PeerInfo{} + var found []pstore.PeerInfo for nextp := range pchan { found = append(found, nextp) } @@ -776,14 +776,14 @@ func TestConnectCollision(t *testing.T) { errs := make(chan error) go func() { - dhtA.peerstore.AddAddr(peerB, addrB, peer.TempAddrTTL) - pi := peer.PeerInfo{ID: peerB} + dhtA.peerstore.AddAddr(peerB, addrB, pstore.TempAddrTTL) + pi := pstore.PeerInfo{ID: peerB} err := dhtA.host.Connect(ctx, pi) errs <- err }() go func() { - dhtB.peerstore.AddAddr(peerA, addrA, peer.TempAddrTTL) - pi := peer.PeerInfo{ID: peerA} + dhtB.peerstore.AddAddr(peerA, addrA, pstore.TempAddrTTL) + pi := pstore.PeerInfo{ID: peerA} err := dhtB.host.Connect(ctx, pi) errs <- err }() diff --git a/routing/dht/diag.go b/routing/dht/diag.go index 59ceae87e..7958a2783 100644 --- a/routing/dht/diag.go +++ b/routing/dht/diag.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" ) type connDiagInfo struct { diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index f64a72539..28868a992 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -9,17 +9,17 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" - ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - inet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net/mock" + + inet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net/mock" + ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) func TestGetFailures(t *testing.T) { @@ -183,7 +183,7 @@ func TestNotFound(t *testing.T) { case pb.Message_GET_VALUE: resp := &pb.Message{Type: pmes.Type} - ps := []peer.PeerInfo{} + ps := []pstore.PeerInfo{} for i := 0; i < 7; i++ { p := hosts[rand.Intn(len(hosts))].ID() pi := host.Peerstore().PeerInfo(p) @@ -262,7 +262,7 @@ func TestLessThanKResponses(t *testing.T) { pi := host.Peerstore().PeerInfo(hosts[1].ID()) resp := &pb.Message{ Type: pmes.Type, - CloserPeers: pb.PeerInfosToPBPeers(d.host.Network(), []peer.PeerInfo{pi}), + CloserPeers: pb.PeerInfosToPBPeers(d.host.Network(), []pstore.PeerInfo{pi}), } if err := pbw.WriteMsg(resp); err != nil { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 47c03f01e..59aae4423 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -9,10 +9,12 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" + + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // The number of closer peers to send on requests. @@ -63,7 +65,7 @@ func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Mess // Find closest peer on given cluster to desired key and reply with that info closer := dht.betterPeersToQuery(pmes, p, CloserPeerCount) if len(closer) > 0 { - closerinfos := peer.PeerInfos(dht.peerstore, closer) + closerinfos := pstore.PeerInfos(dht.peerstore, closer) for _, pi := range closerinfos { log.Debugf("handleGetValue returning closer peer: '%s'", pi.ID) if len(pi.Addrs) < 1 { @@ -190,8 +192,8 @@ func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.ID, pmes *pb.Mess return resp, nil } - var withAddresses []peer.PeerInfo - closestinfos := peer.PeerInfos(dht.peerstore, closest) + var withAddresses []pstore.PeerInfo + closestinfos := pstore.PeerInfos(dht.peerstore, closest) for _, pi := range closestinfos { if len(pi.Addrs) > 0 { withAddresses = append(withAddresses, pi) @@ -232,7 +234,7 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb. } if providers != nil && len(providers) > 0 { - infos := peer.PeerInfos(dht.peerstore, providers) + infos := pstore.PeerInfos(dht.peerstore, providers) resp.ProviderPeers = pb.PeerInfosToPBPeers(dht.host.Network(), infos) log.Debugf("%s have %d providers: %s", reqDesc, len(providers), infos) } @@ -240,7 +242,7 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb. // Also send closer peers. closer := dht.betterPeersToQuery(pmes, p, CloserPeerCount) if closer != nil { - infos := peer.PeerInfos(dht.peerstore, closer) + infos := pstore.PeerInfos(dht.peerstore, closer) resp.CloserPeers = pb.PeerInfosToPBPeers(dht.host.Network(), infos) log.Debugf("%s have %d closer peers: %s", reqDesc, len(closer), infos) } @@ -276,7 +278,7 @@ func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.M log.Infof("received provider %s for %s (addrs: %s)", p, key, pi.Addrs) if pi.ID != dht.self { // dont add own addrs. // add the received addresses to our peerstore. - dht.peerstore.AddAddrs(pi.ID, pi.Addrs, peer.ProviderAddrTTL) + dht.peerstore.AddAddrs(pi.ID, pi.Addrs, pstore.ProviderAddrTTL) } dht.providers.AddProvider(ctx, key, p) } diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 9c1ebc22f..2744fb5a7 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -5,13 +5,15 @@ import ( notif "github.com/ipfs/go-ipfs/notifications" kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" + + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // Required in order for proper JSON marshaling -func pointerizePeerInfos(pis []peer.PeerInfo) []*peer.PeerInfo { - out := make([]*peer.PeerInfo, len(pis)) +func pointerizePeerInfos(pis []pstore.PeerInfo) []*pstore.PeerInfo { + out := make([]*pstore.PeerInfo, len(pis)) for i, p := range pis { np := p out[i] = &np @@ -56,7 +58,7 @@ func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key key.Key) (<-chan pe return nil, err } - var filtered []peer.PeerInfo + var filtered []pstore.PeerInfo for _, clp := range closer { if kb.Closer(clp, dht.self, key) && peerset.TryAdd(clp) { select { @@ -101,7 +103,7 @@ func (dht *IpfsDHT) closerPeersSingle(ctx context.Context, key key.Key, p peer.I for _, pbp := range pmes.GetCloserPeers() { pid := peer.ID(pbp.GetId()) if pid != dht.self { // dont add self - dht.peerstore.AddAddrs(pid, pbp.Addresses(), peer.TempAddrTTL) + dht.peerstore.AddAddrs(pid, pbp.Addresses(), pstore.TempAddrTTL) out = append(out, pid) } } diff --git a/routing/dht/notif.go b/routing/dht/notif.go index c7c6399b9..b9d16819f 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - inet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net" + inet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 9733c38b7..b1ab8097b 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,15 +4,16 @@ import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" - inet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + inet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) var log = logging.Logger("dht.pb") type PeerRoutingInfo struct { - peer.PeerInfo + pstore.PeerInfo inet.Connectedness } @@ -40,7 +41,7 @@ func peerRoutingInfoToPBPeer(p PeerRoutingInfo) *Message_Peer { return pbp } -func peerInfoToPBPeer(p peer.PeerInfo) *Message_Peer { +func peerInfoToPBPeer(p pstore.PeerInfo) *Message_Peer { pbp := new(Message_Peer) pbp.Addrs = make([][]byte, len(p.Addrs)) @@ -52,9 +53,9 @@ func peerInfoToPBPeer(p peer.PeerInfo) *Message_Peer { return pbp } -// PBPeerToPeer turns a *Message_Peer into its peer.PeerInfo counterpart -func PBPeerToPeerInfo(pbp *Message_Peer) peer.PeerInfo { - return peer.PeerInfo{ +// PBPeerToPeer turns a *Message_Peer into its pstore.PeerInfo counterpart +func PBPeerToPeerInfo(pbp *Message_Peer) pstore.PeerInfo { + return pstore.PeerInfo{ ID: peer.ID(pbp.GetId()), Addrs: pbp.Addresses(), } @@ -62,7 +63,7 @@ func PBPeerToPeerInfo(pbp *Message_Peer) peer.PeerInfo { // RawPeerInfosToPBPeers converts a slice of Peers into a slice of *Message_Peers, // ready to go out on the wire. -func RawPeerInfosToPBPeers(peers []peer.PeerInfo) []*Message_Peer { +func RawPeerInfosToPBPeers(peers []pstore.PeerInfo) []*Message_Peer { pbpeers := make([]*Message_Peer, len(peers)) for i, p := range peers { pbpeers[i] = peerInfoToPBPeer(p) @@ -74,7 +75,7 @@ func RawPeerInfosToPBPeers(peers []peer.PeerInfo) []*Message_Peer { // which can be written to a message and sent out. the key thing this function // does (in addition to PeersToPBPeers) is set the ConnectionType with // information from the given inet.Network. -func PeerInfosToPBPeers(n inet.Network, peers []peer.PeerInfo) []*Message_Peer { +func PeerInfosToPBPeers(n inet.Network, peers []pstore.PeerInfo) []*Message_Peer { pbps := RawPeerInfosToPBPeers(peers) for i, pbp := range pbps { c := ConnectionType(n.Connectedness(peers[i].ID)) @@ -91,10 +92,10 @@ func PeerRoutingInfosToPBPeers(peers []PeerRoutingInfo) []*Message_Peer { return pbpeers } -// PBPeersToPeerInfos converts given []*Message_Peer into []peer.PeerInfo +// PBPeersToPeerInfos converts given []*Message_Peer into []pstore.PeerInfo // Invalid addresses will be silently omitted. -func PBPeersToPeerInfos(pbps []*Message_Peer) []peer.PeerInfo { - peers := make([]peer.PeerInfo, 0, len(pbps)) +func PBPeersToPeerInfos(pbps []*Message_Peer) []pstore.PeerInfo { + peers := make([]pstore.PeerInfo, 0, len(pbps)) for _, pbp := range pbps { peers = append(peers, PBPeerToPeerInfo(pbp)) } diff --git a/routing/dht/providers.go b/routing/dht/providers.go index ecfd691ae..a394123c8 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -4,9 +4,9 @@ import ( "time" key "github.com/ipfs/go-ipfs/blocks/key" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 0e9cfd835..8d25cfeba 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -4,7 +4,7 @@ import ( "testing" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index b2d853b85..6b7cc0c04 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -8,14 +8,15 @@ import ( "github.com/ipfs/go-ipfs/routing" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" todoctr "github.com/ipfs/go-ipfs/thirdparty/todocounter" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" - queue "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer/queue" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" ctxproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" + queue "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore/queue" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var maxQueryConcurrency = AlphaValue @@ -28,10 +29,10 @@ type dhtQuery struct { } type dhtQueryResult struct { - value []byte // GetValue - peer peer.PeerInfo // FindPeer - providerPeers []peer.PeerInfo // GetProviders - closerPeers []peer.PeerInfo // * + value []byte // GetValue + peer pstore.PeerInfo // FindPeer + providerPeers []pstore.PeerInfo // GetProviders + closerPeers []pstore.PeerInfo // * success bool } @@ -239,7 +240,7 @@ func (r *dhtQueryRunner) queryPeer(proc process.Process, p peer.ID) { // forward progress during potentially very high latency dials. r.rateLimit <- struct{}{} - pi := peer.PeerInfo{ID: p} + pi := pstore.PeerInfo{ID: p} if err := r.query.dht.host.Connect(ctx, pi); err != nil { log.Debugf("Error connecting: %s", err) @@ -286,7 +287,7 @@ func (r *dhtQueryRunner) queryPeer(proc process.Process, p peer.ID) { } // add their addresses to the dialer's peerstore - r.query.dht.peerstore.AddAddrs(next.ID, next.Addrs, peer.TempAddrTTL) + r.query.dht.peerstore.AddAddrs(next.ID, next.Addrs, pstore.TempAddrTTL) r.addPeerToQuery(next.ID) log.Debugf("PEERS CLOSER -- worker for: %v added %v (%v)", p, next.ID, next.Addrs) } diff --git a/routing/dht/records.go b/routing/dht/records.go index 9d2eab248..477f8c6c0 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -7,10 +7,10 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" ctxfrac "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/frac" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // MaxRecordAge specifies the maximum time that any node will hold onto a record diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 413847e00..ca1018d97 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -12,9 +12,11 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - inet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net" + + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + inet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // asyncQueryBuffer is the size of buffered channels in async queries. This @@ -258,8 +260,8 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key key.Key) error { } // FindProviders searches until the context expires. -func (dht *IpfsDHT) FindProviders(ctx context.Context, key key.Key) ([]peer.PeerInfo, error) { - var providers []peer.PeerInfo +func (dht *IpfsDHT) FindProviders(ctx context.Context, key key.Key) ([]pstore.PeerInfo, error) { + var providers []pstore.PeerInfo for p := range dht.FindProvidersAsync(ctx, key, KValue) { providers = append(providers, p) } @@ -269,14 +271,14 @@ func (dht *IpfsDHT) FindProviders(ctx context.Context, key key.Key) ([]peer.Peer // FindProvidersAsync is the same thing as FindProviders, but returns a channel. // Peers will be returned on the channel as soon as they are found, even before // the search query completes. -func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key key.Key, count int) <-chan peer.PeerInfo { +func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key key.Key, count int) <-chan pstore.PeerInfo { log.Event(ctx, "findProviders", &key) - peerOut := make(chan peer.PeerInfo, count) + peerOut := make(chan pstore.PeerInfo, count) go dht.findProvidersAsyncRoutine(ctx, key, count, peerOut) return peerOut } -func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key key.Key, count int, peerOut chan peer.PeerInfo) { +func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key key.Key, count int, peerOut chan pstore.PeerInfo) { defer log.EventBegin(ctx, "findProvidersAsync", &key).Done() defer close(peerOut) @@ -357,7 +359,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key key.Key, } // FindPeer searches for a peer with given ID. -func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, error) { +func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (pstore.PeerInfo, error) { defer log.EventBegin(ctx, "FindPeer", id).Done() // Check if were already connected to them @@ -367,7 +369,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, er peers := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), KValue) if len(peers) == 0 { - return peer.PeerInfo{}, kb.ErrLookupFailure + return pstore.PeerInfo{}, kb.ErrLookupFailure } // Sanity... @@ -415,22 +417,22 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, er // run it! result, err := query.Run(ctx, peers) if err != nil { - return peer.PeerInfo{}, err + return pstore.PeerInfo{}, err } log.Debugf("FindPeer %v %v", id, result.success) if result.peer.ID == "" { - return peer.PeerInfo{}, routing.ErrNotFound + return pstore.PeerInfo{}, routing.ErrNotFound } return result.peer, nil } // FindPeersConnectedToPeer searches for peers directly connected to a given peer. -func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (<-chan peer.PeerInfo, error) { +func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (<-chan pstore.PeerInfo, error) { - peerchan := make(chan peer.PeerInfo, asyncQueryBuffer) - peersSeen := peer.Set{} + peerchan := make(chan pstore.PeerInfo, asyncQueryBuffer) + peersSeen := make(map[peer.ID]struct{}) peers := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), KValue) if len(peers) == 0 { @@ -445,7 +447,7 @@ func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (< return nil, err } - var clpeers []peer.PeerInfo + var clpeers []pstore.PeerInfo closer := pmes.GetCloserPeers() for _, pbp := range closer { pi := pb.PBPeerToPeerInfo(pbp) diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 96c0a6808..1f887bd72 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index 5d0c834e0..8b4fce863 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 378741586..fc9c09672 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,8 +7,9 @@ import ( "sync" "time" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) var log = logging.Logger("table") @@ -23,7 +24,7 @@ type RoutingTable struct { tabLock sync.RWMutex // latency metrics - metrics peer.Metrics + metrics pstore.Metrics // Maximum acceptable latency for peers in this cluster maxLatency time.Duration @@ -34,7 +35,7 @@ type RoutingTable struct { } // NewRoutingTable creates a new routing table with a given bucketsize, local ID, and latency tolerance. -func NewRoutingTable(bucketsize int, localID ID, latency time.Duration, m peer.Metrics) *RoutingTable { +func NewRoutingTable(bucketsize int, localID ID, latency time.Duration, m pstore.Metrics) *RoutingTable { rt := new(RoutingTable) rt.Buckets = []*Bucket{newBucket()} rt.bucketsize = bucketsize diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 85c579c80..271b798c8 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,7 +7,8 @@ import ( tu "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" ) // Test basic features of the bucket struct @@ -51,7 +52,7 @@ func TestBucket(t *testing.T) { // Right now, this just makes sure that it doesnt hang or crash func TestTableUpdate(t *testing.T) { local := tu.RandPeerIDFatal(t) - m := peer.NewMetrics() + m := pstore.NewMetrics() rt := NewRoutingTable(10, ConvertPeerID(local), time.Hour, m) peers := make([]peer.ID, 100) @@ -75,7 +76,7 @@ func TestTableUpdate(t *testing.T) { func TestTableFind(t *testing.T) { local := tu.RandPeerIDFatal(t) - m := peer.NewMetrics() + m := pstore.NewMetrics() rt := NewRoutingTable(10, ConvertPeerID(local), time.Hour, m) peers := make([]peer.ID, 100) @@ -93,7 +94,7 @@ func TestTableFind(t *testing.T) { func TestTableFindMultiple(t *testing.T) { local := tu.RandPeerIDFatal(t) - m := peer.NewMetrics() + m := pstore.NewMetrics() rt := NewRoutingTable(20, ConvertPeerID(local), time.Hour, m) peers := make([]peer.ID, 100) @@ -114,7 +115,7 @@ func TestTableFindMultiple(t *testing.T) { // and set GOMAXPROCS above 1 func TestTableMultithreaded(t *testing.T) { local := peer.ID("localPeer") - m := peer.NewMetrics() + m := pstore.NewMetrics() tab := NewRoutingTable(20, ConvertPeerID(local), time.Hour, m) var peers []peer.ID for i := 0; i < 500; i++ { @@ -153,7 +154,7 @@ func TestTableMultithreaded(t *testing.T) { func BenchmarkUpdates(b *testing.B) { b.StopTimer() local := ConvertKey("localKey") - m := peer.NewMetrics() + m := pstore.NewMetrics() tab := NewRoutingTable(20, local, time.Hour, m) var peers []peer.ID @@ -170,7 +171,7 @@ func BenchmarkUpdates(b *testing.B) { func BenchmarkFinds(b *testing.B) { b.StopTimer() local := ConvertKey("localKey") - m := peer.NewMetrics() + m := pstore.NewMetrics() tab := NewRoutingTable(20, local, time.Hour, m) var peers []peer.ID diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 107dbf473..ad3fe4983 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // Returned if a routing table query returns no results. This is NOT expected diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index e48cb679b..cc4bc2d0f 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -9,12 +9,14 @@ import ( routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" "github.com/ipfs/go-ipfs/thirdparty/testutil" + + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) var log = logging.Logger("mockrouter") @@ -72,17 +74,17 @@ func (c *client) GetValues(ctx context.Context, key key.Key, count int) ([]routi return []routing.RecvdVal{{Val: data, From: c.peer.ID()}}, nil } -func (c *client) FindProviders(ctx context.Context, key key.Key) ([]peer.PeerInfo, error) { +func (c *client) FindProviders(ctx context.Context, key key.Key) ([]pstore.PeerInfo, error) { return c.server.Providers(key), nil } -func (c *client) FindPeer(ctx context.Context, pid peer.ID) (peer.PeerInfo, error) { +func (c *client) FindPeer(ctx context.Context, pid peer.ID) (pstore.PeerInfo, error) { log.Debugf("FindPeer: %s", pid) - return peer.PeerInfo{}, nil + return pstore.PeerInfo{}, nil } -func (c *client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan peer.PeerInfo { - out := make(chan peer.PeerInfo) +func (c *client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan pstore.PeerInfo { + out := make(chan pstore.PeerInfo) go func() { defer close(out) for i, p := range c.server.Providers(k) { @@ -102,7 +104,7 @@ func (c *client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-c // Provide returns once the message is on the network. Value is not necessarily // visible yet. func (c *client) Provide(_ context.Context, key key.Key) error { - info := peer.PeerInfo{ + info := pstore.PeerInfo{ ID: c.peer.ID(), Addrs: []ma.Multiaddr{c.peer.Address()}, } diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 5af2b92ff..e36c3d1d9 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -9,14 +9,16 @@ import ( dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" + + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // server is the mockrouting.Client's private interface to the routing server type server interface { - Announce(peer.PeerInfo, key.Key) error - Providers(key.Key) []peer.PeerInfo + Announce(pstore.PeerInfo, key.Key) error + Providers(key.Key) []pstore.PeerInfo Server } @@ -30,11 +32,11 @@ type s struct { } type providerRecord struct { - Peer peer.PeerInfo + Peer pstore.PeerInfo Created time.Time } -func (rs *s) Announce(p peer.PeerInfo, k key.Key) error { +func (rs *s) Announce(p pstore.PeerInfo, k key.Key) error { rs.lock.Lock() defer rs.lock.Unlock() @@ -49,13 +51,13 @@ func (rs *s) Announce(p peer.PeerInfo, k key.Key) error { return nil } -func (rs *s) Providers(k key.Key) []peer.PeerInfo { +func (rs *s) Providers(k key.Key) []pstore.PeerInfo { rs.delayConf.Query.Wait() // before locking rs.lock.RLock() defer rs.lock.RUnlock() - var ret []peer.PeerInfo + var ret []pstore.PeerInfo records, ok := rs.providers[k] if !ok { return ret diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 6bbbc1fbf..647a10042 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -7,8 +7,9 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" + + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) func TestKeyNotFound(t *testing.T) { @@ -150,7 +151,7 @@ func TestValidAfter(t *testing.T) { rs.Client(pi).Provide(ctx, key) - var providers []peer.PeerInfo + var providers []pstore.PeerInfo providers, err := rs.Client(pi).FindProviders(ctx, key) if err != nil { t.Fatal(err) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index d5d2d40d8..536f908e5 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,7 +5,7 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 66d43d0bc..1d833858a 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,8 +10,9 @@ import ( routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // Server provides mockrouting Clients @@ -22,7 +23,7 @@ type Server interface { // Client implements IpfsRouting type Client interface { - FindProviders(context.Context, key.Key) ([]peer.PeerInfo, error) + FindProviders(context.Context, key.Key) ([]pstore.PeerInfo, error) routing.IpfsRouting } diff --git a/routing/none/none_client.go b/routing/none/none_client.go index fa80f1e63..1752f33c7 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,10 +6,11 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" - p2phost "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/host" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + p2phost "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/host" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) var log = logging.Logger("mockrouter") @@ -29,12 +30,12 @@ func (c *nilclient) GetValues(_ context.Context, _ key.Key, _ int) ([]routing.Re return nil, errors.New("Tried GetValues from nil routing.") } -func (c *nilclient) FindPeer(_ context.Context, _ peer.ID) (peer.PeerInfo, error) { - return peer.PeerInfo{}, nil +func (c *nilclient) FindPeer(_ context.Context, _ peer.ID) (pstore.PeerInfo, error) { + return pstore.PeerInfo{}, nil } -func (c *nilclient) FindProvidersAsync(_ context.Context, _ key.Key, _ int) <-chan peer.PeerInfo { - out := make(chan peer.PeerInfo) +func (c *nilclient) FindProvidersAsync(_ context.Context, _ key.Key, _ int) <-chan pstore.PeerInfo { + out := make(chan pstore.PeerInfo) defer close(out) return out } diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 398e5b07b..6175f9111 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -9,11 +9,13 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" + + "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" - "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) var log = logging.Logger("offlinerouting") @@ -88,16 +90,16 @@ func (c *offlineRouting) GetValues(ctx context.Context, key key.Key, _ int) ([]r }, nil } -func (c *offlineRouting) FindProviders(ctx context.Context, key key.Key) ([]peer.PeerInfo, error) { +func (c *offlineRouting) FindProviders(ctx context.Context, key key.Key) ([]pstore.PeerInfo, error) { return nil, ErrOffline } -func (c *offlineRouting) FindPeer(ctx context.Context, pid peer.ID) (peer.PeerInfo, error) { - return peer.PeerInfo{}, ErrOffline +func (c *offlineRouting) FindPeer(ctx context.Context, pid peer.ID) (pstore.PeerInfo, error) { + return pstore.PeerInfo{}, ErrOffline } -func (c *offlineRouting) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan peer.PeerInfo { - out := make(chan peer.PeerInfo) +func (c *offlineRouting) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan pstore.PeerInfo { + out := make(chan pstore.PeerInfo) close(out) return out } diff --git a/routing/routing.go b/routing/routing.go index bedcabd53..b2f2a1f71 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -5,9 +5,10 @@ import ( "errors" key "github.com/ipfs/go-ipfs/blocks/key" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // ErrNotFound is returned when a search fails to find anything @@ -16,7 +17,7 @@ var ErrNotFound = errors.New("routing: not found") // IpfsRouting is the routing module interface // It is implemented by things like DHTs, etc. type IpfsRouting interface { - FindProvidersAsync(context.Context, key.Key, int) <-chan peer.PeerInfo + FindProvidersAsync(context.Context, key.Key, int) <-chan pstore.PeerInfo // Basic Put/Get @@ -45,9 +46,9 @@ type IpfsRouting interface { Provide(context.Context, key.Key) error // Find specific Peer - // FindPeer searches for a peer with given ID, returns a peer.PeerInfo + // FindPeer searches for a peer with given ID, returns a pstore.PeerInfo // with relevant addresses. - FindPeer(context.Context, peer.ID) (peer.PeerInfo, error) + FindPeer(context.Context, peer.ID) (pstore.PeerInfo, error) // Bootstrap allows callers to hint to the routing system to get into a // Boostrapped state diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 2a7131961..060d77733 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -5,31 +5,31 @@ import ( "errors" "time" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - - "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/host" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" - key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" loggables "github.com/ipfs/go-ipfs/thirdparty/loggables" + + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/host" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("supernode") type Client struct { peerhost host.Host - peerstore peer.Peerstore + peerstore pstore.Peerstore proxy proxy.Proxy local peer.ID } // TODO take in datastore/cache -func NewClient(px proxy.Proxy, h host.Host, ps peer.Peerstore, local peer.ID) (*Client, error) { +func NewClient(px proxy.Proxy, h host.Host, ps pstore.Peerstore, local peer.ID) (*Client, error) { return &Client{ proxy: px, local: local, @@ -38,10 +38,10 @@ func NewClient(px proxy.Proxy, h host.Host, ps peer.Peerstore, local peer.ID) (* }, nil } -func (c *Client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan peer.PeerInfo { +func (c *Client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan pstore.PeerInfo { logging.ContextWithLoggable(ctx, loggables.Uuid("findProviders")) defer log.EventBegin(ctx, "findProviders", &k).Done() - ch := make(chan peer.PeerInfo) + ch := make(chan pstore.PeerInfo) go func() { defer close(ch) request := pb.NewMessage(pb.Message_GET_PROVIDERS, string(k), 0) @@ -105,7 +105,7 @@ func (c *Client) Provide(ctx context.Context, k key.Key) error { // FIXME how is connectedness defined for the local node pri := []pb.PeerRoutingInfo{ { - PeerInfo: peer.PeerInfo{ + PeerInfo: pstore.PeerInfo{ ID: c.local, Addrs: c.peerhost.Addrs(), }, @@ -115,23 +115,23 @@ func (c *Client) Provide(ctx context.Context, k key.Key) error { return c.proxy.SendMessage(ctx, msg) // TODO wrap to hide remote } -func (c *Client) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, error) { +func (c *Client) FindPeer(ctx context.Context, id peer.ID) (pstore.PeerInfo, error) { defer log.EventBegin(ctx, "findPeer", id).Done() request := pb.NewMessage(pb.Message_FIND_NODE, string(id), 0) response, err := c.proxy.SendRequest(ctx, request) // hide remote if err != nil { - return peer.PeerInfo{}, err + return pstore.PeerInfo{}, err } for _, p := range pb.PBPeersToPeerInfos(response.GetCloserPeers()) { if p.ID == id { return p, nil } } - return peer.PeerInfo{}, errors.New("could not find peer") + return pstore.PeerInfo{}, errors.New("could not find peer") } // creates and signs a record for the given key/value pair -func makeRecord(ps peer.Peerstore, p peer.ID, k key.Key, v []byte) (*pb.Record, error) { +func makeRecord(ps pstore.Peerstore, p peer.ID, k key.Key, v []byte) (*pb.Record, error) { blob := bytes.Join([][]byte{[]byte(k), v, []byte(p)}, []byte{}) sig, err := ps.PrivKey(p).Sign(blob) if err != nil { diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 88041a1af..91efb6001 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + inet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 7e3ea1ee8..4fbf4d1be 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -6,10 +6,11 @@ import ( ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - host "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/host" - inet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + host "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/host" + inet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" @@ -31,11 +32,11 @@ type Proxy interface { type standard struct { Host host.Host - remoteInfos []peer.PeerInfo // addr required for bootstrapping - remoteIDs []peer.ID // []ID is required for each req. here, cached for performance. + remoteInfos []pstore.PeerInfo // addr required for bootstrapping + remoteIDs []peer.ID // []ID is required for each req. here, cached for performance. } -func Standard(h host.Host, remotes []peer.PeerInfo) Proxy { +func Standard(h host.Host, remotes []pstore.PeerInfo) Proxy { var ids []peer.ID for _, remote := range remotes { ids = append(ids, remote.ID) @@ -44,7 +45,7 @@ func Standard(h host.Host, remotes []peer.PeerInfo) Proxy { } func (px *standard) Bootstrap(ctx context.Context) error { - var cxns []peer.PeerInfo + var cxns []pstore.PeerInfo for _, info := range px.remoteInfos { if err := px.Host.Connect(ctx, info); err != nil { continue @@ -97,7 +98,7 @@ func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote pe } e.Done() }() - if err = px.Host.Connect(ctx, peer.PeerInfo{ID: remote}); err != nil { + if err = px.Host.Connect(ctx, pstore.PeerInfo{ID: remote}); err != nil { return err } s, err := px.Host.NewStream(ctx, ProtocolSNR, remote) @@ -131,7 +132,7 @@ func (px *standard) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.M func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote peer.ID) (*dhtpb.Message, error) { e := log.EventBegin(ctx, "sendRoutingRequest", px.Host.ID(), remote, logging.Pair("request", m)) defer e.Done() - if err := px.Host.Connect(ctx, peer.PeerInfo{ID: remote}); err != nil { + if err := px.Host.Connect(ctx, pstore.PeerInfo{ID: remote}); err != nil { e.SetError(err) return nil, err } diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 64b0ef87b..850e96d64 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -5,26 +5,27 @@ import ( "fmt" datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" + + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) // Server handles routing queries using a database backend type Server struct { local peer.ID routingBackend datastore.Datastore - peerstore peer.Peerstore + peerstore pstore.Peerstore *proxy.Loopback // so server can be injected into client } // NewServer creates a new Supernode routing Server -func NewServer(ds datastore.Datastore, ps peer.Peerstore, local peer.ID) (*Server, error) { +func NewServer(ds datastore.Datastore, ps pstore.Peerstore, local peer.ID) (*Server, error) { s := &Server{local, ds, ps, nil} s.Loopback = &proxy.Loopback{ Handler: s, @@ -169,7 +170,7 @@ func putRoutingProviders(ds datastore.Datastore, k key.Key, newRecords []*dhtpb. return ds.Put(providerKey(k), data) } -func storeProvidersToPeerstore(ps peer.Peerstore, p peer.ID, providers []*dhtpb.Message_Peer) { +func storeProvidersToPeerstore(ps pstore.Peerstore, p peer.ID, providers []*dhtpb.Message_Peer) { for _, provider := range providers { providerID := peer.ID(provider.GetId()) if providerID != p { @@ -178,7 +179,7 @@ func storeProvidersToPeerstore(ps peer.Peerstore, p peer.ID, providers []*dhtpb. } for _, maddr := range provider.Addresses() { // as a router, we want to store addresses for peers who have provided - ps.AddAddr(p, maddr, peer.AddressTTL) + ps.AddAddr(p, maddr, pstore.AddressTTL) } } } @@ -203,7 +204,7 @@ func providerKey(k key.Key) datastore.Key { return datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) } -func verify(ps peer.Peerstore, r *dhtpb.Record) error { +func verify(ps pstore.Peerstore, r *dhtpb.Record) error { v := make(record.Validator) v["pk"] = record.PublicKeyValidator p := peer.ID(r.GetAuthor()) From 4636edbb8ddc74fd27c665b7eb4c5fd3fa80c3db Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 1 Jun 2016 15:51:39 -0700 Subject: [PATCH 1287/3817] update libp2p to v3.3.1 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@076a13dbfe63a987e020e8cd7406bea6445d9fea --- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 7 ++++--- namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 5fdbb725a..7c3b4b95c 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -19,9 +19,9 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 3071b9bb8..ef5baef04 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,12 +11,13 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) @@ -32,7 +33,7 @@ const DefaultRecordLifetime = time.Hour * 24 type Republisher struct { r routing.IpfsRouting ds ds.Datastore - ps peer.Peerstore + ps pstore.Peerstore Interval time.Duration @@ -43,7 +44,7 @@ type Republisher struct { entries map[peer.ID]struct{} } -func NewRepublisher(r routing.IpfsRouting, ds ds.Datastore, ps peer.Peerstore) *Republisher { +func NewRepublisher(r routing.IpfsRouting, ds ds.Datastore, ps pstore.Peerstore) *Republisher { return &Republisher{ r: r, ps: ps, diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 44c362256..d9bf19674 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmRW2xiYTpDLWTHb822ZYbPBoh3dGLJwaXLGS9tnPyWZpq/go-libp2p/p2p/net/mock" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" + mocknet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { @@ -44,7 +44,7 @@ func TestRepublish(t *testing.T) { mn.LinkAll() bsinf := core.BootstrapConfigWithPeers( - []peer.PeerInfo{ + []pstore.PeerInfo{ nodes[0].Peerstore.PeerInfo(nodes[0].Identity), }, ) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 95060e903..1025b5f80 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -11,9 +11,9 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" + peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmbyvM8zRFDkbFdYyt1MnevUMJ62SiSGbfDFZ3Z8nkrzr4/go-libp2p-peer" ) func TestRoutingResolve(t *testing.T) { From 2d9b4bd47c8169f68ec1bcb965a7fb50a86c8d0a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 1 Jun 2016 15:51:39 -0700 Subject: [PATCH 1288/3817] update libp2p to v3.3.1 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@f153bc4dabdab5117f9c7e9311842c69fcb4f8ad --- pinning/pinner/pin.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 0696e7984..d68f6b16a 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -45,11 +45,11 @@ const ( func PinModeToString(mode PinMode) (string, bool) { m := map[PinMode]string{ Recursive: linkRecursive, - Direct: linkDirect, - Indirect: linkIndirect, - Internal: linkInternal, + Direct: linkDirect, + Indirect: linkIndirect, + Internal: linkInternal, NotPinned: linkNotPinned, - Any: linkAny, + Any: linkAny, } s, ok := m[mode] return s, ok @@ -58,12 +58,12 @@ func PinModeToString(mode PinMode) (string, bool) { func StringToPinMode(s string) (PinMode, bool) { m := map[string]PinMode{ linkRecursive: Recursive, - linkDirect: Direct, - linkIndirect: Indirect, - linkInternal: Internal, + linkDirect: Direct, + linkIndirect: Indirect, + linkInternal: Internal, linkNotPinned: NotPinned, - linkAny: Any, - linkAll: Any, // "all" and "any" means the same thing + linkAny: Any, + linkAll: Any, // "all" and "any" means the same thing } mode, ok := m[s] return mode, ok From c40aa7fb8d5142c439efebe9cb2841262360335b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Jun 2016 12:12:43 -0700 Subject: [PATCH 1289/3817] rework add-mfs to not use caching License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@4d9ddb1d25546ad5edf4d903dd459e58a1df5a83 --- mfs/dir.go | 14 +------------- mfs/file.go | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index ba14464ae..fba61ea4c 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -131,7 +131,7 @@ func (d *Directory) cacheNode(name string, nd *dag.Node) (FSNode, error) { ndir := NewDirectory(d.ctx, name, nd, d, d.dserv) d.childDirs[name] = ndir return ndir, nil - case ufspb.Data_File, ufspb.Data_Raw: + case ufspb.Data_File, ufspb.Data_Raw, ufspb.Data_Symlink: nfi, err := NewFile(name, nd, d, d.dserv) if err != nil { return nil, err @@ -338,18 +338,6 @@ func (d *Directory) AddChild(name string, nd *dag.Node) error { } d.modTime = time.Now() - - if len(nd.Links) == 0 { - nfi, err := NewFile(name, nd, d, d.dserv) - if err != nil { - return err - } - d.files[name] = nfi - } else { - ndir := NewDirectory(d.ctx, name, nd, d, d.dserv) - d.childDirs[name] = ndir - } - return nil } diff --git a/mfs/file.go b/mfs/file.go index 578da98f6..216bdfa75 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -45,6 +45,20 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { node := fi.node fi.nodelk.Unlock() + fsn, err := ft.FSNodeFromBytes(node.Data) + if err != nil { + return nil, err + } + + switch fsn.Type { + default: + return nil, fmt.Errorf("unsupported fsnode type for 'file'") + case ft.TSymlink: + return nil, fmt.Errorf("symlinks not yet supported") + case ft.TFile, ft.TRaw: + // OK case + } + switch flags { case OpenReadOnly: fi.desclock.RLock() From 586e128b3a14d0524cf21ad208b3f8f23569e2c4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Jun 2016 12:12:43 -0700 Subject: [PATCH 1290/3817] rework add-mfs to not use caching License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@d561f32b6192a56c2a1adb0e75e7500486a01579 --- unixfs/format.go | 1 + 1 file changed, 1 insertion(+) diff --git a/unixfs/format.go b/unixfs/format.go index 6acb41050..f279a8843 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -15,6 +15,7 @@ const ( TFile = pb.Data_File TDirectory = pb.Data_Directory TMetadata = pb.Data_Metadata + TSymlink = pb.Data_Symlink ) var ErrMalformedFileFormat = errors.New("malformed data in file format") From df6b2dc4f573ff7e78f8cf4f2d52136b791518c3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 3 Jun 2016 13:52:28 -0700 Subject: [PATCH 1291/3817] fix cleanup of empty provider sets License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@132e16ab925d0f63c4cde1960e6c89ea560c5a0c --- routing/dht/providers.go | 19 ++++++++++++++---- routing/dht/providers_test.go | 38 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/routing/dht/providers.go b/routing/dht/providers.go index a394123c8..09e263461 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -11,6 +11,9 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) +var ProvideValidity = time.Hour * 24 +var defaultCleanupInterval = time.Hour + type ProviderManager struct { // all non channel fields are meant to be accessed only within // the run method @@ -23,6 +26,8 @@ type ProviderManager struct { getprovs chan *getProv period time.Duration proc goprocess.Process + + cleanupInterval time.Duration } type providerSet struct { @@ -48,13 +53,14 @@ func NewProviderManager(ctx context.Context, local peer.ID) *ProviderManager { pm.getlocal = make(chan chan []key.Key) pm.local = make(map[key.Key]struct{}) pm.proc = goprocessctx.WithContext(ctx) + pm.cleanupInterval = defaultCleanupInterval pm.proc.Go(func(p goprocess.Process) { pm.run() }) return pm } func (pm *ProviderManager) run() { - tick := time.NewTicker(time.Hour) + tick := time.NewTicker(pm.cleanupInterval) for { select { case np := <-pm.newprovs: @@ -85,16 +91,21 @@ func (pm *ProviderManager) run() { lc <- keys case <-tick.C: - for _, provs := range pm.providers { + for k, provs := range pm.providers { var filtered []peer.ID for p, t := range provs.set { - if time.Now().Sub(t) > time.Hour*24 { + if time.Now().Sub(t) > ProvideValidity { delete(provs.set, p) } else { filtered = append(filtered, p) } } - provs.providers = filtered + + if len(filtered) > 0 { + provs.providers = filtered + } else { + delete(pm.providers, k) + } } case <-pm.proc.Closing(): diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 8d25cfeba..9fa9d4b3a 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -2,6 +2,7 @@ package dht import ( "testing" + "time" key "github.com/ipfs/go-ipfs/blocks/key" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" @@ -21,3 +22,40 @@ func TestProviderManager(t *testing.T) { } p.proc.Close() } + +func TestProvidesExpire(t *testing.T) { + ProvideValidity = time.Second + defaultCleanupInterval = time.Second + + ctx := context.Background() + mid := peer.ID("testing") + p := NewProviderManager(ctx, mid) + + peers := []peer.ID{"a", "b"} + var keys []key.Key + for i := 0; i < 10; i++ { + k := key.Key(i) + keys = append(keys, k) + p.AddProvider(ctx, k, peers[0]) + p.AddProvider(ctx, k, peers[1]) + } + + for i := 0; i < 10; i++ { + out := p.GetProviders(ctx, keys[i]) + if len(out) != 2 { + t.Fatal("expected providers to still be there") + } + } + + time.Sleep(time.Second * 3) + for i := 0; i < 10; i++ { + out := p.GetProviders(ctx, keys[i]) + if len(out) > 2 { + t.Fatal("expected providers to be cleaned up") + } + } + + if len(p.providers) != 0 { + t.Fatal("providers map not cleaned up") + } +} From 8db6cf3e4b739571b195646704b694d56a59c87b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 6 Jun 2016 17:35:56 -0700 Subject: [PATCH 1292/3817] reuse streams in the dht networking code License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@415a300f56d49ad5b5d5a14c3e973a67b61a015e --- routing/dht/dht.go | 4 + routing/dht/dht_net.go | 194 ++++++++++++++++++++++++++-------------- routing/dht/ext_test.go | 6 +- 3 files changed, 134 insertions(+), 70 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 6ddd4325e..1ef824598 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -58,6 +58,9 @@ type IpfsDHT struct { ctx context.Context proc goprocess.Process + + strmap map[peer.ID]*messageSender + smlk sync.Mutex } // NewDHT creates a new DHT object with the given peer as the 'local' host @@ -77,6 +80,7 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.Datastore) *IpfsDHT { return nil }) + dht.strmap = make(map[peer.ID]*messageSender) dht.ctx = ctx h.SetStreamHandler(ProtocolDHT, dht.handleNewStream) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 4ebd6e3f6..abafb5297 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -1,7 +1,7 @@ package dht import ( - "errors" + "sync" "time" pb "github.com/ipfs/go-ipfs/routing/dht/pb" @@ -27,40 +27,42 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { w := ggio.NewDelimitedWriter(cw) mPeer := s.Conn().RemotePeer() - // receive msg - pmes := new(pb.Message) - if err := r.ReadMsg(pmes); err != nil { - log.Debugf("Error unmarshaling data: %s", err) - return - } - - // update the peer (on valid msgs only) - dht.updateFromMessage(ctx, mPeer, pmes) - - // get handler for this msg type. - handler := dht.handlerForMsgType(pmes.GetType()) - if handler == nil { - log.Debug("got back nil handler from handlerForMsgType") - return - } - - // dispatch handler. - rpmes, err := handler(ctx, mPeer, pmes) - if err != nil { - log.Debugf("handle message error: %s", err) - return - } - - // if nil response, return it before serializing - if rpmes == nil { - log.Debug("Got back nil response from request.") - return - } - - // send out response msg - if err := w.WriteMsg(rpmes); err != nil { - log.Debugf("send response error: %s", err) - return + for { + // receive msg + pmes := new(pb.Message) + if err := r.ReadMsg(pmes); err != nil { + log.Debugf("Error unmarshaling data: %s", err) + return + } + + // update the peer (on valid msgs only) + dht.updateFromMessage(ctx, mPeer, pmes) + + // get handler for this msg type. + handler := dht.handlerForMsgType(pmes.GetType()) + if handler == nil { + log.Debug("got back nil handler from handlerForMsgType") + return + } + + // dispatch handler. + rpmes, err := handler(ctx, mPeer, pmes) + if err != nil { + log.Debugf("handle message error: %s", err) + return + } + + // if nil response, return it before serializing + if rpmes == nil { + log.Debug("Got back nil response from request.") + continue + } + + // send out response msg + if err := w.WriteMsg(rpmes); err != nil { + log.Debugf("send response error: %s", err) + return + } } return @@ -70,32 +72,14 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { // measure the RTT for latency measurements. func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { - log.Debugf("%s DHT starting stream", dht.self) - s, err := dht.host.NewStream(ctx, ProtocolDHT, p) - if err != nil { - return nil, err - } - defer s.Close() - - cr := ctxio.NewReader(ctx, s) // ok to use. we defer close stream in this func - cw := ctxio.NewWriter(ctx, s) // ok to use. we defer close stream in this func - r := ggio.NewDelimitedReader(cr, inet.MessageSizeMax) - w := ggio.NewDelimitedWriter(cw) + ms := dht.messageSenderForPeer(p) start := time.Now() - if err := w.WriteMsg(pmes); err != nil { - return nil, err - } - log.Event(ctx, "dhtSentMessage", dht.self, p, pmes) - - rpmes := new(pb.Message) - if err := r.ReadMsg(rpmes); err != nil { + rpmes, err := ms.SendRequest(ctx, pmes) + if err != nil { return nil, err } - if rpmes == nil { - return nil, errors.New("no response to request") - } // update the peer (on valid msgs only) dht.updateFromMessage(ctx, p, rpmes) @@ -108,17 +92,9 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message // sendMessage sends out a message func (dht *IpfsDHT) sendMessage(ctx context.Context, p peer.ID, pmes *pb.Message) error { - log.Debugf("%s DHT starting stream", dht.self) - s, err := dht.host.NewStream(ctx, ProtocolDHT, p) - if err != nil { - return err - } - defer s.Close() + ms := dht.messageSenderForPeer(p) - cw := ctxio.NewWriter(ctx, s) // ok to use. we defer close stream in this func - w := ggio.NewDelimitedWriter(cw) - - if err := w.WriteMsg(pmes); err != nil { + if err := ms.SendMessage(ctx, pmes); err != nil { return err } log.Event(ctx, "dhtSentMessage", dht.self, p, pmes) @@ -129,3 +105,89 @@ func (dht *IpfsDHT) updateFromMessage(ctx context.Context, p peer.ID, mes *pb.Me dht.Update(ctx, p) return nil } + +func (dht *IpfsDHT) messageSenderForPeer(p peer.ID) *messageSender { + dht.smlk.Lock() + defer dht.smlk.Unlock() + + ms, ok := dht.strmap[p] + if !ok { + ms = dht.newMessageSender(p) + dht.strmap[p] = ms + } + + return ms +} + +type messageSender struct { + s inet.Stream + r ggio.ReadCloser + w ggio.WriteCloser + lk sync.Mutex + p peer.ID + dht *IpfsDHT +} + +func (dht *IpfsDHT) newMessageSender(p peer.ID) *messageSender { + return &messageSender{p: p, dht: dht} +} + +func (ms *messageSender) prep() error { + if ms.s != nil { + return nil + } + + nstr, err := ms.dht.host.NewStream(ms.dht.ctx, ProtocolDHT, ms.p) + if err != nil { + return err + } + + ms.r = ggio.NewDelimitedReader(nstr, inet.MessageSizeMax) + ms.w = ggio.NewDelimitedWriter(nstr) + ms.s = nstr + + return nil +} + +func (ms *messageSender) SendMessage(ctx context.Context, pmes *pb.Message) error { + ms.lk.Lock() + defer ms.lk.Unlock() + if err := ms.prep(); err != nil { + return err + } + + err := ms.w.WriteMsg(pmes) + if err != nil { + ms.s.Close() + ms.s = nil + return err + } + return nil +} + +func (ms *messageSender) SendRequest(ctx context.Context, pmes *pb.Message) (*pb.Message, error) { + ms.lk.Lock() + defer ms.lk.Unlock() + if err := ms.prep(); err != nil { + return nil, err + } + + err := ms.w.WriteMsg(pmes) + if err != nil { + ms.s.Close() + ms.s = nil + return nil, err + } + + log.Event(ctx, "dhtSentMessage", ms.dht.self, ms.p, pmes) + + mes := new(pb.Message) + err = ms.r.ReadMsg(mes) + if err != nil { + ms.s.Close() + ms.s = nil + return nil, err + } + + return mes, nil +} diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 28868a992..b5fa640d8 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -2,7 +2,6 @@ package dht import ( "io" - "io/ioutil" "math/rand" "testing" "time" @@ -40,8 +39,7 @@ func TestGetFailures(t *testing.T) { // Reply with failures to every message hosts[1].SetStreamHandler(ProtocolDHT, func(s inet.Stream) { - defer s.Close() - io.Copy(ioutil.Discard, s) + s.Close() }) // This one should time out @@ -51,7 +49,7 @@ func TestGetFailures(t *testing.T) { err = merr[0] } - if err.Error() != "process closing" { + if err != io.EOF { t.Fatal("Got different error than we expected", err) } } else { From 7c1c2254fe6efb89cf294b678e0d32a8c0952dee Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 6 Jun 2016 23:28:39 -0700 Subject: [PATCH 1293/3817] cleanup stream reuse License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@9288e70c73aaeb01b695f657f36b863305508663 --- routing/dht/dht_net.go | 45 +++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index abafb5297..9d127425b 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -126,6 +126,8 @@ type messageSender struct { lk sync.Mutex p peer.ID dht *IpfsDHT + + singleMes int } func (dht *IpfsDHT) newMessageSender(p peer.ID) *messageSender { @@ -156,11 +158,39 @@ func (ms *messageSender) SendMessage(ctx context.Context, pmes *pb.Message) erro return err } + if err := ms.writeMessage(pmes); err != nil { + return err + } + + if ms.singleMes > 3 { + ms.s.Close() + ms.s = nil + } + + return nil +} + +func (ms *messageSender) writeMessage(pmes *pb.Message) error { err := ms.w.WriteMsg(pmes) if err != nil { + // If the other side isnt expecting us to be reusing streams, we're gonna + // end up erroring here. To make sure things work seamlessly, lets retry once + // before continuing + + log.Infof("error writing message: ", err) ms.s.Close() ms.s = nil - return err + if err := ms.prep(); err != nil { + return err + } + + if err := ms.w.WriteMsg(pmes); err != nil { + return err + } + + // keep track of this happening. If it happens a few times, its + // likely we can assume the otherside will never support stream reuse + ms.singleMes++ } return nil } @@ -172,22 +202,23 @@ func (ms *messageSender) SendRequest(ctx context.Context, pmes *pb.Message) (*pb return nil, err } - err := ms.w.WriteMsg(pmes) - if err != nil { - ms.s.Close() - ms.s = nil + if err := ms.writeMessage(pmes); err != nil { return nil, err } log.Event(ctx, "dhtSentMessage", ms.dht.self, ms.p, pmes) mes := new(pb.Message) - err = ms.r.ReadMsg(mes) - if err != nil { + if err := ms.r.ReadMsg(mes); err != nil { ms.s.Close() ms.s = nil return nil, err } + if ms.singleMes > 3 { + ms.s.Close() + ms.s = nil + } + return mes, nil } From 4fddd0d1ad2ceb4f09ae0a905958f10727118c62 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 7 Jun 2016 00:20:06 -0700 Subject: [PATCH 1294/3817] update libp2p to version 3.2.2 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@f41ad4ac079c048ccc5e87b91b06493d34942745 --- routing/dht/dht.go | 4 ++-- routing/dht/dht_net.go | 2 +- routing/dht/dht_test.go | 2 +- routing/dht/ext_test.go | 4 ++-- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/routing.go | 2 +- routing/mock/dht.go | 2 +- routing/none/none_client.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 4 ++-- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 6ddd4325e..b54d46983 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -17,11 +17,11 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - host "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/host" - protocol "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/protocol" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + host "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/host" + protocol "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/protocol" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 4ebd6e3f6..5016856c8 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,8 +6,8 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - inet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net" ctxio "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/io" + inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index f4bc863a6..86fccc65e 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -18,7 +18,7 @@ import ( travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - netutil "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/test/util" + netutil "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/test/util" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 28868a992..08981366e 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -14,8 +14,8 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - inet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net/mock" + inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net/mock" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" diff --git a/routing/dht/notif.go b/routing/dht/notif.go index b9d16819f..995d9177c 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - inet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net" + inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index b1ab8097b..bde199dfd 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -5,7 +5,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - inet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net" + inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index ca1018d97..641619397 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -14,7 +14,7 @@ import ( pset "github.com/ipfs/go-ipfs/thirdparty/peerset" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - inet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net" + inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 536f908e5..150235ba8 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,7 +5,7 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 1752f33c7..02c715e58 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -7,7 +7,7 @@ import ( repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - p2phost "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/host" + p2phost "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/host" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 060d77733..ee7fafa55 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -12,7 +12,7 @@ import ( loggables "github.com/ipfs/go-ipfs/thirdparty/loggables" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/host" + "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/host" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 91efb6001..8f49c585d 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -6,7 +6,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - inet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net" + inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 4fbf4d1be..9c8eb04fc 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -7,8 +7,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - host "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/host" - inet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net" + host "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/host" + inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" From eb6f99b1d49b6cff3e672e80923223991455f9b0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 7 Jun 2016 00:20:06 -0700 Subject: [PATCH 1295/3817] update libp2p to version 3.2.2 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@c3e05c6296d8bdee63ff5a115298b550868acbcf --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index d9bf19674..816c388d0 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,7 +13,7 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmQgQeBQxQmJdeUSaDagc8cr2ompDwGn13Cybjdtzfuaki/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" ) From da9e1d0f8064cef099cd49c0380ed0d393ca0645 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 7 Jun 2016 01:27:39 -0700 Subject: [PATCH 1296/3817] use constants for stream reuse heuristics License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@ff292b663d291198eae0d3569d696cf02000653e --- routing/dht/dht_net.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 9d127425b..7deac0a68 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -151,6 +151,11 @@ func (ms *messageSender) prep() error { return nil } +// streamReuseTries is the number of times we will try to reuse a stream to a +// given peer before giving up and reverting to the old one-message-per-stream +// behaviour. +const streamReuseTries = 3 + func (ms *messageSender) SendMessage(ctx context.Context, pmes *pb.Message) error { ms.lk.Lock() defer ms.lk.Unlock() @@ -162,7 +167,7 @@ func (ms *messageSender) SendMessage(ctx context.Context, pmes *pb.Message) erro return err } - if ms.singleMes > 3 { + if ms.singleMes > streamReuseTries { ms.s.Close() ms.s = nil } @@ -215,7 +220,7 @@ func (ms *messageSender) SendRequest(ctx context.Context, pmes *pb.Message) (*pb return nil, err } - if ms.singleMes > 3 { + if ms.singleMes > streamReuseTries { ms.s.Close() ms.s = nil } From 888d3aa4f3cba1d4b9a24af5a3426db2d2348cda Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 7 Jun 2016 01:50:08 -0700 Subject: [PATCH 1297/3817] clean up some dead code in the dht License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@3b4bbf97a3b74a01ef777a88464a58edeeb1309f --- routing/dht/dht.go | 10 --------- routing/dht/dht_logger.go | 46 --------------------------------------- routing/dht/diag.go | 44 ------------------------------------- 3 files changed, 100 deletions(-) delete mode 100644 routing/dht/dht_logger.go delete mode 100644 routing/dht/diag.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index b54d46983..ad5dcd97d 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -96,16 +96,6 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.Datastore) *IpfsDHT { return dht } -// LocalPeer returns the peer.Peer of the dht. -func (dht *IpfsDHT) LocalPeer() peer.ID { - return dht.self -} - -// log returns the dht's logger -func (dht *IpfsDHT) log() logging.EventLogger { - return log // TODO rm -} - // putValueToPeer stores the given key/value pair at the peer 'p' func (dht *IpfsDHT) putValueToPeer(ctx context.Context, p peer.ID, key key.Key, rec *pb.Record) error { diff --git a/routing/dht/dht_logger.go b/routing/dht/dht_logger.go deleted file mode 100644 index eea47ec1a..000000000 --- a/routing/dht/dht_logger.go +++ /dev/null @@ -1,46 +0,0 @@ -package dht - -import ( - "encoding/json" - "fmt" - "time" -) - -type logDhtRPC struct { - Type string - Start time.Time - End time.Time - Duration time.Duration - RPCCount int - Success bool -} - -func startNewRPC(name string) *logDhtRPC { - r := new(logDhtRPC) - r.Type = name - r.Start = time.Now() - return r -} - -func (l *logDhtRPC) EndLog() { - l.End = time.Now() - l.Duration = l.End.Sub(l.Start) -} - -func (l *logDhtRPC) Print() { - b, err := json.Marshal(l) - if err != nil { - log.Debugf("Error marshaling logDhtRPC object: %s", err) - } else { - log.Debug(string(b)) - } -} - -func (l *logDhtRPC) String() string { - return fmt.Sprintf("DHT RPC: %s took %s, success = %v", l.Type, l.Duration, l.Success) -} - -func (l *logDhtRPC) EndAndPrint() { - l.EndLog() - l.Print() -} diff --git a/routing/dht/diag.go b/routing/dht/diag.go deleted file mode 100644 index 7958a2783..000000000 --- a/routing/dht/diag.go +++ /dev/null @@ -1,44 +0,0 @@ -package dht - -import ( - "encoding/json" - "time" - - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" -) - -type connDiagInfo struct { - Latency time.Duration - ID peer.ID -} - -type diagInfo struct { - ID peer.ID - Connections []connDiagInfo - Keys []string - LifeSpan time.Duration - CodeVersion string -} - -func (di *diagInfo) Marshal() []byte { - b, err := json.Marshal(di) - if err != nil { - panic(err) - } - //TODO: also consider compressing this. There will be a lot of these - return b -} - -func (dht *IpfsDHT) getDiagInfo() *diagInfo { - di := new(diagInfo) - di.CodeVersion = "github.com/ipfs/go-ipfs" - di.ID = dht.self - di.LifeSpan = time.Since(dht.birth) - di.Keys = nil // Currently no way to query datastore - - for _, p := range dht.routingTable.ListPeers() { - d := connDiagInfo{dht.peerstore.LatencyEWMA(p), p} - di.Connections = append(di.Connections, d) - } - return di -} From 7fba18a85d90aab37b6cdc638d11fda715e9a800 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 9 Jun 2016 22:12:52 +0200 Subject: [PATCH 1298/3817] Update go-log https://github.com/ipfs/go-log/pull/3 License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-routing@0c91bb2c06a621d437a786928b76fb8b8e2a3512 --- routing/dht/dht.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/query.go | 2 +- routing/kbucket/table.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/none/none_client.go | 2 +- routing/offline/offline.go | 2 +- routing/record/record.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/standard.go | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 6ece6dbae..16012d12d 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -22,10 +22,10 @@ import ( ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" host "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/host" protocol "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/protocol" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("dht") diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index bde199dfd..4d60c7d02 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -6,8 +6,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/query.go b/routing/dht/query.go index 6b7cc0c04..ab13b95da 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -12,11 +12,11 @@ import ( peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" ctxproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" queue "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore/queue" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var maxQueryConcurrency = AlphaValue diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index fc9c09672..2d57c5659 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -8,8 +8,8 @@ import ( "time" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("table") diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index cc4bc2d0f..88b12b87c 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -11,12 +11,12 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("mockrouter") diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 02c715e58..1e9a6f3f3 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -8,9 +8,9 @@ import ( routing "github.com/ipfs/go-ipfs/routing" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" p2phost "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/host" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 6175f9111..b1baf5cd6 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -12,10 +12,10 @@ import ( "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("offlinerouting") diff --git a/routing/record/record.go b/routing/record/record.go index 41071e6c0..83a197423 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" ) var log = logging.Logger("routing/record") diff --git a/routing/supernode/client.go b/routing/supernode/client.go index ee7fafa55..e04ae8721 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -13,10 +13,10 @@ import ( peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/host" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 9c8eb04fc..fee61d00d 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -9,8 +9,8 @@ import ( peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" host "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/host" inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" From de129a2ad696653e555ebc863c4f7977cda23a76 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 9 Jun 2016 22:12:52 +0200 Subject: [PATCH 1299/3817] Update go-log https://github.com/ipfs/go-log/pull/3 License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@7f6705f7cbb9fc0d06288dedf33b2b23fbe95347 --- namesys/republisher/repub.go | 2 +- namesys/routing.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index ef5baef04..f0d141181 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -16,10 +16,10 @@ import ( peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/routing.go b/namesys/routing.go index 174e3506b..053e97440 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -15,8 +15,8 @@ import ( path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("namesys") From cff9e32d6cfb3faee68f85ddaaf043829a26acb1 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 9 Jun 2016 22:12:52 +0200 Subject: [PATCH 1300/3817] Update go-log https://github.com/ipfs/go-log/pull/3 License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@67ac5e9398b34c1c8e3a83d5ee565d20de34f2ab --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index d3a9b1aa1..3ca3c44e3 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -13,8 +13,8 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("blockstore") From 6ab377f8764ae98fd008afbff9734e172958817d Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 9 Jun 2016 22:12:52 +0200 Subject: [PATCH 1301/3817] Update go-log https://github.com/ipfs/go-log/pull/3 License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-merkledag@00c67f6e5a483de42c8881e22f02f7408eb2acce --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index b98fdafe6..6792e3e51 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -8,8 +8,8 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("merkledag") From 21b445d469bf27f3ec595ccdd51d663aeb9e000c Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 9 Jun 2016 22:12:52 +0200 Subject: [PATCH 1302/3817] Update go-log https://github.com/ipfs/go-log/pull/3 License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-mfs@cfdebf6b5bf6a9d7c3dc9d2d699050b7f3e9ee20 --- mfs/system.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/system.go b/mfs/system.go index 19f90a40d..b97b1c594 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -18,8 +18,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var ErrNotExist = errors.New("no such rootfs") From 66b9c1fcb67962bfd82c65867ed0ada473a2c1c0 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 9 Jun 2016 22:12:52 +0200 Subject: [PATCH 1303/3817] Update go-log https://github.com/ipfs/go-log/pull/3 License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@89d04b1aa4ccf4412fa043bde3074545b8183e50 --- unixfs/mod/dagmodifier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index ba266f396..91ceef956 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -17,7 +17,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" ) var ErrSeekFail = errors.New("failed to seek properly") From 910e759ac0773b762576b3419f761762004a5e85 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 9 Jun 2016 22:12:52 +0200 Subject: [PATCH 1304/3817] Update go-log https://github.com/ipfs/go-log/pull/3 License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-blockservice@b4adcd1b0c16a77e029d188d817c98574cdfce5d --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 945f60ae6..b80f8cd17 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -10,8 +10,8 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("blockservice") From 1d878e1abd8aaeac2993c0f3e17618d09c9372b2 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 9 Jun 2016 22:12:52 +0200 Subject: [PATCH 1305/3817] Update go-log https://github.com/ipfs/go-log/pull/3 License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-pinner@16d5517230d78efc00a481b36456cd58dcf60521 --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 425fe1383..1a043c817 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index d68f6b16a..8bfcddebe 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -11,8 +11,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" ) var log = logging.Logger("pin") From a3f5275a13358d7463179baab94c833a5f199c8d Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 9 Jun 2016 22:12:52 +0200 Subject: [PATCH 1306/3817] Update go-log https://github.com/ipfs/go-log/pull/3 License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-path@bd593846ce5157df0b4ec160d8a9492ed8a5da90 --- path/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/resolver.go b/path/resolver.go index fb95b4f52..af4d215ef 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -11,7 +11,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" merkledag "github.com/ipfs/go-ipfs/merkledag" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" ) var log = logging.Logger("path") From 567a12345c1a84658339041256622be5fb6c8aa5 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 9 Jun 2016 22:12:52 +0200 Subject: [PATCH 1307/3817] Update go-log https://github.com/ipfs/go-log/pull/3 License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-chunker@4b3a59d013596e82dd3945cefc1a9b5a39d2c033 --- chunker/splitting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 6b82a8c87..6457de810 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -4,7 +4,7 @@ package chunk import ( "io" - logging "gx/ipfs/QmaDNZ4QMdBdku1YZWBysufYyoQt1negQGNav6PLYarbY8/go-log" + logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" ) var log = logging.Logger("chunk") From 6a46edf5a62ebe15044dea53944cdfe9ae6f7cd1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 8 Jun 2016 16:12:06 -0700 Subject: [PATCH 1308/3817] respect contexts while reading messages in dht License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@98050270bf57fbcb88fd9deda25c0358048fce9d --- routing/dht/dht_net.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 0152dab4a..8ad4286ce 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -214,7 +214,7 @@ func (ms *messageSender) SendRequest(ctx context.Context, pmes *pb.Message) (*pb log.Event(ctx, "dhtSentMessage", ms.dht.self, ms.p, pmes) mes := new(pb.Message) - if err := ms.r.ReadMsg(mes); err != nil { + if err := ms.ctxReadMsg(ctx, mes); err != nil { ms.s.Close() ms.s = nil return nil, err @@ -227,3 +227,17 @@ func (ms *messageSender) SendRequest(ctx context.Context, pmes *pb.Message) (*pb return mes, nil } + +func (ms *messageSender) ctxReadMsg(ctx context.Context, mes *pb.Message) error { + errc := make(chan error, 1) + go func() { + errc <- ms.r.ReadMsg(mes) + }() + + select { + case err := <-errc: + return err + case <-ctx.Done(): + return ctx.Err() + } +} From 33715827de15f6e0b7eb7a2f31700c443ed9523f Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 10 Jun 2016 00:11:00 +0200 Subject: [PATCH 1309/3817] Add some sanity tests for the misdial failure License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-routing@92f236151b33c484f440837000f43f621df9dc9b --- routing/dht/dht_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 86fccc65e..da5e92433 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -56,10 +56,24 @@ func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []peer dhts := make([]*IpfsDHT, n) peers := make([]peer.ID, n) + sanityAddrsMap := make(map[string]struct{}) + sanityPeersMap := make(map[string]struct{}) + for i := 0; i < n; i++ { dhts[i] = setupDHT(ctx, t) peers[i] = dhts[i].self addrs[i] = dhts[i].peerstore.Addrs(dhts[i].self)[0] + + if _, lol := sanityAddrsMap[addrs[i].String()]; lol { + t.Fatal("While setting up DHTs address got dumplicated.") + } else { + sanityAddrsMap[addrs[i].String()] = struct{}{} + } + if _, lol := sanityPeersMap[peers[i].String()]; lol { + t.Fatal("While setting up DHTs peerid got dumplicated.") + } else { + sanityPeersMap[peers[i].String()] = struct{}{} + } } return addrs, peers, dhts From 5abf0d5e80c35b9f8164b271a08c003e88bd48b0 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 10 Jun 2016 00:50:50 +0200 Subject: [PATCH 1310/3817] Fix typo in test License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-routing@7d7c71264565c5f58aad5eac32f7f8e1e3a64b37 --- routing/dht/dht_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index da5e92433..0be4c9080 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -65,12 +65,12 @@ func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []peer addrs[i] = dhts[i].peerstore.Addrs(dhts[i].self)[0] if _, lol := sanityAddrsMap[addrs[i].String()]; lol { - t.Fatal("While setting up DHTs address got dumplicated.") + t.Fatal("While setting up DHTs address got duplicated.") } else { sanityAddrsMap[addrs[i].String()] = struct{}{} } if _, lol := sanityPeersMap[peers[i].String()]; lol { - t.Fatal("While setting up DHTs peerid got dumplicated.") + t.Fatal("While setting up DHTs peerid got duplicated.") } else { sanityPeersMap[peers[i].String()] = struct{}{} } From d154840c6f9f7c129442a02d7f86137ca0c21184 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 11 Jun 2016 10:33:44 -0700 Subject: [PATCH 1311/3817] pull in libp2p updates with utp fixes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@61d243a7f10164798cb8d163673c4f99a62af120 --- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index f0d141181..55007db0d 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -16,9 +16,9 @@ import ( peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 816c388d0..9a2d317f5 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net/mock" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" + mocknet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { From ed405e9aaf683fbf09ffc718016243a261a990ad Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 11 Jun 2016 10:33:44 -0700 Subject: [PATCH 1312/3817] pull in libp2p updates with utp fixes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@e9c78ad8b342e161e1fc83869127989ac1bcdafe --- routing/dht/dht.go | 6 +++--- routing/dht/dht_net.go | 2 +- routing/dht/dht_test.go | 4 ++-- routing/dht/ext_test.go | 6 +++--- routing/dht/handlers.go | 2 +- routing/dht/lookup.go | 2 +- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 4 ++-- routing/dht/query.go | 4 ++-- routing/dht/routing.go | 4 ++-- routing/kbucket/table.go | 2 +- routing/kbucket/table_test.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 2 +- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 2 +- routing/routing.go | 2 +- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 2 +- 24 files changed, 36 insertions(+), 36 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 16012d12d..f9d3963dc 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -17,14 +17,14 @@ import ( ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + host "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/host" + protocol "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/protocol" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - host "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/host" - protocol "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/protocol" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 8ad4286ce..c7d245341 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,8 +6,8 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + inet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net" ctxio "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/io" - inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 0be4c9080..8e682e952 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -18,9 +18,9 @@ import ( travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - netutil "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/test/util" + netutil "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/test/util" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 8288c9186..034d46c7c 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -13,10 +13,10 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net/mock" + inet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 59aae4423..529206d0d 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -11,8 +11,8 @@ import ( lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 2744fb5a7..daa125cf6 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -7,7 +7,7 @@ import ( pset "github.com/ipfs/go-ipfs/thirdparty/peerset" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 995d9177c..059291547 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" + inet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 4d60c7d02..923eaea62 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -5,9 +5,9 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" + inet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/query.go b/routing/dht/query.go index ab13b95da..9c8a2956d 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -12,9 +12,9 @@ import ( peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" ctxproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" + queue "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore/queue" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" - queue "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore/queue" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 641619397..9f56fdba1 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -14,8 +14,8 @@ import ( pset "github.com/ipfs/go-ipfs/thirdparty/peerset" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" + inet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 2d57c5659..ff77275d9 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -8,8 +8,8 @@ import ( "time" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" ) var log = logging.Logger("table") diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 271b798c8..67c2d199d 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -8,7 +8,7 @@ import ( tu "github.com/ipfs/go-ipfs/thirdparty/testutil" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" ) // Test basic features of the bucket struct diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 88b12b87c..419c430a8 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -11,10 +11,10 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index e36c3d1d9..7eff1fddf 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -11,7 +11,7 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 647a10042..66cbbabf1 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,7 +8,7 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 150235ba8..677859273 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,7 +5,7 @@ import ( sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 1d833858a..eda6b3e3c 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -11,7 +11,7 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 1e9a6f3f3..3bbf6df84 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -7,9 +7,9 @@ import ( repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - p2phost "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/host" + p2phost "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/host" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index b1baf5cd6..1755921ef 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -12,9 +12,9 @@ import ( "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/routing.go b/routing/routing.go index b2f2a1f71..a0f3ec555 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index e04ae8721..90029f534 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -12,10 +12,10 @@ import ( loggables "github.com/ipfs/go-ipfs/thirdparty/loggables" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/host" + "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/host" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 8f49c585d..a76a550ef 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -6,7 +6,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" + inet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index fee61d00d..9ce316b6e 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -7,10 +7,10 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - host "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/host" - inet "gx/ipfs/QmXJBB9U6e6ennAJPzk8E2rSaVGuHVR2jCxE9H9gPDtRrq/go-libp2p/p2p/net" + host "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/host" + inet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 850e96d64..6e2c705be 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -11,8 +11,8 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - pstore "gx/ipfs/QmZ62t46e9p7vMYqCmptwQC1RhRv5cpQ5cwoqYspedaXyq/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From a27bcde33a6e402779e23b9fc39f036a2f9242ea Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 11 Jun 2016 17:08:34 -0700 Subject: [PATCH 1313/3817] a few small changes to make the dht more efficient License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@267e180a7f4b52e5acf72410d9f4f956c427bdc5 --- routing/dht/dht.go | 28 ---------------------------- routing/dht/routing.go | 24 +++++++++++++++++++++++- routing/kbucket/table.go | 4 ++-- 3 files changed, 25 insertions(+), 31 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index f9d3963dc..4ab7b8f32 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -117,34 +117,6 @@ func (dht *IpfsDHT) putValueToPeer(ctx context.Context, p peer.ID, return nil } -// putProvider sends a message to peer 'p' saying that the local node -// can provide the value of 'key' -func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.ID, skey string) error { - - // add self as the provider - pi := pstore.PeerInfo{ - ID: dht.self, - Addrs: dht.host.Addrs(), - } - - // // only share WAN-friendly addresses ?? - // pi.Addrs = addrutil.WANShareableAddrs(pi.Addrs) - if len(pi.Addrs) < 1 { - // log.Infof("%s putProvider: %s for %s error: no wan-friendly addresses", dht.self, p, key.Key(key), pi.Addrs) - return fmt.Errorf("no known addresses for self. cannot put provider.") - } - - pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, skey, 0) - pmes.ProviderPeers = pb.RawPeerInfosToPBPeers([]pstore.PeerInfo{pi}) - err := dht.sendMessage(ctx, p, pmes) - if err != nil { - return err - } - - log.Debugf("%s putProvider: %s for %s (%s)", dht.self, p, key.Key(skey), pi.Addrs) - return nil -} - var errInvalidRecord = errors.New("received invalid record") // getValueOrPeers queries a particular peer p for the value for diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 9f56fdba1..7298490df 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -2,6 +2,7 @@ package dht import ( "bytes" + "fmt" "sync" "time" @@ -243,13 +244,18 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key key.Key) error { return err } + mes, err := dht.makeProvRecord(key) + if err != nil { + return err + } + wg := sync.WaitGroup{} for p := range peers { wg.Add(1) go func(p peer.ID) { defer wg.Done() log.Debugf("putProvider(%s, %s)", key, p) - err := dht.putProvider(ctx, p, string(key)) + err := dht.sendMessage(ctx, p, mes) if err != nil { log.Debug(err) } @@ -258,6 +264,22 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key key.Key) error { wg.Wait() return nil } +func (dht *IpfsDHT) makeProvRecord(skey key.Key) (*pb.Message, error) { + pi := pstore.PeerInfo{ + ID: dht.self, + Addrs: dht.host.Addrs(), + } + + // // only share WAN-friendly addresses ?? + // pi.Addrs = addrutil.WANShareableAddrs(pi.Addrs) + if len(pi.Addrs) < 1 { + return nil, fmt.Errorf("no known addresses for self. cannot put provider.") + } + + pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, string(skey), 0) + pmes.ProviderPeers = pb.RawPeerInfosToPBPeers([]pstore.PeerInfo{pi}) + return pmes, nil +} // FindProviders searches until the context expires. func (dht *IpfsDHT) FindProviders(ctx context.Context, key key.Key) ([]pstore.PeerInfo, error) { diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index ff77275d9..49a8b7447 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -48,11 +48,11 @@ func NewRoutingTable(bucketsize int, localID ID, latency time.Duration, m pstore // Update adds or moves the given peer to the front of its respective bucket // If a peer gets removed from a bucket, it is returned func (rt *RoutingTable) Update(p peer.ID) { - rt.tabLock.Lock() - defer rt.tabLock.Unlock() peerID := ConvertPeerID(p) cpl := commonPrefixLen(peerID, rt.local) + rt.tabLock.Lock() + defer rt.tabLock.Unlock() bucketID := cpl if bucketID >= len(rt.Buckets) { bucketID = len(rt.Buckets) - 1 From 189cac735e0c5302866eb55da285ee1b55c81dfa Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 11 Jun 2016 23:07:06 -0700 Subject: [PATCH 1314/3817] sort peers outside of locks License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@3ca92a833f3c24d8a46ae2d46ae4910792aadc05 --- routing/kbucket/table.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 49a8b7447..0dfdff457 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -144,10 +144,10 @@ func (rt *RoutingTable) NearestPeer(id ID) peer.ID { // NearestPeers returns a list of the 'count' closest peers to the given ID func (rt *RoutingTable) NearestPeers(id ID, count int) []peer.ID { - rt.tabLock.RLock() - defer rt.tabLock.RUnlock() cpl := commonPrefixLen(id, rt.local) + rt.tabLock.RLock() + // Get bucket at cpl index or last bucket var bucket *Bucket if cpl >= len(rt.Buckets) { @@ -170,6 +170,7 @@ func (rt *RoutingTable) NearestPeers(id ID, count int) []peer.ID { peerArr = copyPeersFromList(id, peerArr, plist) } } + rt.tabLock.RUnlock() // Sort by distance to local peer sort.Sort(peerArr) From 53925576230f3ab6e882bcaec99a2b4c16040045 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:11:56 +0200 Subject: [PATCH 1315/3817] Remove go-datastore from Godeps License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@fe781ab33e110e2e70eaac17a45cf5ff985a3254 --- blockstore/blockstore.go | 6 +++--- blockstore/blockstore_test.go | 6 +++--- blockstore/write_cache_test.go | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 3ca3c44e3..b3f3f323b 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -7,9 +7,9 @@ import ( "sync" "sync/atomic" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - dsns "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/namespace" - dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/query" + ds "github.com/ipfs/go-datastore" + dsns "github.com/ipfs/go-datastore/namespace" + dsq "github.com/ipfs/go-datastore/query" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 8b0609f1f..3a4a96bc6 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -5,9 +5,9 @@ import ( "fmt" "testing" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/query" - ds_sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ds "github.com/ipfs/go-datastore" + dsq "github.com/ipfs/go-datastore/query" + ds_sync "github.com/ipfs/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" diff --git a/blockstore/write_cache_test.go b/blockstore/write_cache_test.go index 97bf86b12..37ca8b624 100644 --- a/blockstore/write_cache_test.go +++ b/blockstore/write_cache_test.go @@ -3,9 +3,9 @@ package blockstore import ( "testing" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/query" - syncds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ds "github.com/ipfs/go-datastore" + dsq "github.com/ipfs/go-datastore/query" + syncds "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks" ) From 749a120722ed930f3dd1f6929b41cde170e8dd46 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:11:56 +0200 Subject: [PATCH 1316/3817] Remove go-datastore from Godeps License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@42e0aad20c891a52c6090f07a11ff7e77d0ca162 --- namesys/namesys.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/resolve_test.go | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 727aa71ce..bc24b5a11 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -4,7 +4,7 @@ import ( "strings" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + ds "github.com/ipfs/go-datastore" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" diff --git a/namesys/publisher.go b/namesys/publisher.go index 7c3b4b95c..1f50b5bbc 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -6,7 +6,7 @@ import ( "fmt" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + ds "github.com/ipfs/go-datastore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 55007db0d..b6a34d4a9 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -12,7 +12,7 @@ import ( "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + ds "github.com/ipfs/go-datastore" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 1025b5f80..da6fbe1a7 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" From 99efe1fff3d14d28a69e4f8012ca4fa2929a5721 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:11:56 +0200 Subject: [PATCH 1317/3817] Remove go-datastore from Godeps License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-routing@2cfd05409ff564bbe18f3458bcd2553ade916c77 --- routing/dht/dht.go | 2 +- routing/dht/dht_test.go | 4 ++-- routing/dht/ext_test.go | 4 ++-- routing/dht/handlers.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 4 ++-- routing/mock/dht.go | 4 ++-- routing/mock/interface.go | 2 +- routing/offline/offline.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index f9d3963dc..9795d8b8e 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -15,7 +15,7 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + ds "github.com/ipfs/go-datastore" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" host "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/host" protocol "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/protocol" diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 8e682e952..55c978c11 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 034d46c7c..ed1692816 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -6,8 +6,8 @@ import ( "testing" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 529206d0d..05a8ad5b2 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + ds "github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 419c430a8..9973b957a 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -4,7 +4,7 @@ import ( "errors" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + ds "github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 7eff1fddf..6ed8f2749 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -5,8 +5,8 @@ import ( "sync" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 677859273..5171cd160 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -1,8 +1,8 @@ package mockrouting import ( - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ds "github.com/ipfs/go-datastore" + sync "github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" mocknet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net/mock" diff --git a/routing/mock/interface.go b/routing/mock/interface.go index eda6b3e3c..fe23671eb 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -5,7 +5,7 @@ package mockrouting import ( - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + ds "github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 1755921ef..6c470d611 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -4,7 +4,7 @@ import ( "errors" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + ds "github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 6e2c705be..dd125342c 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + datastore "github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index ea3ead0c2..0da7e8aa8 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,7 +3,7 @@ package supernode import ( "testing" - datastore "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + datastore "github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" ) From 4739845d9767c2e9be30c338cebee4ae8d4158fa Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:11:56 +0200 Subject: [PATCH 1318/3817] Remove go-datastore from Godeps License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-merkledag@592a5281c71b99dd929b3e8b2d9ba0417b37cecf --- ipld/merkledag/merkledag_test.go | 4 ++-- ipld/merkledag/test/utils.go | 4 ++-- ipld/merkledag/utils/utils.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 430e31f7a..b5160eb67 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -10,8 +10,8 @@ import ( "sync" "testing" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index 1e96569ad..5bc5c6ef3 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -1,8 +1,8 @@ package mdutils import ( - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks/blockstore" bsrv "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 231397fe3..055543c7a 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -3,8 +3,8 @@ package dagutils import ( "errors" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - syncds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ds "github.com/ipfs/go-datastore" + syncds "github.com/ipfs/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" From 50deb1ba89855bea95bdfb51790ed2ba2ed93e94 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:11:56 +0200 Subject: [PATCH 1319/3817] Remove go-datastore from Godeps License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-mfs@664262c21c73471a6e79b1342ba424bc4f3184b8 --- mfs/mfs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 927a20f86..833bc39ee 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -13,8 +13,8 @@ import ( "testing" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/path" randbo "gx/ipfs/QmYvsG72GsfLgUeSojXArjnU6L4Wmwk7wuAxtNLuyXcc1T/randbo" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" From f7ffe8cb39348128afaef2035a786fe7f36df917 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:11:56 +0200 Subject: [PATCH 1320/3817] Remove go-datastore from Godeps License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@79c57de8fabbea73c4fb8b9f9bde75bcdebb7cc6 --- unixfs/mod/dagmodifier_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index fc3810f3f..d77b9ef73 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -7,7 +7,7 @@ import ( "os" "testing" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks/blockstore" bs "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" @@ -20,7 +20,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + ds "github.com/ipfs/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From b9ab883d8e9df9942d2d1d10e5578332ab1268e9 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:11:56 +0200 Subject: [PATCH 1321/3817] Remove go-datastore from Godeps License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-blockservice@c8d31d23576186a79eb1ffb40a65b380e6125ad9 --- blockservice/test/blocks_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index ed61dad59..7bbb60fcd 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" blocks "github.com/ipfs/go-ipfs/blocks" blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" blocksutil "github.com/ipfs/go-ipfs/blocks/blocksutil" From 495ee4176c463cfb88ced3afaafb90ecf7addb26 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:11:56 +0200 Subject: [PATCH 1322/3817] Remove go-datastore from Godeps License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-pinner@49ed743e7bdd9303893a21885f69761f09888f42 --- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 8bfcddebe..281cab766 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -7,7 +7,7 @@ import ( "sync" "time" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" + ds "github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index c8859660a..1a70b15c6 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -6,8 +6,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" bs "github.com/ipfs/go-ipfs/blockservice" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index b25f91a96..e67bb65bc 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -4,8 +4,8 @@ import ( "testing" "testing/quick" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blockservice" From a1fa03b83b7a3c5e0f99b80bc9c412b57a79aeff Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:11:56 +0200 Subject: [PATCH 1323/3817] Remove go-datastore from Godeps License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-exchange-offline@c322b28bd1002413cd22c25d0ee6243b61948d30 --- exchange/offline/offline_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index d7d17341e..6b66e1abb 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -3,8 +3,8 @@ package offline import ( "testing" - ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore" - ds_sync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync" + ds "github.com/ipfs/go-datastore" + ds_sync "github.com/ipfs/go-datastore/sync" blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" From 2cee0e0e61163d44bd3d4337e55f3e53b5e18c32 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:18:44 +0200 Subject: [PATCH 1324/3817] Import go-datastore to gx License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@f11d6a1fd23b0ad2bb2b0be946894c89f44857ce --- blockstore/blockstore.go | 6 +++--- blockstore/blockstore_test.go | 6 +++--- blockstore/write_cache_test.go | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index b3f3f323b..4d940fc06 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -7,13 +7,13 @@ import ( "sync" "sync/atomic" - ds "github.com/ipfs/go-datastore" - dsns "github.com/ipfs/go-datastore/namespace" - dsq "github.com/ipfs/go-datastore/query" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + dsns "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/namespace" + dsq "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/query" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 3a4a96bc6..2dc828ea2 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -5,9 +5,9 @@ import ( "fmt" "testing" - ds "github.com/ipfs/go-datastore" - dsq "github.com/ipfs/go-datastore/query" - ds_sync "github.com/ipfs/go-datastore/sync" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + dsq "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/query" + ds_sync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" diff --git a/blockstore/write_cache_test.go b/blockstore/write_cache_test.go index 37ca8b624..01c52ae40 100644 --- a/blockstore/write_cache_test.go +++ b/blockstore/write_cache_test.go @@ -3,10 +3,10 @@ package blockstore import ( "testing" - ds "github.com/ipfs/go-datastore" - dsq "github.com/ipfs/go-datastore/query" - syncds "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + dsq "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/query" + syncds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" ) func TestReturnsErrorWhenSizeNegative(t *testing.T) { From 61632560413662198db6dfe3d4143187a39d759e Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:18:44 +0200 Subject: [PATCH 1325/3817] Import go-datastore to gx License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@f4ce73df75ecd627f42596b18ffe261c3f165332 --- namesys/namesys.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/resolve_test.go | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index bc24b5a11..c7c68bfbd 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -4,10 +4,10 @@ import ( "strings" "time" - ds "github.com/ipfs/go-datastore" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 1f50b5bbc..53324d676 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -6,8 +6,8 @@ import ( "fmt" "time" - ds "github.com/ipfs/go-datastore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index b6a34d4a9..7156fc39e 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -12,13 +12,13 @@ import ( "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - ds "github.com/ipfs/go-datastore" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index da6fbe1a7..93198e502 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -5,13 +5,13 @@ import ( "testing" "time" - ds "github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From 9400e1b52f646f3cd7489c71592e1d42b07ee84d Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:18:44 +0200 Subject: [PATCH 1326/3817] Import go-datastore to gx License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-routing@48deaf3bafbf78b343bf7a58f7558c45da1f778f --- routing/dht/dht.go | 2 +- routing/dht/dht_test.go | 4 ++-- routing/dht/ext_test.go | 4 ++-- routing/dht/handlers.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 4 ++-- routing/mock/dht.go | 4 ++-- routing/mock/interface.go | 2 +- routing/offline/offline.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 9795d8b8e..cac05ba7b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -15,7 +15,6 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - ds "github.com/ipfs/go-datastore" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" host "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/host" protocol "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/protocol" @@ -25,6 +24,7 @@ import ( pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 55c978c11..fcac1a7b7 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -9,13 +9,13 @@ import ( "testing" "time" - ds "github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" netutil "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/test/util" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index ed1692816..efc5a8a75 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -6,12 +6,12 @@ import ( "testing" "time" - ds "github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" inet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net" mocknet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net/mock" diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 05a8ad5b2..7672ba0cd 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -5,10 +5,10 @@ import ( "fmt" "time" - ds "github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 9973b957a..57a86e400 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -4,11 +4,11 @@ import ( "errors" "time" - ds "github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" "github.com/ipfs/go-ipfs/thirdparty/testutil" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 6ed8f2749..742b3bac9 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -5,10 +5,10 @@ import ( "sync" "time" - ds "github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-datastore/sync" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 5171cd160..dd3e41f63 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -1,11 +1,11 @@ package mockrouting import ( - ds "github.com/ipfs/go-datastore" - sync "github.com/ipfs/go-datastore/sync" dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" mocknet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net/mock" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + sync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index fe23671eb..be28f4751 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -5,13 +5,13 @@ package mockrouting import ( - ds "github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 6c470d611..f3b3c55dc 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -4,11 +4,11 @@ import ( "errors" "time" - ds "github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index dd125342c..64d54a465 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -4,11 +4,11 @@ import ( "errors" "fmt" - datastore "github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" + datastore "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 0da7e8aa8..1d3ff54c0 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,9 +3,9 @@ package supernode import ( "testing" - datastore "github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" + datastore "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 5b359fd259a97e09cb074a5d07410797ff2f9738 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:18:44 +0200 Subject: [PATCH 1327/3817] Import go-datastore to gx License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-merkledag@97e48a9fd9feb38fd86dd0d8058a0fa38c45e1da --- ipld/merkledag/merkledag_test.go | 4 ++-- ipld/merkledag/test/utils.go | 4 ++-- ipld/merkledag/utils/utils.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index b5160eb67..a84c9d4e4 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -10,8 +10,6 @@ import ( "sync" "testing" - ds "github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-datastore/sync" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" @@ -23,6 +21,8 @@ import ( dstest "github.com/ipfs/go-ipfs/merkledag/test" "github.com/ipfs/go-ipfs/pin" uio "github.com/ipfs/go-ipfs/unixfs/io" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index 5bc5c6ef3..87680d120 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -1,12 +1,12 @@ package mdutils import ( - ds "github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks/blockstore" bsrv "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" ) func Mock() dag.DAGService { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 055543c7a..e268c9f7f 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -3,8 +3,8 @@ package dagutils import ( "errors" - ds "github.com/ipfs/go-datastore" - syncds "github.com/ipfs/go-datastore/sync" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + syncds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" From 2e71d750534b4ce94811358c90b0cb9c1357d758 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:18:44 +0200 Subject: [PATCH 1328/3817] Import go-datastore to gx License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-mfs@1237e96b6664c900da304e0d1b7c71b917e7c82a --- mfs/mfs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 833bc39ee..4c58b83f6 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -13,10 +13,10 @@ import ( "testing" "time" - ds "github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/path" randbo "gx/ipfs/QmYvsG72GsfLgUeSojXArjnU6L4Wmwk7wuAxtNLuyXcc1T/randbo" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" From 2d24226a7c46c62c1155dfc86793e0d3cef16ac9 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:18:44 +0200 Subject: [PATCH 1329/3817] Import go-datastore to gx License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@0a7b40de63387ca95f58a128b205c8669ffced9f --- unixfs/mod/dagmodifier_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index d77b9ef73..404a187ec 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -7,7 +7,6 @@ import ( "os" "testing" - "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks/blockstore" bs "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" @@ -18,9 +17,10 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" + "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - ds "github.com/ipfs/go-datastore" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From 11aefc999e184038e6663f5cce27f4e9a94a8798 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:18:44 +0200 Subject: [PATCH 1330/3817] Import go-datastore to gx License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-blockservice@6c0a36c04fec70cce0cc4c2e4619a02f6d6dab32 --- blockservice/test/blocks_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 7bbb60fcd..7c8e9ba96 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -5,14 +5,14 @@ import ( "testing" "time" - ds "github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-datastore/sync" blocks "github.com/ipfs/go-ipfs/blocks" blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" blocksutil "github.com/ipfs/go-ipfs/blocks/blocksutil" key "github.com/ipfs/go-ipfs/blocks/key" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From c085f4476f33e0ae7f4936f33862678148c904e5 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:18:44 +0200 Subject: [PATCH 1331/3817] Import go-datastore to gx License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-pinner@374a34778cd089263ad0f34dd8bed9804d0b7a29 --- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 281cab766..6cd0b80da 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -7,11 +7,11 @@ import ( "sync" "time" - ds "github.com/ipfs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 1a70b15c6..ecc1fc1f6 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -6,13 +6,13 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" bs "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index e67bb65bc..f1993e8ec 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -4,14 +4,14 @@ import ( "testing" "testing/quick" - "github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" "github.com/ipfs/go-ipfs/merkledag" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" + "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From 00b391a35801bf2f6972ada7ea523327045b88c2 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:18:44 +0200 Subject: [PATCH 1332/3817] Import go-datastore to gx License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-exchange-offline@44d69474541043a87e42021aabedf612a266d794 --- exchange/offline/offline_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 6b66e1abb..46cb4fb2a 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -3,12 +3,12 @@ package offline import ( "testing" - ds "github.com/ipfs/go-datastore" - ds_sync "github.com/ipfs/go-datastore/sync" blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" key "github.com/ipfs/go-ipfs/blocks/key" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + ds_sync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From fa19adbfbfc0e63990e2950b0bce4eb131f26808 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:26:57 +0200 Subject: [PATCH 1333/3817] Move golang-lru to gx License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@7f028d29e0768bb772a23c8ea4ba9f74b9db7162 --- blockstore/write_cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go index f7c2caf45..fbeee25ba 100644 --- a/blockstore/write_cache.go +++ b/blockstore/write_cache.go @@ -1,9 +1,9 @@ package blockstore import ( - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" + "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From 29fc7809c56aad8f0b997382bf967e8b6477ed7c Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 11 Jun 2016 16:26:57 +0200 Subject: [PATCH 1334/3817] Move golang-lru to gx License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@9a45ffb7b46c29ee6bb159df4c3b080f1cfa8439 --- namesys/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/routing.go b/namesys/routing.go index 053e97440..da08213e8 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,7 +5,7 @@ import ( "strings" "time" - lru "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/hashicorp/golang-lru" + lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" From 29eb505a3dd68e6e2e852c3ac10fcb01fef75d68 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 14 Jun 2016 13:32:48 +0200 Subject: [PATCH 1335/3817] Remove errors pointed out by govet License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-routing@9d996c95d27a0bb30228e056355dd2812fc72218 --- routing/dht/dht_net.go | 2 -- routing/dht/dht_test.go | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index c7d245341..f67474760 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -64,8 +64,6 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { return } } - - return } // sendRequest sends out a request, but also makes sure to diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index fcac1a7b7..b9eb5eb5a 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -214,7 +214,7 @@ func TestProvides(t *testing.T) { t.Fatal(err) } if !bytes.Equal(bits.GetValue(), v) { - t.Fatal("didn't store the right bits (%s, %s)", k, v) + t.Fatalf("didn't store the right bits (%s, %s)", k, v) } } @@ -289,7 +289,7 @@ func waitForWellFormedTables(t *testing.T, dhts []*IpfsDHT, minPeers, avgPeers i func printRoutingTables(dhts []*IpfsDHT) { // the routing tables should be full now. let's inspect them. - fmt.Println("checking routing table of %d", len(dhts)) + fmt.Printf("checking routing table of %d\n", len(dhts)) for _, dht := range dhts { fmt.Printf("checking routing table of %s\n", dht.self) dht.routingTable.Print() @@ -487,7 +487,7 @@ func TestProvidesMany(t *testing.T) { t.Fatal(err) } if !bytes.Equal(bits.GetValue(), v) { - t.Fatal("didn't store the right bits (%s, %s)", k, v) + t.Fatalf("didn't store the right bits (%s, %s)", k, v) } t.Logf("announcing provider for %s", k) From 11388b05ad1a3fe6b74f2c4a61e162620f6d57e5 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 14 Jun 2016 13:32:48 +0200 Subject: [PATCH 1336/3817] Remove errors pointed out by govet License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-merkledag@b6290d0081cf4f496b506ac1bf4a6961ec34baac --- ipld/merkledag/merkledag_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index a84c9d4e4..644d4e2d5 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -131,7 +131,7 @@ func SubtestNodeStat(t *testing.T, n *Node) { } if expected != *actual { - t.Error("n.Stat incorrect.\nexpect: %s\nactual: %s", expected, actual) + t.Errorf("n.Stat incorrect.\nexpect: %s\nactual: %s", expected, actual) } else { fmt.Printf("n.Stat correct: %s\n", actual) } From 1b294b9126dfdab0dda96841fbbb1bf6ebb3b643 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 14 Jun 2016 13:32:48 +0200 Subject: [PATCH 1337/3817] Remove errors pointed out by govet License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-mfs@3ddd719a9f939a354eec1e88c9cbe7184354292b --- mfs/ops.go | 2 +- mfs/system.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/ops.go b/mfs/ops.go index 950552f1b..7cf9ed9f3 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -162,7 +162,7 @@ func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { func Lookup(r *Root, path string) (FSNode, error) { dir, ok := r.GetValue().(*Directory) if !ok { - log.Error("root not a dir: %#v", r.GetValue()) + log.Errorf("root not a dir: %#v", r.GetValue()) return nil, errors.New("root was not a directory") } diff --git a/mfs/system.go b/mfs/system.go index b97b1c594..b0ee42e73 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -265,7 +265,7 @@ func (np *Republisher) Run() { pubnowresp <- struct{}{} } if err != nil { - log.Error("republishRoot error: %s", err) + log.Errorf("republishRoot error: %s", err) } case <-np.ctx.Done(): From ece10b3c60885f36f26f6c381a262f9ccef303a1 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 14 Jun 2016 13:32:48 +0200 Subject: [PATCH 1338/3817] Remove errors pointed out by govet License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@b646160d8b12d6cb24b07c629ecdc739f9063abb --- unixfs/io/dagreader.go | 1 - 1 file changed, 1 deletion(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 3c68ad896..b78b46269 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -279,7 +279,6 @@ func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { default: return 0, errors.New("invalid whence") } - return 0, nil } // readSeekNopCloser wraps a bytes.Reader to implement ReadSeekCloser From f77ee6e40b47306a0fe54b29f419c9b5a3a7ce56 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Tue, 14 Jun 2016 12:20:21 +0100 Subject: [PATCH 1339/3817] Decapitalized log.Debug messages According to golang standards, these should not be capitalized nor having a trailing period, AFAIK. License: MIT Signed-off-by: Richard Littauer This commit was moved from ipfs/go-ipfs-routing@be602328c25a641e0cd3a879258399c426e6a456 --- routing/dht/dht.go | 2 +- routing/dht/dht_net.go | 2 +- routing/dht/handlers.go | 2 +- routing/dht/records.go | 2 +- routing/dht/routing.go | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 0b049e575..53b825f4e 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -267,7 +267,7 @@ func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, p peer.ID, count int) [ // == to self? thats bad for _, p := range closer { if p == dht.self { - log.Debug("Attempted to return self! this shouldnt happen...") + log.Debug("attempted to return self! this shouldn't happen...") return nil } } diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index c7d245341..9a1b8f558 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -54,7 +54,7 @@ func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { // if nil response, return it before serializing if rpmes == nil { - log.Debug("Got back nil response from request.") + log.Debug("got back nil response from request") continue } diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 7672ba0cd..734393705 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -108,7 +108,7 @@ func (dht *IpfsDHT) checkLocalDatastore(k key.Key) (*pb.Record, error) { rec := new(pb.Record) err = proto.Unmarshal(byts, rec) if err != nil { - log.Debug("Failed to unmarshal DHT record from datastore.") + log.Debug("failed to unmarshal DHT record from datastore") return nil, err } diff --git a/routing/dht/records.go b/routing/dht/records.go index 477f8c6c0..a1fcc5b8c 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -84,7 +84,7 @@ func (dht *IpfsDHT) getPublicKeyFromNode(ctx context.Context, p peer.ID) (ci.Pub // validity because a) we can't. b) we know the hash of the // key we're looking for. val := record.GetValue() - log.Debug("DHT got a value from other peer.") + log.Debug("DHT got a value from other peer") pk, err = ci.UnmarshalPublicKey(val) if err != nil { diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 7298490df..6b68d4235 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -46,7 +46,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key key.Key, value []byte) err rec, err := record.MakePutRecord(sk, key, value, sign) if err != nil { - log.Debug("Creation of record failed!") + log.Debug("creation of record failed!") return err } @@ -346,7 +346,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key key.Key, select { case peerOut <- prov: case <-ctx.Done(): - log.Debug("Context timed out sending more providers") + log.Debug("context timed out sending more providers") return nil, ctx.Err() } } @@ -397,7 +397,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (pstore.PeerInfo, // Sanity... for _, p := range peers { if p == id { - log.Debug("Found target peer in list of closest peers...") + log.Debug("found target peer in list of closest peers...") return dht.peerstore.PeerInfo(p), nil } } From 8def240137aa095ead93c1442819a1d9efae1890 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Tue, 14 Jun 2016 12:20:21 +0100 Subject: [PATCH 1340/3817] Decapitalized log.Debug messages According to golang standards, these should not be capitalized nor having a trailing period, AFAIK. License: MIT Signed-off-by: Richard Littauer This commit was moved from ipfs/go-namesys@9549e44fb6b48c6b98fa2dea404c22ccbae529a1 --- namesys/publisher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 53324d676..4af9df2cc 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -314,7 +314,7 @@ func ValidateIpnsRecord(k key.Key, val []byte) error { case pb.IpnsEntry_EOL: t, err := u.ParseRFC3339(string(entry.GetValidity())) if err != nil { - log.Debug("Failed parsing time for ipns record EOL") + log.Debug("failed parsing time for ipns record EOL") return err } if time.Now().After(t) { From 8e7613edffd3809277948893f33a236fe444db41 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Tue, 14 Jun 2016 12:20:21 +0100 Subject: [PATCH 1341/3817] Decapitalized log.Debug messages According to golang standards, these should not be capitalized nor having a trailing period, AFAIK. License: MIT Signed-off-by: Richard Littauer This commit was moved from ipfs/go-blockservice@3bcf99c8a6fdf68c6980539545339edb06c8ceab --- blockservice/blockservice.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index b80f8cd17..1c158aaf6 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -86,7 +86,7 @@ func (s *BlockService) GetBlock(ctx context.Context, k key.Key) (blocks.Block, e if err == blockstore.ErrNotFound && s.Exchange != nil { // TODO be careful checking ErrNotFound. If the underlying // implementation changes, this will break. - log.Debug("Blockservice: Searching bitswap.") + log.Debug("Blockservice: Searching bitswap") blk, err := s.Exchange.GetBlock(ctx, k) if err != nil { if err == blockstore.ErrNotFound { @@ -97,7 +97,7 @@ func (s *BlockService) GetBlock(ctx context.Context, k key.Key) (blocks.Block, e return blk, nil } - log.Debug("Blockservice GetBlock: Not found.") + log.Debug("Blockservice GetBlock: Not found") if err == blockstore.ErrNotFound { return nil, ErrNotFound } @@ -119,7 +119,7 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []key.Key) <-chan block misses = append(misses, k) continue } - log.Debug("Blockservice: Got data in datastore.") + log.Debug("Blockservice: Got data in datastore") select { case out <- hit: case <-ctx.Done(): From 83d3d9f365c206411baee048af6bd135198b2ab1 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Tue, 14 Jun 2016 12:20:21 +0100 Subject: [PATCH 1342/3817] Decapitalized log.Debug messages According to golang standards, these should not be capitalized nor having a trailing period, AFAIK. License: MIT Signed-off-by: Richard Littauer This commit was moved from ipfs/go-path@ebf54d5bc1ea7e3153d5ea88e0455dcacf94ecf2 --- path/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/resolver.go b/path/resolver.go index af4d215ef..8eb96de23 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -86,7 +86,7 @@ func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]*me return nil, err } - log.Debug("Resolve dag get.") + log.Debug("resolve dag get") nd, err := s.DAG.Get(ctx, key.Key(h)) if err != nil { return nil, err From b62798f346873584a4af807230b395b5c02e5a34 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 15 Jun 2016 11:13:35 -0700 Subject: [PATCH 1343/3817] pass reference to reader instead of using the one on the object License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@8e809fe925d2227d840e54e80244ce8cd71b031d --- routing/dht/dht_net.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index c7d245341..cc7802e89 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -230,9 +230,9 @@ func (ms *messageSender) SendRequest(ctx context.Context, pmes *pb.Message) (*pb func (ms *messageSender) ctxReadMsg(ctx context.Context, mes *pb.Message) error { errc := make(chan error, 1) - go func() { - errc <- ms.r.ReadMsg(mes) - }() + go func(r ggio.ReadCloser) { + errc <- r.ReadMsg(mes) + }(ms.r) select { case err := <-errc: From a77d46b8e3a05faf0a97b3998b50f97e86acad70 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 15 Jun 2016 20:26:35 +0200 Subject: [PATCH 1344/3817] Remove failing blockstore test with context Why is it failing: process is started, cancel() is called, between we satart listening to the channels in select statemnet there is race of three things that can happent: 1. Task can complete 2. Task can start closing <- expected 3. Task already closed This race causes failures of the test. It is basing heavily on race of conditions where the task not closing, nor the task is completed before channels are being listened. It is quite impossible to resolve without adding bunch of timings in there, which we want to avoid, as there is no atomic "send message on channel and select" in Golang License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@876df4351406ce8a2276b16554488b7429bf6e7e --- blockstore/blockstore_test.go | 125 ++++++++++------------------------ 1 file changed, 35 insertions(+), 90 deletions(-) diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 2dc828ea2..4a7eb7c74 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -117,97 +117,42 @@ func TestAllKeysRespectsContext(t *testing.T) { errors <- nil // a nil one to signal break } - // Once without context, to make sure it all works - { - var results dsq.Results - var resultsmu = make(chan struct{}) - resultChan := make(chan dsq.Result) - d.SetFunc(func(q dsq.Query) (dsq.Results, error) { - results = dsq.ResultsWithChan(q, resultChan) - resultsmu <- struct{}{} - return results, nil - }) - - go getKeys(context.Background()) - - // make sure it's waiting. - <-started - <-resultsmu - select { - case <-done: - t.Fatal("sync is wrong") - case <-results.Process().Closing(): - t.Fatal("should not be closing") - case <-results.Process().Closed(): - t.Fatal("should not be closed") - default: - } - - e := dsq.Entry{Key: BlockPrefix.ChildString("foo").String()} - resultChan <- dsq.Result{Entry: e} // let it go. - close(resultChan) - <-done // should be done now. - <-results.Process().Closed() // should be closed now - - // print any errors - for err := range errors { - if err == nil { - break - } - t.Error(err) - } - } - - // Once with - { - var results dsq.Results - var resultsmu = make(chan struct{}) - resultChan := make(chan dsq.Result) - d.SetFunc(func(q dsq.Query) (dsq.Results, error) { - results = dsq.ResultsWithChan(q, resultChan) - resultsmu <- struct{}{} - return results, nil - }) - - ctx, cancel := context.WithCancel(context.Background()) - go getKeys(ctx) - - // make sure it's waiting. - <-started - <-resultsmu - select { - case <-done: - t.Fatal("sync is wrong") - case <-results.Process().Closing(): - t.Fatal("should not be closing") - case <-results.Process().Closed(): - t.Fatal("should not be closed") - default: - } - - cancel() // let it go. - - select { - case <-done: - t.Fatal("sync is wrong") - case <-results.Process().Closed(): - t.Fatal("should not be closed") // should not be closed yet. - case <-results.Process().Closing(): - // should be closing now! - t.Log("closing correctly at this point.") - } - - close(resultChan) - <-done // should be done now. - <-results.Process().Closed() // should be closed now - - // print any errors - for err := range errors { - if err == nil { - break - } - t.Error(err) + var results dsq.Results + var resultsmu = make(chan struct{}) + resultChan := make(chan dsq.Result) + d.SetFunc(func(q dsq.Query) (dsq.Results, error) { + results = dsq.ResultsWithChan(q, resultChan) + resultsmu <- struct{}{} + return results, nil + }) + + go getKeys(context.Background()) + + // make sure it's waiting. + <-started + <-resultsmu + select { + case <-done: + t.Fatal("sync is wrong") + case <-results.Process().Closing(): + t.Fatal("should not be closing") + case <-results.Process().Closed(): + t.Fatal("should not be closed") + default: + } + + e := dsq.Entry{Key: BlockPrefix.ChildString("foo").String()} + resultChan <- dsq.Result{Entry: e} // let it go. + close(resultChan) + <-done // should be done now. + <-results.Process().Closed() // should be closed now + + // print any errors + for err := range errors { + if err == nil { + break } + t.Error(err) } } From 12b888018a08667a000a9a14a04ef527558e0370 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 15 Jun 2016 13:04:49 -0700 Subject: [PATCH 1345/3817] update go-libp2p to 3.3.4 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@4d2f27d02eaddc5f3a272111635ddda27d1e72ab --- routing/dht/dht.go | 4 ++-- routing/dht/dht_net.go | 2 +- routing/dht/dht_test.go | 2 +- routing/dht/ext_test.go | 4 ++-- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/routing.go | 2 +- routing/mock/dht.go | 2 +- routing/none/none_client.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 4 ++-- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 0b049e575..d33c7977a 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -16,8 +16,6 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - host "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/host" - protocol "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/protocol" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" @@ -26,6 +24,8 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + host "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/host" + protocol "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/protocol" ) var log = logging.Logger("dht") diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 64d20283c..94b589fef 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,10 +6,10 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - inet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net" ctxio "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/io" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + inet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net" ) // handleNewStream implements the inet.StreamHandler diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index b9eb5eb5a..b58eaa853 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -18,11 +18,11 @@ import ( dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - netutil "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/test/util" pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + netutil "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/test/util" ) var testCaseValues = map[key.Key][]byte{} diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index efc5a8a75..049cfffd9 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -13,12 +13,12 @@ import ( ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" - inet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + inet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net/mock" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 059291547..74bbeefe4 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - inet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net" + inet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 923eaea62..5eaaebeed 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -5,9 +5,9 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - inet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net" pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + inet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 7298490df..fbf51895f 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -15,9 +15,9 @@ import ( pset "github.com/ipfs/go-ipfs/thirdparty/peerset" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - inet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net" pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + inet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net" ) // asyncQueryBuffer is the size of buffered channels in async queries. This diff --git a/routing/mock/dht.go b/routing/mock/dht.go index dd3e41f63..41a8684e8 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net/mock" ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" sync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + mocknet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 3bbf6df84..5c1c7227c 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -7,10 +7,10 @@ import ( repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - p2phost "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/host" pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + p2phost "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/host" ) var log = logging.Logger("mockrouter") diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 90029f534..2ef8d5249 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -12,11 +12,11 @@ import ( loggables "github.com/ipfs/go-ipfs/thirdparty/loggables" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/host" pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/host" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index a76a550ef..7e57a8238 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -6,7 +6,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - inet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net" + inet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 9ce316b6e..7d411b427 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -7,10 +7,10 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - host "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/host" - inet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net" pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + host "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/host" + inet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" From 3f6af6fb0834799782c94f7db53a5320c0a9c850 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 15 Jun 2016 13:04:49 -0700 Subject: [PATCH 1346/3817] update go-libp2p to 3.3.4 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@c68978425032b56945d1a0f748d01de871d62f26 --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 9a2d317f5..625f585b1 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmQkQP7WmeT9FRJDsEzAaGYDparttDiB6mCpVBrq2MuWQS/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" + mocknet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { From c68b9a1f010fdfc8980ce56018a9762c8fd25ec6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 17 Jun 2016 11:01:35 -0700 Subject: [PATCH 1347/3817] implement some simple dht request read timeouts License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@ce4158b1c92580f10137c0b5a046a20f12203eae --- routing/dht/dht.go | 43 +++++++++++++++++++++++++++++++++++++++--- routing/dht/dht_net.go | 9 +++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 3e06e978f..b2164ff1f 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -107,6 +107,16 @@ func (dht *IpfsDHT) putValueToPeer(ctx context.Context, p peer.ID, pmes := pb.NewMessage(pb.Message_PUT_VALUE, string(key), 0) pmes.Record = rec rpmes, err := dht.sendRequest(ctx, p, pmes) + switch err { + case ErrReadTimeout: + log.Errorf("read timeout: %s %s", p, key) + fallthrough + default: + return err + case nil: + break + } + if err != nil { return err } @@ -164,7 +174,16 @@ func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.ID, defer log.EventBegin(ctx, "getValueSingle", p, &key).Done() pmes := pb.NewMessage(pb.Message_GET_VALUE, string(key), 0) - return dht.sendRequest(ctx, p, pmes) + resp, err := dht.sendRequest(ctx, p, pmes) + switch err { + case nil: + return resp, nil + case ErrReadTimeout: + log.Errorf("read timeout: %s %s", p, key) + fallthrough + default: + return nil, err + } } // getLocal attempts to retrieve the value from the datastore @@ -238,14 +257,32 @@ func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.ID, id peer.ID) ( defer log.EventBegin(ctx, "findPeerSingle", p, id).Done() pmes := pb.NewMessage(pb.Message_FIND_NODE, string(id), 0) - return dht.sendRequest(ctx, p, pmes) + resp, err := dht.sendRequest(ctx, p, pmes) + switch err { + case nil: + return resp, nil + case ErrReadTimeout: + log.Errorf("read timeout: %s %s", p, id) + fallthrough + default: + return nil, err + } } func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.ID, key key.Key) (*pb.Message, error) { defer log.EventBegin(ctx, "findProvidersSingle", p, &key).Done() pmes := pb.NewMessage(pb.Message_GET_PROVIDERS, string(key), 0) - return dht.sendRequest(ctx, p, pmes) + resp, err := dht.sendRequest(ctx, p, pmes) + switch err { + case nil: + return resp, nil + case ErrReadTimeout: + log.Errorf("read timeout: %s %s", p, key) + fallthrough + default: + return nil, err + } } // nearestPeersToQuery returns the routing tables closest peers. diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index bc5d02d3d..b864d2caa 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -1,6 +1,7 @@ package dht import ( + "fmt" "sync" "time" @@ -12,6 +13,9 @@ import ( inet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net" ) +var dhtReadMessageTimeout = time.Minute +var ErrReadTimeout = fmt.Errorf("timed out reading response") + // handleNewStream implements the inet.StreamHandler func (dht *IpfsDHT) handleNewStream(s inet.Stream) { go dht.handleNewMessage(s) @@ -232,10 +236,15 @@ func (ms *messageSender) ctxReadMsg(ctx context.Context, mes *pb.Message) error errc <- r.ReadMsg(mes) }(ms.r) + t := time.NewTimer(dhtReadMessageTimeout) + defer t.Stop() + select { case err := <-errc: return err case <-ctx.Done(): return ctx.Err() + case <-t.C: + return ErrReadTimeout } } From 604ac4d43e1856109d3d289da0d746b9ba9eae67 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 17 Jun 2016 14:34:48 -0700 Subject: [PATCH 1348/3817] demote errors to warnings License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@6585b9ce1a250f4e2c984205c7d02ab5d685f5a4 --- routing/dht/dht.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index b2164ff1f..f96b67692 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -109,7 +109,7 @@ func (dht *IpfsDHT) putValueToPeer(ctx context.Context, p peer.ID, rpmes, err := dht.sendRequest(ctx, p, pmes) switch err { case ErrReadTimeout: - log.Errorf("read timeout: %s %s", p, key) + log.Warningf("read timeout: %s %s", p.Pretty(), key) fallthrough default: return err @@ -179,7 +179,7 @@ func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.ID, case nil: return resp, nil case ErrReadTimeout: - log.Errorf("read timeout: %s %s", p, key) + log.Warningf("read timeout: %s %s", p.Pretty(), key) fallthrough default: return nil, err @@ -262,7 +262,7 @@ func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.ID, id peer.ID) ( case nil: return resp, nil case ErrReadTimeout: - log.Errorf("read timeout: %s %s", p, id) + log.Warningf("read timeout: %s %s", p.Pretty(), id) fallthrough default: return nil, err @@ -278,7 +278,7 @@ func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.ID, key key. case nil: return resp, nil case ErrReadTimeout: - log.Errorf("read timeout: %s %s", p, key) + log.Warningf("read timeout: %s %s", p.Pretty(), key) fallthrough default: return nil, err From e432ed89f3166946484588248abfb1f90ea47249 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 20 Jun 2016 17:06:04 -0400 Subject: [PATCH 1349/3817] Add Files API root as best-effort pin. Closes #2697. Closes #2698. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@6bb1d6db53c443b40f3151db2a249a2905dfe66d --- ipld/merkledag/merkledag.go | 10 +++++++--- ipld/merkledag/merkledag_test.go | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 6792e3e51..4938e7bab 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -357,16 +357,20 @@ func (t *Batch) Commit() error { // EnumerateChildren will walk the dag below the given root node and add all // unseen children to the passed in set. // TODO: parallelize to avoid disk latency perf hits? -func EnumerateChildren(ctx context.Context, ds DAGService, root *Node, set key.KeySet) error { +func EnumerateChildren(ctx context.Context, ds DAGService, root *Node, set key.KeySet, bestEffort bool) error { for _, lnk := range root.Links { k := key.Key(lnk.Hash) if !set.Has(k) { set.Add(k) child, err := ds.Get(ctx, k) if err != nil { - return err + if bestEffort && err == ErrNotFound { + continue + } else { + return err + } } - err = EnumerateChildren(ctx, ds, child, set) + err = EnumerateChildren(ctx, ds, child, set, bestEffort) if err != nil { return err } diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 644d4e2d5..79b7399b5 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -292,7 +292,7 @@ func TestFetchGraph(t *testing.T) { offline_ds := NewDAGService(bs) ks := key.NewKeySet() - err = EnumerateChildren(context.Background(), offline_ds, root, ks) + err = EnumerateChildren(context.Background(), offline_ds, root, ks, false) if err != nil { t.Fatal(err) } @@ -309,7 +309,7 @@ func TestEnumerateChildren(t *testing.T) { } ks := key.NewKeySet() - err = EnumerateChildren(context.Background(), ds, root, ks) + err = EnumerateChildren(context.Background(), ds, root, ks, false) if err != nil { t.Fatal(err) } From 58bd3a74e858cb21e8082684da28ebdc4c46be14 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 20 Jun 2016 17:06:04 -0400 Subject: [PATCH 1350/3817] Add Files API root as best-effort pin. Closes #2697. Closes #2698. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@76aa2070021787ed8f2319e15960b709b3266736 --- pinning/pinner/gc/gc.go | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 1a043c817..34906fffb 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -17,18 +17,19 @@ var log = logging.Logger("gc") // GC performs a mark and sweep garbage collection of the blocks in the blockstore // first, it creates a 'marked' set and adds to it the following: // - all recursively pinned blocks, plus all of their descendants (recursively) +// - bestEffortRoots, plus all of its descendants (recursively) // - all directly pinned blocks // - all blocks utilized internally by the pinner // // The routine then iterates over every block in the blockstore and // deletes any block that is not found in the marked set. -func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner) (<-chan key.Key, error) { +func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner, bestEffortRoots []key.Key) (<-chan key.Key, error) { unlocker := bs.GCLock() bsrv := bserv.New(bs, offline.Exchange(bs)) ds := dag.NewDAGService(bsrv) - gcs, err := ColoredSet(ctx, pn, ds) + gcs, err := ColoredSet(ctx, pn, ds, bestEffortRoots) if err != nil { return nil, err } @@ -69,7 +70,7 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner) (<-chan key. return output, nil } -func Descendants(ctx context.Context, ds dag.DAGService, set key.KeySet, roots []key.Key) error { +func Descendants(ctx context.Context, ds dag.DAGService, set key.KeySet, roots []key.Key, bestEffort bool) error { for _, k := range roots { set.Add(k) nd, err := ds.Get(ctx, k) @@ -78,7 +79,7 @@ func Descendants(ctx context.Context, ds dag.DAGService, set key.KeySet, roots [ } // EnumerateChildren recursively walks the dag and adds the keys to the given set - err = dag.EnumerateChildren(ctx, ds, nd, set) + err = dag.EnumerateChildren(ctx, ds, nd, set, bestEffort) if err != nil { return err } @@ -87,11 +88,16 @@ func Descendants(ctx context.Context, ds dag.DAGService, set key.KeySet, roots [ return nil } -func ColoredSet(ctx context.Context, pn pin.Pinner, ds dag.DAGService) (key.KeySet, error) { +func ColoredSet(ctx context.Context, pn pin.Pinner, ds dag.DAGService, bestEffortRoots []key.Key) (key.KeySet, error) { // KeySet currently implemented in memory, in the future, may be bloom filter or // disk backed to conserve memory. gcs := key.NewKeySet() - err := Descendants(ctx, ds, gcs, pn.RecursiveKeys()) + err := Descendants(ctx, ds, gcs, pn.RecursiveKeys(), false) + if err != nil { + return nil, err + } + + err = Descendants(ctx, ds, gcs, bestEffortRoots, true) if err != nil { return nil, err } @@ -100,7 +106,7 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ds dag.DAGService) (key.KeyS gcs.Add(k) } - err = Descendants(ctx, ds, gcs, pn.InternalPins()) + err = Descendants(ctx, ds, gcs, pn.InternalPins(), false) if err != nil { return nil, err } From f38062b3502af829db314fed6c99875b7affe6ce Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Tue, 21 Jun 2016 13:20:31 +0100 Subject: [PATCH 1351/3817] Standardized Readme See https://github.com/ipfs/community/issues/124 This commit was moved from ipfs/go-ipfs-util@445bfb31dda5736bfbe206b693c7c3527a167d99 --- util/LICENSE | 2 +- util/README.md | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 util/README.md diff --git a/util/LICENSE b/util/LICENSE index c7386b3c9..9ce974446 100644 --- a/util/LICENSE +++ b/util/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 Juan Batiz-Benet +Copyright (c) 2016 Juan Batiz-Benet Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/util/README.md b/util/README.md new file mode 100644 index 000000000..766f3812b --- /dev/null +++ b/util/README.md @@ -0,0 +1,27 @@ +# go-ipfs-util + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) +[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![](https://img.shields.io/badge/discussion_repo-go_to_issues-brightgreen.svg?style=flat-square)](https://github.com/ipfs/NAME/issues) + +> Common utilities used by go-ipfs and other related go packages + +## Install + +## Usage + +## Contribute + +Feel free to join in. All welcome. Open an [issue](https://github.com/ipfs/go-ipfs-util/issues)! + +This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). + +### Want to hack on IPFS? + +[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/contributing.md) + +## License + +MIT From 766e251cac446773b4907fcf981cce28fb2b65aa Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 21 Jun 2016 13:59:18 -0700 Subject: [PATCH 1352/3817] return a better error if the ref is not an object License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@d836d3b6a42651764d3829041e802cbac27fb50a --- ipld/merkledag/merkledag.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 6792e3e51..f930bcca6 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -3,6 +3,7 @@ package merkledag import ( "fmt" + "strings" "sync" blocks "github.com/ipfs/go-ipfs/blocks" @@ -87,6 +88,9 @@ func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) { res, err := DecodeProtobuf(b.Data()) if err != nil { + if strings.Contains(err.Error(), "Unmarshal failed") { + return nil, fmt.Errorf("%s was not a valid merkledag node", k) + } return nil, fmt.Errorf("Failed to decode Protocol Buffers: %v", err) } return res, nil From d80bf0b70c7ca18dcc18df1b1e090a91bd8837df Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 22 Jun 2016 10:41:37 -0700 Subject: [PATCH 1353/3817] add a little bit more verbosity to the error License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@fc4387ed7bdff84b6d550780891d0455f4d7b4f9 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index f930bcca6..6acfcef5a 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -89,7 +89,7 @@ func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) { res, err := DecodeProtobuf(b.Data()) if err != nil { if strings.Contains(err.Error(), "Unmarshal failed") { - return nil, fmt.Errorf("%s was not a valid merkledag node", k) + return nil, fmt.Errorf("The block referred to by '%s' was not a valid merkledag node", k) } return nil, fmt.Errorf("Failed to decode Protocol Buffers: %v", err) } From dce3662fda5c7e23f84afb77d69c109994850cf8 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Jun 2016 18:38:07 +0200 Subject: [PATCH 1354/3817] Update go-log in whole dependency tree (#2898) * Update golog in go-ipfs License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-secio for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-crypto for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-peer for go-log License: MIT Signed-off-by: Jakub Sztandera * Import peersore, it wasn't imported License: MIT Signed-off-by: Jakub Sztandera * Update peerstore License: MIT Signed-off-by: Jakub Sztandera * Update peer License: MIT Signed-off-by: Jakub Sztandera * Update secio License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@34f3ab653a93d422967b3bdb571ea31fcb994423 --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 4d940fc06..3cc87a270 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -9,8 +9,8 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" dsns "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/namespace" dsq "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/query" From 8d536b17f6c5667b88ff94966eb098ddaee47171 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Jun 2016 18:38:07 +0200 Subject: [PATCH 1355/3817] Update go-log in whole dependency tree (#2898) * Update golog in go-ipfs License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-secio for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-crypto for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-peer for go-log License: MIT Signed-off-by: Jakub Sztandera * Import peersore, it wasn't imported License: MIT Signed-off-by: Jakub Sztandera * Update peerstore License: MIT Signed-off-by: Jakub Sztandera * Update peer License: MIT Signed-off-by: Jakub Sztandera * Update secio License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-routing@f1d4ba963c0e300a17b8b6ae3b3c42246ae849eb --- routing/dht/dht.go | 12 ++++++------ routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 4 ++-- routing/dht/dht_test.go | 6 +++--- routing/dht/ext_test.go | 6 +++--- routing/dht/handlers.go | 4 ++-- routing/dht/lookup.go | 4 ++-- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 8 ++++---- routing/dht/providers.go | 2 +- routing/dht/providers_test.go | 2 +- routing/dht/query.go | 8 ++++---- routing/dht/records.go | 4 ++-- routing/dht/routing.go | 6 +++--- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 6 +++--- routing/kbucket/table_test.go | 4 ++-- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 6 +++--- routing/mock/centralized_server.go | 4 ++-- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 4 ++-- routing/none/none_client.go | 8 ++++---- routing/offline/offline.go | 8 ++++---- routing/record/record.go | 4 ++-- routing/record/validation.go | 2 +- routing/routing.go | 6 +++--- routing/supernode/client.go | 8 ++++---- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 10 +++++----- routing/supernode/server.go | 4 ++-- 33 files changed, 79 insertions(+), 79 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index f96b67692..b914e9b86 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -15,17 +15,17 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + host "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/host" + protocol "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/protocol" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - host "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/host" - protocol "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/protocol" ) var log = logging.Logger("dht") diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 774e632de..5f1447299 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -9,7 +9,7 @@ import ( "time" routing "github.com/ipfs/go-ipfs/routing" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index b864d2caa..2c8232c5d 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,11 +6,11 @@ import ( "time" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" ctxio "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/io" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" + inet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - inet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net" ) var dhtReadMessageTimeout = time.Minute diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index b58eaa853..865263a7b 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -17,12 +17,12 @@ import ( ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" + netutil "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/test/util" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - netutil "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/test/util" ) var testCaseValues = map[key.Key][]byte{} diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 049cfffd9..42de2b477 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -13,12 +13,12 @@ import ( ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" + inet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net/mock" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - inet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net/mock" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 734393705..fa6490fba 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -10,8 +10,8 @@ import ( lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index daa125cf6..db4d651a2 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -6,8 +6,8 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 74bbeefe4..6ce249e4f 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - inet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net" + inet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 5eaaebeed..e35f40891 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,10 +4,10 @@ import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" - inet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + inet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/providers.go b/routing/dht/providers.go index 09e263461..f1bdd088e 100644 --- a/routing/dht/providers.go +++ b/routing/dht/providers.go @@ -4,9 +4,9 @@ import ( "time" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go index 9fa9d4b3a..362a83cb6 100644 --- a/routing/dht/providers_test.go +++ b/routing/dht/providers_test.go @@ -5,7 +5,7 @@ import ( "time" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index 9c8a2956d..3cf1434a4 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -9,12 +9,12 @@ import ( pset "github.com/ipfs/go-ipfs/thirdparty/peerset" todoctr "github.com/ipfs/go-ipfs/thirdparty/todocounter" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + queue "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore/queue" process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" ctxproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" - queue "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore/queue" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/records.go b/routing/dht/records.go index a1fcc5b8c..d920e9843 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -7,8 +7,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" ctxfrac "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/frac" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index c50c50503..9a2ca14aa 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -14,10 +14,10 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + inet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - inet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net" ) // asyncQueryBuffer is the size of buffered channels in async queries. This diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 1f887bd72..d835e24fd 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index 8b4fce863..ff9dc3d89 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 0dfdff457..3898af458 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,9 +7,9 @@ import ( "sync" "time" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" ) var log = logging.Logger("table") diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 67c2d199d..6a0c75e5b 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,8 +7,8 @@ import ( tu "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index ad3fe4983..f9fbed060 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 57a86e400..f508f5b08 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -10,9 +10,9 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 742b3bac9..8628a1db9 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -10,8 +10,8 @@ import ( ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 66cbbabf1..2b407c5e3 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,7 +8,7 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 41a8684e8..2779d3596 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,8 +5,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" sync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" + mocknet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - mocknet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index be28f4751..4a8bc3793 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -9,8 +9,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 5c1c7227c..ee57000a4 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,11 +6,11 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + p2phost "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/host" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - p2phost "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/host" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index f3b3c55dc..333dfac0c 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -10,10 +10,10 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/record/record.go b/routing/record/record.go index 83a197423..0a8a29a39 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" ) var log = logging.Logger("routing/record") diff --git a/routing/record/validation.go b/routing/record/validation.go index e9a35cf54..4cce81b2a 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/routing/routing.go b/routing/routing.go index a0f3ec555..f58f4a737 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -5,9 +5,9 @@ import ( "errors" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 2ef8d5249..4d3b3d7b0 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -11,12 +11,12 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" loggables "github.com/ipfs/go-ipfs/thirdparty/loggables" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/host" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/host" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 7e57a8238..730cef6bf 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - inet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + inet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 7d411b427..a736b5753 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -6,11 +6,11 @@ import ( ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" - host "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/host" - inet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + host "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/host" + inet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 64d54a465..425b99aa8 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -10,8 +10,8 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" datastore "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From b51779c983144b3af0852cbaa327ece8d49d4c5e Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Jun 2016 18:38:07 +0200 Subject: [PATCH 1356/3817] Update go-log in whole dependency tree (#2898) * Update golog in go-ipfs License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-secio for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-crypto for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-peer for go-log License: MIT Signed-off-by: Jakub Sztandera * Import peersore, it wasn't imported License: MIT Signed-off-by: Jakub Sztandera * Update peerstore License: MIT Signed-off-by: Jakub Sztandera * Update peer License: MIT Signed-off-by: Jakub Sztandera * Update secio License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@084cdfbed049ad449bb89f74583626552afbda5d --- namesys/interface.go | 2 +- namesys/ipns_select_test.go | 2 +- namesys/namesys.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 6 +++--- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 4 ++-- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index c70bb86f2..e4a4c3596 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -34,7 +34,7 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index aadaa9795..4660948cc 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -10,7 +10,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/namesys/namesys.go b/namesys/namesys.go index c7c68bfbd..2233032a7 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,7 +6,7 @@ import ( path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 4af9df2cc..ef64f9390 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -19,8 +19,8 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" - ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 7156fc39e..aba06aba6 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -12,11 +12,11 @@ import ( "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 625f585b1..57f706d0f 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - pstore "gx/ipfs/QmXHUpFsnpCmanRnacqYkFoLoFfEq5yS2nUgGkAjJ1Nj9j/go-libp2p-peerstore" - mocknet "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + mocknet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 93198e502..d3097b139 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -9,7 +9,7 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmQGwpJy9P4yXZySmqkZEXCmbBpJUb8xntCv8Ca4taZwDC/go-libp2p-peer" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" diff --git a/namesys/routing.go b/namesys/routing.go index da08213e8..79f38939b 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -14,8 +14,8 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) From ddc2dea0456a99c63ce15eb063bd7e3a3c5bda7f Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Jun 2016 18:38:07 +0200 Subject: [PATCH 1357/3817] Update go-log in whole dependency tree (#2898) * Update golog in go-ipfs License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-secio for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-crypto for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-peer for go-log License: MIT Signed-off-by: Jakub Sztandera * Import peersore, it wasn't imported License: MIT Signed-off-by: Jakub Sztandera * Update peerstore License: MIT Signed-off-by: Jakub Sztandera * Update peer License: MIT Signed-off-by: Jakub Sztandera * Update secio License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-mfs@7d36c96932bb777d316030fa1c02212ace68f428 --- mfs/system.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/system.go b/mfs/system.go index b0ee42e73..15e7c7684 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -18,7 +18,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From 2c9b32b200a16b8d94928149c28c96bb6b4ad146 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Jun 2016 18:38:07 +0200 Subject: [PATCH 1358/3817] Update go-log in whole dependency tree (#2898) * Update golog in go-ipfs License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-secio for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-crypto for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-peer for go-log License: MIT Signed-off-by: Jakub Sztandera * Import peersore, it wasn't imported License: MIT Signed-off-by: Jakub Sztandera * Update peerstore License: MIT Signed-off-by: Jakub Sztandera * Update peer License: MIT Signed-off-by: Jakub Sztandera * Update secio License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@5f0e10bc266494b78f7db871cc7128bbcc745e94 --- unixfs/mod/dagmodifier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 91ceef956..cb3cfd589 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -17,7 +17,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" ) var ErrSeekFail = errors.New("failed to seek properly") From fb8438f638c5ade5b6d48b48b6e7957cbd5ff253 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Jun 2016 18:38:07 +0200 Subject: [PATCH 1359/3817] Update go-log in whole dependency tree (#2898) * Update golog in go-ipfs License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-secio for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-crypto for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-peer for go-log License: MIT Signed-off-by: Jakub Sztandera * Import peersore, it wasn't imported License: MIT Signed-off-by: Jakub Sztandera * Update peerstore License: MIT Signed-off-by: Jakub Sztandera * Update peer License: MIT Signed-off-by: Jakub Sztandera * Update secio License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-merkledag@d1df68c1960cc32a41e1ec87b133b46866117ba5 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 0794ccf31..470f45faa 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -9,7 +9,7 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From e75ba5ecd3cb4944d1be231764f93447239da4d2 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Jun 2016 18:38:07 +0200 Subject: [PATCH 1360/3817] Update go-log in whole dependency tree (#2898) * Update golog in go-ipfs License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-secio for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-crypto for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-peer for go-log License: MIT Signed-off-by: Jakub Sztandera * Import peersore, it wasn't imported License: MIT Signed-off-by: Jakub Sztandera * Update peerstore License: MIT Signed-off-by: Jakub Sztandera * Update peer License: MIT Signed-off-by: Jakub Sztandera * Update secio License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-pinner@8b26136875d76a57afccd501cbaa9bc4482cfbac --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 34906fffb..487f7947e 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,7 +8,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 6cd0b80da..018ce6cf7 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From a3f472fabe9b8fc77de4765deed4f0bec4382673 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Jun 2016 18:38:07 +0200 Subject: [PATCH 1361/3817] Update go-log in whole dependency tree (#2898) * Update golog in go-ipfs License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-secio for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-crypto for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-peer for go-log License: MIT Signed-off-by: Jakub Sztandera * Import peersore, it wasn't imported License: MIT Signed-off-by: Jakub Sztandera * Update peerstore License: MIT Signed-off-by: Jakub Sztandera * Update peer License: MIT Signed-off-by: Jakub Sztandera * Update secio License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-blockservice@9eaf21247baebde621df4147e31eea503d8d3f88 --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 1c158aaf6..710580614 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -10,7 +10,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From 5cc58efe0f96af5b62531d1f6e0019977550ac2a Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Jun 2016 18:38:07 +0200 Subject: [PATCH 1362/3817] Update go-log in whole dependency tree (#2898) * Update golog in go-ipfs License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-secio for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-crypto for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-peer for go-log License: MIT Signed-off-by: Jakub Sztandera * Import peersore, it wasn't imported License: MIT Signed-off-by: Jakub Sztandera * Update peerstore License: MIT Signed-off-by: Jakub Sztandera * Update peer License: MIT Signed-off-by: Jakub Sztandera * Update secio License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-path@f0fd887a66b9e52102875b974ed41e3fcee4a894 --- path/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/resolver.go b/path/resolver.go index 8eb96de23..e5e94f2ff 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -11,7 +11,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" merkledag "github.com/ipfs/go-ipfs/merkledag" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" ) var log = logging.Logger("path") From 5eaf556fccffa4f7b9250333616f2d31560850ff Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Jun 2016 18:38:07 +0200 Subject: [PATCH 1363/3817] Update go-log in whole dependency tree (#2898) * Update golog in go-ipfs License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-secio for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-crypto for go-log License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p-peer for go-log License: MIT Signed-off-by: Jakub Sztandera * Import peersore, it wasn't imported License: MIT Signed-off-by: Jakub Sztandera * Update peerstore License: MIT Signed-off-by: Jakub Sztandera * Update peer License: MIT Signed-off-by: Jakub Sztandera * Update secio License: MIT Signed-off-by: Jakub Sztandera * Update go-libp2p License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-chunker@b5d317a923b95d514bedbce77eebbd7326c85d82 --- chunker/splitting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 6457de810..98cdef739 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -4,7 +4,7 @@ package chunk import ( "io" - logging "gx/ipfs/QmYtB7Qge8cJpXc4irsEp8zRqfnZMBeB7aTrMEkPk67DRv/go-log" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" ) var log = logging.Logger("chunk") From cdaff26d0859275feb279bac4d5ac232b619596a Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Jun 2016 21:31:58 +0200 Subject: [PATCH 1364/3817] blockstore: add fetch rehashing License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@f49cb3536971ec85c5c06c655ac9bffd965d10bc --- blockstore/blockstore.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 3cc87a270..a1f1f600b 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -22,7 +22,8 @@ var log = logging.Logger("blockstore") // BlockPrefix namespaces blockstore datastores var BlockPrefix = ds.NewKey("blocks") -var ValueTypeMismatch = errors.New("The retrieved value is not a Block") +var ValueTypeMismatch = errors.New("the retrieved value is not a Block") +var ErrHashMismatch = errors.New("block in storage has different hash than requested") var ErrNotFound = errors.New("blockstore: block not found") @@ -71,6 +72,12 @@ type blockstore struct { lk sync.RWMutex gcreq int32 gcreqlk sync.Mutex + + rehash bool +} + +func (bs *blockstore) RuntimeHashing(enabled bool) { + bs.rehash = enabled } func (bs *blockstore) Get(k key.Key) (blocks.Block, error) { @@ -90,7 +97,16 @@ func (bs *blockstore) Get(k key.Key) (blocks.Block, error) { return nil, ValueTypeMismatch } - return blocks.NewBlockWithHash(bdata, mh.Multihash(k)) + if bs.rehash { + rb := blocks.NewBlock(bdata) + if rb.Key() != k { + return nil, ErrHashMismatch + } else { + return rb, nil + } + } else { + return blocks.NewBlockWithHash(bdata, mh.Multihash(k)) + } } func (bs *blockstore) Put(block blocks.Block) error { From 6a14db935a1337ce658bb0627c601b95f54be436 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Jun 2016 22:03:16 +0200 Subject: [PATCH 1365/3817] tests: Add test to RuntimeHashing option of blockstore License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@ccf71bab8b88e545fa78bb01cf1852e36508e35e --- blockstore/blockstore_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 4a7eb7c74..babd1a99a 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -53,6 +53,22 @@ func TestPutThenGetBlock(t *testing.T) { } } +func TestRuntimeHashing(t *testing.T) { + bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) + bl := blocks.NewBlock([]byte("some data")) + blBad, err := blocks.NewBlockWithHash([]byte("some other data"), bl.Key().ToMultihash()) + if err != nil { + t.Fatal("Debug is enabled") + } + + bs.Put(blBad) + bs.RuntimeHashing(true) + + if _, err := bs.Get(bl.Key()); err != ErrHashMismatch { + t.Fatalf("Expected '%v' got '%v'\n", ErrHashMismatch, err) + } +} + func newBlockStoreWithKeys(t *testing.T, d ds.Datastore, N int) (Blockstore, []key.Key) { if d == nil { d = ds.NewMapDatastore() From 5ba55e5afe4cb6235c2fd10917a35fb9d65e5a61 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 16 May 2016 17:01:00 -0700 Subject: [PATCH 1366/3817] Write providers to disk to avoid memory leaks License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@0304ec22658a24e19325f87f0780d6c910c14b7c --- routing/dht/dht.go | 7 +- routing/dht/providers.go | 165 ----------- routing/dht/providers/providers.go | 363 ++++++++++++++++++++++++ routing/dht/providers/providers_test.go | 134 +++++++++ routing/dht/providers_test.go | 61 ---- 5 files changed, 501 insertions(+), 229 deletions(-) delete mode 100644 routing/dht/providers.go create mode 100644 routing/dht/providers/providers.go create mode 100644 routing/dht/providers/providers_test.go delete mode 100644 routing/dht/providers_test.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go index b914e9b86..ecee8c820 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -12,6 +12,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" + providers "github.com/ipfs/go-ipfs/routing/dht/providers" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" @@ -48,7 +49,7 @@ type IpfsDHT struct { datastore ds.Datastore // Local data routingTable *kb.RoutingTable // Array of routing tables for differently distanced nodes - providers *ProviderManager + providers *providers.ProviderManager birth time.Time // When this peer started up diaglock sync.Mutex // lock to make diagnostics work better @@ -84,8 +85,8 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.Datastore) *IpfsDHT { dht.ctx = ctx h.SetStreamHandler(ProtocolDHT, dht.handleNewStream) - dht.providers = NewProviderManager(dht.ctx, dht.self) - dht.proc.AddChild(dht.providers.proc) + dht.providers = providers.NewProviderManager(dht.ctx, dht.self, dstore) + dht.proc.AddChild(dht.providers.Process()) goprocessctx.CloseAfterContext(dht.proc, ctx) dht.routingTable = kb.NewRoutingTable(20, kb.ConvertPeerID(dht.self), time.Minute, dht.peerstore) diff --git a/routing/dht/providers.go b/routing/dht/providers.go deleted file mode 100644 index f1bdd088e..000000000 --- a/routing/dht/providers.go +++ /dev/null @@ -1,165 +0,0 @@ -package dht - -import ( - "time" - - key "github.com/ipfs/go-ipfs/blocks/key" - goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" - goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" -) - -var ProvideValidity = time.Hour * 24 -var defaultCleanupInterval = time.Hour - -type ProviderManager struct { - // all non channel fields are meant to be accessed only within - // the run method - providers map[key.Key]*providerSet - local map[key.Key]struct{} - lpeer peer.ID - - getlocal chan chan []key.Key - newprovs chan *addProv - getprovs chan *getProv - period time.Duration - proc goprocess.Process - - cleanupInterval time.Duration -} - -type providerSet struct { - providers []peer.ID - set map[peer.ID]time.Time -} - -type addProv struct { - k key.Key - val peer.ID -} - -type getProv struct { - k key.Key - resp chan []peer.ID -} - -func NewProviderManager(ctx context.Context, local peer.ID) *ProviderManager { - pm := new(ProviderManager) - pm.getprovs = make(chan *getProv) - pm.newprovs = make(chan *addProv) - pm.providers = make(map[key.Key]*providerSet) - pm.getlocal = make(chan chan []key.Key) - pm.local = make(map[key.Key]struct{}) - pm.proc = goprocessctx.WithContext(ctx) - pm.cleanupInterval = defaultCleanupInterval - pm.proc.Go(func(p goprocess.Process) { pm.run() }) - - return pm -} - -func (pm *ProviderManager) run() { - tick := time.NewTicker(pm.cleanupInterval) - for { - select { - case np := <-pm.newprovs: - if np.val == pm.lpeer { - pm.local[np.k] = struct{}{} - } - provs, ok := pm.providers[np.k] - if !ok { - provs = newProviderSet() - pm.providers[np.k] = provs - } - provs.Add(np.val) - - case gp := <-pm.getprovs: - var parr []peer.ID - provs, ok := pm.providers[gp.k] - if ok { - parr = provs.providers - } - - gp.resp <- parr - - case lc := <-pm.getlocal: - var keys []key.Key - for k := range pm.local { - keys = append(keys, k) - } - lc <- keys - - case <-tick.C: - for k, provs := range pm.providers { - var filtered []peer.ID - for p, t := range provs.set { - if time.Now().Sub(t) > ProvideValidity { - delete(provs.set, p) - } else { - filtered = append(filtered, p) - } - } - - if len(filtered) > 0 { - provs.providers = filtered - } else { - delete(pm.providers, k) - } - } - - case <-pm.proc.Closing(): - return - } - } -} - -func (pm *ProviderManager) AddProvider(ctx context.Context, k key.Key, val peer.ID) { - prov := &addProv{ - k: k, - val: val, - } - select { - case pm.newprovs <- prov: - case <-ctx.Done(): - } -} - -func (pm *ProviderManager) GetProviders(ctx context.Context, k key.Key) []peer.ID { - gp := &getProv{ - k: k, - resp: make(chan []peer.ID, 1), // buffered to prevent sender from blocking - } - select { - case <-ctx.Done(): - return nil - case pm.getprovs <- gp: - } - select { - case <-ctx.Done(): - return nil - case peers := <-gp.resp: - return peers - } -} - -func (pm *ProviderManager) GetLocal() []key.Key { - resp := make(chan []key.Key) - pm.getlocal <- resp - return <-resp -} - -func newProviderSet() *providerSet { - return &providerSet{ - set: make(map[peer.ID]time.Time), - } -} - -func (ps *providerSet) Add(p peer.ID) { - _, found := ps.set[p] - if !found { - ps.providers = append(ps.providers, p) - } - - ps.set[p] = time.Now() -} diff --git a/routing/dht/providers/providers.go b/routing/dht/providers/providers.go new file mode 100644 index 000000000..beb53d4ae --- /dev/null +++ b/routing/dht/providers/providers.go @@ -0,0 +1,363 @@ +package providers + +import ( + "encoding/base32" + "encoding/binary" + "fmt" + "strings" + "time" + + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" + goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + dsq "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/query" + + key "github.com/ipfs/go-ipfs/blocks/key" + + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" +) + +var log = logging.Logger("providers") + +var lruCacheSize = 256 +var ProvideValidity = time.Hour * 24 +var defaultCleanupInterval = time.Hour + +type ProviderManager struct { + // all non channel fields are meant to be accessed only within + // the run method + providers *lru.Cache + local map[key.Key]struct{} + lpeer peer.ID + dstore ds.Datastore + + getlocal chan chan []key.Key + newprovs chan *addProv + getprovs chan *getProv + period time.Duration + proc goprocess.Process + + cleanupInterval time.Duration +} + +type providerSet struct { + providers []peer.ID + set map[peer.ID]time.Time +} + +type addProv struct { + k key.Key + val peer.ID +} + +type getProv struct { + k key.Key + resp chan []peer.ID +} + +func NewProviderManager(ctx context.Context, local peer.ID, dstore ds.Datastore) *ProviderManager { + pm := new(ProviderManager) + pm.getprovs = make(chan *getProv) + pm.newprovs = make(chan *addProv) + pm.dstore = dstore + cache, err := lru.New(lruCacheSize) + if err != nil { + panic(err) //only happens if negative value is passed to lru constructor + } + pm.providers = cache + + pm.getlocal = make(chan chan []key.Key) + pm.local = make(map[key.Key]struct{}) + pm.proc = goprocessctx.WithContext(ctx) + pm.cleanupInterval = defaultCleanupInterval + pm.proc.Go(func(p goprocess.Process) { pm.run() }) + + return pm +} + +const providersKeyPrefix = "/providers/" + +func mkProvKey(k key.Key) ds.Key { + return ds.NewKey(providersKeyPrefix + base32.StdEncoding.EncodeToString([]byte(k))) +} + +func (pm *ProviderManager) Process() goprocess.Process { + return pm.proc +} + +func (pm *ProviderManager) providersForKey(k key.Key) ([]peer.ID, error) { + pset, err := pm.getProvSet(k) + if err != nil { + return nil, err + } + return pset.providers, nil +} + +func (pm *ProviderManager) getProvSet(k key.Key) (*providerSet, error) { + cached, ok := pm.providers.Get(k) + if ok { + return cached.(*providerSet), nil + } + + pset, err := loadProvSet(pm.dstore, k) + if err != nil { + return nil, err + } + + if len(pset.providers) > 0 { + pm.providers.Add(k, pset) + } + + return pset, nil +} + +func loadProvSet(dstore ds.Datastore, k key.Key) (*providerSet, error) { + res, err := dstore.Query(dsq.Query{Prefix: mkProvKey(k).String()}) + if err != nil { + return nil, err + } + + out := newProviderSet() + for e := range res.Next() { + if e.Error != nil { + log.Error("got an error: ", err) + continue + } + parts := strings.Split(e.Key, "/") + if len(parts) != 4 { + log.Warning("incorrectly formatted key: ", e.Key) + continue + } + + decstr, err := base32.StdEncoding.DecodeString(parts[len(parts)-1]) + if err != nil { + log.Error("base32 decoding error: ", err) + continue + } + + pid := peer.ID(decstr) + + t, err := readTimeValue(e.Value) + if err != nil { + log.Warning("parsing providers record from disk: ", err) + continue + } + + out.setVal(pid, t) + } + + return out, nil +} + +func readTimeValue(i interface{}) (time.Time, error) { + data, ok := i.([]byte) + if !ok { + return time.Time{}, fmt.Errorf("data was not a []byte") + } + + nsec, _ := binary.Varint(data) + + return time.Unix(0, nsec), nil +} + +func (pm *ProviderManager) addProv(k key.Key, p peer.ID) error { + iprovs, ok := pm.providers.Get(k) + if !ok { + iprovs = newProviderSet() + pm.providers.Add(k, iprovs) + } + provs := iprovs.(*providerSet) + now := time.Now() + provs.setVal(p, now) + + return writeProviderEntry(pm.dstore, k, p, now) +} + +func writeProviderEntry(dstore ds.Datastore, k key.Key, p peer.ID, t time.Time) error { + dsk := mkProvKey(k).ChildString(base32.StdEncoding.EncodeToString([]byte(p))) + + buf := make([]byte, 16) + n := binary.PutVarint(buf, t.UnixNano()) + + return dstore.Put(dsk, buf[:n]) +} + +func (pm *ProviderManager) deleteProvSet(k key.Key) error { + pm.providers.Remove(k) + + res, err := pm.dstore.Query(dsq.Query{ + KeysOnly: true, + Prefix: mkProvKey(k).String(), + }) + + entries, err := res.Rest() + if err != nil { + return err + } + + for _, e := range entries { + err := pm.dstore.Delete(ds.NewKey(e.Key)) + if err != nil { + log.Error("deleting provider set: ", err) + } + } + return nil +} + +func (pm *ProviderManager) getAllProvKeys() ([]key.Key, error) { + res, err := pm.dstore.Query(dsq.Query{ + KeysOnly: true, + Prefix: providersKeyPrefix, + }) + + if err != nil { + return nil, err + } + + entries, err := res.Rest() + if err != nil { + return nil, err + } + + out := make([]key.Key, 0, len(entries)) + seen := make(map[key.Key]struct{}) + for _, e := range entries { + parts := strings.Split(e.Key, "/") + if len(parts) != 4 { + log.Warning("incorrectly formatted provider entry in datastore") + continue + } + decoded, err := base32.StdEncoding.DecodeString(parts[2]) + if err != nil { + log.Warning("error decoding base32 provider key") + continue + } + + k := key.Key(decoded) + if _, ok := seen[k]; !ok { + out = append(out, key.Key(decoded)) + seen[k] = struct{}{} + } + } + + return out, nil +} + +func (pm *ProviderManager) run() { + tick := time.NewTicker(pm.cleanupInterval) + for { + select { + case np := <-pm.newprovs: + if np.val == pm.lpeer { + pm.local[np.k] = struct{}{} + } + err := pm.addProv(np.k, np.val) + if err != nil { + log.Error("error adding new providers: ", err) + } + case gp := <-pm.getprovs: + provs, err := pm.providersForKey(gp.k) + if err != nil && err != ds.ErrNotFound { + log.Error("error reading providers: ", err) + } + + gp.resp <- provs + case lc := <-pm.getlocal: + var keys []key.Key + for k := range pm.local { + keys = append(keys, k) + } + lc <- keys + + case <-tick.C: + keys, err := pm.getAllProvKeys() + if err != nil { + log.Error("Error loading provider keys: ", err) + continue + } + for _, k := range keys { + provs, err := pm.getProvSet(k) + if err != nil { + log.Error("error loading known provset: ", err) + continue + } + var filtered []peer.ID + for p, t := range provs.set { + if time.Now().Sub(t) > ProvideValidity { + delete(provs.set, p) + } else { + filtered = append(filtered, p) + } + } + + if len(filtered) > 0 { + provs.providers = filtered + } else { + err := pm.deleteProvSet(k) + if err != nil { + log.Error("error deleting provider set: ", err) + } + } + } + case <-pm.proc.Closing(): + return + } + } +} + +func (pm *ProviderManager) AddProvider(ctx context.Context, k key.Key, val peer.ID) { + prov := &addProv{ + k: k, + val: val, + } + select { + case pm.newprovs <- prov: + case <-ctx.Done(): + } +} + +func (pm *ProviderManager) GetProviders(ctx context.Context, k key.Key) []peer.ID { + gp := &getProv{ + k: k, + resp: make(chan []peer.ID, 1), // buffered to prevent sender from blocking + } + select { + case <-ctx.Done(): + return nil + case pm.getprovs <- gp: + } + select { + case <-ctx.Done(): + return nil + case peers := <-gp.resp: + return peers + } +} + +func (pm *ProviderManager) GetLocal() []key.Key { + resp := make(chan []key.Key) + pm.getlocal <- resp + return <-resp +} + +func newProviderSet() *providerSet { + return &providerSet{ + set: make(map[peer.ID]time.Time), + } +} + +func (ps *providerSet) Add(p peer.ID) { + ps.setVal(p, time.Now()) +} + +func (ps *providerSet) setVal(p peer.ID, t time.Time) { + _, found := ps.set[p] + if !found { + ps.providers = append(ps.providers, p) + } + + ps.set[p] = t +} diff --git a/routing/dht/providers/providers_test.go b/routing/dht/providers/providers_test.go new file mode 100644 index 000000000..2943e5ccb --- /dev/null +++ b/routing/dht/providers/providers_test.go @@ -0,0 +1,134 @@ +package providers + +import ( + "fmt" + "testing" + "time" + + key "github.com/ipfs/go-ipfs/blocks/key" + peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" +) + +func TestProviderManager(t *testing.T) { + ctx := context.Background() + mid := peer.ID("testing") + p := NewProviderManager(ctx, mid, ds.NewMapDatastore()) + a := key.Key("test") + p.AddProvider(ctx, a, peer.ID("testingprovider")) + resp := p.GetProviders(ctx, a) + if len(resp) != 1 { + t.Fatal("Could not retrieve provider.") + } + p.proc.Close() +} + +func TestProvidersDatastore(t *testing.T) { + old := lruCacheSize + lruCacheSize = 10 + defer func() { lruCacheSize = old }() + + ctx := context.Background() + mid := peer.ID("testing") + p := NewProviderManager(ctx, mid, ds.NewMapDatastore()) + defer p.proc.Close() + + friend := peer.ID("friend") + var keys []key.Key + for i := 0; i < 100; i++ { + k := key.Key(fmt.Sprint(i)) + keys = append(keys, k) + p.AddProvider(ctx, k, friend) + } + + for _, k := range keys { + resp := p.GetProviders(ctx, k) + if len(resp) != 1 { + t.Fatal("Could not retrieve provider.") + } + if resp[0] != friend { + t.Fatal("expected provider to be 'friend'") + } + } +} + +func TestProvidersSerialization(t *testing.T) { + dstore := ds.NewMapDatastore() + + k := key.Key("my key!") + p := peer.ID("my peer") + pt := time.Now() + + err := writeProviderEntry(dstore, k, p, pt) + if err != nil { + t.Fatal(err) + } + + pset, err := loadProvSet(dstore, k) + if err != nil { + t.Fatal(err) + } + + lt, ok := pset.set[p] + if !ok { + t.Fatal("failed to load set correctly") + } + + if pt != lt { + t.Fatal("time wasnt serialized correctly") + } +} + +func TestProvidesExpire(t *testing.T) { + pval := ProvideValidity + cleanup := defaultCleanupInterval + ProvideValidity = time.Second / 2 + defaultCleanupInterval = time.Second / 2 + defer func() { + ProvideValidity = pval + defaultCleanupInterval = cleanup + }() + + ctx := context.Background() + mid := peer.ID("testing") + p := NewProviderManager(ctx, mid, ds.NewMapDatastore()) + + peers := []peer.ID{"a", "b"} + var keys []key.Key + for i := 0; i < 10; i++ { + k := key.Key(i) + keys = append(keys, k) + p.AddProvider(ctx, k, peers[0]) + p.AddProvider(ctx, k, peers[1]) + } + + for i := 0; i < 10; i++ { + out := p.GetProviders(ctx, keys[i]) + if len(out) != 2 { + t.Fatal("expected providers to still be there") + } + } + + time.Sleep(time.Second) + for i := 0; i < 10; i++ { + out := p.GetProviders(ctx, keys[i]) + if len(out) > 2 { + t.Fatal("expected providers to be cleaned up") + } + } + + if p.providers.Len() != 0 { + t.Fatal("providers map not cleaned up") + } + + allprovs, err := p.getAllProvKeys() + if err != nil { + t.Fatal(err) + } + + if len(allprovs) != 0 { + t.Fatal("expected everything to be cleaned out of the datastore") + } +} diff --git a/routing/dht/providers_test.go b/routing/dht/providers_test.go deleted file mode 100644 index 362a83cb6..000000000 --- a/routing/dht/providers_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package dht - -import ( - "testing" - "time" - - key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" -) - -func TestProviderManager(t *testing.T) { - ctx := context.Background() - mid := peer.ID("testing") - p := NewProviderManager(ctx, mid) - a := key.Key("test") - p.AddProvider(ctx, a, peer.ID("testingprovider")) - resp := p.GetProviders(ctx, a) - if len(resp) != 1 { - t.Fatal("Could not retrieve provider.") - } - p.proc.Close() -} - -func TestProvidesExpire(t *testing.T) { - ProvideValidity = time.Second - defaultCleanupInterval = time.Second - - ctx := context.Background() - mid := peer.ID("testing") - p := NewProviderManager(ctx, mid) - - peers := []peer.ID{"a", "b"} - var keys []key.Key - for i := 0; i < 10; i++ { - k := key.Key(i) - keys = append(keys, k) - p.AddProvider(ctx, k, peers[0]) - p.AddProvider(ctx, k, peers[1]) - } - - for i := 0; i < 10; i++ { - out := p.GetProviders(ctx, keys[i]) - if len(out) != 2 { - t.Fatal("expected providers to still be there") - } - } - - time.Sleep(time.Second * 3) - for i := 0; i < 10; i++ { - out := p.GetProviders(ctx, keys[i]) - if len(out) > 2 { - t.Fatal("expected providers to be cleaned up") - } - } - - if len(p.providers) != 0 { - t.Fatal("providers map not cleaned up") - } -} From 6c52054775072d130e157697d0d2e61077f55ddf Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 25 Jun 2016 22:59:57 -0700 Subject: [PATCH 1367/3817] providers test with multiple peers License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@34d3e4e869892312d28e59ce5c387006b9ad9b02 --- routing/dht/providers/providers_test.go | 26 ++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/routing/dht/providers/providers_test.go b/routing/dht/providers/providers_test.go index 2943e5ccb..660640267 100644 --- a/routing/dht/providers/providers_test.go +++ b/routing/dht/providers/providers_test.go @@ -58,10 +58,17 @@ func TestProvidersSerialization(t *testing.T) { dstore := ds.NewMapDatastore() k := key.Key("my key!") - p := peer.ID("my peer") - pt := time.Now() + p1 := peer.ID("peer one") + p2 := peer.ID("peer two") + pt1 := time.Now() + pt2 := pt1.Add(time.Hour) - err := writeProviderEntry(dstore, k, p, pt) + err := writeProviderEntry(dstore, k, p1, pt1) + if err != nil { + t.Fatal(err) + } + + err = writeProviderEntry(dstore, k, p2, pt2) if err != nil { t.Fatal(err) } @@ -71,12 +78,21 @@ func TestProvidersSerialization(t *testing.T) { t.Fatal(err) } - lt, ok := pset.set[p] + lt1, ok := pset.set[p1] + if !ok { + t.Fatal("failed to load set correctly") + } + + if pt1 != lt1 { + t.Fatal("time wasnt serialized correctly") + } + + lt2, ok := pset.set[p2] if !ok { t.Fatal("failed to load set correctly") } - if pt != lt { + if pt2 != lt2 { t.Fatal("time wasnt serialized correctly") } } From 19ce01f6e85ac09edae745dc4baa1751779ed292 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 27 Jun 2016 14:43:01 -0700 Subject: [PATCH 1368/3817] use no padding encoding License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@a020b75eb01df645aded3da596b8a54505ee7102 --- routing/dht/providers/providers.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/routing/dht/providers/providers.go b/routing/dht/providers/providers.go index beb53d4ae..95d9b70d2 100644 --- a/routing/dht/providers/providers.go +++ b/routing/dht/providers/providers.go @@ -1,7 +1,6 @@ package providers import ( - "encoding/base32" "encoding/binary" "fmt" "strings" @@ -14,6 +13,7 @@ import ( lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" dsq "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/query" + base32 "gx/ipfs/Qmb1DA2A9LS2wR4FFweB4uEDomFsdmnw1VLawLE1yQzudj/base32" key "github.com/ipfs/go-ipfs/blocks/key" @@ -81,7 +81,7 @@ func NewProviderManager(ctx context.Context, local peer.ID, dstore ds.Datastore) const providersKeyPrefix = "/providers/" func mkProvKey(k key.Key) ds.Key { - return ds.NewKey(providersKeyPrefix + base32.StdEncoding.EncodeToString([]byte(k))) + return ds.NewKey(providersKeyPrefix + base32.RawStdEncoding.EncodeToString([]byte(k))) } func (pm *ProviderManager) Process() goprocess.Process { @@ -132,7 +132,7 @@ func loadProvSet(dstore ds.Datastore, k key.Key) (*providerSet, error) { continue } - decstr, err := base32.StdEncoding.DecodeString(parts[len(parts)-1]) + decstr, err := base32.RawStdEncoding.DecodeString(parts[len(parts)-1]) if err != nil { log.Error("base32 decoding error: ", err) continue @@ -177,7 +177,7 @@ func (pm *ProviderManager) addProv(k key.Key, p peer.ID) error { } func writeProviderEntry(dstore ds.Datastore, k key.Key, p peer.ID, t time.Time) error { - dsk := mkProvKey(k).ChildString(base32.StdEncoding.EncodeToString([]byte(p))) + dsk := mkProvKey(k).ChildString(base32.RawStdEncoding.EncodeToString([]byte(p))) buf := make([]byte, 16) n := binary.PutVarint(buf, t.UnixNano()) @@ -230,7 +230,7 @@ func (pm *ProviderManager) getAllProvKeys() ([]key.Key, error) { log.Warning("incorrectly formatted provider entry in datastore") continue } - decoded, err := base32.StdEncoding.DecodeString(parts[2]) + decoded, err := base32.RawStdEncoding.DecodeString(parts[2]) if err != nil { log.Warning("error decoding base32 provider key") continue From c38cd01172703e2148897a978ed46e78e0c58b72 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 1 Jul 2016 14:51:19 +0200 Subject: [PATCH 1369/3817] routing: Use correct error variable License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-routing@fe58cd34c47aa7cd87179b213db6bc0df6e80313 --- routing/dht/providers/providers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/providers/providers.go b/routing/dht/providers/providers.go index 95d9b70d2..ca8dff18b 100644 --- a/routing/dht/providers/providers.go +++ b/routing/dht/providers/providers.go @@ -123,7 +123,7 @@ func loadProvSet(dstore ds.Datastore, k key.Key) (*providerSet, error) { out := newProviderSet() for e := range res.Next() { if e.Error != nil { - log.Error("got an error: ", err) + log.Error("got an error: ", e.Error) continue } parts := strings.Split(e.Key, "/") From 680ccdbc1e9f76d5dd322f265afaf9208f4f4810 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 22 Jun 2016 14:28:40 -0700 Subject: [PATCH 1370/3817] encode keys to datastore with base32 standard encoding Fixes #2601 Also bump version to 0.4.3-dev License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@e6f5fb0404f0fe3f5948a74be7cf19ce96ce5e4c --- blockstore/blockstore.go | 23 ++++++++++++++--------- blockstore/blockstore_test.go | 6 +++--- blockstore/write_cache_test.go | 6 +++--- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index a1f1f600b..eb35fdbaf 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -11,10 +11,10 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - dsns "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/namespace" - dsq "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/query" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + dsns "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/namespace" + dsq "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/query" ) var log = logging.Logger("blockstore") @@ -164,26 +164,31 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { } // this function is here to compartmentalize - get := func() (k key.Key, ok bool) { + get := func() (key.Key, bool) { select { case <-ctx.Done(): - return k, false + return "", false case e, more := <-res.Next(): if !more { - return k, false + return "", false } if e.Error != nil { log.Debug("blockstore.AllKeysChan got err:", e.Error) - return k, false + return "", false } // need to convert to key.Key using key.KeyFromDsKey. - k = key.KeyFromDsKey(ds.NewKey(e.Key)) + k, err := key.KeyFromDsKey(ds.NewKey(e.Key)) + if err != nil { + log.Warningf("error parsing key from DsKey: ", err) + return "", true + } log.Debug("blockstore: query got key", k) // key must be a multihash. else ignore it. - _, err := mh.Cast([]byte(k)) + _, err = mh.Cast([]byte(k)) if err != nil { + log.Warningf("key from datastore was not a multihash: ", err) return "", true } diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index babd1a99a..1996a4729 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - dsq "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/query" - ds_sync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + dsq "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/query" + ds_sync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/blockstore/write_cache_test.go b/blockstore/write_cache_test.go index 01c52ae40..e3b32ff0b 100644 --- a/blockstore/write_cache_test.go +++ b/blockstore/write_cache_test.go @@ -4,9 +4,9 @@ import ( "testing" "github.com/ipfs/go-ipfs/blocks" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - dsq "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/query" - syncds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + dsq "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/query" + syncds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" ) func TestReturnsErrorWhenSizeNegative(t *testing.T) { From 2227676ff4ecf0a8edd7dbb197b4f6c082f6a314 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 22 Jun 2016 14:28:40 -0700 Subject: [PATCH 1371/3817] encode keys to datastore with base32 standard encoding Fixes #2601 Also bump version to 0.4.3-dev License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@65264936353b346c8e890dbdf82b1e2c752d3dca --- namesys/namesys.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/resolve_test.go | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 2233032a7..5cbade9ba 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,8 +7,8 @@ import ( path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/publisher.go b/namesys/publisher.go index ef64f9390..ec4629d10 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,8 +7,8 @@ import ( "time" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index aba06aba6..0629e5d4d 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -18,8 +18,8 @@ import ( gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index d3097b139..6fe1e6818 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -10,10 +10,10 @@ import ( mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" ) func TestRoutingResolve(t *testing.T) { From 724e7d0f0303355daa175cc0dccf96edf6a71c31 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 22 Jun 2016 14:28:40 -0700 Subject: [PATCH 1372/3817] encode keys to datastore with base32 standard encoding Fixes #2601 Also bump version to 0.4.3-dev License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@4c7eeaecf225b6c4cd65fe8ab23dd2374e889d84 --- routing/dht/dht.go | 2 +- routing/dht/dht_test.go | 4 ++-- routing/dht/ext_test.go | 4 ++-- routing/dht/handlers.go | 2 +- routing/dht/providers/providers.go | 4 ++-- routing/dht/providers/providers_test.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 4 ++-- routing/mock/dht.go | 4 ++-- routing/mock/interface.go | 2 +- routing/offline/offline.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 13 files changed, 18 insertions(+), 18 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index ecee8c820..bbb8fd5a4 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -23,10 +23,10 @@ import ( peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" host "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/host" protocol "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/protocol" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" ) var log = logging.Logger("dht") diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 865263a7b..92344c801 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -14,8 +14,8 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 42de2b477..867124fc5 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -10,8 +10,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index fa6490fba..dabdd0e37 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" diff --git a/routing/dht/providers/providers.go b/routing/dht/providers/providers.go index ca8dff18b..4a5c0ef0e 100644 --- a/routing/dht/providers/providers.go +++ b/routing/dht/providers/providers.go @@ -11,9 +11,9 @@ import ( goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - dsq "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/query" base32 "gx/ipfs/Qmb1DA2A9LS2wR4FFweB4uEDomFsdmnw1VLawLE1yQzudj/base32" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + dsq "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/query" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/routing/dht/providers/providers_test.go b/routing/dht/providers/providers_test.go index 660640267..8f2e8c229 100644 --- a/routing/dht/providers/providers_test.go +++ b/routing/dht/providers/providers_test.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index f508f5b08..32a0e1436 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,7 +8,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 8628a1db9..7536c8772 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 2779d3596..8d9a9b5af 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - sync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" mocknet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + sync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 4a8bc3793..875522af2 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -11,8 +11,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" ) // Server provides mockrouting Clients diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 333dfac0c..c9cf3d8a8 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -8,7 +8,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 425b99aa8..7c6fb0c7d 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,7 +8,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - datastore "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + datastore "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 1d3ff54c0..7eefa5249 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -5,7 +5,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - datastore "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" + datastore "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 896af3232fcd6f2c0844ac6a7fb7e9e348b096e8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 22 Jun 2016 14:28:40 -0700 Subject: [PATCH 1373/3817] encode keys to datastore with base32 standard encoding Fixes #2601 Also bump version to 0.4.3-dev License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@4a623eb06ec64d035e9ab25dbe7313198da22547 --- mfs/mfs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 4c58b83f6..c75a0db41 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -15,9 +15,9 @@ import ( "github.com/ipfs/go-ipfs/path" randbo "gx/ipfs/QmYvsG72GsfLgUeSojXArjnU6L4Wmwk7wuAxtNLuyXcc1T/randbo" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" From 834a57b99f5d14ac5803a4404b6170ac6c9a7419 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 22 Jun 2016 14:28:40 -0700 Subject: [PATCH 1374/3817] encode keys to datastore with base32 standard encoding Fixes #2601 Also bump version to 0.4.3-dev License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@2d151cc13215d12928b76bb8f3e079a7ebef4700 --- unixfs/mod/dagmodifier_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 404a187ec..8a57d49d6 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -17,11 +17,11 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" ) func getMockDagServ(t testing.TB) mdag.DAGService { From ab0734d5000259c3ae0c59b1b565cb7782d9efac Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 22 Jun 2016 14:28:40 -0700 Subject: [PATCH 1375/3817] encode keys to datastore with base32 standard encoding Fixes #2601 Also bump version to 0.4.3-dev License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@0c87745387c7745498dc9f0901b724ba177b2cf3 --- ipld/merkledag/merkledag_test.go | 4 ++-- ipld/merkledag/test/utils.go | 4 ++-- ipld/merkledag/utils/utils.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 79b7399b5..8b101e8d9 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -21,10 +21,10 @@ import ( dstest "github.com/ipfs/go-ipfs/merkledag/test" "github.com/ipfs/go-ipfs/pin" uio "github.com/ipfs/go-ipfs/unixfs/io" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" ) type dagservAndPinner struct { diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index 87680d120..f99a9f928 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -5,8 +5,8 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" ) func Mock() dag.DAGService { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index e268c9f7f..80bf89311 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -3,9 +3,9 @@ package dagutils import ( "errors" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - syncds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + syncds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" bserv "github.com/ipfs/go-ipfs/blockservice" From a7b2f40ed1cdd36ee8c999674dc9dc120e77356e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 22 Jun 2016 14:28:40 -0700 Subject: [PATCH 1376/3817] encode keys to datastore with base32 standard encoding Fixes #2601 Also bump version to 0.4.3-dev License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@740596d9395c9484740b9727532ad1a5187556db --- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 018ce6cf7..5e39e1ce7 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -11,8 +11,8 @@ import ( "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index ecc1fc1f6..c7220dada 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -11,9 +11,9 @@ import ( bs "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" ) func randNode() (*mdag.Node, key.Key) { diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index f1993e8ec..64006ca41 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -10,10 +10,10 @@ import ( "github.com/ipfs/go-ipfs/exchange/offline" "github.com/ipfs/go-ipfs/merkledag" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" - "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" ) func ignoreKeys(key.Key) {} From 58552c4d4047a2ee2092f1de5780321a726ec1f8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 22 Jun 2016 14:28:40 -0700 Subject: [PATCH 1377/3817] encode keys to datastore with base32 standard encoding Fixes #2601 Also bump version to 0.4.3-dev License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@4f0a801662035b83307fd7e217a0e525d7e92fe5 --- blockservice/test/blocks_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 7c8e9ba96..7a3b9ce94 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -11,10 +11,10 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - dssync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" ) func TestBlocks(t *testing.T) { From b323e9198c0489b2c863ea369e912dce523e3713 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 22 Jun 2016 14:28:40 -0700 Subject: [PATCH 1378/3817] encode keys to datastore with base32 standard encoding Fixes #2601 Also bump version to 0.4.3-dev License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@c0304d193821b1cca840b05947e7f5b76097ec99 --- exchange/offline/offline_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 46cb4fb2a..bdb6262af 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -7,9 +7,9 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" key "github.com/ipfs/go-ipfs/blocks/key" - ds "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore" - ds_sync "gx/ipfs/QmZ6A6P6AMo8SR3jXAwzTuSU6B9R2Y4eqW2yW9VvfUayDN/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + ds_sync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" ) func TestBlockReturnsErr(t *testing.T) { From e87e9022cf5b66334890a199d6f3ebf1f838ec61 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 1 Jul 2016 22:40:57 -0700 Subject: [PATCH 1379/3817] update go-datastore changes 0.1.2 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@cadf17239e4d0470305dce2be50046118e4b2ae0 --- blockstore/blockstore.go | 6 +++--- blockstore/blockstore_test.go | 6 +++--- blockstore/write_cache_test.go | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index eb35fdbaf..477141cd6 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -12,9 +12,9 @@ import ( logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - dsns "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/namespace" - dsq "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/query" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + dsns "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/namespace" + dsq "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/query" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 1996a4729..f7a5325ab 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -6,9 +6,9 @@ import ( "testing" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - dsq "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/query" - ds_sync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + dsq "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/query" + ds_sync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/blockstore/write_cache_test.go b/blockstore/write_cache_test.go index e3b32ff0b..966ff0610 100644 --- a/blockstore/write_cache_test.go +++ b/blockstore/write_cache_test.go @@ -4,9 +4,9 @@ import ( "testing" "github.com/ipfs/go-ipfs/blocks" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - dsq "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/query" - syncds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + dsq "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/query" + syncds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) func TestReturnsErrorWhenSizeNegative(t *testing.T) { From 17aafd8dc3b547b438e7c9aeed4a02985a9e3a98 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 1 Jul 2016 22:40:57 -0700 Subject: [PATCH 1380/3817] update go-datastore changes 0.1.2 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@e27c7535551596a3e51a2fb21265af515cfec7f1 --- namesys/namesys.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/resolve_test.go | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 5cbade9ba..bf058e5ff 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -8,7 +8,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/publisher.go b/namesys/publisher.go index ec4629d10..65c482655 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -8,7 +8,7 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 0629e5d4d..fc6c41bc7 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -19,7 +19,7 @@ import ( peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 6fe1e6818..03f64abac 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -12,8 +12,8 @@ import ( peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) func TestRoutingResolve(t *testing.T) { From a6845e0ecd84ec067ebfb8b7c818c99700f3c27e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 1 Jul 2016 22:40:57 -0700 Subject: [PATCH 1381/3817] update go-datastore changes 0.1.2 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@05ae43401d850329b5172526cb94c91cd6d91e94 --- routing/dht/dht.go | 2 +- routing/dht/dht_test.go | 4 ++-- routing/dht/ext_test.go | 4 ++-- routing/dht/handlers.go | 2 +- routing/dht/providers/providers.go | 4 ++-- routing/dht/providers/providers_test.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 4 ++-- routing/mock/dht.go | 4 ++-- routing/mock/interface.go | 2 +- routing/offline/offline.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 13 files changed, 18 insertions(+), 18 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index bbb8fd5a4..71f21f995 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -26,7 +26,7 @@ import ( host "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/host" protocol "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/protocol" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" ) var log = logging.Logger("dht") diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 92344c801..018f72215 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -14,8 +14,8 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 867124fc5..3e33fd0f0 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -10,8 +10,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index dabdd0e37..0152ff1bd 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" diff --git a/routing/dht/providers/providers.go b/routing/dht/providers/providers.go index 4a5c0ef0e..da3cf7606 100644 --- a/routing/dht/providers/providers.go +++ b/routing/dht/providers/providers.go @@ -12,8 +12,8 @@ import ( peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" base32 "gx/ipfs/Qmb1DA2A9LS2wR4FFweB4uEDomFsdmnw1VLawLE1yQzudj/base32" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - dsq "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/query" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + dsq "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/query" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/routing/dht/providers/providers_test.go b/routing/dht/providers/providers_test.go index 8f2e8c229..11e1506a8 100644 --- a/routing/dht/providers/providers_test.go +++ b/routing/dht/providers/providers_test.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 32a0e1436..e3e036bdf 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,7 +8,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 7536c8772..ed4973e7a 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 8d9a9b5af..bf729f11d 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,8 +5,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" mocknet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - sync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + sync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 875522af2..ea092f13e 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -12,7 +12,7 @@ import ( pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" ) // Server provides mockrouting Clients diff --git a/routing/offline/offline.go b/routing/offline/offline.go index c9cf3d8a8..a20783666 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -8,7 +8,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 7c6fb0c7d..0a105256c 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,7 +8,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - datastore "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + datastore "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 7eefa5249..bde8ca2b6 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -5,7 +5,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - datastore "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + datastore "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 65713afc49790b80f8a08983a1bdef7eae44c23e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 1 Jul 2016 22:40:57 -0700 Subject: [PATCH 1382/3817] update go-datastore changes 0.1.2 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@57018c3957cdc356393b63706ad1551ceaf9755b --- mfs/mfs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index c75a0db41..84ce22668 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -16,8 +16,8 @@ import ( "github.com/ipfs/go-ipfs/path" randbo "gx/ipfs/QmYvsG72GsfLgUeSojXArjnU6L4Wmwk7wuAxtNLuyXcc1T/randbo" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" From 62796c838f090bd1e2dde0d798daa631006aa8ff Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 1 Jul 2016 22:40:57 -0700 Subject: [PATCH 1383/3817] update go-datastore changes 0.1.2 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@b58612a64164d34eba6636040ff4a9fc8691b159 --- unixfs/mod/dagmodifier_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 8a57d49d6..dffd205bf 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -18,10 +18,10 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" ) func getMockDagServ(t testing.TB) mdag.DAGService { From c33321948f19a24a33671fe62e7a4c3e8b8c35bb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 1 Jul 2016 22:40:57 -0700 Subject: [PATCH 1384/3817] update go-datastore changes 0.1.2 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@1655cd1a0fa68b449a24a6dda73fc3fa666015f6 --- ipld/merkledag/merkledag_test.go | 4 ++-- ipld/merkledag/test/utils.go | 4 ++-- ipld/merkledag/utils/utils.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 8b101e8d9..72bc3ef17 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -23,8 +23,8 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) type dagservAndPinner struct { diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index f99a9f928..fd70b7085 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -5,8 +5,8 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) func Mock() dag.DAGService { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 80bf89311..8e0e596fd 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -4,8 +4,8 @@ import ( "errors" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - syncds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + syncds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" bserv "github.com/ipfs/go-ipfs/blockservice" From 45db7db0177a8c21961358f05d45aad67b04e035 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 1 Jul 2016 22:40:57 -0700 Subject: [PATCH 1385/3817] update go-datastore changes 0.1.2 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@4ad46f6ff37f5f0bfa8f3d99329fc139f23ad0ce --- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 5e39e1ce7..83a6f5b88 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -12,7 +12,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index c7220dada..a6ec85dac 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -12,8 +12,8 @@ import ( "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) func randNode() (*mdag.Node, key.Key) { diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 64006ca41..da5f0702d 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -12,8 +12,8 @@ import ( mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) func ignoreKeys(key.Key) {} From 05fb104598af4c1563adc3d602390310741e4f39 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 1 Jul 2016 22:40:57 -0700 Subject: [PATCH 1386/3817] update go-datastore changes 0.1.2 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@123a7d847a3e5e424441f7fd4b760a9e0046d9b7 --- blockservice/test/blocks_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 7a3b9ce94..943bf0944 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -13,8 +13,8 @@ import ( offline "github.com/ipfs/go-ipfs/exchange/offline" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - dssync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) func TestBlocks(t *testing.T) { From 665b7c1c7a924627398caaf193df8da2a31077fd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 1 Jul 2016 22:40:57 -0700 Subject: [PATCH 1387/3817] update go-datastore changes 0.1.2 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@d00c30cf32ca758fcd15de5d79777d02d1904fd6 --- exchange/offline/offline_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index bdb6262af..56066a552 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -8,8 +8,8 @@ import ( "github.com/ipfs/go-ipfs/blocks/blocksutil" key "github.com/ipfs/go-ipfs/blocks/key" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore" - ds_sync "gx/ipfs/QmbCg24DeRKaRDLHbzzSVj7xndmWCPanBLkAM7Lx2nbrFs/go-datastore/sync" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + ds_sync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) func TestBlockReturnsErr(t *testing.T) { From 3c5f7181b8718128286cc2f44a370bca0e407d5f Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 21 Jun 2016 21:05:59 +0200 Subject: [PATCH 1388/3817] blocks/blockstore: Add bloom filter Replace write_cache with bloom_cache Improve ARC caching Fix small issue in case of AllKeysChan fails deps: Update go-datastore blocks/blockstore: Invalidate ARC cache before deletin block deps: Update go-datastore License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@eebe80643815623d206ae2c5c1371d27ef12f785 --- blockstore/bloom_cache.go | 175 ++++++++++++++++++ ...rite_cache_test.go => bloom_cache_test.go} | 51 ++++- blockstore/write_cache.go | 78 -------- 3 files changed, 218 insertions(+), 86 deletions(-) create mode 100644 blockstore/bloom_cache.go rename blockstore/{write_cache_test.go => bloom_cache_test.go} (66%) delete mode 100644 blockstore/write_cache.go diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go new file mode 100644 index 000000000..ca61d755a --- /dev/null +++ b/blockstore/bloom_cache.go @@ -0,0 +1,175 @@ +package blockstore + +import ( + "github.com/ipfs/go-ipfs/blocks" + key "github.com/ipfs/go-ipfs/blocks/key" + lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" + bloom "gx/ipfs/QmWQ2SJisXwcCLsUXLwYCKSfyExXjFRW2WbBH5sqCUnwX5/bbloom" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" +) + +// BloomCached returns Blockstore that caches Has requests using Bloom filter +// Size is size of bloom filter in bytes +func BloomCached(bs Blockstore, bloomSize, lruSize int) (*bloomcache, error) { + bl, err := bloom.New(float64(bloomSize), float64(7)) + if err != nil { + return nil, err + } + arc, err := lru.NewARC(lruSize) + if err != nil { + return nil, err + } + bc := &bloomcache{blockstore: bs, bloom: bl, arc: arc} + bc.Invalidate() + go bc.Rebuild() + + return bc, nil +} + +type bloomcache struct { + bloom *bloom.Bloom + active bool + + arc *lru.ARCCache + // This chan is only used for testing to wait for bloom to enable + rebuildChan chan struct{} + blockstore Blockstore + + // Statistics + hits uint64 + misses uint64 +} + +func (b *bloomcache) Invalidate() { + b.rebuildChan = make(chan struct{}) + b.active = false +} + +func (b *bloomcache) BloomActive() bool { + return b.active +} + +func (b *bloomcache) Rebuild() { + ctx := context.TODO() + evt := log.EventBegin(ctx, "bloomcache.Rebuild") + defer evt.Done() + + ch, err := b.blockstore.AllKeysChan(ctx) + if err != nil { + log.Errorf("AllKeysChan failed in bloomcache rebuild with: %v", err) + return + } + for key := range ch { + b.bloom.AddTS([]byte(key)) // Use binary key, the more compact the better + } + close(b.rebuildChan) + b.active = true +} + +func (b *bloomcache) DeleteBlock(k key.Key) error { + if has, ok := b.hasCached(k); ok && !has { + return ErrNotFound + } + + b.arc.Remove(k) // Invalidate cache before deleting. + err := b.blockstore.DeleteBlock(k) + if err == nil { + b.arc.Add(k, false) + } else if err == ds.ErrNotFound || err == ErrNotFound { + b.arc.Add(k, false) + return ErrNotFound + } + return err +} + +// if ok == false has is inconclusive +// if ok == true then has respons to question: is it contained +func (b *bloomcache) hasCached(k key.Key) (has bool, ok bool) { + if k == "" { + return true, true + } + if b.active { + blr := b.bloom.HasTS([]byte(k)) + if blr == false { // not contained in bloom is only conclusive answer bloom gives + return blr, true + } + } + h, ok := b.arc.Get(k) + if ok { + return h.(bool), ok + } else { + return false, ok + } +} + +func (b *bloomcache) Has(k key.Key) (bool, error) { + if has, ok := b.hasCached(k); ok { + return has, nil + } + + res, err := b.blockstore.Has(k) + if err == nil { + b.arc.Add(k, res) + } + return res, err +} + +func (b *bloomcache) Get(k key.Key) (blocks.Block, error) { + if has, ok := b.hasCached(k); ok && !has { + return nil, ErrNotFound + } + + bl, err := b.blockstore.Get(k) + if bl == nil && err == ErrNotFound { + b.arc.Add(k, false) + } else if bl != nil { + b.arc.Add(k, true) + } + return bl, err +} + +func (b *bloomcache) Put(bl blocks.Block) error { + if has, ok := b.hasCached(bl.Key()); ok && has { + return nil + } + + err := b.blockstore.Put(bl) + if err == nil { + b.bloom.AddTS([]byte(bl.Key())) + b.arc.Add(bl.Key(), true) + } + return err +} + +func (b *bloomcache) PutMany(bs []blocks.Block) error { + var good []blocks.Block + for _, block := range bs { + if has, ok := b.hasCached(block.Key()); !ok || (ok && !has) { + good = append(good, block) + } + } + err := b.blockstore.PutMany(bs) + if err == nil { + for _, block := range bs { + b.bloom.AddTS([]byte(block.Key())) + } + } + return err +} + +func (b *bloomcache) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { + return b.blockstore.AllKeysChan(ctx) +} + +func (b *bloomcache) GCLock() Unlocker { + return b.blockstore.(GCBlockstore).GCLock() +} + +func (b *bloomcache) PinLock() Unlocker { + return b.blockstore.(GCBlockstore).PinLock() +} + +func (b *bloomcache) GCRequested() bool { + return b.blockstore.(GCBlockstore).GCRequested() +} diff --git a/blockstore/write_cache_test.go b/blockstore/bloom_cache_test.go similarity index 66% rename from blockstore/write_cache_test.go rename to blockstore/bloom_cache_test.go index 966ff0610..6c9f13cdc 100644 --- a/blockstore/write_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -1,28 +1,32 @@ package blockstore import ( - "testing" - + "fmt" "github.com/ipfs/go-ipfs/blocks" ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" dsq "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/query" syncds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" + "testing" + "time" ) func TestReturnsErrorWhenSizeNegative(t *testing.T) { bs := NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore())) - _, err := WriteCached(bs, -1) - if err != nil { - return + _, err := BloomCached(bs, 100, -1) + if err == nil { + t.Fail() + } + _, err = BloomCached(bs, -1, 100) + if err == nil { + t.Fail() } - t.Fail() } func TestRemoveCacheEntryOnDelete(t *testing.T) { b := blocks.NewBlock([]byte("foo")) cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} bs := NewBlockstore(syncds.MutexWrap(cd)) - cachedbs, err := WriteCached(bs, 1) + cachedbs, err := BloomCached(bs, 1, 1) if err != nil { t.Fatal(err) } @@ -43,7 +47,7 @@ func TestRemoveCacheEntryOnDelete(t *testing.T) { func TestElideDuplicateWrite(t *testing.T) { cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} bs := NewBlockstore(syncds.MutexWrap(cd)) - cachedbs, err := WriteCached(bs, 1) + cachedbs, err := BloomCached(bs, 1, 1) if err != nil { t.Fatal(err) } @@ -56,6 +60,37 @@ func TestElideDuplicateWrite(t *testing.T) { }) cachedbs.Put(b1) } +func TestHasIsBloomCached(t *testing.T) { + cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} + bs := NewBlockstore(syncds.MutexWrap(cd)) + + for i := 0; i < 1000; i++ { + bs.Put(blocks.NewBlock([]byte(fmt.Sprintf("data: %d", i)))) + } + cachedbs, err := BloomCached(bs, 256*1024, 128) + if err != nil { + t.Fatal(err) + } + + select { + case <-cachedbs.rebuildChan: + case <-time.After(1 * time.Second): + t.Fatalf("Timeout wating for rebuild: %d", cachedbs.bloom.ElementsAdded()) + } + + cacheFails := 0 + cd.SetFunc(func() { + cacheFails++ + }) + + for i := 0; i < 1000; i++ { + cachedbs.Has(blocks.NewBlock([]byte(fmt.Sprintf("data: %d", i+2000))).Key()) + } + + if float64(cacheFails)/float64(1000) > float64(0.05) { + t.Fatal("Bloom filter has cache miss rate of more than 5%") + } +} type callbackDatastore struct { f func() diff --git a/blockstore/write_cache.go b/blockstore/write_cache.go deleted file mode 100644 index fbeee25ba..000000000 --- a/blockstore/write_cache.go +++ /dev/null @@ -1,78 +0,0 @@ -package blockstore - -import ( - "github.com/ipfs/go-ipfs/blocks" - key "github.com/ipfs/go-ipfs/blocks/key" - "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" -) - -// WriteCached returns a blockstore that caches up to |size| unique writes (bs.Put). -func WriteCached(bs Blockstore, size int) (*writecache, error) { - c, err := lru.New(size) - if err != nil { - return nil, err - } - return &writecache{blockstore: bs, cache: c}, nil -} - -type writecache struct { - cache *lru.Cache // pointer b/c Cache contains a Mutex as value (complicates copying) - blockstore Blockstore -} - -func (w *writecache) DeleteBlock(k key.Key) error { - defer log.EventBegin(context.TODO(), "writecache.BlockRemoved", &k).Done() - w.cache.Remove(k) - return w.blockstore.DeleteBlock(k) -} - -func (w *writecache) Has(k key.Key) (bool, error) { - if _, ok := w.cache.Get(k); ok { - return true, nil - } - return w.blockstore.Has(k) -} - -func (w *writecache) Get(k key.Key) (blocks.Block, error) { - return w.blockstore.Get(k) -} - -func (w *writecache) Put(b blocks.Block) error { - k := b.Key() - if _, ok := w.cache.Get(k); ok { - return nil - } - defer log.EventBegin(context.TODO(), "writecache.BlockAdded", &k).Done() - - w.cache.Add(b.Key(), struct{}{}) - return w.blockstore.Put(b) -} - -func (w *writecache) PutMany(bs []blocks.Block) error { - var good []blocks.Block - for _, b := range bs { - if _, ok := w.cache.Get(b.Key()); !ok { - good = append(good, b) - k := b.Key() - defer log.EventBegin(context.TODO(), "writecache.BlockAdded", &k).Done() - } - } - return w.blockstore.PutMany(good) -} - -func (w *writecache) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { - return w.blockstore.AllKeysChan(ctx) -} - -func (w *writecache) GCLock() Unlocker { - return w.blockstore.(GCBlockstore).GCLock() -} - -func (w *writecache) PinLock() Unlocker { - return w.blockstore.(GCBlockstore).PinLock() -} - -func (w *writecache) GCRequested() bool { - return w.blockstore.(GCBlockstore).GCRequested() -} From 67b72ce57dc65642a9391d72186f61d53f22f1ce Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 4 Jul 2016 20:12:13 +0200 Subject: [PATCH 1389/3817] blocks/blockstorage: use automic for bloom.active License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@0858a213d01717cd9a7d51de0a5fad876e91ef60 --- blockstore/bloom_cache.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index ca61d755a..bafa23239 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -7,6 +7,8 @@ import ( bloom "gx/ipfs/QmWQ2SJisXwcCLsUXLwYCKSfyExXjFRW2WbBH5sqCUnwX5/bbloom" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + + "sync/atomic" ) // BloomCached returns Blockstore that caches Has requests using Bloom filter @@ -29,7 +31,7 @@ func BloomCached(bs Blockstore, bloomSize, lruSize int) (*bloomcache, error) { type bloomcache struct { bloom *bloom.Bloom - active bool + active int32 arc *lru.ARCCache // This chan is only used for testing to wait for bloom to enable @@ -43,11 +45,11 @@ type bloomcache struct { func (b *bloomcache) Invalidate() { b.rebuildChan = make(chan struct{}) - b.active = false + atomic.StoreInt32(&b.active, 0) } func (b *bloomcache) BloomActive() bool { - return b.active + return atomic.LoadInt32(&b.active) != 0 } func (b *bloomcache) Rebuild() { @@ -64,7 +66,7 @@ func (b *bloomcache) Rebuild() { b.bloom.AddTS([]byte(key)) // Use binary key, the more compact the better } close(b.rebuildChan) - b.active = true + atomic.StoreInt32(&b.active, 1) } func (b *bloomcache) DeleteBlock(k key.Key) error { @@ -89,7 +91,7 @@ func (b *bloomcache) hasCached(k key.Key) (has bool, ok bool) { if k == "" { return true, true } - if b.active { + if b.BloomActive() { blr := b.bloom.HasTS([]byte(k)) if blr == false { // not contained in bloom is only conclusive answer bloom gives return blr, true From 002b484afe9ab6db4e6312b4c0fd5b6d0af938c9 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 4 Jul 2016 20:34:07 +0200 Subject: [PATCH 1390/3817] test: fix races in bloomcache tests License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@61a4d9b3aa5ee3dbaa5cbf706aa81175ea61e733 --- blockstore/bloom_cache_test.go | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 6c9f13cdc..3b8bb8b91 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -2,12 +2,15 @@ package blockstore import ( "fmt" + "sync" + "testing" + "time" + "github.com/ipfs/go-ipfs/blocks" + ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" dsq "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/query" syncds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" - "testing" - "time" ) func TestReturnsErrorWhenSizeNegative(t *testing.T) { @@ -32,7 +35,10 @@ func TestRemoveCacheEntryOnDelete(t *testing.T) { } cachedbs.Put(b) + cd.Lock() writeHitTheDatastore := false + cd.Unlock() + cd.SetFunc(func() { writeHitTheDatastore = true }) @@ -93,34 +99,45 @@ func TestHasIsBloomCached(t *testing.T) { } type callbackDatastore struct { + sync.Mutex f func() ds ds.Datastore } -func (c *callbackDatastore) SetFunc(f func()) { c.f = f } +func (c *callbackDatastore) SetFunc(f func()) { + c.Lock() + defer c.Unlock() + c.f = f +} + +func (c *callbackDatastore) CallF() { + c.Lock() + defer c.Unlock() + c.f() +} func (c *callbackDatastore) Put(key ds.Key, value interface{}) (err error) { - c.f() + c.CallF() return c.ds.Put(key, value) } func (c *callbackDatastore) Get(key ds.Key) (value interface{}, err error) { - c.f() + c.CallF() return c.ds.Get(key) } func (c *callbackDatastore) Has(key ds.Key) (exists bool, err error) { - c.f() + c.CallF() return c.ds.Has(key) } func (c *callbackDatastore) Delete(key ds.Key) (err error) { - c.f() + c.CallF() return c.ds.Delete(key) } func (c *callbackDatastore) Query(q dsq.Query) (dsq.Results, error) { - c.f() + c.CallF() return c.ds.Query(q) } From 2ba791d2793c91120b9e812848225588818e4e2a Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 4 Jul 2016 20:34:46 +0200 Subject: [PATCH 1391/3817] blocks/blockstore: style cleanup of bloomcache License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@aa8ea8f78ada9a4f77f87242f556276d4a9b6532 --- blockstore/bloom_cache.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index bafa23239..3e4038869 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -76,32 +76,36 @@ func (b *bloomcache) DeleteBlock(k key.Key) error { b.arc.Remove(k) // Invalidate cache before deleting. err := b.blockstore.DeleteBlock(k) - if err == nil { + switch err { + case nil: b.arc.Add(k, false) - } else if err == ds.ErrNotFound || err == ErrNotFound { + case ds.ErrNotFound, ErrNotFound: b.arc.Add(k, false) - return ErrNotFound + default: + return err } - return err + return nil } // if ok == false has is inconclusive // if ok == true then has respons to question: is it contained func (b *bloomcache) hasCached(k key.Key) (has bool, ok bool) { if k == "" { - return true, true + // Return cache invalid so call to blockstore + // in case of invalid key is forwarded deeper + return false, false } if b.BloomActive() { blr := b.bloom.HasTS([]byte(k)) if blr == false { // not contained in bloom is only conclusive answer bloom gives - return blr, true + return false, true } } h, ok := b.arc.Get(k) if ok { return h.(bool), ok } else { - return false, ok + return false, false } } From 7979e8fa3e61f964a1482724c47ac72f56bf0e97 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 4 Jul 2016 12:27:26 -0700 Subject: [PATCH 1392/3817] update go-libp2p License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@d9b322b937e4aac2c984d7dcf1a1b01f4b1798f9 --- routing/dht/dht.go | 4 ++-- routing/dht/dht_net.go | 2 +- routing/dht/dht_test.go | 2 +- routing/dht/ext_test.go | 4 ++-- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/routing.go | 2 +- routing/mock/dht.go | 2 +- routing/none/none_client.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 4 ++-- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 71f21f995..4aab2bce7 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -22,9 +22,9 @@ import ( goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" + host "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/host" + protocol "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/protocol" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - host "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/host" - protocol "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/protocol" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" ) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 2c8232c5d..405ae1572 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -7,9 +7,9 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + inet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net" ctxio "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/io" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - inet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 018f72215..e5ce56fea 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -19,8 +19,8 @@ import ( pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + netutil "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/test/util" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - netutil "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/test/util" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 3e33fd0f0..a675c5d6b 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -14,9 +14,9 @@ import ( dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + inet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net/mock" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - inet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net/mock" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 6ce249e4f..3fdd435fe 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - inet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net" + inet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index e35f40891..26b261535 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -7,7 +7,7 @@ import ( logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - inet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net" + inet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 9a2ca14aa..1ac8983fc 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -16,7 +16,7 @@ import ( pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - inet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net" + inet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index bf729f11d..5a017d55b 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,7 +3,7 @@ package mockrouting import ( dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" sync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" diff --git a/routing/none/none_client.go b/routing/none/none_client.go index ee57000a4..140c3f84e 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -9,7 +9,7 @@ import ( logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - p2phost "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/host" + p2phost "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/host" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 4d3b3d7b0..cba449742 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -14,8 +14,8 @@ import ( logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/host" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/host" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 730cef6bf..2dc2e7721 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -6,7 +6,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - inet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net" + inet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index a736b5753..438cdd99f 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -9,8 +9,8 @@ import ( logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - host "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/host" - inet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net" + host "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/host" + inet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" From 3b147d01ab7af1d15c893e4cc7a684ce4718cb8c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 4 Jul 2016 12:27:26 -0700 Subject: [PATCH 1393/3817] update go-libp2p License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@2dac68bb94b954d426131ec965e12e8d3911395e --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 57f706d0f..26056ca97 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - mocknet "gx/ipfs/QmZ8bCZpMWDbFSh6h2zgTYwrhnjrGM5c9WCzw72SU8p63b/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { From 26cead531077395b71b1f4ac99aad48e3941be5b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 5 Jul 2016 12:19:54 -0700 Subject: [PATCH 1394/3817] fix handling of dht records and local fixups License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@7faccda63d01a075414a30ff4649493047f49ee2 --- routing/dht/routing.go | 7 +++++++ routing/record/validation.go | 14 +++++++++---- routing/record/validation_test.go | 35 +++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 routing/record/validation_test.go diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 9a2ca14aa..49c82f19c 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -122,6 +122,13 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key key.Key) ([]byte, error) { // if someone sent us a different 'less-valid' record, lets correct them if !bytes.Equal(v.Val, best) { go func(v routing.RecvdVal) { + if v.From == dht.self { + err := dht.putLocal(key, fixupRec) + if err != nil { + log.Error("Error correcting local dht entry:", err) + } + return + } ctx, cancel := context.WithTimeout(dht.Context(), time.Second*30) defer cancel() err := dht.putValueToPeer(ctx, v.From, key, fixupRec) diff --git a/routing/record/validation.go b/routing/record/validation.go index 4cce81b2a..16bf60090 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -73,13 +73,19 @@ func (v Validator) IsSigned(k key.Key) (bool, error) { // verifies that the passed in record value is the PublicKey // that matches the passed in key. func ValidatePublicKeyRecord(k key.Key, val []byte) error { - keyparts := bytes.Split([]byte(k), []byte("/")) - if len(keyparts) < 3 { - return errors.New("invalid key") + if len(k) != 38 { + return errors.New("invalid public key record key") } + prefix := string(k[:4]) + if prefix != "/pk/" { + return errors.New("key was not prefixed with /pk/") + } + + keyhash := []byte(k[4:]) + pkh := u.Hash(val) - if !bytes.Equal(keyparts[2], pkh) { + if !bytes.Equal(keyhash, pkh) { return errors.New("public key does not match storage key") } return nil diff --git a/routing/record/validation_test.go b/routing/record/validation_test.go new file mode 100644 index 000000000..ae389244e --- /dev/null +++ b/routing/record/validation_test.go @@ -0,0 +1,35 @@ +package record + +import ( + "encoding/base64" + "testing" + + key "github.com/ipfs/go-ipfs/blocks/key" + ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" +) + +var OffensiveKey = "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDjXAQQMal4SB2tSnX6NJIPmC69/BT8A8jc7/gDUZNkEhdhYHvc7k7S4vntV/c92nJGxNdop9fKJyevuNMuXhhHAgMBAAE=" + +func TestValidatePublicKey(t *testing.T) { + pkb, err := base64.StdEncoding.DecodeString(OffensiveKey) + if err != nil { + t.Fatal(err) + } + + pubk, err := ci.UnmarshalPublicKey(pkb) + if err != nil { + t.Fatal(err) + } + + pkh, err := pubk.Hash() + if err != nil { + t.Fatal(err) + } + + k := key.Key("/pk/" + string(pkh)) + + err = ValidatePublicKeyRecord(k, pkb) + if err != nil { + t.Fatal(err) + } +} From 1f78b07a152510a9ff2b1e6bdb857d5c3c8a6e1a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 5 Jul 2016 13:24:13 -0700 Subject: [PATCH 1395/3817] better checking of dht keys License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@7bc2ead2153e2fb53b44111399b906e43ac0bf63 --- routing/record/validation.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/routing/record/validation.go b/routing/record/validation.go index 16bf60090..23d892236 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -3,11 +3,13 @@ package record import ( "bytes" "errors" + "fmt" key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" + mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) @@ -73,7 +75,7 @@ func (v Validator) IsSigned(k key.Key) (bool, error) { // verifies that the passed in record value is the PublicKey // that matches the passed in key. func ValidatePublicKeyRecord(k key.Key, val []byte) error { - if len(k) != 38 { + if len(k) < 5 { return errors.New("invalid public key record key") } @@ -83,6 +85,9 @@ func ValidatePublicKeyRecord(k key.Key, val []byte) error { } keyhash := []byte(k[4:]) + if _, err := mh.Cast(keyhash); err != nil { + return fmt.Errorf("key did not contain valid multihash: %s", err) + } pkh := u.Hash(val) if !bytes.Equal(keyhash, pkh) { From b8d716f11d23b5bc4282f03ee2dc2c7de10def29 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 4 Jul 2016 20:08:18 +0200 Subject: [PATCH 1396/3817] blocks/blockstore: add CacheOpts - structure of cache config License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@09d9204bf852e114e91720c335b025d613c312ec --- blockstore/caching.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 blockstore/caching.go diff --git a/blockstore/caching.go b/blockstore/caching.go new file mode 100644 index 000000000..5657059d8 --- /dev/null +++ b/blockstore/caching.go @@ -0,0 +1,16 @@ +package blockstore + +// Next to each option is it aproximate memory usage per unit +type CacheOpts struct { + HasBloomFilterSize int // 1 bit + HasBloomFilterHashes int // No size, 7 is usually best, consult bloom papers + HasARCCacheSize int // 32 bytes +} + +func DefaultCacheOpts() CacheOpts { + return CacheOpts{ + HasBloomFilterSize: 512 * 8 * 1024, + HasBloomFilterHashes: 7, + HasARCCacheSize: 64 * 1024, + } +} From d463e86e65d2fe3765dc6873f6a1f01ed7482108 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 4 Jul 2016 23:02:29 +0200 Subject: [PATCH 1397/3817] blocks/blockstore: introduce context passing to blockstore License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@17a64ee5b588423e30b7c401fbc9ffc14a195ded --- blockstore/bloom_cache.go | 26 ++++++++++++++++++-------- blockstore/bloom_cache_test.go | 24 ++++++++++++++++++------ blockstore/caching.go | 26 ++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 3e4038869..d9f93b3e8 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -11,10 +11,10 @@ import ( "sync/atomic" ) -// BloomCached returns Blockstore that caches Has requests using Bloom filter +// bloomCached returns Blockstore that caches Has requests using Bloom filter // Size is size of bloom filter in bytes -func BloomCached(bs Blockstore, bloomSize, lruSize int) (*bloomcache, error) { - bl, err := bloom.New(float64(bloomSize), float64(7)) +func bloomCached(bs Blockstore, ctx context.Context, bloomSize, hashCount, lruSize int) (*bloomcache, error) { + bl, err := bloom.New(float64(bloomSize), float64(hashCount)) if err != nil { return nil, err } @@ -24,7 +24,7 @@ func BloomCached(bs Blockstore, bloomSize, lruSize int) (*bloomcache, error) { } bc := &bloomcache{blockstore: bs, bloom: bl, arc: arc} bc.Invalidate() - go bc.Rebuild() + go bc.Rebuild(ctx) return bc, nil } @@ -52,8 +52,7 @@ func (b *bloomcache) BloomActive() bool { return atomic.LoadInt32(&b.active) != 0 } -func (b *bloomcache) Rebuild() { - ctx := context.TODO() +func (b *bloomcache) Rebuild(ctx context.Context) { evt := log.EventBegin(ctx, "bloomcache.Rebuild") defer evt.Done() @@ -62,8 +61,19 @@ func (b *bloomcache) Rebuild() { log.Errorf("AllKeysChan failed in bloomcache rebuild with: %v", err) return } - for key := range ch { - b.bloom.AddTS([]byte(key)) // Use binary key, the more compact the better + finish := false + for !finish { + select { + case key, ok := <-ch: + if ok { + b.bloom.AddTS([]byte(key)) // Use binary key, the more compact the better + } else { + finish = true + } + case <-ctx.Done(): + log.Warning("Cache rebuild closed by context finishing.") + return + } } close(b.rebuildChan) atomic.StoreInt32(&b.active, 1) diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 3b8bb8b91..768bca750 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -8,18 +8,29 @@ import ( "github.com/ipfs/go-ipfs/blocks" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" dsq "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/query" syncds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) +func testBloomCached(bs GCBlockstore, ctx context.Context) (*bloomcache, error) { + opts := DefaultCacheOpts() + bbs, err := CachedBlockstore(bs, ctx, opts) + if err == nil { + return bbs.(*bloomcache), nil + } else { + return nil, err + } +} + func TestReturnsErrorWhenSizeNegative(t *testing.T) { bs := NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore())) - _, err := BloomCached(bs, 100, -1) + _, err := bloomCached(bs, nil, 100, 1, -1) if err == nil { t.Fail() } - _, err = BloomCached(bs, -1, 100) + _, err = bloomCached(bs, nil, -1, 1, 100) if err == nil { t.Fail() } @@ -29,7 +40,7 @@ func TestRemoveCacheEntryOnDelete(t *testing.T) { b := blocks.NewBlock([]byte("foo")) cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} bs := NewBlockstore(syncds.MutexWrap(cd)) - cachedbs, err := BloomCached(bs, 1, 1) + cachedbs, err := testBloomCached(bs, nil) if err != nil { t.Fatal(err) } @@ -53,7 +64,7 @@ func TestRemoveCacheEntryOnDelete(t *testing.T) { func TestElideDuplicateWrite(t *testing.T) { cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} bs := NewBlockstore(syncds.MutexWrap(cd)) - cachedbs, err := BloomCached(bs, 1, 1) + cachedbs, err := testBloomCached(bs, nil) if err != nil { t.Fatal(err) } @@ -73,14 +84,15 @@ func TestHasIsBloomCached(t *testing.T) { for i := 0; i < 1000; i++ { bs.Put(blocks.NewBlock([]byte(fmt.Sprintf("data: %d", i)))) } - cachedbs, err := BloomCached(bs, 256*1024, 128) + ctx, _ := context.WithTimeout(context.Background(), 1*time.Second) + cachedbs, err := testBloomCached(bs, ctx) if err != nil { t.Fatal(err) } select { case <-cachedbs.rebuildChan: - case <-time.After(1 * time.Second): + case <-ctx.Done(): t.Fatalf("Timeout wating for rebuild: %d", cachedbs.bloom.ElementsAdded()) } diff --git a/blockstore/caching.go b/blockstore/caching.go index 5657059d8..b1121f6b1 100644 --- a/blockstore/caching.go +++ b/blockstore/caching.go @@ -1,5 +1,11 @@ package blockstore +import ( + "errors" + + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" +) + // Next to each option is it aproximate memory usage per unit type CacheOpts struct { HasBloomFilterSize int // 1 bit @@ -14,3 +20,23 @@ func DefaultCacheOpts() CacheOpts { HasARCCacheSize: 64 * 1024, } } + +func CachedBlockstore(bs GCBlockstore, + ctx context.Context, opts CacheOpts) (cbs GCBlockstore, err error) { + if ctx == nil { + ctx = context.TODO() // For tests + } + + if opts.HasBloomFilterSize < 0 || opts.HasBloomFilterHashes < 0 || + opts.HasARCCacheSize < 0 { + return nil, errors.New("all options for cache need to be greater than zero") + } + + if opts.HasBloomFilterSize != 0 && opts.HasBloomFilterHashes == 0 { + return nil, errors.New("bloom filter hash count can't be 0 when there is size set") + } + cbs, err = bloomCached(bs, ctx, opts.HasBloomFilterSize, opts.HasBloomFilterHashes, + opts.HasARCCacheSize) + + return cbs, err +} From a1e04e869984f2bbccaff62effaf63d0a22ffe5f Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 5 Jul 2016 15:17:52 +0200 Subject: [PATCH 1398/3817] blocks/blockstore: improve logic a bit License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@6bd011468acdbb87b8c02323c6a9c5e9223b9d86 --- blockstore/caching.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/blockstore/caching.go b/blockstore/caching.go index b1121f6b1..3decc9e8d 100644 --- a/blockstore/caching.go +++ b/blockstore/caching.go @@ -26,6 +26,7 @@ func CachedBlockstore(bs GCBlockstore, if ctx == nil { ctx = context.TODO() // For tests } + cbs = bs if opts.HasBloomFilterSize < 0 || opts.HasBloomFilterHashes < 0 || opts.HasARCCacheSize < 0 { @@ -35,8 +36,10 @@ func CachedBlockstore(bs GCBlockstore, if opts.HasBloomFilterSize != 0 && opts.HasBloomFilterHashes == 0 { return nil, errors.New("bloom filter hash count can't be 0 when there is size set") } - cbs, err = bloomCached(bs, ctx, opts.HasBloomFilterSize, opts.HasBloomFilterHashes, - opts.HasARCCacheSize) + if opts.HasBloomFilterSize != 0 { + cbs, err = bloomCached(cbs, ctx, opts.HasBloomFilterSize, opts.HasBloomFilterHashes, + opts.HasARCCacheSize) + } return cbs, err } From f1cc4274af50c824e823273d8c15aa1f974319a9 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 5 Jul 2016 16:59:23 +0200 Subject: [PATCH 1399/3817] block/blockstore: bloomcache PutMany logic was not adding to ARC License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@b5293178fe6b0a116bd37cef541ddc466743e821 --- blockstore/bloom_cache.go | 1 + 1 file changed, 1 insertion(+) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index d9f93b3e8..1a4c57ac4 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -169,6 +169,7 @@ func (b *bloomcache) PutMany(bs []blocks.Block) error { if err == nil { for _, block := range bs { b.bloom.AddTS([]byte(block.Key())) + b.arc.Add(block.Key(), true) } } return err From 7eaee7743acd7b9849cc7b65914f5e93e7580a90 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sun, 10 Jul 2016 15:16:42 +0200 Subject: [PATCH 1400/3817] blocks/blockstore: shift insertion of TODO context to tests License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@e34e230d0e474f469bdcbda605b020dbd0046006 --- blockstore/bloom_cache_test.go | 7 +++++-- blockstore/caching.go | 3 --- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 768bca750..bddcee56c 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -15,6 +15,9 @@ import ( ) func testBloomCached(bs GCBlockstore, ctx context.Context) (*bloomcache, error) { + if ctx == nil { + ctx = context.TODO() + } opts := DefaultCacheOpts() bbs, err := CachedBlockstore(bs, ctx, opts) if err == nil { @@ -26,11 +29,11 @@ func testBloomCached(bs GCBlockstore, ctx context.Context) (*bloomcache, error) func TestReturnsErrorWhenSizeNegative(t *testing.T) { bs := NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore())) - _, err := bloomCached(bs, nil, 100, 1, -1) + _, err := bloomCached(bs, context.TODO(), 100, 1, -1) if err == nil { t.Fail() } - _, err = bloomCached(bs, nil, -1, 1, 100) + _, err = bloomCached(bs, context.TODO(), -1, 1, 100) if err == nil { t.Fail() } diff --git a/blockstore/caching.go b/blockstore/caching.go index 3decc9e8d..689a9b5fc 100644 --- a/blockstore/caching.go +++ b/blockstore/caching.go @@ -23,9 +23,6 @@ func DefaultCacheOpts() CacheOpts { func CachedBlockstore(bs GCBlockstore, ctx context.Context, opts CacheOpts) (cbs GCBlockstore, err error) { - if ctx == nil { - ctx = context.TODO() // For tests - } cbs = bs if opts.HasBloomFilterSize < 0 || opts.HasBloomFilterHashes < 0 || From ae9de138661036c59a7667d2794494c9e93a2a0a Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sun, 10 Jul 2016 15:35:27 -0400 Subject: [PATCH 1401/3817] Increase channel buffer size in blockstore.AllKeysChan(). License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-blockstore@c12d54cbf219c979937c09b8dbaa64307df95e69 --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 477141cd6..605be2bd8 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -196,7 +196,7 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { } } - output := make(chan key.Key) + output := make(chan key.Key, dsq.KeysOnlyBufSize) go func() { defer func() { res.Process().Close() // ensure exit (signals early exit, too) From 6f8ddb716fbe474a8c9795967948ded19066abaf Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 10 Jul 2016 00:40:23 -0700 Subject: [PATCH 1402/3817] cache encoded data when reading dag nodes from disk License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@12d9e261df3646007065b56f0fd07d254fcdfac8 --- mfs/dir.go | 5 +++-- mfs/file.go | 4 ++-- mfs/mfs_test.go | 36 +++++++++++++++++++----------------- mfs/system.go | 2 +- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index fba61ea4c..cf178e452 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -121,7 +121,7 @@ func (d *Directory) childNode(name string) (FSNode, error) { // cacheNode caches a node into d.childDirs or d.files and returns the FSNode. func (d *Directory) cacheNode(name string, nd *dag.Node) (FSNode, error) { - i, err := ft.FromBytes(nd.Data) + i, err := ft.FromBytes(nd.Data()) if err != nil { return nil, err } @@ -268,7 +268,8 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { } } - ndir := &dag.Node{Data: ft.FolderPBData()} + ndir := new(dag.Node) + ndir.SetData(ft.FolderPBData()) _, err = d.dserv.Add(ndir) if err != nil { diff --git a/mfs/file.go b/mfs/file.go index 216bdfa75..46ca7314b 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -45,7 +45,7 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { node := fi.node fi.nodelk.Unlock() - fsn, err := ft.FSNodeFromBytes(node.Data) + fsn, err := ft.FSNodeFromBytes(node.Data()) if err != nil { return nil, err } @@ -86,7 +86,7 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { func (fi *File) Size() (int64, error) { fi.nodelk.Lock() defer fi.nodelk.Unlock() - pbd, err := ft.FromBytes(fi.node.Data) + pbd, err := ft.FromBytes(fi.node.Data()) if err != nil { return 0, err } diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 84ce22668..3069fb13c 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -31,6 +31,10 @@ import ( u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) +func emptyDirNode() *dag.Node { + return dag.NodeWithData(ft.FolderPBData()) +} + func getDagserv(t *testing.T) dag.DAGService { db := dssync.MutexWrap(ds.NewMapDatastore()) bs := bstore.NewBlockstore(db) @@ -183,7 +187,7 @@ func catNode(ds dag.DAGService, nd *dag.Node) ([]byte, error) { func setupRoot(ctx context.Context, t *testing.T) (dag.DAGService, *Root) { ds := getDagserv(t) - root := &dag.Node{Data: ft.FolderPBData()} + root := emptyDirNode() rt, err := NewRoot(ctx, ds, root, func(ctx context.Context, k key.Key) error { fmt.Println("PUBLISHED: ", k) return nil @@ -282,7 +286,7 @@ func TestDirectoryLoadFromDag(t *testing.T) { t.Fatal(err) } - dir := &dag.Node{Data: ft.FolderPBData()} + dir := emptyDirNode() _, err = ds.Add(dir) if err != nil { t.Fatal(err) @@ -293,17 +297,15 @@ func TestDirectoryLoadFromDag(t *testing.T) { t.Fatal(err) } - top := &dag.Node{ - Data: ft.FolderPBData(), - Links: []*dag.Link{ - &dag.Link{ - Name: "a", - Hash: fihash, - }, - &dag.Link{ - Name: "b", - Hash: dirhash, - }, + top := emptyDirNode() + top.Links = []*dag.Link{ + &dag.Link{ + Name: "a", + Hash: fihash, + }, + &dag.Link{ + Name: "b", + Hash: dirhash, }, } @@ -540,7 +542,7 @@ func actorMakeFile(d *Directory) error { } name := randomName() - f, err := NewFile(name, &dag.Node{Data: ft.FilePBData(nil, 0)}, d, d.dserv) + f, err := NewFile(name, dag.NodeWithData(ft.FilePBData(nil, 0)), d, d.dserv) if err != nil { return err } @@ -756,7 +758,7 @@ func TestFlushing(t *testing.T) { e := mkdirP(t, dir, "a/b/e") data := []byte("this is a test\n") - nd1 := &dag.Node{Data: ft.FilePBData(data, uint64(len(data)))} + nd1 := dag.NodeWithData(ft.FilePBData(data, uint64(len(data)))) if err := c.AddChild("TEST", nd1); err != nil { t.Fatal(err) @@ -792,7 +794,7 @@ func TestFlushing(t *testing.T) { t.Fatal(err) } - fsnode, err := ft.FSNodeFromBytes(rnd.Data) + fsnode, err := ft.FSNodeFromBytes(rnd.Data()) if err != nil { t.Fatal(err) } @@ -897,7 +899,7 @@ func TestFileDescriptors(t *testing.T) { ds, rt := setupRoot(ctx, t) dir := rt.GetValue().(*Directory) - nd := &dag.Node{Data: ft.FilePBData(nil, 0)} + nd := dag.NodeWithData(ft.FilePBData(nil, 0)) fi, err := NewFile("test", nd, dir, ds) if err != nil { t.Fatal(err) diff --git a/mfs/system.go b/mfs/system.go index 15e7c7684..40d9d29cd 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -83,7 +83,7 @@ func NewRoot(parent context.Context, ds dag.DAGService, node *dag.Node, pf PubFu dserv: ds, } - pbn, err := ft.FromBytes(node.Data) + pbn, err := ft.FromBytes(node.Data()) if err != nil { log.Error("IPNS pointer was not unixfs node") return nil, err From 0f8cbb60eae7abadbb6111410105315ba6060b93 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 10 Jul 2016 00:40:23 -0700 Subject: [PATCH 1403/3817] cache encoded data when reading dag nodes from disk License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@defd7d6aa1da4acd3b7eb260eb590c728adecfd2 --- namesys/publisher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 65c482655..eebb3153e 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -330,7 +330,7 @@ func ValidateIpnsRecord(k key.Key, val []byte) error { // point to an empty directory. // TODO: this doesnt feel like it belongs here func InitializeKeyspace(ctx context.Context, ds dag.DAGService, pub Publisher, pins pin.Pinner, key ci.PrivKey) error { - emptyDir := &dag.Node{Data: ft.FolderPBData()} + emptyDir := ft.EmptyDirNode() nodek, err := ds.Add(emptyDir) if err != nil { return err From 427900ef5db455fec92a498ab10afc3b233e0832 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 10 Jul 2016 00:40:23 -0700 Subject: [PATCH 1404/3817] cache encoded data when reading dag nodes from disk License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@0cabbe550feeb1d7da538107e161ad0d14d2cddc --- unixfs/archive/tar/writer.go | 2 +- unixfs/format.go | 5 +++++ unixfs/io/dagreader.go | 4 ++-- unixfs/io/dirbuilder.go | 4 +++- unixfs/mod/dagmodifier.go | 15 ++++++++------- unixfs/mod/dagmodifier_test.go | 4 ++-- 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index ad151016a..3d1f47eea 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -69,7 +69,7 @@ func (w *Writer) writeFile(nd *mdag.Node, pb *upb.Data, fpath string) error { func (w *Writer) WriteNode(nd *mdag.Node, fpath string) error { pb := new(upb.Data) - if err := proto.Unmarshal(nd.Data, pb); err != nil { + if err := proto.Unmarshal(nd.Data(), pb); err != nil { return err } diff --git a/unixfs/format.go b/unixfs/format.go index f279a8843..0235f0c7c 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -6,6 +6,7 @@ package unixfs import ( "errors" + dag "github.com/ipfs/go-ipfs/merkledag" pb "github.com/ipfs/go-ipfs/unixfs/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) @@ -222,3 +223,7 @@ func BytesForMetadata(m *Metadata) ([]byte, error) { pbd.Data = mdd return proto.Marshal(pbd) } + +func EmptyDirNode() *dag.Node { + return dag.NodeWithData(FolderPBData()) +} diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index b78b46269..cbbe02858 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -60,7 +60,7 @@ type ReadSeekCloser interface { // the given node, using the passed in DAGService for data retreival func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (*DagReader, error) { pb := new(ftpb.Data) - if err := proto.Unmarshal(n.Data, pb); err != nil { + if err := proto.Unmarshal(n.Data(), pb); err != nil { return nil, err } @@ -117,7 +117,7 @@ func (dr *DagReader) precalcNextBuf(ctx context.Context) error { dr.linkPosition++ pb := new(ftpb.Data) - err = proto.Unmarshal(nxt.Data, pb) + err = proto.Unmarshal(nxt.Data(), pb) if err != nil { return fmt.Errorf("incorrectly formatted protobuf: %s", err) } diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 8dad9a16d..3db0b9ef9 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -15,7 +15,9 @@ type directoryBuilder struct { // NewEmptyDirectory returns an empty merkledag Node with a folder Data chunk func NewEmptyDirectory() *mdag.Node { - return &mdag.Node{Data: format.FolderPBData()} + nd := new(mdag.Node) + nd.SetData(format.FolderPBData()) + return nd } // NewDirectory returns a directoryBuilder. It needs a DAGService to add the Children diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index cb3cfd589..66ba5f24b 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -139,7 +139,7 @@ func (dm *DagModifier) Write(b []byte) (int, error) { } func (dm *DagModifier) Size() (int64, error) { - pbn, err := ft.FromBytes(dm.curNode.Data) + pbn, err := ft.FromBytes(dm.curNode.Data()) if err != nil { return 0, err } @@ -207,7 +207,7 @@ func (dm *DagModifier) Sync() error { // returns the new key of the passed in node and whether or not all the data in the reader // has been consumed. func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) (key.Key, bool, error) { - f, err := ft.FromBytes(node.Data) + f, err := ft.FromBytes(node.Data()) if err != nil { return "", false, err } @@ -225,7 +225,8 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) return "", false, err } - nd := &mdag.Node{Data: b} + nd := new(mdag.Node) + nd.SetData(b) k, err := dm.dagserv.Add(nd) if err != nil { return "", false, err @@ -429,12 +430,12 @@ func (dm *DagModifier) Truncate(size int64) error { func dagTruncate(ctx context.Context, nd *mdag.Node, size uint64, ds mdag.DAGService) (*mdag.Node, error) { if len(nd.Links) == 0 { // TODO: this can likely be done without marshaling and remarshaling - pbn, err := ft.FromBytes(nd.Data) + pbn, err := ft.FromBytes(nd.Data()) if err != nil { return nil, err } - nd.Data = ft.WrapData(pbn.Data[:size]) + nd.SetData(ft.WrapData(pbn.Data[:size])) return nd, nil } @@ -448,7 +449,7 @@ func dagTruncate(ctx context.Context, nd *mdag.Node, size uint64, ds mdag.DAGSer return nil, err } - childsize, err := ft.DataSize(child.Data) + childsize, err := ft.DataSize(child.Data()) if err != nil { return nil, err } @@ -486,7 +487,7 @@ func dagTruncate(ctx context.Context, nd *mdag.Node, size uint64, ds mdag.DAGSer return nil, err } - nd.Data = d + nd.SetData(d) // invalidate cache and recompute serialized data _, err = nd.EncodeProtobuf(true) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index dffd205bf..7ed30dd26 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -156,7 +156,7 @@ func TestDagModifierBasic(t *testing.T) { t.Fatal(err) } - size, err := ft.DataSize(node.Data) + size, err := ft.DataSize(node.Data()) if err != nil { t.Fatal(err) } @@ -590,7 +590,7 @@ func arrComp(a, b []byte) error { } func printDag(nd *mdag.Node, ds mdag.DAGService, indent int) { - pbd, err := ft.FromBytes(nd.Data) + pbd, err := ft.FromBytes(nd.Data()) if err != nil { panic(err) } From 571b5c4ed11d07e7ab453ba0f52338db182629e1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 10 Jul 2016 00:40:23 -0700 Subject: [PATCH 1405/3817] cache encoded data when reading dag nodes from disk License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@196e996d982692803a4f8084f60c853d83bae2ae --- ipld/merkledag/coding.go | 11 +++++++--- ipld/merkledag/merkledag.go | 4 ++++ ipld/merkledag/merkledag_test.go | 18 ++++++++-------- ipld/merkledag/node.go | 26 ++++++++++++++++++------ ipld/merkledag/traverse/traverse_test.go | 14 ++++++------- ipld/merkledag/utils/utils_test.go | 6 ++---- 6 files changed, 50 insertions(+), 29 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 10c30727a..2c92b559f 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -33,7 +33,8 @@ func (n *Node) unmarshal(encoded []byte) error { } sort.Stable(LinkSlice(n.Links)) // keep links sorted - n.Data = pbn.GetData() + n.data = pbn.GetData() + n.encoded = encoded return nil } @@ -62,8 +63,8 @@ func (n *Node) getPBNode() *pb.PBNode { pbn.Links[i].Hash = []byte(l.Hash) } - if len(n.Data) > 0 { - pbn.Data = n.Data + if len(n.data) > 0 { + pbn.Data = n.data } return pbn } @@ -73,11 +74,15 @@ func (n *Node) getPBNode() *pb.PBNode { func (n *Node) EncodeProtobuf(force bool) ([]byte, error) { sort.Stable(LinkSlice(n.Links)) // keep links sorted if n.encoded == nil || force { + n.cached = nil var err error n.encoded, err = n.Marshal() if err != nil { return nil, err } + } + + if n.cached == nil { n.cached = u.Hash(n.encoded) } diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 470f45faa..835d26cbf 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -93,6 +93,9 @@ func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) { } return nil, fmt.Errorf("Failed to decode Protocol Buffers: %v", err) } + + res.cached = k.ToMultihash() + return res, nil } @@ -147,6 +150,7 @@ func (ds *dagService) GetMany(ctx context.Context, keys []key.Key) <-chan *NodeO out <- &NodeOption{Err: err} return } + nd.cached = b.Key().ToMultihash() // buffered, no need to select out <- &NodeOption{Node: nd} diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 72bc3ef17..8a679335a 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -46,9 +46,9 @@ func getDagservAndPinner(t *testing.T) dagservAndPinner { func TestNode(t *testing.T) { - n1 := &Node{Data: []byte("beep")} - n2 := &Node{Data: []byte("boop")} - n3 := &Node{Data: []byte("beep boop")} + n1 := NodeWithData([]byte("beep")) + n2 := NodeWithData([]byte("boop")) + n3 := NodeWithData([]byte("beep boop")) if err := n3.AddNodeLink("beep-link", n1); err != nil { t.Error(err) } @@ -58,7 +58,7 @@ func TestNode(t *testing.T) { printn := func(name string, n *Node) { fmt.Println(">", name) - fmt.Println("data:", string(n.Data)) + fmt.Println("data:", string(n.Data())) fmt.Println("links:") for _, l := range n.Links { @@ -118,8 +118,8 @@ func SubtestNodeStat(t *testing.T, n *Node) { expected := NodeStat{ NumLinks: len(n.Links), BlockSize: len(enc), - LinksSize: len(enc) - len(n.Data), // includes framing. - DataSize: len(n.Data), + LinksSize: len(enc) - len(n.Data()), // includes framing. + DataSize: len(n.Data()), CumulativeSize: int(cumSize), Hash: k.B58String(), } @@ -255,7 +255,7 @@ func TestEmptyKey(t *testing.T) { func TestCantGet(t *testing.T) { dsp := getDagservAndPinner(t) - a := &Node{Data: []byte("A")} + a := NodeWithData([]byte("A")) k, err := a.Key() if err != nil { @@ -339,7 +339,7 @@ func TestFetchFailure(t *testing.T) { top := new(Node) for i := 0; i < 10; i++ { - nd := &Node{Data: []byte{byte('a' + i)}} + nd := NodeWithData([]byte{byte('a' + i)}) _, err := ds.Add(nd) if err != nil { t.Fatal(err) @@ -352,7 +352,7 @@ func TestFetchFailure(t *testing.T) { } for i := 0; i < 10; i++ { - nd := &Node{Data: []byte{'f', 'a' + byte(i)}} + nd := NodeWithData([]byte{'f', 'a' + byte(i)}) _, err := ds_bad.Add(nd) if err != nil { t.Fatal(err) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 36479fb75..7be5c4d0a 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -15,7 +15,7 @@ var ErrLinkNotFound = fmt.Errorf("no link by that name") // nodes have opaque data and a set of navigable links. type Node struct { Links []*Link - Data []byte + data []byte // cache encoded/marshaled value encoded []byte @@ -78,6 +78,10 @@ func (l *Link) GetNode(ctx context.Context, serv DAGService) (*Node, error) { return serv.Get(ctx, key.Key(l.Hash)) } +func NodeWithData(d []byte) *Node { + return &Node{data: d} +} + // AddNodeLink adds a link to another node. func (n *Node) AddNodeLink(name string, that *Node) error { n.encoded = nil @@ -168,9 +172,9 @@ func (n *Node) GetLinkedNode(ctx context.Context, ds DAGService, name string) (* // NOTE: Does not make copies of Node objects in the links. func (n *Node) Copy() *Node { nnode := new(Node) - if len(n.Data) > 0 { - nnode.Data = make([]byte, len(n.Data)) - copy(nnode.Data, n.Data) + if len(n.data) > 0 { + nnode.data = make([]byte, len(n.data)) + copy(nnode.data, n.data) } if len(n.Links) > 0 { @@ -180,6 +184,16 @@ func (n *Node) Copy() *Node { return nnode } +func (n *Node) Data() []byte { + return n.data +} + +func (n *Node) SetData(d []byte) { + n.encoded = nil + n.cached = nil + n.data = d +} + // UpdateNodeLink return a copy of the node with the link name set to point to // that. If a link of the same name existed, it is removed. func (n *Node) UpdateNodeLink(name string, that *Node) (*Node, error) { @@ -226,8 +240,8 @@ func (n *Node) Stat() (*NodeStat, error) { Hash: key.B58String(), NumLinks: len(n.Links), BlockSize: len(enc), - LinksSize: len(enc) - len(n.Data), // includes framing. - DataSize: len(n.Data), + LinksSize: len(enc) - len(n.data), // includes framing. + DataSize: len(n.data), CumulativeSize: int(cumSize), }, nil } diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 5ca906a51..2bd344411 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -326,7 +326,7 @@ func testWalkOutputs(t *testing.T, root *mdag.Node, opts Options, expect []byte) buf := new(bytes.Buffer) walk := func(current State) error { - s := fmt.Sprintf("%d %s\n", current.Depth, current.Node.Data) + s := fmt.Sprintf("%d %s\n", current.Depth, current.Node.Data()) t.Logf("walk: %s", s) buf.Write([]byte(s)) return nil @@ -349,7 +349,7 @@ func testWalkOutputs(t *testing.T, root *mdag.Node, opts Options, expect []byte) } func newFan(t *testing.T, ds mdag.DAGService) *mdag.Node { - a := &mdag.Node{Data: []byte("/a")} + a := mdag.NodeWithData([]byte("/a")) addLink(t, ds, a, child(t, ds, a, "aa")) addLink(t, ds, a, child(t, ds, a, "ab")) addLink(t, ds, a, child(t, ds, a, "ac")) @@ -358,7 +358,7 @@ func newFan(t *testing.T, ds mdag.DAGService) *mdag.Node { } func newLinkedList(t *testing.T, ds mdag.DAGService) *mdag.Node { - a := &mdag.Node{Data: []byte("/a")} + a := mdag.NodeWithData([]byte("/a")) aa := child(t, ds, a, "aa") aaa := child(t, ds, aa, "aaa") aaaa := child(t, ds, aaa, "aaaa") @@ -371,7 +371,7 @@ func newLinkedList(t *testing.T, ds mdag.DAGService) *mdag.Node { } func newBinaryTree(t *testing.T, ds mdag.DAGService) *mdag.Node { - a := &mdag.Node{Data: []byte("/a")} + a := mdag.NodeWithData([]byte("/a")) aa := child(t, ds, a, "aa") ab := child(t, ds, a, "ab") addLink(t, ds, aa, child(t, ds, aa, "aaa")) @@ -384,7 +384,7 @@ func newBinaryTree(t *testing.T, ds mdag.DAGService) *mdag.Node { } func newBinaryDAG(t *testing.T, ds mdag.DAGService) *mdag.Node { - a := &mdag.Node{Data: []byte("/a")} + a := mdag.NodeWithData([]byte("/a")) aa := child(t, ds, a, "aa") aaa := child(t, ds, aa, "aaa") aaaa := child(t, ds, aaa, "aaaa") @@ -401,7 +401,7 @@ func newBinaryDAG(t *testing.T, ds mdag.DAGService) *mdag.Node { } func addLink(t *testing.T, ds mdag.DAGService, a, b *mdag.Node) { - to := string(a.Data) + "2" + string(b.Data) + to := string(a.Data()) + "2" + string(b.Data()) if _, err := ds.Add(b); err != nil { t.Error(err) } @@ -411,5 +411,5 @@ func addLink(t *testing.T, ds mdag.DAGService, a, b *mdag.Node) { } func child(t *testing.T, ds mdag.DAGService, a *mdag.Node, name string) *mdag.Node { - return &mdag.Node{Data: []byte(string(a.Data) + "/" + name)} + return mdag.NodeWithData([]byte(string(a.Data()) + "/" + name)) } diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index b225a3dff..1ec444b0b 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -13,9 +13,7 @@ import ( func TestAddLink(t *testing.T) { ds := mdtest.Mock() - fishnode := &dag.Node{ - Data: []byte("fishcakes!"), - } + fishnode := dag.NodeWithData([]byte("fishcakes!")) fk, err := ds.Add(fishnode) if err != nil { @@ -90,7 +88,7 @@ func TestInsertNode(t *testing.T) { } func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr string) { - child := &dag.Node{Data: []byte(data)} + child := dag.NodeWithData([]byte(data)) ck, err := e.tmp.Add(child) if err != nil { t.Fatal(err) From 78e850c120224690775af4f9f57daf4ce26bb3ca Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 10 Jul 2016 00:40:23 -0700 Subject: [PATCH 1406/3817] cache encoded data when reading dag nodes from disk License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@62a1d46c4e91fe06903ee00ed129ed0ac1a5baa5 --- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index a6ec85dac..1430061c2 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -18,8 +18,8 @@ import ( func randNode() (*mdag.Node, key.Key) { nd := new(mdag.Node) - nd.Data = make([]byte, 32) - util.NewTimeSeededRand().Read(nd.Data) + nd.SetData(make([]byte, 32)) + util.NewTimeSeededRand().Read(nd.Data()) k, _ := nd.Key() return nd, k } diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index fec38e254..7257ccaec 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -111,7 +111,7 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint if err := writeHdr(n, hdr); err != nil { return nil, err } - hdrLen := len(n.Data) + hdrLen := len(n.Data()) if estimatedLen < maxItems { // it'll probably fit @@ -122,12 +122,12 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint break } n.Links = append(n.Links, &merkledag.Link{Hash: k.ToMultihash()}) - n.Data = append(n.Data, data...) + n.SetData(append(n.Data(), data...)) } // sort by hash, also swap item Data s := sortByHash{ links: n.Links[defaultFanout:], - data: n.Data[hdrLen:], + data: n.Data()[hdrLen:], } sort.Stable(s) } @@ -179,11 +179,11 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint } func readHdr(n *merkledag.Node) (*pb.Set, []byte, error) { - hdrLenRaw, consumed := binary.Uvarint(n.Data) + hdrLenRaw, consumed := binary.Uvarint(n.Data()) if consumed <= 0 { return nil, nil, errors.New("invalid Set header length") } - buf := n.Data[consumed:] + buf := n.Data()[consumed:] if hdrLenRaw > uint64(len(buf)) { return nil, nil, errors.New("impossibly large Set header length") } @@ -209,10 +209,10 @@ func writeHdr(n *merkledag.Node, hdr *pb.Set) error { if err != nil { return err } - n.Data = make([]byte, binary.MaxVarintLen64, binary.MaxVarintLen64+len(hdrData)) - written := binary.PutUvarint(n.Data, uint64(len(hdrData))) - n.Data = n.Data[:written] - n.Data = append(n.Data, hdrData...) + n.SetData(make([]byte, binary.MaxVarintLen64, binary.MaxVarintLen64+len(hdrData))) + written := binary.PutUvarint(n.Data(), uint64(len(hdrData))) + n.SetData(n.Data()[:written]) + n.SetData(append(n.Data(), hdrData...)) return nil } From 7babb9a48435e9f5cdae0b214901178d0d31adc1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 10 Jul 2016 00:40:23 -0700 Subject: [PATCH 1407/3817] cache encoded data when reading dag nodes from disk License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@8899d71fbefce2e2080fff000a761da7dda1eccf --- path/resolver_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/path/resolver_test.go b/path/resolver_test.go index fe8155a85..735a79e6d 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -15,8 +15,8 @@ import ( func randNode() (*merkledag.Node, key.Key) { node := new(merkledag.Node) - node.Data = make([]byte, 32) - util.NewTimeSeededRand().Read(node.Data) + node.SetData(make([]byte, 32)) + util.NewTimeSeededRand().Read(node.Data()) k, _ := node.Key() return node, k } From 330e3c14d2460ddb3b28208de16aad01aa036319 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 19 Jul 2016 07:41:00 -0700 Subject: [PATCH 1408/3817] mfs: fix copying into directory with no given filename License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@b17fb288234e17df845bd92de4401126951e11e3 --- mfs/ops.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mfs/ops.go b/mfs/ops.go index 7cf9ed9f3..94c6c30df 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -89,6 +89,9 @@ func lookupDir(r *Root, path string) (*Directory, error) { // PutNode inserts 'nd' at 'path' in the given mfs func PutNode(r *Root, path string, nd *dag.Node) error { dirp, filename := gopath.Split(path) + if filename == "" { + return fmt.Errorf("cannot create file with empty name") + } pdir, err := lookupDir(r, dirp) if err != nil { From f5a5cfe68624360ad98b7f6bfd660dc47ba2e4aa Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 26 Jul 2016 10:48:25 -0700 Subject: [PATCH 1409/3817] use batching datastore for providers storage License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@803bc31e2b19dbc068ef9cd0c16368a57e9a0d40 --- mfs/mfs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 3069fb13c..383bcfd73 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,10 +14,10 @@ import ( "time" "github.com/ipfs/go-ipfs/path" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" randbo "gx/ipfs/QmYvsG72GsfLgUeSojXArjnU6L4Wmwk7wuAxtNLuyXcc1T/randbo" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" From aff3ba1392464bdc8bf07a13267a2ff1ec0dd064 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 26 Jul 2016 10:48:25 -0700 Subject: [PATCH 1410/3817] use batching datastore for providers storage License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@456bb4bacc85e4674c74a9dd1b57d571a4819c7b --- blockstore/blockstore.go | 6 +++--- blockstore/blockstore_test.go | 6 +++--- blockstore/bloom_cache.go | 2 +- blockstore/bloom_cache_test.go | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 605be2bd8..380e0b640 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -10,11 +10,11 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + dsns "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/namespace" + dsq "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/query" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - dsns "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/namespace" - dsq "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/query" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index f7a5325ab..2f0269141 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + dsq "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/query" + ds_sync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - dsq "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/query" - ds_sync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 1a4c57ac4..35d6ce38f 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -3,10 +3,10 @@ package blockstore import ( "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" bloom "gx/ipfs/QmWQ2SJisXwcCLsUXLwYCKSfyExXjFRW2WbBH5sqCUnwX5/bbloom" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" "sync/atomic" ) diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index bddcee56c..d9cc5c817 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -8,10 +8,10 @@ import ( "github.com/ipfs/go-ipfs/blocks" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + dsq "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/query" + syncds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - dsq "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/query" - syncds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) func testBloomCached(bs GCBlockstore, ctx context.Context) (*bloomcache, error) { From 3bc3b40b3a6d875cc652f4388b96c6ded1bd45f5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 26 Jul 2016 10:48:25 -0700 Subject: [PATCH 1411/3817] use batching datastore for providers storage License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@12b555448dcdb3fc6f5050bcc33d5a2bad6a2cd6 --- routing/dht/dht.go | 4 +-- routing/dht/dht_test.go | 4 +-- routing/dht/ext_test.go | 4 +-- routing/dht/handlers.go | 2 +- routing/dht/providers/providers.go | 38 +++++++++---------------- routing/dht/providers/providers_test.go | 2 +- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 4 +-- routing/mock/dht.go | 4 +-- routing/mock/interface.go | 2 +- routing/offline/offline.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 13 files changed, 31 insertions(+), 41 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 4aab2bce7..ba2d197aa 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -21,12 +21,12 @@ import ( goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" host "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/host" protocol "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/protocol" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" ) var log = logging.Logger("dht") @@ -65,7 +65,7 @@ type IpfsDHT struct { } // NewDHT creates a new DHT object with the given peer as the 'local' host -func NewDHT(ctx context.Context, h host.Host, dstore ds.Datastore) *IpfsDHT { +func NewDHT(ctx context.Context, h host.Host, dstore ds.Batching) *IpfsDHT { dht := new(IpfsDHT) dht.datastore = dstore dht.self = h.ID() diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index e5ce56fea..0abc27ed7 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -14,8 +14,8 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index a675c5d6b..bcc95aff1 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -10,8 +10,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" inet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net" diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 0152ff1bd..feaf9aea2 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" diff --git a/routing/dht/providers/providers.go b/routing/dht/providers/providers.go index da3cf7606..286108a15 100644 --- a/routing/dht/providers/providers.go +++ b/routing/dht/providers/providers.go @@ -10,16 +10,26 @@ import ( goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + dsq "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/query" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" + autobatch "gx/ipfs/QmVvJ27GcLaLSXvcB4auk3Gn3xuWK5ti5ENkZ2pCoJEYW4/autobatch" base32 "gx/ipfs/Qmb1DA2A9LS2wR4FFweB4uEDomFsdmnw1VLawLE1yQzudj/base32" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - dsq "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/query" key "github.com/ipfs/go-ipfs/blocks/key" + flags "github.com/ipfs/go-ipfs/flags" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) +var batchBufferSize = 256 + +func init() { + if flags.LowMemMode { + batchBufferSize = 8 + } +} + var log = logging.Logger("providers") var lruCacheSize = 256 @@ -30,11 +40,9 @@ type ProviderManager struct { // all non channel fields are meant to be accessed only within // the run method providers *lru.Cache - local map[key.Key]struct{} lpeer peer.ID dstore ds.Datastore - getlocal chan chan []key.Key newprovs chan *addProv getprovs chan *getProv period time.Duration @@ -58,19 +66,17 @@ type getProv struct { resp chan []peer.ID } -func NewProviderManager(ctx context.Context, local peer.ID, dstore ds.Datastore) *ProviderManager { +func NewProviderManager(ctx context.Context, local peer.ID, dstore ds.Batching) *ProviderManager { pm := new(ProviderManager) pm.getprovs = make(chan *getProv) pm.newprovs = make(chan *addProv) - pm.dstore = dstore + pm.dstore = autobatch.NewAutoBatching(dstore, batchBufferSize) cache, err := lru.New(lruCacheSize) if err != nil { panic(err) //only happens if negative value is passed to lru constructor } pm.providers = cache - pm.getlocal = make(chan chan []key.Key) - pm.local = make(map[key.Key]struct{}) pm.proc = goprocessctx.WithContext(ctx) pm.cleanupInterval = defaultCleanupInterval pm.proc.Go(func(p goprocess.Process) { pm.run() }) @@ -251,9 +257,6 @@ func (pm *ProviderManager) run() { for { select { case np := <-pm.newprovs: - if np.val == pm.lpeer { - pm.local[np.k] = struct{}{} - } err := pm.addProv(np.k, np.val) if err != nil { log.Error("error adding new providers: ", err) @@ -265,13 +268,6 @@ func (pm *ProviderManager) run() { } gp.resp <- provs - case lc := <-pm.getlocal: - var keys []key.Key - for k := range pm.local { - keys = append(keys, k) - } - lc <- keys - case <-tick.C: keys, err := pm.getAllProvKeys() if err != nil { @@ -337,12 +333,6 @@ func (pm *ProviderManager) GetProviders(ctx context.Context, k key.Key) []peer.I } } -func (pm *ProviderManager) GetLocal() []key.Key { - resp := make(chan []key.Key) - pm.getlocal <- resp - return <-resp -} - func newProviderSet() *providerSet { return &providerSet{ set: make(map[peer.ID]time.Time), diff --git a/routing/dht/providers/providers_test.go b/routing/dht/providers/providers_test.go index 11e1506a8..287dff6c0 100644 --- a/routing/dht/providers/providers_test.go +++ b/routing/dht/providers/providers_test.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index e3e036bdf..6847bd278 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,7 +8,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index ed4973e7a..c13c3a70c 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 5a017d55b..044905eb2 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + sync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" mocknet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - sync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index ea092f13e..e4e028977 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -11,8 +11,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" ) // Server provides mockrouting Clients diff --git a/routing/offline/offline.go b/routing/offline/offline.go index a20783666..1d85f3ac0 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -8,7 +8,7 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 0a105256c..42f5720f5 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,7 +8,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - datastore "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + datastore "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index bde8ca2b6..025dd1dc2 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -5,7 +5,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - datastore "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" + datastore "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 5f2d1cb47db9a9140a5fc1debc8d1abc8c0a92b3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 26 Jul 2016 10:48:25 -0700 Subject: [PATCH 1412/3817] use batching datastore for providers storage License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@1777498c479848d176d5a5efb2ce987f165eb890 --- namesys/namesys.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/resolve_test.go | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index bf058e5ff..0830fb269 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,9 +6,9 @@ import ( path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/publisher.go b/namesys/publisher.go index eebb3153e..6c4b7e790 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -6,9 +6,9 @@ import ( "fmt" "time" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index fc6c41bc7..5de248c12 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -17,9 +17,9 @@ import ( goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 03f64abac..2adbe6349 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -10,10 +10,10 @@ import ( mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) func TestRoutingResolve(t *testing.T) { From 4d768b4da52d1cedf132e85ec45424b347c9cdf3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 26 Jul 2016 10:48:25 -0700 Subject: [PATCH 1413/3817] use batching datastore for providers storage License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@41db935bb0bd289cf8e7ff3f2ee9fb37fee87157 --- unixfs/mod/dagmodifier_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 7ed30dd26..7767f99d3 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -17,11 +17,11 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" + "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" ) func getMockDagServ(t testing.TB) mdag.DAGService { From 739eb9569f04f4e63754b4e3e0f980d278733c29 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 26 Jul 2016 10:48:25 -0700 Subject: [PATCH 1414/3817] use batching datastore for providers storage License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@6d1508da0aadd56b7d20be0bcd7154ddeec15da5 --- ipld/merkledag/merkledag_test.go | 4 ++-- ipld/merkledag/test/utils.go | 4 ++-- ipld/merkledag/utils/utils.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 8a679335a..dcf9ced1c 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -21,10 +21,10 @@ import ( dstest "github.com/ipfs/go-ipfs/merkledag/test" "github.com/ipfs/go-ipfs/pin" uio "github.com/ipfs/go-ipfs/unixfs/io" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) type dagservAndPinner struct { diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index fd70b7085..bc0177b11 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -5,8 +5,8 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" ) func Mock() dag.DAGService { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 8e0e596fd..15dddff68 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -3,9 +3,9 @@ package dagutils import ( "errors" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + syncds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - syncds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" bserv "github.com/ipfs/go-ipfs/blockservice" From 2d3d2a51780617dd2a704dcdeb5a1933e6fc156e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 26 Jul 2016 10:48:25 -0700 Subject: [PATCH 1415/3817] use batching datastore for providers storage License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@63cbf6e4af611b288ee8e92f3dd7d3e7988273a2 --- blockservice/test/blocks_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 943bf0944..b7df8721c 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -11,10 +11,10 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) func TestBlocks(t *testing.T) { From f58bc2cb5bb0bbebd21b2af57cd7ab662482df3b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 26 Jul 2016 10:48:25 -0700 Subject: [PATCH 1416/3817] use batching datastore for providers storage License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@c30d68d59a8417713acc61d1dbf4e31d93ba49ef --- exchange/offline/offline_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 56066a552..b7962e2d7 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -7,9 +7,9 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" key "github.com/ipfs/go-ipfs/blocks/key" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + ds_sync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - ds_sync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) func TestBlockReturnsErr(t *testing.T) { From ebd8c5211cfc4f64f74b0e901f986374e90eb1b3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 26 Jul 2016 10:48:25 -0700 Subject: [PATCH 1417/3817] use batching datastore for providers storage License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@a519e711d4c2449a9057f6850de8afa1848fd8a9 --- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 83a6f5b88..fa2af39db 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -11,8 +11,8 @@ import ( "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 1430061c2..8e4cfd8a8 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -11,9 +11,9 @@ import ( bs "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) func randNode() (*mdag.Node, key.Key) { diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index da5f0702d..e4c8bd4de 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -9,11 +9,11 @@ import ( "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" "github.com/ipfs/go-ipfs/merkledag" + "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore" - dssync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync" ) func ignoreKeys(key.Key) {} From 319cd12b3bf255b98370ddeb08eb7a4b28120ea0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 1 Aug 2016 16:53:45 -0700 Subject: [PATCH 1418/3817] don't cache entire mfs tree on add finalize License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@45d1eadf2f0961bf2bafbd2d7fb5491fe83c3e98 --- mfs/dir.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mfs/dir.go b/mfs/dir.go index cf178e452..9009d2431 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -152,6 +152,13 @@ func (d *Directory) Child(name string) (FSNode, error) { return d.childUnsync(name) } +func (d *Directory) Uncache(name string) { + d.lock.Lock() + defer d.lock.Unlock() + delete(d.files, name) + delete(d.childDirs, name) +} + // childFromDag searches through this directories dag node for a child link // with the given name func (d *Directory) childFromDag(name string) (*dag.Node, error) { From d91d433c87c298960d5f81a8b1e390a13254d8df Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 2 Aug 2016 01:10:48 +0100 Subject: [PATCH 1419/3817] blockstore: extract ARC cache from Bloom cache it removes race condition that would happen during various calls License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@249be194a84991cdd91b4462dd47c009aed9ad04 --- blockstore/arc_cache.go | 127 +++++++++++++++++++++++++++++++++ blockstore/arc_cache_test.go | 67 +++++++++++++++++ blockstore/bloom_cache.go | 46 ++---------- blockstore/bloom_cache_test.go | 49 +------------ blockstore/caching.go | 6 +- 5 files changed, 206 insertions(+), 89 deletions(-) create mode 100644 blockstore/arc_cache.go create mode 100644 blockstore/arc_cache_test.go diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go new file mode 100644 index 000000000..37a8f0d02 --- /dev/null +++ b/blockstore/arc_cache.go @@ -0,0 +1,127 @@ +package blockstore + +import ( + "github.com/ipfs/go-ipfs/blocks" + key "github.com/ipfs/go-ipfs/blocks/key" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" +) + +type arccache struct { + arc *lru.ARCCache + blockstore Blockstore +} + +func arcCached(bs Blockstore, lruSize int) (*arccache, error) { + arc, err := lru.NewARC(lruSize) + if err != nil { + return nil, err + } + + return &arccache{arc: arc, blockstore: bs}, nil +} + +func (b *arccache) DeleteBlock(k key.Key) error { + if has, ok := b.hasCached(k); ok && !has { + return ErrNotFound + } + + b.arc.Remove(k) // Invalidate cache before deleting. + err := b.blockstore.DeleteBlock(k) + switch err { + case nil: + b.arc.Add(k, false) + case ds.ErrNotFound, ErrNotFound: + b.arc.Add(k, false) + default: + return err + } + return nil +} + +// if ok == false has is inconclusive +// if ok == true then has respons to question: is it contained +func (b *arccache) hasCached(k key.Key) (has bool, ok bool) { + if k == "" { + // Return cache invalid so call to blockstore + // in case of invalid key is forwarded deeper + return false, false + } + h, ok := b.arc.Get(k) + if ok { + return h.(bool), ok + } else { + return false, false + } +} + +func (b *arccache) Has(k key.Key) (bool, error) { + if has, ok := b.hasCached(k); ok { + return has, nil + } + + res, err := b.blockstore.Has(k) + if err == nil { + b.arc.Add(k, res) + } + return res, err +} + +func (b *arccache) Get(k key.Key) (blocks.Block, error) { + if has, ok := b.hasCached(k); ok && !has { + return nil, ErrNotFound + } + + bl, err := b.blockstore.Get(k) + if bl == nil && err == ErrNotFound { + b.arc.Add(k, false) + } else if bl != nil { + b.arc.Add(k, true) + } + return bl, err +} + +func (b *arccache) Put(bl blocks.Block) error { + if has, ok := b.hasCached(bl.Key()); ok && has { + return nil + } + + err := b.blockstore.Put(bl) + if err == nil { + b.arc.Add(bl.Key(), true) + } + return err +} + +func (b *arccache) PutMany(bs []blocks.Block) error { + var good []blocks.Block + for _, block := range bs { + if has, ok := b.hasCached(block.Key()); !ok || (ok && !has) { + good = append(good, block) + } + } + err := b.blockstore.PutMany(bs) + if err == nil { + for _, block := range bs { + b.arc.Add(block.Key(), true) + } + } + return err +} + +func (b *arccache) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { + return b.blockstore.AllKeysChan(ctx) +} + +func (b *arccache) GCLock() Unlocker { + return b.blockstore.(GCBlockstore).GCLock() +} + +func (b *arccache) PinLock() Unlocker { + return b.blockstore.(GCBlockstore).PinLock() +} + +func (b *arccache) GCRequested() bool { + return b.blockstore.(GCBlockstore).GCRequested() +} diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go new file mode 100644 index 000000000..505f7e1ea --- /dev/null +++ b/blockstore/arc_cache_test.go @@ -0,0 +1,67 @@ +package blockstore + +import ( + "github.com/ipfs/go-ipfs/blocks" + "testing" + + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + syncds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" +) + +func testArcCached(bs GCBlockstore, ctx context.Context) (*arccache, error) { + if ctx == nil { + ctx = context.TODO() + } + opts := DefaultCacheOpts() + opts.HasBloomFilterSize = 0 + opts.HasBloomFilterHashes = 0 + bbs, err := CachedBlockstore(bs, ctx, opts) + if err == nil { + return bbs.(*arccache), nil + } else { + return nil, err + } +} + +func TestRemoveCacheEntryOnDelete(t *testing.T) { + b := blocks.NewBlock([]byte("foo")) + cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} + bs := NewBlockstore(syncds.MutexWrap(cd)) + cachedbs, err := testArcCached(bs, nil) + if err != nil { + t.Fatal(err) + } + cachedbs.Put(b) + + cd.Lock() + writeHitTheDatastore := false + cd.Unlock() + + cd.SetFunc(func() { + writeHitTheDatastore = true + }) + + cachedbs.DeleteBlock(b.Key()) + cachedbs.Put(b) + if !writeHitTheDatastore { + t.Fail() + } +} + +func TestElideDuplicateWrite(t *testing.T) { + cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} + bs := NewBlockstore(syncds.MutexWrap(cd)) + cachedbs, err := testArcCached(bs, nil) + if err != nil { + t.Fatal(err) + } + + b1 := blocks.NewBlock([]byte("foo")) + + cachedbs.Put(b1) + cd.SetFunc(func() { + t.Fatal("write hit the datastore") + }) + cachedbs.Put(b1) +} diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 35d6ce38f..e10dacfaf 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -3,8 +3,6 @@ package blockstore import ( "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" bloom "gx/ipfs/QmWQ2SJisXwcCLsUXLwYCKSfyExXjFRW2WbBH5sqCUnwX5/bbloom" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" @@ -13,16 +11,12 @@ import ( // bloomCached returns Blockstore that caches Has requests using Bloom filter // Size is size of bloom filter in bytes -func bloomCached(bs Blockstore, ctx context.Context, bloomSize, hashCount, lruSize int) (*bloomcache, error) { +func bloomCached(bs Blockstore, ctx context.Context, bloomSize, hashCount int) (*bloomcache, error) { bl, err := bloom.New(float64(bloomSize), float64(hashCount)) if err != nil { return nil, err } - arc, err := lru.NewARC(lruSize) - if err != nil { - return nil, err - } - bc := &bloomcache{blockstore: bs, bloom: bl, arc: arc} + bc := &bloomcache{blockstore: bs, bloom: bl} bc.Invalidate() go bc.Rebuild(ctx) @@ -33,7 +27,6 @@ type bloomcache struct { bloom *bloom.Bloom active int32 - arc *lru.ARCCache // This chan is only used for testing to wait for bloom to enable rebuildChan chan struct{} blockstore Blockstore @@ -84,17 +77,7 @@ func (b *bloomcache) DeleteBlock(k key.Key) error { return ErrNotFound } - b.arc.Remove(k) // Invalidate cache before deleting. - err := b.blockstore.DeleteBlock(k) - switch err { - case nil: - b.arc.Add(k, false) - case ds.ErrNotFound, ErrNotFound: - b.arc.Add(k, false) - default: - return err - } - return nil + return b.blockstore.DeleteBlock(k) } // if ok == false has is inconclusive @@ -111,12 +94,7 @@ func (b *bloomcache) hasCached(k key.Key) (has bool, ok bool) { return false, true } } - h, ok := b.arc.Get(k) - if ok { - return h.(bool), ok - } else { - return false, false - } + return false, false } func (b *bloomcache) Has(k key.Key) (bool, error) { @@ -124,11 +102,7 @@ func (b *bloomcache) Has(k key.Key) (bool, error) { return has, nil } - res, err := b.blockstore.Has(k) - if err == nil { - b.arc.Add(k, res) - } - return res, err + return b.blockstore.Has(k) } func (b *bloomcache) Get(k key.Key) (blocks.Block, error) { @@ -136,13 +110,7 @@ func (b *bloomcache) Get(k key.Key) (blocks.Block, error) { return nil, ErrNotFound } - bl, err := b.blockstore.Get(k) - if bl == nil && err == ErrNotFound { - b.arc.Add(k, false) - } else if bl != nil { - b.arc.Add(k, true) - } - return bl, err + return b.blockstore.Get(k) } func (b *bloomcache) Put(bl blocks.Block) error { @@ -153,7 +121,6 @@ func (b *bloomcache) Put(bl blocks.Block) error { err := b.blockstore.Put(bl) if err == nil { b.bloom.AddTS([]byte(bl.Key())) - b.arc.Add(bl.Key(), true) } return err } @@ -169,7 +136,6 @@ func (b *bloomcache) PutMany(bs []blocks.Block) error { if err == nil { for _, block := range bs { b.bloom.AddTS([]byte(block.Key())) - b.arc.Add(block.Key(), true) } } return err diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index d9cc5c817..fbffd42f5 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -19,6 +19,7 @@ func testBloomCached(bs GCBlockstore, ctx context.Context) (*bloomcache, error) ctx = context.TODO() } opts := DefaultCacheOpts() + opts.HasARCCacheSize = 0 bbs, err := CachedBlockstore(bs, ctx, opts) if err == nil { return bbs.(*bloomcache), nil @@ -29,56 +30,10 @@ func testBloomCached(bs GCBlockstore, ctx context.Context) (*bloomcache, error) func TestReturnsErrorWhenSizeNegative(t *testing.T) { bs := NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore())) - _, err := bloomCached(bs, context.TODO(), 100, 1, -1) + _, err := bloomCached(bs, context.TODO(), -1, 1) if err == nil { t.Fail() } - _, err = bloomCached(bs, context.TODO(), -1, 1, 100) - if err == nil { - t.Fail() - } -} - -func TestRemoveCacheEntryOnDelete(t *testing.T) { - b := blocks.NewBlock([]byte("foo")) - cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} - bs := NewBlockstore(syncds.MutexWrap(cd)) - cachedbs, err := testBloomCached(bs, nil) - if err != nil { - t.Fatal(err) - } - cachedbs.Put(b) - - cd.Lock() - writeHitTheDatastore := false - cd.Unlock() - - cd.SetFunc(func() { - writeHitTheDatastore = true - }) - - cachedbs.DeleteBlock(b.Key()) - cachedbs.Put(b) - if !writeHitTheDatastore { - t.Fail() - } -} - -func TestElideDuplicateWrite(t *testing.T) { - cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} - bs := NewBlockstore(syncds.MutexWrap(cd)) - cachedbs, err := testBloomCached(bs, nil) - if err != nil { - t.Fatal(err) - } - - b1 := blocks.NewBlock([]byte("foo")) - - cachedbs.Put(b1) - cd.SetFunc(func() { - t.Fatal("write hit the datastore") - }) - cachedbs.Put(b1) } func TestHasIsBloomCached(t *testing.T) { cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} diff --git a/blockstore/caching.go b/blockstore/caching.go index 689a9b5fc..f691f89f8 100644 --- a/blockstore/caching.go +++ b/blockstore/caching.go @@ -34,8 +34,10 @@ func CachedBlockstore(bs GCBlockstore, return nil, errors.New("bloom filter hash count can't be 0 when there is size set") } if opts.HasBloomFilterSize != 0 { - cbs, err = bloomCached(cbs, ctx, opts.HasBloomFilterSize, opts.HasBloomFilterHashes, - opts.HasARCCacheSize) + cbs, err = bloomCached(cbs, ctx, opts.HasBloomFilterSize, opts.HasBloomFilterHashes) + } + if opts.HasARCCacheSize > 0 { + cbs, err = arcCached(cbs, opts.HasARCCacheSize) } return cbs, err From ebf91aecf16929c33af2f0470b26688c19e2e1ba Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 2 Aug 2016 10:59:38 +0100 Subject: [PATCH 1420/3817] blockstore: cleanup style a bit License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@6ebcf7aa68a6213c3f81610a3f500d5ce5775461 --- blockstore/arc_cache.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 37a8f0d02..7ab07e1d5 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -102,12 +102,13 @@ func (b *arccache) PutMany(bs []blocks.Block) error { } } err := b.blockstore.PutMany(bs) - if err == nil { - for _, block := range bs { - b.arc.Add(block.Key(), true) - } + if err != nil { + return err } - return err + for _, block := range bs { + b.arc.Add(block.Key(), true) + } + return nil } func (b *arccache) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { From 3e1083bff3b5a327481cc6b21ab7368a4f485737 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 3 Aug 2016 13:54:37 +0200 Subject: [PATCH 1421/3817] blockstore: cleanup the style removing some mess from the refactor License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@680c54e90d0f3ec954570c5c40662e2c50ec1acb --- blockstore/arc_cache.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 7ab07e1d5..63253ef9c 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -30,30 +30,28 @@ func (b *arccache) DeleteBlock(k key.Key) error { b.arc.Remove(k) // Invalidate cache before deleting. err := b.blockstore.DeleteBlock(k) switch err { - case nil: - b.arc.Add(k, false) - case ds.ErrNotFound, ErrNotFound: + case nil, ds.ErrNotFound, ErrNotFound: b.arc.Add(k, false) + return nil default: return err } - return nil } // if ok == false has is inconclusive // if ok == true then has respons to question: is it contained func (b *arccache) hasCached(k key.Key) (has bool, ok bool) { if k == "" { - // Return cache invalid so call to blockstore - // in case of invalid key is forwarded deeper + // Return cache invalid so the call to blockstore happens + // in case of invalid key and correct error is created. return false, false } + h, ok := b.arc.Get(k) if ok { - return h.(bool), ok - } else { - return false, false + return h.(bool), true } + return false, false } func (b *arccache) Has(k key.Key) (bool, error) { From 5ef40ffea8a2169a1a3154df68c2e13bf0f5ecc4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 3 Aug 2016 18:19:47 -0700 Subject: [PATCH 1422/3817] dht: add in code to detect and diagnose #3032 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@b9835f0363366c4693c9b798c2bf3dda02678d04 --- routing/dht/routing.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 252d51dd3..b9e547cac 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -3,6 +3,7 @@ package dht import ( "bytes" "fmt" + "runtime" "sync" "time" @@ -380,6 +381,16 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key key.Key, _, err := query.Run(ctx, peers) if err != nil { log.Debugf("Query error: %s", err) + // Special handling for issue: https://github.com/ipfs/go-ipfs/issues/3032 + if fmt.Sprint(err) == "" { + log.Error("reproduced bug 3032:") + log.Errorf("Errors type information: %#v", err) + log.Errorf("go version: %s", runtime.Version()) + log.Error("please report this information to: https://github.com/ipfs/go-ipfs/issues/3032") + + // replace problematic error with something that won't crash the daemon + err = fmt.Errorf("") + } notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ Type: notif.QueryError, Extra: err.Error(), From 1f40257f57713c3a235b789ade7e7a46400a113b Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 12 Aug 2016 16:41:18 +0200 Subject: [PATCH 1423/3817] deps: move go-is-domain to gx License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@dd6fb69dff84f006ec0d819895af4c64ba97fbe8 --- namesys/dns.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/dns.go b/namesys/dns.go index bb9ebdfb3..d825ea00e 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -5,8 +5,8 @@ import ( "net" "strings" - isd "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-is-domain" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + isd "gx/ipfs/QmaeHSCBd9XjXxmgHEiKkHtLcMCb2eZsPLKT7bHgBfBkqw/go-is-domain" path "github.com/ipfs/go-ipfs/path" ) From 5ad8a237987d90dd9b1235f4213b7f2e1cf859fb Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 11 Aug 2016 22:18:15 +0200 Subject: [PATCH 1424/3817] test: 81% coverage on blockstore Coverage report available at: https://ipfs.io/ipfs/QmTuMtwGCfHrbYyZdQ1RaGNwS2MGsmAkjA8AaB69N7Ya1g/coverage.html#file0 Part of #3053 License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@71c187804168f77721f8e2e707edac414ff868e2 --- blockstore/arc_cache_test.go | 140 +++++++++++++++++++++++++++++---- blockstore/blockstore_test.go | 7 +- blockstore/bloom_cache_test.go | 25 ++++++ blockstore/caching_test.go | 35 +++++++++ 4 files changed, 191 insertions(+), 16 deletions(-) create mode 100644 blockstore/caching_test.go diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 505f7e1ea..b37092602 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -2,6 +2,7 @@ package blockstore import ( "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-ipfs/blocks/key" "testing" ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" @@ -9,6 +10,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) +var exampleBlock = blocks.NewBlock([]byte("foo")) + func testArcCached(bs GCBlockstore, ctx context.Context) (*arccache, error) { if ctx == nil { ctx = context.TODO() @@ -24,15 +27,29 @@ func testArcCached(bs GCBlockstore, ctx context.Context) (*arccache, error) { } } -func TestRemoveCacheEntryOnDelete(t *testing.T) { - b := blocks.NewBlock([]byte("foo")) +func createStores(t *testing.T) (*arccache, *blockstore, *callbackDatastore) { cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} bs := NewBlockstore(syncds.MutexWrap(cd)) - cachedbs, err := testArcCached(bs, nil) + arc, err := testArcCached(bs, nil) if err != nil { t.Fatal(err) } - cachedbs.Put(b) + return arc, bs, cd +} + +func trap(message string, cd *callbackDatastore, t *testing.T) { + cd.SetFunc(func() { + t.Fatal(message) + }) +} +func untrap(cd *callbackDatastore) { + cd.SetFunc(func() {}) +} + +func TestRemoveCacheEntryOnDelete(t *testing.T) { + arc, _, cd := createStores(t) + + arc.Put(exampleBlock) cd.Lock() writeHitTheDatastore := false @@ -42,26 +59,119 @@ func TestRemoveCacheEntryOnDelete(t *testing.T) { writeHitTheDatastore = true }) - cachedbs.DeleteBlock(b.Key()) - cachedbs.Put(b) + arc.DeleteBlock(exampleBlock.Key()) + arc.Put(exampleBlock) if !writeHitTheDatastore { t.Fail() } } func TestElideDuplicateWrite(t *testing.T) { - cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} - bs := NewBlockstore(syncds.MutexWrap(cd)) - cachedbs, err := testArcCached(bs, nil) + arc, _, cd := createStores(t) + + arc.Put(exampleBlock) + trap("write hit datastore", cd, t) + arc.Put(exampleBlock) +} + +func TestHasRequestTriggersCache(t *testing.T) { + arc, _, cd := createStores(t) + + arc.Has(exampleBlock.Key()) + trap("has hit datastore", cd, t) + if has, err := arc.Has(exampleBlock.Key()); has || err != nil { + t.Fatal("has was true but there is no such block") + } + + untrap(cd) + err := arc.Put(exampleBlock) if err != nil { t.Fatal(err) } - b1 := blocks.NewBlock([]byte("foo")) + trap("has hit datastore", cd, t) - cachedbs.Put(b1) - cd.SetFunc(func() { - t.Fatal("write hit the datastore") - }) - cachedbs.Put(b1) + if has, err := arc.Has(exampleBlock.Key()); !has || err != nil { + t.Fatal("has returned invalid result") + } +} + +func TestGetFillsCache(t *testing.T) { + arc, _, cd := createStores(t) + + if bl, err := arc.Get(exampleBlock.Key()); bl != nil || err == nil { + t.Fatal("block was found or there was no error") + } + + trap("has hit datastore", cd, t) + + if has, err := arc.Has(exampleBlock.Key()); has || err != nil { + t.Fatal("has was true but there is no such block") + } + + untrap(cd) + + if err := arc.Put(exampleBlock); err != nil { + t.Fatal(err) + } + + trap("has hit datastore", cd, t) + + if has, err := arc.Has(exampleBlock.Key()); !has || err != nil { + t.Fatal("has returned invalid result") + } +} + +func TestGetAndDeleteFalseShortCirciot(t *testing.T) { + arc, _, cd := createStores(t) + + arc.Has(exampleBlock.Key()) + + trap("get hit datastore", cd, t) + + if bl, err := arc.Get(exampleBlock.Key()); bl != nil || err != ErrNotFound { + t.Fatal("get returned invalid result") + } + + if arc.DeleteBlock(exampleBlock.Key()) != ErrNotFound { + t.Fatal("expected ErrNotFound error") + } +} + +func TestArcCreationFailure(t *testing.T) { + if arc, err := arcCached(nil, -1); arc != nil || err == nil { + t.Fatal("expected error and no cache") + } +} + +func TestInvalidKey(t *testing.T) { + arc, _, _ := createStores(t) + + bl, err := arc.Get(key.Key("")) + + if bl != nil { + t.Fatal("blocks should be nil") + } + if err == nil { + t.Fatal("expected error") + } +} + +func TestHasAfterSucessfulGetIsCached(t *testing.T) { + arc, bs, cd := createStores(t) + + bs.Put(exampleBlock) + + arc.Get(exampleBlock.Key()) + + trap("has hit datastore", cd, t) + arc.Has(exampleBlock.Key()) +} + +func TestPutManyCaches(t *testing.T) { + arc, _, cd := createStores(t) + arc.PutMany([]blocks.Block{exampleBlock}) + + trap("has hit datastore", cd, t) + arc.Has(exampleBlock.Key()) } diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 2f0269141..2cca07cfb 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -57,16 +57,21 @@ func TestRuntimeHashing(t *testing.T) { bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) bl := blocks.NewBlock([]byte("some data")) blBad, err := blocks.NewBlockWithHash([]byte("some other data"), bl.Key().ToMultihash()) + bl2 := blocks.NewBlock([]byte("some other data")) if err != nil { t.Fatal("Debug is enabled") } - bs.Put(blBad) + bs.Put(bl2) bs.RuntimeHashing(true) if _, err := bs.Get(bl.Key()); err != ErrHashMismatch { t.Fatalf("Expected '%v' got '%v'\n", ErrHashMismatch, err) } + + if b, err := bs.Get(bl2.Key()); err != nil || b.String() != bl2.String() { + t.Fatal("got wrong blocks") + } } func newBlockStoreWithKeys(t *testing.T, d ds.Datastore, N int) (Blockstore, []key.Key) { diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index fbffd42f5..2a2638eaf 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -66,6 +66,31 @@ func TestHasIsBloomCached(t *testing.T) { if float64(cacheFails)/float64(1000) > float64(0.05) { t.Fatal("Bloom filter has cache miss rate of more than 5%") } + + cacheFails = 0 + block := blocks.NewBlock([]byte("newBlock")) + + cachedbs.PutMany([]blocks.Block{block}) + if cacheFails != 2 { + t.Fatalf("expected two datastore hits: %d", cacheFails) + } + cachedbs.Put(block) + if cacheFails != 3 { + t.Fatalf("expected datastore hit: %d", cacheFails) + } + + if has, err := cachedbs.Has(block.Key()); !has || err != nil { + t.Fatal("has gave wrong response") + } + + bl, err := cachedbs.Get(block.Key()) + if bl.String() != block.String() { + t.Fatal("block data doesn't match") + } + + if err != nil { + t.Fatal("there should't be an error") + } } type callbackDatastore struct { diff --git a/blockstore/caching_test.go b/blockstore/caching_test.go new file mode 100644 index 000000000..473f79a30 --- /dev/null +++ b/blockstore/caching_test.go @@ -0,0 +1,35 @@ +package blockstore + +import "testing" + +func TestCachingOptsLessThanZero(t *testing.T) { + opts := DefaultCacheOpts() + opts.HasARCCacheSize = -1 + + if _, err := CachedBlockstore(nil, nil, opts); err == nil { + t.Fatal() + } + + opts = DefaultCacheOpts() + opts.HasBloomFilterSize = -1 + + if _, err := CachedBlockstore(nil, nil, opts); err == nil { + t.Fatal() + } + + opts = DefaultCacheOpts() + opts.HasBloomFilterHashes = -1 + + if _, err := CachedBlockstore(nil, nil, opts); err == nil { + t.Fatal() + } +} + +func TestBloomHashesAtZero(t *testing.T) { + opts := DefaultCacheOpts() + opts.HasBloomFilterHashes = 0 + + if _, err := CachedBlockstore(nil, nil, opts); err == nil { + t.Fatal() + } +} From 97666a90b21684731be7adde51d2d1d536fc4484 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 11 Aug 2016 17:45:22 -0400 Subject: [PATCH 1425/3817] Check for multiple pinned blocks in a single pass. Provide a new method, Pinner.CheckIfPinned(), which will check if any of the arguments are pinned. Previously IsPinned would need to be called once for each block. The new method will speed up the checking of multiple pinned blocks from O(p*n) to O(p) (where p is the number of pinned blocks and n is the number of blocks to be check) Use the new method in "block rm". License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@eb2035cb98c1fed1c84da35e40fd494ba3a67847 --- pinning/pinner/pin.go | 74 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index fa2af39db..49c5a8dd1 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -75,6 +75,10 @@ type Pinner interface { Pin(context.Context, *mdag.Node, bool) error Unpin(context.Context, key.Key, bool) error + // Check if a set of keys are pinned, more efficient than + // calling IsPinned for each key + CheckIfPinned(keys ...key.Key) ([]Pinned, error) + // PinWithMode is for manually editing the pin structure. Use with // care! If used improperly, garbage collection may not be // successful. @@ -90,6 +94,12 @@ type Pinner interface { InternalPins() []key.Key } +type Pinned struct { + Key key.Key + Mode PinMode + Via key.Key +} + // pinner implements the Pinner interface type pinner struct { lock sync.RWMutex @@ -255,6 +265,70 @@ func (p *pinner) isPinnedWithType(k key.Key, mode PinMode) (string, bool, error) return "", false, nil } +func (p *pinner) CheckIfPinned(keys ...key.Key) ([]Pinned, error) { + p.lock.RLock() + defer p.lock.RUnlock() + pinned := make([]Pinned, 0, len(keys)) + toCheck := make(map[key.Key]struct{}) + + // First check for non-Indirect pins directly + for _, k := range keys { + if p.recursePin.HasKey(k) { + pinned = append(pinned, Pinned{Key: k, Mode: Recursive}) + } else if p.directPin.HasKey(k) { + pinned = append(pinned, Pinned{Key: k, Mode: Direct}) + } else if p.isInternalPin(k) { + pinned = append(pinned, Pinned{Key: k, Mode: Internal}) + } else { + toCheck[k] = struct{}{} + } + } + + // Now walk all recursive pins to check for indirect pins + var checkChildren func(key.Key, key.Key) error + checkChildren = func(rk key.Key, parentKey key.Key) error { + parent, err := p.dserv.Get(context.Background(), parentKey) + if err != nil { + return err + } + for _, lnk := range parent.Links { + k := key.Key(lnk.Hash) + + if _, found := toCheck[k]; found { + pinned = append(pinned, + Pinned{Key: k, Mode: Indirect, Via: rk}) + delete(toCheck, k) + } + + err := checkChildren(rk, k) + if err != nil { + return err + } + + if len(toCheck) == 0 { + return nil + } + } + return nil + } + for _, rk := range p.recursePin.GetKeys() { + err := checkChildren(rk, rk) + if err != nil { + return nil, err + } + if len(toCheck) == 0 { + break + } + } + + // Anything left in toCheck is not pinned + for k, _ := range toCheck { + pinned = append(pinned, Pinned{Key: k, Mode: NotPinned}) + } + + return pinned, nil +} + func (p *pinner) RemovePinWithMode(key key.Key, mode PinMode) { p.lock.Lock() defer p.lock.Unlock() From 05223c88da24681d729a171fa39cb7ee850edf7a Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 16 Aug 2016 18:59:36 +0200 Subject: [PATCH 1426/3817] test: fixup style and add more checks to blockstore tests License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@edf6fdfeb8217b129cbca2b409f4e2ba50cd5d59 --- blockstore/blockstore_test.go | 26 ++++++++++++++++---------- blockstore/caching_test.go | 8 ++++---- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 2cca07cfb..6653a6259 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -8,23 +8,23 @@ import ( ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" dsq "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/query" ds_sync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" ) -// TODO(brian): TestGetReturnsNil - func TestGetWhenKeyNotPresent(t *testing.T) { bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) - _, err := bs.Get(key.Key("not present")) + bl, err := bs.Get(key.Key("not present")) - if err != nil { - t.Log("As expected, block is not present") - return + if bl != nil { + t.Error("nil block expected") + } + if err == nil { + t.Error("error expected, got nil") } - t.Fail() } func TestGetWhenKeyIsEmptyString(t *testing.T) { @@ -54,19 +54,25 @@ func TestPutThenGetBlock(t *testing.T) { } func TestRuntimeHashing(t *testing.T) { + orginalDebug := u.Debug + defer (func() { + u.Debug = orginalDebug + })() + u.Debug = false + bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) bl := blocks.NewBlock([]byte("some data")) blBad, err := blocks.NewBlockWithHash([]byte("some other data"), bl.Key().ToMultihash()) - bl2 := blocks.NewBlock([]byte("some other data")) if err != nil { - t.Fatal("Debug is enabled") + t.Fatal("debug is off, still got an error") } + bl2 := blocks.NewBlock([]byte("some other data")) bs.Put(blBad) bs.Put(bl2) bs.RuntimeHashing(true) if _, err := bs.Get(bl.Key()); err != ErrHashMismatch { - t.Fatalf("Expected '%v' got '%v'\n", ErrHashMismatch, err) + t.Fatalf("expected '%v' got '%v'\n", ErrHashMismatch, err) } if b, err := bs.Get(bl2.Key()); err != nil || b.String() != bl2.String() { diff --git a/blockstore/caching_test.go b/blockstore/caching_test.go index 473f79a30..3c3c19546 100644 --- a/blockstore/caching_test.go +++ b/blockstore/caching_test.go @@ -7,21 +7,21 @@ func TestCachingOptsLessThanZero(t *testing.T) { opts.HasARCCacheSize = -1 if _, err := CachedBlockstore(nil, nil, opts); err == nil { - t.Fatal() + t.Error("wrong ARC setting was not detected") } opts = DefaultCacheOpts() opts.HasBloomFilterSize = -1 if _, err := CachedBlockstore(nil, nil, opts); err == nil { - t.Fatal() + t.Error("negative bloom size was not detected") } opts = DefaultCacheOpts() opts.HasBloomFilterHashes = -1 if _, err := CachedBlockstore(nil, nil, opts); err == nil { - t.Fatal() + t.Error("negative hashes setting was not detected") } } @@ -30,6 +30,6 @@ func TestBloomHashesAtZero(t *testing.T) { opts.HasBloomFilterHashes = 0 if _, err := CachedBlockstore(nil, nil, opts); err == nil { - t.Fatal() + t.Error("zero hashes setting with positive size was not detected") } } From 43edaa9a380b67e1917574469e2c9406f915365a Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 16 Aug 2016 19:26:17 -0400 Subject: [PATCH 1427/3817] Fix bug in arccache.DeleteBlock() method. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-blockstore@885d31cb377f09f695289c0509937a69b2b2e98f --- blockstore/arc_cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 63253ef9c..2b6aa04e2 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -32,7 +32,7 @@ func (b *arccache) DeleteBlock(k key.Key) error { switch err { case nil, ds.ErrNotFound, ErrNotFound: b.arc.Add(k, false) - return nil + return err default: return err } From 92d9feff8fc11a028c61f2087b58f1090e57e6b6 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 17 Aug 2016 19:13:15 +0200 Subject: [PATCH 1428/3817] unixfs: cleanup imports and remove unused error License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@148c16f265fa132e5e4be8b539bb0fb3ba123cb5 --- unixfs/mod/dagmodifier.go | 9 ++++----- unixfs/mod/dagmodifier_test.go | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 66ba5f24b..dd8cfa70b 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -6,10 +6,6 @@ import ( "io" "os" - mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - key "github.com/ipfs/go-ipfs/blocks/key" chunk "github.com/ipfs/go-ipfs/importer/chunk" help "github.com/ipfs/go-ipfs/importer/helpers" @@ -17,11 +13,14 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" + logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) var ErrSeekFail = errors.New("failed to seek properly") -var ErrSeekEndNotImpl = errors.New("SEEK_END currently not implemented") var ErrUnrecognizedWhence = errors.New("unrecognized whence") // 2MB diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 7767f99d3..6ca38f63b 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -17,10 +17,10 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From 9e42a9090325966c48f6d1d752edbe8cf7190eba Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 17 Aug 2016 19:13:38 +0200 Subject: [PATCH 1429/3817] unixfs: fix relative seek not expanding file properly License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@6ecbaeb8b297999164d395e2cfd5b14c1db6d47a --- unixfs/mod/dagmodifier.go | 4 ++-- unixfs/mod/dagmodifier_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index dd8cfa70b..54af9997d 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -377,8 +377,8 @@ func (dm *DagModifier) Seek(offset int64, whence int) (int64, error) { return 0, ErrUnrecognizedWhence } - if offset > fisize { - if err := dm.expandSparse(offset - fisize); err != nil { + if int64(newoffset) > fisize { + if err := dm.expandSparse(int64(newoffset) - fisize); err != nil { return 0, err } } diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 6ca38f63b..9bfc63f7c 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -548,6 +548,36 @@ func TestSeekPastEndWrite(t *testing.T) { } } +func TestRelativeSeek(t *testing.T) { + dserv := getMockDagServ(t) + _, n := getNode(t, dserv, 0) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + if err != nil { + t.Fatal(err) + } + + for i := 0; i < 64; i++ { + dagmod.Write([]byte{byte(i)}) + if _, err := dagmod.Seek(1, os.SEEK_CUR); err != nil { + t.Fatal(err) + } + } + + out, err := ioutil.ReadAll(dagmod) + if err != nil { + t.Fatal(err) + } + + for i, v := range out { + if v != 0 && i/2 != int(v) { + t.Errorf("expected %d, at index %d, got %d", i/2, i, v) + } + } +} + func BenchmarkDagmodWrite(b *testing.B) { b.StopTimer() dserv := getMockDagServ(b) From afc83f900e2d8e9b6db2ef84333c785429547546 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 17 Aug 2016 19:39:09 +0200 Subject: [PATCH 1430/3817] unixfs: add more seek test cases License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@c0ee2cab4576ad2e1529e756e743ae02b0312d4f --- unixfs/mod/dagmodifier_test.go | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 9bfc63f7c..1e6d1968f 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -578,6 +578,57 @@ func TestRelativeSeek(t *testing.T) { } } +func TestInvalidSeek(t *testing.T) { + dserv := getMockDagServ(t) + + _, n := getNode(t, dserv, 0) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + if err != nil { + t.Fatal(err) + } + _, err = dagmod.Seek(10, -10) + + if err != ErrUnrecognizedWhence { + t.Fatal(err) + } +} + +func TestEndSeek(t *testing.T) { + dserv := getMockDagServ(t) + + _, n := getNode(t, dserv, 0) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + if err != nil { + t.Fatal(err) + } + + _, err = dagmod.Write(make([]byte, 100)) + if err != nil { + t.Fatal(err) + } + + offset, err := dagmod.Seek(0, os.SEEK_CUR) + if offset != 100 { + t.Fatal("expected the relative seek 0 to return current location") + } + + offset, err = dagmod.Seek(0, os.SEEK_SET) + if offset != 0 { + t.Fatal("expected the absolute seek to set offset at 0") + } + + offset, err = dagmod.Seek(0, os.SEEK_END) + if offset != 100 { + t.Fatal("expected the end seek to set offset at end") + } +} + func BenchmarkDagmodWrite(b *testing.B) { b.StopTimer() dserv := getMockDagServ(b) From 5056f61eda30525ae3b284c1df0c09a378e8e99c Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 17 Aug 2016 21:51:21 +0200 Subject: [PATCH 1431/3817] unixfs: add ReadAndSeek test License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@d094c87f4a672e96b959fa65a7dae7414497039c --- unixfs/io/dagreader.go | 4 +++ unixfs/mod/dagmodifier_test.go | 64 ++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index cbbe02858..0648f9600 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -210,6 +210,10 @@ func (dr *DagReader) Close() error { return nil } +func (dr *DagReader) Offset() int64 { + return dr.offset +} + // Seek implements io.Seeker, and will seek to a given offset in the file // interface matches standard unix seek // TODO: check if we can do relative seeks, to reduce the amount of dagreader diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 1e6d1968f..c92d5e220 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -629,6 +629,70 @@ func TestEndSeek(t *testing.T) { } } +func TestReadAndSeek(t *testing.T) { + dserv := getMockDagServ(t) + + _, n := getNode(t, dserv, 0) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + if err != nil { + t.Fatal(err) + } + + writeBuf := []byte{0, 1, 2, 3, 4, 5, 6, 7} + dagmod.Write(writeBuf) + + readBuf := make([]byte, 4) + offset, err := dagmod.Seek(0, os.SEEK_SET) + if offset != 0 { + t.Fatal("expected offset to be 0") + } + if err != nil { + t.Fatal(err) + } + + // read 0,1,2,3 + c, err := dagmod.Read(readBuf) + if err != nil { + t.Fatal(err) + } + if c != 4 { + t.Fatalf("expected length of 4 got %d", c) + } + + for i := byte(0); i < 4; i++ { + if readBuf[i] != i { + t.Fatalf("wrong value %d [at index %d]", readBuf[i], i) + } + } + + // skip 4 + _, err = dagmod.Seek(1, os.SEEK_CUR) + if err != nil { + t.Fatalf("error: %s, offset %d, reader offset %d", err, dagmod.curWrOff, dagmod.read.Offset()) + } + + //read 5,6,7 + readBuf = make([]byte, 3) + c, err = dagmod.Read(readBuf) + if err != nil { + t.Fatal(err) + } + if c != 3 { + t.Fatalf("expected length of 3 got %d", c) + } + + for i := byte(0); i < 3; i++ { + if readBuf[i] != i+5 { + t.Fatalf("wrong value %d [at index %d]", readBuf[i], i) + } + + } + +} + func BenchmarkDagmodWrite(b *testing.B) { b.StopTimer() dserv := getMockDagServ(b) From ae39c68b6cff85aa17d300cd84f52e64c04d4d02 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 18 Aug 2016 17:59:47 +0200 Subject: [PATCH 1432/3817] test: reach 80% coverage of unixfs/mod License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@ca57fa6ac4541d4e61f300915ca557bd72cd58f9 --- unixfs/mod/dagmodifier_test.go | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index c92d5e220..929ede941 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -644,6 +644,10 @@ func TestReadAndSeek(t *testing.T) { writeBuf := []byte{0, 1, 2, 3, 4, 5, 6, 7} dagmod.Write(writeBuf) + if !dagmod.HasChanges() { + t.Fatal("there are changes, this should be true") + } + readBuf := make([]byte, 4) offset, err := dagmod.Seek(0, os.SEEK_SET) if offset != 0 { @@ -693,6 +697,37 @@ func TestReadAndSeek(t *testing.T) { } +func TestCtxRead(t *testing.T) { + dserv := getMockDagServ(t) + + _, n := getNode(t, dserv, 0) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + if err != nil { + t.Fatal(err) + } + + _, err = dagmod.Write([]byte{0, 1, 2, 3, 4, 5, 6, 7}) + if err != nil { + t.Fatal(err) + } + dagmod.Seek(0, os.SEEK_SET) + + readBuf := make([]byte, 4) + _, err = dagmod.CtxReadFull(ctx, readBuf) + if err != nil { + t.Fatal(err) + } + err = arrComp(readBuf, []byte{0, 1, 2, 3}) + if err != nil { + t.Fatal(err) + } + // TODO(Kubuxu): context cancel case, I will do it after I figure out dagreader tests, + // because this is exacelly the same. +} + func BenchmarkDagmodWrite(b *testing.B) { b.StopTimer() dserv := getMockDagServ(b) From 1b5dd0154db3042de316343f81b754c892483f9f Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 18 Aug 2016 18:09:16 +0200 Subject: [PATCH 1433/3817] test: fix typo in blockstore test Also format imports License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@1064d01616ed4714180d0e4be2c529b6c3aa431d --- blockstore/arc_cache_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index b37092602..1d6041675 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -1,9 +1,10 @@ package blockstore import ( + "testing" + "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/key" - "testing" ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" syncds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" @@ -122,7 +123,7 @@ func TestGetFillsCache(t *testing.T) { } } -func TestGetAndDeleteFalseShortCirciot(t *testing.T) { +func TestGetAndDeleteFalseShortCircuit(t *testing.T) { arc, _, cd := createStores(t) arc.Has(exampleBlock.Key()) From ba991327ef068670b3da06bebbbac8c2bc4e3dc8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 19 Aug 2016 17:45:49 -0700 Subject: [PATCH 1434/3817] pin: use separate dagservice for storing pinsets License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@07070300892f522f97ab9895ac1f0f9f603fe27a --- ipld/merkledag/merkledag_test.go | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index dcf9ced1c..05ba260f1 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -10,7 +10,6 @@ import ( "sync" "testing" - bstore "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" bstest "github.com/ipfs/go-ipfs/blockservice/test" @@ -19,31 +18,11 @@ import ( chunk "github.com/ipfs/go-ipfs/importer/chunk" . "github.com/ipfs/go-ipfs/merkledag" dstest "github.com/ipfs/go-ipfs/merkledag/test" - "github.com/ipfs/go-ipfs/pin" uio "github.com/ipfs/go-ipfs/unixfs/io" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) -type dagservAndPinner struct { - ds DAGService - mp pin.Pinner -} - -func getDagservAndPinner(t *testing.T) dagservAndPinner { - db := dssync.MutexWrap(ds.NewMapDatastore()) - bs := bstore.NewBlockstore(db) - blockserv := bserv.New(bs, offline.Exchange(bs)) - dserv := NewDAGService(blockserv) - mpin := pin.NewPinner(db, dserv) - return dagservAndPinner{ - ds: dserv, - mp: mpin, - } -} - func TestNode(t *testing.T) { n1 := NodeWithData([]byte("beep")) @@ -254,7 +233,7 @@ func TestEmptyKey(t *testing.T) { } func TestCantGet(t *testing.T) { - dsp := getDagservAndPinner(t) + ds := dstest.Mock() a := NodeWithData([]byte("A")) k, err := a.Key() @@ -262,7 +241,7 @@ func TestCantGet(t *testing.T) { t.Fatal(err) } - _, err = dsp.ds.Get(context.Background(), k) + _, err = ds.Get(context.Background(), k) if !strings.Contains(err.Error(), "not found") { t.Fatal("expected err not found, got: ", err) } From b04b79b78f12bde55c5b2cf3054eb172d2aeeec8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 19 Aug 2016 17:45:49 -0700 Subject: [PATCH 1435/3817] pin: use separate dagservice for storing pinsets License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@e0bf4c12e0904808b01fca0fa8a268cec26574f1 --- pinning/pinner/pin.go | 23 ++++++++++++----------- pinning/pinner/pin_test.go | 10 +++++----- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 49c5a8dd1..2628359cb 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -110,15 +110,14 @@ type pinner struct { // not delete them. internalPin map[key.Key]struct{} dserv mdag.DAGService + internal mdag.DAGService // dagservice used to store internal objects dstore ds.Datastore } // NewPinner creates a new pinner using the given datastore as a backend -func NewPinner(dstore ds.Datastore, serv mdag.DAGService) Pinner { +func NewPinner(dstore ds.Datastore, serv, internal mdag.DAGService) Pinner { - // Load set from given datastore... rcset := set.NewSimpleBlockSet() - dirset := set.NewSimpleBlockSet() return &pinner{ @@ -126,6 +125,7 @@ func NewPinner(dstore ds.Datastore, serv mdag.DAGService) Pinner { directPin: dirset, dserv: serv, dstore: dstore, + internal: internal, } } @@ -344,7 +344,7 @@ func (p *pinner) RemovePinWithMode(key key.Key, mode PinMode) { } // LoadPinner loads a pinner and its keysets from the given datastore -func LoadPinner(d ds.Datastore, dserv mdag.DAGService) (Pinner, error) { +func LoadPinner(d ds.Datastore, dserv, internal mdag.DAGService) (Pinner, error) { p := new(pinner) rootKeyI, err := d.Get(pinDatastoreKey) @@ -361,7 +361,7 @@ func LoadPinner(d ds.Datastore, dserv mdag.DAGService) (Pinner, error) { ctx, cancel := context.WithTimeout(context.TODO(), time.Second*5) defer cancel() - root, err := dserv.Get(ctx, rootKey) + root, err := internal.Get(ctx, rootKey) if err != nil { return nil, fmt.Errorf("cannot find pinning root object: %v", err) } @@ -374,7 +374,7 @@ func LoadPinner(d ds.Datastore, dserv mdag.DAGService) (Pinner, error) { } { // load recursive set - recurseKeys, err := loadSet(ctx, dserv, root, linkRecursive, recordInternal) + recurseKeys, err := loadSet(ctx, internal, root, linkRecursive, recordInternal) if err != nil { return nil, fmt.Errorf("cannot load recursive pins: %v", err) } @@ -382,7 +382,7 @@ func LoadPinner(d ds.Datastore, dserv mdag.DAGService) (Pinner, error) { } { // load direct set - directKeys, err := loadSet(ctx, dserv, root, linkDirect, recordInternal) + directKeys, err := loadSet(ctx, internal, root, linkDirect, recordInternal) if err != nil { return nil, fmt.Errorf("cannot load direct pins: %v", err) } @@ -394,6 +394,7 @@ func LoadPinner(d ds.Datastore, dserv mdag.DAGService) (Pinner, error) { // assign services p.dserv = dserv p.dstore = d + p.internal = internal return p, nil } @@ -422,7 +423,7 @@ func (p *pinner) Flush() error { root := &mdag.Node{} { - n, err := storeSet(ctx, p.dserv, p.directPin.GetKeys(), recordInternal) + n, err := storeSet(ctx, p.internal, p.directPin.GetKeys(), recordInternal) if err != nil { return err } @@ -432,7 +433,7 @@ func (p *pinner) Flush() error { } { - n, err := storeSet(ctx, p.dserv, p.recursePin.GetKeys(), recordInternal) + n, err := storeSet(ctx, p.internal, p.recursePin.GetKeys(), recordInternal) if err != nil { return err } @@ -442,12 +443,12 @@ func (p *pinner) Flush() error { } // add the empty node, its referenced by the pin sets but never created - _, err := p.dserv.Add(new(mdag.Node)) + _, err := p.internal.Add(new(mdag.Node)) if err != nil { return err } - k, err := p.dserv.Add(root) + k, err := p.internal.Add(root) if err != nil { return err } diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 8e4cfd8a8..d6496618e 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -45,7 +45,7 @@ func TestPinnerBasic(t *testing.T) { dserv := mdag.NewDAGService(bserv) // TODO does pinner need to share datastore with blockservice? - p := NewPinner(dstore, dserv) + p := NewPinner(dstore, dserv, dserv) a, ak := randNode() _, err := dserv.Add(a) @@ -133,7 +133,7 @@ func TestPinnerBasic(t *testing.T) { t.Fatal(err) } - np, err := LoadPinner(dstore, dserv) + np, err := LoadPinner(dstore, dserv, dserv) if err != nil { t.Fatal(err) } @@ -154,7 +154,7 @@ func TestDuplicateSemantics(t *testing.T) { dserv := mdag.NewDAGService(bserv) // TODO does pinner need to share datastore with blockservice? - p := NewPinner(dstore, dserv) + p := NewPinner(dstore, dserv, dserv) a, _ := randNode() _, err := dserv.Add(a) @@ -187,7 +187,7 @@ func TestFlush(t *testing.T) { bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) - p := NewPinner(dstore, dserv) + p := NewPinner(dstore, dserv, dserv) _, k := randNode() p.PinWithMode(k, Recursive) @@ -204,7 +204,7 @@ func TestPinRecursiveFail(t *testing.T) { bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) - p := NewPinner(dstore, dserv) + p := NewPinner(dstore, dserv, dserv) a, _ := randNode() b, _ := randNode() From ccbc8933444f1a197406551163bc10bf7e88a243 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 19 Aug 2016 19:52:49 -0700 Subject: [PATCH 1436/3817] cmds: implement ipfs dht provide command License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@d6e466889b00758952a9c67accced988f54c8161 --- routing/dht/routing.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index b9e547cac..4bdb39a86 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -263,6 +263,10 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key key.Key) error { go func(p peer.ID) { defer wg.Done() log.Debugf("putProvider(%s, %s)", key, p) + notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ + Type: notif.FinalPeer, + ID: p, + }) err := dht.sendMessage(ctx, p, mes) if err != nil { log.Debug(err) @@ -272,6 +276,7 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key key.Key) error { wg.Wait() return nil } + func (dht *IpfsDHT) makeProvRecord(skey key.Key) (*pb.Message, error) { pi := pstore.PeerInfo{ ID: dht.self, From 356accf60e1ed78f779ec521c3801fbd8fdecec4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 20 Aug 2016 11:30:15 -0700 Subject: [PATCH 1437/3817] routing: rework interfaces to make separation easier License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@1ea91178f41963d4c215acd41fa36f01af83d96b --- routing/routing.go | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/routing/routing.go b/routing/routing.go index f58f4a737..6473ecc93 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -14,11 +14,27 @@ import ( // ErrNotFound is returned when a search fails to find anything var ErrNotFound = errors.New("routing: not found") -// IpfsRouting is the routing module interface -// It is implemented by things like DHTs, etc. -type IpfsRouting interface { +// ContentRouting is a value provider layer of indirection. It is used to find +// information about who has what content. +type ContentRouting interface { + // Announce that this node can provide value for given key + Provide(context.Context, key.Key) error + + // Search for peers who are able to provide a given key FindProvidersAsync(context.Context, key.Key, int) <-chan pstore.PeerInfo +} + +// PeerRouting is a way to find information about certain peers. +// This can be implemented by a simple lookup table, a tracking server, +// or even a DHT. +type PeerRouting interface { + // Find specific Peer + // FindPeer searches for a peer with given ID, returns a pstore.PeerInfo + // with relevant addresses. + FindPeer(context.Context, peer.ID) (pstore.PeerInfo, error) +} +type ValueStore interface { // Basic Put/Get // PutValue adds value corresponding to given Key. @@ -38,17 +54,15 @@ type IpfsRouting interface { // As a result, a value of '1' is mostly useful for cases where the record // in question has only one valid value (such as public keys) GetValues(c context.Context, k key.Key, count int) ([]RecvdVal, error) +} - // Value provider layer of indirection. - // This is what DSHTs (Coral and MainlineDHT) do to store large values in a DHT. - - // Announce that this node can provide value for given key - Provide(context.Context, key.Key) error - - // Find specific Peer - // FindPeer searches for a peer with given ID, returns a pstore.PeerInfo - // with relevant addresses. - FindPeer(context.Context, peer.ID) (pstore.PeerInfo, error) +// IpfsRouting is the combination of different routing types that ipfs +// uses. It can be satisfied by a single item (such as a DHT) or multiple +// different pieces that are more optimized to each task. +type IpfsRouting interface { + ContentRouting + PeerRouting + ValueStore // Bootstrap allows callers to hint to the routing system to get into a // Boostrapped state @@ -74,7 +88,7 @@ func KeyForPublicKey(id peer.ID) key.Key { return key.Key("/pk/" + string(id)) } -func GetPublicKey(r IpfsRouting, ctx context.Context, pkhash []byte) (ci.PubKey, error) { +func GetPublicKey(r ValueStore, ctx context.Context, pkhash []byte) (ci.PubKey, error) { if dht, ok := r.(PubKeyFetcher); ok { // If we have a DHT as our routing system, use optimized fetcher return dht.GetPublicKey(ctx, peer.ID(pkhash)) From 97d62a48ade25e12bd6c85fd7f0e48212b6494ef Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 20 Aug 2016 11:30:15 -0700 Subject: [PATCH 1438/3817] routing: rework interfaces to make separation easier License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@2691567ba3c0df4261bfae37263c88aa915eda54 --- namesys/namesys.go | 2 +- namesys/publisher.go | 10 +++++----- namesys/republisher/repub.go | 4 ++-- namesys/routing.go | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 0830fb269..29a831f45 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -26,7 +26,7 @@ type mpns struct { } // NewNameSystem will construct the IPFS naming system based on Routing -func NewNameSystem(r routing.IpfsRouting, ds ds.Datastore, cachesize int) NameSystem { +func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSystem { return &mpns{ resolvers: map[string]resolver{ "dns": newDNSResolver(), diff --git a/namesys/publisher.go b/namesys/publisher.go index 6c4b7e790..f18231445 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -37,12 +37,12 @@ var PublishPutValTimeout = time.Minute // ipnsPublisher is capable of publishing and resolving names to the IPFS // routing system. type ipnsPublisher struct { - routing routing.IpfsRouting + routing routing.ValueStore ds ds.Datastore } // NewRoutingPublisher constructs a publisher for the IPFS Routing name system. -func NewRoutingPublisher(route routing.IpfsRouting, ds ds.Datastore) *ipnsPublisher { +func NewRoutingPublisher(route routing.ValueStore, ds ds.Datastore) *ipnsPublisher { if ds == nil { panic("nil datastore") } @@ -134,7 +134,7 @@ func checkCtxTTL(ctx context.Context) (time.Duration, bool) { return d, ok } -func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.IpfsRouting, id peer.ID) error { +func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.ValueStore, id peer.ID) error { ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -181,7 +181,7 @@ func waitOnErrChan(ctx context.Context, errs chan error) error { } } -func PublishPublicKey(ctx context.Context, r routing.IpfsRouting, k key.Key, pubk ci.PubKey) error { +func PublishPublicKey(ctx context.Context, r routing.ValueStore, k key.Key, pubk ci.PubKey) error { log.Debugf("Storing pubkey at: %s", k) pkbytes, err := pubk.Bytes() if err != nil { @@ -199,7 +199,7 @@ func PublishPublicKey(ctx context.Context, r routing.IpfsRouting, k key.Key, pub return nil } -func PublishEntry(ctx context.Context, r routing.IpfsRouting, ipnskey key.Key, rec *pb.IpnsEntry) error { +func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey key.Key, rec *pb.IpnsEntry) error { timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout) defer cancel() diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 5de248c12..633407fd1 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -31,7 +31,7 @@ var DefaultRebroadcastInterval = time.Hour * 4 const DefaultRecordLifetime = time.Hour * 24 type Republisher struct { - r routing.IpfsRouting + r routing.ValueStore ds ds.Datastore ps pstore.Peerstore @@ -44,7 +44,7 @@ type Republisher struct { entries map[peer.ID]struct{} } -func NewRepublisher(r routing.IpfsRouting, ds ds.Datastore, ps pstore.Peerstore) *Republisher { +func NewRepublisher(r routing.ValueStore, ds ds.Datastore, ps pstore.Peerstore) *Republisher { return &Republisher{ r: r, ps: ps, diff --git a/namesys/routing.go b/namesys/routing.go index 79f38939b..d613044ba 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -23,7 +23,7 @@ var log = logging.Logger("namesys") // routingResolver implements NSResolver for the main IPFS SFS-like naming type routingResolver struct { - routing routing.IpfsRouting + routing routing.ValueStore cache *lru.Cache } @@ -88,7 +88,7 @@ type cacheEntry struct { // to implement SFS-like naming on top. // cachesize is the limit of the number of entries in the lru cache. Setting it // to '0' will disable caching. -func NewRoutingResolver(route routing.IpfsRouting, cachesize int) *routingResolver { +func NewRoutingResolver(route routing.ValueStore, cachesize int) *routingResolver { if route == nil { panic("attempt to create resolver with nil routing system") } From 22347201c4615f3058b0584b1acb4c3135410d5f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 23 Aug 2016 12:30:10 -0700 Subject: [PATCH 1439/3817] improve test coverage on merkledag package License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@738204f275b866333541451687e7a07b92966915 --- ipld/merkledag/merkledag_test.go | 25 ++++++++++ ipld/merkledag/node_test.go | 79 +++++++++++++++++++++++++++++++- 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 05ba260f1..38545ac12 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -17,6 +17,7 @@ import ( imp "github.com/ipfs/go-ipfs/importer" chunk "github.com/ipfs/go-ipfs/importer/chunk" . "github.com/ipfs/go-ipfs/merkledag" + mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" @@ -354,3 +355,27 @@ func TestFetchFailure(t *testing.T) { } } } + +func TestUnmarshalFailure(t *testing.T) { + badData := []byte("hello world") + + _, err := DecodeProtobuf(badData) + if err == nil { + t.Fatal("shouldnt succeed to parse this") + } + + // now with a bad link + pbn := &mdpb.PBNode{Links: []*mdpb.PBLink{{Hash: []byte("not a multihash")}}} + badlink, err := pbn.Marshal() + if err != nil { + t.Fatal(err) + } + + _, err = DecodeProtobuf(badlink) + if err == nil { + t.Fatal("should have failed to parse node with bad link") + } + + n := &Node{} + n.Marshal() +} diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index 75aa4c988..d248ad359 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -1,7 +1,12 @@ -package merkledag +package merkledag_test import ( "testing" + + . "github.com/ipfs/go-ipfs/merkledag" + mdtest "github.com/ipfs/go-ipfs/merkledag/test" + + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) func TestRemoveLink(t *testing.T) { @@ -52,3 +57,75 @@ func TestRemoveLink(t *testing.T) { t.Fatal("link order wrong") } } + +func TestFindLink(t *testing.T) { + ds := mdtest.Mock() + k, err := ds.Add(new(Node)) + if err != nil { + t.Fatal(err) + } + + nd := &Node{ + Links: []*Link{ + &Link{Name: "a", Hash: k.ToMultihash()}, + &Link{Name: "c", Hash: k.ToMultihash()}, + &Link{Name: "b", Hash: k.ToMultihash()}, + }, + } + + _, err = ds.Add(nd) + if err != nil { + t.Fatal(err) + } + + lnk, err := nd.GetNodeLink("b") + if err != nil { + t.Fatal(err) + } + + if lnk.Name != "b" { + t.Fatal("got wrong link back") + } + + _, err = nd.GetNodeLink("f") + if err != ErrLinkNotFound { + t.Fatal("shouldnt have found link") + } + + _, err = nd.GetLinkedNode(context.Background(), ds, "b") + if err != nil { + t.Fatal(err) + } + + outnd, err := nd.UpdateNodeLink("b", nd) + if err != nil { + t.Fatal(err) + } + + olnk, err := outnd.GetNodeLink("b") + if err != nil { + t.Fatal(err) + } + + if olnk.Hash.B58String() == k.B58String() { + t.Fatal("new link should have different hash") + } +} + +func TestNodeCopy(t *testing.T) { + nd := &Node{ + Links: []*Link{ + &Link{Name: "a"}, + &Link{Name: "c"}, + &Link{Name: "b"}, + }, + } + nd.SetData([]byte("testing")) + + ond := nd.Copy() + ond.SetData(nil) + + if nd.Data() == nil { + t.Fatal("should be different objects") + } +} From 2bf079f0680559eaa3cc8899b6d1af37cf2e3694 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 19 Aug 2016 18:33:44 -0700 Subject: [PATCH 1440/3817] blockservice: don't store blocks we already have License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@be6f25b06132335e5d55bec19e09ef78ffdb5a2e --- blockservice/blockservice.go | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 710580614..12aa022c0 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -43,7 +43,15 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) *BlockService { // TODO pass a context into this if the remote.HasBlock is going to remain here. func (s *BlockService) AddBlock(b blocks.Block) (key.Key, error) { k := b.Key() - err := s.Blockstore.Put(b) + has, err := s.Blockstore.Has(k) + if err != nil { + return k, err + } + if has { + return k, nil + } + + err = s.Blockstore.Put(b) if err != nil { return k, err } @@ -54,13 +62,27 @@ func (s *BlockService) AddBlock(b blocks.Block) (key.Key, error) { } func (s *BlockService) AddBlocks(bs []blocks.Block) ([]key.Key, error) { - err := s.Blockstore.PutMany(bs) + var toput []blocks.Block + for _, b := range bs { + has, err := s.Blockstore.Has(b.Key()) + if err != nil { + return nil, err + } + + if has { + continue + } + + toput = append(toput, b) + } + + err := s.Blockstore.PutMany(toput) if err != nil { return nil, err } var ks []key.Key - for _, b := range bs { + for _, b := range toput { if err := s.Exchange.HasBlock(b); err != nil { return nil, errors.New("blockservice is closed") } From f54512fba1751e671773748148c6269735fda6f4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Aug 2016 22:29:25 -0700 Subject: [PATCH 1441/3817] update deps for libp2p 3.4.0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@a41d31c6eef9e8996dc2bd61fd47c51da6bb0026 --- mfs/mfs_test.go | 4 ++-- mfs/system.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 383bcfd73..f7398e83b 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,8 +14,8 @@ import ( "time" "github.com/ipfs/go-ipfs/path" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" randbo "gx/ipfs/QmYvsG72GsfLgUeSojXArjnU6L4Wmwk7wuAxtNLuyXcc1T/randbo" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/mfs/system.go b/mfs/system.go index 40d9d29cd..56891cc21 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -18,7 +18,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From 7095a19af51369bfdf396f66e50afd10432ef108 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Aug 2016 22:29:25 -0700 Subject: [PATCH 1442/3817] update deps for libp2p 3.4.0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@6877d2878caba1500f97e08188485995a957d41e --- blockstore/arc_cache.go | 2 +- blockstore/arc_cache_test.go | 4 ++-- blockstore/blockstore.go | 8 ++++---- blockstore/blockstore_test.go | 6 +++--- blockstore/bloom_cache_test.go | 6 +++--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 2b6aa04e2..c0ec19231 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -3,7 +3,7 @@ package blockstore import ( "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 1d6041675..175701232 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -6,8 +6,8 @@ import ( "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/key" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - syncds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + syncds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 380e0b640..f96178b44 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -9,10 +9,10 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dsns "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/namespace" - dsq "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/query" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + dsns "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/namespace" + dsq "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/query" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 6653a6259..fc78ca6e9 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -5,9 +5,9 @@ import ( "fmt" "testing" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dsq "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/query" - ds_sync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + dsq "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/query" + ds_sync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 2a2638eaf..607bf8d24 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -8,9 +8,9 @@ import ( "github.com/ipfs/go-ipfs/blocks" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dsq "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/query" - syncds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + dsq "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/query" + syncds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From e48198822c610b75fc1bb21ea8533f4546e6a4f3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Aug 2016 22:29:25 -0700 Subject: [PATCH 1443/3817] update deps for libp2p 3.4.0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@113e239083637282aea222f44a31990ccd8a6d92 --- ipld/merkledag/merkledag.go | 2 +- ipld/merkledag/test/utils.go | 4 ++-- ipld/merkledag/utils/utils.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 835d26cbf..7d601d86d 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -9,7 +9,7 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index bc0177b11..59bbd4979 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -5,8 +5,8 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" ) func Mock() dag.DAGService { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 15dddff68..a1e4125f7 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -3,8 +3,8 @@ package dagutils import ( "errors" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - syncds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + syncds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" From 6c7141a1c5f534d74f5cb56ddedf429a6341e1a4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Aug 2016 22:29:25 -0700 Subject: [PATCH 1444/3817] update deps for libp2p 3.4.0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@5ff692f28bd6bd132de969a3674424ee64254a85 --- blockservice/blockservice.go | 2 +- blockservice/test/blocks_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 12aa022c0..25282a441 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -10,7 +10,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index b7df8721c..81d61818b 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -11,8 +11,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From f3be971804513f1ff35517266006e7bb5a036fc9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Aug 2016 22:29:25 -0700 Subject: [PATCH 1445/3817] update deps for libp2p 3.4.0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@6152fe93f65a5569ec8c82ef72481c3b00f6df4c --- namesys/interface.go | 2 +- namesys/ipns_select_test.go | 2 +- namesys/namesys.go | 4 ++-- namesys/publisher.go | 6 +++--- namesys/republisher/repub.go | 8 ++++---- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 6 +++--- namesys/routing.go | 4 ++-- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index e4a4c3596..ae734af4f 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -34,7 +34,7 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" + ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index 4660948cc..ca04674c5 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -10,7 +10,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" + ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/namesys/namesys.go b/namesys/namesys.go index 29a831f45..699cc5327 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,8 +6,8 @@ import ( path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index f18231445..77604ae95 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -6,7 +6,7 @@ import ( "fmt" "time" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" @@ -19,8 +19,8 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" + ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 633407fd1..91228ea52 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -12,12 +12,12 @@ import ( "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 26056ca97..be54999eb 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - mocknet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + mocknet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 2adbe6349..57e08ccdb 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -9,9 +9,9 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/namesys/routing.go b/namesys/routing.go index d613044ba..d2c4fda11 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -14,8 +14,8 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" - ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) From b50d5d059307fbe8c0cc2af0ca072a15bafcd221 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Aug 2016 22:29:25 -0700 Subject: [PATCH 1446/3817] update deps for libp2p 3.4.0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@82557145f4da20d9f1655a04fc059a854cd71795 --- unixfs/mod/dagmodifier.go | 2 +- unixfs/mod/dagmodifier_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 54af9997d..784cef8a4 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -14,7 +14,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 929ede941..815ac5fc0 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -18,8 +18,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From 30f9160e072df59b7ea152d4cdfb4c5215af7814 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Aug 2016 22:29:25 -0700 Subject: [PATCH 1447/3817] update deps for libp2p 3.4.0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@8593ac21c56c9d0a0ae78682f7f6def21ff47f0d --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set_test.go | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 487f7947e..0eb87f867 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,7 +8,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 2628359cb..d034cbc43 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,8 +10,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index d6496618e..91c6b8c0e 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -11,8 +11,8 @@ import ( bs "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index e4c8bd4de..83d65dd02 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -9,8 +9,8 @@ import ( "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" "github.com/ipfs/go-ipfs/merkledag" - "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" From 857d21972e926c9f191dbf80c137e1a49c9381ad Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Aug 2016 22:29:25 -0700 Subject: [PATCH 1448/3817] update deps for libp2p 3.4.0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@29fb6a6dca42648bb7ede9f9a53eb15e8fc03523 --- exchange/offline/offline_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index b7962e2d7..6f1687586 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -7,8 +7,8 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" key "github.com/ipfs/go-ipfs/blocks/key" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - ds_sync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + ds_sync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From d98d19b8182e960e73f99d9baed4034c5cecfc7c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Aug 2016 22:29:25 -0700 Subject: [PATCH 1449/3817] update deps for libp2p 3.4.0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@5699b0621fd8235dd61a3272d85681213331d874 --- path/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/resolver.go b/path/resolver.go index e5e94f2ff..a254f456c 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -11,7 +11,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" merkledag "github.com/ipfs/go-ipfs/merkledag" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ) var log = logging.Logger("path") From dcb3e1a4c4019cf25f91ea0aca5371365663962c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Aug 2016 22:29:25 -0700 Subject: [PATCH 1450/3817] update deps for libp2p 3.4.0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@233550e1075b96054390295687206f335f6306e5 --- routing/dht/dht.go | 14 +++++++------- routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 6 +++--- routing/dht/dht_test.go | 10 +++++----- routing/dht/ext_test.go | 12 ++++++------ routing/dht/handlers.go | 6 +++--- routing/dht/lookup.go | 4 ++-- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 8 ++++---- routing/dht/providers/providers.go | 10 +++++----- routing/dht/providers/providers_test.go | 4 ++-- routing/dht/query.go | 8 ++++---- routing/dht/records.go | 4 ++-- routing/dht/routing.go | 6 +++--- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 6 +++--- routing/kbucket/table_test.go | 4 ++-- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 8 ++++---- routing/mock/centralized_server.go | 8 ++++---- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 6 +++--- routing/mock/interface.go | 6 +++--- routing/none/none_client.go | 8 ++++---- routing/offline/offline.go | 10 +++++----- routing/record/record.go | 4 ++-- routing/record/validation.go | 2 +- routing/record/validation_test.go | 2 +- routing/routing.go | 6 +++--- routing/supernode/client.go | 8 ++++---- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 14 +++++++------- routing/supernode/server.go | 6 +++--- routing/supernode/server_test.go | 2 +- 35 files changed, 104 insertions(+), 104 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index ba2d197aa..6de362e13 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -16,17 +16,17 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" - host "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/host" - protocol "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/protocol" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + host "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/host" + protocol "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/protocol" ) var log = logging.Logger("dht") diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 5f1447299..46f549ab9 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -9,7 +9,7 @@ import ( "time" routing "github.com/ipfs/go-ipfs/routing" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 405ae1572..92819b437 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,11 +6,11 @@ import ( "time" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - inet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" ctxio "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/io" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" ) var dhtReadMessageTimeout = time.Minute @@ -141,7 +141,7 @@ func (ms *messageSender) prep() error { return nil } - nstr, err := ms.dht.host.NewStream(ms.dht.ctx, ProtocolDHT, ms.p) + nstr, err := ms.dht.host.NewStream(ms.dht.ctx, ms.p, ProtocolDHT) if err != nil { return err } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 0abc27ed7..8243d2ed9 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -14,15 +14,15 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - netutil "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/test/util" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + netutil "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/test/util" ) var testCaseValues = map[key.Key][]byte{} diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index bcc95aff1..bbfe02538 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -10,15 +10,15 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - inet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" + mocknet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net/mock" ) func TestGetFailures(t *testing.T) { @@ -118,7 +118,7 @@ func TestGetFailures(t *testing.T) { Record: rec, } - s, err := hosts[1].NewStream(context.Background(), ProtocolDHT, hosts[0].ID()) + s, err := hosts[1].NewStream(context.Background(), hosts[0].ID(), ProtocolDHT) if err != nil { t.Fatal(err) } diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index feaf9aea2..b12582a94 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -8,10 +8,10 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index db4d651a2..2a279c89f 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -6,8 +6,8 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 3fdd435fe..4a55724bf 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - inet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net" + inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 26b261535..d7c4dd7d2 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,10 +4,10 @@ import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" key "github.com/ipfs/go-ipfs/blocks/key" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - inet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/providers/providers.go b/routing/dht/providers/providers.go index 286108a15..e48aaccef 100644 --- a/routing/dht/providers/providers.go +++ b/routing/dht/providers/providers.go @@ -6,15 +6,15 @@ import ( "strings" "time" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + dsq "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/query" goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dsq "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/query" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - autobatch "gx/ipfs/QmVvJ27GcLaLSXvcB4auk3Gn3xuWK5ti5ENkZ2pCoJEYW4/autobatch" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" base32 "gx/ipfs/Qmb1DA2A9LS2wR4FFweB4uEDomFsdmnw1VLawLE1yQzudj/base32" + autobatch "gx/ipfs/QmcRHLm2aqDabkpcto1NzLad7YQhH99MGDHSWWvwMxKiZw/autobatch" key "github.com/ipfs/go-ipfs/blocks/key" flags "github.com/ipfs/go-ipfs/flags" diff --git a/routing/dht/providers/providers_test.go b/routing/dht/providers/providers_test.go index 287dff6c0..32ede4469 100644 --- a/routing/dht/providers/providers_test.go +++ b/routing/dht/providers/providers_test.go @@ -6,8 +6,8 @@ import ( "time" key "github.com/ipfs/go-ipfs/blocks/key" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index 3cf1434a4..2b1efb337 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -9,12 +9,12 @@ import ( pset "github.com/ipfs/go-ipfs/thirdparty/peerset" todoctr "github.com/ipfs/go-ipfs/thirdparty/todocounter" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - queue "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore/queue" process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" ctxproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + queue "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore/queue" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/records.go b/routing/dht/records.go index d920e9843..0b461382a 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -7,8 +7,8 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" + ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" ctxfrac "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/frac" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 4bdb39a86..abd9c42ff 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -15,10 +15,10 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - inet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" ) // asyncQueryBuffer is the size of buffered channels in async queries. This diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index d835e24fd..171436279 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index ff9dc3d89..19ea84f68 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 3898af458..47a3228ca 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,9 +7,9 @@ import ( "sync" "time" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" ) var log = logging.Logger("table") diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 6a0c75e5b..115fd34ea 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,8 +7,8 @@ import ( tu "github.com/ipfs/go-ipfs/thirdparty/testutil" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index f9fbed060..58cd3c3e3 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -7,7 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 6847bd278..26437b310 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,11 +8,11 @@ import ( routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index c13c3a70c..b7fe52d9a 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -7,11 +7,11 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 2b407c5e3..3cd8e388a 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,7 +8,7 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 044905eb2..7e09cdf28 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - sync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" - mocknet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net/mock" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + sync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + mocknet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index e4e028977..b86e25f06 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -9,9 +9,9 @@ import ( routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 140c3f84e..2ff9fa68e 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,11 +6,11 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - p2phost "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/host" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + p2phost "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/host" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 1d85f3ac0..855f35191 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -8,12 +8,12 @@ import ( routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" + "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/record/record.go b/routing/record/record.go index 0a8a29a39..316763f7f 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -7,8 +7,8 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" - ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" ) var log = logging.Logger("routing/record") diff --git a/routing/record/validation.go b/routing/record/validation.go index 23d892236..a17e36fad 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -8,7 +8,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" + ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) diff --git a/routing/record/validation_test.go b/routing/record/validation_test.go index ae389244e..56bf6a842 100644 --- a/routing/record/validation_test.go +++ b/routing/record/validation_test.go @@ -5,7 +5,7 @@ import ( "testing" key "github.com/ipfs/go-ipfs/blocks/key" - ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" + ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" ) var OffensiveKey = "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDjXAQQMal4SB2tSnX6NJIPmC69/BT8A8jc7/gDUZNkEhdhYHvc7k7S4vntV/c92nJGxNdop9fKJyevuNMuXhhHAgMBAAE=" diff --git a/routing/routing.go b/routing/routing.go index 6473ecc93..ad12981f2 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -5,9 +5,9 @@ import ( "errors" key "github.com/ipfs/go-ipfs/blocks/key" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index cba449742..17839f3aa 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -11,12 +11,12 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" loggables "github.com/ipfs/go-ipfs/thirdparty/loggables" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/host" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/host" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 2dc2e7721..379527f07 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - inet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 438cdd99f..693fe6bc9 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -6,11 +6,11 @@ import ( ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" - host "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/host" - inet "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + host "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/host" + inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" @@ -101,7 +101,7 @@ func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote pe if err = px.Host.Connect(ctx, pstore.PeerInfo{ID: remote}); err != nil { return err } - s, err := px.Host.NewStream(ctx, ProtocolSNR, remote) + s, err := px.Host.NewStream(ctx, remote, ProtocolSNR) if err != nil { return err } @@ -136,7 +136,7 @@ func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote pe e.SetError(err) return nil, err } - s, err := px.Host.NewStream(ctx, ProtocolSNR, remote) + s, err := px.Host.NewStream(ctx, remote, ProtocolSNR) if err != nil { e.SetError(err) return nil, err diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 42f5720f5..f34c5eb2b 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,10 +8,10 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - datastore "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + datastore "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" - peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 025dd1dc2..25c54ec32 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -5,7 +5,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - datastore "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + datastore "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 9c3b450ea5657bdd42c1dd2bd9b9a150f0c9eec9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Aug 2016 22:29:25 -0700 Subject: [PATCH 1451/3817] update deps for libp2p 3.4.0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-chunker@28b8a70b9f54c6f8e38040e886bf03426f5d9f82 --- chunker/splitting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 98cdef739..f3256c458 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -4,7 +4,7 @@ package chunk import ( "io" - logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ) var log = logging.Logger("chunk") From b089a0a7b92c9b6cb07f621d471a065e433abfbd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 23 Aug 2016 22:23:31 -0700 Subject: [PATCH 1452/3817] remove randbo dep, its no longer needed License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@f80f90c315875a00c8a4f5239b449ab793a8e334 --- mfs/mfs_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index f7398e83b..f4aba72cb 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -13,12 +13,6 @@ import ( "testing" "time" - "github.com/ipfs/go-ipfs/path" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" - randbo "gx/ipfs/QmYvsG72GsfLgUeSojXArjnU6L4Wmwk7wuAxtNLuyXcc1T/randbo" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - bstore "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" @@ -26,9 +20,14 @@ import ( importer "github.com/ipfs/go-ipfs/importer" chunk "github.com/ipfs/go-ipfs/importer/chunk" dag "github.com/ipfs/go-ipfs/merkledag" + "github.com/ipfs/go-ipfs/path" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" + + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) func emptyDirNode() *dag.Node { @@ -552,7 +551,8 @@ func actorMakeFile(d *Directory) error { return err } - r := io.LimitReader(randbo.New(), int64(77*rand.Intn(123))) + rread := rand.New(rand.NewSource(time.Now().UnixNano())) + r := io.LimitReader(rread, int64(77*rand.Intn(123))) _, err = io.Copy(wfd, r) if err != nil { return err @@ -646,7 +646,7 @@ func actorWriteFile(d *Directory) error { size := rand.Intn(1024) + 1 buf := make([]byte, size) - randbo.New().Read(buf) + rand.Read(buf) s, err := fi.Size() if err != nil { @@ -858,7 +858,7 @@ func TestConcurrentReads(t *testing.T) { d := mkdirP(t, rootdir, path) buf := make([]byte, 2048) - randbo.New().Read(buf) + rand.Read(buf) fi := fileNodeFromReader(t, ds, bytes.NewReader(buf)) err := d.AddChild("afile", fi) From 7b2d1ebee67a03ebe3ad213849719891b8c53c7a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 26 Aug 2016 13:56:47 -0700 Subject: [PATCH 1453/3817] use correct protocol names for ipfs services License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@c953271975bc4b658360513a5cd445a66648b451 --- routing/dht/dht.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 6de362e13..29660c6fc 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -31,7 +31,8 @@ import ( var log = logging.Logger("dht") -var ProtocolDHT protocol.ID = "/ipfs/dht" +var ProtocolDHT protocol.ID = "/ipfs/kad/1.0.0" +var ProtocolDHTOld protocol.ID = "/ipfs/dht" // NumBootstrapQueries defines the number of random dht queries to do to // collect members of the routing table. @@ -85,6 +86,7 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.Batching) *IpfsDHT { dht.ctx = ctx h.SetStreamHandler(ProtocolDHT, dht.handleNewStream) + h.SetStreamHandler(ProtocolDHTOld, dht.handleNewStream) dht.providers = providers.NewProviderManager(dht.ctx, dht.self, dstore) dht.proc.AddChild(dht.providers.Process()) goprocessctx.CloseAfterContext(dht.proc, ctx) From 40921fbe2732dbfac2d7d6ee94bc82bfee4998d7 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 29 Aug 2016 21:53:40 +0200 Subject: [PATCH 1454/3817] blockstore: rename RuntimeHashing to HashOnRead License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@5d79e53dbeef5d7d597d23c8083195df5f2327f3 --- blockstore/blockstore.go | 2 +- blockstore/blockstore_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index f96178b44..420046773 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -76,7 +76,7 @@ type blockstore struct { rehash bool } -func (bs *blockstore) RuntimeHashing(enabled bool) { +func (bs *blockstore) HashOnRead(enabled bool) { bs.rehash = enabled } diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index fc78ca6e9..9d97cb542 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -53,7 +53,7 @@ func TestPutThenGetBlock(t *testing.T) { } } -func TestRuntimeHashing(t *testing.T) { +func TestHashOnRead(t *testing.T) { orginalDebug := u.Debug defer (func() { u.Debug = orginalDebug @@ -69,7 +69,7 @@ func TestRuntimeHashing(t *testing.T) { bl2 := blocks.NewBlock([]byte("some other data")) bs.Put(blBad) bs.Put(bl2) - bs.RuntimeHashing(true) + bs.HashOnRead(true) if _, err := bs.Get(bl.Key()); err != ErrHashMismatch { t.Fatalf("expected '%v' got '%v'\n", ErrHashMismatch, err) From 6f45593ddc60aeddd12fdbb11b6ddc06ca620804 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 31 Aug 2016 12:56:06 +0200 Subject: [PATCH 1455/3817] blockstore: fix PutMany with cache logic Thanks @whyrusleeping for noticing it. Removed PutMany logic in bloom cache as it can't help with anything. Fixed ARC cache to use filtered results instad of all blocks. License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@892824cfb334756f584a9ddb8bc7043efef0e370 --- blockstore/arc_cache.go | 7 +++++-- blockstore/bloom_cache.go | 26 +++++++++++++------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index c0ec19231..10ef8b01b 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -3,6 +3,7 @@ package blockstore import ( "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" @@ -95,15 +96,17 @@ func (b *arccache) Put(bl blocks.Block) error { func (b *arccache) PutMany(bs []blocks.Block) error { var good []blocks.Block for _, block := range bs { + // call put on block if result is inconclusive or we are sure that + // the block isn't in storage if has, ok := b.hasCached(block.Key()); !ok || (ok && !has) { good = append(good, block) } } - err := b.blockstore.PutMany(bs) + err := b.blockstore.PutMany(good) if err != nil { return err } - for _, block := range bs { + for _, block := range good { b.arc.Add(block.Key(), true) } return nil diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index e10dacfaf..b064b77db 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -1,12 +1,13 @@ package blockstore import ( + "sync/atomic" + "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" + bloom "gx/ipfs/QmWQ2SJisXwcCLsUXLwYCKSfyExXjFRW2WbBH5sqCUnwX5/bbloom" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - - "sync/atomic" ) // bloomCached returns Blockstore that caches Has requests using Bloom filter @@ -126,19 +127,18 @@ func (b *bloomcache) Put(bl blocks.Block) error { } func (b *bloomcache) PutMany(bs []blocks.Block) error { - var good []blocks.Block - for _, block := range bs { - if has, ok := b.hasCached(block.Key()); !ok || (ok && !has) { - good = append(good, block) - } - } + // bloom cache gives only conclusive resulty if key is not contained + // to reduce number of puts we need conclusive infomration if block is contained + // this means that PutMany can't be improved with bloom cache so we just + // just do a passthrough. err := b.blockstore.PutMany(bs) - if err == nil { - for _, block := range bs { - b.bloom.AddTS([]byte(block.Key())) - } + if err != nil { + return err } - return err + for _, bl := range bs { + b.bloom.AddTS([]byte(bl.Key())) + } + return nil } func (b *bloomcache) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { From 038104eb81257de7e079505c5b5d59afc901e361 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 31 Aug 2016 13:05:07 +0200 Subject: [PATCH 1456/3817] test: add test case for PutMany using cache to eliminate the call License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@186e5a2b06ea6f3fb0fb344bf913762328db56e3 --- blockstore/arc_cache_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 175701232..ac61496d2 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -175,4 +175,10 @@ func TestPutManyCaches(t *testing.T) { trap("has hit datastore", cd, t) arc.Has(exampleBlock.Key()) + untrap(cd) + arc.DeleteBlock(exampleBlock.Key()) + + arc.Put(exampleBlock) + trap("PunMany has hit datastore", cd, t) + arc.PutMany([]blocks.Block{exampleBlock}) } From 61dbf8f980a042f81e3cc5b6dae418e22b1d291e Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 31 Aug 2016 17:27:07 +0200 Subject: [PATCH 1457/3817] test: add test case for PutMany on bloom filter skipping add to bloom License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@aa26a821c3308073396ac95963da07998ed7a0cd --- blockstore/bloom_cache_test.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 607bf8d24..d9d23341a 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -28,6 +28,39 @@ func testBloomCached(bs GCBlockstore, ctx context.Context) (*bloomcache, error) } } +func TestPutManyAddsToBloom(t *testing.T) { + bs := NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore())) + + ctx, _ := context.WithTimeout(context.Background(), 1*time.Second) + cachedbs, err := testBloomCached(bs, ctx) + + select { + case <-cachedbs.rebuildChan: + case <-ctx.Done(): + t.Fatalf("Timeout wating for rebuild: %d", cachedbs.bloom.ElementsAdded()) + } + + block1 := blocks.NewBlock([]byte("foo")) + block2 := blocks.NewBlock([]byte("bar")) + + cachedbs.PutMany([]blocks.Block{block1}) + has, err := cachedbs.Has(block1.Key()) + if err != nil { + t.Fatal(err) + } + if has == false { + t.Fatal("added block is reported missing") + } + + has, err = cachedbs.Has(block2.Key()) + if err != nil { + t.Fatal(err) + } + if has == true { + t.Fatal("not added block is reported to be in blockstore") + } +} + func TestReturnsErrorWhenSizeNegative(t *testing.T) { bs := NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore())) _, err := bloomCached(bs, context.TODO(), -1, 1) From ef4a7d3c72e5c80370d6ae4552cab337817bf001 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 6 Sep 2016 19:25:34 +0200 Subject: [PATCH 1458/3817] blockstore: change unit of bloom filter to byte from bits License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@b9f5b4065c0e2f922008a499e92adf26d8334022 --- blockstore/caching.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/blockstore/caching.go b/blockstore/caching.go index f691f89f8..bc78134e0 100644 --- a/blockstore/caching.go +++ b/blockstore/caching.go @@ -8,16 +8,16 @@ import ( // Next to each option is it aproximate memory usage per unit type CacheOpts struct { - HasBloomFilterSize int // 1 bit + HasBloomFilterSize int // 1 byte HasBloomFilterHashes int // No size, 7 is usually best, consult bloom papers HasARCCacheSize int // 32 bytes } func DefaultCacheOpts() CacheOpts { return CacheOpts{ - HasBloomFilterSize: 512 * 8 * 1024, + HasBloomFilterSize: 512 << 10, HasBloomFilterHashes: 7, - HasARCCacheSize: 64 * 1024, + HasARCCacheSize: 64 << 10, } } @@ -34,7 +34,8 @@ func CachedBlockstore(bs GCBlockstore, return nil, errors.New("bloom filter hash count can't be 0 when there is size set") } if opts.HasBloomFilterSize != 0 { - cbs, err = bloomCached(cbs, ctx, opts.HasBloomFilterSize, opts.HasBloomFilterHashes) + // *8 because of bytes to bits conversion + cbs, err = bloomCached(cbs, ctx, opts.HasBloomFilterSize*8, opts.HasBloomFilterHashes) } if opts.HasARCCacheSize > 0 { cbs, err = arcCached(cbs, opts.HasARCCacheSize) From 63c79204462dacbc444d313fd97732a4ab6d91a5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Sep 2016 07:50:27 -0700 Subject: [PATCH 1459/3817] integrate CIDv0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@1268b9cc2ac1148e26c6d797bc07906b58d63ae9 --- ipld/merkledag/coding.go | 7 +- ipld/merkledag/merkledag.go | 195 ++++++++++++++-------------- ipld/merkledag/merkledag_test.go | 84 +++++------- ipld/merkledag/node.go | 56 +++++--- ipld/merkledag/node_test.go | 8 +- ipld/merkledag/traverse/traverse.go | 6 +- ipld/merkledag/utils/diff.go | 31 ++--- ipld/merkledag/utils/utils_test.go | 29 ++--- 8 files changed, 194 insertions(+), 222 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 2c92b559f..136735615 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -4,10 +4,11 @@ import ( "fmt" "sort" - mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" - pb "github.com/ipfs/go-ipfs/merkledag/pb" + + mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) // for now, we use a PBNode intermediate thing. @@ -83,7 +84,7 @@ func (n *Node) EncodeProtobuf(force bool) ([]byte, error) { } if n.cached == nil { - n.cached = u.Hash(n.encoded) + n.cached = cid.NewCidV0(u.Hash(n.encoded)) } return n.encoded, nil diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 7d601d86d..f872d70ae 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -6,11 +6,12 @@ import ( "strings" "sync" - blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) var log = logging.Logger("merkledag") @@ -18,13 +19,13 @@ var ErrNotFound = fmt.Errorf("merkledag: not found") // DAGService is an IPFS Merkle DAG service. type DAGService interface { - Add(*Node) (key.Key, error) - Get(context.Context, key.Key) (*Node, error) + Add(*Node) (*cid.Cid, error) + Get(context.Context, *cid.Cid) (*Node, error) Remove(*Node) error // GetDAG returns, in order, all the single leve child // nodes of the passed in node. - GetMany(context.Context, []key.Key) <-chan *NodeOption + GetMany(context.Context, []*cid.Cid) <-chan *NodeOption Batch() *Batch } @@ -43,24 +44,12 @@ type dagService struct { } // Add adds a node to the dagService, storing the block in the BlockService -func (n *dagService) Add(nd *Node) (key.Key, error) { +func (n *dagService) Add(nd *Node) (*cid.Cid, error) { if n == nil { // FIXME remove this assertion. protect with constructor invariant - return "", fmt.Errorf("dagService is nil") - } - - d, err := nd.EncodeProtobuf(false) - if err != nil { - return "", err - } - - mh, err := nd.Multihash() - if err != nil { - return "", err + return nil, fmt.Errorf("dagService is nil") } - b, _ := blocks.NewBlockWithHash(d, mh) - - return n.Blocks.AddBlock(b) + return n.Blocks.AddObject(nd) } func (n *dagService) Batch() *Batch { @@ -68,56 +57,57 @@ func (n *dagService) Batch() *Batch { } // Get retrieves a node from the dagService, fetching the block in the BlockService -func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) { - if k == "" { - return nil, ErrNotFound - } +func (n *dagService) Get(ctx context.Context, c *cid.Cid) (*Node, error) { if n == nil { return nil, fmt.Errorf("dagService is nil") } + ctx, cancel := context.WithCancel(ctx) defer cancel() - b, err := n.Blocks.GetBlock(ctx, k) + b, err := n.Blocks.GetBlock(ctx, c) if err != nil { if err == bserv.ErrNotFound { return nil, ErrNotFound } - return nil, fmt.Errorf("Failed to get block for %s: %v", k.B58String(), err) + return nil, fmt.Errorf("Failed to get block for %s: %v", c, err) } - res, err := DecodeProtobuf(b.Data()) - if err != nil { - if strings.Contains(err.Error(), "Unmarshal failed") { - return nil, fmt.Errorf("The block referred to by '%s' was not a valid merkledag node", k) + var res *Node + switch c.Type() { + case cid.Protobuf: + out, err := DecodeProtobuf(b.RawData()) + if err != nil { + if strings.Contains(err.Error(), "Unmarshal failed") { + return nil, fmt.Errorf("The block referred to by '%s' was not a valid merkledag node", c) + } + return nil, fmt.Errorf("Failed to decode Protocol Buffers: %v", err) } - return nil, fmt.Errorf("Failed to decode Protocol Buffers: %v", err) + res = out + default: + return nil, fmt.Errorf("unrecognized formatting type") } - res.cached = k.ToMultihash() + res.cached = c return res, nil } func (n *dagService) Remove(nd *Node) error { - k, err := nd.Key() - if err != nil { - return err - } - return n.Blocks.DeleteBlock(k) + return n.Blocks.DeleteObject(nd) } // FetchGraph fetches all nodes that are children of the given node func FetchGraph(ctx context.Context, root *Node, serv DAGService) error { - return EnumerateChildrenAsync(ctx, serv, root, key.NewKeySet()) + return EnumerateChildrenAsync(ctx, serv, root, cid.NewSet().Visit) } // FindLinks searches this nodes links for the given key, // returns the indexes of any links pointing to it -func FindLinks(links []key.Key, k key.Key, start int) []int { +func FindLinks(links []*cid.Cid, c *cid.Cid, start int) []int { var out []int - for i, lnk_k := range links[start:] { - if k == lnk_k { + for i, lnk_c := range links[start:] { + if c.Equals(lnk_c) { out = append(out, i+start) } } @@ -129,11 +119,21 @@ type NodeOption struct { Err error } -func (ds *dagService) GetMany(ctx context.Context, keys []key.Key) <-chan *NodeOption { +func cidsToKeyMapping(cids []*cid.Cid) map[key.Key]*cid.Cid { + mapping := make(map[key.Key]*cid.Cid) + for _, c := range cids { + mapping[key.Key(c.Hash())] = c + } + return mapping +} + +func (ds *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *NodeOption { out := make(chan *NodeOption, len(keys)) blocks := ds.Blocks.GetBlocks(ctx, keys) var count int + mapping := cidsToKeyMapping(keys) + go func() { defer close(out) for { @@ -145,12 +145,23 @@ func (ds *dagService) GetMany(ctx context.Context, keys []key.Key) <-chan *NodeO } return } - nd, err := DecodeProtobuf(b.Data()) - if err != nil { - out <- &NodeOption{Err: err} + + c := mapping[b.Key()] + + var nd *Node + switch c.Type() { + case cid.Protobuf: + decnd, err := DecodeProtobuf(b.RawData()) + if err != nil { + out <- &NodeOption{Err: err} + return + } + decnd.cached = cid.NewCidV0(b.Multihash()) + nd = decnd + default: + out <- &NodeOption{Err: fmt.Errorf("unrecognized object type: %s", c.Type())} return } - nd.cached = b.Key().ToMultihash() // buffered, no need to select out <- &NodeOption{Node: nd} @@ -169,17 +180,17 @@ func (ds *dagService) GetMany(ctx context.Context, keys []key.Key) <-chan *NodeO // It returns a channel of nodes, which the caller can receive // all the child nodes of 'root' on, in proper order. func GetDAG(ctx context.Context, ds DAGService, root *Node) []NodeGetter { - var keys []key.Key + var cids []*cid.Cid for _, lnk := range root.Links { - keys = append(keys, key.Key(lnk.Hash)) + cids = append(cids, cid.NewCidV0(lnk.Hash)) } - return GetNodes(ctx, ds, keys) + return GetNodes(ctx, ds, cids) } // GetNodes returns an array of 'NodeGetter' promises, with each corresponding // to the key with the same index as the passed in keys -func GetNodes(ctx context.Context, ds DAGService, keys []key.Key) []NodeGetter { +func GetNodes(ctx context.Context, ds DAGService, keys []*cid.Cid) []NodeGetter { // Early out if no work to do if len(keys) == 0 { @@ -216,14 +227,7 @@ func GetNodes(ctx context.Context, ds DAGService, keys []key.Key) []NodeGetter { } nd := opt.Node - - k, err := nd.Key() - if err != nil { - log.Error("Failed to get node key: ", err) - continue - } - - is := FindLinks(keys, k, 0) + is := FindLinks(keys, nd.Cid(), 0) for _, i := range is { count++ promises[i].Send(nd) @@ -237,16 +241,12 @@ func GetNodes(ctx context.Context, ds DAGService, keys []key.Key) []NodeGetter { } // Remove duplicates from a list of keys -func dedupeKeys(ks []key.Key) []key.Key { - kmap := make(map[key.Key]struct{}) - var out []key.Key - for _, k := range ks { - if _, ok := kmap[k]; !ok { - kmap[k] = struct{}{} - out = append(out, k) - } +func dedupeKeys(cids []*cid.Cid) []*cid.Cid { + set := cid.NewSet() + for _, c := range cids { + set.Add(c) } - return out + return set.Keys() } func newNodePromise(ctx context.Context) NodeGetter { @@ -327,50 +327,44 @@ func (np *nodePromise) Get(ctx context.Context) (*Node, error) { type Batch struct { ds *dagService - blocks []blocks.Block + objects []bserv.Object size int MaxSize int } -func (t *Batch) Add(nd *Node) (key.Key, error) { +func (t *Batch) Add(nd *Node) (*cid.Cid, error) { d, err := nd.EncodeProtobuf(false) if err != nil { - return "", err - } - - mh, err := nd.Multihash() - if err != nil { - return "", err + return nil, err } - b, _ := blocks.NewBlockWithHash(d, mh) - - k := key.Key(mh) - - t.blocks = append(t.blocks, b) - t.size += len(b.Data()) + t.objects = append(t.objects, nd) + t.size += len(d) if t.size > t.MaxSize { - return k, t.Commit() + return nd.Cid(), t.Commit() } - return k, nil + return nd.Cid(), nil } func (t *Batch) Commit() error { - _, err := t.ds.Blocks.AddBlocks(t.blocks) - t.blocks = nil + _, err := t.ds.Blocks.AddObjects(t.objects) + t.objects = nil t.size = 0 return err } +func legacyCidFromLink(lnk *Link) *cid.Cid { + return cid.NewCidV0(lnk.Hash) +} + // EnumerateChildren will walk the dag below the given root node and add all // unseen children to the passed in set. // TODO: parallelize to avoid disk latency perf hits? -func EnumerateChildren(ctx context.Context, ds DAGService, root *Node, set key.KeySet, bestEffort bool) error { +func EnumerateChildren(ctx context.Context, ds DAGService, root *Node, visit func(*cid.Cid) bool, bestEffort bool) error { for _, lnk := range root.Links { - k := key.Key(lnk.Hash) - if !set.Has(k) { - set.Add(k) - child, err := ds.Get(ctx, k) + c := legacyCidFromLink(lnk) + if visit(c) { + child, err := ds.Get(ctx, c) if err != nil { if bestEffort && err == ErrNotFound { continue @@ -378,7 +372,7 @@ func EnumerateChildren(ctx context.Context, ds DAGService, root *Node, set key.K return err } } - err = EnumerateChildren(ctx, ds, child, set, bestEffort) + err = EnumerateChildren(ctx, ds, child, visit, bestEffort) if err != nil { return err } @@ -387,8 +381,8 @@ func EnumerateChildren(ctx context.Context, ds DAGService, root *Node, set key.K return nil } -func EnumerateChildrenAsync(ctx context.Context, ds DAGService, root *Node, set key.KeySet) error { - toprocess := make(chan []key.Key, 8) +func EnumerateChildrenAsync(ctx context.Context, ds DAGService, root *Node, visit func(*cid.Cid) bool) error { + toprocess := make(chan []*cid.Cid, 8) nodes := make(chan *NodeOption, 8) ctx, cancel := context.WithCancel(ctx) @@ -416,13 +410,12 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, root *Node, set // a node has been fetched live-- - var keys []key.Key + var cids []*cid.Cid for _, lnk := range nd.Links { - k := key.Key(lnk.Hash) - if !set.Has(k) { - set.Add(k) + c := legacyCidFromLink(lnk) + if visit(c) { live++ - keys = append(keys, k) + cids = append(cids, c) } } @@ -430,9 +423,9 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, root *Node, set return nil } - if len(keys) > 0 { + if len(cids) > 0 { select { - case toprocess <- keys: + case toprocess <- cids: case <-ctx.Done(): return ctx.Err() } @@ -443,7 +436,7 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, root *Node, set } } -func fetchNodes(ctx context.Context, ds DAGService, in <-chan []key.Key, out chan<- *NodeOption) { +func fetchNodes(ctx context.Context, ds DAGService, in <-chan []*cid.Cid, out chan<- *NodeOption) { var wg sync.WaitGroup defer func() { // wait for all 'get' calls to complete so we don't accidentally send @@ -452,7 +445,7 @@ func fetchNodes(ctx context.Context, ds DAGService, in <-chan []key.Key, out cha close(out) }() - get := func(ks []key.Key) { + get := func(ks []*cid.Cid) { defer wg.Done() nodes := ds.GetMany(ctx, ks) for opt := range nodes { diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 38545ac12..7f71f7c2e 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -20,8 +20,10 @@ import ( mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) func TestNode(t *testing.T) { @@ -52,17 +54,9 @@ func TestNode(t *testing.T) { fmt.Println("encoded:", e) } - h, err := n.Multihash() - if err != nil { - t.Error(err) - } else { - fmt.Println("hash:", h) - } - - k, err := n.Key() - if err != nil { - t.Error(err) - } else if k != key.Key(h) { + h := n.Multihash() + k := n.Key() + if k != key.Key(h) { t.Error("Key is not equivalent to multihash") } else { fmt.Println("key: ", k) @@ -89,11 +83,7 @@ func SubtestNodeStat(t *testing.T, n *Node) { return } - k, err := n.Key() - if err != nil { - t.Error("n.Key() failed") - return - } + k := n.Key() expected := NodeStat{ NumLinks: len(n.Links), @@ -169,10 +159,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { t.Log("Added file to first node.") - k, err := root.Key() - if err != nil { - t.Fatal(err) - } + c := root.Cid() wg := sync.WaitGroup{} errs := make(chan error) @@ -181,7 +168,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { wg.Add(1) go func(i int) { defer wg.Done() - first, err := dagservs[i].Get(ctx, k) + first, err := dagservs[i].Get(ctx, c) if err != nil { errs <- err } @@ -215,34 +202,17 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { } func assertCanGet(t *testing.T, ds DAGService, n *Node) { - k, err := n.Key() - if err != nil { - t.Fatal(err) - } - - if _, err := ds.Get(context.Background(), k); err != nil { + if _, err := ds.Get(context.Background(), n.Cid()); err != nil { t.Fatal(err) } } -func TestEmptyKey(t *testing.T) { - ds := dstest.Mock() - _, err := ds.Get(context.Background(), key.Key("")) - if err != ErrNotFound { - t.Error("dag service should error when key is nil", err) - } -} - func TestCantGet(t *testing.T) { ds := dstest.Mock() a := NodeWithData([]byte("A")) - k, err := a.Key() - if err != nil { - t.Fatal(err) - } - - _, err = ds.Get(context.Background(), k) + c := a.Cid() + _, err := ds.Get(context.Background(), c) if !strings.Contains(err.Error(), "not found") { t.Fatal("expected err not found, got: ", err) } @@ -270,9 +240,8 @@ func TestFetchGraph(t *testing.T) { bs := bserv.New(bsis[1].Blockstore, offline.Exchange(bsis[1].Blockstore)) offline_ds := NewDAGService(bs) - ks := key.NewKeySet() - err = EnumerateChildren(context.Background(), offline_ds, root, ks, false) + err = EnumerateChildren(context.Background(), offline_ds, root, func(_ *cid.Cid) bool { return true }, false) if err != nil { t.Fatal(err) } @@ -288,8 +257,8 @@ func TestEnumerateChildren(t *testing.T) { t.Fatal(err) } - ks := key.NewKeySet() - err = EnumerateChildren(context.Background(), ds, root, ks, false) + set := cid.NewSet() + err = EnumerateChildren(context.Background(), ds, root, set.Visit, false) if err != nil { t.Fatal(err) } @@ -298,11 +267,11 @@ func TestEnumerateChildren(t *testing.T) { traverse = func(n *Node) { // traverse dag and check for _, lnk := range n.Links { - k := key.Key(lnk.Hash) - if !ks.Has(k) { + c := cid.NewCidV0(lnk.Hash) + if !set.Has(c) { t.Fatal("missing key in set!") } - child, err := ds.Get(context.Background(), k) + child, err := ds.Get(context.Background(), c) if err != nil { t.Fatal(err) } @@ -379,3 +348,22 @@ func TestUnmarshalFailure(t *testing.T) { n := &Node{} n.Marshal() } + +func TestBasicAddGet(t *testing.T) { + ds := dstest.Mock() + nd := new(Node) + + c, err := ds.Add(nd) + if err != nil { + t.Fatal(err) + } + + out, err := ds.Get(context.Background(), c) + if err != nil { + t.Fatal(err) + } + + if !nd.Cid().Equals(out.Cid()) { + t.Fatal("output didnt match input") + } +} diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 7be5c4d0a..b3add5f37 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -7,6 +7,7 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) var ErrLinkNotFound = fmt.Errorf("no link by that name") @@ -20,7 +21,7 @@ type Node struct { // cache encoded/marshaled value encoded []byte - cached mh.Multihash + cached *cid.Cid } // NodeStat is a statistics object for a Node. Mostly sizes. @@ -63,10 +64,8 @@ func MakeLink(n *Node) (*Link, error) { return nil, err } - h, err := n.Multihash() - if err != nil { - return nil, err - } + h := n.Multihash() + return &Link{ Size: s, Hash: h, @@ -75,7 +74,7 @@ func MakeLink(n *Node) (*Link, error) { // GetNode returns the MDAG Node that this link points to func (l *Link) GetNode(ctx context.Context, serv DAGService) (*Node, error) { - return serv.Get(ctx, key.Key(l.Hash)) + return serv.Get(ctx, legacyCidFromLink(l)) } func NodeWithData(d []byte) *Node { @@ -184,6 +183,11 @@ func (n *Node) Copy() *Node { return nnode } +func (n *Node) RawData() []byte { + out, _ := n.EncodeProtobuf(false) + return out +} + func (n *Node) Data() []byte { return n.data } @@ -231,13 +235,8 @@ func (n *Node) Stat() (*NodeStat, error) { return nil, err } - key, err := n.Key() - if err != nil { - return nil, err - } - return &NodeStat{ - Hash: key.B58String(), + Hash: n.Key().B58String(), NumLinks: len(n.Links), BlockSize: len(enc), LinksSize: len(enc) - len(n.data), // includes framing. @@ -246,19 +245,34 @@ func (n *Node) Stat() (*NodeStat, error) { }, nil } +func (n *Node) Key() key.Key { + return key.Key(n.Multihash()) +} + +func (n *Node) Loggable() map[string]interface{} { + return map[string]interface{}{ + "node": n.String(), + } +} + +func (n *Node) Cid() *cid.Cid { + h := n.Multihash() + + return cid.NewCidV0(h) +} + +func (n *Node) String() string { + return n.Cid().String() +} + // Multihash hashes the encoded data of this node. -func (n *Node) Multihash() (mh.Multihash, error) { +func (n *Node) Multihash() mh.Multihash { // NOTE: EncodeProtobuf generates the hash and puts it in n.cached. _, err := n.EncodeProtobuf(false) if err != nil { - return nil, err + // Note: no possibility exists for an error to be returned through here + panic(err) } - return n.cached, nil -} - -// Key returns the Multihash as a key, for maps. -func (n *Node) Key() (key.Key, error) { - h, err := n.Multihash() - return key.Key(h), err + return n.cached.Hash() } diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index d248ad359..a35013dca 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -67,9 +67,9 @@ func TestFindLink(t *testing.T) { nd := &Node{ Links: []*Link{ - &Link{Name: "a", Hash: k.ToMultihash()}, - &Link{Name: "c", Hash: k.ToMultihash()}, - &Link{Name: "b", Hash: k.ToMultihash()}, + &Link{Name: "a", Hash: k.Hash()}, + &Link{Name: "c", Hash: k.Hash()}, + &Link{Name: "b", Hash: k.Hash()}, }, } @@ -107,7 +107,7 @@ func TestFindLink(t *testing.T) { t.Fatal(err) } - if olnk.Hash.B58String() == k.B58String() { + if olnk.Hash.B58String() == k.String() { t.Fatal("new link should have different hash") } } diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index d07354617..a3bb06001 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -41,11 +41,7 @@ type traversal struct { func (t *traversal) shouldSkip(n *mdag.Node) (bool, error) { if t.opts.SkipDuplicates { - k, err := n.Key() - if err != nil { - return true, err - } - + k := n.Key() if _, found := t.seen[string(k)]; found { return true, nil } diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 493394437..406000596 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -5,9 +5,10 @@ import ( "fmt" "path" - key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) const ( @@ -19,18 +20,18 @@ const ( type Change struct { Type int Path string - Before key.Key - After key.Key + Before *cid.Cid + After *cid.Cid } func (c *Change) String() string { switch c.Type { case Add: - return fmt.Sprintf("Added %s at %s", c.After.B58String()[:6], c.Path) + return fmt.Sprintf("Added %s at %s", c.After.String(), c.Path) case Remove: - return fmt.Sprintf("Removed %s from %s", c.Before.B58String()[:6], c.Path) + return fmt.Sprintf("Removed %s from %s", c.Before.String(), c.Path) case Mod: - return fmt.Sprintf("Changed %s to %s at %s", c.Before.B58String()[:6], c.After.B58String()[:6], c.Path) + return fmt.Sprintf("Changed %s to %s at %s", c.Before.String(), c.After.String(), c.Path) default: panic("nope") } @@ -77,21 +78,11 @@ func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.Node, cs []*Cha func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) ([]*Change, error) { if len(a.Links) == 0 && len(b.Links) == 0 { - ak, err := a.Key() - if err != nil { - return nil, err - } - - bk, err := b.Key() - if err != nil { - return nil, err - } - return []*Change{ &Change{ Type: Mod, - Before: ak, - After: bk, + Before: a.Cid(), + After: b.Cid(), }, }, nil } @@ -136,14 +127,14 @@ func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) ([]*Change, er out = append(out, &Change{ Type: Remove, Path: lnk.Name, - Before: key.Key(lnk.Hash), + Before: cid.NewCidV0(lnk.Hash), }) } for _, lnk := range clean_b.Links { out = append(out, &Change{ Type: Add, Path: lnk.Name, - After: key.Key(lnk.Hash), + After: cid.NewCidV0(lnk.Hash), }) } diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 1ec444b0b..0585f8684 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -3,12 +3,12 @@ package dagutils import ( "testing" - key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) func TestAddLink(t *testing.T) { @@ -31,17 +31,13 @@ func TestAddLink(t *testing.T) { t.Fatal(err) } - fnpkey, err := fnprime.Key() - if err != nil { - t.Fatal(err) - } - - if fnpkey != fk { + fnpkey := fnprime.Cid() + if !fnpkey.Equals(fk) { t.Fatal("wrong child node found!") } } -func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, pth string, exp key.Key) { +func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, pth string, exp *cid.Cid) { parts := path.SplitList(pth) cur := root for _, e := range parts { @@ -53,12 +49,8 @@ func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, pth strin cur = nxt } - curk, err := cur.Key() - if err != nil { - t.Fatal(err) - } - - if curk != exp { + curc := cur.Cid() + if !curc.Equals(exp) { t.Fatal("node not as expected at end of path") } } @@ -77,13 +69,10 @@ func TestInsertNode(t *testing.T) { testInsert(t, e, "", "bar", true, "cannot create link with no name!") testInsert(t, e, "////", "slashes", true, "cannot create link with no name!") - k, err := e.GetNode().Key() - if err != nil { - t.Fatal(err) - } + c := e.GetNode().Cid() - if k.B58String() != "QmZ8yeT9uD6ouJPNAYt62XffYuXBT6b4mP4obRSE9cJrSt" { - t.Fatal("output was different than expected: ", k) + if c.String() != "QmZ8yeT9uD6ouJPNAYt62XffYuXBT6b4mP4obRSE9cJrSt" { + t.Fatal("output was different than expected: ", c) } } From ba89bf9b1f14e11165eaff18fb675af75adc5320 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Sep 2016 07:50:27 -0700 Subject: [PATCH 1460/3817] integrate CIDv0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@1aafbacd50fba458cb671bf5a17c914279d47a7d --- blockservice/blockservice.go | 87 +++++++++++++++++++++----------- blockservice/test/blocks_test.go | 64 ++++++++++++++--------- 2 files changed, 99 insertions(+), 52 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 25282a441..f98c0f96f 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -5,13 +5,16 @@ package blockservice import ( "errors" + "fmt" blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) var log = logging.Logger("blockservice") @@ -27,6 +30,12 @@ type BlockService struct { Exchange exchange.Interface } +// an Object is simply a typed block +type Object interface { + Cid() *cid.Cid + blocks.Block +} + // NewBlockService creates a BlockService with given datastore instance. func New(bs blockstore.Blockstore, rem exchange.Interface) *BlockService { if rem == nil { @@ -41,30 +50,41 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) *BlockService { // AddBlock adds a particular block to the service, Putting it into the datastore. // TODO pass a context into this if the remote.HasBlock is going to remain here. -func (s *BlockService) AddBlock(b blocks.Block) (key.Key, error) { - k := b.Key() - has, err := s.Blockstore.Has(k) +func (s *BlockService) AddObject(o Object) (*cid.Cid, error) { + // TODO: while this is a great optimization, we should think about the + // possibility of streaming writes directly to disk. If we can pass this object + // all the way down to the datastore without having to 'buffer' its data, + // we could implement a `WriteTo` method on it that could do a streaming write + // of the content, saving us (probably) considerable memory. + c := o.Cid() + has, err := s.Blockstore.Has(key.Key(c.Hash())) if err != nil { - return k, err + return nil, err } + if has { - return k, nil + return c, nil } - err = s.Blockstore.Put(b) + err = s.Blockstore.Put(o) if err != nil { - return k, err + return nil, err } - if err := s.Exchange.HasBlock(b); err != nil { - return "", errors.New("blockservice is closed") + + if err := s.Exchange.HasBlock(o); err != nil { + return nil, errors.New("blockservice is closed") } - return k, nil + + return c, nil } -func (s *BlockService) AddBlocks(bs []blocks.Block) ([]key.Key, error) { +func (s *BlockService) AddObjects(bs []Object) ([]*cid.Cid, error) { var toput []blocks.Block + var toputcids []*cid.Cid for _, b := range bs { - has, err := s.Blockstore.Has(b.Key()) + c := b.Cid() + + has, err := s.Blockstore.Has(key.Key(c.Hash())) if err != nil { return nil, err } @@ -74,6 +94,7 @@ func (s *BlockService) AddBlocks(bs []blocks.Block) ([]key.Key, error) { } toput = append(toput, b) + toputcids = append(toputcids, c) } err := s.Blockstore.PutMany(toput) @@ -81,26 +102,25 @@ func (s *BlockService) AddBlocks(bs []blocks.Block) ([]key.Key, error) { return nil, err } - var ks []key.Key - for _, b := range toput { - if err := s.Exchange.HasBlock(b); err != nil { - return nil, errors.New("blockservice is closed") + var ks []*cid.Cid + for _, o := range toput { + if err := s.Exchange.HasBlock(o); err != nil { + return nil, fmt.Errorf("blockservice is closed (%s)", err) } - ks = append(ks, b.Key()) + + c := o.(Object).Cid() // cast is safe, we created these + ks = append(ks, c) } return ks, nil } // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). -func (s *BlockService) GetBlock(ctx context.Context, k key.Key) (blocks.Block, error) { - if k == "" { - log.Debug("BlockService GetBlock: Nil Key") - return nil, ErrNotFound - } +func (s *BlockService) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) { + log.Debugf("BlockService GetBlock: '%s'", c) - log.Debugf("BlockService GetBlock: '%s'", k) - block, err := s.Blockstore.Get(k) + // TODO: blockstore shouldnt care about Cids, need an easier way to strip the abstraction + block, err := s.Blockstore.Get(key.Key(c.Hash())) if err == nil { return block, nil } @@ -109,7 +129,7 @@ func (s *BlockService) GetBlock(ctx context.Context, k key.Key) (blocks.Block, e // TODO be careful checking ErrNotFound. If the underlying // implementation changes, this will break. log.Debug("Blockservice: Searching bitswap") - blk, err := s.Exchange.GetBlock(ctx, k) + blk, err := s.Exchange.GetBlock(ctx, key.Key(c.Hash())) if err != nil { if err == blockstore.ErrNotFound { return nil, ErrNotFound @@ -130,12 +150,13 @@ func (s *BlockService) GetBlock(ctx context.Context, k key.Key) (blocks.Block, e // GetBlocks gets a list of blocks asynchronously and returns through // the returned channel. // NB: No guarantees are made about order. -func (s *BlockService) GetBlocks(ctx context.Context, ks []key.Key) <-chan blocks.Block { +func (s *BlockService) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block { out := make(chan blocks.Block, 0) go func() { defer close(out) var misses []key.Key - for _, k := range ks { + for _, c := range ks { + k := key.Key(c.Hash()) hit, err := s.Blockstore.Get(k) if err != nil { misses = append(misses, k) @@ -171,11 +192,19 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []key.Key) <-chan block } // DeleteBlock deletes a block in the blockservice from the datastore -func (s *BlockService) DeleteBlock(k key.Key) error { - return s.Blockstore.DeleteBlock(k) +func (s *BlockService) DeleteObject(o Object) error { + return s.Blockstore.DeleteBlock(o.Key()) } func (s *BlockService) Close() error { log.Debug("blockservice is shutting down...") return s.Exchange.Close() } + +type RawBlockObject struct { + blocks.Block +} + +func (rob *RawBlockObject) Cid() *cid.Cid { + return cid.NewCidV0(rob.Block.Multihash()) +} diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 81d61818b..a64264dab 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -2,80 +2,98 @@ package bstest import ( "bytes" + "fmt" "testing" "time" blocks "github.com/ipfs/go-ipfs/blocks" blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" - blocksutil "github.com/ipfs/go-ipfs/blocks/blocksutil" key "github.com/ipfs/go-ipfs/blocks/key" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) +func newObject(data []byte) *testObject { + return &testObject{ + Block: blocks.NewBlock(data), + } +} + +type testObject struct { + blocks.Block +} + +func (o *testObject) Cid() *cid.Cid { + return cid.NewCidV0(o.Block.Multihash()) +} + func TestBlocks(t *testing.T) { bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) bs := New(bstore, offline.Exchange(bstore)) defer bs.Close() - _, err := bs.GetBlock(context.Background(), key.Key("")) - if err != ErrNotFound { - t.Error("Empty String Key should error", err) - } - - b := blocks.NewBlock([]byte("beep boop")) + o := newObject([]byte("beep boop")) h := u.Hash([]byte("beep boop")) - if !bytes.Equal(b.Multihash(), h) { + if !bytes.Equal(o.Multihash(), h) { t.Error("Block Multihash and data multihash not equal") } - if b.Key() != key.Key(h) { + if o.Key() != key.Key(h) { t.Error("Block key and data multihash key not equal") } - k, err := bs.AddBlock(b) + k, err := bs.AddObject(o) if err != nil { t.Error("failed to add block to BlockService", err) return } - if k != b.Key() { + if !k.Equals(o.Cid()) { t.Error("returned key is not equal to block key", err) } ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() - b2, err := bs.GetBlock(ctx, b.Key()) + b2, err := bs.GetBlock(ctx, o.Cid()) if err != nil { t.Error("failed to retrieve block from BlockService", err) return } - if b.Key() != b2.Key() { + if o.Key() != b2.Key() { t.Error("Block keys not equal.") } - if !bytes.Equal(b.Data(), b2.Data()) { + if !bytes.Equal(o.RawData(), b2.RawData()) { t.Error("Block data is not equal.") } } +func makeObjects(n int) []*testObject { + var out []*testObject + for i := 0; i < n; i++ { + out = append(out, newObject([]byte(fmt.Sprintf("object %d", i)))) + } + return out +} + func TestGetBlocksSequential(t *testing.T) { var servs = Mocks(4) for _, s := range servs { defer s.Close() } - bg := blocksutil.NewBlockGenerator() - blks := bg.Blocks(50) + objs := makeObjects(50) - var keys []key.Key - for _, blk := range blks { - keys = append(keys, blk.Key()) - servs[0].AddBlock(blk) + var cids []*cid.Cid + for _, o := range objs { + cids = append(cids, o.Cid()) + servs[0].AddObject(o) } t.Log("one instance at a time, get blocks concurrently") @@ -83,7 +101,7 @@ func TestGetBlocksSequential(t *testing.T) { for i := 1; i < len(servs); i++ { ctx, cancel := context.WithTimeout(context.Background(), time.Second*50) defer cancel() - out := servs[i].GetBlocks(ctx, keys) + out := servs[i].GetBlocks(ctx, cids) gotten := make(map[key.Key]blocks.Block) for blk := range out { if _, ok := gotten[blk.Key()]; ok { @@ -91,8 +109,8 @@ func TestGetBlocksSequential(t *testing.T) { } gotten[blk.Key()] = blk } - if len(gotten) != len(blks) { - t.Fatalf("Didnt get enough blocks back: %d/%d", len(gotten), len(blks)) + if len(gotten) != len(objs) { + t.Fatalf("Didnt get enough blocks back: %d/%d", len(gotten), len(objs)) } } } From b1f168d03a0d8186895ff8f1df55bbe228e2b5bd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Sep 2016 07:50:27 -0700 Subject: [PATCH 1461/3817] integrate CIDv0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@8f5dba62074a56af025dd1afb92ba07b687ca867 --- blockstore/blockstore.go | 4 ++-- blockstore/blockstore_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index f96178b44..dc0f36134 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -117,7 +117,7 @@ func (bs *blockstore) Put(block blocks.Block) error { if err == nil && exists { return nil // already stored. } - return bs.datastore.Put(k, block.Data()) + return bs.datastore.Put(k, block.RawData()) } func (bs *blockstore) PutMany(blocks []blocks.Block) error { @@ -132,7 +132,7 @@ func (bs *blockstore) PutMany(blocks []blocks.Block) error { continue } - err = t.Put(k, b.Data()) + err = t.Put(k, b.RawData()) if err != nil { return err } diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index fc78ca6e9..e4b6931ae 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -48,7 +48,7 @@ func TestPutThenGetBlock(t *testing.T) { if err != nil { t.Fatal(err) } - if !bytes.Equal(block.Data(), blockFromBlockstore.Data()) { + if !bytes.Equal(block.RawData(), blockFromBlockstore.RawData()) { t.Fail() } } From 17e87be7ee9eda7c1ce60dc9ba9641e8df6b0665 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Sep 2016 07:50:27 -0700 Subject: [PATCH 1462/3817] integrate CIDv0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@54ea34e71464f4b5ce1638081d3bb89650deb430 --- namesys/dns.go | 2 +- namesys/publisher.go | 2 +- namesys/routing.go | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index d825ea00e..79fb00c2f 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -114,7 +114,7 @@ func workDomain(r *DNSResolver, name string, res chan lookupRes) { } func parseEntry(txt string) (path.Path, error) { - p, err := path.ParseKeyToPath(txt) // bare IPFS multihashes + p, err := path.ParseCidToPath(txt) // bare IPFS multihashes if err == nil { return p, nil } diff --git a/namesys/publisher.go b/namesys/publisher.go index 77604ae95..61fa8d6d0 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -348,7 +348,7 @@ func InitializeKeyspace(ctx context.Context, ds dag.DAGService, pub Publisher, p return err } - err = pub.Publish(ctx, key, path.FromKey(nodek)) + err = pub.Publish(ctx, key, path.FromCid(nodek)) if err != nil { return err } diff --git a/namesys/routing.go b/namesys/routing.go index d2c4fda11..b4eea2af9 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -14,9 +14,11 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) var log = logging.Logger("namesys") @@ -196,7 +198,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa } else { // Its an old style multihash record log.Warning("Detected old style multihash record") - p := path.FromKey(key.Key(valh)) + p := path.FromCid(cid.NewCidV0(valh)) r.cacheSet(name, p, entry) return p, nil } From 067376e4317a48443d45844a790a514207ebe7fe Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Sep 2016 07:50:27 -0700 Subject: [PATCH 1463/3817] integrate CIDv0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@b8d46812f8d2b0f3050d8cdfcdb301591b3163b3 --- unixfs/io/dirbuilder.go | 6 +++--- unixfs/mod/dagmodifier.go | 27 +++++++++++++-------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 3db0b9ef9..7a7783a7d 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -3,9 +3,9 @@ package io import ( "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - key "github.com/ipfs/go-ipfs/blocks/key" mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) type directoryBuilder struct { @@ -29,8 +29,8 @@ func NewDirectory(dserv mdag.DAGService) *directoryBuilder { } // AddChild adds a (name, key)-pair to the root node. -func (d *directoryBuilder) AddChild(ctx context.Context, name string, k key.Key) error { - cnode, err := d.dserv.Get(ctx, k) +func (d *directoryBuilder) AddChild(ctx context.Context, name string, c *cid.Cid) error { + cnode, err := d.dserv.Get(ctx, c) if err != nil { return err } diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 784cef8a4..d45dffdef 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -6,7 +6,6 @@ import ( "io" "os" - key "github.com/ipfs/go-ipfs/blocks/key" chunk "github.com/ipfs/go-ipfs/importer/chunk" help "github.com/ipfs/go-ipfs/importer/helpers" trickle "github.com/ipfs/go-ipfs/importer/trickle" @@ -15,9 +14,9 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) var ErrSeekFail = errors.New("failed to seek properly") @@ -169,12 +168,12 @@ func (dm *DagModifier) Sync() error { buflen := dm.wrBuf.Len() // overwrite existing dag nodes - thisk, done, err := dm.modifyDag(dm.curNode, dm.writeStart, dm.wrBuf) + thisc, done, err := dm.modifyDag(dm.curNode, dm.writeStart, dm.wrBuf) if err != nil { return err } - nd, err := dm.dagserv.Get(dm.ctx, thisk) + nd, err := dm.dagserv.Get(dm.ctx, thisc) if err != nil { return err } @@ -188,7 +187,7 @@ func (dm *DagModifier) Sync() error { return err } - thisk, err = dm.dagserv.Add(nd) + _, err = dm.dagserv.Add(nd) if err != nil { return err } @@ -205,30 +204,30 @@ func (dm *DagModifier) Sync() error { // modifyDag writes the data in 'data' over the data in 'node' starting at 'offset' // returns the new key of the passed in node and whether or not all the data in the reader // has been consumed. -func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) (key.Key, bool, error) { +func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) (*cid.Cid, bool, error) { f, err := ft.FromBytes(node.Data()) if err != nil { - return "", false, err + return nil, false, err } // If we've reached a leaf node. if len(node.Links) == 0 { n, err := data.Read(f.Data[offset:]) if err != nil && err != io.EOF { - return "", false, err + return nil, false, err } // Update newly written node.. b, err := proto.Marshal(f) if err != nil { - return "", false, err + return nil, false, err } nd := new(mdag.Node) nd.SetData(b) k, err := dm.dagserv.Add(nd) if err != nil { - return "", false, err + return nil, false, err } // Hey look! we're done! @@ -247,20 +246,20 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) if cur+bs > offset { child, err := node.Links[i].GetNode(dm.ctx, dm.dagserv) if err != nil { - return "", false, err + return nil, false, err } k, sdone, err := dm.modifyDag(child, offset-cur, data) if err != nil { - return "", false, err + return nil, false, err } offset += bs - node.Links[i].Hash = mh.Multihash(k) + node.Links[i].Hash = k.Hash() // Recache serialized node _, err = node.EncodeProtobuf(true) if err != nil { - return "", false, err + return nil, false, err } if sdone { From 46639086d6dd76b17f0a39f8236dccdd5d073154 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Sep 2016 07:50:27 -0700 Subject: [PATCH 1464/3817] integrate CIDv0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@626b04074d36af61f09bfec0d5d1decffe8ba6db --- pinning/pinner/gc/gc.go | 25 ++-- pinning/pinner/pin.go | 243 ++++++++++++++++++++----------------- pinning/pinner/pin_test.go | 18 +-- pinning/pinner/set.go | 104 +++++----------- pinning/pinner/set_test.go | 92 +------------- 5 files changed, 190 insertions(+), 292 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 0eb87f867..c1e2eb471 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -10,6 +10,7 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) var log = logging.Logger("gc") @@ -23,7 +24,7 @@ var log = logging.Logger("gc") // // The routine then iterates over every block in the blockstore and // deletes any block that is not found in the marked set. -func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner, bestEffortRoots []key.Key) (<-chan key.Key, error) { +func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner, bestEffortRoots []*cid.Cid) (<-chan key.Key, error) { unlocker := bs.GCLock() bsrv := bserv.New(bs, offline.Exchange(bs)) @@ -70,16 +71,24 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner, bestEffortRo return output, nil } -func Descendants(ctx context.Context, ds dag.DAGService, set key.KeySet, roots []key.Key, bestEffort bool) error { - for _, k := range roots { - set.Add(k) - nd, err := ds.Get(ctx, k) +func Descendants(ctx context.Context, ds dag.DAGService, set key.KeySet, roots []*cid.Cid, bestEffort bool) error { + for _, c := range roots { + set.Add(key.Key(c.Hash())) + nd, err := ds.Get(ctx, c) if err != nil { return err } // EnumerateChildren recursively walks the dag and adds the keys to the given set - err = dag.EnumerateChildren(ctx, ds, nd, set, bestEffort) + err = dag.EnumerateChildren(ctx, ds, nd, func(c *cid.Cid) bool { + k := key.Key(c.Hash()) + seen := set.Has(k) + if seen { + return false + } + set.Add(k) + return true + }, bestEffort) if err != nil { return err } @@ -88,7 +97,7 @@ func Descendants(ctx context.Context, ds dag.DAGService, set key.KeySet, roots [ return nil } -func ColoredSet(ctx context.Context, pn pin.Pinner, ds dag.DAGService, bestEffortRoots []key.Key) (key.KeySet, error) { +func ColoredSet(ctx context.Context, pn pin.Pinner, ds dag.DAGService, bestEffortRoots []*cid.Cid) (key.KeySet, error) { // KeySet currently implemented in memory, in the future, may be bloom filter or // disk backed to conserve memory. gcs := key.NewKeySet() @@ -103,7 +112,7 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ds dag.DAGService, bestEffor } for _, k := range pn.DirectKeys() { - gcs.Add(k) + gcs.Add(key.Key(k.Hash())) } err = Descendants(ctx, ds, gcs, pn.InternalPins(), false) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index d034cbc43..56979cc69 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -4,22 +4,33 @@ package pin import ( "fmt" + "os" "sync" "time" key "github.com/ipfs/go-ipfs/blocks/key" - "github.com/ipfs/go-ipfs/blocks/set" mdag "github.com/ipfs/go-ipfs/merkledag" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) var log = logging.Logger("pin") var pinDatastoreKey = ds.NewKey("/local/pins") -var emptyKey = key.B58KeyDecode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n") +var emptyKey *cid.Cid + +func init() { + e, err := cid.Decode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n") + if err != nil { + log.Error("failed to decode empty key constant") + os.Exit(1) + } + emptyKey = e +} const ( linkRecursive = "recursive" @@ -70,45 +81,45 @@ func StringToPinMode(s string) (PinMode, bool) { } type Pinner interface { - IsPinned(key.Key) (string, bool, error) - IsPinnedWithType(key.Key, PinMode) (string, bool, error) + IsPinned(*cid.Cid) (string, bool, error) + IsPinnedWithType(*cid.Cid, PinMode) (string, bool, error) Pin(context.Context, *mdag.Node, bool) error - Unpin(context.Context, key.Key, bool) error + Unpin(context.Context, *cid.Cid, bool) error // Check if a set of keys are pinned, more efficient than // calling IsPinned for each key - CheckIfPinned(keys ...key.Key) ([]Pinned, error) + CheckIfPinned(cids ...*cid.Cid) ([]Pinned, error) // PinWithMode is for manually editing the pin structure. Use with // care! If used improperly, garbage collection may not be // successful. - PinWithMode(key.Key, PinMode) + PinWithMode(*cid.Cid, PinMode) // RemovePinWithMode is for manually editing the pin structure. // Use with care! If used improperly, garbage collection may not // be successful. - RemovePinWithMode(key.Key, PinMode) + RemovePinWithMode(*cid.Cid, PinMode) Flush() error - DirectKeys() []key.Key - RecursiveKeys() []key.Key - InternalPins() []key.Key + DirectKeys() []*cid.Cid + RecursiveKeys() []*cid.Cid + InternalPins() []*cid.Cid } type Pinned struct { - Key key.Key + Key *cid.Cid Mode PinMode - Via key.Key + Via *cid.Cid } // pinner implements the Pinner interface type pinner struct { lock sync.RWMutex - recursePin set.BlockSet - directPin set.BlockSet + recursePin *cid.Set + directPin *cid.Set // Track the keys used for storing the pinning state, so gc does // not delete them. - internalPin map[key.Key]struct{} + internalPin *cid.Set dserv mdag.DAGService internal mdag.DAGService // dagservice used to store internal objects dstore ds.Datastore @@ -117,15 +128,16 @@ type pinner struct { // NewPinner creates a new pinner using the given datastore as a backend func NewPinner(dstore ds.Datastore, serv, internal mdag.DAGService) Pinner { - rcset := set.NewSimpleBlockSet() - dirset := set.NewSimpleBlockSet() + rcset := cid.NewSet() + dirset := cid.NewSet() return &pinner{ - recursePin: rcset, - directPin: dirset, - dserv: serv, - dstore: dstore, - internal: internal, + recursePin: rcset, + directPin: dirset, + dserv: serv, + dstore: dstore, + internal: internal, + internalPin: cid.NewSet(), } } @@ -133,18 +145,16 @@ func NewPinner(dstore ds.Datastore, serv, internal mdag.DAGService) Pinner { func (p *pinner) Pin(ctx context.Context, node *mdag.Node, recurse bool) error { p.lock.Lock() defer p.lock.Unlock() - k, err := node.Key() - if err != nil { - return err - } + c := node.Cid() + k := key.Key(c.Hash()) if recurse { - if p.recursePin.HasKey(k) { + if p.recursePin.Has(c) { return nil } - if p.directPin.HasKey(k) { - p.directPin.RemoveBlock(k) + if p.directPin.Has(c) { + p.directPin.Remove(c) } // fetch entire graph @@ -153,17 +163,17 @@ func (p *pinner) Pin(ctx context.Context, node *mdag.Node, recurse bool) error { return err } - p.recursePin.AddBlock(k) + p.recursePin.Add(c) } else { - if _, err := p.dserv.Get(ctx, k); err != nil { + if _, err := p.dserv.Get(ctx, c); err != nil { return err } - if p.recursePin.HasKey(k) { + if p.recursePin.Has(c) { return fmt.Errorf("%s already pinned recursively", k.B58String()) } - p.directPin.AddBlock(k) + p.directPin.Add(c) } return nil } @@ -171,10 +181,10 @@ func (p *pinner) Pin(ctx context.Context, node *mdag.Node, recurse bool) error { var ErrNotPinned = fmt.Errorf("not pinned") // Unpin a given key -func (p *pinner) Unpin(ctx context.Context, k key.Key, recursive bool) error { +func (p *pinner) Unpin(ctx context.Context, c *cid.Cid, recursive bool) error { p.lock.Lock() defer p.lock.Unlock() - reason, pinned, err := p.isPinnedWithType(k, Any) + reason, pinned, err := p.isPinnedWithType(c, Any) if err != nil { return err } @@ -184,41 +194,41 @@ func (p *pinner) Unpin(ctx context.Context, k key.Key, recursive bool) error { switch reason { case "recursive": if recursive { - p.recursePin.RemoveBlock(k) + p.recursePin.Remove(c) return nil } else { - return fmt.Errorf("%s is pinned recursively", k) + return fmt.Errorf("%s is pinned recursively", c) } case "direct": - p.directPin.RemoveBlock(k) + p.directPin.Remove(c) return nil default: - return fmt.Errorf("%s is pinned indirectly under %s", k, reason) + return fmt.Errorf("%s is pinned indirectly under %s", c, reason) } } -func (p *pinner) isInternalPin(key key.Key) bool { - _, ok := p.internalPin[key] - return ok +func (p *pinner) isInternalPin(c *cid.Cid) bool { + return p.internalPin.Has(c) } // IsPinned returns whether or not the given key is pinned // and an explanation of why its pinned -func (p *pinner) IsPinned(k key.Key) (string, bool, error) { +func (p *pinner) IsPinned(c *cid.Cid) (string, bool, error) { p.lock.RLock() defer p.lock.RUnlock() - return p.isPinnedWithType(k, Any) + return p.isPinnedWithType(c, Any) } -func (p *pinner) IsPinnedWithType(k key.Key, mode PinMode) (string, bool, error) { +func (p *pinner) IsPinnedWithType(c *cid.Cid, mode PinMode) (string, bool, error) { p.lock.RLock() defer p.lock.RUnlock() - return p.isPinnedWithType(k, mode) + return p.isPinnedWithType(c, mode) } // isPinnedWithType is the implementation of IsPinnedWithType that does not lock. // intended for use by other pinned methods that already take locks -func (p *pinner) isPinnedWithType(k key.Key, mode PinMode) (string, bool, error) { +func (p *pinner) isPinnedWithType(c *cid.Cid, mode PinMode) (string, bool, error) { + k := key.Key(c.Hash()) switch mode { case Any, Direct, Indirect, Recursive, Internal: default: @@ -226,21 +236,21 @@ func (p *pinner) isPinnedWithType(k key.Key, mode PinMode) (string, bool, error) mode, Direct, Indirect, Recursive, Internal, Any) return "", false, err } - if (mode == Recursive || mode == Any) && p.recursePin.HasKey(k) { + if (mode == Recursive || mode == Any) && p.recursePin.Has(c) { return linkRecursive, true, nil } if mode == Recursive { return "", false, nil } - if (mode == Direct || mode == Any) && p.directPin.HasKey(k) { + if (mode == Direct || mode == Any) && p.directPin.Has(c) { return linkDirect, true, nil } if mode == Direct { return "", false, nil } - if (mode == Internal || mode == Any) && p.isInternalPin(k) { + if (mode == Internal || mode == Any) && p.isInternalPin(c) { return linkInternal, true, nil } if mode == Internal { @@ -248,8 +258,8 @@ func (p *pinner) isPinnedWithType(k key.Key, mode PinMode) (string, bool, error) } // Default is Indirect - for _, rk := range p.recursePin.GetKeys() { - rnd, err := p.dserv.Get(context.Background(), rk) + for _, rc := range p.recursePin.Keys() { + rnd, err := p.dserv.Get(context.Background(), rc) if err != nil { return "", false, err } @@ -259,90 +269,99 @@ func (p *pinner) isPinnedWithType(k key.Key, mode PinMode) (string, bool, error) return "", false, err } if has { - return rk.B58String(), true, nil + return rc.String(), true, nil } } return "", false, nil } -func (p *pinner) CheckIfPinned(keys ...key.Key) ([]Pinned, error) { +func (p *pinner) CheckIfPinned(cids ...*cid.Cid) ([]Pinned, error) { p.lock.RLock() defer p.lock.RUnlock() - pinned := make([]Pinned, 0, len(keys)) - toCheck := make(map[key.Key]struct{}) + pinned := make([]Pinned, 0, len(cids)) + toCheck := cid.NewSet() // First check for non-Indirect pins directly - for _, k := range keys { - if p.recursePin.HasKey(k) { - pinned = append(pinned, Pinned{Key: k, Mode: Recursive}) - } else if p.directPin.HasKey(k) { - pinned = append(pinned, Pinned{Key: k, Mode: Direct}) - } else if p.isInternalPin(k) { - pinned = append(pinned, Pinned{Key: k, Mode: Internal}) + for _, c := range cids { + if p.recursePin.Has(c) { + pinned = append(pinned, Pinned{Key: c, Mode: Recursive}) + } else if p.directPin.Has(c) { + pinned = append(pinned, Pinned{Key: c, Mode: Direct}) + } else if p.isInternalPin(c) { + pinned = append(pinned, Pinned{Key: c, Mode: Internal}) } else { - toCheck[k] = struct{}{} + toCheck.Add(c) } } // Now walk all recursive pins to check for indirect pins - var checkChildren func(key.Key, key.Key) error - checkChildren = func(rk key.Key, parentKey key.Key) error { + var checkChildren func(*cid.Cid, *cid.Cid) error + checkChildren = func(rk, parentKey *cid.Cid) error { parent, err := p.dserv.Get(context.Background(), parentKey) if err != nil { return err } for _, lnk := range parent.Links { - k := key.Key(lnk.Hash) + c := cid.NewCidV0(lnk.Hash) - if _, found := toCheck[k]; found { + if toCheck.Has(c) { pinned = append(pinned, - Pinned{Key: k, Mode: Indirect, Via: rk}) - delete(toCheck, k) + Pinned{Key: c, Mode: Indirect, Via: rk}) + toCheck.Remove(c) } - err := checkChildren(rk, k) + err := checkChildren(rk, c) if err != nil { return err } - if len(toCheck) == 0 { + if toCheck.Len() == 0 { return nil } } return nil } - for _, rk := range p.recursePin.GetKeys() { + + for _, rk := range p.recursePin.Keys() { err := checkChildren(rk, rk) if err != nil { return nil, err } - if len(toCheck) == 0 { + if toCheck.Len() == 0 { break } } // Anything left in toCheck is not pinned - for k, _ := range toCheck { + for _, k := range toCheck.Keys() { pinned = append(pinned, Pinned{Key: k, Mode: NotPinned}) } return pinned, nil } -func (p *pinner) RemovePinWithMode(key key.Key, mode PinMode) { +func (p *pinner) RemovePinWithMode(c *cid.Cid, mode PinMode) { p.lock.Lock() defer p.lock.Unlock() switch mode { case Direct: - p.directPin.RemoveBlock(key) + p.directPin.Remove(c) case Recursive: - p.recursePin.RemoveBlock(key) + p.recursePin.Remove(c) default: // programmer error, panic OK panic("unrecognized pin type") } } +func cidSetWithValues(cids []*cid.Cid) *cid.Set { + out := cid.NewSet() + for _, c := range cids { + out.Add(c) + } + return out +} + // LoadPinner loads a pinner and its keysets from the given datastore func LoadPinner(d ds.Datastore, dserv, internal mdag.DAGService) (Pinner, error) { p := new(pinner) @@ -356,29 +375,29 @@ func LoadPinner(d ds.Datastore, dserv, internal mdag.DAGService) (Pinner, error) return nil, fmt.Errorf("cannot load pin state: %s was not bytes", pinDatastoreKey) } - rootKey := key.Key(rootKeyBytes) + rootCid, err := cid.Cast(rootKeyBytes) + if err != nil { + return nil, err + } ctx, cancel := context.WithTimeout(context.TODO(), time.Second*5) defer cancel() - root, err := internal.Get(ctx, rootKey) + root, err := internal.Get(ctx, rootCid) if err != nil { return nil, fmt.Errorf("cannot find pinning root object: %v", err) } - internalPin := map[key.Key]struct{}{ - rootKey: struct{}{}, - } - recordInternal := func(k key.Key) { - internalPin[k] = struct{}{} - } + internalset := cid.NewSet() + internalset.Add(rootCid) + recordInternal := internalset.Add { // load recursive set recurseKeys, err := loadSet(ctx, internal, root, linkRecursive, recordInternal) if err != nil { return nil, fmt.Errorf("cannot load recursive pins: %v", err) } - p.recursePin = set.SimpleSetFromKeys(recurseKeys) + p.recursePin = cidSetWithValues(recurseKeys) } { // load direct set @@ -386,10 +405,10 @@ func LoadPinner(d ds.Datastore, dserv, internal mdag.DAGService) (Pinner, error) if err != nil { return nil, fmt.Errorf("cannot load direct pins: %v", err) } - p.directPin = set.SimpleSetFromKeys(directKeys) + p.directPin = cidSetWithValues(directKeys) } - p.internalPin = internalPin + p.internalPin = internalset // assign services p.dserv = dserv @@ -400,13 +419,13 @@ func LoadPinner(d ds.Datastore, dserv, internal mdag.DAGService) (Pinner, error) } // DirectKeys returns a slice containing the directly pinned keys -func (p *pinner) DirectKeys() []key.Key { - return p.directPin.GetKeys() +func (p *pinner) DirectKeys() []*cid.Cid { + return p.directPin.Keys() } // RecursiveKeys returns a slice containing the recursively pinned keys -func (p *pinner) RecursiveKeys() []key.Key { - return p.recursePin.GetKeys() +func (p *pinner) RecursiveKeys() []*cid.Cid { + return p.recursePin.Keys() } // Flush encodes and writes pinner keysets to the datastore @@ -416,14 +435,12 @@ func (p *pinner) Flush() error { ctx := context.TODO() - internalPin := make(map[key.Key]struct{}) - recordInternal := func(k key.Key) { - internalPin[k] = struct{}{} - } + internalset := cid.NewSet() + recordInternal := internalset.Add root := &mdag.Node{} { - n, err := storeSet(ctx, p.internal, p.directPin.GetKeys(), recordInternal) + n, err := storeSet(ctx, p.internal, p.directPin.Keys(), recordInternal) if err != nil { return err } @@ -433,7 +450,7 @@ func (p *pinner) Flush() error { } { - n, err := storeSet(ctx, p.internal, p.recursePin.GetKeys(), recordInternal) + n, err := storeSet(ctx, p.internal, p.recursePin.Keys(), recordInternal) if err != nil { return err } @@ -453,45 +470,45 @@ func (p *pinner) Flush() error { return err } - internalPin[k] = struct{}{} - if err := p.dstore.Put(pinDatastoreKey, []byte(k)); err != nil { + internalset.Add(k) + if err := p.dstore.Put(pinDatastoreKey, k.Bytes()); err != nil { return fmt.Errorf("cannot store pin state: %v", err) } - p.internalPin = internalPin + p.internalPin = internalset return nil } -func (p *pinner) InternalPins() []key.Key { +func (p *pinner) InternalPins() []*cid.Cid { p.lock.Lock() defer p.lock.Unlock() - var out []key.Key - for k, _ := range p.internalPin { - out = append(out, k) + var out []*cid.Cid + for _, c := range p.internalPin.Keys() { + out = append(out, c) } return out } // PinWithMode allows the user to have fine grained control over pin // counts -func (p *pinner) PinWithMode(k key.Key, mode PinMode) { +func (p *pinner) PinWithMode(c *cid.Cid, mode PinMode) { p.lock.Lock() defer p.lock.Unlock() switch mode { case Recursive: - p.recursePin.AddBlock(k) + p.recursePin.Add(c) case Direct: - p.directPin.AddBlock(k) + p.directPin.Add(c) } } func hasChild(ds mdag.DAGService, root *mdag.Node, child key.Key) (bool, error) { for _, lnk := range root.Links { - k := key.Key(lnk.Hash) - if k == child { + c := cid.NewCidV0(lnk.Hash) + if key.Key(c.Hash()) == child { return true, nil } - nd, err := ds.Get(context.Background(), k) + nd, err := ds.Get(context.Background(), c) if err != nil { return false, err } diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 91c6b8c0e..f1f626f54 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -4,28 +4,28 @@ import ( "testing" "time" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - "github.com/ipfs/go-ipfs/blocks/blockstore" - key "github.com/ipfs/go-ipfs/blocks/key" bs "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" + ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) -func randNode() (*mdag.Node, key.Key) { +func randNode() (*mdag.Node, *cid.Cid) { nd := new(mdag.Node) nd.SetData(make([]byte, 32)) util.NewTimeSeededRand().Read(nd.Data()) - k, _ := nd.Key() + k := nd.Cid() return nd, k } -func assertPinned(t *testing.T, p Pinner, k key.Key, failmsg string) { - _, pinned, err := p.IsPinned(k) +func assertPinned(t *testing.T, p Pinner, c *cid.Cid, failmsg string) { + _, pinned, err := p.IsPinned(c) if err != nil { t.Fatal(err) } @@ -93,7 +93,7 @@ func TestPinnerBasic(t *testing.T) { assertPinned(t, p, ck, "child of recursively pinned node not found") - bk, _ := b.Key() + bk := b.Cid() assertPinned(t, p, bk, "Recursively pinned node not found..") d, _ := randNode() @@ -119,7 +119,7 @@ func TestPinnerBasic(t *testing.T) { t.Fatal(err) } - dk, _ := d.Key() + dk := d.Cid() assertPinned(t, p, dk, "pinned node not found.") // Test recursive unpin diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 7257ccaec..eb5cb5d91 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "hash/fnv" - "io" "sort" "unsafe" @@ -16,6 +15,7 @@ import ( "github.com/ipfs/go-ipfs/pin/internal/pb" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) const ( @@ -31,18 +31,18 @@ func randomSeed() (uint32, error) { return binary.LittleEndian.Uint32(buf[:]), nil } -func hash(seed uint32, k key.Key) uint32 { +func hash(seed uint32, c *cid.Cid) uint32 { var buf [4]byte binary.LittleEndian.PutUint32(buf[:], seed) h := fnv.New32a() _, _ = h.Write(buf[:]) - _, _ = io.WriteString(h, string(k)) + _, _ = h.Write(c.Bytes()) return h.Sum32() } -type itemIterator func() (k key.Key, data []byte, ok bool) +type itemIterator func() (c *cid.Cid, data []byte, ok bool) -type keyObserver func(key.Key) +type keyObserver func(*cid.Cid) // refcount is the marshaled format of refcounts. It may change // between versions; this is valid for version 1. Changing it may @@ -100,7 +100,7 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint Links: make([]*merkledag.Link, 0, defaultFanout+maxItems), } for i := 0; i < defaultFanout; i++ { - n.Links = append(n.Links, &merkledag.Link{Hash: emptyKey.ToMultihash()}) + n.Links = append(n.Links, &merkledag.Link{Hash: emptyKey.Hash()}) } internalKeys(emptyKey) hdr := &pb.Set{ @@ -121,7 +121,7 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint // all done break } - n.Links = append(n.Links, &merkledag.Link{Hash: k.ToMultihash()}) + n.Links = append(n.Links, &merkledag.Link{Hash: k.Hash()}) n.SetData(append(n.Data(), data...)) } // sort by hash, also swap item Data @@ -134,7 +134,7 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint // wasteful but simple type item struct { - k key.Key + c *cid.Cid data []byte } hashed := make(map[uint32][]item) @@ -147,13 +147,13 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint hashed[h] = append(hashed[h], item{k, data}) } for h, items := range hashed { - childIter := func() (k key.Key, data []byte, ok bool) { + childIter := func() (c *cid.Cid, data []byte, ok bool) { if len(items) == 0 { - return "", nil, false + return nil, nil, false } first := items[0] items = items[1:] - return first.k, first.data, true + return first.c, first.data, true } child, err := storeItems(ctx, dag, uint64(len(items)), childIter, internalKeys) if err != nil { @@ -170,7 +170,7 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint internalKeys(childKey) l := &merkledag.Link{ Name: "", - Hash: childKey.ToMultihash(), + Hash: childKey.Hash(), Size: size, } n.Links[int(h%defaultFanout)] = l @@ -231,8 +231,9 @@ func walkItems(ctx context.Context, dag merkledag.DAGService, n *merkledag.Node, } } for _, l := range n.Links[:fanout] { - children(key.Key(l.Hash)) - if key.Key(l.Hash) == emptyKey { + c := cid.NewCidV0(l.Hash) + children(c) + if c.Equals(emptyKey) { continue } subtree, err := l.GetNode(ctx, dag) @@ -246,20 +247,23 @@ func walkItems(ctx context.Context, dag merkledag.DAGService, n *merkledag.Node, return nil } -func loadSet(ctx context.Context, dag merkledag.DAGService, root *merkledag.Node, name string, internalKeys keyObserver) ([]key.Key, error) { +func loadSet(ctx context.Context, dag merkledag.DAGService, root *merkledag.Node, name string, internalKeys keyObserver) ([]*cid.Cid, error) { l, err := root.GetNodeLink(name) if err != nil { return nil, err } - internalKeys(key.Key(l.Hash)) + + lnkc := cid.NewCidV0(l.Hash) + internalKeys(lnkc) + n, err := l.GetNode(ctx, dag) if err != nil { return nil, err } - var res []key.Key + var res []*cid.Cid walk := func(buf []byte, idx int, link *merkledag.Link) error { - res = append(res, key.Key(link.Hash)) + res = append(res, cid.NewCidV0(link.Hash)) return nil } if err := walkItems(ctx, dag, n, walk, internalKeys); err != nil { @@ -273,7 +277,8 @@ func loadMultiset(ctx context.Context, dag merkledag.DAGService, root *merkledag if err != nil { return nil, fmt.Errorf("Failed to get link %s: %v", name, err) } - internalKeys(key.Key(l.Hash)) + c := cid.NewCidV0(l.Hash) + internalKeys(c) n, err := l.GetNode(ctx, dag) if err != nil { return nil, fmt.Errorf("Failed to get node from link %s: %v", name, err) @@ -292,24 +297,24 @@ func loadMultiset(ctx context.Context, dag merkledag.DAGService, root *merkledag return refcounts, nil } -func storeSet(ctx context.Context, dag merkledag.DAGService, keys []key.Key, internalKeys keyObserver) (*merkledag.Node, error) { - iter := func() (k key.Key, data []byte, ok bool) { - if len(keys) == 0 { - return "", nil, false +func storeSet(ctx context.Context, dag merkledag.DAGService, cids []*cid.Cid, internalKeys keyObserver) (*merkledag.Node, error) { + iter := func() (c *cid.Cid, data []byte, ok bool) { + if len(cids) == 0 { + return nil, nil, false } - first := keys[0] - keys = keys[1:] + first := cids[0] + cids = cids[1:] return first, nil, true } - n, err := storeItems(ctx, dag, uint64(len(keys)), iter, internalKeys) + n, err := storeItems(ctx, dag, uint64(len(cids)), iter, internalKeys) if err != nil { return nil, err } - k, err := dag.Add(n) + c, err := dag.Add(n) if err != nil { return nil, err } - internalKeys(k) + internalKeys(c) return n, nil } @@ -320,46 +325,3 @@ func copyRefcounts(orig map[key.Key]uint64) map[key.Key]uint64 { } return r } - -func storeMultiset(ctx context.Context, dag merkledag.DAGService, refcounts map[key.Key]uint64, internalKeys keyObserver) (*merkledag.Node, error) { - // make a working copy of the refcounts - refcounts = copyRefcounts(refcounts) - - iter := func() (k key.Key, data []byte, ok bool) { - // Every call of this function returns the next refcount item. - // - // This function splits out the uint64 reference counts as - // smaller increments, as fits in type refcount. Most of the - // time the refcount will fit inside just one, so this saves - // space. - // - // We use range here to pick an arbitrary item in the map, but - // not really iterate the map. - for k, refs := range refcounts { - // Max value a single multiset item can store - num := ^refcount(0) - if refs <= uint64(num) { - // Remaining count fits in a single item; remove the - // key from the map. - num = refcount(refs) - delete(refcounts, k) - } else { - // Count is too large to fit in one item, the key will - // repeat in some later call. - refcounts[k] -= uint64(num) - } - return k, num.Bytes(), true - } - return "", nil, false - } - n, err := storeItems(ctx, dag, uint64(len(refcounts)), iter, internalKeys) - if err != nil { - return nil, err - } - k, err := dag.Add(n) - if err != nil { - return nil, err - } - internalKeys(k) - return n, nil -} diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 83d65dd02..a5e9152d4 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -1,20 +1,6 @@ package pin -import ( - "testing" - "testing/quick" - - "github.com/ipfs/go-ipfs/blocks/blockstore" - "github.com/ipfs/go-ipfs/blocks/key" - "github.com/ipfs/go-ipfs/blockservice" - "github.com/ipfs/go-ipfs/exchange/offline" - "github.com/ipfs/go-ipfs/merkledag" - "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" - mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" -) +import "github.com/ipfs/go-ipfs/blocks/key" func ignoreKeys(key.Key) {} @@ -25,79 +11,3 @@ func copyMap(m map[key.Key]uint16) map[key.Key]uint64 { } return c } - -func TestMultisetRoundtrip(t *testing.T) { - dstore := dssync.MutexWrap(datastore.NewMapDatastore()) - bstore := blockstore.NewBlockstore(dstore) - bserv := blockservice.New(bstore, offline.Exchange(bstore)) - dag := merkledag.NewDAGService(bserv) - - fn := func(m map[key.Key]uint16) bool { - // Convert invalid multihash from input to valid ones - for k, v := range m { - if _, err := mh.Cast([]byte(k)); err != nil { - delete(m, k) - m[key.Key(u.Hash([]byte(k)))] = v - } - } - - // Generate a smaller range for refcounts than full uint64, as - // otherwise this just becomes overly cpu heavy, splitting it - // out into too many items. That means we need to convert to - // the right kind of map. As storeMultiset mutates the map as - // part of its bookkeeping, this is actually good. - refcounts := copyMap(m) - - ctx := context.Background() - n, err := storeMultiset(ctx, dag, refcounts, ignoreKeys) - if err != nil { - t.Fatalf("storing multiset: %v", err) - } - - // Check that the node n is in the DAG - k, err := n.Key() - if err != nil { - t.Fatalf("Could not get key: %v", err) - } - _, err = dag.Get(ctx, k) - if err != nil { - t.Fatalf("Could not get node: %v", err) - } - - root := &merkledag.Node{} - const linkName = "dummylink" - if err := root.AddNodeLink(linkName, n); err != nil { - t.Fatalf("adding link to root node: %v", err) - } - - roundtrip, err := loadMultiset(ctx, dag, root, linkName, ignoreKeys) - if err != nil { - t.Fatalf("loading multiset: %v", err) - } - - orig := copyMap(m) - success := true - for k, want := range orig { - if got, ok := roundtrip[k]; ok { - if got != want { - success = false - t.Logf("refcount changed: %v -> %v for %q", want, got, k) - } - delete(orig, k) - delete(roundtrip, k) - } - } - for k, v := range orig { - success = false - t.Logf("refcount missing: %v for %q", v, k) - } - for k, v := range roundtrip { - success = false - t.Logf("refcount extra: %v for %q", v, k) - } - return success - } - if err := quick.Check(fn, nil); err != nil { - t.Fatal(err) - } -} From afa7972d33562d9582566f263024bf35458cf214 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Sep 2016 07:50:27 -0700 Subject: [PATCH 1465/3817] integrate CIDv0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-interface@10389ab7a8513a693ab1ee2091a5b121078b5ab6 --- exchange/interface.go | 1 + 1 file changed, 1 insertion(+) diff --git a/exchange/interface.go b/exchange/interface.go index 6db476d9e..6f246ebc0 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -6,6 +6,7 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" key "github.com/ipfs/go-ipfs/blocks/key" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From f1996a259d88521ca8891acc34c4207d9f8f54ee Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Sep 2016 07:50:27 -0700 Subject: [PATCH 1466/3817] integrate CIDv0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@e698e39a6382fc0e9171a540c37fbdd084bd738d --- exchange/offline/offline.go | 1 + 1 file changed, 1 insertion(+) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index d2ee4fbaa..e36d59a67 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -7,6 +7,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From e957e3f059522e439436ba6ff3c2bff74426f73e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Sep 2016 07:50:27 -0700 Subject: [PATCH 1467/3817] integrate CIDv0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@09bae926ba05d38aa22589c3a12337760bcc42f2 --- mfs/dir.go | 7 +------ mfs/mfs_test.go | 22 ++++++---------------- mfs/repub_test.go | 8 ++++---- mfs/system.go | 46 ++++++++++++++++------------------------------ 4 files changed, 27 insertions(+), 56 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 9009d2431..3612516f5 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -246,12 +246,7 @@ func (d *Directory) List() ([]NodeListing, error) { return nil, err } - k, err := nd.Key() - if err != nil { - return nil, err - } - - child.Hash = k.B58String() + child.Hash = nd.Key().B58String() out = append(out, child) } diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index f4aba72cb..13da0358e 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,7 +14,6 @@ import ( "time" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" - key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" importer "github.com/ipfs/go-ipfs/importer" @@ -28,6 +27,7 @@ import ( dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) func emptyDirNode() *dag.Node { @@ -187,8 +187,8 @@ func setupRoot(ctx context.Context, t *testing.T) (dag.DAGService, *Root) { ds := getDagserv(t) root := emptyDirNode() - rt, err := NewRoot(ctx, ds, root, func(ctx context.Context, k key.Key) error { - fmt.Println("PUBLISHED: ", k) + rt, err := NewRoot(ctx, ds, root, func(ctx context.Context, c *cid.Cid) error { + fmt.Println("PUBLISHED: ", c) return nil }) @@ -280,10 +280,7 @@ func TestDirectoryLoadFromDag(t *testing.T) { t.Fatal(err) } - fihash, err := nd.Multihash() - if err != nil { - t.Fatal(err) - } + fihash := nd.Multihash() dir := emptyDirNode() _, err = ds.Add(dir) @@ -291,10 +288,7 @@ func TestDirectoryLoadFromDag(t *testing.T) { t.Fatal(err) } - dirhash, err := dir.Multihash() - if err != nil { - t.Fatal(err) - } + dirhash := dir.Multihash() top := emptyDirNode() top.Links = []*dag.Link{ @@ -803,11 +797,7 @@ func TestFlushing(t *testing.T) { t.Fatal("root wasnt a directory") } - rnk, err := rnd.Key() - if err != nil { - t.Fatal(err) - } - + rnk := rnd.Key() exp := "QmWMVyhTuyxUrXX3ynz171jq76yY3PktfY9Bxiph7b9ikr" if rnk.B58String() != exp { t.Fatalf("dag looks wrong, expected %s, but got %s", exp, rnk.B58String()) diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 32e0b2b27..09d8d4124 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - key "github.com/ipfs/go-ipfs/blocks/key" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) func TestRepublisher(t *testing.T) { @@ -19,7 +19,7 @@ func TestRepublisher(t *testing.T) { pub := make(chan struct{}) - pf := func(ctx context.Context, k key.Key) error { + pf := func(ctx context.Context, c *cid.Cid) error { pub <- struct{}{} return nil } @@ -30,7 +30,7 @@ func TestRepublisher(t *testing.T) { rp := NewRepublisher(ctx, pf, tshort, tlong) go rp.Run() - rp.Update("test") + rp.Update(nil) // should hit short timeout select { @@ -43,7 +43,7 @@ func TestRepublisher(t *testing.T) { go func() { for { - rp.Update("a") + rp.Update(nil) time.Sleep(time.Millisecond * 10) select { case <-cctx.Done(): diff --git a/mfs/system.go b/mfs/system.go index 56891cc21..3e2e74e76 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -14,12 +14,12 @@ import ( "sync" "time" - key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) var ErrNotExist = errors.New("no such rootfs") @@ -61,19 +61,15 @@ type Root struct { Type string } -type PubFunc func(context.Context, key.Key) error +type PubFunc func(context.Context, *cid.Cid) error // newRoot creates a new Root and starts up a republisher routine for it func NewRoot(parent context.Context, ds dag.DAGService, node *dag.Node, pf PubFunc) (*Root, error) { - ndk, err := node.Key() - if err != nil { - return nil, err - } var repub *Republisher if pf != nil { repub = NewRepublisher(parent, pf, time.Millisecond*300, time.Second*3) - repub.setVal(ndk) + repub.setVal(node.Cid()) go repub.Run() } @@ -91,9 +87,9 @@ func NewRoot(parent context.Context, ds dag.DAGService, node *dag.Node, pf PubFu switch pbn.GetType() { case ft.TDirectory: - root.val = NewDirectory(parent, ndk.String(), node, root, ds) + root.val = NewDirectory(parent, node.String(), node, root, ds) case ft.TFile, ft.TMetadata, ft.TRaw: - fi, err := NewFile(ndk.String(), node, root, ds) + fi, err := NewFile(node.String(), node, root, ds) if err != nil { return nil, err } @@ -114,13 +110,8 @@ func (kr *Root) Flush() error { return err } - k, err := nd.Key() - if err != nil { - return err - } - if kr.repub != nil { - kr.repub.Update(k) + kr.repub.Update(nd.Cid()) } return nil } @@ -128,13 +119,13 @@ func (kr *Root) Flush() error { // closeChild implements the childCloser interface, and signals to the publisher that // there are changes ready to be published func (kr *Root) closeChild(name string, nd *dag.Node, sync bool) error { - k, err := kr.dserv.Add(nd) + c, err := kr.dserv.Add(nd) if err != nil { return err } if kr.repub != nil { - kr.repub.Update(k) + kr.repub.Update(c) } return nil } @@ -145,13 +136,8 @@ func (kr *Root) Close() error { return err } - k, err := nd.Key() - if err != nil { - return err - } - if kr.repub != nil { - kr.repub.Update(k) + kr.repub.Update(nd.Cid()) return kr.repub.Close() } @@ -170,11 +156,11 @@ type Republisher struct { cancel func() lk sync.Mutex - val key.Key - lastpub key.Key + val *cid.Cid + lastpub *cid.Cid } -func (rp *Republisher) getVal() key.Key { +func (rp *Republisher) getVal() *cid.Cid { rp.lk.Lock() defer rp.lk.Unlock() return rp.val @@ -195,10 +181,10 @@ func NewRepublisher(ctx context.Context, pf PubFunc, tshort, tlong time.Duration } } -func (p *Republisher) setVal(k key.Key) { +func (p *Republisher) setVal(c *cid.Cid) { p.lk.Lock() defer p.lk.Unlock() - p.val = k + p.val = c } func (p *Republisher) pubNow() { @@ -230,8 +216,8 @@ func (p *Republisher) Close() error { // Touch signals that an update has occurred since the last publish. // Multiple consecutive touches may extend the time period before // the next Publish occurs in order to more efficiently batch updates -func (np *Republisher) Update(k key.Key) { - np.setVal(k) +func (np *Republisher) Update(c *cid.Cid) { + np.setVal(c) select { case np.Publish <- struct{}{}: default: From 0920b431ee88001da967f5485561cf844bc2f9f9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Sep 2016 07:50:27 -0700 Subject: [PATCH 1468/3817] integrate CIDv0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@c3c3c3486cca6a1a6c91090917f33673d0362e84 --- path/path.go | 30 ++++++++++++------------------ path/resolver.go | 14 ++++++-------- path/resolver_test.go | 12 +++--------- 3 files changed, 21 insertions(+), 35 deletions(-) diff --git a/path/path.go b/path/path.go index 790168de0..884c1780d 100644 --- a/path/path.go +++ b/path/path.go @@ -5,10 +5,7 @@ import ( "path" "strings" - key "github.com/ipfs/go-ipfs/blocks/key" - - b58 "gx/ipfs/QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf/go-base58" - mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) // ErrBadPath is returned when a given path is incorrectly formatted @@ -23,9 +20,9 @@ func FromString(s string) Path { return Path(s) } -// FromKey safely converts a Key type to a Path type -func FromKey(k key.Key) Path { - return Path("/ipfs/" + k.String()) +// FromCid safely converts a cid.Cid type to a Path type +func FromCid(c *cid.Cid) Path { + return Path("/ipfs/" + c.String()) } func (p Path) Segments() []string { @@ -75,7 +72,7 @@ func FromSegments(prefix string, seg ...string) (Path, error) { func ParsePath(txt string) (Path, error) { parts := strings.Split(txt, "/") if len(parts) == 1 { - kp, err := ParseKeyToPath(txt) + kp, err := ParseCidToPath(txt) if err == nil { return kp, nil } @@ -84,7 +81,7 @@ func ParsePath(txt string) (Path, error) { // if the path doesnt being with a '/' // we expect this to start with a hash, and be an 'ipfs' path if parts[0] != "" { - if _, err := ParseKeyToPath(parts[0]); err != nil { + if _, err := ParseCidToPath(parts[0]); err != nil { return "", ErrBadPath } // The case when the path starts with hash without a protocol prefix @@ -96,7 +93,7 @@ func ParsePath(txt string) (Path, error) { } if parts[1] == "ipfs" { - if _, err := ParseKeyToPath(parts[2]); err != nil { + if _, err := ParseCidToPath(parts[2]); err != nil { return "", err } } else if parts[1] != "ipns" { @@ -106,20 +103,17 @@ func ParsePath(txt string) (Path, error) { return Path(txt), nil } -func ParseKeyToPath(txt string) (Path, error) { +func ParseCidToPath(txt string) (Path, error) { if txt == "" { return "", ErrNoComponents } - chk := b58.Decode(txt) - if len(chk) == 0 { - return "", errors.New("not a key") - } - - if _, err := mh.Cast(chk); err != nil { + c, err := cid.Decode(txt) + if err != nil { return "", err } - return FromKey(key.Key(chk)), nil + + return FromCid(c), nil } func (p *Path) IsValid() error { diff --git a/path/resolver.go b/path/resolver.go index a254f456c..8fc59ac9d 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -9,9 +9,9 @@ import ( mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - key "github.com/ipfs/go-ipfs/blocks/key" merkledag "github.com/ipfs/go-ipfs/merkledag" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) var log = logging.Logger("path") @@ -38,7 +38,7 @@ type Resolver struct { // SplitAbsPath clean up and split fpath. It extracts the first component (which // must be a Multihash) and return it separately. -func SplitAbsPath(fpath Path) (mh.Multihash, []string, error) { +func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) { log.Debugf("Resolve: '%s'", fpath) @@ -52,14 +52,12 @@ func SplitAbsPath(fpath Path) (mh.Multihash, []string, error) { return nil, nil, ErrNoComponents } - // first element in the path is a b58 hash (for now) - h, err := mh.FromB58String(parts[0]) + c, err := cid.Decode(parts[0]) if err != nil { - log.Debug("given path element is not a base58 string.\n") return nil, nil, err } - return h, parts[1:], nil + return c, parts[1:], nil } // ResolvePath fetches the node for given path. It returns the last item @@ -87,7 +85,7 @@ func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]*me } log.Debug("resolve dag get") - nd, err := s.DAG.Get(ctx, key.Key(h)) + nd, err := s.DAG.Get(ctx, h) if err != nil { return nil, err } @@ -117,7 +115,7 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd *merkledag.Node, names nextnode, err := nd.GetLinkedNode(ctx, s.DAG, name) if err == merkledag.ErrLinkNotFound { - n, _ := nd.Multihash() + n := nd.Multihash() return result, ErrNoLink{Name: name, Node: n} } else if err != nil { return append(result, nextnode), err diff --git a/path/resolver_test.go b/path/resolver_test.go index 735a79e6d..3a45581ed 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -17,7 +17,7 @@ func randNode() (*merkledag.Node, key.Key) { node := new(merkledag.Node) node.SetData(make([]byte, 32)) util.NewTimeSeededRand().Read(node.Data()) - k, _ := node.Key() + k := node.Key() return node, k } @@ -46,10 +46,7 @@ func TestRecurivePathResolution(t *testing.T) { } } - aKey, err := a.Key() - if err != nil { - t.Fatal(err) - } + aKey := a.Key() segments := []string{aKey.String(), "child", "grandchild"} p, err := path.FromSegments("/ipfs/", segments...) @@ -63,10 +60,7 @@ func TestRecurivePathResolution(t *testing.T) { t.Fatal(err) } - key, err := node.Key() - if err != nil { - t.Fatal(err) - } + key := node.Key() if key.String() != cKey.String() { t.Fatal(fmt.Errorf( "recursive path resolution failed for %s: %s != %s", From b0012a77f392f857421ea994f65f6744e56f6fe4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 7 Sep 2016 15:16:21 -0700 Subject: [PATCH 1469/3817] SQUASHME: some cleanup License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@a85c958bcfa7152bd49cb0338ec126eec8875a0e --- ipld/merkledag/merkledag.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index f872d70ae..b0efec855 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -119,6 +119,12 @@ type NodeOption struct { Err error } +// TODO: this is a mid-term hack to get around the fact that blocks don't +// have full CIDs and potentially (though we don't know of any such scenario) +// may have the same block with multiple different encodings. +// We have discussed the possiblity of using CIDs as datastore keys +// in the future. This would be a much larger changeset than i want to make +// right now. func cidsToKeyMapping(cids []*cid.Cid) map[key.Key]*cid.Cid { mapping := make(map[key.Key]*cid.Cid) for _, c := range cids { From 4198daa0889773dbdf7863aabaaad0cfbcd40230 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 7 Sep 2016 15:16:21 -0700 Subject: [PATCH 1470/3817] SQUASHME: some cleanup License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@0d2ac7b76050f296e57f81c6ce20be935e89eb7a --- blockservice/blockservice.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index f98c0f96f..aeea822bf 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -119,7 +119,6 @@ func (s *BlockService) AddObjects(bs []Object) ([]*cid.Cid, error) { func (s *BlockService) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) { log.Debugf("BlockService GetBlock: '%s'", c) - // TODO: blockstore shouldnt care about Cids, need an easier way to strip the abstraction block, err := s.Blockstore.Get(key.Key(c.Hash())) if err == nil { return block, nil @@ -200,11 +199,3 @@ func (s *BlockService) Close() error { log.Debug("blockservice is shutting down...") return s.Exchange.Close() } - -type RawBlockObject struct { - blocks.Block -} - -func (rob *RawBlockObject) Cid() *cid.Cid { - return cid.NewCidV0(rob.Block.Multihash()) -} From d629fb51a49a9e082a321479ede114ebf5502d24 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 8 Sep 2016 12:21:29 -0700 Subject: [PATCH 1471/3817] dht: protect against a panic in case record on pbmessage is nil License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@8298f72590810340ff1f0a0dde68d92f86e8df4e --- routing/dht/dht_test.go | 17 +++++++++++++++-- routing/dht/handlers.go | 10 +++++++--- routing/dht/records.go | 4 ++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 0abc27ed7..3bd8880e0 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -11,14 +11,15 @@ import ( key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" + pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore" peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer" + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" netutil "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/test/util" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" @@ -826,3 +827,15 @@ func TestConnectCollision(t *testing.T) { dhtB.host.Close() } } + +func TestBadProtoMessages(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + d := setupDHT(ctx, t) + + nilrec := new(pb.Message) + if _, err := d.handlePutValue(ctx, "testpeer", nilrec); err == nil { + t.Fatal("should have errored on nil record") + } +} diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index feaf9aea2..edb3d9060 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -150,13 +150,17 @@ func (dht *IpfsDHT) handlePutValue(ctx context.Context, p peer.ID, pmes *pb.Mess defer log.EventBegin(ctx, "handlePutValue", p).Done() dskey := key.Key(pmes.GetKey()).DsKey() - if err := dht.verifyRecordLocally(pmes.GetRecord()); err != nil { + rec := pmes.GetRecord() + if rec == nil { + log.Infof("Got nil record from: %s", p.Pretty()) + return nil, errors.New("nil record") + } + + if err := dht.verifyRecordLocally(rec); err != nil { log.Warningf("Bad dht record in PUT from: %s. %s", key.Key(pmes.GetRecord().GetAuthor()), err) return nil, err } - rec := pmes.GetRecord() - // record the time we receive every record rec.TimeReceived = proto.String(u.FormatRFC3339(time.Now())) diff --git a/routing/dht/records.go b/routing/dht/records.go index d920e9843..571469750 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -107,6 +107,10 @@ func (dht *IpfsDHT) getPublicKeyFromNode(ctx context.Context, p peer.ID) (ci.Pub // verifyRecordLocally attempts to verify a record. if we do not have the public // key, we fail. we do not search the dht. func (dht *IpfsDHT) verifyRecordLocally(r *pb.Record) error { + if r == nil { + log.Error("nil record passed into verifyRecordLocally") + return fmt.Errorf("nil record") + } if len(r.Signature) > 0 { // First, validate the signature From 9cc1041763132edcbcc05f4497fc7ccedee88296 Mon Sep 17 00:00:00 2001 From: George Antoniadis Date: Fri, 9 Sep 2016 15:41:28 +0100 Subject: [PATCH 1472/3817] Extract key and datastore License: MIT Signed-off-by: George Antoniadis This commit was moved from ipfs/go-ipfs-routing@8a1163aba7dee3d822e891cfa4d354a3009156ac --- routing/dht/dht.go | 8 ++++---- routing/dht/dht_bootstrap.go | 4 ++-- routing/dht/dht_test.go | 6 +++--- routing/dht/ext_test.go | 6 +++--- routing/dht/handlers.go | 4 ++-- routing/dht/lookup.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/providers/providers.go | 12 ++++++------ routing/dht/providers/providers_test.go | 4 ++-- routing/dht/query.go | 6 +++--- routing/dht/routing.go | 2 +- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 4 ++-- routing/mock/centralized_server.go | 6 +++--- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 4 ++-- routing/mock/interface.go | 4 ++-- routing/none/none_client.go | 2 +- routing/offline/offline.go | 4 ++-- routing/record/record.go | 2 +- routing/record/selection.go | 2 +- routing/record/validation.go | 2 +- routing/record/validation_test.go | 2 +- routing/routing.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 4 ++-- routing/supernode/server_test.go | 4 ++-- 28 files changed, 53 insertions(+), 53 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 6de362e13..2352bebd0 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -9,22 +9,22 @@ import ( "sync" "time" - key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" providers "github.com/ipfs/go-ipfs/routing/dht/providers" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" - goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" + goprocessctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" host "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/host" protocol "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/protocol" ) diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 46f549ab9..9af9ce142 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -12,8 +12,8 @@ import ( peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" - periodicproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/periodic" + goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" + periodicproc "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/periodic" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 8243d2ed9..8926b6deb 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -9,13 +9,13 @@ import ( "testing" "time" - key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" record "github.com/ipfs/go-ipfs/routing/record" ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index bbfe02538..f04214b47 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -6,12 +6,12 @@ import ( "testing" "time" - key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index b12582a94..97a0c0a1e 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -5,10 +5,10 @@ import ( "fmt" "time" - key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 2a279c89f..6df68a40f 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -1,10 +1,10 @@ package dht import ( - key "github.com/ipfs/go-ipfs/blocks/key" notif "github.com/ipfs/go-ipfs/notifications" kb "github.com/ipfs/go-ipfs/routing/kbucket" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index d7c4dd7d2..297829d60 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -3,10 +3,10 @@ package dht_pb import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - key "github.com/ipfs/go-ipfs/blocks/key" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" ) diff --git a/routing/dht/providers/providers.go b/routing/dht/providers/providers.go index e48aaccef..7bd9dc165 100644 --- a/routing/dht/providers/providers.go +++ b/routing/dht/providers/providers.go @@ -6,18 +6,18 @@ import ( "strings" "time" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - dsq "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/query" - goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" - goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" + goprocessctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" + autobatch "gx/ipfs/QmSp3diFRRv4zR25nHU4MWNCdhT4R6cxrTPLx12MCi1TZb/autobatch" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" base32 "gx/ipfs/Qmb1DA2A9LS2wR4FFweB4uEDomFsdmnw1VLawLE1yQzudj/base32" - autobatch "gx/ipfs/QmcRHLm2aqDabkpcto1NzLad7YQhH99MGDHSWWvwMxKiZw/autobatch" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" - key "github.com/ipfs/go-ipfs/blocks/key" flags "github.com/ipfs/go-ipfs/flags" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/providers/providers_test.go b/routing/dht/providers/providers_test.go index 32ede4469..4b74ee6a9 100644 --- a/routing/dht/providers/providers_test.go +++ b/routing/dht/providers/providers_test.go @@ -5,9 +5,9 @@ import ( "testing" "time" - key "github.com/ipfs/go-ipfs/blocks/key" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/query.go b/routing/dht/query.go index 2b1efb337..723f60ce8 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -3,14 +3,14 @@ package dht import ( "sync" - key "github.com/ipfs/go-ipfs/blocks/key" notif "github.com/ipfs/go-ipfs/notifications" "github.com/ipfs/go-ipfs/routing" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" todoctr "github.com/ipfs/go-ipfs/thirdparty/todocounter" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" - ctxproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + process "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" + ctxproc "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" queue "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore/queue" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" diff --git a/routing/dht/routing.go b/routing/dht/routing.go index abd9c42ff..4e4001406 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -7,13 +7,13 @@ import ( "sync" "time" - key "github.com/ipfs/go-ipfs/blocks/key" notif "github.com/ipfs/go-ipfs/notifications" "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" pset "github.com/ipfs/go-ipfs/thirdparty/peerset" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 58cd3c3e3..0a6da860a 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -5,10 +5,10 @@ import ( "crypto/sha256" "errors" - key "github.com/ipfs/go-ipfs/blocks/key" ks "github.com/ipfs/go-ipfs/routing/keyspace" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) // Returned if a routing table query returns no results. This is NOT expected diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 26437b310..dde11087d 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -4,11 +4,11 @@ import ( "errors" "time" - key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index b7fe52d9a..91ccbb6c1 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -5,10 +5,10 @@ import ( "sync" "time" - key "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 3cd8e388a..4557ea8de 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - key "github.com/ipfs/go-ipfs/blocks/key" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 7e09cdf28..354bc5a9c 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,9 +3,9 @@ package mockrouting import ( dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - sync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" mocknet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net/mock" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index b86e25f06..a816c4793 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -5,14 +5,14 @@ package mockrouting import ( - key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) // Server provides mockrouting Clients diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 2ff9fa68e..088d8b961 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -3,13 +3,13 @@ package nilrouting import ( "errors" - key "github.com/ipfs/go-ipfs/blocks/key" repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" p2phost "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/host" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 855f35191..c853e50f9 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -4,11 +4,11 @@ import ( "errors" "time" - key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" diff --git a/routing/record/record.go b/routing/record/record.go index 316763f7f..59b66f68b 100644 --- a/routing/record/record.go +++ b/routing/record/record.go @@ -5,10 +5,10 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/routing/dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) var log = logging.Logger("routing/record") diff --git a/routing/record/selection.go b/routing/record/selection.go index 8e68006c1..5b1f5bb98 100644 --- a/routing/record/selection.go +++ b/routing/record/selection.go @@ -3,8 +3,8 @@ package record import ( "errors" - key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) // A SelectorFunc selects the best value for the given key from diff --git a/routing/record/validation.go b/routing/record/validation.go index a17e36fad..65e181fda 100644 --- a/routing/record/validation.go +++ b/routing/record/validation.go @@ -5,12 +5,12 @@ import ( "errors" "fmt" - key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" pb "github.com/ipfs/go-ipfs/routing/dht/pb" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) // ValidatorFunc is a function that is called to validate a given diff --git a/routing/record/validation_test.go b/routing/record/validation_test.go index 56bf6a842..175f902d8 100644 --- a/routing/record/validation_test.go +++ b/routing/record/validation_test.go @@ -4,8 +4,8 @@ import ( "encoding/base64" "testing" - key "github.com/ipfs/go-ipfs/blocks/key" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) var OffensiveKey = "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDjXAQQMal4SB2tSnX6NJIPmC69/BT8A8jc7/gDUZNkEhdhYHvc7k7S4vntV/c92nJGxNdop9fKJyevuNMuXhhHAgMBAAE=" diff --git a/routing/routing.go b/routing/routing.go index ad12981f2..56671a7c9 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -4,11 +4,11 @@ package routing import ( "errors" - key "github.com/ipfs/go-ipfs/blocks/key" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) // ErrNotFound is returned when a search fails to find anything diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 17839f3aa..929c1497b 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -5,11 +5,11 @@ import ( "errors" "time" - key "github.com/ipfs/go-ipfs/blocks/key" routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" loggables "github.com/ipfs/go-ipfs/thirdparty/loggables" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 693fe6bc9..506514de6 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -12,10 +12,10 @@ import ( host "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/host" inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" - key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" loggables "github.com/ipfs/go-ipfs/thirdparty/loggables" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index f34c5eb2b..edd451cbb 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -4,11 +4,11 @@ import ( "errors" "fmt" - key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - datastore "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 25c54ec32..4cbc7de6f 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,9 +3,9 @@ package supernode import ( "testing" - key "github.com/ipfs/go-ipfs/blocks/key" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - datastore "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" + datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 09f8aa4b5bed23a824a8b0ce5b5eb499434ddd54 Mon Sep 17 00:00:00 2001 From: George Antoniadis Date: Fri, 9 Sep 2016 15:41:28 +0100 Subject: [PATCH 1473/3817] Extract key and datastore License: MIT Signed-off-by: George Antoniadis This commit was moved from ipfs/go-namesys@f8ba607b41c53f8765c5c940298f7500c265c133 --- namesys/namesys.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 8 ++++---- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 6 +++--- namesys/routing.go | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 699cc5327..87c1854cd 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,9 +6,9 @@ import ( path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/publisher.go b/namesys/publisher.go index 61fa8d6d0..02eff0415 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -6,11 +6,10 @@ import ( "fmt" "time" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" @@ -22,6 +21,7 @@ import ( ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 91228ea52..5b03d04bd 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -5,21 +5,21 @@ import ( "sync" "time" - key "github.com/ipfs/go-ipfs/blocks/key" namesys "github.com/ipfs/go-ipfs/namesys" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/routing" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" - gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" + gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index be54999eb..727e4c22e 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" + goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" "github.com/ipfs/go-ipfs/core" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 57e08ccdb..e8e1e74b5 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" - key "github.com/ipfs/go-ipfs/blocks/key" path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index b4eea2af9..31c863fce 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -10,10 +10,10 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - key "github.com/ipfs/go-ipfs/blocks/key" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" routing "github.com/ipfs/go-ipfs/routing" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" From a0b7187371952f45c614f91c5c2aab5165dcc4d7 Mon Sep 17 00:00:00 2001 From: George Antoniadis Date: Fri, 9 Sep 2016 15:41:28 +0100 Subject: [PATCH 1474/3817] Extract key and datastore License: MIT Signed-off-by: George Antoniadis This commit was moved from ipfs/go-unixfs@c5e8eacaffe660d1847b365a4256f7c8bfdb1fb4 --- unixfs/mod/dagmodifier_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 815ac5fc0..6ea3c31f0 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -18,10 +18,10 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) func getMockDagServ(t testing.TB) mdag.DAGService { From ad971cd97cedad16dfd933a823946439a36e5cda Mon Sep 17 00:00:00 2001 From: George Antoniadis Date: Fri, 9 Sep 2016 15:41:28 +0100 Subject: [PATCH 1475/3817] Extract key and datastore License: MIT Signed-off-by: George Antoniadis This commit was moved from ipfs/go-ipfs-pinner@e31be8b47018a9851f787dae99160cfbcbe855d3 --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/indirect.go | 2 +- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index c1e2eb471..3e35c2b27 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -2,11 +2,11 @@ package gc import ( bstore "github.com/ipfs/go-ipfs/blocks/blockstore" - key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index 22e3a1fb4..a837d60fd 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -1,7 +1,7 @@ package pin import ( - key "github.com/ipfs/go-ipfs/blocks/key" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) type indirectPin struct { diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 56979cc69..3e85b894a 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -8,12 +8,12 @@ import ( "sync" "time" - key "github.com/ipfs/go-ipfs/blocks/key" mdag "github.com/ipfs/go-ipfs/merkledag" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index f1f626f54..af3aa08da 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -9,10 +9,10 @@ import ( "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index eb5cb5d91..acb154e77 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,11 +10,11 @@ import ( "sort" "unsafe" - "github.com/ipfs/go-ipfs/blocks/key" "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index a5e9152d4..a71cd0db6 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -1,6 +1,6 @@ package pin -import "github.com/ipfs/go-ipfs/blocks/key" +import "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" func ignoreKeys(key.Key) {} From c71be488012b4f9219a493aaa7534d8dd490f509 Mon Sep 17 00:00:00 2001 From: George Antoniadis Date: Fri, 9 Sep 2016 15:41:28 +0100 Subject: [PATCH 1476/3817] Extract key and datastore License: MIT Signed-off-by: George Antoniadis This commit was moved from ipfs/go-ipfs-blockstore@34307018286e3f59845ff9c361bb46de92ddc920 --- blockstore/arc_cache.go | 4 ++-- blockstore/arc_cache_test.go | 6 +++--- blockstore/blockstore.go | 8 ++++---- blockstore/blockstore_test.go | 8 ++++---- blockstore/bloom_cache.go | 2 +- blockstore/bloom_cache_test.go | 6 +++--- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 10ef8b01b..da50bd470 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -2,11 +2,11 @@ package blockstore import ( "github.com/ipfs/go-ipfs/blocks" - key "github.com/ipfs/go-ipfs/blocks/key" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) type arccache struct { diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index ac61496d2..a8c2227b8 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -4,11 +4,11 @@ import ( "testing" "github.com/ipfs/go-ipfs/blocks" - "github.com/ipfs/go-ipfs/blocks/key" + "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - syncds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) var exampleBlock = blocks.NewBlock([]byte("foo")) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 993d5c682..45d27d1b5 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -8,13 +8,13 @@ import ( "sync/atomic" blocks "github.com/ipfs/go-ipfs/blocks" - key "github.com/ipfs/go-ipfs/blocks/key" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - dsns "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/namespace" - dsq "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/query" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dsns "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/namespace" + dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 64db91dfe..096bf34cd 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -5,14 +5,14 @@ import ( "fmt" "testing" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - dsq "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/query" - ds_sync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" + ds_sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" blocks "github.com/ipfs/go-ipfs/blocks" - key "github.com/ipfs/go-ipfs/blocks/key" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) func TestGetWhenKeyNotPresent(t *testing.T) { diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index b064b77db..af8422d0f 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -4,7 +4,7 @@ import ( "sync/atomic" "github.com/ipfs/go-ipfs/blocks" - key "github.com/ipfs/go-ipfs/blocks/key" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" bloom "gx/ipfs/QmWQ2SJisXwcCLsUXLwYCKSfyExXjFRW2WbBH5sqCUnwX5/bbloom" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index d9d23341a..4ce2d0152 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -8,10 +8,10 @@ import ( "github.com/ipfs/go-ipfs/blocks" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - dsq "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/query" - syncds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" + syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) func testBloomCached(bs GCBlockstore, ctx context.Context) (*bloomcache, error) { From 08e686094dae7ddf2923b30b7229520586a1c36d Mon Sep 17 00:00:00 2001 From: George Antoniadis Date: Fri, 9 Sep 2016 15:41:28 +0100 Subject: [PATCH 1477/3817] Extract key and datastore License: MIT Signed-off-by: George Antoniadis This commit was moved from ipfs/go-merkledag@1f621d0a12b9ccf455b795adf4936f1510da5b39 --- ipld/merkledag/merkledag.go | 2 +- ipld/merkledag/merkledag_test.go | 2 +- ipld/merkledag/node.go | 2 +- ipld/merkledag/test/utils.go | 4 ++-- ipld/merkledag/utils/utils.go | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index b0efec855..d4a8403a3 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -6,8 +6,8 @@ import ( "strings" "sync" - key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 7f71f7c2e..336a81caa 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -10,7 +10,6 @@ import ( "sync" "testing" - key "github.com/ipfs/go-ipfs/blocks/key" bserv "github.com/ipfs/go-ipfs/blockservice" bstest "github.com/ipfs/go-ipfs/blockservice/test" offline "github.com/ipfs/go-ipfs/exchange/offline" @@ -20,6 +19,7 @@ import ( mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index b3add5f37..138828416 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -5,8 +5,8 @@ import ( "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - key "github.com/ipfs/go-ipfs/blocks/key" mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index 59bbd4979..1475a6f84 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -5,8 +5,8 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) func Mock() dag.DAGService { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index a1e4125f7..5f795006b 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -3,9 +3,9 @@ package dagutils import ( "errors" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - syncds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" bserv "github.com/ipfs/go-ipfs/blockservice" From cd68af7f789f5d3a6a8a098055c4ffa8d5eec1ee Mon Sep 17 00:00:00 2001 From: George Antoniadis Date: Fri, 9 Sep 2016 15:41:28 +0100 Subject: [PATCH 1478/3817] Extract key and datastore License: MIT Signed-off-by: George Antoniadis This commit was moved from ipfs/go-ipfs-exchange-interface@27247009d786225eb9aca2e4c39f0e78c9f1a4f4 --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index 6f246ebc0..4b40d7390 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -5,7 +5,7 @@ import ( "io" blocks "github.com/ipfs/go-ipfs/blocks" - key "github.com/ipfs/go-ipfs/blocks/key" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From 3220e777e3d13503872ead6e057ce44ec9ee8547 Mon Sep 17 00:00:00 2001 From: George Antoniadis Date: Fri, 9 Sep 2016 15:41:28 +0100 Subject: [PATCH 1479/3817] Extract key and datastore License: MIT Signed-off-by: George Antoniadis This commit was moved from ipfs/go-ipfs-exchange-offline@d544161c2652c36bd3447716b2c976aa143a78bf --- exchange/offline/offline.go | 2 +- exchange/offline/offline_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index e36d59a67..b1a6ecb97 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -5,8 +5,8 @@ package offline import ( blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" - key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 6f1687586..8eaf75144 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -6,10 +6,10 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" - key "github.com/ipfs/go-ipfs/blocks/key" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - ds_sync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + ds_sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) func TestBlockReturnsErr(t *testing.T) { From 9ac0ee3bce2d3db8f5735a6f54c04e5f761c8816 Mon Sep 17 00:00:00 2001 From: George Antoniadis Date: Fri, 9 Sep 2016 15:41:28 +0100 Subject: [PATCH 1480/3817] Extract key and datastore License: MIT Signed-off-by: George Antoniadis This commit was moved from ipfs/go-mfs@f628ad93956e259741a874dc6115879f1567ee0a --- mfs/mfs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 13da0358e..261ec76e1 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -23,10 +23,10 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) From c709d5f23cb796b7f574188d64e8b7c35a305715 Mon Sep 17 00:00:00 2001 From: George Antoniadis Date: Fri, 9 Sep 2016 15:41:28 +0100 Subject: [PATCH 1481/3817] Extract key and datastore License: MIT Signed-off-by: George Antoniadis This commit was moved from ipfs/go-blockservice@3817dda309bd891f7220c11e27d133d43c34d5b3 --- blockservice/blockservice.go | 2 +- blockservice/test/blocks_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index aeea822bf..840fa606d 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -9,8 +9,8 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" - key "github.com/ipfs/go-ipfs/blocks/key" exchange "github.com/ipfs/go-ipfs/exchange" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index a64264dab..ba67f9f3d 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -8,14 +8,14 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" - key "github.com/ipfs/go-ipfs/blocks/key" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - ds "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore" - dssync "gx/ipfs/QmNgqJarToRiq2GBaPJhkmW4B5BxS5B74E1rkGvv2JoaTp/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) From ff94c0b372b08d50c52e829f3dce2c62abece8b3 Mon Sep 17 00:00:00 2001 From: George Antoniadis Date: Fri, 9 Sep 2016 15:41:28 +0100 Subject: [PATCH 1482/3817] Extract key and datastore License: MIT Signed-off-by: George Antoniadis This commit was moved from ipfs/go-ipfs-chunker@a9d6959ec26edcdafdf27fc1fe5cdef6b3b3d7f5 --- chunker/rabin_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 9b9cfce8f..99b1bad58 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -4,8 +4,8 @@ import ( "bytes" "fmt" "github.com/ipfs/go-ipfs/blocks" - "github.com/ipfs/go-ipfs/blocks/key" "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" "io" "testing" ) From dfc3375c19832e94b205059ebdb17d299a150e30 Mon Sep 17 00:00:00 2001 From: George Antoniadis Date: Fri, 9 Sep 2016 15:41:28 +0100 Subject: [PATCH 1483/3817] Extract key and datastore License: MIT Signed-off-by: George Antoniadis This commit was moved from ipfs/go-path@657dfd3a32c2f89e23e2ec96e6e6e18d81da12e6 --- path/resolver_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/resolver_test.go b/path/resolver_test.go index 3a45581ed..0d26ff48e 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -6,11 +6,11 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - key "github.com/ipfs/go-ipfs/blocks/key" merkledag "github.com/ipfs/go-ipfs/merkledag" dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" util "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) func randNode() (*merkledag.Node, key.Key) { From b41609915c110c457ae81959df7d0356defccc45 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 10 Sep 2016 12:57:12 -0700 Subject: [PATCH 1484/3817] dht: add missing protocol ID to newStream call License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@9e32f1a2ad8fcfe879dfcd4cc177ea6922b602d3 --- routing/dht/dht_net.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index 92819b437..a6ae6f7f2 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -141,7 +141,7 @@ func (ms *messageSender) prep() error { return nil } - nstr, err := ms.dht.host.NewStream(ms.dht.ctx, ms.p, ProtocolDHT) + nstr, err := ms.dht.host.NewStream(ms.dht.ctx, ms.p, ProtocolDHT, ProtocolDHTOld) if err != nil { return err } From 55e82160f833b7f4c7313652b0093f2348c53430 Mon Sep 17 00:00:00 2001 From: George Antoniadis Date: Sat, 10 Sep 2016 23:00:05 +0100 Subject: [PATCH 1485/3817] Extract thirdparty/loggables License: MIT Signed-off-by: George Antoniadis This commit was moved from ipfs/go-ipfs-routing@54f51ae6047277f893181a4da1501563acd486e8 --- routing/dht/handlers.go | 5 +++-- routing/supernode/client.go | 5 +++-- routing/supernode/proxy/standard.go | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 97a0c0a1e..2282dfcec 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -5,11 +5,12 @@ import ( "fmt" "time" - pb "github.com/ipfs/go-ipfs/routing/dht/pb" - lgbl "github.com/ipfs/go-ipfs/thirdparty/loggables" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + pb "github.com/ipfs/go-ipfs/routing/dht/pb" + lgbl "gx/ipfs/QmYrv4LgCC8FhG2Ab4bwuq5DqBdwMtx3hMb3KKJDZcr2d7/go-libp2p-loggables" + pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 929c1497b..eb1392f19 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -5,11 +5,12 @@ import ( "errors" "time" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + routing "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - loggables "github.com/ipfs/go-ipfs/thirdparty/loggables" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + loggables "gx/ipfs/QmYrv4LgCC8FhG2Ab4bwuq5DqBdwMtx3hMb3KKJDZcr2d7/go-libp2p-loggables" pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 506514de6..d1f0fafeb 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -12,10 +12,11 @@ import ( host "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/host" inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" kbucket "github.com/ipfs/go-ipfs/routing/kbucket" - loggables "github.com/ipfs/go-ipfs/thirdparty/loggables" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + loggables "gx/ipfs/QmYrv4LgCC8FhG2Ab4bwuq5DqBdwMtx3hMb3KKJDZcr2d7/go-libp2p-loggables" ) const ProtocolSNR = "/ipfs/supernoderouting" From 1f844b0af1a2b378540b03832a12f13288156e49 Mon Sep 17 00:00:00 2001 From: George Antoniadis Date: Sat, 10 Sep 2016 23:22:17 +0100 Subject: [PATCH 1486/3817] Extract peerset, update peer, peerset, secio, libp2p License: MIT Signed-off-by: George Antoniadis This commit was moved from ipfs/go-ipfs-routing@6b3304648211e725e264fd88af904d80f8a0aabc --- routing/dht/dht.go | 8 ++++---- routing/dht/dht_bootstrap.go | 2 +- routing/dht/dht_net.go | 4 ++-- routing/dht/dht_test.go | 6 +++--- routing/dht/ext_test.go | 6 +++--- routing/dht/handlers.go | 4 ++-- routing/dht/lookup.go | 9 +++++---- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 6 +++--- routing/dht/providers/providers.go | 2 +- routing/dht/providers/providers_test.go | 2 +- routing/dht/query.go | 11 ++++++----- routing/dht/records.go | 2 +- routing/dht/routing.go | 11 ++++++----- routing/kbucket/bucket.go | 2 +- routing/kbucket/sorting.go | 2 +- routing/kbucket/table.go | 4 ++-- routing/kbucket/table_test.go | 4 ++-- routing/kbucket/util.go | 2 +- routing/mock/centralized_client.go | 4 ++-- routing/mock/centralized_server.go | 4 ++-- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 4 ++-- routing/none/none_client.go | 6 +++--- routing/offline/offline.go | 4 ++-- routing/routing.go | 4 ++-- routing/supernode/client.go | 6 +++--- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 8 ++++---- routing/supernode/server.go | 4 ++-- 31 files changed, 72 insertions(+), 69 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 43a4a154e..60d87368b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -18,15 +18,15 @@ import ( goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" goprocessctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + host "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/host" + protocol "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/protocol" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - host "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/host" - protocol "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/protocol" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) var log = logging.Logger("dht") diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go index 9af9ce142..366939ae2 100644 --- a/routing/dht/dht_bootstrap.go +++ b/routing/dht/dht_bootstrap.go @@ -9,7 +9,7 @@ import ( "time" routing "github.com/ipfs/go-ipfs/routing" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index a6ae6f7f2..d9bfc4007 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,11 +6,11 @@ import ( "time" pb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" ctxio "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/io" + inet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" ) var dhtReadMessageTimeout = time.Minute diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 8926b6deb..265612cec 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -17,12 +17,12 @@ import ( dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + netutil "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/test/util" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - netutil "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/test/util" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) var testCaseValues = map[key.Key][]byte{} diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index f04214b47..6f0c6b45e 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -13,12 +13,12 @@ import ( dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" + inet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net/mock" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" - mocknet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) func TestGetFailures(t *testing.T) { diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go index 2282dfcec..ea539a95d 100644 --- a/routing/dht/handlers.go +++ b/routing/dht/handlers.go @@ -11,11 +11,11 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" lgbl "gx/ipfs/QmYrv4LgCC8FhG2Ab4bwuq5DqBdwMtx3hMb3KKJDZcr2d7/go-libp2p-loggables" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) // The number of closer peers to send on requests. diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go index 6df68a40f..2ff6306cc 100644 --- a/routing/dht/lookup.go +++ b/routing/dht/lookup.go @@ -1,14 +1,15 @@ package dht import ( + pset "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer/peerset" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + notif "github.com/ipfs/go-ipfs/notifications" kb "github.com/ipfs/go-ipfs/routing/kbucket" - pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) // Required in order for proper JSON marshaling diff --git a/routing/dht/notif.go b/routing/dht/notif.go index 4a55724bf..e123c15b7 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" + inet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index 297829d60..a9bc57179 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -3,11 +3,11 @@ package dht_pb import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + inet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) var log = logging.Logger("dht.pb") diff --git a/routing/dht/providers/providers.go b/routing/dht/providers/providers.go index 7bd9dc165..a34fec6bb 100644 --- a/routing/dht/providers/providers.go +++ b/routing/dht/providers/providers.go @@ -11,7 +11,7 @@ import ( autobatch "gx/ipfs/QmSp3diFRRv4zR25nHU4MWNCdhT4R6cxrTPLx12MCi1TZb/autobatch" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" base32 "gx/ipfs/Qmb1DA2A9LS2wR4FFweB4uEDomFsdmnw1VLawLE1yQzudj/base32" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" diff --git a/routing/dht/providers/providers_test.go b/routing/dht/providers/providers_test.go index 4b74ee6a9..01cee7b73 100644 --- a/routing/dht/providers/providers_test.go +++ b/routing/dht/providers/providers_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" diff --git a/routing/dht/query.go b/routing/dht/query.go index 723f60ce8..d17e00e2d 100644 --- a/routing/dht/query.go +++ b/routing/dht/query.go @@ -3,20 +3,21 @@ package dht import ( "sync" + pset "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer/peerset" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + notif "github.com/ipfs/go-ipfs/notifications" "github.com/ipfs/go-ipfs/routing" - pset "github.com/ipfs/go-ipfs/thirdparty/peerset" todoctr "github.com/ipfs/go-ipfs/thirdparty/todocounter" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" process "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" ctxproc "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" - queue "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore/queue" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" + queue "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore/queue" ) var maxQueryConcurrency = AlphaValue diff --git a/routing/dht/records.go b/routing/dht/records.go index 0b461382a..af7169861 100644 --- a/routing/dht/records.go +++ b/routing/dht/records.go @@ -8,7 +8,7 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" record "github.com/ipfs/go-ipfs/routing/record" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" ctxfrac "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/frac" "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 4e4001406..16a497c61 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -7,18 +7,19 @@ import ( "sync" "time" + pset "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer/peerset" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + notif "github.com/ipfs/go-ipfs/notifications" "github.com/ipfs/go-ipfs/routing" pb "github.com/ipfs/go-ipfs/routing/dht/pb" kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" - pset "github.com/ipfs/go-ipfs/thirdparty/peerset" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + inet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) // asyncQueryBuffer is the size of buffered channels in async queries. This diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go index 171436279..d280d9140 100644 --- a/routing/kbucket/bucket.go +++ b/routing/kbucket/bucket.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" ) // Bucket holds a list of peers. diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go index 19ea84f68..f662640f2 100644 --- a/routing/kbucket/sorting.go +++ b/routing/kbucket/sorting.go @@ -2,7 +2,7 @@ package kbucket import ( "container/list" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" "sort" ) diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go index 47a3228ca..6c4827a32 100644 --- a/routing/kbucket/table.go +++ b/routing/kbucket/table.go @@ -7,9 +7,9 @@ import ( "sync" "time" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) var log = logging.Logger("table") diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go index 115fd34ea..fb34d9976 100644 --- a/routing/kbucket/table_test.go +++ b/routing/kbucket/table_test.go @@ -7,8 +7,8 @@ import ( tu "github.com/ipfs/go-ipfs/thirdparty/testutil" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) // Test basic features of the bucket struct diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go index 0a6da860a..2722540d6 100644 --- a/routing/kbucket/util.go +++ b/routing/kbucket/util.go @@ -6,7 +6,7 @@ import ( "errors" ks "github.com/ipfs/go-ipfs/routing/keyspace" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index dde11087d..d6f921845 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -10,13 +10,13 @@ import ( ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 91ccbb6c1..b8e95762b 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -10,9 +10,9 @@ import ( dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 4557ea8de..c66085e62 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,8 +8,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 354bc5a9c..d680cc15b 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" + mocknet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - mocknet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index a816c4793..b0c1b14a1 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -8,11 +8,11 @@ import ( routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) // Server provides mockrouting Clients diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 088d8b961..571abb0ac 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -5,12 +5,12 @@ import ( repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + p2phost "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/host" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - p2phost "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/host" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index c853e50f9..66564d0ef 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -10,12 +10,12 @@ import ( ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) var log = logging.Logger("offlinerouting") diff --git a/routing/routing.go b/routing/routing.go index 56671a7c9..6e15bed6f 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -4,11 +4,11 @@ package routing import ( "errors" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) // ErrNotFound is returned when a search fails to find anything diff --git a/routing/supernode/client.go b/routing/supernode/client.go index eb1392f19..3674bc29d 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -12,12 +12,12 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" loggables "gx/ipfs/QmYrv4LgCC8FhG2Ab4bwuq5DqBdwMtx3hMb3KKJDZcr2d7/go-libp2p-loggables" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/host" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/host" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 379527f07..b3123b00a 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" - inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + inet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index d1f0fafeb..06bfdb650 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -6,11 +6,11 @@ import ( ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" - host "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/host" - inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + host "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/host" + inet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index edd451cbb..d3473d12d 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -10,10 +10,10 @@ import ( datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) // Server handles routing queries using a database backend From 080d85f95a56617345874e7da59875d5070b3dee Mon Sep 17 00:00:00 2001 From: George Antoniadis Date: Sat, 10 Sep 2016 23:22:17 +0100 Subject: [PATCH 1487/3817] Extract peerset, update peer, peerset, secio, libp2p License: MIT Signed-off-by: George Antoniadis This commit was moved from ipfs/go-namesys@33429916d8a779bb07efbbe0ba17eb61c1273d16 --- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 4 ++-- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 02eff0415..4e634cef0 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -19,7 +19,7 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 5b03d04bd..6b9174953 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -14,12 +14,12 @@ import ( goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 727e4c22e..51f002f07 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - pstore "gx/ipfs/QmSZi9ygLohBUGyHMqE5N6eToPwqcg7bZQTULeVLFu7Q6d/go-libp2p-peerstore" - mocknet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index e8e1e74b5..d7fbdf6ca 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" From 99c970f0123f37984cadfc3cf703b18841919fa3 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 18 Aug 2016 21:09:56 +0200 Subject: [PATCH 1488/3817] test: add basic dagreader test License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@9698163d2a97e5a8546ac9d3b864f5a58404e32d --- unixfs/io/dagserv_test.go | 90 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 unixfs/io/dagserv_test.go diff --git a/unixfs/io/dagserv_test.go b/unixfs/io/dagserv_test.go new file mode 100644 index 000000000..74da2152a --- /dev/null +++ b/unixfs/io/dagserv_test.go @@ -0,0 +1,90 @@ +package io + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "testing" + + "github.com/ipfs/go-ipfs/blocks/blockstore" + bs "github.com/ipfs/go-ipfs/blockservice" + "github.com/ipfs/go-ipfs/exchange/offline" + imp "github.com/ipfs/go-ipfs/importer" + "github.com/ipfs/go-ipfs/importer/chunk" + mdag "github.com/ipfs/go-ipfs/merkledag" + + ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" + "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" +) + +func getMockDagServ(t testing.TB) mdag.DAGService { + dstore := ds.NewMapDatastore() + tsds := sync.MutexWrap(dstore) + bstore := blockstore.NewBlockstore(tsds) + bserv := bs.New(bstore, offline.Exchange(bstore)) + return mdag.NewDAGService(bserv) +} + +func sizeSplitterGen(size int64) chunk.SplitterGen { + return func(r io.Reader) chunk.Splitter { + return chunk.NewSizeSplitter(r, size) + } +} + +func getNode(t testing.TB, dserv mdag.DAGService, data []byte) *mdag.Node { + in := bytes.NewReader(data) + node, err := imp.BuildTrickleDagFromReader(dserv, sizeSplitterGen(500)(in)) + if err != nil { + t.Fatal(err) + } + + return node +} + +func getRandomNode(t testing.TB, dserv mdag.DAGService, size int64) ([]byte, *mdag.Node) { + in := io.LimitReader(u.NewTimeSeededRand(), size) + buf, err := ioutil.ReadAll(in) + if err != nil { + t.Fatal(err) + } + + node := getNode(t, dserv, buf) + return buf, node +} + +func arrComp(a, b []byte) error { + if len(a) != len(b) { + return fmt.Errorf("Arrays differ in length. %d != %d", len(a), len(b)) + } + for i, v := range a { + if v != b[i] { + return fmt.Errorf("Arrays differ at index: %d", i) + } + } + return nil +} + +func TestBasicRead(t *testing.T) { + dserv := getMockDagServ(t) + inbuf, node := getRandomNode(t, dserv, 1024) + ctx, closer := context.WithCancel(context.Background()) + defer closer() + + reader, err := NewDagReader(ctx, node, dserv) + if err != nil { + t.Fatal(err) + } + + outbuf, err := ioutil.ReadAll(reader) + if err != nil { + t.Fatal(err) + } + + err = arrComp(inbuf, outbuf) + if err != nil { + t.Fatal(err) + } +} From 16eb08cc43761ca088a37c97f0454c4d7e0647ef Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 18 Aug 2016 21:33:59 +0200 Subject: [PATCH 1489/3817] test: use mdag/test.Mock() instead License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@ed88094808e1c6c440b2a2e1bbb976cc0354d3e2 --- unixfs/io/dagserv_test.go | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/unixfs/io/dagserv_test.go b/unixfs/io/dagserv_test.go index 74da2152a..23d01786e 100644 --- a/unixfs/io/dagserv_test.go +++ b/unixfs/io/dagserv_test.go @@ -7,27 +7,15 @@ import ( "io/ioutil" "testing" - "github.com/ipfs/go-ipfs/blocks/blockstore" - bs "github.com/ipfs/go-ipfs/blockservice" - "github.com/ipfs/go-ipfs/exchange/offline" imp "github.com/ipfs/go-ipfs/importer" "github.com/ipfs/go-ipfs/importer/chunk" mdag "github.com/ipfs/go-ipfs/merkledag" + mdagmock "github.com/ipfs/go-ipfs/merkledag/test" - ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) -func getMockDagServ(t testing.TB) mdag.DAGService { - dstore := ds.NewMapDatastore() - tsds := sync.MutexWrap(dstore) - bstore := blockstore.NewBlockstore(tsds) - bserv := bs.New(bstore, offline.Exchange(bstore)) - return mdag.NewDAGService(bserv) -} - func sizeSplitterGen(size int64) chunk.SplitterGen { return func(r io.Reader) chunk.Splitter { return chunk.NewSizeSplitter(r, size) @@ -68,7 +56,7 @@ func arrComp(a, b []byte) error { } func TestBasicRead(t *testing.T) { - dserv := getMockDagServ(t) + dserv := mdagmock.Mock() inbuf, node := getRandomNode(t, dserv, 1024) ctx, closer := context.WithCancel(context.Background()) defer closer() From 740684102363c9d25559fe9ee4bf82892578be26 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 18 Aug 2016 22:34:28 +0200 Subject: [PATCH 1490/3817] test: refactor some utities out of mod package License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@7bd8990644c8d68a03e29553904bea96e1ec3288 --- unixfs/io/dagserv_test.go | 56 +--------- unixfs/mod/dagmodifier_test.go | 189 ++++++++++----------------------- unixfs/test/utils.go | 93 ++++++++++++++++ 3 files changed, 154 insertions(+), 184 deletions(-) create mode 100644 unixfs/test/utils.go diff --git a/unixfs/io/dagserv_test.go b/unixfs/io/dagserv_test.go index 23d01786e..fca849924 100644 --- a/unixfs/io/dagserv_test.go +++ b/unixfs/io/dagserv_test.go @@ -1,63 +1,17 @@ package io import ( - "bytes" - "fmt" - "io" "io/ioutil" "testing" - imp "github.com/ipfs/go-ipfs/importer" - "github.com/ipfs/go-ipfs/importer/chunk" - mdag "github.com/ipfs/go-ipfs/merkledag" - mdagmock "github.com/ipfs/go-ipfs/merkledag/test" - - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" -) - -func sizeSplitterGen(size int64) chunk.SplitterGen { - return func(r io.Reader) chunk.Splitter { - return chunk.NewSizeSplitter(r, size) - } -} - -func getNode(t testing.TB, dserv mdag.DAGService, data []byte) *mdag.Node { - in := bytes.NewReader(data) - node, err := imp.BuildTrickleDagFromReader(dserv, sizeSplitterGen(500)(in)) - if err != nil { - t.Fatal(err) - } - - return node -} -func getRandomNode(t testing.TB, dserv mdag.DAGService, size int64) ([]byte, *mdag.Node) { - in := io.LimitReader(u.NewTimeSeededRand(), size) - buf, err := ioutil.ReadAll(in) - if err != nil { - t.Fatal(err) - } - - node := getNode(t, dserv, buf) - return buf, node -} - -func arrComp(a, b []byte) error { - if len(a) != len(b) { - return fmt.Errorf("Arrays differ in length. %d != %d", len(a), len(b)) - } - for i, v := range a { - if v != b[i] { - return fmt.Errorf("Arrays differ at index: %d", i) - } - } - return nil -} + testu "github.com/ipfs/go-ipfs/unixfs/test" +) func TestBasicRead(t *testing.T) { - dserv := mdagmock.Mock() - inbuf, node := getRandomNode(t, dserv, 1024) + dserv := testu.GetDAGServ() + inbuf, node := testu.GetRandomNode(t, dserv, 1024) ctx, closer := context.WithCancel(context.Background()) defer closer() @@ -71,7 +25,7 @@ func TestBasicRead(t *testing.T) { t.Fatal(err) } - err = arrComp(inbuf, outbuf) + err = testu.ArrComp(inbuf, outbuf) if err != nil { t.Fatal(err) } diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 6ea3c31f0..56a2f922f 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -2,7 +2,6 @@ package mod import ( "fmt" - "io" "io/ioutil" "os" "testing" @@ -10,13 +9,12 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" bs "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" - imp "github.com/ipfs/go-ipfs/importer" - "github.com/ipfs/go-ipfs/importer/chunk" h "github.com/ipfs/go-ipfs/importer/helpers" trickle "github.com/ipfs/go-ipfs/importer/trickle" mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" + testu "github.com/ipfs/go-ipfs/unixfs/test" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" @@ -24,14 +22,6 @@ import ( "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) -func getMockDagServ(t testing.TB) mdag.DAGService { - dstore := ds.NewMapDatastore() - tsds := sync.MutexWrap(dstore) - bstore := blockstore.NewBlockstore(tsds) - bserv := bs.New(bstore, offline.Exchange(bstore)) - return mdag.NewDAGService(bserv) -} - func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.GCBlockstore) { dstore := ds.NewMapDatastore() tsds := sync.MutexWrap(dstore) @@ -41,26 +31,6 @@ func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.GCBlocks return dserv, bstore } -func getNode(t testing.TB, dserv mdag.DAGService, size int64) ([]byte, *mdag.Node) { - in := io.LimitReader(u.NewTimeSeededRand(), size) - node, err := imp.BuildTrickleDagFromReader(dserv, sizeSplitterGen(500)(in)) - if err != nil { - t.Fatal(err) - } - - dr, err := uio.NewDagReader(context.Background(), node, dserv) - if err != nil { - t.Fatal(err) - } - - b, err := ioutil.ReadAll(dr) - if err != nil { - t.Fatal(err) - } - - return b, node -} - func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) []byte { newdata := make([]byte, size) r := u.NewTimeSeededRand() @@ -100,26 +70,20 @@ func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) t.Fatal(err) } - err = arrComp(after, orig) + err = testu.ArrComp(after, orig) if err != nil { t.Fatal(err) } return orig } -func sizeSplitterGen(size int64) chunk.SplitterGen { - return func(r io.Reader) chunk.Splitter { - return chunk.NewSizeSplitter(r, size) - } -} - func TestDagModifierBasic(t *testing.T) { - dserv := getMockDagServ(t) - b, n := getNode(t, dserv, 50000) + dserv := testu.GetDAGServ() + b, n := testu.GetRandomNode(t, dserv, 50000) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -168,13 +132,13 @@ func TestDagModifierBasic(t *testing.T) { } func TestMultiWrite(t *testing.T) { - dserv := getMockDagServ(t) - _, n := getNode(t, dserv, 0) + dserv := testu.GetDAGServ() + n := testu.GetEmptyNode(t, dserv) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -214,20 +178,20 @@ func TestMultiWrite(t *testing.T) { t.Fatal(err) } - err = arrComp(rbuf, data) + err = testu.ArrComp(rbuf, data) if err != nil { t.Fatal(err) } } func TestMultiWriteAndFlush(t *testing.T) { - dserv := getMockDagServ(t) - _, n := getNode(t, dserv, 0) + dserv := testu.GetDAGServ() + n := testu.GetEmptyNode(t, dserv) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -262,20 +226,20 @@ func TestMultiWriteAndFlush(t *testing.T) { t.Fatal(err) } - err = arrComp(rbuf, data) + err = testu.ArrComp(rbuf, data) if err != nil { t.Fatal(err) } } func TestWriteNewFile(t *testing.T) { - dserv := getMockDagServ(t) - _, n := getNode(t, dserv, 0) + dserv := testu.GetDAGServ() + n := testu.GetEmptyNode(t, dserv) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -306,19 +270,19 @@ func TestWriteNewFile(t *testing.T) { t.Fatal(err) } - if err := arrComp(data, towrite); err != nil { + if err := testu.ArrComp(data, towrite); err != nil { t.Fatal(err) } } func TestMultiWriteCoal(t *testing.T) { - dserv := getMockDagServ(t) - _, n := getNode(t, dserv, 0) + dserv := testu.GetDAGServ() + n := testu.GetEmptyNode(t, dserv) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -351,20 +315,20 @@ func TestMultiWriteCoal(t *testing.T) { t.Fatal(err) } - err = arrComp(rbuf, data) + err = testu.ArrComp(rbuf, data) if err != nil { t.Fatal(err) } } func TestLargeWriteChunks(t *testing.T) { - dserv := getMockDagServ(t) - _, n := getNode(t, dserv, 0) + dserv := testu.GetDAGServ() + n := testu.GetEmptyNode(t, dserv) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -390,19 +354,19 @@ func TestLargeWriteChunks(t *testing.T) { t.Fatal(err) } - if err = arrComp(out, data); err != nil { + if err = testu.ArrComp(out, data); err != nil { t.Fatal(err) } } func TestDagTruncate(t *testing.T) { - dserv := getMockDagServ(t) - b, n := getNode(t, dserv, 50000) + dserv := testu.GetDAGServ() + b, n := testu.GetRandomNode(t, dserv, 50000) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -430,7 +394,7 @@ func TestDagTruncate(t *testing.T) { t.Fatal(err) } - if err = arrComp(out, b[:12345]); err != nil { + if err = testu.ArrComp(out, b[:12345]); err != nil { t.Fatal(err) } @@ -464,12 +428,12 @@ func TestDagTruncate(t *testing.T) { } func TestSparseWrite(t *testing.T) { - dserv := getMockDagServ(t) - _, n := getNode(t, dserv, 0) + dserv := testu.GetDAGServ() + n := testu.GetEmptyNode(t, dserv) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -496,18 +460,18 @@ func TestSparseWrite(t *testing.T) { t.Fatal(err) } - if err = arrComp(out, buf); err != nil { + if err = testu.ArrComp(out, buf); err != nil { t.Fatal(err) } } func TestSeekPastEndWrite(t *testing.T) { - dserv := getMockDagServ(t) - _, n := getNode(t, dserv, 0) + dserv := testu.GetDAGServ() + n := testu.GetEmptyNode(t, dserv) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -543,18 +507,18 @@ func TestSeekPastEndWrite(t *testing.T) { t.Fatal(err) } - if err = arrComp(out, buf); err != nil { + if err = testu.ArrComp(out, buf); err != nil { t.Fatal(err) } } func TestRelativeSeek(t *testing.T) { - dserv := getMockDagServ(t) - _, n := getNode(t, dserv, 0) + dserv := testu.GetDAGServ() + n := testu.GetEmptyNode(t, dserv) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -579,13 +543,12 @@ func TestRelativeSeek(t *testing.T) { } func TestInvalidSeek(t *testing.T) { - dserv := getMockDagServ(t) - - _, n := getNode(t, dserv, 0) + dserv := testu.GetDAGServ() + n := testu.GetEmptyNode(t, dserv) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -597,13 +560,13 @@ func TestInvalidSeek(t *testing.T) { } func TestEndSeek(t *testing.T) { - dserv := getMockDagServ(t) + dserv := testu.GetDAGServ() - _, n := getNode(t, dserv, 0) + n := testu.GetEmptyNode(t, dserv) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -630,13 +593,13 @@ func TestEndSeek(t *testing.T) { } func TestReadAndSeek(t *testing.T) { - dserv := getMockDagServ(t) + dserv := testu.GetDAGServ() - _, n := getNode(t, dserv, 0) + n := testu.GetEmptyNode(t, dserv) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -698,13 +661,13 @@ func TestReadAndSeek(t *testing.T) { } func TestCtxRead(t *testing.T) { - dserv := getMockDagServ(t) + dserv := testu.GetDAGServ() - _, n := getNode(t, dserv, 0) + n := testu.GetEmptyNode(t, dserv) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) if err != nil { t.Fatal(err) } @@ -720,7 +683,7 @@ func TestCtxRead(t *testing.T) { if err != nil { t.Fatal(err) } - err = arrComp(readBuf, []byte{0, 1, 2, 3}) + err = testu.ArrComp(readBuf, []byte{0, 1, 2, 3}) if err != nil { t.Fatal(err) } @@ -730,14 +693,14 @@ func TestCtxRead(t *testing.T) { func BenchmarkDagmodWrite(b *testing.B) { b.StopTimer() - dserv := getMockDagServ(b) - _, n := getNode(b, dserv, 0) + dserv := testu.GetDAGServ() + n := testu.GetEmptyNode(b, dserv) ctx, cancel := context.WithCancel(context.Background()) defer cancel() wrsize := 4096 - dagmod, err := NewDagModifier(ctx, n, dserv, sizeSplitterGen(512)) + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) if err != nil { b.Fatal(err) } @@ -756,43 +719,3 @@ func BenchmarkDagmodWrite(b *testing.B) { } } } - -func arrComp(a, b []byte) error { - if len(a) != len(b) { - return fmt.Errorf("Arrays differ in length. %d != %d", len(a), len(b)) - } - for i, v := range a { - if v != b[i] { - return fmt.Errorf("Arrays differ at index: %d", i) - } - } - return nil -} - -func printDag(nd *mdag.Node, ds mdag.DAGService, indent int) { - pbd, err := ft.FromBytes(nd.Data()) - if err != nil { - panic(err) - } - - for i := 0; i < indent; i++ { - fmt.Print(" ") - } - fmt.Printf("{size = %d, type = %s, children = %d", pbd.GetFilesize(), pbd.GetType().String(), len(pbd.GetBlocksizes())) - if len(nd.Links) > 0 { - fmt.Println() - } - for _, lnk := range nd.Links { - child, err := lnk.GetNode(context.Background(), ds) - if err != nil { - panic(err) - } - printDag(child, ds, indent+1) - } - if len(nd.Links) > 0 { - for i := 0; i < indent; i++ { - fmt.Print(" ") - } - } - fmt.Println("}") -} diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go new file mode 100644 index 000000000..e512eeb9d --- /dev/null +++ b/unixfs/test/utils.go @@ -0,0 +1,93 @@ +package testu + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "testing" + + imp "github.com/ipfs/go-ipfs/importer" + "github.com/ipfs/go-ipfs/importer/chunk" + mdag "github.com/ipfs/go-ipfs/merkledag" + mdagmock "github.com/ipfs/go-ipfs/merkledag/test" + ft "github.com/ipfs/go-ipfs/unixfs" + + u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" +) + +func SizeSplitterGen(size int64) chunk.SplitterGen { + return func(r io.Reader) chunk.Splitter { + return chunk.NewSizeSplitter(r, size) + } +} + +func GetDAGServ() mdag.DAGService { + return mdagmock.Mock() +} + +func GetNode(t testing.TB, dserv mdag.DAGService, data []byte) *mdag.Node { + in := bytes.NewReader(data) + node, err := imp.BuildTrickleDagFromReader(dserv, SizeSplitterGen(500)(in)) + if err != nil { + t.Fatal(err) + } + + return node +} + +func GetEmptyNode(t testing.TB, dserv mdag.DAGService) *mdag.Node { + return GetNode(t, dserv, []byte{}) +} + +func GetRandomNode(t testing.TB, dserv mdag.DAGService, size int64) ([]byte, *mdag.Node) { + in := io.LimitReader(u.NewTimeSeededRand(), size) + buf, err := ioutil.ReadAll(in) + if err != nil { + t.Fatal(err) + } + + node := GetNode(t, dserv, buf) + return buf, node +} + +func ArrComp(a, b []byte) error { + if len(a) != len(b) { + return fmt.Errorf("Arrays differ in length. %d != %d", len(a), len(b)) + } + for i, v := range a { + if v != b[i] { + return fmt.Errorf("Arrays differ at index: %d", i) + } + } + return nil +} + +func PrintDag(nd *mdag.Node, ds mdag.DAGService, indent int) { + pbd, err := ft.FromBytes(nd.Data()) + if err != nil { + panic(err) + } + + for i := 0; i < indent; i++ { + fmt.Print(" ") + } + fmt.Printf("{size = %d, type = %s, children = %d", pbd.GetFilesize(), pbd.GetType().String(), len(pbd.GetBlocksizes())) + if len(nd.Links) > 0 { + fmt.Println() + } + for _, lnk := range nd.Links { + child, err := lnk.GetNode(context.Background(), ds) + if err != nil { + panic(err) + } + PrintDag(child, ds, indent+1) + } + if len(nd.Links) > 0 { + for i := 0; i < indent; i++ { + fmt.Print(" ") + } + } + fmt.Println("}") +} From 8dc1abd1cd7a6a942904251daddecab29311d492 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 20 Aug 2016 19:05:26 +0200 Subject: [PATCH 1491/3817] test: add absolute seek test move tests to different file License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@03ad2d8ebd9dc74066910106e8091df5646ee012 --- unixfs/io/dagreader_test.go | 75 +++++++++++++++++++++++++++++++++++++ unixfs/io/dagserv_test.go | 32 ---------------- 2 files changed, 75 insertions(+), 32 deletions(-) create mode 100644 unixfs/io/dagreader_test.go delete mode 100644 unixfs/io/dagserv_test.go diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go new file mode 100644 index 000000000..67d2b01a3 --- /dev/null +++ b/unixfs/io/dagreader_test.go @@ -0,0 +1,75 @@ +package io + +import ( + "io/ioutil" + "os" + "testing" + + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + + testu "github.com/ipfs/go-ipfs/unixfs/test" +) + +func TestBasicRead(t *testing.T) { + dserv := testu.GetDAGServ() + inbuf, node := testu.GetRandomNode(t, dserv, 1024) + ctx, closer := context.WithCancel(context.Background()) + defer closer() + + reader, err := NewDagReader(ctx, node, dserv) + if err != nil { + t.Fatal(err) + } + + outbuf, err := ioutil.ReadAll(reader) + if err != nil { + t.Fatal(err) + } + + err = testu.ArrComp(inbuf, outbuf) + if err != nil { + t.Fatal(err) + } +} + +func TestSeekAndRead(t *testing.T) { + dserv := testu.GetDAGServ() + inbuf := make([]byte, 256) + for i := 0; i <= 255; i++ { + inbuf[i] = byte(i) + } + + node := testu.GetNode(t, dserv, inbuf) + ctx, closer := context.WithCancel(context.Background()) + defer closer() + + reader, err := NewDagReader(ctx, node, dserv) + if err != nil { + t.Fatal(err) + } + + for i := 255; i >= 0; i-- { + reader.Seek(int64(i), os.SEEK_SET) + out := make([]byte, 1) + + if reader.Offset() != int64(i) { + t.Fatal("expected offset to be increased by one after read") + } + + c, err := reader.Read(out) + if c != 1 { + t.Fatal("reader should have read just one byte") + } + if err != nil { + t.Fatal(err) + } + + if int(out[0]) != i { + t.Fatalf("read %d at index %d, expected %d", out[0], i, i) + } + + if reader.Offset() != int64(i+1) { + t.Fatal("expected offset to be increased by one after read") + } + } +} diff --git a/unixfs/io/dagserv_test.go b/unixfs/io/dagserv_test.go deleted file mode 100644 index fca849924..000000000 --- a/unixfs/io/dagserv_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package io - -import ( - "io/ioutil" - "testing" - - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - - testu "github.com/ipfs/go-ipfs/unixfs/test" -) - -func TestBasicRead(t *testing.T) { - dserv := testu.GetDAGServ() - inbuf, node := testu.GetRandomNode(t, dserv, 1024) - ctx, closer := context.WithCancel(context.Background()) - defer closer() - - reader, err := NewDagReader(ctx, node, dserv) - if err != nil { - t.Fatal(err) - } - - outbuf, err := ioutil.ReadAll(reader) - if err != nil { - t.Fatal(err) - } - - err = testu.ArrComp(inbuf, outbuf) - if err != nil { - t.Fatal(err) - } -} From aab02713b84426d727a19cdf2451893ad0efde92 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 20 Aug 2016 19:49:36 +0200 Subject: [PATCH 1492/3817] test: add relative seek test to dagreader License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@0cb0052ac79ad0d95d27f10198f61a9bf5473401 --- unixfs/io/dagreader_test.go | 54 ++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index 67d2b01a3..cb8dd399d 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -50,22 +50,15 @@ func TestSeekAndRead(t *testing.T) { for i := 255; i >= 0; i-- { reader.Seek(int64(i), os.SEEK_SET) - out := make([]byte, 1) if reader.Offset() != int64(i) { t.Fatal("expected offset to be increased by one after read") } - c, err := reader.Read(out) - if c != 1 { - t.Fatal("reader should have read just one byte") - } - if err != nil { - t.Fatal(err) - } + out := readByte(t, reader) - if int(out[0]) != i { - t.Fatalf("read %d at index %d, expected %d", out[0], i, i) + if int(out) != i { + t.Fatalf("read %d at index %d, expected %d", out, i, i) } if reader.Offset() != int64(i+1) { @@ -73,3 +66,44 @@ func TestSeekAndRead(t *testing.T) { } } } + +func TestRelativeSeek(t *testing.T) { + dserv := testu.GetDAGServ() + ctx, closer := context.WithCancel(context.Background()) + defer closer() + + inbuf := make([]byte, 1024) + + for i := 0; i < 256; i++ { + inbuf[i*4] = byte(i) + } + node := testu.GetNode(t, dserv, inbuf) + + reader, err := NewDagReader(ctx, node, dserv) + if err != nil { + t.Fatal(err) + } + + for i := 0; i < 256; i++ { + out := readByte(t, reader) + if int(out) != i { + t.Fatalf("expected to read: %d at %d, read %d", i, reader.Offset(), out) + } + reader.Seek(3, os.SEEK_CUR) + } + +} + +func readByte(t testing.TB, reader *DagReader) byte { + out := make([]byte, 1) + c, err := reader.Read(out) + + if c != 1 { + t.Fatal("reader should have read just one byte") + } + if err != nil { + t.Fatal(err) + } + + return out[0] +} From ce8b477292f81579ccccf6f23caaf256e8269556 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 20 Aug 2016 20:12:19 +0200 Subject: [PATCH 1493/3817] test: add reverse relative seeking test to dagreader License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@03956178f5f102ff320d14c83abb04eada335772 --- unixfs/io/dagreader_test.go | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index cb8dd399d..68179a194 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -77,6 +77,8 @@ func TestRelativeSeek(t *testing.T) { for i := 0; i < 256; i++ { inbuf[i*4] = byte(i) } + + inbuf[1023] = 1 // force the reader to be 1024 bytes node := testu.GetNode(t, dserv, inbuf) reader, err := NewDagReader(ctx, node, dserv) @@ -85,11 +87,35 @@ func TestRelativeSeek(t *testing.T) { } for i := 0; i < 256; i++ { + if reader.Offset() != int64(i*4) { + t.Fatalf("offset should be %d, was %d", i*4, reader.Offset()) + } out := readByte(t, reader) if int(out) != i { - t.Fatalf("expected to read: %d at %d, read %d", i, reader.Offset(), out) + t.Fatalf("expected to read: %d at %d, read %d", i, reader.Offset()-1, out) + } + if i != 255 { + _, err := reader.Seek(3, os.SEEK_CUR) + if err != nil { + t.Fatal(err) + } + } + } + + _, err = reader.Seek(4, os.SEEK_END) + if err != nil { + t.Fatal(err) + } + + for i := 0; i < 256; i++ { + if reader.Offset() != int64(1020-i*4) { + t.Fatalf("offset should be %d, was %d", 1020-i*4, reader.Offset()) + } + out := readByte(t, reader) + if int(out) != 255-i { + t.Fatalf("expected to read: %d at %d, read %d", 255-i, reader.Offset()-1, out) } - reader.Seek(3, os.SEEK_CUR) + reader.Seek(-5, os.SEEK_CUR) // seek 4 bytes but we read one byte every time so 5 bytes } } From b43cea68d752cfc0d1a9888791b05568f8497807 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sun, 21 Aug 2016 00:39:09 +0200 Subject: [PATCH 1494/3817] test: add test for bad node types in dagreader License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@5e98b274828eeaa3f0740b439b73fa09961f054d --- unixfs/io/dagreader.go | 4 +--- unixfs/io/dagreader_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 0648f9600..3b9dfcb28 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -68,9 +68,7 @@ func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (*Dag case ftpb.Data_Directory: // Dont allow reading directories return nil, ErrIsDir - case ftpb.Data_Raw: - fallthrough - case ftpb.Data_File: + case ftpb.Data_File, ftpb.Data_Raw: return NewDataFileReader(ctx, n, pb, serv), nil case ftpb.Data_Metadata: if len(n.Links) == 0 { diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index 68179a194..7924683cc 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -5,6 +5,9 @@ import ( "os" "testing" + mdag "github.com/ipfs/go-ipfs/merkledag" + "github.com/ipfs/go-ipfs/unixfs" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" testu "github.com/ipfs/go-ipfs/unixfs/test" @@ -120,6 +123,27 @@ func TestRelativeSeek(t *testing.T) { } +func TestTypeFailures(t *testing.T) { + dserv := testu.GetDAGServ() + ctx, closer := context.WithCancel(context.Background()) + defer closer() + + node := unixfs.EmptyDirNode() + if _, err := NewDagReader(ctx, node, dserv); err != ErrIsDir { + t.Fatalf("excepted to get %v, got %v", ErrIsDir, err) + } + + data, err := unixfs.SymlinkData("/somelink") + if err != nil { + t.Fatal(err) + } + node = mdag.NodeWithData(data) + + if _, err := NewDagReader(ctx, node, dserv); err != ErrCantReadSymlinks { + t.Fatalf("excepted to get %v, got %v", ErrCantReadSymlinks, err) + } +} + func readByte(t testing.TB, reader *DagReader) byte { out := make([]byte, 1) c, err := reader.Read(out) From 768599b3b0f37af116ffec22ae5a314354fab675 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sun, 21 Aug 2016 00:52:02 +0200 Subject: [PATCH 1495/3817] test: add invialid protobuf data testcase to dagreader License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@600437d88a4497ff7af135bfb2358f2b73736a2c --- unixfs/io/dagreader.go | 2 +- unixfs/io/dagreader_test.go | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 3b9dfcb28..53916aa57 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -131,7 +131,7 @@ func (dr *DagReader) precalcNextBuf(ctx context.Context) error { dr.buf = NewRSNCFromBytes(pb.GetData()) return nil case ftpb.Data_Metadata: - return errors.New("Shouldnt have had metadata object inside file") + return errors.New("shouldnt have had metadata object inside file") case ftpb.Data_Symlink: return errors.New("shouldnt have had symlink inside file") default: diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index 7924683cc..1321da4a6 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -144,6 +144,19 @@ func TestTypeFailures(t *testing.T) { } } +func TestBadPBData(t *testing.T) { + dserv := testu.GetDAGServ() + ctx, closer := context.WithCancel(context.Background()) + defer closer() + + node := mdag.NodeWithData([]byte{42}) + _, err := NewDagReader(ctx, node, dserv) + if err == nil { + t.Fatal("excepted error, got nil") + } + +} + func readByte(t testing.TB, reader *DagReader) byte { out := make([]byte, 1) c, err := reader.Read(out) From da4b66263149fd9905c8a104fc300d6c6297abc0 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sun, 21 Aug 2016 01:11:23 +0200 Subject: [PATCH 1496/3817] test: add metadata node testcase to dagreader.go License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@d76abd44a998c65f6e5ee4c799e19ba58a1c88dd --- unixfs/io/dagreader_test.go | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index 1321da4a6..1d147b4c3 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -3,6 +3,7 @@ package io import ( "io/ioutil" "os" + "strings" "testing" mdag "github.com/ipfs/go-ipfs/merkledag" @@ -154,7 +155,46 @@ func TestBadPBData(t *testing.T) { if err == nil { t.Fatal("excepted error, got nil") } +} + +func TestMetadataNode(t *testing.T) { + dserv := testu.GetDAGServ() + rdata, rnode := testu.GetRandomNode(t, dserv, 512) + _, err := dserv.Add(rnode) + if err != nil { + t.Fatal(err) + } + + ctx, closer := context.WithCancel(context.Background()) + defer closer() + data, err := unixfs.BytesForMetadata(&unixfs.Metadata{"text", 125}) + if err != nil { + t.Fatal(err) + } + node := mdag.NodeWithData(data) + + _, err = NewDagReader(ctx, node, dserv) + if err == nil { + t.Fatal("expected an error") + } + if !strings.Contains(err.Error(), "incorrectly formatted") { + t.Fatal("expected different error") + } + + node.AddNodeLink("", rnode) + + reader, err := NewDagReader(ctx, node, dserv) + if err != nil { + t.Fatal(err) + } + readdata, err := ioutil.ReadAll(reader) + if err != nil { + t.Fatal(err) + } + if err := testu.ArrComp(rdata, readdata); err != nil { + t.Fatal(err) + } } func readByte(t testing.TB, reader *DagReader) byte { From 6f41b4a49fdb156a02b717a8e01d62202d1e0bd7 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 8 Sep 2016 13:28:30 +0200 Subject: [PATCH 1497/3817] test: add unixfs/reader tests for WriteTo and size License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@00edb4c3b579323478b49ae65caad990639e595b --- unixfs/io/dagreader_test.go | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index 1d147b4c3..ac8d4d52f 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -1,6 +1,7 @@ package io import ( + "bytes" "io/ioutil" "os" "strings" @@ -197,6 +198,44 @@ func TestMetadataNode(t *testing.T) { } } +func TestWriteTo(t *testing.T) { + dserv := testu.GetDAGServ() + inbuf, node := testu.GetRandomNode(t, dserv, 1024) + ctx, closer := context.WithCancel(context.Background()) + defer closer() + + reader, err := NewDagReader(ctx, node, dserv) + if err != nil { + t.Fatal(err) + } + + outbuf := new(bytes.Buffer) + reader.WriteTo(outbuf) + + err = testu.ArrComp(inbuf, outbuf.Bytes()) + if err != nil { + t.Fatal(err) + } + +} + +func TestReaderSzie(t *testing.T) { + dserv := testu.GetDAGServ() + size := int64(1024) + _, node := testu.GetRandomNode(t, dserv, size) + ctx, closer := context.WithCancel(context.Background()) + defer closer() + + reader, err := NewDagReader(ctx, node, dserv) + if err != nil { + t.Fatal(err) + } + + if reader.Size() != uint64(size) { + t.Fatal("wrong reader size") + } +} + func readByte(t testing.TB, reader *DagReader) byte { out := make([]byte, 1) c, err := reader.Read(out) From 0172496225662368202a96840a94be8f5ef19ee6 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 8 Sep 2016 13:40:18 +0200 Subject: [PATCH 1498/3817] test: add dirbuilder tests License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@6c0c4ca0ebf832ac621b9b195ad616bbb7c388a3 --- unixfs/io/dirbuilder_test.go | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 unixfs/io/dirbuilder_test.go diff --git a/unixfs/io/dirbuilder_test.go b/unixfs/io/dirbuilder_test.go new file mode 100644 index 000000000..80a01d325 --- /dev/null +++ b/unixfs/io/dirbuilder_test.go @@ -0,0 +1,50 @@ +package io + +import ( + "context" + "io/ioutil" + "testing" + + testu "github.com/ipfs/go-ipfs/unixfs/test" +) + +func TestEmptyNode(t *testing.T) { + n := NewEmptyDirectory() + if len(n.Links) != 0 { + t.Fatal("empty node should have 0 links") + } +} + +func TestDirBuilder(t *testing.T) { + dserv := testu.GetDAGServ() + ctx, closer := context.WithCancel(context.Background()) + defer closer() + inbuf, node := testu.GetRandomNode(t, dserv, 1024) + key := node.Cid() + + b := NewDirectory(dserv) + + b.AddChild(ctx, "random", key) + + dir := b.GetNode() + outn, err := dir.GetLinkedNode(ctx, dserv, "random") + if err != nil { + t.Fatal(err) + } + + reader, err := NewDagReader(ctx, outn, dserv) + if err != nil { + t.Fatal(err) + } + + outbuf, err := ioutil.ReadAll(reader) + if err != nil { + t.Fatal(err) + } + + err = testu.ArrComp(inbuf, outbuf) + if err != nil { + t.Fatal(err) + } + +} From 454fc16915e023917b8df0c19cfb19de961bdb8f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 12 Sep 2016 07:47:04 -0700 Subject: [PATCH 1499/3817] Update libp2p to have fixed spdystream dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@08807bf0da9154d9f6b59a42c000074df65cd436 --- routing/dht/dht.go | 4 ++-- routing/dht/dht_net.go | 2 +- routing/dht/dht_test.go | 2 +- routing/dht/ext_test.go | 4 ++-- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/routing.go | 2 +- routing/mock/dht.go | 2 +- routing/none/none_client.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 4 ++-- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 60d87368b..5615c4d4a 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -21,11 +21,11 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - host "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/host" - protocol "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/protocol" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + host "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/host" + protocol "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/protocol" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index d9bfc4007..a7052257f 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -8,9 +8,9 @@ import ( pb "github.com/ipfs/go-ipfs/routing/dht/pb" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" ctxio "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/io" - inet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + inet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net" ) var dhtReadMessageTimeout = time.Minute diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 265612cec..b3af1e730 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -18,10 +18,10 @@ import ( key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - netutil "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/test/util" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + netutil "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/test/util" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 6f0c6b45e..6454b301f 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -13,11 +13,11 @@ import ( dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - inet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net/mock" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + inet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index e123c15b7..e0f8cb947 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - inet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net" + inet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index a9bc57179..b000abdf2 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -5,8 +5,8 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - inet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + inet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 16a497c61..6b7362c05 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -17,8 +17,8 @@ import ( record "github.com/ipfs/go-ipfs/routing/record" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - inet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + inet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index d680cc15b..f696402f9 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + mocknet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 571abb0ac..efa69f9ec 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -7,9 +7,9 @@ import ( routing "github.com/ipfs/go-ipfs/routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - p2phost "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/host" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + p2phost "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/host" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 3674bc29d..bb75a8cd9 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -14,9 +14,9 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/host" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/host" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index b3123b00a..cbc67863d 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -6,7 +6,7 @@ import ( dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - inet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net" + inet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 06bfdb650..d9440c014 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -8,8 +8,8 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - host "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/host" - inet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net" + host "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/host" + inet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" From a1ecbd0ea0e08ac516c15087c904c511a9deb12e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 12 Sep 2016 07:47:04 -0700 Subject: [PATCH 1500/3817] Update libp2p to have fixed spdystream dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@4b772b5c495840c3ebabdff2ef790b9b61ebc09b --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 51f002f07..ffae1e0a3 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,7 +13,7 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) From 66b1bae970c75c016cdc78bde9d4020cea1388dd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 12 Sep 2016 14:26:55 -0700 Subject: [PATCH 1501/3817] Update libp2p to 3.5.2 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@4a9d1ad26d858871d2ff1cdeb062553fb9454837 --- routing/dht/dht.go | 4 ++-- routing/dht/dht_net.go | 2 +- routing/dht/dht_test.go | 2 +- routing/dht/ext_test.go | 4 ++-- routing/dht/notif.go | 2 +- routing/dht/pb/message.go | 2 +- routing/dht/routing.go | 2 +- routing/mock/dht.go | 2 +- routing/none/none_client.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 4 ++-- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 5615c4d4a..f6ba805d0 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -19,13 +19,13 @@ import ( goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" goprocessctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + host "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/host" + protocol "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/protocol" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - host "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/host" - protocol "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/protocol" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go index a7052257f..fc5af6542 100644 --- a/routing/dht/dht_net.go +++ b/routing/dht/dht_net.go @@ -6,11 +6,11 @@ import ( "time" pb "github.com/ipfs/go-ipfs/routing/dht/pb" + inet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" ctxio "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/io" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - inet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net" ) var dhtReadMessageTimeout = time.Minute diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index b3af1e730..8952abec9 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -17,11 +17,11 @@ import ( dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + netutil "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/test/util" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - netutil "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/test/util" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go index 6454b301f..d600e83dd 100644 --- a/routing/dht/ext_test.go +++ b/routing/dht/ext_test.go @@ -13,11 +13,11 @@ import ( dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + inet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net" + mocknet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net/mock" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - inet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/dht/notif.go b/routing/dht/notif.go index e0f8cb947..57a3b8f2c 100644 --- a/routing/dht/notif.go +++ b/routing/dht/notif.go @@ -3,7 +3,7 @@ package dht import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - inet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net" + inet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net" ) // netNotifiee defines methods to be used with the IpfsDHT diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index b000abdf2..2a6936e4e 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -4,9 +4,9 @@ import ( ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + inet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - inet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/dht/routing.go b/routing/dht/routing.go index 6b7362c05..8b72a1711 100644 --- a/routing/dht/routing.go +++ b/routing/dht/routing.go @@ -16,9 +16,9 @@ import ( kb "github.com/ipfs/go-ipfs/routing/kbucket" record "github.com/ipfs/go-ipfs/routing/record" + inet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - inet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index f696402f9..dfda92770 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" + mocknet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net/mock" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - mocknet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/none/none_client.go b/routing/none/none_client.go index efa69f9ec..0142cf1e6 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,10 +6,10 @@ import ( repo "github.com/ipfs/go-ipfs/repo" routing "github.com/ipfs/go-ipfs/routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + p2phost "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/host" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - p2phost "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/host" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index bb75a8cd9..98ad4026e 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -13,10 +13,10 @@ import ( loggables "gx/ipfs/QmYrv4LgCC8FhG2Ab4bwuq5DqBdwMtx3hMb3KKJDZcr2d7/go-libp2p-loggables" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/host" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/host" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index cbc67863d..9f47ff5e7 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -5,8 +5,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" + inet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - inet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index d9440c014..396dc0f9f 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -7,9 +7,9 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + host "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/host" + inet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - host "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/host" - inet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" From a7493450481eeeb101b8ecb1b139aee52eea6e17 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 12 Sep 2016 14:26:55 -0700 Subject: [PATCH 1502/3817] Update libp2p to 3.5.2 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@d655b1f40d71df6c23ba692c43d608f1f61d7ba9 --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index ffae1e0a3..25cdb6ea5 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,7 +13,7 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmcpZpCmnfjRunzeYtXZdtcy16P2mC65CThjb7aA8sPqNY/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) From 71567a15a8b664f2e38a0b62a7fd5eb9dff59a7c Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 5 Sep 2016 17:04:30 +0200 Subject: [PATCH 1503/3817] metrics: add hit counter for ARC and bloom caches License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@60eb42d1217accfa792a07580aaa0cf33f56555d --- blockstore/arc_cache.go | 16 +++++++++++++--- blockstore/arc_cache_test.go | 2 +- blockstore/bloom_cache.go | 11 +++++++++-- blockstore/caching.go | 6 +++++- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index da50bd470..e2293472f 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -1,9 +1,11 @@ package blockstore import ( - "github.com/ipfs/go-ipfs/blocks" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + "github.com/ipfs/go-ipfs/blocks" + + "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" @@ -12,15 +14,21 @@ import ( type arccache struct { arc *lru.ARCCache blockstore Blockstore + + hits metrics.Counter + total metrics.Counter } -func arcCached(bs Blockstore, lruSize int) (*arccache, error) { +func newARCCachedBS(bs Blockstore, ctx context.Context, lruSize int) (*arccache, error) { arc, err := lru.NewARC(lruSize) if err != nil { return nil, err } + c := &arccache{arc: arc, blockstore: bs} + c.hits = metrics.NewCtx(ctx, "arc.hits_total", "Number of ARC cache hits").Counter() + c.total = metrics.NewCtx(ctx, "arc_total", "Total number of ARC cache requests").Counter() - return &arccache{arc: arc, blockstore: bs}, nil + return c, nil } func (b *arccache) DeleteBlock(k key.Key) error { @@ -42,6 +50,7 @@ func (b *arccache) DeleteBlock(k key.Key) error { // if ok == false has is inconclusive // if ok == true then has respons to question: is it contained func (b *arccache) hasCached(k key.Key) (has bool, ok bool) { + b.total.Inc() if k == "" { // Return cache invalid so the call to blockstore happens // in case of invalid key and correct error is created. @@ -50,6 +59,7 @@ func (b *arccache) hasCached(k key.Key) (has bool, ok bool) { h, ok := b.arc.Get(k) if ok { + b.hits.Inc() return h.(bool), true } return false, false diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index a8c2227b8..02caf1429 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -140,7 +140,7 @@ func TestGetAndDeleteFalseShortCircuit(t *testing.T) { } func TestArcCreationFailure(t *testing.T) { - if arc, err := arcCached(nil, -1); arc != nil || err == nil { + if arc, err := newARCCachedBS(nil, context.TODO(), -1); arc != nil || err == nil { t.Fatal("expected error and no cache") } } diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index af8422d0f..79bf81e31 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -6,6 +6,7 @@ import ( "github.com/ipfs/go-ipfs/blocks" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + "gx/ipfs/QmVWBQQAz4Cd2XgW9KgQoqXXrU8KJoCb9WCrhWRFVBKvFe/go-metrics-interface" bloom "gx/ipfs/QmWQ2SJisXwcCLsUXLwYCKSfyExXjFRW2WbBH5sqCUnwX5/bbloom" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) @@ -18,6 +19,10 @@ func bloomCached(bs Blockstore, ctx context.Context, bloomSize, hashCount int) ( return nil, err } bc := &bloomcache{blockstore: bs, bloom: bl} + bc.hits = metrics.NewCtx(ctx, "bloom.hits_total", + "Number of cache hits in bloom cache").Counter() + bc.total = metrics.NewCtx(ctx, "bloom_total", + "Total number of requests to bloom cache").Counter() bc.Invalidate() go bc.Rebuild(ctx) @@ -33,8 +38,8 @@ type bloomcache struct { blockstore Blockstore // Statistics - hits uint64 - misses uint64 + hits metrics.Counter + total metrics.Counter } func (b *bloomcache) Invalidate() { @@ -84,6 +89,7 @@ func (b *bloomcache) DeleteBlock(k key.Key) error { // if ok == false has is inconclusive // if ok == true then has respons to question: is it contained func (b *bloomcache) hasCached(k key.Key) (has bool, ok bool) { + b.total.Inc() if k == "" { // Return cache invalid so call to blockstore // in case of invalid key is forwarded deeper @@ -92,6 +98,7 @@ func (b *bloomcache) hasCached(k key.Key) (has bool, ok bool) { if b.BloomActive() { blr := b.bloom.HasTS([]byte(k)) if blr == false { // not contained in bloom is only conclusive answer bloom gives + b.hits.Inc() return false, true } } diff --git a/blockstore/caching.go b/blockstore/caching.go index f691f89f8..a0c4c27f5 100644 --- a/blockstore/caching.go +++ b/blockstore/caching.go @@ -3,6 +3,7 @@ package blockstore import ( "errors" + "gx/ipfs/QmVWBQQAz4Cd2XgW9KgQoqXXrU8KJoCb9WCrhWRFVBKvFe/go-metrics-interface" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) @@ -33,11 +34,14 @@ func CachedBlockstore(bs GCBlockstore, if opts.HasBloomFilterSize != 0 && opts.HasBloomFilterHashes == 0 { return nil, errors.New("bloom filter hash count can't be 0 when there is size set") } + + ctx = metrics.CtxSubScope(ctx, "bs.cache") + if opts.HasBloomFilterSize != 0 { cbs, err = bloomCached(cbs, ctx, opts.HasBloomFilterSize, opts.HasBloomFilterHashes) } if opts.HasARCCacheSize > 0 { - cbs, err = arcCached(cbs, opts.HasARCCacheSize) + cbs, err = newARCCachedBS(cbs, ctx, opts.HasARCCacheSize) } return cbs, err From 856460611f9e60fc23f3143579fc17e2dba7f958 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 5 Sep 2016 17:05:36 +0200 Subject: [PATCH 1504/3817] blockstore: move ARC cache below the bloom cache ARC cache is influenced by requests and bloom isn't This means that if bloom is able to remove some requests caching them in ARC is pointless. License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@8966b0c25ab745c6b140c7b53c595ed6bfc3bb98 --- blockstore/caching.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/blockstore/caching.go b/blockstore/caching.go index a0c4c27f5..8e2c6cad1 100644 --- a/blockstore/caching.go +++ b/blockstore/caching.go @@ -37,12 +37,12 @@ func CachedBlockstore(bs GCBlockstore, ctx = metrics.CtxSubScope(ctx, "bs.cache") - if opts.HasBloomFilterSize != 0 { - cbs, err = bloomCached(cbs, ctx, opts.HasBloomFilterSize, opts.HasBloomFilterHashes) - } if opts.HasARCCacheSize > 0 { cbs, err = newARCCachedBS(cbs, ctx, opts.HasARCCacheSize) } + if opts.HasBloomFilterSize != 0 { + cbs, err = bloomCached(cbs, ctx, opts.HasBloomFilterSize, opts.HasBloomFilterHashes) + } return cbs, err } From 8b60e635736ff7892656250e855b0afd011b271b Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 6 Sep 2016 08:03:05 +0200 Subject: [PATCH 1505/3817] blockstore: change order of newARCCachedBS parmaeters so the context is first one License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@c81b3fc927f4e6dc4d76e96ae660b8760fe34c57 --- blockstore/arc_cache.go | 2 +- blockstore/arc_cache_test.go | 2 +- blockstore/caching.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index e2293472f..5cc2ff433 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -19,7 +19,7 @@ type arccache struct { total metrics.Counter } -func newARCCachedBS(bs Blockstore, ctx context.Context, lruSize int) (*arccache, error) { +func newARCCachedBS(ctx context.Context, bs Blockstore, lruSize int) (*arccache, error) { arc, err := lru.NewARC(lruSize) if err != nil { return nil, err diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 02caf1429..eb8086a79 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -140,7 +140,7 @@ func TestGetAndDeleteFalseShortCircuit(t *testing.T) { } func TestArcCreationFailure(t *testing.T) { - if arc, err := newARCCachedBS(nil, context.TODO(), -1); arc != nil || err == nil { + if arc, err := newARCCachedBS(context.TODO(), nil, -1); arc != nil || err == nil { t.Fatal("expected error and no cache") } } diff --git a/blockstore/caching.go b/blockstore/caching.go index 8e2c6cad1..08a841d5f 100644 --- a/blockstore/caching.go +++ b/blockstore/caching.go @@ -38,7 +38,7 @@ func CachedBlockstore(bs GCBlockstore, ctx = metrics.CtxSubScope(ctx, "bs.cache") if opts.HasARCCacheSize > 0 { - cbs, err = newARCCachedBS(cbs, ctx, opts.HasARCCacheSize) + cbs, err = newARCCachedBS(ctx, cbs, opts.HasARCCacheSize) } if opts.HasBloomFilterSize != 0 { cbs, err = bloomCached(cbs, ctx, opts.HasBloomFilterSize, opts.HasBloomFilterHashes) From 98ea675ee5db7bdf8a8c6d9619c3e581079ad5ed Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 6 Sep 2016 09:17:42 +0200 Subject: [PATCH 1506/3817] blockstore: update bbloom License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@1ba651762884e785a76fd1a1d8c8d265618200b6 --- blockstore/bloom_cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 79bf81e31..0eafb7203 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -7,8 +7,8 @@ import ( key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" "gx/ipfs/QmVWBQQAz4Cd2XgW9KgQoqXXrU8KJoCb9WCrhWRFVBKvFe/go-metrics-interface" - bloom "gx/ipfs/QmWQ2SJisXwcCLsUXLwYCKSfyExXjFRW2WbBH5sqCUnwX5/bbloom" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + bloom "gx/ipfs/QmeiMCBkYHxkDkDfnDadzz4YxY5ruL5Pj499essE4vRsGM/bbloom" ) // bloomCached returns Blockstore that caches Has requests using Bloom filter From ea9937ef027397d6c8d5b53ef23fc41d8239ae18 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 6 Sep 2016 09:46:06 +0200 Subject: [PATCH 1507/3817] blockstore: add Bloom fill ratio metric License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@5e08775f0dd24551f5f81850dd6807886ad0df8c --- blockstore/bloom_cache.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 0eafb7203..03fa58348 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -2,6 +2,7 @@ package blockstore import ( "sync/atomic" + "time" "github.com/ipfs/go-ipfs/blocks" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" @@ -23,9 +24,25 @@ func bloomCached(bs Blockstore, ctx context.Context, bloomSize, hashCount int) ( "Number of cache hits in bloom cache").Counter() bc.total = metrics.NewCtx(ctx, "bloom_total", "Total number of requests to bloom cache").Counter() + + fill := metrics.NewCtx(ctx, "bloom_fill_ratio", + "Ratio of bloom filter fullnes, (updated once a minute)").Gauge() + bc.Invalidate() go bc.Rebuild(ctx) - + go func() { + <-bc.rebuildChan + t := time.NewTicker(1 * time.Minute) + for { + select { + case <-ctx.Done(): + t.Stop() + return + case <-t.C: + fill.Set(bc.bloom.FillRatio()) + } + } + }() return bc, nil } From fd13a1c87fd5cf68d7132fc3dc129faa84749696 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 7 Sep 2016 18:12:16 +0200 Subject: [PATCH 1508/3817] deps: update go-metrics-interface and -prometheus to 0.1.2 License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@704dd8889f872b12f8c9b662a38931ff79f77091 --- blockstore/bloom_cache.go | 2 +- blockstore/caching.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 03fa58348..1f54e5482 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -7,7 +7,7 @@ import ( "github.com/ipfs/go-ipfs/blocks" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - "gx/ipfs/QmVWBQQAz4Cd2XgW9KgQoqXXrU8KJoCb9WCrhWRFVBKvFe/go-metrics-interface" + "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" bloom "gx/ipfs/QmeiMCBkYHxkDkDfnDadzz4YxY5ruL5Pj499essE4vRsGM/bbloom" ) diff --git a/blockstore/caching.go b/blockstore/caching.go index 08a841d5f..d482e0459 100644 --- a/blockstore/caching.go +++ b/blockstore/caching.go @@ -3,7 +3,7 @@ package blockstore import ( "errors" - "gx/ipfs/QmVWBQQAz4Cd2XgW9KgQoqXXrU8KJoCb9WCrhWRFVBKvFe/go-metrics-interface" + "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) From d489239887c4d5a202e1731bd88445cc2c4aa9c6 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 7 Sep 2016 18:32:35 +0200 Subject: [PATCH 1509/3817] metrics: do not run bloom fillrate collector when metrics are inactive License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@1532e2d275f53b2621eb5270623b711699bf282b --- blockstore/bloom_cache.go | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 1f54e5482..9607561cb 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -25,24 +25,26 @@ func bloomCached(bs Blockstore, ctx context.Context, bloomSize, hashCount int) ( bc.total = metrics.NewCtx(ctx, "bloom_total", "Total number of requests to bloom cache").Counter() - fill := metrics.NewCtx(ctx, "bloom_fill_ratio", - "Ratio of bloom filter fullnes, (updated once a minute)").Gauge() - bc.Invalidate() go bc.Rebuild(ctx) - go func() { - <-bc.rebuildChan - t := time.NewTicker(1 * time.Minute) - for { - select { - case <-ctx.Done(): - t.Stop() - return - case <-t.C: - fill.Set(bc.bloom.FillRatio()) + if metrics.Active() { + go func() { + fill := metrics.NewCtx(ctx, "bloom_fill_ratio", + "Ratio of bloom filter fullnes, (updated once a minute)").Gauge() + + <-bc.rebuildChan + t := time.NewTicker(1 * time.Minute) + for { + select { + case <-ctx.Done(): + t.Stop() + return + case <-t.C: + fill.Set(bc.bloom.FillRatio()) + } } - } - }() + }() + } return bc, nil } From 594bac466383d5ad4a836a61e0354897096ec876 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sun, 21 Aug 2016 09:39:49 -0400 Subject: [PATCH 1510/3817] Pinner: Provide Pinned.String() method and use it in "block rm" License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@7b4ae57ce234a2ae524ce07ca17a91ec49092f2b --- pinning/pinner/pin.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 3e85b894a..db9034624 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -111,6 +111,26 @@ type Pinned struct { Via *cid.Cid } +func (p Pinned) Pinned() bool { + if p.Mode == NotPinned { + return false + } else { + return true + } +} + +func (p Pinned) String() string { + switch p.Mode { + case NotPinned: + return "not pinned" + case Indirect: + return fmt.Sprintf("pinned via %s", p.Via) + default: + modeStr, _ := PinModeToString(p.Mode) + return fmt.Sprintf("pinned: %s", modeStr) + } +} + // pinner implements the Pinner interface type pinner struct { lock sync.RWMutex From 4e459c018c4dfdeb8ef81452b3bb6c261b3989fe Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 14 Sep 2016 18:45:44 -0400 Subject: [PATCH 1511/3817] "block rm": move core functionally into blockstore_util package Note: this code can not go in the "blockstore" package due to a circular dependency with the "pin" package. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-blockstore@5ba29b4bf284db659537352f3164f10fe0eec54d --- blockstore/util/remove.go | 100 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 blockstore/util/remove.go diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go new file mode 100644 index 000000000..21b4e6015 --- /dev/null +++ b/blockstore/util/remove.go @@ -0,0 +1,100 @@ +package blockstore_util + +import ( + "fmt" + "io" + + bs "github.com/ipfs/go-ipfs/blocks/blockstore" + "github.com/ipfs/go-ipfs/pin" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" +) + +type RemovedBlock struct { + Hash string `json:",omitempty"` + Error string `json:",omitempty"` +} + +type RmBlocksOpts struct { + Prefix string + Quiet bool + Force bool +} + +func RmBlocks(blocks bs.GCBlockstore, pins pin.Pinner, out chan<- interface{}, cids []*cid.Cid, opts RmBlocksOpts) error { + go func() { + defer close(out) + + unlocker := blocks.GCLock() + defer unlocker.Unlock() + + stillOkay, err := checkIfPinned(pins, cids, out) + if err != nil { + out <- &RemovedBlock{Error: fmt.Sprintf("pin check failed: %s", err)} + return + } + + for _, c := range stillOkay { + err := blocks.DeleteBlock(key.Key(c.Hash())) + if err != nil && opts.Force && (err == bs.ErrNotFound || err == ds.ErrNotFound) { + // ignore non-existent blocks + } else if err != nil { + out <- &RemovedBlock{Hash: c.String(), Error: err.Error()} + } else if !opts.Quiet { + out <- &RemovedBlock{Hash: c.String()} + } + } + }() + return nil +} + +func checkIfPinned(pins pin.Pinner, cids []*cid.Cid, out chan<- interface{}) ([]*cid.Cid, error) { + stillOkay := make([]*cid.Cid, 0, len(cids)) + res, err := pins.CheckIfPinned(cids...) + if err != nil { + return nil, err + } + for _, r := range res { + if !r.Pinned() { + stillOkay = append(stillOkay, r.Key) + } else { + out <- &RemovedBlock{ + Hash: r.Key.String(), + Error: r.String(), + } + } + } + return stillOkay, nil +} + +type RmError struct { + Fatal bool + Msg string +} + +func (err RmError) Error() string { return err.Msg } + +func ProcRmOutput(in <-chan interface{}, sout io.Writer, serr io.Writer) *RmError { + someFailed := false + for res := range in { + r := res.(*RemovedBlock) + if r.Hash == "" && r.Error != "" { + return &RmError{ + Fatal: true, + Msg: fmt.Sprintf("aborted: %s", r.Error), + } + } else if r.Error != "" { + someFailed = true + fmt.Fprintf(serr, "cannot remove %s: %s\n", r.Hash, r.Error) + } else { + fmt.Fprintf(sout, "removed %s\n", r.Hash) + } + } + if someFailed { + return &RmError{ + Msg: fmt.Sprintf("some blocks not removed"), + } + } + return nil +} From b2bb98494ad215c17614ad6917cb2934e5ba9377 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 15 Sep 2016 04:11:02 -0400 Subject: [PATCH 1512/3817] "block rm": just return "error" in ProcRmOutput License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-blockstore@45deaaa5882f2abf9e2865ed6f2ddf5831f978c8 --- blockstore/util/remove.go | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 21b4e6015..5b79c09f8 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -68,22 +68,12 @@ func checkIfPinned(pins pin.Pinner, cids []*cid.Cid, out chan<- interface{}) ([] return stillOkay, nil } -type RmError struct { - Fatal bool - Msg string -} - -func (err RmError) Error() string { return err.Msg } - -func ProcRmOutput(in <-chan interface{}, sout io.Writer, serr io.Writer) *RmError { +func ProcRmOutput(in <-chan interface{}, sout io.Writer, serr io.Writer) error { someFailed := false for res := range in { r := res.(*RemovedBlock) if r.Hash == "" && r.Error != "" { - return &RmError{ - Fatal: true, - Msg: fmt.Sprintf("aborted: %s", r.Error), - } + return fmt.Errorf("aborted: %s", r.Error) } else if r.Error != "" { someFailed = true fmt.Fprintf(serr, "cannot remove %s: %s\n", r.Hash, r.Error) @@ -92,9 +82,7 @@ func ProcRmOutput(in <-chan interface{}, sout io.Writer, serr io.Writer) *RmErro } } if someFailed { - return &RmError{ - Msg: fmt.Sprintf("some blocks not removed"), - } + return fmt.Errorf("some blocks not removed") } return nil } From 6123b397ef5b0019a00065ac05b95cd50dd135aa Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 15 Sep 2016 13:41:07 -0400 Subject: [PATCH 1513/3817] "block rm": Document RemovedBlock. Interface tweaks. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-blockstore@a27132a405a5cf349d8198bcf8abc6f8365c46c5 --- blockstore/util/remove.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 5b79c09f8..c7db675a8 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -11,6 +11,12 @@ import ( cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) +// RemovedBlock is used to respresent the result of removing a block. +// If a block was removed successfully than the Error string will be +// empty. If a block could not be removed than Error will contain the +// reason the block could not be removed. If the removal was aborted +// due to a fatal error Hash will be be empty, Error will contain the +// reason, and no more results will be sent. type RemovedBlock struct { Hash string `json:",omitempty"` Error string `json:",omitempty"` @@ -29,11 +35,7 @@ func RmBlocks(blocks bs.GCBlockstore, pins pin.Pinner, out chan<- interface{}, c unlocker := blocks.GCLock() defer unlocker.Unlock() - stillOkay, err := checkIfPinned(pins, cids, out) - if err != nil { - out <- &RemovedBlock{Error: fmt.Sprintf("pin check failed: %s", err)} - return - } + stillOkay := FilterPinned(pins, out, cids) for _, c := range stillOkay { err := blocks.DeleteBlock(key.Key(c.Hash())) @@ -49,11 +51,12 @@ func RmBlocks(blocks bs.GCBlockstore, pins pin.Pinner, out chan<- interface{}, c return nil } -func checkIfPinned(pins pin.Pinner, cids []*cid.Cid, out chan<- interface{}) ([]*cid.Cid, error) { +func FilterPinned(pins pin.Pinner, out chan<- interface{}, cids []*cid.Cid) []*cid.Cid { stillOkay := make([]*cid.Cid, 0, len(cids)) res, err := pins.CheckIfPinned(cids...) if err != nil { - return nil, err + out <- &RemovedBlock{Error: fmt.Sprintf("pin check failed: %s", err)} + return nil } for _, r := range res { if !r.Pinned() { @@ -65,7 +68,7 @@ func checkIfPinned(pins pin.Pinner, cids []*cid.Cid, out chan<- interface{}) ([] } } } - return stillOkay, nil + return stillOkay } func ProcRmOutput(in <-chan interface{}, sout io.Writer, serr io.Writer) error { From 6036f08fa6e991ad0f5018ffaccca04823ea45c3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 13 Sep 2016 15:17:07 -0700 Subject: [PATCH 1514/3817] routing: use extracted dht and routing code License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@cac3e7b96ac8f6fce81c82e57db51843f11efca2 --- routing/dht/dht.go | 342 ---------- routing/dht/dht_bootstrap.go | 182 ------ routing/dht/dht_net.go | 250 ------- routing/dht/dht_test.go | 828 ------------------------ routing/dht/ext_test.go | 290 --------- routing/dht/handlers.go | 288 --------- routing/dht/lookup.go | 112 ---- routing/dht/notif.go | 39 -- routing/dht/pb/Makefile | 11 - routing/dht/pb/dht.pb.go | 272 -------- routing/dht/pb/dht.proto | 81 --- routing/dht/pb/message.go | 185 ------ routing/dht/pb/message_test.go | 15 - routing/dht/providers/providers.go | 353 ---------- routing/dht/providers/providers_test.go | 150 ----- routing/dht/query.go | 298 --------- routing/dht/records.go | 149 ----- routing/dht/routing.go | 538 --------------- routing/dht/util.go | 39 -- routing/kbucket/bucket.go | 108 ---- routing/kbucket/sorting.go | 55 -- routing/kbucket/table.go | 225 ------- routing/kbucket/table_test.go | 187 ------ routing/kbucket/util.go | 63 -- routing/keyspace/keyspace.go | 97 --- routing/keyspace/xor.go | 67 -- routing/keyspace/xor_test.go | 122 ---- routing/mock/centralized_client.go | 8 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 3 +- routing/none/none_client.go | 2 +- routing/offline/offline.go | 6 +- routing/record/record.go | 48 -- routing/record/selection.go | 40 -- routing/record/validation.go | 114 ---- routing/record/validation_test.go | 35 - routing/routing.go | 105 --- routing/supernode/client.go | 30 +- routing/supernode/proxy/loopback.go | 7 +- routing/supernode/proxy/standard.go | 4 +- routing/supernode/server.go | 17 +- routing/supernode/server_test.go | 2 +- 42 files changed, 41 insertions(+), 5728 deletions(-) delete mode 100644 routing/dht/dht.go delete mode 100644 routing/dht/dht_bootstrap.go delete mode 100644 routing/dht/dht_net.go delete mode 100644 routing/dht/dht_test.go delete mode 100644 routing/dht/ext_test.go delete mode 100644 routing/dht/handlers.go delete mode 100644 routing/dht/lookup.go delete mode 100644 routing/dht/notif.go delete mode 100644 routing/dht/pb/Makefile delete mode 100644 routing/dht/pb/dht.pb.go delete mode 100644 routing/dht/pb/dht.proto delete mode 100644 routing/dht/pb/message.go delete mode 100644 routing/dht/pb/message_test.go delete mode 100644 routing/dht/providers/providers.go delete mode 100644 routing/dht/providers/providers_test.go delete mode 100644 routing/dht/query.go delete mode 100644 routing/dht/records.go delete mode 100644 routing/dht/routing.go delete mode 100644 routing/dht/util.go delete mode 100644 routing/kbucket/bucket.go delete mode 100644 routing/kbucket/sorting.go delete mode 100644 routing/kbucket/table.go delete mode 100644 routing/kbucket/table_test.go delete mode 100644 routing/kbucket/util.go delete mode 100644 routing/keyspace/keyspace.go delete mode 100644 routing/keyspace/xor.go delete mode 100644 routing/keyspace/xor_test.go delete mode 100644 routing/record/record.go delete mode 100644 routing/record/selection.go delete mode 100644 routing/record/validation.go delete mode 100644 routing/record/validation_test.go delete mode 100644 routing/routing.go diff --git a/routing/dht/dht.go b/routing/dht/dht.go deleted file mode 100644 index f6ba805d0..000000000 --- a/routing/dht/dht.go +++ /dev/null @@ -1,342 +0,0 @@ -// Package dht implements a distributed hash table that satisfies the ipfs routing -// interface. This DHT is modeled after kademlia with Coral and S/Kademlia modifications. -package dht - -import ( - "bytes" - "errors" - "fmt" - "sync" - "time" - - routing "github.com/ipfs/go-ipfs/routing" - pb "github.com/ipfs/go-ipfs/routing/dht/pb" - providers "github.com/ipfs/go-ipfs/routing/dht/providers" - kb "github.com/ipfs/go-ipfs/routing/kbucket" - record "github.com/ipfs/go-ipfs/routing/record" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - - goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - goprocessctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - host "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/host" - protocol "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/protocol" - ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" -) - -var log = logging.Logger("dht") - -var ProtocolDHT protocol.ID = "/ipfs/kad/1.0.0" -var ProtocolDHTOld protocol.ID = "/ipfs/dht" - -// NumBootstrapQueries defines the number of random dht queries to do to -// collect members of the routing table. -const NumBootstrapQueries = 5 - -// TODO. SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-dht/index.js - -// IpfsDHT is an implementation of Kademlia with Coral and S/Kademlia modifications. -// It is used to implement the base IpfsRouting module. -type IpfsDHT struct { - host host.Host // the network services we need - self peer.ID // Local peer (yourself) - peerstore pstore.Peerstore // Peer Registry - - datastore ds.Datastore // Local data - - routingTable *kb.RoutingTable // Array of routing tables for differently distanced nodes - providers *providers.ProviderManager - - birth time.Time // When this peer started up - diaglock sync.Mutex // lock to make diagnostics work better - - Validator record.Validator // record validator funcs - Selector record.Selector // record selection funcs - - ctx context.Context - proc goprocess.Process - - strmap map[peer.ID]*messageSender - smlk sync.Mutex -} - -// NewDHT creates a new DHT object with the given peer as the 'local' host -func NewDHT(ctx context.Context, h host.Host, dstore ds.Batching) *IpfsDHT { - dht := new(IpfsDHT) - dht.datastore = dstore - dht.self = h.ID() - dht.peerstore = h.Peerstore() - dht.host = h - - // register for network notifs. - dht.host.Network().Notify((*netNotifiee)(dht)) - - dht.proc = goprocess.WithTeardown(func() error { - // remove ourselves from network notifs. - dht.host.Network().StopNotify((*netNotifiee)(dht)) - return nil - }) - - dht.strmap = make(map[peer.ID]*messageSender) - dht.ctx = ctx - - h.SetStreamHandler(ProtocolDHT, dht.handleNewStream) - h.SetStreamHandler(ProtocolDHTOld, dht.handleNewStream) - dht.providers = providers.NewProviderManager(dht.ctx, dht.self, dstore) - dht.proc.AddChild(dht.providers.Process()) - goprocessctx.CloseAfterContext(dht.proc, ctx) - - dht.routingTable = kb.NewRoutingTable(20, kb.ConvertPeerID(dht.self), time.Minute, dht.peerstore) - dht.birth = time.Now() - - dht.Validator = make(record.Validator) - dht.Validator["pk"] = record.PublicKeyValidator - - dht.Selector = make(record.Selector) - dht.Selector["pk"] = record.PublicKeySelector - - return dht -} - -// putValueToPeer stores the given key/value pair at the peer 'p' -func (dht *IpfsDHT) putValueToPeer(ctx context.Context, p peer.ID, - key key.Key, rec *pb.Record) error { - - pmes := pb.NewMessage(pb.Message_PUT_VALUE, string(key), 0) - pmes.Record = rec - rpmes, err := dht.sendRequest(ctx, p, pmes) - switch err { - case ErrReadTimeout: - log.Warningf("read timeout: %s %s", p.Pretty(), key) - fallthrough - default: - return err - case nil: - break - } - - if err != nil { - return err - } - - if !bytes.Equal(rpmes.GetRecord().Value, pmes.GetRecord().Value) { - return errors.New("value not put correctly") - } - return nil -} - -var errInvalidRecord = errors.New("received invalid record") - -// getValueOrPeers queries a particular peer p for the value for -// key. It returns either the value or a list of closer peers. -// NOTE: It will update the dht's peerstore with any new addresses -// it finds for the given peer. -func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.ID, - key key.Key) (*pb.Record, []pstore.PeerInfo, error) { - - pmes, err := dht.getValueSingle(ctx, p, key) - if err != nil { - return nil, nil, err - } - - // Perhaps we were given closer peers - peers := pb.PBPeersToPeerInfos(pmes.GetCloserPeers()) - - if record := pmes.GetRecord(); record != nil { - // Success! We were given the value - log.Debug("getValueOrPeers: got value") - - // make sure record is valid. - err = dht.verifyRecordOnline(ctx, record) - if err != nil { - log.Info("Received invalid record! (discarded)") - // return a sentinal to signify an invalid record was received - err = errInvalidRecord - record = new(pb.Record) - } - return record, peers, err - } - - if len(peers) > 0 { - log.Debug("getValueOrPeers: peers") - return nil, peers, nil - } - - log.Warning("getValueOrPeers: routing.ErrNotFound") - return nil, nil, routing.ErrNotFound -} - -// getValueSingle simply performs the get value RPC with the given parameters -func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.ID, - key key.Key) (*pb.Message, error) { - defer log.EventBegin(ctx, "getValueSingle", p, &key).Done() - - pmes := pb.NewMessage(pb.Message_GET_VALUE, string(key), 0) - resp, err := dht.sendRequest(ctx, p, pmes) - switch err { - case nil: - return resp, nil - case ErrReadTimeout: - log.Warningf("read timeout: %s %s", p.Pretty(), key) - fallthrough - default: - return nil, err - } -} - -// getLocal attempts to retrieve the value from the datastore -func (dht *IpfsDHT) getLocal(key key.Key) (*pb.Record, error) { - - log.Debug("getLocal %s", key) - v, err := dht.datastore.Get(key.DsKey()) - if err != nil { - return nil, err - } - log.Debug("found in db") - - byt, ok := v.([]byte) - if !ok { - return nil, errors.New("value stored in datastore not []byte") - } - rec := new(pb.Record) - err = proto.Unmarshal(byt, rec) - if err != nil { - return nil, err - } - - err = dht.verifyRecordLocally(rec) - if err != nil { - log.Debugf("local record verify failed: %s (discarded)", err) - return nil, err - } - - return rec, nil -} - -// getOwnPrivateKey attempts to load the local peers private -// key from the peerstore. -func (dht *IpfsDHT) getOwnPrivateKey() (ci.PrivKey, error) { - sk := dht.peerstore.PrivKey(dht.self) - if sk == nil { - log.Warningf("%s dht cannot get own private key!", dht.self) - return nil, fmt.Errorf("cannot get private key to sign record!") - } - return sk, nil -} - -// putLocal stores the key value pair in the datastore -func (dht *IpfsDHT) putLocal(key key.Key, rec *pb.Record) error { - data, err := proto.Marshal(rec) - if err != nil { - return err - } - - return dht.datastore.Put(key.DsKey(), data) -} - -// Update signals the routingTable to Update its last-seen status -// on the given peer. -func (dht *IpfsDHT) Update(ctx context.Context, p peer.ID) { - log.Event(ctx, "updatePeer", p) - dht.routingTable.Update(p) -} - -// FindLocal looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in. -func (dht *IpfsDHT) FindLocal(id peer.ID) pstore.PeerInfo { - p := dht.routingTable.Find(id) - if p != "" { - return dht.peerstore.PeerInfo(p) - } - return pstore.PeerInfo{} -} - -// findPeerSingle asks peer 'p' if they know where the peer with id 'id' is -func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.ID, id peer.ID) (*pb.Message, error) { - defer log.EventBegin(ctx, "findPeerSingle", p, id).Done() - - pmes := pb.NewMessage(pb.Message_FIND_NODE, string(id), 0) - resp, err := dht.sendRequest(ctx, p, pmes) - switch err { - case nil: - return resp, nil - case ErrReadTimeout: - log.Warningf("read timeout: %s %s", p.Pretty(), id) - fallthrough - default: - return nil, err - } -} - -func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.ID, key key.Key) (*pb.Message, error) { - defer log.EventBegin(ctx, "findProvidersSingle", p, &key).Done() - - pmes := pb.NewMessage(pb.Message_GET_PROVIDERS, string(key), 0) - resp, err := dht.sendRequest(ctx, p, pmes) - switch err { - case nil: - return resp, nil - case ErrReadTimeout: - log.Warningf("read timeout: %s %s", p.Pretty(), key) - fallthrough - default: - return nil, err - } -} - -// nearestPeersToQuery returns the routing tables closest peers. -func (dht *IpfsDHT) nearestPeersToQuery(pmes *pb.Message, count int) []peer.ID { - key := key.Key(pmes.GetKey()) - closer := dht.routingTable.NearestPeers(kb.ConvertKey(key), count) - return closer -} - -// betterPeerToQuery returns nearestPeersToQuery, but iff closer than self. -func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, p peer.ID, count int) []peer.ID { - closer := dht.nearestPeersToQuery(pmes, count) - - // no node? nil - if closer == nil { - return nil - } - - // == to self? thats bad - for _, p := range closer { - if p == dht.self { - log.Debug("attempted to return self! this shouldn't happen...") - return nil - } - } - - var filtered []peer.ID - for _, clp := range closer { - // Dont send a peer back themselves - if p == clp { - continue - } - - filtered = append(filtered, clp) - } - - // ok seems like closer nodes - return filtered -} - -// Context return dht's context -func (dht *IpfsDHT) Context() context.Context { - return dht.ctx -} - -// Process return dht's process -func (dht *IpfsDHT) Process() goprocess.Process { - return dht.proc -} - -// Close calls Process Close -func (dht *IpfsDHT) Close() error { - return dht.proc.Close() -} diff --git a/routing/dht/dht_bootstrap.go b/routing/dht/dht_bootstrap.go deleted file mode 100644 index 366939ae2..000000000 --- a/routing/dht/dht_bootstrap.go +++ /dev/null @@ -1,182 +0,0 @@ -// Package dht implements a distributed hash table that satisfies the ipfs routing -// interface. This DHT is modeled after kademlia with Coral and S/Kademlia modifications. -package dht - -import ( - "crypto/rand" - "fmt" - "sync" - "time" - - routing "github.com/ipfs/go-ipfs/routing" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - - goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - periodicproc "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/periodic" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" -) - -// BootstrapConfig specifies parameters used bootstrapping the DHT. -// -// Note there is a tradeoff between the bootstrap period and the -// number of queries. We could support a higher period with less -// queries. -type BootstrapConfig struct { - Queries int // how many queries to run per period - Period time.Duration // how often to run periodi cbootstrap. - Timeout time.Duration // how long to wait for a bootstrao query to run -} - -var DefaultBootstrapConfig = BootstrapConfig{ - // For now, this is set to 1 query. - // We are currently more interested in ensuring we have a properly formed - // DHT than making sure our dht minimizes traffic. Once we are more certain - // of our implementation's robustness, we should lower this down to 8 or 4. - Queries: 1, - - // For now, this is set to 1 minute, which is a medium period. We are - // We are currently more interested in ensuring we have a properly formed - // DHT than making sure our dht minimizes traffic. - Period: time.Duration(5 * time.Minute), - - Timeout: time.Duration(10 * time.Second), -} - -// Bootstrap ensures the dht routing table remains healthy as peers come and go. -// it builds up a list of peers by requesting random peer IDs. The Bootstrap -// process will run a number of queries each time, and run every time signal fires. -// These parameters are configurable. -// -// As opposed to BootstrapWithConfig, Bootstrap satisfies the routing interface -func (dht *IpfsDHT) Bootstrap(ctx context.Context) error { - proc, err := dht.BootstrapWithConfig(DefaultBootstrapConfig) - if err != nil { - return err - } - - // wait till ctx or dht.Context exits. - // we have to do it this way to satisfy the Routing interface (contexts) - go func() { - defer proc.Close() - select { - case <-ctx.Done(): - case <-dht.Context().Done(): - } - }() - - return nil -} - -// BootstrapWithConfig ensures the dht routing table remains healthy as peers come and go. -// it builds up a list of peers by requesting random peer IDs. The Bootstrap -// process will run a number of queries each time, and run every time signal fires. -// These parameters are configurable. -// -// BootstrapWithConfig returns a process, so the user can stop it. -func (dht *IpfsDHT) BootstrapWithConfig(config BootstrapConfig) (goprocess.Process, error) { - sig := time.Tick(config.Period) - return dht.BootstrapOnSignal(config, sig) -} - -// SignalBootstrap ensures the dht routing table remains healthy as peers come and go. -// it builds up a list of peers by requesting random peer IDs. The Bootstrap -// process will run a number of queries each time, and run every time signal fires. -// These parameters are configurable. -// -// SignalBootstrap returns a process, so the user can stop it. -func (dht *IpfsDHT) BootstrapOnSignal(cfg BootstrapConfig, signal <-chan time.Time) (goprocess.Process, error) { - if cfg.Queries <= 0 { - return nil, fmt.Errorf("invalid number of queries: %d", cfg.Queries) - } - - if signal == nil { - return nil, fmt.Errorf("invalid signal: %v", signal) - } - - proc := periodicproc.Ticker(signal, func(worker goprocess.Process) { - // it would be useful to be able to send out signals of when we bootstrap, too... - // maybe this is a good case for whole module event pub/sub? - - ctx := dht.Context() - if err := dht.runBootstrap(ctx, cfg); err != nil { - log.Warning(err) - // A bootstrapping error is important to notice but not fatal. - } - }) - - return proc, nil -} - -// runBootstrap builds up list of peers by requesting random peer IDs -func (dht *IpfsDHT) runBootstrap(ctx context.Context, cfg BootstrapConfig) error { - bslog := func(msg string) { - log.Debugf("DHT %s dhtRunBootstrap %s -- routing table size: %d", dht.self, msg, dht.routingTable.Size()) - } - bslog("start") - defer bslog("end") - defer log.EventBegin(ctx, "dhtRunBootstrap").Done() - - var merr u.MultiErr - - randomID := func() peer.ID { - // 16 random bytes is not a valid peer id. it may be fine becuase - // the dht will rehash to its own keyspace anyway. - id := make([]byte, 16) - rand.Read(id) - id = u.Hash(id) - return peer.ID(id) - } - - // bootstrap sequentially, as results will compound - ctx, cancel := context.WithTimeout(ctx, cfg.Timeout) - defer cancel() - runQuery := func(ctx context.Context, id peer.ID) { - p, err := dht.FindPeer(ctx, id) - if err == routing.ErrNotFound { - // this isn't an error. this is precisely what we expect. - } else if err != nil { - merr = append(merr, err) - } else { - // woah, actually found a peer with that ID? this shouldn't happen normally - // (as the ID we use is not a real ID). this is an odd error worth logging. - err := fmt.Errorf("Bootstrap peer error: Actually FOUND peer. (%s, %s)", id, p) - log.Warningf("%s", err) - merr = append(merr, err) - } - } - - sequential := true - if sequential { - // these should be parallel normally. but can make them sequential for debugging. - // note that the core/bootstrap context deadline should be extended too for that. - for i := 0; i < cfg.Queries; i++ { - id := randomID() - log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, cfg.Queries, id) - runQuery(ctx, id) - } - - } else { - // note on parallelism here: the context is passed in to the queries, so they - // **should** exit when it exceeds, making this function exit on ctx cancel. - // normally, we should be selecting on ctx.Done() here too, but this gets - // complicated to do with WaitGroup, and doesnt wait for the children to exit. - var wg sync.WaitGroup - for i := 0; i < cfg.Queries; i++ { - wg.Add(1) - go func() { - defer wg.Done() - - id := randomID() - log.Debugf("Bootstrapping query (%d/%d) to random ID: %s", i+1, cfg.Queries, id) - runQuery(ctx, id) - }() - } - wg.Wait() - } - - if len(merr) > 0 { - return merr - } - return nil -} diff --git a/routing/dht/dht_net.go b/routing/dht/dht_net.go deleted file mode 100644 index fc5af6542..000000000 --- a/routing/dht/dht_net.go +++ /dev/null @@ -1,250 +0,0 @@ -package dht - -import ( - "fmt" - "sync" - "time" - - pb "github.com/ipfs/go-ipfs/routing/dht/pb" - inet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - ctxio "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/io" - ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" -) - -var dhtReadMessageTimeout = time.Minute -var ErrReadTimeout = fmt.Errorf("timed out reading response") - -// handleNewStream implements the inet.StreamHandler -func (dht *IpfsDHT) handleNewStream(s inet.Stream) { - go dht.handleNewMessage(s) -} - -func (dht *IpfsDHT) handleNewMessage(s inet.Stream) { - defer s.Close() - - ctx := dht.Context() - cr := ctxio.NewReader(ctx, s) // ok to use. we defer close stream in this func - cw := ctxio.NewWriter(ctx, s) // ok to use. we defer close stream in this func - r := ggio.NewDelimitedReader(cr, inet.MessageSizeMax) - w := ggio.NewDelimitedWriter(cw) - mPeer := s.Conn().RemotePeer() - - for { - // receive msg - pmes := new(pb.Message) - if err := r.ReadMsg(pmes); err != nil { - log.Debugf("Error unmarshaling data: %s", err) - return - } - - // update the peer (on valid msgs only) - dht.updateFromMessage(ctx, mPeer, pmes) - - // get handler for this msg type. - handler := dht.handlerForMsgType(pmes.GetType()) - if handler == nil { - log.Debug("got back nil handler from handlerForMsgType") - return - } - - // dispatch handler. - rpmes, err := handler(ctx, mPeer, pmes) - if err != nil { - log.Debugf("handle message error: %s", err) - return - } - - // if nil response, return it before serializing - if rpmes == nil { - log.Debug("got back nil response from request") - continue - } - - // send out response msg - if err := w.WriteMsg(rpmes); err != nil { - log.Debugf("send response error: %s", err) - return - } - } -} - -// sendRequest sends out a request, but also makes sure to -// measure the RTT for latency measurements. -func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { - - ms := dht.messageSenderForPeer(p) - - start := time.Now() - - rpmes, err := ms.SendRequest(ctx, pmes) - if err != nil { - return nil, err - } - - // update the peer (on valid msgs only) - dht.updateFromMessage(ctx, p, rpmes) - - dht.peerstore.RecordLatency(p, time.Since(start)) - log.Event(ctx, "dhtReceivedMessage", dht.self, p, rpmes) - return rpmes, nil -} - -// sendMessage sends out a message -func (dht *IpfsDHT) sendMessage(ctx context.Context, p peer.ID, pmes *pb.Message) error { - - ms := dht.messageSenderForPeer(p) - - if err := ms.SendMessage(ctx, pmes); err != nil { - return err - } - log.Event(ctx, "dhtSentMessage", dht.self, p, pmes) - return nil -} - -func (dht *IpfsDHT) updateFromMessage(ctx context.Context, p peer.ID, mes *pb.Message) error { - dht.Update(ctx, p) - return nil -} - -func (dht *IpfsDHT) messageSenderForPeer(p peer.ID) *messageSender { - dht.smlk.Lock() - defer dht.smlk.Unlock() - - ms, ok := dht.strmap[p] - if !ok { - ms = dht.newMessageSender(p) - dht.strmap[p] = ms - } - - return ms -} - -type messageSender struct { - s inet.Stream - r ggio.ReadCloser - w ggio.WriteCloser - lk sync.Mutex - p peer.ID - dht *IpfsDHT - - singleMes int -} - -func (dht *IpfsDHT) newMessageSender(p peer.ID) *messageSender { - return &messageSender{p: p, dht: dht} -} - -func (ms *messageSender) prep() error { - if ms.s != nil { - return nil - } - - nstr, err := ms.dht.host.NewStream(ms.dht.ctx, ms.p, ProtocolDHT, ProtocolDHTOld) - if err != nil { - return err - } - - ms.r = ggio.NewDelimitedReader(nstr, inet.MessageSizeMax) - ms.w = ggio.NewDelimitedWriter(nstr) - ms.s = nstr - - return nil -} - -// streamReuseTries is the number of times we will try to reuse a stream to a -// given peer before giving up and reverting to the old one-message-per-stream -// behaviour. -const streamReuseTries = 3 - -func (ms *messageSender) SendMessage(ctx context.Context, pmes *pb.Message) error { - ms.lk.Lock() - defer ms.lk.Unlock() - if err := ms.prep(); err != nil { - return err - } - - if err := ms.writeMessage(pmes); err != nil { - return err - } - - if ms.singleMes > streamReuseTries { - ms.s.Close() - ms.s = nil - } - - return nil -} - -func (ms *messageSender) writeMessage(pmes *pb.Message) error { - err := ms.w.WriteMsg(pmes) - if err != nil { - // If the other side isnt expecting us to be reusing streams, we're gonna - // end up erroring here. To make sure things work seamlessly, lets retry once - // before continuing - - log.Infof("error writing message: ", err) - ms.s.Close() - ms.s = nil - if err := ms.prep(); err != nil { - return err - } - - if err := ms.w.WriteMsg(pmes); err != nil { - return err - } - - // keep track of this happening. If it happens a few times, its - // likely we can assume the otherside will never support stream reuse - ms.singleMes++ - } - return nil -} - -func (ms *messageSender) SendRequest(ctx context.Context, pmes *pb.Message) (*pb.Message, error) { - ms.lk.Lock() - defer ms.lk.Unlock() - if err := ms.prep(); err != nil { - return nil, err - } - - if err := ms.writeMessage(pmes); err != nil { - return nil, err - } - - log.Event(ctx, "dhtSentMessage", ms.dht.self, ms.p, pmes) - - mes := new(pb.Message) - if err := ms.ctxReadMsg(ctx, mes); err != nil { - ms.s.Close() - ms.s = nil - return nil, err - } - - if ms.singleMes > streamReuseTries { - ms.s.Close() - ms.s = nil - } - - return mes, nil -} - -func (ms *messageSender) ctxReadMsg(ctx context.Context, mes *pb.Message) error { - errc := make(chan error, 1) - go func(r ggio.ReadCloser) { - errc <- r.ReadMsg(mes) - }(ms.r) - - t := time.NewTimer(dhtReadMessageTimeout) - defer t.Stop() - - select { - case err := <-errc: - return err - case <-ctx.Done(): - return ctx.Err() - case <-t.C: - return ErrReadTimeout - } -} diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go deleted file mode 100644 index 8952abec9..000000000 --- a/routing/dht/dht_test.go +++ /dev/null @@ -1,828 +0,0 @@ -package dht - -import ( - "bytes" - "fmt" - "math/rand" - "sort" - "sync" - "testing" - "time" - - routing "github.com/ipfs/go-ipfs/routing" - record "github.com/ipfs/go-ipfs/routing/record" - ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" - travisci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - - netutil "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/test/util" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" -) - -var testCaseValues = map[key.Key][]byte{} - -func init() { - testCaseValues["hello"] = []byte("world") - for i := 0; i < 100; i++ { - k := fmt.Sprintf("%d -- key", i) - v := fmt.Sprintf("%d -- value", i) - testCaseValues[key.Key(k)] = []byte(v) - } -} - -func setupDHT(ctx context.Context, t *testing.T) *IpfsDHT { - h := netutil.GenHostSwarm(t, ctx) - - dss := dssync.MutexWrap(ds.NewMapDatastore()) - d := NewDHT(ctx, h, dss) - - d.Validator["v"] = &record.ValidChecker{ - Func: func(key.Key, []byte) error { - return nil - }, - Sign: false, - } - return d -} - -func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []peer.ID, []*IpfsDHT) { - addrs := make([]ma.Multiaddr, n) - dhts := make([]*IpfsDHT, n) - peers := make([]peer.ID, n) - - sanityAddrsMap := make(map[string]struct{}) - sanityPeersMap := make(map[string]struct{}) - - for i := 0; i < n; i++ { - dhts[i] = setupDHT(ctx, t) - peers[i] = dhts[i].self - addrs[i] = dhts[i].peerstore.Addrs(dhts[i].self)[0] - - if _, lol := sanityAddrsMap[addrs[i].String()]; lol { - t.Fatal("While setting up DHTs address got duplicated.") - } else { - sanityAddrsMap[addrs[i].String()] = struct{}{} - } - if _, lol := sanityPeersMap[peers[i].String()]; lol { - t.Fatal("While setting up DHTs peerid got duplicated.") - } else { - sanityPeersMap[peers[i].String()] = struct{}{} - } - } - - return addrs, peers, dhts -} - -func connect(t *testing.T, ctx context.Context, a, b *IpfsDHT) { - - idB := b.self - addrB := b.peerstore.Addrs(idB) - if len(addrB) == 0 { - t.Fatal("peers setup incorrectly: no local address") - } - - a.peerstore.AddAddrs(idB, addrB, pstore.TempAddrTTL) - pi := pstore.PeerInfo{ID: idB} - if err := a.host.Connect(ctx, pi); err != nil { - t.Fatal(err) - } - - // loop until connection notification has been received. - // under high load, this may not happen as immediately as we would like. - for a.routingTable.Find(b.self) == "" { - time.Sleep(time.Millisecond * 5) - } - - for b.routingTable.Find(a.self) == "" { - time.Sleep(time.Millisecond * 5) - } -} - -func bootstrap(t *testing.T, ctx context.Context, dhts []*IpfsDHT) { - - ctx, cancel := context.WithCancel(ctx) - log.Debugf("Bootstrapping DHTs...") - - // tried async. sequential fares much better. compare: - // 100 async https://gist.github.com/jbenet/56d12f0578d5f34810b2 - // 100 sync https://gist.github.com/jbenet/6c59e7c15426e48aaedd - // probably because results compound - - var cfg BootstrapConfig - cfg = DefaultBootstrapConfig - cfg.Queries = 3 - - start := rand.Intn(len(dhts)) // randomize to decrease bias. - for i := range dhts { - dht := dhts[(start+i)%len(dhts)] - dht.runBootstrap(ctx, cfg) - } - cancel() -} - -func TestValueGetSet(t *testing.T) { - // t.Skip("skipping test to debug another") - - ctx := context.Background() - - dhtA := setupDHT(ctx, t) - dhtB := setupDHT(ctx, t) - - defer dhtA.Close() - defer dhtB.Close() - defer dhtA.host.Close() - defer dhtB.host.Close() - - vf := &record.ValidChecker{ - Func: func(key.Key, []byte) error { - return nil - }, - Sign: false, - } - nulsel := func(_ key.Key, bs [][]byte) (int, error) { - return 0, nil - } - - dhtA.Validator["v"] = vf - dhtB.Validator["v"] = vf - dhtA.Selector["v"] = nulsel - dhtB.Selector["v"] = nulsel - - connect(t, ctx, dhtA, dhtB) - - ctxT, _ := context.WithTimeout(ctx, time.Second) - dhtA.PutValue(ctxT, "/v/hello", []byte("world")) - - ctxT, _ = context.WithTimeout(ctx, time.Second*2) - val, err := dhtA.GetValue(ctxT, "/v/hello") - if err != nil { - t.Fatal(err) - } - - if string(val) != "world" { - t.Fatalf("Expected 'world' got '%s'", string(val)) - } - - ctxT, _ = context.WithTimeout(ctx, time.Second*2) - val, err = dhtB.GetValue(ctxT, "/v/hello") - if err != nil { - t.Fatal(err) - } - - if string(val) != "world" { - t.Fatalf("Expected 'world' got '%s'", string(val)) - } -} - -func TestProvides(t *testing.T) { - // t.Skip("skipping test to debug another") - ctx := context.Background() - - _, _, dhts := setupDHTS(ctx, 4, t) - defer func() { - for i := 0; i < 4; i++ { - dhts[i].Close() - defer dhts[i].host.Close() - } - }() - - connect(t, ctx, dhts[0], dhts[1]) - connect(t, ctx, dhts[1], dhts[2]) - connect(t, ctx, dhts[1], dhts[3]) - - for k, v := range testCaseValues { - log.Debugf("adding local values for %s = %s", k, v) - sk := dhts[3].peerstore.PrivKey(dhts[3].self) - rec, err := record.MakePutRecord(sk, k, v, false) - if err != nil { - t.Fatal(err) - } - - err = dhts[3].putLocal(k, rec) - if err != nil { - t.Fatal(err) - } - - bits, err := dhts[3].getLocal(k) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(bits.GetValue(), v) { - t.Fatalf("didn't store the right bits (%s, %s)", k, v) - } - } - - for k := range testCaseValues { - log.Debugf("announcing provider for %s", k) - if err := dhts[3].Provide(ctx, k); err != nil { - t.Fatal(err) - } - } - - // what is this timeout for? was 60ms before. - time.Sleep(time.Millisecond * 6) - - n := 0 - for k := range testCaseValues { - n = (n + 1) % 3 - - log.Debugf("getting providers for %s from %d", k, n) - ctxT, _ := context.WithTimeout(ctx, time.Second) - provchan := dhts[n].FindProvidersAsync(ctxT, k, 1) - - select { - case prov := <-provchan: - if prov.ID == "" { - t.Fatal("Got back nil provider") - } - if prov.ID != dhts[3].self { - t.Fatal("Got back wrong provider") - } - case <-ctxT.Done(): - t.Fatal("Did not get a provider back.") - } - } -} - -// if minPeers or avgPeers is 0, dont test for it. -func waitForWellFormedTables(t *testing.T, dhts []*IpfsDHT, minPeers, avgPeers int, timeout time.Duration) bool { - // test "well-formed-ness" (>= minPeers peers in every routing table) - - checkTables := func() bool { - totalPeers := 0 - for _, dht := range dhts { - rtlen := dht.routingTable.Size() - totalPeers += rtlen - if minPeers > 0 && rtlen < minPeers { - t.Logf("routing table for %s only has %d peers (should have >%d)", dht.self, rtlen, minPeers) - return false - } - } - actualAvgPeers := totalPeers / len(dhts) - t.Logf("avg rt size: %d", actualAvgPeers) - if avgPeers > 0 && actualAvgPeers < avgPeers { - t.Logf("avg rt size: %d < %d", actualAvgPeers, avgPeers) - return false - } - return true - } - - timeoutA := time.After(timeout) - for { - select { - case <-timeoutA: - log.Debugf("did not reach well-formed routing tables by %s", timeout) - return false // failed - case <-time.After(5 * time.Millisecond): - if checkTables() { - return true // succeeded - } - } - } -} - -func printRoutingTables(dhts []*IpfsDHT) { - // the routing tables should be full now. let's inspect them. - fmt.Printf("checking routing table of %d\n", len(dhts)) - for _, dht := range dhts { - fmt.Printf("checking routing table of %s\n", dht.self) - dht.routingTable.Print() - fmt.Println("") - } -} - -func TestBootstrap(t *testing.T) { - // t.Skip("skipping test to debug another") - if testing.Short() { - t.SkipNow() - } - - ctx := context.Background() - - nDHTs := 30 - _, _, dhts := setupDHTS(ctx, nDHTs, t) - defer func() { - for i := 0; i < nDHTs; i++ { - dhts[i].Close() - defer dhts[i].host.Close() - } - }() - - t.Logf("connecting %d dhts in a ring", nDHTs) - for i := 0; i < nDHTs; i++ { - connect(t, ctx, dhts[i], dhts[(i+1)%len(dhts)]) - } - - <-time.After(100 * time.Millisecond) - // bootstrap a few times until we get good tables. - stop := make(chan struct{}) - go func() { - for { - t.Logf("bootstrapping them so they find each other", nDHTs) - ctxT, _ := context.WithTimeout(ctx, 5*time.Second) - bootstrap(t, ctxT, dhts) - - select { - case <-time.After(50 * time.Millisecond): - continue // being explicit - case <-stop: - return - } - } - }() - - waitForWellFormedTables(t, dhts, 7, 10, 20*time.Second) - close(stop) - - if u.Debug { - // the routing tables should be full now. let's inspect them. - printRoutingTables(dhts) - } -} - -func TestPeriodicBootstrap(t *testing.T) { - // t.Skip("skipping test to debug another") - if ci.IsRunning() { - t.Skip("skipping on CI. highly timing dependent") - } - if testing.Short() { - t.SkipNow() - } - - ctx := context.Background() - - nDHTs := 30 - _, _, dhts := setupDHTS(ctx, nDHTs, t) - defer func() { - for i := 0; i < nDHTs; i++ { - dhts[i].Close() - defer dhts[i].host.Close() - } - }() - - // signal amplifier - amplify := func(signal chan time.Time, other []chan time.Time) { - for t := range signal { - for _, s := range other { - s <- t - } - } - for _, s := range other { - close(s) - } - } - - signal := make(chan time.Time) - allSignals := []chan time.Time{} - - var cfg BootstrapConfig - cfg = DefaultBootstrapConfig - cfg.Queries = 5 - - // kick off periodic bootstrappers with instrumented signals. - for _, dht := range dhts { - s := make(chan time.Time) - allSignals = append(allSignals, s) - dht.BootstrapOnSignal(cfg, s) - } - go amplify(signal, allSignals) - - t.Logf("dhts are not connected.", nDHTs) - for _, dht := range dhts { - rtlen := dht.routingTable.Size() - if rtlen > 0 { - t.Errorf("routing table for %s should have 0 peers. has %d", dht.self, rtlen) - } - } - - for i := 0; i < nDHTs; i++ { - connect(t, ctx, dhts[i], dhts[(i+1)%len(dhts)]) - } - - t.Logf("DHTs are now connected to 1-2 others.", nDHTs) - for _, dht := range dhts { - rtlen := dht.routingTable.Size() - if rtlen > 2 { - t.Errorf("routing table for %s should have at most 2 peers. has %d", dht.self, rtlen) - } - } - - if u.Debug { - printRoutingTables(dhts) - } - - t.Logf("bootstrapping them so they find each other", nDHTs) - signal <- time.Now() - - // this is async, and we dont know when it's finished with one cycle, so keep checking - // until the routing tables look better, or some long timeout for the failure case. - waitForWellFormedTables(t, dhts, 7, 10, 20*time.Second) - - if u.Debug { - printRoutingTables(dhts) - } -} - -func TestProvidesMany(t *testing.T) { - t.Skip("this test doesn't work") - // t.Skip("skipping test to debug another") - ctx := context.Background() - - nDHTs := 40 - _, _, dhts := setupDHTS(ctx, nDHTs, t) - defer func() { - for i := 0; i < nDHTs; i++ { - dhts[i].Close() - defer dhts[i].host.Close() - } - }() - - t.Logf("connecting %d dhts in a ring", nDHTs) - for i := 0; i < nDHTs; i++ { - connect(t, ctx, dhts[i], dhts[(i+1)%len(dhts)]) - } - - <-time.After(100 * time.Millisecond) - t.Logf("bootstrapping them so they find each other", nDHTs) - ctxT, _ := context.WithTimeout(ctx, 20*time.Second) - bootstrap(t, ctxT, dhts) - - if u.Debug { - // the routing tables should be full now. let's inspect them. - t.Logf("checking routing table of %d", nDHTs) - for _, dht := range dhts { - fmt.Printf("checking routing table of %s\n", dht.self) - dht.routingTable.Print() - fmt.Println("") - } - } - - var providers = map[key.Key]peer.ID{} - - d := 0 - for k, v := range testCaseValues { - d = (d + 1) % len(dhts) - dht := dhts[d] - providers[k] = dht.self - - t.Logf("adding local values for %s = %s (on %s)", k, v, dht.self) - rec, err := record.MakePutRecord(nil, k, v, false) - if err != nil { - t.Fatal(err) - } - - err = dht.putLocal(k, rec) - if err != nil { - t.Fatal(err) - } - - bits, err := dht.getLocal(k) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(bits.GetValue(), v) { - t.Fatalf("didn't store the right bits (%s, %s)", k, v) - } - - t.Logf("announcing provider for %s", k) - if err := dht.Provide(ctx, k); err != nil { - t.Fatal(err) - } - } - - // what is this timeout for? was 60ms before. - time.Sleep(time.Millisecond * 6) - - errchan := make(chan error) - - ctxT, _ = context.WithTimeout(ctx, 5*time.Second) - - var wg sync.WaitGroup - getProvider := func(dht *IpfsDHT, k key.Key) { - defer wg.Done() - - expected := providers[k] - - provchan := dht.FindProvidersAsync(ctxT, k, 1) - select { - case prov := <-provchan: - actual := prov.ID - if actual == "" { - errchan <- fmt.Errorf("Got back nil provider (%s at %s)", k, dht.self) - } else if actual != expected { - errchan <- fmt.Errorf("Got back wrong provider (%s != %s) (%s at %s)", - expected, actual, k, dht.self) - } - case <-ctxT.Done(): - errchan <- fmt.Errorf("Did not get a provider back (%s at %s)", k, dht.self) - } - } - - for k := range testCaseValues { - // everyone should be able to find it... - for _, dht := range dhts { - log.Debugf("getting providers for %s at %s", k, dht.self) - wg.Add(1) - go getProvider(dht, k) - } - } - - // we need this because of printing errors - go func() { - wg.Wait() - close(errchan) - }() - - for err := range errchan { - t.Error(err) - } -} - -func TestProvidesAsync(t *testing.T) { - // t.Skip("skipping test to debug another") - if testing.Short() { - t.SkipNow() - } - - ctx := context.Background() - - _, _, dhts := setupDHTS(ctx, 4, t) - defer func() { - for i := 0; i < 4; i++ { - dhts[i].Close() - defer dhts[i].host.Close() - } - }() - - connect(t, ctx, dhts[0], dhts[1]) - connect(t, ctx, dhts[1], dhts[2]) - connect(t, ctx, dhts[1], dhts[3]) - - k := key.Key("hello") - val := []byte("world") - sk := dhts[3].peerstore.PrivKey(dhts[3].self) - rec, err := record.MakePutRecord(sk, k, val, false) - if err != nil { - t.Fatal(err) - } - - err = dhts[3].putLocal(k, rec) - if err != nil { - t.Fatal(err) - } - - bits, err := dhts[3].getLocal(k) - if err != nil && bytes.Equal(bits.GetValue(), val) { - t.Fatal(err) - } - - err = dhts[3].Provide(ctx, key.Key("hello")) - if err != nil { - t.Fatal(err) - } - - time.Sleep(time.Millisecond * 60) - - ctxT, _ := context.WithTimeout(ctx, time.Millisecond*300) - provs := dhts[0].FindProvidersAsync(ctxT, key.Key("hello"), 5) - select { - case p, ok := <-provs: - if !ok { - t.Fatal("Provider channel was closed...") - } - if p.ID == "" { - t.Fatal("Got back nil provider!") - } - if p.ID != dhts[3].self { - t.Fatalf("got a provider, but not the right one. %s", p) - } - case <-ctxT.Done(): - t.Fatal("Didnt get back providers") - } -} - -func TestLayeredGet(t *testing.T) { - // t.Skip("skipping test to debug another") - if testing.Short() { - t.SkipNow() - } - - ctx := context.Background() - - _, _, dhts := setupDHTS(ctx, 4, t) - defer func() { - for i := 0; i < 4; i++ { - dhts[i].Close() - defer dhts[i].host.Close() - } - }() - - connect(t, ctx, dhts[0], dhts[1]) - connect(t, ctx, dhts[1], dhts[2]) - connect(t, ctx, dhts[1], dhts[3]) - - err := dhts[3].Provide(ctx, key.Key("/v/hello")) - if err != nil { - t.Fatal(err) - } - - time.Sleep(time.Millisecond * 6) - - t.Log("interface was changed. GetValue should not use providers.") - ctxT, _ := context.WithTimeout(ctx, time.Second) - val, err := dhts[0].GetValue(ctxT, key.Key("/v/hello")) - if err != routing.ErrNotFound { - t.Error(err) - } - if string(val) == "world" { - t.Error("should not get value.") - } - if len(val) > 0 && string(val) != "world" { - t.Error("worse, there's a value and its not even the right one.") - } -} - -func TestFindPeer(t *testing.T) { - // t.Skip("skipping test to debug another") - if testing.Short() { - t.SkipNow() - } - - ctx := context.Background() - - _, peers, dhts := setupDHTS(ctx, 4, t) - defer func() { - for i := 0; i < 4; i++ { - dhts[i].Close() - dhts[i].host.Close() - } - }() - - connect(t, ctx, dhts[0], dhts[1]) - connect(t, ctx, dhts[1], dhts[2]) - connect(t, ctx, dhts[1], dhts[3]) - - ctxT, _ := context.WithTimeout(ctx, time.Second) - p, err := dhts[0].FindPeer(ctxT, peers[2]) - if err != nil { - t.Fatal(err) - } - - if p.ID == "" { - t.Fatal("Failed to find peer.") - } - - if p.ID != peers[2] { - t.Fatal("Didnt find expected peer.") - } -} - -func TestFindPeersConnectedToPeer(t *testing.T) { - t.Skip("not quite correct (see note)") - - if testing.Short() { - t.SkipNow() - } - - ctx := context.Background() - - _, peers, dhts := setupDHTS(ctx, 4, t) - defer func() { - for i := 0; i < 4; i++ { - dhts[i].Close() - dhts[i].host.Close() - } - }() - - // topology: - // 0-1, 1-2, 1-3, 2-3 - connect(t, ctx, dhts[0], dhts[1]) - connect(t, ctx, dhts[1], dhts[2]) - connect(t, ctx, dhts[1], dhts[3]) - connect(t, ctx, dhts[2], dhts[3]) - - // fmt.Println("0 is", peers[0]) - // fmt.Println("1 is", peers[1]) - // fmt.Println("2 is", peers[2]) - // fmt.Println("3 is", peers[3]) - - ctxT, _ := context.WithTimeout(ctx, time.Second) - pchan, err := dhts[0].FindPeersConnectedToPeer(ctxT, peers[2]) - if err != nil { - t.Fatal(err) - } - - // shouldFind := []peer.ID{peers[1], peers[3]} - var found []pstore.PeerInfo - for nextp := range pchan { - found = append(found, nextp) - } - - // fmt.Printf("querying 0 (%s) FindPeersConnectedToPeer 2 (%s)\n", peers[0], peers[2]) - // fmt.Println("should find 1, 3", shouldFind) - // fmt.Println("found", found) - - // testPeerListsMatch(t, shouldFind, found) - - log.Warning("TestFindPeersConnectedToPeer is not quite correct") - if len(found) == 0 { - t.Fatal("didn't find any peers.") - } -} - -func testPeerListsMatch(t *testing.T, p1, p2 []peer.ID) { - - if len(p1) != len(p2) { - t.Fatal("did not find as many peers as should have", p1, p2) - } - - ids1 := make([]string, len(p1)) - ids2 := make([]string, len(p2)) - - for i, p := range p1 { - ids1[i] = string(p) - } - - for i, p := range p2 { - ids2[i] = string(p) - } - - sort.Sort(sort.StringSlice(ids1)) - sort.Sort(sort.StringSlice(ids2)) - - for i := range ids1 { - if ids1[i] != ids2[i] { - t.Fatal("Didnt find expected peer", ids1[i], ids2) - } - } -} - -func TestConnectCollision(t *testing.T) { - // t.Skip("skipping test to debug another") - if testing.Short() { - t.SkipNow() - } - if travisci.IsRunning() { - t.Skip("Skipping on Travis-CI.") - } - - runTimes := 10 - - for rtime := 0; rtime < runTimes; rtime++ { - log.Info("Running Time: ", rtime) - - ctx := context.Background() - - dhtA := setupDHT(ctx, t) - dhtB := setupDHT(ctx, t) - - addrA := dhtA.peerstore.Addrs(dhtA.self)[0] - addrB := dhtB.peerstore.Addrs(dhtB.self)[0] - - peerA := dhtA.self - peerB := dhtB.self - - errs := make(chan error) - go func() { - dhtA.peerstore.AddAddr(peerB, addrB, pstore.TempAddrTTL) - pi := pstore.PeerInfo{ID: peerB} - err := dhtA.host.Connect(ctx, pi) - errs <- err - }() - go func() { - dhtB.peerstore.AddAddr(peerA, addrA, pstore.TempAddrTTL) - pi := pstore.PeerInfo{ID: peerA} - err := dhtB.host.Connect(ctx, pi) - errs <- err - }() - - timeout := time.After(5 * time.Second) - select { - case e := <-errs: - if e != nil { - t.Fatal(e) - } - case <-timeout: - t.Fatal("Timeout received!") - } - select { - case e := <-errs: - if e != nil { - t.Fatal(e) - } - case <-timeout: - t.Fatal("Timeout received!") - } - - dhtA.Close() - dhtB.Close() - dhtA.host.Close() - dhtB.host.Close() - } -} diff --git a/routing/dht/ext_test.go b/routing/dht/ext_test.go deleted file mode 100644 index d600e83dd..000000000 --- a/routing/dht/ext_test.go +++ /dev/null @@ -1,290 +0,0 @@ -package dht - -import ( - "io" - "math/rand" - "testing" - "time" - - routing "github.com/ipfs/go-ipfs/routing" - pb "github.com/ipfs/go-ipfs/routing/dht/pb" - record "github.com/ipfs/go-ipfs/routing/record" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - - inet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net" - mocknet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net/mock" - ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" -) - -func TestGetFailures(t *testing.T) { - if testing.Short() { - t.SkipNow() - } - - ctx := context.Background() - mn, err := mocknet.FullMeshConnected(ctx, 2) - if err != nil { - t.Fatal(err) - } - hosts := mn.Hosts() - - tsds := dssync.MutexWrap(ds.NewMapDatastore()) - d := NewDHT(ctx, hosts[0], tsds) - d.Update(ctx, hosts[1].ID()) - - // Reply with failures to every message - hosts[1].SetStreamHandler(ProtocolDHT, func(s inet.Stream) { - s.Close() - }) - - // This one should time out - ctx1, _ := context.WithTimeout(context.Background(), 200*time.Millisecond) - if _, err := d.GetValue(ctx1, key.Key("test")); err != nil { - if merr, ok := err.(u.MultiErr); ok && len(merr) > 0 { - err = merr[0] - } - - if err != io.EOF { - t.Fatal("Got different error than we expected", err) - } - } else { - t.Fatal("Did not get expected error!") - } - - t.Log("Timeout test passed.") - - // Reply with failures to every message - hosts[1].SetStreamHandler(ProtocolDHT, func(s inet.Stream) { - defer s.Close() - - pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) - pbw := ggio.NewDelimitedWriter(s) - - pmes := new(pb.Message) - if err := pbr.ReadMsg(pmes); err != nil { - panic(err) - } - - resp := &pb.Message{ - Type: pmes.Type, - } - if err := pbw.WriteMsg(resp); err != nil { - panic(err) - } - }) - - // This one should fail with NotFound. - // long context timeout to ensure we dont end too early. - // the dht should be exhausting its query and returning not found. - // (was 3 seconds before which should be _plenty_ of time, but maybe - // travis machines really have a hard time...) - ctx2, _ := context.WithTimeout(context.Background(), 20*time.Second) - _, err = d.GetValue(ctx2, key.Key("test")) - if err != nil { - if merr, ok := err.(u.MultiErr); ok && len(merr) > 0 { - err = merr[0] - } - if err != routing.ErrNotFound { - t.Fatalf("Expected ErrNotFound, got: %s", err) - } - } else { - t.Fatal("expected error, got none.") - } - - t.Log("ErrNotFound check passed!") - - // Now we test this DHT's handleGetValue failure - { - typ := pb.Message_GET_VALUE - str := "hello" - - sk, err := d.getOwnPrivateKey() - if err != nil { - t.Fatal(err) - } - - rec, err := record.MakePutRecord(sk, key.Key(str), []byte("blah"), true) - if err != nil { - t.Fatal(err) - } - req := pb.Message{ - Type: &typ, - Key: &str, - Record: rec, - } - - s, err := hosts[1].NewStream(context.Background(), hosts[0].ID(), ProtocolDHT) - if err != nil { - t.Fatal(err) - } - defer s.Close() - - pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) - pbw := ggio.NewDelimitedWriter(s) - - if err := pbw.WriteMsg(&req); err != nil { - t.Fatal(err) - } - - pmes := new(pb.Message) - if err := pbr.ReadMsg(pmes); err != nil { - t.Fatal(err) - } - if pmes.GetRecord() != nil { - t.Fatal("shouldnt have value") - } - if pmes.GetProviderPeers() != nil { - t.Fatal("shouldnt have provider peers") - } - } -} - -func TestNotFound(t *testing.T) { - // t.Skip("skipping test to debug another") - if testing.Short() { - t.SkipNow() - } - - ctx := context.Background() - mn, err := mocknet.FullMeshConnected(ctx, 16) - if err != nil { - t.Fatal(err) - } - hosts := mn.Hosts() - tsds := dssync.MutexWrap(ds.NewMapDatastore()) - d := NewDHT(ctx, hosts[0], tsds) - - for _, p := range hosts { - d.Update(ctx, p.ID()) - } - - // Reply with random peers to every message - for _, host := range hosts { - host := host // shadow loop var - host.SetStreamHandler(ProtocolDHT, func(s inet.Stream) { - defer s.Close() - - pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) - pbw := ggio.NewDelimitedWriter(s) - - pmes := new(pb.Message) - if err := pbr.ReadMsg(pmes); err != nil { - panic(err) - } - - switch pmes.GetType() { - case pb.Message_GET_VALUE: - resp := &pb.Message{Type: pmes.Type} - - ps := []pstore.PeerInfo{} - for i := 0; i < 7; i++ { - p := hosts[rand.Intn(len(hosts))].ID() - pi := host.Peerstore().PeerInfo(p) - ps = append(ps, pi) - } - - resp.CloserPeers = pb.PeerInfosToPBPeers(d.host.Network(), ps) - if err := pbw.WriteMsg(resp); err != nil { - panic(err) - } - - default: - panic("Shouldnt recieve this.") - } - }) - } - - // long timeout to ensure timing is not at play. - ctx, cancel := context.WithTimeout(ctx, time.Second*20) - defer cancel() - v, err := d.GetValue(ctx, key.Key("hello")) - log.Debugf("get value got %v", v) - if err != nil { - if merr, ok := err.(u.MultiErr); ok && len(merr) > 0 { - err = merr[0] - } - switch err { - case routing.ErrNotFound: - //Success! - return - case u.ErrTimeout: - t.Fatal("Should not have gotten timeout!") - default: - t.Fatalf("Got unexpected error: %s", err) - } - } - t.Fatal("Expected to recieve an error.") -} - -// If less than K nodes are in the entire network, it should fail when we make -// a GET rpc and nobody has the value -func TestLessThanKResponses(t *testing.T) { - // t.Skip("skipping test to debug another") - // t.Skip("skipping test because it makes a lot of output") - - ctx := context.Background() - mn, err := mocknet.FullMeshConnected(ctx, 6) - if err != nil { - t.Fatal(err) - } - hosts := mn.Hosts() - - tsds := dssync.MutexWrap(ds.NewMapDatastore()) - d := NewDHT(ctx, hosts[0], tsds) - - for i := 1; i < 5; i++ { - d.Update(ctx, hosts[i].ID()) - } - - // Reply with random peers to every message - for _, host := range hosts { - host := host // shadow loop var - host.SetStreamHandler(ProtocolDHT, func(s inet.Stream) { - defer s.Close() - - pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) - pbw := ggio.NewDelimitedWriter(s) - - pmes := new(pb.Message) - if err := pbr.ReadMsg(pmes); err != nil { - panic(err) - } - - switch pmes.GetType() { - case pb.Message_GET_VALUE: - pi := host.Peerstore().PeerInfo(hosts[1].ID()) - resp := &pb.Message{ - Type: pmes.Type, - CloserPeers: pb.PeerInfosToPBPeers(d.host.Network(), []pstore.PeerInfo{pi}), - } - - if err := pbw.WriteMsg(resp); err != nil { - panic(err) - } - default: - panic("Shouldnt recieve this.") - } - - }) - } - - ctx, cancel := context.WithTimeout(ctx, time.Second*30) - defer cancel() - if _, err := d.GetValue(ctx, key.Key("hello")); err != nil { - switch err { - case routing.ErrNotFound: - //Success! - return - case u.ErrTimeout: - t.Fatal("Should not have gotten timeout!") - default: - t.Fatalf("Got unexpected error: %s", err) - } - } - t.Fatal("Expected to recieve an error.") -} diff --git a/routing/dht/handlers.go b/routing/dht/handlers.go deleted file mode 100644 index ea539a95d..000000000 --- a/routing/dht/handlers.go +++ /dev/null @@ -1,288 +0,0 @@ -package dht - -import ( - "errors" - "fmt" - "time" - - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - - pb "github.com/ipfs/go-ipfs/routing/dht/pb" - lgbl "gx/ipfs/QmYrv4LgCC8FhG2Ab4bwuq5DqBdwMtx3hMb3KKJDZcr2d7/go-libp2p-loggables" - - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" -) - -// The number of closer peers to send on requests. -var CloserPeerCount = KValue - -// dhthandler specifies the signature of functions that handle DHT messages. -type dhtHandler func(context.Context, peer.ID, *pb.Message) (*pb.Message, error) - -func (dht *IpfsDHT) handlerForMsgType(t pb.Message_MessageType) dhtHandler { - switch t { - case pb.Message_GET_VALUE: - return dht.handleGetValue - case pb.Message_PUT_VALUE: - return dht.handlePutValue - case pb.Message_FIND_NODE: - return dht.handleFindPeer - case pb.Message_ADD_PROVIDER: - return dht.handleAddProvider - case pb.Message_GET_PROVIDERS: - return dht.handleGetProviders - case pb.Message_PING: - return dht.handlePing - default: - return nil - } -} - -func (dht *IpfsDHT) handleGetValue(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { - defer log.EventBegin(ctx, "handleGetValue", p).Done() - log.Debugf("%s handleGetValue for key: %s", dht.self, pmes.GetKey()) - - // setup response - resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) - - // first, is there even a key? - k := key.Key(pmes.GetKey()) - if k == "" { - return nil, errors.New("handleGetValue but no key was provided") - // TODO: send back an error response? could be bad, but the other node's hanging. - } - - rec, err := dht.checkLocalDatastore(k) - if err != nil { - return nil, err - } - resp.Record = rec - - // Find closest peer on given cluster to desired key and reply with that info - closer := dht.betterPeersToQuery(pmes, p, CloserPeerCount) - if len(closer) > 0 { - closerinfos := pstore.PeerInfos(dht.peerstore, closer) - for _, pi := range closerinfos { - log.Debugf("handleGetValue returning closer peer: '%s'", pi.ID) - if len(pi.Addrs) < 1 { - log.Errorf(`no addresses on peer being sent! - [local:%s] - [sending:%s] - [remote:%s]`, dht.self, pi.ID, p) - } - } - - resp.CloserPeers = pb.PeerInfosToPBPeers(dht.host.Network(), closerinfos) - } - - return resp, nil -} - -func (dht *IpfsDHT) checkLocalDatastore(k key.Key) (*pb.Record, error) { - log.Debugf("%s handleGetValue looking into ds", dht.self) - dskey := k.DsKey() - iVal, err := dht.datastore.Get(dskey) - log.Debugf("%s handleGetValue looking into ds GOT %v", dht.self, iVal) - - if err == ds.ErrNotFound { - return nil, nil - } - - // if we got an unexpected error, bail. - if err != nil { - return nil, err - } - - // if we have the value, send it back - log.Debugf("%s handleGetValue success!", dht.self) - - byts, ok := iVal.([]byte) - if !ok { - return nil, fmt.Errorf("datastore had non byte-slice value for %v", dskey) - } - - rec := new(pb.Record) - err = proto.Unmarshal(byts, rec) - if err != nil { - log.Debug("failed to unmarshal DHT record from datastore") - return nil, err - } - - // if its our record, dont bother checking the times on it - if peer.ID(rec.GetAuthor()) == dht.self { - return rec, nil - } - - var recordIsBad bool - recvtime, err := u.ParseRFC3339(rec.GetTimeReceived()) - if err != nil { - log.Info("either no receive time set on record, or it was invalid: ", err) - recordIsBad = true - } - - if time.Now().Sub(recvtime) > MaxRecordAge { - log.Debug("old record found, tossing.") - recordIsBad = true - } - - // NOTE: We do not verify the record here beyond checking these timestamps. - // we put the burden of checking the records on the requester as checking a record - // may be computationally expensive - - if recordIsBad { - err := dht.datastore.Delete(dskey) - if err != nil { - log.Error("Failed to delete bad record from datastore: ", err) - } - - return nil, nil // can treat this as not having the record at all - } - - return rec, nil -} - -// Store a value in this peer local storage -func (dht *IpfsDHT) handlePutValue(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { - defer log.EventBegin(ctx, "handlePutValue", p).Done() - dskey := key.Key(pmes.GetKey()).DsKey() - - if err := dht.verifyRecordLocally(pmes.GetRecord()); err != nil { - log.Warningf("Bad dht record in PUT from: %s. %s", key.Key(pmes.GetRecord().GetAuthor()), err) - return nil, err - } - - rec := pmes.GetRecord() - - // record the time we receive every record - rec.TimeReceived = proto.String(u.FormatRFC3339(time.Now())) - - data, err := proto.Marshal(rec) - if err != nil { - return nil, err - } - - err = dht.datastore.Put(dskey, data) - log.Debugf("%s handlePutValue %v", dht.self, dskey) - return pmes, err -} - -func (dht *IpfsDHT) handlePing(_ context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { - log.Debugf("%s Responding to ping from %s!\n", dht.self, p) - return pmes, nil -} - -func (dht *IpfsDHT) handleFindPeer(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { - defer log.EventBegin(ctx, "handleFindPeer", p).Done() - resp := pb.NewMessage(pmes.GetType(), "", pmes.GetClusterLevel()) - var closest []peer.ID - - // if looking for self... special case where we send it on CloserPeers. - if peer.ID(pmes.GetKey()) == dht.self { - closest = []peer.ID{dht.self} - } else { - closest = dht.betterPeersToQuery(pmes, p, CloserPeerCount) - } - - if closest == nil { - log.Infof("%s handleFindPeer %s: could not find anything.", dht.self, p) - return resp, nil - } - - var withAddresses []pstore.PeerInfo - closestinfos := pstore.PeerInfos(dht.peerstore, closest) - for _, pi := range closestinfos { - if len(pi.Addrs) > 0 { - withAddresses = append(withAddresses, pi) - log.Debugf("handleFindPeer: sending back '%s'", pi.ID) - } - } - - resp.CloserPeers = pb.PeerInfosToPBPeers(dht.host.Network(), withAddresses) - return resp, nil -} - -func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { - lm := make(lgbl.DeferredMap) - lm["peer"] = func() interface{} { return p.Pretty() } - defer log.EventBegin(ctx, "handleGetProviders", lm).Done() - - resp := pb.NewMessage(pmes.GetType(), pmes.GetKey(), pmes.GetClusterLevel()) - key := key.Key(pmes.GetKey()) - lm["key"] = func() interface{} { return key.B58String() } - - // debug logging niceness. - reqDesc := fmt.Sprintf("%s handleGetProviders(%s, %s): ", dht.self, p, key) - log.Debugf("%s begin", reqDesc) - defer log.Debugf("%s end", reqDesc) - - // check if we have this value, to add ourselves as provider. - has, err := dht.datastore.Has(key.DsKey()) - if err != nil && err != ds.ErrNotFound { - log.Debugf("unexpected datastore error: %v\n", err) - has = false - } - - // setup providers - providers := dht.providers.GetProviders(ctx, key) - if has { - providers = append(providers, dht.self) - log.Debugf("%s have the value. added self as provider", reqDesc) - } - - if providers != nil && len(providers) > 0 { - infos := pstore.PeerInfos(dht.peerstore, providers) - resp.ProviderPeers = pb.PeerInfosToPBPeers(dht.host.Network(), infos) - log.Debugf("%s have %d providers: %s", reqDesc, len(providers), infos) - } - - // Also send closer peers. - closer := dht.betterPeersToQuery(pmes, p, CloserPeerCount) - if closer != nil { - infos := pstore.PeerInfos(dht.peerstore, closer) - resp.CloserPeers = pb.PeerInfosToPBPeers(dht.host.Network(), infos) - log.Debugf("%s have %d closer peers: %s", reqDesc, len(closer), infos) - } - - return resp, nil -} - -func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { - lm := make(lgbl.DeferredMap) - lm["peer"] = func() interface{} { return p.Pretty() } - - defer log.EventBegin(ctx, "handleAddProvider", lm).Done() - key := key.Key(pmes.GetKey()) - lm["key"] = func() interface{} { return key.B58String() } - - log.Debugf("%s adding %s as a provider for '%s'\n", dht.self, p, key) - - // add provider should use the address given in the message - pinfos := pb.PBPeersToPeerInfos(pmes.GetProviderPeers()) - for _, pi := range pinfos { - if pi.ID != p { - // we should ignore this provider reccord! not from originator. - // (we chould sign them and check signature later...) - log.Debugf("handleAddProvider received provider %s from %s. Ignore.", pi.ID, p) - continue - } - - if len(pi.Addrs) < 1 { - log.Debugf("%s got no valid addresses for provider %s. Ignore.", dht.self, p) - continue - } - - log.Infof("received provider %s for %s (addrs: %s)", p, key, pi.Addrs) - if pi.ID != dht.self { // dont add own addrs. - // add the received addresses to our peerstore. - dht.peerstore.AddAddrs(pi.ID, pi.Addrs, pstore.ProviderAddrTTL) - } - dht.providers.AddProvider(ctx, key, p) - } - - return nil, nil -} diff --git a/routing/dht/lookup.go b/routing/dht/lookup.go deleted file mode 100644 index 2ff6306cc..000000000 --- a/routing/dht/lookup.go +++ /dev/null @@ -1,112 +0,0 @@ -package dht - -import ( - pset "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer/peerset" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - - notif "github.com/ipfs/go-ipfs/notifications" - kb "github.com/ipfs/go-ipfs/routing/kbucket" - - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" -) - -// Required in order for proper JSON marshaling -func pointerizePeerInfos(pis []pstore.PeerInfo) []*pstore.PeerInfo { - out := make([]*pstore.PeerInfo, len(pis)) - for i, p := range pis { - np := p - out[i] = &np - } - return out -} - -// Kademlia 'node lookup' operation. Returns a channel of the K closest peers -// to the given key -func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key key.Key) (<-chan peer.ID, error) { - e := log.EventBegin(ctx, "getClosestPeers", &key) - tablepeers := dht.routingTable.NearestPeers(kb.ConvertKey(key), KValue) - if len(tablepeers) == 0 { - return nil, kb.ErrLookupFailure - } - - out := make(chan peer.ID, KValue) - peerset := pset.NewLimited(KValue) - - for _, p := range tablepeers { - select { - case out <- p: - case <-ctx.Done(): - return nil, ctx.Err() - } - peerset.Add(p) - } - - // since the query doesnt actually pass our context down - // we have to hack this here. whyrusleeping isnt a huge fan of goprocess - parent := ctx - query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { - // For DHT query command - notif.PublishQueryEvent(parent, ¬if.QueryEvent{ - Type: notif.SendingQuery, - ID: p, - }) - - closer, err := dht.closerPeersSingle(ctx, key, p) - if err != nil { - log.Debugf("error getting closer peers: %s", err) - return nil, err - } - - var filtered []pstore.PeerInfo - for _, clp := range closer { - if kb.Closer(clp, dht.self, key) && peerset.TryAdd(clp) { - select { - case out <- clp: - case <-ctx.Done(): - return nil, ctx.Err() - } - filtered = append(filtered, dht.peerstore.PeerInfo(clp)) - } - } - - // For DHT query command - notif.PublishQueryEvent(parent, ¬if.QueryEvent{ - Type: notif.PeerResponse, - ID: p, - Responses: pointerizePeerInfos(filtered), - }) - - return &dhtQueryResult{closerPeers: filtered}, nil - }) - - go func() { - defer close(out) - defer e.Done() - // run it! - _, err := query.Run(ctx, tablepeers) - if err != nil { - log.Debugf("closestPeers query run error: %s", err) - } - }() - - return out, nil -} - -func (dht *IpfsDHT) closerPeersSingle(ctx context.Context, key key.Key, p peer.ID) ([]peer.ID, error) { - pmes, err := dht.findPeerSingle(ctx, p, peer.ID(key)) - if err != nil { - return nil, err - } - - var out []peer.ID - for _, pbp := range pmes.GetCloserPeers() { - pid := peer.ID(pbp.GetId()) - if pid != dht.self { // dont add self - dht.peerstore.AddAddrs(pid, pbp.Addresses(), pstore.TempAddrTTL) - out = append(out, pid) - } - } - return out, nil -} diff --git a/routing/dht/notif.go b/routing/dht/notif.go deleted file mode 100644 index 57a3b8f2c..000000000 --- a/routing/dht/notif.go +++ /dev/null @@ -1,39 +0,0 @@ -package dht - -import ( - ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - - inet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net" -) - -// netNotifiee defines methods to be used with the IpfsDHT -type netNotifiee IpfsDHT - -func (nn *netNotifiee) DHT() *IpfsDHT { - return (*IpfsDHT)(nn) -} - -func (nn *netNotifiee) Connected(n inet.Network, v inet.Conn) { - dht := nn.DHT() - select { - case <-dht.Process().Closing(): - return - default: - } - dht.Update(dht.Context(), v.RemotePeer()) -} - -func (nn *netNotifiee) Disconnected(n inet.Network, v inet.Conn) { - dht := nn.DHT() - select { - case <-dht.Process().Closing(): - return - default: - } - dht.routingTable.Remove(v.RemotePeer()) -} - -func (nn *netNotifiee) OpenedStream(n inet.Network, v inet.Stream) {} -func (nn *netNotifiee) ClosedStream(n inet.Network, v inet.Stream) {} -func (nn *netNotifiee) Listen(n inet.Network, a ma.Multiaddr) {} -func (nn *netNotifiee) ListenClose(n inet.Network, a ma.Multiaddr) {} diff --git a/routing/dht/pb/Makefile b/routing/dht/pb/Makefile deleted file mode 100644 index 08ac883d0..000000000 --- a/routing/dht/pb/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -PB = $(wildcard *.proto) -GO = $(PB:.proto=.pb.go) - -all: $(GO) - -%.pb.go: %.proto - protoc --gogo_out=. --proto_path=../../../../../../:/usr/local/opt/protobuf/include:. $< - -clean: - rm -f *.pb.go - rm -f *.go diff --git a/routing/dht/pb/dht.pb.go b/routing/dht/pb/dht.pb.go deleted file mode 100644 index 24dc2e5be..000000000 --- a/routing/dht/pb/dht.pb.go +++ /dev/null @@ -1,272 +0,0 @@ -// Code generated by protoc-gen-gogo. -// source: dht.proto -// DO NOT EDIT! - -/* -Package dht_pb is a generated protocol buffer package. - -It is generated from these files: - dht.proto - -It has these top-level messages: - Message - Record -*/ -package dht_pb - -import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = math.Inf - -type Message_MessageType int32 - -const ( - Message_PUT_VALUE Message_MessageType = 0 - Message_GET_VALUE Message_MessageType = 1 - Message_ADD_PROVIDER Message_MessageType = 2 - Message_GET_PROVIDERS Message_MessageType = 3 - Message_FIND_NODE Message_MessageType = 4 - Message_PING Message_MessageType = 5 -) - -var Message_MessageType_name = map[int32]string{ - 0: "PUT_VALUE", - 1: "GET_VALUE", - 2: "ADD_PROVIDER", - 3: "GET_PROVIDERS", - 4: "FIND_NODE", - 5: "PING", -} -var Message_MessageType_value = map[string]int32{ - "PUT_VALUE": 0, - "GET_VALUE": 1, - "ADD_PROVIDER": 2, - "GET_PROVIDERS": 3, - "FIND_NODE": 4, - "PING": 5, -} - -func (x Message_MessageType) Enum() *Message_MessageType { - p := new(Message_MessageType) - *p = x - return p -} -func (x Message_MessageType) String() string { - return proto.EnumName(Message_MessageType_name, int32(x)) -} -func (x *Message_MessageType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(Message_MessageType_value, data, "Message_MessageType") - if err != nil { - return err - } - *x = Message_MessageType(value) - return nil -} - -type Message_ConnectionType int32 - -const ( - // sender does not have a connection to peer, and no extra information (default) - Message_NOT_CONNECTED Message_ConnectionType = 0 - // sender has a live connection to peer - Message_CONNECTED Message_ConnectionType = 1 - // sender recently connected to peer - Message_CAN_CONNECT Message_ConnectionType = 2 - // sender recently tried to connect to peer repeatedly but failed to connect - // ("try" here is loose, but this should signal "made strong effort, failed") - Message_CANNOT_CONNECT Message_ConnectionType = 3 -) - -var Message_ConnectionType_name = map[int32]string{ - 0: "NOT_CONNECTED", - 1: "CONNECTED", - 2: "CAN_CONNECT", - 3: "CANNOT_CONNECT", -} -var Message_ConnectionType_value = map[string]int32{ - "NOT_CONNECTED": 0, - "CONNECTED": 1, - "CAN_CONNECT": 2, - "CANNOT_CONNECT": 3, -} - -func (x Message_ConnectionType) Enum() *Message_ConnectionType { - p := new(Message_ConnectionType) - *p = x - return p -} -func (x Message_ConnectionType) String() string { - return proto.EnumName(Message_ConnectionType_name, int32(x)) -} -func (x *Message_ConnectionType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(Message_ConnectionType_value, data, "Message_ConnectionType") - if err != nil { - return err - } - *x = Message_ConnectionType(value) - return nil -} - -type Message struct { - // defines what type of message it is. - Type *Message_MessageType `protobuf:"varint,1,opt,name=type,enum=dht.pb.Message_MessageType" json:"type,omitempty"` - // defines what coral cluster level this query/response belongs to. - ClusterLevelRaw *int32 `protobuf:"varint,10,opt,name=clusterLevelRaw" json:"clusterLevelRaw,omitempty"` - // Used to specify the key associated with this message. - // PUT_VALUE, GET_VALUE, ADD_PROVIDER, GET_PROVIDERS - Key *string `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` - // Used to return a value - // PUT_VALUE, GET_VALUE - Record *Record `protobuf:"bytes,3,opt,name=record" json:"record,omitempty"` - // Used to return peers closer to a key in a query - // GET_VALUE, GET_PROVIDERS, FIND_NODE - CloserPeers []*Message_Peer `protobuf:"bytes,8,rep,name=closerPeers" json:"closerPeers,omitempty"` - // Used to return Providers - // GET_VALUE, ADD_PROVIDER, GET_PROVIDERS - ProviderPeers []*Message_Peer `protobuf:"bytes,9,rep,name=providerPeers" json:"providerPeers,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Message) Reset() { *m = Message{} } -func (m *Message) String() string { return proto.CompactTextString(m) } -func (*Message) ProtoMessage() {} - -func (m *Message) GetType() Message_MessageType { - if m != nil && m.Type != nil { - return *m.Type - } - return Message_PUT_VALUE -} - -func (m *Message) GetClusterLevelRaw() int32 { - if m != nil && m.ClusterLevelRaw != nil { - return *m.ClusterLevelRaw - } - return 0 -} - -func (m *Message) GetKey() string { - if m != nil && m.Key != nil { - return *m.Key - } - return "" -} - -func (m *Message) GetRecord() *Record { - if m != nil { - return m.Record - } - return nil -} - -func (m *Message) GetCloserPeers() []*Message_Peer { - if m != nil { - return m.CloserPeers - } - return nil -} - -func (m *Message) GetProviderPeers() []*Message_Peer { - if m != nil { - return m.ProviderPeers - } - return nil -} - -type Message_Peer struct { - // ID of a given peer. - Id *string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` - // multiaddrs for a given peer - Addrs [][]byte `protobuf:"bytes,2,rep,name=addrs" json:"addrs,omitempty"` - // used to signal the sender's connection capabilities to the peer - Connection *Message_ConnectionType `protobuf:"varint,3,opt,name=connection,enum=dht.pb.Message_ConnectionType" json:"connection,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Message_Peer) Reset() { *m = Message_Peer{} } -func (m *Message_Peer) String() string { return proto.CompactTextString(m) } -func (*Message_Peer) ProtoMessage() {} - -func (m *Message_Peer) GetId() string { - if m != nil && m.Id != nil { - return *m.Id - } - return "" -} - -func (m *Message_Peer) GetAddrs() [][]byte { - if m != nil { - return m.Addrs - } - return nil -} - -func (m *Message_Peer) GetConnection() Message_ConnectionType { - if m != nil && m.Connection != nil { - return *m.Connection - } - return Message_NOT_CONNECTED -} - -// Record represents a dht record that contains a value -// for a key value pair -type Record struct { - // The key that references this record - Key *string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` - // The actual value this record is storing - Value []byte `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` - // hash of the authors public key - Author *string `protobuf:"bytes,3,opt,name=author" json:"author,omitempty"` - // A PKI signature for the key+value+author - Signature []byte `protobuf:"bytes,4,opt,name=signature" json:"signature,omitempty"` - // Time the record was received, set by receiver - TimeReceived *string `protobuf:"bytes,5,opt,name=timeReceived" json:"timeReceived,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Record) Reset() { *m = Record{} } -func (m *Record) String() string { return proto.CompactTextString(m) } -func (*Record) ProtoMessage() {} - -func (m *Record) GetKey() string { - if m != nil && m.Key != nil { - return *m.Key - } - return "" -} - -func (m *Record) GetValue() []byte { - if m != nil { - return m.Value - } - return nil -} - -func (m *Record) GetAuthor() string { - if m != nil && m.Author != nil { - return *m.Author - } - return "" -} - -func (m *Record) GetSignature() []byte { - if m != nil { - return m.Signature - } - return nil -} - -func (m *Record) GetTimeReceived() string { - if m != nil && m.TimeReceived != nil { - return *m.TimeReceived - } - return "" -} - -func init() { - proto.RegisterEnum("dht.pb.Message_MessageType", Message_MessageType_name, Message_MessageType_value) - proto.RegisterEnum("dht.pb.Message_ConnectionType", Message_ConnectionType_name, Message_ConnectionType_value) -} diff --git a/routing/dht/pb/dht.proto b/routing/dht/pb/dht.proto deleted file mode 100644 index de88c3451..000000000 --- a/routing/dht/pb/dht.proto +++ /dev/null @@ -1,81 +0,0 @@ -package dht.pb; - -//run `protoc --go_out=. *.proto` to generate - -message Message { - enum MessageType { - PUT_VALUE = 0; - GET_VALUE = 1; - ADD_PROVIDER = 2; - GET_PROVIDERS = 3; - FIND_NODE = 4; - PING = 5; - } - - enum ConnectionType { - // sender does not have a connection to peer, and no extra information (default) - NOT_CONNECTED = 0; - - // sender has a live connection to peer - CONNECTED = 1; - - // sender recently connected to peer - CAN_CONNECT = 2; - - // sender recently tried to connect to peer repeatedly but failed to connect - // ("try" here is loose, but this should signal "made strong effort, failed") - CANNOT_CONNECT = 3; - } - - message Peer { - // ID of a given peer. - optional string id = 1; - - // multiaddrs for a given peer - repeated bytes addrs = 2; - - // used to signal the sender's connection capabilities to the peer - optional ConnectionType connection = 3; - } - - // defines what type of message it is. - optional MessageType type = 1; - - // defines what coral cluster level this query/response belongs to. - optional int32 clusterLevelRaw = 10; - - // Used to specify the key associated with this message. - // PUT_VALUE, GET_VALUE, ADD_PROVIDER, GET_PROVIDERS - optional string key = 2; - - // Used to return a value - // PUT_VALUE, GET_VALUE - optional Record record = 3; - - // Used to return peers closer to a key in a query - // GET_VALUE, GET_PROVIDERS, FIND_NODE - repeated Peer closerPeers = 8; - - // Used to return Providers - // GET_VALUE, ADD_PROVIDER, GET_PROVIDERS - repeated Peer providerPeers = 9; -} - -// Record represents a dht record that contains a value -// for a key value pair -message Record { - // The key that references this record - optional string key = 1; - - // The actual value this record is storing - optional bytes value = 2; - - // hash of the authors public key - optional string author = 3; - - // A PKI signature for the key+value+author - optional bytes signature = 4; - - // Time the record was received, set by receiver - optional string timeReceived = 5; -} diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go deleted file mode 100644 index 2a6936e4e..000000000 --- a/routing/dht/pb/message.go +++ /dev/null @@ -1,185 +0,0 @@ -package dht_pb - -import ( - ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" - - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - inet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" -) - -var log = logging.Logger("dht.pb") - -type PeerRoutingInfo struct { - pstore.PeerInfo - inet.Connectedness -} - -// NewMessage constructs a new dht message with given type, key, and level -func NewMessage(typ Message_MessageType, key string, level int) *Message { - m := &Message{ - Type: &typ, - Key: &key, - } - m.SetClusterLevel(level) - return m -} - -func peerRoutingInfoToPBPeer(p PeerRoutingInfo) *Message_Peer { - pbp := new(Message_Peer) - - pbp.Addrs = make([][]byte, len(p.Addrs)) - for i, maddr := range p.Addrs { - pbp.Addrs[i] = maddr.Bytes() // Bytes, not String. Compressed. - } - s := string(p.ID) - pbp.Id = &s - c := ConnectionType(p.Connectedness) - pbp.Connection = &c - return pbp -} - -func peerInfoToPBPeer(p pstore.PeerInfo) *Message_Peer { - pbp := new(Message_Peer) - - pbp.Addrs = make([][]byte, len(p.Addrs)) - for i, maddr := range p.Addrs { - pbp.Addrs[i] = maddr.Bytes() // Bytes, not String. Compressed. - } - s := string(p.ID) - pbp.Id = &s - return pbp -} - -// PBPeerToPeer turns a *Message_Peer into its pstore.PeerInfo counterpart -func PBPeerToPeerInfo(pbp *Message_Peer) pstore.PeerInfo { - return pstore.PeerInfo{ - ID: peer.ID(pbp.GetId()), - Addrs: pbp.Addresses(), - } -} - -// RawPeerInfosToPBPeers converts a slice of Peers into a slice of *Message_Peers, -// ready to go out on the wire. -func RawPeerInfosToPBPeers(peers []pstore.PeerInfo) []*Message_Peer { - pbpeers := make([]*Message_Peer, len(peers)) - for i, p := range peers { - pbpeers[i] = peerInfoToPBPeer(p) - } - return pbpeers -} - -// PeersToPBPeers converts given []peer.Peer into a set of []*Message_Peer, -// which can be written to a message and sent out. the key thing this function -// does (in addition to PeersToPBPeers) is set the ConnectionType with -// information from the given inet.Network. -func PeerInfosToPBPeers(n inet.Network, peers []pstore.PeerInfo) []*Message_Peer { - pbps := RawPeerInfosToPBPeers(peers) - for i, pbp := range pbps { - c := ConnectionType(n.Connectedness(peers[i].ID)) - pbp.Connection = &c - } - return pbps -} - -func PeerRoutingInfosToPBPeers(peers []PeerRoutingInfo) []*Message_Peer { - pbpeers := make([]*Message_Peer, len(peers)) - for i, p := range peers { - pbpeers[i] = peerRoutingInfoToPBPeer(p) - } - return pbpeers -} - -// PBPeersToPeerInfos converts given []*Message_Peer into []pstore.PeerInfo -// Invalid addresses will be silently omitted. -func PBPeersToPeerInfos(pbps []*Message_Peer) []pstore.PeerInfo { - peers := make([]pstore.PeerInfo, 0, len(pbps)) - for _, pbp := range pbps { - peers = append(peers, PBPeerToPeerInfo(pbp)) - } - return peers -} - -// Addresses returns a multiaddr associated with the Message_Peer entry -func (m *Message_Peer) Addresses() []ma.Multiaddr { - if m == nil { - return nil - } - - maddrs := make([]ma.Multiaddr, 0, len(m.Addrs)) - for _, addr := range m.Addrs { - maddr, err := ma.NewMultiaddrBytes(addr) - if err != nil { - log.Warningf("error decoding Multiaddr for peer: %s", m.GetId()) - continue - } - - maddrs = append(maddrs, maddr) - } - return maddrs -} - -// GetClusterLevel gets and adjusts the cluster level on the message. -// a +/- 1 adjustment is needed to distinguish a valid first level (1) and -// default "no value" protobuf behavior (0) -func (m *Message) GetClusterLevel() int { - level := m.GetClusterLevelRaw() - 1 - if level < 0 { - return 0 - } - return int(level) -} - -// SetClusterLevel adjusts and sets the cluster level on the message. -// a +/- 1 adjustment is needed to distinguish a valid first level (1) and -// default "no value" protobuf behavior (0) -func (m *Message) SetClusterLevel(level int) { - lvl := int32(level) - m.ClusterLevelRaw = &lvl -} - -// Loggable turns a Message into machine-readable log output -func (m *Message) Loggable() map[string]interface{} { - return map[string]interface{}{ - "message": map[string]string{ - "type": m.Type.String(), - "key": key.Key(m.GetKey()).B58String(), - }, - } -} - -// ConnectionType returns a Message_ConnectionType associated with the -// inet.Connectedness. -func ConnectionType(c inet.Connectedness) Message_ConnectionType { - switch c { - default: - return Message_NOT_CONNECTED - case inet.NotConnected: - return Message_NOT_CONNECTED - case inet.Connected: - return Message_CONNECTED - case inet.CanConnect: - return Message_CAN_CONNECT - case inet.CannotConnect: - return Message_CANNOT_CONNECT - } -} - -// Connectedness returns an inet.Connectedness associated with the -// Message_ConnectionType. -func Connectedness(c Message_ConnectionType) inet.Connectedness { - switch c { - default: - return inet.NotConnected - case Message_NOT_CONNECTED: - return inet.NotConnected - case Message_CONNECTED: - return inet.Connected - case Message_CAN_CONNECT: - return inet.CanConnect - case Message_CANNOT_CONNECT: - return inet.CannotConnect - } -} diff --git a/routing/dht/pb/message_test.go b/routing/dht/pb/message_test.go deleted file mode 100644 index 71f4abdc5..000000000 --- a/routing/dht/pb/message_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package dht_pb - -import ( - "testing" -) - -func TestBadAddrsDontReturnNil(t *testing.T) { - mp := new(Message_Peer) - mp.Addrs = [][]byte{[]byte("NOT A VALID MULTIADDR")} - - addrs := mp.Addresses() - if len(addrs) > 0 { - t.Fatal("shouldnt have any multiaddrs") - } -} diff --git a/routing/dht/providers/providers.go b/routing/dht/providers/providers.go deleted file mode 100644 index a34fec6bb..000000000 --- a/routing/dht/providers/providers.go +++ /dev/null @@ -1,353 +0,0 @@ -package providers - -import ( - "encoding/binary" - "fmt" - "strings" - "time" - - goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - goprocessctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - autobatch "gx/ipfs/QmSp3diFRRv4zR25nHU4MWNCdhT4R6cxrTPLx12MCi1TZb/autobatch" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - base32 "gx/ipfs/Qmb1DA2A9LS2wR4FFweB4uEDomFsdmnw1VLawLE1yQzudj/base32" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" - - flags "github.com/ipfs/go-ipfs/flags" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" -) - -var batchBufferSize = 256 - -func init() { - if flags.LowMemMode { - batchBufferSize = 8 - } -} - -var log = logging.Logger("providers") - -var lruCacheSize = 256 -var ProvideValidity = time.Hour * 24 -var defaultCleanupInterval = time.Hour - -type ProviderManager struct { - // all non channel fields are meant to be accessed only within - // the run method - providers *lru.Cache - lpeer peer.ID - dstore ds.Datastore - - newprovs chan *addProv - getprovs chan *getProv - period time.Duration - proc goprocess.Process - - cleanupInterval time.Duration -} - -type providerSet struct { - providers []peer.ID - set map[peer.ID]time.Time -} - -type addProv struct { - k key.Key - val peer.ID -} - -type getProv struct { - k key.Key - resp chan []peer.ID -} - -func NewProviderManager(ctx context.Context, local peer.ID, dstore ds.Batching) *ProviderManager { - pm := new(ProviderManager) - pm.getprovs = make(chan *getProv) - pm.newprovs = make(chan *addProv) - pm.dstore = autobatch.NewAutoBatching(dstore, batchBufferSize) - cache, err := lru.New(lruCacheSize) - if err != nil { - panic(err) //only happens if negative value is passed to lru constructor - } - pm.providers = cache - - pm.proc = goprocessctx.WithContext(ctx) - pm.cleanupInterval = defaultCleanupInterval - pm.proc.Go(func(p goprocess.Process) { pm.run() }) - - return pm -} - -const providersKeyPrefix = "/providers/" - -func mkProvKey(k key.Key) ds.Key { - return ds.NewKey(providersKeyPrefix + base32.RawStdEncoding.EncodeToString([]byte(k))) -} - -func (pm *ProviderManager) Process() goprocess.Process { - return pm.proc -} - -func (pm *ProviderManager) providersForKey(k key.Key) ([]peer.ID, error) { - pset, err := pm.getProvSet(k) - if err != nil { - return nil, err - } - return pset.providers, nil -} - -func (pm *ProviderManager) getProvSet(k key.Key) (*providerSet, error) { - cached, ok := pm.providers.Get(k) - if ok { - return cached.(*providerSet), nil - } - - pset, err := loadProvSet(pm.dstore, k) - if err != nil { - return nil, err - } - - if len(pset.providers) > 0 { - pm.providers.Add(k, pset) - } - - return pset, nil -} - -func loadProvSet(dstore ds.Datastore, k key.Key) (*providerSet, error) { - res, err := dstore.Query(dsq.Query{Prefix: mkProvKey(k).String()}) - if err != nil { - return nil, err - } - - out := newProviderSet() - for e := range res.Next() { - if e.Error != nil { - log.Error("got an error: ", e.Error) - continue - } - parts := strings.Split(e.Key, "/") - if len(parts) != 4 { - log.Warning("incorrectly formatted key: ", e.Key) - continue - } - - decstr, err := base32.RawStdEncoding.DecodeString(parts[len(parts)-1]) - if err != nil { - log.Error("base32 decoding error: ", err) - continue - } - - pid := peer.ID(decstr) - - t, err := readTimeValue(e.Value) - if err != nil { - log.Warning("parsing providers record from disk: ", err) - continue - } - - out.setVal(pid, t) - } - - return out, nil -} - -func readTimeValue(i interface{}) (time.Time, error) { - data, ok := i.([]byte) - if !ok { - return time.Time{}, fmt.Errorf("data was not a []byte") - } - - nsec, _ := binary.Varint(data) - - return time.Unix(0, nsec), nil -} - -func (pm *ProviderManager) addProv(k key.Key, p peer.ID) error { - iprovs, ok := pm.providers.Get(k) - if !ok { - iprovs = newProviderSet() - pm.providers.Add(k, iprovs) - } - provs := iprovs.(*providerSet) - now := time.Now() - provs.setVal(p, now) - - return writeProviderEntry(pm.dstore, k, p, now) -} - -func writeProviderEntry(dstore ds.Datastore, k key.Key, p peer.ID, t time.Time) error { - dsk := mkProvKey(k).ChildString(base32.RawStdEncoding.EncodeToString([]byte(p))) - - buf := make([]byte, 16) - n := binary.PutVarint(buf, t.UnixNano()) - - return dstore.Put(dsk, buf[:n]) -} - -func (pm *ProviderManager) deleteProvSet(k key.Key) error { - pm.providers.Remove(k) - - res, err := pm.dstore.Query(dsq.Query{ - KeysOnly: true, - Prefix: mkProvKey(k).String(), - }) - - entries, err := res.Rest() - if err != nil { - return err - } - - for _, e := range entries { - err := pm.dstore.Delete(ds.NewKey(e.Key)) - if err != nil { - log.Error("deleting provider set: ", err) - } - } - return nil -} - -func (pm *ProviderManager) getAllProvKeys() ([]key.Key, error) { - res, err := pm.dstore.Query(dsq.Query{ - KeysOnly: true, - Prefix: providersKeyPrefix, - }) - - if err != nil { - return nil, err - } - - entries, err := res.Rest() - if err != nil { - return nil, err - } - - out := make([]key.Key, 0, len(entries)) - seen := make(map[key.Key]struct{}) - for _, e := range entries { - parts := strings.Split(e.Key, "/") - if len(parts) != 4 { - log.Warning("incorrectly formatted provider entry in datastore") - continue - } - decoded, err := base32.RawStdEncoding.DecodeString(parts[2]) - if err != nil { - log.Warning("error decoding base32 provider key") - continue - } - - k := key.Key(decoded) - if _, ok := seen[k]; !ok { - out = append(out, key.Key(decoded)) - seen[k] = struct{}{} - } - } - - return out, nil -} - -func (pm *ProviderManager) run() { - tick := time.NewTicker(pm.cleanupInterval) - for { - select { - case np := <-pm.newprovs: - err := pm.addProv(np.k, np.val) - if err != nil { - log.Error("error adding new providers: ", err) - } - case gp := <-pm.getprovs: - provs, err := pm.providersForKey(gp.k) - if err != nil && err != ds.ErrNotFound { - log.Error("error reading providers: ", err) - } - - gp.resp <- provs - case <-tick.C: - keys, err := pm.getAllProvKeys() - if err != nil { - log.Error("Error loading provider keys: ", err) - continue - } - for _, k := range keys { - provs, err := pm.getProvSet(k) - if err != nil { - log.Error("error loading known provset: ", err) - continue - } - var filtered []peer.ID - for p, t := range provs.set { - if time.Now().Sub(t) > ProvideValidity { - delete(provs.set, p) - } else { - filtered = append(filtered, p) - } - } - - if len(filtered) > 0 { - provs.providers = filtered - } else { - err := pm.deleteProvSet(k) - if err != nil { - log.Error("error deleting provider set: ", err) - } - } - } - case <-pm.proc.Closing(): - return - } - } -} - -func (pm *ProviderManager) AddProvider(ctx context.Context, k key.Key, val peer.ID) { - prov := &addProv{ - k: k, - val: val, - } - select { - case pm.newprovs <- prov: - case <-ctx.Done(): - } -} - -func (pm *ProviderManager) GetProviders(ctx context.Context, k key.Key) []peer.ID { - gp := &getProv{ - k: k, - resp: make(chan []peer.ID, 1), // buffered to prevent sender from blocking - } - select { - case <-ctx.Done(): - return nil - case pm.getprovs <- gp: - } - select { - case <-ctx.Done(): - return nil - case peers := <-gp.resp: - return peers - } -} - -func newProviderSet() *providerSet { - return &providerSet{ - set: make(map[peer.ID]time.Time), - } -} - -func (ps *providerSet) Add(p peer.ID) { - ps.setVal(p, time.Now()) -} - -func (ps *providerSet) setVal(p peer.ID, t time.Time) { - _, found := ps.set[p] - if !found { - ps.providers = append(ps.providers, p) - } - - ps.set[p] = t -} diff --git a/routing/dht/providers/providers_test.go b/routing/dht/providers/providers_test.go deleted file mode 100644 index 01cee7b73..000000000 --- a/routing/dht/providers/providers_test.go +++ /dev/null @@ -1,150 +0,0 @@ -package providers - -import ( - "fmt" - "testing" - "time" - - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" -) - -func TestProviderManager(t *testing.T) { - ctx := context.Background() - mid := peer.ID("testing") - p := NewProviderManager(ctx, mid, ds.NewMapDatastore()) - a := key.Key("test") - p.AddProvider(ctx, a, peer.ID("testingprovider")) - resp := p.GetProviders(ctx, a) - if len(resp) != 1 { - t.Fatal("Could not retrieve provider.") - } - p.proc.Close() -} - -func TestProvidersDatastore(t *testing.T) { - old := lruCacheSize - lruCacheSize = 10 - defer func() { lruCacheSize = old }() - - ctx := context.Background() - mid := peer.ID("testing") - p := NewProviderManager(ctx, mid, ds.NewMapDatastore()) - defer p.proc.Close() - - friend := peer.ID("friend") - var keys []key.Key - for i := 0; i < 100; i++ { - k := key.Key(fmt.Sprint(i)) - keys = append(keys, k) - p.AddProvider(ctx, k, friend) - } - - for _, k := range keys { - resp := p.GetProviders(ctx, k) - if len(resp) != 1 { - t.Fatal("Could not retrieve provider.") - } - if resp[0] != friend { - t.Fatal("expected provider to be 'friend'") - } - } -} - -func TestProvidersSerialization(t *testing.T) { - dstore := ds.NewMapDatastore() - - k := key.Key("my key!") - p1 := peer.ID("peer one") - p2 := peer.ID("peer two") - pt1 := time.Now() - pt2 := pt1.Add(time.Hour) - - err := writeProviderEntry(dstore, k, p1, pt1) - if err != nil { - t.Fatal(err) - } - - err = writeProviderEntry(dstore, k, p2, pt2) - if err != nil { - t.Fatal(err) - } - - pset, err := loadProvSet(dstore, k) - if err != nil { - t.Fatal(err) - } - - lt1, ok := pset.set[p1] - if !ok { - t.Fatal("failed to load set correctly") - } - - if pt1 != lt1 { - t.Fatal("time wasnt serialized correctly") - } - - lt2, ok := pset.set[p2] - if !ok { - t.Fatal("failed to load set correctly") - } - - if pt2 != lt2 { - t.Fatal("time wasnt serialized correctly") - } -} - -func TestProvidesExpire(t *testing.T) { - pval := ProvideValidity - cleanup := defaultCleanupInterval - ProvideValidity = time.Second / 2 - defaultCleanupInterval = time.Second / 2 - defer func() { - ProvideValidity = pval - defaultCleanupInterval = cleanup - }() - - ctx := context.Background() - mid := peer.ID("testing") - p := NewProviderManager(ctx, mid, ds.NewMapDatastore()) - - peers := []peer.ID{"a", "b"} - var keys []key.Key - for i := 0; i < 10; i++ { - k := key.Key(i) - keys = append(keys, k) - p.AddProvider(ctx, k, peers[0]) - p.AddProvider(ctx, k, peers[1]) - } - - for i := 0; i < 10; i++ { - out := p.GetProviders(ctx, keys[i]) - if len(out) != 2 { - t.Fatal("expected providers to still be there") - } - } - - time.Sleep(time.Second) - for i := 0; i < 10; i++ { - out := p.GetProviders(ctx, keys[i]) - if len(out) > 2 { - t.Fatal("expected providers to be cleaned up") - } - } - - if p.providers.Len() != 0 { - t.Fatal("providers map not cleaned up") - } - - allprovs, err := p.getAllProvKeys() - if err != nil { - t.Fatal(err) - } - - if len(allprovs) != 0 { - t.Fatal("expected everything to be cleaned out of the datastore") - } -} diff --git a/routing/dht/query.go b/routing/dht/query.go deleted file mode 100644 index d17e00e2d..000000000 --- a/routing/dht/query.go +++ /dev/null @@ -1,298 +0,0 @@ -package dht - -import ( - "sync" - - pset "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer/peerset" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - - notif "github.com/ipfs/go-ipfs/notifications" - "github.com/ipfs/go-ipfs/routing" - todoctr "github.com/ipfs/go-ipfs/thirdparty/todocounter" - - process "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - ctxproc "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" - queue "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore/queue" -) - -var maxQueryConcurrency = AlphaValue - -type dhtQuery struct { - dht *IpfsDHT - key key.Key // the key we're querying for - qfunc queryFunc // the function to execute per peer - concurrency int // the concurrency parameter -} - -type dhtQueryResult struct { - value []byte // GetValue - peer pstore.PeerInfo // FindPeer - providerPeers []pstore.PeerInfo // GetProviders - closerPeers []pstore.PeerInfo // * - success bool -} - -// constructs query -func (dht *IpfsDHT) newQuery(k key.Key, f queryFunc) *dhtQuery { - return &dhtQuery{ - key: k, - dht: dht, - qfunc: f, - concurrency: maxQueryConcurrency, - } -} - -// QueryFunc is a function that runs a particular query with a given peer. -// It returns either: -// - the value -// - a list of peers potentially better able to serve the query -// - an error -type queryFunc func(context.Context, peer.ID) (*dhtQueryResult, error) - -// Run runs the query at hand. pass in a list of peers to use first. -func (q *dhtQuery) Run(ctx context.Context, peers []peer.ID) (*dhtQueryResult, error) { - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - } - - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - runner := newQueryRunner(q) - return runner.Run(ctx, peers) -} - -type dhtQueryRunner struct { - query *dhtQuery // query to run - peersSeen *pset.PeerSet // all peers queried. prevent querying same peer 2x - peersToQuery *queue.ChanQueue // peers remaining to be queried - peersRemaining todoctr.Counter // peersToQuery + currently processing - - result *dhtQueryResult // query result - errs u.MultiErr // result errors. maybe should be a map[peer.ID]error - - rateLimit chan struct{} // processing semaphore - log logging.EventLogger - - runCtx context.Context - - proc process.Process - sync.RWMutex -} - -func newQueryRunner(q *dhtQuery) *dhtQueryRunner { - proc := process.WithParent(process.Background()) - ctx := ctxproc.OnClosingContext(proc) - return &dhtQueryRunner{ - query: q, - peersToQuery: queue.NewChanQueue(ctx, queue.NewXORDistancePQ(string(q.key))), - peersRemaining: todoctr.NewSyncCounter(), - peersSeen: pset.New(), - rateLimit: make(chan struct{}, q.concurrency), - proc: proc, - } -} - -func (r *dhtQueryRunner) Run(ctx context.Context, peers []peer.ID) (*dhtQueryResult, error) { - r.log = log - r.runCtx = ctx - - if len(peers) == 0 { - log.Warning("Running query with no peers!") - return nil, nil - } - - // setup concurrency rate limiting - for i := 0; i < r.query.concurrency; i++ { - r.rateLimit <- struct{}{} - } - - // add all the peers we got first. - for _, p := range peers { - r.addPeerToQuery(p) - } - - // go do this thing. - // do it as a child proc to make sure Run exits - // ONLY AFTER spawn workers has exited. - r.proc.Go(r.spawnWorkers) - - // so workers are working. - - // wait until they're done. - err := routing.ErrNotFound - - // now, if the context finishes, close the proc. - // we have to do it here because the logic before is setup, which - // should run without closing the proc. - ctxproc.CloseAfterContext(r.proc, ctx) - - select { - case <-r.peersRemaining.Done(): - r.proc.Close() - r.RLock() - defer r.RUnlock() - - err = routing.ErrNotFound - - // if every query to every peer failed, something must be very wrong. - if len(r.errs) > 0 && len(r.errs) == r.peersSeen.Size() { - log.Debugf("query errs: %s", r.errs) - err = r.errs[0] - } - - case <-r.proc.Closed(): - r.RLock() - defer r.RUnlock() - err = context.DeadlineExceeded - } - - if r.result != nil && r.result.success { - return r.result, nil - } - - return nil, err -} - -func (r *dhtQueryRunner) addPeerToQuery(next peer.ID) { - // if new peer is ourselves... - if next == r.query.dht.self { - r.log.Debug("addPeerToQuery skip self") - return - } - - if !r.peersSeen.TryAdd(next) { - return - } - - notif.PublishQueryEvent(r.runCtx, ¬if.QueryEvent{ - Type: notif.AddingPeer, - ID: next, - }) - - r.peersRemaining.Increment(1) - select { - case r.peersToQuery.EnqChan <- next: - case <-r.proc.Closing(): - } -} - -func (r *dhtQueryRunner) spawnWorkers(proc process.Process) { - for { - - select { - case <-r.peersRemaining.Done(): - return - - case <-r.proc.Closing(): - return - - case <-r.rateLimit: - select { - case p, more := <-r.peersToQuery.DeqChan: - if !more { - return // channel closed. - } - - // do it as a child func to make sure Run exits - // ONLY AFTER spawn workers has exited. - proc.Go(func(proc process.Process) { - r.queryPeer(proc, p) - }) - case <-r.proc.Closing(): - return - case <-r.peersRemaining.Done(): - return - } - } - } -} - -func (r *dhtQueryRunner) queryPeer(proc process.Process, p peer.ID) { - // ok let's do this! - - // create a context from our proc. - ctx := ctxproc.OnClosingContext(proc) - - // make sure we do this when we exit - defer func() { - // signal we're done proccessing peer p - r.peersRemaining.Decrement(1) - r.rateLimit <- struct{}{} - }() - - // make sure we're connected to the peer. - // FIXME abstract away into the network layer - if conns := r.query.dht.host.Network().ConnsToPeer(p); len(conns) == 0 { - log.Debug("not connected. dialing.") - - notif.PublishQueryEvent(r.runCtx, ¬if.QueryEvent{ - Type: notif.DialingPeer, - ID: p, - }) - // while we dial, we do not take up a rate limit. this is to allow - // forward progress during potentially very high latency dials. - r.rateLimit <- struct{}{} - - pi := pstore.PeerInfo{ID: p} - - if err := r.query.dht.host.Connect(ctx, pi); err != nil { - log.Debugf("Error connecting: %s", err) - - notif.PublishQueryEvent(r.runCtx, ¬if.QueryEvent{ - Type: notif.QueryError, - Extra: err.Error(), - ID: p, - }) - - r.Lock() - r.errs = append(r.errs, err) - r.Unlock() - <-r.rateLimit // need to grab it again, as we deferred. - return - } - <-r.rateLimit // need to grab it again, as we deferred. - log.Debugf("connected. dial success.") - } - - // finally, run the query against this peer - res, err := r.query.qfunc(ctx, p) - - if err != nil { - log.Debugf("ERROR worker for: %v %v", p, err) - r.Lock() - r.errs = append(r.errs, err) - r.Unlock() - - } else if res.success { - log.Debugf("SUCCESS worker for: %v %s", p, res) - r.Lock() - r.result = res - r.Unlock() - go r.proc.Close() // signal to everyone that we're done. - // must be async, as we're one of the children, and Close blocks. - - } else if len(res.closerPeers) > 0 { - log.Debugf("PEERS CLOSER -- worker for: %v (%d closer peers)", p, len(res.closerPeers)) - for _, next := range res.closerPeers { - if next.ID == r.query.dht.self { // dont add self. - log.Debugf("PEERS CLOSER -- worker for: %v found self", p) - continue - } - - // add their addresses to the dialer's peerstore - r.query.dht.peerstore.AddAddrs(next.ID, next.Addrs, pstore.TempAddrTTL) - r.addPeerToQuery(next.ID) - log.Debugf("PEERS CLOSER -- worker for: %v added %v (%v)", p, next.ID, next.Addrs) - } - } else { - log.Debugf("QUERY worker for: %v - not found, and no closer peers.", p) - } -} diff --git a/routing/dht/records.go b/routing/dht/records.go deleted file mode 100644 index af7169861..000000000 --- a/routing/dht/records.go +++ /dev/null @@ -1,149 +0,0 @@ -package dht - -import ( - "fmt" - "time" - - routing "github.com/ipfs/go-ipfs/routing" - pb "github.com/ipfs/go-ipfs/routing/dht/pb" - record "github.com/ipfs/go-ipfs/routing/record" - ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - ctxfrac "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/frac" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" -) - -// MaxRecordAge specifies the maximum time that any node will hold onto a record -// from the time its received. This does not apply to any other forms of validity that -// the record may contain. -// For example, a record may contain an ipns entry with an EOL saying its valid -// until the year 2020 (a great time in the future). For that record to stick around -// it must be rebroadcasted more frequently than once every 'MaxRecordAge' -const MaxRecordAge = time.Hour * 36 - -func (dht *IpfsDHT) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey, error) { - log.Debugf("getPublicKey for: %s", p) - - // check locally. - pk := dht.peerstore.PubKey(p) - if pk != nil { - return pk, nil - } - - // ok, try the node itself. if they're overwhelmed or slow we can move on. - ctxT, cancelFunc := ctxfrac.WithDeadlineFraction(ctx, 0.3) - defer cancelFunc() - if pk, err := dht.getPublicKeyFromNode(ctx, p); err == nil { - err := dht.peerstore.AddPubKey(p, pk) - if err != nil { - return pk, err - } - return pk, nil - } - - // last ditch effort: let's try the dht. - log.Debugf("pk for %s not in peerstore, and peer failed. Trying DHT.", p) - pkkey := routing.KeyForPublicKey(p) - - val, err := dht.GetValue(ctxT, pkkey) - if err != nil { - log.Warning("Failed to find requested public key.") - return nil, err - } - - pk, err = ci.UnmarshalPublicKey(val) - if err != nil { - log.Debugf("Failed to unmarshal public key: %s", err) - return nil, err - } - - return pk, dht.peerstore.AddPubKey(p, pk) -} - -func (dht *IpfsDHT) getPublicKeyFromNode(ctx context.Context, p peer.ID) (ci.PubKey, error) { - - // check locally, just in case... - pk := dht.peerstore.PubKey(p) - if pk != nil { - return pk, nil - } - - pkkey := routing.KeyForPublicKey(p) - pmes, err := dht.getValueSingle(ctx, p, pkkey) - if err != nil { - return nil, err - } - - // node doesn't have key :( - record := pmes.GetRecord() - if record == nil { - return nil, fmt.Errorf("Node not responding with its public key: %s", p) - } - - // Success! We were given the value. we don't need to check - // validity because a) we can't. b) we know the hash of the - // key we're looking for. - val := record.GetValue() - log.Debug("DHT got a value from other peer") - - pk, err = ci.UnmarshalPublicKey(val) - if err != nil { - return nil, err - } - - id, err := peer.IDFromPublicKey(pk) - if err != nil { - return nil, err - } - if id != p { - return nil, fmt.Errorf("public key does not match id: %s", p) - } - - // ok! it's valid. we got it! - log.Debugf("DHT got public key from node itself.") - return pk, nil -} - -// verifyRecordLocally attempts to verify a record. if we do not have the public -// key, we fail. we do not search the dht. -func (dht *IpfsDHT) verifyRecordLocally(r *pb.Record) error { - - if len(r.Signature) > 0 { - // First, validate the signature - p := peer.ID(r.GetAuthor()) - pk := dht.peerstore.PubKey(p) - if pk == nil { - return fmt.Errorf("do not have public key for %s", p) - } - - if err := record.CheckRecordSig(r, pk); err != nil { - return err - } - } - - return dht.Validator.VerifyRecord(r) -} - -// verifyRecordOnline verifies a record, searching the DHT for the public key -// if necessary. The reason there is a distinction in the functions is that -// retrieving arbitrary public keys from the DHT as a result of passively -// receiving records (e.g. through a PUT_VALUE or ADD_PROVIDER) can cause a -// massive amplification attack on the dht. Use with care. -func (dht *IpfsDHT) verifyRecordOnline(ctx context.Context, r *pb.Record) error { - - if len(r.Signature) > 0 { - // get the public key, search for it if necessary. - p := peer.ID(r.GetAuthor()) - pk, err := dht.GetPublicKey(ctx, p) - if err != nil { - return err - } - - err = record.CheckRecordSig(r, pk) - if err != nil { - return err - } - } - - return dht.Validator.VerifyRecord(r) -} diff --git a/routing/dht/routing.go b/routing/dht/routing.go deleted file mode 100644 index 8b72a1711..000000000 --- a/routing/dht/routing.go +++ /dev/null @@ -1,538 +0,0 @@ -package dht - -import ( - "bytes" - "fmt" - "runtime" - "sync" - "time" - - pset "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer/peerset" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - - notif "github.com/ipfs/go-ipfs/notifications" - "github.com/ipfs/go-ipfs/routing" - pb "github.com/ipfs/go-ipfs/routing/dht/pb" - kb "github.com/ipfs/go-ipfs/routing/kbucket" - record "github.com/ipfs/go-ipfs/routing/record" - - inet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" -) - -// asyncQueryBuffer is the size of buffered channels in async queries. This -// buffer allows multiple queries to execute simultaneously, return their -// results and continue querying closer peers. Note that different query -// results will wait for the channel to drain. -var asyncQueryBuffer = 10 - -// This file implements the Routing interface for the IpfsDHT struct. - -// Basic Put/Get - -// PutValue adds value corresponding to given Key. -// This is the top level "Store" operation of the DHT -func (dht *IpfsDHT) PutValue(ctx context.Context, key key.Key, value []byte) error { - log.Debugf("PutValue %s", key) - sk, err := dht.getOwnPrivateKey() - if err != nil { - return err - } - - sign, err := dht.Validator.IsSigned(key) - if err != nil { - return err - } - - rec, err := record.MakePutRecord(sk, key, value, sign) - if err != nil { - log.Debug("creation of record failed!") - return err - } - - err = dht.putLocal(key, rec) - if err != nil { - return err - } - - pchan, err := dht.GetClosestPeers(ctx, key) - if err != nil { - return err - } - - wg := sync.WaitGroup{} - for p := range pchan { - wg.Add(1) - go func(p peer.ID) { - ctx, cancel := context.WithCancel(ctx) - defer cancel() - defer wg.Done() - notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ - Type: notif.Value, - ID: p, - }) - - err := dht.putValueToPeer(ctx, p, key, rec) - if err != nil { - log.Debugf("failed putting value to peer: %s", err) - } - }(p) - } - wg.Wait() - return nil -} - -// GetValue searches for the value corresponding to given Key. -func (dht *IpfsDHT) GetValue(ctx context.Context, key key.Key) ([]byte, error) { - ctx, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() - - vals, err := dht.GetValues(ctx, key, 16) - if err != nil { - return nil, err - } - - var recs [][]byte - for _, v := range vals { - if v.Val != nil { - recs = append(recs, v.Val) - } - } - - i, err := dht.Selector.BestRecord(key, recs) - if err != nil { - return nil, err - } - - best := recs[i] - log.Debugf("GetValue %v %v", key, best) - if best == nil { - log.Errorf("GetValue yielded correct record with nil value.") - return nil, routing.ErrNotFound - } - - fixupRec, err := record.MakePutRecord(dht.peerstore.PrivKey(dht.self), key, best, true) - if err != nil { - // probably shouldnt actually 'error' here as we have found a value we like, - // but this call failing probably isnt something we want to ignore - return nil, err - } - - for _, v := range vals { - // if someone sent us a different 'less-valid' record, lets correct them - if !bytes.Equal(v.Val, best) { - go func(v routing.RecvdVal) { - if v.From == dht.self { - err := dht.putLocal(key, fixupRec) - if err != nil { - log.Error("Error correcting local dht entry:", err) - } - return - } - ctx, cancel := context.WithTimeout(dht.Context(), time.Second*30) - defer cancel() - err := dht.putValueToPeer(ctx, v.From, key, fixupRec) - if err != nil { - log.Error("Error correcting DHT entry: ", err) - } - }(v) - } - } - - return best, nil -} - -func (dht *IpfsDHT) GetValues(ctx context.Context, key key.Key, nvals int) ([]routing.RecvdVal, error) { - var vals []routing.RecvdVal - var valslock sync.Mutex - - // If we have it local, dont bother doing an RPC! - lrec, err := dht.getLocal(key) - if err == nil { - // TODO: this is tricky, we dont always want to trust our own value - // what if the authoritative source updated it? - log.Debug("have it locally") - vals = append(vals, routing.RecvdVal{ - Val: lrec.GetValue(), - From: dht.self, - }) - - if nvals <= 1 { - return vals, nil - } - } else if nvals == 0 { - return nil, err - } - - // get closest peers in the routing table - rtp := dht.routingTable.NearestPeers(kb.ConvertKey(key), KValue) - log.Debugf("peers in rt: %s", len(rtp), rtp) - if len(rtp) == 0 { - log.Warning("No peers from routing table!") - return nil, kb.ErrLookupFailure - } - - // setup the Query - parent := ctx - query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { - notif.PublishQueryEvent(parent, ¬if.QueryEvent{ - Type: notif.SendingQuery, - ID: p, - }) - - rec, peers, err := dht.getValueOrPeers(ctx, p, key) - switch err { - case routing.ErrNotFound: - // in this case, they responded with nothing, - // still send a notification so listeners can know the - // request has completed 'successfully' - notif.PublishQueryEvent(parent, ¬if.QueryEvent{ - Type: notif.PeerResponse, - ID: p, - }) - return nil, err - default: - return nil, err - - case nil, errInvalidRecord: - // in either of these cases, we want to keep going - } - - res := &dhtQueryResult{closerPeers: peers} - - if rec.GetValue() != nil || err == errInvalidRecord { - rv := routing.RecvdVal{ - Val: rec.GetValue(), - From: p, - } - valslock.Lock() - vals = append(vals, rv) - - // If weve collected enough records, we're done - if len(vals) >= nvals { - res.success = true - } - valslock.Unlock() - } - - notif.PublishQueryEvent(parent, ¬if.QueryEvent{ - Type: notif.PeerResponse, - ID: p, - Responses: pointerizePeerInfos(peers), - }) - - return res, nil - }) - - // run it! - _, err = query.Run(ctx, rtp) - if len(vals) == 0 { - if err != nil { - return nil, err - } - } - - return vals, nil - -} - -// Value provider layer of indirection. -// This is what DSHTs (Coral and MainlineDHT) do to store large values in a DHT. - -// Provide makes this node announce that it can provide a value for the given key -func (dht *IpfsDHT) Provide(ctx context.Context, key key.Key) error { - defer log.EventBegin(ctx, "provide", &key).Done() - - // add self locally - dht.providers.AddProvider(ctx, key, dht.self) - - peers, err := dht.GetClosestPeers(ctx, key) - if err != nil { - return err - } - - mes, err := dht.makeProvRecord(key) - if err != nil { - return err - } - - wg := sync.WaitGroup{} - for p := range peers { - wg.Add(1) - go func(p peer.ID) { - defer wg.Done() - log.Debugf("putProvider(%s, %s)", key, p) - notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ - Type: notif.FinalPeer, - ID: p, - }) - err := dht.sendMessage(ctx, p, mes) - if err != nil { - log.Debug(err) - } - }(p) - } - wg.Wait() - return nil -} - -func (dht *IpfsDHT) makeProvRecord(skey key.Key) (*pb.Message, error) { - pi := pstore.PeerInfo{ - ID: dht.self, - Addrs: dht.host.Addrs(), - } - - // // only share WAN-friendly addresses ?? - // pi.Addrs = addrutil.WANShareableAddrs(pi.Addrs) - if len(pi.Addrs) < 1 { - return nil, fmt.Errorf("no known addresses for self. cannot put provider.") - } - - pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, string(skey), 0) - pmes.ProviderPeers = pb.RawPeerInfosToPBPeers([]pstore.PeerInfo{pi}) - return pmes, nil -} - -// FindProviders searches until the context expires. -func (dht *IpfsDHT) FindProviders(ctx context.Context, key key.Key) ([]pstore.PeerInfo, error) { - var providers []pstore.PeerInfo - for p := range dht.FindProvidersAsync(ctx, key, KValue) { - providers = append(providers, p) - } - return providers, nil -} - -// FindProvidersAsync is the same thing as FindProviders, but returns a channel. -// Peers will be returned on the channel as soon as they are found, even before -// the search query completes. -func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key key.Key, count int) <-chan pstore.PeerInfo { - log.Event(ctx, "findProviders", &key) - peerOut := make(chan pstore.PeerInfo, count) - go dht.findProvidersAsyncRoutine(ctx, key, count, peerOut) - return peerOut -} - -func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key key.Key, count int, peerOut chan pstore.PeerInfo) { - defer log.EventBegin(ctx, "findProvidersAsync", &key).Done() - defer close(peerOut) - - ps := pset.NewLimited(count) - provs := dht.providers.GetProviders(ctx, key) - for _, p := range provs { - // NOTE: Assuming that this list of peers is unique - if ps.TryAdd(p) { - select { - case peerOut <- dht.peerstore.PeerInfo(p): - case <-ctx.Done(): - return - } - } - - // If we have enough peers locally, dont bother with remote RPC - if ps.Size() >= count { - return - } - } - - // setup the Query - parent := ctx - query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { - notif.PublishQueryEvent(parent, ¬if.QueryEvent{ - Type: notif.SendingQuery, - ID: p, - }) - pmes, err := dht.findProvidersSingle(ctx, p, key) - if err != nil { - return nil, err - } - - log.Debugf("%d provider entries", len(pmes.GetProviderPeers())) - provs := pb.PBPeersToPeerInfos(pmes.GetProviderPeers()) - log.Debugf("%d provider entries decoded", len(provs)) - - // Add unique providers from request, up to 'count' - for _, prov := range provs { - log.Debugf("got provider: %s", prov) - if ps.TryAdd(prov.ID) { - log.Debugf("using provider: %s", prov) - select { - case peerOut <- prov: - case <-ctx.Done(): - log.Debug("context timed out sending more providers") - return nil, ctx.Err() - } - } - if ps.Size() >= count { - log.Debugf("got enough providers (%d/%d)", ps.Size(), count) - return &dhtQueryResult{success: true}, nil - } - } - - // Give closer peers back to the query to be queried - closer := pmes.GetCloserPeers() - clpeers := pb.PBPeersToPeerInfos(closer) - log.Debugf("got closer peers: %d %s", len(clpeers), clpeers) - - notif.PublishQueryEvent(parent, ¬if.QueryEvent{ - Type: notif.PeerResponse, - ID: p, - Responses: pointerizePeerInfos(clpeers), - }) - return &dhtQueryResult{closerPeers: clpeers}, nil - }) - - peers := dht.routingTable.NearestPeers(kb.ConvertKey(key), KValue) - _, err := query.Run(ctx, peers) - if err != nil { - log.Debugf("Query error: %s", err) - // Special handling for issue: https://github.com/ipfs/go-ipfs/issues/3032 - if fmt.Sprint(err) == "" { - log.Error("reproduced bug 3032:") - log.Errorf("Errors type information: %#v", err) - log.Errorf("go version: %s", runtime.Version()) - log.Error("please report this information to: https://github.com/ipfs/go-ipfs/issues/3032") - - // replace problematic error with something that won't crash the daemon - err = fmt.Errorf("") - } - notif.PublishQueryEvent(ctx, ¬if.QueryEvent{ - Type: notif.QueryError, - Extra: err.Error(), - }) - } -} - -// FindPeer searches for a peer with given ID. -func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (pstore.PeerInfo, error) { - defer log.EventBegin(ctx, "FindPeer", id).Done() - - // Check if were already connected to them - if pi := dht.FindLocal(id); pi.ID != "" { - return pi, nil - } - - peers := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), KValue) - if len(peers) == 0 { - return pstore.PeerInfo{}, kb.ErrLookupFailure - } - - // Sanity... - for _, p := range peers { - if p == id { - log.Debug("found target peer in list of closest peers...") - return dht.peerstore.PeerInfo(p), nil - } - } - - // setup the Query - parent := ctx - query := dht.newQuery(key.Key(id), func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { - notif.PublishQueryEvent(parent, ¬if.QueryEvent{ - Type: notif.SendingQuery, - ID: p, - }) - - pmes, err := dht.findPeerSingle(ctx, p, id) - if err != nil { - return nil, err - } - - closer := pmes.GetCloserPeers() - clpeerInfos := pb.PBPeersToPeerInfos(closer) - - // see it we got the peer here - for _, npi := range clpeerInfos { - if npi.ID == id { - return &dhtQueryResult{ - peer: npi, - success: true, - }, nil - } - } - - notif.PublishQueryEvent(parent, ¬if.QueryEvent{ - Type: notif.PeerResponse, - Responses: pointerizePeerInfos(clpeerInfos), - }) - - return &dhtQueryResult{closerPeers: clpeerInfos}, nil - }) - - // run it! - result, err := query.Run(ctx, peers) - if err != nil { - return pstore.PeerInfo{}, err - } - - log.Debugf("FindPeer %v %v", id, result.success) - if result.peer.ID == "" { - return pstore.PeerInfo{}, routing.ErrNotFound - } - - return result.peer, nil -} - -// FindPeersConnectedToPeer searches for peers directly connected to a given peer. -func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (<-chan pstore.PeerInfo, error) { - - peerchan := make(chan pstore.PeerInfo, asyncQueryBuffer) - peersSeen := make(map[peer.ID]struct{}) - - peers := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), KValue) - if len(peers) == 0 { - return nil, kb.ErrLookupFailure - } - - // setup the Query - query := dht.newQuery(key.Key(id), func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { - - pmes, err := dht.findPeerSingle(ctx, p, id) - if err != nil { - return nil, err - } - - var clpeers []pstore.PeerInfo - closer := pmes.GetCloserPeers() - for _, pbp := range closer { - pi := pb.PBPeerToPeerInfo(pbp) - - // skip peers already seen - if _, found := peersSeen[pi.ID]; found { - continue - } - peersSeen[pi.ID] = struct{}{} - - // if peer is connected, send it to our client. - if pb.Connectedness(*pbp.Connection) == inet.Connected { - select { - case <-ctx.Done(): - return nil, ctx.Err() - case peerchan <- pi: - } - } - - // if peer is the peer we're looking for, don't bother querying it. - // TODO maybe query it? - if pb.Connectedness(*pbp.Connection) != inet.Connected { - clpeers = append(clpeers, pi) - } - } - - return &dhtQueryResult{closerPeers: clpeers}, nil - }) - - // run it! run it asynchronously to gen peers as results are found. - // this does no error checking - go func() { - if _, err := query.Run(ctx, peers); err != nil { - log.Debug(err) - } - - // close the peerchan channel when done. - close(peerchan) - }() - - return peerchan, nil -} diff --git a/routing/dht/util.go b/routing/dht/util.go deleted file mode 100644 index a605759a9..000000000 --- a/routing/dht/util.go +++ /dev/null @@ -1,39 +0,0 @@ -package dht - -import ( - "sync" -) - -// Pool size is the number of nodes used for group find/set RPC calls -var PoolSize = 6 - -// K is the maximum number of requests to perform before returning failure. -var KValue = 20 - -// Alpha is the concurrency factor for asynchronous requests. -var AlphaValue = 3 - -// A counter for incrementing a variable across multiple threads -type counter struct { - n int - mut sync.Mutex -} - -func (c *counter) Increment() { - c.mut.Lock() - c.n++ - c.mut.Unlock() -} - -func (c *counter) Decrement() { - c.mut.Lock() - c.n-- - c.mut.Unlock() -} - -func (c *counter) Size() (s int) { - c.mut.Lock() - s = c.n - c.mut.Unlock() - return -} diff --git a/routing/kbucket/bucket.go b/routing/kbucket/bucket.go deleted file mode 100644 index d280d9140..000000000 --- a/routing/kbucket/bucket.go +++ /dev/null @@ -1,108 +0,0 @@ -package kbucket - -import ( - "container/list" - "sync" - - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" -) - -// Bucket holds a list of peers. -type Bucket struct { - lk sync.RWMutex - list *list.List -} - -func newBucket() *Bucket { - b := new(Bucket) - b.list = list.New() - return b -} - -func (b *Bucket) Peers() []peer.ID { - b.lk.RLock() - defer b.lk.RUnlock() - ps := make([]peer.ID, 0, b.list.Len()) - for e := b.list.Front(); e != nil; e = e.Next() { - id := e.Value.(peer.ID) - ps = append(ps, id) - } - return ps -} - -func (b *Bucket) Has(id peer.ID) bool { - b.lk.RLock() - defer b.lk.RUnlock() - for e := b.list.Front(); e != nil; e = e.Next() { - if e.Value.(peer.ID) == id { - return true - } - } - return false -} - -func (b *Bucket) Remove(id peer.ID) { - b.lk.Lock() - defer b.lk.Unlock() - for e := b.list.Front(); e != nil; e = e.Next() { - if e.Value.(peer.ID) == id { - b.list.Remove(e) - } - } -} - -func (b *Bucket) MoveToFront(id peer.ID) { - b.lk.Lock() - defer b.lk.Unlock() - for e := b.list.Front(); e != nil; e = e.Next() { - if e.Value.(peer.ID) == id { - b.list.MoveToFront(e) - } - } -} - -func (b *Bucket) PushFront(p peer.ID) { - b.lk.Lock() - b.list.PushFront(p) - b.lk.Unlock() -} - -func (b *Bucket) PopBack() peer.ID { - b.lk.Lock() - defer b.lk.Unlock() - last := b.list.Back() - b.list.Remove(last) - return last.Value.(peer.ID) -} - -func (b *Bucket) Len() int { - b.lk.RLock() - defer b.lk.RUnlock() - return b.list.Len() -} - -// Split splits a buckets peers into two buckets, the methods receiver will have -// peers with CPL equal to cpl, the returned bucket will have peers with CPL -// greater than cpl (returned bucket has closer peers) -func (b *Bucket) Split(cpl int, target ID) *Bucket { - b.lk.Lock() - defer b.lk.Unlock() - - out := list.New() - newbuck := newBucket() - newbuck.list = out - e := b.list.Front() - for e != nil { - peerID := ConvertPeerID(e.Value.(peer.ID)) - peerCPL := commonPrefixLen(peerID, target) - if peerCPL > cpl { - cur := e - out.PushBack(e.Value) - e = e.Next() - b.list.Remove(cur) - continue - } - e = e.Next() - } - return newbuck -} diff --git a/routing/kbucket/sorting.go b/routing/kbucket/sorting.go deleted file mode 100644 index f662640f2..000000000 --- a/routing/kbucket/sorting.go +++ /dev/null @@ -1,55 +0,0 @@ -package kbucket - -import ( - "container/list" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - "sort" -) - -// A helper struct to sort peers by their distance to the local node -type peerDistance struct { - p peer.ID - distance ID -} - -// peerSorterArr implements sort.Interface to sort peers by xor distance -type peerSorterArr []*peerDistance - -func (p peerSorterArr) Len() int { return len(p) } -func (p peerSorterArr) Swap(a, b int) { p[a], p[b] = p[b], p[a] } -func (p peerSorterArr) Less(a, b int) bool { - return p[a].distance.less(p[b].distance) -} - -// - -func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) peerSorterArr { - for e := peerList.Front(); e != nil; e = e.Next() { - p := e.Value.(peer.ID) - pID := ConvertPeerID(p) - pd := peerDistance{ - p: p, - distance: xor(target, pID), - } - peerArr = append(peerArr, &pd) - } - return peerArr -} - -func SortClosestPeers(peers []peer.ID, target ID) []peer.ID { - var psarr peerSorterArr - for _, p := range peers { - pID := ConvertPeerID(p) - pd := &peerDistance{ - p: p, - distance: xor(target, pID), - } - psarr = append(psarr, pd) - } - sort.Sort(psarr) - var out []peer.ID - for _, p := range psarr { - out = append(out, p.p) - } - return out -} diff --git a/routing/kbucket/table.go b/routing/kbucket/table.go deleted file mode 100644 index 6c4827a32..000000000 --- a/routing/kbucket/table.go +++ /dev/null @@ -1,225 +0,0 @@ -// package kbucket implements a kademlia 'k-bucket' routing table. -package kbucket - -import ( - "fmt" - "sort" - "sync" - "time" - - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" -) - -var log = logging.Logger("table") - -// RoutingTable defines the routing table. -type RoutingTable struct { - - // ID of the local peer - local ID - - // Blanket lock, refine later for better performance - tabLock sync.RWMutex - - // latency metrics - metrics pstore.Metrics - - // Maximum acceptable latency for peers in this cluster - maxLatency time.Duration - - // kBuckets define all the fingers to other nodes. - Buckets []*Bucket - bucketsize int -} - -// NewRoutingTable creates a new routing table with a given bucketsize, local ID, and latency tolerance. -func NewRoutingTable(bucketsize int, localID ID, latency time.Duration, m pstore.Metrics) *RoutingTable { - rt := new(RoutingTable) - rt.Buckets = []*Bucket{newBucket()} - rt.bucketsize = bucketsize - rt.local = localID - rt.maxLatency = latency - rt.metrics = m - return rt -} - -// Update adds or moves the given peer to the front of its respective bucket -// If a peer gets removed from a bucket, it is returned -func (rt *RoutingTable) Update(p peer.ID) { - peerID := ConvertPeerID(p) - cpl := commonPrefixLen(peerID, rt.local) - - rt.tabLock.Lock() - defer rt.tabLock.Unlock() - bucketID := cpl - if bucketID >= len(rt.Buckets) { - bucketID = len(rt.Buckets) - 1 - } - - bucket := rt.Buckets[bucketID] - if bucket.Has(p) { - // If the peer is already in the table, move it to the front. - // This signifies that it it "more active" and the less active nodes - // Will as a result tend towards the back of the list - bucket.MoveToFront(p) - return - } - - if rt.metrics.LatencyEWMA(p) > rt.maxLatency { - // Connection doesnt meet requirements, skip! - return - } - - // New peer, add to bucket - bucket.PushFront(p) - - // Are we past the max bucket size? - if bucket.Len() > rt.bucketsize { - // If this bucket is the rightmost bucket, and its full - // we need to split it and create a new bucket - if bucketID == len(rt.Buckets)-1 { - rt.nextBucket() - return - } else { - // If the bucket cant split kick out least active node - bucket.PopBack() - return - } - } -} - -// Remove deletes a peer from the routing table. This is to be used -// when we are sure a node has disconnected completely. -func (rt *RoutingTable) Remove(p peer.ID) { - rt.tabLock.Lock() - defer rt.tabLock.Unlock() - peerID := ConvertPeerID(p) - cpl := commonPrefixLen(peerID, rt.local) - - bucketID := cpl - if bucketID >= len(rt.Buckets) { - bucketID = len(rt.Buckets) - 1 - } - - bucket := rt.Buckets[bucketID] - bucket.Remove(p) -} - -func (rt *RoutingTable) nextBucket() peer.ID { - bucket := rt.Buckets[len(rt.Buckets)-1] - newBucket := bucket.Split(len(rt.Buckets)-1, rt.local) - rt.Buckets = append(rt.Buckets, newBucket) - if newBucket.Len() > rt.bucketsize { - return rt.nextBucket() - } - - // If all elements were on left side of split... - if bucket.Len() > rt.bucketsize { - return bucket.PopBack() - } - return "" -} - -// Find a specific peer by ID or return nil -func (rt *RoutingTable) Find(id peer.ID) peer.ID { - srch := rt.NearestPeers(ConvertPeerID(id), 1) - if len(srch) == 0 || srch[0] != id { - return "" - } - return srch[0] -} - -// NearestPeer returns a single peer that is nearest to the given ID -func (rt *RoutingTable) NearestPeer(id ID) peer.ID { - peers := rt.NearestPeers(id, 1) - if len(peers) > 0 { - return peers[0] - } - - log.Debugf("NearestPeer: Returning nil, table size = %d", rt.Size()) - return "" -} - -// NearestPeers returns a list of the 'count' closest peers to the given ID -func (rt *RoutingTable) NearestPeers(id ID, count int) []peer.ID { - cpl := commonPrefixLen(id, rt.local) - - rt.tabLock.RLock() - - // Get bucket at cpl index or last bucket - var bucket *Bucket - if cpl >= len(rt.Buckets) { - cpl = len(rt.Buckets) - 1 - } - bucket = rt.Buckets[cpl] - - var peerArr peerSorterArr - peerArr = copyPeersFromList(id, peerArr, bucket.list) - if len(peerArr) < count { - // In the case of an unusual split, one bucket may be short or empty. - // if this happens, search both surrounding buckets for nearby peers - if cpl > 0 { - plist := rt.Buckets[cpl-1].list - peerArr = copyPeersFromList(id, peerArr, plist) - } - - if cpl < len(rt.Buckets)-1 { - plist := rt.Buckets[cpl+1].list - peerArr = copyPeersFromList(id, peerArr, plist) - } - } - rt.tabLock.RUnlock() - - // Sort by distance to local peer - sort.Sort(peerArr) - - var out []peer.ID - for i := 0; i < count && i < peerArr.Len(); i++ { - out = append(out, peerArr[i].p) - } - - return out -} - -// Size returns the total number of peers in the routing table -func (rt *RoutingTable) Size() int { - var tot int - rt.tabLock.RLock() - for _, buck := range rt.Buckets { - tot += buck.Len() - } - rt.tabLock.RUnlock() - return tot -} - -// ListPeers takes a RoutingTable and returns a list of all peers from all buckets in the table. -// NOTE: This is potentially unsafe... use at your own risk -func (rt *RoutingTable) ListPeers() []peer.ID { - var peers []peer.ID - rt.tabLock.RLock() - for _, buck := range rt.Buckets { - peers = append(peers, buck.Peers()...) - } - rt.tabLock.RUnlock() - return peers -} - -// Print prints a descriptive statement about the provided RoutingTable -func (rt *RoutingTable) Print() { - fmt.Printf("Routing Table, bs = %d, Max latency = %d\n", rt.bucketsize, rt.maxLatency) - rt.tabLock.RLock() - - for i, b := range rt.Buckets { - fmt.Printf("\tbucket: %d\n", i) - - b.lk.RLock() - for e := b.list.Front(); e != nil; e = e.Next() { - p := e.Value.(peer.ID) - fmt.Printf("\t\t- %s %s\n", p.Pretty(), rt.metrics.LatencyEWMA(p).String()) - } - b.lk.RUnlock() - } - rt.tabLock.RUnlock() -} diff --git a/routing/kbucket/table_test.go b/routing/kbucket/table_test.go deleted file mode 100644 index fb34d9976..000000000 --- a/routing/kbucket/table_test.go +++ /dev/null @@ -1,187 +0,0 @@ -package kbucket - -import ( - "math/rand" - "testing" - "time" - - tu "github.com/ipfs/go-ipfs/thirdparty/testutil" - - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" -) - -// Test basic features of the bucket struct -func TestBucket(t *testing.T) { - b := newBucket() - - peers := make([]peer.ID, 100) - for i := 0; i < 100; i++ { - peers[i] = tu.RandPeerIDFatal(t) - b.PushFront(peers[i]) - } - - local := tu.RandPeerIDFatal(t) - localID := ConvertPeerID(local) - - i := rand.Intn(len(peers)) - if !b.Has(peers[i]) { - t.Errorf("Failed to find peer: %v", peers[i]) - } - - spl := b.Split(0, ConvertPeerID(local)) - llist := b.list - for e := llist.Front(); e != nil; e = e.Next() { - p := ConvertPeerID(e.Value.(peer.ID)) - cpl := commonPrefixLen(p, localID) - if cpl > 0 { - t.Fatalf("Split failed. found id with cpl > 0 in 0 bucket") - } - } - - rlist := spl.list - for e := rlist.Front(); e != nil; e = e.Next() { - p := ConvertPeerID(e.Value.(peer.ID)) - cpl := commonPrefixLen(p, localID) - if cpl == 0 { - t.Fatalf("Split failed. found id with cpl == 0 in non 0 bucket") - } - } -} - -// Right now, this just makes sure that it doesnt hang or crash -func TestTableUpdate(t *testing.T) { - local := tu.RandPeerIDFatal(t) - m := pstore.NewMetrics() - rt := NewRoutingTable(10, ConvertPeerID(local), time.Hour, m) - - peers := make([]peer.ID, 100) - for i := 0; i < 100; i++ { - peers[i] = tu.RandPeerIDFatal(t) - } - - // Testing Update - for i := 0; i < 10000; i++ { - rt.Update(peers[rand.Intn(len(peers))]) - } - - for i := 0; i < 100; i++ { - id := ConvertPeerID(tu.RandPeerIDFatal(t)) - ret := rt.NearestPeers(id, 5) - if len(ret) == 0 { - t.Fatal("Failed to find node near ID.") - } - } -} - -func TestTableFind(t *testing.T) { - local := tu.RandPeerIDFatal(t) - m := pstore.NewMetrics() - rt := NewRoutingTable(10, ConvertPeerID(local), time.Hour, m) - - peers := make([]peer.ID, 100) - for i := 0; i < 5; i++ { - peers[i] = tu.RandPeerIDFatal(t) - rt.Update(peers[i]) - } - - t.Logf("Searching for peer: '%s'", peers[2]) - found := rt.NearestPeer(ConvertPeerID(peers[2])) - if !(found == peers[2]) { - t.Fatalf("Failed to lookup known node...") - } -} - -func TestTableFindMultiple(t *testing.T) { - local := tu.RandPeerIDFatal(t) - m := pstore.NewMetrics() - rt := NewRoutingTable(20, ConvertPeerID(local), time.Hour, m) - - peers := make([]peer.ID, 100) - for i := 0; i < 18; i++ { - peers[i] = tu.RandPeerIDFatal(t) - rt.Update(peers[i]) - } - - t.Logf("Searching for peer: '%s'", peers[2]) - found := rt.NearestPeers(ConvertPeerID(peers[2]), 15) - if len(found) != 15 { - t.Fatalf("Got back different number of peers than we expected.") - } -} - -// Looks for race conditions in table operations. For a more 'certain' -// test, increase the loop counter from 1000 to a much higher number -// and set GOMAXPROCS above 1 -func TestTableMultithreaded(t *testing.T) { - local := peer.ID("localPeer") - m := pstore.NewMetrics() - tab := NewRoutingTable(20, ConvertPeerID(local), time.Hour, m) - var peers []peer.ID - for i := 0; i < 500; i++ { - peers = append(peers, tu.RandPeerIDFatal(t)) - } - - done := make(chan struct{}) - go func() { - for i := 0; i < 1000; i++ { - n := rand.Intn(len(peers)) - tab.Update(peers[n]) - } - done <- struct{}{} - }() - - go func() { - for i := 0; i < 1000; i++ { - n := rand.Intn(len(peers)) - tab.Update(peers[n]) - } - done <- struct{}{} - }() - - go func() { - for i := 0; i < 1000; i++ { - n := rand.Intn(len(peers)) - tab.Find(peers[n]) - } - done <- struct{}{} - }() - <-done - <-done - <-done -} - -func BenchmarkUpdates(b *testing.B) { - b.StopTimer() - local := ConvertKey("localKey") - m := pstore.NewMetrics() - tab := NewRoutingTable(20, local, time.Hour, m) - - var peers []peer.ID - for i := 0; i < b.N; i++ { - peers = append(peers, tu.RandPeerIDFatal(b)) - } - - b.StartTimer() - for i := 0; i < b.N; i++ { - tab.Update(peers[i]) - } -} - -func BenchmarkFinds(b *testing.B) { - b.StopTimer() - local := ConvertKey("localKey") - m := pstore.NewMetrics() - tab := NewRoutingTable(20, local, time.Hour, m) - - var peers []peer.ID - for i := 0; i < b.N; i++ { - peers = append(peers, tu.RandPeerIDFatal(b)) - tab.Update(peers[i]) - } - - b.StartTimer() - for i := 0; i < b.N; i++ { - tab.Find(peers[i]) - } -} diff --git a/routing/kbucket/util.go b/routing/kbucket/util.go deleted file mode 100644 index 2722540d6..000000000 --- a/routing/kbucket/util.go +++ /dev/null @@ -1,63 +0,0 @@ -package kbucket - -import ( - "bytes" - "crypto/sha256" - "errors" - - ks "github.com/ipfs/go-ipfs/routing/keyspace" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" -) - -// Returned if a routing table query returns no results. This is NOT expected -// behaviour -var ErrLookupFailure = errors.New("failed to find any peer in table") - -// ID for IpfsDHT is in the XORKeySpace -// -// The type dht.ID signifies that its contents have been hashed from either a -// peer.ID or a util.Key. This unifies the keyspace -type ID []byte - -func (id ID) equal(other ID) bool { - return bytes.Equal(id, other) -} - -func (id ID) less(other ID) bool { - a := ks.Key{Space: ks.XORKeySpace, Bytes: id} - b := ks.Key{Space: ks.XORKeySpace, Bytes: other} - return a.Less(b) -} - -func xor(a, b ID) ID { - return ID(u.XOR(a, b)) -} - -func commonPrefixLen(a, b ID) int { - return ks.ZeroPrefixLen(u.XOR(a, b)) -} - -// ConvertPeerID creates a DHT ID by hashing a Peer ID (Multihash) -func ConvertPeerID(id peer.ID) ID { - hash := sha256.Sum256([]byte(id)) - return hash[:] -} - -// ConvertKey creates a DHT ID by hashing a local key (String) -func ConvertKey(id key.Key) ID { - hash := sha256.Sum256([]byte(id)) - return hash[:] -} - -// Closer returns true if a is closer to key than b is -func Closer(a, b peer.ID, key key.Key) bool { - aid := ConvertPeerID(a) - bid := ConvertPeerID(b) - tgt := ConvertKey(key) - adist := xor(aid, tgt) - bdist := xor(bid, tgt) - - return adist.less(bdist) -} diff --git a/routing/keyspace/keyspace.go b/routing/keyspace/keyspace.go deleted file mode 100644 index e26a0e6d0..000000000 --- a/routing/keyspace/keyspace.go +++ /dev/null @@ -1,97 +0,0 @@ -package keyspace - -import ( - "sort" - - "math/big" -) - -// Key represents an identifier in a KeySpace. It holds a reference to the -// associated KeySpace, as well references to both the Original identifier, -// as well as the new, KeySpace Bytes one. -type Key struct { - - // Space is the KeySpace this Key is related to. - Space KeySpace - - // Original is the original value of the identifier - Original []byte - - // Bytes is the new value of the identifier, in the KeySpace. - Bytes []byte -} - -// Equal returns whether this key is equal to another. -func (k1 Key) Equal(k2 Key) bool { - if k1.Space != k2.Space { - panic("k1 and k2 not in same key space.") - } - return k1.Space.Equal(k1, k2) -} - -// Less returns whether this key comes before another. -func (k1 Key) Less(k2 Key) bool { - if k1.Space != k2.Space { - panic("k1 and k2 not in same key space.") - } - return k1.Space.Less(k1, k2) -} - -// Distance returns this key's distance to another -func (k1 Key) Distance(k2 Key) *big.Int { - if k1.Space != k2.Space { - panic("k1 and k2 not in same key space.") - } - return k1.Space.Distance(k1, k2) -} - -// KeySpace is an object used to do math on identifiers. Each keyspace has its -// own properties and rules. See XorKeySpace. -type KeySpace interface { - - // Key converts an identifier into a Key in this space. - Key([]byte) Key - - // Equal returns whether keys are equal in this key space - Equal(Key, Key) bool - - // Distance returns the distance metric in this key space - Distance(Key, Key) *big.Int - - // Less returns whether the first key is smaller than the second. - Less(Key, Key) bool -} - -// byDistanceToCenter is a type used to sort Keys by proximity to a center. -type byDistanceToCenter struct { - Center Key - Keys []Key -} - -func (s byDistanceToCenter) Len() int { - return len(s.Keys) -} - -func (s byDistanceToCenter) Swap(i, j int) { - s.Keys[i], s.Keys[j] = s.Keys[j], s.Keys[i] -} - -func (s byDistanceToCenter) Less(i, j int) bool { - a := s.Center.Distance(s.Keys[i]) - b := s.Center.Distance(s.Keys[j]) - return a.Cmp(b) == -1 -} - -// SortByDistance takes a KeySpace, a center Key, and a list of Keys toSort. -// It returns a new list, where the Keys toSort have been sorted by their -// distance to the center Key. -func SortByDistance(sp KeySpace, center Key, toSort []Key) []Key { - toSortCopy := make([]Key, len(toSort)) - copy(toSortCopy, toSort) - bdtc := &byDistanceToCenter{ - Center: center, - Keys: toSortCopy, // copy - } - sort.Sort(bdtc) - return bdtc.Keys -} diff --git a/routing/keyspace/xor.go b/routing/keyspace/xor.go deleted file mode 100644 index fd96fd65b..000000000 --- a/routing/keyspace/xor.go +++ /dev/null @@ -1,67 +0,0 @@ -package keyspace - -import ( - "bytes" - "crypto/sha256" - "math/big" - - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" -) - -// XORKeySpace is a KeySpace which: -// - normalizes identifiers using a cryptographic hash (sha256) -// - measures distance by XORing keys together -var XORKeySpace = &xorKeySpace{} -var _ KeySpace = XORKeySpace // ensure it conforms - -type xorKeySpace struct{} - -// Key converts an identifier into a Key in this space. -func (s *xorKeySpace) Key(id []byte) Key { - hash := sha256.Sum256(id) - key := hash[:] - return Key{ - Space: s, - Original: id, - Bytes: key, - } -} - -// Equal returns whether keys are equal in this key space -func (s *xorKeySpace) Equal(k1, k2 Key) bool { - return bytes.Equal(k1.Bytes, k2.Bytes) -} - -// Distance returns the distance metric in this key space -func (s *xorKeySpace) Distance(k1, k2 Key) *big.Int { - // XOR the keys - k3 := u.XOR(k1.Bytes, k2.Bytes) - - // interpret it as an integer - dist := big.NewInt(0).SetBytes(k3) - return dist -} - -// Less returns whether the first key is smaller than the second. -func (s *xorKeySpace) Less(k1, k2 Key) bool { - a := k1.Bytes - b := k2.Bytes - for i := 0; i < len(a); i++ { - if a[i] != b[i] { - return a[i] < b[i] - } - } - return true -} - -// ZeroPrefixLen returns the number of consecutive zeroes in a byte slice. -func ZeroPrefixLen(id []byte) int { - for i := 0; i < len(id); i++ { - for j := 0; j < 8; j++ { - if (id[i]>>uint8(7-j))&0x1 != 0 { - return i*8 + j - } - } - } - return len(id) * 8 -} diff --git a/routing/keyspace/xor_test.go b/routing/keyspace/xor_test.go deleted file mode 100644 index a461c094e..000000000 --- a/routing/keyspace/xor_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package keyspace - -import ( - "bytes" - "math/big" - "testing" - - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" -) - -func TestPrefixLen(t *testing.T) { - cases := [][]byte{ - {0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x58, 0xFF, 0x80, 0x00, 0x00, 0xF0}, - } - lens := []int{24, 56, 9} - - for i, c := range cases { - r := ZeroPrefixLen(c) - if r != lens[i] { - t.Errorf("ZeroPrefixLen failed: %v != %v", r, lens[i]) - } - } - -} - -func TestXorKeySpace(t *testing.T) { - - ids := [][]byte{ - {0xFF, 0xFF, 0xFF, 0xFF}, - {0x00, 0x00, 0x00, 0x00}, - {0xFF, 0xFF, 0xFF, 0xF0}, - } - - ks := [][2]Key{ - {XORKeySpace.Key(ids[0]), XORKeySpace.Key(ids[0])}, - {XORKeySpace.Key(ids[1]), XORKeySpace.Key(ids[1])}, - {XORKeySpace.Key(ids[2]), XORKeySpace.Key(ids[2])}, - } - - for i, set := range ks { - if !set[0].Equal(set[1]) { - t.Errorf("Key not eq. %v != %v", set[0], set[1]) - } - - if !bytes.Equal(set[0].Bytes, set[1].Bytes) { - t.Errorf("Key gen failed. %v != %v", set[0].Bytes, set[1].Bytes) - } - - if !bytes.Equal(set[0].Original, ids[i]) { - t.Errorf("ptrs to original. %v != %v", set[0].Original, ids[i]) - } - - if len(set[0].Bytes) != 32 { - t.Errorf("key length incorrect. 32 != %d", len(set[0].Bytes)) - } - } - - for i := 1; i < len(ks); i++ { - if ks[i][0].Less(ks[i-1][0]) == ks[i-1][0].Less(ks[i][0]) { - t.Errorf("less should be different.") - } - - if ks[i][0].Distance(ks[i-1][0]).Cmp(ks[i-1][0].Distance(ks[i][0])) != 0 { - t.Errorf("distance should be the same.") - } - - if ks[i][0].Equal(ks[i-1][0]) { - t.Errorf("Keys should not be eq. %v != %v", ks[i][0], ks[i-1][0]) - } - } -} - -func TestDistancesAndCenterSorting(t *testing.T) { - - adjs := [][]byte{ - {173, 149, 19, 27, 192, 183, 153, 192, 177, 175, 71, 127, 177, 79, 207, 38, 166, 169, 247, 96, 121, 228, 139, 240, 144, 172, 183, 232, 54, 123, 253, 14}, - {223, 63, 97, 152, 4, 169, 47, 219, 64, 87, 25, 45, 196, 61, 215, 72, 234, 119, 138, 220, 82, 188, 73, 140, 232, 5, 36, 192, 20, 184, 17, 25}, - {73, 176, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 127}, - {73, 176, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 127}, - {73, 176, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 126}, - {73, 0, 221, 176, 149, 143, 22, 42, 129, 124, 213, 114, 232, 95, 189, 154, 18, 3, 122, 132, 32, 199, 53, 185, 58, 157, 117, 78, 52, 146, 157, 127}, - } - - keys := make([]Key, len(adjs)) - for i, a := range adjs { - keys[i] = Key{Space: XORKeySpace, Bytes: a} - } - - cmp := func(a int64, b *big.Int) int { - return big.NewInt(a).Cmp(b) - } - - if 0 != cmp(0, keys[2].Distance(keys[3])) { - t.Errorf("distance calculation wrong: %v", keys[2].Distance(keys[3])) - } - - if 0 != cmp(1, keys[2].Distance(keys[4])) { - t.Errorf("distance calculation wrong: %v", keys[2].Distance(keys[4])) - } - - d1 := keys[2].Distance(keys[5]) - d2 := u.XOR(keys[2].Bytes, keys[5].Bytes) - d2 = d2[len(keys[2].Bytes)-len(d1.Bytes()):] // skip empty space for big - if !bytes.Equal(d1.Bytes(), d2) { - t.Errorf("bytes should be the same. %v == %v", d1.Bytes(), d2) - } - - if -1 != cmp(2<<32, keys[2].Distance(keys[5])) { - t.Errorf("2<<32 should be smaller") - } - - keys2 := SortByDistance(XORKeySpace, keys[2], keys) - order := []int{2, 3, 4, 5, 1, 0} - for i, o := range order { - if !bytes.Equal(keys[o].Bytes, keys2[i].Bytes) { - t.Errorf("order is wrong. %d?? %v == %v", o, keys[o], keys2[i]) - } - } - -} diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index d6f921845..1fa190028 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -4,11 +4,7 @@ import ( "errors" "time" - routing "github.com/ipfs/go-ipfs/routing" - dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" @@ -16,7 +12,11 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" + dhtpb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/dht.go b/routing/mock/dht.go index dfda92770..23b37ba4e 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -1,9 +1,9 @@ package mockrouting import ( - dht "github.com/ipfs/go-ipfs/routing/dht" "github.com/ipfs/go-ipfs/thirdparty/testutil" mocknet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net/mock" + dht "gx/ipfs/QmYvLYkYiVEi5LBHP2uFqiUaHqH7zWnEuRqoNEuGLNG6JB/go-libp2p-kad-dht" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" diff --git a/routing/mock/interface.go b/routing/mock/interface.go index b0c1b14a1..b0f93d2b9 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -5,13 +5,14 @@ package mockrouting import ( - routing "github.com/ipfs/go-ipfs/routing" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" + peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 0142cf1e6..3ca2ce88a 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -4,12 +4,12 @@ import ( "errors" repo "github.com/ipfs/go-ipfs/repo" - routing "github.com/ipfs/go-ipfs/routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" p2phost "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/host" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 66564d0ef..380a146c4 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -4,11 +4,11 @@ import ( "errors" "time" - routing "github.com/ipfs/go-ipfs/routing" - pb "github.com/ipfs/go-ipfs/routing/dht/pb" - record "github.com/ipfs/go-ipfs/routing/record" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" + record "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record" + pb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" diff --git a/routing/record/record.go b/routing/record/record.go deleted file mode 100644 index 59b66f68b..000000000 --- a/routing/record/record.go +++ /dev/null @@ -1,48 +0,0 @@ -package record - -import ( - "bytes" - - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - - pb "github.com/ipfs/go-ipfs/routing/dht/pb" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" -) - -var log = logging.Logger("routing/record") - -// MakePutRecord creates and signs a dht record for the given key/value pair -func MakePutRecord(sk ci.PrivKey, key key.Key, value []byte, sign bool) (*pb.Record, error) { - record := new(pb.Record) - - record.Key = proto.String(string(key)) - record.Value = value - - pkh, err := sk.GetPublic().Hash() - if err != nil { - return nil, err - } - - record.Author = proto.String(string(pkh)) - if sign { - blob := RecordBlobForSig(record) - - sig, err := sk.Sign(blob) - if err != nil { - return nil, err - } - - record.Signature = sig - } - return record, nil -} - -// RecordBlobForSig returns the blob protected by the record signature -func RecordBlobForSig(r *pb.Record) []byte { - k := []byte(r.GetKey()) - v := []byte(r.GetValue()) - a := []byte(r.GetAuthor()) - return bytes.Join([][]byte{k, v, a}, []byte{}) -} diff --git a/routing/record/selection.go b/routing/record/selection.go deleted file mode 100644 index 5b1f5bb98..000000000 --- a/routing/record/selection.go +++ /dev/null @@ -1,40 +0,0 @@ -package record - -import ( - "errors" - - path "github.com/ipfs/go-ipfs/path" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" -) - -// A SelectorFunc selects the best value for the given key from -// a slice of possible values and returns the index of the chosen one -type SelectorFunc func(key.Key, [][]byte) (int, error) - -type Selector map[string]SelectorFunc - -func (s Selector) BestRecord(k key.Key, recs [][]byte) (int, error) { - if len(recs) == 0 { - return 0, errors.New("no records given!") - } - - parts := path.SplitList(string(k)) - if len(parts) < 3 { - log.Infof("Record key does not have selectorfunc: %s", k) - return 0, errors.New("record key does not have selectorfunc") - } - - sel, ok := s[parts[1]] - if !ok { - log.Infof("Unrecognized key prefix: %s", parts[1]) - return 0, ErrInvalidRecordType - } - - return sel(k, recs) -} - -// PublicKeySelector just selects the first entry. -// All valid public key records will be equivalent. -func PublicKeySelector(k key.Key, vals [][]byte) (int, error) { - return 0, nil -} diff --git a/routing/record/validation.go b/routing/record/validation.go deleted file mode 100644 index 65e181fda..000000000 --- a/routing/record/validation.go +++ /dev/null @@ -1,114 +0,0 @@ -package record - -import ( - "bytes" - "errors" - "fmt" - - path "github.com/ipfs/go-ipfs/path" - pb "github.com/ipfs/go-ipfs/routing/dht/pb" - ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" -) - -// ValidatorFunc is a function that is called to validate a given -// type of DHTRecord. -type ValidatorFunc func(key.Key, []byte) error - -// ErrBadRecord is returned any time a dht record is found to be -// incorrectly formatted or signed. -var ErrBadRecord = errors.New("bad dht record") - -// ErrInvalidRecordType is returned if a DHTRecord keys prefix -// is not found in the Validator map of the DHT. -var ErrInvalidRecordType = errors.New("invalid record keytype") - -// Validator is an object that helps ensure routing records are valid. -// It is a collection of validator functions, each of which implements -// its own notion of validity. -type Validator map[string]*ValidChecker - -type ValidChecker struct { - Func ValidatorFunc - Sign bool -} - -// VerifyRecord checks a record and ensures it is still valid. -// It runs needed validators -func (v Validator) VerifyRecord(r *pb.Record) error { - // Now, check validity func - parts := path.SplitList(r.GetKey()) - if len(parts) < 3 { - log.Infof("Record key does not have validator: %s", key.Key(r.GetKey())) - return nil - } - - val, ok := v[parts[1]] - if !ok { - log.Infof("Unrecognized key prefix: %s", parts[1]) - return ErrInvalidRecordType - } - - return val.Func(key.Key(r.GetKey()), r.GetValue()) -} - -func (v Validator) IsSigned(k key.Key) (bool, error) { - // Now, check validity func - parts := path.SplitList(string(k)) - if len(parts) < 3 { - log.Infof("Record key does not have validator: %s", k) - return false, nil - } - - val, ok := v[parts[1]] - if !ok { - log.Infof("Unrecognized key prefix: %s", parts[1]) - return false, ErrInvalidRecordType - } - - return val.Sign, nil -} - -// ValidatePublicKeyRecord implements ValidatorFunc and -// verifies that the passed in record value is the PublicKey -// that matches the passed in key. -func ValidatePublicKeyRecord(k key.Key, val []byte) error { - if len(k) < 5 { - return errors.New("invalid public key record key") - } - - prefix := string(k[:4]) - if prefix != "/pk/" { - return errors.New("key was not prefixed with /pk/") - } - - keyhash := []byte(k[4:]) - if _, err := mh.Cast(keyhash); err != nil { - return fmt.Errorf("key did not contain valid multihash: %s", err) - } - - pkh := u.Hash(val) - if !bytes.Equal(keyhash, pkh) { - return errors.New("public key does not match storage key") - } - return nil -} - -var PublicKeyValidator = &ValidChecker{ - Func: ValidatePublicKeyRecord, - Sign: false, -} - -func CheckRecordSig(r *pb.Record, pk ci.PubKey) error { - blob := RecordBlobForSig(r) - good, err := pk.Verify(blob, r.Signature) - if err != nil { - return nil - } - if !good { - return errors.New("invalid record signature") - } - return nil -} diff --git a/routing/record/validation_test.go b/routing/record/validation_test.go deleted file mode 100644 index 175f902d8..000000000 --- a/routing/record/validation_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package record - -import ( - "encoding/base64" - "testing" - - ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" -) - -var OffensiveKey = "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDjXAQQMal4SB2tSnX6NJIPmC69/BT8A8jc7/gDUZNkEhdhYHvc7k7S4vntV/c92nJGxNdop9fKJyevuNMuXhhHAgMBAAE=" - -func TestValidatePublicKey(t *testing.T) { - pkb, err := base64.StdEncoding.DecodeString(OffensiveKey) - if err != nil { - t.Fatal(err) - } - - pubk, err := ci.UnmarshalPublicKey(pkb) - if err != nil { - t.Fatal(err) - } - - pkh, err := pubk.Hash() - if err != nil { - t.Fatal(err) - } - - k := key.Key("/pk/" + string(pkh)) - - err = ValidatePublicKeyRecord(k, pkb) - if err != nil { - t.Fatal(err) - } -} diff --git a/routing/routing.go b/routing/routing.go deleted file mode 100644 index 6e15bed6f..000000000 --- a/routing/routing.go +++ /dev/null @@ -1,105 +0,0 @@ -// package routing defines the interface for a routing system used by ipfs. -package routing - -import ( - "errors" - - ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" -) - -// ErrNotFound is returned when a search fails to find anything -var ErrNotFound = errors.New("routing: not found") - -// ContentRouting is a value provider layer of indirection. It is used to find -// information about who has what content. -type ContentRouting interface { - // Announce that this node can provide value for given key - Provide(context.Context, key.Key) error - - // Search for peers who are able to provide a given key - FindProvidersAsync(context.Context, key.Key, int) <-chan pstore.PeerInfo -} - -// PeerRouting is a way to find information about certain peers. -// This can be implemented by a simple lookup table, a tracking server, -// or even a DHT. -type PeerRouting interface { - // Find specific Peer - // FindPeer searches for a peer with given ID, returns a pstore.PeerInfo - // with relevant addresses. - FindPeer(context.Context, peer.ID) (pstore.PeerInfo, error) -} - -type ValueStore interface { - // Basic Put/Get - - // PutValue adds value corresponding to given Key. - PutValue(context.Context, key.Key, []byte) error - - // GetValue searches for the value corresponding to given Key. - GetValue(context.Context, key.Key) ([]byte, error) - - // GetValues searches for values corresponding to given Key. - // - // Passing a value of '0' for the count argument will cause the - // routing interface to return values only from cached or local storage - // and return an error if no cached value is found. - // - // Passing a value of '1' will return a local value if found, and query - // the network for the first value it finds otherwise. - // As a result, a value of '1' is mostly useful for cases where the record - // in question has only one valid value (such as public keys) - GetValues(c context.Context, k key.Key, count int) ([]RecvdVal, error) -} - -// IpfsRouting is the combination of different routing types that ipfs -// uses. It can be satisfied by a single item (such as a DHT) or multiple -// different pieces that are more optimized to each task. -type IpfsRouting interface { - ContentRouting - PeerRouting - ValueStore - - // Bootstrap allows callers to hint to the routing system to get into a - // Boostrapped state - Bootstrap(context.Context) error - - // TODO expose io.Closer or plain-old Close error -} - -// RecvdVal represents a dht value record that has been received from a given peer -// it is used to track peers with expired records in order to correct them. -type RecvdVal struct { - From peer.ID - Val []byte -} - -type PubKeyFetcher interface { - GetPublicKey(context.Context, peer.ID) (ci.PubKey, error) -} - -// KeyForPublicKey returns the key used to retrieve public keys -// from the dht. -func KeyForPublicKey(id peer.ID) key.Key { - return key.Key("/pk/" + string(id)) -} - -func GetPublicKey(r ValueStore, ctx context.Context, pkhash []byte) (ci.PubKey, error) { - if dht, ok := r.(PubKeyFetcher); ok { - // If we have a DHT as our routing system, use optimized fetcher - return dht.GetPublicKey(ctx, peer.ID(pkhash)) - } else { - key := key.Key("/pk/" + string(pkhash)) - pkval, err := r.GetValue(ctx, key) - if err != nil { - return nil, err - } - - // get PublicKey from node.Data - return ci.UnmarshalPublicKey(pkval) - } -} diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 98ad4026e..100bfa6c7 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -5,19 +5,19 @@ import ( "errors" "time" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - - routing "github.com/ipfs/go-ipfs/routing" - pb "github.com/ipfs/go-ipfs/routing/dht/pb" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - loggables "gx/ipfs/QmYrv4LgCC8FhG2Ab4bwuq5DqBdwMtx3hMb3KKJDZcr2d7/go-libp2p-loggables" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/host" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + loggables "gx/ipfs/QmYrv4LgCC8FhG2Ab4bwuq5DqBdwMtx3hMb3KKJDZcr2d7/go-libp2p-loggables" + dhtpb "gx/ipfs/QmYvLYkYiVEi5LBHP2uFqiUaHqH7zWnEuRqoNEuGLNG6JB/go-libp2p-kad-dht/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" + pb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" ) var log = logging.Logger("supernode") @@ -45,13 +45,13 @@ func (c *Client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-c ch := make(chan pstore.PeerInfo) go func() { defer close(ch) - request := pb.NewMessage(pb.Message_GET_PROVIDERS, string(k), 0) + request := dhtpb.NewMessage(dhtpb.Message_GET_PROVIDERS, string(k), 0) response, err := c.proxy.SendRequest(ctx, request) if err != nil { log.Debug(err) return } - for _, p := range pb.PBPeersToPeerInfos(response.GetProviderPeers()) { + for _, p := range dhtpb.PBPeersToPeerInfos(response.GetProviderPeers()) { select { case <-ctx.Done(): log.Debug(ctx.Err()) @@ -69,14 +69,14 @@ func (c *Client) PutValue(ctx context.Context, k key.Key, v []byte) error { if err != nil { return err } - pmes := pb.NewMessage(pb.Message_PUT_VALUE, string(k), 0) + pmes := dhtpb.NewMessage(dhtpb.Message_PUT_VALUE, string(k), 0) pmes.Record = r return c.proxy.SendMessage(ctx, pmes) // wrap to hide the remote } func (c *Client) GetValue(ctx context.Context, k key.Key) ([]byte, error) { defer log.EventBegin(ctx, "getValue", &k).Done() - msg := pb.NewMessage(pb.Message_GET_VALUE, string(k), 0) + msg := dhtpb.NewMessage(dhtpb.Message_GET_VALUE, string(k), 0) response, err := c.proxy.SendRequest(ctx, msg) // TODO wrap to hide the remote if err != nil { return nil, err @@ -86,7 +86,7 @@ func (c *Client) GetValue(ctx context.Context, k key.Key) ([]byte, error) { func (c *Client) GetValues(ctx context.Context, k key.Key, _ int) ([]routing.RecvdVal, error) { defer log.EventBegin(ctx, "getValue", &k).Done() - msg := pb.NewMessage(pb.Message_GET_VALUE, string(k), 0) + msg := dhtpb.NewMessage(dhtpb.Message_GET_VALUE, string(k), 0) response, err := c.proxy.SendRequest(ctx, msg) // TODO wrap to hide the remote if err != nil { return nil, err @@ -102,9 +102,9 @@ func (c *Client) GetValues(ctx context.Context, k key.Key, _ int) ([]routing.Rec func (c *Client) Provide(ctx context.Context, k key.Key) error { defer log.EventBegin(ctx, "provide", &k).Done() - msg := pb.NewMessage(pb.Message_ADD_PROVIDER, string(k), 0) + msg := dhtpb.NewMessage(dhtpb.Message_ADD_PROVIDER, string(k), 0) // FIXME how is connectedness defined for the local node - pri := []pb.PeerRoutingInfo{ + pri := []dhtpb.PeerRoutingInfo{ { PeerInfo: pstore.PeerInfo{ ID: c.local, @@ -112,18 +112,18 @@ func (c *Client) Provide(ctx context.Context, k key.Key) error { }, }, } - msg.ProviderPeers = pb.PeerRoutingInfosToPBPeers(pri) + msg.ProviderPeers = dhtpb.PeerRoutingInfosToPBPeers(pri) return c.proxy.SendMessage(ctx, msg) // TODO wrap to hide remote } func (c *Client) FindPeer(ctx context.Context, id peer.ID) (pstore.PeerInfo, error) { defer log.EventBegin(ctx, "findPeer", id).Done() - request := pb.NewMessage(pb.Message_FIND_NODE, string(id), 0) + request := dhtpb.NewMessage(dhtpb.Message_FIND_NODE, string(id), 0) response, err := c.proxy.SendRequest(ctx, request) // hide remote if err != nil { return pstore.PeerInfo{}, err } - for _, p := range pb.PBPeersToPeerInfos(response.GetCloserPeers()) { + for _, p := range dhtpb.PBPeersToPeerInfos(response.GetCloserPeers()) { if p.ID == id { return p, nil } diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 9f47ff5e7..1ce2dbfc7 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -1,12 +1,11 @@ package proxy import ( - ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - - dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" inet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + dhtpb "gx/ipfs/QmYvLYkYiVEi5LBHP2uFqiUaHqH7zWnEuRqoNEuGLNG6JB/go-libp2p-kad-dht/pb" + ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 396dc0f9f..fad4b8790 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -14,9 +14,9 @@ import ( key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - kbucket "github.com/ipfs/go-ipfs/routing/kbucket" + kbucket "gx/ipfs/QmTZsN8hysGnbakvK6mS8rwDQ9uwokxmWFBv94pig6zGd1/go-libp2p-kbucket" loggables "gx/ipfs/QmYrv4LgCC8FhG2Ab4bwuq5DqBdwMtx3hMb3KKJDZcr2d7/go-libp2p-loggables" + dhtpb "gx/ipfs/QmYvLYkYiVEi5LBHP2uFqiUaHqH7zWnEuRqoNEuGLNG6JB/go-libp2p-kad-dht/pb" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index d3473d12d..8fab1a42a 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -4,16 +4,17 @@ import ( "errors" "fmt" - dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - record "github.com/ipfs/go-ipfs/routing/record" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + dhtpb "gx/ipfs/QmYvLYkYiVEi5LBHP2uFqiUaHqH7zWnEuRqoNEuGLNG6JB/go-libp2p-kad-dht/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" + record "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record" + pb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" ) // Server handles routing queries using a database backend @@ -115,7 +116,7 @@ func (s *Server) handleMessage( var _ proxy.RequestHandler = &Server{} var _ proxy.Proxy = &Server{} -func getRoutingRecord(ds datastore.Datastore, k key.Key) (*dhtpb.Record, error) { +func getRoutingRecord(ds datastore.Datastore, k key.Key) (*pb.Record, error) { dskey := k.DsKey() val, err := ds.Get(dskey) if err != nil { @@ -125,14 +126,14 @@ func getRoutingRecord(ds datastore.Datastore, k key.Key) (*dhtpb.Record, error) if !ok { return nil, fmt.Errorf("datastore had non byte-slice value for %v", dskey) } - var record dhtpb.Record + var record pb.Record if err := proto.Unmarshal(recordBytes, &record); err != nil { return nil, errors.New("failed to unmarshal dht record from datastore") } return &record, nil } -func putRoutingRecord(ds datastore.Datastore, k key.Key, value *dhtpb.Record) error { +func putRoutingRecord(ds datastore.Datastore, k key.Key, value *pb.Record) error { data, err := proto.Marshal(value) if err != nil { return err @@ -204,7 +205,7 @@ func providerKey(k key.Key) datastore.Key { return datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) } -func verify(ps pstore.Peerstore, r *dhtpb.Record) error { +func verify(ps pstore.Peerstore, r *pb.Record) error { v := make(record.Validator) v["pk"] = record.PublicKeyValidator p := peer.ID(r.GetAuthor()) diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 4cbc7de6f..0531d1f8c 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,7 +3,7 @@ package supernode import ( "testing" - dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" + dhtpb "gx/ipfs/QmYvLYkYiVEi5LBHP2uFqiUaHqH7zWnEuRqoNEuGLNG6JB/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) From bf3dac5140b3f351c0fc58b0458b3ae72693ccd1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 13 Sep 2016 15:17:07 -0700 Subject: [PATCH 1515/3817] routing: use extracted dht and routing code License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@2692e442b38df5193da5e7b1b327b0bd89d02e0e --- namesys/namesys.go | 2 +- namesys/publisher.go | 14 +++++++------- namesys/republisher/repub.go | 8 ++++---- namesys/routing.go | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 87c1854cd..5b3643cf4 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,10 +5,10 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" - routing "github.com/ipfs/go-ipfs/routing" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/publisher.go b/namesys/publisher.go index 4e634cef0..729532a1a 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -6,22 +6,22 @@ import ( "fmt" "time" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dag "github.com/ipfs/go-ipfs/merkledag" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" pin "github.com/ipfs/go-ipfs/pin" - routing "github.com/ipfs/go-ipfs/routing" - dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - record "github.com/ipfs/go-ipfs/routing/record" ft "github.com/ipfs/go-ipfs/unixfs" + ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" + record "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record" + dhtpb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 6b9174953..b148babe8 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -8,9 +8,6 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - "github.com/ipfs/go-ipfs/routing" - dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" @@ -19,7 +16,10 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" + recpb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" ) var errNoEntry = errors.New("no previous entry") @@ -115,7 +115,7 @@ func (rp *Republisher) getLastVal(k key.Key) (path.Path, uint64, error) { } val := ival.([]byte) - dhtrec := new(dhtpb.Record) + dhtrec := new(recpb.Record) err = proto.Unmarshal(val, dhtrec) if err != nil { return "", 0, err diff --git a/namesys/routing.go b/namesys/routing.go index 31c863fce..6336a1782 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -12,8 +12,8 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - routing "github.com/ipfs/go-ipfs/routing" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" From 51e26851a4d0c9bc770d2fabb0399619180767f2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 25 Sep 2016 23:42:14 -0700 Subject: [PATCH 1516/3817] update libp2p and dht packages License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@f8c488edfb489bcb269fe7387234bdbd45afa7f8 --- routing/mock/centralized_client.go | 4 ++-- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 4 ++-- routing/mock/interface.go | 4 ++-- routing/none/none_client.go | 6 +++--- routing/offline/offline.go | 4 ++-- routing/supernode/client.go | 8 ++++---- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 10 +++++----- routing/supernode/server.go | 4 ++-- routing/supernode/server_test.go | 2 +- 12 files changed, 27 insertions(+), 27 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 1fa190028..7ac431aca 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,15 +8,15 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" dhtpb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" + routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index b8e95762b..a112b0d53 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -11,8 +11,8 @@ import ( key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index c66085e62..d68fc5e5c 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,8 +8,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 23b37ba4e..e987a5e49 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -2,9 +2,9 @@ package mockrouting import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net/mock" - dht "gx/ipfs/QmYvLYkYiVEi5LBHP2uFqiUaHqH7zWnEuRqoNEuGLNG6JB/go-libp2p-kad-dht" + dht "gx/ipfs/QmXVWh4XWRaRGdcGeFtBp3hx7H3mzYgVRpVN7LwtTaYv2E/go-libp2p-kad-dht" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + mocknet "gx/ipfs/QmbiRCGZqhfcSjnm9icGz3oNQQdPLAnLWnKHXixaEWXVCN/go-libp2p/p2p/net/mock" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index b0f93d2b9..6cde15ab3 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -9,11 +9,11 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" + routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" ) // Server provides mockrouting Clients diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 3ca2ce88a..82e2bd02e 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -5,12 +5,12 @@ import ( repo "github.com/ipfs/go-ipfs/repo" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - p2phost "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/host" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + p2phost "gx/ipfs/QmbiRCGZqhfcSjnm9icGz3oNQQdPLAnLWnKHXixaEWXVCN/go-libp2p/p2p/host" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" + routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 380a146c4..982e46ea7 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -6,16 +6,16 @@ import ( ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" record "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record" pb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" + routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" ) var log = logging.Logger("offlinerouting") diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 100bfa6c7..b76b30080 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,16 +8,16 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/host" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + dhtpb "gx/ipfs/QmXVWh4XWRaRGdcGeFtBp3hx7H3mzYgVRpVN7LwtTaYv2E/go-libp2p-kad-dht/pb" + pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" loggables "gx/ipfs/QmYrv4LgCC8FhG2Ab4bwuq5DqBdwMtx3hMb3KKJDZcr2d7/go-libp2p-loggables" - dhtpb "gx/ipfs/QmYvLYkYiVEi5LBHP2uFqiUaHqH7zWnEuRqoNEuGLNG6JB/go-libp2p-kad-dht/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + "gx/ipfs/QmbiRCGZqhfcSjnm9icGz3oNQQdPLAnLWnKHXixaEWXVCN/go-libp2p/p2p/host" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" pb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" + routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 1ce2dbfc7..8afe91a75 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -1,11 +1,11 @@ package proxy import ( - inet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - dhtpb "gx/ipfs/QmYvLYkYiVEi5LBHP2uFqiUaHqH7zWnEuRqoNEuGLNG6JB/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmXVWh4XWRaRGdcGeFtBp3hx7H3mzYgVRpVN7LwtTaYv2E/go-libp2p-kad-dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + inet "gx/ipfs/QmbiRCGZqhfcSjnm9icGz3oNQQdPLAnLWnKHXixaEWXVCN/go-libp2p/p2p/net" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index fad4b8790..6d7ef439f 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -7,16 +7,16 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - host "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/host" - inet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" + pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" + host "gx/ipfs/QmbiRCGZqhfcSjnm9icGz3oNQQdPLAnLWnKHXixaEWXVCN/go-libp2p/p2p/host" + inet "gx/ipfs/QmbiRCGZqhfcSjnm9icGz3oNQQdPLAnLWnKHXixaEWXVCN/go-libp2p/p2p/net" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - kbucket "gx/ipfs/QmTZsN8hysGnbakvK6mS8rwDQ9uwokxmWFBv94pig6zGd1/go-libp2p-kbucket" + kbucket "gx/ipfs/QmVsCNFD32GzZ6Q5XD1TVGPRviNYqDdoNvgq853TU9hhzP/go-libp2p-kbucket" + dhtpb "gx/ipfs/QmXVWh4XWRaRGdcGeFtBp3hx7H3mzYgVRpVN7LwtTaYv2E/go-libp2p-kad-dht/pb" loggables "gx/ipfs/QmYrv4LgCC8FhG2Ab4bwuq5DqBdwMtx3hMb3KKJDZcr2d7/go-libp2p-loggables" - dhtpb "gx/ipfs/QmYvLYkYiVEi5LBHP2uFqiUaHqH7zWnEuRqoNEuGLNG6JB/go-libp2p-kad-dht/pb" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 8fab1a42a..d6c8a4805 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -7,12 +7,12 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - dhtpb "gx/ipfs/QmYvLYkYiVEi5LBHP2uFqiUaHqH7zWnEuRqoNEuGLNG6JB/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmXVWh4XWRaRGdcGeFtBp3hx7H3mzYgVRpVN7LwtTaYv2E/go-libp2p-kad-dht/pb" + pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" record "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record" pb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" ) diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 0531d1f8c..5bdec5ded 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,7 +3,7 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmYvLYkYiVEi5LBHP2uFqiUaHqH7zWnEuRqoNEuGLNG6JB/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmXVWh4XWRaRGdcGeFtBp3hx7H3mzYgVRpVN7LwtTaYv2E/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) From b08175b46e343b6d0eaf9d3dd07e9d2aa1d75f30 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 25 Sep 2016 23:42:14 -0700 Subject: [PATCH 1517/3817] update libp2p and dht packages License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@7b110e63470b2c80df947e7534327f3a5e763a35 --- namesys/namesys.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 8 ++++---- namesys/republisher/repub_test.go | 6 +++--- namesys/routing.go | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 5b3643cf4..a5f4e8a90 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -8,7 +8,7 @@ import ( ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" + routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/publisher.go b/namesys/publisher.go index 729532a1a..d70cc289c 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -19,9 +19,9 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" record "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record" dhtpb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" + routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index b148babe8..d208e093c 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -9,17 +9,17 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" + goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" + gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" recpb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" + routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 25cdb6ea5..0bb9f1031 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" + goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" "github.com/ipfs/go-ipfs/core" @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmUuwQUJmtvC6ReYcu7xaYKEUM3pD46H18dFn3LBhVt2Di/go-libp2p/p2p/net/mock" - pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore" + pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" + mocknet "gx/ipfs/QmbiRCGZqhfcSjnm9icGz3oNQQdPLAnLWnKHXixaEWXVCN/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index 6336a1782..bf481be30 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -13,7 +13,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - routing "gx/ipfs/QmcoQiBzRaaVv1DZbbXoDWiEtvDN94Ca1DcwnQKK2tP92s/go-libp2p-routing" + routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" From 014d43df82a94c6fb5e0332d8b97f3a685f51585 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 4 Oct 2016 18:21:19 -0700 Subject: [PATCH 1518/3817] gx publish 2.0.0 This commit was moved from ipfs/go-ipfs-util@03f76a71a0fc8a58f483f4725d02a442cbea1953 --- util/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/util.go b/util/util.go index 019d0420c..28873fd02 100644 --- a/util/util.go +++ b/util/util.go @@ -13,7 +13,7 @@ import ( "time" b58 "github.com/jbenet/go-base58" - mh "github.com/jbenet/go-multihash" + mh "github.com/multiformats/go-multihash" ) // DefaultIpfsHash is the current default hash function used by IPFS. From 7d198efa26097e76fbe089a7f6a4eb70284ba8a6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 5 Oct 2016 15:49:08 -0700 Subject: [PATCH 1519/3817] update to libp2p 4.0.1 and propogate other changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@0a64a3ecfe4afd1d2792cd94f27c3ef9ba4cfa8f --- ipld/merkledag/coding.go | 6 +++--- ipld/merkledag/merkledag.go | 6 +++--- ipld/merkledag/merkledag_test.go | 8 ++++---- ipld/merkledag/node.go | 8 ++++---- ipld/merkledag/node_test.go | 2 +- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/utils/diff.go | 4 ++-- ipld/merkledag/utils/utils.go | 2 +- ipld/merkledag/utils/utils_test.go | 4 ++-- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 136735615..eab7de058 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -6,9 +6,9 @@ import ( pb "github.com/ipfs/go-ipfs/merkledag/pb" - mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index d4a8403a3..bd415f49f 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -7,11 +7,11 @@ import ( "sync" bserv "github.com/ipfs/go-ipfs/blockservice" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) var log = logging.Logger("merkledag") diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 336a81caa..339c05609 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -19,11 +19,11 @@ import ( mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + "context" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) func TestNode(t *testing.T) { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 138828416..693e06b4e 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -3,11 +3,11 @@ package merkledag import ( "fmt" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + "context" - mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) var ErrLinkNotFound = fmt.Errorf("no link by that name") diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index a35013dca..f12334614 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -6,7 +6,7 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + "context" ) func TestRemoveLink(t *testing.T) { diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index a3bb06001..37c050426 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -4,7 +4,7 @@ package traverse import ( "errors" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + "context" mdag "github.com/ipfs/go-ipfs/merkledag" ) diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 406000596..e1f41a159 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -7,8 +7,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + context "context" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) const ( diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 5f795006b..a6f117ba4 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -3,7 +3,7 @@ package dagutils import ( "errors" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 0585f8684..6310b8939 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -7,8 +7,8 @@ import ( mdtest "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + context "context" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) func TestAddLink(t *testing.T) { From c038d5a774234f505d48e85ce43c58685ac6f1cd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 5 Oct 2016 15:49:08 -0700 Subject: [PATCH 1520/3817] update to libp2p 4.0.1 and propogate other changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@e5e18a5a234be143e44ed312f1b0da3853159872 --- pinning/pinner/gc/gc.go | 6 +++--- pinning/pinner/indirect.go | 2 +- pinning/pinner/pin.go | 6 +++--- pinning/pinner/pin_test.go | 6 +++--- pinning/pinner/set.go | 6 +++--- pinning/pinner/set_test.go | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 3e35c2b27..7bfde538c 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -6,11 +6,11 @@ import ( offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + context "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go index a837d60fd..b30a7c22d 100644 --- a/pinning/pinner/indirect.go +++ b/pinning/pinner/indirect.go @@ -1,7 +1,7 @@ package pin import ( - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" ) type indirectPin struct { diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index db9034624..6edd66abc 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -9,12 +9,12 @@ import ( "time" mdag "github.com/ipfs/go-ipfs/merkledag" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + context "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index af3aa08da..185b27a46 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -9,11 +9,11 @@ import ( "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" - "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) func randNode() (*mdag.Node, *cid.Cid) { diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index acb154e77..ec08971e9 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,12 +10,12 @@ import ( "sort" "unsafe" + "context" "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" + "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index a71cd0db6..f48fc9d17 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -1,6 +1,6 @@ package pin -import "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" +import "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" func ignoreKeys(key.Key) {} From 4cb17b0a06b35fd1d09993693f934dc829f1e3ca Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 5 Oct 2016 15:49:08 -0700 Subject: [PATCH 1521/3817] update to libp2p 4.0.1 and propogate other changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-interface@af55c45e5bdc2643642dbf208e0bed76d433390e --- exchange/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index 4b40d7390..f2edc569b 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -5,9 +5,9 @@ import ( "io" blocks "github.com/ipfs/go-ipfs/blocks" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" ) // Any type that implements exchange.Interface may be used as an IPFS block From 8d4134ab8060e5931571a13651ba176648652840 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 5 Oct 2016 15:49:08 -0700 Subject: [PATCH 1522/3817] update to libp2p 4.0.1 and propogate other changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@45cac731d8f66acd55de79447840bd0755733181 --- exchange/offline/offline.go | 4 ++-- exchange/offline/offline_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index b1a6ecb97..b483e1825 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -6,9 +6,9 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 8eaf75144..9cbd71333 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -3,13 +3,13 @@ package offline import ( "testing" + context "context" blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ds_sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) func TestBlockReturnsErr(t *testing.T) { From 44136bbbb7c2a43f20faad7dc57408abefe072e8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 5 Oct 2016 15:49:08 -0700 Subject: [PATCH 1523/3817] update to libp2p 4.0.1 and propogate other changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@e93f6d5437351fce67f08af4315bd06bb72774c6 --- mfs/dir.go | 2 +- mfs/fd.go | 2 +- mfs/file.go | 2 +- mfs/mfs_test.go | 6 +++--- mfs/repub_test.go | 4 ++-- mfs/system.go | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 3612516f5..8bc486cb7 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -9,7 +9,7 @@ import ( "sync" "time" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" diff --git a/mfs/fd.go b/mfs/fd.go index 2d3f2f3d0..9eb369316 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -6,7 +6,7 @@ import ( mod "github.com/ipfs/go-ipfs/unixfs/mod" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" ) type FileDescriptor interface { diff --git a/mfs/file.go b/mfs/file.go index 46ca7314b..e532fb088 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -9,7 +9,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 261ec76e1..269c24c16 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -23,11 +23,11 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + "context" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) func emptyDirNode() *dag.Node { diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 09d8d4124..2e9c49df5 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -6,8 +6,8 @@ import ( ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + "context" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index 3e2e74e76..f7e31d6d6 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -17,9 +17,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" + context "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) var ErrNotExist = errors.New("no such rootfs") From cdcbcd48db26052ec95b8a72b1bfbe08651950d0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 5 Oct 2016 15:49:08 -0700 Subject: [PATCH 1524/3817] update to libp2p 4.0.1 and propogate other changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@2176b40e1b3b6124343a38458b7d442d0927ae60 --- blockstore/arc_cache.go | 4 ++-- blockstore/arc_cache_test.go | 4 ++-- blockstore/blockstore.go | 6 +++--- blockstore/blockstore_test.go | 6 +++--- blockstore/bloom_cache.go | 4 ++-- blockstore/bloom_cache_test.go | 2 +- blockstore/caching.go | 2 +- blockstore/util/remove.go | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 5cc2ff433..fd6ff7eb9 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -1,13 +1,13 @@ package blockstore import ( - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" "github.com/ipfs/go-ipfs/blocks" + context "context" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index eb8086a79..4bf9307a8 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -4,9 +4,9 @@ import ( "testing" "github.com/ipfs/go-ipfs/blocks" - "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 45d27d1b5..162a21da0 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -7,14 +7,14 @@ import ( "sync" "sync/atomic" + context "context" blocks "github.com/ipfs/go-ipfs/blocks" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dsns "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/namespace" dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 096bf34cd..75385dd2a 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -5,14 +5,14 @@ import ( "fmt" "testing" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" ds_sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" blocks "github.com/ipfs/go-ipfs/blocks" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" ) func TestGetWhenKeyNotPresent(t *testing.T) { diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 9607561cb..6abd4886c 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -5,10 +5,10 @@ import ( "time" "github.com/ipfs/go-ipfs/blocks" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + context "context" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" bloom "gx/ipfs/QmeiMCBkYHxkDkDfnDadzz4YxY5ruL5Pj499essE4vRsGM/bbloom" ) diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 4ce2d0152..248308874 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -8,7 +8,7 @@ import ( "github.com/ipfs/go-ipfs/blocks" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" diff --git a/blockstore/caching.go b/blockstore/caching.go index d1da0f721..d28401cf8 100644 --- a/blockstore/caching.go +++ b/blockstore/caching.go @@ -3,8 +3,8 @@ package blockstore import ( "errors" + context "context" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) // Next to each option is it aproximate memory usage per unit diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index c7db675a8..4b5f86d14 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -6,9 +6,9 @@ import ( bs "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/pin" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) // RemovedBlock is used to respresent the result of removing a block. From b82e6bb8d2419169ab306e4959888e6da6f6a163 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 5 Oct 2016 15:49:08 -0700 Subject: [PATCH 1525/3817] update to libp2p 4.0.1 and propogate other changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@ca11d8482f775c0cbc41d5947593b86a99d42f92 --- blockservice/blockservice.go | 6 +++--- blockservice/test/blocks_test.go | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 840fa606d..dd274d27e 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -10,11 +10,11 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + context "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) var log = logging.Logger("blockservice") diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index ba67f9f3d..9b86d5a61 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -10,13 +10,13 @@ import ( blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + "context" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" ) func newObject(data []byte) *testObject { From daafe88d4b28670f49073e2e0cb70b31e1d1defd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 5 Oct 2016 15:49:08 -0700 Subject: [PATCH 1526/3817] update to libp2p 4.0.1 and propogate other changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-ds-help@b051872689028ce7d4340537fe8b0b5d8a4e44c3 --- datastore/dshelp/key.go | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 datastore/dshelp/key.go diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go new file mode 100644 index 000000000..db680add2 --- /dev/null +++ b/datastore/dshelp/key.go @@ -0,0 +1,11 @@ +package dshelp + +import ( + base32 "gx/ipfs/Qmb1DA2A9LS2wR4FFweB4uEDomFsdmnw1VLawLE1yQzudj/base32" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" +) + +// TODO: put this code into the go-datastore itself +func NewKeyFromBinary(s string) ds.Key { + return ds.NewKey(base32.RawStdEncoding.EncodeToString([]byte(s))) +} From 0910de3e7482840c3123514bddecffce7f31017b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 5 Oct 2016 15:49:08 -0700 Subject: [PATCH 1527/3817] update to libp2p 4.0.1 and propogate other changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-chunker@e82f0f2937e3c303d6c4ef14e668dfd46c57f00d --- chunker/rabin_test.go | 4 ++-- chunker/splitting_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 99b1bad58..a6e08f268 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -4,8 +4,8 @@ import ( "bytes" "fmt" "github.com/ipfs/go-ipfs/blocks" - "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" "io" "testing" ) diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index 83dcaadba..24c2bdcf9 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -5,7 +5,7 @@ import ( "io" "testing" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) func randBuf(t *testing.T, size int) []byte { From 82d09f0de9519d9ff390beb74552165720c1749d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 5 Oct 2016 15:49:08 -0700 Subject: [PATCH 1528/3817] update to libp2p 4.0.1 and propogate other changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@8cc48f427e79c943c2da781efba3e5ac7b922d5e --- path/path.go | 2 +- path/resolver.go | 6 +++--- path/resolver_test.go | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/path/path.go b/path/path.go index 884c1780d..847685f86 100644 --- a/path/path.go +++ b/path/path.go @@ -5,7 +5,7 @@ import ( "path" "strings" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) // ErrBadPath is returned when a given path is incorrectly formatted diff --git a/path/resolver.go b/path/resolver.go index 8fc59ac9d..8ff4cf077 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -6,12 +6,12 @@ import ( "fmt" "time" - mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + "context" + mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" merkledag "github.com/ipfs/go-ipfs/merkledag" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) var log = logging.Logger("path") diff --git a/path/resolver_test.go b/path/resolver_test.go index 0d26ff48e..77e7a27e1 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -4,13 +4,13 @@ import ( "fmt" "testing" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" merkledag "github.com/ipfs/go-ipfs/merkledag" dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - util "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + util "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) func randNode() (*merkledag.Node, key.Key) { From a62808e97dc36bd3bff0c76cae3f280086d3a156 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 5 Oct 2016 15:49:08 -0700 Subject: [PATCH 1529/3817] update to libp2p 4.0.1 and propogate other changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@ad2fa7c5a40f32dff81c77c6746f6f840ec8e65a --- unixfs/archive/archive.go | 2 +- unixfs/archive/tar/writer.go | 2 +- unixfs/io/dagreader.go | 2 +- unixfs/io/dagreader_test.go | 2 +- unixfs/io/dirbuilder.go | 4 ++-- unixfs/mod/dagmodifier.go | 4 ++-- unixfs/mod/dagmodifier_test.go | 4 ++-- unixfs/test/utils.go | 4 ++-- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index 1ec1760f0..8cc1ec2e1 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -6,7 +6,7 @@ import ( "io" "path" - cxt "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cxt "context" mdag "github.com/ipfs/go-ipfs/merkledag" tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 3d1f47eea..475b318a4 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -6,8 +6,8 @@ import ( "path" "time" + cxt "context" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cxt "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 53916aa57..f78fbbf77 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -7,8 +7,8 @@ import ( "io" "os" + "context" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index ac8d4d52f..5f1380c9e 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -10,7 +10,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/unixfs" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" testu "github.com/ipfs/go-ipfs/unixfs/test" ) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 7a7783a7d..ca424e28b 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -1,11 +1,11 @@ package io import ( - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + "context" mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) type directoryBuilder struct { diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index d45dffdef..7e1fd2dc8 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -13,10 +13,10 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" + context "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) var ErrSeekFail = errors.New("failed to seek properly") diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 56a2f922f..810ec6f23 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -16,8 +16,8 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" testu "github.com/ipfs/go-ipfs/unixfs/test" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index e512eeb9d..b997a11a8 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -13,8 +13,8 @@ import ( mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) func SizeSplitterGen(size int64) chunk.SplitterGen { From 997e39939aa5f34cb5fb18f67176073dcf9a35ce Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 5 Oct 2016 15:49:08 -0700 Subject: [PATCH 1530/3817] update to libp2p 4.0.1 and propogate other changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@cbad7164c47187693940a99871627bfb34b6b307 --- routing/mock/centralized_client.go | 33 +++++++++++----------- routing/mock/centralized_server.go | 23 ++++++++------- routing/mock/centralized_test.go | 22 ++++++++------- routing/mock/dht.go | 6 ++-- routing/mock/interface.go | 15 +++++----- routing/none/none_client.go | 23 +++++++-------- routing/offline/offline.go | 37 ++++++++++++------------ routing/supernode/client.go | 44 ++++++++++++++--------------- routing/supernode/proxy/loopback.go | 8 +++--- routing/supernode/proxy/standard.go | 24 +++++++--------- routing/supernode/server.go | 14 ++++----- routing/supernode/server_test.go | 4 +-- 12 files changed, 129 insertions(+), 124 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 7ac431aca..57a130150 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -1,22 +1,23 @@ package mockrouting import ( + "context" "errors" "time" + dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" "github.com/ipfs/go-ipfs/thirdparty/testutil" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" - ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr" + ma "gx/ipfs/QmUAQaWbKxGCUTuoQVvvicbQNZ9APF5pDGWyAZSe93AtKH/go-multiaddr" + routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" + pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - dhtpb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" - routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" + dhtpb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" + peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) var log = logging.Logger("mockrouter") @@ -28,7 +29,7 @@ type client struct { } // FIXME(brian): is this method meant to simulate putting a value into the network? -func (c *client) PutValue(ctx context.Context, key key.Key, val []byte) error { +func (c *client) PutValue(ctx context.Context, key string, val []byte) error { log.Debugf("PutValue: %s", key) rec := new(dhtpb.Record) rec.Value = val @@ -39,13 +40,13 @@ func (c *client) PutValue(ctx context.Context, key key.Key, val []byte) error { return err } - return c.datastore.Put(key.DsKey(), data) + return c.datastore.Put(dshelp.NewKeyFromBinary(key), data) } // FIXME(brian): is this method meant to simulate getting a value from the network? -func (c *client) GetValue(ctx context.Context, key key.Key) ([]byte, error) { +func (c *client) GetValue(ctx context.Context, key string) ([]byte, error) { log.Debugf("GetValue: %s", key) - v, err := c.datastore.Get(key.DsKey()) + v, err := c.datastore.Get(dshelp.NewKeyFromBinary(key)) if err != nil { return nil, err } @@ -64,7 +65,7 @@ func (c *client) GetValue(ctx context.Context, key key.Key) ([]byte, error) { return rec.GetValue(), nil } -func (c *client) GetValues(ctx context.Context, key key.Key, count int) ([]routing.RecvdVal, error) { +func (c *client) GetValues(ctx context.Context, key string, count int) ([]routing.RecvdVal, error) { log.Debugf("GetValues: %s", key) data, err := c.GetValue(ctx, key) if err != nil { @@ -74,7 +75,7 @@ func (c *client) GetValues(ctx context.Context, key key.Key, count int) ([]routi return []routing.RecvdVal{{Val: data, From: c.peer.ID()}}, nil } -func (c *client) FindProviders(ctx context.Context, key key.Key) ([]pstore.PeerInfo, error) { +func (c *client) FindProviders(ctx context.Context, key *cid.Cid) ([]pstore.PeerInfo, error) { return c.server.Providers(key), nil } @@ -83,7 +84,7 @@ func (c *client) FindPeer(ctx context.Context, pid peer.ID) (pstore.PeerInfo, er return pstore.PeerInfo{}, nil } -func (c *client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan pstore.PeerInfo { +func (c *client) FindProvidersAsync(ctx context.Context, k *cid.Cid, max int) <-chan pstore.PeerInfo { out := make(chan pstore.PeerInfo) go func() { defer close(out) @@ -103,7 +104,7 @@ func (c *client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-c // Provide returns once the message is on the network. Value is not necessarily // visible yet. -func (c *client) Provide(_ context.Context, key key.Key) error { +func (c *client) Provide(_ context.Context, key *cid.Cid) error { info := pstore.PeerInfo{ ID: c.peer.ID(), Addrs: []ma.Multiaddr{c.peer.Address()}, diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index a112b0d53..49c681eda 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -1,24 +1,24 @@ package mockrouting import ( + "context" "math/rand" "sync" "time" "github.com/ipfs/go-ipfs/thirdparty/testutil" + + pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) // server is the mockrouting.Client's private interface to the routing server type server interface { - Announce(pstore.PeerInfo, key.Key) error - Providers(key.Key) []pstore.PeerInfo + Announce(pstore.PeerInfo, *cid.Cid) error + Providers(*cid.Cid) []pstore.PeerInfo Server } @@ -28,7 +28,7 @@ type s struct { delayConf DelayConfig lock sync.RWMutex - providers map[key.Key]map[peer.ID]providerRecord + providers map[string]map[peer.ID]providerRecord } type providerRecord struct { @@ -36,10 +36,12 @@ type providerRecord struct { Created time.Time } -func (rs *s) Announce(p pstore.PeerInfo, k key.Key) error { +func (rs *s) Announce(p pstore.PeerInfo, c *cid.Cid) error { rs.lock.Lock() defer rs.lock.Unlock() + k := c.KeyString() + _, ok := rs.providers[k] if !ok { rs.providers[k] = make(map[peer.ID]providerRecord) @@ -51,11 +53,12 @@ func (rs *s) Announce(p pstore.PeerInfo, k key.Key) error { return nil } -func (rs *s) Providers(k key.Key) []pstore.PeerInfo { +func (rs *s) Providers(c *cid.Cid) []pstore.PeerInfo { rs.delayConf.Query.Wait() // before locking rs.lock.RLock() defer rs.lock.RUnlock() + k := c.KeyString() var ret []pstore.PeerInfo records, ok := rs.providers[k] diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index d68fc5e5c..a29ec12ff 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -1,21 +1,22 @@ package mockrouting import ( + "context" "testing" "time" delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) func TestKeyNotFound(t *testing.T) { var pi = testutil.RandIdentityOrFatal(t) - var key = key.Key("mock key") + var key = cid.NewCidV0(u.Hash([]byte("mock key"))) var ctx = context.Background() rs := NewServer() @@ -31,7 +32,7 @@ func TestClientFindProviders(t *testing.T) { rs := NewServer() client := rs.Client(pi) - k := key.Key("hello") + k := cid.NewCidV0(u.Hash([]byte("hello"))) err := client.Provide(context.Background(), k) if err != nil { t.Fatal(err) @@ -41,7 +42,7 @@ func TestClientFindProviders(t *testing.T) { time.Sleep(time.Millisecond * 300) max := 100 - providersFromClient := client.FindProvidersAsync(context.Background(), key.Key("hello"), max) + providersFromClient := client.FindProvidersAsync(context.Background(), k, max) isInClient := false for pi := range providersFromClient { if pi.ID == pi.ID { @@ -55,7 +56,7 @@ func TestClientFindProviders(t *testing.T) { func TestClientOverMax(t *testing.T) { rs := NewServer() - k := key.Key("hello") + k := cid.NewCidV0(u.Hash([]byte("hello"))) numProvidersForHelloKey := 100 for i := 0; i < numProvidersForHelloKey; i++ { pi := testutil.RandIdentityOrFatal(t) @@ -82,7 +83,7 @@ func TestClientOverMax(t *testing.T) { // TODO does dht ensure won't receive self as a provider? probably not. func TestCanceledContext(t *testing.T) { rs := NewServer() - k := key.Key("hello") + k := cid.NewCidV0(u.Hash([]byte("hello"))) // avoid leaking goroutine, without using the context to signal // (we want the goroutine to keep trying to publish on a @@ -138,10 +139,11 @@ func TestCanceledContext(t *testing.T) { } func TestValidAfter(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() pi := testutil.RandIdentityOrFatal(t) - var key = key.Key("mock key") - var ctx = context.Background() + key := cid.NewCidV0(u.Hash([]byte("mock key"))) conf := DelayConfig{ ValueVisibility: delay.Fixed(1 * time.Hour), Query: delay.Fixed(0), diff --git a/routing/mock/dht.go b/routing/mock/dht.go index e987a5e49..3f090abe2 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -1,12 +1,12 @@ package mockrouting import ( + context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - dht "gx/ipfs/QmXVWh4XWRaRGdcGeFtBp3hx7H3mzYgVRpVN7LwtTaYv2E/go-libp2p-kad-dht" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - mocknet "gx/ipfs/QmbiRCGZqhfcSjnm9icGz3oNQQdPLAnLWnKHXixaEWXVCN/go-libp2p/p2p/net/mock" + dht "gx/ipfs/QmRDMP3Y9E6hZtJwcFii8F6RTUSDn67Hi2o5VFTBXNRioo/go-libp2p-kad-dht" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + mocknet "gx/ipfs/QmcRa2qn6iCmap9bjp8jAwkvYAq13AUfxdY3rrYiaJbLum/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 6cde15ab3..e5d0e60f4 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -5,15 +5,16 @@ package mockrouting import ( + "context" + delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" + pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" + peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) // Server provides mockrouting Clients @@ -24,7 +25,7 @@ type Server interface { // Client implements IpfsRouting type Client interface { - FindProviders(context.Context, key.Key) ([]pstore.PeerInfo, error) + FindProviders(context.Context, *cid.Cid) ([]pstore.PeerInfo, error) routing.IpfsRouting } @@ -39,7 +40,7 @@ func NewServer() Server { // NewServerWithDelay returns a mockrouting Server with a delay! func NewServerWithDelay(conf DelayConfig) Server { return &s{ - providers: make(map[key.Key]map[peer.ID]providerRecord), + providers: make(map[string]map[peer.ID]providerRecord), delayConf: conf, } } diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 82e2bd02e..8fdebcc66 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -1,16 +1,17 @@ package nilrouting import ( + "context" "errors" repo "github.com/ipfs/go-ipfs/repo" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - p2phost "gx/ipfs/QmbiRCGZqhfcSjnm9icGz3oNQQdPLAnLWnKHXixaEWXVCN/go-libp2p/p2p/host" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" + routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" + pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + p2phost "gx/ipfs/QmdML3R42PRSwnt46jSuEts9bHSqLctVYEjJqMR3UYV8ki/go-libp2p-host" + peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) var log = logging.Logger("mockrouter") @@ -18,15 +19,15 @@ var log = logging.Logger("mockrouter") type nilclient struct { } -func (c *nilclient) PutValue(_ context.Context, _ key.Key, _ []byte) error { +func (c *nilclient) PutValue(_ context.Context, _ string, _ []byte) error { return nil } -func (c *nilclient) GetValue(_ context.Context, _ key.Key) ([]byte, error) { +func (c *nilclient) GetValue(_ context.Context, _ string) ([]byte, error) { return nil, errors.New("Tried GetValue from nil routing.") } -func (c *nilclient) GetValues(_ context.Context, _ key.Key, _ int) ([]routing.RecvdVal, error) { +func (c *nilclient) GetValues(_ context.Context, _ string, _ int) ([]routing.RecvdVal, error) { return nil, errors.New("Tried GetValues from nil routing.") } @@ -34,13 +35,13 @@ func (c *nilclient) FindPeer(_ context.Context, _ peer.ID) (pstore.PeerInfo, err return pstore.PeerInfo{}, nil } -func (c *nilclient) FindProvidersAsync(_ context.Context, _ key.Key, _ int) <-chan pstore.PeerInfo { +func (c *nilclient) FindProvidersAsync(_ context.Context, _ *cid.Cid, _ int) <-chan pstore.PeerInfo { out := make(chan pstore.PeerInfo) defer close(out) return out } -func (c *nilclient) Provide(_ context.Context, _ key.Key) error { +func (c *nilclient) Provide(_ context.Context, _ *cid.Cid) error { return nil } diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 982e46ea7..398a4d1b9 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -1,21 +1,22 @@ package offline import ( + "context" "errors" "time" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - record "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record" - pb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" - routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" + dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" + routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" + pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" + pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" + "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" + ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) var log = logging.Logger("offlinerouting") @@ -37,7 +38,7 @@ type offlineRouting struct { sk ci.PrivKey } -func (c *offlineRouting) PutValue(ctx context.Context, key key.Key, val []byte) error { +func (c *offlineRouting) PutValue(ctx context.Context, key string, val []byte) error { rec, err := record.MakePutRecord(c.sk, key, val, false) if err != nil { return err @@ -47,11 +48,11 @@ func (c *offlineRouting) PutValue(ctx context.Context, key key.Key, val []byte) return err } - return c.datastore.Put(key.DsKey(), data) + return c.datastore.Put(dshelp.NewKeyFromBinary(key), data) } -func (c *offlineRouting) GetValue(ctx context.Context, key key.Key) ([]byte, error) { - v, err := c.datastore.Get(key.DsKey()) +func (c *offlineRouting) GetValue(ctx context.Context, key string) ([]byte, error) { + v, err := c.datastore.Get(dshelp.NewKeyFromBinary(key)) if err != nil { return nil, err } @@ -69,8 +70,8 @@ func (c *offlineRouting) GetValue(ctx context.Context, key key.Key) ([]byte, err return rec.GetValue(), nil } -func (c *offlineRouting) GetValues(ctx context.Context, key key.Key, _ int) ([]routing.RecvdVal, error) { - v, err := c.datastore.Get(key.DsKey()) +func (c *offlineRouting) GetValues(ctx context.Context, key string, _ int) ([]routing.RecvdVal, error) { + v, err := c.datastore.Get(dshelp.NewKeyFromBinary(key)) if err != nil { return nil, err } @@ -90,7 +91,7 @@ func (c *offlineRouting) GetValues(ctx context.Context, key key.Key, _ int) ([]r }, nil } -func (c *offlineRouting) FindProviders(ctx context.Context, key key.Key) ([]pstore.PeerInfo, error) { +func (c *offlineRouting) FindProviders(ctx context.Context, key *cid.Cid) ([]pstore.PeerInfo, error) { return nil, ErrOffline } @@ -98,13 +99,13 @@ func (c *offlineRouting) FindPeer(ctx context.Context, pid peer.ID) (pstore.Peer return pstore.PeerInfo{}, ErrOffline } -func (c *offlineRouting) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan pstore.PeerInfo { +func (c *offlineRouting) FindProvidersAsync(ctx context.Context, k *cid.Cid, max int) <-chan pstore.PeerInfo { out := make(chan pstore.PeerInfo) close(out) return out } -func (c *offlineRouting) Provide(_ context.Context, key key.Key) error { +func (c *offlineRouting) Provide(_ context.Context, k *cid.Cid) error { return ErrOffline } diff --git a/routing/supernode/client.go b/routing/supernode/client.go index b76b30080..79b058d0a 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -2,22 +2,22 @@ package supernode import ( "bytes" + "context" "errors" "time" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" + dhtpb "gx/ipfs/QmRDMP3Y9E6hZtJwcFii8F6RTUSDn67Hi2o5VFTBXNRioo/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - dhtpb "gx/ipfs/QmXVWh4XWRaRGdcGeFtBp3hx7H3mzYgVRpVN7LwtTaYv2E/go-libp2p-kad-dht/pb" - pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" - loggables "gx/ipfs/QmYrv4LgCC8FhG2Ab4bwuq5DqBdwMtx3hMb3KKJDZcr2d7/go-libp2p-loggables" + loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" + routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" + pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - "gx/ipfs/QmbiRCGZqhfcSjnm9icGz3oNQQdPLAnLWnKHXixaEWXVCN/go-libp2p/p2p/host" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - pb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" - routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" + "gx/ipfs/QmdML3R42PRSwnt46jSuEts9bHSqLctVYEjJqMR3UYV8ki/go-libp2p-host" + peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) var log = logging.Logger("supernode") @@ -39,13 +39,13 @@ func NewClient(px proxy.Proxy, h host.Host, ps pstore.Peerstore, local peer.ID) }, nil } -func (c *Client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-chan pstore.PeerInfo { +func (c *Client) FindProvidersAsync(ctx context.Context, k *cid.Cid, max int) <-chan pstore.PeerInfo { logging.ContextWithLoggable(ctx, loggables.Uuid("findProviders")) - defer log.EventBegin(ctx, "findProviders", &k).Done() + defer log.EventBegin(ctx, "findProviders", k).Done() ch := make(chan pstore.PeerInfo) go func() { defer close(ch) - request := dhtpb.NewMessage(dhtpb.Message_GET_PROVIDERS, string(k), 0) + request := dhtpb.NewMessage(dhtpb.Message_GET_PROVIDERS, k.KeyString(), 0) response, err := c.proxy.SendRequest(ctx, request) if err != nil { log.Debug(err) @@ -63,8 +63,8 @@ func (c *Client) FindProvidersAsync(ctx context.Context, k key.Key, max int) <-c return ch } -func (c *Client) PutValue(ctx context.Context, k key.Key, v []byte) error { - defer log.EventBegin(ctx, "putValue", &k).Done() +func (c *Client) PutValue(ctx context.Context, k string, v []byte) error { + defer log.EventBegin(ctx, "putValue").Done() r, err := makeRecord(c.peerstore, c.local, k, v) if err != nil { return err @@ -74,8 +74,8 @@ func (c *Client) PutValue(ctx context.Context, k key.Key, v []byte) error { return c.proxy.SendMessage(ctx, pmes) // wrap to hide the remote } -func (c *Client) GetValue(ctx context.Context, k key.Key) ([]byte, error) { - defer log.EventBegin(ctx, "getValue", &k).Done() +func (c *Client) GetValue(ctx context.Context, k string) ([]byte, error) { + defer log.EventBegin(ctx, "getValue").Done() msg := dhtpb.NewMessage(dhtpb.Message_GET_VALUE, string(k), 0) response, err := c.proxy.SendRequest(ctx, msg) // TODO wrap to hide the remote if err != nil { @@ -84,8 +84,8 @@ func (c *Client) GetValue(ctx context.Context, k key.Key) ([]byte, error) { return response.Record.GetValue(), nil } -func (c *Client) GetValues(ctx context.Context, k key.Key, _ int) ([]routing.RecvdVal, error) { - defer log.EventBegin(ctx, "getValue", &k).Done() +func (c *Client) GetValues(ctx context.Context, k string, _ int) ([]routing.RecvdVal, error) { + defer log.EventBegin(ctx, "getValue").Done() msg := dhtpb.NewMessage(dhtpb.Message_GET_VALUE, string(k), 0) response, err := c.proxy.SendRequest(ctx, msg) // TODO wrap to hide the remote if err != nil { @@ -100,9 +100,9 @@ func (c *Client) GetValues(ctx context.Context, k key.Key, _ int) ([]routing.Rec }, nil } -func (c *Client) Provide(ctx context.Context, k key.Key) error { - defer log.EventBegin(ctx, "provide", &k).Done() - msg := dhtpb.NewMessage(dhtpb.Message_ADD_PROVIDER, string(k), 0) +func (c *Client) Provide(ctx context.Context, k *cid.Cid) error { + defer log.EventBegin(ctx, "provide", k).Done() + msg := dhtpb.NewMessage(dhtpb.Message_ADD_PROVIDER, k.KeyString(), 0) // FIXME how is connectedness defined for the local node pri := []dhtpb.PeerRoutingInfo{ { @@ -132,7 +132,7 @@ func (c *Client) FindPeer(ctx context.Context, id peer.ID) (pstore.PeerInfo, err } // creates and signs a record for the given key/value pair -func makeRecord(ps pstore.Peerstore, p peer.ID, k key.Key, v []byte) (*pb.Record, error) { +func makeRecord(ps pstore.Peerstore, p peer.ID, k string, v []byte) (*pb.Record, error) { blob := bytes.Join([][]byte{[]byte(k), v, []byte(p)}, []byte{}) sig, err := ps.PrivKey(p).Sign(blob) if err != nil { diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 8afe91a75..8a6e5230f 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -1,11 +1,11 @@ package proxy import ( - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - dhtpb "gx/ipfs/QmXVWh4XWRaRGdcGeFtBp3hx7H3mzYgVRpVN7LwtTaYv2E/go-libp2p-kad-dht/pb" + context "context" + dhtpb "gx/ipfs/QmRDMP3Y9E6hZtJwcFii8F6RTUSDn67Hi2o5VFTBXNRioo/go-libp2p-kad-dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - inet "gx/ipfs/QmbiRCGZqhfcSjnm9icGz3oNQQdPLAnLWnKHXixaEWXVCN/go-libp2p/p2p/net" + inet "gx/ipfs/QmdXimY9QHaasZmw6hWojWnCJvfgxETjZQfg9g6ZrA9wMX/go-libp2p-net" + peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 6d7ef439f..f7d2b80ed 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -1,22 +1,18 @@ package proxy import ( + "context" "errors" - ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" - + dhtpb "gx/ipfs/QmRDMP3Y9E6hZtJwcFii8F6RTUSDn67Hi2o5VFTBXNRioo/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" - host "gx/ipfs/QmbiRCGZqhfcSjnm9icGz3oNQQdPLAnLWnKHXixaEWXVCN/go-libp2p/p2p/host" - inet "gx/ipfs/QmbiRCGZqhfcSjnm9icGz3oNQQdPLAnLWnKHXixaEWXVCN/go-libp2p/p2p/net" - - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - - kbucket "gx/ipfs/QmVsCNFD32GzZ6Q5XD1TVGPRviNYqDdoNvgq853TU9hhzP/go-libp2p-kbucket" - dhtpb "gx/ipfs/QmXVWh4XWRaRGdcGeFtBp3hx7H3mzYgVRpVN7LwtTaYv2E/go-libp2p-kad-dht/pb" - loggables "gx/ipfs/QmYrv4LgCC8FhG2Ab4bwuq5DqBdwMtx3hMb3KKJDZcr2d7/go-libp2p-loggables" + loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" + kbucket "gx/ipfs/QmUKePKcUEXwdvJENZJ6z8mJjPaxLsDZ3V9CZjPPtyawPm/go-libp2p-kbucket" + pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" + host "gx/ipfs/QmdML3R42PRSwnt46jSuEts9bHSqLctVYEjJqMR3UYV8ki/go-libp2p-host" + inet "gx/ipfs/QmdXimY9QHaasZmw6hWojWnCJvfgxETjZQfg9g6ZrA9wMX/go-libp2p-net" + peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) const ProtocolSNR = "/ipfs/supernoderouting" @@ -167,6 +163,6 @@ func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote pe } func sortedByKey(peers []peer.ID, skey string) []peer.ID { - target := kbucket.ConvertKey(key.Key(skey)) + target := kbucket.ConvertKey(skey) return kbucket.SortClosestPeers(peers, target) } diff --git a/routing/supernode/server.go b/routing/supernode/server.go index d6c8a4805..3eabaa415 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -6,15 +6,15 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - dhtpb "gx/ipfs/QmXVWh4XWRaRGdcGeFtBp3hx7H3mzYgVRpVN7LwtTaYv2E/go-libp2p-kad-dht/pb" - pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" + context "context" + dhtpb "gx/ipfs/QmRDMP3Y9E6hZtJwcFii8F6RTUSDn67Hi2o5VFTBXNRioo/go-libp2p-kad-dht/pb" + pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - record "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record" - pb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" + record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" + pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" + peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) // Server handles routing queries using a database backend diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 5bdec5ded..36f8b53b3 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,9 +3,9 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmXVWh4XWRaRGdcGeFtBp3hx7H3mzYgVRpVN7LwtTaYv2E/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmRDMP3Y9E6hZtJwcFii8F6RTUSDn67Hi2o5VFTBXNRioo/go-libp2p-kad-dht/pb" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 70cacafa77d40e6b4b97fc0fee7bcfe808307c79 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 5 Oct 2016 15:49:08 -0700 Subject: [PATCH 1531/3817] update to libp2p 4.0.1 and propogate other changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@ead62da25c72c4056cbdb77f66b1a6fa48074f7f --- namesys/base.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 4 ++-- namesys/ipns_select_test.go | 4 ++-- namesys/namesys.go | 6 +++--- namesys/namesys_test.go | 2 +- namesys/proquint.go | 2 +- namesys/publisher.go | 34 +++++++++++++++---------------- namesys/republisher/repub.go | 20 +++++++++--------- namesys/republisher/repub_test.go | 8 ++++---- namesys/resolve_test.go | 8 ++++---- namesys/routing.go | 16 +++++++-------- 12 files changed, 54 insertions(+), 54 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 569cb4bb3..c79fbeb94 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -3,7 +3,7 @@ package namesys import ( "strings" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" path "github.com/ipfs/go-ipfs/path" ) diff --git a/namesys/dns.go b/namesys/dns.go index 79fb00c2f..93a5501da 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -5,7 +5,7 @@ import ( "net" "strings" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" isd "gx/ipfs/QmaeHSCBd9XjXxmgHEiKkHtLcMCb2eZsPLKT7bHgBfBkqw/go-is-domain" path "github.com/ipfs/go-ipfs/path" diff --git a/namesys/interface.go b/namesys/interface.go index ae734af4f..3f66498ac 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -33,9 +33,9 @@ import ( "errors" "time" + context "context" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) const ( diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index ca04674c5..883c00c2b 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -10,8 +10,8 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" + ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) func shuffle(a []*pb.IpnsEntry) { diff --git a/namesys/namesys.go b/namesys/namesys.go index a5f4e8a90..7f8d298c2 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -4,11 +4,11 @@ import ( "strings" "time" + context "context" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" + ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index f44f17ef5..b2f92deb0 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" path "github.com/ipfs/go-ipfs/path" ) diff --git a/namesys/proquint.go b/namesys/proquint.go index f90a4c8a1..ee6ada978 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -3,9 +3,9 @@ package namesys import ( "errors" + context "context" path "github.com/ipfs/go-ipfs/path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index d70cc289c..b48e742b8 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -2,6 +2,7 @@ package namesys import ( "bytes" + "context" "errors" "fmt" "time" @@ -10,18 +11,17 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" pin "github.com/ipfs/go-ipfs/pin" + dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ft "github.com/ipfs/go-ipfs/unixfs" - ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" + routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - record "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record" - dhtpb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" - routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" + record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" + dhtpb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" + peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" + ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) // ErrExpiredRecord should be returned when an ipns record is @@ -79,8 +79,8 @@ func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value return PutRecordToRouting(ctx, k, value, seqnum, eol, p.routing, id) } -func (p *ipnsPublisher) getPreviousSeqNo(ctx context.Context, ipnskey key.Key) (uint64, error) { - prevrec, err := p.ds.Get(ipnskey.DsKey()) +func (p *ipnsPublisher) getPreviousSeqNo(ctx context.Context, ipnskey string) (uint64, error) { + prevrec, err := p.ds.Get(dshelp.NewKeyFromBinary(ipnskey)) if err != nil && err != ds.ErrNotFound { // None found, lets start at zero! return 0, err @@ -181,7 +181,7 @@ func waitOnErrChan(ctx context.Context, errs chan error) error { } } -func PublishPublicKey(ctx context.Context, r routing.ValueStore, k key.Key, pubk ci.PubKey) error { +func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk ci.PubKey) error { log.Debugf("Storing pubkey at: %s", k) pkbytes, err := pubk.Bytes() if err != nil { @@ -199,7 +199,7 @@ func PublishPublicKey(ctx context.Context, r routing.ValueStore, k key.Key, pubk return nil } -func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey key.Key, rec *pb.IpnsEntry) error { +func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec *pb.IpnsEntry) error { timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout) defer cancel() @@ -248,7 +248,7 @@ var IpnsRecordValidator = &record.ValidChecker{ Sign: true, } -func IpnsSelectorFunc(k key.Key, vals [][]byte) (int, error) { +func IpnsSelectorFunc(k string, vals [][]byte) (int, error) { var recs []*pb.IpnsEntry for _, v := range vals { e := new(pb.IpnsEntry) @@ -304,7 +304,7 @@ func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) { // ValidateIpnsRecord implements ValidatorFunc and verifies that the // given 'val' is an IpnsEntry and that that entry is valid. -func ValidateIpnsRecord(k key.Key, val []byte) error { +func ValidateIpnsRecord(k string, val []byte) error { entry := new(pb.IpnsEntry) err := proto.Unmarshal(val, entry) if err != nil { @@ -356,9 +356,9 @@ func InitializeKeyspace(ctx context.Context, ds dag.DAGService, pub Publisher, p return nil } -func IpnsKeysForID(id peer.ID) (name, ipns key.Key) { - namekey := key.Key("/pk/" + id) - ipnskey := key.Key("/ipns/" + id) +func IpnsKeysForID(id peer.ID) (name, ipns string) { + namekey := "/pk/" + string(id) + ipnskey := "/ipns/" + string(id) return namekey, ipnskey } diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index d208e093c..41c0a04ee 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -1,6 +1,7 @@ package republisher import ( + "context" "errors" "sync" "time" @@ -8,18 +9,17 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" + dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" - gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context" + goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" + gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" + routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" + pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - recpb "gx/ipfs/Qme7D9iKHYxwq28p6PzCymywsYSRBx9uyGzW7qNB3s9VbC/go-libp2p-record/pb" - routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" + recpb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" + peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) var errNoEntry = errors.New("no previous entry") @@ -107,8 +107,8 @@ func (rp *Republisher) republishEntries(p goprocess.Process) error { return nil } -func (rp *Republisher) getLastVal(k key.Key) (path.Path, uint64, error) { - ival, err := rp.ds.Get(k.DsKey()) +func (rp *Republisher) getLastVal(k string) (path.Path, uint64, error) { + ival, err := rp.ds.Get(dshelp.NewKeyFromBinary(k)) if err != nil { // not found means we dont have a previously published entry return "", 0, errNoEntry diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 0bb9f1031..5b6e30794 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -5,16 +5,16 @@ import ( "testing" "time" - goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + context "context" + goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" "github.com/ipfs/go-ipfs/core" mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - pstore "gx/ipfs/QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD/go-libp2p-peerstore" - mocknet "gx/ipfs/QmbiRCGZqhfcSjnm9icGz3oNQQdPLAnLWnKHXixaEWXVCN/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + mocknet "gx/ipfs/QmcRa2qn6iCmap9bjp8jAwkvYAq13AUfxdY3rrYiaJbLum/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index d7fbdf6ca..145396d11 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" + context "context" path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" - peer "gx/ipfs/QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p/go-libp2p-peer" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" + peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index bf481be30..3287d4940 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,20 +5,20 @@ import ( "strings" "time" + "context" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" + mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key" - routing "gx/ipfs/QmemZcG8WprPbnVX3AM43GhhSUiA3V6NjcTLAguvWzkdpQ/go-libp2p-routing" + routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - ci "gx/ipfs/QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ/go-libp2p-crypto" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" + ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) var log = logging.Logger("namesys") @@ -142,7 +142,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa resp := make(chan error, 2) go func() { - ipnsKey := key.Key(h) + ipnsKey := string(h) val, err := r.routing.GetValue(ctx, ipnsKey) if err != nil { log.Warning("RoutingResolve get failed.") From 95047cfd1e9a2c067e0675eb442280ae37248500 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 29 Sep 2016 12:35:40 -0700 Subject: [PATCH 1532/3817] fix bug in pinsets and add a stress test for the scenario License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@8298dcca1eb66606c26cbae61b677ea2889a85cd --- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 67 ++++++++++++++++++++++++++++++++++---- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index ec08971e9..d93ccd114 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -143,7 +143,7 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint if !ok { break } - h := hash(seed, k) + h := hash(seed, k) % defaultFanout hashed[h] = append(hashed[h], item{k, data}) } for h, items := range hashed { diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index f48fc9d17..8c60633b4 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -1,13 +1,66 @@ package pin -import "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" +import ( + "context" + "fmt" + "os" + "testing" -func ignoreKeys(key.Key) {} + dag "github.com/ipfs/go-ipfs/merkledag" + mdtest "github.com/ipfs/go-ipfs/merkledag/test" -func copyMap(m map[key.Key]uint16) map[key.Key]uint64 { - c := make(map[key.Key]uint64, len(m)) - for k, v := range m { - c[k] = uint64(v) + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" +) + +func ignoreCids(_ *cid.Cid) {} + +func TestSet(t *testing.T) { + ds := mdtest.Mock() + limit := 10000 // 10000 reproduces the pinloss issue fairly reliably + + if os.Getenv("STRESS_IT_OUT_YO") != "" { + limit = 10000000 + } + var inputs []*cid.Cid + for i := 0; i < limit; i++ { + c, err := ds.Add(dag.NodeWithData([]byte(fmt.Sprint(i)))) + if err != nil { + t.Fatal(err) + } + + inputs = append(inputs, c) + } + + out, err := storeSet(context.Background(), ds, inputs, ignoreCids) + if err != nil { + t.Fatal(err) + } + + // weird wrapper node because loadSet expects us to pass an + // object pointing to multiple named sets + setroot := &dag.Node{} + err = setroot.AddNodeLinkClean("foo", out) + if err != nil { + t.Fatal(err) + } + + outset, err := loadSet(context.Background(), ds, setroot, "foo", ignoreCids) + if err != nil { + t.Fatal(err) + } + + if len(outset) != limit { + t.Fatal("got wrong number", len(outset), limit) + } + + seen := cid.NewSet() + for _, c := range outset { + seen.Add(c) + } + + for _, c := range inputs { + if !seen.Has(c) { + t.Fatalf("expected to have %s, didnt find it") + } } - return c } From 8b2e48f38c019dac93f51813bff6fbf6d41c07aa Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 29 Sep 2016 13:19:07 -0700 Subject: [PATCH 1533/3817] add comment detailing the algorithm and fix License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@a000caf094d6a7157932af1ddeb5bd5f3355f139 --- pinning/pinner/set.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index d93ccd114..e2ac75790 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -139,6 +139,19 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint } hashed := make(map[uint32][]item) for { + // This loop essentially enumerates every single item in the set + // and maps them all into a set of buckets. Each bucket will be recursively + // turned into its own sub-set, and so on down the chain. Each sub-set + // gets added to the dagservice, and put into its place in a set nodes + // links array. + // + // Previously, the bucket was selected by taking an int32 from the hash of + // the input key + seed. This was erroneous as we would later be assigning + // the created sub-sets into an array of length 256 by the modulus of the + // int32 hash value with 256. This resulted in overwriting existing sub-sets + // and losing pins. The fix (a few lines down from this comment), is to + // map the hash value down to the 8 bit keyspace here while creating the + // buckets. This way, we avoid any overlapping later on. k, data, ok := iter() if !ok { break From a83703e854f7152bc9d92acce662fba9d49e4608 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 29 Sep 2016 13:41:17 -0700 Subject: [PATCH 1534/3817] pinset: clean up storeItems logic a bit Switched from using a map to an array since the bounds are small and fixed. This should save us some significant time and on accesses License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@53781e6150c5b322e2dee56dd85a065a5242a7ff --- pinning/pinner/set.go | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index e2ac75790..bf0b41c74 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -132,12 +132,7 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint sort.Stable(s) } - // wasteful but simple - type item struct { - c *cid.Cid - data []byte - } - hashed := make(map[uint32][]item) + hashed := make([][]*cid.Cid, defaultFanout) for { // This loop essentially enumerates every single item in the set // and maps them all into a set of buckets. Each bucket will be recursively @@ -152,41 +147,49 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint // and losing pins. The fix (a few lines down from this comment), is to // map the hash value down to the 8 bit keyspace here while creating the // buckets. This way, we avoid any overlapping later on. - k, data, ok := iter() + k, _, ok := iter() if !ok { break } h := hash(seed, k) % defaultFanout - hashed[h] = append(hashed[h], item{k, data}) + hashed[h] = append(hashed[h], k) } + for h, items := range hashed { + if len(items) == 0 { + // recursion base case + continue + } + childIter := func() (c *cid.Cid, data []byte, ok bool) { if len(items) == 0 { return nil, nil, false } first := items[0] items = items[1:] - return first.c, first.data, true + return first, nil, true } + child, err := storeItems(ctx, dag, uint64(len(items)), childIter, internalKeys) if err != nil { return nil, err } + size, err := child.Size() if err != nil { return nil, err } + childKey, err := dag.Add(child) if err != nil { return nil, err } + internalKeys(childKey) - l := &merkledag.Link{ - Name: "", + n.Links[int(h)] = &merkledag.Link{ Hash: childKey.Hash(), Size: size, } - n.Links[int(h%defaultFanout)] = l } return n, nil } From 0ddfbd7b699feeaf7daa7a73221e7fb8caf1bde4 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 19 Aug 2016 15:52:27 -0400 Subject: [PATCH 1535/3817] Add DAGService.GetLinks() method and use it in the GC and elsewhere. This method will use the (also new) LinkService if it is available to retrieving just the links for a MerkleDAG without necessary having to retrieve the underlying block. For now the main benefit is that the pinner will not break when a block becomes invalid due to a change in the backing file. This is possible because the metadata for a block (that includes the Links) is stored separately and thus always available even if the backing file changes. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@55ed12edcfc1d616b905544ac6f23043c831cb7c --- ipld/merkledag/merkledag.go | 39 ++++++++++++++++++++++++++------ ipld/merkledag/merkledag_test.go | 4 ++-- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index bd415f49f..f32104d86 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -23,6 +23,10 @@ type DAGService interface { Get(context.Context, *cid.Cid) (*Node, error) Remove(*Node) error + // Return all links for a node, may be more effect than + // calling Get + GetLinks(context.Context, *cid.Cid) ([]*Link, error) + // GetDAG returns, in order, all the single leve child // nodes of the passed in node. GetMany(context.Context, []*cid.Cid) <-chan *NodeOption @@ -30,8 +34,14 @@ type DAGService interface { Batch() *Batch } -func NewDAGService(bs *bserv.BlockService) DAGService { - return &dagService{bs} +// A LinkService returns the links for a node if they are available +// locally without having to retrieve the block from the datastore. +type LinkService interface { + Get(*cid.Cid) ([]*Link, error) +} + +func NewDAGService(bs *bserv.BlockService) *dagService { + return &dagService{Blocks: bs} } // dagService is an IPFS Merkle DAG service. @@ -40,7 +50,8 @@ func NewDAGService(bs *bserv.BlockService) DAGService { // TODO: should cache Nodes that are in memory, and be // able to free some of them when vm pressure is high type dagService struct { - Blocks *bserv.BlockService + Blocks *bserv.BlockService + LinkService LinkService } // Add adds a node to the dagService, storing the block in the BlockService @@ -93,6 +104,20 @@ func (n *dagService) Get(ctx context.Context, c *cid.Cid) (*Node, error) { return res, nil } +func (n *dagService) GetLinks(ctx context.Context, c *cid.Cid) ([]*Link, error) { + if n.LinkService != nil { + links, err := n.LinkService.Get(c) + if err == nil { + return links, nil + } + } + node, err := n.Get(ctx, c) + if err != nil { + return nil, err + } + return node.Links, nil +} + func (n *dagService) Remove(nd *Node) error { return n.Blocks.DeleteObject(nd) } @@ -366,11 +391,11 @@ func legacyCidFromLink(lnk *Link) *cid.Cid { // EnumerateChildren will walk the dag below the given root node and add all // unseen children to the passed in set. // TODO: parallelize to avoid disk latency perf hits? -func EnumerateChildren(ctx context.Context, ds DAGService, root *Node, visit func(*cid.Cid) bool, bestEffort bool) error { - for _, lnk := range root.Links { +func EnumerateChildren(ctx context.Context, ds DAGService, links []*Link, visit func(*cid.Cid) bool, bestEffort bool) error { + for _, lnk := range links { c := legacyCidFromLink(lnk) if visit(c) { - child, err := ds.Get(ctx, c) + children, err := ds.GetLinks(ctx, c) if err != nil { if bestEffort && err == ErrNotFound { continue @@ -378,7 +403,7 @@ func EnumerateChildren(ctx context.Context, ds DAGService, root *Node, visit fun return err } } - err = EnumerateChildren(ctx, ds, child, visit, bestEffort) + err = EnumerateChildren(ctx, ds, children, visit, bestEffort) if err != nil { return err } diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 339c05609..f58bc56bd 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -241,7 +241,7 @@ func TestFetchGraph(t *testing.T) { offline_ds := NewDAGService(bs) - err = EnumerateChildren(context.Background(), offline_ds, root, func(_ *cid.Cid) bool { return true }, false) + err = EnumerateChildren(context.Background(), offline_ds, root.Links, func(_ *cid.Cid) bool { return true }, false) if err != nil { t.Fatal(err) } @@ -258,7 +258,7 @@ func TestEnumerateChildren(t *testing.T) { } set := cid.NewSet() - err = EnumerateChildren(context.Background(), ds, root, set.Visit, false) + err = EnumerateChildren(context.Background(), ds, root.Links, set.Visit, false) if err != nil { t.Fatal(err) } From 5e140a8efb53022ef7a77d39307b01b38b5f3a68 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 19 Aug 2016 15:52:27 -0400 Subject: [PATCH 1536/3817] Add DAGService.GetLinks() method and use it in the GC and elsewhere. This method will use the (also new) LinkService if it is available to retrieving just the links for a MerkleDAG without necessary having to retrieve the underlying block. For now the main benefit is that the pinner will not break when a block becomes invalid due to a change in the backing file. This is possible because the metadata for a block (that includes the Links) is stored separately and thus always available even if the backing file changes. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@fcee2829f584681a8fd6a39d649dfd748c0ffc4d --- pinning/pinner/gc/gc.go | 7 ++++--- pinning/pinner/pin.go | 16 ++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 7bfde538c..ef57cf6ad 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -24,11 +24,12 @@ var log = logging.Logger("gc") // // The routine then iterates over every block in the blockstore and // deletes any block that is not found in the marked set. -func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner, bestEffortRoots []*cid.Cid) (<-chan key.Key, error) { +func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin.Pinner, bestEffortRoots []*cid.Cid) (<-chan key.Key, error) { unlocker := bs.GCLock() bsrv := bserv.New(bs, offline.Exchange(bs)) ds := dag.NewDAGService(bsrv) + ds.LinkService = ls gcs, err := ColoredSet(ctx, pn, ds, bestEffortRoots) if err != nil { @@ -74,13 +75,13 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner, bestEffortRo func Descendants(ctx context.Context, ds dag.DAGService, set key.KeySet, roots []*cid.Cid, bestEffort bool) error { for _, c := range roots { set.Add(key.Key(c.Hash())) - nd, err := ds.Get(ctx, c) + links, err := ds.GetLinks(ctx, c) if err != nil { return err } // EnumerateChildren recursively walks the dag and adds the keys to the given set - err = dag.EnumerateChildren(ctx, ds, nd, func(c *cid.Cid) bool { + err = dag.EnumerateChildren(ctx, ds, links, func(c *cid.Cid) bool { k := key.Key(c.Hash()) seen := set.Has(k) if seen { diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 6edd66abc..ab949ec40 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -279,12 +279,12 @@ func (p *pinner) isPinnedWithType(c *cid.Cid, mode PinMode) (string, bool, error // Default is Indirect for _, rc := range p.recursePin.Keys() { - rnd, err := p.dserv.Get(context.Background(), rc) + links, err := p.dserv.GetLinks(context.Background(), rc) if err != nil { return "", false, err } - has, err := hasChild(p.dserv, rnd, k) + has, err := hasChild(p.dserv, links, k) if err != nil { return "", false, err } @@ -317,11 +317,11 @@ func (p *pinner) CheckIfPinned(cids ...*cid.Cid) ([]Pinned, error) { // Now walk all recursive pins to check for indirect pins var checkChildren func(*cid.Cid, *cid.Cid) error checkChildren = func(rk, parentKey *cid.Cid) error { - parent, err := p.dserv.Get(context.Background(), parentKey) + links, err := p.dserv.GetLinks(context.Background(), parentKey) if err != nil { return err } - for _, lnk := range parent.Links { + for _, lnk := range links { c := cid.NewCidV0(lnk.Hash) if toCheck.Has(c) { @@ -521,19 +521,19 @@ func (p *pinner) PinWithMode(c *cid.Cid, mode PinMode) { } } -func hasChild(ds mdag.DAGService, root *mdag.Node, child key.Key) (bool, error) { - for _, lnk := range root.Links { +func hasChild(ds mdag.DAGService, links []*mdag.Link, child key.Key) (bool, error) { + for _, lnk := range links { c := cid.NewCidV0(lnk.Hash) if key.Key(c.Hash()) == child { return true, nil } - nd, err := ds.Get(context.Background(), c) + children, err := ds.GetLinks(context.Background(), c) if err != nil { return false, err } - has, err := hasChild(ds, nd, child) + has, err := hasChild(ds, children, child) if err != nil { return false, err } From 94530f6b173cc5f7195dad6bc69bf29887fe79c5 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 30 Sep 2016 17:06:12 -0400 Subject: [PATCH 1537/3817] Don't use a separate LinkService for DAGService.GetLinks() Instead make LinkService a part of DAGService. The LinkService is now simply an interface that DAGService implements. Also provide a GetOfflineLinkService() method that the GC uses to get an offline instance. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@5631a9103159a0bb95c87cc63605d51dac59aee7 --- ipld/merkledag/merkledag.go | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index f32104d86..5f7e55fc2 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -7,6 +7,7 @@ import ( "sync" bserv "github.com/ipfs/go-ipfs/blockservice" + offline "github.com/ipfs/go-ipfs/exchange/offline" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" "context" @@ -23,21 +24,21 @@ type DAGService interface { Get(context.Context, *cid.Cid) (*Node, error) Remove(*Node) error - // Return all links for a node, may be more effect than - // calling Get - GetLinks(context.Context, *cid.Cid) ([]*Link, error) - // GetDAG returns, in order, all the single leve child // nodes of the passed in node. GetMany(context.Context, []*cid.Cid) <-chan *NodeOption Batch() *Batch + + LinkService } -// A LinkService returns the links for a node if they are available -// locally without having to retrieve the block from the datastore. type LinkService interface { - Get(*cid.Cid) ([]*Link, error) + // Return all links for a node, may be more effect than + // calling Get in DAGService + GetLinks(context.Context, *cid.Cid) ([]*Link, error) + + GetOfflineLinkService() LinkService } func NewDAGService(bs *bserv.BlockService) *dagService { @@ -50,8 +51,7 @@ func NewDAGService(bs *bserv.BlockService) *dagService { // TODO: should cache Nodes that are in memory, and be // able to free some of them when vm pressure is high type dagService struct { - Blocks *bserv.BlockService - LinkService LinkService + Blocks *bserv.BlockService } // Add adds a node to the dagService, storing the block in the BlockService @@ -105,12 +105,6 @@ func (n *dagService) Get(ctx context.Context, c *cid.Cid) (*Node, error) { } func (n *dagService) GetLinks(ctx context.Context, c *cid.Cid) ([]*Link, error) { - if n.LinkService != nil { - links, err := n.LinkService.Get(c) - if err == nil { - return links, nil - } - } node, err := n.Get(ctx, c) if err != nil { return nil, err @@ -118,6 +112,15 @@ func (n *dagService) GetLinks(ctx context.Context, c *cid.Cid) ([]*Link, error) return node.Links, nil } +func (n *dagService) GetOfflineLinkService() LinkService { + if n.Blocks.Exchange.IsOnline() { + bsrv := bserv.New(n.Blocks.Blockstore, offline.Exchange(n.Blocks.Blockstore)) + return NewDAGService(bsrv) + } else { + return n + } +} + func (n *dagService) Remove(nd *Node) error { return n.Blocks.DeleteObject(nd) } @@ -391,7 +394,7 @@ func legacyCidFromLink(lnk *Link) *cid.Cid { // EnumerateChildren will walk the dag below the given root node and add all // unseen children to the passed in set. // TODO: parallelize to avoid disk latency perf hits? -func EnumerateChildren(ctx context.Context, ds DAGService, links []*Link, visit func(*cid.Cid) bool, bestEffort bool) error { +func EnumerateChildren(ctx context.Context, ds LinkService, links []*Link, visit func(*cid.Cid) bool, bestEffort bool) error { for _, lnk := range links { c := legacyCidFromLink(lnk) if visit(c) { From 0e14be806df32947f1eac2343293fb873a0b23f7 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 30 Sep 2016 17:06:12 -0400 Subject: [PATCH 1538/3817] Don't use a separate LinkService for DAGService.GetLinks() Instead make LinkService a part of DAGService. The LinkService is now simply an interface that DAGService implements. Also provide a GetOfflineLinkService() method that the GC uses to get an offline instance. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@9dfffa9b976d6a0d24ade84aa94814fb4e465133 --- pinning/pinner/gc/gc.go | 22 +++++++++------------- pinning/pinner/pin.go | 2 +- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index ef57cf6ad..dac5e48ba 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -2,8 +2,6 @@ package gc import ( bstore "github.com/ipfs/go-ipfs/blocks/blockstore" - bserv "github.com/ipfs/go-ipfs/blockservice" - offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" @@ -27,11 +25,9 @@ var log = logging.Logger("gc") func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin.Pinner, bestEffortRoots []*cid.Cid) (<-chan key.Key, error) { unlocker := bs.GCLock() - bsrv := bserv.New(bs, offline.Exchange(bs)) - ds := dag.NewDAGService(bsrv) - ds.LinkService = ls + ls = ls.GetOfflineLinkService() - gcs, err := ColoredSet(ctx, pn, ds, bestEffortRoots) + gcs, err := ColoredSet(ctx, pn, ls, bestEffortRoots) if err != nil { return nil, err } @@ -72,16 +68,16 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. return output, nil } -func Descendants(ctx context.Context, ds dag.DAGService, set key.KeySet, roots []*cid.Cid, bestEffort bool) error { +func Descendants(ctx context.Context, ls dag.LinkService, set key.KeySet, roots []*cid.Cid, bestEffort bool) error { for _, c := range roots { set.Add(key.Key(c.Hash())) - links, err := ds.GetLinks(ctx, c) + links, err := ls.GetLinks(ctx, c) if err != nil { return err } // EnumerateChildren recursively walks the dag and adds the keys to the given set - err = dag.EnumerateChildren(ctx, ds, links, func(c *cid.Cid) bool { + err = dag.EnumerateChildren(ctx, ls, links, func(c *cid.Cid) bool { k := key.Key(c.Hash()) seen := set.Has(k) if seen { @@ -98,16 +94,16 @@ func Descendants(ctx context.Context, ds dag.DAGService, set key.KeySet, roots [ return nil } -func ColoredSet(ctx context.Context, pn pin.Pinner, ds dag.DAGService, bestEffortRoots []*cid.Cid) (key.KeySet, error) { +func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffortRoots []*cid.Cid) (key.KeySet, error) { // KeySet currently implemented in memory, in the future, may be bloom filter or // disk backed to conserve memory. gcs := key.NewKeySet() - err := Descendants(ctx, ds, gcs, pn.RecursiveKeys(), false) + err := Descendants(ctx, ls, gcs, pn.RecursiveKeys(), false) if err != nil { return nil, err } - err = Descendants(ctx, ds, gcs, bestEffortRoots, true) + err = Descendants(ctx, ls, gcs, bestEffortRoots, true) if err != nil { return nil, err } @@ -116,7 +112,7 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ds dag.DAGService, bestEffor gcs.Add(key.Key(k.Hash())) } - err = Descendants(ctx, ds, gcs, pn.InternalPins(), false) + err = Descendants(ctx, ls, gcs, pn.InternalPins(), false) if err != nil { return nil, err } diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index ab949ec40..cd55aaa99 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -521,7 +521,7 @@ func (p *pinner) PinWithMode(c *cid.Cid, mode PinMode) { } } -func hasChild(ds mdag.DAGService, links []*mdag.Link, child key.Key) (bool, error) { +func hasChild(ds mdag.LinkService, links []*mdag.Link, child key.Key) (bool, error) { for _, lnk := range links { c := cid.NewCidV0(lnk.Hash) if key.Key(c.Hash()) == child { From b9265575042abf6a86d4e260102b1077d403f2e2 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 30 Sep 2016 17:06:12 -0400 Subject: [PATCH 1539/3817] Don't use a separate LinkService for DAGService.GetLinks() Instead make LinkService a part of DAGService. The LinkService is now simply an interface that DAGService implements. Also provide a GetOfflineLinkService() method that the GC uses to get an offline instance. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-exchange-interface@d25bf371c5ef33138d37f4b9cee576b6f092bf67 --- exchange/interface.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exchange/interface.go b/exchange/interface.go index f2edc569b..23d830466 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -22,5 +22,7 @@ type Interface interface { // type Exchanger interface // available on the network? HasBlock(blocks.Block) error + IsOnline() bool + io.Closer } From aa6a68503289b12225f70c09aaf5d0a4ba988e4e Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 30 Sep 2016 17:06:12 -0400 Subject: [PATCH 1540/3817] Don't use a separate LinkService for DAGService.GetLinks() Instead make LinkService a part of DAGService. The LinkService is now simply an interface that DAGService implements. Also provide a GetOfflineLinkService() method that the GC uses to get an offline instance. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-exchange-offline@be58cab5c2eadc9b9991d518961435a951c44c01 --- exchange/offline/offline.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index b483e1825..190d5bfa2 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -67,3 +67,7 @@ func (e *offlineExchange) GetBlocks(ctx context.Context, ks []key.Key) (<-chan b }() return out, nil } + +func (e *offlineExchange) IsOnline() bool { + return false +} From 94f854bf464be1473564d3de8ad8c0b93f7c2360 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 3 Oct 2016 21:38:47 -0400 Subject: [PATCH 1541/3817] Fix EnumerateChildren & hasChild to take a *cid.Cid instead of []*mdag.Link Author: Kevin Atkinson Fix EnumerateChildren & hasChild to take a *cid.Cid instead of []*mdag.Link Author: Jeromy Johnson make FetchGraph use a cid pin: fix TestPinRecursiveFail License: MIT Signed-off-by: Jeromy License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@67ad3923d09dfd235273f6c2cc437a479ee3307f --- ipld/merkledag/merkledag.go | 29 ++++++++++++++++------------- ipld/merkledag/merkledag_test.go | 8 ++++---- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 5f7e55fc2..c6a7e2654 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -126,8 +126,8 @@ func (n *dagService) Remove(nd *Node) error { } // FetchGraph fetches all nodes that are children of the given node -func FetchGraph(ctx context.Context, root *Node, serv DAGService) error { - return EnumerateChildrenAsync(ctx, serv, root, cid.NewSet().Visit) +func FetchGraph(ctx context.Context, c *cid.Cid, serv DAGService) error { + return EnumerateChildrenAsync(ctx, serv, c, cid.NewSet().Visit) } // FindLinks searches this nodes links for the given key, @@ -394,19 +394,17 @@ func legacyCidFromLink(lnk *Link) *cid.Cid { // EnumerateChildren will walk the dag below the given root node and add all // unseen children to the passed in set. // TODO: parallelize to avoid disk latency perf hits? -func EnumerateChildren(ctx context.Context, ds LinkService, links []*Link, visit func(*cid.Cid) bool, bestEffort bool) error { +func EnumerateChildren(ctx context.Context, ds LinkService, root *cid.Cid, visit func(*cid.Cid) bool, bestEffort bool) error { + links, err := ds.GetLinks(ctx, root) + if bestEffort && err == ErrNotFound { + return nil + } else if err != nil { + return err + } for _, lnk := range links { c := legacyCidFromLink(lnk) if visit(c) { - children, err := ds.GetLinks(ctx, c) - if err != nil { - if bestEffort && err == ErrNotFound { - continue - } else { - return err - } - } - err = EnumerateChildren(ctx, ds, children, visit, bestEffort) + err = EnumerateChildren(ctx, ds, c, visit, bestEffort) if err != nil { return err } @@ -415,7 +413,7 @@ func EnumerateChildren(ctx context.Context, ds LinkService, links []*Link, visit return nil } -func EnumerateChildrenAsync(ctx context.Context, ds DAGService, root *Node, visit func(*cid.Cid) bool) error { +func EnumerateChildrenAsync(ctx context.Context, ds DAGService, c *cid.Cid, visit func(*cid.Cid) bool) error { toprocess := make(chan []*cid.Cid, 8) nodes := make(chan *NodeOption, 8) @@ -425,6 +423,11 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, root *Node, visi go fetchNodes(ctx, ds, toprocess, nodes) + root, err := ds.Get(ctx, c) + if err != nil { + return err + } + nodes <- &NodeOption{Node: root} live := 1 diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index f58bc56bd..006c8b5ca 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -231,7 +231,7 @@ func TestFetchGraph(t *testing.T) { t.Fatal(err) } - err = FetchGraph(context.TODO(), root, dservs[1]) + err = FetchGraph(context.TODO(), root.Cid(), dservs[1]) if err != nil { t.Fatal(err) } @@ -241,7 +241,7 @@ func TestFetchGraph(t *testing.T) { offline_ds := NewDAGService(bs) - err = EnumerateChildren(context.Background(), offline_ds, root.Links, func(_ *cid.Cid) bool { return true }, false) + err = EnumerateChildren(context.Background(), offline_ds, root.Cid(), func(_ *cid.Cid) bool { return true }, false) if err != nil { t.Fatal(err) } @@ -258,7 +258,7 @@ func TestEnumerateChildren(t *testing.T) { } set := cid.NewSet() - err = EnumerateChildren(context.Background(), ds, root.Links, set.Visit, false) + err = EnumerateChildren(context.Background(), ds, root.Cid(), set.Visit, false) if err != nil { t.Fatal(err) } @@ -269,7 +269,7 @@ func TestEnumerateChildren(t *testing.T) { for _, lnk := range n.Links { c := cid.NewCidV0(lnk.Hash) if !set.Has(c) { - t.Fatal("missing key in set!") + t.Fatal("missing key in set! ", lnk.Hash.B58String()) } child, err := ds.Get(context.Background(), c) if err != nil { From 80e7f03321a39a9daec6bf795fba9e6daac6149e Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 3 Oct 2016 21:38:47 -0400 Subject: [PATCH 1542/3817] Fix EnumerateChildren & hasChild to take a *cid.Cid instead of []*mdag.Link Author: Kevin Atkinson Fix EnumerateChildren & hasChild to take a *cid.Cid instead of []*mdag.Link Author: Jeromy Johnson make FetchGraph use a cid pin: fix TestPinRecursiveFail License: MIT Signed-off-by: Jeromy License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@24a9dcfacc344adfd64c1f3f72b6c5ee224d7d6c --- pinning/pinner/gc/gc.go | 6 +----- pinning/pinner/pin.go | 22 ++++++++-------------- pinning/pinner/pin_test.go | 5 +++++ 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index dac5e48ba..32b611f65 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -71,13 +71,9 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. func Descendants(ctx context.Context, ls dag.LinkService, set key.KeySet, roots []*cid.Cid, bestEffort bool) error { for _, c := range roots { set.Add(key.Key(c.Hash())) - links, err := ls.GetLinks(ctx, c) - if err != nil { - return err - } // EnumerateChildren recursively walks the dag and adds the keys to the given set - err = dag.EnumerateChildren(ctx, ls, links, func(c *cid.Cid) bool { + err := dag.EnumerateChildren(ctx, ls, c, func(c *cid.Cid) bool { k := key.Key(c.Hash()) seen := set.Has(k) if seen { diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index cd55aaa99..6e73929a6 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -178,7 +178,7 @@ func (p *pinner) Pin(ctx context.Context, node *mdag.Node, recurse bool) error { } // fetch entire graph - err := mdag.FetchGraph(ctx, node, p.dserv) + err := mdag.FetchGraph(ctx, c, p.dserv) if err != nil { return err } @@ -279,12 +279,7 @@ func (p *pinner) isPinnedWithType(c *cid.Cid, mode PinMode) (string, bool, error // Default is Indirect for _, rc := range p.recursePin.Keys() { - links, err := p.dserv.GetLinks(context.Background(), rc) - if err != nil { - return "", false, err - } - - has, err := hasChild(p.dserv, links, k) + has, err := hasChild(p.dserv, rc, k) if err != nil { return "", false, err } @@ -521,19 +516,18 @@ func (p *pinner) PinWithMode(c *cid.Cid, mode PinMode) { } } -func hasChild(ds mdag.LinkService, links []*mdag.Link, child key.Key) (bool, error) { +func hasChild(ds mdag.LinkService, root *cid.Cid, child key.Key) (bool, error) { + links, err := ds.GetLinks(context.Background(), root) + if err != nil { + return false, err + } for _, lnk := range links { c := cid.NewCidV0(lnk.Hash) if key.Key(c.Hash()) == child { return true, nil } - children, err := ds.GetLinks(context.Background(), c) - if err != nil { - return false, err - } - - has, err := hasChild(ds, children, child) + has, err := hasChild(ds, c, child) if err != nil { return false, err } diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 185b27a46..01cbeb79e 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -225,6 +225,11 @@ func TestPinRecursiveFail(t *testing.T) { t.Fatal(err) } + _, err = dserv.Add(a) + if err != nil { + t.Fatal(err) + } + // this one is time based... but shouldnt cause any issues mctx, _ = context.WithTimeout(ctx, time.Second) err = p.Pin(mctx, a, true) From e1e8dcc441377b66827394a0e13f5ae44067c34a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 6 Oct 2016 19:08:59 -0700 Subject: [PATCH 1543/3817] Remove legacy multiset 'data' fields, comment and cleanup more License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@1573888a444f37d18bfc8f0c40034cd3c2d9d2f7 --- pinning/pinner/set.go | 154 +++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 100 deletions(-) diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index bf0b41c74..9ed155990 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -2,15 +2,14 @@ package pin import ( "bytes" + "context" "crypto/rand" "encoding/binary" "errors" "fmt" "hash/fnv" "sort" - "unsafe" - "context" "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" @@ -19,8 +18,11 @@ import ( ) const ( + // defaultFanout specifies the default number of fan-out links per layer defaultFanout = 256 - maxItems = 8192 + + // maxItems is the maximum number of items that will fit in a single bucket + maxItems = 8192 ) func randomSeed() (uint32, error) { @@ -40,36 +42,12 @@ func hash(seed uint32, c *cid.Cid) uint32 { return h.Sum32() } -type itemIterator func() (c *cid.Cid, data []byte, ok bool) +type itemIterator func() (c *cid.Cid, ok bool) type keyObserver func(*cid.Cid) -// refcount is the marshaled format of refcounts. It may change -// between versions; this is valid for version 1. Changing it may -// become desirable if there are many links with refcount > 255. -// -// There are two guarantees that need to be preserved, if this is -// changed: -// -// - the marshaled format is of fixed size, matching -// unsafe.Sizeof(refcount(0)) -// - methods of refcount handle endianness, and may -// in later versions need encoding/binary. -type refcount uint8 - -func (r refcount) Bytes() []byte { - return []byte{byte(r)} -} - -// readRefcount returns the idx'th refcount in []byte, which is -// assumed to be a sequence of refcount.Bytes results. -func (r *refcount) ReadFromIdx(buf []byte, idx int) { - *r = refcount(buf[idx]) -} - type sortByHash struct { links []*merkledag.Link - data []byte } func (s sortByHash) Len() int { @@ -82,13 +60,6 @@ func (s sortByHash) Less(a, b int) bool { func (s sortByHash) Swap(a, b int) { s.links[a], s.links[b] = s.links[b], s.links[a] - if len(s.data) != 0 { - const n = int(unsafe.Sizeof(refcount(0))) - tmp := make([]byte, n) - copy(tmp, s.data[a*n:a*n+n]) - copy(s.data[a*n:a*n+n], s.data[b*n:b*n+n]) - copy(s.data[b*n:b*n+n], tmp) - } } func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint64, iter itemIterator, internalKeys keyObserver) (*merkledag.Node, error) { @@ -96,13 +67,15 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint if err != nil { return nil, err } - n := &merkledag.Node{ - Links: make([]*merkledag.Link, 0, defaultFanout+maxItems), - } + + n := &merkledag.Node{Links: make([]*merkledag.Link, 0, defaultFanout+maxItems)} for i := 0; i < defaultFanout; i++ { n.Links = append(n.Links, &merkledag.Link{Hash: emptyKey.Hash()}) } + + // add emptyKey to our set of internal pinset objects internalKeys(emptyKey) + hdr := &pb.Set{ Version: proto.Uint32(1), Fanout: proto.Uint32(defaultFanout), @@ -111,23 +84,20 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint if err := writeHdr(n, hdr); err != nil { return nil, err } - hdrLen := len(n.Data()) if estimatedLen < maxItems { // it'll probably fit for i := 0; i < maxItems; i++ { - k, data, ok := iter() + k, ok := iter() if !ok { // all done break } n.Links = append(n.Links, &merkledag.Link{Hash: k.Hash()}) - n.SetData(append(n.Data(), data...)) } // sort by hash, also swap item Data s := sortByHash{ links: n.Links[defaultFanout:], - data: n.Data()[hdrLen:], } sort.Stable(s) } @@ -147,7 +117,7 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint // and losing pins. The fix (a few lines down from this comment), is to // map the hash value down to the 8 bit keyspace here while creating the // buckets. This way, we avoid any overlapping later on. - k, _, ok := iter() + k, ok := iter() if !ok { break } @@ -161,15 +131,9 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint continue } - childIter := func() (c *cid.Cid, data []byte, ok bool) { - if len(items) == 0 { - return nil, nil, false - } - first := items[0] - items = items[1:] - return first, nil, true - } + childIter := getCidListIterator(items) + // recursively create a pinset from the items for this bucket index child, err := storeItems(ctx, dag, uint64(len(items)), childIter, internalKeys) if err != nil { return nil, err @@ -186,7 +150,9 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint } internalKeys(childKey) - n.Links[int(h)] = &merkledag.Link{ + + // overwrite the 'empty key' in the existing links array + n.Links[h] = &merkledag.Link{ Hash: childKey.Hash(), Size: size, } @@ -194,30 +160,30 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint return n, nil } -func readHdr(n *merkledag.Node) (*pb.Set, []byte, error) { +func readHdr(n *merkledag.Node) (*pb.Set, error) { hdrLenRaw, consumed := binary.Uvarint(n.Data()) if consumed <= 0 { - return nil, nil, errors.New("invalid Set header length") + return nil, errors.New("invalid Set header length") } - buf := n.Data()[consumed:] - if hdrLenRaw > uint64(len(buf)) { - return nil, nil, errors.New("impossibly large Set header length") + + pbdata := n.Data()[consumed:] + if hdrLenRaw > uint64(len(pbdata)) { + return nil, errors.New("impossibly large Set header length") } // as hdrLenRaw was <= an int, we now know it fits in an int hdrLen := int(hdrLenRaw) var hdr pb.Set - if err := proto.Unmarshal(buf[:hdrLen], &hdr); err != nil { - return nil, nil, err + if err := proto.Unmarshal(pbdata[:hdrLen], &hdr); err != nil { + return nil, err } - buf = buf[hdrLen:] if v := hdr.GetVersion(); v != 1 { - return nil, nil, fmt.Errorf("unsupported Set version: %d", v) + return nil, fmt.Errorf("unsupported Set version: %d", v) } if uint64(hdr.GetFanout()) > uint64(len(n.Links)) { - return nil, nil, errors.New("impossibly large Fanout") + return nil, errors.New("impossibly large Fanout") } - return &hdr, buf, nil + return &hdr, nil } func writeHdr(n *merkledag.Node, hdr *pb.Set) error { @@ -225,24 +191,31 @@ func writeHdr(n *merkledag.Node, hdr *pb.Set) error { if err != nil { return err } - n.SetData(make([]byte, binary.MaxVarintLen64, binary.MaxVarintLen64+len(hdrData))) - written := binary.PutUvarint(n.Data(), uint64(len(hdrData))) - n.SetData(n.Data()[:written]) - n.SetData(append(n.Data(), hdrData...)) + + // make enough space for the length prefix and the marshalled header data + data := make([]byte, binary.MaxVarintLen64, binary.MaxVarintLen64+len(hdrData)) + + // write the uvarint length of the header data + uvarlen := binary.PutUvarint(data, uint64(len(hdrData))) + + // append the actual protobuf data *after* the length value we wrote + data = append(data[:uvarlen], hdrData...) + + n.SetData(data) return nil } -type walkerFunc func(buf []byte, idx int, link *merkledag.Link) error +type walkerFunc func(idx int, link *merkledag.Link) error func walkItems(ctx context.Context, dag merkledag.DAGService, n *merkledag.Node, fn walkerFunc, children keyObserver) error { - hdr, buf, err := readHdr(n) + hdr, err := readHdr(n) if err != nil { return err } // readHdr guarantees fanout is a safe value fanout := hdr.GetFanout() for i, l := range n.Links[fanout:] { - if err := fn(buf, i, l); err != nil { + if err := fn(i, l); err != nil { return err } } @@ -278,7 +251,7 @@ func loadSet(ctx context.Context, dag merkledag.DAGService, root *merkledag.Node } var res []*cid.Cid - walk := func(buf []byte, idx int, link *merkledag.Link) error { + walk := func(idx int, link *merkledag.Link) error { res = append(res, cid.NewCidV0(link.Hash)) return nil } @@ -288,40 +261,21 @@ func loadSet(ctx context.Context, dag merkledag.DAGService, root *merkledag.Node return res, nil } -func loadMultiset(ctx context.Context, dag merkledag.DAGService, root *merkledag.Node, name string, internalKeys keyObserver) (map[key.Key]uint64, error) { - l, err := root.GetNodeLink(name) - if err != nil { - return nil, fmt.Errorf("Failed to get link %s: %v", name, err) - } - c := cid.NewCidV0(l.Hash) - internalKeys(c) - n, err := l.GetNode(ctx, dag) - if err != nil { - return nil, fmt.Errorf("Failed to get node from link %s: %v", name, err) - } - - refcounts := make(map[key.Key]uint64) - walk := func(buf []byte, idx int, link *merkledag.Link) error { - var r refcount - r.ReadFromIdx(buf, idx) - refcounts[key.Key(link.Hash)] += uint64(r) - return nil - } - if err := walkItems(ctx, dag, n, walk, internalKeys); err != nil { - return nil, err - } - return refcounts, nil -} - -func storeSet(ctx context.Context, dag merkledag.DAGService, cids []*cid.Cid, internalKeys keyObserver) (*merkledag.Node, error) { - iter := func() (c *cid.Cid, data []byte, ok bool) { +func getCidListIterator(cids []*cid.Cid) itemIterator { + return func() (c *cid.Cid, ok bool) { if len(cids) == 0 { - return nil, nil, false + return nil, false } + first := cids[0] cids = cids[1:] - return first, nil, true + return first, true } +} + +func storeSet(ctx context.Context, dag merkledag.DAGService, cids []*cid.Cid, internalKeys keyObserver) (*merkledag.Node, error) { + iter := getCidListIterator(cids) + n, err := storeItems(ctx, dag, uint64(len(cids)), iter, internalKeys) if err != nil { return nil, err From a95849021917776f586331b0aac6366c8a803d14 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 29 Sep 2016 12:35:40 -0700 Subject: [PATCH 1544/3817] fix bug in pinsets and add a stress test for the scenario License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@e4274776e6a6a823a952e7358784c74a383b5f32 --- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 130 +++++++++++++------------------------ 2 files changed, 47 insertions(+), 85 deletions(-) diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 7257ccaec..6a72d7189 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -143,7 +143,7 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint if !ok { break } - h := hash(seed, k) + h := hash(seed, k) % defaultFanout hashed[h] = append(hashed[h], item{k, data}) } for h, items := range hashed { diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index e4c8bd4de..75cf1f348 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -1,103 +1,65 @@ package pin import ( + "context" + "fmt" + "os" "testing" - "testing/quick" - "github.com/ipfs/go-ipfs/blocks/blockstore" - "github.com/ipfs/go-ipfs/blocks/key" - "github.com/ipfs/go-ipfs/blockservice" - "github.com/ipfs/go-ipfs/exchange/offline" - "github.com/ipfs/go-ipfs/merkledag" - "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" - dssync "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore/sync" - mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash" - u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" - "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" + key "github.com/ipfs/go-ipfs/blocks/key" + dag "github.com/ipfs/go-ipfs/merkledag" + mdtest "github.com/ipfs/go-ipfs/merkledag/test" ) -func ignoreKeys(key.Key) {} +func ignoreKey(_ key.Key) {} -func copyMap(m map[key.Key]uint16) map[key.Key]uint64 { - c := make(map[key.Key]uint64, len(m)) - for k, v := range m { - c[k] = uint64(v) - } - return c -} +func TestSet(t *testing.T) { + ds := mdtest.Mock() + limit := 10000 // 10000 reproduces the pinloss issue fairly reliably -func TestMultisetRoundtrip(t *testing.T) { - dstore := dssync.MutexWrap(datastore.NewMapDatastore()) - bstore := blockstore.NewBlockstore(dstore) - bserv := blockservice.New(bstore, offline.Exchange(bstore)) - dag := merkledag.NewDAGService(bserv) - - fn := func(m map[key.Key]uint16) bool { - // Convert invalid multihash from input to valid ones - for k, v := range m { - if _, err := mh.Cast([]byte(k)); err != nil { - delete(m, k) - m[key.Key(u.Hash([]byte(k)))] = v - } + if os.Getenv("STRESS_IT_OUT_YO") != "" { + limit = 10000000 + } + var inputs []key.Key + for i := 0; i < limit; i++ { + c, err := ds.Add(dag.NodeWithData([]byte(fmt.Sprint(i)))) + if err != nil { + t.Fatal(err) } - // Generate a smaller range for refcounts than full uint64, as - // otherwise this just becomes overly cpu heavy, splitting it - // out into too many items. That means we need to convert to - // the right kind of map. As storeMultiset mutates the map as - // part of its bookkeeping, this is actually good. - refcounts := copyMap(m) + inputs = append(inputs, c) + } - ctx := context.Background() - n, err := storeMultiset(ctx, dag, refcounts, ignoreKeys) - if err != nil { - t.Fatalf("storing multiset: %v", err) - } + out, err := storeSet(context.Background(), ds, inputs, ignoreKey) + if err != nil { + t.Fatal(err) + } - // Check that the node n is in the DAG - k, err := n.Key() - if err != nil { - t.Fatalf("Could not get key: %v", err) - } - _, err = dag.Get(ctx, k) - if err != nil { - t.Fatalf("Could not get node: %v", err) - } + // weird wrapper node because loadSet expects us to pass an + // object pointing to multiple named sets + setroot := &dag.Node{} + err = setroot.AddNodeLinkClean("foo", out) + if err != nil { + t.Fatal(err) + } - root := &merkledag.Node{} - const linkName = "dummylink" - if err := root.AddNodeLink(linkName, n); err != nil { - t.Fatalf("adding link to root node: %v", err) - } + outset, err := loadSet(context.Background(), ds, setroot, "foo", ignoreKey) + if err != nil { + t.Fatal(err) + } - roundtrip, err := loadMultiset(ctx, dag, root, linkName, ignoreKeys) - if err != nil { - t.Fatalf("loading multiset: %v", err) - } + if len(outset) != limit { + t.Fatal("got wrong number", len(outset), limit) + } - orig := copyMap(m) - success := true - for k, want := range orig { - if got, ok := roundtrip[k]; ok { - if got != want { - success = false - t.Logf("refcount changed: %v -> %v for %q", want, got, k) - } - delete(orig, k) - delete(roundtrip, k) - } - } - for k, v := range orig { - success = false - t.Logf("refcount missing: %v for %q", v, k) - } - for k, v := range roundtrip { - success = false - t.Logf("refcount extra: %v for %q", v, k) - } - return success + seen := key.NewKeySet() + for _, c := range outset { + seen.Add(c) } - if err := quick.Check(fn, nil); err != nil { - t.Fatal(err) + + for _, c := range inputs { + if !seen.Has(c) { + t.Fatalf("expected to have %s, didnt find it") + } } } From 4f98bbd57684ade8874dfd4cf1715ca52ec3e302 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 7 Oct 2016 11:14:45 -0700 Subject: [PATCH 1545/3817] cid: integrate cid into bitswap and blockstores License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@bc00d3abc5016767b8c47fd736da6e4bfd72123c --- pinning/pinner/gc/gc.go | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 32b611f65..414e061a7 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -1,12 +1,12 @@ package gc import ( + "context" + bstore "github.com/ipfs/go-ipfs/blocks/blockstore" dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - context "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) @@ -22,7 +22,7 @@ var log = logging.Logger("gc") // // The routine then iterates over every block in the blockstore and // deletes any block that is not found in the marked set. -func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin.Pinner, bestEffortRoots []*cid.Cid) (<-chan key.Key, error) { +func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin.Pinner, bestEffortRoots []*cid.Cid) (<-chan *cid.Cid, error) { unlocker := bs.GCLock() ls = ls.GetOfflineLinkService() @@ -37,7 +37,7 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. return nil, err } - output := make(chan key.Key) + output := make(chan *cid.Cid) go func() { defer close(output) defer unlocker.Unlock() @@ -68,20 +68,12 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. return output, nil } -func Descendants(ctx context.Context, ls dag.LinkService, set key.KeySet, roots []*cid.Cid, bestEffort bool) error { +func Descendants(ctx context.Context, ls dag.LinkService, set *cid.Set, roots []*cid.Cid, bestEffort bool) error { for _, c := range roots { - set.Add(key.Key(c.Hash())) + set.Add(c) // EnumerateChildren recursively walks the dag and adds the keys to the given set - err := dag.EnumerateChildren(ctx, ls, c, func(c *cid.Cid) bool { - k := key.Key(c.Hash()) - seen := set.Has(k) - if seen { - return false - } - set.Add(k) - return true - }, bestEffort) + err := dag.EnumerateChildren(ctx, ls, c, set.Visit, bestEffort) if err != nil { return err } @@ -90,10 +82,10 @@ func Descendants(ctx context.Context, ls dag.LinkService, set key.KeySet, roots return nil } -func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffortRoots []*cid.Cid) (key.KeySet, error) { +func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffortRoots []*cid.Cid) (*cid.Set, error) { // KeySet currently implemented in memory, in the future, may be bloom filter or // disk backed to conserve memory. - gcs := key.NewKeySet() + gcs := cid.NewSet() err := Descendants(ctx, ls, gcs, pn.RecursiveKeys(), false) if err != nil { return nil, err @@ -105,7 +97,7 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffo } for _, k := range pn.DirectKeys() { - gcs.Add(key.Key(k.Hash())) + gcs.Add(k) } err = Descendants(ctx, ls, gcs, pn.InternalPins(), false) From 5f38399a2272d908278f94d2a345e6199269c255 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 7 Oct 2016 11:14:45 -0700 Subject: [PATCH 1546/3817] cid: integrate cid into bitswap and blockstores License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@480b8850b6e01685df38b290744a69fc56425845 --- mfs/file.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mfs/file.go b/mfs/file.go index e532fb088..bbd7b48c2 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -1,6 +1,7 @@ package mfs import ( + "context" "fmt" "sync" @@ -8,8 +9,6 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - - context "context" ) type File struct { From b473d2ac053db6564b4a18be96dce93060feb692 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 7 Oct 2016 11:14:45 -0700 Subject: [PATCH 1547/3817] cid: integrate cid into bitswap and blockstores License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@a166832ce6f21b2d3ed3142efad449638b1b3f6c --- blockstore/arc_cache.go | 44 +++++++++++++--------- blockstore/arc_cache_test.go | 52 +++++++++++++++++--------- blockstore/blockstore.go | 67 +++++++++++++++++----------------- blockstore/blockstore_test.go | 42 +++++++++++---------- blockstore/bloom_cache.go | 27 +++++++------- blockstore/bloom_cache_test.go | 10 ++--- blockstore/util/remove.go | 3 +- 7 files changed, 138 insertions(+), 107 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index fd6ff7eb9..42d798a4e 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -1,13 +1,13 @@ package blockstore import ( - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + "context" "github.com/ipfs/go-ipfs/blocks" - context "context" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) @@ -31,7 +31,7 @@ func newARCCachedBS(ctx context.Context, bs Blockstore, lruSize int) (*arccache, return c, nil } -func (b *arccache) DeleteBlock(k key.Key) error { +func (b *arccache) DeleteBlock(k *cid.Cid) error { if has, ok := b.hasCached(k); ok && !has { return ErrNotFound } @@ -40,7 +40,7 @@ func (b *arccache) DeleteBlock(k key.Key) error { err := b.blockstore.DeleteBlock(k) switch err { case nil, ds.ErrNotFound, ErrNotFound: - b.arc.Add(k, false) + b.addCache(k, false) return err default: return err @@ -49,15 +49,16 @@ func (b *arccache) DeleteBlock(k key.Key) error { // if ok == false has is inconclusive // if ok == true then has respons to question: is it contained -func (b *arccache) hasCached(k key.Key) (has bool, ok bool) { +func (b *arccache) hasCached(k *cid.Cid) (has bool, ok bool) { b.total.Inc() - if k == "" { + if k == nil { + log.Error("nil cid in arccache") // Return cache invalid so the call to blockstore happens // in case of invalid key and correct error is created. return false, false } - h, ok := b.arc.Get(k) + h, ok := b.arc.Get(k.KeyString()) if ok { b.hits.Inc() return h.(bool), true @@ -65,40 +66,45 @@ func (b *arccache) hasCached(k key.Key) (has bool, ok bool) { return false, false } -func (b *arccache) Has(k key.Key) (bool, error) { +func (b *arccache) Has(k *cid.Cid) (bool, error) { if has, ok := b.hasCached(k); ok { return has, nil } res, err := b.blockstore.Has(k) if err == nil { - b.arc.Add(k, res) + b.addCache(k, res) } return res, err } -func (b *arccache) Get(k key.Key) (blocks.Block, error) { +func (b *arccache) Get(k *cid.Cid) (blocks.Block, error) { + if k == nil { + log.Error("nil cid in arc cache") + return nil, ErrNotFound + } + if has, ok := b.hasCached(k); ok && !has { return nil, ErrNotFound } bl, err := b.blockstore.Get(k) if bl == nil && err == ErrNotFound { - b.arc.Add(k, false) + b.addCache(k, false) } else if bl != nil { - b.arc.Add(k, true) + b.addCache(k, true) } return bl, err } func (b *arccache) Put(bl blocks.Block) error { - if has, ok := b.hasCached(bl.Key()); ok && has { + if has, ok := b.hasCached(bl.Cid()); ok && has { return nil } err := b.blockstore.Put(bl) if err == nil { - b.arc.Add(bl.Key(), true) + b.addCache(bl.Cid(), true) } return err } @@ -108,7 +114,7 @@ func (b *arccache) PutMany(bs []blocks.Block) error { for _, block := range bs { // call put on block if result is inconclusive or we are sure that // the block isn't in storage - if has, ok := b.hasCached(block.Key()); !ok || (ok && !has) { + if has, ok := b.hasCached(block.Cid()); !ok || (ok && !has) { good = append(good, block) } } @@ -117,12 +123,16 @@ func (b *arccache) PutMany(bs []blocks.Block) error { return err } for _, block := range good { - b.arc.Add(block.Key(), true) + b.addCache(block.Cid(), true) } return nil } -func (b *arccache) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { +func (b *arccache) addCache(c *cid.Cid, has bool) { + b.arc.Add(c.KeyString(), has) +} + +func (b *arccache) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { return b.blockstore.AllKeysChan(ctx) } diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 4bf9307a8..384cca270 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -1,12 +1,12 @@ package blockstore import ( + "context" "testing" "github.com/ipfs/go-ipfs/blocks" - "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - context "context" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) @@ -60,7 +60,7 @@ func TestRemoveCacheEntryOnDelete(t *testing.T) { writeHitTheDatastore = true }) - arc.DeleteBlock(exampleBlock.Key()) + arc.DeleteBlock(exampleBlock.Cid()) arc.Put(exampleBlock) if !writeHitTheDatastore { t.Fail() @@ -78,9 +78,9 @@ func TestElideDuplicateWrite(t *testing.T) { func TestHasRequestTriggersCache(t *testing.T) { arc, _, cd := createStores(t) - arc.Has(exampleBlock.Key()) + arc.Has(exampleBlock.Cid()) trap("has hit datastore", cd, t) - if has, err := arc.Has(exampleBlock.Key()); has || err != nil { + if has, err := arc.Has(exampleBlock.Cid()); has || err != nil { t.Fatal("has was true but there is no such block") } @@ -92,7 +92,7 @@ func TestHasRequestTriggersCache(t *testing.T) { trap("has hit datastore", cd, t) - if has, err := arc.Has(exampleBlock.Key()); !has || err != nil { + if has, err := arc.Has(exampleBlock.Cid()); !has || err != nil { t.Fatal("has returned invalid result") } } @@ -100,13 +100,13 @@ func TestHasRequestTriggersCache(t *testing.T) { func TestGetFillsCache(t *testing.T) { arc, _, cd := createStores(t) - if bl, err := arc.Get(exampleBlock.Key()); bl != nil || err == nil { + if bl, err := arc.Get(exampleBlock.Cid()); bl != nil || err == nil { t.Fatal("block was found or there was no error") } trap("has hit datastore", cd, t) - if has, err := arc.Has(exampleBlock.Key()); has || err != nil { + if has, err := arc.Has(exampleBlock.Cid()); has || err != nil { t.Fatal("has was true but there is no such block") } @@ -118,7 +118,7 @@ func TestGetFillsCache(t *testing.T) { trap("has hit datastore", cd, t) - if has, err := arc.Has(exampleBlock.Key()); !has || err != nil { + if has, err := arc.Has(exampleBlock.Cid()); !has || err != nil { t.Fatal("has returned invalid result") } } @@ -126,15 +126,15 @@ func TestGetFillsCache(t *testing.T) { func TestGetAndDeleteFalseShortCircuit(t *testing.T) { arc, _, cd := createStores(t) - arc.Has(exampleBlock.Key()) + arc.Has(exampleBlock.Cid()) trap("get hit datastore", cd, t) - if bl, err := arc.Get(exampleBlock.Key()); bl != nil || err != ErrNotFound { + if bl, err := arc.Get(exampleBlock.Cid()); bl != nil || err != ErrNotFound { t.Fatal("get returned invalid result") } - if arc.DeleteBlock(exampleBlock.Key()) != ErrNotFound { + if arc.DeleteBlock(exampleBlock.Cid()) != ErrNotFound { t.Fatal("expected ErrNotFound error") } } @@ -148,7 +148,7 @@ func TestArcCreationFailure(t *testing.T) { func TestInvalidKey(t *testing.T) { arc, _, _ := createStores(t) - bl, err := arc.Get(key.Key("")) + bl, err := arc.Get(nil) if bl != nil { t.Fatal("blocks should be nil") @@ -163,10 +163,28 @@ func TestHasAfterSucessfulGetIsCached(t *testing.T) { bs.Put(exampleBlock) - arc.Get(exampleBlock.Key()) + arc.Get(exampleBlock.Cid()) trap("has hit datastore", cd, t) - arc.Has(exampleBlock.Key()) + arc.Has(exampleBlock.Cid()) +} + +func TestDifferentKeyObjectsWork(t *testing.T) { + arc, bs, cd := createStores(t) + + bs.Put(exampleBlock) + + arc.Get(exampleBlock.Cid()) + + trap("has hit datastore", cd, t) + cidstr := exampleBlock.Cid().String() + + ncid, err := cid.Decode(cidstr) + if err != nil { + t.Fatal(err) + } + + arc.Has(ncid) } func TestPutManyCaches(t *testing.T) { @@ -174,9 +192,9 @@ func TestPutManyCaches(t *testing.T) { arc.PutMany([]blocks.Block{exampleBlock}) trap("has hit datastore", cd, t) - arc.Has(exampleBlock.Key()) + arc.Has(exampleBlock.Cid()) untrap(cd) - arc.DeleteBlock(exampleBlock.Key()) + arc.DeleteBlock(exampleBlock.Cid()) arc.Put(exampleBlock) trap("PunMany has hit datastore", cd, t) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 162a21da0..7d4d10de1 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -3,15 +3,16 @@ package blockstore import ( + "context" "errors" "sync" "sync/atomic" - context "context" blocks "github.com/ipfs/go-ipfs/blocks" + dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dsns "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/namespace" dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" @@ -29,13 +30,13 @@ var ErrNotFound = errors.New("blockstore: block not found") // Blockstore wraps a Datastore type Blockstore interface { - DeleteBlock(key.Key) error - Has(key.Key) (bool, error) - Get(key.Key) (blocks.Block, error) + DeleteBlock(*cid.Cid) error + Has(*cid.Cid) (bool, error) + Get(*cid.Cid) (blocks.Block, error) Put(blocks.Block) error PutMany([]blocks.Block) error - AllKeysChan(ctx context.Context) (<-chan key.Key, error) + AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) } type GCBlockstore interface { @@ -80,12 +81,13 @@ func (bs *blockstore) HashOnRead(enabled bool) { bs.rehash = enabled } -func (bs *blockstore) Get(k key.Key) (blocks.Block, error) { - if k == "" { +func (bs *blockstore) Get(k *cid.Cid) (blocks.Block, error) { + if k == nil { + log.Error("nil cid in blockstore") return nil, ErrNotFound } - maybeData, err := bs.datastore.Get(k.DsKey()) + maybeData, err := bs.datastore.Get(dshelp.NewKeyFromBinary(k.KeyString())) if err == ds.ErrNotFound { return nil, ErrNotFound } @@ -99,18 +101,18 @@ func (bs *blockstore) Get(k key.Key) (blocks.Block, error) { if bs.rehash { rb := blocks.NewBlock(bdata) - if rb.Key() != k { + if !rb.Cid().Equals(k) { return nil, ErrHashMismatch } else { return rb, nil } } else { - return blocks.NewBlockWithHash(bdata, mh.Multihash(k)) + return blocks.NewBlockWithCid(bdata, k) } } func (bs *blockstore) Put(block blocks.Block) error { - k := block.Key().DsKey() + k := dshelp.NewKeyFromBinary(block.Cid().KeyString()) // Has is cheaper than Put, so see if we already have it exists, err := bs.datastore.Has(k) @@ -126,7 +128,7 @@ func (bs *blockstore) PutMany(blocks []blocks.Block) error { return err } for _, b := range blocks { - k := b.Key().DsKey() + k := dshelp.NewKeyFromBinary(b.Cid().KeyString()) exists, err := bs.datastore.Has(k) if err == nil && exists { continue @@ -140,19 +142,19 @@ func (bs *blockstore) PutMany(blocks []blocks.Block) error { return t.Commit() } -func (bs *blockstore) Has(k key.Key) (bool, error) { - return bs.datastore.Has(k.DsKey()) +func (bs *blockstore) Has(k *cid.Cid) (bool, error) { + return bs.datastore.Has(dshelp.NewKeyFromBinary(k.KeyString())) } -func (s *blockstore) DeleteBlock(k key.Key) error { - return s.datastore.Delete(k.DsKey()) +func (s *blockstore) DeleteBlock(k *cid.Cid) error { + return s.datastore.Delete(dshelp.NewKeyFromBinary(k.KeyString())) } // AllKeysChan runs a query for keys from the blockstore. // this is very simplistic, in the future, take dsq.Query as a param? // // AllKeysChan respects context -func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { +func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { // KeysOnly, because that would be _a lot_ of data. q := dsq.Query{KeysOnly: true} @@ -164,39 +166,38 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { } // this function is here to compartmentalize - get := func() (key.Key, bool) { + get := func() (*cid.Cid, bool) { select { case <-ctx.Done(): - return "", false + return nil, false case e, more := <-res.Next(): if !more { - return "", false + return nil, false } if e.Error != nil { log.Debug("blockstore.AllKeysChan got err:", e.Error) - return "", false + return nil, false } // need to convert to key.Key using key.KeyFromDsKey. - k, err := key.KeyFromDsKey(ds.NewKey(e.Key)) + kb, err := dshelp.BinaryFromDsKey(ds.NewKey(e.Key)) // TODO: calling NewKey isnt free if err != nil { log.Warningf("error parsing key from DsKey: ", err) - return "", true + return nil, true } - log.Debug("blockstore: query got key", k) - // key must be a multihash. else ignore it. - _, err = mh.Cast([]byte(k)) + c, err := cid.Cast(kb) if err != nil { - log.Warningf("key from datastore was not a multihash: ", err) - return "", true + log.Warning("error parsing cid from decoded DsKey: ", err) + return nil, true } + log.Debug("blockstore: query got key", c) - return k, true + return c, true } } - output := make(chan key.Key, dsq.KeysOnlyBufSize) + output := make(chan *cid.Cid, dsq.KeysOnlyBufSize) go func() { defer func() { res.Process().Close() // ensure exit (signals early exit, too) @@ -208,7 +209,7 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { if !ok { return } - if k == "" { + if k == nil { continue } diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 75385dd2a..98882d479 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -2,22 +2,24 @@ package blockstore import ( "bytes" + "context" "fmt" "testing" - context "context" + blocks "github.com/ipfs/go-ipfs/blocks" + dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" ds_sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - - blocks "github.com/ipfs/go-ipfs/blocks" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" ) func TestGetWhenKeyNotPresent(t *testing.T) { bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) - bl, err := bs.Get(key.Key("not present")) + c := cid.NewCidV0(u.Hash([]byte("stuff"))) + bl, err := bs.Get(c) if bl != nil { t.Error("nil block expected") @@ -27,9 +29,9 @@ func TestGetWhenKeyNotPresent(t *testing.T) { } } -func TestGetWhenKeyIsEmptyString(t *testing.T) { +func TestGetWhenKeyIsNil(t *testing.T) { bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) - _, err := bs.Get(key.Key("")) + _, err := bs.Get(nil) if err != ErrNotFound { t.Fail() } @@ -44,7 +46,7 @@ func TestPutThenGetBlock(t *testing.T) { t.Fatal(err) } - blockFromBlockstore, err := bs.Get(block.Key()) + blockFromBlockstore, err := bs.Get(block.Cid()) if err != nil { t.Fatal(err) } @@ -62,7 +64,7 @@ func TestHashOnRead(t *testing.T) { bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) bl := blocks.NewBlock([]byte("some data")) - blBad, err := blocks.NewBlockWithHash([]byte("some other data"), bl.Key().ToMultihash()) + blBad, err := blocks.NewBlockWithCid([]byte("some other data"), bl.Cid()) if err != nil { t.Fatal("debug is off, still got an error") } @@ -71,35 +73,35 @@ func TestHashOnRead(t *testing.T) { bs.Put(bl2) bs.HashOnRead(true) - if _, err := bs.Get(bl.Key()); err != ErrHashMismatch { + if _, err := bs.Get(bl.Cid()); err != ErrHashMismatch { t.Fatalf("expected '%v' got '%v'\n", ErrHashMismatch, err) } - if b, err := bs.Get(bl2.Key()); err != nil || b.String() != bl2.String() { + if b, err := bs.Get(bl2.Cid()); err != nil || b.String() != bl2.String() { t.Fatal("got wrong blocks") } } -func newBlockStoreWithKeys(t *testing.T, d ds.Datastore, N int) (Blockstore, []key.Key) { +func newBlockStoreWithKeys(t *testing.T, d ds.Datastore, N int) (Blockstore, []*cid.Cid) { if d == nil { d = ds.NewMapDatastore() } bs := NewBlockstore(ds_sync.MutexWrap(d)) - keys := make([]key.Key, N) + keys := make([]*cid.Cid, N) for i := 0; i < N; i++ { block := blocks.NewBlock([]byte(fmt.Sprintf("some data %d", i))) err := bs.Put(block) if err != nil { t.Fatal(err) } - keys[i] = block.Key() + keys[i] = block.Cid() } return bs, keys } -func collect(ch <-chan key.Key) []key.Key { - var keys []key.Key +func collect(ch <-chan *cid.Cid) []*cid.Cid { + var keys []*cid.Cid for k := range ch { keys = append(keys, k) } @@ -188,18 +190,18 @@ func TestValueTypeMismatch(t *testing.T) { block := blocks.NewBlock([]byte("some data")) datastore := ds.NewMapDatastore() - k := BlockPrefix.Child(block.Key().DsKey()) + k := BlockPrefix.Child(dshelp.NewKeyFromBinary(block.Cid().KeyString())) datastore.Put(k, "data that isn't a block!") blockstore := NewBlockstore(ds_sync.MutexWrap(datastore)) - _, err := blockstore.Get(block.Key()) + _, err := blockstore.Get(block.Cid()) if err != ValueTypeMismatch { t.Fatal(err) } } -func expectMatches(t *testing.T, expect, actual []key.Key) { +func expectMatches(t *testing.T, expect, actual []*cid.Cid) { if len(expect) != len(actual) { t.Errorf("expect and actual differ: %d != %d", len(expect), len(actual)) @@ -207,7 +209,7 @@ func expectMatches(t *testing.T, expect, actual []key.Key) { for _, ek := range expect { found := false for _, ak := range actual { - if ek == ak { + if ek.Equals(ak) { found = true } } diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 6abd4886c..e78a4211c 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -1,14 +1,14 @@ package blockstore import ( + "context" "sync/atomic" "time" "github.com/ipfs/go-ipfs/blocks" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - context "context" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" bloom "gx/ipfs/QmeiMCBkYHxkDkDfnDadzz4YxY5ruL5Pj499essE4vRsGM/bbloom" ) @@ -84,7 +84,7 @@ func (b *bloomcache) Rebuild(ctx context.Context) { select { case key, ok := <-ch: if ok { - b.bloom.AddTS([]byte(key)) // Use binary key, the more compact the better + b.bloom.AddTS(key.Bytes()) // Use binary key, the more compact the better } else { finish = true } @@ -97,7 +97,7 @@ func (b *bloomcache) Rebuild(ctx context.Context) { atomic.StoreInt32(&b.active, 1) } -func (b *bloomcache) DeleteBlock(k key.Key) error { +func (b *bloomcache) DeleteBlock(k *cid.Cid) error { if has, ok := b.hasCached(k); ok && !has { return ErrNotFound } @@ -107,15 +107,16 @@ func (b *bloomcache) DeleteBlock(k key.Key) error { // if ok == false has is inconclusive // if ok == true then has respons to question: is it contained -func (b *bloomcache) hasCached(k key.Key) (has bool, ok bool) { +func (b *bloomcache) hasCached(k *cid.Cid) (has bool, ok bool) { b.total.Inc() - if k == "" { + if k == nil { + log.Error("nil cid in bloom cache") // Return cache invalid so call to blockstore // in case of invalid key is forwarded deeper return false, false } if b.BloomActive() { - blr := b.bloom.HasTS([]byte(k)) + blr := b.bloom.HasTS(k.Bytes()) if blr == false { // not contained in bloom is only conclusive answer bloom gives b.hits.Inc() return false, true @@ -124,7 +125,7 @@ func (b *bloomcache) hasCached(k key.Key) (has bool, ok bool) { return false, false } -func (b *bloomcache) Has(k key.Key) (bool, error) { +func (b *bloomcache) Has(k *cid.Cid) (bool, error) { if has, ok := b.hasCached(k); ok { return has, nil } @@ -132,7 +133,7 @@ func (b *bloomcache) Has(k key.Key) (bool, error) { return b.blockstore.Has(k) } -func (b *bloomcache) Get(k key.Key) (blocks.Block, error) { +func (b *bloomcache) Get(k *cid.Cid) (blocks.Block, error) { if has, ok := b.hasCached(k); ok && !has { return nil, ErrNotFound } @@ -141,13 +142,13 @@ func (b *bloomcache) Get(k key.Key) (blocks.Block, error) { } func (b *bloomcache) Put(bl blocks.Block) error { - if has, ok := b.hasCached(bl.Key()); ok && has { + if has, ok := b.hasCached(bl.Cid()); ok && has { return nil } err := b.blockstore.Put(bl) if err == nil { - b.bloom.AddTS([]byte(bl.Key())) + b.bloom.AddTS(bl.Cid().Bytes()) } return err } @@ -162,12 +163,12 @@ func (b *bloomcache) PutMany(bs []blocks.Block) error { return err } for _, bl := range bs { - b.bloom.AddTS([]byte(bl.Key())) + b.bloom.AddTS(bl.Cid().Bytes()) } return nil } -func (b *bloomcache) AllKeysChan(ctx context.Context) (<-chan key.Key, error) { +func (b *bloomcache) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { return b.blockstore.AllKeysChan(ctx) } diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 248308874..8bdf567f0 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -44,7 +44,7 @@ func TestPutManyAddsToBloom(t *testing.T) { block2 := blocks.NewBlock([]byte("bar")) cachedbs.PutMany([]blocks.Block{block1}) - has, err := cachedbs.Has(block1.Key()) + has, err := cachedbs.Has(block1.Cid()) if err != nil { t.Fatal(err) } @@ -52,7 +52,7 @@ func TestPutManyAddsToBloom(t *testing.T) { t.Fatal("added block is reported missing") } - has, err = cachedbs.Has(block2.Key()) + has, err = cachedbs.Has(block2.Cid()) if err != nil { t.Fatal(err) } @@ -93,7 +93,7 @@ func TestHasIsBloomCached(t *testing.T) { }) for i := 0; i < 1000; i++ { - cachedbs.Has(blocks.NewBlock([]byte(fmt.Sprintf("data: %d", i+2000))).Key()) + cachedbs.Has(blocks.NewBlock([]byte(fmt.Sprintf("data: %d", i+2000))).Cid()) } if float64(cacheFails)/float64(1000) > float64(0.05) { @@ -112,11 +112,11 @@ func TestHasIsBloomCached(t *testing.T) { t.Fatalf("expected datastore hit: %d", cacheFails) } - if has, err := cachedbs.Has(block.Key()); !has || err != nil { + if has, err := cachedbs.Has(block.Cid()); !has || err != nil { t.Fatal("has gave wrong response") } - bl, err := cachedbs.Get(block.Key()) + bl, err := cachedbs.Get(block.Cid()) if bl.String() != block.String() { t.Fatal("block data doesn't match") } diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 4b5f86d14..3afc92d45 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -6,7 +6,6 @@ import ( bs "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/pin" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) @@ -38,7 +37,7 @@ func RmBlocks(blocks bs.GCBlockstore, pins pin.Pinner, out chan<- interface{}, c stillOkay := FilterPinned(pins, out, cids) for _, c := range stillOkay { - err := blocks.DeleteBlock(key.Key(c.Hash())) + err := blocks.DeleteBlock(c) if err != nil && opts.Force && (err == bs.ErrNotFound || err == ds.ErrNotFound) { // ignore non-existent blocks } else if err != nil { From 3e45f2b90fd3a1e0a924037b9be56a174af7b64d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 7 Oct 2016 11:14:45 -0700 Subject: [PATCH 1548/3817] cid: integrate cid into bitswap and blockstores License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-interface@b4c35b3ac29f6c4a35df24df8e9e5ea39458b531 --- exchange/interface.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index 23d830466..a2b3c760b 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -2,21 +2,21 @@ package exchange import ( + "context" "io" blocks "github.com/ipfs/go-ipfs/blocks" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - context "context" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) // Any type that implements exchange.Interface may be used as an IPFS block // exchange protocol. type Interface interface { // type Exchanger interface // GetBlock returns the block associated with a given key. - GetBlock(context.Context, key.Key) (blocks.Block, error) + GetBlock(context.Context, *cid.Cid) (blocks.Block, error) - GetBlocks(context.Context, []key.Key) (<-chan blocks.Block, error) + GetBlocks(context.Context, []*cid.Cid) (<-chan blocks.Block, error) // TODO Should callers be concerned with whether the block was made // available on the network? From 6ad04673f33333e4593073607b546d8facb3aba8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 7 Oct 2016 11:14:45 -0700 Subject: [PATCH 1549/3817] cid: integrate cid into bitswap and blockstores License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@83741fa63ed9f162997f4cf5ca6f969e953222e3 --- exchange/offline/offline.go | 11 ++++++----- exchange/offline/offline_test.go | 17 ++++++++++------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 190d5bfa2..7013d10a0 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -3,12 +3,13 @@ package offline import ( + "context" + blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - context "context" + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { @@ -24,7 +25,7 @@ type offlineExchange struct { // GetBlock returns nil to signal that a block could not be retrieved for the // given key. // NB: This function may return before the timeout expires. -func (e *offlineExchange) GetBlock(_ context.Context, k key.Key) (blocks.Block, error) { +func (e *offlineExchange) GetBlock(_ context.Context, k *cid.Cid) (blocks.Block, error) { return e.bs.Get(k) } @@ -40,11 +41,11 @@ func (_ *offlineExchange) Close() error { return nil } -func (e *offlineExchange) GetBlocks(ctx context.Context, ks []key.Key) (<-chan blocks.Block, error) { +func (e *offlineExchange) GetBlocks(ctx context.Context, ks []*cid.Cid) (<-chan blocks.Block, error) { out := make(chan blocks.Block, 0) go func() { defer close(out) - var misses []key.Key + var misses []*cid.Cid for _, k := range ks { hit, err := e.bs.Get(k) if err != nil { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 9cbd71333..80fb6bbed 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -1,20 +1,23 @@ package offline import ( + "context" "testing" - context "context" blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ds_sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) func TestBlockReturnsErr(t *testing.T) { off := Exchange(bstore()) - _, err := off.GetBlock(context.Background(), key.Key("foo")) + c := cid.NewCidV0(u.Hash([]byte("foo"))) + _, err := off.GetBlock(context.Background(), c) if err != nil { return // as desired } @@ -31,7 +34,7 @@ func TestHasBlockReturnsNil(t *testing.T) { t.Fail() } - if _, err := store.Get(block.Key()); err != nil { + if _, err := store.Get(block.Cid()); err != nil { t.Fatal(err) } } @@ -49,11 +52,11 @@ func TestGetBlocks(t *testing.T) { } } - request := func() []key.Key { - var ks []key.Key + request := func() []*cid.Cid { + var ks []*cid.Cid for _, b := range expected { - ks = append(ks, b.Key()) + ks = append(ks, b.Cid()) } return ks }() From 2971e922db2992e5e63010e0e2d9e95ebd292e7b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 7 Oct 2016 11:14:45 -0700 Subject: [PATCH 1550/3817] cid: integrate cid into bitswap and blockstores License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@b36159b9d0e7260e02cb80484e72597d8a7d0f5d --- blockservice/blockservice.go | 37 +++++++++++--------------------- blockservice/test/blocks_test.go | 17 +++++++-------- 2 files changed, 20 insertions(+), 34 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index dd274d27e..3fb47aa0b 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -10,7 +10,6 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" context "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" @@ -30,12 +29,6 @@ type BlockService struct { Exchange exchange.Interface } -// an Object is simply a typed block -type Object interface { - Cid() *cid.Cid - blocks.Block -} - // NewBlockService creates a BlockService with given datastore instance. func New(bs blockstore.Blockstore, rem exchange.Interface) *BlockService { if rem == nil { @@ -50,14 +43,14 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) *BlockService { // AddBlock adds a particular block to the service, Putting it into the datastore. // TODO pass a context into this if the remote.HasBlock is going to remain here. -func (s *BlockService) AddObject(o Object) (*cid.Cid, error) { +func (s *BlockService) AddBlock(o blocks.Block) (*cid.Cid, error) { // TODO: while this is a great optimization, we should think about the // possibility of streaming writes directly to disk. If we can pass this object // all the way down to the datastore without having to 'buffer' its data, // we could implement a `WriteTo` method on it that could do a streaming write // of the content, saving us (probably) considerable memory. c := o.Cid() - has, err := s.Blockstore.Has(key.Key(c.Hash())) + has, err := s.Blockstore.Has(c) if err != nil { return nil, err } @@ -78,13 +71,10 @@ func (s *BlockService) AddObject(o Object) (*cid.Cid, error) { return c, nil } -func (s *BlockService) AddObjects(bs []Object) ([]*cid.Cid, error) { +func (s *BlockService) AddBlocks(bs []blocks.Block) ([]*cid.Cid, error) { var toput []blocks.Block - var toputcids []*cid.Cid for _, b := range bs { - c := b.Cid() - - has, err := s.Blockstore.Has(key.Key(c.Hash())) + has, err := s.Blockstore.Has(b.Cid()) if err != nil { return nil, err } @@ -94,7 +84,6 @@ func (s *BlockService) AddObjects(bs []Object) ([]*cid.Cid, error) { } toput = append(toput, b) - toputcids = append(toputcids, c) } err := s.Blockstore.PutMany(toput) @@ -108,8 +97,7 @@ func (s *BlockService) AddObjects(bs []Object) ([]*cid.Cid, error) { return nil, fmt.Errorf("blockservice is closed (%s)", err) } - c := o.(Object).Cid() // cast is safe, we created these - ks = append(ks, c) + ks = append(ks, o.Cid()) } return ks, nil } @@ -119,7 +107,7 @@ func (s *BlockService) AddObjects(bs []Object) ([]*cid.Cid, error) { func (s *BlockService) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) { log.Debugf("BlockService GetBlock: '%s'", c) - block, err := s.Blockstore.Get(key.Key(c.Hash())) + block, err := s.Blockstore.Get(c) if err == nil { return block, nil } @@ -128,7 +116,7 @@ func (s *BlockService) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, // TODO be careful checking ErrNotFound. If the underlying // implementation changes, this will break. log.Debug("Blockservice: Searching bitswap") - blk, err := s.Exchange.GetBlock(ctx, key.Key(c.Hash())) + blk, err := s.Exchange.GetBlock(ctx, c) if err != nil { if err == blockstore.ErrNotFound { return nil, ErrNotFound @@ -153,12 +141,11 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan bloc out := make(chan blocks.Block, 0) go func() { defer close(out) - var misses []key.Key + var misses []*cid.Cid for _, c := range ks { - k := key.Key(c.Hash()) - hit, err := s.Blockstore.Get(k) + hit, err := s.Blockstore.Get(c) if err != nil { - misses = append(misses, k) + misses = append(misses, c) continue } log.Debug("Blockservice: Got data in datastore") @@ -191,8 +178,8 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan bloc } // DeleteBlock deletes a block in the blockservice from the datastore -func (s *BlockService) DeleteObject(o Object) error { - return s.Blockstore.DeleteBlock(o.Key()) +func (s *BlockService) DeleteBlock(o blocks.Block) error { + return s.Blockstore.DeleteBlock(o.Cid()) } func (s *BlockService) Close() error { diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 9b86d5a61..005207763 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -2,6 +2,7 @@ package bstest import ( "bytes" + "context" "fmt" "testing" "time" @@ -10,9 +11,7 @@ import ( blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - "context" cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" @@ -44,11 +43,11 @@ func TestBlocks(t *testing.T) { t.Error("Block Multihash and data multihash not equal") } - if o.Key() != key.Key(h) { + if !o.Cid().Equals(cid.NewCidV0(h)) { t.Error("Block key and data multihash key not equal") } - k, err := bs.AddObject(o) + k, err := bs.AddBlock(o) if err != nil { t.Error("failed to add block to BlockService", err) return @@ -66,7 +65,7 @@ func TestBlocks(t *testing.T) { return } - if o.Key() != b2.Key() { + if !o.Cid().Equals(b2.Cid()) { t.Error("Block keys not equal.") } @@ -93,7 +92,7 @@ func TestGetBlocksSequential(t *testing.T) { var cids []*cid.Cid for _, o := range objs { cids = append(cids, o.Cid()) - servs[0].AddObject(o) + servs[0].AddBlock(o) } t.Log("one instance at a time, get blocks concurrently") @@ -102,12 +101,12 @@ func TestGetBlocksSequential(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*50) defer cancel() out := servs[i].GetBlocks(ctx, cids) - gotten := make(map[key.Key]blocks.Block) + gotten := make(map[string]blocks.Block) for blk := range out { - if _, ok := gotten[blk.Key()]; ok { + if _, ok := gotten[blk.Cid().KeyString()]; ok { t.Fatal("Got duplicate block!") } - gotten[blk.Key()] = blk + gotten[blk.Cid().KeyString()] = blk } if len(gotten) != len(objs) { t.Fatalf("Didnt get enough blocks back: %d/%d", len(gotten), len(objs)) From 4419b365e0cbf2aff5b4d471a8f077e14493efad Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 7 Oct 2016 11:14:45 -0700 Subject: [PATCH 1551/3817] cid: integrate cid into bitswap and blockstores License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@dbebd41776fad49024f9a34d010a08b87dbea82d --- ipld/merkledag/merkledag.go | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index c6a7e2654..b9431522b 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -2,15 +2,15 @@ package merkledag import ( + "context" "fmt" "strings" "sync" + blocks "github.com/ipfs/go-ipfs/blocks" bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) @@ -60,7 +60,7 @@ func (n *dagService) Add(nd *Node) (*cid.Cid, error) { return nil, fmt.Errorf("dagService is nil") } - return n.Blocks.AddObject(nd) + return n.Blocks.AddBlock(nd) } func (n *dagService) Batch() *Batch { @@ -122,7 +122,7 @@ func (n *dagService) GetOfflineLinkService() LinkService { } func (n *dagService) Remove(nd *Node) error { - return n.Blocks.DeleteObject(nd) + return n.Blocks.DeleteBlock(nd) } // FetchGraph fetches all nodes that are children of the given node @@ -147,27 +147,11 @@ type NodeOption struct { Err error } -// TODO: this is a mid-term hack to get around the fact that blocks don't -// have full CIDs and potentially (though we don't know of any such scenario) -// may have the same block with multiple different encodings. -// We have discussed the possiblity of using CIDs as datastore keys -// in the future. This would be a much larger changeset than i want to make -// right now. -func cidsToKeyMapping(cids []*cid.Cid) map[key.Key]*cid.Cid { - mapping := make(map[key.Key]*cid.Cid) - for _, c := range cids { - mapping[key.Key(c.Hash())] = c - } - return mapping -} - func (ds *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *NodeOption { out := make(chan *NodeOption, len(keys)) blocks := ds.Blocks.GetBlocks(ctx, keys) var count int - mapping := cidsToKeyMapping(keys) - go func() { defer close(out) for { @@ -180,7 +164,7 @@ func (ds *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *Node return } - c := mapping[b.Key()] + c := b.Cid() var nd *Node switch c.Type() { @@ -361,7 +345,7 @@ func (np *nodePromise) Get(ctx context.Context) (*Node, error) { type Batch struct { ds *dagService - objects []bserv.Object + blocks []blocks.Block size int MaxSize int } @@ -372,7 +356,7 @@ func (t *Batch) Add(nd *Node) (*cid.Cid, error) { return nil, err } - t.objects = append(t.objects, nd) + t.blocks = append(t.blocks, nd) t.size += len(d) if t.size > t.MaxSize { return nd.Cid(), t.Commit() @@ -381,8 +365,8 @@ func (t *Batch) Add(nd *Node) (*cid.Cid, error) { } func (t *Batch) Commit() error { - _, err := t.ds.Blocks.AddObjects(t.objects) - t.objects = nil + _, err := t.ds.Blocks.AddBlocks(t.blocks) + t.blocks = nil t.size = 0 return err } From 370cc11d0efcff50e7c89a4d568a8cb10013fc78 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 7 Oct 2016 11:14:45 -0700 Subject: [PATCH 1552/3817] cid: integrate cid into bitswap and blockstores License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-ds-help@3773f2a2ac1918e95350bbd5dacc89ae02fa5d11 --- datastore/dshelp/key.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index db680add2..417f49605 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -9,3 +9,7 @@ import ( func NewKeyFromBinary(s string) ds.Key { return ds.NewKey(base32.RawStdEncoding.EncodeToString([]byte(s))) } + +func BinaryFromDsKey(k ds.Key) ([]byte, error) { + return base32.RawStdEncoding.DecodeString(k.String()[1:]) +} From 2062cbf7524bc272ff362a7cbc6af6805b219932 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 7 Oct 2016 11:14:45 -0700 Subject: [PATCH 1553/3817] cid: integrate cid into bitswap and blockstores License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-chunker@3381102e219ffa6f90608070585ef201bee9b872 --- chunker/rabin_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index a6e08f268..366d44fb8 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "github.com/ipfs/go-ipfs/blocks" - "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" "io" "testing" @@ -39,10 +38,10 @@ func TestRabinChunking(t *testing.T) { } } -func chunkData(t *testing.T, data []byte) map[key.Key]blocks.Block { +func chunkData(t *testing.T, data []byte) map[string]blocks.Block { r := NewRabin(bytes.NewReader(data), 1024*256) - blkmap := make(map[key.Key]blocks.Block) + blkmap := make(map[string]blocks.Block) for { blk, err := r.NextBytes() @@ -54,7 +53,7 @@ func chunkData(t *testing.T, data []byte) map[key.Key]blocks.Block { } b := blocks.NewBlock(blk) - blkmap[b.Key()] = b + blkmap[b.Cid().KeyString()] = b } return blkmap From 07a4c75508eefab06b4f41518c848b4c6289a89f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Oct 2016 17:59:41 -0700 Subject: [PATCH 1554/3817] bitswap: protocol extension to handle cids This change adds the /ipfs/bitswap/1.1.0 protocol. The new protocol adds a 'payload' field to the protobuf message and deprecates the existing 'blocks' field. The 'payload' field is an array of pairs of cid prefixes and block data. The cid prefixes are used to ensure the correct codecs and hash functions are used to handle the block on the receiving end. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@a3c900403cf58c7b8b95cfd7161081e406e17df6 --- ipld/merkledag/coding.go | 2 +- ipld/merkledag/merkledag.go | 2 +- ipld/merkledag/merkledag_test.go | 2 +- ipld/merkledag/node.go | 2 +- ipld/merkledag/utils/diff.go | 2 +- ipld/merkledag/utils/utils_test.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index eab7de058..3f68dd7f1 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -6,8 +6,8 @@ import ( pb "github.com/ipfs/go-ipfs/merkledag/pb" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index b9431522b..3435cb994 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -12,7 +12,7 @@ import ( offline "github.com/ipfs/go-ipfs/exchange/offline" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) var log = logging.Logger("merkledag") diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 006c8b5ca..0b4a1bbd0 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -22,7 +22,7 @@ import ( key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" "context" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 693e06b4e..91b9be641 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -5,9 +5,9 @@ import ( "context" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) var ErrLinkNotFound = fmt.Errorf("no link by that name") diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index e1f41a159..275600443 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -8,7 +8,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" context "context" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) const ( diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 6310b8939..bf11cb8ee 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" context "context" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) func TestAddLink(t *testing.T) { From d7414dea9064f0573de97652d85d9b9d78b5a089 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Oct 2016 17:59:41 -0700 Subject: [PATCH 1555/3817] bitswap: protocol extension to handle cids This change adds the /ipfs/bitswap/1.1.0 protocol. The new protocol adds a 'payload' field to the protobuf message and deprecates the existing 'blocks' field. The 'payload' field is an array of pairs of cid prefixes and block data. The cid prefixes are used to ensure the correct codecs and hash functions are used to handle the block on the receiving end. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@c16035d36a429a67169d51ad53cf78cb1b108828 --- blockservice/blockservice.go | 2 +- blockservice/test/blocks_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 3fb47aa0b..7986f6168 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -13,7 +13,7 @@ import ( context "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) var log = logging.Logger("blockservice") diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 005207763..bda0427b0 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -12,7 +12,7 @@ import ( . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" From 17ab9c5e72681fe1f06d64c015c4d7116c95c761 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Oct 2016 17:59:41 -0700 Subject: [PATCH 1556/3817] bitswap: protocol extension to handle cids This change adds the /ipfs/bitswap/1.1.0 protocol. The new protocol adds a 'payload' field to the protobuf message and deprecates the existing 'blocks' field. The 'payload' field is an array of pairs of cid prefixes and block data. The cid prefixes are used to ensure the correct codecs and hash functions are used to handle the block on the receiving end. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@ea5a3e9397f0b829293e1869df2455f1f4c46682 --- mfs/mfs_test.go | 2 +- mfs/repub_test.go | 2 +- mfs/system.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 269c24c16..70e96c200 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -24,7 +24,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" "context" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 2e9c49df5..b88604bc0 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -7,7 +7,7 @@ import ( ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" "context" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index f7e31d6d6..8f10a93c7 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -19,7 +19,7 @@ import ( context "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) var ErrNotExist = errors.New("no such rootfs") From ab1cad5b2e53694c8a1469c170014bf303dfe5ba Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Oct 2016 17:59:41 -0700 Subject: [PATCH 1557/3817] bitswap: protocol extension to handle cids This change adds the /ipfs/bitswap/1.1.0 protocol. The new protocol adds a 'payload' field to the protobuf message and deprecates the existing 'blocks' field. The 'payload' field is an array of pairs of cid prefixes and block data. The cid prefixes are used to ensure the correct codecs and hash functions are used to handle the block on the receiving end. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@8f620a4482b1120290d0cdacd80a5113ff0a1479 --- path/path.go | 2 +- path/resolver.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/path/path.go b/path/path.go index 847685f86..3713259c4 100644 --- a/path/path.go +++ b/path/path.go @@ -5,7 +5,7 @@ import ( "path" "strings" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) // ErrBadPath is returned when a given path is incorrectly formatted diff --git a/path/resolver.go b/path/resolver.go index 8ff4cf077..fb9cbf37e 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -11,7 +11,7 @@ import ( merkledag "github.com/ipfs/go-ipfs/merkledag" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) var log = logging.Logger("path") From 3585982816dedc180f0a50f07dea80487ada8b25 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Oct 2016 17:59:41 -0700 Subject: [PATCH 1558/3817] bitswap: protocol extension to handle cids This change adds the /ipfs/bitswap/1.1.0 protocol. The new protocol adds a 'payload' field to the protobuf message and deprecates the existing 'blocks' field. The 'payload' field is an array of pairs of cid prefixes and block data. The cid prefixes are used to ensure the correct codecs and hash functions are used to handle the block on the receiving end. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@02f1302faa95952c87a2d34cddb849d5008ab378 --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 2 +- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 414e061a7..5f62eb0b8 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,7 +8,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 6e73929a6..ca36a5588 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -13,7 +13,7 @@ import ( context "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 01cbeb79e..911d0e88a 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -10,7 +10,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" context "context" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 9ed155990..11d56188d 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -12,9 +12,9 @@ import ( "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 8c60633b4..e17906f6f 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -9,7 +9,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) func ignoreCids(_ *cid.Cid) {} From 9cef2ad2b58e6e84f14c763b9546c5dd6ef5bfa5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Oct 2016 17:59:41 -0700 Subject: [PATCH 1559/3817] bitswap: protocol extension to handle cids This change adds the /ipfs/bitswap/1.1.0 protocol. The new protocol adds a 'payload' field to the protobuf message and deprecates the existing 'blocks' field. The 'payload' field is an array of pairs of cid prefixes and block data. The cid prefixes are used to ensure the correct codecs and hash functions are used to handle the block on the receiving end. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@017c1bb3675e6c9e1a79140feb50cc907ca444c4 --- unixfs/io/dirbuilder.go | 2 +- unixfs/mod/dagmodifier.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index ca424e28b..967e22c4b 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -5,7 +5,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) type directoryBuilder struct { diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 7e1fd2dc8..8e3cae16a 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -15,8 +15,8 @@ import ( context "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) var ErrSeekFail = errors.New("failed to seek properly") From 8f846ca1ff2113d63fa4360df928a4e9f68f4752 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Oct 2016 17:59:41 -0700 Subject: [PATCH 1560/3817] bitswap: protocol extension to handle cids This change adds the /ipfs/bitswap/1.1.0 protocol. The new protocol adds a 'payload' field to the protobuf message and deprecates the existing 'blocks' field. The 'payload' field is an array of pairs of cid prefixes and block data. The cid prefixes are used to ensure the correct codecs and hash functions are used to handle the block on the receiving end. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@52e7a8b635192c2346577d76ce1067ab401ea89f --- blockstore/arc_cache.go | 2 +- blockstore/arc_cache_test.go | 2 +- blockstore/blockstore.go | 2 +- blockstore/blockstore_test.go | 2 +- blockstore/bloom_cache.go | 2 +- blockstore/util/remove.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 42d798a4e..03fa3fe0c 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -7,7 +7,7 @@ import ( "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 384cca270..42d388a16 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/blocks" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 7d4d10de1..dfa35ec41 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -12,7 +12,7 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dsns "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/namespace" dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 98882d479..a5ecefd44 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -9,7 +9,7 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index e78a4211c..7f6066ace 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -8,7 +8,7 @@ import ( "github.com/ipfs/go-ipfs/blocks" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" bloom "gx/ipfs/QmeiMCBkYHxkDkDfnDadzz4YxY5ruL5Pj499essE4vRsGM/bbloom" ) diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 3afc92d45..b3fd7501e 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -6,7 +6,7 @@ import ( bs "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/pin" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) From f4ca66fa52282192e47346ce6b41dee0d86ecd54 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Oct 2016 17:59:41 -0700 Subject: [PATCH 1561/3817] bitswap: protocol extension to handle cids This change adds the /ipfs/bitswap/1.1.0 protocol. The new protocol adds a 'payload' field to the protobuf message and deprecates the existing 'blocks' field. The 'payload' field is an array of pairs of cid prefixes and block data. The cid prefixes are used to ensure the correct codecs and hash functions are used to handle the block on the receiving end. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-interface@fac6d91cdf1c28c38b6e030c3d2d8c59afd5314d --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index a2b3c760b..24ed382b3 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -7,7 +7,7 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) // Any type that implements exchange.Interface may be used as an IPFS block From f7025d274f4c9301de52bf0244755b479d3385ca Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Oct 2016 17:59:41 -0700 Subject: [PATCH 1562/3817] bitswap: protocol extension to handle cids This change adds the /ipfs/bitswap/1.1.0 protocol. The new protocol adds a 'payload' field to the protobuf message and deprecates the existing 'blocks' field. The 'payload' field is an array of pairs of cid prefixes and block data. The cid prefixes are used to ensure the correct codecs and hash functions are used to handle the block on the receiving end. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@d1ba51d21d2f381055b0fe9cb1e2188e3b67092a --- routing/mock/centralized_client.go | 4 ++-- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 4 ++-- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 4 ++-- routing/supernode/client.go | 6 +++--- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 57a130150..22b9386ca 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,12 +8,12 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" "github.com/ipfs/go-ipfs/thirdparty/testutil" + routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ma "gx/ipfs/QmUAQaWbKxGCUTuoQVvvicbQNZ9APF5pDGWyAZSe93AtKH/go-multiaddr" - routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dhtpb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 49c681eda..4dff16cfe 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -8,8 +8,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index a29ec12ff..ade8304a4 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,8 +8,8 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 3f090abe2..e71635ab5 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,7 +3,7 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - dht "gx/ipfs/QmRDMP3Y9E6hZtJwcFii8F6RTUSDn67Hi2o5VFTBXNRioo/go-libp2p-kad-dht" + dht "gx/ipfs/QmWHiyk5y2EKgxHogFJ4Zt1xTqKeVsBc4zcBke8ie9C2Bn/go-libp2p-kad-dht" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" mocknet "gx/ipfs/QmcRa2qn6iCmap9bjp8jAwkvYAq13AUfxdY3rrYiaJbLum/go-libp2p/p2p/net/mock" diff --git a/routing/mock/interface.go b/routing/mock/interface.go index e5d0e60f4..5296b529f 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,9 +10,9 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" + routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 8fdebcc66..00a7cfaee 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,10 +6,10 @@ import ( repo "github.com/ipfs/go-ipfs/repo" + routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" p2phost "gx/ipfs/QmdML3R42PRSwnt46jSuEts9bHSqLctVYEjJqMR3UYV8ki/go-libp2p-host" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 398a4d1b9..49ac6ec03 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,11 +7,11 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 79b058d0a..3f7248919 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,13 +8,13 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - dhtpb "gx/ipfs/QmRDMP3Y9E6hZtJwcFii8F6RTUSDn67Hi2o5VFTBXNRioo/go-libp2p-kad-dht/pb" + routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" - routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" + dhtpb "gx/ipfs/QmWHiyk5y2EKgxHogFJ4Zt1xTqKeVsBc4zcBke8ie9C2Bn/go-libp2p-kad-dht/pb" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" "gx/ipfs/QmdML3R42PRSwnt46jSuEts9bHSqLctVYEjJqMR3UYV8ki/go-libp2p-host" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 8a6e5230f..ea9c0b1b0 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,7 +2,7 @@ package proxy import ( context "context" - dhtpb "gx/ipfs/QmRDMP3Y9E6hZtJwcFii8F6RTUSDn67Hi2o5VFTBXNRioo/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmWHiyk5y2EKgxHogFJ4Zt1xTqKeVsBc4zcBke8ie9C2Bn/go-libp2p-kad-dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" inet "gx/ipfs/QmdXimY9QHaasZmw6hWojWnCJvfgxETjZQfg9g6ZrA9wMX/go-libp2p-net" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index f7d2b80ed..15f1a71a0 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,10 +4,10 @@ import ( "context" "errors" - dhtpb "gx/ipfs/QmRDMP3Y9E6hZtJwcFii8F6RTUSDn67Hi2o5VFTBXNRioo/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" kbucket "gx/ipfs/QmUKePKcUEXwdvJENZJ6z8mJjPaxLsDZ3V9CZjPPtyawPm/go-libp2p-kbucket" + dhtpb "gx/ipfs/QmWHiyk5y2EKgxHogFJ4Zt1xTqKeVsBc4zcBke8ie9C2Bn/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" host "gx/ipfs/QmdML3R42PRSwnt46jSuEts9bHSqLctVYEjJqMR3UYV8ki/go-libp2p-host" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 3eabaa415..c6f6daf4c 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -7,7 +7,7 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" context "context" - dhtpb "gx/ipfs/QmRDMP3Y9E6hZtJwcFii8F6RTUSDn67Hi2o5VFTBXNRioo/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmWHiyk5y2EKgxHogFJ4Zt1xTqKeVsBc4zcBke8ie9C2Bn/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 36f8b53b3..a7a315eae 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,7 +3,7 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmRDMP3Y9E6hZtJwcFii8F6RTUSDn67Hi2o5VFTBXNRioo/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmWHiyk5y2EKgxHogFJ4Zt1xTqKeVsBc4zcBke8ie9C2Bn/go-libp2p-kad-dht/pb" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) From 969dfecab87f6718275b027f1f65ce9fa7005bf9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Oct 2016 17:59:41 -0700 Subject: [PATCH 1563/3817] bitswap: protocol extension to handle cids This change adds the /ipfs/bitswap/1.1.0 protocol. The new protocol adds a 'payload' field to the protobuf message and deprecates the existing 'blocks' field. The 'payload' field is an array of pairs of cid prefixes and block data. The cid prefixes are used to ensure the correct codecs and hash functions are used to handle the block on the receiving end. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@42f55cfe19fb55877a85400ef1a257730b16630a --- namesys/namesys.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/routing.go | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 7f8d298c2..4d65feea0 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,7 +6,7 @@ import ( context "context" path "github.com/ipfs/go-ipfs/path" - routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" + routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index b48e742b8..8909f1676 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -14,7 +14,7 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ft "github.com/ipfs/go-ipfs/unixfs" - routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" + routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 41c0a04ee..94a9e1ce3 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,10 +11,10 @@ import ( path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" diff --git a/namesys/routing.go b/namesys/routing.go index 3287d4940..2cf6e0d8e 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -12,11 +12,11 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - routing "gx/ipfs/QmXKuGUzLcgoQvp8M6ZEJzupWUNmx8NoqXEbYLMDjL4rjj/go-libp2p-routing" + routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) From 51a64b98d7922d032f0ddb1708d9f7df428e966d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 8 Oct 2016 17:59:41 -0700 Subject: [PATCH 1564/3817] bitswap: protocol extension to handle cids This change adds the /ipfs/bitswap/1.1.0 protocol. The new protocol adds a 'payload' field to the protobuf message and deprecates the existing 'blocks' field. The 'payload' field is an array of pairs of cid prefixes and block data. The cid prefixes are used to ensure the correct codecs and hash functions are used to handle the block on the receiving end. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@71bcf9d46ec4d93052d858ac65321a8c8a289247 --- exchange/offline/offline.go | 2 +- exchange/offline/offline_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 7013d10a0..ba3fa0017 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -9,7 +9,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 80fb6bbed..4befe796d 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -8,7 +8,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ds_sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" From 51a27084816fabdf43b588e0073eacb8b5decba1 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 5 Oct 2016 23:54:16 -0400 Subject: [PATCH 1565/3817] Make BlockService an interface. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-blockservice@67829690c0e601108c6c21515b5350046432fe3a --- blockservice/blockservice.go | 72 ++++++++++++++++++++++-------------- blockservice/test/mock.go | 4 +- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 3fb47aa0b..d2f068acc 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -23,34 +23,52 @@ var ErrNotFound = errors.New("blockservice: key not found") // BlockService is a hybrid block datastore. It stores data in a local // datastore and may retrieve data from a remote Exchange. // It uses an internal `datastore.Datastore` instance to store values. -type BlockService struct { - // TODO don't expose underlying impl details - Blockstore blockstore.Blockstore - Exchange exchange.Interface +type BlockService interface { + Blockstore() blockstore.Blockstore + Exchange() exchange.Interface + AddBlock(o blocks.Block) (*cid.Cid, error) + AddBlocks(bs []blocks.Block) ([]*cid.Cid, error) + GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) + GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block + DeleteBlock(o blocks.Block) error + Close() error +} + +type blockService struct { + blockstore blockstore.Blockstore + exchange exchange.Interface } // NewBlockService creates a BlockService with given datastore instance. -func New(bs blockstore.Blockstore, rem exchange.Interface) *BlockService { +func New(bs blockstore.Blockstore, rem exchange.Interface) BlockService { if rem == nil { log.Warning("blockservice running in local (offline) mode.") } - return &BlockService{ - Blockstore: bs, - Exchange: rem, + return &blockService{ + blockstore: bs, + exchange: rem, } } +func (bs *blockService) Blockstore() blockstore.Blockstore { + return bs.blockstore +} + +func (bs *blockService) Exchange() exchange.Interface { + return bs.exchange +} + // AddBlock adds a particular block to the service, Putting it into the datastore. // TODO pass a context into this if the remote.HasBlock is going to remain here. -func (s *BlockService) AddBlock(o blocks.Block) (*cid.Cid, error) { +func (s *blockService) AddBlock(o blocks.Block) (*cid.Cid, error) { // TODO: while this is a great optimization, we should think about the // possibility of streaming writes directly to disk. If we can pass this object // all the way down to the datastore without having to 'buffer' its data, // we could implement a `WriteTo` method on it that could do a streaming write // of the content, saving us (probably) considerable memory. c := o.Cid() - has, err := s.Blockstore.Has(c) + has, err := s.blockstore.Has(c) if err != nil { return nil, err } @@ -59,22 +77,22 @@ func (s *BlockService) AddBlock(o blocks.Block) (*cid.Cid, error) { return c, nil } - err = s.Blockstore.Put(o) + err = s.blockstore.Put(o) if err != nil { return nil, err } - if err := s.Exchange.HasBlock(o); err != nil { + if err := s.exchange.HasBlock(o); err != nil { return nil, errors.New("blockservice is closed") } return c, nil } -func (s *BlockService) AddBlocks(bs []blocks.Block) ([]*cid.Cid, error) { +func (s *blockService) AddBlocks(bs []blocks.Block) ([]*cid.Cid, error) { var toput []blocks.Block for _, b := range bs { - has, err := s.Blockstore.Has(b.Cid()) + has, err := s.blockstore.Has(b.Cid()) if err != nil { return nil, err } @@ -86,14 +104,14 @@ func (s *BlockService) AddBlocks(bs []blocks.Block) ([]*cid.Cid, error) { toput = append(toput, b) } - err := s.Blockstore.PutMany(toput) + err := s.blockstore.PutMany(toput) if err != nil { return nil, err } var ks []*cid.Cid for _, o := range toput { - if err := s.Exchange.HasBlock(o); err != nil { + if err := s.exchange.HasBlock(o); err != nil { return nil, fmt.Errorf("blockservice is closed (%s)", err) } @@ -104,19 +122,19 @@ func (s *BlockService) AddBlocks(bs []blocks.Block) ([]*cid.Cid, error) { // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). -func (s *BlockService) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) { +func (s *blockService) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) { log.Debugf("BlockService GetBlock: '%s'", c) - block, err := s.Blockstore.Get(c) + block, err := s.blockstore.Get(c) if err == nil { return block, nil } - if err == blockstore.ErrNotFound && s.Exchange != nil { + if err == blockstore.ErrNotFound && s.exchange != nil { // TODO be careful checking ErrNotFound. If the underlying // implementation changes, this will break. log.Debug("Blockservice: Searching bitswap") - blk, err := s.Exchange.GetBlock(ctx, c) + blk, err := s.exchange.GetBlock(ctx, c) if err != nil { if err == blockstore.ErrNotFound { return nil, ErrNotFound @@ -137,13 +155,13 @@ func (s *BlockService) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, // GetBlocks gets a list of blocks asynchronously and returns through // the returned channel. // NB: No guarantees are made about order. -func (s *BlockService) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block { +func (s *blockService) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block { out := make(chan blocks.Block, 0) go func() { defer close(out) var misses []*cid.Cid for _, c := range ks { - hit, err := s.Blockstore.Get(c) + hit, err := s.blockstore.Get(c) if err != nil { misses = append(misses, c) continue @@ -160,7 +178,7 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan bloc return } - rblocks, err := s.Exchange.GetBlocks(ctx, misses) + rblocks, err := s.exchange.GetBlocks(ctx, misses) if err != nil { log.Debugf("Error with GetBlocks: %s", err) return @@ -178,11 +196,11 @@ func (s *BlockService) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan bloc } // DeleteBlock deletes a block in the blockservice from the datastore -func (s *BlockService) DeleteBlock(o blocks.Block) error { - return s.Blockstore.DeleteBlock(o.Cid()) +func (s *blockService) DeleteBlock(o blocks.Block) error { + return s.blockstore.DeleteBlock(o.Cid()) } -func (s *BlockService) Close() error { +func (s *blockService) Close() error { log.Debug("blockservice is shutting down...") - return s.Exchange.Close() + return s.exchange.Close() } diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index 28e3a4e99..622d1c8d6 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -9,13 +9,13 @@ import ( ) // Mocks returns |n| connected mock Blockservices -func Mocks(n int) []*BlockService { +func Mocks(n int) []BlockService { net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(0)) sg := bitswap.NewTestSessionGenerator(net) instances := sg.Instances(n) - var servs []*BlockService + var servs []BlockService for _, i := range instances { servs = append(servs, New(i.Blockstore(), i.Exchange)) } From 9150fb081b935ae63fc514a4388961a5f3afb29b Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 5 Oct 2016 23:54:16 -0400 Subject: [PATCH 1566/3817] Make BlockService an interface. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@b7b352fe1dcd2b91b9463382c077591af1893b31 --- ipld/merkledag/merkledag.go | 8 ++++---- ipld/merkledag/merkledag_test.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index b9431522b..5d2cd36dc 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -41,7 +41,7 @@ type LinkService interface { GetOfflineLinkService() LinkService } -func NewDAGService(bs *bserv.BlockService) *dagService { +func NewDAGService(bs bserv.BlockService) *dagService { return &dagService{Blocks: bs} } @@ -51,7 +51,7 @@ func NewDAGService(bs *bserv.BlockService) *dagService { // TODO: should cache Nodes that are in memory, and be // able to free some of them when vm pressure is high type dagService struct { - Blocks *bserv.BlockService + Blocks bserv.BlockService } // Add adds a node to the dagService, storing the block in the BlockService @@ -113,8 +113,8 @@ func (n *dagService) GetLinks(ctx context.Context, c *cid.Cid) ([]*Link, error) } func (n *dagService) GetOfflineLinkService() LinkService { - if n.Blocks.Exchange.IsOnline() { - bsrv := bserv.New(n.Blocks.Blockstore, offline.Exchange(n.Blocks.Blockstore)) + if n.Blocks.Exchange().IsOnline() { + bsrv := bserv.New(n.Blocks.Blockstore(), offline.Exchange(n.Blocks.Blockstore())) return NewDAGService(bsrv) } else { return n diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 006c8b5ca..bc9093532 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -237,7 +237,7 @@ func TestFetchGraph(t *testing.T) { } // create an offline dagstore and ensure all blocks were fetched - bs := bserv.New(bsis[1].Blockstore, offline.Exchange(bsis[1].Blockstore)) + bs := bserv.New(bsis[1].Blockstore(), offline.Exchange(bsis[1].Blockstore())) offline_ds := NewDAGService(bs) From c22c3c947d6bf75c9e74c4211971f5a7d1f8da44 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 6 Oct 2016 14:54:25 -0400 Subject: [PATCH 1567/3817] Create a "write through" BlockService. Create a block service where all writes are guaranteed to go though to the blockstore. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-blockservice@2645e2bdb2197a6d24ebe4b096e6a58ced8b0bc3 --- blockservice/blockservice.go | 61 +++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index d2f068acc..8ecec71f9 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -37,6 +37,9 @@ type BlockService interface { type blockService struct { blockstore blockstore.Blockstore exchange exchange.Interface + // If checkFirst is true then first check that a block doesn't + // already exist to avoid republishing the block on the exchange. + checkFirst bool } // NewBlockService creates a BlockService with given datastore instance. @@ -48,6 +51,21 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) BlockService { return &blockService{ blockstore: bs, exchange: rem, + checkFirst: true, + } +} + +// NewWriteThrough ceates a BlockService that guarantees writes will go +// through to the blockstore and are not skipped by cache checks. +func NewWriteThrough(bs blockstore.Blockstore, rem exchange.Interface) BlockService { + if rem == nil { + log.Warning("blockservice running in local (offline) mode.") + } + + return &blockService{ + blockstore: bs, + exchange: rem, + checkFirst: false, } } @@ -62,22 +80,19 @@ func (bs *blockService) Exchange() exchange.Interface { // AddBlock adds a particular block to the service, Putting it into the datastore. // TODO pass a context into this if the remote.HasBlock is going to remain here. func (s *blockService) AddBlock(o blocks.Block) (*cid.Cid, error) { - // TODO: while this is a great optimization, we should think about the - // possibility of streaming writes directly to disk. If we can pass this object - // all the way down to the datastore without having to 'buffer' its data, - // we could implement a `WriteTo` method on it that could do a streaming write - // of the content, saving us (probably) considerable memory. c := o.Cid() - has, err := s.blockstore.Has(c) - if err != nil { - return nil, err - } + if s.checkFirst { + has, err := s.blockstore.Has(c) + if err != nil { + return nil, err + } - if has { - return c, nil + if has { + return c, nil + } } - err = s.blockstore.Put(o) + err := s.blockstore.Put(o) if err != nil { return nil, err } @@ -91,17 +106,19 @@ func (s *blockService) AddBlock(o blocks.Block) (*cid.Cid, error) { func (s *blockService) AddBlocks(bs []blocks.Block) ([]*cid.Cid, error) { var toput []blocks.Block - for _, b := range bs { - has, err := s.blockstore.Has(b.Cid()) - if err != nil { - return nil, err - } - - if has { - continue + if s.checkFirst { + for _, b := range bs { + has, err := s.blockstore.Has(b.Cid()) + if err != nil { + return nil, err + } + if has { + continue + } + toput = append(toput, b) } - - toput = append(toput, b) + } else { + toput = bs; } err := s.blockstore.PutMany(toput) From 87ec46cca8096221a79e6f67e51ec4ada576edf9 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 10 Oct 2016 10:58:48 -0400 Subject: [PATCH 1568/3817] test: check if NewWriteThrough is not calling Has License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-blockservice@06c5dbf883c10633768d9cb9e4f16c5a4c50cb90 --- blockservice/blockservice.go | 2 +- blockservice/blockservice_test.go | 44 +++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 blockservice/blockservice_test.go diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 8ecec71f9..d4461b66d 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -4,6 +4,7 @@ package blockservice import ( + "context" "errors" "fmt" @@ -11,7 +12,6 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - context "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ) diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go new file mode 100644 index 000000000..82244c1cd --- /dev/null +++ b/blockservice/blockservice_test.go @@ -0,0 +1,44 @@ +package blockservice + +import ( + "testing" + + "github.com/ipfs/go-ipfs/blocks/blockstore" + butil "github.com/ipfs/go-ipfs/blocks/blocksutil" + offline "github.com/ipfs/go-ipfs/exchange/offline" + + cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" +) + +func TestWriteThroughWorks(t *testing.T) { + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := HasFailingBlockstore{ + blockstore.NewBlockstore(dstore), + t, + true, + } + exch := offline.Exchange(bstore) + bserv := NewWriteThrough(bstore, exch) + bgen := butil.NewBlockGenerator() + + bserv.AddBlock(bgen.Next()) +} + +var _ blockstore.GCBlockstore = (*HasFailingBlockstore)(nil) + +type HasFailingBlockstore struct { + blockstore.GCBlockstore + t *testing.T + Fail bool +} + +func (bs HasFailingBlockstore) Has(k *cid.Cid) (bool, error) { + if bs.Fail { + bs.t.Fatal("Has shouldn't be called") + return false, nil + } + return bs.GCBlockstore.Has(k) + +} From 7c6625a7e0fa6ebc45eb603124f69b5e5f70466d Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 10 Oct 2016 15:52:50 -0400 Subject: [PATCH 1569/3817] Change the test from being Has based to Put based License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-blockservice@865b8327a9604ac8a697d718c6d6947b36eb5b7f --- blockservice/blockservice_test.go | 43 +++++++++++++++++-------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index 82244c1cd..d87a383e5 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -3,42 +3,47 @@ package blockservice import ( "testing" + "github.com/ipfs/go-ipfs/blocks" "github.com/ipfs/go-ipfs/blocks/blockstore" butil "github.com/ipfs/go-ipfs/blocks/blocksutil" offline "github.com/ipfs/go-ipfs/exchange/offline" - cid "gx/ipfs/QmakyCk6Vnn16WEKjbkxieZmM2YLTzkFWizbmGowoYPjro/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) func TestWriteThroughWorks(t *testing.T) { - dstore := dssync.MutexWrap(ds.NewMapDatastore()) - bstore := HasFailingBlockstore{ - blockstore.NewBlockstore(dstore), - t, - true, + bstore := &PutCountingBlockstore{ + blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())), + 0, } - exch := offline.Exchange(bstore) + bstore2 := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) + exch := offline.Exchange(bstore2) bserv := NewWriteThrough(bstore, exch) bgen := butil.NewBlockGenerator() - bserv.AddBlock(bgen.Next()) + block := bgen.Next() + + t.Logf("PutCounter: %d", bstore.PutCounter) + bserv.AddBlock(block) + if bstore.PutCounter != 1 { + t.Fatalf("expected just one Put call, have: %d", bstore.PutCounter) + } + + bserv.AddBlock(block) + if bstore.PutCounter != 2 { + t.Fatal("Put should have called again, should be 2 is: %d", bstore.PutCounter) + } } -var _ blockstore.GCBlockstore = (*HasFailingBlockstore)(nil) +var _ blockstore.GCBlockstore = (*PutCountingBlockstore)(nil) -type HasFailingBlockstore struct { +type PutCountingBlockstore struct { blockstore.GCBlockstore - t *testing.T - Fail bool + PutCounter int } -func (bs HasFailingBlockstore) Has(k *cid.Cid) (bool, error) { - if bs.Fail { - bs.t.Fatal("Has shouldn't be called") - return false, nil - } - return bs.GCBlockstore.Has(k) - +func (bs *PutCountingBlockstore) Put(block blocks.Block) error { + bs.PutCounter++ + return bs.GCBlockstore.Put(block) } From 10358175cd646c1b51d2602beefce958ede23387 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 12 Oct 2016 07:00:38 -0700 Subject: [PATCH 1570/3817] run gofmt License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@15a27eb0b1e118cd10ad65692024398e9aab0cc0 --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index d4461b66d..be84bb98e 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -118,7 +118,7 @@ func (s *blockService) AddBlocks(bs []blocks.Block) ([]*cid.Cid, error) { toput = append(toput, b) } } else { - toput = bs; + toput = bs } err := s.blockstore.PutMany(toput) From 03963a2d25b8b1ae628d237743536cf0b5b93d0f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 9 Oct 2016 12:59:36 -0700 Subject: [PATCH 1571/3817] merkledag: change 'Node' to be an interface Also change existing 'Node' type to 'ProtoNode' and use that most everywhere for now. As we move forward with the integration we will try and use the Node interface in more places that we're currently using ProtoNode. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@a9cabd600077f66380d64c79f7137e37290a3144 --- mfs/dir.go | 40 +++++++++++++++++++++------------------- mfs/file.go | 6 +++--- mfs/mfs_test.go | 22 +++++++++++----------- mfs/ops.go | 2 +- mfs/system.go | 10 +++++----- 5 files changed, 41 insertions(+), 39 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 8bc486cb7..3a1c7be8e 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -28,7 +28,7 @@ type Directory struct { files map[string]*File lock sync.Mutex - node *dag.Node + node *dag.ProtoNode ctx context.Context modTime time.Time @@ -36,7 +36,7 @@ type Directory struct { name string } -func NewDirectory(ctx context.Context, name string, node *dag.Node, parent childCloser, dserv dag.DAGService) *Directory { +func NewDirectory(ctx context.Context, name string, node *dag.ProtoNode, parent childCloser, dserv dag.DAGService) *Directory { return &Directory{ dserv: dserv, ctx: ctx, @@ -51,7 +51,7 @@ func NewDirectory(ctx context.Context, name string, node *dag.Node, parent child // closeChild updates the child by the given name to the dag node 'nd' // and changes its own dag node -func (d *Directory) closeChild(name string, nd *dag.Node, sync bool) error { +func (d *Directory) closeChild(name string, nd *dag.ProtoNode, sync bool) error { mynd, err := d.closeChildUpdate(name, nd, sync) if err != nil { return err @@ -64,7 +64,7 @@ func (d *Directory) closeChild(name string, nd *dag.Node, sync bool) error { } // closeChildUpdate is the portion of closeChild that needs to be locked around -func (d *Directory) closeChildUpdate(name string, nd *dag.Node, sync bool) (*dag.Node, error) { +func (d *Directory) closeChildUpdate(name string, nd *dag.ProtoNode, sync bool) (*dag.ProtoNode, error) { d.lock.Lock() defer d.lock.Unlock() @@ -79,7 +79,7 @@ func (d *Directory) closeChildUpdate(name string, nd *dag.Node, sync bool) (*dag return nil, nil } -func (d *Directory) flushCurrentNode() (*dag.Node, error) { +func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) { _, err := d.dserv.Add(d.node) if err != nil { return nil, err @@ -88,7 +88,7 @@ func (d *Directory) flushCurrentNode() (*dag.Node, error) { return d.node.Copy(), nil } -func (d *Directory) updateChild(name string, nd *dag.Node) error { +func (d *Directory) updateChild(name string, nd *dag.ProtoNode) error { err := d.node.RemoveNodeLink(name) if err != nil && err != dag.ErrNotFound { return err @@ -120,7 +120,7 @@ func (d *Directory) childNode(name string) (FSNode, error) { } // cacheNode caches a node into d.childDirs or d.files and returns the FSNode. -func (d *Directory) cacheNode(name string, nd *dag.Node) (FSNode, error) { +func (d *Directory) cacheNode(name string, nd *dag.ProtoNode) (FSNode, error) { i, err := ft.FromBytes(nd.Data()) if err != nil { return nil, err @@ -161,14 +161,16 @@ func (d *Directory) Uncache(name string) { // childFromDag searches through this directories dag node for a child link // with the given name -func (d *Directory) childFromDag(name string) (*dag.Node, error) { - for _, lnk := range d.node.Links { - if lnk.Name == name { - return lnk.GetNode(d.ctx, d.dserv) - } +func (d *Directory) childFromDag(name string) (*dag.ProtoNode, error) { + pbn, err := d.node.GetLinkedProtoNode(d.ctx, d.dserv, name) + switch err { + case nil: + return pbn, nil + case dag.ErrLinkNotFound: + return nil, os.ErrNotExist + default: + return nil, err } - - return nil, os.ErrNotExist } // childUnsync returns the child under this directory by the given name @@ -206,7 +208,7 @@ func (d *Directory) ListNames() []string { names[n] = struct{}{} } - for _, l := range d.node.Links { + for _, l := range d.node.Links() { names[l.Name] = struct{}{} } @@ -224,7 +226,7 @@ func (d *Directory) List() ([]NodeListing, error) { defer d.lock.Unlock() var out []NodeListing - for _, l := range d.node.Links { + for _, l := range d.node.Links() { child := NodeListing{} child.Name = l.Name @@ -270,7 +272,7 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { } } - ndir := new(dag.Node) + ndir := new(dag.ProtoNode) ndir.SetData(ft.FolderPBData()) _, err = d.dserv.Add(ndir) @@ -321,7 +323,7 @@ func (d *Directory) Flush() error { } // AddChild adds the node 'nd' under this directory giving it the name 'name' -func (d *Directory) AddChild(name string, nd *dag.Node) error { +func (d *Directory) AddChild(name string, nd *dag.ProtoNode) error { d.lock.Lock() defer d.lock.Unlock() @@ -382,7 +384,7 @@ func (d *Directory) Path() string { return out } -func (d *Directory) GetNode() (*dag.Node, error) { +func (d *Directory) GetNode() (*dag.ProtoNode, error) { d.lock.Lock() defer d.lock.Unlock() diff --git a/mfs/file.go b/mfs/file.go index bbd7b48c2..373a9dd1d 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -19,12 +19,12 @@ type File struct { desclock sync.RWMutex dserv dag.DAGService - node *dag.Node + node *dag.ProtoNode nodelk sync.Mutex } // NewFile returns a NewFile object with the given parameters -func NewFile(name string, node *dag.Node, parent childCloser, dserv dag.DAGService) (*File, error) { +func NewFile(name string, node *dag.ProtoNode, parent childCloser, dserv dag.DAGService) (*File, error) { return &File{ dserv: dserv, parent: parent, @@ -94,7 +94,7 @@ func (fi *File) Size() (int64, error) { } // GetNode returns the dag node associated with this file -func (fi *File) GetNode() (*dag.Node, error) { +func (fi *File) GetNode() (*dag.ProtoNode, error) { fi.nodelk.Lock() defer fi.nodelk.Unlock() return fi.node, nil diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 70e96c200..f9c79769c 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -30,7 +30,7 @@ import ( dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) -func emptyDirNode() *dag.Node { +func emptyDirNode() *dag.ProtoNode { return dag.NodeWithData(ft.FolderPBData()) } @@ -41,12 +41,12 @@ func getDagserv(t *testing.T) dag.DAGService { return dag.NewDAGService(blockserv) } -func getRandFile(t *testing.T, ds dag.DAGService, size int64) *dag.Node { +func getRandFile(t *testing.T, ds dag.DAGService, size int64) *dag.ProtoNode { r := io.LimitReader(u.NewTimeSeededRand(), size) return fileNodeFromReader(t, ds, r) } -func fileNodeFromReader(t *testing.T, ds dag.DAGService, r io.Reader) *dag.Node { +func fileNodeFromReader(t *testing.T, ds dag.DAGService, r io.Reader) *dag.ProtoNode { nd, err := importer.BuildDagFromReader(ds, chunk.DefaultSplitter(r)) if err != nil { t.Fatal(err) @@ -124,7 +124,7 @@ func compStrArrs(a, b []string) bool { return true } -func assertFileAtPath(ds dag.DAGService, root *Directory, exp *dag.Node, pth string) error { +func assertFileAtPath(ds dag.DAGService, root *Directory, exp *dag.ProtoNode, pth string) error { parts := path.SplitList(pth) cur := root for i, d := range parts[:len(parts)-1] { @@ -173,7 +173,7 @@ func assertFileAtPath(ds dag.DAGService, root *Directory, exp *dag.Node, pth str return nil } -func catNode(ds dag.DAGService, nd *dag.Node) ([]byte, error) { +func catNode(ds dag.DAGService, nd *dag.ProtoNode) ([]byte, error) { r, err := uio.NewDagReader(context.TODO(), nd, ds) if err != nil { return nil, err @@ -280,7 +280,7 @@ func TestDirectoryLoadFromDag(t *testing.T) { t.Fatal(err) } - fihash := nd.Multihash() + fihash := nd.Cid() dir := emptyDirNode() _, err = ds.Add(dir) @@ -288,19 +288,19 @@ func TestDirectoryLoadFromDag(t *testing.T) { t.Fatal(err) } - dirhash := dir.Multihash() + dirhash := dir.Cid() top := emptyDirNode() - top.Links = []*dag.Link{ + top.SetLinks([]*dag.Link{ &dag.Link{ Name: "a", - Hash: fihash, + Cid: fihash, }, &dag.Link{ Name: "b", - Hash: dirhash, + Cid: dirhash, }, - } + }) err = rootdir.AddChild("foo", top) if err != nil { diff --git a/mfs/ops.go b/mfs/ops.go index 94c6c30df..6464d8404 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -87,7 +87,7 @@ func lookupDir(r *Root, path string) (*Directory, error) { } // PutNode inserts 'nd' at 'path' in the given mfs -func PutNode(r *Root, path string, nd *dag.Node) error { +func PutNode(r *Root, path string, nd *dag.ProtoNode) error { dirp, filename := gopath.Split(path) if filename == "" { return fmt.Errorf("cannot create file with empty name") diff --git a/mfs/system.go b/mfs/system.go index 8f10a93c7..2a69a1878 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -29,7 +29,7 @@ var log = logging.Logger("mfs") var ErrIsDirectory = errors.New("error: is a directory") type childCloser interface { - closeChild(string, *dag.Node, bool) error + closeChild(string, *dag.ProtoNode, bool) error } type NodeType int @@ -41,7 +41,7 @@ const ( // FSNode represents any node (directory, root, or file) in the mfs filesystem type FSNode interface { - GetNode() (*dag.Node, error) + GetNode() (*dag.ProtoNode, error) Flush() error Type() NodeType } @@ -49,7 +49,7 @@ type FSNode interface { // Root represents the root of a filesystem tree type Root struct { // node is the merkledag root - node *dag.Node + node *dag.ProtoNode // val represents the node. It can either be a File or a Directory val FSNode @@ -64,7 +64,7 @@ type Root struct { type PubFunc func(context.Context, *cid.Cid) error // newRoot creates a new Root and starts up a republisher routine for it -func NewRoot(parent context.Context, ds dag.DAGService, node *dag.Node, pf PubFunc) (*Root, error) { +func NewRoot(parent context.Context, ds dag.DAGService, node *dag.ProtoNode, pf PubFunc) (*Root, error) { var repub *Republisher if pf != nil { @@ -118,7 +118,7 @@ func (kr *Root) Flush() error { // closeChild implements the childCloser interface, and signals to the publisher that // there are changes ready to be published -func (kr *Root) closeChild(name string, nd *dag.Node, sync bool) error { +func (kr *Root) closeChild(name string, nd *dag.ProtoNode, sync bool) error { c, err := kr.dserv.Add(nd) if err != nil { return err From 71b0580c80665f95fe2757e54ddd1d90790e1d96 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 9 Oct 2016 12:59:36 -0700 Subject: [PATCH 1572/3817] merkledag: change 'Node' to be an interface Also change existing 'Node' type to 'ProtoNode' and use that most everywhere for now. As we move forward with the integration we will try and use the Node interface in more places that we're currently using ProtoNode. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@3e624eec9e7df2ebe74ed15284983d592f5ebee8 --- ipld/merkledag/coding.go | 35 +++---- ipld/merkledag/merkledag.go | 77 +++++++------- ipld/merkledag/merkledag_test.go | 35 ++++--- ipld/merkledag/node.go | 127 +++++++++++++++-------- ipld/merkledag/node_test.go | 62 ++++++----- ipld/merkledag/traverse/traverse.go | 20 ++-- ipld/merkledag/traverse/traverse_test.go | 22 ++-- ipld/merkledag/utils/diff.go | 47 ++++++--- ipld/merkledag/utils/utils.go | 35 ++++--- ipld/merkledag/utils/utils_test.go | 14 +-- 10 files changed, 274 insertions(+), 200 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 3f68dd7f1..1d1badd3b 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -7,7 +7,6 @@ import ( pb "github.com/ipfs/go-ipfs/merkledag/pb" cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" - mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) @@ -16,23 +15,23 @@ import ( // unmarshal decodes raw data into a *Node instance. // The conversion uses an intermediate PBNode. -func (n *Node) unmarshal(encoded []byte) error { +func (n *ProtoNode) unmarshal(encoded []byte) error { var pbn pb.PBNode if err := pbn.Unmarshal(encoded); err != nil { return fmt.Errorf("Unmarshal failed. %v", err) } pbnl := pbn.GetLinks() - n.Links = make([]*Link, len(pbnl)) + n.links = make([]*Link, len(pbnl)) for i, l := range pbnl { - n.Links[i] = &Link{Name: l.GetName(), Size: l.GetTsize()} - h, err := mh.Cast(l.GetHash()) + n.links[i] = &Link{Name: l.GetName(), Size: l.GetTsize()} + c, err := cid.Cast(l.GetHash()) if err != nil { return fmt.Errorf("Link hash #%d is not valid multihash. %v", i, err) } - n.Links[i].Hash = h + n.links[i].Cid = c } - sort.Stable(LinkSlice(n.Links)) // keep links sorted + sort.Stable(LinkSlice(n.links)) // keep links sorted n.data = pbn.GetData() n.encoded = encoded @@ -41,7 +40,7 @@ func (n *Node) unmarshal(encoded []byte) error { // Marshal encodes a *Node instance into a new byte slice. // The conversion uses an intermediate PBNode. -func (n *Node) Marshal() ([]byte, error) { +func (n *ProtoNode) Marshal() ([]byte, error) { pbn := n.getPBNode() data, err := pbn.Marshal() if err != nil { @@ -50,18 +49,18 @@ func (n *Node) Marshal() ([]byte, error) { return data, nil } -func (n *Node) getPBNode() *pb.PBNode { +func (n *ProtoNode) getPBNode() *pb.PBNode { pbn := &pb.PBNode{} - if len(n.Links) > 0 { - pbn.Links = make([]*pb.PBLink, len(n.Links)) + if len(n.links) > 0 { + pbn.Links = make([]*pb.PBLink, len(n.links)) } - sort.Stable(LinkSlice(n.Links)) // keep links sorted - for i, l := range n.Links { + sort.Stable(LinkSlice(n.links)) // keep links sorted + for i, l := range n.links { pbn.Links[i] = &pb.PBLink{} pbn.Links[i].Name = &l.Name pbn.Links[i].Tsize = &l.Size - pbn.Links[i].Hash = []byte(l.Hash) + pbn.Links[i].Hash = l.Cid.Bytes() } if len(n.data) > 0 { @@ -72,8 +71,8 @@ func (n *Node) getPBNode() *pb.PBNode { // EncodeProtobuf returns the encoded raw data version of a Node instance. // It may use a cached encoded version, unless the force flag is given. -func (n *Node) EncodeProtobuf(force bool) ([]byte, error) { - sort.Stable(LinkSlice(n.Links)) // keep links sorted +func (n *ProtoNode) EncodeProtobuf(force bool) ([]byte, error) { + sort.Stable(LinkSlice(n.links)) // keep links sorted if n.encoded == nil || force { n.cached = nil var err error @@ -91,8 +90,8 @@ func (n *Node) EncodeProtobuf(force bool) ([]byte, error) { } // Decoded decodes raw data and returns a new Node instance. -func DecodeProtobuf(encoded []byte) (*Node, error) { - n := new(Node) +func DecodeProtobuf(encoded []byte) (*ProtoNode, error) { + n := new(ProtoNode) err := n.unmarshal(encoded) if err != nil { return nil, fmt.Errorf("incorrectly formatted merkledag node: %s", err) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index fa462db0a..16125a1fd 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -20,9 +20,9 @@ var ErrNotFound = fmt.Errorf("merkledag: not found") // DAGService is an IPFS Merkle DAG service. type DAGService interface { - Add(*Node) (*cid.Cid, error) - Get(context.Context, *cid.Cid) (*Node, error) - Remove(*Node) error + Add(Node) (*cid.Cid, error) + Get(context.Context, *cid.Cid) (Node, error) + Remove(Node) error // GetDAG returns, in order, all the single leve child // nodes of the passed in node. @@ -45,6 +45,19 @@ func NewDAGService(bs bserv.BlockService) *dagService { return &dagService{Blocks: bs} } +type Node interface { + Resolve(path []string) (*Link, []string, error) + Links() []*Link + Tree() []string + + Stat() (*NodeStat, error) + Size() (uint64, error) + Cid() *cid.Cid + Loggable() map[string]interface{} + RawData() []byte + String() string +} + // dagService is an IPFS Merkle DAG service. // - the root is virtual (like a forest) // - stores nodes' data in a BlockService @@ -55,7 +68,7 @@ type dagService struct { } // Add adds a node to the dagService, storing the block in the BlockService -func (n *dagService) Add(nd *Node) (*cid.Cid, error) { +func (n *dagService) Add(nd Node) (*cid.Cid, error) { if n == nil { // FIXME remove this assertion. protect with constructor invariant return nil, fmt.Errorf("dagService is nil") } @@ -68,7 +81,7 @@ func (n *dagService) Batch() *Batch { } // Get retrieves a node from the dagService, fetching the block in the BlockService -func (n *dagService) Get(ctx context.Context, c *cid.Cid) (*Node, error) { +func (n *dagService) Get(ctx context.Context, c *cid.Cid) (Node, error) { if n == nil { return nil, fmt.Errorf("dagService is nil") } @@ -84,7 +97,7 @@ func (n *dagService) Get(ctx context.Context, c *cid.Cid) (*Node, error) { return nil, fmt.Errorf("Failed to get block for %s: %v", c, err) } - var res *Node + var res Node switch c.Type() { case cid.Protobuf: out, err := DecodeProtobuf(b.RawData()) @@ -94,13 +107,12 @@ func (n *dagService) Get(ctx context.Context, c *cid.Cid) (*Node, error) { } return nil, fmt.Errorf("Failed to decode Protocol Buffers: %v", err) } + out.cached = c res = out default: return nil, fmt.Errorf("unrecognized formatting type") } - res.cached = c - return res, nil } @@ -109,7 +121,7 @@ func (n *dagService) GetLinks(ctx context.Context, c *cid.Cid) ([]*Link, error) if err != nil { return nil, err } - return node.Links, nil + return node.Links(), nil } func (n *dagService) GetOfflineLinkService() LinkService { @@ -121,7 +133,7 @@ func (n *dagService) GetOfflineLinkService() LinkService { } } -func (n *dagService) Remove(nd *Node) error { +func (n *dagService) Remove(nd Node) error { return n.Blocks.DeleteBlock(nd) } @@ -143,7 +155,7 @@ func FindLinks(links []*cid.Cid, c *cid.Cid, start int) []int { } type NodeOption struct { - Node *Node + Node Node Err error } @@ -166,7 +178,7 @@ func (ds *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *Node c := b.Cid() - var nd *Node + var nd Node switch c.Type() { case cid.Protobuf: decnd, err := DecodeProtobuf(b.RawData()) @@ -174,7 +186,7 @@ func (ds *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *Node out <- &NodeOption{Err: err} return } - decnd.cached = cid.NewCidV0(b.Multihash()) + decnd.cached = b.Cid() nd = decnd default: out <- &NodeOption{Err: fmt.Errorf("unrecognized object type: %s", c.Type())} @@ -197,10 +209,10 @@ func (ds *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *Node // GetDAG will fill out all of the links of the given Node. // It returns a channel of nodes, which the caller can receive // all the child nodes of 'root' on, in proper order. -func GetDAG(ctx context.Context, ds DAGService, root *Node) []NodeGetter { +func GetDAG(ctx context.Context, ds DAGService, root Node) []NodeGetter { var cids []*cid.Cid - for _, lnk := range root.Links { - cids = append(cids, cid.NewCidV0(lnk.Hash)) + for _, lnk := range root.Links() { + cids = append(cids, lnk.Cid) } return GetNodes(ctx, ds, cids) @@ -269,16 +281,16 @@ func dedupeKeys(cids []*cid.Cid) []*cid.Cid { func newNodePromise(ctx context.Context) NodeGetter { return &nodePromise{ - recv: make(chan *Node, 1), + recv: make(chan Node, 1), ctx: ctx, err: make(chan error, 1), } } type nodePromise struct { - cache *Node + cache Node clk sync.Mutex - recv chan *Node + recv chan Node ctx context.Context err chan error } @@ -288,9 +300,9 @@ type nodePromise struct { // from its internal channels, subsequent calls will return the // cached node. type NodeGetter interface { - Get(context.Context) (*Node, error) + Get(context.Context) (Node, error) Fail(err error) - Send(*Node) + Send(Node) } func (np *nodePromise) Fail(err error) { @@ -306,7 +318,7 @@ func (np *nodePromise) Fail(err error) { np.err <- err } -func (np *nodePromise) Send(nd *Node) { +func (np *nodePromise) Send(nd Node) { var already bool np.clk.Lock() if np.cache != nil { @@ -322,7 +334,7 @@ func (np *nodePromise) Send(nd *Node) { np.recv <- nd } -func (np *nodePromise) Get(ctx context.Context) (*Node, error) { +func (np *nodePromise) Get(ctx context.Context) (Node, error) { np.clk.Lock() c := np.cache np.clk.Unlock() @@ -350,14 +362,9 @@ type Batch struct { MaxSize int } -func (t *Batch) Add(nd *Node) (*cid.Cid, error) { - d, err := nd.EncodeProtobuf(false) - if err != nil { - return nil, err - } - +func (t *Batch) Add(nd Node) (*cid.Cid, error) { t.blocks = append(t.blocks, nd) - t.size += len(d) + t.size += len(nd.RawData()) if t.size > t.MaxSize { return nd.Cid(), t.Commit() } @@ -371,10 +378,6 @@ func (t *Batch) Commit() error { return err } -func legacyCidFromLink(lnk *Link) *cid.Cid { - return cid.NewCidV0(lnk.Hash) -} - // EnumerateChildren will walk the dag below the given root node and add all // unseen children to the passed in set. // TODO: parallelize to avoid disk latency perf hits? @@ -386,7 +389,7 @@ func EnumerateChildren(ctx context.Context, ds LinkService, root *cid.Cid, visit return err } for _, lnk := range links { - c := legacyCidFromLink(lnk) + c := lnk.Cid if visit(c) { err = EnumerateChildren(ctx, ds, c, visit, bestEffort) if err != nil { @@ -432,8 +435,8 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, c *cid.Cid, visi live-- var cids []*cid.Cid - for _, lnk := range nd.Links { - c := legacyCidFromLink(lnk) + for _, lnk := range nd.Links() { + c := lnk.Cid if visit(c) { live++ cids = append(cids, c) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index e65f4e417..9ade523a7 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -38,13 +38,13 @@ func TestNode(t *testing.T) { t.Error(err) } - printn := func(name string, n *Node) { + printn := func(name string, n *ProtoNode) { fmt.Println(">", name) fmt.Println("data:", string(n.Data())) fmt.Println("links:") - for _, l := range n.Links { - fmt.Println("-", l.Name, l.Size, l.Hash) + for _, l := range n.Links() { + fmt.Println("-", l.Name, l.Size, l.Cid) } e, err := n.EncodeProtobuf(false) @@ -70,7 +70,7 @@ func TestNode(t *testing.T) { printn("beep boop", n3) } -func SubtestNodeStat(t *testing.T, n *Node) { +func SubtestNodeStat(t *testing.T, n *ProtoNode) { enc, err := n.EncodeProtobuf(true) if err != nil { t.Error("n.EncodeProtobuf(true) failed") @@ -86,7 +86,7 @@ func SubtestNodeStat(t *testing.T, n *Node) { k := n.Key() expected := NodeStat{ - NumLinks: len(n.Links), + NumLinks: len(n.Links()), BlockSize: len(enc), LinksSize: len(enc) - len(n.Data()), // includes framing. DataSize: len(n.Data()), @@ -174,7 +174,12 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { } fmt.Println("Got first node back.") - read, err := uio.NewDagReader(ctx, first, dagservs[i]) + firstpb, ok := first.(*ProtoNode) + if !ok { + errs <- ErrNotProtobuf + } + + read, err := uio.NewDagReader(ctx, firstpb, dagservs[i]) if err != nil { errs <- err } @@ -201,7 +206,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { } } -func assertCanGet(t *testing.T, ds DAGService, n *Node) { +func assertCanGet(t *testing.T, ds DAGService, n Node) { if _, err := ds.Get(context.Background(), n.Cid()); err != nil { t.Fatal(err) } @@ -263,13 +268,13 @@ func TestEnumerateChildren(t *testing.T) { t.Fatal(err) } - var traverse func(n *Node) - traverse = func(n *Node) { + var traverse func(n Node) + traverse = func(n Node) { // traverse dag and check - for _, lnk := range n.Links { - c := cid.NewCidV0(lnk.Hash) + for _, lnk := range n.Links() { + c := lnk.Cid if !set.Has(c) { - t.Fatal("missing key in set! ", lnk.Hash.B58String()) + t.Fatal("missing key in set! ", lnk.Cid.String()) } child, err := ds.Get(context.Background(), c) if err != nil { @@ -286,7 +291,7 @@ func TestFetchFailure(t *testing.T) { ds := dstest.Mock() ds_bad := dstest.Mock() - top := new(Node) + top := new(ProtoNode) for i := 0; i < 10; i++ { nd := NodeWithData([]byte{byte('a' + i)}) _, err := ds.Add(nd) @@ -345,13 +350,13 @@ func TestUnmarshalFailure(t *testing.T) { t.Fatal("should have failed to parse node with bad link") } - n := &Node{} + n := &ProtoNode{} n.Marshal() } func TestBasicAddGet(t *testing.T) { ds := dstest.Mock() - nd := new(Node) + nd := new(ProtoNode) c, err := ds.Add(nd) if err != nil { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 91b9be641..ca2d21021 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -14,8 +14,8 @@ var ErrLinkNotFound = fmt.Errorf("no link by that name") // Node represents a node in the IPFS Merkle DAG. // nodes have opaque data and a set of navigable links. -type Node struct { - Links []*Link +type ProtoNode struct { + links []*Link data []byte // cache encoded/marshaled value @@ -48,7 +48,7 @@ type Link struct { Size uint64 // multihash of the target object - Hash mh.Multihash + Cid *cid.Cid } type LinkSlice []*Link @@ -58,31 +58,29 @@ func (ls LinkSlice) Swap(a, b int) { ls[a], ls[b] = ls[b], ls[a] } func (ls LinkSlice) Less(a, b int) bool { return ls[a].Name < ls[b].Name } // MakeLink creates a link to the given node -func MakeLink(n *Node) (*Link, error) { +func MakeLink(n Node) (*Link, error) { s, err := n.Size() if err != nil { return nil, err } - h := n.Multihash() - return &Link{ Size: s, - Hash: h, + Cid: n.Cid(), }, nil } // GetNode returns the MDAG Node that this link points to -func (l *Link) GetNode(ctx context.Context, serv DAGService) (*Node, error) { - return serv.Get(ctx, legacyCidFromLink(l)) +func (l *Link) GetNode(ctx context.Context, serv DAGService) (Node, error) { + return serv.Get(ctx, l.Cid) } -func NodeWithData(d []byte) *Node { - return &Node{data: d} +func NodeWithData(d []byte) *ProtoNode { + return &ProtoNode{data: d} } // AddNodeLink adds a link to another node. -func (n *Node) AddNodeLink(name string, that *Node) error { +func (n *ProtoNode) AddNodeLink(name string, that *ProtoNode) error { n.encoded = nil lnk, err := MakeLink(that) @@ -99,7 +97,7 @@ func (n *Node) AddNodeLink(name string, that *Node) error { // AddNodeLinkClean adds a link to another node. without keeping a reference to // the child node -func (n *Node) AddNodeLinkClean(name string, that *Node) error { +func (n *ProtoNode) AddNodeLinkClean(name string, that Node) error { n.encoded = nil lnk, err := MakeLink(that) if err != nil { @@ -111,31 +109,31 @@ func (n *Node) AddNodeLinkClean(name string, that *Node) error { } // AddRawLink adds a copy of a link to this node -func (n *Node) AddRawLink(name string, l *Link) error { +func (n *ProtoNode) AddRawLink(name string, l *Link) error { n.encoded = nil - n.Links = append(n.Links, &Link{ + n.links = append(n.links, &Link{ Name: name, Size: l.Size, - Hash: l.Hash, + Cid: l.Cid, }) return nil } // Remove a link on this node by the given name -func (n *Node) RemoveNodeLink(name string) error { +func (n *ProtoNode) RemoveNodeLink(name string) error { n.encoded = nil - good := make([]*Link, 0, len(n.Links)) + good := make([]*Link, 0, len(n.links)) var found bool - for _, l := range n.Links { + for _, l := range n.links { if l.Name != name { good = append(good, l) } else { found = true } } - n.Links = good + n.links = good if !found { return ErrNotFound @@ -145,20 +143,36 @@ func (n *Node) RemoveNodeLink(name string) error { } // Return a copy of the link with given name -func (n *Node) GetNodeLink(name string) (*Link, error) { - for _, l := range n.Links { +func (n *ProtoNode) GetNodeLink(name string) (*Link, error) { + for _, l := range n.links { if l.Name == name { return &Link{ Name: l.Name, Size: l.Size, - Hash: l.Hash, + Cid: l.Cid, }, nil } } return nil, ErrLinkNotFound } -func (n *Node) GetLinkedNode(ctx context.Context, ds DAGService, name string) (*Node, error) { +var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") + +func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds DAGService, name string) (*ProtoNode, error) { + nd, err := n.GetLinkedNode(ctx, ds, name) + if err != nil { + return nil, err + } + + pbnd, ok := nd.(*ProtoNode) + if !ok { + return nil, ErrNotProtobuf + } + + return pbnd, nil +} + +func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds DAGService, name string) (Node, error) { lnk, err := n.GetNodeLink(name) if err != nil { return nil, err @@ -169,30 +183,30 @@ func (n *Node) GetLinkedNode(ctx context.Context, ds DAGService, name string) (* // Copy returns a copy of the node. // NOTE: Does not make copies of Node objects in the links. -func (n *Node) Copy() *Node { - nnode := new(Node) +func (n *ProtoNode) Copy() *ProtoNode { + nnode := new(ProtoNode) if len(n.data) > 0 { nnode.data = make([]byte, len(n.data)) copy(nnode.data, n.data) } - if len(n.Links) > 0 { - nnode.Links = make([]*Link, len(n.Links)) - copy(nnode.Links, n.Links) + if len(n.links) > 0 { + nnode.links = make([]*Link, len(n.links)) + copy(nnode.links, n.links) } return nnode } -func (n *Node) RawData() []byte { +func (n *ProtoNode) RawData() []byte { out, _ := n.EncodeProtobuf(false) return out } -func (n *Node) Data() []byte { +func (n *ProtoNode) Data() []byte { return n.data } -func (n *Node) SetData(d []byte) { +func (n *ProtoNode) SetData(d []byte) { n.encoded = nil n.cached = nil n.data = d @@ -200,7 +214,7 @@ func (n *Node) SetData(d []byte) { // UpdateNodeLink return a copy of the node with the link name set to point to // that. If a link of the same name existed, it is removed. -func (n *Node) UpdateNodeLink(name string, that *Node) (*Node, error) { +func (n *ProtoNode) UpdateNodeLink(name string, that *ProtoNode) (*ProtoNode, error) { newnode := n.Copy() err := newnode.RemoveNodeLink(name) err = nil // ignore error @@ -210,21 +224,21 @@ func (n *Node) UpdateNodeLink(name string, that *Node) (*Node, error) { // Size returns the total size of the data addressed by node, // including the total sizes of references. -func (n *Node) Size() (uint64, error) { +func (n *ProtoNode) Size() (uint64, error) { b, err := n.EncodeProtobuf(false) if err != nil { return 0, err } s := uint64(len(b)) - for _, l := range n.Links { + for _, l := range n.links { s += l.Size } return s, nil } // Stat returns statistics on the node. -func (n *Node) Stat() (*NodeStat, error) { +func (n *ProtoNode) Stat() (*NodeStat, error) { enc, err := n.EncodeProtobuf(false) if err != nil { return nil, err @@ -237,7 +251,7 @@ func (n *Node) Stat() (*NodeStat, error) { return &NodeStat{ Hash: n.Key().B58String(), - NumLinks: len(n.Links), + NumLinks: len(n.links), BlockSize: len(enc), LinksSize: len(enc) - len(n.data), // includes framing. DataSize: len(n.data), @@ -245,28 +259,28 @@ func (n *Node) Stat() (*NodeStat, error) { }, nil } -func (n *Node) Key() key.Key { +func (n *ProtoNode) Key() key.Key { return key.Key(n.Multihash()) } -func (n *Node) Loggable() map[string]interface{} { +func (n *ProtoNode) Loggable() map[string]interface{} { return map[string]interface{}{ "node": n.String(), } } -func (n *Node) Cid() *cid.Cid { +func (n *ProtoNode) Cid() *cid.Cid { h := n.Multihash() return cid.NewCidV0(h) } -func (n *Node) String() string { +func (n *ProtoNode) String() string { return n.Cid().String() } // Multihash hashes the encoded data of this node. -func (n *Node) Multihash() mh.Multihash { +func (n *ProtoNode) Multihash() mh.Multihash { // NOTE: EncodeProtobuf generates the hash and puts it in n.cached. _, err := n.EncodeProtobuf(false) if err != nil { @@ -276,3 +290,32 @@ func (n *Node) Multihash() mh.Multihash { return n.cached.Hash() } + +func (n *ProtoNode) Links() []*Link { + return n.links +} + +func (n *ProtoNode) SetLinks(links []*Link) { + n.links = links +} + +func (n *ProtoNode) Resolve(path []string) (*Link, []string, error) { + if len(path) == 0 { + return nil, nil, fmt.Errorf("end of path, no more links to resolve") + } + + lnk, err := n.GetNodeLink(path[0]) + if err != nil { + return nil, nil, err + } + + return lnk, path[1:], nil +} + +func (n *ProtoNode) Tree() []string { + out := make([]string, 0, len(n.links)) + for _, lnk := range n.links { + out = append(out, lnk.Name) + } + return out +} diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index f12334614..4054d6b93 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -10,31 +10,30 @@ import ( ) func TestRemoveLink(t *testing.T) { - nd := &Node{ - Links: []*Link{ - &Link{Name: "a"}, - &Link{Name: "b"}, - &Link{Name: "a"}, - &Link{Name: "a"}, - &Link{Name: "c"}, - &Link{Name: "a"}, - }, - } + nd := &ProtoNode{} + nd.SetLinks([]*Link{ + &Link{Name: "a"}, + &Link{Name: "b"}, + &Link{Name: "a"}, + &Link{Name: "a"}, + &Link{Name: "c"}, + &Link{Name: "a"}, + }) err := nd.RemoveNodeLink("a") if err != nil { t.Fatal(err) } - if len(nd.Links) != 2 { + if len(nd.Links()) != 2 { t.Fatal("number of links incorrect") } - if nd.Links[0].Name != "b" { + if nd.Links()[0].Name != "b" { t.Fatal("link order wrong") } - if nd.Links[1].Name != "c" { + if nd.Links()[1].Name != "c" { t.Fatal("link order wrong") } @@ -45,33 +44,32 @@ func TestRemoveLink(t *testing.T) { } // ensure nothing else got touched - if len(nd.Links) != 2 { + if len(nd.Links()) != 2 { t.Fatal("number of links incorrect") } - if nd.Links[0].Name != "b" { + if nd.Links()[0].Name != "b" { t.Fatal("link order wrong") } - if nd.Links[1].Name != "c" { + if nd.Links()[1].Name != "c" { t.Fatal("link order wrong") } } func TestFindLink(t *testing.T) { ds := mdtest.Mock() - k, err := ds.Add(new(Node)) + k, err := ds.Add(new(ProtoNode)) if err != nil { t.Fatal(err) } - nd := &Node{ - Links: []*Link{ - &Link{Name: "a", Hash: k.Hash()}, - &Link{Name: "c", Hash: k.Hash()}, - &Link{Name: "b", Hash: k.Hash()}, - }, - } + nd := &ProtoNode{} + nd.SetLinks([]*Link{ + &Link{Name: "a", Cid: k}, + &Link{Name: "c", Cid: k}, + &Link{Name: "b", Cid: k}, + }) _, err = ds.Add(nd) if err != nil { @@ -107,19 +105,19 @@ func TestFindLink(t *testing.T) { t.Fatal(err) } - if olnk.Hash.B58String() == k.String() { + if olnk.Cid.String() == k.String() { t.Fatal("new link should have different hash") } } func TestNodeCopy(t *testing.T) { - nd := &Node{ - Links: []*Link{ - &Link{Name: "a"}, - &Link{Name: "c"}, - &Link{Name: "b"}, - }, - } + nd := &ProtoNode{} + nd.SetLinks([]*Link{ + &Link{Name: "a"}, + &Link{Name: "c"}, + &Link{Name: "b"}, + }) + nd.SetData([]byte("testing")) ond := nd.Copy() diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index 37c050426..fdc06d2cd 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -30,7 +30,7 @@ type Options struct { // State is a current traversal state type State struct { - Node *mdag.Node + Node mdag.Node Depth int } @@ -39,13 +39,13 @@ type traversal struct { seen map[string]struct{} } -func (t *traversal) shouldSkip(n *mdag.Node) (bool, error) { +func (t *traversal) shouldSkip(n mdag.Node) (bool, error) { if t.opts.SkipDuplicates { - k := n.Key() - if _, found := t.seen[string(k)]; found { + k := n.Cid() + if _, found := t.seen[k.KeyString()]; found { return true, nil } - t.seen[string(k)] = struct{}{} + t.seen[k.KeyString()] = struct{}{} } return false, nil @@ -59,9 +59,9 @@ func (t *traversal) callFunc(next State) error { // stop processing. if it returns a nil node, just skip it. // // the error handling is a little complicated. -func (t *traversal) getNode(link *mdag.Link) (*mdag.Node, error) { +func (t *traversal) getNode(link *mdag.Link) (mdag.Node, error) { - getNode := func(l *mdag.Link) (*mdag.Node, error) { + getNode := func(l *mdag.Link) (mdag.Node, error) { next, err := l.GetNode(context.TODO(), t.opts.DAG) if err != nil { return nil, err @@ -99,7 +99,7 @@ type Func func(current State) error // type ErrFunc func(err error) error -func Traverse(root *mdag.Node, o Options) error { +func Traverse(root mdag.Node, o Options) error { t := traversal{ opts: o, seen: map[string]struct{}{}, @@ -145,7 +145,7 @@ func dfsPostTraverse(state State, t *traversal) error { } func dfsDescend(df dfsFunc, curr State, t *traversal) error { - for _, l := range curr.Node.Links { + for _, l := range curr.Node.Links() { node, err := t.getNode(l) if err != nil { return err @@ -184,7 +184,7 @@ func bfsTraverse(root State, t *traversal) error { return err } - for _, l := range curr.Node.Links { + for _, l := range curr.Node.Links() { node, err := t.getNode(l) if err != nil { return err diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 2bd344411..c7dd93a47 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -321,12 +321,12 @@ func TestBFSSkip(t *testing.T) { `)) } -func testWalkOutputs(t *testing.T, root *mdag.Node, opts Options, expect []byte) { +func testWalkOutputs(t *testing.T, root mdag.Node, opts Options, expect []byte) { expect = bytes.TrimLeft(expect, "\n") buf := new(bytes.Buffer) walk := func(current State) error { - s := fmt.Sprintf("%d %s\n", current.Depth, current.Node.Data()) + s := fmt.Sprintf("%d %s\n", current.Depth, current.Node.(*mdag.ProtoNode).Data()) t.Logf("walk: %s", s) buf.Write([]byte(s)) return nil @@ -348,7 +348,7 @@ func testWalkOutputs(t *testing.T, root *mdag.Node, opts Options, expect []byte) } } -func newFan(t *testing.T, ds mdag.DAGService) *mdag.Node { +func newFan(t *testing.T, ds mdag.DAGService) mdag.Node { a := mdag.NodeWithData([]byte("/a")) addLink(t, ds, a, child(t, ds, a, "aa")) addLink(t, ds, a, child(t, ds, a, "ab")) @@ -357,7 +357,7 @@ func newFan(t *testing.T, ds mdag.DAGService) *mdag.Node { return a } -func newLinkedList(t *testing.T, ds mdag.DAGService) *mdag.Node { +func newLinkedList(t *testing.T, ds mdag.DAGService) mdag.Node { a := mdag.NodeWithData([]byte("/a")) aa := child(t, ds, a, "aa") aaa := child(t, ds, aa, "aaa") @@ -370,7 +370,7 @@ func newLinkedList(t *testing.T, ds mdag.DAGService) *mdag.Node { return a } -func newBinaryTree(t *testing.T, ds mdag.DAGService) *mdag.Node { +func newBinaryTree(t *testing.T, ds mdag.DAGService) mdag.Node { a := mdag.NodeWithData([]byte("/a")) aa := child(t, ds, a, "aa") ab := child(t, ds, a, "ab") @@ -383,7 +383,7 @@ func newBinaryTree(t *testing.T, ds mdag.DAGService) *mdag.Node { return a } -func newBinaryDAG(t *testing.T, ds mdag.DAGService) *mdag.Node { +func newBinaryDAG(t *testing.T, ds mdag.DAGService) mdag.Node { a := mdag.NodeWithData([]byte("/a")) aa := child(t, ds, a, "aa") aaa := child(t, ds, aa, "aaa") @@ -400,16 +400,16 @@ func newBinaryDAG(t *testing.T, ds mdag.DAGService) *mdag.Node { return a } -func addLink(t *testing.T, ds mdag.DAGService, a, b *mdag.Node) { - to := string(a.Data()) + "2" + string(b.Data()) +func addLink(t *testing.T, ds mdag.DAGService, a, b mdag.Node) { + to := string(a.(*mdag.ProtoNode).Data()) + "2" + string(b.(*mdag.ProtoNode).Data()) if _, err := ds.Add(b); err != nil { t.Error(err) } - if err := a.AddNodeLink(to, b); err != nil { + if err := a.(*mdag.ProtoNode).AddNodeLink(to, b.(*mdag.ProtoNode)); err != nil { t.Error(err) } } -func child(t *testing.T, ds mdag.DAGService, a *mdag.Node, name string) *mdag.Node { - return mdag.NodeWithData([]byte(string(a.Data()) + "/" + name)) +func child(t *testing.T, ds mdag.DAGService, a mdag.Node, name string) mdag.Node { + return mdag.NodeWithData([]byte(string(a.(*mdag.ProtoNode).Data()) + "/" + name)) } diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 275600443..2b5ddb72b 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -1,7 +1,6 @@ package dagutils import ( - "bytes" "fmt" "path" @@ -37,7 +36,7 @@ func (c *Change) String() string { } } -func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.Node, cs []*Change) (*dag.Node, error) { +func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.ProtoNode, cs []*Change) (*dag.ProtoNode, error) { e := NewDagEditor(nd, ds) for _, c := range cs { switch c.Type { @@ -46,7 +45,13 @@ func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.Node, cs []*Cha if err != nil { return nil, err } - err = e.InsertNodeAtPath(ctx, c.Path, child, nil) + + childpb, ok := child.(*dag.ProtoNode) + if !ok { + return nil, dag.ErrNotProtobuf + } + + err = e.InsertNodeAtPath(ctx, c.Path, childpb, nil) if err != nil { return nil, err } @@ -66,7 +71,13 @@ func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.Node, cs []*Cha if err != nil { return nil, err } - err = e.InsertNodeAtPath(ctx, c.Path, child, nil) + + childpb, ok := child.(*dag.ProtoNode) + if !ok { + return nil, dag.ErrNotProtobuf + } + + err = e.InsertNodeAtPath(ctx, c.Path, childpb, nil) if err != nil { return nil, err } @@ -76,8 +87,8 @@ func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.Node, cs []*Cha return e.Finalize(ds) } -func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) ([]*Change, error) { - if len(a.Links) == 0 && len(b.Links) == 0 { +func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.ProtoNode) ([]*Change, error) { + if len(a.Links()) == 0 && len(b.Links()) == 0 { return []*Change{ &Change{ Type: Mod, @@ -92,10 +103,10 @@ func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) ([]*Change, er clean_b := b.Copy() // strip out unchanged stuff - for _, lnk := range a.Links { + for _, lnk := range a.Links() { l, err := b.GetNodeLink(lnk.Name) if err == nil { - if bytes.Equal(l.Hash, lnk.Hash) { + if l.Cid.Equals(lnk.Cid) { // no change... ignore it } else { anode, err := lnk.GetNode(ctx, ds) @@ -108,7 +119,17 @@ func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) ([]*Change, er return nil, err } - sub, err := Diff(ctx, ds, anode, bnode) + anodepb, ok := anode.(*dag.ProtoNode) + if !ok { + return nil, dag.ErrNotProtobuf + } + + bnodepb, ok := bnode.(*dag.ProtoNode) + if !ok { + return nil, dag.ErrNotProtobuf + } + + sub, err := Diff(ctx, ds, anodepb, bnodepb) if err != nil { return nil, err } @@ -123,18 +144,18 @@ func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) ([]*Change, er } } - for _, lnk := range clean_a.Links { + for _, lnk := range clean_a.Links() { out = append(out, &Change{ Type: Remove, Path: lnk.Name, - Before: cid.NewCidV0(lnk.Hash), + Before: lnk.Cid, }) } - for _, lnk := range clean_b.Links { + for _, lnk := range clean_b.Links() { out = append(out, &Change{ Type: Add, Path: lnk.Name, - After: cid.NewCidV0(lnk.Hash), + After: lnk.Cid, }) } diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index a6f117ba4..a44d94621 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -15,7 +15,7 @@ import ( ) type Editor struct { - root *dag.Node + root *dag.ProtoNode // tmp is a temporary in memory (for now) dagstore for all of the // intermediary nodes to be stored in @@ -34,7 +34,7 @@ func NewMemoryDagService() dag.DAGService { } // root is the node to be modified, source is the dagstore to pull nodes from (optional) -func NewDagEditor(root *dag.Node, source dag.DAGService) *Editor { +func NewDagEditor(root *dag.ProtoNode, source dag.DAGService) *Editor { return &Editor{ root: root, tmp: NewMemoryDagService(), @@ -42,7 +42,7 @@ func NewDagEditor(root *dag.Node, source dag.DAGService) *Editor { } } -func (e *Editor) GetNode() *dag.Node { +func (e *Editor) GetNode() *dag.ProtoNode { return e.root.Copy() } @@ -50,7 +50,7 @@ func (e *Editor) GetDagService() dag.DAGService { return e.tmp } -func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childnd *dag.Node) (*dag.Node, error) { +func addLink(ctx context.Context, ds dag.DAGService, root *dag.ProtoNode, childname string, childnd *dag.ProtoNode) (*dag.ProtoNode, error) { if childname == "" { return nil, errors.New("cannot create link with no name!") } @@ -76,7 +76,7 @@ func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname s return root, nil } -func (e *Editor) InsertNodeAtPath(ctx context.Context, pth string, toinsert *dag.Node, create func() *dag.Node) error { +func (e *Editor) InsertNodeAtPath(ctx context.Context, pth string, toinsert *dag.ProtoNode, create func() *dag.ProtoNode) error { splpath := path.SplitList(pth) nd, err := e.insertNodeAtPath(ctx, e.root, splpath, toinsert, create) if err != nil { @@ -86,12 +86,12 @@ func (e *Editor) InsertNodeAtPath(ctx context.Context, pth string, toinsert *dag return nil } -func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.Node, path []string, toinsert *dag.Node, create func() *dag.Node) (*dag.Node, error) { +func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path []string, toinsert *dag.ProtoNode, create func() *dag.ProtoNode) (*dag.ProtoNode, error) { if len(path) == 1 { return addLink(ctx, e.tmp, root, path[0], toinsert) } - nd, err := root.GetLinkedNode(ctx, e.tmp, path[0]) + nd, err := root.GetLinkedProtoNode(ctx, e.tmp, path[0]) if err != nil { // if 'create' is true, we create directories on the way down as needed if err == dag.ErrLinkNotFound && create != nil { @@ -99,7 +99,7 @@ func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.Node, path []st err = nil // no longer an error case } else if err == dag.ErrNotFound { // try finding it in our source dagstore - nd, err = root.GetLinkedNode(ctx, e.src, path[0]) + nd, err = root.GetLinkedProtoNode(ctx, e.src, path[0]) } // if we receive an ErrNotFound, then our second 'GetLinkedNode' call @@ -140,7 +140,7 @@ func (e *Editor) RmLink(ctx context.Context, pth string) error { return nil } -func (e *Editor) rmLink(ctx context.Context, root *dag.Node, path []string) (*dag.Node, error) { +func (e *Editor) rmLink(ctx context.Context, root *dag.ProtoNode, path []string) (*dag.ProtoNode, error) { if len(path) == 1 { // base case, remove node in question err := root.RemoveNodeLink(path[0]) @@ -157,9 +157,9 @@ func (e *Editor) rmLink(ctx context.Context, root *dag.Node, path []string) (*da } // search for node in both tmp dagstore and source dagstore - nd, err := root.GetLinkedNode(ctx, e.tmp, path[0]) + nd, err := root.GetLinkedProtoNode(ctx, e.tmp, path[0]) if err == dag.ErrNotFound { - nd, err = root.GetLinkedNode(ctx, e.src, path[0]) + nd, err = root.GetLinkedProtoNode(ctx, e.src, path[0]) } if err != nil { @@ -187,19 +187,19 @@ func (e *Editor) rmLink(ctx context.Context, root *dag.Node, path []string) (*da return root, nil } -func (e *Editor) Finalize(ds dag.DAGService) (*dag.Node, error) { +func (e *Editor) Finalize(ds dag.DAGService) (*dag.ProtoNode, error) { nd := e.GetNode() err := copyDag(nd, e.tmp, ds) return nd, err } -func copyDag(nd *dag.Node, from, to dag.DAGService) error { +func copyDag(nd *dag.ProtoNode, from, to dag.DAGService) error { _, err := to.Add(nd) if err != nil { return err } - for _, lnk := range nd.Links { + for _, lnk := range nd.Links() { child, err := lnk.GetNode(context.Background(), from) if err != nil { if err == dag.ErrNotFound { @@ -210,7 +210,12 @@ func copyDag(nd *dag.Node, from, to dag.DAGService) error { return err } - err = copyDag(child, from, to) + childpb, ok := child.(*dag.ProtoNode) + if !ok { + return dag.ErrNotProtobuf + } + + err = copyDag(childpb, from, to) if err != nil { return err } diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index bf11cb8ee..4f822e5cd 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -20,7 +20,7 @@ func TestAddLink(t *testing.T) { t.Fatal(err) } - nd := new(dag.Node) + nd := new(dag.ProtoNode) nnode, err := addLink(context.Background(), ds, nd, "fish", fishnode) if err != nil { t.Fatal(err) @@ -37,11 +37,11 @@ func TestAddLink(t *testing.T) { } } -func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, pth string, exp *cid.Cid) { +func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.ProtoNode, pth string, exp *cid.Cid) { parts := path.SplitList(pth) cur := root for _, e := range parts { - nxt, err := cur.GetLinkedNode(context.Background(), ds, e) + nxt, err := cur.GetLinkedProtoNode(context.Background(), ds, e) if err != nil { t.Fatal(err) } @@ -56,7 +56,7 @@ func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, pth strin } func TestInsertNode(t *testing.T) { - root := new(dag.Node) + root := new(dag.ProtoNode) e := NewDagEditor(root, nil) testInsert(t, e, "a", "anodefortesting", false, "") @@ -83,10 +83,10 @@ func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr t.Fatal(err) } - var c func() *dag.Node + var c func() *dag.ProtoNode if create { - c = func() *dag.Node { - return &dag.Node{} + c = func() *dag.ProtoNode { + return &dag.ProtoNode{} } } From bd8265f3121a8da3b3fcea4e9c7478f33f9a6c49 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 9 Oct 2016 12:59:36 -0700 Subject: [PATCH 1573/3817] merkledag: change 'Node' to be an interface Also change existing 'Node' type to 'ProtoNode' and use that most everywhere for now. As we move forward with the integration we will try and use the Node interface in more places that we're currently using ProtoNode. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@3c44991bb030cabb4d3f9f27c406288f1bee0660 --- path/resolver.go | 34 +++++++++++++++++++--------------- path/resolver_test.go | 8 ++++---- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index fb9cbf37e..14ed1d87c 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -2,14 +2,13 @@ package path import ( + "context" "errors" "fmt" "time" - "context" - mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" - merkledag "github.com/ipfs/go-ipfs/merkledag" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) @@ -23,11 +22,11 @@ var ErrNoComponents = errors.New( // ErrNoLink is returned when a link is not found in a path type ErrNoLink struct { Name string - Node mh.Multihash + Node *cid.Cid } func (e ErrNoLink) Error() string { - return fmt.Sprintf("no link named %q under %s", e.Name, e.Node.B58String()) + return fmt.Sprintf("no link named %q under %s", e.Name, e.Node.String()) } // Resolver provides path resolution to IPFS @@ -62,7 +61,7 @@ func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) { // ResolvePath fetches the node for given path. It returns the last item // returned by ResolvePathComponents. -func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (*merkledag.Node, error) { +func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (merkledag.Node, error) { // validate path if err := fpath.IsValid(); err != nil { return nil, err @@ -78,7 +77,7 @@ func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (*merkledag.Node // ResolvePathComponents fetches the nodes for each segment of the given path. // It uses the first path component as a hash (key) of the first node, then // resolves all other components walking the links, with ResolveLinks. -func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]*merkledag.Node, error) { +func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]merkledag.Node, error) { h, parts, err := SplitAbsPath(fpath) if err != nil { return nil, err @@ -100,28 +99,33 @@ func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]*me // // ResolveLinks(nd, []string{"foo", "bar", "baz"}) // would retrieve "baz" in ("bar" in ("foo" in nd.Links).Links).Links -func (s *Resolver) ResolveLinks(ctx context.Context, ndd *merkledag.Node, names []string) ([]*merkledag.Node, error) { +func (s *Resolver) ResolveLinks(ctx context.Context, ndd merkledag.Node, names []string) ([]merkledag.Node, error) { - result := make([]*merkledag.Node, 0, len(names)+1) + result := make([]merkledag.Node, 0, len(names)+1) result = append(result, ndd) nd := ndd // dup arg workaround // for each of the path components - for _, name := range names { - + for len(names) > 0 { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, time.Minute) defer cancel() - nextnode, err := nd.GetLinkedNode(ctx, s.DAG, name) + lnk, rest, err := nd.Resolve(names) if err == merkledag.ErrLinkNotFound { - n := nd.Multihash() - return result, ErrNoLink{Name: name, Node: n} + n := nd.Cid() + return result, ErrNoLink{Name: names[0], Node: n} } else if err != nil { - return append(result, nextnode), err + return result, err + } + + nextnode, err := s.DAG.Get(ctx, lnk.Cid) + if err != nil { + return result, err } nd = nextnode + names = rest result = append(result, nextnode) } return result, nil diff --git a/path/resolver_test.go b/path/resolver_test.go index 77e7a27e1..b0130bb17 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -13,8 +13,8 @@ import ( util "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) -func randNode() (*merkledag.Node, key.Key) { - node := new(merkledag.Node) +func randNode() (*merkledag.ProtoNode, key.Key) { + node := new(merkledag.ProtoNode) node.SetData(make([]byte, 32)) util.NewTimeSeededRand().Read(node.Data()) k := node.Key() @@ -39,7 +39,7 @@ func TestRecurivePathResolution(t *testing.T) { t.Fatal(err) } - for _, n := range []*merkledag.Node{a, b, c} { + for _, n := range []merkledag.Node{a, b, c} { _, err = dagService.Add(n) if err != nil { t.Fatal(err) @@ -60,7 +60,7 @@ func TestRecurivePathResolution(t *testing.T) { t.Fatal(err) } - key := node.Key() + key := node.Cid() if key.String() != cKey.String() { t.Fatal(fmt.Errorf( "recursive path resolution failed for %s: %s != %s", From f09ec740a02e56bcff0c040701f139e7fc152dba Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 9 Oct 2016 12:59:36 -0700 Subject: [PATCH 1574/3817] merkledag: change 'Node' to be an interface Also change existing 'Node' type to 'ProtoNode' and use that most everywhere for now. As we move forward with the integration we will try and use the Node interface in more places that we're currently using ProtoNode. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@c167c8f6c4fc9ff9cf9995311e3bfb369ef17ca8 --- pinning/pinner/pin.go | 21 ++++++++----- pinning/pinner/pin_test.go | 4 +-- pinning/pinner/set.go | 63 +++++++++++++++++++++++++------------- pinning/pinner/set_test.go | 2 +- 4 files changed, 57 insertions(+), 33 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index ca36a5588..4a59e78d9 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -83,7 +83,7 @@ func StringToPinMode(s string) (PinMode, bool) { type Pinner interface { IsPinned(*cid.Cid) (string, bool, error) IsPinnedWithType(*cid.Cid, PinMode) (string, bool, error) - Pin(context.Context, *mdag.Node, bool) error + Pin(context.Context, mdag.Node, bool) error Unpin(context.Context, *cid.Cid, bool) error // Check if a set of keys are pinned, more efficient than @@ -162,7 +162,7 @@ func NewPinner(dstore ds.Datastore, serv, internal mdag.DAGService) Pinner { } // Pin the given node, optionally recursive -func (p *pinner) Pin(ctx context.Context, node *mdag.Node, recurse bool) error { +func (p *pinner) Pin(ctx context.Context, node mdag.Node, recurse bool) error { p.lock.Lock() defer p.lock.Unlock() c := node.Cid() @@ -317,7 +317,7 @@ func (p *pinner) CheckIfPinned(cids ...*cid.Cid) ([]Pinned, error) { return err } for _, lnk := range links { - c := cid.NewCidV0(lnk.Hash) + c := lnk.Cid if toCheck.Has(c) { pinned = append(pinned, @@ -403,12 +403,17 @@ func LoadPinner(d ds.Datastore, dserv, internal mdag.DAGService) (Pinner, error) return nil, fmt.Errorf("cannot find pinning root object: %v", err) } + rootpb, ok := root.(*mdag.ProtoNode) + if !ok { + return nil, mdag.ErrNotProtobuf + } + internalset := cid.NewSet() internalset.Add(rootCid) recordInternal := internalset.Add { // load recursive set - recurseKeys, err := loadSet(ctx, internal, root, linkRecursive, recordInternal) + recurseKeys, err := loadSet(ctx, internal, rootpb, linkRecursive, recordInternal) if err != nil { return nil, fmt.Errorf("cannot load recursive pins: %v", err) } @@ -416,7 +421,7 @@ func LoadPinner(d ds.Datastore, dserv, internal mdag.DAGService) (Pinner, error) } { // load direct set - directKeys, err := loadSet(ctx, internal, root, linkDirect, recordInternal) + directKeys, err := loadSet(ctx, internal, rootpb, linkDirect, recordInternal) if err != nil { return nil, fmt.Errorf("cannot load direct pins: %v", err) } @@ -453,7 +458,7 @@ func (p *pinner) Flush() error { internalset := cid.NewSet() recordInternal := internalset.Add - root := &mdag.Node{} + root := &mdag.ProtoNode{} { n, err := storeSet(ctx, p.internal, p.directPin.Keys(), recordInternal) if err != nil { @@ -475,7 +480,7 @@ func (p *pinner) Flush() error { } // add the empty node, its referenced by the pin sets but never created - _, err := p.internal.Add(new(mdag.Node)) + _, err := p.internal.Add(new(mdag.ProtoNode)) if err != nil { return err } @@ -522,7 +527,7 @@ func hasChild(ds mdag.LinkService, root *cid.Cid, child key.Key) (bool, error) { return false, err } for _, lnk := range links { - c := cid.NewCidV0(lnk.Hash) + c := lnk.Cid if key.Key(c.Hash()) == child { return true, nil } diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 911d0e88a..787b8226a 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -16,8 +16,8 @@ import ( dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) -func randNode() (*mdag.Node, *cid.Cid) { - nd := new(mdag.Node) +func randNode() (*mdag.ProtoNode, *cid.Cid) { + nd := new(mdag.ProtoNode) nd.SetData(make([]byte, 32)) util.NewTimeSeededRand().Read(nd.Data()) k := nd.Cid() diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 11d56188d..1a1f9f3bf 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -55,25 +55,27 @@ func (s sortByHash) Len() int { } func (s sortByHash) Less(a, b int) bool { - return bytes.Compare(s.links[a].Hash, s.links[b].Hash) == -1 + return bytes.Compare(s.links[a].Cid.Bytes(), s.links[b].Cid.Bytes()) == -1 } func (s sortByHash) Swap(a, b int) { s.links[a], s.links[b] = s.links[b], s.links[a] } -func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint64, iter itemIterator, internalKeys keyObserver) (*merkledag.Node, error) { +func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint64, iter itemIterator, internalKeys keyObserver) (*merkledag.ProtoNode, error) { seed, err := randomSeed() if err != nil { return nil, err } - - n := &merkledag.Node{Links: make([]*merkledag.Link, 0, defaultFanout+maxItems)} + links := make([]*merkledag.Link, 0, defaultFanout+maxItems) for i := 0; i < defaultFanout; i++ { - n.Links = append(n.Links, &merkledag.Link{Hash: emptyKey.Hash()}) + links = append(links, &merkledag.Link{Cid: emptyKey}) } // add emptyKey to our set of internal pinset objects + n := &merkledag.ProtoNode{} + n.SetLinks(links) + internalKeys(emptyKey) hdr := &pb.Set{ @@ -87,17 +89,22 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint if estimatedLen < maxItems { // it'll probably fit + links := n.Links() for i := 0; i < maxItems; i++ { k, ok := iter() if !ok { // all done break } - n.Links = append(n.Links, &merkledag.Link{Hash: k.Hash()}) + + links = append(links, &merkledag.Link{Cid: k}) } + + n.SetLinks(links) + // sort by hash, also swap item Data s := sortByHash{ - links: n.Links[defaultFanout:], + links: n.Links()[defaultFanout:], } sort.Stable(s) } @@ -152,15 +159,15 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint internalKeys(childKey) // overwrite the 'empty key' in the existing links array - n.Links[h] = &merkledag.Link{ - Hash: childKey.Hash(), + n.Links()[h] = &merkledag.Link{ + Cid: childKey, Size: size, } } return n, nil } -func readHdr(n *merkledag.Node) (*pb.Set, error) { +func readHdr(n *merkledag.ProtoNode) (*pb.Set, error) { hdrLenRaw, consumed := binary.Uvarint(n.Data()) if consumed <= 0 { return nil, errors.New("invalid Set header length") @@ -180,13 +187,13 @@ func readHdr(n *merkledag.Node) (*pb.Set, error) { if v := hdr.GetVersion(); v != 1 { return nil, fmt.Errorf("unsupported Set version: %d", v) } - if uint64(hdr.GetFanout()) > uint64(len(n.Links)) { + if uint64(hdr.GetFanout()) > uint64(len(n.Links())) { return nil, errors.New("impossibly large Fanout") } return &hdr, nil } -func writeHdr(n *merkledag.Node, hdr *pb.Set) error { +func writeHdr(n *merkledag.ProtoNode, hdr *pb.Set) error { hdrData, err := proto.Marshal(hdr) if err != nil { return err @@ -207,20 +214,20 @@ func writeHdr(n *merkledag.Node, hdr *pb.Set) error { type walkerFunc func(idx int, link *merkledag.Link) error -func walkItems(ctx context.Context, dag merkledag.DAGService, n *merkledag.Node, fn walkerFunc, children keyObserver) error { +func walkItems(ctx context.Context, dag merkledag.DAGService, n *merkledag.ProtoNode, fn walkerFunc, children keyObserver) error { hdr, err := readHdr(n) if err != nil { return err } // readHdr guarantees fanout is a safe value fanout := hdr.GetFanout() - for i, l := range n.Links[fanout:] { + for i, l := range n.Links()[fanout:] { if err := fn(i, l); err != nil { return err } } - for _, l := range n.Links[:fanout] { - c := cid.NewCidV0(l.Hash) + for _, l := range n.Links()[:fanout] { + c := l.Cid children(c) if c.Equals(emptyKey) { continue @@ -229,20 +236,26 @@ func walkItems(ctx context.Context, dag merkledag.DAGService, n *merkledag.Node, if err != nil { return err } - if err := walkItems(ctx, dag, subtree, fn, children); err != nil { + + stpb, ok := subtree.(*merkledag.ProtoNode) + if !ok { + return merkledag.ErrNotProtobuf + } + + if err := walkItems(ctx, dag, stpb, fn, children); err != nil { return err } } return nil } -func loadSet(ctx context.Context, dag merkledag.DAGService, root *merkledag.Node, name string, internalKeys keyObserver) ([]*cid.Cid, error) { +func loadSet(ctx context.Context, dag merkledag.DAGService, root *merkledag.ProtoNode, name string, internalKeys keyObserver) ([]*cid.Cid, error) { l, err := root.GetNodeLink(name) if err != nil { return nil, err } - lnkc := cid.NewCidV0(l.Hash) + lnkc := l.Cid internalKeys(lnkc) n, err := l.GetNode(ctx, dag) @@ -250,12 +263,18 @@ func loadSet(ctx context.Context, dag merkledag.DAGService, root *merkledag.Node return nil, err } + pbn, ok := n.(*merkledag.ProtoNode) + if !ok { + return nil, merkledag.ErrNotProtobuf + } + var res []*cid.Cid walk := func(idx int, link *merkledag.Link) error { - res = append(res, cid.NewCidV0(link.Hash)) + res = append(res, link.Cid) return nil } - if err := walkItems(ctx, dag, n, walk, internalKeys); err != nil { + + if err := walkItems(ctx, dag, pbn, walk, internalKeys); err != nil { return nil, err } return res, nil @@ -273,7 +292,7 @@ func getCidListIterator(cids []*cid.Cid) itemIterator { } } -func storeSet(ctx context.Context, dag merkledag.DAGService, cids []*cid.Cid, internalKeys keyObserver) (*merkledag.Node, error) { +func storeSet(ctx context.Context, dag merkledag.DAGService, cids []*cid.Cid, internalKeys keyObserver) (*merkledag.ProtoNode, error) { iter := getCidListIterator(cids) n, err := storeItems(ctx, dag, uint64(len(cids)), iter, internalKeys) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index e17906f6f..335b59e99 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -38,7 +38,7 @@ func TestSet(t *testing.T) { // weird wrapper node because loadSet expects us to pass an // object pointing to multiple named sets - setroot := &dag.Node{} + setroot := &dag.ProtoNode{} err = setroot.AddNodeLinkClean("foo", out) if err != nil { t.Fatal(err) From d7b13141c74df3b3c5e823fbc3647d1b7b0b3999 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 9 Oct 2016 12:59:36 -0700 Subject: [PATCH 1575/3817] merkledag: change 'Node' to be an interface Also change existing 'Node' type to 'ProtoNode' and use that most everywhere for now. As we move forward with the integration we will try and use the Node interface in more places that we're currently using ProtoNode. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@93ba0e48c3e8fdf57a021c63085f52388a03232c --- unixfs/archive/archive.go | 2 +- unixfs/archive/tar/writer.go | 15 ++++++---- unixfs/format.go | 2 +- unixfs/io/dagreader.go | 26 +++++++++++------ unixfs/io/dirbuilder.go | 15 ++++++---- unixfs/io/dirbuilder_test.go | 4 +-- unixfs/mod/dagmodifier.go | 54 +++++++++++++++++++++++------------- unixfs/test/utils.go | 16 +++++------ 8 files changed, 85 insertions(+), 49 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index 8cc1ec2e1..a94c9f7af 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -30,7 +30,7 @@ func (i *identityWriteCloser) Close() error { } // DagArchive is equivalent to `ipfs getdag $hash | maybe_tar | maybe_gzip` -func DagArchive(ctx cxt.Context, nd *mdag.Node, name string, dag mdag.DAGService, archive bool, compression int) (io.Reader, error) { +func DagArchive(ctx cxt.Context, nd *mdag.ProtoNode, name string, dag mdag.DAGService, archive bool, compression int) (io.Reader, error) { _, filename := path.Split(name) diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 475b318a4..f710d4063 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -34,7 +34,7 @@ func NewWriter(ctx cxt.Context, dag mdag.DAGService, archive bool, compression i }, nil } -func (w *Writer) writeDir(nd *mdag.Node, fpath string) error { +func (w *Writer) writeDir(nd *mdag.ProtoNode, fpath string) error { if err := writeDirHeader(w.TarW, fpath); err != nil { return err } @@ -45,8 +45,13 @@ func (w *Writer) writeDir(nd *mdag.Node, fpath string) error { return err } - npath := path.Join(fpath, nd.Links[i].Name) - if err := w.WriteNode(child, npath); err != nil { + childpb, ok := child.(*mdag.ProtoNode) + if !ok { + return mdag.ErrNotProtobuf + } + + npath := path.Join(fpath, nd.Links()[i].Name) + if err := w.WriteNode(childpb, npath); err != nil { return err } } @@ -54,7 +59,7 @@ func (w *Writer) writeDir(nd *mdag.Node, fpath string) error { return nil } -func (w *Writer) writeFile(nd *mdag.Node, pb *upb.Data, fpath string) error { +func (w *Writer) writeFile(nd *mdag.ProtoNode, pb *upb.Data, fpath string) error { if err := writeFileHeader(w.TarW, fpath, pb.GetFilesize()); err != nil { return err } @@ -67,7 +72,7 @@ func (w *Writer) writeFile(nd *mdag.Node, pb *upb.Data, fpath string) error { return nil } -func (w *Writer) WriteNode(nd *mdag.Node, fpath string) error { +func (w *Writer) WriteNode(nd *mdag.ProtoNode, fpath string) error { pb := new(upb.Data) if err := proto.Unmarshal(nd.Data(), pb); err != nil { return err diff --git a/unixfs/format.go b/unixfs/format.go index 0235f0c7c..a8ade430c 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -224,6 +224,6 @@ func BytesForMetadata(m *Metadata) ([]byte, error) { return proto.Marshal(pbd) } -func EmptyDirNode() *dag.Node { +func EmptyDirNode() *dag.ProtoNode { return dag.NodeWithData(FolderPBData()) } diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index f78fbbf77..086e5038c 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -24,7 +24,7 @@ type DagReader struct { serv mdag.DAGService // the node being read - node *mdag.Node + node *mdag.ProtoNode // cached protobuf structure from node.Data pbdata *ftpb.Data @@ -58,7 +58,7 @@ type ReadSeekCloser interface { // NewDagReader creates a new reader object that reads the data represented by // the given node, using the passed in DAGService for data retreival -func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (*DagReader, error) { +func NewDagReader(ctx context.Context, n *mdag.ProtoNode, serv mdag.DAGService) (*DagReader, error) { pb := new(ftpb.Data) if err := proto.Unmarshal(n.Data(), pb); err != nil { return nil, err @@ -71,14 +71,19 @@ func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (*Dag case ftpb.Data_File, ftpb.Data_Raw: return NewDataFileReader(ctx, n, pb, serv), nil case ftpb.Data_Metadata: - if len(n.Links) == 0 { + if len(n.Links()) == 0 { return nil, errors.New("incorrectly formatted metadata object") } - child, err := n.Links[0].GetNode(ctx, serv) + child, err := n.Links()[0].GetNode(ctx, serv) if err != nil { return nil, err } - return NewDagReader(ctx, child, serv) + + childpb, ok := child.(*mdag.ProtoNode) + if !ok { + return nil, mdag.ErrNotProtobuf + } + return NewDagReader(ctx, childpb, serv) case ftpb.Data_Symlink: return nil, ErrCantReadSymlinks default: @@ -86,7 +91,7 @@ func NewDagReader(ctx context.Context, n *mdag.Node, serv mdag.DAGService) (*Dag } } -func NewDataFileReader(ctx context.Context, n *mdag.Node, pb *ftpb.Data, serv mdag.DAGService) *DagReader { +func NewDataFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv mdag.DAGService) *DagReader { fctx, cancel := context.WithCancel(ctx) promises := mdag.GetDAG(fctx, serv, n) return &DagReader{ @@ -114,8 +119,13 @@ func (dr *DagReader) precalcNextBuf(ctx context.Context) error { } dr.linkPosition++ + nxtpb, ok := nxt.(*mdag.ProtoNode) + if !ok { + return mdag.ErrNotProtobuf + } + pb := new(ftpb.Data) - err = proto.Unmarshal(nxt.Data(), pb) + err = proto.Unmarshal(nxtpb.Data(), pb) if err != nil { return fmt.Errorf("incorrectly formatted protobuf: %s", err) } @@ -125,7 +135,7 @@ func (dr *DagReader) precalcNextBuf(ctx context.Context) error { // A directory should not exist within a file return ft.ErrInvalidDirLocation case ftpb.Data_File: - dr.buf = NewDataFileReader(dr.ctx, nxt, pb, dr.serv) + dr.buf = NewDataFileReader(dr.ctx, nxtpb, pb, dr.serv) return nil case ftpb.Data_Raw: dr.buf = NewRSNCFromBytes(pb.GetData()) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 967e22c4b..ac316f8a2 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -10,12 +10,12 @@ import ( type directoryBuilder struct { dserv mdag.DAGService - dirnode *mdag.Node + dirnode *mdag.ProtoNode } // NewEmptyDirectory returns an empty merkledag Node with a folder Data chunk -func NewEmptyDirectory() *mdag.Node { - nd := new(mdag.Node) +func NewEmptyDirectory() *mdag.ProtoNode { + nd := new(mdag.ProtoNode) nd.SetData(format.FolderPBData()) return nd } @@ -35,10 +35,15 @@ func (d *directoryBuilder) AddChild(ctx context.Context, name string, c *cid.Cid return err } - return d.dirnode.AddNodeLinkClean(name, cnode) + cnpb, ok := cnode.(*mdag.ProtoNode) + if !ok { + return mdag.ErrNotProtobuf + } + + return d.dirnode.AddNodeLinkClean(name, cnpb) } // GetNode returns the root of this directoryBuilder -func (d *directoryBuilder) GetNode() *mdag.Node { +func (d *directoryBuilder) GetNode() *mdag.ProtoNode { return d.dirnode } diff --git a/unixfs/io/dirbuilder_test.go b/unixfs/io/dirbuilder_test.go index 80a01d325..e7539a8bc 100644 --- a/unixfs/io/dirbuilder_test.go +++ b/unixfs/io/dirbuilder_test.go @@ -10,7 +10,7 @@ import ( func TestEmptyNode(t *testing.T) { n := NewEmptyDirectory() - if len(n.Links) != 0 { + if len(n.Links()) != 0 { t.Fatal("empty node should have 0 links") } } @@ -27,7 +27,7 @@ func TestDirBuilder(t *testing.T) { b.AddChild(ctx, "random", key) dir := b.GetNode() - outn, err := dir.GetLinkedNode(ctx, dserv, "random") + outn, err := dir.GetLinkedProtoNode(ctx, dserv, "random") if err != nil { t.Fatal(err) } diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 8e3cae16a..3479ab4a4 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -32,7 +32,7 @@ var log = logging.Logger("dagio") // Dear god, please rename this to something more pleasant type DagModifier struct { dagserv mdag.DAGService - curNode *mdag.Node + curNode *mdag.ProtoNode splitter chunk.SplitterGen ctx context.Context @@ -45,7 +45,7 @@ type DagModifier struct { read *uio.DagReader } -func NewDagModifier(ctx context.Context, from *mdag.Node, serv mdag.DAGService, spl chunk.SplitterGen) (*DagModifier, error) { +func NewDagModifier(ctx context.Context, from *mdag.ProtoNode, serv mdag.DAGService, spl chunk.SplitterGen) (*DagModifier, error) { return &DagModifier{ curNode: from.Copy(), dagserv: serv, @@ -178,11 +178,16 @@ func (dm *DagModifier) Sync() error { return err } - dm.curNode = nd + pbnd, ok := nd.(*mdag.ProtoNode) + if !ok { + return mdag.ErrNotProtobuf + } + + dm.curNode = pbnd // need to write past end of current dag if !done { - nd, err = dm.appendData(dm.curNode, dm.splitter(dm.wrBuf)) + nd, err := dm.appendData(dm.curNode, dm.splitter(dm.wrBuf)) if err != nil { return err } @@ -204,14 +209,14 @@ func (dm *DagModifier) Sync() error { // modifyDag writes the data in 'data' over the data in 'node' starting at 'offset' // returns the new key of the passed in node and whether or not all the data in the reader // has been consumed. -func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) (*cid.Cid, bool, error) { +func (dm *DagModifier) modifyDag(node *mdag.ProtoNode, offset uint64, data io.Reader) (*cid.Cid, bool, error) { f, err := ft.FromBytes(node.Data()) if err != nil { return nil, false, err } // If we've reached a leaf node. - if len(node.Links) == 0 { + if len(node.Links()) == 0 { n, err := data.Read(f.Data[offset:]) if err != nil && err != io.EOF { return nil, false, err @@ -223,7 +228,7 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) return nil, false, err } - nd := new(mdag.Node) + nd := new(mdag.ProtoNode) nd.SetData(b) k, err := dm.dagserv.Add(nd) if err != nil { @@ -244,17 +249,23 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) for i, bs := range f.GetBlocksizes() { // We found the correct child to write into if cur+bs > offset { - child, err := node.Links[i].GetNode(dm.ctx, dm.dagserv) + child, err := node.Links()[i].GetNode(dm.ctx, dm.dagserv) if err != nil { return nil, false, err } - k, sdone, err := dm.modifyDag(child, offset-cur, data) + + childpb, ok := child.(*mdag.ProtoNode) + if !ok { + return nil, false, mdag.ErrNotProtobuf + } + + k, sdone, err := dm.modifyDag(childpb, offset-cur, data) if err != nil { return nil, false, err } offset += bs - node.Links[i].Hash = k.Hash() + node.Links()[i].Cid = k // Recache serialized node _, err = node.EncodeProtobuf(true) @@ -277,7 +288,7 @@ func (dm *DagModifier) modifyDag(node *mdag.Node, offset uint64, data io.Reader) } // appendData appends the blocks from the given chan to the end of this dag -func (dm *DagModifier) appendData(node *mdag.Node, spl chunk.Splitter) (*mdag.Node, error) { +func (dm *DagModifier) appendData(node *mdag.ProtoNode, spl chunk.Splitter) (*mdag.ProtoNode, error) { dbp := &help.DagBuilderParams{ Dagserv: dm.dagserv, Maxlinks: help.DefaultLinksPerBlock, @@ -340,7 +351,7 @@ func (dm *DagModifier) CtxReadFull(ctx context.Context, b []byte) (int, error) { } // GetNode gets the modified DAG Node -func (dm *DagModifier) GetNode() (*mdag.Node, error) { +func (dm *DagModifier) GetNode() (*mdag.ProtoNode, error) { err := dm.Sync() if err != nil { return nil, err @@ -425,8 +436,8 @@ func (dm *DagModifier) Truncate(size int64) error { } // dagTruncate truncates the given node to 'size' and returns the modified Node -func dagTruncate(ctx context.Context, nd *mdag.Node, size uint64, ds mdag.DAGService) (*mdag.Node, error) { - if len(nd.Links) == 0 { +func dagTruncate(ctx context.Context, nd *mdag.ProtoNode, size uint64, ds mdag.DAGService) (*mdag.ProtoNode, error) { + if len(nd.Links()) == 0 { // TODO: this can likely be done without marshaling and remarshaling pbn, err := ft.FromBytes(nd.Data()) if err != nil { @@ -439,22 +450,27 @@ func dagTruncate(ctx context.Context, nd *mdag.Node, size uint64, ds mdag.DAGSer var cur uint64 end := 0 - var modified *mdag.Node + var modified *mdag.ProtoNode ndata := new(ft.FSNode) - for i, lnk := range nd.Links { + for i, lnk := range nd.Links() { child, err := lnk.GetNode(ctx, ds) if err != nil { return nil, err } - childsize, err := ft.DataSize(child.Data()) + childpb, ok := child.(*mdag.ProtoNode) + if !ok { + return nil, err + } + + childsize, err := ft.DataSize(childpb.Data()) if err != nil { return nil, err } // found the child we want to cut if size < cur+childsize { - nchild, err := dagTruncate(ctx, child, size-cur, ds) + nchild, err := dagTruncate(ctx, childpb, size-cur, ds) if err != nil { return nil, err } @@ -474,7 +490,7 @@ func dagTruncate(ctx context.Context, nd *mdag.Node, size uint64, ds mdag.DAGSer return nil, err } - nd.Links = nd.Links[:end] + nd.SetLinks(nd.Links()[:end]) err = nd.AddNodeLinkClean("", modified) if err != nil { return nil, err diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index b997a11a8..26755cec5 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -27,7 +27,7 @@ func GetDAGServ() mdag.DAGService { return mdagmock.Mock() } -func GetNode(t testing.TB, dserv mdag.DAGService, data []byte) *mdag.Node { +func GetNode(t testing.TB, dserv mdag.DAGService, data []byte) *mdag.ProtoNode { in := bytes.NewReader(data) node, err := imp.BuildTrickleDagFromReader(dserv, SizeSplitterGen(500)(in)) if err != nil { @@ -37,11 +37,11 @@ func GetNode(t testing.TB, dserv mdag.DAGService, data []byte) *mdag.Node { return node } -func GetEmptyNode(t testing.TB, dserv mdag.DAGService) *mdag.Node { +func GetEmptyNode(t testing.TB, dserv mdag.DAGService) *mdag.ProtoNode { return GetNode(t, dserv, []byte{}) } -func GetRandomNode(t testing.TB, dserv mdag.DAGService, size int64) ([]byte, *mdag.Node) { +func GetRandomNode(t testing.TB, dserv mdag.DAGService, size int64) ([]byte, *mdag.ProtoNode) { in := io.LimitReader(u.NewTimeSeededRand(), size) buf, err := ioutil.ReadAll(in) if err != nil { @@ -64,7 +64,7 @@ func ArrComp(a, b []byte) error { return nil } -func PrintDag(nd *mdag.Node, ds mdag.DAGService, indent int) { +func PrintDag(nd *mdag.ProtoNode, ds mdag.DAGService, indent int) { pbd, err := ft.FromBytes(nd.Data()) if err != nil { panic(err) @@ -74,17 +74,17 @@ func PrintDag(nd *mdag.Node, ds mdag.DAGService, indent int) { fmt.Print(" ") } fmt.Printf("{size = %d, type = %s, children = %d", pbd.GetFilesize(), pbd.GetType().String(), len(pbd.GetBlocksizes())) - if len(nd.Links) > 0 { + if len(nd.Links()) > 0 { fmt.Println() } - for _, lnk := range nd.Links { + for _, lnk := range nd.Links() { child, err := lnk.GetNode(context.Background(), ds) if err != nil { panic(err) } - PrintDag(child, ds, indent+1) + PrintDag(child.(*mdag.ProtoNode), ds, indent+1) } - if len(nd.Links) > 0 { + if len(nd.Links()) > 0 { for i := 0; i < indent; i++ { fmt.Print(" ") } From 0cc119fbe3e79281134fcd011ba56706784d5a43 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 9 Oct 2016 12:59:36 -0700 Subject: [PATCH 1576/3817] merkledag: change 'Node' to be an interface Also change existing 'Node' type to 'ProtoNode' and use that most everywhere for now. As we move forward with the integration we will try and use the Node interface in more places that we're currently using ProtoNode. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@210328aa58edd3b56851df0378c707a322fb1b38 --- blockservice/test/blocks_test.go | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index bda0427b0..956420da2 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -18,18 +18,8 @@ import ( dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) -func newObject(data []byte) *testObject { - return &testObject{ - Block: blocks.NewBlock(data), - } -} - -type testObject struct { - blocks.Block -} - -func (o *testObject) Cid() *cid.Cid { - return cid.NewCidV0(o.Block.Multihash()) +func newObject(data []byte) blocks.Block { + return blocks.NewBlock(data) } func TestBlocks(t *testing.T) { @@ -38,12 +28,8 @@ func TestBlocks(t *testing.T) { defer bs.Close() o := newObject([]byte("beep boop")) - h := u.Hash([]byte("beep boop")) - if !bytes.Equal(o.Multihash(), h) { - t.Error("Block Multihash and data multihash not equal") - } - - if !o.Cid().Equals(cid.NewCidV0(h)) { + h := cid.NewCidV0(u.Hash([]byte("beep boop"))) + if !o.Cid().Equals(h) { t.Error("Block key and data multihash key not equal") } @@ -74,8 +60,8 @@ func TestBlocks(t *testing.T) { } } -func makeObjects(n int) []*testObject { - var out []*testObject +func makeObjects(n int) []blocks.Block { + var out []blocks.Block for i := 0; i < n; i++ { out = append(out, newObject([]byte(fmt.Sprintf("object %d", i)))) } From d7ffbf5f165decc6696c3a5c57a53b7ffe74d0ae Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 14 Oct 2016 07:53:48 -0700 Subject: [PATCH 1577/3817] extract node interface License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@7a8bd733876e95add95b94e83c1fbae577bf8d0d --- mfs/mfs_test.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index f9c79769c..dcec37356 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -2,6 +2,7 @@ package mfs import ( "bytes" + "context" "errors" "fmt" "io" @@ -23,8 +24,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - "context" cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" @@ -291,12 +292,12 @@ func TestDirectoryLoadFromDag(t *testing.T) { dirhash := dir.Cid() top := emptyDirNode() - top.SetLinks([]*dag.Link{ - &dag.Link{ + top.SetLinks([]*node.Link{ + { Name: "a", Cid: fihash, }, - &dag.Link{ + { Name: "b", Cid: dirhash, }, From 04a19ab9a5fd1a498427568f33f9993781e5d828 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 14 Oct 2016 07:53:48 -0700 Subject: [PATCH 1578/3817] extract node interface License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@e23c22044a847c18a4d876cb6d83702f8ca339e2 --- ipld/merkledag/coding.go | 5 +- ipld/merkledag/merkledag.go | 54 ++++++-------- ipld/merkledag/merkledag_test.go | 13 ++-- ipld/merkledag/node.go | 90 ++++++------------------ ipld/merkledag/node_test.go | 33 ++++----- ipld/merkledag/traverse/traverse.go | 17 +++-- ipld/merkledag/traverse/traverse_test.go | 16 +++-- 7 files changed, 87 insertions(+), 141 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 1d1badd3b..c37a63db5 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -7,6 +7,7 @@ import ( pb "github.com/ipfs/go-ipfs/merkledag/pb" cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) @@ -22,9 +23,9 @@ func (n *ProtoNode) unmarshal(encoded []byte) error { } pbnl := pbn.GetLinks() - n.links = make([]*Link, len(pbnl)) + n.links = make([]*node.Link, len(pbnl)) for i, l := range pbnl { - n.links[i] = &Link{Name: l.GetName(), Size: l.GetTsize()} + n.links[i] = &node.Link{Name: l.GetName(), Size: l.GetTsize()} c, err := cid.Cast(l.GetHash()) if err != nil { return fmt.Errorf("Link hash #%d is not valid multihash. %v", i, err) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 16125a1fd..223927892 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -13,6 +13,7 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) var log = logging.Logger("merkledag") @@ -20,9 +21,9 @@ var ErrNotFound = fmt.Errorf("merkledag: not found") // DAGService is an IPFS Merkle DAG service. type DAGService interface { - Add(Node) (*cid.Cid, error) - Get(context.Context, *cid.Cid) (Node, error) - Remove(Node) error + Add(node.Node) (*cid.Cid, error) + Get(context.Context, *cid.Cid) (node.Node, error) + Remove(node.Node) error // GetDAG returns, in order, all the single leve child // nodes of the passed in node. @@ -36,7 +37,7 @@ type DAGService interface { type LinkService interface { // Return all links for a node, may be more effect than // calling Get in DAGService - GetLinks(context.Context, *cid.Cid) ([]*Link, error) + GetLinks(context.Context, *cid.Cid) ([]*node.Link, error) GetOfflineLinkService() LinkService } @@ -45,19 +46,6 @@ func NewDAGService(bs bserv.BlockService) *dagService { return &dagService{Blocks: bs} } -type Node interface { - Resolve(path []string) (*Link, []string, error) - Links() []*Link - Tree() []string - - Stat() (*NodeStat, error) - Size() (uint64, error) - Cid() *cid.Cid - Loggable() map[string]interface{} - RawData() []byte - String() string -} - // dagService is an IPFS Merkle DAG service. // - the root is virtual (like a forest) // - stores nodes' data in a BlockService @@ -68,7 +56,7 @@ type dagService struct { } // Add adds a node to the dagService, storing the block in the BlockService -func (n *dagService) Add(nd Node) (*cid.Cid, error) { +func (n *dagService) Add(nd node.Node) (*cid.Cid, error) { if n == nil { // FIXME remove this assertion. protect with constructor invariant return nil, fmt.Errorf("dagService is nil") } @@ -81,7 +69,7 @@ func (n *dagService) Batch() *Batch { } // Get retrieves a node from the dagService, fetching the block in the BlockService -func (n *dagService) Get(ctx context.Context, c *cid.Cid) (Node, error) { +func (n *dagService) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { if n == nil { return nil, fmt.Errorf("dagService is nil") } @@ -97,7 +85,7 @@ func (n *dagService) Get(ctx context.Context, c *cid.Cid) (Node, error) { return nil, fmt.Errorf("Failed to get block for %s: %v", c, err) } - var res Node + var res node.Node switch c.Type() { case cid.Protobuf: out, err := DecodeProtobuf(b.RawData()) @@ -116,7 +104,7 @@ func (n *dagService) Get(ctx context.Context, c *cid.Cid) (Node, error) { return res, nil } -func (n *dagService) GetLinks(ctx context.Context, c *cid.Cid) ([]*Link, error) { +func (n *dagService) GetLinks(ctx context.Context, c *cid.Cid) ([]*node.Link, error) { node, err := n.Get(ctx, c) if err != nil { return nil, err @@ -133,7 +121,7 @@ func (n *dagService) GetOfflineLinkService() LinkService { } } -func (n *dagService) Remove(nd Node) error { +func (n *dagService) Remove(nd node.Node) error { return n.Blocks.DeleteBlock(nd) } @@ -155,7 +143,7 @@ func FindLinks(links []*cid.Cid, c *cid.Cid, start int) []int { } type NodeOption struct { - Node Node + Node node.Node Err error } @@ -178,7 +166,7 @@ func (ds *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *Node c := b.Cid() - var nd Node + var nd node.Node switch c.Type() { case cid.Protobuf: decnd, err := DecodeProtobuf(b.RawData()) @@ -209,7 +197,7 @@ func (ds *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *Node // GetDAG will fill out all of the links of the given Node. // It returns a channel of nodes, which the caller can receive // all the child nodes of 'root' on, in proper order. -func GetDAG(ctx context.Context, ds DAGService, root Node) []NodeGetter { +func GetDAG(ctx context.Context, ds DAGService, root node.Node) []NodeGetter { var cids []*cid.Cid for _, lnk := range root.Links() { cids = append(cids, lnk.Cid) @@ -281,16 +269,16 @@ func dedupeKeys(cids []*cid.Cid) []*cid.Cid { func newNodePromise(ctx context.Context) NodeGetter { return &nodePromise{ - recv: make(chan Node, 1), + recv: make(chan node.Node, 1), ctx: ctx, err: make(chan error, 1), } } type nodePromise struct { - cache Node + cache node.Node clk sync.Mutex - recv chan Node + recv chan node.Node ctx context.Context err chan error } @@ -300,9 +288,9 @@ type nodePromise struct { // from its internal channels, subsequent calls will return the // cached node. type NodeGetter interface { - Get(context.Context) (Node, error) + Get(context.Context) (node.Node, error) Fail(err error) - Send(Node) + Send(node.Node) } func (np *nodePromise) Fail(err error) { @@ -318,7 +306,7 @@ func (np *nodePromise) Fail(err error) { np.err <- err } -func (np *nodePromise) Send(nd Node) { +func (np *nodePromise) Send(nd node.Node) { var already bool np.clk.Lock() if np.cache != nil { @@ -334,7 +322,7 @@ func (np *nodePromise) Send(nd Node) { np.recv <- nd } -func (np *nodePromise) Get(ctx context.Context) (Node, error) { +func (np *nodePromise) Get(ctx context.Context) (node.Node, error) { np.clk.Lock() c := np.cache np.clk.Unlock() @@ -362,7 +350,7 @@ type Batch struct { MaxSize int } -func (t *Batch) Add(nd Node) (*cid.Cid, error) { +func (t *Batch) Add(nd node.Node) (*cid.Cid, error) { t.blocks = append(t.blocks, nd) t.size += len(nd.RawData()) if t.size > t.MaxSize { diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 9ade523a7..310134fa0 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -2,6 +2,7 @@ package merkledag_test import ( "bytes" + "context" "errors" "fmt" "io" @@ -19,10 +20,10 @@ import ( mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - "context" cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) @@ -85,7 +86,7 @@ func SubtestNodeStat(t *testing.T, n *ProtoNode) { k := n.Key() - expected := NodeStat{ + expected := node.NodeStat{ NumLinks: len(n.Links()), BlockSize: len(enc), LinksSize: len(enc) - len(n.Data()), // includes framing. @@ -206,7 +207,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { } } -func assertCanGet(t *testing.T, ds DAGService, n Node) { +func assertCanGet(t *testing.T, ds DAGService, n node.Node) { if _, err := ds.Get(context.Background(), n.Cid()); err != nil { t.Fatal(err) } @@ -268,8 +269,8 @@ func TestEnumerateChildren(t *testing.T) { t.Fatal(err) } - var traverse func(n Node) - traverse = func(n Node) { + var traverse func(n node.Node) + traverse = func(n node.Node) { // traverse dag and check for _, lnk := range n.Links() { c := lnk.Cid diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index ca2d21021..4c01c9c9c 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -1,21 +1,22 @@ package merkledag import ( - "fmt" - "context" + "fmt" cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) +var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") var ErrLinkNotFound = fmt.Errorf("no link by that name") // Node represents a node in the IPFS Merkle DAG. // nodes have opaque data and a set of navigable links. type ProtoNode struct { - links []*Link + links []*node.Link data []byte // cache encoded/marshaled value @@ -24,57 +25,12 @@ type ProtoNode struct { cached *cid.Cid } -// NodeStat is a statistics object for a Node. Mostly sizes. -type NodeStat struct { - Hash string - NumLinks int // number of links in link table - BlockSize int // size of the raw, encoded data - LinksSize int // size of the links segment - DataSize int // size of the data segment - CumulativeSize int // cumulative size of object and its references -} - -func (ns NodeStat) String() string { - f := "NodeStat{NumLinks: %d, BlockSize: %d, LinksSize: %d, DataSize: %d, CumulativeSize: %d}" - return fmt.Sprintf(f, ns.NumLinks, ns.BlockSize, ns.LinksSize, ns.DataSize, ns.CumulativeSize) -} - -// Link represents an IPFS Merkle DAG Link between Nodes. -type Link struct { - // utf string name. should be unique per object - Name string // utf8 - - // cumulative size of target object - Size uint64 - - // multihash of the target object - Cid *cid.Cid -} - -type LinkSlice []*Link +type LinkSlice []*node.Link func (ls LinkSlice) Len() int { return len(ls) } func (ls LinkSlice) Swap(a, b int) { ls[a], ls[b] = ls[b], ls[a] } func (ls LinkSlice) Less(a, b int) bool { return ls[a].Name < ls[b].Name } -// MakeLink creates a link to the given node -func MakeLink(n Node) (*Link, error) { - s, err := n.Size() - if err != nil { - return nil, err - } - - return &Link{ - Size: s, - Cid: n.Cid(), - }, nil -} - -// GetNode returns the MDAG Node that this link points to -func (l *Link) GetNode(ctx context.Context, serv DAGService) (Node, error) { - return serv.Get(ctx, l.Cid) -} - func NodeWithData(d []byte) *ProtoNode { return &ProtoNode{data: d} } @@ -83,13 +39,13 @@ func NodeWithData(d []byte) *ProtoNode { func (n *ProtoNode) AddNodeLink(name string, that *ProtoNode) error { n.encoded = nil - lnk, err := MakeLink(that) - - lnk.Name = name + lnk, err := node.MakeLink(that) if err != nil { return err } + lnk.Name = name + n.AddRawLink(name, lnk) return nil @@ -97,9 +53,9 @@ func (n *ProtoNode) AddNodeLink(name string, that *ProtoNode) error { // AddNodeLinkClean adds a link to another node. without keeping a reference to // the child node -func (n *ProtoNode) AddNodeLinkClean(name string, that Node) error { +func (n *ProtoNode) AddNodeLinkClean(name string, that node.Node) error { n.encoded = nil - lnk, err := MakeLink(that) + lnk, err := node.MakeLink(that) if err != nil { return err } @@ -109,9 +65,9 @@ func (n *ProtoNode) AddNodeLinkClean(name string, that Node) error { } // AddRawLink adds a copy of a link to this node -func (n *ProtoNode) AddRawLink(name string, l *Link) error { +func (n *ProtoNode) AddRawLink(name string, l *node.Link) error { n.encoded = nil - n.links = append(n.links, &Link{ + n.links = append(n.links, &node.Link{ Name: name, Size: l.Size, Cid: l.Cid, @@ -123,7 +79,7 @@ func (n *ProtoNode) AddRawLink(name string, l *Link) error { // Remove a link on this node by the given name func (n *ProtoNode) RemoveNodeLink(name string) error { n.encoded = nil - good := make([]*Link, 0, len(n.links)) + good := make([]*node.Link, 0, len(n.links)) var found bool for _, l := range n.links { @@ -143,10 +99,10 @@ func (n *ProtoNode) RemoveNodeLink(name string) error { } // Return a copy of the link with given name -func (n *ProtoNode) GetNodeLink(name string) (*Link, error) { +func (n *ProtoNode) GetNodeLink(name string) (*node.Link, error) { for _, l := range n.links { if l.Name == name { - return &Link{ + return &node.Link{ Name: l.Name, Size: l.Size, Cid: l.Cid, @@ -156,8 +112,6 @@ func (n *ProtoNode) GetNodeLink(name string) (*Link, error) { return nil, ErrLinkNotFound } -var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") - func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds DAGService, name string) (*ProtoNode, error) { nd, err := n.GetLinkedNode(ctx, ds, name) if err != nil { @@ -172,7 +126,7 @@ func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds DAGService, name return pbnd, nil } -func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds DAGService, name string) (Node, error) { +func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds DAGService, name string) (node.Node, error) { lnk, err := n.GetNodeLink(name) if err != nil { return nil, err @@ -191,7 +145,7 @@ func (n *ProtoNode) Copy() *ProtoNode { } if len(n.links) > 0 { - nnode.links = make([]*Link, len(n.links)) + nnode.links = make([]*node.Link, len(n.links)) copy(nnode.links, n.links) } return nnode @@ -238,7 +192,7 @@ func (n *ProtoNode) Size() (uint64, error) { } // Stat returns statistics on the node. -func (n *ProtoNode) Stat() (*NodeStat, error) { +func (n *ProtoNode) Stat() (*node.NodeStat, error) { enc, err := n.EncodeProtobuf(false) if err != nil { return nil, err @@ -249,7 +203,7 @@ func (n *ProtoNode) Stat() (*NodeStat, error) { return nil, err } - return &NodeStat{ + return &node.NodeStat{ Hash: n.Key().B58String(), NumLinks: len(n.links), BlockSize: len(enc), @@ -291,15 +245,15 @@ func (n *ProtoNode) Multihash() mh.Multihash { return n.cached.Hash() } -func (n *ProtoNode) Links() []*Link { +func (n *ProtoNode) Links() []*node.Link { return n.links } -func (n *ProtoNode) SetLinks(links []*Link) { +func (n *ProtoNode) SetLinks(links []*node.Link) { n.links = links } -func (n *ProtoNode) Resolve(path []string) (*Link, []string, error) { +func (n *ProtoNode) Resolve(path []string) (*node.Link, []string, error) { if len(path) == 0 { return nil, nil, fmt.Errorf("end of path, no more links to resolve") } diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index 4054d6b93..392a51ea2 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -1,23 +1,24 @@ package merkledag_test import ( + "context" "testing" . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - "context" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) func TestRemoveLink(t *testing.T) { nd := &ProtoNode{} - nd.SetLinks([]*Link{ - &Link{Name: "a"}, - &Link{Name: "b"}, - &Link{Name: "a"}, - &Link{Name: "a"}, - &Link{Name: "c"}, - &Link{Name: "a"}, + nd.SetLinks([]*node.Link{ + {Name: "a"}, + {Name: "b"}, + {Name: "a"}, + {Name: "a"}, + {Name: "c"}, + {Name: "a"}, }) err := nd.RemoveNodeLink("a") @@ -65,10 +66,10 @@ func TestFindLink(t *testing.T) { } nd := &ProtoNode{} - nd.SetLinks([]*Link{ - &Link{Name: "a", Cid: k}, - &Link{Name: "c", Cid: k}, - &Link{Name: "b", Cid: k}, + nd.SetLinks([]*node.Link{ + {Name: "a", Cid: k}, + {Name: "c", Cid: k}, + {Name: "b", Cid: k}, }) _, err = ds.Add(nd) @@ -112,10 +113,10 @@ func TestFindLink(t *testing.T) { func TestNodeCopy(t *testing.T) { nd := &ProtoNode{} - nd.SetLinks([]*Link{ - &Link{Name: "a"}, - &Link{Name: "c"}, - &Link{Name: "b"}, + nd.SetLinks([]*node.Link{ + {Name: "a"}, + {Name: "c"}, + {Name: "b"}, }) nd.SetData([]byte("testing")) diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index fdc06d2cd..17e1b666c 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -2,11 +2,10 @@ package traverse import ( - "errors" - "context" + "errors" - mdag "github.com/ipfs/go-ipfs/merkledag" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) // Order is an identifier for traversal algorithm orders @@ -20,7 +19,7 @@ const ( // Options specifies a series of traversal options type Options struct { - DAG mdag.DAGService // the dagservice to fetch nodes + DAG node.NodeGetter // the dagservice to fetch nodes Order Order // what order to traverse in Func Func // the function to perform at each step ErrFunc ErrFunc // see ErrFunc. Optional @@ -30,7 +29,7 @@ type Options struct { // State is a current traversal state type State struct { - Node mdag.Node + Node node.Node Depth int } @@ -39,7 +38,7 @@ type traversal struct { seen map[string]struct{} } -func (t *traversal) shouldSkip(n mdag.Node) (bool, error) { +func (t *traversal) shouldSkip(n node.Node) (bool, error) { if t.opts.SkipDuplicates { k := n.Cid() if _, found := t.seen[k.KeyString()]; found { @@ -59,9 +58,9 @@ func (t *traversal) callFunc(next State) error { // stop processing. if it returns a nil node, just skip it. // // the error handling is a little complicated. -func (t *traversal) getNode(link *mdag.Link) (mdag.Node, error) { +func (t *traversal) getNode(link *node.Link) (node.Node, error) { - getNode := func(l *mdag.Link) (mdag.Node, error) { + getNode := func(l *node.Link) (node.Node, error) { next, err := l.GetNode(context.TODO(), t.opts.DAG) if err != nil { return nil, err @@ -99,7 +98,7 @@ type Func func(current State) error // type ErrFunc func(err error) error -func Traverse(root mdag.Node, o Options) error { +func Traverse(root node.Node, o Options) error { t := traversal{ opts: o, seen: map[string]struct{}{}, diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index c7dd93a47..fc8d053fa 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -7,6 +7,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagtest "github.com/ipfs/go-ipfs/merkledag/test" + + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) func TestDFSPreNoSkip(t *testing.T) { @@ -321,7 +323,7 @@ func TestBFSSkip(t *testing.T) { `)) } -func testWalkOutputs(t *testing.T, root mdag.Node, opts Options, expect []byte) { +func testWalkOutputs(t *testing.T, root node.Node, opts Options, expect []byte) { expect = bytes.TrimLeft(expect, "\n") buf := new(bytes.Buffer) @@ -348,7 +350,7 @@ func testWalkOutputs(t *testing.T, root mdag.Node, opts Options, expect []byte) } } -func newFan(t *testing.T, ds mdag.DAGService) mdag.Node { +func newFan(t *testing.T, ds mdag.DAGService) node.Node { a := mdag.NodeWithData([]byte("/a")) addLink(t, ds, a, child(t, ds, a, "aa")) addLink(t, ds, a, child(t, ds, a, "ab")) @@ -357,7 +359,7 @@ func newFan(t *testing.T, ds mdag.DAGService) mdag.Node { return a } -func newLinkedList(t *testing.T, ds mdag.DAGService) mdag.Node { +func newLinkedList(t *testing.T, ds mdag.DAGService) node.Node { a := mdag.NodeWithData([]byte("/a")) aa := child(t, ds, a, "aa") aaa := child(t, ds, aa, "aaa") @@ -370,7 +372,7 @@ func newLinkedList(t *testing.T, ds mdag.DAGService) mdag.Node { return a } -func newBinaryTree(t *testing.T, ds mdag.DAGService) mdag.Node { +func newBinaryTree(t *testing.T, ds mdag.DAGService) node.Node { a := mdag.NodeWithData([]byte("/a")) aa := child(t, ds, a, "aa") ab := child(t, ds, a, "ab") @@ -383,7 +385,7 @@ func newBinaryTree(t *testing.T, ds mdag.DAGService) mdag.Node { return a } -func newBinaryDAG(t *testing.T, ds mdag.DAGService) mdag.Node { +func newBinaryDAG(t *testing.T, ds mdag.DAGService) node.Node { a := mdag.NodeWithData([]byte("/a")) aa := child(t, ds, a, "aa") aaa := child(t, ds, aa, "aaa") @@ -400,7 +402,7 @@ func newBinaryDAG(t *testing.T, ds mdag.DAGService) mdag.Node { return a } -func addLink(t *testing.T, ds mdag.DAGService, a, b mdag.Node) { +func addLink(t *testing.T, ds mdag.DAGService, a, b node.Node) { to := string(a.(*mdag.ProtoNode).Data()) + "2" + string(b.(*mdag.ProtoNode).Data()) if _, err := ds.Add(b); err != nil { t.Error(err) @@ -410,6 +412,6 @@ func addLink(t *testing.T, ds mdag.DAGService, a, b mdag.Node) { } } -func child(t *testing.T, ds mdag.DAGService, a mdag.Node, name string) mdag.Node { +func child(t *testing.T, ds mdag.DAGService, a node.Node, name string) node.Node { return mdag.NodeWithData([]byte(string(a.(*mdag.ProtoNode).Data()) + "/" + name)) } From 933a4315576ca66edc1084868620c8c69e6f1ff0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 14 Oct 2016 07:53:48 -0700 Subject: [PATCH 1579/3817] extract node interface License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@5158c31c19713b1c4127fa3f09d9c5dbb8efebda --- path/resolver.go | 9 +++++---- path/resolver_test.go | 7 ++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index 14ed1d87c..e4bfe8f79 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -11,6 +11,7 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) var log = logging.Logger("path") @@ -61,7 +62,7 @@ func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) { // ResolvePath fetches the node for given path. It returns the last item // returned by ResolvePathComponents. -func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (merkledag.Node, error) { +func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (node.Node, error) { // validate path if err := fpath.IsValid(); err != nil { return nil, err @@ -77,7 +78,7 @@ func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (merkledag.Node, // ResolvePathComponents fetches the nodes for each segment of the given path. // It uses the first path component as a hash (key) of the first node, then // resolves all other components walking the links, with ResolveLinks. -func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]merkledag.Node, error) { +func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]node.Node, error) { h, parts, err := SplitAbsPath(fpath) if err != nil { return nil, err @@ -99,9 +100,9 @@ func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]mer // // ResolveLinks(nd, []string{"foo", "bar", "baz"}) // would retrieve "baz" in ("bar" in ("foo" in nd.Links).Links).Links -func (s *Resolver) ResolveLinks(ctx context.Context, ndd merkledag.Node, names []string) ([]merkledag.Node, error) { +func (s *Resolver) ResolveLinks(ctx context.Context, ndd node.Node, names []string) ([]node.Node, error) { - result := make([]merkledag.Node, 0, len(names)+1) + result := make([]node.Node, 0, len(names)+1) result = append(result, ndd) nd := ndd // dup arg workaround diff --git a/path/resolver_test.go b/path/resolver_test.go index b0130bb17..652f38796 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -1,15 +1,16 @@ package path_test import ( + "context" "fmt" "testing" - context "context" - merkledag "github.com/ipfs/go-ipfs/merkledag" dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" + key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" util "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) @@ -39,7 +40,7 @@ func TestRecurivePathResolution(t *testing.T) { t.Fatal(err) } - for _, n := range []merkledag.Node{a, b, c} { + for _, n := range []node.Node{a, b, c} { _, err = dagService.Add(n) if err != nil { t.Fatal(err) From 40ba80e69b56352fac75896731c82545325bb3ca Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 14 Oct 2016 07:53:48 -0700 Subject: [PATCH 1580/3817] extract node interface License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@11add1c863910f892792585ac152d08ee4056389 --- pinning/pinner/pin.go | 18 ++++++++---------- pinning/pinner/set.go | 16 +++++++++------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 4a59e78d9..10c60c256 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -3,17 +3,17 @@ package pin import ( + "context" "fmt" "os" "sync" "time" mdag "github.com/ipfs/go-ipfs/merkledag" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - context "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) @@ -83,7 +83,7 @@ func StringToPinMode(s string) (PinMode, bool) { type Pinner interface { IsPinned(*cid.Cid) (string, bool, error) IsPinnedWithType(*cid.Cid, PinMode) (string, bool, error) - Pin(context.Context, mdag.Node, bool) error + Pin(context.Context, node.Node, bool) error Unpin(context.Context, *cid.Cid, bool) error // Check if a set of keys are pinned, more efficient than @@ -162,11 +162,10 @@ func NewPinner(dstore ds.Datastore, serv, internal mdag.DAGService) Pinner { } // Pin the given node, optionally recursive -func (p *pinner) Pin(ctx context.Context, node mdag.Node, recurse bool) error { +func (p *pinner) Pin(ctx context.Context, node node.Node, recurse bool) error { p.lock.Lock() defer p.lock.Unlock() c := node.Cid() - k := key.Key(c.Hash()) if recurse { if p.recursePin.Has(c) { @@ -190,7 +189,7 @@ func (p *pinner) Pin(ctx context.Context, node mdag.Node, recurse bool) error { } if p.recursePin.Has(c) { - return fmt.Errorf("%s already pinned recursively", k.B58String()) + return fmt.Errorf("%s already pinned recursively", c.String()) } p.directPin.Add(c) @@ -248,7 +247,6 @@ func (p *pinner) IsPinnedWithType(c *cid.Cid, mode PinMode) (string, bool, error // isPinnedWithType is the implementation of IsPinnedWithType that does not lock. // intended for use by other pinned methods that already take locks func (p *pinner) isPinnedWithType(c *cid.Cid, mode PinMode) (string, bool, error) { - k := key.Key(c.Hash()) switch mode { case Any, Direct, Indirect, Recursive, Internal: default: @@ -279,7 +277,7 @@ func (p *pinner) isPinnedWithType(c *cid.Cid, mode PinMode) (string, bool, error // Default is Indirect for _, rc := range p.recursePin.Keys() { - has, err := hasChild(p.dserv, rc, k) + has, err := hasChild(p.dserv, rc, c) if err != nil { return "", false, err } @@ -521,14 +519,14 @@ func (p *pinner) PinWithMode(c *cid.Cid, mode PinMode) { } } -func hasChild(ds mdag.LinkService, root *cid.Cid, child key.Key) (bool, error) { +func hasChild(ds mdag.LinkService, root *cid.Cid, child *cid.Cid) (bool, error) { links, err := ds.GetLinks(context.Background(), root) if err != nil { return false, err } for _, lnk := range links { c := lnk.Cid - if key.Key(c.Hash()) == child { + if lnk.Cid.Equals(child) { return true, nil } diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 1a1f9f3bf..eaaba7884 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -12,9 +12,11 @@ import ( "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) const ( @@ -47,7 +49,7 @@ type itemIterator func() (c *cid.Cid, ok bool) type keyObserver func(*cid.Cid) type sortByHash struct { - links []*merkledag.Link + links []*node.Link } func (s sortByHash) Len() int { @@ -67,9 +69,9 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint if err != nil { return nil, err } - links := make([]*merkledag.Link, 0, defaultFanout+maxItems) + links := make([]*node.Link, 0, defaultFanout+maxItems) for i := 0; i < defaultFanout; i++ { - links = append(links, &merkledag.Link{Cid: emptyKey}) + links = append(links, &node.Link{Cid: emptyKey}) } // add emptyKey to our set of internal pinset objects @@ -97,7 +99,7 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint break } - links = append(links, &merkledag.Link{Cid: k}) + links = append(links, &node.Link{Cid: k}) } n.SetLinks(links) @@ -159,7 +161,7 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint internalKeys(childKey) // overwrite the 'empty key' in the existing links array - n.Links()[h] = &merkledag.Link{ + n.Links()[h] = &node.Link{ Cid: childKey, Size: size, } @@ -212,7 +214,7 @@ func writeHdr(n *merkledag.ProtoNode, hdr *pb.Set) error { return nil } -type walkerFunc func(idx int, link *merkledag.Link) error +type walkerFunc func(idx int, link *node.Link) error func walkItems(ctx context.Context, dag merkledag.DAGService, n *merkledag.ProtoNode, fn walkerFunc, children keyObserver) error { hdr, err := readHdr(n) @@ -269,7 +271,7 @@ func loadSet(ctx context.Context, dag merkledag.DAGService, root *merkledag.Prot } var res []*cid.Cid - walk := func(idx int, link *merkledag.Link) error { + walk := func(idx int, link *node.Link) error { res = append(res, link.Cid) return nil } From 467e22510c8ed2082589cacd78e6ed3cb135249a Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 17 Oct 2016 18:13:07 -0400 Subject: [PATCH 1581/3817] ds-help: add helper func to convert from Cid to DsKey and the reverse License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-blockstore@05752dceaa35d49d149c109b74654388a740b768 --- blockstore/blockstore.go | 17 ++++++----------- blockstore/blockstore_test.go | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index dfa35ec41..861863d9d 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -87,7 +87,7 @@ func (bs *blockstore) Get(k *cid.Cid) (blocks.Block, error) { return nil, ErrNotFound } - maybeData, err := bs.datastore.Get(dshelp.NewKeyFromBinary(k.KeyString())) + maybeData, err := bs.datastore.Get(dshelp.CidToDsKey(k)) if err == ds.ErrNotFound { return nil, ErrNotFound } @@ -112,7 +112,7 @@ func (bs *blockstore) Get(k *cid.Cid) (blocks.Block, error) { } func (bs *blockstore) Put(block blocks.Block) error { - k := dshelp.NewKeyFromBinary(block.Cid().KeyString()) + k := dshelp.CidToDsKey(block.Cid()) // Has is cheaper than Put, so see if we already have it exists, err := bs.datastore.Has(k) @@ -128,7 +128,7 @@ func (bs *blockstore) PutMany(blocks []blocks.Block) error { return err } for _, b := range blocks { - k := dshelp.NewKeyFromBinary(b.Cid().KeyString()) + k := dshelp.CidToDsKey(b.Cid()) exists, err := bs.datastore.Has(k) if err == nil && exists { continue @@ -143,11 +143,11 @@ func (bs *blockstore) PutMany(blocks []blocks.Block) error { } func (bs *blockstore) Has(k *cid.Cid) (bool, error) { - return bs.datastore.Has(dshelp.NewKeyFromBinary(k.KeyString())) + return bs.datastore.Has(dshelp.CidToDsKey(k)) } func (s *blockstore) DeleteBlock(k *cid.Cid) error { - return s.datastore.Delete(dshelp.NewKeyFromBinary(k.KeyString())) + return s.datastore.Delete(dshelp.CidToDsKey(k)) } // AllKeysChan runs a query for keys from the blockstore. @@ -180,17 +180,12 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) } // need to convert to key.Key using key.KeyFromDsKey. - kb, err := dshelp.BinaryFromDsKey(ds.NewKey(e.Key)) // TODO: calling NewKey isnt free + c, err := dshelp.DsKeyToCid(ds.NewKey(e.Key)) // TODO: calling NewKey isnt free if err != nil { log.Warningf("error parsing key from DsKey: ", err) return nil, true } - c, err := cid.Cast(kb) - if err != nil { - log.Warning("error parsing cid from decoded DsKey: ", err) - return nil, true - } log.Debug("blockstore: query got key", c) return c, true diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index a5ecefd44..4c1a4db88 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -190,7 +190,7 @@ func TestValueTypeMismatch(t *testing.T) { block := blocks.NewBlock([]byte("some data")) datastore := ds.NewMapDatastore() - k := BlockPrefix.Child(dshelp.NewKeyFromBinary(block.Cid().KeyString())) + k := BlockPrefix.Child(dshelp.CidToDsKey(block.Cid())) datastore.Put(k, "data that isn't a block!") blockstore := NewBlockstore(ds_sync.MutexWrap(datastore)) From 992aa07fa54ef0ad89f156769dcea6027b46fadc Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 17 Oct 2016 18:13:07 -0400 Subject: [PATCH 1582/3817] ds-help: add helper func to convert from Cid to DsKey and the reverse License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-ds-help@e6e00eda4cde5fd07edcaff0c2246567b8dfa98c --- datastore/dshelp/key.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 417f49605..7db86aedb 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -3,6 +3,7 @@ package dshelp import ( base32 "gx/ipfs/Qmb1DA2A9LS2wR4FFweB4uEDomFsdmnw1VLawLE1yQzudj/base32" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) // TODO: put this code into the go-datastore itself @@ -13,3 +14,15 @@ func NewKeyFromBinary(s string) ds.Key { func BinaryFromDsKey(k ds.Key) ([]byte, error) { return base32.RawStdEncoding.DecodeString(k.String()[1:]) } + +func CidToDsKey(k *cid.Cid) ds.Key { + return NewKeyFromBinary(k.KeyString()) +} + +func DsKeyToCid(dsKey ds.Key) (*cid.Cid, error) { + kb, err := BinaryFromDsKey(dsKey) + if err != nil { + return nil, err + } + return cid.Cast(kb) +} From d98265033d89af122cb363cfb5fa7cd23a45e3b4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 15 Oct 2016 09:06:44 -0700 Subject: [PATCH 1583/3817] unixfs: allow use of raw merkledag nodes for unixfs files License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@f47e36a5e8caa016533eaf857b62aeaa00d16655 --- unixfs/io/dagreader.go | 66 ++++++++++++++++++++++----------------- unixfs/mod/dagmodifier.go | 29 +++++++++++++---- unixfs/test/utils.go | 9 +++--- 3 files changed, 66 insertions(+), 38 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 086e5038c..4eb3e04c6 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -2,17 +2,18 @@ package io import ( "bytes" + "context" "errors" "fmt" "io" "os" - "context" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" + + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) var ErrIsDir = errors.New("this dag node is a directory") @@ -58,36 +59,45 @@ type ReadSeekCloser interface { // NewDagReader creates a new reader object that reads the data represented by // the given node, using the passed in DAGService for data retreival -func NewDagReader(ctx context.Context, n *mdag.ProtoNode, serv mdag.DAGService) (*DagReader, error) { - pb := new(ftpb.Data) - if err := proto.Unmarshal(n.Data(), pb); err != nil { - return nil, err - } - - switch pb.GetType() { - case ftpb.Data_Directory: - // Dont allow reading directories - return nil, ErrIsDir - case ftpb.Data_File, ftpb.Data_Raw: - return NewDataFileReader(ctx, n, pb, serv), nil - case ftpb.Data_Metadata: - if len(n.Links()) == 0 { - return nil, errors.New("incorrectly formatted metadata object") - } - child, err := n.Links()[0].GetNode(ctx, serv) - if err != nil { +func NewDagReader(ctx context.Context, n node.Node, serv mdag.DAGService) (*DagReader, error) { + switch n := n.(type) { + case *mdag.RawNode: + return &DagReader{ + buf: NewRSNCFromBytes(n.RawData()), + }, nil + case *mdag.ProtoNode: + pb := new(ftpb.Data) + if err := proto.Unmarshal(n.Data(), pb); err != nil { return nil, err } - childpb, ok := child.(*mdag.ProtoNode) - if !ok { - return nil, mdag.ErrNotProtobuf + switch pb.GetType() { + case ftpb.Data_Directory: + // Dont allow reading directories + return nil, ErrIsDir + case ftpb.Data_File, ftpb.Data_Raw: + return NewDataFileReader(ctx, n, pb, serv), nil + case ftpb.Data_Metadata: + if len(n.Links()) == 0 { + return nil, errors.New("incorrectly formatted metadata object") + } + child, err := n.Links()[0].GetNode(ctx, serv) + if err != nil { + return nil, err + } + + childpb, ok := child.(*mdag.ProtoNode) + if !ok { + return nil, mdag.ErrNotProtobuf + } + return NewDagReader(ctx, childpb, serv) + case ftpb.Data_Symlink: + return nil, ErrCantReadSymlinks + default: + return nil, ft.ErrUnrecognizedType } - return NewDagReader(ctx, childpb, serv) - case ftpb.Data_Symlink: - return nil, ErrCantReadSymlinks default: - return nil, ft.ErrUnrecognizedType + return nil, fmt.Errorf("unrecognized node type") } } diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 3479ab4a4..fe59436ee 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -2,6 +2,7 @@ package mod import ( "bytes" + "context" "errors" "io" "os" @@ -13,10 +14,10 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - context "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) var ErrSeekFail = errors.New("failed to seek properly") @@ -45,9 +46,14 @@ type DagModifier struct { read *uio.DagReader } -func NewDagModifier(ctx context.Context, from *mdag.ProtoNode, serv mdag.DAGService, spl chunk.SplitterGen) (*DagModifier, error) { +func NewDagModifier(ctx context.Context, from node.Node, serv mdag.DAGService, spl chunk.SplitterGen) (*DagModifier, error) { + pbn, ok := from.(*mdag.ProtoNode) + if !ok { + return nil, mdag.ErrNotProtobuf + } + return &DagModifier{ - curNode: from.Copy(), + curNode: pbn.Copy(), dagserv: serv, splitter: spl, ctx: ctx, @@ -109,7 +115,13 @@ func (dm *DagModifier) expandSparse(size int64) error { if err != nil { return err } - dm.curNode = nnode + + pbnnode, ok := nnode.(*mdag.ProtoNode) + if !ok { + return mdag.ErrNotProtobuf + } + + dm.curNode = pbnnode return nil } @@ -197,7 +209,12 @@ func (dm *DagModifier) Sync() error { return err } - dm.curNode = nd + pbnode, ok := nd.(*mdag.ProtoNode) + if !ok { + return mdag.ErrNotProtobuf + } + + dm.curNode = pbnode } dm.writeStart += uint64(buflen) @@ -288,7 +305,7 @@ func (dm *DagModifier) modifyDag(node *mdag.ProtoNode, offset uint64, data io.Re } // appendData appends the blocks from the given chan to the end of this dag -func (dm *DagModifier) appendData(node *mdag.ProtoNode, spl chunk.Splitter) (*mdag.ProtoNode, error) { +func (dm *DagModifier) appendData(node *mdag.ProtoNode, spl chunk.Splitter) (node.Node, error) { dbp := &help.DagBuilderParams{ Dagserv: dm.dagserv, Maxlinks: help.DefaultLinksPerBlock, diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 26755cec5..abe292300 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -2,6 +2,7 @@ package testu import ( "bytes" + "context" "fmt" "io" "io/ioutil" @@ -13,7 +14,7 @@ import ( mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" - context "context" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) @@ -27,7 +28,7 @@ func GetDAGServ() mdag.DAGService { return mdagmock.Mock() } -func GetNode(t testing.TB, dserv mdag.DAGService, data []byte) *mdag.ProtoNode { +func GetNode(t testing.TB, dserv mdag.DAGService, data []byte) node.Node { in := bytes.NewReader(data) node, err := imp.BuildTrickleDagFromReader(dserv, SizeSplitterGen(500)(in)) if err != nil { @@ -37,11 +38,11 @@ func GetNode(t testing.TB, dserv mdag.DAGService, data []byte) *mdag.ProtoNode { return node } -func GetEmptyNode(t testing.TB, dserv mdag.DAGService) *mdag.ProtoNode { +func GetEmptyNode(t testing.TB, dserv mdag.DAGService) node.Node { return GetNode(t, dserv, []byte{}) } -func GetRandomNode(t testing.TB, dserv mdag.DAGService, size int64) ([]byte, *mdag.ProtoNode) { +func GetRandomNode(t testing.TB, dserv mdag.DAGService, size int64) ([]byte, node.Node) { in := io.LimitReader(u.NewTimeSeededRand(), size) buf, err := ioutil.ReadAll(in) if err != nil { From 6a3aa9d939ea8b3ad96f37fc8c9337b15622091a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 15 Oct 2016 09:06:44 -0700 Subject: [PATCH 1584/3817] unixfs: allow use of raw merkledag nodes for unixfs files License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@0e015bac5adc170aca8e97a7ff7237f1565a1e84 --- mfs/dir.go | 7 ++++--- mfs/mfs_test.go | 11 ++++++++--- mfs/ops.go | 5 +++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 3a1c7be8e..e8004c80f 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -1,6 +1,7 @@ package mfs import ( + "context" "errors" "fmt" "os" @@ -9,11 +10,11 @@ import ( "sync" "time" - context "context" - dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" + + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) var ErrNotYetImplemented = errors.New("not yet implemented") @@ -323,7 +324,7 @@ func (d *Directory) Flush() error { } // AddChild adds the node 'nd' under this directory giving it the name 'name' -func (d *Directory) AddChild(name string, nd *dag.ProtoNode) error { +func (d *Directory) AddChild(name string, nd node.Node) error { d.lock.Lock() defer d.lock.Unlock() diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index dcec37356..4ac1b4a74 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -42,12 +42,12 @@ func getDagserv(t *testing.T) dag.DAGService { return dag.NewDAGService(blockserv) } -func getRandFile(t *testing.T, ds dag.DAGService, size int64) *dag.ProtoNode { +func getRandFile(t *testing.T, ds dag.DAGService, size int64) node.Node { r := io.LimitReader(u.NewTimeSeededRand(), size) return fileNodeFromReader(t, ds, r) } -func fileNodeFromReader(t *testing.T, ds dag.DAGService, r io.Reader) *dag.ProtoNode { +func fileNodeFromReader(t *testing.T, ds dag.DAGService, r io.Reader) node.Node { nd, err := importer.BuildDagFromReader(ds, chunk.DefaultSplitter(r)) if err != nil { t.Fatal(err) @@ -125,7 +125,12 @@ func compStrArrs(a, b []string) bool { return true } -func assertFileAtPath(ds dag.DAGService, root *Directory, exp *dag.ProtoNode, pth string) error { +func assertFileAtPath(ds dag.DAGService, root *Directory, expn node.Node, pth string) error { + exp, ok := expn.(*dag.ProtoNode) + if !ok { + return dag.ErrNotProtobuf + } + parts := path.SplitList(pth) cur := root for i, d := range parts[:len(parts)-1] { diff --git a/mfs/ops.go b/mfs/ops.go index 6464d8404..1c1fef82b 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -7,8 +7,9 @@ import ( gopath "path" "strings" - dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" + + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) // Mv moves the file or directory at 'src' to 'dst' @@ -87,7 +88,7 @@ func lookupDir(r *Root, path string) (*Directory, error) { } // PutNode inserts 'nd' at 'path' in the given mfs -func PutNode(r *Root, path string, nd *dag.ProtoNode) error { +func PutNode(r *Root, path string, nd node.Node) error { dirp, filename := gopath.Split(path) if filename == "" { return fmt.Errorf("cannot create file with empty name") From 08aa7890606e3b703ecf8bea803f305a7ac1d98d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 15 Oct 2016 09:06:44 -0700 Subject: [PATCH 1585/3817] unixfs: allow use of raw merkledag nodes for unixfs files License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@01a9d93f1f11452f9bd6ba2cf11571610ea7a2e5 --- ipld/merkledag/merkledag.go | 38 +++++++--------- ipld/merkledag/merkledag_test.go | 78 ++++++++++++++++++++++++++++++++ ipld/merkledag/node.go | 2 +- ipld/merkledag/raw.go | 46 +++++++++++++++++++ ipld/merkledag/utils/utils.go | 15 +++--- 5 files changed, 149 insertions(+), 30 deletions(-) create mode 100644 ipld/merkledag/raw.go diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 223927892..b6a8d8558 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -85,23 +85,29 @@ func (n *dagService) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { return nil, fmt.Errorf("Failed to get block for %s: %v", c, err) } - var res node.Node + return decodeBlock(b) +} + +func decodeBlock(b blocks.Block) (node.Node, error) { + c := b.Cid() + switch c.Type() { case cid.Protobuf: - out, err := DecodeProtobuf(b.RawData()) + decnd, err := DecodeProtobuf(b.RawData()) if err != nil { if strings.Contains(err.Error(), "Unmarshal failed") { return nil, fmt.Errorf("The block referred to by '%s' was not a valid merkledag node", c) } return nil, fmt.Errorf("Failed to decode Protocol Buffers: %v", err) } - out.cached = c - res = out + + decnd.cached = b.Cid() + return decnd, nil + case cid.Raw: + return NewRawNode(b.RawData()), nil default: - return nil, fmt.Errorf("unrecognized formatting type") + return nil, fmt.Errorf("unrecognized object type: %s", c.Type()) } - - return res, nil } func (n *dagService) GetLinks(ctx context.Context, c *cid.Cid) ([]*node.Link, error) { @@ -164,24 +170,12 @@ func (ds *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *Node return } - c := b.Cid() - - var nd node.Node - switch c.Type() { - case cid.Protobuf: - decnd, err := DecodeProtobuf(b.RawData()) - if err != nil { - out <- &NodeOption{Err: err} - return - } - decnd.cached = b.Cid() - nd = decnd - default: - out <- &NodeOption{Err: fmt.Errorf("unrecognized object type: %s", c.Type())} + nd, err := decodeBlock(b) + if err != nil { + out <- &NodeOption{Err: err} return } - // buffered, no need to select out <- &NodeOption{Node: nd} count++ diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 310134fa0..a0e91e8a0 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -373,3 +373,81 @@ func TestBasicAddGet(t *testing.T) { t.Fatal("output didnt match input") } } + +func TestGetRawNodes(t *testing.T) { + rn := NewRawNode([]byte("test")) + + ds := dstest.Mock() + + c, err := ds.Add(rn) + if err != nil { + t.Fatal(err) + } + + if !c.Equals(rn.Cid()) { + t.Fatal("output cids didnt match") + } + + out, err := ds.Get(context.TODO(), c) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(out.RawData(), []byte("test")) { + t.Fatal("raw block should match input data") + } + + if out.Links() != nil { + t.Fatal("raw blocks shouldnt have links") + } + + if out.Tree() != nil { + t.Fatal("tree should return no paths in a raw block") + } + + size, err := out.Size() + if err != nil { + t.Fatal(err) + } + if size != 4 { + t.Fatal("expected size to be 4") + } + + ns, err := out.Stat() + if err != nil { + t.Fatal(err) + } + + if ns.DataSize != 4 { + t.Fatal("expected size to be 4, got: ", ns.DataSize) + } + + _, _, err = out.Resolve([]string{"foo"}) + if err != ErrLinkNotFound { + t.Fatal("shouldnt find links under raw blocks") + } +} + +func TestProtoNodeResolve(t *testing.T) { + + nd := new(ProtoNode) + nd.SetLinks([]*node.Link{{Name: "foo"}}) + + lnk, left, err := nd.Resolve([]string{"foo", "bar"}) + if err != nil { + t.Fatal(err) + } + + if len(left) != 1 || left[0] != "bar" { + t.Fatal("expected the single path element 'bar' to remain") + } + + if lnk.Name != "foo" { + t.Fatal("how did we get anything else?") + } + + tvals := nd.Tree() + if len(tvals) != 1 || tvals[0] != "foo" { + t.Fatal("expected tree to return []{\"foo\"}") + } +} diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 4c01c9c9c..b0fca652b 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -36,7 +36,7 @@ func NodeWithData(d []byte) *ProtoNode { } // AddNodeLink adds a link to another node. -func (n *ProtoNode) AddNodeLink(name string, that *ProtoNode) error { +func (n *ProtoNode) AddNodeLink(name string, that node.Node) error { n.encoded = nil lnk, err := node.MakeLink(that) diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go new file mode 100644 index 000000000..deb2e1a1f --- /dev/null +++ b/ipld/merkledag/raw.go @@ -0,0 +1,46 @@ +package merkledag + +import ( + "github.com/ipfs/go-ipfs/blocks" + + cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" +) + +type RawNode struct { + blocks.Block +} + +func NewRawNode(data []byte) *RawNode { + h := u.Hash(data) + c := cid.NewCidV1(cid.Raw, h) + blk, _ := blocks.NewBlockWithCid(data, c) + + return &RawNode{blk} +} + +func (rn *RawNode) Links() []*node.Link { + return nil +} + +func (rn *RawNode) Resolve(path []string) (*node.Link, []string, error) { + return nil, nil, ErrLinkNotFound +} + +func (rn *RawNode) Tree() []string { + return nil +} + +func (rn *RawNode) Size() (uint64, error) { + return uint64(len(rn.RawData())), nil +} + +func (rn *RawNode) Stat() (*node.NodeStat, error) { + return &node.NodeStat{ + CumulativeSize: len(rn.RawData()), + DataSize: len(rn.RawData()), + }, nil +} + +var _ node.Node = (*RawNode)(nil) diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index a44d94621..7ef67b939 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -1,17 +1,18 @@ package dagutils import ( + "context" "errors" - context "context" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - bstore "github.com/ipfs/go-ipfs/blocks/blockstore" bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" + + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) type Editor struct { @@ -50,7 +51,7 @@ func (e *Editor) GetDagService() dag.DAGService { return e.tmp } -func addLink(ctx context.Context, ds dag.DAGService, root *dag.ProtoNode, childname string, childnd *dag.ProtoNode) (*dag.ProtoNode, error) { +func addLink(ctx context.Context, ds dag.DAGService, root *dag.ProtoNode, childname string, childnd node.Node) (*dag.ProtoNode, error) { if childname == "" { return nil, errors.New("cannot create link with no name!") } @@ -76,7 +77,7 @@ func addLink(ctx context.Context, ds dag.DAGService, root *dag.ProtoNode, childn return root, nil } -func (e *Editor) InsertNodeAtPath(ctx context.Context, pth string, toinsert *dag.ProtoNode, create func() *dag.ProtoNode) error { +func (e *Editor) InsertNodeAtPath(ctx context.Context, pth string, toinsert node.Node, create func() *dag.ProtoNode) error { splpath := path.SplitList(pth) nd, err := e.insertNodeAtPath(ctx, e.root, splpath, toinsert, create) if err != nil { @@ -86,7 +87,7 @@ func (e *Editor) InsertNodeAtPath(ctx context.Context, pth string, toinsert *dag return nil } -func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path []string, toinsert *dag.ProtoNode, create func() *dag.ProtoNode) (*dag.ProtoNode, error) { +func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path []string, toinsert node.Node, create func() *dag.ProtoNode) (*dag.ProtoNode, error) { if len(path) == 1 { return addLink(ctx, e.tmp, root, path[0], toinsert) } From 7f0e864e0241b6b0022c06c0d1e6334e787cbcd4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 18 Oct 2016 11:07:32 -0700 Subject: [PATCH 1586/3817] raw dag: make raw nodes work in cat and get, add tests License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@64720f3b628abca1db75690c7772b86e05708490 --- unixfs/io/dagreader.go | 51 ++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 4eb3e04c6..44945dd31 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -129,33 +129,36 @@ func (dr *DagReader) precalcNextBuf(ctx context.Context) error { } dr.linkPosition++ - nxtpb, ok := nxt.(*mdag.ProtoNode) - if !ok { - return mdag.ErrNotProtobuf - } - - pb := new(ftpb.Data) - err = proto.Unmarshal(nxtpb.Data(), pb) - if err != nil { - return fmt.Errorf("incorrectly formatted protobuf: %s", err) - } + switch nxt := nxt.(type) { + case *mdag.ProtoNode: + pb := new(ftpb.Data) + err = proto.Unmarshal(nxt.Data(), pb) + if err != nil { + return fmt.Errorf("incorrectly formatted protobuf: %s", err) + } - switch pb.GetType() { - case ftpb.Data_Directory: - // A directory should not exist within a file - return ft.ErrInvalidDirLocation - case ftpb.Data_File: - dr.buf = NewDataFileReader(dr.ctx, nxtpb, pb, dr.serv) - return nil - case ftpb.Data_Raw: - dr.buf = NewRSNCFromBytes(pb.GetData()) + switch pb.GetType() { + case ftpb.Data_Directory: + // A directory should not exist within a file + return ft.ErrInvalidDirLocation + case ftpb.Data_File: + dr.buf = NewDataFileReader(dr.ctx, nxt, pb, dr.serv) + return nil + case ftpb.Data_Raw: + dr.buf = NewRSNCFromBytes(pb.GetData()) + return nil + case ftpb.Data_Metadata: + return errors.New("shouldnt have had metadata object inside file") + case ftpb.Data_Symlink: + return errors.New("shouldnt have had symlink inside file") + default: + return ft.ErrUnrecognizedType + } + case *mdag.RawNode: + dr.buf = NewRSNCFromBytes(nxt.RawData()) return nil - case ftpb.Data_Metadata: - return errors.New("shouldnt have had metadata object inside file") - case ftpb.Data_Symlink: - return errors.New("shouldnt have had symlink inside file") default: - return ft.ErrUnrecognizedType + return errors.New("unrecognized node type in DagReader") } } From 80eb0bcc50170d93d11e3f386a873b93f69acfd0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 18 Oct 2016 14:41:47 -0700 Subject: [PATCH 1587/3817] fix add/cat of small files License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@acf015ec49216cca7aceaf2c1f7788740cd7914d --- mfs/dir.go | 50 ++++++++++++++++++++++++++++------------------ mfs/file.go | 53 ++++++++++++++++++++++++++++++------------------- mfs/mfs_test.go | 13 ++++++++---- mfs/system.go | 5 +++-- 4 files changed, 76 insertions(+), 45 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index e8004c80f..38bee4ccc 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -89,7 +89,7 @@ func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) { return d.node.Copy(), nil } -func (d *Directory) updateChild(name string, nd *dag.ProtoNode) error { +func (d *Directory) updateChild(name string, nd node.Node) error { err := d.node.RemoveNodeLink(name) if err != nil && err != dag.ErrNotFound { return err @@ -121,28 +121,40 @@ func (d *Directory) childNode(name string) (FSNode, error) { } // cacheNode caches a node into d.childDirs or d.files and returns the FSNode. -func (d *Directory) cacheNode(name string, nd *dag.ProtoNode) (FSNode, error) { - i, err := ft.FromBytes(nd.Data()) - if err != nil { - return nil, err - } +func (d *Directory) cacheNode(name string, nd node.Node) (FSNode, error) { + switch nd := nd.(type) { + case *dag.ProtoNode: + i, err := ft.FromBytes(nd.Data()) + if err != nil { + return nil, err + } - switch i.GetType() { - case ufspb.Data_Directory: - ndir := NewDirectory(d.ctx, name, nd, d, d.dserv) - d.childDirs[name] = ndir - return ndir, nil - case ufspb.Data_File, ufspb.Data_Raw, ufspb.Data_Symlink: + switch i.GetType() { + case ufspb.Data_Directory: + ndir := NewDirectory(d.ctx, name, nd, d, d.dserv) + d.childDirs[name] = ndir + return ndir, nil + case ufspb.Data_File, ufspb.Data_Raw, ufspb.Data_Symlink: + nfi, err := NewFile(name, nd, d, d.dserv) + if err != nil { + return nil, err + } + d.files[name] = nfi + return nfi, nil + case ufspb.Data_Metadata: + return nil, ErrNotYetImplemented + default: + return nil, ErrInvalidChild + } + case *dag.RawNode: nfi, err := NewFile(name, nd, d, d.dserv) if err != nil { return nil, err } d.files[name] = nfi return nfi, nil - case ufspb.Data_Metadata: - return nil, ErrNotYetImplemented default: - return nil, ErrInvalidChild + return nil, fmt.Errorf("unrecognized node type in cache node") } } @@ -162,8 +174,8 @@ func (d *Directory) Uncache(name string) { // childFromDag searches through this directories dag node for a child link // with the given name -func (d *Directory) childFromDag(name string) (*dag.ProtoNode, error) { - pbn, err := d.node.GetLinkedProtoNode(d.ctx, d.dserv, name) +func (d *Directory) childFromDag(name string) (node.Node, error) { + pbn, err := d.node.GetLinkedNode(d.ctx, d.dserv, name) switch err { case nil: return pbn, nil @@ -249,7 +261,7 @@ func (d *Directory) List() ([]NodeListing, error) { return nil, err } - child.Hash = nd.Key().B58String() + child.Hash = nd.Cid().String() out = append(out, child) } @@ -385,7 +397,7 @@ func (d *Directory) Path() string { return out } -func (d *Directory) GetNode() (*dag.ProtoNode, error) { +func (d *Directory) GetNode() (node.Node, error) { d.lock.Lock() defer d.lock.Unlock() diff --git a/mfs/file.go b/mfs/file.go index 373a9dd1d..931827ebb 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -9,6 +9,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" + + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) type File struct { @@ -19,12 +21,12 @@ type File struct { desclock sync.RWMutex dserv dag.DAGService - node *dag.ProtoNode + node node.Node nodelk sync.Mutex } // NewFile returns a NewFile object with the given parameters -func NewFile(name string, node *dag.ProtoNode, parent childCloser, dserv dag.DAGService) (*File, error) { +func NewFile(name string, node node.Node, parent childCloser, dserv dag.DAGService) (*File, error) { return &File{ dserv: dserv, parent: parent, @@ -44,18 +46,23 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { node := fi.node fi.nodelk.Unlock() - fsn, err := ft.FSNodeFromBytes(node.Data()) - if err != nil { - return nil, err - } - - switch fsn.Type { - default: - return nil, fmt.Errorf("unsupported fsnode type for 'file'") - case ft.TSymlink: - return nil, fmt.Errorf("symlinks not yet supported") - case ft.TFile, ft.TRaw: - // OK case + switch node := node.(type) { + case *dag.ProtoNode: + fsn, err := ft.FSNodeFromBytes(node.Data()) + if err != nil { + return nil, err + } + + switch fsn.Type { + default: + return nil, fmt.Errorf("unsupported fsnode type for 'file'") + case ft.TSymlink: + return nil, fmt.Errorf("symlinks not yet supported") + case ft.TFile, ft.TRaw: + // OK case + } + case *dag.RawNode: + // Ok as well. } switch flags { @@ -85,16 +92,22 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { func (fi *File) Size() (int64, error) { fi.nodelk.Lock() defer fi.nodelk.Unlock() - pbd, err := ft.FromBytes(fi.node.Data()) - if err != nil { - return 0, err + switch nd := fi.node.(type) { + case *dag.ProtoNode: + pbd, err := ft.FromBytes(nd.Data()) + if err != nil { + return 0, err + } + return int64(pbd.GetFilesize()), nil + case *dag.RawNode: + return int64(len(nd.RawData())), nil + default: + return 0, fmt.Errorf("unrecognized node type in mfs/file.Size()") } - - return int64(pbd.GetFilesize()), nil } // GetNode returns the dag node associated with this file -func (fi *File) GetNode() (*dag.ProtoNode, error) { +func (fi *File) GetNode() (node.Node, error) { fi.nodelk.Lock() defer fi.nodelk.Unlock() return fi.node, nil diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 4ac1b4a74..b7e725fbc 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -794,7 +794,12 @@ func TestFlushing(t *testing.T) { t.Fatal(err) } - fsnode, err := ft.FSNodeFromBytes(rnd.Data()) + pbrnd, ok := rnd.(*dag.ProtoNode) + if !ok { + t.Fatal(dag.ErrNotProtobuf) + } + + fsnode, err := ft.FSNodeFromBytes(pbrnd.Data()) if err != nil { t.Fatal(err) } @@ -803,10 +808,10 @@ func TestFlushing(t *testing.T) { t.Fatal("root wasnt a directory") } - rnk := rnd.Key() + rnk := rnd.Cid() exp := "QmWMVyhTuyxUrXX3ynz171jq76yY3PktfY9Bxiph7b9ikr" - if rnk.B58String() != exp { - t.Fatalf("dag looks wrong, expected %s, but got %s", exp, rnk.B58String()) + if rnk.String() != exp { + t.Fatalf("dag looks wrong, expected %s, but got %s", exp, rnk.String()) } } diff --git a/mfs/system.go b/mfs/system.go index 2a69a1878..4912c0fd3 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -10,6 +10,7 @@ package mfs import ( + "context" "errors" "sync" "time" @@ -17,9 +18,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - context "context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) var ErrNotExist = errors.New("no such rootfs") @@ -41,7 +42,7 @@ const ( // FSNode represents any node (directory, root, or file) in the mfs filesystem type FSNode interface { - GetNode() (*dag.ProtoNode, error) + GetNode() (node.Node, error) Flush() error Type() NodeType } From 7f3f4222b25f2b49aefae0e9c370ad4677e8556e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 18 Oct 2016 16:03:26 -0700 Subject: [PATCH 1588/3817] update HashOnRead validation to properly support cids License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@c0661448235c35536f55a811cdd00c989000ee99 --- blockstore/blockstore.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index dfa35ec41..fb8a0f067 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -100,12 +100,16 @@ func (bs *blockstore) Get(k *cid.Cid) (blocks.Block, error) { } if bs.rehash { - rb := blocks.NewBlock(bdata) - if !rb.Cid().Equals(k) { + rbcid, err := k.Prefix().Sum(bdata) + if err != nil { + return nil, err + } + + if !rbcid.Equals(k) { return nil, ErrHashMismatch - } else { - return rb, nil } + + return blocks.NewBlockWithCid(bdata, rbcid) } else { return blocks.NewBlockWithCid(bdata, k) } From 38733364d9df454d5fda1ecc7945fd91e05d97f8 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sun, 16 Oct 2016 21:06:28 -0400 Subject: [PATCH 1589/3817] Create a FilestoreNode object to carry PosInfo When doing a filestore add, we wrap whatever nodes we create in a FilestoreNode object and add the PosInfo to it so that the filestore will be able to extract information as needed. Edited by whyrusleeping License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-chunker@a61e184fe3a89cbed366a8ec27f72c753f1eb395 --- chunker/rabin.go | 10 ++++++++-- chunker/splitting.go | 5 +++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/chunker/rabin.go b/chunker/rabin.go index ce9b5fc56..d2d71460d 100644 --- a/chunker/rabin.go +++ b/chunker/rabin.go @@ -10,7 +10,8 @@ import ( var IpfsRabinPoly = chunker.Pol(17437180132763653) type Rabin struct { - r *chunker.Chunker + r *chunker.Chunker + reader io.Reader } func NewRabin(r io.Reader, avgBlkSize uint64) *Rabin { @@ -25,7 +26,8 @@ func NewRabinMinMax(r io.Reader, min, avg, max uint64) *Rabin { ch := chunker.New(r, IpfsRabinPoly, h, avg, min, max) return &Rabin{ - r: ch, + r: ch, + reader: r, } } @@ -37,3 +39,7 @@ func (r *Rabin) NextBytes() ([]byte, error) { return ch.Data, nil } + +func (r *Rabin) Reader() io.Reader { + return r.reader +} diff --git a/chunker/splitting.go b/chunker/splitting.go index f3256c458..6fd55e22d 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -12,6 +12,7 @@ var log = logging.Logger("chunk") var DefaultBlockSize int64 = 1024 * 256 type Splitter interface { + Reader() io.Reader NextBytes() ([]byte, error) } @@ -77,3 +78,7 @@ func (ss *sizeSplitterv2) NextBytes() ([]byte, error) { return buf[:n], nil } + +func (ss *sizeSplitterv2) Reader() io.Reader { + return ss.r +} From 95c599f6ac8ab9551a4511cf478589bd9b60b581 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 24 Oct 2016 20:39:27 -0700 Subject: [PATCH 1590/3817] update to new cid and ipld node packages License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@12a491b4b90594e21b6e3ae6ca41d7059a8dd60f --- unixfs/io/dagreader.go | 2 +- unixfs/io/dirbuilder.go | 2 +- unixfs/mod/dagmodifier.go | 4 ++-- unixfs/test/utils.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 44945dd31..2d25895f1 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -12,8 +12,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) var ErrIsDir = errors.New("this dag node is a directory") diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index ac316f8a2..df2f18b40 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -5,7 +5,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ) type directoryBuilder struct { diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index fe59436ee..63afd33e7 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -15,9 +15,9 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) var ErrSeekFail = errors.New("failed to seek properly") diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index abe292300..4b1ef7c49 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -14,7 +14,7 @@ import ( mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) From 20ce4a7da8ff420848f9d4c4097ffe7cdc2635e9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 24 Oct 2016 20:39:27 -0700 Subject: [PATCH 1591/3817] update to new cid and ipld node packages License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@b68ea04482dcb7b3517a507fc92c7ca168c505eb --- path/path.go | 2 +- path/resolver.go | 6 +++--- path/resolver_test.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/path/path.go b/path/path.go index 3713259c4..c05c3c798 100644 --- a/path/path.go +++ b/path/path.go @@ -5,7 +5,7 @@ import ( "path" "strings" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ) // ErrBadPath is returned when a given path is incorrectly formatted diff --git a/path/resolver.go b/path/resolver.go index e4bfe8f79..8a1bb5e2e 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -10,8 +10,8 @@ import ( merkledag "github.com/ipfs/go-ipfs/merkledag" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ) var log = logging.Logger("path") @@ -112,7 +112,7 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd node.Node, names []stri ctx, cancel = context.WithTimeout(ctx, time.Minute) defer cancel() - lnk, rest, err := nd.Resolve(names) + lnk, rest, err := nd.ResolveLink(names) if err == merkledag.ErrLinkNotFound { n := nd.Cid() return result, ErrNoLink{Name: names[0], Node: n} diff --git a/path/resolver_test.go b/path/resolver_test.go index 652f38796..db69fde5f 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -9,8 +9,8 @@ import ( dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" util "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) From 7cf980d6bb189e74afefdd520b48adb8fe4a0b16 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 24 Oct 2016 20:39:27 -0700 Subject: [PATCH 1592/3817] update to new cid and ipld node packages License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@ff1df7aa6f86ba897c03ed4597bfc3edd8ac5ab8 --- ipld/merkledag/coding.go | 4 ++-- ipld/merkledag/merkledag.go | 4 ++-- ipld/merkledag/merkledag_test.go | 6 +++--- ipld/merkledag/node.go | 10 +++++++--- ipld/merkledag/node_test.go | 2 +- ipld/merkledag/raw.go | 10 +++++++--- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/traverse/traverse_test.go | 2 +- ipld/merkledag/utils/diff.go | 2 +- ipld/merkledag/utils/utils.go | 2 +- ipld/merkledag/utils/utils_test.go | 2 +- 11 files changed, 27 insertions(+), 19 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index c37a63db5..2745b833d 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -6,8 +6,8 @@ import ( pb "github.com/ipfs/go-ipfs/merkledag/pb" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index b6a8d8558..4867e583f 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -12,8 +12,8 @@ import ( offline "github.com/ipfs/go-ipfs/exchange/offline" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ) var log = logging.Logger("merkledag") diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index a0e91e8a0..b3209a18a 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -21,9 +21,9 @@ import ( dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) @@ -433,7 +433,7 @@ func TestProtoNodeResolve(t *testing.T) { nd := new(ProtoNode) nd.SetLinks([]*node.Link{{Name: "foo"}}) - lnk, left, err := nd.Resolve([]string{"foo", "bar"}) + lnk, left, err := nd.ResolveLink([]string{"foo", "bar"}) if err != nil { t.Fatal(err) } diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index b0fca652b..8111cb8c8 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -4,10 +4,10 @@ import ( "context" "fmt" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") @@ -253,7 +253,11 @@ func (n *ProtoNode) SetLinks(links []*node.Link) { n.links = links } -func (n *ProtoNode) Resolve(path []string) (*node.Link, []string, error) { +func (n *ProtoNode) Resolve(path []string) (interface{}, []string, error) { + return n.ResolveLink(path) +} + +func (n *ProtoNode) ResolveLink(path []string) (*node.Link, []string, error) { if len(path) == 0 { return nil, nil, fmt.Errorf("end of path, no more links to resolve") } diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index 392a51ea2..996c82622 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -7,7 +7,7 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" ) func TestRemoveLink(t *testing.T) { diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index deb2e1a1f..42f56fc4a 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -3,8 +3,8 @@ package merkledag import ( "github.com/ipfs/go-ipfs/blocks" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) @@ -24,7 +24,11 @@ func (rn *RawNode) Links() []*node.Link { return nil } -func (rn *RawNode) Resolve(path []string) (*node.Link, []string, error) { +func (rn *RawNode) ResolveLink(path []string) (*node.Link, []string, error) { + return nil, nil, ErrLinkNotFound +} + +func (rn *RawNode) Resolve(path []string) (interface{}, []string, error) { return nil, nil, ErrLinkNotFound } diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index 17e1b666c..85ccc3075 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -5,7 +5,7 @@ import ( "context" "errors" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" ) // Order is an identifier for traversal algorithm orders diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index fc8d053fa..74f2a6f46 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -8,7 +8,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" ) func TestDFSPreNoSkip(t *testing.T) { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 2b5ddb72b..5d2cfbd6f 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -7,7 +7,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" context "context" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ) const ( diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 7ef67b939..f57cc6cff 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -10,7 +10,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 4f822e5cd..c2788f3a7 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" context "context" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ) func TestAddLink(t *testing.T) { From 6fb4fcf05901ad106df4fd590803c56a57f1b0b5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 24 Oct 2016 20:39:27 -0700 Subject: [PATCH 1593/3817] update to new cid and ipld node packages License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@c52c4ee1415a0aa8a658350f6818991dc3860088 --- mfs/dir.go | 2 +- mfs/file.go | 2 +- mfs/mfs_test.go | 4 ++-- mfs/ops.go | 2 +- mfs/repub_test.go | 2 +- mfs/system.go | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 38bee4ccc..e425a6094 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -14,7 +14,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" ) var ErrNotYetImplemented = errors.New("not yet implemented") diff --git a/mfs/file.go b/mfs/file.go index 931827ebb..72be2117a 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index b7e725fbc..04513d3e3 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -24,8 +24,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" diff --git a/mfs/ops.go b/mfs/ops.go index 1c1fef82b..a27eb7d4a 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -9,7 +9,7 @@ import ( path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" ) // Mv moves the file or directory at 'src' to 'dst' diff --git a/mfs/repub_test.go b/mfs/repub_test.go index b88604bc0..6cb38850a 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -7,7 +7,7 @@ import ( ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" "context" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index 4912c0fd3..234fc92fa 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -19,8 +19,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ) var ErrNotExist = errors.New("no such rootfs") From 79584b7ea7c53ea29fe273440e5b14a7c860f76a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 24 Oct 2016 20:39:27 -0700 Subject: [PATCH 1594/3817] update to new cid and ipld node packages License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-interface@99935f5baff7ce959a5c6e78c24486491ff3f04a --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index 24ed382b3..d8d8a14fc 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -7,7 +7,7 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ) // Any type that implements exchange.Interface may be used as an IPFS block From b8971d6ab29a4fef25faca535f11203e7480ed4e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 24 Oct 2016 20:39:27 -0700 Subject: [PATCH 1595/3817] update to new cid and ipld node packages License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@0bc06285476a2361714e27e51b01b9d3520613d3 --- routing/mock/centralized_client.go | 4 ++-- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 4 ++-- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 4 ++-- routing/supernode/client.go | 6 +++--- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 22b9386ca..753f8bc86 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,11 +8,11 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" "github.com/ipfs/go-ipfs/thirdparty/testutil" - routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" + routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ma "gx/ipfs/QmUAQaWbKxGCUTuoQVvvicbQNZ9APF5pDGWyAZSe93AtKH/go-multiaddr" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 4dff16cfe..424f3ea58 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -8,8 +8,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index ade8304a4..2cd9855c3 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,8 +8,8 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index e71635ab5..eb117afd6 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,7 +3,7 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - dht "gx/ipfs/QmWHiyk5y2EKgxHogFJ4Zt1xTqKeVsBc4zcBke8ie9C2Bn/go-libp2p-kad-dht" + dht "gx/ipfs/QmTyXZijAwx3ptKzQkzq7BWBhmSJhjxLpDKF2Fp95WUd13/go-libp2p-kad-dht" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" mocknet "gx/ipfs/QmcRa2qn6iCmap9bjp8jAwkvYAq13AUfxdY3rrYiaJbLum/go-libp2p/p2p/net/mock" diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 5296b529f..a29716563 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,9 +10,9 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 00a7cfaee..939ebea6e 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,10 +6,10 @@ import ( repo "github.com/ipfs/go-ipfs/repo" - routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" + routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" p2phost "gx/ipfs/QmdML3R42PRSwnt46jSuEts9bHSqLctVYEjJqMR3UYV8ki/go-libp2p-host" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 49ac6ec03..b675cbec5 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,10 +7,10 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" + routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 3f7248919..12b6fc295 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,12 +8,12 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" + routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" - dhtpb "gx/ipfs/QmWHiyk5y2EKgxHogFJ4Zt1xTqKeVsBc4zcBke8ie9C2Bn/go-libp2p-kad-dht/pb" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + dhtpb "gx/ipfs/QmTyXZijAwx3ptKzQkzq7BWBhmSJhjxLpDKF2Fp95WUd13/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" "gx/ipfs/QmdML3R42PRSwnt46jSuEts9bHSqLctVYEjJqMR3UYV8ki/go-libp2p-host" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index ea9c0b1b0..eb0881710 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,7 +2,7 @@ package proxy import ( context "context" - dhtpb "gx/ipfs/QmWHiyk5y2EKgxHogFJ4Zt1xTqKeVsBc4zcBke8ie9C2Bn/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmTyXZijAwx3ptKzQkzq7BWBhmSJhjxLpDKF2Fp95WUd13/go-libp2p-kad-dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" inet "gx/ipfs/QmdXimY9QHaasZmw6hWojWnCJvfgxETjZQfg9g6ZrA9wMX/go-libp2p-net" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 15f1a71a0..264038195 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -6,8 +6,8 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" + dhtpb "gx/ipfs/QmTyXZijAwx3ptKzQkzq7BWBhmSJhjxLpDKF2Fp95WUd13/go-libp2p-kad-dht/pb" kbucket "gx/ipfs/QmUKePKcUEXwdvJENZJ6z8mJjPaxLsDZ3V9CZjPPtyawPm/go-libp2p-kbucket" - dhtpb "gx/ipfs/QmWHiyk5y2EKgxHogFJ4Zt1xTqKeVsBc4zcBke8ie9C2Bn/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" host "gx/ipfs/QmdML3R42PRSwnt46jSuEts9bHSqLctVYEjJqMR3UYV8ki/go-libp2p-host" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index c6f6daf4c..25268e7c4 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -7,7 +7,7 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" context "context" - dhtpb "gx/ipfs/QmWHiyk5y2EKgxHogFJ4Zt1xTqKeVsBc4zcBke8ie9C2Bn/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmTyXZijAwx3ptKzQkzq7BWBhmSJhjxLpDKF2Fp95WUd13/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index a7a315eae..59cdd814e 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,7 +3,7 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmWHiyk5y2EKgxHogFJ4Zt1xTqKeVsBc4zcBke8ie9C2Bn/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmTyXZijAwx3ptKzQkzq7BWBhmSJhjxLpDKF2Fp95WUd13/go-libp2p-kad-dht/pb" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) From be1dbf059b07aeae70399be647cbd2f089060bd2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 24 Oct 2016 20:39:27 -0700 Subject: [PATCH 1596/3817] update to new cid and ipld node packages License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@d91d4b27218cca72ef174583d50fba701762c5f5 --- namesys/namesys.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/routing.go | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 4d65feea0..54c305c9d 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,7 +6,7 @@ import ( context "context" path "github.com/ipfs/go-ipfs/path" - routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" + routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 8909f1676..d23f66fb2 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -14,7 +14,7 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ft "github.com/ipfs/go-ipfs/unixfs" - routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" + routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 94a9e1ce3..727270504 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,7 +11,7 @@ import ( path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" + routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" diff --git a/namesys/routing.go b/namesys/routing.go index 2cf6e0d8e..33b5431d4 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -12,11 +12,11 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" + routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) From 6dbd2037cd7ce94f2972a7d1f684a4ebbf7c2bcd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 24 Oct 2016 20:39:27 -0700 Subject: [PATCH 1597/3817] update to new cid and ipld node packages License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@8f3fb21c4e11f4e0b1201246f982c8c841270765 --- blockstore/arc_cache.go | 2 +- blockstore/arc_cache_test.go | 2 +- blockstore/blockstore.go | 2 +- blockstore/blockstore_test.go | 2 +- blockstore/bloom_cache.go | 2 +- blockstore/util/remove.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 03fa3fe0c..8bc74436c 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -7,7 +7,7 @@ import ( "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 42d388a16..d796214c5 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/blocks" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index b607bfc09..f5dc26c1c 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -12,7 +12,7 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dsns "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/namespace" dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 4c1a4db88..abe8a1a72 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -9,7 +9,7 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 7f6066ace..3febffd01 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -8,7 +8,7 @@ import ( "github.com/ipfs/go-ipfs/blocks" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" bloom "gx/ipfs/QmeiMCBkYHxkDkDfnDadzz4YxY5ruL5Pj499essE4vRsGM/bbloom" ) diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index b3fd7501e..01f2ce44e 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -6,7 +6,7 @@ import ( bs "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/pin" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) From 18ac09553f871c0431719abda2c68997dbd62ef8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 24 Oct 2016 20:39:27 -0700 Subject: [PATCH 1598/3817] update to new cid and ipld node packages License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@6b702f1fa501aa1ee2d295d0758e5f9d59791c50 --- blockservice/blockservice.go | 2 +- blockservice/test/blocks_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 675bb9751..8f1d069ab 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -13,7 +13,7 @@ import ( exchange "github.com/ipfs/go-ipfs/exchange" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ) var log = logging.Logger("blockservice") diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 956420da2..4aae38bef 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -12,7 +12,7 @@ import ( . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" From 649e4c899b7de38af5d8e6bfd2a1d6a97be11896 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 24 Oct 2016 20:39:27 -0700 Subject: [PATCH 1599/3817] update to new cid and ipld node packages License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-ds-help@fbd1999a51a43078eb70f9d5c05215774fefe3c1 --- datastore/dshelp/key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 7db86aedb..20308d704 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -1,9 +1,9 @@ package dshelp import ( + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" base32 "gx/ipfs/Qmb1DA2A9LS2wR4FFweB4uEDomFsdmnw1VLawLE1yQzudj/base32" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" ) // TODO: put this code into the go-datastore itself From b5eafffcd72c12f8b3475d2ac50fd092deebdf3e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 24 Oct 2016 20:39:27 -0700 Subject: [PATCH 1600/3817] update to new cid and ipld node packages License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@2c9e8d7a00b464fa9ea86d68da830f82ff60ecc7 --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 2 +- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 5f62eb0b8..d2607bdbe 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,7 +8,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 10c60c256..2263d5111 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -12,8 +12,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 787b8226a..65c480ab8 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -10,7 +10,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" context "context" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index eaaba7884..2d7566b77 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -13,10 +13,10 @@ import ( "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 335b59e99..3a33219ac 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -9,7 +9,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ) func ignoreCids(_ *cid.Cid) {} From 077d708d57482b3c4bbaff1cc55f91f08e2783aa Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 24 Oct 2016 20:39:27 -0700 Subject: [PATCH 1601/3817] update to new cid and ipld node packages License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@7867ef218191e26eff094e5d44857f3ecc0fb3b4 --- exchange/offline/offline.go | 2 +- exchange/offline/offline_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index ba3fa0017..5017cc8df 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -9,7 +9,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 4befe796d..3ec1478e8 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -8,7 +8,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" - cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ds_sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" From f88dcd60ed2185ee1a3f7bdf197f91113a4e1bef Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 24 Oct 2016 12:00:29 -0700 Subject: [PATCH 1602/3817] Implement cbor ipld nodes and a first pass at the 'dag' command License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@b783f032eee6f868d49d6c93ef6d997a922a9531 --- ipld/merkledag/merkledag.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 4867e583f..7b15fa4d5 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -14,6 +14,7 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + ipldcbor "gx/ipfs/QmYRzW9YDHVNCDbfFzbS7TEXAG1swE1yjq1basZ5WnJYH4/go-ipld-cbor" ) var log = logging.Logger("merkledag") @@ -105,6 +106,12 @@ func decodeBlock(b blocks.Block) (node.Node, error) { return decnd, nil case cid.Raw: return NewRawNode(b.RawData()), nil + case cid.CBOR: + return ipldcbor.Decode(b.RawData()) + /* + case cid.Bitcoin: + return ipldbtc.DecodeBlock(b.RawData()) + */ default: return nil, fmt.Errorf("unrecognized object type: %s", c.Type()) } From 160604fae1ca2e36ab55ec0f04ab00f1a0f6767d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 19 Oct 2016 10:05:58 -0700 Subject: [PATCH 1603/3817] make path resolver no longer require whole node for construction License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@31b5b667d044b2a00fca7bf2aed7ac2a3a1f4bfd --- unixfs/io/resolve.go | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 unixfs/io/resolve.go diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go new file mode 100644 index 000000000..a796c6bb7 --- /dev/null +++ b/unixfs/io/resolve.go @@ -0,0 +1,46 @@ +package io + +import ( + "context" + + dag "github.com/ipfs/go-ipfs/merkledag" + ft "github.com/ipfs/go-ipfs/unixfs" + + node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" +) + +func ResolveUnixfsOnce(ctx context.Context, ds dag.DAGService, nd node.Node, name string) (*node.Link, error) { + pbnd, ok := nd.(*dag.ProtoNode) + if !ok { + lnk, _, err := nd.ResolveLink([]string{name}) + return lnk, err + } + + upb, err := ft.FromBytes(pbnd.Data()) + if err != nil { + // Not a unixfs node, use standard object traversal code + lnk, _, err := nd.ResolveLink([]string{name}) + return lnk, err + } + + switch upb.GetType() { + /* + case ft.THAMTShard: + s, err := hamt.NewHamtFromDag(ds, nd) + if err != nil { + return nil, err + } + + // TODO: optimized routine on HAMT for returning a dag.Link to avoid extra disk hits + out, err := s.Find(ctx, name) + if err != nil { + return nil, err + } + + return dag.MakeLink(out) + */ + default: + lnk, _, err := nd.ResolveLink([]string{name}) + return lnk, err + } +} From be5c19c26ba069e9663eaabaad7303ca0dd5f2cc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 19 Oct 2016 10:05:58 -0700 Subject: [PATCH 1604/3817] make path resolver no longer require whole node for construction License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@8205cde77f68c847312b375a2fd1dabcaa90b5e5 --- path/resolver.go | 31 ++++++++++++++++++++++++------- path/resolver_test.go | 2 +- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index 8a1bb5e2e..e284ce136 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -7,7 +7,7 @@ import ( "fmt" "time" - merkledag "github.com/ipfs/go-ipfs/merkledag" + dag "github.com/ipfs/go-ipfs/merkledag" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" @@ -32,8 +32,19 @@ func (e ErrNoLink) Error() string { // Resolver provides path resolution to IPFS // It has a pointer to a DAGService, which is uses to resolve nodes. +// TODO: now that this is more modular, try to unify this code with the +// the resolvers in namesys type Resolver struct { - DAG merkledag.DAGService + DAG dag.DAGService + + ResolveOnce func(ctx context.Context, ds dag.DAGService, nd node.Node, name string) (*node.Link, error) +} + +func NewBasicResolver(ds dag.DAGService) *Resolver { + return &Resolver{ + DAG: ds, + ResolveOnce: ResolveSingle, + } } // SplitAbsPath clean up and split fpath. It extracts the first component (which @@ -53,7 +64,9 @@ func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) { } c, err := cid.Decode(parts[0]) + // first element in the path is a cid if err != nil { + log.Debug("given path element is not a cid.\n") return nil, nil, err } @@ -75,6 +88,11 @@ func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (node.Node, erro return nodes[len(nodes)-1], err } +func ResolveSingle(ctx context.Context, ds dag.DAGService, nd node.Node, name string) (*node.Link, error) { + lnk, _, err := nd.ResolveLink([]string{name}) + return lnk, err +} + // ResolvePathComponents fetches the nodes for each segment of the given path. // It uses the first path component as a hash (key) of the first node, then // resolves all other components walking the links, with ResolveLinks. @@ -113,21 +131,20 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd node.Node, names []stri defer cancel() lnk, rest, err := nd.ResolveLink(names) - if err == merkledag.ErrLinkNotFound { - n := nd.Cid() - return result, ErrNoLink{Name: names[0], Node: n} + if err == dag.ErrLinkNotFound { + return result, ErrNoLink{Name: names[0], Node: nd.Cid()} } else if err != nil { return result, err } - nextnode, err := s.DAG.Get(ctx, lnk.Cid) + nextnode, err := lnk.GetNode(ctx, s.DAG) if err != nil { return result, err } nd = nextnode - names = rest result = append(result, nextnode) + names = rest } return result, nil } diff --git a/path/resolver_test.go b/path/resolver_test.go index db69fde5f..68e67b36a 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -55,7 +55,7 @@ func TestRecurivePathResolution(t *testing.T) { t.Fatal(err) } - resolver := &path.Resolver{DAG: dagService} + resolver := path.NewBasicResolver(dagService) node, err := resolver.ResolvePath(ctx, p) if err != nil { t.Fatal(err) From a06a798b8507b1e8ac8d7d44fa41b97270cbdd16 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 27 Oct 2016 15:49:41 -0700 Subject: [PATCH 1605/3817] clean up some code, update cbor package, and add tests License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@9d059951c58e0f3d2b737c237c450281855572a1 --- ipld/merkledag/merkledag.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 7b15fa4d5..5d3c3aebd 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -14,7 +14,7 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" - ipldcbor "gx/ipfs/QmYRzW9YDHVNCDbfFzbS7TEXAG1swE1yjq1basZ5WnJYH4/go-ipld-cbor" + ipldcbor "gx/ipfs/QmY7L2aEa1rHjkSSbXJB8oC7825JTpUUvDygmM2JPQeqhr/go-ipld-cbor" ) var log = logging.Logger("merkledag") @@ -108,10 +108,6 @@ func decodeBlock(b blocks.Block) (node.Node, error) { return NewRawNode(b.RawData()), nil case cid.CBOR: return ipldcbor.Decode(b.RawData()) - /* - case cid.Bitcoin: - return ipldbtc.DecodeBlock(b.RawData()) - */ default: return nil, fmt.Errorf("unrecognized object type: %s", c.Type()) } From 52b3590f7fa5b91fde6386138c58e0d5e338bbe9 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Fri, 1 Jul 2016 18:36:55 +0100 Subject: [PATCH 1606/3817] Changed so only explicit ipfs cli commands are lowercased License: MIT Signed-off-by: Richard Littauer This commit was moved from ipfs/go-namesys@16183d0dfc06b4dc145ee730e0771038e52232eb --- namesys/namesys.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 54c305c9d..87294190d 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -14,11 +14,11 @@ import ( // mpns (a multi-protocol NameSystem) implements generic IPFS naming. // // Uses several Resolvers: -// (a) ipfs routing naming: SFS-like PKI names. +// (a) IPFS routing naming: SFS-like PKI names. // (b) dns domains: resolves using links in DNS TXT records // (c) proquints: interprets string as the raw byte data. // -// It can only publish to: (a) ipfs routing naming. +// It can only publish to: (a) IPFS routing naming. // type mpns struct { resolvers map[string]resolver From 0d4e13f41756df520b6f6b12ebc2e5ab201c4a32 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Fri, 1 Jul 2016 18:36:55 +0100 Subject: [PATCH 1607/3817] Changed so only explicit ipfs cli commands are lowercased License: MIT Signed-off-by: Richard Littauer This commit was moved from ipfs/go-merkledag@5ac8c21d6bf01047d7a255e23c6a808708d1fc62 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 4867e583f..6e1292b0a 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -1,4 +1,4 @@ -// package merkledag implements the ipfs Merkle DAG datastructures. +// package merkledag implements the IPFS Merkle DAG datastructures. package merkledag import ( From b9d6852bb40b57e526ed906de633aee8e11c3caf Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Fri, 1 Jul 2016 18:36:55 +0100 Subject: [PATCH 1608/3817] Changed so only explicit ipfs cli commands are lowercased License: MIT Signed-off-by: Richard Littauer This commit was moved from ipfs/go-unixfs@3d3560255024b08e2a2073fcd783b92e95ad3915 --- unixfs/format.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/format.go b/unixfs/format.go index a8ade430c..7a602362e 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -1,4 +1,4 @@ -// Package format implements a data format for files in the ipfs filesystem It +// Package format implements a data format for files in the IPFS filesystem It // is not the only format in ipfs, but it is the one that the filesystem // assumes package unixfs From 90b45f468bcf4ff6605e4d7fe4e65c22bbbfbb17 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Fri, 1 Jul 2016 18:36:55 +0100 Subject: [PATCH 1609/3817] Changed so only explicit ipfs cli commands are lowercased License: MIT Signed-off-by: Richard Littauer This commit was moved from ipfs/go-path@fdf89242fbd0f5424d22296ae3dbe704f2e88da3 --- path/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/path.go b/path/path.go index c05c3c798..dbc67189a 100644 --- a/path/path.go +++ b/path/path.go @@ -9,7 +9,7 @@ import ( ) // ErrBadPath is returned when a given path is incorrectly formatted -var ErrBadPath = errors.New("invalid ipfs ref path") +var ErrBadPath = errors.New("invalid 'ipfs ref' path") // TODO: debate making this a private struct wrapped in a public interface // would allow us to control creation, and cache segments. From 02387765608d89af2eff392372a56d614b624db4 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Fri, 1 Jul 2016 18:36:55 +0100 Subject: [PATCH 1610/3817] Changed so only explicit ipfs cli commands are lowercased License: MIT Signed-off-by: Richard Littauer This commit was moved from ipfs/go-mfs@b4356f1afd5eab9e5edee7d4c71d17a1943b19cb --- mfs/system.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/system.go b/mfs/system.go index 234fc92fa..a565d7346 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -1,4 +1,4 @@ -// package mfs implements an in memory model of a mutable ipfs filesystem. +// package mfs implements an in memory model of a mutable IPFS filesystem. // // It consists of four main structs: // 1) The Filesystem From 59d465b30b558e2473d00865c432c86c05f35400 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Fri, 1 Jul 2016 18:36:55 +0100 Subject: [PATCH 1611/3817] Changed so only explicit ipfs cli commands are lowercased License: MIT Signed-off-by: Richard Littauer This commit was moved from ipfs/go-ipfs-exchange-interface@35dc50badcccd77d0210935f4c7257aefa4015e1 --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index d8d8a14fc..caf372001 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -1,4 +1,4 @@ -// package exchange defines the IPFS Exchange interface +// package exchange defines the IPFS exchange interface package exchange import ( From 909ec2fe2d3f492cd48026dfe72d83c65b6f1cc7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 28 Oct 2016 12:56:47 -0700 Subject: [PATCH 1612/3817] more cleanup, update cboripld package License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@462556200a4e1b4a959ce787733669b561a966cf --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 5d3c3aebd..bb115aae1 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -11,10 +11,10 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" + ipldcbor "gx/ipfs/QmRcAVqrbY5wryx7hfNLtiUZbCcstzaJL7YJFBboitcqWF/go-ipld-cbor" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" - ipldcbor "gx/ipfs/QmY7L2aEa1rHjkSSbXJB8oC7825JTpUUvDygmM2JPQeqhr/go-ipld-cbor" ) var log = logging.Logger("merkledag") From 67f5e0294339594032595cbe074ee86c89cf5b62 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 30 Oct 2016 19:01:03 -0700 Subject: [PATCH 1613/3817] update go-libp2p-swarm with deadlock fixes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@06b8db4ec90fb686ec002b5dad377a31ebadb85f --- routing/mock/dht.go | 4 ++-- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index eb117afd6..07a79d65c 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - dht "gx/ipfs/QmTyXZijAwx3ptKzQkzq7BWBhmSJhjxLpDKF2Fp95WUd13/go-libp2p-kad-dht" + mocknet "gx/ipfs/QmQ7iWUfqrLEoJwsoNdrZu4625bKyhZCi4Sh6MfjywEfbG/go-libp2p/p2p/net/mock" + dht "gx/ipfs/QmSb1SYG4dxsuiscd8dyNvn4UuT6gKU8HzQCwa5jtGgGMR/go-libp2p-kad-dht" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - mocknet "gx/ipfs/QmcRa2qn6iCmap9bjp8jAwkvYAq13AUfxdY3rrYiaJbLum/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 12b6fc295..aee5c3b63 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -9,9 +9,9 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" + dhtpb "gx/ipfs/QmSb1SYG4dxsuiscd8dyNvn4UuT6gKU8HzQCwa5jtGgGMR/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" - dhtpb "gx/ipfs/QmTyXZijAwx3ptKzQkzq7BWBhmSJhjxLpDKF2Fp95WUd13/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index eb0881710..506966684 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,7 +2,7 @@ package proxy import ( context "context" - dhtpb "gx/ipfs/QmTyXZijAwx3ptKzQkzq7BWBhmSJhjxLpDKF2Fp95WUd13/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmSb1SYG4dxsuiscd8dyNvn4UuT6gKU8HzQCwa5jtGgGMR/go-libp2p-kad-dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" inet "gx/ipfs/QmdXimY9QHaasZmw6hWojWnCJvfgxETjZQfg9g6ZrA9wMX/go-libp2p-net" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 264038195..b50fdfa49 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,9 +4,9 @@ import ( "context" "errors" + dhtpb "gx/ipfs/QmSb1SYG4dxsuiscd8dyNvn4UuT6gKU8HzQCwa5jtGgGMR/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" - dhtpb "gx/ipfs/QmTyXZijAwx3ptKzQkzq7BWBhmSJhjxLpDKF2Fp95WUd13/go-libp2p-kad-dht/pb" kbucket "gx/ipfs/QmUKePKcUEXwdvJENZJ6z8mJjPaxLsDZ3V9CZjPPtyawPm/go-libp2p-kbucket" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 25268e7c4..7bbdaf354 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -7,7 +7,7 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" context "context" - dhtpb "gx/ipfs/QmTyXZijAwx3ptKzQkzq7BWBhmSJhjxLpDKF2Fp95WUd13/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmSb1SYG4dxsuiscd8dyNvn4UuT6gKU8HzQCwa5jtGgGMR/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 59cdd814e..87008840d 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,7 +3,7 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmTyXZijAwx3ptKzQkzq7BWBhmSJhjxLpDKF2Fp95WUd13/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmSb1SYG4dxsuiscd8dyNvn4UuT6gKU8HzQCwa5jtGgGMR/go-libp2p-kad-dht/pb" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) From e100d17ffcf91368ebe4e97c07bcf87bad7a2eb9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 30 Oct 2016 19:01:03 -0700 Subject: [PATCH 1614/3817] update go-libp2p-swarm with deadlock fixes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@2ba84ca317cd56c24647d681cfc84fc22ff731b3 --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 5b6e30794..951d89b60 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" + mocknet "gx/ipfs/QmQ7iWUfqrLEoJwsoNdrZu4625bKyhZCi4Sh6MfjywEfbG/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - mocknet "gx/ipfs/QmcRa2qn6iCmap9bjp8jAwkvYAq13AUfxdY3rrYiaJbLum/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { From 300f8829ccf8bd930ac582b4200670a7f16661ce Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 1 Nov 2016 14:00:02 -0700 Subject: [PATCH 1615/3817] namesys: return right after errors License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@0ee319347b397d34def2328cb1e23b7ec86a8f9d --- namesys/routing.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/namesys/routing.go b/namesys/routing.go index 33b5431d4..eb9652e7a 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -147,13 +147,16 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa if err != nil { log.Warning("RoutingResolve get failed.") resp <- err + return } entry = new(pb.IpnsEntry) err = proto.Unmarshal(val, entry) if err != nil { resp <- err + return } + resp <- nil }() @@ -162,7 +165,9 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa pubk, err := routing.GetPublicKey(r.routing, ctx, hash) if err != nil { resp <- err + return } + pubkey = pubk resp <- nil }() From e8ba0bbecb2e8ab53e6ed2ee5cd119683d4c4dff Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 1 Nov 2016 15:37:51 -0700 Subject: [PATCH 1616/3817] dht: update to dht code with fixed GetClosestPeers License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@3eebc714bf4158e5da1821cdcd5b551bc16cad17 --- routing/mock/dht.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 07a79d65c..1acb2cd58 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -4,7 +4,7 @@ import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" mocknet "gx/ipfs/QmQ7iWUfqrLEoJwsoNdrZu4625bKyhZCi4Sh6MfjywEfbG/go-libp2p/p2p/net/mock" - dht "gx/ipfs/QmSb1SYG4dxsuiscd8dyNvn4UuT6gKU8HzQCwa5jtGgGMR/go-libp2p-kad-dht" + dht "gx/ipfs/QmaWzyiqs7sUayh2G1EaovupWrA1qdKXqRMYA97ruU1xS5/go-libp2p-kad-dht" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index aee5c3b63..b04fdb0b2 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -9,12 +9,12 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" - dhtpb "gx/ipfs/QmSb1SYG4dxsuiscd8dyNvn4UuT6gKU8HzQCwa5jtGgGMR/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + dhtpb "gx/ipfs/QmaWzyiqs7sUayh2G1EaovupWrA1qdKXqRMYA97ruU1xS5/go-libp2p-kad-dht/pb" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" "gx/ipfs/QmdML3R42PRSwnt46jSuEts9bHSqLctVYEjJqMR3UYV8ki/go-libp2p-host" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 506966684..a825bb734 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,8 +2,8 @@ package proxy import ( context "context" - dhtpb "gx/ipfs/QmSb1SYG4dxsuiscd8dyNvn4UuT6gKU8HzQCwa5jtGgGMR/go-libp2p-kad-dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" + dhtpb "gx/ipfs/QmaWzyiqs7sUayh2G1EaovupWrA1qdKXqRMYA97ruU1xS5/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmdXimY9QHaasZmw6hWojWnCJvfgxETjZQfg9g6ZrA9wMX/go-libp2p-net" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index b50fdfa49..4a2d94f1a 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,12 +4,12 @@ import ( "context" "errors" - dhtpb "gx/ipfs/QmSb1SYG4dxsuiscd8dyNvn4UuT6gKU8HzQCwa5jtGgGMR/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" kbucket "gx/ipfs/QmUKePKcUEXwdvJENZJ6z8mJjPaxLsDZ3V9CZjPPtyawPm/go-libp2p-kbucket" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" + dhtpb "gx/ipfs/QmaWzyiqs7sUayh2G1EaovupWrA1qdKXqRMYA97ruU1xS5/go-libp2p-kad-dht/pb" host "gx/ipfs/QmdML3R42PRSwnt46jSuEts9bHSqLctVYEjJqMR3UYV8ki/go-libp2p-host" inet "gx/ipfs/QmdXimY9QHaasZmw6hWojWnCJvfgxETjZQfg9g6ZrA9wMX/go-libp2p-net" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 7bbdaf354..6171dc294 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -7,10 +7,10 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" context "context" - dhtpb "gx/ipfs/QmSb1SYG4dxsuiscd8dyNvn4UuT6gKU8HzQCwa5jtGgGMR/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + dhtpb "gx/ipfs/QmaWzyiqs7sUayh2G1EaovupWrA1qdKXqRMYA97ruU1xS5/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 87008840d..0f0afff2b 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmSb1SYG4dxsuiscd8dyNvn4UuT6gKU8HzQCwa5jtGgGMR/go-libp2p-kad-dht/pb" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + dhtpb "gx/ipfs/QmaWzyiqs7sUayh2G1EaovupWrA1qdKXqRMYA97ruU1xS5/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) From 150ce099470195cff4a5839183f567f9f73071cd Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 2 Nov 2016 21:56:34 -0400 Subject: [PATCH 1617/3817] Separate out the G.C. Locking from the Blockstore interface. Factored out of #3257 (Add support for multiple blockstores). License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-blockstore@a4a6958c2715497fd4b961d01aae60ab4b59478a --- blockstore/arc_cache_test.go | 2 +- blockstore/blockstore.go | 34 ++++++++++++++++++++++++++++------ blockstore/bloom_cache_test.go | 2 +- blockstore/caching.go | 4 ++-- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index d796214c5..0f7823c5c 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -13,7 +13,7 @@ import ( var exampleBlock = blocks.NewBlock([]byte("foo")) -func testArcCached(bs GCBlockstore, ctx context.Context) (*arccache, error) { +func testArcCached(bs Blockstore, ctx context.Context) (*arccache, error) { if ctx == nil { ctx = context.TODO() } diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index f5dc26c1c..274c1ee7b 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -39,9 +39,7 @@ type Blockstore interface { AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) } -type GCBlockstore interface { - Blockstore - +type GCLocker interface { // GCLock locks the blockstore for garbage collection. No operations // that expect to finish with a pin should ocurr simultaneously. // Reading during GC is safe, and requires no lock. @@ -58,6 +56,20 @@ type GCBlockstore interface { GCRequested() bool } +type GCBlockstore interface { + Blockstore + GCLocker +} + +func NewGCBlockstore(bs Blockstore, gcl GCLocker) GCBlockstore { + return gcBlockstore{bs, gcl} +} + +type gcBlockstore struct { + Blockstore + GCLocker +} + func NewBlockstore(d ds.Batching) *blockstore { var dsb ds.Batching dd := dsns.Wrap(d, BlockPrefix) @@ -223,6 +235,16 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) return output, nil } +func NewGCLocker() *gclocker { + return &gclocker{} +} + +type gclocker struct { + lk sync.RWMutex + gcreq int32 + gcreqlk sync.Mutex +} + type Unlocker interface { Unlock() } @@ -236,18 +258,18 @@ func (u *unlocker) Unlock() { u.unlock = nil // ensure its not called twice } -func (bs *blockstore) GCLock() Unlocker { +func (bs *gclocker) GCLock() Unlocker { atomic.AddInt32(&bs.gcreq, 1) bs.lk.Lock() atomic.AddInt32(&bs.gcreq, -1) return &unlocker{bs.lk.Unlock} } -func (bs *blockstore) PinLock() Unlocker { +func (bs *gclocker) PinLock() Unlocker { bs.lk.RLock() return &unlocker{bs.lk.RUnlock} } -func (bs *blockstore) GCRequested() bool { +func (bs *gclocker) GCRequested() bool { return atomic.LoadInt32(&bs.gcreq) > 0 } diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 8bdf567f0..72223cd44 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -14,7 +14,7 @@ import ( syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) -func testBloomCached(bs GCBlockstore, ctx context.Context) (*bloomcache, error) { +func testBloomCached(bs Blockstore, ctx context.Context) (*bloomcache, error) { if ctx == nil { ctx = context.TODO() } diff --git a/blockstore/caching.go b/blockstore/caching.go index d28401cf8..d19f47822 100644 --- a/blockstore/caching.go +++ b/blockstore/caching.go @@ -22,8 +22,8 @@ func DefaultCacheOpts() CacheOpts { } } -func CachedBlockstore(bs GCBlockstore, - ctx context.Context, opts CacheOpts) (cbs GCBlockstore, err error) { +func CachedBlockstore(bs Blockstore, + ctx context.Context, opts CacheOpts) (cbs Blockstore, err error) { cbs = bs if opts.HasBloomFilterSize < 0 || opts.HasBloomFilterHashes < 0 || From b42513cefb7abbb11f1e6bd2bfdd40b4730eb56f Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 2 Nov 2016 21:56:34 -0400 Subject: [PATCH 1618/3817] Separate out the G.C. Locking from the Blockstore interface. Factored out of #3257 (Add support for multiple blockstores). License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-unixfs@f60182927913c30c35dd796f83e8b9e4e741301a --- unixfs/mod/dagmodifier_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 810ec6f23..9c7ac89d7 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -22,7 +22,7 @@ import ( "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) -func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.GCBlockstore) { +func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.Blockstore) { dstore := ds.NewMapDatastore() tsds := sync.MutexWrap(dstore) bstore := blockstore.NewBlockstore(tsds) From 86af8bc836bdc0bfb3184f50958c3f18f9f66d6e Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 2 Nov 2016 21:56:34 -0400 Subject: [PATCH 1619/3817] Separate out the G.C. Locking from the Blockstore interface. Factored out of #3257 (Add support for multiple blockstores). License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-blockservice@8ec3091f5788fe6bbe971e04239fdcf6efe2371b --- blockservice/blockservice_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index d87a383e5..0415f8213 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -36,14 +36,14 @@ func TestWriteThroughWorks(t *testing.T) { } } -var _ blockstore.GCBlockstore = (*PutCountingBlockstore)(nil) +var _ blockstore.Blockstore = (*PutCountingBlockstore)(nil) type PutCountingBlockstore struct { - blockstore.GCBlockstore + blockstore.Blockstore PutCounter int } func (bs *PutCountingBlockstore) Put(block blocks.Block) error { bs.PutCounter++ - return bs.GCBlockstore.Put(block) + return bs.Blockstore.Put(block) } From 448e6d2a4f3310e16c0551b450a1ca8b02a7cf70 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 3 Nov 2016 20:06:32 -0700 Subject: [PATCH 1620/3817] update go-libp2p License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@03aa9e4b9742ef1714fa39cc3196b13cc2f7d5bc --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 951d89b60..747d9938d 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,7 +13,7 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmQ7iWUfqrLEoJwsoNdrZu4625bKyhZCi4Sh6MfjywEfbG/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmVN76ekoYakYa8WVDwhkUsnjt2MYuFpQs1uuU57T5KMD8/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" ) From b38395cd6d4fde68a02af1b2cc58b7e228946a3b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 3 Nov 2016 20:06:32 -0700 Subject: [PATCH 1621/3817] update go-libp2p License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@22e95c64975a2fa01a10f7a8d18eb75ba414e8cb --- routing/mock/dht.go | 4 ++-- routing/none/none_client.go | 2 +- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 1acb2cd58..c39c97fe9 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmQ7iWUfqrLEoJwsoNdrZu4625bKyhZCi4Sh6MfjywEfbG/go-libp2p/p2p/net/mock" - dht "gx/ipfs/QmaWzyiqs7sUayh2G1EaovupWrA1qdKXqRMYA97ruU1xS5/go-libp2p-kad-dht" + mocknet "gx/ipfs/QmVN76ekoYakYa8WVDwhkUsnjt2MYuFpQs1uuU57T5KMD8/go-libp2p/p2p/net/mock" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + dht "gx/ipfs/Qmct31kwWeGYkbwGmKPvUVp4BGsydhtkk69iM6NSGiJkfR/go-libp2p-kad-dht" ) type mocknetserver struct { diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 939ebea6e..71737b8e9 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -8,9 +8,9 @@ import ( routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + p2phost "gx/ipfs/QmWf338UyG5DKyemvoFiomDPtkVNHLsw3GAt9XXHX5ZtsM/go-libp2p-host" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" - p2phost "gx/ipfs/QmdML3R42PRSwnt46jSuEts9bHSqLctVYEjJqMR3UYV8ki/go-libp2p-host" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index b04fdb0b2..fe09c4cc2 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -11,12 +11,12 @@ import ( routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" + "gx/ipfs/QmWf338UyG5DKyemvoFiomDPtkVNHLsw3GAt9XXHX5ZtsM/go-libp2p-host" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/QmaWzyiqs7sUayh2G1EaovupWrA1qdKXqRMYA97ruU1xS5/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/Qmct31kwWeGYkbwGmKPvUVp4BGsydhtkk69iM6NSGiJkfR/go-libp2p-kad-dht/pb" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" - "gx/ipfs/QmdML3R42PRSwnt46jSuEts9bHSqLctVYEjJqMR3UYV8ki/go-libp2p-host" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index a825bb734..3b989fb0d 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -3,8 +3,8 @@ package proxy import ( context "context" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/QmaWzyiqs7sUayh2G1EaovupWrA1qdKXqRMYA97ruU1xS5/go-libp2p-kad-dht/pb" - inet "gx/ipfs/QmdXimY9QHaasZmw6hWojWnCJvfgxETjZQfg9g6ZrA9wMX/go-libp2p-net" + dhtpb "gx/ipfs/Qmct31kwWeGYkbwGmKPvUVp4BGsydhtkk69iM6NSGiJkfR/go-libp2p-kad-dht/pb" + inet "gx/ipfs/QmdysBu77i3YaagNtMAjiCJdeWWvds18ho5XEB784guQ41/go-libp2p-net" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 4a2d94f1a..22aa5711e 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -7,11 +7,11 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" kbucket "gx/ipfs/QmUKePKcUEXwdvJENZJ6z8mJjPaxLsDZ3V9CZjPPtyawPm/go-libp2p-kbucket" + host "gx/ipfs/QmWf338UyG5DKyemvoFiomDPtkVNHLsw3GAt9XXHX5ZtsM/go-libp2p-host" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/QmaWzyiqs7sUayh2G1EaovupWrA1qdKXqRMYA97ruU1xS5/go-libp2p-kad-dht/pb" - host "gx/ipfs/QmdML3R42PRSwnt46jSuEts9bHSqLctVYEjJqMR3UYV8ki/go-libp2p-host" - inet "gx/ipfs/QmdXimY9QHaasZmw6hWojWnCJvfgxETjZQfg9g6ZrA9wMX/go-libp2p-net" + dhtpb "gx/ipfs/Qmct31kwWeGYkbwGmKPvUVp4BGsydhtkk69iM6NSGiJkfR/go-libp2p-kad-dht/pb" + inet "gx/ipfs/QmdysBu77i3YaagNtMAjiCJdeWWvds18ho5XEB784guQ41/go-libp2p-net" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 6171dc294..c15fb6447 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -10,8 +10,8 @@ import ( pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/QmaWzyiqs7sUayh2G1EaovupWrA1qdKXqRMYA97ruU1xS5/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dhtpb "gx/ipfs/Qmct31kwWeGYkbwGmKPvUVp4BGsydhtkk69iM6NSGiJkfR/go-libp2p-kad-dht/pb" record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 0f0afff2b..bbf987e5f 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -4,8 +4,8 @@ import ( "testing" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - dhtpb "gx/ipfs/QmaWzyiqs7sUayh2G1EaovupWrA1qdKXqRMYA97ruU1xS5/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dhtpb "gx/ipfs/Qmct31kwWeGYkbwGmKPvUVp4BGsydhtkk69iM6NSGiJkfR/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From c206dec6f40ebe912b6b67c69d6dc838cbc51e51 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 3 Nov 2016 23:14:57 -0400 Subject: [PATCH 1622/3817] merkledag: optimize DagService GetLinks for Raw Nodes. A Raw Node can not possible have links, so there is no need to retrive the node. Once Raw Nodes are in common usage this can likely make a big difference in the GC and other places that just care about the Links. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@32d30ab5f0b852814962f4d98e611a8f47e96f12 --- ipld/merkledag/merkledag.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index b2a097a52..222adb550 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -114,6 +114,9 @@ func decodeBlock(b blocks.Block) (node.Node, error) { } func (n *dagService) GetLinks(ctx context.Context, c *cid.Cid) ([]*node.Link, error) { + if c.Type() == cid.Raw { + return nil, nil + } node, err := n.Get(ctx, c) if err != nil { return nil, err From ff1cba37c1eda3086c478f0ab429dc8eafb4597f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 5 Nov 2016 20:10:32 -0700 Subject: [PATCH 1623/3817] update to libp2p 4.0.4 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@5fed7589280760e4a49f814053481e2500f0c875 --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 747d9938d..9204a89ec 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,7 +13,7 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmVN76ekoYakYa8WVDwhkUsnjt2MYuFpQs1uuU57T5KMD8/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmQfvKShQ2v7nkfCE4ygisxpcSBFvBYaorQ54SibY6PGXV/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" ) From 01f8f836d1717f95a9dfb4ee3e81442cb23b72d3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 5 Nov 2016 20:10:32 -0700 Subject: [PATCH 1624/3817] update to libp2p 4.0.4 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@9e0faefb3f5aecf7828f364f124b707bc6235641 --- routing/mock/dht.go | 4 ++-- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index c39c97fe9..298f8cdef 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmVN76ekoYakYa8WVDwhkUsnjt2MYuFpQs1uuU57T5KMD8/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmQfvKShQ2v7nkfCE4ygisxpcSBFvBYaorQ54SibY6PGXV/go-libp2p/p2p/net/mock" + dht "gx/ipfs/QmTmmre42AYNbLX7N9nt7qo3DHVmRZ6ZoGisrYcCsBYuVw/go-libp2p-kad-dht" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - dht "gx/ipfs/Qmct31kwWeGYkbwGmKPvUVp4BGsydhtkk69iM6NSGiJkfR/go-libp2p-kad-dht" ) type mocknetserver struct { diff --git a/routing/supernode/client.go b/routing/supernode/client.go index fe09c4cc2..f67795875 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -11,11 +11,11 @@ import ( routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" + dhtpb "gx/ipfs/QmTmmre42AYNbLX7N9nt7qo3DHVmRZ6ZoGisrYcCsBYuVw/go-libp2p-kad-dht/pb" "gx/ipfs/QmWf338UyG5DKyemvoFiomDPtkVNHLsw3GAt9XXHX5ZtsM/go-libp2p-host" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/Qmct31kwWeGYkbwGmKPvUVp4BGsydhtkk69iM6NSGiJkfR/go-libp2p-kad-dht/pb" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 3b989fb0d..cf46dd69c 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,8 +2,8 @@ package proxy import ( context "context" + dhtpb "gx/ipfs/QmTmmre42AYNbLX7N9nt7qo3DHVmRZ6ZoGisrYcCsBYuVw/go-libp2p-kad-dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/Qmct31kwWeGYkbwGmKPvUVp4BGsydhtkk69iM6NSGiJkfR/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmdysBu77i3YaagNtMAjiCJdeWWvds18ho5XEB784guQ41/go-libp2p-net" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 22aa5711e..cecca688b 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -6,11 +6,11 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" + dhtpb "gx/ipfs/QmTmmre42AYNbLX7N9nt7qo3DHVmRZ6ZoGisrYcCsBYuVw/go-libp2p-kad-dht/pb" kbucket "gx/ipfs/QmUKePKcUEXwdvJENZJ6z8mJjPaxLsDZ3V9CZjPPtyawPm/go-libp2p-kbucket" host "gx/ipfs/QmWf338UyG5DKyemvoFiomDPtkVNHLsw3GAt9XXHX5ZtsM/go-libp2p-host" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/Qmct31kwWeGYkbwGmKPvUVp4BGsydhtkk69iM6NSGiJkfR/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmdysBu77i3YaagNtMAjiCJdeWWvds18ho5XEB784guQ41/go-libp2p-net" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index c15fb6447..7a81c6629 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -7,11 +7,11 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" context "context" + dhtpb "gx/ipfs/QmTmmre42AYNbLX7N9nt7qo3DHVmRZ6ZoGisrYcCsBYuVw/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dhtpb "gx/ipfs/Qmct31kwWeGYkbwGmKPvUVp4BGsydhtkk69iM6NSGiJkfR/go-libp2p-kad-dht/pb" record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index bbf987e5f..57816bf5d 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,9 +3,9 @@ package supernode import ( "testing" + dhtpb "gx/ipfs/QmTmmre42AYNbLX7N9nt7qo3DHVmRZ6ZoGisrYcCsBYuVw/go-libp2p-kad-dht/pb" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dhtpb "gx/ipfs/Qmct31kwWeGYkbwGmKPvUVp4BGsydhtkk69iM6NSGiJkfR/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 9a989442e7cd6463a7473cf7d6ed03e823c8b38a Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Sun, 11 Sep 2016 05:09:43 +0200 Subject: [PATCH 1625/3817] coreapi: get going, add Cat() and Ls() License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/interface-go-ipfs-core@d6cc518f50f331dfd20dc79e87319f307fa2b5e7 --- coreiface/interface.go | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 coreiface/interface.go diff --git a/coreiface/interface.go b/coreiface/interface.go new file mode 100644 index 000000000..694a116a5 --- /dev/null +++ b/coreiface/interface.go @@ -0,0 +1,56 @@ +package iface + +import ( + "context" + "errors" + "io" + + cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" +) + +// type CoreAPI interface { +// ID() CoreID +// Version() CoreVersion +// } + +type Link struct { + Name string + Size uint64 + Cid *cid.Cid +} + +type Reader interface { + io.ReadSeeker + io.Closer +} + +type UnixfsAPI interface { + Cat(context.Context, string) (Reader, error) + Ls(context.Context, string) ([]*Link, error) +} + +// type ObjectAPI interface { +// New() (cid.Cid, Object) +// Get(string) (Object, error) +// Links(string) ([]*Link, error) +// Data(string) (Reader, error) +// Stat(string) (ObjectStat, error) +// Put(Object) (cid.Cid, error) +// SetData(string, Reader) (cid.Cid, error) +// AppendData(string, Data) (cid.Cid, error) +// AddLink(string, string, string) (cid.Cid, error) +// RmLink(string, string) (cid.Cid, error) +// } + +// type ObjectStat struct { +// Cid cid.Cid +// NumLinks int +// BlockSize int +// LinksSize int +// DataSize int +// CumulativeSize int +// } + +var ErrIsDir = errors.New("object is a directory") +var ErrIsNonDag = errors.New("not a merkledag object") +var ErrOffline = errors.New("can't resolve, ipfs node is offline") From b83c5e3fc362276e9bfa6a34848598556289bdfa Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Tue, 20 Sep 2016 04:30:43 +0200 Subject: [PATCH 1626/3817] coreapi: add Add() License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/interface-go-ipfs-core@87e9bc0419bdabee9aa6534e53a669484ac2e706 --- coreiface/interface.go | 1 + 1 file changed, 1 insertion(+) diff --git a/coreiface/interface.go b/coreiface/interface.go index 694a116a5..297e8bfed 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -25,6 +25,7 @@ type Reader interface { } type UnixfsAPI interface { + Add(context.Context, io.Reader) (*cid.Cid, error) Cat(context.Context, string) (Reader, error) Ls(context.Context, string) ([]*Link, error) } From 54a9bc53742ad876e1b9705c10b50fc47d58377d Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Sun, 30 Oct 2016 03:09:48 +0100 Subject: [PATCH 1627/3817] coreapi: reuse go-ipld-node.Link License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/interface-go-ipfs-core@c34bbcac7cbf4b4b075dac8474dad72ef4b6063b --- coreiface/interface.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 297e8bfed..18328a2a0 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -5,6 +5,7 @@ import ( "errors" "io" + ipld "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ) @@ -13,11 +14,7 @@ import ( // Version() CoreVersion // } -type Link struct { - Name string - Size uint64 - Cid *cid.Cid -} +type Link ipld.Link type Reader interface { io.ReadSeeker From fdf795607b140caba491ac6c0845389a5c81e6fc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 10 Nov 2016 17:38:10 -0800 Subject: [PATCH 1628/3817] update to go-libp2p 4.1.0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@2f06e829e1a0d980d4b2343b4e0d12989fbaca8f --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 9204a89ec..3ac209038 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,7 +13,7 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmQfvKShQ2v7nkfCE4ygisxpcSBFvBYaorQ54SibY6PGXV/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmUYzZRJcuUxLSnSzF1bSyw1jYbNAULkBrbS6rnr7F72uK/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" ) From e2cefebdc955a5b59c4a54f454446fe6a2441a8c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 10 Nov 2016 17:38:10 -0800 Subject: [PATCH 1629/3817] update to go-libp2p 4.1.0 License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@9cab2bab02910d5e25176a37af5393c5ba0a01cc --- routing/mock/dht.go | 4 ++-- routing/none/none_client.go | 2 +- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 298f8cdef..382df6fca 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,8 +3,8 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmQfvKShQ2v7nkfCE4ygisxpcSBFvBYaorQ54SibY6PGXV/go-libp2p/p2p/net/mock" - dht "gx/ipfs/QmTmmre42AYNbLX7N9nt7qo3DHVmRZ6ZoGisrYcCsBYuVw/go-libp2p-kad-dht" + mocknet "gx/ipfs/QmUYzZRJcuUxLSnSzF1bSyw1jYbNAULkBrbS6rnr7F72uK/go-libp2p/p2p/net/mock" + dht "gx/ipfs/QmaQrN5Gi5jz2ViKuJ5PU2LXV79D6vGuH7eVQnwxpoRqrd/go-libp2p-kad-dht" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 71737b8e9..a2e7fab79 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -8,9 +8,9 @@ import ( routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - p2phost "gx/ipfs/QmWf338UyG5DKyemvoFiomDPtkVNHLsw3GAt9XXHX5ZtsM/go-libp2p-host" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + p2phost "gx/ipfs/Qmb6UFbVu1grhv5o5KnouvtZ6cqdrjXj6zLejAHWunxgCt/go-libp2p-host" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index f67795875..ad4d3d642 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -11,11 +11,11 @@ import ( routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" - dhtpb "gx/ipfs/QmTmmre42AYNbLX7N9nt7qo3DHVmRZ6ZoGisrYcCsBYuVw/go-libp2p-kad-dht/pb" - "gx/ipfs/QmWf338UyG5DKyemvoFiomDPtkVNHLsw3GAt9XXHX5ZtsM/go-libp2p-host" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + dhtpb "gx/ipfs/QmaQrN5Gi5jz2ViKuJ5PU2LXV79D6vGuH7eVQnwxpoRqrd/go-libp2p-kad-dht/pb" + "gx/ipfs/Qmb6UFbVu1grhv5o5KnouvtZ6cqdrjXj6zLejAHWunxgCt/go-libp2p-host" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index cf46dd69c..66974c045 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,9 +2,9 @@ package proxy import ( context "context" - dhtpb "gx/ipfs/QmTmmre42AYNbLX7N9nt7qo3DHVmRZ6ZoGisrYcCsBYuVw/go-libp2p-kad-dht/pb" + inet "gx/ipfs/QmU3pGGVT1riXp5dBJbNrGpxssVScfvk9236drRHZZbKJ1/go-libp2p-net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - inet "gx/ipfs/QmdysBu77i3YaagNtMAjiCJdeWWvds18ho5XEB784guQ41/go-libp2p-net" + dhtpb "gx/ipfs/QmaQrN5Gi5jz2ViKuJ5PU2LXV79D6vGuH7eVQnwxpoRqrd/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index cecca688b..75ec2dd4d 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -6,12 +6,12 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" - dhtpb "gx/ipfs/QmTmmre42AYNbLX7N9nt7qo3DHVmRZ6ZoGisrYcCsBYuVw/go-libp2p-kad-dht/pb" + inet "gx/ipfs/QmU3pGGVT1riXp5dBJbNrGpxssVScfvk9236drRHZZbKJ1/go-libp2p-net" kbucket "gx/ipfs/QmUKePKcUEXwdvJENZJ6z8mJjPaxLsDZ3V9CZjPPtyawPm/go-libp2p-kbucket" - host "gx/ipfs/QmWf338UyG5DKyemvoFiomDPtkVNHLsw3GAt9XXHX5ZtsM/go-libp2p-host" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - inet "gx/ipfs/QmdysBu77i3YaagNtMAjiCJdeWWvds18ho5XEB784guQ41/go-libp2p-net" + dhtpb "gx/ipfs/QmaQrN5Gi5jz2ViKuJ5PU2LXV79D6vGuH7eVQnwxpoRqrd/go-libp2p-kad-dht/pb" + host "gx/ipfs/Qmb6UFbVu1grhv5o5KnouvtZ6cqdrjXj6zLejAHWunxgCt/go-libp2p-host" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 7a81c6629..58cbc32e2 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -7,10 +7,10 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" context "context" - dhtpb "gx/ipfs/QmTmmre42AYNbLX7N9nt7qo3DHVmRZ6ZoGisrYcCsBYuVw/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + dhtpb "gx/ipfs/QmaQrN5Gi5jz2ViKuJ5PU2LXV79D6vGuH7eVQnwxpoRqrd/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 57816bf5d..eda46cabc 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmTmmre42AYNbLX7N9nt7qo3DHVmRZ6ZoGisrYcCsBYuVw/go-libp2p-kad-dht/pb" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + dhtpb "gx/ipfs/QmaQrN5Gi5jz2ViKuJ5PU2LXV79D6vGuH7eVQnwxpoRqrd/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) From af6044b5d6f3ac201c65ad43a6a64905ace911a6 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 14 Nov 2016 01:49:39 -0500 Subject: [PATCH 1630/3817] blockstore: fix TODO and avoid calling ds.NewKey License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-blockstore@970d14cd1e0d257e0d53eb0e7b7536da81fbf704 --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 274c1ee7b..004a5bf1f 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -196,7 +196,7 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) } // need to convert to key.Key using key.KeyFromDsKey. - c, err := dshelp.DsKeyToCid(ds.NewKey(e.Key)) // TODO: calling NewKey isnt free + c, err := dshelp.DsKeyStringToCid(e.Key) if err != nil { log.Warningf("error parsing key from DsKey: ", err) return nil, true From 65ba3494f68e1ac31943dc8d7e59127917f77991 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 14 Nov 2016 01:49:39 -0500 Subject: [PATCH 1631/3817] blockstore: fix TODO and avoid calling ds.NewKey License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-ds-help@08888f5e463874f643bf39b0d5318fc374dce4e2 --- datastore/dshelp/key.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 20308d704..1f2248a22 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -26,3 +26,11 @@ func DsKeyToCid(dsKey ds.Key) (*cid.Cid, error) { } return cid.Cast(kb) } + +func DsKeyStringToCid(dsKey string) (*cid.Cid, error) { + kb, err := base32.RawStdEncoding.DecodeString(dsKey[1:]) + if err != nil { + return nil, err + } + return cid.Cast(kb) +} From 0058db4cb919fe860f74bf2745172c112b965bb9 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 14 Nov 2016 16:57:40 -0500 Subject: [PATCH 1632/3817] blockstore: remove expensive debug statement in AllKeysChan License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-blockstore@fb2b28398795ff1b8df0a55e04657ee68e82ed44 --- blockstore/blockstore.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 274c1ee7b..bd944fc54 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -202,8 +202,6 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) return nil, true } - log.Debug("blockstore: query got key", c) - return c, true } } From 725744b25edc198fc3d04e1ebeb6040cf86b2441 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 15 Nov 2016 18:00:49 -0800 Subject: [PATCH 1633/3817] update to newer ipld node interface with Copy and better Tree License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@7cf2f1e00c7fe49a740df1dc3d3d9adc128df3e2 --- unixfs/io/dagreader.go | 2 +- unixfs/io/dirbuilder.go | 2 +- unixfs/io/resolve.go | 2 +- unixfs/mod/dagmodifier.go | 8 ++++---- unixfs/test/utils.go | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 2d25895f1..61276e5b8 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -12,7 +12,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index df2f18b40..1fe8fbd99 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -5,7 +5,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) type directoryBuilder struct { diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index a796c6bb7..80d51773c 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -6,7 +6,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" ) func ResolveUnixfsOnce(ctx context.Context, ds dag.DAGService, nd node.Node, name string) (*node.Link, error) { diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 63afd33e7..4de446bce 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -15,9 +15,9 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) var ErrSeekFail = errors.New("failed to seek properly") @@ -53,7 +53,7 @@ func NewDagModifier(ctx context.Context, from node.Node, serv mdag.DAGService, s } return &DagModifier{ - curNode: pbn.Copy(), + curNode: pbn.Copy().(*mdag.ProtoNode), dagserv: serv, splitter: spl, ctx: ctx, @@ -373,7 +373,7 @@ func (dm *DagModifier) GetNode() (*mdag.ProtoNode, error) { if err != nil { return nil, err } - return dm.curNode.Copy(), nil + return dm.curNode.Copy().(*mdag.ProtoNode), nil } // HasChanges returned whether or not there are unflushed changes to this dag diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 4b1ef7c49..d4eb01020 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -14,7 +14,7 @@ import ( mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) From aeeb14818886cbfebe60809988b6b2170d6ed5a7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 15 Nov 2016 18:00:49 -0800 Subject: [PATCH 1634/3817] update to newer ipld node interface with Copy and better Tree License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@9569d2e0c091d1ca0565c20b45b15383c2778dcf --- namesys/namesys.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/routing.go | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 87294190d..eb4408294 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,7 +6,7 @@ import ( context "context" path "github.com/ipfs/go-ipfs/path" - routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" + routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index d23f66fb2..6883589b6 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -14,7 +14,7 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ft "github.com/ipfs/go-ipfs/unixfs" - routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" + routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 727270504..3e108e6b4 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,10 +11,10 @@ import ( path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" diff --git a/namesys/routing.go b/namesys/routing.go index eb9652e7a..c78532013 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -12,12 +12,12 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" + routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) From bfa20138bc3037207a8ea55d67651e93ee315375 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 15 Nov 2016 18:00:49 -0800 Subject: [PATCH 1635/3817] update to newer ipld node interface with Copy and better Tree License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@3a846ff3c61df384ccf082a92c6111989c8dc6d3 --- routing/mock/centralized_client.go | 4 ++-- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/mock/interface.go | 4 ++-- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 4 ++-- routing/supernode/client.go | 6 +++--- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 753f8bc86..4b78d535c 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,14 +8,14 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" "github.com/ipfs/go-ipfs/thirdparty/testutil" - routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ma "gx/ipfs/QmUAQaWbKxGCUTuoQVvvicbQNZ9APF5pDGWyAZSe93AtKH/go-multiaddr" + routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" dhtpb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 424f3ea58..6ba6bb6ca 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -9,9 +9,9 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 2cd9855c3..013a95884 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -9,8 +9,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 382df6fca..7f9d864b5 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -4,7 +4,7 @@ import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" mocknet "gx/ipfs/QmUYzZRJcuUxLSnSzF1bSyw1jYbNAULkBrbS6rnr7F72uK/go-libp2p/p2p/net/mock" - dht "gx/ipfs/QmaQrN5Gi5jz2ViKuJ5PU2LXV79D6vGuH7eVQnwxpoRqrd/go-libp2p-kad-dht" + dht "gx/ipfs/Qmap6Qnt8RRvQ1BawQw4HZKHaSJAZC5VybyapizCXGdfUK/go-libp2p-kad-dht" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index a29716563..63e893c4e 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,10 +10,10 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" + routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index a2e7fab79..62f46dadc 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,11 +6,11 @@ import ( repo "github.com/ipfs/go-ipfs/repo" - routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" p2phost "gx/ipfs/Qmb6UFbVu1grhv5o5KnouvtZ6cqdrjXj6zLejAHWunxgCt/go-libp2p-host" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index b675cbec5..dc86585c1 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,12 +7,12 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" diff --git a/routing/supernode/client.go b/routing/supernode/client.go index ad4d3d642..33b01a134 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,14 +8,14 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" + routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/QmaQrN5Gi5jz2ViKuJ5PU2LXV79D6vGuH7eVQnwxpoRqrd/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/Qmap6Qnt8RRvQ1BawQw4HZKHaSJAZC5VybyapizCXGdfUK/go-libp2p-kad-dht/pb" "gx/ipfs/Qmb6UFbVu1grhv5o5KnouvtZ6cqdrjXj6zLejAHWunxgCt/go-libp2p-host" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 66974c045..690011d66 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -4,7 +4,7 @@ import ( context "context" inet "gx/ipfs/QmU3pGGVT1riXp5dBJbNrGpxssVScfvk9236drRHZZbKJ1/go-libp2p-net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/QmaQrN5Gi5jz2ViKuJ5PU2LXV79D6vGuH7eVQnwxpoRqrd/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/Qmap6Qnt8RRvQ1BawQw4HZKHaSJAZC5VybyapizCXGdfUK/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 75ec2dd4d..922169924 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -10,7 +10,7 @@ import ( kbucket "gx/ipfs/QmUKePKcUEXwdvJENZJ6z8mJjPaxLsDZ3V9CZjPPtyawPm/go-libp2p-kbucket" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/QmaQrN5Gi5jz2ViKuJ5PU2LXV79D6vGuH7eVQnwxpoRqrd/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/Qmap6Qnt8RRvQ1BawQw4HZKHaSJAZC5VybyapizCXGdfUK/go-libp2p-kad-dht/pb" host "gx/ipfs/Qmb6UFbVu1grhv5o5KnouvtZ6cqdrjXj6zLejAHWunxgCt/go-libp2p-host" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 58cbc32e2..71e59d4be 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -10,7 +10,7 @@ import ( pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/QmaQrN5Gi5jz2ViKuJ5PU2LXV79D6vGuH7eVQnwxpoRqrd/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/Qmap6Qnt8RRvQ1BawQw4HZKHaSJAZC5VybyapizCXGdfUK/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index eda46cabc..83dfd9aa4 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -4,7 +4,7 @@ import ( "testing" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - dhtpb "gx/ipfs/QmaQrN5Gi5jz2ViKuJ5PU2LXV79D6vGuH7eVQnwxpoRqrd/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/Qmap6Qnt8RRvQ1BawQw4HZKHaSJAZC5VybyapizCXGdfUK/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) From 19038a01430459f16f36d3e7e6478f6f5e37bae5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 15 Nov 2016 18:00:49 -0800 Subject: [PATCH 1636/3817] update to newer ipld node interface with Copy and better Tree License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@49c8ac01de04e9ba20143a9964229d2cba7dca81 --- ipld/merkledag/coding.go | 4 ++-- ipld/merkledag/merkledag.go | 6 +++--- ipld/merkledag/merkledag_test.go | 8 ++++---- ipld/merkledag/node.go | 16 +++++++++++----- ipld/merkledag/node_test.go | 4 ++-- ipld/merkledag/raw.go | 18 +++++++++++++++--- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/traverse/traverse_test.go | 2 +- ipld/merkledag/utils/diff.go | 6 +++--- ipld/merkledag/utils/utils.go | 4 ++-- ipld/merkledag/utils/utils_test.go | 2 +- 11 files changed, 45 insertions(+), 27 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 2745b833d..2c6751a6e 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -6,9 +6,9 @@ import ( pb "github.com/ipfs/go-ipfs/merkledag/pb" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 222adb550..423da0cc8 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -11,10 +11,10 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - ipldcbor "gx/ipfs/QmRcAVqrbY5wryx7hfNLtiUZbCcstzaJL7YJFBboitcqWF/go-ipld-cbor" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + ipldcbor "gx/ipfs/QmVVfh9urmDSL1upPtAKKMxFUwW1R6hYr95uCuJUP8RhUu/go-ipld-cbor" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) var log = logging.Logger("merkledag") diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index b3209a18a..c75b33692 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -21,10 +21,10 @@ import ( dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) func TestNode(t *testing.T) { @@ -401,7 +401,7 @@ func TestGetRawNodes(t *testing.T) { t.Fatal("raw blocks shouldnt have links") } - if out.Tree() != nil { + if out.Tree("", -1) != nil { t.Fatal("tree should return no paths in a raw block") } @@ -446,7 +446,7 @@ func TestProtoNodeResolve(t *testing.T) { t.Fatal("how did we get anything else?") } - tvals := nd.Tree() + tvals := nd.Tree("", -1) if len(tvals) != 1 || tvals[0] != "foo" { t.Fatal("expected tree to return []{\"foo\"}") } diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 8111cb8c8..332b083fa 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -4,10 +4,10 @@ import ( "context" "fmt" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") @@ -137,7 +137,7 @@ func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds DAGService, name strin // Copy returns a copy of the node. // NOTE: Does not make copies of Node objects in the links. -func (n *ProtoNode) Copy() *ProtoNode { +func (n *ProtoNode) Copy() node.Node { nnode := new(ProtoNode) if len(n.data) > 0 { nnode.data = make([]byte, len(n.data)) @@ -169,7 +169,7 @@ func (n *ProtoNode) SetData(d []byte) { // UpdateNodeLink return a copy of the node with the link name set to point to // that. If a link of the same name existed, it is removed. func (n *ProtoNode) UpdateNodeLink(name string, that *ProtoNode) (*ProtoNode, error) { - newnode := n.Copy() + newnode := n.Copy().(*ProtoNode) err := newnode.RemoveNodeLink(name) err = nil // ignore error err = newnode.AddNodeLink(name, that) @@ -270,7 +270,13 @@ func (n *ProtoNode) ResolveLink(path []string) (*node.Link, []string, error) { return lnk, path[1:], nil } -func (n *ProtoNode) Tree() []string { +func (n *ProtoNode) Tree(p string, depth int) []string { + // ProtoNodes are only ever one path deep, anything below that results in + // nothing + if p != "" { + return nil + } + out := make([]string, 0, len(n.links)) for _, lnk := range n.links { out = append(out, lnk.Name) diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index 996c82622..e57cf4059 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -7,7 +7,7 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" ) func TestRemoveLink(t *testing.T) { @@ -121,7 +121,7 @@ func TestNodeCopy(t *testing.T) { nd.SetData([]byte("testing")) - ond := nd.Copy() + ond := nd.Copy().(*ProtoNode) ond.SetData(nil) if nd.Data() == nil { diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 42f56fc4a..7479d7e11 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -3,9 +3,9 @@ package merkledag import ( "github.com/ipfs/go-ipfs/blocks" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) type RawNode struct { @@ -32,10 +32,22 @@ func (rn *RawNode) Resolve(path []string) (interface{}, []string, error) { return nil, nil, ErrLinkNotFound } -func (rn *RawNode) Tree() []string { +func (rn *RawNode) Tree(p string, depth int) []string { return nil } +func (rn *RawNode) Copy() node.Node { + copybuf := make([]byte, len(rn.RawData())) + copy(copybuf, rn.RawData()) + nblk, err := blocks.NewBlockWithCid(rn.RawData(), rn.Cid()) + if err != nil { + // programmer error + panic("failure attempting to clone raw block: " + err.Error()) + } + + return &RawNode{nblk} +} + func (rn *RawNode) Size() (uint64, error) { return uint64(len(rn.RawData())), nil } diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index 85ccc3075..35ec74d8f 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -5,7 +5,7 @@ import ( "context" "errors" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" ) // Order is an identifier for traversal algorithm orders diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 74f2a6f46..7bd69fbe5 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -8,7 +8,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" ) func TestDFSPreNoSkip(t *testing.T) { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 5d2cfbd6f..e7511ae6d 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -7,7 +7,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" context "context" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) const ( @@ -99,8 +99,8 @@ func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.ProtoNode) ([]*Chang } var out []*Change - clean_a := a.Copy() - clean_b := b.Copy() + clean_a := a.Copy().(*dag.ProtoNode) + clean_b := b.Copy().(*dag.ProtoNode) // strip out unchanged stuff for _, lnk := range a.Links() { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index f57cc6cff..9d21bc81c 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -10,7 +10,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) @@ -44,7 +44,7 @@ func NewDagEditor(root *dag.ProtoNode, source dag.DAGService) *Editor { } func (e *Editor) GetNode() *dag.ProtoNode { - return e.root.Copy() + return e.root.Copy().(*dag.ProtoNode) } func (e *Editor) GetDagService() dag.DAGService { diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index c2788f3a7..d45a89917 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" context "context" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) func TestAddLink(t *testing.T) { From a726738979f4d056fe3c35b191f9830eb06e9c7c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 15 Nov 2016 18:00:49 -0800 Subject: [PATCH 1637/3817] update to newer ipld node interface with Copy and better Tree License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@943dbbe0d853384c9cd7c8cee3e950739011f22b --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 2 +- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index d2607bdbe..c09480ea8 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,7 +8,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 2263d5111..f83aa40d8 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -12,9 +12,9 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 65c480ab8..a0e7d88ab 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -10,10 +10,10 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" context "context" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) func randNode() (*mdag.ProtoNode, *cid.Cid) { diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 2d7566b77..02a279bf9 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -13,10 +13,10 @@ import ( "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 3a33219ac..7d69f4ce4 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -9,7 +9,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) func ignoreCids(_ *cid.Cid) {} From f2f7bfa8581d42acd5193d13165e65f647f68a02 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 15 Nov 2016 18:00:49 -0800 Subject: [PATCH 1638/3817] update to newer ipld node interface with Copy and better Tree License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@a8365a42815401d426e017eb6ddf94ab0351720c --- path/path.go | 2 +- path/resolver.go | 4 ++-- path/resolver_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/path/path.go b/path/path.go index dbc67189a..e9f300df9 100644 --- a/path/path.go +++ b/path/path.go @@ -5,7 +5,7 @@ import ( "path" "strings" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) // ErrBadPath is returned when a given path is incorrectly formatted diff --git a/path/resolver.go b/path/resolver.go index e284ce136..2c57eff42 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -10,8 +10,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) var log = logging.Logger("path") diff --git a/path/resolver_test.go b/path/resolver_test.go index 68e67b36a..bf13fac0a 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -9,7 +9,7 @@ import ( dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" util "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) From 9f7a17cc3eca10af79fb0e0c4ff2468fba375ecb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 15 Nov 2016 18:00:49 -0800 Subject: [PATCH 1639/3817] update to newer ipld node interface with Copy and better Tree License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-ds-help@65a4ab2e346731f2dd53b7f373ad671b0bb2d8b7 --- datastore/dshelp/key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 1f2248a22..5e89209f5 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -1,9 +1,9 @@ package dshelp import ( - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" base32 "gx/ipfs/Qmb1DA2A9LS2wR4FFweB4uEDomFsdmnw1VLawLE1yQzudj/base32" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) // TODO: put this code into the go-datastore itself From 0fbb197434a311e38b0a9c931510e16a3a5c17c9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 15 Nov 2016 18:00:49 -0800 Subject: [PATCH 1640/3817] update to newer ipld node interface with Copy and better Tree License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@07f1af9aa37d454a502caead140e37edd088cad6 --- blockstore/arc_cache.go | 2 +- blockstore/arc_cache_test.go | 2 +- blockstore/blockstore.go | 2 +- blockstore/blockstore_test.go | 2 +- blockstore/bloom_cache.go | 2 +- blockstore/util/remove.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 8bc74436c..145dbe011 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -7,8 +7,8 @@ import ( "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) type arccache struct { diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 0f7823c5c..7a977680a 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -6,9 +6,9 @@ import ( "github.com/ipfs/go-ipfs/blocks" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) var exampleBlock = blocks.NewBlock([]byte("foo")) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index ecc54bb90..953737f9c 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -12,10 +12,10 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dsns "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/namespace" dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index abe8a1a72..23e754ed7 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -9,11 +9,11 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" ds_sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) func TestGetWhenKeyNotPresent(t *testing.T) { diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 3febffd01..a0ef90333 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -8,7 +8,7 @@ import ( "github.com/ipfs/go-ipfs/blocks" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" bloom "gx/ipfs/QmeiMCBkYHxkDkDfnDadzz4YxY5ruL5Pj499essE4vRsGM/bbloom" ) diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 01f2ce44e..1c8f0c31e 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -6,8 +6,8 @@ import ( bs "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/pin" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) // RemovedBlock is used to respresent the result of removing a block. From f9d92ae74d60501fa02c072d1f7cc581c8ce675d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 15 Nov 2016 18:00:49 -0800 Subject: [PATCH 1641/3817] update to newer ipld node interface with Copy and better Tree License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@6faf71a6925b8cd92d4b146eae48de7362c69dd9 --- blockservice/blockservice.go | 2 +- blockservice/test/blocks_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 8f1d069ab..ae5bec193 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -13,7 +13,7 @@ import ( exchange "github.com/ipfs/go-ipfs/exchange" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) var log = logging.Logger("blockservice") diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 4aae38bef..813a6ff96 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -12,10 +12,10 @@ import ( . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) func newObject(data []byte) blocks.Block { From 4f28fc777233e298a426081032b35501ea5ae8eb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 15 Nov 2016 18:00:49 -0800 Subject: [PATCH 1642/3817] update to newer ipld node interface with Copy and better Tree License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/interface-go-ipfs-core@23f95c6d1e4016ab5a01c9339b3926fdace37198 --- coreiface/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 18328a2a0..c1c83fa26 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -5,8 +5,8 @@ import ( "errors" "io" - ipld "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + ipld "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) // type CoreAPI interface { From 257528d9f73471bc9c6530c1bca81422bb26ca78 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 15 Nov 2016 18:00:49 -0800 Subject: [PATCH 1643/3817] update to newer ipld node interface with Copy and better Tree License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@c5598ad4b658de5ba60d75d8251f793df230a091 --- mfs/dir.go | 6 +++--- mfs/file.go | 2 +- mfs/mfs_test.go | 4 ++-- mfs/ops.go | 2 +- mfs/repub_test.go | 2 +- mfs/system.go | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index e425a6094..3ff28f5fd 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -14,7 +14,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" ) var ErrNotYetImplemented = errors.New("not yet implemented") @@ -86,7 +86,7 @@ func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) { return nil, err } - return d.node.Copy(), nil + return d.node.Copy().(*dag.ProtoNode), nil } func (d *Directory) updateChild(name string, nd node.Node) error { @@ -411,5 +411,5 @@ func (d *Directory) GetNode() (node.Node, error) { return nil, err } - return d.node.Copy(), nil + return d.node.Copy().(*dag.ProtoNode), nil } diff --git a/mfs/file.go b/mfs/file.go index 72be2117a..65b9f6cc7 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 04513d3e3..22fa3f774 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -24,11 +24,11 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) func emptyDirNode() *dag.ProtoNode { diff --git a/mfs/ops.go b/mfs/ops.go index a27eb7d4a..0df570463 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -9,7 +9,7 @@ import ( path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" ) // Mv moves the file or directory at 'src' to 'dst' diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 6cb38850a..f56b5a84d 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -7,7 +7,7 @@ import ( ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" "context" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index a565d7346..bb1f4baf0 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -19,8 +19,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - node "gx/ipfs/QmU7bFWQ793qmvNy7outdCaMfSDNk8uqhx4VNrxYj5fj5g/go-ipld-node" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) var ErrNotExist = errors.New("no such rootfs") From 6791b43841c5f69f6f590ce79c2eb89b6da24d6c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 15 Nov 2016 18:00:49 -0800 Subject: [PATCH 1644/3817] update to newer ipld node interface with Copy and better Tree License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@2ac36f5a30c1892e8dd71077b317e2a0a6ccab39 --- exchange/offline/offline.go | 2 +- exchange/offline/offline_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 5017cc8df..b0df47f13 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -9,7 +9,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 3ec1478e8..7b3cc1ee9 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -8,10 +8,10 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ds_sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) func TestBlockReturnsErr(t *testing.T) { From c20bfadda94de1b8715395c9d7c5238f23ab4bf0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 15 Nov 2016 18:00:49 -0800 Subject: [PATCH 1645/3817] update to newer ipld node interface with Copy and better Tree License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-interface@ace823f6a9d766e8cb8137dd34e2ddae71f1b9b3 --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index caf372001..1ee0eaf0e 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -7,7 +7,7 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" - cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid" + cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) // Any type that implements exchange.Interface may be used as an IPFS block From 90e71bd1621e71691ae866044c726bb1fb0098b2 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 21 Nov 2016 17:12:49 +0100 Subject: [PATCH 1646/3817] Make unixio.DagReader an interface License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@b736dc15fa72acd2dd0fee86ece4c6b288c95f96 --- unixfs/io/dagreader.go | 35 +++++++++++++++++++++-------------- unixfs/io/dagreader_test.go | 2 +- unixfs/mod/dagmodifier.go | 2 +- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 61276e5b8..da1fd65c8 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -20,8 +20,15 @@ var ErrIsDir = errors.New("this dag node is a directory") var ErrCantReadSymlinks = errors.New("cannot currently read symlinks") +type DagReader interface { + ReadSeekCloser + Size() uint64 + CtxReadFull(context.Context, []byte) (int, error) + Offset() int64 +} + // DagReader provides a way to easily read the data contained in a dag. -type DagReader struct { +type pbDagReader struct { serv mdag.DAGService // the node being read @@ -59,10 +66,10 @@ type ReadSeekCloser interface { // NewDagReader creates a new reader object that reads the data represented by // the given node, using the passed in DAGService for data retreival -func NewDagReader(ctx context.Context, n node.Node, serv mdag.DAGService) (*DagReader, error) { +func NewDagReader(ctx context.Context, n node.Node, serv mdag.DAGService) (DagReader, error) { switch n := n.(type) { case *mdag.RawNode: - return &DagReader{ + return &pbDagReader{ buf: NewRSNCFromBytes(n.RawData()), }, nil case *mdag.ProtoNode: @@ -101,10 +108,10 @@ func NewDagReader(ctx context.Context, n node.Node, serv mdag.DAGService) (*DagR } } -func NewDataFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv mdag.DAGService) *DagReader { +func NewDataFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv mdag.DAGService) *pbDagReader { fctx, cancel := context.WithCancel(ctx) promises := mdag.GetDAG(fctx, serv, n) - return &DagReader{ + return &pbDagReader{ node: n, serv: serv, buf: NewRSNCFromBytes(pb.GetData()), @@ -117,7 +124,7 @@ func NewDataFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, se // precalcNextBuf follows the next link in line and loads it from the // DAGService, setting the next buffer to read from -func (dr *DagReader) precalcNextBuf(ctx context.Context) error { +func (dr *pbDagReader) precalcNextBuf(ctx context.Context) error { dr.buf.Close() // Just to make sure if dr.linkPosition >= len(dr.promises) { return io.EOF @@ -158,22 +165,22 @@ func (dr *DagReader) precalcNextBuf(ctx context.Context) error { dr.buf = NewRSNCFromBytes(nxt.RawData()) return nil default: - return errors.New("unrecognized node type in DagReader") + return errors.New("unrecognized node type in pbDagReader") } } // Size return the total length of the data from the DAG structured file. -func (dr *DagReader) Size() uint64 { +func (dr *pbDagReader) Size() uint64 { return dr.pbdata.GetFilesize() } // Read reads data from the DAG structured file -func (dr *DagReader) Read(b []byte) (int, error) { +func (dr *pbDagReader) Read(b []byte) (int, error) { return dr.CtxReadFull(dr.ctx, b) } // CtxReadFull reads data from the DAG structured file -func (dr *DagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { +func (dr *pbDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { // If no cached buffer, load one total := 0 for { @@ -201,7 +208,7 @@ func (dr *DagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { } } -func (dr *DagReader) WriteTo(w io.Writer) (int64, error) { +func (dr *pbDagReader) WriteTo(w io.Writer) (int64, error) { // If no cached buffer, load one total := int64(0) for { @@ -226,12 +233,12 @@ func (dr *DagReader) WriteTo(w io.Writer) (int64, error) { } } -func (dr *DagReader) Close() error { +func (dr *pbDagReader) Close() error { dr.cancel() return nil } -func (dr *DagReader) Offset() int64 { +func (dr *pbDagReader) Offset() int64 { return dr.offset } @@ -239,7 +246,7 @@ func (dr *DagReader) Offset() int64 { // interface matches standard unix seek // TODO: check if we can do relative seeks, to reduce the amount of dagreader // recreations that need to happen. -func (dr *DagReader) Seek(offset int64, whence int) (int64, error) { +func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) { switch whence { case os.SEEK_SET: if offset < 0 { diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index 5f1380c9e..27d7f3b09 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -236,7 +236,7 @@ func TestReaderSzie(t *testing.T) { } } -func readByte(t testing.TB, reader *DagReader) byte { +func readByte(t testing.TB, reader DagReader) byte { out := make([]byte, 1) c, err := reader.Read(out) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 4de446bce..943e309b8 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -43,7 +43,7 @@ type DagModifier struct { curWrOff uint64 wrBuf *bytes.Buffer - read *uio.DagReader + read uio.DagReader } func NewDagModifier(ctx context.Context, from node.Node, serv mdag.DAGService, spl chunk.SplitterGen) (*DagModifier, error) { From e72fadd7e80c75237e46a346d8a16cf20cceb16a Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 21 Nov 2016 17:18:22 +0100 Subject: [PATCH 1647/3817] Move proto-dag reader to separate file and change name License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@72df8b2f61e0e1797257e2013d083fab0d5e43f7 --- unixfs/archive/tar/writer.go | 2 +- unixfs/io/dagreader.go | 250 +-------------------------------- unixfs/io/pbdagreader.go | 262 +++++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+), 250 deletions(-) create mode 100644 unixfs/io/pbdagreader.go diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index f710d4063..17c43e717 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -64,7 +64,7 @@ func (w *Writer) writeFile(nd *mdag.ProtoNode, pb *upb.Data, fpath string) error return err } - dagr := uio.NewDataFileReader(w.ctx, nd, pb, w.Dag) + dagr := uio.NewPBFileReader(w.ctx, nd, pb, w.Dag) if _, err := dagr.WriteTo(w.TarW); err != nil { return err } diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index da1fd65c8..f893de802 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -1,12 +1,10 @@ package io import ( - "bytes" "context" "errors" "fmt" "io" - "os" mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" @@ -27,36 +25,6 @@ type DagReader interface { Offset() int64 } -// DagReader provides a way to easily read the data contained in a dag. -type pbDagReader struct { - serv mdag.DAGService - - // the node being read - node *mdag.ProtoNode - - // cached protobuf structure from node.Data - pbdata *ftpb.Data - - // the current data buffer to be read from - // will either be a bytes.Reader or a child DagReader - buf ReadSeekCloser - - // NodeGetters for each of 'nodes' child links - promises []mdag.NodeGetter - - // the index of the child link currently being read from - linkPosition int - - // current offset for the read head within the 'file' - offset int64 - - // Our context - ctx context.Context - - // context cancel for children - cancel func() -} - type ReadSeekCloser interface { io.Reader io.Seeker @@ -83,7 +51,7 @@ func NewDagReader(ctx context.Context, n node.Node, serv mdag.DAGService) (DagRe // Dont allow reading directories return nil, ErrIsDir case ftpb.Data_File, ftpb.Data_Raw: - return NewDataFileReader(ctx, n, pb, serv), nil + return NewPBFileReader(ctx, n, pb, serv), nil case ftpb.Data_Metadata: if len(n.Links()) == 0 { return nil, errors.New("incorrectly formatted metadata object") @@ -107,219 +75,3 @@ func NewDagReader(ctx context.Context, n node.Node, serv mdag.DAGService) (DagRe return nil, fmt.Errorf("unrecognized node type") } } - -func NewDataFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv mdag.DAGService) *pbDagReader { - fctx, cancel := context.WithCancel(ctx) - promises := mdag.GetDAG(fctx, serv, n) - return &pbDagReader{ - node: n, - serv: serv, - buf: NewRSNCFromBytes(pb.GetData()), - promises: promises, - ctx: fctx, - cancel: cancel, - pbdata: pb, - } -} - -// precalcNextBuf follows the next link in line and loads it from the -// DAGService, setting the next buffer to read from -func (dr *pbDagReader) precalcNextBuf(ctx context.Context) error { - dr.buf.Close() // Just to make sure - if dr.linkPosition >= len(dr.promises) { - return io.EOF - } - - nxt, err := dr.promises[dr.linkPosition].Get(ctx) - if err != nil { - return err - } - dr.linkPosition++ - - switch nxt := nxt.(type) { - case *mdag.ProtoNode: - pb := new(ftpb.Data) - err = proto.Unmarshal(nxt.Data(), pb) - if err != nil { - return fmt.Errorf("incorrectly formatted protobuf: %s", err) - } - - switch pb.GetType() { - case ftpb.Data_Directory: - // A directory should not exist within a file - return ft.ErrInvalidDirLocation - case ftpb.Data_File: - dr.buf = NewDataFileReader(dr.ctx, nxt, pb, dr.serv) - return nil - case ftpb.Data_Raw: - dr.buf = NewRSNCFromBytes(pb.GetData()) - return nil - case ftpb.Data_Metadata: - return errors.New("shouldnt have had metadata object inside file") - case ftpb.Data_Symlink: - return errors.New("shouldnt have had symlink inside file") - default: - return ft.ErrUnrecognizedType - } - case *mdag.RawNode: - dr.buf = NewRSNCFromBytes(nxt.RawData()) - return nil - default: - return errors.New("unrecognized node type in pbDagReader") - } -} - -// Size return the total length of the data from the DAG structured file. -func (dr *pbDagReader) Size() uint64 { - return dr.pbdata.GetFilesize() -} - -// Read reads data from the DAG structured file -func (dr *pbDagReader) Read(b []byte) (int, error) { - return dr.CtxReadFull(dr.ctx, b) -} - -// CtxReadFull reads data from the DAG structured file -func (dr *pbDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { - // If no cached buffer, load one - total := 0 - for { - // Attempt to fill bytes from cached buffer - n, err := dr.buf.Read(b[total:]) - total += n - dr.offset += int64(n) - if err != nil { - // EOF is expected - if err != io.EOF { - return total, err - } - } - - // If weve read enough bytes, return - if total == len(b) { - return total, nil - } - - // Otherwise, load up the next block - err = dr.precalcNextBuf(ctx) - if err != nil { - return total, err - } - } -} - -func (dr *pbDagReader) WriteTo(w io.Writer) (int64, error) { - // If no cached buffer, load one - total := int64(0) - for { - // Attempt to write bytes from cached buffer - n, err := dr.buf.WriteTo(w) - total += n - dr.offset += n - if err != nil { - if err != io.EOF { - return total, err - } - } - - // Otherwise, load up the next block - err = dr.precalcNextBuf(dr.ctx) - if err != nil { - if err == io.EOF { - return total, nil - } - return total, err - } - } -} - -func (dr *pbDagReader) Close() error { - dr.cancel() - return nil -} - -func (dr *pbDagReader) Offset() int64 { - return dr.offset -} - -// Seek implements io.Seeker, and will seek to a given offset in the file -// interface matches standard unix seek -// TODO: check if we can do relative seeks, to reduce the amount of dagreader -// recreations that need to happen. -func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) { - switch whence { - case os.SEEK_SET: - if offset < 0 { - return -1, errors.New("Invalid offset") - } - - // Grab cached protobuf object (solely to make code look cleaner) - pb := dr.pbdata - - // left represents the number of bytes remaining to seek to (from beginning) - left := offset - if int64(len(pb.Data)) >= offset { - // Close current buf to close potential child dagreader - dr.buf.Close() - dr.buf = NewRSNCFromBytes(pb.GetData()[offset:]) - - // start reading links from the beginning - dr.linkPosition = 0 - dr.offset = offset - return offset, nil - } else { - // skip past root block data - left -= int64(len(pb.Data)) - } - - // iterate through links and find where we need to be - for i := 0; i < len(pb.Blocksizes); i++ { - if pb.Blocksizes[i] > uint64(left) { - dr.linkPosition = i - break - } else { - left -= int64(pb.Blocksizes[i]) - } - } - - // start sub-block request - err := dr.precalcNextBuf(dr.ctx) - if err != nil { - return 0, err - } - - // set proper offset within child readseeker - n, err := dr.buf.Seek(left, os.SEEK_SET) - if err != nil { - return -1, err - } - - // sanity - left -= n - if left != 0 { - return -1, errors.New("failed to seek properly") - } - dr.offset = offset - return offset, nil - case os.SEEK_CUR: - // TODO: be smarter here - noffset := dr.offset + offset - return dr.Seek(noffset, os.SEEK_SET) - case os.SEEK_END: - noffset := int64(dr.pbdata.GetFilesize()) - offset - return dr.Seek(noffset, os.SEEK_SET) - default: - return 0, errors.New("invalid whence") - } -} - -// readSeekNopCloser wraps a bytes.Reader to implement ReadSeekCloser -type readSeekNopCloser struct { - *bytes.Reader -} - -func NewRSNCFromBytes(b []byte) ReadSeekCloser { - return &readSeekNopCloser{bytes.NewReader(b)} -} - -func (r *readSeekNopCloser) Close() error { return nil } diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go new file mode 100644 index 000000000..34f622bd6 --- /dev/null +++ b/unixfs/io/pbdagreader.go @@ -0,0 +1,262 @@ +package io + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "os" + + mdag "github.com/ipfs/go-ipfs/merkledag" + ft "github.com/ipfs/go-ipfs/unixfs" + ftpb "github.com/ipfs/go-ipfs/unixfs/pb" + + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" +) + +// DagReader provides a way to easily read the data contained in a dag. +type pbDagReader struct { + serv mdag.DAGService + + // the node being read + node *mdag.ProtoNode + + // cached protobuf structure from node.Data + pbdata *ftpb.Data + + // the current data buffer to be read from + // will either be a bytes.Reader or a child DagReader + buf ReadSeekCloser + + // NodeGetters for each of 'nodes' child links + promises []mdag.NodeGetter + + // the index of the child link currently being read from + linkPosition int + + // current offset for the read head within the 'file' + offset int64 + + // Our context + ctx context.Context + + // context cancel for children + cancel func() +} + +func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv mdag.DAGService) *pbDagReader { + fctx, cancel := context.WithCancel(ctx) + promises := mdag.GetDAG(fctx, serv, n) + return &pbDagReader{ + node: n, + serv: serv, + buf: NewRSNCFromBytes(pb.GetData()), + promises: promises, + ctx: fctx, + cancel: cancel, + pbdata: pb, + } +} + +// precalcNextBuf follows the next link in line and loads it from the +// DAGService, setting the next buffer to read from +func (dr *pbDagReader) precalcNextBuf(ctx context.Context) error { + dr.buf.Close() // Just to make sure + if dr.linkPosition >= len(dr.promises) { + return io.EOF + } + + nxt, err := dr.promises[dr.linkPosition].Get(ctx) + if err != nil { + return err + } + dr.linkPosition++ + + switch nxt := nxt.(type) { + case *mdag.ProtoNode: + pb := new(ftpb.Data) + err = proto.Unmarshal(nxt.Data(), pb) + if err != nil { + return fmt.Errorf("incorrectly formatted protobuf: %s", err) + } + + switch pb.GetType() { + case ftpb.Data_Directory: + // A directory should not exist within a file + return ft.ErrInvalidDirLocation + case ftpb.Data_File: + dr.buf = NewPBFileReader(dr.ctx, nxt, pb, dr.serv) + return nil + case ftpb.Data_Raw: + dr.buf = NewRSNCFromBytes(pb.GetData()) + return nil + case ftpb.Data_Metadata: + return errors.New("shouldnt have had metadata object inside file") + case ftpb.Data_Symlink: + return errors.New("shouldnt have had symlink inside file") + default: + return ft.ErrUnrecognizedType + } + case *mdag.RawNode: + dr.buf = NewRSNCFromBytes(nxt.RawData()) + return nil + default: + return errors.New("unrecognized node type in pbDagReader") + } +} + +// Size return the total length of the data from the DAG structured file. +func (dr *pbDagReader) Size() uint64 { + return dr.pbdata.GetFilesize() +} + +// Read reads data from the DAG structured file +func (dr *pbDagReader) Read(b []byte) (int, error) { + return dr.CtxReadFull(dr.ctx, b) +} + +// CtxReadFull reads data from the DAG structured file +func (dr *pbDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { + // If no cached buffer, load one + total := 0 + for { + // Attempt to fill bytes from cached buffer + n, err := dr.buf.Read(b[total:]) + total += n + dr.offset += int64(n) + if err != nil { + // EOF is expected + if err != io.EOF { + return total, err + } + } + + // If weve read enough bytes, return + if total == len(b) { + return total, nil + } + + // Otherwise, load up the next block + err = dr.precalcNextBuf(ctx) + if err != nil { + return total, err + } + } +} + +func (dr *pbDagReader) WriteTo(w io.Writer) (int64, error) { + // If no cached buffer, load one + total := int64(0) + for { + // Attempt to write bytes from cached buffer + n, err := dr.buf.WriteTo(w) + total += n + dr.offset += n + if err != nil { + if err != io.EOF { + return total, err + } + } + + // Otherwise, load up the next block + err = dr.precalcNextBuf(dr.ctx) + if err != nil { + if err == io.EOF { + return total, nil + } + return total, err + } + } +} + +func (dr *pbDagReader) Close() error { + dr.cancel() + return nil +} + +func (dr *pbDagReader) Offset() int64 { + return dr.offset +} + +// Seek implements io.Seeker, and will seek to a given offset in the file +// interface matches standard unix seek +// TODO: check if we can do relative seeks, to reduce the amount of dagreader +// recreations that need to happen. +func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) { + switch whence { + case os.SEEK_SET: + if offset < 0 { + return -1, errors.New("Invalid offset") + } + + // Grab cached protobuf object (solely to make code look cleaner) + pb := dr.pbdata + + // left represents the number of bytes remaining to seek to (from beginning) + left := offset + if int64(len(pb.Data)) >= offset { + // Close current buf to close potential child dagreader + dr.buf.Close() + dr.buf = NewRSNCFromBytes(pb.GetData()[offset:]) + + // start reading links from the beginning + dr.linkPosition = 0 + dr.offset = offset + return offset, nil + } else { + // skip past root block data + left -= int64(len(pb.Data)) + } + + // iterate through links and find where we need to be + for i := 0; i < len(pb.Blocksizes); i++ { + if pb.Blocksizes[i] > uint64(left) { + dr.linkPosition = i + break + } else { + left -= int64(pb.Blocksizes[i]) + } + } + + // start sub-block request + err := dr.precalcNextBuf(dr.ctx) + if err != nil { + return 0, err + } + + // set proper offset within child readseeker + n, err := dr.buf.Seek(left, os.SEEK_SET) + if err != nil { + return -1, err + } + + // sanity + left -= n + if left != 0 { + return -1, errors.New("failed to seek properly") + } + dr.offset = offset + return offset, nil + case os.SEEK_CUR: + // TODO: be smarter here + noffset := dr.offset + offset + return dr.Seek(noffset, os.SEEK_SET) + case os.SEEK_END: + noffset := int64(dr.pbdata.GetFilesize()) - offset + return dr.Seek(noffset, os.SEEK_SET) + default: + return 0, errors.New("invalid whence") + } +} + +// readSeekNopCloser wraps a bytes.Reader to implement ReadSeekCloser +type readSeekNopCloser struct { + *bytes.Reader +} + +func NewRSNCFromBytes(b []byte) ReadSeekCloser { + return &readSeekNopCloser{bytes.NewReader(b)} +} + +func (r *readSeekNopCloser) Close() error { return nil } From 87330a25dcfcd10a2e39ba54620d5ff9cadea9cb Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 21 Nov 2016 17:49:25 +0100 Subject: [PATCH 1648/3817] Create bufDagReader License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@d6e8b60e554a4e4e72dcd3eae0fd136fd028c6fc --- unixfs/io/bufdagreader.go | 41 +++++++++++++++++++++++++++++++++++++++ unixfs/io/dagreader.go | 4 +--- unixfs/io/pbdagreader.go | 2 ++ 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 unixfs/io/bufdagreader.go diff --git a/unixfs/io/bufdagreader.go b/unixfs/io/bufdagreader.go new file mode 100644 index 000000000..2f7358640 --- /dev/null +++ b/unixfs/io/bufdagreader.go @@ -0,0 +1,41 @@ +package io + +import ( + "bytes" + "context" + "io" +) + +type bufDagReader struct { + *bytes.Reader +} + +func NewBufDagReader(b []byte) *bufDagReader { + return &bufDagReader{bytes.NewReader(b)} +} + +var _ DagReader = (*bufDagReader)(nil) + +func (*bufDagReader) Close() error { + return nil +} + +func (rd *bufDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { + return rd.Read(b) +} + +func (rd *bufDagReader) Offset() int64 { + of, err := rd.Seek(0, io.SeekCurrent) + if err != nil { + panic("this should never happen " + err.Error()) + } + return of +} + +func (rd *bufDagReader) Size() uint64 { + s := rd.Reader.Size() + if s < 0 { + panic("size smaller than 0 (impossible!!)") + } + return uint64(s) +} diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index f893de802..8da86b924 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -37,9 +37,7 @@ type ReadSeekCloser interface { func NewDagReader(ctx context.Context, n node.Node, serv mdag.DAGService) (DagReader, error) { switch n := n.(type) { case *mdag.RawNode: - return &pbDagReader{ - buf: NewRSNCFromBytes(n.RawData()), - }, nil + return NewBufDagReader(n.RawData()), nil case *mdag.ProtoNode: pb := new(ftpb.Data) if err := proto.Unmarshal(n.Data(), pb); err != nil { diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 34f622bd6..b2724e104 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -45,6 +45,8 @@ type pbDagReader struct { cancel func() } +var _ DagReader = (*pbDagReader)(nil) + func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv mdag.DAGService) *pbDagReader { fctx, cancel := context.WithCancel(ctx) promises := mdag.GetDAG(fctx, serv, n) From 38ad0130453a2f53b681916898a83d7f524d523a Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 18 Nov 2016 00:24:00 +0100 Subject: [PATCH 1649/3817] Update go-libp2p across codebase License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@97a5e50f21ac159bb4ec36d114023831686de064 --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 3ac209038..96b76da62 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmUYzZRJcuUxLSnSzF1bSyw1jYbNAULkBrbS6rnr7F72uK/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + mocknet "gx/ipfs/QmZyBJGpRnbQ7oUstoGNZbhXC4HJuFUCgpp8pmsVTUwdS3/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { From 3bfb3d6f873087e933312cee1f35c45956d6c9cf Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 18 Nov 2016 00:24:00 +0100 Subject: [PATCH 1650/3817] Update go-libp2p across codebase License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-routing@83e9c2bdc8f92d6a639fd7264e71b63722c43fe2 --- routing/mock/dht.go | 4 ++-- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 7f9d864b5..352717da6 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,8 +3,8 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmUYzZRJcuUxLSnSzF1bSyw1jYbNAULkBrbS6rnr7F72uK/go-libp2p/p2p/net/mock" - dht "gx/ipfs/Qmap6Qnt8RRvQ1BawQw4HZKHaSJAZC5VybyapizCXGdfUK/go-libp2p-kad-dht" + mocknet "gx/ipfs/QmZyBJGpRnbQ7oUstoGNZbhXC4HJuFUCgpp8pmsVTUwdS3/go-libp2p/p2p/net/mock" + dht "gx/ipfs/QmaMRCpeKL34rdT7t3bEndrENbVdD6gcCZr3YdkDUk6jue/go-libp2p-kad-dht" ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 33b01a134..385558da1 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -13,7 +13,7 @@ import ( routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/Qmap6Qnt8RRvQ1BawQw4HZKHaSJAZC5VybyapizCXGdfUK/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmaMRCpeKL34rdT7t3bEndrENbVdD6gcCZr3YdkDUk6jue/go-libp2p-kad-dht/pb" "gx/ipfs/Qmb6UFbVu1grhv5o5KnouvtZ6cqdrjXj6zLejAHWunxgCt/go-libp2p-host" cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 690011d66..7dc615b24 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -4,7 +4,7 @@ import ( context "context" inet "gx/ipfs/QmU3pGGVT1riXp5dBJbNrGpxssVScfvk9236drRHZZbKJ1/go-libp2p-net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/Qmap6Qnt8RRvQ1BawQw4HZKHaSJAZC5VybyapizCXGdfUK/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmaMRCpeKL34rdT7t3bEndrENbVdD6gcCZr3YdkDUk6jue/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 922169924..af843fc0c 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -10,7 +10,7 @@ import ( kbucket "gx/ipfs/QmUKePKcUEXwdvJENZJ6z8mJjPaxLsDZ3V9CZjPPtyawPm/go-libp2p-kbucket" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/Qmap6Qnt8RRvQ1BawQw4HZKHaSJAZC5VybyapizCXGdfUK/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmaMRCpeKL34rdT7t3bEndrENbVdD6gcCZr3YdkDUk6jue/go-libp2p-kad-dht/pb" host "gx/ipfs/Qmb6UFbVu1grhv5o5KnouvtZ6cqdrjXj6zLejAHWunxgCt/go-libp2p-host" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 71e59d4be..0efb77467 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -10,7 +10,7 @@ import ( pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/Qmap6Qnt8RRvQ1BawQw4HZKHaSJAZC5VybyapizCXGdfUK/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmaMRCpeKL34rdT7t3bEndrENbVdD6gcCZr3YdkDUk6jue/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 83dfd9aa4..4dab1aac7 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -4,7 +4,7 @@ import ( "testing" key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - dhtpb "gx/ipfs/Qmap6Qnt8RRvQ1BawQw4HZKHaSJAZC5VybyapizCXGdfUK/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmaMRCpeKL34rdT7t3bEndrENbVdD6gcCZr3YdkDUk6jue/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) From 5f1a2bfba660037c959aaef8a6e03f4c8c8558ce Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 22 Nov 2016 19:10:35 +0100 Subject: [PATCH 1651/3817] Remove NewRSNCFromBytes and use NewDagReader for recursive reading License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@b845e420de6801d6ba35c5c4ea267d4a25384ee6 --- unixfs/io/pbdagreader.go | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index b2724e104..344ee3440 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -1,7 +1,6 @@ package io import ( - "bytes" "context" "errors" "fmt" @@ -53,7 +52,7 @@ func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv return &pbDagReader{ node: n, serv: serv, - buf: NewRSNCFromBytes(pb.GetData()), + buf: NewBufDagReader(pb.GetData()), promises: promises, ctx: fctx, cancel: cancel, @@ -91,7 +90,7 @@ func (dr *pbDagReader) precalcNextBuf(ctx context.Context) error { dr.buf = NewPBFileReader(dr.ctx, nxt, pb, dr.serv) return nil case ftpb.Data_Raw: - dr.buf = NewRSNCFromBytes(pb.GetData()) + dr.buf = NewBufDagReader(pb.GetData()) return nil case ftpb.Data_Metadata: return errors.New("shouldnt have had metadata object inside file") @@ -100,11 +99,10 @@ func (dr *pbDagReader) precalcNextBuf(ctx context.Context) error { default: return ft.ErrUnrecognizedType } - case *mdag.RawNode: - dr.buf = NewRSNCFromBytes(nxt.RawData()) - return nil default: - return errors.New("unrecognized node type in pbDagReader") + var err error + dr.buf, err = NewDagReader(ctx, nxt, dr.serv) + return err } } @@ -200,7 +198,7 @@ func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) { if int64(len(pb.Data)) >= offset { // Close current buf to close potential child dagreader dr.buf.Close() - dr.buf = NewRSNCFromBytes(pb.GetData()[offset:]) + dr.buf = NewBufDagReader(pb.GetData()[offset:]) // start reading links from the beginning dr.linkPosition = 0 @@ -252,13 +250,3 @@ func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) { } } -// readSeekNopCloser wraps a bytes.Reader to implement ReadSeekCloser -type readSeekNopCloser struct { - *bytes.Reader -} - -func NewRSNCFromBytes(b []byte) ReadSeekCloser { - return &readSeekNopCloser{bytes.NewReader(b)} -} - -func (r *readSeekNopCloser) Close() error { return nil } From 70ace495fbbc405713b93f5a78ab17a00eb552a1 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 23 Nov 2016 18:00:20 -0500 Subject: [PATCH 1652/3817] "block rm": make channel large enough to avoid blocking Make the channel for the output of RmBlocks large enough to hold any result to avoid blocking while holding the GCLock. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-blockstore@7556923648ff10224935c60001d5ac0ec45f0254 --- blockstore/util/remove.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 1c8f0c31e..b4375855b 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -27,7 +27,10 @@ type RmBlocksOpts struct { Force bool } -func RmBlocks(blocks bs.GCBlockstore, pins pin.Pinner, out chan<- interface{}, cids []*cid.Cid, opts RmBlocksOpts) error { +func RmBlocks(blocks bs.GCBlockstore, pins pin.Pinner, cids []*cid.Cid, opts RmBlocksOpts) (<-chan interface{}, error) { + // make the channel large enough to hold any result to avoid + // blocking while holding the GCLock + out := make(chan interface{}, len(cids)) go func() { defer close(out) @@ -47,7 +50,7 @@ func RmBlocks(blocks bs.GCBlockstore, pins pin.Pinner, out chan<- interface{}, c } } }() - return nil + return out, nil } func FilterPinned(pins pin.Pinner, out chan<- interface{}, cids []*cid.Cid) []*cid.Cid { From 117256bca45ca444432fec1e2e341a6ead9a59f6 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 24 Nov 2016 20:05:01 +0100 Subject: [PATCH 1653/3817] Remove trailing new-line from pbdagreader.go License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@91871b4117ad549232001a514eed3184cef21b57 --- unixfs/io/pbdagreader.go | 1 - 1 file changed, 1 deletion(-) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 344ee3440..a5a53ffa2 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -249,4 +249,3 @@ func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) { return 0, errors.New("invalid whence") } } - From af92c4baeb03e298c95b267253ecafd762aeb194 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 21:42:32 -0800 Subject: [PATCH 1654/3817] completely remove go-key dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@f22a7ba55d81868af412969565f2e89a374350f3 --- namesys/resolve_test.go | 10 ++++------ namesys/routing.go | 15 +++++---------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 145396d11..78c4444f0 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -1,16 +1,15 @@ package namesys import ( + "context" "errors" "testing" "time" - context "context" path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" + ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" @@ -36,13 +35,12 @@ func TestRoutingResolve(t *testing.T) { t.Fatal(err) } - pubkb, err := pubk.Bytes() + pid, err := peer.IDFromPublicKey(pubk) if err != nil { t.Fatal(err) } - pkhash := u.Hash(pubkb) - res, err := resolver.Resolve(context.Background(), key.Key(pkhash).B58String()) + res, err := resolver.Resolve(context.Background(), pid.Pretty()) if err != nil { t.Fatal(err) } diff --git a/namesys/routing.go b/namesys/routing.go index c78532013..8c2dc8488 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -1,21 +1,19 @@ package namesys import ( + "context" "fmt" "strings" "time" - "context" - lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" + lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" + mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" @@ -179,9 +177,6 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa } } - hsh, _ := pubkey.Hash() - log.Debugf("pk hash = %s", key.Key(hsh)) - // check sig with pk if ok, err := pubkey.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok { return "", fmt.Errorf("Invalid value. Not signed by PrivateKey corresponding to %v", pubkey) From b0260ab74e225671ea50d8b4515a55db7f043149 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 21:42:32 -0800 Subject: [PATCH 1655/3817] completely remove go-key dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@353586cff7d87960fc6e88deaa6b3f9fadf225dd --- routing/supernode/server.go | 32 ++++++++++++++++---------------- routing/supernode/server_test.go | 3 +-- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 0efb77467..e5aaea434 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -1,14 +1,14 @@ package supernode import ( + "context" "errors" "fmt" proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" + dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - context "context" pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" dhtpb "gx/ipfs/QmaMRCpeKL34rdT7t3bEndrENbVdD6gcCZr3YdkDUk6jue/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" @@ -55,7 +55,7 @@ func (s *Server) handleMessage( switch req.GetType() { case dhtpb.Message_GET_VALUE: - rawRecord, err := getRoutingRecord(s.routingBackend, key.Key(req.GetKey())) + rawRecord, err := getRoutingRecord(s.routingBackend, req.GetKey()) if err != nil { return "", nil } @@ -69,7 +69,7 @@ func (s *Server) handleMessage( // log.Event(ctx, "validationFailed", req, p) // return "", nil // } - putRoutingRecord(s.routingBackend, key.Key(req.GetKey()), req.GetRecord()) + putRoutingRecord(s.routingBackend, req.GetKey(), req.GetRecord()) return p, req case dhtpb.Message_FIND_NODE: @@ -89,7 +89,7 @@ func (s *Server) handleMessage( if providerID == p { store := []*dhtpb.Message_Peer{provider} storeProvidersToPeerstore(s.peerstore, p, store) - if err := putRoutingProviders(s.routingBackend, key.Key(req.GetKey()), store); err != nil { + if err := putRoutingProviders(s.routingBackend, req.GetKey(), store); err != nil { return "", nil } } else { @@ -99,7 +99,7 @@ func (s *Server) handleMessage( return "", nil case dhtpb.Message_GET_PROVIDERS: - providers, err := getRoutingProviders(s.routingBackend, key.Key(req.GetKey())) + providers, err := getRoutingProviders(s.routingBackend, req.GetKey()) if err != nil { return "", nil } @@ -116,8 +116,8 @@ func (s *Server) handleMessage( var _ proxy.RequestHandler = &Server{} var _ proxy.Proxy = &Server{} -func getRoutingRecord(ds datastore.Datastore, k key.Key) (*pb.Record, error) { - dskey := k.DsKey() +func getRoutingRecord(ds datastore.Datastore, k string) (*pb.Record, error) { + dskey := dshelp.NewKeyFromBinary(k) val, err := ds.Get(dskey) if err != nil { return nil, err @@ -133,12 +133,12 @@ func getRoutingRecord(ds datastore.Datastore, k key.Key) (*pb.Record, error) { return &record, nil } -func putRoutingRecord(ds datastore.Datastore, k key.Key, value *pb.Record) error { +func putRoutingRecord(ds datastore.Datastore, k string, value *pb.Record) error { data, err := proto.Marshal(value) if err != nil { return err } - dskey := k.DsKey() + dskey := dshelp.NewKeyFromBinary(k) // TODO namespace if err := ds.Put(dskey, data); err != nil { return err @@ -146,8 +146,8 @@ func putRoutingRecord(ds datastore.Datastore, k key.Key, value *pb.Record) error return nil } -func putRoutingProviders(ds datastore.Datastore, k key.Key, newRecords []*dhtpb.Message_Peer) error { - log.Event(context.Background(), "putRoutingProviders", &k) +func putRoutingProviders(ds datastore.Datastore, k string, newRecords []*dhtpb.Message_Peer) error { + log.Event(context.Background(), "putRoutingProviders") oldRecords, err := getRoutingProviders(ds, k) if err != nil { return err @@ -185,8 +185,8 @@ func storeProvidersToPeerstore(ps pstore.Peerstore, p peer.ID, providers []*dhtp } } -func getRoutingProviders(ds datastore.Datastore, k key.Key) ([]*dhtpb.Message_Peer, error) { - e := log.EventBegin(context.Background(), "getProviders", &k) +func getRoutingProviders(ds datastore.Datastore, k string) ([]*dhtpb.Message_Peer, error) { + e := log.EventBegin(context.Background(), "getProviders") defer e.Done() var providers []*dhtpb.Message_Peer if v, err := ds.Get(providerKey(k)); err == nil { @@ -201,8 +201,8 @@ func getRoutingProviders(ds datastore.Datastore, k key.Key) ([]*dhtpb.Message_Pe return providers, nil } -func providerKey(k key.Key) datastore.Key { - return datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) +func providerKey(k string) datastore.Key { + return datastore.KeyWithNamespaces([]string{"routing", "providers", k}) } func verify(ps pstore.Peerstore, r *pb.Record) error { diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 4dab1aac7..180acbf3d 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,14 +3,13 @@ package supernode import ( "testing" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" dhtpb "gx/ipfs/QmaMRCpeKL34rdT7t3bEndrENbVdD6gcCZr3YdkDUk6jue/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { routingBackend := datastore.NewMapDatastore() - k := key.Key("foo") + k := "foo" put := []*dhtpb.Message_Peer{ convPeer("bob", "127.0.0.1/tcp/4001"), convPeer("alice", "10.0.0.10/tcp/4001"), From 3e8f16ee30825cbcd84c9b192e192eae1c62fc0f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 21:42:32 -0800 Subject: [PATCH 1656/3817] completely remove go-key dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@7737cb02c3d8d3065b7e78815e97e3b393401470 --- ipld/merkledag/merkledag_test.go | 9 ++++----- ipld/merkledag/node.go | 7 +------ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index c75b33692..0e3637c71 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -22,7 +22,6 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) @@ -56,8 +55,8 @@ func TestNode(t *testing.T) { } h := n.Multihash() - k := n.Key() - if k != key.Key(h) { + k := n.Cid().Hash() + if k.String() != h.String() { t.Error("Key is not equivalent to multihash") } else { fmt.Println("key: ", k) @@ -84,7 +83,7 @@ func SubtestNodeStat(t *testing.T, n *ProtoNode) { return } - k := n.Key() + k := n.Cid() expected := node.NodeStat{ NumLinks: len(n.Links()), @@ -92,7 +91,7 @@ func SubtestNodeStat(t *testing.T, n *ProtoNode) { LinksSize: len(enc) - len(n.Data()), // includes framing. DataSize: len(n.Data()), CumulativeSize: int(cumSize), - Hash: k.B58String(), + Hash: k.String(), } actual, err := n.Stat() diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 332b083fa..df68c9c86 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -6,7 +6,6 @@ import ( node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) @@ -204,7 +203,7 @@ func (n *ProtoNode) Stat() (*node.NodeStat, error) { } return &node.NodeStat{ - Hash: n.Key().B58String(), + Hash: n.Cid().String(), NumLinks: len(n.links), BlockSize: len(enc), LinksSize: len(enc) - len(n.data), // includes framing. @@ -213,10 +212,6 @@ func (n *ProtoNode) Stat() (*node.NodeStat, error) { }, nil } -func (n *ProtoNode) Key() key.Key { - return key.Key(n.Multihash()) -} - func (n *ProtoNode) Loggable() map[string]interface{} { return map[string]interface{}{ "node": n.String(), From f22026c4e7fc926075d267b2e20d0e92e6991f8c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 21:42:32 -0800 Subject: [PATCH 1657/3817] completely remove go-key dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@6b9e0880d4e8871f9dd4d073f18f8c43c1608ef8 --- pinning/pinner/indirect.go | 39 -------------------------------------- pinning/pinner/set.go | 9 --------- 2 files changed, 48 deletions(-) delete mode 100644 pinning/pinner/indirect.go diff --git a/pinning/pinner/indirect.go b/pinning/pinner/indirect.go deleted file mode 100644 index b30a7c22d..000000000 --- a/pinning/pinner/indirect.go +++ /dev/null @@ -1,39 +0,0 @@ -package pin - -import ( - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" -) - -type indirectPin struct { - refCounts map[key.Key]uint64 -} - -func newIndirectPin() *indirectPin { - return &indirectPin{ - refCounts: make(map[key.Key]uint64), - } -} - -func (i *indirectPin) Increment(k key.Key) { - i.refCounts[k]++ -} - -func (i *indirectPin) Decrement(k key.Key) { - if i.refCounts[k] == 0 { - log.Warningf("pinning: bad call: asked to unpin nonexistent indirect key: %v", k) - return - } - i.refCounts[k]-- - if i.refCounts[k] == 0 { - delete(i.refCounts, k) - } -} - -func (i *indirectPin) HasKey(k key.Key) bool { - _, found := i.refCounts[k] - return found -} - -func (i *indirectPin) GetRefs() map[key.Key]uint64 { - return i.refCounts -} diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 02a279bf9..f84a57a27 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -14,7 +14,6 @@ import ( "github.com/ipfs/go-ipfs/pin/internal/pb" node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" - "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" ) @@ -308,11 +307,3 @@ func storeSet(ctx context.Context, dag merkledag.DAGService, cids []*cid.Cid, in internalKeys(c) return n, nil } - -func copyRefcounts(orig map[key.Key]uint64) map[key.Key]uint64 { - r := make(map[key.Key]uint64, len(orig)) - for k, v := range orig { - r[k] = v - } - return r -} From fc08a836401cd18020c428e58f05693584a4fd5d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 21:42:32 -0800 Subject: [PATCH 1658/3817] completely remove go-key dep License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@2c94649950b491ca4dea4d3de2c367793bd6df84 --- path/resolver_test.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/path/resolver_test.go b/path/resolver_test.go index bf13fac0a..b05c0393c 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -10,25 +10,23 @@ import ( path "github.com/ipfs/go-ipfs/path" node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" - key "gx/ipfs/QmYEoKZXHoAToWfhGF3vryhMn3WWhE1o2MasQ8uzY5iDi9/go-key" util "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) -func randNode() (*merkledag.ProtoNode, key.Key) { +func randNode() *merkledag.ProtoNode { node := new(merkledag.ProtoNode) node.SetData(make([]byte, 32)) util.NewTimeSeededRand().Read(node.Data()) - k := node.Key() - return node, k + return node } func TestRecurivePathResolution(t *testing.T) { ctx := context.Background() dagService := dagmock.Mock() - a, _ := randNode() - b, _ := randNode() - c, cKey := randNode() + a := randNode() + b := randNode() + c := randNode() err := b.AddNodeLink("grandchild", c) if err != nil { @@ -47,7 +45,7 @@ func TestRecurivePathResolution(t *testing.T) { } } - aKey := a.Key() + aKey := a.Cid() segments := []string{aKey.String(), "child", "grandchild"} p, err := path.FromSegments("/ipfs/", segments...) @@ -61,6 +59,7 @@ func TestRecurivePathResolution(t *testing.T) { t.Fatal(err) } + cKey := c.Cid() key := node.Cid() if key.String() != cKey.String() { t.Fatal(fmt.Errorf( From 9a756692d97f4ef297c87b094953a3ff94b4d22c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 22:29:38 -0800 Subject: [PATCH 1659/3817] bubble up go-datastore deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@f89d2c11bec95194f6e093e32ab9835b10b263c3 --- namesys/namesys.go | 4 ++-- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 6 +++--- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 4 ++-- namesys/routing.go | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index eb4408294..d2f4a3bf7 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,8 +6,8 @@ import ( context "context" path "github.com/ipfs/go-ipfs/path" - routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 6883589b6..345f21e33 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -14,10 +14,10 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ft "github.com/ipfs/go-ipfs/unixfs" - routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" dhtpb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 3e108e6b4..9abe8688e 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,14 +11,14 @@ import ( path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" - pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" recpb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" + pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 96b76da62..ff456593d 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - mocknet "gx/ipfs/QmZyBJGpRnbQ7oUstoGNZbhXC4HJuFUCgpp8pmsVTUwdS3/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmbzCT1CwxVZ2ednptC9RavuJe7Bv8DDi2Ne89qUrA37XM/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 78c4444f0..f7d41ead4 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -10,8 +10,8 @@ import ( mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/namesys/routing.go b/namesys/routing.go index 8c2dc8488..859da7d60 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -10,12 +10,12 @@ import ( path "github.com/ipfs/go-ipfs/path" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) From 937a097813cef8beab10380b5a73d0768e5ad2e9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 22:29:38 -0800 Subject: [PATCH 1660/3817] bubble up go-datastore deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@7c7f24a9a3af360204188faa817a867fb36cda1f --- routing/mock/centralized_client.go | 8 ++++---- routing/mock/centralized_server.go | 8 ++++---- routing/mock/centralized_test.go | 4 ++-- routing/mock/dht.go | 8 ++++---- routing/mock/interface.go | 8 ++++---- routing/none/none_client.go | 8 ++++---- routing/offline/offline.go | 8 ++++---- routing/supernode/client.go | 10 +++++----- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 10 +++++----- routing/supernode/server.go | 6 +++--- routing/supernode/server_test.go | 4 ++-- 12 files changed, 43 insertions(+), 43 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 4b78d535c..1a413ab9c 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,15 +8,15 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" "github.com/ipfs/go-ipfs/thirdparty/testutil" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ma "gx/ipfs/QmUAQaWbKxGCUTuoQVvvicbQNZ9APF5pDGWyAZSe93AtKH/go-multiaddr" - routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" - pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" dhtpb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" + pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 6ba6bb6ca..041a8ad29 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -8,10 +8,10 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" - pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 013a95884..13c3708d6 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,9 +8,9 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 352717da6..fa2b666d2 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmZyBJGpRnbQ7oUstoGNZbhXC4HJuFUCgpp8pmsVTUwdS3/go-libp2p/p2p/net/mock" - dht "gx/ipfs/QmaMRCpeKL34rdT7t3bEndrENbVdD6gcCZr3YdkDUk6jue/go-libp2p-kad-dht" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + dht "gx/ipfs/QmNQPjpcXrwwwgDErKzKUm2xxhXCB3cuFgTHsrcCJ5uGbu/go-libp2p-kad-dht" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + mocknet "gx/ipfs/QmbzCT1CwxVZ2ednptC9RavuJe7Bv8DDi2Ne89qUrA37XM/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 63e893c4e..28217d600 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,10 +10,10 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" - pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 62f46dadc..cd6ceec03 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,11 +6,11 @@ import ( repo "github.com/ipfs/go-ipfs/repo" + p2phost "gx/ipfs/QmPTGbC34bPKaUm9wTxBo7zSCac7pDuG42ZmnXC718CKZZ/go-libp2p-host" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" - pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" - p2phost "gx/ipfs/Qmb6UFbVu1grhv5o5KnouvtZ6cqdrjXj6zLejAHWunxgCt/go-libp2p-host" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index dc86585c1..1e3297bb3 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,14 +7,14 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" - pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" + pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 385558da1..fe642d262 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,15 +8,15 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" + dhtpb "gx/ipfs/QmNQPjpcXrwwwgDErKzKUm2xxhXCB3cuFgTHsrcCJ5uGbu/go-libp2p-kad-dht/pb" + "gx/ipfs/QmPTGbC34bPKaUm9wTxBo7zSCac7pDuG42ZmnXC718CKZZ/go-libp2p-host" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" - routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing" - pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/QmaMRCpeKL34rdT7t3bEndrENbVdD6gcCZr3YdkDUk6jue/go-libp2p-kad-dht/pb" - "gx/ipfs/Qmb6UFbVu1grhv5o5KnouvtZ6cqdrjXj6zLejAHWunxgCt/go-libp2p-host" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" + pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 7dc615b24..da73fee7f 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,9 +2,9 @@ package proxy import ( context "context" - inet "gx/ipfs/QmU3pGGVT1riXp5dBJbNrGpxssVScfvk9236drRHZZbKJ1/go-libp2p-net" + dhtpb "gx/ipfs/QmNQPjpcXrwwwgDErKzKUm2xxhXCB3cuFgTHsrcCJ5uGbu/go-libp2p-kad-dht/pb" + inet "gx/ipfs/QmQx1dHDDYENugYgqA22BaBrRfuv1coSsuPiM7rYh1wwGH/go-libp2p-net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/QmaMRCpeKL34rdT7t3bEndrENbVdD6gcCZr3YdkDUk6jue/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index af843fc0c..fa619ff32 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,14 +4,14 @@ import ( "context" "errors" + dhtpb "gx/ipfs/QmNQPjpcXrwwwgDErKzKUm2xxhXCB3cuFgTHsrcCJ5uGbu/go-libp2p-kad-dht/pb" + host "gx/ipfs/QmPTGbC34bPKaUm9wTxBo7zSCac7pDuG42ZmnXC718CKZZ/go-libp2p-host" + inet "gx/ipfs/QmQx1dHDDYENugYgqA22BaBrRfuv1coSsuPiM7rYh1wwGH/go-libp2p-net" + kbucket "gx/ipfs/QmRVHVr38ChANF2PUMNKQs7Q4uVWCLVabrfcTG9taNbcVy/go-libp2p-kbucket" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" - inet "gx/ipfs/QmU3pGGVT1riXp5dBJbNrGpxssVScfvk9236drRHZZbKJ1/go-libp2p-net" - kbucket "gx/ipfs/QmUKePKcUEXwdvJENZJ6z8mJjPaxLsDZ3V9CZjPPtyawPm/go-libp2p-kbucket" - pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/QmaMRCpeKL34rdT7t3bEndrENbVdD6gcCZr3YdkDUk6jue/go-libp2p-kad-dht/pb" - host "gx/ipfs/Qmb6UFbVu1grhv5o5KnouvtZ6cqdrjXj6zLejAHWunxgCt/go-libp2p-host" + pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index e5aaea434..6c96cbc2e 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,12 +8,12 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore" + dhtpb "gx/ipfs/QmNQPjpcXrwwwgDErKzKUm2xxhXCB3cuFgTHsrcCJ5uGbu/go-libp2p-kad-dht/pb" + datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/QmaMRCpeKL34rdT7t3bEndrENbVdD6gcCZr3YdkDUk6jue/go-libp2p-kad-dht/pb" - datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" + pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 180acbf3d..3f65808c9 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmaMRCpeKL34rdT7t3bEndrENbVdD6gcCZr3YdkDUk6jue/go-libp2p-kad-dht/pb" - datastore "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" + dhtpb "gx/ipfs/QmNQPjpcXrwwwgDErKzKUm2xxhXCB3cuFgTHsrcCJ5uGbu/go-libp2p-kad-dht/pb" + datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 071255c104615fbab260bd870aec34665de075cc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 22:29:38 -0800 Subject: [PATCH 1661/3817] bubble up go-datastore deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-ds-help@764e5c7cd3b92cd38d9d6af8073de4ca510f02ca --- datastore/dshelp/key.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 5e89209f5..256cb1592 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -1,9 +1,9 @@ package dshelp import ( - base32 "gx/ipfs/Qmb1DA2A9LS2wR4FFweB4uEDomFsdmnw1VLawLE1yQzudj/base32" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + base32 "gx/ipfs/QmZvZSVtvxak4dcTkhsQhqd1SQ6rg5UzaSTu62WfWKjj93/base32" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) // TODO: put this code into the go-datastore itself From a180175939537844f523240771c59bf0500c35ee Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 22:29:38 -0800 Subject: [PATCH 1662/3817] bubble up go-datastore deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@119ca42b2a8b33b0c8c40fac6291462fbcfd7136 --- ipld/merkledag/coding.go | 4 ++-- ipld/merkledag/merkledag.go | 10 +++++----- ipld/merkledag/merkledag_test.go | 4 ++-- ipld/merkledag/node.go | 4 ++-- ipld/merkledag/node_test.go | 2 +- ipld/merkledag/raw.go | 4 ++-- ipld/merkledag/test/utils.go | 4 ++-- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/traverse/traverse_test.go | 2 +- ipld/merkledag/utils/diff.go | 2 +- ipld/merkledag/utils/utils.go | 6 +++--- ipld/merkledag/utils/utils_test.go | 2 +- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 2c6751a6e..3b43e4c95 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -6,9 +6,9 @@ import ( pb "github.com/ipfs/go-ipfs/merkledag/pb" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 423da0cc8..10ff5fb58 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -11,10 +11,10 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" - ipldcbor "gx/ipfs/QmVVfh9urmDSL1upPtAKKMxFUwW1R6hYr95uCuJUP8RhUu/go-ipld-cbor" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + ipldcbor "gx/ipfs/QmbuuwTd9x4NReZ7sxtiKk7wFcfDUo54MfWBdtF5MRCPGR/go-ipld-cbor" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) var log = logging.Logger("merkledag") @@ -93,7 +93,7 @@ func decodeBlock(b blocks.Block) (node.Node, error) { c := b.Cid() switch c.Type() { - case cid.Protobuf: + case cid.DagProtobuf: decnd, err := DecodeProtobuf(b.RawData()) if err != nil { if strings.Contains(err.Error(), "Unmarshal failed") { @@ -106,7 +106,7 @@ func decodeBlock(b blocks.Block) (node.Node, error) { return decnd, nil case cid.Raw: return NewRawNode(b.RawData()), nil - case cid.CBOR: + case cid.DagCBOR: return ipldcbor.Decode(b.RawData()) default: return nil, fmt.Errorf("unrecognized object type: %s", c.Type()) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 0e3637c71..13d138105 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -21,9 +21,9 @@ import ( dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) func TestNode(t *testing.T) { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index df68c9c86..16d6941a2 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index e57cf4059..beec7ba65 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -7,7 +7,7 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" ) func TestRemoveLink(t *testing.T) { diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 7479d7e11..17aaa21c1 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -3,9 +3,9 @@ package merkledag import ( "github.com/ipfs/go-ipfs/blocks" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) type RawNode struct { diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index 1475a6f84..b866ec6e4 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -5,8 +5,8 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" ) func Mock() dag.DAGService { diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index 35ec74d8f..96562b7be 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -5,7 +5,7 @@ import ( "context" "errors" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" ) // Order is an identifier for traversal algorithm orders diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 7bd69fbe5..43f06ce7b 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -8,7 +8,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" ) func TestDFSPreNoSkip(t *testing.T) { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index e7511ae6d..e854ccc0b 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -7,7 +7,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" context "context" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) const ( diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 9d21bc81c..7d52e480c 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -10,9 +10,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + syncds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" ) type Editor struct { diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index d45a89917..9614990a5 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" context "context" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) func TestAddLink(t *testing.T) { From b18a7ee46f7039005eb38ef5630f3cdca0f0e0a8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 22:29:38 -0800 Subject: [PATCH 1663/3817] bubble up go-datastore deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@0434e61ca23776c1d73d868b4ee291de8c553029 --- blockstore/arc_cache.go | 4 ++-- blockstore/arc_cache_test.go | 6 +++--- blockstore/blockstore.go | 8 ++++---- blockstore/blockstore_test.go | 8 ++++---- blockstore/bloom_cache.go | 2 +- blockstore/bloom_cache_test.go | 6 +++--- blockstore/util/remove.go | 4 ++-- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 145dbe011..d4b85136f 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -5,10 +5,10 @@ import ( "github.com/ipfs/go-ipfs/blocks" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) type arccache struct { diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 7a977680a..7e59a49df 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -6,9 +6,9 @@ import ( "github.com/ipfs/go-ipfs/blocks" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + syncds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) var exampleBlock = blocks.NewBlock([]byte("foo")) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 953737f9c..40763276b 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -11,11 +11,11 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dsns "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/namespace" + dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dsns "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/namespace" - dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 23e754ed7..0ea102b2b 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -9,11 +9,11 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" + ds_sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" - ds_sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) func TestGetWhenKeyNotPresent(t *testing.T) { diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index a0ef90333..7d234b3fc 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -8,7 +8,7 @@ import ( "github.com/ipfs/go-ipfs/blocks" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" bloom "gx/ipfs/QmeiMCBkYHxkDkDfnDadzz4YxY5ruL5Pj499essE4vRsGM/bbloom" ) diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 72223cd44..7941a647c 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -9,9 +9,9 @@ import ( "github.com/ipfs/go-ipfs/blocks" context "context" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dsq "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/query" - syncds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" + syncds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" ) func testBloomCached(bs Blockstore, ctx context.Context) (*bloomcache, error) { diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 1c8f0c31e..3b87a1de5 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -6,8 +6,8 @@ import ( bs "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/pin" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) // RemovedBlock is used to respresent the result of removing a block. From e747adfea4ba0725bcb62ed46c4db2d4571034ba Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 22:29:38 -0800 Subject: [PATCH 1664/3817] bubble up go-datastore deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@b4c08b0e5b4bfb8cbc69b6173a99e3ed11fc8b4f --- unixfs/io/dagreader.go | 2 +- unixfs/io/dirbuilder.go | 2 +- unixfs/io/resolve.go | 2 +- unixfs/mod/dagmodifier.go | 4 ++-- unixfs/mod/dagmodifier_test.go | 4 ++-- unixfs/test/utils.go | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 8da86b924..aded131dc 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 1fe8fbd99..569ee371f 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -5,7 +5,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) type directoryBuilder struct { diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index 80d51773c..9603b08dc 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -6,7 +6,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" ) func ResolveUnixfsOnce(ctx context.Context, ds dag.DAGService, nd node.Node, name string) (*node.Link, error) { diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 943e309b8..356a5b1e9 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -14,10 +14,10 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) var ErrSeekFail = errors.New("failed to seek properly") diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 9c7ac89d7..fdeabbbb2 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -17,9 +17,9 @@ import ( testu "github.com/ipfs/go-ipfs/unixfs/test" context "context" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" ) func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.Blockstore) { diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index d4eb01020..7b9775cff 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -14,7 +14,7 @@ import ( mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) From 0c1d2f6b444adb1f14560a935f48c409e4b20f92 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 22:29:38 -0800 Subject: [PATCH 1665/3817] bubble up go-datastore deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@d0390c91ccc4a505486a34849d57e0cfc1f645ef --- blockservice/blockservice.go | 2 +- blockservice/blockservice_test.go | 4 ++-- blockservice/test/blocks_test.go | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index ae5bec193..d05a01355 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -13,7 +13,7 @@ import ( exchange "github.com/ipfs/go-ipfs/exchange" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) var log = logging.Logger("blockservice") diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index 0415f8213..a97c315ba 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -8,8 +8,8 @@ import ( butil "github.com/ipfs/go-ipfs/blocks/blocksutil" offline "github.com/ipfs/go-ipfs/exchange/offline" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" ) func TestWriteThroughWorks(t *testing.T) { diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 813a6ff96..ed700d46f 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -12,10 +12,10 @@ import ( . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) func newObject(data []byte) blocks.Block { From bc3f5df70df5e51a2f7c22b6d7175340f4bc08c1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 22:29:38 -0800 Subject: [PATCH 1666/3817] bubble up go-datastore deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@235ff52e67024c1c84d83dac7efba25cd71f68ff --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin.go | 6 +++--- pinning/pinner/pin_test.go | 6 +++--- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index c09480ea8..cc85d9979 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,7 +8,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index f83aa40d8..baf0d5958 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -11,10 +11,10 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index a0e7d88ab..90bbc5213 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -10,10 +10,10 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" context "context" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) func randNode() (*mdag.ProtoNode, *cid.Cid) { diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index f84a57a27..89791b1b6 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -13,9 +13,9 @@ import ( "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 7d69f4ce4..c409fae4b 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -9,7 +9,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) func ignoreCids(_ *cid.Cid) {} From 4210265e4dcb474b826ada83b2a0145c75305e59 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 22:29:38 -0800 Subject: [PATCH 1667/3817] bubble up go-datastore deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@c981bb52ad5c57b59c9be0dfed6f7b1421e01030 --- path/path.go | 2 +- path/resolver.go | 4 ++-- path/resolver_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/path/path.go b/path/path.go index e9f300df9..550368ad8 100644 --- a/path/path.go +++ b/path/path.go @@ -5,7 +5,7 @@ import ( "path" "strings" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) // ErrBadPath is returned when a given path is incorrectly formatted diff --git a/path/resolver.go b/path/resolver.go index 2c57eff42..fb472953e 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -9,9 +9,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) var log = logging.Logger("path") diff --git a/path/resolver_test.go b/path/resolver_test.go index b05c0393c..93f3ff7c4 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -9,7 +9,7 @@ import ( dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" util "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) From da6ace83a4549449f1c3f08748d0906d0bfeee37 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 22:29:38 -0800 Subject: [PATCH 1668/3817] bubble up go-datastore deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/interface-go-ipfs-core@3f68a10d21f3553c4639f477388b3b36492912a2 --- coreiface/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index c1c83fa26..cf6471947 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -5,8 +5,8 @@ import ( "errors" "io" - ipld "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + ipld "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) // type CoreAPI interface { From b68906032f58cedbe0401b0f8bce9e0d7d0607a5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 22:29:38 -0800 Subject: [PATCH 1669/3817] bubble up go-datastore deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@3a7f9b09f6da9fe3f5107bcda73ab5ca4a7c8f81 --- mfs/dir.go | 2 +- mfs/file.go | 2 +- mfs/mfs_test.go | 8 ++++---- mfs/ops.go | 2 +- mfs/repub_test.go | 2 +- mfs/system.go | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 3ff28f5fd..ec6de0a45 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -14,7 +14,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" ) var ErrNotYetImplemented = errors.New("not yet implemented") diff --git a/mfs/file.go b/mfs/file.go index 65b9f6cc7..b61380d77 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 22fa3f774..7b19f50b3 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -24,11 +24,11 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - dssync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) func emptyDirNode() *dag.ProtoNode { diff --git a/mfs/ops.go b/mfs/ops.go index 0df570463..d71109226 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -9,7 +9,7 @@ import ( path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" ) // Mv moves the file or directory at 'src' to 'dst' diff --git a/mfs/repub_test.go b/mfs/repub_test.go index f56b5a84d..2b8acea73 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -7,7 +7,7 @@ import ( ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" "context" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index bb1f4baf0..0578166af 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -18,9 +18,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" + node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - node "gx/ipfs/QmUsVJ7AEnGyjX8YWnrwq9vmECVGwBQNAKPpgz5KSg8dcq/go-ipld-node" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) var ErrNotExist = errors.New("no such rootfs") From f867cf89df35bdcb8009c444d19cbae0b05617d7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 22:29:38 -0800 Subject: [PATCH 1670/3817] bubble up go-datastore deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@4c9286a80e68ce2636a592f3693d9e20634631e3 --- exchange/offline/offline.go | 2 +- exchange/offline/offline_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index b0df47f13..0fae10da6 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -9,7 +9,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 7b3cc1ee9..08c4aaf87 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -8,10 +8,10 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + ds_sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore" - ds_sync "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore/sync" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) func TestBlockReturnsErr(t *testing.T) { From d152fab36757862e7bdf332ad18d30741334c8dd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 28 Nov 2016 22:29:38 -0800 Subject: [PATCH 1671/3817] bubble up go-datastore deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-interface@cf6f27e29a938eba19ca56542e3a8c5fe30aa1ff --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index 1ee0eaf0e..62e22c38d 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -7,7 +7,7 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" - cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid" + cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) // Any type that implements exchange.Interface may be used as an IPFS block From 75981c86d1b56cfd8a15f8231cb761225bd7ba6b Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 21 Nov 2016 21:33:28 -0500 Subject: [PATCH 1672/3817] ds-help: avoid unnecessary allocs when posssible and make use of RawKey License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-blockstore@02ca16d30272a72916ea71c725b8a42b13a598b0 --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 40763276b..9e5d8ca80 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -196,7 +196,7 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) } // need to convert to key.Key using key.KeyFromDsKey. - c, err := dshelp.DsKeyStringToCid(e.Key) + c, err := dshelp.DsKeyToCid(ds.RawKey(e.Key)) if err != nil { log.Warningf("error parsing key from DsKey: ", err) return nil, true From 778a8e5806853c3bcf6c6a237265cec6608a49ed Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 21 Nov 2016 21:33:28 -0500 Subject: [PATCH 1673/3817] ds-help: avoid unnecessary allocs when posssible and make use of RawKey License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-namesys@22f525be5590c82bb80b508d32ad046968a81432 --- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 345f21e33..795023c83 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -80,7 +80,7 @@ func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value } func (p *ipnsPublisher) getPreviousSeqNo(ctx context.Context, ipnskey string) (uint64, error) { - prevrec, err := p.ds.Get(dshelp.NewKeyFromBinary(ipnskey)) + prevrec, err := p.ds.Get(dshelp.NewKeyFromBinary([]byte(ipnskey))) if err != nil && err != ds.ErrNotFound { // None found, lets start at zero! return 0, err diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 9abe8688e..e4e0bdc92 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -108,7 +108,7 @@ func (rp *Republisher) republishEntries(p goprocess.Process) error { } func (rp *Republisher) getLastVal(k string) (path.Path, uint64, error) { - ival, err := rp.ds.Get(dshelp.NewKeyFromBinary(k)) + ival, err := rp.ds.Get(dshelp.NewKeyFromBinary([]byte(k))) if err != nil { // not found means we dont have a previously published entry return "", 0, errNoEntry From 58c0937faab6cf98bb9aaaf16c6957c8ceed755e Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 21 Nov 2016 21:33:28 -0500 Subject: [PATCH 1674/3817] ds-help: avoid unnecessary allocs when posssible and make use of RawKey License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-routing@79b5c2e449be69fb5f08af18b5d9ff5db37fa27d --- routing/mock/centralized_client.go | 4 ++-- routing/offline/offline.go | 6 +++--- routing/supernode/server.go | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 1a413ab9c..135f3be99 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -40,13 +40,13 @@ func (c *client) PutValue(ctx context.Context, key string, val []byte) error { return err } - return c.datastore.Put(dshelp.NewKeyFromBinary(key), data) + return c.datastore.Put(dshelp.NewKeyFromBinary([]byte(key)), data) } // FIXME(brian): is this method meant to simulate getting a value from the network? func (c *client) GetValue(ctx context.Context, key string) ([]byte, error) { log.Debugf("GetValue: %s", key) - v, err := c.datastore.Get(dshelp.NewKeyFromBinary(key)) + v, err := c.datastore.Get(dshelp.NewKeyFromBinary([]byte(key))) if err != nil { return nil, err } diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 1e3297bb3..566466139 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -48,11 +48,11 @@ func (c *offlineRouting) PutValue(ctx context.Context, key string, val []byte) e return err } - return c.datastore.Put(dshelp.NewKeyFromBinary(key), data) + return c.datastore.Put(dshelp.NewKeyFromBinary([]byte(key)), data) } func (c *offlineRouting) GetValue(ctx context.Context, key string) ([]byte, error) { - v, err := c.datastore.Get(dshelp.NewKeyFromBinary(key)) + v, err := c.datastore.Get(dshelp.NewKeyFromBinary([]byte(key))) if err != nil { return nil, err } @@ -71,7 +71,7 @@ func (c *offlineRouting) GetValue(ctx context.Context, key string) ([]byte, erro } func (c *offlineRouting) GetValues(ctx context.Context, key string, _ int) ([]routing.RecvdVal, error) { - v, err := c.datastore.Get(dshelp.NewKeyFromBinary(key)) + v, err := c.datastore.Get(dshelp.NewKeyFromBinary([]byte(key))) if err != nil { return nil, err } diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 6c96cbc2e..b97e613a5 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -117,7 +117,7 @@ var _ proxy.RequestHandler = &Server{} var _ proxy.Proxy = &Server{} func getRoutingRecord(ds datastore.Datastore, k string) (*pb.Record, error) { - dskey := dshelp.NewKeyFromBinary(k) + dskey := dshelp.NewKeyFromBinary([]byte(k)) val, err := ds.Get(dskey) if err != nil { return nil, err @@ -138,7 +138,7 @@ func putRoutingRecord(ds datastore.Datastore, k string, value *pb.Record) error if err != nil { return err } - dskey := dshelp.NewKeyFromBinary(k) + dskey := dshelp.NewKeyFromBinary([]byte(k)) // TODO namespace if err := ds.Put(dskey, data); err != nil { return err From 76d273cd644b0884d0b23979709d7111eab50b65 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 21 Nov 2016 21:33:28 -0500 Subject: [PATCH 1675/3817] ds-help: avoid unnecessary allocs when posssible and make use of RawKey License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-ds-help@d0530afe5763c6712b536f4f1f33349a7d015499 --- datastore/dshelp/key.go | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 256cb1592..7e962fff0 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -7,8 +7,12 @@ import ( ) // TODO: put this code into the go-datastore itself -func NewKeyFromBinary(s string) ds.Key { - return ds.NewKey(base32.RawStdEncoding.EncodeToString([]byte(s))) + +func NewKeyFromBinary(rawKey []byte) ds.Key { + buf := make([]byte, 1+base32.RawStdEncoding.EncodedLen(len(rawKey))) + buf[0] = '/' + base32.RawStdEncoding.Encode(buf[1:], rawKey) + return ds.RawKey(string(buf)) } func BinaryFromDsKey(k ds.Key) ([]byte, error) { @@ -16,7 +20,7 @@ func BinaryFromDsKey(k ds.Key) ([]byte, error) { } func CidToDsKey(k *cid.Cid) ds.Key { - return NewKeyFromBinary(k.KeyString()) + return NewKeyFromBinary(k.Bytes()) } func DsKeyToCid(dsKey ds.Key) (*cid.Cid, error) { @@ -26,11 +30,3 @@ func DsKeyToCid(dsKey ds.Key) (*cid.Cid, error) { } return cid.Cast(kb) } - -func DsKeyStringToCid(dsKey string) (*cid.Cid, error) { - kb, err := base32.RawStdEncoding.DecodeString(dsKey[1:]) - if err != nil { - return nil, err - } - return cid.Cast(kb) -} From c0c29602548fac2a372e6be76e51ee6945990188 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 29 Nov 2016 10:15:40 -0800 Subject: [PATCH 1676/3817] merkledag: retain cid types when roundtripping through a ProtoNode License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@e5a1a0759b04a08850bae43cd99518fe9528381e --- ipld/merkledag/coding.go | 15 +++++++++++++-- ipld/merkledag/merkledag.go | 1 + ipld/merkledag/node.go | 3 +++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 3b43e4c95..2bd88265b 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -7,7 +7,7 @@ import ( pb "github.com/ipfs/go-ipfs/merkledag/pb" node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" + mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) @@ -84,7 +84,18 @@ func (n *ProtoNode) EncodeProtobuf(force bool) ([]byte, error) { } if n.cached == nil { - n.cached = cid.NewCidV0(u.Hash(n.encoded)) + if n.prefix.MhType == 0 { // unset + n.prefix.Codec = cid.DagProtobuf + n.prefix.MhLength = -1 + n.prefix.MhType = mh.SHA2_256 + n.prefix.Version = 0 + } + c, err := n.prefix.Sum(n.encoded) + if err != nil { + return nil, err + } + + n.cached = c } return n.encoded, nil diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 10ff5fb58..67169ed05 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -103,6 +103,7 @@ func decodeBlock(b blocks.Block) (node.Node, error) { } decnd.cached = b.Cid() + decnd.prefix = b.Cid().Prefix() return decnd, nil case cid.Raw: return NewRawNode(b.RawData()), nil diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 16d6941a2..6c89947ab 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -22,6 +22,9 @@ type ProtoNode struct { encoded []byte cached *cid.Cid + + // prefix specifies cid version and hashing function + prefix cid.Prefix } type LinkSlice []*node.Link From 9bab300914bf3f7b639c308cf33f69430e189c19 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 29 Nov 2016 14:03:05 -0800 Subject: [PATCH 1677/3817] merkledag: respond with correct cid to Cid() method License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@fdf8dcc961192e1492434975ef5c0f397fdcb719 --- ipld/merkledag/coding.go | 10 +++------ ipld/merkledag/merkledag.go | 2 +- ipld/merkledag/merkledag_test.go | 35 ++++++++++++++++++++++++++++++++ ipld/merkledag/node.go | 28 +++++++++++++++++++++---- ipld/merkledag/test/utils.go | 7 +++++-- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 2bd88265b..e538e519c 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -7,7 +7,6 @@ import ( pb "github.com/ipfs/go-ipfs/merkledag/pb" node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" - mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) @@ -84,13 +83,10 @@ func (n *ProtoNode) EncodeProtobuf(force bool) ([]byte, error) { } if n.cached == nil { - if n.prefix.MhType == 0 { // unset - n.prefix.Codec = cid.DagProtobuf - n.prefix.MhLength = -1 - n.prefix.MhType = mh.SHA2_256 - n.prefix.Version = 0 + if n.Prefix.Codec == 0 { // unset + n.Prefix = defaultCidPrefix } - c, err := n.prefix.Sum(n.encoded) + c, err := n.Prefix.Sum(n.encoded) if err != nil { return nil, err } diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 67169ed05..dce41f516 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -103,7 +103,7 @@ func decodeBlock(b blocks.Block) (node.Node, error) { } decnd.cached = b.Cid() - decnd.prefix = b.Cid().Prefix() + decnd.Prefix = b.Cid().Prefix() return decnd, nil case cid.Raw: return NewRawNode(b.RawData()), nil diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 13d138105..3fa45d8ac 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -11,6 +11,7 @@ import ( "sync" "testing" + blocks "github.com/ipfs/go-ipfs/blocks" bserv "github.com/ipfs/go-ipfs/blockservice" bstest "github.com/ipfs/go-ipfs/blockservice/test" offline "github.com/ipfs/go-ipfs/exchange/offline" @@ -450,3 +451,37 @@ func TestProtoNodeResolve(t *testing.T) { t.Fatal("expected tree to return []{\"foo\"}") } } + +func TestCidRetention(t *testing.T) { + nd := new(ProtoNode) + nd.SetData([]byte("fooooo")) + + pref := nd.Cid().Prefix() + pref.Version = 1 + + c2, err := pref.Sum(nd.RawData()) + if err != nil { + t.Fatal(err) + } + + blk, err := blocks.NewBlockWithCid(nd.RawData(), c2) + if err != nil { + t.Fatal(err) + } + + bs := dstest.Bserv() + _, err = bs.AddBlock(blk) + if err != nil { + t.Fatal(err) + } + + ds := NewDAGService(bs) + out, err := ds.Get(context.Background(), c2) + if err != nil { + t.Fatal(err) + } + + if !out.Cid().Equals(c2) { + t.Fatal("output cid didnt match") + } +} diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 6c89947ab..38a47382b 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -23,8 +23,15 @@ type ProtoNode struct { cached *cid.Cid - // prefix specifies cid version and hashing function - prefix cid.Prefix + // Prefix specifies cid version and hashing function + Prefix cid.Prefix +} + +var defaultCidPrefix = cid.Prefix{ + Codec: cid.DagProtobuf, + MhLength: -1, + MhType: mh.SHA2_256, + Version: 0, } type LinkSlice []*node.Link @@ -222,9 +229,22 @@ func (n *ProtoNode) Loggable() map[string]interface{} { } func (n *ProtoNode) Cid() *cid.Cid { - h := n.Multihash() + if n.encoded != nil && n.cached != nil { + return n.cached + } + + if n.Prefix.Codec == 0 { + n.Prefix = defaultCidPrefix + } + + c, err := n.Prefix.Sum(n.RawData()) + if err != nil { + // programmer error + panic(err) + } - return cid.NewCidV0(h) + n.cached = c + return c } func (n *ProtoNode) String() string { diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index b866ec6e4..c004d9a36 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -10,7 +10,10 @@ import ( ) func Mock() dag.DAGService { + return dag.NewDAGService(Bserv()) +} + +func Bserv() bsrv.BlockService { bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) - bserv := bsrv.New(bstore, offline.Exchange(bstore)) - return dag.NewDAGService(bserv) + return bsrv.New(bstore, offline.Exchange(bstore)) } From 5754c72858f6f8d76948df029485dd40b1cdc2f6 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 30 Nov 2016 17:45:17 -0500 Subject: [PATCH 1678/3817] blockstore.AllKeyChan: avoid channels by using the new NextSync method License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-blockstore@c2ebb19a608323aef7bd1565661116188c7eb4a4 --- blockstore/blockstore.go | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 9e5d8ca80..d5a77ab23 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -181,44 +181,27 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) return nil, err } - // this function is here to compartmentalize - get := func() (*cid.Cid, bool) { - select { - case <-ctx.Done(): - return nil, false - case e, more := <-res.Next(): - if !more { - return nil, false - } - if e.Error != nil { - log.Debug("blockstore.AllKeysChan got err:", e.Error) - return nil, false - } - - // need to convert to key.Key using key.KeyFromDsKey. - c, err := dshelp.DsKeyToCid(ds.RawKey(e.Key)) - if err != nil { - log.Warningf("error parsing key from DsKey: ", err) - return nil, true - } - - return c, true - } - } - output := make(chan *cid.Cid, dsq.KeysOnlyBufSize) go func() { defer func() { - res.Process().Close() // ensure exit (signals early exit, too) + res.Close() // ensure exit (signals early exit, too) close(output) }() for { - k, ok := get() + e, ok := res.NextSync() if !ok { return } - if k == nil { + if e.Error != nil { + log.Debug("blockstore.AllKeysChan got err:", e.Error) + continue + } + + // need to convert to key.Key using key.KeyFromDsKey. + k, err := dshelp.DsKeyToCid(ds.RawKey(e.Key)) + if err != nil { + log.Warningf("error parsing key from DsKey: ", err) continue } From afe8ba0ad9767c0668cd1408e547ec31d88c371f Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 30 Nov 2016 18:21:24 -0500 Subject: [PATCH 1679/3817] blockstore.AllKeyChan: fix/cleanup error handling License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-blockstore@68412cde465609210425e277b1dc56ab7790b797 --- blockstore/blockstore.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index d5a77ab23..6313cfffe 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -194,8 +194,8 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) return } if e.Error != nil { - log.Debug("blockstore.AllKeysChan got err:", e.Error) - continue + log.Errorf("blockstore.AllKeysChan got err:", e.Error) + return } // need to convert to key.Key using key.KeyFromDsKey. From 8a7a19377805f96366b9ac6ed735fe842db7be25 Mon Sep 17 00:00:00 2001 From: Mib Kd743naq Date: Fri, 2 Dec 2016 05:25:27 +0100 Subject: [PATCH 1680/3817] Fix bad formatting introduced by e855047ec License: MIT Signed-off-by: Mib Kd743naq This commit was moved from ipfs/go-ipfs-blockstore@2426bda300ef587362e66055b6cab15f3de659d3 --- blockstore/util/remove.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 3c16549bf..e4ae67868 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -29,7 +29,7 @@ type RmBlocksOpts struct { func RmBlocks(blocks bs.GCBlockstore, pins pin.Pinner, cids []*cid.Cid, opts RmBlocksOpts) (<-chan interface{}, error) { // make the channel large enough to hold any result to avoid - // blocking while holding the GCLock + // blocking while holding the GCLock out := make(chan interface{}, len(cids)) go func() { defer close(out) From dfc7c724a7f33eb8f27e8e713c3077a9b8758ea1 Mon Sep 17 00:00:00 2001 From: Mib Kd743naq Date: Fri, 2 Dec 2016 06:37:11 +0100 Subject: [PATCH 1681/3817] Switch unixfs.Metadata.MimeType to optional *** THIS IS A BREAKING CHANGE *** as per [1]: "Required is forever" Nevertheless this seems like a good idea at this time: there are no known producers ( nor consumers ) of MetaData nodes, and the current requirement of MimeType has an extremely narrow application scope. This change could very well be rejected in lieu of implementing a new type of node ( e.g. TheRealMetadata ) in the DataType enum. Based on https://github.com/ipfs/go-ipfs/issues/3451#issuecomment-264246718 License: MIT Signed-off-by: Mib Kd743naq [1] https://developers.google.com/protocol-buffers/docs/proto#specifying-field-rules This commit was moved from ipfs/go-unixfs@e94be522d89ba2057d8b12796e4f3cfbeed7fe37 --- unixfs/pb/unixfs.pb.go | 10 +++++++--- unixfs/pb/unixfs.proto | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/unixfs/pb/unixfs.pb.go b/unixfs/pb/unixfs.pb.go index 55348ad76..ffd3bb905 100644 --- a/unixfs/pb/unixfs.pb.go +++ b/unixfs/pb/unixfs.pb.go @@ -15,10 +15,12 @@ It has these top-level messages: package unixfs_pb import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" +import fmt "fmt" import math "math" // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal +var _ = fmt.Errorf var _ = math.Inf type Data_DataType int32 @@ -64,8 +66,8 @@ func (x *Data_DataType) UnmarshalJSON(data []byte) error { } type Data struct { - Type *Data_DataType `protobuf:"varint,1,req,enum=unixfs.pb.Data_DataType" json:"Type,omitempty"` - Data []byte `protobuf:"bytes,2,opt" json:"Data,omitempty"` + Type *Data_DataType `protobuf:"varint,1,req,name=Type,enum=unixfs.pb.Data_DataType" json:"Type,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=Data" json:"Data,omitempty"` Filesize *uint64 `protobuf:"varint,3,opt,name=filesize" json:"filesize,omitempty"` Blocksizes []uint64 `protobuf:"varint,4,rep,name=blocksizes" json:"blocksizes,omitempty"` XXX_unrecognized []byte `json:"-"` @@ -104,7 +106,7 @@ func (m *Data) GetBlocksizes() []uint64 { } type Metadata struct { - MimeType *string `protobuf:"bytes,1,req" json:"MimeType,omitempty"` + MimeType *string `protobuf:"bytes,1,opt,name=MimeType" json:"MimeType,omitempty"` XXX_unrecognized []byte `json:"-"` } @@ -120,5 +122,7 @@ func (m *Metadata) GetMimeType() string { } func init() { + proto.RegisterType((*Data)(nil), "unixfs.pb.Data") + proto.RegisterType((*Metadata)(nil), "unixfs.pb.Metadata") proto.RegisterEnum("unixfs.pb.Data_DataType", Data_DataType_name, Data_DataType_value) } diff --git a/unixfs/pb/unixfs.proto b/unixfs/pb/unixfs.proto index 4a52c3af5..2e4d47947 100644 --- a/unixfs/pb/unixfs.proto +++ b/unixfs/pb/unixfs.proto @@ -16,5 +16,5 @@ message Data { } message Metadata { - required string MimeType = 1; + optional string MimeType = 1; } From 9397964cf68a0b92b2151bae1c22ba7b0684025d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 30 Nov 2016 10:18:09 -0800 Subject: [PATCH 1682/3817] basic keystore implementation License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-keystore@c2eb8ce52da2c03ea3932e2e54a37aecda6b7782 --- keystore/keystore.go | 123 ++++++++++++++++++++++++++++++++++++++++ keystore/memkeystore.go | 55 ++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 keystore/keystore.go create mode 100644 keystore/memkeystore.go diff --git a/keystore/keystore.go b/keystore/keystore.go new file mode 100644 index 000000000..19e77f173 --- /dev/null +++ b/keystore/keystore.go @@ -0,0 +1,123 @@ +package keystore + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" +) + +type Keystore interface { + Put(string, ci.PrivKey) error + Get(string) (ci.PrivKey, error) + Delete(string) error + List() ([]string, error) +} + +var ErrNoSuchKey = fmt.Errorf("no key by the given name was found") +var ErrKeyExists = fmt.Errorf("key by that name already exists, refusing to overwrite") + +type FSKeystore struct { + dir string +} + +func validateName(name string) error { + if name == "" { + return fmt.Errorf("key names must be at least one character") + } + + if strings.Contains(name, "/") { + return fmt.Errorf("key names may not contain slashes") + } + + if strings.HasPrefix(name, ".") { + return fmt.Errorf("key names may not begin with a period") + } + + return nil +} + +func NewFSKeystore(dir string) (*FSKeystore, error) { + _, err := os.Stat(dir) + if err != nil { + if !os.IsNotExist(err) { + return nil, err + } + if err := os.Mkdir(dir, 0700); err != nil { + return nil, err + } + } + + return &FSKeystore{dir}, nil +} + +func (ks *FSKeystore) Put(name string, k ci.PrivKey) error { + if err := validateName(name); err != nil { + return err + } + + b, err := k.Bytes() + if err != nil { + return err + } + + kp := filepath.Join(ks.dir, name) + + _, err = os.Stat(kp) + if err == nil { + return ErrKeyExists + } + + fi, err := os.Create(kp) + if err != nil { + return err + } + defer fi.Close() + + _, err = fi.Write(b) + if err != nil { + return err + } + + return nil +} + +func (ks *FSKeystore) Get(name string) (ci.PrivKey, error) { + if err := validateName(name); err != nil { + return nil, err + } + + kp := filepath.Join(ks.dir, name) + + data, err := ioutil.ReadFile(kp) + if err != nil { + if os.IsNotExist(err) { + return nil, ErrNoSuchKey + } + return nil, err + } + + return ci.UnmarshalPrivateKey(data) +} + +func (ks *FSKeystore) Delete(name string) error { + if err := validateName(name); err != nil { + return err + } + + kp := filepath.Join(ks.dir, name) + + return os.Remove(kp) +} + +func (ks *FSKeystore) List() ([]string, error) { + dir, err := os.Open(ks.dir) + if err != nil { + return nil, err + } + + return dir.Readdirnames(0) +} diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go new file mode 100644 index 000000000..9eec377db --- /dev/null +++ b/keystore/memkeystore.go @@ -0,0 +1,55 @@ +package keystore + +import ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" + +type MemKeystore struct { + keys map[string]ci.PrivKey +} + +func NewMemKeystore() *MemKeystore { + return &MemKeystore{make(map[string]ci.PrivKey)} +} + +func (mk *MemKeystore) Put(name string, k ci.PrivKey) error { + if err := validateName(name); err != nil { + return err + } + + _, ok := mk.keys[name] + if ok { + return ErrKeyExists + } + + mk.keys[name] = k + return nil +} + +func (mk *MemKeystore) Get(name string) (ci.PrivKey, error) { + if err := validateName(name); err != nil { + return nil, err + } + + k, ok := mk.keys[name] + if !ok { + return nil, ErrNoSuchKey + } + + return k, nil +} + +func (mk *MemKeystore) Delete(name string) error { + if err := validateName(name); err != nil { + return err + } + + delete(mk.keys, name) + return nil +} + +func (mk *MemKeystore) List() ([]string, error) { + out := make([]string, 0, len(mk.keys)) + for k, _ := range mk.keys { + out = append(out, k) + } + return out, nil +} From 7add5c37293ab06b5003eb10c976213f4b82d13d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 6 Dec 2016 11:20:18 -0800 Subject: [PATCH 1683/3817] address comments and add some tests License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-keystore@1bcdb41555eb4723075846b6314ca2663a909310 --- keystore/keystore.go | 2 + keystore/keystore_test.go | 177 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 keystore/keystore_test.go diff --git a/keystore/keystore.go b/keystore/keystore.go index 19e77f173..a18da4620 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -69,6 +69,8 @@ func (ks *FSKeystore) Put(name string, k ci.PrivKey) error { _, err = os.Stat(kp) if err == nil { return ErrKeyExists + } else if !os.IsNotExist(err) { + return err } fi, err := os.Create(kp) diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go new file mode 100644 index 000000000..7f1fcd5ba --- /dev/null +++ b/keystore/keystore_test.go @@ -0,0 +1,177 @@ +package keystore + +import ( + "fmt" + "io/ioutil" + "math/rand" + "sort" + "testing" + + ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" +) + +type rr struct{} + +func (rr rr) Read(b []byte) (int, error) { + return rand.Read(b) +} + +func privKeyOrFatal(t *testing.T) ci.PrivKey { + priv, _, err := ci.GenerateEd25519Key(rr{}) + if err != nil { + t.Fatal(err) + } + return priv +} + +func TestKeystoreBasics(t *testing.T) { + tdir, err := ioutil.TempDir("", "keystore-test") + if err != nil { + t.Fatal(err) + } + + ks, err := NewFSKeystore(tdir) + if err != nil { + t.Fatal(err) + } + + l, err := ks.List() + if err != nil { + t.Fatal(err) + } + + if len(l) != 0 { + t.Fatal("expected no keys") + } + + k1 := privKeyOrFatal(t) + k2 := privKeyOrFatal(t) + k3 := privKeyOrFatal(t) + k4 := privKeyOrFatal(t) + + err = ks.Put("foo", k1) + if err != nil { + t.Fatal(err) + } + + err = ks.Put("bar", k2) + if err != nil { + t.Fatal(err) + } + + l, err = ks.List() + if err != nil { + t.Fatal(err) + } + + sort.Strings(l) + if l[0] != "bar" || l[1] != "foo" { + t.Fatal("wrong entries listed") + } + + if err := assertDirContents(tdir, []string{"foo", "bar"}); err != nil { + t.Fatal(err) + } + + err = ks.Put("foo", k3) + if err == nil { + t.Fatal("should not be able to overwrite key") + } + + if err := assertDirContents(tdir, []string{"foo", "bar"}); err != nil { + t.Fatal(err) + } + + if err := ks.Delete("bar"); err != nil { + t.Fatal(err) + } + + if err := assertDirContents(tdir, []string{"foo"}); err != nil { + t.Fatal(err) + } + + if err := ks.Put("beep", k3); err != nil { + t.Fatal(err) + } + + if err := ks.Put("boop", k4); err != nil { + t.Fatal(err) + } + + if err := assertDirContents(tdir, []string{"foo", "beep", "boop"}); err != nil { + t.Fatal(err) + } + + if err := assertGetKey(ks, "foo", k1); err != nil { + t.Fatal(err) + } + + if err := assertGetKey(ks, "beep", k3); err != nil { + t.Fatal(err) + } + + if err := assertGetKey(ks, "boop", k4); err != nil { + t.Fatal(err) + } + + if err := ks.Put("..///foo/", k1); err == nil { + t.Fatal("shouldnt be able to put a poorly named key") + } + + if err := ks.Put("", k1); err == nil { + t.Fatal("shouldnt be able to put a key with no name") + } + + if err := ks.Put(".foo", k1); err == nil { + t.Fatal("shouldnt be able to put a key with a 'hidden' name") + } +} + +func TestMakeKeystoreNoDir(t *testing.T) { + _, err := NewFSKeystore("/this/is/not/a/real/dir") + if err == nil { + t.Fatal("shouldnt be able to make a keystore in a nonexistant directory") + } +} + +func assertGetKey(ks Keystore, name string, exp ci.PrivKey) error { + out_k, err := ks.Get(name) + if err != nil { + return err + } + + if !out_k.Equals(exp) { + return fmt.Errorf("key we got out didnt match expectation") + } + + return nil +} + +func assertDirContents(dir string, exp []string) error { + finfos, err := ioutil.ReadDir(dir) + if err != nil { + return err + } + + if len(finfos) != len(exp) { + return fmt.Errorf("Expected %d directory entries", len(exp)) + } + + var names []string + for _, fi := range finfos { + names = append(names, fi.Name()) + } + + sort.Strings(names) + sort.Strings(exp) + if len(names) != len(exp) { + return fmt.Errorf("directory had wrong number of entries in it") + } + + for i, v := range names { + if v != exp[i] { + return fmt.Errorf("had wrong entry in directory") + } + } + return nil +} From 4f7baa099162f46fe6ca103a1da043da9b500b0f Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 7 Dec 2016 08:59:54 +0100 Subject: [PATCH 1684/3817] test: add test for nonexistant key License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-keystore@387bce4f9b807a631df77ac3aff8e529378925f1 --- keystore/keystore_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index 7f1fcd5ba..a58fe778c 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -127,6 +127,26 @@ func TestKeystoreBasics(t *testing.T) { } } +func TestNonExistingKey(t *testing.T) { + tdir, err := ioutil.TempDir("", "keystore-test") + if err != nil { + t.Fatal(err) + } + + ks, err := NewFSKeystore(tdir) + if err != nil { + t.Fatal(err) + } + + k, err := ks.Get("does-it-exist") + if err != ErrNoSuchKey { + t.Fatalf("expected: %s, got %s", ErrNoSuchKey, err) + } + if k != nil { + t.Fatalf("Get on nonexistant key should give nil") + } +} + func TestMakeKeystoreNoDir(t *testing.T) { _, err := NewFSKeystore("/this/is/not/a/real/dir") if err == nil { From 58ac5c59d1afabd7b68868d3ef006bf3afd7e837 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 7 Dec 2016 09:02:23 +0100 Subject: [PATCH 1685/3817] test: add memkeystore test License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-keystore@79f8956be7122a8f337b58b7639427dd9a00aa80 --- keystore/memkeystore_test.go | 82 ++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 keystore/memkeystore_test.go diff --git a/keystore/memkeystore_test.go b/keystore/memkeystore_test.go new file mode 100644 index 000000000..7f4362795 --- /dev/null +++ b/keystore/memkeystore_test.go @@ -0,0 +1,82 @@ +package keystore + +import ( + "sort" + "testing" +) + +func TestMemKeyStoreBasics(t *testing.T) { + ks := NewMemKeystore() + + l, err := ks.List() + if err != nil { + t.Fatal(err) + } + + if len(l) != 0 { + t.Fatal("expected no keys") + } + + k1 := privKeyOrFatal(t) + k2 := privKeyOrFatal(t) + k3 := privKeyOrFatal(t) + k4 := privKeyOrFatal(t) + + err = ks.Put("foo", k1) + if err != nil { + t.Fatal(err) + } + + err = ks.Put("bar", k2) + if err != nil { + t.Fatal(err) + } + + l, err = ks.List() + if err != nil { + t.Fatal(err) + } + + sort.Strings(l) + if l[0] != "bar" || l[1] != "foo" { + t.Fatal("wrong entries listed") + } + + err = ks.Put("foo", k3) + if err == nil { + t.Fatal("should not be able to overwrite key") + } + if err := ks.Delete("bar"); err != nil { + t.Fatal(err) + } + if err := ks.Put("beep", k3); err != nil { + t.Fatal(err) + } + + if err := ks.Put("boop", k4); err != nil { + t.Fatal(err) + } + if err := assertGetKey(ks, "foo", k1); err != nil { + t.Fatal(err) + } + + if err := assertGetKey(ks, "beep", k3); err != nil { + t.Fatal(err) + } + + if err := assertGetKey(ks, "boop", k4); err != nil { + t.Fatal(err) + } + + if err := ks.Put("..///foo/", k1); err == nil { + t.Fatal("shouldnt be able to put a poorly named key") + } + + if err := ks.Put("", k1); err == nil { + t.Fatal("shouldnt be able to put a key with no name") + } + + if err := ks.Put(".foo", k1); err == nil { + t.Fatal("shouldnt be able to put a key with a 'hidden' name") + } +} From 4d7c91c043ecd518cc091f4befb4710ee83d367f Mon Sep 17 00:00:00 2001 From: Zander Mackie Date: Fri, 9 Dec 2016 07:28:24 -0500 Subject: [PATCH 1686/3817] Add test for unixfs/format to reach 87% coverage. License: MIT Signed-off-by: Zander Mackie This commit was moved from ipfs/go-unixfs@63cfd751de156ddacfe3cf3d583a939ed18bc2b7 --- unixfs/format_test.go | 124 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 1 deletion(-) diff --git a/unixfs/format_test.go b/unixfs/format_test.go index ac35db56f..10421431a 100644 --- a/unixfs/format_test.go +++ b/unixfs/format_test.go @@ -1,6 +1,7 @@ package unixfs import ( + "bytes" "testing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" @@ -11,9 +12,10 @@ import ( func TestFSNode(t *testing.T) { fsn := new(FSNode) fsn.Type = TFile - for i := 0; i < 15; i++ { + for i := 0; i < 16; i++ { fsn.AddBlockSize(100) } + fsn.RemoveBlockSize(15) fsn.Data = make([]byte, 128) @@ -32,8 +34,128 @@ func TestFSNode(t *testing.T) { if err != nil { t.Fatal(err) } + nKids := fsn.NumChildren() + if nKids != 15 { + t.Fatal("Wrong number of child nodes") + } if ds != (100*15)+128 { t.Fatal("Datasize calculations incorrect!") } + + nfsn, err := FSNodeFromBytes(b) + if err != nil { + t.Fatal(err) + } + + if nfsn.FileSize() != (100*15)+128 { + t.Fatal("fsNode FileSize calculations incorrect") + } +} + +func TestPBdataTools(t *testing.T) { + raw := []byte{0x00, 0x01, 0x02, 0x17, 0xA1} + rawPB := WrapData(raw) + + pbDataSize, err := DataSize(rawPB) + if err != nil { + t.Fatal(err) + } + + same := len(raw) == int(pbDataSize) + if !same { + t.Fatal("WrapData changes the size of data.") + } + + rawPBBytes, err := UnwrapData(rawPB) + if err != nil { + t.Fatal(err) + } + + same = bytes.Equal(raw, rawPBBytes) + if !same { + t.Fatal("Unwrap failed to produce the correct wrapped data.") + } + + rawPBdata, err := FromBytes(rawPB) + if err != nil { + t.Fatal(err) + } + + isRaw := rawPBdata.GetType() == TRaw + if !isRaw { + t.Fatal("WrapData does not create pb.Data_Raw!") + } + + catFile := []byte("Mr_Meowgie.gif") + catPBfile := FilePBData(catFile, 17) + catSize, err := DataSize(catPBfile) + if catSize != 17 { + t.Fatal("FilePBData is the wrong size.") + } + if err != nil { + t.Fatal(err) + } + + dirPB := FolderPBData() + dir, err := FromBytes(dirPB) + isDir := dir.GetType() == TDirectory + if !isDir { + t.Fatal("FolderPBData does not create a directory!") + } + if err != nil { + t.Fatal(err) + } + _, dirErr := DataSize(dirPB) + if dirErr == nil { + t.Fatal("DataSize didn't throw an error when taking the size of a directory.") + } + + catSym, err := SymlinkData("/ipfs/adad123123/meowgie.gif") + if err != nil { + t.Fatal(err) + } + + catSymPB, err := FromBytes(catSym) + isSym := catSymPB.GetType() == TSymlink + if !isSym { + t.Fatal("Failed to make a Symlink.") + } + if err != nil { + t.Fatal(err) + } + + _, sizeErr := DataSize(catSym) + if sizeErr == nil { + t.Fatal("DataSize didn't throw an error when taking the size of a Symlink.") + } + +} + +func TestMetedata(t *testing.T) { + meta := &Metadata{ + MimeType: "audio/aiff", + Size: 12345, + } + + _, err := meta.Bytes() + if err != nil { + t.Fatal(err) + } + + metaPB, err := BytesForMetadata(meta) + if err != nil { + t.Fatal(err) + } + + meta, err = MetadataFromBytes(metaPB) + if err != nil { + t.Fatal(err) + } + + mimeAiff := meta.MimeType == "audio/aiff" + if !mimeAiff { + t.Fatal("Metadata does not Marshal and Unmarshal properly!") + } + } From 3faabecca7efb39b5ada2e3e5e31e6f8719332d6 Mon Sep 17 00:00:00 2001 From: Zander Mackie Date: Fri, 9 Dec 2016 07:27:16 -0500 Subject: [PATCH 1687/3817] Add Some Comments to unixfs/format License: MIT Signed-off-by: Zander Mackie This commit was moved from ipfs/go-unixfs@2e3a9f76a80e455c5e9627dce74be5197c9518b3 --- unixfs/format.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/unixfs/format.go b/unixfs/format.go index 7a602362e..96dd109d1 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -51,7 +51,7 @@ func FilePBData(data []byte, totalsize uint64) []byte { return data } -// Returns Bytes that represent a Directory +//FolderPBData returns Bytes that represent a Directory. func FolderPBData() []byte { pbfile := new(pb.Data) typ := pb.Data_Directory @@ -65,6 +65,7 @@ func FolderPBData() []byte { return data } +//WrapData marshals raw bytes into a `Data_Raw` type protobuf message. func WrapData(b []byte) []byte { pbdata := new(pb.Data) typ := pb.Data_Raw @@ -81,6 +82,7 @@ func WrapData(b []byte) []byte { return out } +//SymlinkData returns a `Data_Symlink` protobuf message for the path you specify. func SymlinkData(path string) ([]byte, error) { pbdata := new(pb.Data) typ := pb.Data_Symlink @@ -184,6 +186,7 @@ type Metadata struct { Size uint64 } +//MetadataFromBytes Unmarshals a protobuf message into Metadata. func MetadataFromBytes(b []byte) (*Metadata, error) { pbd := new(pb.Data) err := proto.Unmarshal(b, pbd) From b954edcf84f82cdf76fd8bc8fb67ce3cf3b52ed5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 9 Dec 2016 14:58:12 -0800 Subject: [PATCH 1688/3817] merkledag: add a concurrency limit to merkledag fetch graph License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@9eb1a21c591ef08a4113dac9f756e39ca7cc6c5d --- ipld/merkledag/merkledag.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index dce41f516..50ea34438 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -449,6 +449,10 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, c *cid.Cid, visi } } +// FetchGraphConcurrency is total number of concurrenct fetches that +// 'fetchNodes' will start at a time +var FetchGraphConcurrency = 8 + func fetchNodes(ctx context.Context, ds DAGService, in <-chan []*cid.Cid, out chan<- *NodeOption) { var wg sync.WaitGroup defer func() { @@ -458,8 +462,13 @@ func fetchNodes(ctx context.Context, ds DAGService, in <-chan []*cid.Cid, out ch close(out) }() + rateLimit := make(chan struct{}, FetchGraphConcurrency) + get := func(ks []*cid.Cid) { defer wg.Done() + defer func() { + <-rateLimit + }() nodes := ds.GetMany(ctx, ks) for opt := range nodes { select { @@ -471,6 +480,11 @@ func fetchNodes(ctx context.Context, ds DAGService, in <-chan []*cid.Cid, out ch } for ks := range in { + select { + case rateLimit <- struct{}{}: + case <-ctx.Done(): + return + } wg.Add(1) go get(ks) } From d73ec21cf71c8e64e767f5b452d6e3f3bd7afe31 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 13 Dec 2016 18:51:54 +0100 Subject: [PATCH 1689/3817] namesys: add entry to DHT cache after publish License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@8999061a8c2ed44c0869e067fbd8522a9b467c19 --- namesys/namesys.go | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index d2f4a3bf7..271aaee89 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -1,13 +1,15 @@ package namesys import ( + "context" "strings" "time" - context "context" path "github.com/ipfs/go-ipfs/path" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" + peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) @@ -87,9 +89,44 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) // Publish implements Publisher func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error { - return ns.publishers["/ipns/"].Publish(ctx, name, value) + err := ns.publishers["/ipns/"].Publish(ctx, name, value) + if err != nil { + return err + } + ns.addToDHTCache(name, value, time.Now().Add(time.Hour*24)) + return nil +} + +func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error { + err := ns.publishers["/ipns/"].PublishWithEOL(ctx, name, value, eol) + if err != nil { + return err + } + ns.addToDHTCache(name, value, eol) + return nil } -func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, val path.Path, eol time.Time) error { - return ns.publishers["/ipns/"].PublishWithEOL(ctx, name, val, eol) +func (ns *mpns) addToDHTCache(key ci.PrivKey, value path.Path, eol time.Time) { + var err error + value, err = path.ParsePath(value.String()) + if err != nil { + log.Error("could not parse path") + return + } + + name, err := peer.IDFromPrivateKey(key) + if err != nil { + log.Error("while adding to cache, could not get peerid from private key") + return + } + + rr, ok := ns.resolvers["dht"].(*routingResolver) + if !ok { + // should never happen, purely for sanity + log.Panicf("unexpected type %T as DHT resolver.", ns.resolvers["dht"]) + } + rr.cache.Add(name.Pretty(), cacheEntry{ + val: value, + eol: eol, + }) } From 07735e48c505d1c8b867c97818169e5f57cf1461 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 13 Dec 2016 20:58:10 +0100 Subject: [PATCH 1690/3817] namesys: fix length of self resolve cache License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@5bcfdda9220461bd774dd506dc60cd69bcb9ee23 --- namesys/namesys.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/namesys/namesys.go b/namesys/namesys.go index 271aaee89..3fa72c332 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -125,6 +125,9 @@ func (ns *mpns) addToDHTCache(key ci.PrivKey, value path.Path, eol time.Time) { // should never happen, purely for sanity log.Panicf("unexpected type %T as DHT resolver.", ns.resolvers["dht"]) } + if time.Now().Add(DefaultResolverCacheTTL).Before(eol) { + eol = time.Now().Add(DefaultResolverCacheTTL) + } rr.cache.Add(name.Pretty(), cacheEntry{ val: value, eol: eol, From 0851cb8da61f0136361fb74496d2e756fcd94171 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 14 Dec 2016 20:50:04 +0100 Subject: [PATCH 1691/3817] namesys: extract DefaultRecortTTL to a variable License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@412670d5e5649fb228b32242f6e367b4083c23f6 --- namesys/namesys.go | 2 +- namesys/publisher.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 3fa72c332..8acc1dc2c 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -93,7 +93,7 @@ func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) e if err != nil { return err } - ns.addToDHTCache(name, value, time.Now().Add(time.Hour*24)) + ns.addToDHTCache(name, value, time.Now().Add(DefaultRecortTTL)) return nil } diff --git a/namesys/publisher.go b/namesys/publisher.go index 795023c83..5f5a15abd 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -33,6 +33,7 @@ var ErrExpiredRecord = errors.New("expired record") var ErrUnrecognizedValidity = errors.New("unrecognized validity type") var PublishPutValTimeout = time.Minute +var DefaultRecortTTL = 24 * time.Hour // ipnsPublisher is capable of publishing and resolving names to the IPFS // routing system. @@ -53,7 +54,7 @@ func NewRoutingPublisher(route routing.ValueStore, ds ds.Datastore) *ipnsPublish // and publishes it out to the routing system func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error { log.Debugf("Publish %s", value) - return p.PublishWithEOL(ctx, k, value, time.Now().Add(time.Hour*24)) + return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecortTTL)) } // PublishWithEOL is a temporary stand in for the ipns records implementation From 2ed19f93609eb1c073658f1e5c5ed0db944a2c25 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 14 Dec 2016 21:53:10 +0100 Subject: [PATCH 1692/3817] namesys: fix TYPO, make constant constant License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@5958b93e9fba009cc19117665628e9d6744ac644 --- namesys/namesys.go | 2 +- namesys/publisher.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 8acc1dc2c..3e0456ce2 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -93,7 +93,7 @@ func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) e if err != nil { return err } - ns.addToDHTCache(name, value, time.Now().Add(DefaultRecortTTL)) + ns.addToDHTCache(name, value, time.Now().Add(DefaultRecordTTL)) return nil } diff --git a/namesys/publisher.go b/namesys/publisher.go index 5f5a15abd..54a0e834e 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -32,8 +32,8 @@ var ErrExpiredRecord = errors.New("expired record") // unknown validity type. var ErrUnrecognizedValidity = errors.New("unrecognized validity type") -var PublishPutValTimeout = time.Minute -var DefaultRecortTTL = 24 * time.Hour +const PublishPutValTimeout = time.Minute +const DefaultRecordTTL = 24 * time.Hour // ipnsPublisher is capable of publishing and resolving names to the IPFS // routing system. @@ -54,7 +54,7 @@ func NewRoutingPublisher(route routing.ValueStore, ds ds.Datastore) *ipnsPublish // and publishes it out to the routing system func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error { log.Debugf("Publish %s", value) - return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecortTTL)) + return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordTTL)) } // PublishWithEOL is a temporary stand in for the ipns records implementation From 16de07e7d50bb66ac1230ee85e4e27fb0d1ccb62 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 13 Dec 2016 10:47:14 -0800 Subject: [PATCH 1693/3817] merkledag: fix json marshalling of pbnode License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@eddf3ec3e9d37ab369af5ea63be84e3a4e03dec8 --- ipld/merkledag/node.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 38a47382b..0b2490eeb 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -2,6 +2,7 @@ package merkledag import ( "context" + "encoding/json" "fmt" node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" @@ -228,6 +229,15 @@ func (n *ProtoNode) Loggable() map[string]interface{} { } } +func (n *ProtoNode) MarshalJSON() ([]byte, error) { + out := map[string]interface{}{ + "data": n.data, + "links": n.links, + } + + return json.Marshal(out) +} + func (n *ProtoNode) Cid() *cid.Cid { if n.encoded != nil && n.cached != nil { return n.cached From ad4579b629bdb84c1f244274bb1fb81fc73d8d46 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 14 Dec 2016 16:11:07 -0800 Subject: [PATCH 1694/3817] Add json unmarshal code and fix panic A panic would occur when a link was created with a nil cid, this should be allowable, just catch the potential problem and skip marshaling the cid. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@6b350ac34daee7f96aa6eda79c552b7132f73f1a --- ipld/merkledag/coding.go | 4 +++- ipld/merkledag/node.go | 16 ++++++++++++++++ ipld/merkledag/node_test.go | 30 ++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index e538e519c..e76af12c7 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -60,7 +60,9 @@ func (n *ProtoNode) getPBNode() *pb.PBNode { pbn.Links[i] = &pb.PBLink{} pbn.Links[i].Name = &l.Name pbn.Links[i].Tsize = &l.Size - pbn.Links[i].Hash = l.Cid.Bytes() + if l.Cid != nil { + pbn.Links[i].Hash = l.Cid.Bytes() + } } if len(n.data) > 0 { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 0b2490eeb..4f0d72bc9 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -229,6 +229,22 @@ func (n *ProtoNode) Loggable() map[string]interface{} { } } +func (n *ProtoNode) UnmarshalJSON(b []byte) error { + s := struct { + Data []byte `json:"data"` + Links []*node.Link `json:"links"` + }{} + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + n.data = s.Data + n.links = s.Links + return nil +} + func (n *ProtoNode) MarshalJSON() ([]byte, error) { out := map[string]interface{}{ "data": n.data, diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index beec7ba65..63f0473ba 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -1,6 +1,7 @@ package merkledag_test import ( + "bytes" "context" "testing" @@ -128,3 +129,32 @@ func TestNodeCopy(t *testing.T) { t.Fatal("should be different objects") } } + +func TestJsonRoundtrip(t *testing.T) { + nd := new(ProtoNode) + nd.SetLinks([]*node.Link{ + {Name: "a"}, + {Name: "c"}, + {Name: "b"}, + }) + nd.SetData([]byte("testing")) + + jb, err := nd.MarshalJSON() + if err != nil { + t.Fatal(err) + } + + nn := new(ProtoNode) + err = nn.UnmarshalJSON(jb) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(nn.Data(), nd.Data()) { + t.Fatal("data wasnt the same") + } + + if !nn.Cid().Equals(nd.Cid()) { + t.Fatal("objects differed after marshaling") + } +} From d3de2aeda32ed324697051d34ad016589c139acb Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 15 Dec 2016 02:16:40 +0100 Subject: [PATCH 1695/3817] namesys: fix case where there is no cache License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@1826eb9b65de1b4614e5d8cd33a44b6f781eb286 --- namesys/namesys.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 3e0456ce2..bf1c68967 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -107,6 +107,16 @@ func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path. } func (ns *mpns) addToDHTCache(key ci.PrivKey, value path.Path, eol time.Time) { + rr, ok := ns.resolvers["dht"].(*routingResolver) + if !ok { + // should never happen, purely for sanity + log.Panicf("unexpected type %T as DHT resolver.", ns.resolvers["dht"]) + } + if rr.cache == nil { + // resolver has no caching + return + } + var err error value, err = path.ParsePath(value.String()) if err != nil { @@ -120,11 +130,6 @@ func (ns *mpns) addToDHTCache(key ci.PrivKey, value path.Path, eol time.Time) { return } - rr, ok := ns.resolvers["dht"].(*routingResolver) - if !ok { - // should never happen, purely for sanity - log.Panicf("unexpected type %T as DHT resolver.", ns.resolvers["dht"]) - } if time.Now().Add(DefaultResolverCacheTTL).Before(eol) { eol = time.Now().Add(DefaultResolverCacheTTL) } From f8bb6be911145d6d2e0e449711c4b4797d07d6a9 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 15 Dec 2016 22:05:23 +0100 Subject: [PATCH 1696/3817] namesys: add test for publish with cache size 0 License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@cf5666c0f802ec61727cf21bd1308278f49dbf6f --- namesys/namesys_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index b2f92deb0..7d5c637b5 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,6 +7,11 @@ import ( context "context" path "github.com/ipfs/go-ipfs/path" + offroute "github.com/ipfs/go-ipfs/routing/offline" + "github.com/ipfs/go-ipfs/unixfs" + + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) type mockResolver struct { @@ -69,3 +74,19 @@ func TestNamesysResolution(t *testing.T) { testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 2, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", ErrResolveRecursion) testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 3, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", ErrResolveRecursion) } + +func TestPublishWithCache0(t *testing.T) { + dst := ds.NewMapDatastore() + priv, _, err := ci.GenerateKeyPair(ci.RSA, 1024) + if err != nil { + t.Fatal(err) + } + routing := offroute.NewOfflineRouter(dst, priv) + + nsys := NewNameSystem(routing, dst, 0) + p, err := path.ParsePath(unixfs.EmptyDirNode().Cid().String()) + if err != nil { + t.Fatal(err) + } + nsys.Publish(context.Background(), priv, p) +} From 3edcfea3e1b218eb523dc36f15e90811dcf6c385 Mon Sep 17 00:00:00 2001 From: Zander Mackie Date: Tue, 13 Dec 2016 07:46:29 -0500 Subject: [PATCH 1697/3817] Tests for OfflineRouting storage and Retrieval License: MIT Signed-off-by: Zander Mackie This commit was moved from ipfs/go-ipfs-routing@eac4ce0474c2e15c49cc4d5640abfc67e032e68b --- routing/offline/offline_test.go | 48 +++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 routing/offline/offline_test.go diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go new file mode 100644 index 000000000..5f031a14a --- /dev/null +++ b/routing/offline/offline_test.go @@ -0,0 +1,48 @@ +package offline + +import ( + "bytes" + "context" + "github.com/ipfs/go-ipfs/thirdparty/testutil" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + "testing" +) + +func TestOfflineRouterStorage(t *testing.T) { + ctx := context.Background() + + nds := ds.NewMapDatastore() + privkey, _, _ := testutil.RandTestKeyPair(128) + offline := NewOfflineRouter(nds, privkey) + + err := offline.PutValue(ctx, "key", []byte("testing 1 2 3")) + if err != nil { + t.Fatal(err) + } + + val, err := offline.GetValue(ctx, "key") + if !bytes.Equal([]byte("testing 1 2 3"), val) { + t.Fatal("OfflineRouter does not properly store") + } + + val, err = offline.GetValue(ctx, "notHere") + if err == nil { + t.Fatal("Router should throw errors for unfound records") + } + + recVal, err := offline.GetValues(ctx, "key", 0) + if err != nil { + t.Fatal(err) + } + + _, err = offline.GetValues(ctx, "notHere", 0) + if err == nil { + t.Fatal("Router should throw errors for unfound records") + } + + local := recVal[0].Val + if !bytes.Equal([]byte("testing 1 2 3"), local) { + t.Fatal("OfflineRouter does not properly store") + } +} + From 430ae6b33ea33152ae50c93418e0d525bf2faaee Mon Sep 17 00:00:00 2001 From: Zander Mackie Date: Wed, 14 Dec 2016 07:25:21 -0500 Subject: [PATCH 1698/3817] Testing the rest of the interface License: MIT Signed-off-by: Zander Mackie This commit was moved from ipfs/go-ipfs-routing@3ca260628ecff7d727dcf2781f89ae1629a9ba47 --- routing/offline/offline_test.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index 5f031a14a..629206b4e 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -46,3 +46,34 @@ func TestOfflineRouterStorage(t *testing.T) { } } +func TestOfflineRouterLocal(t *testing.T) { + ctx := context.Background() + + nds := ds.NewMapDatastore() + privkey, _, _ := testutil.RandTestKeyPair(128) + offline := NewOfflineRouter(nds, privkey) + + id, _ := testutil.RandPeerID() + _, err := offline.FindPeer(ctx, id) + if err != ErrOffline { + t.Fatal("OfflineRouting should alert that its offline") + } + + cid, _ := testutil.RandCidV0() + pChan := offline.FindProvidersAsync(ctx, cid, 1) + p, ok := <-pChan + if ok { + t.Fatalf("FindProvidersAsync did not return a closed channel. Instead we got %+v !", p) + } + + cid, _ = testutil.RandCidV0() + err = offline.Provide(ctx, cid) + if err != ErrOffline { + t.Fatal("OfflineRouting should alert that its offline") + } + + err = offline.Bootstrap(ctx) + if err != nil { + t.Fatal("You shouldn't be able to bootstrap offline routing.") + } +} From bf3a8a92ce2575f6672ffd5f889b0ee084b2981a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 27 Dec 2016 02:13:59 -0800 Subject: [PATCH 1699/3817] update libp2p for identify configuration updates License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@27a18f150e430b4364fa5596b3bc3b3cbd9150db --- routing/mock/dht.go | 4 ++-- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index fa2b666d2..925142973 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - dht "gx/ipfs/QmNQPjpcXrwwwgDErKzKUm2xxhXCB3cuFgTHsrcCJ5uGbu/go-libp2p-kad-dht" + mocknet "gx/ipfs/QmQHmMFyhfp2ZXnbYWqAWhEideDCNDM6hzJwqCU29Y5zV2/go-libp2p/p2p/net/mock" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - mocknet "gx/ipfs/QmbzCT1CwxVZ2ednptC9RavuJe7Bv8DDi2Ne89qUrA37XM/go-libp2p/p2p/net/mock" + dht "gx/ipfs/QmYTccce26rGtEbE7SpnSeRcJkT4uqa7aPyzRXufisiTEd/go-libp2p-kad-dht" ) type mocknetserver struct { diff --git a/routing/supernode/client.go b/routing/supernode/client.go index fe642d262..45bab6121 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,10 +8,10 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - dhtpb "gx/ipfs/QmNQPjpcXrwwwgDErKzKUm2xxhXCB3cuFgTHsrcCJ5uGbu/go-libp2p-kad-dht/pb" "gx/ipfs/QmPTGbC34bPKaUm9wTxBo7zSCac7pDuG42ZmnXC718CKZZ/go-libp2p-host" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" + dhtpb "gx/ipfs/QmYTccce26rGtEbE7SpnSeRcJkT4uqa7aPyzRXufisiTEd/go-libp2p-kad-dht/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index da73fee7f..56bb99147 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,8 +2,8 @@ package proxy import ( context "context" - dhtpb "gx/ipfs/QmNQPjpcXrwwwgDErKzKUm2xxhXCB3cuFgTHsrcCJ5uGbu/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmQx1dHDDYENugYgqA22BaBrRfuv1coSsuPiM7rYh1wwGH/go-libp2p-net" + dhtpb "gx/ipfs/QmYTccce26rGtEbE7SpnSeRcJkT4uqa7aPyzRXufisiTEd/go-libp2p-kad-dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index fa619ff32..73e4f420e 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,12 +4,12 @@ import ( "context" "errors" - dhtpb "gx/ipfs/QmNQPjpcXrwwwgDErKzKUm2xxhXCB3cuFgTHsrcCJ5uGbu/go-libp2p-kad-dht/pb" host "gx/ipfs/QmPTGbC34bPKaUm9wTxBo7zSCac7pDuG42ZmnXC718CKZZ/go-libp2p-host" inet "gx/ipfs/QmQx1dHDDYENugYgqA22BaBrRfuv1coSsuPiM7rYh1wwGH/go-libp2p-net" kbucket "gx/ipfs/QmRVHVr38ChANF2PUMNKQs7Q4uVWCLVabrfcTG9taNbcVy/go-libp2p-kbucket" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" + dhtpb "gx/ipfs/QmYTccce26rGtEbE7SpnSeRcJkT4uqa7aPyzRXufisiTEd/go-libp2p-kad-dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index b97e613a5..01d72563a 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,8 +8,8 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - dhtpb "gx/ipfs/QmNQPjpcXrwwwgDErKzKUm2xxhXCB3cuFgTHsrcCJ5uGbu/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dhtpb "gx/ipfs/QmYTccce26rGtEbE7SpnSeRcJkT4uqa7aPyzRXufisiTEd/go-libp2p-kad-dht/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 3f65808c9..7715e98fb 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmNQPjpcXrwwwgDErKzKUm2xxhXCB3cuFgTHsrcCJ5uGbu/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dhtpb "gx/ipfs/QmYTccce26rGtEbE7SpnSeRcJkT4uqa7aPyzRXufisiTEd/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 6678d2a4d9a986f186f4720c456d59efaaa0e254 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 27 Dec 2016 02:13:59 -0800 Subject: [PATCH 1700/3817] update libp2p for identify configuration updates License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@ac0e3c59259c2484c868f6068e6de2c1f033e99d --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index ff456593d..3ed2eee59 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,7 +13,7 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmbzCT1CwxVZ2ednptC9RavuJe7Bv8DDi2Ne89qUrA37XM/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmQHmMFyhfp2ZXnbYWqAWhEideDCNDM6hzJwqCU29Y5zV2/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" ) From 3fb616239ffbc6000dd1e1783ff32829e88b7afe Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 4 Jan 2017 16:01:23 -0500 Subject: [PATCH 1701/3817] Fix typo and formatting issues. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-unixfs@4df138770eea3a1f003619a04b49669bedfdc026 --- unixfs/format_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/format_test.go b/unixfs/format_test.go index 10421431a..6edc2ca0b 100644 --- a/unixfs/format_test.go +++ b/unixfs/format_test.go @@ -132,7 +132,7 @@ func TestPBdataTools(t *testing.T) { } -func TestMetedata(t *testing.T) { +func TestMetadata(t *testing.T) { meta := &Metadata{ MimeType: "audio/aiff", Size: 12345, From bbe517e90d6282bb2e424ffe92a59488e41b0313 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 10 Jan 2017 17:12:21 +0100 Subject: [PATCH 1702/3817] test: add test for dag service doing short circuit for raw.Links() License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-merkledag@495577645d5929c97fb44c8b04a33d46b483e967 --- ipld/merkledag/merkledag_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 3fa45d8ac..4f793eae8 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -10,6 +10,7 @@ import ( "strings" "sync" "testing" + "time" blocks "github.com/ipfs/go-ipfs/blocks" bserv "github.com/ipfs/go-ipfs/blockservice" @@ -485,3 +486,21 @@ func TestCidRetention(t *testing.T) { t.Fatal("output cid didnt match") } } + +func TestCidRawDoesnNeedData(t *testing.T) { + srv := NewDAGService(dstest.Bserv()) + nd := NewRawNode([]byte("somedata")) + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // there is no data for this node in the blockservice + // so dag service can't load it + links, err := srv.GetLinks(ctx, nd.Cid()) + if err != nil { + t.Fatal(err) + } + if len(links) != 0 { + t.Fatal("raw node shouldn't have any links") + } +} From 0d2240b544b571a94a3526aeea81aff101c5c550 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 10 Jan 2017 05:56:28 -0800 Subject: [PATCH 1703/3817] update go-libp2p with negotiate lazy fixes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@3a15ceb9527de07e55ea750c1696a261124aab83 --- routing/mock/dht.go | 4 ++-- routing/none/none_client.go | 2 +- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 4 ++-- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 925142973..c307410ab 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmQHmMFyhfp2ZXnbYWqAWhEideDCNDM6hzJwqCU29Y5zV2/go-libp2p/p2p/net/mock" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - dht "gx/ipfs/QmYTccce26rGtEbE7SpnSeRcJkT4uqa7aPyzRXufisiTEd/go-libp2p-kad-dht" + dht "gx/ipfs/QmZbinR1CdVPaoom5vgD5YC5c1oeCPJqYhoGJFXoA32GKn/go-libp2p-kad-dht" + mocknet "gx/ipfs/QmdzDdLZ7nj133QvNHypyS9Y39g35bMFk5DJ2pmX7YqtKU/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/none/none_client.go b/routing/none/none_client.go index cd6ceec03..2d0bba3a4 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,7 +6,7 @@ import ( repo "github.com/ipfs/go-ipfs/repo" - p2phost "gx/ipfs/QmPTGbC34bPKaUm9wTxBo7zSCac7pDuG42ZmnXC718CKZZ/go-libp2p-host" + p2phost "gx/ipfs/QmPsRtodRuBUir32nz5v4zuSBTSszrR1d3fA6Ahb6eaejj/go-libp2p-host" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 45bab6121..67f27c396 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,11 +8,11 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - "gx/ipfs/QmPTGbC34bPKaUm9wTxBo7zSCac7pDuG42ZmnXC718CKZZ/go-libp2p-host" + "gx/ipfs/QmPsRtodRuBUir32nz5v4zuSBTSszrR1d3fA6Ahb6eaejj/go-libp2p-host" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" - dhtpb "gx/ipfs/QmYTccce26rGtEbE7SpnSeRcJkT4uqa7aPyzRXufisiTEd/go-libp2p-kad-dht/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + dhtpb "gx/ipfs/QmZbinR1CdVPaoom5vgD5YC5c1oeCPJqYhoGJFXoA32GKn/go-libp2p-kad-dht/pb" routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 56bb99147..71ff52f00 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -3,8 +3,8 @@ package proxy import ( context "context" inet "gx/ipfs/QmQx1dHDDYENugYgqA22BaBrRfuv1coSsuPiM7rYh1wwGH/go-libp2p-net" - dhtpb "gx/ipfs/QmYTccce26rGtEbE7SpnSeRcJkT4uqa7aPyzRXufisiTEd/go-libp2p-kad-dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" + dhtpb "gx/ipfs/QmZbinR1CdVPaoom5vgD5YC5c1oeCPJqYhoGJFXoA32GKn/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 73e4f420e..f6e37a412 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,13 +4,13 @@ import ( "context" "errors" - host "gx/ipfs/QmPTGbC34bPKaUm9wTxBo7zSCac7pDuG42ZmnXC718CKZZ/go-libp2p-host" + host "gx/ipfs/QmPsRtodRuBUir32nz5v4zuSBTSszrR1d3fA6Ahb6eaejj/go-libp2p-host" inet "gx/ipfs/QmQx1dHDDYENugYgqA22BaBrRfuv1coSsuPiM7rYh1wwGH/go-libp2p-net" kbucket "gx/ipfs/QmRVHVr38ChANF2PUMNKQs7Q4uVWCLVabrfcTG9taNbcVy/go-libp2p-kbucket" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" - dhtpb "gx/ipfs/QmYTccce26rGtEbE7SpnSeRcJkT4uqa7aPyzRXufisiTEd/go-libp2p-kad-dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" + dhtpb "gx/ipfs/QmZbinR1CdVPaoom5vgD5YC5c1oeCPJqYhoGJFXoA32GKn/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 01d72563a..616b9f1de 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -9,8 +9,8 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dhtpb "gx/ipfs/QmYTccce26rGtEbE7SpnSeRcJkT4uqa7aPyzRXufisiTEd/go-libp2p-kad-dht/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + dhtpb "gx/ipfs/QmZbinR1CdVPaoom5vgD5YC5c1oeCPJqYhoGJFXoA32GKn/go-libp2p-kad-dht/pb" record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 7715e98fb..421b04e84 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -4,7 +4,7 @@ import ( "testing" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dhtpb "gx/ipfs/QmYTccce26rGtEbE7SpnSeRcJkT4uqa7aPyzRXufisiTEd/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmZbinR1CdVPaoom5vgD5YC5c1oeCPJqYhoGJFXoA32GKn/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 547158a304fa7e1b2dd27f31951405533a56e3d6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 10 Jan 2017 05:56:28 -0800 Subject: [PATCH 1704/3817] update go-libp2p with negotiate lazy fixes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@b849ce7af81ee96ca83ef363b747924aab0e86ce --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 3ed2eee59..65a2df82f 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,7 +13,7 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmQHmMFyhfp2ZXnbYWqAWhEideDCNDM6hzJwqCU29Y5zV2/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmdzDdLZ7nj133QvNHypyS9Y39g35bMFk5DJ2pmX7YqtKU/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" ) From 3bfc79e3ecf9120244b52c67f13ffd3f177ea949 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 7 Jan 2017 05:46:17 -0800 Subject: [PATCH 1705/3817] rewrite enumerate children async to be less fragile License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@9eb769d7707801dc205bf1799548fd1afa2b55ab --- ipld/merkledag/merkledag.go | 143 ++++++++++++++----------------- ipld/merkledag/merkledag_test.go | 43 ++++++++++ 2 files changed, 109 insertions(+), 77 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 50ea34438..5ab7daebe 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -389,103 +389,92 @@ func EnumerateChildren(ctx context.Context, ds LinkService, root *cid.Cid, visit return nil } -func EnumerateChildrenAsync(ctx context.Context, ds DAGService, c *cid.Cid, visit func(*cid.Cid) bool) error { - toprocess := make(chan []*cid.Cid, 8) - nodes := make(chan *NodeOption, 8) - - ctx, cancel := context.WithCancel(ctx) - defer cancel() - defer close(toprocess) +// FetchGraphConcurrency is total number of concurrent fetches that +// 'fetchNodes' will start at a time +var FetchGraphConcurrency = 8 - go fetchNodes(ctx, ds, toprocess, nodes) +func EnumerateChildrenAsync(ctx context.Context, ds DAGService, c *cid.Cid, visit func(*cid.Cid) bool) error { + if !visit(c) { + return nil + } root, err := ds.Get(ctx, c) if err != nil { return err } - nodes <- &NodeOption{Node: root} - live := 1 - - for { - select { - case opt, ok := <-nodes: - if !ok { - return nil - } - - if opt.Err != nil { - return opt.Err - } - - nd := opt.Node - - // a node has been fetched - live-- - - var cids []*cid.Cid - for _, lnk := range nd.Links() { - c := lnk.Cid - if visit(c) { - live++ - cids = append(cids, c) + feed := make(chan node.Node) + out := make(chan *NodeOption) + done := make(chan struct{}) + + var setlk sync.Mutex + + for i := 0; i < FetchGraphConcurrency; i++ { + go func() { + for n := range feed { + links := n.Links() + cids := make([]*cid.Cid, 0, len(links)) + for _, l := range links { + setlk.Lock() + unseen := visit(l.Cid) + setlk.Unlock() + if unseen { + cids = append(cids, l.Cid) + } } - } - - if live == 0 { - return nil - } - if len(cids) > 0 { + for nopt := range ds.GetMany(ctx, cids) { + select { + case out <- nopt: + case <-ctx.Done(): + return + } + } select { - case toprocess <- cids: + case done <- struct{}{}: case <-ctx.Done(): - return ctx.Err() } } - case <-ctx.Done(): - return ctx.Err() - } + }() } -} + defer close(feed) -// FetchGraphConcurrency is total number of concurrenct fetches that -// 'fetchNodes' will start at a time -var FetchGraphConcurrency = 8 - -func fetchNodes(ctx context.Context, ds DAGService, in <-chan []*cid.Cid, out chan<- *NodeOption) { - var wg sync.WaitGroup - defer func() { - // wait for all 'get' calls to complete so we don't accidentally send - // on a closed channel - wg.Wait() - close(out) - }() + send := feed + var todobuffer []node.Node + var inProgress int - rateLimit := make(chan struct{}, FetchGraphConcurrency) + next := root + for { + select { + case send <- next: + inProgress++ + if len(todobuffer) > 0 { + next = todobuffer[0] + todobuffer = todobuffer[1:] + } else { + next = nil + send = nil + } + case <-done: + inProgress-- + if inProgress == 0 && next == nil { + return nil + } + case nc := <-out: + if nc.Err != nil { + return nc.Err + } - get := func(ks []*cid.Cid) { - defer wg.Done() - defer func() { - <-rateLimit - }() - nodes := ds.GetMany(ctx, ks) - for opt := range nodes { - select { - case out <- opt: - case <-ctx.Done(): - return + if next == nil { + next = nc.Node + send = feed + } else { + todobuffer = append(todobuffer, nc.Node) } - } - } - for ks := range in { - select { - case rateLimit <- struct{}{}: case <-ctx.Done(): - return + return ctx.Err() } - wg.Add(1) - go get(ks) } + } diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 4f793eae8..c55a7b551 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -504,3 +504,46 @@ func TestCidRawDoesnNeedData(t *testing.T) { t.Fatal("raw node shouldn't have any links") } } + +func TestEnumerateAsyncFailsNotFound(t *testing.T) { + a := NodeWithData([]byte("foo1")) + b := NodeWithData([]byte("foo2")) + c := NodeWithData([]byte("foo3")) + d := NodeWithData([]byte("foo4")) + + ds := dstest.Mock() + for _, n := range []node.Node{a, b, c} { + _, err := ds.Add(n) + if err != nil { + t.Fatal(err) + } + } + + parent := new(ProtoNode) + if err := parent.AddNodeLinkClean("a", a); err != nil { + t.Fatal(err) + } + + if err := parent.AddNodeLinkClean("b", b); err != nil { + t.Fatal(err) + } + + if err := parent.AddNodeLinkClean("c", c); err != nil { + t.Fatal(err) + } + + if err := parent.AddNodeLinkClean("d", d); err != nil { + t.Fatal(err) + } + + pcid, err := ds.Add(parent) + if err != nil { + t.Fatal(err) + } + + cset := cid.NewSet() + err = EnumerateChildrenAsync(context.Background(), ds, pcid, cset.Visit) + if err == nil { + t.Fatal("this should have failed") + } +} From 55aaaa50857b41ba46b1d8ee9a79bd1d4884e85d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 11 Jan 2017 04:42:39 -0800 Subject: [PATCH 1706/3817] make pinning use serial graph enumeration License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@f0def5957a020f90545564b46c516c884c747440 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 5ab7daebe..f508b950c 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -140,7 +140,7 @@ func (n *dagService) Remove(nd node.Node) error { // FetchGraph fetches all nodes that are children of the given node func FetchGraph(ctx context.Context, c *cid.Cid, serv DAGService) error { - return EnumerateChildrenAsync(ctx, serv, c, cid.NewSet().Visit) + return EnumerateChildren(ctx, serv, c, cid.NewSet().Visit, false) } // FindLinks searches this nodes links for the given key, From e3aee1cd33fd62aacc14d609123f069da2691b0b Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 11 Jan 2017 18:24:11 -0500 Subject: [PATCH 1707/3817] blockservice: avoid using unnecessary continue statement License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-blockservice@53eb10a43657ccc3e01906c31a373fc8f14e265d --- blockservice/blockservice.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index d05a01355..e109b85fe 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -112,10 +112,9 @@ func (s *blockService) AddBlocks(bs []blocks.Block) ([]*cid.Cid, error) { if err != nil { return nil, err } - if has { - continue + if !has { + toput = append(toput, b) } - toput = append(toput, b) } } else { toput = bs From 171885cdd64d301bd83d8159b474d1da09cd4c87 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 18 Jan 2017 19:39:57 -0800 Subject: [PATCH 1708/3817] update dht code to drop error log to warning License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@8a0870c29dac01a9712f36e89cb22edc4cebcee2 --- routing/mock/dht.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index c307410ab..707ea0271 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,9 +3,9 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" + dht "gx/ipfs/QmRG9fdibExi5DFy8kzyxF76jvZVUb2mQBUSMNP1YaYn9M/go-libp2p-kad-dht" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - dht "gx/ipfs/QmZbinR1CdVPaoom5vgD5YC5c1oeCPJqYhoGJFXoA32GKn/go-libp2p-kad-dht" mocknet "gx/ipfs/QmdzDdLZ7nj133QvNHypyS9Y39g35bMFk5DJ2pmX7YqtKU/go-libp2p/p2p/net/mock" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 67f27c396..5f4822425 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -9,10 +9,10 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" "gx/ipfs/QmPsRtodRuBUir32nz5v4zuSBTSszrR1d3fA6Ahb6eaejj/go-libp2p-host" + dhtpb "gx/ipfs/QmRG9fdibExi5DFy8kzyxF76jvZVUb2mQBUSMNP1YaYn9M/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/QmZbinR1CdVPaoom5vgD5YC5c1oeCPJqYhoGJFXoA32GKn/go-libp2p-kad-dht/pb" routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 71ff52f00..ab617b15a 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -3,8 +3,8 @@ package proxy import ( context "context" inet "gx/ipfs/QmQx1dHDDYENugYgqA22BaBrRfuv1coSsuPiM7rYh1wwGH/go-libp2p-net" + dhtpb "gx/ipfs/QmRG9fdibExi5DFy8kzyxF76jvZVUb2mQBUSMNP1YaYn9M/go-libp2p-kad-dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/QmZbinR1CdVPaoom5vgD5YC5c1oeCPJqYhoGJFXoA32GKn/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index f6e37a412..0612fefe4 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -6,11 +6,11 @@ import ( host "gx/ipfs/QmPsRtodRuBUir32nz5v4zuSBTSszrR1d3fA6Ahb6eaejj/go-libp2p-host" inet "gx/ipfs/QmQx1dHDDYENugYgqA22BaBrRfuv1coSsuPiM7rYh1wwGH/go-libp2p-net" + dhtpb "gx/ipfs/QmRG9fdibExi5DFy8kzyxF76jvZVUb2mQBUSMNP1YaYn9M/go-libp2p-kad-dht/pb" kbucket "gx/ipfs/QmRVHVr38ChANF2PUMNKQs7Q4uVWCLVabrfcTG9taNbcVy/go-libp2p-kbucket" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/QmZbinR1CdVPaoom5vgD5YC5c1oeCPJqYhoGJFXoA32GKn/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 616b9f1de..79c98d898 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,9 +8,9 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + dhtpb "gx/ipfs/QmRG9fdibExi5DFy8kzyxF76jvZVUb2mQBUSMNP1YaYn9M/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/QmZbinR1CdVPaoom5vgD5YC5c1oeCPJqYhoGJFXoA32GKn/go-libp2p-kad-dht/pb" record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 421b04e84..b802700e2 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" + dhtpb "gx/ipfs/QmRG9fdibExi5DFy8kzyxF76jvZVUb2mQBUSMNP1YaYn9M/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dhtpb "gx/ipfs/QmZbinR1CdVPaoom5vgD5YC5c1oeCPJqYhoGJFXoA32GKn/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From dfbd8bd2427cca2383494dce67c99c6f25dc70c7 Mon Sep 17 00:00:00 2001 From: Zander Mackie Date: Fri, 20 Jan 2017 16:32:15 -0500 Subject: [PATCH 1709/3817] Remove deprecated 'FindProviders' method from mock License: MIT Signed-off-by: Zander Mackie This commit was moved from ipfs/go-ipfs-routing@8451a895dbec88fe1e003c450ae4574b0bc76e39 --- routing/mock/centralized_test.go | 16 ++++++++-------- routing/mock/interface.go | 3 --- routing/offline/offline.go | 4 ---- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 13c3708d6..5aca0b089 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -7,7 +7,6 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" @@ -154,20 +153,21 @@ func TestValidAfter(t *testing.T) { rs.Client(pi).Provide(ctx, key) var providers []pstore.PeerInfo - providers, err := rs.Client(pi).FindProviders(ctx, key) - if err != nil { - t.Fatal(err) + max := 100 + providersChan := rs.Client(pi).FindProvidersAsync(ctx, key, max) + for p := range providersChan { + providers = append(providers, p) } if len(providers) > 0 { t.Fail() } conf.ValueVisibility.Set(0) - providers, err = rs.Client(pi).FindProviders(ctx, key) - if err != nil { - t.Fatal(err) - } + providersChan = rs.Client(pi).FindProvidersAsync(ctx, key, max) t.Log("providers", providers) + for p := range providersChan { + providers = append(providers, p) + } if len(providers) != 1 { t.Fail() } diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 28217d600..91dbc1deb 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -12,8 +12,6 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" - pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" ) @@ -25,7 +23,6 @@ type Server interface { // Client implements IpfsRouting type Client interface { - FindProviders(context.Context, *cid.Cid) ([]pstore.PeerInfo, error) routing.IpfsRouting } diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 566466139..7813208ed 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -91,10 +91,6 @@ func (c *offlineRouting) GetValues(ctx context.Context, key string, _ int) ([]ro }, nil } -func (c *offlineRouting) FindProviders(ctx context.Context, key *cid.Cid) ([]pstore.PeerInfo, error) { - return nil, ErrOffline -} - func (c *offlineRouting) FindPeer(ctx context.Context, pid peer.ID) (pstore.PeerInfo, error) { return pstore.PeerInfo{}, ErrOffline } From b4abf94645645c6a0ed67f9a2f1a7c4047099ee4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 20 Jan 2017 11:25:17 -0800 Subject: [PATCH 1710/3817] update to the correct ipld cbor code License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@f11228dcf5a840228c792edd2828063f4f0b880e --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index f508b950c..c5f913fa8 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -13,8 +13,8 @@ import ( node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - ipldcbor "gx/ipfs/QmbuuwTd9x4NReZ7sxtiKk7wFcfDUo54MfWBdtF5MRCPGR/go-ipld-cbor" cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + ipldcbor "gx/ipfs/QmfMxth6d2po8YGrtSVyNb2u6SFNrPdAsWQoZG83oXRBqX/go-ipld-cbor" ) var log = logging.Logger("merkledag") From f8a3b24ca53ab2eeef3fc3adbf539e31278d8cab Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 27 Jan 2017 17:19:58 -0800 Subject: [PATCH 1711/3817] Make pinset sharding deterministic Making this deterministic keeps us from creating an exponential amount of objects as the number of pins in the set increases. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@ccb8151bcc998e7f9aa8f0eb81da4c86ec1379c1 --- pinning/pinner/set.go | 23 +++----------- pinning/pinner/set_test.go | 65 +++++++++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 33 deletions(-) diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 89791b1b6..01e0e198b 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -3,7 +3,6 @@ package pin import ( "bytes" "context" - "crypto/rand" "encoding/binary" "errors" "fmt" @@ -26,14 +25,6 @@ const ( maxItems = 8192 ) -func randomSeed() (uint32, error) { - var buf [4]byte - if _, err := rand.Read(buf[:]); err != nil { - return 0, err - } - return binary.LittleEndian.Uint32(buf[:]), nil -} - func hash(seed uint32, c *cid.Cid) uint32 { var buf [4]byte binary.LittleEndian.PutUint32(buf[:], seed) @@ -63,11 +54,7 @@ func (s sortByHash) Swap(a, b int) { s.links[a], s.links[b] = s.links[b], s.links[a] } -func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint64, iter itemIterator, internalKeys keyObserver) (*merkledag.ProtoNode, error) { - seed, err := randomSeed() - if err != nil { - return nil, err - } +func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint64, depth uint32, iter itemIterator, internalKeys keyObserver) (*merkledag.ProtoNode, error) { links := make([]*node.Link, 0, defaultFanout+maxItems) for i := 0; i < defaultFanout; i++ { links = append(links, &node.Link{Cid: emptyKey}) @@ -82,7 +69,7 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint hdr := &pb.Set{ Version: proto.Uint32(1), Fanout: proto.Uint32(defaultFanout), - Seed: proto.Uint32(seed), + Seed: proto.Uint32(depth), } if err := writeHdr(n, hdr); err != nil { return nil, err @@ -129,7 +116,7 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint if !ok { break } - h := hash(seed, k) % defaultFanout + h := hash(depth, k) % defaultFanout hashed[h] = append(hashed[h], k) } @@ -142,7 +129,7 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint childIter := getCidListIterator(items) // recursively create a pinset from the items for this bucket index - child, err := storeItems(ctx, dag, uint64(len(items)), childIter, internalKeys) + child, err := storeItems(ctx, dag, uint64(len(items)), depth+1, childIter, internalKeys) if err != nil { return nil, err } @@ -296,7 +283,7 @@ func getCidListIterator(cids []*cid.Cid) itemIterator { func storeSet(ctx context.Context, dag merkledag.DAGService, cids []*cid.Cid, internalKeys keyObserver) (*merkledag.ProtoNode, error) { iter := getCidListIterator(cids) - n, err := storeItems(ctx, dag, uint64(len(cids)), iter, internalKeys) + n, err := storeItems(ctx, dag, uint64(len(cids)), 0, iter, internalKeys) if err != nil { return nil, err } diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index c409fae4b..788af5a46 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -2,40 +2,75 @@ package pin import ( "context" - "fmt" - "os" + "encoding/binary" "testing" + blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" + bserv "github.com/ipfs/go-ipfs/blockservice" + offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - mdtest "github.com/ipfs/go-ipfs/merkledag/test" + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) func ignoreCids(_ *cid.Cid) {} -func TestSet(t *testing.T) { - ds := mdtest.Mock() - limit := 10000 // 10000 reproduces the pinloss issue fairly reliably - - if os.Getenv("STRESS_IT_OUT_YO") != "" { - limit = 10000000 +func objCount(d ds.Datastore) int { + q := dsq.Query{KeysOnly: true} + res, err := d.Query(q) + if err != nil { + panic(err) } - var inputs []*cid.Cid - for i := 0; i < limit; i++ { - c, err := ds.Add(dag.NodeWithData([]byte(fmt.Sprint(i)))) - if err != nil { - t.Fatal(err) + + var count int + for { + _, ok := res.NextSync() + if !ok { + break } + count++ + } + return count +} + +func TestSet(t *testing.T) { + dst := ds.NewMapDatastore() + bstore := blockstore.NewBlockstore(dst) + ds := dag.NewDAGService(bserv.New(bstore, offline.Exchange(bstore))) + + // this value triggers the creation of a recursive shard. + // If the recursive sharding is done improperly, this will result in + // an infinite recursion and crash (OOM) + limit := uint32((defaultFanout * maxItems) + 1) + + var inputs []*cid.Cid + buf := make([]byte, 4) + for i := uint32(0); i < limit; i++ { + binary.BigEndian.PutUint32(buf, i) + c := dag.NewRawNode(buf).Cid() inputs = append(inputs, c) } + _, err := storeSet(context.Background(), ds, inputs[:len(inputs)-1], ignoreCids) + if err != nil { + t.Fatal(err) + } + + objs1 := objCount(dst) + out, err := storeSet(context.Background(), ds, inputs, ignoreCids) if err != nil { t.Fatal(err) } + objs2 := objCount(dst) + if objs2-objs1 > 2 { + t.Fatal("set sharding does not appear to be deterministic") + } + // weird wrapper node because loadSet expects us to pass an // object pointing to multiple named sets setroot := &dag.ProtoNode{} @@ -49,7 +84,7 @@ func TestSet(t *testing.T) { t.Fatal(err) } - if len(outset) != limit { + if uint32(len(outset)) != limit { t.Fatal("got wrong number", len(outset), limit) } From 6e76f9512e6612d0d6191b159b3f885d2552aeef Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 5 Feb 2017 15:36:15 -0800 Subject: [PATCH 1712/3817] dag/get: fix link formatting in json output License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@934356b448dc6c5478205a14d1697553cf84e026 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index c5f913fa8..1083ba099 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -14,7 +14,7 @@ import ( node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" - ipldcbor "gx/ipfs/QmfMxth6d2po8YGrtSVyNb2u6SFNrPdAsWQoZG83oXRBqX/go-ipld-cbor" + ipldcbor "gx/ipfs/Qmf658QLDTXfRDgnGmUB6TYj671XjmHScG61p3g7dSxUcF/go-ipld-cbor" ) var log = logging.Logger("merkledag") From 0784520f3d6b06d7439ba670fafb36bfee8f0a5b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 8 Feb 2017 10:47:02 -0800 Subject: [PATCH 1713/3817] Fix marshaling of null cbor arrays License: MIT Signed-off-by: Jeromy Fix non-canonical imports via dag put License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@4e289537c311d224cf33a09db2baa17d3670b7db --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 1083ba099..ae8d71cfe 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -13,8 +13,8 @@ import ( node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + ipldcbor "gx/ipfs/QmT1B6cKXnMMki8nbuhrnLuiU32HLvwi6xe99bJ79482UK/go-ipld-cbor" cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" - ipldcbor "gx/ipfs/Qmf658QLDTXfRDgnGmUB6TYj671XjmHScG61p3g7dSxUcF/go-ipld-cbor" ) var log = logging.Logger("merkledag") From dcea9a1f86b413c00e5e7dc1d5cd332481260bf9 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 16 Dec 2016 19:04:22 +0100 Subject: [PATCH 1714/3817] make: rework makefiles for non-recursive make and add sharness coverage This commit introduces non-recursive Makefile infrastructure that replaces current Makefile infrastructure. It also generally cleanups the Makefiles, separates them into nicer sub-modules and centralizes common operations into single definitions. It allows to depend on any target that is defined in the makefile, this means that for example `gx install` is called once when `make build test_expensive_sharness` is called instead of 4 or 5 times. It also makes the dependencies much cleaner and allows for reuse of modules. For example sharness coverage collection (WIP) uses sharness target with amended PATH, previously it might have been possible but not without wiring in the coverage collection into sharness make runner code. Yes, it is more complex but not much more. There are few rules that have to be followed and few complexities added but IMHO it is worth it. How to NR-make: 1. If make is to generate some file via a target, it MUST be defined in Rules.mk file in the directory of the target. 2. `Rules.mk` file MUST have `include mk/header.mk` statement as the first line and `include mk/footer.mk` statement as the last line (apart from project root `Rules.mk`). 3. It then MUST be included by the closest `Rules.mk` file up the directory tree. 4. Inside a `Rules.mk` special variable accessed as `$(d)` is defined. Its value is current directory, use it so if the `Rules.mk` file is moved in the tree it still works without a problem. Caution: this variable is not available in the recipe part and MUST NOT be used. Use name of the target or prerequisite to extract it if you need it. 5. Make has only one global scope, this means that name conflicts are a thing. Names SHOULD follow `VAR_NAME_$(d)` convention. There are exceptions from this rule in form of well defined global variables. Examples: General lists `TGT_BIN`, `CLEAN`; General targets: `TEST`, `COVERAGE`; General variables: `GOFLAGS`, `DEPS_GO`. 3. Any rules, definitions or variables that fit some family SHOULD be defined in `mk/$family.mk` file and included from project root `Rules.mk` License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-pinner@2d6a8b8ed1135387fc21a46c7d9a2c6cab738911 --- pinning/pinner/pin.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index baf0d5958..c0eccc203 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -5,7 +5,6 @@ package pin import ( "context" "fmt" - "os" "sync" "time" @@ -26,8 +25,9 @@ var emptyKey *cid.Cid func init() { e, err := cid.Decode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n") if err != nil { - log.Error("failed to decode empty key constant") - os.Exit(1) + msg := "failed to decode empty key constant" + log.Error(msg) + panic(msg) } emptyKey = e } From be14dcd5aec53c3ed061537ca671a17968e37715 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 20 Dec 2016 20:20:23 +0100 Subject: [PATCH 1715/3817] make: revert the panic change in pin License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-pinner@108b07ffe1c62841503881b80f514281faf30f55 --- pinning/pinner/pin.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index c0eccc203..baf0d5958 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -5,6 +5,7 @@ package pin import ( "context" "fmt" + "os" "sync" "time" @@ -25,9 +26,8 @@ var emptyKey *cid.Cid func init() { e, err := cid.Decode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n") if err != nil { - msg := "failed to decode empty key constant" - log.Error(msg) - panic(msg) + log.Error("failed to decode empty key constant") + os.Exit(1) } emptyKey = e } From 367d358916ef270c182f1920474110dc7fcd1018 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 16 Dec 2016 19:04:22 +0100 Subject: [PATCH 1716/3817] make: rework makefiles for non-recursive make and add sharness coverage This commit introduces non-recursive Makefile infrastructure that replaces current Makefile infrastructure. It also generally cleanups the Makefiles, separates them into nicer sub-modules and centralizes common operations into single definitions. It allows to depend on any target that is defined in the makefile, this means that for example `gx install` is called once when `make build test_expensive_sharness` is called instead of 4 or 5 times. It also makes the dependencies much cleaner and allows for reuse of modules. For example sharness coverage collection (WIP) uses sharness target with amended PATH, previously it might have been possible but not without wiring in the coverage collection into sharness make runner code. Yes, it is more complex but not much more. There are few rules that have to be followed and few complexities added but IMHO it is worth it. How to NR-make: 1. If make is to generate some file via a target, it MUST be defined in Rules.mk file in the directory of the target. 2. `Rules.mk` file MUST have `include mk/header.mk` statement as the first line and `include mk/footer.mk` statement as the last line (apart from project root `Rules.mk`). 3. It then MUST be included by the closest `Rules.mk` file up the directory tree. 4. Inside a `Rules.mk` special variable accessed as `$(d)` is defined. Its value is current directory, use it so if the `Rules.mk` file is moved in the tree it still works without a problem. Caution: this variable is not available in the recipe part and MUST NOT be used. Use name of the target or prerequisite to extract it if you need it. 5. Make has only one global scope, this means that name conflicts are a thing. Names SHOULD follow `VAR_NAME_$(d)` convention. There are exceptions from this rule in form of well defined global variables. Examples: General lists `TGT_BIN`, `CLEAN`; General targets: `TEST`, `COVERAGE`; General variables: `GOFLAGS`, `DEPS_GO`. 3. Any rules, definitions or variables that fit some family SHOULD be defined in `mk/$family.mk` file and included from project root `Rules.mk` License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@596a7e2355a198e3c4192073556ee30e06194199 --- namesys/pb/Makefile | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 namesys/pb/Makefile diff --git a/namesys/pb/Makefile b/namesys/pb/Makefile deleted file mode 100644 index 334feee74..000000000 --- a/namesys/pb/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -PB = $(wildcard *.proto) -GO = $(PB:.proto=.pb.go) - -all: $(GO) - -%.pb.go: %.proto - protoc --gogo_out=. --proto_path=../../../../../../:/usr/local/opt/protobuf/include:. $< - -clean: - rm *.pb.go From 0299ba28210b690d3a19a45207aeac4b5bdf6e76 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 16 Dec 2016 19:04:22 +0100 Subject: [PATCH 1717/3817] make: rework makefiles for non-recursive make and add sharness coverage This commit introduces non-recursive Makefile infrastructure that replaces current Makefile infrastructure. It also generally cleanups the Makefiles, separates them into nicer sub-modules and centralizes common operations into single definitions. It allows to depend on any target that is defined in the makefile, this means that for example `gx install` is called once when `make build test_expensive_sharness` is called instead of 4 or 5 times. It also makes the dependencies much cleaner and allows for reuse of modules. For example sharness coverage collection (WIP) uses sharness target with amended PATH, previously it might have been possible but not without wiring in the coverage collection into sharness make runner code. Yes, it is more complex but not much more. There are few rules that have to be followed and few complexities added but IMHO it is worth it. How to NR-make: 1. If make is to generate some file via a target, it MUST be defined in Rules.mk file in the directory of the target. 2. `Rules.mk` file MUST have `include mk/header.mk` statement as the first line and `include mk/footer.mk` statement as the last line (apart from project root `Rules.mk`). 3. It then MUST be included by the closest `Rules.mk` file up the directory tree. 4. Inside a `Rules.mk` special variable accessed as `$(d)` is defined. Its value is current directory, use it so if the `Rules.mk` file is moved in the tree it still works without a problem. Caution: this variable is not available in the recipe part and MUST NOT be used. Use name of the target or prerequisite to extract it if you need it. 5. Make has only one global scope, this means that name conflicts are a thing. Names SHOULD follow `VAR_NAME_$(d)` convention. There are exceptions from this rule in form of well defined global variables. Examples: General lists `TGT_BIN`, `CLEAN`; General targets: `TEST`, `COVERAGE`; General variables: `GOFLAGS`, `DEPS_GO`. 3. Any rules, definitions or variables that fit some family SHOULD be defined in `mk/$family.mk` file and included from project root `Rules.mk` License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@978939bcf10a49ac030051aa109915e495e67a07 --- unixfs/pb/Makefile | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 unixfs/pb/Makefile diff --git a/unixfs/pb/Makefile b/unixfs/pb/Makefile deleted file mode 100644 index 334feee74..000000000 --- a/unixfs/pb/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -PB = $(wildcard *.proto) -GO = $(PB:.proto=.pb.go) - -all: $(GO) - -%.pb.go: %.proto - protoc --gogo_out=. --proto_path=../../../../../../:/usr/local/opt/protobuf/include:. $< - -clean: - rm *.pb.go From 1663d8265259c877507f50306669a482dc22fe71 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 16 Dec 2016 19:04:22 +0100 Subject: [PATCH 1718/3817] make: rework makefiles for non-recursive make and add sharness coverage This commit introduces non-recursive Makefile infrastructure that replaces current Makefile infrastructure. It also generally cleanups the Makefiles, separates them into nicer sub-modules and centralizes common operations into single definitions. It allows to depend on any target that is defined in the makefile, this means that for example `gx install` is called once when `make build test_expensive_sharness` is called instead of 4 or 5 times. It also makes the dependencies much cleaner and allows for reuse of modules. For example sharness coverage collection (WIP) uses sharness target with amended PATH, previously it might have been possible but not without wiring in the coverage collection into sharness make runner code. Yes, it is more complex but not much more. There are few rules that have to be followed and few complexities added but IMHO it is worth it. How to NR-make: 1. If make is to generate some file via a target, it MUST be defined in Rules.mk file in the directory of the target. 2. `Rules.mk` file MUST have `include mk/header.mk` statement as the first line and `include mk/footer.mk` statement as the last line (apart from project root `Rules.mk`). 3. It then MUST be included by the closest `Rules.mk` file up the directory tree. 4. Inside a `Rules.mk` special variable accessed as `$(d)` is defined. Its value is current directory, use it so if the `Rules.mk` file is moved in the tree it still works without a problem. Caution: this variable is not available in the recipe part and MUST NOT be used. Use name of the target or prerequisite to extract it if you need it. 5. Make has only one global scope, this means that name conflicts are a thing. Names SHOULD follow `VAR_NAME_$(d)` convention. There are exceptions from this rule in form of well defined global variables. Examples: General lists `TGT_BIN`, `CLEAN`; General targets: `TEST`, `COVERAGE`; General variables: `GOFLAGS`, `DEPS_GO`. 3. Any rules, definitions or variables that fit some family SHOULD be defined in `mk/$family.mk` file and included from project root `Rules.mk` License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-merkledag@99cea5f1570e5a5319ae112402bfd6a55997350d --- ipld/merkledag/pb/Makefile | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 ipld/merkledag/pb/Makefile diff --git a/ipld/merkledag/pb/Makefile b/ipld/merkledag/pb/Makefile deleted file mode 100644 index 08ac883d0..000000000 --- a/ipld/merkledag/pb/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -PB = $(wildcard *.proto) -GO = $(PB:.proto=.pb.go) - -all: $(GO) - -%.pb.go: %.proto - protoc --gogo_out=. --proto_path=../../../../../../:/usr/local/opt/protobuf/include:. $< - -clean: - rm -f *.pb.go - rm -f *.go From a318d4fcdd538a1052243d197681dce6593f715c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Feb 2017 20:09:02 -0800 Subject: [PATCH 1719/3817] update go-multihash and bubble up deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@9e53626f6a8c4986a61c2508d026500ad759e5e9 --- path/path.go | 2 +- path/resolver.go | 4 ++-- path/resolver_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/path/path.go b/path/path.go index 550368ad8..3a885b478 100644 --- a/path/path.go +++ b/path/path.go @@ -5,7 +5,7 @@ import ( "path" "strings" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) // ErrBadPath is returned when a given path is incorrectly formatted diff --git a/path/resolver.go b/path/resolver.go index fb472953e..1df66303a 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -9,9 +9,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) var log = logging.Logger("path") diff --git a/path/resolver_test.go b/path/resolver_test.go index 93f3ff7c4..f489c1eb1 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -9,8 +9,8 @@ import ( dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" - util "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + util "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) func randNode() *merkledag.ProtoNode { From 96fbd128fabe1dcf7fb1121e11ca806d9770fe11 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Feb 2017 20:09:02 -0800 Subject: [PATCH 1720/3817] update go-multihash and bubble up deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@fcea327827bd359a5e90ba7ad10d609f066679fa --- ipld/merkledag/coding.go | 4 ++-- ipld/merkledag/merkledag.go | 6 +++--- ipld/merkledag/merkledag_test.go | 6 +++--- ipld/merkledag/node.go | 6 +++--- ipld/merkledag/node_test.go | 2 +- ipld/merkledag/raw.go | 6 +++--- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/traverse/traverse_test.go | 2 +- ipld/merkledag/utils/diff.go | 2 +- ipld/merkledag/utils/utils.go | 2 +- ipld/merkledag/utils/utils_test.go | 2 +- 11 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index e76af12c7..74c2319dc 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -6,8 +6,8 @@ import ( pb "github.com/ipfs/go-ipfs/merkledag/pb" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index ae8d71cfe..6257d8c5f 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -11,10 +11,10 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - ipldcbor "gx/ipfs/QmT1B6cKXnMMki8nbuhrnLuiU32HLvwi6xe99bJ79482UK/go-ipld-cbor" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + ipldcbor "gx/ipfs/QmWcQMNruWC3wphK1L6zEcV4MZBJqfsNKSRFcuo4AsNk4k/go-ipld-cbor" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) var log = logging.Logger("merkledag") diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index c55a7b551..bd310469f 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -23,9 +23,9 @@ import ( dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) func TestNode(t *testing.T) { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 4f0d72bc9..4204f5276 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -5,9 +5,9 @@ import ( "encoding/json" "fmt" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" - mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + mh "gx/ipfs/QmbZ6Cee2uHjG7hf19qLHppgKDRtaG4CVtMzdmK9VCVqLu/go-multihash" ) var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index 63f0473ba..3465b8299 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -8,7 +8,7 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) func TestRemoveLink(t *testing.T) { diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 17aaa21c1..88cb564e4 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -3,9 +3,9 @@ package merkledag import ( "github.com/ipfs/go-ipfs/blocks" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) type RawNode struct { diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index 96562b7be..34149f891 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -5,7 +5,7 @@ import ( "context" "errors" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) // Order is an identifier for traversal algorithm orders diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 43f06ce7b..087bfa982 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -8,7 +8,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) func TestDFSPreNoSkip(t *testing.T) { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index e854ccc0b..87add22dd 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -7,7 +7,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" context "context" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) const ( diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 7d52e480c..972e98a68 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -10,9 +10,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" syncds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) type Editor struct { diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 9614990a5..3634ac595 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" context "context" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) func TestAddLink(t *testing.T) { From bf1ad22e5bd05466835ffbb002a16374e598b3be Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Feb 2017 20:09:02 -0800 Subject: [PATCH 1721/3817] update go-multihash and bubble up deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@b0702a0c807e489eb81d107f045c2e2039af4a7b --- namesys/interface.go | 2 +- namesys/ipns_select_test.go | 4 ++-- namesys/namesys.go | 6 +++--- namesys/namesys_test.go | 2 +- namesys/publisher.go | 12 ++++++------ namesys/republisher/repub.go | 8 ++++---- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 10 +++++----- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index 3f66498ac..ac2307e76 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,7 +35,7 @@ import ( context "context" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" + ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" ) const ( diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index 883c00c2b..84250baa3 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -10,8 +10,8 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" + ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" + u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) func shuffle(a []*pb.IpnsEntry) { diff --git a/namesys/namesys.go b/namesys/namesys.go index bf1c68967..78b406d42 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,10 +7,10 @@ import ( path "github.com/ipfs/go-ipfs/path" + ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" - peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" - ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" + peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" + routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 7d5c637b5..ca99ff799 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,8 +10,8 @@ import ( offroute "github.com/ipfs/go-ipfs/routing/offline" "github.com/ipfs/go-ipfs/unixfs" + ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" ) type mockResolver struct { diff --git a/namesys/publisher.go b/namesys/publisher.go index 54a0e834e..8d01937d7 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -14,14 +14,14 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ft "github.com/ipfs/go-ipfs/unixfs" + ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" - record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" - dhtpb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" - peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" - ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" + peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" + routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" + record "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record" + dhtpb "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record/pb" + u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index e4e0bdc92..89e32033d 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,15 +11,15 @@ import ( path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" - recpb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" - pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" - peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" + peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" + routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" + recpb "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record/pb" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 65a2df82f..30f4140f7 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmdzDdLZ7nj133QvNHypyS9Y39g35bMFk5DJ2pmX7YqtKU/go-libp2p/p2p/net/mock" - pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" + pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" + mocknet "gx/ipfs/QmSNJRX4uphb3Eyp69uYbpRVvgqjPxfjnJmjcdMWkDH5Pn/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index f7d41ead4..5bc4b3e07 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -12,7 +12,7 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" + peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index 859da7d60..89b507261 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -9,14 +9,14 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" + ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - mh "gx/ipfs/QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J/go-multihash" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" - ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" + routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" + u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + mh "gx/ipfs/QmbZ6Cee2uHjG7hf19qLHppgKDRtaG4CVtMzdmK9VCVqLu/go-multihash" ) var log = logging.Logger("namesys") From 4ff35e37016e3a38a16c44a0e20a4a3742610645 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Feb 2017 20:09:02 -0800 Subject: [PATCH 1722/3817] update go-multihash and bubble up deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@3e5b1ea788a6d93305159afb551924a13a4af63d --- routing/mock/centralized_client.go | 14 +++++++------- routing/mock/centralized_server.go | 6 +++--- routing/mock/centralized_test.go | 7 ++++--- routing/mock/dht.go | 4 ++-- routing/mock/interface.go | 4 ++-- routing/none/none_client.go | 10 +++++----- routing/offline/offline.go | 14 +++++++------- routing/supernode/client.go | 16 ++++++++-------- routing/supernode/proxy/loopback.go | 6 +++--- routing/supernode/proxy/standard.go | 14 +++++++------- routing/supernode/server.go | 10 +++++----- routing/supernode/server_test.go | 2 +- 12 files changed, 54 insertions(+), 53 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 135f3be99..b9802adcd 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,16 +8,16 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" "github.com/ipfs/go-ipfs/thirdparty/testutil" + pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + ma "gx/ipfs/QmSWLfmj5frN9xVLMMN846dMDriy5wN5jeghUm7aTW3DAG/go-multiaddr" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - ma "gx/ipfs/QmUAQaWbKxGCUTuoQVvvicbQNZ9APF5pDGWyAZSe93AtKH/go-multiaddr" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" - dhtpb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" - pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" - peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" + peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" + routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" + dhtpb "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record/pb" + u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 041a8ad29..1e83ddb6b 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -8,11 +8,11 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" + pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" - pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" - peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 5aca0b089..f6f945ddb 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -7,9 +7,10 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" - pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" + + pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 707ea0271..a227232f0 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - dht "gx/ipfs/QmRG9fdibExi5DFy8kzyxF76jvZVUb2mQBUSMNP1YaYn9M/go-libp2p-kad-dht" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - mocknet "gx/ipfs/QmdzDdLZ7nj133QvNHypyS9Y39g35bMFk5DJ2pmX7YqtKU/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmSNJRX4uphb3Eyp69uYbpRVvgqjPxfjnJmjcdMWkDH5Pn/go-libp2p/p2p/net/mock" + dht "gx/ipfs/QmdFu71pRmWMNWht96ZTJ3wRx4D7BPJ2JfHH24z59Gidsc/go-libp2p-kad-dht" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 91dbc1deb..63e4dd5a8 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -11,8 +11,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" - peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" + peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" + routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" ) // Server provides mockrouting Clients diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 2d0bba3a4..5818b5593 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,12 +6,12 @@ import ( repo "github.com/ipfs/go-ipfs/repo" - p2phost "gx/ipfs/QmPsRtodRuBUir32nz5v4zuSBTSszrR1d3fA6Ahb6eaejj/go-libp2p-host" + pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" - pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" - peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" + routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" + p2phost "gx/ipfs/QmbzbRyd22gcW92U1rA2yKagB3myMYhk45XBknJ49F9XWJ/go-libp2p-host" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 7813208ed..1ce0da1e7 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,16 +7,16 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" + pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" - record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" - pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" - pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" - "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" - ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" + "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" + routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" + record "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record" + pb "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record/pb" ) var log = logging.Logger("offlinerouting") diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 5f4822425..63251ad86 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,16 +8,16 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - "gx/ipfs/QmPsRtodRuBUir32nz5v4zuSBTSszrR1d3fA6Ahb6eaejj/go-libp2p-host" - dhtpb "gx/ipfs/QmRG9fdibExi5DFy8kzyxF76jvZVUb2mQBUSMNP1YaYn9M/go-libp2p-kad-dht/pb" + pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" + loggables "gx/ipfs/QmTcfnDHimxBJqx6utpnWqVHdvyquXgkwAvYt4zMaJMKS2/go-libp2p-loggables" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" - pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" - pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" - peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" + peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" + routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" + pb "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record/pb" + "gx/ipfs/QmbzbRyd22gcW92U1rA2yKagB3myMYhk45XBknJ49F9XWJ/go-libp2p-host" + dhtpb "gx/ipfs/QmdFu71pRmWMNWht96ZTJ3wRx4D7BPJ2JfHH24z59Gidsc/go-libp2p-kad-dht/pb" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index ab617b15a..3ae8bce8d 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,10 +2,10 @@ package proxy import ( context "context" - inet "gx/ipfs/QmQx1dHDDYENugYgqA22BaBrRfuv1coSsuPiM7rYh1wwGH/go-libp2p-net" - dhtpb "gx/ipfs/QmRG9fdibExi5DFy8kzyxF76jvZVUb2mQBUSMNP1YaYn9M/go-libp2p-kad-dht/pb" + inet "gx/ipfs/QmRuZnMorqodado1yeTQiv1i9rmtKj29CjPSsBKM7DFXV4/go-libp2p-net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" + peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" + dhtpb "gx/ipfs/QmdFu71pRmWMNWht96ZTJ3wRx4D7BPJ2JfHH24z59Gidsc/go-libp2p-kad-dht/pb" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 0612fefe4..5933259ae 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,15 +4,15 @@ import ( "context" "errors" - host "gx/ipfs/QmPsRtodRuBUir32nz5v4zuSBTSszrR1d3fA6Ahb6eaejj/go-libp2p-host" - inet "gx/ipfs/QmQx1dHDDYENugYgqA22BaBrRfuv1coSsuPiM7rYh1wwGH/go-libp2p-net" - dhtpb "gx/ipfs/QmRG9fdibExi5DFy8kzyxF76jvZVUb2mQBUSMNP1YaYn9M/go-libp2p-kad-dht/pb" - kbucket "gx/ipfs/QmRVHVr38ChANF2PUMNKQs7Q4uVWCLVabrfcTG9taNbcVy/go-libp2p-kbucket" + pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" + inet "gx/ipfs/QmRuZnMorqodado1yeTQiv1i9rmtKj29CjPSsBKM7DFXV4/go-libp2p-net" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - loggables "gx/ipfs/QmTMy4hVSY28DdwJ9kBz6y7q6MuioFzPcpM3Ma3aPjo1i3/go-libp2p-loggables" + loggables "gx/ipfs/QmTcfnDHimxBJqx6utpnWqVHdvyquXgkwAvYt4zMaJMKS2/go-libp2p-loggables" + kbucket "gx/ipfs/QmUwZcbSVMsLZzovZssH96rCUM5FAkrjaqhHLhJnFYd5z3/go-libp2p-kbucket" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" - peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" + peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" + host "gx/ipfs/QmbzbRyd22gcW92U1rA2yKagB3myMYhk45XBknJ49F9XWJ/go-libp2p-host" + dhtpb "gx/ipfs/QmdFu71pRmWMNWht96ZTJ3wRx4D7BPJ2JfHH24z59Gidsc/go-libp2p-kad-dht/pb" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 79c98d898..7cfe3ba17 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,13 +8,13 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - dhtpb "gx/ipfs/QmRG9fdibExi5DFy8kzyxF76jvZVUb2mQBUSMNP1YaYn9M/go-libp2p-kad-dht/pb" + pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - record "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record" - pb "gx/ipfs/QmdM4ohF7cr4MvAECVeD3hRA3HtZrk1ngaek4n8ojVT87h/go-libp2p-record/pb" - pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore" - peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer" + peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" + record "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record" + pb "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record/pb" + dhtpb "gx/ipfs/QmdFu71pRmWMNWht96ZTJ3wRx4D7BPJ2JfHH24z59Gidsc/go-libp2p-kad-dht/pb" ) // Server handles routing queries using a database backend diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index b802700e2..5af0a8fd2 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmRG9fdibExi5DFy8kzyxF76jvZVUb2mQBUSMNP1YaYn9M/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dhtpb "gx/ipfs/QmdFu71pRmWMNWht96ZTJ3wRx4D7BPJ2JfHH24z59Gidsc/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 33dfe6467ebe3aa5e17dd6ed6031c15026181053 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Feb 2017 20:09:02 -0800 Subject: [PATCH 1723/3817] update go-multihash and bubble up deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@fa89822abc9732670da14f752243bfd46f6838b4 --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index cc85d9979..2c99cb502 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,7 +8,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index baf0d5958..489809b0c 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -11,10 +11,10 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 90bbc5213..e9c8a8843 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -12,8 +12,8 @@ import ( context "context" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) func randNode() (*mdag.ProtoNode, *cid.Cid) { diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 01e0e198b..bf05924fd 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -12,9 +12,9 @@ import ( "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 788af5a46..57826a998 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -12,7 +12,7 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) func ignoreCids(_ *cid.Cid) {} From f21d0c6305f8608497a805048ec3bf3cc14eb6ea Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Feb 2017 20:09:02 -0800 Subject: [PATCH 1724/3817] update go-multihash and bubble up deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-keystore@668bab43da04cd97ee9a2640a1c3fd287ae9f2f5 --- keystore/keystore.go | 2 +- keystore/keystore_test.go | 2 +- keystore/memkeystore.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index a18da4620..761cd514d 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -7,7 +7,7 @@ import ( "path/filepath" "strings" - ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" + ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" ) type Keystore interface { diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index a58fe778c..12dd6d29b 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -7,7 +7,7 @@ import ( "sort" "testing" - ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" + ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" ) type rr struct{} diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index 9eec377db..a6462913b 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -1,6 +1,6 @@ package keystore -import ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto" +import ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" type MemKeystore struct { keys map[string]ci.PrivKey From da0c7152963c7a1e1de89442c24d449b0da2eb61 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Feb 2017 20:09:02 -0800 Subject: [PATCH 1725/3817] update go-multihash and bubble up deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@740dde9d611c679ad112dcc52236892d5936e29a --- blockstore/arc_cache.go | 2 +- blockstore/arc_cache_test.go | 2 +- blockstore/blockstore.go | 2 +- blockstore/blockstore_test.go | 4 ++-- blockstore/bloom_cache.go | 2 +- blockstore/util/remove.go | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index d4b85136f..989f36e11 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -7,8 +7,8 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) type arccache struct { diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 7e59a49df..987185e80 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -8,7 +8,7 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" syncds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) var exampleBlock = blocks.NewBlock([]byte("foo")) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 6313cfffe..17ab24b3e 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -15,7 +15,7 @@ import ( dsns "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/namespace" dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 0ea102b2b..6e5216609 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -12,8 +12,8 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" ds_sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) func TestGetWhenKeyNotPresent(t *testing.T) { diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 7d234b3fc..63c1c368a 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -8,7 +8,7 @@ import ( "github.com/ipfs/go-ipfs/blocks" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" bloom "gx/ipfs/QmeiMCBkYHxkDkDfnDadzz4YxY5ruL5Pj499essE4vRsGM/bbloom" ) diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index e4ae67868..60cb1aee8 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -7,7 +7,7 @@ import ( bs "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/pin" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) // RemovedBlock is used to respresent the result of removing a block. From 687de649285756dca7e2fa6e9deb304b64c2406a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Feb 2017 20:09:02 -0800 Subject: [PATCH 1726/3817] update go-multihash and bubble up deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@a3e72305bf49dc93bf22495b163109486fd9908a --- unixfs/io/dagreader.go | 2 +- unixfs/io/dirbuilder.go | 2 +- unixfs/io/resolve.go | 2 +- unixfs/mod/dagmodifier.go | 4 ++-- unixfs/mod/dagmodifier_test.go | 2 +- unixfs/test/utils.go | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index aded131dc..d36c33c2e 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 569ee371f..071eba055 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -5,7 +5,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) type directoryBuilder struct { diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index 9603b08dc..5970e72b5 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -6,7 +6,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) func ResolveUnixfsOnce(ctx context.Context, ds dag.DAGService, nd node.Node, name string) (*node.Link, error) { diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 356a5b1e9..52e821de9 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -14,10 +14,10 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) var ErrSeekFail = errors.New("failed to seek properly") diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index fdeabbbb2..cdd97038b 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -19,7 +19,7 @@ import ( context "context" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" + u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.Blockstore) { diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 7b9775cff..c46c4d3e5 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -14,8 +14,8 @@ import ( mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) func SizeSplitterGen(size int64) chunk.SplitterGen { From c8a57ca4e3df2570f75832b5bebcb925ae647bce Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Feb 2017 20:09:02 -0800 Subject: [PATCH 1727/3817] update go-multihash and bubble up deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/interface-go-ipfs-core@c7723c40fead3509cec1fa255fc8531e8e87c744 --- coreiface/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index cf6471947..b506e6509 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -5,8 +5,8 @@ import ( "errors" "io" - ipld "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + ipld "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) // type CoreAPI interface { From d80198da6e3dd8c0aa5a94a3166fd9b45f77f173 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Feb 2017 20:09:02 -0800 Subject: [PATCH 1728/3817] update go-multihash and bubble up deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@3141caa870c41a8fbce5ab157905a95f5bf07ec4 --- mfs/dir.go | 2 +- mfs/file.go | 2 +- mfs/mfs_test.go | 6 +++--- mfs/ops.go | 2 +- mfs/repub_test.go | 2 +- mfs/system.go | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index ec6de0a45..f1a61eefa 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -14,7 +14,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) var ErrNotYetImplemented = errors.New("not yet implemented") diff --git a/mfs/file.go b/mfs/file.go index b61380d77..a379c802f 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 7b19f50b3..74e0d6dfb 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -24,11 +24,11 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) func emptyDirNode() *dag.ProtoNode { diff --git a/mfs/ops.go b/mfs/ops.go index d71109226..8dd7131d8 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -9,7 +9,7 @@ import ( path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) // Mv moves the file or directory at 'src' to 'dst' diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 2b8acea73..0952de0dd 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -7,7 +7,7 @@ import ( ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" "context" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index 0578166af..1c57677b5 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -18,9 +18,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - node "gx/ipfs/QmRSU5EqqWVZSNdbU51yXmVoF1uNw3JgTNB6RaiL7DZM16/go-ipld-node" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) var ErrNotExist = errors.New("no such rootfs") From 679a018e5ab7938056bd0c0993d3f5a78052abe9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Feb 2017 20:09:02 -0800 Subject: [PATCH 1729/3817] update go-multihash and bubble up deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@806865119ab7c4ce79d7325135de987d98d7f5ce --- blockservice/blockservice.go | 2 +- blockservice/test/blocks_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index e109b85fe..6629d67cd 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -13,7 +13,7 @@ import ( exchange "github.com/ipfs/go-ipfs/exchange" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) var log = logging.Logger("blockservice") diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index ed700d46f..db60a3726 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -14,8 +14,8 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) func newObject(data []byte) blocks.Block { From 20b7da53808190fb90abb5911a877c924f613d30 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Feb 2017 20:09:02 -0800 Subject: [PATCH 1730/3817] update go-multihash and bubble up deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@c48251feac46347b694eb83660ffa8454b4640e5 --- exchange/offline/offline.go | 2 +- exchange/offline/offline_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 0fae10da6..10d6609a0 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -9,7 +9,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 08c4aaf87..df1b0452b 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -10,8 +10,8 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" ds_sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) func TestBlockReturnsErr(t *testing.T) { From 913ef5dea81b2022e3c79ff1c034aefad3018f48 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Feb 2017 20:09:02 -0800 Subject: [PATCH 1731/3817] update go-multihash and bubble up deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-interface@f16adb6f37e6afa51a9c1f1d8fa4c76dded0c223 --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index 62e22c38d..aabece6b3 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -7,7 +7,7 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) // Any type that implements exchange.Interface may be used as an IPFS block From c9058e78832167522e91499c0fa5eb429c631fe8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Feb 2017 20:09:02 -0800 Subject: [PATCH 1732/3817] update go-multihash and bubble up deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-chunker@281ee7f81f89edc00c9f3c9dd951535ebb0caf8d --- chunker/rabin_test.go | 2 +- chunker/splitting_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 366d44fb8..5603621b2 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -4,7 +4,7 @@ import ( "bytes" "fmt" "github.com/ipfs/go-ipfs/blocks" - "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" + "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" "io" "testing" ) diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index 24c2bdcf9..bbe1e499f 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -5,7 +5,7 @@ import ( "io" "testing" - u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" + u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) func randBuf(t *testing.T, size int) []byte { From 5c6505e8c3ddff5bfa0e9bd906fbf62b527d6abf Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 2 Feb 2017 20:09:02 -0800 Subject: [PATCH 1733/3817] update go-multihash and bubble up deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-ds-help@e8042edfcaea633762680d452fd6ebe1dd3cfe85 --- datastore/dshelp/key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 7e962fff0..93c22ef1a 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -2,8 +2,8 @@ package dshelp import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" base32 "gx/ipfs/QmZvZSVtvxak4dcTkhsQhqd1SQ6rg5UzaSTu62WfWKjj93/base32" - cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid" ) // TODO: put this code into the go-datastore itself From 5ff02703456b10d03a563767a338648510498340 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 10 Dec 2016 11:25:29 -0800 Subject: [PATCH 1734/3817] add partial resolving to resolver code License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@e830b010e6dc488aff1b45ebe0e07ece1a4da731 --- path/resolver.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/path/resolver.go b/path/resolver.go index 1df66303a..4339fbf0e 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -73,6 +73,39 @@ func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) { return c, parts[1:], nil } +func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath Path) (node.Node, []string, error) { + c, p, err := SplitAbsPath(fpath) + if err != nil { + return nil, nil, err + } + + nd, err := r.DAG.Get(ctx, c) + if err != nil { + return nil, nil, err + } + + for len(p) > 0 { + val, rest, err := nd.Resolve(p) + if err != nil { + return nil, nil, err + } + + switch val := val.(type) { + case *node.Link: + next, err := val.GetNode(ctx, r.DAG) + if err != nil { + return nil, nil, err + } + nd = next + p = rest + default: + return nd, p, nil + } + } + + return nd, nil, nil +} + // ResolvePath fetches the node for given path. It returns the last item // returned by ResolvePathComponents. func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (node.Node, error) { From 27ad4dd1b35c70cc05dde7113f3a254d03b4c560 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 13 Feb 2017 19:15:17 -0800 Subject: [PATCH 1735/3817] allow for sub-object resolution in dag get License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@0ac36ee7f4358dc8bba33188438f675de9c59052 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 6257d8c5f..fb20948bc 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -13,8 +13,8 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - ipldcbor "gx/ipfs/QmWcQMNruWC3wphK1L6zEcV4MZBJqfsNKSRFcuo4AsNk4k/go-ipld-cbor" node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + ipldcbor "gx/ipfs/QmdaC21UyoyN3t9QdapHZfsaUo3mqVf5p4CEuFaYVFqwap/go-ipld-cbor" ) var log = logging.Logger("merkledag") From 545e2c1b3806587af99b4a38cd1f7fa0e4a689b8 Mon Sep 17 00:00:00 2001 From: Iaroslav Gridin Date: Sat, 14 Jan 2017 19:54:52 +0200 Subject: [PATCH 1736/3817] Pass cids instead of nodes around in EnumerateChildrenAsync License: MIT Signed-off-by: Iaroslav Gridin This commit was moved from ipfs/go-merkledag@7379cc1adec72a99140be06207ab1f80189089f3 --- ipld/merkledag/merkledag.go | 75 +++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index ae8d71cfe..f6bc8f9f5 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -394,45 +394,40 @@ func EnumerateChildren(ctx context.Context, ds LinkService, root *cid.Cid, visit var FetchGraphConcurrency = 8 func EnumerateChildrenAsync(ctx context.Context, ds DAGService, c *cid.Cid, visit func(*cid.Cid) bool) error { - if !visit(c) { - return nil - } - - root, err := ds.Get(ctx, c) - if err != nil { - return err - } - - feed := make(chan node.Node) - out := make(chan *NodeOption) + feed := make(chan *cid.Cid) + out := make(chan node.Node) done := make(chan struct{}) var setlk sync.Mutex - + + errChan := make(chan error) + fetchersCtx, cancel := context.WithCancel(ctx) + + defer cancel() + for i := 0; i < FetchGraphConcurrency; i++ { go func() { - for n := range feed { - links := n.Links() - cids := make([]*cid.Cid, 0, len(links)) - for _, l := range links { - setlk.Lock() - unseen := visit(l.Cid) - setlk.Unlock() - if unseen { - cids = append(cids, l.Cid) - } + for ic := range feed { + n, err := ds.Get(ctx, ic) + if err != nil { + errChan <- err + return } - - for nopt := range ds.GetMany(ctx, cids) { + + setlk.Lock() + unseen := visit(ic) + setlk.Unlock() + + if unseen { select { - case out <- nopt: - case <-ctx.Done(): + case out <- n: + case <-fetchersCtx.Done(): return } } select { case done <- struct{}{}: - case <-ctx.Done(): + case <-fetchersCtx.Done(): } } }() @@ -440,10 +435,10 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, c *cid.Cid, visi defer close(feed) send := feed - var todobuffer []node.Node + var todobuffer []*cid.Cid var inProgress int - next := root + next := c for { select { case send <- next: @@ -460,18 +455,18 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, c *cid.Cid, visi if inProgress == 0 && next == nil { return nil } - case nc := <-out: - if nc.Err != nil { - return nc.Err - } - - if next == nil { - next = nc.Node - send = feed - } else { - todobuffer = append(todobuffer, nc.Node) + case nd := <-out: + for _, lnk := range nd.Links() { + if next == nil { + next = lnk.Cid + send = feed + } else { + todobuffer = append(todobuffer, lnk.Cid) + } } - + case err := <-errChan: + return err + case <-ctx.Done(): return ctx.Err() } From eab3a8a315e942e1d73b1b20f5a81d794f5ec02c Mon Sep 17 00:00:00 2001 From: Iaroslav Gridin Date: Thu, 19 Jan 2017 13:51:55 +0200 Subject: [PATCH 1737/3817] Re-enable async children enumerating in FetchGraph License: MIT Signed-off-by: Iaroslav Gridin This commit was moved from ipfs/go-merkledag@03782baff57dcc6716275ac1b0eb7e69cc5f0826 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index f6bc8f9f5..faff47796 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -140,7 +140,7 @@ func (n *dagService) Remove(nd node.Node) error { // FetchGraph fetches all nodes that are children of the given node func FetchGraph(ctx context.Context, c *cid.Cid, serv DAGService) error { - return EnumerateChildren(ctx, serv, c, cid.NewSet().Visit, false) + return EnumerateChildrenAsync(ctx, serv, c, cid.NewSet().Visit) } // FindLinks searches this nodes links for the given key, From 4fafef834b260347860b72da768689824567da4e Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 17 Feb 2017 08:39:59 +0100 Subject: [PATCH 1738/3817] Fix formatting in merkledag.go License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-merkledag@5dc5456c7dcb71dd4007b03da6d7ef4420114150 --- ipld/merkledag/merkledag.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 0bd32b015..f752ff50f 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -399,12 +399,12 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, c *cid.Cid, visi done := make(chan struct{}) var setlk sync.Mutex - + errChan := make(chan error) fetchersCtx, cancel := context.WithCancel(ctx) - + defer cancel() - + for i := 0; i < FetchGraphConcurrency; i++ { go func() { for ic := range feed { @@ -413,11 +413,11 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, c *cid.Cid, visi errChan <- err return } - + setlk.Lock() unseen := visit(ic) setlk.Unlock() - + if unseen { select { case out <- n: @@ -466,7 +466,7 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, c *cid.Cid, visi } case err := <-errChan: return err - + case <-ctx.Done(): return ctx.Err() } From b594c66c2ccade86ac485ea6e15a1469002d451d Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 17 Feb 2017 16:17:50 -0500 Subject: [PATCH 1739/3817] Report progress during 'pin add'. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@c9dcb0257fcc44a62516faecee72317b9da45c55 --- ipld/merkledag/merkledag.go | 38 +++++++++++++++- ipld/merkledag/merkledag_test.go | 78 ++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index f752ff50f..b81d2b60b 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -139,8 +139,21 @@ func (n *dagService) Remove(nd node.Node) error { } // FetchGraph fetches all nodes that are children of the given node -func FetchGraph(ctx context.Context, c *cid.Cid, serv DAGService) error { - return EnumerateChildrenAsync(ctx, serv, c, cid.NewSet().Visit) +func FetchGraph(ctx context.Context, root *cid.Cid, serv DAGService) error { + v, _ := ctx.Value("progress").(*ProgressTracker) + if v == nil { + return EnumerateChildrenAsync(ctx, serv, root, cid.NewSet().Visit) + } + set := cid.NewSet() + visit := func(c *cid.Cid) bool { + if set.Visit(c) { + v.Increment() + return true + } else { + return false + } + } + return EnumerateChildrenAsync(ctx, serv, root, visit) } // FindLinks searches this nodes links for the given key, @@ -389,6 +402,27 @@ func EnumerateChildren(ctx context.Context, ds LinkService, root *cid.Cid, visit return nil } +type ProgressTracker struct { + Total int + lk sync.Mutex +} + +func (p *ProgressTracker) DeriveContext(ctx context.Context) context.Context { + return context.WithValue(ctx, "progress", p) +} + +func (p *ProgressTracker) Increment() { + p.lk.Lock() + defer p.lk.Unlock() + p.Total++ +} + +func (p *ProgressTracker) Value() int { + p.lk.Lock() + defer p.lk.Unlock() + return p.Total +} + // FetchGraphConcurrency is total number of concurrent fetches that // 'fetchNodes' will start at a time var FetchGraphConcurrency = 8 diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index bd310469f..e7cfc8891 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "io/ioutil" + "math/rand" "strings" "sync" "testing" @@ -547,3 +548,80 @@ func TestEnumerateAsyncFailsNotFound(t *testing.T) { t.Fatal("this should have failed") } } + +func TestProgressIndicator(t *testing.T) { + testProgressIndicator(t, 5) +} + +func TestProgressIndicatorNoChildren(t *testing.T) { + testProgressIndicator(t, 0) +} + +func testProgressIndicator(t *testing.T, depth int) { + ds := dstest.Mock() + + top, numChildren := mkDag(ds, depth) + + v := new(ProgressTracker) + ctx := v.DeriveContext(context.Background()) + + err := FetchGraph(ctx, top, ds) + if err != nil { + t.Fatal(err) + } + + if v.Value() != numChildren+1 { + t.Errorf("wrong number of children reported in progress indicator, expected %d, got %d", + numChildren+1, v.Value()) + } +} + +func mkDag(ds DAGService, depth int) (*cid.Cid, int) { + totalChildren := 0 + f := func() *ProtoNode { + p := new(ProtoNode) + buf := make([]byte, 16) + rand.Read(buf) + + p.SetData(buf) + _, err := ds.Add(p) + if err != nil { + panic(err) + } + return p + } + + for i := 0; i < depth; i++ { + thisf := f + f = func() *ProtoNode { + pn := mkNodeWithChildren(thisf, 10) + _, err := ds.Add(pn) + if err != nil { + panic(err) + } + totalChildren += 10 + return pn + } + } + + nd := f() + c, err := ds.Add(nd) + if err != nil { + panic(err) + } + + return c, totalChildren +} + +func mkNodeWithChildren(getChild func() *ProtoNode, width int) *ProtoNode { + cur := new(ProtoNode) + + for i := 0; i < width; i++ { + c := getChild() + if err := cur.AddNodeLinkClean(fmt.Sprint(i), c); err != nil { + panic(err) + } + } + + return cur +} From ca7ee46771345022cab18b1b6b39695cf1985b97 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 16 Feb 2017 15:19:48 +0100 Subject: [PATCH 1740/3817] deps: update dependencies for PNet License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@cb2bb69ea78f58395e079bffbd0a047ecaebda68 --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 30f4140f7..aeffa790b 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" - mocknet "gx/ipfs/QmSNJRX4uphb3Eyp69uYbpRVvgqjPxfjnJmjcdMWkDH5Pn/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmU3g3psEDiC4tQh1Qu2NYg5aYVQqxC3m74ZavLwPfJEtu/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { From c89671ffdaf5354891ee8103fe0e847d038fd868 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 16 Feb 2017 15:19:48 +0100 Subject: [PATCH 1741/3817] deps: update dependencies for PNet License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-routing@de646f0caeae773b8085bbf955f269e45f772c22 --- routing/mock/dht.go | 4 ++-- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index a227232f0..95a9931f9 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,8 +5,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - mocknet "gx/ipfs/QmSNJRX4uphb3Eyp69uYbpRVvgqjPxfjnJmjcdMWkDH5Pn/go-libp2p/p2p/net/mock" - dht "gx/ipfs/QmdFu71pRmWMNWht96ZTJ3wRx4D7BPJ2JfHH24z59Gidsc/go-libp2p-kad-dht" + mocknet "gx/ipfs/QmU3g3psEDiC4tQh1Qu2NYg5aYVQqxC3m74ZavLwPfJEtu/go-libp2p/p2p/net/mock" + dht "gx/ipfs/QmUpZqxzrUoyDsgWXDri9yYgi5r5EK7J5Tan1MbgnawYLx/go-libp2p-kad-dht" ) type mocknetserver struct { diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 63251ad86..fb4a8ddda 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -11,13 +11,13 @@ import ( pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTcfnDHimxBJqx6utpnWqVHdvyquXgkwAvYt4zMaJMKS2/go-libp2p-loggables" + dhtpb "gx/ipfs/QmUpZqxzrUoyDsgWXDri9yYgi5r5EK7J5Tan1MbgnawYLx/go-libp2p-kad-dht/pb" cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" pb "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record/pb" "gx/ipfs/QmbzbRyd22gcW92U1rA2yKagB3myMYhk45XBknJ49F9XWJ/go-libp2p-host" - dhtpb "gx/ipfs/QmdFu71pRmWMNWht96ZTJ3wRx4D7BPJ2JfHH24z59Gidsc/go-libp2p-kad-dht/pb" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 3ae8bce8d..a7aaa2b75 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -3,9 +3,9 @@ package proxy import ( context "context" inet "gx/ipfs/QmRuZnMorqodado1yeTQiv1i9rmtKj29CjPSsBKM7DFXV4/go-libp2p-net" + dhtpb "gx/ipfs/QmUpZqxzrUoyDsgWXDri9yYgi5r5EK7J5Tan1MbgnawYLx/go-libp2p-kad-dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" - dhtpb "gx/ipfs/QmdFu71pRmWMNWht96ZTJ3wRx4D7BPJ2JfHH24z59Gidsc/go-libp2p-kad-dht/pb" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 5933259ae..a5ae9061e 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -8,11 +8,11 @@ import ( inet "gx/ipfs/QmRuZnMorqodado1yeTQiv1i9rmtKj29CjPSsBKM7DFXV4/go-libp2p-net" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmTcfnDHimxBJqx6utpnWqVHdvyquXgkwAvYt4zMaJMKS2/go-libp2p-loggables" + dhtpb "gx/ipfs/QmUpZqxzrUoyDsgWXDri9yYgi5r5EK7J5Tan1MbgnawYLx/go-libp2p-kad-dht/pb" kbucket "gx/ipfs/QmUwZcbSVMsLZzovZssH96rCUM5FAkrjaqhHLhJnFYd5z3/go-libp2p-kbucket" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" host "gx/ipfs/QmbzbRyd22gcW92U1rA2yKagB3myMYhk45XBknJ49F9XWJ/go-libp2p-host" - dhtpb "gx/ipfs/QmdFu71pRmWMNWht96ZTJ3wRx4D7BPJ2JfHH24z59Gidsc/go-libp2p-kad-dht/pb" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 7cfe3ba17..25b20724a 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -10,11 +10,11 @@ import ( pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dhtpb "gx/ipfs/QmUpZqxzrUoyDsgWXDri9yYgi5r5EK7J5Tan1MbgnawYLx/go-libp2p-kad-dht/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" record "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record" pb "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record/pb" - dhtpb "gx/ipfs/QmdFu71pRmWMNWht96ZTJ3wRx4D7BPJ2JfHH24z59Gidsc/go-libp2p-kad-dht/pb" ) // Server handles routing queries using a database backend diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 5af0a8fd2..b4b49e05a 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -4,7 +4,7 @@ import ( "testing" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dhtpb "gx/ipfs/QmdFu71pRmWMNWht96ZTJ3wRx4D7BPJ2JfHH24z59Gidsc/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmUpZqxzrUoyDsgWXDri9yYgi5r5EK7J5Tan1MbgnawYLx/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 326f1173f3961ed26ff3b3bb1a2fb674470f33d2 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 16 Feb 2017 20:39:37 -0500 Subject: [PATCH 1742/3817] Refactor EnumerateChildren to avoid need for bestEffort parameter. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@090bf56811b88a83bb4aeca1726eeae586b25ca2 --- ipld/merkledag/merkledag.go | 11 +++++------ ipld/merkledag/merkledag_test.go | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index b81d2b60b..1cf36f2cb 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -383,17 +383,16 @@ func (t *Batch) Commit() error { // EnumerateChildren will walk the dag below the given root node and add all // unseen children to the passed in set. // TODO: parallelize to avoid disk latency perf hits? -func EnumerateChildren(ctx context.Context, ds LinkService, root *cid.Cid, visit func(*cid.Cid) bool, bestEffort bool) error { - links, err := ds.GetLinks(ctx, root) - if bestEffort && err == ErrNotFound { - return nil - } else if err != nil { +type GetLinks func(context.Context, *cid.Cid) ([]*node.Link, error) +func EnumerateChildren(ctx context.Context, getLinks GetLinks, root *cid.Cid, visit func(*cid.Cid) bool) error { + links, err := getLinks(ctx, root) + if err != nil { return err } for _, lnk := range links { c := lnk.Cid if visit(c) { - err = EnumerateChildren(ctx, ds, c, visit, bestEffort) + err = EnumerateChildren(ctx, getLinks, c, visit) if err != nil { return err } diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index e7cfc8891..27eaec05a 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -249,7 +249,7 @@ func TestFetchGraph(t *testing.T) { offline_ds := NewDAGService(bs) - err = EnumerateChildren(context.Background(), offline_ds, root.Cid(), func(_ *cid.Cid) bool { return true }, false) + err = EnumerateChildren(context.Background(), offline_ds.GetLinks, root.Cid(), func(_ *cid.Cid) bool { return true }) if err != nil { t.Fatal(err) } @@ -266,7 +266,7 @@ func TestEnumerateChildren(t *testing.T) { } set := cid.NewSet() - err = EnumerateChildren(context.Background(), ds, root.Cid(), set.Visit, false) + err = EnumerateChildren(context.Background(), ds.GetLinks, root.Cid(), set.Visit) if err != nil { t.Fatal(err) } From 86e81b41f32aae3312637a21a471c7af8aaca7a1 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 16 Feb 2017 20:39:37 -0500 Subject: [PATCH 1743/3817] Refactor EnumerateChildren to avoid need for bestEffort parameter. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@5592a13db4704321f0bdf1610975ecf3238e2a16 --- pinning/pinner/gc/gc.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 2c99cb502..91bdde299 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -9,6 +9,7 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) var log = logging.Logger("gc") @@ -68,12 +69,12 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. return output, nil } -func Descendants(ctx context.Context, ls dag.LinkService, set *cid.Set, roots []*cid.Cid, bestEffort bool) error { +func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots []*cid.Cid) error { for _, c := range roots { set.Add(c) // EnumerateChildren recursively walks the dag and adds the keys to the given set - err := dag.EnumerateChildren(ctx, ls, c, set.Visit, bestEffort) + err := dag.EnumerateChildren(ctx, getLinks, c, set.Visit) if err != nil { return err } @@ -86,12 +87,19 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffo // KeySet currently implemented in memory, in the future, may be bloom filter or // disk backed to conserve memory. gcs := cid.NewSet() - err := Descendants(ctx, ls, gcs, pn.RecursiveKeys(), false) + err := Descendants(ctx, ls.GetLinks, gcs, pn.RecursiveKeys()) if err != nil { return nil, err } - err = Descendants(ctx, ls, gcs, bestEffortRoots, true) + bestEffortGetLinks := func(ctx context.Context, cid *cid.Cid) ([]*node.Link, error) { + links, err := ls.GetLinks(ctx, cid) + if err == dag.ErrNotFound { + err = nil + } + return links, err + } + err = Descendants(ctx, bestEffortGetLinks, gcs, bestEffortRoots) if err != nil { return nil, err } @@ -100,7 +108,7 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffo gcs.Add(k) } - err = Descendants(ctx, ls, gcs, pn.InternalPins(), false) + err = Descendants(ctx, ls.GetLinks, gcs, pn.InternalPins()) if err != nil { return nil, err } From 76c799c75781aa715ab6ce05464eab7c57a97b29 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 16 Feb 2017 21:29:50 -0500 Subject: [PATCH 1744/3817] Refactor EnumerateChildrenAsync to take in a function to get the links. For now it is always called with the helper function GetLinksDirect to avoid any change in behaviour. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@26f7ee2f6cdfcab2f7e694eb8ecc6317e3d62ca9 --- ipld/merkledag/merkledag.go | 31 ++++++++++++++++++++++--------- ipld/merkledag/merkledag_test.go | 2 +- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 1cf36f2cb..7b9b2a8e7 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -138,11 +138,23 @@ func (n *dagService) Remove(nd node.Node) error { return n.Blocks.DeleteBlock(nd) } +// get the links for a node, from the node, bypassing the +// LinkService +func GetLinksDirect(serv DAGService) GetLinks { + return func(ctx context.Context, c *cid.Cid) ([]*node.Link, error) { + node, err := serv.Get(ctx, c) + if err != nil { + return nil, err + } + return node.Links(), nil + } +} + // FetchGraph fetches all nodes that are children of the given node func FetchGraph(ctx context.Context, root *cid.Cid, serv DAGService) error { v, _ := ctx.Value("progress").(*ProgressTracker) if v == nil { - return EnumerateChildrenAsync(ctx, serv, root, cid.NewSet().Visit) + return EnumerateChildrenAsync(ctx, GetLinksDirect(serv), root, cid.NewSet().Visit) } set := cid.NewSet() visit := func(c *cid.Cid) bool { @@ -153,7 +165,7 @@ func FetchGraph(ctx context.Context, root *cid.Cid, serv DAGService) error { return false } } - return EnumerateChildrenAsync(ctx, serv, root, visit) + return EnumerateChildrenAsync(ctx, GetLinksDirect(serv), root, visit) } // FindLinks searches this nodes links for the given key, @@ -380,10 +392,11 @@ func (t *Batch) Commit() error { return err } +type GetLinks func(context.Context, *cid.Cid) ([]*node.Link, error) + // EnumerateChildren will walk the dag below the given root node and add all // unseen children to the passed in set. // TODO: parallelize to avoid disk latency perf hits? -type GetLinks func(context.Context, *cid.Cid) ([]*node.Link, error) func EnumerateChildren(ctx context.Context, getLinks GetLinks, root *cid.Cid, visit func(*cid.Cid) bool) error { links, err := getLinks(ctx, root) if err != nil { @@ -426,9 +439,9 @@ func (p *ProgressTracker) Value() int { // 'fetchNodes' will start at a time var FetchGraphConcurrency = 8 -func EnumerateChildrenAsync(ctx context.Context, ds DAGService, c *cid.Cid, visit func(*cid.Cid) bool) error { +func EnumerateChildrenAsync(ctx context.Context, getLinks GetLinks, c *cid.Cid, visit func(*cid.Cid) bool) error { feed := make(chan *cid.Cid) - out := make(chan node.Node) + out := make(chan []*node.Link) done := make(chan struct{}) var setlk sync.Mutex @@ -441,7 +454,7 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, c *cid.Cid, visi for i := 0; i < FetchGraphConcurrency; i++ { go func() { for ic := range feed { - n, err := ds.Get(ctx, ic) + links, err := getLinks(ctx, ic) if err != nil { errChan <- err return @@ -453,7 +466,7 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, c *cid.Cid, visi if unseen { select { - case out <- n: + case out <- links: case <-fetchersCtx.Done(): return } @@ -488,8 +501,8 @@ func EnumerateChildrenAsync(ctx context.Context, ds DAGService, c *cid.Cid, visi if inProgress == 0 && next == nil { return nil } - case nd := <-out: - for _, lnk := range nd.Links() { + case links := <-out: + for _, lnk := range links { if next == nil { next = lnk.Cid send = feed diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 27eaec05a..d5de2fe9d 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -543,7 +543,7 @@ func TestEnumerateAsyncFailsNotFound(t *testing.T) { } cset := cid.NewSet() - err = EnumerateChildrenAsync(context.Background(), ds, pcid, cset.Visit) + err = EnumerateChildrenAsync(context.Background(), GetLinksDirect(ds), pcid, cset.Visit) if err == nil { t.Fatal("this should have failed") } From ae5e38bdf796657d8ab3f692c1867b6f15517fc8 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 21 Feb 2017 12:48:03 -0500 Subject: [PATCH 1745/3817] Add some documentation on the intended purpose of GetLinks. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@ec392edf5e5e9f47098ad027b1922afccdf11d8c --- ipld/merkledag/merkledag.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 7b9b2a8e7..f89698c72 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -36,8 +36,10 @@ type DAGService interface { } type LinkService interface { - // Return all links for a node, may be more effect than - // calling Get in DAGService + // GetLinks return all links for a node. The complete node does not + // necessarily have to exist locally, or at all. For example, raw + // leaves cannot possibly have links so there is no need to look + // at the node. GetLinks(context.Context, *cid.Cid) ([]*node.Link, error) GetOfflineLinkService() LinkService @@ -114,6 +116,8 @@ func decodeBlock(b blocks.Block) (node.Node, error) { } } +// GetLinks return the links for the node, the node doesn't necessarily have +// to exist locally. func (n *dagService) GetLinks(ctx context.Context, c *cid.Cid) ([]*node.Link, error) { if c.Type() == cid.Raw { return nil, nil @@ -138,8 +142,9 @@ func (n *dagService) Remove(nd node.Node) error { return n.Blocks.DeleteBlock(nd) } -// get the links for a node, from the node, bypassing the -// LinkService +// GetLinksDirect creates a function to get the links for a node, from +// the node, bypassing the LinkService. If the node does not exist +// locally (and can not be retrieved) an error will be returned. func GetLinksDirect(serv DAGService) GetLinks { return func(ctx context.Context, c *cid.Cid) ([]*node.Link, error) { node, err := serv.Get(ctx, c) From d7ba73933058bf9a9b810f40682a197fd6ceba51 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 5 Mar 2017 23:06:04 -0800 Subject: [PATCH 1746/3817] update go-libp2p-kad-dht with getclosestpeers fix License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@414c48365f549270ec85f54891a89325b185f51e --- namesys/interface.go | 2 +- namesys/ipns_select_test.go | 2 +- namesys/namesys.go | 6 +++--- namesys/namesys_test.go | 2 +- namesys/publisher.go | 10 +++++----- namesys/republisher/repub.go | 8 ++++---- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 4 ++-- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index ac2307e76..abbc3c676 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,7 +35,7 @@ import ( context "context" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" + ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" ) const ( diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index 84250baa3..9bc856dd4 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -10,7 +10,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" + ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" ) diff --git a/namesys/namesys.go b/namesys/namesys.go index 78b406d42..dbc9bfdf6 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,10 +7,10 @@ import ( path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" + ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" - routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" + routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" + peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index ca99ff799..030dd8bfc 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,7 +10,7 @@ import ( offroute "github.com/ipfs/go-ipfs/routing/offline" "github.com/ipfs/go-ipfs/unixfs" - ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" + ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 8d01937d7..d80561237 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -14,14 +14,14 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ft "github.com/ipfs/go-ipfs/unixfs" - ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" + ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" + peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" - routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" - record "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record" - dhtpb "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record/pb" u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + record "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record" + dhtpb "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record/pb" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 89e32033d..622066e70 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,15 +11,15 @@ import ( path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" + peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" - routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" - recpb "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record/pb" + recpb "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record/pb" + pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index aeffa790b..675a43675 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" - mocknet "gx/ipfs/QmU3g3psEDiC4tQh1Qu2NYg5aYVQqxC3m74ZavLwPfJEtu/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" + mocknet "gx/ipfs/QmeWJwi61vii5g8zQUB9UGegfUbmhTKHgeDFP9XuSp5jZ4/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 5bc4b3e07..462168f56 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -12,7 +12,7 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" + peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index 89b507261..11236bbcb 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -9,12 +9,12 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" + ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" mh "gx/ipfs/QmbZ6Cee2uHjG7hf19qLHppgKDRtaG4CVtMzdmK9VCVqLu/go-multihash" ) From b081a313c65d22946bdb49886294b9cc07d4db1a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 5 Mar 2017 23:06:04 -0800 Subject: [PATCH 1747/3817] update go-libp2p-kad-dht with getclosestpeers fix License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-keystore@beb8ea4e790cdcd9e0a1586b3a8c5d97191d936e --- keystore/keystore.go | 2 +- keystore/keystore_test.go | 2 +- keystore/memkeystore.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index 761cd514d..b52dabdea 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -7,7 +7,7 @@ import ( "path/filepath" "strings" - ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" + ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" ) type Keystore interface { diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index 12dd6d29b..58b699888 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -7,7 +7,7 @@ import ( "sort" "testing" - ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" + ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" ) type rr struct{} diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index a6462913b..2351f54d8 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -1,6 +1,6 @@ package keystore -import ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" +import ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" type MemKeystore struct { keys map[string]ci.PrivKey From 360edf92cc20b19c3d272ee57ee80a126eb8a4ab Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 5 Mar 2017 23:06:04 -0800 Subject: [PATCH 1748/3817] update go-libp2p-kad-dht with getclosestpeers fix License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@7aaad8204d85140d5c9cdcd641ed9c2031511a95 --- routing/mock/centralized_client.go | 8 ++++---- routing/mock/centralized_server.go | 4 ++-- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 4 ++-- routing/mock/interface.go | 4 ++-- routing/none/none_client.go | 8 ++++---- routing/offline/offline.go | 12 ++++++------ routing/supernode/client.go | 18 +++++++++--------- routing/supernode/proxy/loopback.go | 6 +++--- routing/supernode/proxy/standard.go | 14 +++++++------- routing/supernode/server.go | 10 +++++----- routing/supernode/server_test.go | 2 +- 12 files changed, 46 insertions(+), 46 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index b9802adcd..eb2c183bb 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,16 +8,16 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" "github.com/ipfs/go-ipfs/thirdparty/testutil" - pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" ma "gx/ipfs/QmSWLfmj5frN9xVLMMN846dMDriy5wN5jeghUm7aTW3DAG/go-multiaddr" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" - routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" - dhtpb "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record/pb" u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + dhtpb "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record/pb" + pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 1e83ddb6b..936c06f14 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -8,11 +8,11 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" - pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" + peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" + pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index f6f945ddb..35a51f16c 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,9 +8,9 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 95a9931f9..3bc799a89 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -5,8 +5,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - mocknet "gx/ipfs/QmU3g3psEDiC4tQh1Qu2NYg5aYVQqxC3m74ZavLwPfJEtu/go-libp2p/p2p/net/mock" - dht "gx/ipfs/QmUpZqxzrUoyDsgWXDri9yYgi5r5EK7J5Tan1MbgnawYLx/go-libp2p-kad-dht" + dht "gx/ipfs/QmaoxFZcgwGyoB57pCYQobejLoNgqaA6trr3zxxrbm4UXe/go-libp2p-kad-dht" + mocknet "gx/ipfs/QmeWJwi61vii5g8zQUB9UGegfUbmhTKHgeDFP9XuSp5jZ4/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 63e4dd5a8..96a5a6f4e 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -11,8 +11,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" - routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" + routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" + peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" ) // Server provides mockrouting Clients diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 5818b5593..e48ffffda 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,12 +6,12 @@ import ( repo "github.com/ipfs/go-ipfs/repo" - pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" - routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" - p2phost "gx/ipfs/QmbzbRyd22gcW92U1rA2yKagB3myMYhk45XBknJ49F9XWJ/go-libp2p-host" + peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" + p2phost "gx/ipfs/QmXzeAcmKDTfNZQBiyF22hQKuTK7P5z6MBBQLTk9bbiSUc/go-libp2p-host" + pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 1ce0da1e7..5515e49f9 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,16 +7,16 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - ci "gx/ipfs/QmNiCwBNA8MWDADTFVq1BonUEJbS2SvjAoNkZZrhEwcuUi/go-libp2p-crypto" - pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" + ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" - routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" - record "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record" - pb "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record/pb" + record "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record" + pb "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record/pb" + pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" ) var log = logging.Logger("offlinerouting") diff --git a/routing/supernode/client.go b/routing/supernode/client.go index fb4a8ddda..d96ed3ebc 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,16 +8,16 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - loggables "gx/ipfs/QmTcfnDHimxBJqx6utpnWqVHdvyquXgkwAvYt4zMaJMKS2/go-libp2p-loggables" - dhtpb "gx/ipfs/QmUpZqxzrUoyDsgWXDri9yYgi5r5EK7J5Tan1MbgnawYLx/go-libp2p-kad-dht/pb" + routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" + loggables "gx/ipfs/QmXs1igHHEaUmMxKtbP8Z9wTjitQ75sqxaKQP4QgnLN4nn/go-libp2p-loggables" + "gx/ipfs/QmXzeAcmKDTfNZQBiyF22hQKuTK7P5z6MBBQLTk9bbiSUc/go-libp2p-host" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" - routing "gx/ipfs/QmZghcVHwXQC3Zvnvn24LgTmSPkEn2o3PDyKb6nrtPRzRh/go-libp2p-routing" - pb "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record/pb" - "gx/ipfs/QmbzbRyd22gcW92U1rA2yKagB3myMYhk45XBknJ49F9XWJ/go-libp2p-host" + dhtpb "gx/ipfs/QmaoxFZcgwGyoB57pCYQobejLoNgqaA6trr3zxxrbm4UXe/go-libp2p-kad-dht/pb" + pb "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record/pb" + pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" ) var log = logging.Logger("supernode") @@ -56,7 +56,7 @@ func (c *Client) FindProvidersAsync(ctx context.Context, k *cid.Cid, max int) <- case <-ctx.Done(): log.Debug(ctx.Err()) return - case ch <- p: + case ch <- *p: } } }() @@ -125,7 +125,7 @@ func (c *Client) FindPeer(ctx context.Context, id peer.ID) (pstore.PeerInfo, err } for _, p := range dhtpb.PBPeersToPeerInfos(response.GetCloserPeers()) { if p.ID == id { - return p, nil + return *p, nil } } return pstore.PeerInfo{}, errors.New("could not find peer") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index a7aaa2b75..f2fa36242 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,10 +2,10 @@ package proxy import ( context "context" - inet "gx/ipfs/QmRuZnMorqodado1yeTQiv1i9rmtKj29CjPSsBKM7DFXV4/go-libp2p-net" - dhtpb "gx/ipfs/QmUpZqxzrUoyDsgWXDri9yYgi5r5EK7J5Tan1MbgnawYLx/go-libp2p-kad-dht/pb" + inet "gx/ipfs/QmVtMT3fD7DzQNW7hdm6Xe6KPstzcggrhNpeVZ4422UpKK/go-libp2p-net" + peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" + dhtpb "gx/ipfs/QmaoxFZcgwGyoB57pCYQobejLoNgqaA6trr3zxxrbm4UXe/go-libp2p-kad-dht/pb" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index a5ae9061e..69b8812af 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,15 +4,15 @@ import ( "context" "errors" - pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" - inet "gx/ipfs/QmRuZnMorqodado1yeTQiv1i9rmtKj29CjPSsBKM7DFXV4/go-libp2p-net" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - loggables "gx/ipfs/QmTcfnDHimxBJqx6utpnWqVHdvyquXgkwAvYt4zMaJMKS2/go-libp2p-loggables" - dhtpb "gx/ipfs/QmUpZqxzrUoyDsgWXDri9yYgi5r5EK7J5Tan1MbgnawYLx/go-libp2p-kad-dht/pb" - kbucket "gx/ipfs/QmUwZcbSVMsLZzovZssH96rCUM5FAkrjaqhHLhJnFYd5z3/go-libp2p-kbucket" + kbucket "gx/ipfs/QmTxn7JEA8DiBvd9vVzErAzadHn6TwjCKTjjUfPyRH9wjZ/go-libp2p-kbucket" + inet "gx/ipfs/QmVtMT3fD7DzQNW7hdm6Xe6KPstzcggrhNpeVZ4422UpKK/go-libp2p-net" + peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" + loggables "gx/ipfs/QmXs1igHHEaUmMxKtbP8Z9wTjitQ75sqxaKQP4QgnLN4nn/go-libp2p-loggables" + host "gx/ipfs/QmXzeAcmKDTfNZQBiyF22hQKuTK7P5z6MBBQLTk9bbiSUc/go-libp2p-host" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" - host "gx/ipfs/QmbzbRyd22gcW92U1rA2yKagB3myMYhk45XBknJ49F9XWJ/go-libp2p-host" + dhtpb "gx/ipfs/QmaoxFZcgwGyoB57pCYQobejLoNgqaA6trr3zxxrbm4UXe/go-libp2p-kad-dht/pb" + pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 25b20724a..7744237ad 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,13 +8,13 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - pstore "gx/ipfs/QmQMQ2RUjnaEEX8ybmrhuFFGhAwPjyL1Eo6ZoJGD7aAccM/go-libp2p-peerstore" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dhtpb "gx/ipfs/QmUpZqxzrUoyDsgWXDri9yYgi5r5EK7J5Tan1MbgnawYLx/go-libp2p-kad-dht/pb" + peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmZcUPvPhD1Xvk6mwijYF8AfR3mG31S1YsEfHG4khrFPRr/go-libp2p-peer" - record "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record" - pb "gx/ipfs/QmZp9q8DbrGLztoxpkTC62mnRayRwHcAzGJJ8AvYRwjanR/go-libp2p-record/pb" + dhtpb "gx/ipfs/QmaoxFZcgwGyoB57pCYQobejLoNgqaA6trr3zxxrbm4UXe/go-libp2p-kad-dht/pb" + record "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record" + pb "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record/pb" + pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" ) // Server handles routing queries using a database backend diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index b4b49e05a..d08e157bb 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -4,7 +4,7 @@ import ( "testing" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dhtpb "gx/ipfs/QmUpZqxzrUoyDsgWXDri9yYgi5r5EK7J5Tan1MbgnawYLx/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmaoxFZcgwGyoB57pCYQobejLoNgqaA6trr3zxxrbm4UXe/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 64f1fc45db2b5a1cc7aea0f45306eaf7894f8f0a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 20 Jan 2017 10:48:23 -0800 Subject: [PATCH 1749/3817] Implement basic filestore 'no-copy' functionality License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-filestore@57285e5ea0aabd3e90ecf2d0be6979cadf4f5ad9 --- filestore/filestore.go | 169 ++++++++++++++++++++++++++++++++++ filestore/filestore_test.go | 104 +++++++++++++++++++++ filestore/fsrefstore.go | 177 ++++++++++++++++++++++++++++++++++++ filestore/pb/Makefile | 10 ++ filestore/pb/dataobj.pb.go | 67 ++++++++++++++ filestore/pb/dataobj.proto | 9 ++ 6 files changed, 536 insertions(+) create mode 100644 filestore/filestore.go create mode 100644 filestore/filestore_test.go create mode 100644 filestore/fsrefstore.go create mode 100644 filestore/pb/Makefile create mode 100644 filestore/pb/dataobj.pb.go create mode 100644 filestore/pb/dataobj.proto diff --git a/filestore/filestore.go b/filestore/filestore.go new file mode 100644 index 000000000..668b6149c --- /dev/null +++ b/filestore/filestore.go @@ -0,0 +1,169 @@ +package filestore + +import ( + "context" + + "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-ipfs/blocks/blockstore" + posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" + + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" +) + +var log = logging.Logger("filestore") + +type Filestore struct { + fm *FileManager + bs blockstore.Blockstore +} + +func NewFilestore(bs blockstore.Blockstore, fm *FileManager) *Filestore { + return &Filestore{fm, bs} +} + +func (f *Filestore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { + ctx, cancel := context.WithCancel(ctx) + + a, err := f.bs.AllKeysChan(ctx) + if err != nil { + return nil, err + } + + out := make(chan *cid.Cid) + go func() { + defer cancel() + defer close(out) + + var done bool + for !done { + select { + case c, ok := <-a: + if !ok { + done = true + continue + } + select { + case out <- c: + case <-ctx.Done(): + return + } + case <-ctx.Done(): + return + } + } + + // Can't do these at the same time because the abstractions around + // leveldb make us query leveldb for both operations. We apparently + // cant query leveldb concurrently + b, err := f.fm.AllKeysChan(ctx) + if err != nil { + log.Error("error querying filestore: ", err) + return + } + + done = false + for !done { + select { + case c, ok := <-b: + if !ok { + done = true + continue + } + select { + case out <- c: + case <-ctx.Done(): + return + } + case <-ctx.Done(): + return + } + } + }() + return out, nil +} + +func (f *Filestore) DeleteBlock(c *cid.Cid) error { + err1 := f.bs.DeleteBlock(c) + if err1 != nil && err1 != blockstore.ErrNotFound { + return err1 + } + + if err2 := f.fm.DeleteBlock(c); err2 != nil { + // if we successfully removed something from the blockstore, but the + // filestore didnt have it, return success + if err1 == nil && err2 != blockstore.ErrNotFound { + return nil + } + return err2 + } + + return nil +} + +func (f *Filestore) Get(c *cid.Cid) (blocks.Block, error) { + blk, err := f.bs.Get(c) + switch err { + default: + return nil, err + case nil: + return blk, nil + case blockstore.ErrNotFound: + // try filestore + } + + return f.fm.Get(c) +} + +func (f *Filestore) Has(c *cid.Cid) (bool, error) { + has, err := f.bs.Has(c) + if err != nil { + return false, err + } + + if has { + return true, nil + } + + return f.fm.Has(c) +} + +func (f *Filestore) Put(b blocks.Block) error { + switch b := b.(type) { + case *posinfo.FilestoreNode: + return f.fm.Put(b) + default: + return f.bs.Put(b) + } +} + +func (f *Filestore) PutMany(bs []blocks.Block) error { + var normals []blocks.Block + var fstores []*posinfo.FilestoreNode + + for _, b := range bs { + switch b := b.(type) { + case *posinfo.FilestoreNode: + fstores = append(fstores, b) + default: + normals = append(normals, b) + } + } + + if len(normals) > 0 { + err := f.bs.PutMany(normals) + if err != nil { + return err + } + } + + if len(fstores) > 0 { + err := f.fm.PutMany(fstores) + if err != nil { + return err + } + } + return nil +} + +var _ blockstore.Blockstore = (*Filestore)(nil) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go new file mode 100644 index 000000000..87f180003 --- /dev/null +++ b/filestore/filestore_test.go @@ -0,0 +1,104 @@ +package filestore + +import ( + "bytes" + "context" + "io/ioutil" + "math/rand" + "testing" + + "github.com/ipfs/go-ipfs/blocks/blockstore" + dag "github.com/ipfs/go-ipfs/merkledag" + posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" + + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" +) + +func newTestFilestore(t *testing.T) (string, *Filestore) { + mds := ds.NewMapDatastore() + + testdir, err := ioutil.TempDir("", "filestore-test") + if err != nil { + t.Fatal(err) + } + fm := NewFileManager(mds, testdir) + + bs := blockstore.NewBlockstore(mds) + fstore := NewFilestore(bs, fm) + return testdir, fstore +} + +func makeFile(dir string, data []byte) (string, error) { + f, err := ioutil.TempFile(dir, "file") + if err != nil { + return "", err + } + + _, err = f.Write(data) + if err != nil { + return "", err + } + + return f.Name(), nil +} + +func TestBasicFilestore(t *testing.T) { + dir, fs := newTestFilestore(t) + + buf := make([]byte, 1000) + rand.Read(buf) + + fname, err := makeFile(dir, buf) + if err != nil { + t.Fatal(err) + } + + var cids []*cid.Cid + for i := 0; i < 100; i++ { + n := &posinfo.FilestoreNode{ + PosInfo: &posinfo.PosInfo{ + FullPath: fname, + Offset: uint64(i * 10), + }, + Node: dag.NewRawNode(buf[i*10 : (i+1)*10]), + } + + err := fs.Put(n) + if err != nil { + t.Fatal(err) + } + cids = append(cids, n.Node.Cid()) + } + + for i, c := range cids { + blk, err := fs.Get(c) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(blk.RawData(), buf[i*10:(i+1)*10]) { + t.Fatal("data didnt match on the way out") + } + } + + kch, err := fs.AllKeysChan(context.Background()) + if err != nil { + t.Fatal(err) + } + + out := make(map[string]struct{}) + for c := range kch { + out[c.KeyString()] = struct{}{} + } + + if len(out) != len(cids) { + t.Fatal("mismatch in number of entries") + } + + for _, c := range cids { + if _, ok := out[c.KeyString()]; !ok { + t.Fatal("missing cid: ", c) + } + } +} diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go new file mode 100644 index 000000000..351c81124 --- /dev/null +++ b/filestore/fsrefstore.go @@ -0,0 +1,177 @@ +package filestore + +import ( + "context" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-ipfs/blocks/blockstore" + pb "github.com/ipfs/go-ipfs/filestore/pb" + dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" + + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dsns "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/namespace" + dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" + proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" +) + +var FilestorePrefix = ds.NewKey("filestore") + +type FileManager struct { + ds ds.Batching + root string +} + +type CorruptReferenceError struct { + Err error +} + +func (c CorruptReferenceError) Error() string { + return c.Err.Error() +} + +func NewFileManager(ds ds.Batching, root string) *FileManager { + return &FileManager{dsns.Wrap(ds, FilestorePrefix), root} +} + +func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { + q := dsq.Query{KeysOnly: true} + q.Prefix = FilestorePrefix.String() + + res, err := f.ds.Query(q) + if err != nil { + return nil, err + } + + out := make(chan *cid.Cid) + go func() { + defer close(out) + for { + v, ok := res.NextSync() + if !ok { + return + } + + k := ds.RawKey(v.Key) + c, err := dshelp.DsKeyToCid(k) + if err != nil { + log.Error("decoding cid from filestore: %s", err) + continue + } + + select { + case out <- c: + case <-ctx.Done(): + return + } + } + }() + + return out, nil +} + +func (f *FileManager) DeleteBlock(c *cid.Cid) error { + err := f.ds.Delete(dshelp.CidToDsKey(c)) + if err == ds.ErrNotFound { + return blockstore.ErrNotFound + } + return err +} + +func (f *FileManager) Get(c *cid.Cid) (blocks.Block, error) { + o, err := f.ds.Get(dshelp.CidToDsKey(c)) + switch err { + case ds.ErrNotFound: + return nil, blockstore.ErrNotFound + default: + return nil, err + case nil: + // + } + + data, ok := o.([]byte) + if !ok { + return nil, fmt.Errorf("stored filestore dataobj was not a []byte") + } + + var dobj pb.DataObj + if err := proto.Unmarshal(data, &dobj); err != nil { + return nil, err + } + + out, err := f.readDataObj(&dobj) + if err != nil { + return nil, err + } + + return blocks.NewBlockWithCid(out, c) +} + +func (f *FileManager) readDataObj(d *pb.DataObj) ([]byte, error) { + abspath := filepath.Join(f.root, d.GetFilePath()) + + fi, err := os.Open(abspath) + if err != nil { + return nil, &CorruptReferenceError{err} + } + defer fi.Close() + + _, err = fi.Seek(int64(d.GetOffset()), os.SEEK_SET) + if err != nil { + return nil, &CorruptReferenceError{err} + } + + outbuf := make([]byte, d.GetSize_()) + _, err = io.ReadFull(fi, outbuf) + if err != nil { + return nil, &CorruptReferenceError{err} + } + + return outbuf, nil +} + +func (f *FileManager) Has(c *cid.Cid) (bool, error) { + // NOTE: interesting thing to consider. Has doesnt validate the data. + // So the data on disk could be invalid, and we could think we have it. + dsk := dshelp.CidToDsKey(c) + return f.ds.Has(dsk) +} + +func (f *FileManager) Put(b *posinfo.FilestoreNode) error { + var dobj pb.DataObj + + if !filepath.HasPrefix(b.PosInfo.FullPath, f.root) { + return fmt.Errorf("cannot add filestore references outside ipfs root") + } + + p, err := filepath.Rel(f.root, b.PosInfo.FullPath) + if err != nil { + return err + } + + dobj.FilePath = proto.String(p) + dobj.Offset = proto.Uint64(b.PosInfo.Offset) + dobj.Size_ = proto.Uint64(uint64(len(b.RawData()))) + + data, err := proto.Marshal(&dobj) + if err != nil { + return err + } + + return f.ds.Put(dshelp.CidToDsKey(b.Cid()), data) +} + +func (f *FileManager) PutMany(bs []*posinfo.FilestoreNode) error { + // TODO: this better + for _, b := range bs { + if err := f.Put(b); err != nil { + return err + } + } + return nil +} diff --git a/filestore/pb/Makefile b/filestore/pb/Makefile new file mode 100644 index 000000000..5101a482d --- /dev/null +++ b/filestore/pb/Makefile @@ -0,0 +1,10 @@ +PB = $(wildcard *.proto) +GO = $(PB:.proto=.pb.go) + +all: $(GO) + +%.pb.go: %.proto + protoc --gogo_out=. $< + +clean: + rm *.pb.go diff --git a/filestore/pb/dataobj.pb.go b/filestore/pb/dataobj.pb.go new file mode 100644 index 000000000..6f1005add --- /dev/null +++ b/filestore/pb/dataobj.pb.go @@ -0,0 +1,67 @@ +// Code generated by protoc-gen-gogo. +// source: dataobj.proto +// DO NOT EDIT! + +/* +Package datastore_pb is a generated protocol buffer package. + +It is generated from these files: + dataobj.proto + +It has these top-level messages: + DataObj +*/ +package datastore_pb + +import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +type DataObj struct { + FilePath *string `protobuf:"bytes,1,opt,name=FilePath" json:"FilePath,omitempty"` + Offset *uint64 `protobuf:"varint,2,opt,name=Offset" json:"Offset,omitempty"` + Size_ *uint64 `protobuf:"varint,3,opt,name=Size" json:"Size,omitempty"` + Modtime *float64 `protobuf:"fixed64,4,opt,name=Modtime" json:"Modtime,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *DataObj) Reset() { *m = DataObj{} } +func (m *DataObj) String() string { return proto.CompactTextString(m) } +func (*DataObj) ProtoMessage() {} + +func (m *DataObj) GetFilePath() string { + if m != nil && m.FilePath != nil { + return *m.FilePath + } + return "" +} + +func (m *DataObj) GetOffset() uint64 { + if m != nil && m.Offset != nil { + return *m.Offset + } + return 0 +} + +func (m *DataObj) GetSize_() uint64 { + if m != nil && m.Size_ != nil { + return *m.Size_ + } + return 0 +} + +func (m *DataObj) GetModtime() float64 { + if m != nil && m.Modtime != nil { + return *m.Modtime + } + return 0 +} + +func init() { + proto.RegisterType((*DataObj)(nil), "datastore.pb.DataObj") +} diff --git a/filestore/pb/dataobj.proto b/filestore/pb/dataobj.proto new file mode 100644 index 000000000..a5364e5e0 --- /dev/null +++ b/filestore/pb/dataobj.proto @@ -0,0 +1,9 @@ +package datastore.pb; + +message DataObj { + optional string FilePath = 1; + optional uint64 Offset = 2; + optional uint64 Size = 3; + + optional double Modtime = 4; +} From 43b8ea320fcd0b9acae343c45dbb604234f0e8e2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 24 Jan 2017 14:17:29 -0800 Subject: [PATCH 1750/3817] use proper batching for filestore puts License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-filestore@ad3c5cc2dabbf6224cb9e89fcae5a3f7cda64267 --- filestore/fsrefstore.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 351c81124..7b63a039e 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -142,7 +142,15 @@ func (f *FileManager) Has(c *cid.Cid) (bool, error) { return f.ds.Has(dsk) } +type putter interface { + Put(ds.Key, interface{}) error +} + func (f *FileManager) Put(b *posinfo.FilestoreNode) error { + return f.putTo(b, f.ds) +} + +func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { var dobj pb.DataObj if !filepath.HasPrefix(b.PosInfo.FullPath, f.root) { @@ -163,15 +171,20 @@ func (f *FileManager) Put(b *posinfo.FilestoreNode) error { return err } - return f.ds.Put(dshelp.CidToDsKey(b.Cid()), data) + return to.Put(dshelp.CidToDsKey(b.Cid()), data) } func (f *FileManager) PutMany(bs []*posinfo.FilestoreNode) error { - // TODO: this better + batch, err := f.ds.Batch() + if err != nil { + return err + } + for _, b := range bs { - if err := f.Put(b); err != nil { + if err := f.putTo(b, batch); err != nil { return err } } - return nil + + return batch.Commit() } From 773463f40752b74aab9268a110b9f4313d5f1a73 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 24 Jan 2017 15:05:40 -0800 Subject: [PATCH 1751/3817] skip putting blocks we already have License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-filestore@699bd277cce3a35ded9c051dce8c2c8055d976a3 --- filestore/filestore.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/filestore/filestore.go b/filestore/filestore.go index 668b6149c..34a690422 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -129,6 +129,15 @@ func (f *Filestore) Has(c *cid.Cid) (bool, error) { } func (f *Filestore) Put(b blocks.Block) error { + has, err := f.Has(b.Cid()) + if err != nil { + return err + } + + if has { + return nil + } + switch b := b.(type) { case *posinfo.FilestoreNode: return f.fm.Put(b) @@ -142,6 +151,15 @@ func (f *Filestore) PutMany(bs []blocks.Block) error { var fstores []*posinfo.FilestoreNode for _, b := range bs { + has, err := f.Has(b.Cid()) + if err != nil { + return err + } + + if has { + continue + } + switch b := b.(type) { case *posinfo.FilestoreNode: fstores = append(fstores, b) From a51ad9d02d77bcbd2ecbf7e4d9163e492a7fadf5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 24 Jan 2017 15:46:20 -0800 Subject: [PATCH 1752/3817] fix delete logic License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-filestore@14df6d2ae902dc2e4d541b5134ea93f41a09792f --- filestore/filestore.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 34a690422..81bda68e3 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -89,16 +89,21 @@ func (f *Filestore) DeleteBlock(c *cid.Cid) error { return err1 } - if err2 := f.fm.DeleteBlock(c); err2 != nil { - // if we successfully removed something from the blockstore, but the - // filestore didnt have it, return success - if err1 == nil && err2 != blockstore.ErrNotFound { - return nil + err2 := f.fm.DeleteBlock(c) + // if we successfully removed something from the blockstore, but the + // filestore didnt have it, return success + + switch err2 { + case nil: + return nil + case blockstore.ErrNotFound: + if err1 == blockstore.ErrNotFound { + return blockstore.ErrNotFound } + return nil + default: return err2 } - - return nil } func (f *Filestore) Get(c *cid.Cid) (blocks.Block, error) { From 8e41cff71c71c8fa0689478ee2acc77768654e33 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 25 Jan 2017 12:20:32 -0800 Subject: [PATCH 1753/3817] add test for deletes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-filestore@0b252719deb507894a164531288f918fa0177480 --- filestore/filestore_test.go | 60 +++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 87f180003..2b30c7957 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -102,3 +102,63 @@ func TestBasicFilestore(t *testing.T) { } } } + +func randomFileAdd(t *testing.T, fs *Filestore, dir string, size int) (string, []*cid.Cid) { + buf := make([]byte, size) + rand.Read(buf) + + fname, err := makeFile(dir, buf) + if err != nil { + t.Fatal(err) + } + + var out []*cid.Cid + for i := 0; i < size/10; i++ { + n := &posinfo.FilestoreNode{ + PosInfo: &posinfo.PosInfo{ + FullPath: fname, + Offset: uint64(i * 10), + }, + Node: dag.NewRawNode(buf[i*10 : (i+1)*10]), + } + err := fs.Put(n) + if err != nil { + t.Fatal(err) + } + out = append(out, n.Cid()) + } + + return fname, out +} + +func TestDeletes(t *testing.T) { + dir, fs := newTestFilestore(t) + _, cids := randomFileAdd(t, fs, dir, 100) + todelete := cids[:4] + for _, c := range todelete { + err := fs.DeleteBlock(c) + if err != nil { + t.Fatal(err) + } + } + + deleted := make(map[string]bool) + for _, c := range todelete { + _, err := fs.Get(c) + if err != blockstore.ErrNotFound { + t.Fatal("expected blockstore not found error") + } + deleted[c.KeyString()] = true + } + + keys, err := fs.AllKeysChan(context.Background()) + if err != nil { + t.Fatal(err) + } + + for c := range keys { + if deleted[c.KeyString()] { + t.Fatal("shouldnt have reference to this key anymore") + } + } +} From 542fcdeb8eeab249df1b7ad7b9d6e6301ce8fc60 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 30 Jan 2017 00:48:15 -0800 Subject: [PATCH 1754/3817] validate data read from fsrefstore License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-filestore@98d19bb981400a104fd669b9edfbcfafc39050e0 --- filestore/fsrefstore.go | 14 ++++++++++++-- filestore/pb/dataobj.pb.go | 16 ++++------------ filestore/pb/dataobj.proto | 2 -- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 7b63a039e..5cca02d9a 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -109,11 +109,21 @@ func (f *FileManager) Get(c *cid.Cid) (blocks.Block, error) { return nil, err } + outcid, err := c.Prefix().Sum(out) + if err != nil { + return nil, err + } + + if !c.Equals(outcid) { + return nil, &CorruptReferenceError{fmt.Errorf("data in file did not match. %s offset %d", dobj.GetFilePath(), dobj.GetOffset())} + } + return blocks.NewBlockWithCid(out, c) } func (f *FileManager) readDataObj(d *pb.DataObj) ([]byte, error) { - abspath := filepath.Join(f.root, d.GetFilePath()) + p := filepath.FromSlash(d.GetFilePath()) + abspath := filepath.Join(f.root, p) fi, err := os.Open(abspath) if err != nil { @@ -162,7 +172,7 @@ func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { return err } - dobj.FilePath = proto.String(p) + dobj.FilePath = proto.String(filepath.ToSlash(p)) dobj.Offset = proto.Uint64(b.PosInfo.Offset) dobj.Size_ = proto.Uint64(uint64(len(b.RawData()))) diff --git a/filestore/pb/dataobj.pb.go b/filestore/pb/dataobj.pb.go index 6f1005add..fadd40c1a 100644 --- a/filestore/pb/dataobj.pb.go +++ b/filestore/pb/dataobj.pb.go @@ -23,11 +23,10 @@ var _ = fmt.Errorf var _ = math.Inf type DataObj struct { - FilePath *string `protobuf:"bytes,1,opt,name=FilePath" json:"FilePath,omitempty"` - Offset *uint64 `protobuf:"varint,2,opt,name=Offset" json:"Offset,omitempty"` - Size_ *uint64 `protobuf:"varint,3,opt,name=Size" json:"Size,omitempty"` - Modtime *float64 `protobuf:"fixed64,4,opt,name=Modtime" json:"Modtime,omitempty"` - XXX_unrecognized []byte `json:"-"` + FilePath *string `protobuf:"bytes,1,opt,name=FilePath" json:"FilePath,omitempty"` + Offset *uint64 `protobuf:"varint,2,opt,name=Offset" json:"Offset,omitempty"` + Size_ *uint64 `protobuf:"varint,3,opt,name=Size" json:"Size,omitempty"` + XXX_unrecognized []byte `json:"-"` } func (m *DataObj) Reset() { *m = DataObj{} } @@ -55,13 +54,6 @@ func (m *DataObj) GetSize_() uint64 { return 0 } -func (m *DataObj) GetModtime() float64 { - if m != nil && m.Modtime != nil { - return *m.Modtime - } - return 0 -} - func init() { proto.RegisterType((*DataObj)(nil), "datastore.pb.DataObj") } diff --git a/filestore/pb/dataobj.proto b/filestore/pb/dataobj.proto index a5364e5e0..c7d7f0eea 100644 --- a/filestore/pb/dataobj.proto +++ b/filestore/pb/dataobj.proto @@ -4,6 +4,4 @@ message DataObj { optional string FilePath = 1; optional uint64 Offset = 2; optional uint64 Size = 3; - - optional double Modtime = 4; } From dd2eb72250ffbc9497825c83bc054db1b01ab4ad Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 3 Feb 2017 16:21:35 -0500 Subject: [PATCH 1755/3817] Move block verification into readDataObj. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@509462e0b3373bafb78e736ef4c5c3ef5fb8e997 --- filestore/fsrefstore.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 5cca02d9a..f5e43ad7b 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -104,24 +104,16 @@ func (f *FileManager) Get(c *cid.Cid) (blocks.Block, error) { return nil, err } - out, err := f.readDataObj(&dobj) + out, err := f.readDataObj(c, &dobj) if err != nil { return nil, err } - outcid, err := c.Prefix().Sum(out) - if err != nil { - return nil, err - } - - if !c.Equals(outcid) { - return nil, &CorruptReferenceError{fmt.Errorf("data in file did not match. %s offset %d", dobj.GetFilePath(), dobj.GetOffset())} - } - return blocks.NewBlockWithCid(out, c) } -func (f *FileManager) readDataObj(d *pb.DataObj) ([]byte, error) { +// reads and verifies the block +func (f *FileManager) readDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { p := filepath.FromSlash(d.GetFilePath()) abspath := filepath.Join(f.root, p) @@ -142,6 +134,15 @@ func (f *FileManager) readDataObj(d *pb.DataObj) ([]byte, error) { return nil, &CorruptReferenceError{err} } + outcid, err := c.Prefix().Sum(outbuf) + if err != nil { + return nil, err + } + + if !c.Equals(outcid) { + return nil, &CorruptReferenceError{fmt.Errorf("data in file did not match. %s offset %d", d.GetFilePath(), d.GetOffset())} + } + return outbuf, nil } From ab697d8dc39eb4b5ae7b42dbac5f284fc2f14f61 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 3 Feb 2017 16:33:51 -0500 Subject: [PATCH 1756/3817] Refactor. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@eb4c49f5ba671d0ce3e25786dccfd996f6c93d93 --- filestore/fsrefstore.go | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index f5e43ad7b..382a186d6 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -84,6 +84,20 @@ func (f *FileManager) DeleteBlock(c *cid.Cid) error { } func (f *FileManager) Get(c *cid.Cid) (blocks.Block, error) { + dobj, err := f.getDataObj(c) + if err != nil { + return nil, err + } + + out, err := f.readDataObj(c, dobj) + if err != nil { + return nil, err + } + + return blocks.NewBlockWithCid(out, c) +} + +func (f *FileManager) getDataObj(c *cid.Cid) (*pb.DataObj, error) { o, err := f.ds.Get(dshelp.CidToDsKey(c)) switch err { case ds.ErrNotFound: @@ -104,12 +118,7 @@ func (f *FileManager) Get(c *cid.Cid) (blocks.Block, error) { return nil, err } - out, err := f.readDataObj(c, &dobj) - if err != nil { - return nil, err - } - - return blocks.NewBlockWithCid(out, c) + return &dobj, nil } // reads and verifies the block From 8b2ac73ae7f51f34e71f453e537c665f7e935a22 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 20 Jan 2017 10:48:23 -0800 Subject: [PATCH 1757/3817] Implement basic filestore 'no-copy' functionality License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@bb463151df6911c3bcd48ab91fa5fbc5ca61e7b1 --- blockstore/blockstore.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 17ab24b3e..e34c3a8ee 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -163,7 +163,11 @@ func (bs *blockstore) Has(k *cid.Cid) (bool, error) { } func (s *blockstore) DeleteBlock(k *cid.Cid) error { - return s.datastore.Delete(dshelp.CidToDsKey(k)) + err := s.datastore.Delete(dshelp.CidToDsKey(k)) + if err == ds.ErrNotFound { + return ErrNotFound + } + return err } // AllKeysChan runs a query for keys from the blockstore. From 7595e4a4835fa202556ab151b74ed836d50e03db Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 20 Jan 2017 10:48:23 -0800 Subject: [PATCH 1758/3817] Implement basic filestore 'no-copy' functionality License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@3a4b1d068c2c5958691ecd576b525053b279c356 --- pinning/pinner/gc/gc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 91bdde299..78289d028 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -51,7 +51,7 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. if !gcs.Has(k) { err := bs.DeleteBlock(k) if err != nil { - log.Debugf("Error removing key from blockstore: %s", err) + log.Errorf("Error removing key from blockstore: %s", err) return } select { From a1dc0b323aeb52eef9b72433db009299468fc39f Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 3 Feb 2017 18:42:11 -0500 Subject: [PATCH 1759/3817] Use buffered channels in AllKeysChan to increase performance. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@3bdb485d54ea5c7b133c4524867fe4ee54182421 --- filestore/filestore.go | 3 ++- filestore/fsrefstore.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 81bda68e3..eefd925e3 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -7,6 +7,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" + dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) @@ -30,7 +31,7 @@ func (f *Filestore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { return nil, err } - out := make(chan *cid.Cid) + out := make(chan *cid.Cid, dsq.KeysOnlyBufSize) go func() { defer cancel() defer close(out) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 382a186d6..f333a845d 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -48,7 +48,7 @@ func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) return nil, err } - out := make(chan *cid.Cid) + out := make(chan *cid.Cid, dsq.KeysOnlyBufSize) go func() { defer close(out) for { From e284d27962aed6101e84ff8be31be64ce56e2bf2 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sun, 19 Feb 2017 22:47:23 -0500 Subject: [PATCH 1760/3817] gc: collect all errors during ColoredSet phase License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@0bdedc0344a9a620f76b478e3c0cbec5d28ad6d5 --- pinning/pinner/gc/gc.go | 53 +++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 78289d028..a75128af8 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -1,6 +1,7 @@ package gc import ( + "bytes" "context" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" @@ -28,9 +29,9 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. ls = ls.GetOfflineLinkService() - gcs, err := ColoredSet(ctx, pn, ls, bestEffortRoots) - if err != nil { - return nil, err + gcs, errs := ColoredSet(ctx, pn, ls, bestEffortRoots) + if errs != nil { + return nil, &UnsafeToContinueError{errs} } keychan, err := bs.AllKeysChan(ctx) @@ -83,35 +84,61 @@ func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots return nil } -func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffortRoots []*cid.Cid) (*cid.Set, error) { +func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffortRoots []*cid.Cid) (*cid.Set, []error) { // KeySet currently implemented in memory, in the future, may be bloom filter or // disk backed to conserve memory. gcs := cid.NewSet() - err := Descendants(ctx, ls.GetLinks, gcs, pn.RecursiveKeys()) + var errors []error + getLinks := func(ctx context.Context, cid *cid.Cid) ([]*node.Link, error) { + links, err := ls.GetLinks(ctx, cid) + if err != nil { + errors = append(errors, err) + } + return links, nil + } + err := Descendants(ctx, getLinks, gcs, pn.RecursiveKeys()) if err != nil { - return nil, err + errors = append(errors, err) } bestEffortGetLinks := func(ctx context.Context, cid *cid.Cid) ([]*node.Link, error) { links, err := ls.GetLinks(ctx, cid) - if err == dag.ErrNotFound { - err = nil + if err != nil && err != dag.ErrNotFound { + errors = append(errors, err) } - return links, err + return links, nil } err = Descendants(ctx, bestEffortGetLinks, gcs, bestEffortRoots) if err != nil { - return nil, err + errors = append(errors, err) } for _, k := range pn.DirectKeys() { gcs.Add(k) } - err = Descendants(ctx, ls.GetLinks, gcs, pn.InternalPins()) + err = Descendants(ctx, getLinks, gcs, pn.InternalPins()) if err != nil { - return nil, err + errors = append(errors, err) + } + + if errors != nil { + return nil, errors + } else { + return gcs, nil } +} - return gcs, nil +type UnsafeToContinueError struct { + Errors []error +} + +func (e *UnsafeToContinueError) Error() string { + var buf bytes.Buffer + for _, err := range e.Errors { + buf.WriteString(err.Error()) + buf.WriteString("\n") + } + buf.WriteString("aborting due to previous errors") + return buf.String() } From a33b48dad891ce43c10ae011b9ae7c78999a5487 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 20 Feb 2017 18:00:57 -0500 Subject: [PATCH 1761/3817] gc: output all errors to a channel Errors from ColoredSet are now reported as encountered and errors encountered when deleting blocks are no longer ignored. License: MIT Signed-off-by: Kevin Atkinson gc: report errors from ColoredSet as encountered License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@c028ab33dfa7c09dc107228a78a18d130507ff58 --- pinning/pinner/gc/gc.go | 105 +++++++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 38 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index a75128af8..bc9a1e2dc 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -1,8 +1,9 @@ package gc import ( - "bytes" "context" + "errors" + "fmt" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" dag "github.com/ipfs/go-ipfs/merkledag" @@ -24,50 +25,65 @@ var log = logging.Logger("gc") // // The routine then iterates over every block in the blockstore and // deletes any block that is not found in the marked set. -func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin.Pinner, bestEffortRoots []*cid.Cid) (<-chan *cid.Cid, error) { +// +func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin.Pinner, bestEffortRoots []*cid.Cid) (<-chan *cid.Cid, <-chan error) { unlocker := bs.GCLock() - ls = ls.GetOfflineLinkService() - gcs, errs := ColoredSet(ctx, pn, ls, bestEffortRoots) - if errs != nil { - return nil, &UnsafeToContinueError{errs} - } - - keychan, err := bs.AllKeysChan(ctx) - if err != nil { - return nil, err - } - output := make(chan *cid.Cid) + errOutput := make(chan error) + go func() { + defer close(errOutput) defer close(output) defer unlocker.Unlock() + + gcs, err := ColoredSet(ctx, pn, ls, bestEffortRoots, errOutput) + if err != nil { + errOutput <- err + return + } + + keychan, err := bs.AllKeysChan(ctx) + if err != nil { + errOutput <- err + return + } + + errors := false + + loop: for { select { case k, ok := <-keychan: if !ok { - return + break loop } if !gcs.Has(k) { err := bs.DeleteBlock(k) if err != nil { - log.Errorf("Error removing key from blockstore: %s", err) - return + errors = true + errOutput <- &CouldNotDeleteBlockError{k, err} + //log.Errorf("Error removing key from blockstore: %s", err) + // continue as error is non-fatal + continue loop } select { case output <- k: case <-ctx.Done(): - return + break loop } } case <-ctx.Done(): - return + break loop } } + if errors { + errOutput <- ErrCouldNotDeleteSomeBlocks + } }() - return output, nil + return output, errOutput } func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots []*cid.Cid) error { @@ -84,33 +100,37 @@ func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots return nil } -func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffortRoots []*cid.Cid) (*cid.Set, []error) { +func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffortRoots []*cid.Cid, errOutput chan<- error) (*cid.Set, error) { // KeySet currently implemented in memory, in the future, may be bloom filter or // disk backed to conserve memory. + errors := false gcs := cid.NewSet() - var errors []error getLinks := func(ctx context.Context, cid *cid.Cid) ([]*node.Link, error) { links, err := ls.GetLinks(ctx, cid) if err != nil { - errors = append(errors, err) + errors = true + errOutput <- &CouldNotFetchLinksError{cid, err} } return links, nil } err := Descendants(ctx, getLinks, gcs, pn.RecursiveKeys()) if err != nil { - errors = append(errors, err) + errors = true + errOutput <- err } bestEffortGetLinks := func(ctx context.Context, cid *cid.Cid) ([]*node.Link, error) { links, err := ls.GetLinks(ctx, cid) if err != nil && err != dag.ErrNotFound { - errors = append(errors, err) + errors = true + errOutput <- &CouldNotFetchLinksError{cid, err} } return links, nil } err = Descendants(ctx, bestEffortGetLinks, gcs, bestEffortRoots) if err != nil { - errors = append(errors, err) + errors = true + errOutput <- err } for _, k := range pn.DirectKeys() { @@ -119,26 +139,35 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffo err = Descendants(ctx, getLinks, gcs, pn.InternalPins()) if err != nil { - errors = append(errors, err) + errors = true + errOutput <- err } - if errors != nil { - return nil, errors + if errors { + return nil, ErrCouldNotFetchAllLinks } else { return gcs, nil } } -type UnsafeToContinueError struct { - Errors []error +var ErrCouldNotFetchAllLinks = errors.New("garbage collection aborted: could not retrieve some links") + +var ErrCouldNotDeleteSomeBlocks = errors.New("garbage collection incomplete: could not delete some blocks") + +type CouldNotFetchLinksError struct { + Key *cid.Cid + Err error +} + +func (e *CouldNotFetchLinksError) Error() string { + return fmt.Sprintf("could not retrieve links for %s: %s", e.Key, e.Err) } -func (e *UnsafeToContinueError) Error() string { - var buf bytes.Buffer - for _, err := range e.Errors { - buf.WriteString(err.Error()) - buf.WriteString("\n") - } - buf.WriteString("aborting due to previous errors") - return buf.String() +type CouldNotDeleteBlockError struct { + Key *cid.Cid + Err error +} + +func (e *CouldNotDeleteBlockError) Error() string { + return fmt.Sprintf("could not remove %s: %s", e.Key, e.Err) } From 83f76db7eee2370d06250f875990cae5ec92fcc2 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 24 Feb 2017 14:47:47 -0500 Subject: [PATCH 1762/3817] gc: return Result instead of two channels License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@8fab9b4849e74790ccc426437815179de7b2ad27 --- pinning/pinner/gc/gc.go | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index bc9a1e2dc..ccf354b45 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -16,6 +16,11 @@ import ( var log = logging.Logger("gc") +type Result struct { + KeyRemoved *cid.Cid + Error error +} + // GC performs a mark and sweep garbage collection of the blocks in the blockstore // first, it creates a 'marked' set and adds to it the following: // - all recursively pinned blocks, plus all of their descendants (recursively) @@ -26,27 +31,25 @@ var log = logging.Logger("gc") // The routine then iterates over every block in the blockstore and // deletes any block that is not found in the marked set. // -func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin.Pinner, bestEffortRoots []*cid.Cid) (<-chan *cid.Cid, <-chan error) { +func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin.Pinner, bestEffortRoots []*cid.Cid) <-chan Result { unlocker := bs.GCLock() ls = ls.GetOfflineLinkService() - output := make(chan *cid.Cid) - errOutput := make(chan error) + output := make(chan Result, 128) go func() { - defer close(errOutput) defer close(output) defer unlocker.Unlock() - gcs, err := ColoredSet(ctx, pn, ls, bestEffortRoots, errOutput) + gcs, err := ColoredSet(ctx, pn, ls, bestEffortRoots, output) if err != nil { - errOutput <- err + output <- Result{Error: err} return } keychan, err := bs.AllKeysChan(ctx) if err != nil { - errOutput <- err + output <- Result{Error: err} return } @@ -63,13 +66,13 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. err := bs.DeleteBlock(k) if err != nil { errors = true - errOutput <- &CouldNotDeleteBlockError{k, err} + output <- Result{Error: &CouldNotDeleteBlockError{k, err}} //log.Errorf("Error removing key from blockstore: %s", err) // continue as error is non-fatal continue loop } select { - case output <- k: + case output <- Result{KeyRemoved: k}: case <-ctx.Done(): break loop } @@ -79,11 +82,11 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. } } if errors { - errOutput <- ErrCouldNotDeleteSomeBlocks + output <- Result{Error: ErrCouldNotDeleteSomeBlocks} } }() - return output, errOutput + return output } func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots []*cid.Cid) error { @@ -100,7 +103,7 @@ func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots return nil } -func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffortRoots []*cid.Cid, errOutput chan<- error) (*cid.Set, error) { +func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffortRoots []*cid.Cid, output chan<- Result) (*cid.Set, error) { // KeySet currently implemented in memory, in the future, may be bloom filter or // disk backed to conserve memory. errors := false @@ -109,28 +112,28 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffo links, err := ls.GetLinks(ctx, cid) if err != nil { errors = true - errOutput <- &CouldNotFetchLinksError{cid, err} + output <- Result{Error: &CouldNotFetchLinksError{cid, err}} } return links, nil } err := Descendants(ctx, getLinks, gcs, pn.RecursiveKeys()) if err != nil { errors = true - errOutput <- err + output <- Result{Error: err} } bestEffortGetLinks := func(ctx context.Context, cid *cid.Cid) ([]*node.Link, error) { links, err := ls.GetLinks(ctx, cid) if err != nil && err != dag.ErrNotFound { errors = true - errOutput <- &CouldNotFetchLinksError{cid, err} + output <- Result{Error: &CouldNotFetchLinksError{cid, err}} } return links, nil } err = Descendants(ctx, bestEffortGetLinks, gcs, bestEffortRoots) if err != nil { errors = true - errOutput <- err + output <- Result{Error: err} } for _, k := range pn.DirectKeys() { @@ -140,7 +143,7 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffo err = Descendants(ctx, getLinks, gcs, pn.InternalPins()) if err != nil { errors = true - errOutput <- err + output <- Result{Error: err} } if errors { From c02fbbf0a92f566a309228fec00c48b04bdbeec0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 6 Mar 2017 17:01:35 -0800 Subject: [PATCH 1763/3817] merkledag: limit number of objects in a batch to prevent out of fd issues License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@ebae432367f718841a6c713cd9cfd0b46d6620b0 --- ipld/merkledag/merkledag.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index f89698c72..606a90957 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -68,7 +68,15 @@ func (n *dagService) Add(nd node.Node) (*cid.Cid, error) { } func (n *dagService) Batch() *Batch { - return &Batch{ds: n, MaxSize: 8 * 1024 * 1024} + return &Batch{ + ds: n, + MaxSize: 8 << 20, + + // By default, only batch up to 128 nodes at a time. + // The current implementation of flatfs opens this many file + // descriptors at the same time for the optimized batch write. + MaxBlocks: 128, + } } // Get retrieves a node from the dagService, fetching the block in the BlockService @@ -376,15 +384,16 @@ func (np *nodePromise) Get(ctx context.Context) (node.Node, error) { type Batch struct { ds *dagService - blocks []blocks.Block - size int - MaxSize int + blocks []blocks.Block + size int + MaxSize int + MaxBlocks int } func (t *Batch) Add(nd node.Node) (*cid.Cid, error) { t.blocks = append(t.blocks, nd) t.size += len(nd.RawData()) - if t.size > t.MaxSize { + if t.size > t.MaxSize || len(t.blocks) > t.MaxBlocks { return nd.Cid(), t.Commit() } return nd.Cid(), nil From 8c91e506537a31a9f676ac1a7738fa65602a8963 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 6 Mar 2017 20:12:05 -0800 Subject: [PATCH 1764/3817] make raw leaves work with 'ipfs get' License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@a0f5ffd87e16688f755523dffbeb88d9ebdfdb86 --- unixfs/archive/archive.go | 7 ++-- unixfs/archive/tar/writer.go | 68 +++++++++++++++++++++--------------- 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index a94c9f7af..b39c71560 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -3,14 +3,15 @@ package archive import ( "bufio" "compress/gzip" + "context" "io" "path" - cxt "context" - mdag "github.com/ipfs/go-ipfs/merkledag" tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" uio "github.com/ipfs/go-ipfs/unixfs/io" + + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) // DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. @@ -30,7 +31,7 @@ func (i *identityWriteCloser) Close() error { } // DagArchive is equivalent to `ipfs getdag $hash | maybe_tar | maybe_gzip` -func DagArchive(ctx cxt.Context, nd *mdag.ProtoNode, name string, dag mdag.DAGService, archive bool, compression int) (io.Reader, error) { +func DagArchive(ctx context.Context, nd node.Node, name string, dag mdag.DAGService, archive bool, compression int) (io.Reader, error) { _, filename := path.Split(name) diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 17c43e717..26492d897 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -2,17 +2,19 @@ package tar import ( "archive/tar" + "context" + "fmt" "io" "path" "time" - cxt "context" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" upb "github.com/ipfs/go-ipfs/unixfs/pb" + + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) // Writer is a utility structure that helps to write @@ -22,11 +24,11 @@ type Writer struct { Dag mdag.DAGService TarW *tar.Writer - ctx cxt.Context + ctx context.Context } // NewWriter wraps given io.Writer. -func NewWriter(ctx cxt.Context, dag mdag.DAGService, archive bool, compression int, w io.Writer) (*Writer, error) { +func NewWriter(ctx context.Context, dag mdag.DAGService, archive bool, compression int, w io.Writer) (*Writer, error) { return &Writer{ Dag: dag, TarW: tar.NewWriter(w), @@ -45,13 +47,8 @@ func (w *Writer) writeDir(nd *mdag.ProtoNode, fpath string) error { return err } - childpb, ok := child.(*mdag.ProtoNode) - if !ok { - return mdag.ErrNotProtobuf - } - npath := path.Join(fpath, nd.Links()[i].Name) - if err := w.WriteNode(childpb, npath); err != nil { + if err := w.WriteNode(child, npath); err != nil { return err } } @@ -72,25 +69,40 @@ func (w *Writer) writeFile(nd *mdag.ProtoNode, pb *upb.Data, fpath string) error return nil } -func (w *Writer) WriteNode(nd *mdag.ProtoNode, fpath string) error { - pb := new(upb.Data) - if err := proto.Unmarshal(nd.Data(), pb); err != nil { - return err - } +func (w *Writer) WriteNode(nd node.Node, fpath string) error { + switch nd := nd.(type) { + case *mdag.ProtoNode: + pb := new(upb.Data) + if err := proto.Unmarshal(nd.Data(), pb); err != nil { + return err + } + + switch pb.GetType() { + case upb.Data_Metadata: + fallthrough + case upb.Data_Directory: + return w.writeDir(nd, fpath) + case upb.Data_Raw: + fallthrough + case upb.Data_File: + return w.writeFile(nd, pb, fpath) + case upb.Data_Symlink: + return writeSymlinkHeader(w.TarW, string(pb.GetData()), fpath) + default: + return ft.ErrUnrecognizedType + } + case *mdag.RawNode: + if err := writeFileHeader(w.TarW, fpath, uint64(len(nd.RawData()))); err != nil { + return err + } - switch pb.GetType() { - case upb.Data_Metadata: - fallthrough - case upb.Data_Directory: - return w.writeDir(nd, fpath) - case upb.Data_Raw: - fallthrough - case upb.Data_File: - return w.writeFile(nd, pb, fpath) - case upb.Data_Symlink: - return writeSymlinkHeader(w.TarW, string(pb.GetData()), fpath) + if _, err := w.TarW.Write(nd.RawData()); err != nil { + return err + } + w.TarW.Flush() + return nil default: - return ft.ErrUnrecognizedType + return fmt.Errorf("nodes of type %T are not supported in unixfs", nd) } } From b5c93d1470afc63122ca2a55ad8fd11ac0fc5f4f Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 14 Mar 2017 00:57:13 +0100 Subject: [PATCH 1765/3817] fix: remove bloom filter check on Put call in blockstore To prevent put we need to have conclusive information if item is contained in the repo, bloom filter won't give this information. It only says if it is for sure not contained. License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@985475b8f70698bcd6c80d3b54e3844931868546 --- blockstore/bloom_cache.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 63c1c368a..5c8c76ad5 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -142,10 +142,7 @@ func (b *bloomcache) Get(k *cid.Cid) (blocks.Block, error) { } func (b *bloomcache) Put(bl blocks.Block) error { - if has, ok := b.hasCached(bl.Cid()); ok && has { - return nil - } - + // See comment in PutMany err := b.blockstore.Put(bl) if err == nil { b.bloom.AddTS(bl.Cid().Bytes()) @@ -155,7 +152,7 @@ func (b *bloomcache) Put(bl blocks.Block) error { func (b *bloomcache) PutMany(bs []blocks.Block) error { // bloom cache gives only conclusive resulty if key is not contained - // to reduce number of puts we need conclusive infomration if block is contained + // to reduce number of puts we need conclusive information if block is contained // this means that PutMany can't be improved with bloom cache so we just // just do a passthrough. err := b.blockstore.PutMany(bs) From 295f1305501eabc7645b7d947d823148c3b0bb56 Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Wed, 16 Nov 2016 06:21:15 +0100 Subject: [PATCH 1766/3817] coreapi: smarter way of dealing with the different APIs License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/interface-go-ipfs-core@e69000d481d335aef4d610be31750e8558f3b795 --- coreiface/interface.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index b506e6509..7bf9d5c0a 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -9,11 +9,6 @@ import ( ipld "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) -// type CoreAPI interface { -// ID() CoreID -// Version() CoreVersion -// } - type Link ipld.Link type Reader interface { @@ -21,6 +16,10 @@ type Reader interface { io.Closer } +type CoreAPI interface { + Unixfs() UnixfsAPI +} + type UnixfsAPI interface { Add(context.Context, io.Reader) (*cid.Cid, error) Cat(context.Context, string) (Reader, error) From 61e07c830c0222fbd796118e7784e24ecf1c7e24 Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Fri, 17 Mar 2017 03:47:59 +0100 Subject: [PATCH 1767/3817] coreapi: make the interfaces path centric The new coreiface.Path maps a path to the cid.Cid resulting from a full path resolution. The path is internally represented as a go-ipfs/path.Path, but that doesn't matter to the outside. Apart from the path-to-CID mapping, it also aims to hold all resolved segment CIDs of the path. Right now it only exposes Root(), and only for flat paths a la /ipfs/Qmfoo. In other cases, the root is nil. In the future, resolution will internally use go-ipfs/path.Resolver.ResolvePathComponents and thus always return the proper resolved segments, via Root(), or a future Segments() func. - Add coreiface.Path with Cid() and Root(). - Add CoreAPI.ResolvePath() for getting a coreiface.Path. - All functions now expect and return coreiface.Path. - Add ParsePath() and ParseCid() for constructing a coreiface.Path. - Add coreiface.Node and Link which are simply go-ipld-node.Node and Link. - Add CoreAPI.ResolveNode() for getting a Node from a Path. License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/interface-go-ipfs-core@66af039105d5e4ebc82a178143d8643ad3fed91d --- coreiface/interface.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 7bf9d5c0a..d72fc8a3b 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -9,6 +9,16 @@ import ( ipld "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) +type Path interface { + String() string + Cid() *cid.Cid + Root() *cid.Cid + Resolved() bool +} + +// TODO: should we really copy these? +// if we didn't, godoc would generate nice links straight to go-ipld-node +type Node ipld.Node type Link ipld.Link type Reader interface { @@ -18,12 +28,14 @@ type Reader interface { type CoreAPI interface { Unixfs() UnixfsAPI + ResolvePath(context.Context, Path) (Path, error) + ResolveNode(context.Context, Path) (Node, error) } type UnixfsAPI interface { - Add(context.Context, io.Reader) (*cid.Cid, error) - Cat(context.Context, string) (Reader, error) - Ls(context.Context, string) ([]*Link, error) + Add(context.Context, io.Reader) (Path, error) + Cat(context.Context, Path) (Reader, error) + Ls(context.Context, Path) ([]*Link, error) } // type ObjectAPI interface { @@ -49,5 +61,4 @@ type UnixfsAPI interface { // } var ErrIsDir = errors.New("object is a directory") -var ErrIsNonDag = errors.New("not a merkledag object") var ErrOffline = errors.New("can't resolve, ipfs node is offline") From 2aa25233c700d98900bf73f515675cf1c39b19f3 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 16 Mar 2017 09:41:35 +0100 Subject: [PATCH 1768/3817] Docs: Improve Readme and make golint happy License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-util@5e06988d92bcaaae765b36db9b70de3afc66418f --- util/README.md | 18 ++++++++++++++++++ util/file.go | 1 + util/time.go | 5 +++++ util/util.go | 12 +++++++++++- 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/util/README.md b/util/README.md index 766f3812b..33bff12cd 100644 --- a/util/README.md +++ b/util/README.md @@ -10,8 +10,26 @@ ## Install +This is a Go module which can be installed with `go get github.com/ipfs/go-ipfs-util`. `go-ipfs-util` is however packaged with Gx, so it is recommended to use Gx to install it (see Usage section). + ## Usage +This module is packaged with [Gx](https://github.com/whyrusleeping/gx). +In order to use it in your own project do: + +``` +go get -u github.com/whyrusleeping/gx +go get -u github.com/whyrusleeping/gx-go +cd +gx init +gx import github.com/ipfs/go-ipfs-util +gx install --global +gx-go --rewrite +``` + +Please check [Gx](https://github.com/whyrusleeping/gx) and [Gx-go](https://github.com/whyrusleeping/gx-go) documentation for more information. + + ## Contribute Feel free to join in. All welcome. Open an [issue](https://github.com/ipfs/go-ipfs-util/issues)! diff --git a/util/file.go b/util/file.go index e3bd49d71..e6e30df4d 100644 --- a/util/file.go +++ b/util/file.go @@ -2,6 +2,7 @@ package util import "os" +// FileExists check if the file with the given path exits. func FileExists(filename string) bool { fi, err := os.Lstat(filename) if fi != nil || (err != nil && !os.IsNotExist(err)) { diff --git a/util/time.go b/util/time.go index 5fc6ec66d..37d720fb1 100644 --- a/util/time.go +++ b/util/time.go @@ -2,8 +2,11 @@ package util import "time" +// TimeFormatIpfs is the format ipfs uses to represent time in string form. var TimeFormatIpfs = time.RFC3339Nano +// ParseRFC3339 parses an RFC3339Nano-formatted time stamp and +// returns the UTC time. func ParseRFC3339(s string) (time.Time, error) { t, err := time.Parse(TimeFormatIpfs, s) if err != nil { @@ -12,6 +15,8 @@ func ParseRFC3339(s string) (time.Time, error) { return t.UTC(), nil } +// FormatRFC3339 returns the string representation of the +// UTC value of the given time in RFC3339Nano format. func FormatRFC3339(t time.Time) string { return t.UTC().Format(TimeFormatIpfs) } diff --git a/util/util.go b/util/util.go index 28873fd02..fb4dd9828 100644 --- a/util/util.go +++ b/util/util.go @@ -28,7 +28,7 @@ var ErrNotImplemented = errors.New("Error: not implemented yet.") // ErrTimeout implies that a timeout has been triggered var ErrTimeout = errors.New("Error: Call timed out.") -// ErrSeErrSearchIncomplete implies that a search type operation didnt +// ErrSearchIncomplete implies that a search type operation didnt // find the expected node, but did find 'a' node. var ErrSearchIncomplete = errors.New("Error: Search Incomplete.") @@ -57,6 +57,8 @@ type randGen struct { rand.Rand } +// NewTimeSeededRand returns a random bytes reader +// which has been initialized with the current time. func NewTimeSeededRand() io.Reader { src := rand.NewSource(time.Now().UnixNano()) return &randGen{ @@ -64,6 +66,8 @@ func NewTimeSeededRand() io.Reader { } } +// NewSeededRand returns a random bytes reader +// initialized with the given seed. func NewSeededRand(seed int64) io.Reader { src := rand.NewSource(seed) return &randGen{ @@ -102,6 +106,9 @@ func (m MultiErr) Error() string { return s } +// Partition splits a subject 3 parts: prefix, separator, suffix. +// The first occurrence of the separator will be matched. +// ie. Partition("Ready, steady, go!", ", ") -> ["Ready", ", ", "steady, go!"] func Partition(subject string, sep string) (string, string, string) { if i := strings.Index(subject, sep); i != -1 { return subject[:i], subject[i : i+len(sep)], subject[i+len(sep):] @@ -109,6 +116,9 @@ func Partition(subject string, sep string) (string, string, string) { return subject, "", "" } +// RPartition splits a subject 3 parts: prefix, separator, suffix. +// The last occurrence of the separator will be matched. +// ie. RPartition("Ready, steady, go!", ", ") -> ["Ready, steady", ", ", "go!"] func RPartition(subject string, sep string) (string, string, string) { if i := strings.LastIndex(subject, sep); i != -1 { return subject[:i], subject[i : i+len(sep)], subject[i+len(sep):] From b71f41bbf535cca99f25ea98d56d145dfcc1054e Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 20 Mar 2017 14:46:02 -0400 Subject: [PATCH 1769/3817] gc: address CR comments License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@9a4b4e93aa0a1474867a025425df040621fa8473 --- pinning/pinner/gc/gc.go | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index ccf354b45..4a990da9a 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -16,6 +16,8 @@ import ( var log = logging.Logger("gc") +// Result represents an incremental output from a garbage collection +// run. It contains either an error, or the cid of a removed object. type Result struct { KeyRemoved *cid.Cid Error error @@ -66,7 +68,7 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. err := bs.DeleteBlock(k) if err != nil { errors = true - output <- Result{Error: &CouldNotDeleteBlockError{k, err}} + output <- Result{Error: &CannotDeleteBlockError{k, err}} //log.Errorf("Error removing key from blockstore: %s", err) // continue as error is non-fatal continue loop @@ -82,7 +84,7 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. } } if errors { - output <- Result{Error: ErrCouldNotDeleteSomeBlocks} + output <- Result{Error: ErrCannotDeleteSomeBlocks} } }() @@ -103,6 +105,8 @@ func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots return nil } +// ColoredSet computes the set of nodes in the graph that are pinned by the +// pins in the given pinner. func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffortRoots []*cid.Cid, output chan<- Result) (*cid.Set, error) { // KeySet currently implemented in memory, in the future, may be bloom filter or // disk backed to conserve memory. @@ -112,7 +116,7 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffo links, err := ls.GetLinks(ctx, cid) if err != nil { errors = true - output <- Result{Error: &CouldNotFetchLinksError{cid, err}} + output <- Result{Error: &CannotFetchLinksError{cid, err}} } return links, nil } @@ -126,7 +130,7 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffo links, err := ls.GetLinks(ctx, cid) if err != nil && err != dag.ErrNotFound { errors = true - output <- Result{Error: &CouldNotFetchLinksError{cid, err}} + output <- Result{Error: &CannotFetchLinksError{cid, err}} } return links, nil } @@ -147,30 +151,30 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffo } if errors { - return nil, ErrCouldNotFetchAllLinks - } else { - return gcs, nil + return nil, ErrCannotFetchAllLinks } + + return gcs, nil } -var ErrCouldNotFetchAllLinks = errors.New("garbage collection aborted: could not retrieve some links") +var ErrCannotFetchAllLinks = errors.New("garbage collection aborted: could not retrieve some links") -var ErrCouldNotDeleteSomeBlocks = errors.New("garbage collection incomplete: could not delete some blocks") +var ErrCannotDeleteSomeBlocks = errors.New("garbage collection incomplete: could not delete some blocks") -type CouldNotFetchLinksError struct { +type CannotFetchLinksError struct { Key *cid.Cid Err error } -func (e *CouldNotFetchLinksError) Error() string { +func (e *CannotFetchLinksError) Error() string { return fmt.Sprintf("could not retrieve links for %s: %s", e.Key, e.Err) } -type CouldNotDeleteBlockError struct { +type CannotDeleteBlockError struct { Key *cid.Cid Err error } -func (e *CouldNotDeleteBlockError) Error() string { +func (e *CannotDeleteBlockError) Error() string { return fmt.Sprintf("could not remove %s: %s", e.Key, e.Err) } From 407cacfe3615cfb7415dad85f33fb0c99742dd74 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 4 Aug 2016 12:45:00 -0700 Subject: [PATCH 1770/3817] implement an HAMT for unixfs directory sharding License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@21a5430e46a58a6703e8f5110853512da15fc7b4 --- unixfs/format.go | 1 + unixfs/hamt/hamt.go | 464 +++++++++++++++++++++++++++ unixfs/hamt/hamt_stress_test.go | 280 ++++++++++++++++ unixfs/hamt/hamt_test.go | 552 ++++++++++++++++++++++++++++++++ unixfs/hamt/util.go | 61 ++++ unixfs/hamt/util_test.go | 58 ++++ unixfs/io/dirbuilder.go | 142 ++++++-- unixfs/io/dirbuilder_test.go | 138 +++++++- unixfs/io/resolve.go | 36 +-- unixfs/pb/unixfs.pb.go | 19 ++ unixfs/pb/unixfs.proto | 4 + 11 files changed, 1699 insertions(+), 56 deletions(-) create mode 100644 unixfs/hamt/hamt.go create mode 100644 unixfs/hamt/hamt_stress_test.go create mode 100644 unixfs/hamt/hamt_test.go create mode 100644 unixfs/hamt/util.go create mode 100644 unixfs/hamt/util_test.go diff --git a/unixfs/format.go b/unixfs/format.go index 96dd109d1..4157ec0e5 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -17,6 +17,7 @@ const ( TDirectory = pb.Data_Directory TMetadata = pb.Data_Metadata TSymlink = pb.Data_Symlink + THAMTShard = pb.Data_HAMTShard ) var ErrMalformedFileFormat = errors.New("malformed data in file format") diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go new file mode 100644 index 000000000..05b9402fd --- /dev/null +++ b/unixfs/hamt/hamt.go @@ -0,0 +1,464 @@ +package hamt + +import ( + "context" + "fmt" + "math" + "math/big" + "os" + + dag "github.com/ipfs/go-ipfs/merkledag" + format "github.com/ipfs/go-ipfs/unixfs" + upb "github.com/ipfs/go-ipfs/unixfs/pb" + + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + "gx/ipfs/QmfJHywXQu98UeZtGJBQrPAR6AtmDjjbe3qjTo9piXHPnx/murmur3" +) + +const ( + HashMurmur3 uint64 = 0x22 +) + +type HamtShard struct { + nd *dag.ProtoNode + + bitfield *big.Int + + children []child + + tableSize int + tableSizeLg2 int + + hashFunc uint64 + + prefixPadStr string + maxpadlen int + + dserv dag.DAGService +} + +// child can either be another shard, or a leaf node value +type child interface { + Node() (node.Node, error) + Label() string +} + +func NewHamtShard(dserv dag.DAGService, size int) *HamtShard { + ds := makeHamtShard(dserv, size) + ds.bitfield = big.NewInt(0) + ds.nd = new(dag.ProtoNode) + ds.hashFunc = HashMurmur3 + return ds +} + +func makeHamtShard(ds dag.DAGService, size int) *HamtShard { + maxpadding := fmt.Sprintf("%X", size-1) + return &HamtShard{ + tableSizeLg2: int(math.Log2(float64(size))), + prefixPadStr: fmt.Sprintf("%%0%dX", len(maxpadding)), + maxpadlen: len(maxpadding), + tableSize: size, + dserv: ds, + } +} + +func NewHamtFromDag(dserv dag.DAGService, nd node.Node) (*HamtShard, error) { + pbnd, ok := nd.(*dag.ProtoNode) + if !ok { + return nil, dag.ErrLinkNotFound + } + + pbd, err := format.FromBytes(pbnd.Data()) + if err != nil { + return nil, err + } + + if pbd.GetType() != upb.Data_HAMTShard { + return nil, fmt.Errorf("node was not a dir shard") + } + + if pbd.GetHashType() != HashMurmur3 { + return nil, fmt.Errorf("only murmur3 supported as hash function") + } + + ds := makeHamtShard(dserv, int(pbd.GetFanout())) + ds.nd = pbnd.Copy().(*dag.ProtoNode) + ds.children = make([]child, len(pbnd.Links())) + ds.bitfield = new(big.Int).SetBytes(pbd.GetData()) + ds.hashFunc = pbd.GetHashType() + + return ds, nil +} + +// Node serializes the HAMT structure into a merkledag node with unixfs formatting +func (ds *HamtShard) Node() (node.Node, error) { + out := new(dag.ProtoNode) + + // TODO: optimized 'for each set bit' + for i := 0; i < ds.tableSize; i++ { + if ds.bitfield.Bit(i) == 0 { + continue + } + + cindex := ds.indexForBitPos(i) + ch := ds.children[cindex] + if ch != nil { + cnd, err := ch.Node() + if err != nil { + return nil, err + } + + err = out.AddNodeLinkClean(ds.linkNamePrefix(i)+ch.Label(), cnd) + if err != nil { + return nil, err + } + } else { + // child unloaded, just copy in link with updated name + lnk := ds.nd.Links()[cindex] + label := lnk.Name[ds.maxpadlen:] + + err := out.AddRawLink(ds.linkNamePrefix(i)+label, lnk) + if err != nil { + return nil, err + } + } + } + + typ := upb.Data_HAMTShard + data, err := proto.Marshal(&upb.Data{ + Type: &typ, + Fanout: proto.Uint64(uint64(ds.tableSize)), + HashType: proto.Uint64(HashMurmur3), + Data: ds.bitfield.Bytes(), + }) + if err != nil { + return nil, err + } + + out.SetData(data) + + _, err = ds.dserv.Add(out) + if err != nil { + return nil, err + } + + return out, nil +} + +type shardValue struct { + key string + val node.Node +} + +func (sv *shardValue) Node() (node.Node, error) { + return sv.val, nil +} + +func (sv *shardValue) Label() string { + return sv.key +} + +func hash(val []byte) []byte { + h := murmur3.New64() + h.Write(val) + return h.Sum(nil) +} + +// Label for HamtShards is the empty string, this is used to differentiate them from +// value entries +func (ds *HamtShard) Label() string { + return "" +} + +// Set sets 'name' = nd in the HAMT +func (ds *HamtShard) Set(ctx context.Context, name string, nd node.Node) error { + hv := &hashBits{b: hash([]byte(name))} + return ds.modifyValue(ctx, hv, name, nd) +} + +// Remove deletes the named entry if it exists, this operation is idempotent. +func (ds *HamtShard) Remove(ctx context.Context, name string) error { + hv := &hashBits{b: hash([]byte(name))} + return ds.modifyValue(ctx, hv, name, nil) +} + +func (ds *HamtShard) Find(ctx context.Context, name string) (node.Node, error) { + hv := &hashBits{b: hash([]byte(name))} + + var out node.Node + err := ds.getValue(ctx, hv, name, func(sv *shardValue) error { + out = sv.val + return nil + }) + + return out, err +} + +// getChild returns the i'th child of this shard. If it is cached in the +// children array, it will return it from there. Otherwise, it loads the child +// node from disk. +func (ds *HamtShard) getChild(ctx context.Context, i int) (child, error) { + if i >= len(ds.children) || i < 0 { + return nil, fmt.Errorf("invalid index passed to getChild (likely corrupt bitfield)") + } + + if len(ds.children) != len(ds.nd.Links()) { + return nil, fmt.Errorf("inconsistent lengths between children array and Links array") + } + + c := ds.children[i] + if c != nil { + return c, nil + } + + return ds.loadChild(ctx, i) +} + +// loadChild reads the i'th child node of this shard from disk and returns it +// as a 'child' interface +func (ds *HamtShard) loadChild(ctx context.Context, i int) (child, error) { + lnk := ds.nd.Links()[i] + if len(lnk.Name) < ds.maxpadlen { + return nil, fmt.Errorf("invalid link name '%s'", lnk.Name) + } + + nd, err := lnk.GetNode(ctx, ds.dserv) + if err != nil { + return nil, err + } + + var c child + if len(lnk.Name) == ds.maxpadlen { + pbnd, ok := nd.(*dag.ProtoNode) + if !ok { + return nil, dag.ErrNotProtobuf + } + + pbd, err := format.FromBytes(pbnd.Data()) + if err != nil { + return nil, err + } + + if pbd.GetType() != format.THAMTShard { + return nil, fmt.Errorf("HAMT entries must have non-zero length name") + } + + cds, err := NewHamtFromDag(ds.dserv, nd) + if err != nil { + return nil, err + } + + c = cds + } else { + c = &shardValue{ + key: lnk.Name[ds.maxpadlen:], + val: nd, + } + } + + ds.children[i] = c + return c, nil +} + +func (ds *HamtShard) setChild(i int, c child) { + ds.children[i] = c +} + +func (ds *HamtShard) insertChild(idx int, key string, val node.Node) error { + if val == nil { + return os.ErrNotExist + } + + i := ds.indexForBitPos(idx) + ds.bitfield.SetBit(ds.bitfield, idx, 1) + sv := &shardValue{ + key: key, + val: val, + } + + ds.children = append(ds.children[:i], append([]child{sv}, ds.children[i:]...)...) + ds.nd.SetLinks(append(ds.nd.Links()[:i], append([]*node.Link{nil}, ds.nd.Links()[i:]...)...)) + return nil +} + +func (ds *HamtShard) rmChild(i int) error { + if i < 0 || i >= len(ds.children) || i >= len(ds.nd.Links()) { + return fmt.Errorf("hamt: attempted to remove child with out of range index") + } + + copy(ds.children[i:], ds.children[i+1:]) + ds.children = ds.children[:len(ds.children)-1] + + copy(ds.nd.Links()[i:], ds.nd.Links()[i+1:]) + ds.nd.SetLinks(ds.nd.Links()[:len(ds.nd.Links())-1]) + + return nil +} + +func (ds *HamtShard) getValue(ctx context.Context, hv *hashBits, key string, cb func(*shardValue) error) error { + idx := hv.Next(ds.tableSizeLg2) + if ds.bitfield.Bit(int(idx)) == 1 { + cindex := ds.indexForBitPos(idx) + + child, err := ds.getChild(ctx, cindex) + if err != nil { + return err + } + + switch child := child.(type) { + case *HamtShard: + return child.getValue(ctx, hv, key, cb) + case *shardValue: + if child.key == key { + return cb(child) + } + } + } + + return os.ErrNotExist +} + +func (ds *HamtShard) EnumLinks() ([]*node.Link, error) { + var links []*node.Link + err := ds.walkTrie(func(sv *shardValue) error { + lnk, err := node.MakeLink(sv.val) + if err != nil { + return err + } + + lnk.Name = sv.key + + links = append(links, lnk) + return nil + }) + if err != nil { + return nil, err + } + + return links, nil +} + +func (ds *HamtShard) walkTrie(cb func(*shardValue) error) error { + for i := 0; i < ds.tableSize; i++ { + if ds.bitfield.Bit(i) == 0 { + continue + } + + idx := ds.indexForBitPos(i) + // NOTE: an optimized version could simply iterate over each + // element in the 'children' array. + c, err := ds.getChild(context.TODO(), idx) + if err != nil { + return err + } + + switch c := c.(type) { + case *shardValue: + err := cb(c) + if err != nil { + return err + } + + case *HamtShard: + err := c.walkTrie(cb) + if err != nil { + return err + } + default: + return fmt.Errorf("unexpected child type: %#v", c) + } + } + return nil +} + +func (ds *HamtShard) modifyValue(ctx context.Context, hv *hashBits, key string, val node.Node) error { + idx := hv.Next(ds.tableSizeLg2) + + if ds.bitfield.Bit(idx) != 1 { + return ds.insertChild(idx, key, val) + } + + cindex := ds.indexForBitPos(idx) + + child, err := ds.getChild(ctx, cindex) + if err != nil { + return err + } + + switch child := child.(type) { + case *HamtShard: + err := child.modifyValue(ctx, hv, key, val) + if err != nil { + return err + } + + if val == nil { + switch len(child.children) { + case 0: + // empty sub-shard, prune it + // Note: this shouldnt normally ever happen + // in the event of another implementation creates flawed + // structures, this will help to normalize them. + ds.bitfield.SetBit(ds.bitfield, idx, 0) + return ds.rmChild(cindex) + case 1: + nchild, ok := child.children[0].(*shardValue) + if ok { + // sub-shard with a single value element, collapse it + ds.setChild(cindex, nchild) + } + return nil + } + } + + return nil + case *shardValue: + switch { + case val == nil: // passing a nil value signifies a 'delete' + ds.bitfield.SetBit(ds.bitfield, idx, 0) + return ds.rmChild(cindex) + + case child.key == key: // value modification + child.val = val + return nil + + default: // replace value with another shard, one level deeper + ns := NewHamtShard(ds.dserv, ds.tableSize) + chhv := &hashBits{ + b: hash([]byte(child.key)), + consumed: hv.consumed, + } + + err := ns.modifyValue(ctx, hv, key, val) + if err != nil { + return err + } + + err = ns.modifyValue(ctx, chhv, child.key, child.val) + if err != nil { + return err + } + + ds.setChild(cindex, ns) + return nil + } + default: + return fmt.Errorf("unexpected type for child: %#v", child) + } +} + +func (ds *HamtShard) indexForBitPos(bp int) int { + // TODO: an optimization could reuse the same 'mask' here and change the size + // as needed. This isnt yet done as the bitset package doesnt make it easy + // to do. + mask := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(bp)), nil), big.NewInt(1)) + mask.And(mask, ds.bitfield) + + return popCount(mask) +} + +// linkNamePrefix takes in the bitfield index of an entry and returns its hex prefix +func (ds *HamtShard) linkNamePrefix(idx int) string { + return fmt.Sprintf(ds.prefixPadStr, idx) +} diff --git a/unixfs/hamt/hamt_stress_test.go b/unixfs/hamt/hamt_stress_test.go new file mode 100644 index 000000000..83ad25597 --- /dev/null +++ b/unixfs/hamt/hamt_stress_test.go @@ -0,0 +1,280 @@ +package hamt + +import ( + "bufio" + "context" + "fmt" + "math/rand" + "os" + "strconv" + "strings" + "testing" + "time" + + dag "github.com/ipfs/go-ipfs/merkledag" + mdtest "github.com/ipfs/go-ipfs/merkledag/test" + ft "github.com/ipfs/go-ipfs/unixfs" +) + +func getNames(prefix string, count int) []string { + out := make([]string, count) + for i := 0; i < count; i++ { + out[i] = fmt.Sprintf("%s%d", prefix, i) + } + return out +} + +const ( + opAdd = iota + opDel + opFind +) + +type testOp struct { + Op int + Val string +} + +func stringArrToSet(arr []string) map[string]bool { + out := make(map[string]bool) + for _, s := range arr { + out[s] = true + } + return out +} + +// generate two different random sets of operations to result in the same +// ending directory (same set of entries at the end) and execute each of them +// in turn, then compare to ensure the output is the same on each. +func TestOrderConsistency(t *testing.T) { + seed := time.Now().UnixNano() + t.Logf("using seed = %d", seed) + ds := mdtest.Mock() + + shardWidth := 1024 + + keep := getNames("good", 4000) + temp := getNames("tempo", 6000) + + ops := genOpSet(seed, keep, temp) + s, err := executeOpSet(t, ds, shardWidth, ops) + if err != nil { + t.Fatal(err) + } + + err = validateOpSetCompletion(t, s, keep, temp) + if err != nil { + t.Fatal(err) + } + + ops2 := genOpSet(seed+1000, keep, temp) + s2, err := executeOpSet(t, ds, shardWidth, ops2) + if err != nil { + t.Fatal(err) + } + + err = validateOpSetCompletion(t, s2, keep, temp) + if err != nil { + t.Fatal(err) + } + + nd, err := s.Node() + if err != nil { + t.Fatal(err) + } + + nd2, err := s2.Node() + if err != nil { + t.Fatal(err) + } + + k := nd.Cid() + k2 := nd2.Cid() + + if !k.Equals(k2) { + t.Fatal("got different results: ", k, k2) + } +} + +func validateOpSetCompletion(t *testing.T, s *HamtShard, keep, temp []string) error { + ctx := context.TODO() + for _, n := range keep { + _, err := s.Find(ctx, n) + if err != nil { + return fmt.Errorf("couldnt find %s: %s", n, err) + } + } + + for _, n := range temp { + _, err := s.Find(ctx, n) + if err != os.ErrNotExist { + return fmt.Errorf("expected not to find: %s", err) + } + } + + return nil +} + +func executeOpSet(t *testing.T, ds dag.DAGService, width int, ops []testOp) (*HamtShard, error) { + ctx := context.TODO() + s := NewHamtShard(ds, width) + e := ft.EmptyDirNode() + ds.Add(e) + + for _, o := range ops { + switch o.Op { + case opAdd: + err := s.Set(ctx, o.Val, e) + if err != nil { + return nil, fmt.Errorf("inserting %s: %s", o.Val, err) + } + case opDel: + err := s.Remove(ctx, o.Val) + if err != nil { + return nil, fmt.Errorf("deleting %s: %s", o.Val, err) + } + case opFind: + _, err := s.Find(ctx, o.Val) + if err != nil { + return nil, fmt.Errorf("finding %s: %s", o.Val, err) + } + } + } + + return s, nil +} + +func genOpSet(seed int64, keep, temp []string) []testOp { + tempset := stringArrToSet(temp) + + allnames := append(keep, temp...) + shuffle(seed, allnames) + + var todel []string + + var ops []testOp + + for { + n := len(allnames) + len(todel) + if n == 0 { + return ops + } + + rn := rand.Intn(n) + + if rn < len(allnames) { + next := allnames[0] + allnames = allnames[1:] + ops = append(ops, testOp{ + Op: opAdd, + Val: next, + }) + + if tempset[next] { + todel = append(todel, next) + } + } else { + shuffle(seed+100, todel) + next := todel[0] + todel = todel[1:] + + ops = append(ops, testOp{ + Op: opDel, + Val: next, + }) + } + } +} + +// executes the given op set with a repl to allow easier debugging +func debugExecuteOpSet(ds dag.DAGService, width int, ops []testOp) (*HamtShard, error) { + s := NewHamtShard(ds, width) + e := ft.EmptyDirNode() + ds.Add(e) + ctx := context.TODO() + + run := 0 + + opnames := map[int]string{ + opAdd: "add", + opDel: "del", + } + +mainloop: + for i := 0; i < len(ops); i++ { + o := ops[i] + + fmt.Printf("Op %d: %s %s\n", i, opnames[o.Op], o.Val) + for run == 0 { + cmd := readCommand() + parts := strings.Split(cmd, " ") + switch parts[0] { + case "": + run = 1 + case "find": + _, err := s.Find(ctx, parts[1]) + if err == nil { + fmt.Println("success") + } else { + fmt.Println(err) + } + case "run": + if len(parts) > 1 { + n, err := strconv.Atoi(parts[1]) + if err != nil { + panic(err) + } + + run = n + } else { + run = -1 + } + case "lookop": + for k := 0; k < len(ops); k++ { + if ops[k].Val == parts[1] { + fmt.Printf(" Op %d: %s %s\n", k, opnames[ops[k].Op], parts[1]) + } + } + case "restart": + s = NewHamtShard(ds, width) + i = -1 + continue mainloop + case "print": + nd, err := s.Node() + if err != nil { + panic(err) + } + printDag(ds, nd.(*dag.ProtoNode), 0) + } + } + run-- + + switch o.Op { + case opAdd: + err := s.Set(ctx, o.Val, e) + if err != nil { + return nil, fmt.Errorf("inserting %s: %s", o.Val, err) + } + case opDel: + fmt.Println("deleting: ", o.Val) + err := s.Remove(ctx, o.Val) + if err != nil { + return nil, fmt.Errorf("deleting %s: %s", o.Val, err) + } + case opFind: + _, err := s.Find(ctx, o.Val) + if err != nil { + return nil, fmt.Errorf("finding %s: %s", o.Val, err) + } + } + } + + return s, nil +} + +func readCommand() string { + fmt.Print("> ") + scan := bufio.NewScanner(os.Stdin) + scan.Scan() + return scan.Text() +} diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go new file mode 100644 index 000000000..cafa1ce8c --- /dev/null +++ b/unixfs/hamt/hamt_test.go @@ -0,0 +1,552 @@ +package hamt + +import ( + "context" + "fmt" + "math/rand" + "os" + "sort" + "strings" + "testing" + "time" + + dag "github.com/ipfs/go-ipfs/merkledag" + mdtest "github.com/ipfs/go-ipfs/merkledag/test" + dagutils "github.com/ipfs/go-ipfs/merkledag/utils" + ft "github.com/ipfs/go-ipfs/unixfs" +) + +func shuffle(seed int64, arr []string) { + r := rand.New(rand.NewSource(seed)) + for i := 0; i < len(arr); i++ { + a := r.Intn(len(arr)) + b := r.Intn(len(arr)) + arr[a], arr[b] = arr[b], arr[a] + } +} + +func makeDir(ds dag.DAGService, size int) ([]string, *HamtShard, error) { + return makeDirWidth(ds, size, 256) +} + +func makeDirWidth(ds dag.DAGService, size, width int) ([]string, *HamtShard, error) { + s := NewHamtShard(ds, width) + + var dirs []string + for i := 0; i < size; i++ { + dirs = append(dirs, fmt.Sprintf("DIRNAME%d", i)) + } + + shuffle(time.Now().UnixNano(), dirs) + + for i := 0; i < len(dirs); i++ { + nd := ft.EmptyDirNode() + ds.Add(nd) + err := s.Set(context.Background(), dirs[i], nd) + if err != nil { + return nil, nil, err + } + } + + return dirs, s, nil +} + +func assertLink(s *HamtShard, name string, found bool) error { + _, err := s.Find(context.Background(), name) + switch err { + case os.ErrNotExist: + if found { + return err + } + + return nil + case nil: + if found { + return nil + } + + return fmt.Errorf("expected not to find link named %s", name) + default: + return err + } +} + +func assertSerializationWorks(ds dag.DAGService, s *HamtShard) error { + nd, err := s.Node() + if err != nil { + return err + } + + nds, err := NewHamtFromDag(ds, nd) + if err != nil { + return err + } + + linksA, err := s.EnumLinks() + if err != nil { + return err + } + + linksB, err := nds.EnumLinks() + if err != nil { + return err + } + + if len(linksA) != len(linksB) { + return fmt.Errorf("links arrays are different sizes") + } + + for i, a := range linksA { + b := linksB[i] + if a.Name != b.Name { + return fmt.Errorf("links names mismatch") + } + + if a.Cid.String() != b.Cid.String() { + return fmt.Errorf("link hashes dont match") + } + + if a.Size != b.Size { + return fmt.Errorf("link sizes not the same") + } + } + + return nil +} + +func TestBasicSet(t *testing.T) { + ds := mdtest.Mock() + for _, w := range []int{128, 256, 512, 1024, 2048, 4096} { + t.Run(fmt.Sprintf("BasicSet%d", w), func(t *testing.T) { + names, s, err := makeDirWidth(ds, 1000, w) + if err != nil { + t.Fatal(err) + } + ctx := context.Background() + + for _, d := range names { + _, err := s.Find(ctx, d) + if err != nil { + t.Fatal(err) + } + } + }) + } +} + +func TestDirBuilding(t *testing.T) { + ds := mdtest.Mock() + s := NewHamtShard(ds, 256) + + _, s, err := makeDir(ds, 200) + if err != nil { + t.Fatal(err) + } + + nd, err := s.Node() + if err != nil { + t.Fatal(err) + } + + //printDag(ds, nd, 0) + + k := nd.Cid() + + if k.String() != "QmY89TkSEVHykWMHDmyejSWFj9CYNtvzw4UwnT9xbc4Zjc" { + t.Fatalf("output didnt match what we expected (got %s)", k.String()) + } +} + +func TestShardReload(t *testing.T) { + ds := mdtest.Mock() + s := NewHamtShard(ds, 256) + ctx := context.Background() + + _, s, err := makeDir(ds, 200) + if err != nil { + t.Fatal(err) + } + + nd, err := s.Node() + if err != nil { + t.Fatal(err) + } + + nds, err := NewHamtFromDag(ds, nd) + if err != nil { + t.Fatal(err) + } + + lnks, err := nds.EnumLinks() + if err != nil { + t.Fatal(err) + } + + if len(lnks) != 200 { + t.Fatal("not enough links back") + } + + _, err = nds.Find(ctx, "DIRNAME50") + if err != nil { + t.Fatal(err) + } + + // Now test roundtrip marshal with no operations + + nds, err = NewHamtFromDag(ds, nd) + if err != nil { + t.Fatal(err) + } + + ond, err := nds.Node() + if err != nil { + t.Fatal(err) + } + + outk := ond.Cid() + ndk := nd.Cid() + + if !outk.Equals(ndk) { + printDiff(ds, nd.(*dag.ProtoNode), ond.(*dag.ProtoNode)) + t.Fatal("roundtrip serialization failed") + } +} + +func TestRemoveElems(t *testing.T) { + ds := mdtest.Mock() + dirs, s, err := makeDir(ds, 500) + if err != nil { + t.Fatal(err) + } + ctx := context.Background() + + shuffle(time.Now().UnixNano(), dirs) + + for _, d := range dirs { + err := s.Remove(ctx, d) + if err != nil { + t.Fatal(err) + } + } + + nd, err := s.Node() + if err != nil { + t.Fatal(err) + } + + if len(nd.Links()) > 0 { + t.Fatal("shouldnt have any links here") + } + + err = s.Remove(ctx, "doesnt exist") + if err != os.ErrNotExist { + t.Fatal("expected error does not exist") + } +} + +func TestSetAfterMarshal(t *testing.T) { + ds := mdtest.Mock() + _, s, err := makeDir(ds, 300) + if err != nil { + t.Fatal(err) + } + ctx := context.Background() + + nd, err := s.Node() + if err != nil { + t.Fatal(err) + } + + nds, err := NewHamtFromDag(ds, nd) + if err != nil { + t.Fatal(err) + } + + empty := ft.EmptyDirNode() + for i := 0; i < 100; i++ { + err := nds.Set(ctx, fmt.Sprintf("moredirs%d", i), empty) + if err != nil { + t.Fatal(err) + } + } + + links, err := nds.EnumLinks() + if err != nil { + t.Fatal(err) + } + + if len(links) != 400 { + t.Fatal("expected 400 links") + } + + err = assertSerializationWorks(ds, nds) + if err != nil { + t.Fatal(err) + } +} + +func TestDuplicateAddShard(t *testing.T) { + ds := mdtest.Mock() + dir := NewHamtShard(ds, 256) + nd := new(dag.ProtoNode) + ctx := context.Background() + + err := dir.Set(ctx, "test", nd) + if err != nil { + t.Fatal(err) + } + + err = dir.Set(ctx, "test", nd) + if err != nil { + t.Fatal(err) + } + + lnks, err := dir.EnumLinks() + if err != nil { + t.Fatal(err) + } + + if len(lnks) != 1 { + t.Fatal("expected only one link") + } +} + +func TestLoadFailsFromNonShard(t *testing.T) { + ds := mdtest.Mock() + nd := ft.EmptyDirNode() + + _, err := NewHamtFromDag(ds, nd) + if err == nil { + t.Fatal("expected dir shard creation to fail when given normal directory") + } + + nd = new(dag.ProtoNode) + + _, err = NewHamtFromDag(ds, nd) + if err == nil { + t.Fatal("expected dir shard creation to fail when given normal directory") + } +} + +func TestFindNonExisting(t *testing.T) { + ds := mdtest.Mock() + _, s, err := makeDir(ds, 100) + if err != nil { + t.Fatal(err) + } + ctx := context.Background() + + for i := 0; i < 200; i++ { + _, err := s.Find(ctx, fmt.Sprintf("notfound%d", i)) + if err != os.ErrNotExist { + t.Fatal("expected ErrNotExist") + } + } +} + +func TestRemoveElemsAfterMarshal(t *testing.T) { + ds := mdtest.Mock() + dirs, s, err := makeDir(ds, 30) + if err != nil { + t.Fatal(err) + } + ctx := context.Background() + + sort.Strings(dirs) + + err = s.Remove(ctx, dirs[0]) + if err != nil { + t.Fatal(err) + } + + out, err := s.Find(ctx, dirs[0]) + if err == nil { + t.Fatal("expected error, got: ", out) + } + + nd, err := s.Node() + if err != nil { + t.Fatal(err) + } + + nds, err := NewHamtFromDag(ds, nd) + if err != nil { + t.Fatal(err) + } + + _, err = nds.Find(ctx, dirs[0]) + if err == nil { + t.Fatal("expected not to find ", dirs[0]) + } + + for _, d := range dirs[1:] { + _, err := nds.Find(ctx, d) + if err != nil { + t.Fatal("could not find expected link after unmarshaling") + } + } + + for _, d := range dirs[1:] { + err := nds.Remove(ctx, d) + if err != nil { + t.Fatal(err) + } + } + + links, err := nds.EnumLinks() + if err != nil { + t.Fatal(err) + } + + if len(links) != 0 { + t.Fatal("expected all links to be removed") + } + + err = assertSerializationWorks(ds, nds) + if err != nil { + t.Fatal(err) + } +} + +func TestBitfieldIndexing(t *testing.T) { + ds := mdtest.Mock() + s := NewHamtShard(ds, 256) + + set := func(i int) { + s.bitfield.SetBit(s.bitfield, i, 1) + } + + assert := func(i int, val int) { + if s.indexForBitPos(i) != val { + t.Fatalf("expected index %d to be %d", i, val) + } + } + + assert(50, 0) + set(4) + set(5) + set(60) + + assert(10, 2) + set(3) + assert(10, 3) + assert(1, 0) + + assert(100, 4) + set(50) + assert(45, 3) + set(100) + assert(100, 5) +} + +// test adding a sharded directory node as the child of another directory node. +// if improperly implemented, the parent hamt may assume the child is a part of +// itself. +func TestSetHamtChild(t *testing.T) { + ds := mdtest.Mock() + s := NewHamtShard(ds, 256) + ctx := context.Background() + + e := ft.EmptyDirNode() + ds.Add(e) + + err := s.Set(ctx, "bar", e) + if err != nil { + t.Fatal(err) + } + + snd, err := s.Node() + if err != nil { + t.Fatal(err) + } + + _, ns, err := makeDir(ds, 50) + if err != nil { + t.Fatal(err) + } + + err = ns.Set(ctx, "foo", snd) + if err != nil { + t.Fatal(err) + } + + nsnd, err := ns.Node() + if err != nil { + t.Fatal(err) + } + + hs, err := NewHamtFromDag(ds, nsnd) + if err != nil { + t.Fatal(err) + } + + err = assertLink(hs, "bar", false) + if err != nil { + t.Fatal(err) + } + + err = assertLink(hs, "foo", true) + if err != nil { + t.Fatal(err) + } +} + +func printDag(ds dag.DAGService, nd *dag.ProtoNode, depth int) { + padding := strings.Repeat(" ", depth) + fmt.Println("{") + for _, l := range nd.Links() { + fmt.Printf("%s%s: %s", padding, l.Name, l.Cid.String()) + ch, err := ds.Get(context.Background(), l.Cid) + if err != nil { + panic(err) + } + + printDag(ds, ch.(*dag.ProtoNode), depth+1) + } + fmt.Println(padding + "}") +} + +func printDiff(ds dag.DAGService, a, b *dag.ProtoNode) { + diff, err := dagutils.Diff(context.TODO(), ds, a, b) + if err != nil { + panic(err) + } + + for _, d := range diff { + fmt.Println(d) + } +} + +func BenchmarkHAMTSet(b *testing.B) { + ds := mdtest.Mock() + sh := NewHamtShard(ds, 256) + nd, err := sh.Node() + if err != nil { + b.Fatal(err) + } + + _, err = ds.Add(nd) + if err != nil { + b.Fatal(err) + } + ds.Add(ft.EmptyDirNode()) + + for i := 0; i < b.N; i++ { + s, err := NewHamtFromDag(ds, nd) + if err != nil { + b.Fatal(err) + } + + err = s.Set(context.TODO(), fmt.Sprint(i), ft.EmptyDirNode()) + if err != nil { + b.Fatal(err) + } + + out, err := s.Node() + if err != nil { + b.Fatal(err) + } + + nd = out + } +} diff --git a/unixfs/hamt/util.go b/unixfs/hamt/util.go new file mode 100644 index 000000000..1727dbfed --- /dev/null +++ b/unixfs/hamt/util.go @@ -0,0 +1,61 @@ +package hamt + +import ( + "math/big" +) + +type hashBits struct { + b []byte + consumed int +} + +func mkmask(n int) byte { + return (1 << uint(n)) - 1 +} + +func (hb *hashBits) Next(i int) int { + curbi := hb.consumed / 8 + leftb := 8 - (hb.consumed % 8) + + curb := hb.b[curbi] + if i == leftb { + out := int(mkmask(i) & curb) + hb.consumed += i + return out + } else if i < leftb { + a := curb & mkmask(leftb) // mask out the high bits we don't want + b := a & ^mkmask(leftb-i) // mask out the low bits we don't want + c := b >> uint(leftb-i) // shift whats left down + hb.consumed += i + return int(c) + } else { + out := int(mkmask(leftb) & curb) + out <<= uint(i - leftb) + hb.consumed += leftb + out += hb.Next(i - leftb) + return out + } +} + +const ( + m1 = 0x5555555555555555 //binary: 0101... + m2 = 0x3333333333333333 //binary: 00110011.. + m4 = 0x0f0f0f0f0f0f0f0f //binary: 4 zeros, 4 ones ... + h01 = 0x0101010101010101 //the sum of 256 to the power of 0,1,2,3... +) + +// from https://en.wikipedia.org/wiki/Hamming_weight +func popCountUint64(x uint64) int { + x -= (x >> 1) & m1 //put count of each 2 bits into those 2 bits + x = (x & m2) + ((x >> 2) & m2) //put count of each 4 bits into those 4 bits + x = (x + (x >> 4)) & m4 //put count of each 8 bits into those 8 bits + return int((x * h01) >> 56) +} + +func popCount(i *big.Int) int { + var n int + for _, v := range i.Bits() { + n += popCountUint64(uint64(v)) + } + return n +} diff --git a/unixfs/hamt/util_test.go b/unixfs/hamt/util_test.go new file mode 100644 index 000000000..4406ac859 --- /dev/null +++ b/unixfs/hamt/util_test.go @@ -0,0 +1,58 @@ +package hamt + +import ( + "math/big" + "testing" +) + +func TestPopCount(t *testing.T) { + x := big.NewInt(0) + + for i := 0; i < 50; i++ { + x.SetBit(x, i, 1) + } + + if popCount(x) != 50 { + t.Fatal("expected popcount to be 50") + } +} + +func TestHashBitsEvenSizes(t *testing.T) { + buf := []byte{255, 127, 79, 45, 116, 99, 35, 17} + hb := hashBits{b: buf} + + for _, v := range buf { + if hb.Next(8) != int(v) { + t.Fatal("got wrong numbers back") + } + } +} + +func TestHashBitsUneven(t *testing.T) { + buf := []byte{255, 127, 79, 45, 116, 99, 35, 17} + hb := hashBits{b: buf} + + v := hb.Next(4) + if v != 15 { + t.Fatal("should have gotten 15: ", v) + } + + v = hb.Next(4) + if v != 15 { + t.Fatal("should have gotten 15: ", v) + } + + if v := hb.Next(3); v != 3 { + t.Fatalf("expected 3, but got %b", v) + } + if v := hb.Next(3); v != 7 { + t.Fatalf("expected 7, but got %b", v) + } + if v := hb.Next(3); v != 6 { + t.Fatalf("expected 6, but got %b", v) + } + + if v := hb.Next(15); v != 20269 { + t.Fatalf("expected 20269, but got %b (%d)", v, v) + } +} diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 071eba055..fdb616e1b 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -2,48 +2,144 @@ package io import ( "context" + "fmt" + "os" mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + hamt "github.com/ipfs/go-ipfs/unixfs/hamt" + + node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) -type directoryBuilder struct { +// ShardSplitThreshold specifies how large of an unsharded directory +// the Directory code will generate. Adding entries over this value will +// result in the node being restructured into a sharded object. +var ShardSplitThreshold = 1000 + +// DefaultShardWidth is the default value used for hamt sharding width. +var DefaultShardWidth = 256 + +type Directory struct { dserv mdag.DAGService dirnode *mdag.ProtoNode -} -// NewEmptyDirectory returns an empty merkledag Node with a folder Data chunk -func NewEmptyDirectory() *mdag.ProtoNode { - nd := new(mdag.ProtoNode) - nd.SetData(format.FolderPBData()) - return nd + shard *hamt.HamtShard } -// NewDirectory returns a directoryBuilder. It needs a DAGService to add the Children -func NewDirectory(dserv mdag.DAGService) *directoryBuilder { - db := new(directoryBuilder) +// NewDirectory returns a Directory. It needs a DAGService to add the Children +func NewDirectory(dserv mdag.DAGService) *Directory { + db := new(Directory) db.dserv = dserv - db.dirnode = NewEmptyDirectory() + db.dirnode = format.EmptyDirNode() return db } -// AddChild adds a (name, key)-pair to the root node. -func (d *directoryBuilder) AddChild(ctx context.Context, name string, c *cid.Cid) error { - cnode, err := d.dserv.Get(ctx, c) +func NewDirectoryFromNode(dserv mdag.DAGService, nd node.Node) (*Directory, error) { + pbnd, ok := nd.(*mdag.ProtoNode) + if !ok { + return nil, mdag.ErrNotProtobuf + } + + pbd, err := format.FromBytes(pbnd.Data()) if err != nil { - return err + return nil, err } - cnpb, ok := cnode.(*mdag.ProtoNode) - if !ok { - return mdag.ErrNotProtobuf + switch pbd.GetType() { + case format.TDirectory: + return &Directory{ + dserv: dserv, + dirnode: pbnd.Copy().(*mdag.ProtoNode), + }, nil + case format.THAMTShard: + shard, err := hamt.NewHamtFromDag(dserv, nd) + if err != nil { + return nil, err + } + + return &Directory{ + dserv: dserv, + shard: shard, + }, nil + default: + return nil, fmt.Errorf("merkledag node was not a directory or shard") } +} - return d.dirnode.AddNodeLinkClean(name, cnpb) +// AddChild adds a (name, key)-pair to the root node. +func (d *Directory) AddChild(ctx context.Context, name string, nd node.Node) error { + if d.shard == nil { + if len(d.dirnode.Links()) < ShardSplitThreshold { + _ = d.dirnode.RemoveNodeLink(name) + return d.dirnode.AddNodeLinkClean(name, nd) + } + + err := d.switchToSharding(ctx) + if err != nil { + return err + } + } + + return d.shard.Set(ctx, name, nd) } -// GetNode returns the root of this directoryBuilder -func (d *directoryBuilder) GetNode() *mdag.ProtoNode { - return d.dirnode +func (d *Directory) switchToSharding(ctx context.Context) error { + d.shard = hamt.NewHamtShard(d.dserv, DefaultShardWidth) + for _, lnk := range d.dirnode.Links() { + cnd, err := d.dserv.Get(ctx, lnk.Cid) + if err != nil { + return err + } + + err = d.shard.Set(ctx, lnk.Name, cnd) + if err != nil { + return err + } + } + + d.dirnode = nil + return nil +} + +func (d *Directory) Links() ([]*node.Link, error) { + if d.shard == nil { + return d.dirnode.Links(), nil + } + + return d.shard.EnumLinks() +} + +func (d *Directory) Find(ctx context.Context, name string) (node.Node, error) { + if d.shard == nil { + lnk, err := d.dirnode.GetNodeLink(name) + switch err { + case mdag.ErrLinkNotFound: + return nil, os.ErrNotExist + default: + return nil, err + case nil: + } + + return d.dserv.Get(ctx, lnk.Cid) + } + + return d.shard.Find(ctx, name) +} + +func (d *Directory) RemoveChild(ctx context.Context, name string) error { + if d.shard == nil { + return d.dirnode.RemoveNodeLink(name) + } + + return d.shard.Remove(ctx, name) +} + +// GetNode returns the root of this Directory +func (d *Directory) GetNode() (node.Node, error) { + if d.shard == nil { + return d.dirnode, nil + } + + return d.shard.Node() } diff --git a/unixfs/io/dirbuilder_test.go b/unixfs/io/dirbuilder_test.go index e7539a8bc..f07ee8894 100644 --- a/unixfs/io/dirbuilder_test.go +++ b/unixfs/io/dirbuilder_test.go @@ -2,49 +2,157 @@ package io import ( "context" - "io/ioutil" + "fmt" "testing" - testu "github.com/ipfs/go-ipfs/unixfs/test" + mdtest "github.com/ipfs/go-ipfs/merkledag/test" + ft "github.com/ipfs/go-ipfs/unixfs" ) func TestEmptyNode(t *testing.T) { - n := NewEmptyDirectory() + n := ft.EmptyDirNode() if len(n.Links()) != 0 { t.Fatal("empty node should have 0 links") } } +func TestDirectoryGrowth(t *testing.T) { + ds := mdtest.Mock() + dir := NewDirectory(ds) + ctx := context.Background() + + d := ft.EmptyDirNode() + ds.Add(d) + + nelems := 10000 + + for i := 0; i < nelems; i++ { + err := dir.AddChild(ctx, fmt.Sprintf("dir%d", i), d) + if err != nil { + t.Fatal(err) + } + } + + _, err := dir.GetNode() + if err != nil { + t.Fatal(err) + } + + links, err := dir.Links() + if err != nil { + t.Fatal(err) + } + + if len(links) != nelems { + t.Fatal("didnt get right number of elements") + } + + dirc := d.Cid() + + names := make(map[string]bool) + for _, l := range links { + names[l.Name] = true + if !l.Cid.Equals(dirc) { + t.Fatal("link wasnt correct") + } + } + + for i := 0; i < nelems; i++ { + dn := fmt.Sprintf("dir%d", i) + if !names[dn] { + t.Fatal("didnt find directory: ", dn) + } + + _, err := dir.Find(context.Background(), dn) + if err != nil { + t.Fatal(err) + } + } +} + +func TestDuplicateAddDir(t *testing.T) { + ds := mdtest.Mock() + dir := NewDirectory(ds) + ctx := context.Background() + nd := ft.EmptyDirNode() + + err := dir.AddChild(ctx, "test", nd) + if err != nil { + t.Fatal(err) + } + + err = dir.AddChild(ctx, "test", nd) + if err != nil { + t.Fatal(err) + } + + lnks, err := dir.Links() + if err != nil { + t.Fatal(err) + } + + if len(lnks) != 1 { + t.Fatal("expected only one link") + } +} + func TestDirBuilder(t *testing.T) { - dserv := testu.GetDAGServ() - ctx, closer := context.WithCancel(context.Background()) - defer closer() - inbuf, node := testu.GetRandomNode(t, dserv, 1024) - key := node.Cid() + ds := mdtest.Mock() + dir := NewDirectory(ds) + ctx := context.Background() - b := NewDirectory(dserv) + child := ft.EmptyDirNode() + _, err := ds.Add(child) + if err != nil { + t.Fatal(err) + } - b.AddChild(ctx, "random", key) + count := 5000 - dir := b.GetNode() - outn, err := dir.GetLinkedProtoNode(ctx, dserv, "random") + for i := 0; i < count; i++ { + err := dir.AddChild(ctx, fmt.Sprintf("entry %d", i), child) + if err != nil { + t.Fatal(err) + } + } + + dirnd, err := dir.GetNode() if err != nil { t.Fatal(err) } - reader, err := NewDagReader(ctx, outn, dserv) + links, err := dir.Links() if err != nil { t.Fatal(err) } - outbuf, err := ioutil.ReadAll(reader) + if len(links) != count { + t.Fatal("not enough links dawg", len(links), count) + } + + adir, err := NewDirectoryFromNode(ds, dirnd) if err != nil { t.Fatal(err) } - err = testu.ArrComp(inbuf, outbuf) + links, err = adir.Links() if err != nil { t.Fatal(err) } + names := make(map[string]bool) + for _, lnk := range links { + names[lnk.Name] = true + } + + for i := 0; i < count; i++ { + n := fmt.Sprintf("entry %d", i) + if !names[n] { + t.Fatal("COULDNT FIND: ", n) + } + } + + if len(links) != count { + t.Fatal("wrong number of links", len(links), count) + } } diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index 5970e72b5..ab9239601 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -5,26 +5,21 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" + hamt "github.com/ipfs/go-ipfs/unixfs/hamt" node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" ) func ResolveUnixfsOnce(ctx context.Context, ds dag.DAGService, nd node.Node, name string) (*node.Link, error) { - pbnd, ok := nd.(*dag.ProtoNode) - if !ok { - lnk, _, err := nd.ResolveLink([]string{name}) - return lnk, err - } - - upb, err := ft.FromBytes(pbnd.Data()) - if err != nil { - // Not a unixfs node, use standard object traversal code - lnk, _, err := nd.ResolveLink([]string{name}) - return lnk, err - } - - switch upb.GetType() { - /* + switch nd := nd.(type) { + case *dag.ProtoNode: + upb, err := ft.FromBytes(nd.Data()) + if err != nil { + // Not a unixfs node, use standard object traversal code + return nd.GetNodeLink(name) + } + + switch upb.GetType() { case ft.THAMTShard: s, err := hamt.NewHamtFromDag(ds, nd) if err != nil { @@ -37,10 +32,15 @@ func ResolveUnixfsOnce(ctx context.Context, ds dag.DAGService, nd node.Node, nam return nil, err } - return dag.MakeLink(out) - */ + return node.MakeLink(out) + default: + return nd.GetNodeLink(name) + } default: lnk, _, err := nd.ResolveLink([]string{name}) - return lnk, err + if err != nil { + return nil, err + } + return lnk, nil } } diff --git a/unixfs/pb/unixfs.pb.go b/unixfs/pb/unixfs.pb.go index ffd3bb905..e28053031 100644 --- a/unixfs/pb/unixfs.pb.go +++ b/unixfs/pb/unixfs.pb.go @@ -31,6 +31,7 @@ const ( Data_File Data_DataType = 2 Data_Metadata Data_DataType = 3 Data_Symlink Data_DataType = 4 + Data_HAMTShard Data_DataType = 5 ) var Data_DataType_name = map[int32]string{ @@ -39,6 +40,7 @@ var Data_DataType_name = map[int32]string{ 2: "File", 3: "Metadata", 4: "Symlink", + 5: "HAMTShard", } var Data_DataType_value = map[string]int32{ "Raw": 0, @@ -46,6 +48,7 @@ var Data_DataType_value = map[string]int32{ "File": 2, "Metadata": 3, "Symlink": 4, + "HAMTShard": 5, } func (x Data_DataType) Enum() *Data_DataType { @@ -70,6 +73,8 @@ type Data struct { Data []byte `protobuf:"bytes,2,opt,name=Data" json:"Data,omitempty"` Filesize *uint64 `protobuf:"varint,3,opt,name=filesize" json:"filesize,omitempty"` Blocksizes []uint64 `protobuf:"varint,4,rep,name=blocksizes" json:"blocksizes,omitempty"` + HashType *uint64 `protobuf:"varint,5,opt,name=hashType" json:"hashType,omitempty"` + Fanout *uint64 `protobuf:"varint,6,opt,name=fanout" json:"fanout,omitempty"` XXX_unrecognized []byte `json:"-"` } @@ -105,6 +110,20 @@ func (m *Data) GetBlocksizes() []uint64 { return nil } +func (m *Data) GetHashType() uint64 { + if m != nil && m.HashType != nil { + return *m.HashType + } + return 0 +} + +func (m *Data) GetFanout() uint64 { + if m != nil && m.Fanout != nil { + return *m.Fanout + } + return 0 +} + type Metadata struct { MimeType *string `protobuf:"bytes,1,opt,name=MimeType" json:"MimeType,omitempty"` XXX_unrecognized []byte `json:"-"` diff --git a/unixfs/pb/unixfs.proto b/unixfs/pb/unixfs.proto index 2e4d47947..6feb7aad6 100644 --- a/unixfs/pb/unixfs.proto +++ b/unixfs/pb/unixfs.proto @@ -7,12 +7,16 @@ message Data { File = 2; Metadata = 3; Symlink = 4; + HAMTShard = 5; } required DataType Type = 1; optional bytes Data = 2; optional uint64 filesize = 3; repeated uint64 blocksizes = 4; + + optional uint64 hashType = 5; + optional uint64 fanout = 6; } message Metadata { From b9adb607a87ebf2a02d8577be4db203f28fda0f3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 15 Nov 2016 16:17:22 -0800 Subject: [PATCH 1771/3817] iterator technique for unixfs dir listing License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@c9a8a08c5a05dad83a30358f8d46af4ab5bc1817 --- unixfs/hamt/hamt.go | 18 ++++++++++-------- unixfs/io/dirbuilder.go | 13 +++++++++++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 05b9402fd..7d0e47909 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -321,7 +321,15 @@ func (ds *HamtShard) getValue(ctx context.Context, hv *hashBits, key string, cb func (ds *HamtShard) EnumLinks() ([]*node.Link, error) { var links []*node.Link - err := ds.walkTrie(func(sv *shardValue) error { + err := ds.ForEachLink(func(l *node.Link) error { + links = append(links, l) + return nil + }) + return links, err +} + +func (ds *HamtShard) ForEachLink(f func(*node.Link) error) error { + return ds.walkTrie(func(sv *shardValue) error { lnk, err := node.MakeLink(sv.val) if err != nil { return err @@ -329,14 +337,8 @@ func (ds *HamtShard) EnumLinks() ([]*node.Link, error) { lnk.Name = sv.key - links = append(links, lnk) - return nil + return f(lnk) }) - if err != nil { - return nil, err - } - - return links, nil } func (ds *HamtShard) walkTrie(cb func(*shardValue) error) error { diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index fdb616e1b..5f0fc2242 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -102,6 +102,19 @@ func (d *Directory) switchToSharding(ctx context.Context) error { return nil } +func (d *Directory) ForEachLink(f func(*node.Link) error) error { + if d.shard == nil { + for _, l := range d.dirnode.Links() { + if err := f(l); err != nil { + return err + } + } + return nil + } + + return d.shard.ForEachLink(f) +} + func (d *Directory) Links() ([]*node.Link, error) { if d.shard == nil { return d.dirnode.Links(), nil From cb54e845e962c6312bc243e44d6e1b64c2bda8cd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 17 Nov 2016 13:39:37 -0800 Subject: [PATCH 1772/3817] add more docs on hamt License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@ba636f0e7880dc3834a436b95c35d97a86018ee4 --- unixfs/hamt/hamt.go | 25 +++++++++++++++++++++++++ unixfs/hamt/util.go | 2 ++ 2 files changed, 27 insertions(+) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 7d0e47909..a3b0c24d9 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -1,3 +1,23 @@ +// Package hamt implements a Hash Array Mapped Trie over ipfs merkledag nodes. +// It is implemented mostly as described in the wikipedia article on HAMTs, +// however the table size is variable (usually 256 in our usages) as opposed to +// 32 as suggested in the article. The hash function used is currently +// Murmur3, but this value is configurable (the datastructure reports which +// hash function its using). +// +// The one algorithmic change we implement that is not mentioned in the +// wikipedia article is the collapsing of empty shards. +// Given the following tree: ( '[' = shards, '{' = values ) +// [ 'A' ] -> [ 'B' ] -> { "ABC" } +// | L-> { "ABD" } +// L-> { "ASDF" } +// If we simply removed "ABC", we would end up with a tree where shard 'B' only +// has a single child. This causes two issues, the first, is that now we have +// an extra lookup required to get to "ABD". The second issue is that now we +// have a tree that contains only "ABD", but is not the same tree that we would +// get by simply inserting "ABD" into a new tree. To address this, we always +// check for empty shard nodes upon deletion and prune them to maintain a +// consistent tree, independent of insertion order. package hamt import ( @@ -450,10 +470,15 @@ func (ds *HamtShard) modifyValue(ctx context.Context, hv *hashBits, key string, } } +// indexForBitPos returns the index within the collapsed array corresponding to +// the given bit in the bitset. The collapsed array contains only one entry +// per bit set in the bitfield, and this function is used to map the indices. func (ds *HamtShard) indexForBitPos(bp int) int { // TODO: an optimization could reuse the same 'mask' here and change the size // as needed. This isnt yet done as the bitset package doesnt make it easy // to do. + + // make a bitmask (all bits set) 'bp' bits long mask := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(bp)), nil), big.NewInt(1)) mask.And(mask, ds.bitfield) diff --git a/unixfs/hamt/util.go b/unixfs/hamt/util.go index 1727dbfed..08c232a8a 100644 --- a/unixfs/hamt/util.go +++ b/unixfs/hamt/util.go @@ -4,6 +4,7 @@ import ( "math/big" ) +// hashBits is a helper that allows the reading of the 'next n bits' as an integer. type hashBits struct { b []byte consumed int @@ -13,6 +14,7 @@ func mkmask(n int) byte { return (1 << uint(n)) - 1 } +// Next returns the next 'i' bits of the hashBits value as an integer func (hb *hashBits) Next(i int) int { curbi := hb.consumed / 8 leftb := 8 - (hb.consumed % 8) From 1deaaea2cfcff221b3f55de809f98b5fd7652d13 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 4 Aug 2016 12:45:00 -0700 Subject: [PATCH 1773/3817] implement an HAMT for unixfs directory sharding License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@17ba1579599743a6374ed4ff3eac70d9de6c3de3 --- mfs/dir.go | 116 ++++++++++++++++++++++++++------------------ mfs/mfs_test.go | 125 ++++++++++++++++++++++++++++++++++++++++++++++++ mfs/system.go | 7 ++- 3 files changed, 200 insertions(+), 48 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index f1a61eefa..51a26cf13 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -12,6 +12,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" + uio "github.com/ipfs/go-ipfs/unixfs/io" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" @@ -29,25 +30,31 @@ type Directory struct { files map[string]*File lock sync.Mutex - node *dag.ProtoNode ctx context.Context + dirbuilder *uio.Directory + modTime time.Time name string } -func NewDirectory(ctx context.Context, name string, node *dag.ProtoNode, parent childCloser, dserv dag.DAGService) *Directory { - return &Directory{ - dserv: dserv, - ctx: ctx, - name: name, - node: node, - parent: parent, - childDirs: make(map[string]*Directory), - files: make(map[string]*File), - modTime: time.Now(), +func NewDirectory(ctx context.Context, name string, node node.Node, parent childCloser, dserv dag.DAGService) (*Directory, error) { + db, err := uio.NewDirectoryFromNode(dserv, node) + if err != nil { + return nil, err } + + return &Directory{ + dserv: dserv, + ctx: ctx, + name: name, + dirbuilder: db, + parent: parent, + childDirs: make(map[string]*Directory), + files: make(map[string]*File), + modTime: time.Now(), + }, nil } // closeChild updates the child by the given name to the dag node 'nd' @@ -81,21 +88,26 @@ func (d *Directory) closeChildUpdate(name string, nd *dag.ProtoNode, sync bool) } func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) { - _, err := d.dserv.Add(d.node) + nd, err := d.dirbuilder.GetNode() if err != nil { return nil, err } - return d.node.Copy().(*dag.ProtoNode), nil -} + _, err = d.dserv.Add(nd) + if err != nil { + return nil, err + } -func (d *Directory) updateChild(name string, nd node.Node) error { - err := d.node.RemoveNodeLink(name) - if err != nil && err != dag.ErrNotFound { - return err + pbnd, ok := nd.(*dag.ProtoNode) + if !ok { + return nil, dag.ErrNotProtobuf } - err = d.node.AddNodeLinkClean(name, nd) + return pbnd.Copy().(*dag.ProtoNode), nil +} + +func (d *Directory) updateChild(name string, nd node.Node) error { + err := d.dirbuilder.AddChild(d.ctx, name, nd) if err != nil { return err } @@ -130,8 +142,12 @@ func (d *Directory) cacheNode(name string, nd node.Node) (FSNode, error) { } switch i.GetType() { - case ufspb.Data_Directory: - ndir := NewDirectory(d.ctx, name, nd, d, d.dserv) + case ufspb.Data_Directory, ufspb.Data_HAMTShard: + ndir, err := NewDirectory(d.ctx, name, nd, d, d.dserv) + if err != nil { + return nil, err + } + d.childDirs[name] = ndir return ndir, nil case ufspb.Data_File, ufspb.Data_Raw, ufspb.Data_Symlink: @@ -175,15 +191,7 @@ func (d *Directory) Uncache(name string) { // childFromDag searches through this directories dag node for a child link // with the given name func (d *Directory) childFromDag(name string) (node.Node, error) { - pbn, err := d.node.GetLinkedNode(d.ctx, d.dserv, name) - switch err { - case nil: - return pbn, nil - case dag.ErrLinkNotFound: - return nil, os.ErrNotExist - default: - return nil, err - } + return d.dirbuilder.Find(d.ctx, name) } // childUnsync returns the child under this directory by the given name @@ -209,7 +217,7 @@ type NodeListing struct { Hash string } -func (d *Directory) ListNames() []string { +func (d *Directory) ListNames() ([]string, error) { d.lock.Lock() defer d.lock.Unlock() @@ -221,7 +229,12 @@ func (d *Directory) ListNames() []string { names[n] = struct{}{} } - for _, l := range d.node.Links() { + links, err := d.dirbuilder.Links() + if err != nil { + return nil, err + } + + for _, l := range links { names[l.Name] = struct{}{} } @@ -231,7 +244,7 @@ func (d *Directory) ListNames() []string { } sort.Strings(out) - return out + return out, nil } func (d *Directory) List() ([]NodeListing, error) { @@ -239,7 +252,13 @@ func (d *Directory) List() ([]NodeListing, error) { defer d.lock.Unlock() var out []NodeListing - for _, l := range d.node.Links() { + + links, err := d.dirbuilder.Links() + if err != nil { + return nil, err + } + + for _, l := range links { child := NodeListing{} child.Name = l.Name @@ -285,20 +304,23 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { } } - ndir := new(dag.ProtoNode) - ndir.SetData(ft.FolderPBData()) + ndir := ft.EmptyDirNode() _, err = d.dserv.Add(ndir) if err != nil { return nil, err } - err = d.node.AddNodeLinkClean(name, ndir) + err = d.dirbuilder.AddChild(d.ctx, name, ndir) + if err != nil { + return nil, err + } + + dirobj, err := NewDirectory(d.ctx, name, ndir, d, d.dserv) if err != nil { return nil, err } - dirobj := NewDirectory(d.ctx, name, ndir, d, d.dserv) d.childDirs[name] = dirobj return dirobj, nil } @@ -310,12 +332,7 @@ func (d *Directory) Unlink(name string) error { delete(d.childDirs, name) delete(d.files, name) - err := d.node.RemoveNodeLink(name) - if err != nil { - return err - } - - _, err = d.dserv.Add(d.node) + err := d.dirbuilder.RemoveChild(d.ctx, name) if err != nil { return err } @@ -350,7 +367,7 @@ func (d *Directory) AddChild(name string, nd node.Node) error { return err } - err = d.node.AddNodeLinkClean(name, nd) + err = d.dirbuilder.AddChild(d.ctx, name, nd) if err != nil { return err } @@ -406,10 +423,15 @@ func (d *Directory) GetNode() (node.Node, error) { return nil, err } - _, err = d.dserv.Add(d.node) + nd, err := d.dirbuilder.GetNode() + if err != nil { + return nil, err + } + + _, err = d.dserv.Add(nd) if err != nil { return nil, err } - return d.node.Copy().(*dag.ProtoNode), nil + return nd, err } diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 74e0d6dfb..8471fd02c 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -747,6 +747,67 @@ func TestMfsStress(t *testing.T) { } } +func TestMfsHugeDir(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + _, rt := setupRoot(ctx, t) + + for i := 0; i < 100000; i++ { + err := Mkdir(rt, fmt.Sprintf("/dir%d", i), false, false) + if err != nil { + t.Fatal(err) + } + } +} + +func TestMkdirP(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + _, rt := setupRoot(ctx, t) + + err := Mkdir(rt, "/a/b/c/d/e/f", true, true) + if err != nil { + t.Fatal(err) + } +} + +func TestConcurrentWriteAndFlush(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ds, rt := setupRoot(ctx, t) + + d := mkdirP(t, rt.GetValue().(*Directory), "foo/bar/baz") + fn := fileNodeFromReader(t, ds, bytes.NewBuffer(nil)) + err := d.AddChild("file", fn) + if err != nil { + t.Fatal(err) + } + + nloops := 5000 + + wg := new(sync.WaitGroup) + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < nloops; i++ { + err := writeFile(rt, "/foo/bar/baz/file", []byte("STUFF")) + if err != nil { + t.Error("file write failed: ", err) + return + } + } + }() + + for i := 0; i < nloops; i++ { + _, err := rt.GetValue().GetNode() + if err != nil { + t.Fatal(err) + } + } + + wg.Wait() +} + func TestFlushing(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -892,6 +953,70 @@ func TestConcurrentReads(t *testing.T) { } wg.Wait() } +func writeFile(rt *Root, path string, data []byte) error { + n, err := Lookup(rt, path) + if err != nil { + return err + } + + fi, ok := n.(*File) + if !ok { + return fmt.Errorf("expected to receive a file, but didnt get one") + } + + fd, err := fi.Open(OpenWriteOnly, true) + if err != nil { + return err + } + defer fd.Close() + + nw, err := fd.Write(data) + if err != nil { + return err + } + + if nw != 10 { + fmt.Errorf("wrote incorrect amount") + } + + return nil +} + +func TestConcurrentWrites(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ds, rt := setupRoot(ctx, t) + + rootdir := rt.GetValue().(*Directory) + + path := "a/b/c" + d := mkdirP(t, rootdir, path) + + fi := fileNodeFromReader(t, ds, bytes.NewReader(make([]byte, 0))) + err := d.AddChild("afile", fi) + if err != nil { + t.Fatal(err) + } + + var wg sync.WaitGroup + nloops := 100 + for i := 0; i < 10; i++ { + wg.Add(1) + go func(me int) { + defer wg.Done() + mybuf := bytes.Repeat([]byte{byte(me)}, 10) + for j := 0; j < nloops; j++ { + err := writeFile(rt, "a/b/c/afile", mybuf) + if err != nil { + t.Error("writefile failed: ", err) + return + } + } + }(i) + } + wg.Wait() +} func TestFileDescriptors(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) diff --git a/mfs/system.go b/mfs/system.go index 1c57677b5..05d2a1c53 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -88,7 +88,12 @@ func NewRoot(parent context.Context, ds dag.DAGService, node *dag.ProtoNode, pf switch pbn.GetType() { case ft.TDirectory: - root.val = NewDirectory(parent, node.String(), node, root, ds) + rval, err := NewDirectory(parent, node.String(), node, root, ds) + if err != nil { + return nil, err + } + + root.val = rval case ft.TFile, ft.TMetadata, ft.TRaw: fi, err := NewFile(node.String(), node, root, ds) if err != nil { From 20c0bb05e4e5684897dd68066280e61dbc30ac11 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 22 Nov 2016 12:31:10 -0800 Subject: [PATCH 1774/3817] chekc that size input to newHamtShard is a power of two License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@a6f8ee7010efaafc2aaadd5bc5f88c1baf3b4fbf --- unixfs/hamt/hamt.go | 33 ++++++++++++++++++++++++--------- unixfs/hamt/hamt_stress_test.go | 18 +++++++++++++++--- unixfs/hamt/hamt_test.go | 21 ++++++++++++++------- unixfs/io/dirbuilder.go | 7 ++++++- 4 files changed, 59 insertions(+), 20 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index a3b0c24d9..c9d6eb9dc 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -64,23 +64,31 @@ type child interface { Label() string } -func NewHamtShard(dserv dag.DAGService, size int) *HamtShard { - ds := makeHamtShard(dserv, size) +func NewHamtShard(dserv dag.DAGService, size int) (*HamtShard, error) { + ds, err := makeHamtShard(dserv, size) + if err != nil { + return nil, err + } + ds.bitfield = big.NewInt(0) ds.nd = new(dag.ProtoNode) ds.hashFunc = HashMurmur3 - return ds + return ds, nil } -func makeHamtShard(ds dag.DAGService, size int) *HamtShard { +func makeHamtShard(ds dag.DAGService, size int) (*HamtShard, error) { + lg2s := int(math.Log2(float64(size))) + if 1< Date: Tue, 15 Nov 2016 16:17:22 -0800 Subject: [PATCH 1775/3817] iterator technique for unixfs dir listing License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@0d1ebb9077bc0e0bb5a10232cf7c2c80eff1c74f --- mfs/dir.go | 74 +++++++++++++++++++++++------------------------------- 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 51a26cf13..cab2d9a1f 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -221,71 +221,59 @@ func (d *Directory) ListNames() ([]string, error) { d.lock.Lock() defer d.lock.Unlock() - names := make(map[string]struct{}) - for n, _ := range d.childDirs { - names[n] = struct{}{} - } - for n, _ := range d.files { - names[n] = struct{}{} - } - - links, err := d.dirbuilder.Links() + var out []string + err := d.dirbuilder.ForEachLink(func(l *node.Link) error { + out = append(out, l.Name) + return nil + }) if err != nil { return nil, err } - for _, l := range links { - names[l.Name] = struct{}{} - } - - var out []string - for n, _ := range names { - out = append(out, n) - } sort.Strings(out) return out, nil } func (d *Directory) List() ([]NodeListing, error) { - d.lock.Lock() - defer d.lock.Unlock() - var out []NodeListing + err := d.ForEachEntry(context.TODO(), func(nl NodeListing) error { + out = append(out, nl) + return nil + }) + return out, err +} - links, err := d.dirbuilder.Links() - if err != nil { - return nil, err - } - - for _, l := range links { - child := NodeListing{} - child.Name = l.Name - +func (d *Directory) ForEachEntry(ctx context.Context, f func(NodeListing) error) error { + d.lock.Lock() + defer d.lock.Unlock() + return d.dirbuilder.ForEachLink(func(l *node.Link) error { c, err := d.childUnsync(l.Name) if err != nil { - return nil, err + return err + } + + nd, err := c.GetNode() + if err != nil { + return err + } + + child := NodeListing{ + Name: l.Name, + Type: int(c.Type()), + Hash: nd.Cid().String(), } - child.Type = int(c.Type()) if c, ok := c.(*File); ok { size, err := c.Size() if err != nil { - return nil, err + return err } child.Size = size } - nd, err := c.GetNode() - if err != nil { - return nil, err - } - child.Hash = nd.Cid().String() - - out = append(out, child) - } - - return out, nil + return f(child) + }) } func (d *Directory) Mkdir(name string) (*Directory, error) { @@ -433,5 +421,5 @@ func (d *Directory) GetNode() (node.Node, error) { return nil, err } - return nd, err + return nd.Copy(), err } From d69addcebfadcbcaeff8e8d5c7b73ff5774289e9 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 21 Mar 2017 12:25:48 +0100 Subject: [PATCH 1776/3817] Fix #3783: Improve IsPinned() lookups for indirect pins This avoids revisiting already-searched branches and cut down the IsPinned() check times considerably when recursive pins share big underlying DAGs. A test has been added which double-checks that pinned and unpinned items lookups respond as expected with shared branches. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-pinner@071a31787afb705cfaae868e37e936609d517524 --- pinning/pinner/pin.go | 22 ++++--- pinning/pinner/pin_test.go | 127 +++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 9 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 489809b0c..e0c211ffb 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -276,8 +276,9 @@ func (p *pinner) isPinnedWithType(c *cid.Cid, mode PinMode) (string, bool, error } // Default is Indirect + visitedSet := cid.NewSet() for _, rc := range p.recursePin.Keys() { - has, err := hasChild(p.dserv, rc, c) + has, err := hasChild(p.dserv, rc, c, visitedSet.Visit) if err != nil { return "", false, err } @@ -519,7 +520,9 @@ func (p *pinner) PinWithMode(c *cid.Cid, mode PinMode) { } } -func hasChild(ds mdag.LinkService, root *cid.Cid, child *cid.Cid) (bool, error) { +// hasChild recursively looks for a Cid among the children of a root Cid. +// The visit function can be used to shortcut already-visited branches. +func hasChild(ds mdag.LinkService, root *cid.Cid, child *cid.Cid, visit func(*cid.Cid) bool) (bool, error) { links, err := ds.GetLinks(context.Background(), root) if err != nil { return false, err @@ -529,14 +532,15 @@ func hasChild(ds mdag.LinkService, root *cid.Cid, child *cid.Cid) (bool, error) if lnk.Cid.Equals(child) { return true, nil } + if visit(c) { + has, err := hasChild(ds, c, child, visit) + if err != nil { + return false, err + } - has, err := hasChild(ds, c, child) - if err != nil { - return false, err - } - - if has { - return has, nil + if has { + return has, nil + } } } return false, nil diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index e9c8a8843..009373dfe 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -35,6 +35,17 @@ func assertPinned(t *testing.T, p Pinner, c *cid.Cid, failmsg string) { } } +func assertUnpinned(t *testing.T, p Pinner, c *cid.Cid, failmsg string) { + _, pinned, err := p.IsPinned(c) + if err != nil { + t.Fatal(err) + } + + if pinned { + t.Fatal(failmsg) + } +} + func TestPinnerBasic(t *testing.T) { ctx := context.Background() @@ -145,6 +156,122 @@ func TestPinnerBasic(t *testing.T) { assertPinned(t, np, bk, "could not find recursively pinned node") } +func TestIsPinnedLookup(t *testing.T) { + // We are going to test that lookups work in pins which share + // the same branches. For that we will construct this tree: + // + // A5->A4->A3->A2->A1->A0 + // / / + // B------- / + // \ / + // C--------------- + // + // We will ensure that IsPinned works for all objects both when they + // are pinned and once they have been unpinned. + aBranchLen := 6 + if aBranchLen < 3 { + t.Fatal("set aBranchLen to at least 3") + } + + ctx := context.Background() + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv := bs.New(bstore, offline.Exchange(bstore)) + + dserv := mdag.NewDAGService(bserv) + + // TODO does pinner need to share datastore with blockservice? + p := NewPinner(dstore, dserv, dserv) + + aNodes := make([]*mdag.ProtoNode, aBranchLen, aBranchLen) + aKeys := make([]*cid.Cid, aBranchLen, aBranchLen) + for i := 0; i < aBranchLen; i++ { + a, _ := randNode() + if i >= 1 { + err := a.AddNodeLink("child", aNodes[i-1]) + if err != nil { + t.Fatal(err) + } + } + + ak, err := dserv.Add(a) + if err != nil { + t.Fatal(err) + } + //t.Logf("a[%d] is %s", i, ak) + aNodes[i] = a + aKeys[i] = ak + } + + // Pin A5 recursively + if err := p.Pin(ctx, aNodes[aBranchLen-1], true); err != nil { + t.Fatal(err) + } + + // Create node B and add A3 as child + b, _ := randNode() + if err := b.AddNodeLink("mychild", aNodes[3]); err != nil { + t.Fatal(err) + } + + // Create C node + c, _ := randNode() + // Add A0 as child of C + if err := c.AddNodeLink("child", aNodes[0]); err != nil { + t.Fatal(err) + } + + // Add C + ck, err := dserv.Add(c) + if err != nil { + t.Fatal(err) + } + //t.Logf("C is %s", ck) + + // Add C to B and Add B + if err := b.AddNodeLink("myotherchild", c); err != nil { + t.Fatal(err) + } + bk, err := dserv.Add(b) + if err != nil { + t.Fatal(err) + } + //t.Logf("B is %s", bk) + + // Pin C recursively + + if err := p.Pin(ctx, c, true); err != nil { + t.Fatal(err) + } + + // Pin B recursively + + if err := p.Pin(ctx, b, true); err != nil { + t.Fatal(err) + } + + assertPinned(t, p, aKeys[0], "A0 should be pinned") + assertPinned(t, p, aKeys[1], "A1 should be pinned") + assertPinned(t, p, ck, "C should be pinned") + assertPinned(t, p, bk, "B should be pinned") + + // Unpin A5 recursively + if err := p.Unpin(ctx, aKeys[5], true); err != nil { + t.Fatal(err) + } + + assertPinned(t, p, aKeys[0], "A0 should still be pinned through B") + assertUnpinned(t, p, aKeys[4], "A4 should be unpinned") + + // Unpin B recursively + if err := p.Unpin(ctx, bk, true); err != nil { + t.Fatal(err) + } + assertUnpinned(t, p, bk, "B should be unpinned") + assertUnpinned(t, p, aKeys[1], "A1 should be unpinned") + assertPinned(t, p, aKeys[0], "A0 should still be pinned through C") +} + func TestDuplicateSemantics(t *testing.T) { ctx := context.Background() dstore := dssync.MutexWrap(ds.NewMapDatastore()) From 88584b794099e645529d24f91f0ba9c9747b08a0 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 3 Feb 2017 16:20:51 -0500 Subject: [PATCH 1777/3817] filestore util: basic filestore commands. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@bdf9b0ef4b752a512480e033ce10b918d5bb3698 --- filestore/util.go | 138 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 filestore/util.go diff --git a/filestore/util.go b/filestore/util.go new file mode 100644 index 000000000..ac6909bc6 --- /dev/null +++ b/filestore/util.go @@ -0,0 +1,138 @@ +package filestore + +import ( + "fmt" + + pb "github.com/ipfs/go-ipfs/filestore/pb" + dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + + ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" + proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" + cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" +) + +type Status int32 + +const ( + StatusOk Status = 0 + StatusFileError Status = 10 // Backing File Error + //StatusFileNotFound Status = 11 // Backing File Not Found + //StatusFileChanged Status = 12 // Contents of the file changed + StatusOtherError Status = 20 // Internal Error, likely corrupt entry +) + +func (s Status) String() string { + switch s { + case StatusOk: + return "ok" + case StatusFileError: + return "error" + case StatusOtherError: + return "ERROR" + default: + return "???" + } +} + +func (s Status) Format() string { + return fmt.Sprintf("%-5s", s.String()) +} + +type ListRes struct { + Status Status + ErrorMsg string + Key *cid.Cid + FilePath string + Offset uint64 + Size uint64 +} + +func (r *ListRes) FormatLong() string { + switch { + case r.Key == nil: + return "?????????????????????????????????????????????????" + default: + return fmt.Sprintf("%-50s %6d %s %d", r.Key, r.Size, r.FilePath, r.Offset) + } +} + +func ListAll(fs *Filestore) (func() *ListRes, error) { + return listAll(fs, false) +} + +func VerifyAll(fs *Filestore) (func() *ListRes, error) { + return listAll(fs, true) +} + +func listAll(fs *Filestore, verify bool) (func() *ListRes, error) { + q := dsq.Query{} + qr, err := fs.fm.ds.Query(q) + if err != nil { + return nil, err + } + + return func() *ListRes { + cid, dobj, err := next(qr) + if dobj == nil && err == nil { + return nil + } else if err == nil && verify { + _, err = fs.fm.readDataObj(cid, dobj) + } + return mkListRes(cid, dobj, err) + }, nil +} + +func next(qr dsq.Results) (*cid.Cid, *pb.DataObj, error) { + v, ok := qr.NextSync() + if !ok { + return nil, nil, nil + } + + k := ds.RawKey(v.Key) + c, err := dshelp.DsKeyToCid(k) + if err != nil { + return nil, nil, fmt.Errorf("decoding cid from filestore: %s", err) + } + + data, ok := v.Value.([]byte) + if !ok { + return c, nil, fmt.Errorf("stored filestore dataobj was not a []byte") + } + + var dobj pb.DataObj + if err := proto.Unmarshal(data, &dobj); err != nil { + return c, nil, err + } + + return c, &dobj, nil +} + +func mkListRes(c *cid.Cid, d *pb.DataObj, err error) *ListRes { + status := StatusOk + errorMsg := "" + if err != nil { + if _, ok := err.(*CorruptReferenceError); ok { + status = StatusFileError + } else { + status = StatusOtherError + } + errorMsg = err.Error() + } + if d == nil { + return &ListRes{ + Status: status, + ErrorMsg: errorMsg, + Key: c, + } + } else { + return &ListRes{ + Status: status, + ErrorMsg: errorMsg, + Key: c, + FilePath: *d.FilePath, + Size: *d.Size_, + Offset: *d.Offset, + } + } +} From 6e1a8cdc7cff32937876a169d99469508edc8295 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 7 Mar 2017 00:48:53 -0500 Subject: [PATCH 1778/3817] filestore util: allow listing/verifying of individual blocks. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@8fdbd2fa700e769d4280f04b9a4a617f31ea3e2a --- filestore/util.go | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/filestore/util.go b/filestore/util.go index ac6909bc6..75bb76bc1 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -3,6 +3,7 @@ package filestore import ( "fmt" + "github.com/ipfs/go-ipfs/blocks/blockstore" pb "github.com/ipfs/go-ipfs/filestore/pb" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" @@ -19,7 +20,8 @@ const ( StatusFileError Status = 10 // Backing File Error //StatusFileNotFound Status = 11 // Backing File Not Found //StatusFileChanged Status = 12 // Contents of the file changed - StatusOtherError Status = 20 // Internal Error, likely corrupt entry + StatusOtherError Status = 20 // Internal Error, likely corrupt entry + StatusKeyNotFound Status = 30 ) func (s Status) String() string { @@ -30,13 +32,15 @@ func (s Status) String() string { return "error" case StatusOtherError: return "ERROR" + case StatusKeyNotFound: + return "missing" default: return "???" } } func (s Status) Format() string { - return fmt.Sprintf("%-5s", s.String()) + return fmt.Sprintf("%-7s", s.String()) } type ListRes struct { @@ -52,19 +56,40 @@ func (r *ListRes) FormatLong() string { switch { case r.Key == nil: return "?????????????????????????????????????????????????" + case r.FilePath == "": + return r.Key.String() default: return fmt.Sprintf("%-50s %6d %s %d", r.Key, r.Size, r.FilePath, r.Offset) } } +func List(fs *Filestore, key *cid.Cid) *ListRes { + return list(fs, false, key) +} + func ListAll(fs *Filestore) (func() *ListRes, error) { return listAll(fs, false) } +func Verify(fs *Filestore, key *cid.Cid) *ListRes { + return list(fs, true, key) +} + func VerifyAll(fs *Filestore) (func() *ListRes, error) { return listAll(fs, true) } +func list(fs *Filestore, verify bool, key *cid.Cid) *ListRes { + dobj, err := fs.fm.getDataObj(key) + if err != nil { + return mkListRes(key, nil, err) + } + if verify { + _, err = fs.fm.readDataObj(key, dobj) + } + return mkListRes(key, dobj, err) +} + func listAll(fs *Filestore, verify bool) (func() *ListRes, error) { q := dsq.Query{} qr, err := fs.fm.ds.Query(q) @@ -112,7 +137,9 @@ func mkListRes(c *cid.Cid, d *pb.DataObj, err error) *ListRes { status := StatusOk errorMsg := "" if err != nil { - if _, ok := err.(*CorruptReferenceError); ok { + if err == ds.ErrNotFound || err == blockstore.ErrNotFound { + status = StatusKeyNotFound + } else if _, ok := err.(*CorruptReferenceError); ok { status = StatusFileError } else { status = StatusOtherError From 8eeb62628efbfc79ad57aefa9f00e3f6d9615bed Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 3 Feb 2017 18:34:08 -0500 Subject: [PATCH 1779/3817] filestore: be more specific when there is a problem reading the backing file. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@b30419ca789a2d34283d4c1b99df7158d39a69a6 --- filestore/fsrefstore.go | 29 ++++++++++++++++++++++------- filestore/util.go | 29 +++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index f333a845d..5af21d419 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -27,8 +27,18 @@ type FileManager struct { root string } +type CorruptReferenceCode int + +const ( + OtherErr CorruptReferenceCode = 0 + FileError CorruptReferenceCode = 1 + FileMissing CorruptReferenceCode = 2 + FileChanged CorruptReferenceCode = 3 +) + type CorruptReferenceError struct { - Err error + Code CorruptReferenceCode + Err error } func (c CorruptReferenceError) Error() string { @@ -127,20 +137,24 @@ func (f *FileManager) readDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { abspath := filepath.Join(f.root, p) fi, err := os.Open(abspath) - if err != nil { - return nil, &CorruptReferenceError{err} + if os.IsNotExist(err) { + return nil, &CorruptReferenceError{FileMissing, err} + } else if err != nil { + return nil, &CorruptReferenceError{FileError, err} } defer fi.Close() _, err = fi.Seek(int64(d.GetOffset()), os.SEEK_SET) if err != nil { - return nil, &CorruptReferenceError{err} + return nil, &CorruptReferenceError{FileError, err} } outbuf := make([]byte, d.GetSize_()) _, err = io.ReadFull(fi, outbuf) - if err != nil { - return nil, &CorruptReferenceError{err} + if err == io.EOF || err == io.ErrUnexpectedEOF { + return nil, &CorruptReferenceError{FileChanged, err} + } else if err != nil { + return nil, &CorruptReferenceError{FileError, err} } outcid, err := c.Prefix().Sum(outbuf) @@ -149,7 +163,8 @@ func (f *FileManager) readDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { } if !c.Equals(outcid) { - return nil, &CorruptReferenceError{fmt.Errorf("data in file did not match. %s offset %d", d.GetFilePath(), d.GetOffset())} + return nil, &CorruptReferenceError{FileChanged, + fmt.Errorf("data in file did not match. %s offset %d", d.GetFilePath(), d.GetOffset())} } return outbuf, nil diff --git a/filestore/util.go b/filestore/util.go index 75bb76bc1..8049f7ec1 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -16,12 +16,12 @@ import ( type Status int32 const ( - StatusOk Status = 0 - StatusFileError Status = 10 // Backing File Error - //StatusFileNotFound Status = 11 // Backing File Not Found - //StatusFileChanged Status = 12 // Contents of the file changed - StatusOtherError Status = 20 // Internal Error, likely corrupt entry - StatusKeyNotFound Status = 30 + StatusOk Status = 0 + StatusFileError Status = 10 // Backing File Error + StatusFileNotFound Status = 11 // Backing File Not Found + StatusFileChanged Status = 12 // Contents of the file changed + StatusOtherError Status = 20 // Internal Error, likely corrupt entry + StatusKeyNotFound Status = 30 ) func (s Status) String() string { @@ -30,6 +30,10 @@ func (s Status) String() string { return "ok" case StatusFileError: return "error" + case StatusFileNotFound: + return "no-file" + case StatusFileChanged: + return "changed" case StatusOtherError: return "ERROR" case StatusKeyNotFound: @@ -139,8 +143,17 @@ func mkListRes(c *cid.Cid, d *pb.DataObj, err error) *ListRes { if err != nil { if err == ds.ErrNotFound || err == blockstore.ErrNotFound { status = StatusKeyNotFound - } else if _, ok := err.(*CorruptReferenceError); ok { - status = StatusFileError + } else if err, ok := err.(*CorruptReferenceError); ok { + switch err.Code { + case FileError: + status = StatusFileError + case FileMissing: + status = StatusFileNotFound + case FileChanged: + status = StatusFileChanged + default: + status = StatusOtherError + } } else { status = StatusOtherError } From ae2535559cf9272742599ee1df03c5c97d820e98 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 6 Feb 2017 15:54:36 -0500 Subject: [PATCH 1780/3817] filestore: use the same codes in ListRes and CorruptReferenceError. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@141c09614909930ce9d75533d9f7452dbfcfec88 --- filestore/fsrefstore.go | 23 +++++++---------------- filestore/util.go | 11 +---------- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 5af21d419..f493139e5 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -27,17 +27,8 @@ type FileManager struct { root string } -type CorruptReferenceCode int - -const ( - OtherErr CorruptReferenceCode = 0 - FileError CorruptReferenceCode = 1 - FileMissing CorruptReferenceCode = 2 - FileChanged CorruptReferenceCode = 3 -) - type CorruptReferenceError struct { - Code CorruptReferenceCode + Code Status Err error } @@ -138,23 +129,23 @@ func (f *FileManager) readDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { fi, err := os.Open(abspath) if os.IsNotExist(err) { - return nil, &CorruptReferenceError{FileMissing, err} + return nil, &CorruptReferenceError{StatusFileNotFound, err} } else if err != nil { - return nil, &CorruptReferenceError{FileError, err} + return nil, &CorruptReferenceError{StatusFileError, err} } defer fi.Close() _, err = fi.Seek(int64(d.GetOffset()), os.SEEK_SET) if err != nil { - return nil, &CorruptReferenceError{FileError, err} + return nil, &CorruptReferenceError{StatusFileError, err} } outbuf := make([]byte, d.GetSize_()) _, err = io.ReadFull(fi, outbuf) if err == io.EOF || err == io.ErrUnexpectedEOF { - return nil, &CorruptReferenceError{FileChanged, err} + return nil, &CorruptReferenceError{StatusFileChanged, err} } else if err != nil { - return nil, &CorruptReferenceError{FileError, err} + return nil, &CorruptReferenceError{StatusFileError, err} } outcid, err := c.Prefix().Sum(outbuf) @@ -163,7 +154,7 @@ func (f *FileManager) readDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { } if !c.Equals(outcid) { - return nil, &CorruptReferenceError{FileChanged, + return nil, &CorruptReferenceError{StatusFileChanged, fmt.Errorf("data in file did not match. %s offset %d", d.GetFilePath(), d.GetOffset())} } diff --git a/filestore/util.go b/filestore/util.go index 8049f7ec1..652dfa229 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -144,16 +144,7 @@ func mkListRes(c *cid.Cid, d *pb.DataObj, err error) *ListRes { if err == ds.ErrNotFound || err == blockstore.ErrNotFound { status = StatusKeyNotFound } else if err, ok := err.(*CorruptReferenceError); ok { - switch err.Code { - case FileError: - status = StatusFileError - case FileMissing: - status = StatusFileNotFound - case FileChanged: - status = StatusFileChanged - default: - status = StatusOtherError - } + status = err.Code } else { status = StatusOtherError } From dcc7c50cb75ae1b797a37f8ccc77b0d579bb3692 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 6 Feb 2017 21:53:51 -0500 Subject: [PATCH 1781/3817] filestore util: Add 'filestore dups' command. Enhance tests. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@8b58a800986bba53b9b58a9d4a33b539aadff479 --- filestore/filestore.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/filestore/filestore.go b/filestore/filestore.go index eefd925e3..da3dc17c6 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -19,6 +19,14 @@ type Filestore struct { bs blockstore.Blockstore } +func (f *Filestore) FileManager() *FileManager { + return f.fm +} + +func (f *Filestore) MainBlockstore() blockstore.Blockstore { + return f.bs +} + func NewFilestore(bs blockstore.Blockstore, fm *FileManager) *Filestore { return &Filestore{fm, bs} } From 257c125808c4122292423dcff0310a0666890354 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 9 Feb 2017 11:59:16 -0500 Subject: [PATCH 1782/3817] filestore util: change "???..." to "" License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@987419d41b403f8a7e75030db9bf17e8677953e0 --- filestore/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/util.go b/filestore/util.go index 652dfa229..ef77b417a 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -59,7 +59,7 @@ type ListRes struct { func (r *ListRes) FormatLong() string { switch { case r.Key == nil: - return "?????????????????????????????????????????????????" + return "" case r.FilePath == "": return r.Key.String() default: From 73674400f2f9dea95235b7bfbcbc360827747b9c Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 9 Feb 2017 12:16:36 -0500 Subject: [PATCH 1783/3817] filestore: Refactor. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@48f9f6162283fad480ac8d3b6234b459b8397d80 --- filestore/fsrefstore.go | 4 ++++ filestore/util.go | 12 +++--------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index f493139e5..3e3750cb9 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -109,6 +109,10 @@ func (f *FileManager) getDataObj(c *cid.Cid) (*pb.DataObj, error) { // } + return unmarshalDataObj(o) +} + +func unmarshalDataObj(o interface{}) (*pb.DataObj, error) { data, ok := o.([]byte) if !ok { return nil, fmt.Errorf("stored filestore dataobj was not a []byte") diff --git a/filestore/util.go b/filestore/util.go index ef77b417a..f098d2e17 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -9,7 +9,6 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" - proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) @@ -124,17 +123,12 @@ func next(qr dsq.Results) (*cid.Cid, *pb.DataObj, error) { return nil, nil, fmt.Errorf("decoding cid from filestore: %s", err) } - data, ok := v.Value.([]byte) - if !ok { - return c, nil, fmt.Errorf("stored filestore dataobj was not a []byte") - } - - var dobj pb.DataObj - if err := proto.Unmarshal(data, &dobj); err != nil { + dobj, err := unmarshalDataObj(v.Value) + if err != nil { return c, nil, err } - return c, &dobj, nil + return c, dobj, nil } func mkListRes(c *cid.Cid, d *pb.DataObj, err error) *ListRes { From f94b3e51d11a289ef86d4524255797546df28347 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 22 Mar 2017 16:09:34 -0700 Subject: [PATCH 1784/3817] add global config switch for sharding License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@bd3637aab41b70978d4039330f0a761d05f4480a --- unixfs/io/dirbuilder.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 964eac582..4b872f905 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -17,6 +17,10 @@ import ( // result in the node being restructured into a sharded object. var ShardSplitThreshold = 1000 +// UseHAMTSharding is a global flag that signifies whether or not to use the +// HAMT sharding scheme for directory creation +var UseHAMTSharding = false + // DefaultShardWidth is the default value used for hamt sharding width. var DefaultShardWidth = 256 @@ -31,7 +35,15 @@ type Directory struct { func NewDirectory(dserv mdag.DAGService) *Directory { db := new(Directory) db.dserv = dserv - db.dirnode = format.EmptyDirNode() + if UseHAMTSharding { + s, err := hamt.NewHamtShard(dserv, DefaultShardWidth) + if err != nil { + panic(err) // will only panic if DefaultShardWidth is a bad value + } + db.shard = s + } else { + db.dirnode = format.EmptyDirNode() + } return db } @@ -70,7 +82,7 @@ func NewDirectoryFromNode(dserv mdag.DAGService, nd node.Node) (*Directory, erro // AddChild adds a (name, key)-pair to the root node. func (d *Directory) AddChild(ctx context.Context, name string, nd node.Node) error { if d.shard == nil { - if len(d.dirnode.Links()) < ShardSplitThreshold { + if !UseHAMTSharding { _ = d.dirnode.RemoveNodeLink(name) return d.dirnode.AddNodeLinkClean(name, nd) } From 3c2b34f3a4d11234f0633f3794779aa920e64eb9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 22 Mar 2017 16:09:34 -0700 Subject: [PATCH 1785/3817] add global config switch for sharding License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@6554bb81f4af819d25e1a6dd049a45c48475b67c --- mfs/dir.go | 9 +++------ mfs/mfs_test.go | 2 +- mfs/system.go | 9 +++++---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index cab2d9a1f..2f3387b59 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -59,7 +59,7 @@ func NewDirectory(ctx context.Context, name string, node node.Node, parent child // closeChild updates the child by the given name to the dag node 'nd' // and changes its own dag node -func (d *Directory) closeChild(name string, nd *dag.ProtoNode, sync bool) error { +func (d *Directory) closeChild(name string, nd node.Node, sync bool) error { mynd, err := d.closeChildUpdate(name, nd, sync) if err != nil { return err @@ -72,7 +72,7 @@ func (d *Directory) closeChild(name string, nd *dag.ProtoNode, sync bool) error } // closeChildUpdate is the portion of closeChild that needs to be locked around -func (d *Directory) closeChildUpdate(name string, nd *dag.ProtoNode, sync bool) (*dag.ProtoNode, error) { +func (d *Directory) closeChildUpdate(name string, nd node.Node, sync bool) (*dag.ProtoNode, error) { d.lock.Lock() defer d.lock.Unlock() @@ -329,13 +329,10 @@ func (d *Directory) Unlink(name string) error { } func (d *Directory) Flush() error { - d.lock.Lock() - nd, err := d.flushCurrentNode() + nd, err := d.GetNode() if err != nil { - d.lock.Unlock() return err } - d.lock.Unlock() return d.parent.closeChild(d.name, nd, true) } diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 8471fd02c..84df1d758 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -752,7 +752,7 @@ func TestMfsHugeDir(t *testing.T) { defer cancel() _, rt := setupRoot(ctx, t) - for i := 0; i < 100000; i++ { + for i := 0; i < 10000; i++ { err := Mkdir(rt, fmt.Sprintf("/dir%d", i), false, false) if err != nil { t.Fatal(err) diff --git a/mfs/system.go b/mfs/system.go index 05d2a1c53..0bc319240 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -12,6 +12,7 @@ package mfs import ( "context" "errors" + "fmt" "sync" "time" @@ -30,7 +31,7 @@ var log = logging.Logger("mfs") var ErrIsDirectory = errors.New("error: is a directory") type childCloser interface { - closeChild(string, *dag.ProtoNode, bool) error + closeChild(string, node.Node, bool) error } type NodeType int @@ -87,7 +88,7 @@ func NewRoot(parent context.Context, ds dag.DAGService, node *dag.ProtoNode, pf } switch pbn.GetType() { - case ft.TDirectory: + case ft.TDirectory, ft.THAMTShard: rval, err := NewDirectory(parent, node.String(), node, root, ds) if err != nil { return nil, err @@ -101,7 +102,7 @@ func NewRoot(parent context.Context, ds dag.DAGService, node *dag.ProtoNode, pf } root.val = fi default: - panic("unrecognized! (NYI)") + return nil, fmt.Errorf("unrecognized unixfs type: %s", pbn.GetType()) } return root, nil } @@ -124,7 +125,7 @@ func (kr *Root) Flush() error { // closeChild implements the childCloser interface, and signals to the publisher that // there are changes ready to be published -func (kr *Root) closeChild(name string, nd *dag.ProtoNode, sync bool) error { +func (kr *Root) closeChild(name string, nd node.Node, sync bool) error { c, err := kr.dserv.Add(nd) if err != nil { return err From 4f97fbb09d49f1a032cf5bd55ad2a866db4f2b89 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Mar 2017 01:02:50 +0100 Subject: [PATCH 1786/3817] fix: multiple govet warnings License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@8bf6483e4458130432e53448fa7da06d562f48c7 --- unixfs/mod/dagmodifier.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 52e821de9..73852e2fa 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -336,15 +336,18 @@ func (dm *DagModifier) readPrep() error { ctx, cancel := context.WithCancel(dm.ctx) dr, err := uio.NewDagReader(ctx, dm.curNode, dm.dagserv) if err != nil { + cancel() return err } i, err := dr.Seek(int64(dm.curWrOff), os.SEEK_SET) if err != nil { + cancel() return err } if i != int64(dm.curWrOff) { + cancel() return ErrSeekFail } From 85e57d00b2a36aa96c68a23a89a805e3931067a4 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Mar 2017 01:02:50 +0100 Subject: [PATCH 1787/3817] fix: multiple govet warnings License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-filestore@3bed4af6cb5640aca7d18f426795863fb4260199 --- filestore/filestore.go | 1 + 1 file changed, 1 insertion(+) diff --git a/filestore/filestore.go b/filestore/filestore.go index da3dc17c6..29725cd57 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -36,6 +36,7 @@ func (f *Filestore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { a, err := f.bs.AllKeysChan(ctx) if err != nil { + cancel() return nil, err } From b4f3e9ad96510d678151ea23a2dec008c355a4f8 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Mar 2017 01:02:50 +0100 Subject: [PATCH 1788/3817] fix: multiple govet warnings License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-blockstore@696689fb0df53001e6628b8611428698fc737814 --- blockstore/blockstore.go | 4 ++-- blockstore/bloom_cache_test.go | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index e34c3a8ee..3d66c5ae3 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -198,14 +198,14 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) return } if e.Error != nil { - log.Errorf("blockstore.AllKeysChan got err:", e.Error) + log.Errorf("blockstore.AllKeysChan got err: %s", e.Error) return } // need to convert to key.Key using key.KeyFromDsKey. k, err := dshelp.DsKeyToCid(ds.RawKey(e.Key)) if err != nil { - log.Warningf("error parsing key from DsKey: ", err) + log.Warningf("error parsing key from DsKey: %s", err) continue } diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 7941a647c..8682267ea 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -31,7 +31,9 @@ func testBloomCached(bs Blockstore, ctx context.Context) (*bloomcache, error) { func TestPutManyAddsToBloom(t *testing.T) { bs := NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore())) - ctx, _ := context.WithTimeout(context.Background(), 1*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + cachedbs, err := testBloomCached(bs, ctx) select { @@ -75,7 +77,9 @@ func TestHasIsBloomCached(t *testing.T) { for i := 0; i < 1000; i++ { bs.Put(blocks.NewBlock([]byte(fmt.Sprintf("data: %d", i)))) } - ctx, _ := context.WithTimeout(context.Background(), 1*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + cachedbs, err := testBloomCached(bs, ctx) if err != nil { t.Fatal(err) From da34e7df6fddcc2c02d9f8705aa354b42a098ef4 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Mar 2017 01:02:50 +0100 Subject: [PATCH 1789/3817] fix: multiple govet warnings License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-path@c546875ce706e539b114305baee80c7940ddefaf --- path/path_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/path_test.go b/path/path_test.go index a718bd81f..c3bdcd59e 100644 --- a/path/path_test.go +++ b/path/path_test.go @@ -24,7 +24,7 @@ func TestPathParsing(t *testing.T) { _, err := ParsePath(p) valid := (err == nil) if valid != expected { - t.Fatalf("expected %s to have valid == %s", p, expected) + t.Fatalf("expected %s to have valid == %t", p, expected) } } } From cae592c120d0c37a3b9699a54ac38f97614699e4 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Mar 2017 01:02:50 +0100 Subject: [PATCH 1790/3817] fix: multiple govet warnings License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-pinner@0ffe40144021f7bdc9f6f52433f3be24926cdb7c --- pinning/pinner/pin_test.go | 7 +++++-- pinning/pinner/set_test.go | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 009373dfe..656f8f63d 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -341,7 +341,9 @@ func TestPinRecursiveFail(t *testing.T) { } // NOTE: This isnt a time based test, we expect the pin to fail - mctx, _ := context.WithTimeout(ctx, time.Millisecond) + mctx, cancel := context.WithTimeout(ctx, time.Millisecond) + defer cancel() + err = p.Pin(mctx, a, true) if err == nil { t.Fatal("should have failed to pin here") @@ -358,7 +360,8 @@ func TestPinRecursiveFail(t *testing.T) { } // this one is time based... but shouldnt cause any issues - mctx, _ = context.WithTimeout(ctx, time.Second) + mctx, cancel = context.WithTimeout(ctx, time.Second) + defer cancel() err = p.Pin(mctx, a, true) if err != nil { t.Fatal(err) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 57826a998..7dbc61a85 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -95,7 +95,7 @@ func TestSet(t *testing.T) { for _, c := range inputs { if !seen.Has(c) { - t.Fatalf("expected to have %s, didnt find it") + t.Fatalf("expected to have '%s', didnt find it", c) } } } From a81cc5d98dd15bdf327d5a76061e922b22acab66 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 24 Mar 2017 01:02:50 +0100 Subject: [PATCH 1791/3817] fix: multiple govet warnings License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-blockservice@44679931b1a8788797d4c6e899d93816e355bb5a --- blockservice/blockservice_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index a97c315ba..bd3b0c665 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -32,7 +32,7 @@ func TestWriteThroughWorks(t *testing.T) { bserv.AddBlock(block) if bstore.PutCounter != 2 { - t.Fatal("Put should have called again, should be 2 is: %d", bstore.PutCounter) + t.Fatalf("Put should have called again, should be 2 is: %d", bstore.PutCounter) } } From 8bb89cbe90b8348758fc90073511568114f4390d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 23 Mar 2017 17:49:26 -0700 Subject: [PATCH 1792/3817] fix go vet issues in hamt sharding PR License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@5e8347cb4b8262ae7b5e8891f52966c97959413b --- unixfs/hamt/hamt.go | 14 +++++++------- unixfs/hamt/hamt_test.go | 17 ++++++++++------- unixfs/io/dagreader_test.go | 5 ++++- unixfs/io/dirbuilder.go | 8 ++++---- unixfs/io/dirbuilder_test.go | 8 ++++---- 5 files changed, 29 insertions(+), 23 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index c9d6eb9dc..cfe448d9c 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -351,17 +351,17 @@ func (ds *HamtShard) getValue(ctx context.Context, hv *hashBits, key string, cb return os.ErrNotExist } -func (ds *HamtShard) EnumLinks() ([]*node.Link, error) { +func (ds *HamtShard) EnumLinks(ctx context.Context) ([]*node.Link, error) { var links []*node.Link - err := ds.ForEachLink(func(l *node.Link) error { + err := ds.ForEachLink(ctx, func(l *node.Link) error { links = append(links, l) return nil }) return links, err } -func (ds *HamtShard) ForEachLink(f func(*node.Link) error) error { - return ds.walkTrie(func(sv *shardValue) error { +func (ds *HamtShard) ForEachLink(ctx context.Context, f func(*node.Link) error) error { + return ds.walkTrie(ctx, func(sv *shardValue) error { lnk, err := node.MakeLink(sv.val) if err != nil { return err @@ -373,7 +373,7 @@ func (ds *HamtShard) ForEachLink(f func(*node.Link) error) error { }) } -func (ds *HamtShard) walkTrie(cb func(*shardValue) error) error { +func (ds *HamtShard) walkTrie(ctx context.Context, cb func(*shardValue) error) error { for i := 0; i < ds.tableSize; i++ { if ds.bitfield.Bit(i) == 0 { continue @@ -382,7 +382,7 @@ func (ds *HamtShard) walkTrie(cb func(*shardValue) error) error { idx := ds.indexForBitPos(i) // NOTE: an optimized version could simply iterate over each // element in the 'children' array. - c, err := ds.getChild(context.TODO(), idx) + c, err := ds.getChild(ctx, idx) if err != nil { return err } @@ -395,7 +395,7 @@ func (ds *HamtShard) walkTrie(cb func(*shardValue) error) error { } case *HamtShard: - err := c.walkTrie(cb) + err := c.walkTrie(ctx, cb) if err != nil { return err } diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 25b7125c4..9f834a5ae 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -72,6 +72,8 @@ func assertLink(s *HamtShard, name string, found bool) error { } func assertSerializationWorks(ds dag.DAGService, s *HamtShard) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() nd, err := s.Node() if err != nil { return err @@ -82,12 +84,12 @@ func assertSerializationWorks(ds dag.DAGService, s *HamtShard) error { return err } - linksA, err := s.EnumLinks() + linksA, err := s.EnumLinks(ctx) if err != nil { return err } - linksB, err := nds.EnumLinks() + linksB, err := nds.EnumLinks(ctx) if err != nil { return err } @@ -160,7 +162,8 @@ func TestDirBuilding(t *testing.T) { func TestShardReload(t *testing.T) { ds := mdtest.Mock() s, _ := NewHamtShard(ds, 256) - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() _, s, err := makeDir(ds, 200) if err != nil { @@ -177,7 +180,7 @@ func TestShardReload(t *testing.T) { t.Fatal(err) } - lnks, err := nds.EnumLinks() + lnks, err := nds.EnumLinks(ctx) if err != nil { t.Fatal(err) } @@ -270,7 +273,7 @@ func TestSetAfterMarshal(t *testing.T) { } } - links, err := nds.EnumLinks() + links, err := nds.EnumLinks(ctx) if err != nil { t.Fatal(err) } @@ -301,7 +304,7 @@ func TestDuplicateAddShard(t *testing.T) { t.Fatal(err) } - lnks, err := dir.EnumLinks() + lnks, err := dir.EnumLinks(ctx) if err != nil { t.Fatal(err) } @@ -393,7 +396,7 @@ func TestRemoveElemsAfterMarshal(t *testing.T) { } } - links, err := nds.EnumLinks() + links, err := nds.EnumLinks(ctx) if err != nil { t.Fatal(err) } diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index 27d7f3b09..b57426e38 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -169,7 +169,10 @@ func TestMetadataNode(t *testing.T) { ctx, closer := context.WithCancel(context.Background()) defer closer() - data, err := unixfs.BytesForMetadata(&unixfs.Metadata{"text", 125}) + data, err := unixfs.BytesForMetadata(&unixfs.Metadata{ + MimeType: "text", + Size: 125, + }) if err != nil { t.Fatal(err) } diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 4b872f905..45059f78e 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -119,7 +119,7 @@ func (d *Directory) switchToSharding(ctx context.Context) error { return nil } -func (d *Directory) ForEachLink(f func(*node.Link) error) error { +func (d *Directory) ForEachLink(ctx context.Context, f func(*node.Link) error) error { if d.shard == nil { for _, l := range d.dirnode.Links() { if err := f(l); err != nil { @@ -129,15 +129,15 @@ func (d *Directory) ForEachLink(f func(*node.Link) error) error { return nil } - return d.shard.ForEachLink(f) + return d.shard.ForEachLink(ctx, f) } -func (d *Directory) Links() ([]*node.Link, error) { +func (d *Directory) Links(ctx context.Context) ([]*node.Link, error) { if d.shard == nil { return d.dirnode.Links(), nil } - return d.shard.EnumLinks() + return d.shard.EnumLinks(ctx) } func (d *Directory) Find(ctx context.Context, name string) (node.Node, error) { diff --git a/unixfs/io/dirbuilder_test.go b/unixfs/io/dirbuilder_test.go index f07ee8894..6118c4112 100644 --- a/unixfs/io/dirbuilder_test.go +++ b/unixfs/io/dirbuilder_test.go @@ -38,7 +38,7 @@ func TestDirectoryGrowth(t *testing.T) { t.Fatal(err) } - links, err := dir.Links() + links, err := dir.Links(ctx) if err != nil { t.Fatal(err) } @@ -86,7 +86,7 @@ func TestDuplicateAddDir(t *testing.T) { t.Fatal(err) } - lnks, err := dir.Links() + lnks, err := dir.Links(ctx) if err != nil { t.Fatal(err) } @@ -121,7 +121,7 @@ func TestDirBuilder(t *testing.T) { t.Fatal(err) } - links, err := dir.Links() + links, err := dir.Links(ctx) if err != nil { t.Fatal(err) } @@ -135,7 +135,7 @@ func TestDirBuilder(t *testing.T) { t.Fatal(err) } - links, err = adir.Links() + links, err = adir.Links(ctx) if err != nil { t.Fatal(err) } From 631a403da8df009b68ee5020e4f84bc8cbe69e72 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 23 Mar 2017 17:49:26 -0700 Subject: [PATCH 1793/3817] fix go vet issues in hamt sharding PR License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@35c2051cd2534363187c5625c02bb31d98abfa81 --- mfs/dir.go | 10 +++++----- mfs/mfs_test.go | 16 ++++++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 2f3387b59..63ae8d408 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -217,12 +217,12 @@ type NodeListing struct { Hash string } -func (d *Directory) ListNames() ([]string, error) { +func (d *Directory) ListNames(ctx context.Context) ([]string, error) { d.lock.Lock() defer d.lock.Unlock() var out []string - err := d.dirbuilder.ForEachLink(func(l *node.Link) error { + err := d.dirbuilder.ForEachLink(ctx, func(l *node.Link) error { out = append(out, l.Name) return nil }) @@ -235,9 +235,9 @@ func (d *Directory) ListNames() ([]string, error) { return out, nil } -func (d *Directory) List() ([]NodeListing, error) { +func (d *Directory) List(ctx context.Context) ([]NodeListing, error) { var out []NodeListing - err := d.ForEachEntry(context.TODO(), func(nl NodeListing) error { + err := d.ForEachEntry(ctx, func(nl NodeListing) error { out = append(out, nl) return nil }) @@ -247,7 +247,7 @@ func (d *Directory) List() ([]NodeListing, error) { func (d *Directory) ForEachEntry(ctx context.Context, f func(NodeListing) error) error { d.lock.Lock() defer d.lock.Unlock() - return d.dirbuilder.ForEachLink(func(l *node.Link) error { + return d.dirbuilder.ForEachLink(ctx, func(l *node.Link) error { c, err := d.childUnsync(l.Name) if err != nil { return err diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 84df1d758..e3c2f3e19 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -82,6 +82,9 @@ func mkdirP(t *testing.T, root *Directory, pth string) *Directory { } func assertDirAtPath(root *Directory, pth string, children []string) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + fsn, err := DirLookup(root, pth) if err != nil { return err @@ -92,7 +95,7 @@ func assertDirAtPath(root *Directory, pth string, children []string) error { return fmt.Errorf("%s was not a directory", pth) } - listing, err := dir.List() + listing, err := dir.List(ctx) if err != nil { return err } @@ -496,7 +499,7 @@ func TestMfsFile(t *testing.T) { func randomWalk(d *Directory, n int) (*Directory, error) { for i := 0; i < n; i++ { - dirents, err := d.List() + dirents, err := d.List(context.Background()) if err != nil { return nil, err } @@ -585,7 +588,7 @@ func actorRemoveFile(d *Directory) error { return err } - ents, err := d.List() + ents, err := d.List(context.Background()) if err != nil { return err } @@ -605,7 +608,7 @@ func randomFile(d *Directory) (*File, error) { return nil, err } - ents, err := d.List() + ents, err := d.List(context.Background()) if err != nil { return nil, err } @@ -953,6 +956,7 @@ func TestConcurrentReads(t *testing.T) { } wg.Wait() } + func writeFile(rt *Root, path string, data []byte) error { n, err := Lookup(rt, path) if err != nil { @@ -975,8 +979,8 @@ func writeFile(rt *Root, path string, data []byte) error { return err } - if nw != 10 { - fmt.Errorf("wrote incorrect amount") + if nw != len(data) { + return fmt.Errorf("wrote incorrect amount: %d != 10", nw) } return nil From 1e32292e7b52eb27306584c2157aaf25fe594c3b Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 24 Mar 2017 16:36:46 +0100 Subject: [PATCH 1794/3817] Make Golint happy in the blocks submodule. This has required changing the order of some parameters and adding HashOnRead to the Blockstore interface (which I have in turn added to all the wrapper implementations). License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-filestore@9ed31e98c5905a34016c0f96013fe2227a7c22a5 --- filestore/filestore.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/filestore/filestore.go b/filestore/filestore.go index 29725cd57..801f78d1c 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -199,4 +199,9 @@ func (f *Filestore) PutMany(bs []blocks.Block) error { return nil } +// HashOnRead calls blockstore.HashOnRead. +func (f *Filestore) HashOnRead(enabled bool) { + f.bs.HashOnRead(enabled) +} + var _ blockstore.Blockstore = (*Filestore)(nil) From 3fdb77b4824fa697ff4c004fd09c1e0e68dce76f Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 24 Mar 2017 16:36:46 +0100 Subject: [PATCH 1795/3817] Make Golint happy in the blocks submodule. This has required changing the order of some parameters and adding HashOnRead to the Blockstore interface (which I have in turn added to all the wrapper implementations). License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-blockstore@94eff5ef801c07c0a9a530bd90d9067ff4f8d19b --- blockstore/arc_cache.go | 7 +++++ blockstore/arc_cache_test.go | 11 ++++---- blockstore/blockstore.go | 47 +++++++++++++++++++++++++--------- blockstore/blockstore_test.go | 4 +-- blockstore/bloom_cache.go | 11 +++++--- blockstore/bloom_cache_test.go | 15 +++++------ blockstore/caching.go | 12 ++++++--- blockstore/util/remove.go | 21 ++++++++++++--- 8 files changed, 91 insertions(+), 37 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 989f36e11..d14600f01 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -11,6 +11,9 @@ import ( lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" ) +// arccache wraps a BlockStore with an Adaptive Replacement Cache (ARC) for +// block Cids. This provides block access-time improvements, allowing +// to short-cut many searches without query-ing the underlying datastore. type arccache struct { arc *lru.ARCCache blockstore Blockstore @@ -128,6 +131,10 @@ func (b *arccache) PutMany(bs []blocks.Block) error { return nil } +func (b *arccache) HashOnRead(enabled bool) { + b.blockstore.HashOnRead(enabled) +} + func (b *arccache) addCache(c *cid.Cid, has bool) { b.arc.Add(c.KeyString(), has) } diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 987185e80..f143a1a43 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -13,25 +13,24 @@ import ( var exampleBlock = blocks.NewBlock([]byte("foo")) -func testArcCached(bs Blockstore, ctx context.Context) (*arccache, error) { +func testArcCached(ctx context.Context, bs Blockstore) (*arccache, error) { if ctx == nil { ctx = context.TODO() } opts := DefaultCacheOpts() opts.HasBloomFilterSize = 0 opts.HasBloomFilterHashes = 0 - bbs, err := CachedBlockstore(bs, ctx, opts) + bbs, err := CachedBlockstore(ctx, bs, opts) if err == nil { return bbs.(*arccache), nil - } else { - return nil, err } + return nil, err } -func createStores(t *testing.T) (*arccache, *blockstore, *callbackDatastore) { +func createStores(t *testing.T) (*arccache, Blockstore, *callbackDatastore) { cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} bs := NewBlockstore(syncds.MutexWrap(cd)) - arc, err := testArcCached(bs, nil) + arc, err := testArcCached(nil, bs) if err != nil { t.Fatal(err) } diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 3d66c5ae3..092a9cced 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -1,4 +1,4 @@ -// package blockstore implements a thin wrapper over a datastore, giving a +// Package blockstore implements a thin wrapper over a datastore, giving a // clean interface for Getting and Putting block objects. package blockstore @@ -23,22 +23,36 @@ var log = logging.Logger("blockstore") // BlockPrefix namespaces blockstore datastores var BlockPrefix = ds.NewKey("blocks") -var ValueTypeMismatch = errors.New("the retrieved value is not a Block") +// ErrValueTypeMismatch is an error returned when the item retrieved from +// the datatstore is not a block. +var ErrValueTypeMismatch = errors.New("the retrieved value is not a Block") + +// ErrHashMismatch is an error returned when the hash of a block +// is different than expected. var ErrHashMismatch = errors.New("block in storage has different hash than requested") +// ErrNotFound is an error returned when a block is not found. var ErrNotFound = errors.New("blockstore: block not found") -// Blockstore wraps a Datastore +// Blockstore wraps a Datastore block-centered methods and provides a layer +// of abstraction which allows to add different caching strategies. type Blockstore interface { DeleteBlock(*cid.Cid) error Has(*cid.Cid) (bool, error) Get(*cid.Cid) (blocks.Block, error) Put(blocks.Block) error PutMany([]blocks.Block) error - + // AllKeysChan returns a channel from which + // the CIDs in the Blockstore can be read. It should respect + // the given context, closing the channel if it becomes Done. AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) + // HashOnRead specifies if every read block should be + // rehashed to make sure it matches its CID. + HashOnRead(enabled bool) } +// GCLocker abstract functionality to lock a blockstore when performing +// garbage-collection operations. type GCLocker interface { // GCLock locks the blockstore for garbage collection. No operations // that expect to finish with a pin should ocurr simultaneously. @@ -56,11 +70,15 @@ type GCLocker interface { GCRequested() bool } +// GCBlockstore is a blockstore that can safely run garbage-collection +// operations. type GCBlockstore interface { Blockstore GCLocker } +// NewGCBlockstore returns a default implementation of GCBlockstore +// using the given Blockstore and GCLocker. func NewGCBlockstore(bs Blockstore, gcl GCLocker) GCBlockstore { return gcBlockstore{bs, gcl} } @@ -70,7 +88,9 @@ type gcBlockstore struct { GCLocker } -func NewBlockstore(d ds.Batching) *blockstore { +// NewBlockstore returns a default Blockstore implementation +// using the provided datastore.Batching backend. +func NewBlockstore(d ds.Batching) Blockstore { var dsb ds.Batching dd := dsns.Wrap(d, BlockPrefix) dsb = dd @@ -108,7 +128,7 @@ func (bs *blockstore) Get(k *cid.Cid) (blocks.Block, error) { } bdata, ok := maybeData.([]byte) if !ok { - return nil, ValueTypeMismatch + return nil, ErrValueTypeMismatch } if bs.rehash { @@ -122,9 +142,8 @@ func (bs *blockstore) Get(k *cid.Cid) (blocks.Block, error) { } return blocks.NewBlockWithCid(bdata, rbcid) - } else { - return blocks.NewBlockWithCid(bdata, k) } + return blocks.NewBlockWithCid(bdata, k) } func (bs *blockstore) Put(block blocks.Block) error { @@ -162,8 +181,8 @@ func (bs *blockstore) Has(k *cid.Cid) (bool, error) { return bs.datastore.Has(dshelp.CidToDsKey(k)) } -func (s *blockstore) DeleteBlock(k *cid.Cid) error { - err := s.datastore.Delete(dshelp.CidToDsKey(k)) +func (bs *blockstore) DeleteBlock(k *cid.Cid) error { + err := bs.datastore.Delete(dshelp.CidToDsKey(k)) if err == ds.ErrNotFound { return ErrNotFound } @@ -173,7 +192,7 @@ func (s *blockstore) DeleteBlock(k *cid.Cid) error { // AllKeysChan runs a query for keys from the blockstore. // this is very simplistic, in the future, take dsq.Query as a param? // -// AllKeysChan respects context +// AllKeysChan respects context. func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { // KeysOnly, because that would be _a lot_ of data. @@ -220,7 +239,9 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) return output, nil } -func NewGCLocker() *gclocker { +// NewGCLocker returns a default implementation of +// GCLocker using standard [RW] mutexes. +func NewGCLocker() GCLocker { return &gclocker{} } @@ -230,6 +251,8 @@ type gclocker struct { gcreqlk sync.Mutex } +// Unlocker represents an object which can Unlock +// something. type Unlocker interface { Unlock() } diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 6e5216609..781a1eec1 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -186,7 +186,7 @@ func TestAllKeysRespectsContext(t *testing.T) { } -func TestValueTypeMismatch(t *testing.T) { +func TestErrValueTypeMismatch(t *testing.T) { block := blocks.NewBlock([]byte("some data")) datastore := ds.NewMapDatastore() @@ -196,7 +196,7 @@ func TestValueTypeMismatch(t *testing.T) { blockstore := NewBlockstore(ds_sync.MutexWrap(datastore)) _, err := blockstore.Get(block.Cid()) - if err != ValueTypeMismatch { + if err != ErrValueTypeMismatch { t.Fatal(err) } } diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 5c8c76ad5..cc6526bc8 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -12,9 +12,10 @@ import ( bloom "gx/ipfs/QmeiMCBkYHxkDkDfnDadzz4YxY5ruL5Pj499essE4vRsGM/bbloom" ) -// bloomCached returns Blockstore that caches Has requests using Bloom filter -// Size is size of bloom filter in bytes -func bloomCached(bs Blockstore, ctx context.Context, bloomSize, hashCount int) (*bloomcache, error) { +// bloomCached returns a Blockstore that caches Has requests using a Bloom +// filter. bloomSize is size of bloom filter in bytes. hashCount specifies the +// number of hashing functions in the bloom filter (usually known as k). +func bloomCached(ctx context.Context, bs Blockstore, bloomSize, hashCount int) (*bloomcache, error) { bl, err := bloom.New(float64(bloomSize), float64(hashCount)) if err != nil { return nil, err @@ -165,6 +166,10 @@ func (b *bloomcache) PutMany(bs []blocks.Block) error { return nil } +func (b *bloomcache) HashOnRead(enabled bool) { + b.blockstore.HashOnRead(enabled) +} + func (b *bloomcache) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { return b.blockstore.AllKeysChan(ctx) } diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 8682267ea..f021efd8e 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -14,18 +14,17 @@ import ( syncds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" ) -func testBloomCached(bs Blockstore, ctx context.Context) (*bloomcache, error) { +func testBloomCached(ctx context.Context, bs Blockstore) (*bloomcache, error) { if ctx == nil { - ctx = context.TODO() + ctx = context.Background() } opts := DefaultCacheOpts() opts.HasARCCacheSize = 0 - bbs, err := CachedBlockstore(bs, ctx, opts) + bbs, err := CachedBlockstore(ctx, bs, opts) if err == nil { return bbs.(*bloomcache), nil - } else { - return nil, err } + return nil, err } func TestPutManyAddsToBloom(t *testing.T) { @@ -34,7 +33,7 @@ func TestPutManyAddsToBloom(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() - cachedbs, err := testBloomCached(bs, ctx) + cachedbs, err := testBloomCached(ctx, bs) select { case <-cachedbs.rebuildChan: @@ -65,7 +64,7 @@ func TestPutManyAddsToBloom(t *testing.T) { func TestReturnsErrorWhenSizeNegative(t *testing.T) { bs := NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore())) - _, err := bloomCached(bs, context.TODO(), -1, 1) + _, err := bloomCached(context.Background(), bs, -1, 1) if err == nil { t.Fail() } @@ -80,7 +79,7 @@ func TestHasIsBloomCached(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() - cachedbs, err := testBloomCached(bs, ctx) + cachedbs, err := testBloomCached(ctx, bs) if err != nil { t.Fatal(err) } diff --git a/blockstore/caching.go b/blockstore/caching.go index d19f47822..0ea375b06 100644 --- a/blockstore/caching.go +++ b/blockstore/caching.go @@ -7,6 +7,7 @@ import ( "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" ) +// CacheOpts wraps options for CachedBlockStore(). // Next to each option is it aproximate memory usage per unit type CacheOpts struct { HasBloomFilterSize int // 1 byte @@ -14,6 +15,7 @@ type CacheOpts struct { HasARCCacheSize int // 32 bytes } +// DefaultCacheOpts returns a CacheOpts initialized with default values. func DefaultCacheOpts() CacheOpts { return CacheOpts{ HasBloomFilterSize: 512 << 10, @@ -22,8 +24,12 @@ func DefaultCacheOpts() CacheOpts { } } -func CachedBlockstore(bs Blockstore, - ctx context.Context, opts CacheOpts) (cbs Blockstore, err error) { +// CachedBlockstore returns a blockstore wrapped in an ARCCache and +// then in a bloom filter cache, if the options indicate it. +func CachedBlockstore( + ctx context.Context, + bs Blockstore, + opts CacheOpts) (cbs Blockstore, err error) { cbs = bs if opts.HasBloomFilterSize < 0 || opts.HasBloomFilterHashes < 0 || @@ -42,7 +48,7 @@ func CachedBlockstore(bs Blockstore, } if opts.HasBloomFilterSize != 0 { // *8 because of bytes to bits conversion - cbs, err = bloomCached(cbs, ctx, opts.HasBloomFilterSize*8, opts.HasBloomFilterHashes) + cbs, err = bloomCached(ctx, cbs, opts.HasBloomFilterSize*8, opts.HasBloomFilterHashes) } return cbs, err diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 60cb1aee8..2523b3ac2 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -1,13 +1,15 @@ -package blockstore_util +// Package blockstoreutil provides utility functions for Blockstores. +package blockstoreutil import ( "fmt" "io" - bs "github.com/ipfs/go-ipfs/blocks/blockstore" - "github.com/ipfs/go-ipfs/pin" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + + bs "github.com/ipfs/go-ipfs/blocks/blockstore" + "github.com/ipfs/go-ipfs/pin" ) // RemovedBlock is used to respresent the result of removing a block. @@ -21,12 +23,17 @@ type RemovedBlock struct { Error string `json:",omitempty"` } +// RmBlocksOpts is used to wrap options for RmBlocks(). type RmBlocksOpts struct { Prefix string Quiet bool Force bool } +// RmBlocks removes the blocks provided in the cids slice. +// It returns a channel where objects of type RemovedBlock are placed, when +// not using the Quiet option. Block removal is asynchronous and will +// skip any pinned blocks. func RmBlocks(blocks bs.GCBlockstore, pins pin.Pinner, cids []*cid.Cid, opts RmBlocksOpts) (<-chan interface{}, error) { // make the channel large enough to hold any result to avoid // blocking while holding the GCLock @@ -53,6 +60,11 @@ func RmBlocks(blocks bs.GCBlockstore, pins pin.Pinner, cids []*cid.Cid, opts RmB return out, nil } +// FilterPinned takes a slice of Cids and returns it with the pinned Cids +// removed. If a Cid is pinned, it will place RemovedBlock objects in the given +// out channel, with an error which indicates that the Cid is pinned. +// This function is used in RmBlocks to filter out any blocks which are not +// to be removed (because they are pinned). func FilterPinned(pins pin.Pinner, out chan<- interface{}, cids []*cid.Cid) []*cid.Cid { stillOkay := make([]*cid.Cid, 0, len(cids)) res, err := pins.CheckIfPinned(cids...) @@ -73,6 +85,9 @@ func FilterPinned(pins pin.Pinner, out chan<- interface{}, cids []*cid.Cid) []*c return stillOkay } +// ProcRmOutput takes the channel returned by RmBlocks and writes +// to stdout/stderr according to the RemovedBlock objects received in +// that channel. func ProcRmOutput(in <-chan interface{}, sout io.Writer, serr io.Writer) error { someFailed := false for res := range in { From 88bb63cffc0e5f03aefa1288fdf2b1ce5b7e47e8 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 24 Mar 2017 20:36:00 +0100 Subject: [PATCH 1796/3817] Filestore: make golint happy Comments for exported functions and little else. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-filestore@f6b153a1eb94f6bc4bdd265918bfe612abf1b08d --- filestore/filestore.go | 29 ++++++++++++++++++++++++++ filestore/fsrefstore.go | 29 ++++++++++++++++++++++++++ filestore/util.go | 45 ++++++++++++++++++++++++++++++++--------- 3 files changed, 94 insertions(+), 9 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 801f78d1c..8c5822705 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -1,3 +1,10 @@ +// Package filestore implements a Blockstore which is able to read certain +// blocks of data directly from its original location in the filesystem. +// +// In a Filestore, object leaves are stored as FilestoreNodes. FilestoreNodes +// include a filesystem path and an offset, allowing a Blockstore dealing with +// such blocks to avoid storing the whole contents and reading them from their +// filesystem location instead. package filestore import ( @@ -14,23 +21,31 @@ import ( var log = logging.Logger("filestore") +// Filestore implements a Blockstore by combining a standard Blockstore +// to store regular blocks and a special Blockstore called +// FileManager to store blocks which data exists in an external file. type Filestore struct { fm *FileManager bs blockstore.Blockstore } +// FileManager returns the FileManager in Filestore. func (f *Filestore) FileManager() *FileManager { return f.fm } +// MainBlockstore returns the standard Blockstore in the Filestore. func (f *Filestore) MainBlockstore() blockstore.Blockstore { return f.bs } +// NewFilestore creates one using the given Blockstore and FileManager. func NewFilestore(bs blockstore.Blockstore, fm *FileManager) *Filestore { return &Filestore{fm, bs} } +// AllKeysChan returns a channel from which to read the keys stored in +// the blockstore. If the given context is cancelled the channel will be closed. func (f *Filestore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { ctx, cancel := context.WithCancel(ctx) @@ -93,6 +108,10 @@ func (f *Filestore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { return out, nil } +// DeleteBlock deletes the block with the given key from the +// blockstore. As expected, in the case of FileManager blocks, only the +// reference is deleted, not its contents. It may return +// ErrNotFound when the block is not stored. func (f *Filestore) DeleteBlock(c *cid.Cid) error { err1 := f.bs.DeleteBlock(c) if err1 != nil && err1 != blockstore.ErrNotFound { @@ -116,6 +135,8 @@ func (f *Filestore) DeleteBlock(c *cid.Cid) error { } } +// Get retrieves the block with the given Cid. It may return +// ErrNotFound when the block is not stored. func (f *Filestore) Get(c *cid.Cid) (blocks.Block, error) { blk, err := f.bs.Get(c) switch err { @@ -130,6 +151,8 @@ func (f *Filestore) Get(c *cid.Cid) (blocks.Block, error) { return f.fm.Get(c) } +// Has returns true if the block with the given Cid is +// stored in the Filestore. func (f *Filestore) Has(c *cid.Cid) (bool, error) { has, err := f.bs.Has(c) if err != nil { @@ -143,6 +166,10 @@ func (f *Filestore) Has(c *cid.Cid) (bool, error) { return f.fm.Has(c) } +// Put stores a block in the Filestore. For blocks of +// underlying type FilestoreNode, the operation is +// delegated to the FileManager, while the rest of blocks +// are handled by the regular blockstore. func (f *Filestore) Put(b blocks.Block) error { has, err := f.Has(b.Cid()) if err != nil { @@ -161,6 +188,8 @@ func (f *Filestore) Put(b blocks.Block) error { } } +// PutMany is like Put(), but takes a slice of blocks, allowing +// the underlying blockstore to perform batch transactions. func (f *Filestore) PutMany(bs []blocks.Block) error { var normals []blocks.Block var fstores []*posinfo.FilestoreNode diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 3e3750cb9..7b41555b9 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -20,26 +20,43 @@ import ( cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) +// FilestorePrefix identifies the key prefix for FileManager blocks. var FilestorePrefix = ds.NewKey("filestore") +// FileManager is a blockstore implementation which stores special +// blocks FilestoreNode type. These nodes only contain a reference +// to the actual location of the block data in the filesystem +// (a path and an offset). type FileManager struct { ds ds.Batching root string } +// CorruptReferenceError implements the error interface. +// It is used to indicate that the block contents pointed +// by the referencing blocks cannot be retrieved (i.e. the +// file is not found, or the data changed as it was being read). type CorruptReferenceError struct { Code Status Err error } +// Error() returns the error message in the CorruptReferenceError +// as a string. func (c CorruptReferenceError) Error() string { return c.Err.Error() } +// NewFileManager initializes a new file manager with the given +// datastore and root. All FilestoreNodes paths are relative to the +// root path given here, which is prepended for any operations. func NewFileManager(ds ds.Batching, root string) *FileManager { return &FileManager{dsns.Wrap(ds, FilestorePrefix), root} } +// AllKeysChan returns a channel from which to read the keys stored in +// the FileManager. If the given context is cancelled the channel will be +// closed. func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { q := dsq.Query{KeysOnly: true} q.Prefix = FilestorePrefix.String() @@ -76,6 +93,8 @@ func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) return out, nil } +// DeleteBlock deletes the reference-block from the underlying +// datastore. It does not touch the referenced data. func (f *FileManager) DeleteBlock(c *cid.Cid) error { err := f.ds.Delete(dshelp.CidToDsKey(c)) if err == ds.ErrNotFound { @@ -84,6 +103,10 @@ func (f *FileManager) DeleteBlock(c *cid.Cid) error { return err } +// Get reads a block from the datastore. Reading a block +// is done in two steps: the first step retrieves the reference +// block from the datastore. The second step uses the stored +// path and offsets to read the raw block data directly from disk. func (f *FileManager) Get(c *cid.Cid) (blocks.Block, error) { dobj, err := f.getDataObj(c) if err != nil { @@ -165,6 +188,8 @@ func (f *FileManager) readDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { return outbuf, nil } +// Has returns if the FileManager is storing a block reference. It does not +// validate the data, nor checks if the reference is valid. func (f *FileManager) Has(c *cid.Cid) (bool, error) { // NOTE: interesting thing to consider. Has doesnt validate the data. // So the data on disk could be invalid, and we could think we have it. @@ -176,6 +201,8 @@ type putter interface { Put(ds.Key, interface{}) error } +// Put adds a new reference block to the FileManager. It does not check +// that the reference is valid. func (f *FileManager) Put(b *posinfo.FilestoreNode) error { return f.putTo(b, f.ds) } @@ -204,6 +231,8 @@ func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { return to.Put(dshelp.CidToDsKey(b.Cid()), data) } +// PutMany is like Put() but takes a slice of blocks instead, +// allowing it to create a batch transaction. func (f *FileManager) PutMany(bs []*posinfo.FilestoreNode) error { batch, err := f.ds.Batch() if err != nil { diff --git a/filestore/util.go b/filestore/util.go index f098d2e17..0d764cfb7 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -12,8 +12,11 @@ import ( cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" ) +// Status is used to identify the state of the block data referenced +// by a FilestoreNode. Among other places, it is used by CorruptReferenceError. type Status int32 +// These are the supported Status codes. const ( StatusOk Status = 0 StatusFileError Status = 10 // Backing File Error @@ -23,6 +26,7 @@ const ( StatusKeyNotFound Status = 30 ) +// String provides a human-readable representation for Status codes. func (s Status) String() string { switch s { case StatusOk: @@ -42,10 +46,16 @@ func (s Status) String() string { } } +// Format returns the status formatted as a string +// with leading 0s. func (s Status) Format() string { return fmt.Sprintf("%-7s", s.String()) } +// ListRes wraps the response of the List*() functions, which +// allows to obtain and verify blocks stored by the FileManager +// of a Filestore. It includes information about the referenced +// block. type ListRes struct { Status Status ErrorMsg string @@ -55,6 +65,7 @@ type ListRes struct { Size uint64 } +// FormatLong returns a human readable string for a ListRes object. func (r *ListRes) FormatLong() string { switch { case r.Key == nil: @@ -66,18 +77,34 @@ func (r *ListRes) FormatLong() string { } } +// List fetches the block with the given key from the Filemanager +// of the given Filestore and returns a ListRes object with the information. +// List does not verify that the reference is valid or whether the +// raw data is accesible. See Verify(). func List(fs *Filestore, key *cid.Cid) *ListRes { return list(fs, false, key) } +// ListAll returns a function as an iterator which, once invoked, returns +// one by one each block in the Filestore's FileManager. +// ListAll does not verify that the references are valid or whether +// the raw data is accessible. See VerifyAll(). func ListAll(fs *Filestore) (func() *ListRes, error) { return listAll(fs, false) } +// Verify fetches the block with the given key from the Filemanager +// of the given Filestore and returns a ListRes object with the information. +// Verify makes sure that the reference is valid and the block data can be +// read. func Verify(fs *Filestore, key *cid.Cid) *ListRes { return list(fs, true, key) } +// VerifyAll returns a function as an iterator which, once invoked, +// returns one by one each block in the Filestore's FileManager. +// VerifyAll checks that the reference is valid and that the block data +// can be read. func VerifyAll(fs *Filestore) (func() *ListRes, error) { return listAll(fs, true) } @@ -150,14 +177,14 @@ func mkListRes(c *cid.Cid, d *pb.DataObj, err error) *ListRes { ErrorMsg: errorMsg, Key: c, } - } else { - return &ListRes{ - Status: status, - ErrorMsg: errorMsg, - Key: c, - FilePath: *d.FilePath, - Size: *d.Size_, - Offset: *d.Offset, - } + } + + return &ListRes{ + Status: status, + ErrorMsg: errorMsg, + Key: c, + FilePath: *d.FilePath, + Size: *d.Size_, + Offset: *d.Offset, } } From a64e494e79313353256e878ccfe3fe9719a58557 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1797/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@edd462b2035c2770a7da0a2532713f16624fd1d4 --- namesys/interface.go | 2 +- namesys/ipns_select_test.go | 4 ++-- namesys/namesys.go | 6 +++--- namesys/namesys_test.go | 2 +- namesys/publisher.go | 12 ++++++------ namesys/republisher/repub.go | 8 ++++---- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 10 +++++----- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index abbc3c676..f103fc045 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,7 +35,7 @@ import ( context "context" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" + ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ) const ( diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index 9bc856dd4..497548d71 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -10,8 +10,8 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" - u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" + u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" ) func shuffle(a []*pb.IpnsEntry) { diff --git a/namesys/namesys.go b/namesys/namesys.go index dbc9bfdf6..72055331c 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,10 +7,10 @@ import ( path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" + ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" - peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" + routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" + peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 030dd8bfc..7df4ac926 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,7 +10,7 @@ import ( offroute "github.com/ipfs/go-ipfs/routing/offline" "github.com/ipfs/go-ipfs/unixfs" - ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" + ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index d80561237..fe4a03b08 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -14,14 +14,14 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ft "github.com/ipfs/go-ipfs/unixfs" - ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" + ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" - peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" + record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" + dhtpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" + u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" - record "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record" - dhtpb "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record/pb" + routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" + peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 622066e70..fddbc2ea4 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,15 +11,15 @@ import ( path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" - peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" + recpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - recpb "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record/pb" - pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" + routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" + peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 675a43675..10c7c55a7 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" - mocknet "gx/ipfs/QmeWJwi61vii5g8zQUB9UGegfUbmhTKHgeDFP9XuSp5jZ4/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" + mocknet "gx/ipfs/QmRai5yZNL67pWCoznW7sBdFnqZrFULuJ5w8KhmRyhdgN4/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 462168f56..c7e13e853 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -12,7 +12,7 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" + peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index 11236bbcb..88983cedb 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -9,14 +9,14 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" + ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + mh "gx/ipfs/QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw/go-multihash" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" + u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" - mh "gx/ipfs/QmbZ6Cee2uHjG7hf19qLHppgKDRtaG4CVtMzdmK9VCVqLu/go-multihash" + routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" ) var log = logging.Logger("namesys") From 9d74387c3c19147f80a5c4784f750546bd08c26b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1798/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@c053e4d7201d855e1f1572c0da62ff95199d2559 --- ipld/merkledag/coding.go | 4 ++-- ipld/merkledag/merkledag.go | 6 +++--- ipld/merkledag/merkledag_test.go | 6 +++--- ipld/merkledag/node.go | 6 +++--- ipld/merkledag/node_test.go | 2 +- ipld/merkledag/raw.go | 6 +++--- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/traverse/traverse_test.go | 2 +- ipld/merkledag/utils/diff.go | 2 +- ipld/merkledag/utils/utils.go | 2 +- ipld/merkledag/utils/utils_test.go | 2 +- 11 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 74c2319dc..25f43e0d6 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -6,8 +6,8 @@ import ( pb "github.com/ipfs/go-ipfs/merkledag/pb" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 606a90957..e0dc5c74a 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -11,10 +11,10 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" + ipldcbor "gx/ipfs/QmNrbCt8j9DT5W9Pmjy2SdudT9k8GpaDr4sRuFix3BXhgR/go-ipld-cbor" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" - ipldcbor "gx/ipfs/QmdaC21UyoyN3t9QdapHZfsaUo3mqVf5p4CEuFaYVFqwap/go-ipld-cbor" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) var log = logging.Logger("merkledag") diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index d5de2fe9d..da43bdb67 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -24,9 +24,9 @@ import ( dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" - u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) func TestNode(t *testing.T) { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 4204f5276..0b11b928e 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -5,9 +5,9 @@ import ( "encoding/json" "fmt" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" - mh "gx/ipfs/QmbZ6Cee2uHjG7hf19qLHppgKDRtaG4CVtMzdmK9VCVqLu/go-multihash" + mh "gx/ipfs/QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw/go-multihash" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index 3465b8299..c630a8a06 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -8,7 +8,7 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) func TestRemoveLink(t *testing.T) { diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 88cb564e4..30fe639af 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -3,9 +3,9 @@ package merkledag import ( "github.com/ipfs/go-ipfs/blocks" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" - u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) type RawNode struct { diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index 34149f891..e5caa9cf4 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -5,7 +5,7 @@ import ( "context" "errors" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) // Order is an identifier for traversal algorithm orders diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 087bfa982..601c39bfb 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -8,7 +8,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) func TestDFSPreNoSkip(t *testing.T) { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 87add22dd..d7e5462ec 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -7,7 +7,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" context "context" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) const ( diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 972e98a68..4a3d2e7f8 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -12,7 +12,7 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" syncds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) type Editor struct { diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 3634ac595..541920529 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" context "context" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func TestAddLink(t *testing.T) { From 919d5c2b6120349b006d3d17459176aedb35ad3e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1799/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@1f3adf35a9a7d6818c443d0b149727c242535e38 --- unixfs/archive/archive.go | 2 +- unixfs/archive/tar/writer.go | 2 +- unixfs/hamt/hamt.go | 2 +- unixfs/io/dagreader.go | 2 +- unixfs/io/dirbuilder.go | 2 +- unixfs/io/resolve.go | 2 +- unixfs/mod/dagmodifier.go | 4 ++-- unixfs/mod/dagmodifier_test.go | 2 +- unixfs/test/utils.go | 4 ++-- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index b39c71560..c28b2fe68 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -11,7 +11,7 @@ import ( tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) // DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 26492d897..5498c463e 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -13,8 +13,8 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" upb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) // Writer is a utility structure that helps to write diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index cfe448d9c..4c3f4f913 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -31,8 +31,8 @@ import ( format "github.com/ipfs/go-ipfs/unixfs" upb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" "gx/ipfs/QmfJHywXQu98UeZtGJBQrPAR6AtmDjjbe3qjTo9piXHPnx/murmur3" ) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index d36c33c2e..48aa91369 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -10,8 +10,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) var ErrIsDir = errors.New("this dag node is a directory") diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 45059f78e..c0587480d 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -9,7 +9,7 @@ import ( format "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) // ShardSplitThreshold specifies how large of an unsharded directory diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index ab9239601..16f360b4a 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -7,7 +7,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) func ResolveUnixfsOnce(ctx context.Context, ds dag.DAGService, nd node.Node, name string) (*node.Link, error) { diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 73852e2fa..c531caa15 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -15,9 +15,9 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) var ErrSeekFail = errors.New("failed to seek properly") diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index cdd97038b..ecc9be644 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -19,7 +19,7 @@ import ( context "context" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" ) func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.Blockstore) { diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index c46c4d3e5..ada15a086 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -14,8 +14,8 @@ import ( mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" - u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) func SizeSplitterGen(size int64) chunk.SplitterGen { From e5e51ca9a394ef8f72e0f43ce0032607ae77e00d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1800/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@7bdb9ce0045df40e8a8ee4a012ea70357dd4c632 --- mfs/dir.go | 2 +- mfs/file.go | 2 +- mfs/mfs_test.go | 6 +++--- mfs/ops.go | 2 +- mfs/repub_test.go | 2 +- mfs/system.go | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 63ae8d408..a0a9205b6 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -15,7 +15,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) var ErrNotYetImplemented = errors.New("not yet implemented") diff --git a/mfs/file.go b/mfs/file.go index a379c802f..02a5b62c8 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index e3c2f3e19..11a13b8e0 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -26,9 +26,9 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" - u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) func emptyDirNode() *dag.ProtoNode { diff --git a/mfs/ops.go b/mfs/ops.go index 8dd7131d8..694a001b0 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -9,7 +9,7 @@ import ( path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) // Mv moves the file or directory at 'src' to 'dst' diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 0952de0dd..832bee0d2 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -7,7 +7,7 @@ import ( ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" "context" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index 0bc319240..8bc6893c8 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -20,8 +20,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) var ErrNotExist = errors.New("no such rootfs") From 57b144a54e7031f8983bd7d0f3ae063a783d13b5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1801/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@1f9263d15217bf8dc5e6e97342fb308405194ec1 --- path/path.go | 2 +- path/resolver.go | 4 ++-- path/resolver_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/path/path.go b/path/path.go index 3a885b478..419f6c9c9 100644 --- a/path/path.go +++ b/path/path.go @@ -5,7 +5,7 @@ import ( "path" "strings" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) // ErrBadPath is returned when a given path is incorrectly formatted diff --git a/path/resolver.go b/path/resolver.go index 4339fbf0e..4ebde479f 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -10,8 +10,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) var log = logging.Logger("path") diff --git a/path/resolver_test.go b/path/resolver_test.go index f489c1eb1..6e7ad64de 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -9,8 +9,8 @@ import ( dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" - util "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + util "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) func randNode() *merkledag.ProtoNode { From efa1be798c6eea5d3f523aca09e09ed1fabc6967 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1802/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-keystore@c13ce809f7a06f45ef67fe4fb0c018b7442ed5b8 --- keystore/keystore.go | 2 +- keystore/keystore_test.go | 2 +- keystore/memkeystore.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index b52dabdea..b69e3e940 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -7,7 +7,7 @@ import ( "path/filepath" "strings" - ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" + ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ) type Keystore interface { diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index 58b699888..4840069bc 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -7,7 +7,7 @@ import ( "sort" "testing" - ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" + ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ) type rr struct{} diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index 2351f54d8..ae45ecf21 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -1,6 +1,6 @@ package keystore -import ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" +import ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" type MemKeystore struct { keys map[string]ci.PrivKey From 2a2956584c2a9d4d6a3badef785b17b6bf407f46 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1803/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@566ac617e7f2998385bb497219a69e2fb39e568b --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 4a990da9a..ff1ca8a35 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -10,8 +10,8 @@ import ( pin "github.com/ipfs/go-ipfs/pin" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index e0c211ffb..8c742d1c0 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -13,8 +13,8 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 656f8f63d..cbf89c601 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -12,8 +12,8 @@ import ( context "context" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func randNode() (*mdag.ProtoNode, *cid.Cid) { diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index bf05924fd..472142b5c 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -12,9 +12,9 @@ import ( "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 7dbc61a85..f31cb890f 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -12,7 +12,7 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func ignoreCids(_ *cid.Cid) {} From f4d8afc5c656dd3dce440f58efae176c42f70e41 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1804/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@6f691a3b9785251b2079a37b0a2b0bc59e81771d --- routing/mock/centralized_client.go | 14 +++++++------- routing/mock/centralized_server.go | 6 +++--- routing/mock/centralized_test.go | 6 +++--- routing/mock/dht.go | 4 ++-- routing/mock/interface.go | 4 ++-- routing/none/none_client.go | 10 +++++----- routing/offline/offline.go | 14 +++++++------- routing/supernode/client.go | 16 ++++++++-------- routing/supernode/proxy/loopback.go | 6 +++--- routing/supernode/proxy/standard.go | 14 +++++++------- routing/supernode/server.go | 10 +++++----- routing/supernode/server_test.go | 2 +- 12 files changed, 53 insertions(+), 53 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index eb2c183bb..513a50cb3 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,16 +8,16 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" "github.com/ipfs/go-ipfs/thirdparty/testutil" + pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - ma "gx/ipfs/QmSWLfmj5frN9xVLMMN846dMDriy5wN5jeghUm7aTW3DAG/go-multiaddr" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" + dhtpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" + u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" - dhtpb "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record/pb" - pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" + routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" + ma "gx/ipfs/QmcyqRMCAXVtYPS4DiBrA7sezL9rRGfW8Ctx7cywL4TXJj/go-multiaddr" + peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 936c06f14..50844f9f7 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -8,11 +8,11 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" + pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" - pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 35a51f16c..c7c6836d1 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,9 +8,9 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" - pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" + pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" + u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 3bc799a89..9a084d603 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" + dht "gx/ipfs/QmQcRLisUbREko56ThfgzdBorMGNfNjgqzvwuPPr1jFw6A/go-libp2p-kad-dht" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - dht "gx/ipfs/QmaoxFZcgwGyoB57pCYQobejLoNgqaA6trr3zxxrbm4UXe/go-libp2p-kad-dht" - mocknet "gx/ipfs/QmeWJwi61vii5g8zQUB9UGegfUbmhTKHgeDFP9XuSp5jZ4/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmRai5yZNL67pWCoznW7sBdFnqZrFULuJ5w8KhmRyhdgN4/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 96a5a6f4e..77db71313 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -11,8 +11,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" - peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" + routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" + peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) // Server provides mockrouting Clients diff --git a/routing/none/none_client.go b/routing/none/none_client.go index e48ffffda..8e0a2a307 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,12 +6,12 @@ import ( repo "github.com/ipfs/go-ipfs/repo" + pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" - p2phost "gx/ipfs/QmXzeAcmKDTfNZQBiyF22hQKuTK7P5z6MBBQLTk9bbiSUc/go-libp2p-host" - pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" + p2phost "gx/ipfs/QmcyNeWPsoFGxThGpV8JnJdfUNankKhWCTrbrcFRQda4xR/go-libp2p-host" + peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 5515e49f9..165af084a 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,16 +7,16 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - ci "gx/ipfs/QmPGxZ1DP2w45WcogpW1h43BvseXbfke9N91qotpoQcUeS/go-libp2p-crypto" + pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" + ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" + record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" + pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - record "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record" - pb "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record/pb" - pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" + routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" + "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) var log = logging.Logger("offlinerouting") diff --git a/routing/supernode/client.go b/routing/supernode/client.go index d96ed3ebc..1c2fe866a 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,16 +8,16 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" + pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" + dhtpb "gx/ipfs/QmQcRLisUbREko56ThfgzdBorMGNfNjgqzvwuPPr1jFw6A/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - routing "gx/ipfs/QmUc6twRJRE9MNrUGd8eo9WjHHxebGppdZfptGCASkR7fF/go-libp2p-routing" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" - loggables "gx/ipfs/QmXs1igHHEaUmMxKtbP8Z9wTjitQ75sqxaKQP4QgnLN4nn/go-libp2p-loggables" - "gx/ipfs/QmXzeAcmKDTfNZQBiyF22hQKuTK7P5z6MBBQLTk9bbiSUc/go-libp2p-host" + loggables "gx/ipfs/QmVesPmqbPp7xRGyY96tnBwzDtVV1nqv4SCVxo5zCqKyH8/go-libp2p-loggables" + pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/QmaoxFZcgwGyoB57pCYQobejLoNgqaA6trr3zxxrbm4UXe/go-libp2p-kad-dht/pb" - pb "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record/pb" - pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" + routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" + "gx/ipfs/QmcyNeWPsoFGxThGpV8JnJdfUNankKhWCTrbrcFRQda4xR/go-libp2p-host" + peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index f2fa36242..02014cef6 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,10 +2,10 @@ package proxy import ( context "context" - inet "gx/ipfs/QmVtMT3fD7DzQNW7hdm6Xe6KPstzcggrhNpeVZ4422UpKK/go-libp2p-net" - peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" + dhtpb "gx/ipfs/QmQcRLisUbREko56ThfgzdBorMGNfNjgqzvwuPPr1jFw6A/go-libp2p-kad-dht/pb" + inet "gx/ipfs/QmVHSBsn8LEeay8m5ERebgUVuhzw838PsyTttCmP6GMJkg/go-libp2p-net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/QmaoxFZcgwGyoB57pCYQobejLoNgqaA6trr3zxxrbm4UXe/go-libp2p-kad-dht/pb" + peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 69b8812af..8cbffe2b3 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,15 +4,15 @@ import ( "context" "errors" + pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" + dhtpb "gx/ipfs/QmQcRLisUbREko56ThfgzdBorMGNfNjgqzvwuPPr1jFw6A/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - kbucket "gx/ipfs/QmTxn7JEA8DiBvd9vVzErAzadHn6TwjCKTjjUfPyRH9wjZ/go-libp2p-kbucket" - inet "gx/ipfs/QmVtMT3fD7DzQNW7hdm6Xe6KPstzcggrhNpeVZ4422UpKK/go-libp2p-net" - peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" - loggables "gx/ipfs/QmXs1igHHEaUmMxKtbP8Z9wTjitQ75sqxaKQP4QgnLN4nn/go-libp2p-loggables" - host "gx/ipfs/QmXzeAcmKDTfNZQBiyF22hQKuTK7P5z6MBBQLTk9bbiSUc/go-libp2p-host" + inet "gx/ipfs/QmVHSBsn8LEeay8m5ERebgUVuhzw838PsyTttCmP6GMJkg/go-libp2p-net" + loggables "gx/ipfs/QmVesPmqbPp7xRGyY96tnBwzDtVV1nqv4SCVxo5zCqKyH8/go-libp2p-loggables" + kbucket "gx/ipfs/QmXKSwZVoHCTne4jTLzDtMc2K6paEZ2QaUMQfJ4ogYd28n/go-libp2p-kbucket" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/QmaoxFZcgwGyoB57pCYQobejLoNgqaA6trr3zxxrbm4UXe/go-libp2p-kad-dht/pb" - pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" + host "gx/ipfs/QmcyNeWPsoFGxThGpV8JnJdfUNankKhWCTrbrcFRQda4xR/go-libp2p-host" + peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 7744237ad..1110c4320 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,13 +8,13 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" + dhtpb "gx/ipfs/QmQcRLisUbREko56ThfgzdBorMGNfNjgqzvwuPPr1jFw6A/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - peer "gx/ipfs/QmWUswjn261LSyVxWAEpMVtPdy8zmKBJJfBpG3Qdpa8ZsE/go-libp2p-peer" + record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" + pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/QmaoxFZcgwGyoB57pCYQobejLoNgqaA6trr3zxxrbm4UXe/go-libp2p-kad-dht/pb" - record "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record" - pb "gx/ipfs/QmcTnycWsBgvNYFYgWdWi8SRDCeevG8HBUQHkvg4KLXUsW/go-libp2p-record/pb" - pstore "gx/ipfs/Qme1g4e3m2SmdiSGGU3vSWmUStwUjc5oECnEriaK9Xa1HU/go-libp2p-peerstore" + peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) // Server handles routing queries using a database backend diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index d08e157bb..a06e29e76 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" + dhtpb "gx/ipfs/QmQcRLisUbREko56ThfgzdBorMGNfNjgqzvwuPPr1jFw6A/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dhtpb "gx/ipfs/QmaoxFZcgwGyoB57pCYQobejLoNgqaA6trr3zxxrbm4UXe/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 247fb7bb38ae5f085b1f4173ac4e850431023c9e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1805/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-filestore@408083807a58fcc981ac1805840605722fd2eb8d --- filestore/filestore.go | 2 +- filestore/filestore_test.go | 2 +- filestore/fsrefstore.go | 2 +- filestore/util.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 8c5822705..047d26b51 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -16,7 +16,7 @@ import ( dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 2b30c7957..5569c61b5 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -12,7 +12,7 @@ import ( posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 7b41555b9..46cc39b7f 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -17,7 +17,7 @@ import ( dsns "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/namespace" dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index 0d764cfb7..6dd6cf1c2 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -9,7 +9,7 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) // Status is used to identify the state of the block data referenced From 5f3b9fb0e4baec9eb2902b5f6d3abeba720ddb16 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1806/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@8b2cebdbd6601ccf88e5a6ca744e9ac5a4ca5568 --- blockstore/arc_cache.go | 2 +- blockstore/arc_cache_test.go | 2 +- blockstore/blockstore.go | 2 +- blockstore/blockstore_test.go | 4 ++-- blockstore/bloom_cache.go | 2 +- blockstore/util/remove.go | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index d14600f01..75c7ee489 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -7,8 +7,8 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) // arccache wraps a BlockStore with an Adaptive Replacement Cache (ARC) for diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index f143a1a43..879042380 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -8,7 +8,7 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" syncds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) var exampleBlock = blocks.NewBlock([]byte("foo")) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 092a9cced..ac4b87405 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -15,7 +15,7 @@ import ( dsns "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/namespace" dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 781a1eec1..93705997e 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -12,8 +12,8 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" ds_sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func TestGetWhenKeyNotPresent(t *testing.T) { diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index cc6526bc8..47f5ac018 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -8,7 +8,7 @@ import ( "github.com/ipfs/go-ipfs/blocks" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" bloom "gx/ipfs/QmeiMCBkYHxkDkDfnDadzz4YxY5ruL5Pj499essE4vRsGM/bbloom" ) diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 2523b3ac2..57c6741ca 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -6,7 +6,7 @@ import ( "io" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" bs "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/pin" From 4b41430dc8f886ac7467fa38275964d65417c648 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1807/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@35ed8c337050c01d367456f1b5f84200a862d583 --- blockservice/blockservice.go | 2 +- blockservice/test/blocks_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 6629d67cd..294b54133 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -13,7 +13,7 @@ import ( exchange "github.com/ipfs/go-ipfs/exchange" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) var log = logging.Logger("blockservice") diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index db60a3726..68f77be7e 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -14,8 +14,8 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func newObject(data []byte) blocks.Block { From 72520c5c5d0ab22c9e4f2f7d6413528d09a25983 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1808/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@b7fdde70d963266a1700a38e7c6d7d6b151ce4c0 --- exchange/offline/offline.go | 2 +- exchange/offline/offline_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 10d6609a0..399af0f58 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -9,7 +9,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index df1b0452b..d2f877a94 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -10,8 +10,8 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" ds_sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func TestBlockReturnsErr(t *testing.T) { From 0b9e5c49713066479418ba48f37171fa209e56db Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1809/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-interface@e3b7efad7ef666e9b56b5ceca8f8766e2c51be68 --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index aabece6b3..58c4c14ae 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -7,7 +7,7 @@ import ( blocks "github.com/ipfs/go-ipfs/blocks" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) // Any type that implements exchange.Interface may be used as an IPFS block From 9c0f3bcde5e0c8920656acad62e46155a330a2af Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1810/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-chunker@3a5a6fad0b1aa609d1901e0f7f27d9aa7a4edf7f --- chunker/rabin_test.go | 2 +- chunker/splitting_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 5603621b2..907b80999 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -4,7 +4,7 @@ import ( "bytes" "fmt" "github.com/ipfs/go-ipfs/blocks" - "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" "io" "testing" ) diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index bbe1e499f..918a46659 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -5,7 +5,7 @@ import ( "io" "testing" - u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util" + u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" ) func randBuf(t *testing.T, size int) []byte { From 5bd6e88a5f3d017ae1d1bb3e8f061b23d2c5e51c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1811/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-ds-help@c2f362d688362c5440b2dafbf136421680f28b2e --- datastore/dshelp/key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 93c22ef1a..0c4fab85b 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -2,7 +2,7 @@ package dshelp import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" base32 "gx/ipfs/QmZvZSVtvxak4dcTkhsQhqd1SQ6rg5UzaSTu62WfWKjj93/base32" ) From dcecd3345b8ceed091ee2e80857695e170ea5bf2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 24 Mar 2017 23:51:18 -0700 Subject: [PATCH 1812/3817] bubble up updates from go-multihash changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/interface-go-ipfs-core@be73d10538ec977bdb62976f52ac926f5eb83232 --- coreiface/interface.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index d72fc8a3b..a7762c8c2 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -5,8 +5,8 @@ import ( "errors" "io" - cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid" - ipld "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + ipld "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) type Path interface { @@ -17,7 +17,7 @@ type Path interface { } // TODO: should we really copy these? -// if we didn't, godoc would generate nice links straight to go-ipld-node +// if we didn't, godoc would generate nice links straight to go-ipld-format type Node ipld.Node type Link ipld.Link From 7bd8b43d47ab9dcb2e0acba39cd8e07c8c179cda Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 20 Apr 2017 19:09:38 +0200 Subject: [PATCH 1813/3817] deps: Update go-is-domain to contain new gTLD It should resolve issues with newer gTLDs being not selected License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@90b1c01d20e087f8f2354fc21a1184c461ad4f01 --- namesys/dns.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/dns.go b/namesys/dns.go index 93a5501da..feb97e04a 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -6,7 +6,7 @@ import ( "strings" context "context" - isd "gx/ipfs/QmaeHSCBd9XjXxmgHEiKkHtLcMCb2eZsPLKT7bHgBfBkqw/go-is-domain" + isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" path "github.com/ipfs/go-ipfs/path" ) From e8cd337a96c3fd724f80118d53239e735129ddbb Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 24 Apr 2017 14:57:57 +0200 Subject: [PATCH 1814/3817] mics: cleanup imports in touched files License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@94b011a00a7d051bd044c0c24c5b7269d1b14a8c --- namesys/dns.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index feb97e04a..3cb2cd6e2 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -1,14 +1,14 @@ package namesys import ( + "context" "errors" "net" "strings" - context "context" - isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - path "github.com/ipfs/go-ipfs/path" + + isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) type LookupTXTFunc func(name string) (txt []string, err error) From 4ee6ea6967190329d1741d0227141de05e3e8a8f Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 24 Mar 2017 16:18:20 -0400 Subject: [PATCH 1815/3817] merkledag: provide better diagnostics when Prefix.Sum fails License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@90386a3772846f36217248da1f6fa74838344a4a --- ipld/merkledag/node.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 0b11b928e..ce075726d 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -266,6 +266,7 @@ func (n *ProtoNode) Cid() *cid.Cid { c, err := n.Prefix.Sum(n.RawData()) if err != nil { // programmer error + err = fmt.Errorf("invalid CID of length %d: %x: %v", len(n.RawData()), n.RawData(), err) panic(err) } From 1030811c0bc35cb9931b15bc4c7b14590b77ba66 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 2 Mar 2017 21:35:04 -0500 Subject: [PATCH 1816/3817] adder: add support for using CidV1 License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-unixfs@123b0fd65389bbbd92e04af01bdab9b4a49e4c2b --- unixfs/io/dirbuilder.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index c0587480d..a8663763c 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -8,6 +8,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) @@ -79,6 +80,17 @@ func NewDirectoryFromNode(dserv mdag.DAGService, nd node.Node) (*Directory, erro } } +// SetPrefix sets the prefix of the root node +func (d *Directory) SetPrefix(prefix cid.Prefix) { + if d.dirnode != nil { + d.dirnode.SetPrefix(prefix) + } + // FIXME: Should we do this? -- kevina + //if d.shard != nil { + // d.shard.SetPrefix(prefix) + //} +} + // AddChild adds a (name, key)-pair to the root node. func (d *Directory) AddChild(ctx context.Context, name string, nd node.Node) error { if d.shard == nil { From ea4604bf243309085382a25f1d8be80732cdcfee Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 2 Mar 2017 21:35:04 -0500 Subject: [PATCH 1817/3817] adder: add support for using CidV1 License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@f8a265e7a5bda1850fa2990f3aeab7b6cdd4f1f1 --- ipld/merkledag/coding.go | 2 +- ipld/merkledag/node.go | 31 +++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 25f43e0d6..b5c2075da 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -86,7 +86,7 @@ func (n *ProtoNode) EncodeProtobuf(force bool) ([]byte, error) { if n.cached == nil { if n.Prefix.Codec == 0 { // unset - n.Prefix = defaultCidPrefix + n.Prefix = v0CidPrefix } c, err := n.Prefix.Sum(n.encoded) if err != nil { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index ce075726d..f4c4a0839 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -28,13 +28,37 @@ type ProtoNode struct { Prefix cid.Prefix } -var defaultCidPrefix = cid.Prefix{ +var v0CidPrefix = cid.Prefix{ Codec: cid.DagProtobuf, MhLength: -1, MhType: mh.SHA2_256, Version: 0, } +var v1CidPrefix = cid.Prefix{ + Codec: cid.DagProtobuf, + MhLength: -1, + MhType: mh.SHA2_256, + Version: 1, +} + +func PrefixForCidVersion(version int) (cid.Prefix, error) { + switch version { + case 0: + return v0CidPrefix, nil + case 1: + return v1CidPrefix, nil + default: + return cid.Prefix{}, fmt.Errorf("unknown CID version: %d", version) + } +} + +func (n *ProtoNode) SetPrefix(prefix cid.Prefix) { + n.Prefix = prefix + n.encoded = nil + n.cached = nil +} + type LinkSlice []*node.Link func (ls LinkSlice) Len() int { return len(ls) } @@ -158,6 +182,9 @@ func (n *ProtoNode) Copy() node.Node { nnode.links = make([]*node.Link, len(n.links)) copy(nnode.links, n.links) } + + nnode.Prefix = n.Prefix + return nnode } @@ -260,7 +287,7 @@ func (n *ProtoNode) Cid() *cid.Cid { } if n.Prefix.Codec == 0 { - n.Prefix = defaultCidPrefix + n.Prefix = v0CidPrefix } c, err := n.Prefix.Sum(n.RawData()) From 7f20a557542d366cebb1877d458ef16f60153ff8 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 2 Mar 2017 21:35:04 -0500 Subject: [PATCH 1818/3817] adder: add support for using CidV1 License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-mfs@d054ee61c95a4d261b3dc2ac9d226017e88c700e --- mfs/dir.go | 5 +++++ mfs/ops.go | 2 ++ mfs/system.go | 3 +++ 3 files changed, 10 insertions(+) diff --git a/mfs/dir.go b/mfs/dir.go index a0a9205b6..11280bc17 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -15,6 +15,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) @@ -57,6 +58,10 @@ func NewDirectory(ctx context.Context, name string, node node.Node, parent child }, nil } +func (d *Directory) SetPrefix(prefix cid.Prefix) { + d.dirbuilder.SetPrefix(prefix) +} + // closeChild updates the child by the given name to the dag node 'nd' // and changes its own dag node func (d *Directory) closeChild(name string, nd node.Node, sync bool) error { diff --git a/mfs/ops.go b/mfs/ops.go index 694a001b0..f84540a6a 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -134,6 +134,7 @@ func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { if err != nil { return err } + mkd.SetPrefix(r.Prefix) fsn = mkd } else if err != nil { return err @@ -152,6 +153,7 @@ func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { return err } } + final.SetPrefix(r.Prefix) if flush { err := final.Flush() diff --git a/mfs/system.go b/mfs/system.go index 8bc6893c8..a28a7fb10 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -61,6 +61,9 @@ type Root struct { dserv dag.DAGService Type string + + // Prefix to use for any children created + Prefix cid.Prefix } type PubFunc func(context.Context, *cid.Cid) error From 113478c26c042d40548b0128897b537141c188fe Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 24 Mar 2017 16:41:50 -0400 Subject: [PATCH 1819/3817] merkeldag: change SetPrefix param to a pointer and reset the prefix on nil License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-unixfs@c1e9ccd624ebfe172459cf7c5752d37bb5b9ff50 --- unixfs/io/dirbuilder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index a8663763c..a5dce7f48 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -81,7 +81,7 @@ func NewDirectoryFromNode(dserv mdag.DAGService, nd node.Node) (*Directory, erro } // SetPrefix sets the prefix of the root node -func (d *Directory) SetPrefix(prefix cid.Prefix) { +func (d *Directory) SetPrefix(prefix *cid.Prefix) { if d.dirnode != nil { d.dirnode.SetPrefix(prefix) } From f914dff23092ee34d396a2cefa402b2bb4a1d6dd Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 24 Mar 2017 16:41:50 -0400 Subject: [PATCH 1820/3817] merkeldag: change SetPrefix param to a pointer and reset the prefix on nil License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@d95bc83ce83978af36e63d3025048046505b6b57 --- ipld/merkledag/node.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index f4c4a0839..4090a99b8 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -53,10 +53,17 @@ func PrefixForCidVersion(version int) (cid.Prefix, error) { } } -func (n *ProtoNode) SetPrefix(prefix cid.Prefix) { - n.Prefix = prefix - n.encoded = nil - n.cached = nil +// SetPrefix sets the prefix if it is non nil, if prefix is nil then +// it resets it the default value +func (n *ProtoNode) SetPrefix(prefix *cid.Prefix) { + if prefix == nil { + n.Prefix = v0CidPrefix + } else { + n.Prefix = *prefix + n.Prefix.Codec = cid.DagProtobuf + n.encoded = nil + n.cached = nil + } } type LinkSlice []*node.Link @@ -287,7 +294,7 @@ func (n *ProtoNode) Cid() *cid.Cid { } if n.Prefix.Codec == 0 { - n.Prefix = v0CidPrefix + n.SetPrefix(nil) } c, err := n.Prefix.Sum(n.RawData()) From e56a34700f02cc7bc2ba04400beed71b06e0d480 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 24 Mar 2017 16:41:50 -0400 Subject: [PATCH 1821/3817] merkeldag: change SetPrefix param to a pointer and reset the prefix on nil License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-mfs@1ecae41d13ce9628e29ee898a8d77baadf2446ea --- mfs/dir.go | 2 +- mfs/system.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 11280bc17..102ee15cb 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -58,7 +58,7 @@ func NewDirectory(ctx context.Context, name string, node node.Node, parent child }, nil } -func (d *Directory) SetPrefix(prefix cid.Prefix) { +func (d *Directory) SetPrefix(prefix *cid.Prefix) { d.dirbuilder.SetPrefix(prefix) } diff --git a/mfs/system.go b/mfs/system.go index a28a7fb10..4ed84d83b 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -63,7 +63,7 @@ type Root struct { Type string // Prefix to use for any children created - Prefix cid.Prefix + Prefix *cid.Prefix } type PubFunc func(context.Context, *cid.Cid) error From fb0aded8cb0267ffeda9e8cf4bcb0596b0f94100 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 31 Mar 2017 01:00:50 -0400 Subject: [PATCH 1822/3817] hamt: support using CIDv1 by allowing the prefix to be set License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-unixfs@957da2c0573e73d81fb1a185c472a3a210fc888c --- unixfs/hamt/hamt.go | 7 +++++++ unixfs/io/dirbuilder.go | 7 +++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 4c3f4f913..17aa01733 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -31,6 +31,7 @@ import ( format "github.com/ipfs/go-ipfs/unixfs" upb "github.com/ipfs/go-ipfs/unixfs/pb" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" "gx/ipfs/QmfJHywXQu98UeZtGJBQrPAR6AtmDjjbe3qjTo9piXHPnx/murmur3" @@ -50,6 +51,7 @@ type HamtShard struct { tableSize int tableSizeLg2 int + prefix *cid.Prefix hashFunc uint64 prefixPadStr string @@ -123,9 +125,14 @@ func NewHamtFromDag(dserv dag.DAGService, nd node.Node) (*HamtShard, error) { return ds, nil } +func (ds *HamtShard) SetPrefix(prefix *cid.Prefix) { + ds.prefix = prefix +} + // Node serializes the HAMT structure into a merkledag node with unixfs formatting func (ds *HamtShard) Node() (node.Node, error) { out := new(dag.ProtoNode) + out.SetPrefix(ds.prefix) // TODO: optimized 'for each set bit' for i := 0; i < ds.tableSize; i++ { diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index a5dce7f48..bcf9770f4 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -85,10 +85,9 @@ func (d *Directory) SetPrefix(prefix *cid.Prefix) { if d.dirnode != nil { d.dirnode.SetPrefix(prefix) } - // FIXME: Should we do this? -- kevina - //if d.shard != nil { - // d.shard.SetPrefix(prefix) - //} + if d.shard != nil { + d.shard.SetPrefix(prefix) + } } // AddChild adds a (name, key)-pair to the root node. From 6224668825fdeef1b266c1648845def45c25fbc1 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 25 Apr 2017 23:47:48 -0400 Subject: [PATCH 1823/3817] Documentation License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-unixfs@e19161f5bb5997477122528a3fcfb285b26f465a --- unixfs/hamt/hamt.go | 1 + 1 file changed, 1 insertion(+) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 17aa01733..ccdffe7e4 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -125,6 +125,7 @@ func NewHamtFromDag(dserv dag.DAGService, nd node.Node) (*HamtShard, error) { return ds, nil } +// SetPrefix sets the CID Prefix func (ds *HamtShard) SetPrefix(prefix *cid.Prefix) { ds.prefix = prefix } From 41b0441758122e5bc80c9ae6d08949c13356bf67 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 25 Apr 2017 23:47:48 -0400 Subject: [PATCH 1824/3817] Documentation License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@b709fb48090336fcc5a7450a065f79cedddec3da --- ipld/merkledag/node.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 4090a99b8..fa575097a 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -42,6 +42,7 @@ var v1CidPrefix = cid.Prefix{ Version: 1, } +// PrefixForCidVersion returns the Protobuf prefix for a given CID version func PrefixForCidVersion(version int) (cid.Prefix, error) { switch version { case 0: @@ -53,7 +54,7 @@ func PrefixForCidVersion(version int) (cid.Prefix, error) { } } -// SetPrefix sets the prefix if it is non nil, if prefix is nil then +// SetPrefix sets the CID prefix if it is non nil, if prefix is nil then // it resets it the default value func (n *ProtoNode) SetPrefix(prefix *cid.Prefix) { if prefix == nil { From cd456954760ea63431d2c4a7692d6edb50878cee Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 25 Apr 2017 23:47:48 -0400 Subject: [PATCH 1825/3817] Documentation License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-mfs@58dbbadffe1cac2f0729213d9a0982828cb34dd1 --- mfs/dir.go | 1 + 1 file changed, 1 insertion(+) diff --git a/mfs/dir.go b/mfs/dir.go index 102ee15cb..60cae39c7 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -58,6 +58,7 @@ func NewDirectory(ctx context.Context, name string, node node.Node, parent child }, nil } +// SetPrefix sets the CID prefix func (d *Directory) SetPrefix(prefix *cid.Prefix) { d.dirbuilder.SetPrefix(prefix) } From 9339a2478a19cd8004dd4674cd27b884aeeb2d5b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 29 Apr 2017 13:01:22 -0700 Subject: [PATCH 1826/3817] Fix gateway handling of sharded directories License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@fb78dc263cd3ca6ea02fbda27e4438916c4851ce --- unixfs/io/dagreader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 48aa91369..9abfe0c9d 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -45,7 +45,7 @@ func NewDagReader(ctx context.Context, n node.Node, serv mdag.DAGService) (DagRe } switch pb.GetType() { - case ftpb.Data_Directory: + case ftpb.Data_Directory, ftpb.Data_HAMTShard: // Dont allow reading directories return nil, ErrIsDir case ftpb.Data_File, ftpb.Data_Raw: From f79019bdcdff5e75257e15249287b51009221cc5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 30 Apr 2017 13:37:37 -0700 Subject: [PATCH 1827/3817] Fix sharding memory growth, and fix resolver for unixfs paths License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@a01e57d25debd78b135ae0f8f8ff58b49bb84f50 --- unixfs/hamt/hamt.go | 63 +++++++++++++++++++++++++++++------------ unixfs/io/dirbuilder.go | 6 ++-- 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index ccdffe7e4..ceda529c9 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -62,7 +62,7 @@ type HamtShard struct { // child can either be another shard, or a leaf node value type child interface { - Node() (node.Node, error) + Link() (*node.Link, error) Label() string } @@ -144,12 +144,12 @@ func (ds *HamtShard) Node() (node.Node, error) { cindex := ds.indexForBitPos(i) ch := ds.children[cindex] if ch != nil { - cnd, err := ch.Node() + clnk, err := ch.Link() if err != nil { return nil, err } - err = out.AddNodeLinkClean(ds.linkNamePrefix(i)+ch.Label(), cnd) + err = out.AddRawLink(ds.linkNamePrefix(i)+ch.Label(), clnk) if err != nil { return nil, err } @@ -188,10 +188,10 @@ func (ds *HamtShard) Node() (node.Node, error) { type shardValue struct { key string - val node.Node + val *node.Link } -func (sv *shardValue) Node() (node.Node, error) { +func (sv *shardValue) Link() (*node.Link, error) { return sv.val, nil } @@ -214,7 +214,18 @@ func (ds *HamtShard) Label() string { // Set sets 'name' = nd in the HAMT func (ds *HamtShard) Set(ctx context.Context, name string, nd node.Node) error { hv := &hashBits{b: hash([]byte(name))} - return ds.modifyValue(ctx, hv, name, nd) + _, err := ds.dserv.Add(nd) + if err != nil { + return err + } + + lnk, err := node.MakeLink(nd) + if err != nil { + return err + } + lnk.Name = ds.linkNamePrefix(0) + name + + return ds.modifyValue(ctx, hv, name, lnk) } // Remove deletes the named entry if it exists, this operation is idempotent. @@ -226,13 +237,16 @@ func (ds *HamtShard) Remove(ctx context.Context, name string) error { func (ds *HamtShard) Find(ctx context.Context, name string) (node.Node, error) { hv := &hashBits{b: hash([]byte(name))} - var out node.Node + var out *node.Link err := ds.getValue(ctx, hv, name, func(sv *shardValue) error { out = sv.val return nil }) + if err != nil { + return nil, err + } - return out, err + return ds.dserv.Get(ctx, out.Cid) } // getChild returns the i'th child of this shard. If it is cached in the @@ -291,9 +305,10 @@ func (ds *HamtShard) loadChild(ctx context.Context, i int) (child, error) { c = cds } else { + lnk2 := *lnk c = &shardValue{ key: lnk.Name[ds.maxpadlen:], - val: nd, + val: &lnk2, } } @@ -305,16 +320,32 @@ func (ds *HamtShard) setChild(i int, c child) { ds.children[i] = c } -func (ds *HamtShard) insertChild(idx int, key string, val node.Node) error { - if val == nil { +func (ds *HamtShard) Link() (*node.Link, error) { + nd, err := ds.Node() + if err != nil { + return nil, err + } + + _, err = ds.dserv.Add(nd) + if err != nil { + return nil, err + } + + return node.MakeLink(nd) +} + +func (ds *HamtShard) insertChild(idx int, key string, lnk *node.Link) error { + if lnk == nil { return os.ErrNotExist } i := ds.indexForBitPos(idx) ds.bitfield.SetBit(ds.bitfield, idx, 1) + + lnk.Name = ds.linkNamePrefix(idx) + key sv := &shardValue{ key: key, - val: val, + val: lnk, } ds.children = append(ds.children[:i], append([]child{sv}, ds.children[i:]...)...) @@ -370,11 +401,7 @@ func (ds *HamtShard) EnumLinks(ctx context.Context) ([]*node.Link, error) { func (ds *HamtShard) ForEachLink(ctx context.Context, f func(*node.Link) error) error { return ds.walkTrie(ctx, func(sv *shardValue) error { - lnk, err := node.MakeLink(sv.val) - if err != nil { - return err - } - + lnk := sv.val lnk.Name = sv.key return f(lnk) @@ -414,7 +441,7 @@ func (ds *HamtShard) walkTrie(ctx context.Context, cb func(*shardValue) error) e return nil } -func (ds *HamtShard) modifyValue(ctx context.Context, hv *hashBits, key string, val node.Node) error { +func (ds *HamtShard) modifyValue(ctx context.Context, hv *hashBits, key string, val *node.Link) error { idx := hv.Next(ds.tableSizeLg2) if ds.bitfield.Bit(idx) != 1 { diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index bcf9770f4..285992081 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -48,10 +48,12 @@ func NewDirectory(dserv mdag.DAGService) *Directory { return db } +var ErrNotADir = fmt.Errorf("merkledag node was not a directory or shard") + func NewDirectoryFromNode(dserv mdag.DAGService, nd node.Node) (*Directory, error) { pbnd, ok := nd.(*mdag.ProtoNode) if !ok { - return nil, mdag.ErrNotProtobuf + return nil, ErrNotADir } pbd, err := format.FromBytes(pbnd.Data()) @@ -76,7 +78,7 @@ func NewDirectoryFromNode(dserv mdag.DAGService, nd node.Node) (*Directory, erro shard: shard, }, nil default: - return nil, fmt.Errorf("merkledag node was not a directory or shard") + return nil, ErrNotADir } } From e2940720d2d5a0d371f975564a43f329f2655ada Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 30 Apr 2017 13:37:37 -0700 Subject: [PATCH 1828/3817] Fix sharding memory growth, and fix resolver for unixfs paths License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@ced50d306fd9b655d06ed86d5b8e2abaca53c2d7 --- path/resolver.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index 4ebde479f..84a6fe66c 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -163,7 +163,7 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd node.Node, names []stri ctx, cancel = context.WithTimeout(ctx, time.Minute) defer cancel() - lnk, rest, err := nd.ResolveLink(names) + lnk, err := s.ResolveOnce(ctx, s.DAG, nd, names[0]) if err == dag.ErrLinkNotFound { return result, ErrNoLink{Name: names[0], Node: nd.Cid()} } else if err != nil { @@ -177,7 +177,7 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd node.Node, names []stri nd = nextnode result = append(result, nextnode) - names = rest + names = names[1:] } return result, nil } From 33450184a4dc3437cbcaf348a815efab32d4e1e4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 30 Apr 2017 14:01:48 -0700 Subject: [PATCH 1829/3817] fix coreapi unixfs resolving License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@4f4ba29659064a963973b9f1a3467373deb49b04 --- unixfs/hamt/hamt.go | 7 +++++-- unixfs/io/dirbuilder.go | 8 +++++++- unixfs/io/resolve.go | 33 ++++++++++++++++++++++----------- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index ceda529c9..d0b60a9c6 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -191,6 +191,7 @@ type shardValue struct { val *node.Link } +// Link returns a link to this node func (sv *shardValue) Link() (*node.Link, error) { return sv.val, nil } @@ -234,7 +235,8 @@ func (ds *HamtShard) Remove(ctx context.Context, name string) error { return ds.modifyValue(ctx, hv, name, nil) } -func (ds *HamtShard) Find(ctx context.Context, name string) (node.Node, error) { +// Find searches for a child node by 'name' within this hamt +func (ds *HamtShard) Find(ctx context.Context, name string) (*node.Link, error) { hv := &hashBits{b: hash([]byte(name))} var out *node.Link @@ -246,7 +248,7 @@ func (ds *HamtShard) Find(ctx context.Context, name string) (node.Node, error) { return nil, err } - return ds.dserv.Get(ctx, out.Cid) + return out, nil } // getChild returns the i'th child of this shard. If it is cached in the @@ -320,6 +322,7 @@ func (ds *HamtShard) setChild(i int, c child) { ds.children[i] = c } +// Link returns a merklelink to this shard node func (ds *HamtShard) Link() (*node.Link, error) { nd, err := ds.Node() if err != nil { diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 285992081..8d8509763 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -48,6 +48,7 @@ func NewDirectory(dserv mdag.DAGService) *Directory { return db } +// ErrNotADir implies that the given node was not a unixfs directory var ErrNotADir = fmt.Errorf("merkledag node was not a directory or shard") func NewDirectoryFromNode(dserv mdag.DAGService, nd node.Node) (*Directory, error) { @@ -167,7 +168,12 @@ func (d *Directory) Find(ctx context.Context, name string) (node.Node, error) { return d.dserv.Get(ctx, lnk.Cid) } - return d.shard.Find(ctx, name) + lnk, err := d.shard.Find(ctx, name) + if err != nil { + return nil, err + } + + return lnk.GetNode(ctx, d.dserv) } func (d *Directory) RemoveChild(ctx context.Context, name string) error { diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index 16f360b4a..f9213b55c 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -10,37 +10,48 @@ import ( node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) -func ResolveUnixfsOnce(ctx context.Context, ds dag.DAGService, nd node.Node, name string) (*node.Link, error) { +// ResolveUnixfsOnce resolves a single hop of a path through a graph in a +// unixfs context. This includes handling traversing sharded directories. +func ResolveUnixfsOnce(ctx context.Context, ds dag.DAGService, nd node.Node, names []string) (*node.Link, []string, error) { switch nd := nd.(type) { case *dag.ProtoNode: upb, err := ft.FromBytes(nd.Data()) if err != nil { // Not a unixfs node, use standard object traversal code - return nd.GetNodeLink(name) + lnk, err := nd.GetNodeLink(names[0]) + if err != nil { + return nil, nil, err + } + + return lnk, names[1:], nil } switch upb.GetType() { case ft.THAMTShard: s, err := hamt.NewHamtFromDag(ds, nd) if err != nil { - return nil, err + return nil, nil, err } - // TODO: optimized routine on HAMT for returning a dag.Link to avoid extra disk hits - out, err := s.Find(ctx, name) + out, err := s.Find(ctx, names[0]) if err != nil { - return nil, err + return nil, nil, err } - return node.MakeLink(out) + return out, names[1:], nil default: - return nd.GetNodeLink(name) + lnk, err := nd.GetNodeLink(names[0]) + if err != nil { + return nil, nil, err + } + + return lnk, names[1:], nil } default: - lnk, _, err := nd.ResolveLink([]string{name}) + lnk, rest, err := nd.ResolveLink(names) if err != nil { - return nil, err + return nil, nil, err } - return lnk, nil + return lnk, rest, nil } } From f02f7c8c5f9f3dea8906adda55a25c098d18c16b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 30 Apr 2017 14:01:48 -0700 Subject: [PATCH 1830/3817] fix coreapi unixfs resolving License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@9f6c93e19d6ff42601bbe439c48077bc6cfdd3ce --- path/resolver.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index 84a6fe66c..22bde65ee 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -37,7 +37,7 @@ func (e ErrNoLink) Error() string { type Resolver struct { DAG dag.DAGService - ResolveOnce func(ctx context.Context, ds dag.DAGService, nd node.Node, name string) (*node.Link, error) + ResolveOnce func(ctx context.Context, ds dag.DAGService, nd node.Node, names []string) (*node.Link, []string, error) } func NewBasicResolver(ds dag.DAGService) *Resolver { @@ -121,9 +121,10 @@ func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (node.Node, erro return nodes[len(nodes)-1], err } -func ResolveSingle(ctx context.Context, ds dag.DAGService, nd node.Node, name string) (*node.Link, error) { - lnk, _, err := nd.ResolveLink([]string{name}) - return lnk, err +// ResolveSingle simply resolves one hop of a path through a graph with no +// extra context (does not opaquely resolve through sharded nodes) +func ResolveSingle(ctx context.Context, ds dag.DAGService, nd node.Node, names []string) (*node.Link, []string, error) { + return nd.ResolveLink(names) } // ResolvePathComponents fetches the nodes for each segment of the given path. @@ -163,7 +164,7 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd node.Node, names []stri ctx, cancel = context.WithTimeout(ctx, time.Minute) defer cancel() - lnk, err := s.ResolveOnce(ctx, s.DAG, nd, names[0]) + lnk, rest, err := s.ResolveOnce(ctx, s.DAG, nd, names) if err == dag.ErrLinkNotFound { return result, ErrNoLink{Name: names[0], Node: nd.Cid()} } else if err != nil { @@ -177,7 +178,7 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd node.Node, names []stri nd = nextnode result = append(result, nextnode) - names = names[1:] + names = rest } return result, nil } From 5a49a18f6c3457c72463841918bca19160aa9d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Thu, 4 May 2017 17:29:29 +0900 Subject: [PATCH 1831/3817] Add a Has(name) method to the keystore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Michael Muré This commit was moved from ipfs/go-ipfs-keystore@a021dc6492240e19420b1b9b18d591525f59ac18 --- keystore/keystore.go | 9 +++++++++ keystore/keystore_test.go | 8 ++++++++ keystore/memkeystore.go | 5 +++++ keystore/memkeystore_test.go | 9 +++++++++ 4 files changed, 31 insertions(+) diff --git a/keystore/keystore.go b/keystore/keystore.go index b69e3e940..de0b62dc6 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -11,6 +11,7 @@ import ( ) type Keystore interface { + Has(string) bool Put(string, ci.PrivKey) error Get(string) (ci.PrivKey, error) Delete(string) error @@ -54,6 +55,14 @@ func NewFSKeystore(dir string) (*FSKeystore, error) { return &FSKeystore{dir}, nil } +func (ks *FSKeystore) Has(name string) bool { + kp := filepath.Join(ks.dir, name) + + _, err := os.Stat(kp) + + return err == nil +} + func (ks *FSKeystore) Put(name string, k ci.PrivKey) error { if err := validateName(name); err != nil { return err diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index 4840069bc..505d9119d 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -82,6 +82,14 @@ func TestKeystoreBasics(t *testing.T) { t.Fatal(err) } + if !ks.Has("foo") { + t.Fatal("should know it has a key named foo") + } + + if ks.Has("nonexistingkey") { + t.Fatal("should know it doesn't have a key named nonexistingkey") + } + if err := ks.Delete("bar"); err != nil { t.Fatal(err) } diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index ae45ecf21..0018ade4d 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -10,6 +10,11 @@ func NewMemKeystore() *MemKeystore { return &MemKeystore{make(map[string]ci.PrivKey)} } +func (mk *MemKeystore) Has(name string) bool { + _, ok := mk.keys[name] + return ok +} + func (mk *MemKeystore) Put(name string, k ci.PrivKey) error { if err := validateName(name); err != nil { return err diff --git a/keystore/memkeystore_test.go b/keystore/memkeystore_test.go index 7f4362795..913f14b83 100644 --- a/keystore/memkeystore_test.go +++ b/keystore/memkeystore_test.go @@ -46,6 +46,15 @@ func TestMemKeyStoreBasics(t *testing.T) { if err == nil { t.Fatal("should not be able to overwrite key") } + + if !ks.Has("foo") { + t.Fatal("should know it has a key named foo") + } + + if ks.Has("nonexistingkey") { + t.Fatal("should know it doesn't have a key named nonexistingkey") + } + if err := ks.Delete("bar"); err != nil { t.Fatal(err) } From bec127b3681497b8aeda41b78c6fa46f6570b7c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Mon, 8 May 2017 17:00:00 +0900 Subject: [PATCH 1832/3817] Future-proof keystore.Has by returning an error as well MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Michael Muré This commit was moved from ipfs/go-ipfs-keystore@c06823879c5e8984c570ca5abfce88ee3a81d4b2 --- keystore/keystore.go | 14 +++++++++++--- keystore/keystore_test.go | 12 ++++++++++-- keystore/memkeystore.go | 4 ++-- keystore/memkeystore_test.go | 12 ++++++++++-- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index de0b62dc6..e38211480 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -11,7 +11,7 @@ import ( ) type Keystore interface { - Has(string) bool + Has(string) (bool, error) Put(string, ci.PrivKey) error Get(string) (ci.PrivKey, error) Delete(string) error @@ -55,12 +55,20 @@ func NewFSKeystore(dir string) (*FSKeystore, error) { return &FSKeystore{dir}, nil } -func (ks *FSKeystore) Has(name string) bool { +func (ks *FSKeystore) Has(name string) (bool, error) { kp := filepath.Join(ks.dir, name) _, err := os.Stat(kp) - return err == nil + if os.IsNotExist(err) { + return false, nil + } + + if err != nil { + return false, err + } + + return true, nil } func (ks *FSKeystore) Put(name string, k ci.PrivKey) error { diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index 505d9119d..53c30b0d7 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -82,13 +82,21 @@ func TestKeystoreBasics(t *testing.T) { t.Fatal(err) } - if !ks.Has("foo") { + exist, err := ks.Has("foo") + if !exist { t.Fatal("should know it has a key named foo") } + if err != nil { + t.Fatal(err) + } - if ks.Has("nonexistingkey") { + exist, err = ks.Has("nonexistingkey") + if exist { t.Fatal("should know it doesn't have a key named nonexistingkey") } + if err != nil { + t.Fatal(err) + } if err := ks.Delete("bar"); err != nil { t.Fatal(err) diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index 0018ade4d..626ad8bc0 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -10,9 +10,9 @@ func NewMemKeystore() *MemKeystore { return &MemKeystore{make(map[string]ci.PrivKey)} } -func (mk *MemKeystore) Has(name string) bool { +func (mk *MemKeystore) Has(name string) (bool, error) { _, ok := mk.keys[name] - return ok + return ok, nil } func (mk *MemKeystore) Put(name string, k ci.PrivKey) error { diff --git a/keystore/memkeystore_test.go b/keystore/memkeystore_test.go index 913f14b83..62533d54b 100644 --- a/keystore/memkeystore_test.go +++ b/keystore/memkeystore_test.go @@ -47,13 +47,21 @@ func TestMemKeyStoreBasics(t *testing.T) { t.Fatal("should not be able to overwrite key") } - if !ks.Has("foo") { + exist, err := ks.Has("foo") + if !exist { t.Fatal("should know it has a key named foo") } + if err != nil { + t.Fatal(err) + } - if ks.Has("nonexistingkey") { + exist, err = ks.Has("nonexistingkey") + if exist { t.Fatal("should know it doesn't have a key named nonexistingkey") } + if err != nil { + t.Fatal(err) + } if err := ks.Delete("bar"); err != nil { t.Fatal(err) From fe31ce163b1aa79a3d6d508f7991481550acbf5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Sun, 14 May 2017 21:02:01 +0900 Subject: [PATCH 1833/3817] Document exported symbols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Michael Muré This commit was moved from ipfs/go-ipfs-keystore@b6968f6a708cf767b2f981b2d2ca32826b68797e --- keystore/keystore.go | 10 ++++++++++ keystore/memkeystore.go | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/keystore/keystore.go b/keystore/keystore.go index e38211480..8424dbafa 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -11,10 +11,15 @@ import ( ) type Keystore interface { + // Has return whether or not a key exist in the Keystore Has(string) (bool, error) + // Put store a key in the Keystore Put(string, ci.PrivKey) error + // Get retrieve a key from the Keystore Get(string) (ci.PrivKey, error) + // Delete remove a key from the Keystore Delete(string) error + // List return a list of key identifier List() ([]string, error) } @@ -55,6 +60,7 @@ func NewFSKeystore(dir string) (*FSKeystore, error) { return &FSKeystore{dir}, nil } +// Has return whether or not a key exist in the Keystore func (ks *FSKeystore) Has(name string) (bool, error) { kp := filepath.Join(ks.dir, name) @@ -71,6 +77,7 @@ func (ks *FSKeystore) Has(name string) (bool, error) { return true, nil } +// Put store a key in the Keystore func (ks *FSKeystore) Put(name string, k ci.PrivKey) error { if err := validateName(name); err != nil { return err @@ -104,6 +111,7 @@ func (ks *FSKeystore) Put(name string, k ci.PrivKey) error { return nil } +// Get retrieve a key from the Keystore func (ks *FSKeystore) Get(name string) (ci.PrivKey, error) { if err := validateName(name); err != nil { return nil, err @@ -122,6 +130,7 @@ func (ks *FSKeystore) Get(name string) (ci.PrivKey, error) { return ci.UnmarshalPrivateKey(data) } +// Delete remove a key from the Keystore func (ks *FSKeystore) Delete(name string) error { if err := validateName(name); err != nil { return err @@ -132,6 +141,7 @@ func (ks *FSKeystore) Delete(name string) error { return os.Remove(kp) } +// List return a list of key identifier func (ks *FSKeystore) List() ([]string, error) { dir, err := os.Open(ks.dir) if err != nil { diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index 626ad8bc0..068c5e189 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -10,11 +10,13 @@ func NewMemKeystore() *MemKeystore { return &MemKeystore{make(map[string]ci.PrivKey)} } +// Has return whether or not a key exist in the Keystore func (mk *MemKeystore) Has(name string) (bool, error) { _, ok := mk.keys[name] return ok, nil } +// Put store a key in the Keystore func (mk *MemKeystore) Put(name string, k ci.PrivKey) error { if err := validateName(name); err != nil { return err @@ -29,6 +31,7 @@ func (mk *MemKeystore) Put(name string, k ci.PrivKey) error { return nil } +// Get retrieve a key from the Keystore func (mk *MemKeystore) Get(name string) (ci.PrivKey, error) { if err := validateName(name); err != nil { return nil, err @@ -42,6 +45,7 @@ func (mk *MemKeystore) Get(name string) (ci.PrivKey, error) { return k, nil } +// Delete remove a key from the Keystore func (mk *MemKeystore) Delete(name string) error { if err := validateName(name); err != nil { return err @@ -51,6 +55,7 @@ func (mk *MemKeystore) Delete(name string) error { return nil } +// List return a list of key identifier func (mk *MemKeystore) List() ([]string, error) { out := make([]string, 0, len(mk.keys)) for k, _ := range mk.keys { From 34edf7ffe96e157c6bfc5ef038ba455ee3aed73b Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sat, 13 May 2017 00:35:22 -0400 Subject: [PATCH 1834/3817] Add support for using an alternative hash function with raw nodes. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@82bf38e829267377d0c6939a308d627ccfdedc42 --- ipld/merkledag/merkledag.go | 2 +- ipld/merkledag/raw.go | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index e0dc5c74a..5509bcaeb 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -116,7 +116,7 @@ func decodeBlock(b blocks.Block) (node.Node, error) { decnd.Prefix = b.Cid().Prefix() return decnd, nil case cid.Raw: - return NewRawNode(b.RawData()), nil + return NewRawNodeWPrefix(b.RawData(), b.Cid().Prefix()) case cid.DagCBOR: return ipldcbor.Decode(b.RawData()) default: diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 30fe639af..7c5ba56af 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -12,6 +12,8 @@ type RawNode struct { blocks.Block } +// NewRawNode creates a RawNode using the default sha2-256 hash +// funcition. func NewRawNode(data []byte) *RawNode { h := u.Hash(data) c := cid.NewCidV1(cid.Raw, h) @@ -20,6 +22,24 @@ func NewRawNode(data []byte) *RawNode { return &RawNode{blk} } +// NewRawNodeWPrefix creates a RawNode with the hash function +// specified in prefix. +func NewRawNodeWPrefix(data []byte, prefix cid.Prefix) (*RawNode, error) { + prefix.Codec = cid.Raw + if prefix.Version == 0 { + prefix.Version = 1 + } + c, err := prefix.Sum(data) + if err != nil { + return nil, err + } + blk, err := blocks.NewBlockWithCid(data, c) + if err != nil { + return nil, err + } + return &RawNode{blk}, nil +} + func (rn *RawNode) Links() []*node.Link { return nil } From d907387b204a5c9aa7159c23c7bd73a931b23ed9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 30 Mar 2017 18:20:36 -0700 Subject: [PATCH 1835/3817] implement ipfs pin update License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@55069e8291e5dea4d1d5b0ab2f505fbb24f1d74d --- ipld/merkledag/merkledag.go | 2 +- ipld/merkledag/utils/diff.go | 8 +- ipld/merkledag/utils/diffenum.go | 75 +++++++++++++ ipld/merkledag/utils/diffenum_test.go | 149 ++++++++++++++++++++++++++ 4 files changed, 230 insertions(+), 4 deletions(-) create mode 100644 ipld/merkledag/utils/diffenum.go create mode 100644 ipld/merkledag/utils/diffenum_test.go diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 5509bcaeb..b7a6ccf15 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -153,7 +153,7 @@ func (n *dagService) Remove(nd node.Node) error { // GetLinksDirect creates a function to get the links for a node, from // the node, bypassing the LinkService. If the node does not exist // locally (and can not be retrieved) an error will be returned. -func GetLinksDirect(serv DAGService) GetLinks { +func GetLinksDirect(serv node.NodeGetter) GetLinks { return func(ctx context.Context, c *cid.Cid) ([]*node.Link, error) { node, err := serv.Get(ctx, c) if err != nil { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index d7e5462ec..8605c470c 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -1,12 +1,13 @@ package dagutils import ( + "context" "fmt" "path" dag "github.com/ipfs/go-ipfs/merkledag" - context "context" + node "github.com/ipfs/go-ipld-node" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) @@ -87,7 +88,8 @@ func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.ProtoNode, cs [ return e.Finalize(ds) } -func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.ProtoNode) ([]*Change, error) { +// Diff returns a set of changes that transform node 'a' into node 'b' +func Diff(ctx context.Context, ds dag.DAGService, a, b node.Node) ([]*Change, error) { if len(a.Links()) == 0 && len(b.Links()) == 0 { return []*Change{ &Change{ @@ -104,7 +106,7 @@ func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.ProtoNode) ([]*Chang // strip out unchanged stuff for _, lnk := range a.Links() { - l, err := b.GetNodeLink(lnk.Name) + l, _, err := b.ResolveLink([]string{lnk.Name}) if err == nil { if l.Cid.Equals(lnk.Cid) { // no change... ignore it diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go new file mode 100644 index 000000000..19cb0e117 --- /dev/null +++ b/ipld/merkledag/utils/diffenum.go @@ -0,0 +1,75 @@ +package dagutils + +import ( + "context" + "fmt" + + mdag "github.com/ipfs/go-ipfs/merkledag" + + node "github.com/ipfs/go-ipld-node" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" +) + +// DiffEnumerate fetches every object in the graph pointed to by 'to' that is +// not in 'from'. This can be used to more efficiently fetch a graph if you can +// guarantee you already have the entirety of 'from' +func DiffEnumerate(ctx context.Context, dserv node.NodeGetter, from, to *cid.Cid) error { + fnd, err := dserv.Get(ctx, from) + if err != nil { + return fmt.Errorf("get %s: %s", from, err) + } + + tnd, err := dserv.Get(ctx, to) + if err != nil { + return fmt.Errorf("get %s: %s", to, err) + } + + diff := getLinkDiff(fnd, tnd) + + sset := cid.NewSet() + for _, c := range diff { + if c.a == nil { + err := mdag.EnumerateChildrenAsync(ctx, mdag.GetLinksDirect(dserv), c.b, sset.Visit) + if err != nil { + return err + } + } else { + err := DiffEnumerate(ctx, dserv, c.a, c.b) + if err != nil { + return err + } + } + } + + return nil +} + +type diffpair struct { + a, b *cid.Cid +} + +func getLinkDiff(a, b node.Node) []diffpair { + have := make(map[string]*node.Link) + names := make(map[string]*node.Link) + for _, l := range a.Links() { + have[l.Cid.KeyString()] = l + names[l.Name] = l + } + + var out []diffpair + + for _, l := range b.Links() { + if have[l.Cid.KeyString()] != nil { + continue + } + + match, ok := names[l.Name] + if !ok { + out = append(out, diffpair{b: l.Cid}) + continue + } + + out = append(out, diffpair{a: match.Cid, b: l.Cid}) + } + return out +} diff --git a/ipld/merkledag/utils/diffenum_test.go b/ipld/merkledag/utils/diffenum_test.go new file mode 100644 index 000000000..ed94b0dda --- /dev/null +++ b/ipld/merkledag/utils/diffenum_test.go @@ -0,0 +1,149 @@ +package dagutils + +import ( + "context" + "fmt" + "testing" + + dag "github.com/ipfs/go-ipfs/merkledag" + mdtest "github.com/ipfs/go-ipfs/merkledag/test" + + node "github.com/ipfs/go-ipld-node" + cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" +) + +func buildNode(name string, desc map[string]ndesc, out map[string]node.Node) node.Node { + this := desc[name] + nd := new(dag.ProtoNode) + nd.SetData([]byte(name)) + for k, v := range this { + child, ok := out[v] + if !ok { + child = buildNode(v, desc, out) + out[v] = child + } + + if err := nd.AddNodeLink(k, child); err != nil { + panic(err) + } + } + + return nd +} + +type ndesc map[string]string + +func mkGraph(desc map[string]ndesc) map[string]node.Node { + out := make(map[string]node.Node) + for name := range desc { + if _, ok := out[name]; ok { + continue + } + + out[name] = buildNode(name, desc, out) + } + return out +} + +var tg1 = map[string]ndesc{ + "a1": ndesc{ + "foo": "b", + }, + "b": ndesc{}, + "a2": ndesc{ + "foo": "b", + "bar": "c", + }, + "c": ndesc{}, +} + +var tg2 = map[string]ndesc{ + "a1": ndesc{ + "foo": "b", + }, + "b": ndesc{}, + "a2": ndesc{ + "foo": "b", + "bar": "c", + }, + "c": ndesc{"baz": "d"}, + "d": ndesc{}, +} + +func TestDiffEnumBasic(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nds := mkGraph(tg1) + + ds := mdtest.Mock() + lgds := &getLogger{ds: ds} + + for _, nd := range nds { + _, err := ds.Add(nd) + if err != nil { + t.Fatal(err) + } + } + + err := DiffEnumerate(ctx, lgds, nds["a1"].Cid(), nds["a2"].Cid()) + if err != nil { + t.Fatal(err) + } + + err = assertCidList(lgds.log, []*cid.Cid{nds["a1"].Cid(), nds["a2"].Cid(), nds["c"].Cid()}) + if err != nil { + t.Fatal(err) + } +} + +type getLogger struct { + ds node.NodeGetter + log []*cid.Cid +} + +func (gl *getLogger) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { + nd, err := gl.ds.Get(ctx, c) + if err != nil { + return nil, err + } + gl.log = append(gl.log, c) + return nd, nil +} + +func assertCidList(a, b []*cid.Cid) error { + if len(a) != len(b) { + return fmt.Errorf("got different number of cids than expected") + } + for i, c := range a { + if !c.Equals(b[i]) { + return fmt.Errorf("expected %s, got %s", c, b[i]) + } + } + return nil +} +func TestDiffEnumFail(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nds := mkGraph(tg2) + + ds := mdtest.Mock() + lgds := &getLogger{ds: ds} + + for _, s := range []string{"a1", "a2", "b", "c"} { + _, err := ds.Add(nds[s]) + if err != nil { + t.Fatal(err) + } + } + + err := DiffEnumerate(ctx, lgds, nds["a1"].Cid(), nds["a2"].Cid()) + if err != dag.ErrNotFound { + t.Fatal("expected err not found") + } + + err = assertCidList(lgds.log, []*cid.Cid{nds["a1"].Cid(), nds["a2"].Cid(), nds["c"].Cid()}) + if err != nil { + t.Fatal(err) + } + +} From 5fb41accb3314057040d489152177bca42396154 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 1 Apr 2017 10:04:41 -0700 Subject: [PATCH 1836/3817] comments and optimize potential rebalances License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@73c413e34fa43d9eca0f2cc8f5dd030d6d259b72 --- ipld/merkledag/utils/diffenum.go | 26 +++++++++++++---- ipld/merkledag/utils/diffenum_test.go | 41 +++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go index 19cb0e117..bc7e09321 100644 --- a/ipld/merkledag/utils/diffenum.go +++ b/ipld/merkledag/utils/diffenum.go @@ -28,13 +28,24 @@ func DiffEnumerate(ctx context.Context, dserv node.NodeGetter, from, to *cid.Cid sset := cid.NewSet() for _, c := range diff { - if c.a == nil { - err := mdag.EnumerateChildrenAsync(ctx, mdag.GetLinksDirect(dserv), c.b, sset.Visit) + // Since we're already assuming we have everything in the 'from' graph, + // add all those cids to our 'already seen' set to avoid potentially + // enumerating them later + if c.bef != nil { + sset.Add(c.bef) + } + } + for _, c := range diff { + if c.bef == nil { + if sset.Has(c.aft) { + continue + } + err := mdag.EnumerateChildrenAsync(ctx, mdag.GetLinksDirect(dserv), c.aft, sset.Visit) if err != nil { return err } } else { - err := DiffEnumerate(ctx, dserv, c.a, c.b) + err := DiffEnumerate(ctx, dserv, c.bef, c.aft) if err != nil { return err } @@ -45,9 +56,12 @@ func DiffEnumerate(ctx context.Context, dserv node.NodeGetter, from, to *cid.Cid } type diffpair struct { - a, b *cid.Cid + bef, aft *cid.Cid } +// getLinkDiff returns a changset (minimum edit distance style) between nodes +// 'a' and 'b'. Currently does not log deletions as our usecase doesnt call for +// this. func getLinkDiff(a, b node.Node) []diffpair { have := make(map[string]*node.Link) names := make(map[string]*node.Link) @@ -65,11 +79,11 @@ func getLinkDiff(a, b node.Node) []diffpair { match, ok := names[l.Name] if !ok { - out = append(out, diffpair{b: l.Cid}) + out = append(out, diffpair{aft: l.Cid}) continue } - out = append(out, diffpair{a: match.Cid, b: l.Cid}) + out = append(out, diffpair{bef: match.Cid, aft: l.Cid}) } return out } diff --git a/ipld/merkledag/utils/diffenum_test.go b/ipld/merkledag/utils/diffenum_test.go index ed94b0dda..686fdc35b 100644 --- a/ipld/merkledag/utils/diffenum_test.go +++ b/ipld/merkledag/utils/diffenum_test.go @@ -70,6 +70,20 @@ var tg2 = map[string]ndesc{ "d": ndesc{}, } +var tg3 = map[string]ndesc{ + "a1": ndesc{ + "foo": "b", + "bar": "c", + }, + "b": ndesc{}, + "a2": ndesc{ + "foo": "b", + "bar": "d", + }, + "c": ndesc{}, + "d": ndesc{}, +} + func TestDiffEnumBasic(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -121,6 +135,7 @@ func assertCidList(a, b []*cid.Cid) error { } return nil } + func TestDiffEnumFail(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -147,3 +162,29 @@ func TestDiffEnumFail(t *testing.T) { } } + +func TestDiffEnumRecurse(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nds := mkGraph(tg3) + + ds := mdtest.Mock() + lgds := &getLogger{ds: ds} + + for _, s := range []string{"a1", "a2", "b", "c", "d"} { + _, err := ds.Add(nds[s]) + if err != nil { + t.Fatal(err) + } + } + + err := DiffEnumerate(ctx, lgds, nds["a1"].Cid(), nds["a2"].Cid()) + if err != nil { + t.Fatal(err) + } + + err = assertCidList(lgds.log, []*cid.Cid{nds["a1"].Cid(), nds["a2"].Cid(), nds["c"].Cid(), nds["d"].Cid()}) + if err != nil { + t.Fatal(err) + } +} From e73ce7eb0abca2c71eb4d3aef7b7a24a03827257 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 30 Mar 2017 18:20:36 -0700 Subject: [PATCH 1837/3817] implement ipfs pin update License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@a8df3a22a07b5f6e47fac3b7f74a894632a843ad --- pinning/pinner/pin.go | 27 +++++++++++++++++++++++++++ pinning/pinner/pin_test.go | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 8c742d1c0..8de1780f0 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,6 +10,7 @@ import ( "time" mdag "github.com/ipfs/go-ipfs/merkledag" + dutils "github.com/ipfs/go-ipfs/merkledag/utils" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" @@ -86,6 +87,11 @@ type Pinner interface { Pin(context.Context, node.Node, bool) error Unpin(context.Context, *cid.Cid, bool) error + // Update updates a recursive pin from one cid to another + // this is more efficient than simply pinning the new one and unpinning the + // old one + Update(context.Context, *cid.Cid, *cid.Cid, bool) error + // Check if a set of keys are pinned, more efficient than // calling IsPinned for each key CheckIfPinned(cids ...*cid.Cid) ([]Pinned, error) @@ -94,6 +100,7 @@ type Pinner interface { // care! If used improperly, garbage collection may not be // successful. PinWithMode(*cid.Cid, PinMode) + // RemovePinWithMode is for manually editing the pin structure. // Use with care! If used improperly, garbage collection may not // be successful. @@ -447,6 +454,26 @@ func (p *pinner) RecursiveKeys() []*cid.Cid { return p.recursePin.Keys() } +func (p *pinner) Update(ctx context.Context, from, to *cid.Cid, unpin bool) error { + p.lock.Lock() + defer p.lock.Unlock() + + if !p.recursePin.Has(from) { + return fmt.Errorf("'from' cid was not recursively pinned already") + } + + err := dutils.DiffEnumerate(ctx, p.dserv, from, to) + if err != nil { + return err + } + + p.recursePin.Add(to) + if unpin { + p.recursePin.Remove(from) + } + return nil +} + // Flush encodes and writes pinner keysets to the datastore func (p *pinner) Flush() error { p.lock.Lock() diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index cbf89c601..bb90ea089 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -1,6 +1,7 @@ package pin import ( + "context" "testing" "time" @@ -9,7 +10,6 @@ import ( "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" - context "context" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" @@ -367,3 +367,36 @@ func TestPinRecursiveFail(t *testing.T) { t.Fatal(err) } } + +func TestPinUpdate(t *testing.T) { + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv := bs.New(bstore, offline.Exchange(bstore)) + + dserv := mdag.NewDAGService(bserv) + p := NewPinner(dstore, dserv, dserv) + n1, c1 := randNode() + n2, c2 := randNode() + + dserv.Add(n1) + dserv.Add(n2) + + ctx := context.Background() + if err := p.Pin(ctx, n1, true); err != nil { + t.Fatal(err) + } + + if err := p.Update(ctx, c1, c2, true); err != nil { + t.Fatal(err) + } + + assertPinned(t, p, c2, "c2 should be pinned now") + assertUnpinned(t, p, c1, "c1 should no longer be pinned") + + if err := p.Update(ctx, c2, c1, false); err != nil { + t.Fatal(err) + } + + assertPinned(t, p, c2, "c2 should be pinned still") + assertPinned(t, p, c1, "c1 should be pinned now") +} From d6d839fe10cb877d8c7edb56affd0c648a44d4d3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 12 May 2017 22:04:12 -0700 Subject: [PATCH 1838/3817] address code review, add comments License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@9947834f372cd08bedc67e637b9aa61dbe34acef --- ipld/merkledag/utils/diff.go | 2 +- ipld/merkledag/utils/diffenum.go | 10 ++++++---- ipld/merkledag/utils/diffenum_test.go | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 8605c470c..87b27d20c 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -7,8 +7,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - node "github.com/ipfs/go-ipld-node" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) const ( diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go index bc7e09321..28229a4fa 100644 --- a/ipld/merkledag/utils/diffenum.go +++ b/ipld/merkledag/utils/diffenum.go @@ -6,8 +6,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" - node "github.com/ipfs/go-ipld-node" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) // DiffEnumerate fetches every object in the graph pointed to by 'to' that is @@ -55,13 +55,15 @@ func DiffEnumerate(ctx context.Context, dserv node.NodeGetter, from, to *cid.Cid return nil } +// if both bef and aft are not nil, then that signifies bef was replaces with aft. +// if bef is nil and aft is not, that means aft was newly added +// if aft is nil and bef is not, that means bef was deleted type diffpair struct { bef, aft *cid.Cid } -// getLinkDiff returns a changset (minimum edit distance style) between nodes -// 'a' and 'b'. Currently does not log deletions as our usecase doesnt call for -// this. +// getLinkDiff returns a changeset between nodes 'a' and 'b'. Currently does +// not log deletions as our usecase doesnt call for this. func getLinkDiff(a, b node.Node) []diffpair { have := make(map[string]*node.Link) names := make(map[string]*node.Link) diff --git a/ipld/merkledag/utils/diffenum_test.go b/ipld/merkledag/utils/diffenum_test.go index 686fdc35b..45a0e178b 100644 --- a/ipld/merkledag/utils/diffenum_test.go +++ b/ipld/merkledag/utils/diffenum_test.go @@ -8,8 +8,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - node "github.com/ipfs/go-ipld-node" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) func buildNode(name string, desc map[string]ndesc, out map[string]node.Node) node.Node { From 1d3d2f1fc46355f9c7a44e27ba129f169afacc07 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 12 May 2017 22:04:12 -0700 Subject: [PATCH 1839/3817] address code review, add comments License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@3abaca0a752208350935b1d16e8968aebfa724b7 --- pinning/pinner/pin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 8de1780f0..482d070fd 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -90,7 +90,7 @@ type Pinner interface { // Update updates a recursive pin from one cid to another // this is more efficient than simply pinning the new one and unpinning the // old one - Update(context.Context, *cid.Cid, *cid.Cid, bool) error + Update(ctx context.Context, from, to *cid.Cid, unpin bool) error // Check if a set of keys are pinned, more efficient than // calling IsPinned for each key From 5e4bf09f78d1643461b65bbbee9e7f67c97abc1e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 16 May 2017 19:35:43 -0700 Subject: [PATCH 1840/3817] update to dht code with provide announce option License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@7dbb530a72f71f5d9f581c8a65b8c577fa2c42d3 --- namesys/namesys.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/routing.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 72055331c..9d3f1d8f6 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -9,7 +9,7 @@ import ( ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" + routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index fe4a03b08..82e95d196 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -19,8 +19,8 @@ import ( record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" dhtpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index fddbc2ea4..9ac7b2968 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -17,8 +17,8 @@ import ( gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" recpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" + routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/namesys/routing.go b/namesys/routing.go index 88983cedb..f87905386 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -14,9 +14,9 @@ import ( mh "gx/ipfs/QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw/go-multihash" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" ) var log = logging.Logger("namesys") From ed155421f451bdf779dcf7d5df77ae1b03692052 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 16 May 2017 19:35:43 -0700 Subject: [PATCH 1841/3817] update to dht code with provide announce option License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@4bd7c545f6d235e4d2228406ea029afeca3d6b26 --- routing/mock/centralized_client.go | 7 +++++-- routing/mock/centralized_test.go | 8 ++++---- routing/mock/dht.go | 2 +- routing/mock/interface.go | 2 +- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 4 ++-- routing/offline/offline_test.go | 2 +- routing/supernode/client.go | 12 +++++++++--- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 12 files changed, 29 insertions(+), 20 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 513a50cb3..2e59fa8d6 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -13,9 +13,9 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" dhtpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" ma "gx/ipfs/QmcyqRMCAXVtYPS4DiBrA7sezL9rRGfW8Ctx7cywL4TXJj/go-multiaddr" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) @@ -104,7 +104,10 @@ func (c *client) FindProvidersAsync(ctx context.Context, k *cid.Cid, max int) <- // Provide returns once the message is on the network. Value is not necessarily // visible yet. -func (c *client) Provide(_ context.Context, key *cid.Cid) error { +func (c *client) Provide(_ context.Context, key *cid.Cid, brd bool) error { + if !brd { + return nil + } info := pstore.PeerInfo{ ID: c.peer.ID(), Addrs: []ma.Multiaddr{c.peer.Address()}, diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index c7c6836d1..1f0850b4c 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -33,7 +33,7 @@ func TestClientFindProviders(t *testing.T) { client := rs.Client(pi) k := cid.NewCidV0(u.Hash([]byte("hello"))) - err := client.Provide(context.Background(), k) + err := client.Provide(context.Background(), k, true) if err != nil { t.Fatal(err) } @@ -60,7 +60,7 @@ func TestClientOverMax(t *testing.T) { numProvidersForHelloKey := 100 for i := 0; i < numProvidersForHelloKey; i++ { pi := testutil.RandIdentityOrFatal(t) - err := rs.Client(pi).Provide(context.Background(), k) + err := rs.Client(pi).Provide(context.Background(), k, true) if err != nil { t.Fatal(err) } @@ -106,7 +106,7 @@ func TestCanceledContext(t *testing.T) { if err != nil { t.Error(err) } - err = rs.Client(pi).Provide(context.Background(), k) + err = rs.Client(pi).Provide(context.Background(), k, true) if err != nil { t.Error(err) } @@ -151,7 +151,7 @@ func TestValidAfter(t *testing.T) { rs := NewServerWithDelay(conf) - rs.Client(pi).Provide(ctx, key) + rs.Client(pi).Provide(ctx, key, true) var providers []pstore.PeerInfo max := 100 diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 9a084d603..d5dde3388 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - dht "gx/ipfs/QmQcRLisUbREko56ThfgzdBorMGNfNjgqzvwuPPr1jFw6A/go-libp2p-kad-dht" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" mocknet "gx/ipfs/QmRai5yZNL67pWCoznW7sBdFnqZrFULuJ5w8KhmRyhdgN4/go-libp2p/p2p/net/mock" + dht "gx/ipfs/QmUJKdWyaf2dpuACw7ctu3KNciyzR7S69yGFr2BP6vYUB8/go-libp2p-kad-dht" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 77db71313..9983ccf82 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -11,7 +11,7 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" + routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 8e0a2a307..9e9dd90ea 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -8,8 +8,8 @@ import ( pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" p2phost "gx/ipfs/QmcyNeWPsoFGxThGpV8JnJdfUNankKhWCTrbrcFRQda4xR/go-libp2p-host" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) @@ -41,7 +41,7 @@ func (c *nilclient) FindProvidersAsync(_ context.Context, _ *cid.Cid, _ int) <-c return out } -func (c *nilclient) Provide(_ context.Context, _ *cid.Cid) error { +func (c *nilclient) Provide(_ context.Context, _ *cid.Cid, _ bool) error { return nil } diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 165af084a..c1abf7071 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -13,9 +13,9 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" + routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) @@ -101,7 +101,7 @@ func (c *offlineRouting) FindProvidersAsync(ctx context.Context, k *cid.Cid, max return out } -func (c *offlineRouting) Provide(_ context.Context, k *cid.Cid) error { +func (c *offlineRouting) Provide(_ context.Context, k *cid.Cid, _ bool) error { return ErrOffline } diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index 629206b4e..a847a2814 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -67,7 +67,7 @@ func TestOfflineRouterLocal(t *testing.T) { } cid, _ = testutil.RandCidV0() - err = offline.Provide(ctx, cid) + err = offline.Provide(ctx, cid, true) if err != ErrOffline { t.Fatal("OfflineRouting should alert that its offline") } diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 1c2fe866a..2487ffe22 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -9,13 +9,13 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" - dhtpb "gx/ipfs/QmQcRLisUbREko56ThfgzdBorMGNfNjgqzvwuPPr1jFw6A/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + dhtpb "gx/ipfs/QmUJKdWyaf2dpuACw7ctu3KNciyzR7S69yGFr2BP6vYUB8/go-libp2p-kad-dht/pb" loggables "gx/ipfs/QmVesPmqbPp7xRGyY96tnBwzDtVV1nqv4SCVxo5zCqKyH8/go-libp2p-loggables" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" + routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmafuecpeZp3k3sHJ5mUARHd4795revuadECQMkmHB8LfW/go-libp2p-routing" "gx/ipfs/QmcyNeWPsoFGxThGpV8JnJdfUNankKhWCTrbrcFRQda4xR/go-libp2p-host" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) @@ -100,7 +100,13 @@ func (c *Client) GetValues(ctx context.Context, k string, _ int) ([]routing.Recv }, nil } -func (c *Client) Provide(ctx context.Context, k *cid.Cid) error { +// Provide adds the given key 'k' to the content routing system. If 'brd' is +// true, it announces that content to the network. For the supernode client, +// setting 'brd' to false makes this call a no-op +func (c *Client) Provide(ctx context.Context, k *cid.Cid, brd bool) error { + if !brd { + return nil + } defer log.EventBegin(ctx, "provide", k).Done() msg := dhtpb.NewMessage(dhtpb.Message_ADD_PROVIDER, k.KeyString(), 0) // FIXME how is connectedness defined for the local node diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 02014cef6..adff6b4d8 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,7 +2,7 @@ package proxy import ( context "context" - dhtpb "gx/ipfs/QmQcRLisUbREko56ThfgzdBorMGNfNjgqzvwuPPr1jFw6A/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmUJKdWyaf2dpuACw7ctu3KNciyzR7S69yGFr2BP6vYUB8/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmVHSBsn8LEeay8m5ERebgUVuhzw838PsyTttCmP6GMJkg/go-libp2p-net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 8cbffe2b3..ba9c41aea 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -5,8 +5,8 @@ import ( "errors" pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" - dhtpb "gx/ipfs/QmQcRLisUbREko56ThfgzdBorMGNfNjgqzvwuPPr1jFw6A/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + dhtpb "gx/ipfs/QmUJKdWyaf2dpuACw7ctu3KNciyzR7S69yGFr2BP6vYUB8/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmVHSBsn8LEeay8m5ERebgUVuhzw838PsyTttCmP6GMJkg/go-libp2p-net" loggables "gx/ipfs/QmVesPmqbPp7xRGyY96tnBwzDtVV1nqv4SCVxo5zCqKyH8/go-libp2p-loggables" kbucket "gx/ipfs/QmXKSwZVoHCTne4jTLzDtMc2K6paEZ2QaUMQfJ4ogYd28n/go-libp2p-kbucket" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 1110c4320..e10440615 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -9,8 +9,8 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" - dhtpb "gx/ipfs/QmQcRLisUbREko56ThfgzdBorMGNfNjgqzvwuPPr1jFw6A/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dhtpb "gx/ipfs/QmUJKdWyaf2dpuACw7ctu3KNciyzR7S69yGFr2BP6vYUB8/go-libp2p-kad-dht/pb" record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index a06e29e76..d95c3684d 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmQcRLisUbREko56ThfgzdBorMGNfNjgqzvwuPPr1jFw6A/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + dhtpb "gx/ipfs/QmUJKdWyaf2dpuACw7ctu3KNciyzR7S69yGFr2BP6vYUB8/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From ab1d1c5e585cc1654b36a6d7b255b77771730002 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 23 May 2017 17:40:20 -0400 Subject: [PATCH 1842/3817] filestore: add "--file-order" option to "filestore ls" and "verify" License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@fed25f2030fa159f77fd4fb4b999ca0bce8dd56d --- filestore/util.go | 98 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 2 deletions(-) diff --git a/filestore/util.go b/filestore/util.go index 6dd6cf1c2..9a13de39a 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -2,6 +2,7 @@ package filestore import ( "fmt" + "sort" "github.com/ipfs/go-ipfs/blocks/blockstore" pb "github.com/ipfs/go-ipfs/filestore/pb" @@ -89,7 +90,10 @@ func List(fs *Filestore, key *cid.Cid) *ListRes { // one by one each block in the Filestore's FileManager. // ListAll does not verify that the references are valid or whether // the raw data is accessible. See VerifyAll(). -func ListAll(fs *Filestore) (func() *ListRes, error) { +func ListAll(fs *Filestore, fileOrder bool) (func() *ListRes, error) { + if fileOrder { + return listAllFileOrder(fs, false) + } return listAll(fs, false) } @@ -105,7 +109,10 @@ func Verify(fs *Filestore, key *cid.Cid) *ListRes { // returns one by one each block in the Filestore's FileManager. // VerifyAll checks that the reference is valid and that the block data // can be read. -func VerifyAll(fs *Filestore) (func() *ListRes, error) { +func VerifyAll(fs *Filestore, fileOrder bool) (func() *ListRes, error) { + if fileOrder { + return listAllFileOrder(fs, true) + } return listAll(fs, true) } @@ -158,6 +165,93 @@ func next(qr dsq.Results) (*cid.Cid, *pb.DataObj, error) { return c, dobj, nil } +func listAllFileOrder(fs *Filestore, verify bool) (func() *ListRes, error) { + q := dsq.Query{} + qr, err := fs.fm.ds.Query(q) + if err != nil { + return nil, err + } + + var entries listEntries + + for { + v, ok := qr.NextSync() + if !ok { + break + } + dobj, err := unmarshalDataObj(v.Value) + if err != nil { + entries = append(entries, &listEntry{ + dsKey: v.Key, + err: err, + }) + } else { + entries = append(entries, &listEntry{ + dsKey: v.Key, + filePath: dobj.GetFilePath(), + offset: dobj.GetOffset(), + size: dobj.GetSize_(), + }) + } + } + sort.Sort(entries) + + i := 0 + return func() *ListRes { + if i >= len(entries) { + return nil + } + v := entries[i] + i++ + // attempt to convert the datastore key to a CID, + // store the error but don't use it yet + cid, keyErr := dshelp.DsKeyToCid(ds.RawKey(v.dsKey)) + // first if they listRes already had an error return that error + if v.err != nil { + return mkListRes(cid, nil, v.err) + } + // now reconstruct the DataObj + dobj := pb.DataObj{ + FilePath: &v.filePath, + Offset: &v.offset, + Size_: &v.size, + } + // now if we could not convert the datastore key return that + // error + if keyErr != nil { + return mkListRes(cid, &dobj, keyErr) + } + // finally verify the dataobj if requested + var err error + if verify { + _, err = fs.fm.readDataObj(cid, &dobj) + } + return mkListRes(cid, &dobj, err) + }, nil +} + +type listEntry struct { + filePath string + offset uint64 + dsKey string + size uint64 + err error +} + +type listEntries []*listEntry + +func (l listEntries) Len() int { return len(l) } +func (l listEntries) Swap(i, j int) { l[i], l[j] = l[j], l[i] } +func (l listEntries) Less(i, j int) bool { + if l[i].filePath == l[j].filePath { + if l[i].offset == l[j].offset { + return l[i].dsKey < l[j].dsKey + } + return l[i].offset < l[j].offset + } + return l[i].filePath < l[j].filePath +} + func mkListRes(c *cid.Cid, d *pb.DataObj, err error) *ListRes { status := StatusOk errorMsg := "" From a4fdc8a17f6bc62fba0d32c978d28ff9350d8b0b Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Tue, 30 May 2017 02:26:05 +0200 Subject: [PATCH 1843/3817] gx: update go-libp2p-peerstore, go-libp2p, go-libp2p-kbucket License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/go-namesys@82a22e4768f1c8dbc1153b4727a0c737d5b0461b --- namesys/namesys.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 4 ++-- namesys/republisher/repub_test.go | 4 ++-- namesys/routing.go | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 9d3f1d8f6..1f662bd82 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,9 +7,9 @@ import ( path "github.com/ipfs/go-ipfs/path" + routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 82e95d196..6c64372f0 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -14,12 +14,12 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ft "github.com/ipfs/go-ipfs/unixfs" + routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" dhtpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 9ac7b2968..94ad4e8e0 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,13 +11,13 @@ import ( path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" + routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" recpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" - routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" + pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 10c7c55a7..a4810e8ba 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" - mocknet "gx/ipfs/QmRai5yZNL67pWCoznW7sBdFnqZrFULuJ5w8KhmRyhdgN4/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index f87905386..6b94876c6 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -9,12 +9,12 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" + routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" mh "gx/ipfs/QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw/go-multihash" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) From a519f3746c9722964fa51dca97b02286e970cd91 Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Tue, 30 May 2017 02:26:05 +0200 Subject: [PATCH 1844/3817] gx: update go-libp2p-peerstore, go-libp2p, go-libp2p-kbucket License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/go-ipfs-routing@379ef8665d1782ef1d4a042deea3e07c1bb9ec19 --- routing/mock/centralized_client.go | 4 ++-- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 4 ++-- routing/mock/interface.go | 2 +- routing/none/none_client.go | 6 +++--- routing/offline/offline.go | 4 ++-- routing/supernode/client.go | 8 ++++---- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 10 +++++----- routing/supernode/server.go | 4 ++-- routing/supernode/server_test.go | 2 +- 12 files changed, 26 insertions(+), 26 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 2e59fa8d6..3dad5d320 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,12 +8,12 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" "github.com/ipfs/go-ipfs/thirdparty/testutil" - pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" + routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" dhtpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" + pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ma "gx/ipfs/QmcyqRMCAXVtYPS4DiBrA7sezL9rRGfW8Ctx7cywL4TXJj/go-multiaddr" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 50844f9f7..4d824e673 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -8,9 +8,9 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" - pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 1f0850b4c..69b451ce3 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,8 +8,8 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index d5dde3388..b9ee9b5ba 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" + mocknet "gx/ipfs/QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime/go-libp2p/p2p/net/mock" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - mocknet "gx/ipfs/QmRai5yZNL67pWCoznW7sBdFnqZrFULuJ5w8KhmRyhdgN4/go-libp2p/p2p/net/mock" - dht "gx/ipfs/QmUJKdWyaf2dpuACw7ctu3KNciyzR7S69yGFr2BP6vYUB8/go-libp2p-kad-dht" + dht "gx/ipfs/QmRmroYSdievxnjiuy99C8BzShNstdEWcEF3LQHF7fUbez/go-libp2p-kad-dht" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 9983ccf82..4e9f3e8d5 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,8 +10,8 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" + routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 9e9dd90ea..35f3dedea 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,11 +6,11 @@ import ( repo "github.com/ipfs/go-ipfs/repo" - pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" + routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" + p2phost "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" + pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - p2phost "gx/ipfs/QmcyNeWPsoFGxThGpV8JnJdfUNankKhWCTrbrcFRQda4xR/go-libp2p-host" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index c1abf7071..8c91a1c43 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,13 +7,13 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" + routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" - routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" + pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 2487ffe22..47345cd82 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,15 +8,15 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" + routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" + dhtpb "gx/ipfs/QmRmroYSdievxnjiuy99C8BzShNstdEWcEF3LQHF7fUbez/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - dhtpb "gx/ipfs/QmUJKdWyaf2dpuACw7ctu3KNciyzR7S69yGFr2BP6vYUB8/go-libp2p-kad-dht/pb" + "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" loggables "gx/ipfs/QmVesPmqbPp7xRGyY96tnBwzDtVV1nqv4SCVxo5zCqKyH8/go-libp2p-loggables" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" - routing "gx/ipfs/QmXiH3yLocPhjkAmL8R29fKRcEKoVXKCaVDbAS9tdTrVEd/go-libp2p-routing" + pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - "gx/ipfs/QmcyNeWPsoFGxThGpV8JnJdfUNankKhWCTrbrcFRQda4xR/go-libp2p-host" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index adff6b4d8..d38cc4f43 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,8 +2,8 @@ package proxy import ( context "context" - dhtpb "gx/ipfs/QmUJKdWyaf2dpuACw7ctu3KNciyzR7S69yGFr2BP6vYUB8/go-libp2p-kad-dht/pb" - inet "gx/ipfs/QmVHSBsn8LEeay8m5ERebgUVuhzw838PsyTttCmP6GMJkg/go-libp2p-net" + dhtpb "gx/ipfs/QmRmroYSdievxnjiuy99C8BzShNstdEWcEF3LQHF7fUbez/go-libp2p-kad-dht/pb" + inet "gx/ipfs/QmRscs8KxrSmSv4iuevHv8JfuUzHBMoqiaHzxfDRiksd6e/go-libp2p-net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index ba9c41aea..da8160254 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,14 +4,14 @@ import ( "context" "errors" - pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" + dhtpb "gx/ipfs/QmRmroYSdievxnjiuy99C8BzShNstdEWcEF3LQHF7fUbez/go-libp2p-kad-dht/pb" + inet "gx/ipfs/QmRscs8KxrSmSv4iuevHv8JfuUzHBMoqiaHzxfDRiksd6e/go-libp2p-net" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - dhtpb "gx/ipfs/QmUJKdWyaf2dpuACw7ctu3KNciyzR7S69yGFr2BP6vYUB8/go-libp2p-kad-dht/pb" - inet "gx/ipfs/QmVHSBsn8LEeay8m5ERebgUVuhzw838PsyTttCmP6GMJkg/go-libp2p-net" + host "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" loggables "gx/ipfs/QmVesPmqbPp7xRGyY96tnBwzDtVV1nqv4SCVxo5zCqKyH8/go-libp2p-loggables" - kbucket "gx/ipfs/QmXKSwZVoHCTne4jTLzDtMc2K6paEZ2QaUMQfJ4ogYd28n/go-libp2p-kbucket" + pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - host "gx/ipfs/QmcyNeWPsoFGxThGpV8JnJdfUNankKhWCTrbrcFRQda4xR/go-libp2p-host" + kbucket "gx/ipfs/QmaQG6fJdzn2532WHoPdVwKqftXr6iCSr5NtWyGi1BHytT/go-libp2p-kbucket" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index e10440615..5d42231d0 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,11 +8,11 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - pstore "gx/ipfs/QmNUVzEjq3XWJ89hegahPvyfJbTXgTaom48pLb7YBD9gHQ/go-libp2p-peerstore" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dhtpb "gx/ipfs/QmUJKdWyaf2dpuACw7ctu3KNciyzR7S69yGFr2BP6vYUB8/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmRmroYSdievxnjiuy99C8BzShNstdEWcEF3LQHF7fUbez/go-libp2p-kad-dht/pb" record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" + pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index d95c3684d..925dad867 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -4,7 +4,7 @@ import ( "testing" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dhtpb "gx/ipfs/QmUJKdWyaf2dpuACw7ctu3KNciyzR7S69yGFr2BP6vYUB8/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmRmroYSdievxnjiuy99C8BzShNstdEWcEF3LQHF7fUbez/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 22e21c4acb24a7a27488d863789329071edd2f75 Mon Sep 17 00:00:00 2001 From: zramsay Date: Wed, 31 May 2017 16:56:11 -0400 Subject: [PATCH 1845/3817] apply the megacheck tool to improve code quality License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-unixfs@ee18aaa340af00f6cc37f09dfb979286b94c4823 --- unixfs/io/dagreader_test.go | 10 +++++----- unixfs/io/pbdagreader.go | 13 ++++++------- unixfs/mod/dagmodifier.go | 12 ++++-------- unixfs/mod/dagmodifier_test.go | 33 +++++++++++++++++++++------------ 4 files changed, 36 insertions(+), 32 deletions(-) diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index b57426e38..3ac82fc5f 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -2,8 +2,8 @@ package io import ( "bytes" + "io" "io/ioutil" - "os" "strings" "testing" @@ -54,7 +54,7 @@ func TestSeekAndRead(t *testing.T) { } for i := 255; i >= 0; i-- { - reader.Seek(int64(i), os.SEEK_SET) + reader.Seek(int64(i), io.SeekStart) if reader.Offset() != int64(i) { t.Fatal("expected offset to be increased by one after read") @@ -100,14 +100,14 @@ func TestRelativeSeek(t *testing.T) { t.Fatalf("expected to read: %d at %d, read %d", i, reader.Offset()-1, out) } if i != 255 { - _, err := reader.Seek(3, os.SEEK_CUR) + _, err := reader.Seek(3, io.SeekCurrent) if err != nil { t.Fatal(err) } } } - _, err = reader.Seek(4, os.SEEK_END) + _, err = reader.Seek(4, io.SeekEnd) if err != nil { t.Fatal(err) } @@ -120,7 +120,7 @@ func TestRelativeSeek(t *testing.T) { if int(out) != 255-i { t.Fatalf("expected to read: %d at %d, read %d", 255-i, reader.Offset()-1, out) } - reader.Seek(-5, os.SEEK_CUR) // seek 4 bytes but we read one byte every time so 5 bytes + reader.Seek(-5, io.SeekCurrent) // seek 4 bytes but we read one byte every time so 5 bytes } } diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index a5a53ffa2..0b75fd916 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "os" mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" @@ -185,7 +184,7 @@ func (dr *pbDagReader) Offset() int64 { // recreations that need to happen. func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) { switch whence { - case os.SEEK_SET: + case io.SeekStart: if offset < 0 { return -1, errors.New("Invalid offset") } @@ -226,7 +225,7 @@ func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) { } // set proper offset within child readseeker - n, err := dr.buf.Seek(left, os.SEEK_SET) + n, err := dr.buf.Seek(left, io.SeekStart) if err != nil { return -1, err } @@ -238,13 +237,13 @@ func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) { } dr.offset = offset return offset, nil - case os.SEEK_CUR: + case io.SeekCurrent: // TODO: be smarter here noffset := dr.offset + offset - return dr.Seek(noffset, os.SEEK_SET) - case os.SEEK_END: + return dr.Seek(noffset, io.SeekStart) + case io.SeekEnd: noffset := int64(dr.pbdata.GetFilesize()) - offset - return dr.Seek(noffset, os.SEEK_SET) + return dr.Seek(noffset, io.SeekStart) default: return 0, errors.New("invalid whence") } diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index c531caa15..090cdb593 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -5,7 +5,6 @@ import ( "context" "errors" "io" - "os" chunk "github.com/ipfs/go-ipfs/importer/chunk" help "github.com/ipfs/go-ipfs/importer/helpers" @@ -14,7 +13,6 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" @@ -26,8 +24,6 @@ var ErrUnrecognizedWhence = errors.New("unrecognized whence") // 2MB var writebufferSize = 1 << 21 -var log = logging.Logger("dagio") - // DagModifier is the only struct licensed and able to correctly // perform surgery on a DAG 'file' // Dear god, please rename this to something more pleasant @@ -340,7 +336,7 @@ func (dm *DagModifier) readPrep() error { return err } - i, err := dr.Seek(int64(dm.curWrOff), os.SEEK_SET) + i, err := dr.Seek(int64(dm.curWrOff), io.SeekStart) if err != nil { cancel() return err @@ -397,11 +393,11 @@ func (dm *DagModifier) Seek(offset int64, whence int) (int64, error) { var newoffset uint64 switch whence { - case os.SEEK_CUR: + case io.SeekCurrent: newoffset = dm.curWrOff + uint64(offset) - case os.SEEK_SET: + case io.SeekStart: newoffset = uint64(offset) - case os.SEEK_END: + case io.SeekEnd: newoffset = uint64(fisize) - uint64(offset) default: return 0, ErrUnrecognizedWhence diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index ecc9be644..192200dd2 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -2,8 +2,8 @@ package mod import ( "fmt" + "io" "io/ioutil" - "os" "testing" "github.com/ipfs/go-ipfs/blocks/blockstore" @@ -384,7 +384,7 @@ func TestDagTruncate(t *testing.T) { t.Fatal("size was incorrect!") } - _, err = dagmod.Seek(0, os.SEEK_SET) + _, err = dagmod.Seek(0, io.SeekStart) if err != nil { t.Fatal(err) } @@ -450,7 +450,7 @@ func TestSparseWrite(t *testing.T) { t.Fatal("incorrect write amount") } - _, err = dagmod.Seek(0, os.SEEK_SET) + _, err = dagmod.Seek(0, io.SeekStart) if err != nil { t.Fatal(err) } @@ -479,7 +479,7 @@ func TestSeekPastEndWrite(t *testing.T) { buf := make([]byte, 5000) u.NewTimeSeededRand().Read(buf[2500:]) - nseek, err := dagmod.Seek(2500, os.SEEK_SET) + nseek, err := dagmod.Seek(2500, io.SeekStart) if err != nil { t.Fatal(err) } @@ -497,7 +497,7 @@ func TestSeekPastEndWrite(t *testing.T) { t.Fatal("incorrect write amount") } - _, err = dagmod.Seek(0, os.SEEK_SET) + _, err = dagmod.Seek(0, io.SeekStart) if err != nil { t.Fatal(err) } @@ -525,7 +525,7 @@ func TestRelativeSeek(t *testing.T) { for i := 0; i < 64; i++ { dagmod.Write([]byte{byte(i)}) - if _, err := dagmod.Seek(1, os.SEEK_CUR); err != nil { + if _, err := dagmod.Seek(1, io.SeekCurrent); err != nil { t.Fatal(err) } } @@ -576,17 +576,26 @@ func TestEndSeek(t *testing.T) { t.Fatal(err) } - offset, err := dagmod.Seek(0, os.SEEK_CUR) + offset, err := dagmod.Seek(0, io.SeekCurrent) + if err != nil { + t.Fatal(err) + } if offset != 100 { t.Fatal("expected the relative seek 0 to return current location") } - offset, err = dagmod.Seek(0, os.SEEK_SET) + offset, err = dagmod.Seek(0, io.SeekStart) + if err != nil { + t.Fatal(err) + } if offset != 0 { t.Fatal("expected the absolute seek to set offset at 0") } - offset, err = dagmod.Seek(0, os.SEEK_END) + offset, err = dagmod.Seek(0, io.SeekEnd) + if err != nil { + t.Fatal(err) + } if offset != 100 { t.Fatal("expected the end seek to set offset at end") } @@ -612,7 +621,7 @@ func TestReadAndSeek(t *testing.T) { } readBuf := make([]byte, 4) - offset, err := dagmod.Seek(0, os.SEEK_SET) + offset, err := dagmod.Seek(0, io.SeekStart) if offset != 0 { t.Fatal("expected offset to be 0") } @@ -636,7 +645,7 @@ func TestReadAndSeek(t *testing.T) { } // skip 4 - _, err = dagmod.Seek(1, os.SEEK_CUR) + _, err = dagmod.Seek(1, io.SeekCurrent) if err != nil { t.Fatalf("error: %s, offset %d, reader offset %d", err, dagmod.curWrOff, dagmod.read.Offset()) } @@ -676,7 +685,7 @@ func TestCtxRead(t *testing.T) { if err != nil { t.Fatal(err) } - dagmod.Seek(0, os.SEEK_SET) + dagmod.Seek(0, io.SeekStart) readBuf := make([]byte, 4) _, err = dagmod.CtxReadFull(ctx, readBuf) From ebdd4281dd1fcfb3b92fa733defc5a68d00c35d4 Mon Sep 17 00:00:00 2001 From: zramsay Date: Wed, 31 May 2017 16:56:11 -0400 Subject: [PATCH 1846/3817] apply the megacheck tool to improve code quality License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-namesys@594573bdc1fe063bbe34b77da0b4682ba216f6a0 --- namesys/publisher.go | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 6c64372f0..4a8570c01 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -192,12 +192,7 @@ func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk // Store associated public key timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout) defer cancel() - err = r.PutValue(timectx, k, pkbytes) - if err != nil { - return err - } - - return nil + return r.PutValue(timectx, k, pkbytes) } func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec *pb.IpnsEntry) error { @@ -211,11 +206,7 @@ func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec log.Debugf("Storing ipns entry at: %s", ipnskey) // Store ipns entry at "/ipns/"+b58(h(pubkey)) - if err := r.PutValue(timectx, ipnskey, data); err != nil { - return err - } - - return nil + return r.PutValue(timectx, ipnskey, data) } func CreateRoutingEntryData(pk ci.PrivKey, val path.Path, seq uint64, eol time.Time) (*pb.IpnsEntry, error) { From bb86068b615a66a2adceb53e5e78f004d4d09262 Mon Sep 17 00:00:00 2001 From: zramsay Date: Wed, 31 May 2017 16:56:11 -0400 Subject: [PATCH 1847/3817] apply the megacheck tool to improve code quality License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-ipfs-blockstore@6ca4e115221e6d22bd14e2994103cddc8f8d41e7 --- blockstore/arc_cache_test.go | 2 +- blockstore/bloom_cache.go | 2 +- blockstore/bloom_cache_test.go | 7 +++++-- blockstore/caching_test.go | 13 ++++++++----- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 879042380..e6f35144d 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -30,7 +30,7 @@ func testArcCached(ctx context.Context, bs Blockstore) (*arccache, error) { func createStores(t *testing.T) (*arccache, Blockstore, *callbackDatastore) { cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} bs := NewBlockstore(syncds.MutexWrap(cd)) - arc, err := testArcCached(nil, bs) + arc, err := testArcCached(context.TODO(), bs) if err != nil { t.Fatal(err) } diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 47f5ac018..8bcf962fe 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -118,7 +118,7 @@ func (b *bloomcache) hasCached(k *cid.Cid) (has bool, ok bool) { } if b.BloomActive() { blr := b.bloom.HasTS(k.Bytes()) - if blr == false { // not contained in bloom is only conclusive answer bloom gives + if !blr { // not contained in bloom is only conclusive answer bloom gives b.hits.Inc() return false, true } diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index f021efd8e..85046e270 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -34,6 +34,9 @@ func TestPutManyAddsToBloom(t *testing.T) { defer cancel() cachedbs, err := testBloomCached(ctx, bs) + if err != nil { + t.Fatal(err) + } select { case <-cachedbs.rebuildChan: @@ -49,7 +52,7 @@ func TestPutManyAddsToBloom(t *testing.T) { if err != nil { t.Fatal(err) } - if has == false { + if !has { t.Fatal("added block is reported missing") } @@ -57,7 +60,7 @@ func TestPutManyAddsToBloom(t *testing.T) { if err != nil { t.Fatal(err) } - if has == true { + if has { t.Fatal("not added block is reported to be in blockstore") } } diff --git a/blockstore/caching_test.go b/blockstore/caching_test.go index 3c3c19546..16066ad18 100644 --- a/blockstore/caching_test.go +++ b/blockstore/caching_test.go @@ -1,26 +1,29 @@ package blockstore -import "testing" +import ( + "context" + "testing" +) func TestCachingOptsLessThanZero(t *testing.T) { opts := DefaultCacheOpts() opts.HasARCCacheSize = -1 - if _, err := CachedBlockstore(nil, nil, opts); err == nil { + if _, err := CachedBlockstore(context.TODO(), nil, opts); err == nil { t.Error("wrong ARC setting was not detected") } opts = DefaultCacheOpts() opts.HasBloomFilterSize = -1 - if _, err := CachedBlockstore(nil, nil, opts); err == nil { + if _, err := CachedBlockstore(context.TODO(), nil, opts); err == nil { t.Error("negative bloom size was not detected") } opts = DefaultCacheOpts() opts.HasBloomFilterHashes = -1 - if _, err := CachedBlockstore(nil, nil, opts); err == nil { + if _, err := CachedBlockstore(context.TODO(), nil, opts); err == nil { t.Error("negative hashes setting was not detected") } } @@ -29,7 +32,7 @@ func TestBloomHashesAtZero(t *testing.T) { opts := DefaultCacheOpts() opts.HasBloomFilterHashes = 0 - if _, err := CachedBlockstore(nil, nil, opts); err == nil { + if _, err := CachedBlockstore(context.TODO(), nil, opts); err == nil { t.Error("zero hashes setting with positive size was not detected") } } From 2d662a6123dbcbf055e65ee81f04a5365f537378 Mon Sep 17 00:00:00 2001 From: zramsay Date: Wed, 31 May 2017 16:56:11 -0400 Subject: [PATCH 1848/3817] apply the megacheck tool to improve code quality License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-ipfs-routing@e213e17cd09526b06ab1316c8647d87cfb93fdfd --- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 6 +++--- routing/none/none_client.go | 3 --- routing/offline/offline.go | 3 --- routing/offline/offline_test.go | 6 ++++-- routing/supernode/proxy/standard.go | 5 +---- routing/supernode/server.go | 23 +---------------------- 7 files changed, 10 insertions(+), 38 deletions(-) diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 4d824e673..afa250b9a 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -66,7 +66,7 @@ func (rs *s) Providers(c *cid.Cid) []pstore.PeerInfo { return ret } for _, r := range records { - if time.Now().Sub(r.Created) > rs.delayConf.ValueVisibility.Get() { + if time.Since(r.Created) > rs.delayConf.ValueVisibility.Get() { ret = append(ret, r.Peer) } } diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 69b451ce3..3c51340d6 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -45,7 +45,7 @@ func TestClientFindProviders(t *testing.T) { providersFromClient := client.FindProvidersAsync(context.Background(), k, max) isInClient := false for pi := range providersFromClient { - if pi.ID == pi.ID { + if pi.ID == pi.ID { // <-- typo? isInClient = true } } @@ -72,7 +72,7 @@ func TestClientOverMax(t *testing.T) { providersFromClient := client.FindProvidersAsync(context.Background(), k, max) i := 0 - for _ = range providersFromClient { + for range providersFromClient { i++ } if i != max { @@ -128,7 +128,7 @@ func TestCanceledContext(t *testing.T) { providers := client.FindProvidersAsync(ctx, k, max) numProvidersReturned := 0 - for _ = range providers { + for range providers { numProvidersReturned++ } t.Log(numProvidersReturned) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 35f3dedea..66767fdd0 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -7,15 +7,12 @@ import ( repo "github.com/ipfs/go-ipfs/repo" routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" p2phost "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) -var log = logging.Logger("mockrouter") - type nilclient struct { } diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 8c91a1c43..2be677920 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -10,7 +10,6 @@ import ( routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" @@ -19,8 +18,6 @@ import ( "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) -var log = logging.Logger("offlinerouting") - var ErrOffline = errors.New("routing system in offline mode") func NewOfflineRouter(dstore ds.Datastore, privkey ci.PrivKey) routing.IpfsRouting { diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index a847a2814..a9564379f 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -15,12 +15,14 @@ func TestOfflineRouterStorage(t *testing.T) { privkey, _, _ := testutil.RandTestKeyPair(128) offline := NewOfflineRouter(nds, privkey) - err := offline.PutValue(ctx, "key", []byte("testing 1 2 3")) - if err != nil { + if err := offline.PutValue(ctx, "key", []byte("testing 1 2 3")); err != nil { t.Fatal(err) } val, err := offline.GetValue(ctx, "key") + if err != nil { + t.Fatal(err) + } if !bytes.Equal([]byte("testing 1 2 3"), val) { t.Fatal("OfflineRouter does not properly store") } diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index da8160254..eaa9e0786 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -104,10 +104,7 @@ func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote pe } defer s.Close() pbw := ggio.NewDelimitedWriter(s) - if err := pbw.WriteMsg(m); err != nil { - return err - } - return nil + return pbw.WriteMsg(m) } // SendRequest sends the request to each remote sequentially (randomized order), diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 5d42231d0..95e70e0be 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -10,7 +10,6 @@ import ( datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dhtpb "gx/ipfs/QmRmroYSdievxnjiuy99C8BzShNstdEWcEF3LQHF7fUbez/go-libp2p-kad-dht/pb" - record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" @@ -140,10 +139,7 @@ func putRoutingRecord(ds datastore.Datastore, k string, value *pb.Record) error } dskey := dshelp.NewKeyFromBinary([]byte(k)) // TODO namespace - if err := ds.Put(dskey, data); err != nil { - return err - } - return nil + return ds.Put(dskey, data) } func putRoutingProviders(ds datastore.Datastore, k string, newRecords []*dhtpb.Message_Peer) error { @@ -204,20 +200,3 @@ func getRoutingProviders(ds datastore.Datastore, k string) ([]*dhtpb.Message_Pee func providerKey(k string) datastore.Key { return datastore.KeyWithNamespaces([]string{"routing", "providers", k}) } - -func verify(ps pstore.Peerstore, r *pb.Record) error { - v := make(record.Validator) - v["pk"] = record.PublicKeyValidator - p := peer.ID(r.GetAuthor()) - pk := ps.PubKey(p) - if pk == nil { - return fmt.Errorf("do not have public key for %s", p) - } - if err := record.CheckRecordSig(r, pk); err != nil { - return err - } - if err := v.VerifyRecord(r); err != nil { - return err - } - return nil -} From 780ae022981af50d7f54437a015f32c17f17712d Mon Sep 17 00:00:00 2001 From: zramsay Date: Wed, 31 May 2017 16:56:11 -0400 Subject: [PATCH 1849/3817] apply the megacheck tool to improve code quality License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-filestore@165caf091f0a3f94c24bfb10f017b3c0ec7f31a7 --- filestore/fsrefstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 46cc39b7f..f1db5b6a8 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -162,7 +162,7 @@ func (f *FileManager) readDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { } defer fi.Close() - _, err = fi.Seek(int64(d.GetOffset()), os.SEEK_SET) + _, err = fi.Seek(int64(d.GetOffset()), io.SeekStart) if err != nil { return nil, &CorruptReferenceError{StatusFileError, err} } From 622e2731a3bbd94cc6f8d19cd66b5fd032c349ae Mon Sep 17 00:00:00 2001 From: zramsay Date: Wed, 31 May 2017 16:56:11 -0400 Subject: [PATCH 1850/3817] apply the megacheck tool to improve code quality License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-merkledag@2f6bea5fa5c21e385611a0d9ed03116939ab6437 --- ipld/merkledag/merkledag.go | 2 -- ipld/merkledag/merkledag_test.go | 6 ------ ipld/merkledag/node.go | 5 ++--- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index b7a6ccf15..1a29b56e4 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -12,12 +12,10 @@ import ( offline "github.com/ipfs/go-ipfs/exchange/offline" ipldcbor "gx/ipfs/QmNrbCt8j9DT5W9Pmjy2SdudT9k8GpaDr4sRuFix3BXhgR/go-ipld-cbor" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) -var log = logging.Logger("merkledag") var ErrNotFound = fmt.Errorf("merkledag: not found") // DAGService is an IPFS Merkle DAG service. diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index da43bdb67..2182f9098 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -209,12 +209,6 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { } } -func assertCanGet(t *testing.T, ds DAGService, n node.Node) { - if _, err := ds.Get(context.Background(), n.Cid()); err != nil { - t.Fatal(err) - } -} - func TestCantGet(t *testing.T) { ds := dstest.Mock() a := NodeWithData([]byte("A")) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index fa575097a..4ef497184 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -215,9 +215,8 @@ func (n *ProtoNode) SetData(d []byte) { // that. If a link of the same name existed, it is removed. func (n *ProtoNode) UpdateNodeLink(name string, that *ProtoNode) (*ProtoNode, error) { newnode := n.Copy().(*ProtoNode) - err := newnode.RemoveNodeLink(name) - err = nil // ignore error - err = newnode.AddNodeLink(name, that) + _ = newnode.RemoveNodeLink(name) // ignore error + err := newnode.AddNodeLink(name, that) return newnode, err } From 80442fd3d798d59cd7f4ca78f06121780b32ae3f Mon Sep 17 00:00:00 2001 From: zramsay Date: Wed, 31 May 2017 16:56:11 -0400 Subject: [PATCH 1851/3817] apply the megacheck tool to improve code quality License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-blockservice@9a395f496dd0226bf36a5602beee727578f4d87b --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 294b54133..66701bc9d 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -172,7 +172,7 @@ func (s *blockService) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, // the returned channel. // NB: No guarantees are made about order. func (s *blockService) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block { - out := make(chan blocks.Block, 0) + out := make(chan blocks.Block) go func() { defer close(out) var misses []*cid.Cid From a352f9e140a63ab8f024f8ed24971a9427e20b92 Mon Sep 17 00:00:00 2001 From: zramsay Date: Wed, 31 May 2017 16:56:11 -0400 Subject: [PATCH 1852/3817] apply the megacheck tool to improve code quality License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-ipfs-exchange-offline@ed54fb7ffc251e75951e0ae6a154d2b569ca07a5 --- exchange/offline/offline.go | 2 +- exchange/offline/offline_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 399af0f58..a70201c64 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -42,7 +42,7 @@ func (_ *offlineExchange) Close() error { } func (e *offlineExchange) GetBlocks(ctx context.Context, ks []*cid.Cid) (<-chan blocks.Block, error) { - out := make(chan blocks.Block, 0) + out := make(chan blocks.Block) go func() { defer close(out) var misses []*cid.Cid diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index d2f877a94..efdf2c7b1 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -67,7 +67,7 @@ func TestGetBlocks(t *testing.T) { } var count int - for _ = range received { + for range received { count++ } if len(expected) != count { From 76cdcebe8d4754db8764a79d8ae2429aeb52d5ab Mon Sep 17 00:00:00 2001 From: zramsay Date: Wed, 31 May 2017 16:56:11 -0400 Subject: [PATCH 1853/3817] apply the megacheck tool to improve code quality License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-ipfs-pinner@4d125eac21b861a1b6e59f2a2911016cc537bd8a --- pinning/pinner/gc/gc.go | 3 --- pinning/pinner/pin.go | 4 +--- pinning/pinner/pin_test.go | 4 ++-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index ff1ca8a35..f92e8eead 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -9,13 +9,10 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) -var log = logging.Logger("gc") - // Result represents an incremental output from a garbage collection // run. It contains either an error, or the cid of a removed object. type Result struct { diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 482d070fd..4270884d9 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -528,9 +528,7 @@ func (p *pinner) InternalPins() []*cid.Cid { p.lock.Lock() defer p.lock.Unlock() var out []*cid.Cid - for _, c := range p.internalPin.Keys() { - out = append(out, c) - } + out = append(out, p.internalPin.Keys()...) return out } diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index bb90ea089..072761f0a 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -183,8 +183,8 @@ func TestIsPinnedLookup(t *testing.T) { // TODO does pinner need to share datastore with blockservice? p := NewPinner(dstore, dserv, dserv) - aNodes := make([]*mdag.ProtoNode, aBranchLen, aBranchLen) - aKeys := make([]*cid.Cid, aBranchLen, aBranchLen) + aNodes := make([]*mdag.ProtoNode, aBranchLen) + aKeys := make([]*cid.Cid, aBranchLen) for i := 0; i < aBranchLen; i++ { a, _ := randNode() if i >= 1 { From fe07a192c5cc5de6864e32dfe6c5e6944c989def Mon Sep 17 00:00:00 2001 From: zramsay Date: Wed, 31 May 2017 16:56:11 -0400 Subject: [PATCH 1854/3817] apply the megacheck tool to improve code quality License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-mfs@3e4258701b259e30e2530eee7fe9cb70bf7d7753 --- mfs/dir.go | 7 +------ mfs/mfs_test.go | 43 +++++++++++-------------------------------- mfs/ops.go | 7 +------ mfs/system.go | 13 ------------- 4 files changed, 13 insertions(+), 57 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 60cae39c7..fdfb49538 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -326,12 +326,7 @@ func (d *Directory) Unlink(name string) error { delete(d.childDirs, name) delete(d.files, name) - err := d.dirbuilder.RemoveChild(d.ctx, name) - if err != nil { - return err - } - - return nil + return d.dirbuilder.RemoveChild(d.ctx, name) } func (d *Directory) Flush() error { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 11a13b8e0..fa2e7c53d 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -396,6 +396,9 @@ func TestMfsFile(t *testing.T) { // assert size is as expected size, err := fi.Size() + if err != nil { + t.Fatal(err) + } if size != int64(fisize) { t.Fatal("size isnt correct") } @@ -419,12 +422,15 @@ func TestMfsFile(t *testing.T) { // make sure size hasnt changed size, err = wfd.Size() + if err != nil { + t.Fatal(err) + } if size != int64(fisize) { t.Fatal("size isnt correct") } // seek back to beginning - ns, err := wfd.Seek(0, os.SEEK_SET) + ns, err := wfd.Seek(0, io.SeekStart) if err != nil { t.Fatal(err) } @@ -561,13 +567,9 @@ func actorMakeFile(d *Directory) error { return err } - err = wfd.Close() - if err != nil { - return err - } - - return nil + return wfd.Close() } + func actorMkdir(d *Directory) error { d, err := randomWalk(d, rand.Intn(7)) if err != nil { @@ -575,31 +577,8 @@ func actorMkdir(d *Directory) error { } _, err = d.Mkdir(randomName()) - if err != nil { - return err - } - - return nil -} - -func actorRemoveFile(d *Directory) error { - d, err := randomWalk(d, rand.Intn(7)) - if err != nil { - return err - } - - ents, err := d.List(context.Background()) - if err != nil { - return err - } - - if len(ents) == 0 { - return nil - } - - re := ents[rand.Intn(len(ents))] - return d.Unlink(re.Name) + return err } func randomFile(d *Directory) (*File, error) { @@ -895,7 +874,7 @@ func readFile(rt *Root, path string, offset int64, buf []byte) error { return err } - _, err = fd.Seek(offset, os.SEEK_SET) + _, err = fd.Seek(offset, io.SeekStart) if err != nil { return err } diff --git a/mfs/ops.go b/mfs/ops.go index f84540a6a..0d02cbb08 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -65,12 +65,7 @@ func Mv(r *Root, src, dst string) error { return err } - err = srcDirObj.Unlink(srcFname) - if err != nil { - return err - } - - return nil + return srcDirObj.Unlink(srcFname) } func lookupDir(r *Root, path string) (*Directory, error) { diff --git a/mfs/system.go b/mfs/system.go index 4ed84d83b..934a32610 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -170,12 +170,6 @@ type Republisher struct { lastpub *cid.Cid } -func (rp *Republisher) getVal() *cid.Cid { - rp.lk.Lock() - defer rp.lk.Unlock() - return rp.val -} - // NewRepublisher creates a new Republisher object to republish the given root // using the given short and long time intervals func NewRepublisher(ctx context.Context, pf PubFunc, tshort, tlong time.Duration) *Republisher { @@ -197,13 +191,6 @@ func (p *Republisher) setVal(c *cid.Cid) { p.val = c } -func (p *Republisher) pubNow() { - select { - case p.pubnowch <- nil: - default: - } -} - func (p *Republisher) WaitPub() { p.lk.Lock() consistent := p.lastpub == p.val From dc27deafda3c4497e16495aaea1d0019f9352dd8 Mon Sep 17 00:00:00 2001 From: zramsay Date: Wed, 31 May 2017 16:56:11 -0400 Subject: [PATCH 1855/3817] apply the megacheck tool to improve code quality License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-ipfs-keystore@e0c64913def8a1d01fea7a7de3aaa06d510d8c17 --- keystore/keystore.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index 8424dbafa..acb8cb3cc 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -104,11 +104,8 @@ func (ks *FSKeystore) Put(name string, k ci.PrivKey) error { defer fi.Close() _, err = fi.Write(b) - if err != nil { - return err - } - return nil + return err } // Get retrieve a key from the Keystore From 3e7e5d1daffeb4879715401d9f1653fb67f6f8ec Mon Sep 17 00:00:00 2001 From: zramsay Date: Wed, 31 May 2017 23:41:26 -0400 Subject: [PATCH 1856/3817] address PR comments; remove commented/dead code License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-unixfs@427d991207ad47b698613c5e01d91afc4cf76531 --- unixfs/hamt/hamt_stress_test.go | 104 -------------------------------- unixfs/hamt/hamt_test.go | 20 +----- unixfs/mod/dagmodifier_test.go | 19 +----- 3 files changed, 4 insertions(+), 139 deletions(-) diff --git a/unixfs/hamt/hamt_stress_test.go b/unixfs/hamt/hamt_stress_test.go index 76357b23d..94bfce878 100644 --- a/unixfs/hamt/hamt_stress_test.go +++ b/unixfs/hamt/hamt_stress_test.go @@ -1,13 +1,10 @@ package hamt import ( - "bufio" "context" "fmt" "math/rand" "os" - "strconv" - "strings" "testing" "time" @@ -189,104 +186,3 @@ func genOpSet(seed int64, keep, temp []string) []testOp { } } } - -// executes the given op set with a repl to allow easier debugging -func debugExecuteOpSet(ds dag.DAGService, width int, ops []testOp) (*HamtShard, error) { - s, err := NewHamtShard(ds, width) - if err != nil { - return nil, err - } - - e := ft.EmptyDirNode() - ds.Add(e) - ctx := context.TODO() - - run := 0 - - opnames := map[int]string{ - opAdd: "add", - opDel: "del", - } - -mainloop: - for i := 0; i < len(ops); i++ { - o := ops[i] - - fmt.Printf("Op %d: %s %s\n", i, opnames[o.Op], o.Val) - for run == 0 { - cmd := readCommand() - parts := strings.Split(cmd, " ") - switch parts[0] { - case "": - run = 1 - case "find": - _, err := s.Find(ctx, parts[1]) - if err == nil { - fmt.Println("success") - } else { - fmt.Println(err) - } - case "run": - if len(parts) > 1 { - n, err := strconv.Atoi(parts[1]) - if err != nil { - panic(err) - } - - run = n - } else { - run = -1 - } - case "lookop": - for k := 0; k < len(ops); k++ { - if ops[k].Val == parts[1] { - fmt.Printf(" Op %d: %s %s\n", k, opnames[ops[k].Op], parts[1]) - } - } - case "restart": - var err error - s, err = NewHamtShard(ds, width) - if err != nil { - panic(err) - } - i = -1 - continue mainloop - case "print": - nd, err := s.Node() - if err != nil { - panic(err) - } - printDag(ds, nd.(*dag.ProtoNode), 0) - } - } - run-- - - switch o.Op { - case opAdd: - err := s.Set(ctx, o.Val, e) - if err != nil { - return nil, fmt.Errorf("inserting %s: %s", o.Val, err) - } - case opDel: - fmt.Println("deleting: ", o.Val) - err := s.Remove(ctx, o.Val) - if err != nil { - return nil, fmt.Errorf("deleting %s: %s", o.Val, err) - } - case opFind: - _, err := s.Find(ctx, o.Val) - if err != nil { - return nil, fmt.Errorf("finding %s: %s", o.Val, err) - } - } - } - - return s, nil -} - -func readCommand() string { - fmt.Print("> ") - scan := bufio.NewScanner(os.Stdin) - scan.Scan() - return scan.Text() -} diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 9f834a5ae..77997d2fd 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -6,7 +6,6 @@ import ( "math/rand" "os" "sort" - "strings" "testing" "time" @@ -138,7 +137,7 @@ func TestBasicSet(t *testing.T) { func TestDirBuilding(t *testing.T) { ds := mdtest.Mock() - s, _ := NewHamtShard(ds, 256) + _, _ = NewHamtShard(ds, 256) _, s, err := makeDir(ds, 200) if err != nil { @@ -161,7 +160,7 @@ func TestDirBuilding(t *testing.T) { func TestShardReload(t *testing.T) { ds := mdtest.Mock() - s, _ := NewHamtShard(ds, 256) + _, _ = NewHamtShard(ds, 256) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -494,21 +493,6 @@ func TestSetHamtChild(t *testing.T) { } } -func printDag(ds dag.DAGService, nd *dag.ProtoNode, depth int) { - padding := strings.Repeat(" ", depth) - fmt.Println("{") - for _, l := range nd.Links() { - fmt.Printf("%s%s: %s", padding, l.Name, l.Cid.String()) - ch, err := ds.Get(context.Background(), l.Cid) - if err != nil { - panic(err) - } - - printDag(ds, ch.(*dag.ProtoNode), depth+1) - } - fmt.Println(padding + "}") -} - func printDiff(ds dag.DAGService, a, b *dag.ProtoNode) { diff, err := dagutils.Diff(context.TODO(), ds, a, b) if err != nil { diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 192200dd2..d7b3f3267 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -1,36 +1,21 @@ package mod import ( + "context" "fmt" "io" "io/ioutil" "testing" - "github.com/ipfs/go-ipfs/blocks/blockstore" - bs "github.com/ipfs/go-ipfs/blockservice" - "github.com/ipfs/go-ipfs/exchange/offline" h "github.com/ipfs/go-ipfs/importer/helpers" trickle "github.com/ipfs/go-ipfs/importer/trickle" - mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" testu "github.com/ipfs/go-ipfs/unixfs/test" - context "context" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" ) -func getMockDagServAndBstore(t testing.TB) (mdag.DAGService, blockstore.Blockstore) { - dstore := ds.NewMapDatastore() - tsds := sync.MutexWrap(dstore) - bstore := blockstore.NewBlockstore(tsds) - bserv := bs.New(bstore, offline.Exchange(bstore)) - dserv := mdag.NewDAGService(bserv) - return dserv, bstore -} - func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) []byte { newdata := make([]byte, size) r := u.NewTimeSeededRand() @@ -112,7 +97,7 @@ func TestDagModifierBasic(t *testing.T) { beg = uint64(len(b)) length = 3000 t.Log("Testing pure append") - b = testModWrite(t, beg, length, b, dagmod) + _ = testModWrite(t, beg, length, b, dagmod) // Verify reported length node, err := dagmod.GetNode() From 153b52ddaf8e798df2e292f7b829da55d761aa99 Mon Sep 17 00:00:00 2001 From: zramsay Date: Wed, 31 May 2017 23:41:26 -0400 Subject: [PATCH 1857/3817] address PR comments; remove commented/dead code License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-namesys@14ef6a6d7d54134090c9f8dd3f3d5944a9fdd7a5 --- namesys/publisher.go | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 4a8570c01..cba463492 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -160,17 +160,11 @@ func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqn errs <- PublishPublicKey(ctx, r, namekey, k.GetPublic()) }() - err = waitOnErrChan(ctx, errs) - if err != nil { - return err - } - - err = waitOnErrChan(ctx, errs) - if err != nil { + if err := waitOnErrChan(ctx, errs); err != nil { return err } - return nil + return waitOnErrChan(ctx, errs) } func waitOnErrChan(ctx context.Context, errs chan error) error { @@ -340,12 +334,7 @@ func InitializeKeyspace(ctx context.Context, ds dag.DAGService, pub Publisher, p return err } - err = pub.Publish(ctx, key, path.FromCid(nodek)) - if err != nil { - return err - } - - return nil + return pub.Publish(ctx, key, path.FromCid(nodek)) } func IpnsKeysForID(id peer.ID) (name, ipns string) { From 2347cc9739d8810848fa9efad7fad8b6aca8f605 Mon Sep 17 00:00:00 2001 From: zramsay Date: Wed, 31 May 2017 23:41:26 -0400 Subject: [PATCH 1858/3817] address PR comments; remove commented/dead code License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-ipfs-blockstore@604f49c47351b0266571e260b0ebc1bd6771b274 --- blockstore/blockstore.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index ac4b87405..e1c7dcf35 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -102,10 +102,6 @@ func NewBlockstore(d ds.Batching) Blockstore { type blockstore struct { datastore ds.Batching - lk sync.RWMutex - gcreq int32 - gcreqlk sync.Mutex - rehash bool } @@ -246,9 +242,8 @@ func NewGCLocker() GCLocker { } type gclocker struct { - lk sync.RWMutex - gcreq int32 - gcreqlk sync.Mutex + lk sync.RWMutex + gcreq int32 } // Unlocker represents an object which can Unlock From 20330dc9bd0ee66de362f1733af49c773bb0b413 Mon Sep 17 00:00:00 2001 From: zramsay Date: Wed, 31 May 2017 23:41:26 -0400 Subject: [PATCH 1859/3817] address PR comments; remove commented/dead code License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-ipfs-routing@908c167f8002d1bd345089a8e6bb062f0387e640 --- routing/offline/offline_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index a9564379f..f4ccb2729 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -27,7 +27,7 @@ func TestOfflineRouterStorage(t *testing.T) { t.Fatal("OfflineRouter does not properly store") } - val, err = offline.GetValue(ctx, "notHere") + _, err = offline.GetValue(ctx, "notHere") if err == nil { t.Fatal("Router should throw errors for unfound records") } From fc3dd63083553c244feb99f9b830d904d846041c Mon Sep 17 00:00:00 2001 From: zramsay Date: Thu, 1 Jun 2017 13:58:34 -0400 Subject: [PATCH 1860/3817] hamt: reinstate a useful debug function License: MIT Signed-off-by: Zach Ramsay This commit was moved from ipfs/go-unixfs@6b9f909aeda55971691fdb181e4bbf68978199ef --- unixfs/hamt/hamt_stress_test.go | 102 ++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/unixfs/hamt/hamt_stress_test.go b/unixfs/hamt/hamt_stress_test.go index 94bfce878..6044631b9 100644 --- a/unixfs/hamt/hamt_stress_test.go +++ b/unixfs/hamt/hamt_stress_test.go @@ -186,3 +186,105 @@ func genOpSet(seed int64, keep, temp []string) []testOp { } } } + +// executes the given op set with a repl to allow easier debugging +/*func debugExecuteOpSet(ds dag.DAGService, width int, ops []testOp) (*HamtShard, error) { + + s, err := NewHamtShard(ds, width) + if err != nil { + return nil, err + } + + e := ft.EmptyDirNode() + ds.Add(e) + ctx := context.TODO() + + run := 0 + + opnames := map[int]string{ + opAdd: "add", + opDel: "del", + } + +mainloop: + for i := 0; i < len(ops); i++ { + o := ops[i] + + fmt.Printf("Op %d: %s %s\n", i, opnames[o.Op], o.Val) + for run == 0 { + cmd := readCommand() + parts := strings.Split(cmd, " ") + switch parts[0] { + case "": + run = 1 + case "find": + _, err := s.Find(ctx, parts[1]) + if err == nil { + fmt.Println("success") + } else { + fmt.Println(err) + } + case "run": + if len(parts) > 1 { + n, err := strconv.Atoi(parts[1]) + if err != nil { + panic(err) + } + + run = n + } else { + run = -1 + } + case "lookop": + for k = 0; k < len(ops); k++ { + if ops[k].Val == parts[1] { + fmt.Printf(" Op %d: %s %s\n", k, opnames[ops[k].Op], parts[1]) + } + } + case "restart": + var err error + s, err = NewHamtShard(ds, width) + if err != nil { + panic(err) + } + i = -1 + continue mainloop + case "print": + nd, err := s.Node() + if err != nil { + panic(err) + } + printDag(ds, nd.(*dag.ProtoNode), 0) + } + } + run-- + + switch o.Op { + case opAdd: + err := s.Set(ctx, o.Val, e) + if err != nil { + return nil, fmt.Errorf("inserting %s: %s", o.Val, err) + } + case opDel: + fmt.Println("deleting: ", o.Val) + err := s.Remove(ctx, o.Val) + if err != nil { + return nil, fmt.Errorf("deleting %s: %s", o.Val, err) + } + case opFind: + _, err := s.Find(ctx, o.Val) + if err != nil { + return nil, fmt.Errorf("finding %s: %s", o.Val, err) + } + } + } + + return s, nil +} + +func readCommand() string { + fmt.Print("> ") + scan := bufio.NewScanner(os.Stdin) + scan.Scan() + return scan.Text() +}*/ From 25bc686917f81189cd037ba8ca241ac8d6b718f1 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 7 Jun 2017 14:23:58 -0400 Subject: [PATCH 1861/3817] repub: iterate through all keys in keystore Iterate through all keys in the keystore so keys added with "ipfs key gen" behave the same as the key. Don't maintain a separate repub list as it does not really serve a purpose at this point in time. See #3808. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-namesys@95dd586dd4021626841aaa5c71dcc6a5e17c03ed --- namesys/republisher/repub.go | 81 ++++++++++++++++++++----------- namesys/republisher/repub_test.go | 3 +- 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 94ad4e8e0..9fa0907da 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -6,18 +6,19 @@ import ( "sync" "time" + keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" + ic "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" recpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" - pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) @@ -31,9 +32,10 @@ var DefaultRebroadcastInterval = time.Hour * 4 const DefaultRecordLifetime = time.Hour * 24 type Republisher struct { - r routing.ValueStore - ds ds.Datastore - ps pstore.Peerstore + r routing.ValueStore + ds ds.Datastore + self ic.PrivKey + ks keystore.Keystore Interval time.Duration @@ -44,23 +46,18 @@ type Republisher struct { entries map[peer.ID]struct{} } -func NewRepublisher(r routing.ValueStore, ds ds.Datastore, ps pstore.Peerstore) *Republisher { +// NewRepublisher creates a new Republisher +func NewRepublisher(r routing.ValueStore, ds ds.Datastore, self ic.PrivKey, ks keystore.Keystore) *Republisher { return &Republisher{ r: r, - ps: ps, ds: ds, - entries: make(map[peer.ID]struct{}), + self: self, + ks: ks, Interval: DefaultRebroadcastInterval, RecordLifetime: DefaultRecordLifetime, } } -func (rp *Republisher) AddName(id peer.ID) { - rp.entrylock.Lock() - defer rp.entrylock.Unlock() - rp.entries[id] = struct{}{} -} - func (rp *Republisher) Run(proc goprocess.Process) { tick := time.NewTicker(rp.Interval) defer tick.Stop() @@ -82,31 +79,61 @@ func (rp *Republisher) republishEntries(p goprocess.Process) error { ctx, cancel := context.WithCancel(gpctx.OnClosingContext(p)) defer cancel() - for id, _ := range rp.entries { - log.Debugf("republishing ipns entry for %s", id) - priv := rp.ps.PrivKey(id) + err := rp.republishEntry(ctx, rp.self) + if err != nil { + return err + } - // Look for it locally only - _, ipnskey := namesys.IpnsKeysForID(id) - p, seq, err := rp.getLastVal(ipnskey) + if rp.ks != nil { + keyNames, err := rp.ks.List() if err != nil { - if err == errNoEntry { - continue - } return err } + for _, name := range keyNames { + priv, err := rp.ks.Get(name) + if err != nil { + return err + } + err = rp.republishEntry(ctx, priv) + if err != nil { + return err + } - // update record with same sequence number - eol := time.Now().Add(rp.RecordLifetime) - err = namesys.PutRecordToRouting(ctx, priv, p, seq, eol, rp.r, id) - if err != nil { - return err } } return nil } +func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) error { + id, err := peer.IDFromPrivateKey(priv) + if err != nil { + return err + } + + log.Debugf("republishing ipns entry for %s", id) + + // Look for it locally only + _, ipnskey := namesys.IpnsKeysForID(id) + p, seq, err := rp.getLastVal(ipnskey) + if err != nil { + if err == errNoEntry { + return nil + } + return err + } + + // update record with same sequence number + eol := time.Now().Add(rp.RecordLifetime) + err = namesys.PutRecordToRouting(ctx, priv, p, seq, eol, rp.r, id) + if err != nil { + println("put record to routing error: " + err.Error()) + return err + } + + return nil +} + func (rp *Republisher) getLastVal(k string) (path.Path, uint64, error) { ival, err := rp.ds.Get(dshelp.NewKeyFromBinary([]byte(k))) if err != nil { diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index a4810e8ba..d4d7e1282 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -78,10 +78,9 @@ func TestRepublish(t *testing.T) { // The republishers that are contained within the nodes have their timeout set // to 12 hours. Instead of trying to tweak those, we're just going to pretend // they dont exist and make our own. - repub := NewRepublisher(publisher.Routing, publisher.Repo.Datastore(), publisher.Peerstore) + repub := NewRepublisher(publisher.Routing, publisher.Repo.Datastore(), publisher.PrivateKey, publisher.Repo.Keystore()) repub.Interval = time.Second repub.RecordLifetime = time.Second * 5 - repub.AddName(publisher.Identity) proc := goprocess.Go(repub.Run) defer proc.Close() From ed9cd61ad1fb1f175500aa4371fc8ba21c9695bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Thu, 8 Jun 2017 16:20:14 +0900 Subject: [PATCH 1862/3817] Filestore: more verbose error when adding a file from outside of the root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Michael Muré This commit was moved from ipfs/go-filestore@0f6b7c31ec134e8dcba0cfa4c233d9dad7f1107f --- filestore/fsrefstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index f1db5b6a8..46b385067 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -211,7 +211,7 @@ func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { var dobj pb.DataObj if !filepath.HasPrefix(b.PosInfo.FullPath, f.root) { - return fmt.Errorf("cannot add filestore references outside ipfs root") + return fmt.Errorf("cannot add filestore references outside ipfs root (%s)", f.root) } p, err := filepath.Rel(f.root, b.PosInfo.FullPath) From eca827863f0057151835a54ca3714ef04664fb8c Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sat, 10 Jun 2017 01:55:04 -0400 Subject: [PATCH 1863/3817] repub: remove unused field License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-namesys@c242b5be2011a2e81a372113ec3c60a6da6be0d1 --- namesys/republisher/repub.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 9fa0907da..b33328b68 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -3,7 +3,6 @@ package republisher import ( "context" "errors" - "sync" "time" keystore "github.com/ipfs/go-ipfs/keystore" @@ -41,9 +40,6 @@ type Republisher struct { // how long records that are republished should be valid for RecordLifetime time.Duration - - entrylock sync.Mutex - entries map[peer.ID]struct{} } // NewRepublisher creates a new Republisher From 6cc8281c909228c1da9083439cf63c8ca51698b6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 19 Jun 2017 22:10:37 -0400 Subject: [PATCH 1864/3817] Allow dagmodifier to be created (but not used) with raw nodes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@982e5532052f2f3e4876c0658b1229e147a5de3f --- unixfs/mod/dagmodifier.go | 55 ++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 090cdb593..9e86d7b60 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "errors" + "fmt" "io" chunk "github.com/ipfs/go-ipfs/importer/chunk" @@ -29,7 +30,7 @@ var writebufferSize = 1 << 21 // Dear god, please rename this to something more pleasant type DagModifier struct { dagserv mdag.DAGService - curNode *mdag.ProtoNode + curNode node.Node splitter chunk.SplitterGen ctx context.Context @@ -42,14 +43,18 @@ type DagModifier struct { read uio.DagReader } +var ErrNotUnixfs = fmt.Errorf("dagmodifier only supports unixfs nodes (proto or raw)") + func NewDagModifier(ctx context.Context, from node.Node, serv mdag.DAGService, spl chunk.SplitterGen) (*DagModifier, error) { - pbn, ok := from.(*mdag.ProtoNode) - if !ok { - return nil, mdag.ErrNotProtobuf + switch from.(type) { + case *mdag.ProtoNode, *mdag.RawNode: + // ok + default: + return nil, ErrNotUnixfs } return &DagModifier{ - curNode: pbn.Copy().(*mdag.ProtoNode), + curNode: from.Copy(), dagserv: serv, splitter: spl, ctx: ctx, @@ -144,8 +149,15 @@ func (dm *DagModifier) Write(b []byte) (int, error) { return n, nil } +var ErrNoRawYet = fmt.Errorf("currently only fully support protonodes in the dagmodifier") + func (dm *DagModifier) Size() (int64, error) { - pbn, err := ft.FromBytes(dm.curNode.Data()) + pbnd, ok := dm.curNode.(*mdag.ProtoNode) + if !ok { + return 0, ErrNoRawYet + } + + pbn, err := ft.FromBytes(pbnd.Data()) if err != nil { return 0, err } @@ -222,7 +234,12 @@ func (dm *DagModifier) Sync() error { // modifyDag writes the data in 'data' over the data in 'node' starting at 'offset' // returns the new key of the passed in node and whether or not all the data in the reader // has been consumed. -func (dm *DagModifier) modifyDag(node *mdag.ProtoNode, offset uint64, data io.Reader) (*cid.Cid, bool, error) { +func (dm *DagModifier) modifyDag(n node.Node, offset uint64, data io.Reader) (*cid.Cid, bool, error) { + node, ok := n.(*mdag.ProtoNode) + if !ok { + return nil, false, ErrNoRawYet + } + f, err := ft.FromBytes(node.Data()) if err != nil { return nil, false, err @@ -301,13 +318,26 @@ func (dm *DagModifier) modifyDag(node *mdag.ProtoNode, offset uint64, data io.Re } // appendData appends the blocks from the given chan to the end of this dag -func (dm *DagModifier) appendData(node *mdag.ProtoNode, spl chunk.Splitter) (node.Node, error) { +func (dm *DagModifier) appendData(nd node.Node, spl chunk.Splitter) (node.Node, error) { + + var root *mdag.ProtoNode + switch nd := nd.(type) { + case *mdag.ProtoNode: + root = nd + case *mdag.RawNode: + // TODO: be able to append to rawnodes. Probably requires making this + // node a child of a unxifs intermediate node and passing it down + return nil, fmt.Errorf("appending to raw node types not yet supported") + default: + return nil, ErrNotUnixfs + } + dbp := &help.DagBuilderParams{ Dagserv: dm.dagserv, Maxlinks: help.DefaultLinksPerBlock, } - return trickle.TrickleAppend(dm.ctx, node, dbp.New(spl)) + return trickle.TrickleAppend(dm.ctx, root, dbp.New(spl)) } // Read data from this dag starting at the current offset @@ -452,7 +482,12 @@ func (dm *DagModifier) Truncate(size int64) error { } // dagTruncate truncates the given node to 'size' and returns the modified Node -func dagTruncate(ctx context.Context, nd *mdag.ProtoNode, size uint64, ds mdag.DAGService) (*mdag.ProtoNode, error) { +func dagTruncate(ctx context.Context, n node.Node, size uint64, ds mdag.DAGService) (*mdag.ProtoNode, error) { + nd, ok := n.(*mdag.ProtoNode) + if !ok { + return nil, ErrNoRawYet + } + if len(nd.Links()) == 0 { // TODO: this can likely be done without marshaling and remarshaling pbn, err := ft.FromBytes(nd.Data()) From df2c4ec583fe8e620f920f9a4d46e52df7ef0a56 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 20 Jun 2017 17:05:24 -0400 Subject: [PATCH 1865/3817] Finish basic support for raw nodes in dag modifier. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-unixfs@ec9e96ffc1bb694a74e84f8d7a678967fdda92f7 --- unixfs/mod/dagmodifier.go | 34 ++++++++++++++++++---------------- unixfs/mod/dagmodifier_test.go | 3 ++- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 9e86d7b60..83da608b9 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -152,23 +152,25 @@ func (dm *DagModifier) Write(b []byte) (int, error) { var ErrNoRawYet = fmt.Errorf("currently only fully support protonodes in the dagmodifier") func (dm *DagModifier) Size() (int64, error) { - pbnd, ok := dm.curNode.(*mdag.ProtoNode) - if !ok { - return 0, ErrNoRawYet - } - - pbn, err := ft.FromBytes(pbnd.Data()) - if err != nil { - return 0, err - } - - if dm.wrBuf != nil { - if uint64(dm.wrBuf.Len())+dm.writeStart > pbn.GetFilesize() { + switch nd := dm.curNode.(type) { + case *mdag.ProtoNode: + pbn, err := ft.FromBytes(nd.Data()) + if err != nil { + return 0, err + } + if dm.wrBuf != nil && uint64(dm.wrBuf.Len())+dm.writeStart > pbn.GetFilesize() { return int64(dm.wrBuf.Len()) + int64(dm.writeStart), nil } + return int64(pbn.GetFilesize()), nil + case *mdag.RawNode: + if dm.wrBuf != nil { + return 0, ErrNoRawYet + } + sz, err := nd.Size() + return int64(sz), err + default: + return 0, ErrNotUnixfs } - - return int64(pbn.GetFilesize()), nil } // Sync writes changes to this dag to disk @@ -397,12 +399,12 @@ func (dm *DagModifier) CtxReadFull(ctx context.Context, b []byte) (int, error) { } // GetNode gets the modified DAG Node -func (dm *DagModifier) GetNode() (*mdag.ProtoNode, error) { +func (dm *DagModifier) GetNode() (node.Node, error) { err := dm.Sync() if err != nil { return nil, err } - return dm.curNode.Copy().(*mdag.ProtoNode), nil + return dm.curNode.Copy(), nil } // HasChanges returned whether or not there are unflushed changes to this dag diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index d7b3f3267..b22844194 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -9,6 +9,7 @@ import ( h "github.com/ipfs/go-ipfs/importer/helpers" trickle "github.com/ipfs/go-ipfs/importer/trickle" + mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" testu "github.com/ipfs/go-ipfs/unixfs/test" @@ -105,7 +106,7 @@ func TestDagModifierBasic(t *testing.T) { t.Fatal(err) } - size, err := ft.DataSize(node.Data()) + size, err := ft.DataSize(node.(*mdag.ProtoNode).Data()) if err != nil { t.Fatal(err) } From 1c7c1d05e2414cde2c376bba33f0600a8f74d97a Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 21 Jun 2017 00:49:30 -0400 Subject: [PATCH 1866/3817] dagmodifer: refactor appendData method License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-unixfs@db7c21c9391738094bd8af0ec9ef87554f7edcae --- unixfs/mod/dagmodifier.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 83da608b9..f99453c8d 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -151,6 +151,7 @@ func (dm *DagModifier) Write(b []byte) (int, error) { var ErrNoRawYet = fmt.Errorf("currently only fully support protonodes in the dagmodifier") +// Size returns the Filesize of the node func (dm *DagModifier) Size() (int64, error) { switch nd := dm.curNode.(type) { case *mdag.ProtoNode: @@ -321,25 +322,18 @@ func (dm *DagModifier) modifyDag(n node.Node, offset uint64, data io.Reader) (*c // appendData appends the blocks from the given chan to the end of this dag func (dm *DagModifier) appendData(nd node.Node, spl chunk.Splitter) (node.Node, error) { - - var root *mdag.ProtoNode switch nd := nd.(type) { case *mdag.ProtoNode: - root = nd + dbp := &help.DagBuilderParams{ + Dagserv: dm.dagserv, + Maxlinks: help.DefaultLinksPerBlock, + } + return trickle.TrickleAppend(dm.ctx, nd, dbp.New(spl)) case *mdag.RawNode: - // TODO: be able to append to rawnodes. Probably requires making this - // node a child of a unxifs intermediate node and passing it down return nil, fmt.Errorf("appending to raw node types not yet supported") default: return nil, ErrNotUnixfs } - - dbp := &help.DagBuilderParams{ - Dagserv: dm.dagserv, - Maxlinks: help.DefaultLinksPerBlock, - } - - return trickle.TrickleAppend(dm.ctx, root, dbp.New(spl)) } // Read data from this dag starting at the current offset From beaadf936a05bc79c2d63c8353625a4e44c98671 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 15 Jun 2017 21:02:21 -0700 Subject: [PATCH 1867/3817] blocks: move block format to it's own repo We need to reference it from outside of this repo. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@ccfd0a097e59d9f956925c2c17b3a780be4b1f6b --- ipld/merkledag/merkledag.go | 2 +- ipld/merkledag/merkledag_test.go | 2 +- ipld/merkledag/raw.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 1a29b56e4..af5619752 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -7,7 +7,7 @@ import ( "strings" "sync" - blocks "github.com/ipfs/go-ipfs/blocks" + blocks "github.com/ipfs/go-block-format" bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 2182f9098..b2ce16c1a 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -13,7 +13,7 @@ import ( "testing" "time" - blocks "github.com/ipfs/go-ipfs/blocks" + blocks "github.com/ipfs/go-block-format" bserv "github.com/ipfs/go-ipfs/blockservice" bstest "github.com/ipfs/go-ipfs/blockservice/test" offline "github.com/ipfs/go-ipfs/exchange/offline" diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 7c5ba56af..a2b223367 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -1,7 +1,7 @@ package merkledag import ( - "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-block-format" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" From 074085a43852b5b2970aa70a1f55dafaf86963cf Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 15 Jun 2017 21:02:21 -0700 Subject: [PATCH 1868/3817] blocks: move block format to it's own repo We need to reference it from outside of this repo. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-blockstore@649232fda100f48b272b21dca8cb74eea3ef9493 --- blockstore/arc_cache.go | 2 +- blockstore/arc_cache_test.go | 2 +- blockstore/blockstore.go | 2 +- blockstore/blockstore_test.go | 2 +- blockstore/bloom_cache.go | 2 +- blockstore/bloom_cache_test.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 75c7ee489..ddc9ace87 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -3,7 +3,7 @@ package blockstore import ( "context" - "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-block-format" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index e6f35144d..9ee955f0e 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-block-format" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" syncds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index e1c7dcf35..4a0daad6d 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -8,7 +8,7 @@ import ( "sync" "sync/atomic" - blocks "github.com/ipfs/go-ipfs/blocks" + blocks "github.com/ipfs/go-block-format" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 93705997e..bb1525d17 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -6,7 +6,7 @@ import ( "fmt" "testing" - blocks "github.com/ipfs/go-ipfs/blocks" + blocks "github.com/ipfs/go-block-format" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 8bcf962fe..0f2028c9a 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -5,7 +5,7 @@ import ( "sync/atomic" "time" - "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-block-format" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 85046e270..51a41a115 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-block-format" context "context" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" From dac90c140b144b01cbae43803d66800f67dcac73 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 15 Jun 2017 21:02:21 -0700 Subject: [PATCH 1869/3817] blocks: move block format to it's own repo We need to reference it from outside of this repo. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@d45c198d8319da53f143e122dc5ac4ccbd6058f8 --- filestore/filestore.go | 2 +- filestore/fsrefstore.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 047d26b51..3472b2059 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -10,7 +10,7 @@ package filestore import ( "context" - "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-block-format" "github.com/ipfs/go-ipfs/blocks/blockstore" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 46b385067..89e50344a 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -7,7 +7,7 @@ import ( "os" "path/filepath" - "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-block-format" "github.com/ipfs/go-ipfs/blocks/blockstore" pb "github.com/ipfs/go-ipfs/filestore/pb" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" From 473d305c919b13246548bb8e4c2ff49fe3efed03 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 18 Jun 2017 13:07:24 -0700 Subject: [PATCH 1870/3817] blocks: gx import go-block-format And updated related dependencies. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@6e8340a4c6dd2b2bef9cd80768eeecc521203e26 --- namesys/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/routing.go b/namesys/routing.go index 6b94876c6..1f7177a1b 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -10,12 +10,12 @@ import ( path "github.com/ipfs/go-ipfs/path" routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" mh "gx/ipfs/QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw/go-multihash" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) From d0bca8c9443dd951ed0448cb3c6fe34dd4eb4407 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 15 Jun 2017 21:02:21 -0700 Subject: [PATCH 1871/3817] blocks: move block format to it's own repo We need to reference it from outside of this repo. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@6bd4a5102f4c080f2c248946ecd35a0611847c5a --- blockservice/blockservice.go | 2 +- blockservice/blockservice_test.go | 2 +- blockservice/test/blocks_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 66701bc9d..14c445f89 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -8,7 +8,7 @@ import ( "errors" "fmt" - blocks "github.com/ipfs/go-ipfs/blocks" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index bd3b0c665..3085a3444 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -3,7 +3,7 @@ package blockservice import ( "testing" - "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-block-format" "github.com/ipfs/go-ipfs/blocks/blockstore" butil "github.com/ipfs/go-ipfs/blocks/blocksutil" offline "github.com/ipfs/go-ipfs/exchange/offline" diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 68f77be7e..0b66202bc 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - blocks "github.com/ipfs/go-ipfs/blocks" + blocks "github.com/ipfs/go-block-format" blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" From 8825244cbbfd8eae8dc318fcb495b74d67efd182 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 18 Jun 2017 13:07:24 -0700 Subject: [PATCH 1872/3817] blocks: gx import go-block-format And updated related dependencies. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@bf3c1e0596f0abcfb28f7ec5f4a1a074ea2a201a --- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 2 +- routing/none/none_client.go | 2 +- routing/offline/offline.go | 2 +- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 3dad5d320..a047c1dae 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -9,12 +9,12 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" dhtpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ma "gx/ipfs/QmcyqRMCAXVtYPS4DiBrA7sezL9rRGfW8Ctx7cywL4TXJj/go-multiaddr" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index afa250b9a..77759d085 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -8,10 +8,10 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 3c51340d6..401a571ec 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,9 +8,9 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index b9ee9b5ba..51bd21118 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -6,7 +6,7 @@ import ( mocknet "gx/ipfs/QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime/go-libp2p/p2p/net/mock" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - dht "gx/ipfs/QmRmroYSdievxnjiuy99C8BzShNstdEWcEF3LQHF7fUbez/go-libp2p-kad-dht" + dht "gx/ipfs/QmUisRCuGWoJM7WtVQDYT2jrNxUtfZMJzvVFTogzdwv7uV/go-libp2p-kad-dht" ) type mocknetserver struct { diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 66767fdd0..c803aea6d 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -7,9 +7,9 @@ import ( repo "github.com/ipfs/go-ipfs/repo" routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" p2phost "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 2be677920..7b1392dfb 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -8,12 +8,12 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 47345cd82..251489a66 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -9,13 +9,13 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" - dhtpb "gx/ipfs/QmRmroYSdievxnjiuy99C8BzShNstdEWcEF3LQHF7fUbez/go-libp2p-kad-dht/pb" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + dhtpb "gx/ipfs/QmUisRCuGWoJM7WtVQDYT2jrNxUtfZMJzvVFTogzdwv7uV/go-libp2p-kad-dht/pb" "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" loggables "gx/ipfs/QmVesPmqbPp7xRGyY96tnBwzDtVV1nqv4SCVxo5zCqKyH8/go-libp2p-loggables" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index d38cc4f43..6fa5a8391 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,8 +2,8 @@ package proxy import ( context "context" - dhtpb "gx/ipfs/QmRmroYSdievxnjiuy99C8BzShNstdEWcEF3LQHF7fUbez/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmRscs8KxrSmSv4iuevHv8JfuUzHBMoqiaHzxfDRiksd6e/go-libp2p-net" + dhtpb "gx/ipfs/QmUisRCuGWoJM7WtVQDYT2jrNxUtfZMJzvVFTogzdwv7uV/go-libp2p-kad-dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index eaa9e0786..04c68c5f6 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,9 +4,9 @@ import ( "context" "errors" - dhtpb "gx/ipfs/QmRmroYSdievxnjiuy99C8BzShNstdEWcEF3LQHF7fUbez/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmRscs8KxrSmSv4iuevHv8JfuUzHBMoqiaHzxfDRiksd6e/go-libp2p-net" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + dhtpb "gx/ipfs/QmUisRCuGWoJM7WtVQDYT2jrNxUtfZMJzvVFTogzdwv7uV/go-libp2p-kad-dht/pb" host "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" loggables "gx/ipfs/QmVesPmqbPp7xRGyY96tnBwzDtVV1nqv4SCVxo5zCqKyH8/go-libp2p-loggables" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 95e70e0be..610daa854 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -9,7 +9,7 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dhtpb "gx/ipfs/QmRmroYSdievxnjiuy99C8BzShNstdEWcEF3LQHF7fUbez/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmUisRCuGWoJM7WtVQDYT2jrNxUtfZMJzvVFTogzdwv7uV/go-libp2p-kad-dht/pb" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 925dad867..b090d272d 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -4,7 +4,7 @@ import ( "testing" datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dhtpb "gx/ipfs/QmRmroYSdievxnjiuy99C8BzShNstdEWcEF3LQHF7fUbez/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmUisRCuGWoJM7WtVQDYT2jrNxUtfZMJzvVFTogzdwv7uV/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 7a35b04cb8b981bc3ea8912487f4bd80324183a1 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 15 Jun 2017 21:02:21 -0700 Subject: [PATCH 1873/3817] blocks: move block format to it's own repo We need to reference it from outside of this repo. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-exchange-offline@8712d9bf9b3aa241c78220a468738270b813a619 --- exchange/offline/offline.go | 2 +- exchange/offline/offline_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index a70201c64..a7507943a 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -5,7 +5,7 @@ package offline import ( "context" - blocks "github.com/ipfs/go-ipfs/blocks" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index efdf2c7b1..a143183bb 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - blocks "github.com/ipfs/go-ipfs/blocks" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" From 117048cedfc7a660735206ddf6f48bb9110af3d7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 15 Jun 2017 21:02:21 -0700 Subject: [PATCH 1874/3817] blocks: move block format to it's own repo We need to reference it from outside of this repo. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-exchange-interface@2185a8d7b5fc67d8b0b01fa36ae832def322fd1f --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index 58c4c14ae..becb88c1a 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -5,7 +5,7 @@ import ( "context" "io" - blocks "github.com/ipfs/go-ipfs/blocks" + blocks "github.com/ipfs/go-block-format" cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) From f9d4d8e1c26579658b0a73d8f65221428c2667e3 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 15 Jun 2017 21:02:21 -0700 Subject: [PATCH 1875/3817] blocks: move block format to it's own repo We need to reference it from outside of this repo. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-chunker@d1535004d070a594684c058280e343453e07e5d3 --- chunker/rabin_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 907b80999..3605a3dd3 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -3,7 +3,7 @@ package chunk import ( "bytes" "fmt" - "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-block-format" "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" "io" "testing" From c48d600f91f3fc3e7274fba3ee3e1de9ff872636 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 18 Jun 2017 13:07:24 -0700 Subject: [PATCH 1876/3817] blocks: gx import go-block-format And updated related dependencies. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@f51725235962d51fcb29c0c19c4cd3ca3b864d54 --- ipld/merkledag/coding.go | 4 ++-- ipld/merkledag/merkledag.go | 8 ++++---- ipld/merkledag/merkledag_test.go | 6 +++--- ipld/merkledag/node.go | 4 ++-- ipld/merkledag/node_test.go | 2 +- ipld/merkledag/raw.go | 6 +++--- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/traverse/traverse_test.go | 2 +- ipld/merkledag/utils/diff.go | 4 ++-- ipld/merkledag/utils/diffenum.go | 4 ++-- ipld/merkledag/utils/diffenum_test.go | 4 ++-- ipld/merkledag/utils/utils.go | 2 +- ipld/merkledag/utils/utils_test.go | 2 +- 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index b5c2075da..3674a3674 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -6,8 +6,8 @@ import ( pb "github.com/ipfs/go-ipfs/merkledag/pb" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index af5619752..03485a909 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -7,13 +7,13 @@ import ( "strings" "sync" - blocks "github.com/ipfs/go-block-format" bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" + blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" - ipldcbor "gx/ipfs/QmNrbCt8j9DT5W9Pmjy2SdudT9k8GpaDr4sRuFix3BXhgR/go-ipld-cbor" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + ipldcbor "gx/ipfs/QmeYUiuN29RaXEK79Arqe9iBaek6xExz4iikREQq9bWNGM/go-ipld-cbor" ) var ErrNotFound = fmt.Errorf("merkledag: not found") diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index b2ce16c1a..0e5d90fc8 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -13,7 +13,6 @@ import ( "testing" "time" - blocks "github.com/ipfs/go-block-format" bserv "github.com/ipfs/go-ipfs/blockservice" bstest "github.com/ipfs/go-ipfs/blockservice/test" offline "github.com/ipfs/go-ipfs/exchange/offline" @@ -23,10 +22,11 @@ import ( mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" + blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) func TestNode(t *testing.T) { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 4ef497184..bd9322d36 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -5,9 +5,9 @@ import ( "encoding/json" "fmt" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" mh "gx/ipfs/QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw/go-multihash" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index c630a8a06..ca0d06e95 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -8,7 +8,7 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) func TestRemoveLink(t *testing.T) { diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index a2b223367..01558b18f 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -1,11 +1,11 @@ package merkledag import ( - "github.com/ipfs/go-block-format" + "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) type RawNode struct { diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index e5caa9cf4..25a90f461 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -5,7 +5,7 @@ import ( "context" "errors" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) // Order is an identifier for traversal algorithm orders diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 601c39bfb..40d1d739b 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -8,7 +8,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) func TestDFSPreNoSkip(t *testing.T) { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 87b27d20c..4d079301c 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -7,8 +7,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) const ( diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go index 28229a4fa..9fb8de9c4 100644 --- a/ipld/merkledag/utils/diffenum.go +++ b/ipld/merkledag/utils/diffenum.go @@ -6,8 +6,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) // DiffEnumerate fetches every object in the graph pointed to by 'to' that is diff --git a/ipld/merkledag/utils/diffenum_test.go b/ipld/merkledag/utils/diffenum_test.go index 45a0e178b..c840283c1 100644 --- a/ipld/merkledag/utils/diffenum_test.go +++ b/ipld/merkledag/utils/diffenum_test.go @@ -8,8 +8,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) func buildNode(name string, desc map[string]ndesc, out map[string]node.Node) node.Node { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 4a3d2e7f8..cf50da80f 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -12,7 +12,7 @@ import ( ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" syncds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) type Editor struct { diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 541920529..1a0bf2c4d 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" context "context" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ) func TestAddLink(t *testing.T) { From fbcec1faaa48f26c05a3061d20a94f1d0b4c9d16 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 18 Jun 2017 13:07:24 -0700 Subject: [PATCH 1877/3817] blocks: gx import go-block-format And updated related dependencies. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-blockstore@b878e0db7d001573638dc0d6fa02989349e7a910 --- blockstore/arc_cache.go | 4 ++-- blockstore/arc_cache_test.go | 4 ++-- blockstore/blockstore.go | 4 ++-- blockstore/blockstore_test.go | 4 ++-- blockstore/bloom_cache.go | 4 ++-- blockstore/bloom_cache_test.go | 2 +- blockstore/util/remove.go | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index ddc9ace87..8eb48fc18 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -3,12 +3,12 @@ package blockstore import ( "context" - "github.com/ipfs/go-block-format" + "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) // arccache wraps a BlockStore with an Adaptive Replacement Cache (ARC) for diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 9ee955f0e..c2aaf9314 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -4,11 +4,11 @@ import ( "context" "testing" - "github.com/ipfs/go-block-format" + "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" syncds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) var exampleBlock = blocks.NewBlock([]byte("foo")) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 4a0daad6d..ad799df88 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -8,14 +8,14 @@ import ( "sync" "sync/atomic" - blocks "github.com/ipfs/go-block-format" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dsns "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/namespace" dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index bb1525d17..6003172fd 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -6,14 +6,14 @@ import ( "fmt" "testing" - blocks "github.com/ipfs/go-block-format" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" ds_sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func TestGetWhenKeyNotPresent(t *testing.T) { diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 0f2028c9a..cda44785c 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -5,10 +5,10 @@ import ( "sync/atomic" "time" - "github.com/ipfs/go-block-format" + "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" bloom "gx/ipfs/QmeiMCBkYHxkDkDfnDadzz4YxY5ruL5Pj499essE4vRsGM/bbloom" ) diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 51a41a115..275244af4 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/ipfs/go-block-format" + "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" context "context" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 57c6741ca..3d2d0430b 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -5,8 +5,8 @@ import ( "fmt" "io" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" bs "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/pin" From c6d21725a341f05654bed14d0b9b24e8eca0450c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 18 Jun 2017 13:07:24 -0700 Subject: [PATCH 1878/3817] blocks: gx import go-block-format And updated related dependencies. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@1c60426310df5573a88235818bbcf2319262c87f --- filestore/filestore.go | 4 ++-- filestore/filestore_test.go | 2 +- filestore/fsrefstore.go | 4 ++-- filestore/util.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 3472b2059..46f9c3fad 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -10,13 +10,13 @@ package filestore import ( "context" - "github.com/ipfs/go-block-format" "github.com/ipfs/go-ipfs/blocks/blockstore" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" + "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 5569c61b5..2003827cf 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -11,8 +11,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 89e50344a..edfdd9322 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -7,17 +7,17 @@ import ( "os" "path/filepath" - "github.com/ipfs/go-block-format" "github.com/ipfs/go-ipfs/blocks/blockstore" pb "github.com/ipfs/go-ipfs/filestore/pb" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" + "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dsns "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/namespace" dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index 9a13de39a..8abaead11 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -8,9 +8,9 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) // Status is used to identify the state of the block data referenced From 27a3e69810f36f2c07f8db637c972c2cc27c1041 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 19 Jun 2017 19:11:32 -0700 Subject: [PATCH 1879/3817] gx import/update libp2p/go-libp2p-routing For some reason, this was referenced but wasn't listed in packages.json. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@b4713a3f06216a87aebdb8bde664111b7f9fc41b --- namesys/namesys.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/routing.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 1f662bd82..7da3720f2 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,9 +7,9 @@ import ( path "github.com/ipfs/go-ipfs/path" - routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index cba463492..5d2405461 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -14,13 +14,13 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ft "github.com/ipfs/go-ipfs/unixfs" - routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" dhtpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index b33328b68..a7c3af44d 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,7 +11,6 @@ import ( path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" ic "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" @@ -19,6 +18,7 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" recpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/namesys/routing.go b/namesys/routing.go index 1f7177a1b..07f264557 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -9,7 +9,6 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" @@ -17,6 +16,7 @@ import ( lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" ) var log = logging.Logger("namesys") From d858029dbf63e9d9f2ecfceea51dad7ea0cdc216 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 18 Jun 2017 13:07:24 -0700 Subject: [PATCH 1880/3817] blocks: gx import go-block-format And updated related dependencies. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@edfc03bf080c46552f1cd6abaadc424501884cd3 --- blockservice/blockservice.go | 4 ++-- blockservice/blockservice_test.go | 2 +- blockservice/test/blocks_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 14c445f89..4adb979e1 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -8,12 +8,12 @@ import ( "errors" "fmt" - blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" + blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) var log = logging.Logger("blockservice") diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index 3085a3444..6650f2f7a 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -3,10 +3,10 @@ package blockservice import ( "testing" - "github.com/ipfs/go-block-format" "github.com/ipfs/go-ipfs/blocks/blockstore" butil "github.com/ipfs/go-ipfs/blocks/blocksutil" offline "github.com/ipfs/go-ipfs/exchange/offline" + "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 0b66202bc..adc89a4f1 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -7,15 +7,15 @@ import ( "testing" "time" - blocks "github.com/ipfs/go-block-format" blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" + blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func newObject(data []byte) blocks.Block { From 88c5aab4dad3889e8515b1fae9418e8b2e938537 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 19 Jun 2017 19:11:32 -0700 Subject: [PATCH 1881/3817] gx import/update libp2p/go-libp2p-routing For some reason, this was referenced but wasn't listed in packages.json. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@38b54c6b5af1379658c11eae4618af6367290178 --- routing/mock/centralized_client.go | 2 +- routing/mock/interface.go | 2 +- routing/none/none_client.go | 2 +- routing/offline/offline.go | 2 +- routing/supernode/client.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index a047c1dae..41cd6e318 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,7 +8,6 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" "github.com/ipfs/go-ipfs/thirdparty/testutil" - routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" @@ -16,6 +15,7 @@ import ( u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" ma "gx/ipfs/QmcyqRMCAXVtYPS4DiBrA7sezL9rRGfW8Ctx7cywL4TXJj/go-multiaddr" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 4e9f3e8d5..448c4fd32 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,8 +10,8 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index c803aea6d..2eb769dbc 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,10 +6,10 @@ import ( repo "github.com/ipfs/go-ipfs/repo" - routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" p2phost "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" + routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 7b1392dfb..7bebb3923 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,7 +7,6 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" @@ -15,6 +14,7 @@ import ( pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 251489a66..2aed305f2 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,7 +8,6 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" dhtpb "gx/ipfs/QmUisRCuGWoJM7WtVQDYT2jrNxUtfZMJzvVFTogzdwv7uV/go-libp2p-kad-dht/pb" @@ -17,6 +16,7 @@ import ( pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) From 4153d2165aa83d3b923a53ba77663ecb45ae579a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 18 Jun 2017 13:07:24 -0700 Subject: [PATCH 1882/3817] blocks: gx import go-block-format And updated related dependencies. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@f368840e159d875701d4a3eb1854417a7741e432 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 2 +- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index f92e8eead..030131bf7 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -9,8 +9,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) // Result represents an incremental output from a garbage collection diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 4270884d9..d66476739 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -12,10 +12,10 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" dutils "github.com/ipfs/go-ipfs/merkledag/utils" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 072761f0a..d6e4f3b0a 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -10,10 +10,10 @@ import ( "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func randNode() (*mdag.ProtoNode, *cid.Cid) { diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 472142b5c..47301bea0 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -12,9 +12,9 @@ import ( "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index f31cb890f..37741bf27 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -10,9 +10,9 @@ import ( offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func ignoreCids(_ *cid.Cid) {} From 91653bbff5ae15c6838c1097a33d31c76bcdda4f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 18 Jun 2017 13:07:24 -0700 Subject: [PATCH 1883/3817] blocks: gx import go-block-format And updated related dependencies. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@560403fac54425a1286285d179869cb751449ca9 --- mfs/dir.go | 4 ++-- mfs/file.go | 2 +- mfs/mfs_test.go | 4 ++-- mfs/ops.go | 2 +- mfs/repub_test.go | 2 +- mfs/system.go | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index fdfb49538..2b208f76b 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -15,8 +15,8 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) var ErrNotYetImplemented = errors.New("not yet implemented") diff --git a/mfs/file.go b/mfs/file.go index 02a5b62c8..8d5bafc7b 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index fa2e7c53d..2b39b3768 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -24,11 +24,11 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) func emptyDirNode() *dag.ProtoNode { diff --git a/mfs/ops.go b/mfs/ops.go index 0d02cbb08..3b8d8ffed 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -9,7 +9,7 @@ import ( path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) // Mv moves the file or directory at 'src' to 'dst' diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 832bee0d2..37df12c10 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -7,7 +7,7 @@ import ( ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" "context" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index 934a32610..832acc344 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -19,9 +19,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) var ErrNotExist = errors.New("no such rootfs") From 4d48747261e6d3d16264ed454db2fa54352f20ae Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 18 Jun 2017 13:07:24 -0700 Subject: [PATCH 1884/3817] blocks: gx import go-block-format And updated related dependencies. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-ds-help@91ba2644abaa4818eb188abc3a676ceebdc0e209 --- datastore/dshelp/key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 0c4fab85b..17f519368 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -1,8 +1,8 @@ package dshelp import ( + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" base32 "gx/ipfs/QmZvZSVtvxak4dcTkhsQhqd1SQ6rg5UzaSTu62WfWKjj93/base32" ) From 275547cf15e72755b43cb3b0d8a0f1805cddcaea Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 18 Jun 2017 13:07:24 -0700 Subject: [PATCH 1885/3817] blocks: gx import go-block-format And updated related dependencies. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-exchange-offline@97bc28e43eb6550fb3e8b21e39d41949b7455267 --- exchange/offline/offline.go | 4 ++-- exchange/offline/offline_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index a7507943a..7399e1a31 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -5,11 +5,11 @@ package offline import ( "context" - blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" + blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index a143183bb..efdbf1b2d 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -4,14 +4,14 @@ import ( "context" "testing" - blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" + blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" ds_sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" ) func TestBlockReturnsErr(t *testing.T) { From 1cdb8a4c10049038b4d262607fbcc0dfa081b0ee Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 18 Jun 2017 13:07:24 -0700 Subject: [PATCH 1886/3817] blocks: gx import go-block-format And updated related dependencies. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-exchange-interface@f6584cf10a7277016faa9a0819150e94f211bdef --- exchange/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index becb88c1a..ea0086ffc 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -5,9 +5,9 @@ import ( "context" "io" - blocks "github.com/ipfs/go-block-format" + blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ) // Any type that implements exchange.Interface may be used as an IPFS block From 745603197b6b3ac23702e7fab84eb7cb2b565c8c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 18 Jun 2017 13:07:24 -0700 Subject: [PATCH 1887/3817] blocks: gx import go-block-format And updated related dependencies. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@ee13c792b06acd8b87a3c2f4e819769b464909d1 --- unixfs/archive/archive.go | 2 +- unixfs/archive/tar/writer.go | 2 +- unixfs/hamt/hamt.go | 4 ++-- unixfs/io/dagreader.go | 2 +- unixfs/io/dirbuilder.go | 4 ++-- unixfs/io/resolve.go | 2 +- unixfs/mod/dagmodifier.go | 4 ++-- unixfs/test/utils.go | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index c28b2fe68..c2db3779f 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -11,7 +11,7 @@ import ( tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) // DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 5498c463e..af00093bd 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -13,8 +13,8 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" upb "github.com/ipfs/go-ipfs/unixfs/pb" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) // Writer is a utility structure that helps to write diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index d0b60a9c6..ebb37fd9b 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -31,9 +31,9 @@ import ( format "github.com/ipfs/go-ipfs/unixfs" upb "github.com/ipfs/go-ipfs/unixfs/pb" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" "gx/ipfs/QmfJHywXQu98UeZtGJBQrPAR6AtmDjjbe3qjTo9piXHPnx/murmur3" ) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 9abfe0c9d..db549b381 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -10,8 +10,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) var ErrIsDir = errors.New("this dag node is a directory") diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 8d8509763..379cde295 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -8,9 +8,9 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) // ShardSplitThreshold specifies how large of an unsharded directory diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index f9213b55c..b73bd3561 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -7,7 +7,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) // ResolveUnixfsOnce resolves a single hop of a path through a graph in a diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index f99453c8d..832b0a7bc 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -14,9 +14,9 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) var ErrSeekFail = errors.New("failed to seek properly") diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index ada15a086..72eed5dd1 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -14,8 +14,8 @@ import ( mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) func SizeSplitterGen(size int64) chunk.SplitterGen { From a4b8bb12c172151aaf889734fba353178bfeedd8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 18 Jun 2017 13:07:24 -0700 Subject: [PATCH 1888/3817] blocks: gx import go-block-format And updated related dependencies. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-path@9fc2f8147b50898e33111348c8cf94040bfabdb4 --- path/path.go | 2 +- path/resolver.go | 4 ++-- path/resolver_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/path/path.go b/path/path.go index 419f6c9c9..a44a8d99b 100644 --- a/path/path.go +++ b/path/path.go @@ -5,7 +5,7 @@ import ( "path" "strings" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ) // ErrBadPath is returned when a given path is incorrectly formatted diff --git a/path/resolver.go b/path/resolver.go index 22bde65ee..84a39a84a 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -9,9 +9,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) var log = logging.Logger("path") diff --git a/path/resolver_test.go b/path/resolver_test.go index 6e7ad64de..dae766b78 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -9,8 +9,8 @@ import ( dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" + node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" util "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" ) func randNode() *merkledag.ProtoNode { From 3ccc38e85a712d99e50b0a6e2e518bbbaf4bc099 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 18 Jun 2017 13:07:24 -0700 Subject: [PATCH 1889/3817] blocks: gx import go-block-format And updated related dependencies. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-chunker@6a8b93c1fd8eec7f2724891dec0e76e13a80fac1 --- chunker/rabin_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 3605a3dd3..534c5c948 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -3,8 +3,8 @@ package chunk import ( "bytes" "fmt" - "github.com/ipfs/go-block-format" "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" "io" "testing" ) From 8fd5b213838670d5fed83a6ca39240179ab8cf3f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 18 Jun 2017 13:07:24 -0700 Subject: [PATCH 1890/3817] blocks: gx import go-block-format And updated related dependencies. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@477bd882b4bdd348fd7ef8f35c81f5a11a732199 --- coreiface/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index a7762c8c2..273d8e25a 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -5,8 +5,8 @@ import ( "errors" "io" - cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" - ipld "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format" + cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + ipld "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) type Path interface { From 89935fa297b00736213be6c785adae72e3f0749e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 1 Jul 2017 02:56:22 +0200 Subject: [PATCH 1891/3817] blocks: update go-ipld-cbor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-merkledag@28ae5ac0bd1b27709f1378390358542033462b06 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 03485a909..a761d198c 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -13,7 +13,7 @@ import ( cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" - ipldcbor "gx/ipfs/QmeYUiuN29RaXEK79Arqe9iBaek6xExz4iikREQq9bWNGM/go-ipld-cbor" + ipldcbor "gx/ipfs/QmYAfx21gPrN2hxNUmsuP6GqLWpxPn351st6rRH3fNuiMU/go-ipld-cbor" ) var ErrNotFound = fmt.Errorf("merkledag: not found") From e34f7d06977c7a51d162cd6b1a14ed91992302ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 3 Jul 2017 20:17:03 +0200 Subject: [PATCH 1892/3817] Update go-datastore to 1.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-blockstore@7ef096ce1fc889321b98e6d3a23ffee16a47fc67 --- blockstore/arc_cache.go | 2 +- blockstore/arc_cache_test.go | 4 ++-- blockstore/blockstore.go | 6 +++--- blockstore/blockstore_test.go | 6 +++--- blockstore/bloom_cache_test.go | 6 +++--- blockstore/util/remove.go | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 8eb48fc18..6ee0d52e7 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -6,8 +6,8 @@ import ( "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" ) diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index c2aaf9314..c6d2df3f7 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -7,8 +7,8 @@ import ( "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - syncds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + syncds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" ) var exampleBlock = blocks.NewBlock([]byte("foo")) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index ad799df88..078a867a4 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -12,9 +12,9 @@ import ( blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dsns "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/namespace" - dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + dsns "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/namespace" + dsq "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/query" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ) diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 6003172fd..c05e60282 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -10,9 +10,9 @@ import ( blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" - ds_sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + dsq "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/query" + ds_sync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" ) diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 275244af4..ea9d690cc 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -9,9 +9,9 @@ import ( "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" context "context" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" - syncds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + dsq "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/query" + syncds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" ) func testBloomCached(ctx context.Context, bs Blockstore) (*bloomcache, error) { diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 3d2d0430b..b5b58469f 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -6,7 +6,7 @@ import ( "io" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" bs "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/pin" From 805266eb202ee877058d514db4dacbce4b304899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 3 Jul 2017 20:17:03 +0200 Subject: [PATCH 1893/3817] Update go-datastore to 1.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-filestore@ef991dc0f92250257f83fbe82352ade8dd9ee9cb --- filestore/filestore.go | 2 +- filestore/filestore_test.go | 2 +- filestore/fsrefstore.go | 6 +++--- filestore/util.go | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 46f9c3fad..7867cee0e 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -15,7 +15,7 @@ import ( "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" + dsq "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/query" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 2003827cf..f3a277246 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -12,7 +12,7 @@ import ( posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index edfdd9322..fb560b2e0 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -14,9 +14,9 @@ import ( "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dsns "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/namespace" - dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + dsns "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/namespace" + dsq "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/query" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" ) diff --git a/filestore/util.go b/filestore/util.go index 8abaead11..3437b3a29 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -9,8 +9,8 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + dsq "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/query" ) // Status is used to identify the state of the block data referenced From 22206301dfff1c3abb612ca3bffed7f048564ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 3 Jul 2017 20:17:03 +0200 Subject: [PATCH 1894/3817] Update go-datastore to 1.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@8139fe03a030ef55b8eb820a683b9508e3cac0a4 --- namesys/namesys.go | 2 +- namesys/namesys_test.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/resolve_test.go | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 7da3720f2..735a18a13 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 7df4ac926..4c5a3a2f3 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -11,7 +11,7 @@ import ( "github.com/ipfs/go-ipfs/unixfs" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" ) type mockResolver struct { diff --git a/namesys/publisher.go b/namesys/publisher.go index 5d2405461..8cf5ea4f0 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -15,7 +15,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" dhtpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index a7c3af44d..9c573b917 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -12,9 +12,9 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ic "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" recpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index c7e13e853..45d7590b9 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -10,8 +10,8 @@ import ( mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + dssync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) From f75867b1083d6462aefeb6343cee431ed8d1ac0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 3 Jul 2017 20:17:03 +0200 Subject: [PATCH 1895/3817] Update go-datastore to 1.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-blockservice@084f57e0655ff36e4f2520dc538ca19199ef97a1 --- blockservice/blockservice_test.go | 4 ++-- blockservice/test/blocks_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index 6650f2f7a..123ea9a05 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -8,8 +8,8 @@ import ( offline "github.com/ipfs/go-ipfs/exchange/offline" "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + dssync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" ) func TestWriteThroughWorks(t *testing.T) { diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index adc89a4f1..c361212f0 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -13,8 +13,8 @@ import ( blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + dssync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" ) From 766f3168c16ed4316f047428ec15ccdc779f08d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 3 Jul 2017 20:17:03 +0200 Subject: [PATCH 1896/3817] Update go-datastore to 1.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-merkledag@0053a47aabe8ebc810afead64a84b4fdca89b5cc --- ipld/merkledag/test/utils.go | 4 ++-- ipld/merkledag/utils/utils.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index c004d9a36..74879dc32 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -5,8 +5,8 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + dssync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" ) func Mock() dag.DAGService { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index cf50da80f..2c8d0752b 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -10,8 +10,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - syncds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + syncds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) From 400288204942ed8f5715bcb3bbbc0e4422533302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 3 Jul 2017 20:17:03 +0200 Subject: [PATCH 1897/3817] Update go-datastore to 1.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-routing@78767a6deae5b06d0c12dc02f0c09c2e154a218a --- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 4 ++-- routing/mock/dht.go | 6 +++--- routing/mock/interface.go | 2 +- routing/offline/offline.go | 2 +- routing/offline/offline_test.go | 2 +- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 4 ++-- routing/supernode/server_test.go | 4 ++-- 11 files changed, 16 insertions(+), 16 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 41cd6e318..28a28608d 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -9,7 +9,7 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" dhtpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 77759d085..1ca5ac17f 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -9,8 +9,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + dssync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 51bd21118..8c0519b8b 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" + dht "gx/ipfs/QmNaAVhp2UXfeDTLhHRUxjB69Tpku38ovSmQegcAMoJXbY/go-libp2p-kad-dht" mocknet "gx/ipfs/QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime/go-libp2p/p2p/net/mock" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" - dht "gx/ipfs/QmUisRCuGWoJM7WtVQDYT2jrNxUtfZMJzvVFTogzdwv7uV/go-libp2p-kad-dht" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + sync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 448c4fd32..128ad9fbc 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,7 +10,7 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 7bebb3923..38b0f4eb8 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -9,7 +9,7 @@ import ( cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index f4ccb2729..0e0bc7d59 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" "testing" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 2aed305f2..34b6f2bc3 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,9 +8,9 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" + dhtpb "gx/ipfs/QmNaAVhp2UXfeDTLhHRUxjB69Tpku38ovSmQegcAMoJXbY/go-libp2p-kad-dht/pb" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - dhtpb "gx/ipfs/QmUisRCuGWoJM7WtVQDYT2jrNxUtfZMJzvVFTogzdwv7uV/go-libp2p-kad-dht/pb" "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" loggables "gx/ipfs/QmVesPmqbPp7xRGyY96tnBwzDtVV1nqv4SCVxo5zCqKyH8/go-libp2p-loggables" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 6fa5a8391..0eb0615ad 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,8 +2,8 @@ package proxy import ( context "context" + dhtpb "gx/ipfs/QmNaAVhp2UXfeDTLhHRUxjB69Tpku38ovSmQegcAMoJXbY/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmRscs8KxrSmSv4iuevHv8JfuUzHBMoqiaHzxfDRiksd6e/go-libp2p-net" - dhtpb "gx/ipfs/QmUisRCuGWoJM7WtVQDYT2jrNxUtfZMJzvVFTogzdwv7uV/go-libp2p-kad-dht/pb" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 04c68c5f6..2193a268c 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,9 +4,9 @@ import ( "context" "errors" + dhtpb "gx/ipfs/QmNaAVhp2UXfeDTLhHRUxjB69Tpku38ovSmQegcAMoJXbY/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmRscs8KxrSmSv4iuevHv8JfuUzHBMoqiaHzxfDRiksd6e/go-libp2p-net" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - dhtpb "gx/ipfs/QmUisRCuGWoJM7WtVQDYT2jrNxUtfZMJzvVFTogzdwv7uV/go-libp2p-kad-dht/pb" host "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" loggables "gx/ipfs/QmVesPmqbPp7xRGyY96tnBwzDtVV1nqv4SCVxo5zCqKyH8/go-libp2p-loggables" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 610daa854..ffc924a43 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,8 +8,8 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dhtpb "gx/ipfs/QmUisRCuGWoJM7WtVQDYT2jrNxUtfZMJzvVFTogzdwv7uV/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmNaAVhp2UXfeDTLhHRUxjB69Tpku38ovSmQegcAMoJXbY/go-libp2p-kad-dht/pb" + datastore "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index b090d272d..10570d325 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" - datastore "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dhtpb "gx/ipfs/QmUisRCuGWoJM7WtVQDYT2jrNxUtfZMJzvVFTogzdwv7uV/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmNaAVhp2UXfeDTLhHRUxjB69Tpku38ovSmQegcAMoJXbY/go-libp2p-kad-dht/pb" + datastore "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From dc004c084aa07931c9910c9b001ef534a744e026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 3 Jul 2017 20:17:03 +0200 Subject: [PATCH 1898/3817] Update go-datastore to 1.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-pinner@632ca03c5f550da90fb5729deab39b0115486833 --- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index d66476739..2ea244848 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -13,7 +13,7 @@ import ( dutils "github.com/ipfs/go-ipfs/merkledag/utils" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index d6e4f3b0a..93c224353 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -11,8 +11,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + dssync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 37741bf27..5dd9c0083 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -11,8 +11,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dsq "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/query" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + dsq "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/query" ) func ignoreCids(_ *cid.Cid) {} From 0c1f9d247ce5b2818c445520d59347116cc227a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 3 Jul 2017 20:17:03 +0200 Subject: [PATCH 1899/3817] Update go-datastore to 1.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-mfs@011f45e4b1d8a0db75b4695636defed77338d65c --- mfs/mfs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 2b39b3768..77ac5f682 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -25,8 +25,8 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - dssync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + dssync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" ) From 007731ca66e5f213dd5b86761075f3799c083dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 3 Jul 2017 20:17:03 +0200 Subject: [PATCH 1900/3817] Update go-datastore to 1.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-ds-help@6360b841854ff7c6bc83deb2d85fd22f3681b1f1 --- datastore/dshelp/key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 17f519368..5e92b0603 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -2,7 +2,7 @@ package dshelp import ( cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" base32 "gx/ipfs/QmZvZSVtvxak4dcTkhsQhqd1SQ6rg5UzaSTu62WfWKjj93/base32" ) From f5d13978fb2528fd6449630a72ba943640f20ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 3 Jul 2017 20:17:03 +0200 Subject: [PATCH 1901/3817] Update go-datastore to 1.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-exchange-offline@6f11f9a22884daca8f38259cbbf88ad8a68c8904 --- exchange/offline/offline_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index efdbf1b2d..10e685066 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -9,8 +9,8 @@ import ( blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore" - ds_sync "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore/sync" + ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + ds_sync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" ) From 0969d708ee9fc3ecbb79ffe1669ffda58da3c569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:18:57 +0200 Subject: [PATCH 1902/3817] Update go-datastore to 1.2.2, go-cid to 0.7.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-blockstore@cb9c119c891dead778a092eed6669a01834f3914 --- blockstore/arc_cache.go | 6 +++--- blockstore/arc_cache_test.go | 8 ++++---- blockstore/blockstore.go | 10 +++++----- blockstore/blockstore_test.go | 10 +++++----- blockstore/bloom_cache.go | 4 ++-- blockstore/bloom_cache_test.go | 8 ++++---- blockstore/util/remove.go | 4 ++-- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 6ee0d52e7..1e9d46464 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -3,12 +3,12 @@ package blockstore import ( "context" - "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) // arccache wraps a BlockStore with an Adaptive Replacement Cache (ARC) for diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index c6d2df3f7..c1868814b 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -4,11 +4,11 @@ import ( "context" "testing" - "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - syncds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + syncds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) var exampleBlock = blocks.NewBlock([]byte("foo")) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 078a867a4..96b5c9407 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -9,13 +9,13 @@ import ( "sync/atomic" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - dsns "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/namespace" - dsq "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/query" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dsns "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/namespace" + dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index c05e60282..866fd40cd 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -7,13 +7,13 @@ import ( "testing" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - dsq "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/query" - ds_sync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" + ds_sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func TestGetWhenKeyNotPresent(t *testing.T) { diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index cda44785c..1467ad31f 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -5,10 +5,10 @@ import ( "sync/atomic" "time" - "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" bloom "gx/ipfs/QmeiMCBkYHxkDkDfnDadzz4YxY5ruL5Pj499essE4vRsGM/bbloom" ) diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index ea9d690cc..85ed29dc9 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -6,12 +6,12 @@ import ( "testing" "time" - "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" context "context" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - dsq "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/query" - syncds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" + syncds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" ) func testBloomCached(ctx context.Context, bs Blockstore) (*bloomcache, error) { diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index b5b58469f..defe9347d 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -5,8 +5,8 @@ import ( "fmt" "io" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" bs "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/pin" From 45c59a2733efafc1f87a9abc8cb5009be84f8016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:18:57 +0200 Subject: [PATCH 1903/3817] Update go-datastore to 1.2.2, go-cid to 0.7.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-filestore@7ca8c39c57d3b02c013eb612a8d81413a86c5405 --- filestore/filestore.go | 6 +++--- filestore/filestore_test.go | 4 ++-- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 7867cee0e..c1f2df981 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -12,11 +12,11 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" - "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - dsq "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/query" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index f3a277246..2bbc43a26 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -11,8 +11,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index fb560b2e0..58d18aed9 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -11,13 +11,13 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" - "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - dsns "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/namespace" - dsq "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/query" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dsns "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/namespace" + dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index 3437b3a29..6a1f90d17 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -8,9 +8,9 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - dsq "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/query" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) // Status is used to identify the state of the block data referenced From d8366833ea30ef9b763feecefc04e5ce8109ba6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:18:57 +0200 Subject: [PATCH 1904/3817] Update go-datastore to 1.2.2, go-cid to 0.7.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@78a2140357f280f533756cb50ee55bd7c061257a --- namesys/namesys.go | 4 ++-- namesys/namesys_test.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 4 ++-- namesys/resolve_test.go | 4 ++-- namesys/routing.go | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 735a18a13..33883eb39 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -8,8 +8,8 @@ import ( path "github.com/ipfs/go-ipfs/path" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" + routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 4c5a3a2f3..a3cda90be 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -11,7 +11,7 @@ import ( "github.com/ipfs/go-ipfs/unixfs" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" ) type mockResolver struct { diff --git a/namesys/publisher.go b/namesys/publisher.go index 8cf5ea4f0..31fbbc1da 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -15,12 +15,12 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" dhtpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 9c573b917..a8fa74d04 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -12,13 +12,13 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ic "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" + routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" recpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 45d7590b9..6eeb958c8 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -10,8 +10,8 @@ import ( mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - dssync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/namesys/routing.go b/namesys/routing.go index 07f264557..45eb38557 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -9,14 +9,14 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" + routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" mh "gx/ipfs/QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw/go-multihash" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) var log = logging.Logger("namesys") From b4ba8e81352d9409811b92a1e9dd032f2cbef406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:18:57 +0200 Subject: [PATCH 1905/3817] Update go-datastore to 1.2.2, go-cid to 0.7.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-blockservice@3d6dfaad59752d198e35bc8a62c9b59f6074f287 --- blockservice/blockservice.go | 4 ++-- blockservice/blockservice_test.go | 6 +++--- blockservice/test/blocks_test.go | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 4adb979e1..98afac842 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -10,10 +10,10 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) var log = logging.Logger("blockservice") diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index 123ea9a05..db3b78ecc 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -6,10 +6,10 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" butil "github.com/ipfs/go-ipfs/blocks/blocksutil" offline "github.com/ipfs/go-ipfs/exchange/offline" - "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - dssync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" ) func TestWriteThroughWorks(t *testing.T) { diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index c361212f0..45ac82955 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -10,12 +10,12 @@ import ( blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - dssync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func newObject(data []byte) blocks.Block { From 3338a855d79b813c0bf56a2ff381bcd218575a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:18:57 +0200 Subject: [PATCH 1906/3817] Update go-datastore to 1.2.2, go-cid to 0.7.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-merkledag@7eaacd3ae48d88efb8cd7f30e0015ba249dc70d6 --- ipld/merkledag/coding.go | 4 ++-- ipld/merkledag/merkledag.go | 8 ++++---- ipld/merkledag/merkledag_test.go | 6 +++--- ipld/merkledag/node.go | 4 ++-- ipld/merkledag/node_test.go | 2 +- ipld/merkledag/raw.go | 6 +++--- ipld/merkledag/test/utils.go | 4 ++-- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/traverse/traverse_test.go | 2 +- ipld/merkledag/utils/diff.go | 4 ++-- ipld/merkledag/utils/diffenum.go | 4 ++-- ipld/merkledag/utils/diffenum_test.go | 4 ++-- ipld/merkledag/utils/utils.go | 6 +++--- ipld/merkledag/utils/utils_test.go | 2 +- 14 files changed, 29 insertions(+), 29 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 3674a3674..063003ac7 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -6,8 +6,8 @@ import ( pb "github.com/ipfs/go-ipfs/merkledag/pb" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index a761d198c..d23a42d07 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -9,11 +9,11 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" - ipldcbor "gx/ipfs/QmYAfx21gPrN2hxNUmsuP6GqLWpxPn351st6rRH3fNuiMU/go-ipld-cbor" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + ipldcbor "gx/ipfs/Qmcdid3XrCxcoNQUqZKiiKtM7JXxtyipU3izyRqwjFbVWw/go-ipld-cbor" ) var ErrNotFound = fmt.Errorf("merkledag: not found") diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 0e5d90fc8..e3a41bd15 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -22,11 +22,11 @@ import ( mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" - blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func TestNode(t *testing.T) { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index bd9322d36..8f9a6257b 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -5,9 +5,9 @@ import ( "encoding/json" "fmt" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" mh "gx/ipfs/QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw/go-multihash" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index ca0d06e95..12a94ab7c 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -8,7 +8,7 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" ) func TestRemoveLink(t *testing.T) { diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 01558b18f..3270b30ee 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -1,11 +1,11 @@ package merkledag import ( - "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) type RawNode struct { diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index 74879dc32..68e257265 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -5,8 +5,8 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - dssync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" ) func Mock() dag.DAGService { diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index 25a90f461..d18429b13 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -5,7 +5,7 @@ import ( "context" "errors" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" ) // Order is an identifier for traversal algorithm orders diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 40d1d739b..7f4f93fa5 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -8,7 +8,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" ) func TestDFSPreNoSkip(t *testing.T) { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 4d079301c..c497c426b 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -7,8 +7,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) const ( diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go index 9fb8de9c4..1e656e4e4 100644 --- a/ipld/merkledag/utils/diffenum.go +++ b/ipld/merkledag/utils/diffenum.go @@ -6,8 +6,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) // DiffEnumerate fetches every object in the graph pointed to by 'to' that is diff --git a/ipld/merkledag/utils/diffenum_test.go b/ipld/merkledag/utils/diffenum_test.go index c840283c1..fad992a64 100644 --- a/ipld/merkledag/utils/diffenum_test.go +++ b/ipld/merkledag/utils/diffenum_test.go @@ -8,8 +8,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func buildNode(name string, desc map[string]ndesc, out map[string]node.Node) node.Node { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 2c8d0752b..651101908 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -10,9 +10,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - syncds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + syncds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" ) type Editor struct { diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 1a0bf2c4d..c05398440 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" context "context" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func TestAddLink(t *testing.T) { From 5f45c72dd2065d67422b862acd49f51423d9956d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:18:57 +0200 Subject: [PATCH 1907/3817] Update go-datastore to 1.2.2, go-cid to 0.7.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-exchange-interface@e5970566436509ba4f09054ad89a15dce6e705bb --- exchange/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index ea0086ffc..fb590d25a 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -5,9 +5,9 @@ import ( "context" "io" - blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) // Any type that implements exchange.Interface may be used as an IPFS block From 122b918d64f16370faf891efeeec97aa4e3bf380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:18:57 +0200 Subject: [PATCH 1908/3817] Update go-datastore to 1.2.2, go-cid to 0.7.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-routing@240183f18ff810684445d81e6641180cd377bf5a --- routing/mock/centralized_client.go | 6 +++--- routing/mock/centralized_server.go | 6 +++--- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 6 +++--- routing/mock/interface.go | 4 ++-- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 6 +++--- routing/offline/offline_test.go | 2 +- routing/supernode/client.go | 6 +++--- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 4 ++-- routing/supernode/server_test.go | 4 ++-- 13 files changed, 27 insertions(+), 27 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 28a28608d..7886201b6 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,14 +8,14 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" "github.com/ipfs/go-ipfs/thirdparty/testutil" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dhtpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ma "gx/ipfs/QmcyqRMCAXVtYPS4DiBrA7sezL9rRGfW8Ctx7cywL4TXJj/go-multiaddr" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 1ca5ac17f..d65ab3ac1 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -8,10 +8,10 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - dssync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 401a571ec..5b2386236 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,9 +8,9 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 8c0519b8b..83a030ba6 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - dht "gx/ipfs/QmNaAVhp2UXfeDTLhHRUxjB69Tpku38ovSmQegcAMoJXbY/go-libp2p-kad-dht" mocknet "gx/ipfs/QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime/go-libp2p/p2p/net/mock" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - sync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + dht "gx/ipfs/QmfDWfnZZiwHSD58FE2PveLQhZZhZjbuvo1TU1Zu4P9Hd3/go-libp2p-kad-dht" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 128ad9fbc..ae46c1ea7 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,8 +10,8 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" + routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 2eb769dbc..cde9dd26f 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,10 +6,10 @@ import ( repo "github.com/ipfs/go-ipfs/repo" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" p2phost "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" - routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 38b0f4eb8..f492d73be 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,14 +7,14 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" ) diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index 0e0bc7d59..aaa44befc 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" "testing" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 34b6f2bc3..04936acaf 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,16 +8,16 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - dhtpb "gx/ipfs/QmNaAVhp2UXfeDTLhHRUxjB69Tpku38ovSmQegcAMoJXbY/go-libp2p-kad-dht/pb" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" loggables "gx/ipfs/QmVesPmqbPp7xRGyY96tnBwzDtVV1nqv4SCVxo5zCqKyH8/go-libp2p-loggables" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - routing "gx/ipfs/QmaNDbaV1wvPRLxTYepVsXrppXNjQ1NbrnG7ibAgKeyaXD/go-libp2p-routing" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" + dhtpb "gx/ipfs/QmfDWfnZZiwHSD58FE2PveLQhZZhZjbuvo1TU1Zu4P9Hd3/go-libp2p-kad-dht/pb" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 0eb0615ad..0452e7b7e 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,10 +2,10 @@ package proxy import ( context "context" - dhtpb "gx/ipfs/QmNaAVhp2UXfeDTLhHRUxjB69Tpku38ovSmQegcAMoJXbY/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmRscs8KxrSmSv4iuevHv8JfuUzHBMoqiaHzxfDRiksd6e/go-libp2p-net" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" + dhtpb "gx/ipfs/QmfDWfnZZiwHSD58FE2PveLQhZZhZjbuvo1TU1Zu4P9Hd3/go-libp2p-kad-dht/pb" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 2193a268c..c214643ee 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,7 +4,6 @@ import ( "context" "errors" - dhtpb "gx/ipfs/QmNaAVhp2UXfeDTLhHRUxjB69Tpku38ovSmQegcAMoJXbY/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmRscs8KxrSmSv4iuevHv8JfuUzHBMoqiaHzxfDRiksd6e/go-libp2p-net" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" host "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" @@ -13,6 +12,7 @@ import ( ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" kbucket "gx/ipfs/QmaQG6fJdzn2532WHoPdVwKqftXr6iCSr5NtWyGi1BHytT/go-libp2p-kbucket" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" + dhtpb "gx/ipfs/QmfDWfnZZiwHSD58FE2PveLQhZZhZjbuvo1TU1Zu4P9Hd3/go-libp2p-kad-dht/pb" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index ffc924a43..6ed1bfe1d 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,12 +8,12 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - dhtpb "gx/ipfs/QmNaAVhp2UXfeDTLhHRUxjB69Tpku38ovSmQegcAMoJXbY/go-libp2p-kad-dht/pb" - datastore "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" + dhtpb "gx/ipfs/QmfDWfnZZiwHSD58FE2PveLQhZZhZjbuvo1TU1Zu4P9Hd3/go-libp2p-kad-dht/pb" ) // Server handles routing queries using a database backend diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 10570d325..05dfb68f4 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmNaAVhp2UXfeDTLhHRUxjB69Tpku38ovSmQegcAMoJXbY/go-libp2p-kad-dht/pb" - datastore "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dhtpb "gx/ipfs/QmfDWfnZZiwHSD58FE2PveLQhZZhZjbuvo1TU1Zu4P9Hd3/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 5cd326ac165fc0f632f284157c8944af600e8189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:18:57 +0200 Subject: [PATCH 1909/3817] Update go-datastore to 1.2.2, go-cid to 0.7.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-pinner@b903fe09510faeb3db9c4731a577626fbdd87bde --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 6 +++--- pinning/pinner/pin_test.go | 6 +++--- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 6 +++--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 030131bf7..e838046f8 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -9,8 +9,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) // Result represents an incremental output from a garbage collection diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 2ea244848..47886ba6f 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -12,10 +12,10 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" dutils "github.com/ipfs/go-ipfs/merkledag/utils" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 93c224353..c0068705b 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -10,10 +10,10 @@ import ( "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - dssync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func randNode() (*mdag.ProtoNode, *cid.Cid) { diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 47301bea0..833f32cde 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -12,9 +12,9 @@ import ( "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 5dd9c0083..50e27a0e1 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -10,9 +10,9 @@ import ( offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - dsq "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/query" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func ignoreCids(_ *cid.Cid) {} From dad8f72a614dc9c51cb426d4cd1fe9592533b632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:18:57 +0200 Subject: [PATCH 1910/3817] Update go-datastore to 1.2.2, go-cid to 0.7.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-mfs@d912c24574f4b07ab8f05b645178642de696c3f8 --- mfs/dir.go | 4 ++-- mfs/file.go | 2 +- mfs/mfs_test.go | 8 ++++---- mfs/ops.go | 2 +- mfs/repub_test.go | 2 +- mfs/system.go | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 2b208f76b..c9cacb005 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -15,8 +15,8 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) var ErrNotYetImplemented = errors.New("not yet implemented") diff --git a/mfs/file.go b/mfs/file.go index 8d5bafc7b..71ca2ced6 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 77ac5f682..75f5c0012 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -24,11 +24,11 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - dssync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func emptyDirNode() *dag.ProtoNode { diff --git a/mfs/ops.go b/mfs/ops.go index 3b8d8ffed..bb99d1860 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -9,7 +9,7 @@ import ( path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" ) // Mv moves the file or directory at 'src' to 'dst' diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 37df12c10..d96edf07f 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -7,7 +7,7 @@ import ( ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" "context" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index 832acc344..88e1f2ff4 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -19,9 +19,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) var ErrNotExist = errors.New("no such rootfs") From 3f8b5380a264ecd561c922f6c6020eb1a88295fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:18:57 +0200 Subject: [PATCH 1911/3817] Update go-datastore to 1.2.2, go-cid to 0.7.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-unixfs@459773570529e2ba6ad14dedb5b2f4780619eca3 --- unixfs/archive/archive.go | 2 +- unixfs/archive/tar/writer.go | 2 +- unixfs/hamt/hamt.go | 4 ++-- unixfs/io/dagreader.go | 2 +- unixfs/io/dirbuilder.go | 4 ++-- unixfs/io/resolve.go | 2 +- unixfs/mod/dagmodifier.go | 4 ++-- unixfs/test/utils.go | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index c2db3779f..cf1d74ac9 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -11,7 +11,7 @@ import ( tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" ) // DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index af00093bd..01d7fd833 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -13,7 +13,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" upb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index ebb37fd9b..1a6b3d1f9 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -31,9 +31,9 @@ import ( format "github.com/ipfs/go-ipfs/unixfs" upb "github.com/ipfs/go-ipfs/unixfs/pb" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" "gx/ipfs/QmfJHywXQu98UeZtGJBQrPAR6AtmDjjbe3qjTo9piXHPnx/murmur3" ) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index db549b381..17cd6b4ca 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 379cde295..83b49df9d 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -8,9 +8,9 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" ) // ShardSplitThreshold specifies how large of an unsharded directory diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index b73bd3561..75d88a72b 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -7,7 +7,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" ) // ResolveUnixfsOnce resolves a single hop of a path through a graph in a diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 832b0a7bc..00a3c1c93 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -14,9 +14,9 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) var ErrSeekFail = errors.New("failed to seek properly") diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 72eed5dd1..cadc8081d 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -14,7 +14,7 @@ import ( mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" ) From 9cfe107e4867b816e6a6829b52887b22307536fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:18:57 +0200 Subject: [PATCH 1912/3817] Update go-datastore to 1.2.2, go-cid to 0.7.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-path@be59187d9e138960b010e39e6eddf1619cd1e4fd --- path/path.go | 2 +- path/resolver.go | 4 ++-- path/resolver_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/path/path.go b/path/path.go index a44a8d99b..bdb5ba156 100644 --- a/path/path.go +++ b/path/path.go @@ -5,7 +5,7 @@ import ( "path" "strings" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) // ErrBadPath is returned when a given path is incorrectly formatted diff --git a/path/resolver.go b/path/resolver.go index 84a39a84a..09703cd76 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -9,9 +9,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) var log = logging.Logger("path") diff --git a/path/resolver_test.go b/path/resolver_test.go index dae766b78..020861024 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -9,7 +9,7 @@ import ( dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" util "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" ) From d9bbb0d296c37a2b35df667d5854e83abf14eb3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:18:57 +0200 Subject: [PATCH 1913/3817] Update go-datastore to 1.2.2, go-cid to 0.7.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-chunker@c48cf1250bd65049d35d5b83d69e33f9d83c4f8c --- chunker/rabin_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 534c5c948..45f2b2061 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -4,7 +4,7 @@ import ( "bytes" "fmt" "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" "io" "testing" ) From 55a5ab8c4712e614e86258e8ddfd6904e24c34cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:18:57 +0200 Subject: [PATCH 1914/3817] Update go-datastore to 1.2.2, go-cid to 0.7.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-ds-help@506b965f6ff8dd56918bc54135ab438cedaf0ffc --- datastore/dshelp/key.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 5e92b0603..b0b2ebe98 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -1,9 +1,9 @@ package dshelp import ( - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" base32 "gx/ipfs/QmZvZSVtvxak4dcTkhsQhqd1SQ6rg5UzaSTu62WfWKjj93/base32" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) // TODO: put this code into the go-datastore itself From 9faf4c4d2a7c45a8f667ee69b09dda56526dbda5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:18:57 +0200 Subject: [PATCH 1915/3817] Update go-datastore to 1.2.2, go-cid to 0.7.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-exchange-offline@0b956ae92b8e08c29ff9e2e61b8e9388efb1e240 --- exchange/offline/offline.go | 4 ++-- exchange/offline/offline_test.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 7399e1a31..7c33b7af2 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -7,9 +7,9 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 10e685066..7055150ae 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -6,12 +6,12 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" - blocks "gx/ipfs/QmbJUay5h1HtzhJb5QQk2t26yCnJksHynvhcqp18utBPqG/go-block-format" + blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ds "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore" - ds_sync "gx/ipfs/QmSiN66ybp5udnQnvhb6euiWiiQWdGvwMhAWa95cC1DTCV/go-datastore/sync" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + ds_sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func TestBlockReturnsErr(t *testing.T) { From 624057e9d94d8d44cc424dca843027b47fa25ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:18:57 +0200 Subject: [PATCH 1916/3817] Update go-datastore to 1.2.2, go-cid to 0.7.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@e706b34ea178ca535760a511e3950510e6bb1355 --- coreiface/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 273d8e25a..363c5adac 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -5,8 +5,8 @@ import ( "errors" "io" - cid "gx/ipfs/QmNw61A6sJoXMeP37mJRtQZdNhj5e3FdjoTN3v4FyE96Gk/go-cid" - ipld "gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" + ipld "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) type Path interface { From c810f26b20efd058b689f7e4efec5f3a4dbe6da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:57:18 +0200 Subject: [PATCH 1917/3817] gx updates: Fix CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-blockstore@15125df8cbab26a02b73633072ad2fae071ecbf3 --- blockstore/blockstore.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 96b5c9407..905fcf029 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -193,8 +193,6 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) // KeysOnly, because that would be _a lot_ of data. q := dsq.Query{KeysOnly: true} - // datastore/namespace does *NOT* fix up Query.Prefix - q.Prefix = BlockPrefix.String() res, err := bs.datastore.Query(q) if err != nil { return nil, err From 025f0e16c986bb87e302873d36b792fe3960fb5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Jul 2017 20:57:18 +0200 Subject: [PATCH 1918/3817] gx updates: Fix CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-filestore@be1ec53b19946c75ef83a4d133eba65d4b6547f7 --- filestore/fsrefstore.go | 1 - 1 file changed, 1 deletion(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 58d18aed9..63978fbfd 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -59,7 +59,6 @@ func NewFileManager(ds ds.Batching, root string) *FileManager { // closed. func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { q := dsq.Query{KeysOnly: true} - q.Prefix = FilestorePrefix.String() res, err := f.ds.Query(q) if err != nil { From b1fd507caef9d0dc1a6264dec928a59e28075d9f Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 5 Jul 2017 13:34:49 +0200 Subject: [PATCH 1919/3817] test: fix race in namesys tests Resolves #4018 License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@0cd1de91610917df0611c5d9cf64488069ec6cd0 --- namesys/namesys_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index a3cda90be..40f3bbe74 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -12,6 +12,7 @@ import ( ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" ) type mockResolver struct { @@ -76,7 +77,7 @@ func TestNamesysResolution(t *testing.T) { } func TestPublishWithCache0(t *testing.T) { - dst := ds.NewMapDatastore() + dst := dssync.MutexWrap(ds.NewMapDatastore()) priv, _, err := ci.GenerateKeyPair(ci.RSA, 1024) if err != nil { t.Fatal(err) From 49ff30badfce48f788fd294c2c6d9376b4eebfa3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 4 May 2017 18:00:15 -0700 Subject: [PATCH 1920/3817] WIP: wire sessions up through into FetchGraph License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@7f34056f5ecbc3656666de41b91e5c4535caa2bb --- blockservice/blockservice.go | 55 ++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 98afac842..e034b46fb 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -10,9 +10,10 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) @@ -31,6 +32,7 @@ type BlockService interface { GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block DeleteBlock(o blocks.Block) error + NewSession(context.Context) *Session Close() error } @@ -77,6 +79,21 @@ func (bs *blockService) Exchange() exchange.Interface { return bs.exchange } +func (bs *blockService) NewSession(ctx context.Context) *Session { + bswap, ok := bs.Exchange().(*bitswap.Bitswap) + if ok { + ses := bswap.NewSession(ctx) + return &Session{ + ses: ses, + bs: bs.blockstore, + } + } + return &Session{ + ses: bs.exchange, + bs: bs.blockstore, + } +} + // AddBlock adds a particular block to the service, Putting it into the datastore. // TODO pass a context into this if the remote.HasBlock is going to remain here. func (s *blockService) AddBlock(o blocks.Block) (*cid.Cid, error) { @@ -141,16 +158,25 @@ func (s *blockService) AddBlocks(bs []blocks.Block) ([]*cid.Cid, error) { func (s *blockService) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) { log.Debugf("BlockService GetBlock: '%s'", c) - block, err := s.blockstore.Get(c) + var f exchange.Fetcher + if s.exchange != nil { + f = s.exchange + } + + return getBlock(ctx, c, s.blockstore, f) +} + +func getBlock(ctx context.Context, c *cid.Cid, bs blockstore.Blockstore, f exchange.Fetcher) (blocks.Block, error) { + block, err := bs.Get(c) if err == nil { return block, nil } - if err == blockstore.ErrNotFound && s.exchange != nil { + if err == blockstore.ErrNotFound && f != nil { // TODO be careful checking ErrNotFound. If the underlying // implementation changes, this will break. log.Debug("Blockservice: Searching bitswap") - blk, err := s.exchange.GetBlock(ctx, c) + blk, err := f.GetBlock(ctx, c) if err != nil { if err == blockstore.ErrNotFound { return nil, ErrNotFound @@ -172,12 +198,16 @@ func (s *blockService) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, // the returned channel. // NB: No guarantees are made about order. func (s *blockService) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block { + return getBlocks(ctx, ks, s.blockstore, s.exchange) +} + +func getBlocks(ctx context.Context, ks []*cid.Cid, bs blockstore.Blockstore, f exchange.Fetcher) <-chan blocks.Block { out := make(chan blocks.Block) go func() { defer close(out) var misses []*cid.Cid for _, c := range ks { - hit, err := s.blockstore.Get(c) + hit, err := bs.Get(c) if err != nil { misses = append(misses, c) continue @@ -194,7 +224,7 @@ func (s *blockService) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan bloc return } - rblocks, err := s.exchange.GetBlocks(ctx, misses) + rblocks, err := f.GetBlocks(ctx, misses) if err != nil { log.Debugf("Error with GetBlocks: %s", err) return @@ -220,3 +250,16 @@ func (s *blockService) Close() error { log.Debug("blockservice is shutting down...") return s.exchange.Close() } + +type Session struct { + bs blockstore.Blockstore + ses exchange.Fetcher +} + +func (s *Session) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) { + return getBlock(ctx, c, s.bs, s.ses) +} + +func (s *Session) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block { + return getBlocks(ctx, ks, s.bs, s.ses) +} From b2b674588dd17ff6c4d768a9061531ebcc0390c1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 4 May 2017 18:00:15 -0700 Subject: [PATCH 1921/3817] WIP: wire sessions up through into FetchGraph License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@f274b02d5bd99c042f4a1bb07c9221bfb5357ce2 --- ipld/merkledag/merkledag.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index d23a42d07..f6ee7e562 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -161,11 +161,30 @@ func GetLinksDirect(serv node.NodeGetter) GetLinks { } } +type sesGetter struct { + bs *bserv.Session +} + +func (sg *sesGetter) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { + blk, err := sg.bs.GetBlock(ctx, c) + if err != nil { + return nil, err + } + + return decodeBlock(blk) +} + // FetchGraph fetches all nodes that are children of the given node func FetchGraph(ctx context.Context, root *cid.Cid, serv DAGService) error { + var ng node.NodeGetter = serv + ds, ok := serv.(*dagService) + if ok { + ng = &sesGetter{ds.Blocks.NewSession(ctx)} + } + v, _ := ctx.Value("progress").(*ProgressTracker) if v == nil { - return EnumerateChildrenAsync(ctx, GetLinksDirect(serv), root, cid.NewSet().Visit) + return EnumerateChildrenAsync(ctx, GetLinksDirect(ng), root, cid.NewSet().Visit) } set := cid.NewSet() visit := func(c *cid.Cid) bool { @@ -176,7 +195,7 @@ func FetchGraph(ctx context.Context, root *cid.Cid, serv DAGService) error { return false } } - return EnumerateChildrenAsync(ctx, GetLinksDirect(serv), root, visit) + return EnumerateChildrenAsync(ctx, GetLinksDirect(ng), root, visit) } // FindLinks searches this nodes links for the given key, From 7a67e77797493060da873bd499f35a409b0a98bd Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 4 May 2017 18:00:15 -0700 Subject: [PATCH 1922/3817] WIP: wire sessions up through into FetchGraph License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-interface@b010499f1267097fe0aeb5dae4289648822e6b24 --- exchange/interface.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index fb590d25a..5b9135342 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -13,10 +13,7 @@ import ( // Any type that implements exchange.Interface may be used as an IPFS block // exchange protocol. type Interface interface { // type Exchanger interface - // GetBlock returns the block associated with a given key. - GetBlock(context.Context, *cid.Cid) (blocks.Block, error) - - GetBlocks(context.Context, []*cid.Cid) (<-chan blocks.Block, error) + Fetcher // TODO Should callers be concerned with whether the block was made // available on the network? @@ -26,3 +23,9 @@ type Interface interface { // type Exchanger interface io.Closer } + +type Fetcher interface { + // GetBlock returns the block associated with a given key. + GetBlock(context.Context, *cid.Cid) (blocks.Block, error) + GetBlocks(context.Context, []*cid.Cid) (<-chan blocks.Block, error) +} From 955823088e15561dbd46badd1d1104d64727db85 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 19 May 2017 21:04:11 -0700 Subject: [PATCH 1923/3817] track broadcasted wantlist entries License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@d27d0fef65dd2ca20a1fbe95b01efa3a699ba572 --- blockservice/blockservice.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index e034b46fb..c6ecbc386 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -251,15 +251,18 @@ func (s *blockService) Close() error { return s.exchange.Close() } +// Session is a helper type to provide higher level access to bitswap sessions type Session struct { bs blockstore.Blockstore ses exchange.Fetcher } +// GetBlock gets a block in the context of a request session func (s *Session) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) { return getBlock(ctx, c, s.bs, s.ses) } +// GetBlocks gets blocks in the context of a request session func (s *Session) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block { return getBlocks(ctx, ks, s.bs, s.ses) } From 534e30e0a927addbeaa0958f2dbfdf83cc5b8884 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 19 May 2017 21:04:11 -0700 Subject: [PATCH 1924/3817] track broadcasted wantlist entries License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@cc8384d6dc8101ae4b26684887f524fc46608217 --- ipld/merkledag/merkledag.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index f6ee7e562..9fa8446d8 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -155,6 +155,9 @@ func GetLinksDirect(serv node.NodeGetter) GetLinks { return func(ctx context.Context, c *cid.Cid) ([]*node.Link, error) { node, err := serv.Get(ctx, c) if err != nil { + if err == bserv.ErrNotFound { + err = ErrNotFound + } return nil, err } return node.Links(), nil From 82f9dd8ccaffbbecb04fd2a74a269e1a37a1f5ad Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 19 May 2017 21:04:11 -0700 Subject: [PATCH 1925/3817] track broadcasted wantlist entries License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-interface@84f2f5c22412ddaa834ee9fde01f64285d33a2e4 --- exchange/interface.go | 1 + 1 file changed, 1 insertion(+) diff --git a/exchange/interface.go b/exchange/interface.go index 5b9135342..ac494ff99 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -24,6 +24,7 @@ type Interface interface { // type Exchanger interface io.Closer } +// Fetcher is an object that can be used to retrieve blocks type Fetcher interface { // GetBlock returns the block associated with a given key. GetBlock(context.Context, *cid.Cid) (blocks.Block, error) From 80ffa9f9387e258b546b7755d727feddb5ef61f7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 5 Jul 2017 12:31:34 -0700 Subject: [PATCH 1926/3817] make NewSession in the blockservice be a function, not a method License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@5b4cb5695f5ce89f01e157dee6b2ac36349c76de --- blockservice/blockservice.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index c6ecbc386..d746f2fe0 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -32,7 +32,6 @@ type BlockService interface { GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block DeleteBlock(o blocks.Block) error - NewSession(context.Context) *Session Close() error } @@ -79,18 +78,18 @@ func (bs *blockService) Exchange() exchange.Interface { return bs.exchange } -func (bs *blockService) NewSession(ctx context.Context) *Session { - bswap, ok := bs.Exchange().(*bitswap.Bitswap) - if ok { +func NewSession(ctx context.Context, bs BlockService) *Session { + exchange := bs.Exchange() + if bswap, ok := exchange.(*bitswap.Bitswap); ok { ses := bswap.NewSession(ctx) return &Session{ ses: ses, - bs: bs.blockstore, + bs: bs.Blockstore(), } } return &Session{ - ses: bs.exchange, - bs: bs.blockstore, + ses: exchange, + bs: bs.Blockstore(), } } From 5933c5cc5fe9dbbe88d16abfc95d92b8bede84f9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 5 Jul 2017 12:31:34 -0700 Subject: [PATCH 1927/3817] make NewSession in the blockservice be a function, not a method License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@913f45dabdafee264189ba4b8fdc6874b30a3ef2 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 9fa8446d8..587c481cb 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -182,7 +182,7 @@ func FetchGraph(ctx context.Context, root *cid.Cid, serv DAGService) error { var ng node.NodeGetter = serv ds, ok := serv.(*dagService) if ok { - ng = &sesGetter{ds.Blocks.NewSession(ctx)} + ng = &sesGetter{bserv.NewSession(ctx, ds.Blocks)} } v, _ := ctx.Value("progress").(*ProgressTracker) From 9ab90d4dfe97b7b7497a45e75f45ed88bf1e83b8 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 7 Jul 2017 20:54:07 +0200 Subject: [PATCH 1928/3817] bitswap: add few method comments License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-blockservice@f26d40ed910d5c0bb2313dc57a8882d02d029764 --- blockservice/blockservice.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index d746f2fe0..1f2603637 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -78,6 +78,8 @@ func (bs *blockService) Exchange() exchange.Interface { return bs.exchange } +// NewSession creates a bitswap session that allows for controlled exchange of +// wantlists to decrease the bandwidth overhead. func NewSession(ctx context.Context, bs BlockService) *Session { exchange := bs.Exchange() if bswap, ok := exchange.(*bitswap.Bitswap); ok { From b6b9710b5eedf424e9542310e696473a596b86ed Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1929/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@285da113c0725204028f315bd05ce7ce702bf07c --- ipld/merkledag/coding.go | 4 ++-- ipld/merkledag/merkledag.go | 8 ++++---- ipld/merkledag/merkledag_test.go | 8 ++++---- ipld/merkledag/node.go | 6 +++--- ipld/merkledag/node_test.go | 2 +- ipld/merkledag/raw.go | 8 ++++---- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/traverse/traverse_test.go | 2 +- ipld/merkledag/utils/diff.go | 4 ++-- ipld/merkledag/utils/diffenum.go | 4 ++-- ipld/merkledag/utils/diffenum_test.go | 4 ++-- ipld/merkledag/utils/utils.go | 2 +- ipld/merkledag/utils/utils_test.go | 2 +- 13 files changed, 28 insertions(+), 28 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 063003ac7..c66bb889a 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -6,8 +6,8 @@ import ( pb "github.com/ipfs/go-ipfs/merkledag/pb" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 587c481cb..b033af405 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -9,11 +9,11 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" - ipldcbor "gx/ipfs/Qmcdid3XrCxcoNQUqZKiiKtM7JXxtyipU3izyRqwjFbVWw/go-ipld-cbor" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + ipldcbor "gx/ipfs/QmemYymP73eVdTUUMZEiSpiHeZQKNJdT5dP2iuHssZh1sR/go-ipld-cbor" ) var ErrNotFound = fmt.Errorf("merkledag: not found") diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index e3a41bd15..7e8c08069 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -22,11 +22,11 @@ import ( mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" - blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" - u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) func TestNode(t *testing.T) { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 8f9a6257b..161905eb4 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -5,9 +5,9 @@ import ( "encoding/json" "fmt" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" - mh "gx/ipfs/QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw/go-multihash" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + mh "gx/ipfs/QmU9a9NV9RdPNwZQDYd5uKsm6N6LJLSvLbywDDYFbaaC6P/go-multihash" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index 12a94ab7c..f585d87cc 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -8,7 +8,7 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) func TestRemoveLink(t *testing.T) { diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 3270b30ee..9d1e18671 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -1,11 +1,11 @@ package merkledag import ( - "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" - u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) type RawNode struct { diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index d18429b13..f1aa9c6a8 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -5,7 +5,7 @@ import ( "context" "errors" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) // Order is an identifier for traversal algorithm orders diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 7f4f93fa5..93afef1ea 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -8,7 +8,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) func TestDFSPreNoSkip(t *testing.T) { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index c497c426b..167f03c14 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -7,8 +7,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) const ( diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go index 1e656e4e4..491c87cf1 100644 --- a/ipld/merkledag/utils/diffenum.go +++ b/ipld/merkledag/utils/diffenum.go @@ -6,8 +6,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) // DiffEnumerate fetches every object in the graph pointed to by 'to' that is diff --git a/ipld/merkledag/utils/diffenum_test.go b/ipld/merkledag/utils/diffenum_test.go index fad992a64..e34440ada 100644 --- a/ipld/merkledag/utils/diffenum_test.go +++ b/ipld/merkledag/utils/diffenum_test.go @@ -8,8 +8,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) func buildNode(name string, desc map[string]ndesc, out map[string]node.Node) node.Node { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 651101908..791068e74 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -10,9 +10,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" syncds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) type Editor struct { diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index c05398440..815e7d13f 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" context "context" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ) func TestAddLink(t *testing.T) { From 13fb00905b6772e5c4f2fed5630cfde29ad0144a Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1930/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@dd868eb2135f438efd7ff72b89f450c71b45e2fc --- routing/mock/centralized_client.go | 14 +++++++------- routing/mock/centralized_server.go | 6 +++--- routing/mock/centralized_test.go | 6 +++--- routing/mock/dht.go | 4 ++-- routing/mock/interface.go | 4 ++-- routing/none/none_client.go | 10 +++++----- routing/offline/offline.go | 14 +++++++------- routing/supernode/client.go | 16 ++++++++-------- routing/supernode/proxy/loopback.go | 6 +++--- routing/supernode/proxy/standard.go | 14 +++++++------- routing/supernode/server.go | 8 ++++---- routing/supernode/server_test.go | 2 +- 12 files changed, 52 insertions(+), 52 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 7886201b6..e790a631c 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -8,16 +8,16 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" "github.com/ipfs/go-ipfs/thirdparty/testutil" - routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" + pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" + routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dhtpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" - u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" + ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr" + peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" - ma "gx/ipfs/QmcyqRMCAXVtYPS4DiBrA7sezL9rRGfW8Ctx7cywL4TXJj/go-multiaddr" - peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" + dhtpb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index d65ab3ac1..4ba17ed19 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -8,11 +8,11 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/testutil" + pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" - peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" + peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 5b2386236..7ac250481 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,9 +8,9 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 83a030ba6..25f9cdc97 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - mocknet "gx/ipfs/QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime/go-libp2p/p2p/net/mock" + dht "gx/ipfs/QmTHyAbD9KzGrseLNzmEoNkVxA8F2h7LQG2iV6uhBqs6kX/go-libp2p-kad-dht" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - dht "gx/ipfs/QmfDWfnZZiwHSD58FE2PveLQhZZhZjbuvo1TU1Zu4P9Hd3/go-libp2p-kad-dht" + mocknet "gx/ipfs/QmapADMpK4e5kFGBxC2aHreaDqKP9vmMng5f91MA14Ces9/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index ae46c1ea7..de70685a4 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -10,9 +10,9 @@ import ( delay "github.com/ipfs/go-ipfs/thirdparty/delay" "github.com/ipfs/go-ipfs/thirdparty/testutil" - routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" + routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" + peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ) // Server provides mockrouting Clients diff --git a/routing/none/none_client.go b/routing/none/none_client.go index cde9dd26f..28edfa5b4 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,11 +6,11 @@ import ( repo "github.com/ipfs/go-ipfs/repo" - routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" - p2phost "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" - pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" - peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" + pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" + routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + p2phost "gx/ipfs/QmZy7c24mmkEHpNJndwgsEE3wcVxHd8yB969yTnAJFVw7f/go-libp2p-host" ) type nilclient struct { diff --git a/routing/offline/offline.go b/routing/offline/offline.go index f492d73be..f2a703110 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,15 +7,15 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" - routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" + pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" + routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" - pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" - pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" + "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" - "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" + ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + record "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record" + pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ) var ErrOffline = errors.New("routing system in offline mode") diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 04936acaf..e65187b01 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,16 +8,16 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" + pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" + routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" - loggables "gx/ipfs/QmVesPmqbPp7xRGyY96tnBwzDtVV1nqv4SCVxo5zCqKyH8/go-libp2p-loggables" - pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" - pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" + loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" + dhtpb "gx/ipfs/QmTHyAbD9KzGrseLNzmEoNkVxA8F2h7LQG2iV6uhBqs6kX/go-libp2p-kad-dht/pb" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" - peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" - dhtpb "gx/ipfs/QmfDWfnZZiwHSD58FE2PveLQhZZhZjbuvo1TU1Zu4P9Hd3/go-libp2p-kad-dht/pb" + "gx/ipfs/QmZy7c24mmkEHpNJndwgsEE3wcVxHd8yB969yTnAJFVw7f/go-libp2p-host" + pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 0452e7b7e..40d15c0a8 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,10 +2,10 @@ package proxy import ( context "context" - inet "gx/ipfs/QmRscs8KxrSmSv4iuevHv8JfuUzHBMoqiaHzxfDRiksd6e/go-libp2p-net" + dhtpb "gx/ipfs/QmTHyAbD9KzGrseLNzmEoNkVxA8F2h7LQG2iV6uhBqs6kX/go-libp2p-kad-dht/pb" + peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" - dhtpb "gx/ipfs/QmfDWfnZZiwHSD58FE2PveLQhZZhZjbuvo1TU1Zu4P9Hd3/go-libp2p-kad-dht/pb" + inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index c214643ee..42b76feab 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,15 +4,15 @@ import ( "context" "errors" - inet "gx/ipfs/QmRscs8KxrSmSv4iuevHv8JfuUzHBMoqiaHzxfDRiksd6e/go-libp2p-net" + pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - host "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host" - loggables "gx/ipfs/QmVesPmqbPp7xRGyY96tnBwzDtVV1nqv4SCVxo5zCqKyH8/go-libp2p-loggables" - pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" + loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" + dhtpb "gx/ipfs/QmTHyAbD9KzGrseLNzmEoNkVxA8F2h7LQG2iV6uhBqs6kX/go-libp2p-kad-dht/pb" + peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - kbucket "gx/ipfs/QmaQG6fJdzn2532WHoPdVwKqftXr6iCSr5NtWyGi1BHytT/go-libp2p-kbucket" - peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" - dhtpb "gx/ipfs/QmfDWfnZZiwHSD58FE2PveLQhZZhZjbuvo1TU1Zu4P9Hd3/go-libp2p-kad-dht/pb" + host "gx/ipfs/QmZy7c24mmkEHpNJndwgsEE3wcVxHd8yB969yTnAJFVw7f/go-libp2p-host" + inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" + kbucket "gx/ipfs/QmbiCMdwmmhif5axuGSHzYbPFGeKjLAuMY6JrGpVteHFsy/go-libp2p-kbucket" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 6ed1bfe1d..577f26c7b 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,12 +8,12 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" + dhtpb "gx/ipfs/QmTHyAbD9KzGrseLNzmEoNkVxA8F2h7LQG2iV6uhBqs6kX/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - pb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" - pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" + peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" - dhtpb "gx/ipfs/QmfDWfnZZiwHSD58FE2PveLQhZZhZjbuvo1TU1Zu4P9Hd3/go-libp2p-kad-dht/pb" + pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ) // Server handles routing queries using a database backend diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 05dfb68f4..44269717f 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" + dhtpb "gx/ipfs/QmTHyAbD9KzGrseLNzmEoNkVxA8F2h7LQG2iV6uhBqs6kX/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dhtpb "gx/ipfs/QmfDWfnZZiwHSD58FE2PveLQhZZhZjbuvo1TU1Zu4P9Hd3/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 5fa37fff366ff36da4d5ef35f3da912735a01ff5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1931/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@e57e6c2e7df00cb66569d45cdfcfc4e276cb91f4 --- blockstore/arc_cache.go | 4 ++-- blockstore/arc_cache_test.go | 4 ++-- blockstore/blockstore.go | 4 ++-- blockstore/blockstore_test.go | 6 +++--- blockstore/bloom_cache.go | 4 ++-- blockstore/bloom_cache_test.go | 2 +- blockstore/util/remove.go | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 1e9d46464..e7a702406 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -3,12 +3,12 @@ package blockstore import ( "context" - "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) // arccache wraps a BlockStore with an Adaptive Replacement Cache (ARC) for diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index c1868814b..b6575a58c 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -4,11 +4,11 @@ import ( "context" "testing" - "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" syncds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) var exampleBlock = blocks.NewBlock([]byte("foo")) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 905fcf029..e8f11aa7c 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -9,13 +9,13 @@ import ( "sync/atomic" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dsns "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/namespace" dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 866fd40cd..4382c9111 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -7,13 +7,13 @@ import ( "testing" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" ds_sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func TestGetWhenKeyNotPresent(t *testing.T) { diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 1467ad31f..8314fba02 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -5,10 +5,10 @@ import ( "sync/atomic" "time" - "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" bloom "gx/ipfs/QmeiMCBkYHxkDkDfnDadzz4YxY5ruL5Pj499essE4vRsGM/bbloom" ) diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 85ed29dc9..af318a0aa 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" context "context" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index defe9347d..1b6d4f33a 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -5,8 +5,8 @@ import ( "fmt" "io" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" bs "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/pin" From e9cd392ccc198422c5f93e6f1856dc04c32cc309 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1932/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@8a760c6c2dd432d84e362e7d93ed61dbde8c2689 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index e838046f8..fc2a09ed4 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -9,8 +9,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) // Result represents an incremental output from a garbage collection diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 47886ba6f..c73d3dd7b 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -12,10 +12,10 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" dutils "github.com/ipfs/go-ipfs/merkledag/utils" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index c0068705b..57683eef9 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -10,10 +10,10 @@ import ( "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" + "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func randNode() (*mdag.ProtoNode, *cid.Cid) { diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 833f32cde..74bea0375 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -12,9 +12,9 @@ import ( "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 50e27a0e1..310780f30 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -10,9 +10,9 @@ import ( offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func ignoreCids(_ *cid.Cid) {} From 96cda700359639edd78d86f9e57bd92b9379ab57 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1933/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@9984eaafe4ceb7d6ba47700d632fbd10ce05260c --- mfs/dir.go | 4 ++-- mfs/file.go | 2 +- mfs/mfs_test.go | 6 +++--- mfs/ops.go | 2 +- mfs/repub_test.go | 2 +- mfs/system.go | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index c9cacb005..387becb6c 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -15,8 +15,8 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) var ErrNotYetImplemented = errors.New("not yet implemented") diff --git a/mfs/file.go b/mfs/file.go index 71ca2ced6..d527eacf2 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 75f5c0012..3be7ed8fc 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -24,11 +24,11 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) func emptyDirNode() *dag.ProtoNode { diff --git a/mfs/ops.go b/mfs/ops.go index bb99d1860..33f8f9b79 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -9,7 +9,7 @@ import ( path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) // Mv moves the file or directory at 'src' to 'dst' diff --git a/mfs/repub_test.go b/mfs/repub_test.go index d96edf07f..4a4e53a56 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -7,7 +7,7 @@ import ( ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" "context" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index 88e1f2ff4..086808af5 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -19,9 +19,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) var ErrNotExist = errors.New("no such rootfs") From 0e8998953c32c1dc85c1cbb0f40cf4a3b8ea64c2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1934/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@2aa75b733458941152fddc3b78bf78a9a99c383a --- namesys/interface.go | 2 +- namesys/ipns_select_test.go | 4 ++-- namesys/namesys.go | 6 +++--- namesys/namesys_test.go | 2 +- namesys/publisher.go | 12 ++++++------ namesys/republisher/repub.go | 8 ++++---- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 10 +++++----- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index f103fc045..84a6bbe2c 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,7 +35,7 @@ import ( context "context" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" + ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) const ( diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index 497548d71..e12af16d9 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -10,8 +10,8 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" - u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) func shuffle(a []*pb.IpnsEntry) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 33883eb39..e86979914 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,10 +7,10 @@ import ( path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" - routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" + routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" + peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 40f3bbe74..1507f5510 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,9 +10,9 @@ import ( offroute "github.com/ipfs/go-ipfs/routing/offline" "github.com/ipfs/go-ipfs/unixfs" - ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) type mockResolver struct { diff --git a/namesys/publisher.go b/namesys/publisher.go index 31fbbc1da..c90207649 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -14,14 +14,14 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ft "github.com/ipfs/go-ipfs/unixfs" - ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" - routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" + routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - record "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record" - dhtpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" - u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" + ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + record "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record" + dhtpb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index a8fa74d04..787636588 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,15 +11,15 @@ import ( path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - ic "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" - routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" + routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - recpb "gx/ipfs/QmWYCqr6UDqqD1bfRybaAPtbAqcN3TSJpveaBXMwbQ3ePZ/go-libp2p-record/pb" + peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" + ic "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + recpb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index d4d7e1282..cc9eaa76b 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime/go-libp2p/p2p/net/mock" - pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore" + pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" + mocknet "gx/ipfs/QmapADMpK4e5kFGBxC2aHreaDqKP9vmMng5f91MA14Ces9/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 6eeb958c8..48142b2e9 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -12,7 +12,7 @@ import ( ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer" + peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index 45eb38557..faff33690 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -9,14 +9,14 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" - routing "gx/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD/go-libp2p-routing" + routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - mh "gx/ipfs/QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw/go-multihash" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + mh "gx/ipfs/QmU9a9NV9RdPNwZQDYd5uKsm6N6LJLSvLbywDDYFbaaC6P/go-multihash" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) var log = logging.Logger("namesys") From 5ddc90819a9bf49be91796a7276fc3f7fc82d263 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1935/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@3da56381a18f50ae6de65e1f399a412af654284c --- unixfs/archive/archive.go | 2 +- unixfs/archive/tar/writer.go | 2 +- unixfs/hamt/hamt.go | 4 ++-- unixfs/io/dagreader.go | 2 +- unixfs/io/dirbuilder.go | 4 ++-- unixfs/io/resolve.go | 2 +- unixfs/mod/dagmodifier.go | 4 ++-- unixfs/mod/dagmodifier_test.go | 2 +- unixfs/test/utils.go | 4 ++-- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index cf1d74ac9..fc380baf8 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -11,7 +11,7 @@ import ( tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) // DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 01d7fd833..dde968cec 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -13,7 +13,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" upb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 1a6b3d1f9..b91738a08 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -31,9 +31,9 @@ import ( format "github.com/ipfs/go-ipfs/unixfs" upb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" "gx/ipfs/QmfJHywXQu98UeZtGJBQrPAR6AtmDjjbe3qjTo9piXHPnx/murmur3" ) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 17cd6b4ca..ae1517362 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 83b49df9d..b4af5441b 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -8,9 +8,9 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) // ShardSplitThreshold specifies how large of an unsharded directory diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index 75d88a72b..f1cb7f35b 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -7,7 +7,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) // ResolveUnixfsOnce resolves a single hop of a path through a graph in a diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 00a3c1c93..cdf2b4c78 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -14,9 +14,9 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) var ErrSeekFail = errors.New("failed to seek properly") diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index b22844194..314178dd5 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -14,7 +14,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" testu "github.com/ipfs/go-ipfs/unixfs/test" - u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" ) func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) []byte { diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index cadc8081d..c70d3f3bd 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -14,8 +14,8 @@ import ( mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" - u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) func SizeSplitterGen(size int64) chunk.SplitterGen { From 6d5df0f0e782142523d63dbcd41c4e785398fc7b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1936/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@b3b1849cd11de49e968a47758dceb082b696305b --- path/path.go | 2 +- path/resolver.go | 4 ++-- path/resolver_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/path/path.go b/path/path.go index bdb5ba156..d56efd1d8 100644 --- a/path/path.go +++ b/path/path.go @@ -5,7 +5,7 @@ import ( "path" "strings" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ) // ErrBadPath is returned when a given path is incorrectly formatted diff --git a/path/resolver.go b/path/resolver.go index 09703cd76..ef9bc90c1 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -9,9 +9,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) var log = logging.Logger("path") diff --git a/path/resolver_test.go b/path/resolver_test.go index 020861024..ba903d01c 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -9,8 +9,8 @@ import ( dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" - util "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + util "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) func randNode() *merkledag.ProtoNode { From fb0c8e08bfabf99cb5542ccc54ff2391f3e8e32c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1937/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-chunker@e37393beb8360df5c4321893f8830393e7650631 --- chunker/rabin_test.go | 4 ++-- chunker/splitting_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 45f2b2061..6f9d6b7ff 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -3,8 +3,8 @@ package chunk import ( "bytes" "fmt" - "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" "io" "testing" ) diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index 918a46659..a9d3798e6 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -5,7 +5,7 @@ import ( "io" "testing" - u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" ) func randBuf(t *testing.T, size int) []byte { From be73ae01013b733fc84b7cce99cd77a302d088dc Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1938/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@d148605df9905ad2d609f4a79f48d26bd295e943 --- blockservice/blockservice.go | 4 ++-- blockservice/blockservice_test.go | 2 +- blockservice/test/blocks_test.go | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 1f2603637..f87674056 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -13,8 +13,8 @@ import ( bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" ) var log = logging.Logger("blockservice") diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index db3b78ecc..663d9856d 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" butil "github.com/ipfs/go-ipfs/blocks/blocksutil" offline "github.com/ipfs/go-ipfs/exchange/offline" - "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 45ac82955..96ed5e54d 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -10,12 +10,12 @@ import ( blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func newObject(data []byte) blocks.Block { From 56bff68eb2505e4139b2cd32f761aca1715d319d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1939/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-filestore@31880033aa95fb48a7ac67b3640ba6a5e118413f --- filestore/filestore.go | 4 ++-- filestore/filestore_test.go | 2 +- filestore/fsrefstore.go | 4 ++-- filestore/util.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index c1f2df981..d69aa9d23 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -12,11 +12,11 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" - "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 2bbc43a26..ce2f4b168 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -11,8 +11,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 63978fbfd..ad7dc898e 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -11,13 +11,13 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" - "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dsns "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/namespace" dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index 6a1f90d17..3086ccadb 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -8,9 +8,9 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) // Status is used to identify the state of the block data referenced From e69baac283881dc8397a96d38a9d4f838a8ff161 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1940/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-ds-help@224f344c49dbcbd42e74c06b0a7ae43db6e37833 --- datastore/dshelp/key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index b0b2ebe98..36cc841f3 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -1,9 +1,9 @@ package dshelp import ( + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" base32 "gx/ipfs/QmZvZSVtvxak4dcTkhsQhqd1SQ6rg5UzaSTu62WfWKjj93/base32" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) // TODO: put this code into the go-datastore itself From 3d01a09aa3c751830fbdbf0f5629789a199d859b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1941/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-offline@55faa4c18605fee4d455481d2d2e079f904f4970 --- exchange/offline/offline.go | 4 ++-- exchange/offline/offline_test.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 7c33b7af2..1c0b1a424 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -7,9 +7,9 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 7055150ae..d61947562 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -6,12 +6,12 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" - blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" ds_sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" ) func TestBlockReturnsErr(t *testing.T) { From 00f1dfd698b2fd0e37058284766ecea68d053f3e Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1942/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-exchange-interface@5e140152bd585904b6d655de6d4152b610e11220 --- exchange/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index ac494ff99..1373f0d3c 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -5,9 +5,9 @@ import ( "context" "io" - blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format" + blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ) // Any type that implements exchange.Interface may be used as an IPFS block From 0997d0c7d5f69975acdf597e7266427e54c223b4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1943/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/interface-go-ipfs-core@ee874380410e6a909c9517ecd58730cb17b4424d --- coreiface/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 363c5adac..81197bb46 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -5,8 +5,8 @@ import ( "errors" "io" - ipld "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format" - cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid" + cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + ipld "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) type Path interface { From 7b6c96d7fe457deec1ebca55c9c6f10d68e0f54f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 11 Jul 2017 19:17:51 -0700 Subject: [PATCH 1944/3817] update go-multihash and bubble up changes License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-keystore@1050cc226f0a76bab5600b0db8967318cfebd7d6 --- keystore/keystore.go | 2 +- keystore/keystore_test.go | 2 +- keystore/memkeystore.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index acb8cb3cc..fd9ab94b4 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -7,7 +7,7 @@ import ( "path/filepath" "strings" - ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" + ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) type Keystore interface { diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index 53c30b0d7..cf6281be2 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -7,7 +7,7 @@ import ( "sort" "testing" - ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" + ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) type rr struct{} diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index 068c5e189..3732a3262 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -1,6 +1,6 @@ package keystore -import ci "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto" +import ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" type MemKeystore struct { keys map[string]ci.PrivKey From 7e15deb0a7f988767bdbfaa90e6e652bd3277ed8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 11 Jul 2017 20:27:22 -0700 Subject: [PATCH 1945/3817] Change IPFS to use the new pluggable Block to IPLD decoding framework. Later, we should: 1. Pull the other node formats out of IPFS (at least the raw one). 2. Pull out the decoder registration/management into a `go-ipld` library. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@4220827818666bce33ca819b94dd86fe7805f1e2 --- ipld/merkledag/coding.go | 27 ++++++++++++++++++++++++ ipld/merkledag/merkledag.go | 41 +++++++++++-------------------------- ipld/merkledag/raw.go | 12 +++++++++++ 3 files changed, 51 insertions(+), 29 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index c66bb889a..39da14a09 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -3,6 +3,9 @@ package merkledag import ( "fmt" "sort" + "strings" + + "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" pb "github.com/ipfs/go-ipfs/merkledag/pb" @@ -108,3 +111,27 @@ func DecodeProtobuf(encoded []byte) (*ProtoNode, error) { } return n, nil } + +// DecodeProtobufBlock is a block decoder for protobuf IPLD nodes conforming to +// node.DecodeBlockFunc +func DecodeProtobufBlock(b blocks.Block) (node.Node, error) { + c := b.Cid() + if c.Type() != cid.DagProtobuf { + return nil, fmt.Errorf("this function can only decode protobuf nodes") + } + + decnd, err := DecodeProtobuf(b.RawData()) + if err != nil { + if strings.Contains(err.Error(), "Unmarshal failed") { + return nil, fmt.Errorf("The block referred to by '%s' was not a valid merkledag node", c) + } + return nil, fmt.Errorf("Failed to decode Protocol Buffers: %v", err) + } + + decnd.cached = c + decnd.Prefix = c.Prefix() + return decnd, nil +} + +// Type assertion +var _ node.DecodeBlockFunc = DecodeProtobufBlock diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index b033af405..82b1693ae 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -4,7 +4,6 @@ package merkledag import ( "context" "fmt" - "strings" "sync" bserv "github.com/ipfs/go-ipfs/blockservice" @@ -16,6 +15,15 @@ import ( ipldcbor "gx/ipfs/QmemYymP73eVdTUUMZEiSpiHeZQKNJdT5dP2iuHssZh1sR/go-ipld-cbor" ) +// TODO: We should move these registrations elsewhere. Really, most of the IPLD +// functionality should go in a `go-ipld` repo but that will take a lot of work +// and design. +func init() { + node.Register(cid.DagProtobuf, DecodeProtobufBlock) + node.Register(cid.Raw, DecodeRawBlock) + node.Register(cid.DagCBOR, ipldcbor.DecodeBlock) +} + var ErrNotFound = fmt.Errorf("merkledag: not found") // DAGService is an IPFS Merkle DAG service. @@ -94,32 +102,7 @@ func (n *dagService) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { return nil, fmt.Errorf("Failed to get block for %s: %v", c, err) } - return decodeBlock(b) -} - -func decodeBlock(b blocks.Block) (node.Node, error) { - c := b.Cid() - - switch c.Type() { - case cid.DagProtobuf: - decnd, err := DecodeProtobuf(b.RawData()) - if err != nil { - if strings.Contains(err.Error(), "Unmarshal failed") { - return nil, fmt.Errorf("The block referred to by '%s' was not a valid merkledag node", c) - } - return nil, fmt.Errorf("Failed to decode Protocol Buffers: %v", err) - } - - decnd.cached = b.Cid() - decnd.Prefix = b.Cid().Prefix() - return decnd, nil - case cid.Raw: - return NewRawNodeWPrefix(b.RawData(), b.Cid().Prefix()) - case cid.DagCBOR: - return ipldcbor.Decode(b.RawData()) - default: - return nil, fmt.Errorf("unrecognized object type: %s", c.Type()) - } + return node.Decode(b) } // GetLinks return the links for the node, the node doesn't necessarily have @@ -174,7 +157,7 @@ func (sg *sesGetter) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { return nil, err } - return decodeBlock(blk) + return node.Decode(blk) } // FetchGraph fetches all nodes that are children of the given node @@ -235,7 +218,7 @@ func (ds *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *Node return } - nd, err := decodeBlock(b) + nd, err := node.Decode(b) if err != nil { out <- &NodeOption{Err: err} return diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 9d1e18671..856a407fc 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -1,6 +1,7 @@ package merkledag import ( + "fmt" "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" @@ -22,6 +23,17 @@ func NewRawNode(data []byte) *RawNode { return &RawNode{blk} } +// DecodeRawBlock is a block decoder for raw IPLD nodes conforming to `node.DecodeBlockFunc`. +func DecodeRawBlock(block blocks.Block) (node.Node, error) { + if block.Cid().Type() != cid.Raw { + return nil, fmt.Errorf("raw nodes cannot be decoded from non-raw blocks: %d", block.Cid().Type()) + } + // Once you "share" a block, it should be immutable. Therefore, we can just use this block as-is. + return &RawNode{block}, nil +} + +var _ node.DecodeBlockFunc = DecodeRawBlock + // NewRawNodeWPrefix creates a RawNode with the hash function // specified in prefix. func NewRawNodeWPrefix(data []byte, prefix cid.Prefix) (*RawNode, error) { From 53a60fe9338f7ac25784f3c8938b6393b91f7803 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 14 Jul 2017 12:17:05 -0700 Subject: [PATCH 1946/3817] gx: update ipldcbor I previously optimized the IPLD cbor decoder to *not* encode and then re-decode objects when constructing them with `WrapObject`. Unfortunately, we rely on this to canonicalize the object before computing the tree/links. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@840b169d9214d5c5a6c4b678e522878ac008f736 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 82b1693ae..859f79844 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -11,8 +11,8 @@ import ( blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + ipldcbor "gx/ipfs/QmXgUVPAxjMLZSyxx818YstJJAoRg3nyPWENmBLVzLtoax/go-ipld-cbor" node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" - ipldcbor "gx/ipfs/QmemYymP73eVdTUUMZEiSpiHeZQKNJdT5dP2iuHssZh1sR/go-ipld-cbor" ) // TODO: We should move these registrations elsewhere. Really, most of the IPLD From 85d456df584b2b0514507f51579acffa9b244714 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 27 Jul 2017 00:02:03 -0700 Subject: [PATCH 1947/3817] gx: update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@3a7078e71c544a82b58cc94a02b72407f2c0d393 --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index cc9eaa76b..40f381250 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - mocknet "gx/ipfs/QmapADMpK4e5kFGBxC2aHreaDqKP9vmMng5f91MA14Ces9/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmSatLR9HCrZjPqomt6VdNCoJmHMz8NP34WfpfBznJZ25M/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { From 4c2443de609e5f7dd72efa86567563f5df6beb0c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 27 Jul 2017 00:02:03 -0700 Subject: [PATCH 1948/3817] gx: update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@6cd48d4d9c9bbc4e6939fbfe558bc13a6505570b --- routing/mock/dht.go | 4 ++-- routing/none/none_client.go | 2 +- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 4 ++-- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 25f9cdc97..d64645acd 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - dht "gx/ipfs/QmTHyAbD9KzGrseLNzmEoNkVxA8F2h7LQG2iV6uhBqs6kX/go-libp2p-kad-dht" + dht "gx/ipfs/QmRKEzkaiwud2LnwJ9CgBrKw122ddKPTMtLizV3DNimVRD/go-libp2p-kad-dht" + mocknet "gx/ipfs/QmSatLR9HCrZjPqomt6VdNCoJmHMz8NP34WfpfBznJZ25M/go-libp2p/p2p/net/mock" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - mocknet "gx/ipfs/QmapADMpK4e5kFGBxC2aHreaDqKP9vmMng5f91MA14Ces9/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 28edfa5b4..8b414d188 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -8,9 +8,9 @@ import ( pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" + p2phost "gx/ipfs/QmRNyPNJGNCaZyYonJj7owciWTsMd9gRfEKmZY3o6xwN3h/go-libp2p-host" cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" - p2phost "gx/ipfs/QmZy7c24mmkEHpNJndwgsEE3wcVxHd8yB969yTnAJFVw7f/go-libp2p-host" ) type nilclient struct { diff --git a/routing/supernode/client.go b/routing/supernode/client.go index e65187b01..fd5a3359e 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -10,13 +10,13 @@ import ( pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" + dhtpb "gx/ipfs/QmRKEzkaiwud2LnwJ9CgBrKw122ddKPTMtLizV3DNimVRD/go-libp2p-kad-dht/pb" + "gx/ipfs/QmRNyPNJGNCaZyYonJj7owciWTsMd9gRfEKmZY3o6xwN3h/go-libp2p-host" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" - dhtpb "gx/ipfs/QmTHyAbD9KzGrseLNzmEoNkVxA8F2h7LQG2iV6uhBqs6kX/go-libp2p-kad-dht/pb" cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - "gx/ipfs/QmZy7c24mmkEHpNJndwgsEE3wcVxHd8yB969yTnAJFVw7f/go-libp2p-host" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 40d15c0a8..5006e968c 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,7 +2,7 @@ package proxy import ( context "context" - dhtpb "gx/ipfs/QmTHyAbD9KzGrseLNzmEoNkVxA8F2h7LQG2iV6uhBqs6kX/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmRKEzkaiwud2LnwJ9CgBrKw122ddKPTMtLizV3DNimVRD/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 42b76feab..ebe4f20a9 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -5,12 +5,12 @@ import ( "errors" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" + dhtpb "gx/ipfs/QmRKEzkaiwud2LnwJ9CgBrKw122ddKPTMtLizV3DNimVRD/go-libp2p-kad-dht/pb" + host "gx/ipfs/QmRNyPNJGNCaZyYonJj7owciWTsMd9gRfEKmZY3o6xwN3h/go-libp2p-host" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" - dhtpb "gx/ipfs/QmTHyAbD9KzGrseLNzmEoNkVxA8F2h7LQG2iV6uhBqs6kX/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - host "gx/ipfs/QmZy7c24mmkEHpNJndwgsEE3wcVxHd8yB969yTnAJFVw7f/go-libp2p-host" inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" kbucket "gx/ipfs/QmbiCMdwmmhif5axuGSHzYbPFGeKjLAuMY6JrGpVteHFsy/go-libp2p-kbucket" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 577f26c7b..b3af59de6 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -9,7 +9,7 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - dhtpb "gx/ipfs/QmTHyAbD9KzGrseLNzmEoNkVxA8F2h7LQG2iV6uhBqs6kX/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmRKEzkaiwud2LnwJ9CgBrKw122ddKPTMtLizV3DNimVRD/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 44269717f..78379025f 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,7 +3,7 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmTHyAbD9KzGrseLNzmEoNkVxA8F2h7LQG2iV6uhBqs6kX/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmRKEzkaiwud2LnwJ9CgBrKw122ddKPTMtLizV3DNimVRD/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" ) From 88a63511b94e563e278110c6c5df35a84f959af5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 27 Jul 2017 00:02:03 -0700 Subject: [PATCH 1949/3817] gx: update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-blockstore@5c8c656c7f9e306a2fa735cf49f814e75aa29f0f --- blockstore/bloom_cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 8314fba02..8360f82dd 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -9,7 +9,7 @@ import ( "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - bloom "gx/ipfs/QmeiMCBkYHxkDkDfnDadzz4YxY5ruL5Pj499essE4vRsGM/bbloom" + bloom "gx/ipfs/QmXqKGu7QzfRzFC4yd5aL9sThYx22vY163VGwmxfp5qGHk/bbloom" ) // bloomCached returns a Blockstore that caches Has requests using a Bloom From 416337d64a8729e97c4618a1250a309c3bb5a80f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 31 Jul 2017 14:04:40 -0700 Subject: [PATCH 1950/3817] gx: update go-libp2p-swarm fixes #4102 (fixed in go-libp2p-swarm) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@bd0d24ed4217c15ddae72e33191a9a44ca19fbb5 --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 40f381250..272975913 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - mocknet "gx/ipfs/QmSatLR9HCrZjPqomt6VdNCoJmHMz8NP34WfpfBznJZ25M/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmZPBrKq6S1fdYaRAzYZivJL12QkUqHwnNzF9wC8VXC4bo/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { From 1731405a0af2504f711fecac448e5b85114e12c2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 31 Jul 2017 14:04:40 -0700 Subject: [PATCH 1951/3817] gx: update go-libp2p-swarm fixes #4102 (fixed in go-libp2p-swarm) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@f62a67a153cd1ba0495b7d191e1eab7124cc415d --- routing/mock/dht.go | 4 ++-- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index d64645acd..c57eaf4c9 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -3,10 +3,10 @@ package mockrouting import ( context "context" "github.com/ipfs/go-ipfs/thirdparty/testutil" - dht "gx/ipfs/QmRKEzkaiwud2LnwJ9CgBrKw122ddKPTMtLizV3DNimVRD/go-libp2p-kad-dht" - mocknet "gx/ipfs/QmSatLR9HCrZjPqomt6VdNCoJmHMz8NP34WfpfBznJZ25M/go-libp2p/p2p/net/mock" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + mocknet "gx/ipfs/QmZPBrKq6S1fdYaRAzYZivJL12QkUqHwnNzF9wC8VXC4bo/go-libp2p/p2p/net/mock" + dht "gx/ipfs/QmcKxeQomXUjo54VwisTiXeic5FFBknwUPtT7yRWvmPD1D/go-libp2p-kad-dht" ) type mocknetserver struct { diff --git a/routing/supernode/client.go b/routing/supernode/client.go index fd5a3359e..259d6c210 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -10,7 +10,6 @@ import ( pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" - dhtpb "gx/ipfs/QmRKEzkaiwud2LnwJ9CgBrKw122ddKPTMtLizV3DNimVRD/go-libp2p-kad-dht/pb" "gx/ipfs/QmRNyPNJGNCaZyYonJj7owciWTsMd9gRfEKmZY3o6xwN3h/go-libp2p-host" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" @@ -18,6 +17,7 @@ import ( peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" + dhtpb "gx/ipfs/QmcKxeQomXUjo54VwisTiXeic5FFBknwUPtT7yRWvmPD1D/go-libp2p-kad-dht/pb" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 5006e968c..f7ed151c6 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,10 +2,10 @@ package proxy import ( context "context" - dhtpb "gx/ipfs/QmRKEzkaiwud2LnwJ9CgBrKw122ddKPTMtLizV3DNimVRD/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" + dhtpb "gx/ipfs/QmcKxeQomXUjo54VwisTiXeic5FFBknwUPtT7yRWvmPD1D/go-libp2p-kad-dht/pb" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index ebe4f20a9..bbcf2b5da 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -5,7 +5,6 @@ import ( "errors" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - dhtpb "gx/ipfs/QmRKEzkaiwud2LnwJ9CgBrKw122ddKPTMtLizV3DNimVRD/go-libp2p-kad-dht/pb" host "gx/ipfs/QmRNyPNJGNCaZyYonJj7owciWTsMd9gRfEKmZY3o6xwN3h/go-libp2p-host" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" @@ -13,6 +12,7 @@ import ( ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" kbucket "gx/ipfs/QmbiCMdwmmhif5axuGSHzYbPFGeKjLAuMY6JrGpVteHFsy/go-libp2p-kbucket" + dhtpb "gx/ipfs/QmcKxeQomXUjo54VwisTiXeic5FFBknwUPtT7yRWvmPD1D/go-libp2p-kad-dht/pb" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index b3af59de6..94bf3ccaa 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -9,11 +9,11 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - dhtpb "gx/ipfs/QmRKEzkaiwud2LnwJ9CgBrKw122ddKPTMtLizV3DNimVRD/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" + dhtpb "gx/ipfs/QmcKxeQomXUjo54VwisTiXeic5FFBknwUPtT7yRWvmPD1D/go-libp2p-kad-dht/pb" ) // Server handles routing queries using a database backend diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 78379025f..4e8ba883b 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmRKEzkaiwud2LnwJ9CgBrKw122ddKPTMtLizV3DNimVRD/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dhtpb "gx/ipfs/QmcKxeQomXUjo54VwisTiXeic5FFBknwUPtT7yRWvmPD1D/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From d56271eb7f0bb4278ccc27c6518777b0c7248544 Mon Sep 17 00:00:00 2001 From: Arthur Elliott Date: Wed, 9 Aug 2017 15:24:52 -0400 Subject: [PATCH 1952/3817] trivial comment update License: MIT Signed-off-by: Arthur Elliott This commit was moved from ipfs/go-merkledag@3cf3c886d4b4196758e7c627f5d0cba219f51252 --- ipld/merkledag/merkledag.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 859f79844..067f9df21 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -6,9 +6,10 @@ import ( "fmt" "sync" + blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ipldcbor "gx/ipfs/QmXgUVPAxjMLZSyxx818YstJJAoRg3nyPWENmBLVzLtoax/go-ipld-cbor" @@ -32,8 +33,8 @@ type DAGService interface { Get(context.Context, *cid.Cid) (node.Node, error) Remove(node.Node) error - // GetDAG returns, in order, all the single leve child - // nodes of the passed in node. + // GetMany returns a channel of NodeOption given + // a set of CIDs GetMany(context.Context, []*cid.Cid) <-chan *NodeOption Batch() *Batch From 3baadd0455ff519b8be781badeced7030f07b438 Mon Sep 17 00:00:00 2001 From: Arthur Elliott Date: Wed, 9 Aug 2017 15:59:29 -0400 Subject: [PATCH 1953/3817] fix import order License: MIT Signed-off-by: Arthur Elliott This commit was moved from ipfs/go-merkledag@d26f138633b8074953ab2a9a3f00e405c750b3ec --- ipld/merkledag/merkledag.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 067f9df21..4c0b4585f 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -6,12 +6,11 @@ import ( "fmt" "sync" - blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" - bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" ipldcbor "gx/ipfs/QmXgUVPAxjMLZSyxx818YstJJAoRg3nyPWENmBLVzLtoax/go-ipld-cbor" node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) @@ -34,7 +33,7 @@ type DAGService interface { Remove(node.Node) error // GetMany returns a channel of NodeOption given - // a set of CIDs + // a set of CIDs. GetMany(context.Context, []*cid.Cid) <-chan *NodeOption Batch() *Batch From 132a47895e4c186a17b67f21cc1145bf30957289 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 9 Aug 2017 23:18:50 +0200 Subject: [PATCH 1954/3817] gc: add events for profiling GC License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-pinner@a1a412a83e3b0b0b17d403d2f46193352aaf9ecf --- pinning/pinner/gc/gc.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index fc2a09ed4..c2ce05945 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -9,10 +9,13 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" + logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) +var log = logging.Logger("gc") + // Result represents an incremental output from a garbage collection // run. It contains either an error, or the cid of a removed object. type Result struct { @@ -31,7 +34,13 @@ type Result struct { // deletes any block that is not found in the marked set. // func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin.Pinner, bestEffortRoots []*cid.Cid) <-chan Result { + + elock := log.EventBegin(ctx, "GC.lockWait") unlocker := bs.GCLock() + elock.Done() + elock = log.EventBegin(ctx, "GC.locked") + emark := log.EventBegin(ctx, "GC.mark") + ls = ls.GetOfflineLinkService() output := make(chan Result, 128) @@ -39,12 +48,18 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. go func() { defer close(output) defer unlocker.Unlock() + defer elock.Done() gcs, err := ColoredSet(ctx, pn, ls, bestEffortRoots, output) if err != nil { output <- Result{Error: err} return } + emark.Append(logging.LoggableMap{ + "blackSetSize": fmt.Sprintf("%d", gcs.Len()), + }) + emark.Done() + esweep := log.EventBegin(ctx, "GC.sweep") keychan, err := bs.AllKeysChan(ctx) if err != nil { @@ -53,6 +68,7 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. } errors := false + var removed uint64 loop: for { @@ -63,6 +79,7 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. } if !gcs.Has(k) { err := bs.DeleteBlock(k) + removed++ if err != nil { errors = true output <- Result{Error: &CannotDeleteBlockError{k, err}} @@ -80,6 +97,10 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. break loop } } + esweep.Append(logging.LoggableMap{ + "whiteSetSize": fmt.Sprintf("%d", removed), + }) + esweep.Done() if errors { output <- Result{Error: ErrCannotDeleteSomeBlocks} } From c68be89e205f5b83e98b48f5da46591a2251dac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 9 Aug 2017 19:25:25 +0200 Subject: [PATCH 1955/3817] dag: add option to specify hash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-merkledag@57fbb422f6b877caf10835049cd559b3d8998b5c --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 4c0b4585f..e0c244e45 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -11,8 +11,8 @@ import ( cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" - ipldcbor "gx/ipfs/QmXgUVPAxjMLZSyxx818YstJJAoRg3nyPWENmBLVzLtoax/go-ipld-cbor" node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + ipldcbor "gx/ipfs/QmeebqVZeEXBqJ2B4urQWfdhwRRPm84ajnCo8x8pfwbsPM/go-ipld-cbor" ) // TODO: We should move these registrations elsewhere. Really, most of the IPLD From 15fb02ec8a679e3ac19f6864ee2e52c6bebeb6cc Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 16 Aug 2017 16:51:18 -0700 Subject: [PATCH 1956/3817] extract update go-testutil License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@3fdcbb848d7db18a365b4b8ac55b884ee49e5c32 --- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 272975913..d87cb924c 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - mocknet "gx/ipfs/QmZPBrKq6S1fdYaRAzYZivJL12QkUqHwnNzF9wC8VXC4bo/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmXZ6XetFwaDNmszPCux9DaKqMykEJGDtWHSqprn94UXzM/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 48142b2e9..b13c249bb 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" - testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" + testutil "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" From a3803bab0d22ddbb3359241c9122c5dad925d0d7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 16 Aug 2017 16:51:18 -0700 Subject: [PATCH 1957/3817] extract update go-testutil License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@02c3d2c4f81d6be7bb5dc147fb5a18e491bb76e4 --- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/dht.go | 6 +++--- routing/mock/interface.go | 2 +- routing/none/none_client.go | 2 +- routing/offline/offline_test.go | 2 +- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 12 files changed, 17 insertions(+), 17 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index e790a631c..0161ed8f4 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -6,7 +6,7 @@ import ( "time" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - "github.com/ipfs/go-ipfs/thirdparty/testutil" + "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 4ba17ed19..3ac4a558e 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -6,7 +6,7 @@ import ( "sync" "time" - "github.com/ipfs/go-ipfs/thirdparty/testutil" + "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 7ac250481..fb35a08c1 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -6,7 +6,7 @@ import ( "time" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "github.com/ipfs/go-ipfs/thirdparty/testutil" + "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" diff --git a/routing/mock/dht.go b/routing/mock/dht.go index c57eaf4c9..7b0f25d2f 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -2,11 +2,11 @@ package mockrouting import ( context "context" - "github.com/ipfs/go-ipfs/thirdparty/testutil" + dht "gx/ipfs/QmSFNexSqAnJKGfDwKnGx2sGB1EtLwsNQ586u18UkVLjAy/go-libp2p-kad-dht" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - mocknet "gx/ipfs/QmZPBrKq6S1fdYaRAzYZivJL12QkUqHwnNzF9wC8VXC4bo/go-libp2p/p2p/net/mock" - dht "gx/ipfs/QmcKxeQomXUjo54VwisTiXeic5FFBknwUPtT7yRWvmPD1D/go-libp2p-kad-dht" + mocknet "gx/ipfs/QmXZ6XetFwaDNmszPCux9DaKqMykEJGDtWHSqprn94UXzM/go-libp2p/p2p/net/mock" + "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index de70685a4..b9c348698 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -8,7 +8,7 @@ import ( "context" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "github.com/ipfs/go-ipfs/thirdparty/testutil" + "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 8b414d188..fe2320388 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -8,8 +8,8 @@ import ( pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" - p2phost "gx/ipfs/QmRNyPNJGNCaZyYonJj7owciWTsMd9gRfEKmZY3o6xwN3h/go-libp2p-host" cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + p2phost "gx/ipfs/QmW8Rgju5JrSMHP7RDNdiwwXyenRqAbtSaPfdQKQC7ZdH6/go-libp2p-host" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ) diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index aaa44befc..02b86232e 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -3,8 +3,8 @@ package offline import ( "bytes" "context" - "github.com/ipfs/go-ipfs/thirdparty/testutil" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" "testing" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 259d6c210..e3f00dca7 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -10,14 +10,14 @@ import ( pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" - "gx/ipfs/QmRNyPNJGNCaZyYonJj7owciWTsMd9gRfEKmZY3o6xwN3h/go-libp2p-host" + dhtpb "gx/ipfs/QmSFNexSqAnJKGfDwKnGx2sGB1EtLwsNQ586u18UkVLjAy/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + "gx/ipfs/QmW8Rgju5JrSMHP7RDNdiwwXyenRqAbtSaPfdQKQC7ZdH6/go-libp2p-host" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" - dhtpb "gx/ipfs/QmcKxeQomXUjo54VwisTiXeic5FFBknwUPtT7yRWvmPD1D/go-libp2p-kad-dht/pb" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index f7ed151c6..db77deeac 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,10 +2,10 @@ package proxy import ( context "context" + dhtpb "gx/ipfs/QmSFNexSqAnJKGfDwKnGx2sGB1EtLwsNQ586u18UkVLjAy/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" - dhtpb "gx/ipfs/QmcKxeQomXUjo54VwisTiXeic5FFBknwUPtT7yRWvmPD1D/go-libp2p-kad-dht/pb" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index bbcf2b5da..1d215ed9f 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -5,14 +5,14 @@ import ( "errors" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - host "gx/ipfs/QmRNyPNJGNCaZyYonJj7owciWTsMd9gRfEKmZY3o6xwN3h/go-libp2p-host" + dhtpb "gx/ipfs/QmSFNexSqAnJKGfDwKnGx2sGB1EtLwsNQ586u18UkVLjAy/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" + kbucket "gx/ipfs/QmVU26BGUSt3LkbWmoH7dP16mNz5VVRg4hDmWZBHAkq97w/go-libp2p-kbucket" + host "gx/ipfs/QmW8Rgju5JrSMHP7RDNdiwwXyenRqAbtSaPfdQKQC7ZdH6/go-libp2p-host" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" - kbucket "gx/ipfs/QmbiCMdwmmhif5axuGSHzYbPFGeKjLAuMY6JrGpVteHFsy/go-libp2p-kbucket" - dhtpb "gx/ipfs/QmcKxeQomXUjo54VwisTiXeic5FFBknwUPtT7yRWvmPD1D/go-libp2p-kad-dht/pb" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 94bf3ccaa..c5097e167 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -9,11 +9,11 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" + dhtpb "gx/ipfs/QmSFNexSqAnJKGfDwKnGx2sGB1EtLwsNQ586u18UkVLjAy/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" - dhtpb "gx/ipfs/QmcKxeQomXUjo54VwisTiXeic5FFBknwUPtT7yRWvmPD1D/go-libp2p-kad-dht/pb" ) // Server handles routing queries using a database backend diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 4e8ba883b..affcd3db6 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" + dhtpb "gx/ipfs/QmSFNexSqAnJKGfDwKnGx2sGB1EtLwsNQ586u18UkVLjAy/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dhtpb "gx/ipfs/QmcKxeQomXUjo54VwisTiXeic5FFBknwUPtT7yRWvmPD1D/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 6822cd341f77dc906509b9d80036dd0677dd2f76 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 16 Aug 2017 16:51:18 -0700 Subject: [PATCH 1958/3817] extract update go-testutil License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@74600a507cd13a00c40e01666ffff6d176ae3aa5 --- mfs/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 4a4e53a56..f35a54800 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - ci "github.com/ipfs/go-ipfs/thirdparty/testutil/ci" + ci "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil/ci" "context" cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" From 6640db6f589c52310e0faeedb904ed4bcfaa8cbf Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Wed, 28 Jun 2017 11:18:45 +0100 Subject: [PATCH 1959/3817] Do not publish public keys extractable from ID (with tests) License: MIT Signed-off-by: Justin Drake This commit was moved from ipfs/go-namesys@1cf05e7ae9ca7f1cfb6de5254b2da3e129f8b362 --- namesys/publisher.go | 18 ++++-- namesys/publisher_test.go | 115 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 namesys/publisher_test.go diff --git a/namesys/publisher.go b/namesys/publisher.go index c90207649..05a18cd95 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -150,18 +150,24 @@ func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqn entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds())) } - errs := make(chan error, 2) + errs := make(chan error, 2) // At most two errors (IPNS, and public key) + + // Attempt to extract the public key from the ID + extractedPublicKey := id.ExtractPublicKey() go func() { errs <- PublishEntry(ctx, r, ipnskey, entry) }() - go func() { - errs <- PublishPublicKey(ctx, r, namekey, k.GetPublic()) - }() + // Publish the public key if a public key cannot be extracted from the ID + if extractedPublicKey == nil { + go func() { + errs <- PublishPublicKey(ctx, r, namekey, k.GetPublic()) + }() - if err := waitOnErrChan(ctx, errs); err != nil { - return err + if err := waitOnErrChan(ctx, errs); err != nil { + return err + } } return waitOnErrChan(ctx, errs) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go new file mode 100644 index 000000000..53cf22a62 --- /dev/null +++ b/namesys/publisher_test.go @@ -0,0 +1,115 @@ +package namesys + +import ( + "context" + "crypto/rand" + "testing" + "time" + + path "github.com/ipfs/go-ipfs/path" + mockrouting "github.com/ipfs/go-ipfs/routing/mock" + dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" + + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr" + peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" +) + +type identity struct { + testutil.PeerNetParams +} + +func (p *identity) ID() peer.ID { + return p.PeerNetParams.ID +} + +func (p *identity) Address() ma.Multiaddr { + return p.Addr +} + +func (p *identity) PrivateKey() ci.PrivKey { + return p.PrivKey +} + +func (p *identity) PublicKey() ci.PubKey { + return p.PubKey +} + +func testNamekeyPublisher(t *testing.T, keyType int, expectedErr error, expectedExistence bool) { + // Context + ctx := context.Background() + + // Private key + privKey, pubKey, err := ci.GenerateKeyPairWithReader(keyType, 2048, rand.Reader) + if err != nil { + t.Fatal(err) + } + + // ID + var id peer.ID + switch keyType { + case ci.Ed25519: + id, err = peer.IDFromEd25519PublicKey(pubKey) + default: + id, err = peer.IDFromPublicKey(pubKey) + } + + if err != nil { + t.Fatal(err) + } + + // Value + value := path.Path("ipfs/TESTING") + + // Seqnum + seqnum := uint64(0) + + // Eol + eol := time.Now().Add(24 * time.Hour) + + // Routing value store + p := testutil.PeerNetParams{ + ID: id, + PrivKey: privKey, + PubKey: pubKey, + Addr: testutil.ZeroLocalTCPAddress, + } + + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + serv := mockrouting.NewServer() + r := serv.ClientWithDatastore(context.Background(), &identity{p}, dstore) + + err = PutRecordToRouting(ctx, privKey, value, seqnum, eol, r, id) + if err != nil { + t.Fatal(err) + } + + // Check for namekey existence in value store + namekey, _ := IpnsKeysForID(id) + _, err = r.GetValue(ctx, namekey) + if err != expectedErr { + t.Fatal(err) + } + + // Also check datastore for completeness + key := dshelp.NewKeyFromBinary([]byte(namekey)) + exists, err := dstore.Has(key) + if err != nil { + t.Fatal(err) + } + + if exists != expectedExistence { + t.Fatal("Unexpected key existence in datastore") + } +} + +func TestRSAPublisher(t *testing.T) { + testNamekeyPublisher(t, ci.RSA, nil, true) +} + +func TestEd22519Publisher(t *testing.T) { + testNamekeyPublisher(t, ci.Ed25519, ds.ErrNotFound, false) +} From 035967340f706737c030ba67d477a6d7aa9a2f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 23 Aug 2017 16:32:32 +0200 Subject: [PATCH 1960/3817] gx: update go-reuseport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@2647219811901e350e1cde32759e81d4f8a98150 --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index d87cb924c..60026fc93 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - mocknet "gx/ipfs/QmXZ6XetFwaDNmszPCux9DaKqMykEJGDtWHSqprn94UXzM/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmZyngpQxUGyx1T2bzEcst6YzERkvVwDzBMbsSQF4f1smE/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { From aa74cb903269312162366ccafd357c0cea071860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 23 Aug 2017 16:32:32 +0200 Subject: [PATCH 1961/3817] gx: update go-reuseport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-routing@d7213f1e07897a34c995c8182ea6e191411d78b0 --- routing/mock/dht.go | 4 ++-- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/routing/mock/dht.go b/routing/mock/dht.go index 7b0f25d2f..d83cf590b 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -2,11 +2,11 @@ package mockrouting import ( context "context" - dht "gx/ipfs/QmSFNexSqAnJKGfDwKnGx2sGB1EtLwsNQ586u18UkVLjAy/go-libp2p-kad-dht" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - mocknet "gx/ipfs/QmXZ6XetFwaDNmszPCux9DaKqMykEJGDtWHSqprn94UXzM/go-libp2p/p2p/net/mock" "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" + dht "gx/ipfs/QmZSEstKChFezMwTcAWH5UQxvRckmnvgVZjFeSnGLDHU3Y/go-libp2p-kad-dht" + mocknet "gx/ipfs/QmZyngpQxUGyx1T2bzEcst6YzERkvVwDzBMbsSQF4f1smE/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/supernode/client.go b/routing/supernode/client.go index e3f00dca7..88e187dc3 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -10,13 +10,13 @@ import ( pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" - dhtpb "gx/ipfs/QmSFNexSqAnJKGfDwKnGx2sGB1EtLwsNQ586u18UkVLjAy/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" "gx/ipfs/QmW8Rgju5JrSMHP7RDNdiwwXyenRqAbtSaPfdQKQC7ZdH6/go-libp2p-host" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + dhtpb "gx/ipfs/QmZSEstKChFezMwTcAWH5UQxvRckmnvgVZjFeSnGLDHU3Y/go-libp2p-kad-dht/pb" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index db77deeac..e112173c1 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,9 +2,9 @@ package proxy import ( context "context" - dhtpb "gx/ipfs/QmSFNexSqAnJKGfDwKnGx2sGB1EtLwsNQ586u18UkVLjAy/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" + dhtpb "gx/ipfs/QmZSEstKChFezMwTcAWH5UQxvRckmnvgVZjFeSnGLDHU3Y/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 1d215ed9f..7b53a6a16 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -5,13 +5,13 @@ import ( "errors" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - dhtpb "gx/ipfs/QmSFNexSqAnJKGfDwKnGx2sGB1EtLwsNQ586u18UkVLjAy/go-libp2p-kad-dht/pb" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" kbucket "gx/ipfs/QmVU26BGUSt3LkbWmoH7dP16mNz5VVRg4hDmWZBHAkq97w/go-libp2p-kbucket" host "gx/ipfs/QmW8Rgju5JrSMHP7RDNdiwwXyenRqAbtSaPfdQKQC7ZdH6/go-libp2p-host" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" + dhtpb "gx/ipfs/QmZSEstKChFezMwTcAWH5UQxvRckmnvgVZjFeSnGLDHU3Y/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index c5097e167..e14d14d8b 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -9,10 +9,10 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - dhtpb "gx/ipfs/QmSFNexSqAnJKGfDwKnGx2sGB1EtLwsNQ586u18UkVLjAy/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + dhtpb "gx/ipfs/QmZSEstKChFezMwTcAWH5UQxvRckmnvgVZjFeSnGLDHU3Y/go-libp2p-kad-dht/pb" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ) diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index affcd3db6..35a6edd59 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmSFNexSqAnJKGfDwKnGx2sGB1EtLwsNQ586u18UkVLjAy/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dhtpb "gx/ipfs/QmZSEstKChFezMwTcAWH5UQxvRckmnvgVZjFeSnGLDHU3Y/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 6dcd3727b59f3a1ec475704847084fdda7888872 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 30 Aug 2017 00:27:11 -0400 Subject: [PATCH 1962/3817] Fix broken import in publisher_test.go. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-namesys@e8c08d08dc5580c7a6361d733b8644e52824c029 --- namesys/publisher_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 53cf22a62..fb80d46f1 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -9,12 +9,12 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - testutil "github.com/ipfs/go-ipfs/thirdparty/testutil" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + testutil "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) From 146f43a703b7f4d43f703b1d9e18ef8a398f7d27 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 31 Aug 2017 16:33:09 -0700 Subject: [PATCH 1963/3817] various fixes for /ipfs fuse code License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@7c7036e00f5d06bc617ccb9fab41912db7d88af9 --- ipld/merkledag/merkledag.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index e0c244e45..ca672dea1 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -153,7 +153,10 @@ type sesGetter struct { func (sg *sesGetter) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { blk, err := sg.bs.GetBlock(ctx, c) - if err != nil { + switch err { + case bserv.ErrNotFound: + return nil, ErrNotFound + default: return nil, err } From 2a38421b818d18a074a5938533d00a231349d772 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 1 Sep 2017 14:51:01 -0700 Subject: [PATCH 1964/3817] address code review License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@3e73bba8534b8dcbd39e774037b28bab76743ee0 --- ipld/merkledag/merkledag.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index ca672dea1..173dee723 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -158,6 +158,8 @@ func (sg *sesGetter) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { return nil, ErrNotFound default: return nil, err + case nil: + // noop } return node.Decode(blk) From a18378cf4a9f0dd1474aadde408c3777c937b65b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 28 Aug 2017 20:32:16 -0700 Subject: [PATCH 1965/3817] gx: update go-cid, go-multibase, base32 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@38818c7c0844dfcc844853a209bfd9a22a222a72 --- routing/mock/centralized_client.go | 6 +++--- routing/mock/centralized_server.go | 4 ++-- routing/mock/centralized_test.go | 4 ++-- routing/mock/dht.go | 6 +++--- routing/mock/interface.go | 4 ++-- routing/none/none_client.go | 6 +++--- routing/offline/offline.go | 4 ++-- routing/offline/offline_test.go | 2 +- routing/supernode/client.go | 8 ++++---- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 13 files changed, 28 insertions(+), 28 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 0161ed8f4..0614d2d77 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -6,13 +6,13 @@ import ( "time" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" + "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 3ac4a558e..c5f90d1d8 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -6,10 +6,10 @@ import ( "sync" "time" - "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" + "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index fb35a08c1..84f9fcbcf 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -6,11 +6,11 @@ import ( "time" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" + "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/dht.go b/routing/mock/dht.go index d83cf590b..ac2df5f9e 100644 --- a/routing/mock/dht.go +++ b/routing/mock/dht.go @@ -2,11 +2,11 @@ package mockrouting import ( context "context" + dht "gx/ipfs/QmNV315eTphFgCttWPrT5ARNsiPNRLGFWHRJZyXyqvmjD6/go-libp2p-kad-dht" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" - dht "gx/ipfs/QmZSEstKChFezMwTcAWH5UQxvRckmnvgVZjFeSnGLDHU3Y/go-libp2p-kad-dht" - mocknet "gx/ipfs/QmZyngpQxUGyx1T2bzEcst6YzERkvVwDzBMbsSQF4f1smE/go-libp2p/p2p/net/mock" + "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + mocknet "gx/ipfs/QmbRT4BwPQEx4CPCd8LKYL46tFWYneGswQnHFdsuiczJRL/go-libp2p/p2p/net/mock" ) type mocknetserver struct { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index b9c348698..3522c6f38 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -8,9 +8,9 @@ import ( "context" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" + "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" - routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" + routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index fe2320388..8d65e81bc 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,10 +6,10 @@ import ( repo "github.com/ipfs/go-ipfs/repo" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - p2phost "gx/ipfs/QmW8Rgju5JrSMHP7RDNdiwwXyenRqAbtSaPfdQKQC7ZdH6/go-libp2p-host" + p2phost "gx/ipfs/QmUwW8jMQDxXhLD2j4EfWqLEMX3MsvyWcWGvJPVDh1aTmu/go-libp2p-host" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index f2a703110..df2657175 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,9 +7,9 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index 02b86232e..9f5b3f0b2 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -4,7 +4,7 @@ import ( "bytes" "context" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" + "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" "testing" ) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 88e187dc3..7ddc5bc06 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,15 +8,15 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" + dhtpb "gx/ipfs/QmNV315eTphFgCttWPrT5ARNsiPNRLGFWHRJZyXyqvmjD6/go-libp2p-kad-dht/pb" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - "gx/ipfs/QmW8Rgju5JrSMHP7RDNdiwwXyenRqAbtSaPfdQKQC7ZdH6/go-libp2p-host" + "gx/ipfs/QmUwW8jMQDxXhLD2j4EfWqLEMX3MsvyWcWGvJPVDh1aTmu/go-libp2p-host" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/QmZSEstKChFezMwTcAWH5UQxvRckmnvgVZjFeSnGLDHU3Y/go-libp2p-kad-dht/pb" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index e112173c1..6b8482431 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,9 +2,9 @@ package proxy import ( context "context" + dhtpb "gx/ipfs/QmNV315eTphFgCttWPrT5ARNsiPNRLGFWHRJZyXyqvmjD6/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/QmZSEstKChFezMwTcAWH5UQxvRckmnvgVZjFeSnGLDHU3Y/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 7b53a6a16..4a0254ab3 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,14 +4,14 @@ import ( "context" "errors" + dhtpb "gx/ipfs/QmNV315eTphFgCttWPrT5ARNsiPNRLGFWHRJZyXyqvmjD6/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" + kbucket "gx/ipfs/QmSAFA8v42u4gpJNy1tb7vW3JiiXiaYDC2b845c2RnNSJL/go-libp2p-kbucket" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" - kbucket "gx/ipfs/QmVU26BGUSt3LkbWmoH7dP16mNz5VVRg4hDmWZBHAkq97w/go-libp2p-kbucket" - host "gx/ipfs/QmW8Rgju5JrSMHP7RDNdiwwXyenRqAbtSaPfdQKQC7ZdH6/go-libp2p-host" + host "gx/ipfs/QmUwW8jMQDxXhLD2j4EfWqLEMX3MsvyWcWGvJPVDh1aTmu/go-libp2p-host" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/QmZSEstKChFezMwTcAWH5UQxvRckmnvgVZjFeSnGLDHU3Y/go-libp2p-kad-dht/pb" inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" ) diff --git a/routing/supernode/server.go b/routing/supernode/server.go index e14d14d8b..1ccda74f7 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,11 +8,11 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + dhtpb "gx/ipfs/QmNV315eTphFgCttWPrT5ARNsiPNRLGFWHRJZyXyqvmjD6/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/QmZSEstKChFezMwTcAWH5UQxvRckmnvgVZjFeSnGLDHU3Y/go-libp2p-kad-dht/pb" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ) diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 35a6edd59..453b18b82 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" + dhtpb "gx/ipfs/QmNV315eTphFgCttWPrT5ARNsiPNRLGFWHRJZyXyqvmjD6/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dhtpb "gx/ipfs/QmZSEstKChFezMwTcAWH5UQxvRckmnvgVZjFeSnGLDHU3Y/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From d2404664b65eb0e5fcfe5dca73d80187a9bf496f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 28 Aug 2017 20:32:16 -0700 Subject: [PATCH 1966/3817] gx: update go-cid, go-multibase, base32 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@7e34904d41a0eaa5495337ca64195b4c89137d3d --- namesys/namesys.go | 2 +- namesys/publisher.go | 2 +- namesys/publisher_test.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- namesys/routing.go | 4 ++-- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index e86979914..d1cda0870 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,7 +7,7 @@ import ( path "github.com/ipfs/go-ipfs/path" - routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" + routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" diff --git a/namesys/publisher.go b/namesys/publisher.go index 05a18cd95..66214100f 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -14,7 +14,7 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ft "github.com/ipfs/go-ipfs/unixfs" - routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" + routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index fb80d46f1..3149b3e48 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -12,9 +12,9 @@ import ( ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + testutil "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" - testutil "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 787636588..3ab382473 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,7 +11,7 @@ import ( path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" + routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 60026fc93..04c7b34f0 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - mocknet "gx/ipfs/QmZyngpQxUGyx1T2bzEcst6YzERkvVwDzBMbsSQF4f1smE/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmbRT4BwPQEx4CPCd8LKYL46tFWYneGswQnHFdsuiczJRL/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index b13c249bb..bb30a243a 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" - testutil "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil" + testutil "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" diff --git a/namesys/routing.go b/namesys/routing.go index faff33690..19e8b34d3 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -9,10 +9,10 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" mh "gx/ipfs/QmU9a9NV9RdPNwZQDYd5uKsm6N6LJLSvLbywDDYFbaaC6P/go-multihash" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" From 0dfbdd6e51d8d436b253494f2ebbd5f4c9512640 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 28 Aug 2017 20:32:16 -0700 Subject: [PATCH 1967/3817] gx: update go-cid, go-multibase, base32 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@d9c5d7df75a3e3d8b2d44df5266797daa17e53e2 --- ipld/merkledag/coding.go | 6 +++--- ipld/merkledag/merkledag.go | 8 ++++---- ipld/merkledag/merkledag_test.go | 6 +++--- ipld/merkledag/node.go | 4 ++-- ipld/merkledag/node_test.go | 2 +- ipld/merkledag/raw.go | 6 +++--- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/traverse/traverse_test.go | 2 +- ipld/merkledag/utils/diff.go | 4 ++-- ipld/merkledag/utils/diffenum.go | 4 ++-- ipld/merkledag/utils/diffenum_test.go | 4 ++-- ipld/merkledag/utils/utils.go | 2 +- ipld/merkledag/utils/utils_test.go | 2 +- 13 files changed, 26 insertions(+), 26 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 39da14a09..06644d912 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -5,12 +5,12 @@ import ( "sort" "strings" - "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" pb "github.com/ipfs/go-ipfs/merkledag/pb" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index e0c244e45..e83474375 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -9,10 +9,10 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" - ipldcbor "gx/ipfs/QmeebqVZeEXBqJ2B4urQWfdhwRRPm84ajnCo8x8pfwbsPM/go-ipld-cbor" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + ipldcbor "gx/ipfs/QmcRu2X6kdDKmCbMpYXKHVgDrhLqVYCACMe1aghUcdHj2z/go-ipld-cbor" ) // TODO: We should move these registrations elsewhere. Really, most of the IPLD diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 7e8c08069..5caabe94b 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -22,11 +22,11 @@ import ( mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" - blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) func TestNode(t *testing.T) { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 161905eb4..ad4f246cc 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -5,9 +5,9 @@ import ( "encoding/json" "fmt" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" mh "gx/ipfs/QmU9a9NV9RdPNwZQDYd5uKsm6N6LJLSvLbywDDYFbaaC6P/go-multihash" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index f585d87cc..e8520bcd3 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -8,7 +8,7 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) func TestRemoveLink(t *testing.T) { diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 856a407fc..e73fb88ba 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -2,11 +2,11 @@ package merkledag import ( "fmt" - "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) type RawNode struct { diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index f1aa9c6a8..5059a83db 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -5,7 +5,7 @@ import ( "context" "errors" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) // Order is an identifier for traversal algorithm orders diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 93afef1ea..3411d2e44 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -8,7 +8,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) func TestDFSPreNoSkip(t *testing.T) { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 167f03c14..e5d891203 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -7,8 +7,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) const ( diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go index 491c87cf1..7e3a76356 100644 --- a/ipld/merkledag/utils/diffenum.go +++ b/ipld/merkledag/utils/diffenum.go @@ -6,8 +6,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) // DiffEnumerate fetches every object in the graph pointed to by 'to' that is diff --git a/ipld/merkledag/utils/diffenum_test.go b/ipld/merkledag/utils/diffenum_test.go index e34440ada..ed5e0db36 100644 --- a/ipld/merkledag/utils/diffenum_test.go +++ b/ipld/merkledag/utils/diffenum_test.go @@ -8,8 +8,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) func buildNode(name string, desc map[string]ndesc, out map[string]node.Node) node.Node { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 791068e74..6f08bf2fc 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -10,9 +10,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" syncds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) type Editor struct { diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 815e7d13f..75d7181fc 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" context "context" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ) func TestAddLink(t *testing.T) { From e6542fd58a7d5691fa8d76198fb481e6378bc3c2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 28 Aug 2017 20:32:16 -0700 Subject: [PATCH 1968/3817] gx: update go-cid, go-multibase, base32 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@d405e1858a48354398d771136bb8c4e956fb4476 --- mfs/dir.go | 4 ++-- mfs/file.go | 2 +- mfs/mfs_test.go | 4 ++-- mfs/ops.go | 2 +- mfs/repub_test.go | 4 ++-- mfs/system.go | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 387becb6c..a489336d6 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -15,8 +15,8 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) var ErrNotYetImplemented = errors.New("not yet implemented") diff --git a/mfs/file.go b/mfs/file.go index d527eacf2..6e249e329 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 3be7ed8fc..09e9de00d 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -24,11 +24,11 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) func emptyDirNode() *dag.ProtoNode { diff --git a/mfs/ops.go b/mfs/ops.go index 33f8f9b79..a086e8602 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -9,7 +9,7 @@ import ( path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) // Mv moves the file or directory at 'src' to 'dst' diff --git a/mfs/repub_test.go b/mfs/repub_test.go index f35a54800..1c21bd793 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - ci "gx/ipfs/QmZJD56ZWLViJAVkvLc7xbbDerHzUMLr2X4fLRYfbxZWDN/go-testutil/ci" + ci "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil/ci" "context" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index 086808af5..0641704cf 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -19,9 +19,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) var ErrNotExist = errors.New("no such rootfs") From 0855c20ae1c2f3244dc3b5db5cc5474c2cda5f56 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 28 Aug 2017 20:32:16 -0700 Subject: [PATCH 1969/3817] gx: update go-cid, go-multibase, base32 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@ef6144db00d6c9c97442fa5897c5b395f94b8a52 --- unixfs/archive/archive.go | 2 +- unixfs/archive/tar/writer.go | 2 +- unixfs/hamt/hamt.go | 4 ++-- unixfs/io/dagreader.go | 2 +- unixfs/io/dirbuilder.go | 4 ++-- unixfs/io/resolve.go | 2 +- unixfs/mod/dagmodifier.go | 4 ++-- unixfs/test/utils.go | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index fc380baf8..b913300ef 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -11,7 +11,7 @@ import ( tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) // DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index dde968cec..f00d0422f 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -13,7 +13,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" upb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index b91738a08..a360c37c2 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -31,8 +31,8 @@ import ( format "github.com/ipfs/go-ipfs/unixfs" upb "github.com/ipfs/go-ipfs/unixfs/pb" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" "gx/ipfs/QmfJHywXQu98UeZtGJBQrPAR6AtmDjjbe3qjTo9piXHPnx/murmur3" ) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index ae1517362..a7ed7944e 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index b4af5441b..76ec34faa 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -8,9 +8,9 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) // ShardSplitThreshold specifies how large of an unsharded directory diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index f1cb7f35b..dceba71a7 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -7,7 +7,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) // ResolveUnixfsOnce resolves a single hop of a path through a graph in a diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index cdf2b4c78..e3955e20c 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -14,8 +14,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index c70d3f3bd..c0b8ae18d 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -14,8 +14,8 @@ import ( mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) func SizeSplitterGen(size int64) chunk.SplitterGen { From b1f0870348cb75e63ccbf2e90ea38a6bb93780e9 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 28 Aug 2017 20:32:16 -0700 Subject: [PATCH 1970/3817] gx: update go-cid, go-multibase, base32 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-path@5f825db8701e945a3618e68937f28a5ed1da7b63 --- path/path.go | 2 +- path/resolver.go | 4 ++-- path/resolver_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/path/path.go b/path/path.go index d56efd1d8..85e163a25 100644 --- a/path/path.go +++ b/path/path.go @@ -5,7 +5,7 @@ import ( "path" "strings" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ) // ErrBadPath is returned when a given path is incorrectly formatted diff --git a/path/resolver.go b/path/resolver.go index ef9bc90c1..5b8fe515a 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -9,9 +9,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) var log = logging.Logger("path") diff --git a/path/resolver_test.go b/path/resolver_test.go index ba903d01c..200fe1f37 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -9,8 +9,8 @@ import ( dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" util "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) func randNode() *merkledag.ProtoNode { From 17ec98358367d8098d1a788e7765a5e733c8508f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 28 Aug 2017 20:32:16 -0700 Subject: [PATCH 1971/3817] gx: update go-cid, go-multibase, base32 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-blockstore@b5f4f310b1aaeb59b4d27b7834075bae5a95c985 --- blockstore/arc_cache.go | 4 ++-- blockstore/arc_cache_test.go | 4 ++-- blockstore/blockstore.go | 4 ++-- blockstore/blockstore_test.go | 4 ++-- blockstore/bloom_cache.go | 4 ++-- blockstore/bloom_cache_test.go | 2 +- blockstore/util/remove.go | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index e7a702406..47ddf2d4c 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -3,10 +3,10 @@ package blockstore import ( "context" - "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" ) diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index b6575a58c..0232249d3 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -4,9 +4,9 @@ import ( "context" "testing" - "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" syncds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" ) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index e8f11aa7c..21bf04612 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -9,10 +9,10 @@ import ( "sync/atomic" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dsns "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/namespace" dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 4382c9111..8bc2b2f1b 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -7,10 +7,10 @@ import ( "testing" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" ds_sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 8360f82dd..79a4596e0 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -5,10 +5,10 @@ import ( "sync/atomic" "time" - "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" bloom "gx/ipfs/QmXqKGu7QzfRzFC4yd5aL9sThYx22vY163VGwmxfp5qGHk/bbloom" ) diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index af318a0aa..4eb8d1aca 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" context "context" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 1b6d4f33a..47552988b 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -5,7 +5,7 @@ import ( "fmt" "io" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" bs "github.com/ipfs/go-ipfs/blocks/blockstore" From acc4bfcb8327c45d25cd6936f670c61c8aa5aa3b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 28 Aug 2017 20:32:16 -0700 Subject: [PATCH 1972/3817] gx: update go-cid, go-multibase, base32 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-chunker@a6be3040da28168059210e078ebfebcbaaa7e396 --- chunker/rabin_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 6f9d6b7ff..ede2bc20a 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -4,7 +4,7 @@ import ( "bytes" "fmt" "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" "io" "testing" ) From 70c93152caf5e394eed367fe60f0e1fce078b512 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 28 Aug 2017 20:32:16 -0700 Subject: [PATCH 1973/3817] gx: update go-cid, go-multibase, base32 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@22195b644a585291debafbbeb1c5031afa2a08b7 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 2 +- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index c2ce05945..c9e6cac70 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -9,9 +9,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index c73d3dd7b..ce3722aeb 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -12,10 +12,10 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" dutils "github.com/ipfs/go-ipfs/merkledag/utils" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 57683eef9..fc303b6f2 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -10,8 +10,8 @@ import ( "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" ) diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 74bea0375..c191744fd 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -12,8 +12,8 @@ import ( "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - node "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 310780f30..856a86a97 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -10,7 +10,7 @@ import ( offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" ) From 8126cc6294f2a5d169b69c145c6359efb7961394 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 28 Aug 2017 20:32:16 -0700 Subject: [PATCH 1974/3817] gx: update go-cid, go-multibase, base32 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@e6b1b9e9ef6dbfb3c4aa0d2ed882a721015b79b6 --- blockservice/blockservice.go | 4 ++-- blockservice/blockservice_test.go | 2 +- blockservice/test/blocks_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index f87674056..6bd04c3ab 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -12,9 +12,9 @@ import ( exchange "github.com/ipfs/go-ipfs/exchange" bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" ) var log = logging.Logger("blockservice") diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index 663d9856d..1ce735f8b 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" butil "github.com/ipfs/go-ipfs/blocks/blocksutil" offline "github.com/ipfs/go-ipfs/exchange/offline" - "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 96ed5e54d..59e1b4e91 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -10,10 +10,10 @@ import ( blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" ) From 4c8be8166fedc554e99ce3b3835de76be2c46aae Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 28 Aug 2017 20:32:16 -0700 Subject: [PATCH 1975/3817] gx: update go-cid, go-multibase, base32 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@763b4fc8c5e6b1502f8ffc97a0cb2d99a18163f1 --- filestore/filestore.go | 4 ++-- filestore/filestore_test.go | 2 +- filestore/fsrefstore.go | 4 ++-- filestore/util.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index d69aa9d23..9e33f7ed8 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -12,10 +12,10 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" - "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" ) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index ce2f4b168..8f972dec0 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -11,7 +11,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" ) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index ad7dc898e..9f8ac62f5 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -11,10 +11,10 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" - "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dsns "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/namespace" dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" diff --git a/filestore/util.go b/filestore/util.go index 3086ccadb..1e4f462b9 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -8,7 +8,7 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" ) From b38e8ebf12b2a17924624ec116b39bb0d709f331 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 28 Aug 2017 20:32:16 -0700 Subject: [PATCH 1976/3817] gx: update go-cid, go-multibase, base32 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-ds-help@4b5026248ff4c569fcdca1c7fa47e19313e5627e --- datastore/dshelp/key.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 36cc841f3..21a12c7b4 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -1,9 +1,9 @@ package dshelp import ( - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - base32 "gx/ipfs/QmZvZSVtvxak4dcTkhsQhqd1SQ6rg5UzaSTu62WfWKjj93/base32" + base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) // TODO: put this code into the go-datastore itself From fc81cbaf3ee8ec42e2c8c5337bd4fd5ad8ed6aaf Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 28 Aug 2017 20:32:16 -0700 Subject: [PATCH 1977/3817] gx: update go-cid, go-multibase, base32 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-exchange-offline@58a7fd34fa09a38c8df473365a938d3da2e3b662 --- exchange/offline/offline.go | 4 ++-- exchange/offline/offline_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 1c0b1a424..fed9c2d87 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -7,9 +7,9 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index d61947562..fce8b613e 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -6,10 +6,10 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" - blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" ds_sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" ) From fe32e90cb6326d44f8ed28fa0ecc9e5f28e36a8e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 28 Aug 2017 20:32:16 -0700 Subject: [PATCH 1978/3817] gx: update go-cid, go-multibase, base32 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-exchange-interface@79cacb5624acc5ad0621ea0a61395d8c1338d667 --- exchange/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index 1373f0d3c..4fe8e0f3d 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -5,9 +5,9 @@ import ( "context" "io" - blocks "gx/ipfs/QmVA4mafxbfH5aEvNz8fyoxC6J1xhAtw88B4GerPznSZBg/go-block-format" + blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ) // Any type that implements exchange.Interface may be used as an IPFS block From d45714f3fa2fcf0224d6b0774cd729941109a450 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 28 Aug 2017 20:32:16 -0700 Subject: [PATCH 1979/3817] gx: update go-cid, go-multibase, base32 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@6c6807b399ac688cbe92d85156b74cae3bd45321 --- coreiface/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 81197bb46..78a64dd40 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -5,8 +5,8 @@ import ( "errors" "io" - cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid" - ipld "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + ipld "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) type Path interface { From e35c0cc0a8b95627dda05c6b6b21712d0e13e4b2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 4 Sep 2017 20:15:20 -0700 Subject: [PATCH 1980/3817] remove some dead code License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@df02a9befa77fb250839dd34cdfbfc4e49cd384c --- routing/mock/dht.go | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 routing/mock/dht.go diff --git a/routing/mock/dht.go b/routing/mock/dht.go deleted file mode 100644 index ac2df5f9e..000000000 --- a/routing/mock/dht.go +++ /dev/null @@ -1,37 +0,0 @@ -package mockrouting - -import ( - context "context" - dht "gx/ipfs/QmNV315eTphFgCttWPrT5ARNsiPNRLGFWHRJZyXyqvmjD6/go-libp2p-kad-dht" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" - mocknet "gx/ipfs/QmbRT4BwPQEx4CPCd8LKYL46tFWYneGswQnHFdsuiczJRL/go-libp2p/p2p/net/mock" -) - -type mocknetserver struct { - mn mocknet.Mocknet -} - -func NewDHTNetwork(mn mocknet.Mocknet) Server { - return &mocknetserver{ - mn: mn, - } -} - -func (rs *mocknetserver) Client(p testutil.Identity) Client { - return rs.ClientWithDatastore(context.TODO(), p, ds.NewMapDatastore()) -} - -func (rs *mocknetserver) ClientWithDatastore(ctx context.Context, p testutil.Identity, ds ds.Datastore) Client { - - // FIXME AddPeer doesn't appear to be idempotent - - host, err := rs.mn.AddPeer(p.PrivateKey(), p.Address()) - if err != nil { - panic("FIXME") - } - return dht.NewDHT(ctx, host, sync.MutexWrap(ds)) -} - -var _ Server = &mocknetserver{} From 41178aaeef0a935d84b5e2d76be2d95b7179cc84 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 4 Sep 2017 23:37:11 -0700 Subject: [PATCH 1981/3817] gx: update go-ws-transport License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@a8742aaa7e4d581c2da1046bb6bab37e98870357 --- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index 7ddc5bc06..ed8ebab85 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -8,7 +8,6 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - dhtpb "gx/ipfs/QmNV315eTphFgCttWPrT5ARNsiPNRLGFWHRJZyXyqvmjD6/go-libp2p-kad-dht/pb" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" @@ -18,6 +17,7 @@ import ( peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" + dhtpb "gx/ipfs/QmeQMs9pr9Goci9xJ1Wo5ZQrknzBZwnmHYWJXA8stQDFMx/go-libp2p-kad-dht/pb" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 6b8482431..5b996b02b 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,10 +2,10 @@ package proxy import ( context "context" - dhtpb "gx/ipfs/QmNV315eTphFgCttWPrT5ARNsiPNRLGFWHRJZyXyqvmjD6/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" + dhtpb "gx/ipfs/QmeQMs9pr9Goci9xJ1Wo5ZQrknzBZwnmHYWJXA8stQDFMx/go-libp2p-kad-dht/pb" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 4a0254ab3..67c06458d 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,7 +4,6 @@ import ( "context" "errors" - dhtpb "gx/ipfs/QmNV315eTphFgCttWPrT5ARNsiPNRLGFWHRJZyXyqvmjD6/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" kbucket "gx/ipfs/QmSAFA8v42u4gpJNy1tb7vW3JiiXiaYDC2b845c2RnNSJL/go-libp2p-kbucket" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" @@ -13,6 +12,7 @@ import ( peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" + dhtpb "gx/ipfs/QmeQMs9pr9Goci9xJ1Wo5ZQrknzBZwnmHYWJXA8stQDFMx/go-libp2p-kad-dht/pb" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 1ccda74f7..081f42313 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -8,12 +8,12 @@ import ( proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - dhtpb "gx/ipfs/QmNV315eTphFgCttWPrT5ARNsiPNRLGFWHRJZyXyqvmjD6/go-libp2p-kad-dht/pb" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" + dhtpb "gx/ipfs/QmeQMs9pr9Goci9xJ1Wo5ZQrknzBZwnmHYWJXA8stQDFMx/go-libp2p-kad-dht/pb" ) // Server handles routing queries using a database backend diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 453b18b82..6d3155f88 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" - dhtpb "gx/ipfs/QmNV315eTphFgCttWPrT5ARNsiPNRLGFWHRJZyXyqvmjD6/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dhtpb "gx/ipfs/QmeQMs9pr9Goci9xJ1Wo5ZQrknzBZwnmHYWJXA8stQDFMx/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 819d9f016b6771fa37e52730b89a8ec9eaeec35d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 4 Sep 2017 23:37:11 -0700 Subject: [PATCH 1982/3817] gx: update go-ws-transport License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@facb6fd902726f0707b31c68635b1e404886df5a --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 04c7b34f0..364d84e60 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - mocknet "gx/ipfs/QmbRT4BwPQEx4CPCd8LKYL46tFWYneGswQnHFdsuiczJRL/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmXZyBQMkqSYigxhJResC6fLWDGFhbphK67eZoqMDUvBmK/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { From a1a652a68d9417118c2eb5e55da70dec80fc7d67 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 14 Sep 2017 11:39:25 -0700 Subject: [PATCH 1983/3817] gx: update go-stream-muxer Introduces a new Reset method on streams that kills both sides of the connection. Close now officially just closes the write side (what it did all along...) * Also pull through shiny new go-multiplexer fixes. * Also pull in go-reuseport update. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@9a26f829c8e391182e5f1156aa9973f5b8fc1205 --- routing/none/none_client.go | 2 +- routing/supernode/client.go | 4 ++-- routing/supernode/proxy/loopback.go | 4 ++-- routing/supernode/proxy/standard.go | 6 +++--- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 8d65e81bc..ae9c6c352 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -9,8 +9,8 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - p2phost "gx/ipfs/QmUwW8jMQDxXhLD2j4EfWqLEMX3MsvyWcWGvJPVDh1aTmu/go-libp2p-host" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + p2phost "gx/ipfs/QmaSxYRuMq4pkpBBG2CYaRrPx2z7NmMVEs34b9g61biQA6/go-libp2p-host" ) type nilclient struct { diff --git a/routing/supernode/client.go b/routing/supernode/client.go index ed8ebab85..a9dacd82d 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -13,11 +13,11 @@ import ( pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" - "gx/ipfs/QmUwW8jMQDxXhLD2j4EfWqLEMX3MsvyWcWGvJPVDh1aTmu/go-libp2p-host" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + "gx/ipfs/QmaSxYRuMq4pkpBBG2CYaRrPx2z7NmMVEs34b9g61biQA6/go-libp2p-host" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" - dhtpb "gx/ipfs/QmeQMs9pr9Goci9xJ1Wo5ZQrknzBZwnmHYWJXA8stQDFMx/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmdPuLwm97UHxH7nR33TJxFi4PteFEDZagYWfTY1mNQWA5/go-libp2p-kad-dht/pb" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index 5b996b02b..e8b77b322 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -2,10 +2,10 @@ package proxy import ( context "context" + inet "gx/ipfs/QmNa31VPzC561NWwRsJLE7nGYZYuuD2QfpK2b1q9BK54J1/go-libp2p-net" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" - dhtpb "gx/ipfs/QmeQMs9pr9Goci9xJ1Wo5ZQrknzBZwnmHYWJXA8stQDFMx/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmdPuLwm97UHxH7nR33TJxFi4PteFEDZagYWfTY1mNQWA5/go-libp2p-kad-dht/pb" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index 67c06458d..d5a9b51bf 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -4,15 +4,15 @@ import ( "context" "errors" + inet "gx/ipfs/QmNa31VPzC561NWwRsJLE7nGYZYuuD2QfpK2b1q9BK54J1/go-libp2p-net" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" kbucket "gx/ipfs/QmSAFA8v42u4gpJNy1tb7vW3JiiXiaYDC2b845c2RnNSJL/go-libp2p-kbucket" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" - host "gx/ipfs/QmUwW8jMQDxXhLD2j4EfWqLEMX3MsvyWcWGvJPVDh1aTmu/go-libp2p-host" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - inet "gx/ipfs/QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF/go-libp2p-net" - dhtpb "gx/ipfs/QmeQMs9pr9Goci9xJ1Wo5ZQrknzBZwnmHYWJXA8stQDFMx/go-libp2p-kad-dht/pb" + host "gx/ipfs/QmaSxYRuMq4pkpBBG2CYaRrPx2z7NmMVEs34b9g61biQA6/go-libp2p-host" + dhtpb "gx/ipfs/QmdPuLwm97UHxH7nR33TJxFi4PteFEDZagYWfTY1mNQWA5/go-libp2p-kad-dht/pb" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 081f42313..46f00ccdf 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -13,7 +13,7 @@ import ( peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" - dhtpb "gx/ipfs/QmeQMs9pr9Goci9xJ1Wo5ZQrknzBZwnmHYWJXA8stQDFMx/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmdPuLwm97UHxH7nR33TJxFi4PteFEDZagYWfTY1mNQWA5/go-libp2p-kad-dht/pb" ) // Server handles routing queries using a database backend diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 6d3155f88..6579a6b35 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -4,7 +4,7 @@ import ( "testing" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dhtpb "gx/ipfs/QmeQMs9pr9Goci9xJ1Wo5ZQrknzBZwnmHYWJXA8stQDFMx/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmdPuLwm97UHxH7nR33TJxFi4PteFEDZagYWfTY1mNQWA5/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From dd7472fae7cbc82ea43b26efa09c83bef7497b6b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 14 Sep 2017 11:39:25 -0700 Subject: [PATCH 1984/3817] gx: update go-stream-muxer Introduces a new Reset method on streams that kills both sides of the connection. Close now officially just closes the write side (what it did all along...) * Also pull through shiny new go-multiplexer fixes. * Also pull in go-reuseport update. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@f3fec337803608235494be5dc1f8a16abf1c5dc5 --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 364d84e60..c1398a909 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - mocknet "gx/ipfs/QmXZyBQMkqSYigxhJResC6fLWDGFhbphK67eZoqMDUvBmK/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmZ3ma9g2NTg7GNF1ntWNRa1GW9jVzGq8UE9cKCwRKv6dS/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { From 5844fd6dc3124b1a73957fb1aba2cd7c4bbb9a4f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 14 Sep 2017 11:52:14 -0700 Subject: [PATCH 1985/3817] use stream.Reset where appropriate License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@38fc7aeea18921b9a4c8f8ead11aebea6edb3376 --- routing/supernode/proxy/loopback.go | 5 ++++- routing/supernode/proxy/standard.go | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index e8b77b322..f6d9c0bb7 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -42,6 +42,7 @@ func (lb *Loopback) HandleStream(s inet.Stream) { pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) var incoming dhtpb.Message if err := pbr.ReadMsg(&incoming); err != nil { + s.Reset() log.Debug(err) return } @@ -51,6 +52,8 @@ func (lb *Loopback) HandleStream(s inet.Stream) { pbw := ggio.NewDelimitedWriter(s) if err := pbw.WriteMsg(outgoing); err != nil { - return // TODO logerr + s.Reset() + log.Debug(err) + return } } diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index d5a9b51bf..eddd1e84f 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -60,7 +60,7 @@ func (px *standard) Bootstrap(ctx context.Context) error { func (p *standard) HandleStream(s inet.Stream) { // TODO(brian): Should clients be able to satisfy requests? log.Error("supernode client received (dropped) a routing message from", s.Conn().RemotePeer()) - s.Close() + s.Reset() } const replicationFactor = 2 @@ -102,9 +102,15 @@ func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote pe if err != nil { return err } - defer s.Close() pbw := ggio.NewDelimitedWriter(s) - return pbw.WriteMsg(m) + + err = pbw.WriteMsg(m) + if err == nil { + s.Close() + } else { + s.Reset() + } + return err } // SendRequest sends the request to each remote sequentially (randomized order), @@ -139,17 +145,20 @@ func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote pe r := ggio.NewDelimitedReader(s, inet.MessageSizeMax) w := ggio.NewDelimitedWriter(s) if err = w.WriteMsg(m); err != nil { + s.Reset() e.SetError(err) return nil, err } response := &dhtpb.Message{} if err = r.ReadMsg(response); err != nil { + s.Reset() e.SetError(err) return nil, err } // need ctx expiration? if response == nil { + s.Reset() err := errors.New("no response to request") e.SetError(err) return nil, err From 4a1f699f3bebccb7351ddefff56766590b0ea693 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 15 Sep 2017 18:56:44 -0700 Subject: [PATCH 1986/3817] update yamux We need to cancel out all readers/writers on stream reset. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@dac011aa6b28aa7627ed20ce662810afcdddcd64 --- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index a9dacd82d..fcfca86ca 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -13,11 +13,11 @@ import ( pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" + dhtpb "gx/ipfs/QmXF6hA3AWGNmE33GarqeCu3ksAXhLiwhcRR4mvfyWTZcT/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" "gx/ipfs/QmaSxYRuMq4pkpBBG2CYaRrPx2z7NmMVEs34b9g61biQA6/go-libp2p-host" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" - dhtpb "gx/ipfs/QmdPuLwm97UHxH7nR33TJxFi4PteFEDZagYWfTY1mNQWA5/go-libp2p-kad-dht/pb" ) var log = logging.Logger("supernode") diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index f6d9c0bb7..a2887851c 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -3,9 +3,9 @@ package proxy import ( context "context" inet "gx/ipfs/QmNa31VPzC561NWwRsJLE7nGYZYuuD2QfpK2b1q9BK54J1/go-libp2p-net" + dhtpb "gx/ipfs/QmXF6hA3AWGNmE33GarqeCu3ksAXhLiwhcRR4mvfyWTZcT/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - dhtpb "gx/ipfs/QmdPuLwm97UHxH7nR33TJxFi4PteFEDZagYWfTY1mNQWA5/go-libp2p-kad-dht/pb" ) // RequestHandler handles routing requests locally diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index eddd1e84f..a7359a75f 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -9,10 +9,10 @@ import ( kbucket "gx/ipfs/QmSAFA8v42u4gpJNy1tb7vW3JiiXiaYDC2b845c2RnNSJL/go-libp2p-kbucket" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" + dhtpb "gx/ipfs/QmXF6hA3AWGNmE33GarqeCu3ksAXhLiwhcRR4mvfyWTZcT/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" host "gx/ipfs/QmaSxYRuMq4pkpBBG2CYaRrPx2z7NmMVEs34b9g61biQA6/go-libp2p-host" - dhtpb "gx/ipfs/QmdPuLwm97UHxH7nR33TJxFi4PteFEDZagYWfTY1mNQWA5/go-libp2p-kad-dht/pb" ) const ProtocolSNR = "/ipfs/supernoderouting" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 46f00ccdf..458a36a49 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -10,10 +10,10 @@ import ( pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dhtpb "gx/ipfs/QmXF6hA3AWGNmE33GarqeCu3ksAXhLiwhcRR4mvfyWTZcT/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" - dhtpb "gx/ipfs/QmdPuLwm97UHxH7nR33TJxFi4PteFEDZagYWfTY1mNQWA5/go-libp2p-kad-dht/pb" ) // Server handles routing queries using a database backend diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index 6579a6b35..d9d214c33 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -4,7 +4,7 @@ import ( "testing" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dhtpb "gx/ipfs/QmdPuLwm97UHxH7nR33TJxFi4PteFEDZagYWfTY1mNQWA5/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmXF6hA3AWGNmE33GarqeCu3ksAXhLiwhcRR4mvfyWTZcT/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 53158b40756aa0ef4f348d47eb2e2654e542dfb2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 15 Sep 2017 18:56:44 -0700 Subject: [PATCH 1987/3817] update yamux We need to cancel out all readers/writers on stream reset. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@38886dbfb2062305637d77b41971933210dba7ae --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index c1398a909..2a7187537 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - mocknet "gx/ipfs/QmZ3ma9g2NTg7GNF1ntWNRa1GW9jVzGq8UE9cKCwRKv6dS/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmRQ76P5dgvxTujhfPsCRAG83rC15jgb1G9bKLuomuC6dQ/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { From 825e97aed83037bf87fa4b2761a81bc63ad7a7ef Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 19 Sep 2017 16:00:44 -0700 Subject: [PATCH 1988/3817] fix dht memory leak update go-libp2p-kad-dht to fix a nasty memory leak License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@6cac0ea04794a7cd7c73580bed8ed9b307f88732 --- routing/supernode/client.go | 2 +- routing/supernode/proxy/loopback.go | 2 +- routing/supernode/proxy/standard.go | 2 +- routing/supernode/server.go | 2 +- routing/supernode/server_test.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/routing/supernode/client.go b/routing/supernode/client.go index fcfca86ca..6764f784f 100644 --- a/routing/supernode/client.go +++ b/routing/supernode/client.go @@ -13,7 +13,7 @@ import ( pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" - dhtpb "gx/ipfs/QmXF6hA3AWGNmE33GarqeCu3ksAXhLiwhcRR4mvfyWTZcT/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmT7PnPxYkeKPCG8pAnucfcjrXc15Q7FgvFv7YC24EPrw8/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" "gx/ipfs/QmaSxYRuMq4pkpBBG2CYaRrPx2z7NmMVEs34b9g61biQA6/go-libp2p-host" diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go index a2887851c..16480e65c 100644 --- a/routing/supernode/proxy/loopback.go +++ b/routing/supernode/proxy/loopback.go @@ -3,7 +3,7 @@ package proxy import ( context "context" inet "gx/ipfs/QmNa31VPzC561NWwRsJLE7nGYZYuuD2QfpK2b1q9BK54J1/go-libp2p-net" - dhtpb "gx/ipfs/QmXF6hA3AWGNmE33GarqeCu3ksAXhLiwhcRR4mvfyWTZcT/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmT7PnPxYkeKPCG8pAnucfcjrXc15Q7FgvFv7YC24EPrw8/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" ) diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go index a7359a75f..09c78d69a 100644 --- a/routing/supernode/proxy/standard.go +++ b/routing/supernode/proxy/standard.go @@ -9,7 +9,7 @@ import ( kbucket "gx/ipfs/QmSAFA8v42u4gpJNy1tb7vW3JiiXiaYDC2b845c2RnNSJL/go-libp2p-kbucket" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" - dhtpb "gx/ipfs/QmXF6hA3AWGNmE33GarqeCu3ksAXhLiwhcRR4mvfyWTZcT/go-libp2p-kad-dht/pb" + dhtpb "gx/ipfs/QmT7PnPxYkeKPCG8pAnucfcjrXc15Q7FgvFv7YC24EPrw8/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" host "gx/ipfs/QmaSxYRuMq4pkpBBG2CYaRrPx2z7NmMVEs34b9g61biQA6/go-libp2p-host" diff --git a/routing/supernode/server.go b/routing/supernode/server.go index 458a36a49..592cd0732 100644 --- a/routing/supernode/server.go +++ b/routing/supernode/server.go @@ -9,8 +9,8 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" + dhtpb "gx/ipfs/QmT7PnPxYkeKPCG8pAnucfcjrXc15Q7FgvFv7YC24EPrw8/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dhtpb "gx/ipfs/QmXF6hA3AWGNmE33GarqeCu3ksAXhLiwhcRR4mvfyWTZcT/go-libp2p-kad-dht/pb" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go index d9d214c33..348fbe392 100644 --- a/routing/supernode/server_test.go +++ b/routing/supernode/server_test.go @@ -3,8 +3,8 @@ package supernode import ( "testing" + dhtpb "gx/ipfs/QmT7PnPxYkeKPCG8pAnucfcjrXc15Q7FgvFv7YC24EPrw8/go-libp2p-kad-dht/pb" datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dhtpb "gx/ipfs/QmXF6hA3AWGNmE33GarqeCu3ksAXhLiwhcRR4mvfyWTZcT/go-libp2p-kad-dht/pb" ) func TestPutProviderDoesntResultInDuplicates(t *testing.T) { From 0cf7c3505f2c1193948a0edf33f1e995dfd6254a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 5 Oct 2017 13:51:10 +0200 Subject: [PATCH 1989/3817] gx: update cbor to 0.2.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-merkledag@57cfb4c241fcba0c29ae66cb828d9d81f77e3bd8 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 8e7e74b1c..214fbbd84 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -12,7 +12,7 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" - ipldcbor "gx/ipfs/QmcRu2X6kdDKmCbMpYXKHVgDrhLqVYCACMe1aghUcdHj2z/go-ipld-cbor" + ipldcbor "gx/ipfs/QmWCs8kMecJwCPK8JThue8TjgM2ieJ2HjTLDu7Cv2NEmZi/go-ipld-cbor" ) // TODO: We should move these registrations elsewhere. Really, most of the IPLD From 808c7bea35630577d1ba7727a893aa94c0414901 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 5 Oct 2017 17:10:16 +0300 Subject: [PATCH 1990/3817] update go-testutil to 1.1.12 License: MIT Signed-off-by: vyzo This commit was moved from ipfs/go-ipfs-routing@7538286951c86c2318bce4a11ea8d4ed34003550 --- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/interface.go | 2 +- routing/offline/offline_test.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 0614d2d77..407629299 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -6,7 +6,7 @@ import ( "time" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index c5f90d1d8..02929a4cf 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -6,7 +6,7 @@ import ( "sync" "time" - "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 84f9fcbcf..9ecba1181 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -6,7 +6,7 @@ import ( "time" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 3522c6f38..5dcbbd21d 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -8,7 +8,7 @@ import ( "context" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index 9f5b3f0b2..f922f399c 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -3,8 +3,8 @@ package offline import ( "bytes" "context" + "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" "testing" ) From e87f67d10f4b991ba94c3210d0af9e2ffd0fe2e6 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 5 Oct 2017 17:10:16 +0300 Subject: [PATCH 1991/3817] update go-testutil to 1.1.12 License: MIT Signed-off-by: vyzo This commit was moved from ipfs/go-namesys@32574df8cadde4250f37e5faa21cee5ab2cf9371 --- namesys/publisher_test.go | 2 +- namesys/resolve_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 3149b3e48..28635f3bf 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -10,9 +10,9 @@ import ( mockrouting "github.com/ipfs/go-ipfs/routing/mock" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + testutil "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - testutil "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index bb30a243a..7503ad3d7 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" - testutil "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + testutil "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" From 8e81dc64a51e41f1925e612bf13982cff0f0f289 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 5 Oct 2017 17:10:16 +0300 Subject: [PATCH 1992/3817] update go-testutil to 1.1.12 License: MIT Signed-off-by: vyzo This commit was moved from ipfs/go-mfs@65b0e8640cdff39997bbbbdeae0edc65b2eee1b6 --- mfs/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 1c21bd793..2dd199e8f 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - ci "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil/ci" + ci "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil/ci" "context" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" From 6121ae2f5025100bc7c2713b0196240491d1305b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 10 Oct 2017 19:21:17 -0700 Subject: [PATCH 1993/3817] parallelize batch flushing 1. Modern storage devices (i.e., SSDs) tend to be highly parallel. 2. Allows us to read and write at the same time (avoids pausing while flushing). fixes https://github.com/ipfs/go-ipfs/issues/898#issuecomment-331849064 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@888d58cdc3f7ec09c9cdc0e654ae676e1f374f14 --- ipld/merkledag/batch.go | 98 +++++++++++++++++++++++++++++++++++++ ipld/merkledag/merkledag.go | 31 ++---------- 2 files changed, 101 insertions(+), 28 deletions(-) create mode 100644 ipld/merkledag/batch.go diff --git a/ipld/merkledag/batch.go b/ipld/merkledag/batch.go new file mode 100644 index 000000000..5879ad99b --- /dev/null +++ b/ipld/merkledag/batch.go @@ -0,0 +1,98 @@ +package merkledag + +import ( + "runtime" + + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" +) + +// ParallelBatchCommits is the number of batch commits that can be in-flight before blocking. +// TODO: Experiment with multiple datastores, storage devices, and CPUs to find +// the right value/formula. +var ParallelBatchCommits = runtime.NumCPU() * 2 + +// Batch is a buffer for batching adds to a dag. +type Batch struct { + ds *dagService + + activeCommits int + commitError error + commitResults chan error + + blocks []blocks.Block + size int + + MaxSize int + MaxBlocks int +} + +func (t *Batch) processResults() { + for t.activeCommits > 0 && t.commitError == nil { + select { + case err := <-t.commitResults: + t.activeCommits-- + if err != nil { + t.commitError = err + } + default: + return + } + } +} + +func (t *Batch) asyncCommit() { + if len(t.blocks) == 0 || t.commitError != nil { + return + } + if t.activeCommits >= ParallelBatchCommits { + err := <-t.commitResults + t.activeCommits-- + + if err != nil { + t.commitError = err + return + } + } + go func(b []blocks.Block) { + _, err := t.ds.Blocks.AddBlocks(b) + t.commitResults <- err + }(t.blocks) + + t.activeCommits++ + t.blocks = nil + t.size = 0 + + return +} + +// Add adds a node to the batch and commits the batch if necessary. +func (t *Batch) Add(nd node.Node) (*cid.Cid, error) { + // Not strictly necessary but allows us to catch errors early. + t.processResults() + if t.commitError != nil { + return nil, t.commitError + } + + t.blocks = append(t.blocks, nd) + t.size += len(nd.RawData()) + if t.size > t.MaxSize || len(t.blocks) > t.MaxBlocks { + t.asyncCommit() + } + return nd.Cid(), t.commitError +} + +// Commit commits batched nodes. +func (t *Batch) Commit() error { + t.asyncCommit() + for t.activeCommits > 0 && t.commitError == nil { + err := <-t.commitResults + t.activeCommits-- + if err != nil { + t.commitError = err + } + } + + return t.commitError +} diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 214fbbd84..92cb5fa86 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -11,7 +11,6 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" - blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" ipldcbor "gx/ipfs/QmWCs8kMecJwCPK8JThue8TjgM2ieJ2HjTLDu7Cv2NEmZi/go-ipld-cbor" ) @@ -75,8 +74,9 @@ func (n *dagService) Add(nd node.Node) (*cid.Cid, error) { func (n *dagService) Batch() *Batch { return &Batch{ - ds: n, - MaxSize: 8 << 20, + ds: n, + commitResults: make(chan error, ParallelBatchCommits), + MaxSize: 8 << 20, // By default, only batch up to 128 nodes at a time. // The current implementation of flatfs opens this many file @@ -389,31 +389,6 @@ func (np *nodePromise) Get(ctx context.Context) (node.Node, error) { } } -type Batch struct { - ds *dagService - - blocks []blocks.Block - size int - MaxSize int - MaxBlocks int -} - -func (t *Batch) Add(nd node.Node) (*cid.Cid, error) { - t.blocks = append(t.blocks, nd) - t.size += len(nd.RawData()) - if t.size > t.MaxSize || len(t.blocks) > t.MaxBlocks { - return nd.Cid(), t.Commit() - } - return nd.Cid(), nil -} - -func (t *Batch) Commit() error { - _, err := t.ds.Blocks.AddBlocks(t.blocks) - t.blocks = nil - t.size = 0 - return err -} - type GetLinks func(context.Context, *cid.Cid) ([]*node.Link, error) // EnumerateChildren will walk the dag below the given root node and add all From 42d1fbe1b6a15c5809d40e72a2ee344bf1ea1d05 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 11 Oct 2017 11:40:54 -0700 Subject: [PATCH 1994/3817] create an issue for tuning the parallelism constant License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@88e6deb4d7859cacdee537640106188edcd0effb --- ipld/merkledag/batch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/batch.go b/ipld/merkledag/batch.go index 5879ad99b..bf3e74048 100644 --- a/ipld/merkledag/batch.go +++ b/ipld/merkledag/batch.go @@ -9,7 +9,7 @@ import ( ) // ParallelBatchCommits is the number of batch commits that can be in-flight before blocking. -// TODO: Experiment with multiple datastores, storage devices, and CPUs to find +// TODO(#4299): Experiment with multiple datastores, storage devices, and CPUs to find // the right value/formula. var ParallelBatchCommits = runtime.NumCPU() * 2 From c3e4069bc6cc8254a84b73d7025ef1700b1567fc Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 11 Oct 2017 11:43:30 -0700 Subject: [PATCH 1995/3817] preallocate Batch's blocks buffer on commit. It's probably safe to assume that this buffer will be about the same time each flush. This could cause 1 extra allocation (if this is the last commit) but that's unlikely to be an issue. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@0a5f16ec54dcad7cf4d51c7807928794cade0499 --- ipld/merkledag/batch.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/batch.go b/ipld/merkledag/batch.go index bf3e74048..b6dd286b9 100644 --- a/ipld/merkledag/batch.go +++ b/ipld/merkledag/batch.go @@ -43,7 +43,8 @@ func (t *Batch) processResults() { } func (t *Batch) asyncCommit() { - if len(t.blocks) == 0 || t.commitError != nil { + numBlocks := len(t.blocks) + if numBlocks == 0 || t.commitError != nil { return } if t.activeCommits >= ParallelBatchCommits { @@ -61,7 +62,7 @@ func (t *Batch) asyncCommit() { }(t.blocks) t.activeCommits++ - t.blocks = nil + t.blocks = make([]blocks.Block, 0, numBlocks) t.size = 0 return From a6fa6e29395f52d5ae54e0d75667676fe8a3e070 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 12 Oct 2017 19:39:29 -0700 Subject: [PATCH 1996/3817] remove supernode routing It was never fully implemented and isn't used. fixes #3950 (not removing routing/mock because that *is* in use). License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@330c8e84f88434faa9c430de4b9ca77cab733ee7 --- routing/supernode/client.go | 164 ---------------------- routing/supernode/proxy/loopback.go | 59 -------- routing/supernode/proxy/standard.go | 174 ------------------------ routing/supernode/server.go | 202 ---------------------------- routing/supernode/server_test.go | 39 ------ 5 files changed, 638 deletions(-) delete mode 100644 routing/supernode/client.go delete mode 100644 routing/supernode/proxy/loopback.go delete mode 100644 routing/supernode/proxy/standard.go delete mode 100644 routing/supernode/server.go delete mode 100644 routing/supernode/server_test.go diff --git a/routing/supernode/client.go b/routing/supernode/client.go deleted file mode 100644 index 6764f784f..000000000 --- a/routing/supernode/client.go +++ /dev/null @@ -1,164 +0,0 @@ -package supernode - -import ( - "bytes" - "context" - "errors" - "time" - - proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" - pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" - dhtpb "gx/ipfs/QmT7PnPxYkeKPCG8pAnucfcjrXc15Q7FgvFv7YC24EPrw8/go-libp2p-kad-dht/pb" - peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - "gx/ipfs/QmaSxYRuMq4pkpBBG2CYaRrPx2z7NmMVEs34b9g61biQA6/go-libp2p-host" - pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" -) - -var log = logging.Logger("supernode") - -type Client struct { - peerhost host.Host - peerstore pstore.Peerstore - proxy proxy.Proxy - local peer.ID -} - -// TODO take in datastore/cache -func NewClient(px proxy.Proxy, h host.Host, ps pstore.Peerstore, local peer.ID) (*Client, error) { - return &Client{ - proxy: px, - local: local, - peerstore: ps, - peerhost: h, - }, nil -} - -func (c *Client) FindProvidersAsync(ctx context.Context, k *cid.Cid, max int) <-chan pstore.PeerInfo { - logging.ContextWithLoggable(ctx, loggables.Uuid("findProviders")) - defer log.EventBegin(ctx, "findProviders", k).Done() - ch := make(chan pstore.PeerInfo) - go func() { - defer close(ch) - request := dhtpb.NewMessage(dhtpb.Message_GET_PROVIDERS, k.KeyString(), 0) - response, err := c.proxy.SendRequest(ctx, request) - if err != nil { - log.Debug(err) - return - } - for _, p := range dhtpb.PBPeersToPeerInfos(response.GetProviderPeers()) { - select { - case <-ctx.Done(): - log.Debug(ctx.Err()) - return - case ch <- *p: - } - } - }() - return ch -} - -func (c *Client) PutValue(ctx context.Context, k string, v []byte) error { - defer log.EventBegin(ctx, "putValue").Done() - r, err := makeRecord(c.peerstore, c.local, k, v) - if err != nil { - return err - } - pmes := dhtpb.NewMessage(dhtpb.Message_PUT_VALUE, string(k), 0) - pmes.Record = r - return c.proxy.SendMessage(ctx, pmes) // wrap to hide the remote -} - -func (c *Client) GetValue(ctx context.Context, k string) ([]byte, error) { - defer log.EventBegin(ctx, "getValue").Done() - msg := dhtpb.NewMessage(dhtpb.Message_GET_VALUE, string(k), 0) - response, err := c.proxy.SendRequest(ctx, msg) // TODO wrap to hide the remote - if err != nil { - return nil, err - } - return response.Record.GetValue(), nil -} - -func (c *Client) GetValues(ctx context.Context, k string, _ int) ([]routing.RecvdVal, error) { - defer log.EventBegin(ctx, "getValue").Done() - msg := dhtpb.NewMessage(dhtpb.Message_GET_VALUE, string(k), 0) - response, err := c.proxy.SendRequest(ctx, msg) // TODO wrap to hide the remote - if err != nil { - return nil, err - } - - return []routing.RecvdVal{ - { - Val: response.Record.GetValue(), - From: c.local, - }, - }, nil -} - -// Provide adds the given key 'k' to the content routing system. If 'brd' is -// true, it announces that content to the network. For the supernode client, -// setting 'brd' to false makes this call a no-op -func (c *Client) Provide(ctx context.Context, k *cid.Cid, brd bool) error { - if !brd { - return nil - } - defer log.EventBegin(ctx, "provide", k).Done() - msg := dhtpb.NewMessage(dhtpb.Message_ADD_PROVIDER, k.KeyString(), 0) - // FIXME how is connectedness defined for the local node - pri := []dhtpb.PeerRoutingInfo{ - { - PeerInfo: pstore.PeerInfo{ - ID: c.local, - Addrs: c.peerhost.Addrs(), - }, - }, - } - msg.ProviderPeers = dhtpb.PeerRoutingInfosToPBPeers(pri) - return c.proxy.SendMessage(ctx, msg) // TODO wrap to hide remote -} - -func (c *Client) FindPeer(ctx context.Context, id peer.ID) (pstore.PeerInfo, error) { - defer log.EventBegin(ctx, "findPeer", id).Done() - request := dhtpb.NewMessage(dhtpb.Message_FIND_NODE, string(id), 0) - response, err := c.proxy.SendRequest(ctx, request) // hide remote - if err != nil { - return pstore.PeerInfo{}, err - } - for _, p := range dhtpb.PBPeersToPeerInfos(response.GetCloserPeers()) { - if p.ID == id { - return *p, nil - } - } - return pstore.PeerInfo{}, errors.New("could not find peer") -} - -// creates and signs a record for the given key/value pair -func makeRecord(ps pstore.Peerstore, p peer.ID, k string, v []byte) (*pb.Record, error) { - blob := bytes.Join([][]byte{[]byte(k), v, []byte(p)}, []byte{}) - sig, err := ps.PrivKey(p).Sign(blob) - if err != nil { - return nil, err - } - return &pb.Record{ - Key: proto.String(string(k)), - Value: v, - Author: proto.String(string(p)), - Signature: sig, - }, nil -} - -func (c *Client) Ping(ctx context.Context, id peer.ID) (time.Duration, error) { - defer log.EventBegin(ctx, "ping", id).Done() - return time.Nanosecond, errors.New("supernode routing does not support the ping method") -} - -func (c *Client) Bootstrap(ctx context.Context) error { - return c.proxy.Bootstrap(ctx) -} - -var _ routing.IpfsRouting = &Client{} diff --git a/routing/supernode/proxy/loopback.go b/routing/supernode/proxy/loopback.go deleted file mode 100644 index 16480e65c..000000000 --- a/routing/supernode/proxy/loopback.go +++ /dev/null @@ -1,59 +0,0 @@ -package proxy - -import ( - context "context" - inet "gx/ipfs/QmNa31VPzC561NWwRsJLE7nGYZYuuD2QfpK2b1q9BK54J1/go-libp2p-net" - dhtpb "gx/ipfs/QmT7PnPxYkeKPCG8pAnucfcjrXc15Q7FgvFv7YC24EPrw8/go-libp2p-kad-dht/pb" - peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" - ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" -) - -// RequestHandler handles routing requests locally -type RequestHandler interface { - HandleRequest(ctx context.Context, p peer.ID, m *dhtpb.Message) *dhtpb.Message -} - -// Loopback forwards requests to a local handler -type Loopback struct { - Handler RequestHandler - Local peer.ID -} - -func (_ *Loopback) Bootstrap(ctx context.Context) error { - return nil -} - -// SendMessage intercepts local requests, forwarding them to a local handler -func (lb *Loopback) SendMessage(ctx context.Context, m *dhtpb.Message) error { - response := lb.Handler.HandleRequest(ctx, lb.Local, m) - if response != nil { - log.Warning("loopback handler returned unexpected message") - } - return nil -} - -// SendRequest intercepts local requests, forwarding them to a local handler -func (lb *Loopback) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) { - return lb.Handler.HandleRequest(ctx, lb.Local, m), nil -} - -func (lb *Loopback) HandleStream(s inet.Stream) { - defer s.Close() - pbr := ggio.NewDelimitedReader(s, inet.MessageSizeMax) - var incoming dhtpb.Message - if err := pbr.ReadMsg(&incoming); err != nil { - s.Reset() - log.Debug(err) - return - } - ctx := context.TODO() - outgoing := lb.Handler.HandleRequest(ctx, s.Conn().RemotePeer(), &incoming) - - pbw := ggio.NewDelimitedWriter(s) - - if err := pbw.WriteMsg(outgoing); err != nil { - s.Reset() - log.Debug(err) - return - } -} diff --git a/routing/supernode/proxy/standard.go b/routing/supernode/proxy/standard.go deleted file mode 100644 index 09c78d69a..000000000 --- a/routing/supernode/proxy/standard.go +++ /dev/null @@ -1,174 +0,0 @@ -package proxy - -import ( - "context" - "errors" - - inet "gx/ipfs/QmNa31VPzC561NWwRsJLE7nGYZYuuD2QfpK2b1q9BK54J1/go-libp2p-net" - pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - kbucket "gx/ipfs/QmSAFA8v42u4gpJNy1tb7vW3JiiXiaYDC2b845c2RnNSJL/go-libp2p-kbucket" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables" - dhtpb "gx/ipfs/QmT7PnPxYkeKPCG8pAnucfcjrXc15Q7FgvFv7YC24EPrw8/go-libp2p-kad-dht/pb" - peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" - ggio "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/io" - host "gx/ipfs/QmaSxYRuMq4pkpBBG2CYaRrPx2z7NmMVEs34b9g61biQA6/go-libp2p-host" -) - -const ProtocolSNR = "/ipfs/supernoderouting" - -var log = logging.Logger("supernode/proxy") - -type Proxy interface { - Bootstrap(context.Context) error - HandleStream(inet.Stream) - SendMessage(ctx context.Context, m *dhtpb.Message) error - SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) -} - -type standard struct { - Host host.Host - - remoteInfos []pstore.PeerInfo // addr required for bootstrapping - remoteIDs []peer.ID // []ID is required for each req. here, cached for performance. -} - -func Standard(h host.Host, remotes []pstore.PeerInfo) Proxy { - var ids []peer.ID - for _, remote := range remotes { - ids = append(ids, remote.ID) - } - return &standard{h, remotes, ids} -} - -func (px *standard) Bootstrap(ctx context.Context) error { - var cxns []pstore.PeerInfo - for _, info := range px.remoteInfos { - if err := px.Host.Connect(ctx, info); err != nil { - continue - } - cxns = append(cxns, info) - } - if len(cxns) == 0 { - log.Error("unable to bootstrap to any supernode routers") - } else { - log.Infof("bootstrapped to %d supernode routers: %s", len(cxns), cxns) - } - return nil -} - -func (p *standard) HandleStream(s inet.Stream) { - // TODO(brian): Should clients be able to satisfy requests? - log.Error("supernode client received (dropped) a routing message from", s.Conn().RemotePeer()) - s.Reset() -} - -const replicationFactor = 2 - -// SendMessage sends message to each remote sequentially (randomized order), -// stopping after the first successful response. If all fail, returns the last -// error. -func (px *standard) SendMessage(ctx context.Context, m *dhtpb.Message) error { - var err error - var numSuccesses int - for _, remote := range sortedByKey(px.remoteIDs, m.GetKey()) { - if err = px.sendMessage(ctx, m, remote); err != nil { // careful don't re-declare err! - continue - } - numSuccesses++ - switch m.GetType() { - case dhtpb.Message_ADD_PROVIDER, dhtpb.Message_PUT_VALUE: - if numSuccesses < replicationFactor { - continue - } - } - return nil // success - } - return err // NB: returns the last error -} - -func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote peer.ID) (err error) { - e := log.EventBegin(ctx, "sendRoutingMessage", px.Host.ID(), remote, m) - defer func() { - if err != nil { - e.SetError(err) - } - e.Done() - }() - if err = px.Host.Connect(ctx, pstore.PeerInfo{ID: remote}); err != nil { - return err - } - s, err := px.Host.NewStream(ctx, remote, ProtocolSNR) - if err != nil { - return err - } - pbw := ggio.NewDelimitedWriter(s) - - err = pbw.WriteMsg(m) - if err == nil { - s.Close() - } else { - s.Reset() - } - return err -} - -// SendRequest sends the request to each remote sequentially (randomized order), -// stopping after the first successful response. If all fail, returns the last -// error. -func (px *standard) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) { - var err error - for _, remote := range sortedByKey(px.remoteIDs, m.GetKey()) { - var reply *dhtpb.Message - reply, err = px.sendRequest(ctx, m, remote) // careful don't redeclare err! - if err != nil { - continue - } - return reply, nil // success - } - return nil, err // NB: returns the last error -} - -func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote peer.ID) (*dhtpb.Message, error) { - e := log.EventBegin(ctx, "sendRoutingRequest", px.Host.ID(), remote, logging.Pair("request", m)) - defer e.Done() - if err := px.Host.Connect(ctx, pstore.PeerInfo{ID: remote}); err != nil { - e.SetError(err) - return nil, err - } - s, err := px.Host.NewStream(ctx, remote, ProtocolSNR) - if err != nil { - e.SetError(err) - return nil, err - } - defer s.Close() - r := ggio.NewDelimitedReader(s, inet.MessageSizeMax) - w := ggio.NewDelimitedWriter(s) - if err = w.WriteMsg(m); err != nil { - s.Reset() - e.SetError(err) - return nil, err - } - - response := &dhtpb.Message{} - if err = r.ReadMsg(response); err != nil { - s.Reset() - e.SetError(err) - return nil, err - } - // need ctx expiration? - if response == nil { - s.Reset() - err := errors.New("no response to request") - e.SetError(err) - return nil, err - } - e.Append(logging.Pair("response", response)) - e.Append(logging.Pair("uuid", loggables.Uuid("foo"))) - return response, nil -} - -func sortedByKey(peers []peer.ID, skey string) []peer.ID { - target := kbucket.ConvertKey(skey) - return kbucket.SortClosestPeers(peers, target) -} diff --git a/routing/supernode/server.go b/routing/supernode/server.go deleted file mode 100644 index 592cd0732..000000000 --- a/routing/supernode/server.go +++ /dev/null @@ -1,202 +0,0 @@ -package supernode - -import ( - "context" - "errors" - "fmt" - - proxy "github.com/ipfs/go-ipfs/routing/supernode/proxy" - dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - - pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - dhtpb "gx/ipfs/QmT7PnPxYkeKPCG8pAnucfcjrXc15Q7FgvFv7YC24EPrw8/go-libp2p-kad-dht/pb" - datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" -) - -// Server handles routing queries using a database backend -type Server struct { - local peer.ID - routingBackend datastore.Datastore - peerstore pstore.Peerstore - *proxy.Loopback // so server can be injected into client -} - -// NewServer creates a new Supernode routing Server -func NewServer(ds datastore.Datastore, ps pstore.Peerstore, local peer.ID) (*Server, error) { - s := &Server{local, ds, ps, nil} - s.Loopback = &proxy.Loopback{ - Handler: s, - Local: local, - } - return s, nil -} - -func (_ *Server) Bootstrap(ctx context.Context) error { - return nil -} - -// HandleLocalRequest implements the proxy.RequestHandler interface. This is -// where requests are received from the outside world. -func (s *Server) HandleRequest(ctx context.Context, p peer.ID, req *dhtpb.Message) *dhtpb.Message { - _, response := s.handleMessage(ctx, p, req) // ignore response peer. it's local. - return response -} - -func (s *Server) handleMessage( - ctx context.Context, p peer.ID, req *dhtpb.Message) (peer.ID, *dhtpb.Message) { - - defer log.EventBegin(ctx, "routingMessageReceived", req, p).Done() - - var response = dhtpb.NewMessage(req.GetType(), req.GetKey(), req.GetClusterLevel()) - switch req.GetType() { - - case dhtpb.Message_GET_VALUE: - rawRecord, err := getRoutingRecord(s.routingBackend, req.GetKey()) - if err != nil { - return "", nil - } - response.Record = rawRecord - return p, response - - case dhtpb.Message_PUT_VALUE: - // FIXME: verify complains that the peer's ID is not present in the - // peerstore. Mocknet problem? - // if err := verify(s.peerstore, req.GetRecord()); err != nil { - // log.Event(ctx, "validationFailed", req, p) - // return "", nil - // } - putRoutingRecord(s.routingBackend, req.GetKey(), req.GetRecord()) - return p, req - - case dhtpb.Message_FIND_NODE: - p := s.peerstore.PeerInfo(peer.ID(req.GetKey())) - pri := []dhtpb.PeerRoutingInfo{ - { - PeerInfo: p, - // Connectedness: TODO - }, - } - response.CloserPeers = dhtpb.PeerRoutingInfosToPBPeers(pri) - return p.ID, response - - case dhtpb.Message_ADD_PROVIDER: - for _, provider := range req.GetProviderPeers() { - providerID := peer.ID(provider.GetId()) - if providerID == p { - store := []*dhtpb.Message_Peer{provider} - storeProvidersToPeerstore(s.peerstore, p, store) - if err := putRoutingProviders(s.routingBackend, req.GetKey(), store); err != nil { - return "", nil - } - } else { - log.Event(ctx, "addProviderBadRequest", p, req) - } - } - return "", nil - - case dhtpb.Message_GET_PROVIDERS: - providers, err := getRoutingProviders(s.routingBackend, req.GetKey()) - if err != nil { - return "", nil - } - response.ProviderPeers = providers - return p, response - - case dhtpb.Message_PING: - return p, req - default: - } - return "", nil -} - -var _ proxy.RequestHandler = &Server{} -var _ proxy.Proxy = &Server{} - -func getRoutingRecord(ds datastore.Datastore, k string) (*pb.Record, error) { - dskey := dshelp.NewKeyFromBinary([]byte(k)) - val, err := ds.Get(dskey) - if err != nil { - return nil, err - } - recordBytes, ok := val.([]byte) - if !ok { - return nil, fmt.Errorf("datastore had non byte-slice value for %v", dskey) - } - var record pb.Record - if err := proto.Unmarshal(recordBytes, &record); err != nil { - return nil, errors.New("failed to unmarshal dht record from datastore") - } - return &record, nil -} - -func putRoutingRecord(ds datastore.Datastore, k string, value *pb.Record) error { - data, err := proto.Marshal(value) - if err != nil { - return err - } - dskey := dshelp.NewKeyFromBinary([]byte(k)) - // TODO namespace - return ds.Put(dskey, data) -} - -func putRoutingProviders(ds datastore.Datastore, k string, newRecords []*dhtpb.Message_Peer) error { - log.Event(context.Background(), "putRoutingProviders") - oldRecords, err := getRoutingProviders(ds, k) - if err != nil { - return err - } - mergedRecords := make(map[string]*dhtpb.Message_Peer) - for _, provider := range oldRecords { - mergedRecords[provider.GetId()] = provider // add original records - } - for _, provider := range newRecords { - mergedRecords[provider.GetId()] = provider // overwrite old record if new exists - } - var protomsg dhtpb.Message - protomsg.ProviderPeers = make([]*dhtpb.Message_Peer, 0, len(mergedRecords)) - for _, provider := range mergedRecords { - protomsg.ProviderPeers = append(protomsg.ProviderPeers, provider) - } - data, err := proto.Marshal(&protomsg) - if err != nil { - return err - } - return ds.Put(providerKey(k), data) -} - -func storeProvidersToPeerstore(ps pstore.Peerstore, p peer.ID, providers []*dhtpb.Message_Peer) { - for _, provider := range providers { - providerID := peer.ID(provider.GetId()) - if providerID != p { - log.Errorf("provider message came from third-party %s", p) - continue - } - for _, maddr := range provider.Addresses() { - // as a router, we want to store addresses for peers who have provided - ps.AddAddr(p, maddr, pstore.AddressTTL) - } - } -} - -func getRoutingProviders(ds datastore.Datastore, k string) ([]*dhtpb.Message_Peer, error) { - e := log.EventBegin(context.Background(), "getProviders") - defer e.Done() - var providers []*dhtpb.Message_Peer - if v, err := ds.Get(providerKey(k)); err == nil { - if data, ok := v.([]byte); ok { - var msg dhtpb.Message - if err := proto.Unmarshal(data, &msg); err != nil { - return nil, err - } - providers = append(providers, msg.GetProviderPeers()...) - } - } - return providers, nil -} - -func providerKey(k string) datastore.Key { - return datastore.KeyWithNamespaces([]string{"routing", "providers", k}) -} diff --git a/routing/supernode/server_test.go b/routing/supernode/server_test.go deleted file mode 100644 index 348fbe392..000000000 --- a/routing/supernode/server_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package supernode - -import ( - "testing" - - dhtpb "gx/ipfs/QmT7PnPxYkeKPCG8pAnucfcjrXc15Q7FgvFv7YC24EPrw8/go-libp2p-kad-dht/pb" - datastore "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" -) - -func TestPutProviderDoesntResultInDuplicates(t *testing.T) { - routingBackend := datastore.NewMapDatastore() - k := "foo" - put := []*dhtpb.Message_Peer{ - convPeer("bob", "127.0.0.1/tcp/4001"), - convPeer("alice", "10.0.0.10/tcp/4001"), - } - if err := putRoutingProviders(routingBackend, k, put); err != nil { - t.Fatal(err) - } - if err := putRoutingProviders(routingBackend, k, put); err != nil { - t.Fatal(err) - } - - got, err := getRoutingProviders(routingBackend, k) - if err != nil { - t.Fatal(err) - } - if len(got) != 2 { - t.Fatal("should be 2 values, but there are", len(got)) - } -} - -func convPeer(name string, addrs ...string) *dhtpb.Message_Peer { - var rawAddrs [][]byte - for _, addr := range addrs { - rawAddrs = append(rawAddrs, []byte(addr)) - } - return &dhtpb.Message_Peer{Id: &name, Addrs: rawAddrs} -} From 1522cf05171e46ec19da15305426692e3a57e6ca Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 6 Oct 2017 08:42:59 -0700 Subject: [PATCH 1997/3817] update deps for new connmgr code License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@de5141478619b23ddb5e584980879bbc7526eb78 --- namesys/publisher_test.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 28635f3bf..3149b3e48 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -10,9 +10,9 @@ import ( mockrouting "github.com/ipfs/go-ipfs/routing/mock" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - testutil "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + testutil "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 2a7187537..5078c704a 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - mocknet "gx/ipfs/QmRQ76P5dgvxTujhfPsCRAG83rC15jgb1G9bKLuomuC6dQ/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/Qmbgce14YTWE2qhE49JVvTBPaHTyz3FaFmqQPyuZAz6C28/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 7503ad3d7..bb30a243a 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" - testutil "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" + testutil "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" From 3cbca5580b4e01b5fc4ec4525990dee328633953 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 6 Oct 2017 08:42:59 -0700 Subject: [PATCH 1998/3817] update deps for new connmgr code License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@c649b07ceeed976ef09646df1bc2195c9f456ba3 --- mfs/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 2dd199e8f..1c21bd793 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - ci "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil/ci" + ci "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil/ci" "context" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" From 4b81492c629965d8a4920ad3f63de29e5db793d7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 6 Oct 2017 08:42:59 -0700 Subject: [PATCH 1999/3817] update deps for new connmgr code License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-routing@dc82c3c14ad3dfd16f9aebd8834c087581890f55 --- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/interface.go | 2 +- routing/none/none_client.go | 2 +- routing/offline/offline_test.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 407629299..0614d2d77 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -6,7 +6,7 @@ import ( "time" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" + "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 02929a4cf..c5f90d1d8 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -6,7 +6,7 @@ import ( "sync" "time" - "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" + "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 9ecba1181..84f9fcbcf 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -6,7 +6,7 @@ import ( "time" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" + "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 5dcbbd21d..3522c6f38 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -8,7 +8,7 @@ import ( "context" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" + "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" diff --git a/routing/none/none_client.go b/routing/none/none_client.go index ae9c6c352..d0a00a0ee 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -10,7 +10,7 @@ import ( routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" - p2phost "gx/ipfs/QmaSxYRuMq4pkpBBG2CYaRrPx2z7NmMVEs34b9g61biQA6/go-libp2p-host" + p2phost "gx/ipfs/Qmc1XhrFEiSeBNn3mpfg6gEuYCt5im2gYmNVmncsvmpeAk/go-libp2p-host" ) type nilclient struct { diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index f922f399c..9f5b3f0b2 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -3,8 +3,8 @@ package offline import ( "bytes" "context" - "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" "testing" ) From 7f16b33e24bd390da0dd32f53a93f4c51a51896d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 18 Oct 2017 20:10:22 +0200 Subject: [PATCH 2000/3817] gateway: fix seeker can't seek on specific files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-unixfs@6d459435f3c99ce2048baa775a6140fd559d69ce --- unixfs/io/pbdagreader.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 0b75fd916..7ba63649f 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -243,7 +243,16 @@ func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) { return dr.Seek(noffset, io.SeekStart) case io.SeekEnd: noffset := int64(dr.pbdata.GetFilesize()) - offset - return dr.Seek(noffset, io.SeekStart) + n, err := dr.Seek(noffset, io.SeekStart) + + // Return negative number if we can't figure out the file size. Using io.EOF + // for this seems to be good(-enough) solution as it's only returned by + // precalcNextBuf when we step out of file range. + // This is needed for gateway to function properly + if err == io.EOF && *dr.pbdata.Type == ftpb.Data_File { + return -1, nil + } + return n, err default: return 0, errors.New("invalid whence") } From c6d1565a62ca06753ca5c5fa34b13a850fe0183f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 18 Oct 2017 22:25:36 +0200 Subject: [PATCH 2001/3817] gateway: custom seeker for files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-unixfs@f98d19ae1bb1e1b973787b9504b569c0e961ff2e --- unixfs/io/pbdagreader.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 7ba63649f..dcd383460 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -188,6 +188,9 @@ func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) { if offset < 0 { return -1, errors.New("Invalid offset") } + if offset == dr.offset { + return offset, nil + } // Grab cached protobuf object (solely to make code look cleaner) pb := dr.pbdata @@ -239,6 +242,10 @@ func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) { return offset, nil case io.SeekCurrent: // TODO: be smarter here + if offset == 0 { + return dr.offset, nil + } + noffset := dr.offset + offset return dr.Seek(noffset, io.SeekStart) case io.SeekEnd: From b1ba8f4df0417efeae025976462920459e439714 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 19 Oct 2017 07:51:55 -0700 Subject: [PATCH 2002/3817] gx update go-peerstream, go-libp2p-floodsub License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@fb512415880c09f5236b83441a6a403ac43f1593 --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 5078c704a..be438b838 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - mocknet "gx/ipfs/Qmbgce14YTWE2qhE49JVvTBPaHTyz3FaFmqQPyuZAz6C28/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmefgzMbKZYsmHFkLqxgaTBG9ypeEjrdWRD5WXH4j1cWDL/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { From 5d3cc5cb43f19bfe3d9ed0e9f5a01d88f4c59f8f Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sat, 5 Aug 2017 17:22:34 -0400 Subject: [PATCH 2003/3817] Test raw leaves in trickle dag tests. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-unixfs@5a0548a024b75710449639fba990f90d1a7cf42f --- unixfs/mod/dagmodifier_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 314178dd5..a79436b8d 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -41,7 +41,11 @@ func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) t.Fatal(err) } - err = trickle.VerifyTrickleDagStructure(nd, dm.dagserv, h.DefaultLinksPerBlock, 4) + err = trickle.VerifyTrickleDagStructure(nd, trickle.VerifyParams{ + Getter: dm.dagserv, + Direct: h.DefaultLinksPerBlock, + LayerRepeat: 4, + }) if err != nil { t.Fatal(err) } From ed43d10ebb054949effab3483c13f11823cabe90 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 8 Aug 2017 01:56:49 -0400 Subject: [PATCH 2004/3817] Provide support for raw leaves in DAG modifier. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-unixfs@e3ad5de807f518499064e58816000dfc5f8d784f --- unixfs/io/dagreader_test.go | 12 +- unixfs/mod/dagmodifier.go | 222 ++++++++++++++++++--------------- unixfs/mod/dagmodifier_test.go | 112 +++++++++++++---- unixfs/test/utils.go | 29 +++-- 4 files changed, 235 insertions(+), 140 deletions(-) diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index 3ac82fc5f..85c805e9c 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -17,7 +17,7 @@ import ( func TestBasicRead(t *testing.T) { dserv := testu.GetDAGServ() - inbuf, node := testu.GetRandomNode(t, dserv, 1024) + inbuf, node := testu.GetRandomNode(t, dserv, 1024, testu.ProtoBufLeaves) ctx, closer := context.WithCancel(context.Background()) defer closer() @@ -44,7 +44,7 @@ func TestSeekAndRead(t *testing.T) { inbuf[i] = byte(i) } - node := testu.GetNode(t, dserv, inbuf) + node := testu.GetNode(t, dserv, inbuf, testu.ProtoBufLeaves) ctx, closer := context.WithCancel(context.Background()) defer closer() @@ -84,7 +84,7 @@ func TestRelativeSeek(t *testing.T) { } inbuf[1023] = 1 // force the reader to be 1024 bytes - node := testu.GetNode(t, dserv, inbuf) + node := testu.GetNode(t, dserv, inbuf, testu.ProtoBufLeaves) reader, err := NewDagReader(ctx, node, dserv) if err != nil { @@ -160,7 +160,7 @@ func TestBadPBData(t *testing.T) { func TestMetadataNode(t *testing.T) { dserv := testu.GetDAGServ() - rdata, rnode := testu.GetRandomNode(t, dserv, 512) + rdata, rnode := testu.GetRandomNode(t, dserv, 512, testu.ProtoBufLeaves) _, err := dserv.Add(rnode) if err != nil { t.Fatal(err) @@ -203,7 +203,7 @@ func TestMetadataNode(t *testing.T) { func TestWriteTo(t *testing.T) { dserv := testu.GetDAGServ() - inbuf, node := testu.GetRandomNode(t, dserv, 1024) + inbuf, node := testu.GetRandomNode(t, dserv, 1024, testu.ProtoBufLeaves) ctx, closer := context.WithCancel(context.Background()) defer closer() @@ -225,7 +225,7 @@ func TestWriteTo(t *testing.T) { func TestReaderSzie(t *testing.T) { dserv := testu.GetDAGServ() size := int64(1024) - _, node := testu.GetRandomNode(t, dserv, size) + _, node := testu.GetRandomNode(t, dserv, size, testu.ProtoBufLeaves) ctx, closer := context.WithCancel(context.Background()) defer closer() diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index e3955e20c..5eaad4779 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -40,6 +40,8 @@ type DagModifier struct { curWrOff uint64 wrBuf *bytes.Buffer + RawLeaves bool + read uio.DagReader } @@ -113,17 +115,7 @@ func (dm *DagModifier) expandSparse(size int64) error { return err } _, err = dm.dagserv.Add(nnode) - if err != nil { - return err - } - - pbnnode, ok := nnode.(*mdag.ProtoNode) - if !ok { - return mdag.ErrNotProtobuf - } - - dm.curNode = pbnnode - return nil + return err } // Write continues writing to the dag at the current offset @@ -149,26 +141,28 @@ func (dm *DagModifier) Write(b []byte) (int, error) { return n, nil } -var ErrNoRawYet = fmt.Errorf("currently only fully support protonodes in the dagmodifier") - // Size returns the Filesize of the node func (dm *DagModifier) Size() (int64, error) { - switch nd := dm.curNode.(type) { + fileSize, err := fileSize(dm.curNode) + if err != nil { + return 0, err + } + if dm.wrBuf != nil && int64(dm.wrBuf.Len())+int64(dm.writeStart) > int64(fileSize) { + return int64(dm.wrBuf.Len()) + int64(dm.writeStart), nil + } + return int64(fileSize), nil +} + +func fileSize(n node.Node) (uint64, error) { + switch nd := n.(type) { case *mdag.ProtoNode: - pbn, err := ft.FromBytes(nd.Data()) + f, err := ft.FromBytes(nd.Data()) if err != nil { return 0, err } - if dm.wrBuf != nil && uint64(dm.wrBuf.Len())+dm.writeStart > pbn.GetFilesize() { - return int64(dm.wrBuf.Len()) + int64(dm.writeStart), nil - } - return int64(pbn.GetFilesize()), nil + return f.GetFilesize(), nil case *mdag.RawNode: - if dm.wrBuf != nil { - return 0, ErrNoRawYet - } - sz, err := nd.Size() - return int64(sz), err + return uint64(len(nd.RawData())), nil default: return 0, ErrNotUnixfs } @@ -196,36 +190,22 @@ func (dm *DagModifier) Sync() error { return err } - nd, err := dm.dagserv.Get(dm.ctx, thisc) + dm.curNode, err = dm.dagserv.Get(dm.ctx, thisc) if err != nil { return err } - pbnd, ok := nd.(*mdag.ProtoNode) - if !ok { - return mdag.ErrNotProtobuf - } - - dm.curNode = pbnd - // need to write past end of current dag if !done { - nd, err := dm.appendData(dm.curNode, dm.splitter(dm.wrBuf)) + dm.curNode, err = dm.appendData(dm.curNode, dm.splitter(dm.wrBuf)) if err != nil { return err } - _, err = dm.dagserv.Add(nd) + _, err = dm.dagserv.Add(dm.curNode) if err != nil { return err } - - pbnode, ok := nd.(*mdag.ProtoNode) - if !ok { - return mdag.ErrNotProtobuf - } - - dm.curNode = pbnode } dm.writeStart += uint64(buflen) @@ -238,43 +218,86 @@ func (dm *DagModifier) Sync() error { // returns the new key of the passed in node and whether or not all the data in the reader // has been consumed. func (dm *DagModifier) modifyDag(n node.Node, offset uint64, data io.Reader) (*cid.Cid, bool, error) { - node, ok := n.(*mdag.ProtoNode) - if !ok { - return nil, false, ErrNoRawYet - } + // If we've reached a leaf node. + if len(n.Links()) == 0 { + switch nd0 := n.(type) { + case *mdag.ProtoNode: + f, err := ft.FromBytes(nd0.Data()) + if err != nil { + return nil, false, err + } - f, err := ft.FromBytes(node.Data()) - if err != nil { - return nil, false, err - } + n, err := data.Read(f.Data[offset:]) + if err != nil && err != io.EOF { + return nil, false, err + } - // If we've reached a leaf node. - if len(node.Links()) == 0 { - n, err := data.Read(f.Data[offset:]) - if err != nil && err != io.EOF { - return nil, false, err - } + // Update newly written node.. + b, err := proto.Marshal(f) + if err != nil { + return nil, false, err + } - // Update newly written node.. - b, err := proto.Marshal(f) - if err != nil { - return nil, false, err - } + nd := new(mdag.ProtoNode) + nd.SetData(b) + k, err := dm.dagserv.Add(nd) + if err != nil { + return nil, false, err + } - nd := new(mdag.ProtoNode) - nd.SetData(b) - k, err := dm.dagserv.Add(nd) - if err != nil { - return nil, false, err - } + // Hey look! we're done! + var done bool + if n < len(f.Data[offset:]) { + done = true + } + + return k, done, nil + case *mdag.RawNode: + origData := nd0.RawData() + bytes := make([]byte, len(origData)) - // Hey look! we're done! - var done bool - if n < len(f.Data[offset:]) { - done = true + // copy orig data up to offset + copy(bytes, origData[:offset]) + + // copy in new data + n, err := data.Read(bytes[offset:]) + if err != nil && err != io.EOF { + return nil, false, err + } + + // copy remaining data + offsetPlusN := int(offset) + n + if offsetPlusN < len(origData) { + copy(bytes[offsetPlusN:], origData[offsetPlusN:]) + } + + nd, err := mdag.NewRawNodeWPrefix(bytes, nd0.Cid().Prefix()) + if err != nil { + return nil, false, err + } + k, err := dm.dagserv.Add(nd) + if err != nil { + return nil, false, err + } + + // Hey look! we're done! + var done bool + if n < len(bytes[offset:]) { + done = true + } + + return k, done, nil } + } - return k, done, nil + node, ok := n.(*mdag.ProtoNode) + if !ok { + return nil, false, ErrNotUnixfs + } + + f, err := ft.FromBytes(node.Data()) + if err != nil { + return nil, false, err } var cur uint64 @@ -287,12 +310,7 @@ func (dm *DagModifier) modifyDag(n node.Node, offset uint64, data io.Reader) (*c return nil, false, err } - childpb, ok := child.(*mdag.ProtoNode) - if !ok { - return nil, false, mdag.ErrNotProtobuf - } - - k, sdone, err := dm.modifyDag(childpb, offset-cur, data) + k, sdone, err := dm.modifyDag(child, offset-cur, data) if err != nil { return nil, false, err } @@ -323,14 +341,13 @@ func (dm *DagModifier) modifyDag(n node.Node, offset uint64, data io.Reader) (*c // appendData appends the blocks from the given chan to the end of this dag func (dm *DagModifier) appendData(nd node.Node, spl chunk.Splitter) (node.Node, error) { switch nd := nd.(type) { - case *mdag.ProtoNode: + case *mdag.ProtoNode, *mdag.RawNode: dbp := &help.DagBuilderParams{ - Dagserv: dm.dagserv, - Maxlinks: help.DefaultLinksPerBlock, + Dagserv: dm.dagserv, + Maxlinks: help.DefaultLinksPerBlock, + RawLeaves: dm.RawLeaves, } return trickle.TrickleAppend(dm.ctx, nd, dbp.New(spl)) - case *mdag.RawNode: - return nil, fmt.Errorf("appending to raw node types not yet supported") default: return nil, ErrNotUnixfs } @@ -478,26 +495,30 @@ func (dm *DagModifier) Truncate(size int64) error { } // dagTruncate truncates the given node to 'size' and returns the modified Node -func dagTruncate(ctx context.Context, n node.Node, size uint64, ds mdag.DAGService) (*mdag.ProtoNode, error) { - nd, ok := n.(*mdag.ProtoNode) - if !ok { - return nil, ErrNoRawYet - } - - if len(nd.Links()) == 0 { - // TODO: this can likely be done without marshaling and remarshaling - pbn, err := ft.FromBytes(nd.Data()) - if err != nil { - return nil, err +func dagTruncate(ctx context.Context, n node.Node, size uint64, ds mdag.DAGService) (node.Node, error) { + if len(n.Links()) == 0 { + switch nd := n.(type) { + case *mdag.ProtoNode: + // TODO: this can likely be done without marshaling and remarshaling + pbn, err := ft.FromBytes(nd.Data()) + if err != nil { + return nil, err + } + nd.SetData(ft.WrapData(pbn.Data[:size])) + return nd, nil + case *mdag.RawNode: + return mdag.NewRawNodeWPrefix(nd.RawData()[:size], nd.Cid().Prefix()) } + } - nd.SetData(ft.WrapData(pbn.Data[:size])) - return nd, nil + nd, ok := n.(*mdag.ProtoNode) + if !ok { + return nil, ErrNotUnixfs } var cur uint64 end := 0 - var modified *mdag.ProtoNode + var modified node.Node ndata := new(ft.FSNode) for i, lnk := range nd.Links() { child, err := lnk.GetNode(ctx, ds) @@ -505,19 +526,14 @@ func dagTruncate(ctx context.Context, n node.Node, size uint64, ds mdag.DAGServi return nil, err } - childpb, ok := child.(*mdag.ProtoNode) - if !ok { - return nil, err - } - - childsize, err := ft.DataSize(childpb.Data()) + childsize, err := fileSize(child) if err != nil { return nil, err } // found the child we want to cut if size < cur+childsize { - nchild, err := dagTruncate(ctx, childpb, size-cur, ds) + nchild, err := dagTruncate(ctx, child, size-cur, ds) if err != nil { return nil, err } diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index a79436b8d..7b15b8532 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -9,15 +9,14 @@ import ( h "github.com/ipfs/go-ipfs/importer/helpers" trickle "github.com/ipfs/go-ipfs/importer/trickle" - mdag "github.com/ipfs/go-ipfs/merkledag" - ft "github.com/ipfs/go-ipfs/unixfs" + uio "github.com/ipfs/go-ipfs/unixfs/io" testu "github.com/ipfs/go-ipfs/unixfs/test" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" ) -func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) []byte { +func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier, rawLeaves testu.UseRawLeaves) []byte { newdata := make([]byte, size) r := u.NewTimeSeededRand() r.Read(newdata) @@ -45,9 +44,10 @@ func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) Getter: dm.dagserv, Direct: h.DefaultLinksPerBlock, LayerRepeat: 4, + RawLeaves: bool(rawLeaves), }) if err != nil { - t.Fatal(err) + t.Error(err) } rd, err := uio.NewDagReader(context.Background(), nd, dm.dagserv) @@ -67,9 +67,17 @@ func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier) return orig } +func runBothSubtests(t *testing.T, tfunc func(*testing.T, testu.UseRawLeaves)) { + t.Run("leaves=ProtoBuf", func(t *testing.T) { tfunc(t, testu.ProtoBufLeaves) }) + t.Run("leaves=Raw", func(t *testing.T) { tfunc(t, testu.RawLeaves) }) +} + func TestDagModifierBasic(t *testing.T) { + runBothSubtests(t, testDagModifierBasic) +} +func testDagModifierBasic(t *testing.T, rawLeaves testu.UseRawLeaves) { dserv := testu.GetDAGServ() - b, n := testu.GetRandomNode(t, dserv, 50000) + b, n := testu.GetRandomNode(t, dserv, 50000, rawLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -77,32 +85,33 @@ func TestDagModifierBasic(t *testing.T) { if err != nil { t.Fatal(err) } + dagmod.RawLeaves = bool(rawLeaves) // Within zero block beg := uint64(15) length := uint64(60) t.Log("Testing mod within zero block") - b = testModWrite(t, beg, length, b, dagmod) + b = testModWrite(t, beg, length, b, dagmod, rawLeaves) // Within bounds of existing file beg = 1000 length = 4000 t.Log("Testing mod within bounds of existing multiblock file.") - b = testModWrite(t, beg, length, b, dagmod) + b = testModWrite(t, beg, length, b, dagmod, rawLeaves) // Extend bounds beg = 49500 length = 4000 t.Log("Testing mod that extends file.") - b = testModWrite(t, beg, length, b, dagmod) + b = testModWrite(t, beg, length, b, dagmod, rawLeaves) // "Append" beg = uint64(len(b)) length = 3000 t.Log("Testing pure append") - _ = testModWrite(t, beg, length, b, dagmod) + _ = testModWrite(t, beg, length, b, dagmod, rawLeaves) // Verify reported length node, err := dagmod.GetNode() @@ -110,7 +119,7 @@ func TestDagModifierBasic(t *testing.T) { t.Fatal(err) } - size, err := ft.DataSize(node.(*mdag.ProtoNode).Data()) + size, err := fileSize(node) if err != nil { t.Fatal(err) } @@ -122,8 +131,11 @@ func TestDagModifierBasic(t *testing.T) { } func TestMultiWrite(t *testing.T) { + runBothSubtests(t, testMultiWrite) +} +func testMultiWrite(t *testing.T, rawLeaves testu.UseRawLeaves) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv) + n := testu.GetEmptyNode(t, dserv, rawLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -132,6 +144,7 @@ func TestMultiWrite(t *testing.T) { if err != nil { t.Fatal(err) } + dagmod.RawLeaves = bool(rawLeaves) data := make([]byte, 4000) u.NewTimeSeededRand().Read(data) @@ -175,8 +188,11 @@ func TestMultiWrite(t *testing.T) { } func TestMultiWriteAndFlush(t *testing.T) { + runBothSubtests(t, testMultiWriteAndFlush) +} +func testMultiWriteAndFlush(t *testing.T, rawLeaves testu.UseRawLeaves) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv) + n := testu.GetEmptyNode(t, dserv, rawLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -185,6 +201,7 @@ func TestMultiWriteAndFlush(t *testing.T) { if err != nil { t.Fatal(err) } + dagmod.RawLeaves = bool(rawLeaves) data := make([]byte, 20) u.NewTimeSeededRand().Read(data) @@ -223,8 +240,11 @@ func TestMultiWriteAndFlush(t *testing.T) { } func TestWriteNewFile(t *testing.T) { + runBothSubtests(t, testWriteNewFile) +} +func testWriteNewFile(t *testing.T, rawLeaves testu.UseRawLeaves) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv) + n := testu.GetEmptyNode(t, dserv, rawLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -233,6 +253,7 @@ func TestWriteNewFile(t *testing.T) { if err != nil { t.Fatal(err) } + dagmod.RawLeaves = bool(rawLeaves) towrite := make([]byte, 2000) u.NewTimeSeededRand().Read(towrite) @@ -266,8 +287,11 @@ func TestWriteNewFile(t *testing.T) { } func TestMultiWriteCoal(t *testing.T) { + runBothSubtests(t, testMultiWriteCoal) +} +func testMultiWriteCoal(t *testing.T, rawLeaves testu.UseRawLeaves) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv) + n := testu.GetEmptyNode(t, dserv, rawLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -276,6 +300,7 @@ func TestMultiWriteCoal(t *testing.T) { if err != nil { t.Fatal(err) } + dagmod.RawLeaves = bool(rawLeaves) data := make([]byte, 1000) u.NewTimeSeededRand().Read(data) @@ -300,6 +325,8 @@ func TestMultiWriteCoal(t *testing.T) { if err != nil { t.Fatal(err) } + dagmod.RawLeaves = bool(rawLeaves) + rbuf, err := ioutil.ReadAll(read) if err != nil { t.Fatal(err) @@ -312,8 +339,11 @@ func TestMultiWriteCoal(t *testing.T) { } func TestLargeWriteChunks(t *testing.T) { + runBothSubtests(t, testLargeWriteChunks) +} +func testLargeWriteChunks(t *testing.T, rawLeaves testu.UseRawLeaves) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv) + n := testu.GetEmptyNode(t, dserv, rawLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -322,6 +352,7 @@ func TestLargeWriteChunks(t *testing.T) { if err != nil { t.Fatal(err) } + dagmod.RawLeaves = bool(rawLeaves) wrsize := 1000 datasize := 10000000 @@ -351,8 +382,11 @@ func TestLargeWriteChunks(t *testing.T) { } func TestDagTruncate(t *testing.T) { + runBothSubtests(t, testDagTruncate) +} +func testDagTruncate(t *testing.T, rawLeaves testu.UseRawLeaves) { dserv := testu.GetDAGServ() - b, n := testu.GetRandomNode(t, dserv, 50000) + b, n := testu.GetRandomNode(t, dserv, 50000, rawLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -360,6 +394,7 @@ func TestDagTruncate(t *testing.T) { if err != nil { t.Fatal(err) } + dagmod.RawLeaves = bool(rawLeaves) err = dagmod.Truncate(12345) if err != nil { @@ -418,8 +453,11 @@ func TestDagTruncate(t *testing.T) { } func TestSparseWrite(t *testing.T) { + runBothSubtests(t, testSparseWrite) +} +func testSparseWrite(t *testing.T, rawLeaves testu.UseRawLeaves) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv) + n := testu.GetEmptyNode(t, dserv, rawLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -427,6 +465,7 @@ func TestSparseWrite(t *testing.T) { if err != nil { t.Fatal(err) } + dagmod.RawLeaves = bool(rawLeaves) buf := make([]byte, 5000) u.NewTimeSeededRand().Read(buf[2500:]) @@ -456,8 +495,11 @@ func TestSparseWrite(t *testing.T) { } func TestSeekPastEndWrite(t *testing.T) { + runBothSubtests(t, testSeekPastEndWrite) +} +func testSeekPastEndWrite(t *testing.T, rawLeaves testu.UseRawLeaves) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv) + n := testu.GetEmptyNode(t, dserv, rawLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -465,6 +507,7 @@ func TestSeekPastEndWrite(t *testing.T) { if err != nil { t.Fatal(err) } + dagmod.RawLeaves = bool(rawLeaves) buf := make([]byte, 5000) u.NewTimeSeededRand().Read(buf[2500:]) @@ -503,8 +546,11 @@ func TestSeekPastEndWrite(t *testing.T) { } func TestRelativeSeek(t *testing.T) { + runBothSubtests(t, testRelativeSeek) +} +func testRelativeSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv) + n := testu.GetEmptyNode(t, dserv, rawLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -512,6 +558,7 @@ func TestRelativeSeek(t *testing.T) { if err != nil { t.Fatal(err) } + dagmod.RawLeaves = bool(rawLeaves) for i := 0; i < 64; i++ { dagmod.Write([]byte{byte(i)}) @@ -533,8 +580,11 @@ func TestRelativeSeek(t *testing.T) { } func TestInvalidSeek(t *testing.T) { + runBothSubtests(t, testInvalidSeek) +} +func testInvalidSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv) + n := testu.GetEmptyNode(t, dserv, rawLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -542,6 +592,8 @@ func TestInvalidSeek(t *testing.T) { if err != nil { t.Fatal(err) } + dagmod.RawLeaves = bool(rawLeaves) + _, err = dagmod.Seek(10, -10) if err != ErrUnrecognizedWhence { @@ -550,9 +602,12 @@ func TestInvalidSeek(t *testing.T) { } func TestEndSeek(t *testing.T) { + runBothSubtests(t, testEndSeek) +} +func testEndSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv) + n := testu.GetEmptyNode(t, dserv, rawLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -560,6 +615,7 @@ func TestEndSeek(t *testing.T) { if err != nil { t.Fatal(err) } + dagmod.RawLeaves = bool(rawLeaves) _, err = dagmod.Write(make([]byte, 100)) if err != nil { @@ -592,9 +648,12 @@ func TestEndSeek(t *testing.T) { } func TestReadAndSeek(t *testing.T) { + runBothSubtests(t, testReadAndSeek) +} +func testReadAndSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv) + n := testu.GetEmptyNode(t, dserv, rawLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -602,6 +661,7 @@ func TestReadAndSeek(t *testing.T) { if err != nil { t.Fatal(err) } + dagmod.RawLeaves = bool(rawLeaves) writeBuf := []byte{0, 1, 2, 3, 4, 5, 6, 7} dagmod.Write(writeBuf) @@ -660,9 +720,12 @@ func TestReadAndSeek(t *testing.T) { } func TestCtxRead(t *testing.T) { + runBothSubtests(t, testCtxRead) +} +func testCtxRead(t *testing.T, rawLeaves testu.UseRawLeaves) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv) + n := testu.GetEmptyNode(t, dserv, rawLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -670,6 +733,7 @@ func TestCtxRead(t *testing.T) { if err != nil { t.Fatal(err) } + dagmod.RawLeaves = bool(rawLeaves) _, err = dagmod.Write([]byte{0, 1, 2, 3, 4, 5, 6, 7}) if err != nil { @@ -693,7 +757,7 @@ func TestCtxRead(t *testing.T) { func BenchmarkDagmodWrite(b *testing.B) { b.StopTimer() dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(b, dserv) + n := testu.GetEmptyNode(b, dserv, testu.ProtoBufLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index c0b8ae18d..933493f36 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -8,8 +8,9 @@ import ( "io/ioutil" "testing" - imp "github.com/ipfs/go-ipfs/importer" "github.com/ipfs/go-ipfs/importer/chunk" + h "github.com/ipfs/go-ipfs/importer/helpers" + trickle "github.com/ipfs/go-ipfs/importer/trickle" mdag "github.com/ipfs/go-ipfs/merkledag" mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" @@ -28,9 +29,23 @@ func GetDAGServ() mdag.DAGService { return mdagmock.Mock() } -func GetNode(t testing.TB, dserv mdag.DAGService, data []byte) node.Node { +type UseRawLeaves bool + +const ( + ProtoBufLeaves UseRawLeaves = false + RawLeaves UseRawLeaves = true +) + +func GetNode(t testing.TB, dserv mdag.DAGService, data []byte, rawLeaves UseRawLeaves) node.Node { in := bytes.NewReader(data) - node, err := imp.BuildTrickleDagFromReader(dserv, SizeSplitterGen(500)(in)) + + dbp := h.DagBuilderParams{ + Dagserv: dserv, + Maxlinks: h.DefaultLinksPerBlock, + RawLeaves: bool(rawLeaves), + } + + node, err := trickle.TrickleLayout(dbp.New(SizeSplitterGen(500)(in))) if err != nil { t.Fatal(err) } @@ -38,18 +53,18 @@ func GetNode(t testing.TB, dserv mdag.DAGService, data []byte) node.Node { return node } -func GetEmptyNode(t testing.TB, dserv mdag.DAGService) node.Node { - return GetNode(t, dserv, []byte{}) +func GetEmptyNode(t testing.TB, dserv mdag.DAGService, rawLeaves UseRawLeaves) node.Node { + return GetNode(t, dserv, []byte{}, rawLeaves) } -func GetRandomNode(t testing.TB, dserv mdag.DAGService, size int64) ([]byte, node.Node) { +func GetRandomNode(t testing.TB, dserv mdag.DAGService, size int64, rawLeaves UseRawLeaves) ([]byte, node.Node) { in := io.LimitReader(u.NewTimeSeededRand(), size) buf, err := ioutil.ReadAll(in) if err != nil { t.Fatal(err) } - node := GetNode(t, dserv, buf) + node := GetNode(t, dserv, buf, rawLeaves) return buf, node } From 90941cbccd392e1dd3aff47d07b583c5c2607129 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 8 Aug 2017 14:33:47 -0400 Subject: [PATCH 2005/3817] Add "--raw-leaves" option to "ipfs files" License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-mfs@2e660e8fa590886edcb10911d55c08ed5b55e18c --- mfs/file.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mfs/file.go b/mfs/file.go index 6e249e329..85c9e59bc 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -23,6 +23,8 @@ type File struct { dserv dag.DAGService node node.Node nodelk sync.Mutex + + RawLeaves bool } // NewFile returns a NewFile object with the given parameters @@ -79,6 +81,7 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { if err != nil { return nil, err } + dmod.RawLeaves = fi.RawLeaves return &fileDescriptor{ inode: fi, From 3a8feec6d443a70efa813982140d25927767fa7c Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 8 Aug 2017 18:19:46 -0400 Subject: [PATCH 2006/3817] Enable CidV1 (and other prefixes) in the Dag Modifier. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-unixfs@109d1983cab5fc6c0bff9518dff05eb781175551 --- unixfs/io/dagreader_test.go | 12 +- unixfs/mod/dagmodifier.go | 24 +++- unixfs/mod/dagmodifier_test.go | 247 ++++++++++++++------------------- unixfs/test/utils.go | 29 ++-- 4 files changed, 151 insertions(+), 161 deletions(-) diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index 85c805e9c..a5ed6dd39 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -17,7 +17,7 @@ import ( func TestBasicRead(t *testing.T) { dserv := testu.GetDAGServ() - inbuf, node := testu.GetRandomNode(t, dserv, 1024, testu.ProtoBufLeaves) + inbuf, node := testu.GetRandomNode(t, dserv, 1024, testu.UseProtoBufLeaves) ctx, closer := context.WithCancel(context.Background()) defer closer() @@ -44,7 +44,7 @@ func TestSeekAndRead(t *testing.T) { inbuf[i] = byte(i) } - node := testu.GetNode(t, dserv, inbuf, testu.ProtoBufLeaves) + node := testu.GetNode(t, dserv, inbuf, testu.UseProtoBufLeaves) ctx, closer := context.WithCancel(context.Background()) defer closer() @@ -84,7 +84,7 @@ func TestRelativeSeek(t *testing.T) { } inbuf[1023] = 1 // force the reader to be 1024 bytes - node := testu.GetNode(t, dserv, inbuf, testu.ProtoBufLeaves) + node := testu.GetNode(t, dserv, inbuf, testu.UseProtoBufLeaves) reader, err := NewDagReader(ctx, node, dserv) if err != nil { @@ -160,7 +160,7 @@ func TestBadPBData(t *testing.T) { func TestMetadataNode(t *testing.T) { dserv := testu.GetDAGServ() - rdata, rnode := testu.GetRandomNode(t, dserv, 512, testu.ProtoBufLeaves) + rdata, rnode := testu.GetRandomNode(t, dserv, 512, testu.UseProtoBufLeaves) _, err := dserv.Add(rnode) if err != nil { t.Fatal(err) @@ -203,7 +203,7 @@ func TestMetadataNode(t *testing.T) { func TestWriteTo(t *testing.T) { dserv := testu.GetDAGServ() - inbuf, node := testu.GetRandomNode(t, dserv, 1024, testu.ProtoBufLeaves) + inbuf, node := testu.GetRandomNode(t, dserv, 1024, testu.UseProtoBufLeaves) ctx, closer := context.WithCancel(context.Background()) defer closer() @@ -225,7 +225,7 @@ func TestWriteTo(t *testing.T) { func TestReaderSzie(t *testing.T) { dserv := testu.GetDAGServ() size := int64(1024) - _, node := testu.GetRandomNode(t, dserv, size, testu.ProtoBufLeaves) + _, node := testu.GetRandomNode(t, dserv, size, testu.UseProtoBufLeaves) ctx, closer := context.WithCancel(context.Background()) defer closer() diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 5eaad4779..23c1945a5 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -40,6 +40,7 @@ type DagModifier struct { curWrOff uint64 wrBuf *bytes.Buffer + Prefix cid.Prefix RawLeaves bool read uio.DagReader @@ -47,6 +48,10 @@ type DagModifier struct { var ErrNotUnixfs = fmt.Errorf("dagmodifier only supports unixfs nodes (proto or raw)") +// NewDagModifier returns a new DagModifier, the Cid prefix for newly +// created nodes will be inherted from the passed in node. If the Cid +// version if not 0 raw leaves will also be enabled. The Prefix and +// RawLeaves options can be overridden by changing them after the call. func NewDagModifier(ctx context.Context, from node.Node, serv mdag.DAGService, spl chunk.SplitterGen) (*DagModifier, error) { switch from.(type) { case *mdag.ProtoNode, *mdag.RawNode: @@ -55,11 +60,20 @@ func NewDagModifier(ctx context.Context, from node.Node, serv mdag.DAGService, s return nil, ErrNotUnixfs } + prefix := from.Cid().Prefix() + prefix.Codec = cid.DagProtobuf + rawLeaves := false + if prefix.Version > 0 { + rawLeaves = true + } + return &DagModifier{ - curNode: from.Copy(), - dagserv: serv, - splitter: spl, - ctx: ctx, + curNode: from.Copy(), + dagserv: serv, + splitter: spl, + ctx: ctx, + Prefix: prefix, + RawLeaves: rawLeaves, }, nil } @@ -240,6 +254,7 @@ func (dm *DagModifier) modifyDag(n node.Node, offset uint64, data io.Reader) (*c nd := new(mdag.ProtoNode) nd.SetData(b) + nd.SetPrefix(&nd0.Prefix) k, err := dm.dagserv.Add(nd) if err != nil { return nil, false, err @@ -345,6 +360,7 @@ func (dm *DagModifier) appendData(nd node.Node, spl chunk.Splitter) (node.Node, dbp := &help.DagBuilderParams{ Dagserv: dm.dagserv, Maxlinks: help.DefaultLinksPerBlock, + Prefix: &dm.Prefix, RawLeaves: dm.RawLeaves, } return trickle.TrickleAppend(dm.ctx, nd, dbp.New(spl)) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 7b15b8532..1b1cc52f7 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -16,7 +16,7 @@ import ( u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" ) -func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier, rawLeaves testu.UseRawLeaves) []byte { +func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier, opts testu.NodeOpts) []byte { newdata := make([]byte, size) r := u.NewTimeSeededRand() r.Read(newdata) @@ -35,6 +35,12 @@ func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier, t.Fatalf("Mod length not correct! %d != %d", nmod, size) } + verifyNode(t, orig, dm, opts) + + return orig +} + +func verifyNode(t *testing.T, orig []byte, dm *DagModifier, opts testu.NodeOpts) { nd, err := dm.GetNode() if err != nil { t.Fatal(err) @@ -44,10 +50,11 @@ func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier, Getter: dm.dagserv, Direct: h.DefaultLinksPerBlock, LayerRepeat: 4, - RawLeaves: bool(rawLeaves), + Prefix: &opts.Prefix, + RawLeaves: opts.RawLeavesUsed, }) if err != nil { - t.Error(err) + t.Fatal(err) } rd, err := uio.NewDagReader(context.Background(), nd, dm.dagserv) @@ -64,20 +71,20 @@ func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier, if err != nil { t.Fatal(err) } - return orig } -func runBothSubtests(t *testing.T, tfunc func(*testing.T, testu.UseRawLeaves)) { - t.Run("leaves=ProtoBuf", func(t *testing.T) { tfunc(t, testu.ProtoBufLeaves) }) - t.Run("leaves=Raw", func(t *testing.T) { tfunc(t, testu.RawLeaves) }) +func runAllSubtests(t *testing.T, tfunc func(*testing.T, testu.NodeOpts)) { + t.Run("opts=ProtoBufLeaves", func(t *testing.T) { tfunc(t, testu.UseProtoBufLeaves) }) + t.Run("opts=RawLeaves", func(t *testing.T) { tfunc(t, testu.UseRawLeaves) }) + t.Run("opts=CidV1", func(t *testing.T) { tfunc(t, testu.UseCidV1) }) } func TestDagModifierBasic(t *testing.T) { - runBothSubtests(t, testDagModifierBasic) + runAllSubtests(t, testDagModifierBasic) } -func testDagModifierBasic(t *testing.T, rawLeaves testu.UseRawLeaves) { +func testDagModifierBasic(t *testing.T, opts testu.NodeOpts) { dserv := testu.GetDAGServ() - b, n := testu.GetRandomNode(t, dserv, 50000, rawLeaves) + b, n := testu.GetRandomNode(t, dserv, 50000, opts) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -85,33 +92,35 @@ func testDagModifierBasic(t *testing.T, rawLeaves testu.UseRawLeaves) { if err != nil { t.Fatal(err) } - dagmod.RawLeaves = bool(rawLeaves) + if opts.ForceRawLeaves { + dagmod.RawLeaves = true + } // Within zero block beg := uint64(15) length := uint64(60) t.Log("Testing mod within zero block") - b = testModWrite(t, beg, length, b, dagmod, rawLeaves) + b = testModWrite(t, beg, length, b, dagmod, opts) // Within bounds of existing file beg = 1000 length = 4000 t.Log("Testing mod within bounds of existing multiblock file.") - b = testModWrite(t, beg, length, b, dagmod, rawLeaves) + b = testModWrite(t, beg, length, b, dagmod, opts) // Extend bounds beg = 49500 length = 4000 t.Log("Testing mod that extends file.") - b = testModWrite(t, beg, length, b, dagmod, rawLeaves) + b = testModWrite(t, beg, length, b, dagmod, opts) // "Append" beg = uint64(len(b)) length = 3000 t.Log("Testing pure append") - _ = testModWrite(t, beg, length, b, dagmod, rawLeaves) + _ = testModWrite(t, beg, length, b, dagmod, opts) // Verify reported length node, err := dagmod.GetNode() @@ -131,11 +140,11 @@ func testDagModifierBasic(t *testing.T, rawLeaves testu.UseRawLeaves) { } func TestMultiWrite(t *testing.T) { - runBothSubtests(t, testMultiWrite) + runAllSubtests(t, testMultiWrite) } -func testMultiWrite(t *testing.T, rawLeaves testu.UseRawLeaves) { +func testMultiWrite(t *testing.T, opts testu.NodeOpts) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv, rawLeaves) + n := testu.GetEmptyNode(t, dserv, opts) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -144,7 +153,9 @@ func testMultiWrite(t *testing.T, rawLeaves testu.UseRawLeaves) { if err != nil { t.Fatal(err) } - dagmod.RawLeaves = bool(rawLeaves) + if opts.ForceRawLeaves { + dagmod.RawLeaves = true + } data := make([]byte, 4000) u.NewTimeSeededRand().Read(data) @@ -167,32 +178,16 @@ func testMultiWrite(t *testing.T, rawLeaves testu.UseRawLeaves) { t.Fatal("Size was reported incorrectly") } } - nd, err := dagmod.GetNode() - if err != nil { - t.Fatal(err) - } - read, err := uio.NewDagReader(context.Background(), nd, dserv) - if err != nil { - t.Fatal(err) - } - rbuf, err := ioutil.ReadAll(read) - if err != nil { - t.Fatal(err) - } - - err = testu.ArrComp(rbuf, data) - if err != nil { - t.Fatal(err) - } + verifyNode(t, data, dagmod, opts) } func TestMultiWriteAndFlush(t *testing.T) { - runBothSubtests(t, testMultiWriteAndFlush) + runAllSubtests(t, testMultiWriteAndFlush) } -func testMultiWriteAndFlush(t *testing.T, rawLeaves testu.UseRawLeaves) { +func testMultiWriteAndFlush(t *testing.T, opts testu.NodeOpts) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv, rawLeaves) + n := testu.GetEmptyNode(t, dserv, opts) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -201,7 +196,9 @@ func testMultiWriteAndFlush(t *testing.T, rawLeaves testu.UseRawLeaves) { if err != nil { t.Fatal(err) } - dagmod.RawLeaves = bool(rawLeaves) + if opts.ForceRawLeaves { + dagmod.RawLeaves = true + } data := make([]byte, 20) u.NewTimeSeededRand().Read(data) @@ -219,32 +216,16 @@ func testMultiWriteAndFlush(t *testing.T, rawLeaves testu.UseRawLeaves) { t.Fatal(err) } } - nd, err := dagmod.GetNode() - if err != nil { - t.Fatal(err) - } - read, err := uio.NewDagReader(context.Background(), nd, dserv) - if err != nil { - t.Fatal(err) - } - rbuf, err := ioutil.ReadAll(read) - if err != nil { - t.Fatal(err) - } - - err = testu.ArrComp(rbuf, data) - if err != nil { - t.Fatal(err) - } + verifyNode(t, data, dagmod, opts) } func TestWriteNewFile(t *testing.T) { - runBothSubtests(t, testWriteNewFile) + runAllSubtests(t, testWriteNewFile) } -func testWriteNewFile(t *testing.T, rawLeaves testu.UseRawLeaves) { +func testWriteNewFile(t *testing.T, opts testu.NodeOpts) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv, rawLeaves) + n := testu.GetEmptyNode(t, dserv, opts) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -253,7 +234,9 @@ func testWriteNewFile(t *testing.T, rawLeaves testu.UseRawLeaves) { if err != nil { t.Fatal(err) } - dagmod.RawLeaves = bool(rawLeaves) + if opts.ForceRawLeaves { + dagmod.RawLeaves = true + } towrite := make([]byte, 2000) u.NewTimeSeededRand().Read(towrite) @@ -266,32 +249,15 @@ func testWriteNewFile(t *testing.T, rawLeaves testu.UseRawLeaves) { t.Fatal("Wrote wrong amount") } - nd, err := dagmod.GetNode() - if err != nil { - t.Fatal(err) - } - - read, err := uio.NewDagReader(ctx, nd, dserv) - if err != nil { - t.Fatal(err) - } - - data, err := ioutil.ReadAll(read) - if err != nil { - t.Fatal(err) - } - - if err := testu.ArrComp(data, towrite); err != nil { - t.Fatal(err) - } + verifyNode(t, towrite, dagmod, opts) } func TestMultiWriteCoal(t *testing.T) { - runBothSubtests(t, testMultiWriteCoal) + runAllSubtests(t, testMultiWriteCoal) } -func testMultiWriteCoal(t *testing.T, rawLeaves testu.UseRawLeaves) { +func testMultiWriteCoal(t *testing.T, opts testu.NodeOpts) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv, rawLeaves) + n := testu.GetEmptyNode(t, dserv, opts) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -300,7 +266,9 @@ func testMultiWriteCoal(t *testing.T, rawLeaves testu.UseRawLeaves) { if err != nil { t.Fatal(err) } - dagmod.RawLeaves = bool(rawLeaves) + if opts.ForceRawLeaves { + dagmod.RawLeaves = true + } data := make([]byte, 1000) u.NewTimeSeededRand().Read(data) @@ -316,34 +284,16 @@ func testMultiWriteCoal(t *testing.T, rawLeaves testu.UseRawLeaves) { } } - nd, err := dagmod.GetNode() - if err != nil { - t.Fatal(err) - } - read, err := uio.NewDagReader(context.Background(), nd, dserv) - if err != nil { - t.Fatal(err) - } - dagmod.RawLeaves = bool(rawLeaves) - - rbuf, err := ioutil.ReadAll(read) - if err != nil { - t.Fatal(err) - } - - err = testu.ArrComp(rbuf, data) - if err != nil { - t.Fatal(err) - } + verifyNode(t, data, dagmod, opts) } func TestLargeWriteChunks(t *testing.T) { - runBothSubtests(t, testLargeWriteChunks) + runAllSubtests(t, testLargeWriteChunks) } -func testLargeWriteChunks(t *testing.T, rawLeaves testu.UseRawLeaves) { +func testLargeWriteChunks(t *testing.T, opts testu.NodeOpts) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv, rawLeaves) + n := testu.GetEmptyNode(t, dserv, opts) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -352,7 +302,9 @@ func testLargeWriteChunks(t *testing.T, rawLeaves testu.UseRawLeaves) { if err != nil { t.Fatal(err) } - dagmod.RawLeaves = bool(rawLeaves) + if opts.ForceRawLeaves { + dagmod.RawLeaves = true + } wrsize := 1000 datasize := 10000000 @@ -378,15 +330,14 @@ func testLargeWriteChunks(t *testing.T, rawLeaves testu.UseRawLeaves) { if err = testu.ArrComp(out, data); err != nil { t.Fatal(err) } - } func TestDagTruncate(t *testing.T) { - runBothSubtests(t, testDagTruncate) + runAllSubtests(t, testDagTruncate) } -func testDagTruncate(t *testing.T, rawLeaves testu.UseRawLeaves) { +func testDagTruncate(t *testing.T, opts testu.NodeOpts) { dserv := testu.GetDAGServ() - b, n := testu.GetRandomNode(t, dserv, 50000, rawLeaves) + b, n := testu.GetRandomNode(t, dserv, 50000, opts) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -394,7 +345,9 @@ func testDagTruncate(t *testing.T, rawLeaves testu.UseRawLeaves) { if err != nil { t.Fatal(err) } - dagmod.RawLeaves = bool(rawLeaves) + if opts.ForceRawLeaves { + dagmod.RawLeaves = true + } err = dagmod.Truncate(12345) if err != nil { @@ -453,11 +406,11 @@ func testDagTruncate(t *testing.T, rawLeaves testu.UseRawLeaves) { } func TestSparseWrite(t *testing.T) { - runBothSubtests(t, testSparseWrite) + runAllSubtests(t, testSparseWrite) } -func testSparseWrite(t *testing.T, rawLeaves testu.UseRawLeaves) { +func testSparseWrite(t *testing.T, opts testu.NodeOpts) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv, rawLeaves) + n := testu.GetEmptyNode(t, dserv, opts) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -465,7 +418,9 @@ func testSparseWrite(t *testing.T, rawLeaves testu.UseRawLeaves) { if err != nil { t.Fatal(err) } - dagmod.RawLeaves = bool(rawLeaves) + if opts.ForceRawLeaves { + dagmod.RawLeaves = true + } buf := make([]byte, 5000) u.NewTimeSeededRand().Read(buf[2500:]) @@ -495,11 +450,11 @@ func testSparseWrite(t *testing.T, rawLeaves testu.UseRawLeaves) { } func TestSeekPastEndWrite(t *testing.T) { - runBothSubtests(t, testSeekPastEndWrite) + runAllSubtests(t, testSeekPastEndWrite) } -func testSeekPastEndWrite(t *testing.T, rawLeaves testu.UseRawLeaves) { +func testSeekPastEndWrite(t *testing.T, opts testu.NodeOpts) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv, rawLeaves) + n := testu.GetEmptyNode(t, dserv, opts) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -507,7 +462,9 @@ func testSeekPastEndWrite(t *testing.T, rawLeaves testu.UseRawLeaves) { if err != nil { t.Fatal(err) } - dagmod.RawLeaves = bool(rawLeaves) + if opts.ForceRawLeaves { + dagmod.RawLeaves = true + } buf := make([]byte, 5000) u.NewTimeSeededRand().Read(buf[2500:]) @@ -546,11 +503,11 @@ func testSeekPastEndWrite(t *testing.T, rawLeaves testu.UseRawLeaves) { } func TestRelativeSeek(t *testing.T) { - runBothSubtests(t, testRelativeSeek) + runAllSubtests(t, testRelativeSeek) } -func testRelativeSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { +func testRelativeSeek(t *testing.T, opts testu.NodeOpts) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv, rawLeaves) + n := testu.GetEmptyNode(t, dserv, opts) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -558,7 +515,9 @@ func testRelativeSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { if err != nil { t.Fatal(err) } - dagmod.RawLeaves = bool(rawLeaves) + if opts.ForceRawLeaves { + dagmod.RawLeaves = true + } for i := 0; i < 64; i++ { dagmod.Write([]byte{byte(i)}) @@ -580,11 +539,11 @@ func testRelativeSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { } func TestInvalidSeek(t *testing.T) { - runBothSubtests(t, testInvalidSeek) + runAllSubtests(t, testInvalidSeek) } -func testInvalidSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { +func testInvalidSeek(t *testing.T, opts testu.NodeOpts) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv, rawLeaves) + n := testu.GetEmptyNode(t, dserv, opts) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -592,7 +551,9 @@ func testInvalidSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { if err != nil { t.Fatal(err) } - dagmod.RawLeaves = bool(rawLeaves) + if opts.ForceRawLeaves { + dagmod.RawLeaves = true + } _, err = dagmod.Seek(10, -10) @@ -602,12 +563,12 @@ func testInvalidSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { } func TestEndSeek(t *testing.T) { - runBothSubtests(t, testEndSeek) + runAllSubtests(t, testEndSeek) } -func testEndSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { +func testEndSeek(t *testing.T, opts testu.NodeOpts) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv, rawLeaves) + n := testu.GetEmptyNode(t, dserv, opts) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -615,7 +576,9 @@ func testEndSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { if err != nil { t.Fatal(err) } - dagmod.RawLeaves = bool(rawLeaves) + if opts.ForceRawLeaves { + dagmod.RawLeaves = true + } _, err = dagmod.Write(make([]byte, 100)) if err != nil { @@ -648,12 +611,12 @@ func testEndSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { } func TestReadAndSeek(t *testing.T) { - runBothSubtests(t, testReadAndSeek) + runAllSubtests(t, testReadAndSeek) } -func testReadAndSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { +func testReadAndSeek(t *testing.T, opts testu.NodeOpts) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv, rawLeaves) + n := testu.GetEmptyNode(t, dserv, opts) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -661,7 +624,9 @@ func testReadAndSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { if err != nil { t.Fatal(err) } - dagmod.RawLeaves = bool(rawLeaves) + if opts.ForceRawLeaves { + dagmod.RawLeaves = true + } writeBuf := []byte{0, 1, 2, 3, 4, 5, 6, 7} dagmod.Write(writeBuf) @@ -720,12 +685,12 @@ func testReadAndSeek(t *testing.T, rawLeaves testu.UseRawLeaves) { } func TestCtxRead(t *testing.T) { - runBothSubtests(t, testCtxRead) + runAllSubtests(t, testCtxRead) } -func testCtxRead(t *testing.T, rawLeaves testu.UseRawLeaves) { +func testCtxRead(t *testing.T, opts testu.NodeOpts) { dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(t, dserv, rawLeaves) + n := testu.GetEmptyNode(t, dserv, opts) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -733,7 +698,9 @@ func testCtxRead(t *testing.T, rawLeaves testu.UseRawLeaves) { if err != nil { t.Fatal(err) } - dagmod.RawLeaves = bool(rawLeaves) + if opts.ForceRawLeaves { + dagmod.RawLeaves = true + } _, err = dagmod.Write([]byte{0, 1, 2, 3, 4, 5, 6, 7}) if err != nil { @@ -757,7 +724,7 @@ func testCtxRead(t *testing.T, rawLeaves testu.UseRawLeaves) { func BenchmarkDagmodWrite(b *testing.B) { b.StopTimer() dserv := testu.GetDAGServ() - n := testu.GetEmptyNode(b, dserv, testu.ProtoBufLeaves) + n := testu.GetEmptyNode(b, dserv, testu.UseProtoBufLeaves) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 933493f36..fc9a04be3 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -15,6 +15,7 @@ import ( mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" ) @@ -29,20 +30,26 @@ func GetDAGServ() mdag.DAGService { return mdagmock.Mock() } -type UseRawLeaves bool +type NodeOpts struct { + Prefix cid.Prefix + // ForceRawLeaves if true will force the use of raw leaves + ForceRawLeaves bool + // RawLeavesUsed is true if raw leaves or either implicitly or explicitly enabled + RawLeavesUsed bool +} -const ( - ProtoBufLeaves UseRawLeaves = false - RawLeaves UseRawLeaves = true -) +var UseProtoBufLeaves = NodeOpts{Prefix: mdag.V0CidPrefix()} +var UseRawLeaves = NodeOpts{Prefix: mdag.V0CidPrefix(), ForceRawLeaves: true, RawLeavesUsed: true} +var UseCidV1 = NodeOpts{Prefix: mdag.V1CidPrefix(), RawLeavesUsed: true} -func GetNode(t testing.TB, dserv mdag.DAGService, data []byte, rawLeaves UseRawLeaves) node.Node { +func GetNode(t testing.TB, dserv mdag.DAGService, data []byte, opts NodeOpts) node.Node { in := bytes.NewReader(data) dbp := h.DagBuilderParams{ Dagserv: dserv, Maxlinks: h.DefaultLinksPerBlock, - RawLeaves: bool(rawLeaves), + Prefix: &opts.Prefix, + RawLeaves: opts.RawLeavesUsed, } node, err := trickle.TrickleLayout(dbp.New(SizeSplitterGen(500)(in))) @@ -53,18 +60,18 @@ func GetNode(t testing.TB, dserv mdag.DAGService, data []byte, rawLeaves UseRawL return node } -func GetEmptyNode(t testing.TB, dserv mdag.DAGService, rawLeaves UseRawLeaves) node.Node { - return GetNode(t, dserv, []byte{}, rawLeaves) +func GetEmptyNode(t testing.TB, dserv mdag.DAGService, opts NodeOpts) node.Node { + return GetNode(t, dserv, []byte{}, opts) } -func GetRandomNode(t testing.TB, dserv mdag.DAGService, size int64, rawLeaves UseRawLeaves) ([]byte, node.Node) { +func GetRandomNode(t testing.TB, dserv mdag.DAGService, size int64, opts NodeOpts) ([]byte, node.Node) { in := io.LimitReader(u.NewTimeSeededRand(), size) buf, err := ioutil.ReadAll(in) if err != nil { t.Fatal(err) } - node := GetNode(t, dserv, buf, rawLeaves) + node := GetNode(t, dserv, buf, opts) return buf, node } From d4cf3dda207267a4cda7887c5a92aa4e0e7c9aa5 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 8 Aug 2017 18:19:46 -0400 Subject: [PATCH 2007/3817] Enable CidV1 (and other prefixes) in the Dag Modifier. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@6df3bd3ba00bde86634f30d69fec75af3fa7f85c --- ipld/merkledag/node.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index ad4f246cc..fae3fa7fc 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -42,6 +42,9 @@ var v1CidPrefix = cid.Prefix{ Version: 1, } +func V0CidPrefix() cid.Prefix { return v0CidPrefix } +func V1CidPrefix() cid.Prefix { return v1CidPrefix } + // PrefixForCidVersion returns the Protobuf prefix for a given CID version func PrefixForCidVersion(version int) (cid.Prefix, error) { switch version { From a1af55c1896cb85a2123d695d1caad2b2a7b1de6 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 8 Aug 2017 18:20:56 -0400 Subject: [PATCH 2008/3817] Test for alternative hash function in Dag Modifier. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-unixfs@95015163f834f4e9b47358b4461a31449bdfd09f --- unixfs/mod/dagmodifier_test.go | 1 + unixfs/test/utils.go | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 1b1cc52f7..473d34294 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -77,6 +77,7 @@ func runAllSubtests(t *testing.T, tfunc func(*testing.T, testu.NodeOpts)) { t.Run("opts=ProtoBufLeaves", func(t *testing.T) { tfunc(t, testu.UseProtoBufLeaves) }) t.Run("opts=RawLeaves", func(t *testing.T) { tfunc(t, testu.UseRawLeaves) }) t.Run("opts=CidV1", func(t *testing.T) { tfunc(t, testu.UseCidV1) }) + t.Run("opts=Blake2b256", func(t *testing.T) { tfunc(t, testu.UseBlake2b256) }) } func TestDagModifierBasic(t *testing.T) { diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index fc9a04be3..8b18ad9cd 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -18,6 +18,7 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + mh "gx/ipfs/QmU9a9NV9RdPNwZQDYd5uKsm6N6LJLSvLbywDDYFbaaC6P/go-multihash" ) func SizeSplitterGen(size int64) chunk.SplitterGen { @@ -41,6 +42,13 @@ type NodeOpts struct { var UseProtoBufLeaves = NodeOpts{Prefix: mdag.V0CidPrefix()} var UseRawLeaves = NodeOpts{Prefix: mdag.V0CidPrefix(), ForceRawLeaves: true, RawLeavesUsed: true} var UseCidV1 = NodeOpts{Prefix: mdag.V1CidPrefix(), RawLeavesUsed: true} +var UseBlake2b256 NodeOpts + +func init() { + UseBlake2b256 = UseCidV1 + UseBlake2b256.Prefix.MhType = mh.Names["blake2b-256"] + UseBlake2b256.Prefix.MhLength = -1 +} func GetNode(t testing.TB, dserv mdag.DAGService, data []byte, opts NodeOpts) node.Node { in := bytes.NewReader(data) From 466288eddeee5bc9887983552c2094d772b34317 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 16 Aug 2017 19:18:05 -0400 Subject: [PATCH 2009/3817] mfs: inherit CID prefix from from parent directory License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-unixfs@d0d21fa723a8bd5d23c5f33cf6d07c68ddb8b072 --- unixfs/hamt/hamt.go | 7 +++++++ unixfs/io/dirbuilder.go | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index a360c37c2..bd2809301 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -121,6 +121,7 @@ func NewHamtFromDag(dserv dag.DAGService, nd node.Node) (*HamtShard, error) { ds.children = make([]child, len(pbnd.Links())) ds.bitfield = new(big.Int).SetBytes(pbd.GetData()) ds.hashFunc = pbd.GetHashType() + ds.prefix = &ds.nd.Prefix return ds, nil } @@ -130,6 +131,11 @@ func (ds *HamtShard) SetPrefix(prefix *cid.Prefix) { ds.prefix = prefix } +// GetPrefix gets the CID Prefix, may be nil if unset +func (ds *HamtShard) Prefix() *cid.Prefix { + return ds.prefix +} + // Node serializes the HAMT structure into a merkledag node with unixfs formatting func (ds *HamtShard) Node() (node.Node, error) { out := new(dag.ProtoNode) @@ -500,6 +506,7 @@ func (ds *HamtShard) modifyValue(ctx context.Context, hv *hashBits, key string, if err != nil { return err } + ns.prefix = ds.prefix chhv := &hashBits{ b: hash([]byte(child.key)), consumed: hv.consumed, diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 76ec34faa..9ca587e2c 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -115,6 +115,7 @@ func (d *Directory) switchToSharding(ctx context.Context) error { if err != nil { return err } + s.SetPrefix(&d.dirnode.Prefix) d.shard = s for _, lnk := range d.dirnode.Links() { @@ -192,3 +193,11 @@ func (d *Directory) GetNode() (node.Node, error) { return d.shard.Node() } + +func (d *Directory) GetPrefix() *cid.Prefix { + if d.shard == nil { + return &d.dirnode.Prefix + } + + return d.shard.Prefix() +} From dcbf369ce4c4271a6dff8a45a778379ea5b901f7 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 16 Aug 2017 19:18:05 -0400 Subject: [PATCH 2010/3817] mfs: inherit CID prefix from from parent directory License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-mfs@032e029850f09a7f929576fba589657770384c7e --- mfs/dir.go | 6 ++++++ mfs/file.go | 11 ++++++++--- mfs/ops.go | 8 ++++++-- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index a489336d6..219dc4cce 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -58,6 +58,11 @@ func NewDirectory(ctx context.Context, name string, node node.Node, parent child }, nil } +// GetPrefix gets the CID prefix of the root node +func (d *Directory) GetPrefix() *cid.Prefix { + return d.dirbuilder.GetPrefix() +} + // SetPrefix sets the CID prefix func (d *Directory) SetPrefix(prefix *cid.Prefix) { d.dirbuilder.SetPrefix(prefix) @@ -299,6 +304,7 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { } ndir := ft.EmptyDirNode() + ndir.SetPrefix(d.GetPrefix()) _, err = d.dserv.Add(ndir) if err != nil { diff --git a/mfs/file.go b/mfs/file.go index 85c9e59bc..0ff8b41de 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -27,14 +27,19 @@ type File struct { RawLeaves bool } -// NewFile returns a NewFile object with the given parameters +// NewFile returns a NewFile object with the given parameters. If the +// Cid version is non-zero RawLeaves will be enabled. func NewFile(name string, node node.Node, parent childCloser, dserv dag.DAGService) (*File, error) { - return &File{ + fi := &File{ dserv: dserv, parent: parent, name: name, node: node, - }, nil + } + if node.Cid().Prefix().Version > 0 { + fi.RawLeaves = true + } + return fi, nil } const ( diff --git a/mfs/ops.go b/mfs/ops.go index a086e8602..5b72adcad 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -129,7 +129,9 @@ func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { if err != nil { return err } - mkd.SetPrefix(r.Prefix) + if r.Prefix != nil { + mkd.SetPrefix(r.Prefix) + } fsn = mkd } else if err != nil { return err @@ -148,7 +150,9 @@ func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { return err } } - final.SetPrefix(r.Prefix) + if r.Prefix != nil { + final.SetPrefix(r.Prefix) + } if flush { err := final.Flush() From 8c1962f0951d34dfe036d63cc93d233f732cc60e Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 19 Oct 2017 12:09:56 -0400 Subject: [PATCH 2011/3817] Documentation. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-merkledag@bd644961fe67b58aa4eebfb3e176c9ed6cd218f4 --- ipld/merkledag/node.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index fae3fa7fc..ad021fa30 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -42,7 +42,10 @@ var v1CidPrefix = cid.Prefix{ Version: 1, } +// V0CidPrefix returns a prefix for CIDv0 func V0CidPrefix() cid.Prefix { return v0CidPrefix } + +// V1CidPrefix returns a prefix for CIDv1 with the default settings func V1CidPrefix() cid.Prefix { return v1CidPrefix } // PrefixForCidVersion returns the Protobuf prefix for a given CID version From 0c4947c2d28bcc0b049b889b653e985f74aceee5 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 19 Oct 2017 12:09:56 -0400 Subject: [PATCH 2012/3817] Documentation. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-unixfs@e94381ac5176c45d95d376fc0974a8d564eaf267 --- unixfs/hamt/hamt.go | 2 +- unixfs/io/dirbuilder.go | 1 + unixfs/test/utils.go | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index bd2809301..fecf23b46 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -131,7 +131,7 @@ func (ds *HamtShard) SetPrefix(prefix *cid.Prefix) { ds.prefix = prefix } -// GetPrefix gets the CID Prefix, may be nil if unset +// Prefix gets the CID Prefix, may be nil if unset func (ds *HamtShard) Prefix() *cid.Prefix { return ds.prefix } diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 9ca587e2c..f86d23fb7 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -194,6 +194,7 @@ func (d *Directory) GetNode() (node.Node, error) { return d.shard.Node() } +// GetPrefix returns the CID Prefix used func (d *Directory) GetPrefix() *cid.Prefix { if d.shard == nil { return &d.dirnode.Prefix diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 8b18ad9cd..24359d377 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -31,6 +31,7 @@ func GetDAGServ() mdag.DAGService { return mdagmock.Mock() } +// NodeOpts is used by GetNode, GetEmptyNode and GetRandomNode type NodeOpts struct { Prefix cid.Prefix // ForceRawLeaves if true will force the use of raw leaves From 202f8628892026ebf3d4c9d31e2a1dd125d6576a Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 8 Sep 2017 21:00:55 -0400 Subject: [PATCH 2013/3817] Eliminate Prefix field from MFS root, use MkdirOpts. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-mfs@bff6529fcc742f1080f6bdb4eee91b2b7af5d229 --- mfs/mfs_test.go | 4 ++-- mfs/ops.go | 26 +++++++++++++++++--------- mfs/system.go | 3 --- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 09e9de00d..bebfa8d30 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -735,7 +735,7 @@ func TestMfsHugeDir(t *testing.T) { _, rt := setupRoot(ctx, t) for i := 0; i < 10000; i++ { - err := Mkdir(rt, fmt.Sprintf("/dir%d", i), false, false) + err := Mkdir(rt, fmt.Sprintf("/dir%d", i), MkdirOpts{Mkparents: false, Flush: false}) if err != nil { t.Fatal(err) } @@ -747,7 +747,7 @@ func TestMkdirP(t *testing.T) { defer cancel() _, rt := setupRoot(ctx, t) - err := Mkdir(rt, "/a/b/c/d/e/f", true, true) + err := Mkdir(rt, "/a/b/c/d/e/f", MkdirOpts{Mkparents: true, Flush: true}) if err != nil { t.Fatal(err) } diff --git a/mfs/ops.go b/mfs/ops.go index 5b72adcad..49ce398d4 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -9,6 +9,7 @@ import ( path "github.com/ipfs/go-ipfs/path" + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) @@ -97,9 +98,16 @@ func PutNode(r *Root, path string, nd node.Node) error { return pdir.AddChild(filename, nd) } +// MkdirOpts is used by Mkdir +type MkdirOpts struct { + Mkparents bool + Flush bool + Prefix *cid.Prefix +} + // Mkdir creates a directory at 'path' under the directory 'd', creating // intermediary directories as needed if 'mkparents' is set to true -func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { +func Mkdir(r *Root, pth string, opts MkdirOpts) error { if pth == "" { return fmt.Errorf("no path given to Mkdir") } @@ -115,7 +123,7 @@ func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { if len(parts) == 0 { // this will only happen on 'mkdir /' - if mkparents { + if opts.Mkparents { return nil } return fmt.Errorf("cannot create directory '/': Already exists") @@ -124,13 +132,13 @@ func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { cur := r.GetValue().(*Directory) for i, d := range parts[:len(parts)-1] { fsn, err := cur.Child(d) - if err == os.ErrNotExist && mkparents { + if err == os.ErrNotExist && opts.Mkparents { mkd, err := cur.Mkdir(d) if err != nil { return err } - if r.Prefix != nil { - mkd.SetPrefix(r.Prefix) + if opts.Prefix != nil { + mkd.SetPrefix(opts.Prefix) } fsn = mkd } else if err != nil { @@ -146,15 +154,15 @@ func Mkdir(r *Root, pth string, mkparents bool, flush bool) error { final, err := cur.Mkdir(parts[len(parts)-1]) if err != nil { - if !mkparents || err != os.ErrExist || final == nil { + if !opts.Mkparents || err != os.ErrExist || final == nil { return err } } - if r.Prefix != nil { - final.SetPrefix(r.Prefix) + if opts.Prefix != nil { + final.SetPrefix(opts.Prefix) } - if flush { + if opts.Flush { err := final.Flush() if err != nil { return err diff --git a/mfs/system.go b/mfs/system.go index 0641704cf..fc5be0f6e 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -61,9 +61,6 @@ type Root struct { dserv dag.DAGService Type string - - // Prefix to use for any children created - Prefix *cid.Prefix } type PubFunc func(context.Context, *cid.Cid) error From 2733a20d68cf563f7673900ef598dff8ec4145ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 18 Oct 2017 22:26:03 +0200 Subject: [PATCH 2014/3817] merkledag: keep key order in dedupeKeys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-merkledag@e4ee5dc85baa3cb801006d86303b1c8aa7a1966c --- ipld/merkledag/merkledag.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 92cb5fa86..bcf0e84a1 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -307,11 +307,14 @@ func GetNodes(ctx context.Context, ds DAGService, keys []*cid.Cid) []NodeGetter // Remove duplicates from a list of keys func dedupeKeys(cids []*cid.Cid) []*cid.Cid { + out := make([]*cid.Cid, 0, len(cids)) set := cid.NewSet() for _, c := range cids { - set.Add(c) + if set.Visit(c) { + out = append(out, c) + } } - return set.Keys() + return out } func newNodePromise(ctx context.Context) NodeGetter { From d6e5c75d31e1e5e8295d310765c337feef5f33c1 Mon Sep 17 00:00:00 2001 From: Ian Preston Date: Sat, 28 Oct 2017 12:14:49 +0100 Subject: [PATCH 2015/3817] optimise pin update command This handles merkle links that aren't named. And improves the Peergos usage from worst case 30s to ~20ms License: MIT Signed-off-by: Ian Preston This commit was moved from ipfs/go-merkledag@889c7246028dd1f03572e4c67a97b09e57fda522 --- ipld/merkledag/utils/diffenum.go | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go index 7e3a76356..2066fa338 100644 --- a/ipld/merkledag/utils/diffenum.go +++ b/ipld/merkledag/utils/diffenum.go @@ -65,27 +65,34 @@ type diffpair struct { // getLinkDiff returns a changeset between nodes 'a' and 'b'. Currently does // not log deletions as our usecase doesnt call for this. func getLinkDiff(a, b node.Node) []diffpair { - have := make(map[string]*node.Link) - names := make(map[string]*node.Link) + ina := make(map[string]*node.Link) + inb := make(map[string]*node.Link) + var aonly []*cid.Cid + for _, l := range b.Links() { + inb[l.Cid.KeyString()] = l + } for _, l := range a.Links() { - have[l.Cid.KeyString()] = l - names[l.Name] = l + ina[l.Cid.KeyString()] = l + if inb[l.Cid.KeyString()] == nil { + aonly = append(aonly, l.Cid) + } } var out []diffpair + var aindex = 0 for _, l := range b.Links() { - if have[l.Cid.KeyString()] != nil { + if ina[l.Cid.KeyString()] != nil { continue } - match, ok := names[l.Name] - if !ok { + if aindex < len(aonly) { + out = append(out, diffpair{bef: aonly[aindex], aft: l.Cid}) + aindex++ + } else { out = append(out, diffpair{aft: l.Cid}) continue } - - out = append(out, diffpair{bef: match.Cid, aft: l.Cid}) } return out } From 9dcab32d945af3c7ac1d7f7c2c3edd8ee72c001c Mon Sep 17 00:00:00 2001 From: Ian Preston Date: Sat, 28 Oct 2017 12:55:08 +0100 Subject: [PATCH 2016/3817] improve style License: MIT Signed-off-by: Ian Preston This commit was moved from ipfs/go-merkledag@3e66775643ea7a1a4a44799c64cfd9f498369267 --- ipld/merkledag/utils/diffenum.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go index 2066fa338..c186f22d9 100644 --- a/ipld/merkledag/utils/diffenum.go +++ b/ipld/merkledag/utils/diffenum.go @@ -72,14 +72,15 @@ func getLinkDiff(a, b node.Node) []diffpair { inb[l.Cid.KeyString()] = l } for _, l := range a.Links() { - ina[l.Cid.KeyString()] = l - if inb[l.Cid.KeyString()] == nil { + var key = l.Cid.KeyString() + ina[key] = l + if inb[key] == nil { aonly = append(aonly, l.Cid) } } var out []diffpair - var aindex = 0 + var aindex int for _, l := range b.Links() { if ina[l.Cid.KeyString()] != nil { From eb010628df0fbca0be69cb5c3ae0e1099255bfe8 Mon Sep 17 00:00:00 2001 From: Ian Preston Date: Sat, 28 Oct 2017 17:09:22 +0100 Subject: [PATCH 2017/3817] add tests for basic name matching in pin update License: MIT Signed-off-by: Ian Preston This commit was moved from ipfs/go-merkledag@2feb26df952321608f32694230c26121e176012d --- ipld/merkledag/utils/diffenum.go | 2 +- ipld/merkledag/utils/diffenum_test.go | 43 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go index c186f22d9..c2904cbc0 100644 --- a/ipld/merkledag/utils/diffenum.go +++ b/ipld/merkledag/utils/diffenum.go @@ -72,7 +72,7 @@ func getLinkDiff(a, b node.Node) []diffpair { inb[l.Cid.KeyString()] = l } for _, l := range a.Links() { - var key = l.Cid.KeyString() + var key = l.Cid.KeyString() ina[key] = l if inb[key] == nil { aonly = append(aonly, l.Cid) diff --git a/ipld/merkledag/utils/diffenum_test.go b/ipld/merkledag/utils/diffenum_test.go index ed5e0db36..bf7d38d42 100644 --- a/ipld/merkledag/utils/diffenum_test.go +++ b/ipld/merkledag/utils/diffenum_test.go @@ -84,6 +84,49 @@ var tg3 = map[string]ndesc{ "d": ndesc{}, } +var tg4 = map[string]ndesc{ + "a1": ndesc{ + "key1": "b", + "key2": "c", + }, + "a2": ndesc{ + "key1": "b", + "key2": "d", + }, +} + +var tg5 = map[string]ndesc{ + "a1": ndesc{ + "key1": "a", + "key2": "b", + }, + "a2": ndesc{ + "key1": "c", + "key2": "d", + }, +} + +func TestNameMatching(t *testing.T) { + nds := mkGraph(tg4) + + diff := getLinkDiff(nds["a1"], nds["a2"]) + if len(diff) != 1 { + t.Fatal(fmt.Errorf("node diff didn't match by name")) + } +} + +func TestNameMatching2(t *testing.T) { + nds := mkGraph(tg5) + + diff := getLinkDiff(nds["a1"], nds["a2"]) + if len(diff) != 2 { + t.Fatal(fmt.Errorf("incorrect number of link diff elements")) + } + if !(diff[0].bef.Equals(nds["a1"].Links()[0].Cid) && diff[0].aft.Equals(nds["a2"].Links()[0].Cid)) { + t.Fatal(fmt.Errorf("node diff didn't match by name")) + } +} + func TestDiffEnumBasic(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() From 570e7f4f00972642107f803848c1aa708bde4243 Mon Sep 17 00:00:00 2001 From: Forrest Weston Date: Tue, 14 Nov 2017 17:39:30 -0800 Subject: [PATCH 2018/3817] Add event logging around path resolution License: MIT Signed-off-by: Forrest Weston This commit was moved from ipfs/go-path@cd26e8c3067055d430c594cfc53ec99033ac7de2 --- path/resolver.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/path/resolver.go b/path/resolver.go index 5b8fe515a..e11c2767d 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -135,6 +135,7 @@ func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]nod if err != nil { return nil, err } + defer log.EventBegin(ctx, "resolvePathComponents", logging.LoggableMap{"parts": parts, "cid": h}).Done() log.Debug("resolve dag get") nd, err := s.DAG.Get(ctx, h) @@ -154,6 +155,7 @@ func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]nod // would retrieve "baz" in ("bar" in ("foo" in nd.Links).Links).Links func (s *Resolver) ResolveLinks(ctx context.Context, ndd node.Node, names []string) ([]node.Node, error) { + defer log.EventBegin(ctx, "resolveLinks", logging.LoggableMap{"names": names}).Done() result := make([]node.Node, 0, len(names)+1) result = append(result, ndd) nd := ndd // dup arg workaround From b6f9fbb2bc163d4eb806e0710cc60e9f38ba0332 Mon Sep 17 00:00:00 2001 From: Forrest Weston Date: Wed, 15 Nov 2017 12:21:08 -0800 Subject: [PATCH 2019/3817] Add error message to event logs in path resolution If an error occurs during an event add it to the events metadata License: MIT Signed-off-by: Forrest Weston This commit was moved from ipfs/go-path@0fbb0f904f4002b8709009e468122649bfbcdd0e --- path/resolver.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index e11c2767d..094bc755e 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -131,15 +131,19 @@ func ResolveSingle(ctx context.Context, ds dag.DAGService, nd node.Node, names [ // It uses the first path component as a hash (key) of the first node, then // resolves all other components walking the links, with ResolveLinks. func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]node.Node, error) { + evt := log.EventBegin(ctx, "resolvePathComponents", logging.LoggableMap{"fpath": fpath}) + defer evt.Done() + h, parts, err := SplitAbsPath(fpath) if err != nil { + evt.Append(logging.LoggableMap{"error": err.Error()}) return nil, err } - defer log.EventBegin(ctx, "resolvePathComponents", logging.LoggableMap{"parts": parts, "cid": h}).Done() log.Debug("resolve dag get") nd, err := s.DAG.Get(ctx, h) if err != nil { + evt.Append(logging.LoggableMap{"error": err.Error()}) return nil, err } @@ -155,7 +159,8 @@ func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]nod // would retrieve "baz" in ("bar" in ("foo" in nd.Links).Links).Links func (s *Resolver) ResolveLinks(ctx context.Context, ndd node.Node, names []string) ([]node.Node, error) { - defer log.EventBegin(ctx, "resolveLinks", logging.LoggableMap{"names": names}).Done() + evt := log.EventBegin(ctx, "resolveLinks", logging.LoggableMap{"names": names}) + defer evt.Done() result := make([]node.Node, 0, len(names)+1) result = append(result, ndd) nd := ndd // dup arg workaround @@ -168,13 +173,16 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd node.Node, names []stri lnk, rest, err := s.ResolveOnce(ctx, s.DAG, nd, names) if err == dag.ErrLinkNotFound { + evt.Append(logging.LoggableMap{"error": err.Error()}) return result, ErrNoLink{Name: names[0], Node: nd.Cid()} } else if err != nil { + evt.Append(logging.LoggableMap{"error": err.Error()}) return result, err } nextnode, err := lnk.GetNode(ctx, s.DAG) if err != nil { + evt.Append(logging.LoggableMap{"error": err.Error()}) return result, err } From 1c1d72f6e54924c5b6ffe741a69a18fbafc90e1b Mon Sep 17 00:00:00 2001 From: Jan Winkelmann Date: Sat, 1 Apr 2017 16:58:17 +0200 Subject: [PATCH 2020/3817] cmd: use go-ipfs-cmds License: MIT Signed-off-by: keks This commit was moved from ipfs/go-namesys@3f50c3f4fb66fa91d7205841cb58620dd1420dcf --- namesys/dns.go | 1 - namesys/interface.go | 1 + namesys/ipns_select_test.go | 4 ++-- namesys/proquint.go | 1 + namesys/republisher/repub_test.go | 6 +++--- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 3cb2cd6e2..1267ef56b 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -7,7 +7,6 @@ import ( "strings" path "github.com/ipfs/go-ipfs/path" - isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index 84a6bbe2c..acaec1740 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -34,6 +34,7 @@ import ( "time" context "context" + path "github.com/ipfs/go-ipfs/path" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index e12af16d9..9c0f34114 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) diff --git a/namesys/proquint.go b/namesys/proquint.go index ee6ada978..3a842f97a 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,6 +4,7 @@ import ( "errors" context "context" + path "github.com/ipfs/go-ipfs/path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index be438b838..377b959ae 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -1,20 +1,20 @@ package republisher_test import ( + "context" "errors" "testing" "time" - context "context" - goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - "github.com/ipfs/go-ipfs/core" mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" + pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" mocknet "gx/ipfs/QmefgzMbKZYsmHFkLqxgaTBG9ypeEjrdWRD5WXH4j1cWDL/go-libp2p/p2p/net/mock" + goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" ) func TestRepublish(t *testing.T) { From e3d111303c5dcdd442930445a07b229046e1864d Mon Sep 17 00:00:00 2001 From: Jan Winkelmann Date: Sat, 1 Apr 2017 16:58:17 +0200 Subject: [PATCH 2021/3817] cmd: use go-ipfs-cmds License: MIT Signed-off-by: keks This commit was moved from ipfs/go-ipfs-routing@9983272af4dab3ae091b12205e3a6bb762190fa6 --- routing/offline/offline_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index 9f5b3f0b2..253c533c0 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -3,9 +3,10 @@ package offline import ( "bytes" "context" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" "testing" + + "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" ) func TestOfflineRouterStorage(t *testing.T) { From 82b858de9b79db8d6d62e8ccdd413feb8a20654e Mon Sep 17 00:00:00 2001 From: Jan Winkelmann Date: Sat, 1 Apr 2017 16:58:17 +0200 Subject: [PATCH 2022/3817] cmd: use go-ipfs-cmds License: MIT Signed-off-by: keks This commit was moved from ipfs/go-mfs@5e27273a759088ca800a5ba0a8da149f54b54cc3 --- mfs/repub_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 1c21bd793..4a9bc4869 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -1,13 +1,12 @@ package mfs import ( + "context" "testing" "time" - ci "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil/ci" - - "context" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + ci "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil/ci" ) func TestRepublisher(t *testing.T) { From 5d81cbd77ab83b19f4f71c61706925cc0430d245 Mon Sep 17 00:00:00 2001 From: Jan Winkelmann Date: Sat, 1 Apr 2017 16:58:17 +0200 Subject: [PATCH 2023/3817] cmd: use go-ipfs-cmds License: MIT Signed-off-by: keks This commit was moved from ipfs/go-merkledag@eff5b2f32a714c42d8fbd18288d448c24666e6d2 --- ipld/merkledag/utils/utils_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 75d7181fc..0c3d77199 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -1,13 +1,13 @@ package dagutils import ( + "context" "testing" dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - context "context" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ) From d06a7c961f382ae908fbce332633bae651901333 Mon Sep 17 00:00:00 2001 From: Jan Winkelmann Date: Sat, 1 Apr 2017 16:58:17 +0200 Subject: [PATCH 2024/3817] cmd: use go-ipfs-cmds License: MIT Signed-off-by: keks This commit was moved from ipfs/go-ipfs-blockstore@6593a2089deeb670590ba6fec93c8dd78125843e --- blockstore/caching.go | 1 + blockstore/util/remove.go | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/blockstore/caching.go b/blockstore/caching.go index 0ea375b06..5d6f3bc85 100644 --- a/blockstore/caching.go +++ b/blockstore/caching.go @@ -4,6 +4,7 @@ import ( "errors" context "context" + "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" ) diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 47552988b..467f55092 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -85,12 +85,17 @@ func FilterPinned(pins pin.Pinner, out chan<- interface{}, cids []*cid.Cid) []*c return stillOkay } -// ProcRmOutput takes the channel returned by RmBlocks and writes -// to stdout/stderr according to the RemovedBlock objects received in -// that channel. -func ProcRmOutput(in <-chan interface{}, sout io.Writer, serr io.Writer) error { +// ProcRmOutput takes a function which returns a result from RmBlocks or EOF if there is no input. +// It then writes to stdout/stderr according to the RemovedBlock object returned from the function. +func ProcRmOutput(next func() (interface{}, error), sout io.Writer, serr io.Writer) error { someFailed := false - for res := range in { + for { + res, err := next() + if err == io.EOF { + break + } else if err != nil { + return err + } r := res.(*RemovedBlock) if r.Hash == "" && r.Error != "" { return fmt.Errorf("aborted: %s", r.Error) From b3f2eff24aee7366c41d0aa817f2c8268570f0a5 Mon Sep 17 00:00:00 2001 From: Jan Winkelmann Date: Sat, 1 Apr 2017 16:58:17 +0200 Subject: [PATCH 2025/3817] cmd: use go-ipfs-cmds License: MIT Signed-off-by: keks This commit was moved from ipfs/go-ipfs-chunker@c33af760f3207511885976640353577de98fa471 --- chunker/rabin_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index ede2bc20a..9cef888ce 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -3,10 +3,11 @@ package chunk import ( "bytes" "fmt" - "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" "io" "testing" + + util "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" ) func TestRabinChunking(t *testing.T) { From 8a45b9465e4ccdbd156dae25ccee464459c16eef Mon Sep 17 00:00:00 2001 From: keks Date: Mon, 23 Oct 2017 16:50:39 +0200 Subject: [PATCH 2026/3817] compatible to js-ipfs-api License: MIT Signed-off-by: keks This commit was moved from ipfs/go-namesys@01755f54922f0c05f28f25ea776b45c5f6e8207b --- namesys/republisher/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 377b959ae..7586b2f6c 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( path "github.com/ipfs/go-ipfs/path" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - mocknet "gx/ipfs/QmefgzMbKZYsmHFkLqxgaTBG9ypeEjrdWRD5WXH4j1cWDL/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" + mocknet "gx/ipfs/QmefgzMbKZYsmHFkLqxgaTBG9ypeEjrdWRD5WXH4j1cWDL/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { From 467b6c80fadbc8529b4edd0cedb836d7a213ce5d Mon Sep 17 00:00:00 2001 From: keks Date: Mon, 23 Oct 2017 16:50:39 +0200 Subject: [PATCH 2027/3817] compatible to js-ipfs-api License: MIT Signed-off-by: keks This commit was moved from ipfs/go-ipfs-routing@d47f90716dd411ac613cdf6cad1cd94e38e41506 --- routing/offline/offline_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index 253c533c0..5c00bc3c1 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -4,9 +4,9 @@ import ( "bytes" "context" "testing" - - "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" ) func TestOfflineRouterStorage(t *testing.T) { From fd7d7c96b5f44ae191f3d723260479d1803a62e4 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 18 Nov 2017 08:39:38 -0800 Subject: [PATCH 2028/3817] fix hamt delete issue License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@2f01bfb081e436e3adcc05c38ff0c2aacf091b0b --- unixfs/hamt/hamt.go | 17 +++++++++++------ unixfs/hamt/hamt_test.go | 14 ++++++++++++++ unixfs/hamt/util.go | 18 ++---------------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index fecf23b46..bd73434cb 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -492,16 +492,21 @@ func (ds *HamtShard) modifyValue(ctx context.Context, hv *hashBits, key string, return nil case *shardValue: - switch { - case val == nil: // passing a nil value signifies a 'delete' - ds.bitfield.SetBit(ds.bitfield, idx, 0) - return ds.rmChild(cindex) + if child.key == key { + // value modification + if val == nil { + ds.bitfield.SetBit(ds.bitfield, idx, 0) + return ds.rmChild(cindex) + } - case child.key == key: // value modification child.val = val return nil + } else { + if val == nil { + return os.ErrNotExist + } - default: // replace value with another shard, one level deeper + // replace value with another shard, one level deeper ns, err := NewHamtShard(ds.dserv, ds.tableSize) if err != nil { return err diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 77997d2fd..eb204dfd6 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -222,6 +222,20 @@ func TestRemoveElems(t *testing.T) { } ctx := context.Background() + for i := 0; i < 100; i++ { + err := s.Remove(ctx, fmt.Sprintf("NOTEXIST%d", rand.Int())) + if err != os.ErrNotExist { + t.Fatal("shouldnt be able to remove things that don't exist") + } + } + + for _, d := range dirs { + _, err := s.Find(ctx, d) + if err != nil { + t.Fatal(err) + } + } + shuffle(time.Now().UnixNano(), dirs) for _, d := range dirs { diff --git a/unixfs/hamt/util.go b/unixfs/hamt/util.go index 08c232a8a..4692e7493 100644 --- a/unixfs/hamt/util.go +++ b/unixfs/hamt/util.go @@ -2,6 +2,7 @@ package hamt import ( "math/big" + "math/bits" ) // hashBits is a helper that allows the reading of the 'next n bits' as an integer. @@ -39,25 +40,10 @@ func (hb *hashBits) Next(i int) int { } } -const ( - m1 = 0x5555555555555555 //binary: 0101... - m2 = 0x3333333333333333 //binary: 00110011.. - m4 = 0x0f0f0f0f0f0f0f0f //binary: 4 zeros, 4 ones ... - h01 = 0x0101010101010101 //the sum of 256 to the power of 0,1,2,3... -) - -// from https://en.wikipedia.org/wiki/Hamming_weight -func popCountUint64(x uint64) int { - x -= (x >> 1) & m1 //put count of each 2 bits into those 2 bits - x = (x & m2) + ((x >> 2) & m2) //put count of each 4 bits into those 4 bits - x = (x + (x >> 4)) & m4 //put count of each 8 bits into those 8 bits - return int((x * h01) >> 56) -} - func popCount(i *big.Int) int { var n int for _, v := range i.Bits() { - n += popCountUint64(uint64(v)) + n += bits.OnesCount64(uint64(v)) } return n } From 49b81e8c1995959ac93aef793e8236986789f39d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 20 Nov 2017 16:25:06 -0800 Subject: [PATCH 2029/3817] gx: massive update Note: This commit is technically broken. However, I need to make a bunch of cmds changes to make this work and I'd rather not bundle both changes into a single commit. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@3d92102c8593ebe056b3459d8bde243531fb358a --- namesys/publisher_test.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 3149b3e48..28635f3bf 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -10,9 +10,9 @@ import ( mockrouting "github.com/ipfs/go-ipfs/routing/mock" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + testutil "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" - testutil "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 7586b2f6c..43c9c1f5e 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmefgzMbKZYsmHFkLqxgaTBG9ypeEjrdWRD5WXH4j1cWDL/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmTzs3Gp2rU3HuNayjBVG7qBgbaKWE8bgtwJ7faRxAe9UP/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index bb30a243a..7503ad3d7 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" - testutil "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + testutil "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" From b116aeb9dfdc7751ee63fc885d6cd7d4630001bb Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 20 Nov 2017 16:25:06 -0800 Subject: [PATCH 2030/3817] gx: massive update Note: This commit is technically broken. However, I need to make a bunch of cmds changes to make this work and I'd rather not bundle both changes into a single commit. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@f547b08f69da817f05362da898ee302b77c446c0 --- mfs/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 4a9bc4869..45b9006b4 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -6,7 +6,7 @@ import ( "time" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - ci "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil/ci" + ci "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil/ci" ) func TestRepublisher(t *testing.T) { From b3895e641e436a845eeed0468b03f31a5a01942d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 20 Nov 2017 16:25:06 -0800 Subject: [PATCH 2031/3817] gx: massive update Note: This commit is technically broken. However, I need to make a bunch of cmds changes to make this work and I'd rather not bundle both changes into a single commit. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@517d7a32e9c0980dc8c0c6650f7a2fbc6ffc9abe --- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 2 +- routing/mock/centralized_test.go | 2 +- routing/mock/interface.go | 2 +- routing/none/none_client.go | 2 +- routing/offline/offline_test.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 0614d2d77..407629299 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -6,7 +6,7 @@ import ( "time" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index c5f90d1d8..02929a4cf 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -6,7 +6,7 @@ import ( "sync" "time" - "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 84f9fcbcf..9ecba1181 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -6,7 +6,7 @@ import ( "time" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 3522c6f38..5dcbbd21d 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -8,7 +8,7 @@ import ( "context" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" + "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" diff --git a/routing/none/none_client.go b/routing/none/none_client.go index d0a00a0ee..ccf535fca 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -9,8 +9,8 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" + p2phost "gx/ipfs/QmRS46AyqtpJBsf1zmQdeizSDEzo1qkWR7rdEuPFAv8237/go-libp2p-host" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" - p2phost "gx/ipfs/Qmc1XhrFEiSeBNn3mpfg6gEuYCt5im2gYmNVmncsvmpeAk/go-libp2p-host" ) type nilclient struct { diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index 5c00bc3c1..2b1eb521e 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -5,8 +5,8 @@ import ( "context" "testing" + "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - "gx/ipfs/QmWRCn8vruNAzHx8i6SAXinuheRitKEGu8c7m26stKvsYx/go-testutil" ) func TestOfflineRouterStorage(t *testing.T) { From 9790921dd683cfd5075e932df7777f1cd58cee4e Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 7 Jul 2017 18:32:59 +0300 Subject: [PATCH 2032/3817] namesys/pubsub: publisher and resolver Commits: namesys: pubsub Publisher and Resolver namesys/pubsub: pacify code climate. namesys/pubsub: timeout for rendezvous namesys/pubsub: filter self in bootstrap connections namesys/pubsub: Publish to the correct topic License: MIT Signed-off-by: vyzo namesys/pubsub: unit test Commits: namesys/pubsub: test namesys/pubsub_test: pacify code climate namesys/pubsub: update test to use extant mock routing License: MIT Signed-off-by: vyzo namesys/pubsub: integrate namesys pubsub namesys: integrate pubsub resolvers namesys/pubsub_test: tweak delays - trying to make travis happy. namesys/pubsub: fix duplicate bootstraps - subscription key is topic, not ipnskey. namesys/pubsub: no warning needed on cancellation namesys/pubsub: warning for receive errors - and more informative error messages at that. namesys/pubsub_test: smaller test - make it work with seemingly low fdlimits in travis/macosx. also, more informative test failures. namesys/pubsub: add delay to let pubsub perform handshake namesys/pubsub: update gx imports namesys/pubsub_test: preconnect publisher, reduce delays - preconnects the publisher to the receivers in order to avoid bootstrap flakiness with connectivity problems in travis. reduces sleeps to 1s for flood propagation (3s seems excessive with 5 hosts). namesys/pubsub: drop named return values in resolveOnce - per review comment. namesys/pubsub: check errors namesys/pubsub: store bytes in resolver datastore namesys/pubsub: resolver Cancel - for canceling subscriptions, pre whyrusleeping's request. namesys/pubsub: fix resolution without /ipns prefix - also improve the logging a bit. namesys/pubsub: don't resolve own keys through pubsub namesys/pubsub: signal ErrResolveFailed on resolution failure namesys/pubsub: use sync datastore, resolver lock only for subs namesys/pubsub_test: coverage for Cancel License: MIT Signed-off-by: vyzo namesys/pubsub: parallelize dht and pubsub publishing Commits: namesys/pubsub: code cosmetics namesys: parallelize publishing with dht and pubsub namesys/pubsub: periodically reprovide topic rendezvous namesys/pubsub: cancelation for rendezvous goroutine namesys/pubsub: log ipns record seqno on publish License: MIT Signed-off-by: vyzo namesys/pubsub: error checking License: MIT Signed-off-by: vyzo namesys/pubsub: --enable-namesys-pubsub option and management Commits: package.json: update go-libp2p-blankhost namesys: fix stale package imports update go-testutil namesys/pubsub: reduce bootstrap provide period to 8hr namesys/pubsub: try to extract the key from id first option to enable ipns pubsub: --enable-namesys-pubsub ipfs name pubsub management subcommands corehttp/gateway_test: mockNamesys needs to implement GetResolver pacify code climate License: MIT Signed-off-by: vyzo namesys/pubsub: pubsub sharness test test/sharness: test for ipns pubsub namesys/pubsub: return boolean indicator on Cancel package.json: remove duplicate entry for go-testutil update gx deps, testutil to 1.1.12 fix jenkins failure: use tabs in t0183-namesys-pubsub t0183: use 4 spaces for tabification License: MIT Signed-off-by: vyzo namesys/pubsub: update for new command interface License: MIT Signed-off-by: vyzo namesys/pubsub: fix sharness test for broken MacOS echo echo -n "" should print -n, but hey it's a mac. License: MIT Signed-off-by: vyzo This commit was moved from ipfs/go-namesys@054f5016250e786b1d94dd1c88872409ed65329a --- namesys/interface.go | 8 + namesys/namesys.go | 138 +++++++++++-- namesys/namesys_test.go | 4 +- namesys/pubsub.go | 430 ++++++++++++++++++++++++++++++++++++++++ namesys/pubsub_test.go | 187 +++++++++++++++++ 5 files changed, 745 insertions(+), 22 deletions(-) create mode 100644 namesys/pubsub.go create mode 100644 namesys/pubsub_test.go diff --git a/namesys/interface.go b/namesys/interface.go index acaec1740..8097ac616 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -70,6 +70,7 @@ var ErrPublishFailed = errors.New("Could not publish name.") type NameSystem interface { Resolver Publisher + ResolverLookup } // Resolver is an object capable of resolving names. @@ -112,3 +113,10 @@ type Publisher interface { // call once the records spec is implemented PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error } + +// ResolverLookup is an object capable of finding resolvers for a subsystem +type ResolverLookup interface { + + // GetResolver retrieves a resolver associated with a subsystem + GetResolver(subs string) (Resolver, bool) +} diff --git a/namesys/namesys.go b/namesys/namesys.go index d1cda0870..82952f250 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -2,14 +2,20 @@ package namesys import ( "context" + "errors" "strings" + "sync" "time" path "github.com/ipfs/go-ipfs/path" routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" + p2phost "gx/ipfs/QmRS46AyqtpJBsf1zmQdeizSDEzo1qkWR7rdEuPFAv8237/go-libp2p-host" + mh "gx/ipfs/QmU9a9NV9RdPNwZQDYd5uKsm6N6LJLSvLbywDDYFbaaC6P/go-multihash" + floodsub "gx/ipfs/QmVNv1WV6XxzQV4MBuiLX5729wMazaf8TNzm2Sq6ejyHh7/go-libp2p-floodsub" ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) @@ -36,11 +42,28 @@ func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSys "dht": NewRoutingResolver(r, cachesize), }, publishers: map[string]Publisher{ - "/ipns/": NewRoutingPublisher(r, ds), + "dht": NewRoutingPublisher(r, ds), }, } } +// AddPubsubNameSystem adds the pubsub publisher and resolver to the namesystem +func AddPubsubNameSystem(ctx context.Context, ns NameSystem, host p2phost.Host, r routing.IpfsRouting, ds ds.Datastore, ps *floodsub.PubSub) error { + mpns, ok := ns.(*mpns) + if !ok { + return errors.New("unexpected NameSystem; not an mpns instance") + } + + pkf, ok := r.(routing.PubKeyFetcher) + if !ok { + return errors.New("unexpected IpfsRouting; not a PubKeyFetcher instance") + } + + mpns.resolvers["pubsub"] = NewPubsubResolver(ctx, host, r, pkf, ps) + mpns.publishers["pubsub"] = NewPubsubPublisher(ctx, host, ds, r, ps) + return nil +} + const DefaultResolverCacheTTL = time.Minute // Resolve implements Resolver. @@ -72,38 +95,100 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) return "", ErrResolveFailed } - for protocol, resolver := range ns.resolvers { - log.Debugf("Attempting to resolve %s with %s", segments[2], protocol) - p, err := resolver.resolveOnce(ctx, segments[2]) - if err == nil { - if len(segments) > 3 { - return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) - } else { - return p, err + makePath := func(p path.Path) (path.Path, error) { + if len(segments) > 3 { + return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) + } else { + return p, nil + } + } + + // Resolver selection: + // 1. if it is a multihash resolve through "pubsub" (if available), + // with fallback to "dht" + // 2. if it is a domain name, resolve through "dns" + // 3. otherwise resolve through the "proquint" resolver + key := segments[2] + + _, err := mh.FromB58String(key) + if err == nil { + res, ok := ns.resolvers["pubsub"] + if ok { + p, err := res.resolveOnce(ctx, key) + if err == nil { + return makePath(p) + } + } + + res, ok = ns.resolvers["dht"] + if ok { + p, err := res.resolveOnce(ctx, key) + if err == nil { + return makePath(p) + } + } + + return "", ErrResolveFailed + } + + if isd.IsDomain(key) { + res, ok := ns.resolvers["dns"] + if ok { + p, err := res.resolveOnce(ctx, key) + if err == nil { + return makePath(p) } } + + return "", ErrResolveFailed } + + res, ok := ns.resolvers["proquint"] + if ok { + p, err := res.resolveOnce(ctx, key) + if err == nil { + return makePath(p) + } + + return "", ErrResolveFailed + } + log.Warningf("No resolver found for %s", name) return "", ErrResolveFailed } // Publish implements Publisher func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error { - err := ns.publishers["/ipns/"].Publish(ctx, name, value) - if err != nil { - return err - } - ns.addToDHTCache(name, value, time.Now().Add(DefaultRecordTTL)) - return nil + return ns.PublishWithEOL(ctx, name, value, time.Now().Add(DefaultRecordTTL)) } func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error { - err := ns.publishers["/ipns/"].PublishWithEOL(ctx, name, value, eol) - if err != nil { - return err + var dhtErr error + + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + dhtErr = ns.publishers["dht"].PublishWithEOL(ctx, name, value, eol) + if dhtErr == nil { + ns.addToDHTCache(name, value, eol) + } + wg.Done() + }() + + pub, ok := ns.publishers["pubsub"] + if ok { + wg.Add(1) + go func() { + err := pub.PublishWithEOL(ctx, name, value, eol) + if err != nil { + log.Warningf("error publishing %s with pubsub: %s", name, err.Error()) + } + wg.Done() + }() } - ns.addToDHTCache(name, value, eol) - return nil + + wg.Wait() + return dhtErr } func (ns *mpns) addToDHTCache(key ci.PrivKey, value path.Path, eol time.Time) { @@ -138,3 +223,16 @@ func (ns *mpns) addToDHTCache(key ci.PrivKey, value path.Path, eol time.Time) { eol: eol, }) } + +// GetResolver implements ResolverLookup +func (ns *mpns) GetResolver(subs string) (Resolver, bool) { + res, ok := ns.resolvers[subs] + if ok { + ires, ok := res.(Resolver) + if ok { + return ires, true + } + } + + return nil, false +} diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 1507f5510..78396c25e 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -58,8 +58,8 @@ func mockResolverTwo() *mockResolver { func TestNamesysResolution(t *testing.T) { r := &mpns{ resolvers: map[string]resolver{ - "one": mockResolverOne(), - "two": mockResolverTwo(), + "dht": mockResolverOne(), + "dns": mockResolverTwo(), }, } diff --git a/namesys/pubsub.go b/namesys/pubsub.go new file mode 100644 index 000000000..6c1284f27 --- /dev/null +++ b/namesys/pubsub.go @@ -0,0 +1,430 @@ +package namesys + +import ( + "context" + "errors" + "fmt" + "strings" + "sync" + "time" + + pb "github.com/ipfs/go-ipfs/namesys/pb" + path "github.com/ipfs/go-ipfs/path" + dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" + + cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" + pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" + p2phost "gx/ipfs/QmRS46AyqtpJBsf1zmQdeizSDEzo1qkWR7rdEuPFAv8237/go-libp2p-host" + u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + mh "gx/ipfs/QmU9a9NV9RdPNwZQDYd5uKsm6N6LJLSvLbywDDYFbaaC6P/go-multihash" + floodsub "gx/ipfs/QmVNv1WV6XxzQV4MBuiLX5729wMazaf8TNzm2Sq6ejyHh7/go-libp2p-floodsub" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + record "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record" + dhtpb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" +) + +// PubsubPublisher is a publisher that distributes IPNS records through pubsub +type PubsubPublisher struct { + ctx context.Context + ds ds.Datastore + host p2phost.Host + cr routing.ContentRouting + ps *floodsub.PubSub + + mx sync.Mutex + subs map[string]struct{} +} + +// PubsubResolver is a resolver that receives IPNS records through pubsub +type PubsubResolver struct { + ctx context.Context + ds ds.Datastore + host p2phost.Host + cr routing.ContentRouting + pkf routing.PubKeyFetcher + ps *floodsub.PubSub + + mx sync.Mutex + subs map[string]*floodsub.Subscription +} + +// NewPubsubPublisher constructs a new Publisher that publishes IPNS records through pubsub. +// The constructor interface is complicated by the need to bootstrap the pubsub topic. +// This could be greatly simplified if the pubsub implementation handled bootstrap itself +func NewPubsubPublisher(ctx context.Context, host p2phost.Host, ds ds.Datastore, cr routing.ContentRouting, ps *floodsub.PubSub) *PubsubPublisher { + return &PubsubPublisher{ + ctx: ctx, + ds: ds, + host: host, // needed for pubsub bootstrap + cr: cr, // needed for pubsub bootstrap + ps: ps, + subs: make(map[string]struct{}), + } +} + +// NewPubsubResolver constructs a new Resolver that resolves IPNS records through pubsub. +// same as above for pubsub bootstrap dependencies +func NewPubsubResolver(ctx context.Context, host p2phost.Host, cr routing.ContentRouting, pkf routing.PubKeyFetcher, ps *floodsub.PubSub) *PubsubResolver { + return &PubsubResolver{ + ctx: ctx, + ds: dssync.MutexWrap(ds.NewMapDatastore()), + host: host, // needed for pubsub bootstrap + cr: cr, // needed for pubsub bootstrap + pkf: pkf, + ps: ps, + subs: make(map[string]*floodsub.Subscription), + } +} + +// Publish publishes an IPNS record through pubsub with default TTL +func (p *PubsubPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error { + return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordTTL)) +} + +// PublishWithEOL publishes an IPNS record through pubsub +func (p *PubsubPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error { + id, err := peer.IDFromPrivateKey(k) + if err != nil { + return err + } + + _, ipnskey := IpnsKeysForID(id) + + seqno, err := p.getPreviousSeqNo(ctx, ipnskey) + if err != nil { + return err + } + + seqno++ + + return p.publishRecord(ctx, k, value, seqno, eol, ipnskey, id) +} + +func (p *PubsubPublisher) getPreviousSeqNo(ctx context.Context, ipnskey string) (uint64, error) { + // the datastore is shared with the routing publisher to properly increment and persist + // ipns record sequence numbers. + prevrec, err := p.ds.Get(dshelp.NewKeyFromBinary([]byte(ipnskey))) + if err != nil { + if err == ds.ErrNotFound { + // None found, lets start at zero! + return 0, nil + } + return 0, err + } + + prbytes, ok := prevrec.([]byte) + if !ok { + return 0, fmt.Errorf("unexpected type returned from datastore: %#v", prevrec) + } + + var dsrec dhtpb.Record + err = proto.Unmarshal(prbytes, &dsrec) + if err != nil { + return 0, err + } + + var entry pb.IpnsEntry + err = proto.Unmarshal(dsrec.GetValue(), &entry) + if err != nil { + return 0, err + } + + return entry.GetSequence(), nil +} + +func (p *PubsubPublisher) publishRecord(ctx context.Context, k ci.PrivKey, value path.Path, seqno uint64, eol time.Time, ipnskey string, ID peer.ID) error { + entry, err := CreateRoutingEntryData(k, value, seqno, eol) + if err != nil { + return err + } + + data, err := proto.Marshal(entry) + if err != nil { + return err + } + + // the datastore is shared with the routing publisher to properly increment and persist + // ipns record sequence numbers; so we need to Record our new entry in the datastore + dsrec, err := record.MakePutRecord(k, ipnskey, data, true) + if err != nil { + return err + } + + dsdata, err := proto.Marshal(dsrec) + if err != nil { + return err + } + + err = p.ds.Put(dshelp.NewKeyFromBinary([]byte(ipnskey)), dsdata) + if err != nil { + return err + } + + // now we publish, but we also need to bootstrap pubsub for our messages to propagate + topic := "/ipns/" + ID.Pretty() + + p.mx.Lock() + _, ok := p.subs[topic] + + if !ok { + p.subs[topic] = struct{}{} + p.mx.Unlock() + + bootstrapPubsub(p.ctx, p.cr, p.host, topic) + } else { + p.mx.Unlock() + } + + log.Debugf("PubsubPublish: publish IPNS record for %s (%d)", topic, seqno) + return p.ps.Publish(topic, data) +} + +// Resolve resolves a name through pubsub and default depth limit +func (r *PubsubResolver) Resolve(ctx context.Context, name string) (path.Path, error) { + return r.ResolveN(ctx, name, DefaultDepthLimit) +} + +// ResolveN resolves a name through pubsub with the specified depth limit +func (r *PubsubResolver) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) { + return resolve(ctx, r, name, depth, "/ipns/") +} + +func (r *PubsubResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) { + log.Debugf("PubsubResolve: resolve '%s'", name) + + // retrieve the public key once (for verifying messages) + xname := strings.TrimPrefix(name, "/ipns/") + hash, err := mh.FromB58String(xname) + if err != nil { + log.Warningf("PubsubResolve: bad input hash: [%s]", xname) + return "", err + } + + id := peer.ID(hash) + if r.host.Peerstore().PrivKey(id) != nil { + return "", errors.New("Cannot resolve own name through pubsub") + } + + pubk := id.ExtractPublicKey() + if pubk == nil { + pubk, err = r.pkf.GetPublicKey(ctx, id) + if err != nil { + log.Warningf("PubsubResolve: error fetching public key: %s [%s]", err.Error(), xname) + return "", err + } + } + + // the topic is /ipns/Qmhash + if !strings.HasPrefix(name, "/ipns/") { + name = "/ipns/" + name + } + + r.mx.Lock() + // see if we already have a pubsub subscription; if not, subscribe + sub, ok := r.subs[name] + if !ok { + sub, err = r.ps.Subscribe(name) + if err != nil { + r.mx.Unlock() + return "", err + } + + log.Debugf("PubsubResolve: subscribed to %s", name) + + r.subs[name] = sub + + ctx, cancel := context.WithCancel(r.ctx) + go r.handleSubscription(sub, name, pubk, cancel) + go bootstrapPubsub(ctx, r.cr, r.host, name) + } + r.mx.Unlock() + + // resolve to what we may already have in the datastore + dsval, err := r.ds.Get(dshelp.NewKeyFromBinary([]byte(name))) + if err != nil { + if err == ds.ErrNotFound { + return "", ErrResolveFailed + } + return "", err + } + + data := dsval.([]byte) + entry := new(pb.IpnsEntry) + + err = proto.Unmarshal(data, entry) + if err != nil { + return "", err + } + + // check EOL; if the entry has expired, delete from datastore and return ds.ErrNotFound + eol, ok := checkEOL(entry) + if ok && eol.Before(time.Now()) { + err = r.ds.Delete(dshelp.NewKeyFromBinary([]byte(name))) + if err != nil { + log.Warningf("PubsubResolve: error deleting stale value for %s: %s", name, err.Error()) + } + + return "", ErrResolveFailed + } + + value, err := path.ParsePath(string(entry.GetValue())) + return value, err +} + +// GetSubscriptions retrieves a list of active topic subscriptions +func (r *PubsubResolver) GetSubscriptions() []string { + r.mx.Lock() + defer r.mx.Unlock() + + var res []string + for sub := range r.subs { + res = append(res, sub) + } + + return res +} + +// Cancel cancels a topic subscription; returns true if an active +// subscription was canceled +func (r *PubsubResolver) Cancel(name string) bool { + r.mx.Lock() + defer r.mx.Unlock() + + sub, ok := r.subs[name] + if ok { + sub.Cancel() + delete(r.subs, name) + } + + return ok +} + +func (r *PubsubResolver) handleSubscription(sub *floodsub.Subscription, name string, pubk ci.PubKey, cancel func()) { + defer sub.Cancel() + defer cancel() + + for { + msg, err := sub.Next(r.ctx) + if err != nil { + if err != context.Canceled { + log.Warningf("PubsubResolve: subscription error in %s: %s", name, err.Error()) + } + return + } + + err = r.receive(msg, name, pubk) + if err != nil { + log.Warningf("PubsubResolve: error proessing update for %s: %s", name, err.Error()) + } + } +} + +func (r *PubsubResolver) receive(msg *floodsub.Message, name string, pubk ci.PubKey) error { + data := msg.GetData() + if data == nil { + return errors.New("empty message") + } + + entry := new(pb.IpnsEntry) + err := proto.Unmarshal(data, entry) + if err != nil { + return err + } + + ok, err := pubk.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()) + if err != nil || !ok { + return errors.New("signature verification failed") + } + + _, err = path.ParsePath(string(entry.GetValue())) + if err != nil { + return err + } + + eol, ok := checkEOL(entry) + if ok && eol.Before(time.Now()) { + return errors.New("stale update; EOL exceeded") + } + + // check the sequence number against what we may already have in our datastore + oval, err := r.ds.Get(dshelp.NewKeyFromBinary([]byte(name))) + if err == nil { + odata := oval.([]byte) + oentry := new(pb.IpnsEntry) + + err = proto.Unmarshal(odata, oentry) + if err != nil { + return err + } + + if entry.GetSequence() <= oentry.GetSequence() { + return errors.New("stale update; sequence number too small") + } + } + + log.Debugf("PubsubResolve: receive IPNS record for %s", name) + + return r.ds.Put(dshelp.NewKeyFromBinary([]byte(name)), data) +} + +// rendezvous with peers in the name topic through provider records +// Note: rendezbous/boostrap should really be handled by the pubsub implementation itself! +func bootstrapPubsub(ctx context.Context, cr routing.ContentRouting, host p2phost.Host, name string) { + topic := "floodsub:" + name + hash := u.Hash([]byte(topic)) + rz := cid.NewCidV1(cid.Raw, hash) + + err := cr.Provide(ctx, rz, true) + if err != nil { + log.Warningf("bootstrapPubsub: error providing rendezvous for %s: %s", topic, err.Error()) + } + + go func() { + for { + select { + case <-time.After(8 * time.Hour): + err := cr.Provide(ctx, rz, true) + if err != nil { + log.Warningf("bootstrapPubsub: error providing rendezvous for %s: %s", topic, err.Error()) + } + case <-ctx.Done(): + return + } + } + }() + + rzctx, cancel := context.WithTimeout(ctx, time.Second*10) + defer cancel() + + wg := &sync.WaitGroup{} + for pi := range cr.FindProvidersAsync(rzctx, rz, 10) { + if pi.ID == host.ID() { + continue + } + wg.Add(1) + go func(pi pstore.PeerInfo) { + defer wg.Done() + + ctx, cancel := context.WithTimeout(ctx, time.Second*10) + defer cancel() + + err := host.Connect(ctx, pi) + if err != nil { + log.Debugf("Error connecting to pubsub peer %s: %s", pi.ID, err.Error()) + return + } + + // delay to let pubsub perform its handshake + time.Sleep(time.Millisecond * 250) + + log.Debugf("Connected to pubsub peer %s", pi.ID) + }(pi) + } + + wg.Wait() +} diff --git a/namesys/pubsub_test.go b/namesys/pubsub_test.go new file mode 100644 index 000000000..a7fa7be9b --- /dev/null +++ b/namesys/pubsub_test.go @@ -0,0 +1,187 @@ +package namesys + +import ( + "context" + "sync" + "testing" + "time" + + path "github.com/ipfs/go-ipfs/path" + mockrouting "github.com/ipfs/go-ipfs/routing/mock" + + routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" + pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" + testutil "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" + p2phost "gx/ipfs/QmRS46AyqtpJBsf1zmQdeizSDEzo1qkWR7rdEuPFAv8237/go-libp2p-host" + netutil "gx/ipfs/QmUUNDRYXgfqdjxTg79ogkciczU5y4WY1tKMU2vEX9CRN7/go-libp2p-netutil" + floodsub "gx/ipfs/QmVNv1WV6XxzQV4MBuiLX5729wMazaf8TNzm2Sq6ejyHh7/go-libp2p-floodsub" + ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + bhost "gx/ipfs/Qmb37wDRoh9VZMZXmmZktN35szvj9GeBYDtA9giDmXwwd7/go-libp2p-blankhost" +) + +func newNetHost(ctx context.Context, t *testing.T) p2phost.Host { + netw := netutil.GenSwarmNetwork(t, ctx) + return bhost.NewBlankHost(netw) +} + +func newNetHosts(ctx context.Context, t *testing.T, n int) []p2phost.Host { + var out []p2phost.Host + + for i := 0; i < n; i++ { + h := newNetHost(ctx, t) + out = append(out, h) + } + + return out +} + +// PubKeyFetcher implementation with a global key store +type mockKeyStore struct { + keys map[peer.ID]ci.PubKey + mx sync.Mutex +} + +func (m *mockKeyStore) addPubKey(id peer.ID, pkey ci.PubKey) { + m.mx.Lock() + defer m.mx.Unlock() + m.keys[id] = pkey +} + +func (m *mockKeyStore) getPubKey(id peer.ID) (ci.PubKey, error) { + m.mx.Lock() + defer m.mx.Unlock() + pkey, ok := m.keys[id] + if ok { + return pkey, nil + } + + return nil, routing.ErrNotFound +} + +func (m *mockKeyStore) GetPublicKey(ctx context.Context, id peer.ID) (ci.PubKey, error) { + return m.getPubKey(id) +} + +func newMockKeyStore() *mockKeyStore { + return &mockKeyStore{ + keys: make(map[peer.ID]ci.PubKey), + } +} + +// ConentRouting mock +func newMockRouting(ms mockrouting.Server, ks *mockKeyStore, host p2phost.Host) routing.ContentRouting { + id := host.ID() + + privk := host.Peerstore().PrivKey(id) + pubk := host.Peerstore().PubKey(id) + pi := host.Peerstore().PeerInfo(id) + + ks.addPubKey(id, pubk) + return ms.Client(testutil.NewIdentity(id, pi.Addrs[0], privk, pubk)) +} + +func newMockRoutingForHosts(ms mockrouting.Server, ks *mockKeyStore, hosts []p2phost.Host) []routing.ContentRouting { + rs := make([]routing.ContentRouting, len(hosts)) + for i := 0; i < len(hosts); i++ { + rs[i] = newMockRouting(ms, ks, hosts[i]) + } + return rs +} + +// tests +func TestPubsubPublishSubscribe(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ms := mockrouting.NewServer() + ks := newMockKeyStore() + + pubhost := newNetHost(ctx, t) + pubmr := newMockRouting(ms, ks, pubhost) + pub := NewPubsubPublisher(ctx, pubhost, ds.NewMapDatastore(), pubmr, floodsub.NewFloodSub(ctx, pubhost)) + privk := pubhost.Peerstore().PrivKey(pubhost.ID()) + pubpinfo := pstore.PeerInfo{ID: pubhost.ID(), Addrs: pubhost.Addrs()} + + name := "/ipns/" + pubhost.ID().Pretty() + + reshosts := newNetHosts(ctx, t, 5) + resmrs := newMockRoutingForHosts(ms, ks, reshosts) + res := make([]*PubsubResolver, len(reshosts)) + for i := 0; i < len(res); i++ { + res[i] = NewPubsubResolver(ctx, reshosts[i], resmrs[i], ks, floodsub.NewFloodSub(ctx, reshosts[i])) + if err := reshosts[i].Connect(ctx, pubpinfo); err != nil { + t.Fatal(err) + } + } + + time.Sleep(time.Millisecond * 100) + for i := 0; i < len(res); i++ { + checkResolveNotFound(ctx, t, i, res[i], name) + // delay to avoid connection storms + time.Sleep(time.Millisecond * 100) + } + + // let the bootstrap finish + time.Sleep(time.Second * 1) + + val := path.Path("/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY") + err := pub.Publish(ctx, privk, val) + if err != nil { + t.Fatal(err) + } + + // let the flood propagate + time.Sleep(time.Second * 1) + for i := 0; i < len(res); i++ { + checkResolve(ctx, t, i, res[i], name, val) + } + + val = path.Path("/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD") + err = pub.Publish(ctx, privk, val) + if err != nil { + t.Fatal(err) + } + + // let the flood propagate + time.Sleep(time.Second * 1) + for i := 0; i < len(res); i++ { + checkResolve(ctx, t, i, res[i], name, val) + } + + // cancel subscriptions + for i := 0; i < len(res); i++ { + res[i].Cancel(name) + } + time.Sleep(time.Millisecond * 100) + + nval := path.Path("/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr") + err = pub.Publish(ctx, privk, nval) + if err != nil { + t.Fatal(err) + } + + // check we still have the old value in the resolver + time.Sleep(time.Second * 1) + for i := 0; i < len(res); i++ { + checkResolve(ctx, t, i, res[i], name, val) + } +} + +func checkResolveNotFound(ctx context.Context, t *testing.T, i int, resolver Resolver, name string) { + _, err := resolver.Resolve(ctx, name) + if err != ErrResolveFailed { + t.Fatalf("[resolver %d] unexpected error: %s", i, err.Error()) + } +} + +func checkResolve(ctx context.Context, t *testing.T, i int, resolver Resolver, name string, val path.Path) { + xval, err := resolver.Resolve(ctx, name) + if err != nil { + t.Fatalf("[resolver %d] resolve failed: %s", i, err.Error()) + } + if xval != val { + t.Fatalf("[resolver %d] unexpected value: %s %s", i, val, xval) + } +} From 411d608548d4683f786b6f1bcf30081683f4a02c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 29 Nov 2017 15:22:00 -0800 Subject: [PATCH 2033/3817] publish ipns records on start (after a delay of 1 minute) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@70bbe63ca77d8d38c5cffaa358946c2ec7224ae2 --- namesys/republisher/repub.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 3ab382473..b7c864cbd 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -27,6 +27,7 @@ var errNoEntry = errors.New("no previous entry") var log = logging.Logger("ipns-repub") var DefaultRebroadcastInterval = time.Hour * 4 +var InitialRebroadcastDelay = time.Minute * 1 const DefaultRecordLifetime = time.Hour * 24 @@ -57,10 +58,12 @@ func NewRepublisher(r routing.ValueStore, ds ds.Datastore, self ic.PrivKey, ks k func (rp *Republisher) Run(proc goprocess.Process) { tick := time.NewTicker(rp.Interval) defer tick.Stop() + delayCh := time.After(InitialRebroadcastDelay) for { select { - case <-tick.C: + case <-delayCh: + delayCh = tick.C err := rp.republishEntries(proc) if err != nil { log.Error("Republisher failed to republish: ", err) From b76d31c5bb68102810886e4cedcb9244f1075a39 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 29 Nov 2017 15:27:49 -0800 Subject: [PATCH 2034/3817] retry publishing IPNS records every 5 minutes on failure This way, if we *happen* to be offline while attempting a publish, we don't wait the full interval. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@a0ddd0a72812a25cab5d6965eabe18671941227a --- namesys/republisher/repub.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index b7c864cbd..28a873f10 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -28,6 +28,7 @@ var log = logging.Logger("ipns-repub") var DefaultRebroadcastInterval = time.Hour * 4 var InitialRebroadcastDelay = time.Minute * 1 +var FailureRetryInterval = time.Minute * 5 const DefaultRecordLifetime = time.Hour * 24 @@ -56,17 +57,17 @@ func NewRepublisher(r routing.ValueStore, ds ds.Datastore, self ic.PrivKey, ks k } func (rp *Republisher) Run(proc goprocess.Process) { - tick := time.NewTicker(rp.Interval) - defer tick.Stop() - delayCh := time.After(InitialRebroadcastDelay) + timer := time.NewTimer(InitialRebroadcastDelay) + defer timer.Stop() for { select { - case <-delayCh: - delayCh = tick.C + case <-timer.C: + timer.Reset(rp.Interval) err := rp.republishEntries(proc) if err != nil { log.Error("Republisher failed to republish: ", err) + timer.Reset(FailureRetryInterval) } case <-proc.Closing(): return From c126bd313e6c5a6b2326d208f28219660d6e2a38 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 29 Nov 2017 15:46:34 -0800 Subject: [PATCH 2035/3817] document ipns republisher variables (makes code climate happy) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@85da0e65d1d92b55cd09146dfdb57849eefb10d8 --- namesys/republisher/repub.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 28a873f10..e1b0473d8 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -26,10 +26,16 @@ var errNoEntry = errors.New("no previous entry") var log = logging.Logger("ipns-repub") +// DefaultRebroadcastInterval is the default interval at which we rebroadcast IPNS records var DefaultRebroadcastInterval = time.Hour * 4 + +// InitialRebroadcastDelay is the delay before first broadcasting IPNS records on start var InitialRebroadcastDelay = time.Minute * 1 + +// FailureRetryInterval is the interval at which we retry IPNS records broadcasts (when they fail) var FailureRetryInterval = time.Minute * 5 +// DefaultRecordLifetime is the default lifetime for IPNS records const DefaultRecordLifetime = time.Hour * 24 type Republisher struct { From 36b580c1f51076d1d4dc774c9624362f2a8eb0d9 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 29 Nov 2017 15:54:06 -0800 Subject: [PATCH 2036/3817] always obey the IPNS rebroadcast interval if it's smaller License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@a67ebbd2a801005f2eda25f698cbb74e278e217a --- namesys/republisher/repub.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index e1b0473d8..4b1496fe6 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -65,6 +65,9 @@ func NewRepublisher(r routing.ValueStore, ds ds.Datastore, self ic.PrivKey, ks k func (rp *Republisher) Run(proc goprocess.Process) { timer := time.NewTimer(InitialRebroadcastDelay) defer timer.Stop() + if rp.Interval < InitialRebroadcastDelay { + timer.Reset(rp.Interval) + } for { select { @@ -73,7 +76,9 @@ func (rp *Republisher) Run(proc goprocess.Process) { err := rp.republishEntries(proc) if err != nil { log.Error("Republisher failed to republish: ", err) - timer.Reset(FailureRetryInterval) + if FailureRetryInterval < rp.Interval { + timer.Reset(FailureRetryInterval) + } } case <-proc.Closing(): return From 3240e210b45bafc00d63d8a19783e82955618886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 19 Nov 2017 04:32:55 +0100 Subject: [PATCH 2037/3817] gx: Update go-datastore to 1.4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@8df35968bdab1afdb7d2c5481e3855136a020325 --- namesys/namesys.go | 2 +- namesys/namesys_test.go | 4 ++-- namesys/publisher.go | 2 +- namesys/publisher_test.go | 4 ++-- namesys/pubsub.go | 4 ++-- namesys/pubsub_test.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/resolve_test.go | 4 ++-- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 82952f250..cc284fb2c 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -13,10 +13,10 @@ import ( p2phost "gx/ipfs/QmRS46AyqtpJBsf1zmQdeizSDEzo1qkWR7rdEuPFAv8237/go-libp2p-host" mh "gx/ipfs/QmU9a9NV9RdPNwZQDYd5uKsm6N6LJLSvLbywDDYFbaaC6P/go-multihash" floodsub "gx/ipfs/QmVNv1WV6XxzQV4MBuiLX5729wMazaf8TNzm2Sq6ejyHh7/go-libp2p-floodsub" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 78396c25e..66062156d 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,9 +10,9 @@ import ( offroute "github.com/ipfs/go-ipfs/routing/offline" "github.com/ipfs/go-ipfs/unixfs" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) type mockResolver struct { diff --git a/namesys/publisher.go b/namesys/publisher.go index 66214100f..03feb204a 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -16,12 +16,12 @@ import ( routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" record "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record" dhtpb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 28635f3bf..47045669a 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -11,11 +11,11 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" testutil "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) type identity struct { diff --git a/namesys/pubsub.go b/namesys/pubsub.go index 6c1284f27..f6af423c8 100644 --- a/namesys/pubsub.go +++ b/namesys/pubsub.go @@ -19,13 +19,13 @@ import ( u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" mh "gx/ipfs/QmU9a9NV9RdPNwZQDYd5uKsm6N6LJLSvLbywDDYFbaaC6P/go-multihash" floodsub "gx/ipfs/QmVNv1WV6XxzQV4MBuiLX5729wMazaf8TNzm2Sq6ejyHh7/go-libp2p-floodsub" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" record "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record" dhtpb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) // PubsubPublisher is a publisher that distributes IPNS records through pubsub diff --git a/namesys/pubsub_test.go b/namesys/pubsub_test.go index a7fa7be9b..9fab0238b 100644 --- a/namesys/pubsub_test.go +++ b/namesys/pubsub_test.go @@ -15,10 +15,10 @@ import ( p2phost "gx/ipfs/QmRS46AyqtpJBsf1zmQdeizSDEzo1qkWR7rdEuPFAv8237/go-libp2p-host" netutil "gx/ipfs/QmUUNDRYXgfqdjxTg79ogkciczU5y4WY1tKMU2vEX9CRN7/go-libp2p-netutil" floodsub "gx/ipfs/QmVNv1WV6XxzQV4MBuiLX5729wMazaf8TNzm2Sq6ejyHh7/go-libp2p-floodsub" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" bhost "gx/ipfs/Qmb37wDRoh9VZMZXmmZktN35szvj9GeBYDtA9giDmXwwd7/go-libp2p-blankhost" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" ) func newNetHost(ctx context.Context, t *testing.T) p2phost.Host { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 4b1496fe6..ffd12a30e 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -15,11 +15,11 @@ import ( goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ic "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" recpb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 7503ad3d7..649050781 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -10,9 +10,9 @@ import ( mockrouting "github.com/ipfs/go-ipfs/routing/mock" testutil "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) func TestRoutingResolve(t *testing.T) { From a02b31d52830eb52f7926be62753177525998e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 19 Nov 2017 04:32:55 +0100 Subject: [PATCH 2038/3817] gx: Update go-datastore to 1.4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-pinner@efdb0f508f62347d47b89a63f3863c33720609f1 --- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index ce3722aeb..4faaea6f7 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -15,7 +15,7 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index fc303b6f2..3298ef7e0 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -12,8 +12,8 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) func randNode() (*mdag.ProtoNode, *cid.Cid) { diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 856a86a97..d68e79f8a 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -11,8 +11,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" ) func ignoreCids(_ *cid.Cid) {} From b7230121c07b975d5be5a65f517822c74c914232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 19 Nov 2017 04:32:55 +0100 Subject: [PATCH 2039/3817] gx: Update go-datastore to 1.4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-merkledag@bd7f6d450eefc8f4c0e447399b45aed2bb5cda6f --- ipld/merkledag/test/utils.go | 4 ++-- ipld/merkledag/utils/utils.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index 68e257265..89d44b7ee 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -5,8 +5,8 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) func Mock() dag.DAGService { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 6f08bf2fc..790d98dc8 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -11,8 +11,8 @@ import ( path "github.com/ipfs/go-ipfs/path" node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - syncds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + syncds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) type Editor struct { From afb1c758a87da6a880cc599b43705a49f50aaf38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 19 Nov 2017 04:32:55 +0100 Subject: [PATCH 2040/3817] gx: Update go-datastore to 1.4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-mfs@b7193a383a0d03df094af30b803308352dcd6017 --- mfs/mfs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index bebfa8d30..c225a08eb 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -27,8 +27,8 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) func emptyDirNode() *dag.ProtoNode { From cc00df5d85ffab633a1455cbe98d256bbbb31805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 19 Nov 2017 04:32:55 +0100 Subject: [PATCH 2041/3817] gx: Update go-datastore to 1.4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-blockservice@15d7b8a115e4c2c236800d7d97c34166537f451b --- blockservice/blockservice_test.go | 4 ++-- blockservice/test/blocks_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index 1ce735f8b..7d808f516 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -8,8 +8,8 @@ import ( offline "github.com/ipfs/go-ipfs/exchange/offline" "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) func TestWriteThroughWorks(t *testing.T) { diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 59e1b4e91..f59ccf1e5 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -14,8 +14,8 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) func newObject(data []byte) blocks.Block { From c4619300f19d5c8752c8dfe5a4c6510be2dd6d91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 19 Nov 2017 04:32:55 +0100 Subject: [PATCH 2042/3817] gx: Update go-datastore to 1.4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-blockstore@2f8f9452adcc1da360f8371a86fbfc483be1e5c1 --- blockstore/arc_cache.go | 2 +- blockstore/arc_cache_test.go | 4 ++-- blockstore/blockstore.go | 6 +++--- blockstore/blockstore_test.go | 6 +++--- blockstore/bloom_cache_test.go | 6 +++--- blockstore/util/remove.go | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 47ddf2d4c..5062ab197 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -7,8 +7,8 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" ) // arccache wraps a BlockStore with an Adaptive Replacement Cache (ARC) for diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 0232249d3..c73ecfffb 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -7,8 +7,8 @@ import ( "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - syncds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + syncds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) var exampleBlock = blocks.NewBlock([]byte("foo")) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 21bf04612..21f6919c6 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -13,9 +13,9 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dsns "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/namespace" - dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dsns "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/namespace" + dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 8bc2b2f1b..e0e40f3b8 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -11,9 +11,9 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" - ds_sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" + ds_sync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) func TestGetWhenKeyNotPresent(t *testing.T) { diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 4eb8d1aca..21bfaca1b 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -9,9 +9,9 @@ import ( "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" context "context" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" - syncds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" + syncds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) func testBloomCached(ctx context.Context, bs Blockstore) (*bloomcache, error) { diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 467f55092..6fc24f01e 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -6,7 +6,7 @@ import ( "io" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" bs "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/pin" From 495bc37e30d1c7d64d8791951a0aad0546933d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 19 Nov 2017 04:32:55 +0100 Subject: [PATCH 2043/3817] gx: Update go-datastore to 1.4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-routing@e189eacc32c210d1d9a9e912c44df5f84bd5a6dd --- routing/mock/centralized_client.go | 2 +- routing/mock/centralized_server.go | 4 ++-- routing/mock/interface.go | 2 +- routing/offline/offline.go | 2 +- routing/offline/offline_test.go | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 407629299..7006b20e4 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -13,11 +13,11 @@ import ( pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" dhtpb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 02929a4cf..e3544de59 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -10,9 +10,9 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dssync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 5dcbbd21d..cd1da17df 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -11,8 +11,8 @@ import ( "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" ) // Server provides mockrouting Clients diff --git a/routing/offline/offline.go b/routing/offline/offline.go index df2657175..a154d3187 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -10,12 +10,12 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" record "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record" pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" ) var ErrOffline = errors.New("routing system in offline mode") diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index 2b1eb521e..d49c9c851 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -6,7 +6,7 @@ import ( "testing" "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" ) func TestOfflineRouterStorage(t *testing.T) { From bdc02ef7589956f1ccfe64b47080033c9652615e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 19 Nov 2017 04:32:55 +0100 Subject: [PATCH 2044/3817] gx: Update go-datastore to 1.4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-filestore@771a82690d5642860d6b25301d46fb28c9d32369 --- filestore/filestore.go | 2 +- filestore/filestore_test.go | 2 +- filestore/fsrefstore.go | 6 +++--- filestore/util.go | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 9e33f7ed8..095b1018d 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -16,7 +16,7 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" + dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 8f972dec0..e9425aa95 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -12,7 +12,7 @@ import ( posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 9f8ac62f5..881ce8233 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -15,9 +15,9 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dsns "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/namespace" - dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dsns "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/namespace" + dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index 1e4f462b9..985281e23 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -9,8 +9,8 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - dsq "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/query" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" ) // Status is used to identify the state of the block data referenced From 5f315566b1073a405615ac3a7181a78a83d94e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 19 Nov 2017 04:32:55 +0100 Subject: [PATCH 2045/3817] gx: Update go-datastore to 1.4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-ds-help@4809e1e105c9ec9fe618846bf805700938c4fc55 --- datastore/dshelp/key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 21a12c7b4..a176ee0bb 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -2,7 +2,7 @@ package dshelp import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) From a29bc82628eb3350a0715ff11829053f52b1ee33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 19 Nov 2017 04:32:55 +0100 Subject: [PATCH 2046/3817] gx: Update go-datastore to 1.4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-exchange-offline@9939618cfde044d154fd681e22da3d8da4f222e9 --- exchange/offline/offline_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index fce8b613e..9abf97eb7 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -10,8 +10,8 @@ import ( cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore" - ds_sync "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore/sync" + ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + ds_sync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) func TestBlockReturnsErr(t *testing.T) { From 38f74d8431adf5aecd0b99b6b64bc22d3b4c9960 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 18:58:05 -0800 Subject: [PATCH 2047/3817] don't add nodes to DAG twice. Now that we add nodes to the DAG when pinning, don't bother adding them twice. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@9e4a37349f6855d53836a9ea48e6a838b9d19ae4 --- namesys/publisher.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 03feb204a..e98c23916 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,7 +7,6 @@ import ( "fmt" "time" - dag "github.com/ipfs/go-ipfs/merkledag" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" pin "github.com/ipfs/go-ipfs/pin" @@ -321,16 +320,12 @@ func ValidateIpnsRecord(k string, val []byte) error { // InitializeKeyspace sets the ipns record for the given key to // point to an empty directory. // TODO: this doesnt feel like it belongs here -func InitializeKeyspace(ctx context.Context, ds dag.DAGService, pub Publisher, pins pin.Pinner, key ci.PrivKey) error { +func InitializeKeyspace(ctx context.Context, pub Publisher, pins pin.Pinner, key ci.PrivKey) error { emptyDir := ft.EmptyDirNode() - nodek, err := ds.Add(emptyDir) - if err != nil { - return err - } // pin recursively because this might already be pinned // and doing a direct pin would throw an error in that case - err = pins.Pin(ctx, emptyDir, true) + err := pins.Pin(ctx, emptyDir, true) if err != nil { return err } @@ -340,7 +335,7 @@ func InitializeKeyspace(ctx context.Context, ds dag.DAGService, pub Publisher, p return err } - return pub.Publish(ctx, key, path.FromCid(nodek)) + return pub.Publish(ctx, key, path.FromCid(emptyDir.Cid())) } func IpnsKeysForID(id peer.ID) (name, ipns string) { From b7e9594ed2781939379dbe23426dbebb9caec943 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 18:53:02 -0800 Subject: [PATCH 2048/3817] add node to dagserv under lock Otherwise, we could run a GC between adding and pinning. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@d8e8d19bf7bc60315bccfe6383180b6616015ffa --- pinning/pinner/pin.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 4faaea6f7..0e55963b3 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -172,7 +172,10 @@ func NewPinner(dstore ds.Datastore, serv, internal mdag.DAGService) Pinner { func (p *pinner) Pin(ctx context.Context, node node.Node, recurse bool) error { p.lock.Lock() defer p.lock.Unlock() - c := node.Cid() + c, err := p.dserv.Add(node) + if err != nil { + return err + } if recurse { if p.recursePin.Has(c) { From d49a740ee20d6c855a3751c54cf51fda64bf3119 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 4 Dec 2017 10:59:57 -0800 Subject: [PATCH 2049/3817] appease codeclimate License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@0ad237b4cbbdf5ec0b729bb79e09c74760de929e --- unixfs/hamt/hamt.go | 48 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index bd73434cb..034c45ebb 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -501,35 +501,35 @@ func (ds *HamtShard) modifyValue(ctx context.Context, hv *hashBits, key string, child.val = val return nil - } else { - if val == nil { - return os.ErrNotExist - } + } - // replace value with another shard, one level deeper - ns, err := NewHamtShard(ds.dserv, ds.tableSize) - if err != nil { - return err - } - ns.prefix = ds.prefix - chhv := &hashBits{ - b: hash([]byte(child.key)), - consumed: hv.consumed, - } + if val == nil { + return os.ErrNotExist + } - err = ns.modifyValue(ctx, hv, key, val) - if err != nil { - return err - } + // replace value with another shard, one level deeper + ns, err := NewHamtShard(ds.dserv, ds.tableSize) + if err != nil { + return err + } + ns.prefix = ds.prefix + chhv := &hashBits{ + b: hash([]byte(child.key)), + consumed: hv.consumed, + } - err = ns.modifyValue(ctx, chhv, child.key, child.val) - if err != nil { - return err - } + err = ns.modifyValue(ctx, hv, key, val) + if err != nil { + return err + } - ds.setChild(cindex, ns) - return nil + err = ns.modifyValue(ctx, chhv, child.key, child.val) + if err != nil { + return err } + + ds.setChild(cindex, ns) + return nil default: return fmt.Errorf("unexpected type for child: %#v", child) } From 6e7bb94710dd2b73b4a132705c69eba9960c83c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 6 Dec 2017 20:59:34 +0100 Subject: [PATCH 2050/3817] object-patch: Support linking to non-dagpb objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-merkledag@f0dc6dbc69b5f6a335cc53364ffa857dc1814905 --- ipld/merkledag/utils/utils.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 790d98dc8..b976907dc 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -212,8 +212,9 @@ func copyDag(nd *dag.ProtoNode, from, to dag.DAGService) error { } childpb, ok := child.(*dag.ProtoNode) - if !ok { - return dag.ErrNotProtobuf + if !ok { // leaf node + _, err := to.Add(nd) + return err } err = copyDag(childpb, from, to) From 9be3c6c6c8f3f311a96917d7ac3c323e2ae44ab7 Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Sun, 26 Nov 2017 03:17:41 +0100 Subject: [PATCH 2051/3817] namesys: degrade most logging to debug level License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/go-namesys@e1de4367cc2ebcf3fa5968ddc4b1a6f954b5f8d7 --- namesys/base.go | 3 +-- namesys/dns.go | 2 +- namesys/namesys.go | 4 ++-- namesys/routing.go | 10 +++++----- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index c79fbeb94..9953eddc5 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -18,10 +18,9 @@ func resolve(ctx context.Context, r resolver, name string, depth int, prefixes . for { p, err := r.resolveOnce(ctx, name) if err != nil { - log.Warningf("Could not resolve %s", name) return "", err } - log.Debugf("Resolved %s to %s", name, p.String()) + log.Debugf("resolved %s to %s", name, p.String()) if strings.HasPrefix(p.String(), "/ipfs/") { // we've bottomed out with an IPFS path diff --git a/namesys/dns.go b/namesys/dns.go index 1267ef56b..de5c98fdb 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -55,7 +55,7 @@ func (r *DNSResolver) resolveOnce(ctx context.Context, name string) (path.Path, if !isd.IsDomain(domain) { return "", errors.New("not a valid domain name") } - log.Infof("DNSResolver resolving %s", domain) + log.Debugf("DNSResolver resolving %s", domain) rootChan := make(chan lookupRes, 1) go workDomain(r, domain, rootChan) diff --git a/namesys/namesys.go b/namesys/namesys.go index cc284fb2c..f6c8adbc2 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -91,7 +91,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) } segments := strings.SplitN(name, "/", 4) if len(segments) < 3 || segments[0] != "" { - log.Warningf("Invalid name syntax for %s", name) + log.Debugf("invalid name syntax for %s", name) return "", ErrResolveFailed } @@ -153,7 +153,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) return "", ErrResolveFailed } - log.Warningf("No resolver found for %s", name) + log.Debugf("no resolver found for %s", name) return "", ErrResolveFailed } diff --git a/namesys/routing.go b/namesys/routing.go index 19e8b34d3..22ec45a47 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -117,7 +117,7 @@ func (r *routingResolver) ResolveN(ctx context.Context, name string, depth int) // resolveOnce implements resolver. Uses the IPFS routing system to // resolve SFS-like names. func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) { - log.Debugf("RoutingResolve: '%s'", name) + log.Debugf("RoutingResolver resolving %s", name) cached, ok := r.cacheGet(name) if ok { return cached, nil @@ -127,7 +127,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa hash, err := mh.FromB58String(name) if err != nil { // name should be a multihash. if it isn't, error out here. - log.Warningf("RoutingResolve: bad input hash: [%s]\n", name) + log.Debugf("RoutingResolver: bad input hash: [%s]\n", name) return "", err } @@ -143,7 +143,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa ipnsKey := string(h) val, err := r.routing.GetValue(ctx, ipnsKey) if err != nil { - log.Warning("RoutingResolve get failed.") + log.Debugf("RoutingResolver: dht get failed: %s", err) resp <- err return } @@ -179,7 +179,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa // check sig with pk if ok, err := pubkey.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok { - return "", fmt.Errorf("Invalid value. Not signed by PrivateKey corresponding to %v", pubkey) + return "", fmt.Errorf("ipns entry for %s has invalid signature", h) } // ok sig checks out. this is a valid name. @@ -197,7 +197,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa return p, nil } else { // Its an old style multihash record - log.Warning("Detected old style multihash record") + log.Debugf("encountered CIDv0 ipns entry: %s", h) p := path.FromCid(cid.NewCidV0(valh)) r.cacheSet(name, p, entry) return p, nil From 38ea4f273707467d3f08a75083b6de548cf60423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 7 Dec 2017 22:27:20 +0100 Subject: [PATCH 2052/3817] merkledag/utils: switch copyDag to node.Node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-merkledag@a167f17797e9d870874f5faa0a9ac720b3eca792 --- ipld/merkledag/utils/utils.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index b976907dc..5cef1b50e 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -194,7 +194,7 @@ func (e *Editor) Finalize(ds dag.DAGService) (*dag.ProtoNode, error) { return nd, err } -func copyDag(nd *dag.ProtoNode, from, to dag.DAGService) error { +func copyDag(nd node.Node, from, to dag.DAGService) error { _, err := to.Add(nd) if err != nil { return err @@ -211,13 +211,7 @@ func copyDag(nd *dag.ProtoNode, from, to dag.DAGService) error { return err } - childpb, ok := child.(*dag.ProtoNode) - if !ok { // leaf node - _, err := to.Add(nd) - return err - } - - err = copyDag(childpb, from, to) + err = copyDag(child, from, to) if err != nil { return err } From efbab5e602175954bcef811bbbba89340ebfe687 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 21:34:29 -0800 Subject: [PATCH 2053/3817] gx: update go-multihash License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@b5fea556bf3c13bbeed2b0fe72605f0c23b17ae3 --- coreiface/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 78a64dd40..aef86d63a 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -5,8 +5,8 @@ import ( "errors" "io" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - ipld "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + ipld "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) type Path interface { From 8632fa68f62d80c1c3b7ca2f3977ed0b08b21a3f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 21:34:29 -0800 Subject: [PATCH 2054/3817] gx: update go-multihash License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-chunker@2a5b689c9bfd914578286cc09d68d9189bdf072d --- chunker/rabin_test.go | 4 ++-- chunker/splitting_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 9cef888ce..888b9ac2e 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -6,8 +6,8 @@ import ( "io" "testing" - util "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + util "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" + blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" ) func TestRabinChunking(t *testing.T) { diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index a9d3798e6..ff433f048 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -5,7 +5,7 @@ import ( "io" "testing" - u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" ) func randBuf(t *testing.T, size int) []byte { From 1e33e263ecc232a8b3718715a3698fd5113f1155 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 21:34:29 -0800 Subject: [PATCH 2055/3817] gx: update go-multihash License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@5f0190feb7335474468851c45ba98a0e751cfd45 --- unixfs/archive/archive.go | 2 +- unixfs/archive/tar/writer.go | 2 +- unixfs/hamt/hamt.go | 4 ++-- unixfs/io/dagreader.go | 2 +- unixfs/io/dirbuilder.go | 4 ++-- unixfs/io/resolve.go | 2 +- unixfs/mod/dagmodifier.go | 4 ++-- unixfs/mod/dagmodifier_test.go | 2 +- unixfs/test/utils.go | 8 ++++---- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index b913300ef..8061f90dd 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -11,7 +11,7 @@ import ( tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" ) // DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index f00d0422f..360334b79 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -13,7 +13,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" upb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 034c45ebb..fdf722387 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -31,9 +31,9 @@ import ( format "github.com/ipfs/go-ipfs/unixfs" upb "github.com/ipfs/go-ipfs/unixfs/pb" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" "gx/ipfs/QmfJHywXQu98UeZtGJBQrPAR6AtmDjjbe3qjTo9piXHPnx/murmur3" ) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index a7ed7944e..8ec527033 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index f86d23fb7..1ab8cf712 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -8,9 +8,9 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" ) // ShardSplitThreshold specifies how large of an unsharded directory diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index dceba71a7..a66de7bdf 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -7,7 +7,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" ) // ResolveUnixfsOnce resolves a single hop of a path through a graph in a diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 23c1945a5..92fd3f4cc 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -14,9 +14,9 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) var ErrSeekFail = errors.New("failed to seek properly") diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 473d34294..d6ba1ddb8 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -13,7 +13,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" testu "github.com/ipfs/go-ipfs/unixfs/test" - u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" ) func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier, opts testu.NodeOpts) []byte { diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 24359d377..8ea7de6e1 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -15,10 +15,10 @@ import ( mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" - u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - mh "gx/ipfs/QmU9a9NV9RdPNwZQDYd5uKsm6N6LJLSvLbywDDYFbaaC6P/go-multihash" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" + mh "gx/ipfs/QmYeKnKpubCMRiq3PGZcTREErthbb5Q9cXsCoSkD9bjEBd/go-multihash" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) func SizeSplitterGen(size int64) chunk.SplitterGen { From 99a2ed8b0f2f144481baae958f8ce6e205e048a7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 21:34:29 -0800 Subject: [PATCH 2056/3817] gx: update go-multihash License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@b91d6098e0baf15df89a9088e970273a1b850052 --- mfs/dir.go | 4 ++-- mfs/file.go | 2 +- mfs/mfs_test.go | 6 +++--- mfs/ops.go | 4 ++-- mfs/repub_test.go | 4 ++-- mfs/system.go | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 219dc4cce..57e4554f0 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -15,8 +15,8 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) var ErrNotYetImplemented = errors.New("not yet implemented") diff --git a/mfs/file.go b/mfs/file.go index 0ff8b41de..363026886 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index c225a08eb..6498a7615 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -24,11 +24,11 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" - u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) func emptyDirNode() *dag.ProtoNode { diff --git a/mfs/ops.go b/mfs/ops.go index 49ce398d4..214a66fca 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -9,8 +9,8 @@ import ( path "github.com/ipfs/go-ipfs/path" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) // Mv moves the file or directory at 'src' to 'dst' diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 45b9006b4..60045fec5 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - ci "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil/ci" + ci "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil/ci" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index fc5be0f6e..f0dd09958 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -19,9 +19,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) var ErrNotExist = errors.New("no such rootfs") From 1ee4bdd150a0b944ec6df4465af3741535ce0f67 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 21:34:29 -0800 Subject: [PATCH 2057/3817] gx: update go-multihash License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@96db6fa79cda8cb18fda63ce7d613daff1550fd8 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index c9e6cac70..f3b98597b 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -9,9 +9,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 0e55963b3..dc2eee7f2 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -12,10 +12,10 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" dutils "github.com/ipfs/go-ipfs/merkledag/utils" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 3298ef7e0..f34283b79 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -10,10 +10,10 @@ import ( "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) func randNode() (*mdag.ProtoNode, *cid.Cid) { diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index c191744fd..116e62297 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -12,9 +12,9 @@ import ( "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index d68e79f8a..1ee16ed38 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -10,9 +10,9 @@ import ( offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) func ignoreCids(_ *cid.Cid) {} From 74f6ea40d22641ea904224a624db60087c8fde8c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 21:34:29 -0800 Subject: [PATCH 2058/3817] gx: update go-multihash License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@8b810ae1865bedd8cf3d29a1148d53fd1638dff6 --- blockservice/blockservice.go | 4 ++-- blockservice/blockservice_test.go | 2 +- blockservice/test/blocks_test.go | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 6bd04c3ab..7929a67f3 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -12,9 +12,9 @@ import ( exchange "github.com/ipfs/go-ipfs/exchange" bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) var log = logging.Logger("blockservice") diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index 7d808f516..00d9ccabf 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" butil "github.com/ipfs/go-ipfs/blocks/blocksutil" offline "github.com/ipfs/go-ipfs/exchange/offline" - "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index f59ccf1e5..da54461d6 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -10,12 +10,12 @@ import ( blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) func newObject(data []byte) blocks.Block { From 5b168b444987b02e84fd45fb0c84bcd118324b09 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 21:34:29 -0800 Subject: [PATCH 2059/3817] gx: update go-multihash License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-blockstore@ce76423d55ce7315b6f1ef7e20e9dc8cf0f67e3d --- blockstore/arc_cache.go | 4 ++-- blockstore/arc_cache_test.go | 4 ++-- blockstore/blockstore.go | 4 ++-- blockstore/blockstore_test.go | 6 +++--- blockstore/bloom_cache.go | 4 ++-- blockstore/bloom_cache_test.go | 2 +- blockstore/util/remove.go | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 5062ab197..da3b6d7a2 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -3,12 +3,12 @@ package blockstore import ( "context" - "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) // arccache wraps a BlockStore with an Adaptive Replacement Cache (ARC) for diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index c73ecfffb..734f1e73d 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -4,11 +4,11 @@ import ( "context" "testing" - "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" syncds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) var exampleBlock = blocks.NewBlock([]byte("foo")) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 21f6919c6..4ef59ffe3 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -9,13 +9,13 @@ import ( "sync/atomic" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" dsns "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/namespace" dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index e0e40f3b8..31e75acf6 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -7,13 +7,13 @@ import ( "testing" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" ds_sync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) func TestGetWhenKeyNotPresent(t *testing.T) { diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 79a4596e0..b06f56c11 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -5,11 +5,11 @@ import ( "sync/atomic" "time" - "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" bloom "gx/ipfs/QmXqKGu7QzfRzFC4yd5aL9sThYx22vY163VGwmxfp5qGHk/bbloom" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) // bloomCached returns a Blockstore that caches Has requests using a Bloom diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 21bfaca1b..f2f1666fe 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" context "context" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 6fc24f01e..94b52254e 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -5,8 +5,8 @@ import ( "fmt" "io" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" bs "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/pin" From 06ddf1138054c99ac4ec166224612042efca4b88 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 21:34:29 -0800 Subject: [PATCH 2060/3817] gx: update go-multihash License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@30054b1b9c5aba630bcf5e852e6f60a40abef9c7 --- ipld/merkledag/batch.go | 6 +++--- ipld/merkledag/coding.go | 6 +++--- ipld/merkledag/merkledag.go | 6 +++--- ipld/merkledag/merkledag_test.go | 8 ++++---- ipld/merkledag/node.go | 6 +++--- ipld/merkledag/node_test.go | 2 +- ipld/merkledag/raw.go | 8 ++++---- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/traverse/traverse_test.go | 2 +- ipld/merkledag/utils/diff.go | 4 ++-- ipld/merkledag/utils/diffenum.go | 4 ++-- ipld/merkledag/utils/diffenum_test.go | 4 ++-- ipld/merkledag/utils/utils.go | 2 +- ipld/merkledag/utils/utils_test.go | 2 +- 14 files changed, 31 insertions(+), 31 deletions(-) diff --git a/ipld/merkledag/batch.go b/ipld/merkledag/batch.go index b6dd286b9..79c86aa9a 100644 --- a/ipld/merkledag/batch.go +++ b/ipld/merkledag/batch.go @@ -3,9 +3,9 @@ package merkledag import ( "runtime" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" - blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) // ParallelBatchCommits is the number of batch commits that can be in-flight before blocking. diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 06644d912..dec46f168 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -5,12 +5,12 @@ import ( "sort" "strings" - "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" pb "github.com/ipfs/go-ipfs/merkledag/pb" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index bcf0e84a1..7b85bf008 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -9,9 +9,9 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" - ipldcbor "gx/ipfs/QmWCs8kMecJwCPK8JThue8TjgM2ieJ2HjTLDu7Cv2NEmZi/go-ipld-cbor" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + ipldcbor "gx/ipfs/QmeZv9VXw2SfVbX55LV6kGTWASKBc9ZxAVqGBeJcDGdoXy/go-ipld-cbor" ) // TODO: We should move these registrations elsewhere. Really, most of the IPLD diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 5caabe94b..a1fd02d81 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -22,11 +22,11 @@ import ( mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" - blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" - u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) func TestNode(t *testing.T) { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index ad021fa30..8dd7fed3d 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -5,9 +5,9 @@ import ( "encoding/json" "fmt" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" - mh "gx/ipfs/QmU9a9NV9RdPNwZQDYd5uKsm6N6LJLSvLbywDDYFbaaC6P/go-multihash" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + mh "gx/ipfs/QmYeKnKpubCMRiq3PGZcTREErthbb5Q9cXsCoSkD9bjEBd/go-multihash" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index e8520bcd3..72b65c289 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -8,7 +8,7 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" ) func TestRemoveLink(t *testing.T) { diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index e73fb88ba..9ef9e4da8 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -2,11 +2,11 @@ package merkledag import ( "fmt" - "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" - u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) type RawNode struct { diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index 5059a83db..2759d0269 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -5,7 +5,7 @@ import ( "context" "errors" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" ) // Order is an identifier for traversal algorithm orders diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 3411d2e44..30f9c524d 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -8,7 +8,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" ) func TestDFSPreNoSkip(t *testing.T) { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index e5d891203..1114d18d5 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -7,8 +7,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) const ( diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go index c2904cbc0..e63ce985e 100644 --- a/ipld/merkledag/utils/diffenum.go +++ b/ipld/merkledag/utils/diffenum.go @@ -6,8 +6,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) // DiffEnumerate fetches every object in the graph pointed to by 'to' that is diff --git a/ipld/merkledag/utils/diffenum_test.go b/ipld/merkledag/utils/diffenum_test.go index bf7d38d42..72f6ea23f 100644 --- a/ipld/merkledag/utils/diffenum_test.go +++ b/ipld/merkledag/utils/diffenum_test.go @@ -8,8 +8,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) func buildNode(name string, desc map[string]ndesc, out map[string]node.Node) node.Node { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 5cef1b50e..de782536d 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -10,7 +10,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" syncds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 0c3d77199..185aadf04 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -8,7 +8,7 @@ import ( mdtest "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) func TestAddLink(t *testing.T) { From 872fc007660901d824fd7e2b0638c3ca6c79a0e4 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 21:34:29 -0800 Subject: [PATCH 2061/3817] gx: update go-multihash License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@7d6cd1c45f81cc8c03c4aac2fb50a137cf564606 --- namesys/ipns_select_test.go | 2 +- namesys/namesys.go | 10 +++++----- namesys/publisher.go | 10 +++++----- namesys/publisher_test.go | 6 +++--- namesys/pubsub.go | 20 ++++++++++---------- namesys/pubsub_test.go | 16 ++++++++-------- namesys/republisher/repub.go | 6 +++--- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 4 ++-- namesys/routing.go | 8 ++++---- 10 files changed, 43 insertions(+), 43 deletions(-) diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index 9c0f34114..e36eade00 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -9,7 +9,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) diff --git a/namesys/namesys.go b/namesys/namesys.go index f6c8adbc2..7f7adec1f 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -9,11 +9,11 @@ import ( path "github.com/ipfs/go-ipfs/path" - routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" - p2phost "gx/ipfs/QmRS46AyqtpJBsf1zmQdeizSDEzo1qkWR7rdEuPFAv8237/go-libp2p-host" - mh "gx/ipfs/QmU9a9NV9RdPNwZQDYd5uKsm6N6LJLSvLbywDDYFbaaC6P/go-multihash" - floodsub "gx/ipfs/QmVNv1WV6XxzQV4MBuiLX5729wMazaf8TNzm2Sq6ejyHh7/go-libp2p-floodsub" - peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + floodsub "gx/ipfs/QmP1T1SGU6276R2MHKP2owbck37Fnzd6ZkpyNJvnG2LoTG/go-libp2p-floodsub" + p2phost "gx/ipfs/QmP46LGWhzVZTMmt5akNNLfoV8qL4h5wTwmzQxLyDafggd/go-libp2p-host" + routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" + peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" + mh "gx/ipfs/QmYeKnKpubCMRiq3PGZcTREErthbb5Q9cXsCoSkD9bjEBd/go-multihash" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" diff --git a/namesys/publisher.go b/namesys/publisher.go index e98c23916..bdfbe5e3b 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -13,13 +13,13 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ft "github.com/ipfs/go-ipfs/unixfs" - routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" - u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" + u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" + record "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record" + dhtpb "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record/pb" + peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - record "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record" - dhtpb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 47045669a..37e3aeeae 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -10,12 +10,12 @@ import ( mockrouting "github.com/ipfs/go-ipfs/routing/mock" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - testutil "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" - ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr" - peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + ma "gx/ipfs/QmW8s4zTsUoX1Q6CeYxVKPyqSKbF7H1YDUyTostBtZ8DaG/go-multiaddr" + peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" + testutil "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" ) type identity struct { diff --git a/namesys/pubsub.go b/namesys/pubsub.go index f6af423c8..676056852 100644 --- a/namesys/pubsub.go +++ b/namesys/pubsub.go @@ -12,20 +12,20 @@ import ( path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" - pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - p2phost "gx/ipfs/QmRS46AyqtpJBsf1zmQdeizSDEzo1qkWR7rdEuPFAv8237/go-libp2p-host" - u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" - mh "gx/ipfs/QmU9a9NV9RdPNwZQDYd5uKsm6N6LJLSvLbywDDYFbaaC6P/go-multihash" - floodsub "gx/ipfs/QmVNv1WV6XxzQV4MBuiLX5729wMazaf8TNzm2Sq6ejyHh7/go-libp2p-floodsub" - peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + floodsub "gx/ipfs/QmP1T1SGU6276R2MHKP2owbck37Fnzd6ZkpyNJvnG2LoTG/go-libp2p-floodsub" + p2phost "gx/ipfs/QmP46LGWhzVZTMmt5akNNLfoV8qL4h5wTwmzQxLyDafggd/go-libp2p-host" + routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" + u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" + record "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record" + dhtpb "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record/pb" + peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" + mh "gx/ipfs/QmYeKnKpubCMRiq3PGZcTREErthbb5Q9cXsCoSkD9bjEBd/go-multihash" + pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - record "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record" - dhtpb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) // PubsubPublisher is a publisher that distributes IPNS records through pubsub diff --git a/namesys/pubsub_test.go b/namesys/pubsub_test.go index 9fab0238b..c82a425db 100644 --- a/namesys/pubsub_test.go +++ b/namesys/pubsub_test.go @@ -9,16 +9,16 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" - routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" - pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - testutil "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" - p2phost "gx/ipfs/QmRS46AyqtpJBsf1zmQdeizSDEzo1qkWR7rdEuPFAv8237/go-libp2p-host" - netutil "gx/ipfs/QmUUNDRYXgfqdjxTg79ogkciczU5y4WY1tKMU2vEX9CRN7/go-libp2p-netutil" - floodsub "gx/ipfs/QmVNv1WV6XxzQV4MBuiLX5729wMazaf8TNzm2Sq6ejyHh7/go-libp2p-floodsub" - peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + floodsub "gx/ipfs/QmP1T1SGU6276R2MHKP2owbck37Fnzd6ZkpyNJvnG2LoTG/go-libp2p-floodsub" + p2phost "gx/ipfs/QmP46LGWhzVZTMmt5akNNLfoV8qL4h5wTwmzQxLyDafggd/go-libp2p-host" + routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" + peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" + pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" + bhost "gx/ipfs/QmYmhgAcvmDGXct1qBvc1kz9BxQSit1XBrTeiGZp2FvRyn/go-libp2p-blankhost" + netutil "gx/ipfs/QmZTcPxK6VqrwY94JpKZPvEqAZ6tEr1rLrpcqJbbRZbg2V/go-libp2p-netutil" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - bhost "gx/ipfs/Qmb37wDRoh9VZMZXmmZktN35szvj9GeBYDtA9giDmXwwd7/go-libp2p-blankhost" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + testutil "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" ) func newNetHost(ctx context.Context, t *testing.T) p2phost.Host { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index ffd12a30e..d871aaca3 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,14 +11,14 @@ import ( path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" + routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + recpb "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record/pb" + peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ic "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - recpb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 43c9c1f5e..441b7cce2 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -12,9 +12,9 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmTzs3Gp2rU3HuNayjBVG7qBgbaKWE8bgtwJ7faRxAe9UP/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" + mocknet "gx/ipfs/Qma23bpHwQrQyvKeBemaeJh7sAoRHggPkgnge1B9489ff5/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 649050781..9c1f83816 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,9 +8,9 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" - testutil "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" + testutil "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" - peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) diff --git a/namesys/routing.go b/namesys/routing.go index 22ec45a47..cd6ee7c4b 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -9,14 +9,14 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" - u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" + u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - mh "gx/ipfs/QmU9a9NV9RdPNwZQDYd5uKsm6N6LJLSvLbywDDYFbaaC6P/go-multihash" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" + mh "gx/ipfs/QmYeKnKpubCMRiq3PGZcTREErthbb5Q9cXsCoSkD9bjEBd/go-multihash" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) var log = logging.Logger("namesys") From ee36ce753bab98788b884d3969aa797063e1172a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 21:34:29 -0800 Subject: [PATCH 2062/3817] gx: update go-multihash License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-path@e46eb708290007ceaf819a1174d0ce6fa1b00c4a --- path/path.go | 2 +- path/resolver.go | 4 ++-- path/resolver_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/path/path.go b/path/path.go index 85e163a25..9c4d8f135 100644 --- a/path/path.go +++ b/path/path.go @@ -5,7 +5,7 @@ import ( "path" "strings" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) // ErrBadPath is returned when a given path is incorrectly formatted diff --git a/path/resolver.go b/path/resolver.go index 094bc755e..c4aabe1b7 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -9,9 +9,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) var log = logging.Logger("path") diff --git a/path/resolver_test.go b/path/resolver_test.go index 200fe1f37..533932814 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -9,8 +9,8 @@ import ( dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" - util "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + util "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" ) func randNode() *merkledag.ProtoNode { From 80b1e90338a91053e4f8d6370e4bdcc102e1378d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 21:34:29 -0800 Subject: [PATCH 2063/3817] gx: update go-multihash License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@1ff01d3f89b31d1cf3e714587d4bbc48960b29f3 --- routing/mock/centralized_client.go | 16 ++++++++-------- routing/mock/centralized_server.go | 8 ++++---- routing/mock/centralized_test.go | 8 ++++---- routing/mock/interface.go | 6 +++--- routing/none/none_client.go | 10 +++++----- routing/offline/offline.go | 12 ++++++------ routing/offline/offline_test.go | 2 +- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 7006b20e4..175a9d436 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -6,18 +6,18 @@ import ( "time" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" + "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" - pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" + u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr" - peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + ma "gx/ipfs/QmW8s4zTsUoX1Q6CeYxVKPyqSKbF7H1YDUyTostBtZ8DaG/go-multiaddr" + dhtpb "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record/pb" + peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" + pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dhtpb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index e3544de59..1a3d6ef37 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -6,13 +6,13 @@ import ( "sync" "time" - "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" + "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" + pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 9ecba1181..917b71afb 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -6,11 +6,11 @@ import ( "time" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" + "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" + pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index cd1da17df..3aed934ce 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -8,10 +8,10 @@ import ( "context" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" + "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" - routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" - peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" + peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" ) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index ccf535fca..64f9893b0 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,11 +6,11 @@ import ( repo "github.com/ipfs/go-ipfs/repo" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" - pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - p2phost "gx/ipfs/QmRS46AyqtpJBsf1zmQdeizSDEzo1qkWR7rdEuPFAv8237/go-libp2p-host" - peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + p2phost "gx/ipfs/QmP46LGWhzVZTMmt5akNNLfoV8qL4h5wTwmzQxLyDafggd/go-libp2p-host" + routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" + peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" + pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) type nilclient struct { diff --git a/routing/offline/offline.go b/routing/offline/offline.go index a154d3187..a1d58f092 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,15 +7,15 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - routing "gx/ipfs/QmPR2JzfKd9poHx9XBhzoFeBBC31ZM3W5iUPKJZWyaoZZm/go-libp2p-routing" - pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore" - "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" + routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" + record "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record" + pb "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record/pb" + "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" + pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - record "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record" - pb "gx/ipfs/QmbxkgUceEcuSZ4ZdBA3x74VUDSSYjHYmmeEqkjxbtZ6Jg/go-libp2p-record/pb" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) var ErrOffline = errors.New("routing system in offline mode") diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index d49c9c851..e0da0d2ef 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -5,8 +5,8 @@ import ( "context" "testing" - "gx/ipfs/QmQgLZP9haZheimMHqqAjJh2LhRmNfEoZDfbtkpeMhi9xK/go-testutil" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" ) func TestOfflineRouterStorage(t *testing.T) { From 2d886dab72a0ac9dfc3a0491a74088d8a907d918 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 21:34:29 -0800 Subject: [PATCH 2064/3817] gx: update go-multihash License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@3ba2c9f70a3eafef77a375e6c295314191db2d4a --- filestore/filestore.go | 4 ++-- filestore/filestore_test.go | 2 +- filestore/fsrefstore.go | 4 ++-- filestore/util.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 095b1018d..502b11fed 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -12,11 +12,11 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" - "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index e9425aa95..13a95ee5f 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -11,8 +11,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 881ce8233..b77990073 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -11,13 +11,13 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" - "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" dsns "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/namespace" dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index 985281e23..932061be3 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -8,9 +8,9 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) // Status is used to identify the state of the block data referenced From bf4886933cd1d3708c2c036d47d39611ec10a964 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 21:34:29 -0800 Subject: [PATCH 2065/3817] gx: update go-multihash License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-ds-help@dccc9977ef0be890dbad443da03fbb6fb18af9e8 --- datastore/dshelp/key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index a176ee0bb..072d4fa9a 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -1,8 +1,8 @@ package dshelp import ( - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) From 9eb5ec46865a1cde749b6af24a571933ca4815fb Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 21:34:29 -0800 Subject: [PATCH 2066/3817] gx: update go-multihash License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-exchange-offline@dbbdca60e030eae6b3a6a4db2816d4310b8cf22b --- exchange/offline/offline.go | 4 ++-- exchange/offline/offline_test.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index fed9c2d87..f104d20b1 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -7,9 +7,9 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 9abf97eb7..681ea2328 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -6,12 +6,12 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" - blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" - u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util" + u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" ds_sync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) func TestBlockReturnsErr(t *testing.T) { From 4deb98bb0c4065ab512331567e62944b30e81d23 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 3 Dec 2017 21:34:29 -0800 Subject: [PATCH 2067/3817] gx: update go-multihash License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-exchange-interface@10c75eed681e86ba6dc34491d59f06ace43c5a9f --- exchange/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index 4fe8e0f3d..443672d89 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -5,9 +5,9 @@ import ( "context" "io" - blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format" + blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" - cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) // Any type that implements exchange.Interface may be used as an IPFS block From 9cc221d2ae772fe67425e873db64516bf1bf1f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 15 Dec 2017 01:34:49 +0100 Subject: [PATCH 2068/3817] docs/coreapi: Add some documentation to CoreAPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@2fbdcd09c87e05ff355b4a492b01029273b1f0f3 --- coreiface/interface.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/coreiface/interface.go b/coreiface/interface.go index 78a64dd40..87e9fcd0f 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -1,3 +1,5 @@ +// Package iface defines IPFS Core API which is a set of interfaces used to +// interact with IPFS nodes. package iface import ( @@ -9,6 +11,8 @@ import ( ipld "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format" ) +// Path is a generic wrapper for paths used in the API. A path can be resolved +// to a CID using one of Resolve functions in the API. type Path interface { String() string Cid() *cid.Cid @@ -26,15 +30,28 @@ type Reader interface { io.Closer } +// CoreAPI defines an unified interface to IPFS for Go programs. type CoreAPI interface { + // Unixfs returns an implementation of Unixfs API Unixfs() UnixfsAPI + + // ResolvePath resolves the path using Unixfs resolver ResolvePath(context.Context, Path) (Path, error) + + // ResolveNode resolves the path (if not resolved already) using Unixfs + // resolver, gets and returns the resolved Node ResolveNode(context.Context, Path) (Node, error) } +// UnixfsAPI is the basic interface to immutable files in IPFS type UnixfsAPI interface { + // Add imports the data from the reader into merkledag file Add(context.Context, io.Reader) (Path, error) + + // Cat returns a reader for the file Cat(context.Context, Path) (Reader, error) + + // Ls returns the list of links in a directory Ls(context.Context, Path) ([]*Link, error) } From 3a9d6a18c6a68c0cc91db2f56d040706e9172fc0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 18 Dec 2017 10:37:12 -0800 Subject: [PATCH 2069/3817] switch to a faster b58 library and update go-multihash This commit was moved from ipfs/go-ipfs-util@a5ec80aed64a2438aee9a0a4a4c1a9140c0db273 --- util/util.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/util/util.go b/util/util.go index fb4dd9828..8ebe3c706 100644 --- a/util/util.go +++ b/util/util.go @@ -12,7 +12,7 @@ import ( "strings" "time" - b58 "github.com/jbenet/go-base58" + b58 "github.com/mr-tron/base58/base58" mh "github.com/multiformats/go-multihash" ) @@ -140,15 +140,12 @@ func Hash(data []byte) mh.Multihash { // IsValidHash checks whether a given hash is valid (b58 decodable, len > 0) func IsValidHash(s string) bool { - out := b58.Decode(s) - if out == nil || len(out) == 0 { - return false - } - _, err := mh.Cast(out) + out, err := b58.Decode(s) if err != nil { return false } - return true + _, err = mh.Cast(out) + return err == nil } // XOR takes two byte slices, XORs them together, returns the resulting slice. From f92658b17920f3c022e2e7cf6e6a7acaa00f8ebb Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 19 Dec 2017 14:23:31 -0800 Subject: [PATCH 2070/3817] Don't waste 256KiB buffers on small chunks. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-chunker@73797622845836601496e64ba3481b207d53d4bc --- chunker/splitting.go | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 6fd55e22d..ddd71e969 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -5,6 +5,7 @@ import ( "io" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + mpool "gx/ipfs/QmWBug6eBS7AxRdCDVuSY5CnSit7cS2XnPFYJWqWDumhCG/go-msgio/mpool" ) var log = logging.Logger("chunk") @@ -51,14 +52,14 @@ func Chan(s Splitter) (<-chan []byte, <-chan error) { type sizeSplitterv2 struct { r io.Reader - size int64 + size uint32 err error } func NewSizeSplitter(r io.Reader, size int64) Splitter { return &sizeSplitterv2{ r: r, - size: size, + size: uint32(size), } } @@ -66,17 +67,22 @@ func (ss *sizeSplitterv2) NextBytes() ([]byte, error) { if ss.err != nil { return nil, ss.err } - buf := make([]byte, ss.size) - n, err := io.ReadFull(ss.r, buf) - if err == io.ErrUnexpectedEOF { + + full := mpool.ByteSlicePool.Get(ss.size).([]byte)[:ss.size] + n, err := io.ReadFull(ss.r, full) + switch err { + case io.ErrUnexpectedEOF: ss.err = io.EOF - err = nil - } - if err != nil { + small := make([]byte, n) + copy(small, full) + mpool.ByteSlicePool.Put(ss.size, full) + return small, nil + case nil: + return full, nil + default: + mpool.ByteSlicePool.Put(ss.size, full) return nil, err } - - return buf[:n], nil } func (ss *sizeSplitterv2) Reader() io.Reader { From 40dd4d8df955c6e62dd77e7cf76a27d93f909d2b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 19 Dec 2017 15:49:33 -0800 Subject: [PATCH 2071/3817] use DefaultSplitter function where appropriate License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-chunker@03fd98e790e23947354096a6693cdd7177570125 --- chunker/parse.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/parse.go b/chunker/parse.go index 55e96cc04..f4cc56290 100644 --- a/chunker/parse.go +++ b/chunker/parse.go @@ -11,7 +11,7 @@ import ( func FromString(r io.Reader, chunker string) (Splitter, error) { switch { case chunker == "" || chunker == "default": - return NewSizeSplitter(r, DefaultBlockSize), nil + return DefaultSplitter(r), nil case strings.HasPrefix(chunker, "size-"): sizeStr := strings.Split(chunker, "-")[1] From 12599113d0e6eac51dc5fe6096bbcdf51438b14b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 19 Dec 2017 19:29:49 -0800 Subject: [PATCH 2072/3817] add test for overallocation in chunker License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-chunker@9d0e6bcff5c2f577e761aba9548b159336a8d631 --- chunker/splitting_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index ff433f048..68df42803 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -22,6 +22,20 @@ func copyBuf(buf []byte) []byte { return cpy } +func TestSizeSplitterOverAllocate(t *testing.T) { + max := 1000 + r := bytes.NewReader(randBuf(t, max)) + chunksize := int64(1024 * 256) + splitter := NewSizeSplitter(r, chunksize) + chunk, err := splitter.NextBytes() + if err != nil { + t.Fatal(err) + } + if cap(chunk) > len(chunk) { + t.Fatal("chunk capacity too large") + } +} + func TestSizeSplitterIsDeterministic(t *testing.T) { if testing.Short() { t.SkipNow() From b351a5a73c7db7a1dbbbffe546f76563c91c2fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 8 Dec 2017 22:51:35 +0100 Subject: [PATCH 2073/3817] coreapi: DAG API proposal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@c6015845cbf43a2b9fc83d749137bee10f9d6396 --- coreiface/interface.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index b753c4184..508588a04 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -34,13 +34,14 @@ type Reader interface { type CoreAPI interface { // Unixfs returns an implementation of Unixfs API Unixfs() UnixfsAPI + Dag() DagAPI // ResolvePath resolves the path using Unixfs resolver ResolvePath(context.Context, Path) (Path, error) // ResolveNode resolves the path (if not resolved already) using Unixfs // resolver, gets and returns the resolved Node - ResolveNode(context.Context, Path) (Node, error) + ResolveNode(context.Context, Path) (Node, error) //TODO: should this get dropped in favor of DagAPI.Get? } // UnixfsAPI is the basic interface to immutable files in IPFS @@ -55,6 +56,12 @@ type UnixfsAPI interface { Ls(context.Context, Path) ([]*Link, error) } +type DagAPI interface { + Put(ctx context.Context, src io.Reader, inputEnc string, format *cid.Prefix) ([]Node, error) + Get(ctx context.Context, path Path) (Node, error) + Tree(ctx context.Context, path Path, depth int) ([]Path, error) +} + // type ObjectAPI interface { // New() (cid.Cid, Object) // Get(string) (Object, error) From b0b4e68ff7103e39dd2698db9877b96978bc3ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 11 Dec 2017 17:22:21 +0100 Subject: [PATCH 2074/3817] coreapi: add tests for dag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@c593c49d857c407d9e3b6a5576f2cdb5a1fe1e97 --- coreiface/interface.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 508588a04..ecccc1c64 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -41,7 +41,7 @@ type CoreAPI interface { // ResolveNode resolves the path (if not resolved already) using Unixfs // resolver, gets and returns the resolved Node - ResolveNode(context.Context, Path) (Node, error) //TODO: should this get dropped in favor of DagAPI.Get? + ResolveNode(context.Context, Path) (Node, error) } // UnixfsAPI is the basic interface to immutable files in IPFS @@ -56,9 +56,17 @@ type UnixfsAPI interface { Ls(context.Context, Path) ([]*Link, error) } +// DagAPI specifies the interface to IPLD type DagAPI interface { - Put(ctx context.Context, src io.Reader, inputEnc string, format *cid.Prefix) ([]Node, error) + // Put inserts data using specified format and input encoding. + // If format is not specified (nil), default dag-cbor/sha256 is used + Put(ctx context.Context, src io.Reader, inputEnc string, format *cid.Prefix) ([]Node, error) //TODO: make format optional + + // Get attempts to resolve and get the node specified by the path Get(ctx context.Context, path Path) (Node, error) + + // Tree returns list of paths within a node specified by the path. + // To get all paths in a tree, set depth to -1 Tree(ctx context.Context, path Path, depth int) ([]Path, error) } From 9901857d2d284da55025a64aacf72d6ac9c89b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 17 Dec 2017 03:23:50 +0100 Subject: [PATCH 2075/3817] coreapi: functional options for DagAPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@39f8afada852b5189996725ccde10b6d64f1b5ab --- coreiface/interface.go | 25 ++++++++++-- coreiface/options/dag.go | 83 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 coreiface/options/dag.go diff --git a/coreiface/interface.go b/coreiface/interface.go index ecccc1c64..f43700e9c 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -7,6 +7,8 @@ import ( "errors" "io" + options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + ipld "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) @@ -60,14 +62,31 @@ type UnixfsAPI interface { type DagAPI interface { // Put inserts data using specified format and input encoding. // If format is not specified (nil), default dag-cbor/sha256 is used - Put(ctx context.Context, src io.Reader, inputEnc string, format *cid.Prefix) ([]Node, error) //TODO: make format optional + Put(ctx context.Context, src io.Reader, opts ...options.DagPutOption) ([]Node, error) + + // WithInputEnc is an option for Put which specifies the input encoding of the + // data. Default is "json", most formats/codecs support "raw" + WithInputEnc(enc string) options.DagPutOption + + // WithCodec is an option for Put which specifies the multicodec to use to + // serialize the object. Default is cid.DagCBOR (0x71) + WithCodec(codec uint64) options.DagPutOption + + // WithHash is an option for Put which specifies the multihash settings to use + // when hashing the object. Default is based on the codec used + // (mh.SHA2_256 (0x12) for DagCBOR). If mhLen is set to -1, default length for + // the hash will be used + WithHash(mhType uint64, mhLen int) options.DagPutOption // Get attempts to resolve and get the node specified by the path Get(ctx context.Context, path Path) (Node, error) // Tree returns list of paths within a node specified by the path. - // To get all paths in a tree, set depth to -1 - Tree(ctx context.Context, path Path, depth int) ([]Path, error) + Tree(ctx context.Context, path Path, opts ...options.DagTreeOption) ([]Path, error) + + // WithDepth is an option for Tree which specifies maximum depth of the + // returned tree. Default is -1 (no depth limit) + WithDepth(depth int) options.DagTreeOption } // type ObjectAPI interface { diff --git a/coreiface/options/dag.go b/coreiface/options/dag.go new file mode 100644 index 000000000..7850c4bc3 --- /dev/null +++ b/coreiface/options/dag.go @@ -0,0 +1,83 @@ +package options + +import ( + "math" + + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" +) + +type DagPutSettings struct { + InputEnc string + Codec uint64 + MhType uint64 + MhLength int +} + +type DagTreeSettings struct { + Depth int +} + +type DagPutOption func(*DagPutSettings) error +type DagTreeOption func(*DagTreeSettings) error + +func DagPutOptions(opts ...DagPutOption) (*DagPutSettings, error) { + options := &DagPutSettings{ + InputEnc: "json", + Codec: cid.DagCBOR, + MhType: math.MaxUint64, + MhLength: -1, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +func DagTreeOptions(opts ...DagTreeOption) (*DagTreeSettings, error) { + options := &DagTreeSettings{ + Depth: -1, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type DagOptions struct{} + +func (api *DagOptions) WithInputEnc(enc string) DagPutOption { + return func(settings *DagPutSettings) error { + settings.InputEnc = enc + return nil + } +} + +func (api *DagOptions) WithCodec(codec uint64) DagPutOption { + return func(settings *DagPutSettings) error { + settings.Codec = codec + return nil + } +} + +func (api *DagOptions) WithHash(mhType uint64, mhLen int) DagPutOption { + return func(settings *DagPutSettings) error { + settings.MhType = mhType + settings.MhLength = mhLen + return nil + } +} + +func (api *DagOptions) WithDepth(depth int) DagTreeOption { + return func(settings *DagTreeSettings) error { + settings.Depth = depth + return nil + } +} From bae5f5303ec03d6399c3345ba6c14397812b1ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 21 Dec 2017 01:56:41 +0100 Subject: [PATCH 2076/3817] coreapi: dag review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@20fa7a599e839af20bef873f12fcc2f94fd0c529 --- coreiface/interface.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index f43700e9c..e51888e60 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -61,8 +61,9 @@ type UnixfsAPI interface { // DagAPI specifies the interface to IPLD type DagAPI interface { // Put inserts data using specified format and input encoding. - // If format is not specified (nil), default dag-cbor/sha256 is used - Put(ctx context.Context, src io.Reader, opts ...options.DagPutOption) ([]Node, error) + // Unless used with WithCodec or WithHash, the defaults "dag-cbor" and + // "sha256" are used. + Put(ctx context.Context, src io.Reader, opts ...options.DagPutOption) (Path, error) // WithInputEnc is an option for Put which specifies the input encoding of the // data. Default is "json", most formats/codecs support "raw" From 3faa30b50066c0ee87ff43c4e0408b69cf1cb3e0 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 26 Dec 2017 14:30:22 -0800 Subject: [PATCH 2077/3817] clear out memory after reads from the dagreader License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@c2f7361a8ea07e8db59c42ce1a4dd0648a20c7c3 --- unixfs/io/pbdagreader.go | 60 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index dcd383460..891865400 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -10,7 +10,9 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" + node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" ) // DagReader provides a way to easily read the data contained in a dag. @@ -30,6 +32,9 @@ type pbDagReader struct { // NodeGetters for each of 'nodes' child links promises []mdag.NodeGetter + // the cid of each child of the current node + links []*cid.Cid + // the index of the child link currently being read from linkPosition int @@ -47,30 +52,54 @@ var _ DagReader = (*pbDagReader)(nil) func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv mdag.DAGService) *pbDagReader { fctx, cancel := context.WithCancel(ctx) - promises := mdag.GetDAG(fctx, serv, n) + curLinks := getLinkCids(n) return &pbDagReader{ node: n, serv: serv, buf: NewBufDagReader(pb.GetData()), - promises: promises, + promises: make([]mdag.NodeGetter, len(curLinks)), + links: curLinks, ctx: fctx, cancel: cancel, pbdata: pb, } } +const preloadSize = 10 + +func (dr *pbDagReader) preloadNextNodes(ctx context.Context) { + beg := dr.linkPosition + end := beg + preloadSize + if end >= len(dr.links) { + end = len(dr.links) + } + + for i, p := range mdag.GetNodes(ctx, dr.serv, dr.links[beg:end]) { + dr.promises[beg+i] = p + } +} + // precalcNextBuf follows the next link in line and loads it from the // DAGService, setting the next buffer to read from func (dr *pbDagReader) precalcNextBuf(ctx context.Context) error { - dr.buf.Close() // Just to make sure + if dr.buf != nil { + dr.buf.Close() // Just to make sure + dr.buf = nil + } + if dr.linkPosition >= len(dr.promises) { return io.EOF } + if dr.promises[dr.linkPosition] == nil { + dr.preloadNextNodes(ctx) + } + nxt, err := dr.promises[dr.linkPosition].Get(ctx) if err != nil { return err } + dr.promises[dr.linkPosition] = nil dr.linkPosition++ switch nxt := nxt.(type) { @@ -105,6 +134,15 @@ func (dr *pbDagReader) precalcNextBuf(ctx context.Context) error { } } +func getLinkCids(n node.Node) []*cid.Cid { + links := n.Links() + out := make([]*cid.Cid, 0, len(links)) + for _, l := range links { + out = append(out, l.Cid) + } + return out +} + // Size return the total length of the data from the DAG structured file. func (dr *pbDagReader) Size() uint64 { return dr.pbdata.GetFilesize() @@ -117,6 +155,12 @@ func (dr *pbDagReader) Read(b []byte) (int, error) { // CtxReadFull reads data from the DAG structured file func (dr *pbDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { + if dr.buf == nil { + if err := dr.precalcNextBuf(ctx); err != nil { + return 0, err + } + } + // If no cached buffer, load one total := 0 for { @@ -145,6 +189,12 @@ func (dr *pbDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { } func (dr *pbDagReader) WriteTo(w io.Writer) (int64, error) { + if dr.buf == nil { + if err := dr.precalcNextBuf(dr.ctx); err != nil { + return 0, err + } + } + // If no cached buffer, load one total := int64(0) for { @@ -199,7 +249,9 @@ func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) { left := offset if int64(len(pb.Data)) >= offset { // Close current buf to close potential child dagreader - dr.buf.Close() + if dr.buf != nil { + dr.buf.Close() + } dr.buf = NewBufDagReader(pb.GetData()[offset:]) // start reading links from the beginning From b85f8e2d0cdb6238c90e8e954875b4fcd897a2ec Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 19 Dec 2017 21:53:02 -0800 Subject: [PATCH 2078/3817] Fix memory clearing in adder License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@adaacbb8a96c4484cb2051b4b06f69f1bbaf2513 --- mfs/system.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/mfs/system.go b/mfs/system.go index f0dd09958..28875d3b3 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -123,6 +123,36 @@ func (kr *Root) Flush() error { return nil } +// FlushMemFree flushes the root directory and then uncaches all of its links. +// This has the effect of clearing out potentially stale references and allows +// them to be garbage collected. +// CAUTION: Take care not to ever call this while holding a reference to any +// child directories. Those directories will be bad references and using them +// may have unintended racy side effects. +// A better implemented mfs system (one that does smarter internal caching and +// refcounting) shouldnt need this method. +func (kr *Root) FlushMemFree(ctx context.Context) error { + dir, ok := kr.GetValue().(*Directory) + if !ok { + return fmt.Errorf("invalid mfs structure, root should be a directory") + } + + if err := dir.Flush(); err != nil { + return err + } + + dir.lock.Lock() + defer dir.lock.Unlock() + for name := range dir.files { + delete(dir.files, name) + } + for name := range dir.childDirs { + delete(dir.childDirs, name) + } + + return nil +} + // closeChild implements the childCloser interface, and signals to the publisher that // there are changes ready to be published func (kr *Root) closeChild(name string, nd node.Node, sync bool) error { From 8972ab9e110214d72b9c048240ac75e33bd69248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 10 Dec 2017 23:15:37 +0100 Subject: [PATCH 2079/3817] coreapi: Name API proposal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@1b5e997ca845b58519a3592db60c78d4ec7efdc8 --- coreiface/interface.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/coreiface/interface.go b/coreiface/interface.go index e51888e60..3fcf7374e 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -6,6 +6,7 @@ import ( "context" "errors" "io" + "time" options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" @@ -27,6 +28,11 @@ type Path interface { type Node ipld.Node type Link ipld.Link +type IpnsEntry struct { + Name string + Value Path +} + type Reader interface { io.ReadSeeker io.Closer @@ -37,6 +43,7 @@ type CoreAPI interface { // Unixfs returns an implementation of Unixfs API Unixfs() UnixfsAPI Dag() DagAPI + Name() NameAPI // ResolvePath resolves the path using Unixfs resolver ResolvePath(context.Context, Path) (Path, error) @@ -90,6 +97,18 @@ type DagAPI interface { WithDepth(depth int) options.DagTreeOption } +type NameAPI interface { + Publish(ctx context.Context, path Path, validTime time.Duration, key string) (*IpnsEntry, error) + Resolve(ctx context.Context, name string, recursive bool, local bool, nocache bool) (Path, error) +} + +type KeyApi interface { + Generate(ctx context.Context, name string, algorithm string, size int) error + List(ctx context.Context) (map[string]string, error) //TODO: better key type? + Rename(ctx context.Context, oldName string, newName string) error + Remove(ctx context.Context, name string) error +} + // type ObjectAPI interface { // New() (cid.Cid, Object) // Get(string) (Object, error) From 286e418f6c66494631d6b570b1ec86396c13e2f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 10 Dec 2017 23:48:16 +0100 Subject: [PATCH 2080/3817] coreapi: Keystore API proposal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@4fbdf56dc24b8b7357c0eb06c223093c506c1bbc --- coreiface/interface.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 3fcf7374e..f6412d68f 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -44,6 +44,7 @@ type CoreAPI interface { Unixfs() UnixfsAPI Dag() DagAPI Name() NameAPI + Key() KeyAPI // ResolvePath resolves the path using Unixfs resolver ResolvePath(context.Context, Path) (Path, error) @@ -102,11 +103,11 @@ type NameAPI interface { Resolve(ctx context.Context, name string, recursive bool, local bool, nocache bool) (Path, error) } -type KeyApi interface { - Generate(ctx context.Context, name string, algorithm string, size int) error +type KeyAPI interface { + Generate(ctx context.Context, name string, algorithm string, size int) (string, error) List(ctx context.Context) (map[string]string, error) //TODO: better key type? - Rename(ctx context.Context, oldName string, newName string) error - Remove(ctx context.Context, name string) error + Rename(ctx context.Context, oldName string, newName string, force bool) (string, bool, error) + Remove(ctx context.Context, name string) (string, error) } // type ObjectAPI interface { From c02dc49770c2cbbb89c695c8e83bfc1276c6d665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 13 Dec 2017 19:05:23 +0100 Subject: [PATCH 2081/3817] coreapi: name/key functional options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@7a786c5509ec924e3cfe2c29b42f76c47ccecbc4 --- coreiface/interface.go | 19 +++++++-- coreiface/options/key.go | 65 ++++++++++++++++++++++++++++ coreiface/options/name.go | 89 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 coreiface/options/key.go create mode 100644 coreiface/options/name.go diff --git a/coreiface/interface.go b/coreiface/interface.go index f6412d68f..36bd801a1 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -99,14 +99,25 @@ type DagAPI interface { } type NameAPI interface { - Publish(ctx context.Context, path Path, validTime time.Duration, key string) (*IpnsEntry, error) - Resolve(ctx context.Context, name string, recursive bool, local bool, nocache bool) (Path, error) + Publish(ctx context.Context, path Path, opts ...options.NamePublishOption) (*IpnsEntry, error) + WithValidTime(validTime time.Duration) options.NamePublishOption + WithKey(key string) options.NamePublishOption + + Resolve(ctx context.Context, name string, opts ...options.NameResolveOption) (Path, error) + WithRecursive(recursive bool) options.NameResolveOption + WithLocal(local bool) options.NameResolveOption + WithNoCache(nocache bool) options.NameResolveOption } type KeyAPI interface { - Generate(ctx context.Context, name string, algorithm string, size int) (string, error) + Generate(ctx context.Context, name string, opts ...options.KeyGenerateOption) (string, error) + WithAlgorithm(algorithm string) options.KeyGenerateOption + WithSize(size int) options.KeyGenerateOption + + Rename(ctx context.Context, oldName string, newName string, opts ...options.KeyRenameOption) (string, bool, error) + WithForce(force bool) options.KeyRenameOption + List(ctx context.Context) (map[string]string, error) //TODO: better key type? - Rename(ctx context.Context, oldName string, newName string, force bool) (string, bool, error) Remove(ctx context.Context, name string) (string, error) } diff --git a/coreiface/options/key.go b/coreiface/options/key.go new file mode 100644 index 000000000..5ed7b408f --- /dev/null +++ b/coreiface/options/key.go @@ -0,0 +1,65 @@ +package options + +type KeyGenerateSettings struct { + Algorithm string + Size int +} + +type KeyRenameSettings struct { + Force bool +} + +type KeyGenerateOption func(*KeyGenerateSettings) error +type KeyRenameOption func(*KeyRenameSettings) error + +func KeyGenerateOptions(opts ...KeyGenerateOption) (*KeyGenerateSettings, error) { + options := &KeyGenerateSettings{ + Algorithm: "rsa", + Size: 0, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +func KeyRenameOptions(opts ...KeyRenameOption) (*KeyRenameSettings, error) { + options := &KeyRenameSettings{ + Force: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type KeyOptions struct{} + +func (api *KeyOptions) WithAlgorithm(algorithm string) KeyGenerateOption { + return func(settings *KeyGenerateSettings) error { + settings.Algorithm = algorithm + return nil + } +} + +func (api *KeyOptions) WithSize(size int) KeyGenerateOption { + return func(settings *KeyGenerateSettings) error { + settings.Size = size + return nil + } +} + +func (api *KeyOptions) WithForce(force bool) KeyRenameOption { + return func(settings *KeyRenameSettings) error { + settings.Force = force + return nil + } +} diff --git a/coreiface/options/name.go b/coreiface/options/name.go new file mode 100644 index 000000000..aa2129162 --- /dev/null +++ b/coreiface/options/name.go @@ -0,0 +1,89 @@ +package options + +import ( + "time" +) + +type NamePublishSettings struct { + ValidTime time.Duration + Key string +} + +type NameResolveSettings struct { + Recursive bool + Local bool + Nocache bool +} + +type NamePublishOption func(*NamePublishSettings) error +type NameResolveOption func(*NameResolveSettings) error + +func NamePublishOptions(opts ...NamePublishOption) (*NamePublishSettings, error) { + options := &NamePublishSettings{ + ValidTime: 24 * time.Hour, + Key: "self", + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +func NameResolveOptions(opts ...NameResolveOption) (*NameResolveSettings, error) { + options := &NameResolveSettings{ + Recursive: false, + Local: false, + Nocache: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +type NameOptions struct{} + +func (api *NameOptions) WithValidTime(validTime time.Duration) NamePublishOption { + return func(settings *NamePublishSettings) error { + settings.ValidTime = validTime + return nil + } +} + +func (api *NameOptions) WithKey(key string) NamePublishOption { + return func(settings *NamePublishSettings) error { + settings.Key = key + return nil + } +} + +func (api *NameOptions) WithRecursive(recursive bool) NameResolveOption { + return func(settings *NameResolveSettings) error { + settings.Recursive = recursive + return nil + } +} + +func (api *NameOptions) WithLocal(local bool) NameResolveOption { + return func(settings *NameResolveSettings) error { + settings.Local = local + return nil + } +} + +func (api *NameOptions) WithNoCache(nocache bool) NameResolveOption { + return func(settings *NameResolveSettings) error { + settings.Nocache = nocache + return nil + } +} From d733fa263ca5d8340684f8c062ef474000f54fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 15 Dec 2017 01:03:53 +0100 Subject: [PATCH 2082/3817] coreapi: Documentation for Name/Key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@74695ab9b4f2efc64b6a88fcd900fbd67a63b416 --- coreiface/interface.go | 47 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/coreiface/interface.go b/coreiface/interface.go index 36bd801a1..a861e4700 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -98,26 +98,73 @@ type DagAPI interface { WithDepth(depth int) options.DagTreeOption } +// NameAPI specifies the interface to IPNS. +// +// IPNS is a PKI namespace, where names are the hashes of public keys, and the +// private key enables publishing new (signed) values. In both publish and +// resolve, the default name used is the node's own PeerID, which is the hash of +// its public key. +// +// You can use .Key API to list and generate more names and their respective keys. type NameAPI interface { + // Publish announces new IPNS name Publish(ctx context.Context, path Path, opts ...options.NamePublishOption) (*IpnsEntry, error) + + // WithValidTime is an option for Publish which specifies for how long the + // entry will remain valid. Default value is 24h WithValidTime(validTime time.Duration) options.NamePublishOption + + // WithKey is an option for Publish which specifies the key to use for + // publishing. Default value is "self" which is the node's own PeerID. + // + // You can use .Key API to list and generate more names and their respective keys. WithKey(key string) options.NamePublishOption + // Resolve attempts to resolve the newest version of the specified name Resolve(ctx context.Context, name string, opts ...options.NameResolveOption) (Path, error) + + // WithRecursive is an option for Resolve which specifies whether to perform a + // recursive lookup. Default value is false WithRecursive(recursive bool) options.NameResolveOption + + // WithLocal is an option for Resolve which specifies if the lookup should be + // offline. Default value is false WithLocal(local bool) options.NameResolveOption + + // WithNoCache is an option for Resolve which specifies when set to true + // disables the use of local name cache. Default value is false WithNoCache(nocache bool) options.NameResolveOption } +// KeyAPI specifies the interface to Keystore type KeyAPI interface { + // Generate generates new key, stores it in the keystore under the specified + // name and returns a base58 encoded multihash of it's public key Generate(ctx context.Context, name string, opts ...options.KeyGenerateOption) (string, error) + + // WithAlgorithm is an option for Generate which specifies which algorithm + // should be used for the key. Default is "rsa" + // + // Supported algorithms: + // * rsa + // * ed25519 WithAlgorithm(algorithm string) options.KeyGenerateOption + + // WithSize is an option for Generate which specifies the size of the key to + // generated. Default is 0 WithSize(size int) options.KeyGenerateOption + // Rename renames oldName key to newName. Rename(ctx context.Context, oldName string, newName string, opts ...options.KeyRenameOption) (string, bool, error) + + // WithForce is an option for Rename which specifies whether to allow to + // replace existing keys. WithForce(force bool) options.KeyRenameOption + // List lists keys stored in keystore List(ctx context.Context) (map[string]string, error) //TODO: better key type? + + // Remove removes keys from keystore Remove(ctx context.Context, name string) (string, error) } From d0730a47d3a8d4e4be5cc9d1886b8b5a32816267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 17 Dec 2017 03:44:13 +0100 Subject: [PATCH 2083/3817] coreapi: name/key review suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@8f7e0241ec5a85f3acaf3152107bd9ad202f56a8 --- coreiface/interface.go | 45 ++++++++++++++++++++++----------------- coreiface/options/key.go | 7 +++++- coreiface/options/name.go | 14 +++++++----- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index a861e4700..0086bd0d6 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -28,16 +28,21 @@ type Path interface { type Node ipld.Node type Link ipld.Link -type IpnsEntry struct { - Name string - Value Path -} - type Reader interface { io.ReadSeeker io.Closer } +type IpnsEntry interface { + Name() string + Value() Path +} + +type Key interface { + Name() string + Path() Path +} + // CoreAPI defines an unified interface to IPFS for Go programs. type CoreAPI interface { // Unixfs returns an implementation of Unixfs API @@ -108,7 +113,7 @@ type DagAPI interface { // You can use .Key API to list and generate more names and their respective keys. type NameAPI interface { // Publish announces new IPNS name - Publish(ctx context.Context, path Path, opts ...options.NamePublishOption) (*IpnsEntry, error) + Publish(ctx context.Context, path Path, opts ...options.NamePublishOption) (IpnsEntry, error) // WithValidTime is an option for Publish which specifies for how long the // entry will remain valid. Default value is 24h @@ -116,8 +121,9 @@ type NameAPI interface { // WithKey is an option for Publish which specifies the key to use for // publishing. Default value is "self" which is the node's own PeerID. + // The key parameter must be either PeerID or keystore key alias. // - // You can use .Key API to list and generate more names and their respective keys. + // You can use KeyAPI to list and generate more names and their respective keys. WithKey(key string) options.NamePublishOption // Resolve attempts to resolve the newest version of the specified name @@ -131,41 +137,42 @@ type NameAPI interface { // offline. Default value is false WithLocal(local bool) options.NameResolveOption - // WithNoCache is an option for Resolve which specifies when set to true - // disables the use of local name cache. Default value is false - WithNoCache(nocache bool) options.NameResolveOption + // WithCache is an option for Resolve which specifies if cache should be used. + // Default value is true + WithCache(cache bool) options.NameResolveOption } // KeyAPI specifies the interface to Keystore type KeyAPI interface { // Generate generates new key, stores it in the keystore under the specified // name and returns a base58 encoded multihash of it's public key - Generate(ctx context.Context, name string, opts ...options.KeyGenerateOption) (string, error) + Generate(ctx context.Context, name string, opts ...options.KeyGenerateOption) (Key, error) // WithAlgorithm is an option for Generate which specifies which algorithm - // should be used for the key. Default is "rsa" + // should be used for the key. Default is options.RSAKey // // Supported algorithms: - // * rsa - // * ed25519 + // * options.RSAKey + // * options.Ed25519Key WithAlgorithm(algorithm string) options.KeyGenerateOption // WithSize is an option for Generate which specifies the size of the key to // generated. Default is 0 WithSize(size int) options.KeyGenerateOption - // Rename renames oldName key to newName. - Rename(ctx context.Context, oldName string, newName string, opts ...options.KeyRenameOption) (string, bool, error) + // Rename renames oldName key to newName. Returns the key and whether another + // key was overwritten, or an error + Rename(ctx context.Context, oldName string, newName string, opts ...options.KeyRenameOption) (Key, bool, error) // WithForce is an option for Rename which specifies whether to allow to // replace existing keys. WithForce(force bool) options.KeyRenameOption // List lists keys stored in keystore - List(ctx context.Context) (map[string]string, error) //TODO: better key type? + List(ctx context.Context) ([]Key, error) - // Remove removes keys from keystore - Remove(ctx context.Context, name string) (string, error) + // Remove removes keys from keystore. Returns ipns path of the removed key + Remove(ctx context.Context, name string) (Path, error) } // type ObjectAPI interface { diff --git a/coreiface/options/key.go b/coreiface/options/key.go index 5ed7b408f..c84f0f8f8 100644 --- a/coreiface/options/key.go +++ b/coreiface/options/key.go @@ -1,5 +1,10 @@ package options +const ( + RSAKey = "rsa" + Ed25519Key = "ed25519" +) + type KeyGenerateSettings struct { Algorithm string Size int @@ -14,7 +19,7 @@ type KeyRenameOption func(*KeyRenameSettings) error func KeyGenerateOptions(opts ...KeyGenerateOption) (*KeyGenerateSettings, error) { options := &KeyGenerateSettings{ - Algorithm: "rsa", + Algorithm: RSAKey, Size: 0, } diff --git a/coreiface/options/name.go b/coreiface/options/name.go index aa2129162..9f8aaafc8 100644 --- a/coreiface/options/name.go +++ b/coreiface/options/name.go @@ -4,6 +4,10 @@ import ( "time" ) +const ( + DefaultNameValidTime = 24 * time.Hour +) + type NamePublishSettings struct { ValidTime time.Duration Key string @@ -12,7 +16,7 @@ type NamePublishSettings struct { type NameResolveSettings struct { Recursive bool Local bool - Nocache bool + Cache bool } type NamePublishOption func(*NamePublishSettings) error @@ -20,7 +24,7 @@ type NameResolveOption func(*NameResolveSettings) error func NamePublishOptions(opts ...NamePublishOption) (*NamePublishSettings, error) { options := &NamePublishSettings{ - ValidTime: 24 * time.Hour, + ValidTime: DefaultNameValidTime, Key: "self", } @@ -38,7 +42,7 @@ func NameResolveOptions(opts ...NameResolveOption) (*NameResolveSettings, error) options := &NameResolveSettings{ Recursive: false, Local: false, - Nocache: false, + Cache: true, } for _, opt := range opts { @@ -81,9 +85,9 @@ func (api *NameOptions) WithLocal(local bool) NameResolveOption { } } -func (api *NameOptions) WithNoCache(nocache bool) NameResolveOption { +func (api *NameOptions) WithCache(cache bool) NameResolveOption { return func(settings *NameResolveSettings) error { - settings.Nocache = nocache + settings.Cache = cache return nil } } From 6527d6e06318ce93a01a40d769225f9ad4fce6b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 1 Jan 2018 18:59:07 +0100 Subject: [PATCH 2084/3817] coreapi: key tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@ed5f040416bbc6a72008cf3c9f299c47da6f42f1 --- coreiface/interface.go | 6 +++--- coreiface/options/key.go | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 0086bd0d6..dc8365669 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -148,13 +148,13 @@ type KeyAPI interface { // name and returns a base58 encoded multihash of it's public key Generate(ctx context.Context, name string, opts ...options.KeyGenerateOption) (Key, error) - // WithAlgorithm is an option for Generate which specifies which algorithm + // WithType is an option for Generate which specifies which algorithm // should be used for the key. Default is options.RSAKey // - // Supported algorithms: + // Supported key types: // * options.RSAKey // * options.Ed25519Key - WithAlgorithm(algorithm string) options.KeyGenerateOption + WithType(algorithm string) options.KeyGenerateOption // WithSize is an option for Generate which specifies the size of the key to // generated. Default is 0 diff --git a/coreiface/options/key.go b/coreiface/options/key.go index c84f0f8f8..114361875 100644 --- a/coreiface/options/key.go +++ b/coreiface/options/key.go @@ -3,6 +3,8 @@ package options const ( RSAKey = "rsa" Ed25519Key = "ed25519" + + DefaultRSALen = 2048 ) type KeyGenerateSettings struct { @@ -20,7 +22,7 @@ type KeyRenameOption func(*KeyRenameSettings) error func KeyGenerateOptions(opts ...KeyGenerateOption) (*KeyGenerateSettings, error) { options := &KeyGenerateSettings{ Algorithm: RSAKey, - Size: 0, + Size: -1, } for _, opt := range opts { @@ -48,7 +50,7 @@ func KeyRenameOptions(opts ...KeyRenameOption) (*KeyRenameSettings, error) { type KeyOptions struct{} -func (api *KeyOptions) WithAlgorithm(algorithm string) KeyGenerateOption { +func (api *KeyOptions) WithType(algorithm string) KeyGenerateOption { return func(settings *KeyGenerateSettings) error { settings.Algorithm = algorithm return nil From 78823e7ccbdc16b651c269f86e4cf5915144250a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 2 Jan 2018 00:53:48 +0100 Subject: [PATCH 2085/3817] coreapi: Name tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@d28b9301ac2ca1a9e209680ba5bb613f19cbb6fe --- coreiface/interface.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index dc8365669..cdbb2508b 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -157,7 +157,10 @@ type KeyAPI interface { WithType(algorithm string) options.KeyGenerateOption // WithSize is an option for Generate which specifies the size of the key to - // generated. Default is 0 + // generated. Default is -1 + // + // value of -1 means 'use default size for key type': + // * 2048 for RSA WithSize(size int) options.KeyGenerateOption // Rename renames oldName key to newName. Returns the key and whether another From 7432bcc1bb9b78ac813df705042e2b8997b255f6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 31 Dec 2017 15:15:18 -0800 Subject: [PATCH 2086/3817] test promised don't get allocated where not wanted License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@83c1bd02c0d308574b1e6b6e9b326d0dd368b571 --- unixfs/io/dagreader_test.go | 50 +++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index a5ed6dd39..da16feaf7 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -4,6 +4,7 @@ import ( "bytes" "io" "io/ioutil" + "math/rand" "strings" "testing" @@ -72,6 +73,55 @@ func TestSeekAndRead(t *testing.T) { } } +func TestSeekAndReadLarge(t *testing.T) { + dserv := testu.GetDAGServ() + inbuf := make([]byte, 20000) + rand.Read(inbuf) + + node := testu.GetNode(t, dserv, inbuf, testu.UseProtoBufLeaves) + ctx, closer := context.WithCancel(context.Background()) + defer closer() + + reader, err := NewDagReader(ctx, node, dserv) + if err != nil { + t.Fatal(err) + } + + _, err = reader.Seek(10000, io.SeekStart) + if err != nil { + t.Fatal(err) + } + + buf := make([]byte, 100) + _, err = io.ReadFull(reader, buf) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(buf, inbuf[10000:10100]) { + t.Fatal("seeked read failed") + } + + pbdr := reader.(*pbDagReader) + var count int + for i, p := range pbdr.promises { + if i > 20 && i < 30 { + if p == nil { + t.Fatal("expected index to be not nil: ", i) + } + count++ + } else { + if p != nil { + t.Fatal("expected index to be nil: ", i) + } + } + } + // -1 because we read some and it cleared one + if count != preloadSize-1 { + t.Fatalf("expected %d preloaded promises, got %d", preloadSize-1, count) + } +} + func TestRelativeSeek(t *testing.T) { dserv := testu.GetDAGServ() ctx, closer := context.WithCancel(context.Background()) From a4f17aa015a281f2c32278ab7e7419cd5b06d085 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 21 Jan 2018 11:31:47 -0800 Subject: [PATCH 2087/3817] interface docs for pinner License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@407d3b91bcca61d36257bdcc24844180c3dd0b60 --- pinning/pinner/pin.go | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index dc2eee7f2..4d66408ef 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -46,11 +46,22 @@ const ( type PinMode int const ( + // Recursive pins pin the target cids along with any reachable children. Recursive PinMode = iota + + // Direct pins pin just the target cid. Direct + + // Indirect pins are cids who have some ancestor pinned recursively. Indirect + + // Internal pins are cids used to keep the internal state of the pinner. Internal + + // NotPinned NotPinned + + // Any refers to any pinned cid Any ) @@ -82,10 +93,20 @@ func StringToPinMode(s string) (PinMode, bool) { } type Pinner interface { + // IsPinned returns whether or not the given cid is pinned + // and an explanation of why its pinned IsPinned(*cid.Cid) (string, bool, error) + + // IsPinnedWithType returns whether or not the given cid is pinned with the + // given pin type, as well as returning the type of pin its pinned with. IsPinnedWithType(*cid.Cid, PinMode) (string, bool, error) - Pin(context.Context, node.Node, bool) error - Unpin(context.Context, *cid.Cid, bool) error + + // Pin the given node, optionally recursively. + Pin(ctx context.Context, node node.Node, recursive bool) error + + // Unpin the given cid. If recursive is true, removes either a recursive or + // a direct pin. If recursive is false, only removes a direct pin. + Unpin(ctx context.Context, cid *cid.Cid, recursive bool) error // Update updates a recursive pin from one cid to another // this is more efficient than simply pinning the new one and unpinning the @@ -106,9 +127,17 @@ type Pinner interface { // be successful. RemovePinWithMode(*cid.Cid, PinMode) + // Flush writes the pin state to the backing datastore Flush() error + + // DirectKeys returns all directly pinned cids DirectKeys() []*cid.Cid + + // DirectKeys returns all recursively pinned cids RecursiveKeys() []*cid.Cid + + // InternalPins returns all cids kept pinned for the internal state of the + // pinner InternalPins() []*cid.Cid } From 85305cdd71ff3684195bb904670715376a6ced0c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 21 Jan 2018 11:46:03 -0800 Subject: [PATCH 2088/3817] data storage interfaces License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-blockservice@dda5735c758fa289ef61220497569d3b09c7993b --- blockservice/blockservice.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 7929a67f3..65d98c8d9 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -25,13 +25,26 @@ var ErrNotFound = errors.New("blockservice: key not found") // datastore and may retrieve data from a remote Exchange. // It uses an internal `datastore.Datastore` instance to store values. type BlockService interface { + // Blockstore returns a reference to the underlying blockstore Blockstore() blockstore.Blockstore + + // Exchange returns a reference to the underlying exchange (usually bitswap) Exchange() exchange.Interface + + // AddBlock puts a given block to the underlying datastore AddBlock(o blocks.Block) (*cid.Cid, error) + + // AddBlocks adds a slice of blocks at the same time using batching + // capabilities of the underlying datastore whenever possible. AddBlocks(bs []blocks.Block) ([]*cid.Cid, error) + GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) - GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block DeleteBlock(o blocks.Block) error + + // GetBlocks does a batch request for the given cids, returning blocks as + // they are found, in no particular order. + GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block + Close() error } From a0f854cef0db5c0a5195b108564f1cf37d2b996d Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 21 Jan 2018 11:46:03 -0800 Subject: [PATCH 2089/3817] data storage interfaces License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@d86b647b2129e82c2c6b7050747d62cef1aa1112 --- blockstore/blockstore.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 4ef59ffe3..946b1d69c 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -40,12 +40,19 @@ type Blockstore interface { DeleteBlock(*cid.Cid) error Has(*cid.Cid) (bool, error) Get(*cid.Cid) (blocks.Block, error) + + // Put puts a given block to the underlying datastore Put(blocks.Block) error + + // PutMany puts a slice of blocks at the same time using batching + // capabilities of the underlying datastore whenever possible. PutMany([]blocks.Block) error + // AllKeysChan returns a channel from which // the CIDs in the Blockstore can be read. It should respect // the given context, closing the channel if it becomes Done. AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) + // HashOnRead specifies if every read block should be // rehashed to make sure it matches its CID. HashOnRead(enabled bool) From adaf5f3c2403fc849702c3341f40d3a888161a99 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 22 Jan 2018 11:20:07 -0800 Subject: [PATCH 2090/3817] keystore interface docs License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-keystore@797c1f50b9add32cfec7ca92f87bbfe27f605608 --- keystore/keystore.go | 22 +++++++++++++--------- keystore/memkeystore.go | 4 +++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index fd9ab94b4..56dfd1b01 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -10,22 +10,25 @@ import ( ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) +// Keystore provides a key management interface type Keystore interface { - // Has return whether or not a key exist in the Keystore + // Has returns whether or not a key exist in the Keystore Has(string) (bool, error) - // Put store a key in the Keystore + // Put stores a key in the Keystore, if a key with the same name already exists, returns ErrKeyExists Put(string, ci.PrivKey) error - // Get retrieve a key from the Keystore + // Get retrieves a key from the Keystore if it exists, and returns ErrNoSuchKey + // otherwise. Get(string) (ci.PrivKey, error) - // Delete remove a key from the Keystore + // Delete removes a key from the Keystore Delete(string) error - // List return a list of key identifier + // List returns a list of key identifier List() ([]string, error) } var ErrNoSuchKey = fmt.Errorf("no key by the given name was found") var ErrKeyExists = fmt.Errorf("key by that name already exists, refusing to overwrite") +// FSKeystore is a keystore backed by files in a given directory stored on disk. type FSKeystore struct { dir string } @@ -60,7 +63,7 @@ func NewFSKeystore(dir string) (*FSKeystore, error) { return &FSKeystore{dir}, nil } -// Has return whether or not a key exist in the Keystore +// Has returns whether or not a key exist in the Keystore func (ks *FSKeystore) Has(name string) (bool, error) { kp := filepath.Join(ks.dir, name) @@ -77,7 +80,7 @@ func (ks *FSKeystore) Has(name string) (bool, error) { return true, nil } -// Put store a key in the Keystore +// Put stores a key in the Keystore, if a key with the same name already exists, returns ErrKeyExists func (ks *FSKeystore) Put(name string, k ci.PrivKey) error { if err := validateName(name); err != nil { return err @@ -108,7 +111,8 @@ func (ks *FSKeystore) Put(name string, k ci.PrivKey) error { return err } -// Get retrieve a key from the Keystore +// Get retrieves a key from the Keystore if it exists, and returns ErrNoSuchKey +// otherwise. func (ks *FSKeystore) Get(name string) (ci.PrivKey, error) { if err := validateName(name); err != nil { return nil, err @@ -127,7 +131,7 @@ func (ks *FSKeystore) Get(name string) (ci.PrivKey, error) { return ci.UnmarshalPrivateKey(data) } -// Delete remove a key from the Keystore +// Delete removes a key from the Keystore func (ks *FSKeystore) Delete(name string) error { if err := validateName(name); err != nil { return err diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index 3732a3262..6d07f6dc3 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -2,6 +2,8 @@ package keystore import ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" +// MemKeystore is an in memory keystore implementation that is not persisted to +// any backing storage. type MemKeystore struct { keys map[string]ci.PrivKey } @@ -58,7 +60,7 @@ func (mk *MemKeystore) Delete(name string) error { // List return a list of key identifier func (mk *MemKeystore) List() ([]string, error) { out := make([]string, 0, len(mk.keys)) - for k, _ := range mk.keys { + for k := range mk.keys { out = append(out, k) } return out, nil From 5debdd2c6b53f24a9f1afd6bd976d260905bd296 Mon Sep 17 00:00:00 2001 From: ForrestWeston Date: Mon, 22 Jan 2018 14:51:46 -0800 Subject: [PATCH 2091/3817] docs for mfs system method impls License: MIT Signed-off-by: ForrestWeston This commit was moved from ipfs/go-mfs@03918104b7a1af5d200e4e1b47fc721ee73d6fe8 --- mfs/system.go | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/mfs/system.go b/mfs/system.go index 28875d3b3..319ba3020 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -41,19 +41,19 @@ const ( TDir ) -// FSNode represents any node (directory, root, or file) in the mfs filesystem +// FSNode represents any node (directory, root, or file) in the mfs filesystem. type FSNode interface { GetNode() (node.Node, error) Flush() error Type() NodeType } -// Root represents the root of a filesystem tree +// Root represents the root of a filesystem tree. type Root struct { - // node is the merkledag root + // node is the merkledag root. node *dag.ProtoNode - // val represents the node. It can either be a File or a Directory + // val represents the node. It can either be a File or a Directory. val FSNode repub *Republisher @@ -63,9 +63,10 @@ type Root struct { Type string } +// PubFunc is the function used by the `publish()` method. type PubFunc func(context.Context, *cid.Cid) error -// newRoot creates a new Root and starts up a republisher routine for it +// NewRoot creates a new Root and starts up a republisher routine for it. func NewRoot(parent context.Context, ds dag.DAGService, node *dag.ProtoNode, pf PubFunc) (*Root, error) { var repub *Republisher @@ -107,10 +108,13 @@ func NewRoot(parent context.Context, ds dag.DAGService, node *dag.ProtoNode, pf return root, nil } +// GetValue returns the value of Root. func (kr *Root) GetValue() FSNode { return kr.val } +// Flush signals that an update has occurred since the last publish, +// and updates the Root republisher. func (kr *Root) Flush() error { nd, err := kr.GetValue().GetNode() if err != nil { @@ -154,7 +158,7 @@ func (kr *Root) FlushMemFree(ctx context.Context) error { } // closeChild implements the childCloser interface, and signals to the publisher that -// there are changes ready to be published +// there are changes ready to be published. func (kr *Root) closeChild(name string, nd node.Node, sync bool) error { c, err := kr.dserv.Add(nd) if err != nil { @@ -181,7 +185,7 @@ func (kr *Root) Close() error { return nil } -// Republisher manages when to publish a given entry +// Republisher manages when to publish a given entry. type Republisher struct { TimeoutLong time.Duration TimeoutShort time.Duration @@ -198,7 +202,7 @@ type Republisher struct { } // NewRepublisher creates a new Republisher object to republish the given root -// using the given short and long time intervals +// using the given short and long time intervals. func NewRepublisher(ctx context.Context, pf PubFunc, tshort, tlong time.Duration) *Republisher { ctx, cancel := context.WithCancel(ctx) return &Republisher{ @@ -218,6 +222,8 @@ func (p *Republisher) setVal(c *cid.Cid) { p.val = c } +// WaitPub Returns immediately if `lastpub` value is consistent with the +// current value `val`, else will block until `val` has been published. func (p *Republisher) WaitPub() { p.lk.Lock() consistent := p.lastpub == p.val @@ -239,7 +245,7 @@ func (p *Republisher) Close() error { // Touch signals that an update has occurred since the last publish. // Multiple consecutive touches may extend the time period before -// the next Publish occurs in order to more efficiently batch updates +// the next Publish occurs in order to more efficiently batch updates. func (np *Republisher) Update(c *cid.Cid) { np.setVal(c) select { @@ -248,7 +254,7 @@ func (np *Republisher) Update(c *cid.Cid) { } } -// Run is the main republisher loop +// Run is the main republisher loop. func (np *Republisher) Run() { for { select { @@ -284,6 +290,7 @@ func (np *Republisher) Run() { } } +// publish calls the `PubFunc`. func (np *Republisher) publish(ctx context.Context) error { np.lk.Lock() topub := np.val From 09ea58328fd83b23552b067be543628fe1d0bf05 Mon Sep 17 00:00:00 2001 From: ForrestWeston Date: Mon, 22 Jan 2018 14:49:04 -0800 Subject: [PATCH 2092/3817] interface docs for merkledag License: MIT Signed-off-by: ForrestWeston This commit was moved from ipfs/go-merkledag@329bbe245420d4fedc41694683426e0366308833 --- ipld/merkledag/merkledag.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 7b85bf008..311bcef27 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -27,14 +27,18 @@ var ErrNotFound = fmt.Errorf("merkledag: not found") // DAGService is an IPFS Merkle DAG service. type DAGService interface { + // Add adds the node to the DAGService Add(node.Node) (*cid.Cid, error) + // Get gets the node the from the DAGService Get(context.Context, *cid.Cid) (node.Node, error) + // Remove removes the node from the DAGService Remove(node.Node) error // GetMany returns a channel of NodeOption given // a set of CIDs. GetMany(context.Context, []*cid.Cid) <-chan *NodeOption + // Batch is a buffer for batching adds to a dag. Batch() *Batch LinkService From 985de409b9b6b302aaefe1372ea0419c235f06ca Mon Sep 17 00:00:00 2001 From: ForrestWeston Date: Mon, 22 Jan 2018 14:52:22 -0800 Subject: [PATCH 2093/3817] docs for pin method impls License: MIT Signed-off-by: ForrestWeston This commit was moved from ipfs/go-ipfs-pinner@5972a48b4657c99d6b8552ba4c39814d096b01d9 --- pinning/pinner/pin.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 4d66408ef..001981792 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -147,6 +147,7 @@ type Pinned struct { Via *cid.Cid } +// Pinned returns whether or not the given cid is pinned func (p Pinned) Pinned() bool { if p.Mode == NotPinned { return false @@ -155,6 +156,7 @@ func (p Pinned) Pinned() bool { } } +// String Returns pin status as string func (p Pinned) String() string { switch p.Mode { case NotPinned: @@ -277,6 +279,8 @@ func (p *pinner) IsPinned(c *cid.Cid) (string, bool, error) { return p.isPinnedWithType(c, Any) } +// IsPinnedWithType returns whether or not the given cid is pinned with the +// given pin type, as well as returning the type of pin its pinned with. func (p *pinner) IsPinnedWithType(c *cid.Cid, mode PinMode) (string, bool, error) { p.lock.RLock() defer p.lock.RUnlock() @@ -328,6 +332,8 @@ func (p *pinner) isPinnedWithType(c *cid.Cid, mode PinMode) (string, bool, error return "", false, nil } +// CheckIfPinned Checks if a set of keys are pinned, more efficient than +// calling IsPinned for each key, returns the pinned status of cid(s) func (p *pinner) CheckIfPinned(cids ...*cid.Cid) ([]Pinned, error) { p.lock.RLock() defer p.lock.RUnlock() @@ -393,6 +399,9 @@ func (p *pinner) CheckIfPinned(cids ...*cid.Cid) ([]Pinned, error) { return pinned, nil } +// RemovePinWithMode is for manually editing the pin structure. +// Use with care! If used improperly, garbage collection may not +// be successful. func (p *pinner) RemovePinWithMode(c *cid.Cid, mode PinMode) { p.lock.Lock() defer p.lock.Unlock() @@ -486,6 +495,9 @@ func (p *pinner) RecursiveKeys() []*cid.Cid { return p.recursePin.Keys() } +// Update updates a recursive pin from one cid to another +// this is more efficient than simply pinning the new one and unpinning the +// old one func (p *pinner) Update(ctx context.Context, from, to *cid.Cid, unpin bool) error { p.lock.Lock() defer p.lock.Unlock() @@ -556,6 +568,8 @@ func (p *pinner) Flush() error { return nil } +// InternalPins returns all cids kept pinned for the internal state of the +// pinner func (p *pinner) InternalPins() []*cid.Cid { p.lock.Lock() defer p.lock.Unlock() From 4d4c3dc176859461010b576f9af66919f34aaed3 Mon Sep 17 00:00:00 2001 From: ForrestWeston Date: Mon, 22 Jan 2018 14:44:46 -0800 Subject: [PATCH 2094/3817] interface docs for coreapi interface License: MIT Signed-off-by: ForrestWeston This commit was moved from ipfs/interface-go-ipfs-core@2c3137f0557f44ca23730ff134b38b9426731e56 --- coreiface/interface.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index cdbb2508b..720f935e2 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -17,9 +17,13 @@ import ( // Path is a generic wrapper for paths used in the API. A path can be resolved // to a CID using one of Resolve functions in the API. type Path interface { + // String returns the path as a string. String() string + // Cid returns cid referred to by path Cid() *cid.Cid + // Root returns cid of root path Root() *cid.Cid + // Resolved returns whether path has been fully resolved Resolved() bool } @@ -33,22 +37,31 @@ type Reader interface { io.Closer } +// IpnsEntry specifies the interface to IpnsEntries type IpnsEntry interface { + // Name returns IpnsEntry name Name() string + // Value returns IpnsEntry value Value() Path } +// Key specifies the interface to Keys in KeyAPI Keystore type Key interface { + // Key returns key name Name() string + // Path returns key path Path() Path } // CoreAPI defines an unified interface to IPFS for Go programs. type CoreAPI interface { - // Unixfs returns an implementation of Unixfs API + // Unixfs returns an implementation of Unixfs API. Unixfs() UnixfsAPI + // Dag returns an implementation of Dag API. Dag() DagAPI + // Name returns an implementation of Name API. Name() NameAPI + // Key returns an implementation of Key API. Key() KeyAPI // ResolvePath resolves the path using Unixfs resolver From d6646aa5af68ad060b12d0d9a6500c84496712dc Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jan 2018 15:55:28 -0800 Subject: [PATCH 2095/3817] gx: mass update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@0001c46f2a5fe3a0b7d7593a78ef32b0b2a7a8f4 --- namesys/ipns_select_test.go | 2 +- namesys/namesys.go | 12 ++++++------ namesys/namesys_test.go | 4 ++-- namesys/publisher.go | 12 ++++++------ namesys/publisher_test.go | 10 +++++----- namesys/pubsub.go | 24 ++++++++++++------------ namesys/pubsub_test.go | 18 +++++++++--------- namesys/republisher/repub.go | 8 ++++---- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 8 ++++---- namesys/routing.go | 8 ++++---- 11 files changed, 55 insertions(+), 55 deletions(-) diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index e36eade00..7489f139b 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -9,7 +9,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) diff --git a/namesys/namesys.go b/namesys/namesys.go index 7f7adec1f..e38d58455 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -9,14 +9,14 @@ import ( path "github.com/ipfs/go-ipfs/path" - floodsub "gx/ipfs/QmP1T1SGU6276R2MHKP2owbck37Fnzd6ZkpyNJvnG2LoTG/go-libp2p-floodsub" - p2phost "gx/ipfs/QmP46LGWhzVZTMmt5akNNLfoV8qL4h5wTwmzQxLyDafggd/go-libp2p-host" - routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" - peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" - mh "gx/ipfs/QmYeKnKpubCMRiq3PGZcTREErthbb5Q9cXsCoSkD9bjEBd/go-multihash" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" + mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" + floodsub "gx/ipfs/Qma2TkMxcFLVGkYECTo4hrQohBYPx7uhpYL9EejEi8y3Nm/go-libp2p-floodsub" + peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + p2phost "gx/ipfs/QmfCtHMCd9xFvehvHeVxtKVXJTMVTuHhyPRVHEXetn87vL/go-libp2p-host" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 66062156d..2cd91c79f 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,9 +10,9 @@ import ( offroute "github.com/ipfs/go-ipfs/routing/offline" "github.com/ipfs/go-ipfs/unixfs" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" ) type mockResolver struct { diff --git a/namesys/publisher.go b/namesys/publisher.go index bdfbe5e3b..6d033d502 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -13,14 +13,14 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ft "github.com/ipfs/go-ipfs/unixfs" - routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" - u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" - record "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record" - dhtpb "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record/pb" - peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + record "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record" + dhtpb "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record/pb" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 37e3aeeae..de6a5c694 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -10,12 +10,12 @@ import ( mockrouting "github.com/ipfs/go-ipfs/routing/mock" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - ma "gx/ipfs/QmW8s4zTsUoX1Q6CeYxVKPyqSKbF7H1YDUyTostBtZ8DaG/go-multiaddr" - peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" + peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" - testutil "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" + testutil "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" ) type identity struct { diff --git a/namesys/pubsub.go b/namesys/pubsub.go index 676056852..c71d82a7d 100644 --- a/namesys/pubsub.go +++ b/namesys/pubsub.go @@ -12,20 +12,20 @@ import ( path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - floodsub "gx/ipfs/QmP1T1SGU6276R2MHKP2owbck37Fnzd6ZkpyNJvnG2LoTG/go-libp2p-floodsub" - p2phost "gx/ipfs/QmP46LGWhzVZTMmt5akNNLfoV8qL4h5wTwmzQxLyDafggd/go-libp2p-host" - routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" - u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" - record "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record" - dhtpb "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record/pb" - peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" - mh "gx/ipfs/QmYeKnKpubCMRiq3PGZcTREErthbb5Q9cXsCoSkD9bjEBd/go-multihash" - pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" + floodsub "gx/ipfs/Qma2TkMxcFLVGkYECTo4hrQohBYPx7uhpYL9EejEi8y3Nm/go-libp2p-floodsub" + peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + record "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record" + dhtpb "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record/pb" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" + p2phost "gx/ipfs/QmfCtHMCd9xFvehvHeVxtKVXJTMVTuHhyPRVHEXetn87vL/go-libp2p-host" ) // PubsubPublisher is a publisher that distributes IPNS records through pubsub diff --git a/namesys/pubsub_test.go b/namesys/pubsub_test.go index c82a425db..abafa732b 100644 --- a/namesys/pubsub_test.go +++ b/namesys/pubsub_test.go @@ -9,16 +9,16 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" - floodsub "gx/ipfs/QmP1T1SGU6276R2MHKP2owbck37Fnzd6ZkpyNJvnG2LoTG/go-libp2p-floodsub" - p2phost "gx/ipfs/QmP46LGWhzVZTMmt5akNNLfoV8qL4h5wTwmzQxLyDafggd/go-libp2p-host" - routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" - peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" - pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" - bhost "gx/ipfs/QmYmhgAcvmDGXct1qBvc1kz9BxQSit1XBrTeiGZp2FvRyn/go-libp2p-blankhost" - netutil "gx/ipfs/QmZTcPxK6VqrwY94JpKZPvEqAZ6tEr1rLrpcqJbbRZbg2V/go-libp2p-netutil" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" + netutil "gx/ipfs/QmV1axkk86DDkYwS269AvPy9eV5h7mUyHveJkSVHPjrQtY/go-libp2p-netutil" + bhost "gx/ipfs/QmZ15dDSCo4DKn4o4GnqqLExKATBeeo3oNyQ5FBKtNjEQT/go-libp2p-blankhost" + floodsub "gx/ipfs/Qma2TkMxcFLVGkYECTo4hrQohBYPx7uhpYL9EejEi8y3Nm/go-libp2p-floodsub" + peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - testutil "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" + pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" + testutil "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" + p2phost "gx/ipfs/QmfCtHMCd9xFvehvHeVxtKVXJTMVTuHhyPRVHEXetn87vL/go-libp2p-host" ) func newNetHost(ctx context.Context, t *testing.T) p2phost.Host { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index d871aaca3..dc158fea6 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,15 +11,15 @@ import ( path "github.com/ipfs/go-ipfs/path" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - recpb "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record/pb" - peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" ic "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + recpb "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record/pb" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 441b7cce2..08bb700a3 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -12,9 +12,9 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" + mocknet "gx/ipfs/QmNRN4eZGmY89CRC4T5PC4xDYRx6GkDKEfRnvrT65fVeio/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" - mocknet "gx/ipfs/Qma23bpHwQrQyvKeBemaeJh7sAoRHggPkgnge1B9489ff5/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 9c1f83816..2e0159860 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,11 +8,11 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" - testutil "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" + testutil "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" - peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index cd6ee7c4b..7a1abbecf 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -9,14 +9,14 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" - u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - mh "gx/ipfs/QmYeKnKpubCMRiq3PGZcTREErthbb5Q9cXsCoSkD9bjEBd/go-multihash" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) var log = logging.Logger("namesys") From d4c45857d8ae7c5cae0e4a705821e40b13542625 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jan 2018 15:55:28 -0800 Subject: [PATCH 2096/3817] gx: mass update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@9f63580f460e49383e675ac95e277d1c7014c015 --- unixfs/archive/archive.go | 2 +- unixfs/archive/tar/writer.go | 2 +- unixfs/hamt/hamt.go | 4 ++-- unixfs/io/dagreader.go | 2 +- unixfs/io/dirbuilder.go | 4 ++-- unixfs/io/pbdagreader.go | 4 ++-- unixfs/io/resolve.go | 2 +- unixfs/mod/dagmodifier.go | 4 ++-- unixfs/mod/dagmodifier_test.go | 2 +- unixfs/test/utils.go | 8 ++++---- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index 8061f90dd..dc3afd0c2 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -11,7 +11,7 @@ import ( tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 360334b79..27372e8f8 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -13,8 +13,8 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" upb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // Writer is a utility structure that helps to write diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index fdf722387..044017f97 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -31,9 +31,9 @@ import ( format "github.com/ipfs/go-ipfs/unixfs" upb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" "gx/ipfs/QmfJHywXQu98UeZtGJBQrPAR6AtmDjjbe3qjTo9piXHPnx/murmur3" ) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 8ec527033..9d197876c 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -10,8 +10,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) var ErrIsDir = errors.New("this dag node is a directory") diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 1ab8cf712..822533435 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -8,9 +8,9 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // ShardSplitThreshold specifies how large of an unsharded directory diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 891865400..0bc8d61e8 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -10,9 +10,9 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // DagReader provides a way to easily read the data contained in a dag. diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index a66de7bdf..a5b4f132a 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -7,7 +7,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // ResolveUnixfsOnce resolves a single hop of a path through a graph in a diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 92fd3f4cc..b6ae06fa1 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -14,9 +14,9 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) var ErrSeekFail = errors.New("failed to seek properly") diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index d6ba1ddb8..41979c613 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -13,7 +13,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" testu "github.com/ipfs/go-ipfs/unixfs/test" - u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ) func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier, opts testu.NodeOpts) []byte { diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 8ea7de6e1..fe2a23508 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -15,10 +15,10 @@ import ( mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" - mh "gx/ipfs/QmYeKnKpubCMRiq3PGZcTREErthbb5Q9cXsCoSkD9bjEBd/go-multihash" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) func SizeSplitterGen(size int64) chunk.SplitterGen { From 64cdae564e63caee9fcdddbcc7e90e3a04f8e921 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jan 2018 15:55:28 -0800 Subject: [PATCH 2097/3817] gx: mass update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@c1c25e0487f7f1fe77667fca019893ced93fd320 --- mfs/dir.go | 4 ++-- mfs/file.go | 2 +- mfs/mfs_test.go | 10 +++++----- mfs/ops.go | 4 ++-- mfs/repub_test.go | 4 ++-- mfs/system.go | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 57e4554f0..250219d75 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -15,8 +15,8 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) var ErrNotYetImplemented = errors.New("not yet implemented") diff --git a/mfs/file.go b/mfs/file.go index 363026886..aa870551a 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 6498a7615..b9ec1b4e4 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -24,11 +24,11 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) func emptyDirNode() *dag.ProtoNode { diff --git a/mfs/ops.go b/mfs/ops.go index 214a66fca..9d7182124 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -9,8 +9,8 @@ import ( path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // Mv moves the file or directory at 'src' to 'dst' diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 60045fec5..499650d13 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - ci "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil/ci" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + ci "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil/ci" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index 319ba3020..28dbe8aec 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -19,9 +19,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) var ErrNotExist = errors.New("no such rootfs") From 75177db6b353784d06b86c4b6fd9c2bce4392e26 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jan 2018 15:55:28 -0800 Subject: [PATCH 2098/3817] gx: mass update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@2b1de3e6819d867684e1aee75c7cf01509d338db --- ipld/merkledag/batch.go | 6 +++--- ipld/merkledag/coding.go | 6 +++--- ipld/merkledag/merkledag.go | 6 +++--- ipld/merkledag/merkledag_test.go | 8 ++++---- ipld/merkledag/node.go | 6 +++--- ipld/merkledag/node_test.go | 2 +- ipld/merkledag/raw.go | 8 ++++---- ipld/merkledag/test/utils.go | 4 ++-- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/traverse/traverse_test.go | 2 +- ipld/merkledag/utils/diff.go | 4 ++-- ipld/merkledag/utils/diffenum.go | 4 ++-- ipld/merkledag/utils/diffenum_test.go | 4 ++-- ipld/merkledag/utils/utils.go | 6 +++--- ipld/merkledag/utils/utils_test.go | 2 +- 15 files changed, 35 insertions(+), 35 deletions(-) diff --git a/ipld/merkledag/batch.go b/ipld/merkledag/batch.go index 79c86aa9a..a6ee02f06 100644 --- a/ipld/merkledag/batch.go +++ b/ipld/merkledag/batch.go @@ -3,9 +3,9 @@ package merkledag import ( "runtime" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) // ParallelBatchCommits is the number of batch commits that can be in-flight before blocking. diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index dec46f168..660052796 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -5,12 +5,12 @@ import ( "sort" "strings" - "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" pb "github.com/ipfs/go-ipfs/merkledag/pb" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 311bcef27..8f89b137e 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -9,9 +9,9 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" - ipldcbor "gx/ipfs/QmeZv9VXw2SfVbX55LV6kGTWASKBc9ZxAVqGBeJcDGdoXy/go-ipld-cbor" + ipldcbor "gx/ipfs/QmNRz7BDWfdFNVLt7AVvmRefkrURD25EeoipcXqo6yoXU1/go-ipld-cbor" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // TODO: We should move these registrations elsewhere. Really, most of the IPLD diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index a1fd02d81..8b1ad1414 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -22,11 +22,11 @@ import ( mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" - blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) func TestNode(t *testing.T) { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 8dd7fed3d..44d44617d 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -5,9 +5,9 @@ import ( "encoding/json" "fmt" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - mh "gx/ipfs/QmYeKnKpubCMRiq3PGZcTREErthbb5Q9cXsCoSkD9bjEBd/go-multihash" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index 72b65c289..27726c454 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -8,7 +8,7 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) func TestRemoveLink(t *testing.T) { diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 9ef9e4da8..2d195dd5a 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -2,11 +2,11 @@ package merkledag import ( "fmt" - "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) type RawNode struct { diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index 89d44b7ee..c0ab995e2 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -5,8 +5,8 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" ) func Mock() dag.DAGService { diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index 2759d0269..d12c9f301 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -5,7 +5,7 @@ import ( "context" "errors" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // Order is an identifier for traversal algorithm orders diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 30f9c524d..23d42c6c0 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -8,7 +8,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) func TestDFSPreNoSkip(t *testing.T) { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 1114d18d5..db5e22936 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -7,8 +7,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) const ( diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go index e63ce985e..1dc8e4ecd 100644 --- a/ipld/merkledag/utils/diffenum.go +++ b/ipld/merkledag/utils/diffenum.go @@ -6,8 +6,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // DiffEnumerate fetches every object in the graph pointed to by 'to' that is diff --git a/ipld/merkledag/utils/diffenum_test.go b/ipld/merkledag/utils/diffenum_test.go index 72f6ea23f..668751609 100644 --- a/ipld/merkledag/utils/diffenum_test.go +++ b/ipld/merkledag/utils/diffenum_test.go @@ -8,8 +8,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) func buildNode(name string, desc map[string]ndesc, out map[string]node.Node) node.Node { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index de782536d..6c16967ca 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -10,9 +10,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - syncds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + syncds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) type Editor struct { diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 185aadf04..2c3e953d3 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -8,7 +8,7 @@ import ( mdtest "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) func TestAddLink(t *testing.T) { From c0d999672929fec9c44b96dede6eec18d3f3a09e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jan 2018 15:55:28 -0800 Subject: [PATCH 2099/3817] gx: mass update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-path@9d578c6c95a0df18fc0283b56a1acf47caea02c3 --- path/path.go | 2 +- path/resolver.go | 4 ++-- path/resolver_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/path/path.go b/path/path.go index 9c4d8f135..decf28904 100644 --- a/path/path.go +++ b/path/path.go @@ -5,7 +5,7 @@ import ( "path" "strings" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) // ErrBadPath is returned when a given path is incorrectly formatted diff --git a/path/resolver.go b/path/resolver.go index c4aabe1b7..68639fbc0 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -9,9 +9,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) var log = logging.Logger("path") diff --git a/path/resolver_test.go b/path/resolver_test.go index 533932814..8347fc602 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -9,8 +9,8 @@ import ( dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - util "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" + util "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) func randNode() *merkledag.ProtoNode { From fd663e85e9d4bb599ba038d0dcab5818f9fb834f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jan 2018 15:55:28 -0800 Subject: [PATCH 2100/3817] gx: mass update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@851ef0de0db5a513f92059bb78c94fa3aead01da --- blockservice/blockservice.go | 4 ++-- blockservice/blockservice_test.go | 6 +++--- blockservice/test/blocks_test.go | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 65d98c8d9..738822605 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -13,8 +13,8 @@ import ( bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) var log = logging.Logger("blockservice") diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index 00d9ccabf..c7ed02dc0 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -6,10 +6,10 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" butil "github.com/ipfs/go-ipfs/blocks/blocksutil" offline "github.com/ipfs/go-ipfs/exchange/offline" - "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" ) func TestWriteThroughWorks(t *testing.T) { diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index da54461d6..965327dae 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -10,12 +10,12 @@ import ( blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) func newObject(data []byte) blocks.Block { From 125aac2a8b61ef5b36ce8af54c9e11b756e36297 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jan 2018 15:55:28 -0800 Subject: [PATCH 2101/3817] gx: mass update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@b6e449bf82c9e9be359e3a1cd635f79d07830284 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 6 +++--- pinning/pinner/pin_test.go | 8 ++++---- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index f3b98597b..7a19b6969 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -9,9 +9,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 001981792..f148053ff 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -12,10 +12,10 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" dutils "github.com/ipfs/go-ipfs/merkledag/utils" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index f34283b79..7980c8dbc 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -10,10 +10,10 @@ import ( "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" - "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) func randNode() (*mdag.ProtoNode, *cid.Cid) { diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 116e62297..6d4bff54b 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -12,9 +12,9 @@ import ( "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" - node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 1ee16ed38..0eab73b35 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -10,9 +10,9 @@ import ( offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) func ignoreCids(_ *cid.Cid) {} From 5e7d211f6eb13d87e9a47729d28beb2a4250aa14 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jan 2018 15:55:28 -0800 Subject: [PATCH 2102/3817] gx: mass update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@318e958cdd426798e644612d76dc294463975f71 --- coreiface/interface.go | 4 ++-- coreiface/options/dag.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 720f935e2..6e00d51ab 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -10,8 +10,8 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ipld "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // Path is a generic wrapper for paths used in the API. A path can be resolved diff --git a/coreiface/options/dag.go b/coreiface/options/dag.go index 7850c4bc3..b56fcd81a 100644 --- a/coreiface/options/dag.go +++ b/coreiface/options/dag.go @@ -3,7 +3,7 @@ package options import ( "math" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) type DagPutSettings struct { From b882f44bec20e69f81e5c28432db26b2f4e21c92 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jan 2018 15:55:28 -0800 Subject: [PATCH 2103/3817] gx: mass update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@25bee4be0b1987d02114dce9fd35fe5e9012fc98 --- routing/mock/centralized_client.go | 18 +++++++++--------- routing/mock/centralized_server.go | 12 ++++++------ routing/mock/centralized_test.go | 8 ++++---- routing/mock/interface.go | 8 ++++---- routing/none/none_client.go | 10 +++++----- routing/offline/offline.go | 14 +++++++------- routing/offline/offline_test.go | 4 ++-- 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 175a9d436..683a2a884 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -6,18 +6,18 @@ import ( "time" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" + "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" - routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" - u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - ma "gx/ipfs/QmW8s4zTsUoX1Q6CeYxVKPyqSKbF7H1YDUyTostBtZ8DaG/go-multiaddr" - dhtpb "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record/pb" - peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" - pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" + ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" + dhtpb "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record/pb" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 1a3d6ef37..6a86a6c6b 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -6,13 +6,13 @@ import ( "sync" "time" - "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" + "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" - peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" - pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dssync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 917b71afb..0fb7ce90f 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -6,11 +6,11 @@ import ( "time" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" + "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" - u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" - pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 3aed934ce..31de6b0cc 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -8,11 +8,11 @@ import ( "context" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" + "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" - routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" - peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" + peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" ) // Server provides mockrouting Clients diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 64f9893b0..7d2939593 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,11 +6,11 @@ import ( repo "github.com/ipfs/go-ipfs/repo" - p2phost "gx/ipfs/QmP46LGWhzVZTMmt5akNNLfoV8qL4h5wTwmzQxLyDafggd/go-libp2p-host" - routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" - peer "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" - pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" + peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" + p2phost "gx/ipfs/QmfCtHMCd9xFvehvHeVxtKVXJTMVTuHhyPRVHEXetn87vL/go-libp2p-host" ) type nilclient struct { diff --git a/routing/offline/offline.go b/routing/offline/offline.go index a1d58f092..46cb34ada 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,15 +7,15 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - routing "gx/ipfs/QmPCGUjMRuBcPybZFpjhzpifwPP9wPRoiy5geTQKU4vqWA/go-libp2p-routing" - record "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record" - pb "gx/ipfs/QmWGtsyPYEoiqTtWLpeUA2jpW4YSZgarKDD2zivYAFz7sR/go-libp2p-record/pb" - "gx/ipfs/QmWNY7dV54ZDYmTA1ykVdwNCqC11mpU4zSUp6XDpLTH9eG/go-libp2p-peer" - pstore "gx/ipfs/QmYijbtjCxFEjSXaudaQAUz3LN5VKLssm8WCUsRoqzXmQR/go-libp2p-peerstore" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + record "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record" + pb "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record/pb" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" ) var ErrOffline = errors.New("routing system in offline mode") diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index e0da0d2ef..42a800162 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -5,8 +5,8 @@ import ( "context" "testing" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - "gx/ipfs/QmeDA8gNhvRTsbrjEieay5wezupJDiky8xvCzDABbsGzmp/go-testutil" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" ) func TestOfflineRouterStorage(t *testing.T) { From c80dc3e03e3fdd15a06d75a381ac352bc6460b8c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jan 2018 15:55:28 -0800 Subject: [PATCH 2104/3817] gx: mass update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@c332004241e4750ddb8b2e0d52d1707e2ffd8a2b --- filestore/filestore.go | 6 +++--- filestore/filestore_test.go | 4 ++-- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 502b11fed..873ef12c3 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -12,11 +12,11 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" - "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 13a95ee5f..802111424 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -11,8 +11,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index b77990073..603a51380 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -11,13 +11,13 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" - "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dsns "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/namespace" + dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dsns "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/namespace" - dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index 932061be3..35a640b68 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -8,9 +8,9 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) // Status is used to identify the state of the block data referenced From 9698cb6d6474a01803c23f22712555a8da2c1357 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jan 2018 15:55:28 -0800 Subject: [PATCH 2105/3817] gx: mass update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-blockstore@b2425312319f61818535a45da36fe73801b5a16c --- blockstore/arc_cache.go | 6 +++--- blockstore/arc_cache_test.go | 8 ++++---- blockstore/blockstore.go | 10 +++++----- blockstore/blockstore_test.go | 12 ++++++------ blockstore/bloom_cache.go | 4 ++-- blockstore/bloom_cache_test.go | 8 ++++---- blockstore/util/remove.go | 4 ++-- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index da3b6d7a2..e403bb96c 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -3,12 +3,12 @@ package blockstore import ( "context" - "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) // arccache wraps a BlockStore with an Adaptive Replacement Cache (ARC) for diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 734f1e73d..5a160d72b 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -4,11 +4,11 @@ import ( "context" "testing" - "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - syncds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + syncds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) var exampleBlock = blocks.NewBlock([]byte("foo")) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 946b1d69c..5a37560c5 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -9,13 +9,13 @@ import ( "sync/atomic" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dsns "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/namespace" + dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dsns "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/namespace" - dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 31e75acf6..2b0366096 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -7,13 +7,13 @@ import ( "testing" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" - ds_sync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" + ds_sync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) func TestGetWhenKeyNotPresent(t *testing.T) { diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index b06f56c11..5c2366207 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -5,11 +5,11 @@ import ( "sync/atomic" "time" - "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" bloom "gx/ipfs/QmXqKGu7QzfRzFC4yd5aL9sThYx22vY163VGwmxfp5qGHk/bbloom" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) // bloomCached returns a Blockstore that caches Has requests using a Bloom diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index f2f1666fe..a0385a99c 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -6,12 +6,12 @@ import ( "testing" "time" - "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" context "context" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - dsq "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/query" - syncds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" + syncds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" ) func testBloomCached(ctx context.Context, bs Blockstore) (*bloomcache, error) { diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go index 94b52254e..0ce2b3fea 100644 --- a/blockstore/util/remove.go +++ b/blockstore/util/remove.go @@ -5,8 +5,8 @@ import ( "fmt" "io" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" bs "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/pin" From 7b3a3b536b555668c405fc98074d4eeedc2b8a09 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jan 2018 15:55:28 -0800 Subject: [PATCH 2106/3817] gx: mass update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-chunker@3778982739f1fffde380705e245f20a4d5070c96 --- chunker/rabin_test.go | 4 ++-- chunker/splitting_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 888b9ac2e..1d5702e38 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -6,8 +6,8 @@ import ( "io" "testing" - util "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" - blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + util "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) func TestRabinChunking(t *testing.T) { diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index 68df42803..c5ef621e0 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -5,7 +5,7 @@ import ( "io" "testing" - u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ) func randBuf(t *testing.T, size int) []byte { From 0a0b9070e16d622c5cb7397e5fd034d979b3d656 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jan 2018 15:55:28 -0800 Subject: [PATCH 2107/3817] gx: mass update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-ds-help@1880bdda2ec2d13f7cf9d93395c3afffa261acae --- datastore/dshelp/key.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 072d4fa9a..c1cf2c484 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -1,8 +1,8 @@ package dshelp import ( - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) From cb3474c1465f33743fd27b08d8715a4ea8a07f29 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jan 2018 15:55:28 -0800 Subject: [PATCH 2108/3817] gx: mass update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-exchange-offline@ee573eb86105abb7082cf09a68b55a01e4af893e --- exchange/offline/offline.go | 4 ++-- exchange/offline/offline_test.go | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index f104d20b1..35c38887f 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -7,9 +7,9 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 681ea2328..5bc2926c1 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -6,12 +6,12 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" - blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - u "gx/ipfs/QmPsAfmDBnZN3kZGSuNwvCNDZiHneERSKmRcFyG3UkvcT3/go-ipfs-util" - ds "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore" - ds_sync "gx/ipfs/QmdHG8MAuARdGHxx4rPQASLcvhz24fzjSQq7AJRAQEorq5/go-datastore/sync" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + ds_sync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) func TestBlockReturnsErr(t *testing.T) { From 7338fb157f61f38dbe9f75dbc4f1e61ea13e02e6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jan 2018 15:55:28 -0800 Subject: [PATCH 2109/3817] gx: mass update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-exchange-interface@51df926aeeebd1ee3a8ff8d317b61194089f625b --- exchange/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index 443672d89..c1dd11624 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -5,9 +5,9 @@ import ( "context" "io" - blocks "gx/ipfs/QmYsEQydGrsxNZfAiskvQ76N2xE9hDQtSAkRSynwMiUK3c/go-block-format" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) // Any type that implements exchange.Interface may be used as an IPFS block From f10a09d9b25f8ec1a43f7e93f9fe182ca5ef82ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 14 Dec 2017 23:55:24 +0100 Subject: [PATCH 2110/3817] coreapi: Basic object API implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@52f5b7ce1f8cc0503ec9d9ce070afdee7997049c --- coreiface/interface.go | 44 ++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 720f935e2..2c31d01b1 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -191,27 +191,29 @@ type KeyAPI interface { Remove(ctx context.Context, name string) (Path, error) } -// type ObjectAPI interface { -// New() (cid.Cid, Object) -// Get(string) (Object, error) -// Links(string) ([]*Link, error) -// Data(string) (Reader, error) -// Stat(string) (ObjectStat, error) -// Put(Object) (cid.Cid, error) -// SetData(string, Reader) (cid.Cid, error) -// AppendData(string, Data) (cid.Cid, error) -// AddLink(string, string, string) (cid.Cid, error) -// RmLink(string, string) (cid.Cid, error) -// } - -// type ObjectStat struct { -// Cid cid.Cid -// NumLinks int -// BlockSize int -// LinksSize int -// DataSize int -// CumulativeSize int -// } +//TODO: Should this use paths instead of cids? +type ObjectAPI interface { + New(ctx context.Context) (Node, error) + Put(context.Context, Node) error + Get(context.Context, Path) (Node, error) + Data(context.Context, Path) (io.Reader, error) + Links(context.Context, Path) ([]*Link, error) + Stat(context.Context, Path) (*ObjectStat, error) + + AddLink(ctx context.Context, base Path, name string, child Path, create bool) (Node, error) //TODO: make create optional + RmLink(context.Context, Path, string) (Node, error) + AppendData(context.Context, Path, io.Reader) (Node, error) + SetData(context.Context, Path, io.Reader) (Node, error) +} + +type ObjectStat struct { + Cid *cid.Cid + NumLinks int + BlockSize int + LinksSize int + DataSize int + CumulativeSize int +} var ErrIsDir = errors.New("object is a directory") var ErrOffline = errors.New("can't resolve, ipfs node is offline") From 85688ca1e91bcfa3cc7441142e5391b760b2b43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 22 Dec 2017 17:22:53 +0100 Subject: [PATCH 2111/3817] coreapi: Object api review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@3b4b26deb1bc75aefe85df809afca5e3e09f71c6 --- coreiface/interface.go | 10 +++++-- coreiface/options/object.go | 56 +++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 coreiface/options/object.go diff --git a/coreiface/interface.go b/coreiface/interface.go index 2c31d01b1..db993a5c3 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -193,14 +193,18 @@ type KeyAPI interface { //TODO: Should this use paths instead of cids? type ObjectAPI interface { - New(ctx context.Context) (Node, error) - Put(context.Context, Node) error + New(context.Context, ...options.ObjectNewOption) (Node, error) + WithType(string) options.ObjectNewOption + + Put(context.Context, Node) (Path, error) Get(context.Context, Path) (Node, error) Data(context.Context, Path) (io.Reader, error) Links(context.Context, Path) ([]*Link, error) Stat(context.Context, Path) (*ObjectStat, error) - AddLink(ctx context.Context, base Path, name string, child Path, create bool) (Node, error) //TODO: make create optional + AddLink(ctx context.Context, base Path, name string, child Path, opts ...options.ObjectAddLinkOption) (Node, error) + WithCreate(create bool) options.ObjectAddLinkOption + RmLink(context.Context, Path, string) (Node, error) AppendData(context.Context, Path, io.Reader) (Node, error) SetData(context.Context, Path, io.Reader) (Node, error) diff --git a/coreiface/options/object.go b/coreiface/options/object.go new file mode 100644 index 000000000..6a144ab2b --- /dev/null +++ b/coreiface/options/object.go @@ -0,0 +1,56 @@ +package options + +type ObjectNewSettings struct { + Type string +} + +type ObjectAddLinkSettings struct { + Create bool +} + +type ObjectNewOption func(*ObjectNewSettings) error +type ObjectAddLinkOption func(*ObjectAddLinkSettings) error + +func ObjectNewOptions(opts ...ObjectNewOption) (*ObjectNewSettings, error) { + options := &ObjectNewSettings{ + Type: "empty", + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +func ObjectAddLinkOptions(opts ...ObjectAddLinkOption) (*ObjectAddLinkSettings, error) { + options := &ObjectAddLinkSettings{ + Create: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type ObjectOptions struct{} + +func (api *ObjectOptions) WithType(t string) ObjectNewOption { + return func(settings *ObjectNewSettings) error { + settings.Type = t + return nil + } +} + +func (api *ObjectOptions) WithCreate(create bool) ObjectAddLinkOption { + return func(settings *ObjectAddLinkSettings) error { + settings.Create = create + return nil + } +} From 36545a88bcc2f636493de49939e8105d7fd1977c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 2 Jan 2018 21:22:20 +0100 Subject: [PATCH 2112/3817] coreapi: object docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@55c56578038a9e748aa996f7f9cb7dc1b546f1ac --- coreiface/interface.go | 55 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index db993a5c3..7809c8b99 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -191,31 +191,72 @@ type KeyAPI interface { Remove(ctx context.Context, name string) (Path, error) } -//TODO: Should this use paths instead of cids? +// ObjectAPI specifies the interface to MerkleDAG and contains useful utilities +// for manipulating MerkleDAG data structures. type ObjectAPI interface { + // New creates new, empty (by default) dag-node. New(context.Context, ...options.ObjectNewOption) (Node, error) + + // WithType is an option for New which allows to change the type of created + // dag node. + // + // Supported types: + // * 'empty' - Empty node + // * 'unixfs-dir' - Empty UnixFS directory WithType(string) options.ObjectNewOption + // Put imports the node into merkledag Put(context.Context, Node) (Path, error) + + // Get returns the node for the path Get(context.Context, Path) (Node, error) + + // Data returns reader for data of the node Data(context.Context, Path) (io.Reader, error) + + // Links returns lint or links the node contains Links(context.Context, Path) ([]*Link, error) + + // Stat returns information about the node Stat(context.Context, Path) (*ObjectStat, error) + // AddLink adds a link under the specified path. child path can point to a + // subdirectory within the patent which must be present (can be overridden + // with WithCreate option). AddLink(ctx context.Context, base Path, name string, child Path, opts ...options.ObjectAddLinkOption) (Node, error) + + // WithCreate is an option for AddLink which specifies whether create required + // directories for the child WithCreate(create bool) options.ObjectAddLinkOption - RmLink(context.Context, Path, string) (Node, error) + // RmLink removes a link from the node + RmLink(ctx context.Context, base Path, link string) (Node, error) + + // AppendData appends data to the node AppendData(context.Context, Path, io.Reader) (Node, error) + + // SetData sets the data contained in the node SetData(context.Context, Path, io.Reader) (Node, error) } +// ObjectStat provides information about dag nodes type ObjectStat struct { - Cid *cid.Cid - NumLinks int - BlockSize int - LinksSize int - DataSize int + // Cid is the CID of the node + Cid *cid.Cid + + // NumLinks is number of links the node contains + NumLinks int + + // BlockSize is size of the raw serialized node + BlockSize int + + // LinksSize is size of the links block section + LinksSize int + + // DataSize is the size of data block section + DataSize int + + // CumulativeSize is size of node CumulativeSize int } From 8162a16f7c173b07fa645d5a20121f1ad29e5198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 4 Jan 2018 17:20:16 +0100 Subject: [PATCH 2113/3817] coreapi: implement object.Put MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@104bc87d13e259e45b68c3d00231e140a7b88fb6 --- coreiface/interface.go | 15 +++++++++++++-- coreiface/options/object.go | 26 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 7809c8b99..3e0e3d460 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -64,6 +64,9 @@ type CoreAPI interface { // Key returns an implementation of Key API. Key() KeyAPI + // ObjectAPI returns an implementation of Object API + Object() ObjectAPI + // ResolvePath resolves the path using Unixfs resolver ResolvePath(context.Context, Path) (Path, error) @@ -205,8 +208,16 @@ type ObjectAPI interface { // * 'unixfs-dir' - Empty UnixFS directory WithType(string) options.ObjectNewOption - // Put imports the node into merkledag - Put(context.Context, Node) (Path, error) + // Put imports the data into merkledag + Put(context.Context, io.Reader, ...options.ObjectPutOption) (Path, error) + + // WithInputEnc is an option for Put which specifies the input encoding of the + // data. Default is "json". + // + // Supported encodings: + // * "protobuf" + // * "json" + WithInputEnc(e string) options.ObjectPutOption // Get returns the node for the path Get(context.Context, Path) (Node, error) diff --git a/coreiface/options/object.go b/coreiface/options/object.go index 6a144ab2b..fe86a1cde 100644 --- a/coreiface/options/object.go +++ b/coreiface/options/object.go @@ -4,11 +4,16 @@ type ObjectNewSettings struct { Type string } +type ObjectPutSettings struct { + InputEnc string +} + type ObjectAddLinkSettings struct { Create bool } type ObjectNewOption func(*ObjectNewSettings) error +type ObjectPutOption func(*ObjectPutSettings) error type ObjectAddLinkOption func(*ObjectAddLinkSettings) error func ObjectNewOptions(opts ...ObjectNewOption) (*ObjectNewSettings, error) { @@ -25,6 +30,20 @@ func ObjectNewOptions(opts ...ObjectNewOption) (*ObjectNewSettings, error) { return options, nil } +func ObjectPutOptions(opts ...ObjectPutOption) (*ObjectPutSettings, error) { + options := &ObjectPutSettings{ + InputEnc: "json", + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + func ObjectAddLinkOptions(opts ...ObjectAddLinkOption) (*ObjectAddLinkSettings, error) { options := &ObjectAddLinkSettings{ Create: false, @@ -48,6 +67,13 @@ func (api *ObjectOptions) WithType(t string) ObjectNewOption { } } +func (api *ObjectOptions) WithInputEnc(e string) ObjectPutOption { + return func(settings *ObjectPutSettings) error { + settings.InputEnc = e + return nil + } +} + func (api *ObjectOptions) WithCreate(create bool) ObjectAddLinkOption { return func(settings *ObjectAddLinkSettings) error { settings.Create = create From d878d812272fc70b7e3e799f139056885dab474f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 5 Jan 2018 01:13:07 +0100 Subject: [PATCH 2114/3817] coreapi: object API tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@858d49b07c296b36a58fa4d4469f5e08a9f9b5e7 --- coreiface/interface.go | 18 +++++++++++++----- coreiface/options/object.go | 9 +++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 3e0e3d460..9dca7f3c9 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -219,6 +219,14 @@ type ObjectAPI interface { // * "json" WithInputEnc(e string) options.ObjectPutOption + // WithDataType specifies the encoding of data field when using Josn or XML + // input encoding. + // + // Supported types: + // * "text" (default) + // * "base64" + WithDataType(t string) options.ObjectPutOption + // Get returns the node for the path Get(context.Context, Path) (Node, error) @@ -234,20 +242,20 @@ type ObjectAPI interface { // AddLink adds a link under the specified path. child path can point to a // subdirectory within the patent which must be present (can be overridden // with WithCreate option). - AddLink(ctx context.Context, base Path, name string, child Path, opts ...options.ObjectAddLinkOption) (Node, error) + AddLink(ctx context.Context, base Path, name string, child Path, opts ...options.ObjectAddLinkOption) (Path, error) // WithCreate is an option for AddLink which specifies whether create required // directories for the child WithCreate(create bool) options.ObjectAddLinkOption // RmLink removes a link from the node - RmLink(ctx context.Context, base Path, link string) (Node, error) + RmLink(ctx context.Context, base Path, link string) (Path, error) // AppendData appends data to the node - AppendData(context.Context, Path, io.Reader) (Node, error) + AppendData(context.Context, Path, io.Reader) (Path, error) // SetData sets the data contained in the node - SetData(context.Context, Path, io.Reader) (Node, error) + SetData(context.Context, Path, io.Reader) (Path, error) } // ObjectStat provides information about dag nodes @@ -267,7 +275,7 @@ type ObjectStat struct { // DataSize is the size of data block section DataSize int - // CumulativeSize is size of node + // CumulativeSize is size of the tree (BlockSize + link sizes) CumulativeSize int } diff --git a/coreiface/options/object.go b/coreiface/options/object.go index fe86a1cde..9c8c9a9dd 100644 --- a/coreiface/options/object.go +++ b/coreiface/options/object.go @@ -6,6 +6,7 @@ type ObjectNewSettings struct { type ObjectPutSettings struct { InputEnc string + DataType string } type ObjectAddLinkSettings struct { @@ -33,6 +34,7 @@ func ObjectNewOptions(opts ...ObjectNewOption) (*ObjectNewSettings, error) { func ObjectPutOptions(opts ...ObjectPutOption) (*ObjectPutSettings, error) { options := &ObjectPutSettings{ InputEnc: "json", + DataType: "text", } for _, opt := range opts { @@ -74,6 +76,13 @@ func (api *ObjectOptions) WithInputEnc(e string) ObjectPutOption { } } +func (api *ObjectOptions) WithDataType(t string) ObjectPutOption { + return func(settings *ObjectPutSettings) error { + settings.DataType = t + return nil + } +} + func (api *ObjectOptions) WithCreate(create bool) ObjectAddLinkOption { return func(settings *ObjectAddLinkSettings) error { settings.Create = create From b87a1b9994009e70cfe8218c668fab3cc5f0f78d Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Sat, 20 Jan 2018 17:25:55 -0500 Subject: [PATCH 2115/3817] Update ipns validator License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@8e71a4ffdaf238757874d0dec1d733ec7a7fe7a1 --- namesys/ipns_validate_test.go | 93 +++++++++++++++++++++++++++++++++++ namesys/publisher.go | 32 +++++++++++- 2 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 namesys/ipns_validate_test.go diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go new file mode 100644 index 000000000..d75fa6cb7 --- /dev/null +++ b/namesys/ipns_validate_test.go @@ -0,0 +1,93 @@ +package namesys + +import ( + "testing" + "time" + + path "github.com/ipfs/go-ipfs/path" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + record "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record" +) + +func TestValidation(t *testing.T) { + // Create a record validator + validator := make(record.Validator) + validator["ipns"] = &record.ValidChecker{ValidateIpnsRecord, true} + + // Generate a key for signing the records + r := u.NewSeededRand(15) // generate deterministic keypair + priv, pubk, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, r) + if err != nil { + t.Fatal(err) + } + + // Create entry with expiry in one hour + ts := time.Now() + entry, err := CreateRoutingEntryData(priv, path.Path("foo"), 1, ts.Add(time.Hour)) + if err != nil { + t.Fatal(err) + } + + // Get IPNS record path + pubkb, err := pubk.Bytes() + if err != nil { + t.Fatal(err) + } + pubkh := u.Hash(pubkb).B58String() + ipnsPath := "/ipns/" + pubkh + + val, err := proto.Marshal(entry) + if err != nil { + t.Fatal(err) + } + + // Create the record + r1, err := record.MakePutRecord(priv, ipnsPath, val, true) + + // Validate the record + err = validator.VerifyRecord(r1) + if err != nil { + t.Fatal(err) + } + + // Create IPNS record path with a different key + _, pubk2, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, r) + if err != nil { + t.Fatal(err) + } + pubkb2, err := pubk2.Bytes() + if err != nil { + t.Fatal(err) + } + pubkh2 := u.Hash(pubkb2).B58String() + ipnsWrongPath := "/ipns/" + pubkh2 + + r2, err := record.MakePutRecord(priv, ipnsWrongPath, val, true) + + // Record should fail validation because path doesn't match author + err = validator.VerifyRecord(r2) + if err != ErrInvalidAuthor { + t.Fatal("ValidateIpnsRecord should have returned ErrInvalidAuthor") + } + + // Create expired entry + expired, err := CreateRoutingEntryData(priv, path.Path("foo"), 1, ts.Add(-1*time.Hour)) + if err != nil { + t.Fatal(err) + } + valExp, err := proto.Marshal(expired) + if err != nil { + t.Fatal(err) + } + + // Create record with the expired entry + r3, err := record.MakePutRecord(priv, ipnsPath, valExp, true) + + // Record should fail validation because entry is expired + err = validator.VerifyRecord(r3) + if err != ErrExpiredRecord { + t.Fatal("ValidateIpnsRecord should have returned ErrExpiredRecord") + } +} diff --git a/namesys/publisher.go b/namesys/publisher.go index 6d033d502..4861a1eae 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "strings" "time" pb "github.com/ipfs/go-ipfs/namesys/pb" @@ -31,6 +32,14 @@ var ErrExpiredRecord = errors.New("expired record") // unknown validity type. var ErrUnrecognizedValidity = errors.New("unrecognized validity type") +// ErrInvalidAuthor is returned when an IpnsRecord has an +// author that does not match the IPNS path +var ErrInvalidAuthor = errors.New("author does not match path") + +// ErrInvalidPath should be returned when an ipns record path +// is not in a valid format +var ErrInvalidPath = errors.New("record path invalid") + const PublishPutValTimeout = time.Minute const DefaultRecordTTL = 24 * time.Hour @@ -295,12 +304,31 @@ func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) { // ValidateIpnsRecord implements ValidatorFunc and verifies that the // given 'val' is an IpnsEntry and that that entry is valid. -func ValidateIpnsRecord(k string, val []byte) error { +func ValidateIpnsRecord(r *record.ValidationRecord) error { + if r.Namespace != "ipns" { + return ErrInvalidPath + } + entry := new(pb.IpnsEntry) - err := proto.Unmarshal(val, entry) + err := proto.Unmarshal(r.Value, entry) if err != nil { return err } + + // Note: The DHT will actually check the signature so we don't + // need to do that here + + // Author in key must match author in record + parts := strings.Split(r.Key, "/") + pid, err := peer.IDB58Decode(parts[0]) + if err != nil { + return ErrInvalidAuthor + } + if string(pid) != string(r.Author) { + return ErrInvalidAuthor + } + + // Check that record has not expired switch entry.GetValidityType() { case pb.IpnsEntry_EOL: t, err := u.ParseRFC3339(string(entry.GetValidity())) From 2bd9c1713931b57131fcb52ac0bd6f273ebf729e Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Sun, 21 Jan 2018 09:51:29 -0500 Subject: [PATCH 2116/3817] Remove unneccesary split in IpnsValidator License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@4b48de6de6e8830892677480c488169202600fc0 --- namesys/ipns_validate_test.go | 91 ++++++++++++++++++++++++++--------- namesys/publisher.go | 4 +- 2 files changed, 68 insertions(+), 27 deletions(-) diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index d75fa6cb7..8e9adc781 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -1,6 +1,7 @@ package namesys import ( + "io" "testing" "time" @@ -18,10 +19,7 @@ func TestValidation(t *testing.T) { // Generate a key for signing the records r := u.NewSeededRand(15) // generate deterministic keypair - priv, pubk, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, r) - if err != nil { - t.Fatal(err) - } + priv, ipnsPath := genKeys(t, r) // Create entry with expiry in one hour ts := time.Now() @@ -30,64 +28,109 @@ func TestValidation(t *testing.T) { t.Fatal(err) } - // Get IPNS record path - pubkb, err := pubk.Bytes() + val, err := proto.Marshal(entry) if err != nil { t.Fatal(err) } - pubkh := u.Hash(pubkb).B58String() - ipnsPath := "/ipns/" + pubkh - val, err := proto.Marshal(entry) + // Create the record + rec, err := record.MakePutRecord(priv, ipnsPath, val, true) if err != nil { t.Fatal(err) } - // Create the record - r1, err := record.MakePutRecord(priv, ipnsPath, val, true) - // Validate the record - err = validator.VerifyRecord(r1) + err = validator.VerifyRecord(rec) if err != nil { t.Fatal(err) } + // Create IPNS record path with a different key - _, pubk2, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, r) + _, ipnsWrongAuthor := genKeys(t, r) + wrongAuthorRec, err := record.MakePutRecord(priv, ipnsWrongAuthor, val, true) if err != nil { t.Fatal(err) } - pubkb2, err := pubk2.Bytes() + + // Record should fail validation because path doesn't match author + err = validator.VerifyRecord(wrongAuthorRec) + if err != ErrInvalidAuthor { + t.Fatal("ValidateIpnsRecord should have returned ErrInvalidAuthor") + } + + + // Create IPNS record path with extra path components after author + extraPath := ipnsPath + "/some/path" + extraPathRec, err := record.MakePutRecord(priv, extraPath, val, true) if err != nil { t.Fatal(err) } - pubkh2 := u.Hash(pubkb2).B58String() - ipnsWrongPath := "/ipns/" + pubkh2 - r2, err := record.MakePutRecord(priv, ipnsWrongPath, val, true) + // Record should fail validation because path has extra components after author + err = validator.VerifyRecord(extraPathRec) + if err != ErrInvalidAuthor { + t.Fatal("ValidateIpnsRecord should have returned ErrInvalidAuthor") + } - // Record should fail validation because path doesn't match author - err = validator.VerifyRecord(r2) + + // Create unsigned IPNS record + unsignedRec, err := record.MakePutRecord(priv, ipnsPath, val, false) + if err != nil { + t.Fatal(err) + } + + // Record should fail validation because IPNS records require signature + err = validator.VerifyRecord(unsignedRec) + if err != ErrInvalidAuthor { + t.Fatal("ValidateIpnsRecord should have returned ErrInvalidAuthor") + } + + + // Create unsigned IPNS record with no author + unsignedRecNoAuthor, err := record.MakePutRecord(priv, ipnsPath, val, false) + if err != nil { + t.Fatal(err) + } + noAuth := "" + unsignedRecNoAuthor.Author = &noAuth + + // Record should fail validation because IPNS records require author + err = validator.VerifyRecord(unsignedRecNoAuthor) if err != ErrInvalidAuthor { t.Fatal("ValidateIpnsRecord should have returned ErrInvalidAuthor") } + // Create expired entry - expired, err := CreateRoutingEntryData(priv, path.Path("foo"), 1, ts.Add(-1*time.Hour)) + expiredEntry, err := CreateRoutingEntryData(priv, path.Path("foo"), 1, ts.Add(-1*time.Hour)) if err != nil { t.Fatal(err) } - valExp, err := proto.Marshal(expired) + valExp, err := proto.Marshal(expiredEntry) if err != nil { t.Fatal(err) } // Create record with the expired entry - r3, err := record.MakePutRecord(priv, ipnsPath, valExp, true) + expiredRec, err := record.MakePutRecord(priv, ipnsPath, valExp, true) // Record should fail validation because entry is expired - err = validator.VerifyRecord(r3) + err = validator.VerifyRecord(expiredRec) if err != ErrExpiredRecord { t.Fatal("ValidateIpnsRecord should have returned ErrExpiredRecord") } } + +func genKeys(t *testing.T, r io.Reader) (ci.PrivKey, string) { + priv, pubk, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, r) + if err != nil { + t.Fatal(err) + } + pubkb, err := pubk.Bytes() + if err != nil { + t.Fatal(err) + } + p := "/ipns/" + u.Hash(pubkb).B58String() + return priv, p +} diff --git a/namesys/publisher.go b/namesys/publisher.go index 4861a1eae..48d32bcc1 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -5,7 +5,6 @@ import ( "context" "errors" "fmt" - "strings" "time" pb "github.com/ipfs/go-ipfs/namesys/pb" @@ -319,8 +318,7 @@ func ValidateIpnsRecord(r *record.ValidationRecord) error { // need to do that here // Author in key must match author in record - parts := strings.Split(r.Key, "/") - pid, err := peer.IDB58Decode(parts[0]) + pid, err := peer.IDB58Decode(r.Key) if err != nil { return ErrInvalidAuthor } From 5e1e5f3c47e937c72b7d0435d3a2b352551240f5 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Sun, 21 Jan 2018 17:48:57 -0500 Subject: [PATCH 2117/3817] go fmt License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@4be57d9a4fe84a059451d8a1cacf79f1d868408c --- namesys/ipns_validate_test.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index 8e9adc781..2d46c7a82 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -45,7 +45,6 @@ func TestValidation(t *testing.T) { t.Fatal(err) } - // Create IPNS record path with a different key _, ipnsWrongAuthor := genKeys(t, r) wrongAuthorRec, err := record.MakePutRecord(priv, ipnsWrongAuthor, val, true) @@ -59,7 +58,6 @@ func TestValidation(t *testing.T) { t.Fatal("ValidateIpnsRecord should have returned ErrInvalidAuthor") } - // Create IPNS record path with extra path components after author extraPath := ipnsPath + "/some/path" extraPathRec, err := record.MakePutRecord(priv, extraPath, val, true) @@ -73,7 +71,6 @@ func TestValidation(t *testing.T) { t.Fatal("ValidateIpnsRecord should have returned ErrInvalidAuthor") } - // Create unsigned IPNS record unsignedRec, err := record.MakePutRecord(priv, ipnsPath, val, false) if err != nil { @@ -86,7 +83,6 @@ func TestValidation(t *testing.T) { t.Fatal("ValidateIpnsRecord should have returned ErrInvalidAuthor") } - // Create unsigned IPNS record with no author unsignedRecNoAuthor, err := record.MakePutRecord(priv, ipnsPath, val, false) if err != nil { @@ -101,7 +97,6 @@ func TestValidation(t *testing.T) { t.Fatal("ValidateIpnsRecord should have returned ErrInvalidAuthor") } - // Create expired entry expiredEntry, err := CreateRoutingEntryData(priv, path.Path("foo"), 1, ts.Add(-1*time.Hour)) if err != nil { From 7ed9b3c6ff4c84cdc689443b9605cdc451ca8ee8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 25 Jan 2018 12:21:08 -0800 Subject: [PATCH 2118/3817] dagmodifier: remove useless offset update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@3796a7dad5bacccdabcc2f5f7342783227c20556 --- unixfs/mod/dagmodifier.go | 1 - 1 file changed, 1 deletion(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index b6ae06fa1..3ac7d77c0 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -330,7 +330,6 @@ func (dm *DagModifier) modifyDag(n node.Node, offset uint64, data io.Reader) (*c return nil, false, err } - offset += bs node.Links()[i].Cid = k // Recache serialized node From 57c918b713b7c40b90a51fc2ff12c9dc35ff8ea0 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Tue, 23 Jan 2018 08:25:40 -0500 Subject: [PATCH 2119/3817] Fix ipns validator key parsing License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@f447fab0a12625c817a83245ae2aea8ef0291eeb --- namesys/ipns_validate_test.go | 11 ++++++----- namesys/publisher.go | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index 2d46c7a82..fa7888103 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -8,6 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" record "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record" ) @@ -45,7 +46,7 @@ func TestValidation(t *testing.T) { t.Fatal(err) } - // Create IPNS record path with a different key + // Create IPNS record path with a different private key _, ipnsWrongAuthor := genKeys(t, r) wrongAuthorRec, err := record.MakePutRecord(priv, ipnsWrongAuthor, val, true) if err != nil { @@ -118,14 +119,14 @@ func TestValidation(t *testing.T) { } func genKeys(t *testing.T, r io.Reader) (ci.PrivKey, string) { - priv, pubk, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, r) + priv, _, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, r) if err != nil { t.Fatal(err) } - pubkb, err := pubk.Bytes() + id, err := peer.IDFromPrivateKey(priv) if err != nil { t.Fatal(err) } - p := "/ipns/" + u.Hash(pubkb).B58String() - return priv, p + _, ipnsKey := IpnsKeysForID(id) + return priv, ipnsKey } diff --git a/namesys/publisher.go b/namesys/publisher.go index 48d32bcc1..3dc38c4d1 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -318,7 +318,7 @@ func ValidateIpnsRecord(r *record.ValidationRecord) error { // need to do that here // Author in key must match author in record - pid, err := peer.IDB58Decode(r.Key) + pid, err := peer.IDFromString(r.Key) if err != nil { return ErrInvalidAuthor } From 3103a10f1e4ae425d5719619c38c9804bbb74c2d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 25 Jan 2018 12:21:51 -0800 Subject: [PATCH 2120/3817] merkledag: switch to new dag interface Also: * Update the blockstore/blockservice methods to match. * Construct a new temporary offline dag instead of having a GetOfflineLinkService method. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@3e19ae340e325f096cec1e63949c0471fae6f994 --- mfs/dir.go | 12 ++++++------ mfs/fd.go | 2 +- mfs/file.go | 4 ++-- mfs/mfs_test.go | 16 ++++++++-------- mfs/system.go | 8 ++++---- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 250219d75..5ad39d205 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -24,7 +24,7 @@ var ErrInvalidChild = errors.New("invalid child node") var ErrDirExists = errors.New("directory already has entry by that name") type Directory struct { - dserv dag.DAGService + dserv node.DAGService parent childCloser childDirs map[string]*Directory @@ -40,7 +40,7 @@ type Directory struct { name string } -func NewDirectory(ctx context.Context, name string, node node.Node, parent childCloser, dserv dag.DAGService) (*Directory, error) { +func NewDirectory(ctx context.Context, name string, node node.Node, parent childCloser, dserv node.DAGService) (*Directory, error) { db, err := uio.NewDirectoryFromNode(dserv, node) if err != nil { return nil, err @@ -104,7 +104,7 @@ func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) { return nil, err } - _, err = d.dserv.Add(nd) + err = d.dserv.Add(d.ctx, nd) if err != nil { return nil, err } @@ -306,7 +306,7 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { ndir := ft.EmptyDirNode() ndir.SetPrefix(d.GetPrefix()) - _, err = d.dserv.Add(ndir) + err = d.dserv.Add(d.ctx, ndir) if err != nil { return nil, err } @@ -354,7 +354,7 @@ func (d *Directory) AddChild(name string, nd node.Node) error { return ErrDirExists } - _, err = d.dserv.Add(nd) + err = d.dserv.Add(d.ctx, nd) if err != nil { return err } @@ -420,7 +420,7 @@ func (d *Directory) GetNode() (node.Node, error) { return nil, err } - _, err = d.dserv.Add(nd) + err = d.dserv.Add(d.ctx, nd) if err != nil { return nil, err } diff --git a/mfs/fd.go b/mfs/fd.go index 9eb369316..a93a9bb42 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -122,7 +122,7 @@ func (fi *fileDescriptor) flushUp(fullsync bool) error { return err } - _, err = fi.inode.dserv.Add(nd) + err = fi.inode.dserv.Add(context.TODO(), nd) if err != nil { return err } diff --git a/mfs/file.go b/mfs/file.go index aa870551a..033965fa2 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -20,7 +20,7 @@ type File struct { desclock sync.RWMutex - dserv dag.DAGService + dserv node.DAGService node node.Node nodelk sync.Mutex @@ -29,7 +29,7 @@ type File struct { // NewFile returns a NewFile object with the given parameters. If the // Cid version is non-zero RawLeaves will be enabled. -func NewFile(name string, node node.Node, parent childCloser, dserv dag.DAGService) (*File, error) { +func NewFile(name string, node node.Node, parent childCloser, dserv node.DAGService) (*File, error) { fi := &File{ dserv: dserv, parent: parent, diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index b9ec1b4e4..3745d8887 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -35,19 +35,19 @@ func emptyDirNode() *dag.ProtoNode { return dag.NodeWithData(ft.FolderPBData()) } -func getDagserv(t *testing.T) dag.DAGService { +func getDagserv(t *testing.T) node.DAGService { db := dssync.MutexWrap(ds.NewMapDatastore()) bs := bstore.NewBlockstore(db) blockserv := bserv.New(bs, offline.Exchange(bs)) return dag.NewDAGService(blockserv) } -func getRandFile(t *testing.T, ds dag.DAGService, size int64) node.Node { +func getRandFile(t *testing.T, ds node.DAGService, size int64) node.Node { r := io.LimitReader(u.NewTimeSeededRand(), size) return fileNodeFromReader(t, ds, r) } -func fileNodeFromReader(t *testing.T, ds dag.DAGService, r io.Reader) node.Node { +func fileNodeFromReader(t *testing.T, ds node.DAGService, r io.Reader) node.Node { nd, err := importer.BuildDagFromReader(ds, chunk.DefaultSplitter(r)) if err != nil { t.Fatal(err) @@ -128,7 +128,7 @@ func compStrArrs(a, b []string) bool { return true } -func assertFileAtPath(ds dag.DAGService, root *Directory, expn node.Node, pth string) error { +func assertFileAtPath(ds node.DAGService, root *Directory, expn node.Node, pth string) error { exp, ok := expn.(*dag.ProtoNode) if !ok { return dag.ErrNotProtobuf @@ -182,7 +182,7 @@ func assertFileAtPath(ds dag.DAGService, root *Directory, expn node.Node, pth st return nil } -func catNode(ds dag.DAGService, nd *dag.ProtoNode) ([]byte, error) { +func catNode(ds node.DAGService, nd *dag.ProtoNode) ([]byte, error) { r, err := uio.NewDagReader(context.TODO(), nd, ds) if err != nil { return nil, err @@ -192,7 +192,7 @@ func catNode(ds dag.DAGService, nd *dag.ProtoNode) ([]byte, error) { return ioutil.ReadAll(r) } -func setupRoot(ctx context.Context, t *testing.T) (dag.DAGService, *Root) { +func setupRoot(ctx context.Context, t *testing.T) (node.DAGService, *Root) { ds := getDagserv(t) root := emptyDirNode() @@ -284,7 +284,7 @@ func TestDirectoryLoadFromDag(t *testing.T) { rootdir := rt.GetValue().(*Directory) nd := getRandFile(t, ds, 1000) - _, err := ds.Add(nd) + err := ds.Add(ctx, nd) if err != nil { t.Fatal(err) } @@ -292,7 +292,7 @@ func TestDirectoryLoadFromDag(t *testing.T) { fihash := nd.Cid() dir := emptyDirNode() - _, err = ds.Add(dir) + err = ds.Add(ctx, dir) if err != nil { t.Fatal(err) } diff --git a/mfs/system.go b/mfs/system.go index 28dbe8aec..5c30db57c 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -58,7 +58,7 @@ type Root struct { repub *Republisher - dserv dag.DAGService + dserv node.DAGService Type string } @@ -67,7 +67,7 @@ type Root struct { type PubFunc func(context.Context, *cid.Cid) error // NewRoot creates a new Root and starts up a republisher routine for it. -func NewRoot(parent context.Context, ds dag.DAGService, node *dag.ProtoNode, pf PubFunc) (*Root, error) { +func NewRoot(parent context.Context, ds node.DAGService, node *dag.ProtoNode, pf PubFunc) (*Root, error) { var repub *Republisher if pf != nil { @@ -160,13 +160,13 @@ func (kr *Root) FlushMemFree(ctx context.Context) error { // closeChild implements the childCloser interface, and signals to the publisher that // there are changes ready to be published. func (kr *Root) closeChild(name string, nd node.Node, sync bool) error { - c, err := kr.dserv.Add(nd) + err := kr.dserv.Add(context.TODO(), nd) if err != nil { return err } if kr.repub != nil { - kr.repub.Update(c) + kr.repub.Update(nd.Cid()) } return nil } From ae540120c7f8d1963f4f2214c77b4d575096900c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 25 Jan 2018 12:21:51 -0800 Subject: [PATCH 2121/3817] merkledag: switch to new dag interface Also: * Update the blockstore/blockservice methods to match. * Construct a new temporary offline dag instead of having a GetOfflineLinkService method. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@2fbf40a057c66542458e0a7e43591b78e6139f30 --- ipld/merkledag/batch.go | 99 -------- ipld/merkledag/merkledag.go | 287 ++++++----------------- ipld/merkledag/merkledag_test.go | 56 +++-- ipld/merkledag/node.go | 6 +- ipld/merkledag/node_test.go | 19 +- ipld/merkledag/test/utils.go | 4 +- ipld/merkledag/traverse/traverse_test.go | 15 +- ipld/merkledag/utils/diff.go | 6 +- ipld/merkledag/utils/diffenum_test.go | 24 +- ipld/merkledag/utils/utils.go | 47 ++-- ipld/merkledag/utils/utils_test.go | 18 +- 11 files changed, 187 insertions(+), 394 deletions(-) delete mode 100644 ipld/merkledag/batch.go diff --git a/ipld/merkledag/batch.go b/ipld/merkledag/batch.go deleted file mode 100644 index a6ee02f06..000000000 --- a/ipld/merkledag/batch.go +++ /dev/null @@ -1,99 +0,0 @@ -package merkledag - -import ( - "runtime" - - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" -) - -// ParallelBatchCommits is the number of batch commits that can be in-flight before blocking. -// TODO(#4299): Experiment with multiple datastores, storage devices, and CPUs to find -// the right value/formula. -var ParallelBatchCommits = runtime.NumCPU() * 2 - -// Batch is a buffer for batching adds to a dag. -type Batch struct { - ds *dagService - - activeCommits int - commitError error - commitResults chan error - - blocks []blocks.Block - size int - - MaxSize int - MaxBlocks int -} - -func (t *Batch) processResults() { - for t.activeCommits > 0 && t.commitError == nil { - select { - case err := <-t.commitResults: - t.activeCommits-- - if err != nil { - t.commitError = err - } - default: - return - } - } -} - -func (t *Batch) asyncCommit() { - numBlocks := len(t.blocks) - if numBlocks == 0 || t.commitError != nil { - return - } - if t.activeCommits >= ParallelBatchCommits { - err := <-t.commitResults - t.activeCommits-- - - if err != nil { - t.commitError = err - return - } - } - go func(b []blocks.Block) { - _, err := t.ds.Blocks.AddBlocks(b) - t.commitResults <- err - }(t.blocks) - - t.activeCommits++ - t.blocks = make([]blocks.Block, 0, numBlocks) - t.size = 0 - - return -} - -// Add adds a node to the batch and commits the batch if necessary. -func (t *Batch) Add(nd node.Node) (*cid.Cid, error) { - // Not strictly necessary but allows us to catch errors early. - t.processResults() - if t.commitError != nil { - return nil, t.commitError - } - - t.blocks = append(t.blocks, nd) - t.size += len(nd.RawData()) - if t.size > t.MaxSize || len(t.blocks) > t.MaxBlocks { - t.asyncCommit() - } - return nd.Cid(), t.commitError -} - -// Commit commits batched nodes. -func (t *Batch) Commit() error { - t.asyncCommit() - for t.activeCommits > 0 && t.commitError == nil { - err := <-t.commitResults - t.activeCommits-- - if err != nil { - t.commitError = err - } - } - - return t.commitError -} diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 8f89b137e..33b097510 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -7,11 +7,11 @@ import ( "sync" bserv "github.com/ipfs/go-ipfs/blockservice" - offline "github.com/ipfs/go-ipfs/exchange/offline" ipldcbor "gx/ipfs/QmNRz7BDWfdFNVLt7AVvmRefkrURD25EeoipcXqo6yoXU1/go-ipld-cbor" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) // TODO: We should move these registrations elsewhere. Really, most of the IPLD @@ -23,37 +23,7 @@ func init() { node.Register(cid.DagCBOR, ipldcbor.DecodeBlock) } -var ErrNotFound = fmt.Errorf("merkledag: not found") - -// DAGService is an IPFS Merkle DAG service. -type DAGService interface { - // Add adds the node to the DAGService - Add(node.Node) (*cid.Cid, error) - // Get gets the node the from the DAGService - Get(context.Context, *cid.Cid) (node.Node, error) - // Remove removes the node from the DAGService - Remove(node.Node) error - - // GetMany returns a channel of NodeOption given - // a set of CIDs. - GetMany(context.Context, []*cid.Cid) <-chan *NodeOption - - // Batch is a buffer for batching adds to a dag. - Batch() *Batch - - LinkService -} - -type LinkService interface { - // GetLinks return all links for a node. The complete node does not - // necessarily have to exist locally, or at all. For example, raw - // leaves cannot possibly have links so there is no need to look - // at the node. - GetLinks(context.Context, *cid.Cid) ([]*node.Link, error) - - GetOfflineLinkService() LinkService -} - +// NewDAGService constructs a new DAGService (using the default implementation). func NewDAGService(bs bserv.BlockService) *dagService { return &dagService{Blocks: bs} } @@ -68,25 +38,20 @@ type dagService struct { } // Add adds a node to the dagService, storing the block in the BlockService -func (n *dagService) Add(nd node.Node) (*cid.Cid, error) { +func (n *dagService) Add(ctx context.Context, nd node.Node) error { if n == nil { // FIXME remove this assertion. protect with constructor invariant - return nil, fmt.Errorf("dagService is nil") + return fmt.Errorf("dagService is nil") } return n.Blocks.AddBlock(nd) } -func (n *dagService) Batch() *Batch { - return &Batch{ - ds: n, - commitResults: make(chan error, ParallelBatchCommits), - MaxSize: 8 << 20, - - // By default, only batch up to 128 nodes at a time. - // The current implementation of flatfs opens this many file - // descriptors at the same time for the optimized batch write. - MaxBlocks: 128, +func (n *dagService) AddMany(ctx context.Context, nds []node.Node) error { + blks := make([]blocks.Block, len(nds)) + for i, nd := range nds { + blks[i] = nd } + return n.Blocks.AddBlocks(blks) } // Get retrieves a node from the dagService, fetching the block in the BlockService @@ -101,7 +66,7 @@ func (n *dagService) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { b, err := n.Blocks.GetBlock(ctx, c) if err != nil { if err == bserv.ErrNotFound { - return nil, ErrNotFound + return nil, node.ErrNotFound } return nil, fmt.Errorf("Failed to get block for %s: %v", c, err) } @@ -122,17 +87,23 @@ func (n *dagService) GetLinks(ctx context.Context, c *cid.Cid) ([]*node.Link, er return node.Links(), nil } -func (n *dagService) GetOfflineLinkService() LinkService { - if n.Blocks.Exchange().IsOnline() { - bsrv := bserv.New(n.Blocks.Blockstore(), offline.Exchange(n.Blocks.Blockstore())) - return NewDAGService(bsrv) - } else { - return n - } +func (n *dagService) Remove(ctx context.Context, c *cid.Cid) error { + return n.Blocks.DeleteBlock(c) } -func (n *dagService) Remove(nd node.Node) error { - return n.Blocks.DeleteBlock(nd) +// RemoveMany removes multiple nodes from the DAG. It will likely be faster than +// removing them individually. +// +// This operation is not atomic. If it returns an error, some nodes may or may +// not have been removed. +func (n *dagService) RemoveMany(ctx context.Context, cids []*cid.Cid) error { + // TODO(#4608): make this batch all the way down. + for _, c := range cids { + if err := n.Blocks.DeleteBlock(c); err != nil { + return err + } + } + return nil } // GetLinksDirect creates a function to get the links for a node, from @@ -140,14 +111,14 @@ func (n *dagService) Remove(nd node.Node) error { // locally (and can not be retrieved) an error will be returned. func GetLinksDirect(serv node.NodeGetter) GetLinks { return func(ctx context.Context, c *cid.Cid) ([]*node.Link, error) { - node, err := serv.Get(ctx, c) + nd, err := serv.Get(ctx, c) if err != nil { if err == bserv.ErrNotFound { - err = ErrNotFound + err = node.ErrNotFound } return nil, err } - return node.Links(), nil + return nd.Links(), nil } } @@ -155,11 +126,12 @@ type sesGetter struct { bs *bserv.Session } +// Get gets a single node from the DAG. func (sg *sesGetter) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { blk, err := sg.bs.GetBlock(ctx, c) switch err { case bserv.ErrNotFound: - return nil, ErrNotFound + return nil, node.ErrNotFound default: return nil, err case nil: @@ -169,8 +141,13 @@ func (sg *sesGetter) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { return node.Decode(blk) } +// GetMany gets many nodes at once, batching the request if possible. +func (sg *sesGetter) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *node.NodeOption { + return getNodesFromBG(ctx, sg.bs, keys) +} + // FetchGraph fetches all nodes that are children of the given node -func FetchGraph(ctx context.Context, root *cid.Cid, serv DAGService) error { +func FetchGraph(ctx context.Context, root *cid.Cid, serv node.DAGService) error { var ng node.NodeGetter = serv ds, ok := serv.(*dagService) if ok { @@ -205,14 +182,18 @@ func FindLinks(links []*cid.Cid, c *cid.Cid, start int) []int { return out } -type NodeOption struct { - Node node.Node - Err error +// GetMany gets many nodes from the DAG at once. +// +// This method may not return all requested nodes (and may or may not return an +// error indicating that it failed to do so. It is up to the caller to verify +// that it received all nodes. +func (n *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *node.NodeOption { + return getNodesFromBG(ctx, n.Blocks, keys) } -func (ds *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *NodeOption { - out := make(chan *NodeOption, len(keys)) - blocks := ds.Blocks.GetBlocks(ctx, keys) +func getNodesFromBG(ctx context.Context, bs bserv.BlockGetter, keys []*cid.Cid) <-chan *node.NodeOption { + out := make(chan *node.NodeOption, len(keys)) + blocks := bs.GetBlocks(ctx, keys) var count int go func() { @@ -222,182 +203,43 @@ func (ds *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *Node case b, ok := <-blocks: if !ok { if count != len(keys) { - out <- &NodeOption{Err: fmt.Errorf("failed to fetch all nodes")} + out <- &node.NodeOption{Err: fmt.Errorf("failed to fetch all nodes")} } return } nd, err := node.Decode(b) if err != nil { - out <- &NodeOption{Err: err} + out <- &node.NodeOption{Err: err} return } - out <- &NodeOption{Node: nd} + out <- &node.NodeOption{Node: nd} count++ case <-ctx.Done(): - out <- &NodeOption{Err: ctx.Err()} - return - } - } - }() - return out -} - -// GetDAG will fill out all of the links of the given Node. -// It returns a channel of nodes, which the caller can receive -// all the child nodes of 'root' on, in proper order. -func GetDAG(ctx context.Context, ds DAGService, root node.Node) []NodeGetter { - var cids []*cid.Cid - for _, lnk := range root.Links() { - cids = append(cids, lnk.Cid) - } - - return GetNodes(ctx, ds, cids) -} - -// GetNodes returns an array of 'NodeGetter' promises, with each corresponding -// to the key with the same index as the passed in keys -func GetNodes(ctx context.Context, ds DAGService, keys []*cid.Cid) []NodeGetter { - - // Early out if no work to do - if len(keys) == 0 { - return nil - } - - promises := make([]NodeGetter, len(keys)) - for i := range keys { - promises[i] = newNodePromise(ctx) - } - - dedupedKeys := dedupeKeys(keys) - go func() { - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - nodechan := ds.GetMany(ctx, dedupedKeys) - - for count := 0; count < len(keys); { - select { - case opt, ok := <-nodechan: - if !ok { - for _, p := range promises { - p.Fail(ErrNotFound) - } - return - } - - if opt.Err != nil { - for _, p := range promises { - p.Fail(opt.Err) - } - return - } - - nd := opt.Node - is := FindLinks(keys, nd.Cid(), 0) - for _, i := range is { - count++ - promises[i].Send(nd) - } - case <-ctx.Done(): + out <- &node.NodeOption{Err: ctx.Err()} return } } }() - return promises -} - -// Remove duplicates from a list of keys -func dedupeKeys(cids []*cid.Cid) []*cid.Cid { - out := make([]*cid.Cid, 0, len(cids)) - set := cid.NewSet() - for _, c := range cids { - if set.Visit(c) { - out = append(out, c) - } - } return out } -func newNodePromise(ctx context.Context) NodeGetter { - return &nodePromise{ - recv: make(chan node.Node, 1), - ctx: ctx, - err: make(chan error, 1), - } -} - -type nodePromise struct { - cache node.Node - clk sync.Mutex - recv chan node.Node - ctx context.Context - err chan error -} - -// NodeGetter provides a promise like interface for a dag Node -// the first call to Get will block until the Node is received -// from its internal channels, subsequent calls will return the -// cached node. -type NodeGetter interface { - Get(context.Context) (node.Node, error) - Fail(err error) - Send(node.Node) -} - -func (np *nodePromise) Fail(err error) { - np.clk.Lock() - v := np.cache - np.clk.Unlock() - - // if promise has a value, don't fail it - if v != nil { - return - } - - np.err <- err -} - -func (np *nodePromise) Send(nd node.Node) { - var already bool - np.clk.Lock() - if np.cache != nil { - already = true - } - np.cache = nd - np.clk.Unlock() - - if already { - panic("sending twice to the same promise is an error!") - } - - np.recv <- nd -} - -func (np *nodePromise) Get(ctx context.Context) (node.Node, error) { - np.clk.Lock() - c := np.cache - np.clk.Unlock() - if c != nil { - return c, nil - } +// GetLinks is the type of function passed to the EnumerateChildren function(s) +// for getting the children of an IPLD node. +type GetLinks func(context.Context, *cid.Cid) ([]*node.Link, error) - select { - case nd := <-np.recv: - return nd, nil - case <-np.ctx.Done(): - return nil, np.ctx.Err() - case <-ctx.Done(): - return nil, ctx.Err() - case err := <-np.err: - return nil, err +// GetLinksWithDAG returns a GetLinks function that tries to use the given +// NodeGetter as a LinkGetter to get the children of a given IPLD node. This may +// allow us to traverse the DAG without actually loading and parsing the node in +// question (if we already have the links cached). +func GetLinksWithDAG(ng node.NodeGetter) GetLinks { + return func(ctx context.Context, c *cid.Cid) ([]*node.Link, error) { + return node.GetLinks(ctx, ng, c) } } -type GetLinks func(context.Context, *cid.Cid) ([]*node.Link, error) - // EnumerateChildren will walk the dag below the given root node and add all // unseen children to the passed in set. // TODO: parallelize to avoid disk latency perf hits? @@ -443,6 +285,10 @@ func (p *ProgressTracker) Value() int { // 'fetchNodes' will start at a time var FetchGraphConcurrency = 8 +// EnumerateChildrenAsync is equivalent to EnumerateChildren *except* that it +// fetches children in parallel. +// +// NOTE: It *does not* make multiple concurrent calls to the passed `visit` function. func EnumerateChildrenAsync(ctx context.Context, getLinks GetLinks, c *cid.Cid, visit func(*cid.Cid) bool) error { feed := make(chan *cid.Cid) out := make(chan []*node.Link) @@ -523,3 +369,8 @@ func EnumerateChildrenAsync(ctx context.Context, getLinks GetLinks, c *cid.Cid, } } + +var _ node.LinkGetter = &dagService{} +var _ node.NodeGetter = &dagService{} +var _ node.NodeGetter = &sesGetter{} +var _ node.DAGService = &dagService{} diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 8b1ad1414..b23cc4feb 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -131,7 +131,7 @@ func TestBatchFetchDupBlock(t *testing.T) { func runBatchFetchTest(t *testing.T, read io.Reader) { ctx := context.Background() - var dagservs []DAGService + var dagservs []node.DAGService for _, bsi := range bstest.Mocks(5) { dagservs = append(dagservs, NewDAGService(bsi)) } @@ -155,7 +155,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { t.Fatal(err) } - _, err = dagservs[0].Add(root) + err = dagservs[0].Add(ctx, root) if err != nil { t.Fatal(err) } @@ -221,7 +221,7 @@ func TestCantGet(t *testing.T) { } func TestFetchGraph(t *testing.T) { - var dservs []DAGService + var dservs []node.DAGService bsis := bstest.Mocks(2) for _, bsi := range bsis { dservs = append(dservs, NewDAGService(bsi)) @@ -285,13 +285,15 @@ func TestEnumerateChildren(t *testing.T) { } func TestFetchFailure(t *testing.T) { + ctx := context.Background() + ds := dstest.Mock() ds_bad := dstest.Mock() top := new(ProtoNode) for i := 0; i < 10; i++ { nd := NodeWithData([]byte{byte('a' + i)}) - _, err := ds.Add(nd) + err := ds.Add(ctx, nd) if err != nil { t.Fatal(err) } @@ -304,7 +306,7 @@ func TestFetchFailure(t *testing.T) { for i := 0; i < 10; i++ { nd := NodeWithData([]byte{'f', 'a' + byte(i)}) - _, err := ds_bad.Add(nd) + err := ds_bad.Add(ctx, nd) if err != nil { t.Fatal(err) } @@ -315,9 +317,9 @@ func TestFetchFailure(t *testing.T) { } } - getters := GetDAG(context.Background(), ds, top) + getters := node.GetDAG(ctx, ds, top) for i, getter := range getters { - _, err := getter.Get(context.Background()) + _, err := getter.Get(ctx) if err != nil && i < 10 { t.Fatal(err) } @@ -352,15 +354,17 @@ func TestUnmarshalFailure(t *testing.T) { } func TestBasicAddGet(t *testing.T) { + ctx := context.Background() + ds := dstest.Mock() nd := new(ProtoNode) - c, err := ds.Add(nd) + err := ds.Add(ctx, nd) if err != nil { t.Fatal(err) } - out, err := ds.Get(context.Background(), c) + out, err := ds.Get(ctx, nd.Cid()) if err != nil { t.Fatal(err) } @@ -371,20 +375,22 @@ func TestBasicAddGet(t *testing.T) { } func TestGetRawNodes(t *testing.T) { + ctx := context.Background() + rn := NewRawNode([]byte("test")) ds := dstest.Mock() - c, err := ds.Add(rn) + err := ds.Add(ctx, rn) if err != nil { t.Fatal(err) } - if !c.Equals(rn.Cid()) { + if !rn.Cid().Equals(rn.Cid()) { t.Fatal("output cids didnt match") } - out, err := ds.Get(context.TODO(), c) + out, err := ds.Get(ctx, rn.Cid()) if err != nil { t.Fatal(err) } @@ -449,6 +455,8 @@ func TestProtoNodeResolve(t *testing.T) { } func TestCidRetention(t *testing.T) { + ctx := context.Background() + nd := new(ProtoNode) nd.SetData([]byte("fooooo")) @@ -466,13 +474,13 @@ func TestCidRetention(t *testing.T) { } bs := dstest.Bserv() - _, err = bs.AddBlock(blk) + err = bs.AddBlock(blk) if err != nil { t.Fatal(err) } ds := NewDAGService(bs) - out, err := ds.Get(context.Background(), c2) + out, err := ds.Get(ctx, c2) if err != nil { t.Fatal(err) } @@ -501,6 +509,8 @@ func TestCidRawDoesnNeedData(t *testing.T) { } func TestEnumerateAsyncFailsNotFound(t *testing.T) { + ctx := context.Background() + a := NodeWithData([]byte("foo1")) b := NodeWithData([]byte("foo2")) c := NodeWithData([]byte("foo3")) @@ -508,7 +518,7 @@ func TestEnumerateAsyncFailsNotFound(t *testing.T) { ds := dstest.Mock() for _, n := range []node.Node{a, b, c} { - _, err := ds.Add(n) + err := ds.Add(ctx, n) if err != nil { t.Fatal(err) } @@ -531,13 +541,13 @@ func TestEnumerateAsyncFailsNotFound(t *testing.T) { t.Fatal(err) } - pcid, err := ds.Add(parent) + err := ds.Add(ctx, parent) if err != nil { t.Fatal(err) } cset := cid.NewSet() - err = EnumerateChildrenAsync(context.Background(), GetLinksDirect(ds), pcid, cset.Visit) + err = EnumerateChildrenAsync(ctx, GetLinksDirect(ds), parent.Cid(), cset.Visit) if err == nil { t.Fatal("this should have failed") } @@ -570,7 +580,9 @@ func testProgressIndicator(t *testing.T, depth int) { } } -func mkDag(ds DAGService, depth int) (*cid.Cid, int) { +func mkDag(ds node.DAGService, depth int) (*cid.Cid, int) { + ctx := context.Background() + totalChildren := 0 f := func() *ProtoNode { p := new(ProtoNode) @@ -578,7 +590,7 @@ func mkDag(ds DAGService, depth int) (*cid.Cid, int) { rand.Read(buf) p.SetData(buf) - _, err := ds.Add(p) + err := ds.Add(ctx, p) if err != nil { panic(err) } @@ -589,7 +601,7 @@ func mkDag(ds DAGService, depth int) (*cid.Cid, int) { thisf := f f = func() *ProtoNode { pn := mkNodeWithChildren(thisf, 10) - _, err := ds.Add(pn) + err := ds.Add(ctx, pn) if err != nil { panic(err) } @@ -599,12 +611,12 @@ func mkDag(ds DAGService, depth int) (*cid.Cid, int) { } nd := f() - c, err := ds.Add(nd) + err := ds.Add(ctx, nd) if err != nil { panic(err) } - return c, totalChildren + return nd.Cid(), totalChildren } func mkNodeWithChildren(getChild func() *ProtoNode, width int) *ProtoNode { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 44d44617d..143e7e075 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -140,7 +140,7 @@ func (n *ProtoNode) RemoveNodeLink(name string) error { n.links = good if !found { - return ErrNotFound + return node.ErrNotFound } return nil @@ -160,7 +160,7 @@ func (n *ProtoNode) GetNodeLink(name string) (*node.Link, error) { return nil, ErrLinkNotFound } -func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds DAGService, name string) (*ProtoNode, error) { +func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds node.DAGService, name string) (*ProtoNode, error) { nd, err := n.GetLinkedNode(ctx, ds, name) if err != nil { return nil, err @@ -174,7 +174,7 @@ func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds DAGService, name return pbnd, nil } -func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds DAGService, name string) (node.Node, error) { +func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds node.DAGService, name string) (node.Node, error) { lnk, err := n.GetNodeLink(name) if err != nil { return nil, err diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index 27726c454..4b6a412d2 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -41,7 +41,7 @@ func TestRemoveLink(t *testing.T) { // should fail err = nd.RemoveNodeLink("a") - if err != ErrNotFound { + if err != node.ErrNotFound { t.Fatal("should have failed to remove link") } @@ -60,20 +60,25 @@ func TestRemoveLink(t *testing.T) { } func TestFindLink(t *testing.T) { + ctx := context.Background() + ds := mdtest.Mock() - k, err := ds.Add(new(ProtoNode)) + ndEmpty := new(ProtoNode) + err := ds.Add(ctx, ndEmpty) if err != nil { t.Fatal(err) } + kEmpty := ndEmpty.Cid() + nd := &ProtoNode{} nd.SetLinks([]*node.Link{ - {Name: "a", Cid: k}, - {Name: "c", Cid: k}, - {Name: "b", Cid: k}, + {Name: "a", Cid: kEmpty}, + {Name: "c", Cid: kEmpty}, + {Name: "b", Cid: kEmpty}, }) - _, err = ds.Add(nd) + err = ds.Add(ctx, nd) if err != nil { t.Fatal(err) } @@ -107,7 +112,7 @@ func TestFindLink(t *testing.T) { t.Fatal(err) } - if olnk.Cid.String() == k.String() { + if olnk.Cid.String() == kEmpty.String() { t.Fatal("new link should have different hash") } } diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index c0ab995e2..87bfe23c8 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -5,11 +5,13 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) -func Mock() dag.DAGService { +func Mock() node.DAGService { return dag.NewDAGService(Bserv()) } diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 23d42c6c0..8c32a6934 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -2,6 +2,7 @@ package traverse import ( "bytes" + "context" "fmt" "testing" @@ -350,7 +351,7 @@ func testWalkOutputs(t *testing.T, root node.Node, opts Options, expect []byte) } } -func newFan(t *testing.T, ds mdag.DAGService) node.Node { +func newFan(t *testing.T, ds node.DAGService) node.Node { a := mdag.NodeWithData([]byte("/a")) addLink(t, ds, a, child(t, ds, a, "aa")) addLink(t, ds, a, child(t, ds, a, "ab")) @@ -359,7 +360,7 @@ func newFan(t *testing.T, ds mdag.DAGService) node.Node { return a } -func newLinkedList(t *testing.T, ds mdag.DAGService) node.Node { +func newLinkedList(t *testing.T, ds node.DAGService) node.Node { a := mdag.NodeWithData([]byte("/a")) aa := child(t, ds, a, "aa") aaa := child(t, ds, aa, "aaa") @@ -372,7 +373,7 @@ func newLinkedList(t *testing.T, ds mdag.DAGService) node.Node { return a } -func newBinaryTree(t *testing.T, ds mdag.DAGService) node.Node { +func newBinaryTree(t *testing.T, ds node.DAGService) node.Node { a := mdag.NodeWithData([]byte("/a")) aa := child(t, ds, a, "aa") ab := child(t, ds, a, "ab") @@ -385,7 +386,7 @@ func newBinaryTree(t *testing.T, ds mdag.DAGService) node.Node { return a } -func newBinaryDAG(t *testing.T, ds mdag.DAGService) node.Node { +func newBinaryDAG(t *testing.T, ds node.DAGService) node.Node { a := mdag.NodeWithData([]byte("/a")) aa := child(t, ds, a, "aa") aaa := child(t, ds, aa, "aaa") @@ -402,9 +403,9 @@ func newBinaryDAG(t *testing.T, ds mdag.DAGService) node.Node { return a } -func addLink(t *testing.T, ds mdag.DAGService, a, b node.Node) { +func addLink(t *testing.T, ds node.DAGService, a, b node.Node) { to := string(a.(*mdag.ProtoNode).Data()) + "2" + string(b.(*mdag.ProtoNode).Data()) - if _, err := ds.Add(b); err != nil { + if err := ds.Add(context.Background(), b); err != nil { t.Error(err) } if err := a.(*mdag.ProtoNode).AddNodeLink(to, b.(*mdag.ProtoNode)); err != nil { @@ -412,6 +413,6 @@ func addLink(t *testing.T, ds mdag.DAGService, a, b node.Node) { } } -func child(t *testing.T, ds mdag.DAGService, a node.Node, name string) node.Node { +func child(t *testing.T, ds node.DAGService, a node.Node, name string) node.Node { return mdag.NodeWithData([]byte(string(a.(*mdag.ProtoNode).Data()) + "/" + name)) } diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index db5e22936..2ead4bdf7 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -37,7 +37,7 @@ func (c *Change) String() string { } } -func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.ProtoNode, cs []*Change) (*dag.ProtoNode, error) { +func ApplyChange(ctx context.Context, ds node.DAGService, nd *dag.ProtoNode, cs []*Change) (*dag.ProtoNode, error) { e := NewDagEditor(nd, ds) for _, c := range cs { switch c.Type { @@ -85,11 +85,11 @@ func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.ProtoNode, cs [ } } - return e.Finalize(ds) + return e.Finalize(ctx, ds) } // Diff returns a set of changes that transform node 'a' into node 'b' -func Diff(ctx context.Context, ds dag.DAGService, a, b node.Node) ([]*Change, error) { +func Diff(ctx context.Context, ds node.DAGService, a, b node.Node) ([]*Change, error) { if len(a.Links()) == 0 && len(b.Links()) == 0 { return []*Change{ &Change{ diff --git a/ipld/merkledag/utils/diffenum_test.go b/ipld/merkledag/utils/diffenum_test.go index 668751609..5fd463aec 100644 --- a/ipld/merkledag/utils/diffenum_test.go +++ b/ipld/merkledag/utils/diffenum_test.go @@ -136,7 +136,7 @@ func TestDiffEnumBasic(t *testing.T) { lgds := &getLogger{ds: ds} for _, nd := range nds { - _, err := ds.Add(nd) + err := ds.Add(ctx, nd) if err != nil { t.Fatal(err) } @@ -167,6 +167,22 @@ func (gl *getLogger) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { return nd, nil } +func (gl *getLogger) GetMany(ctx context.Context, cids []*cid.Cid) <-chan *node.NodeOption { + outCh := make(chan *node.NodeOption, len(cids)) + nds := gl.ds.GetMany(ctx, cids) + for no := range nds { + if no.Err == nil { + gl.log = append(gl.log, no.Node.Cid()) + } + select { + case outCh <- no: + default: + panic("too many responses") + } + } + return nds +} + func assertCidList(a, b []*cid.Cid) error { if len(a) != len(b) { return fmt.Errorf("got different number of cids than expected") @@ -188,14 +204,14 @@ func TestDiffEnumFail(t *testing.T) { lgds := &getLogger{ds: ds} for _, s := range []string{"a1", "a2", "b", "c"} { - _, err := ds.Add(nds[s]) + err := ds.Add(ctx, nds[s]) if err != nil { t.Fatal(err) } } err := DiffEnumerate(ctx, lgds, nds["a1"].Cid(), nds["a2"].Cid()) - if err != dag.ErrNotFound { + if err != node.ErrNotFound { t.Fatal("expected err not found") } @@ -215,7 +231,7 @@ func TestDiffEnumRecurse(t *testing.T) { lgds := &getLogger{ds: ds} for _, s := range []string{"a1", "a2", "b", "c", "d"} { - _, err := ds.Add(nds[s]) + err := ds.Add(ctx, nds[s]) if err != nil { t.Fatal(err) } diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 6c16967ca..dd119834d 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -20,14 +20,14 @@ type Editor struct { // tmp is a temporary in memory (for now) dagstore for all of the // intermediary nodes to be stored in - tmp dag.DAGService + tmp node.DAGService // src is the dagstore with *all* of the data on it, it is used to pull // nodes from for modification (nil is a valid value) - src dag.DAGService + src node.DAGService } -func NewMemoryDagService() dag.DAGService { +func NewMemoryDagService() node.DAGService { // build mem-datastore for editor's intermediary nodes bs := bstore.NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore())) bsrv := bserv.New(bs, offline.Exchange(bs)) @@ -35,7 +35,7 @@ func NewMemoryDagService() dag.DAGService { } // root is the node to be modified, source is the dagstore to pull nodes from (optional) -func NewDagEditor(root *dag.ProtoNode, source dag.DAGService) *Editor { +func NewDagEditor(root *dag.ProtoNode, source node.DAGService) *Editor { return &Editor{ root: root, tmp: NewMemoryDagService(), @@ -47,22 +47,22 @@ func (e *Editor) GetNode() *dag.ProtoNode { return e.root.Copy().(*dag.ProtoNode) } -func (e *Editor) GetDagService() dag.DAGService { +func (e *Editor) GetDagService() node.DAGService { return e.tmp } -func addLink(ctx context.Context, ds dag.DAGService, root *dag.ProtoNode, childname string, childnd node.Node) (*dag.ProtoNode, error) { +func addLink(ctx context.Context, ds node.DAGService, root *dag.ProtoNode, childname string, childnd node.Node) (*dag.ProtoNode, error) { if childname == "" { return nil, errors.New("cannot create link with no name!") } // ensure that the node we are adding is in the dagservice - _, err := ds.Add(childnd) + err := ds.Add(ctx, childnd) if err != nil { return nil, err } - _ = ds.Remove(root) + _ = ds.Remove(ctx, root.Cid()) // ensure no link with that name already exists _ = root.RemoveNodeLink(childname) // ignore error, only option is ErrNotFound @@ -71,7 +71,7 @@ func addLink(ctx context.Context, ds dag.DAGService, root *dag.ProtoNode, childn return nil, err } - if _, err := ds.Add(root); err != nil { + if err := ds.Add(ctx, root); err != nil { return nil, err } return root, nil @@ -98,7 +98,7 @@ func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path if err == dag.ErrLinkNotFound && create != nil { nd = create() err = nil // no longer an error case - } else if err == dag.ErrNotFound { + } else if err == node.ErrNotFound { // try finding it in our source dagstore nd, err = root.GetLinkedProtoNode(ctx, e.src, path[0]) } @@ -115,7 +115,7 @@ func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path return nil, err } - _ = e.tmp.Remove(root) + _ = e.tmp.Remove(ctx, root.Cid()) _ = root.RemoveNodeLink(path[0]) err = root.AddNodeLinkClean(path[0], ndprime) @@ -123,7 +123,7 @@ func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path return nil, err } - _, err = e.tmp.Add(root) + err = e.tmp.Add(ctx, root) if err != nil { return nil, err } @@ -149,7 +149,7 @@ func (e *Editor) rmLink(ctx context.Context, root *dag.ProtoNode, path []string) return nil, err } - _, err = e.tmp.Add(root) + err = e.tmp.Add(ctx, root) if err != nil { return nil, err } @@ -159,7 +159,7 @@ func (e *Editor) rmLink(ctx context.Context, root *dag.ProtoNode, path []string) // search for node in both tmp dagstore and source dagstore nd, err := root.GetLinkedProtoNode(ctx, e.tmp, path[0]) - if err == dag.ErrNotFound { + if err == node.ErrNotFound { nd, err = root.GetLinkedProtoNode(ctx, e.src, path[0]) } @@ -172,7 +172,7 @@ func (e *Editor) rmLink(ctx context.Context, root *dag.ProtoNode, path []string) return nil, err } - _ = e.tmp.Remove(root) + e.tmp.Remove(ctx, root.Cid()) _ = root.RemoveNodeLink(path[0]) err = root.AddNodeLinkClean(path[0], nnode) @@ -180,7 +180,7 @@ func (e *Editor) rmLink(ctx context.Context, root *dag.ProtoNode, path []string) return nil, err } - _, err = e.tmp.Add(root) + err = e.tmp.Add(ctx, root) if err != nil { return nil, err } @@ -188,22 +188,23 @@ func (e *Editor) rmLink(ctx context.Context, root *dag.ProtoNode, path []string) return root, nil } -func (e *Editor) Finalize(ds dag.DAGService) (*dag.ProtoNode, error) { +func (e *Editor) Finalize(ctx context.Context, ds node.DAGService) (*dag.ProtoNode, error) { nd := e.GetNode() - err := copyDag(nd, e.tmp, ds) + err := copyDag(ctx, nd, e.tmp, ds) return nd, err } -func copyDag(nd node.Node, from, to dag.DAGService) error { - _, err := to.Add(nd) +func copyDag(ctx context.Context, nd node.Node, from, to node.DAGService) error { + // TODO(#4609): make this batch. + err := to.Add(ctx, nd) if err != nil { return err } for _, lnk := range nd.Links() { - child, err := lnk.GetNode(context.Background(), from) + child, err := lnk.GetNode(ctx, from) if err != nil { - if err == dag.ErrNotFound { + if err == node.ErrNotFound { // not found means we didnt modify it, and it should // already be in the target datastore continue @@ -211,7 +212,7 @@ func copyDag(nd node.Node, from, to dag.DAGService) error { return err } - err = copyDag(child, from, to) + err = copyDag(ctx, child, from, to) if err != nil { return err } diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 2c3e953d3..c404ffed4 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -9,35 +9,39 @@ import ( path "github.com/ipfs/go-ipfs/path" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) func TestAddLink(t *testing.T) { + ctx, context := context.WithCancel(context.Background()) + defer context() + ds := mdtest.Mock() fishnode := dag.NodeWithData([]byte("fishcakes!")) - fk, err := ds.Add(fishnode) + err := ds.Add(ctx, fishnode) if err != nil { t.Fatal(err) } nd := new(dag.ProtoNode) - nnode, err := addLink(context.Background(), ds, nd, "fish", fishnode) + nnode, err := addLink(ctx, ds, nd, "fish", fishnode) if err != nil { t.Fatal(err) } - fnprime, err := nnode.GetLinkedNode(context.Background(), ds, "fish") + fnprime, err := nnode.GetLinkedNode(ctx, ds, "fish") if err != nil { t.Fatal(err) } fnpkey := fnprime.Cid() - if !fnpkey.Equals(fk) { + if !fnpkey.Equals(fishnode.Cid()) { t.Fatal("wrong child node found!") } } -func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.ProtoNode, pth string, exp *cid.Cid) { +func assertNodeAtPath(t *testing.T, ds node.DAGService, root *dag.ProtoNode, pth string, exp *cid.Cid) { parts := path.SplitList(pth) cur := root for _, e := range parts { @@ -78,7 +82,7 @@ func TestInsertNode(t *testing.T) { func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr string) { child := dag.NodeWithData([]byte(data)) - ck, err := e.tmp.Add(child) + err := e.tmp.Add(context.Background(), child) if err != nil { t.Fatal(err) } @@ -106,5 +110,5 @@ func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr t.Fatal(err, path, data, create, experr) } - assertNodeAtPath(t, e.tmp, e.root, path, ck) + assertNodeAtPath(t, e.tmp, e.root, path, child.Cid()) } From 948194813b89f3e6e2794c85b312da92be4415e0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 25 Jan 2018 12:21:51 -0800 Subject: [PATCH 2122/3817] merkledag: switch to new dag interface Also: * Update the blockstore/blockservice methods to match. * Construct a new temporary offline dag instead of having a GetOfflineLinkService method. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@3ef1c87d92aec8afb54b1fb54138c3a83e41c9cb --- unixfs/archive/archive.go | 3 +-- unixfs/archive/tar/writer.go | 6 +++--- unixfs/hamt/hamt.go | 14 +++++++------- unixfs/hamt/hamt_stress_test.go | 9 +++++---- unixfs/hamt/hamt_test.go | 27 +++++++++++++++++---------- unixfs/io/dagreader.go | 2 +- unixfs/io/dagreader_test.go | 8 ++++---- unixfs/io/dirbuilder.go | 6 +++--- unixfs/io/dirbuilder_test.go | 4 ++-- unixfs/io/pbdagreader.go | 12 ++++++------ unixfs/io/resolve.go | 2 +- unixfs/mod/dagmodifier.go | 26 +++++++++++++------------- unixfs/test/utils.go | 10 +++++----- 13 files changed, 68 insertions(+), 61 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index dc3afd0c2..2f6dc7183 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -7,7 +7,6 @@ import ( "io" "path" - mdag "github.com/ipfs/go-ipfs/merkledag" tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" uio "github.com/ipfs/go-ipfs/unixfs/io" @@ -31,7 +30,7 @@ func (i *identityWriteCloser) Close() error { } // DagArchive is equivalent to `ipfs getdag $hash | maybe_tar | maybe_gzip` -func DagArchive(ctx context.Context, nd node.Node, name string, dag mdag.DAGService, archive bool, compression int) (io.Reader, error) { +func DagArchive(ctx context.Context, nd node.Node, name string, dag node.DAGService, archive bool, compression int) (io.Reader, error) { _, filename := path.Split(name) diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 27372e8f8..9cd696660 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -21,14 +21,14 @@ import ( // unixfs merkledag nodes as a tar archive format. // It wraps any io.Writer. type Writer struct { - Dag mdag.DAGService + Dag node.DAGService TarW *tar.Writer ctx context.Context } // NewWriter wraps given io.Writer. -func NewWriter(ctx context.Context, dag mdag.DAGService, archive bool, compression int, w io.Writer) (*Writer, error) { +func NewWriter(ctx context.Context, dag node.DAGService, archive bool, compression int, w io.Writer) (*Writer, error) { return &Writer{ Dag: dag, TarW: tar.NewWriter(w), @@ -41,7 +41,7 @@ func (w *Writer) writeDir(nd *mdag.ProtoNode, fpath string) error { return err } - for i, ng := range mdag.GetDAG(w.ctx, w.Dag, nd) { + for i, ng := range node.GetDAG(w.ctx, w.Dag, nd) { child, err := ng.Get(w.ctx) if err != nil { return err diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 044017f97..2974d4928 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -57,7 +57,7 @@ type HamtShard struct { prefixPadStr string maxpadlen int - dserv dag.DAGService + dserv node.DAGService } // child can either be another shard, or a leaf node value @@ -66,7 +66,7 @@ type child interface { Label() string } -func NewHamtShard(dserv dag.DAGService, size int) (*HamtShard, error) { +func NewHamtShard(dserv node.DAGService, size int) (*HamtShard, error) { ds, err := makeHamtShard(dserv, size) if err != nil { return nil, err @@ -78,7 +78,7 @@ func NewHamtShard(dserv dag.DAGService, size int) (*HamtShard, error) { return ds, nil } -func makeHamtShard(ds dag.DAGService, size int) (*HamtShard, error) { +func makeHamtShard(ds node.DAGService, size int) (*HamtShard, error) { lg2s := int(math.Log2(float64(size))) if 1< Date: Thu, 25 Jan 2018 12:21:51 -0800 Subject: [PATCH 2123/3817] merkledag: switch to new dag interface Also: * Update the blockstore/blockservice methods to match. * Construct a new temporary offline dag instead of having a GetOfflineLinkService method. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-path@6aa9623d9cabfef03d7a04fdad8411ee4cc3e883 --- path/resolver.go | 8 ++++---- path/resolver_test.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index 68639fbc0..71ac52ba0 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -35,12 +35,12 @@ func (e ErrNoLink) Error() string { // TODO: now that this is more modular, try to unify this code with the // the resolvers in namesys type Resolver struct { - DAG dag.DAGService + DAG node.DAGService - ResolveOnce func(ctx context.Context, ds dag.DAGService, nd node.Node, names []string) (*node.Link, []string, error) + ResolveOnce func(ctx context.Context, ds node.DAGService, nd node.Node, names []string) (*node.Link, []string, error) } -func NewBasicResolver(ds dag.DAGService) *Resolver { +func NewBasicResolver(ds node.DAGService) *Resolver { return &Resolver{ DAG: ds, ResolveOnce: ResolveSingle, @@ -123,7 +123,7 @@ func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (node.Node, erro // ResolveSingle simply resolves one hop of a path through a graph with no // extra context (does not opaquely resolve through sharded nodes) -func ResolveSingle(ctx context.Context, ds dag.DAGService, nd node.Node, names []string) (*node.Link, []string, error) { +func ResolveSingle(ctx context.Context, ds node.DAGService, nd node.Node, names []string) (*node.Link, []string, error) { return nd.ResolveLink(names) } diff --git a/path/resolver_test.go b/path/resolver_test.go index 8347fc602..5f5a9ee50 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -39,7 +39,7 @@ func TestRecurivePathResolution(t *testing.T) { } for _, n := range []node.Node{a, b, c} { - _, err = dagService.Add(n) + err = dagService.Add(ctx, n) if err != nil { t.Fatal(err) } From 4d2e1b1752663b19e855beaa54db21e110ae9280 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 25 Jan 2018 12:21:51 -0800 Subject: [PATCH 2124/3817] merkledag: switch to new dag interface Also: * Update the blockstore/blockservice methods to match. * Construct a new temporary offline dag instead of having a GetOfflineLinkService method. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@8fcf235b0161d7c15a66de915c5a1cd430ac7c52 --- blockservice/blockservice.go | 72 +++++++++++++++++--------------- blockservice/test/blocks_test.go | 6 +-- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 738822605..d4f6fffad 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "io" "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" @@ -21,10 +22,27 @@ var log = logging.Logger("blockservice") var ErrNotFound = errors.New("blockservice: key not found") +type BlockGetter interface { + // GetBlock gets the requested block. + GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) + + // GetBlocks does a batch request for the given cids, returning blocks as + // they are found, in no particular order. + // + // It may not be able to find all requested blocks (or the context may + // be canceled). In that case, it will close the channel early. It is up + // to the consumer to detect this situation and keep track which blocks + // it has received and which it hasn't. + GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block +} + // BlockService is a hybrid block datastore. It stores data in a local // datastore and may retrieve data from a remote Exchange. // It uses an internal `datastore.Datastore` instance to store values. type BlockService interface { + io.Closer + BlockGetter + // Blockstore returns a reference to the underlying blockstore Blockstore() blockstore.Blockstore @@ -32,20 +50,14 @@ type BlockService interface { Exchange() exchange.Interface // AddBlock puts a given block to the underlying datastore - AddBlock(o blocks.Block) (*cid.Cid, error) + AddBlock(o blocks.Block) error // AddBlocks adds a slice of blocks at the same time using batching // capabilities of the underlying datastore whenever possible. - AddBlocks(bs []blocks.Block) ([]*cid.Cid, error) - - GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) - DeleteBlock(o blocks.Block) error + AddBlocks(bs []blocks.Block) error - // GetBlocks does a batch request for the given cids, returning blocks as - // they are found, in no particular order. - GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block - - Close() error + // DeleteBlock deletes the given block from the blockservice. + DeleteBlock(o *cid.Cid) error } type blockService struct { @@ -110,38 +122,34 @@ func NewSession(ctx context.Context, bs BlockService) *Session { // AddBlock adds a particular block to the service, Putting it into the datastore. // TODO pass a context into this if the remote.HasBlock is going to remain here. -func (s *blockService) AddBlock(o blocks.Block) (*cid.Cid, error) { +func (s *blockService) AddBlock(o blocks.Block) error { c := o.Cid() if s.checkFirst { - has, err := s.blockstore.Has(c) - if err != nil { - return nil, err - } - - if has { - return c, nil + if has, err := s.blockstore.Has(c); has || err != nil { + return err } } - err := s.blockstore.Put(o) - if err != nil { - return nil, err + if err := s.blockstore.Put(o); err != nil { + return err } if err := s.exchange.HasBlock(o); err != nil { - return nil, errors.New("blockservice is closed") + // TODO(stebalien): really an error? + return errors.New("blockservice is closed") } - return c, nil + return nil } -func (s *blockService) AddBlocks(bs []blocks.Block) ([]*cid.Cid, error) { +func (s *blockService) AddBlocks(bs []blocks.Block) error { var toput []blocks.Block if s.checkFirst { + toput = make([]blocks.Block, 0, len(bs)) for _, b := range bs { has, err := s.blockstore.Has(b.Cid()) if err != nil { - return nil, err + return err } if !has { toput = append(toput, b) @@ -153,18 +161,16 @@ func (s *blockService) AddBlocks(bs []blocks.Block) ([]*cid.Cid, error) { err := s.blockstore.PutMany(toput) if err != nil { - return nil, err + return err } - var ks []*cid.Cid for _, o := range toput { if err := s.exchange.HasBlock(o); err != nil { - return nil, fmt.Errorf("blockservice is closed (%s)", err) + // TODO(stebalien): Should this really *return*? + return fmt.Errorf("blockservice is closed (%s)", err) } - - ks = append(ks, o.Cid()) } - return ks, nil + return nil } // GetBlock retrieves a particular block from the service, @@ -256,8 +262,8 @@ func getBlocks(ctx context.Context, ks []*cid.Cid, bs blockstore.Blockstore, f e } // DeleteBlock deletes a block in the blockservice from the datastore -func (s *blockService) DeleteBlock(o blocks.Block) error { - return s.blockstore.DeleteBlock(o.Cid()) +func (s *blockService) DeleteBlock(c *cid.Cid) error { + return s.blockstore.DeleteBlock(c) } func (s *blockService) Close() error { diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 965327dae..3ff25366c 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -33,16 +33,12 @@ func TestBlocks(t *testing.T) { t.Error("Block key and data multihash key not equal") } - k, err := bs.AddBlock(o) + err := bs.AddBlock(o) if err != nil { t.Error("failed to add block to BlockService", err) return } - if !k.Equals(o.Cid()) { - t.Error("returned key is not equal to block key", err) - } - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() b2, err := bs.GetBlock(ctx, o.Cid()) From 4ce3ef8d3e8747a81cafd2de250f479d76d8e07e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 25 Jan 2018 12:21:51 -0800 Subject: [PATCH 2125/3817] merkledag: switch to new dag interface Also: * Update the blockstore/blockservice methods to match. * Construct a new temporary offline dag instead of having a GetOfflineLinkService method. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@19fbe75ad3037d256affca05f700d4f5b44913c1 --- pinning/pinner/gc/gc.go | 17 ++++++++++------- pinning/pinner/pin.go | 26 +++++++++++++++----------- pinning/pinner/pin_test.go | 36 ++++++++++++++++++++---------------- pinning/pinner/set.go | 15 ++++++++------- 4 files changed, 53 insertions(+), 41 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 7a19b6969..c3ab3db90 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -6,6 +6,8 @@ import ( "fmt" bstore "github.com/ipfs/go-ipfs/blocks/blockstore" + bserv "github.com/ipfs/go-ipfs/blockservice" + offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" @@ -33,7 +35,7 @@ type Result struct { // The routine then iterates over every block in the blockstore and // deletes any block that is not found in the marked set. // -func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin.Pinner, bestEffortRoots []*cid.Cid) <-chan Result { +func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner, bestEffortRoots []*cid.Cid) <-chan Result { elock := log.EventBegin(ctx, "GC.lockWait") unlocker := bs.GCLock() @@ -41,7 +43,8 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. elock = log.EventBegin(ctx, "GC.locked") emark := log.EventBegin(ctx, "GC.mark") - ls = ls.GetOfflineLinkService() + bsrv := bserv.New(bs, offline.Exchange(bs)) + ds := dag.NewDAGService(bsrv) output := make(chan Result, 128) @@ -50,7 +53,7 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, ls dag.LinkService, pn pin. defer unlocker.Unlock() defer elock.Done() - gcs, err := ColoredSet(ctx, pn, ls, bestEffortRoots, output) + gcs, err := ColoredSet(ctx, pn, ds, bestEffortRoots, output) if err != nil { output <- Result{Error: err} return @@ -125,13 +128,13 @@ func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots // ColoredSet computes the set of nodes in the graph that are pinned by the // pins in the given pinner. -func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffortRoots []*cid.Cid, output chan<- Result) (*cid.Set, error) { +func ColoredSet(ctx context.Context, pn pin.Pinner, ng node.NodeGetter, bestEffortRoots []*cid.Cid, output chan<- Result) (*cid.Set, error) { // KeySet currently implemented in memory, in the future, may be bloom filter or // disk backed to conserve memory. errors := false gcs := cid.NewSet() getLinks := func(ctx context.Context, cid *cid.Cid) ([]*node.Link, error) { - links, err := ls.GetLinks(ctx, cid) + links, err := node.GetLinks(ctx, ng, cid) if err != nil { errors = true output <- Result{Error: &CannotFetchLinksError{cid, err}} @@ -145,8 +148,8 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ls dag.LinkService, bestEffo } bestEffortGetLinks := func(ctx context.Context, cid *cid.Cid) ([]*node.Link, error) { - links, err := ls.GetLinks(ctx, cid) - if err != nil && err != dag.ErrNotFound { + links, err := node.GetLinks(ctx, ng, cid) + if err != nil && err != node.ErrNotFound { errors = true output <- Result{Error: &CannotFetchLinksError{cid, err}} } diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index f148053ff..9387439fb 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -178,13 +178,13 @@ type pinner struct { // Track the keys used for storing the pinning state, so gc does // not delete them. internalPin *cid.Set - dserv mdag.DAGService - internal mdag.DAGService // dagservice used to store internal objects + dserv node.DAGService + internal node.DAGService // dagservice used to store internal objects dstore ds.Datastore } // NewPinner creates a new pinner using the given datastore as a backend -func NewPinner(dstore ds.Datastore, serv, internal mdag.DAGService) Pinner { +func NewPinner(dstore ds.Datastore, serv, internal node.DAGService) Pinner { rcset := cid.NewSet() dirset := cid.NewSet() @@ -203,11 +203,13 @@ func NewPinner(dstore ds.Datastore, serv, internal mdag.DAGService) Pinner { func (p *pinner) Pin(ctx context.Context, node node.Node, recurse bool) error { p.lock.Lock() defer p.lock.Unlock() - c, err := p.dserv.Add(node) + err := p.dserv.Add(ctx, node) if err != nil { return err } + c := node.Cid() + if recurse { if p.recursePin.Has(c) { return nil @@ -356,7 +358,7 @@ func (p *pinner) CheckIfPinned(cids ...*cid.Cid) ([]Pinned, error) { // Now walk all recursive pins to check for indirect pins var checkChildren func(*cid.Cid, *cid.Cid) error checkChildren = func(rk, parentKey *cid.Cid) error { - links, err := p.dserv.GetLinks(context.Background(), parentKey) + links, err := node.GetLinks(context.TODO(), p.dserv, parentKey) if err != nil { return err } @@ -425,7 +427,7 @@ func cidSetWithValues(cids []*cid.Cid) *cid.Set { } // LoadPinner loads a pinner and its keysets from the given datastore -func LoadPinner(d ds.Datastore, dserv, internal mdag.DAGService) (Pinner, error) { +func LoadPinner(d ds.Datastore, dserv, internal node.DAGService) (Pinner, error) { p := new(pinner) rootKeyI, err := d.Get(pinDatastoreKey) @@ -550,16 +552,18 @@ func (p *pinner) Flush() error { } // add the empty node, its referenced by the pin sets but never created - _, err := p.internal.Add(new(mdag.ProtoNode)) + err := p.internal.Add(ctx, new(mdag.ProtoNode)) if err != nil { return err } - k, err := p.internal.Add(root) + err = p.internal.Add(ctx, root) if err != nil { return err } + k := root.Cid() + internalset.Add(k) if err := p.dstore.Put(pinDatastoreKey, k.Bytes()); err != nil { return fmt.Errorf("cannot store pin state: %v", err) @@ -593,8 +597,8 @@ func (p *pinner) PinWithMode(c *cid.Cid, mode PinMode) { // hasChild recursively looks for a Cid among the children of a root Cid. // The visit function can be used to shortcut already-visited branches. -func hasChild(ds mdag.LinkService, root *cid.Cid, child *cid.Cid, visit func(*cid.Cid) bool) (bool, error) { - links, err := ds.GetLinks(context.Background(), root) +func hasChild(ng node.NodeGetter, root *cid.Cid, child *cid.Cid, visit func(*cid.Cid) bool) (bool, error) { + links, err := node.GetLinks(context.TODO(), ng, root) if err != nil { return false, err } @@ -604,7 +608,7 @@ func hasChild(ds mdag.LinkService, root *cid.Cid, child *cid.Cid, visit func(*ci return true, nil } if visit(c) { - has, err := hasChild(ds, c, child, visit) + has, err := hasChild(ng, c, child, visit) if err != nil { return false, err } diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 7980c8dbc..3652ef8e7 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -59,7 +59,7 @@ func TestPinnerBasic(t *testing.T) { p := NewPinner(dstore, dserv, dserv) a, ak := randNode() - _, err := dserv.Add(a) + err := dserv.Add(ctx, a) if err != nil { t.Fatal(err) } @@ -74,10 +74,11 @@ func TestPinnerBasic(t *testing.T) { // create new node c, to be indirectly pinned through b c, _ := randNode() - ck, err := dserv.Add(c) + err = dserv.Add(ctx, c) if err != nil { t.Fatal(err) } + ck := c.Cid() // Create new node b, to be parent to a and c b, _ := randNode() @@ -91,10 +92,11 @@ func TestPinnerBasic(t *testing.T) { t.Fatal(err) } - _, err = dserv.Add(b) + err = dserv.Add(ctx, b) if err != nil { t.Fatal(err) } + bk := b.Cid() // recursively pin B{A,C} err = p.Pin(ctx, b, true) @@ -104,7 +106,6 @@ func TestPinnerBasic(t *testing.T) { assertPinned(t, p, ck, "child of recursively pinned node not found") - bk := b.Cid() assertPinned(t, p, bk, "Recursively pinned node not found..") d, _ := randNode() @@ -115,11 +116,11 @@ func TestPinnerBasic(t *testing.T) { d.AddNodeLink("e", e) // Must be in dagserv for unpin to work - _, err = dserv.Add(e) + err = dserv.Add(ctx, e) if err != nil { t.Fatal(err) } - _, err = dserv.Add(d) + err = dserv.Add(ctx, d) if err != nil { t.Fatal(err) } @@ -194,13 +195,13 @@ func TestIsPinnedLookup(t *testing.T) { } } - ak, err := dserv.Add(a) + err := dserv.Add(ctx, a) if err != nil { t.Fatal(err) } //t.Logf("a[%d] is %s", i, ak) aNodes[i] = a - aKeys[i] = ak + aKeys[i] = a.Cid() } // Pin A5 recursively @@ -222,20 +223,22 @@ func TestIsPinnedLookup(t *testing.T) { } // Add C - ck, err := dserv.Add(c) + err := dserv.Add(ctx, c) if err != nil { t.Fatal(err) } + ck := c.Cid() //t.Logf("C is %s", ck) // Add C to B and Add B if err := b.AddNodeLink("myotherchild", c); err != nil { t.Fatal(err) } - bk, err := dserv.Add(b) + err = dserv.Add(ctx, b) if err != nil { t.Fatal(err) } + bk := b.Cid() //t.Logf("B is %s", bk) // Pin C recursively @@ -284,7 +287,7 @@ func TestDuplicateSemantics(t *testing.T) { p := NewPinner(dstore, dserv, dserv) a, _ := randNode() - _, err := dserv.Add(a) + err := dserv.Add(ctx, a) if err != nil { t.Fatal(err) } @@ -349,12 +352,12 @@ func TestPinRecursiveFail(t *testing.T) { t.Fatal("should have failed to pin here") } - _, err = dserv.Add(b) + err = dserv.Add(ctx, b) if err != nil { t.Fatal(err) } - _, err = dserv.Add(a) + err = dserv.Add(ctx, a) if err != nil { t.Fatal(err) } @@ -369,6 +372,8 @@ func TestPinRecursiveFail(t *testing.T) { } func TestPinUpdate(t *testing.T) { + ctx := context.Background() + dstore := dssync.MutexWrap(ds.NewMapDatastore()) bstore := blockstore.NewBlockstore(dstore) bserv := bs.New(bstore, offline.Exchange(bstore)) @@ -378,10 +383,9 @@ func TestPinUpdate(t *testing.T) { n1, c1 := randNode() n2, c2 := randNode() - dserv.Add(n1) - dserv.Add(n2) + dserv.Add(ctx, n1) + dserv.Add(ctx, n2) - ctx := context.Background() if err := p.Pin(ctx, n1, true); err != nil { t.Fatal(err) } diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 6d4bff54b..8778df960 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -54,7 +54,7 @@ func (s sortByHash) Swap(a, b int) { s.links[a], s.links[b] = s.links[b], s.links[a] } -func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint64, depth uint32, iter itemIterator, internalKeys keyObserver) (*merkledag.ProtoNode, error) { +func storeItems(ctx context.Context, dag node.DAGService, estimatedLen uint64, depth uint32, iter itemIterator, internalKeys keyObserver) (*merkledag.ProtoNode, error) { links := make([]*node.Link, 0, defaultFanout+maxItems) for i := 0; i < defaultFanout; i++ { links = append(links, &node.Link{Cid: emptyKey}) @@ -139,10 +139,11 @@ func storeItems(ctx context.Context, dag merkledag.DAGService, estimatedLen uint return nil, err } - childKey, err := dag.Add(child) + err = dag.Add(ctx, child) if err != nil { return nil, err } + childKey := child.Cid() internalKeys(childKey) @@ -202,7 +203,7 @@ func writeHdr(n *merkledag.ProtoNode, hdr *pb.Set) error { type walkerFunc func(idx int, link *node.Link) error -func walkItems(ctx context.Context, dag merkledag.DAGService, n *merkledag.ProtoNode, fn walkerFunc, children keyObserver) error { +func walkItems(ctx context.Context, dag node.DAGService, n *merkledag.ProtoNode, fn walkerFunc, children keyObserver) error { hdr, err := readHdr(n) if err != nil { return err @@ -237,7 +238,7 @@ func walkItems(ctx context.Context, dag merkledag.DAGService, n *merkledag.Proto return nil } -func loadSet(ctx context.Context, dag merkledag.DAGService, root *merkledag.ProtoNode, name string, internalKeys keyObserver) ([]*cid.Cid, error) { +func loadSet(ctx context.Context, dag node.DAGService, root *merkledag.ProtoNode, name string, internalKeys keyObserver) ([]*cid.Cid, error) { l, err := root.GetNodeLink(name) if err != nil { return nil, err @@ -280,17 +281,17 @@ func getCidListIterator(cids []*cid.Cid) itemIterator { } } -func storeSet(ctx context.Context, dag merkledag.DAGService, cids []*cid.Cid, internalKeys keyObserver) (*merkledag.ProtoNode, error) { +func storeSet(ctx context.Context, dag node.DAGService, cids []*cid.Cid, internalKeys keyObserver) (*merkledag.ProtoNode, error) { iter := getCidListIterator(cids) n, err := storeItems(ctx, dag, uint64(len(cids)), 0, iter, internalKeys) if err != nil { return nil, err } - c, err := dag.Add(n) + err = dag.Add(ctx, n) if err != nil { return nil, err } - internalKeys(c) + internalKeys(n.Cid()) return n, nil } From de11d7a761a5c85d0f77d9c296000cfe07e40194 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 25 Jan 2018 14:33:42 -0800 Subject: [PATCH 2126/3817] namesys: remove unecessary peerID cast License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@9d6564a12a71b80d9ac3a511ee61bf50ad299777 --- namesys/publisher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 3dc38c4d1..3bdca3771 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -322,7 +322,7 @@ func ValidateIpnsRecord(r *record.ValidationRecord) error { if err != nil { return ErrInvalidAuthor } - if string(pid) != string(r.Author) { + if pid != r.Author { return ErrInvalidAuthor } From 49bab0334779d31a5eaaceebb5b52425b2bd2ffe Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 25 Jan 2018 15:10:07 -0800 Subject: [PATCH 2127/3817] make code-climate happier License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@604df012abe28021cb711050051eaa99571b0f7f --- mfs/dir.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mfs/dir.go b/mfs/dir.go index 5ad39d205..901f69952 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -40,6 +40,10 @@ type Directory struct { name string } +// NewDirectory constructs a new MFS directory. +// +// You probably don't want to call this directly. Instead, construct a new root +// using NewRoot. func NewDirectory(ctx context.Context, name string, node node.Node, parent childCloser, dserv node.DAGService) (*Directory, error) { db, err := uio.NewDirectoryFromNode(dserv, node) if err != nil { From f196506bc2f9c33e03d2dd3b54143ba8575a4bf9 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 25 Jan 2018 15:10:07 -0800 Subject: [PATCH 2128/3817] make code-climate happier License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@e787a157c58c5fec27c83e301a2c6943fefe2a63 --- ipld/merkledag/node.go | 6 ++++-- ipld/merkledag/test/utils.go | 2 ++ ipld/merkledag/utils/diff.go | 1 + ipld/merkledag/utils/utils.go | 12 ++++++++++-- ipld/merkledag/utils/utils_test.go | 4 ++-- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 143e7e075..4cb79ca9c 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -124,7 +124,7 @@ func (n *ProtoNode) AddRawLink(name string, l *node.Link) error { return nil } -// Remove a link on this node by the given name +// RemoveNodeLink removes a link on this node by the given name. func (n *ProtoNode) RemoveNodeLink(name string) error { n.encoded = nil good := make([]*node.Link, 0, len(n.links)) @@ -146,7 +146,7 @@ func (n *ProtoNode) RemoveNodeLink(name string) error { return nil } -// Return a copy of the link with given name +// GetNodeLink returns a copy of the link with the given name. func (n *ProtoNode) GetNodeLink(name string) (*node.Link, error) { for _, l := range n.links { if l.Name == name { @@ -160,6 +160,7 @@ func (n *ProtoNode) GetNodeLink(name string) (*node.Link, error) { return nil, ErrLinkNotFound } +// GetLinkedProtoNode returns a copy of the ProtoNode with the given name. func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds node.DAGService, name string) (*ProtoNode, error) { nd, err := n.GetLinkedNode(ctx, ds, name) if err != nil { @@ -174,6 +175,7 @@ func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds node.DAGService, return pbnd, nil } +// GetLinkedNode returns a copy of the IPLD Node with the given name. func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds node.DAGService, name string) (node.Node, error) { lnk, err := n.GetNodeLink(name) if err != nil { diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index 87bfe23c8..b4a180ac6 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -11,10 +11,12 @@ import ( node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) +// Mock returns a new thread-safe, mock DAGService. func Mock() node.DAGService { return dag.NewDAGService(Bserv()) } +// Bserv returns a new, thread-safe, mock BlockService. func Bserv() bsrv.BlockService { bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) return bsrv.New(bstore, offline.Exchange(bstore)) diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 2ead4bdf7..5dfd37fb7 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -37,6 +37,7 @@ func (c *Change) String() string { } } +// ApplyChange applies the requested changes to the given node in the given dag. func ApplyChange(ctx context.Context, ds node.DAGService, nd *dag.ProtoNode, cs []*Change) (*dag.ProtoNode, error) { e := NewDagEditor(nd, ds) for _, c := range cs { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index dd119834d..eb4413f15 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -27,6 +27,7 @@ type Editor struct { src node.DAGService } +// NewMemoryDagService returns a new, thread-safe in-memory DAGService. func NewMemoryDagService() node.DAGService { // build mem-datastore for editor's intermediary nodes bs := bstore.NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore())) @@ -34,7 +35,10 @@ func NewMemoryDagService() node.DAGService { return dag.NewDAGService(bsrv) } -// root is the node to be modified, source is the dagstore to pull nodes from (optional) +// NewDagEditor returns an ProtoNode editor. +// +// * root is the node to be modified +// * source is the dagstore to pull nodes from (optional) func NewDagEditor(root *dag.ProtoNode, source node.DAGService) *Editor { return &Editor{ root: root, @@ -43,17 +47,19 @@ func NewDagEditor(root *dag.ProtoNode, source node.DAGService) *Editor { } } +// GetNode returns the a copy of the root node being edited. func (e *Editor) GetNode() *dag.ProtoNode { return e.root.Copy().(*dag.ProtoNode) } +// GetDagService returns the DAGService used by this editor. func (e *Editor) GetDagService() node.DAGService { return e.tmp } func addLink(ctx context.Context, ds node.DAGService, root *dag.ProtoNode, childname string, childnd node.Node) (*dag.ProtoNode, error) { if childname == "" { - return nil, errors.New("cannot create link with no name!") + return nil, errors.New("cannot create link with no name") } // ensure that the node we are adding is in the dagservice @@ -188,6 +194,8 @@ func (e *Editor) rmLink(ctx context.Context, root *dag.ProtoNode, path []string) return root, nil } +// Finalize writes the new DAG to the given DAGService and returns the modified +// root node. func (e *Editor) Finalize(ctx context.Context, ds node.DAGService) (*dag.ProtoNode, error) { nd := e.GetNode() err := copyDag(ctx, nd, e.tmp, ds) diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index c404ffed4..345bd2928 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -70,8 +70,8 @@ func TestInsertNode(t *testing.T) { testInsert(t, e, "a/b/c/d/f", "baz", true, "") testInsert(t, e, "a/b/c/d/f", "bar", true, "") - testInsert(t, e, "", "bar", true, "cannot create link with no name!") - testInsert(t, e, "////", "slashes", true, "cannot create link with no name!") + testInsert(t, e, "", "bar", true, "cannot create link with no name") + testInsert(t, e, "////", "slashes", true, "cannot create link with no name") c := e.GetNode().Cid() From c099ce91547f57e738e533ad9c5fa6d8c515d866 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 25 Jan 2018 15:10:07 -0800 Subject: [PATCH 2129/3817] make code-climate happier License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@e8ed89ced222f6f8e20b4af93c2876a0f1b91bd1 --- unixfs/hamt/hamt.go | 2 ++ unixfs/io/dirbuilder.go | 2 ++ unixfs/io/pbdagreader.go | 1 + unixfs/test/utils.go | 5 +++++ 4 files changed, 10 insertions(+) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 2974d4928..e0b063e4f 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -66,6 +66,7 @@ type child interface { Label() string } +// NewHamtShard creates a new, empty HAMT shard with the given size. func NewHamtShard(dserv node.DAGService, size int) (*HamtShard, error) { ds, err := makeHamtShard(dserv, size) if err != nil { @@ -93,6 +94,7 @@ func makeHamtShard(ds node.DAGService, size int) (*HamtShard, error) { }, nil } +// NewHamtFromDag creates new a HAMT shard from the given DAG. func NewHamtFromDag(dserv node.DAGService, nd node.Node) (*HamtShard, error) { pbnd, ok := nd.(*dag.ProtoNode) if !ok { diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index bc9eb6c8e..cc3a3905c 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -51,6 +51,8 @@ func NewDirectory(dserv node.DAGService) *Directory { // ErrNotADir implies that the given node was not a unixfs directory var ErrNotADir = fmt.Errorf("merkledag node was not a directory or shard") +// NewDirectoryFromNode loads a unixfs directory from the given IPLD node and +// DAGService. func NewDirectoryFromNode(dserv node.DAGService, nd node.Node) (*Directory, error) { pbnd, ok := nd.(*mdag.ProtoNode) if !ok { diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 8f0f22d3b..85cbc1968 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -50,6 +50,7 @@ type pbDagReader struct { var _ DagReader = (*pbDagReader)(nil) +// NewPBFileReader constructs a new PBFileReader. func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv node.DAGService) *pbDagReader { fctx, cancel := context.WithCancel(ctx) curLinks := getLinkCids(n) diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 8b234c9ad..1e44b1e9c 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -27,6 +27,7 @@ func SizeSplitterGen(size int64) chunk.SplitterGen { } } +// GetDAGServ returns a mock DAGService. func GetDAGServ() node.DAGService { return mdagmock.Mock() } @@ -51,6 +52,7 @@ func init() { UseBlake2b256.Prefix.MhLength = -1 } +// GetNode returns a unixfs file node with the specified data. func GetNode(t testing.TB, dserv node.DAGService, data []byte, opts NodeOpts) node.Node { in := bytes.NewReader(data) @@ -69,10 +71,12 @@ func GetNode(t testing.TB, dserv node.DAGService, data []byte, opts NodeOpts) no return node } +// GetEmptyNode returns an empty unixfs file node. func GetEmptyNode(t testing.TB, dserv node.DAGService, opts NodeOpts) node.Node { return GetNode(t, dserv, []byte{}, opts) } +// GetRandomNode returns a random unixfs file node. func GetRandomNode(t testing.TB, dserv node.DAGService, size int64, opts NodeOpts) ([]byte, node.Node) { in := io.LimitReader(u.NewTimeSeededRand(), size) buf, err := ioutil.ReadAll(in) @@ -96,6 +100,7 @@ func ArrComp(a, b []byte) error { return nil } +// PrintDag pretty-prints the given dag to stdout. func PrintDag(nd *mdag.ProtoNode, ds node.DAGService, indent int) { pbd, err := ft.FromBytes(nd.Data()) if err != nil { From 0a8ef98c75496ffd0eba909e0afc0a8cb7382e38 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 25 Jan 2018 15:10:07 -0800 Subject: [PATCH 2130/3817] make code-climate happier License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-path@9d312e2f2ff7c40cbfee073dec7ed263bef9e7d5 --- path/resolver.go | 1 + 1 file changed, 1 insertion(+) diff --git a/path/resolver.go b/path/resolver.go index 71ac52ba0..68865283a 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -40,6 +40,7 @@ type Resolver struct { ResolveOnce func(ctx context.Context, ds node.DAGService, nd node.Node, names []string) (*node.Link, []string, error) } +// NewBasicResolver constructs a new basic resolver. func NewBasicResolver(ds node.DAGService) *Resolver { return &Resolver{ DAG: ds, From 179c418b6e5e3e30b2427b8d3003fe23afbbb50e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 25 Jan 2018 15:10:07 -0800 Subject: [PATCH 2131/3817] make code-climate happier License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@1eec61df5cd3421e485f6ff238f273696a114077 --- blockservice/blockservice.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index d4f6fffad..3969dad69 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -22,6 +22,8 @@ var log = logging.Logger("blockservice") var ErrNotFound = errors.New("blockservice: key not found") +// BlockGetter is the common interface shared between blockservice sessions and +// the blockservice. type BlockGetter interface { // GetBlock gets the requested block. GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) @@ -95,12 +97,14 @@ func NewWriteThrough(bs blockstore.Blockstore, rem exchange.Interface) BlockServ } } -func (bs *blockService) Blockstore() blockstore.Blockstore { - return bs.blockstore +// Blockstore returns the blockstore behind this blockservice. +func (s *blockService) Blockstore() blockstore.Blockstore { + return s.blockstore } -func (bs *blockService) Exchange() exchange.Interface { - return bs.exchange +// Exchange returns the exchange behind this blockservice. +func (s *blockService) Exchange() exchange.Interface { + return s.exchange } // NewSession creates a bitswap session that allows for controlled exchange of @@ -286,3 +290,5 @@ func (s *Session) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error func (s *Session) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block { return getBlocks(ctx, ks, s.bs, s.ses) } + +var _ BlockGetter = (*Session)(nil) From b348fd328a98805b68218e0b5736c3927405399e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 26 Jan 2018 00:33:36 -0800 Subject: [PATCH 2132/3817] remove new DHT record author check We're going to just fix this a future commit. *This* change breaks publishing IPNS records using alternative IPNS keys (because the author signature (peer ID) differs from the record signature). We're going to fix it by validating the IPNS signature and ditching the author/signature fields. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@c0dca5d9ae5a17a49ec89cc1ed231f6155b4d248 --- namesys/ipns_validate_test.go | 4 +++- namesys/publisher.go | 33 ++++++++++++++++++--------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index fa7888103..82a6be2c0 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -16,7 +16,7 @@ import ( func TestValidation(t *testing.T) { // Create a record validator validator := make(record.Validator) - validator["ipns"] = &record.ValidChecker{ValidateIpnsRecord, true} + validator["ipns"] = &record.ValidChecker{Func: ValidateIpnsRecord, Sign: true} // Generate a key for signing the records r := u.NewSeededRand(15) // generate deterministic keypair @@ -46,6 +46,7 @@ func TestValidation(t *testing.T) { t.Fatal(err) } + /* TODO(#4613) // Create IPNS record path with a different private key _, ipnsWrongAuthor := genKeys(t, r) wrongAuthorRec, err := record.MakePutRecord(priv, ipnsWrongAuthor, val, true) @@ -97,6 +98,7 @@ func TestValidation(t *testing.T) { if err != ErrInvalidAuthor { t.Fatal("ValidateIpnsRecord should have returned ErrInvalidAuthor") } + */ // Create expired entry expiredEntry, err := CreateRoutingEntryData(priv, path.Path("foo"), 1, ts.Add(-1*time.Hour)) diff --git a/namesys/publisher.go b/namesys/publisher.go index 3bdca3771..473e9042b 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -31,10 +31,6 @@ var ErrExpiredRecord = errors.New("expired record") // unknown validity type. var ErrUnrecognizedValidity = errors.New("unrecognized validity type") -// ErrInvalidAuthor is returned when an IpnsRecord has an -// author that does not match the IPNS path -var ErrInvalidAuthor = errors.New("author does not match path") - // ErrInvalidPath should be returned when an ipns record path // is not in a valid format var ErrInvalidPath = errors.New("record path invalid") @@ -314,17 +310,24 @@ func ValidateIpnsRecord(r *record.ValidationRecord) error { return err } - // Note: The DHT will actually check the signature so we don't - // need to do that here - - // Author in key must match author in record - pid, err := peer.IDFromString(r.Key) - if err != nil { - return ErrInvalidAuthor - } - if pid != r.Author { - return ErrInvalidAuthor - } + // NOTE/FIXME(#4613): We're not checking the DHT signature/author here. + // We're going to remove them in a followup commit and then check the + // *IPNS* signature. However, to do that, we need to ensure we *have* + // the public key and: + // + // 1. Don't want to fetch it from the network when handling PUTs. + // 2. Do want to fetch it from the network when handling GETs. + // + // Therefore, we'll need to either: + // + // 1. Pass some for of offline hint to the validator (e.g., using a context). + // 2. Ensure we pre-fetch the key when performing gets. + // + // This PR is already *way* too large so we're punting that fix to a new + // PR. + // + // This is not a regression, it just restores the current (bad) + // behavior. // Check that record has not expired switch entry.GetValidityType() { From 6a4275ac700dcfea82bcdae0acbef8055cff964a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sat, 27 Jan 2018 18:03:59 -0800 Subject: [PATCH 2133/3817] update go-lib2p-loggables fixes a UUID bug I introduced (UUIDs were always an error value) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@8b9ea5a5b26caedf52907ea9e5b4bb4b9bfaeddb --- namesys/namesys.go | 2 +- namesys/pubsub.go | 2 +- namesys/pubsub_test.go | 4 ++-- namesys/republisher/repub_test.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index e38d58455..673583d96 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -11,9 +11,9 @@ import ( ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" + floodsub "gx/ipfs/QmSjoxpBJV71bpSojnUY1K382Ly3Up55EspnDx6EKAmQX4/go-libp2p-floodsub" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" - floodsub "gx/ipfs/Qma2TkMxcFLVGkYECTo4hrQohBYPx7uhpYL9EejEi8y3Nm/go-libp2p-floodsub" peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" p2phost "gx/ipfs/QmfCtHMCd9xFvehvHeVxtKVXJTMVTuHhyPRVHEXetn87vL/go-libp2p-host" diff --git a/namesys/pubsub.go b/namesys/pubsub.go index c71d82a7d..1bb45ae90 100644 --- a/namesys/pubsub.go +++ b/namesys/pubsub.go @@ -16,9 +16,9 @@ import ( ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" + floodsub "gx/ipfs/QmSjoxpBJV71bpSojnUY1K382Ly3Up55EspnDx6EKAmQX4/go-libp2p-floodsub" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" - floodsub "gx/ipfs/Qma2TkMxcFLVGkYECTo4hrQohBYPx7uhpYL9EejEi8y3Nm/go-libp2p-floodsub" peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" record "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record" diff --git a/namesys/pubsub_test.go b/namesys/pubsub_test.go index abafa732b..bda40899d 100644 --- a/namesys/pubsub_test.go +++ b/namesys/pubsub_test.go @@ -11,9 +11,9 @@ import ( ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" - netutil "gx/ipfs/QmV1axkk86DDkYwS269AvPy9eV5h7mUyHveJkSVHPjrQtY/go-libp2p-netutil" + floodsub "gx/ipfs/QmSjoxpBJV71bpSojnUY1K382Ly3Up55EspnDx6EKAmQX4/go-libp2p-floodsub" + netutil "gx/ipfs/QmWUugnJBbcuin8qdfiCYKAsNkG8NeDLhzoBqRaqXhAHd4/go-libp2p-netutil" bhost "gx/ipfs/QmZ15dDSCo4DKn4o4GnqqLExKATBeeo3oNyQ5FBKtNjEQT/go-libp2p-blankhost" - floodsub "gx/ipfs/Qma2TkMxcFLVGkYECTo4hrQohBYPx7uhpYL9EejEi8y3Nm/go-libp2p-floodsub" peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 08bb700a3..033957200 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -12,7 +12,7 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmNRN4eZGmY89CRC4T5PC4xDYRx6GkDKEfRnvrT65fVeio/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmPd5qhppUqewTQMfStvNNCFtcxiWGsnE6Vs3va6788gsX/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" ) From f3ce7f8aadb0e887a8061229134832344921c96f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sat, 27 Jan 2018 18:59:58 -0800 Subject: [PATCH 2134/3817] handle error from changed NewFloodSub method License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@21a350c043a233a0e115df345b5f8ef190da17ac --- namesys/pubsub_test.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/namesys/pubsub_test.go b/namesys/pubsub_test.go index bda40899d..1fe590cd1 100644 --- a/namesys/pubsub_test.go +++ b/namesys/pubsub_test.go @@ -100,7 +100,11 @@ func TestPubsubPublishSubscribe(t *testing.T) { pubhost := newNetHost(ctx, t) pubmr := newMockRouting(ms, ks, pubhost) - pub := NewPubsubPublisher(ctx, pubhost, ds.NewMapDatastore(), pubmr, floodsub.NewFloodSub(ctx, pubhost)) + fs, err := floodsub.NewFloodSub(ctx, pubhost) + if err != nil { + t.Fatal(err) + } + pub := NewPubsubPublisher(ctx, pubhost, ds.NewMapDatastore(), pubmr, fs) privk := pubhost.Peerstore().PrivKey(pubhost.ID()) pubpinfo := pstore.PeerInfo{ID: pubhost.ID(), Addrs: pubhost.Addrs()} @@ -110,7 +114,13 @@ func TestPubsubPublishSubscribe(t *testing.T) { resmrs := newMockRoutingForHosts(ms, ks, reshosts) res := make([]*PubsubResolver, len(reshosts)) for i := 0; i < len(res); i++ { - res[i] = NewPubsubResolver(ctx, reshosts[i], resmrs[i], ks, floodsub.NewFloodSub(ctx, reshosts[i])) + + fs, err := floodsub.NewFloodSub(ctx, reshosts[i]) + if err != nil { + t.Fatal(err) + } + + res[i] = NewPubsubResolver(ctx, reshosts[i], resmrs[i], ks, fs) if err := reshosts[i].Connect(ctx, pubpinfo); err != nil { t.Fatal(err) } @@ -127,7 +137,7 @@ func TestPubsubPublishSubscribe(t *testing.T) { time.Sleep(time.Second * 1) val := path.Path("/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY") - err := pub.Publish(ctx, privk, val) + err = pub.Publish(ctx, privk, val) if err != nil { t.Fatal(err) } From bdd2167f12f620eac73a269f71ddb9c120eb9133 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 29 Jan 2018 11:55:34 -0800 Subject: [PATCH 2135/3817] rename import of go-ipld-format from node/format to ipld License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@8eaf9ff4d233b594fa0ce9f5a5810fc7f9518469 --- pinning/pinner/gc/gc.go | 14 +++++++------- pinning/pinner/pin.go | 20 ++++++++++---------- pinning/pinner/set.go | 24 ++++++++++++------------ 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index c3ab3db90..f48ebacb6 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -13,7 +13,7 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) var log = logging.Logger("gc") @@ -128,13 +128,13 @@ func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots // ColoredSet computes the set of nodes in the graph that are pinned by the // pins in the given pinner. -func ColoredSet(ctx context.Context, pn pin.Pinner, ng node.NodeGetter, bestEffortRoots []*cid.Cid, output chan<- Result) (*cid.Set, error) { +func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffortRoots []*cid.Cid, output chan<- Result) (*cid.Set, error) { // KeySet currently implemented in memory, in the future, may be bloom filter or // disk backed to conserve memory. errors := false gcs := cid.NewSet() - getLinks := func(ctx context.Context, cid *cid.Cid) ([]*node.Link, error) { - links, err := node.GetLinks(ctx, ng, cid) + getLinks := func(ctx context.Context, cid *cid.Cid) ([]*ipld.Link, error) { + links, err := ipld.GetLinks(ctx, ng, cid) if err != nil { errors = true output <- Result{Error: &CannotFetchLinksError{cid, err}} @@ -147,9 +147,9 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng node.NodeGetter, bestEffo output <- Result{Error: err} } - bestEffortGetLinks := func(ctx context.Context, cid *cid.Cid) ([]*node.Link, error) { - links, err := node.GetLinks(ctx, ng, cid) - if err != nil && err != node.ErrNotFound { + bestEffortGetLinks := func(ctx context.Context, cid *cid.Cid) ([]*ipld.Link, error) { + links, err := ipld.GetLinks(ctx, ng, cid) + if err != nil && err != ipld.ErrNotFound { errors = true output <- Result{Error: &CannotFetchLinksError{cid, err}} } diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 9387439fb..268d8e004 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -15,7 +15,7 @@ import ( ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) var log = logging.Logger("pin") @@ -102,7 +102,7 @@ type Pinner interface { IsPinnedWithType(*cid.Cid, PinMode) (string, bool, error) // Pin the given node, optionally recursively. - Pin(ctx context.Context, node node.Node, recursive bool) error + Pin(ctx context.Context, node ipld.Node, recursive bool) error // Unpin the given cid. If recursive is true, removes either a recursive or // a direct pin. If recursive is false, only removes a direct pin. @@ -178,13 +178,13 @@ type pinner struct { // Track the keys used for storing the pinning state, so gc does // not delete them. internalPin *cid.Set - dserv node.DAGService - internal node.DAGService // dagservice used to store internal objects + dserv ipld.DAGService + internal ipld.DAGService // dagservice used to store internal objects dstore ds.Datastore } // NewPinner creates a new pinner using the given datastore as a backend -func NewPinner(dstore ds.Datastore, serv, internal node.DAGService) Pinner { +func NewPinner(dstore ds.Datastore, serv, internal ipld.DAGService) Pinner { rcset := cid.NewSet() dirset := cid.NewSet() @@ -200,7 +200,7 @@ func NewPinner(dstore ds.Datastore, serv, internal node.DAGService) Pinner { } // Pin the given node, optionally recursive -func (p *pinner) Pin(ctx context.Context, node node.Node, recurse bool) error { +func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { p.lock.Lock() defer p.lock.Unlock() err := p.dserv.Add(ctx, node) @@ -358,7 +358,7 @@ func (p *pinner) CheckIfPinned(cids ...*cid.Cid) ([]Pinned, error) { // Now walk all recursive pins to check for indirect pins var checkChildren func(*cid.Cid, *cid.Cid) error checkChildren = func(rk, parentKey *cid.Cid) error { - links, err := node.GetLinks(context.TODO(), p.dserv, parentKey) + links, err := ipld.GetLinks(context.TODO(), p.dserv, parentKey) if err != nil { return err } @@ -427,7 +427,7 @@ func cidSetWithValues(cids []*cid.Cid) *cid.Set { } // LoadPinner loads a pinner and its keysets from the given datastore -func LoadPinner(d ds.Datastore, dserv, internal node.DAGService) (Pinner, error) { +func LoadPinner(d ds.Datastore, dserv, internal ipld.DAGService) (Pinner, error) { p := new(pinner) rootKeyI, err := d.Get(pinDatastoreKey) @@ -597,8 +597,8 @@ func (p *pinner) PinWithMode(c *cid.Cid, mode PinMode) { // hasChild recursively looks for a Cid among the children of a root Cid. // The visit function can be used to shortcut already-visited branches. -func hasChild(ng node.NodeGetter, root *cid.Cid, child *cid.Cid, visit func(*cid.Cid) bool) (bool, error) { - links, err := node.GetLinks(context.TODO(), ng, root) +func hasChild(ng ipld.NodeGetter, root *cid.Cid, child *cid.Cid, visit func(*cid.Cid) bool) (bool, error) { + links, err := ipld.GetLinks(context.TODO(), ng, root) if err != nil { return false, err } diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 8778df960..e2ba3ed11 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -14,7 +14,7 @@ import ( "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) const ( @@ -39,7 +39,7 @@ type itemIterator func() (c *cid.Cid, ok bool) type keyObserver func(*cid.Cid) type sortByHash struct { - links []*node.Link + links []*ipld.Link } func (s sortByHash) Len() int { @@ -54,10 +54,10 @@ func (s sortByHash) Swap(a, b int) { s.links[a], s.links[b] = s.links[b], s.links[a] } -func storeItems(ctx context.Context, dag node.DAGService, estimatedLen uint64, depth uint32, iter itemIterator, internalKeys keyObserver) (*merkledag.ProtoNode, error) { - links := make([]*node.Link, 0, defaultFanout+maxItems) +func storeItems(ctx context.Context, dag ipld.DAGService, estimatedLen uint64, depth uint32, iter itemIterator, internalKeys keyObserver) (*merkledag.ProtoNode, error) { + links := make([]*ipld.Link, 0, defaultFanout+maxItems) for i := 0; i < defaultFanout; i++ { - links = append(links, &node.Link{Cid: emptyKey}) + links = append(links, &ipld.Link{Cid: emptyKey}) } // add emptyKey to our set of internal pinset objects @@ -85,7 +85,7 @@ func storeItems(ctx context.Context, dag node.DAGService, estimatedLen uint64, d break } - links = append(links, &node.Link{Cid: k}) + links = append(links, &ipld.Link{Cid: k}) } n.SetLinks(links) @@ -148,7 +148,7 @@ func storeItems(ctx context.Context, dag node.DAGService, estimatedLen uint64, d internalKeys(childKey) // overwrite the 'empty key' in the existing links array - n.Links()[h] = &node.Link{ + n.Links()[h] = &ipld.Link{ Cid: childKey, Size: size, } @@ -201,9 +201,9 @@ func writeHdr(n *merkledag.ProtoNode, hdr *pb.Set) error { return nil } -type walkerFunc func(idx int, link *node.Link) error +type walkerFunc func(idx int, link *ipld.Link) error -func walkItems(ctx context.Context, dag node.DAGService, n *merkledag.ProtoNode, fn walkerFunc, children keyObserver) error { +func walkItems(ctx context.Context, dag ipld.DAGService, n *merkledag.ProtoNode, fn walkerFunc, children keyObserver) error { hdr, err := readHdr(n) if err != nil { return err @@ -238,7 +238,7 @@ func walkItems(ctx context.Context, dag node.DAGService, n *merkledag.ProtoNode, return nil } -func loadSet(ctx context.Context, dag node.DAGService, root *merkledag.ProtoNode, name string, internalKeys keyObserver) ([]*cid.Cid, error) { +func loadSet(ctx context.Context, dag ipld.DAGService, root *merkledag.ProtoNode, name string, internalKeys keyObserver) ([]*cid.Cid, error) { l, err := root.GetNodeLink(name) if err != nil { return nil, err @@ -258,7 +258,7 @@ func loadSet(ctx context.Context, dag node.DAGService, root *merkledag.ProtoNode } var res []*cid.Cid - walk := func(idx int, link *node.Link) error { + walk := func(idx int, link *ipld.Link) error { res = append(res, link.Cid) return nil } @@ -281,7 +281,7 @@ func getCidListIterator(cids []*cid.Cid) itemIterator { } } -func storeSet(ctx context.Context, dag node.DAGService, cids []*cid.Cid, internalKeys keyObserver) (*merkledag.ProtoNode, error) { +func storeSet(ctx context.Context, dag ipld.DAGService, cids []*cid.Cid, internalKeys keyObserver) (*merkledag.ProtoNode, error) { iter := getCidListIterator(cids) n, err := storeItems(ctx, dag, uint64(len(cids)), 0, iter, internalKeys) From be5b2da9154308a4c15628a1781d661b29446ec2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 29 Jan 2018 11:55:34 -0800 Subject: [PATCH 2136/3817] rename import of go-ipld-format from node/format to ipld License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@4e431ca9c62b3a55a96e2ef0a95c17a2f9b9c7f2 --- mfs/dir.go | 24 ++++++++++++------------ mfs/file.go | 10 +++++----- mfs/mfs_test.go | 16 ++++++++-------- mfs/ops.go | 4 ++-- mfs/system.go | 12 ++++++------ 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 901f69952..08b703fb2 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -16,7 +16,7 @@ import ( ufspb "github.com/ipfs/go-ipfs/unixfs/pb" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) var ErrNotYetImplemented = errors.New("not yet implemented") @@ -24,7 +24,7 @@ var ErrInvalidChild = errors.New("invalid child node") var ErrDirExists = errors.New("directory already has entry by that name") type Directory struct { - dserv node.DAGService + dserv ipld.DAGService parent childCloser childDirs map[string]*Directory @@ -44,7 +44,7 @@ type Directory struct { // // You probably don't want to call this directly. Instead, construct a new root // using NewRoot. -func NewDirectory(ctx context.Context, name string, node node.Node, parent childCloser, dserv node.DAGService) (*Directory, error) { +func NewDirectory(ctx context.Context, name string, node ipld.Node, parent childCloser, dserv ipld.DAGService) (*Directory, error) { db, err := uio.NewDirectoryFromNode(dserv, node) if err != nil { return nil, err @@ -74,7 +74,7 @@ func (d *Directory) SetPrefix(prefix *cid.Prefix) { // closeChild updates the child by the given name to the dag node 'nd' // and changes its own dag node -func (d *Directory) closeChild(name string, nd node.Node, sync bool) error { +func (d *Directory) closeChild(name string, nd ipld.Node, sync bool) error { mynd, err := d.closeChildUpdate(name, nd, sync) if err != nil { return err @@ -87,7 +87,7 @@ func (d *Directory) closeChild(name string, nd node.Node, sync bool) error { } // closeChildUpdate is the portion of closeChild that needs to be locked around -func (d *Directory) closeChildUpdate(name string, nd node.Node, sync bool) (*dag.ProtoNode, error) { +func (d *Directory) closeChildUpdate(name string, nd ipld.Node, sync bool) (*dag.ProtoNode, error) { d.lock.Lock() defer d.lock.Unlock() @@ -121,7 +121,7 @@ func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) { return pbnd.Copy().(*dag.ProtoNode), nil } -func (d *Directory) updateChild(name string, nd node.Node) error { +func (d *Directory) updateChild(name string, nd ipld.Node) error { err := d.dirbuilder.AddChild(d.ctx, name, nd) if err != nil { return err @@ -148,7 +148,7 @@ func (d *Directory) childNode(name string) (FSNode, error) { } // cacheNode caches a node into d.childDirs or d.files and returns the FSNode. -func (d *Directory) cacheNode(name string, nd node.Node) (FSNode, error) { +func (d *Directory) cacheNode(name string, nd ipld.Node) (FSNode, error) { switch nd := nd.(type) { case *dag.ProtoNode: i, err := ft.FromBytes(nd.Data()) @@ -205,7 +205,7 @@ func (d *Directory) Uncache(name string) { // childFromDag searches through this directories dag node for a child link // with the given name -func (d *Directory) childFromDag(name string) (node.Node, error) { +func (d *Directory) childFromDag(name string) (ipld.Node, error) { return d.dirbuilder.Find(d.ctx, name) } @@ -237,7 +237,7 @@ func (d *Directory) ListNames(ctx context.Context) ([]string, error) { defer d.lock.Unlock() var out []string - err := d.dirbuilder.ForEachLink(ctx, func(l *node.Link) error { + err := d.dirbuilder.ForEachLink(ctx, func(l *ipld.Link) error { out = append(out, l.Name) return nil }) @@ -262,7 +262,7 @@ func (d *Directory) List(ctx context.Context) ([]NodeListing, error) { func (d *Directory) ForEachEntry(ctx context.Context, f func(NodeListing) error) error { d.lock.Lock() defer d.lock.Unlock() - return d.dirbuilder.ForEachLink(ctx, func(l *node.Link) error { + return d.dirbuilder.ForEachLink(ctx, func(l *ipld.Link) error { c, err := d.childUnsync(l.Name) if err != nil { return err @@ -349,7 +349,7 @@ func (d *Directory) Flush() error { } // AddChild adds the node 'nd' under this directory giving it the name 'name' -func (d *Directory) AddChild(name string, nd node.Node) error { +func (d *Directory) AddChild(name string, nd ipld.Node) error { d.lock.Lock() defer d.lock.Unlock() @@ -410,7 +410,7 @@ func (d *Directory) Path() string { return out } -func (d *Directory) GetNode() (node.Node, error) { +func (d *Directory) GetNode() (ipld.Node, error) { d.lock.Lock() defer d.lock.Unlock() diff --git a/mfs/file.go b/mfs/file.go index 033965fa2..496b05d8e 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) type File struct { @@ -20,8 +20,8 @@ type File struct { desclock sync.RWMutex - dserv node.DAGService - node node.Node + dserv ipld.DAGService + node ipld.Node nodelk sync.Mutex RawLeaves bool @@ -29,7 +29,7 @@ type File struct { // NewFile returns a NewFile object with the given parameters. If the // Cid version is non-zero RawLeaves will be enabled. -func NewFile(name string, node node.Node, parent childCloser, dserv node.DAGService) (*File, error) { +func NewFile(name string, node ipld.Node, parent childCloser, dserv ipld.DAGService) (*File, error) { fi := &File{ dserv: dserv, parent: parent, @@ -115,7 +115,7 @@ func (fi *File) Size() (int64, error) { } // GetNode returns the dag node associated with this file -func (fi *File) GetNode() (node.Node, error) { +func (fi *File) GetNode() (ipld.Node, error) { fi.nodelk.Lock() defer fi.nodelk.Unlock() return fi.node, nil diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 3745d8887..5db5d4987 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -28,26 +28,26 @@ import ( ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) func emptyDirNode() *dag.ProtoNode { return dag.NodeWithData(ft.FolderPBData()) } -func getDagserv(t *testing.T) node.DAGService { +func getDagserv(t *testing.T) ipld.DAGService { db := dssync.MutexWrap(ds.NewMapDatastore()) bs := bstore.NewBlockstore(db) blockserv := bserv.New(bs, offline.Exchange(bs)) return dag.NewDAGService(blockserv) } -func getRandFile(t *testing.T, ds node.DAGService, size int64) node.Node { +func getRandFile(t *testing.T, ds ipld.DAGService, size int64) ipld.Node { r := io.LimitReader(u.NewTimeSeededRand(), size) return fileNodeFromReader(t, ds, r) } -func fileNodeFromReader(t *testing.T, ds node.DAGService, r io.Reader) node.Node { +func fileNodeFromReader(t *testing.T, ds ipld.DAGService, r io.Reader) ipld.Node { nd, err := importer.BuildDagFromReader(ds, chunk.DefaultSplitter(r)) if err != nil { t.Fatal(err) @@ -128,7 +128,7 @@ func compStrArrs(a, b []string) bool { return true } -func assertFileAtPath(ds node.DAGService, root *Directory, expn node.Node, pth string) error { +func assertFileAtPath(ds ipld.DAGService, root *Directory, expn ipld.Node, pth string) error { exp, ok := expn.(*dag.ProtoNode) if !ok { return dag.ErrNotProtobuf @@ -182,7 +182,7 @@ func assertFileAtPath(ds node.DAGService, root *Directory, expn node.Node, pth s return nil } -func catNode(ds node.DAGService, nd *dag.ProtoNode) ([]byte, error) { +func catNode(ds ipld.DAGService, nd *dag.ProtoNode) ([]byte, error) { r, err := uio.NewDagReader(context.TODO(), nd, ds) if err != nil { return nil, err @@ -192,7 +192,7 @@ func catNode(ds node.DAGService, nd *dag.ProtoNode) ([]byte, error) { return ioutil.ReadAll(r) } -func setupRoot(ctx context.Context, t *testing.T) (node.DAGService, *Root) { +func setupRoot(ctx context.Context, t *testing.T) (ipld.DAGService, *Root) { ds := getDagserv(t) root := emptyDirNode() @@ -300,7 +300,7 @@ func TestDirectoryLoadFromDag(t *testing.T) { dirhash := dir.Cid() top := emptyDirNode() - top.SetLinks([]*node.Link{ + top.SetLinks([]*ipld.Link{ { Name: "a", Cid: fihash, diff --git a/mfs/ops.go b/mfs/ops.go index 9d7182124..e6ad1a3be 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -10,7 +10,7 @@ import ( path "github.com/ipfs/go-ipfs/path" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // Mv moves the file or directory at 'src' to 'dst' @@ -84,7 +84,7 @@ func lookupDir(r *Root, path string) (*Directory, error) { } // PutNode inserts 'nd' at 'path' in the given mfs -func PutNode(r *Root, path string, nd node.Node) error { +func PutNode(r *Root, path string, nd ipld.Node) error { dirp, filename := gopath.Split(path) if filename == "" { return fmt.Errorf("cannot create file with empty name") diff --git a/mfs/system.go b/mfs/system.go index 5c30db57c..2150dc621 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -21,7 +21,7 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) var ErrNotExist = errors.New("no such rootfs") @@ -31,7 +31,7 @@ var log = logging.Logger("mfs") var ErrIsDirectory = errors.New("error: is a directory") type childCloser interface { - closeChild(string, node.Node, bool) error + closeChild(string, ipld.Node, bool) error } type NodeType int @@ -43,7 +43,7 @@ const ( // FSNode represents any node (directory, root, or file) in the mfs filesystem. type FSNode interface { - GetNode() (node.Node, error) + GetNode() (ipld.Node, error) Flush() error Type() NodeType } @@ -58,7 +58,7 @@ type Root struct { repub *Republisher - dserv node.DAGService + dserv ipld.DAGService Type string } @@ -67,7 +67,7 @@ type Root struct { type PubFunc func(context.Context, *cid.Cid) error // NewRoot creates a new Root and starts up a republisher routine for it. -func NewRoot(parent context.Context, ds node.DAGService, node *dag.ProtoNode, pf PubFunc) (*Root, error) { +func NewRoot(parent context.Context, ds ipld.DAGService, node *dag.ProtoNode, pf PubFunc) (*Root, error) { var repub *Republisher if pf != nil { @@ -159,7 +159,7 @@ func (kr *Root) FlushMemFree(ctx context.Context) error { // closeChild implements the childCloser interface, and signals to the publisher that // there are changes ready to be published. -func (kr *Root) closeChild(name string, nd node.Node, sync bool) error { +func (kr *Root) closeChild(name string, nd ipld.Node, sync bool) error { err := kr.dserv.Add(context.TODO(), nd) if err != nil { return err From b1963a87d0bb908bad1320cea16b2a4ed574efa5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 29 Jan 2018 11:55:34 -0800 Subject: [PATCH 2137/3817] rename import of go-ipld-format from node/format to ipld License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@129535d04cee7bf8932ef41c4ca756a2cf9d2b2a --- ipld/merkledag/coding.go | 10 ++-- ipld/merkledag/merkledag.go | 72 ++++++++++++------------ ipld/merkledag/merkledag_test.go | 20 +++---- ipld/merkledag/node.go | 46 +++++++-------- ipld/merkledag/node_test.go | 12 ++-- ipld/merkledag/raw.go | 18 +++--- ipld/merkledag/test/utils.go | 4 +- ipld/merkledag/traverse/traverse.go | 14 ++--- ipld/merkledag/traverse/traverse_test.go | 16 +++--- ipld/merkledag/utils/diff.go | 6 +- ipld/merkledag/utils/diffenum.go | 10 ++-- ipld/merkledag/utils/diffenum_test.go | 18 +++--- ipld/merkledag/utils/utils.go | 28 ++++----- ipld/merkledag/utils/utils_test.go | 4 +- 14 files changed, 139 insertions(+), 139 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 660052796..37d2a56a0 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -10,7 +10,7 @@ import ( pb "github.com/ipfs/go-ipfs/merkledag/pb" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // for now, we use a PBNode intermediate thing. @@ -25,9 +25,9 @@ func (n *ProtoNode) unmarshal(encoded []byte) error { } pbnl := pbn.GetLinks() - n.links = make([]*node.Link, len(pbnl)) + n.links = make([]*ipld.Link, len(pbnl)) for i, l := range pbnl { - n.links[i] = &node.Link{Name: l.GetName(), Size: l.GetTsize()} + n.links[i] = &ipld.Link{Name: l.GetName(), Size: l.GetTsize()} c, err := cid.Cast(l.GetHash()) if err != nil { return fmt.Errorf("Link hash #%d is not valid multihash. %v", i, err) @@ -114,7 +114,7 @@ func DecodeProtobuf(encoded []byte) (*ProtoNode, error) { // DecodeProtobufBlock is a block decoder for protobuf IPLD nodes conforming to // node.DecodeBlockFunc -func DecodeProtobufBlock(b blocks.Block) (node.Node, error) { +func DecodeProtobufBlock(b blocks.Block) (ipld.Node, error) { c := b.Cid() if c.Type() != cid.DagProtobuf { return nil, fmt.Errorf("this function can only decode protobuf nodes") @@ -134,4 +134,4 @@ func DecodeProtobufBlock(b blocks.Block) (node.Node, error) { } // Type assertion -var _ node.DecodeBlockFunc = DecodeProtobufBlock +var _ ipld.DecodeBlockFunc = DecodeProtobufBlock diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 33b097510..fd66fb269 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -10,7 +10,7 @@ import ( ipldcbor "gx/ipfs/QmNRz7BDWfdFNVLt7AVvmRefkrURD25EeoipcXqo6yoXU1/go-ipld-cbor" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) @@ -18,9 +18,9 @@ import ( // functionality should go in a `go-ipld` repo but that will take a lot of work // and design. func init() { - node.Register(cid.DagProtobuf, DecodeProtobufBlock) - node.Register(cid.Raw, DecodeRawBlock) - node.Register(cid.DagCBOR, ipldcbor.DecodeBlock) + ipld.Register(cid.DagProtobuf, DecodeProtobufBlock) + ipld.Register(cid.Raw, DecodeRawBlock) + ipld.Register(cid.DagCBOR, ipldcbor.DecodeBlock) } // NewDAGService constructs a new DAGService (using the default implementation). @@ -38,7 +38,7 @@ type dagService struct { } // Add adds a node to the dagService, storing the block in the BlockService -func (n *dagService) Add(ctx context.Context, nd node.Node) error { +func (n *dagService) Add(ctx context.Context, nd ipld.Node) error { if n == nil { // FIXME remove this assertion. protect with constructor invariant return fmt.Errorf("dagService is nil") } @@ -46,7 +46,7 @@ func (n *dagService) Add(ctx context.Context, nd node.Node) error { return n.Blocks.AddBlock(nd) } -func (n *dagService) AddMany(ctx context.Context, nds []node.Node) error { +func (n *dagService) AddMany(ctx context.Context, nds []ipld.Node) error { blks := make([]blocks.Block, len(nds)) for i, nd := range nds { blks[i] = nd @@ -55,7 +55,7 @@ func (n *dagService) AddMany(ctx context.Context, nds []node.Node) error { } // Get retrieves a node from the dagService, fetching the block in the BlockService -func (n *dagService) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { +func (n *dagService) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { if n == nil { return nil, fmt.Errorf("dagService is nil") } @@ -66,17 +66,17 @@ func (n *dagService) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { b, err := n.Blocks.GetBlock(ctx, c) if err != nil { if err == bserv.ErrNotFound { - return nil, node.ErrNotFound + return nil, ipld.ErrNotFound } return nil, fmt.Errorf("Failed to get block for %s: %v", c, err) } - return node.Decode(b) + return ipld.Decode(b) } // GetLinks return the links for the node, the node doesn't necessarily have // to exist locally. -func (n *dagService) GetLinks(ctx context.Context, c *cid.Cid) ([]*node.Link, error) { +func (n *dagService) GetLinks(ctx context.Context, c *cid.Cid) ([]*ipld.Link, error) { if c.Type() == cid.Raw { return nil, nil } @@ -109,12 +109,12 @@ func (n *dagService) RemoveMany(ctx context.Context, cids []*cid.Cid) error { // GetLinksDirect creates a function to get the links for a node, from // the node, bypassing the LinkService. If the node does not exist // locally (and can not be retrieved) an error will be returned. -func GetLinksDirect(serv node.NodeGetter) GetLinks { - return func(ctx context.Context, c *cid.Cid) ([]*node.Link, error) { +func GetLinksDirect(serv ipld.NodeGetter) GetLinks { + return func(ctx context.Context, c *cid.Cid) ([]*ipld.Link, error) { nd, err := serv.Get(ctx, c) if err != nil { if err == bserv.ErrNotFound { - err = node.ErrNotFound + err = ipld.ErrNotFound } return nil, err } @@ -127,28 +127,28 @@ type sesGetter struct { } // Get gets a single node from the DAG. -func (sg *sesGetter) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { +func (sg *sesGetter) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { blk, err := sg.bs.GetBlock(ctx, c) switch err { case bserv.ErrNotFound: - return nil, node.ErrNotFound + return nil, ipld.ErrNotFound default: return nil, err case nil: // noop } - return node.Decode(blk) + return ipld.Decode(blk) } // GetMany gets many nodes at once, batching the request if possible. -func (sg *sesGetter) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *node.NodeOption { +func (sg *sesGetter) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *ipld.NodeOption { return getNodesFromBG(ctx, sg.bs, keys) } // FetchGraph fetches all nodes that are children of the given node -func FetchGraph(ctx context.Context, root *cid.Cid, serv node.DAGService) error { - var ng node.NodeGetter = serv +func FetchGraph(ctx context.Context, root *cid.Cid, serv ipld.DAGService) error { + var ng ipld.NodeGetter = serv ds, ok := serv.(*dagService) if ok { ng = &sesGetter{bserv.NewSession(ctx, ds.Blocks)} @@ -187,12 +187,12 @@ func FindLinks(links []*cid.Cid, c *cid.Cid, start int) []int { // This method may not return all requested nodes (and may or may not return an // error indicating that it failed to do so. It is up to the caller to verify // that it received all nodes. -func (n *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *node.NodeOption { +func (n *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *ipld.NodeOption { return getNodesFromBG(ctx, n.Blocks, keys) } -func getNodesFromBG(ctx context.Context, bs bserv.BlockGetter, keys []*cid.Cid) <-chan *node.NodeOption { - out := make(chan *node.NodeOption, len(keys)) +func getNodesFromBG(ctx context.Context, bs bserv.BlockGetter, keys []*cid.Cid) <-chan *ipld.NodeOption { + out := make(chan *ipld.NodeOption, len(keys)) blocks := bs.GetBlocks(ctx, keys) var count int @@ -203,22 +203,22 @@ func getNodesFromBG(ctx context.Context, bs bserv.BlockGetter, keys []*cid.Cid) case b, ok := <-blocks: if !ok { if count != len(keys) { - out <- &node.NodeOption{Err: fmt.Errorf("failed to fetch all nodes")} + out <- &ipld.NodeOption{Err: fmt.Errorf("failed to fetch all nodes")} } return } - nd, err := node.Decode(b) + nd, err := ipld.Decode(b) if err != nil { - out <- &node.NodeOption{Err: err} + out <- &ipld.NodeOption{Err: err} return } - out <- &node.NodeOption{Node: nd} + out <- &ipld.NodeOption{Node: nd} count++ case <-ctx.Done(): - out <- &node.NodeOption{Err: ctx.Err()} + out <- &ipld.NodeOption{Err: ctx.Err()} return } } @@ -228,15 +228,15 @@ func getNodesFromBG(ctx context.Context, bs bserv.BlockGetter, keys []*cid.Cid) // GetLinks is the type of function passed to the EnumerateChildren function(s) // for getting the children of an IPLD node. -type GetLinks func(context.Context, *cid.Cid) ([]*node.Link, error) +type GetLinks func(context.Context, *cid.Cid) ([]*ipld.Link, error) // GetLinksWithDAG returns a GetLinks function that tries to use the given // NodeGetter as a LinkGetter to get the children of a given IPLD node. This may // allow us to traverse the DAG without actually loading and parsing the node in // question (if we already have the links cached). -func GetLinksWithDAG(ng node.NodeGetter) GetLinks { - return func(ctx context.Context, c *cid.Cid) ([]*node.Link, error) { - return node.GetLinks(ctx, ng, c) +func GetLinksWithDAG(ng ipld.NodeGetter) GetLinks { + return func(ctx context.Context, c *cid.Cid) ([]*ipld.Link, error) { + return ipld.GetLinks(ctx, ng, c) } } @@ -291,7 +291,7 @@ var FetchGraphConcurrency = 8 // NOTE: It *does not* make multiple concurrent calls to the passed `visit` function. func EnumerateChildrenAsync(ctx context.Context, getLinks GetLinks, c *cid.Cid, visit func(*cid.Cid) bool) error { feed := make(chan *cid.Cid) - out := make(chan []*node.Link) + out := make(chan []*ipld.Link) done := make(chan struct{}) var setlk sync.Mutex @@ -370,7 +370,7 @@ func EnumerateChildrenAsync(ctx context.Context, getLinks GetLinks, c *cid.Cid, } -var _ node.LinkGetter = &dagService{} -var _ node.NodeGetter = &dagService{} -var _ node.NodeGetter = &sesGetter{} -var _ node.DAGService = &dagService{} +var _ ipld.LinkGetter = &dagService{} +var _ ipld.NodeGetter = &dagService{} +var _ ipld.NodeGetter = &sesGetter{} +var _ ipld.DAGService = &dagService{} diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index b23cc4feb..9ca3d79e3 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -26,7 +26,7 @@ import ( u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) func TestNode(t *testing.T) { @@ -88,7 +88,7 @@ func SubtestNodeStat(t *testing.T, n *ProtoNode) { k := n.Cid() - expected := node.NodeStat{ + expected := ipld.NodeStat{ NumLinks: len(n.Links()), BlockSize: len(enc), LinksSize: len(enc) - len(n.Data()), // includes framing. @@ -131,7 +131,7 @@ func TestBatchFetchDupBlock(t *testing.T) { func runBatchFetchTest(t *testing.T, read io.Reader) { ctx := context.Background() - var dagservs []node.DAGService + var dagservs []ipld.DAGService for _, bsi := range bstest.Mocks(5) { dagservs = append(dagservs, NewDAGService(bsi)) } @@ -221,7 +221,7 @@ func TestCantGet(t *testing.T) { } func TestFetchGraph(t *testing.T) { - var dservs []node.DAGService + var dservs []ipld.DAGService bsis := bstest.Mocks(2) for _, bsi := range bsis { dservs = append(dservs, NewDAGService(bsi)) @@ -265,8 +265,8 @@ func TestEnumerateChildren(t *testing.T) { t.Fatal(err) } - var traverse func(n node.Node) - traverse = func(n node.Node) { + var traverse func(n ipld.Node) + traverse = func(n ipld.Node) { // traverse dag and check for _, lnk := range n.Links() { c := lnk.Cid @@ -317,7 +317,7 @@ func TestFetchFailure(t *testing.T) { } } - getters := node.GetDAG(ctx, ds, top) + getters := ipld.GetDAG(ctx, ds, top) for i, getter := range getters { _, err := getter.Get(ctx) if err != nil && i < 10 { @@ -433,7 +433,7 @@ func TestGetRawNodes(t *testing.T) { func TestProtoNodeResolve(t *testing.T) { nd := new(ProtoNode) - nd.SetLinks([]*node.Link{{Name: "foo"}}) + nd.SetLinks([]*ipld.Link{{Name: "foo"}}) lnk, left, err := nd.ResolveLink([]string{"foo", "bar"}) if err != nil { @@ -517,7 +517,7 @@ func TestEnumerateAsyncFailsNotFound(t *testing.T) { d := NodeWithData([]byte("foo4")) ds := dstest.Mock() - for _, n := range []node.Node{a, b, c} { + for _, n := range []ipld.Node{a, b, c} { err := ds.Add(ctx, n) if err != nil { t.Fatal(err) @@ -580,7 +580,7 @@ func testProgressIndicator(t *testing.T, depth int) { } } -func mkDag(ds node.DAGService, depth int) (*cid.Cid, int) { +func mkDag(ds ipld.DAGService, depth int) (*cid.Cid, int) { ctx := context.Background() totalChildren := 0 diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 4cb79ca9c..215161258 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -7,7 +7,7 @@ import ( mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") @@ -16,7 +16,7 @@ var ErrLinkNotFound = fmt.Errorf("no link by that name") // Node represents a node in the IPFS Merkle DAG. // nodes have opaque data and a set of navigable links. type ProtoNode struct { - links []*node.Link + links []*ipld.Link data []byte // cache encoded/marshaled value @@ -73,7 +73,7 @@ func (n *ProtoNode) SetPrefix(prefix *cid.Prefix) { } } -type LinkSlice []*node.Link +type LinkSlice []*ipld.Link func (ls LinkSlice) Len() int { return len(ls) } func (ls LinkSlice) Swap(a, b int) { ls[a], ls[b] = ls[b], ls[a] } @@ -84,10 +84,10 @@ func NodeWithData(d []byte) *ProtoNode { } // AddNodeLink adds a link to another node. -func (n *ProtoNode) AddNodeLink(name string, that node.Node) error { +func (n *ProtoNode) AddNodeLink(name string, that ipld.Node) error { n.encoded = nil - lnk, err := node.MakeLink(that) + lnk, err := ipld.MakeLink(that) if err != nil { return err } @@ -101,9 +101,9 @@ func (n *ProtoNode) AddNodeLink(name string, that node.Node) error { // AddNodeLinkClean adds a link to another node. without keeping a reference to // the child node -func (n *ProtoNode) AddNodeLinkClean(name string, that node.Node) error { +func (n *ProtoNode) AddNodeLinkClean(name string, that ipld.Node) error { n.encoded = nil - lnk, err := node.MakeLink(that) + lnk, err := ipld.MakeLink(that) if err != nil { return err } @@ -113,9 +113,9 @@ func (n *ProtoNode) AddNodeLinkClean(name string, that node.Node) error { } // AddRawLink adds a copy of a link to this node -func (n *ProtoNode) AddRawLink(name string, l *node.Link) error { +func (n *ProtoNode) AddRawLink(name string, l *ipld.Link) error { n.encoded = nil - n.links = append(n.links, &node.Link{ + n.links = append(n.links, &ipld.Link{ Name: name, Size: l.Size, Cid: l.Cid, @@ -127,7 +127,7 @@ func (n *ProtoNode) AddRawLink(name string, l *node.Link) error { // RemoveNodeLink removes a link on this node by the given name. func (n *ProtoNode) RemoveNodeLink(name string) error { n.encoded = nil - good := make([]*node.Link, 0, len(n.links)) + good := make([]*ipld.Link, 0, len(n.links)) var found bool for _, l := range n.links { @@ -140,17 +140,17 @@ func (n *ProtoNode) RemoveNodeLink(name string) error { n.links = good if !found { - return node.ErrNotFound + return ipld.ErrNotFound } return nil } // GetNodeLink returns a copy of the link with the given name. -func (n *ProtoNode) GetNodeLink(name string) (*node.Link, error) { +func (n *ProtoNode) GetNodeLink(name string) (*ipld.Link, error) { for _, l := range n.links { if l.Name == name { - return &node.Link{ + return &ipld.Link{ Name: l.Name, Size: l.Size, Cid: l.Cid, @@ -161,7 +161,7 @@ func (n *ProtoNode) GetNodeLink(name string) (*node.Link, error) { } // GetLinkedProtoNode returns a copy of the ProtoNode with the given name. -func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds node.DAGService, name string) (*ProtoNode, error) { +func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds ipld.DAGService, name string) (*ProtoNode, error) { nd, err := n.GetLinkedNode(ctx, ds, name) if err != nil { return nil, err @@ -176,7 +176,7 @@ func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds node.DAGService, } // GetLinkedNode returns a copy of the IPLD Node with the given name. -func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds node.DAGService, name string) (node.Node, error) { +func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds ipld.DAGService, name string) (ipld.Node, error) { lnk, err := n.GetNodeLink(name) if err != nil { return nil, err @@ -187,7 +187,7 @@ func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds node.DAGService, name // Copy returns a copy of the node. // NOTE: Does not make copies of Node objects in the links. -func (n *ProtoNode) Copy() node.Node { +func (n *ProtoNode) Copy() ipld.Node { nnode := new(ProtoNode) if len(n.data) > 0 { nnode.data = make([]byte, len(n.data)) @@ -195,7 +195,7 @@ func (n *ProtoNode) Copy() node.Node { } if len(n.links) > 0 { - nnode.links = make([]*node.Link, len(n.links)) + nnode.links = make([]*ipld.Link, len(n.links)) copy(nnode.links, n.links) } @@ -244,7 +244,7 @@ func (n *ProtoNode) Size() (uint64, error) { } // Stat returns statistics on the node. -func (n *ProtoNode) Stat() (*node.NodeStat, error) { +func (n *ProtoNode) Stat() (*ipld.NodeStat, error) { enc, err := n.EncodeProtobuf(false) if err != nil { return nil, err @@ -255,7 +255,7 @@ func (n *ProtoNode) Stat() (*node.NodeStat, error) { return nil, err } - return &node.NodeStat{ + return &ipld.NodeStat{ Hash: n.Cid().String(), NumLinks: len(n.links), BlockSize: len(enc), @@ -274,7 +274,7 @@ func (n *ProtoNode) Loggable() map[string]interface{} { func (n *ProtoNode) UnmarshalJSON(b []byte) error { s := struct { Data []byte `json:"data"` - Links []*node.Link `json:"links"` + Links []*ipld.Link `json:"links"` }{} err := json.Unmarshal(b, &s) @@ -332,11 +332,11 @@ func (n *ProtoNode) Multihash() mh.Multihash { return n.cached.Hash() } -func (n *ProtoNode) Links() []*node.Link { +func (n *ProtoNode) Links() []*ipld.Link { return n.links } -func (n *ProtoNode) SetLinks(links []*node.Link) { +func (n *ProtoNode) SetLinks(links []*ipld.Link) { n.links = links } @@ -344,7 +344,7 @@ func (n *ProtoNode) Resolve(path []string) (interface{}, []string, error) { return n.ResolveLink(path) } -func (n *ProtoNode) ResolveLink(path []string) (*node.Link, []string, error) { +func (n *ProtoNode) ResolveLink(path []string) (*ipld.Link, []string, error) { if len(path) == 0 { return nil, nil, fmt.Errorf("end of path, no more links to resolve") } diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index 4b6a412d2..273aaa5b4 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -8,12 +8,12 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) func TestRemoveLink(t *testing.T) { nd := &ProtoNode{} - nd.SetLinks([]*node.Link{ + nd.SetLinks([]*ipld.Link{ {Name: "a"}, {Name: "b"}, {Name: "a"}, @@ -41,7 +41,7 @@ func TestRemoveLink(t *testing.T) { // should fail err = nd.RemoveNodeLink("a") - if err != node.ErrNotFound { + if err != ipld.ErrNotFound { t.Fatal("should have failed to remove link") } @@ -72,7 +72,7 @@ func TestFindLink(t *testing.T) { kEmpty := ndEmpty.Cid() nd := &ProtoNode{} - nd.SetLinks([]*node.Link{ + nd.SetLinks([]*ipld.Link{ {Name: "a", Cid: kEmpty}, {Name: "c", Cid: kEmpty}, {Name: "b", Cid: kEmpty}, @@ -119,7 +119,7 @@ func TestFindLink(t *testing.T) { func TestNodeCopy(t *testing.T) { nd := &ProtoNode{} - nd.SetLinks([]*node.Link{ + nd.SetLinks([]*ipld.Link{ {Name: "a"}, {Name: "c"}, {Name: "b"}, @@ -137,7 +137,7 @@ func TestNodeCopy(t *testing.T) { func TestJsonRoundtrip(t *testing.T) { nd := new(ProtoNode) - nd.SetLinks([]*node.Link{ + nd.SetLinks([]*ipld.Link{ {Name: "a"}, {Name: "c"}, {Name: "b"}, diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 2d195dd5a..33f881550 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -6,7 +6,7 @@ import ( u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) type RawNode struct { @@ -24,7 +24,7 @@ func NewRawNode(data []byte) *RawNode { } // DecodeRawBlock is a block decoder for raw IPLD nodes conforming to `node.DecodeBlockFunc`. -func DecodeRawBlock(block blocks.Block) (node.Node, error) { +func DecodeRawBlock(block blocks.Block) (ipld.Node, error) { if block.Cid().Type() != cid.Raw { return nil, fmt.Errorf("raw nodes cannot be decoded from non-raw blocks: %d", block.Cid().Type()) } @@ -32,7 +32,7 @@ func DecodeRawBlock(block blocks.Block) (node.Node, error) { return &RawNode{block}, nil } -var _ node.DecodeBlockFunc = DecodeRawBlock +var _ ipld.DecodeBlockFunc = DecodeRawBlock // NewRawNodeWPrefix creates a RawNode with the hash function // specified in prefix. @@ -52,11 +52,11 @@ func NewRawNodeWPrefix(data []byte, prefix cid.Prefix) (*RawNode, error) { return &RawNode{blk}, nil } -func (rn *RawNode) Links() []*node.Link { +func (rn *RawNode) Links() []*ipld.Link { return nil } -func (rn *RawNode) ResolveLink(path []string) (*node.Link, []string, error) { +func (rn *RawNode) ResolveLink(path []string) (*ipld.Link, []string, error) { return nil, nil, ErrLinkNotFound } @@ -68,7 +68,7 @@ func (rn *RawNode) Tree(p string, depth int) []string { return nil } -func (rn *RawNode) Copy() node.Node { +func (rn *RawNode) Copy() ipld.Node { copybuf := make([]byte, len(rn.RawData())) copy(copybuf, rn.RawData()) nblk, err := blocks.NewBlockWithCid(rn.RawData(), rn.Cid()) @@ -84,11 +84,11 @@ func (rn *RawNode) Size() (uint64, error) { return uint64(len(rn.RawData())), nil } -func (rn *RawNode) Stat() (*node.NodeStat, error) { - return &node.NodeStat{ +func (rn *RawNode) Stat() (*ipld.NodeStat, error) { + return &ipld.NodeStat{ CumulativeSize: len(rn.RawData()), DataSize: len(rn.RawData()), }, nil } -var _ node.Node = (*RawNode)(nil) +var _ ipld.Node = (*RawNode)(nil) diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index b4a180ac6..efaec1d45 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -8,11 +8,11 @@ import ( ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // Mock returns a new thread-safe, mock DAGService. -func Mock() node.DAGService { +func Mock() ipld.DAGService { return dag.NewDAGService(Bserv()) } diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index d12c9f301..c649cb02a 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -5,7 +5,7 @@ import ( "context" "errors" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // Order is an identifier for traversal algorithm orders @@ -19,7 +19,7 @@ const ( // Options specifies a series of traversal options type Options struct { - DAG node.NodeGetter // the dagservice to fetch nodes + DAG ipld.NodeGetter // the dagservice to fetch nodes Order Order // what order to traverse in Func Func // the function to perform at each step ErrFunc ErrFunc // see ErrFunc. Optional @@ -29,7 +29,7 @@ type Options struct { // State is a current traversal state type State struct { - Node node.Node + Node ipld.Node Depth int } @@ -38,7 +38,7 @@ type traversal struct { seen map[string]struct{} } -func (t *traversal) shouldSkip(n node.Node) (bool, error) { +func (t *traversal) shouldSkip(n ipld.Node) (bool, error) { if t.opts.SkipDuplicates { k := n.Cid() if _, found := t.seen[k.KeyString()]; found { @@ -58,9 +58,9 @@ func (t *traversal) callFunc(next State) error { // stop processing. if it returns a nil node, just skip it. // // the error handling is a little complicated. -func (t *traversal) getNode(link *node.Link) (node.Node, error) { +func (t *traversal) getNode(link *ipld.Link) (ipld.Node, error) { - getNode := func(l *node.Link) (node.Node, error) { + getNode := func(l *ipld.Link) (ipld.Node, error) { next, err := l.GetNode(context.TODO(), t.opts.DAG) if err != nil { return nil, err @@ -98,7 +98,7 @@ type Func func(current State) error // type ErrFunc func(err error) error -func Traverse(root node.Node, o Options) error { +func Traverse(root ipld.Node, o Options) error { t := traversal{ opts: o, seen: map[string]struct{}{}, diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 8c32a6934..c9fcb4136 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -9,7 +9,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagtest "github.com/ipfs/go-ipfs/merkledag/test" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) func TestDFSPreNoSkip(t *testing.T) { @@ -324,7 +324,7 @@ func TestBFSSkip(t *testing.T) { `)) } -func testWalkOutputs(t *testing.T, root node.Node, opts Options, expect []byte) { +func testWalkOutputs(t *testing.T, root ipld.Node, opts Options, expect []byte) { expect = bytes.TrimLeft(expect, "\n") buf := new(bytes.Buffer) @@ -351,7 +351,7 @@ func testWalkOutputs(t *testing.T, root node.Node, opts Options, expect []byte) } } -func newFan(t *testing.T, ds node.DAGService) node.Node { +func newFan(t *testing.T, ds ipld.DAGService) ipld.Node { a := mdag.NodeWithData([]byte("/a")) addLink(t, ds, a, child(t, ds, a, "aa")) addLink(t, ds, a, child(t, ds, a, "ab")) @@ -360,7 +360,7 @@ func newFan(t *testing.T, ds node.DAGService) node.Node { return a } -func newLinkedList(t *testing.T, ds node.DAGService) node.Node { +func newLinkedList(t *testing.T, ds ipld.DAGService) ipld.Node { a := mdag.NodeWithData([]byte("/a")) aa := child(t, ds, a, "aa") aaa := child(t, ds, aa, "aaa") @@ -373,7 +373,7 @@ func newLinkedList(t *testing.T, ds node.DAGService) node.Node { return a } -func newBinaryTree(t *testing.T, ds node.DAGService) node.Node { +func newBinaryTree(t *testing.T, ds ipld.DAGService) ipld.Node { a := mdag.NodeWithData([]byte("/a")) aa := child(t, ds, a, "aa") ab := child(t, ds, a, "ab") @@ -386,7 +386,7 @@ func newBinaryTree(t *testing.T, ds node.DAGService) node.Node { return a } -func newBinaryDAG(t *testing.T, ds node.DAGService) node.Node { +func newBinaryDAG(t *testing.T, ds ipld.DAGService) ipld.Node { a := mdag.NodeWithData([]byte("/a")) aa := child(t, ds, a, "aa") aaa := child(t, ds, aa, "aaa") @@ -403,7 +403,7 @@ func newBinaryDAG(t *testing.T, ds node.DAGService) node.Node { return a } -func addLink(t *testing.T, ds node.DAGService, a, b node.Node) { +func addLink(t *testing.T, ds ipld.DAGService, a, b ipld.Node) { to := string(a.(*mdag.ProtoNode).Data()) + "2" + string(b.(*mdag.ProtoNode).Data()) if err := ds.Add(context.Background(), b); err != nil { t.Error(err) @@ -413,6 +413,6 @@ func addLink(t *testing.T, ds node.DAGService, a, b node.Node) { } } -func child(t *testing.T, ds node.DAGService, a node.Node, name string) node.Node { +func child(t *testing.T, ds ipld.DAGService, a ipld.Node, name string) ipld.Node { return mdag.NodeWithData([]byte(string(a.(*mdag.ProtoNode).Data()) + "/" + name)) } diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 5dfd37fb7..648e9b2c3 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -8,7 +8,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) const ( @@ -38,7 +38,7 @@ func (c *Change) String() string { } // ApplyChange applies the requested changes to the given node in the given dag. -func ApplyChange(ctx context.Context, ds node.DAGService, nd *dag.ProtoNode, cs []*Change) (*dag.ProtoNode, error) { +func ApplyChange(ctx context.Context, ds ipld.DAGService, nd *dag.ProtoNode, cs []*Change) (*dag.ProtoNode, error) { e := NewDagEditor(nd, ds) for _, c := range cs { switch c.Type { @@ -90,7 +90,7 @@ func ApplyChange(ctx context.Context, ds node.DAGService, nd *dag.ProtoNode, cs } // Diff returns a set of changes that transform node 'a' into node 'b' -func Diff(ctx context.Context, ds node.DAGService, a, b node.Node) ([]*Change, error) { +func Diff(ctx context.Context, ds ipld.DAGService, a, b ipld.Node) ([]*Change, error) { if len(a.Links()) == 0 && len(b.Links()) == 0 { return []*Change{ &Change{ diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go index 1dc8e4ecd..d815102e7 100644 --- a/ipld/merkledag/utils/diffenum.go +++ b/ipld/merkledag/utils/diffenum.go @@ -7,13 +7,13 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // DiffEnumerate fetches every object in the graph pointed to by 'to' that is // not in 'from'. This can be used to more efficiently fetch a graph if you can // guarantee you already have the entirety of 'from' -func DiffEnumerate(ctx context.Context, dserv node.NodeGetter, from, to *cid.Cid) error { +func DiffEnumerate(ctx context.Context, dserv ipld.NodeGetter, from, to *cid.Cid) error { fnd, err := dserv.Get(ctx, from) if err != nil { return fmt.Errorf("get %s: %s", from, err) @@ -64,9 +64,9 @@ type diffpair struct { // getLinkDiff returns a changeset between nodes 'a' and 'b'. Currently does // not log deletions as our usecase doesnt call for this. -func getLinkDiff(a, b node.Node) []diffpair { - ina := make(map[string]*node.Link) - inb := make(map[string]*node.Link) +func getLinkDiff(a, b ipld.Node) []diffpair { + ina := make(map[string]*ipld.Link) + inb := make(map[string]*ipld.Link) var aonly []*cid.Cid for _, l := range b.Links() { inb[l.Cid.KeyString()] = l diff --git a/ipld/merkledag/utils/diffenum_test.go b/ipld/merkledag/utils/diffenum_test.go index 5fd463aec..bc9728fe7 100644 --- a/ipld/merkledag/utils/diffenum_test.go +++ b/ipld/merkledag/utils/diffenum_test.go @@ -9,10 +9,10 @@ import ( mdtest "github.com/ipfs/go-ipfs/merkledag/test" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) -func buildNode(name string, desc map[string]ndesc, out map[string]node.Node) node.Node { +func buildNode(name string, desc map[string]ndesc, out map[string]ipld.Node) ipld.Node { this := desc[name] nd := new(dag.ProtoNode) nd.SetData([]byte(name)) @@ -33,8 +33,8 @@ func buildNode(name string, desc map[string]ndesc, out map[string]node.Node) nod type ndesc map[string]string -func mkGraph(desc map[string]ndesc) map[string]node.Node { - out := make(map[string]node.Node) +func mkGraph(desc map[string]ndesc) map[string]ipld.Node { + out := make(map[string]ipld.Node) for name := range desc { if _, ok := out[name]; ok { continue @@ -154,11 +154,11 @@ func TestDiffEnumBasic(t *testing.T) { } type getLogger struct { - ds node.NodeGetter + ds ipld.NodeGetter log []*cid.Cid } -func (gl *getLogger) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { +func (gl *getLogger) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { nd, err := gl.ds.Get(ctx, c) if err != nil { return nil, err @@ -167,8 +167,8 @@ func (gl *getLogger) Get(ctx context.Context, c *cid.Cid) (node.Node, error) { return nd, nil } -func (gl *getLogger) GetMany(ctx context.Context, cids []*cid.Cid) <-chan *node.NodeOption { - outCh := make(chan *node.NodeOption, len(cids)) +func (gl *getLogger) GetMany(ctx context.Context, cids []*cid.Cid) <-chan *ipld.NodeOption { + outCh := make(chan *ipld.NodeOption, len(cids)) nds := gl.ds.GetMany(ctx, cids) for no := range nds { if no.Err == nil { @@ -211,7 +211,7 @@ func TestDiffEnumFail(t *testing.T) { } err := DiffEnumerate(ctx, lgds, nds["a1"].Cid(), nds["a2"].Cid()) - if err != node.ErrNotFound { + if err != ipld.ErrNotFound { t.Fatal("expected err not found") } diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index eb4413f15..ed8eec07c 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -12,7 +12,7 @@ import ( ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" syncds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) type Editor struct { @@ -20,15 +20,15 @@ type Editor struct { // tmp is a temporary in memory (for now) dagstore for all of the // intermediary nodes to be stored in - tmp node.DAGService + tmp ipld.DAGService // src is the dagstore with *all* of the data on it, it is used to pull // nodes from for modification (nil is a valid value) - src node.DAGService + src ipld.DAGService } // NewMemoryDagService returns a new, thread-safe in-memory DAGService. -func NewMemoryDagService() node.DAGService { +func NewMemoryDagService() ipld.DAGService { // build mem-datastore for editor's intermediary nodes bs := bstore.NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore())) bsrv := bserv.New(bs, offline.Exchange(bs)) @@ -39,7 +39,7 @@ func NewMemoryDagService() node.DAGService { // // * root is the node to be modified // * source is the dagstore to pull nodes from (optional) -func NewDagEditor(root *dag.ProtoNode, source node.DAGService) *Editor { +func NewDagEditor(root *dag.ProtoNode, source ipld.DAGService) *Editor { return &Editor{ root: root, tmp: NewMemoryDagService(), @@ -53,11 +53,11 @@ func (e *Editor) GetNode() *dag.ProtoNode { } // GetDagService returns the DAGService used by this editor. -func (e *Editor) GetDagService() node.DAGService { +func (e *Editor) GetDagService() ipld.DAGService { return e.tmp } -func addLink(ctx context.Context, ds node.DAGService, root *dag.ProtoNode, childname string, childnd node.Node) (*dag.ProtoNode, error) { +func addLink(ctx context.Context, ds ipld.DAGService, root *dag.ProtoNode, childname string, childnd ipld.Node) (*dag.ProtoNode, error) { if childname == "" { return nil, errors.New("cannot create link with no name") } @@ -83,7 +83,7 @@ func addLink(ctx context.Context, ds node.DAGService, root *dag.ProtoNode, child return root, nil } -func (e *Editor) InsertNodeAtPath(ctx context.Context, pth string, toinsert node.Node, create func() *dag.ProtoNode) error { +func (e *Editor) InsertNodeAtPath(ctx context.Context, pth string, toinsert ipld.Node, create func() *dag.ProtoNode) error { splpath := path.SplitList(pth) nd, err := e.insertNodeAtPath(ctx, e.root, splpath, toinsert, create) if err != nil { @@ -93,7 +93,7 @@ func (e *Editor) InsertNodeAtPath(ctx context.Context, pth string, toinsert node return nil } -func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path []string, toinsert node.Node, create func() *dag.ProtoNode) (*dag.ProtoNode, error) { +func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path []string, toinsert ipld.Node, create func() *dag.ProtoNode) (*dag.ProtoNode, error) { if len(path) == 1 { return addLink(ctx, e.tmp, root, path[0], toinsert) } @@ -104,7 +104,7 @@ func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path if err == dag.ErrLinkNotFound && create != nil { nd = create() err = nil // no longer an error case - } else if err == node.ErrNotFound { + } else if err == ipld.ErrNotFound { // try finding it in our source dagstore nd, err = root.GetLinkedProtoNode(ctx, e.src, path[0]) } @@ -165,7 +165,7 @@ func (e *Editor) rmLink(ctx context.Context, root *dag.ProtoNode, path []string) // search for node in both tmp dagstore and source dagstore nd, err := root.GetLinkedProtoNode(ctx, e.tmp, path[0]) - if err == node.ErrNotFound { + if err == ipld.ErrNotFound { nd, err = root.GetLinkedProtoNode(ctx, e.src, path[0]) } @@ -196,13 +196,13 @@ func (e *Editor) rmLink(ctx context.Context, root *dag.ProtoNode, path []string) // Finalize writes the new DAG to the given DAGService and returns the modified // root node. -func (e *Editor) Finalize(ctx context.Context, ds node.DAGService) (*dag.ProtoNode, error) { +func (e *Editor) Finalize(ctx context.Context, ds ipld.DAGService) (*dag.ProtoNode, error) { nd := e.GetNode() err := copyDag(ctx, nd, e.tmp, ds) return nd, err } -func copyDag(ctx context.Context, nd node.Node, from, to node.DAGService) error { +func copyDag(ctx context.Context, nd ipld.Node, from, to ipld.DAGService) error { // TODO(#4609): make this batch. err := to.Add(ctx, nd) if err != nil { @@ -212,7 +212,7 @@ func copyDag(ctx context.Context, nd node.Node, from, to node.DAGService) error for _, lnk := range nd.Links() { child, err := lnk.GetNode(ctx, from) if err != nil { - if err == node.ErrNotFound { + if err == ipld.ErrNotFound { // not found means we didnt modify it, and it should // already be in the target datastore continue diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 345bd2928..eb440c39c 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -9,7 +9,7 @@ import ( path "github.com/ipfs/go-ipfs/path" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) func TestAddLink(t *testing.T) { @@ -41,7 +41,7 @@ func TestAddLink(t *testing.T) { } } -func assertNodeAtPath(t *testing.T, ds node.DAGService, root *dag.ProtoNode, pth string, exp *cid.Cid) { +func assertNodeAtPath(t *testing.T, ds ipld.DAGService, root *dag.ProtoNode, pth string, exp *cid.Cid) { parts := path.SplitList(pth) cur := root for _, e := range parts { From fbc6fdf742a94038e3b0b3538e20eeb9aaee75cb Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 29 Jan 2018 11:55:34 -0800 Subject: [PATCH 2138/3817] rename import of go-ipld-format from node/format to ipld License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@93f532b6a06ea040a44a66aed8322f830546e34c --- unixfs/archive/archive.go | 4 +-- unixfs/archive/tar/writer.go | 10 ++++---- unixfs/hamt/hamt.go | 44 ++++++++++++++++----------------- unixfs/hamt/hamt_stress_test.go | 4 +-- unixfs/hamt/hamt_test.go | 10 ++++---- unixfs/io/dagreader.go | 4 +-- unixfs/io/dirbuilder.go | 18 +++++++------- unixfs/io/pbdagreader.go | 14 +++++------ unixfs/io/resolve.go | 4 +-- unixfs/mod/dagmodifier.go | 20 +++++++-------- unixfs/test/utils.go | 12 ++++----- 11 files changed, 72 insertions(+), 72 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index 2f6dc7183..72b3e2dac 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -10,7 +10,7 @@ import ( tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" uio "github.com/ipfs/go-ipfs/unixfs/io" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. @@ -30,7 +30,7 @@ func (i *identityWriteCloser) Close() error { } // DagArchive is equivalent to `ipfs getdag $hash | maybe_tar | maybe_gzip` -func DagArchive(ctx context.Context, nd node.Node, name string, dag node.DAGService, archive bool, compression int) (io.Reader, error) { +func DagArchive(ctx context.Context, nd ipld.Node, name string, dag ipld.DAGService, archive bool, compression int) (io.Reader, error) { _, filename := path.Split(name) diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 9cd696660..17614e10e 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -14,21 +14,21 @@ import ( upb "github.com/ipfs/go-ipfs/unixfs/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // Writer is a utility structure that helps to write // unixfs merkledag nodes as a tar archive format. // It wraps any io.Writer. type Writer struct { - Dag node.DAGService + Dag ipld.DAGService TarW *tar.Writer ctx context.Context } // NewWriter wraps given io.Writer. -func NewWriter(ctx context.Context, dag node.DAGService, archive bool, compression int, w io.Writer) (*Writer, error) { +func NewWriter(ctx context.Context, dag ipld.DAGService, archive bool, compression int, w io.Writer) (*Writer, error) { return &Writer{ Dag: dag, TarW: tar.NewWriter(w), @@ -41,7 +41,7 @@ func (w *Writer) writeDir(nd *mdag.ProtoNode, fpath string) error { return err } - for i, ng := range node.GetDAG(w.ctx, w.Dag, nd) { + for i, ng := range ipld.GetDAG(w.ctx, w.Dag, nd) { child, err := ng.Get(w.ctx) if err != nil { return err @@ -69,7 +69,7 @@ func (w *Writer) writeFile(nd *mdag.ProtoNode, pb *upb.Data, fpath string) error return nil } -func (w *Writer) WriteNode(nd node.Node, fpath string) error { +func (w *Writer) WriteNode(nd ipld.Node, fpath string) error { switch nd := nd.(type) { case *mdag.ProtoNode: pb := new(upb.Data) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index e0b063e4f..80d97d8ec 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -33,7 +33,7 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" "gx/ipfs/QmfJHywXQu98UeZtGJBQrPAR6AtmDjjbe3qjTo9piXHPnx/murmur3" ) @@ -57,17 +57,17 @@ type HamtShard struct { prefixPadStr string maxpadlen int - dserv node.DAGService + dserv ipld.DAGService } // child can either be another shard, or a leaf node value type child interface { - Link() (*node.Link, error) + Link() (*ipld.Link, error) Label() string } // NewHamtShard creates a new, empty HAMT shard with the given size. -func NewHamtShard(dserv node.DAGService, size int) (*HamtShard, error) { +func NewHamtShard(dserv ipld.DAGService, size int) (*HamtShard, error) { ds, err := makeHamtShard(dserv, size) if err != nil { return nil, err @@ -79,7 +79,7 @@ func NewHamtShard(dserv node.DAGService, size int) (*HamtShard, error) { return ds, nil } -func makeHamtShard(ds node.DAGService, size int) (*HamtShard, error) { +func makeHamtShard(ds ipld.DAGService, size int) (*HamtShard, error) { lg2s := int(math.Log2(float64(size))) if 1< Date: Mon, 29 Jan 2018 11:55:34 -0800 Subject: [PATCH 2139/3817] rename import of go-ipld-format from node/format to ipld License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-path@09472fc7fe0f5b3d20f3a02c8aa21286e1c01693 --- path/resolver.go | 22 +++++++++++----------- path/resolver_test.go | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index 68865283a..2609454a4 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -11,7 +11,7 @@ import ( logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) var log = logging.Logger("path") @@ -35,13 +35,13 @@ func (e ErrNoLink) Error() string { // TODO: now that this is more modular, try to unify this code with the // the resolvers in namesys type Resolver struct { - DAG node.DAGService + DAG ipld.DAGService - ResolveOnce func(ctx context.Context, ds node.DAGService, nd node.Node, names []string) (*node.Link, []string, error) + ResolveOnce func(ctx context.Context, ds ipld.DAGService, nd ipld.Node, names []string) (*ipld.Link, []string, error) } // NewBasicResolver constructs a new basic resolver. -func NewBasicResolver(ds node.DAGService) *Resolver { +func NewBasicResolver(ds ipld.DAGService) *Resolver { return &Resolver{ DAG: ds, ResolveOnce: ResolveSingle, @@ -74,7 +74,7 @@ func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) { return c, parts[1:], nil } -func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath Path) (node.Node, []string, error) { +func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath Path) (ipld.Node, []string, error) { c, p, err := SplitAbsPath(fpath) if err != nil { return nil, nil, err @@ -92,7 +92,7 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath Path) (node.Node } switch val := val.(type) { - case *node.Link: + case *ipld.Link: next, err := val.GetNode(ctx, r.DAG) if err != nil { return nil, nil, err @@ -109,7 +109,7 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath Path) (node.Node // ResolvePath fetches the node for given path. It returns the last item // returned by ResolvePathComponents. -func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (node.Node, error) { +func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (ipld.Node, error) { // validate path if err := fpath.IsValid(); err != nil { return nil, err @@ -124,14 +124,14 @@ func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (node.Node, erro // ResolveSingle simply resolves one hop of a path through a graph with no // extra context (does not opaquely resolve through sharded nodes) -func ResolveSingle(ctx context.Context, ds node.DAGService, nd node.Node, names []string) (*node.Link, []string, error) { +func ResolveSingle(ctx context.Context, ds ipld.DAGService, nd ipld.Node, names []string) (*ipld.Link, []string, error) { return nd.ResolveLink(names) } // ResolvePathComponents fetches the nodes for each segment of the given path. // It uses the first path component as a hash (key) of the first node, then // resolves all other components walking the links, with ResolveLinks. -func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]node.Node, error) { +func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]ipld.Node, error) { evt := log.EventBegin(ctx, "resolvePathComponents", logging.LoggableMap{"fpath": fpath}) defer evt.Done() @@ -158,11 +158,11 @@ func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]nod // // ResolveLinks(nd, []string{"foo", "bar", "baz"}) // would retrieve "baz" in ("bar" in ("foo" in nd.Links).Links).Links -func (s *Resolver) ResolveLinks(ctx context.Context, ndd node.Node, names []string) ([]node.Node, error) { +func (s *Resolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names []string) ([]ipld.Node, error) { evt := log.EventBegin(ctx, "resolveLinks", logging.LoggableMap{"names": names}) defer evt.Done() - result := make([]node.Node, 0, len(names)+1) + result := make([]ipld.Node, 0, len(names)+1) result = append(result, ndd) nd := ndd // dup arg workaround diff --git a/path/resolver_test.go b/path/resolver_test.go index 5f5a9ee50..d741bbcf1 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -10,7 +10,7 @@ import ( path "github.com/ipfs/go-ipfs/path" util "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) func randNode() *merkledag.ProtoNode { @@ -38,7 +38,7 @@ func TestRecurivePathResolution(t *testing.T) { t.Fatal(err) } - for _, n := range []node.Node{a, b, c} { + for _, n := range []ipld.Node{a, b, c} { err = dagService.Add(ctx, n) if err != nil { t.Fatal(err) From dcec746b5fb149407e0e2bec33775e5c75a2b050 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 29 Jan 2018 15:05:57 -0800 Subject: [PATCH 2140/3817] update comment to note that we're punting on this License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@083827c9512ab1e630f1fe39da07d2880d84dc09 --- blockservice/blockservice.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 3969dad69..0c3261dac 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -139,7 +139,7 @@ func (s *blockService) AddBlock(o blocks.Block) error { } if err := s.exchange.HasBlock(o); err != nil { - // TODO(stebalien): really an error? + // TODO(#4623): really an error? return errors.New("blockservice is closed") } @@ -170,7 +170,7 @@ func (s *blockService) AddBlocks(bs []blocks.Block) error { for _, o := range toput { if err := s.exchange.HasBlock(o); err != nil { - // TODO(stebalien): Should this really *return*? + // TODO(#4623): Should this really *return*? return fmt.Errorf("blockservice is closed (%s)", err) } } From 3a525b897573cdf1e8d493d004e6b9623f07527b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 5 Jan 2018 01:50:14 +0100 Subject: [PATCH 2141/3817] coreapi: draft block API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@3cd2995b5bd80584d23ba16037045558a17d04b9 --- coreiface/interface.go | 18 ++++++++++++++++++ coreiface/options/block.go | 14 ++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 coreiface/options/block.go diff --git a/coreiface/interface.go b/coreiface/interface.go index ddcdc8db6..147a85412 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -53,6 +53,11 @@ type Key interface { Path() Path } +type BlockStat interface { + Size() int + Path() Path +} + // CoreAPI defines an unified interface to IPFS for Go programs. type CoreAPI interface { // Unixfs returns an implementation of Unixfs API. @@ -87,6 +92,19 @@ type UnixfsAPI interface { Ls(context.Context, Path) ([]*Link, error) } +type BlockAPI interface { + Put(context.Context, io.Reader) (Path, error) + WithCodec(codec uint64) options.BlockPutOption + WithHash(mhType uint64, mhLen int) options.BlockPutOption + + Get(context.Context) (io.Reader, error) + + Rm(context.Context) error + WithForce(force bool) options.BlockRmOption + + Stat(context.Context) (BlockStat, error) +} + // DagAPI specifies the interface to IPLD type DagAPI interface { // Put inserts data using specified format and input encoding. diff --git a/coreiface/options/block.go b/coreiface/options/block.go new file mode 100644 index 000000000..e2473e3f7 --- /dev/null +++ b/coreiface/options/block.go @@ -0,0 +1,14 @@ +package options + +type BlockPutSettings struct { + Codec uint64 + MhType uint64 + MhLength int +} + +type BlockRmSettings struct { + Force bool +} + +type BlockPutOption func(*BlockPutSettings) error +type BlockRmOption func(*BlockRmSettings) error From bfcc5b4d08e6efbcfb31f0551db2630613b94b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 6 Jan 2018 16:57:41 +0100 Subject: [PATCH 2142/3817] coreapi: implement block API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@a7509ebfcac3b153716fc1d2cf4bf021d8ece1b5 --- coreiface/interface.go | 12 ++++---- coreiface/options/block.go | 61 +++++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 147a85412..bbe544344 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -62,6 +62,8 @@ type BlockStat interface { type CoreAPI interface { // Unixfs returns an implementation of Unixfs API. Unixfs() UnixfsAPI + // Block returns an implementation of Block API. + Block() BlockAPI // Dag returns an implementation of Dag API. Dag() DagAPI // Name returns an implementation of Name API. @@ -93,16 +95,16 @@ type UnixfsAPI interface { } type BlockAPI interface { - Put(context.Context, io.Reader) (Path, error) - WithCodec(codec uint64) options.BlockPutOption + Put(context.Context, io.Reader, ...options.BlockPutOption) (Path, error) + WithFormat(codec string) options.BlockPutOption WithHash(mhType uint64, mhLen int) options.BlockPutOption - Get(context.Context) (io.Reader, error) + Get(context.Context, Path) (io.Reader, error) - Rm(context.Context) error + Rm(context.Context, Path, ...options.BlockRmOption) error WithForce(force bool) options.BlockRmOption - Stat(context.Context) (BlockStat, error) + Stat(context.Context, Path) (BlockStat, error) } // DagAPI specifies the interface to IPLD diff --git a/coreiface/options/block.go b/coreiface/options/block.go index e2473e3f7..7e6ad3230 100644 --- a/coreiface/options/block.go +++ b/coreiface/options/block.go @@ -1,7 +1,12 @@ package options +import ( + //cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" + "gx/ipfs/QmYeKnKpubCMRiq3PGZcTREErthbb5Q9cXsCoSkD9bjEBd/go-multihash" +) + type BlockPutSettings struct { - Codec uint64 + Codec string MhType uint64 MhLength int } @@ -12,3 +17,57 @@ type BlockRmSettings struct { type BlockPutOption func(*BlockPutSettings) error type BlockRmOption func(*BlockRmSettings) error + +func BlockPutOptions(opts ...BlockPutOption) (*BlockPutSettings, error) { + options := &BlockPutSettings{ + Codec: "v0", + MhType: multihash.SHA2_256, + MhLength: -1, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +func BlockRmOptions(opts ...BlockRmOption) (*BlockRmSettings, error) { + options := &BlockRmSettings{ + Force: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type BlockOptions struct{} + +func (api *BlockOptions) WithFormat(codec string) BlockPutOption { + return func(settings *BlockPutSettings) error { + settings.Codec = codec + return nil + } +} + +func (api *BlockOptions) WithHash(mhType uint64, mhLen int) BlockPutOption { + return func(settings *BlockPutSettings) error { + settings.MhType = mhType + settings.MhLength = mhLen + return nil + } +} + +func (api *BlockOptions) WithForce(force bool) BlockRmOption { + return func(settings *BlockRmSettings) error { + settings.Force = force + return nil + } +} From db1f6da007f8c3d45668459b81d4b99559b0cea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 6 Jan 2018 17:13:33 +0100 Subject: [PATCH 2143/3817] corapi: block docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@1ffde91c6cc373e97ed28eb80ad3fb9ddf5103e1 --- coreiface/interface.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/coreiface/interface.go b/coreiface/interface.go index bbe544344..2402ecf81 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -94,16 +94,35 @@ type UnixfsAPI interface { Ls(context.Context, Path) ([]*Link, error) } +// BlockAPI specifies the interface to the block layer type BlockAPI interface { + // Put imports raw block data, hashing it using specified settings. Put(context.Context, io.Reader, ...options.BlockPutOption) (Path, error) + + // WithFormat is an option for Put which specifies the multicodec to use to + // serialize the object. Default is "v0" WithFormat(codec string) options.BlockPutOption + + // WithHash is an option for 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 WithHash(mhType uint64, mhLen int) options.BlockPutOption + // Get attempts to resolve the path and return a reader for data in the block Get(context.Context, Path) (io.Reader, error) + // Rm removes the block specified by the path from local blockstore. + // By default an error will be returned if the block can't be found locally. + // + // NOTE: If the specified block is pinned it won't be removed and no error + // will be returned Rm(context.Context, Path, ...options.BlockRmOption) error + + // WithForce is an option for Rm which, when set to true, will ignore + // non-existing blocks WithForce(force bool) options.BlockRmOption + // Stat returns information on Stat(context.Context, Path) (BlockStat, error) } From 553000ad23cf89d17e0e456abd366a03f6371bc7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 31 Jan 2018 18:54:57 -0800 Subject: [PATCH 2144/3817] gx: update go-log License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-chunker@4af2265b2268ad5ded9bb4a673155960c12e86c4 --- chunker/splitting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index ddd71e969..d2eaf6fb4 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -4,7 +4,7 @@ package chunk import ( "io" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" mpool "gx/ipfs/QmWBug6eBS7AxRdCDVuSY5CnSit7cS2XnPFYJWqWDumhCG/go-msgio/mpool" ) From bc3c9d99fa267c2c7cc04fba3909cee9a7ed7198 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 31 Jan 2018 18:54:57 -0800 Subject: [PATCH 2145/3817] gx: update go-log License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-path@1d86542e34fb1381982c6704a72f75daa8b1b2ca --- path/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/resolver.go b/path/resolver.go index 2609454a4..edba489b5 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -9,7 +9,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) From 7d3ba7f749552a8ea596dc60902e568df410059a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 31 Jan 2018 18:54:57 -0800 Subject: [PATCH 2146/3817] gx: update go-log License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-routing@62ccb5d08e2107c9047d1edf5ad2a46cb67baca1 --- routing/mock/centralized_client.go | 12 ++++++------ routing/mock/centralized_server.go | 6 +++--- routing/mock/centralized_test.go | 4 ++-- routing/mock/interface.go | 6 +++--- routing/none/none_client.go | 8 ++++---- routing/offline/offline.go | 10 +++++----- routing/offline/offline_test.go | 2 +- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 683a2a884..bc90ca6f7 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -6,18 +6,18 @@ import ( "time" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" + "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" + routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" + dhtpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" + pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" - dhtpb "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record/pb" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 6a86a6c6b..b4263d1d2 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -6,13 +6,13 @@ import ( "sync" "time" - "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" + "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" + pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 0fb7ce90f..00ca6aec0 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -6,11 +6,11 @@ import ( "time" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" + "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 31de6b0cc..6306ff060 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -8,11 +8,11 @@ import ( "context" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" + "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" - peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" + routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ) // Server provides mockrouting Clients diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 7d2939593..c38443362 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,11 +6,11 @@ import ( repo "github.com/ipfs/go-ipfs/repo" - routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" - peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" + p2phost "gx/ipfs/QmNmJZL7FQySMtE2BQuLMuZg2EB2CLEunJJUSVSc9YnnbV/go-libp2p-host" + routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" + pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" - p2phost "gx/ipfs/QmfCtHMCd9xFvehvHeVxtKVXJTMVTuHhyPRVHEXetn87vL/go-libp2p-host" ) type nilclient struct { diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 46cb34ada..443d4a9ec 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -8,14 +8,14 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" + routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" + record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" + pb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" + pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" + "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - record "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record" - pb "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record/pb" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" ) var ErrOffline = errors.New("routing system in offline mode") diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index 42a800162..66d851700 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -6,7 +6,7 @@ import ( "testing" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" + "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" ) func TestOfflineRouterStorage(t *testing.T) { From 32e0e118937bb25d6cfd289f2631d1a33234570a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 31 Jan 2018 18:54:57 -0800 Subject: [PATCH 2147/3817] gx: update go-log License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@d7fe68852d0dbe754c4e0d1f8c0b12b105b8d774 --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index f48ebacb6..20ed77b4e 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -11,7 +11,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 268d8e004..07cda7cd0 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -13,7 +13,7 @@ import ( dutils "github.com/ipfs/go-ipfs/merkledag/utils" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) From a2ef7efe90ffc266ff58ed533b55d8766db455c0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 31 Jan 2018 18:54:57 -0800 Subject: [PATCH 2148/3817] gx: update go-log License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@bafbd425a8b7b6acb079f0bced3d96957a27a760 --- filestore/filestore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 873ef12c3..534d3f26d 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -15,7 +15,7 @@ import ( "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) From 5b33bd74d022682b1f078c439edaa4d5b5c2ccc9 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 31 Jan 2018 18:54:57 -0800 Subject: [PATCH 2149/3817] gx: update go-log License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@b7ae541fde07cab7ab8f23772379a2ff7db32584 --- namesys/ipns_validate_test.go | 4 ++-- namesys/namesys.go | 8 ++++---- namesys/publisher.go | 8 ++++---- namesys/publisher_test.go | 4 ++-- namesys/pubsub.go | 14 +++++++------- namesys/pubsub_test.go | 16 ++++++++-------- namesys/republisher/repub.go | 8 ++++---- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 4 ++-- namesys/routing.go | 4 ++-- 10 files changed, 37 insertions(+), 37 deletions(-) diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index 82a6be2c0..430381cbe 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -7,10 +7,10 @@ import ( path "github.com/ipfs/go-ipfs/path" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - record "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record" ) func TestValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 673583d96..c65492360 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -9,14 +9,14 @@ import ( path "github.com/ipfs/go-ipfs/path" + p2phost "gx/ipfs/QmNmJZL7FQySMtE2BQuLMuZg2EB2CLEunJJUSVSc9YnnbV/go-libp2p-host" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" - floodsub "gx/ipfs/QmSjoxpBJV71bpSojnUY1K382Ly3Up55EspnDx6EKAmQX4/go-libp2p-floodsub" + floodsub "gx/ipfs/QmSFihvoND3eDaAYRCeLgLPt62yCPgMZs1NSZmKFEtJQQw/go-libp2p-floodsub" + routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" - peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - p2phost "gx/ipfs/QmfCtHMCd9xFvehvHeVxtKVXJTMVTuHhyPRVHEXetn87vL/go-libp2p-host" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/publisher.go b/namesys/publisher.go index 473e9042b..c7159036a 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -15,12 +15,12 @@ import ( u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" + routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" + record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" + dhtpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - record "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record" - dhtpb "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record/pb" ) // ErrExpiredRecord should be returned when an ipns record is diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index de6a5c694..86d4a0a41 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -12,10 +12,10 @@ import ( ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" - peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - testutil "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" ) type identity struct { diff --git a/namesys/pubsub.go b/namesys/pubsub.go index 1bb45ae90..229b9ff42 100644 --- a/namesys/pubsub.go +++ b/namesys/pubsub.go @@ -13,19 +13,19 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + p2phost "gx/ipfs/QmNmJZL7FQySMtE2BQuLMuZg2EB2CLEunJJUSVSc9YnnbV/go-libp2p-host" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" - floodsub "gx/ipfs/QmSjoxpBJV71bpSojnUY1K382Ly3Up55EspnDx6EKAmQX4/go-libp2p-floodsub" + floodsub "gx/ipfs/QmSFihvoND3eDaAYRCeLgLPt62yCPgMZs1NSZmKFEtJQQw/go-libp2p-floodsub" + routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" + record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" + dhtpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" + pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" - peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - record "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record" - dhtpb "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record/pb" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" - p2phost "gx/ipfs/QmfCtHMCd9xFvehvHeVxtKVXJTMVTuHhyPRVHEXetn87vL/go-libp2p-host" ) // PubsubPublisher is a publisher that distributes IPNS records through pubsub diff --git a/namesys/pubsub_test.go b/namesys/pubsub_test.go index 1fe590cd1..b8e6dbec3 100644 --- a/namesys/pubsub_test.go +++ b/namesys/pubsub_test.go @@ -9,16 +9,16 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" + p2phost "gx/ipfs/QmNmJZL7FQySMtE2BQuLMuZg2EB2CLEunJJUSVSc9YnnbV/go-libp2p-host" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" - floodsub "gx/ipfs/QmSjoxpBJV71bpSojnUY1K382Ly3Up55EspnDx6EKAmQX4/go-libp2p-floodsub" - netutil "gx/ipfs/QmWUugnJBbcuin8qdfiCYKAsNkG8NeDLhzoBqRaqXhAHd4/go-libp2p-netutil" - bhost "gx/ipfs/QmZ15dDSCo4DKn4o4GnqqLExKATBeeo3oNyQ5FBKtNjEQT/go-libp2p-blankhost" - peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" + bhost "gx/ipfs/QmQr1j6UvdhpponAaqSdswqRpdzsFwNop2N8kXLNw8afem/go-libp2p-blankhost" + floodsub "gx/ipfs/QmSFihvoND3eDaAYRCeLgLPt62yCPgMZs1NSZmKFEtJQQw/go-libp2p-floodsub" + routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" + testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" + pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" + netutil "gx/ipfs/QmYVR3C8DWPHdHxvLtNFYfjsXgaRAdh6hPMNH3KiwCgu4o/go-libp2p-netutil" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" - testutil "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" - p2phost "gx/ipfs/QmfCtHMCd9xFvehvHeVxtKVXJTMVTuHhyPRVHEXetn87vL/go-libp2p-host" ) func newNetHost(ctx context.Context, t *testing.T) p2phost.Host { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index dc158fea6..345ba4abc 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -12,14 +12,14 @@ import ( dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" + logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" + recpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ic "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - recpb "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record/pb" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 033957200..580b708de 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -12,9 +12,9 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmPd5qhppUqewTQMfStvNNCFtcxiWGsnE6Vs3va6788gsX/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmNh1kGFFdsPu79KNSaL4NUKUPb4Eiz4KHdMtFY6664RDp/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - pstore "gx/ipfs/QmeZVQzUrXqaszo24DAoHfGzcmCptN9JyngLkGAiEfk2x7/go-libp2p-peerstore" + pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 2e0159860..17dab14af 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,11 +8,11 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" - testutil "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil" + testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - peer "gx/ipfs/Qma7H6RW8wRrfZpNSXwxYGcd1E149s42FpWNpDNieSVrnU/go-libp2p-peer" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index 7a1abbecf..4ec68e7b6 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -10,8 +10,8 @@ import ( path "github.com/ipfs/go-ipfs/path" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - routing "gx/ipfs/QmRijoA6zGS98ELTDbGsLWPZbVotYsGbjp3RbXcKCYBeon/go-libp2p-routing" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" + routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" From 78ef54969aa6c13d2ae1d997639359fc54b31603 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 31 Jan 2018 18:54:57 -0800 Subject: [PATCH 2150/3817] gx: update go-log License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@33437438f1e2f4c7fd8bb1b84db86f8d17a71afb --- mfs/repub_test.go | 2 +- mfs/system.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 499650d13..15400c9a3 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" + ci "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil/ci" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ci "gx/ipfs/QmfB65MYJqaKzBiMvW47fquCRhmEeXW6AhrJSGM7TeY5eG/go-testutil/ci" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index 2150dc621..d93af7cfd 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -19,7 +19,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) From a4836bdfc93b10f71712a2ce179ea3b8f589ea67 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 31 Jan 2018 18:54:57 -0800 Subject: [PATCH 2151/3817] gx: update go-log License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@aa6283a09e9a0b0bdbbedd206f2b18dbaaaa82ec --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 0c3261dac..fb8144d88 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -13,7 +13,7 @@ import ( exchange "github.com/ipfs/go-ipfs/exchange" bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) From d49d1ec38db10a67f70659243dc2e4cbc7134ec6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 31 Jan 2018 18:54:57 -0800 Subject: [PATCH 2152/3817] gx: update go-log License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-blockstore@6ff6d6a46c37934c7cd739466125cb9afecdac28 --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 5a37560c5..7e5e8cbdc 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -14,7 +14,7 @@ import ( ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dsns "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/namespace" dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" - logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" + logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) From 6bddfa5f233dce4a2a775255d2db6715dff00b65 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 1 Feb 2018 23:39:43 +0100 Subject: [PATCH 2153/3817] Docs: golint-ify "importers" module This fixes all golint warnings in the importers module, adding documentation and module descriptions. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@6f545b7e2635fdb69dd33320a17d0f7ae7b660c0 --- unixfs/format.go | 2 ++ unixfs/mod/dagmodifier.go | 2 +- unixfs/test/utils.go | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/unixfs/format.go b/unixfs/format.go index 4157ec0e5..26d4b0cc3 100644 --- a/unixfs/format.go +++ b/unixfs/format.go @@ -174,6 +174,8 @@ func (n *FSNode) GetBytes() ([]byte, error) { return proto.Marshal(pbn) } +// FileSize returns the total size of this tree. That is, the size of +// the data in this node plus the size of all its children. func (n *FSNode) FileSize() uint64 { return uint64(len(n.Data)) + n.subtotal } diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index ff2d51f25..8bf9382dd 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -362,7 +362,7 @@ func (dm *DagModifier) appendData(nd ipld.Node, spl chunk.Splitter) (ipld.Node, Prefix: &dm.Prefix, RawLeaves: dm.RawLeaves, } - return trickle.TrickleAppend(dm.ctx, nd, dbp.New(spl)) + return trickle.Append(dm.ctx, nd, dbp.New(spl)) default: return nil, ErrNotUnixfs } diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index a50c4ecbe..5e1977ddb 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -63,7 +63,7 @@ func GetNode(t testing.TB, dserv ipld.DAGService, data []byte, opts NodeOpts) ip RawLeaves: opts.RawLeavesUsed, } - node, err := trickle.TrickleLayout(dbp.New(SizeSplitterGen(500)(in))) + node, err := trickle.Layout(dbp.New(SizeSplitterGen(500)(in))) if err != nil { t.Fatal(err) } From 1adc3cd0933bb9c789c2794ea94b5589f4cb9743 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 1 Feb 2018 23:39:43 +0100 Subject: [PATCH 2154/3817] Docs: golint-ify "importers" module This fixes all golint warnings in the importers module, adding documentation and module descriptions. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-chunker@674ac127ee62d49a3283bc634c71dfdbf0300161 --- chunker/parse.go | 3 +++ chunker/rabin.go | 9 +++++++++ chunker/rabin_test.go | 2 +- chunker/splitting.go | 17 ++++++++++++++++- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/chunker/parse.go b/chunker/parse.go index f4cc56290..7d511c217 100644 --- a/chunker/parse.go +++ b/chunker/parse.go @@ -8,6 +8,9 @@ import ( "strings" ) +// FromString returns a Splitter depending on the given string: +// it supports "default" (""), "size-{size}", "rabin", "rabin-{blocksize}" and +// "rabin-{min}-{avg}-{max}". func FromString(r io.Reader, chunker string) (Splitter, error) { switch { case chunker == "" || chunker == "default": diff --git a/chunker/rabin.go b/chunker/rabin.go index d2d71460d..c3d1ebdba 100644 --- a/chunker/rabin.go +++ b/chunker/rabin.go @@ -7,13 +7,18 @@ import ( "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/chunker" ) +// IpfsRabinPoly is the irreducible polynomial of degree 53 used by for Rabin. var IpfsRabinPoly = chunker.Pol(17437180132763653) +// Rabin implements the Splitter interface and splits content with Rabin +// fingerprints. type Rabin struct { r *chunker.Chunker reader io.Reader } +// NewRabin creates a new Rabin splitter with the given +// average block size. func NewRabin(r io.Reader, avgBlkSize uint64) *Rabin { min := avgBlkSize / 3 max := avgBlkSize + (avgBlkSize / 2) @@ -21,6 +26,8 @@ func NewRabin(r io.Reader, avgBlkSize uint64) *Rabin { return NewRabinMinMax(r, min, avgBlkSize, max) } +// NewRabinMinMax returns a new Rabin splitter which uses +// the given min, average and max block sizes. func NewRabinMinMax(r io.Reader, min, avg, max uint64) *Rabin { h := fnv.New32a() ch := chunker.New(r, IpfsRabinPoly, h, avg, min, max) @@ -31,6 +38,7 @@ func NewRabinMinMax(r io.Reader, min, avg, max uint64) *Rabin { } } +// NextBytes reads the next bytes from the reader and returns a slice. func (r *Rabin) NextBytes() ([]byte, error) { ch, err := r.r.Next() if err != nil { @@ -40,6 +48,7 @@ func (r *Rabin) NextBytes() ([]byte, error) { return ch.Data, nil } +// Reader returns the io.Reader associated to this Splitter. func (r *Rabin) Reader() io.Reader { return r.reader } diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 1d5702e38..2f68f01c4 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -68,7 +68,7 @@ func TestRabinChunkReuse(t *testing.T) { ch2 := chunkData(t, data) var extra int - for k, _ := range ch2 { + for k := range ch2 { _, ok := ch1[k] if !ok { extra++ diff --git a/chunker/splitting.go b/chunker/splitting.go index ddd71e969..9586df850 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -1,4 +1,7 @@ -// package chunk implements streaming block splitters +// Package chunk implements streaming block splitters. +// Splitters read data from a reader and provide byte slices (chunks) +// The size and contents of these slices depend on the splitting method +// used. package chunk import ( @@ -10,25 +13,34 @@ import ( var log = logging.Logger("chunk") +// DefaultBlockSize is the chunk size that splitters produce (or aim to). var DefaultBlockSize int64 = 1024 * 256 +// A Splitter reads bytes from a Reader and creates "chunks" (byte slices) +// that can be used to build DAG nodes. type Splitter interface { Reader() io.Reader NextBytes() ([]byte, error) } +// SplitterGen is a splitter generator, given a reader. type SplitterGen func(r io.Reader) Splitter +// DefaultSplitter returns a SizeSplitter with the DefaultBlockSize. func DefaultSplitter(r io.Reader) Splitter { return NewSizeSplitter(r, DefaultBlockSize) } +// SizeSplitterGen returns a SplitterGen function which will create +// a splitter with the given size when called. func SizeSplitterGen(size int64) SplitterGen { return func(r io.Reader) Splitter { return NewSizeSplitter(r, size) } } +// Chan returns a channel that receives each of the chunks produced +// by a splitter, along with another one for errors. func Chan(s Splitter) (<-chan []byte, <-chan error) { out := make(chan []byte) errs := make(chan error, 1) @@ -56,6 +68,7 @@ type sizeSplitterv2 struct { err error } +// NewSizeSplitter returns a new size-based Splitter with the given block size. func NewSizeSplitter(r io.Reader, size int64) Splitter { return &sizeSplitterv2{ r: r, @@ -63,6 +76,7 @@ func NewSizeSplitter(r io.Reader, size int64) Splitter { } } +// NextBytes produces a new chunk. func (ss *sizeSplitterv2) NextBytes() ([]byte, error) { if ss.err != nil { return nil, ss.err @@ -85,6 +99,7 @@ func (ss *sizeSplitterv2) NextBytes() ([]byte, error) { } } +// Reader returns the io.Reader associated to this Splitter. func (ss *sizeSplitterv2) Reader() io.Reader { return ss.r } From a93e66c305d7a52d7fdad9587dfc1b586d3e89ba Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Feb 2018 22:24:43 -0800 Subject: [PATCH 2155/3817] Use a bitswap session for 'Cat' License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@7c2d481e5bc3b385a8253effa7d50858273922f2 --- ipld/merkledag/errservice.go | 41 +++++++++++++++++++++ ipld/merkledag/merkledag.go | 4 +++ ipld/merkledag/readonly.go | 16 +++++++++ ipld/merkledag/readonly_test.go | 64 +++++++++++++++++++++++++++++++++ ipld/merkledag/rwservice.go | 41 +++++++++++++++++++++ ipld/merkledag/session.go | 18 ++++++++++ 6 files changed, 184 insertions(+) create mode 100644 ipld/merkledag/errservice.go create mode 100644 ipld/merkledag/readonly.go create mode 100644 ipld/merkledag/readonly_test.go create mode 100644 ipld/merkledag/rwservice.go create mode 100644 ipld/merkledag/session.go diff --git a/ipld/merkledag/errservice.go b/ipld/merkledag/errservice.go new file mode 100644 index 000000000..a8cd23737 --- /dev/null +++ b/ipld/merkledag/errservice.go @@ -0,0 +1,41 @@ +package merkledag + +import ( + "context" + + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" +) + +// ErrorService implements ipld.DAGService, returning 'Err' for every call. +type ErrorService struct { + Err error +} + +var _ ipld.DAGService = (*ErrorService)(nil) + +func (cs *ErrorService) Add(ctx context.Context, nd ipld.Node) error { + return cs.Err +} + +func (cs *ErrorService) AddMany(ctx context.Context, nds []ipld.Node) error { + return cs.Err +} + +func (cs *ErrorService) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { + return nil, cs.Err +} + +func (cs *ErrorService) GetMany(ctx context.Context, cids []*cid.Cid) <-chan *ipld.NodeOption { + ch := make(chan *ipld.NodeOption) + close(ch) + return ch +} + +func (cs *ErrorService) Remove(ctx context.Context, c *cid.Cid) error { + return cs.Err +} + +func (cs *ErrorService) RemoveMany(ctx context.Context, cids []*cid.Cid) error { + return cs.Err +} diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index fd66fb269..080abcb89 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -146,6 +146,10 @@ func (sg *sesGetter) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *ipld. return getNodesFromBG(ctx, sg.bs, keys) } +func (ds *dagService) Session(ctx context.Context) ipld.NodeGetter { + return &sesGetter{bserv.NewSession(ctx, ds.Blocks)} +} + // FetchGraph fetches all nodes that are children of the given node func FetchGraph(ctx context.Context, root *cid.Cid, serv ipld.DAGService) error { var ng ipld.NodeGetter = serv diff --git a/ipld/merkledag/readonly.go b/ipld/merkledag/readonly.go new file mode 100644 index 000000000..e23440617 --- /dev/null +++ b/ipld/merkledag/readonly.go @@ -0,0 +1,16 @@ +package merkledag + +import ( + "fmt" + + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" +) + +var ErrReadOnly = fmt.Errorf("cannot write to readonly DAGService") + +func NewReadOnlyDagService(ng ipld.NodeGetter) ipld.DAGService { + return &ComboService{ + Read: ng, + Write: &ErrorService{ErrReadOnly}, + } +} diff --git a/ipld/merkledag/readonly_test.go b/ipld/merkledag/readonly_test.go new file mode 100644 index 000000000..86ea86cda --- /dev/null +++ b/ipld/merkledag/readonly_test.go @@ -0,0 +1,64 @@ +package merkledag_test + +import ( + "context" + "testing" + + . "github.com/ipfs/go-ipfs/merkledag" + dstest "github.com/ipfs/go-ipfs/merkledag/test" + + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" +) + +func TestReadonlyProperties(t *testing.T) { + ds := dstest.Mock() + ro := NewReadOnlyDagService(ds) + + ctx := context.Background() + nds := []ipld.Node{ + NewRawNode([]byte("foo1")), + NewRawNode([]byte("foo2")), + NewRawNode([]byte("foo3")), + NewRawNode([]byte("foo4")), + } + cids := []*cid.Cid{ + nds[0].Cid(), + nds[1].Cid(), + nds[2].Cid(), + nds[3].Cid(), + } + + // add to the actual underlying datastore + if err := ds.Add(ctx, nds[2]); err != nil { + t.Fatal(err) + } + if err := ds.Add(ctx, nds[3]); err != nil { + t.Fatal(err) + } + + if err := ro.Add(ctx, nds[0]); err != ErrReadOnly { + t.Fatal("expected ErrReadOnly") + } + if err := ro.Add(ctx, nds[2]); err != ErrReadOnly { + t.Fatal("expected ErrReadOnly") + } + + if err := ro.AddMany(ctx, nds[0:1]); err != ErrReadOnly { + t.Fatal("expected ErrReadOnly") + } + + if err := ro.Remove(ctx, cids[3]); err != ErrReadOnly { + t.Fatal("expected ErrReadOnly") + } + if err := ro.RemoveMany(ctx, cids[1:2]); err != ErrReadOnly { + t.Fatal("expected ErrReadOnly") + } + + if _, err := ro.Get(ctx, cids[0]); err != ipld.ErrNotFound { + t.Fatal("expected ErrNotFound") + } + if _, err := ro.Get(ctx, cids[3]); err != nil { + t.Fatal(err) + } +} diff --git a/ipld/merkledag/rwservice.go b/ipld/merkledag/rwservice.go new file mode 100644 index 000000000..1252d248e --- /dev/null +++ b/ipld/merkledag/rwservice.go @@ -0,0 +1,41 @@ +package merkledag + +import ( + "context" + + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" +) + +// ComboService implements ipld.DAGService, using 'Read' for all fetch methods, +// and 'Write' for all methods that add new objects. +type ComboService struct { + Read ipld.NodeGetter + Write ipld.DAGService +} + +var _ ipld.DAGService = (*ComboService)(nil) + +func (cs *ComboService) Add(ctx context.Context, nd ipld.Node) error { + return cs.Write.Add(ctx, nd) +} + +func (cs *ComboService) AddMany(ctx context.Context, nds []ipld.Node) error { + return cs.Write.AddMany(ctx, nds) +} + +func (cs *ComboService) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { + return cs.Read.Get(ctx, c) +} + +func (cs *ComboService) GetMany(ctx context.Context, cids []*cid.Cid) <-chan *ipld.NodeOption { + return cs.Read.GetMany(ctx, cids) +} + +func (cs *ComboService) Remove(ctx context.Context, c *cid.Cid) error { + return cs.Write.Remove(ctx, c) +} + +func (cs *ComboService) RemoveMany(ctx context.Context, cids []*cid.Cid) error { + return cs.Write.RemoveMany(ctx, cids) +} diff --git a/ipld/merkledag/session.go b/ipld/merkledag/session.go new file mode 100644 index 000000000..50015dc3a --- /dev/null +++ b/ipld/merkledag/session.go @@ -0,0 +1,18 @@ +package merkledag + +import ( + "context" + + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" +) + +type SessionMaker interface { + Session(context.Context) ipld.NodeGetter +} + +func NewSession(ctx context.Context, g ipld.NodeGetter) ipld.NodeGetter { + if sm, ok := g.(SessionMaker); ok { + return sm.Session(ctx) + } + return g +} From e1ae80b2e143bcd35f82947064b5d7235f4615e3 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Feb 2018 22:24:43 -0800 Subject: [PATCH 2156/3817] Use a bitswap session for 'Cat' License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@53bab4f9652f76c0518a3373efe1e8e9b6b7a3c0 --- unixfs/io/dagreader.go | 2 +- unixfs/io/pbdagreader.go | 4 ++-- unixfs/io/resolve.go | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index e92223230..c6e9cf0d9 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -34,7 +34,7 @@ type ReadSeekCloser interface { // NewDagReader creates a new reader object that reads the data represented by // the given node, using the passed in DAGService for data retreival -func NewDagReader(ctx context.Context, n ipld.Node, serv ipld.DAGService) (DagReader, error) { +func NewDagReader(ctx context.Context, n ipld.Node, serv ipld.NodeGetter) (DagReader, error) { switch n := n.(type) { case *mdag.RawNode: return NewBufDagReader(n.RawData()), nil diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 8745798a3..0c6bee832 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -17,7 +17,7 @@ import ( // DagReader provides a way to easily read the data contained in a dag. type pbDagReader struct { - serv ipld.DAGService + serv ipld.NodeGetter // the node being read node *mdag.ProtoNode @@ -51,7 +51,7 @@ type pbDagReader struct { var _ DagReader = (*pbDagReader)(nil) // NewPBFileReader constructs a new PBFileReader. -func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv ipld.DAGService) *pbDagReader { +func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv ipld.NodeGetter) *pbDagReader { fctx, cancel := context.WithCancel(ctx) curLinks := getLinkCids(n) return &pbDagReader{ diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index a06e29ca3..26d360bb3 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -12,7 +12,7 @@ import ( // ResolveUnixfsOnce resolves a single hop of a path through a graph in a // unixfs context. This includes handling traversing sharded directories. -func ResolveUnixfsOnce(ctx context.Context, ds ipld.DAGService, nd ipld.Node, names []string) (*ipld.Link, []string, error) { +func ResolveUnixfsOnce(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, names []string) (*ipld.Link, []string, error) { switch nd := nd.(type) { case *dag.ProtoNode: upb, err := ft.FromBytes(nd.Data()) @@ -28,7 +28,8 @@ func ResolveUnixfsOnce(ctx context.Context, ds ipld.DAGService, nd ipld.Node, na switch upb.GetType() { case ft.THAMTShard: - s, err := hamt.NewHamtFromDag(ds, nd) + rods := dag.NewReadOnlyDagService(ds) + s, err := hamt.NewHamtFromDag(rods, nd) if err != nil { return nil, nil, err } From 7647a84e18d02f6816b32a299be69925de3ee159 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 1 Feb 2018 22:24:43 -0800 Subject: [PATCH 2157/3817] Use a bitswap session for 'Cat' License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@749e6f76d3d4e19a2536c8635141e75880b7d8b7 --- path/resolver.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/path/resolver.go b/path/resolver.go index 2609454a4..4106e8788 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -35,9 +35,9 @@ func (e ErrNoLink) Error() string { // TODO: now that this is more modular, try to unify this code with the // the resolvers in namesys type Resolver struct { - DAG ipld.DAGService + DAG ipld.NodeGetter - ResolveOnce func(ctx context.Context, ds ipld.DAGService, nd ipld.Node, names []string) (*ipld.Link, []string, error) + ResolveOnce func(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, names []string) (*ipld.Link, []string, error) } // NewBasicResolver constructs a new basic resolver. @@ -124,7 +124,7 @@ func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (ipld.Node, erro // ResolveSingle simply resolves one hop of a path through a graph with no // extra context (does not opaquely resolve through sharded nodes) -func ResolveSingle(ctx context.Context, ds ipld.DAGService, nd ipld.Node, names []string) (*ipld.Link, []string, error) { +func ResolveSingle(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, names []string) (*ipld.Link, []string, error) { return nd.ResolveLink(names) } From 72b62f5f64893169686ccb5569f41545c3ba8c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 31 Jan 2018 00:34:51 +0100 Subject: [PATCH 2158/3817] coreapi: update block after update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@bf7867a20c4aed75aead015b6c720b16645e9626 --- coreiface/interface.go | 4 ++++ coreiface/options/block.go | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 2402ecf81..95351e7d0 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -62,12 +62,16 @@ type BlockStat interface { type CoreAPI interface { // Unixfs returns an implementation of Unixfs API. Unixfs() UnixfsAPI + // Block returns an implementation of Block API. Block() BlockAPI + // Dag returns an implementation of Dag API. Dag() DagAPI + // Name returns an implementation of Name API. Name() NameAPI + // Key returns an implementation of Key API. Key() KeyAPI diff --git a/coreiface/options/block.go b/coreiface/options/block.go index 7e6ad3230..bbb14612f 100644 --- a/coreiface/options/block.go +++ b/coreiface/options/block.go @@ -1,8 +1,7 @@ package options import ( - //cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid" - "gx/ipfs/QmYeKnKpubCMRiq3PGZcTREErthbb5Q9cXsCoSkD9bjEBd/go-multihash" + "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" ) type BlockPutSettings struct { From c58c90356c44cde54b4eed474533305e54400d30 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 3 Feb 2018 14:45:11 -0800 Subject: [PATCH 2159/3817] comments License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@deacce766f995f9e8ee85eef0be61267e8443689 --- ipld/merkledag/merkledag.go | 1 + ipld/merkledag/readonly.go | 4 ++++ ipld/merkledag/session.go | 3 +++ 3 files changed, 8 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 080abcb89..1ee6ccfb6 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -146,6 +146,7 @@ func (sg *sesGetter) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *ipld. return getNodesFromBG(ctx, sg.bs, keys) } +// Session returns a NodeGetter using a new session for block fetches. func (ds *dagService) Session(ctx context.Context) ipld.NodeGetter { return &sesGetter{bserv.NewSession(ctx, ds.Blocks)} } diff --git a/ipld/merkledag/readonly.go b/ipld/merkledag/readonly.go index e23440617..1fd48eff9 100644 --- a/ipld/merkledag/readonly.go +++ b/ipld/merkledag/readonly.go @@ -6,8 +6,12 @@ import ( ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) +// ErrReadOnly is used when a read-only datastructure is written to. var ErrReadOnly = fmt.Errorf("cannot write to readonly DAGService") +// NewReadOnlyDagService takes a NodeGetter, and returns a full DAGService +// implementation that returns ErrReadOnly when its 'write' methods are +// invoked. func NewReadOnlyDagService(ng ipld.NodeGetter) ipld.DAGService { return &ComboService{ Read: ng, diff --git a/ipld/merkledag/session.go b/ipld/merkledag/session.go index 50015dc3a..fe0df24d0 100644 --- a/ipld/merkledag/session.go +++ b/ipld/merkledag/session.go @@ -6,10 +6,13 @@ import ( ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) +// SessionMaker is an object that can generate a new fetching session. type SessionMaker interface { Session(context.Context) ipld.NodeGetter } +// NewSession returns a session backed NodeGetter if the given NodeGetter +// implements SessionMaker. func NewSession(ctx context.Context, g ipld.NodeGetter) ipld.NodeGetter { if sm, ok := g.(SessionMaker); ok { return sm.Session(ctx) From 1820ac00cdfc8ead719a81ef5b08bf5744a482a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 31 Jan 2018 15:03:22 +0100 Subject: [PATCH 2160/3817] fix pin test on windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-pinner@1bef62d6bfd7190167ce86b465298433b490afde --- pinning/pinner/pin_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 3652ef8e7..c0137a0d3 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -16,10 +16,12 @@ import ( cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) +var rand = util.NewTimeSeededRand() + func randNode() (*mdag.ProtoNode, *cid.Cid) { nd := new(mdag.ProtoNode) nd.SetData(make([]byte, 32)) - util.NewTimeSeededRand().Read(nd.Data()) + rand.Read(nd.Data()) k := nd.Cid() return nd, k } From b6b112691c550f81c6cfb31b3cab4fcf6250d62a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 31 Jan 2018 15:47:07 +0100 Subject: [PATCH 2161/3817] "fix" routing/mock tests on windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-routing@3f0bd8ab769c2f15ca6e9066bcf51af263045a80 --- routing/mock/centralized_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 00ca6aec0..8d056a139 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -6,9 +6,9 @@ import ( "time" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) @@ -164,6 +164,8 @@ func TestValidAfter(t *testing.T) { } conf.ValueVisibility.Set(0) + time.Sleep(100 * time.Millisecond) + providersChan = rs.Client(pi).FindProvidersAsync(ctx, key, max) t.Log("providers", providers) for p := range providersChan { From 9d7df8e02a27a7b2c6e8192197bcfa8dba034c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 1 Feb 2018 13:20:14 +0100 Subject: [PATCH 2162/3817] make repo gc call CollectGarbage on datastore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-pinner@4f8b95f7f5ca4474977e60ba84c15e200a4c95f8 --- pinning/pinner/gc/gc.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 20ed77b4e..92c8cb52a 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -11,6 +11,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" + dstore "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" @@ -35,7 +36,7 @@ type Result struct { // The routine then iterates over every block in the blockstore and // deletes any block that is not found in the marked set. // -func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner, bestEffortRoots []*cid.Cid) <-chan Result { +func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn pin.Pinner, bestEffortRoots []*cid.Cid) <-chan Result { elock := log.EventBegin(ctx, "GC.lockWait") unlocker := bs.GCLock() @@ -107,6 +108,18 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, pn pin.Pinner, bestEffortRo if errors { output <- Result{Error: ErrCannotDeleteSomeBlocks} } + + defer log.EventBegin(ctx, "GC.datastore").Done() + gds, ok := dstor.(dstore.GCDatastore) + if !ok { + return + } + + err = gds.CollectGarbage() + if err != nil { + output <- Result{Error: err} + return + } }() return output From 3c009fdf88416285baea4056a361ad1e1b764f33 Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Sun, 4 Feb 2018 04:28:55 +0100 Subject: [PATCH 2163/3817] blockservice: add BlockedFetched/Added/Removed events ... and remove a few log lines that don't seem terribly useful. License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/go-blockservice@3d6da969ea772aa4178da53e5023bd9f3e52c165 --- blockservice/blockservice.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 0c3261dac..18eae3ec5 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -138,6 +138,8 @@ func (s *blockService) AddBlock(o blocks.Block) error { return err } + log.Event(context.TODO(), "BlockService.BlockAdded", c) + if err := s.exchange.HasBlock(o); err != nil { // TODO(#4623): really an error? return errors.New("blockservice is closed") @@ -169,6 +171,7 @@ func (s *blockService) AddBlocks(bs []blocks.Block) error { } for _, o := range toput { + log.Event(context.TODO(), "BlockService.BlockAdded", o.Cid()) if err := s.exchange.HasBlock(o); err != nil { // TODO(#4623): Should this really *return*? return fmt.Errorf("blockservice is closed (%s)", err) @@ -207,6 +210,7 @@ func getBlock(ctx context.Context, c *cid.Cid, bs blockstore.Blockstore, f excha } return nil, err } + log.Event(ctx, "BlockService.BlockFetched", c) return blk, nil } @@ -236,7 +240,6 @@ func getBlocks(ctx context.Context, ks []*cid.Cid, bs blockstore.Blockstore, f e misses = append(misses, c) continue } - log.Debug("Blockservice: Got data in datastore") select { case out <- hit: case <-ctx.Done(): @@ -255,6 +258,7 @@ func getBlocks(ctx context.Context, ks []*cid.Cid, bs blockstore.Blockstore, f e } for b := range rblocks { + log.Event(ctx, "BlockService.BlockFetched", b.Cid()) select { case out <- b: case <-ctx.Done(): @@ -267,7 +271,11 @@ func getBlocks(ctx context.Context, ks []*cid.Cid, bs blockstore.Blockstore, f e // DeleteBlock deletes a block in the blockservice from the datastore func (s *blockService) DeleteBlock(c *cid.Cid) error { - return s.blockstore.DeleteBlock(c) + err := s.blockstore.DeleteBlock(c) + if err == nil { + log.Event(context.TODO(), "BlockService.BlockDeleted", c) + } + return err } func (s *blockService) Close() error { From 7b5b64eefb105adf64cc51224ea381a15239767a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 9 Jan 2018 01:10:03 +0100 Subject: [PATCH 2164/3817] coreapi: pin draft MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@b3293937f6e9b665c70038d40b304844fd015930 --- coreiface/interface.go | 44 ++++++++++++++++++++++++++++++ coreiface/options/pin.go | 58 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 coreiface/options/pin.go diff --git a/coreiface/interface.go b/coreiface/interface.go index 95351e7d0..40fa4131e 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -58,6 +58,15 @@ type BlockStat interface { Path() Path } +// Pin holds information about pinned resource +type Pin interface { + // Path to the pinned object + Path() Path + + // Type of the pin + Type() string +} + // CoreAPI defines an unified interface to IPFS for Go programs. type CoreAPI interface { // Unixfs returns an implementation of Unixfs API. @@ -322,5 +331,40 @@ type ObjectStat struct { CumulativeSize int } +// PinAPI specifies the interface to pining +type PinAPI interface { + // Add creates new pin, be default recursive - pinning the whole referenced + // tree + Add(context.Context, Path, ...options.PinAddOption) error + + // WithRecursive is an option for Add which specifies whether to pin an entire + // object tree or just one object. Default: true + WithRecursive(bool) options.PinAddOption + + // Ls returns list of pinned objects on this node + Ls(context.Context) ([]Pin, error) + + // WithType is an option for Ls which allows to specify which pin types should + // be returned + // + // Supported values: + // * "direct" - directly pinned objects + // * "recursive" - roots of recursive pins + // * "indirect" - indirectly pinned objects (referenced by recursively pinned + // objects) + // * "all" - all pinned objects (default) + WithType(string) options.PinLsOption + + // Rm removes pin for object specified by the path + Rm(context.Context, Path) error + + // Update changes one pin to another, skipping checks for matching paths in + // the old tree + Update(ctx context.Context, from Path, to Path) error + + // Verify verifies the integrity of pinned objects + Verify(context.Context) error +} + var ErrIsDir = errors.New("object is a directory") var ErrOffline = errors.New("can't resolve, ipfs node is offline") diff --git a/coreiface/options/pin.go b/coreiface/options/pin.go new file mode 100644 index 000000000..4ad16d555 --- /dev/null +++ b/coreiface/options/pin.go @@ -0,0 +1,58 @@ +package options + +type PinAddSettings struct { + Recursive bool +} + +type PinLsSettings struct { + Type string +} + +type PinAddOption func(*PinAddSettings) error +type PinLsOption func(settings *PinLsSettings) error + +func PinAddOptions(opts ...PinAddOption) (*PinAddSettings, error) { + options := &PinAddSettings{ + Recursive: true, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +func PinLsOptions(opts ...PinLsOption) (*PinLsSettings, error) { + options := &PinLsSettings{ + Type: "all", + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +type PinOptions struct{} + +func (api *PinOptions) WithRecursive(recucsive bool) PinAddOption { + return func(settings *PinAddSettings) error { + settings.Recursive = recucsive + return nil + } +} + +func (api *PinOptions) WithType(t string) PinLsOption { + return func(settings *PinLsSettings) error { + settings.Type = t + return nil + } +} From c1c32446e398484fac04c9a35c4ca92f930bbe2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 10 Jan 2018 18:41:06 +0100 Subject: [PATCH 2165/3817] coreapi: implement pin api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@4597fde83e61fa8d63fa7aef8a3118f7e441b6db --- coreiface/interface.go | 25 ++++++++++++++++++++++--- coreiface/options/pin.go | 27 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 40fa4131e..75a168bf3 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -67,6 +67,24 @@ type Pin interface { Type() string } +// PinStatus holds information about pin health +type PinStatus interface { + // Ok indicates whether the pin has been verified to be correct + Ok() bool + + // BadNodes returns any bad (usually missing) nodes from the pin + BadNodes() []BadPinNode +} + +// BadPinNode is a node that has been marked as bad by Pin.Verify +type BadPinNode interface { + // Path is the path of the node + Path() Path + + // Err is the reason why the node has been marked as bad + Err() error +} + // CoreAPI defines an unified interface to IPFS for Go programs. type CoreAPI interface { // Unixfs returns an implementation of Unixfs API. @@ -83,6 +101,7 @@ type CoreAPI interface { // Key returns an implementation of Key API. Key() KeyAPI + Pin() PinAPI // ObjectAPI returns an implementation of Object API Object() ObjectAPI @@ -342,7 +361,7 @@ type PinAPI interface { WithRecursive(bool) options.PinAddOption // Ls returns list of pinned objects on this node - Ls(context.Context) ([]Pin, error) + Ls(context.Context, ...options.PinLsOption) ([]Pin, error) // WithType is an option for Ls which allows to specify which pin types should // be returned @@ -360,10 +379,10 @@ type PinAPI interface { // Update changes one pin to another, skipping checks for matching paths in // the old tree - Update(ctx context.Context, from Path, to Path) error + Update(ctx context.Context, from Path, to Path, opts ...options.PinUpdateOption) error // Verify verifies the integrity of pinned objects - Verify(context.Context) error + Verify(context.Context) (<-chan PinStatus, error) } var ErrIsDir = errors.New("object is a directory") diff --git a/coreiface/options/pin.go b/coreiface/options/pin.go index 4ad16d555..f97f7b16e 100644 --- a/coreiface/options/pin.go +++ b/coreiface/options/pin.go @@ -8,8 +8,13 @@ type PinLsSettings struct { Type string } +type PinUpdateSettings struct { + Unpin bool +} + type PinAddOption func(*PinAddSettings) error type PinLsOption func(settings *PinLsSettings) error +type PinUpdateOption func(*PinUpdateSettings) error func PinAddOptions(opts ...PinAddOption) (*PinAddSettings, error) { options := &PinAddSettings{ @@ -41,6 +46,21 @@ func PinLsOptions(opts ...PinLsOption) (*PinLsSettings, error) { return options, nil } +func PinUpdateOptions(opts ...PinUpdateOption) (*PinUpdateSettings, error) { + options := &PinUpdateSettings{ + Unpin: true, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + type PinOptions struct{} func (api *PinOptions) WithRecursive(recucsive bool) PinAddOption { @@ -56,3 +76,10 @@ func (api *PinOptions) WithType(t string) PinLsOption { return nil } } + +func (api *PinOptions) WithUnpin(unpin bool) PinUpdateOption { + return func(settings *PinUpdateSettings) error { + settings.Unpin = unpin + return nil + } +} From 115cc884844895575877f443f27cccacdd21f4d9 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 6 Feb 2018 12:05:41 +0100 Subject: [PATCH 2166/3817] Golint: unixfs main module License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@fc05603fea3d88a86d0f0909f36b2208ed6310d1 --- unixfs/{format.go => unixfs.go} | 41 ++++++++++++++++++----- unixfs/{format_test.go => unixfs_test.go} | 0 2 files changed, 33 insertions(+), 8 deletions(-) rename unixfs/{format.go => unixfs.go} (74%) rename unixfs/{format_test.go => unixfs_test.go} (100%) diff --git a/unixfs/format.go b/unixfs/unixfs.go similarity index 74% rename from unixfs/format.go rename to unixfs/unixfs.go index 26d4b0cc3..d04a461ed 100644 --- a/unixfs/format.go +++ b/unixfs/unixfs.go @@ -1,4 +1,4 @@ -// Package format implements a data format for files in the IPFS filesystem It +// Package unixfs implements a data format for files in the IPFS filesystem It // is not the only format in ipfs, but it is the one that the filesystem // assumes package unixfs @@ -6,11 +6,13 @@ package unixfs import ( "errors" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + dag "github.com/ipfs/go-ipfs/merkledag" pb "github.com/ipfs/go-ipfs/unixfs/pb" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) +// Shorthands for protobuffer types const ( TRaw = pb.Data_Raw TFile = pb.Data_File @@ -20,10 +22,14 @@ const ( THAMTShard = pb.Data_HAMTShard ) -var ErrMalformedFileFormat = errors.New("malformed data in file format") -var ErrInvalidDirLocation = errors.New("found directory node in unexpected place") -var ErrUnrecognizedType = errors.New("unrecognized node type") +// Common errors +var ( + ErrMalformedFileFormat = errors.New("malformed data in file format") + ErrInvalidDirLocation = errors.New("found directory node in unexpected place") + ErrUnrecognizedType = errors.New("unrecognized node type") +) +// FromBytes unmarshals a byte slice as protobuf Data. func FromBytes(data []byte) (*pb.Data, error) { pbdata := new(pb.Data) err := proto.Unmarshal(data, pbdata) @@ -33,6 +39,8 @@ func FromBytes(data []byte) (*pb.Data, error) { return pbdata, nil } +// FilePBData creates a protobuf File with the given +// byte slice and returns the marshaled protobuf bytes representing it. func FilePBData(data []byte, totalsize uint64) []byte { pbfile := new(pb.Data) typ := pb.Data_File @@ -98,6 +106,7 @@ func SymlinkData(path string) ([]byte, error) { return out, nil } +// UnwrapData unmarshals a protobuf messages and returns the contents. func UnwrapData(data []byte) ([]byte, error) { pbdata := new(pb.Data) err := proto.Unmarshal(data, pbdata) @@ -107,6 +116,10 @@ func UnwrapData(data []byte) ([]byte, error) { return pbdata.GetData(), nil } +// DataSize returns the size of the contents in protobuf wrapped slice. +// For raw data it simply provides the length of it. For Data_Files, it +// will return the associated filesize. Note that Data_Directories will +// return an error. func DataSize(data []byte) (uint64, error) { pbdata := new(pb.Data) err := proto.Unmarshal(data, pbdata) @@ -116,16 +129,17 @@ func DataSize(data []byte) (uint64, error) { switch pbdata.GetType() { case pb.Data_Directory: - return 0, errors.New("Cant get data size of directory!") + return 0, errors.New("Cant get data size of directory") case pb.Data_File: return pbdata.GetFilesize(), nil case pb.Data_Raw: return uint64(len(pbdata.GetData())), nil default: - return 0, errors.New("Unrecognized node data type!") + return 0, errors.New("Unrecognized node data type") } } +// An FSNode represents a filesystem object. type FSNode struct { Data []byte @@ -139,6 +153,7 @@ type FSNode struct { Type pb.Data_DataType } +// FSNodeFromBytes unmarshal a protobuf message onto an FSNode. func FSNodeFromBytes(b []byte) (*FSNode, error) { pbn := new(pb.Data) err := proto.Unmarshal(b, pbn) @@ -160,11 +175,13 @@ func (n *FSNode) AddBlockSize(s uint64) { n.blocksizes = append(n.blocksizes, s) } +// RemoveBlockSize removes the given child block's size. func (n *FSNode) RemoveBlockSize(i int) { n.subtotal -= n.blocksizes[i] n.blocksizes = append(n.blocksizes[:i], n.blocksizes[i+1:]...) } +// GetBytes marshals this node as a protobuf message. func (n *FSNode) GetBytes() ([]byte, error) { pbn := new(pb.Data) pbn.Type = &n.Type @@ -180,16 +197,19 @@ func (n *FSNode) FileSize() uint64 { return uint64(len(n.Data)) + n.subtotal } +// NumChildren returns the number of child blocks of this node func (n *FSNode) NumChildren() int { return len(n.blocksizes) } +// Metadata is used to store additional FSNode information. type Metadata struct { MimeType string Size uint64 } -//MetadataFromBytes Unmarshals a protobuf message into Metadata. +// MetadataFromBytes Unmarshals a protobuf Data message into Metadata. +// The provided slice should have been encoded with BytesForMetadata(). func MetadataFromBytes(b []byte) (*Metadata, error) { pbd := new(pb.Data) err := proto.Unmarshal(b, pbd) @@ -210,12 +230,16 @@ func MetadataFromBytes(b []byte) (*Metadata, error) { return md, nil } +// Bytes marshals Metadata as a protobuf message of Metadata type. func (m *Metadata) Bytes() ([]byte, error) { pbm := new(pb.Metadata) pbm.MimeType = &m.MimeType return proto.Marshal(pbm) } +// BytesForMetadata wraps the given Metadata as a profobuf message of Data type, +// setting the DataType to Metadata. The wrapped bytes are itself the +// result of calling m.Bytes(). func BytesForMetadata(m *Metadata) ([]byte, error) { pbd := new(pb.Data) pbd.Filesize = proto.Uint64(m.Size) @@ -230,6 +254,7 @@ func BytesForMetadata(m *Metadata) ([]byte, error) { return proto.Marshal(pbd) } +// EmptyDirNode creates an empty folder Protonode. func EmptyDirNode() *dag.ProtoNode { return dag.NodeWithData(FolderPBData()) } diff --git a/unixfs/format_test.go b/unixfs/unixfs_test.go similarity index 100% rename from unixfs/format_test.go rename to unixfs/unixfs_test.go From 81a926ce507e66b4b88fa7598c201d1a77f7889e Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 6 Feb 2018 12:43:13 +0100 Subject: [PATCH 2167/3817] Golint: unixfs/archive License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@ee06103334f5dcdda869a19b73d6ab850c86ac4d --- unixfs/archive/archive.go | 1 + unixfs/archive/tar/writer.go | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index 72b3e2dac..7a561992e 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -1,3 +1,4 @@ +// Package archive provides utilities to archive and compress a [Unixfs] DAG. package archive import ( diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 17614e10e..4503f5593 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -1,3 +1,5 @@ +// Package tar provides functionality to write a unixfs merkledag +// as a tar archive. package tar import ( @@ -69,6 +71,7 @@ func (w *Writer) writeFile(nd *mdag.ProtoNode, pb *upb.Data, fpath string) error return nil } +// WriteNode adds a node to the archive. func (w *Writer) WriteNode(nd ipld.Node, fpath string) error { switch nd := nd.(type) { case *mdag.ProtoNode: @@ -106,6 +109,7 @@ func (w *Writer) WriteNode(nd ipld.Node, fpath string) error { } } +// Close closes the tar writer. func (w *Writer) Close() error { return w.TarW.Close() } From 55e644b60c0f54359a5b3996b793853bf1f01bde Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 6 Feb 2018 12:43:30 +0100 Subject: [PATCH 2168/3817] Golint: unixfs/hamt Note, stuttering required renaming of HamtShard to Shard. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@a7431628611413edab3c025dac844d6e3c9fbc89 --- unixfs/hamt/hamt.go | 70 +++++++++++++++++---------------- unixfs/hamt/hamt_stress_test.go | 12 +++--- unixfs/hamt/hamt_test.go | 24 +++++------ 3 files changed, 55 insertions(+), 51 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 80d97d8ec..70c0b371c 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -38,10 +38,12 @@ import ( ) const ( + // HashMurmur3 is the multiformats identifier for Murmur3 HashMurmur3 uint64 = 0x22 ) -type HamtShard struct { +// A Shard represents the HAMT. It should be initialized with NewShard(). +type Shard struct { nd *dag.ProtoNode bitfield *big.Int @@ -66,9 +68,9 @@ type child interface { Label() string } -// NewHamtShard creates a new, empty HAMT shard with the given size. -func NewHamtShard(dserv ipld.DAGService, size int) (*HamtShard, error) { - ds, err := makeHamtShard(dserv, size) +// NewShard creates a new, empty HAMT shard with the given size. +func NewShard(dserv ipld.DAGService, size int) (*Shard, error) { + ds, err := makeShard(dserv, size) if err != nil { return nil, err } @@ -79,13 +81,13 @@ func NewHamtShard(dserv ipld.DAGService, size int) (*HamtShard, error) { return ds, nil } -func makeHamtShard(ds ipld.DAGService, size int) (*HamtShard, error) { +func makeShard(ds ipld.DAGService, size int) (*Shard, error) { lg2s := int(math.Log2(float64(size))) if 1<= len(ds.children) || i < 0 { return nil, fmt.Errorf("invalid index passed to getChild (likely corrupt bitfield)") } @@ -281,7 +283,7 @@ func (ds *HamtShard) getChild(ctx context.Context, i int) (child, error) { // loadChild reads the i'th child node of this shard from disk and returns it // as a 'child' interface -func (ds *HamtShard) loadChild(ctx context.Context, i int) (child, error) { +func (ds *Shard) loadChild(ctx context.Context, i int) (child, error) { lnk := ds.nd.Links()[i] if len(lnk.Name) < ds.maxpadlen { return nil, fmt.Errorf("invalid link name '%s'", lnk.Name) @@ -326,12 +328,12 @@ func (ds *HamtShard) loadChild(ctx context.Context, i int) (child, error) { return c, nil } -func (ds *HamtShard) setChild(i int, c child) { +func (ds *Shard) setChild(i int, c child) { ds.children[i] = c } // Link returns a merklelink to this shard node -func (ds *HamtShard) Link() (*ipld.Link, error) { +func (ds *Shard) Link() (*ipld.Link, error) { nd, err := ds.Node() if err != nil { return nil, err @@ -345,7 +347,7 @@ func (ds *HamtShard) Link() (*ipld.Link, error) { return ipld.MakeLink(nd) } -func (ds *HamtShard) insertChild(idx int, key string, lnk *ipld.Link) error { +func (ds *Shard) insertChild(idx int, key string, lnk *ipld.Link) error { if lnk == nil { return os.ErrNotExist } @@ -364,7 +366,7 @@ func (ds *HamtShard) insertChild(idx int, key string, lnk *ipld.Link) error { return nil } -func (ds *HamtShard) rmChild(i int) error { +func (ds *Shard) rmChild(i int) error { if i < 0 || i >= len(ds.children) || i >= len(ds.nd.Links()) { return fmt.Errorf("hamt: attempted to remove child with out of range index") } @@ -378,7 +380,7 @@ func (ds *HamtShard) rmChild(i int) error { return nil } -func (ds *HamtShard) getValue(ctx context.Context, hv *hashBits, key string, cb func(*shardValue) error) error { +func (ds *Shard) getValue(ctx context.Context, hv *hashBits, key string, cb func(*shardValue) error) error { idx := hv.Next(ds.tableSizeLg2) if ds.bitfield.Bit(int(idx)) == 1 { cindex := ds.indexForBitPos(idx) @@ -389,7 +391,7 @@ func (ds *HamtShard) getValue(ctx context.Context, hv *hashBits, key string, cb } switch child := child.(type) { - case *HamtShard: + case *Shard: return child.getValue(ctx, hv, key, cb) case *shardValue: if child.key == key { @@ -401,7 +403,8 @@ func (ds *HamtShard) getValue(ctx context.Context, hv *hashBits, key string, cb return os.ErrNotExist } -func (ds *HamtShard) EnumLinks(ctx context.Context) ([]*ipld.Link, error) { +// EnumLinks collects all links in the Shard. +func (ds *Shard) EnumLinks(ctx context.Context) ([]*ipld.Link, error) { var links []*ipld.Link err := ds.ForEachLink(ctx, func(l *ipld.Link) error { links = append(links, l) @@ -410,7 +413,8 @@ func (ds *HamtShard) EnumLinks(ctx context.Context) ([]*ipld.Link, error) { return links, err } -func (ds *HamtShard) ForEachLink(ctx context.Context, f func(*ipld.Link) error) error { +// ForEachLink walks the Shard and calls the given function. +func (ds *Shard) ForEachLink(ctx context.Context, f func(*ipld.Link) error) error { return ds.walkTrie(ctx, func(sv *shardValue) error { lnk := sv.val lnk.Name = sv.key @@ -419,7 +423,7 @@ func (ds *HamtShard) ForEachLink(ctx context.Context, f func(*ipld.Link) error) }) } -func (ds *HamtShard) walkTrie(ctx context.Context, cb func(*shardValue) error) error { +func (ds *Shard) walkTrie(ctx context.Context, cb func(*shardValue) error) error { for i := 0; i < ds.tableSize; i++ { if ds.bitfield.Bit(i) == 0 { continue @@ -440,7 +444,7 @@ func (ds *HamtShard) walkTrie(ctx context.Context, cb func(*shardValue) error) e return err } - case *HamtShard: + case *Shard: err := c.walkTrie(ctx, cb) if err != nil { return err @@ -452,7 +456,7 @@ func (ds *HamtShard) walkTrie(ctx context.Context, cb func(*shardValue) error) e return nil } -func (ds *HamtShard) modifyValue(ctx context.Context, hv *hashBits, key string, val *ipld.Link) error { +func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val *ipld.Link) error { idx := hv.Next(ds.tableSizeLg2) if ds.bitfield.Bit(idx) != 1 { @@ -467,7 +471,7 @@ func (ds *HamtShard) modifyValue(ctx context.Context, hv *hashBits, key string, } switch child := child.(type) { - case *HamtShard: + case *Shard: err := child.modifyValue(ctx, hv, key, val) if err != nil { return err @@ -510,7 +514,7 @@ func (ds *HamtShard) modifyValue(ctx context.Context, hv *hashBits, key string, } // replace value with another shard, one level deeper - ns, err := NewHamtShard(ds.dserv, ds.tableSize) + ns, err := NewShard(ds.dserv, ds.tableSize) if err != nil { return err } @@ -540,7 +544,7 @@ func (ds *HamtShard) modifyValue(ctx context.Context, hv *hashBits, key string, // indexForBitPos returns the index within the collapsed array corresponding to // the given bit in the bitset. The collapsed array contains only one entry // per bit set in the bitfield, and this function is used to map the indices. -func (ds *HamtShard) indexForBitPos(bp int) int { +func (ds *Shard) indexForBitPos(bp int) int { // TODO: an optimization could reuse the same 'mask' here and change the size // as needed. This isnt yet done as the bitset package doesnt make it easy // to do. @@ -553,6 +557,6 @@ func (ds *HamtShard) indexForBitPos(bp int) int { } // linkNamePrefix takes in the bitfield index of an entry and returns its hex prefix -func (ds *HamtShard) linkNamePrefix(idx int) string { +func (ds *Shard) linkNamePrefix(idx int) string { return fmt.Sprintf(ds.prefixPadStr, idx) } diff --git a/unixfs/hamt/hamt_stress_test.go b/unixfs/hamt/hamt_stress_test.go index e746a44b5..185e385e1 100644 --- a/unixfs/hamt/hamt_stress_test.go +++ b/unixfs/hamt/hamt_stress_test.go @@ -94,7 +94,7 @@ func TestOrderConsistency(t *testing.T) { } } -func validateOpSetCompletion(t *testing.T, s *HamtShard, keep, temp []string) error { +func validateOpSetCompletion(t *testing.T, s *Shard, keep, temp []string) error { ctx := context.TODO() for _, n := range keep { _, err := s.Find(ctx, n) @@ -113,9 +113,9 @@ func validateOpSetCompletion(t *testing.T, s *HamtShard, keep, temp []string) er return nil } -func executeOpSet(t *testing.T, ds ipld.DAGService, width int, ops []testOp) (*HamtShard, error) { +func executeOpSet(t *testing.T, ds ipld.DAGService, width int, ops []testOp) (*Shard, error) { ctx := context.TODO() - s, err := NewHamtShard(ds, width) + s, err := NewShard(ds, width) if err != nil { return nil, err } @@ -189,9 +189,9 @@ func genOpSet(seed int64, keep, temp []string) []testOp { } // executes the given op set with a repl to allow easier debugging -/*func debugExecuteOpSet(ds node.DAGService, width int, ops []testOp) (*HamtShard, error) { +/*func debugExecuteOpSet(ds node.DAGService, width int, ops []testOp) (*Shard, error) { - s, err := NewHamtShard(ds, width) + s, err := NewShard(ds, width) if err != nil { return nil, err } @@ -244,7 +244,7 @@ mainloop: } case "restart": var err error - s, err = NewHamtShard(ds, width) + s, err = NewShard(ds, width) if err != nil { panic(err) } diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index f89e9ac57..72f74526a 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -26,14 +26,14 @@ func shuffle(seed int64, arr []string) { } } -func makeDir(ds ipld.DAGService, size int) ([]string, *HamtShard, error) { +func makeDir(ds ipld.DAGService, size int) ([]string, *Shard, error) { return makeDirWidth(ds, size, 256) } -func makeDirWidth(ds ipld.DAGService, size, width int) ([]string, *HamtShard, error) { +func makeDirWidth(ds ipld.DAGService, size, width int) ([]string, *Shard, error) { ctx := context.Background() - s, _ := NewHamtShard(ds, width) + s, _ := NewShard(ds, width) var dirs []string for i := 0; i < size; i++ { @@ -54,7 +54,7 @@ func makeDirWidth(ds ipld.DAGService, size, width int) ([]string, *HamtShard, er return dirs, s, nil } -func assertLink(s *HamtShard, name string, found bool) error { +func assertLink(s *Shard, name string, found bool) error { _, err := s.Find(context.Background(), name) switch err { case os.ErrNotExist: @@ -74,7 +74,7 @@ func assertLink(s *HamtShard, name string, found bool) error { } } -func assertSerializationWorks(ds ipld.DAGService, s *HamtShard) error { +func assertSerializationWorks(ds ipld.DAGService, s *Shard) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() nd, err := s.Node() @@ -141,7 +141,7 @@ func TestBasicSet(t *testing.T) { func TestDirBuilding(t *testing.T) { ds := mdtest.Mock() - _, _ = NewHamtShard(ds, 256) + _, _ = NewShard(ds, 256) _, s, err := makeDir(ds, 200) if err != nil { @@ -164,7 +164,7 @@ func TestDirBuilding(t *testing.T) { func TestShardReload(t *testing.T) { ds := mdtest.Mock() - _, _ = NewHamtShard(ds, 256) + _, _ = NewShard(ds, 256) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -307,7 +307,7 @@ func TestSetAfterMarshal(t *testing.T) { func TestDuplicateAddShard(t *testing.T) { ds := mdtest.Mock() - dir, _ := NewHamtShard(ds, 256) + dir, _ := NewShard(ds, 256) nd := new(dag.ProtoNode) ctx := context.Background() @@ -430,7 +430,7 @@ func TestRemoveElemsAfterMarshal(t *testing.T) { func TestBitfieldIndexing(t *testing.T) { ds := mdtest.Mock() - s, _ := NewHamtShard(ds, 256) + s, _ := NewShard(ds, 256) set := func(i int) { s.bitfield.SetBit(s.bitfield, i, 1) @@ -466,7 +466,7 @@ func TestSetHamtChild(t *testing.T) { ctx := context.Background() ds := mdtest.Mock() - s, _ := NewHamtShard(ds, 256) + s, _ := NewShard(ds, 256) e := ft.EmptyDirNode() ds.Add(ctx, e) @@ -527,7 +527,7 @@ func BenchmarkHAMTSet(b *testing.B) { ctx := context.Background() ds := mdtest.Mock() - sh, _ := NewHamtShard(ds, 256) + sh, _ := NewShard(ds, 256) nd, err := sh.Node() if err != nil { b.Fatal(err) @@ -560,7 +560,7 @@ func BenchmarkHAMTSet(b *testing.B) { } func TestHamtBadSize(t *testing.T) { - _, err := NewHamtShard(nil, 7) + _, err := NewShard(nil, 7) if err == nil { t.Fatal("should have failed to construct hamt with bad size") } From 7ba8ec18d81d01eb24541333f8df753b702479bf Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 6 Feb 2018 13:08:58 +0100 Subject: [PATCH 2169/3817] Golint: unixfs/mod unixfs/test License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@55c72fea1bef18ace94805423f8e518ae61c662a --- unixfs/mod/dagmodifier.go | 18 +++++++++++++----- unixfs/test/utils.go | 13 +++++++++---- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 8bf9382dd..05c5c3587 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -1,3 +1,5 @@ +// Package mod provides DAG modification utilities to, for example, +// insert additional nodes in a unixfs DAG or truncate them. package mod import ( @@ -19,8 +21,12 @@ import ( ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) -var ErrSeekFail = errors.New("failed to seek properly") -var ErrUnrecognizedWhence = errors.New("unrecognized whence") +// Common errors +var ( + ErrSeekFail = errors.New("failed to seek properly") + ErrUnrecognizedWhence = errors.New("unrecognized whence") + ErrNotUnixfs = fmt.Errorf("dagmodifier only supports unixfs nodes (proto or raw)") +) // 2MB var writebufferSize = 1 << 21 @@ -46,8 +52,6 @@ type DagModifier struct { read uio.DagReader } -var ErrNotUnixfs = fmt.Errorf("dagmodifier only supports unixfs nodes (proto or raw)") - // NewDagModifier returns a new DagModifier, the Cid prefix for newly // created nodes will be inherted from the passed in node. If the Cid // version if not 0 raw leaves will also be enabled. The Prefix and @@ -412,7 +416,7 @@ func (dm *DagModifier) readPrep() error { return nil } -// Read data from this dag starting at the current offset +// CtxReadFull reads data from this dag starting at the current offset func (dm *DagModifier) CtxReadFull(ctx context.Context, b []byte) (int, error) { err := dm.readPrep() if err != nil { @@ -438,6 +442,8 @@ func (dm *DagModifier) HasChanges() bool { return dm.wrBuf != nil } +// Seek modifies the offset according to whence. See unixfs/io for valid whence +// values. func (dm *DagModifier) Seek(offset int64, whence int) (int64, error) { err := dm.Sync() if err != nil { @@ -479,6 +485,8 @@ func (dm *DagModifier) Seek(offset int64, whence int) (int64, error) { return int64(dm.curWrOff), nil } +// Truncate truncates the current Node to 'size' and replaces it with the +// new one. func (dm *DagModifier) Truncate(size int64) error { err := dm.Sync() if err != nil { diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 5e1977ddb..0ca47c842 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -21,6 +21,7 @@ import ( ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) +// SizeSplitterGen creates a generator. func SizeSplitterGen(size int64) chunk.SplitterGen { return func(r io.Reader) chunk.Splitter { return chunk.NewSizeSplitter(r, size) @@ -41,10 +42,13 @@ type NodeOpts struct { RawLeavesUsed bool } -var UseProtoBufLeaves = NodeOpts{Prefix: mdag.V0CidPrefix()} -var UseRawLeaves = NodeOpts{Prefix: mdag.V0CidPrefix(), ForceRawLeaves: true, RawLeavesUsed: true} -var UseCidV1 = NodeOpts{Prefix: mdag.V1CidPrefix(), RawLeavesUsed: true} -var UseBlake2b256 NodeOpts +// Some shorthands for NodeOpts. +var ( + UseProtoBufLeaves = NodeOpts{Prefix: mdag.V0CidPrefix()} + UseRawLeaves = NodeOpts{Prefix: mdag.V0CidPrefix(), ForceRawLeaves: true, RawLeavesUsed: true} + UseCidV1 = NodeOpts{Prefix: mdag.V1CidPrefix(), RawLeavesUsed: true} + UseBlake2b256 NodeOpts +) func init() { UseBlake2b256 = UseCidV1 @@ -88,6 +92,7 @@ func GetRandomNode(t testing.TB, dserv ipld.DAGService, size int64, opts NodeOpt return buf, node } +// ArrComp checks if two byte slices are the same. func ArrComp(a, b []byte) error { if len(a) != len(b) { return fmt.Errorf("Arrays differ in length. %d != %d", len(a), len(b)) From cde1a522c783be3ab90fd3c9c83f6aae3435b5a9 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 6 Feb 2018 13:48:35 +0100 Subject: [PATCH 2170/3817] Golint: unixfs/io License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@15596cf791ded4a03cc8997cf84ee3843f4f8981 --- unixfs/io/bufdagreader.go | 4 +++- unixfs/io/dagreader.go | 14 ++++++++---- unixfs/io/dagreader_test.go | 2 +- unixfs/io/dirbuilder.go | 17 +++++++++++---- unixfs/io/doc.go | 2 +- unixfs/io/pbdagreader.go | 43 ++++++++++++++++++++----------------- 6 files changed, 51 insertions(+), 31 deletions(-) diff --git a/unixfs/io/bufdagreader.go b/unixfs/io/bufdagreader.go index 2f7358640..8c6fd0cf8 100644 --- a/unixfs/io/bufdagreader.go +++ b/unixfs/io/bufdagreader.go @@ -10,7 +10,9 @@ type bufDagReader struct { *bytes.Reader } -func NewBufDagReader(b []byte) *bufDagReader { +// newBufDagReader returns a DAG reader for the given byte slice. +// BufDagReader is used to read RawNodes. +func newBufDagReader(b []byte) *bufDagReader { return &bufDagReader{bytes.NewReader(b)} } diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index c6e9cf0d9..037e0a256 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -14,10 +14,15 @@ import ( ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) -var ErrIsDir = errors.New("this dag node is a directory") - -var ErrCantReadSymlinks = errors.New("cannot currently read symlinks") +// Common errors +var ( + ErrIsDir = errors.New("this dag node is a directory") + ErrCantReadSymlinks = errors.New("cannot currently read symlinks") +) +// A DagReader represents a ReadSeekCloser which offers additional methods +// like Size. Different implementations of readers are used for the different +// types of unixfs/protobuf-encoded nodes. type DagReader interface { ReadSeekCloser Size() uint64 @@ -25,6 +30,7 @@ type DagReader interface { Offset() int64 } +// A ReadSeekCloser implements interfaces to read, write, seek and close. type ReadSeekCloser interface { io.Reader io.Seeker @@ -37,7 +43,7 @@ type ReadSeekCloser interface { func NewDagReader(ctx context.Context, n ipld.Node, serv ipld.NodeGetter) (DagReader, error) { switch n := n.(type) { case *mdag.RawNode: - return NewBufDagReader(n.RawData()), nil + return newBufDagReader(n.RawData()), nil case *mdag.ProtoNode: pb := new(ftpb.Data) if err := proto.Unmarshal(n.Data(), pb); err != nil { diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index 82ff10234..e3d3d042b 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -102,7 +102,7 @@ func TestSeekAndReadLarge(t *testing.T) { t.Fatal("seeked read failed") } - pbdr := reader.(*pbDagReader) + pbdr := reader.(*PBDagReader) var count int for i, p := range pbdr.promises { if i > 20 && i < 30 { diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 2deb4bcc8..f2dee053c 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -8,8 +8,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) @@ -25,11 +25,14 @@ var UseHAMTSharding = false // DefaultShardWidth is the default value used for hamt sharding width. var DefaultShardWidth = 256 +// Directory allows to work with UnixFS directory nodes, adding and removing +// children. It allows to work with different directory schemes, +// like the classic or the HAMT one. type Directory struct { dserv ipld.DAGService dirnode *mdag.ProtoNode - shard *hamt.HamtShard + shard *hamt.Shard } // NewDirectory returns a Directory. It needs a DAGService to add the Children @@ -37,7 +40,7 @@ func NewDirectory(dserv ipld.DAGService) *Directory { db := new(Directory) db.dserv = dserv if UseHAMTSharding { - s, err := hamt.NewHamtShard(dserv, DefaultShardWidth) + s, err := hamt.NewShard(dserv, DefaultShardWidth) if err != nil { panic(err) // will only panic if DefaultShardWidth is a bad value } @@ -113,7 +116,7 @@ func (d *Directory) AddChild(ctx context.Context, name string, nd ipld.Node) err } func (d *Directory) switchToSharding(ctx context.Context) error { - s, err := hamt.NewHamtShard(d.dserv, DefaultShardWidth) + s, err := hamt.NewShard(d.dserv, DefaultShardWidth) if err != nil { return err } @@ -136,6 +139,7 @@ func (d *Directory) switchToSharding(ctx context.Context) error { return nil } +// ForEachLink applies the given function to Links in the directory. func (d *Directory) ForEachLink(ctx context.Context, f func(*ipld.Link) error) error { if d.shard == nil { for _, l := range d.dirnode.Links() { @@ -149,6 +153,7 @@ func (d *Directory) ForEachLink(ctx context.Context, f func(*ipld.Link) error) e return d.shard.ForEachLink(ctx, f) } +// Links returns the all the links in the directory node. func (d *Directory) Links(ctx context.Context) ([]*ipld.Link, error) { if d.shard == nil { return d.dirnode.Links(), nil @@ -157,6 +162,9 @@ func (d *Directory) Links(ctx context.Context) ([]*ipld.Link, error) { return d.shard.EnumLinks(ctx) } +// Find returns the ipld.Node with the given name, if it is contained in this +// directory. Find only searches in the most inmediate links, and not +// recursively in the tree. func (d *Directory) Find(ctx context.Context, name string) (ipld.Node, error) { if d.shard == nil { lnk, err := d.dirnode.GetNodeLink(name) @@ -179,6 +187,7 @@ func (d *Directory) Find(ctx context.Context, name string) (ipld.Node, error) { return lnk.GetNode(ctx, d.dserv) } +// RemoveChild removes the child with the given name. func (d *Directory) RemoveChild(ctx context.Context, name string) error { if d.shard == nil { return d.dirnode.RemoveNodeLink(name) diff --git a/unixfs/io/doc.go b/unixfs/io/doc.go index b28755fc6..cf844bd23 100644 --- a/unixfs/io/doc.go +++ b/unixfs/io/doc.go @@ -1,3 +1,3 @@ -// package unixfs/io implements convenience objects for working with the ipfs +// Package io implements convenience objects for working with the ipfs // unixfs data format. package io diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 0c6bee832..4523481b3 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -15,8 +15,8 @@ import ( ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) -// DagReader provides a way to easily read the data contained in a dag. -type pbDagReader struct { +// PBDagReader provides a way to easily read the data contained in a dag. +type PBDagReader struct { serv ipld.NodeGetter // the node being read @@ -48,16 +48,16 @@ type pbDagReader struct { cancel func() } -var _ DagReader = (*pbDagReader)(nil) +var _ DagReader = (*PBDagReader)(nil) // NewPBFileReader constructs a new PBFileReader. -func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv ipld.NodeGetter) *pbDagReader { +func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv ipld.NodeGetter) *PBDagReader { fctx, cancel := context.WithCancel(ctx) curLinks := getLinkCids(n) - return &pbDagReader{ + return &PBDagReader{ node: n, serv: serv, - buf: NewBufDagReader(pb.GetData()), + buf: newBufDagReader(pb.GetData()), promises: make([]*ipld.NodePromise, len(curLinks)), links: curLinks, ctx: fctx, @@ -68,7 +68,7 @@ func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv const preloadSize = 10 -func (dr *pbDagReader) preloadNextNodes(ctx context.Context) { +func (dr *PBDagReader) preloadNextNodes(ctx context.Context) { beg := dr.linkPosition end := beg + preloadSize if end >= len(dr.links) { @@ -82,7 +82,7 @@ func (dr *pbDagReader) preloadNextNodes(ctx context.Context) { // precalcNextBuf follows the next link in line and loads it from the // DAGService, setting the next buffer to read from -func (dr *pbDagReader) precalcNextBuf(ctx context.Context) error { +func (dr *PBDagReader) precalcNextBuf(ctx context.Context) error { if dr.buf != nil { dr.buf.Close() // Just to make sure dr.buf = nil @@ -119,7 +119,7 @@ func (dr *pbDagReader) precalcNextBuf(ctx context.Context) error { dr.buf = NewPBFileReader(dr.ctx, nxt, pb, dr.serv) return nil case ftpb.Data_Raw: - dr.buf = NewBufDagReader(pb.GetData()) + dr.buf = newBufDagReader(pb.GetData()) return nil case ftpb.Data_Metadata: return errors.New("shouldnt have had metadata object inside file") @@ -145,17 +145,17 @@ func getLinkCids(n ipld.Node) []*cid.Cid { } // Size return the total length of the data from the DAG structured file. -func (dr *pbDagReader) Size() uint64 { +func (dr *PBDagReader) Size() uint64 { return dr.pbdata.GetFilesize() } // Read reads data from the DAG structured file -func (dr *pbDagReader) Read(b []byte) (int, error) { +func (dr *PBDagReader) Read(b []byte) (int, error) { return dr.CtxReadFull(dr.ctx, b) } // CtxReadFull reads data from the DAG structured file -func (dr *pbDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { +func (dr *PBDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { if dr.buf == nil { if err := dr.precalcNextBuf(ctx); err != nil { return 0, err @@ -189,7 +189,8 @@ func (dr *pbDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { } } -func (dr *pbDagReader) WriteTo(w io.Writer) (int64, error) { +// WriteTo writes to the given writer. +func (dr *PBDagReader) WriteTo(w io.Writer) (int64, error) { if dr.buf == nil { if err := dr.precalcNextBuf(dr.ctx); err != nil { return 0, err @@ -220,12 +221,14 @@ func (dr *pbDagReader) WriteTo(w io.Writer) (int64, error) { } } -func (dr *pbDagReader) Close() error { +// Close closes the reader. +func (dr *PBDagReader) Close() error { dr.cancel() return nil } -func (dr *pbDagReader) Offset() int64 { +// Offset returns the current reader offset +func (dr *PBDagReader) Offset() int64 { return dr.offset } @@ -233,7 +236,7 @@ func (dr *pbDagReader) Offset() int64 { // interface matches standard unix seek // TODO: check if we can do relative seeks, to reduce the amount of dagreader // recreations that need to happen. -func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) { +func (dr *PBDagReader) Seek(offset int64, whence int) (int64, error) { switch whence { case io.SeekStart: if offset < 0 { @@ -253,17 +256,17 @@ func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) { if dr.buf != nil { dr.buf.Close() } - dr.buf = NewBufDagReader(pb.GetData()[offset:]) + dr.buf = newBufDagReader(pb.GetData()[offset:]) // start reading links from the beginning dr.linkPosition = 0 dr.offset = offset return offset, nil - } else { - // skip past root block data - left -= int64(len(pb.Data)) } + // skip past root block data + left -= int64(len(pb.Data)) + // iterate through links and find where we need to be for i := 0; i < len(pb.Blocksizes); i++ { if pb.Blocksizes[i] > uint64(left) { From 87e0d66f3438c1b2b7f831a48d443aad5eb8e0fe Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 6 Feb 2018 17:05:59 +0100 Subject: [PATCH 2171/3817] Golint: fix golint warnings in merkledag submodule License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@e91a9c493ff985138fbbf473dc8cac88ce3a5aa3 --- ipld/merkledag/coding.go | 2 +- ipld/merkledag/errservice.go | 6 ++++++ ipld/merkledag/merkledag.go | 30 ++++++++++++++++++---------- ipld/merkledag/merkledag_test.go | 14 ++++++++----- ipld/merkledag/node.go | 31 ++++++++++++++++++++++++----- ipld/merkledag/raw.go | 8 ++++++++ ipld/merkledag/rwservice.go | 6 ++++++ ipld/merkledag/traverse/traverse.go | 28 +++++++++++++------------- ipld/merkledag/utils/diff.go | 22 ++++++++++++++------ ipld/merkledag/utils/utils.go | 5 +++++ 10 files changed, 111 insertions(+), 41 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 37d2a56a0..62b8353b2 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -102,7 +102,7 @@ func (n *ProtoNode) EncodeProtobuf(force bool) ([]byte, error) { return n.encoded, nil } -// Decoded decodes raw data and returns a new Node instance. +// DecodeProtobuf decodes raw data and returns a new Node instance. func DecodeProtobuf(encoded []byte) (*ProtoNode, error) { n := new(ProtoNode) err := n.unmarshal(encoded) diff --git a/ipld/merkledag/errservice.go b/ipld/merkledag/errservice.go index a8cd23737..8681cb6ee 100644 --- a/ipld/merkledag/errservice.go +++ b/ipld/merkledag/errservice.go @@ -14,28 +14,34 @@ type ErrorService struct { var _ ipld.DAGService = (*ErrorService)(nil) +// Add returns an error. func (cs *ErrorService) Add(ctx context.Context, nd ipld.Node) error { return cs.Err } +// AddMany returns an error. func (cs *ErrorService) AddMany(ctx context.Context, nds []ipld.Node) error { return cs.Err } +// Get returns an error. func (cs *ErrorService) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { return nil, cs.Err } +// GetMany many returns an error. func (cs *ErrorService) GetMany(ctx context.Context, cids []*cid.Cid) <-chan *ipld.NodeOption { ch := make(chan *ipld.NodeOption) close(ch) return ch } +// Remove returns an error. func (cs *ErrorService) Remove(ctx context.Context, c *cid.Cid) error { return cs.Err } +// RemoveMany returns an error. func (cs *ErrorService) RemoveMany(ctx context.Context, cids []*cid.Cid) error { return cs.Err } diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 1ee6ccfb6..77ceb5cec 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -1,4 +1,4 @@ -// package merkledag implements the IPFS Merkle DAG datastructures. +// Package merkledag implements the IPFS Merkle DAG data structures. package merkledag import ( @@ -23,8 +23,14 @@ func init() { ipld.Register(cid.DagCBOR, ipldcbor.DecodeBlock) } +// contextKey is a type to use as value for the ProgressTracker contexts. +type contextKey string + +const progressContextKey contextKey = "progress" + // NewDAGService constructs a new DAGService (using the default implementation). -func NewDAGService(bs bserv.BlockService) *dagService { +// Note that the default implementation is also an ipld.LinkGetter. +func NewDAGService(bs bserv.BlockService) ipld.DAGService { return &dagService{Blocks: bs} } @@ -147,8 +153,8 @@ func (sg *sesGetter) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *ipld. } // Session returns a NodeGetter using a new session for block fetches. -func (ds *dagService) Session(ctx context.Context) ipld.NodeGetter { - return &sesGetter{bserv.NewSession(ctx, ds.Blocks)} +func (n *dagService) Session(ctx context.Context) ipld.NodeGetter { + return &sesGetter{bserv.NewSession(ctx, n.Blocks)} } // FetchGraph fetches all nodes that are children of the given node @@ -159,7 +165,7 @@ func FetchGraph(ctx context.Context, root *cid.Cid, serv ipld.DAGService) error ng = &sesGetter{bserv.NewSession(ctx, ds.Blocks)} } - v, _ := ctx.Value("progress").(*ProgressTracker) + v, _ := ctx.Value(progressContextKey).(*ProgressTracker) if v == nil { return EnumerateChildrenAsync(ctx, GetLinksDirect(ng), root, cid.NewSet().Visit) } @@ -168,9 +174,8 @@ func FetchGraph(ctx context.Context, root *cid.Cid, serv ipld.DAGService) error if set.Visit(c) { v.Increment() return true - } else { - return false } + return false } return EnumerateChildrenAsync(ctx, GetLinksDirect(ng), root, visit) } @@ -179,8 +184,8 @@ func FetchGraph(ctx context.Context, root *cid.Cid, serv ipld.DAGService) error // returns the indexes of any links pointing to it func FindLinks(links []*cid.Cid, c *cid.Cid, start int) []int { var out []int - for i, lnk_c := range links[start:] { - if c.Equals(lnk_c) { + for i, lnkC := range links[start:] { + if c.Equals(lnkC) { out = append(out, i+start) } } @@ -265,21 +270,26 @@ func EnumerateChildren(ctx context.Context, getLinks GetLinks, root *cid.Cid, vi return nil } +// ProgressTracker is used to show progress when fetching nodes. type ProgressTracker struct { Total int lk sync.Mutex } +// DeriveContext returns a new context with value "progress" derived from +// the given one. func (p *ProgressTracker) DeriveContext(ctx context.Context) context.Context { - return context.WithValue(ctx, "progress", p) + return context.WithValue(ctx, progressContextKey, p) } +// Increment adds one to the total progress. func (p *ProgressTracker) Increment() { p.lk.Lock() defer p.lk.Unlock() p.Total++ } +// Value returns the current progress. func (p *ProgressTracker) Value() int { p.lk.Lock() defer p.lk.Unlock() diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 9ca3d79e3..2306d5c64 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -13,6 +13,8 @@ import ( "testing" "time" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + bserv "github.com/ipfs/go-ipfs/blockservice" bstest "github.com/ipfs/go-ipfs/blockservice/test" offline "github.com/ipfs/go-ipfs/exchange/offline" @@ -22,7 +24,6 @@ import ( mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" @@ -241,9 +242,10 @@ func TestFetchGraph(t *testing.T) { // create an offline dagstore and ensure all blocks were fetched bs := bserv.New(bsis[1].Blockstore(), offline.Exchange(bsis[1].Blockstore())) - offline_ds := NewDAGService(bs) + // we know the default dagService implements LinkGetter + offlineDS := NewDAGService(bs).(ipld.LinkGetter) - err = EnumerateChildren(context.Background(), offline_ds.GetLinks, root.Cid(), func(_ *cid.Cid) bool { return true }) + err = EnumerateChildren(context.Background(), offlineDS.GetLinks, root.Cid(), func(_ *cid.Cid) bool { return true }) if err != nil { t.Fatal(err) } @@ -260,7 +262,9 @@ func TestEnumerateChildren(t *testing.T) { } set := cid.NewSet() - err = EnumerateChildren(context.Background(), ds.GetLinks, root.Cid(), set.Visit) + lg := ds.(ipld.LinkGetter) + + err = EnumerateChildren(context.Background(), lg.GetLinks, root.Cid(), set.Visit) if err != nil { t.Fatal(err) } @@ -491,7 +495,7 @@ func TestCidRetention(t *testing.T) { } func TestCidRawDoesnNeedData(t *testing.T) { - srv := NewDAGService(dstest.Bserv()) + srv := NewDAGService(dstest.Bserv()).(ipld.LinkGetter) nd := NewRawNode([]byte("somedata")) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 215161258..a33dfcf6c 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -10,10 +10,13 @@ import ( ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) -var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") -var ErrLinkNotFound = fmt.Errorf("no link by that name") +// Common errors +var ( + ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") + ErrLinkNotFound = fmt.Errorf("no link by that name") +) -// Node represents a node in the IPFS Merkle DAG. +// ProtoNode represents a node in the IPFS Merkle DAG. // nodes have opaque data and a set of navigable links. type ProtoNode struct { links []*ipld.Link @@ -73,12 +76,14 @@ func (n *ProtoNode) SetPrefix(prefix *cid.Prefix) { } } +// LinkSlice is a slice of ipld.Links type LinkSlice []*ipld.Link func (ls LinkSlice) Len() int { return len(ls) } func (ls LinkSlice) Swap(a, b int) { ls[a], ls[b] = ls[b], ls[a] } func (ls LinkSlice) Less(a, b int) bool { return ls[a].Name < ls[b].Name } +// NodeWithData builds a new Protonode with the given data. func NodeWithData(d []byte) *ProtoNode { return &ProtoNode{data: d} } @@ -204,15 +209,18 @@ func (n *ProtoNode) Copy() ipld.Node { return nnode } +// RawData returns the protobuf-encoded version of the node. func (n *ProtoNode) RawData() []byte { out, _ := n.EncodeProtobuf(false) return out } +// Data returns the data stored by this node. func (n *ProtoNode) Data() []byte { return n.data } +// SetData stores data in this nodes. func (n *ProtoNode) SetData(d []byte) { n.encoded = nil n.cached = nil @@ -265,12 +273,14 @@ func (n *ProtoNode) Stat() (*ipld.NodeStat, error) { }, nil } +// Loggable implements the ipfs/go-log.Loggable interface. func (n *ProtoNode) Loggable() map[string]interface{} { return map[string]interface{}{ "node": n.String(), } } +// UnmarshalJSON reads the node fields from a JSON-encoded byte slice. func (n *ProtoNode) UnmarshalJSON(b []byte) error { s := struct { Data []byte `json:"data"` @@ -287,6 +297,7 @@ func (n *ProtoNode) UnmarshalJSON(b []byte) error { return nil } +// MarshalJSON returns a JSON representation of the node. func (n *ProtoNode) MarshalJSON() ([]byte, error) { out := map[string]interface{}{ "data": n.data, @@ -296,6 +307,8 @@ func (n *ProtoNode) MarshalJSON() ([]byte, error) { return json.Marshal(out) } +// Cid returns the node's Cid, calculated according to its prefix +// and raw data contents. func (n *ProtoNode) Cid() *cid.Cid { if n.encoded != nil && n.cached != nil { return n.cached @@ -316,6 +329,7 @@ func (n *ProtoNode) Cid() *cid.Cid { return c } +// String prints the node's Cid. func (n *ProtoNode) String() string { return n.Cid().String() } @@ -332,18 +346,24 @@ func (n *ProtoNode) Multihash() mh.Multihash { return n.cached.Hash() } +// Links returns the node links. func (n *ProtoNode) Links() []*ipld.Link { return n.links } +// SetLinks replaces the node links with the given ones. func (n *ProtoNode) SetLinks(links []*ipld.Link) { n.links = links } +// Resolve is an alias for ResolveLink. func (n *ProtoNode) Resolve(path []string) (interface{}, []string, error) { return n.ResolveLink(path) } +// ResolveLink consumes the first element of the path and obtains the link +// corresponding to it from the node. It returns the link +// and the path without the consumed element. func (n *ProtoNode) ResolveLink(path []string) (*ipld.Link, []string, error) { if len(path) == 0 { return nil, nil, fmt.Errorf("end of path, no more links to resolve") @@ -357,9 +377,10 @@ func (n *ProtoNode) ResolveLink(path []string) (*ipld.Link, []string, error) { return lnk, path[1:], nil } +// Tree returns the link names of the ProtoNode. +// ProtoNodes are only ever one path deep, so anything different than an empty +// string for p results in nothing. The depth parameter is ignored. func (n *ProtoNode) Tree(p string, depth int) []string { - // ProtoNodes are only ever one path deep, anything below that results in - // nothing if p != "" { return nil } diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 33f881550..f3fb4f762 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -9,6 +9,7 @@ import ( ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) +// RawNode represents a node which only contains data. type RawNode struct { blocks.Block } @@ -52,22 +53,27 @@ func NewRawNodeWPrefix(data []byte, prefix cid.Prefix) (*RawNode, error) { return &RawNode{blk}, nil } +// Links returns nil. func (rn *RawNode) Links() []*ipld.Link { return nil } +// ResolveLink returns an error. func (rn *RawNode) ResolveLink(path []string) (*ipld.Link, []string, error) { return nil, nil, ErrLinkNotFound } +// Resolve returns an error. func (rn *RawNode) Resolve(path []string) (interface{}, []string, error) { return nil, nil, ErrLinkNotFound } +// Tree returns nil. func (rn *RawNode) Tree(p string, depth int) []string { return nil } +// Copy performs a deep copy of this node and returns it as an ipld.Node func (rn *RawNode) Copy() ipld.Node { copybuf := make([]byte, len(rn.RawData())) copy(copybuf, rn.RawData()) @@ -80,10 +86,12 @@ func (rn *RawNode) Copy() ipld.Node { return &RawNode{nblk} } +// Size returns the size of this node func (rn *RawNode) Size() (uint64, error) { return uint64(len(rn.RawData())), nil } +// Stat returns some Stats about this node. func (rn *RawNode) Stat() (*ipld.NodeStat, error) { return &ipld.NodeStat{ CumulativeSize: len(rn.RawData()), diff --git a/ipld/merkledag/rwservice.go b/ipld/merkledag/rwservice.go index 1252d248e..eb0c19b1c 100644 --- a/ipld/merkledag/rwservice.go +++ b/ipld/merkledag/rwservice.go @@ -16,26 +16,32 @@ type ComboService struct { var _ ipld.DAGService = (*ComboService)(nil) +// Add writes a new node using the Write DAGService. func (cs *ComboService) Add(ctx context.Context, nd ipld.Node) error { return cs.Write.Add(ctx, nd) } +// AddMany adds nodes using the Write DAGService. func (cs *ComboService) AddMany(ctx context.Context, nds []ipld.Node) error { return cs.Write.AddMany(ctx, nds) } +// Get fetches a node using the Read DAGService. func (cs *ComboService) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { return cs.Read.Get(ctx, c) } +// GetMany fetches nodes using the Read DAGService. func (cs *ComboService) GetMany(ctx context.Context, cids []*cid.Cid) <-chan *ipld.NodeOption { return cs.Read.GetMany(ctx, cids) } +// Remove deletes a node using the Write DAGService. func (cs *ComboService) Remove(ctx context.Context, c *cid.Cid) error { return cs.Write.Remove(ctx, c) } +// RemoveMany deletes nodes using the Write DAGService. func (cs *ComboService) RemoveMany(ctx context.Context, cids []*cid.Cid) error { return cs.Write.RemoveMany(ctx, cids) } diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index c649cb02a..5561fe026 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -11,10 +11,14 @@ import ( // Order is an identifier for traversal algorithm orders type Order int +// These constants define different traversing methods const ( - DFSPre Order = iota // depth-first pre-order - DFSPost // depth-first post-order - BFS // breadth-first + // DFSPre defines depth-first pre-order + DFSPre Order = iota + // DFSPost defines depth-first post-order + DFSPost + // BFS defines breadth-first order + BFS ) // Options specifies a series of traversal options @@ -86,9 +90,9 @@ func (t *traversal) getNode(link *ipld.Link) (ipld.Node, error) { // If an error is returned, processing stops. type Func func(current State) error -// If there is a problem walking to the Node, and ErrFunc is provided, Traverse -// will call ErrFunc with the error encountered. ErrFunc can decide how to handle -// that error, and return an error back to Traversal with how to proceed: +// ErrFunc is provided to handle problems when walking to the Node. Traverse +// will call ErrFunc with the error encountered. ErrFunc can decide how to +// handle that error, and return an error back to Traversal with how to proceed: // * nil - skip the Node and its children, but continue processing // * all other errors halt processing immediately. // @@ -98,6 +102,8 @@ type Func func(current State) error // type ErrFunc func(err error) error +// Traverse initiates a DAG traversal with the given options starting at +// the given root. func Traverse(root ipld.Node, o Options) error { t := traversal{ opts: o, @@ -127,20 +133,14 @@ func dfsPreTraverse(state State, t *traversal) error { if err := t.callFunc(state); err != nil { return err } - if err := dfsDescend(dfsPreTraverse, state, t); err != nil { - return err - } - return nil + return dfsDescend(dfsPreTraverse, state, t) } func dfsPostTraverse(state State, t *traversal) error { if err := dfsDescend(dfsPostTraverse, state, t); err != nil { return err } - if err := t.callFunc(state); err != nil { - return err - } - return nil + return t.callFunc(state) } func dfsDescend(df dfsFunc, curr State, t *traversal) error { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 648e9b2c3..5af348d53 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -11,12 +11,15 @@ import ( ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) +// These constants define the changes that can be applied to a DAG. const ( Add = iota Remove Mod ) +// Change represents a change to a DAG and contains a reference to the old and +// new CIDs. type Change struct { Type int Path string @@ -24,6 +27,7 @@ type Change struct { After *cid.Cid } +// String prints a human-friendly line about a change. func (c *Change) String() string { switch c.Type { case Add: @@ -102,8 +106,8 @@ func Diff(ctx context.Context, ds ipld.DAGService, a, b ipld.Node) ([]*Change, e } var out []*Change - clean_a := a.Copy().(*dag.ProtoNode) - clean_b := b.Copy().(*dag.ProtoNode) + cleanA := a.Copy().(*dag.ProtoNode) + cleanB := b.Copy().(*dag.ProtoNode) // strip out unchanged stuff for _, lnk := range a.Links() { @@ -142,19 +146,19 @@ func Diff(ctx context.Context, ds ipld.DAGService, a, b ipld.Node) ([]*Change, e out = append(out, subc) } } - clean_a.RemoveNodeLink(l.Name) - clean_b.RemoveNodeLink(l.Name) + cleanA.RemoveNodeLink(l.Name) + cleanB.RemoveNodeLink(l.Name) } } - for _, lnk := range clean_a.Links() { + for _, lnk := range cleanA.Links() { out = append(out, &Change{ Type: Remove, Path: lnk.Name, Before: lnk.Cid, }) } - for _, lnk := range clean_b.Links() { + for _, lnk := range cleanB.Links() { out = append(out, &Change{ Type: Add, Path: lnk.Name, @@ -165,11 +169,17 @@ func Diff(ctx context.Context, ds ipld.DAGService, a, b ipld.Node) ([]*Change, e return out, nil } +// Conflict represents two incompatible changes and is returned by MergeDiffs(). type Conflict struct { A *Change B *Change } +// MergeDiffs takes two slice of changes and adds them to a single slice. +// When a Change from b happens to the same path of an existing change in a, +// a conflict is created and b is not added to the merged slice. +// A slice of Conflicts is returned and contains pointers to the +// Changes involved (which share the same path). func MergeDiffs(a, b []*Change) ([]*Change, []Conflict) { var out []*Change var conflicts []Conflict diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index ed8eec07c..2d08ff0da 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -15,6 +15,8 @@ import ( ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) +// Editor represents a ProtoNode tree editor and provides methods to +// modify it. type Editor struct { root *dag.ProtoNode @@ -83,6 +85,7 @@ func addLink(ctx context.Context, ds ipld.DAGService, root *dag.ProtoNode, child return root, nil } +// InsertNodeAtPath inserts a new node in the tree and replaces the current root with the new one. func (e *Editor) InsertNodeAtPath(ctx context.Context, pth string, toinsert ipld.Node, create func() *dag.ProtoNode) error { splpath := path.SplitList(pth) nd, err := e.insertNodeAtPath(ctx, e.root, splpath, toinsert, create) @@ -137,6 +140,8 @@ func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path return root, nil } +// RmLink removes the link with the given name and updates the root node of +// the editor. func (e *Editor) RmLink(ctx context.Context, pth string) error { splpath := path.SplitList(pth) nd, err := e.rmLink(ctx, e.root, splpath) From 0bce0165dc25f2ad0b196a7f0740566bfe4fcb65 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 6 Feb 2018 19:14:48 +0100 Subject: [PATCH 2172/3817] Golint: make BufDagReader public License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@7ab328df42d2f649cb294eb22fc940e32cf47601 --- unixfs/io/bufdagreader.go | 24 +++++++++++++++--------- unixfs/io/dagreader.go | 2 +- unixfs/io/pbdagreader.go | 6 +++--- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/unixfs/io/bufdagreader.go b/unixfs/io/bufdagreader.go index 8c6fd0cf8..6074099e8 100644 --- a/unixfs/io/bufdagreader.go +++ b/unixfs/io/bufdagreader.go @@ -6,27 +6,32 @@ import ( "io" ) -type bufDagReader struct { +// BufDagReader implements a DagReader that reads from a byte slice +// using a bytes.Reader. It is used for RawNodes. +type BufDagReader struct { *bytes.Reader } -// newBufDagReader returns a DAG reader for the given byte slice. +// NewBufDagReader returns a DAG reader for the given byte slice. // BufDagReader is used to read RawNodes. -func newBufDagReader(b []byte) *bufDagReader { - return &bufDagReader{bytes.NewReader(b)} +func NewBufDagReader(b []byte) *BufDagReader { + return &BufDagReader{bytes.NewReader(b)} } -var _ DagReader = (*bufDagReader)(nil) +var _ DagReader = (*BufDagReader)(nil) -func (*bufDagReader) Close() error { +// Close is a nop. +func (*BufDagReader) Close() error { return nil } -func (rd *bufDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { +// CtxReadFull reads the slice onto b. +func (rd *BufDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { return rd.Read(b) } -func (rd *bufDagReader) Offset() int64 { +// Offset returns the current offset. +func (rd *BufDagReader) Offset() int64 { of, err := rd.Seek(0, io.SeekCurrent) if err != nil { panic("this should never happen " + err.Error()) @@ -34,7 +39,8 @@ func (rd *bufDagReader) Offset() int64 { return of } -func (rd *bufDagReader) Size() uint64 { +// Size returns the size of the buffer. +func (rd *BufDagReader) Size() uint64 { s := rd.Reader.Size() if s < 0 { panic("size smaller than 0 (impossible!!)") diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 037e0a256..ec3b3e028 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -43,7 +43,7 @@ type ReadSeekCloser interface { func NewDagReader(ctx context.Context, n ipld.Node, serv ipld.NodeGetter) (DagReader, error) { switch n := n.(type) { case *mdag.RawNode: - return newBufDagReader(n.RawData()), nil + return NewBufDagReader(n.RawData()), nil case *mdag.ProtoNode: pb := new(ftpb.Data) if err := proto.Unmarshal(n.Data(), pb); err != nil { diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 4523481b3..ce3abf439 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -57,7 +57,7 @@ func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv return &PBDagReader{ node: n, serv: serv, - buf: newBufDagReader(pb.GetData()), + buf: NewBufDagReader(pb.GetData()), promises: make([]*ipld.NodePromise, len(curLinks)), links: curLinks, ctx: fctx, @@ -119,7 +119,7 @@ func (dr *PBDagReader) precalcNextBuf(ctx context.Context) error { dr.buf = NewPBFileReader(dr.ctx, nxt, pb, dr.serv) return nil case ftpb.Data_Raw: - dr.buf = newBufDagReader(pb.GetData()) + dr.buf = NewBufDagReader(pb.GetData()) return nil case ftpb.Data_Metadata: return errors.New("shouldnt have had metadata object inside file") @@ -256,7 +256,7 @@ func (dr *PBDagReader) Seek(offset int64, whence int) (int64, error) { if dr.buf != nil { dr.buf.Close() } - dr.buf = newBufDagReader(pb.GetData()[offset:]) + dr.buf = NewBufDagReader(pb.GetData()[offset:]) // start reading links from the beginning dr.linkPosition = 0 From 5ba88077e4a64897878c67888dedd1631df894c7 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 6 Feb 2018 19:17:59 +0100 Subject: [PATCH 2173/3817] Golint: improve io.Find documentation per @stebalien's comment License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@e917cad093860af7f0a7000d56ef0e87ee7b4707 --- unixfs/io/dirbuilder.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index f2dee053c..616617dee 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -162,9 +162,8 @@ func (d *Directory) Links(ctx context.Context) ([]*ipld.Link, error) { return d.shard.EnumLinks(ctx) } -// Find returns the ipld.Node with the given name, if it is contained in this -// directory. Find only searches in the most inmediate links, and not -// recursively in the tree. +// Find returns the root node of the file named 'name' within this directory. +// In the case of HAMT-directories, it will traverse the tree. func (d *Directory) Find(ctx context.Context, name string) (ipld.Node, error) { if d.shard == nil { lnk, err := d.dirnode.GetNodeLink(name) From a66b9f7f75961e2d6f26e3a45bd9b12856d92c75 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 6 Feb 2018 19:19:30 +0100 Subject: [PATCH 2174/3817] unixfs/mod: use errors.New() for all defined errors License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@e78def80a5acf767cf643aca493b27f8d6a56925 --- unixfs/mod/dagmodifier.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 05c5c3587..dfadd778b 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -6,7 +6,6 @@ import ( "bytes" "context" "errors" - "fmt" "io" chunk "github.com/ipfs/go-ipfs/importer/chunk" @@ -25,7 +24,7 @@ import ( var ( ErrSeekFail = errors.New("failed to seek properly") ErrUnrecognizedWhence = errors.New("unrecognized whence") - ErrNotUnixfs = fmt.Errorf("dagmodifier only supports unixfs nodes (proto or raw)") + ErrNotUnixfs = errors.New("dagmodifier only supports unixfs nodes (proto or raw)") ) // 2MB From 1ddcf7a9bd2ab30686f2e2d4a6d35484d2091bff Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 7 Feb 2018 00:35:35 +0100 Subject: [PATCH 2175/3817] Golint: merkledag: let NewDagService return *dagService License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@a7fc136a3f39f827468475457c9ae1d890f5c747 --- ipld/merkledag/merkledag.go | 2 +- ipld/merkledag/merkledag_test.go | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 77ceb5cec..197785d39 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -30,7 +30,7 @@ const progressContextKey contextKey = "progress" // NewDAGService constructs a new DAGService (using the default implementation). // Note that the default implementation is also an ipld.LinkGetter. -func NewDAGService(bs bserv.BlockService) ipld.DAGService { +func NewDAGService(bs bserv.BlockService) *dagService { return &dagService{Blocks: bs} } diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 2306d5c64..36e16c2fa 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -242,8 +242,7 @@ func TestFetchGraph(t *testing.T) { // create an offline dagstore and ensure all blocks were fetched bs := bserv.New(bsis[1].Blockstore(), offline.Exchange(bsis[1].Blockstore())) - // we know the default dagService implements LinkGetter - offlineDS := NewDAGService(bs).(ipld.LinkGetter) + offlineDS := NewDAGService(bs) err = EnumerateChildren(context.Background(), offlineDS.GetLinks, root.Cid(), func(_ *cid.Cid) bool { return true }) if err != nil { @@ -262,9 +261,8 @@ func TestEnumerateChildren(t *testing.T) { } set := cid.NewSet() - lg := ds.(ipld.LinkGetter) - err = EnumerateChildren(context.Background(), lg.GetLinks, root.Cid(), set.Visit) + err = EnumerateChildren(context.Background(), ds.GetLinks, root.Cid(), set.Visit) if err != nil { t.Fatal(err) } @@ -495,7 +493,7 @@ func TestCidRetention(t *testing.T) { } func TestCidRawDoesnNeedData(t *testing.T) { - srv := NewDAGService(dstest.Bserv()).(ipld.LinkGetter) + srv := NewDAGService(dstest.Bserv()) nd := NewRawNode([]byte("somedata")) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) From a21407ac2c08c50fb256098836233a04bd5d9356 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 7 Feb 2018 09:58:43 +0100 Subject: [PATCH 2176/3817] Golint: improve comments in merkledag/errservice.go Per @kevina's comments License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@10bb33f8c0578eaa34acff300de39688fee64977 --- ipld/merkledag/errservice.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/errservice.go b/ipld/merkledag/errservice.go index 8681cb6ee..499e54f59 100644 --- a/ipld/merkledag/errservice.go +++ b/ipld/merkledag/errservice.go @@ -14,34 +14,34 @@ type ErrorService struct { var _ ipld.DAGService = (*ErrorService)(nil) -// Add returns an error. +// Add returns the cs.Err. func (cs *ErrorService) Add(ctx context.Context, nd ipld.Node) error { return cs.Err } -// AddMany returns an error. +// AddMany returns the cs.Err. func (cs *ErrorService) AddMany(ctx context.Context, nds []ipld.Node) error { return cs.Err } -// Get returns an error. +// Get returns the cs.Err. func (cs *ErrorService) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { return nil, cs.Err } -// GetMany many returns an error. +// GetMany many returns the cs.Err. func (cs *ErrorService) GetMany(ctx context.Context, cids []*cid.Cid) <-chan *ipld.NodeOption { ch := make(chan *ipld.NodeOption) close(ch) return ch } -// Remove returns an error. +// Remove returns the cs.Err. func (cs *ErrorService) Remove(ctx context.Context, c *cid.Cid) error { return cs.Err } -// RemoveMany returns an error. +// RemoveMany returns the cs.Err. func (cs *ErrorService) RemoveMany(ctx context.Context, cids []*cid.Cid) error { return cs.Err } From 04864fe044c6dcca0d424b2233700bc43dc65bd0 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 7 Feb 2018 10:06:59 +0100 Subject: [PATCH 2177/3817] Golint: improve unixfs/dagreader.go comments Per @whys suggestions License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@8a5eb96c89986b3a2fb0253d80c580aca74e8789 --- unixfs/io/dagreader.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index ec3b3e028..53e990297 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -20,8 +20,8 @@ var ( ErrCantReadSymlinks = errors.New("cannot currently read symlinks") ) -// A DagReader represents a ReadSeekCloser which offers additional methods -// like Size. Different implementations of readers are used for the different +// // A DagReader provides read-only read and seek acess to a unixfs file. +// Different implementations of readers are used for the different // types of unixfs/protobuf-encoded nodes. type DagReader interface { ReadSeekCloser @@ -30,7 +30,7 @@ type DagReader interface { Offset() int64 } -// A ReadSeekCloser implements interfaces to read, write, seek and close. +// A ReadSeekCloser implements interfaces to read, copy, seek and close. type ReadSeekCloser interface { io.Reader io.Seeker From 840aeefeaba76e225948c5c70e97a544b1fba1ea Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 7 Feb 2018 12:33:58 +0100 Subject: [PATCH 2178/3817] Golint: remove extra // in comment License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@e8516ef5b8f65515923fe9550d2fa40e1f888ef1 --- unixfs/io/dagreader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 53e990297..af3fbb330 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -20,7 +20,7 @@ var ( ErrCantReadSymlinks = errors.New("cannot currently read symlinks") ) -// // A DagReader provides read-only read and seek acess to a unixfs file. +// A DagReader provides read-only read and seek acess to a unixfs file. // Different implementations of readers are used for the different // types of unixfs/protobuf-encoded nodes. type DagReader interface { From 2153cfc3cc1deca606d8b9cdb5cbc7ed7cd442d5 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 7 Feb 2018 12:44:20 +0100 Subject: [PATCH 2179/3817] Initial commit This commit was moved from ipfs/go-ipfs-posinfo@020018caceee2a6eba8745ada0de332e0346a962 --- filestore/posinfo/.gitignore | 14 ++++++++++++++ filestore/posinfo/LICENSE | 21 ++++++++++++++++++++ filestore/posinfo/Makefile | 18 ++++++++++++++++++ filestore/posinfo/README.md | 37 ++++++++++++++++++++++++++++++++++++ filestore/posinfo/posinfo.go | 23 ++++++++++++++++++++++ 5 files changed, 113 insertions(+) create mode 100644 filestore/posinfo/.gitignore create mode 100644 filestore/posinfo/LICENSE create mode 100644 filestore/posinfo/Makefile create mode 100644 filestore/posinfo/README.md create mode 100644 filestore/posinfo/posinfo.go diff --git a/filestore/posinfo/.gitignore b/filestore/posinfo/.gitignore new file mode 100644 index 000000000..a1338d685 --- /dev/null +++ b/filestore/posinfo/.gitignore @@ -0,0 +1,14 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ diff --git a/filestore/posinfo/LICENSE b/filestore/posinfo/LICENSE new file mode 100644 index 000000000..e4224df5b --- /dev/null +++ b/filestore/posinfo/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 IPFS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/filestore/posinfo/Makefile b/filestore/posinfo/Makefile new file mode 100644 index 000000000..24d71558e --- /dev/null +++ b/filestore/posinfo/Makefile @@ -0,0 +1,18 @@ +all: deps +gx: + go get github.com/whyrusleeping/gx + go get github.com/whyrusleeping/gx-go +deps: gx + gx --verbose install --global + gx-go rewrite +test: deps + go test -v -covermode count -coverprofile=coverage.out . +rw: + gx-go rewrite +rwundo: + gx-go rewrite --undo +publish: rwundo + gx publish +.PHONY: all gx deps test rw rwundo publish + + diff --git a/filestore/posinfo/README.md b/filestore/posinfo/README.md new file mode 100644 index 000000000..bd509c17e --- /dev/null +++ b/filestore/posinfo/README.md @@ -0,0 +1,37 @@ +# go-ipfs-posinfo + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![GoDoc](https://godoc.org/github.com/ipfs/go-ipfs-posinfo?status.svg)](https://godoc.org/github.com/ipfs/go-ipfs-posinfo) +[![Build Status](https://travis-ci.org/ipfs/go-ipfs-posinfo.svg?branch=master)](https://travis-ci.org/ipfs/go-ipfs-posinfo) + +> Posinfo wraps offset information for ipfs filestore nodes + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Contribute](#contribute) +- [License](#license) + +## Install + +``` +go get github.com/ipfs/go-ipfs-posinfo +``` + +## Usage + +See the [GoDoc documentation](https://godoc.org/github.com/ipfs/go-ipfs-posinfo) + + +## Contribute + +PRs accepted. + +Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +MIT © Protocol Labs, Inc. diff --git a/filestore/posinfo/posinfo.go b/filestore/posinfo/posinfo.go new file mode 100644 index 000000000..0b32c89da --- /dev/null +++ b/filestore/posinfo/posinfo.go @@ -0,0 +1,23 @@ +// Package posinfo wraps offset information used by ipfs filestore nodes +package posinfo + +import ( + "os" + + ipld "github.com/ipfs/go-ipld-format" +) + +// PosInfo stores information about the file offset, its path and +// stat. +type PosInfo struct { + Offset uint64 + FullPath string + Stat os.FileInfo // can be nil +} + +// FilestoreNode is an ipld.Node which arries PosInfo with it +// allowing to map it directly to a filesystem object. +type FilestoreNode struct { + ipld.Node + PosInfo *PosInfo +} From 1a31952b03e7d3754927c48bc7d1053f4d4a8946 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 7 Feb 2018 13:37:27 +0100 Subject: [PATCH 2180/3817] Extract posinfo package to github.com/ipfs/go-ipfs-posinfo License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-filestore@1ac2b1405dc5b905a6b3ebd598dcca36f0c52f8d --- filestore/filestore.go | 2 +- filestore/filestore_test.go | 2 +- filestore/fsrefstore.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 534d3f26d..e7215dfcc 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,7 +11,7 @@ import ( "context" "github.com/ipfs/go-ipfs/blocks/blockstore" - posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" + posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 802111424..883df0d76 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -9,7 +9,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" dag "github.com/ipfs/go-ipfs/merkledag" - posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" + posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 603a51380..8e048fd69 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,7 +10,7 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" pb "github.com/ipfs/go-ipfs/filestore/pb" dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - posinfo "github.com/ipfs/go-ipfs/thirdparty/posinfo" + posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" From c38e5844923b4a64ccb93e223edaf0f2e27438f9 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 7 Feb 2018 19:27:45 +0100 Subject: [PATCH 2181/3817] golint: merkledag_test: reorder imports License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@3dfccfd841ae6d6d167b2da062dbb2809f290260 --- ipld/merkledag/merkledag_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 36e16c2fa..e0b9d8b80 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -13,8 +13,6 @@ import ( "testing" "time" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - bserv "github.com/ipfs/go-ipfs/blockservice" bstest "github.com/ipfs/go-ipfs/blockservice/test" offline "github.com/ipfs/go-ipfs/exchange/offline" @@ -28,6 +26,7 @@ import ( u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) func TestNode(t *testing.T) { From e86426aeebcc3d96b0128afa0201fa162a79e135 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Tue, 30 Jan 2018 22:32:12 -0500 Subject: [PATCH 2182/3817] namesys: verify signature in ipns validator License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@c7c1d92751b62f347bf3b697ece4b500a0d7f817 --- namesys/ipns_validate_test.go | 199 ++++++++++++++++++++++++---------- namesys/publisher.go | 124 +-------------------- namesys/routing.go | 78 +++++-------- namesys/selector.go | 65 +++++++++++ namesys/validator.go | 86 +++++++++++++++ 5 files changed, 320 insertions(+), 232 deletions(-) create mode 100644 namesys/selector.go create mode 100644 namesys/validator.go diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index 430381cbe..cb7d80954 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -1,134 +1,215 @@ package namesys import ( - "io" + "context" "testing" "time" path "github.com/ipfs/go-ipfs/path" + mockrouting "github.com/ipfs/go-ipfs/routing/mock" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" + recordpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" + testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" + pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) func TestValidation(t *testing.T) { - // Create a record validator - validator := make(record.Validator) - validator["ipns"] = &record.ValidChecker{Func: ValidateIpnsRecord, Sign: true} + ctx := context.Background() + rid := testutil.RandIdentityOrFatal(t) + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + peerstore := pstore.NewPeerstore() - // Generate a key for signing the records - r := u.NewSeededRand(15) // generate deterministic keypair - priv, ipnsPath := genKeys(t, r) + vstore := newMockValueStore(rid, dstore, peerstore) + vstore.Validator["ipns"] = NewIpnsRecordValidator(peerstore) + vstore.Validator["pk"] = &record.ValidChecker{ + Func: func(r *record.ValidationRecord) error { + return nil + }, + Sign: false, + } + resolver := NewRoutingResolver(vstore, 0) // Create entry with expiry in one hour + priv, id, _, ipnsDHTPath := genKeys(t) ts := time.Now() - entry, err := CreateRoutingEntryData(priv, path.Path("foo"), 1, ts.Add(time.Hour)) + p := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") + entry, err := CreateRoutingEntryData(priv, p, 1, ts.Add(time.Hour)) if err != nil { t.Fatal(err) } - val, err := proto.Marshal(entry) + // Make peer's public key available in peer store + err = peerstore.AddPubKey(id, priv.GetPublic()) if err != nil { t.Fatal(err) } - // Create the record - rec, err := record.MakePutRecord(priv, ipnsPath, val, true) + // Publish entry + err = PublishEntry(ctx, vstore, ipnsDHTPath, entry) if err != nil { t.Fatal(err) } - // Validate the record - err = validator.VerifyRecord(rec) + // Resolve entry + resp, err := resolver.resolveOnce(ctx, id.Pretty()) if err != nil { t.Fatal(err) } + if resp != p { + t.Fatal("Mismatch between published path %s and resolved path %s", p, resp) + } - /* TODO(#4613) - // Create IPNS record path with a different private key - _, ipnsWrongAuthor := genKeys(t, r) - wrongAuthorRec, err := record.MakePutRecord(priv, ipnsWrongAuthor, val, true) + // Create expired entry + expiredEntry, err := CreateRoutingEntryData(priv, p, 1, ts.Add(-1*time.Hour)) if err != nil { t.Fatal(err) } - // Record should fail validation because path doesn't match author - err = validator.VerifyRecord(wrongAuthorRec) - if err != ErrInvalidAuthor { - t.Fatal("ValidateIpnsRecord should have returned ErrInvalidAuthor") + // Publish entry + err = PublishEntry(ctx, vstore, ipnsDHTPath, expiredEntry) + if err != nil { + t.Fatal(err) } - // Create IPNS record path with extra path components after author - extraPath := ipnsPath + "/some/path" - extraPathRec, err := record.MakePutRecord(priv, extraPath, val, true) + // Record should fail validation because entry is expired + _, err = resolver.resolveOnce(ctx, id.Pretty()) + if err != ErrExpiredRecord { + t.Fatal("ValidateIpnsRecord should have returned ErrExpiredRecord") + } + + // Create IPNS record path with a different private key + priv2, id2, _, ipnsDHTPath2 := genKeys(t) + + // Make peer's public key available in peer store + err = peerstore.AddPubKey(id2, priv2.GetPublic()) + if err != nil { + t.Fatal(err) + } + + // Publish entry + err = PublishEntry(ctx, vstore, ipnsDHTPath2, entry) if err != nil { t.Fatal(err) } - // Record should fail validation because path has extra components after author - err = validator.VerifyRecord(extraPathRec) - if err != ErrInvalidAuthor { - t.Fatal("ValidateIpnsRecord should have returned ErrInvalidAuthor") + // Record should fail validation because public key defined by + // ipns path doesn't match record signature + _, err = resolver.resolveOnce(ctx, id2.Pretty()) + if err != ErrSignature { + t.Fatal("ValidateIpnsRecord should have failed signature verification") } - // Create unsigned IPNS record - unsignedRec, err := record.MakePutRecord(priv, ipnsPath, val, false) + // Publish entry without making public key available in peer store + priv3, id3, pubkDHTPath3, ipnsDHTPath3 := genKeys(t) + entry3, err := CreateRoutingEntryData(priv3, p, 1, ts.Add(time.Hour)) + if err != nil { + t.Fatal(err) + } + err = PublishEntry(ctx, vstore, ipnsDHTPath3, entry3) if err != nil { t.Fatal(err) } - // Record should fail validation because IPNS records require signature - err = validator.VerifyRecord(unsignedRec) - if err != ErrInvalidAuthor { - t.Fatal("ValidateIpnsRecord should have returned ErrInvalidAuthor") + // Record should fail validation because public key is not available + // in peer store or on network + _, err = resolver.resolveOnce(ctx, id3.Pretty()) + if err == nil { + t.Fatal("ValidateIpnsRecord should have failed because public key was not found") } - // Create unsigned IPNS record with no author - unsignedRecNoAuthor, err := record.MakePutRecord(priv, ipnsPath, val, false) + // Publish public key to the network + err = PublishPublicKey(ctx, vstore, pubkDHTPath3, priv3.GetPublic()) if err != nil { t.Fatal(err) } - noAuth := "" - unsignedRecNoAuthor.Author = &noAuth - // Record should fail validation because IPNS records require author - err = validator.VerifyRecord(unsignedRecNoAuthor) - if err != ErrInvalidAuthor { - t.Fatal("ValidateIpnsRecord should have returned ErrInvalidAuthor") + // Record should now pass validation because resolver will ensure + // public key is available in the peer store by looking it up in + // the DHT, which causes the DHT to fetch it and cache it in the + // peer store + _, err = resolver.resolveOnce(ctx, id3.Pretty()) + if err != nil { + t.Fatal(err) } - */ +} - // Create expired entry - expiredEntry, err := CreateRoutingEntryData(priv, path.Path("foo"), 1, ts.Add(-1*time.Hour)) +func genKeys(t *testing.T) (ci.PrivKey, peer.ID, string, string) { + sr := u.NewTimeSeededRand() + priv, _, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, sr) if err != nil { t.Fatal(err) } - valExp, err := proto.Marshal(expiredEntry) + + // Create entry with expiry in one hour + pid, err := peer.IDFromPrivateKey(priv) if err != nil { t.Fatal(err) } + pubkDHTPath, ipnsDHTPath := IpnsKeysForID(pid) - // Create record with the expired entry - expiredRec, err := record.MakePutRecord(priv, ipnsPath, valExp, true) + return priv, pid, pubkDHTPath, ipnsDHTPath +} - // Record should fail validation because entry is expired - err = validator.VerifyRecord(expiredRec) - if err != ErrExpiredRecord { - t.Fatal("ValidateIpnsRecord should have returned ErrExpiredRecord") +type mockValueStore struct { + r routing.ValueStore + kbook pstore.KeyBook + Validator record.Validator +} + +func newMockValueStore(id testutil.Identity, dstore ds.Datastore, kbook pstore.KeyBook) *mockValueStore { + serv := mockrouting.NewServer() + r := serv.ClientWithDatastore(context.Background(), id, dstore) + return &mockValueStore{r, kbook, make(record.Validator)} +} + +func (m *mockValueStore) GetValue(ctx context.Context, k string) ([]byte, error) { + data, err := m.r.GetValue(ctx, k) + if err != nil { + return data, err + } + + rec := new(recordpb.Record) + rec.Key = proto.String(k) + rec.Value = data + if err = m.Validator.VerifyRecord(rec); err != nil { + return nil, err } + + return data, err } -func genKeys(t *testing.T, r io.Reader) (ci.PrivKey, string) { - priv, _, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, r) +func (m *mockValueStore) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey, error) { + pk := m.kbook.PubKey(p) + if pk != nil { + return pk, nil + } + + pkkey := routing.KeyForPublicKey(p) + val, err := m.GetValue(ctx, pkkey) if err != nil { - t.Fatal(err) + return nil, err } - id, err := peer.IDFromPrivateKey(priv) + + pk, err = ci.UnmarshalPublicKey(val) if err != nil { - t.Fatal(err) + return nil, err } - _, ipnsKey := IpnsKeysForID(id) - return priv, ipnsKey + + return pk, m.kbook.AddPubKey(p, pk) +} + +func (m *mockValueStore) GetValues(ctx context.Context, k string, count int) ([]routing.RecvdVal, error) { + return m.r.GetValues(ctx, k, count) +} + +func (m *mockValueStore) PutValue(ctx context.Context, k string, d []byte) error { + return m.r.PutValue(ctx, k, d) } diff --git a/namesys/publisher.go b/namesys/publisher.go index c7159036a..79e3168e8 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -3,7 +3,6 @@ package namesys import ( "bytes" "context" - "errors" "fmt" "time" @@ -16,25 +15,12 @@ import ( u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" - record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" dhtpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) -// ErrExpiredRecord should be returned when an ipns record is -// invalid due to being too old -var ErrExpiredRecord = errors.New("expired record") - -// ErrUnrecognizedValidity is returned when an IpnsRecord has an -// unknown validity type. -var ErrUnrecognizedValidity = errors.New("unrecognized validity type") - -// ErrInvalidPath should be returned when an ipns record path -// is not in a valid format -var ErrInvalidPath = errors.New("record path invalid") - const PublishPutValTimeout = time.Minute const DefaultRecordTTL = 24 * time.Hour @@ -208,7 +194,7 @@ func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec } log.Debugf("Storing ipns entry at: %s", ipnskey) - // Store ipns entry at "/ipns/"+b58(h(pubkey)) + // Store ipns entry at "/ipns/"+h(pubkey) return r.PutValue(timectx, ipnskey, data) } @@ -238,114 +224,6 @@ func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte { []byte{}) } -var IpnsRecordValidator = &record.ValidChecker{ - Func: ValidateIpnsRecord, - Sign: true, -} - -func IpnsSelectorFunc(k string, vals [][]byte) (int, error) { - var recs []*pb.IpnsEntry - for _, v := range vals { - e := new(pb.IpnsEntry) - err := proto.Unmarshal(v, e) - if err == nil { - recs = append(recs, e) - } else { - recs = append(recs, nil) - } - } - - return selectRecord(recs, vals) -} - -func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) { - var best_seq uint64 - best_i := -1 - - for i, r := range recs { - if r == nil || r.GetSequence() < best_seq { - continue - } - - if best_i == -1 || r.GetSequence() > best_seq { - best_seq = r.GetSequence() - best_i = i - } else if r.GetSequence() == best_seq { - rt, err := u.ParseRFC3339(string(r.GetValidity())) - if err != nil { - continue - } - - bestt, err := u.ParseRFC3339(string(recs[best_i].GetValidity())) - if err != nil { - continue - } - - if rt.After(bestt) { - best_i = i - } else if rt == bestt { - if bytes.Compare(vals[i], vals[best_i]) > 0 { - best_i = i - } - } - } - } - if best_i == -1 { - return 0, errors.New("no usable records in given set") - } - - return best_i, nil -} - -// ValidateIpnsRecord implements ValidatorFunc and verifies that the -// given 'val' is an IpnsEntry and that that entry is valid. -func ValidateIpnsRecord(r *record.ValidationRecord) error { - if r.Namespace != "ipns" { - return ErrInvalidPath - } - - entry := new(pb.IpnsEntry) - err := proto.Unmarshal(r.Value, entry) - if err != nil { - return err - } - - // NOTE/FIXME(#4613): We're not checking the DHT signature/author here. - // We're going to remove them in a followup commit and then check the - // *IPNS* signature. However, to do that, we need to ensure we *have* - // the public key and: - // - // 1. Don't want to fetch it from the network when handling PUTs. - // 2. Do want to fetch it from the network when handling GETs. - // - // Therefore, we'll need to either: - // - // 1. Pass some for of offline hint to the validator (e.g., using a context). - // 2. Ensure we pre-fetch the key when performing gets. - // - // This PR is already *way* too large so we're punting that fix to a new - // PR. - // - // This is not a regression, it just restores the current (bad) - // behavior. - - // Check that record has not expired - switch entry.GetValidityType() { - case pb.IpnsEntry_EOL: - t, err := u.ParseRFC3339(string(entry.GetValidity())) - if err != nil { - log.Debug("failed parsing time for ipns record EOL") - return err - } - if time.Now().After(t) { - return ErrExpiredRecord - } - default: - return ErrUnrecognizedValidity - } - return nil -} - // InitializeKeyspace sets the ipns record for the given key to // point to an empty directory. // TODO: this doesnt feel like it belongs here diff --git a/namesys/routing.go b/namesys/routing.go index 4ec68e7b6..0c17ca4ff 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -2,7 +2,6 @@ package namesys import ( "context" - "fmt" "strings" "time" @@ -15,7 +14,7 @@ import ( lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" - ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) @@ -131,58 +130,37 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa return "", err } - // use the routing system to get the name. - // /ipns/ - h := []byte("/ipns/" + string(hash)) - - var entry *pb.IpnsEntry - var pubkey ci.PubKey - - resp := make(chan error, 2) - go func() { - ipnsKey := string(h) - val, err := r.routing.GetValue(ctx, ipnsKey) - if err != nil { - log.Debugf("RoutingResolver: dht get failed: %s", err) - resp <- err - return - } - - entry = new(pb.IpnsEntry) - err = proto.Unmarshal(val, entry) - if err != nil { - resp <- err - return - } - - resp <- nil - }() - - go func() { - // name should be a public key retrievable from ipfs - pubk, err := routing.GetPublicKey(r.routing, ctx, hash) - if err != nil { - resp <- err - return - } - - pubkey = pubk - resp <- nil - }() + // Name should be the hash of a public key retrievable from ipfs. + // We retrieve the public key here to make certain that it's in the peer + // store before calling GetValue() on the DHT - the DHT will call the + // ipns validator, which in turn will get the public key from the peer + // store to verify the record signature + _, err = routing.GetPublicKey(r.routing, ctx, hash) + if err != nil { + log.Debugf("RoutingResolver: could not retrieve public key %s: %s\n", name, err) + return "", err + } - for i := 0; i < 2; i++ { - err = <-resp - if err != nil { - return "", err - } + pid, err := peer.IDFromBytes(hash) + if err != nil { + log.Debugf("RoutingResolver: could not convert public key hash %s to peer ID: %s\n", name, err) + return "", err } - // check sig with pk - if ok, err := pubkey.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok { - return "", fmt.Errorf("ipns entry for %s has invalid signature", h) + // use the routing system to get the name. + _, ipnsKey := IpnsKeysForID(pid) + val, err := r.routing.GetValue(ctx, ipnsKey) + if err != nil { + log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err) + return "", err } - // ok sig checks out. this is a valid name. + entry := new(pb.IpnsEntry) + err = proto.Unmarshal(val, entry) + if err != nil { + log.Debugf("RoutingResolver: could not unmarshal value for name %s: %s", name, err) + return "", err + } // check for old style record: valh, err := mh.Cast(entry.GetValue()) @@ -197,7 +175,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa return p, nil } else { // Its an old style multihash record - log.Debugf("encountered CIDv0 ipns entry: %s", h) + log.Debugf("encountered CIDv0 ipns entry: %s", valh) p := path.FromCid(cid.NewCidV0(valh)) r.cacheSet(name, p, entry) return p, nil diff --git a/namesys/selector.go b/namesys/selector.go new file mode 100644 index 000000000..6114bfcce --- /dev/null +++ b/namesys/selector.go @@ -0,0 +1,65 @@ +package namesys + +import ( + "bytes" + "errors" + + pb "github.com/ipfs/go-ipfs/namesys/pb" + + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" +) + +func IpnsSelectorFunc(k string, vals [][]byte) (int, error) { + var recs []*pb.IpnsEntry + for _, v := range vals { + e := new(pb.IpnsEntry) + err := proto.Unmarshal(v, e) + if err == nil { + recs = append(recs, e) + } else { + recs = append(recs, nil) + } + } + + return selectRecord(recs, vals) +} + +func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) { + var best_seq uint64 + best_i := -1 + + for i, r := range recs { + if r == nil || r.GetSequence() < best_seq { + continue + } + + if best_i == -1 || r.GetSequence() > best_seq { + best_seq = r.GetSequence() + best_i = i + } else if r.GetSequence() == best_seq { + rt, err := u.ParseRFC3339(string(r.GetValidity())) + if err != nil { + continue + } + + bestt, err := u.ParseRFC3339(string(recs[best_i].GetValidity())) + if err != nil { + continue + } + + if rt.After(bestt) { + best_i = i + } else if rt == bestt { + if bytes.Compare(vals[i], vals[best_i]) > 0 { + best_i = i + } + } + } + } + if best_i == -1 { + return 0, errors.New("no usable records in given set") + } + + return best_i, nil +} diff --git a/namesys/validator.go b/namesys/validator.go new file mode 100644 index 000000000..3eebce7f9 --- /dev/null +++ b/namesys/validator.go @@ -0,0 +1,86 @@ +package namesys + +import ( + "errors" + "time" + + pb "github.com/ipfs/go-ipfs/namesys/pb" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" + pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" + + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" +) + +// ErrExpiredRecord should be returned when an ipns record is +// invalid due to being too old +var ErrExpiredRecord = errors.New("expired record") + +// ErrUnrecognizedValidity is returned when an IpnsRecord has an +// unknown validity type. +var ErrUnrecognizedValidity = errors.New("unrecognized validity type") + +// ErrInvalidPath should be returned when an ipns record path +// is not in a valid format +var ErrInvalidPath = errors.New("record path invalid") + +// ErrSignature should be returned when an ipns record fails +// signature verification +var ErrSignature = errors.New("record signature verification failed") + +func NewIpnsRecordValidator(kbook pstore.KeyBook) *record.ValidChecker { + // ValidateIpnsRecord implements ValidatorFunc and verifies that the + // given 'val' is an IpnsEntry and that that entry is valid. + ValidateIpnsRecord := func(r *record.ValidationRecord) error { + if r.Namespace != "ipns" { + return ErrInvalidPath + } + + // Parse the value into an IpnsEntry + entry := new(pb.IpnsEntry) + err := proto.Unmarshal(r.Value, entry) + if err != nil { + return err + } + + // Get the public key defined by the ipns path + pid, err := peer.IDFromString(r.Key) + if err != nil { + log.Debugf("failed to parse ipns record key %s into public key hash", r.Key) + return ErrSignature + } + pubk := kbook.PubKey(pid) + if pubk == nil { + log.Debugf("public key with hash %s not found in peer store", pid) + return ErrSignature + } + + // Check the ipns record signature with the public key + if ok, err := pubk.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok { + log.Debugf("failed to verify signature for ipns record %s", r.Key) + return ErrSignature + } + + // Check that record has not expired + switch entry.GetValidityType() { + case pb.IpnsEntry_EOL: + t, err := u.ParseRFC3339(string(entry.GetValidity())) + if err != nil { + log.Debugf("failed parsing time for ipns record EOL in record %s", r.Key) + return err + } + if time.Now().After(t) { + return ErrExpiredRecord + } + default: + return ErrUnrecognizedValidity + } + return nil + } + + return &record.ValidChecker{ + Func: ValidateIpnsRecord, + Sign: false, + } +} From 3d828960f86d43234fc54e04f0ff560ca3831787 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Tue, 30 Jan 2018 22:44:46 -0500 Subject: [PATCH 2183/3817] Code cleanup License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@aafdee05539091a5b32ea9f8500204fde63d7106 --- namesys/ipns_validate_test.go | 2 +- namesys/selector.go | 28 +++++++++++++++------------- namesys/validator.go | 3 +++ 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index cb7d80954..b5df33a00 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -64,7 +64,7 @@ func TestValidation(t *testing.T) { t.Fatal(err) } if resp != p { - t.Fatal("Mismatch between published path %s and resolved path %s", p, resp) + t.Fatalf("Mismatch between published path %s and resolved path %s", p, resp) } // Create expired entry diff --git a/namesys/selector.go b/namesys/selector.go index 6114bfcce..c93ddce89 100644 --- a/namesys/selector.go +++ b/namesys/selector.go @@ -10,6 +10,8 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) +// Selects best record by checking which has the highest sequence number +// and latest EOL func IpnsSelectorFunc(k string, vals [][]byte) (int, error) { var recs []*pb.IpnsEntry for _, v := range vals { @@ -26,40 +28,40 @@ func IpnsSelectorFunc(k string, vals [][]byte) (int, error) { } func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) { - var best_seq uint64 - best_i := -1 + var bestSeq uint64 + besti := -1 for i, r := range recs { - if r == nil || r.GetSequence() < best_seq { + if r == nil || r.GetSequence() < bestSeq { continue } - if best_i == -1 || r.GetSequence() > best_seq { - best_seq = r.GetSequence() - best_i = i - } else if r.GetSequence() == best_seq { + if besti == -1 || r.GetSequence() > bestSeq { + bestSeq = r.GetSequence() + besti = i + } else if r.GetSequence() == bestSeq { rt, err := u.ParseRFC3339(string(r.GetValidity())) if err != nil { continue } - bestt, err := u.ParseRFC3339(string(recs[best_i].GetValidity())) + bestt, err := u.ParseRFC3339(string(recs[besti].GetValidity())) if err != nil { continue } if rt.After(bestt) { - best_i = i + besti = i } else if rt == bestt { - if bytes.Compare(vals[i], vals[best_i]) > 0 { - best_i = i + if bytes.Compare(vals[i], vals[besti]) > 0 { + besti = i } } } } - if best_i == -1 { + if besti == -1 { return 0, errors.New("no usable records in given set") } - return best_i, nil + return besti, nil } diff --git a/namesys/validator.go b/namesys/validator.go index 3eebce7f9..57924535a 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -29,6 +29,9 @@ var ErrInvalidPath = errors.New("record path invalid") // signature verification var ErrSignature = errors.New("record signature verification failed") +// Returns a ValidChecker for IPNS records +// The validator function will get a public key from the KeyBook +// to verify the record's signature func NewIpnsRecordValidator(kbook pstore.KeyBook) *record.ValidChecker { // ValidateIpnsRecord implements ValidatorFunc and verifies that the // given 'val' is an IpnsEntry and that that entry is valid. From 60e4d0fdd0e1d4d0c3da86fb0a9d374dc2addb9e Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Tue, 30 Jan 2018 22:47:16 -0500 Subject: [PATCH 2184/3817] Comment fixes License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@12d98f64de9c7d27f0916df55c331006a2c8a99f --- namesys/selector.go | 4 ++-- namesys/validator.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/namesys/selector.go b/namesys/selector.go index c93ddce89..296d34830 100644 --- a/namesys/selector.go +++ b/namesys/selector.go @@ -10,8 +10,8 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) -// Selects best record by checking which has the highest sequence number -// and latest EOL +// IpnsSelectorFunc selects the best record by checking which has the highest +// sequence number and latest EOL func IpnsSelectorFunc(k string, vals [][]byte) (int, error) { var recs []*pb.IpnsEntry for _, v := range vals { diff --git a/namesys/validator.go b/namesys/validator.go index 57924535a..1466eac87 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -29,7 +29,7 @@ var ErrInvalidPath = errors.New("record path invalid") // signature verification var ErrSignature = errors.New("record signature verification failed") -// Returns a ValidChecker for IPNS records +// NewIpnsRecordValidator returns a ValidChecker for IPNS records // The validator function will get a public key from the KeyBook // to verify the record's signature func NewIpnsRecordValidator(kbook pstore.KeyBook) *record.ValidChecker { From c27e283932a82771e6cae37abfedd9ad96e03547 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 31 Jan 2018 00:37:39 -0500 Subject: [PATCH 2185/3817] namesys: differentiate between validation errors License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@b75e29b472765df6f72839db48fe3aac23934464 --- namesys/ipns_validate_test.go | 58 ++++++++++++++++++++++++++++++++++- namesys/routing.go | 4 ++- namesys/selector.go | 2 ++ namesys/validator.go | 20 +++++++++--- 4 files changed, 78 insertions(+), 6 deletions(-) diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index b5df33a00..262b0711d 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -2,6 +2,7 @@ package namesys import ( "context" + "fmt" "testing" "time" @@ -21,7 +22,62 @@ import ( ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) -func TestValidation(t *testing.T) { +func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, ns string, key string, val []byte, eol time.Time, exp error) { + validChecker := NewIpnsRecordValidator(kbook) + + p := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") + entry, err := CreateRoutingEntryData(priv, p, 1, eol) + if err != nil { + t.Fatal(err) + } + + data := val + if data == nil { + data, err = proto.Marshal(entry) + if err != nil { + t.Fatal(err) + } + } + rec := &record.ValidationRecord{ + Namespace: ns, + Key: key, + Value: data, + } + + err = validChecker.Func(rec) + if err != exp { + params := fmt.Sprintf("namespace: %s\nkey: %s\neol: %s\n", ns, key, eol) + if exp == nil { + t.Fatalf("Unexpected error %s for params %s", err, params) + } else if err == nil { + t.Fatalf("Expected error %s but there was no error for params %s", exp, params) + } else { + t.Fatalf("Expected error %s but got %s for params %s", exp, err, params) + } + } +} + +func TestValidator(t *testing.T) { + ts := time.Now() + + priv, id, _, _ := genKeys(t) + priv2, id2, _, _ := genKeys(t) + kbook := pstore.NewPeerstore() + kbook.AddPubKey(id, priv.GetPublic()) + emptyKbook := pstore.NewPeerstore() + + testValidatorCase(t, priv, kbook, "ipns", string(id), nil, ts.Add(time.Hour), nil) + testValidatorCase(t, priv, kbook, "ipns", string(id), nil, ts.Add(time.Hour*-1), ErrExpiredRecord) + testValidatorCase(t, priv, kbook, "ipns", string(id), []byte("bad data"), ts.Add(time.Hour), ErrBadRecord) + testValidatorCase(t, priv, kbook, "ipns", "bad key", nil, ts.Add(time.Hour), ErrKeyFormat) + testValidatorCase(t, priv, emptyKbook, "ipns", string(id), nil, ts.Add(time.Hour), ErrPublicKeyNotFound) + testValidatorCase(t, priv2, kbook, "ipns", string(id2), nil, ts.Add(time.Hour), ErrPublicKeyNotFound) + testValidatorCase(t, priv2, kbook, "ipns", string(id), nil, ts.Add(time.Hour), ErrSignature) + testValidatorCase(t, priv, kbook, "", string(id), nil, ts.Add(time.Hour), ErrInvalidPath) + testValidatorCase(t, priv, kbook, "wrong", string(id), nil, ts.Add(time.Hour), ErrInvalidPath) +} + +func TestResolverValidation(t *testing.T) { ctx := context.Background() rid := testutil.RandIdentityOrFatal(t) dstore := dssync.MutexWrap(ds.NewMapDatastore()) diff --git a/namesys/routing.go b/namesys/routing.go index 0c17ca4ff..120fabd66 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -147,7 +147,9 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa return "", err } - // use the routing system to get the name. + // Use the routing system to get the name. + // Note that the DHT will call the ipns validator when retrieving + // the value, which in turn verifies the ipns record signature _, ipnsKey := IpnsKeysForID(pid) val, err := r.routing.GetValue(ctx, ipnsKey) if err != nil { diff --git a/namesys/selector.go b/namesys/selector.go index 296d34830..53c712d1c 100644 --- a/namesys/selector.go +++ b/namesys/selector.go @@ -42,11 +42,13 @@ func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) { } else if r.GetSequence() == bestSeq { rt, err := u.ParseRFC3339(string(r.GetValidity())) if err != nil { + log.Errorf("failed to parse ipns record EOL %s", r.GetValidity()) continue } bestt, err := u.ParseRFC3339(string(recs[besti].GetValidity())) if err != nil { + log.Errorf("failed to parse ipns record EOL %s", recs[besti].GetValidity()) continue } diff --git a/namesys/validator.go b/namesys/validator.go index 1466eac87..e9cefc562 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -29,6 +29,18 @@ var ErrInvalidPath = errors.New("record path invalid") // signature verification var ErrSignature = errors.New("record signature verification failed") +// ErrBadRecord should be returned when an ipns record cannot be unmarshalled +var ErrBadRecord = errors.New("record could not be unmarshalled") + +// ErrKeyFormat should be returned when an ipns record key is +// incorrectly formatted (not a peer ID) +var ErrKeyFormat = errors.New("record key could not be parsed into peer ID") + +// ErrPublicKeyNotFound should be returned when the public key +// corresponding to the ipns record path cannot be retrieved +// from the peer store +var ErrPublicKeyNotFound = errors.New("public key not found in peer store") + // NewIpnsRecordValidator returns a ValidChecker for IPNS records // The validator function will get a public key from the KeyBook // to verify the record's signature @@ -44,19 +56,19 @@ func NewIpnsRecordValidator(kbook pstore.KeyBook) *record.ValidChecker { entry := new(pb.IpnsEntry) err := proto.Unmarshal(r.Value, entry) if err != nil { - return err + return ErrBadRecord } // Get the public key defined by the ipns path pid, err := peer.IDFromString(r.Key) if err != nil { - log.Debugf("failed to parse ipns record key %s into public key hash", r.Key) - return ErrSignature + log.Debugf("failed to parse ipns record key %s into peer ID", r.Key) + return ErrKeyFormat } pubk := kbook.PubKey(pid) if pubk == nil { log.Debugf("public key with hash %s not found in peer store", pid) - return ErrSignature + return ErrPublicKeyNotFound } // Check the ipns record signature with the public key From fe5fdb7e50e3f7b6e3fb486baae413727f182ecf Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 31 Jan 2018 10:08:22 -0500 Subject: [PATCH 2186/3817] namesys: more comments License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@e03522f670b021444da94567a9acac0329987336 --- namesys/validator.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/namesys/validator.go b/namesys/validator.go index e9cefc562..d7d1dc42d 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -41,12 +41,15 @@ var ErrKeyFormat = errors.New("record key could not be parsed into peer ID") // from the peer store var ErrPublicKeyNotFound = errors.New("public key not found in peer store") -// NewIpnsRecordValidator returns a ValidChecker for IPNS records +// NewIpnsRecordValidator returns a ValidChecker for IPNS records. // The validator function will get a public key from the KeyBook -// to verify the record's signature +// to verify the record's signature. Note that the public key must +// already have been fetched from the network and put into the KeyBook +// by the caller. func NewIpnsRecordValidator(kbook pstore.KeyBook) *record.ValidChecker { // ValidateIpnsRecord implements ValidatorFunc and verifies that the - // given 'val' is an IpnsEntry and that that entry is valid. + // given record's value is an IpnsEntry, that the entry has been correctly + // signed, and that the entry has not expired ValidateIpnsRecord := func(r *record.ValidationRecord) error { if r.Namespace != "ipns" { return ErrInvalidPath From 4c5e5216e2b31c02a63d801183d86d49093b3b21 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 7 Feb 2018 15:48:08 -0500 Subject: [PATCH 2187/3817] namesys: discard records with invalid EOL in record selection License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@1dc5c4406810028bc9d39d877a91e9e259eadda2 --- namesys/selector.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/namesys/selector.go b/namesys/selector.go index 53c712d1c..aebfb1533 100644 --- a/namesys/selector.go +++ b/namesys/selector.go @@ -35,23 +35,17 @@ func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) { if r == nil || r.GetSequence() < bestSeq { continue } + rt, err := u.ParseRFC3339(string(r.GetValidity())) + if err != nil { + log.Errorf("failed to parse ipns record EOL %s", r.GetValidity()) + continue + } if besti == -1 || r.GetSequence() > bestSeq { bestSeq = r.GetSequence() besti = i } else if r.GetSequence() == bestSeq { - rt, err := u.ParseRFC3339(string(r.GetValidity())) - if err != nil { - log.Errorf("failed to parse ipns record EOL %s", r.GetValidity()) - continue - } - - bestt, err := u.ParseRFC3339(string(recs[besti].GetValidity())) - if err != nil { - log.Errorf("failed to parse ipns record EOL %s", recs[besti].GetValidity()) - continue - } - + bestt, _ := u.ParseRFC3339(string(recs[besti].GetValidity())) if rt.After(bestt) { besti = i } else if rt == bestt { From b43c29f8bcefaa51dd01482dbe79505ee8e6b308 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 7 Feb 2018 16:24:25 -0500 Subject: [PATCH 2188/3817] go fmt License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@5a1ee4e9f1019197e338b63e8535a5fd32fdfc3d --- namesys/routing.go | 2 +- namesys/validator.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/namesys/routing.go b/namesys/routing.go index 120fabd66..effb3fa01 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -13,8 +13,8 @@ import ( routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" + mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) diff --git a/namesys/validator.go b/namesys/validator.go index d7d1dc42d..c50da5512 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -5,12 +5,12 @@ import ( "time" pb "github.com/ipfs/go-ipfs/namesys/pb" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" + proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) // ErrExpiredRecord should be returned when an ipns record is From 3795334fa692f6b19af07867e4a3f918ef4546b9 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 8 Feb 2018 11:58:30 +0100 Subject: [PATCH 2189/3817] Export from go-ipfs/importer/chunker This commit was moved from ipfs/go-ipfs-chunker@d0125832512163708c0804a3cda060e21acddae4 --- chunker/rabin.go | 2 +- chunker/rabin_test.go | 4 ++-- chunker/splitting.go | 4 ++-- chunker/splitting_test.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/chunker/rabin.go b/chunker/rabin.go index c3d1ebdba..4247057b2 100644 --- a/chunker/rabin.go +++ b/chunker/rabin.go @@ -4,7 +4,7 @@ import ( "hash/fnv" "io" - "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/chunker" + "github.com/whyrusleeping/chunker" ) // IpfsRabinPoly is the irreducible polynomial of degree 53 used by for Rabin. diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 2f68f01c4..f267db41a 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -6,8 +6,8 @@ import ( "io" "testing" - util "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + blocks "github.com/ipfs/go-block-format" + util "github.com/ipfs/go-ipfs-util" ) func TestRabinChunking(t *testing.T) { diff --git a/chunker/splitting.go b/chunker/splitting.go index 5be27625b..6a10de07a 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -7,8 +7,8 @@ package chunk import ( "io" - logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" - mpool "gx/ipfs/QmWBug6eBS7AxRdCDVuSY5CnSit7cS2XnPFYJWqWDumhCG/go-msgio/mpool" + logging "github.com/ipfs/go-log" + mpool "github.com/libp2p/go-msgio/mpool" ) var log = logging.Logger("chunk") diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index c5ef621e0..3153427ed 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -5,7 +5,7 @@ import ( "io" "testing" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + u "github.com/ipfs/go-ipfs-util" ) func randBuf(t *testing.T, size int) []byte { From 1ed47b8a06e5946959f4f61678f3945b810e8d40 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 8 Feb 2018 12:19:01 +0100 Subject: [PATCH 2190/3817] Add .travis.yml, Makefile, README This commit was moved from ipfs/go-ipfs-chunker@1e96e4c7d6cdb32d82ae5bc893abc207f1aa48c2 --- chunker/Makefile | 18 ++++++++++++++++++ chunker/README.md | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 chunker/Makefile create mode 100644 chunker/README.md diff --git a/chunker/Makefile b/chunker/Makefile new file mode 100644 index 000000000..73f2841f6 --- /dev/null +++ b/chunker/Makefile @@ -0,0 +1,18 @@ +all: deps +gx: + go get github.com/whyrusleeping/gx + go get github.com/whyrusleeping/gx-go +deps: gx + gx --verbose install --global + gx-go rewrite +test: deps + gx test -v -race -coverprofile=coverage.txt -covermode=atomic . +rw: + gx-go rewrite +rwundo: + gx-go rewrite --undo +publish: rwundo + gx publish +.PHONY: all gx deps test rw rwundo publish + + diff --git a/chunker/README.md b/chunker/README.md new file mode 100644 index 000000000..96faa3de3 --- /dev/null +++ b/chunker/README.md @@ -0,0 +1,48 @@ +# go-ipfs-chunker + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![GoDoc](https://godoc.org/github.com/ipfs/go-ipfs-chunker?status.svg)](https://godoc.org/github.com/ipfs/go-ipfs-chunker) +[![Build Status](https://travis-ci.org/ipfs/go-ipfs-chunker.svg?branch=master)](https://travis-ci.org/ipfs/go-ipfs-chunker) + +> go-ipfs-chunker implements data Splitters for go-ipfs. + +`go-ipfs-chunker` provides the `Splitter` interface. IPFS splitters read data from a reader an create "chunks". These chunks are used to build the ipfs DAGs (Merkle Tree) and are the base unit to obtain the sums that ipfs uses to address content. + +The package provides a `SizeSplitter` which creates chunks of equal size and it is used by default in most cases, and a `rabin` fingerprint chunker. This chunker will attempt to split data in a way that the resulting blocks are the same when the data has repetitive patterns, thus optimizing the resulting DAGs. + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Contribute](#contribute) +- [License](#license) + +## Install + +`go-ipfs-chunker` works like a regular Go module: + +``` +> go get github.com/ipfs/go-ipfs-chunker +``` + +It uses [Gx](https://github.com/whyrusleeping/gx) to manage dependencies. You can use `make all` to build it with the `gx` dependencies. + +## Usage + +``` +import "github.com/ipfs/go-ipfs-chunker" +``` + +Check the [GoDoc documentation](https://godoc.org/github.com/ipfs/go-ipfs-chunker) + +## Contribute + +PRs accepted. + +Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +MIT © Protocol Labs, Inc. From 34fa9d7e73e93142c06920d25e284dc96b4870d1 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 5 Feb 2018 20:28:27 +0100 Subject: [PATCH 2191/3817] WIP: Extract: importers/chunk module as go-ipfs-chunker License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@48941d57620b0d942bce93b0558d24ec5d45a628 --- unixfs/mod/dagmodifier.go | 2 +- unixfs/test/utils.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index dfadd778b..dbeee7266 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -8,12 +8,12 @@ import ( "errors" "io" - chunk "github.com/ipfs/go-ipfs/importer/chunk" help "github.com/ipfs/go-ipfs/importer/helpers" trickle "github.com/ipfs/go-ipfs/importer/trickle" mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" + chunk "gx/ipfs/QmQXcAyrC4VBu9ZBqxoCthPot9PNhb4Uiw6iBDfQXudZJd/go-ipfs-chunker" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 0ca47c842..380c74f44 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -8,12 +8,12 @@ import ( "io/ioutil" "testing" - "github.com/ipfs/go-ipfs/importer/chunk" h "github.com/ipfs/go-ipfs/importer/helpers" trickle "github.com/ipfs/go-ipfs/importer/trickle" mdag "github.com/ipfs/go-ipfs/merkledag" mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" + "gx/ipfs/QmQXcAyrC4VBu9ZBqxoCthPot9PNhb4Uiw6iBDfQXudZJd/go-ipfs-chunker" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" From ba3e0768520f235fad8491eb8641353a546cf524 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 5 Feb 2018 20:28:27 +0100 Subject: [PATCH 2192/3817] WIP: Extract: importers/chunk module as go-ipfs-chunker License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-mfs@4c210683b4a33c78c0febb6dbc01f26d9dee2f25 --- mfs/file.go | 2 +- mfs/mfs_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/file.go b/mfs/file.go index 496b05d8e..fc7a9ef32 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -5,10 +5,10 @@ import ( "fmt" "sync" - chunk "github.com/ipfs/go-ipfs/importer/chunk" dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" + chunk "gx/ipfs/QmQXcAyrC4VBu9ZBqxoCthPot9PNhb4Uiw6iBDfQXudZJd/go-ipfs-chunker" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 5db5d4987..187481077 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -18,11 +18,11 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" importer "github.com/ipfs/go-ipfs/importer" - chunk "github.com/ipfs/go-ipfs/importer/chunk" dag "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/path" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" + chunk "gx/ipfs/QmQXcAyrC4VBu9ZBqxoCthPot9PNhb4Uiw6iBDfQXudZJd/go-ipfs-chunker" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" From 8978dcc6402119686228c24122b960cc7a0e5ebc Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 5 Feb 2018 20:28:27 +0100 Subject: [PATCH 2193/3817] WIP: Extract: importers/chunk module as go-ipfs-chunker License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@3d0a19670780e670d8203066793cd534aea3ed49 --- ipld/merkledag/merkledag_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index e0b9d8b80..02cf56ce0 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -17,13 +17,13 @@ import ( bstest "github.com/ipfs/go-ipfs/blockservice/test" offline "github.com/ipfs/go-ipfs/exchange/offline" imp "github.com/ipfs/go-ipfs/importer" - chunk "github.com/ipfs/go-ipfs/importer/chunk" . "github.com/ipfs/go-ipfs/merkledag" mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" uio "github.com/ipfs/go-ipfs/unixfs/io" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + chunk "gx/ipfs/QmQXcAyrC4VBu9ZBqxoCthPot9PNhb4Uiw6iBDfQXudZJd/go-ipfs-chunker" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" From b467002fc0c2e1d0109ad01a7bb3883904a90528 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 5 Feb 2018 21:27:05 +0100 Subject: [PATCH 2194/3817] Fix missing rename in tests License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@34dcbba26297ebadf2728c30770c0b67cf56a377 --- unixfs/test/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 380c74f44..c0e7cd37c 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -13,9 +13,9 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" - "gx/ipfs/QmQXcAyrC4VBu9ZBqxoCthPot9PNhb4Uiw6iBDfQXudZJd/go-ipfs-chunker" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + chunk "gx/ipfs/QmQXcAyrC4VBu9ZBqxoCthPot9PNhb4Uiw6iBDfQXudZJd/go-ipfs-chunker" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" From da5a0b9140fbb5a2b354f74e050304a3c5d3677a Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 7 Feb 2018 10:39:41 +0100 Subject: [PATCH 2195/3817] go-ipfs-chunker: Use the stable gx'ed release License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-mfs@bac2343b2a074eb4c4de3ff4fc130d0902525776 --- mfs/file.go | 2 +- mfs/mfs_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/file.go b/mfs/file.go index fc7a9ef32..e73968c3d 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -8,7 +8,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - chunk "gx/ipfs/QmQXcAyrC4VBu9ZBqxoCthPot9PNhb4Uiw6iBDfQXudZJd/go-ipfs-chunker" + chunk "gx/ipfs/QmcYFMP9mfpJ2swedZCBskeAoxGKGBGY7LaS6VZ1jEWqUs/go-ipfs-chunker" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 187481077..3842c65bf 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -22,7 +22,7 @@ import ( "github.com/ipfs/go-ipfs/path" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - chunk "gx/ipfs/QmQXcAyrC4VBu9ZBqxoCthPot9PNhb4Uiw6iBDfQXudZJd/go-ipfs-chunker" + chunk "gx/ipfs/QmcYFMP9mfpJ2swedZCBskeAoxGKGBGY7LaS6VZ1jEWqUs/go-ipfs-chunker" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" From 423f643418b2b709b6a0b83e68a17c59bd14f99a Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 7 Feb 2018 10:39:41 +0100 Subject: [PATCH 2196/3817] go-ipfs-chunker: Use the stable gx'ed release License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@a22b077cbe5ff98389b0c8c8e8bbb9ec75b8e3e3 --- ipld/merkledag/merkledag_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 02cf56ce0..a041c19bd 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -24,6 +24,7 @@ import ( u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" chunk "gx/ipfs/QmQXcAyrC4VBu9ZBqxoCthPot9PNhb4Uiw6iBDfQXudZJd/go-ipfs-chunker" + chunk "gx/ipfs/QmcYFMP9mfpJ2swedZCBskeAoxGKGBGY7LaS6VZ1jEWqUs/go-ipfs-chunker" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" From 9f28a7b32b9786dfdf2884950e8e20822f37a393 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 7 Feb 2018 10:39:41 +0100 Subject: [PATCH 2197/3817] go-ipfs-chunker: Use the stable gx'ed release License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@4b7dace2f6e291da4e90ace622261db6348a1ede --- unixfs/mod/dagmodifier.go | 2 +- unixfs/test/utils.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index dbeee7266..4c3707064 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -13,7 +13,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - chunk "gx/ipfs/QmQXcAyrC4VBu9ZBqxoCthPot9PNhb4Uiw6iBDfQXudZJd/go-ipfs-chunker" + chunk "gx/ipfs/QmcYFMP9mfpJ2swedZCBskeAoxGKGBGY7LaS6VZ1jEWqUs/go-ipfs-chunker" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index c0e7cd37c..6b7c5312b 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -15,8 +15,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - chunk "gx/ipfs/QmQXcAyrC4VBu9ZBqxoCthPot9PNhb4Uiw6iBDfQXudZJd/go-ipfs-chunker" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" + chunk "gx/ipfs/QmcYFMP9mfpJ2swedZCBskeAoxGKGBGY7LaS6VZ1jEWqUs/go-ipfs-chunker" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) From bb970d335da5426808f3dc015aaf994d196272b2 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 8 Feb 2018 12:43:29 +0100 Subject: [PATCH 2198/3817] Extract chunker: Use last gx'ed go-ipfs-chunker License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-mfs@d282cb66dbf78bc001480dd8664bd40dcdce144a --- mfs/file.go | 2 +- mfs/mfs_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/file.go b/mfs/file.go index e73968c3d..50035da99 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -8,8 +8,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - chunk "gx/ipfs/QmcYFMP9mfpJ2swedZCBskeAoxGKGBGY7LaS6VZ1jEWqUs/go-ipfs-chunker" + chunk "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 3842c65bf..5092d45ab 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -22,11 +22,11 @@ import ( "github.com/ipfs/go-ipfs/path" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - chunk "gx/ipfs/QmcYFMP9mfpJ2swedZCBskeAoxGKGBGY7LaS6VZ1jEWqUs/go-ipfs-chunker" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + chunk "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) From d731a78c5a64ae91b55430c41d0e4877e225aa5c Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 8 Feb 2018 12:43:29 +0100 Subject: [PATCH 2199/3817] Extract chunker: Use last gx'ed go-ipfs-chunker License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@f3c8524d4681fd6c33651d6d770d7cbd6dfdbbf3 --- ipld/merkledag/merkledag_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index a041c19bd..d6efe54ac 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -23,8 +23,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - chunk "gx/ipfs/QmQXcAyrC4VBu9ZBqxoCthPot9PNhb4Uiw6iBDfQXudZJd/go-ipfs-chunker" - chunk "gx/ipfs/QmcYFMP9mfpJ2swedZCBskeAoxGKGBGY7LaS6VZ1jEWqUs/go-ipfs-chunker" + chunk "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" From bf1593f1ca5356e716e8f25e1cf70d55ea6931f5 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 8 Feb 2018 12:43:29 +0100 Subject: [PATCH 2200/3817] Extract chunker: Use last gx'ed go-ipfs-chunker License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@ac048e95ddacb849f8931f69a05faa154c305dca --- unixfs/mod/dagmodifier.go | 2 +- unixfs/test/utils.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 4c3707064..683ce056a 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -13,8 +13,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - chunk "gx/ipfs/QmcYFMP9mfpJ2swedZCBskeAoxGKGBGY7LaS6VZ1jEWqUs/go-ipfs-chunker" + chunk "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 6b7c5312b..a127c1a65 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -15,8 +15,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + chunk "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" - chunk "gx/ipfs/QmcYFMP9mfpJ2swedZCBskeAoxGKGBGY7LaS6VZ1jEWqUs/go-ipfs-chunker" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) From a0df8e1fa7cfd835379d72324ae46f324ddcd82d Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 8 Feb 2018 12:49:39 +0100 Subject: [PATCH 2201/3817] Extract: chunker: rename "chunk" to "chunker" as it is more consistent License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-mfs@c89547ed597123ff16ebf3cbed3d842207a89fcd --- mfs/file.go | 4 ++-- mfs/mfs_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mfs/file.go b/mfs/file.go index 50035da99..11d4a2a75 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -9,7 +9,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - chunk "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" + chunker "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) @@ -82,7 +82,7 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { return nil, fmt.Errorf("mode not supported") } - dmod, err := mod.NewDagModifier(context.TODO(), node, fi.dserv, chunk.DefaultSplitter) + dmod, err := mod.NewDagModifier(context.TODO(), node, fi.dserv, chunker.DefaultSplitter) if err != nil { return nil, err } diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 5092d45ab..8a1f9ccf2 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -26,7 +26,7 @@ import ( u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - chunk "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" + chunker "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) @@ -48,7 +48,7 @@ func getRandFile(t *testing.T, ds ipld.DAGService, size int64) ipld.Node { } func fileNodeFromReader(t *testing.T, ds ipld.DAGService, r io.Reader) ipld.Node { - nd, err := importer.BuildDagFromReader(ds, chunk.DefaultSplitter(r)) + nd, err := importer.BuildDagFromReader(ds, chunker.DefaultSplitter(r)) if err != nil { t.Fatal(err) } From eec66c1743f8612e3f6bdcd7b6a9bb48eb983433 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 8 Feb 2018 12:49:39 +0100 Subject: [PATCH 2202/3817] Extract: chunker: rename "chunk" to "chunker" as it is more consistent License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@87341562bc2666fa55d3df0e5346427eee054f24 --- ipld/merkledag/merkledag_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index d6efe54ac..db8d49bcc 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -23,7 +23,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - chunk "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" + chunker "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" @@ -136,7 +136,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { dagservs = append(dagservs, NewDAGService(bsi)) } - spl := chunk.NewSizeSplitter(read, 512) + spl := chunker.NewSizeSplitter(read, 512) root, err := imp.BuildDagFromReader(dagservs[0], spl) if err != nil { @@ -228,7 +228,7 @@ func TestFetchGraph(t *testing.T) { } read := io.LimitReader(u.NewTimeSeededRand(), 1024*32) - root, err := imp.BuildDagFromReader(dservs[0], chunk.NewSizeSplitter(read, 512)) + root, err := imp.BuildDagFromReader(dservs[0], chunker.NewSizeSplitter(read, 512)) if err != nil { t.Fatal(err) } @@ -254,7 +254,7 @@ func TestEnumerateChildren(t *testing.T) { ds := NewDAGService(bsi[0]) read := io.LimitReader(u.NewTimeSeededRand(), 1024*1024) - root, err := imp.BuildDagFromReader(ds, chunk.NewSizeSplitter(read, 512)) + root, err := imp.BuildDagFromReader(ds, chunker.NewSizeSplitter(read, 512)) if err != nil { t.Fatal(err) } From 0974b8d9e29f2b895e3ff86ae627c504d5372ba2 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 8 Feb 2018 12:49:39 +0100 Subject: [PATCH 2203/3817] Extract: chunker: rename "chunk" to "chunker" as it is more consistent License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@a8bd950cb77138defb0ebae2ba1e1d4dd9870354 --- unixfs/mod/dagmodifier.go | 10 +++++----- unixfs/test/utils.go | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 683ce056a..8f2766aee 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -14,7 +14,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - chunk "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" + chunker "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" @@ -37,7 +37,7 @@ type DagModifier struct { dagserv ipld.DAGService curNode ipld.Node - splitter chunk.SplitterGen + splitter chunker.SplitterGen ctx context.Context readCancel func() @@ -55,7 +55,7 @@ type DagModifier struct { // created nodes will be inherted from the passed in node. If the Cid // version if not 0 raw leaves will also be enabled. The Prefix and // RawLeaves options can be overridden by changing them after the call. -func NewDagModifier(ctx context.Context, from ipld.Node, serv ipld.DAGService, spl chunk.SplitterGen) (*DagModifier, error) { +func NewDagModifier(ctx context.Context, from ipld.Node, serv ipld.DAGService, spl chunker.SplitterGen) (*DagModifier, error) { switch from.(type) { case *mdag.ProtoNode, *mdag.RawNode: // ok @@ -126,7 +126,7 @@ func (zr zeroReader) Read(b []byte) (int, error) { // A small blocksize is chosen to aid in deduplication func (dm *DagModifier) expandSparse(size int64) error { r := io.LimitReader(zeroReader{}, size) - spl := chunk.NewSizeSplitter(r, 4096) + spl := chunker.NewSizeSplitter(r, 4096) nnode, err := dm.appendData(dm.curNode, spl) if err != nil { return err @@ -356,7 +356,7 @@ func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64, data io.Reader) (*c } // appendData appends the blocks from the given chan to the end of this dag -func (dm *DagModifier) appendData(nd ipld.Node, spl chunk.Splitter) (ipld.Node, error) { +func (dm *DagModifier) appendData(nd ipld.Node, spl chunker.Splitter) (ipld.Node, error) { switch nd := nd.(type) { case *mdag.ProtoNode, *mdag.RawNode: dbp := &help.DagBuilderParams{ diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index a127c1a65..f96fcfcb5 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -15,16 +15,16 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - chunk "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" + chunker "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // SizeSplitterGen creates a generator. -func SizeSplitterGen(size int64) chunk.SplitterGen { - return func(r io.Reader) chunk.Splitter { - return chunk.NewSizeSplitter(r, size) +func SizeSplitterGen(size int64) chunker.SplitterGen { + return func(r io.Reader) chunker.Splitter { + return chunker.NewSizeSplitter(r, size) } } From aa344d7e5c0dad863115a7c64501d4016b444bd9 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 9 Feb 2018 13:10:06 +0100 Subject: [PATCH 2204/3817] Doc: golint-ify routing module License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-routing@8436b8ae9bf07f0a2f0ac8ef658d75a44e565e2c --- routing/mock/interface.go | 12 +++++++----- routing/none/none_client.go | 6 ++++-- routing/offline/offline.go | 7 +++++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 6306ff060..6f906935e 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -1,17 +1,17 @@ -// Package mock provides a virtual routing server. To use it, create a virtual -// routing server and use the Client() method to get a routing client -// (IpfsRouting). The server quacks like a DHT but is really a local in-memory -// hash table. +// Package mockrouting provides a virtual routing server. To use it, +// create a virtual routing server and use the Client() method to get a +// routing client (IpfsRouting). The server quacks like a DHT but is +// really a local in-memory hash table. package mockrouting import ( "context" delay "github.com/ipfs/go-ipfs/thirdparty/delay" - "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" + "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ) @@ -42,6 +42,8 @@ func NewServerWithDelay(conf DelayConfig) Server { } } +// DelayConfig can be used to configured the fake delays of a mock server. +// Use with NewServerWithDelay(). type DelayConfig struct { // ValueVisibility is the time it takes for a value to be visible in the network // FIXME there _must_ be a better term for this diff --git a/routing/none/none_client.go b/routing/none/none_client.go index c38443362..aee2b281b 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -1,3 +1,4 @@ +// Package nilrouting implements a routing client that does nothing. package nilrouting import ( @@ -21,11 +22,11 @@ func (c *nilclient) PutValue(_ context.Context, _ string, _ []byte) error { } func (c *nilclient) GetValue(_ context.Context, _ string) ([]byte, error) { - return nil, errors.New("Tried GetValue from nil routing.") + return nil, errors.New("tried GetValue from nil routing") } func (c *nilclient) GetValues(_ context.Context, _ string, _ int) ([]routing.RecvdVal, error) { - return nil, errors.New("Tried GetValues from nil routing.") + return nil, errors.New("tried GetValues from nil routing") } func (c *nilclient) FindPeer(_ context.Context, _ peer.ID) (pstore.PeerInfo, error) { @@ -46,6 +47,7 @@ func (c *nilclient) Bootstrap(_ context.Context) error { return nil } +// ConstructNilRouting creates an IpfsRouting client which does nothing. func ConstructNilRouting(_ context.Context, _ p2phost.Host, _ repo.Datastore) (routing.IpfsRouting, error) { return &nilclient{}, nil } diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 443d4a9ec..84b52950f 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -1,3 +1,5 @@ +// Package offline implements IpfsRouting with a client which +// is only able to perform offline operations. package offline import ( @@ -18,8 +20,13 @@ import ( cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) +// ErrOffline is returned when trying to perform operations that +// require connectivity. var ErrOffline = errors.New("routing system in offline mode") +// NewOfflineRouter returns an IpfsRouting implementation which only performs +// offline operations. It allows to Put and Get signed dht +// records to and from the local datastore. func NewOfflineRouter(dstore ds.Datastore, privkey ci.PrivKey) routing.IpfsRouting { return &offlineRouting{ datastore: dstore, From 7abe2e7f10119df50a38a100219924a992de9ba2 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 9 Feb 2018 13:35:20 +0100 Subject: [PATCH 2205/3817] Add LICENSE This commit was moved from ipfs/go-ipfs-chunker@a4b89204c7352f2d8cc900463025fc4198233aa6 --- chunker/LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 chunker/LICENSE diff --git a/chunker/LICENSE b/chunker/LICENSE new file mode 100644 index 000000000..e4224df5b --- /dev/null +++ b/chunker/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 IPFS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From a0b042755fc564097b15a51e67e082fedc15b94c Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 12 Feb 2018 11:31:15 +0100 Subject: [PATCH 2206/3817] Extract from go-ipfs: readme, travis, golint, makefile, tests... This commit was moved from ipfs/go-ipfs-ds-help@1c74d493369298b09646db74be972505b137d27e --- datastore/dshelp/LICENSE | 21 +++++++++++++++++ datastore/dshelp/Makefile | 18 +++++++++++++++ datastore/dshelp/README.md | 44 ++++++++++++++++++++++++++++++++++++ datastore/dshelp/key.go | 24 ++++++++++++-------- datastore/dshelp/key_test.go | 19 ++++++++++++++++ 5 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 datastore/dshelp/LICENSE create mode 100644 datastore/dshelp/Makefile create mode 100644 datastore/dshelp/README.md create mode 100644 datastore/dshelp/key_test.go diff --git a/datastore/dshelp/LICENSE b/datastore/dshelp/LICENSE new file mode 100644 index 000000000..e4224df5b --- /dev/null +++ b/datastore/dshelp/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 IPFS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/datastore/dshelp/Makefile b/datastore/dshelp/Makefile new file mode 100644 index 000000000..73f2841f6 --- /dev/null +++ b/datastore/dshelp/Makefile @@ -0,0 +1,18 @@ +all: deps +gx: + go get github.com/whyrusleeping/gx + go get github.com/whyrusleeping/gx-go +deps: gx + gx --verbose install --global + gx-go rewrite +test: deps + gx test -v -race -coverprofile=coverage.txt -covermode=atomic . +rw: + gx-go rewrite +rwundo: + gx-go rewrite --undo +publish: rwundo + gx publish +.PHONY: all gx deps test rw rwundo publish + + diff --git a/datastore/dshelp/README.md b/datastore/dshelp/README.md new file mode 100644 index 000000000..2af3bff46 --- /dev/null +++ b/datastore/dshelp/README.md @@ -0,0 +1,44 @@ +# go-ipfs-ds-help + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![GoDoc](https://godoc.org/github.com/ipfs/go-ipfs-ds-help?status.svg)](https://godoc.org/github.com/ipfs/go-ipfs-ds-help) +[![Build Status](https://travis-ci.org/ipfs/go-ipfs-ds-help.svg?branch=master)](https://travis-ci.org/ipfs/go-ipfs-ds-help) + +> go-ipfs-ds-help provides utilities for parsing and creating datastore keys used by go-ipfs. + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Contribute](#contribute) +- [License](#license) + +## Install + +`go-ipfs-ds-help` works like a regular Go module: + +``` +> go get github.com/ipfs/go-ipfs-ds-help +``` + +## Usage + +``` +import "github.com/ipfs/go-ipfs-ds-help" +``` + +Check the [GoDoc documentation](https://godoc.org/github.com/ipfs/go-ipfs-ds-help) + +This module uses [Gx](https://github.com/whyrusleeping/gx) to manage dependencies. You can use `make all` to build it with the `gx` dependencies. + +## Contribute + +PRs accepted. + +Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +MIT © Protocol Labs, Inc. diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index c1cf2c484..b4ee6743c 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -1,29 +1,33 @@ +// Package dshelp provides utilities for parsing and creating +// datastore keys used by go-ipfs package dshelp import ( - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" + cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-datastore" + "github.com/whyrusleeping/base32" ) -// TODO: put this code into the go-datastore itself - -func NewKeyFromBinary(rawKey []byte) ds.Key { +// NewKeyFromBinary creates a new key from a byte slice. +func NewKeyFromBinary(rawKey []byte) datastore.Key { buf := make([]byte, 1+base32.RawStdEncoding.EncodedLen(len(rawKey))) buf[0] = '/' base32.RawStdEncoding.Encode(buf[1:], rawKey) - return ds.RawKey(string(buf)) + return datastore.RawKey(string(buf)) } -func BinaryFromDsKey(k ds.Key) ([]byte, error) { +// BinaryFromDsKey returns the byte slice corresponding to the given Key. +func BinaryFromDsKey(k datastore.Key) ([]byte, error) { return base32.RawStdEncoding.DecodeString(k.String()[1:]) } -func CidToDsKey(k *cid.Cid) ds.Key { +// CidToDsKey creates a Key from the given Cid. +func CidToDsKey(k *cid.Cid) datastore.Key { return NewKeyFromBinary(k.Bytes()) } -func DsKeyToCid(dsKey ds.Key) (*cid.Cid, error) { +// DsKeyToCid converts the given Key to its corresponding Cid. +func DsKeyToCid(dsKey datastore.Key) (*cid.Cid, error) { kb, err := BinaryFromDsKey(dsKey) if err != nil { return nil, err diff --git a/datastore/dshelp/key_test.go b/datastore/dshelp/key_test.go new file mode 100644 index 000000000..1f739bf8b --- /dev/null +++ b/datastore/dshelp/key_test.go @@ -0,0 +1,19 @@ +package dshelp + +import ( + "testing" + + cid "github.com/ipfs/go-cid" +) + +func TestKey(t *testing.T) { + c, _ := cid.Decode("QmP63DkAFEnDYNjDYBpyNDfttu1fvUw99x1brscPzpqmmq") + dsKey := CidToDsKey(c) + c2, err := DsKeyToCid(dsKey) + if err != nil { + t.Fatal(err) + } + if c.String() != c2.String() { + t.Fatal("should have parsed the same key") + } +} From 160c4e0baf9161a5079eb7457db0adab0822be1f Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 12 Feb 2018 22:18:18 +0100 Subject: [PATCH 2207/3817] Doc: golint-ify path package. This removes all go-lint warnings in the path package. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-path@9d2e89e3c330c58d444c86a812c19bb75a2799bc --- path/path.go | 29 +++++++++++++++++++++++++---- path/resolver.go | 23 ++++++++++++++--------- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/path/path.go b/path/path.go index decf28904..aeaeb5c7a 100644 --- a/path/path.go +++ b/path/path.go @@ -1,3 +1,4 @@ +// Package path contains utilities to work with ipfs paths. package path import ( @@ -11,20 +12,29 @@ import ( // ErrBadPath is returned when a given path is incorrectly formatted var ErrBadPath = errors.New("invalid 'ipfs ref' path") +// A Path represents an ipfs content path: +// * //path/to/file +// * /ipfs/ +// * /ipns//path/to/folder +// * etc +type Path string + +// ^^^ // TODO: debate making this a private struct wrapped in a public interface // would allow us to control creation, and cache segments. -type Path string -// FromString safely converts a string type to a Path type +// FromString safely converts a string type to a Path type. func FromString(s string) Path { return Path(s) } -// FromCid safely converts a cid.Cid type to a Path type +// FromCid safely converts a cid.Cid type to a Path type. func FromCid(c *cid.Cid) Path { return Path("/ipfs/" + c.String()) } +// Segments returns the different elements of a path +// (elements are delimited by a /). func (p Path) Segments() []string { cleaned := path.Clean(string(p)) segments := strings.Split(cleaned, "/") @@ -37,6 +47,7 @@ func (p Path) Segments() []string { return segments } +// String converts a path to string. func (p Path) String() string { return string(p) } @@ -65,10 +76,16 @@ func (p Path) PopLastSegment() (Path, string, error) { return newPath, segs[len(segs)-1], nil } +// FromSegments returns a path given its different segments. func FromSegments(prefix string, seg ...string) (Path, error) { return ParsePath(prefix + strings.Join(seg, "/")) } +// ParsePath returns a well-formed ipfs Path. +// The returned path will always be prefixed with /ipfs/ or /ipns/. +// The prefix will be added if not present in the given string. +// This function will return an error when the given string is +// not a valid ipfs path. func ParsePath(txt string) (Path, error) { parts := strings.Split(txt, "/") if len(parts) == 1 { @@ -78,7 +95,7 @@ func ParsePath(txt string) (Path, error) { } } - // if the path doesnt being with a '/' + // if the path doesnt begin with a '/' // we expect this to start with a hash, and be an 'ipfs' path if parts[0] != "" { if _, err := ParseCidToPath(parts[0]); err != nil { @@ -103,6 +120,7 @@ func ParsePath(txt string) (Path, error) { return Path(txt), nil } +// ParseCidToPath takes a CID in string form and returns a valid ipfs Path. func ParseCidToPath(txt string) (Path, error) { if txt == "" { return "", ErrNoComponents @@ -116,15 +134,18 @@ func ParseCidToPath(txt string) (Path, error) { return FromCid(c), nil } +// IsValid checks if a path is a valid ipfs Path. func (p *Path) IsValid() error { _, err := ParsePath(p.String()) return err } +// Join joins strings slices using / func Join(pths []string) string { return strings.Join(pths, "/") } +// SplitList splits strings usings / func SplitList(pth string) []string { return strings.Split(pth, "/") } diff --git a/path/resolver.go b/path/resolver.go index 30c249d45..64bdbf752 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -16,7 +16,8 @@ import ( var log = logging.Logger("path") -// Paths after a protocol must contain at least one component +// ErrNoComponents is used when Paths after a protocol +// do not contain at least one component var ErrNoComponents = errors.New( "path must contain at least one component") @@ -26,6 +27,8 @@ type ErrNoLink struct { Node *cid.Cid } +// Error implements the Error interface for ErrNoLink with a useful +// human readable message. func (e ErrNoLink) Error() string { return fmt.Sprintf("no link named %q under %s", e.Name, e.Node.String()) } @@ -74,6 +77,8 @@ func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) { return c, parts[1:], nil } +// ResolveToLastNode walks the given path and returns the ipld.Node +// referenced by the last element in it. func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath Path) (ipld.Node, []string, error) { c, p, err := SplitAbsPath(fpath) if err != nil { @@ -109,13 +114,13 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath Path) (ipld.Node // ResolvePath fetches the node for given path. It returns the last item // returned by ResolvePathComponents. -func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (ipld.Node, error) { +func (r *Resolver) ResolvePath(ctx context.Context, fpath Path) (ipld.Node, error) { // validate path if err := fpath.IsValid(); err != nil { return nil, err } - nodes, err := s.ResolvePathComponents(ctx, fpath) + nodes, err := r.ResolvePathComponents(ctx, fpath) if err != nil || nodes == nil { return nil, err } @@ -131,7 +136,7 @@ func ResolveSingle(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, names // ResolvePathComponents fetches the nodes for each segment of the given path. // It uses the first path component as a hash (key) of the first node, then // resolves all other components walking the links, with ResolveLinks. -func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]ipld.Node, error) { +func (r *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]ipld.Node, error) { evt := log.EventBegin(ctx, "resolvePathComponents", logging.LoggableMap{"fpath": fpath}) defer evt.Done() @@ -142,13 +147,13 @@ func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]ipl } log.Debug("resolve dag get") - nd, err := s.DAG.Get(ctx, h) + nd, err := r.DAG.Get(ctx, h) if err != nil { evt.Append(logging.LoggableMap{"error": err.Error()}) return nil, err } - return s.ResolveLinks(ctx, nd, parts) + return r.ResolveLinks(ctx, nd, parts) } // ResolveLinks iteratively resolves names by walking the link hierarchy. @@ -158,7 +163,7 @@ func (s *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]ipl // // ResolveLinks(nd, []string{"foo", "bar", "baz"}) // would retrieve "baz" in ("bar" in ("foo" in nd.Links).Links).Links -func (s *Resolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names []string) ([]ipld.Node, error) { +func (r *Resolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names []string) ([]ipld.Node, error) { evt := log.EventBegin(ctx, "resolveLinks", logging.LoggableMap{"names": names}) defer evt.Done() @@ -172,7 +177,7 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names []stri ctx, cancel = context.WithTimeout(ctx, time.Minute) defer cancel() - lnk, rest, err := s.ResolveOnce(ctx, s.DAG, nd, names) + lnk, rest, err := r.ResolveOnce(ctx, r.DAG, nd, names) if err == dag.ErrLinkNotFound { evt.Append(logging.LoggableMap{"error": err.Error()}) return result, ErrNoLink{Name: names[0], Node: nd.Cid()} @@ -181,7 +186,7 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names []stri return result, err } - nextnode, err := lnk.GetNode(ctx, s.DAG) + nextnode, err := lnk.GetNode(ctx, r.DAG) if err != nil { evt.Append(logging.LoggableMap{"error": err.Error()}) return result, err From 5da5eb3293a672babc092da5ec3015cbb18522f7 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 9 Feb 2018 14:36:19 +0100 Subject: [PATCH 2208/3817] Extract: flags and thirdparty/delay submodules They have been moved to their own repositories: * github.com/ipfs/go-ipfs-delay * github.com/ipfs/go-ipfs-flags History has been preserved. They have been published with gx'ed. Imports have been updated and re-ordered accordingly. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-routing@fe2bf7957948c303687de319de220fbee904e16f --- routing/mock/centralized_test.go | 2 +- routing/mock/interface.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 8d056a139..b2de10140 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - delay "github.com/ipfs/go-ipfs/thirdparty/delay" + delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 6f906935e..a4b198115 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -7,9 +7,8 @@ package mockrouting import ( "context" - delay "github.com/ipfs/go-ipfs/thirdparty/delay" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" From 31f674e6930f298abc4e013f1640d46a10cff355 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 12 Feb 2018 11:46:32 +0100 Subject: [PATCH 2209/3817] Extract: thirdparty/ds-help submodule It has been moved to its own repository: * github.com/ipfs/go-ipfs-ds-help History has been preserved. It has been published with gx. Imports have been updated and re-ordered accordingly. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-routing@50155e2ea53c49566ef88474c30815235681cb7c --- routing/mock/centralized_client.go | 5 ++--- routing/offline/offline.go | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index bc90ca6f7..985eb2814 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -5,19 +5,18 @@ import ( "errors" "time" - dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" dhtpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" + "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" ) var log = logging.Logger("mockrouter") diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 84b52950f..cc525a06d 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,8 +7,6 @@ import ( "errors" "time" - dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" @@ -18,6 +16,7 @@ import ( "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" ) // ErrOffline is returned when trying to perform operations that From ed3f969b41956a84e67e090e1727b8f5f6d4cf70 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 9 Feb 2018 14:36:19 +0100 Subject: [PATCH 2210/3817] Extract: flags and thirdparty/delay submodules They have been moved to their own repositories: * github.com/ipfs/go-ipfs-delay * github.com/ipfs/go-ipfs-flags History has been preserved. They have been published with gx'ed. Imports have been updated and re-ordered accordingly. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-blockservice@e240865f3e4900791a5febafba88a142fd711af7 --- blockservice/test/mock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index 622d1c8d6..42eb97ba3 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -5,7 +5,7 @@ import ( bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" mockrouting "github.com/ipfs/go-ipfs/routing/mock" - delay "github.com/ipfs/go-ipfs/thirdparty/delay" + delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" ) // Mocks returns |n| connected mock Blockservices From f49f4162fab2e441a69851b8150f5088bb50b18a Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 12 Feb 2018 11:46:32 +0100 Subject: [PATCH 2211/3817] Extract: thirdparty/ds-help submodule It has been moved to its own repository: * github.com/ipfs/go-ipfs-ds-help History has been preserved. It has been published with gx. Imports have been updated and re-ordered accordingly. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-filestore@9beea522bdee86a0c32d59b6bdf1736e7011517f --- filestore/fsrefstore.go | 6 +++--- filestore/util.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 8e048fd69..c4a35a5fe 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -9,15 +9,15 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" pb "github.com/ipfs/go-ipfs/filestore/pb" - dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" - "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dsns "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/namespace" dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" + posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" + "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index 35a640b68..98463271c 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,11 +6,11 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" pb "github.com/ipfs/go-ipfs/filestore/pb" - dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" ) // Status is used to identify the state of the block data referenced From 290f41ba93e5f502707daf40bcd9d8038b1a3fd4 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 12 Feb 2018 11:46:32 +0100 Subject: [PATCH 2212/3817] Extract: thirdparty/ds-help submodule It has been moved to its own repository: * github.com/ipfs/go-ipfs-ds-help History has been preserved. It has been published with gx. Imports have been updated and re-ordered accordingly. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-namesys@78135537446aa1f85505ce9c34f175305c9051a1 --- namesys/publisher.go | 2 +- namesys/publisher_test.go | 2 +- namesys/pubsub.go | 2 +- namesys/republisher/repub.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 79e3168e8..12c10f752 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -9,7 +9,6 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" pin "github.com/ipfs/go-ipfs/pin" - dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ft "github.com/ipfs/go-ipfs/unixfs" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" @@ -19,6 +18,7 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" ) const PublishPutValTimeout = time.Minute diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 86d4a0a41..7e28179bf 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -8,7 +8,6 @@ import ( path "github.com/ipfs/go-ipfs/path" mockrouting "github.com/ipfs/go-ipfs/routing/mock" - dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" @@ -16,6 +15,7 @@ import ( ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" ) type identity struct { diff --git a/namesys/pubsub.go b/namesys/pubsub.go index 229b9ff42..20052cd78 100644 --- a/namesys/pubsub.go +++ b/namesys/pubsub.go @@ -10,7 +10,6 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" p2phost "gx/ipfs/QmNmJZL7FQySMtE2BQuLMuZg2EB2CLEunJJUSVSc9YnnbV/go-libp2p-host" @@ -26,6 +25,7 @@ import ( mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" ) // PubsubPublisher is a publisher that distributes IPNS records through pubsub diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 345ba4abc..7bb54fcd4 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -9,7 +9,6 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" @@ -20,6 +19,7 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ic "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" ) var errNoEntry = errors.New("no previous entry") From 0f39cfcbef6bf9f397feb1563fbe589d8cb5e0a6 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 12 Feb 2018 11:46:32 +0100 Subject: [PATCH 2213/3817] Extract: thirdparty/ds-help submodule It has been moved to its own repository: * github.com/ipfs/go-ipfs-ds-help History has been preserved. It has been published with gx. Imports have been updated and re-ordered accordingly. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-blockstore@d427bf1af8b267b4ab79ff8722f0c082c360b26b --- blockstore/blockstore.go | 5 ++--- blockstore/blockstore_test.go | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 7e5e8cbdc..f5bbb826a 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -8,14 +8,13 @@ import ( "sync" "sync/atomic" - dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dsns "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/namespace" dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 2b0366096..757aa67e1 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -6,14 +6,13 @@ import ( "fmt" "testing" - dshelp "github.com/ipfs/go-ipfs/thirdparty/ds-help" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" ds_sync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) func TestGetWhenKeyNotPresent(t *testing.T) { From 5828abc587445e7dbcf6420ea76c19d54564a624 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 12 Feb 2018 11:55:03 +0100 Subject: [PATCH 2214/3817] Import re-ordering License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-routing@a2716d727f04007374a1c4d1e471bd705b7772d7 --- routing/mock/centralized_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index b2de10140..b3f6c2926 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -5,9 +5,8 @@ import ( "testing" "time" - delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" From d18cb72e076fb786f6ad128d01218809c2de7e09 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 12 Feb 2018 11:55:03 +0100 Subject: [PATCH 2215/3817] Import re-ordering License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-blockservice@e2f33c199a2638385521f060c97fbb35e399a171 --- blockservice/test/mock.go | 1 + 1 file changed, 1 insertion(+) diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index 42eb97ba3..c76c4d025 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -5,6 +5,7 @@ import ( bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" mockrouting "github.com/ipfs/go-ipfs/routing/mock" + delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" ) From 08744ade4c21f22fc2f5b5020542624eaa9d2e26 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 13 Feb 2018 11:29:32 +0100 Subject: [PATCH 2216/3817] More consistency in imports Per @magik6k comments. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-filestore@3030c6acfa87419cb640e8873d684abdd735f0f2 --- filestore/filestore.go | 4 ++-- filestore/fsrefstore.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index e7215dfcc..69fb72014 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,12 +11,12 @@ import ( "context" "github.com/ipfs/go-ipfs/blocks/blockstore" - posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" - "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" + posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) var log = logging.Logger("filestore") diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index c4a35a5fe..0f08771bf 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -17,7 +17,7 @@ import ( posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" - "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) // FilestorePrefix identifies the key prefix for FileManager blocks. From ab321f577c7b22fb847a65dad8f88bb16f205c16 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 13 Feb 2018 12:53:20 +0100 Subject: [PATCH 2217/3817] Docs: golint-ify pin package License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-pinner@a8e7138215fc76656db4961e61886c464086e546 --- pinning/pinner/gc/gc.go | 19 ++++++++++++++++++- pinning/pinner/pin.go | 26 ++++++++++++++++++-------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 92c8cb52a..1ed5f1672 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -1,3 +1,4 @@ +// Package gc provides garbage collection for go-ipfs. package gc import ( @@ -35,7 +36,6 @@ type Result struct { // // The routine then iterates over every block in the blockstore and // deletes any block that is not found in the marked set. -// func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn pin.Pinner, bestEffortRoots []*cid.Cid) <-chan Result { elock := log.EventBegin(ctx, "GC.lockWait") @@ -125,6 +125,9 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn return output } +// Descendants recursively finds all the descendants of the given roots and +// adds them to the given cid.Set, using the provided dag.GetLinks function +// to walk the tree. func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots []*cid.Cid) error { for _, c := range roots { set.Add(c) @@ -191,24 +194,38 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo return gcs, nil } +// ErrCannotFetchAllLinks is returned as the last Result in the GC output +// channel when there was a error creating the marked set because of a +// problem when finding descendants. var ErrCannotFetchAllLinks = errors.New("garbage collection aborted: could not retrieve some links") +// ErrCannotDeleteSomeBlocks is returned when removing blocks marked for +// deletion fails as the last Result in GC output channel. var ErrCannotDeleteSomeBlocks = errors.New("garbage collection incomplete: could not delete some blocks") +// CannotFetchLinksError provides detailed information about which links +// could not be fetched and can appear as a Result in the GC output channel. type CannotFetchLinksError struct { Key *cid.Cid Err error } +// Error implements the error interface for this type with a useful +// message. func (e *CannotFetchLinksError) Error() string { return fmt.Sprintf("could not retrieve links for %s: %s", e.Key, e.Err) } +// CannotDeleteBlockError provides detailed information about which +// blocks could not be deleted and can appear as a Result in the GC output +// channel. type CannotDeleteBlockError struct { Key *cid.Cid Err error } +// Error implements the error interface for this type with a +// useful message. func (e *CannotDeleteBlockError) Error() string { return fmt.Sprintf("could not remove %s: %s", e.Key, e.Err) } diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 07cda7cd0..ded36900a 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -1,4 +1,4 @@ -// package pin implements structures and methods to keep track of +// Package pin implements structures and methods to keep track of // which objects a user wants to keep stored locally. package pin @@ -43,8 +43,11 @@ const ( linkAll = "all" ) +// PinMode allows to specify different types of pin (recursive, direct etc.). +// See the Pin Modes constants for a full list. type PinMode int +// Pin Modes const ( // Recursive pins pin the target cids along with any reachable children. Recursive PinMode = iota @@ -65,6 +68,7 @@ const ( Any ) +// PinModeToString returns a human-readable name for the PinMode. func PinModeToString(mode PinMode) (string, bool) { m := map[PinMode]string{ Recursive: linkRecursive, @@ -78,6 +82,8 @@ func PinModeToString(mode PinMode) (string, bool) { return s, ok } +// StringToPinMode parses the result of PinModeToString() back to a PinMode. +// It returns a boolean which is set to false if the mode is unknown. func StringToPinMode(s string) (PinMode, bool) { m := map[string]PinMode{ linkRecursive: Recursive, @@ -92,6 +98,10 @@ func StringToPinMode(s string) (PinMode, bool) { return mode, ok } +// A Pinner provides the necessary methods to keep track of Nodes which are +// to be kept locally, according to a pin mode. In practice, a Pinner is in +// in charge of keeping the list of items from the local storage that should +// not be garbaged-collected. type Pinner interface { // IsPinned returns whether or not the given cid is pinned // and an explanation of why its pinned @@ -141,6 +151,10 @@ type Pinner interface { InternalPins() []*cid.Cid } +// Pinned represents CID which has been pinned with a pinning strategy. +// The Via field allows to identify the pinning parent of this CID, in the +// case that the item is not pinned directly (but rather pinned recursively +// by some ascendant). type Pinned struct { Key *cid.Cid Mode PinMode @@ -149,11 +163,7 @@ type Pinned struct { // Pinned returns whether or not the given cid is pinned func (p Pinned) Pinned() bool { - if p.Mode == NotPinned { - return false - } else { - return true - } + return p.Mode != NotPinned } // String Returns pin status as string @@ -240,6 +250,7 @@ func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { return nil } +// ErrNotPinned is returned when trying to unpin items which are not pinned. var ErrNotPinned = fmt.Errorf("not pinned") // Unpin a given key @@ -258,9 +269,8 @@ func (p *pinner) Unpin(ctx context.Context, c *cid.Cid, recursive bool) error { if recursive { p.recursePin.Remove(c) return nil - } else { - return fmt.Errorf("%s is pinned recursively", c) } + return fmt.Errorf("%s is pinned recursively", c) case "direct": p.directPin.Remove(c) return nil From 2098ed2b4559e869f4c8b28e90a37ee6e1fc0e10 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 13 Feb 2018 13:00:48 +0100 Subject: [PATCH 2218/3817] Doc: golint: remove stuttering in pin package License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-pinner@12448909d819e86b464c049b1b63382712450a52 --- pinning/pinner/pin.go | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index ded36900a..685f8cf65 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -43,14 +43,14 @@ const ( linkAll = "all" ) -// PinMode allows to specify different types of pin (recursive, direct etc.). +// Mode allows to specify different types of pin (recursive, direct etc.). // See the Pin Modes constants for a full list. -type PinMode int +type Mode int // Pin Modes const ( // Recursive pins pin the target cids along with any reachable children. - Recursive PinMode = iota + Recursive Mode = iota // Direct pins pin just the target cid. Direct @@ -68,9 +68,9 @@ const ( Any ) -// PinModeToString returns a human-readable name for the PinMode. -func PinModeToString(mode PinMode) (string, bool) { - m := map[PinMode]string{ +// ModeToString returns a human-readable name for the Mode. +func ModeToString(mode Mode) (string, bool) { + m := map[Mode]string{ Recursive: linkRecursive, Direct: linkDirect, Indirect: linkIndirect, @@ -82,10 +82,10 @@ func PinModeToString(mode PinMode) (string, bool) { return s, ok } -// StringToPinMode parses the result of PinModeToString() back to a PinMode. +// StringToMode parses the result of ModeToString() back to a Mode. // It returns a boolean which is set to false if the mode is unknown. -func StringToPinMode(s string) (PinMode, bool) { - m := map[string]PinMode{ +func StringToMode(s string) (Mode, bool) { + m := map[string]Mode{ linkRecursive: Recursive, linkDirect: Direct, linkIndirect: Indirect, @@ -109,7 +109,7 @@ type Pinner interface { // IsPinnedWithType returns whether or not the given cid is pinned with the // given pin type, as well as returning the type of pin its pinned with. - IsPinnedWithType(*cid.Cid, PinMode) (string, bool, error) + IsPinnedWithType(*cid.Cid, Mode) (string, bool, error) // Pin the given node, optionally recursively. Pin(ctx context.Context, node ipld.Node, recursive bool) error @@ -130,12 +130,12 @@ type Pinner interface { // PinWithMode is for manually editing the pin structure. Use with // care! If used improperly, garbage collection may not be // successful. - PinWithMode(*cid.Cid, PinMode) + PinWithMode(*cid.Cid, Mode) // RemovePinWithMode is for manually editing the pin structure. // Use with care! If used improperly, garbage collection may not // be successful. - RemovePinWithMode(*cid.Cid, PinMode) + RemovePinWithMode(*cid.Cid, Mode) // Flush writes the pin state to the backing datastore Flush() error @@ -157,7 +157,7 @@ type Pinner interface { // by some ascendant). type Pinned struct { Key *cid.Cid - Mode PinMode + Mode Mode Via *cid.Cid } @@ -174,7 +174,7 @@ func (p Pinned) String() string { case Indirect: return fmt.Sprintf("pinned via %s", p.Via) default: - modeStr, _ := PinModeToString(p.Mode) + modeStr, _ := ModeToString(p.Mode) return fmt.Sprintf("pinned: %s", modeStr) } } @@ -293,7 +293,7 @@ func (p *pinner) IsPinned(c *cid.Cid) (string, bool, error) { // IsPinnedWithType returns whether or not the given cid is pinned with the // given pin type, as well as returning the type of pin its pinned with. -func (p *pinner) IsPinnedWithType(c *cid.Cid, mode PinMode) (string, bool, error) { +func (p *pinner) IsPinnedWithType(c *cid.Cid, mode Mode) (string, bool, error) { p.lock.RLock() defer p.lock.RUnlock() return p.isPinnedWithType(c, mode) @@ -301,7 +301,7 @@ func (p *pinner) IsPinnedWithType(c *cid.Cid, mode PinMode) (string, bool, error // isPinnedWithType is the implementation of IsPinnedWithType that does not lock. // intended for use by other pinned methods that already take locks -func (p *pinner) isPinnedWithType(c *cid.Cid, mode PinMode) (string, bool, error) { +func (p *pinner) isPinnedWithType(c *cid.Cid, mode Mode) (string, bool, error) { switch mode { case Any, Direct, Indirect, Recursive, Internal: default: @@ -414,7 +414,7 @@ func (p *pinner) CheckIfPinned(cids ...*cid.Cid) ([]Pinned, error) { // RemovePinWithMode is for manually editing the pin structure. // Use with care! If used improperly, garbage collection may not // be successful. -func (p *pinner) RemovePinWithMode(c *cid.Cid, mode PinMode) { +func (p *pinner) RemovePinWithMode(c *cid.Cid, mode Mode) { p.lock.Lock() defer p.lock.Unlock() switch mode { @@ -594,7 +594,7 @@ func (p *pinner) InternalPins() []*cid.Cid { // PinWithMode allows the user to have fine grained control over pin // counts -func (p *pinner) PinWithMode(c *cid.Cid, mode PinMode) { +func (p *pinner) PinWithMode(c *cid.Cid, mode Mode) { p.lock.Lock() defer p.lock.Unlock() switch mode { From b1cc9b6e67092724926611225cecfd249584989a Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 14 Feb 2018 17:59:52 +0100 Subject: [PATCH 2219/3817] Extract from go-ipfs: gxify, license, readme, travis, makefile This commit was moved from ipfs/go-ipfs-routing@a32f8f983edcb78fc1f410308953b3fa64d9c4b1 --- routing/LICENSE | 21 ++++++++++++++ routing/Makefile | 18 ++++++++++++ routing/README.md | 44 ++++++++++++++++++++++++++++++ routing/mock/centralized_client.go | 24 ++++++++-------- routing/mock/centralized_server.go | 13 ++++----- routing/mock/centralized_test.go | 10 +++---- routing/mock/interface.go | 10 +++---- routing/none/none_client.go | 11 ++++---- routing/offline/offline.go | 20 +++++++------- routing/offline/offline_test.go | 4 +-- 10 files changed, 128 insertions(+), 47 deletions(-) create mode 100644 routing/LICENSE create mode 100644 routing/Makefile create mode 100644 routing/README.md diff --git a/routing/LICENSE b/routing/LICENSE new file mode 100644 index 000000000..e4224df5b --- /dev/null +++ b/routing/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 IPFS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/routing/Makefile b/routing/Makefile new file mode 100644 index 000000000..c12161c96 --- /dev/null +++ b/routing/Makefile @@ -0,0 +1,18 @@ +all: deps +gx: + go get github.com/whyrusleeping/gx + go get github.com/whyrusleeping/gx-go +deps: gx + gx --verbose install --global + gx-go rewrite +test: deps + gx test -v -race ./... +rw: + gx-go rewrite +rwundo: + gx-go rewrite --undo +publish: rwundo + gx publish +.PHONY: all gx deps test rw rwundo publish + + diff --git a/routing/README.md b/routing/README.md new file mode 100644 index 000000000..636148909 --- /dev/null +++ b/routing/README.md @@ -0,0 +1,44 @@ +# go-ipfs-routing + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![GoDoc](https://godoc.org/github.com/ipfs/go-ipfs-routing?status.svg)](https://godoc.org/github.com/ipfs/go-ipfs-routing) +[![Build Status](https://travis-ci.org/ipfs/go-ipfs-routing.svg?branch=master)](https://travis-ci.org/ipfs/go-ipfs-routing) + +> go-ipfs-routing provides go-libp2p-routing implementations used in go-ipfs. + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Contribute](#contribute) +- [License](#license) + +## Install + +`go-ipfs-routing` works like a regular Go module: + +``` +> go get github.com/ipfs/go-ipfs-routing +``` + +This module uses [Gx](https://github.com/whyrusleeping/gx) to manage dependencies. You can use `make all` to build it with the `gx` dependencies. + +## Usage + +``` +import "github.com/ipfs/go-ipfs-routing" +``` + +Check the [GoDoc documentation](https://godoc.org/github.com/ipfs/go-ipfs-routing) + +## Contribute + +PRs accepted. + +Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +MIT © Protocol Labs, Inc. diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 985eb2814..f00f11a09 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -5,18 +5,18 @@ import ( "errors" "time" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" - routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" - dhtpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" - "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" - ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" - pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" + proto "github.com/gogo/protobuf/proto" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + dshelp "github.com/ipfs/go-ipfs-ds-help" + u "github.com/ipfs/go-ipfs-util" + logging "github.com/ipfs/go-log" + peer "github.com/libp2p/go-libp2p-peer" + pstore "github.com/libp2p/go-libp2p-peerstore" + dhtpb "github.com/libp2p/go-libp2p-record/pb" + routing "github.com/libp2p/go-libp2p-routing" + "github.com/libp2p/go-testutil" + ma "github.com/multiformats/go-multiaddr" ) var log = logging.Logger("mockrouter") diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index b4263d1d2..ab1a985fd 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -6,13 +6,12 @@ import ( "sync" "time" - "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" - - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + peer "github.com/libp2p/go-libp2p-peer" + pstore "github.com/libp2p/go-libp2p-peerstore" + "github.com/libp2p/go-testutil" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index b3f6c2926..704557a66 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -5,11 +5,11 @@ import ( "testing" "time" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" - testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" - pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + cid "github.com/ipfs/go-cid" + delay "github.com/ipfs/go-ipfs-delay" + u "github.com/ipfs/go-ipfs-util" + pstore "github.com/libp2p/go-libp2p-peerstore" + testutil "github.com/libp2p/go-testutil" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index a4b198115..c14a7763f 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -7,11 +7,11 @@ package mockrouting import ( "context" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" - routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" - "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" + ds "github.com/ipfs/go-datastore" + delay "github.com/ipfs/go-ipfs-delay" + peer "github.com/libp2p/go-libp2p-peer" + routing "github.com/libp2p/go-libp2p-routing" + "github.com/libp2p/go-testutil" ) // Server provides mockrouting Clients diff --git a/routing/none/none_client.go b/routing/none/none_client.go index aee2b281b..e5d00b0e3 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -5,13 +5,12 @@ import ( "context" "errors" + cid "github.com/ipfs/go-cid" repo "github.com/ipfs/go-ipfs/repo" - - p2phost "gx/ipfs/QmNmJZL7FQySMtE2BQuLMuZg2EB2CLEunJJUSVSc9YnnbV/go-libp2p-host" - routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" - pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + p2phost "github.com/libp2p/go-libp2p-host" + peer "github.com/libp2p/go-libp2p-peer" + pstore "github.com/libp2p/go-libp2p-peerstore" + routing "github.com/libp2p/go-libp2p-routing" ) type nilclient struct { diff --git a/routing/offline/offline.go b/routing/offline/offline.go index cc525a06d..7384c6d79 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -7,16 +7,16 @@ import ( "errors" "time" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" - record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" - pb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" - pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" - ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" + proto "github.com/gogo/protobuf/proto" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + dshelp "github.com/ipfs/go-ipfs-ds-help" + ci "github.com/libp2p/go-libp2p-crypto" + "github.com/libp2p/go-libp2p-peer" + pstore "github.com/libp2p/go-libp2p-peerstore" + record "github.com/libp2p/go-libp2p-record" + pb "github.com/libp2p/go-libp2p-record/pb" + routing "github.com/libp2p/go-libp2p-routing" ) // ErrOffline is returned when trying to perform operations that diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index 66d851700..a685dcab8 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -5,8 +5,8 @@ import ( "context" "testing" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" + ds "github.com/ipfs/go-datastore" + "github.com/libp2p/go-testutil" ) func TestOfflineRouterStorage(t *testing.T) { From 78943a135a21d59ea4f763f01c2f93088af70aa6 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 14 Feb 2018 18:05:01 +0100 Subject: [PATCH 2220/3817] Replace go-ipfs repo dependency with datastore.Batching interface. This commit was moved from ipfs/go-ipfs-routing@9fa690cf15b917d27a51e9b3cee974b3e3075bf0 --- routing/none/none_client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index e5d00b0e3..8935708d1 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -6,7 +6,7 @@ import ( "errors" cid "github.com/ipfs/go-cid" - repo "github.com/ipfs/go-ipfs/repo" + ds "github.com/ipfs/go-datastore" p2phost "github.com/libp2p/go-libp2p-host" peer "github.com/libp2p/go-libp2p-peer" pstore "github.com/libp2p/go-libp2p-peerstore" @@ -47,7 +47,7 @@ func (c *nilclient) Bootstrap(_ context.Context) error { } // ConstructNilRouting creates an IpfsRouting client which does nothing. -func ConstructNilRouting(_ context.Context, _ p2phost.Host, _ repo.Datastore) (routing.IpfsRouting, error) { +func ConstructNilRouting(_ context.Context, _ p2phost.Host, _ ds.Batching) (routing.IpfsRouting, error) { return &nilclient{}, nil } From d5ee4420c20f1ee7092dfad35825227ecb5f9194 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 9 Feb 2018 15:06:31 +0100 Subject: [PATCH 2221/3817] Extract: routing package to github.com/ipfs/go-ipfs-routing This extracts the routing package to its own repository (https://github.com/ipfs/go-ipfs-routing). History has been preserved. The new module has been gx'ed and published. Imports have been rewritten and re-ordered accordingly. An internal dependency to go-ipfs/repo has been removed by substituting it with the go-datastore.Batching interface. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-blockservice@06a91e4a35ffabab45cc33aff73641e21d0859a5 --- blockservice/test/mock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index c76c4d025..06d8e65ad 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -4,9 +4,9 @@ import ( . "github.com/ipfs/go-ipfs/blockservice" bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" - mockrouting "github.com/ipfs/go-ipfs/routing/mock" delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" + mockrouting "gx/ipfs/QmZRcGYvxdauCd7hHnMYLYqcZRaDjv24c7eUNyJojAcdBb/go-ipfs-routing/mock" ) // Mocks returns |n| connected mock Blockservices From 7bc813bc0c5ef09ea06921cd8f7c10125d1f26e2 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 9 Feb 2018 15:06:31 +0100 Subject: [PATCH 2222/3817] Extract: routing package to github.com/ipfs/go-ipfs-routing This extracts the routing package to its own repository (https://github.com/ipfs/go-ipfs-routing). History has been preserved. The new module has been gx'ed and published. Imports have been rewritten and re-ordered accordingly. An internal dependency to go-ipfs/repo has been removed by substituting it with the go-datastore.Batching interface. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-namesys@e82a7a15cbfce030cd04d23991ceb3c74eb7a749 --- namesys/ipns_validate_test.go | 2 +- namesys/namesys_test.go | 2 +- namesys/publisher_test.go | 2 +- namesys/pubsub_test.go | 2 +- namesys/resolve_test.go | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index 262b0711d..559a1b78a 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -7,7 +7,6 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" - mockrouting "github.com/ipfs/go-ipfs/routing/mock" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" @@ -18,6 +17,7 @@ import ( testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + mockrouting "gx/ipfs/QmZRcGYvxdauCd7hHnMYLYqcZRaDjv24c7eUNyJojAcdBb/go-ipfs-routing/mock" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 2cd91c79f..e4f7af196 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,11 +7,11 @@ import ( context "context" path "github.com/ipfs/go-ipfs/path" - offroute "github.com/ipfs/go-ipfs/routing/offline" "github.com/ipfs/go-ipfs/unixfs" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + offroute "gx/ipfs/QmZRcGYvxdauCd7hHnMYLYqcZRaDjv24c7eUNyJojAcdBb/go-ipfs-routing/offline" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 7e28179bf..e7d2dd686 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -7,12 +7,12 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" - mockrouting "github.com/ipfs/go-ipfs/routing/mock" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" + mockrouting "gx/ipfs/QmZRcGYvxdauCd7hHnMYLYqcZRaDjv24c7eUNyJojAcdBb/go-ipfs-routing/mock" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" diff --git a/namesys/pubsub_test.go b/namesys/pubsub_test.go index b8e6dbec3..e2cf15b5d 100644 --- a/namesys/pubsub_test.go +++ b/namesys/pubsub_test.go @@ -7,7 +7,6 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" - mockrouting "github.com/ipfs/go-ipfs/routing/mock" p2phost "gx/ipfs/QmNmJZL7FQySMtE2BQuLMuZg2EB2CLEunJJUSVSc9YnnbV/go-libp2p-host" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" @@ -17,6 +16,7 @@ import ( testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" netutil "gx/ipfs/QmYVR3C8DWPHdHxvLtNFYfjsXgaRAdh6hPMNH3KiwCgu4o/go-libp2p-netutil" + mockrouting "gx/ipfs/QmZRcGYvxdauCd7hHnMYLYqcZRaDjv24c7eUNyJojAcdBb/go-ipfs-routing/mock" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 17dab14af..a55d5a4d4 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -7,11 +7,11 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" - mockrouting "github.com/ipfs/go-ipfs/routing/mock" - testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" + mockrouting "gx/ipfs/QmZRcGYvxdauCd7hHnMYLYqcZRaDjv24c7eUNyJojAcdBb/go-ipfs-routing/mock" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ) From 5b277c3e14a28d3068ffa5544a219d6243bd8300 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 15 Feb 2018 00:38:02 +0100 Subject: [PATCH 2223/3817] Feat: remove circular dependencies in merkledag package tests This avoids using unixfs package and importer packages in merkledag, which removes circular depedencies making it hard to extract this module. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@331b83e351a4fc6310d3c558e4d589fe8d131886 --- ipld/merkledag/merkledag_test.go | 92 ++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 28 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index db8d49bcc..51083859f 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -16,14 +16,11 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" bstest "github.com/ipfs/go-ipfs/blockservice/test" offline "github.com/ipfs/go-ipfs/exchange/offline" - imp "github.com/ipfs/go-ipfs/importer" . "github.com/ipfs/go-ipfs/merkledag" mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" - uio "github.com/ipfs/go-ipfs/unixfs/io" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - chunker "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" @@ -129,6 +126,61 @@ func TestBatchFetchDupBlock(t *testing.T) { runBatchFetchTest(t, read) } +// makeTestDAG creates a simple DAG from the data in a reader. +// First, a node is created from each 512 bytes of data from the reader +// (like a the Size chunker would do). Then all nodes are added as children +// to a root node, which is returned. +func makeTestDAG(t *testing.T, read io.Reader, ds ipld.DAGService) ipld.Node { + p := make([]byte, 512) + nodes := []*ProtoNode{} + var err error = nil + _, err = read.Read(p) + for err == nil { + protoNode := NodeWithData(p) + nodes = append(nodes, protoNode) + _, err = read.Read(p) + } + + if err != io.EOF { + t.Fatal(err) + } + + ctx := context.Background() + // Add a root referencing all created nodes + root := NodeWithData(nil) + for _, n := range nodes { + root.AddNodeLink(n.Cid().String(), n) + err := ds.Add(ctx, n) + if err != nil { + t.Fatal(err) + } + } + err = ds.Add(ctx, root) + if err != nil { + t.Fatal(err) + } + return root +} + +// makeTestDAGReader takes the root node as returned by makeTestDAG and +// provides a reader that reads all the RawData from that node and its children. +func makeTestDAGReader(t *testing.T, root ipld.Node, ds ipld.DAGService) io.Reader { + ctx := context.Background() + buf := new(bytes.Buffer) + buf.Write(root.RawData()) + for _, l := range root.Links() { + n, err := ds.Get(ctx, l.Cid) + if err != nil { + t.Fatal(err) + } + _, err = buf.Write(n.RawData()) + if err != nil { + t.Fatal(err) + } + } + return buf +} + func runBatchFetchTest(t *testing.T, read io.Reader) { ctx := context.Background() var dagservs []ipld.DAGService @@ -136,19 +188,11 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { dagservs = append(dagservs, NewDAGService(bsi)) } - spl := chunker.NewSizeSplitter(read, 512) - - root, err := imp.BuildDagFromReader(dagservs[0], spl) - if err != nil { - t.Fatal(err) - } + root := makeTestDAG(t, read, dagservs[0]) t.Log("finished setup.") - dagr, err := uio.NewDagReader(ctx, root, dagservs[0]) - if err != nil { - t.Fatal(err) - } + dagr := makeTestDAGReader(t, root, dagservs[0]) expected, err := ioutil.ReadAll(dagr) if err != nil { @@ -181,11 +225,9 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { if !ok { errs <- ErrNotProtobuf } - - read, err := uio.NewDagReader(ctx, firstpb, dagservs[i]) - if err != nil { - errs <- err - } + _ = firstpb + _ = expected + read := makeTestDAGReader(t, firstpb, dagservs[i]) datagot, err := ioutil.ReadAll(read) if err != nil { errs <- err @@ -228,12 +270,9 @@ func TestFetchGraph(t *testing.T) { } read := io.LimitReader(u.NewTimeSeededRand(), 1024*32) - root, err := imp.BuildDagFromReader(dservs[0], chunker.NewSizeSplitter(read, 512)) - if err != nil { - t.Fatal(err) - } + root := makeTestDAG(t, read, dservs[0]) - err = FetchGraph(context.TODO(), root.Cid(), dservs[1]) + err := FetchGraph(context.TODO(), root.Cid(), dservs[1]) if err != nil { t.Fatal(err) } @@ -254,14 +293,11 @@ func TestEnumerateChildren(t *testing.T) { ds := NewDAGService(bsi[0]) read := io.LimitReader(u.NewTimeSeededRand(), 1024*1024) - root, err := imp.BuildDagFromReader(ds, chunker.NewSizeSplitter(read, 512)) - if err != nil { - t.Fatal(err) - } + root := makeTestDAG(t, read, ds) set := cid.NewSet() - err = EnumerateChildren(context.Background(), ds.GetLinks, root.Cid(), set.Visit) + err := EnumerateChildren(context.Background(), ds.GetLinks, root.Cid(), set.Visit) if err != nil { t.Fatal(err) } From 69610ff207876e3aefab8576da397f5929b4fc0f Mon Sep 17 00:00:00 2001 From: matrushka Date: Wed, 14 Feb 2018 12:31:57 +0100 Subject: [PATCH 2224/3817] Modified keystore to ignore invalid key files inside the keystore directory. * Has calls the validateName function before checking if we have the file * List filters the returned list of file names by validateName. License: MIT Signed-off-by: matrushka This commit was moved from ipfs/go-ipfs-keystore@03d905ad9b8c228dd43b65ef194057311e8c394a --- keystore/keystore.go | 20 +++++++++++++- keystore/keystore_test.go | 57 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index 56dfd1b01..2db9a9b92 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -77,6 +77,10 @@ func (ks *FSKeystore) Has(name string) (bool, error) { return false, err } + if err := validateName(name); err != nil { + return false, err + } + return true, nil } @@ -149,5 +153,19 @@ func (ks *FSKeystore) List() ([]string, error) { return nil, err } - return dir.Readdirnames(0) + dirs, err := dir.Readdirnames(0) + if err != nil { + return nil, err + } + + var list []string + + for _, name := range dirs { + err := validateName(name) + if err == nil { + list = append(list, name) + } + } + + return list, err } diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index cf6281be2..f0c1b3105 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "math/rand" + "path/filepath" "sort" "testing" @@ -143,6 +144,62 @@ func TestKeystoreBasics(t *testing.T) { } } +func TestInvalidKeyFiles(t *testing.T) { + tdir, err := ioutil.TempDir("", "keystore-test") + + if err != nil { + t.Fatal(err) + } + + ks, err := NewFSKeystore(tdir) + if err != nil { + t.Fatal(err) + } + + key := privKeyOrFatal(t) + + bytes, err := key.Bytes() + if err != nil { + t.Fatal(err) + } + + err = ioutil.WriteFile(filepath.Join(ks.dir, "valid"), bytes, 0644) + if err != nil { + t.Fatal(err) + } + + err = ioutil.WriteFile(filepath.Join(ks.dir, ".invalid"), bytes, 0644) + if err != nil { + t.Fatal(err) + } + + l, err := ks.List() + if err != nil { + t.Fatal(err) + } + + sort.Strings(l) + if len(l) != 1 { + t.Fatal("wrong entry count") + } + + if l[0] != "valid" { + t.Fatal("wrong entries listed") + } + + exist, err := ks.Has("valid") + if !exist { + t.Fatal("should know it has a key named valid") + } + if err != nil { + t.Fatal(err) + } + + if exist, err = ks.Has(".invalid"); err == nil { + t.Fatal("shouldnt be able to put a key with a 'hidden' name") + } +} + func TestNonExistingKey(t *testing.T) { tdir, err := ioutil.TempDir("", "keystore-test") if err != nil { From 44c951bc50f1e7a381be40e95a912ef84678b923 Mon Sep 17 00:00:00 2001 From: matrushka Date: Wed, 14 Feb 2018 15:15:25 +0100 Subject: [PATCH 2225/3817] Removing the tmp directory after the TestInvalidKeyFiles test. License: MIT Signed-off-by: matrushka This commit was moved from ipfs/go-ipfs-keystore@fecc54b15b47300c7aa83575141a713e25168f11 --- keystore/keystore_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index f0c1b3105..0731f252b 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "math/rand" + "os" "path/filepath" "sort" "testing" @@ -151,6 +152,8 @@ func TestInvalidKeyFiles(t *testing.T) { t.Fatal(err) } + defer os.RemoveAll(tdir) + ks, err := NewFSKeystore(tdir) if err != nil { t.Fatal(err) From efdb6ad4e234b3daf6acd4ff9fdf1a1a7199f774 Mon Sep 17 00:00:00 2001 From: matrushka Date: Wed, 14 Feb 2018 18:16:29 +0100 Subject: [PATCH 2226/3817] Added logging for ignored keyfiles in keystore.List and minor improvements. License: MIT Signed-off-by: matrushka This commit was moved from ipfs/go-ipfs-keystore@c7dda3d68c8bf842f5614341c09d73fc7654433a --- keystore/keystore.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index 2db9a9b92..1f95d2e4f 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -7,9 +7,12 @@ import ( "path/filepath" "strings" + logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) +var log = logging.Logger("keystore") + // Keystore provides a key management interface type Keystore interface { // Has returns whether or not a key exist in the Keystore @@ -158,14 +161,16 @@ func (ks *FSKeystore) List() ([]string, error) { return nil, err } - var list []string + list := make([]string, 0) for _, name := range dirs { err := validateName(name) if err == nil { list = append(list, name) + } else { + log.Warningf("Ignoring the invalid keyfile: %s", name) } } - return list, err + return list, nil } From 00269014f66eae2dbd1406340686cea60ae6b366 Mon Sep 17 00:00:00 2001 From: matrushka Date: Wed, 14 Feb 2018 19:40:05 +0100 Subject: [PATCH 2227/3817] Handling requested changes. License: MIT Signed-off-by: matrushka This commit was moved from ipfs/go-ipfs-keystore@9b52e3f303aebf193a9ff15b489dc31d311223a5 --- keystore/keystore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index 1f95d2e4f..5dd433852 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -161,7 +161,7 @@ func (ks *FSKeystore) List() ([]string, error) { return nil, err } - list := make([]string, 0) + list := make([]string, 0, len(dirs)) for _, name := range dirs { err := validateName(name) From daac18ab25d00192d6bf0aa0e067f910ab0407bf Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 15 Feb 2018 17:36:55 +0100 Subject: [PATCH 2228/3817] Extract from go-ipfs: readme, travis, gx, license etc. This commit was moved from ipfs/go-ipfs-blockstore@b9697beb4525ca5161b3fc396863b460a00fefb9 --- blockstore/LICENSE | 21 ++++++ blockstore/Makefile | 18 ++++++ blockstore/README.md | 44 +++++++++++++ blockstore/arc_cache.go | 11 ++-- blockstore/arc_cache_test.go | 9 ++- blockstore/blockstore.go | 14 ++-- blockstore/blockstore_test.go | 14 ++-- blockstore/bloom_cache.go | 9 ++- blockstore/bloom_cache_test.go | 11 ++-- blockstore/caching.go | 5 +- blockstore/util/remove.go | 113 --------------------------------- 11 files changed, 117 insertions(+), 152 deletions(-) create mode 100644 blockstore/LICENSE create mode 100644 blockstore/Makefile create mode 100644 blockstore/README.md delete mode 100644 blockstore/util/remove.go diff --git a/blockstore/LICENSE b/blockstore/LICENSE new file mode 100644 index 000000000..e4224df5b --- /dev/null +++ b/blockstore/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 IPFS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/blockstore/Makefile b/blockstore/Makefile new file mode 100644 index 000000000..73f2841f6 --- /dev/null +++ b/blockstore/Makefile @@ -0,0 +1,18 @@ +all: deps +gx: + go get github.com/whyrusleeping/gx + go get github.com/whyrusleeping/gx-go +deps: gx + gx --verbose install --global + gx-go rewrite +test: deps + gx test -v -race -coverprofile=coverage.txt -covermode=atomic . +rw: + gx-go rewrite +rwundo: + gx-go rewrite --undo +publish: rwundo + gx publish +.PHONY: all gx deps test rw rwundo publish + + diff --git a/blockstore/README.md b/blockstore/README.md new file mode 100644 index 000000000..446a95e25 --- /dev/null +++ b/blockstore/README.md @@ -0,0 +1,44 @@ +# go-ipfs-blockstore + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![GoDoc](https://godoc.org/github.com/ipfs/go-ipfs-blockstore?status.svg)](https://godoc.org/github.com/ipfs/go-ipfs-blockstore) +[![Build Status](https://travis-ci.org/ipfs/go-ipfs-blockstore.svg?branch=master)](https://travis-ci.org/ipfs/go-ipfs-blockstore) + +> go-ipfs-blockstore implements a thin wrapper over a datastore, giving a clean interface for Getting and Putting block objects. + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Contribute](#contribute) +- [License](#license) + +## Install + +`go-ipfs-blockstore` works like a regular Go module: + +``` +> go get github.com/ipfs/go-ipfs-blockstore +``` + +## Usage + +``` +import "github.com/ipfs/go-ipfs-blockstore" +``` + +Check the [GoDoc documentation](https://godoc.org/github.com/ipfs/go-ipfs-blockstore) + +This module uses [Gx](https://github.com/whyrusleeping/gx) to manage dependencies. You can use `make all` to build it with the `gx` dependencies. + +## Contribute + +PRs accepted. + +Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +MIT © Protocol Labs, Inc. diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index e403bb96c..3a79c4e59 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -3,12 +3,11 @@ package blockstore import ( "context" - "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + lru "github.com/hashicorp/golang-lru" + blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + metrics "github.com/ipfs/go-metrics-interface" ) // arccache wraps a BlockStore with an Adaptive Replacement Cache (ARC) for diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 5a160d72b..84789e7e8 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -4,11 +4,10 @@ import ( "context" "testing" - "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - syncds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + syncds "github.com/ipfs/go-datastore/sync" ) var exampleBlock = blocks.NewBlock([]byte("foo")) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index f5bbb826a..748387c00 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -8,13 +8,13 @@ import ( "sync" "sync/atomic" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dsns "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/namespace" - dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" - logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + dsns "github.com/ipfs/go-datastore/namespace" + dsq "github.com/ipfs/go-datastore/query" + dshelp "github.com/ipfs/go-ipfs-ds-help" + logging "github.com/ipfs/go-log" ) var log = logging.Logger("blockstore") diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 757aa67e1..7def52eb0 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -6,13 +6,13 @@ import ( "fmt" "testing" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" - ds_sync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + dsq "github.com/ipfs/go-datastore/query" + ds_sync "github.com/ipfs/go-datastore/sync" + dshelp "github.com/ipfs/go-ipfs-ds-help" + u "github.com/ipfs/go-ipfs-util" ) func TestGetWhenKeyNotPresent(t *testing.T) { diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 5c2366207..7dd0bbe9f 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -5,11 +5,10 @@ import ( "sync/atomic" "time" - "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - - "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" - bloom "gx/ipfs/QmXqKGu7QzfRzFC4yd5aL9sThYx22vY163VGwmxfp5qGHk/bbloom" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + bloom "github.com/gxed/bbloom" + blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + metrics "github.com/ipfs/go-metrics-interface" ) // bloomCached returns a Blockstore that caches Has requests using a Bloom diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index a0385a99c..c165eee6e 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -1,17 +1,16 @@ package blockstore import ( + "context" "fmt" "sync" "testing" "time" - "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - - context "context" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" - syncds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + blocks "github.com/ipfs/go-block-format" + ds "github.com/ipfs/go-datastore" + dsq "github.com/ipfs/go-datastore/query" + syncds "github.com/ipfs/go-datastore/sync" ) func testBloomCached(ctx context.Context, bs Blockstore) (*bloomcache, error) { diff --git a/blockstore/caching.go b/blockstore/caching.go index 5d6f3bc85..798b84ce2 100644 --- a/blockstore/caching.go +++ b/blockstore/caching.go @@ -1,11 +1,10 @@ package blockstore import ( + "context" "errors" - context "context" - - "gx/ipfs/QmRg1gKTHzc3CZXSKzem8aR4E3TubFhbgXwfVuWnSK5CC5/go-metrics-interface" + metrics "github.com/ipfs/go-metrics-interface" ) // CacheOpts wraps options for CachedBlockStore(). diff --git a/blockstore/util/remove.go b/blockstore/util/remove.go deleted file mode 100644 index 0ce2b3fea..000000000 --- a/blockstore/util/remove.go +++ /dev/null @@ -1,113 +0,0 @@ -// Package blockstoreutil provides utility functions for Blockstores. -package blockstoreutil - -import ( - "fmt" - "io" - - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - - bs "github.com/ipfs/go-ipfs/blocks/blockstore" - "github.com/ipfs/go-ipfs/pin" -) - -// RemovedBlock is used to respresent the result of removing a block. -// If a block was removed successfully than the Error string will be -// empty. If a block could not be removed than Error will contain the -// reason the block could not be removed. If the removal was aborted -// due to a fatal error Hash will be be empty, Error will contain the -// reason, and no more results will be sent. -type RemovedBlock struct { - Hash string `json:",omitempty"` - Error string `json:",omitempty"` -} - -// RmBlocksOpts is used to wrap options for RmBlocks(). -type RmBlocksOpts struct { - Prefix string - Quiet bool - Force bool -} - -// RmBlocks removes the blocks provided in the cids slice. -// It returns a channel where objects of type RemovedBlock are placed, when -// not using the Quiet option. Block removal is asynchronous and will -// skip any pinned blocks. -func RmBlocks(blocks bs.GCBlockstore, pins pin.Pinner, cids []*cid.Cid, opts RmBlocksOpts) (<-chan interface{}, error) { - // make the channel large enough to hold any result to avoid - // blocking while holding the GCLock - out := make(chan interface{}, len(cids)) - go func() { - defer close(out) - - unlocker := blocks.GCLock() - defer unlocker.Unlock() - - stillOkay := FilterPinned(pins, out, cids) - - for _, c := range stillOkay { - err := blocks.DeleteBlock(c) - if err != nil && opts.Force && (err == bs.ErrNotFound || err == ds.ErrNotFound) { - // ignore non-existent blocks - } else if err != nil { - out <- &RemovedBlock{Hash: c.String(), Error: err.Error()} - } else if !opts.Quiet { - out <- &RemovedBlock{Hash: c.String()} - } - } - }() - return out, nil -} - -// FilterPinned takes a slice of Cids and returns it with the pinned Cids -// removed. If a Cid is pinned, it will place RemovedBlock objects in the given -// out channel, with an error which indicates that the Cid is pinned. -// This function is used in RmBlocks to filter out any blocks which are not -// to be removed (because they are pinned). -func FilterPinned(pins pin.Pinner, out chan<- interface{}, cids []*cid.Cid) []*cid.Cid { - stillOkay := make([]*cid.Cid, 0, len(cids)) - res, err := pins.CheckIfPinned(cids...) - if err != nil { - out <- &RemovedBlock{Error: fmt.Sprintf("pin check failed: %s", err)} - return nil - } - for _, r := range res { - if !r.Pinned() { - stillOkay = append(stillOkay, r.Key) - } else { - out <- &RemovedBlock{ - Hash: r.Key.String(), - Error: r.String(), - } - } - } - return stillOkay -} - -// ProcRmOutput takes a function which returns a result from RmBlocks or EOF if there is no input. -// It then writes to stdout/stderr according to the RemovedBlock object returned from the function. -func ProcRmOutput(next func() (interface{}, error), sout io.Writer, serr io.Writer) error { - someFailed := false - for { - res, err := next() - if err == io.EOF { - break - } else if err != nil { - return err - } - r := res.(*RemovedBlock) - if r.Hash == "" && r.Error != "" { - return fmt.Errorf("aborted: %s", r.Error) - } else if r.Error != "" { - someFailed = true - fmt.Fprintf(serr, "cannot remove %s: %s\n", r.Hash, r.Error) - } else { - fmt.Fprintf(sout, "removed %s\n", r.Hash) - } - } - if someFailed { - return fmt.Errorf("some blocks not removed") - } - return nil -} From 62f6f6d28517b253f2abc2a09dee8d78466b11ba Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 15 Feb 2018 18:03:41 +0100 Subject: [PATCH 2229/3817] Extract blocks/blockstore package to go-ipfs-blockstore This extracts the blocks/blockstore package and renames the blocks/blockstore/util package to /blocks/blockstoreutil (because util depends on Pin and I don't plan to extract Pin and its depedencies). The history of blocks/blockstore has been preserved. It has been gx'ed and imported. Imports have been rewritten accordingly and re-ordered. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-blockservice@993a75e9d0e6ee747da14aca145ab7a3640dfe81 --- blockservice/blockservice.go | 2 +- blockservice/blockservice_test.go | 4 ++-- blockservice/test/blocks_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index fb8144d88..a016a777d 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -9,11 +9,11 @@ import ( "fmt" "io" - "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" + blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index c7ed02dc0..6407949c7 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -3,13 +3,13 @@ package blockservice import ( "testing" - "github.com/ipfs/go-ipfs/blocks/blockstore" butil "github.com/ipfs/go-ipfs/blocks/blocksutil" offline "github.com/ipfs/go-ipfs/exchange/offline" - "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) func TestWriteThroughWorks(t *testing.T) { diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 3ff25366c..44d4d3ca0 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -7,15 +7,15 @@ import ( "testing" "time" - blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" . "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) func newObject(data []byte) blocks.Block { From 3ce59956f5fd3011a251220be832055d783c51ae Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 15 Feb 2018 18:03:41 +0100 Subject: [PATCH 2230/3817] Extract blocks/blockstore package to go-ipfs-blockstore This extracts the blocks/blockstore package and renames the blocks/blockstore/util package to /blocks/blockstoreutil (because util depends on Pin and I don't plan to extract Pin and its depedencies). The history of blocks/blockstore has been preserved. It has been gx'ed and imported. Imports have been rewritten accordingly and re-ordered. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-pinner@28826e121688b8729f16f9a458beda7c01ad45dd --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 1ed5f1672..c665e355e 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" - bstore "github.com/ipfs/go-ipfs/blocks/blockstore" bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" @@ -14,6 +13,7 @@ import ( dstore "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" + bstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index c0137a0d3..874689dd8 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,14 +5,14 @@ import ( "testing" "time" - "github.com/ipfs/go-ipfs/blocks/blockstore" bs "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" - "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + util "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 0eab73b35..e05806788 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,13 +5,13 @@ import ( "encoding/binary" "testing" - blockstore "github.com/ipfs/go-ipfs/blocks/blockstore" bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" + blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) From 7f0542a51f224c24896e8e3235cc9ac2523532da Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 15 Feb 2018 18:03:41 +0100 Subject: [PATCH 2231/3817] Extract blocks/blockstore package to go-ipfs-blockstore This extracts the blocks/blockstore package and renames the blocks/blockstore/util package to /blocks/blockstoreutil (because util depends on Pin and I don't plan to extract Pin and its depedencies). The history of blocks/blockstore has been preserved. It has been gx'ed and imported. Imports have been rewritten accordingly and re-ordered. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-mfs@362efc4892194e896249a3bcd21a9cb63d12777c --- mfs/mfs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 8a1f9ccf2..d7124bdf1 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,7 +14,6 @@ import ( "testing" "time" - bstore "github.com/ipfs/go-ipfs/blocks/blockstore" bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" importer "github.com/ipfs/go-ipfs/importer" @@ -26,6 +25,7 @@ import ( u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + bstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" chunker "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" From 28266fef50774305bde6426f3e4a4e79e034080b Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 15 Feb 2018 18:03:41 +0100 Subject: [PATCH 2232/3817] Extract blocks/blockstore package to go-ipfs-blockstore This extracts the blocks/blockstore package and renames the blocks/blockstore/util package to /blocks/blockstoreutil (because util depends on Pin and I don't plan to extract Pin and its depedencies). The history of blocks/blockstore has been preserved. It has been gx'ed and imported. Imports have been rewritten accordingly and re-ordered. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-filestore@9eab397f590adef739bce3093aaeb0f6e96e97d1 --- filestore/filestore.go | 3 +-- filestore/filestore_test.go | 4 ++-- filestore/fsrefstore.go | 2 +- filestore/util.go | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 69fb72014..f781d5262 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -10,10 +10,9 @@ package filestore import ( "context" - "github.com/ipfs/go-ipfs/blocks/blockstore" - dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" + blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 883df0d76..6b4065471 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,11 +7,11 @@ import ( "math/rand" "testing" - "github.com/ipfs/go-ipfs/blocks/blockstore" dag "github.com/ipfs/go-ipfs/merkledag" - posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" + blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 0f08771bf..84a02d426 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -7,13 +7,13 @@ import ( "os" "path/filepath" - "github.com/ipfs/go-ipfs/blocks/blockstore" pb "github.com/ipfs/go-ipfs/filestore/pb" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dsns "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/namespace" dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" + blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" diff --git a/filestore/util.go b/filestore/util.go index 98463271c..f923ec563 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -4,11 +4,11 @@ import ( "fmt" "sort" - "github.com/ipfs/go-ipfs/blocks/blockstore" pb "github.com/ipfs/go-ipfs/filestore/pb" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" + blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" ) From 40afc221e988dbf7f083c9f993583912bea67e92 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 15 Feb 2018 18:03:41 +0100 Subject: [PATCH 2233/3817] Extract blocks/blockstore package to go-ipfs-blockstore This extracts the blocks/blockstore package and renames the blocks/blockstore/util package to /blocks/blockstoreutil (because util depends on Pin and I don't plan to extract Pin and its depedencies). The history of blocks/blockstore has been preserved. It has been gx'ed and imported. Imports have been rewritten accordingly and re-ordered. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@4e36c0d5124c82304555b46b1e77e402518959e8 --- ipld/merkledag/test/utils.go | 2 +- ipld/merkledag/utils/utils.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index efaec1d45..479f8c27c 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -1,13 +1,13 @@ package mdutils import ( - "github.com/ipfs/go-ipfs/blocks/blockstore" bsrv "github.com/ipfs/go-ipfs/blockservice" "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 2d08ff0da..1e7c88325 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -4,7 +4,6 @@ import ( "context" "errors" - bstore "github.com/ipfs/go-ipfs/blocks/blockstore" bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" @@ -12,6 +11,7 @@ import ( ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" syncds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + bstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) From 8ece45053a9e89f3b859926f6b92e935b19fd419 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 15 Feb 2018 18:03:41 +0100 Subject: [PATCH 2234/3817] Extract blocks/blockstore package to go-ipfs-blockstore This extracts the blocks/blockstore package and renames the blocks/blockstore/util package to /blocks/blockstoreutil (because util depends on Pin and I don't plan to extract Pin and its depedencies). The history of blocks/blockstore has been preserved. It has been gx'ed and imported. Imports have been rewritten accordingly and re-ordered. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-exchange-offline@a259bbeff3771a15308e9ff969110bfbb745cb54 --- exchange/offline/offline.go | 4 ++-- exchange/offline/offline_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 35c38887f..4ed7d7dc3 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -5,11 +5,11 @@ package offline import ( "context" - "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 5bc2926c1..6535cd639 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -4,14 +4,14 @@ import ( "context" "testing" - "github.com/ipfs/go-ipfs/blocks/blockstore" "github.com/ipfs/go-ipfs/blocks/blocksutil" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" ds_sync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) func TestBlockReturnsErr(t *testing.T) { From db79904c9831d56b00ed7f4ca5f19c87a043cc47 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 15 Feb 2018 20:08:37 +0100 Subject: [PATCH 2235/3817] Golint merkledag_test.go License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@0a8f8e3076ef978f108e100f5775ed0f24462829 --- ipld/merkledag/merkledag_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 51083859f..c80fa80b7 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -133,7 +133,7 @@ func TestBatchFetchDupBlock(t *testing.T) { func makeTestDAG(t *testing.T, read io.Reader, ds ipld.DAGService) ipld.Node { p := make([]byte, 512) nodes := []*ProtoNode{} - var err error = nil + var err error _, err = read.Read(p) for err == nil { protoNode := NodeWithData(p) From ea900e4b6bd0ca65325a30c7f473e2b3db109611 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 15 Feb 2018 21:11:21 +0100 Subject: [PATCH 2236/3817] Add exchange.SessionExchange interface for exchanges that support sessions. Blockservice has an explicit dependency on bitswap so it can call NewSession. It should rely on the exchange interfaces though, not on specific implementations. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-blockservice@649e876d72284c90d6346b16921b061406f22dd7 --- blockservice/blockservice.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index fb8144d88..c4bedb7ac 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -11,7 +11,6 @@ import ( "github.com/ipfs/go-ipfs/blocks/blockstore" exchange "github.com/ipfs/go-ipfs/exchange" - bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" @@ -107,19 +106,22 @@ func (s *blockService) Exchange() exchange.Interface { return s.exchange } -// NewSession creates a bitswap session that allows for controlled exchange of -// wantlists to decrease the bandwidth overhead. +// NewSession creates a new session that allows for +// controlled exchange of wantlists to decrease the bandwidth overhead. +// If the current exchange is a SessionExchange, a new exchange +// session will be created. Otherwise, the current exchange will be used +// directly. func NewSession(ctx context.Context, bs BlockService) *Session { - exchange := bs.Exchange() - if bswap, ok := exchange.(*bitswap.Bitswap); ok { - ses := bswap.NewSession(ctx) + exch := bs.Exchange() + if sessEx, ok := exch.(exchange.SessionExchange); ok { + ses := sessEx.NewSession(ctx) return &Session{ ses: ses, bs: bs.Blockstore(), } } return &Session{ - ses: exchange, + ses: exch, bs: bs.Blockstore(), } } From 4851ffc75b5cefadbd58e7c3830e0076b51a22e8 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 15 Feb 2018 21:11:21 +0100 Subject: [PATCH 2237/3817] Add exchange.SessionExchange interface for exchanges that support sessions. Blockservice has an explicit dependency on bitswap so it can call NewSession. It should rely on the exchange interfaces though, not on specific implementations. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-exchange-interface@c4b9a06e0ac8dc92b3f7676e2f077ec786268504 --- exchange/interface.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index c1dd11624..e3971d06c 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -1,4 +1,4 @@ -// package exchange defines the IPFS exchange interface +// Package exchange defines the IPFS exchange interface package exchange import ( @@ -10,8 +10,7 @@ import ( cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) -// Any type that implements exchange.Interface may be used as an IPFS block -// exchange protocol. +// Interface defines the functionality of the IPFS block exchange protocol. type Interface interface { // type Exchanger interface Fetcher @@ -30,3 +29,10 @@ type Fetcher interface { GetBlock(context.Context, *cid.Cid) (blocks.Block, error) GetBlocks(context.Context, []*cid.Cid) (<-chan blocks.Block, error) } + +// SessionExchange is an exchange.Interface which supports +// sessions. +type SessionExchange interface { + Interface + NewSession(context.Context) Interface +} From bb9ff320cb705fc8e21cab32c9bd97fe0a38adb8 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 16 Feb 2018 00:09:50 +0100 Subject: [PATCH 2238/3817] Feat: Separate "path" from "path/resolver" Currently the "path" module does two very different things: * Defines how ipfs paths look like and provides tools to parse/split etc. * Provides a resolver to resolve paths. This moves the resolver stuff to `path/resolver` and leaves the path utilities in `path`. The result is that now the IPFS `path` package just defines what a path looks like and becomes a module that can be exported/re-used without problems. Currently there are circular dependency cycles (resolve_test -> merkledag/utils, merkledag->path), which the prevent the export of merkledag itself. License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-path@4dac8e9a4520f37964e9b649dabf66af2687c918 --- path/path.go | 33 +++++++++++++++++++-- path/{ => resolver}/resolver.go | 43 ++++++---------------------- path/{ => resolver}/resolver_test.go | 5 ++-- 3 files changed, 43 insertions(+), 38 deletions(-) rename path/{ => resolver}/resolver.go (83%) rename path/{ => resolver}/resolver_test.go (92%) diff --git a/path/path.go b/path/path.go index aeaeb5c7a..924aa5dc1 100644 --- a/path/path.go +++ b/path/path.go @@ -9,8 +9,15 @@ import ( cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) -// ErrBadPath is returned when a given path is incorrectly formatted -var ErrBadPath = errors.New("invalid 'ipfs ref' path") +var ( + // ErrBadPath is returned when a given path is incorrectly formatted + ErrBadPath = errors.New("invalid 'ipfs ref' path") + + // ErrNoComponents is used when Paths after a protocol + // do not contain at least one component + ErrNoComponents = errors.New( + "path must contain at least one component") +) // A Path represents an ipfs content path: // * //path/to/file @@ -149,3 +156,25 @@ func Join(pths []string) string { func SplitList(pth string) []string { return strings.Split(pth, "/") } + +// SplitAbsPath clean up and split fpath. It extracts the first component (which +// must be a Multihash) and return it separately. +func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) { + parts := fpath.Segments() + if parts[0] == "ipfs" { + parts = parts[1:] + } + + // if nothing, bail. + if len(parts) == 0 { + return nil, nil, ErrNoComponents + } + + c, err := cid.Decode(parts[0]) + // first element in the path is a cid + if err != nil { + return nil, nil, err + } + + return c, parts[1:], nil +} diff --git a/path/resolver.go b/path/resolver/resolver.go similarity index 83% rename from path/resolver.go rename to path/resolver/resolver.go index 64bdbf752..203fe9ce9 100644 --- a/path/resolver.go +++ b/path/resolver/resolver.go @@ -1,5 +1,5 @@ -// Package path implements utilities for resolving paths within ipfs. -package path +// Package resolver implements utilities for resolving paths within ipfs. +package resolver import ( "context" @@ -8,13 +8,14 @@ import ( "time" dag "github.com/ipfs/go-ipfs/merkledag" + path "github.com/ipfs/go-ipfs/path" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) -var log = logging.Logger("path") +var log = logging.Logger("pathresolv") // ErrNoComponents is used when Paths after a protocol // do not contain at least one component @@ -51,36 +52,10 @@ func NewBasicResolver(ds ipld.DAGService) *Resolver { } } -// SplitAbsPath clean up and split fpath. It extracts the first component (which -// must be a Multihash) and return it separately. -func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) { - - log.Debugf("Resolve: '%s'", fpath) - - parts := fpath.Segments() - if parts[0] == "ipfs" { - parts = parts[1:] - } - - // if nothing, bail. - if len(parts) == 0 { - return nil, nil, ErrNoComponents - } - - c, err := cid.Decode(parts[0]) - // first element in the path is a cid - if err != nil { - log.Debug("given path element is not a cid.\n") - return nil, nil, err - } - - return c, parts[1:], nil -} - // ResolveToLastNode walks the given path and returns the ipld.Node // referenced by the last element in it. -func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath Path) (ipld.Node, []string, error) { - c, p, err := SplitAbsPath(fpath) +func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (ipld.Node, []string, error) { + c, p, err := path.SplitAbsPath(fpath) if err != nil { return nil, nil, err } @@ -114,7 +89,7 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath Path) (ipld.Node // ResolvePath fetches the node for given path. It returns the last item // returned by ResolvePathComponents. -func (r *Resolver) ResolvePath(ctx context.Context, fpath Path) (ipld.Node, error) { +func (r *Resolver) ResolvePath(ctx context.Context, fpath path.Path) (ipld.Node, error) { // validate path if err := fpath.IsValid(); err != nil { return nil, err @@ -136,11 +111,11 @@ func ResolveSingle(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, names // ResolvePathComponents fetches the nodes for each segment of the given path. // It uses the first path component as a hash (key) of the first node, then // resolves all other components walking the links, with ResolveLinks. -func (r *Resolver) ResolvePathComponents(ctx context.Context, fpath Path) ([]ipld.Node, error) { +func (r *Resolver) ResolvePathComponents(ctx context.Context, fpath path.Path) ([]ipld.Node, error) { evt := log.EventBegin(ctx, "resolvePathComponents", logging.LoggableMap{"fpath": fpath}) defer evt.Done() - h, parts, err := SplitAbsPath(fpath) + h, parts, err := path.SplitAbsPath(fpath) if err != nil { evt.Append(logging.LoggableMap{"error": err.Error()}) return nil, err diff --git a/path/resolver_test.go b/path/resolver/resolver_test.go similarity index 92% rename from path/resolver_test.go rename to path/resolver/resolver_test.go index d741bbcf1..79a857cb6 100644 --- a/path/resolver_test.go +++ b/path/resolver/resolver_test.go @@ -1,4 +1,4 @@ -package path_test +package resolver_test import ( "context" @@ -8,6 +8,7 @@ import ( merkledag "github.com/ipfs/go-ipfs/merkledag" dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" + "github.com/ipfs/go-ipfs/path/resolver" util "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" @@ -53,7 +54,7 @@ func TestRecurivePathResolution(t *testing.T) { t.Fatal(err) } - resolver := path.NewBasicResolver(dagService) + resolver := resolver.NewBasicResolver(dagService) node, err := resolver.ResolvePath(ctx, p) if err != nil { t.Fatal(err) From e6484bcbc01de843df102d60f3ab06c3482815c5 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Fri, 23 Feb 2018 11:47:30 -0500 Subject: [PATCH 2239/3817] Add options for record count and timeout for resolving DHT paths License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@c488b3da68614c23a00f9a1a5eea31f43b458361 --- namesys/base.go | 7 +++-- namesys/dns.go | 11 ++----- namesys/interface.go | 13 ++------ namesys/ipns_validate_test.go | 31 +++++++++++++------ namesys/namesys.go | 19 +++++------- namesys/namesys_test.go | 8 +++-- namesys/opts.go | 29 ++++++++++++++++++ namesys/proquint.go | 11 ++----- namesys/pubsub.go | 11 ++----- namesys/pubsub_test.go | 4 +-- namesys/republisher/repub_test.go | 4 +-- namesys/resolve_test.go | 4 +-- namesys/routing.go | 49 +++++++++++++++++++++++++------ 13 files changed, 124 insertions(+), 77 deletions(-) create mode 100644 namesys/opts.go diff --git a/namesys/base.go b/namesys/base.go index 9953eddc5..574de9b7a 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -10,13 +10,14 @@ import ( type resolver interface { // resolveOnce looks up a name once (without recursion). - resolveOnce(ctx context.Context, name string) (value path.Path, err error) + resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (value path.Path, err error) } // resolve is a helper for implementing Resolver.ResolveN using resolveOnce. -func resolve(ctx context.Context, r resolver, name string, depth int, prefixes ...string) (path.Path, error) { +func resolve(ctx context.Context, r resolver, name string, opts *ResolveOpts, prefixes ...string) (path.Path, error) { + depth := opts.Depth for { - p, err := r.resolveOnce(ctx, name) + p, err := r.resolveOnce(ctx, name, opts) if err != nil { return "", err } diff --git a/namesys/dns.go b/namesys/dns.go index de5c98fdb..c58f0ea24 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -31,13 +31,8 @@ func newDNSResolver() resolver { } // Resolve implements Resolver. -func (r *DNSResolver) Resolve(ctx context.Context, name string) (path.Path, error) { - return r.ResolveN(ctx, name, DefaultDepthLimit) -} - -// ResolveN implements Resolver. -func (r *DNSResolver) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) { - return resolve(ctx, r, name, depth, "/ipns/") +func (r *DNSResolver) Resolve(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { + return resolve(ctx, r, name, opts, "/ipns/") } type lookupRes struct { @@ -48,7 +43,7 @@ type lookupRes struct { // resolveOnce implements resolver. // TXT records for a given domain name should contain a b58 // encoded multihash. -func (r *DNSResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) { +func (r *DNSResolver) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { segments := strings.SplitN(name, "/", 2) domain := segments[0] diff --git a/namesys/interface.go b/namesys/interface.go index 8097ac616..8ad84e57f 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -89,17 +89,8 @@ type Resolver interface { // // There is a default depth-limit to avoid infinite recursion. Most // users will be fine with this default limit, but if you need to - // adjust the limit you can use ResolveN. - Resolve(ctx context.Context, name string) (value path.Path, err error) - - // ResolveN performs a recursive lookup, returning the dereferenced - // path. The only difference from Resolve is that the depth limit - // is configurable. You can use DefaultDepthLimit, UnlimitedDepth, - // or a depth limit of your own choosing. - // - // Most users should use Resolve, since the default limit works well - // in most real-world situations. - ResolveN(ctx context.Context, name string, depth int) (value path.Path, err error) + // adjust the limit you can specify it as an option. + Resolve(ctx context.Context, name string, opts *ResolveOpts) (value path.Path, err error) } // Publisher is an object capable of publishing particular names. diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index 559a1b78a..7e3c285bc 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -115,7 +115,7 @@ func TestResolverValidation(t *testing.T) { } // Resolve entry - resp, err := resolver.resolveOnce(ctx, id.Pretty()) + resp, err := resolver.resolveOnce(ctx, id.Pretty(), DefaultResolveOpts()) if err != nil { t.Fatal(err) } @@ -136,9 +136,9 @@ func TestResolverValidation(t *testing.T) { } // Record should fail validation because entry is expired - _, err = resolver.resolveOnce(ctx, id.Pretty()) - if err != ErrExpiredRecord { - t.Fatal("ValidateIpnsRecord should have returned ErrExpiredRecord") + _, err = resolver.resolveOnce(ctx, id.Pretty(), DefaultResolveOpts()) + if err == nil { + t.Fatal("ValidateIpnsRecord should have returned error") } // Create IPNS record path with a different private key @@ -158,8 +158,8 @@ func TestResolverValidation(t *testing.T) { // Record should fail validation because public key defined by // ipns path doesn't match record signature - _, err = resolver.resolveOnce(ctx, id2.Pretty()) - if err != ErrSignature { + _, err = resolver.resolveOnce(ctx, id2.Pretty(), DefaultResolveOpts()) + if err == nil { t.Fatal("ValidateIpnsRecord should have failed signature verification") } @@ -176,7 +176,7 @@ func TestResolverValidation(t *testing.T) { // Record should fail validation because public key is not available // in peer store or on network - _, err = resolver.resolveOnce(ctx, id3.Pretty()) + _, err = resolver.resolveOnce(ctx, id3.Pretty(), DefaultResolveOpts()) if err == nil { t.Fatal("ValidateIpnsRecord should have failed because public key was not found") } @@ -191,7 +191,7 @@ func TestResolverValidation(t *testing.T) { // public key is available in the peer store by looking it up in // the DHT, which causes the DHT to fetch it and cache it in the // peer store - _, err = resolver.resolveOnce(ctx, id3.Pretty()) + _, err = resolver.resolveOnce(ctx, id3.Pretty(), DefaultResolveOpts()) if err != nil { t.Fatal(err) } @@ -263,7 +263,20 @@ func (m *mockValueStore) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey } func (m *mockValueStore) GetValues(ctx context.Context, k string, count int) ([]routing.RecvdVal, error) { - return m.r.GetValues(ctx, k, count) + vals, err := m.r.GetValues(ctx, k, count) + if err != nil { + return nil, err + } + valid := make([]routing.RecvdVal, 0, len(vals)) + for _, v := range vals { + rec := new(recordpb.Record) + rec.Key = proto.String(k) + rec.Value = v.Val + if err = m.Validator.VerifyRecord(rec); err == nil { + valid = append(valid, v) + } + } + return valid, nil } func (m *mockValueStore) PutValue(ctx context.Context, k string, d []byte) error { diff --git a/namesys/namesys.go b/namesys/namesys.go index c65492360..0a9cb5283 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -67,12 +67,7 @@ func AddPubsubNameSystem(ctx context.Context, ns NameSystem, host p2phost.Host, const DefaultResolverCacheTTL = time.Minute // Resolve implements Resolver. -func (ns *mpns) Resolve(ctx context.Context, name string) (path.Path, error) { - return ns.ResolveN(ctx, name, DefaultDepthLimit) -} - -// ResolveN implements Resolver. -func (ns *mpns) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) { +func (ns *mpns) Resolve(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { if strings.HasPrefix(name, "/ipfs/") { return path.ParsePath(name) } @@ -81,11 +76,11 @@ func (ns *mpns) ResolveN(ctx context.Context, name string, depth int) (path.Path return path.ParsePath("/ipfs/" + name) } - return resolve(ctx, ns, name, depth, "/ipns/") + return resolve(ctx, ns, name, opts, "/ipns/") } // resolveOnce implements resolver. -func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) { +func (ns *mpns) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { if !strings.HasPrefix(name, "/ipns/") { name = "/ipns/" + name } @@ -114,7 +109,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) if err == nil { res, ok := ns.resolvers["pubsub"] if ok { - p, err := res.resolveOnce(ctx, key) + p, err := res.resolveOnce(ctx, key, opts) if err == nil { return makePath(p) } @@ -122,7 +117,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) res, ok = ns.resolvers["dht"] if ok { - p, err := res.resolveOnce(ctx, key) + p, err := res.resolveOnce(ctx, key, opts) if err == nil { return makePath(p) } @@ -134,7 +129,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) if isd.IsDomain(key) { res, ok := ns.resolvers["dns"] if ok { - p, err := res.resolveOnce(ctx, key) + p, err := res.resolveOnce(ctx, key, opts) if err == nil { return makePath(p) } @@ -145,7 +140,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) res, ok := ns.resolvers["proquint"] if ok { - p, err := res.resolveOnce(ctx, key) + p, err := res.resolveOnce(ctx, key, opts) if err == nil { return makePath(p) } diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index e4f7af196..59d544fd8 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -19,8 +19,10 @@ type mockResolver struct { entries map[string]string } -func testResolution(t *testing.T, resolver Resolver, name string, depth int, expected string, expError error) { - p, err := resolver.ResolveN(context.Background(), name, depth) +func testResolution(t *testing.T, resolver Resolver, name string, depth uint, expected string, expError error) { + opts := DefaultResolveOpts() + opts.Depth = depth + p, err := resolver.Resolve(context.Background(), name, opts) if err != expError { t.Fatal(fmt.Errorf( "Expected %s with a depth of %d to have a '%s' error, but got '%s'", @@ -33,7 +35,7 @@ func testResolution(t *testing.T, resolver Resolver, name string, depth int, exp } } -func (r *mockResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) { +func (r *mockResolver) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { return path.ParsePath(r.entries[name]) } diff --git a/namesys/opts.go b/namesys/opts.go new file mode 100644 index 000000000..2807eecef --- /dev/null +++ b/namesys/opts.go @@ -0,0 +1,29 @@ +package namesys + +import ( + "time" +) + +// ResolveOpts specifies options for resolving an IPNS path +type ResolveOpts struct { + // Recursion depth limit + Depth uint + // The number of IPNS records to retrieve from the DHT + // (the best record is selected from this set) + DhtRecordCount uint + // The amount of time to wait for DHT records to be fetched + // and verified. A zero value indicates that there is no explicit + // timeout (although there is an implicit timeout due to dial + // timeouts within the DHT) + DhtTimeout time.Duration +} + +// DefaultResolveOpts returns the default options for resolving +// an IPNS path +func DefaultResolveOpts() *ResolveOpts { + return &ResolveOpts{ + Depth: DefaultDepthLimit, + DhtRecordCount: 16, + DhtTimeout: time.Minute, + } +} diff --git a/namesys/proquint.go b/namesys/proquint.go index 3a842f97a..48cb4013d 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -12,17 +12,12 @@ import ( type ProquintResolver struct{} // Resolve implements Resolver. -func (r *ProquintResolver) Resolve(ctx context.Context, name string) (path.Path, error) { - return r.ResolveN(ctx, name, DefaultDepthLimit) -} - -// ResolveN implements Resolver. -func (r *ProquintResolver) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) { - return resolve(ctx, r, name, depth, "/ipns/") +func (r *ProquintResolver) Resolve(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { + return resolve(ctx, r, name, opts, "/ipns/") } // resolveOnce implements resolver. Decodes the proquint string. -func (r *ProquintResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) { +func (r *ProquintResolver) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { ok, err := proquint.IsProquint(name) if err != nil || !ok { return "", errors.New("not a valid proquint string") diff --git a/namesys/pubsub.go b/namesys/pubsub.go index 20052cd78..856d0ff70 100644 --- a/namesys/pubsub.go +++ b/namesys/pubsub.go @@ -185,16 +185,11 @@ func (p *PubsubPublisher) publishRecord(ctx context.Context, k ci.PrivKey, value } // Resolve resolves a name through pubsub and default depth limit -func (r *PubsubResolver) Resolve(ctx context.Context, name string) (path.Path, error) { - return r.ResolveN(ctx, name, DefaultDepthLimit) +func (r *PubsubResolver) Resolve(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { + return resolve(ctx, r, name, opts, "/ipns/") } -// ResolveN resolves a name through pubsub with the specified depth limit -func (r *PubsubResolver) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) { - return resolve(ctx, r, name, depth, "/ipns/") -} - -func (r *PubsubResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) { +func (r *PubsubResolver) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { log.Debugf("PubsubResolve: resolve '%s'", name) // retrieve the public key once (for verifying messages) diff --git a/namesys/pubsub_test.go b/namesys/pubsub_test.go index e2cf15b5d..1d9873ccd 100644 --- a/namesys/pubsub_test.go +++ b/namesys/pubsub_test.go @@ -180,14 +180,14 @@ func TestPubsubPublishSubscribe(t *testing.T) { } func checkResolveNotFound(ctx context.Context, t *testing.T, i int, resolver Resolver, name string) { - _, err := resolver.Resolve(ctx, name) + _, err := resolver.Resolve(ctx, name, DefaultResolveOpts()) if err != ErrResolveFailed { t.Fatalf("[resolver %d] unexpected error: %s", i, err.Error()) } } func checkResolve(ctx context.Context, t *testing.T, i int, resolver Resolver, name string, val path.Path) { - xval, err := resolver.Resolve(ctx, name) + xval, err := resolver.Resolve(ctx, name, DefaultResolveOpts()) if err != nil { t.Fatalf("[resolver %d] resolve failed: %s", i, err.Error()) } diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 580b708de..3b20a9a53 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -98,7 +98,7 @@ func verifyResolution(nodes []*core.IpfsNode, key string, exp path.Path) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() for _, n := range nodes { - val, err := n.Namesys.Resolve(ctx, key) + val, err := n.Namesys.Resolve(ctx, key, namesys.DefaultResolveOpts()) if err != nil { return err } @@ -114,7 +114,7 @@ func verifyResolutionFails(nodes []*core.IpfsNode, key string) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() for _, n := range nodes { - _, err := n.Namesys.Resolve(ctx, key) + _, err := n.Namesys.Resolve(ctx, key, namesys.DefaultResolveOpts()) if err == nil { return errors.New("expected resolution to fail") } diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index a55d5a4d4..28e9c80bb 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -40,7 +40,7 @@ func TestRoutingResolve(t *testing.T) { t.Fatal(err) } - res, err := resolver.Resolve(context.Background(), pid.Pretty()) + res, err := resolver.Resolve(context.Background(), pid.Pretty(), DefaultResolveOpts()) if err != nil { t.Fatal(err) } @@ -125,7 +125,7 @@ func TestPrexistingRecord(t *testing.T) { } func verifyCanResolve(r Resolver, name string, exp path.Path) error { - res, err := r.Resolve(context.Background(), name) + res, err := r.Resolve(context.Background(), name, DefaultResolveOpts()) if err != nil { return err } diff --git a/namesys/routing.go b/namesys/routing.go index effb3fa01..ca87292d7 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -104,24 +104,26 @@ func NewRoutingResolver(route routing.ValueStore, cachesize int) *routingResolve } // Resolve implements Resolver. -func (r *routingResolver) Resolve(ctx context.Context, name string) (path.Path, error) { - return r.ResolveN(ctx, name, DefaultDepthLimit) -} - -// ResolveN implements Resolver. -func (r *routingResolver) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) { - return resolve(ctx, r, name, depth, "/ipns/") +func (r *routingResolver) Resolve(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { + return resolve(ctx, r, name, opts, "/ipns/") } // resolveOnce implements resolver. Uses the IPFS routing system to // resolve SFS-like names. -func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) { +func (r *routingResolver) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { log.Debugf("RoutingResolver resolving %s", name) cached, ok := r.cacheGet(name) if ok { return cached, nil } + if opts.DhtTimeout != 0 { + // Resolution must complete within the timeout + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, opts.DhtTimeout) + defer cancel() + } + name = strings.TrimPrefix(name, "/ipns/") hash, err := mh.FromB58String(name) if err != nil { @@ -151,7 +153,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa // Note that the DHT will call the ipns validator when retrieving // the value, which in turn verifies the ipns record signature _, ipnsKey := IpnsKeysForID(pid) - val, err := r.routing.GetValue(ctx, ipnsKey) + val, err := r.getValue(ctx, ipnsKey, opts) if err != nil { log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err) return "", err @@ -184,6 +186,35 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa } } +func (r *routingResolver) getValue(ctx context.Context, ipnsKey string, opts *ResolveOpts) ([]byte, error) { + // Get specified number of values from the DHT + vals, err := r.routing.GetValues(ctx, ipnsKey, int(opts.DhtRecordCount)) + if err != nil { + return nil, err + } + + // Select the best value + recs := make([][]byte, 0, len(vals)) + for _, v := range vals { + if v.Val != nil { + recs = append(recs, v.Val) + } + } + + i, err := IpnsSelectorFunc(ipnsKey, recs) + if err != nil { + return nil, err + } + + best := recs[i] + if best == nil { + log.Errorf("GetValues %s yielded record with nil value", ipnsKey) + return nil, routing.ErrNotFound + } + + return best, nil +} + func checkEOL(e *pb.IpnsEntry) (time.Time, bool) { if e.GetValidityType() == pb.IpnsEntry_EOL { eol, err := u.ParseRFC3339(string(e.GetValidity())) From c736a67e3b561a8a63df741cc976ef407af1ca22 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 26 Feb 2018 10:27:06 -0800 Subject: [PATCH 2240/3817] remove a spurious debug message License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@46ed36bea8fddedb806dafeeba3ca6a916d6bd08 --- namesys/republisher/repub.go | 1 - 1 file changed, 1 deletion(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 7bb54fcd4..ae8c5e8d1 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -138,7 +138,6 @@ func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) erro eol := time.Now().Add(rp.RecordLifetime) err = namesys.PutRecordToRouting(ctx, priv, p, seq, eol, rp.r, id) if err != nil { - println("put record to routing error: " + err.Error()) return err } From 382e6177a71836ae82196e69094e186ff9496d50 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 27 Feb 2018 18:37:50 +0100 Subject: [PATCH 2241/3817] merkledag_test: address #4704 review comments License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@1bcb27fd4ad7c8e38b43ac96c2e2f02480531da9 --- ipld/merkledag/merkledag_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index c80fa80b7..ec7465162 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -134,7 +134,7 @@ func makeTestDAG(t *testing.T, read io.Reader, ds ipld.DAGService) ipld.Node { p := make([]byte, 512) nodes := []*ProtoNode{} var err error - _, err = read.Read(p) + _, err = io.ReadFull(read, p) for err == nil { protoNode := NodeWithData(p) nodes = append(nodes, protoNode) @@ -225,8 +225,6 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { if !ok { errs <- ErrNotProtobuf } - _ = firstpb - _ = expected read := makeTestDAGReader(t, firstpb, dagservs[i]) datagot, err := ioutil.ReadAll(read) if err != nil { From 9a14fc686b635ea25780a96c7ff78acaf188d047 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 27 Feb 2018 18:40:02 +0100 Subject: [PATCH 2242/3817] merkledag_test.go: Handle short reads in makeTestDAG License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@90a5c15f039aafc1dd4e1175c98bcca7cb36b179 --- ipld/merkledag/merkledag_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index ec7465162..cae3566fc 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -134,7 +134,10 @@ func makeTestDAG(t *testing.T, read io.Reader, ds ipld.DAGService) ipld.Node { p := make([]byte, 512) nodes := []*ProtoNode{} var err error - _, err = io.ReadFull(read, p) + n, err = io.ReadFull(read, p) + if n != len(p) { + t.Fatal("should have read 512 bytes from the reader") + } for err == nil { protoNode := NodeWithData(p) nodes = append(nodes, protoNode) From 73a4a60693d3a9baad33ddb60e437c3aec396625 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 27 Feb 2018 20:38:49 +0100 Subject: [PATCH 2243/3817] fix with newer go-libp2p-record This commit was moved from ipfs/go-ipfs-routing@a60359e9107664fd2380211544160f35be180f7a --- routing/offline/offline.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 7384c6d79..f008e2529 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -42,10 +42,7 @@ type offlineRouting struct { } func (c *offlineRouting) PutValue(ctx context.Context, key string, val []byte) error { - rec, err := record.MakePutRecord(c.sk, key, val, false) - if err != nil { - return err - } + rec := record.MakePutRecord(key, val) data, err := proto.Marshal(rec) if err != nil { return err From cd0aeff717b341b50b959af9123077cf5098bac1 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 28 Feb 2018 16:57:24 -0500 Subject: [PATCH 2244/3817] Use variadic options License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@2e2974541ea1a61d97d1712705758dfd80690d5f --- namesys/base.go | 9 ++-- namesys/dns.go | 7 ++-- namesys/dns_test.go | 38 +++++++++-------- namesys/interface.go | 14 +------ namesys/ipns_validate_test.go | 11 ++--- namesys/namesys.go | 15 +++---- namesys/namesys_test.go | 17 ++++---- namesys/opts.go | 29 ------------- namesys/opts/opts.go | 68 +++++++++++++++++++++++++++++++ namesys/proquint.go | 7 ++-- namesys/pubsub.go | 7 ++-- namesys/pubsub_test.go | 4 +- namesys/republisher/repub_test.go | 4 +- namesys/resolve_test.go | 4 +- namesys/routing.go | 17 ++++---- 15 files changed, 144 insertions(+), 107 deletions(-) delete mode 100644 namesys/opts.go create mode 100644 namesys/opts/opts.go diff --git a/namesys/base.go b/namesys/base.go index 574de9b7a..a301a5a61 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -5,19 +5,20 @@ import ( context "context" + opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" ) type resolver interface { // resolveOnce looks up a name once (without recursion). - resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (value path.Path, err error) + resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (value path.Path, err error) } // resolve is a helper for implementing Resolver.ResolveN using resolveOnce. -func resolve(ctx context.Context, r resolver, name string, opts *ResolveOpts, prefixes ...string) (path.Path, error) { - depth := opts.Depth +func resolve(ctx context.Context, r resolver, name string, options *opts.ResolveOpts, prefixes ...string) (path.Path, error) { + depth := options.Depth for { - p, err := r.resolveOnce(ctx, name, opts) + p, err := r.resolveOnce(ctx, name, options) if err != nil { return "", err } diff --git a/namesys/dns.go b/namesys/dns.go index c58f0ea24..6d74e5221 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -6,6 +6,7 @@ import ( "net" "strings" + opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) @@ -31,8 +32,8 @@ func newDNSResolver() resolver { } // Resolve implements Resolver. -func (r *DNSResolver) Resolve(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { - return resolve(ctx, r, name, opts, "/ipns/") +func (r *DNSResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) { + return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/") } type lookupRes struct { @@ -43,7 +44,7 @@ type lookupRes struct { // resolveOnce implements resolver. // TXT records for a given domain name should contain a b58 // encoded multihash. -func (r *DNSResolver) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { +func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) { segments := strings.SplitN(name, "/", 2) domain := segments[0] diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 9b11845ac..1a3110c9b 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -3,6 +3,8 @@ package namesys import ( "fmt" "testing" + + opts "github.com/ipfs/go-ipfs/namesys/opts" ) type mockDNS struct { @@ -128,33 +130,33 @@ func newMockDNS() *mockDNS { func TestDNSResolution(t *testing.T) { mock := newMockDNS() r := &DNSResolver{lookupTXT: mock.lookupTXT} - testResolution(t, r, "multihash.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) - testResolution(t, r, "ipfs.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) - testResolution(t, r, "dipfs.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) - testResolution(t, r, "dns1.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "multihash.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "ipfs.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "dipfs.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "dns1.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) testResolution(t, r, "dns1.example.com", 1, "/ipns/ipfs.example.com", ErrResolveRecursion) - testResolution(t, r, "dns2.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "dns2.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) testResolution(t, r, "dns2.example.com", 1, "/ipns/dns1.example.com", ErrResolveRecursion) testResolution(t, r, "dns2.example.com", 2, "/ipns/ipfs.example.com", ErrResolveRecursion) - testResolution(t, r, "multi.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "multi.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) testResolution(t, r, "multi.example.com", 1, "/ipns/dns1.example.com", ErrResolveRecursion) testResolution(t, r, "multi.example.com", 2, "/ipns/ipfs.example.com", ErrResolveRecursion) - testResolution(t, r, "equals.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/=equals", nil) + testResolution(t, r, "equals.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/=equals", nil) testResolution(t, r, "loop1.example.com", 1, "/ipns/loop2.example.com", ErrResolveRecursion) testResolution(t, r, "loop1.example.com", 2, "/ipns/loop1.example.com", ErrResolveRecursion) testResolution(t, r, "loop1.example.com", 3, "/ipns/loop2.example.com", ErrResolveRecursion) - testResolution(t, r, "loop1.example.com", DefaultDepthLimit, "/ipns/loop1.example.com", ErrResolveRecursion) + testResolution(t, r, "loop1.example.com", opts.DefaultDepthLimit, "/ipns/loop1.example.com", ErrResolveRecursion) testResolution(t, r, "dloop1.example.com", 1, "/ipns/loop2.example.com", ErrResolveRecursion) testResolution(t, r, "dloop1.example.com", 2, "/ipns/loop1.example.com", ErrResolveRecursion) testResolution(t, r, "dloop1.example.com", 3, "/ipns/loop2.example.com", ErrResolveRecursion) - testResolution(t, r, "dloop1.example.com", DefaultDepthLimit, "/ipns/loop1.example.com", ErrResolveRecursion) - testResolution(t, r, "bad.example.com", DefaultDepthLimit, "", ErrResolveFailed) - testResolution(t, r, "withsegment.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment", nil) - testResolution(t, r, "withrecsegment.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub", nil) - testResolution(t, r, "withsegment.example.com/test1", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/test1", nil) - testResolution(t, r, "withrecsegment.example.com/test2", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub/test2", nil) - testResolution(t, r, "withrecsegment.example.com/test3/", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub/test3/", nil) - testResolution(t, r, "withtrailingrec.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/", nil) - testResolution(t, r, "double.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) - testResolution(t, r, "conflict.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjE", nil) + testResolution(t, r, "dloop1.example.com", opts.DefaultDepthLimit, "/ipns/loop1.example.com", ErrResolveRecursion) + testResolution(t, r, "bad.example.com", opts.DefaultDepthLimit, "", ErrResolveFailed) + testResolution(t, r, "withsegment.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment", nil) + testResolution(t, r, "withrecsegment.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub", nil) + testResolution(t, r, "withsegment.example.com/test1", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/test1", nil) + testResolution(t, r, "withrecsegment.example.com/test2", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub/test2", nil) + testResolution(t, r, "withrecsegment.example.com/test3/", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub/test3/", nil) + testResolution(t, r, "withtrailingrec.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/", nil) + testResolution(t, r, "double.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "conflict.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjE", nil) } diff --git a/namesys/interface.go b/namesys/interface.go index 8ad84e57f..db2aa0a22 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,21 +35,11 @@ import ( context "context" + opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) -const ( - // DefaultDepthLimit is the default depth limit used by Resolve. - DefaultDepthLimit = 32 - - // UnlimitedDepth allows infinite recursion in ResolveN. You - // probably don't want to use this, but it's here if you absolutely - // trust resolution to eventually complete and can't put an upper - // limit on how many steps it will take. - UnlimitedDepth = 0 -) - // ErrResolveFailed signals an error when attempting to resolve. var ErrResolveFailed = errors.New("Could not resolve name.") @@ -90,7 +80,7 @@ type Resolver interface { // There is a default depth-limit to avoid infinite recursion. Most // users will be fine with this default limit, but if you need to // adjust the limit you can specify it as an option. - Resolve(ctx context.Context, name string, opts *ResolveOpts) (value path.Path, err error) + Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (value path.Path, err error) } // Publisher is an object capable of publishing particular names. diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index 7e3c285bc..9e72b9fe5 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" @@ -115,7 +116,7 @@ func TestResolverValidation(t *testing.T) { } // Resolve entry - resp, err := resolver.resolveOnce(ctx, id.Pretty(), DefaultResolveOpts()) + resp, err := resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts()) if err != nil { t.Fatal(err) } @@ -136,7 +137,7 @@ func TestResolverValidation(t *testing.T) { } // Record should fail validation because entry is expired - _, err = resolver.resolveOnce(ctx, id.Pretty(), DefaultResolveOpts()) + _, err = resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts()) if err == nil { t.Fatal("ValidateIpnsRecord should have returned error") } @@ -158,7 +159,7 @@ func TestResolverValidation(t *testing.T) { // Record should fail validation because public key defined by // ipns path doesn't match record signature - _, err = resolver.resolveOnce(ctx, id2.Pretty(), DefaultResolveOpts()) + _, err = resolver.resolveOnce(ctx, id2.Pretty(), opts.DefaultResolveOpts()) if err == nil { t.Fatal("ValidateIpnsRecord should have failed signature verification") } @@ -176,7 +177,7 @@ func TestResolverValidation(t *testing.T) { // Record should fail validation because public key is not available // in peer store or on network - _, err = resolver.resolveOnce(ctx, id3.Pretty(), DefaultResolveOpts()) + _, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts()) if err == nil { t.Fatal("ValidateIpnsRecord should have failed because public key was not found") } @@ -191,7 +192,7 @@ func TestResolverValidation(t *testing.T) { // public key is available in the peer store by looking it up in // the DHT, which causes the DHT to fetch it and cache it in the // peer store - _, err = resolver.resolveOnce(ctx, id3.Pretty(), DefaultResolveOpts()) + _, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts()) if err != nil { t.Fatal(err) } diff --git a/namesys/namesys.go b/namesys/namesys.go index 0a9cb5283..e47d433a3 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,6 +7,7 @@ import ( "sync" "time" + opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" p2phost "gx/ipfs/QmNmJZL7FQySMtE2BQuLMuZg2EB2CLEunJJUSVSc9YnnbV/go-libp2p-host" @@ -67,7 +68,7 @@ func AddPubsubNameSystem(ctx context.Context, ns NameSystem, host p2phost.Host, const DefaultResolverCacheTTL = time.Minute // Resolve implements Resolver. -func (ns *mpns) Resolve(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { +func (ns *mpns) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) { if strings.HasPrefix(name, "/ipfs/") { return path.ParsePath(name) } @@ -76,11 +77,11 @@ func (ns *mpns) Resolve(ctx context.Context, name string, opts *ResolveOpts) (pa return path.ParsePath("/ipfs/" + name) } - return resolve(ctx, ns, name, opts, "/ipns/") + return resolve(ctx, ns, name, opts.ProcessOpts(options), "/ipns/") } // resolveOnce implements resolver. -func (ns *mpns) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { +func (ns *mpns) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) { if !strings.HasPrefix(name, "/ipns/") { name = "/ipns/" + name } @@ -109,7 +110,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) if err == nil { res, ok := ns.resolvers["pubsub"] if ok { - p, err := res.resolveOnce(ctx, key, opts) + p, err := res.resolveOnce(ctx, key, options) if err == nil { return makePath(p) } @@ -117,7 +118,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) res, ok = ns.resolvers["dht"] if ok { - p, err := res.resolveOnce(ctx, key, opts) + p, err := res.resolveOnce(ctx, key, options) if err == nil { return makePath(p) } @@ -129,7 +130,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) if isd.IsDomain(key) { res, ok := ns.resolvers["dns"] if ok { - p, err := res.resolveOnce(ctx, key, opts) + p, err := res.resolveOnce(ctx, key, options) if err == nil { return makePath(p) } @@ -140,7 +141,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) res, ok := ns.resolvers["proquint"] if ok { - p, err := res.resolveOnce(ctx, key, opts) + p, err := res.resolveOnce(ctx, key, options) if err == nil { return makePath(p) } diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 59d544fd8..7cc4b780c 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -6,6 +6,7 @@ import ( context "context" + opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/unixfs" @@ -20,9 +21,7 @@ type mockResolver struct { } func testResolution(t *testing.T, resolver Resolver, name string, depth uint, expected string, expError error) { - opts := DefaultResolveOpts() - opts.Depth = depth - p, err := resolver.Resolve(context.Background(), name, opts) + p, err := resolver.Resolve(context.Background(), name, opts.Depth(depth)) if err != expError { t.Fatal(fmt.Errorf( "Expected %s with a depth of %d to have a '%s' error, but got '%s'", @@ -35,7 +34,7 @@ func testResolution(t *testing.T, resolver Resolver, name string, depth uint, ex } } -func (r *mockResolver) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { +func (r *mockResolver) resolveOnce(ctx context.Context, name string, opts *opts.ResolveOpts) (path.Path, error) { return path.ParsePath(r.entries[name]) } @@ -65,14 +64,14 @@ func TestNamesysResolution(t *testing.T) { }, } - testResolution(t, r, "Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) - testResolution(t, r, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) - testResolution(t, r, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) + testResolution(t, r, "Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", opts.DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) + testResolution(t, r, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", opts.DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) + testResolution(t, r, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", opts.DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) testResolution(t, r, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", 1, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", ErrResolveRecursion) - testResolution(t, r, "/ipns/ipfs.io", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) + testResolution(t, r, "/ipns/ipfs.io", opts.DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) testResolution(t, r, "/ipns/ipfs.io", 1, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", ErrResolveRecursion) testResolution(t, r, "/ipns/ipfs.io", 2, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", ErrResolveRecursion) - testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) + testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", opts.DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 1, "/ipns/ipfs.io", ErrResolveRecursion) testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 2, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", ErrResolveRecursion) testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 3, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", ErrResolveRecursion) diff --git a/namesys/opts.go b/namesys/opts.go deleted file mode 100644 index 2807eecef..000000000 --- a/namesys/opts.go +++ /dev/null @@ -1,29 +0,0 @@ -package namesys - -import ( - "time" -) - -// ResolveOpts specifies options for resolving an IPNS path -type ResolveOpts struct { - // Recursion depth limit - Depth uint - // The number of IPNS records to retrieve from the DHT - // (the best record is selected from this set) - DhtRecordCount uint - // The amount of time to wait for DHT records to be fetched - // and verified. A zero value indicates that there is no explicit - // timeout (although there is an implicit timeout due to dial - // timeouts within the DHT) - DhtTimeout time.Duration -} - -// DefaultResolveOpts returns the default options for resolving -// an IPNS path -func DefaultResolveOpts() *ResolveOpts { - return &ResolveOpts{ - Depth: DefaultDepthLimit, - DhtRecordCount: 16, - DhtTimeout: time.Minute, - } -} diff --git a/namesys/opts/opts.go b/namesys/opts/opts.go new file mode 100644 index 000000000..ff7b44c51 --- /dev/null +++ b/namesys/opts/opts.go @@ -0,0 +1,68 @@ +package namesys_opts + +import ( + "time" +) + +const ( + // DefaultDepthLimit is the default depth limit used by Resolve. + DefaultDepthLimit = 32 + + // UnlimitedDepth allows infinite recursion in Resolve. You + // probably don't want to use this, but it's here if you absolutely + // trust resolution to eventually complete and can't put an upper + // limit on how many steps it will take. + UnlimitedDepth = 0 +) + +// ResolveOpts specifies options for resolving an IPNS path +type ResolveOpts struct { + // Recursion depth limit + Depth uint + // The number of IPNS records to retrieve from the DHT + // (the best record is selected from this set) + DhtRecordCount uint + // The amount of time to wait for DHT records to be fetched + // and verified. A zero value indicates that there is no explicit + // timeout (although there is an implicit timeout due to dial + // timeouts within the DHT) + DhtTimeout time.Duration +} + +// DefaultResolveOpts returns the default options for resolving +// an IPNS path +func DefaultResolveOpts() *ResolveOpts { + return &ResolveOpts{ + Depth: DefaultDepthLimit, + DhtRecordCount: 16, + DhtTimeout: time.Minute, + } +} + +type ResolveOpt func(*ResolveOpts) + +func Depth(depth uint) ResolveOpt { + return func(o *ResolveOpts) { + o.Depth = depth + } +} + +func DhtRecordCount(count uint) ResolveOpt { + return func(o *ResolveOpts) { + o.DhtRecordCount = count + } +} + +func DhtTimeout(timeout time.Duration) ResolveOpt { + return func(o *ResolveOpts) { + o.DhtTimeout = timeout + } +} + +func ProcessOpts(opts []ResolveOpt) *ResolveOpts { + rsopts := DefaultResolveOpts() + for _, option := range opts { + option(rsopts) + } + return rsopts +} diff --git a/namesys/proquint.go b/namesys/proquint.go index 48cb4013d..2c61c98d3 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -5,6 +5,7 @@ import ( context "context" + opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) @@ -12,12 +13,12 @@ import ( type ProquintResolver struct{} // Resolve implements Resolver. -func (r *ProquintResolver) Resolve(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { - return resolve(ctx, r, name, opts, "/ipns/") +func (r *ProquintResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) { + return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/") } // resolveOnce implements resolver. Decodes the proquint string. -func (r *ProquintResolver) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { +func (r *ProquintResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) { ok, err := proquint.IsProquint(name) if err != nil || !ok { return "", errors.New("not a valid proquint string") diff --git a/namesys/pubsub.go b/namesys/pubsub.go index 856d0ff70..abbf8f0cc 100644 --- a/namesys/pubsub.go +++ b/namesys/pubsub.go @@ -8,6 +8,7 @@ import ( "sync" "time" + opts "github.com/ipfs/go-ipfs/namesys/opts" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" @@ -185,11 +186,11 @@ func (p *PubsubPublisher) publishRecord(ctx context.Context, k ci.PrivKey, value } // Resolve resolves a name through pubsub and default depth limit -func (r *PubsubResolver) Resolve(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { - return resolve(ctx, r, name, opts, "/ipns/") +func (r *PubsubResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) { + return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/") } -func (r *PubsubResolver) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { +func (r *PubsubResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) { log.Debugf("PubsubResolve: resolve '%s'", name) // retrieve the public key once (for verifying messages) diff --git a/namesys/pubsub_test.go b/namesys/pubsub_test.go index 1d9873ccd..e2cf15b5d 100644 --- a/namesys/pubsub_test.go +++ b/namesys/pubsub_test.go @@ -180,14 +180,14 @@ func TestPubsubPublishSubscribe(t *testing.T) { } func checkResolveNotFound(ctx context.Context, t *testing.T, i int, resolver Resolver, name string) { - _, err := resolver.Resolve(ctx, name, DefaultResolveOpts()) + _, err := resolver.Resolve(ctx, name) if err != ErrResolveFailed { t.Fatalf("[resolver %d] unexpected error: %s", i, err.Error()) } } func checkResolve(ctx context.Context, t *testing.T, i int, resolver Resolver, name string, val path.Path) { - xval, err := resolver.Resolve(ctx, name, DefaultResolveOpts()) + xval, err := resolver.Resolve(ctx, name) if err != nil { t.Fatalf("[resolver %d] resolve failed: %s", i, err.Error()) } diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 3b20a9a53..580b708de 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -98,7 +98,7 @@ func verifyResolution(nodes []*core.IpfsNode, key string, exp path.Path) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() for _, n := range nodes { - val, err := n.Namesys.Resolve(ctx, key, namesys.DefaultResolveOpts()) + val, err := n.Namesys.Resolve(ctx, key) if err != nil { return err } @@ -114,7 +114,7 @@ func verifyResolutionFails(nodes []*core.IpfsNode, key string) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() for _, n := range nodes { - _, err := n.Namesys.Resolve(ctx, key, namesys.DefaultResolveOpts()) + _, err := n.Namesys.Resolve(ctx, key) if err == nil { return errors.New("expected resolution to fail") } diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 28e9c80bb..a55d5a4d4 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -40,7 +40,7 @@ func TestRoutingResolve(t *testing.T) { t.Fatal(err) } - res, err := resolver.Resolve(context.Background(), pid.Pretty(), DefaultResolveOpts()) + res, err := resolver.Resolve(context.Background(), pid.Pretty()) if err != nil { t.Fatal(err) } @@ -125,7 +125,7 @@ func TestPrexistingRecord(t *testing.T) { } func verifyCanResolve(r Resolver, name string, exp path.Path) error { - res, err := r.Resolve(context.Background(), name, DefaultResolveOpts()) + res, err := r.Resolve(context.Background(), name) if err != nil { return err } diff --git a/namesys/routing.go b/namesys/routing.go index ca87292d7..29b26cdbd 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,6 +5,7 @@ import ( "strings" "time" + opts "github.com/ipfs/go-ipfs/namesys/opts" pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" @@ -104,23 +105,23 @@ func NewRoutingResolver(route routing.ValueStore, cachesize int) *routingResolve } // Resolve implements Resolver. -func (r *routingResolver) Resolve(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { - return resolve(ctx, r, name, opts, "/ipns/") +func (r *routingResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) { + return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/") } // resolveOnce implements resolver. Uses the IPFS routing system to // resolve SFS-like names. -func (r *routingResolver) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) { +func (r *routingResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) { log.Debugf("RoutingResolver resolving %s", name) cached, ok := r.cacheGet(name) if ok { return cached, nil } - if opts.DhtTimeout != 0 { + if options.DhtTimeout != 0 { // Resolution must complete within the timeout var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, opts.DhtTimeout) + ctx, cancel = context.WithTimeout(ctx, options.DhtTimeout) defer cancel() } @@ -153,7 +154,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, opts *Re // Note that the DHT will call the ipns validator when retrieving // the value, which in turn verifies the ipns record signature _, ipnsKey := IpnsKeysForID(pid) - val, err := r.getValue(ctx, ipnsKey, opts) + val, err := r.getValue(ctx, ipnsKey, options) if err != nil { log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err) return "", err @@ -186,9 +187,9 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, opts *Re } } -func (r *routingResolver) getValue(ctx context.Context, ipnsKey string, opts *ResolveOpts) ([]byte, error) { +func (r *routingResolver) getValue(ctx context.Context, ipnsKey string, options *opts.ResolveOpts) ([]byte, error) { // Get specified number of values from the DHT - vals, err := r.routing.GetValues(ctx, ipnsKey, int(opts.DhtRecordCount)) + vals, err := r.routing.GetValues(ctx, ipnsKey, int(options.DhtRecordCount)) if err != nil { return nil, err } From 709dc3c7e131c62f9e35615919792a0265b4d0ff Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 28 Feb 2018 17:06:31 -0500 Subject: [PATCH 2245/3817] Document namesys options License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@06de6509d0ceb9177e77e3b50e57d6e3d6b018b9 --- namesys/opts/opts.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/namesys/opts/opts.go b/namesys/opts/opts.go index ff7b44c51..63491d683 100644 --- a/namesys/opts/opts.go +++ b/namesys/opts/opts.go @@ -39,26 +39,32 @@ func DefaultResolveOpts() *ResolveOpts { } } +// ResolveOpt is used to set an option type ResolveOpt func(*ResolveOpts) +// Depth is the recursion depth limit func Depth(depth uint) ResolveOpt { return func(o *ResolveOpts) { o.Depth = depth } } +// DhtRecordCount is the number of IPNS records to retrieve from the DHT func DhtRecordCount(count uint) ResolveOpt { return func(o *ResolveOpts) { o.DhtRecordCount = count } } +// DhtTimeout is the amount of time to wait for DHT records to be fetched +// and verified. A zero value indicates that there is no explicit timeout func DhtTimeout(timeout time.Duration) ResolveOpt { return func(o *ResolveOpts) { o.DhtTimeout = timeout } } +// ProcessOpts converts an array of ResolveOpt into a ResolveOpts object func ProcessOpts(opts []ResolveOpt) *ResolveOpts { rsopts := DefaultResolveOpts() for _, option := range opts { From d2519604341a0580b6703abc1ad27be26c72eb8e Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 28 Feb 2018 17:11:46 -0500 Subject: [PATCH 2246/3817] Fix namesys opts package name License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@0592a715746bb66f9fef2d388cbbdc767a9e6073 --- namesys/opts/opts.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/opts/opts.go b/namesys/opts/opts.go index 63491d683..6690cf779 100644 --- a/namesys/opts/opts.go +++ b/namesys/opts/opts.go @@ -1,4 +1,4 @@ -package namesys_opts +package nsopts import ( "time" From 27958c8c4225ba0aaf542b592a53948b4074c020 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 1 Mar 2018 14:17:22 +0100 Subject: [PATCH 2247/3817] Use ReadFull. Remove duplicated code License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@1e4a4eaab015d41d294dd834de19319614f2c2ba --- ipld/merkledag/merkledag_test.go | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index cae3566fc..d67a20aa7 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -133,19 +133,23 @@ func TestBatchFetchDupBlock(t *testing.T) { func makeTestDAG(t *testing.T, read io.Reader, ds ipld.DAGService) ipld.Node { p := make([]byte, 512) nodes := []*ProtoNode{} - var err error - n, err = io.ReadFull(read, p) - if n != len(p) { - t.Fatal("should have read 512 bytes from the reader") - } - for err == nil { + + for { + n, err := io.ReadFull(read, p) + if err == io.EOF { + break + } + + if err != nil { + t.Fatal(err) + } + + if n != len(p) { + t.Fatal("should have read 512 bytes from the reader") + } + protoNode := NodeWithData(p) nodes = append(nodes, protoNode) - _, err = read.Read(p) - } - - if err != io.EOF { - t.Fatal(err) } ctx := context.Background() @@ -158,7 +162,7 @@ func makeTestDAG(t *testing.T, read io.Reader, ds ipld.DAGService) ipld.Node { t.Fatal(err) } } - err = ds.Add(ctx, root) + err := ds.Add(ctx, root) if err != nil { t.Fatal(err) } From 06fa6953a16ef6796249d497763a69b3d7fe04dd Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 1 Mar 2018 21:21:11 +0100 Subject: [PATCH 2248/3817] Enforce Cid security rules for getting and adding blocks License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-blockservice@1492bf6e0dcdd593a53ad5661f2b7516675185df --- blockservice/blockservice.go | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index c6d99c97d..db7aad515 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -10,6 +10,7 @@ import ( "io" exchange "github.com/ipfs/go-ipfs/exchange" + "github.com/ipfs/go-ipfs/removeme/verifcid" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" @@ -130,6 +131,11 @@ func NewSession(ctx context.Context, bs BlockService) *Session { // TODO pass a context into this if the remote.HasBlock is going to remain here. func (s *blockService) AddBlock(o blocks.Block) error { c := o.Cid() + // hash security + err := verifcid.ValidateCid(c) + if err != nil { + return err + } if s.checkFirst { if has, err := s.blockstore.Has(c); has || err != nil { return err @@ -149,6 +155,13 @@ func (s *blockService) AddBlock(o blocks.Block) error { } func (s *blockService) AddBlocks(bs []blocks.Block) error { + //hash security + for _, b := range bs { + err := verifcid.ValidateCid(b.Cid()) + if err != nil { + return err + } + } var toput []blocks.Block if s.checkFirst { toput = make([]blocks.Block, 0, len(bs)) @@ -189,10 +202,15 @@ func (s *blockService) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, f = s.exchange } - return getBlock(ctx, c, s.blockstore, f) + return getBlock(ctx, c, s.blockstore, f) //hash security } func getBlock(ctx context.Context, c *cid.Cid, bs blockstore.Blockstore, f exchange.Fetcher) (blocks.Block, error) { + err := verifcid.ValidateCid(c) + if err != nil { + return nil, err + } + block, err := bs.Get(c) if err == nil { return block, nil @@ -224,11 +242,18 @@ func getBlock(ctx context.Context, c *cid.Cid, bs blockstore.Blockstore, f excha // the returned channel. // NB: No guarantees are made about order. func (s *blockService) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block { - return getBlocks(ctx, ks, s.blockstore, s.exchange) + return getBlocks(ctx, ks, s.blockstore, s.exchange) //hash security } func getBlocks(ctx context.Context, ks []*cid.Cid, bs blockstore.Blockstore, f exchange.Fetcher) <-chan blocks.Block { out := make(chan blocks.Block) + for _, c := range ks { + // hash security + if err := verifcid.ValidateCid(c); err != nil { + log.Errorf("unsafe CID (%s) passed to blockService.GetBlocks: %s", c, err) + } + } + go func() { defer close(out) var misses []*cid.Cid @@ -285,12 +310,12 @@ type Session struct { // GetBlock gets a block in the context of a request session func (s *Session) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) { - return getBlock(ctx, c, s.bs, s.ses) + return getBlock(ctx, c, s.bs, s.ses) // hash security } // GetBlocks gets blocks in the context of a request session func (s *Session) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block { - return getBlocks(ctx, ks, s.bs, s.ses) + return getBlocks(ctx, ks, s.bs, s.ses) // hash security } var _ BlockGetter = (*Session)(nil) From c6be2f337d85387833bc194bde005246e0666886 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 1 Mar 2018 23:31:12 +0100 Subject: [PATCH 2249/3817] Move the temporary packages to thirdparty License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-blockservice@48e84b32f0afbb77890c649806e349fcec2f8276 --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index db7aad515..2d1bc6cbb 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -10,7 +10,7 @@ import ( "io" exchange "github.com/ipfs/go-ipfs/exchange" - "github.com/ipfs/go-ipfs/removeme/verifcid" + "github.com/ipfs/go-ipfs/thirdparty/verifcid" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" From 149b4687dd3f139b272e862627b246dcff36ad24 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 1 Mar 2018 23:41:17 +0100 Subject: [PATCH 2250/3817] //hash -> // hash License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-blockservice@e7295a1b6b02334f29410c9b401062e9354022ef --- blockservice/blockservice.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 2d1bc6cbb..9864dcb2f 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -155,7 +155,7 @@ func (s *blockService) AddBlock(o blocks.Block) error { } func (s *blockService) AddBlocks(bs []blocks.Block) error { - //hash security + // hash security for _, b := range bs { err := verifcid.ValidateCid(b.Cid()) if err != nil { @@ -202,7 +202,7 @@ func (s *blockService) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, f = s.exchange } - return getBlock(ctx, c, s.blockstore, f) //hash security + return getBlock(ctx, c, s.blockstore, f) // hash security } func getBlock(ctx context.Context, c *cid.Cid, bs blockstore.Blockstore, f exchange.Fetcher) (blocks.Block, error) { @@ -242,7 +242,7 @@ func getBlock(ctx context.Context, c *cid.Cid, bs blockstore.Blockstore, f excha // the returned channel. // NB: No guarantees are made about order. func (s *blockService) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block { - return getBlocks(ctx, ks, s.blockstore, s.exchange) //hash security + return getBlocks(ctx, ks, s.blockstore, s.exchange) // hash security } func getBlocks(ctx context.Context, ks []*cid.Cid, bs blockstore.Blockstore, f exchange.Fetcher) <-chan blocks.Block { From 9a60c754f89f0c8af493b781c369eb1f8f31d64d Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 2 Mar 2018 23:54:33 +0100 Subject: [PATCH 2251/3817] add hash security note License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-blockservice@9764858ee2bd30376d746c3f85e7db979c3e5a94 --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 9864dcb2f..cbabbd4e4 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -206,7 +206,7 @@ func (s *blockService) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, } func getBlock(ctx context.Context, c *cid.Cid, bs blockstore.Blockstore, f exchange.Fetcher) (blocks.Block, error) { - err := verifcid.ValidateCid(c) + err := verifcid.ValidateCid(c) // hash security if err != nil { return nil, err } From 1108b08f0636e0520cb8485e117044fccb7cbd10 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 1 Mar 2018 23:31:12 +0100 Subject: [PATCH 2252/3817] Move the temporary packages to thirdparty License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-verifcid@a82b5fb80c15b2171f545ab8624a56df8f6f2acd --- verifcid/validate.go | 62 +++++++++++++++++++++++++++++++++++++++ verifcid/validate_test.go | 59 +++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 verifcid/validate.go create mode 100644 verifcid/validate_test.go diff --git a/verifcid/validate.go b/verifcid/validate.go new file mode 100644 index 000000000..4af24b4c4 --- /dev/null +++ b/verifcid/validate.go @@ -0,0 +1,62 @@ +package verifcid + +import ( + "fmt" + + mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" +) + +var ErrPossiblyInsecureHashFunction = fmt.Errorf("potentially insecure hash functions not allowed") +var ErrBelowMinimumHashLength = fmt.Errorf("hashes must be at %d least bytes long", minimumHashLength) + +const minimumHashLength = 20 + +var goodset = map[uint64]bool{ + mh.SHA2_256: true, + mh.SHA2_512: true, + mh.SHA3_224: true, + mh.SHA3_256: true, + mh.SHA3_384: true, + mh.SHA3_512: true, + mh.SHAKE_256: true, + mh.DBL_SHA2_256: true, + mh.KECCAK_224: true, + mh.KECCAK_256: true, + mh.KECCAK_384: true, + mh.KECCAK_512: true, + mh.ID: true, + + mh.SHA1: true, // not really secure but still useful +} + +func IsGoodHash(code uint64) bool { + good, found := goodset[code] + if good { + return true + } + + if !found { + if code >= mh.BLAKE2B_MIN+19 && code <= mh.BLAKE2B_MAX { + return true + } + if code >= mh.BLAKE2S_MIN+19 && code <= mh.BLAKE2S_MAX { + return true + } + } + + return false +} + +func ValidateCid(c *cid.Cid) error { + pref := c.Prefix() + if !IsGoodHash(pref.MhType) { + return ErrPossiblyInsecureHashFunction + } + + if pref.MhType != mh.ID && pref.MhLength < minimumHashLength { + return ErrBelowMinimumHashLength + } + + return nil +} diff --git a/verifcid/validate_test.go b/verifcid/validate_test.go new file mode 100644 index 000000000..21ab09021 --- /dev/null +++ b/verifcid/validate_test.go @@ -0,0 +1,59 @@ +package verifcid + +import ( + "testing" + + mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" + + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" +) + +func TestValidateCids(t *testing.T) { + assertTrue := func(v bool) { + t.Helper() + if !v { + t.Fatal("expected success") + } + } + assertFalse := func(v bool) { + t.Helper() + if v { + t.Fatal("expected failure") + } + } + + assertTrue(IsGoodHash(mh.SHA2_256)) + assertTrue(IsGoodHash(mh.BLAKE2B_MIN + 32)) + assertTrue(IsGoodHash(mh.DBL_SHA2_256)) + assertTrue(IsGoodHash(mh.KECCAK_256)) + assertTrue(IsGoodHash(mh.SHA3)) + + assertTrue(IsGoodHash(mh.SHA1)) + + assertFalse(IsGoodHash(mh.BLAKE2B_MIN + 5)) + + mhcid := func(code uint64, length int) *cid.Cid { + mhash, err := mh.Sum([]byte{}, code, length) + if err != nil { + t.Fatal(err) + } + return cid.NewCidV1(cid.DagCBOR, mhash) + } + + cases := []struct { + cid *cid.Cid + err error + }{ + {mhcid(mh.SHA2_256, 32), nil}, + {mhcid(mh.SHA2_256, 16), ErrBelowMinimumHashLength}, + {mhcid(mh.MURMUR3, 4), ErrPossiblyInsecureHashFunction}, + } + + for i, cas := range cases { + if ValidateCid(cas.cid) != cas.err { + t.Errorf("wrong result in case of %s (index %d). Expected: %s, got %s", + cas.cid, i, cas.err, ValidateCid(cas.cid)) + } + } + +} From 3e8d35ca2e8fa93d366b13641cf2613bdd05a9cb Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sun, 4 Mar 2018 01:29:24 +0100 Subject: [PATCH 2253/3817] Significanly improve GC UX with verifcid License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-pinner@9f4a5307f2393b0eb81bfb53e2ef1693fbe1a74a --- pinning/pinner/gc/gc.go | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index c665e355e..6c3f438c6 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -5,11 +5,13 @@ import ( "context" "errors" "fmt" + "strings" bserv "github.com/ipfs/go-ipfs/blockservice" offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" + "github.com/ipfs/go-ipfs/thirdparty/verifcid" dstore "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" @@ -129,12 +131,34 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn // adds them to the given cid.Set, using the provided dag.GetLinks function // to walk the tree. func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots []*cid.Cid) error { + verifyGetLinks := func(ctx context.Context, c *cid.Cid) ([]*ipld.Link, error) { + err := verifcid.ValidateCid(c) + if err != nil { + return nil, err + } + + return getLinks(ctx, c) + } + + verboseCidError := func(err error) error { + if strings.Contains(err.Error(), verifcid.ErrBelowMinimumHashLength.Error()) || + strings.Contains(err.Error(), verifcid.ErrPossiblyInsecureHashFunction.Error()) { + err = fmt.Errorf("\"%s\"\nPlease run 'ipfs pin verify'"+ + " to list insecure hashes. If you want to read them,"+ + " please downgrade your go-ipfs to 0.4.13\n", err) + log.Error(err) + } + return err + } + for _, c := range roots { set.Add(c) // EnumerateChildren recursively walks the dag and adds the keys to the given set - err := dag.EnumerateChildren(ctx, getLinks, c, set.Visit) + err := dag.EnumerateChildren(ctx, verifyGetLinks, c, set.Visit) + if err != nil { + err = verboseCidError(err) return err } } From 1f78b9c083643a85176c3902e825eb1d2a221e13 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 28 Feb 2018 10:29:18 -0300 Subject: [PATCH 2254/3817] unixfs: clean path in DagArchive Fixes #4720. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-unixfs@9d25153a7ae2f351eb3a3d232f770dbdf982c5b3 --- unixfs/archive/archive.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index 7a561992e..4aecb186f 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -33,7 +33,8 @@ func (i *identityWriteCloser) Close() error { // DagArchive is equivalent to `ipfs getdag $hash | maybe_tar | maybe_gzip` func DagArchive(ctx context.Context, nd ipld.Node, name string, dag ipld.DAGService, archive bool, compression int) (io.Reader, error) { - _, filename := path.Split(name) + cleaned := path.Clean(name) + _, filename := path.Split(cleaned) // need to connect a writer to a reader piper, pipew := io.Pipe() From 48d0c0b251ef82bf4534b05ade6b67fa86fbb4d8 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Mon, 5 Mar 2018 12:00:34 -0300 Subject: [PATCH 2255/3817] dag: diff: check CIDs in base case when comparing nodes Fixes #4591. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-merkledag@14f3d3ca174d995612f70a933234fc1159c6f9ce --- ipld/merkledag/utils/diff.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 5af348d53..4ba0f48c5 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -95,7 +95,12 @@ func ApplyChange(ctx context.Context, ds ipld.DAGService, nd *dag.ProtoNode, cs // Diff returns a set of changes that transform node 'a' into node 'b' func Diff(ctx context.Context, ds ipld.DAGService, a, b ipld.Node) ([]*Change, error) { + // Base case where both nodes are leaves, just compare + // their CIDs. if len(a.Links()) == 0 && len(b.Links()) == 0 { + if a.Cid().Equals(b.Cid()) { + return []*Change{}, nil + } return []*Change{ &Change{ Type: Mod, From 963f83df40b7e960430e8da91aef942e58278acd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 17:59:43 +0100 Subject: [PATCH 2256/3817] coreapi: move unixfs errors to the top MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@848f12365b041d32adfa2ba7d5e3b30a1bc4233b --- coreiface/interface.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 75a168bf3..4d68b5f4b 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -14,6 +14,9 @@ import ( ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) +var ErrIsDir = errors.New("object is a directory") +var ErrOffline = errors.New("can't resolve, ipfs node is offline") + // Path is a generic wrapper for paths used in the API. A path can be resolved // to a CID using one of Resolve functions in the API. type Path interface { @@ -384,6 +387,3 @@ type PinAPI interface { // Verify verifies the integrity of pinned objects Verify(context.Context) (<-chan PinStatus, error) } - -var ErrIsDir = errors.New("object is a directory") -var ErrOffline = errors.New("can't resolve, ipfs node is offline") From 222c7a617e1ee17ba106b5e5279c15931138abc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 18:31:28 +0100 Subject: [PATCH 2257/3817] coreapi: don't alias ipld types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@ab9053378b45e67758591727b9140366d4874fc9 --- coreiface/interface.go | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/coreiface/interface.go b/coreiface/interface.go index 4d68b5f4b..02525c0d2 100644 --- a/coreiface/interface.go +++ b/coreiface/interface.go @@ -30,11 +30,6 @@ type Path interface { Resolved() bool } -// TODO: should we really copy these? -// if we didn't, godoc would generate nice links straight to go-ipld-format -type Node ipld.Node -type Link ipld.Link - type Reader interface { io.ReadSeeker io.Closer @@ -114,7 +109,7 @@ type CoreAPI interface { // ResolveNode resolves the path (if not resolved already) using Unixfs // resolver, gets and returns the resolved Node - ResolveNode(context.Context, Path) (Node, error) + ResolveNode(context.Context, Path) (ipld.Node, error) } // UnixfsAPI is the basic interface to immutable files in IPFS @@ -126,7 +121,7 @@ type UnixfsAPI interface { Cat(context.Context, Path) (Reader, error) // Ls returns the list of links in a directory - Ls(context.Context, Path) ([]*Link, error) + Ls(context.Context, Path) ([]*ipld.Link, error) } // BlockAPI specifies the interface to the block layer @@ -183,7 +178,7 @@ type DagAPI interface { WithHash(mhType uint64, mhLen int) options.DagPutOption // Get attempts to resolve and get the node specified by the path - Get(ctx context.Context, path Path) (Node, error) + Get(ctx context.Context, path Path) (ipld.Node, error) // Tree returns list of paths within a node specified by the path. Tree(ctx context.Context, path Path, opts ...options.DagTreeOption) ([]Path, error) @@ -272,7 +267,7 @@ type KeyAPI interface { // for manipulating MerkleDAG data structures. type ObjectAPI interface { // New creates new, empty (by default) dag-node. - New(context.Context, ...options.ObjectNewOption) (Node, error) + New(context.Context, ...options.ObjectNewOption) (ipld.Node, error) // WithType is an option for New which allows to change the type of created // dag node. @@ -302,13 +297,13 @@ type ObjectAPI interface { WithDataType(t string) options.ObjectPutOption // Get returns the node for the path - Get(context.Context, Path) (Node, error) + Get(context.Context, Path) (ipld.Node, error) // Data returns reader for data of the node Data(context.Context, Path) (io.Reader, error) // Links returns lint or links the node contains - Links(context.Context, Path) ([]*Link, error) + Links(context.Context, Path) ([]*ipld.Link, error) // Stat returns information about the node Stat(context.Context, Path) (*ObjectStat, error) From a05b8c4e76d49997baeb11f87f247965fe2b33ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 18:46:45 +0100 Subject: [PATCH 2258/3817] coreapi: split the interface into multiple files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@d4077754e8de62308542ca3838c8f280ca4aad22 --- coreiface/block.go | 49 ++++++ coreiface/coreapi.go | 38 ++++ coreiface/dag.go | 42 +++++ coreiface/errors.go | 6 + coreiface/interface.go | 384 ----------------------------------------- coreiface/key.go | 51 ++++++ coreiface/name.go | 55 ++++++ coreiface/object.go | 96 +++++++++++ coreiface/path.go | 18 ++ coreiface/pin.go | 69 ++++++++ coreiface/unixfs.go | 20 +++ coreiface/util.go | 10 ++ 12 files changed, 454 insertions(+), 384 deletions(-) create mode 100644 coreiface/block.go create mode 100644 coreiface/coreapi.go create mode 100644 coreiface/dag.go create mode 100644 coreiface/errors.go delete mode 100644 coreiface/interface.go create mode 100644 coreiface/key.go create mode 100644 coreiface/name.go create mode 100644 coreiface/object.go create mode 100644 coreiface/path.go create mode 100644 coreiface/pin.go create mode 100644 coreiface/unixfs.go create mode 100644 coreiface/util.go diff --git a/coreiface/block.go b/coreiface/block.go new file mode 100644 index 000000000..f38a664c3 --- /dev/null +++ b/coreiface/block.go @@ -0,0 +1,49 @@ +package iface + +import ( + "context" + "io" + + options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" +) + +// BlockStat contains information about a block +type BlockStat interface { + // Size is the size of a block + Size() int + + // Path returns path to the block + Path() Path +} + +// BlockAPI specifies the interface to the block layer +type BlockAPI interface { + // Put imports raw block data, hashing it using specified settings. + Put(context.Context, io.Reader, ...options.BlockPutOption) (Path, error) + + // WithFormat is an option for Put which specifies the multicodec to use to + // serialize the object. Default is "v0" + WithFormat(codec string) options.BlockPutOption + + // WithHash is an option for 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 + WithHash(mhType uint64, mhLen int) options.BlockPutOption + + // Get attempts to resolve the path and return a reader for data in the block + Get(context.Context, Path) (io.Reader, error) + + // Rm removes the block specified by the path from local blockstore. + // By default an error will be returned if the block can't be found locally. + // + // NOTE: If the specified block is pinned it won't be removed and no error + // will be returned + Rm(context.Context, Path, ...options.BlockRmOption) error + + // WithForce is an option for Rm which, when set to true, will ignore + // non-existing blocks + WithForce(force bool) options.BlockRmOption + + // Stat returns information on + Stat(context.Context, Path) (BlockStat, error) +} diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go new file mode 100644 index 000000000..f1388e5bc --- /dev/null +++ b/coreiface/coreapi.go @@ -0,0 +1,38 @@ +// Package iface defines IPFS Core API which is a set of interfaces used to +// interact with IPFS nodes. +package iface + +import ( + "context" + + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" +) + +// CoreAPI defines an unified interface to IPFS for Go programs. +type CoreAPI interface { + // Unixfs returns an implementation of Unixfs API. + Unixfs() UnixfsAPI + + // Block returns an implementation of Block API. + Block() BlockAPI + + // Dag returns an implementation of Dag API. + Dag() DagAPI + + // Name returns an implementation of Name API. + Name() NameAPI + + // Key returns an implementation of Key API. + Key() KeyAPI + Pin() PinAPI + + // ObjectAPI returns an implementation of Object API + Object() ObjectAPI + + // ResolvePath resolves the path using Unixfs resolver + ResolvePath(context.Context, Path) (Path, error) + + // ResolveNode resolves the path (if not resolved already) using Unixfs + // resolver, gets and returns the resolved Node + ResolveNode(context.Context, Path) (ipld.Node, error) +} diff --git a/coreiface/dag.go b/coreiface/dag.go new file mode 100644 index 000000000..1635d71b1 --- /dev/null +++ b/coreiface/dag.go @@ -0,0 +1,42 @@ +package iface + +import ( + "context" + "io" + + options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" +) + +// DagAPI specifies the interface to IPLD +type DagAPI interface { + // Put inserts data using specified format and input encoding. + // Unless used with WithCodec or WithHash, the defaults "dag-cbor" and + // "sha256" are used. + Put(ctx context.Context, src io.Reader, opts ...options.DagPutOption) (Path, error) + + // WithInputEnc is an option for Put which specifies the input encoding of the + // data. Default is "json", most formats/codecs support "raw" + WithInputEnc(enc string) options.DagPutOption + + // WithCodec is an option for Put which specifies the multicodec to use to + // serialize the object. Default is cid.DagCBOR (0x71) + WithCodec(codec uint64) options.DagPutOption + + // WithHash is an option for Put which specifies the multihash settings to use + // when hashing the object. Default is based on the codec used + // (mh.SHA2_256 (0x12) for DagCBOR). If mhLen is set to -1, default length for + // the hash will be used + WithHash(mhType uint64, mhLen int) options.DagPutOption + + // Get attempts to resolve and get the node specified by the path + Get(ctx context.Context, path Path) (ipld.Node, error) + + // Tree returns list of paths within a node specified by the path. + Tree(ctx context.Context, path Path, opts ...options.DagTreeOption) ([]Path, error) + + // WithDepth is an option for Tree which specifies maximum depth of the + // returned tree. Default is -1 (no depth limit) + WithDepth(depth int) options.DagTreeOption +} diff --git a/coreiface/errors.go b/coreiface/errors.go new file mode 100644 index 000000000..73442be11 --- /dev/null +++ b/coreiface/errors.go @@ -0,0 +1,6 @@ +package iface + +import "errors" + +var ErrIsDir = errors.New("object is a directory") +var ErrOffline = errors.New("can't resolve, ipfs node is offline") diff --git a/coreiface/interface.go b/coreiface/interface.go deleted file mode 100644 index 02525c0d2..000000000 --- a/coreiface/interface.go +++ /dev/null @@ -1,384 +0,0 @@ -// Package iface defines IPFS Core API which is a set of interfaces used to -// interact with IPFS nodes. -package iface - -import ( - "context" - "errors" - "io" - "time" - - options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" -) - -var ErrIsDir = errors.New("object is a directory") -var ErrOffline = errors.New("can't resolve, ipfs node is offline") - -// Path is a generic wrapper for paths used in the API. A path can be resolved -// to a CID using one of Resolve functions in the API. -type Path interface { - // String returns the path as a string. - String() string - // Cid returns cid referred to by path - Cid() *cid.Cid - // Root returns cid of root path - Root() *cid.Cid - // Resolved returns whether path has been fully resolved - Resolved() bool -} - -type Reader interface { - io.ReadSeeker - io.Closer -} - -// IpnsEntry specifies the interface to IpnsEntries -type IpnsEntry interface { - // Name returns IpnsEntry name - Name() string - // Value returns IpnsEntry value - Value() Path -} - -// Key specifies the interface to Keys in KeyAPI Keystore -type Key interface { - // Key returns key name - Name() string - // Path returns key path - Path() Path -} - -type BlockStat interface { - Size() int - Path() Path -} - -// Pin holds information about pinned resource -type Pin interface { - // Path to the pinned object - Path() Path - - // Type of the pin - Type() string -} - -// PinStatus holds information about pin health -type PinStatus interface { - // Ok indicates whether the pin has been verified to be correct - Ok() bool - - // BadNodes returns any bad (usually missing) nodes from the pin - BadNodes() []BadPinNode -} - -// BadPinNode is a node that has been marked as bad by Pin.Verify -type BadPinNode interface { - // Path is the path of the node - Path() Path - - // Err is the reason why the node has been marked as bad - Err() error -} - -// CoreAPI defines an unified interface to IPFS for Go programs. -type CoreAPI interface { - // Unixfs returns an implementation of Unixfs API. - Unixfs() UnixfsAPI - - // Block returns an implementation of Block API. - Block() BlockAPI - - // Dag returns an implementation of Dag API. - Dag() DagAPI - - // Name returns an implementation of Name API. - Name() NameAPI - - // Key returns an implementation of Key API. - Key() KeyAPI - Pin() PinAPI - - // ObjectAPI returns an implementation of Object API - Object() ObjectAPI - - // ResolvePath resolves the path using Unixfs resolver - ResolvePath(context.Context, Path) (Path, error) - - // ResolveNode resolves the path (if not resolved already) using Unixfs - // resolver, gets and returns the resolved Node - ResolveNode(context.Context, Path) (ipld.Node, error) -} - -// UnixfsAPI is the basic interface to immutable files in IPFS -type UnixfsAPI interface { - // Add imports the data from the reader into merkledag file - Add(context.Context, io.Reader) (Path, error) - - // Cat returns a reader for the file - Cat(context.Context, Path) (Reader, error) - - // Ls returns the list of links in a directory - Ls(context.Context, Path) ([]*ipld.Link, error) -} - -// BlockAPI specifies the interface to the block layer -type BlockAPI interface { - // Put imports raw block data, hashing it using specified settings. - Put(context.Context, io.Reader, ...options.BlockPutOption) (Path, error) - - // WithFormat is an option for Put which specifies the multicodec to use to - // serialize the object. Default is "v0" - WithFormat(codec string) options.BlockPutOption - - // WithHash is an option for 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 - WithHash(mhType uint64, mhLen int) options.BlockPutOption - - // Get attempts to resolve the path and return a reader for data in the block - Get(context.Context, Path) (io.Reader, error) - - // Rm removes the block specified by the path from local blockstore. - // By default an error will be returned if the block can't be found locally. - // - // NOTE: If the specified block is pinned it won't be removed and no error - // will be returned - Rm(context.Context, Path, ...options.BlockRmOption) error - - // WithForce is an option for Rm which, when set to true, will ignore - // non-existing blocks - WithForce(force bool) options.BlockRmOption - - // Stat returns information on - Stat(context.Context, Path) (BlockStat, error) -} - -// DagAPI specifies the interface to IPLD -type DagAPI interface { - // Put inserts data using specified format and input encoding. - // Unless used with WithCodec or WithHash, the defaults "dag-cbor" and - // "sha256" are used. - Put(ctx context.Context, src io.Reader, opts ...options.DagPutOption) (Path, error) - - // WithInputEnc is an option for Put which specifies the input encoding of the - // data. Default is "json", most formats/codecs support "raw" - WithInputEnc(enc string) options.DagPutOption - - // WithCodec is an option for Put which specifies the multicodec to use to - // serialize the object. Default is cid.DagCBOR (0x71) - WithCodec(codec uint64) options.DagPutOption - - // WithHash is an option for Put which specifies the multihash settings to use - // when hashing the object. Default is based on the codec used - // (mh.SHA2_256 (0x12) for DagCBOR). If mhLen is set to -1, default length for - // the hash will be used - WithHash(mhType uint64, mhLen int) options.DagPutOption - - // Get attempts to resolve and get the node specified by the path - Get(ctx context.Context, path Path) (ipld.Node, error) - - // Tree returns list of paths within a node specified by the path. - Tree(ctx context.Context, path Path, opts ...options.DagTreeOption) ([]Path, error) - - // WithDepth is an option for Tree which specifies maximum depth of the - // returned tree. Default is -1 (no depth limit) - WithDepth(depth int) options.DagTreeOption -} - -// NameAPI specifies the interface to IPNS. -// -// IPNS is a PKI namespace, where names are the hashes of public keys, and the -// private key enables publishing new (signed) values. In both publish and -// resolve, the default name used is the node's own PeerID, which is the hash of -// its public key. -// -// You can use .Key API to list and generate more names and their respective keys. -type NameAPI interface { - // Publish announces new IPNS name - Publish(ctx context.Context, path Path, opts ...options.NamePublishOption) (IpnsEntry, error) - - // WithValidTime is an option for Publish which specifies for how long the - // entry will remain valid. Default value is 24h - WithValidTime(validTime time.Duration) options.NamePublishOption - - // WithKey is an option for Publish which specifies the key to use for - // publishing. Default value is "self" which is the node's own PeerID. - // The key parameter must be either PeerID or keystore key alias. - // - // You can use KeyAPI to list and generate more names and their respective keys. - WithKey(key string) options.NamePublishOption - - // Resolve attempts to resolve the newest version of the specified name - Resolve(ctx context.Context, name string, opts ...options.NameResolveOption) (Path, error) - - // WithRecursive is an option for Resolve which specifies whether to perform a - // recursive lookup. Default value is false - WithRecursive(recursive bool) options.NameResolveOption - - // WithLocal is an option for Resolve which specifies if the lookup should be - // offline. Default value is false - WithLocal(local bool) options.NameResolveOption - - // WithCache is an option for Resolve which specifies if cache should be used. - // Default value is true - WithCache(cache bool) options.NameResolveOption -} - -// KeyAPI specifies the interface to Keystore -type KeyAPI interface { - // Generate generates new key, stores it in the keystore under the specified - // name and returns a base58 encoded multihash of it's public key - Generate(ctx context.Context, name string, opts ...options.KeyGenerateOption) (Key, error) - - // WithType is an option for Generate which specifies which algorithm - // should be used for the key. Default is options.RSAKey - // - // Supported key types: - // * options.RSAKey - // * options.Ed25519Key - WithType(algorithm string) options.KeyGenerateOption - - // WithSize is an option for Generate which specifies the size of the key to - // generated. Default is -1 - // - // value of -1 means 'use default size for key type': - // * 2048 for RSA - WithSize(size int) options.KeyGenerateOption - - // Rename renames oldName key to newName. Returns the key and whether another - // key was overwritten, or an error - Rename(ctx context.Context, oldName string, newName string, opts ...options.KeyRenameOption) (Key, bool, error) - - // WithForce is an option for Rename which specifies whether to allow to - // replace existing keys. - WithForce(force bool) options.KeyRenameOption - - // List lists keys stored in keystore - List(ctx context.Context) ([]Key, error) - - // Remove removes keys from keystore. Returns ipns path of the removed key - Remove(ctx context.Context, name string) (Path, error) -} - -// ObjectAPI specifies the interface to MerkleDAG and contains useful utilities -// for manipulating MerkleDAG data structures. -type ObjectAPI interface { - // New creates new, empty (by default) dag-node. - New(context.Context, ...options.ObjectNewOption) (ipld.Node, error) - - // WithType is an option for New which allows to change the type of created - // dag node. - // - // Supported types: - // * 'empty' - Empty node - // * 'unixfs-dir' - Empty UnixFS directory - WithType(string) options.ObjectNewOption - - // Put imports the data into merkledag - Put(context.Context, io.Reader, ...options.ObjectPutOption) (Path, error) - - // WithInputEnc is an option for Put which specifies the input encoding of the - // data. Default is "json". - // - // Supported encodings: - // * "protobuf" - // * "json" - WithInputEnc(e string) options.ObjectPutOption - - // WithDataType specifies the encoding of data field when using Josn or XML - // input encoding. - // - // Supported types: - // * "text" (default) - // * "base64" - WithDataType(t string) options.ObjectPutOption - - // Get returns the node for the path - Get(context.Context, Path) (ipld.Node, error) - - // Data returns reader for data of the node - Data(context.Context, Path) (io.Reader, error) - - // Links returns lint or links the node contains - Links(context.Context, Path) ([]*ipld.Link, error) - - // Stat returns information about the node - Stat(context.Context, Path) (*ObjectStat, error) - - // AddLink adds a link under the specified path. child path can point to a - // subdirectory within the patent which must be present (can be overridden - // with WithCreate option). - AddLink(ctx context.Context, base Path, name string, child Path, opts ...options.ObjectAddLinkOption) (Path, error) - - // WithCreate is an option for AddLink which specifies whether create required - // directories for the child - WithCreate(create bool) options.ObjectAddLinkOption - - // RmLink removes a link from the node - RmLink(ctx context.Context, base Path, link string) (Path, error) - - // AppendData appends data to the node - AppendData(context.Context, Path, io.Reader) (Path, error) - - // SetData sets the data contained in the node - SetData(context.Context, Path, io.Reader) (Path, error) -} - -// ObjectStat provides information about dag nodes -type ObjectStat struct { - // Cid is the CID of the node - Cid *cid.Cid - - // NumLinks is number of links the node contains - NumLinks int - - // BlockSize is size of the raw serialized node - BlockSize int - - // LinksSize is size of the links block section - LinksSize int - - // DataSize is the size of data block section - DataSize int - - // CumulativeSize is size of the tree (BlockSize + link sizes) - CumulativeSize int -} - -// PinAPI specifies the interface to pining -type PinAPI interface { - // Add creates new pin, be default recursive - pinning the whole referenced - // tree - Add(context.Context, Path, ...options.PinAddOption) error - - // WithRecursive is an option for Add which specifies whether to pin an entire - // object tree or just one object. Default: true - WithRecursive(bool) options.PinAddOption - - // Ls returns list of pinned objects on this node - Ls(context.Context, ...options.PinLsOption) ([]Pin, error) - - // WithType is an option for Ls which allows to specify which pin types should - // be returned - // - // Supported values: - // * "direct" - directly pinned objects - // * "recursive" - roots of recursive pins - // * "indirect" - indirectly pinned objects (referenced by recursively pinned - // objects) - // * "all" - all pinned objects (default) - WithType(string) options.PinLsOption - - // Rm removes pin for object specified by the path - Rm(context.Context, Path) error - - // Update changes one pin to another, skipping checks for matching paths in - // the old tree - Update(ctx context.Context, from Path, to Path, opts ...options.PinUpdateOption) error - - // Verify verifies the integrity of pinned objects - Verify(context.Context) (<-chan PinStatus, error) -} diff --git a/coreiface/key.go b/coreiface/key.go new file mode 100644 index 000000000..730e855d7 --- /dev/null +++ b/coreiface/key.go @@ -0,0 +1,51 @@ +package iface + +import ( + "context" + + options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" +) + +// Key specifies the interface to Keys in KeyAPI Keystore +type Key interface { + // Key returns key name + Name() string + // Path returns key path + Path() Path +} + +// KeyAPI specifies the interface to Keystore +type KeyAPI interface { + // Generate generates new key, stores it in the keystore under the specified + // name and returns a base58 encoded multihash of it's public key + Generate(ctx context.Context, name string, opts ...options.KeyGenerateOption) (Key, error) + + // WithType is an option for Generate which specifies which algorithm + // should be used for the key. Default is options.RSAKey + // + // Supported key types: + // * options.RSAKey + // * options.Ed25519Key + WithType(algorithm string) options.KeyGenerateOption + + // WithSize is an option for Generate which specifies the size of the key to + // generated. Default is -1 + // + // value of -1 means 'use default size for key type': + // * 2048 for RSA + WithSize(size int) options.KeyGenerateOption + + // Rename renames oldName key to newName. Returns the key and whether another + // key was overwritten, or an error + Rename(ctx context.Context, oldName string, newName string, opts ...options.KeyRenameOption) (Key, bool, error) + + // WithForce is an option for Rename which specifies whether to allow to + // replace existing keys. + WithForce(force bool) options.KeyRenameOption + + // List lists keys stored in keystore + List(ctx context.Context) ([]Key, error) + + // Remove removes keys from keystore. Returns ipns path of the removed key + Remove(ctx context.Context, name string) (Path, error) +} diff --git a/coreiface/name.go b/coreiface/name.go new file mode 100644 index 000000000..6d17d840a --- /dev/null +++ b/coreiface/name.go @@ -0,0 +1,55 @@ +package iface + +import ( + "context" + "time" + + options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" +) + +// IpnsEntry specifies the interface to IpnsEntries +type IpnsEntry interface { + // Name returns IpnsEntry name + Name() string + // Value returns IpnsEntry value + Value() Path +} + +// NameAPI specifies the interface to IPNS. +// +// IPNS is a PKI namespace, where names are the hashes of public keys, and the +// private key enables publishing new (signed) values. In both publish and +// resolve, the default name used is the node's own PeerID, which is the hash of +// its public key. +// +// You can use .Key API to list and generate more names and their respective keys. +type NameAPI interface { + // Publish announces new IPNS name + Publish(ctx context.Context, path Path, opts ...options.NamePublishOption) (IpnsEntry, error) + + // WithValidTime is an option for Publish which specifies for how long the + // entry will remain valid. Default value is 24h + WithValidTime(validTime time.Duration) options.NamePublishOption + + // WithKey is an option for Publish which specifies the key to use for + // publishing. Default value is "self" which is the node's own PeerID. + // The key parameter must be either PeerID or keystore key alias. + // + // You can use KeyAPI to list and generate more names and their respective keys. + WithKey(key string) options.NamePublishOption + + // Resolve attempts to resolve the newest version of the specified name + Resolve(ctx context.Context, name string, opts ...options.NameResolveOption) (Path, error) + + // WithRecursive is an option for Resolve which specifies whether to perform a + // recursive lookup. Default value is false + WithRecursive(recursive bool) options.NameResolveOption + + // WithLocal is an option for Resolve which specifies if the lookup should be + // offline. Default value is false + WithLocal(local bool) options.NameResolveOption + + // WithCache is an option for Resolve which specifies if cache should be used. + // Default value is true + WithCache(cache bool) options.NameResolveOption +} diff --git a/coreiface/object.go b/coreiface/object.go new file mode 100644 index 000000000..75837f93e --- /dev/null +++ b/coreiface/object.go @@ -0,0 +1,96 @@ +package iface + +import ( + "context" + "io" + + options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" +) + +// ObjectStat provides information about dag nodes +type ObjectStat struct { + // Cid is the CID of the node + Cid *cid.Cid + + // NumLinks is number of links the node contains + NumLinks int + + // BlockSize is size of the raw serialized node + BlockSize int + + // LinksSize is size of the links block section + LinksSize int + + // DataSize is the size of data block section + DataSize int + + // CumulativeSize is size of the tree (BlockSize + link sizes) + CumulativeSize int +} + +// ObjectAPI specifies the interface to MerkleDAG and contains useful utilities +// for manipulating MerkleDAG data structures. +type ObjectAPI interface { + // New creates new, empty (by default) dag-node. + New(context.Context, ...options.ObjectNewOption) (ipld.Node, error) + + // WithType is an option for New which allows to change the type of created + // dag node. + // + // Supported types: + // * 'empty' - Empty node + // * 'unixfs-dir' - Empty UnixFS directory + WithType(string) options.ObjectNewOption + + // Put imports the data into merkledag + Put(context.Context, io.Reader, ...options.ObjectPutOption) (Path, error) + + // WithInputEnc is an option for Put which specifies the input encoding of the + // data. Default is "json". + // + // Supported encodings: + // * "protobuf" + // * "json" + WithInputEnc(e string) options.ObjectPutOption + + // WithDataType specifies the encoding of data field when using Josn or XML + // input encoding. + // + // Supported types: + // * "text" (default) + // * "base64" + WithDataType(t string) options.ObjectPutOption + + // Get returns the node for the path + Get(context.Context, Path) (ipld.Node, error) + + // Data returns reader for data of the node + Data(context.Context, Path) (io.Reader, error) + + // Links returns lint or links the node contains + Links(context.Context, Path) ([]*ipld.Link, error) + + // Stat returns information about the node + Stat(context.Context, Path) (*ObjectStat, error) + + // AddLink adds a link under the specified path. child path can point to a + // subdirectory within the patent which must be present (can be overridden + // with WithCreate option). + AddLink(ctx context.Context, base Path, name string, child Path, opts ...options.ObjectAddLinkOption) (Path, error) + + // WithCreate is an option for AddLink which specifies whether create required + // directories for the child + WithCreate(create bool) options.ObjectAddLinkOption + + // RmLink removes a link from the node + RmLink(ctx context.Context, base Path, link string) (Path, error) + + // AppendData appends data to the node + AppendData(context.Context, Path, io.Reader) (Path, error) + + // SetData sets the data contained in the node + SetData(context.Context, Path, io.Reader) (Path, error) +} diff --git a/coreiface/path.go b/coreiface/path.go new file mode 100644 index 000000000..b2160b942 --- /dev/null +++ b/coreiface/path.go @@ -0,0 +1,18 @@ +package iface + +import ( + cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" +) + +// Path is a generic wrapper for paths used in the API. A path can be resolved +// to a CID using one of Resolve functions in the API. +type Path interface { + // String returns the path as a string. + String() string + // Cid returns cid referred to by path + Cid() *cid.Cid + // Root returns cid of root path + Root() *cid.Cid + // Resolved returns whether path has been fully resolved + Resolved() bool +} diff --git a/coreiface/pin.go b/coreiface/pin.go new file mode 100644 index 000000000..47a5a0bb2 --- /dev/null +++ b/coreiface/pin.go @@ -0,0 +1,69 @@ +package iface + +import ( + "context" + + options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" +) + +// Pin holds information about pinned resource +type Pin interface { + // Path to the pinned object + Path() Path + + // Type of the pin + Type() string +} + +// PinStatus holds information about pin health +type PinStatus interface { + // Ok indicates whether the pin has been verified to be correct + Ok() bool + + // BadNodes returns any bad (usually missing) nodes from the pin + BadNodes() []BadPinNode +} + +// BadPinNode is a node that has been marked as bad by Pin.Verify +type BadPinNode interface { + // Path is the path of the node + Path() Path + + // Err is the reason why the node has been marked as bad + Err() error +} + +// PinAPI specifies the interface to pining +type PinAPI interface { + // Add creates new pin, be default recursive - pinning the whole referenced + // tree + Add(context.Context, Path, ...options.PinAddOption) error + + // WithRecursive is an option for Add which specifies whether to pin an entire + // object tree or just one object. Default: true + WithRecursive(bool) options.PinAddOption + + // Ls returns list of pinned objects on this node + Ls(context.Context, ...options.PinLsOption) ([]Pin, error) + + // WithType is an option for Ls which allows to specify which pin types should + // be returned + // + // Supported values: + // * "direct" - directly pinned objects + // * "recursive" - roots of recursive pins + // * "indirect" - indirectly pinned objects (referenced by recursively pinned + // objects) + // * "all" - all pinned objects (default) + WithType(string) options.PinLsOption + + // Rm removes pin for object specified by the path + Rm(context.Context, Path) error + + // Update changes one pin to another, skipping checks for matching paths in + // the old tree + Update(ctx context.Context, from Path, to Path, opts ...options.PinUpdateOption) error + + // Verify verifies the integrity of pinned objects + Verify(context.Context) (<-chan PinStatus, error) +} diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go new file mode 100644 index 000000000..c1b4efa43 --- /dev/null +++ b/coreiface/unixfs.go @@ -0,0 +1,20 @@ +package iface + +import ( + "context" + "io" + + ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" +) + +// UnixfsAPI is the basic interface to immutable files in IPFS +type UnixfsAPI interface { + // Add imports the data from the reader into merkledag file + Add(context.Context, io.Reader) (Path, error) + + // Cat returns a reader for the file + Cat(context.Context, Path) (Reader, error) + + // Ls returns the list of links in a directory + Ls(context.Context, Path) ([]*ipld.Link, error) +} diff --git a/coreiface/util.go b/coreiface/util.go new file mode 100644 index 000000000..8fd3e058f --- /dev/null +++ b/coreiface/util.go @@ -0,0 +1,10 @@ +package iface + +import ( + "io" +) + +type Reader interface { + io.ReadSeeker + io.Closer +} From 64d53e28ce0e428dadef3a318a1f44eb983376e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 18:52:10 +0100 Subject: [PATCH 2259/3817] coreapi: minor doc fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@dba6c1b7a33059613de8019eafb527031e52e61d --- coreiface/coreapi.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index f1388e5bc..9428b3b63 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -8,22 +8,24 @@ import ( ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) -// CoreAPI defines an unified interface to IPFS for Go programs. +// CoreAPI defines an unified interface to IPFS for Go programs type CoreAPI interface { - // Unixfs returns an implementation of Unixfs API. + // Unixfs returns an implementation of Unixfs API Unixfs() UnixfsAPI - // Block returns an implementation of Block API. + // Block returns an implementation of Block API Block() BlockAPI - // Dag returns an implementation of Dag API. + // Dag returns an implementation of Dag API Dag() DagAPI - // Name returns an implementation of Name API. + // Name returns an implementation of Name API Name() NameAPI - // Key returns an implementation of Key API. + // Key returns an implementation of Key API Key() KeyAPI + + // Pin returns an implementation of Pin API Pin() PinAPI // ObjectAPI returns an implementation of Object API From 5de1a8368e0d7b6abe9226f7e789ab8a521fb886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 22:17:27 +0100 Subject: [PATCH 2260/3817] coreapi: var block for errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@60fc569676d93341f657737a221de2228a178183 --- coreiface/errors.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/coreiface/errors.go b/coreiface/errors.go index 73442be11..81f978971 100644 --- a/coreiface/errors.go +++ b/coreiface/errors.go @@ -2,5 +2,7 @@ package iface import "errors" -var ErrIsDir = errors.New("object is a directory") -var ErrOffline = errors.New("can't resolve, ipfs node is offline") +var ( + ErrIsDir = errors.New("object is a directory") + ErrOffline = errors.New("can't resolve, ipfs node is offline") +) From bd72b23c7576074a08776640c57a122551b255e4 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 15 Mar 2018 21:07:22 +0100 Subject: [PATCH 2261/3817] misc: spelling of retrieval License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@5b0247a27d031233182796eb28c2585871a0be88 --- unixfs/io/dagreader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index af3fbb330..b1841bd74 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -39,7 +39,7 @@ type ReadSeekCloser interface { } // NewDagReader creates a new reader object that reads the data represented by -// the given node, using the passed in DAGService for data retreival +// the given node, using the passed in DAGService for data retrieval func NewDagReader(ctx context.Context, n ipld.Node, serv ipld.NodeGetter) (DagReader, error) { switch n := n.(type) { case *mdag.RawNode: From 684f89bd6a458a5ed5fdaf50943028eaf1a64c9a Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 20 Mar 2018 13:55:52 +0100 Subject: [PATCH 2262/3817] fix(mfs): Directory.Path not working, add test Credit goes to @ridewindx License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-mfs@70a8736e5907c93ea48eca9f31ee6110283440cf --- mfs/dir.go | 11 +++++++++-- mfs/mfs_test.go | 5 +++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 08b703fb2..0cc6e6568 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -404,8 +404,15 @@ func (d *Directory) Path() string { cur := d var out string for cur != nil { - out = path.Join(cur.name, out) - cur = cur.parent.(*Directory) + switch parent := cur.parent.(type) { + case *Directory: + out = path.Join(cur.name, out) + cur = parent + case *Root: + return "/" + out + default: + panic("directory parent neither a directory nor a root") + } } return out } diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index d7124bdf1..25e61854b 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -324,6 +324,11 @@ func TestDirectoryLoadFromDag(t *testing.T) { topd := topi.(*Directory) + path := topd.Path() + if path != "/foo" { + t.Fatalf("Expected path '/foo', got '%s'", path) + } + // mkdir over existing but unloaded child file should fail _, err = topd.Mkdir("a") if err == nil { From fb3396642db290adef8079864235262e7763dc46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 19 Mar 2018 02:09:29 +0100 Subject: [PATCH 2263/3817] misc: Remove some dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-merkledag@b6ee1b4204fa67e7dc6b337918cde921211851f2 --- ipld/merkledag/merkledag.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 197785d39..4ece495f5 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -180,18 +180,6 @@ func FetchGraph(ctx context.Context, root *cid.Cid, serv ipld.DAGService) error return EnumerateChildrenAsync(ctx, GetLinksDirect(ng), root, visit) } -// FindLinks searches this nodes links for the given key, -// returns the indexes of any links pointing to it -func FindLinks(links []*cid.Cid, c *cid.Cid, start int) []int { - var out []int - for i, lnkC := range links[start:] { - if c.Equals(lnkC) { - out = append(out, i+start) - } - } - return out -} - // GetMany gets many nodes from the DAG at once. // // This method may not return all requested nodes (and may or may not return an From 138ada16076a6697e5986d8873678da6eb02e022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 19 Mar 2018 02:09:29 +0100 Subject: [PATCH 2264/3817] misc: Remove some dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-unixfs@d87b75ccf159a1f8018d90b82801d171291e9301 --- unixfs/archive/archive.go | 2 +- unixfs/archive/tar/writer.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index 7a561992e..aeaffe78e 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -81,7 +81,7 @@ func DagArchive(ctx context.Context, nd ipld.Node, name string, dag ipld.DAGServ // the case for 1. archive, and 2. not archived and not compressed, in which tar is used anyway as a transport format // construct the tar writer - w, err := tar.NewWriter(ctx, dag, archive, compression, maybeGzw) + w, err := tar.NewWriter(ctx, dag, maybeGzw) if checkErrAndClosePipe(err) { return nil, err } diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 4503f5593..eddb44673 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -30,7 +30,7 @@ type Writer struct { } // NewWriter wraps given io.Writer. -func NewWriter(ctx context.Context, dag ipld.DAGService, archive bool, compression int, w io.Writer) (*Writer, error) { +func NewWriter(ctx context.Context, dag ipld.DAGService, w io.Writer) (*Writer, error) { return &Writer{ Dag: dag, TarW: tar.NewWriter(w), From c5a04a72619b4b79d409699ece319d5cbbe1659e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 19 Mar 2018 02:09:29 +0100 Subject: [PATCH 2265/3817] misc: Remove some dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-path@2b1ce8e93ea3d515d8311de1cbb6f69a26919a4d --- path/path.go | 2 +- path/path_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/path/path.go b/path/path.go index 924aa5dc1..ac9348b56 100644 --- a/path/path.go +++ b/path/path.go @@ -62,7 +62,7 @@ func (p Path) String() string { // IsJustAKey returns true if the path is of the form or /ipfs/. func (p Path) IsJustAKey() bool { parts := p.Segments() - return (len(parts) == 2 && parts[0] == "ipfs") + return len(parts) == 2 && parts[0] == "ipfs" } // PopLastSegment returns a new Path without its final segment, and the final diff --git a/path/path_test.go b/path/path_test.go index c3bdcd59e..b095ffd98 100644 --- a/path/path_test.go +++ b/path/path_test.go @@ -22,7 +22,7 @@ func TestPathParsing(t *testing.T) { for p, expected := range cases { _, err := ParsePath(p) - valid := (err == nil) + valid := err == nil if valid != expected { t.Fatalf("expected %s to have valid == %t", p, expected) } From e2c06e292e9a43d83997121555991d3e24d2c0de Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Wed, 21 Mar 2018 22:58:32 -0400 Subject: [PATCH 2266/3817] Return ErrNotFound when zero values are returned from DHT namesys resolve License: MIT Signed-off-by: Dirk McCormick This commit was moved from ipfs/go-namesys@32e77f935ee01070806fa8fe6823175b509509c8 --- namesys/routing.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/namesys/routing.go b/namesys/routing.go index 29b26cdbd..ccf305066 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -202,6 +202,10 @@ func (r *routingResolver) getValue(ctx context.Context, ipnsKey string, options } } + if len(recs) == 0 { + return nil, routing.ErrNotFound + } + i, err := IpnsSelectorFunc(ipnsKey, recs) if err != nil { return nil, err From fbeb8ead4e2f9a387afe83e56fe5d1c4b7108c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 22 Mar 2018 04:01:13 +0100 Subject: [PATCH 2267/3817] fix govet warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-filestore@8390183529a95abcd0521da2fe46aa242d1300ef --- filestore/fsrefstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 84a02d426..9e8ff73e4 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -77,7 +77,7 @@ func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) k := ds.RawKey(v.Key) c, err := dshelp.DsKeyToCid(k) if err != nil { - log.Error("decoding cid from filestore: %s", err) + log.Errorf("decoding cid from filestore: %s", err) continue } From 856dcd082eda0136a1c5907da9183f66659c2ac6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 7 Mar 2018 18:25:42 -0800 Subject: [PATCH 2268/3817] don't make assumptions about readers in the dagmodifier The dagmodifier *only* works because we're using a bytes.Buffer. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@72e35de5164bb7ad3f4754737f0b819a40f62293 --- unixfs/mod/dagmodifier.go | 63 +++++++++++++++------------------------ 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 8f2766aee..0fe9df1e4 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -202,7 +202,7 @@ func (dm *DagModifier) Sync() error { buflen := dm.wrBuf.Len() // overwrite existing dag nodes - thisc, done, err := dm.modifyDag(dm.curNode, dm.writeStart, dm.wrBuf) + thisc, err := dm.modifyDag(dm.curNode, dm.writeStart) if err != nil { return err } @@ -213,7 +213,7 @@ func (dm *DagModifier) Sync() error { } // need to write past end of current dag - if !done { + if dm.wrBuf.Len() > 0 { dm.curNode, err = dm.appendData(dm.curNode, dm.splitter(dm.wrBuf)) if err != nil { return err @@ -231,28 +231,27 @@ func (dm *DagModifier) Sync() error { return nil } -// modifyDag writes the data in 'data' over the data in 'node' starting at 'offset' -// returns the new key of the passed in node and whether or not all the data in the reader -// has been consumed. -func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64, data io.Reader) (*cid.Cid, bool, error) { +// modifyDag writes the data in 'dm.wrBuf' over the data in 'node' starting at 'offset' +// returns the new key of the passed in node. +func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64) (*cid.Cid, error) { // If we've reached a leaf node. if len(n.Links()) == 0 { switch nd0 := n.(type) { case *mdag.ProtoNode: f, err := ft.FromBytes(nd0.Data()) if err != nil { - return nil, false, err + return nil, err } - n, err := data.Read(f.Data[offset:]) + _, err = dm.wrBuf.Read(f.Data[offset:]) if err != nil && err != io.EOF { - return nil, false, err + return nil, err } // Update newly written node.. b, err := proto.Marshal(f) if err != nil { - return nil, false, err + return nil, err } nd := new(mdag.ProtoNode) @@ -260,16 +259,10 @@ func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64, data io.Reader) (*c nd.SetPrefix(&nd0.Prefix) err = dm.dagserv.Add(dm.ctx, nd) if err != nil { - return nil, false, err - } - - // Hey look! we're done! - var done bool - if n < len(f.Data[offset:]) { - done = true + return nil, err } - return nd.Cid(), done, nil + return nd.Cid(), nil case *mdag.RawNode: origData := nd0.RawData() bytes := make([]byte, len(origData)) @@ -278,9 +271,9 @@ func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64, data io.Reader) (*c copy(bytes, origData[:offset]) // copy in new data - n, err := data.Read(bytes[offset:]) + n, err := dm.wrBuf.Read(bytes[offset:]) if err != nil && err != io.EOF { - return nil, false, err + return nil, err } // copy remaining data @@ -291,46 +284,39 @@ func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64, data io.Reader) (*c nd, err := mdag.NewRawNodeWPrefix(bytes, nd0.Cid().Prefix()) if err != nil { - return nil, false, err + return nil, err } err = dm.dagserv.Add(dm.ctx, nd) if err != nil { - return nil, false, err - } - - // Hey look! we're done! - var done bool - if n < len(bytes[offset:]) { - done = true + return nil, err } - return nd.Cid(), done, nil + return nd.Cid(), nil } } node, ok := n.(*mdag.ProtoNode) if !ok { - return nil, false, ErrNotUnixfs + return nil, ErrNotUnixfs } f, err := ft.FromBytes(node.Data()) if err != nil { - return nil, false, err + return nil, err } var cur uint64 - var done bool for i, bs := range f.GetBlocksizes() { // We found the correct child to write into if cur+bs > offset { child, err := node.Links()[i].GetNode(dm.ctx, dm.dagserv) if err != nil { - return nil, false, err + return nil, err } - k, sdone, err := dm.modifyDag(child, offset-cur, data) + k, err := dm.modifyDag(child, offset-cur) if err != nil { - return nil, false, err + return nil, err } node.Links()[i].Cid = k @@ -338,12 +324,11 @@ func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64, data io.Reader) (*c // Recache serialized node _, err = node.EncodeProtobuf(true) if err != nil { - return nil, false, err + return nil, err } - if sdone { + if dm.wrBuf.Len() == 0 { // No more bytes to write! - done = true break } offset = cur + bs @@ -352,7 +337,7 @@ func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64, data io.Reader) (*c } err = dm.dagserv.Add(dm.ctx, node) - return node.Cid(), done, err + return node.Cid(), err } // appendData appends the blocks from the given chan to the end of this dag From 53687fd540d218d0b07ddce802e19002e6f23610 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 7 Mar 2018 18:30:00 -0800 Subject: [PATCH 2269/3817] don't assume that Read reads all available bytes in pbdagreader License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@433814ad75922f8e42038977e63bb260084d847d --- unixfs/io/pbdagreader.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index ce3abf439..e5fbaa928 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -181,6 +181,11 @@ func (dr *PBDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { return total, nil } + // We haven't hit the end yet. + if err != io.EOF { + continue + } + // Otherwise, load up the next block err = dr.precalcNextBuf(ctx) if err != nil { From 9799ff64c5aa4b087c8b9f59c04cedc411c146be Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 8 Mar 2018 23:16:31 +0100 Subject: [PATCH 2270/3817] pbreader: use ReadFull instead of reimplementing it License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-unixfs@f6d577e22f3b9c9078d120e96ced6ebf33303c19 --- unixfs/io/pbdagreader.go | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index e5fbaa928..5be60bd8e 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -166,27 +166,20 @@ func (dr *PBDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { total := 0 for { // Attempt to fill bytes from cached buffer - n, err := dr.buf.Read(b[total:]) + n, err := io.ReadFull(dr.buf, b[total:]) total += n dr.offset += int64(n) - if err != nil { - // EOF is expected - if err != io.EOF { - return total, err - } - } - - // If weve read enough bytes, return - if total == len(b) { + switch err { + // io.EOF will happen is dr.buf had noting more to read (n == 0) + case io.EOF, io.ErrUnexpectedEOF: + // do nothing + case nil: return total, nil + default: + return total, err } - // We haven't hit the end yet. - if err != io.EOF { - continue - } - - // Otherwise, load up the next block + // if we are not done with the output buffer load next block err = dr.precalcNextBuf(ctx) if err != nil { return total, err From fb44c18e78886b3d99be1c60c5399c7e46d198cc Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 23 Mar 2018 15:56:57 -0700 Subject: [PATCH 2271/3817] make the tar writer handle sharded ipfs directories makes ipfs get work on sharded directories fixes #4871 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@da35c8e47c49e36bec032f6b9059e3826db0ef1c --- unixfs/archive/tar/writer.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 4503f5593..7e6f13894 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -39,23 +39,25 @@ func NewWriter(ctx context.Context, dag ipld.DAGService, archive bool, compressi } func (w *Writer) writeDir(nd *mdag.ProtoNode, fpath string) error { + dir, err := uio.NewDirectoryFromNode(w.Dag, nd) + if err != nil { + return err + } if err := writeDirHeader(w.TarW, fpath); err != nil { return err } - for i, ng := range ipld.GetDAG(w.ctx, w.Dag, nd) { - child, err := ng.Get(w.ctx) + return dir.ForEachLink(w.ctx, func(l *ipld.Link) error { + child, err := w.Dag.Get(w.ctx, l.Cid) if err != nil { return err } - - npath := path.Join(fpath, nd.Links()[i].Name) + npath := path.Join(fpath, l.Name) if err := w.WriteNode(child, npath); err != nil { return err } - } - - return nil + return nil + }) } func (w *Writer) writeFile(nd *mdag.ProtoNode, pb *upb.Data, fpath string) error { @@ -83,7 +85,7 @@ func (w *Writer) WriteNode(nd ipld.Node, fpath string) error { switch pb.GetType() { case upb.Data_Metadata: fallthrough - case upb.Data_Directory: + case upb.Data_Directory, upb.Data_HAMTShard: return w.writeDir(nd, fpath) case upb.Data_Raw: fallthrough From 56f7d2f492b5a1638d90c702f3915b629df777ac Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 23 Mar 2018 15:59:21 -0700 Subject: [PATCH 2272/3817] fix error when encountering a sharded directory when expecting a file License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@178c24af3deafa32564f960e4e21472f708eea56 --- unixfs/io/pbdagreader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 5be60bd8e..ac08fade8 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -112,7 +112,7 @@ func (dr *PBDagReader) precalcNextBuf(ctx context.Context) error { } switch pb.GetType() { - case ftpb.Data_Directory: + case ftpb.Data_Directory, ftpb.Data_HAMTShard: // A directory should not exist within a file return ft.ErrInvalidDirLocation case ftpb.Data_File: From 23177c7b3decb426c21b7a02fb6b76da1e1d6d29 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 23 Mar 2018 18:02:02 -0700 Subject: [PATCH 2273/3817] appease go lint License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@d1ae33783af51cefc48d2bf62665b4a5e0890ddb --- unixfs/archive/tar/writer.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 7e6f13894..04f5bc4cc 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -53,10 +53,7 @@ func (w *Writer) writeDir(nd *mdag.ProtoNode, fpath string) error { return err } npath := path.Join(fpath, l.Name) - if err := w.WriteNode(child, npath); err != nil { - return err - } - return nil + return w.WriteNode(child, npath) }) } From 595afb8260f48d77dfff80a7d4a191f185adfb87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 11 Mar 2018 18:55:35 +0100 Subject: [PATCH 2274/3817] coreapi: remove options from interfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@38ccf0555876033759418de2c43b5d9c727b2c19 --- coreiface/block.go | 13 ------------- coreiface/dag.go | 18 ------------------ coreiface/key.go | 19 ------------------- coreiface/name.go | 24 ------------------------ coreiface/object.go | 28 ---------------------------- coreiface/options/block.go | 17 +++++++++++++---- coreiface/options/dag.go | 22 +++++++++++++++++----- coreiface/options/key.go | 23 +++++++++++++++++++---- coreiface/options/name.go | 27 +++++++++++++++++++++------ coreiface/options/object.go | 32 +++++++++++++++++++++++++++----- coreiface/options/pin.go | 23 +++++++++++++++++++---- coreiface/pin.go | 15 --------------- 12 files changed, 116 insertions(+), 145 deletions(-) diff --git a/coreiface/block.go b/coreiface/block.go index f38a664c3..a9e577d76 100644 --- a/coreiface/block.go +++ b/coreiface/block.go @@ -21,15 +21,6 @@ type BlockAPI interface { // Put imports raw block data, hashing it using specified settings. Put(context.Context, io.Reader, ...options.BlockPutOption) (Path, error) - // WithFormat is an option for Put which specifies the multicodec to use to - // serialize the object. Default is "v0" - WithFormat(codec string) options.BlockPutOption - - // WithHash is an option for 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 - WithHash(mhType uint64, mhLen int) options.BlockPutOption - // Get attempts to resolve the path and return a reader for data in the block Get(context.Context, Path) (io.Reader, error) @@ -40,10 +31,6 @@ type BlockAPI interface { // will be returned Rm(context.Context, Path, ...options.BlockRmOption) error - // WithForce is an option for Rm which, when set to true, will ignore - // non-existing blocks - WithForce(force bool) options.BlockRmOption - // Stat returns information on Stat(context.Context, Path) (BlockStat, error) } diff --git a/coreiface/dag.go b/coreiface/dag.go index 1635d71b1..f20c88f25 100644 --- a/coreiface/dag.go +++ b/coreiface/dag.go @@ -16,27 +16,9 @@ type DagAPI interface { // "sha256" are used. Put(ctx context.Context, src io.Reader, opts ...options.DagPutOption) (Path, error) - // WithInputEnc is an option for Put which specifies the input encoding of the - // data. Default is "json", most formats/codecs support "raw" - WithInputEnc(enc string) options.DagPutOption - - // WithCodec is an option for Put which specifies the multicodec to use to - // serialize the object. Default is cid.DagCBOR (0x71) - WithCodec(codec uint64) options.DagPutOption - - // WithHash is an option for Put which specifies the multihash settings to use - // when hashing the object. Default is based on the codec used - // (mh.SHA2_256 (0x12) for DagCBOR). If mhLen is set to -1, default length for - // the hash will be used - WithHash(mhType uint64, mhLen int) options.DagPutOption - // Get attempts to resolve and get the node specified by the path Get(ctx context.Context, path Path) (ipld.Node, error) // Tree returns list of paths within a node specified by the path. Tree(ctx context.Context, path Path, opts ...options.DagTreeOption) ([]Path, error) - - // WithDepth is an option for Tree which specifies maximum depth of the - // returned tree. Default is -1 (no depth limit) - WithDepth(depth int) options.DagTreeOption } diff --git a/coreiface/key.go b/coreiface/key.go index 730e855d7..928aa265f 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -20,29 +20,10 @@ type KeyAPI interface { // name and returns a base58 encoded multihash of it's public key Generate(ctx context.Context, name string, opts ...options.KeyGenerateOption) (Key, error) - // WithType is an option for Generate which specifies which algorithm - // should be used for the key. Default is options.RSAKey - // - // Supported key types: - // * options.RSAKey - // * options.Ed25519Key - WithType(algorithm string) options.KeyGenerateOption - - // WithSize is an option for Generate which specifies the size of the key to - // generated. Default is -1 - // - // value of -1 means 'use default size for key type': - // * 2048 for RSA - WithSize(size int) options.KeyGenerateOption - // Rename renames oldName key to newName. Returns the key and whether another // key was overwritten, or an error Rename(ctx context.Context, oldName string, newName string, opts ...options.KeyRenameOption) (Key, bool, error) - // WithForce is an option for Rename which specifies whether to allow to - // replace existing keys. - WithForce(force bool) options.KeyRenameOption - // List lists keys stored in keystore List(ctx context.Context) ([]Key, error) diff --git a/coreiface/name.go b/coreiface/name.go index 6d17d840a..a6aad0c3e 100644 --- a/coreiface/name.go +++ b/coreiface/name.go @@ -2,7 +2,6 @@ package iface import ( "context" - "time" options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) @@ -27,29 +26,6 @@ type NameAPI interface { // Publish announces new IPNS name Publish(ctx context.Context, path Path, opts ...options.NamePublishOption) (IpnsEntry, error) - // WithValidTime is an option for Publish which specifies for how long the - // entry will remain valid. Default value is 24h - WithValidTime(validTime time.Duration) options.NamePublishOption - - // WithKey is an option for Publish which specifies the key to use for - // publishing. Default value is "self" which is the node's own PeerID. - // The key parameter must be either PeerID or keystore key alias. - // - // You can use KeyAPI to list and generate more names and their respective keys. - WithKey(key string) options.NamePublishOption - // Resolve attempts to resolve the newest version of the specified name Resolve(ctx context.Context, name string, opts ...options.NameResolveOption) (Path, error) - - // WithRecursive is an option for Resolve which specifies whether to perform a - // recursive lookup. Default value is false - WithRecursive(recursive bool) options.NameResolveOption - - // WithLocal is an option for Resolve which specifies if the lookup should be - // offline. Default value is false - WithLocal(local bool) options.NameResolveOption - - // WithCache is an option for Resolve which specifies if cache should be used. - // Default value is true - WithCache(cache bool) options.NameResolveOption } diff --git a/coreiface/object.go b/coreiface/object.go index 75837f93e..548b15a73 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -37,33 +37,9 @@ type ObjectAPI interface { // New creates new, empty (by default) dag-node. New(context.Context, ...options.ObjectNewOption) (ipld.Node, error) - // WithType is an option for New which allows to change the type of created - // dag node. - // - // Supported types: - // * 'empty' - Empty node - // * 'unixfs-dir' - Empty UnixFS directory - WithType(string) options.ObjectNewOption - // Put imports the data into merkledag Put(context.Context, io.Reader, ...options.ObjectPutOption) (Path, error) - // WithInputEnc is an option for Put which specifies the input encoding of the - // data. Default is "json". - // - // Supported encodings: - // * "protobuf" - // * "json" - WithInputEnc(e string) options.ObjectPutOption - - // WithDataType specifies the encoding of data field when using Josn or XML - // input encoding. - // - // Supported types: - // * "text" (default) - // * "base64" - WithDataType(t string) options.ObjectPutOption - // Get returns the node for the path Get(context.Context, Path) (ipld.Node, error) @@ -81,10 +57,6 @@ type ObjectAPI interface { // with WithCreate option). AddLink(ctx context.Context, base Path, name string, child Path, opts ...options.ObjectAddLinkOption) (Path, error) - // WithCreate is an option for AddLink which specifies whether create required - // directories for the child - WithCreate(create bool) options.ObjectAddLinkOption - // RmLink removes a link from the node RmLink(ctx context.Context, base Path, link string) (Path, error) diff --git a/coreiface/options/block.go b/coreiface/options/block.go index bbb14612f..20320705e 100644 --- a/coreiface/options/block.go +++ b/coreiface/options/block.go @@ -47,16 +47,23 @@ func BlockRmOptions(opts ...BlockRmOption) (*BlockRmSettings, error) { return options, nil } -type BlockOptions struct{} +type blockOpts struct{} -func (api *BlockOptions) WithFormat(codec string) BlockPutOption { +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 { return func(settings *BlockPutSettings) error { settings.Codec = codec return nil } } -func (api *BlockOptions) WithHash(mhType uint64, mhLen int) BlockPutOption { +// 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 @@ -64,7 +71,9 @@ func (api *BlockOptions) WithHash(mhType uint64, mhLen int) BlockPutOption { } } -func (api *BlockOptions) WithForce(force bool) BlockRmOption { +// Force is an option for Block.Rm which, when set to true, will ignore +// non-existing blocks +func (_ blockOpts) Force(force bool) BlockRmOption { return func(settings *BlockRmSettings) error { settings.Force = force return nil diff --git a/coreiface/options/dag.go b/coreiface/options/dag.go index b56fcd81a..ec258cf95 100644 --- a/coreiface/options/dag.go +++ b/coreiface/options/dag.go @@ -51,23 +51,33 @@ func DagTreeOptions(opts ...DagTreeOption) (*DagTreeSettings, error) { return options, nil } -type DagOptions struct{} +type dagOpts struct{} -func (api *DagOptions) WithInputEnc(enc string) DagPutOption { +var Dag dagOpts + +// InputEnc is an option for Dag.Put which specifies the input encoding of the +// data. Default is "json", most formats/codecs support "raw" +func (_ dagOpts) InputEnc(enc string) DagPutOption { return func(settings *DagPutSettings) error { settings.InputEnc = enc return nil } } -func (api *DagOptions) WithCodec(codec uint64) DagPutOption { +// Codec is an option for Dag.Put which specifies the multicodec to use to +// serialize the object. Default is cid.DagCBOR (0x71) +func (_ dagOpts) Codec(codec uint64) DagPutOption { return func(settings *DagPutSettings) error { settings.Codec = codec return nil } } -func (api *DagOptions) WithHash(mhType uint64, mhLen int) DagPutOption { +// Hash is an option for Dag.Put which specifies the multihash settings to use +// when hashing the object. Default is based on the codec used +// (mh.SHA2_256 (0x12) for DagCBOR). If mhLen is set to -1, default length for +// the hash will be used +func (_ dagOpts) Hash(mhType uint64, mhLen int) DagPutOption { return func(settings *DagPutSettings) error { settings.MhType = mhType settings.MhLength = mhLen @@ -75,7 +85,9 @@ func (api *DagOptions) WithHash(mhType uint64, mhLen int) DagPutOption { } } -func (api *DagOptions) WithDepth(depth int) DagTreeOption { +// Depth is an option for Dag.Tree which specifies maximum depth of the +// returned tree. Default is -1 (no depth limit) +func (_ dagOpts) Depth(depth int) DagTreeOption { return func(settings *DagTreeSettings) error { settings.Depth = depth return nil diff --git a/coreiface/options/key.go b/coreiface/options/key.go index 114361875..a29261d14 100644 --- a/coreiface/options/key.go +++ b/coreiface/options/key.go @@ -48,23 +48,38 @@ func KeyRenameOptions(opts ...KeyRenameOption) (*KeyRenameSettings, error) { return options, nil } -type KeyOptions struct{} +type keyOpts struct{} -func (api *KeyOptions) WithType(algorithm string) KeyGenerateOption { +var Key keyOpts + +// Type is an option for Key.Generate which specifies which algorithm +// should be used for the key. Default is options.RSAKey +// +// Supported key types: +// * options.RSAKey +// * options.Ed25519Key +func (_ keyOpts) Type(algorithm string) KeyGenerateOption { return func(settings *KeyGenerateSettings) error { settings.Algorithm = algorithm return nil } } -func (api *KeyOptions) WithSize(size int) KeyGenerateOption { +// Size is an option for Key.Generate which specifies the size of the key to +// generated. Default is -1 +// +// value of -1 means 'use default size for key type': +// * 2048 for RSA +func (_ keyOpts) Size(size int) KeyGenerateOption { return func(settings *KeyGenerateSettings) error { settings.Size = size return nil } } -func (api *KeyOptions) WithForce(force bool) KeyRenameOption { +// Force is an option for Key.Rename which specifies whether to allow to +// replace existing keys. +func (_ keyOpts) Force(force bool) KeyRenameOption { return func(settings *KeyRenameSettings) error { settings.Force = force return nil diff --git a/coreiface/options/name.go b/coreiface/options/name.go index 9f8aaafc8..1f6de0ee3 100644 --- a/coreiface/options/name.go +++ b/coreiface/options/name.go @@ -55,37 +55,52 @@ func NameResolveOptions(opts ...NameResolveOption) (*NameResolveSettings, error) return options, nil } -type NameOptions struct{} +type nameOpts struct{} -func (api *NameOptions) WithValidTime(validTime time.Duration) NamePublishOption { +var Name nameOpts + +// ValidTime is an option for Name.Publish which specifies for how long the +// entry will remain valid. Default value is 24h +func (_ nameOpts) ValidTime(validTime time.Duration) NamePublishOption { return func(settings *NamePublishSettings) error { settings.ValidTime = validTime return nil } } -func (api *NameOptions) WithKey(key string) NamePublishOption { +// Key is an option for Name.Publish which specifies the key to use for +// publishing. Default value is "self" which is the node's own PeerID. +// The key parameter must be either PeerID or keystore key alias. +// +// You can use KeyAPI to list and generate more names and their respective keys. +func (_ nameOpts) Key(key string) NamePublishOption { return func(settings *NamePublishSettings) error { settings.Key = key return nil } } -func (api *NameOptions) WithRecursive(recursive bool) NameResolveOption { +// Recursive is an option for Name.Resolve which specifies whether to perform a +// recursive lookup. Default value is false +func (_ nameOpts) Recursive(recursive bool) NameResolveOption { return func(settings *NameResolveSettings) error { settings.Recursive = recursive return nil } } -func (api *NameOptions) WithLocal(local bool) NameResolveOption { +// Local is an option for Name.Resolve which specifies if the lookup should be +// offline. Default value is false +func (_ nameOpts) Local(local bool) NameResolveOption { return func(settings *NameResolveSettings) error { settings.Local = local return nil } } -func (api *NameOptions) WithCache(cache bool) NameResolveOption { +// Cache is an option for Name.Resolve which specifies if cache should be used. +// Default value is true +func (_ nameOpts) Cache(cache bool) NameResolveOption { return func(settings *NameResolveSettings) error { settings.Cache = cache return nil diff --git a/coreiface/options/object.go b/coreiface/options/object.go index 9c8c9a9dd..00e41d28b 100644 --- a/coreiface/options/object.go +++ b/coreiface/options/object.go @@ -60,30 +60,52 @@ func ObjectAddLinkOptions(opts ...ObjectAddLinkOption) (*ObjectAddLinkSettings, return options, nil } -type ObjectOptions struct{} +type objectOpts struct{} -func (api *ObjectOptions) WithType(t string) ObjectNewOption { +var Object objectOpts + +// Type is an option for Object.New which allows to change the type of created +// dag node. +// +// Supported types: +// * 'empty' - Empty node +// * 'unixfs-dir' - Empty UnixFS directory +func (_ objectOpts) Type(t string) ObjectNewOption { return func(settings *ObjectNewSettings) error { settings.Type = t return nil } } -func (api *ObjectOptions) WithInputEnc(e string) ObjectPutOption { +// InputEnc is an option for Object.Put which specifies the input encoding of the +// data. Default is "json". +// +// Supported encodings: +// * "protobuf" +// * "json" +func (_ objectOpts) InputEnc(e string) ObjectPutOption { return func(settings *ObjectPutSettings) error { settings.InputEnc = e return nil } } -func (api *ObjectOptions) WithDataType(t string) ObjectPutOption { +// DataType is an option for Object.Put which specifies the encoding of data +// field when using Json or XML input encoding. +// +// Supported types: +// * "text" (default) +// * "base64" +func (_ objectOpts) DataType(t string) ObjectPutOption { return func(settings *ObjectPutSettings) error { settings.DataType = t return nil } } -func (api *ObjectOptions) WithCreate(create bool) ObjectAddLinkOption { +// Create is an option for Object.AddLink which specifies whether create required +// directories for the child +func (_ objectOpts) Create(create bool) ObjectAddLinkOption { return func(settings *ObjectAddLinkSettings) error { settings.Create = create return nil diff --git a/coreiface/options/pin.go b/coreiface/options/pin.go index f97f7b16e..680ed391d 100644 --- a/coreiface/options/pin.go +++ b/coreiface/options/pin.go @@ -61,23 +61,38 @@ func PinUpdateOptions(opts ...PinUpdateOption) (*PinUpdateSettings, error) { return options, nil } -type PinOptions struct{} +type pinOpts struct{} -func (api *PinOptions) WithRecursive(recucsive bool) PinAddOption { +var Pin pinOpts + +// Recursive is an option for Pin.Add which specifies whether to pin an entire +// object tree or just one object. Default: true +func (_ pinOpts) Recursive(recucsive bool) PinAddOption { return func(settings *PinAddSettings) error { settings.Recursive = recucsive return nil } } -func (api *PinOptions) WithType(t string) PinLsOption { +// Type is an option for Pin.Ls which allows to specify which pin types should +// be returned +// +// Supported values: +// * "direct" - directly pinned objects +// * "recursive" - roots of recursive pins +// * "indirect" - indirectly pinned objects (referenced by recursively pinned +// objects) +// * "all" - all pinned objects (default) +func (_ pinOpts) Type(t string) PinLsOption { return func(settings *PinLsSettings) error { settings.Type = t return nil } } -func (api *PinOptions) WithUnpin(unpin bool) PinUpdateOption { +// Unpin is an option for Pin.Update which specifies whether to remove the old pin. +// Default is true. +func (_ pinOpts) Unpin(unpin bool) PinUpdateOption { return func(settings *PinUpdateSettings) error { settings.Unpin = unpin return nil diff --git a/coreiface/pin.go b/coreiface/pin.go index 47a5a0bb2..5994c7586 100644 --- a/coreiface/pin.go +++ b/coreiface/pin.go @@ -39,24 +39,9 @@ type PinAPI interface { // tree Add(context.Context, Path, ...options.PinAddOption) error - // WithRecursive is an option for Add which specifies whether to pin an entire - // object tree or just one object. Default: true - WithRecursive(bool) options.PinAddOption - // Ls returns list of pinned objects on this node Ls(context.Context, ...options.PinLsOption) ([]Pin, error) - // WithType is an option for Ls which allows to specify which pin types should - // be returned - // - // Supported values: - // * "direct" - directly pinned objects - // * "recursive" - roots of recursive pins - // * "indirect" - indirectly pinned objects (referenced by recursively pinned - // objects) - // * "all" - all pinned objects (default) - WithType(string) options.PinLsOption - // Rm removes pin for object specified by the path Rm(context.Context, Path) error From 8a0f4029598ebf3be0d56e91fe07675702349a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 25 Mar 2018 13:58:29 +0200 Subject: [PATCH 2275/3817] coreapi: use defined functions for pin type option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@2e3f9758c88ce4572922c3fd4e8ef51c703e183d --- coreiface/options/pin.go | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/coreiface/options/pin.go b/coreiface/options/pin.go index 680ed391d..7cb4d09d2 100644 --- a/coreiface/options/pin.go +++ b/coreiface/options/pin.go @@ -61,10 +61,38 @@ func PinUpdateOptions(opts ...PinUpdateOption) (*PinUpdateSettings, error) { return options, nil } -type pinOpts struct{} +type pinType struct{} + +type pinOpts struct { + Type pinType +} var Pin pinOpts +// All is an option for Pin.Ls which will make it return all pins. It is +// the default +func (_ pinType) All() PinLsOption { + return Pin.pinType("all") +} + +// Recursive is an option for Pin.Ls which will make it only return recursive +// pins +func (_ pinType) Recursive() PinLsOption { + return Pin.pinType("recursive") +} + +// Direct is an option for Pin.Ls which will make it only return direct (non +// recursive) pins +func (_ pinType) Direct() PinLsOption { + return Pin.pinType("direct") +} + +// Indirect is an option for Pin.Ls which will make it only return indirect pins +// (objects referenced by other recursively pinned objects) +func (_ pinType) Indirect() PinLsOption { + return Pin.pinType("indirect") +} + // Recursive is an option for Pin.Add which specifies whether to pin an entire // object tree or just one object. Default: true func (_ pinOpts) Recursive(recucsive bool) PinAddOption { @@ -83,7 +111,7 @@ func (_ pinOpts) Recursive(recucsive bool) PinAddOption { // * "indirect" - indirectly pinned objects (referenced by recursively pinned // objects) // * "all" - all pinned objects (default) -func (_ pinOpts) Type(t string) PinLsOption { +func (_ pinOpts) pinType(t string) PinLsOption { return func(settings *PinLsSettings) error { settings.Type = t return nil From 6bd3ea5ab8c0df771963b4f8a9f5cfdc18a95b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 25 Mar 2018 14:09:59 +0200 Subject: [PATCH 2276/3817] coreapi: don't use underscores in opt reciever funcs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@8b65fc5e1ed16902cda0e3e36fe624b01856b182 --- coreiface/options/block.go | 6 +++--- coreiface/options/dag.go | 8 ++++---- coreiface/options/key.go | 6 +++--- coreiface/options/name.go | 10 +++++----- coreiface/options/object.go | 8 ++++---- coreiface/options/pin.go | 14 +++++++------- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/coreiface/options/block.go b/coreiface/options/block.go index 20320705e..55964b2b7 100644 --- a/coreiface/options/block.go +++ b/coreiface/options/block.go @@ -53,7 +53,7 @@ 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 { +func (blockOpts) Format(codec string) BlockPutOption { return func(settings *BlockPutSettings) error { settings.Codec = codec return nil @@ -63,7 +63,7 @@ func (_ blockOpts) Format(codec string) BlockPutOption { // 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 { +func (blockOpts) Hash(mhType uint64, mhLen int) BlockPutOption { return func(settings *BlockPutSettings) error { settings.MhType = mhType settings.MhLength = mhLen @@ -73,7 +73,7 @@ func (_ blockOpts) Hash(mhType uint64, mhLen int) BlockPutOption { // Force is an option for Block.Rm which, when set to true, will ignore // non-existing blocks -func (_ blockOpts) Force(force bool) BlockRmOption { +func (blockOpts) Force(force bool) BlockRmOption { return func(settings *BlockRmSettings) error { settings.Force = force return nil diff --git a/coreiface/options/dag.go b/coreiface/options/dag.go index ec258cf95..96eea5b46 100644 --- a/coreiface/options/dag.go +++ b/coreiface/options/dag.go @@ -57,7 +57,7 @@ var Dag dagOpts // InputEnc is an option for Dag.Put which specifies the input encoding of the // data. Default is "json", most formats/codecs support "raw" -func (_ dagOpts) InputEnc(enc string) DagPutOption { +func (dagOpts) InputEnc(enc string) DagPutOption { return func(settings *DagPutSettings) error { settings.InputEnc = enc return nil @@ -66,7 +66,7 @@ func (_ dagOpts) InputEnc(enc string) DagPutOption { // Codec is an option for Dag.Put which specifies the multicodec to use to // serialize the object. Default is cid.DagCBOR (0x71) -func (_ dagOpts) Codec(codec uint64) DagPutOption { +func (dagOpts) Codec(codec uint64) DagPutOption { return func(settings *DagPutSettings) error { settings.Codec = codec return nil @@ -77,7 +77,7 @@ func (_ dagOpts) Codec(codec uint64) DagPutOption { // when hashing the object. Default is based on the codec used // (mh.SHA2_256 (0x12) for DagCBOR). If mhLen is set to -1, default length for // the hash will be used -func (_ dagOpts) Hash(mhType uint64, mhLen int) DagPutOption { +func (dagOpts) Hash(mhType uint64, mhLen int) DagPutOption { return func(settings *DagPutSettings) error { settings.MhType = mhType settings.MhLength = mhLen @@ -87,7 +87,7 @@ func (_ dagOpts) Hash(mhType uint64, mhLen int) DagPutOption { // Depth is an option for Dag.Tree which specifies maximum depth of the // returned tree. Default is -1 (no depth limit) -func (_ dagOpts) Depth(depth int) DagTreeOption { +func (dagOpts) Depth(depth int) DagTreeOption { return func(settings *DagTreeSettings) error { settings.Depth = depth return nil diff --git a/coreiface/options/key.go b/coreiface/options/key.go index a29261d14..80beea352 100644 --- a/coreiface/options/key.go +++ b/coreiface/options/key.go @@ -58,7 +58,7 @@ var Key keyOpts // Supported key types: // * options.RSAKey // * options.Ed25519Key -func (_ keyOpts) Type(algorithm string) KeyGenerateOption { +func (keyOpts) Type(algorithm string) KeyGenerateOption { return func(settings *KeyGenerateSettings) error { settings.Algorithm = algorithm return nil @@ -70,7 +70,7 @@ func (_ keyOpts) Type(algorithm string) KeyGenerateOption { // // value of -1 means 'use default size for key type': // * 2048 for RSA -func (_ keyOpts) Size(size int) KeyGenerateOption { +func (keyOpts) Size(size int) KeyGenerateOption { return func(settings *KeyGenerateSettings) error { settings.Size = size return nil @@ -79,7 +79,7 @@ func (_ keyOpts) Size(size int) KeyGenerateOption { // Force is an option for Key.Rename which specifies whether to allow to // replace existing keys. -func (_ keyOpts) Force(force bool) KeyRenameOption { +func (keyOpts) Force(force bool) KeyRenameOption { return func(settings *KeyRenameSettings) error { settings.Force = force return nil diff --git a/coreiface/options/name.go b/coreiface/options/name.go index 1f6de0ee3..48aecf18b 100644 --- a/coreiface/options/name.go +++ b/coreiface/options/name.go @@ -61,7 +61,7 @@ var Name nameOpts // ValidTime is an option for Name.Publish which specifies for how long the // entry will remain valid. Default value is 24h -func (_ nameOpts) ValidTime(validTime time.Duration) NamePublishOption { +func (nameOpts) ValidTime(validTime time.Duration) NamePublishOption { return func(settings *NamePublishSettings) error { settings.ValidTime = validTime return nil @@ -73,7 +73,7 @@ func (_ nameOpts) ValidTime(validTime time.Duration) NamePublishOption { // The key parameter must be either PeerID or keystore key alias. // // You can use KeyAPI to list and generate more names and their respective keys. -func (_ nameOpts) Key(key string) NamePublishOption { +func (nameOpts) Key(key string) NamePublishOption { return func(settings *NamePublishSettings) error { settings.Key = key return nil @@ -82,7 +82,7 @@ func (_ nameOpts) Key(key string) NamePublishOption { // Recursive is an option for Name.Resolve which specifies whether to perform a // recursive lookup. Default value is false -func (_ nameOpts) Recursive(recursive bool) NameResolveOption { +func (nameOpts) Recursive(recursive bool) NameResolveOption { return func(settings *NameResolveSettings) error { settings.Recursive = recursive return nil @@ -91,7 +91,7 @@ func (_ nameOpts) Recursive(recursive bool) NameResolveOption { // Local is an option for Name.Resolve which specifies if the lookup should be // offline. Default value is false -func (_ nameOpts) Local(local bool) NameResolveOption { +func (nameOpts) Local(local bool) NameResolveOption { return func(settings *NameResolveSettings) error { settings.Local = local return nil @@ -100,7 +100,7 @@ func (_ nameOpts) Local(local bool) NameResolveOption { // Cache is an option for Name.Resolve which specifies if cache should be used. // Default value is true -func (_ nameOpts) Cache(cache bool) NameResolveOption { +func (nameOpts) Cache(cache bool) NameResolveOption { return func(settings *NameResolveSettings) error { settings.Cache = cache return nil diff --git a/coreiface/options/object.go b/coreiface/options/object.go index 00e41d28b..aca02d672 100644 --- a/coreiface/options/object.go +++ b/coreiface/options/object.go @@ -70,7 +70,7 @@ var Object objectOpts // Supported types: // * 'empty' - Empty node // * 'unixfs-dir' - Empty UnixFS directory -func (_ objectOpts) Type(t string) ObjectNewOption { +func (objectOpts) Type(t string) ObjectNewOption { return func(settings *ObjectNewSettings) error { settings.Type = t return nil @@ -83,7 +83,7 @@ func (_ objectOpts) Type(t string) ObjectNewOption { // Supported encodings: // * "protobuf" // * "json" -func (_ objectOpts) InputEnc(e string) ObjectPutOption { +func (objectOpts) InputEnc(e string) ObjectPutOption { return func(settings *ObjectPutSettings) error { settings.InputEnc = e return nil @@ -96,7 +96,7 @@ func (_ objectOpts) InputEnc(e string) ObjectPutOption { // Supported types: // * "text" (default) // * "base64" -func (_ objectOpts) DataType(t string) ObjectPutOption { +func (objectOpts) DataType(t string) ObjectPutOption { return func(settings *ObjectPutSettings) error { settings.DataType = t return nil @@ -105,7 +105,7 @@ func (_ objectOpts) DataType(t string) ObjectPutOption { // Create is an option for Object.AddLink which specifies whether create required // directories for the child -func (_ objectOpts) Create(create bool) ObjectAddLinkOption { +func (objectOpts) Create(create bool) ObjectAddLinkOption { return func(settings *ObjectAddLinkSettings) error { settings.Create = create return nil diff --git a/coreiface/options/pin.go b/coreiface/options/pin.go index 7cb4d09d2..e46c27246 100644 --- a/coreiface/options/pin.go +++ b/coreiface/options/pin.go @@ -71,31 +71,31 @@ var Pin pinOpts // All is an option for Pin.Ls which will make it return all pins. It is // the default -func (_ pinType) All() PinLsOption { +func (pinType) All() PinLsOption { return Pin.pinType("all") } // Recursive is an option for Pin.Ls which will make it only return recursive // pins -func (_ pinType) Recursive() PinLsOption { +func (pinType) Recursive() PinLsOption { return Pin.pinType("recursive") } // Direct is an option for Pin.Ls which will make it only return direct (non // recursive) pins -func (_ pinType) Direct() PinLsOption { +func (pinType) Direct() PinLsOption { return Pin.pinType("direct") } // Indirect is an option for Pin.Ls which will make it only return indirect pins // (objects referenced by other recursively pinned objects) -func (_ pinType) Indirect() PinLsOption { +func (pinType) Indirect() PinLsOption { return Pin.pinType("indirect") } // Recursive is an option for Pin.Add which specifies whether to pin an entire // object tree or just one object. Default: true -func (_ pinOpts) Recursive(recucsive bool) PinAddOption { +func (pinOpts) Recursive(recucsive bool) PinAddOption { return func(settings *PinAddSettings) error { settings.Recursive = recucsive return nil @@ -111,7 +111,7 @@ func (_ pinOpts) Recursive(recucsive bool) PinAddOption { // * "indirect" - indirectly pinned objects (referenced by recursively pinned // objects) // * "all" - all pinned objects (default) -func (_ pinOpts) pinType(t string) PinLsOption { +func (pinOpts) pinType(t string) PinLsOption { return func(settings *PinLsSettings) error { settings.Type = t return nil @@ -120,7 +120,7 @@ func (_ pinOpts) pinType(t string) PinLsOption { // Unpin is an option for Pin.Update which specifies whether to remove the old pin. // Default is true. -func (_ pinOpts) Unpin(unpin bool) PinUpdateOption { +func (pinOpts) Unpin(unpin bool) PinUpdateOption { return func(settings *PinUpdateSettings) error { settings.Unpin = unpin return nil From b7cfddfa735b41458abfeb32b165749aea3c6683 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 27 Feb 2018 21:03:55 +0100 Subject: [PATCH 2277/3817] Update to latest go-datastore License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-namesys@7d6df3815dcdb177408f441d01b5837de02b4d5f --- namesys/ipns_validate_test.go | 21 +++++++++------------ namesys/namesys.go | 2 +- namesys/namesys_test.go | 6 +++--- namesys/publisher.go | 6 +++--- namesys/publisher_test.go | 8 ++++---- namesys/pubsub.go | 15 ++++++--------- namesys/pubsub_test.go | 4 ++-- namesys/republisher/repub.go | 6 +++--- namesys/resolve_test.go | 6 +++--- namesys/validator.go | 18 +++++++----------- 10 files changed, 41 insertions(+), 51 deletions(-) diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index 9e72b9fe5..c62d5e2f5 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -10,21 +10,21 @@ import ( path "github.com/ipfs/go-ipfs/path" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + mockrouting "gx/ipfs/QmT51m6og9tmYo8FdaYin3zk1R7vA6ek5WYoHYEiMorfon/go-ipfs-routing/mock" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" - record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" - recordpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - mockrouting "gx/ipfs/QmZRcGYvxdauCd7hHnMYLYqcZRaDjv24c7eUNyJojAcdBb/go-ipfs-routing/mock" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + record "gx/ipfs/QmcBSi3Zxa6ytDQxig2iMv4VMfiKKy7v4tibi1Sq6Z5u2x/go-libp2p-record" + recordpb "gx/ipfs/QmcBSi3Zxa6ytDQxig2iMv4VMfiKKy7v4tibi1Sq6Z5u2x/go-libp2p-record/pb" ) func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, ns string, key string, val []byte, eol time.Time, exp error) { - validChecker := NewIpnsRecordValidator(kbook) + validFunc := NewIpnsRecordValidator(kbook) p := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") entry, err := CreateRoutingEntryData(priv, p, 1, eol) @@ -45,7 +45,7 @@ func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, ns s Value: data, } - err = validChecker.Func(rec) + err = validFunc(rec) if err != exp { params := fmt.Sprintf("namespace: %s\nkey: %s\neol: %s\n", ns, key, eol) if exp == nil { @@ -86,11 +86,8 @@ func TestResolverValidation(t *testing.T) { vstore := newMockValueStore(rid, dstore, peerstore) vstore.Validator["ipns"] = NewIpnsRecordValidator(peerstore) - vstore.Validator["pk"] = &record.ValidChecker{ - Func: func(r *record.ValidationRecord) error { - return nil - }, - Sign: false, + vstore.Validator["pk"] = func(r *record.ValidationRecord) error { + return nil } resolver := NewRoutingResolver(vstore, 0) diff --git a/namesys/namesys.go b/namesys/namesys.go index e47d433a3..9b4cdf3f7 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -11,9 +11,9 @@ import ( path "github.com/ipfs/go-ipfs/path" p2phost "gx/ipfs/QmNmJZL7FQySMtE2BQuLMuZg2EB2CLEunJJUSVSc9YnnbV/go-libp2p-host" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" floodsub "gx/ipfs/QmSFihvoND3eDaAYRCeLgLPt62yCPgMZs1NSZmKFEtJQQw/go-libp2p-floodsub" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 7cc4b780c..06d9e1e6d 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,9 +10,9 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/unixfs" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - offroute "gx/ipfs/QmZRcGYvxdauCd7hHnMYLYqcZRaDjv24c7eUNyJojAcdBb/go-ipfs-routing/offline" + offroute "gx/ipfs/QmT51m6og9tmYo8FdaYin3zk1R7vA6ek5WYoHYEiMorfon/go-ipfs-routing/offline" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 12c10f752..83b7a2300 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -12,13 +12,13 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" - dhtpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" + dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" + dhtpb "gx/ipfs/QmcBSi3Zxa6ytDQxig2iMv4VMfiKKy7v4tibi1Sq6Z5u2x/go-libp2p-record/pb" ) const PublishPutValTimeout = time.Minute diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index e7d2dd686..c2670f985 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -8,14 +8,14 @@ import ( path "github.com/ipfs/go-ipfs/path" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + mockrouting "gx/ipfs/QmT51m6og9tmYo8FdaYin3zk1R7vA6ek5WYoHYEiMorfon/go-ipfs-routing/mock" + dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" - mockrouting "gx/ipfs/QmZRcGYvxdauCd7hHnMYLYqcZRaDjv24c7eUNyJojAcdBb/go-ipfs-routing/mock" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" ) type identity struct { diff --git a/namesys/pubsub.go b/namesys/pubsub.go index abbf8f0cc..5859fd2c6 100644 --- a/namesys/pubsub.go +++ b/namesys/pubsub.go @@ -14,19 +14,19 @@ import ( u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" p2phost "gx/ipfs/QmNmJZL7FQySMtE2BQuLMuZg2EB2CLEunJJUSVSc9YnnbV/go-libp2p-host" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" floodsub "gx/ipfs/QmSFihvoND3eDaAYRCeLgLPt62yCPgMZs1NSZmKFEtJQQw/go-libp2p-floodsub" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" - record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" - dhtpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" + dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + record "gx/ipfs/QmcBSi3Zxa6ytDQxig2iMv4VMfiKKy7v4tibi1Sq6Z5u2x/go-libp2p-record" + dhtpb "gx/ipfs/QmcBSi3Zxa6ytDQxig2iMv4VMfiKKy7v4tibi1Sq6Z5u2x/go-libp2p-record/pb" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" ) // PubsubPublisher is a publisher that distributes IPNS records through pubsub @@ -151,10 +151,7 @@ func (p *PubsubPublisher) publishRecord(ctx context.Context, k ci.PrivKey, value // the datastore is shared with the routing publisher to properly increment and persist // ipns record sequence numbers; so we need to Record our new entry in the datastore - dsrec, err := record.MakePutRecord(k, ipnskey, data, true) - if err != nil { - return err - } + dsrec := record.MakePutRecord(ipnskey, data) dsdata, err := proto.Marshal(dsrec) if err != nil { diff --git a/namesys/pubsub_test.go b/namesys/pubsub_test.go index e2cf15b5d..b9cd40842 100644 --- a/namesys/pubsub_test.go +++ b/namesys/pubsub_test.go @@ -9,14 +9,14 @@ import ( path "github.com/ipfs/go-ipfs/path" p2phost "gx/ipfs/QmNmJZL7FQySMtE2BQuLMuZg2EB2CLEunJJUSVSc9YnnbV/go-libp2p-host" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" bhost "gx/ipfs/QmQr1j6UvdhpponAaqSdswqRpdzsFwNop2N8kXLNw8afem/go-libp2p-blankhost" floodsub "gx/ipfs/QmSFihvoND3eDaAYRCeLgLPt62yCPgMZs1NSZmKFEtJQQw/go-libp2p-floodsub" + mockrouting "gx/ipfs/QmT51m6og9tmYo8FdaYin3zk1R7vA6ek5WYoHYEiMorfon/go-ipfs-routing/mock" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" netutil "gx/ipfs/QmYVR3C8DWPHdHxvLtNFYfjsXgaRAdh6hPMNH3KiwCgu4o/go-libp2p-netutil" - mockrouting "gx/ipfs/QmZRcGYvxdauCd7hHnMYLYqcZRaDjv24c7eUNyJojAcdBb/go-ipfs-routing/mock" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index ae8c5e8d1..33be2349c 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -10,16 +10,16 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" - recpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" + dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ic "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" + recpb "gx/ipfs/QmcBSi3Zxa6ytDQxig2iMv4VMfiKKy7v4tibi1Sq6Z5u2x/go-libp2p-record/pb" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index a55d5a4d4..6efb9d6a9 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,10 +8,10 @@ import ( path "github.com/ipfs/go-ipfs/path" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" + mockrouting "gx/ipfs/QmT51m6og9tmYo8FdaYin3zk1R7vA6ek5WYoHYEiMorfon/go-ipfs-routing/mock" testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" - mockrouting "gx/ipfs/QmZRcGYvxdauCd7hHnMYLYqcZRaDjv24c7eUNyJojAcdBb/go-ipfs-routing/mock" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ) diff --git a/namesys/validator.go b/namesys/validator.go index c50da5512..3c6cc8bb6 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -4,13 +4,14 @@ import ( "errors" "time" - pb "github.com/ipfs/go-ipfs/namesys/pb" pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" + pb "github.com/ipfs/go-ipfs/namesys/pb" + u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + record "gx/ipfs/QmcBSi3Zxa6ytDQxig2iMv4VMfiKKy7v4tibi1Sq6Z5u2x/go-libp2p-record" ) // ErrExpiredRecord should be returned when an ipns record is @@ -41,16 +42,16 @@ var ErrKeyFormat = errors.New("record key could not be parsed into peer ID") // from the peer store var ErrPublicKeyNotFound = errors.New("public key not found in peer store") -// NewIpnsRecordValidator returns a ValidChecker for IPNS records. +// NewIpnsRecordValidator returns a record.ValidatorFunc for IPNS records. // The validator function will get a public key from the KeyBook // to verify the record's signature. Note that the public key must // already have been fetched from the network and put into the KeyBook // by the caller. -func NewIpnsRecordValidator(kbook pstore.KeyBook) *record.ValidChecker { - // ValidateIpnsRecord implements ValidatorFunc and verifies that the +func NewIpnsRecordValidator(kbook pstore.KeyBook) record.ValidatorFunc { + // This provides a ValidatorFunc which verifies that the // given record's value is an IpnsEntry, that the entry has been correctly // signed, and that the entry has not expired - ValidateIpnsRecord := func(r *record.ValidationRecord) error { + return func(r *record.ValidationRecord) error { if r.Namespace != "ipns" { return ErrInvalidPath } @@ -96,9 +97,4 @@ func NewIpnsRecordValidator(kbook pstore.KeyBook) *record.ValidChecker { } return nil } - - return &record.ValidChecker{ - Func: ValidateIpnsRecord, - Sign: false, - } } From 3e0de5076a3025f886fed4a4543478b676fa2f80 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 27 Feb 2018 21:03:55 +0100 Subject: [PATCH 2278/3817] Update to latest go-datastore License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-blockservice@b4a5068ac65297b6b3b68238a817b55b5b4e5131 --- blockservice/blockservice.go | 2 +- blockservice/blockservice_test.go | 6 +++--- blockservice/test/blocks_test.go | 6 +++--- blockservice/test/mock.go | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index f77e248d8..a54c0ff2b 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -13,7 +13,7 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/verifcid" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" - blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index 6407949c7..edcd4cd98 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -6,9 +6,9 @@ import ( butil "github.com/ipfs/go-ipfs/blocks/blocksutil" offline "github.com/ipfs/go-ipfs/exchange/offline" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" + blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 44d4d3ca0..2a56afeb7 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -11,9 +11,9 @@ import ( offline "github.com/ipfs/go-ipfs/exchange/offline" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" + blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index 06d8e65ad..a70eb06e0 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -6,7 +6,7 @@ import ( tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" - mockrouting "gx/ipfs/QmZRcGYvxdauCd7hHnMYLYqcZRaDjv24c7eUNyJojAcdBb/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmT51m6og9tmYo8FdaYin3zk1R7vA6ek5WYoHYEiMorfon/go-ipfs-routing/mock" ) // Mocks returns |n| connected mock Blockservices From 60299ef76c2237aa04e08ee4bf0b0486cc5817cb Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 27 Feb 2018 21:03:55 +0100 Subject: [PATCH 2279/3817] Update to latest go-datastore License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@5044152b35495822ee88a473b74fb972f3860894 --- ipld/merkledag/test/utils.go | 6 +++--- ipld/merkledag/utils/utils.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index 479f8c27c..7de0b841e 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -5,9 +5,9 @@ import ( "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" + blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 1e7c88325..d6ac9726a 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -9,9 +9,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - syncds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - bstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + syncds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" + bstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) From 0bf74fa13bdb7401bc3579e78f525f8df40fd3e5 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 27 Feb 2018 21:03:55 +0100 Subject: [PATCH 2280/3817] Update to latest go-datastore License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-mfs@107a720cc3a7ade83dd5c08acfaaf6ec7a1e5bfd --- mfs/mfs_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 25e61854b..ce8208293 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -23,10 +23,10 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - bstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" chunker "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" + bstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) From 54d168ac8d0563071aa7426e0281c62fb6171460 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 2 Mar 2018 15:01:12 +0100 Subject: [PATCH 2281/3817] Revert go-libp2p-kad-dht and related changes to a working version This uses a working libp2p-kad-dht and libp2p-record libraries, reverts the changes that were introduced to support the newer versions License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-namesys@4028e8123474c310ec3c13d9050fad1665c86d82 --- namesys/ipns_validate_test.go | 17 ++++++++++------- namesys/namesys_test.go | 2 +- namesys/publisher.go | 2 +- namesys/publisher_test.go | 2 +- namesys/pubsub.go | 9 ++++++--- namesys/pubsub_test.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/resolve_test.go | 2 +- namesys/validator.go | 18 +++++++++++------- 9 files changed, 33 insertions(+), 23 deletions(-) diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index c62d5e2f5..ac3fb8470 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -10,21 +10,21 @@ import ( path "github.com/ipfs/go-ipfs/path" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - mockrouting "gx/ipfs/QmT51m6og9tmYo8FdaYin3zk1R7vA6ek5WYoHYEiMorfon/go-ipfs-routing/mock" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" + record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" + recordpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" + mockrouting "gx/ipfs/QmXtoXbu9ReyV6Q4kDQ5CF9wXQNDY1PdHc4HhfxRR5AHB3/go-ipfs-routing/mock" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - record "gx/ipfs/QmcBSi3Zxa6ytDQxig2iMv4VMfiKKy7v4tibi1Sq6Z5u2x/go-libp2p-record" - recordpb "gx/ipfs/QmcBSi3Zxa6ytDQxig2iMv4VMfiKKy7v4tibi1Sq6Z5u2x/go-libp2p-record/pb" ) func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, ns string, key string, val []byte, eol time.Time, exp error) { - validFunc := NewIpnsRecordValidator(kbook) + validChecker := NewIpnsRecordValidator(kbook) p := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") entry, err := CreateRoutingEntryData(priv, p, 1, eol) @@ -45,7 +45,7 @@ func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, ns s Value: data, } - err = validFunc(rec) + err = validChecker.Func(rec) if err != exp { params := fmt.Sprintf("namespace: %s\nkey: %s\neol: %s\n", ns, key, eol) if exp == nil { @@ -86,8 +86,11 @@ func TestResolverValidation(t *testing.T) { vstore := newMockValueStore(rid, dstore, peerstore) vstore.Validator["ipns"] = NewIpnsRecordValidator(peerstore) - vstore.Validator["pk"] = func(r *record.ValidationRecord) error { - return nil + vstore.Validator["pk"] = &record.ValidChecker{ + Func: func(r *record.ValidationRecord) error { + return nil + }, + Sign: false, } resolver := NewRoutingResolver(vstore, 0) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 06d9e1e6d..e0d9ecf08 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,9 +10,9 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/unixfs" - offroute "gx/ipfs/QmT51m6og9tmYo8FdaYin3zk1R7vA6ek5WYoHYEiMorfon/go-ipfs-routing/offline" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" + offroute "gx/ipfs/QmXtoXbu9ReyV6Q4kDQ5CF9wXQNDY1PdHc4HhfxRR5AHB3/go-ipfs-routing/offline" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 83b7a2300..b485b24b2 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -14,11 +14,11 @@ import ( u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" + dhtpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - dhtpb "gx/ipfs/QmcBSi3Zxa6ytDQxig2iMv4VMfiKKy7v4tibi1Sq6Z5u2x/go-libp2p-record/pb" ) const PublishPutValTimeout = time.Minute diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index c2670f985..20f774fa3 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -8,12 +8,12 @@ import ( path "github.com/ipfs/go-ipfs/path" - mockrouting "gx/ipfs/QmT51m6og9tmYo8FdaYin3zk1R7vA6ek5WYoHYEiMorfon/go-ipfs-routing/mock" dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" + mockrouting "gx/ipfs/QmXtoXbu9ReyV6Q4kDQ5CF9wXQNDY1PdHc4HhfxRR5AHB3/go-ipfs-routing/mock" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) diff --git a/namesys/pubsub.go b/namesys/pubsub.go index 5859fd2c6..8f8722721 100644 --- a/namesys/pubsub.go +++ b/namesys/pubsub.go @@ -17,6 +17,8 @@ import ( floodsub "gx/ipfs/QmSFihvoND3eDaAYRCeLgLPt62yCPgMZs1NSZmKFEtJQQw/go-libp2p-floodsub" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" + record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" + dhtpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" @@ -24,8 +26,6 @@ import ( peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - record "gx/ipfs/QmcBSi3Zxa6ytDQxig2iMv4VMfiKKy7v4tibi1Sq6Z5u2x/go-libp2p-record" - dhtpb "gx/ipfs/QmcBSi3Zxa6ytDQxig2iMv4VMfiKKy7v4tibi1Sq6Z5u2x/go-libp2p-record/pb" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) @@ -151,7 +151,10 @@ func (p *PubsubPublisher) publishRecord(ctx context.Context, k ci.PrivKey, value // the datastore is shared with the routing publisher to properly increment and persist // ipns record sequence numbers; so we need to Record our new entry in the datastore - dsrec := record.MakePutRecord(ipnskey, data) + dsrec, err := record.MakePutRecord(k, ipnskey, data, true) + if err != nil { + return err + } dsdata, err := proto.Marshal(dsrec) if err != nil { diff --git a/namesys/pubsub_test.go b/namesys/pubsub_test.go index b9cd40842..16dae39fc 100644 --- a/namesys/pubsub_test.go +++ b/namesys/pubsub_test.go @@ -11,11 +11,11 @@ import ( p2phost "gx/ipfs/QmNmJZL7FQySMtE2BQuLMuZg2EB2CLEunJJUSVSc9YnnbV/go-libp2p-host" bhost "gx/ipfs/QmQr1j6UvdhpponAaqSdswqRpdzsFwNop2N8kXLNw8afem/go-libp2p-blankhost" floodsub "gx/ipfs/QmSFihvoND3eDaAYRCeLgLPt62yCPgMZs1NSZmKFEtJQQw/go-libp2p-floodsub" - mockrouting "gx/ipfs/QmT51m6og9tmYo8FdaYin3zk1R7vA6ek5WYoHYEiMorfon/go-ipfs-routing/mock" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" + mockrouting "gx/ipfs/QmXtoXbu9ReyV6Q4kDQ5CF9wXQNDY1PdHc4HhfxRR5AHB3/go-ipfs-routing/mock" netutil "gx/ipfs/QmYVR3C8DWPHdHxvLtNFYfjsXgaRAdh6hPMNH3KiwCgu4o/go-libp2p-netutil" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 33be2349c..0aabf3738 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -15,11 +15,11 @@ import ( gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" + recpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ic "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" - recpb "gx/ipfs/QmcBSi3Zxa6ytDQxig2iMv4VMfiKKy7v4tibi1Sq6Z5u2x/go-libp2p-record/pb" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 6efb9d6a9..31bfd4956 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,10 +8,10 @@ import ( path "github.com/ipfs/go-ipfs/path" - mockrouting "gx/ipfs/QmT51m6og9tmYo8FdaYin3zk1R7vA6ek5WYoHYEiMorfon/go-ipfs-routing/mock" testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" + mockrouting "gx/ipfs/QmXtoXbu9ReyV6Q4kDQ5CF9wXQNDY1PdHc4HhfxRR5AHB3/go-ipfs-routing/mock" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ) diff --git a/namesys/validator.go b/namesys/validator.go index 3c6cc8bb6..c50da5512 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -4,14 +4,13 @@ import ( "errors" "time" + pb "github.com/ipfs/go-ipfs/namesys/pb" pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" - pb "github.com/ipfs/go-ipfs/namesys/pb" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - record "gx/ipfs/QmcBSi3Zxa6ytDQxig2iMv4VMfiKKy7v4tibi1Sq6Z5u2x/go-libp2p-record" ) // ErrExpiredRecord should be returned when an ipns record is @@ -42,16 +41,16 @@ var ErrKeyFormat = errors.New("record key could not be parsed into peer ID") // from the peer store var ErrPublicKeyNotFound = errors.New("public key not found in peer store") -// NewIpnsRecordValidator returns a record.ValidatorFunc for IPNS records. +// NewIpnsRecordValidator returns a ValidChecker for IPNS records. // The validator function will get a public key from the KeyBook // to verify the record's signature. Note that the public key must // already have been fetched from the network and put into the KeyBook // by the caller. -func NewIpnsRecordValidator(kbook pstore.KeyBook) record.ValidatorFunc { - // This provides a ValidatorFunc which verifies that the +func NewIpnsRecordValidator(kbook pstore.KeyBook) *record.ValidChecker { + // ValidateIpnsRecord implements ValidatorFunc and verifies that the // given record's value is an IpnsEntry, that the entry has been correctly // signed, and that the entry has not expired - return func(r *record.ValidationRecord) error { + ValidateIpnsRecord := func(r *record.ValidationRecord) error { if r.Namespace != "ipns" { return ErrInvalidPath } @@ -97,4 +96,9 @@ func NewIpnsRecordValidator(kbook pstore.KeyBook) record.ValidatorFunc { } return nil } + + return &record.ValidChecker{ + Func: ValidateIpnsRecord, + Sign: false, + } } From 70cb8f60050a1c3e5af9de3d2cd9e566940a59ec Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 27 Feb 2018 21:03:55 +0100 Subject: [PATCH 2282/3817] Update to latest go-datastore License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-pinner@79bfbbf6bcf9fe56a10db95dde9d886c5aaba977 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 6 +++--- pinning/pinner/set_test.go | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 6c3f438c6..8769db8e1 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -13,9 +13,9 @@ import ( pin "github.com/ipfs/go-ipfs/pin" "github.com/ipfs/go-ipfs/thirdparty/verifcid" - dstore "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" - bstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + dstore "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + bstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 685f8cf65..08eed36fe 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -12,8 +12,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" dutils "github.com/ipfs/go-ipfs/merkledag/utils" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 874689dd8..94da70ed3 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -10,9 +10,9 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" util "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dssync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" + blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index e05806788..815322796 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -9,9 +9,9 @@ import ( offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" - blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + dsq "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/query" + blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) From e57196a2fb978b47fb7bf4a25474e5755eaeb8e7 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 27 Feb 2018 21:03:55 +0100 Subject: [PATCH 2283/3817] Update to latest go-datastore License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-exchange-offline@87106931300b0625c10c9b6aa822538e39736dd3 --- exchange/offline/offline.go | 2 +- exchange/offline/offline_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 4ed7d7dc3..5b4ad1685 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -7,7 +7,7 @@ import ( exchange "github.com/ipfs/go-ipfs/exchange" - blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 6535cd639..19f2d77ae 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -7,9 +7,9 @@ import ( "github.com/ipfs/go-ipfs/blocks/blocksutil" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - ds_sync "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/sync" - blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + ds_sync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" + blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) From f6e4038ce1469682aab35c55e7e13f8e0811df97 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 2 Mar 2018 15:01:12 +0100 Subject: [PATCH 2284/3817] Revert go-libp2p-kad-dht and related changes to a working version This uses a working libp2p-kad-dht and libp2p-record libraries, reverts the changes that were introduced to support the newer versions License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-blockservice@94b32079f49123c571ffeaea6e1571a3d42e0c3d --- blockservice/test/mock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index a70eb06e0..a9c34d42d 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -6,7 +6,7 @@ import ( tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" - mockrouting "gx/ipfs/QmT51m6og9tmYo8FdaYin3zk1R7vA6ek5WYoHYEiMorfon/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmXtoXbu9ReyV6Q4kDQ5CF9wXQNDY1PdHc4HhfxRR5AHB3/go-ipfs-routing/mock" ) // Mocks returns |n| connected mock Blockservices From d3f84d5479a223839d71d41dcb92a2851fde38d5 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 27 Feb 2018 21:03:55 +0100 Subject: [PATCH 2285/3817] Update to latest go-datastore License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-filestore@f11275a4f607c72f569e876e51d2e8a173326562 --- filestore/filestore.go | 4 ++-- filestore/filestore_test.go | 4 ++-- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index f781d5262..87819e831 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -10,9 +10,9 @@ package filestore import ( "context" - dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" - blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + dsq "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/query" + blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 6b4065471..4bb8bfa04 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -9,8 +9,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 9e8ff73e4..dbdb8396f 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -9,14 +9,14 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dsns "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/namespace" - dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" - blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + dsns "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/namespace" + dsq "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/query" + blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) diff --git a/filestore/util.go b/filestore/util.go index f923ec563..ce03ba927 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,11 +6,11 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - ds "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore" - dsq "gx/ipfs/QmPpegoMqhAEqjncrzArm7KVWAkCm78rqL2DPuNjhPrshg/go-datastore/query" - blockstore "gx/ipfs/QmTVDM4LCSUMFNQzbDLL9zQwp8usE6QHymFdh3h8vL9v6b/go-ipfs-blockstore" + dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" + ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + dsq "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/query" + blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - dshelp "gx/ipfs/QmdQTPWduSeyveSxeCAte33M592isSW5Z979g81aJphrgn/go-ipfs-ds-help" ) // Status is used to identify the state of the block data referenced From b1ea2816ebb03b974154fa4d8138c11452dc5de5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 27 Mar 2018 15:01:32 -0700 Subject: [PATCH 2286/3817] don't resolve children unnecessarily when listing a sharded directory We only need to get the child if it's a shard. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@cb89bd3d4e37b565560cd329a05024818504e73e --- unixfs/hamt/hamt.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 70c0b371c..72a008066 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -289,13 +289,13 @@ func (ds *Shard) loadChild(ctx context.Context, i int) (child, error) { return nil, fmt.Errorf("invalid link name '%s'", lnk.Name) } - nd, err := lnk.GetNode(ctx, ds.dserv) - if err != nil { - return nil, err - } - var c child if len(lnk.Name) == ds.maxpadlen { + nd, err := lnk.GetNode(ctx, ds.dserv) + if err != nil { + return nil, err + } + pbnd, ok := nd.(*dag.ProtoNode) if !ok { return nil, dag.ErrNotProtobuf From c7ba396ace9c791a143f7f78f1d4a3c90d80065b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 27 Mar 2018 16:48:59 -0700 Subject: [PATCH 2287/3817] only visit nodes in EnumerateChildrenAsync when asked No idea why this was changed this was introduced in: 08f342e8bada4f4eb0c5462a3623f0c2b828240f (part of #3598) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@016ac37d96e8a312dd6e710cfd378f725e8911f9 --- ipld/merkledag/merkledag.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 197785d39..c7a6296da 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -319,17 +319,17 @@ func EnumerateChildrenAsync(ctx context.Context, getLinks GetLinks, c *cid.Cid, for i := 0; i < FetchGraphConcurrency; i++ { go func() { for ic := range feed { - links, err := getLinks(ctx, ic) - if err != nil { - errChan <- err - return - } - setlk.Lock() - unseen := visit(ic) + shouldVisit := visit(ic) setlk.Unlock() - if unseen { + if shouldVisit { + links, err := getLinks(ctx, ic) + if err != nil { + errChan <- err + return + } + select { case out <- links: case <-fetchersCtx.Done(): From b8a93cb6878c6fde99d72fac5eafb8ecb5a7b8a2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 28 Mar 2018 13:41:59 -0700 Subject: [PATCH 2288/3817] dedup keys in GetMany Otherwise, GetMany on the children of a node with duplicate links may fail License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@17f4c175c1f0e6c8502b2585d2659e57a6ab0aef --- ipld/merkledag/merkledag.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 197785d39..2343d7ab1 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -201,7 +201,20 @@ func (n *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *ipld. return getNodesFromBG(ctx, n.Blocks, keys) } +func dedupKeys(keys []*cid.Cid) []*cid.Cid { + set := cid.NewSet() + for _, c := range keys { + set.Add(c) + } + if set.Len() == len(keys) { + return keys + } + return set.Keys() +} + func getNodesFromBG(ctx context.Context, bs bserv.BlockGetter, keys []*cid.Cid) <-chan *ipld.NodeOption { + keys = dedupKeys(keys) + out := make(chan *ipld.NodeOption, len(keys)) blocks := bs.GetBlocks(ctx, keys) var count int From 65efb91ecbf5aa841e4c086dec9f564f2a771637 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 28 Mar 2018 14:11:51 -0700 Subject: [PATCH 2289/3817] test duplicate CIDs in getMany License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@7d5c56b1c65150ee4306c3c297d25031edfe98c6 --- ipld/merkledag/merkledag_test.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index d67a20aa7..48fe8e8a9 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -550,6 +550,36 @@ func TestCidRawDoesnNeedData(t *testing.T) { } } +func TestGetManyDuplicate(t *testing.T) { + ctx := context.Background() + + srv := NewDAGService(dstest.Bserv()) + + nd := NodeWithData([]byte("foo")) + if err := srv.Add(ctx, nd); err != nil { + t.Fatal(err) + } + nds := srv.GetMany(ctx, []*cid.Cid{nd.Cid(), nd.Cid(), nd.Cid()}) + out, ok := <-nds + if !ok { + t.Fatal("expecting node foo") + } + if out.Err != nil { + t.Fatal(out.Err) + } + if !out.Node.Cid().Equals(nd.Cid()) { + t.Fatal("got wrong node") + } + out, ok = <-nds + if ok { + if out.Err != nil { + t.Fatal(out.Err) + } else { + t.Fatal("expecting no more nodes") + } + } +} + func TestEnumerateAsyncFailsNotFound(t *testing.T) { ctx := context.Background() From 01b7c69b2e48ed906bb9e179980537b9d0426d41 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 28 Mar 2018 15:11:49 -0700 Subject: [PATCH 2290/3817] fix hamt node not protobuf error License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@4632d49fedcbabf36144c41491342bcc0771aee2 --- unixfs/hamt/hamt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 72a008066..77ce9ceff 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -100,7 +100,7 @@ func makeShard(ds ipld.DAGService, size int) (*Shard, error) { func NewHamtFromDag(dserv ipld.DAGService, nd ipld.Node) (*Shard, error) { pbnd, ok := nd.(*dag.ProtoNode) if !ok { - return nil, dag.ErrLinkNotFound + return nil, dag.ErrNotProtobuf } pbd, err := format.FromBytes(pbnd.Data()) From a882c108023b807ee011c1e6eed098603d12f6d4 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 28 Mar 2018 15:12:04 -0700 Subject: [PATCH 2291/3817] remove redundant validation logic License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@0d57297715fe828c46c92389567e1c3375027bd9 --- unixfs/hamt/hamt.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 77ce9ceff..09bbb24e4 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -295,21 +295,6 @@ func (ds *Shard) loadChild(ctx context.Context, i int) (child, error) { if err != nil { return nil, err } - - pbnd, ok := nd.(*dag.ProtoNode) - if !ok { - return nil, dag.ErrNotProtobuf - } - - pbd, err := format.FromBytes(pbnd.Data()) - if err != nil { - return nil, err - } - - if pbd.GetType() != format.THAMTShard { - return nil, fmt.Errorf("HAMT entries must have non-zero length name") - } - cds, err := NewHamtFromDag(ds.dserv, nd) if err != nil { return nil, err From 8ca1b20596e90a9cc29d634eb1c7867a71c62984 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 28 Mar 2018 17:41:28 -0700 Subject: [PATCH 2292/3817] faster hamt logic 1. Use a custom bitfield type instead of bigints. 2. Make iterating over a hamt *significantly* faster. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@5e52afed5b3c8f05cf1b783f37a6ec47f28a68bf --- unixfs/hamt/hamt.go | 55 ++++++++++++++-------------------------- unixfs/hamt/hamt_test.go | 2 +- unixfs/hamt/util.go | 15 ++++++----- unixfs/hamt/util_test.go | 13 ---------- 4 files changed, 29 insertions(+), 56 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 09bbb24e4..c83ec1cea 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -23,14 +23,13 @@ package hamt import ( "context" "fmt" - "math" - "math/big" "os" dag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" upb "github.com/ipfs/go-ipfs/unixfs/pb" + bitfield "gx/ipfs/QmTbBs3Y3u5F69XNJzdnnc6SP5GKgcXxCDzx6w8m6piVRT/go-bitfield" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" @@ -46,7 +45,7 @@ const ( type Shard struct { nd *dag.ProtoNode - bitfield *big.Int + bitfield bitfield.Bitfield children []child @@ -75,22 +74,22 @@ func NewShard(dserv ipld.DAGService, size int) (*Shard, error) { return nil, err } - ds.bitfield = big.NewInt(0) ds.nd = new(dag.ProtoNode) ds.hashFunc = HashMurmur3 return ds, nil } func makeShard(ds ipld.DAGService, size int) (*Shard, error) { - lg2s := int(math.Log2(float64(size))) - if 1< Date: Wed, 28 Mar 2018 17:53:42 -0700 Subject: [PATCH 2293/3817] add benchmark for hamt walking License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@06d3ab218bf5b8613722c23fcfcfc4b6f837d263 --- unixfs/hamt/hamt_test.go | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 224ff8c3a..9a0e172ac 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -523,6 +523,49 @@ func printDiff(ds ipld.DAGService, a, b *dag.ProtoNode) { } } +func BenchmarkHAMTWalk(b *testing.B) { + ctx := context.Background() + + ds := mdtest.Mock() + sh, _ := NewShard(ds, 256) + nd, err := sh.Node() + if err != nil { + b.Fatal(err) + } + + err = ds.Add(ctx, nd) + if err != nil { + b.Fatal(err) + } + ds.Add(ctx, ft.EmptyDirNode()) + + s, err := NewHamtFromDag(ds, nd) + if err != nil { + b.Fatal(err) + } + + for j := 0; j < 1000; j++ { + err = s.Set(ctx, fmt.Sprintf("%d", j), ft.EmptyDirNode()) + if err != nil { + b.Fatal(err) + } + } + + for i := 0; i < b.N; i++ { + cnt := 0 + err = s.ForEachLink(ctx, func(l *ipld.Link) error { + cnt++ + return nil + }) + if err != nil { + b.Fatal(err) + } + if cnt < 1000 { + b.Fatal("expected 100 children") + } + } +} + func BenchmarkHAMTSet(b *testing.B) { ctx := context.Background() From 19767fcca8c847c36dc6e47fe456f97ace925b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 18 Mar 2018 19:54:46 +0100 Subject: [PATCH 2294/3817] fix error style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-unixfs@283f5df37fccc3f653328bc8920d73d3be0f3847 --- unixfs/io/pbdagreader.go | 2 +- unixfs/test/utils.go | 4 ++-- unixfs/unixfs.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index ac08fade8..a194dccf4 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -238,7 +238,7 @@ func (dr *PBDagReader) Seek(offset int64, whence int) (int64, error) { switch whence { case io.SeekStart: if offset < 0 { - return -1, errors.New("Invalid offset") + return -1, errors.New("invalid offset") } if offset == dr.offset { return offset, nil diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index f96fcfcb5..574b7ec97 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -95,11 +95,11 @@ func GetRandomNode(t testing.TB, dserv ipld.DAGService, size int64, opts NodeOpt // ArrComp checks if two byte slices are the same. func ArrComp(a, b []byte) error { if len(a) != len(b) { - return fmt.Errorf("Arrays differ in length. %d != %d", len(a), len(b)) + return fmt.Errorf("arrays differ in length. %d != %d", len(a), len(b)) } for i, v := range a { if v != b[i] { - return fmt.Errorf("Arrays differ at index: %d", i) + return fmt.Errorf("arrays differ at index: %d", i) } } return nil diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index d04a461ed..654de7ff8 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -129,13 +129,13 @@ func DataSize(data []byte) (uint64, error) { switch pbdata.GetType() { case pb.Data_Directory: - return 0, errors.New("Cant get data size of directory") + return 0, errors.New("can't get data size of directory") case pb.Data_File: return pbdata.GetFilesize(), nil case pb.Data_Raw: return uint64(len(pbdata.GetData())), nil default: - return 0, errors.New("Unrecognized node data type") + return 0, errors.New("unrecognized node data type") } } From fbe09cb689cbeddedf2b3522124a3e918fb95691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 18 Mar 2018 19:54:46 +0100 Subject: [PATCH 2295/3817] fix error style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-merkledag@823a0716b06334ed5eb08f6b137da87137ff6e72 --- ipld/merkledag/coding.go | 10 +++++----- ipld/merkledag/merkledag.go | 2 +- ipld/merkledag/merkledag_test.go | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 62b8353b2..f76631b2e 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -21,7 +21,7 @@ import ( func (n *ProtoNode) unmarshal(encoded []byte) error { var pbn pb.PBNode if err := pbn.Unmarshal(encoded); err != nil { - return fmt.Errorf("Unmarshal failed. %v", err) + return fmt.Errorf("unmarshal failed. %v", err) } pbnl := pbn.GetLinks() @@ -30,7 +30,7 @@ func (n *ProtoNode) unmarshal(encoded []byte) error { n.links[i] = &ipld.Link{Name: l.GetName(), Size: l.GetTsize()} c, err := cid.Cast(l.GetHash()) if err != nil { - return fmt.Errorf("Link hash #%d is not valid multihash. %v", i, err) + return fmt.Errorf("link hash #%d is not valid multihash. %v", i, err) } n.links[i].Cid = c } @@ -47,7 +47,7 @@ func (n *ProtoNode) Marshal() ([]byte, error) { pbn := n.getPBNode() data, err := pbn.Marshal() if err != nil { - return data, fmt.Errorf("Marshal failed. %v", err) + return data, fmt.Errorf("marshal failed. %v", err) } return data, nil } @@ -123,9 +123,9 @@ func DecodeProtobufBlock(b blocks.Block) (ipld.Node, error) { decnd, err := DecodeProtobuf(b.RawData()) if err != nil { if strings.Contains(err.Error(), "Unmarshal failed") { - return nil, fmt.Errorf("The block referred to by '%s' was not a valid merkledag node", c) + return nil, fmt.Errorf("the block referred to by '%s' was not a valid merkledag node", c) } - return nil, fmt.Errorf("Failed to decode Protocol Buffers: %v", err) + return nil, fmt.Errorf("failed to decode Protocol Buffers: %v", err) } decnd.cached = c diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index c7a6296da..915997274 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -74,7 +74,7 @@ func (n *dagService) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { if err == bserv.ErrNotFound { return nil, ipld.ErrNotFound } - return nil, fmt.Errorf("Failed to get block for %s: %v", c, err) + return nil, fmt.Errorf("failed to get block for %s: %v", c, err) } return ipld.Decode(b) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index d67a20aa7..1591e94f1 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -239,7 +239,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { } if !bytes.Equal(datagot, expected) { - errs <- errors.New("Got bad data back!") + errs <- errors.New("got bad data back") } }(i) } From 6c0f7f09f6bff811122abd0ad5c5ce1dcb058fb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 18 Mar 2018 19:54:46 +0100 Subject: [PATCH 2296/3817] fix error style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-pinner@ced44557acfcec2e3cb10ca8a3989b6591f2be0c --- pinning/pinner/pin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 08eed36fe..8b6eb0aef 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -305,7 +305,7 @@ func (p *pinner) isPinnedWithType(c *cid.Cid, mode Mode) (string, bool, error) { switch mode { case Any, Direct, Indirect, Recursive, Internal: default: - err := fmt.Errorf("Invalid Pin Mode '%d', must be one of {%d, %d, %d, %d, %d}", + err := fmt.Errorf("invalid Pin Mode '%d', must be one of {%d, %d, %d, %d, %d}", mode, Direct, Indirect, Recursive, Internal, Any) return "", false, err } From 03506fcd068272c49a13ffba62504a2906882c55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 18 Mar 2018 19:54:46 +0100 Subject: [PATCH 2297/3817] fix error style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-mfs@9df1d72924be4765a8e87d6e40e59847a06aab03 --- mfs/mfs_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index ce8208293..6255c90c4 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -108,7 +108,7 @@ func assertDirAtPath(root *Directory, pth string, children []string) error { sort.Strings(children) sort.Strings(names) if !compStrArrs(children, names) { - return errors.New("directories children did not match!") + return errors.New("directories children did not match") } return nil @@ -158,7 +158,7 @@ func assertFileAtPath(ds ipld.DAGService, root *Directory, expn ipld.Node, pth s file, ok := finaln.(*File) if !ok { - return fmt.Errorf("%s was not a file!", pth) + return fmt.Errorf("%s was not a file", pth) } rfd, err := file.Open(OpenReadOnly, false) @@ -177,7 +177,7 @@ func assertFileAtPath(ds ipld.DAGService, root *Directory, expn ipld.Node, pth s } if !bytes.Equal(out, expbytes) { - return fmt.Errorf("Incorrect data at path!") + return fmt.Errorf("incorrect data at path") } return nil } @@ -616,7 +616,7 @@ func randomFile(d *Directory) (*File, error) { fi, ok := fsn.(*File) if !ok { - return nil, errors.New("file wasnt a file, race?") + return nil, errors.New("file wasn't a file, race?") } return fi, nil @@ -889,7 +889,7 @@ func readFile(rt *Root, path string, offset int64, buf []byte) error { return err } if nread != len(buf) { - return fmt.Errorf("didnt read enough!") + return fmt.Errorf("didn't read enough") } return fd.Close() From 31bdf01fd8aa911df28a1ef3b2f7578251114b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 18 Mar 2018 19:54:46 +0100 Subject: [PATCH 2298/3817] fix error style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@2ba277657c1ff7fc7eb4b14d183748dcfcddf704 --- namesys/dns_test.go | 2 +- namesys/interface.go | 7 ++++--- namesys/namesys_test.go | 2 +- namesys/pubsub.go | 2 +- namesys/resolve_test.go | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 1a3110c9b..2a58124ed 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -14,7 +14,7 @@ type mockDNS struct { func (m *mockDNS) lookupTXT(name string) (txt []string, err error) { txt, ok := m.entries[name] if !ok { - return nil, fmt.Errorf("No TXT entry for %s", name) + return nil, fmt.Errorf("no TXT entry for %s", name) } return txt, nil } diff --git a/namesys/interface.go b/namesys/interface.go index db2aa0a22..4def9b1d7 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -37,18 +37,19 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" + ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" ) // ErrResolveFailed signals an error when attempting to resolve. -var ErrResolveFailed = errors.New("Could not resolve name.") +var ErrResolveFailed = errors.New("could not resolve name") // ErrResolveRecursion signals a recursion-depth limit. var ErrResolveRecursion = errors.New( - "Could not resolve name (recursion limit exceeded).") + "could not resolve name (recursion limit exceeded)") // ErrPublishFailed signals an error when attempting to publish. -var ErrPublishFailed = errors.New("Could not publish name.") +var ErrPublishFailed = errors.New("could not publish name") // Namesys represents a cohesive name publishing and resolving system. // diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index e0d9ecf08..9b27cf322 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -24,7 +24,7 @@ func testResolution(t *testing.T, resolver Resolver, name string, depth uint, ex p, err := resolver.Resolve(context.Background(), name, opts.Depth(depth)) if err != expError { t.Fatal(fmt.Errorf( - "Expected %s with a depth of %d to have a '%s' error, but got '%s'", + "expected %s with a depth of %d to have a '%s' error, but got '%s'", name, depth, expError, err)) } if p.String() != expected { diff --git a/namesys/pubsub.go b/namesys/pubsub.go index 8f8722721..4173fb6e8 100644 --- a/namesys/pubsub.go +++ b/namesys/pubsub.go @@ -203,7 +203,7 @@ func (r *PubsubResolver) resolveOnce(ctx context.Context, name string, options * id := peer.ID(hash) if r.host.Peerstore().PrivKey(id) != nil { - return "", errors.New("Cannot resolve own name through pubsub") + return "", errors.New("cannot resolve own name through pubsub") } pubk := id.ExtractPublicKey() diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 31bfd4956..9999801e4 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -131,7 +131,7 @@ func verifyCanResolve(r Resolver, name string, exp path.Path) error { } if res != exp { - return errors.New("got back wrong record!") + return errors.New("got back wrong record") } return nil From 4e88bd13e311dc3f57282a9144e619080a23bb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 18 Mar 2018 19:54:46 +0100 Subject: [PATCH 2299/3817] fix error style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-keystore@47fd52f3015fc18cb58864222ff3d0905e34698d --- keystore/keystore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index 0731f252b..5058964f8 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -250,7 +250,7 @@ func assertDirContents(dir string, exp []string) error { } if len(finfos) != len(exp) { - return fmt.Errorf("Expected %d directory entries", len(exp)) + return fmt.Errorf("expected %d directory entries", len(exp)) } var names []string From 57491c2b51bcb6116730fa5f53b868d6e6271892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 19 Mar 2018 03:41:28 +0100 Subject: [PATCH 2300/3817] misc: Fix a few typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-unixfs@c81738582a1ddb1a297ac2a80b2df1fe1a8f5c97 --- unixfs/mod/dagmodifier.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 0fe9df1e4..289d72778 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -52,7 +52,7 @@ type DagModifier struct { } // NewDagModifier returns a new DagModifier, the Cid prefix for newly -// created nodes will be inherted from the passed in node. If the Cid +// created nodes will be inhered from the passed in node. If the Cid // version if not 0 raw leaves will also be enabled. The Prefix and // RawLeaves options can be overridden by changing them after the call. func NewDagModifier(ctx context.Context, from ipld.Node, serv ipld.DAGService, spl chunker.SplitterGen) (*DagModifier, error) { @@ -82,7 +82,7 @@ func NewDagModifier(ctx context.Context, from ipld.Node, serv ipld.DAGService, s // WriteAt will modify a dag file in place func (dm *DagModifier) WriteAt(b []byte, offset int64) (int, error) { - // TODO: this is currently VERY inneficient + // TODO: this is currently VERY inefficient // each write that happens at an offset other than the current one causes a // flush to disk, and dag rewrite if offset == int64(dm.writeStart) && dm.wrBuf != nil { From 0ca93ce871437941f38de16c130ffca54b064882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 19 Mar 2018 03:41:28 +0100 Subject: [PATCH 2301/3817] misc: Fix a few typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-merkledag@795a29d5fe084d6ba3b86b3d20c0ac474f0acb20 --- ipld/merkledag/raw.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index f3fb4f762..d0d37b2e6 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -14,8 +14,7 @@ type RawNode struct { blocks.Block } -// NewRawNode creates a RawNode using the default sha2-256 hash -// funcition. +// NewRawNode creates a RawNode using the default sha2-256 hash function. func NewRawNode(data []byte) *RawNode { h := u.Hash(data) c := cid.NewCidV1(cid.Raw, h) From bf4aee0e8f6dfa35e009b57c7295881a52006215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 19 Mar 2018 03:41:28 +0100 Subject: [PATCH 2302/3817] misc: Fix a few typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@d7943a912758b36ebf4f0357b522aece0867f13c --- namesys/pubsub.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/pubsub.go b/namesys/pubsub.go index 8f8722721..808e2c5c0 100644 --- a/namesys/pubsub.go +++ b/namesys/pubsub.go @@ -315,7 +315,7 @@ func (r *PubsubResolver) handleSubscription(sub *floodsub.Subscription, name str err = r.receive(msg, name, pubk) if err != nil { - log.Warningf("PubsubResolve: error proessing update for %s: %s", name, err.Error()) + log.Warningf("PubsubResolve: error processing update for %s: %s", name, err.Error()) } } } @@ -369,7 +369,7 @@ func (r *PubsubResolver) receive(msg *floodsub.Message, name string, pubk ci.Pub } // rendezvous with peers in the name topic through provider records -// Note: rendezbous/boostrap should really be handled by the pubsub implementation itself! +// Note: rendezvous/boostrap should really be handled by the pubsub implementation itself! func bootstrapPubsub(ctx context.Context, cr routing.ContentRouting, host p2phost.Host, name string) { topic := "floodsub:" + name hash := u.Hash([]byte(topic)) From 9c617548f5ca8fc1ff845ec5609016cb8f07c67a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 19 Mar 2018 03:41:28 +0100 Subject: [PATCH 2303/3817] misc: Fix a few typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-pinner@5dcb944b8a37d7caad2a642e1f37c159355171c3 --- pinning/pinner/pin.go | 2 +- pinning/pinner/set.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 08eed36fe..d89704a5d 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -101,7 +101,7 @@ func StringToMode(s string) (Mode, bool) { // A Pinner provides the necessary methods to keep track of Nodes which are // to be kept locally, according to a pin mode. In practice, a Pinner is in // in charge of keeping the list of items from the local storage that should -// not be garbaged-collected. +// not be garbage-collected. type Pinner interface { // IsPinned returns whether or not the given cid is pinned // and an explanation of why its pinned diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index e2ba3ed11..d239859ea 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -188,7 +188,7 @@ func writeHdr(n *merkledag.ProtoNode, hdr *pb.Set) error { return err } - // make enough space for the length prefix and the marshalled header data + // make enough space for the length prefix and the marshaled header data data := make([]byte, binary.MaxVarintLen64, binary.MaxVarintLen64+len(hdrData)) // write the uvarint length of the header data From 76ac2a8842d68a73acece177235ec746ca637793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 19 Mar 2018 03:41:28 +0100 Subject: [PATCH 2304/3817] misc: Fix a few typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@646b14412abd5b2382fc11f37e40ef592c557fcb --- coreiface/options/pin.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/options/pin.go b/coreiface/options/pin.go index e46c27246..9d1107f92 100644 --- a/coreiface/options/pin.go +++ b/coreiface/options/pin.go @@ -95,9 +95,9 @@ func (pinType) Indirect() PinLsOption { // Recursive is an option for Pin.Add which specifies whether to pin an entire // object tree or just one object. Default: true -func (pinOpts) Recursive(recucsive bool) PinAddOption { +func (pinOpts) Recursive(recursive bool) PinAddOption { return func(settings *PinAddSettings) error { - settings.Recursive = recucsive + settings.Recursive = recursive return nil } } From fd2bbc831d85cf3d6c95b81df7614ac80220f418 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 3 Apr 2018 14:29:49 +0200 Subject: [PATCH 2305/3817] Extract: gx, travis, readme, license, makefile This commit was moved from ipfs/go-ipfs-exchange-interface@6e91bf9ba223bd8a03a82f0f9ae23fe6707aea07 --- exchange/LICENSE | 21 +++++++++++++++++++++ exchange/Makefile | 18 ++++++++++++++++++ exchange/README.md | 42 ++++++++++++++++++++++++++++++++++++++++++ exchange/interface.go | 5 ++--- 4 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 exchange/LICENSE create mode 100644 exchange/Makefile create mode 100644 exchange/README.md diff --git a/exchange/LICENSE b/exchange/LICENSE new file mode 100644 index 000000000..e4224df5b --- /dev/null +++ b/exchange/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 IPFS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/exchange/Makefile b/exchange/Makefile new file mode 100644 index 000000000..73f2841f6 --- /dev/null +++ b/exchange/Makefile @@ -0,0 +1,18 @@ +all: deps +gx: + go get github.com/whyrusleeping/gx + go get github.com/whyrusleeping/gx-go +deps: gx + gx --verbose install --global + gx-go rewrite +test: deps + gx test -v -race -coverprofile=coverage.txt -covermode=atomic . +rw: + gx-go rewrite +rwundo: + gx-go rewrite --undo +publish: rwundo + gx publish +.PHONY: all gx deps test rw rwundo publish + + diff --git a/exchange/README.md b/exchange/README.md new file mode 100644 index 000000000..8dbcfe1c3 --- /dev/null +++ b/exchange/README.md @@ -0,0 +1,42 @@ +# go-ipfs-exchange-interface + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![GoDoc](https://godoc.org/github.com/ipfs/go-ipfs-exchange-interface?status.svg)](https://godoc.org/github.com/ipfs/go-ipfs-exchange-interface) +[![Build Status](https://travis-ci.org/ipfs/go-ipfs-exchange-interface.svg?branch=master)](https://travis-ci.org/ipfs/go-ipfs-exchange-interface) + +> go-ipfs-exchange-interface defines the IPFS exchange interface + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Contribute](#contribute) +- [License](#license) + +## Install + +`go-ipfs-exchange-interface` works like a regular Go module: + +``` +> go get github.com/ipfs/go-ipfs-exchange-interface +``` + +## Usage + +``` +import "github.com/ipfs/go-ipfs-exchange-interface" +``` + +Check the [GoDoc documentation](https://godoc.org/github.com/ipfs/go-ipfs-exchange-interface) + +## Contribute + +PRs accepted. + +Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +MIT © Protocol Labs, Inc. diff --git a/exchange/interface.go b/exchange/interface.go index e3971d06c..675592ffd 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -5,9 +5,8 @@ import ( "context" "io" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" ) // Interface defines the functionality of the IPFS block exchange protocol. From bc86309c243894c6b2593eac93b2dee35685ddaa Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 3 Apr 2018 14:39:17 +0200 Subject: [PATCH 2306/3817] Extract: exchange/interface.go to go-ipfs-exchange-interface License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-exchange-offline@5f28f67ed156cd53bdadfe0bdf6b138b9b164467 --- exchange/offline/offline.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 5b4ad1685..ffeafd5d9 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -5,10 +5,9 @@ package offline import ( "context" - exchange "github.com/ipfs/go-ipfs/exchange" - blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + exchange "gx/ipfs/QmdcAXgEHUueP4A7b5hjabKn2EooeHgMreMvFC249dGCgc/go-ipfs-exchange-interface" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) From a5d4f04b28a29a76419c36dbb7cd99369536740d Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 3 Apr 2018 15:00:12 +0200 Subject: [PATCH 2307/3817] Extract: blocks/blocksutil to go-ipfs-blocksutil License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-exchange-offline@7c27fa9c3adb3d7b155c842c911aa97b69e1c9a4 --- exchange/offline/offline_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 19f2d77ae..487d188a0 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -4,14 +4,13 @@ import ( "context" "testing" - "github.com/ipfs/go-ipfs/blocks/blocksutil" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" ds_sync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + blocksutil "gx/ipfs/Qmf951DP11mCoctpyF3ZppPZdo2oAxuNi2vnkVDgHJ8Fqk/go-ipfs-blocksutil" ) func TestBlockReturnsErr(t *testing.T) { From 138b175ba11c507a8cbb79f21290a77acd210588 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 3 Apr 2018 16:24:13 +0200 Subject: [PATCH 2308/3817] Extract: gx, readme, license, travis This commit was moved from ipfs/go-ipfs-exchange-offline@19894bd93a6f19c7e72617b2e0a3e5dca1f83772 --- exchange/offline/LICENSE | 21 +++++++++++++++ exchange/offline/Makefile | 18 +++++++++++++ exchange/offline/README.md | 46 ++++++++++++++++++++++++++++++++ exchange/offline/offline.go | 8 +++--- exchange/offline/offline_test.go | 14 +++++----- 5 files changed, 96 insertions(+), 11 deletions(-) create mode 100644 exchange/offline/LICENSE create mode 100644 exchange/offline/Makefile create mode 100644 exchange/offline/README.md diff --git a/exchange/offline/LICENSE b/exchange/offline/LICENSE new file mode 100644 index 000000000..e4224df5b --- /dev/null +++ b/exchange/offline/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 IPFS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/exchange/offline/Makefile b/exchange/offline/Makefile new file mode 100644 index 000000000..73f2841f6 --- /dev/null +++ b/exchange/offline/Makefile @@ -0,0 +1,18 @@ +all: deps +gx: + go get github.com/whyrusleeping/gx + go get github.com/whyrusleeping/gx-go +deps: gx + gx --verbose install --global + gx-go rewrite +test: deps + gx test -v -race -coverprofile=coverage.txt -covermode=atomic . +rw: + gx-go rewrite +rwundo: + gx-go rewrite --undo +publish: rwundo + gx publish +.PHONY: all gx deps test rw rwundo publish + + diff --git a/exchange/offline/README.md b/exchange/offline/README.md new file mode 100644 index 000000000..707099e9a --- /dev/null +++ b/exchange/offline/README.md @@ -0,0 +1,46 @@ +# go-ipfs-exchange-offline + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![GoDoc](https://godoc.org/github.com/ipfs/go-ipfs-exchange-offline?status.svg)](https://godoc.org/github.com/ipfs/go-ipfs-exchange-offline) +[![Build Status](https://travis-ci.org/ipfs/go-ipfs-exchange-offline.svg?branch=master)](https://travis-ci.org/ipfs/go-ipfs-exchange-offline) + +> go-ipfs-exchange-offline implements the go-ipfs-exchange-interface + +This is an offline exchange implementation which will not perform any request. + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Contribute](#contribute) +- [License](#license) + +## Install + +`go-ipfs-exchange-offline` works like a regular Go module: + +``` +> go get github.com/ipfs/go-ipfs-exchange-offline +``` + +It uses [Gx](https://github.com/whyrusleeping/gx) to manage dependencies. You can use `make all` to build it with the `gx` dependencies. + +## Usage + +``` +import "github.com/ipfs/go-ipfs-exchange-offline" +``` + +Check the [GoDoc documentation](https://godoc.org/github.com/ipfs/go-ipfs-exchange-offline) + +## Contribute + +PRs accepted. + +Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +MIT © Protocol Labs, Inc. diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index ffeafd5d9..c3a284c7e 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -5,10 +5,10 @@ package offline import ( "context" - blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - exchange "gx/ipfs/QmdcAXgEHUueP4A7b5hjabKn2EooeHgMreMvFC249dGCgc/go-ipfs-exchange-interface" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + blockstore "github.com/ipfs/go-ipfs-blockstore" + exchange "github.com/ipfs/go-ipfs-exchange-interface" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 487d188a0..159208621 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -4,13 +4,13 @@ import ( "context" "testing" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - ds_sync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" - blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - blocksutil "gx/ipfs/Qmf951DP11mCoctpyF3ZppPZdo2oAxuNi2vnkVDgHJ8Fqk/go-ipfs-blocksutil" + blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + ds_sync "github.com/ipfs/go-datastore/sync" + blockstore "github.com/ipfs/go-ipfs-blockstore" + blocksutil "github.com/ipfs/go-ipfs-blocksutil" + u "github.com/ipfs/go-ipfs-util" ) func TestBlockReturnsErr(t *testing.T) { From fdd9542ddd8f3248a8f0aa8d36c220c6dad5b386 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Mon, 16 Apr 2018 17:49:54 -0300 Subject: [PATCH 2309/3817] dag: deduplicate AddNodeLinkClean into AddNodeLink `AddNodeLink` used to cache the linked node whereas `AddNodeLinkClean` did not, however, at some point the former was changed to do the same thing as the latter (i.e., not cache the linked node). That is, they now do the same thing so there's no reason to have both. The name `AddNodeLink` is preserved, even though it used to imply the cache functionality contrasting with the `Clean` suffix of `AddNodeLinkClean`, with this function removed the cache connotation doesn't hold anymore. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-merkledag@82e7692e0ea4c1d27f5b178a6eedc462f0ba0dde --- ipld/merkledag/merkledag_test.go | 14 +++++++------- ipld/merkledag/node.go | 13 ------------- ipld/merkledag/utils/utils.go | 6 +++--- 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 181ccbb88..ac4805363 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -340,7 +340,7 @@ func TestFetchFailure(t *testing.T) { t.Fatal(err) } - err = top.AddNodeLinkClean(fmt.Sprintf("AA%d", i), nd) + err = top.AddNodeLink(fmt.Sprintf("AA%d", i), nd) if err != nil { t.Fatal(err) } @@ -353,7 +353,7 @@ func TestFetchFailure(t *testing.T) { t.Fatal(err) } - err = top.AddNodeLinkClean(fmt.Sprintf("BB%d", i), nd) + err = top.AddNodeLink(fmt.Sprintf("BB%d", i), nd) if err != nil { t.Fatal(err) } @@ -597,19 +597,19 @@ func TestEnumerateAsyncFailsNotFound(t *testing.T) { } parent := new(ProtoNode) - if err := parent.AddNodeLinkClean("a", a); err != nil { + if err := parent.AddNodeLink("a", a); err != nil { t.Fatal(err) } - if err := parent.AddNodeLinkClean("b", b); err != nil { + if err := parent.AddNodeLink("b", b); err != nil { t.Fatal(err) } - if err := parent.AddNodeLinkClean("c", c); err != nil { + if err := parent.AddNodeLink("c", c); err != nil { t.Fatal(err) } - if err := parent.AddNodeLinkClean("d", d); err != nil { + if err := parent.AddNodeLink("d", d); err != nil { t.Fatal(err) } @@ -696,7 +696,7 @@ func mkNodeWithChildren(getChild func() *ProtoNode, width int) *ProtoNode { for i := 0; i < width; i++ { c := getChild() - if err := cur.AddNodeLinkClean(fmt.Sprint(i), c); err != nil { + if err := cur.AddNodeLink(fmt.Sprint(i), c); err != nil { panic(err) } } diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index a33dfcf6c..5ff66e76c 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -104,19 +104,6 @@ func (n *ProtoNode) AddNodeLink(name string, that ipld.Node) error { return nil } -// AddNodeLinkClean adds a link to another node. without keeping a reference to -// the child node -func (n *ProtoNode) AddNodeLinkClean(name string, that ipld.Node) error { - n.encoded = nil - lnk, err := ipld.MakeLink(that) - if err != nil { - return err - } - n.AddRawLink(name, lnk) - - return nil -} - // AddRawLink adds a copy of a link to this node func (n *ProtoNode) AddRawLink(name string, l *ipld.Link) error { n.encoded = nil diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index d6ac9726a..5e0e3b352 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -75,7 +75,7 @@ func addLink(ctx context.Context, ds ipld.DAGService, root *dag.ProtoNode, child // ensure no link with that name already exists _ = root.RemoveNodeLink(childname) // ignore error, only option is ErrNotFound - if err := root.AddNodeLinkClean(childname, childnd); err != nil { + if err := root.AddNodeLink(childname, childnd); err != nil { return nil, err } @@ -127,7 +127,7 @@ func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path _ = e.tmp.Remove(ctx, root.Cid()) _ = root.RemoveNodeLink(path[0]) - err = root.AddNodeLinkClean(path[0], ndprime) + err = root.AddNodeLink(path[0], ndprime) if err != nil { return nil, err } @@ -186,7 +186,7 @@ func (e *Editor) rmLink(ctx context.Context, root *dag.ProtoNode, path []string) e.tmp.Remove(ctx, root.Cid()) _ = root.RemoveNodeLink(path[0]) - err = root.AddNodeLinkClean(path[0], nnode) + err = root.AddNodeLink(path[0], nnode) if err != nil { return nil, err } From ba030c237a97bf6e3991831dd2122442a21b77c1 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Mon, 16 Apr 2018 17:49:54 -0300 Subject: [PATCH 2310/3817] dag: deduplicate AddNodeLinkClean into AddNodeLink `AddNodeLink` used to cache the linked node whereas `AddNodeLinkClean` did not, however, at some point the former was changed to do the same thing as the latter (i.e., not cache the linked node). That is, they now do the same thing so there's no reason to have both. The name `AddNodeLink` is preserved, even though it used to imply the cache functionality contrasting with the `Clean` suffix of `AddNodeLinkClean`, with this function removed the cache connotation doesn't hold anymore. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-ipfs-pinner@af580ea1b13624df041f48ab3be39e9fb36a45ea --- pinning/pinner/pin_test.go | 2 +- pinning/pinner/set_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 94da70ed3..e6a8a0850 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -340,7 +340,7 @@ func TestPinRecursiveFail(t *testing.T) { a, _ := randNode() b, _ := randNode() - err := a.AddNodeLinkClean("child", b) + err := a.AddNodeLink("child", b) if err != nil { t.Fatal(err) } diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 815322796..4fea86bd2 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -74,7 +74,7 @@ func TestSet(t *testing.T) { // weird wrapper node because loadSet expects us to pass an // object pointing to multiple named sets setroot := &dag.ProtoNode{} - err = setroot.AddNodeLinkClean("foo", out) + err = setroot.AddNodeLink("foo", out) if err != nil { t.Fatal(err) } From d2d9783165ae4085cc7d0adbc7d7d1b6d6f77bec Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Mon, 16 Apr 2018 17:49:54 -0300 Subject: [PATCH 2311/3817] dag: deduplicate AddNodeLinkClean into AddNodeLink `AddNodeLink` used to cache the linked node whereas `AddNodeLinkClean` did not, however, at some point the former was changed to do the same thing as the latter (i.e., not cache the linked node). That is, they now do the same thing so there's no reason to have both. The name `AddNodeLink` is preserved, even though it used to imply the cache functionality contrasting with the `Clean` suffix of `AddNodeLinkClean`, with this function removed the cache connotation doesn't hold anymore. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-unixfs@165433d217b834ba9673a73435e718c265770454 --- unixfs/io/dirbuilder.go | 2 +- unixfs/mod/dagmodifier.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 616617dee..e8cbff52d 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -103,7 +103,7 @@ func (d *Directory) AddChild(ctx context.Context, name string, nd ipld.Node) err if d.shard == nil { if !UseHAMTSharding { _ = d.dirnode.RemoveNodeLink(name) - return d.dirnode.AddNodeLinkClean(name, nd) + return d.dirnode.AddNodeLink(name, nd) } err := d.switchToSharding(ctx) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 0fe9df1e4..edf05ecba 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -561,7 +561,7 @@ func dagTruncate(ctx context.Context, n ipld.Node, size uint64, ds ipld.DAGServi } nd.SetLinks(nd.Links()[:end]) - err = nd.AddNodeLinkClean("", modified) + err = nd.AddNodeLink("", modified) if err != nil { return nil, err } From 5e8974368d0e6ec6c09d77321f45b9d25ac74fec Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 3 Apr 2018 14:39:17 +0200 Subject: [PATCH 2312/3817] Extract: exchange/interface.go to go-ipfs-exchange-interface License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-blockservice@65f110bb15016b9520877d7c35266b37d0fde4ac --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index a54c0ff2b..3b9f203eb 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -9,12 +9,12 @@ import ( "fmt" "io" - exchange "github.com/ipfs/go-ipfs/exchange" "github.com/ipfs/go-ipfs/thirdparty/verifcid" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + exchange "gx/ipfs/QmdcAXgEHUueP4A7b5hjabKn2EooeHgMreMvFC249dGCgc/go-ipfs-exchange-interface" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) From e790fc7669289e6dd898400453822658e42f9095 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 3 Apr 2018 15:00:12 +0200 Subject: [PATCH 2313/3817] Extract: blocks/blocksutil to go-ipfs-blocksutil License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-blockservice@72a2ce66ee277cf3fff87dc5b9bd63d8faadd4e8 --- blockservice/blockservice_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index edcd4cd98..77e664568 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -3,13 +3,13 @@ package blockservice import ( "testing" - butil "github.com/ipfs/go-ipfs/blocks/blocksutil" offline "github.com/ipfs/go-ipfs/exchange/offline" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + butil "gx/ipfs/Qmf951DP11mCoctpyF3ZppPZdo2oAxuNi2vnkVDgHJ8Fqk/go-ipfs-blocksutil" ) func TestWriteThroughWorks(t *testing.T) { From 9844a40a9b0cb579fd47579da8d7aa7df779ab8e Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 3 Apr 2018 16:30:32 +0200 Subject: [PATCH 2314/3817] Extract exchange/offline to go-ipfs-exchange offline License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@49051a39c21fc6f50ad07789ed2c252923fa0ae9 --- ipld/merkledag/merkledag_test.go | 2 +- ipld/merkledag/test/utils.go | 2 +- ipld/merkledag/utils/utils.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 181ccbb88..fc972ecec 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -15,12 +15,12 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" bstest "github.com/ipfs/go-ipfs/blockservice/test" - offline "github.com/ipfs/go-ipfs/exchange/offline" . "github.com/ipfs/go-ipfs/merkledag" mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index 7de0b841e..ebba914bc 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -2,9 +2,9 @@ package mdutils import ( bsrv "github.com/ipfs/go-ipfs/blockservice" - "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" + offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index d6ac9726a..1a107b364 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -5,10 +5,10 @@ import ( "errors" bserv "github.com/ipfs/go-ipfs/blockservice" - offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" + offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" syncds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" bstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" From d752b9835c69b49d02a975a7b71e752c91b0e9da Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 3 Apr 2018 16:30:32 +0200 Subject: [PATCH 2315/3817] Extract exchange/offline to go-ipfs-exchange offline License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-pinner@c5d98d7a764e6803a74334ac3a11c2f1d33ba3d6 --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin_test.go | 2 +- pinning/pinner/set_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 8769db8e1..1606a0179 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,12 +8,12 @@ import ( "strings" bserv "github.com/ipfs/go-ipfs/blockservice" - offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" "github.com/ipfs/go-ipfs/thirdparty/verifcid" logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" + offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" dstore "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" bstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 94da70ed3..69169c174 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -6,10 +6,10 @@ import ( "time" bs "github.com/ipfs/go-ipfs/blockservice" - "github.com/ipfs/go-ipfs/exchange/offline" mdag "github.com/ipfs/go-ipfs/merkledag" util "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 815322796..3a266ecaa 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -6,9 +6,9 @@ import ( "testing" bserv "github.com/ipfs/go-ipfs/blockservice" - offline "github.com/ipfs/go-ipfs/exchange/offline" dag "github.com/ipfs/go-ipfs/merkledag" + offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" dsq "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/query" blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" From f7d9d0112b04149506c336594113b1c8cae5a92f Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 3 Apr 2018 16:30:32 +0200 Subject: [PATCH 2316/3817] Extract exchange/offline to go-ipfs-exchange offline License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-blockservice@1793868c5ea62c5dae68540506e84ee0d573069a --- blockservice/blockservice_test.go | 3 +-- blockservice/test/blocks_test.go | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index 77e664568..eda9497c1 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -3,8 +3,7 @@ package blockservice import ( "testing" - offline "github.com/ipfs/go-ipfs/exchange/offline" - + offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 2a56afeb7..978bc8658 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -8,9 +8,9 @@ import ( "time" . "github.com/ipfs/go-ipfs/blockservice" - offline "github.com/ipfs/go-ipfs/exchange/offline" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" From 8b738d27b70819ca5bf77182e1bf098bce3c5864 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 3 Apr 2018 16:30:32 +0200 Subject: [PATCH 2317/3817] Extract exchange/offline to go-ipfs-exchange offline License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-mfs@f2a77a13453793425719daac03417efbd7d86786 --- mfs/mfs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 6255c90c4..f7f8c877b 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -15,7 +15,6 @@ import ( "time" bserv "github.com/ipfs/go-ipfs/blockservice" - offline "github.com/ipfs/go-ipfs/exchange/offline" importer "github.com/ipfs/go-ipfs/importer" dag "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/path" @@ -23,6 +22,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" chunker "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" From c55a7c738e598fe715bde7dc2eddbf131d2b9c08 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 19 Apr 2018 12:13:33 +0200 Subject: [PATCH 2318/3817] fix json License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-merkledag@2b4aa1a364db0b1a338d0bbcf476a52442ad44c6 --- ipld/merkledag/merkledag_test.go | 14 +++++++------- ipld/merkledag/node.go | 13 ------------- ipld/merkledag/utils/utils.go | 6 +++--- 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index fc972ecec..bcc207d89 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -340,7 +340,7 @@ func TestFetchFailure(t *testing.T) { t.Fatal(err) } - err = top.AddNodeLinkClean(fmt.Sprintf("AA%d", i), nd) + err = top.AddNodeLink(fmt.Sprintf("AA%d", i), nd) if err != nil { t.Fatal(err) } @@ -353,7 +353,7 @@ func TestFetchFailure(t *testing.T) { t.Fatal(err) } - err = top.AddNodeLinkClean(fmt.Sprintf("BB%d", i), nd) + err = top.AddNodeLink(fmt.Sprintf("BB%d", i), nd) if err != nil { t.Fatal(err) } @@ -597,19 +597,19 @@ func TestEnumerateAsyncFailsNotFound(t *testing.T) { } parent := new(ProtoNode) - if err := parent.AddNodeLinkClean("a", a); err != nil { + if err := parent.AddNodeLink("a", a); err != nil { t.Fatal(err) } - if err := parent.AddNodeLinkClean("b", b); err != nil { + if err := parent.AddNodeLink("b", b); err != nil { t.Fatal(err) } - if err := parent.AddNodeLinkClean("c", c); err != nil { + if err := parent.AddNodeLink("c", c); err != nil { t.Fatal(err) } - if err := parent.AddNodeLinkClean("d", d); err != nil { + if err := parent.AddNodeLink("d", d); err != nil { t.Fatal(err) } @@ -696,7 +696,7 @@ func mkNodeWithChildren(getChild func() *ProtoNode, width int) *ProtoNode { for i := 0; i < width; i++ { c := getChild() - if err := cur.AddNodeLinkClean(fmt.Sprint(i), c); err != nil { + if err := cur.AddNodeLink(fmt.Sprint(i), c); err != nil { panic(err) } } diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index a33dfcf6c..5ff66e76c 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -104,19 +104,6 @@ func (n *ProtoNode) AddNodeLink(name string, that ipld.Node) error { return nil } -// AddNodeLinkClean adds a link to another node. without keeping a reference to -// the child node -func (n *ProtoNode) AddNodeLinkClean(name string, that ipld.Node) error { - n.encoded = nil - lnk, err := ipld.MakeLink(that) - if err != nil { - return err - } - n.AddRawLink(name, lnk) - - return nil -} - // AddRawLink adds a copy of a link to this node func (n *ProtoNode) AddRawLink(name string, l *ipld.Link) error { n.encoded = nil diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 1a107b364..4416fb15d 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -75,7 +75,7 @@ func addLink(ctx context.Context, ds ipld.DAGService, root *dag.ProtoNode, child // ensure no link with that name already exists _ = root.RemoveNodeLink(childname) // ignore error, only option is ErrNotFound - if err := root.AddNodeLinkClean(childname, childnd); err != nil { + if err := root.AddNodeLink(childname, childnd); err != nil { return nil, err } @@ -127,7 +127,7 @@ func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path _ = e.tmp.Remove(ctx, root.Cid()) _ = root.RemoveNodeLink(path[0]) - err = root.AddNodeLinkClean(path[0], ndprime) + err = root.AddNodeLink(path[0], ndprime) if err != nil { return nil, err } @@ -186,7 +186,7 @@ func (e *Editor) rmLink(ctx context.Context, root *dag.ProtoNode, path []string) e.tmp.Remove(ctx, root.Cid()) _ = root.RemoveNodeLink(path[0]) - err = root.AddNodeLinkClean(path[0], nnode) + err = root.AddNodeLink(path[0], nnode) if err != nil { return nil, err } From 54c696a60dc8c73b4fdf27bfb9ed9e23f4216ae6 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 19 Apr 2018 12:13:33 +0200 Subject: [PATCH 2319/3817] fix json License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-pinner@7cfca5e39ac19a7da8d506df2aa04e476fc92625 --- pinning/pinner/pin_test.go | 2 +- pinning/pinner/set_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 69169c174..e65f8b63e 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -340,7 +340,7 @@ func TestPinRecursiveFail(t *testing.T) { a, _ := randNode() b, _ := randNode() - err := a.AddNodeLinkClean("child", b) + err := a.AddNodeLink("child", b) if err != nil { t.Fatal(err) } diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 3a266ecaa..f7d6d0ede 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -74,7 +74,7 @@ func TestSet(t *testing.T) { // weird wrapper node because loadSet expects us to pass an // object pointing to multiple named sets setroot := &dag.ProtoNode{} - err = setroot.AddNodeLinkClean("foo", out) + err = setroot.AddNodeLink("foo", out) if err != nil { t.Fatal(err) } From 5a72bc2a21ca56fc4f0a3001cae5e22915d64e10 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 19 Apr 2018 12:13:33 +0200 Subject: [PATCH 2320/3817] fix json License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-unixfs@6facbb3a060d2b55d458048e8fdccac9bde26c9d --- unixfs/io/dirbuilder.go | 2 +- unixfs/mod/dagmodifier.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index 616617dee..e8cbff52d 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -103,7 +103,7 @@ func (d *Directory) AddChild(ctx context.Context, name string, nd ipld.Node) err if d.shard == nil { if !UseHAMTSharding { _ = d.dirnode.RemoveNodeLink(name) - return d.dirnode.AddNodeLinkClean(name, nd) + return d.dirnode.AddNodeLink(name, nd) } err := d.switchToSharding(ctx) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 0fe9df1e4..edf05ecba 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -561,7 +561,7 @@ func dagTruncate(ctx context.Context, n ipld.Node, size uint64, ds ipld.DAGServi } nd.SetLinks(nd.Links()[:end]) - err = nd.AddNodeLinkClean("", modified) + err = nd.AddNodeLink("", modified) if err != nil { return nil, err } From 7e82d94fe773f4e069c6b798ce3eb0fa88e4e6f9 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 18 Apr 2018 16:43:20 +0200 Subject: [PATCH 2321/3817] cid-sec: fix bitswap strom caused by insecure CIDs When we introduced CID security we didn't take into account that bitswap might repeatly try getting the objects from the network if it fails putting them into the blockstore. Solution from this is not requesting those objects from bitswap. The proper solution of failing at CID creation will make in much more cleaner in future. License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-blockservice@fab4b5edc46f95cab94297cd6bee6ce10e0cb2f2 --- blockservice/blockservice.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 3b9f203eb..b40c66473 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -251,15 +251,22 @@ func (s *blockService) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan bloc func getBlocks(ctx context.Context, ks []*cid.Cid, bs blockstore.Blockstore, f exchange.Fetcher) <-chan blocks.Block { out := make(chan blocks.Block) - for _, c := range ks { - // hash security - if err := verifcid.ValidateCid(c); err != nil { - log.Errorf("unsafe CID (%s) passed to blockService.GetBlocks: %s", c, err) - } - } go func() { defer close(out) + + k := 0 + for _, c := range ks { + // hash security + if err := verifcid.ValidateCid(c); err == nil { + ks[k] = c + k++ + } else { + log.Errorf("unsafe CID (%s) passed to blockService.GetBlocks: %s", c, err) + } + } + ks = ks[:k] + var misses []*cid.Cid for _, c := range ks { hit, err := bs.Get(c) From 2e85217a048994843c9ceeb776b74bc42f4ae67f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 3 May 2018 20:53:45 -0700 Subject: [PATCH 2322/3817] gx update deps This commit was moved from ipfs/go-ipfs-routing@92235eb8773d209cf169403b969f2226f9183586 --- routing/offline/offline_test.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index a685dcab8..548822b6a 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -5,8 +5,10 @@ import ( "context" "testing" + cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" - "github.com/libp2p/go-testutil" + testutil "github.com/libp2p/go-testutil" + mh "github.com/multiformats/go-multihash" ) func TestOfflineRouterStorage(t *testing.T) { @@ -62,15 +64,17 @@ func TestOfflineRouterLocal(t *testing.T) { t.Fatal("OfflineRouting should alert that its offline") } - cid, _ := testutil.RandCidV0() - pChan := offline.FindProvidersAsync(ctx, cid, 1) + h, _ := mh.Sum([]byte("test data1"), mh.SHA2_256, -1) + c1 := cid.NewCidV0(h) + pChan := offline.FindProvidersAsync(ctx, c1, 1) p, ok := <-pChan if ok { t.Fatalf("FindProvidersAsync did not return a closed channel. Instead we got %+v !", p) } - cid, _ = testutil.RandCidV0() - err = offline.Provide(ctx, cid, true) + h2, _ := mh.Sum([]byte("test data1"), mh.SHA2_256, -1) + c2 := cid.NewCidV0(h2) + err = offline.Provide(ctx, c2, true) if err != ErrOffline { t.Fatal("OfflineRouting should alert that its offline") } From 3e90aa8079be712214517738384a50924c7148a8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 4 May 2018 18:06:17 -0700 Subject: [PATCH 2323/3817] fix the documentation This repo actually has 3 different packages which are documented independently. fixes #3 This commit was moved from ipfs/go-ipfs-routing@21c5e942ab4d78634d2e7e4727526f98480ad5fd --- routing/README.md | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/routing/README.md b/routing/README.md index 636148909..b2b7a96d4 100644 --- a/routing/README.md +++ b/routing/README.md @@ -3,7 +3,6 @@ [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) [![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) -[![GoDoc](https://godoc.org/github.com/ipfs/go-ipfs-routing?status.svg)](https://godoc.org/github.com/ipfs/go-ipfs-routing) [![Build Status](https://travis-ci.org/ipfs/go-ipfs-routing.svg?branch=master)](https://travis-ci.org/ipfs/go-ipfs-routing) > go-ipfs-routing provides go-libp2p-routing implementations used in go-ipfs. @@ -17,27 +16,60 @@ ## Install -`go-ipfs-routing` works like a regular Go module: +`go-ipfs-routing` works like a set of regular Go packages: ``` -> go get github.com/ipfs/go-ipfs-routing +> go get github.com/ipfs/go-ipfs-routing/... ``` -This module uses [Gx](https://github.com/whyrusleeping/gx) to manage dependencies. You can use `make all` to build it with the `gx` dependencies. +This module uses [Gx](https://github.com/whyrusleeping/gx) to manage +dependencies. You can use `make all` to build it with the `gx` dependencies. ## Usage +This repo contains 3 different packages. + +### Mock + +[![GoDoc](https://godoc.org/github.com/ipfs/go-ipfs-routing/mock?status.svg)](https://godoc.org/github.com/ipfs/go-ipfs-routing/mock) + +``` +import "github.com/ipfs/go-ipfs-routing/mock" +``` + +Mock is a fake router useful for tests. It provides a mock client that +implements the `IpfsRouting` interface and a mock server from which the client +retrieves routing records. + + +### Offline + +[![GoDoc](https://godoc.org/github.com/ipfs/go-ipfs-routing/offline?status.svg)](https://godoc.org/github.com/ipfs/go-ipfs-routing/offline) + +``` +import "github.com/ipfs/go-ipfs-routing/offline" +``` + +Offline is an offline router that can put and get records to and from a local +`Datastore` but can't retrieve them from the network. + +### None + +[![GoDoc](https://godoc.org/github.com/ipfs/go-ipfs-routing/none?status.svg)](https://godoc.org/github.com/ipfs/go-ipfs-routing/none) + ``` -import "github.com/ipfs/go-ipfs-routing" +import "github.com/ipfs/go-ipfs-routing/none" ``` -Check the [GoDoc documentation](https://godoc.org/github.com/ipfs/go-ipfs-routing) +None is a router no-op router that doesn't do anything. Puts always succeed and +lookups always fail. ## Contribute PRs accepted. -Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. +Small note: If editing the README, please conform to the +[standard-readme](https://github.com/RichardLitt/standard-readme) specification. ## License From 91879c039d477d46fe5e0aba58fa4bf7c675db62 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 3 May 2018 21:04:12 -0700 Subject: [PATCH 2324/3817] update for routing interface changes Specifically, remove GetValues This commit was moved from ipfs/go-ipfs-routing@cc777593c6159ad81c0a2ed21c4af9ef0598a6e4 --- routing/mock/centralized_client.go | 15 +++------------ routing/none/none_client.go | 12 +++++------- routing/offline/offline.go | 26 +++----------------------- routing/offline/offline_test.go | 6 +++--- 4 files changed, 14 insertions(+), 45 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index f00f11a09..c0d70f94a 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -15,6 +15,7 @@ import ( pstore "github.com/libp2p/go-libp2p-peerstore" dhtpb "github.com/libp2p/go-libp2p-record/pb" routing "github.com/libp2p/go-libp2p-routing" + ropts "github.com/libp2p/go-libp2p-routing/options" "github.com/libp2p/go-testutil" ma "github.com/multiformats/go-multiaddr" ) @@ -28,7 +29,7 @@ type client struct { } // FIXME(brian): is this method meant to simulate putting a value into the network? -func (c *client) PutValue(ctx context.Context, key string, val []byte) error { +func (c *client) PutValue(ctx context.Context, key string, val []byte, opts ...ropts.Option) error { log.Debugf("PutValue: %s", key) rec := new(dhtpb.Record) rec.Value = val @@ -43,7 +44,7 @@ func (c *client) PutValue(ctx context.Context, key string, val []byte) error { } // FIXME(brian): is this method meant to simulate getting a value from the network? -func (c *client) GetValue(ctx context.Context, key string) ([]byte, error) { +func (c *client) GetValue(ctx context.Context, key string, opts ...ropts.Option) ([]byte, error) { log.Debugf("GetValue: %s", key) v, err := c.datastore.Get(dshelp.NewKeyFromBinary([]byte(key))) if err != nil { @@ -64,16 +65,6 @@ func (c *client) GetValue(ctx context.Context, key string) ([]byte, error) { return rec.GetValue(), nil } -func (c *client) GetValues(ctx context.Context, key string, count int) ([]routing.RecvdVal, error) { - log.Debugf("GetValues: %s", key) - data, err := c.GetValue(ctx, key) - if err != nil { - return nil, err - } - - return []routing.RecvdVal{{Val: data, From: c.peer.ID()}}, nil -} - func (c *client) FindProviders(ctx context.Context, key *cid.Cid) ([]pstore.PeerInfo, error) { return c.server.Providers(key), nil } diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 8935708d1..a7c1f8fc9 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -10,24 +10,22 @@ import ( p2phost "github.com/libp2p/go-libp2p-host" peer "github.com/libp2p/go-libp2p-peer" pstore "github.com/libp2p/go-libp2p-peerstore" + record "github.com/libp2p/go-libp2p-record" routing "github.com/libp2p/go-libp2p-routing" + ropts "github.com/libp2p/go-libp2p-routing/options" ) type nilclient struct { } -func (c *nilclient) PutValue(_ context.Context, _ string, _ []byte) error { +func (c *nilclient) PutValue(_ context.Context, _ string, _ []byte, _ ...ropts.Option) error { return nil } -func (c *nilclient) GetValue(_ context.Context, _ string) ([]byte, error) { +func (c *nilclient) GetValue(_ context.Context, _ string, _ ...ropts.Option) ([]byte, error) { return nil, errors.New("tried GetValue from nil routing") } -func (c *nilclient) GetValues(_ context.Context, _ string, _ int) ([]routing.RecvdVal, error) { - return nil, errors.New("tried GetValues from nil routing") -} - func (c *nilclient) FindPeer(_ context.Context, _ peer.ID) (pstore.PeerInfo, error) { return pstore.PeerInfo{}, nil } @@ -47,7 +45,7 @@ func (c *nilclient) Bootstrap(_ context.Context) error { } // ConstructNilRouting creates an IpfsRouting client which does nothing. -func ConstructNilRouting(_ context.Context, _ p2phost.Host, _ ds.Batching) (routing.IpfsRouting, error) { +func ConstructNilRouting(_ context.Context, _ p2phost.Host, _ ds.Batching, _ record.Validator) (routing.IpfsRouting, error) { return &nilclient{}, nil } diff --git a/routing/offline/offline.go b/routing/offline/offline.go index f008e2529..422af8d61 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -17,6 +17,7 @@ import ( record "github.com/libp2p/go-libp2p-record" pb "github.com/libp2p/go-libp2p-record/pb" routing "github.com/libp2p/go-libp2p-routing" + ropts "github.com/libp2p/go-libp2p-routing/options" ) // ErrOffline is returned when trying to perform operations that @@ -41,7 +42,7 @@ type offlineRouting struct { sk ci.PrivKey } -func (c *offlineRouting) PutValue(ctx context.Context, key string, val []byte) error { +func (c *offlineRouting) PutValue(ctx context.Context, key string, val []byte, _ ...ropts.Option) error { rec := record.MakePutRecord(key, val) data, err := proto.Marshal(rec) if err != nil { @@ -51,7 +52,7 @@ func (c *offlineRouting) PutValue(ctx context.Context, key string, val []byte) e return c.datastore.Put(dshelp.NewKeyFromBinary([]byte(key)), data) } -func (c *offlineRouting) GetValue(ctx context.Context, key string) ([]byte, error) { +func (c *offlineRouting) GetValue(ctx context.Context, key string, _ ...ropts.Option) ([]byte, error) { v, err := c.datastore.Get(dshelp.NewKeyFromBinary([]byte(key))) if err != nil { return nil, err @@ -70,27 +71,6 @@ func (c *offlineRouting) GetValue(ctx context.Context, key string) ([]byte, erro return rec.GetValue(), nil } -func (c *offlineRouting) GetValues(ctx context.Context, key string, _ int) ([]routing.RecvdVal, error) { - v, err := c.datastore.Get(dshelp.NewKeyFromBinary([]byte(key))) - if err != nil { - return nil, err - } - - byt, ok := v.([]byte) - if !ok { - return nil, errors.New("value stored in datastore not []byte") - } - rec := new(pb.Record) - err = proto.Unmarshal(byt, rec) - if err != nil { - return nil, err - } - - return []routing.RecvdVal{ - {Val: rec.GetValue()}, - }, nil -} - func (c *offlineRouting) FindPeer(ctx context.Context, pid peer.ID) (pstore.PeerInfo, error) { return pstore.PeerInfo{}, ErrOffline } diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index 548822b6a..61670f442 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -7,6 +7,7 @@ import ( cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" + ropt "github.com/libp2p/go-libp2p-routing/options" testutil "github.com/libp2p/go-testutil" mh "github.com/multiformats/go-multihash" ) @@ -35,17 +36,16 @@ func TestOfflineRouterStorage(t *testing.T) { t.Fatal("Router should throw errors for unfound records") } - recVal, err := offline.GetValues(ctx, "key", 0) + local, err := offline.GetValue(ctx, "key", ropt.Offline) if err != nil { t.Fatal(err) } - _, err = offline.GetValues(ctx, "notHere", 0) + _, err = offline.GetValue(ctx, "notHere", ropt.Offline) if err == nil { t.Fatal("Router should throw errors for unfound records") } - local := recVal[0].Val if !bytes.Equal([]byte("testing 1 2 3"), local) { t.Fatal("OfflineRouter does not properly store") } From cd66f5d11e53811eb676e16b956f90c3492e32ab Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 3 May 2018 21:39:52 -0700 Subject: [PATCH 2325/3817] update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@2d4a451ff32beb43c365eeae1f1ade3aa9990850 --- namesys/interface.go | 2 +- namesys/ipns_select_test.go | 2 +- namesys/ipns_validate_test.go | 20 ++++++++++---------- namesys/namesys.go | 12 ++++++------ namesys/namesys_test.go | 8 ++++---- namesys/publisher.go | 12 ++++++------ namesys/publisher_test.go | 14 +++++++------- namesys/pubsub.go | 22 +++++++++++----------- namesys/pubsub_test.go | 22 +++++++++++----------- namesys/republisher/repub.go | 14 +++++++------- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 10 +++++----- namesys/routing.go | 6 +++--- namesys/validator.go | 6 +++--- 14 files changed, 77 insertions(+), 77 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index 4def9b1d7..fcd619b49 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -38,7 +38,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" - ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index 7489f139b..9ba39ce7f 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -11,7 +11,7 @@ import ( u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ) func shuffle(a []*pb.IpnsEntry) { diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index ac3fb8470..44149180e 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -10,17 +10,17 @@ import ( path "github.com/ipfs/go-ipfs/path" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" - record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" - recordpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" - testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" - pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" - mockrouting "gx/ipfs/QmXtoXbu9ReyV6Q4kDQ5CF9wXQNDY1PdHc4HhfxRR5AHB3/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmPuPdzoG4b5uyYSQCjLEHB8NM593m3BW19UHX2jZ6Wzfm/go-ipfs-routing/mock" + record "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record" + recordpb "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record/pb" + routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" + testutil "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" - ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" + pstore "gx/ipfs/QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh/go-libp2p-peerstore" + ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, ns string, key string, val []byte, eol time.Time, exp error) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 9b4cdf3f7..47e1f874f 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -10,14 +10,14 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" - p2phost "gx/ipfs/QmNmJZL7FQySMtE2BQuLMuZg2EB2CLEunJJUSVSc9YnnbV/go-libp2p-host" - floodsub "gx/ipfs/QmSFihvoND3eDaAYRCeLgLPt62yCPgMZs1NSZmKFEtJQQw/go-libp2p-floodsub" - routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" + floodsub "gx/ipfs/QmVKrsEgixRtMWcMd6WQzuwqCUC3jfLf7Q7xcjnKoMMikS/go-libp2p-floodsub" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" - ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" + ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + p2phost "gx/ipfs/QmfZTdmunzKzAGJrSvXXQbQ5kLLUiEMX5vdwux7iXkdk7D/go-libp2p-host" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 9b27cf322..03ba60ed0 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,10 +10,10 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/unixfs" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" - offroute "gx/ipfs/QmXtoXbu9ReyV6Q4kDQ5CF9wXQNDY1PdHc4HhfxRR5AHB3/go-ipfs-routing/offline" - ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + offroute "gx/ipfs/QmPuPdzoG4b5uyYSQCjLEHB8NM593m3BW19UHX2jZ6Wzfm/go-ipfs-routing/offline" + ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) type mockResolver struct { diff --git a/namesys/publisher.go b/namesys/publisher.go index b485b24b2..d75a4e5cf 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -12,13 +12,13 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" - dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" - dhtpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + dhtpb "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record/pb" + routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" + dshelp "gx/ipfs/QmYJgz1Z5PbBGP7n2XA8uv5sF1EKLfYUjL7kFemVAjMNqC/go-ipfs-ds-help" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" - ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" + ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) const PublishPutValTimeout = time.Minute diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 20f774fa3..e4ad1fa69 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -8,14 +8,14 @@ import ( path "github.com/ipfs/go-ipfs/path" - dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" - testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" + mockrouting "gx/ipfs/QmPuPdzoG4b5uyYSQCjLEHB8NM593m3BW19UHX2jZ6Wzfm/go-ipfs-routing/mock" + testutil "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil" ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" - mockrouting "gx/ipfs/QmXtoXbu9ReyV6Q4kDQ5CF9wXQNDY1PdHc4HhfxRR5AHB3/go-ipfs-routing/mock" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" - ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + dshelp "gx/ipfs/QmYJgz1Z5PbBGP7n2XA8uv5sF1EKLfYUjL7kFemVAjMNqC/go-ipfs-ds-help" + peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" + ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) type identity struct { diff --git a/namesys/pubsub.go b/namesys/pubsub.go index 9171b16cc..ba4a73f66 100644 --- a/namesys/pubsub.go +++ b/namesys/pubsub.go @@ -13,20 +13,20 @@ import ( path "github.com/ipfs/go-ipfs/path" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - p2phost "gx/ipfs/QmNmJZL7FQySMtE2BQuLMuZg2EB2CLEunJJUSVSc9YnnbV/go-libp2p-host" - floodsub "gx/ipfs/QmSFihvoND3eDaAYRCeLgLPt62yCPgMZs1NSZmKFEtJQQw/go-libp2p-floodsub" - routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" - dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" - record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" - dhtpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" - pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" + record "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record" + dhtpb "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record/pb" + routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" + floodsub "gx/ipfs/QmVKrsEgixRtMWcMd6WQzuwqCUC3jfLf7Q7xcjnKoMMikS/go-libp2p-floodsub" + dshelp "gx/ipfs/QmYJgz1Z5PbBGP7n2XA8uv5sF1EKLfYUjL7kFemVAjMNqC/go-ipfs-ds-help" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" - ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + pstore "gx/ipfs/QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh/go-libp2p-peerstore" + ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" + p2phost "gx/ipfs/QmfZTdmunzKzAGJrSvXXQbQ5kLLUiEMX5vdwux7iXkdk7D/go-libp2p-host" ) // PubsubPublisher is a publisher that distributes IPNS records through pubsub diff --git a/namesys/pubsub_test.go b/namesys/pubsub_test.go index 16dae39fc..46e414b9d 100644 --- a/namesys/pubsub_test.go +++ b/namesys/pubsub_test.go @@ -8,17 +8,17 @@ import ( path "github.com/ipfs/go-ipfs/path" - p2phost "gx/ipfs/QmNmJZL7FQySMtE2BQuLMuZg2EB2CLEunJJUSVSc9YnnbV/go-libp2p-host" - bhost "gx/ipfs/QmQr1j6UvdhpponAaqSdswqRpdzsFwNop2N8kXLNw8afem/go-libp2p-blankhost" - floodsub "gx/ipfs/QmSFihvoND3eDaAYRCeLgLPt62yCPgMZs1NSZmKFEtJQQw/go-libp2p-floodsub" - routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" - testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" - mockrouting "gx/ipfs/QmXtoXbu9ReyV6Q4kDQ5CF9wXQNDY1PdHc4HhfxRR5AHB3/go-ipfs-routing/mock" - netutil "gx/ipfs/QmYVR3C8DWPHdHxvLtNFYfjsXgaRAdh6hPMNH3KiwCgu4o/go-libp2p-netutil" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" - ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + mockrouting "gx/ipfs/QmPuPdzoG4b5uyYSQCjLEHB8NM593m3BW19UHX2jZ6Wzfm/go-ipfs-routing/mock" + routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" + testutil "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil" + floodsub "gx/ipfs/QmVKrsEgixRtMWcMd6WQzuwqCUC3jfLf7Q7xcjnKoMMikS/go-libp2p-floodsub" + netutil "gx/ipfs/Qmb6BsZf6Y3kxffXMNTubGPF1w1bkHtpvhfYbmnwP3NQyw/go-libp2p-netutil" + bhost "gx/ipfs/Qmc64U41EEB4nPG7wxjEqFwKJajS2f8kk5q2TvUrQf78Xu/go-libp2p-blankhost" + peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" + pstore "gx/ipfs/QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh/go-libp2p-peerstore" + ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + p2phost "gx/ipfs/QmfZTdmunzKzAGJrSvXXQbQ5kLLUiEMX5vdwux7iXkdk7D/go-libp2p-host" ) func newNetHost(ctx context.Context, t *testing.T) p2phost.Host { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 0aabf3738..db7ae590e 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -10,16 +10,16 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" - dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" - recpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" + recpb "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record/pb" + routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" + dshelp "gx/ipfs/QmYJgz1Z5PbBGP7n2XA8uv5sF1EKLfYUjL7kFemVAjMNqC/go-ipfs-ds-help" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" - ic "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" + ic "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 580b708de..f47df4696 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -12,9 +12,9 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmNh1kGFFdsPu79KNSaL4NUKUPb4Eiz4KHdMtFY6664RDp/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" + mocknet "gx/ipfs/QmWsV6kzPaYGBDVyuUfWBvyQygEc9Qrv9vzo8vZ7X4mdLN/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 9999801e4..39a670088 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,11 +8,11 @@ import ( path "github.com/ipfs/go-ipfs/path" - testutil "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" - mockrouting "gx/ipfs/QmXtoXbu9ReyV6Q4kDQ5CF9wXQNDY1PdHc4HhfxRR5AHB3/go-ipfs-routing/mock" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" + mockrouting "gx/ipfs/QmPuPdzoG4b5uyYSQCjLEHB8NM593m3BW19UHX2jZ6Wzfm/go-ipfs-routing/mock" + testutil "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil" + peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index ccf305066..5831e3ea6 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -10,12 +10,12 @@ import ( path "github.com/ipfs/go-ipfs/path" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" - routing "gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing" + logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" + routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" + peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) diff --git a/namesys/validator.go b/namesys/validator.go index c50da5512..cde4e92ed 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -5,11 +5,11 @@ import ( "time" pb "github.com/ipfs/go-ipfs/namesys/pb" - pstore "gx/ipfs/QmXauCuJzmzapetmC6W4TuDJLL1yFFrVzSHoWv8YdbmnxH/go-libp2p-peerstore" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" + peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" + pstore "gx/ipfs/QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh/go-libp2p-peerstore" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - record "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record" + record "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) From 65edf608a326ace0c73657ec98ccb81c1cac2f94 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 3 May 2018 21:39:52 -0700 Subject: [PATCH 2326/3817] update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@a6bfe5b679e3ea0cfb216541184fff6a007749d3 --- blockservice/blockservice.go | 4 ++-- blockservice/blockservice_test.go | 8 ++++---- blockservice/test/blocks_test.go | 8 ++++---- blockservice/test/mock.go | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index b40c66473..c913d1c58 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -11,8 +11,8 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/verifcid" - logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" - blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" + logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" + blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" exchange "gx/ipfs/QmdcAXgEHUueP4A7b5hjabKn2EooeHgMreMvFC249dGCgc/go-ipfs-exchange-interface" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index eda9497c1..af9cbe8b5 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -3,10 +3,10 @@ package blockservice import ( "testing" - offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" - blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" + offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" butil "gx/ipfs/Qmf951DP11mCoctpyF3ZppPZdo2oAxuNi2vnkVDgHJ8Fqk/go-ipfs-blocksutil" ) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 978bc8658..2ba445079 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -10,11 +10,11 @@ import ( . "github.com/ipfs/go-ipfs/blockservice" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" - blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" + offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index a9c34d42d..5fe23cb84 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -5,8 +5,8 @@ import ( bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" + mockrouting "gx/ipfs/QmPuPdzoG4b5uyYSQCjLEHB8NM593m3BW19UHX2jZ6Wzfm/go-ipfs-routing/mock" delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" - mockrouting "gx/ipfs/QmXtoXbu9ReyV6Q4kDQ5CF9wXQNDY1PdHc4HhfxRR5AHB3/go-ipfs-routing/mock" ) // Mocks returns |n| connected mock Blockservices From 85774db2adeb04f5ee6b21397203a84fae421876 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 3 May 2018 21:39:52 -0700 Subject: [PATCH 2327/3817] update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@fc412d643afb03d5e3c143074cd7d7991ac30be8 --- filestore/filestore.go | 6 +++--- filestore/filestore_test.go | 4 ++-- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 87819e831..5342cfa17 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -10,11 +10,11 @@ package filestore import ( "context" - logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" - dsq "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/query" - blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" + logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" + blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 4bb8bfa04..3a67dba1e 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -9,10 +9,10 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" + blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index dbdb8396f..c5e255c6c 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,13 +10,13 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" - dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - dsns "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/namespace" - dsq "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/query" - blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" + dshelp "gx/ipfs/QmYJgz1Z5PbBGP7n2XA8uv5sF1EKLfYUjL7kFemVAjMNqC/go-ipfs-ds-help" + blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + dsns "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/namespace" + dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) diff --git a/filestore/util.go b/filestore/util.go index ce03ba927..e09b69744 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,11 +6,11 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - dsq "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/query" - blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" + dshelp "gx/ipfs/QmYJgz1Z5PbBGP7n2XA8uv5sF1EKLfYUjL7kFemVAjMNqC/go-ipfs-ds-help" + blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) // Status is used to identify the state of the block data referenced From b5c18b01c6494492636a82a08ffe428a1b4346c9 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 3 May 2018 21:39:52 -0700 Subject: [PATCH 2328/3817] update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-path@f120e1b035b406450f6cd096c4b3598adaac21fa --- path/resolver/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 203fe9ce9..d4e058829 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -10,7 +10,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" + logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) From 68deabd4eb765afa454af92b645f87eea2e8734c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 3 May 2018 21:39:52 -0700 Subject: [PATCH 2329/3817] update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@86b9582587e2f1d74c6eacf6ccf5750f10a6ec02 --- ipld/merkledag/merkledag_test.go | 2 +- ipld/merkledag/test/utils.go | 8 ++++---- ipld/merkledag/utils/utils.go | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index bcc207d89..e5ad8ee61 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -20,7 +20,7 @@ import ( dstest "github.com/ipfs/go-ipfs/merkledag/test" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" + offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index ebba914bc..1166648f6 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -4,11 +4,11 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" dag "github.com/ipfs/go-ipfs/merkledag" - offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" - blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" + offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) // Mock returns a new thread-safe, mock DAGService. diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 4416fb15d..f2d45d8b0 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -8,11 +8,11 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - syncds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" - bstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" + offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" + bstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + syncds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) // Editor represents a ProtoNode tree editor and provides methods to From ca880060536149e4179f2d2fa07539075c736ec6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 3 May 2018 21:39:52 -0700 Subject: [PATCH 2330/3817] update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@a7fcad1350d93f711f96bfc20b2e3e4d220e1377 --- pinning/pinner/gc/gc.go | 8 ++++---- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 8 ++++---- pinning/pinner/set_test.go | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 1606a0179..95e706dbc 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -12,12 +12,12 @@ import ( pin "github.com/ipfs/go-ipfs/pin" "github.com/ipfs/go-ipfs/thirdparty/verifcid" - logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" - offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" - dstore "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - bstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" + logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" + offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" + bstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + dstore "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index ce7779883..f4024f0c0 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -12,10 +12,10 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" dutils "github.com/ipfs/go-ipfs/merkledag/utils" - logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" + logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index e65f8b63e..27963c371 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -9,11 +9,11 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" util "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" - blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" + offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) var rand = util.NewTimeSeededRand() diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index f7d6d0ede..a54dd84fc 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -8,11 +8,11 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" dag "github.com/ipfs/go-ipfs/merkledag" - offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - dsq "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/query" - blockstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" + offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) func ignoreCids(_ *cid.Cid) {} From 409ef18c182e709408cd171c8391feffd14b2636 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 3 May 2018 21:39:52 -0700 Subject: [PATCH 2331/3817] update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-keystore@432500c60b199219faf5620dda08b08175d61ef9 --- keystore/keystore.go | 4 ++-- keystore/keystore_test.go | 2 +- keystore/memkeystore.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index 5dd433852..a11b5d5f3 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -7,8 +7,8 @@ import ( "path/filepath" "strings" - logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" - ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" + ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ) var log = logging.Logger("keystore") diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index 5058964f8..f8ef62f49 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -9,7 +9,7 @@ import ( "sort" "testing" - ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" + ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ) type rr struct{} diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index 6d07f6dc3..4a525ce59 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -1,6 +1,6 @@ package keystore -import ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto" +import ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" // MemKeystore is an in memory keystore implementation that is not persisted to // any backing storage. From 28060cf31c15ceebd6b4fde229cd1fd0741f0a99 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 3 May 2018 21:39:52 -0700 Subject: [PATCH 2332/3817] update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@5c2e2438ca5cdf1747e654045e7f10c56c0d3ec6 --- unixfs/mod/dagmodifier.go | 2 +- unixfs/test/utils.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index f8a616dda..12781b219 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -14,8 +14,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - chunker "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + chunker "gx/ipfs/QmbGDSVKnYJZrtUnyxwsUpCeuigshNuVFxXCpv13jXecq1/go-ipfs-chunker" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 574b7ec97..046384679 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -15,8 +15,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - chunker "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" + chunker "gx/ipfs/QmbGDSVKnYJZrtUnyxwsUpCeuigshNuVFxXCpv13jXecq1/go-ipfs-chunker" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) From 25e387edabb92544b8a019bae7b025653dbb107f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 3 May 2018 21:39:52 -0700 Subject: [PATCH 2333/3817] update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@4a937c2edd295a80bb0adfc68ca15ae80e43332e --- mfs/file.go | 2 +- mfs/mfs_test.go | 10 +++++----- mfs/repub_test.go | 2 +- mfs/system.go | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mfs/file.go b/mfs/file.go index 11d4a2a75..3839a279d 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -9,7 +9,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - chunker "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" + chunker "gx/ipfs/QmbGDSVKnYJZrtUnyxwsUpCeuigshNuVFxXCpv13jXecq1/go-ipfs-chunker" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index f7f8c877b..0fdeed8e5 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -22,13 +22,13 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - offline "gx/ipfs/QmWM5HhdG5ZQNyHQ5XhMdGmV9CvLpFynQfGpTxN2MEM7Lc/go-ipfs-exchange-offline" - chunker "gx/ipfs/QmWo8jYc19ppG7YoTsrr2kEtLRbARTJho5oNXFTR6B7Peq/go-ipfs-chunker" - ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore" - dssync "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore/sync" - bstore "gx/ipfs/QmaG4DZ4JaqEfvPWt5nPPgoTzhc1tr1T3f4Nu9Jpdm8ymY/go-ipfs-blockstore" + offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" + bstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" + chunker "gx/ipfs/QmbGDSVKnYJZrtUnyxwsUpCeuigshNuVFxXCpv13jXecq1/go-ipfs-chunker" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) func emptyDirNode() *dag.ProtoNode { diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 15400c9a3..14eaa3001 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - ci "gx/ipfs/QmVvkK7s5imCiq3JVbL3pGfnhcCnf3LrFJPF4GE2sAoGZf/go-testutil/ci" + ci "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil/ci" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ) diff --git a/mfs/system.go b/mfs/system.go index d93af7cfd..a86ecf735 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -19,7 +19,7 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - logging "gx/ipfs/QmRb5jh8z2E8hMGN2tkvs1yHynUanqnZ3UeKwgN1i9P1F8/go-log" + logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) From a9927d7ff2dfe30c18078a293f04be844c22dbeb Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 3 May 2018 21:55:38 -0700 Subject: [PATCH 2334/3817] extract IPNS over pubsub as a ValueStore And: * Update for DHT changes. * Switch to the new record validation system. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@2c8314dee1bf1e4abaa683486382869fd87aa419 --- namesys/interface.go | 8 - namesys/ipns_validate_test.go | 78 ++----- namesys/namesys.go | 33 --- namesys/publisher.go | 2 +- namesys/publisher_test.go | 9 +- namesys/pubsub.go | 426 ---------------------------------- namesys/pubsub_test.go | 197 ---------------- namesys/routing.go | 50 +--- namesys/selector.go | 63 ----- namesys/validator.go | 139 +++++++---- 10 files changed, 127 insertions(+), 878 deletions(-) delete mode 100644 namesys/pubsub.go delete mode 100644 namesys/pubsub_test.go delete mode 100644 namesys/selector.go diff --git a/namesys/interface.go b/namesys/interface.go index fcd619b49..6536ac712 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -61,7 +61,6 @@ var ErrPublishFailed = errors.New("could not publish name") type NameSystem interface { Resolver Publisher - ResolverLookup } // Resolver is an object capable of resolving names. @@ -95,10 +94,3 @@ type Publisher interface { // call once the records spec is implemented PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error } - -// ResolverLookup is an object capable of finding resolvers for a subsystem -type ResolverLookup interface { - - // GetResolver retrieves a resolver associated with a subsystem - GetResolver(subs string) (Resolver, bool) -} diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index 44149180e..bcffcc5e6 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -12,8 +12,8 @@ import ( u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" mockrouting "gx/ipfs/QmPuPdzoG4b5uyYSQCjLEHB8NM593m3BW19UHX2jZ6Wzfm/go-ipfs-routing/mock" record "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record" - recordpb "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record/pb" routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" + ropts "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing/options" testutil "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" @@ -23,8 +23,10 @@ import ( dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) -func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, ns string, key string, val []byte, eol time.Time, exp error) { - validChecker := NewIpnsRecordValidator(kbook) +func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, key string, val []byte, eol time.Time, exp error) { + t.Helper() + + validator := IpnsValidator{kbook} p := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") entry, err := CreateRoutingEntryData(priv, p, 1, eol) @@ -39,15 +41,9 @@ func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, ns s t.Fatal(err) } } - rec := &record.ValidationRecord{ - Namespace: ns, - Key: key, - Value: data, - } - - err = validChecker.Func(rec) + err = validator.Validate(key, data) if err != exp { - params := fmt.Sprintf("namespace: %s\nkey: %s\neol: %s\n", ns, key, eol) + params := fmt.Sprintf("key: %s\neol: %s\n", key, eol) if exp == nil { t.Fatalf("Unexpected error %s for params %s", err, params) } else if err == nil { @@ -67,15 +63,15 @@ func TestValidator(t *testing.T) { kbook.AddPubKey(id, priv.GetPublic()) emptyKbook := pstore.NewPeerstore() - testValidatorCase(t, priv, kbook, "ipns", string(id), nil, ts.Add(time.Hour), nil) - testValidatorCase(t, priv, kbook, "ipns", string(id), nil, ts.Add(time.Hour*-1), ErrExpiredRecord) - testValidatorCase(t, priv, kbook, "ipns", string(id), []byte("bad data"), ts.Add(time.Hour), ErrBadRecord) - testValidatorCase(t, priv, kbook, "ipns", "bad key", nil, ts.Add(time.Hour), ErrKeyFormat) - testValidatorCase(t, priv, emptyKbook, "ipns", string(id), nil, ts.Add(time.Hour), ErrPublicKeyNotFound) - testValidatorCase(t, priv2, kbook, "ipns", string(id2), nil, ts.Add(time.Hour), ErrPublicKeyNotFound) - testValidatorCase(t, priv2, kbook, "ipns", string(id), nil, ts.Add(time.Hour), ErrSignature) - testValidatorCase(t, priv, kbook, "", string(id), nil, ts.Add(time.Hour), ErrInvalidPath) - testValidatorCase(t, priv, kbook, "wrong", string(id), nil, ts.Add(time.Hour), ErrInvalidPath) + testValidatorCase(t, priv, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour), nil) + testValidatorCase(t, priv, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour*-1), ErrExpiredRecord) + testValidatorCase(t, priv, kbook, "/ipns/"+string(id), []byte("bad data"), ts.Add(time.Hour), ErrBadRecord) + testValidatorCase(t, priv, kbook, "/ipns/"+"bad key", nil, ts.Add(time.Hour), ErrKeyFormat) + testValidatorCase(t, priv, emptyKbook, "/ipns/"+string(id), nil, ts.Add(time.Hour), ErrPublicKeyNotFound) + testValidatorCase(t, priv2, kbook, "/ipns/"+string(id2), nil, ts.Add(time.Hour), ErrPublicKeyNotFound) + testValidatorCase(t, priv2, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour), ErrSignature) + testValidatorCase(t, priv, kbook, "//"+string(id), nil, ts.Add(time.Hour), ErrInvalidPath) + testValidatorCase(t, priv, kbook, "/wrong/"+string(id), nil, ts.Add(time.Hour), ErrInvalidPath) } func TestResolverValidation(t *testing.T) { @@ -85,13 +81,6 @@ func TestResolverValidation(t *testing.T) { peerstore := pstore.NewPeerstore() vstore := newMockValueStore(rid, dstore, peerstore) - vstore.Validator["ipns"] = NewIpnsRecordValidator(peerstore) - vstore.Validator["pk"] = &record.ValidChecker{ - Func: func(r *record.ValidationRecord) error { - return nil - }, - Sign: false, - } resolver := NewRoutingResolver(vstore, 0) // Create entry with expiry in one hour @@ -224,19 +213,19 @@ type mockValueStore struct { func newMockValueStore(id testutil.Identity, dstore ds.Datastore, kbook pstore.KeyBook) *mockValueStore { serv := mockrouting.NewServer() r := serv.ClientWithDatastore(context.Background(), id, dstore) - return &mockValueStore{r, kbook, make(record.Validator)} + return &mockValueStore{r, kbook, record.NamespacedValidator{ + "ipns": IpnsValidator{kbook}, + "pk": record.PublicKeyValidator{}, + }} } -func (m *mockValueStore) GetValue(ctx context.Context, k string) ([]byte, error) { - data, err := m.r.GetValue(ctx, k) +func (m *mockValueStore) GetValue(ctx context.Context, k string, opts ...ropts.Option) ([]byte, error) { + data, err := m.r.GetValue(ctx, k, opts...) if err != nil { return data, err } - rec := new(recordpb.Record) - rec.Key = proto.String(k) - rec.Value = data - if err = m.Validator.VerifyRecord(rec); err != nil { + if err = m.Validator.Validate(k, data); err != nil { return nil, err } @@ -263,23 +252,6 @@ func (m *mockValueStore) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey return pk, m.kbook.AddPubKey(p, pk) } -func (m *mockValueStore) GetValues(ctx context.Context, k string, count int) ([]routing.RecvdVal, error) { - vals, err := m.r.GetValues(ctx, k, count) - if err != nil { - return nil, err - } - valid := make([]routing.RecvdVal, 0, len(vals)) - for _, v := range vals { - rec := new(recordpb.Record) - rec.Key = proto.String(k) - rec.Value = v.Val - if err = m.Validator.VerifyRecord(rec); err == nil { - valid = append(valid, v) - } - } - return valid, nil -} - -func (m *mockValueStore) PutValue(ctx context.Context, k string, d []byte) error { - return m.r.PutValue(ctx, k, d) +func (m *mockValueStore) PutValue(ctx context.Context, k string, d []byte, opts ...ropts.Option) error { + return m.r.PutValue(ctx, k, d, opts...) } diff --git a/namesys/namesys.go b/namesys/namesys.go index 47e1f874f..6e37d1c6f 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -2,7 +2,6 @@ package namesys import ( "context" - "errors" "strings" "sync" "time" @@ -11,13 +10,11 @@ import ( path "github.com/ipfs/go-ipfs/path" routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" - floodsub "gx/ipfs/QmVKrsEgixRtMWcMd6WQzuwqCUC3jfLf7Q7xcjnKoMMikS/go-libp2p-floodsub" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - p2phost "gx/ipfs/QmfZTdmunzKzAGJrSvXXQbQ5kLLUiEMX5vdwux7iXkdk7D/go-libp2p-host" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. @@ -48,23 +45,6 @@ func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSys } } -// AddPubsubNameSystem adds the pubsub publisher and resolver to the namesystem -func AddPubsubNameSystem(ctx context.Context, ns NameSystem, host p2phost.Host, r routing.IpfsRouting, ds ds.Datastore, ps *floodsub.PubSub) error { - mpns, ok := ns.(*mpns) - if !ok { - return errors.New("unexpected NameSystem; not an mpns instance") - } - - pkf, ok := r.(routing.PubKeyFetcher) - if !ok { - return errors.New("unexpected IpfsRouting; not a PubKeyFetcher instance") - } - - mpns.resolvers["pubsub"] = NewPubsubResolver(ctx, host, r, pkf, ps) - mpns.publishers["pubsub"] = NewPubsubPublisher(ctx, host, ds, r, ps) - return nil -} - const DefaultResolverCacheTTL = time.Minute // Resolve implements Resolver. @@ -219,16 +199,3 @@ func (ns *mpns) addToDHTCache(key ci.PrivKey, value path.Path, eol time.Time) { eol: eol, }) } - -// GetResolver implements ResolverLookup -func (ns *mpns) GetResolver(subs string) (Resolver, bool) { - res, ok := ns.resolvers[subs] - if ok { - ires, ok := res.(Resolver) - if ok { - return ires, true - } - } - - return nil, false -} diff --git a/namesys/publisher.go b/namesys/publisher.go index d75a4e5cf..2e470d66b 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -142,7 +142,7 @@ func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqn errs := make(chan error, 2) // At most two errors (IPNS, and public key) // Attempt to extract the public key from the ID - extractedPublicKey := id.ExtractPublicKey() + extractedPublicKey, _ := id.ExtractPublicKey() go func() { errs <- PublishEntry(ctx, r, ipnskey, entry) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index e4ad1fa69..39a975332 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -49,14 +49,7 @@ func testNamekeyPublisher(t *testing.T, keyType int, expectedErr error, expected } // ID - var id peer.ID - switch keyType { - case ci.Ed25519: - id, err = peer.IDFromEd25519PublicKey(pubKey) - default: - id, err = peer.IDFromPublicKey(pubKey) - } - + id, err := peer.IDFromPublicKey(pubKey) if err != nil { t.Fatal(err) } diff --git a/namesys/pubsub.go b/namesys/pubsub.go deleted file mode 100644 index ba4a73f66..000000000 --- a/namesys/pubsub.go +++ /dev/null @@ -1,426 +0,0 @@ -package namesys - -import ( - "context" - "errors" - "fmt" - "strings" - "sync" - "time" - - opts "github.com/ipfs/go-ipfs/namesys/opts" - pb "github.com/ipfs/go-ipfs/namesys/pb" - path "github.com/ipfs/go-ipfs/path" - - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - record "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record" - dhtpb "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record/pb" - routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" - floodsub "gx/ipfs/QmVKrsEgixRtMWcMd6WQzuwqCUC3jfLf7Q7xcjnKoMMikS/go-libp2p-floodsub" - dshelp "gx/ipfs/QmYJgz1Z5PbBGP7n2XA8uv5sF1EKLfYUjL7kFemVAjMNqC/go-ipfs-ds-help" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" - peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - pstore "gx/ipfs/QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh/go-libp2p-peerstore" - ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" - p2phost "gx/ipfs/QmfZTdmunzKzAGJrSvXXQbQ5kLLUiEMX5vdwux7iXkdk7D/go-libp2p-host" -) - -// PubsubPublisher is a publisher that distributes IPNS records through pubsub -type PubsubPublisher struct { - ctx context.Context - ds ds.Datastore - host p2phost.Host - cr routing.ContentRouting - ps *floodsub.PubSub - - mx sync.Mutex - subs map[string]struct{} -} - -// PubsubResolver is a resolver that receives IPNS records through pubsub -type PubsubResolver struct { - ctx context.Context - ds ds.Datastore - host p2phost.Host - cr routing.ContentRouting - pkf routing.PubKeyFetcher - ps *floodsub.PubSub - - mx sync.Mutex - subs map[string]*floodsub.Subscription -} - -// NewPubsubPublisher constructs a new Publisher that publishes IPNS records through pubsub. -// The constructor interface is complicated by the need to bootstrap the pubsub topic. -// This could be greatly simplified if the pubsub implementation handled bootstrap itself -func NewPubsubPublisher(ctx context.Context, host p2phost.Host, ds ds.Datastore, cr routing.ContentRouting, ps *floodsub.PubSub) *PubsubPublisher { - return &PubsubPublisher{ - ctx: ctx, - ds: ds, - host: host, // needed for pubsub bootstrap - cr: cr, // needed for pubsub bootstrap - ps: ps, - subs: make(map[string]struct{}), - } -} - -// NewPubsubResolver constructs a new Resolver that resolves IPNS records through pubsub. -// same as above for pubsub bootstrap dependencies -func NewPubsubResolver(ctx context.Context, host p2phost.Host, cr routing.ContentRouting, pkf routing.PubKeyFetcher, ps *floodsub.PubSub) *PubsubResolver { - return &PubsubResolver{ - ctx: ctx, - ds: dssync.MutexWrap(ds.NewMapDatastore()), - host: host, // needed for pubsub bootstrap - cr: cr, // needed for pubsub bootstrap - pkf: pkf, - ps: ps, - subs: make(map[string]*floodsub.Subscription), - } -} - -// Publish publishes an IPNS record through pubsub with default TTL -func (p *PubsubPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error { - return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordTTL)) -} - -// PublishWithEOL publishes an IPNS record through pubsub -func (p *PubsubPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error { - id, err := peer.IDFromPrivateKey(k) - if err != nil { - return err - } - - _, ipnskey := IpnsKeysForID(id) - - seqno, err := p.getPreviousSeqNo(ctx, ipnskey) - if err != nil { - return err - } - - seqno++ - - return p.publishRecord(ctx, k, value, seqno, eol, ipnskey, id) -} - -func (p *PubsubPublisher) getPreviousSeqNo(ctx context.Context, ipnskey string) (uint64, error) { - // the datastore is shared with the routing publisher to properly increment and persist - // ipns record sequence numbers. - prevrec, err := p.ds.Get(dshelp.NewKeyFromBinary([]byte(ipnskey))) - if err != nil { - if err == ds.ErrNotFound { - // None found, lets start at zero! - return 0, nil - } - return 0, err - } - - prbytes, ok := prevrec.([]byte) - if !ok { - return 0, fmt.Errorf("unexpected type returned from datastore: %#v", prevrec) - } - - var dsrec dhtpb.Record - err = proto.Unmarshal(prbytes, &dsrec) - if err != nil { - return 0, err - } - - var entry pb.IpnsEntry - err = proto.Unmarshal(dsrec.GetValue(), &entry) - if err != nil { - return 0, err - } - - return entry.GetSequence(), nil -} - -func (p *PubsubPublisher) publishRecord(ctx context.Context, k ci.PrivKey, value path.Path, seqno uint64, eol time.Time, ipnskey string, ID peer.ID) error { - entry, err := CreateRoutingEntryData(k, value, seqno, eol) - if err != nil { - return err - } - - data, err := proto.Marshal(entry) - if err != nil { - return err - } - - // the datastore is shared with the routing publisher to properly increment and persist - // ipns record sequence numbers; so we need to Record our new entry in the datastore - dsrec, err := record.MakePutRecord(k, ipnskey, data, true) - if err != nil { - return err - } - - dsdata, err := proto.Marshal(dsrec) - if err != nil { - return err - } - - err = p.ds.Put(dshelp.NewKeyFromBinary([]byte(ipnskey)), dsdata) - if err != nil { - return err - } - - // now we publish, but we also need to bootstrap pubsub for our messages to propagate - topic := "/ipns/" + ID.Pretty() - - p.mx.Lock() - _, ok := p.subs[topic] - - if !ok { - p.subs[topic] = struct{}{} - p.mx.Unlock() - - bootstrapPubsub(p.ctx, p.cr, p.host, topic) - } else { - p.mx.Unlock() - } - - log.Debugf("PubsubPublish: publish IPNS record for %s (%d)", topic, seqno) - return p.ps.Publish(topic, data) -} - -// Resolve resolves a name through pubsub and default depth limit -func (r *PubsubResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) { - return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/") -} - -func (r *PubsubResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) { - log.Debugf("PubsubResolve: resolve '%s'", name) - - // retrieve the public key once (for verifying messages) - xname := strings.TrimPrefix(name, "/ipns/") - hash, err := mh.FromB58String(xname) - if err != nil { - log.Warningf("PubsubResolve: bad input hash: [%s]", xname) - return "", err - } - - id := peer.ID(hash) - if r.host.Peerstore().PrivKey(id) != nil { - return "", errors.New("cannot resolve own name through pubsub") - } - - pubk := id.ExtractPublicKey() - if pubk == nil { - pubk, err = r.pkf.GetPublicKey(ctx, id) - if err != nil { - log.Warningf("PubsubResolve: error fetching public key: %s [%s]", err.Error(), xname) - return "", err - } - } - - // the topic is /ipns/Qmhash - if !strings.HasPrefix(name, "/ipns/") { - name = "/ipns/" + name - } - - r.mx.Lock() - // see if we already have a pubsub subscription; if not, subscribe - sub, ok := r.subs[name] - if !ok { - sub, err = r.ps.Subscribe(name) - if err != nil { - r.mx.Unlock() - return "", err - } - - log.Debugf("PubsubResolve: subscribed to %s", name) - - r.subs[name] = sub - - ctx, cancel := context.WithCancel(r.ctx) - go r.handleSubscription(sub, name, pubk, cancel) - go bootstrapPubsub(ctx, r.cr, r.host, name) - } - r.mx.Unlock() - - // resolve to what we may already have in the datastore - dsval, err := r.ds.Get(dshelp.NewKeyFromBinary([]byte(name))) - if err != nil { - if err == ds.ErrNotFound { - return "", ErrResolveFailed - } - return "", err - } - - data := dsval.([]byte) - entry := new(pb.IpnsEntry) - - err = proto.Unmarshal(data, entry) - if err != nil { - return "", err - } - - // check EOL; if the entry has expired, delete from datastore and return ds.ErrNotFound - eol, ok := checkEOL(entry) - if ok && eol.Before(time.Now()) { - err = r.ds.Delete(dshelp.NewKeyFromBinary([]byte(name))) - if err != nil { - log.Warningf("PubsubResolve: error deleting stale value for %s: %s", name, err.Error()) - } - - return "", ErrResolveFailed - } - - value, err := path.ParsePath(string(entry.GetValue())) - return value, err -} - -// GetSubscriptions retrieves a list of active topic subscriptions -func (r *PubsubResolver) GetSubscriptions() []string { - r.mx.Lock() - defer r.mx.Unlock() - - var res []string - for sub := range r.subs { - res = append(res, sub) - } - - return res -} - -// Cancel cancels a topic subscription; returns true if an active -// subscription was canceled -func (r *PubsubResolver) Cancel(name string) bool { - r.mx.Lock() - defer r.mx.Unlock() - - sub, ok := r.subs[name] - if ok { - sub.Cancel() - delete(r.subs, name) - } - - return ok -} - -func (r *PubsubResolver) handleSubscription(sub *floodsub.Subscription, name string, pubk ci.PubKey, cancel func()) { - defer sub.Cancel() - defer cancel() - - for { - msg, err := sub.Next(r.ctx) - if err != nil { - if err != context.Canceled { - log.Warningf("PubsubResolve: subscription error in %s: %s", name, err.Error()) - } - return - } - - err = r.receive(msg, name, pubk) - if err != nil { - log.Warningf("PubsubResolve: error processing update for %s: %s", name, err.Error()) - } - } -} - -func (r *PubsubResolver) receive(msg *floodsub.Message, name string, pubk ci.PubKey) error { - data := msg.GetData() - if data == nil { - return errors.New("empty message") - } - - entry := new(pb.IpnsEntry) - err := proto.Unmarshal(data, entry) - if err != nil { - return err - } - - ok, err := pubk.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()) - if err != nil || !ok { - return errors.New("signature verification failed") - } - - _, err = path.ParsePath(string(entry.GetValue())) - if err != nil { - return err - } - - eol, ok := checkEOL(entry) - if ok && eol.Before(time.Now()) { - return errors.New("stale update; EOL exceeded") - } - - // check the sequence number against what we may already have in our datastore - oval, err := r.ds.Get(dshelp.NewKeyFromBinary([]byte(name))) - if err == nil { - odata := oval.([]byte) - oentry := new(pb.IpnsEntry) - - err = proto.Unmarshal(odata, oentry) - if err != nil { - return err - } - - if entry.GetSequence() <= oentry.GetSequence() { - return errors.New("stale update; sequence number too small") - } - } - - log.Debugf("PubsubResolve: receive IPNS record for %s", name) - - return r.ds.Put(dshelp.NewKeyFromBinary([]byte(name)), data) -} - -// rendezvous with peers in the name topic through provider records -// Note: rendezvous/boostrap should really be handled by the pubsub implementation itself! -func bootstrapPubsub(ctx context.Context, cr routing.ContentRouting, host p2phost.Host, name string) { - topic := "floodsub:" + name - hash := u.Hash([]byte(topic)) - rz := cid.NewCidV1(cid.Raw, hash) - - err := cr.Provide(ctx, rz, true) - if err != nil { - log.Warningf("bootstrapPubsub: error providing rendezvous for %s: %s", topic, err.Error()) - } - - go func() { - for { - select { - case <-time.After(8 * time.Hour): - err := cr.Provide(ctx, rz, true) - if err != nil { - log.Warningf("bootstrapPubsub: error providing rendezvous for %s: %s", topic, err.Error()) - } - case <-ctx.Done(): - return - } - } - }() - - rzctx, cancel := context.WithTimeout(ctx, time.Second*10) - defer cancel() - - wg := &sync.WaitGroup{} - for pi := range cr.FindProvidersAsync(rzctx, rz, 10) { - if pi.ID == host.ID() { - continue - } - wg.Add(1) - go func(pi pstore.PeerInfo) { - defer wg.Done() - - ctx, cancel := context.WithTimeout(ctx, time.Second*10) - defer cancel() - - err := host.Connect(ctx, pi) - if err != nil { - log.Debugf("Error connecting to pubsub peer %s: %s", pi.ID, err.Error()) - return - } - - // delay to let pubsub perform its handshake - time.Sleep(time.Millisecond * 250) - - log.Debugf("Connected to pubsub peer %s", pi.ID) - }(pi) - } - - wg.Wait() -} diff --git a/namesys/pubsub_test.go b/namesys/pubsub_test.go deleted file mode 100644 index 46e414b9d..000000000 --- a/namesys/pubsub_test.go +++ /dev/null @@ -1,197 +0,0 @@ -package namesys - -import ( - "context" - "sync" - "testing" - "time" - - path "github.com/ipfs/go-ipfs/path" - - mockrouting "gx/ipfs/QmPuPdzoG4b5uyYSQCjLEHB8NM593m3BW19UHX2jZ6Wzfm/go-ipfs-routing/mock" - routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" - testutil "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil" - floodsub "gx/ipfs/QmVKrsEgixRtMWcMd6WQzuwqCUC3jfLf7Q7xcjnKoMMikS/go-libp2p-floodsub" - netutil "gx/ipfs/Qmb6BsZf6Y3kxffXMNTubGPF1w1bkHtpvhfYbmnwP3NQyw/go-libp2p-netutil" - bhost "gx/ipfs/Qmc64U41EEB4nPG7wxjEqFwKJajS2f8kk5q2TvUrQf78Xu/go-libp2p-blankhost" - peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" - pstore "gx/ipfs/QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh/go-libp2p-peerstore" - ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - p2phost "gx/ipfs/QmfZTdmunzKzAGJrSvXXQbQ5kLLUiEMX5vdwux7iXkdk7D/go-libp2p-host" -) - -func newNetHost(ctx context.Context, t *testing.T) p2phost.Host { - netw := netutil.GenSwarmNetwork(t, ctx) - return bhost.NewBlankHost(netw) -} - -func newNetHosts(ctx context.Context, t *testing.T, n int) []p2phost.Host { - var out []p2phost.Host - - for i := 0; i < n; i++ { - h := newNetHost(ctx, t) - out = append(out, h) - } - - return out -} - -// PubKeyFetcher implementation with a global key store -type mockKeyStore struct { - keys map[peer.ID]ci.PubKey - mx sync.Mutex -} - -func (m *mockKeyStore) addPubKey(id peer.ID, pkey ci.PubKey) { - m.mx.Lock() - defer m.mx.Unlock() - m.keys[id] = pkey -} - -func (m *mockKeyStore) getPubKey(id peer.ID) (ci.PubKey, error) { - m.mx.Lock() - defer m.mx.Unlock() - pkey, ok := m.keys[id] - if ok { - return pkey, nil - } - - return nil, routing.ErrNotFound -} - -func (m *mockKeyStore) GetPublicKey(ctx context.Context, id peer.ID) (ci.PubKey, error) { - return m.getPubKey(id) -} - -func newMockKeyStore() *mockKeyStore { - return &mockKeyStore{ - keys: make(map[peer.ID]ci.PubKey), - } -} - -// ConentRouting mock -func newMockRouting(ms mockrouting.Server, ks *mockKeyStore, host p2phost.Host) routing.ContentRouting { - id := host.ID() - - privk := host.Peerstore().PrivKey(id) - pubk := host.Peerstore().PubKey(id) - pi := host.Peerstore().PeerInfo(id) - - ks.addPubKey(id, pubk) - return ms.Client(testutil.NewIdentity(id, pi.Addrs[0], privk, pubk)) -} - -func newMockRoutingForHosts(ms mockrouting.Server, ks *mockKeyStore, hosts []p2phost.Host) []routing.ContentRouting { - rs := make([]routing.ContentRouting, len(hosts)) - for i := 0; i < len(hosts); i++ { - rs[i] = newMockRouting(ms, ks, hosts[i]) - } - return rs -} - -// tests -func TestPubsubPublishSubscribe(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - ms := mockrouting.NewServer() - ks := newMockKeyStore() - - pubhost := newNetHost(ctx, t) - pubmr := newMockRouting(ms, ks, pubhost) - fs, err := floodsub.NewFloodSub(ctx, pubhost) - if err != nil { - t.Fatal(err) - } - pub := NewPubsubPublisher(ctx, pubhost, ds.NewMapDatastore(), pubmr, fs) - privk := pubhost.Peerstore().PrivKey(pubhost.ID()) - pubpinfo := pstore.PeerInfo{ID: pubhost.ID(), Addrs: pubhost.Addrs()} - - name := "/ipns/" + pubhost.ID().Pretty() - - reshosts := newNetHosts(ctx, t, 5) - resmrs := newMockRoutingForHosts(ms, ks, reshosts) - res := make([]*PubsubResolver, len(reshosts)) - for i := 0; i < len(res); i++ { - - fs, err := floodsub.NewFloodSub(ctx, reshosts[i]) - if err != nil { - t.Fatal(err) - } - - res[i] = NewPubsubResolver(ctx, reshosts[i], resmrs[i], ks, fs) - if err := reshosts[i].Connect(ctx, pubpinfo); err != nil { - t.Fatal(err) - } - } - - time.Sleep(time.Millisecond * 100) - for i := 0; i < len(res); i++ { - checkResolveNotFound(ctx, t, i, res[i], name) - // delay to avoid connection storms - time.Sleep(time.Millisecond * 100) - } - - // let the bootstrap finish - time.Sleep(time.Second * 1) - - val := path.Path("/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY") - err = pub.Publish(ctx, privk, val) - if err != nil { - t.Fatal(err) - } - - // let the flood propagate - time.Sleep(time.Second * 1) - for i := 0; i < len(res); i++ { - checkResolve(ctx, t, i, res[i], name, val) - } - - val = path.Path("/ipfs/QmP1wMAqk6aZYRZirbaAwmrNeqFRgQrwBt3orUtvSa1UYD") - err = pub.Publish(ctx, privk, val) - if err != nil { - t.Fatal(err) - } - - // let the flood propagate - time.Sleep(time.Second * 1) - for i := 0; i < len(res); i++ { - checkResolve(ctx, t, i, res[i], name, val) - } - - // cancel subscriptions - for i := 0; i < len(res); i++ { - res[i].Cancel(name) - } - time.Sleep(time.Millisecond * 100) - - nval := path.Path("/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr") - err = pub.Publish(ctx, privk, nval) - if err != nil { - t.Fatal(err) - } - - // check we still have the old value in the resolver - time.Sleep(time.Second * 1) - for i := 0; i < len(res); i++ { - checkResolve(ctx, t, i, res[i], name, val) - } -} - -func checkResolveNotFound(ctx context.Context, t *testing.T, i int, resolver Resolver, name string) { - _, err := resolver.Resolve(ctx, name) - if err != ErrResolveFailed { - t.Fatalf("[resolver %d] unexpected error: %s", i, err.Error()) - } -} - -func checkResolve(ctx context.Context, t *testing.T, i int, resolver Resolver, name string, val path.Path) { - xval, err := resolver.Resolve(ctx, name) - if err != nil { - t.Fatalf("[resolver %d] resolve failed: %s", i, err.Error()) - } - if xval != val { - t.Fatalf("[resolver %d] unexpected value: %s %s", i, val, xval) - } -} diff --git a/namesys/routing.go b/namesys/routing.go index 5831e3ea6..73670145e 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -17,6 +17,7 @@ import ( mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + dht "gx/ipfs/Qmd3jqhBQFvhfBNTSJMQL15GgyVMpdxKTta69Napvx6Myd/go-libp2p-kad-dht" ) var log = logging.Logger("namesys") @@ -133,28 +134,28 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, options return "", err } + pid, err := peer.IDFromBytes(hash) + if err != nil { + log.Debugf("RoutingResolver: could not convert public key hash %s to peer ID: %s\n", name, err) + return "", err + } + // Name should be the hash of a public key retrievable from ipfs. // We retrieve the public key here to make certain that it's in the peer // store before calling GetValue() on the DHT - the DHT will call the // ipns validator, which in turn will get the public key from the peer // store to verify the record signature - _, err = routing.GetPublicKey(r.routing, ctx, hash) + _, err = routing.GetPublicKey(r.routing, ctx, pid) if err != nil { log.Debugf("RoutingResolver: could not retrieve public key %s: %s\n", name, err) return "", err } - pid, err := peer.IDFromBytes(hash) - if err != nil { - log.Debugf("RoutingResolver: could not convert public key hash %s to peer ID: %s\n", name, err) - return "", err - } - // Use the routing system to get the name. // Note that the DHT will call the ipns validator when retrieving // the value, which in turn verifies the ipns record signature _, ipnsKey := IpnsKeysForID(pid) - val, err := r.getValue(ctx, ipnsKey, options) + val, err := r.routing.GetValue(ctx, ipnsKey, dht.Quorum(int(options.DhtRecordCount))) if err != nil { log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err) return "", err @@ -187,39 +188,6 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, options } } -func (r *routingResolver) getValue(ctx context.Context, ipnsKey string, options *opts.ResolveOpts) ([]byte, error) { - // Get specified number of values from the DHT - vals, err := r.routing.GetValues(ctx, ipnsKey, int(options.DhtRecordCount)) - if err != nil { - return nil, err - } - - // Select the best value - recs := make([][]byte, 0, len(vals)) - for _, v := range vals { - if v.Val != nil { - recs = append(recs, v.Val) - } - } - - if len(recs) == 0 { - return nil, routing.ErrNotFound - } - - i, err := IpnsSelectorFunc(ipnsKey, recs) - if err != nil { - return nil, err - } - - best := recs[i] - if best == nil { - log.Errorf("GetValues %s yielded record with nil value", ipnsKey) - return nil, routing.ErrNotFound - } - - return best, nil -} - func checkEOL(e *pb.IpnsEntry) (time.Time, bool) { if e.GetValidityType() == pb.IpnsEntry_EOL { eol, err := u.ParseRFC3339(string(e.GetValidity())) diff --git a/namesys/selector.go b/namesys/selector.go deleted file mode 100644 index aebfb1533..000000000 --- a/namesys/selector.go +++ /dev/null @@ -1,63 +0,0 @@ -package namesys - -import ( - "bytes" - "errors" - - pb "github.com/ipfs/go-ipfs/namesys/pb" - - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" -) - -// IpnsSelectorFunc selects the best record by checking which has the highest -// sequence number and latest EOL -func IpnsSelectorFunc(k string, vals [][]byte) (int, error) { - var recs []*pb.IpnsEntry - for _, v := range vals { - e := new(pb.IpnsEntry) - err := proto.Unmarshal(v, e) - if err == nil { - recs = append(recs, e) - } else { - recs = append(recs, nil) - } - } - - return selectRecord(recs, vals) -} - -func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) { - var bestSeq uint64 - besti := -1 - - for i, r := range recs { - if r == nil || r.GetSequence() < bestSeq { - continue - } - rt, err := u.ParseRFC3339(string(r.GetValidity())) - if err != nil { - log.Errorf("failed to parse ipns record EOL %s", r.GetValidity()) - continue - } - - if besti == -1 || r.GetSequence() > bestSeq { - bestSeq = r.GetSequence() - besti = i - } else if r.GetSequence() == bestSeq { - bestt, _ := u.ParseRFC3339(string(recs[besti].GetValidity())) - if rt.After(bestt) { - besti = i - } else if rt == bestt { - if bytes.Compare(vals[i], vals[besti]) > 0 { - besti = i - } - } - } - } - if besti == -1 { - return 0, errors.New("no usable records in given set") - } - - return besti, nil -} diff --git a/namesys/validator.go b/namesys/validator.go index cde4e92ed..941d6a667 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -1,6 +1,7 @@ package namesys import ( + "bytes" "errors" "time" @@ -41,64 +42,106 @@ var ErrKeyFormat = errors.New("record key could not be parsed into peer ID") // from the peer store var ErrPublicKeyNotFound = errors.New("public key not found in peer store") -// NewIpnsRecordValidator returns a ValidChecker for IPNS records. -// The validator function will get a public key from the KeyBook -// to verify the record's signature. Note that the public key must -// already have been fetched from the network and put into the KeyBook -// by the caller. -func NewIpnsRecordValidator(kbook pstore.KeyBook) *record.ValidChecker { - // ValidateIpnsRecord implements ValidatorFunc and verifies that the - // given record's value is an IpnsEntry, that the entry has been correctly - // signed, and that the entry has not expired - ValidateIpnsRecord := func(r *record.ValidationRecord) error { - if r.Namespace != "ipns" { - return ErrInvalidPath - } +type IpnsValidator struct { + KeyBook pstore.KeyBook +} - // Parse the value into an IpnsEntry - entry := new(pb.IpnsEntry) - err := proto.Unmarshal(r.Value, entry) - if err != nil { - return ErrBadRecord - } +func (v IpnsValidator) Validate(key string, value []byte) error { + ns, pidString, err := record.SplitKey(key) + if err != nil || ns != "ipns" { + return ErrInvalidPath + } + + // Parse the value into an IpnsEntry + entry := new(pb.IpnsEntry) + err = proto.Unmarshal(value, entry) + if err != nil { + return ErrBadRecord + } - // Get the public key defined by the ipns path - pid, err := peer.IDFromString(r.Key) + // Get the public key defined by the ipns path + pid, err := peer.IDFromString(pidString) + if err != nil { + log.Debugf("failed to parse ipns record key %s into peer ID", pidString) + return ErrKeyFormat + } + pubk := v.KeyBook.PubKey(pid) + if pubk == nil { + log.Debugf("public key with hash %s not found in peer store", pid) + return ErrPublicKeyNotFound + } + + // Check the ipns record signature with the public key + if ok, err := pubk.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok { + log.Debugf("failed to verify signature for ipns record %s", pidString) + return ErrSignature + } + + // Check that record has not expired + switch entry.GetValidityType() { + case pb.IpnsEntry_EOL: + t, err := u.ParseRFC3339(string(entry.GetValidity())) if err != nil { - log.Debugf("failed to parse ipns record key %s into peer ID", r.Key) - return ErrKeyFormat + log.Debugf("failed parsing time for ipns record EOL in record %s", pidString) + return err } - pubk := kbook.PubKey(pid) - if pubk == nil { - log.Debugf("public key with hash %s not found in peer store", pid) - return ErrPublicKeyNotFound + if time.Now().After(t) { + return ErrExpiredRecord } + default: + return ErrUnrecognizedValidity + } + return nil +} - // Check the ipns record signature with the public key - if ok, err := pubk.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok { - log.Debugf("failed to verify signature for ipns record %s", r.Key) - return ErrSignature +// IpnsSelectorFunc selects the best record by checking which has the highest +// sequence number and latest EOL +func (v IpnsValidator) Select(k string, vals [][]byte) (int, error) { + var recs []*pb.IpnsEntry + for _, v := range vals { + e := new(pb.IpnsEntry) + err := proto.Unmarshal(v, e) + if err == nil { + recs = append(recs, e) + } else { + recs = append(recs, nil) } + } - // Check that record has not expired - switch entry.GetValidityType() { - case pb.IpnsEntry_EOL: - t, err := u.ParseRFC3339(string(entry.GetValidity())) - if err != nil { - log.Debugf("failed parsing time for ipns record EOL in record %s", r.Key) - return err - } - if time.Now().After(t) { - return ErrExpiredRecord + return selectRecord(recs, vals) +} + +func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) { + var bestSeq uint64 + besti := -1 + + for i, r := range recs { + if r == nil || r.GetSequence() < bestSeq { + continue + } + rt, err := u.ParseRFC3339(string(r.GetValidity())) + if err != nil { + log.Errorf("failed to parse ipns record EOL %s", r.GetValidity()) + continue + } + + if besti == -1 || r.GetSequence() > bestSeq { + bestSeq = r.GetSequence() + besti = i + } else if r.GetSequence() == bestSeq { + bestt, _ := u.ParseRFC3339(string(recs[besti].GetValidity())) + if rt.After(bestt) { + besti = i + } else if rt == bestt { + if bytes.Compare(vals[i], vals[besti]) > 0 { + besti = i + } } - default: - return ErrUnrecognizedValidity } - return nil } - - return &record.ValidChecker{ - Func: ValidateIpnsRecord, - Sign: false, + if besti == -1 { + return 0, errors.New("no usable records in given set") } + + return besti, nil } From 5850aa046795eb5fa1285db51e01f237334540e0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 7 May 2018 21:34:36 -0700 Subject: [PATCH 2335/3817] simplify routing resolution a bit License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@dcf4e313f9a0352de3d36cdd4e8f8aef4c713514 --- namesys/routing.go | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/namesys/routing.go b/namesys/routing.go index 73670145e..4a1668c31 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -168,24 +168,22 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, options return "", err } + var p path.Path // check for old style record: - valh, err := mh.Cast(entry.GetValue()) - if err != nil { + if valh, err := mh.Cast(entry.GetValue()); err == nil { + // Its an old style multihash record + log.Debugf("encountered CIDv0 ipns entry: %s", valh) + p = path.FromCid(cid.NewCidV0(valh)) + } else { // Not a multihash, probably a new record - p, err := path.ParsePath(string(entry.GetValue())) + p, err = path.ParsePath(string(entry.GetValue())) if err != nil { return "", err } - - r.cacheSet(name, p, entry) - return p, nil - } else { - // Its an old style multihash record - log.Debugf("encountered CIDv0 ipns entry: %s", valh) - p := path.FromCid(cid.NewCidV0(valh)) - r.cacheSet(name, p, entry) - return p, nil } + + r.cacheSet(name, p, entry) + return p, nil } func checkEOL(e *pb.IpnsEntry) (time.Time, bool) { From a6eefc7352495af00403c51acad0af635e7fef17 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 9 May 2018 08:14:01 +0100 Subject: [PATCH 2336/3817] consolidate dns resolver constructors The current convention is to return the concrete type instead of an interface so let's go with that and have one constructor. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@93822e8bc76559dccc03139ef46f6d895031d2b5 --- namesys/dns.go | 8 +------- namesys/namesys.go | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 6d74e5221..de1d7cb49 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -21,13 +21,7 @@ type DNSResolver struct { } // NewDNSResolver constructs a name resolver using DNS TXT records. -func NewDNSResolver() Resolver { - return &DNSResolver{lookupTXT: net.LookupTXT} -} - -// newDNSResolver constructs a name resolver using DNS TXT records, -// returning a resolver instead of NewDNSResolver's Resolver. -func newDNSResolver() resolver { +func NewDNSResolver() *DNSResolver { return &DNSResolver{lookupTXT: net.LookupTXT} } diff --git a/namesys/namesys.go b/namesys/namesys.go index 6e37d1c6f..c1f3f7c6d 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -35,7 +35,7 @@ type mpns struct { func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSystem { return &mpns{ resolvers: map[string]resolver{ - "dns": newDNSResolver(), + "dns": NewDNSResolver(), "proquint": new(ProquintResolver), "dht": NewRoutingResolver(r, cachesize), }, From 8b31b88d0b7c3efe2c7c88c7208f5e4133945256 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 9 May 2018 09:21:55 +0100 Subject: [PATCH 2337/3817] store IPNS records *outside* of the DHT fixes #4749 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@8643e1c1bd087e53e4eb901ab8180066e16b9a43 --- namesys/namesys.go | 111 +++++----------- namesys/namesys_test.go | 4 +- namesys/publisher.go | 206 ++++++++++++++++++++++-------- namesys/publisher_test.go | 7 +- namesys/republisher/repub.go | 52 ++++---- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 13 +- 7 files changed, 222 insertions(+), 173 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index c1f3f7c6d..afd264ac1 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -3,7 +3,6 @@ package namesys import ( "context" "strings" - "sync" "time" opts "github.com/ipfs/go-ipfs/namesys/opts" @@ -37,10 +36,10 @@ func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSys resolvers: map[string]resolver{ "dns": NewDNSResolver(), "proquint": new(ProquintResolver), - "dht": NewRoutingResolver(r, cachesize), + "ipns": NewRoutingResolver(r, cachesize), }, publishers: map[string]Publisher{ - "dht": NewRoutingPublisher(r, ds), + "ipns": NewRoutingPublisher(r, ds), }, } } @@ -71,66 +70,32 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string, options *opts.Reso return "", ErrResolveFailed } - makePath := func(p path.Path) (path.Path, error) { - if len(segments) > 3 { - return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) - } else { - return p, nil - } - } - // Resolver selection: - // 1. if it is a multihash resolve through "pubsub" (if available), - // with fallback to "dht" + // 1. if it is a multihash resolve through "ipns". // 2. if it is a domain name, resolve through "dns" // 3. otherwise resolve through the "proquint" resolver key := segments[2] - - _, err := mh.FromB58String(key) - if err == nil { - res, ok := ns.resolvers["pubsub"] - if ok { - p, err := res.resolveOnce(ctx, key, options) - if err == nil { - return makePath(p) - } - } - - res, ok = ns.resolvers["dht"] - if ok { - p, err := res.resolveOnce(ctx, key, options) - if err == nil { - return makePath(p) - } - } - - return "", ErrResolveFailed + resName := "proquint" + if _, err := mh.FromB58String(key); err == nil { + resName = "ipns" + } else if isd.IsDomain(key) { + resName = "dns" } - if isd.IsDomain(key) { - res, ok := ns.resolvers["dns"] - if ok { - p, err := res.resolveOnce(ctx, key, options) - if err == nil { - return makePath(p) - } - } - + res, ok := ns.resolvers[resName] + if !ok { + log.Debugf("no resolver found for %s", name) return "", ErrResolveFailed } - - res, ok := ns.resolvers["proquint"] - if ok { - p, err := res.resolveOnce(ctx, key, options) - if err == nil { - return makePath(p) - } - + p, err := res.resolveOnce(ctx, key, options) + if err != nil { return "", ErrResolveFailed } - log.Debugf("no resolver found for %s", name) - return "", ErrResolveFailed + if len(segments) > 3 { + return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) + } + return p, nil } // Publish implements Publisher @@ -139,39 +104,23 @@ func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) e } func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error { - var dhtErr error - - wg := &sync.WaitGroup{} - wg.Add(1) - go func() { - dhtErr = ns.publishers["dht"].PublishWithEOL(ctx, name, value, eol) - if dhtErr == nil { - ns.addToDHTCache(name, value, eol) - } - wg.Done() - }() - - pub, ok := ns.publishers["pubsub"] - if ok { - wg.Add(1) - go func() { - err := pub.PublishWithEOL(ctx, name, value, eol) - if err != nil { - log.Warningf("error publishing %s with pubsub: %s", name, err.Error()) - } - wg.Done() - }() - } - - wg.Wait() - return dhtErr + pub, ok := ns.publishers["ipns"] + if !ok { + return ErrPublishFailed + } + if err := pub.PublishWithEOL(ctx, name, value, eol); err != nil { + return err + } + ns.addToIpnsCache(name, value, eol) + return nil + } -func (ns *mpns) addToDHTCache(key ci.PrivKey, value path.Path, eol time.Time) { - rr, ok := ns.resolvers["dht"].(*routingResolver) +func (ns *mpns) addToIpnsCache(key ci.PrivKey, value path.Path, eol time.Time) { + rr, ok := ns.resolvers["ipns"].(*routingResolver) if !ok { // should never happen, purely for sanity - log.Panicf("unexpected type %T as DHT resolver.", ns.resolvers["dht"]) + log.Panicf("unexpected type %T as DHT resolver.", ns.resolvers["ipns"]) } if rr.cache == nil { // resolver has no caching diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 03ba60ed0..766217296 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -59,8 +59,8 @@ func mockResolverTwo() *mockResolver { func TestNamesysResolution(t *testing.T) { r := &mpns{ resolvers: map[string]resolver{ - "dht": mockResolverOne(), - "dns": mockResolverTwo(), + "ipns": mockResolverOne(), + "dns": mockResolverTwo(), }, } diff --git a/namesys/publisher.go b/namesys/publisher.go index 2e470d66b..8c376d1af 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -4,6 +4,8 @@ import ( "bytes" "context" "fmt" + "strings" + "sync" "time" pb "github.com/ipfs/go-ipfs/namesys/pb" @@ -12,15 +14,17 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - dhtpb "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record/pb" routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" - dshelp "gx/ipfs/QmYJgz1Z5PbBGP7n2XA8uv5sF1EKLfYUjL7kFemVAjMNqC/go-ipfs-ds-help" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + dsquery "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" + base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) +const ipnsPrefix = "/ipns/" + const PublishPutValTimeout = time.Minute const DefaultRecordTTL = 24 * time.Hour @@ -29,6 +33,9 @@ const DefaultRecordTTL = 24 * time.Hour type ipnsPublisher struct { routing routing.ValueStore ds ds.Datastore + + // Used to ensure we assign IPNS records *sequential* sequence numbers. + mu sync.Mutex } // NewRoutingPublisher constructs a publisher for the IPFS Routing name system. @@ -46,69 +53,157 @@ func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Pa return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordTTL)) } -// PublishWithEOL is a temporary stand in for the ipns records implementation -// see here for more details: https://github.com/ipfs/specs/tree/master/records -func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error { +func IpnsDsKey(id peer.ID) ds.Key { + return ds.NewKey("/ipns/" + base32.RawStdEncoding.EncodeToString([]byte(id))) +} + +// PublishedNames returns the latest IPNS records published by this node and +// their expiration times. +// +// This method will not search the routing system for records published by other +// nodes. +func (p *ipnsPublisher) ListPublished(ctx context.Context) (map[peer.ID]*pb.IpnsEntry, error) { + query, err := p.ds.Query(dsquery.Query{ + Prefix: ipnsPrefix, + }) + if err != nil { + return nil, err + } + defer query.Close() + + records := make(map[peer.ID]*pb.IpnsEntry) + for { + select { + case result, ok := <-query.Next(): + if !ok { + return records, nil + } + if result.Error != nil { + return nil, result.Error + } + value, ok := result.Value.([]byte) + if !ok { + log.Error("found ipns record that we couldn't convert to a value") + continue + } + e := new(pb.IpnsEntry) + if err := proto.Unmarshal(value, e); err != nil { + // Might as well return what we can. + log.Error("found an invalid IPNS entry:", err) + continue + } + if !strings.HasPrefix(result.Key, ipnsPrefix) { + log.Errorf("datastore query for keys with prefix %s returned a key: %s", ipnsPrefix, result.Key) + continue + } + k := result.Key[len(ipnsPrefix):] + pid, err := base32.RawStdEncoding.DecodeString(k) + if err != nil { + log.Errorf("ipns ds key invalid: %s", result.Key) + continue + } + records[peer.ID(pid)] = e + case <-ctx.Done(): + return nil, ctx.Err() + } + } +} +// GetPublished returns the record this node has published corresponding to the +// given peer ID. +// +// If `checkRouting` is true and we have no existing record, this method will +// check the routing system for any existing records. +func (p *ipnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouting bool) (*pb.IpnsEntry, error) { + ctx, cancel := context.WithTimeout(ctx, time.Second*30) + defer cancel() + + dsVal, err := p.ds.Get(IpnsDsKey(id)) + var value []byte + switch err { + case nil: + var ok bool + value, ok = dsVal.([]byte) + if !ok { + return nil, fmt.Errorf("found ipns record that we couldn't convert to a value") + } + case ds.ErrNotFound: + if !checkRouting { + return nil, nil + } + _, ipnskey := IpnsKeysForID(id) + value, err = p.routing.GetValue(ctx, ipnskey) + if err != nil { + // Not found or other network issue. Can't really do + // anything about this case. + return nil, nil + } + default: + return nil, err + } + e := new(pb.IpnsEntry) + if err := proto.Unmarshal(value, e); err != nil { + return nil, err + } + return e, nil +} + +func (p *ipnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) (*pb.IpnsEntry, error) { id, err := peer.IDFromPrivateKey(k) if err != nil { - return err + return nil, err } - _, ipnskey := IpnsKeysForID(id) + p.mu.Lock() + defer p.mu.Unlock() // get previous records sequence number - seqnum, err := p.getPreviousSeqNo(ctx, ipnskey) + rec, err := p.GetPublished(ctx, id, true) if err != nil { - return err + return nil, err } - // increment it - seqnum++ - - return PutRecordToRouting(ctx, k, value, seqnum, eol, p.routing, id) -} + seqno := rec.GetSequence() // returns 0 if rec is nil + if rec != nil && value != path.Path(rec.GetValue()) { + // Don't bother incrementing the sequence number unless the + // value changes. + seqno++ + } -func (p *ipnsPublisher) getPreviousSeqNo(ctx context.Context, ipnskey string) (uint64, error) { - prevrec, err := p.ds.Get(dshelp.NewKeyFromBinary([]byte(ipnskey))) - if err != nil && err != ds.ErrNotFound { - // None found, lets start at zero! - return 0, err + // Create record + entry, err := CreateRoutingEntryData(k, value, seqno, eol) + if err != nil { + return nil, err } - var val []byte - if err == nil { - prbytes, ok := prevrec.([]byte) - if !ok { - return 0, fmt.Errorf("unexpected type returned from datastore: %#v", prevrec) - } - dhtrec := new(dhtpb.Record) - err := proto.Unmarshal(prbytes, dhtrec) - if err != nil { - return 0, err - } - val = dhtrec.GetValue() - } else { - // try and check the dht for a record - ctx, cancel := context.WithTimeout(ctx, time.Second*30) - defer cancel() + // Set the TTL + // TODO: Make this less hacky. + ttl, ok := checkCtxTTL(ctx) + if ok { + entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds())) + } - rv, err := p.routing.GetValue(ctx, ipnskey) - if err != nil { - // no such record found, start at zero! - return 0, nil - } + data, err := proto.Marshal(entry) + if err != nil { + return nil, err + } - val = rv + // Put the new record. + if err := p.ds.Put(IpnsDsKey(id), data); err != nil { + return nil, err } + return entry, nil +} - e := new(pb.IpnsEntry) - err = proto.Unmarshal(val, e) +// PublishWithEOL is a temporary stand in for the ipns records implementation +// see here for more details: https://github.com/ipfs/specs/tree/master/records +func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error { + record, err := p.updateRecord(ctx, k, value, eol) if err != nil { - return 0, err + return err } - return e.GetSequence(), nil + return PutRecordToRouting(ctx, p.routing, k.GetPublic(), record) } // setting the TTL on published records is an experimental feature. @@ -124,25 +219,24 @@ func checkCtxTTL(ctx context.Context) (time.Duration, bool) { return d, ok } -func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.ValueStore, id peer.ID) error { +func PutRecordToRouting(ctx context.Context, r routing.ValueStore, k ci.PubKey, entry *pb.IpnsEntry) error { ctx, cancel := context.WithCancel(ctx) defer cancel() - namekey, ipnskey := IpnsKeysForID(id) - entry, err := CreateRoutingEntryData(k, value, seqnum, eol) + errs := make(chan error, 2) // At most two errors (IPNS, and public key) + + id, err := peer.IDFromPublicKey(k) if err != nil { return err } - ttl, ok := checkCtxTTL(ctx) - if ok { - entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds())) + // Attempt to extract the public key from the ID + extractedPublicKey, err := id.ExtractPublicKey() + if err != nil { + return err } - errs := make(chan error, 2) // At most two errors (IPNS, and public key) - - // Attempt to extract the public key from the ID - extractedPublicKey, _ := id.ExtractPublicKey() + namekey, ipnskey := IpnsKeysForID(id) go func() { errs <- PublishEntry(ctx, r, ipnskey, entry) @@ -151,7 +245,7 @@ func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqn // Publish the public key if a public key cannot be extracted from the ID if extractedPublicKey == nil { go func() { - errs <- PublishPublicKey(ctx, r, namekey, k.GetPublic()) + errs <- PublishPublicKey(ctx, r, namekey, k) }() if err := waitOnErrChan(ctx, errs); err != nil { diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 39a975332..8f544d0c1 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -75,7 +75,12 @@ func testNamekeyPublisher(t *testing.T, keyType int, expectedErr error, expected serv := mockrouting.NewServer() r := serv.ClientWithDatastore(context.Background(), &identity{p}, dstore) - err = PutRecordToRouting(ctx, privKey, value, seqnum, eol, r, id) + entry, err := CreateRoutingEntryData(privKey, value, seqnum, eol) + if err != nil { + t.Fatal(err) + } + + err = PutRecordToRouting(ctx, r, pubKey, entry) if err != nil { t.Fatal(err) } diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index db7ae590e..aa1f85647 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -13,9 +13,6 @@ import ( goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" - recpb "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record/pb" - routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" - dshelp "gx/ipfs/QmYJgz1Z5PbBGP7n2XA8uv5sF1EKLfYUjL7kFemVAjMNqC/go-ipfs-ds-help" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" ic "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" @@ -39,7 +36,7 @@ var FailureRetryInterval = time.Minute * 5 const DefaultRecordLifetime = time.Hour * 24 type Republisher struct { - r routing.ValueStore + ns namesys.Publisher ds ds.Datastore self ic.PrivKey ks keystore.Keystore @@ -51,9 +48,9 @@ type Republisher struct { } // NewRepublisher creates a new Republisher -func NewRepublisher(r routing.ValueStore, ds ds.Datastore, self ic.PrivKey, ks keystore.Keystore) *Republisher { +func NewRepublisher(ns namesys.Publisher, ds ds.Datastore, self ic.PrivKey, ks keystore.Keystore) *Republisher { return &Republisher{ - r: r, + ns: ns, ds: ds, self: self, ks: ks, @@ -90,6 +87,10 @@ func (rp *Republisher) republishEntries(p goprocess.Process) error { ctx, cancel := context.WithCancel(gpctx.OnClosingContext(p)) defer cancel() + // TODO: Use rp.ipns.ListPublished(). We can't currently *do* that + // because: + // 1. There's no way to get keys from the keystore by ID. + // 2. We don't actually have access to the IPNS publisher. err := rp.republishEntry(ctx, rp.self) if err != nil { return err @@ -125,8 +126,7 @@ func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) erro log.Debugf("republishing ipns entry for %s", id) // Look for it locally only - _, ipnskey := namesys.IpnsKeysForID(id) - p, seq, err := rp.getLastVal(ipnskey) + p, err := rp.getLastVal(id) if err != nil { if err == errNoEntry { return nil @@ -136,33 +136,25 @@ func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) erro // update record with same sequence number eol := time.Now().Add(rp.RecordLifetime) - err = namesys.PutRecordToRouting(ctx, priv, p, seq, eol, rp.r, id) - if err != nil { - return err - } - - return nil + return rp.ns.PublishWithEOL(ctx, priv, p, eol) } -func (rp *Republisher) getLastVal(k string) (path.Path, uint64, error) { - ival, err := rp.ds.Get(dshelp.NewKeyFromBinary([]byte(k))) - if err != nil { - // not found means we dont have a previously published entry - return "", 0, errNoEntry +func (rp *Republisher) getLastVal(id peer.ID) (path.Path, error) { + // Look for it locally only + vali, err := rp.ds.Get(namesys.IpnsDsKey(id)) + switch err { + case nil: + case ds.ErrNotFound: + return "", errNoEntry + default: + return "", err } - val := ival.([]byte) - dhtrec := new(recpb.Record) - err = proto.Unmarshal(val, dhtrec) - if err != nil { - return "", 0, err - } + val := vali.([]byte) - // extract published data from record e := new(pb.IpnsEntry) - err = proto.Unmarshal(dhtrec.GetValue(), e) - if err != nil { - return "", 0, err + if err := proto.Unmarshal(val, e); err != nil { + return "", err } - return path.Path(e.Value), e.GetSequence(), nil + return path.Path(e.Value), nil } diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index f47df4696..8a9ab366f 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -78,7 +78,7 @@ func TestRepublish(t *testing.T) { // The republishers that are contained within the nodes have their timeout set // to 12 hours. Instead of trying to tweak those, we're just going to pretend // they dont exist and make our own. - repub := NewRepublisher(publisher.Routing, publisher.Repo.Datastore(), publisher.PrivateKey, publisher.Repo.Keystore()) + repub := NewRepublisher(rp, publisher.Repo.Datastore(), publisher.PrivateKey, publisher.Repo.Keystore()) repub.Interval = time.Second repub.RecordLifetime = time.Second * 5 diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 39a670088..206329667 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -70,7 +70,12 @@ func TestPrexistingExpiredRecord(t *testing.T) { // Make an expired record and put it in the datastore h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN") eol := time.Now().Add(time.Hour * -1) - err = PutRecordToRouting(context.Background(), privk, h, 0, eol, d, id) + + entry, err := CreateRoutingEntryData(privk, h, 0, eol) + if err != nil { + t.Fatal(err) + } + err = PutRecordToRouting(context.Background(), d, pubk, entry) if err != nil { t.Fatal(err) } @@ -107,7 +112,11 @@ func TestPrexistingRecord(t *testing.T) { // Make a good record and put it in the datastore h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN") eol := time.Now().Add(time.Hour) - err = PutRecordToRouting(context.Background(), privk, h, 0, eol, d, id) + entry, err := CreateRoutingEntryData(privk, h, 0, eol) + if err != nil { + t.Fatal(err) + } + err = PutRecordToRouting(context.Background(), d, pubk, entry) if err != nil { t.Fatal(err) } From 4a8b66fab275c7f5c6a69694b705877e82459453 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 9 May 2018 13:55:58 +0100 Subject: [PATCH 2338/3817] cleanup namesys a bit Remove ~50 lines of code, some casting, and a superfluous map (when go starts looking like python, something's wrong). License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@ad1299864f29181b29db24403dfb265a9eb4063c --- namesys/base.go | 5 +- namesys/cache.go | 47 ++++++++++++ namesys/dns.go | 17 +++-- namesys/ipns_validate_test.go | 12 +-- namesys/namesys.go | 119 ++++++++++++------------------ namesys/namesys_test.go | 16 ++-- namesys/proquint.go | 8 +- namesys/publisher.go | 20 ++--- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 12 +-- namesys/routing.go | 115 +++++++---------------------- 11 files changed, 172 insertions(+), 201 deletions(-) create mode 100644 namesys/cache.go diff --git a/namesys/base.go b/namesys/base.go index a301a5a61..525a9afb0 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -2,6 +2,7 @@ package namesys import ( "strings" + "time" context "context" @@ -11,14 +12,14 @@ import ( type resolver interface { // resolveOnce looks up a name once (without recursion). - resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (value path.Path, err error) + resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (value path.Path, ttl time.Duration, err error) } // resolve is a helper for implementing Resolver.ResolveN using resolveOnce. func resolve(ctx context.Context, r resolver, name string, options *opts.ResolveOpts, prefixes ...string) (path.Path, error) { depth := options.Depth for { - p, err := r.resolveOnce(ctx, name, options) + p, _, err := r.resolveOnce(ctx, name, options) if err != nil { return "", err } diff --git a/namesys/cache.go b/namesys/cache.go new file mode 100644 index 000000000..8249fea14 --- /dev/null +++ b/namesys/cache.go @@ -0,0 +1,47 @@ +package namesys + +import ( + "time" + + path "github.com/ipfs/go-ipfs/path" +) + +func (ns *mpns) cacheGet(name string) (path.Path, bool) { + if ns.cache == nil { + return "", false + } + + ientry, ok := ns.cache.Get(name) + if !ok { + return "", false + } + + entry, ok := ientry.(cacheEntry) + if !ok { + // should never happen, purely for sanity + log.Panicf("unexpected type %T in cache for %q.", ientry, name) + } + + if time.Now().Before(entry.eol) { + return entry.val, true + } + + ns.cache.Remove(name) + + return "", false +} + +func (ns *mpns) cacheSet(name string, val path.Path, ttl time.Duration) { + if ns.cache == nil || ttl <= 0 { + return + } + ns.cache.Add(name, cacheEntry{ + val: val, + eol: time.Now().Add(ttl), + }) +} + +type cacheEntry struct { + val path.Path + eol time.Time +} diff --git a/namesys/dns.go b/namesys/dns.go index de1d7cb49..1591f16e4 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -5,6 +5,7 @@ import ( "errors" "net" "strings" + "time" opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" @@ -38,12 +39,12 @@ type lookupRes struct { // resolveOnce implements resolver. // TXT records for a given domain name should contain a b58 // encoded multihash. -func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) { +func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) { segments := strings.SplitN(name, "/", 2) domain := segments[0] if !isd.IsDomain(domain) { - return "", errors.New("not a valid domain name") + return "", 0, errors.New("not a valid domain name") } log.Debugf("DNSResolver resolving %s", domain) @@ -57,7 +58,7 @@ func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options *opt select { case subRes = <-subChan: case <-ctx.Done(): - return "", ctx.Err() + return "", 0, ctx.Err() } var p path.Path @@ -68,19 +69,19 @@ func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options *opt select { case rootRes = <-rootChan: case <-ctx.Done(): - return "", ctx.Err() + return "", 0, ctx.Err() } if rootRes.error == nil { p = rootRes.path } else { - return "", ErrResolveFailed + return "", 0, ErrResolveFailed } } + var err error if len(segments) > 1 { - return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[1]) - } else { - return p, nil + p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[1]) } + return p, 0, err } func workDomain(r *DNSResolver, name string, res chan lookupRes) { diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index bcffcc5e6..f9cdf024a 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -81,7 +81,7 @@ func TestResolverValidation(t *testing.T) { peerstore := pstore.NewPeerstore() vstore := newMockValueStore(rid, dstore, peerstore) - resolver := NewRoutingResolver(vstore, 0) + resolver := NewIpnsResolver(vstore) // Create entry with expiry in one hour priv, id, _, ipnsDHTPath := genKeys(t) @@ -105,7 +105,7 @@ func TestResolverValidation(t *testing.T) { } // Resolve entry - resp, err := resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts()) + resp, _, err := resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts()) if err != nil { t.Fatal(err) } @@ -126,7 +126,7 @@ func TestResolverValidation(t *testing.T) { } // Record should fail validation because entry is expired - _, err = resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts()) + _, _, err = resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts()) if err == nil { t.Fatal("ValidateIpnsRecord should have returned error") } @@ -148,7 +148,7 @@ func TestResolverValidation(t *testing.T) { // Record should fail validation because public key defined by // ipns path doesn't match record signature - _, err = resolver.resolveOnce(ctx, id2.Pretty(), opts.DefaultResolveOpts()) + _, _, err = resolver.resolveOnce(ctx, id2.Pretty(), opts.DefaultResolveOpts()) if err == nil { t.Fatal("ValidateIpnsRecord should have failed signature verification") } @@ -166,7 +166,7 @@ func TestResolverValidation(t *testing.T) { // Record should fail validation because public key is not available // in peer store or on network - _, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts()) + _, _, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts()) if err == nil { t.Fatal("ValidateIpnsRecord should have failed because public key was not found") } @@ -181,7 +181,7 @@ func TestResolverValidation(t *testing.T) { // public key is available in the peer store by looking it up in // the DHT, which causes the DHT to fetch it and cache it in the // peer store - _, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts()) + _, _, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts()) if err != nil { t.Fatal(err) } diff --git a/namesys/namesys.go b/namesys/namesys.go index afd264ac1..bbdeb9b86 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -9,6 +9,7 @@ import ( path "github.com/ipfs/go-ipfs/path" routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" + lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" @@ -26,21 +27,25 @@ import ( // It can only publish to: (a) IPFS routing naming. // type mpns struct { - resolvers map[string]resolver - publishers map[string]Publisher + dnsResolver, proquintResolver, ipnsResolver resolver + ipnsPublisher Publisher + + cache *lru.Cache } // NewNameSystem will construct the IPFS naming system based on Routing func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSystem { + var cache *lru.Cache + if cachesize > 0 { + cache, _ = lru.New(cachesize) + } + return &mpns{ - resolvers: map[string]resolver{ - "dns": NewDNSResolver(), - "proquint": new(ProquintResolver), - "ipns": NewRoutingResolver(r, cachesize), - }, - publishers: map[string]Publisher{ - "ipns": NewRoutingPublisher(r, ds), - }, + dnsResolver: NewDNSResolver(), + proquintResolver: new(ProquintResolver), + ipnsResolver: NewIpnsResolver(r), + ipnsPublisher: NewIpnsPublisher(r, ds), + cache: cache, } } @@ -60,42 +65,46 @@ func (ns *mpns) Resolve(ctx context.Context, name string, options ...opts.Resolv } // resolveOnce implements resolver. -func (ns *mpns) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) { +func (ns *mpns) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) { if !strings.HasPrefix(name, "/ipns/") { name = "/ipns/" + name } segments := strings.SplitN(name, "/", 4) if len(segments) < 3 || segments[0] != "" { log.Debugf("invalid name syntax for %s", name) - return "", ErrResolveFailed + return "", 0, ErrResolveFailed } - // Resolver selection: - // 1. if it is a multihash resolve through "ipns". - // 2. if it is a domain name, resolve through "dns" - // 3. otherwise resolve through the "proquint" resolver key := segments[2] - resName := "proquint" - if _, err := mh.FromB58String(key); err == nil { - resName = "ipns" - } else if isd.IsDomain(key) { - resName = "dns" - } - res, ok := ns.resolvers[resName] + p, ok := ns.cacheGet(key) + var err error if !ok { - log.Debugf("no resolver found for %s", name) - return "", ErrResolveFailed - } - p, err := res.resolveOnce(ctx, key, options) - if err != nil { - return "", ErrResolveFailed + // Resolver selection: + // 1. if it is a multihash resolve through "ipns". + // 2. if it is a domain name, resolve through "dns" + // 3. otherwise resolve through the "proquint" resolver + var res resolver + if _, err := mh.FromB58String(key); err == nil { + res = ns.ipnsResolver + } else if isd.IsDomain(key) { + res = ns.dnsResolver + } else { + res = ns.proquintResolver + } + + var ttl time.Duration + p, ttl, err = res.resolveOnce(ctx, key, options) + if err != nil { + return "", 0, ErrResolveFailed + } + ns.cacheSet(key, p, ttl) } if len(segments) > 3 { - return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) + p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) } - return p, nil + return p, 0, err } // Publish implements Publisher @@ -104,47 +113,17 @@ func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) e } func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error { - pub, ok := ns.publishers["ipns"] - if !ok { - return ErrPublishFailed - } - if err := pub.PublishWithEOL(ctx, name, value, eol); err != nil { - return err - } - ns.addToIpnsCache(name, value, eol) - return nil - -} - -func (ns *mpns) addToIpnsCache(key ci.PrivKey, value path.Path, eol time.Time) { - rr, ok := ns.resolvers["ipns"].(*routingResolver) - if !ok { - // should never happen, purely for sanity - log.Panicf("unexpected type %T as DHT resolver.", ns.resolvers["ipns"]) - } - if rr.cache == nil { - // resolver has no caching - return - } - - var err error - value, err = path.ParsePath(value.String()) + id, err := peer.IDFromPrivateKey(name) if err != nil { - log.Error("could not parse path") - return + return err } - - name, err := peer.IDFromPrivateKey(key) - if err != nil { - log.Error("while adding to cache, could not get peerid from private key") - return + if err := ns.ipnsPublisher.PublishWithEOL(ctx, name, value, eol); err != nil { + return err } - - if time.Now().Add(DefaultResolverCacheTTL).Before(eol) { - eol = time.Now().Add(DefaultResolverCacheTTL) + ttl := DefaultResolverCacheTTL + if ttEol := eol.Sub(time.Now()); ttEol < ttl { + ttl = ttEol } - rr.cache.Add(name.Pretty(), cacheEntry{ - val: value, - eol: eol, - }) + ns.cacheSet(peer.IDB58Encode(id), value, ttl) + return nil } diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 766217296..9cf41aea0 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -1,10 +1,10 @@ package namesys import ( + "context" "fmt" "testing" - - context "context" + "time" opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" @@ -21,6 +21,7 @@ type mockResolver struct { } func testResolution(t *testing.T, resolver Resolver, name string, depth uint, expected string, expError error) { + t.Helper() p, err := resolver.Resolve(context.Background(), name, opts.Depth(depth)) if err != expError { t.Fatal(fmt.Errorf( @@ -34,8 +35,9 @@ func testResolution(t *testing.T, resolver Resolver, name string, depth uint, ex } } -func (r *mockResolver) resolveOnce(ctx context.Context, name string, opts *opts.ResolveOpts) (path.Path, error) { - return path.ParsePath(r.entries[name]) +func (r *mockResolver) resolveOnce(ctx context.Context, name string, opts *opts.ResolveOpts) (path.Path, time.Duration, error) { + p, err := path.ParsePath(r.entries[name]) + return p, 0, err } func mockResolverOne() *mockResolver { @@ -58,10 +60,8 @@ func mockResolverTwo() *mockResolver { func TestNamesysResolution(t *testing.T) { r := &mpns{ - resolvers: map[string]resolver{ - "ipns": mockResolverOne(), - "dns": mockResolverTwo(), - }, + ipnsResolver: mockResolverOne(), + dnsResolver: mockResolverTwo(), } testResolution(t, r, "Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", opts.DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil) diff --git a/namesys/proquint.go b/namesys/proquint.go index 2c61c98d3..c065db2d7 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -2,6 +2,7 @@ package namesys import ( "errors" + "time" context "context" @@ -18,10 +19,11 @@ func (r *ProquintResolver) Resolve(ctx context.Context, name string, options ... } // resolveOnce implements resolver. Decodes the proquint string. -func (r *ProquintResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) { +func (r *ProquintResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) { ok, err := proquint.IsProquint(name) if err != nil || !ok { - return "", errors.New("not a valid proquint string") + return "", 0, errors.New("not a valid proquint string") } - return path.FromString(string(proquint.Decode(name))), nil + // Return a 0 TTL as caching this result is pointless. + return path.FromString(string(proquint.Decode(name))), 0, nil } diff --git a/namesys/publisher.go b/namesys/publisher.go index 8c376d1af..4fe15ca68 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -28,9 +28,9 @@ const ipnsPrefix = "/ipns/" const PublishPutValTimeout = time.Minute const DefaultRecordTTL = 24 * time.Hour -// ipnsPublisher is capable of publishing and resolving names to the IPFS +// IpnsPublisher is capable of publishing and resolving names to the IPFS // routing system. -type ipnsPublisher struct { +type IpnsPublisher struct { routing routing.ValueStore ds ds.Datastore @@ -38,17 +38,17 @@ type ipnsPublisher struct { mu sync.Mutex } -// NewRoutingPublisher constructs a publisher for the IPFS Routing name system. -func NewRoutingPublisher(route routing.ValueStore, ds ds.Datastore) *ipnsPublisher { +// NewIpnsPublisher constructs a publisher for the IPFS Routing name system. +func NewIpnsPublisher(route routing.ValueStore, ds ds.Datastore) *IpnsPublisher { if ds == nil { panic("nil datastore") } - return &ipnsPublisher{routing: route, ds: ds} + return &IpnsPublisher{routing: route, ds: ds} } // Publish implements Publisher. Accepts a keypair and a value, // and publishes it out to the routing system -func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error { +func (p *IpnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error { log.Debugf("Publish %s", value) return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordTTL)) } @@ -62,7 +62,7 @@ func IpnsDsKey(id peer.ID) ds.Key { // // This method will not search the routing system for records published by other // nodes. -func (p *ipnsPublisher) ListPublished(ctx context.Context) (map[peer.ID]*pb.IpnsEntry, error) { +func (p *IpnsPublisher) ListPublished(ctx context.Context) (map[peer.ID]*pb.IpnsEntry, error) { query, err := p.ds.Query(dsquery.Query{ Prefix: ipnsPrefix, }) @@ -114,7 +114,7 @@ func (p *ipnsPublisher) ListPublished(ctx context.Context) (map[peer.ID]*pb.Ipns // // If `checkRouting` is true and we have no existing record, this method will // check the routing system for any existing records. -func (p *ipnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouting bool) (*pb.IpnsEntry, error) { +func (p *IpnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouting bool) (*pb.IpnsEntry, error) { ctx, cancel := context.WithTimeout(ctx, time.Second*30) defer cancel() @@ -148,7 +148,7 @@ func (p *ipnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouti return e, nil } -func (p *ipnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) (*pb.IpnsEntry, error) { +func (p *IpnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) (*pb.IpnsEntry, error) { id, err := peer.IDFromPrivateKey(k) if err != nil { return nil, err @@ -197,7 +197,7 @@ func (p *ipnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value pa // PublishWithEOL is a temporary stand in for the ipns records implementation // see here for more details: https://github.com/ipfs/specs/tree/master/records -func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error { +func (p *IpnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error { record, err := p.updateRecord(ctx, k, value, eol) if err != nil { return err diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 8a9ab366f..0878cb13f 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -58,7 +58,7 @@ func TestRepublish(t *testing.T) { // have one node publish a record that is valid for 1 second publisher := nodes[3] p := path.FromString("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") // does not need to be valid - rp := namesys.NewRoutingPublisher(publisher.Routing, publisher.Repo.Datastore()) + rp := namesys.NewIpnsPublisher(publisher.Routing, publisher.Repo.Datastore()) err := rp.PublishWithEOL(ctx, publisher.PrivateKey, p, time.Now().Add(time.Second)) if err != nil { t.Fatal(err) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 206329667..c3f7fbde1 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -21,8 +21,8 @@ func TestRoutingResolve(t *testing.T) { id := testutil.RandIdentityOrFatal(t) d := serv.ClientWithDatastore(context.Background(), id, dstore) - resolver := NewRoutingResolver(d, 0) - publisher := NewRoutingPublisher(d, dstore) + resolver := NewIpnsResolver(d) + publisher := NewIpnsPublisher(d, dstore) privk, pubk, err := testutil.RandTestKeyPair(512) if err != nil { @@ -54,8 +54,8 @@ func TestPrexistingExpiredRecord(t *testing.T) { dstore := dssync.MutexWrap(ds.NewMapDatastore()) d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore) - resolver := NewRoutingResolver(d, 0) - publisher := NewRoutingPublisher(d, dstore) + resolver := NewIpnsResolver(d) + publisher := NewIpnsPublisher(d, dstore) privk, pubk, err := testutil.RandTestKeyPair(512) if err != nil { @@ -96,8 +96,8 @@ func TestPrexistingRecord(t *testing.T) { dstore := dssync.MutexWrap(ds.NewMapDatastore()) d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore) - resolver := NewRoutingResolver(d, 0) - publisher := NewRoutingPublisher(d, dstore) + resolver := NewIpnsResolver(d) + publisher := NewIpnsPublisher(d, dstore) privk, pubk, err := testutil.RandTestKeyPair(512) if err != nil { diff --git a/namesys/routing.go b/namesys/routing.go index 4a1668c31..3e9c6ecae 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -12,7 +12,6 @@ import ( u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" - lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" @@ -22,102 +21,31 @@ import ( var log = logging.Logger("namesys") -// routingResolver implements NSResolver for the main IPFS SFS-like naming -type routingResolver struct { +// IpnsResolver implements NSResolver for the main IPFS SFS-like naming +type IpnsResolver struct { routing routing.ValueStore - - cache *lru.Cache -} - -func (r *routingResolver) cacheGet(name string) (path.Path, bool) { - if r.cache == nil { - return "", false - } - - ientry, ok := r.cache.Get(name) - if !ok { - return "", false - } - - entry, ok := ientry.(cacheEntry) - if !ok { - // should never happen, purely for sanity - log.Panicf("unexpected type %T in cache for %q.", ientry, name) - } - - if time.Now().Before(entry.eol) { - return entry.val, true - } - - r.cache.Remove(name) - - return "", false -} - -func (r *routingResolver) cacheSet(name string, val path.Path, rec *pb.IpnsEntry) { - if r.cache == nil { - return - } - - // if completely unspecified, just use one minute - ttl := DefaultResolverCacheTTL - if rec.Ttl != nil { - recttl := time.Duration(rec.GetTtl()) - if recttl >= 0 { - ttl = recttl - } - } - - cacheTil := time.Now().Add(ttl) - eol, ok := checkEOL(rec) - if ok && eol.Before(cacheTil) { - cacheTil = eol - } - - r.cache.Add(name, cacheEntry{ - val: val, - eol: cacheTil, - }) -} - -type cacheEntry struct { - val path.Path - eol time.Time } -// NewRoutingResolver constructs a name resolver using the IPFS Routing system +// NewIpnsResolver constructs a name resolver using the IPFS Routing system // to implement SFS-like naming on top. -// cachesize is the limit of the number of entries in the lru cache. Setting it -// to '0' will disable caching. -func NewRoutingResolver(route routing.ValueStore, cachesize int) *routingResolver { +func NewIpnsResolver(route routing.ValueStore) *IpnsResolver { if route == nil { panic("attempt to create resolver with nil routing system") } - - var cache *lru.Cache - if cachesize > 0 { - cache, _ = lru.New(cachesize) - } - - return &routingResolver{ + return &IpnsResolver{ routing: route, - cache: cache, } } // Resolve implements Resolver. -func (r *routingResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) { +func (r *IpnsResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) { return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/") } // resolveOnce implements resolver. Uses the IPFS routing system to // resolve SFS-like names. -func (r *routingResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) { +func (r *IpnsResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) { log.Debugf("RoutingResolver resolving %s", name) - cached, ok := r.cacheGet(name) - if ok { - return cached, nil - } if options.DhtTimeout != 0 { // Resolution must complete within the timeout @@ -131,13 +59,13 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, options if err != nil { // name should be a multihash. if it isn't, error out here. log.Debugf("RoutingResolver: bad input hash: [%s]\n", name) - return "", err + return "", 0, err } pid, err := peer.IDFromBytes(hash) if err != nil { log.Debugf("RoutingResolver: could not convert public key hash %s to peer ID: %s\n", name, err) - return "", err + return "", 0, err } // Name should be the hash of a public key retrievable from ipfs. @@ -148,7 +76,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, options _, err = routing.GetPublicKey(r.routing, ctx, pid) if err != nil { log.Debugf("RoutingResolver: could not retrieve public key %s: %s\n", name, err) - return "", err + return "", 0, err } // Use the routing system to get the name. @@ -158,14 +86,14 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, options val, err := r.routing.GetValue(ctx, ipnsKey, dht.Quorum(int(options.DhtRecordCount))) if err != nil { log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err) - return "", err + return "", 0, err } entry := new(pb.IpnsEntry) err = proto.Unmarshal(val, entry) if err != nil { log.Debugf("RoutingResolver: could not unmarshal value for name %s: %s", name, err) - return "", err + return "", 0, err } var p path.Path @@ -178,12 +106,25 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, options // Not a multihash, probably a new record p, err = path.ParsePath(string(entry.GetValue())) if err != nil { - return "", err + return "", 0, err + } + } + + ttl := DefaultResolverCacheTTL + if entry.Ttl != nil { + ttl = time.Duration(*entry.Ttl) + } + if eol, ok := checkEOL(entry); ok { + ttEol := eol.Sub(time.Now()) + if ttEol < 0 { + // It *was* valid when we first resolved it. + ttl = 0 + } else if ttEol < ttl { + ttl = ttEol } } - r.cacheSet(name, p, entry) - return p, nil + return p, ttl, nil } func checkEOL(e *pb.IpnsEntry) (time.Time, bool) { From 5786f02bfba5eba63d9693859864d70e99e69593 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sat, 2 Jun 2018 00:38:00 -0700 Subject: [PATCH 2339/3817] log on network error when resolving the last published IPNS record License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@071c54772b243f5b06b4144254f77c808a723504 --- namesys/publisher.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/namesys/publisher.go b/namesys/publisher.go index 4fe15ca68..f5f7d3695 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -136,6 +136,10 @@ func (p *IpnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouti if err != nil { // Not found or other network issue. Can't really do // anything about this case. + if err != routing.ErrNotFound { + log.Debugf("error when determining the last published IPNS record for %s: %s", id, err) + } + return nil, nil } default: From f8b077ad538f40047673ce20bb5756869ca7b4fa Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 4 Jun 2018 09:53:40 -0700 Subject: [PATCH 2340/3817] update multiplexers License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@66c0bcdc68b7e69e1ed4a58fccd02eea2706e7f7 --- namesys/ipns_validate_test.go | 2 +- namesys/namesys_test.go | 2 +- namesys/publisher_test.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index f9cdf024a..5fc713bf3 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -10,12 +10,12 @@ import ( path "github.com/ipfs/go-ipfs/path" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - mockrouting "gx/ipfs/QmPuPdzoG4b5uyYSQCjLEHB8NM593m3BW19UHX2jZ6Wzfm/go-ipfs-routing/mock" record "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record" routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" ropts "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing/options" testutil "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + mockrouting "gx/ipfs/QmcE3B6ittYBmctva8Q155LPa1YPcVqg8N7pPcgt9i7iAQ/go-ipfs-routing/mock" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" pstore "gx/ipfs/QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh/go-libp2p-peerstore" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 9cf41aea0..a2d8bd01f 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,7 +10,7 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/unixfs" - offroute "gx/ipfs/QmPuPdzoG4b5uyYSQCjLEHB8NM593m3BW19UHX2jZ6Wzfm/go-ipfs-routing/offline" + offroute "gx/ipfs/QmcE3B6ittYBmctva8Q155LPa1YPcVqg8N7pPcgt9i7iAQ/go-ipfs-routing/offline" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 8f544d0c1..67ed6471c 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -8,10 +8,10 @@ import ( path "github.com/ipfs/go-ipfs/path" - mockrouting "gx/ipfs/QmPuPdzoG4b5uyYSQCjLEHB8NM593m3BW19UHX2jZ6Wzfm/go-ipfs-routing/mock" testutil "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil" ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" dshelp "gx/ipfs/QmYJgz1Z5PbBGP7n2XA8uv5sF1EKLfYUjL7kFemVAjMNqC/go-ipfs-ds-help" + mockrouting "gx/ipfs/QmcE3B6ittYBmctva8Q155LPa1YPcVqg8N7pPcgt9i7iAQ/go-ipfs-routing/mock" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 0878cb13f..0d927f1ef 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,7 +13,7 @@ import ( path "github.com/ipfs/go-ipfs/path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmWsV6kzPaYGBDVyuUfWBvyQygEc9Qrv9vzo8vZ7X4mdLN/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmY6iAoG9DVgZwh5ZRcQEpa2uErAe1Hbei8qXPCjpDS9Ge/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh/go-libp2p-peerstore" ) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index c3f7fbde1..45609e95d 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,8 +8,8 @@ import ( path "github.com/ipfs/go-ipfs/path" - mockrouting "gx/ipfs/QmPuPdzoG4b5uyYSQCjLEHB8NM593m3BW19UHX2jZ6Wzfm/go-ipfs-routing/mock" testutil "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil" + mockrouting "gx/ipfs/QmcE3B6ittYBmctva8Q155LPa1YPcVqg8N7pPcgt9i7iAQ/go-ipfs-routing/mock" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" diff --git a/namesys/routing.go b/namesys/routing.go index 3e9c6ecae..e399b1936 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -16,7 +16,7 @@ import ( mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - dht "gx/ipfs/Qmd3jqhBQFvhfBNTSJMQL15GgyVMpdxKTta69Napvx6Myd/go-libp2p-kad-dht" + dht "gx/ipfs/Qme6C1xZFKUQVxvj8Sb7afWiQxzkQt67gq5V2o85pivCjV/go-libp2p-kad-dht" ) var log = logging.Logger("namesys") From 6d5f3eb89e17759cf18eb4a7df68429d2aa5f4d9 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 4 Jun 2018 09:53:40 -0700 Subject: [PATCH 2341/3817] update multiplexers License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@f227461b6ab84f9287d68e12783c9ec6384eceab --- blockservice/test/mock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index 5fe23cb84..dd458b807 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -5,8 +5,8 @@ import ( bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" - mockrouting "gx/ipfs/QmPuPdzoG4b5uyYSQCjLEHB8NM593m3BW19UHX2jZ6Wzfm/go-ipfs-routing/mock" delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" + mockrouting "gx/ipfs/QmcE3B6ittYBmctva8Q155LPa1YPcVqg8N7pPcgt9i7iAQ/go-ipfs-routing/mock" ) // Mocks returns |n| connected mock Blockservices From 9bde23e78c50f3411258ae5e563a5a0289dda552 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 5 Jun 2018 02:01:18 -0700 Subject: [PATCH 2342/3817] embed public keys inside ipns records, use for validation License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@5d8148222cc17389d8ed71cee6a7a99ead571fe6 --- namesys/pb/namesys.pb.go | 33 ++++++++++++++++++++++++--------- namesys/pb/namesys.proto | 6 ++++++ namesys/publisher.go | 13 +++++++++++++ namesys/validator.go | 38 ++++++++++++++++++++++++++++++++++---- 4 files changed, 77 insertions(+), 13 deletions(-) diff --git a/namesys/pb/namesys.pb.go b/namesys/pb/namesys.pb.go index 31e6355d7..66626ca7d 100644 --- a/namesys/pb/namesys.pb.go +++ b/namesys/pb/namesys.pb.go @@ -1,12 +1,12 @@ // Code generated by protoc-gen-gogo. -// source: namesys.proto +// source: namesys/pb/namesys.proto // DO NOT EDIT! /* Package namesys_pb is a generated protocol buffer package. It is generated from these files: - namesys.proto + namesys/pb/namesys.proto It has these top-level messages: IpnsEntry @@ -14,10 +14,12 @@ It has these top-level messages: package namesys_pb import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" +import fmt "fmt" import math "math" // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal +var _ = fmt.Errorf var _ = math.Inf type IpnsEntry_ValidityType int32 @@ -52,13 +54,18 @@ func (x *IpnsEntry_ValidityType) UnmarshalJSON(data []byte) error { } type IpnsEntry struct { - Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"` - Signature []byte `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"` - ValidityType *IpnsEntry_ValidityType `protobuf:"varint,3,opt,name=validityType,enum=namesys.pb.IpnsEntry_ValidityType" json:"validityType,omitempty"` - Validity []byte `protobuf:"bytes,4,opt,name=validity" json:"validity,omitempty"` - Sequence *uint64 `protobuf:"varint,5,opt,name=sequence" json:"sequence,omitempty"` - Ttl *uint64 `protobuf:"varint,6,opt,name=ttl" json:"ttl,omitempty"` - XXX_unrecognized []byte `json:"-"` + Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"` + Signature []byte `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"` + ValidityType *IpnsEntry_ValidityType `protobuf:"varint,3,opt,name=validityType,enum=namesys.pb.IpnsEntry_ValidityType" json:"validityType,omitempty"` + Validity []byte `protobuf:"bytes,4,opt,name=validity" json:"validity,omitempty"` + Sequence *uint64 `protobuf:"varint,5,opt,name=sequence" json:"sequence,omitempty"` + Ttl *uint64 `protobuf:"varint,6,opt,name=ttl" json:"ttl,omitempty"` + // in order for nodes to properly validate a record upon receipt, they need the public + // key associated with it. For old RSA keys, its easiest if we just send this as part of + // the record itself. For newer ed25519 keys, the public key can be embedded in the + // peerID, making this field unnecessary. + PubKey []byte `protobuf:"bytes,7,opt,name=pubKey" json:"pubKey,omitempty"` + XXX_unrecognized []byte `json:"-"` } func (m *IpnsEntry) Reset() { *m = IpnsEntry{} } @@ -107,6 +114,14 @@ func (m *IpnsEntry) GetTtl() uint64 { return 0 } +func (m *IpnsEntry) GetPubKey() []byte { + if m != nil { + return m.PubKey + } + return nil +} + func init() { + proto.RegisterType((*IpnsEntry)(nil), "namesys.pb.IpnsEntry") proto.RegisterEnum("namesys.pb.IpnsEntry_ValidityType", IpnsEntry_ValidityType_name, IpnsEntry_ValidityType_value) } diff --git a/namesys/pb/namesys.proto b/namesys/pb/namesys.proto index d6eaf3243..b72d49843 100644 --- a/namesys/pb/namesys.proto +++ b/namesys/pb/namesys.proto @@ -14,4 +14,10 @@ message IpnsEntry { optional uint64 sequence = 5; optional uint64 ttl = 6; + + // in order for nodes to properly validate a record upon receipt, they need the public + // key associated with it. For old RSA keys, its easiest if we just send this as part of + // the record itself. For newer ed25519 keys, the public key can be embedded in the + // peerID, making this field unnecessary. + optional bytes pubKey = 7; } diff --git a/namesys/publisher.go b/namesys/publisher.go index f5f7d3695..80bc10d47 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -240,6 +240,17 @@ func PutRecordToRouting(ctx context.Context, r routing.ValueStore, k ci.PubKey, return err } + // if we can't derive the public key from the peerID, embed the entire pubkey in + // the record to make the verifiers job easier + if extractedPublicKey == nil { + pubkeyBytes, err := k.Bytes() + if err != nil { + return err + } + + entry.PubKey = pubkeyBytes + } + namekey, ipnskey := IpnsKeysForID(id) go func() { @@ -247,6 +258,8 @@ func PutRecordToRouting(ctx context.Context, r routing.ValueStore, k ci.PubKey, }() // Publish the public key if a public key cannot be extracted from the ID + // TODO: once v0.4.16 is widespread enough, we can stop doing this + // and at that point we can even deprecate the /pk/ namespace in the dht if extractedPublicKey == nil { go func() { errs <- PublishPublicKey(ctx, r, namekey, k) diff --git a/namesys/validator.go b/namesys/validator.go index 941d6a667..852276f17 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -3,11 +3,13 @@ package namesys import ( "bytes" "errors" + "fmt" "time" pb "github.com/ipfs/go-ipfs/namesys/pb" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" pstore "gx/ipfs/QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh/go-libp2p-peerstore" + ic "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" record "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record" @@ -65,10 +67,10 @@ func (v IpnsValidator) Validate(key string, value []byte) error { log.Debugf("failed to parse ipns record key %s into peer ID", pidString) return ErrKeyFormat } - pubk := v.KeyBook.PubKey(pid) - if pubk == nil { - log.Debugf("public key with hash %s not found in peer store", pid) - return ErrPublicKeyNotFound + + pubk, err := v.getPublicKey(pid, entry) + if err != nil { + return fmt.Errorf("getting public key failed: %s", err) } // Check the ipns record signature with the public key @@ -94,6 +96,34 @@ func (v IpnsValidator) Validate(key string, value []byte) error { return nil } +func (v IpnsValidator) getPublicKey(pid peer.ID, entry *pb.IpnsEntry) (ic.PubKey, error) { + if entry.PubKey != nil { + pk, err := ic.UnmarshalPublicKey(entry.PubKey) + if err != nil { + // TODO: i think this counts as a 'malformed record' and should be discarded + log.Debugf("public key in ipns record failed to parse: ", err) + return nil, err + } + expPid, err := peer.IDFromPublicKey(pk) + if err != nil { + return nil, fmt.Errorf("could not regenerate peerID from pubkey: %s", err) + } + + if pid != expPid { + return nil, fmt.Errorf("pubkey in record did not match expected pubkey") + } + + return pk, nil + } + + pubk := v.KeyBook.PubKey(pid) + if pubk == nil { + log.Debugf("public key with hash %s not found in peer store", pid) + return nil, ErrPublicKeyNotFound + } + return pubk, nil +} + // IpnsSelectorFunc selects the best record by checking which has the highest // sequence number and latest EOL func (v IpnsValidator) Select(k string, vals [][]byte) (int, error) { From 2cfa5c07573adef8fa7a30a4b125d85c2491f7d7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 5 Jun 2018 04:52:17 -0700 Subject: [PATCH 2343/3817] test to ensure embedding the key in the record works License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@1bbdd92db6a35e7d12e9a8d57f195de5429834bc --- namesys/ipns_validate_test.go | 82 ++++++++++++++++++++++++++++++++--- namesys/validator.go | 4 +- 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index f9cdf024a..606c5944d 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -3,6 +3,7 @@ package namesys import ( "context" "fmt" + "math/rand" "testing" "time" @@ -28,20 +29,21 @@ func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, key validator := IpnsValidator{kbook} - p := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") - entry, err := CreateRoutingEntryData(priv, p, 1, eol) - if err != nil { - t.Fatal(err) - } - data := val if data == nil { + p := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") + entry, err := CreateRoutingEntryData(priv, p, 1, eol) + if err != nil { + t.Fatal(err) + } + data, err = proto.Marshal(entry) if err != nil { t.Fatal(err) } } - err = validator.Validate(key, data) + + err := validator.Validate(key, data) if err != exp { params := fmt.Sprintf("key: %s\neol: %s\n", key, eol) if exp == nil { @@ -74,6 +76,72 @@ func TestValidator(t *testing.T) { testValidatorCase(t, priv, kbook, "/wrong/"+string(id), nil, ts.Add(time.Hour), ErrInvalidPath) } +func TestEmbeddedPubKeyValidate(t *testing.T) { + goodeol := time.Now().Add(time.Hour) + kbook := pstore.NewPeerstore() + + pth := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") + + priv, _, _, ipnsk := genKeys(t) + + entry, err := CreateRoutingEntryData(priv, pth, 1, goodeol) + if err != nil { + t.Fatal(err) + } + + dataNoKey, err := proto.Marshal(entry) + if err != nil { + t.Fatal(err) + } + + testValidatorCase(t, priv, kbook, ipnsk, dataNoKey, goodeol, ErrPublicKeyNotFound) + + pubkb, err := priv.GetPublic().Bytes() + if err != nil { + t.Fatal(err) + } + + entry.PubKey = pubkb + + dataWithKey, err := proto.Marshal(entry) + if err != nil { + t.Fatal(err) + } + + testValidatorCase(t, priv, kbook, ipnsk, dataWithKey, goodeol, nil) +} + +func TestPeerIDPubKeyValidate(t *testing.T) { + goodeol := time.Now().Add(time.Hour) + kbook := pstore.NewPeerstore() + + pth := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") + + sk, pk, err := ci.GenerateEd25519Key(rand.New(rand.NewSource(42))) + if err != nil { + t.Fatal(err) + } + + pid, err := peer.IDFromPublicKey(pk) + if err != nil { + t.Fatal(err) + } + + ipnsk := "/ipns/" + string(pid) + + entry, err := CreateRoutingEntryData(sk, pth, 1, goodeol) + if err != nil { + t.Fatal(err) + } + + dataNoKey, err := proto.Marshal(entry) + if err != nil { + t.Fatal(err) + } + + testValidatorCase(t, sk, kbook, ipnsk, dataNoKey, goodeol, nil) +} + func TestResolverValidation(t *testing.T) { ctx := context.Background() rid := testutil.RandIdentityOrFatal(t) diff --git a/namesys/validator.go b/namesys/validator.go index 852276f17..9d1e65e91 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -70,7 +70,7 @@ func (v IpnsValidator) Validate(key string, value []byte) error { pubk, err := v.getPublicKey(pid, entry) if err != nil { - return fmt.Errorf("getting public key failed: %s", err) + return err } // Check the ipns record signature with the public key @@ -102,7 +102,7 @@ func (v IpnsValidator) getPublicKey(pid peer.ID, entry *pb.IpnsEntry) (ic.PubKey if err != nil { // TODO: i think this counts as a 'malformed record' and should be discarded log.Debugf("public key in ipns record failed to parse: ", err) - return nil, err + return nil, fmt.Errorf("unmarshaling pubkey in record: %s", err) } expPid, err := peer.IDFromPublicKey(pk) if err != nil { From a07ec595788b2ad539e8ea2ea2603770e8b4442f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 5 Jun 2018 07:51:46 -0700 Subject: [PATCH 2344/3817] add tests for pubkey mismatch and bad pubkey License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@3e1116b843a2631086893e668e02a9189b812e62 --- namesys/ipns_validate_test.go | 63 ++++++++++++++++++++++++----------- namesys/validator.go | 5 ++- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index 606c5944d..d7e46f328 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -4,10 +4,12 @@ import ( "context" "fmt" "math/rand" + "strings" "testing" "time" opts "github.com/ipfs/go-ipfs/namesys/opts" + pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" @@ -27,6 +29,25 @@ import ( func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, key string, val []byte, eol time.Time, exp error) { t.Helper() + match := func(t *testing.T, err error) { + t.Helper() + if err != exp { + params := fmt.Sprintf("key: %s\neol: %s\n", key, eol) + if exp == nil { + t.Fatalf("Unexpected error %s for params %s", err, params) + } else if err == nil { + t.Fatalf("Expected error %s but there was no error for params %s", exp, params) + } else { + t.Fatalf("Expected error %s but got %s for params %s", exp, err, params) + } + } + } + + testValidatorCaseMatchFunc(t, priv, kbook, key, val, eol, match) +} + +func testValidatorCaseMatchFunc(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, key string, val []byte, eol time.Time, matchf func(*testing.T, error)) { + t.Helper() validator := IpnsValidator{kbook} data := val @@ -43,17 +64,7 @@ func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, key } } - err := validator.Validate(key, data) - if err != exp { - params := fmt.Sprintf("key: %s\neol: %s\n", key, eol) - if exp == nil { - t.Fatalf("Unexpected error %s for params %s", err, params) - } else if err == nil { - t.Fatalf("Expected error %s but there was no error for params %s", exp, params) - } else { - t.Fatalf("Expected error %s but got %s for params %s", exp, err, params) - } - } + matchf(t, validator.Validate(key, data)) } func TestValidator(t *testing.T) { @@ -76,6 +87,15 @@ func TestValidator(t *testing.T) { testValidatorCase(t, priv, kbook, "/wrong/"+string(id), nil, ts.Add(time.Hour), ErrInvalidPath) } +func mustMarshal(t *testing.T, entry *pb.IpnsEntry) []byte { + t.Helper() + data, err := proto.Marshal(entry) + if err != nil { + t.Fatal(err) + } + return data +} + func TestEmbeddedPubKeyValidate(t *testing.T) { goodeol := time.Now().Add(time.Hour) kbook := pstore.NewPeerstore() @@ -89,12 +109,7 @@ func TestEmbeddedPubKeyValidate(t *testing.T) { t.Fatal(err) } - dataNoKey, err := proto.Marshal(entry) - if err != nil { - t.Fatal(err) - } - - testValidatorCase(t, priv, kbook, ipnsk, dataNoKey, goodeol, ErrPublicKeyNotFound) + testValidatorCase(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, ErrPublicKeyNotFound) pubkb, err := priv.GetPublic().Bytes() if err != nil { @@ -102,13 +117,23 @@ func TestEmbeddedPubKeyValidate(t *testing.T) { } entry.PubKey = pubkb + testValidatorCase(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, nil) + + entry.PubKey = []byte("probably not a public key") + testValidatorCaseMatchFunc(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, func(t *testing.T, err error) { + if !strings.Contains(err.Error(), "unmarshaling pubkey in record:") { + t.Fatal("expected pubkey unmarshaling to fail") + } + }) - dataWithKey, err := proto.Marshal(entry) + opriv, _, _, _ := genKeys(t) + wrongkeydata, err := opriv.GetPublic().Bytes() if err != nil { t.Fatal(err) } - testValidatorCase(t, priv, kbook, ipnsk, dataWithKey, goodeol, nil) + entry.PubKey = wrongkeydata + testValidatorCase(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, ErrPublicKeyMismatch) } func TestPeerIDPubKeyValidate(t *testing.T) { diff --git a/namesys/validator.go b/namesys/validator.go index 9d1e65e91..ff9cc99ee 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -44,6 +44,8 @@ var ErrKeyFormat = errors.New("record key could not be parsed into peer ID") // from the peer store var ErrPublicKeyNotFound = errors.New("public key not found in peer store") +var ErrPublicKeyMismatch = errors.New("public key in record did not match expected pubkey") + type IpnsValidator struct { KeyBook pstore.KeyBook } @@ -104,13 +106,14 @@ func (v IpnsValidator) getPublicKey(pid peer.ID, entry *pb.IpnsEntry) (ic.PubKey log.Debugf("public key in ipns record failed to parse: ", err) return nil, fmt.Errorf("unmarshaling pubkey in record: %s", err) } + expPid, err := peer.IDFromPublicKey(pk) if err != nil { return nil, fmt.Errorf("could not regenerate peerID from pubkey: %s", err) } if pid != expPid { - return nil, fmt.Errorf("pubkey in record did not match expected pubkey") + return nil, ErrPublicKeyMismatch } return pk, nil From 005043b600bf74345a0a4e7347cba5533513f16c Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 5 Jun 2018 08:37:21 -0700 Subject: [PATCH 2345/3817] drop review TODO comment License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@9109ad4921b66dcec79e4877c5b6c5af159eb795 --- namesys/validator.go | 1 - 1 file changed, 1 deletion(-) diff --git a/namesys/validator.go b/namesys/validator.go index ff9cc99ee..94eb912b6 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -102,7 +102,6 @@ func (v IpnsValidator) getPublicKey(pid peer.ID, entry *pb.IpnsEntry) (ic.PubKey if entry.PubKey != nil { pk, err := ic.UnmarshalPublicKey(entry.PubKey) if err != nil { - // TODO: i think this counts as a 'malformed record' and should be discarded log.Debugf("public key in ipns record failed to parse: ", err) return nil, fmt.Errorf("unmarshaling pubkey in record: %s", err) } From 1d22da9f7e9cf6c1e43ea5aa9d0eb9a55bf7b6f3 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 5 Jun 2018 23:55:08 -0700 Subject: [PATCH 2346/3817] update gx imports License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@38509122c60bb6e6cc2622b82500491b4258b34c --- namesys/ipns_validate_test.go | 8 ++++---- namesys/namesys.go | 2 +- namesys/namesys_test.go | 2 +- namesys/publisher.go | 2 +- namesys/publisher_test.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 4 ++-- namesys/validator.go | 2 +- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index 687642b80..546e27069 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -13,14 +13,14 @@ import ( path "github.com/ipfs/go-ipfs/path" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + mockrouting "gx/ipfs/QmPFAxh9UwfqwseVcWkj1Lz1gCHyQ6QuCk5m5XUp6vifkL/go-ipfs-routing/mock" record "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record" - routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" - ropts "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing/options" testutil "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil" + routing "gx/ipfs/QmXijJ3T9MjB2v8xpFDoEX6FqR9u8PkJkzu49TgwJ8Ndr5/go-libp2p-routing" + ropts "gx/ipfs/QmXijJ3T9MjB2v8xpFDoEX6FqR9u8PkJkzu49TgwJ8Ndr5/go-libp2p-routing/options" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - mockrouting "gx/ipfs/QmcE3B6ittYBmctva8Q155LPa1YPcVqg8N7pPcgt9i7iAQ/go-ipfs-routing/mock" + pstore "gx/ipfs/QmZb7hAgQEhW9dBbzBudU39gCeD4zbe6xafD52LUuF4cUN/go-libp2p-peerstore" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" - pstore "gx/ipfs/QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh/go-libp2p-peerstore" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" diff --git a/namesys/namesys.go b/namesys/namesys.go index bbdeb9b86..5f0065396 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -8,8 +8,8 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" - routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" + routing "gx/ipfs/QmXijJ3T9MjB2v8xpFDoEX6FqR9u8PkJkzu49TgwJ8Ndr5/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index a2d8bd01f..922b2342d 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,7 +10,7 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/unixfs" - offroute "gx/ipfs/QmcE3B6ittYBmctva8Q155LPa1YPcVqg8N7pPcgt9i7iAQ/go-ipfs-routing/offline" + offroute "gx/ipfs/QmPFAxh9UwfqwseVcWkj1Lz1gCHyQ6QuCk5m5XUp6vifkL/go-ipfs-routing/offline" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" diff --git a/namesys/publisher.go b/namesys/publisher.go index 80bc10d47..df09e6d40 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -14,7 +14,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" + routing "gx/ipfs/QmXijJ3T9MjB2v8xpFDoEX6FqR9u8PkJkzu49TgwJ8Ndr5/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 67ed6471c..8f625bb8f 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -8,10 +8,10 @@ import ( path "github.com/ipfs/go-ipfs/path" + mockrouting "gx/ipfs/QmPFAxh9UwfqwseVcWkj1Lz1gCHyQ6QuCk5m5XUp6vifkL/go-ipfs-routing/mock" testutil "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil" ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" dshelp "gx/ipfs/QmYJgz1Z5PbBGP7n2XA8uv5sF1EKLfYUjL7kFemVAjMNqC/go-ipfs-ds-help" - mockrouting "gx/ipfs/QmcE3B6ittYBmctva8Q155LPa1YPcVqg8N7pPcgt9i7iAQ/go-ipfs-routing/mock" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 0d927f1ef..b5d1dc474 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -12,9 +12,9 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" + mocknet "gx/ipfs/QmRvoAami8AAf5Yy6jcPq5KqQT1ZCaoi9dF1vdKAghmq9X/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmY6iAoG9DVgZwh5ZRcQEpa2uErAe1Hbei8qXPCjpDS9Ge/go-libp2p/p2p/net/mock" - pstore "gx/ipfs/QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh/go-libp2p-peerstore" + pstore "gx/ipfs/QmZb7hAgQEhW9dBbzBudU39gCeD4zbe6xafD52LUuF4cUN/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 45609e95d..8d700378b 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,8 +8,8 @@ import ( path "github.com/ipfs/go-ipfs/path" + mockrouting "gx/ipfs/QmPFAxh9UwfqwseVcWkj1Lz1gCHyQ6QuCk5m5XUp6vifkL/go-ipfs-routing/mock" testutil "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil" - mockrouting "gx/ipfs/QmcE3B6ittYBmctva8Q155LPa1YPcVqg8N7pPcgt9i7iAQ/go-ipfs-routing/mock" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" diff --git a/namesys/routing.go b/namesys/routing.go index e399b1936..1373d50ce 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -11,12 +11,12 @@ import ( u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" - routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing" + routing "gx/ipfs/QmXijJ3T9MjB2v8xpFDoEX6FqR9u8PkJkzu49TgwJ8Ndr5/go-libp2p-routing" + dht "gx/ipfs/QmYyonQoGb5Gw5VnGqgjKPPm1x3rY9QSquWCZqGKdiwuTw/go-libp2p-kad-dht" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - dht "gx/ipfs/Qme6C1xZFKUQVxvj8Sb7afWiQxzkQt67gq5V2o85pivCjV/go-libp2p-kad-dht" ) var log = logging.Logger("namesys") diff --git a/namesys/validator.go b/namesys/validator.go index 94eb912b6..6a9c330a8 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -7,8 +7,8 @@ import ( "time" pb "github.com/ipfs/go-ipfs/namesys/pb" + pstore "gx/ipfs/QmZb7hAgQEhW9dBbzBudU39gCeD4zbe6xafD52LUuF4cUN/go-libp2p-peerstore" peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" - pstore "gx/ipfs/QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh/go-libp2p-peerstore" ic "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" From 77dda61231923bbc4607fb8adf77835016f3c6c1 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 5 Jun 2018 23:55:08 -0700 Subject: [PATCH 2347/3817] update gx imports License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@0668c2e1550acbca488c67056143dd4b93a91659 --- blockservice/test/mock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index dd458b807..ee093fd36 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -5,8 +5,8 @@ import ( bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" + mockrouting "gx/ipfs/QmPFAxh9UwfqwseVcWkj1Lz1gCHyQ6QuCk5m5XUp6vifkL/go-ipfs-routing/mock" delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" - mockrouting "gx/ipfs/QmcE3B6ittYBmctva8Q155LPa1YPcVqg8N7pPcgt9i7iAQ/go-ipfs-routing/mock" ) // Mocks returns |n| connected mock Blockservices From 516946b7d4f26c79bfd3d94ecbf7c8b57754d43e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 6 Jun 2018 21:36:30 -0700 Subject: [PATCH 2348/3817] reduce log level when we can't republish This is almost never an error, it just means we don't have any connections. We could leave this at Warning but we'd like to be able to turn those on by default at some point. fixes #5029 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@6dc859f10251feb3ceabdc3c2688715fe88a1769 --- namesys/republisher/repub.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index aa1f85647..3e731fbf9 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -72,7 +72,7 @@ func (rp *Republisher) Run(proc goprocess.Process) { timer.Reset(rp.Interval) err := rp.republishEntries(proc) if err != nil { - log.Error("Republisher failed to republish: ", err) + log.Info("republisher failed to republish: ", err) if FailureRetryInterval < rp.Interval { timer.Reset(FailureRetryInterval) } From 0aaacf840917934de1584c22fb3496f4f0b8019e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 8 Jun 2018 22:01:00 -0700 Subject: [PATCH 2349/3817] gx update go-log, sys, go-crypto * go-log * sys * go-crypto License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@a36fd94d3804ea04e45738ee84529200330d6f44 --- unixfs/archive/archive.go | 2 +- unixfs/archive/tar/writer.go | 2 +- unixfs/hamt/hamt.go | 4 ++-- unixfs/hamt/hamt_stress_test.go | 2 +- unixfs/hamt/hamt_test.go | 2 +- unixfs/io/dagreader.go | 2 +- unixfs/io/dirbuilder.go | 4 ++-- unixfs/io/pbdagreader.go | 4 ++-- unixfs/io/resolve.go | 2 +- unixfs/mod/dagmodifier.go | 6 +++--- unixfs/mod/dagmodifier_test.go | 2 +- unixfs/test/utils.go | 10 +++++----- 12 files changed, 21 insertions(+), 21 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index 936b7b8c2..c32715e27 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -11,7 +11,7 @@ import ( tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" uio "github.com/ipfs/go-ipfs/unixfs/io" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" ) // DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 1c00ce674..61ceb2e98 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -15,8 +15,8 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" upb "github.com/ipfs/go-ipfs/unixfs/pb" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // Writer is a utility structure that helps to write diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index c83ec1cea..9d6c00f62 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -30,9 +30,9 @@ import ( upb "github.com/ipfs/go-ipfs/unixfs/pb" bitfield "gx/ipfs/QmTbBs3Y3u5F69XNJzdnnc6SP5GKgcXxCDzx6w8m6piVRT/go-bitfield" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" "gx/ipfs/QmfJHywXQu98UeZtGJBQrPAR6AtmDjjbe3qjTo9piXHPnx/murmur3" ) diff --git a/unixfs/hamt/hamt_stress_test.go b/unixfs/hamt/hamt_stress_test.go index 185e385e1..c5a4fef9b 100644 --- a/unixfs/hamt/hamt_stress_test.go +++ b/unixfs/hamt/hamt_stress_test.go @@ -11,7 +11,7 @@ import ( mdtest "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" ) func getNames(prefix string, count int) []string { diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 9a0e172ac..48a45c49d 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -14,7 +14,7 @@ import ( dagutils "github.com/ipfs/go-ipfs/merkledag/utils" ft "github.com/ipfs/go-ipfs/unixfs" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" ) func shuffle(seed int64, arr []string) { diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index b1841bd74..e3f795732 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -10,8 +10,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" ) // Common errors diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go index e8cbff52d..3a36fe535 100644 --- a/unixfs/io/dirbuilder.go +++ b/unixfs/io/dirbuilder.go @@ -9,8 +9,8 @@ import ( format "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // ShardSplitThreshold specifies how large of an unsharded directory diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index a194dccf4..ce53d6711 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -10,9 +10,9 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // PBDagReader provides a way to easily read the data contained in a dag. diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index 26d360bb3..087d1b12f 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -7,7 +7,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" ) // ResolveUnixfsOnce resolves a single hop of a path through a graph in a diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 12781b219..83caf6408 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -14,10 +14,10 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" + chunker "gx/ipfs/QmR4G4WBNGA5S5pvjFiTkuehstC9769sLAHei8vZernhYR/go-ipfs-chunker" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - chunker "gx/ipfs/QmbGDSVKnYJZrtUnyxwsUpCeuigshNuVFxXCpv13jXecq1/go-ipfs-chunker" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // Common errors diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 41979c613..731322db1 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -13,7 +13,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" testu "github.com/ipfs/go-ipfs/unixfs/test" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ) func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier, opts testu.NodeOpts) []byte { diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 046384679..d92547e9f 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -14,11 +14,11 @@ import ( mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" - chunker "gx/ipfs/QmbGDSVKnYJZrtUnyxwsUpCeuigshNuVFxXCpv13jXecq1/go-ipfs-chunker" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + chunker "gx/ipfs/QmR4G4WBNGA5S5pvjFiTkuehstC9769sLAHei8vZernhYR/go-ipfs-chunker" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // SizeSplitterGen creates a generator. From d75b6f012a7b24857b3ffe8adcf41f45bcf7a8ba Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 8 Jun 2018 22:01:00 -0700 Subject: [PATCH 2350/3817] gx update go-log, sys, go-crypto * go-log * sys * go-crypto License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@9621fbb7298259a3a7601fb30eb823aa8586b98b --- mfs/dir.go | 4 ++-- mfs/file.go | 4 ++-- mfs/mfs_test.go | 12 ++++++------ mfs/ops.go | 4 ++-- mfs/repub_test.go | 4 ++-- mfs/system.go | 6 +++--- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 0cc6e6568..17f09356f 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -15,8 +15,8 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) var ErrNotYetImplemented = errors.New("not yet implemented") diff --git a/mfs/file.go b/mfs/file.go index 3839a279d..403042b14 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -9,8 +9,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - chunker "gx/ipfs/QmbGDSVKnYJZrtUnyxwsUpCeuigshNuVFxXCpv13jXecq1/go-ipfs-chunker" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + chunker "gx/ipfs/QmR4G4WBNGA5S5pvjFiTkuehstC9769sLAHei8vZernhYR/go-ipfs-chunker" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 0fdeed8e5..b051086b0 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -21,12 +21,12 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" - bstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" - chunker "gx/ipfs/QmbGDSVKnYJZrtUnyxwsUpCeuigshNuVFxXCpv13jXecq1/go-ipfs-chunker" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + chunker "gx/ipfs/QmR4G4WBNGA5S5pvjFiTkuehstC9769sLAHei8vZernhYR/go-ipfs-chunker" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + bstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/mfs/ops.go b/mfs/ops.go index e6ad1a3be..6ade2bee0 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -9,8 +9,8 @@ import ( path "github.com/ipfs/go-ipfs/path" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // Mv moves the file or directory at 'src' to 'dst' diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 14eaa3001..cec6f699d 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - ci "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil/ci" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + ci "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil/ci" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index a86ecf735..975d3da67 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -19,9 +19,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" ) var ErrNotExist = errors.New("no such rootfs") From fbbfef87078eb5741c7db405194c7a096876a822 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 8 Jun 2018 22:01:00 -0700 Subject: [PATCH 2351/3817] gx update go-log, sys, go-crypto * go-log * sys * go-crypto License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@e27fc14bb163e1c20c77be3bd28683b5cbed61dc --- namesys/ipns_select_test.go | 2 +- namesys/ipns_validate_test.go | 16 ++++++++-------- namesys/namesys.go | 6 +++--- namesys/namesys_test.go | 2 +- namesys/publisher.go | 6 +++--- namesys/publisher_test.go | 10 +++++----- namesys/republisher/repub.go | 4 ++-- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 6 +++--- namesys/routing.go | 14 +++++++------- namesys/validator.go | 8 ++++---- 11 files changed, 39 insertions(+), 39 deletions(-) diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go index 9ba39ce7f..a0067ada6 100644 --- a/namesys/ipns_select_test.go +++ b/namesys/ipns_select_test.go @@ -9,7 +9,7 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ) diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_validate_test.go index 546e27069..c1ea78899 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_validate_test.go @@ -12,15 +12,15 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - mockrouting "gx/ipfs/QmPFAxh9UwfqwseVcWkj1Lz1gCHyQ6QuCk5m5XUp6vifkL/go-ipfs-routing/mock" - record "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record" - testutil "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil" - routing "gx/ipfs/QmXijJ3T9MjB2v8xpFDoEX6FqR9u8PkJkzu49TgwJ8Ndr5/go-libp2p-routing" - ropts "gx/ipfs/QmXijJ3T9MjB2v8xpFDoEX6FqR9u8PkJkzu49TgwJ8Ndr5/go-libp2p-routing/options" + record "gx/ipfs/QmPWjVzxHeJdrjp4Jr2R2sPxBrMbBgGPWQtKwCKHHCBF7x/go-libp2p-record" + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + testutil "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil" + routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing" + ropts "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing/options" + peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - pstore "gx/ipfs/QmZb7hAgQEhW9dBbzBudU39gCeD4zbe6xafD52LUuF4cUN/go-libp2p-peerstore" - peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" + pstore "gx/ipfs/QmZhsmorLpD9kmQ4ynbAu4vbKv2goMUnXazwGA4gnWHDjB/go-libp2p-peerstore" + mockrouting "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/mock" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" diff --git a/namesys/namesys.go b/namesys/namesys.go index 5f0065396..99780cc88 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -8,11 +8,11 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" + mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - routing "gx/ipfs/QmXijJ3T9MjB2v8xpFDoEX6FqR9u8PkJkzu49TgwJ8Ndr5/go-libp2p-routing" + peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" - peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 922b2342d..b4f35c0de 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,7 +10,7 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/unixfs" - offroute "gx/ipfs/QmPFAxh9UwfqwseVcWkj1Lz1gCHyQ6QuCk5m5XUp6vifkL/go-ipfs-routing/offline" + offroute "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/offline" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" diff --git a/namesys/publisher.go b/namesys/publisher.go index df09e6d40..344004f24 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -13,10 +13,10 @@ import ( pin "github.com/ipfs/go-ipfs/pin" ft "github.com/ipfs/go-ipfs/unixfs" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - routing "gx/ipfs/QmXijJ3T9MjB2v8xpFDoEX6FqR9u8PkJkzu49TgwJ8Ndr5/go-libp2p-routing" + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing" + peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsquery "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 8f625bb8f..67f6be322 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -8,11 +8,11 @@ import ( path "github.com/ipfs/go-ipfs/path" - mockrouting "gx/ipfs/QmPFAxh9UwfqwseVcWkj1Lz1gCHyQ6QuCk5m5XUp6vifkL/go-ipfs-routing/mock" - testutil "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil" - ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" - dshelp "gx/ipfs/QmYJgz1Z5PbBGP7n2XA8uv5sF1EKLfYUjL7kFemVAjMNqC/go-ipfs-ds-help" - peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" + dshelp "gx/ipfs/QmNP2u7bofwUQptHQGPfabGWtTCbxhNLSZKqbf1uzsup9V/go-ipfs-ds-help" + testutil "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil" + ma "gx/ipfs/QmUxSEGbv2nmYNnfXi7839wwQqTN3kwQeUxe8dTjZWZs7J/go-multiaddr" + peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" + mockrouting "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/mock" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index aa1f85647..a0286f345 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -12,9 +12,9 @@ import ( goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" + peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" + logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" ic "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index b5d1dc474..9cc5a940e 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -12,9 +12,9 @@ import ( . "github.com/ipfs/go-ipfs/namesys/republisher" path "github.com/ipfs/go-ipfs/path" - mocknet "gx/ipfs/QmRvoAami8AAf5Yy6jcPq5KqQT1ZCaoi9dF1vdKAghmq9X/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - pstore "gx/ipfs/QmZb7hAgQEhW9dBbzBudU39gCeD4zbe6xafD52LUuF4cUN/go-libp2p-peerstore" + mocknet "gx/ipfs/QmUEAR2pS7fP1GPseS3i8MWFyENs7oDp4CZrgn8FCjbsBu/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmZhsmorLpD9kmQ4ynbAu4vbKv2goMUnXazwGA4gnWHDjB/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 8d700378b..9dd566404 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,9 +8,9 @@ import ( path "github.com/ipfs/go-ipfs/path" - mockrouting "gx/ipfs/QmPFAxh9UwfqwseVcWkj1Lz1gCHyQ6QuCk5m5XUp6vifkL/go-ipfs-routing/mock" - testutil "gx/ipfs/QmUJzxQQ2kzwQubsMqBTr1NGDpLfh7pGA2E1oaJULcKDPq/go-testutil" - peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" + testutil "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil" + peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" + mockrouting "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/mock" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/namesys/routing.go b/namesys/routing.go index 1373d50ce..c10464a1b 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -9,14 +9,14 @@ import ( pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" - routing "gx/ipfs/QmXijJ3T9MjB2v8xpFDoEX6FqR9u8PkJkzu49TgwJ8Ndr5/go-libp2p-routing" - dht "gx/ipfs/QmYyonQoGb5Gw5VnGqgjKPPm1x3rY9QSquWCZqGKdiwuTw/go-libp2p-kad-dht" + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing" + peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" - peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + dht "gx/ipfs/QmagBkuFfySAMouyXeiy8XjV1GyfNAgTCuVYGF9z3Z4Vvc/go-libp2p-kad-dht" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" ) var log = logging.Logger("namesys") diff --git a/namesys/validator.go b/namesys/validator.go index 6a9c330a8..f947ef046 100644 --- a/namesys/validator.go +++ b/namesys/validator.go @@ -7,12 +7,12 @@ import ( "time" pb "github.com/ipfs/go-ipfs/namesys/pb" - pstore "gx/ipfs/QmZb7hAgQEhW9dBbzBudU39gCeD4zbe6xafD52LUuF4cUN/go-libp2p-peerstore" - peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer" + peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" + pstore "gx/ipfs/QmZhsmorLpD9kmQ4ynbAu4vbKv2goMUnXazwGA4gnWHDjB/go-libp2p-peerstore" ic "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - record "gx/ipfs/QmTUyK82BVPA6LmSzEJpfEunk9uBaQzWtMsNP917tVj4sT/go-libp2p-record" + record "gx/ipfs/QmPWjVzxHeJdrjp4Jr2R2sPxBrMbBgGPWQtKwCKHHCBF7x/go-libp2p-record" + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) From 15ff99015bce734a73dc680ce7e8e4c8a7d450be Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 8 Jun 2018 22:01:00 -0700 Subject: [PATCH 2352/3817] gx update go-log, sys, go-crypto * go-log * sys * go-crypto License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@9b835a79efaecc288a4898e0d3a704a0cd62a7e0 --- blockservice/blockservice.go | 10 +++++----- blockservice/blockservice_test.go | 8 ++++---- blockservice/test/blocks_test.go | 10 +++++----- blockservice/test/mock.go | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index c913d1c58..c39b36d25 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -11,11 +11,11 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/verifcid" - logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" - blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - exchange "gx/ipfs/QmdcAXgEHUueP4A7b5hjabKn2EooeHgMreMvFC249dGCgc/go-ipfs-exchange-interface" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" + exchange "gx/ipfs/QmVSe7YJbPnEmkSUKD3HxSvp8HJoyCU55hQoCMRq7N1jaK/go-ipfs-exchange-interface" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" + logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" ) var log = logging.Logger("blockservice") diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index af9cbe8b5..d3c0bb1e6 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -3,12 +3,12 @@ package blockservice import ( "testing" - offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" - blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" + offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" + butil "gx/ipfs/QmYmE4kxv6uFGaWkeBAFYDuNcxzCn87pzwm6CkBkM9C8BM/go-ipfs-blocksutil" + blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" - butil "gx/ipfs/Qmf951DP11mCoctpyF3ZppPZdo2oAxuNi2vnkVDgHJ8Fqk/go-ipfs-blocksutil" ) func TestWriteThroughWorks(t *testing.T) { diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 2ba445079..745a1c727 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -9,13 +9,13 @@ import ( . "github.com/ipfs/go-ipfs/blockservice" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" - blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) func newObject(data []byte) blocks.Block { diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index ee093fd36..5018a6c8d 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -5,8 +5,8 @@ import ( bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" - mockrouting "gx/ipfs/QmPFAxh9UwfqwseVcWkj1Lz1gCHyQ6QuCk5m5XUp6vifkL/go-ipfs-routing/mock" delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" + mockrouting "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/mock" ) // Mocks returns |n| connected mock Blockservices From 73b43b2101329201f0f2eff4c49be8ba6064ad5a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 8 Jun 2018 22:01:00 -0700 Subject: [PATCH 2353/3817] gx update go-log, sys, go-crypto * go-log * sys * go-crypto License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@8c558a85cc4e2ed3dc2ee903906069c236f6b61c --- filestore/filestore.go | 10 +++++----- filestore/filestore_test.go | 6 +++--- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 5342cfa17..0df41aafb 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -10,12 +10,12 @@ package filestore import ( "context" - logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" - blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" - posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" + posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" + logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 3a67dba1e..43fa263b8 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -9,9 +9,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" - posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index c5e255c6c..1b1b94ea7 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -9,15 +9,15 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" + dshelp "gx/ipfs/QmNP2u7bofwUQptHQGPfabGWtTCbxhNLSZKqbf1uzsup9V/go-ipfs-ds-help" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" - dshelp "gx/ipfs/QmYJgz1Z5PbBGP7n2XA8uv5sF1EKLfYUjL7kFemVAjMNqC/go-ipfs-ds-help" - blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" - posinfo "gx/ipfs/Qmb3jLEFAQrqdVgWUajqEyuuDoavkSq1XQXz6tWdFWF995/go-ipfs-posinfo" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" + posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsns "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/namespace" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index e09b69744..8af7d860b 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,9 +6,9 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - dshelp "gx/ipfs/QmYJgz1Z5PbBGP7n2XA8uv5sF1EKLfYUjL7kFemVAjMNqC/go-ipfs-ds-help" - blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + dshelp "gx/ipfs/QmNP2u7bofwUQptHQGPfabGWtTCbxhNLSZKqbf1uzsup9V/go-ipfs-ds-help" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) From 8c9a3e0f95214b2411ed8d7588b8d63b818ea167 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 8 Jun 2018 22:01:00 -0700 Subject: [PATCH 2354/3817] gx update go-log, sys, go-crypto * go-log * sys * go-crypto License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-path@f9ff47a1e5d33513e88b532b94c45f2b969c1df6 --- path/path.go | 2 +- path/resolver/resolver.go | 6 +++--- path/resolver/resolver_test.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/path/path.go b/path/path.go index ac9348b56..c1b7de2a3 100644 --- a/path/path.go +++ b/path/path.go @@ -6,7 +6,7 @@ import ( "path" "strings" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) var ( diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index d4e058829..a263f1150 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -10,9 +10,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" ) var log = logging.Logger("pathresolv") diff --git a/path/resolver/resolver_test.go b/path/resolver/resolver_test.go index 79a857cb6..1c2e0e6b9 100644 --- a/path/resolver/resolver_test.go +++ b/path/resolver/resolver_test.go @@ -10,8 +10,8 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/path/resolver" - util "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" ) func randNode() *merkledag.ProtoNode { From 32bfa358b98a988454ee4ceabab0b417ae32e54e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 8 Jun 2018 22:01:00 -0700 Subject: [PATCH 2355/3817] gx update go-log, sys, go-crypto * go-log * sys * go-crypto License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@bbc8c1405201ecd8f4c43d144ef0bd14e853ab74 --- ipld/merkledag/coding.go | 6 +++--- ipld/merkledag/errservice.go | 4 ++-- ipld/merkledag/merkledag.go | 8 ++++---- ipld/merkledag/merkledag_test.go | 10 +++++----- ipld/merkledag/node.go | 6 +++--- ipld/merkledag/node_test.go | 2 +- ipld/merkledag/raw.go | 8 ++++---- ipld/merkledag/readonly.go | 2 +- ipld/merkledag/readonly_test.go | 4 ++-- ipld/merkledag/rwservice.go | 4 ++-- ipld/merkledag/session.go | 2 +- ipld/merkledag/test/utils.go | 6 +++--- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/traverse/traverse_test.go | 2 +- ipld/merkledag/utils/diff.go | 4 ++-- ipld/merkledag/utils/diffenum.go | 4 ++-- ipld/merkledag/utils/diffenum_test.go | 4 ++-- ipld/merkledag/utils/utils.go | 6 +++--- ipld/merkledag/utils/utils_test.go | 4 ++-- 19 files changed, 44 insertions(+), 44 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index f76631b2e..92a7f135e 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -5,12 +5,12 @@ import ( "sort" "strings" - "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" pb "github.com/ipfs/go-ipfs/merkledag/pb" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/errservice.go b/ipld/merkledag/errservice.go index 499e54f59..b55172c44 100644 --- a/ipld/merkledag/errservice.go +++ b/ipld/merkledag/errservice.go @@ -3,8 +3,8 @@ package merkledag import ( "context" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // ErrorService implements ipld.DAGService, returning 'Err' for every call. diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index d8b133658..a59e3469e 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -8,10 +8,10 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" - ipldcbor "gx/ipfs/QmNRz7BDWfdFNVLt7AVvmRefkrURD25EeoipcXqo6yoXU1/go-ipld-cbor" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + ipldcbor "gx/ipfs/QmSF1Ksgn5d7JCTBt4e1yp4wzs6tpYyweCZ4PcDYp3tNeK/go-ipld-cbor" + blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // TODO: We should move these registrations elsewhere. Really, most of the IPLD diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index e5ad8ee61..460df81be 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -19,11 +19,11 @@ import ( mdpb "github.com/ipfs/go-ipfs/merkledag/pb" dstest "github.com/ipfs/go-ipfs/merkledag/test" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" - blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) func TestNode(t *testing.T) { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 5ff66e76c..33fa02d69 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -5,9 +5,9 @@ import ( "encoding/json" "fmt" - mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // Common errors diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index 273aaa5b4..61277bf0b 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -8,7 +8,7 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" ) func TestRemoveLink(t *testing.T) { diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index d0d37b2e6..b9338fe9b 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -2,11 +2,11 @@ package merkledag import ( "fmt" - "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format" + "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" - u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // RawNode represents a node which only contains data. diff --git a/ipld/merkledag/readonly.go b/ipld/merkledag/readonly.go index 1fd48eff9..03a46d0f1 100644 --- a/ipld/merkledag/readonly.go +++ b/ipld/merkledag/readonly.go @@ -3,7 +3,7 @@ package merkledag import ( "fmt" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" ) // ErrReadOnly is used when a read-only datastructure is written to. diff --git a/ipld/merkledag/readonly_test.go b/ipld/merkledag/readonly_test.go index 86ea86cda..43313ecf5 100644 --- a/ipld/merkledag/readonly_test.go +++ b/ipld/merkledag/readonly_test.go @@ -7,8 +7,8 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" dstest "github.com/ipfs/go-ipfs/merkledag/test" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) func TestReadonlyProperties(t *testing.T) { diff --git a/ipld/merkledag/rwservice.go b/ipld/merkledag/rwservice.go index eb0c19b1c..44303fc3f 100644 --- a/ipld/merkledag/rwservice.go +++ b/ipld/merkledag/rwservice.go @@ -3,8 +3,8 @@ package merkledag import ( "context" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // ComboService implements ipld.DAGService, using 'Read' for all fetch methods, diff --git a/ipld/merkledag/session.go b/ipld/merkledag/session.go index fe0df24d0..f2aac1286 100644 --- a/ipld/merkledag/session.go +++ b/ipld/merkledag/session.go @@ -3,7 +3,7 @@ package merkledag import ( "context" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" ) // SessionMaker is an object that can generate a new fetching session. diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index 1166648f6..dfaef609a 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -4,9 +4,9 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" dag "github.com/ipfs/go-ipfs/merkledag" - offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" - blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index 5561fe026..683dfc878 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -5,7 +5,7 @@ import ( "context" "errors" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" ) // Order is an identifier for traversal algorithm orders diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index c9fcb4136..cf0e97614 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -9,7 +9,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagtest "github.com/ipfs/go-ipfs/merkledag/test" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" ) func TestDFSPreNoSkip(t *testing.T) { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 4ba0f48c5..0743d8573 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -7,8 +7,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // These constants define the changes that can be applied to a DAG. diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go index d815102e7..e506e82c7 100644 --- a/ipld/merkledag/utils/diffenum.go +++ b/ipld/merkledag/utils/diffenum.go @@ -6,8 +6,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // DiffEnumerate fetches every object in the graph pointed to by 'to' that is diff --git a/ipld/merkledag/utils/diffenum_test.go b/ipld/merkledag/utils/diffenum_test.go index bc9728fe7..7ded02483 100644 --- a/ipld/merkledag/utils/diffenum_test.go +++ b/ipld/merkledag/utils/diffenum_test.go @@ -8,8 +8,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) func buildNode(name string, desc map[string]ndesc, out map[string]ipld.Node) ipld.Node { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index f2d45d8b0..32515b5b2 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -8,9 +8,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" - bstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + bstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" syncds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index eb440c39c..3f2a6494a 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -8,8 +8,8 @@ import ( mdtest "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) func TestAddLink(t *testing.T) { From 9dfce0ee52ff5df85f14926afb214a41800fee7f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 8 Jun 2018 22:01:00 -0700 Subject: [PATCH 2356/3817] gx update go-log, sys, go-crypto * go-log * sys * go-crypto License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@7185d0db5b5ab07eda6b9b44b0f00e2f1d9d7f89 --- pinning/pinner/gc/gc.go | 10 +++++----- pinning/pinner/pin.go | 6 +++--- pinning/pinner/pin_test.go | 8 ++++---- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 95e706dbc..69ca731d1 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -12,11 +12,11 @@ import ( pin "github.com/ipfs/go-ipfs/pin" "github.com/ipfs/go-ipfs/thirdparty/verifcid" - logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" - offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" - bstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + bstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" + logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" dstore "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index f4024f0c0..348ead7bf 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -12,9 +12,9 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" dutils "github.com/ipfs/go-ipfs/merkledag/utils" - logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 27963c371..0a0f65206 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -8,10 +8,10 @@ import ( bs "github.com/ipfs/go-ipfs/blockservice" mdag "github.com/ipfs/go-ipfs/merkledag" - util "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util" - offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" - blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index d239859ea..67d3c345a 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -12,9 +12,9 @@ import ( "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index a54dd84fc..221487173 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -8,9 +8,9 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" dag "github.com/ipfs/go-ipfs/merkledag" - offline "gx/ipfs/QmYk9mQ4iByLLFzZPGWMnjJof3DQ3QneFFR6ZtNAXd8UvS/go-ipfs-exchange-offline" - blockstore "gx/ipfs/QmayRSLCiM2gWR7Kay8vqu3Yy5mf7yPqocF9ZRgDUPYMcc/go-ipfs-blockstore" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) From e5476a562dd62ec1df38b5c71ebb649f78b57dbc Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 8 Jun 2018 22:01:00 -0700 Subject: [PATCH 2357/3817] gx update go-log, sys, go-crypto * go-log * sys * go-crypto License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-keystore@becc82c9adc2368499b35caae3313cbc2df036ed --- keystore/keystore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index a11b5d5f3..78bca6ecd 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -7,7 +7,7 @@ import ( "path/filepath" "strings" - logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log" + logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ) From 724c6143570accc8e163cc355b46eff0338f52e5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 8 Jun 2018 22:01:00 -0700 Subject: [PATCH 2358/3817] gx update go-log, sys, go-crypto * go-log * sys * go-crypto License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@0e613d93eacb5247a4ca23790a5fe82dde8917dc --- coreiface/coreapi.go | 2 +- coreiface/dag.go | 2 +- coreiface/object.go | 4 ++-- coreiface/options/block.go | 2 +- coreiface/options/dag.go | 2 +- coreiface/path.go | 2 +- coreiface/unixfs.go | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index 9428b3b63..220f8df50 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -5,7 +5,7 @@ package iface import ( "context" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" ) // CoreAPI defines an unified interface to IPFS for Go programs diff --git a/coreiface/dag.go b/coreiface/dag.go index f20c88f25..3c4dc0c3a 100644 --- a/coreiface/dag.go +++ b/coreiface/dag.go @@ -6,7 +6,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" ) // DagAPI specifies the interface to IPLD diff --git a/coreiface/object.go b/coreiface/object.go index 548b15a73..d53f4d214 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -6,8 +6,8 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // ObjectStat provides information about dag nodes diff --git a/coreiface/options/block.go b/coreiface/options/block.go index 55964b2b7..d6da99774 100644 --- a/coreiface/options/block.go +++ b/coreiface/options/block.go @@ -1,7 +1,7 @@ package options import ( - "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" + "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ) type BlockPutSettings struct { diff --git a/coreiface/options/dag.go b/coreiface/options/dag.go index 96eea5b46..b5e6dcea1 100644 --- a/coreiface/options/dag.go +++ b/coreiface/options/dag.go @@ -3,7 +3,7 @@ package options import ( "math" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) type DagPutSettings struct { diff --git a/coreiface/path.go b/coreiface/path.go index b2160b942..929b97bcd 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // Path is a generic wrapper for paths used in the API. A path can be resolved diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index c1b4efa43..11e14cc84 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -4,7 +4,7 @@ import ( "context" "io" - ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format" + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" ) // UnixfsAPI is the basic interface to immutable files in IPFS From 1e5b96437fba9155461a75b8a50aa109e52aee11 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 8 Jun 2018 22:01:00 -0700 Subject: [PATCH 2359/3817] gx update go-log, sys, go-crypto * go-log * sys * go-crypto License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-verifcid@e76065d6e0bb78d7f20c5918af335c0374cc78d2 --- verifcid/validate.go | 4 ++-- verifcid/validate_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/verifcid/validate.go b/verifcid/validate.go index 4af24b4c4..cc4a761bc 100644 --- a/verifcid/validate.go +++ b/verifcid/validate.go @@ -3,8 +3,8 @@ package verifcid import ( "fmt" - mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) var ErrPossiblyInsecureHashFunction = fmt.Errorf("potentially insecure hash functions not allowed") diff --git a/verifcid/validate_test.go b/verifcid/validate_test.go index 21ab09021..740707593 100644 --- a/verifcid/validate_test.go +++ b/verifcid/validate_test.go @@ -3,9 +3,9 @@ package verifcid import ( "testing" - mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash" + mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) func TestValidateCids(t *testing.T) { From 4a1bb6990fccac58f695c82938b4406c488b09c1 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Fri, 8 Jun 2018 12:21:32 -0300 Subject: [PATCH 2360/3817] unixfs: integrate `pb.Data` into `FSNode` To avoid duplicating fields and making the code easier to follow. Remove all of `FSNode` previous fields in favor on a single `pb.Data` structure that is not exported. Accessor methods are added only for the necessary internal fields. This takes up more memory, `pb.Data` is always created inside `FSNode` and it stays there instead of just being created and destroyed during the (un)marshal operations. The removed fields `Data`, `blocksizes` and `Type` had a direct counterpart in the embedded `pb.Data` structure, in contrast (only) the `subtotal` field doesn't have one, it was used as a temporary accumulator to track the `Filesize`, which is now being kept updated on every modification (to ensure the entire `FSNode` is always at a valid state), so `subtotal` could just be removed without the addition of any other field (this temporary accumulator was obscuring how `Filesize` was computed). To keep `Filesize` up to date a method was added (`UpdateFilesize()`) to adjust its value in the two places where the file size could be modified, when changing its data (in `SetData()`, accessor method added) and when adding or removing child nodes (in `AddBlockSize()` and `RemoveBlockSize()`). A constructor method was added (`NewFSNode()`) to initialize the required fields, like `Type` which is explicitly set, this deprecates the previous methodology of just calling `new(FSNode)` and relying in the default value of `pb.Data_DataType` (`Data_Raw`) to avoid an explicit assignment. Also, `Filesize` is initialized to avoid being left with a `nil` value before marshaling empty nodes, which would result in a different hash from previous versions, to be backwards compatible. Previous versions of `GetBytes()` always set the `Filesize` value, even though it is reflected as an `optional` field in the `.proto` file (this may be an inaccurate field rule). Without the duplicated fields the functions `GetBytes()` and `FSNodeFromBytes()` are now reduced to simple `Marshal()` and `Unmarshal()` operations respectively. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-unixfs@3c982f0b2a8481b5d31c474df0ffdc6f7197ffb6 --- unixfs/mod/dagmodifier.go | 2 +- unixfs/unixfs.go | 92 +++++++++++++++++++++++++++------------ unixfs/unixfs_test.go | 5 +-- 3 files changed, 66 insertions(+), 33 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 12781b219..f30898295 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -526,7 +526,7 @@ func dagTruncate(ctx context.Context, n ipld.Node, size uint64, ds ipld.DAGServi var cur uint64 end := 0 var modified ipld.Node - ndata := new(ft.FSNode) + ndata := ft.NewFSNode(ft.TRaw) for i, lnk := range nd.Links() { child, err := lnk.GetNode(ctx, ds) if err != nil { diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index 654de7ff8..3ba01fb0f 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -139,67 +139,101 @@ func DataSize(data []byte) (uint64, error) { } } -// An FSNode represents a filesystem object. +// An FSNode represents a filesystem object using the UnixFS specification. +// +// The `NewFSNode` constructor should be used instead of just calling `new(FSNode)` +// to guarantee that the required (`Type` and `Filesize`) fields in the `format` +// structure are initialized before marshaling (in `GetBytes()`). type FSNode struct { - Data []byte - // total data size for each child - blocksizes []uint64 - - // running sum of blocksizes - subtotal uint64 - - // node type of this node - Type pb.Data_DataType + // UnixFS format defined as a protocol buffers message. + format pb.Data } // FSNodeFromBytes unmarshal a protobuf message onto an FSNode. func FSNodeFromBytes(b []byte) (*FSNode, error) { - pbn := new(pb.Data) - err := proto.Unmarshal(b, pbn) + n := new(FSNode) + err := proto.Unmarshal(b, &n.format) if err != nil { return nil, err } - n := new(FSNode) - n.Data = pbn.Data - n.blocksizes = pbn.Blocksizes - n.subtotal = pbn.GetFilesize() - uint64(len(n.Data)) - n.Type = pbn.GetType() return n, nil } +// NewFSNode creates a new FSNode structure with the given `dataType`. +// +// It initializes the (required) `Type` field (that doesn't have a `Set()` +// accessor so it must be specified at creation), otherwise the `Marshal()` +// method in `GetBytes()` would fail (`required field "Type" not set`). +// +// It also initializes the `Filesize` pointer field to ensure its value +// is never nil before marshaling, this is not a required field but it is +// done to be backwards compatible with previous `go-ipfs` versions hash. +// (If it wasn't initialized there could be cases where `Filesize` could +// have been left at nil, when the `FSNode` was created but no data or +// child nodes were set to adjust it, as is the case in `NewLeaf()`.) +func NewFSNode(dataType pb.Data_DataType) *FSNode { + n := new(FSNode) + n.format.Type = &dataType + + // Initialize by `Filesize` by updating it with a dummy (zero) value. + n.UpdateFilesize(0) + + return n +} + // AddBlockSize adds the size of the next child block of this node func (n *FSNode) AddBlockSize(s uint64) { - n.subtotal += s - n.blocksizes = append(n.blocksizes, s) + n.UpdateFilesize(int64(s)) + n.format.Blocksizes = append(n.format.Blocksizes, s) } // RemoveBlockSize removes the given child block's size. func (n *FSNode) RemoveBlockSize(i int) { - n.subtotal -= n.blocksizes[i] - n.blocksizes = append(n.blocksizes[:i], n.blocksizes[i+1:]...) + n.UpdateFilesize(-int64(n.format.Blocksizes[i])) + n.format.Blocksizes = append(n.format.Blocksizes[:i], n.format.Blocksizes[i+1:]...) } // GetBytes marshals this node as a protobuf message. func (n *FSNode) GetBytes() ([]byte, error) { - pbn := new(pb.Data) - pbn.Type = &n.Type - pbn.Filesize = proto.Uint64(uint64(len(n.Data)) + n.subtotal) - pbn.Blocksizes = n.blocksizes - pbn.Data = n.Data - return proto.Marshal(pbn) + return proto.Marshal(&n.format) } // FileSize returns the total size of this tree. That is, the size of // the data in this node plus the size of all its children. func (n *FSNode) FileSize() uint64 { - return uint64(len(n.Data)) + n.subtotal + return n.format.GetFilesize() } // NumChildren returns the number of child blocks of this node func (n *FSNode) NumChildren() int { - return len(n.blocksizes) + return len(n.format.Blocksizes) +} + +// GetData retrieves the `Data` field from the internal `format`. +func (n *FSNode) GetData() []byte { + return n.format.GetData() +} + +// SetData sets the `Data` field from the internal `format` +// updating its `Filesize`. +func (n *FSNode) SetData(newData []byte) { + n.UpdateFilesize(int64(len(newData) - len(n.GetData()))) + n.format.Data = newData +} + +// UpdateFilesize updates the `Filesize` field from the internal `format` +// by a signed difference (`filesizeDiff`). +// TODO: Add assert to check for `Filesize` > 0? +func (n *FSNode) UpdateFilesize(filesizeDiff int64) { + n.format.Filesize = proto.Uint64(uint64( + int64(n.format.GetFilesize()) + filesizeDiff)) +} + +// GetType retrieves the `Type` field from the internal `format`. +func (n *FSNode) GetType() pb.Data_DataType { + return n.format.GetType() } // Metadata is used to store additional FSNode information. diff --git a/unixfs/unixfs_test.go b/unixfs/unixfs_test.go index 6edc2ca0b..967ee0ca8 100644 --- a/unixfs/unixfs_test.go +++ b/unixfs/unixfs_test.go @@ -10,14 +10,13 @@ import ( ) func TestFSNode(t *testing.T) { - fsn := new(FSNode) - fsn.Type = TFile + fsn := NewFSNode(TFile) for i := 0; i < 16; i++ { fsn.AddBlockSize(100) } fsn.RemoveBlockSize(15) - fsn.Data = make([]byte, 128) + fsn.SetData(make([]byte, 128)) b, err := fsn.GetBytes() if err != nil { From 52e64d8b7c2da43f8a30335764c6b1aa7b27b666 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Fri, 8 Jun 2018 12:21:32 -0300 Subject: [PATCH 2361/3817] unixfs: integrate `pb.Data` into `FSNode` To avoid duplicating fields and making the code easier to follow. Remove all of `FSNode` previous fields in favor on a single `pb.Data` structure that is not exported. Accessor methods are added only for the necessary internal fields. This takes up more memory, `pb.Data` is always created inside `FSNode` and it stays there instead of just being created and destroyed during the (un)marshal operations. The removed fields `Data`, `blocksizes` and `Type` had a direct counterpart in the embedded `pb.Data` structure, in contrast (only) the `subtotal` field doesn't have one, it was used as a temporary accumulator to track the `Filesize`, which is now being kept updated on every modification (to ensure the entire `FSNode` is always at a valid state), so `subtotal` could just be removed without the addition of any other field (this temporary accumulator was obscuring how `Filesize` was computed). To keep `Filesize` up to date a method was added (`UpdateFilesize()`) to adjust its value in the two places where the file size could be modified, when changing its data (in `SetData()`, accessor method added) and when adding or removing child nodes (in `AddBlockSize()` and `RemoveBlockSize()`). A constructor method was added (`NewFSNode()`) to initialize the required fields, like `Type` which is explicitly set, this deprecates the previous methodology of just calling `new(FSNode)` and relying in the default value of `pb.Data_DataType` (`Data_Raw`) to avoid an explicit assignment. Also, `Filesize` is initialized to avoid being left with a `nil` value before marshaling empty nodes, which would result in a different hash from previous versions, to be backwards compatible. Previous versions of `GetBytes()` always set the `Filesize` value, even though it is reflected as an `optional` field in the `.proto` file (this may be an inaccurate field rule). Without the duplicated fields the functions `GetBytes()` and `FSNodeFromBytes()` are now reduced to simple `Marshal()` and `Unmarshal()` operations respectively. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-mfs@27b9ea0211399e073586d9b87b4714ee4d0da639 --- mfs/file.go | 2 +- mfs/mfs_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/file.go b/mfs/file.go index 3839a279d..14b0a65db 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -60,7 +60,7 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { return nil, err } - switch fsn.Type { + switch fsn.GetType() { default: return nil, fmt.Errorf("unsupported fsnode type for 'file'") case ft.TSymlink: diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 0fdeed8e5..6ea8cd7ca 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -852,7 +852,7 @@ func TestFlushing(t *testing.T) { t.Fatal(err) } - if fsnode.Type != ft.TDirectory { + if fsnode.GetType() != ft.TDirectory { t.Fatal("root wasnt a directory") } From 08b564ca962f26cd6dbc3d1832f09d88f3c5413a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 13 Jun 2018 18:12:02 -0700 Subject: [PATCH 2362/3817] implement record validation we should be doing this in the offline router This commit was moved from ipfs/go-ipfs-routing@4a8cbbce65f0ea6d3e6ce99cc4486cb86490f59c --- routing/mock/centralized_client.go | 41 ++++-------------------------- routing/mock/centralized_server.go | 8 +++--- routing/mock/interface.go | 6 +++++ routing/offline/offline.go | 33 ++++++++++++++++++++---- routing/offline/offline_test.go | 11 +++++--- 5 files changed, 51 insertions(+), 48 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index c0d70f94a..e3d488240 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -2,18 +2,12 @@ package mockrouting import ( "context" - "errors" "time" - proto "github.com/gogo/protobuf/proto" cid "github.com/ipfs/go-cid" - ds "github.com/ipfs/go-datastore" - dshelp "github.com/ipfs/go-ipfs-ds-help" - u "github.com/ipfs/go-ipfs-util" logging "github.com/ipfs/go-log" peer "github.com/libp2p/go-libp2p-peer" pstore "github.com/libp2p/go-libp2p-peerstore" - dhtpb "github.com/libp2p/go-libp2p-record/pb" routing "github.com/libp2p/go-libp2p-routing" ropts "github.com/libp2p/go-libp2p-routing/options" "github.com/libp2p/go-testutil" @@ -23,46 +17,21 @@ import ( var log = logging.Logger("mockrouter") type client struct { - datastore ds.Datastore - server server - peer testutil.Identity + vs routing.ValueStore + server server + peer testutil.Identity } // FIXME(brian): is this method meant to simulate putting a value into the network? func (c *client) PutValue(ctx context.Context, key string, val []byte, opts ...ropts.Option) error { log.Debugf("PutValue: %s", key) - rec := new(dhtpb.Record) - rec.Value = val - rec.Key = proto.String(string(key)) - rec.TimeReceived = proto.String(u.FormatRFC3339(time.Now())) - data, err := proto.Marshal(rec) - if err != nil { - return err - } - - return c.datastore.Put(dshelp.NewKeyFromBinary([]byte(key)), data) + return c.vs.PutValue(ctx, key, val, opts...) } // FIXME(brian): is this method meant to simulate getting a value from the network? func (c *client) GetValue(ctx context.Context, key string, opts ...ropts.Option) ([]byte, error) { log.Debugf("GetValue: %s", key) - v, err := c.datastore.Get(dshelp.NewKeyFromBinary([]byte(key))) - if err != nil { - return nil, err - } - - data, ok := v.([]byte) - if !ok { - return nil, errors.New("could not cast value from datastore") - } - - rec := new(dhtpb.Record) - err = proto.Unmarshal(data, rec) - if err != nil { - return nil, err - } - - return rec.GetValue(), nil + return c.vs.GetValue(ctx, key, opts...) } func (c *client) FindProviders(ctx context.Context, key *cid.Cid) ([]pstore.PeerInfo, error) { diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index ab1a985fd..b869a4f43 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -12,6 +12,8 @@ import ( peer "github.com/libp2p/go-libp2p-peer" pstore "github.com/libp2p/go-libp2p-peerstore" "github.com/libp2p/go-testutil" + + offline "github.com/ipfs/go-ipfs-routing/offline" ) // server is the mockrouting.Client's private interface to the routing server @@ -84,8 +86,8 @@ func (rs *s) Client(p testutil.Identity) Client { func (rs *s) ClientWithDatastore(_ context.Context, p testutil.Identity, datastore ds.Datastore) Client { return &client{ - peer: p, - datastore: datastore, - server: rs, + peer: p, + vs: offline.NewOfflineRouter(datastore, MockValidator{}), + server: rs, } } diff --git a/routing/mock/interface.go b/routing/mock/interface.go index c14a7763f..5d4e9f9aa 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -14,6 +14,12 @@ import ( "github.com/libp2p/go-testutil" ) +// MockValidator is a record validator that always returns success. +type MockValidator struct{} + +func (MockValidator) Validate(_ string, _ []byte) error { return nil } +func (MockValidator) Select(_ string, _ [][]byte) (int, error) { return 0, nil } + // Server provides mockrouting Clients type Server interface { Client(p testutil.Identity) Client diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 422af8d61..1be28bcf4 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -3,6 +3,7 @@ package offline import ( + "bytes" "context" "errors" "time" @@ -11,7 +12,6 @@ import ( cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dshelp "github.com/ipfs/go-ipfs-ds-help" - ci "github.com/libp2p/go-libp2p-crypto" "github.com/libp2p/go-libp2p-peer" pstore "github.com/libp2p/go-libp2p-peerstore" record "github.com/libp2p/go-libp2p-record" @@ -27,10 +27,10 @@ var ErrOffline = errors.New("routing system in offline mode") // NewOfflineRouter returns an IpfsRouting implementation which only performs // offline operations. It allows to Put and Get signed dht // records to and from the local datastore. -func NewOfflineRouter(dstore ds.Datastore, privkey ci.PrivKey) routing.IpfsRouting { +func NewOfflineRouter(dstore ds.Datastore, validator record.Validator) routing.IpfsRouting { return &offlineRouting{ datastore: dstore, - sk: privkey, + validator: validator, } } @@ -39,10 +39,28 @@ func NewOfflineRouter(dstore ds.Datastore, privkey ci.PrivKey) routing.IpfsRouti // records to and from the local datastore. type offlineRouting struct { datastore ds.Datastore - sk ci.PrivKey + validator record.Validator } func (c *offlineRouting) PutValue(ctx context.Context, key string, val []byte, _ ...ropts.Option) error { + if err := c.validator.Validate(key, val); err != nil { + return err + } + if old, err := c.GetValue(ctx, key); err == nil { + // be idempotent to be nice. + if bytes.Equal(old, val) { + return nil + } + // check to see if the older record is better + i, err := c.validator.Select(key, [][]byte{val, old}) + if err != nil { + // this shouldn't happen for validated records. + return err + } + if i != 0 { + return errors.New("can't replace a newer record with an older one") + } + } rec := record.MakePutRecord(key, val) data, err := proto.Marshal(rec) if err != nil { @@ -67,8 +85,13 @@ func (c *offlineRouting) GetValue(ctx context.Context, key string, _ ...ropts.Op if err != nil { return nil, err } + val := rec.GetValue() - return rec.GetValue(), nil + err = c.validator.Validate(key, val) + if err != nil { + return nil, err + } + return val, nil } func (c *offlineRouting) FindPeer(ctx context.Context, pid peer.ID) (pstore.PeerInfo, error) { diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index 61670f442..9703bac57 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -12,12 +12,16 @@ import ( mh "github.com/multiformats/go-multihash" ) +type blankValidator struct{} + +func (blankValidator) Validate(_ string, _ []byte) error { return nil } +func (blankValidator) Select(_ string, _ [][]byte) (int, error) { return 0, nil } + func TestOfflineRouterStorage(t *testing.T) { ctx := context.Background() nds := ds.NewMapDatastore() - privkey, _, _ := testutil.RandTestKeyPair(128) - offline := NewOfflineRouter(nds, privkey) + offline := NewOfflineRouter(nds, blankValidator{}) if err := offline.PutValue(ctx, "key", []byte("testing 1 2 3")); err != nil { t.Fatal(err) @@ -55,8 +59,7 @@ func TestOfflineRouterLocal(t *testing.T) { ctx := context.Background() nds := ds.NewMapDatastore() - privkey, _, _ := testutil.RandTestKeyPair(128) - offline := NewOfflineRouter(nds, privkey) + offline := NewOfflineRouter(nds, blankValidator{}) id, _ := testutil.RandPeerID() _, err := offline.FindPeer(ctx, id) From f99530f37a0195f72acaba1fc59fe5f21ad6b731 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 5 Jun 2018 21:24:31 -0400 Subject: [PATCH 2363/3817] Add a special blockstore to support identity hashes. This commit was moved from ipfs/go-ipfs-blockstore@7ba147892bc451221adf2429b79e7b4a2b103917 --- blockstore/idstore.go | 78 +++++++++++++++++++++++ blockstore/idstore_test.go | 127 +++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 blockstore/idstore.go create mode 100644 blockstore/idstore_test.go diff --git a/blockstore/idstore.go b/blockstore/idstore.go new file mode 100644 index 000000000..e86db2a65 --- /dev/null +++ b/blockstore/idstore.go @@ -0,0 +1,78 @@ +package blockstore + +import ( + "context" + + blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + mh "github.com/multiformats/go-multihash" +) + +// idstore wraps a BlockStore to add support for identity hashes +type idstore struct { + bs Blockstore +} + +func IdStore(bs Blockstore) Blockstore { + return &idstore{bs} +} + +func extractContents(k *cid.Cid) (bool, []byte) { + dmh, err := mh.Decode(k.Hash()) + if err != nil || dmh.Code != mh.ID { + return false, nil + } + return true, dmh.Digest +} + +func (b *idstore) DeleteBlock(k *cid.Cid) error { + isId, _ := extractContents(k) + if isId { + return nil + } + return b.bs.DeleteBlock(k) +} + +func (b *idstore) Has(k *cid.Cid) (bool, error) { + isId, _ := extractContents(k) + if isId { + return true, nil + } + return b.bs.Has(k) +} + +func (b *idstore) Get(k *cid.Cid) (blocks.Block, error) { + isId, bdata := extractContents(k) + if isId { + return blocks.NewBlockWithCid(bdata, k) + } + return b.bs.Get(k) +} + +func (b *idstore) Put(bl blocks.Block) error { + isId, _ := extractContents(bl.Cid()) + if isId { + return nil + } + return b.bs.Put(bl) +} + +func (b *idstore) PutMany(bs []blocks.Block) error { + toPut := make([]blocks.Block, 0, len(bs)) + for _, bl := range bs { + isId, _ := extractContents(bl.Cid()) + if isId { + continue + } + toPut = append(toPut, bl) + } + return b.bs.PutMany(toPut) +} + +func (b *idstore) HashOnRead(enabled bool) { + b.bs.HashOnRead(enabled) +} + +func (b *idstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { + return b.bs.AllKeysChan(ctx) +} diff --git a/blockstore/idstore_test.go b/blockstore/idstore_test.go new file mode 100644 index 000000000..6fe0a5e1e --- /dev/null +++ b/blockstore/idstore_test.go @@ -0,0 +1,127 @@ +package blockstore + +import ( + "context" + "testing" + + blk "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + mh "github.com/multiformats/go-multihash" +) + +func createTestStores() (Blockstore, *callbackDatastore) { + cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} + ids := IdStore(NewBlockstore(cd)) + return ids, cd +} + +func TestIdStore(t *testing.T) { + idhash1, _ := cid.NewPrefixV1(cid.Raw, mh.ID).Sum([]byte("idhash1")) + idblock1, _ := blk.NewBlockWithCid([]byte("idhash1"), idhash1) + hash1, _ := cid.NewPrefixV1(cid.Raw, mh.SHA2_256).Sum([]byte("hash1")) + block1, _ := blk.NewBlockWithCid([]byte("hash1"), hash1) + + ids, cb := createTestStores() + + have, _ := ids.Has(idhash1) + if !have { + t.Fatal("Has() failed on idhash") + } + + _, err := ids.Get(idhash1) + if err != nil { + t.Fatalf("Get() failed on idhash: %v", err) + } + + noop := func() {} + failIfPassThough := func() { + t.Fatal("operation on identity hash passed though to datastore") + } + + cb.f = failIfPassThough + err = ids.Put(idblock1) + if err != nil { + t.Fatal(err) + } + + cb.f = noop + err = ids.Put(block1) + if err != nil { + t.Fatalf("Put() failed on normal block: %v", err) + } + + have, _ = ids.Has(hash1) + if !have { + t.Fatal("normal block not added to datastore") + } + + _, err = ids.Get(hash1) + if err != nil { + t.Fatal(err) + } + + cb.f = failIfPassThough + err = ids.DeleteBlock(idhash1) + if err != nil { + t.Fatal(err) + } + + cb.f = noop + err = ids.DeleteBlock(hash1) + if err != nil { + t.Fatal(err) + } + + have, _ = ids.Has(hash1) + if have { + t.Fatal("normal block not deleted from datastore") + } + + idhash2, _ := cid.NewPrefixV1(cid.Raw, mh.ID).Sum([]byte("idhash2")) + idblock2, _ := blk.NewBlockWithCid([]byte("idhash2"), idhash2) + hash2, _ := cid.NewPrefixV1(cid.Raw, mh.SHA2_256).Sum([]byte("hash2")) + block2, _ := blk.NewBlockWithCid([]byte("hash2"), hash2) + + cb.f = failIfPassThough + err = ids.PutMany([]blk.Block{idblock1, idblock2}) + if err != nil { + t.Fatal(err) + } + + opCount := 0 + cb.f = func() { + opCount++ + } + + err = ids.PutMany([]blk.Block{block1, block2}) + if err != nil { + t.Fatal(err) + } + if opCount != 4 { + // one call to Has and Put for each Cid + t.Fatalf("expected exactly 4 operations got %d", opCount) + } + + opCount = 0 + err = ids.PutMany([]blk.Block{idblock1, block1}) + if err != nil { + t.Fatal(err) + } + if opCount != 1 { + // just one call to Put from the normal (non-id) block + t.Fatalf("expected exactly 1 operations got %d", opCount) + } + + ch, err := ids.AllKeysChan(context.TODO()) + cnt := 0 + for c := range ch { + cnt++ + if c.Prefix().MhType == mh.ID { + t.Fatalf("block with identity hash found in blockstore") + } + } + if cnt != 2 { + t.Fatalf("expected exactly two keys returned by AllKeysChan got %d", cnt) + } +} From 18cbdf84fcf365b9cabf174b3f8199fe41099b33 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 5 Jun 2018 22:01:52 -0400 Subject: [PATCH 2364/3817] IdStore => NewIdStore This commit was moved from ipfs/go-ipfs-blockstore@4da9b7e9a9dc0f8c2d9dd729a4876cb4b82e7433 --- blockstore/idstore.go | 2 +- blockstore/idstore_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blockstore/idstore.go b/blockstore/idstore.go index e86db2a65..5b31b3f8b 100644 --- a/blockstore/idstore.go +++ b/blockstore/idstore.go @@ -13,7 +13,7 @@ type idstore struct { bs Blockstore } -func IdStore(bs Blockstore) Blockstore { +func NewIdStore(bs Blockstore) Blockstore { return &idstore{bs} } diff --git a/blockstore/idstore_test.go b/blockstore/idstore_test.go index 6fe0a5e1e..5a8861990 100644 --- a/blockstore/idstore_test.go +++ b/blockstore/idstore_test.go @@ -12,7 +12,7 @@ import ( func createTestStores() (Blockstore, *callbackDatastore) { cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} - ids := IdStore(NewBlockstore(cd)) + ids := NewIdStore(NewBlockstore(cd)) return ids, cd } From f7b51203e7926328c17ea1aea93185cbc0e34922 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 15 Jun 2018 12:37:58 -0700 Subject: [PATCH 2365/3817] initial commit This commit was moved from ipfs/go-ipns@37f90cd1ed9ffa74c5af1e766bd3415f139f10dd --- ipns/Makefile | 9 +++ ipns/README.md | 31 ++++++++ ipns/errors.go | 37 +++++++++ ipns/ipns.go | 162 ++++++++++++++++++++++++++++++++++++++ ipns/pb/ipns.pb.go | 127 ++++++++++++++++++++++++++++++ ipns/pb/ipns.proto | 23 ++++++ ipns/record.go | 126 ++++++++++++++++++++++++++++++ ipns/select_test.go | 126 ++++++++++++++++++++++++++++++ ipns/validate_test.go | 175 ++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 816 insertions(+) create mode 100644 ipns/Makefile create mode 100644 ipns/README.md create mode 100644 ipns/errors.go create mode 100644 ipns/ipns.go create mode 100644 ipns/pb/ipns.pb.go create mode 100644 ipns/pb/ipns.proto create mode 100644 ipns/record.go create mode 100644 ipns/select_test.go create mode 100644 ipns/validate_test.go diff --git a/ipns/Makefile b/ipns/Makefile new file mode 100644 index 000000000..54152565e --- /dev/null +++ b/ipns/Makefile @@ -0,0 +1,9 @@ +export IPFS_API ?= v04x.ipfs.io + +gx: + go get -u github.com/whyrusleeping/gx + go get -u github.com/whyrusleeping/gx-go + +deps: gx + gx --verbose install --global + gx-go rewrite diff --git a/ipns/README.md b/ipns/README.md new file mode 100644 index 000000000..ca2dbd7ea --- /dev/null +++ b/ipns/README.md @@ -0,0 +1,31 @@ +# go-ipns + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) +[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![GoDoc](https://godoc.org/github.com/ipfs/go-datastore?status.svg)](https://godoc.org/github.com/ipfs/go-ipns) + +> ipns record definitions + +This package contains all of components necessary to create, understand, and +validate IPNS records. + +## Documentation + +https://godoc.org/github.com/ipfs/go-ipns + +## Contribute + +Feel free to join in. All welcome. Open an [issue](https://github.com/ipfs/go-ipns/issues)! + +This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). + +### Want to hack on IPFS? + +[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/contributing.md) + +## License + +MIT + diff --git a/ipns/errors.go b/ipns/errors.go new file mode 100644 index 000000000..ebcd4e263 --- /dev/null +++ b/ipns/errors.go @@ -0,0 +1,37 @@ +package ipns + +import ( + "errors" +) + +// ErrExpiredRecord should be returned when an ipns record is +// invalid due to being too old +var ErrExpiredRecord = errors.New("expired record") + +// ErrUnrecognizedValidity is returned when an IpnsRecord has an +// unknown validity type. +var ErrUnrecognizedValidity = errors.New("unrecognized validity type") + +// ErrInvalidPath should be returned when an ipns record path +// is not in a valid format +var ErrInvalidPath = errors.New("record path invalid") + +// ErrSignature should be returned when an ipns record fails +// signature verification +var ErrSignature = errors.New("record signature verification failed") + +// ErrKeyFormat should be returned when an ipns record key is +// incorrectly formatted (not a peer ID) +var ErrKeyFormat = errors.New("record key could not be parsed into peer ID") + +// ErrPublicKeyNotFound should be returned when the public key +// corresponding to the ipns record path cannot be retrieved +// from the peer store +var ErrPublicKeyNotFound = errors.New("public key not found in peer store") + +// ErrPublicKeyMismatch should be returned when the public key embedded in the +// record doesn't match the expected public key. +var ErrPublicKeyMismatch = errors.New("public key in record did not match expected pubkey") + +// ErrBadRecord should be returned when an ipns record cannot be unmarshalled +var ErrBadRecord = errors.New("record could not be unmarshalled") diff --git a/ipns/ipns.go b/ipns/ipns.go new file mode 100644 index 000000000..b3d374abf --- /dev/null +++ b/ipns/ipns.go @@ -0,0 +1,162 @@ +package ipns + +import ( + "bytes" + "fmt" + "time" + + proto "github.com/gogo/protobuf/proto" + u "github.com/ipfs/go-ipfs-util" + ic "github.com/libp2p/go-libp2p-crypto" + peer "github.com/libp2p/go-libp2p-peer" + + pb "github.com/ipfs/go-ipns/pb" +) + +// Create creates a new IPNS entry and signs it with the given private key. +// +// This function does not embed the public key. If you want to do that, use +// `EmbedPublicKey`. +func Create(sk ic.PrivKey, val []byte, seq uint64, eol time.Time) (*pb.IpnsEntry, error) { + entry := new(pb.IpnsEntry) + + entry.Value = val + typ := pb.IpnsEntry_EOL + entry.ValidityType = &typ + entry.Sequence = proto.Uint64(seq) + entry.Validity = []byte(u.FormatRFC3339(eol)) + + sig, err := sk.Sign(ipnsEntryDataForSig(entry)) + if err != nil { + return nil, err + } + entry.Signature = sig + + return entry, nil +} + +// Validates validates the given IPNS entry against the given public key. +func Validate(pk ic.PubKey, entry *pb.IpnsEntry) error { + // Check the ipns record signature with the public key + if ok, err := pk.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok { + return ErrSignature + } + + // Check that record has not expired + switch entry.GetValidityType() { + case pb.IpnsEntry_EOL: + t, err := u.ParseRFC3339(string(entry.GetValidity())) + if err != nil { + return err + } + if time.Now().After(t) { + return ErrExpiredRecord + } + default: + return ErrUnrecognizedValidity + } + return nil +} + +// EmbedPublicKey embeds the given public key in the given ipns entry. While not +// strictly required, some nodes (e.g., DHT servers) may reject IPNS entries +// that don't embed their public keys as they may not be able to validate them +// efficiently. +func EmbedPublicKey(pk ic.PubKey, entry *pb.IpnsEntry) error { + id, err := peer.IDFromPublicKey(pk) + if err != nil { + return err + } + extraced, err := id.ExtractPublicKey() + if err != nil { + return err + } + if extraced != nil { + return nil + } + pkBytes, err := pk.Bytes() + if err != nil { + return err + } + entry.PubKey = pkBytes + return nil +} + +// ExtractPublicKey extracts a public key matching `pid` from the IPNS record, +// if possible. +// +// This function returns (nil, nil) when no public key can be extracted and +// nothing is malformed. +func ExtractPublicKey(pid peer.ID, entry *pb.IpnsEntry) (ic.PubKey, error) { + if entry.PubKey != nil { + pk, err := ic.UnmarshalPublicKey(entry.PubKey) + if err != nil { + return nil, fmt.Errorf("unmarshaling pubkey in record: %s", err) + } + + expPid, err := peer.IDFromPublicKey(pk) + if err != nil { + return nil, fmt.Errorf("could not regenerate peerID from pubkey: %s", err) + } + + if pid != expPid { + return nil, ErrPublicKeyMismatch + } + return pk, nil + } + + return pid.ExtractPublicKey() +} + +// Compare compares two IPNS entries. It returns: +// +// * -1 if a is older than b +// * 0 if a and b cannot be ordered (this doesn't mean that they are equal) +// * +1 if a is newer than b +// +// It returns an error when either a or b are malformed. +// +// NOTE: It *does not* validate the records, the caller is responsible for calling +// `Validate` first. +// +// NOTE: If a and b cannot be ordered by this function, you can determine their +// order by comparing their serialized byte representations (using +// `bytes.Compare`). You must do this if you are implementing a libp2p record +// validator (or you can just use the one provided for you by this package). +func Compare(a, b *pb.IpnsEntry) (int, error) { + as := a.GetSequence() + bs := b.GetSequence() + + if as > bs { + return 1, nil + } else if as < bs { + return -1, nil + } + + at, err := u.ParseRFC3339(string(a.GetValidity())) + if err != nil { + return 0, err + } + + bt, err := u.ParseRFC3339(string(b.GetValidity())) + if err != nil { + return 0, err + } + + if at.After(bt) { + return 1, nil + } else if bt.After(at) { + return -1, nil + } + + return 0, nil +} + +func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte { + return bytes.Join([][]byte{ + e.Value, + e.Validity, + []byte(fmt.Sprint(e.GetValidityType())), + }, + []byte{}) +} diff --git a/ipns/pb/ipns.pb.go b/ipns/pb/ipns.pb.go new file mode 100644 index 000000000..692ba564c --- /dev/null +++ b/ipns/pb/ipns.pb.go @@ -0,0 +1,127 @@ +// Code generated by protoc-gen-gogo. +// source: pb/ipns.proto +// DO NOT EDIT! + +/* +Package ipns_pb is a generated protocol buffer package. + +It is generated from these files: + pb/ipns.proto + +It has these top-level messages: + IpnsEntry +*/ +package ipns_pb + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +type IpnsEntry_ValidityType int32 + +const ( + // setting an EOL says "this record is valid until..." + IpnsEntry_EOL IpnsEntry_ValidityType = 0 +) + +var IpnsEntry_ValidityType_name = map[int32]string{ + 0: "EOL", +} +var IpnsEntry_ValidityType_value = map[string]int32{ + "EOL": 0, +} + +func (x IpnsEntry_ValidityType) Enum() *IpnsEntry_ValidityType { + p := new(IpnsEntry_ValidityType) + *p = x + return p +} +func (x IpnsEntry_ValidityType) String() string { + return proto.EnumName(IpnsEntry_ValidityType_name, int32(x)) +} +func (x *IpnsEntry_ValidityType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(IpnsEntry_ValidityType_value, data, "IpnsEntry_ValidityType") + if err != nil { + return err + } + *x = IpnsEntry_ValidityType(value) + return nil +} + +type IpnsEntry struct { + Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"` + Signature []byte `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"` + ValidityType *IpnsEntry_ValidityType `protobuf:"varint,3,opt,name=validityType,enum=ipns.pb.IpnsEntry_ValidityType" json:"validityType,omitempty"` + Validity []byte `protobuf:"bytes,4,opt,name=validity" json:"validity,omitempty"` + Sequence *uint64 `protobuf:"varint,5,opt,name=sequence" json:"sequence,omitempty"` + Ttl *uint64 `protobuf:"varint,6,opt,name=ttl" json:"ttl,omitempty"` + // in order for nodes to properly validate a record upon receipt, they need the public + // key associated with it. For old RSA keys, its easiest if we just send this as part of + // the record itself. For newer ed25519 keys, the public key can be embedded in the + // peerID, making this field unnecessary. + PubKey []byte `protobuf:"bytes,7,opt,name=pubKey" json:"pubKey,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *IpnsEntry) Reset() { *m = IpnsEntry{} } +func (m *IpnsEntry) String() string { return proto.CompactTextString(m) } +func (*IpnsEntry) ProtoMessage() {} + +func (m *IpnsEntry) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func (m *IpnsEntry) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +func (m *IpnsEntry) GetValidityType() IpnsEntry_ValidityType { + if m != nil && m.ValidityType != nil { + return *m.ValidityType + } + return IpnsEntry_EOL +} + +func (m *IpnsEntry) GetValidity() []byte { + if m != nil { + return m.Validity + } + return nil +} + +func (m *IpnsEntry) GetSequence() uint64 { + if m != nil && m.Sequence != nil { + return *m.Sequence + } + return 0 +} + +func (m *IpnsEntry) GetTtl() uint64 { + if m != nil && m.Ttl != nil { + return *m.Ttl + } + return 0 +} + +func (m *IpnsEntry) GetPubKey() []byte { + if m != nil { + return m.PubKey + } + return nil +} + +func init() { + proto.RegisterType((*IpnsEntry)(nil), "ipns.pb.IpnsEntry") + proto.RegisterEnum("ipns.pb.IpnsEntry_ValidityType", IpnsEntry_ValidityType_name, IpnsEntry_ValidityType_value) +} diff --git a/ipns/pb/ipns.proto b/ipns/pb/ipns.proto new file mode 100644 index 000000000..a59cfcf29 --- /dev/null +++ b/ipns/pb/ipns.proto @@ -0,0 +1,23 @@ +package ipns.pb; + +message IpnsEntry { + enum ValidityType { + // setting an EOL says "this record is valid until..." + EOL = 0; + } + required bytes value = 1; + required bytes signature = 2; + + optional ValidityType validityType = 3; + optional bytes validity = 4; + + optional uint64 sequence = 5; + + optional uint64 ttl = 6; + + // in order for nodes to properly validate a record upon receipt, they need the public + // key associated with it. For old RSA keys, its easiest if we just send this as part of + // the record itself. For newer ed25519 keys, the public key can be embedded in the + // peerID, making this field unnecessary. + optional bytes pubKey = 7; +} diff --git a/ipns/record.go b/ipns/record.go new file mode 100644 index 000000000..24a75dacf --- /dev/null +++ b/ipns/record.go @@ -0,0 +1,126 @@ +package ipns + +import ( + "bytes" + "errors" + + proto "github.com/gogo/protobuf/proto" + logging "github.com/ipfs/go-log" + ic "github.com/libp2p/go-libp2p-crypto" + peer "github.com/libp2p/go-libp2p-peer" + pstore "github.com/libp2p/go-libp2p-peerstore" + record "github.com/libp2p/go-libp2p-record" + + pb "github.com/ipfs/go-ipns/pb" +) + +var log = logging.Logger("ipns") + +var _ record.Validator = Validator{} + +// RecordKey returns the libp2p record key for a given peer ID. +func RecordKey(pid peer.ID) string { + return "/ipns/" + string(pid) +} + +// Validator is an IPNS record validator that satisfies the libp2p record +// validator interface. +type Validator struct { + // KeyBook, if non-nil, will be used to lookup keys for validating IPNS + // records. + KeyBook pstore.KeyBook +} + +// Validate validates an IPNS record. +func (v Validator) Validate(key string, value []byte) error { + ns, pidString, err := record.SplitKey(key) + if err != nil || ns != "ipns" { + return ErrInvalidPath + } + + // Parse the value into an IpnsEntry + entry := new(pb.IpnsEntry) + err = proto.Unmarshal(value, entry) + if err != nil { + return ErrBadRecord + } + + // Get the public key defined by the ipns path + pid, err := peer.IDFromString(pidString) + if err != nil { + log.Debugf("failed to parse ipns record key %s into peer ID", pidString) + return ErrKeyFormat + } + + pubk, err := v.getPublicKey(pid, entry) + if err != nil { + return err + } + + return Validate(pubk, entry) +} + +func (v Validator) getPublicKey(pid peer.ID, entry *pb.IpnsEntry) (ic.PubKey, error) { + pk, err := ExtractPublicKey(pid, entry) + if err != nil { + return nil, err + } + if pk != nil { + return pk, nil + } + + if v.KeyBook == nil { + log.Debugf("public key with hash %s not found in IPNS record and no peer store provided", pid) + return nil, ErrPublicKeyNotFound + } + + pubk := v.KeyBook.PubKey(pid) + if pubk == nil { + log.Debugf("public key with hash %s not found in peer store", pid) + return nil, ErrPublicKeyNotFound + } + return pubk, nil +} + +// Select selects the best record by checking which has the highest sequence +// number and latest EOL. +// +// This function returns an error if any of the records fail to parse. Validate +// your records first! +func (v Validator) Select(k string, vals [][]byte) (int, error) { + var recs []*pb.IpnsEntry + for _, v := range vals { + e := new(pb.IpnsEntry) + if err := proto.Unmarshal(v, e); err != nil { + return -1, err + } + recs = append(recs, e) + } + + return selectRecord(recs, vals) +} + +func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) { + switch len(recs) { + case 0: + return -1, errors.New("no usable records in given set") + case 1: + return 0, nil + } + + var i int + for j := 1; j < len(recs); j++ { + cmp, err := Compare(recs[i], recs[j]) + if err != nil { + return -1, err + } + if cmp == 0 { + cmp = bytes.Compare(vals[i], vals[j]) + } + if cmp < 0 { + i = j + } + } + + return i, nil +} diff --git a/ipns/select_test.go b/ipns/select_test.go new file mode 100644 index 000000000..83405345a --- /dev/null +++ b/ipns/select_test.go @@ -0,0 +1,126 @@ +package ipns + +import ( + "fmt" + "math/rand" + "testing" + "time" + + proto "github.com/gogo/protobuf/proto" + u "github.com/ipfs/go-ipfs-util" + ci "github.com/libp2p/go-libp2p-crypto" + + pb "github.com/ipfs/go-ipns/pb" +) + +func shuffle(a []*pb.IpnsEntry) { + for n := 0; n < 5; n++ { + for i, _ := range a { + j := rand.Intn(len(a)) + a[i], a[j] = a[j], a[i] + } + } +} + +func AssertSelected(r *pb.IpnsEntry, from ...*pb.IpnsEntry) error { + shuffle(from) + var vals [][]byte + for _, r := range from { + data, err := proto.Marshal(r) + if err != nil { + return err + } + vals = append(vals, data) + } + + i, err := selectRecord(from, vals) + if err != nil { + return err + } + + if from[i] != r { + return fmt.Errorf("selected incorrect record %d", i) + } + + return nil +} + +func TestOrdering(t *testing.T) { + // select timestamp so selection is deterministic + ts := time.Unix(1000000, 0) + + // generate a key for signing the records + r := u.NewSeededRand(15) // generate deterministic keypair + priv, _, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, r) + if err != nil { + t.Fatal(err) + } + + e1, err := Create(priv, []byte("foo"), 1, ts.Add(time.Hour)) + if err != nil { + t.Fatal(err) + } + + e2, err := Create(priv, []byte("bar"), 2, ts.Add(time.Hour)) + if err != nil { + t.Fatal(err) + } + + e3, err := Create(priv, []byte("baz"), 3, ts.Add(time.Hour)) + if err != nil { + t.Fatal(err) + } + + e4, err := Create(priv, []byte("cat"), 3, ts.Add(time.Hour*2)) + if err != nil { + t.Fatal(err) + } + + e5, err := Create(priv, []byte("dog"), 4, ts.Add(time.Hour*3)) + if err != nil { + t.Fatal(err) + } + + e6, err := Create(priv, []byte("fish"), 4, ts.Add(time.Hour*3)) + if err != nil { + t.Fatal(err) + } + + // e1 is the only record, i hope it gets this right + err = AssertSelected(e1, e1) + if err != nil { + t.Fatal(err) + } + + // e2 has the highest sequence number + err = AssertSelected(e2, e1, e2) + if err != nil { + t.Fatal(err) + } + + // e3 has the highest sequence number + err = AssertSelected(e3, e1, e2, e3) + if err != nil { + t.Fatal(err) + } + + // e4 has a higher timeout + err = AssertSelected(e4, e1, e2, e3, e4) + if err != nil { + t.Fatal(err) + } + + // e5 has the highest sequence number + err = AssertSelected(e5, e1, e2, e3, e4, e5) + if err != nil { + t.Fatal(err) + } + + // e6 should be selected as its signauture will win in the comparison + err = AssertSelected(e6, e1, e2, e3, e4, e5, e6) + if err != nil { + t.Fatal(err) + } + + _ = []interface{}{e1, e2, e3, e4, e5, e6} +} diff --git a/ipns/validate_test.go b/ipns/validate_test.go new file mode 100644 index 000000000..634636844 --- /dev/null +++ b/ipns/validate_test.go @@ -0,0 +1,175 @@ +package ipns + +import ( + "fmt" + "math/rand" + "strings" + "testing" + "time" + + pb "github.com/ipfs/go-ipns/pb" + + proto "github.com/gogo/protobuf/proto" + u "github.com/ipfs/go-ipfs-util" + ci "github.com/libp2p/go-libp2p-crypto" + peer "github.com/libp2p/go-libp2p-peer" + pstore "github.com/libp2p/go-libp2p-peerstore" +) + +func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, key string, val []byte, eol time.Time, exp error) { + t.Helper() + + match := func(t *testing.T, err error) { + t.Helper() + if err != exp { + params := fmt.Sprintf("key: %s\neol: %s\n", key, eol) + if exp == nil { + t.Fatalf("Unexpected error %s for params %s", err, params) + } else if err == nil { + t.Fatalf("Expected error %s but there was no error for params %s", exp, params) + } else { + t.Fatalf("Expected error %s but got %s for params %s", exp, err, params) + } + } + } + + testValidatorCaseMatchFunc(t, priv, kbook, key, val, eol, match) +} + +func testValidatorCaseMatchFunc(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, key string, val []byte, eol time.Time, matchf func(*testing.T, error)) { + t.Helper() + validator := Validator{kbook} + + data := val + if data == nil { + p := []byte("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") + entry, err := Create(priv, p, 1, eol) + if err != nil { + t.Fatal(err) + } + + data, err = proto.Marshal(entry) + if err != nil { + t.Fatal(err) + } + } + + matchf(t, validator.Validate(key, data)) +} + +func TestValidator(t *testing.T) { + ts := time.Now() + + priv, id, _ := genKeys(t) + priv2, id2, _ := genKeys(t) + kbook := pstore.NewPeerstore() + kbook.AddPubKey(id, priv.GetPublic()) + emptyKbook := pstore.NewPeerstore() + + testValidatorCase(t, priv, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour), nil) + testValidatorCase(t, priv, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour*-1), ErrExpiredRecord) + testValidatorCase(t, priv, kbook, "/ipns/"+string(id), []byte("bad data"), ts.Add(time.Hour), ErrBadRecord) + testValidatorCase(t, priv, kbook, "/ipns/"+"bad key", nil, ts.Add(time.Hour), ErrKeyFormat) + testValidatorCase(t, priv, emptyKbook, "/ipns/"+string(id), nil, ts.Add(time.Hour), ErrPublicKeyNotFound) + testValidatorCase(t, priv2, kbook, "/ipns/"+string(id2), nil, ts.Add(time.Hour), ErrPublicKeyNotFound) + testValidatorCase(t, priv2, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour), ErrSignature) + testValidatorCase(t, priv, kbook, "//"+string(id), nil, ts.Add(time.Hour), ErrInvalidPath) + testValidatorCase(t, priv, kbook, "/wrong/"+string(id), nil, ts.Add(time.Hour), ErrInvalidPath) +} + +func mustMarshal(t *testing.T, entry *pb.IpnsEntry) []byte { + t.Helper() + data, err := proto.Marshal(entry) + if err != nil { + t.Fatal(err) + } + return data +} + +func TestEmbeddedPubKeyValidate(t *testing.T) { + goodeol := time.Now().Add(time.Hour) + kbook := pstore.NewPeerstore() + + pth := []byte("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") + + priv, _, ipnsk := genKeys(t) + + entry, err := Create(priv, pth, 1, goodeol) + if err != nil { + t.Fatal(err) + } + + testValidatorCase(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, ErrPublicKeyNotFound) + + pubkb, err := priv.GetPublic().Bytes() + if err != nil { + t.Fatal(err) + } + + entry.PubKey = pubkb + testValidatorCase(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, nil) + + entry.PubKey = []byte("probably not a public key") + testValidatorCaseMatchFunc(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, func(t *testing.T, err error) { + if !strings.Contains(err.Error(), "unmarshaling pubkey in record:") { + t.Fatal("expected pubkey unmarshaling to fail") + } + }) + + opriv, _, _ := genKeys(t) + wrongkeydata, err := opriv.GetPublic().Bytes() + if err != nil { + t.Fatal(err) + } + + entry.PubKey = wrongkeydata + testValidatorCase(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, ErrPublicKeyMismatch) +} + +func TestPeerIDPubKeyValidate(t *testing.T) { + goodeol := time.Now().Add(time.Hour) + kbook := pstore.NewPeerstore() + + pth := []byte("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") + + sk, pk, err := ci.GenerateEd25519Key(rand.New(rand.NewSource(42))) + if err != nil { + t.Fatal(err) + } + + pid, err := peer.IDFromPublicKey(pk) + if err != nil { + t.Fatal(err) + } + + ipnsk := "/ipns/" + string(pid) + + entry, err := Create(sk, pth, 1, goodeol) + if err != nil { + t.Fatal(err) + } + + dataNoKey, err := proto.Marshal(entry) + if err != nil { + t.Fatal(err) + } + + testValidatorCase(t, sk, kbook, ipnsk, dataNoKey, goodeol, nil) +} + +func genKeys(t *testing.T) (ci.PrivKey, peer.ID, string) { + sr := u.NewTimeSeededRand() + priv, _, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, sr) + if err != nil { + t.Fatal(err) + } + + // Create entry with expiry in one hour + pid, err := peer.IDFromPrivateKey(priv) + if err != nil { + t.Fatal(err) + } + ipnsKey := RecordKey(pid) + + return priv, pid, ipnsKey +} From 9ee7f48c51bb26b33afe68654cdeda27abbbab38 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 15 Jun 2018 12:50:32 -0700 Subject: [PATCH 2366/3817] fix import order This commit was moved from ipfs/go-ipns@abf430262ce6777d56db37b50e7aa5d0be1d73bb --- ipns/ipns.go | 4 ++-- ipns/record.go | 4 ++-- ipns/select_test.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ipns/ipns.go b/ipns/ipns.go index b3d374abf..46c9fab67 100644 --- a/ipns/ipns.go +++ b/ipns/ipns.go @@ -5,12 +5,12 @@ import ( "fmt" "time" + pb "github.com/ipfs/go-ipns/pb" + proto "github.com/gogo/protobuf/proto" u "github.com/ipfs/go-ipfs-util" ic "github.com/libp2p/go-libp2p-crypto" peer "github.com/libp2p/go-libp2p-peer" - - pb "github.com/ipfs/go-ipns/pb" ) // Create creates a new IPNS entry and signs it with the given private key. diff --git a/ipns/record.go b/ipns/record.go index 24a75dacf..56e221948 100644 --- a/ipns/record.go +++ b/ipns/record.go @@ -4,14 +4,14 @@ import ( "bytes" "errors" + pb "github.com/ipfs/go-ipns/pb" + proto "github.com/gogo/protobuf/proto" logging "github.com/ipfs/go-log" ic "github.com/libp2p/go-libp2p-crypto" peer "github.com/libp2p/go-libp2p-peer" pstore "github.com/libp2p/go-libp2p-peerstore" record "github.com/libp2p/go-libp2p-record" - - pb "github.com/ipfs/go-ipns/pb" ) var log = logging.Logger("ipns") diff --git a/ipns/select_test.go b/ipns/select_test.go index 83405345a..a9a34a91d 100644 --- a/ipns/select_test.go +++ b/ipns/select_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" + pb "github.com/ipfs/go-ipns/pb" + proto "github.com/gogo/protobuf/proto" u "github.com/ipfs/go-ipfs-util" ci "github.com/libp2p/go-libp2p-crypto" - - pb "github.com/ipfs/go-ipns/pb" ) func shuffle(a []*pb.IpnsEntry) { From 721caad3c5ae15b008a4cda65afe6b8ca729cc08 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 15 Jun 2018 14:57:16 -0700 Subject: [PATCH 2367/3817] add a helper function for getting the EOL of a record Useful for caching. This commit was moved from ipfs/go-ipns@697df1a9ea91addb8d693e645a79e44179262eab --- ipns/ipns.go | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/ipns/ipns.go b/ipns/ipns.go index 46c9fab67..2d67945e5 100644 --- a/ipns/ipns.go +++ b/ipns/ipns.go @@ -42,22 +42,27 @@ func Validate(pk ic.PubKey, entry *pb.IpnsEntry) error { return ErrSignature } - // Check that record has not expired - switch entry.GetValidityType() { - case pb.IpnsEntry_EOL: - t, err := u.ParseRFC3339(string(entry.GetValidity())) - if err != nil { - return err - } - if time.Now().After(t) { - return ErrExpiredRecord - } - default: - return ErrUnrecognizedValidity + eol, err := GetEOL(entry) + if err != nil { + return err + } + if time.Now().After(eol) { + return ErrExpiredRecord } return nil } +// GetEOL returns the EOL of this IPNS entry +// +// This function returns ErrUnrecognizedValidity if the validity type of the +// record isn't EOL. Otherwise, it returns an error if it can't parse the EOL. +func GetEOL(entry *pb.IpnsEntry) (time.Time, error) { + if entry.GetValidityType() != pb.IpnsEntry_EOL { + return time.Time{}, ErrUnrecognizedValidity + } + return u.ParseRFC3339(string(entry.GetValidity())) +} + // EmbedPublicKey embeds the given public key in the given ipns entry. While not // strictly required, some nodes (e.g., DHT servers) may reject IPNS entries // that don't embed their public keys as they may not be able to validate them From c6fd683f52f04fe3413fdc28fc6e788dfa45bff5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 15 Jun 2018 20:06:44 -0700 Subject: [PATCH 2368/3817] make republisher test robust against timing issues retry publishing with a longer EOL if the first attempt fails due to a timeout. fixes #5099 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@e57d1900d14fbb8361f62ab10965137af560843b --- namesys/republisher/repub_test.go | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 9cc5a940e..8a76cbcc1 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -59,18 +59,33 @@ func TestRepublish(t *testing.T) { publisher := nodes[3] p := path.FromString("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") // does not need to be valid rp := namesys.NewIpnsPublisher(publisher.Routing, publisher.Repo.Datastore()) - err := rp.PublishWithEOL(ctx, publisher.PrivateKey, p, time.Now().Add(time.Second)) - if err != nil { - t.Fatal(err) - } - name := "/ipns/" + publisher.Identity.Pretty() - if err := verifyResolution(nodes, name, p); err != nil { + + // Retry in case the record expires before we can fetch it. This can + // happen when running the test on a slow machine. + var expiration time.Time + timeout := time.Second + for { + expiration = time.Now().Add(time.Second) + err := rp.PublishWithEOL(ctx, publisher.PrivateKey, p, expiration) + if err != nil { + t.Fatal(err) + } + + err = verifyResolution(nodes, name, p) + if err == nil { + break + } + + if time.Now().After(expiration) { + timeout *= 2 + continue + } t.Fatal(err) } // Now wait a second, the records will be invalid and we should fail to resolve - time.Sleep(time.Second) + time.Sleep(timeout) if err := verifyResolutionFails(nodes, name); err != nil { t.Fatal(err) } From d80b2330f7666cab334bee9a4f7ee1c3011337da Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 15 Jun 2018 16:53:47 -0700 Subject: [PATCH 2369/3817] extract ipns record logic to go-ipns License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@8e0233c48f3724f5a52aca8652e07c991f9545af --- ...st.go => ipns_resolver_validation_test.go} | 162 +--------------- namesys/ipns_select_test.go | 127 ------------- namesys/pb/namesys.pb.go | 127 ------------- namesys/pb/namesys.proto | 23 --- namesys/publisher.go | 72 ++----- namesys/publisher_test.go | 9 +- namesys/republisher/repub.go | 2 +- namesys/resolve_test.go | 5 +- namesys/routing.go | 25 +-- namesys/validator.go | 179 ------------------ 10 files changed, 42 insertions(+), 689 deletions(-) rename namesys/{ipns_validate_test.go => ipns_resolver_validation_test.go} (53%) delete mode 100644 namesys/ipns_select_test.go delete mode 100644 namesys/pb/namesys.pb.go delete mode 100644 namesys/pb/namesys.proto delete mode 100644 namesys/validator.go diff --git a/namesys/ipns_validate_test.go b/namesys/ipns_resolver_validation_test.go similarity index 53% rename from namesys/ipns_validate_test.go rename to namesys/ipns_resolver_validation_test.go index c1ea78899..864aa443a 100644 --- a/namesys/ipns_validate_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -2,23 +2,19 @@ package namesys import ( "context" - "fmt" - "math/rand" - "strings" "testing" "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" record "gx/ipfs/QmPWjVzxHeJdrjp4Jr2R2sPxBrMbBgGPWQtKwCKHHCBF7x/go-libp2p-record" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" testutil "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil" + ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns" routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing" ropts "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing/options" peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" pstore "gx/ipfs/QmZhsmorLpD9kmQ4ynbAu4vbKv2goMUnXazwGA4gnWHDjB/go-libp2p-peerstore" mockrouting "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/mock" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" @@ -26,147 +22,6 @@ import ( dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) -func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, key string, val []byte, eol time.Time, exp error) { - t.Helper() - - match := func(t *testing.T, err error) { - t.Helper() - if err != exp { - params := fmt.Sprintf("key: %s\neol: %s\n", key, eol) - if exp == nil { - t.Fatalf("Unexpected error %s for params %s", err, params) - } else if err == nil { - t.Fatalf("Expected error %s but there was no error for params %s", exp, params) - } else { - t.Fatalf("Expected error %s but got %s for params %s", exp, err, params) - } - } - } - - testValidatorCaseMatchFunc(t, priv, kbook, key, val, eol, match) -} - -func testValidatorCaseMatchFunc(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, key string, val []byte, eol time.Time, matchf func(*testing.T, error)) { - t.Helper() - validator := IpnsValidator{kbook} - - data := val - if data == nil { - p := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") - entry, err := CreateRoutingEntryData(priv, p, 1, eol) - if err != nil { - t.Fatal(err) - } - - data, err = proto.Marshal(entry) - if err != nil { - t.Fatal(err) - } - } - - matchf(t, validator.Validate(key, data)) -} - -func TestValidator(t *testing.T) { - ts := time.Now() - - priv, id, _, _ := genKeys(t) - priv2, id2, _, _ := genKeys(t) - kbook := pstore.NewPeerstore() - kbook.AddPubKey(id, priv.GetPublic()) - emptyKbook := pstore.NewPeerstore() - - testValidatorCase(t, priv, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour), nil) - testValidatorCase(t, priv, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour*-1), ErrExpiredRecord) - testValidatorCase(t, priv, kbook, "/ipns/"+string(id), []byte("bad data"), ts.Add(time.Hour), ErrBadRecord) - testValidatorCase(t, priv, kbook, "/ipns/"+"bad key", nil, ts.Add(time.Hour), ErrKeyFormat) - testValidatorCase(t, priv, emptyKbook, "/ipns/"+string(id), nil, ts.Add(time.Hour), ErrPublicKeyNotFound) - testValidatorCase(t, priv2, kbook, "/ipns/"+string(id2), nil, ts.Add(time.Hour), ErrPublicKeyNotFound) - testValidatorCase(t, priv2, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour), ErrSignature) - testValidatorCase(t, priv, kbook, "//"+string(id), nil, ts.Add(time.Hour), ErrInvalidPath) - testValidatorCase(t, priv, kbook, "/wrong/"+string(id), nil, ts.Add(time.Hour), ErrInvalidPath) -} - -func mustMarshal(t *testing.T, entry *pb.IpnsEntry) []byte { - t.Helper() - data, err := proto.Marshal(entry) - if err != nil { - t.Fatal(err) - } - return data -} - -func TestEmbeddedPubKeyValidate(t *testing.T) { - goodeol := time.Now().Add(time.Hour) - kbook := pstore.NewPeerstore() - - pth := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") - - priv, _, _, ipnsk := genKeys(t) - - entry, err := CreateRoutingEntryData(priv, pth, 1, goodeol) - if err != nil { - t.Fatal(err) - } - - testValidatorCase(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, ErrPublicKeyNotFound) - - pubkb, err := priv.GetPublic().Bytes() - if err != nil { - t.Fatal(err) - } - - entry.PubKey = pubkb - testValidatorCase(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, nil) - - entry.PubKey = []byte("probably not a public key") - testValidatorCaseMatchFunc(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, func(t *testing.T, err error) { - if !strings.Contains(err.Error(), "unmarshaling pubkey in record:") { - t.Fatal("expected pubkey unmarshaling to fail") - } - }) - - opriv, _, _, _ := genKeys(t) - wrongkeydata, err := opriv.GetPublic().Bytes() - if err != nil { - t.Fatal(err) - } - - entry.PubKey = wrongkeydata - testValidatorCase(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, ErrPublicKeyMismatch) -} - -func TestPeerIDPubKeyValidate(t *testing.T) { - goodeol := time.Now().Add(time.Hour) - kbook := pstore.NewPeerstore() - - pth := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") - - sk, pk, err := ci.GenerateEd25519Key(rand.New(rand.NewSource(42))) - if err != nil { - t.Fatal(err) - } - - pid, err := peer.IDFromPublicKey(pk) - if err != nil { - t.Fatal(err) - } - - ipnsk := "/ipns/" + string(pid) - - entry, err := CreateRoutingEntryData(sk, pth, 1, goodeol) - if err != nil { - t.Fatal(err) - } - - dataNoKey, err := proto.Marshal(entry) - if err != nil { - t.Fatal(err) - } - - testValidatorCase(t, sk, kbook, ipnsk, dataNoKey, goodeol, nil) -} - func TestResolverValidation(t *testing.T) { ctx := context.Background() rid := testutil.RandIdentityOrFatal(t) @@ -179,8 +34,8 @@ func TestResolverValidation(t *testing.T) { // Create entry with expiry in one hour priv, id, _, ipnsDHTPath := genKeys(t) ts := time.Now() - p := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") - entry, err := CreateRoutingEntryData(priv, p, 1, ts.Add(time.Hour)) + p := []byte("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") + entry, err := ipns.Create(priv, p, 1, ts.Add(time.Hour)) if err != nil { t.Fatal(err) } @@ -202,12 +57,12 @@ func TestResolverValidation(t *testing.T) { if err != nil { t.Fatal(err) } - if resp != p { + if resp != path.Path(p) { t.Fatalf("Mismatch between published path %s and resolved path %s", p, resp) } // Create expired entry - expiredEntry, err := CreateRoutingEntryData(priv, p, 1, ts.Add(-1*time.Hour)) + expiredEntry, err := ipns.Create(priv, p, 1, ts.Add(-1*time.Hour)) if err != nil { t.Fatal(err) } @@ -248,7 +103,7 @@ func TestResolverValidation(t *testing.T) { // Publish entry without making public key available in peer store priv3, id3, pubkDHTPath3, ipnsDHTPath3 := genKeys(t) - entry3, err := CreateRoutingEntryData(priv3, p, 1, ts.Add(time.Hour)) + entry3, err := ipns.Create(priv3, p, 1, ts.Add(time.Hour)) if err != nil { t.Fatal(err) } @@ -292,9 +147,8 @@ func genKeys(t *testing.T) (ci.PrivKey, peer.ID, string, string) { if err != nil { t.Fatal(err) } - pubkDHTPath, ipnsDHTPath := IpnsKeysForID(pid) - return priv, pid, pubkDHTPath, ipnsDHTPath + return priv, pid, PkKeyForID(pid), ipns.RecordKey(pid) } type mockValueStore struct { @@ -307,7 +161,7 @@ func newMockValueStore(id testutil.Identity, dstore ds.Datastore, kbook pstore.K serv := mockrouting.NewServer() r := serv.ClientWithDatastore(context.Background(), id, dstore) return &mockValueStore{r, kbook, record.NamespacedValidator{ - "ipns": IpnsValidator{kbook}, + "ipns": ipns.Validator{KeyBook: kbook}, "pk": record.PublicKeyValidator{}, }} } diff --git a/namesys/ipns_select_test.go b/namesys/ipns_select_test.go deleted file mode 100644 index a0067ada6..000000000 --- a/namesys/ipns_select_test.go +++ /dev/null @@ -1,127 +0,0 @@ -package namesys - -import ( - "fmt" - "math/rand" - "testing" - "time" - - pb "github.com/ipfs/go-ipfs/namesys/pb" - path "github.com/ipfs/go-ipfs/path" - - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" -) - -func shuffle(a []*pb.IpnsEntry) { - for n := 0; n < 5; n++ { - for i, _ := range a { - j := rand.Intn(len(a)) - a[i], a[j] = a[j], a[i] - } - } -} - -func AssertSelected(r *pb.IpnsEntry, from ...*pb.IpnsEntry) error { - shuffle(from) - var vals [][]byte - for _, r := range from { - data, err := proto.Marshal(r) - if err != nil { - return err - } - vals = append(vals, data) - } - - i, err := selectRecord(from, vals) - if err != nil { - return err - } - - if from[i] != r { - return fmt.Errorf("selected incorrect record %d", i) - } - - return nil -} - -func TestOrdering(t *testing.T) { - // select timestamp so selection is deterministic - ts := time.Unix(1000000, 0) - - // generate a key for signing the records - r := u.NewSeededRand(15) // generate deterministic keypair - priv, _, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, r) - if err != nil { - t.Fatal(err) - } - - e1, err := CreateRoutingEntryData(priv, path.Path("foo"), 1, ts.Add(time.Hour)) - if err != nil { - t.Fatal(err) - } - - e2, err := CreateRoutingEntryData(priv, path.Path("bar"), 2, ts.Add(time.Hour)) - if err != nil { - t.Fatal(err) - } - - e3, err := CreateRoutingEntryData(priv, path.Path("baz"), 3, ts.Add(time.Hour)) - if err != nil { - t.Fatal(err) - } - - e4, err := CreateRoutingEntryData(priv, path.Path("cat"), 3, ts.Add(time.Hour*2)) - if err != nil { - t.Fatal(err) - } - - e5, err := CreateRoutingEntryData(priv, path.Path("dog"), 4, ts.Add(time.Hour*3)) - if err != nil { - t.Fatal(err) - } - - e6, err := CreateRoutingEntryData(priv, path.Path("fish"), 4, ts.Add(time.Hour*3)) - if err != nil { - t.Fatal(err) - } - - // e1 is the only record, i hope it gets this right - err = AssertSelected(e1, e1) - if err != nil { - t.Fatal(err) - } - - // e2 has the highest sequence number - err = AssertSelected(e2, e1, e2) - if err != nil { - t.Fatal(err) - } - - // e3 has the highest sequence number - err = AssertSelected(e3, e1, e2, e3) - if err != nil { - t.Fatal(err) - } - - // e4 has a higher timeout - err = AssertSelected(e4, e1, e2, e3, e4) - if err != nil { - t.Fatal(err) - } - - // e5 has the highest sequence number - err = AssertSelected(e5, e1, e2, e3, e4, e5) - if err != nil { - t.Fatal(err) - } - - // e6 should be selected as its signauture will win in the comparison - err = AssertSelected(e6, e1, e2, e3, e4, e5, e6) - if err != nil { - t.Fatal(err) - } - - _ = []interface{}{e1, e2, e3, e4, e5, e6} -} diff --git a/namesys/pb/namesys.pb.go b/namesys/pb/namesys.pb.go deleted file mode 100644 index 66626ca7d..000000000 --- a/namesys/pb/namesys.pb.go +++ /dev/null @@ -1,127 +0,0 @@ -// Code generated by protoc-gen-gogo. -// source: namesys/pb/namesys.proto -// DO NOT EDIT! - -/* -Package namesys_pb is a generated protocol buffer package. - -It is generated from these files: - namesys/pb/namesys.proto - -It has these top-level messages: - IpnsEntry -*/ -package namesys_pb - -import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" -import fmt "fmt" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -type IpnsEntry_ValidityType int32 - -const ( - // setting an EOL says "this record is valid until..." - IpnsEntry_EOL IpnsEntry_ValidityType = 0 -) - -var IpnsEntry_ValidityType_name = map[int32]string{ - 0: "EOL", -} -var IpnsEntry_ValidityType_value = map[string]int32{ - "EOL": 0, -} - -func (x IpnsEntry_ValidityType) Enum() *IpnsEntry_ValidityType { - p := new(IpnsEntry_ValidityType) - *p = x - return p -} -func (x IpnsEntry_ValidityType) String() string { - return proto.EnumName(IpnsEntry_ValidityType_name, int32(x)) -} -func (x *IpnsEntry_ValidityType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(IpnsEntry_ValidityType_value, data, "IpnsEntry_ValidityType") - if err != nil { - return err - } - *x = IpnsEntry_ValidityType(value) - return nil -} - -type IpnsEntry struct { - Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"` - Signature []byte `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"` - ValidityType *IpnsEntry_ValidityType `protobuf:"varint,3,opt,name=validityType,enum=namesys.pb.IpnsEntry_ValidityType" json:"validityType,omitempty"` - Validity []byte `protobuf:"bytes,4,opt,name=validity" json:"validity,omitempty"` - Sequence *uint64 `protobuf:"varint,5,opt,name=sequence" json:"sequence,omitempty"` - Ttl *uint64 `protobuf:"varint,6,opt,name=ttl" json:"ttl,omitempty"` - // in order for nodes to properly validate a record upon receipt, they need the public - // key associated with it. For old RSA keys, its easiest if we just send this as part of - // the record itself. For newer ed25519 keys, the public key can be embedded in the - // peerID, making this field unnecessary. - PubKey []byte `protobuf:"bytes,7,opt,name=pubKey" json:"pubKey,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *IpnsEntry) Reset() { *m = IpnsEntry{} } -func (m *IpnsEntry) String() string { return proto.CompactTextString(m) } -func (*IpnsEntry) ProtoMessage() {} - -func (m *IpnsEntry) GetValue() []byte { - if m != nil { - return m.Value - } - return nil -} - -func (m *IpnsEntry) GetSignature() []byte { - if m != nil { - return m.Signature - } - return nil -} - -func (m *IpnsEntry) GetValidityType() IpnsEntry_ValidityType { - if m != nil && m.ValidityType != nil { - return *m.ValidityType - } - return IpnsEntry_EOL -} - -func (m *IpnsEntry) GetValidity() []byte { - if m != nil { - return m.Validity - } - return nil -} - -func (m *IpnsEntry) GetSequence() uint64 { - if m != nil && m.Sequence != nil { - return *m.Sequence - } - return 0 -} - -func (m *IpnsEntry) GetTtl() uint64 { - if m != nil && m.Ttl != nil { - return *m.Ttl - } - return 0 -} - -func (m *IpnsEntry) GetPubKey() []byte { - if m != nil { - return m.PubKey - } - return nil -} - -func init() { - proto.RegisterType((*IpnsEntry)(nil), "namesys.pb.IpnsEntry") - proto.RegisterEnum("namesys.pb.IpnsEntry_ValidityType", IpnsEntry_ValidityType_name, IpnsEntry_ValidityType_value) -} diff --git a/namesys/pb/namesys.proto b/namesys/pb/namesys.proto deleted file mode 100644 index b72d49843..000000000 --- a/namesys/pb/namesys.proto +++ /dev/null @@ -1,23 +0,0 @@ -package namesys.pb; - -message IpnsEntry { - enum ValidityType { - // setting an EOL says "this record is valid until..." - EOL = 0; - } - required bytes value = 1; - required bytes signature = 2; - - optional ValidityType validityType = 3; - optional bytes validity = 4; - - optional uint64 sequence = 5; - - optional uint64 ttl = 6; - - // in order for nodes to properly validate a record upon receipt, they need the public - // key associated with it. For old RSA keys, its easiest if we just send this as part of - // the record itself. For newer ed25519 keys, the public key can be embedded in the - // peerID, making this field unnecessary. - optional bytes pubKey = 7; -} diff --git a/namesys/publisher.go b/namesys/publisher.go index 344004f24..7eb548395 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -1,19 +1,18 @@ package namesys import ( - "bytes" "context" "fmt" "strings" "sync" "time" - pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" pin "github.com/ipfs/go-ipfs/pin" ft "github.com/ipfs/go-ipfs/unixfs" - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns" + pb "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns/pb" routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing" peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" @@ -131,7 +130,7 @@ func (p *IpnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouti if !checkRouting { return nil, nil } - _, ipnskey := IpnsKeysForID(id) + ipnskey := ipns.RecordKey(id) value, err = p.routing.GetValue(ctx, ipnskey) if err != nil { // Not found or other network issue. Can't really do @@ -175,7 +174,7 @@ func (p *IpnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value pa } // Create record - entry, err := CreateRoutingEntryData(k, value, seqno, eol) + entry, err := ipns.Create(k, []byte(value), seqno, eol) if err != nil { return nil, err } @@ -229,40 +228,29 @@ func PutRecordToRouting(ctx context.Context, r routing.ValueStore, k ci.PubKey, errs := make(chan error, 2) // At most two errors (IPNS, and public key) - id, err := peer.IDFromPublicKey(k) - if err != nil { + if err := ipns.EmbedPublicKey(k, entry); err != nil { return err } - // Attempt to extract the public key from the ID - extractedPublicKey, err := id.ExtractPublicKey() + id, err := peer.IDFromPublicKey(k) if err != nil { return err } - // if we can't derive the public key from the peerID, embed the entire pubkey in - // the record to make the verifiers job easier - if extractedPublicKey == nil { - pubkeyBytes, err := k.Bytes() - if err != nil { - return err - } - - entry.PubKey = pubkeyBytes - } - - namekey, ipnskey := IpnsKeysForID(id) - go func() { - errs <- PublishEntry(ctx, r, ipnskey, entry) + errs <- PublishEntry(ctx, r, ipns.RecordKey(id), entry) }() // Publish the public key if a public key cannot be extracted from the ID // TODO: once v0.4.16 is widespread enough, we can stop doing this // and at that point we can even deprecate the /pk/ namespace in the dht - if extractedPublicKey == nil { + // + // NOTE: This check actually checks if the public key has been embedded + // in the IPNS entry. This check is sufficient because we embed the + // public key in the IPNS entry if it can't be extracted from the ID. + if entry.PubKey != nil { go func() { - errs <- PublishPublicKey(ctx, r, namekey, k) + errs <- PublishPublicKey(ctx, r, PkKeyForID(id), k) }() if err := waitOnErrChan(ctx, errs); err != nil { @@ -309,32 +297,6 @@ func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec return r.PutValue(timectx, ipnskey, data) } -func CreateRoutingEntryData(pk ci.PrivKey, val path.Path, seq uint64, eol time.Time) (*pb.IpnsEntry, error) { - entry := new(pb.IpnsEntry) - - entry.Value = []byte(val) - typ := pb.IpnsEntry_EOL - entry.ValidityType = &typ - entry.Sequence = proto.Uint64(seq) - entry.Validity = []byte(u.FormatRFC3339(eol)) - - sig, err := pk.Sign(ipnsEntryDataForSig(entry)) - if err != nil { - return nil, err - } - entry.Signature = sig - return entry, nil -} - -func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte { - return bytes.Join([][]byte{ - e.Value, - e.Validity, - []byte(fmt.Sprint(e.GetValidityType())), - }, - []byte{}) -} - // InitializeKeyspace sets the ipns record for the given key to // point to an empty directory. // TODO: this doesnt feel like it belongs here @@ -356,9 +318,7 @@ func InitializeKeyspace(ctx context.Context, pub Publisher, pins pin.Pinner, key return pub.Publish(ctx, key, path.FromCid(emptyDir.Cid())) } -func IpnsKeysForID(id peer.ID) (name, ipns string) { - namekey := "/pk/" + string(id) - ipnskey := "/ipns/" + string(id) - - return namekey, ipnskey +// PkKeyForID returns the public key routing key for the given peer ID. +func PkKeyForID(id peer.ID) string { + return "/pk/" + string(id) } diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 67f6be322..a79492c3d 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -6,10 +6,9 @@ import ( "testing" "time" - path "github.com/ipfs/go-ipfs/path" - dshelp "gx/ipfs/QmNP2u7bofwUQptHQGPfabGWtTCbxhNLSZKqbf1uzsup9V/go-ipfs-ds-help" testutil "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil" + ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns" ma "gx/ipfs/QmUxSEGbv2nmYNnfXi7839wwQqTN3kwQeUxe8dTjZWZs7J/go-multiaddr" peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" mockrouting "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/mock" @@ -55,7 +54,7 @@ func testNamekeyPublisher(t *testing.T, keyType int, expectedErr error, expected } // Value - value := path.Path("ipfs/TESTING") + value := []byte("ipfs/TESTING") // Seqnum seqnum := uint64(0) @@ -75,7 +74,7 @@ func testNamekeyPublisher(t *testing.T, keyType int, expectedErr error, expected serv := mockrouting.NewServer() r := serv.ClientWithDatastore(context.Background(), &identity{p}, dstore) - entry, err := CreateRoutingEntryData(privKey, value, seqnum, eol) + entry, err := ipns.Create(privKey, value, seqnum, eol) if err != nil { t.Fatal(err) } @@ -86,7 +85,7 @@ func testNamekeyPublisher(t *testing.T, keyType int, expectedErr error, expected } // Check for namekey existence in value store - namekey, _ := IpnsKeysForID(id) + namekey := PkKeyForID(id) _, err = r.GetValue(ctx, namekey) if err != expectedErr { t.Fatal(err) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 1864174c0..fb90ccbc2 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,9 +7,9 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" + pb "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns/pb" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 9dd566404..a930eb4e7 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -9,6 +9,7 @@ import ( path "github.com/ipfs/go-ipfs/path" testutil "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil" + ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns" peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" mockrouting "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/mock" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" @@ -71,7 +72,7 @@ func TestPrexistingExpiredRecord(t *testing.T) { h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN") eol := time.Now().Add(time.Hour * -1) - entry, err := CreateRoutingEntryData(privk, h, 0, eol) + entry, err := ipns.Create(privk, []byte(h), 0, eol) if err != nil { t.Fatal(err) } @@ -112,7 +113,7 @@ func TestPrexistingRecord(t *testing.T) { // Make a good record and put it in the datastore h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN") eol := time.Now().Add(time.Hour) - entry, err := CreateRoutingEntryData(privk, h, 0, eol) + entry, err := ipns.Create(privk, []byte(h), 0, eol) if err != nil { t.Fatal(err) } diff --git a/namesys/routing.go b/namesys/routing.go index c10464a1b..22209e8f0 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,11 +6,11 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - pb "github.com/ipfs/go-ipfs/namesys/pb" path "github.com/ipfs/go-ipfs/path" - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns" + pb "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns/pb" routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing" peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" @@ -82,7 +82,7 @@ func (r *IpnsResolver) resolveOnce(ctx context.Context, name string, options *op // Use the routing system to get the name. // Note that the DHT will call the ipns validator when retrieving // the value, which in turn verifies the ipns record signature - _, ipnsKey := IpnsKeysForID(pid) + ipnsKey := ipns.RecordKey(pid) val, err := r.routing.GetValue(ctx, ipnsKey, dht.Quorum(int(options.DhtRecordCount))) if err != nil { log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err) @@ -114,7 +114,10 @@ func (r *IpnsResolver) resolveOnce(ctx context.Context, name string, options *op if entry.Ttl != nil { ttl = time.Duration(*entry.Ttl) } - if eol, ok := checkEOL(entry); ok { + switch eol, err := ipns.GetEOL(entry); err { + case ipns.ErrUnrecognizedValidity: + // No EOL. + case nil: ttEol := eol.Sub(time.Now()) if ttEol < 0 { // It *was* valid when we first resolved it. @@ -122,18 +125,10 @@ func (r *IpnsResolver) resolveOnce(ctx context.Context, name string, options *op } else if ttEol < ttl { ttl = ttEol } + default: + log.Errorf("encountered error when parsing EOL: %s", err) + return "", 0, err } return p, ttl, nil } - -func checkEOL(e *pb.IpnsEntry) (time.Time, bool) { - if e.GetValidityType() == pb.IpnsEntry_EOL { - eol, err := u.ParseRFC3339(string(e.GetValidity())) - if err != nil { - return time.Time{}, false - } - return eol, true - } - return time.Time{}, false -} diff --git a/namesys/validator.go b/namesys/validator.go deleted file mode 100644 index f947ef046..000000000 --- a/namesys/validator.go +++ /dev/null @@ -1,179 +0,0 @@ -package namesys - -import ( - "bytes" - "errors" - "fmt" - "time" - - pb "github.com/ipfs/go-ipfs/namesys/pb" - peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" - pstore "gx/ipfs/QmZhsmorLpD9kmQ4ynbAu4vbKv2goMUnXazwGA4gnWHDjB/go-libp2p-peerstore" - ic "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" - - record "gx/ipfs/QmPWjVzxHeJdrjp4Jr2R2sPxBrMbBgGPWQtKwCKHHCBF7x/go-libp2p-record" - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" -) - -// ErrExpiredRecord should be returned when an ipns record is -// invalid due to being too old -var ErrExpiredRecord = errors.New("expired record") - -// ErrUnrecognizedValidity is returned when an IpnsRecord has an -// unknown validity type. -var ErrUnrecognizedValidity = errors.New("unrecognized validity type") - -// ErrInvalidPath should be returned when an ipns record path -// is not in a valid format -var ErrInvalidPath = errors.New("record path invalid") - -// ErrSignature should be returned when an ipns record fails -// signature verification -var ErrSignature = errors.New("record signature verification failed") - -// ErrBadRecord should be returned when an ipns record cannot be unmarshalled -var ErrBadRecord = errors.New("record could not be unmarshalled") - -// ErrKeyFormat should be returned when an ipns record key is -// incorrectly formatted (not a peer ID) -var ErrKeyFormat = errors.New("record key could not be parsed into peer ID") - -// ErrPublicKeyNotFound should be returned when the public key -// corresponding to the ipns record path cannot be retrieved -// from the peer store -var ErrPublicKeyNotFound = errors.New("public key not found in peer store") - -var ErrPublicKeyMismatch = errors.New("public key in record did not match expected pubkey") - -type IpnsValidator struct { - KeyBook pstore.KeyBook -} - -func (v IpnsValidator) Validate(key string, value []byte) error { - ns, pidString, err := record.SplitKey(key) - if err != nil || ns != "ipns" { - return ErrInvalidPath - } - - // Parse the value into an IpnsEntry - entry := new(pb.IpnsEntry) - err = proto.Unmarshal(value, entry) - if err != nil { - return ErrBadRecord - } - - // Get the public key defined by the ipns path - pid, err := peer.IDFromString(pidString) - if err != nil { - log.Debugf("failed to parse ipns record key %s into peer ID", pidString) - return ErrKeyFormat - } - - pubk, err := v.getPublicKey(pid, entry) - if err != nil { - return err - } - - // Check the ipns record signature with the public key - if ok, err := pubk.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok { - log.Debugf("failed to verify signature for ipns record %s", pidString) - return ErrSignature - } - - // Check that record has not expired - switch entry.GetValidityType() { - case pb.IpnsEntry_EOL: - t, err := u.ParseRFC3339(string(entry.GetValidity())) - if err != nil { - log.Debugf("failed parsing time for ipns record EOL in record %s", pidString) - return err - } - if time.Now().After(t) { - return ErrExpiredRecord - } - default: - return ErrUnrecognizedValidity - } - return nil -} - -func (v IpnsValidator) getPublicKey(pid peer.ID, entry *pb.IpnsEntry) (ic.PubKey, error) { - if entry.PubKey != nil { - pk, err := ic.UnmarshalPublicKey(entry.PubKey) - if err != nil { - log.Debugf("public key in ipns record failed to parse: ", err) - return nil, fmt.Errorf("unmarshaling pubkey in record: %s", err) - } - - expPid, err := peer.IDFromPublicKey(pk) - if err != nil { - return nil, fmt.Errorf("could not regenerate peerID from pubkey: %s", err) - } - - if pid != expPid { - return nil, ErrPublicKeyMismatch - } - - return pk, nil - } - - pubk := v.KeyBook.PubKey(pid) - if pubk == nil { - log.Debugf("public key with hash %s not found in peer store", pid) - return nil, ErrPublicKeyNotFound - } - return pubk, nil -} - -// IpnsSelectorFunc selects the best record by checking which has the highest -// sequence number and latest EOL -func (v IpnsValidator) Select(k string, vals [][]byte) (int, error) { - var recs []*pb.IpnsEntry - for _, v := range vals { - e := new(pb.IpnsEntry) - err := proto.Unmarshal(v, e) - if err == nil { - recs = append(recs, e) - } else { - recs = append(recs, nil) - } - } - - return selectRecord(recs, vals) -} - -func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) { - var bestSeq uint64 - besti := -1 - - for i, r := range recs { - if r == nil || r.GetSequence() < bestSeq { - continue - } - rt, err := u.ParseRFC3339(string(r.GetValidity())) - if err != nil { - log.Errorf("failed to parse ipns record EOL %s", r.GetValidity()) - continue - } - - if besti == -1 || r.GetSequence() > bestSeq { - bestSeq = r.GetSequence() - besti = i - } else if r.GetSequence() == bestSeq { - bestt, _ := u.ParseRFC3339(string(recs[besti].GetValidity())) - if rt.After(bestt) { - besti = i - } else if rt == bestt { - if bytes.Compare(vals[i], vals[besti]) > 0 { - besti = i - } - } - } - } - if besti == -1 { - return 0, errors.New("no usable records in given set") - } - - return besti, nil -} From 9d70b77085e67e3e9b22dbe16a427d2ded30f68e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 17 Jun 2018 15:16:42 -0700 Subject: [PATCH 2370/3817] add an explicit test case for the EmbedPublicKey function This commit was moved from ipfs/go-ipns@d7779be9f3d26474e8aab64d2afe4fb0391af53f --- ipns/ipns.go | 5 +++++ ipns/ipns_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 ipns/ipns_test.go diff --git a/ipns/ipns.go b/ipns/ipns.go index 2d67945e5..be71edb72 100644 --- a/ipns/ipns.go +++ b/ipns/ipns.go @@ -68,6 +68,8 @@ func GetEOL(entry *pb.IpnsEntry) (time.Time, error) { // that don't embed their public keys as they may not be able to validate them // efficiently. func EmbedPublicKey(pk ic.PubKey, entry *pb.IpnsEntry) error { + // Try extracting the public key from the ID. If we can, *don't* embed + // it. id, err := peer.IDFromPublicKey(pk) if err != nil { return err @@ -79,6 +81,9 @@ func EmbedPublicKey(pk ic.PubKey, entry *pb.IpnsEntry) error { if extraced != nil { return nil } + + // We failed to extract the public key from the peer ID, embed it in the + // record. pkBytes, err := pk.Bytes() if err != nil { return err diff --git a/ipns/ipns_test.go b/ipns/ipns_test.go new file mode 100644 index 000000000..d46423fef --- /dev/null +++ b/ipns/ipns_test.go @@ -0,0 +1,43 @@ +package ipns + +import ( + "testing" + "time" + + u "github.com/ipfs/go-ipfs-util" + ci "github.com/libp2p/go-libp2p-crypto" + peer "github.com/libp2p/go-libp2p-peer" +) + +func TestEmbedPublicKey(t *testing.T) { + + sr := u.NewTimeSeededRand() + priv, pub, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, sr) + if err != nil { + t.Fatal(err) + } + + pid, err := peer.IDFromPublicKey(pub) + if err != nil { + t.Fatal(err) + } + + e, err := Create(priv, []byte("/a/b"), 0, time.Now().Add(1*time.Hour)) + if err != nil { + t.Fatal(err) + } + if err := EmbedPublicKey(pub, e); err != nil { + t.Fatal(err) + } + embeddedPk, err := ci.UnmarshalPublicKey(e.PubKey) + if err != nil { + t.Fatal(err) + } + embeddedPid, err := peer.IDFromPublicKey(embeddedPk) + if err != nil { + t.Fatal(err) + } + if embeddedPid != pid { + t.Fatalf("pid mismatch: %s != %s", pid, embeddedPid) + } +} From 86391e59a559c332e8d3948e65ab92362222eeac Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 17 Jun 2018 15:18:15 -0700 Subject: [PATCH 2371/3817] fix typo This commit was moved from ipfs/go-ipns@0264a049adee7628c91a9b47e710e9b37cde99a3 --- ipns/ipns.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipns/ipns.go b/ipns/ipns.go index be71edb72..38b50764b 100644 --- a/ipns/ipns.go +++ b/ipns/ipns.go @@ -74,11 +74,11 @@ func EmbedPublicKey(pk ic.PubKey, entry *pb.IpnsEntry) error { if err != nil { return err } - extraced, err := id.ExtractPublicKey() + extracted, err := id.ExtractPublicKey() if err != nil { return err } - if extraced != nil { + if extracted != nil { return nil } From c01ef070cb9792efcb9e0751de979bcd0f0daf8f Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Mon, 18 Jun 2018 13:46:40 -0700 Subject: [PATCH 2372/3817] Add a usage example to the README, fixes #9 We could still use some more examples of other operations, but this should be a good start. License: MIT Signed-off-by: Rob Brackett This commit was moved from ipfs/go-ipns@6e1876c2d81b48a80ede344b3d90f80495b237b2 --- ipns/README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/ipns/README.md b/ipns/README.md index ca2dbd7ea..75d7ca3f9 100644 --- a/ipns/README.md +++ b/ipns/README.md @@ -11,6 +11,35 @@ This package contains all of components necessary to create, understand, and validate IPNS records. +## Usage + +To create a new IPNS record: + +```go +import ( + "time" + + ipns "github.com/ipfs/go-ipns" + crypto "github.com/libp2p/go-libp2p-crypto" +) + +// Generate a private key to sign the IPNS record with. Most of the time, +// however, you'll want to retrieve an already-existing key from IPFS using the +// go-ipfs/core/coreapi CoreAPI.KeyAPI() interface. +privateKey, publicKey, err := crypto.GenerateKeyPair(crypto.RSA, 2048) + +// Create an IPNS record that expires in one hour and points to the IPFS address +// /ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5 +ipnsRecord, err := ipns.Create(privateKey, []byte("/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5"), 0, time.Now().Add(1*time.Hour)) +if err != nil { + panic(err) +} +``` + +Once you have the record, you’ll need to use IPFS to *publish* it. + +There are several other major operations you can do with `go-ipns`. Check out the [API docs](https://godoc.org/github.com/ipfs/go-ipns) or look at the tests in this repo for examples. + ## Documentation https://godoc.org/github.com/ipfs/go-ipns From f706aa07d6f6b70b75a74c1a2f3005f979946a0a Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Mon, 18 Jun 2018 14:11:11 -0700 Subject: [PATCH 2373/3817] Add `Create` example to godoc License: MIT Signed-off-by: Rob Brackett This commit was moved from ipfs/go-ipns@fa15ed2ecc1a7b2d198f160fcc6d5e94ad87e1c7 --- ipns/ipns_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ipns/ipns_test.go b/ipns/ipns_test.go index d46423fef..426a43101 100644 --- a/ipns/ipns_test.go +++ b/ipns/ipns_test.go @@ -41,3 +41,17 @@ func TestEmbedPublicKey(t *testing.T) { t.Fatalf("pid mismatch: %s != %s", pid, embeddedPid) } } + +func ExampleCreate() { + // Generate a private key to sign the IPNS record with. Most of the time, + // however, you'll want to retrieve an already-existing key from IPFS using + // go-ipfs/core/coreapi CoreAPI.KeyAPI() interface. + privateKey, publicKey, err := ic.GenerateKeyPair(ic.RSA, 2048) + + // Create an IPNS record that expires in one hour and points to the IPFS address + // /ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5 + ipnsRecord, err := Create(privateKey, []byte("/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5"), 0, time.Now().Add(1*time.Hour)) + if err != nil { + panic(err) + } +} From 3209a0c783c927e27ea876a2439077dcc55af1de Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Mon, 18 Jun 2018 14:48:04 -0700 Subject: [PATCH 2374/3817] Add note about what this package does *not* do This is based on a note in #1, which seems like an important and useful clarification :) License: MIT Signed-off-by: Rob Brackett This commit was moved from ipfs/go-ipns@a52d149c80aa8720c10f0e563cc95362ddc62d09 --- ipns/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ipns/README.md b/ipns/README.md index 75d7ca3f9..edc5e5e63 100644 --- a/ipns/README.md +++ b/ipns/README.md @@ -8,8 +8,7 @@ > ipns record definitions -This package contains all of components necessary to create, understand, and -validate IPNS records. +This package contains all of components necessary to create, understand, and validate IPNS records. It does *not* publish or resolve those records. [`go-ipfs`](https://github.com/ipfs/go-ipfs) uses this package internally to manipulate records. ## Usage From 86d38fa20b7f21eb67d2049870b1dbb31945a573 Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Mon, 18 Jun 2018 14:57:42 -0700 Subject: [PATCH 2375/3817] Follow Protocol Labs licensing policy Update the README and add a LICENSE file to follow our licensing policy: https://github.com/ipfs/community/blob/68f2fc02c4384eeb765ebc7547ea07f3aa2268c1/docs/licensing-policy.md License: MIT Signed-off-by: Rob Brackett This commit was moved from ipfs/go-ipns@8a9a5c36889b73e48667556589d5511af91c1089 --- ipns/LICENSE | 21 +++++++++++++++++++++ ipns/README.md | 3 +-- 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 ipns/LICENSE diff --git a/ipns/LICENSE b/ipns/LICENSE new file mode 100644 index 000000000..8ce028785 --- /dev/null +++ b/ipns/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Protocol Labs, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/ipns/README.md b/ipns/README.md index ca2dbd7ea..f1af2dd87 100644 --- a/ipns/README.md +++ b/ipns/README.md @@ -27,5 +27,4 @@ This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/c ## License -MIT - +Copyright (c) Protocol Labs, Inc. under the **MIT license**. See [LICENSE file](./LICENSE) for details. From 78976bfb69d14f0561d6c4ffc2ccaddb972b29fa Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Mon, 18 Jun 2018 15:07:36 -0700 Subject: [PATCH 2376/3817] Satisfy `go vet` License: MIT Signed-off-by: Rob Brackett This commit was moved from ipfs/go-ipns@9d17206712ec41333c75cea41498a88c7b83a81d --- ipns/README.md | 3 +++ ipns/ipns_test.go | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ipns/README.md b/ipns/README.md index edc5e5e63..966cce93b 100644 --- a/ipns/README.md +++ b/ipns/README.md @@ -26,6 +26,9 @@ import ( // however, you'll want to retrieve an already-existing key from IPFS using the // go-ipfs/core/coreapi CoreAPI.KeyAPI() interface. privateKey, publicKey, err := crypto.GenerateKeyPair(crypto.RSA, 2048) +if err != nil { + panic(err) +} // Create an IPNS record that expires in one hour and points to the IPFS address // /ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5 diff --git a/ipns/ipns_test.go b/ipns/ipns_test.go index 426a43101..0f2e30d79 100644 --- a/ipns/ipns_test.go +++ b/ipns/ipns_test.go @@ -1,6 +1,7 @@ package ipns import ( + "fmt" "testing" "time" @@ -46,7 +47,10 @@ func ExampleCreate() { // Generate a private key to sign the IPNS record with. Most of the time, // however, you'll want to retrieve an already-existing key from IPFS using // go-ipfs/core/coreapi CoreAPI.KeyAPI() interface. - privateKey, publicKey, err := ic.GenerateKeyPair(ic.RSA, 2048) + privateKey, _, err := ci.GenerateKeyPair(ci.RSA, 2048) + if err != nil { + panic(err) + } // Create an IPNS record that expires in one hour and points to the IPFS address // /ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5 @@ -54,4 +58,6 @@ func ExampleCreate() { if err != nil { panic(err) } + + fmt.Println(ipnsRecord) } From 722ab70d3a9c9f418bafb311e8a5ae1849efc6c2 Mon Sep 17 00:00:00 2001 From: potsables Date: Mon, 18 Jun 2018 20:59:26 -0700 Subject: [PATCH 2377/3817] added examples This commit was moved from ipfs/go-ipns@7651b3ea93b1a0adaa5deb7fc65a65631b98f3cf --- ipns/examples/embed.go | 27 +++++++++++++++ ipns/examples/examples_test.go | 63 ++++++++++++++++++++++++++++++++++ ipns/examples/key.go | 35 +++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 ipns/examples/embed.go create mode 100644 ipns/examples/examples_test.go create mode 100644 ipns/examples/key.go diff --git a/ipns/examples/embed.go b/ipns/examples/embed.go new file mode 100644 index 000000000..c9d196789 --- /dev/null +++ b/ipns/examples/embed.go @@ -0,0 +1,27 @@ +package examples + +import ( + "time" + + pb "github.com/ipfs/go-ipns/pb" + + ipns "github.com/ipfs/go-ipns" + crypto "github.com/libp2p/go-libp2p-crypto" +) + +// CreateEntryWithEmbed shows how you can create an IPNS entry +// and embed it with a public key. For ed25519 keys this is not needed +// so attempting to embed with an ed25519 key, will not actually embed the key +func CreateEntryWithEmbed(ipfsPath string, publicKey crypto.PubKey, privateKey crypto.PrivKey) (*pb.IpnsEntry, error) { + ipfsPathByte := []byte(ipfsPath) + eol := time.Now().Add(time.Hour * 48) + entry, err := ipns.Create(privateKey, ipfsPathByte, 1, eol) + if err != nil { + return nil, err + } + err = ipns.EmbedPublicKey(publicKey, entry) + if err != nil { + return nil, nil + } + return entry, nil +} diff --git a/ipns/examples/examples_test.go b/ipns/examples/examples_test.go new file mode 100644 index 000000000..f51c8c624 --- /dev/null +++ b/ipns/examples/examples_test.go @@ -0,0 +1,63 @@ +package examples_test + +import ( + "testing" + + "github.com/ipfs/go-ipns/examples" +) + +var testPath = "/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5" + +func TestKeyGeneration(t *testing.T) { + _, err := generateRSAKey() + if err != nil { + t.Error(err) + } + + _, err = generateEDKey() + if err != nil { + t.Error(err) + } +} + +func TestEmbeddedEntryCreation(t *testing.T) { + rk, err := generateRSAKey() + if err != nil { + t.Fatal(err) + } + + ek, err := generateEDKey() + if err != nil { + t.Fatal(err) + } + + _, err = examples.CreateEntryWithEmbed(testPath, rk.Public, rk.Private) + if err != nil { + t.Error(err) + } + + _, err = examples.CreateEntryWithEmbed(testPath, ek.Public, ek.Private) + if err != nil { + t.Error(err) + } + +} +func generateRSAKey() (*examples.KeyPair, error) { + // DO NOT USE 1024 BITS IN PRODUCTION + // THIS IS ONLY FOR TESTING PURPOSES + kp, err := examples.GenerateRSAKeyPair(1024) + if err != nil { + return nil, err + } + return kp, nil +} + +func generateEDKey() (*examples.KeyPair, error) { + // DO NOT USE 1024 BITS IN PRODUCTION + // THIS IS ONLY FOR TESTING PURPOSES + kp, err := examples.GenerateEDKeyPair(1024) + if err != nil { + return nil, err + } + return kp, nil +} diff --git a/ipns/examples/key.go b/ipns/examples/key.go new file mode 100644 index 000000000..1433f0079 --- /dev/null +++ b/ipns/examples/key.go @@ -0,0 +1,35 @@ +package examples + +import ( + crypto "github.com/libp2p/go-libp2p-crypto" +) + +// KeyPair is a helper struct used to contain the parts of a key +type KeyPair struct { + Private crypto.PrivKey + Public crypto.PubKey +} + +// GenerateRSAKeyPair is used to generate an RSA key pair +func GenerateRSAKeyPair(bits int) (*KeyPair, error) { + var kp KeyPair + priv, pub, err := crypto.GenerateKeyPair(crypto.RSA, bits) + if err != nil { + return nil, err + } + kp.Private = priv + kp.Public = pub + return &kp, nil +} + +// GenerateEDKeyPair is used to generate an ED25519 keypair +func GenerateEDKeyPair(bits int) (*KeyPair, error) { + var kp KeyPair + priv, pub, err := crypto.GenerateKeyPair(crypto.Ed25519, bits) + if err != nil { + return nil, err + } + kp.Private = priv + kp.Public = pub + return &kp, nil +} From 78b8172fc6905d7f5f0b1b30f14fe4db58e2e90f Mon Sep 17 00:00:00 2001 From: potsables Date: Mon, 18 Jun 2018 21:02:57 -0700 Subject: [PATCH 2378/3817] Had a small typo return nil,nil instead of nil,err This commit was moved from ipfs/go-ipns@5e4b85a466c9134ba30860053768ec2c32d110c6 --- ipns/examples/embed.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipns/examples/embed.go b/ipns/examples/embed.go index c9d196789..ffc635eaf 100644 --- a/ipns/examples/embed.go +++ b/ipns/examples/embed.go @@ -21,7 +21,7 @@ func CreateEntryWithEmbed(ipfsPath string, publicKey crypto.PubKey, privateKey c } err = ipns.EmbedPublicKey(publicKey, entry) if err != nil { - return nil, nil + return nil, err } return entry, nil } From cbb60852e88bea1c92a41f6562ef11cc472088fa Mon Sep 17 00:00:00 2001 From: potsables Date: Mon, 18 Jun 2018 21:51:29 -0700 Subject: [PATCH 2379/3817] updates tests and examples as per Stebalien's comment This commit was moved from ipfs/go-ipns@3b408d34642bf02231833fce0dbde14e1fa15060 --- ipns/examples/examples_test.go | 23 +++++++++++------------ ipns/examples/key.go | 25 +++++++------------------ 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/ipns/examples/examples_test.go b/ipns/examples/examples_test.go index f51c8c624..eb67d7a07 100644 --- a/ipns/examples/examples_test.go +++ b/ipns/examples/examples_test.go @@ -3,7 +3,8 @@ package examples_test import ( "testing" - "github.com/ipfs/go-ipns/examples" + "github.com/RTradeLtd/go-ipns/examples" + crypto "github.com/libp2p/go-libp2p-crypto" ) var testPath = "/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5" @@ -30,34 +31,32 @@ func TestEmbeddedEntryCreation(t *testing.T) { if err != nil { t.Fatal(err) } - - _, err = examples.CreateEntryWithEmbed(testPath, rk.Public, rk.Private) + _, err = examples.CreateEntryWithEmbed(testPath, rk.GetPublic(), rk) if err != nil { t.Error(err) } - _, err = examples.CreateEntryWithEmbed(testPath, ek.Public, ek.Private) + _, err = examples.CreateEntryWithEmbed(testPath, ek.GetPublic(), ek) if err != nil { t.Error(err) } } -func generateRSAKey() (*examples.KeyPair, error) { +func generateRSAKey() (crypto.PrivKey, error) { // DO NOT USE 1024 BITS IN PRODUCTION // THIS IS ONLY FOR TESTING PURPOSES - kp, err := examples.GenerateRSAKeyPair(1024) + k, err := examples.GenerateRSAKeyPair(1024) if err != nil { return nil, err } - return kp, nil + return k, nil } -func generateEDKey() (*examples.KeyPair, error) { - // DO NOT USE 1024 BITS IN PRODUCTION - // THIS IS ONLY FOR TESTING PURPOSES - kp, err := examples.GenerateEDKeyPair(1024) +func generateEDKey() (crypto.PrivKey, error) { + // ED25519 uses 256bit keys, and ignore the bit param + k, err := examples.GenerateEDKeyPair() if err != nil { return nil, err } - return kp, nil + return k, nil } diff --git a/ipns/examples/key.go b/ipns/examples/key.go index 1433f0079..408e3da80 100644 --- a/ipns/examples/key.go +++ b/ipns/examples/key.go @@ -4,32 +4,21 @@ import ( crypto "github.com/libp2p/go-libp2p-crypto" ) -// KeyPair is a helper struct used to contain the parts of a key -type KeyPair struct { - Private crypto.PrivKey - Public crypto.PubKey -} - // GenerateRSAKeyPair is used to generate an RSA key pair -func GenerateRSAKeyPair(bits int) (*KeyPair, error) { - var kp KeyPair - priv, pub, err := crypto.GenerateKeyPair(crypto.RSA, bits) +func GenerateRSAKeyPair(bits int) (crypto.PrivKey, error) { + priv, _, err := crypto.GenerateKeyPair(crypto.RSA, bits) if err != nil { return nil, err } - kp.Private = priv - kp.Public = pub - return &kp, nil + return priv, nil } // GenerateEDKeyPair is used to generate an ED25519 keypair -func GenerateEDKeyPair(bits int) (*KeyPair, error) { - var kp KeyPair - priv, pub, err := crypto.GenerateKeyPair(crypto.Ed25519, bits) +func GenerateEDKeyPair() (crypto.PrivKey, error) { + // ED25519 ignores the bit param and uses 256bit keys + priv, _, err := crypto.GenerateKeyPair(crypto.Ed25519, 256) if err != nil { return nil, err } - kp.Private = priv - kp.Public = pub - return &kp, nil + return priv, nil } From 55d9ae443575e7ca7e24ed2cd452a5330d3356ff Mon Sep 17 00:00:00 2001 From: potsables Date: Mon, 18 Jun 2018 21:52:26 -0700 Subject: [PATCH 2380/3817] changed import path to use ipfs repo This commit was moved from ipfs/go-ipns@cbfa50ea5dce40724a6ee84dad062608e37e1448 --- ipns/examples/examples_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipns/examples/examples_test.go b/ipns/examples/examples_test.go index eb67d7a07..af765f9f9 100644 --- a/ipns/examples/examples_test.go +++ b/ipns/examples/examples_test.go @@ -3,7 +3,7 @@ package examples_test import ( "testing" - "github.com/RTradeLtd/go-ipns/examples" + "github.com/ipfs/go-ipns/examples" crypto "github.com/libp2p/go-libp2p-crypto" ) From 1e4c83d145f4276d0d54f05804202b206dbec8e0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 13 Jun 2018 20:04:48 -0700 Subject: [PATCH 2381/3817] add record validation to offline routing fixes #5115 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@80b447da71616c176aa55e448b9f5e22111bc99f --- blockservice/test/mock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index 5018a6c8d..bc6a66835 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -5,8 +5,8 @@ import ( bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" + mockrouting "gx/ipfs/QmQUPmFYZBSWn4mtX1YwYkSaMoWVore7tCiSetr6k8JW21/go-ipfs-routing/mock" delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" - mockrouting "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/mock" ) // Mocks returns |n| connected mock Blockservices From 637e58ed0e01d90f51136bc0a2b524413909ddd0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 13 Jun 2018 20:04:48 -0700 Subject: [PATCH 2382/3817] add record validation to offline routing fixes #5115 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@7e0fe91fc0d80e6239af995bd0e270c68b8a92ad --- namesys/ipns_resolver_validation_test.go | 42 ++++++++++-------------- namesys/namesys_test.go | 17 ++++++++-- namesys/publisher.go | 4 +-- namesys/publisher_test.go | 4 +-- namesys/republisher/repub.go | 2 +- namesys/resolve_test.go | 4 +-- namesys/routing.go | 4 +-- 7 files changed, 42 insertions(+), 35 deletions(-) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 864aa443a..5a3150f05 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -11,12 +11,13 @@ import ( record "gx/ipfs/QmPWjVzxHeJdrjp4Jr2R2sPxBrMbBgGPWQtKwCKHHCBF7x/go-libp2p-record" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" testutil "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil" - ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns" + mockrouting "gx/ipfs/QmQUPmFYZBSWn4mtX1YwYkSaMoWVore7tCiSetr6k8JW21/go-ipfs-routing/mock" + offline "gx/ipfs/QmQUPmFYZBSWn4mtX1YwYkSaMoWVore7tCiSetr6k8JW21/go-ipfs-routing/offline" routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing" ropts "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing/options" peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" pstore "gx/ipfs/QmZhsmorLpD9kmQ4ynbAu4vbKv2goMUnXazwGA4gnWHDjB/go-libp2p-peerstore" - mockrouting "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/mock" + ipns "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" @@ -31,6 +32,8 @@ func TestResolverValidation(t *testing.T) { vstore := newMockValueStore(rid, dstore, peerstore) resolver := NewIpnsResolver(vstore) + nvVstore := offline.NewOfflineRouter(dstore, mockrouting.MockValidator{}) + // Create entry with expiry in one hour priv, id, _, ipnsDHTPath := genKeys(t) ts := time.Now() @@ -68,7 +71,7 @@ func TestResolverValidation(t *testing.T) { } // Publish entry - err = PublishEntry(ctx, vstore, ipnsDHTPath, expiredEntry) + err = PublishEntry(ctx, nvVstore, ipnsDHTPath, expiredEntry) if err != nil { t.Fatal(err) } @@ -89,7 +92,7 @@ func TestResolverValidation(t *testing.T) { } // Publish entry - err = PublishEntry(ctx, vstore, ipnsDHTPath2, entry) + err = PublishEntry(ctx, nvVstore, ipnsDHTPath2, entry) if err != nil { t.Fatal(err) } @@ -107,7 +110,7 @@ func TestResolverValidation(t *testing.T) { if err != nil { t.Fatal(err) } - err = PublishEntry(ctx, vstore, ipnsDHTPath3, entry3) + err = PublishEntry(ctx, nvVstore, ipnsDHTPath3, entry3) if err != nil { t.Fatal(err) } @@ -152,31 +155,22 @@ func genKeys(t *testing.T) (ci.PrivKey, peer.ID, string, string) { } type mockValueStore struct { - r routing.ValueStore - kbook pstore.KeyBook - Validator record.Validator + r routing.ValueStore + kbook pstore.KeyBook } func newMockValueStore(id testutil.Identity, dstore ds.Datastore, kbook pstore.KeyBook) *mockValueStore { - serv := mockrouting.NewServer() - r := serv.ClientWithDatastore(context.Background(), id, dstore) - return &mockValueStore{r, kbook, record.NamespacedValidator{ - "ipns": ipns.Validator{KeyBook: kbook}, - "pk": record.PublicKeyValidator{}, - }} + return &mockValueStore{ + r: offline.NewOfflineRouter(dstore, record.NamespacedValidator{ + "ipns": ipns.Validator{KeyBook: kbook}, + "pk": record.PublicKeyValidator{}, + }), + kbook: kbook, + } } func (m *mockValueStore) GetValue(ctx context.Context, k string, opts ...ropts.Option) ([]byte, error) { - data, err := m.r.GetValue(ctx, k, opts...) - if err != nil { - return data, err - } - - if err = m.Validator.Validate(k, data); err != nil { - return nil, err - } - - return data, err + return m.r.GetValue(ctx, k, opts...) } func (m *mockValueStore) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey, error) { diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index b4f35c0de..cdd8c51f6 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,7 +10,10 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/unixfs" - offroute "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/offline" + offroute "gx/ipfs/QmQUPmFYZBSWn4mtX1YwYkSaMoWVore7tCiSetr6k8JW21/go-ipfs-routing/offline" + peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" + pstore "gx/ipfs/QmZhsmorLpD9kmQ4ynbAu4vbKv2goMUnXazwGA4gnWHDjB/go-libp2p-peerstore" + ipns "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" @@ -83,7 +86,17 @@ func TestPublishWithCache0(t *testing.T) { if err != nil { t.Fatal(err) } - routing := offroute.NewOfflineRouter(dst, priv) + ps := pstore.NewPeerstore() + pid, err := peer.IDFromPrivateKey(priv) + if err != nil { + t.Fatal(err) + } + err = ps.AddPrivKey(pid, priv) + if err != nil { + t.Fatal(err) + } + + routing := offroute.NewOfflineRouter(dst, ipns.Validator{KeyBook: ps}) nsys := NewNameSystem(routing, dst, 0) p, err := path.ParsePath(unixfs.EmptyDirNode().Cid().String()) diff --git a/namesys/publisher.go b/namesys/publisher.go index 7eb548395..393e3181f 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -11,11 +11,11 @@ import ( pin "github.com/ipfs/go-ipfs/pin" ft "github.com/ipfs/go-ipfs/unixfs" - ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns" - pb "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns/pb" routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing" peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + ipns "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns" + pb "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns/pb" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsquery "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index a79492c3d..74e4339a5 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -8,10 +8,10 @@ import ( dshelp "gx/ipfs/QmNP2u7bofwUQptHQGPfabGWtTCbxhNLSZKqbf1uzsup9V/go-ipfs-ds-help" testutil "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil" - ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns" + mockrouting "gx/ipfs/QmQUPmFYZBSWn4mtX1YwYkSaMoWVore7tCiSetr6k8JW21/go-ipfs-routing/mock" ma "gx/ipfs/QmUxSEGbv2nmYNnfXi7839wwQqTN3kwQeUxe8dTjZWZs7J/go-multiaddr" peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" - mockrouting "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/mock" + ipns "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index fb90ccbc2..5f2254747 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -9,11 +9,11 @@ import ( namesys "github.com/ipfs/go-ipfs/namesys" path "github.com/ipfs/go-ipfs/path" - pb "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns/pb" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + pb "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns/pb" logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" ic "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index a930eb4e7..58ea5b400 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -9,9 +9,9 @@ import ( path "github.com/ipfs/go-ipfs/path" testutil "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil" - ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns" + mockrouting "gx/ipfs/QmQUPmFYZBSWn4mtX1YwYkSaMoWVore7tCiSetr6k8JW21/go-ipfs-routing/mock" peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" - mockrouting "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/mock" + ipns "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/namesys/routing.go b/namesys/routing.go index 22209e8f0..05110eba5 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -9,13 +9,13 @@ import ( path "github.com/ipfs/go-ipfs/path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns" - pb "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns/pb" routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing" peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" dht "gx/ipfs/QmagBkuFfySAMouyXeiy8XjV1GyfNAgTCuVYGF9z3Z4Vvc/go-libp2p-kad-dht" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + ipns "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns" + pb "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns/pb" logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" ) From 0d85bd2de98acd5f8c7b93cfdc83f765da58c722 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 25 Jun 2018 20:41:25 -0700 Subject: [PATCH 2383/3817] gx update Updates: * go-kad-dht: Query performance improvements, DHT client fixes, validates records on *local* put. * go-libp2p-swarm/go-libp2p-transport: Timeout improvements. * go-multiaddr-net: Exposes useful Conn methods (CloseWrite, CloseRead, etc.) * go-log: fixes possible panic when enabling/disabling events. * go-multiaddr: fixes possible panic when stringifying malformed multiaddrs, adds support for consuming /p2p/ multiaddrs. fixes #5113 unblocks #4895 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@afe71e4b0597f42a403da021fc1d8c22cc987973 --- unixfs/mod/dagmodifier.go | 2 +- unixfs/test/utils.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index c6648b9ab..adc172428 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -14,8 +14,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - chunker "gx/ipfs/QmR4G4WBNGA5S5pvjFiTkuehstC9769sLAHei8vZernhYR/go-ipfs-chunker" ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + chunker "gx/ipfs/QmXnzH7wowyLZy8XJxxaQCVTgLMcDXdMBznmsrmQWCyiQV/go-ipfs-chunker" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index d92547e9f..a59aeaea5 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -16,8 +16,8 @@ import ( u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - chunker "gx/ipfs/QmR4G4WBNGA5S5pvjFiTkuehstC9769sLAHei8vZernhYR/go-ipfs-chunker" ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + chunker "gx/ipfs/QmXnzH7wowyLZy8XJxxaQCVTgLMcDXdMBznmsrmQWCyiQV/go-ipfs-chunker" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) From 4114369421714ed6a7fe80462a4c8186c14ef2b5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 25 Jun 2018 20:41:25 -0700 Subject: [PATCH 2384/3817] gx update Updates: * go-kad-dht: Query performance improvements, DHT client fixes, validates records on *local* put. * go-libp2p-swarm/go-libp2p-transport: Timeout improvements. * go-multiaddr-net: Exposes useful Conn methods (CloseWrite, CloseRead, etc.) * go-log: fixes possible panic when enabling/disabling events. * go-multiaddr: fixes possible panic when stringifying malformed multiaddrs, adds support for consuming /p2p/ multiaddrs. fixes #5113 unblocks #4895 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@b5d3b18b7aed70e1e74b363e50ff24f5c8cac4fe --- mfs/file.go | 2 +- mfs/mfs_test.go | 6 +++--- mfs/repub_test.go | 2 +- mfs/system.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mfs/file.go b/mfs/file.go index 7ced25eed..f28cf79f8 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -9,8 +9,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - chunker "gx/ipfs/QmR4G4WBNGA5S5pvjFiTkuehstC9769sLAHei8vZernhYR/go-ipfs-chunker" ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + chunker "gx/ipfs/QmXnzH7wowyLZy8XJxxaQCVTgLMcDXdMBznmsrmQWCyiQV/go-ipfs-chunker" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 9d03b45f2..7088c8c9c 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -22,11 +22,11 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" - chunker "gx/ipfs/QmR4G4WBNGA5S5pvjFiTkuehstC9769sLAHei8vZernhYR/go-ipfs-chunker" + offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + chunker "gx/ipfs/QmXnzH7wowyLZy8XJxxaQCVTgLMcDXdMBznmsrmQWCyiQV/go-ipfs-chunker" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - bstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" + bstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/mfs/repub_test.go b/mfs/repub_test.go index cec6f699d..0782d50c8 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - ci "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil/ci" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + ci "gx/ipfs/QmcW4FGAt24fdK1jBgWQn3yP4R9ZLyWQqjozv9QK7epRhL/go-testutil/ci" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index 975d3da67..67f60d5d9 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -21,7 +21,7 @@ import ( ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" + logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" ) var ErrNotExist = errors.New("no such rootfs") From 371adcd86156d09c59608bf140672882d97b1c0f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 25 Jun 2018 20:41:25 -0700 Subject: [PATCH 2385/3817] gx update Updates: * go-kad-dht: Query performance improvements, DHT client fixes, validates records on *local* put. * go-libp2p-swarm/go-libp2p-transport: Timeout improvements. * go-multiaddr-net: Exposes useful Conn methods (CloseWrite, CloseRead, etc.) * go-log: fixes possible panic when enabling/disabling events. * go-multiaddr: fixes possible panic when stringifying malformed multiaddrs, adds support for consuming /p2p/ multiaddrs. fixes #5113 unblocks #4895 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@1e6bfc7a62bf7b978e7a69e915e2651424969f60 --- filestore/filestore.go | 4 ++-- filestore/filestore_test.go | 2 +- filestore/fsrefstore.go | 2 +- filestore/util.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 0df41aafb..66289b068 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -13,8 +13,8 @@ import ( blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" - logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" + logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" + blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 43fa263b8..9589c3dd5 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -11,7 +11,7 @@ import ( posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" + blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 1b1b94ea7..2e481ade9 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -14,7 +14,7 @@ import ( blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" + blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsns "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/namespace" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" diff --git a/filestore/util.go b/filestore/util.go index 8af7d860b..55a099859 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -8,7 +8,7 @@ import ( dshelp "gx/ipfs/QmNP2u7bofwUQptHQGPfabGWtTCbxhNLSZKqbf1uzsup9V/go-ipfs-ds-help" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" + blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) From 5eff93470b291e7bc6388cf9ec77eefc12494863 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 25 Jun 2018 20:41:25 -0700 Subject: [PATCH 2386/3817] gx update Updates: * go-kad-dht: Query performance improvements, DHT client fixes, validates records on *local* put. * go-libp2p-swarm/go-libp2p-transport: Timeout improvements. * go-multiaddr-net: Exposes useful Conn methods (CloseWrite, CloseRead, etc.) * go-log: fixes possible panic when enabling/disabling events. * go-multiaddr: fixes possible panic when stringifying malformed multiaddrs, adds support for consuming /p2p/ multiaddrs. fixes #5113 unblocks #4895 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-path@b501bd66c4faab2c9e13bb2141095024ae3958d3 --- path/resolver/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index a263f1150..73ac3fa23 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -12,7 +12,7 @@ import ( ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" + logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" ) var log = logging.Logger("pathresolv") From 50ff34d38b2141f7d5b9f85048836c150de270ce Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 25 Jun 2018 20:41:25 -0700 Subject: [PATCH 2387/3817] gx update Updates: * go-kad-dht: Query performance improvements, DHT client fixes, validates records on *local* put. * go-libp2p-swarm/go-libp2p-transport: Timeout improvements. * go-multiaddr-net: Exposes useful Conn methods (CloseWrite, CloseRead, etc.) * go-log: fixes possible panic when enabling/disabling events. * go-multiaddr: fixes possible panic when stringifying malformed multiaddrs, adds support for consuming /p2p/ multiaddrs. fixes #5113 unblocks #4895 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@0821b4ddb1aa3ee33b060775a0f6c98c11c7fab6 --- ipld/merkledag/merkledag_test.go | 2 +- ipld/merkledag/test/utils.go | 4 ++-- ipld/merkledag/utils/utils.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 460df81be..86cc66040 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -20,7 +20,7 @@ import ( dstest "github.com/ipfs/go-ipfs/merkledag/test" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index dfaef609a..e9b07f06c 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -4,9 +4,9 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" dag "github.com/ipfs/go-ipfs/merkledag" - offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" + blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 32515b5b2..e527e105a 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -8,9 +8,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - bstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" + bstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" syncds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) From bd3961e6e3e84f8b332c94017173bf9cacd93bd7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 25 Jun 2018 20:41:25 -0700 Subject: [PATCH 2388/3817] gx update Updates: * go-kad-dht: Query performance improvements, DHT client fixes, validates records on *local* put. * go-libp2p-swarm/go-libp2p-transport: Timeout improvements. * go-multiaddr-net: Exposes useful Conn methods (CloseWrite, CloseRead, etc.) * go-log: fixes possible panic when enabling/disabling events. * go-multiaddr: fixes possible panic when stringifying malformed multiaddrs, adds support for consuming /p2p/ multiaddrs. fixes #5113 unblocks #4895 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@7479955c0624bd6eac5b5325d48e11c7a7ec496f --- blockservice/blockservice.go | 4 ++-- blockservice/blockservice_test.go | 4 ++-- blockservice/test/blocks_test.go | 4 ++-- blockservice/test/mock.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index c39b36d25..38c67bbd3 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -14,8 +14,8 @@ import ( blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" exchange "gx/ipfs/QmVSe7YJbPnEmkSUKD3HxSvp8HJoyCU55hQoCMRq7N1jaK/go-ipfs-exchange-interface" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" - logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" + logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" + blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ) var log = logging.Logger("blockservice") diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index d3c0bb1e6..d8bdaa9b1 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -3,10 +3,10 @@ package blockservice import ( "testing" - offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" butil "gx/ipfs/QmYmE4kxv6uFGaWkeBAFYDuNcxzCn87pzwm6CkBkM9C8BM/go-ipfs-blocksutil" - blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" + blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 745a1c727..ee63050db 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -10,10 +10,10 @@ import ( . "github.com/ipfs/go-ipfs/blockservice" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" + blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index bc6a66835..425cf7659 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -5,8 +5,8 @@ import ( bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" - mockrouting "gx/ipfs/QmQUPmFYZBSWn4mtX1YwYkSaMoWVore7tCiSetr6k8JW21/go-ipfs-routing/mock" delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" + mockrouting "gx/ipfs/QmWLQyLU7yopJnwMvpHM5VSMG4xmbKgcq6P246mDy9xy5E/go-ipfs-routing/mock" ) // Mocks returns |n| connected mock Blockservices From 2b830fb307e89f1b85a56738bcdf60f203dd2831 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 25 Jun 2018 20:41:25 -0700 Subject: [PATCH 2389/3817] gx update Updates: * go-kad-dht: Query performance improvements, DHT client fixes, validates records on *local* put. * go-libp2p-swarm/go-libp2p-transport: Timeout improvements. * go-multiaddr-net: Exposes useful Conn methods (CloseWrite, CloseRead, etc.) * go-log: fixes possible panic when enabling/disabling events. * go-multiaddr: fixes possible panic when stringifying malformed multiaddrs, adds support for consuming /p2p/ multiaddrs. fixes #5113 unblocks #4895 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@b8056e418facbf951985601e323b9b1701b1f881 --- pinning/pinner/gc/gc.go | 6 +++--- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set_test.go | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 69ca731d1..d1dd9a6d9 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -12,11 +12,11 @@ import ( pin "github.com/ipfs/go-ipfs/pin" "github.com/ipfs/go-ipfs/thirdparty/verifcid" - offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - bstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" - logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" + logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" + bstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" dstore "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 348ead7bf..3bf114bd6 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -14,7 +14,7 @@ import ( ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" + logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 0a0f65206..9f4a397f2 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -9,9 +9,9 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" + blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 221487173..fefda87d9 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -8,9 +8,9 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" dag "github.com/ipfs/go-ipfs/merkledag" - offline "gx/ipfs/QmPf114DXfa6TqGKYhBGR7EtXRho4rCJgwyA1xkuMY5vwF/go-ipfs-exchange-offline" + offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - blockstore "gx/ipfs/QmbaPGg81pvQiC5vTXtC9Jo8rdrWUjRaugH71WYNsgi6Ev/go-ipfs-blockstore" + blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) From b69c4796e2fd40bce19f9c1db8bec5600c771ee0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 25 Jun 2018 20:41:25 -0700 Subject: [PATCH 2390/3817] gx update Updates: * go-kad-dht: Query performance improvements, DHT client fixes, validates records on *local* put. * go-libp2p-swarm/go-libp2p-transport: Timeout improvements. * go-multiaddr-net: Exposes useful Conn methods (CloseWrite, CloseRead, etc.) * go-log: fixes possible panic when enabling/disabling events. * go-multiaddr: fixes possible panic when stringifying malformed multiaddrs, adds support for consuming /p2p/ multiaddrs. fixes #5113 unblocks #4895 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@d8f4e9f24d974cfa5f4a483418406cf6aa4143b3 --- namesys/ipns_resolver_validation_test.go | 18 +++++++++--------- namesys/namesys.go | 4 ++-- namesys/namesys_test.go | 8 ++++---- namesys/publisher.go | 8 ++++---- namesys/publisher_test.go | 10 +++++----- namesys/republisher/repub.go | 6 +++--- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 8 ++++---- namesys/routing.go | 12 ++++++------ 9 files changed, 39 insertions(+), 39 deletions(-) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 5a3150f05..faf9d3e3b 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -8,16 +8,16 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" - record "gx/ipfs/QmPWjVzxHeJdrjp4Jr2R2sPxBrMbBgGPWQtKwCKHHCBF7x/go-libp2p-record" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - testutil "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil" - mockrouting "gx/ipfs/QmQUPmFYZBSWn4mtX1YwYkSaMoWVore7tCiSetr6k8JW21/go-ipfs-routing/mock" - offline "gx/ipfs/QmQUPmFYZBSWn4mtX1YwYkSaMoWVore7tCiSetr6k8JW21/go-ipfs-routing/offline" - routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing" - ropts "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing/options" - peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" - pstore "gx/ipfs/QmZhsmorLpD9kmQ4ynbAu4vbKv2goMUnXazwGA4gnWHDjB/go-libp2p-peerstore" - ipns "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns" + routing "gx/ipfs/QmPpdpS9fknTBM3qHDcpayU6nYPZQeVjia2fbNrD8YWDe6/go-libp2p-routing" + ropts "gx/ipfs/QmPpdpS9fknTBM3qHDcpayU6nYPZQeVjia2fbNrD8YWDe6/go-libp2p-routing/options" + record "gx/ipfs/QmVsp2KdPYE6M8ryzCk5KHLo3zprcY5hBDaYx6uPCFUdxA/go-libp2p-record" + mockrouting "gx/ipfs/QmWLQyLU7yopJnwMvpHM5VSMG4xmbKgcq6P246mDy9xy5E/go-ipfs-routing/mock" + offline "gx/ipfs/QmWLQyLU7yopJnwMvpHM5VSMG4xmbKgcq6P246mDy9xy5E/go-ipfs-routing/offline" + pstore "gx/ipfs/QmZR2XWVVBCtbgBWnQhWk2xcQfaR3W8faQPriAiaaj7rsr/go-libp2p-peerstore" + testutil "gx/ipfs/QmcW4FGAt24fdK1jBgWQn3yP4R9ZLyWQqjozv9QK7epRhL/go-testutil" + peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" + ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" diff --git a/namesys/namesys.go b/namesys/namesys.go index 99780cc88..06bd212fe 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -9,10 +9,10 @@ import ( path "github.com/ipfs/go-ipfs/path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing" + routing "gx/ipfs/QmPpdpS9fknTBM3qHDcpayU6nYPZQeVjia2fbNrD8YWDe6/go-libp2p-routing" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" + peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index cdd8c51f6..c6584e8fc 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,10 +10,10 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/unixfs" - offroute "gx/ipfs/QmQUPmFYZBSWn4mtX1YwYkSaMoWVore7tCiSetr6k8JW21/go-ipfs-routing/offline" - peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" - pstore "gx/ipfs/QmZhsmorLpD9kmQ4ynbAu4vbKv2goMUnXazwGA4gnWHDjB/go-libp2p-peerstore" - ipns "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns" + offroute "gx/ipfs/QmWLQyLU7yopJnwMvpHM5VSMG4xmbKgcq6P246mDy9xy5E/go-ipfs-routing/offline" + pstore "gx/ipfs/QmZR2XWVVBCtbgBWnQhWk2xcQfaR3W8faQPriAiaaj7rsr/go-libp2p-peerstore" + peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" + ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" diff --git a/namesys/publisher.go b/namesys/publisher.go index 393e3181f..d057dba76 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -11,11 +11,11 @@ import ( pin "github.com/ipfs/go-ipfs/pin" ft "github.com/ipfs/go-ipfs/unixfs" - routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing" - peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" + routing "gx/ipfs/QmPpdpS9fknTBM3qHDcpayU6nYPZQeVjia2fbNrD8YWDe6/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - ipns "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns" - pb "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns/pb" + peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" + ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" + pb "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns/pb" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsquery "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 74e4339a5..c96f38039 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -7,11 +7,11 @@ import ( "time" dshelp "gx/ipfs/QmNP2u7bofwUQptHQGPfabGWtTCbxhNLSZKqbf1uzsup9V/go-ipfs-ds-help" - testutil "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil" - mockrouting "gx/ipfs/QmQUPmFYZBSWn4mtX1YwYkSaMoWVore7tCiSetr6k8JW21/go-ipfs-routing/mock" - ma "gx/ipfs/QmUxSEGbv2nmYNnfXi7839wwQqTN3kwQeUxe8dTjZWZs7J/go-multiaddr" - peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" - ipns "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns" + mockrouting "gx/ipfs/QmWLQyLU7yopJnwMvpHM5VSMG4xmbKgcq6P246mDy9xy5E/go-ipfs-routing/mock" + ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" + testutil "gx/ipfs/QmcW4FGAt24fdK1jBgWQn3yP4R9ZLyWQqjozv9QK7epRhL/go-testutil" + peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" + ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 5f2254747..abfb283dd 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,10 +11,10 @@ import ( goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - pb "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns/pb" - logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" + logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" + peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" + pb "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns/pb" ic "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 8a76cbcc1..623a1b1e0 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,8 +13,8 @@ import ( path "github.com/ipfs/go-ipfs/path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmUEAR2pS7fP1GPseS3i8MWFyENs7oDp4CZrgn8FCjbsBu/go-libp2p/p2p/net/mock" - pstore "gx/ipfs/QmZhsmorLpD9kmQ4ynbAu4vbKv2goMUnXazwGA4gnWHDjB/go-libp2p-peerstore" + mocknet "gx/ipfs/QmZ86eLPtXkQ1Dfa992Q8NpXArUoWWh3y728JDcWvzRrvC/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmZR2XWVVBCtbgBWnQhWk2xcQfaR3W8faQPriAiaaj7rsr/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 58ea5b400..19c5443b3 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,10 +8,10 @@ import ( path "github.com/ipfs/go-ipfs/path" - testutil "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil" - mockrouting "gx/ipfs/QmQUPmFYZBSWn4mtX1YwYkSaMoWVore7tCiSetr6k8JW21/go-ipfs-routing/mock" - peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" - ipns "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns" + mockrouting "gx/ipfs/QmWLQyLU7yopJnwMvpHM5VSMG4xmbKgcq6P246mDy9xy5E/go-ipfs-routing/mock" + testutil "gx/ipfs/QmcW4FGAt24fdK1jBgWQn3yP4R9ZLyWQqjozv9QK7epRhL/go-testutil" + peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" + ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/namesys/routing.go b/namesys/routing.go index 05110eba5..0851aaa4f 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -8,15 +8,15 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" + dht "gx/ipfs/QmNg6M98bwS97SL9ArvrRxKujFps3eV6XvmKgduiYga8Bn/go-libp2p-kad-dht" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing" - peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer" + routing "gx/ipfs/QmPpdpS9fknTBM3qHDcpayU6nYPZQeVjia2fbNrD8YWDe6/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dht "gx/ipfs/QmagBkuFfySAMouyXeiy8XjV1GyfNAgTCuVYGF9z3Z4Vvc/go-libp2p-kad-dht" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - ipns "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns" - pb "gx/ipfs/Qmb7iqDPPNogT8fJeYoLavoKhnp41tpoMPJ9D5qZVYynNQ/go-ipns/pb" - logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" + logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" + peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" + ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" + pb "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns/pb" ) var log = logging.Logger("namesys") From 22e9bd86393d38d078c473fd2c0bfe3a374acffa Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 25 Jun 2018 20:41:25 -0700 Subject: [PATCH 2391/3817] gx update Updates: * go-kad-dht: Query performance improvements, DHT client fixes, validates records on *local* put. * go-libp2p-swarm/go-libp2p-transport: Timeout improvements. * go-multiaddr-net: Exposes useful Conn methods (CloseWrite, CloseRead, etc.) * go-log: fixes possible panic when enabling/disabling events. * go-multiaddr: fixes possible panic when stringifying malformed multiaddrs, adds support for consuming /p2p/ multiaddrs. fixes #5113 unblocks #4895 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-keystore@95a5cd3ebde8d58c5a2a8965e6c37ff5ab955e37 --- keystore/keystore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index 78bca6ecd..e16dc265e 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -7,7 +7,7 @@ import ( "path/filepath" "strings" - logging "gx/ipfs/Qmbi1CTJsbnBZjCEgc2otwu8cUFPsGpzWXG7edVCLZ7Gvk/go-log" + logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ) From 7c862dc96516290977feabb97ca033e4841dc2b4 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 27 Jun 2018 16:52:57 -0700 Subject: [PATCH 2392/3817] use `copy` instead of looping License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@b5263d6ae25657dbd787590689c06973bca6c976 --- unixfs/io/pbdagreader.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index ce53d6711..8c357fb8c 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -75,9 +75,7 @@ func (dr *PBDagReader) preloadNextNodes(ctx context.Context) { end = len(dr.links) } - for i, p := range ipld.GetNodes(ctx, dr.serv, dr.links[beg:end]) { - dr.promises[beg+i] = p - } + copy(dr.promises[beg:], ipld.GetNodes(ctx, dr.serv, dr.links[beg:end])) } // precalcNextBuf follows the next link in line and loads it from the From a9d34b38022bab235803ad902c107111f18469f8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 27 Jun 2018 17:06:30 -0700 Subject: [PATCH 2393/3817] better handle context cancellations in the PBDagReader Good: If a previous read is canceled, we cancel the preloads that the read triggered. Bad: Future reads at that point will fail. This fixes that issue. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@f191b87344d01f6437d31ce620f297605e528089 --- unixfs/io/pbdagreader.go | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 8c357fb8c..8d21f8da3 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -95,10 +95,27 @@ func (dr *PBDagReader) precalcNextBuf(ctx context.Context) error { } nxt, err := dr.promises[dr.linkPosition].Get(ctx) - if err != nil { + dr.promises[dr.linkPosition] = nil + switch err { + case nil: + case context.DeadlineExceeded, context.Canceled: + err = ctx.Err() + if err != nil { + return ctx.Err() + } + // In this case, the context used to *preload* the node has been canceled. + // We need to retry the load with our context and we might as + // well preload some extra nodes while we're at it. + dr.preload(ctx, dr.linkPosition) + nxt, err = dr.promises[dr.linkPosition].Get(ctx) + dr.promises[dr.linkPosition] = nil + if err != nil { + return err + } + default: return err } - dr.promises[dr.linkPosition] = nil + dr.linkPosition++ switch nxt := nxt.(type) { From f92d7d47787cbc6201acf7153302afbf6dbfac51 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 27 Jun 2018 17:08:50 -0700 Subject: [PATCH 2394/3817] always prefetch at least 5 blocks ahead This should reduce stuttering when streaming. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@cd315c81737f0694cddcfed1ab1621d418599017 --- unixfs/io/pbdagreader.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 8d21f8da3..9c3909577 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -68,8 +68,7 @@ func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv const preloadSize = 10 -func (dr *PBDagReader) preloadNextNodes(ctx context.Context) { - beg := dr.linkPosition +func (dr *PBDagReader) preload(ctx context.Context, beg int) { end := beg + preloadSize if end >= len(dr.links) { end = len(dr.links) @@ -90,8 +89,13 @@ func (dr *PBDagReader) precalcNextBuf(ctx context.Context) error { return io.EOF } - if dr.promises[dr.linkPosition] == nil { - dr.preloadNextNodes(ctx) + // If we drop to <= preloadSize/2 preloading nodes, preload the next 10. + for i := dr.linkPosition; i < dr.linkPosition+preloadSize/2 && i < len(dr.promises); i++ { + // TODO: check if canceled. + if dr.promises[i] == nil { + dr.preload(ctx, i) + break + } } nxt, err := dr.promises[dr.linkPosition].Get(ctx) From 8d57640596e64b602af4abb9f5894e32f2ec6ba2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 27 Jun 2018 17:51:26 -0700 Subject: [PATCH 2395/3817] test dag reader context cancellation License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@5901259ccd9898fb9653d7075859172a27ff74a8 --- unixfs/io/dagreader_test.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index e3d3d042b..7cbe35bb5 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -122,6 +122,41 @@ func TestSeekAndReadLarge(t *testing.T) { } } +func TestReadAndCancel(t *testing.T) { + dserv := testu.GetDAGServ() + inbuf := make([]byte, 20000) + rand.Read(inbuf) + + node := testu.GetNode(t, dserv, inbuf, testu.UseProtoBufLeaves) + ctx, closer := context.WithCancel(context.Background()) + defer closer() + + reader, err := NewDagReader(ctx, node, dserv) + if err != nil { + t.Fatal(err) + } + + ctx, cancel := context.WithCancel(context.Background()) + buf := make([]byte, 100) + _, err = reader.CtxReadFull(ctx, buf) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(buf, inbuf[0:100]) { + t.Fatal("read failed") + } + cancel() + + b, err := ioutil.ReadAll(reader) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(inbuf[100:], b) { + t.Fatal("buffers not equal") + } +} + func TestRelativeSeek(t *testing.T) { dserv := testu.GetDAGServ() ctx, closer := context.WithCancel(context.Background()) From 807e8d344c4c5b2730f93774b62c787d7053b9f1 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Fri, 29 Jun 2018 10:57:34 -0300 Subject: [PATCH 2396/3817] mfs: make `Root` value a `Directory` Make `Root` value explicitly a `Directory` structure instead of the `FSNode` interface (which also allowed the `File` type). This helps to make the code easier to reason about: the root of an MFS layout is always a directory, not a (single) file. Rename `GetValue()` to `GetDirectory()` to also make it more explicit, the renamed function now returns a `Directory` so there is no need for type assertions that were previously done on the `FSNode` interface to check that it was actually a `Directory`. `NewRoot()` now doesn't allow to create `Root` structures from DAG nodes that contain UnixFS files. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-mfs@09d6e80111f1450b8f1da0e12600cf0faf0ccd48 --- mfs/mfs_test.go | 22 +++++++++++----------- mfs/ops.go | 12 +++++------- mfs/system.go | 29 +++++++++++------------------ 3 files changed, 27 insertions(+), 36 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 9d03b45f2..85ac54d03 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -213,7 +213,7 @@ func TestBasic(t *testing.T) { defer cancel() ds, rt := setupRoot(ctx, t) - rootdir := rt.GetValue().(*Directory) + rootdir := rt.GetDirectory() // test making a basic dir _, err := rootdir.Mkdir("a") @@ -243,7 +243,7 @@ func TestMkdir(t *testing.T) { defer cancel() _, rt := setupRoot(ctx, t) - rootdir := rt.GetValue().(*Directory) + rootdir := rt.GetDirectory() dirsToMake := []string{"a", "B", "foo", "bar", "cats", "fish"} sort.Strings(dirsToMake) // sort for easy comparing later @@ -281,7 +281,7 @@ func TestDirectoryLoadFromDag(t *testing.T) { defer cancel() ds, rt := setupRoot(ctx, t) - rootdir := rt.GetValue().(*Directory) + rootdir := rt.GetDirectory() nd := getRandFile(t, ds, 1000) err := ds.Add(ctx, nd) @@ -373,7 +373,7 @@ func TestMfsFile(t *testing.T) { defer cancel() ds, rt := setupRoot(ctx, t) - rootdir := rt.GetValue().(*Directory) + rootdir := rt.GetDirectory() fisize := 1000 nd := getRandFile(t, ds, 1000) @@ -686,7 +686,7 @@ func actorReadFile(d *Directory) error { } func testActor(rt *Root, iterations int, errs chan error) { - d := rt.GetValue().(*Directory) + d := rt.GetDirectory() for i := 0; i < iterations; i++ { switch rand.Intn(5) { case 0: @@ -763,7 +763,7 @@ func TestConcurrentWriteAndFlush(t *testing.T) { defer cancel() ds, rt := setupRoot(ctx, t) - d := mkdirP(t, rt.GetValue().(*Directory), "foo/bar/baz") + d := mkdirP(t, rt.GetDirectory(), "foo/bar/baz") fn := fileNodeFromReader(t, ds, bytes.NewBuffer(nil)) err := d.AddChild("file", fn) if err != nil { @@ -786,7 +786,7 @@ func TestConcurrentWriteAndFlush(t *testing.T) { }() for i := 0; i < nloops; i++ { - _, err := rt.GetValue().GetNode() + _, err := rt.GetDirectory().GetNode() if err != nil { t.Fatal(err) } @@ -800,7 +800,7 @@ func TestFlushing(t *testing.T) { defer cancel() _, rt := setupRoot(ctx, t) - dir := rt.GetValue().(*Directory) + dir := rt.GetDirectory() c := mkdirP(t, dir, "a/b/c") d := mkdirP(t, dir, "a/b/d") e := mkdirP(t, dir, "a/b/e") @@ -901,7 +901,7 @@ func TestConcurrentReads(t *testing.T) { ds, rt := setupRoot(ctx, t) - rootdir := rt.GetValue().(*Directory) + rootdir := rt.GetDirectory() path := "a/b/c" d := mkdirP(t, rootdir, path) @@ -976,7 +976,7 @@ func TestConcurrentWrites(t *testing.T) { ds, rt := setupRoot(ctx, t) - rootdir := rt.GetValue().(*Directory) + rootdir := rt.GetDirectory() path := "a/b/c" d := mkdirP(t, rootdir, path) @@ -1011,7 +1011,7 @@ func TestFileDescriptors(t *testing.T) { defer cancel() ds, rt := setupRoot(ctx, t) - dir := rt.GetValue().(*Directory) + dir := rt.GetDirectory() nd := dag.NodeWithData(ft.FilePBData(nil, 0)) fi, err := NewFile("test", nd, dir, ds) diff --git a/mfs/ops.go b/mfs/ops.go index 6ade2bee0..20d2c5e74 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -1,7 +1,6 @@ package mfs import ( - "errors" "fmt" "os" gopath "path" @@ -129,7 +128,7 @@ func Mkdir(r *Root, pth string, opts MkdirOpts) error { return fmt.Errorf("cannot create directory '/': Already exists") } - cur := r.GetValue().(*Directory) + cur := r.GetDirectory() for i, d := range parts[:len(parts)-1] { fsn, err := cur.Child(d) if err == os.ErrNotExist && opts.Mkparents { @@ -172,12 +171,11 @@ func Mkdir(r *Root, pth string, opts MkdirOpts) error { return nil } +// Lookup extracts the root directory and performs a lookup under it. +// TODO: Now that the root is always a directory, can this function +// be collapsed with `DirLookup`? Or at least be made a method of `Root`? func Lookup(r *Root, path string) (FSNode, error) { - dir, ok := r.GetValue().(*Directory) - if !ok { - log.Errorf("root not a dir: %#v", r.GetValue()) - return nil, errors.New("root was not a directory") - } + dir := r.GetDirectory() return DirLookup(dir, path) } diff --git a/mfs/system.go b/mfs/system.go index 975d3da67..6f3e10bad 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -53,8 +53,8 @@ type Root struct { // node is the merkledag root. node *dag.ProtoNode - // val represents the node. It can either be a File or a Directory. - val FSNode + // Root directory of the MFS layout. + dir *Directory repub *Republisher @@ -90,33 +90,29 @@ func NewRoot(parent context.Context, ds ipld.DAGService, node *dag.ProtoNode, pf switch pbn.GetType() { case ft.TDirectory, ft.THAMTShard: - rval, err := NewDirectory(parent, node.String(), node, root, ds) + newDir, err := NewDirectory(parent, node.String(), node, root, ds) if err != nil { return nil, err } - root.val = rval + root.dir = newDir case ft.TFile, ft.TMetadata, ft.TRaw: - fi, err := NewFile(node.String(), node, root, ds) - if err != nil { - return nil, err - } - root.val = fi + return nil, fmt.Errorf("root can't be a file (unixfs type: %s)", pbn.GetType()) default: return nil, fmt.Errorf("unrecognized unixfs type: %s", pbn.GetType()) } return root, nil } -// GetValue returns the value of Root. -func (kr *Root) GetValue() FSNode { - return kr.val +// GetDirectory returns the root directory. +func (kr *Root) GetDirectory() *Directory { + return kr.dir } // Flush signals that an update has occurred since the last publish, // and updates the Root republisher. func (kr *Root) Flush() error { - nd, err := kr.GetValue().GetNode() + nd, err := kr.GetDirectory().GetNode() if err != nil { return err } @@ -136,10 +132,7 @@ func (kr *Root) Flush() error { // A better implemented mfs system (one that does smarter internal caching and // refcounting) shouldnt need this method. func (kr *Root) FlushMemFree(ctx context.Context) error { - dir, ok := kr.GetValue().(*Directory) - if !ok { - return fmt.Errorf("invalid mfs structure, root should be a directory") - } + dir := kr.GetDirectory() if err := dir.Flush(); err != nil { return err @@ -172,7 +165,7 @@ func (kr *Root) closeChild(name string, nd ipld.Node, sync bool) error { } func (kr *Root) Close() error { - nd, err := kr.GetValue().GetNode() + nd, err := kr.GetDirectory().GetNode() if err != nil { return err } From 741dc7d187fa57ef9b07947866773a22d9cf460d Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Fri, 29 Jun 2018 11:51:26 -0300 Subject: [PATCH 2397/3817] mfs: remove unused `Root` variables `node` and `Type` License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-mfs@f7f899d6dc8c28753ca0d054bd7666ded4356a42 --- mfs/system.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/mfs/system.go b/mfs/system.go index 6f3e10bad..d92d55a2b 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -50,8 +50,6 @@ type FSNode interface { // Root represents the root of a filesystem tree. type Root struct { - // node is the merkledag root. - node *dag.ProtoNode // Root directory of the MFS layout. dir *Directory @@ -59,8 +57,6 @@ type Root struct { repub *Republisher dserv ipld.DAGService - - Type string } // PubFunc is the function used by the `publish()` method. @@ -77,7 +73,6 @@ func NewRoot(parent context.Context, ds ipld.DAGService, node *dag.ProtoNode, pf } root := &Root{ - node: node, repub: repub, dserv: ds, } From 0ea8b158cfd3c8c1f71cc6deb395973fa97323b8 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Fri, 29 Jun 2018 12:04:48 -0300 Subject: [PATCH 2398/3817] mfs: remove `DAGService` from `Root` The `Root` structure now explicitly contains a `Directory` (instead of an `FSNode` interface), use that `Directory`'s `DAGService` instead of its own `dserv` variable (which was used only once in `closeChild()`). The `DAGService` in the `Root` and the `Directory` was the same (passed as an argument in the `NewRoot` initializer function). This leaves the `Root` structure with only a `Directory` and a `Republisher` and allows to better rethink its role and whether if those two structures should be grouped together (and if that group's name should be `Root`). License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-mfs@b7cf40115a605e4ddbf9867aa3e5af70e099ec28 --- mfs/system.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mfs/system.go b/mfs/system.go index d92d55a2b..5324b8bf8 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -55,8 +55,6 @@ type Root struct { dir *Directory repub *Republisher - - dserv ipld.DAGService } // PubFunc is the function used by the `publish()` method. @@ -74,7 +72,6 @@ func NewRoot(parent context.Context, ds ipld.DAGService, node *dag.ProtoNode, pf root := &Root{ repub: repub, - dserv: ds, } pbn, err := ft.FromBytes(node.Data()) @@ -148,7 +145,7 @@ func (kr *Root) FlushMemFree(ctx context.Context) error { // closeChild implements the childCloser interface, and signals to the publisher that // there are changes ready to be published. func (kr *Root) closeChild(name string, nd ipld.Node, sync bool) error { - err := kr.dserv.Add(context.TODO(), nd) + err := kr.GetDirectory().dserv.Add(context.TODO(), nd) if err != nil { return err } From 055dfeb9d8d401629f1089108947d9143227aecc Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 4 Jul 2018 23:51:10 -0300 Subject: [PATCH 2399/3817] dagreader: remove `Offset()` method Remove `Offset()` from the `DagReader` interface. It's not part of the Unix API and it wasn't used anywhere except for the tests (a helper function was added to replace it). License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-unixfs@513c0689ab0f79a8dfe2eb619f6e54b755bda941 --- unixfs/io/bufdagreader.go | 10 ---------- unixfs/io/dagreader.go | 1 - unixfs/io/dagreader_test.go | 24 ++++++++++++++++-------- unixfs/io/pbdagreader.go | 5 ----- unixfs/mod/dagmodifier_test.go | 10 +++++++++- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/unixfs/io/bufdagreader.go b/unixfs/io/bufdagreader.go index 6074099e8..48efe98ad 100644 --- a/unixfs/io/bufdagreader.go +++ b/unixfs/io/bufdagreader.go @@ -3,7 +3,6 @@ package io import ( "bytes" "context" - "io" ) // BufDagReader implements a DagReader that reads from a byte slice @@ -30,15 +29,6 @@ func (rd *BufDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) return rd.Read(b) } -// Offset returns the current offset. -func (rd *BufDagReader) Offset() int64 { - of, err := rd.Seek(0, io.SeekCurrent) - if err != nil { - panic("this should never happen " + err.Error()) - } - return of -} - // Size returns the size of the buffer. func (rd *BufDagReader) Size() uint64 { s := rd.Reader.Size() diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index e3f795732..37b9e4e6b 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -27,7 +27,6 @@ type DagReader interface { ReadSeekCloser Size() uint64 CtxReadFull(context.Context, []byte) (int, error) - Offset() int64 } // A ReadSeekCloser implements interfaces to read, copy, seek and close. diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index e3d3d042b..99973f7dc 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -57,7 +57,7 @@ func TestSeekAndRead(t *testing.T) { for i := 255; i >= 0; i-- { reader.Seek(int64(i), io.SeekStart) - if reader.Offset() != int64(i) { + if getOffset(reader) != int64(i) { t.Fatal("expected offset to be increased by one after read") } @@ -67,7 +67,7 @@ func TestSeekAndRead(t *testing.T) { t.Fatalf("read %d at index %d, expected %d", out, i, i) } - if reader.Offset() != int64(i+1) { + if getOffset(reader) != int64(i+1) { t.Fatal("expected offset to be increased by one after read") } } @@ -142,12 +142,12 @@ func TestRelativeSeek(t *testing.T) { } for i := 0; i < 256; i++ { - if reader.Offset() != int64(i*4) { - t.Fatalf("offset should be %d, was %d", i*4, reader.Offset()) + if getOffset(reader) != int64(i*4) { + t.Fatalf("offset should be %d, was %d", i*4, getOffset(reader)) } out := readByte(t, reader) if int(out) != i { - t.Fatalf("expected to read: %d at %d, read %d", i, reader.Offset()-1, out) + t.Fatalf("expected to read: %d at %d, read %d", i, getOffset(reader)-1, out) } if i != 255 { _, err := reader.Seek(3, io.SeekCurrent) @@ -163,12 +163,12 @@ func TestRelativeSeek(t *testing.T) { } for i := 0; i < 256; i++ { - if reader.Offset() != int64(1020-i*4) { - t.Fatalf("offset should be %d, was %d", 1020-i*4, reader.Offset()) + if getOffset(reader) != int64(1020-i*4) { + t.Fatalf("offset should be %d, was %d", 1020-i*4, getOffset(reader)) } out := readByte(t, reader) if int(out) != 255-i { - t.Fatalf("expected to read: %d at %d, read %d", 255-i, reader.Offset()-1, out) + t.Fatalf("expected to read: %d at %d, read %d", 255-i, getOffset(reader)-1, out) } reader.Seek(-5, io.SeekCurrent) // seek 4 bytes but we read one byte every time so 5 bytes } @@ -302,3 +302,11 @@ func readByte(t testing.TB, reader DagReader) byte { return out[0] } + +func getOffset(reader DagReader) int64 { + offset, err := reader.Seek(0, io.SeekCurrent) + if err != nil { + panic("failed to retrieve offset: " + err.Error()) + } + return offset +} diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index ce53d6711..f83233409 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -225,11 +225,6 @@ func (dr *PBDagReader) Close() error { return nil } -// Offset returns the current reader offset -func (dr *PBDagReader) Offset() int64 { - return dr.offset -} - // Seek implements io.Seeker, and will seek to a given offset in the file // interface matches standard unix seek // TODO: check if we can do relative seeks, to reduce the amount of dagreader diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 731322db1..92e4ce2d6 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -663,7 +663,7 @@ func testReadAndSeek(t *testing.T, opts testu.NodeOpts) { // skip 4 _, err = dagmod.Seek(1, io.SeekCurrent) if err != nil { - t.Fatalf("error: %s, offset %d, reader offset %d", err, dagmod.curWrOff, dagmod.read.Offset()) + t.Fatalf("error: %s, offset %d, reader offset %d", err, dagmod.curWrOff, getOffset(dagmod.read)) } //read 5,6,7 @@ -750,3 +750,11 @@ func BenchmarkDagmodWrite(b *testing.B) { } } } + +func getOffset(reader uio.DagReader) int64 { + offset, err := reader.Seek(0, io.SeekCurrent) + if err != nil { + panic("failed to retrieve offset: " + err.Error()) + } + return offset +} From d53cfdfdba8143dc13a4f20833e05ed78d69d150 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 5 Jul 2018 17:06:50 -0700 Subject: [PATCH 2400/3817] explain when a promise can be canceled in pbdagreader License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@5d60ef5eedb22a0087d25e27747e1b11bb58e575 --- unixfs/io/pbdagreader.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 9c3909577..42d903aac 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -110,6 +110,11 @@ func (dr *PBDagReader) precalcNextBuf(ctx context.Context) error { // In this case, the context used to *preload* the node has been canceled. // We need to retry the load with our context and we might as // well preload some extra nodes while we're at it. + // + // Note: When using `Read`, this code will never execute as + // `Read` will use the global context. It only runs if the user + // explicitly reads with a custom context (e.g., by calling + // `CtxReadFull`). dr.preload(ctx, dr.linkPosition) nxt, err = dr.promises[dr.linkPosition].Get(ctx) dr.promises[dr.linkPosition] = nil From f7b80bbbadeb97d2a9f9a4a6d93fbb3d6c607797 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 4 Jul 2018 23:12:12 -0300 Subject: [PATCH 2401/3817] pbdagreader: use FSNode instead of protobuf structure Focus on the UnixFS layer and avoid explicit references to protocol buffers format (used to serialize objects of that layer). Use the `unixfs.FSNode` structure which it abstracts from the `unixfs.pb.Data` format. Replace `PBDagReader` field `ftpb.Data` with `ft.FSNode`, renaming it to `file` (which is the type of UnixFS object represented in the reader) and changing its comment removing the "cached" reference, as this structure is not used here as a cache (`PBDagReader` doesn't modify the DAG, it's read-only). Also, removed unused `ProtoNode` field to avoid confusions, as it would normally be present if the `FSNode` was in fact used as a cache of the contents of the `ProtoNode`. An example of the advantage of shifting the focus from the format to the UnixFS layer is dropping the of use `len(pb.Blocksizes)` in favor of the more clear `NumChildren()` abstraction. Added `BlockSize()` accessor. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-unixfs@c847ff39b473987b91d218dc92dfdf687e40ac70 --- unixfs/archive/tar/writer.go | 17 +++++++------- unixfs/io/dagreader.go | 9 ++++---- unixfs/io/pbdagreader.go | 45 +++++++++++++++--------------------- unixfs/unixfs.go | 6 +++++ 4 files changed, 36 insertions(+), 41 deletions(-) diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 61ceb2e98..04eda65d4 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -16,7 +16,6 @@ import ( upb "github.com/ipfs/go-ipfs/unixfs/pb" ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) // Writer is a utility structure that helps to write @@ -57,12 +56,12 @@ func (w *Writer) writeDir(nd *mdag.ProtoNode, fpath string) error { }) } -func (w *Writer) writeFile(nd *mdag.ProtoNode, pb *upb.Data, fpath string) error { - if err := writeFileHeader(w.TarW, fpath, pb.GetFilesize()); err != nil { +func (w *Writer) writeFile(nd *mdag.ProtoNode, fsNode *ft.FSNode, fpath string) error { + if err := writeFileHeader(w.TarW, fpath, fsNode.FileSize()); err != nil { return err } - dagr := uio.NewPBFileReader(w.ctx, nd, pb, w.Dag) + dagr := uio.NewPBFileReader(w.ctx, nd, fsNode, w.Dag) if _, err := dagr.WriteTo(w.TarW); err != nil { return err } @@ -74,12 +73,12 @@ func (w *Writer) writeFile(nd *mdag.ProtoNode, pb *upb.Data, fpath string) error func (w *Writer) WriteNode(nd ipld.Node, fpath string) error { switch nd := nd.(type) { case *mdag.ProtoNode: - pb := new(upb.Data) - if err := proto.Unmarshal(nd.Data(), pb); err != nil { + fsNode, err := ft.FSNodeFromBytes(nd.Data()) + if err != nil { return err } - switch pb.GetType() { + switch fsNode.GetType() { case upb.Data_Metadata: fallthrough case upb.Data_Directory, upb.Data_HAMTShard: @@ -87,9 +86,9 @@ func (w *Writer) WriteNode(nd ipld.Node, fpath string) error { case upb.Data_Raw: fallthrough case upb.Data_File: - return w.writeFile(nd, pb, fpath) + return w.writeFile(nd, fsNode, fpath) case upb.Data_Symlink: - return writeSymlinkHeader(w.TarW, string(pb.GetData()), fpath) + return writeSymlinkHeader(w.TarW, string(fsNode.GetData()), fpath) default: return ft.ErrUnrecognizedType } diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index e3f795732..1b4c48571 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -11,7 +11,6 @@ import ( ftpb "github.com/ipfs/go-ipfs/unixfs/pb" ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ) // Common errors @@ -45,17 +44,17 @@ func NewDagReader(ctx context.Context, n ipld.Node, serv ipld.NodeGetter) (DagRe case *mdag.RawNode: return NewBufDagReader(n.RawData()), nil case *mdag.ProtoNode: - pb := new(ftpb.Data) - if err := proto.Unmarshal(n.Data(), pb); err != nil { + fsNode, err := ft.FSNodeFromBytes(n.Data()) + if err != nil { return nil, err } - switch pb.GetType() { + switch fsNode.GetType() { case ftpb.Data_Directory, ftpb.Data_HAMTShard: // Dont allow reading directories return nil, ErrIsDir case ftpb.Data_File, ftpb.Data_Raw: - return NewPBFileReader(ctx, n, pb, serv), nil + return NewPBFileReader(ctx, n, fsNode, serv), nil case ftpb.Data_Metadata: if len(n.Links()) == 0 { return nil, errors.New("incorrectly formatted metadata object") diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index ce53d6711..84cef04dc 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -11,7 +11,6 @@ import ( ftpb "github.com/ipfs/go-ipfs/unixfs/pb" ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) @@ -19,11 +18,8 @@ import ( type PBDagReader struct { serv ipld.NodeGetter - // the node being read - node *mdag.ProtoNode - - // cached protobuf structure from node.Data - pbdata *ftpb.Data + // UnixFS file (it should be of type `Data_File` or `Data_Raw` only). + file *ft.FSNode // the current data buffer to be read from // will either be a bytes.Reader or a child DagReader @@ -51,18 +47,17 @@ type PBDagReader struct { var _ DagReader = (*PBDagReader)(nil) // NewPBFileReader constructs a new PBFileReader. -func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, pb *ftpb.Data, serv ipld.NodeGetter) *PBDagReader { +func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, file *ft.FSNode, serv ipld.NodeGetter) *PBDagReader { fctx, cancel := context.WithCancel(ctx) curLinks := getLinkCids(n) return &PBDagReader{ - node: n, serv: serv, - buf: NewBufDagReader(pb.GetData()), + buf: NewBufDagReader(file.GetData()), promises: make([]*ipld.NodePromise, len(curLinks)), links: curLinks, ctx: fctx, cancel: cancel, - pbdata: pb, + file: file, } } @@ -105,21 +100,20 @@ func (dr *PBDagReader) precalcNextBuf(ctx context.Context) error { switch nxt := nxt.(type) { case *mdag.ProtoNode: - pb := new(ftpb.Data) - err = proto.Unmarshal(nxt.Data(), pb) + fsNode, err := ft.FSNodeFromBytes(nxt.Data()) if err != nil { return fmt.Errorf("incorrectly formatted protobuf: %s", err) } - switch pb.GetType() { + switch fsNode.GetType() { case ftpb.Data_Directory, ftpb.Data_HAMTShard: // A directory should not exist within a file return ft.ErrInvalidDirLocation case ftpb.Data_File: - dr.buf = NewPBFileReader(dr.ctx, nxt, pb, dr.serv) + dr.buf = NewPBFileReader(dr.ctx, nxt, fsNode, dr.serv) return nil case ftpb.Data_Raw: - dr.buf = NewBufDagReader(pb.GetData()) + dr.buf = NewBufDagReader(fsNode.GetData()) return nil case ftpb.Data_Metadata: return errors.New("shouldnt have had metadata object inside file") @@ -146,7 +140,7 @@ func getLinkCids(n ipld.Node) []*cid.Cid { // Size return the total length of the data from the DAG structured file. func (dr *PBDagReader) Size() uint64 { - return dr.pbdata.GetFilesize() + return dr.file.FileSize() } // Read reads data from the DAG structured file @@ -244,17 +238,14 @@ func (dr *PBDagReader) Seek(offset int64, whence int) (int64, error) { return offset, nil } - // Grab cached protobuf object (solely to make code look cleaner) - pb := dr.pbdata - // left represents the number of bytes remaining to seek to (from beginning) left := offset - if int64(len(pb.Data)) >= offset { + if int64(len(dr.file.GetData())) >= offset { // Close current buf to close potential child dagreader if dr.buf != nil { dr.buf.Close() } - dr.buf = NewBufDagReader(pb.GetData()[offset:]) + dr.buf = NewBufDagReader(dr.file.GetData()[offset:]) // start reading links from the beginning dr.linkPosition = 0 @@ -263,15 +254,15 @@ func (dr *PBDagReader) Seek(offset int64, whence int) (int64, error) { } // skip past root block data - left -= int64(len(pb.Data)) + left -= int64(len(dr.file.GetData())) // iterate through links and find where we need to be - for i := 0; i < len(pb.Blocksizes); i++ { - if pb.Blocksizes[i] > uint64(left) { + for i := 0; i < dr.file.NumChildren(); i++ { + if dr.file.BlockSize(i) > uint64(left) { dr.linkPosition = i break } else { - left -= int64(pb.Blocksizes[i]) + left -= int64(dr.file.BlockSize(i)) } } @@ -303,14 +294,14 @@ func (dr *PBDagReader) Seek(offset int64, whence int) (int64, error) { noffset := dr.offset + offset return dr.Seek(noffset, io.SeekStart) case io.SeekEnd: - noffset := int64(dr.pbdata.GetFilesize()) - offset + noffset := int64(dr.file.FileSize()) - offset n, err := dr.Seek(noffset, io.SeekStart) // Return negative number if we can't figure out the file size. Using io.EOF // for this seems to be good(-enough) solution as it's only returned by // precalcNextBuf when we step out of file range. // This is needed for gateway to function properly - if err == io.EOF && *dr.pbdata.Type == ftpb.Data_File { + if err == io.EOF && dr.file.GetType() == ftpb.Data_File { return -1, nil } return n, err diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index 3ba01fb0f..f08da9415 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -195,6 +195,12 @@ func (n *FSNode) RemoveBlockSize(i int) { n.format.Blocksizes = append(n.format.Blocksizes[:i], n.format.Blocksizes[i+1:]...) } +// BlockSize returns the block size indexed by `i`. +// TODO: Evaluate if this function should be bounds checking. +func (n *FSNode) BlockSize(i int) uint64 { + return n.format.Blocksizes[i] +} + // GetBytes marshals this node as a protobuf message. func (n *FSNode) GetBytes() ([]byte, error) { return proto.Marshal(&n.format) From 4fe08e799b80e48006e073709789179a527349ef Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Fri, 6 Jul 2018 13:22:19 -0300 Subject: [PATCH 2402/3817] unixfs: remove `Get` prefix from `FSNode` accessors See https://golang.org/doc/effective_go.html#Getters. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-unixfs@cf2e4238c9df639b533fc654aa1f863bf7372c37 --- unixfs/archive/tar/writer.go | 4 ++-- unixfs/io/dagreader.go | 2 +- unixfs/io/pbdagreader.go | 14 +++++++------- unixfs/unixfs.go | 10 +++++----- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 04eda65d4..0c2df9fb1 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -78,7 +78,7 @@ func (w *Writer) WriteNode(nd ipld.Node, fpath string) error { return err } - switch fsNode.GetType() { + switch fsNode.Type() { case upb.Data_Metadata: fallthrough case upb.Data_Directory, upb.Data_HAMTShard: @@ -88,7 +88,7 @@ func (w *Writer) WriteNode(nd ipld.Node, fpath string) error { case upb.Data_File: return w.writeFile(nd, fsNode, fpath) case upb.Data_Symlink: - return writeSymlinkHeader(w.TarW, string(fsNode.GetData()), fpath) + return writeSymlinkHeader(w.TarW, string(fsNode.Data()), fpath) default: return ft.ErrUnrecognizedType } diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 1b4c48571..504c0b6f0 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -49,7 +49,7 @@ func NewDagReader(ctx context.Context, n ipld.Node, serv ipld.NodeGetter) (DagRe return nil, err } - switch fsNode.GetType() { + switch fsNode.Type() { case ftpb.Data_Directory, ftpb.Data_HAMTShard: // Dont allow reading directories return nil, ErrIsDir diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 84cef04dc..c0aa90922 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -52,7 +52,7 @@ func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, file *ft.FSNode, se curLinks := getLinkCids(n) return &PBDagReader{ serv: serv, - buf: NewBufDagReader(file.GetData()), + buf: NewBufDagReader(file.Data()), promises: make([]*ipld.NodePromise, len(curLinks)), links: curLinks, ctx: fctx, @@ -105,7 +105,7 @@ func (dr *PBDagReader) precalcNextBuf(ctx context.Context) error { return fmt.Errorf("incorrectly formatted protobuf: %s", err) } - switch fsNode.GetType() { + switch fsNode.Type() { case ftpb.Data_Directory, ftpb.Data_HAMTShard: // A directory should not exist within a file return ft.ErrInvalidDirLocation @@ -113,7 +113,7 @@ func (dr *PBDagReader) precalcNextBuf(ctx context.Context) error { dr.buf = NewPBFileReader(dr.ctx, nxt, fsNode, dr.serv) return nil case ftpb.Data_Raw: - dr.buf = NewBufDagReader(fsNode.GetData()) + dr.buf = NewBufDagReader(fsNode.Data()) return nil case ftpb.Data_Metadata: return errors.New("shouldnt have had metadata object inside file") @@ -240,12 +240,12 @@ func (dr *PBDagReader) Seek(offset int64, whence int) (int64, error) { // left represents the number of bytes remaining to seek to (from beginning) left := offset - if int64(len(dr.file.GetData())) >= offset { + if int64(len(dr.file.Data())) >= offset { // Close current buf to close potential child dagreader if dr.buf != nil { dr.buf.Close() } - dr.buf = NewBufDagReader(dr.file.GetData()[offset:]) + dr.buf = NewBufDagReader(dr.file.Data()[offset:]) // start reading links from the beginning dr.linkPosition = 0 @@ -254,7 +254,7 @@ func (dr *PBDagReader) Seek(offset int64, whence int) (int64, error) { } // skip past root block data - left -= int64(len(dr.file.GetData())) + left -= int64(len(dr.file.Data())) // iterate through links and find where we need to be for i := 0; i < dr.file.NumChildren(); i++ { @@ -301,7 +301,7 @@ func (dr *PBDagReader) Seek(offset int64, whence int) (int64, error) { // for this seems to be good(-enough) solution as it's only returned by // precalcNextBuf when we step out of file range. // This is needed for gateway to function properly - if err == io.EOF && dr.file.GetType() == ftpb.Data_File { + if err == io.EOF && dr.file.Type() == ftpb.Data_File { return -1, nil } return n, err diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index f08da9415..9cd9731ed 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -217,15 +217,15 @@ func (n *FSNode) NumChildren() int { return len(n.format.Blocksizes) } -// GetData retrieves the `Data` field from the internal `format`. -func (n *FSNode) GetData() []byte { +// Data retrieves the `Data` field from the internal `format`. +func (n *FSNode) Data() []byte { return n.format.GetData() } // SetData sets the `Data` field from the internal `format` // updating its `Filesize`. func (n *FSNode) SetData(newData []byte) { - n.UpdateFilesize(int64(len(newData) - len(n.GetData()))) + n.UpdateFilesize(int64(len(newData) - len(n.Data()))) n.format.Data = newData } @@ -237,8 +237,8 @@ func (n *FSNode) UpdateFilesize(filesizeDiff int64) { int64(n.format.GetFilesize()) + filesizeDiff)) } -// GetType retrieves the `Type` field from the internal `format`. -func (n *FSNode) GetType() pb.Data_DataType { +// Type retrieves the `Type` field from the internal `format`. +func (n *FSNode) Type() pb.Data_DataType { return n.format.GetType() } From 0b0c11a02c2f6776fc21d99aaaa926fa111af22d Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Fri, 6 Jul 2018 13:22:19 -0300 Subject: [PATCH 2403/3817] unixfs: remove `Get` prefix from `FSNode` accessors See https://golang.org/doc/effective_go.html#Getters. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-mfs@fe3a1ee72e161b07b87a46db8b34737e1e8a72c6 --- mfs/file.go | 2 +- mfs/mfs_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/file.go b/mfs/file.go index f28cf79f8..40106d1da 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -60,7 +60,7 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { return nil, err } - switch fsn.GetType() { + switch fsn.Type() { default: return nil, fmt.Errorf("unsupported fsnode type for 'file'") case ft.TSymlink: diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 7088c8c9c..1d9aee46f 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -852,7 +852,7 @@ func TestFlushing(t *testing.T) { t.Fatal(err) } - if fsnode.GetType() != ft.TDirectory { + if fsnode.Type() != ft.TDirectory { t.Fatal("root wasnt a directory") } From 52baed290a6983dcbd8b5e0ec35ec952943961b1 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 27 Jun 2018 10:29:03 -0300 Subject: [PATCH 2404/3817] unixfs: add a directory interface Add a UnixFS `Directory` that hides implementation details and helps to distinguish *what* is a UnixFS directory. Replace the `unixfs.io.Directory` structure that contained the HAMT and basic directory implementations (through inner pointers) with an interface containing the same methods. Implement those methods in two clearly distinct structures for each implementation (`BasicDirectory` and `HAMTDirectory`) avoiding pointer logic and clearly differentiating which implementation does what. The potential basic to HAMT transition was being hidden behind the `AddChild` call at the UnixFS layer (changing one implementation pointer with the other one), it is now being explicitly done at the MFS layer. Rename the `dirbuilder.go` file to `directory.go` and change the `Directory` MFS attribute `dirbuilder` to `unixfsDir` to be consistent. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-unixfs@842c17b93f8829ad5a38def3daf38cf81b39d05c --- unixfs/io/dirbuilder.go | 214 -------------- unixfs/io/directory.go | 262 ++++++++++++++++++ .../{dirbuilder_test.go => directory_test.go} | 0 3 files changed, 262 insertions(+), 214 deletions(-) delete mode 100644 unixfs/io/dirbuilder.go create mode 100644 unixfs/io/directory.go rename unixfs/io/{dirbuilder_test.go => directory_test.go} (100%) diff --git a/unixfs/io/dirbuilder.go b/unixfs/io/dirbuilder.go deleted file mode 100644 index 3a36fe535..000000000 --- a/unixfs/io/dirbuilder.go +++ /dev/null @@ -1,214 +0,0 @@ -package io - -import ( - "context" - "fmt" - "os" - - mdag "github.com/ipfs/go-ipfs/merkledag" - format "github.com/ipfs/go-ipfs/unixfs" - hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" -) - -// ShardSplitThreshold specifies how large of an unsharded directory -// the Directory code will generate. Adding entries over this value will -// result in the node being restructured into a sharded object. -var ShardSplitThreshold = 1000 - -// UseHAMTSharding is a global flag that signifies whether or not to use the -// HAMT sharding scheme for directory creation -var UseHAMTSharding = false - -// DefaultShardWidth is the default value used for hamt sharding width. -var DefaultShardWidth = 256 - -// Directory allows to work with UnixFS directory nodes, adding and removing -// children. It allows to work with different directory schemes, -// like the classic or the HAMT one. -type Directory struct { - dserv ipld.DAGService - dirnode *mdag.ProtoNode - - shard *hamt.Shard -} - -// NewDirectory returns a Directory. It needs a DAGService to add the Children -func NewDirectory(dserv ipld.DAGService) *Directory { - db := new(Directory) - db.dserv = dserv - if UseHAMTSharding { - s, err := hamt.NewShard(dserv, DefaultShardWidth) - if err != nil { - panic(err) // will only panic if DefaultShardWidth is a bad value - } - db.shard = s - } else { - db.dirnode = format.EmptyDirNode() - } - return db -} - -// ErrNotADir implies that the given node was not a unixfs directory -var ErrNotADir = fmt.Errorf("merkledag node was not a directory or shard") - -// NewDirectoryFromNode loads a unixfs directory from the given IPLD node and -// DAGService. -func NewDirectoryFromNode(dserv ipld.DAGService, nd ipld.Node) (*Directory, error) { - pbnd, ok := nd.(*mdag.ProtoNode) - if !ok { - return nil, ErrNotADir - } - - pbd, err := format.FromBytes(pbnd.Data()) - if err != nil { - return nil, err - } - - switch pbd.GetType() { - case format.TDirectory: - return &Directory{ - dserv: dserv, - dirnode: pbnd.Copy().(*mdag.ProtoNode), - }, nil - case format.THAMTShard: - shard, err := hamt.NewHamtFromDag(dserv, nd) - if err != nil { - return nil, err - } - - return &Directory{ - dserv: dserv, - shard: shard, - }, nil - default: - return nil, ErrNotADir - } -} - -// SetPrefix sets the prefix of the root node -func (d *Directory) SetPrefix(prefix *cid.Prefix) { - if d.dirnode != nil { - d.dirnode.SetPrefix(prefix) - } - if d.shard != nil { - d.shard.SetPrefix(prefix) - } -} - -// AddChild adds a (name, key)-pair to the root node. -func (d *Directory) AddChild(ctx context.Context, name string, nd ipld.Node) error { - if d.shard == nil { - if !UseHAMTSharding { - _ = d.dirnode.RemoveNodeLink(name) - return d.dirnode.AddNodeLink(name, nd) - } - - err := d.switchToSharding(ctx) - if err != nil { - return err - } - } - - return d.shard.Set(ctx, name, nd) -} - -func (d *Directory) switchToSharding(ctx context.Context) error { - s, err := hamt.NewShard(d.dserv, DefaultShardWidth) - if err != nil { - return err - } - s.SetPrefix(&d.dirnode.Prefix) - - d.shard = s - for _, lnk := range d.dirnode.Links() { - cnd, err := d.dserv.Get(ctx, lnk.Cid) - if err != nil { - return err - } - - err = d.shard.Set(ctx, lnk.Name, cnd) - if err != nil { - return err - } - } - - d.dirnode = nil - return nil -} - -// ForEachLink applies the given function to Links in the directory. -func (d *Directory) ForEachLink(ctx context.Context, f func(*ipld.Link) error) error { - if d.shard == nil { - for _, l := range d.dirnode.Links() { - if err := f(l); err != nil { - return err - } - } - return nil - } - - return d.shard.ForEachLink(ctx, f) -} - -// Links returns the all the links in the directory node. -func (d *Directory) Links(ctx context.Context) ([]*ipld.Link, error) { - if d.shard == nil { - return d.dirnode.Links(), nil - } - - return d.shard.EnumLinks(ctx) -} - -// Find returns the root node of the file named 'name' within this directory. -// In the case of HAMT-directories, it will traverse the tree. -func (d *Directory) Find(ctx context.Context, name string) (ipld.Node, error) { - if d.shard == nil { - lnk, err := d.dirnode.GetNodeLink(name) - switch err { - case mdag.ErrLinkNotFound: - return nil, os.ErrNotExist - default: - return nil, err - case nil: - } - - return d.dserv.Get(ctx, lnk.Cid) - } - - lnk, err := d.shard.Find(ctx, name) - if err != nil { - return nil, err - } - - return lnk.GetNode(ctx, d.dserv) -} - -// RemoveChild removes the child with the given name. -func (d *Directory) RemoveChild(ctx context.Context, name string) error { - if d.shard == nil { - return d.dirnode.RemoveNodeLink(name) - } - - return d.shard.Remove(ctx, name) -} - -// GetNode returns the root of this Directory -func (d *Directory) GetNode() (ipld.Node, error) { - if d.shard == nil { - return d.dirnode, nil - } - - return d.shard.Node() -} - -// GetPrefix returns the CID Prefix used -func (d *Directory) GetPrefix() *cid.Prefix { - if d.shard == nil { - return &d.dirnode.Prefix - } - - return d.shard.Prefix() -} diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go new file mode 100644 index 000000000..31b2846ac --- /dev/null +++ b/unixfs/io/directory.go @@ -0,0 +1,262 @@ +package io + +import ( + "context" + "fmt" + "os" + + mdag "github.com/ipfs/go-ipfs/merkledag" + format "github.com/ipfs/go-ipfs/unixfs" + hamt "github.com/ipfs/go-ipfs/unixfs/hamt" + + ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" +) + +// ShardSplitThreshold specifies how large of an unsharded directory +// the Directory code will generate. Adding entries over this value will +// result in the node being restructured into a sharded object. +var ShardSplitThreshold = 1000 + +// UseHAMTSharding is a global flag that signifies whether or not to use the +// HAMT sharding scheme for directory creation +var UseHAMTSharding = false + +// DefaultShardWidth is the default value used for hamt sharding width. +var DefaultShardWidth = 256 + +// Directory defines a UnixFS directory. It is used for creating, reading and +// editing directories. It allows to work with different directory schemes, +// like the basic or the HAMT implementation. +// +// It just allows to perform explicit edits on a single directory, working with +// directory trees is out of its scope, they are managed by the MFS layer +// (which is the main consumer of this interface). +type Directory interface { + + // SetPrefix sets the CID prefix of the root node. + SetPrefix(*cid.Prefix) + + // AddChild adds a (name, key) pair to the root node. + AddChild(context.Context, string, ipld.Node) error + + // ForEachLink applies the given function to Links in the directory. + ForEachLink(context.Context, func(*ipld.Link) error) error + + // Links returns the all the links in the directory node. + Links(context.Context) ([]*ipld.Link, error) + + // Find returns the root node of the file named 'name' within this directory. + // In the case of HAMT-directories, it will traverse the tree. + Find(context.Context, string) (ipld.Node, error) + + // RemoveChild removes the child with the given name. + RemoveChild(context.Context, string) error + + // GetNode returns the root of this directory. + GetNode() (ipld.Node, error) + + // GetPrefix returns the CID Prefix used. + GetPrefix() *cid.Prefix +} + +// TODO: Evaluate removing `dserv` from this layer and providing it in MFS. +// (The functions should in that case add a `DAGService` argument.) + +// BasicDirectory is the basic implementation of `Directory`. All the entries +// are stored in a single node. +type BasicDirectory struct { + node *mdag.ProtoNode + dserv ipld.DAGService +} + +// HAMTDirectory is the HAMT implementation of `Directory`. +// (See package `hamt` for more information.) +type HAMTDirectory struct { + shard *hamt.Shard + dserv ipld.DAGService +} + +// NewDirectory returns a Directory. It needs a `DAGService` to add the children. +func NewDirectory(dserv ipld.DAGService) Directory { + if UseHAMTSharding { + dir := new(HAMTDirectory) + s, err := hamt.NewShard(dserv, DefaultShardWidth) + if err != nil { + panic(err) // will only panic if DefaultShardWidth is a bad value + } + dir.shard = s + dir.dserv = dserv + return dir + } + + dir := new(BasicDirectory) + dir.node = format.EmptyDirNode() + dir.dserv = dserv + return dir +} + +// ErrNotADir implies that the given node was not a unixfs directory +var ErrNotADir = fmt.Errorf("merkledag node was not a directory or shard") + +// NewDirectoryFromNode loads a unixfs directory from the given IPLD node and +// DAGService. +func NewDirectoryFromNode(dserv ipld.DAGService, node ipld.Node) (Directory, error) { + protoBufNode, ok := node.(*mdag.ProtoNode) + if !ok { + return nil, ErrNotADir + } + + fsNode, err := format.FSNodeFromBytes(protoBufNode.Data()) + if err != nil { + return nil, err + } + + switch fsNode.GetType() { + case format.TDirectory: + return &BasicDirectory{ + dserv: dserv, + node: protoBufNode.Copy().(*mdag.ProtoNode), + }, nil + case format.THAMTShard: + shard, err := hamt.NewHamtFromDag(dserv, node) + if err != nil { + return nil, err + } + return &HAMTDirectory{ + dserv: dserv, + shard: shard, + }, nil + } + + return nil, ErrNotADir +} + +// SetPrefix implements the `Directory` interface. +func (d *BasicDirectory) SetPrefix(prefix *cid.Prefix) { + d.node.SetPrefix(prefix) +} + +// AddChild implements the `Directory` interface. It adds (or replaces) +// a link to the given `node` under `name`. +func (d *BasicDirectory) AddChild(ctx context.Context, name string, node ipld.Node) error { + d.node.RemoveNodeLink(name) + // Remove old link (if it existed), don't check a potential `ErrNotFound`. + + return d.node.AddNodeLink(name, node) +} + +// ForEachLink implements the `Directory` interface. +func (d *BasicDirectory) ForEachLink(ctx context.Context, f func(*ipld.Link) error) error { + for _, l := range d.node.Links() { + if err := f(l); err != nil { + return err + } + } + return nil +} + +// Links implements the `Directory` interface. +func (d *BasicDirectory) Links(ctx context.Context) ([]*ipld.Link, error) { + return d.node.Links(), nil +} + +// Find implements the `Directory` interface. +func (d *BasicDirectory) Find(ctx context.Context, name string) (ipld.Node, error) { + lnk, err := d.node.GetNodeLink(name) + if err == mdag.ErrLinkNotFound { + err = os.ErrNotExist + } + if err != nil { + return nil, err + } + + return d.dserv.Get(ctx, lnk.Cid) +} + +// RemoveChild implements the `Directory` interface. +func (d *BasicDirectory) RemoveChild(ctx context.Context, name string) error { + return d.node.RemoveNodeLink(name) +} + +// GetNode implements the `Directory` interface. +func (d *BasicDirectory) GetNode() (ipld.Node, error) { + return d.node, nil +} + +// GetPrefix implements the `Directory` interface. +func (d *BasicDirectory) GetPrefix() *cid.Prefix { + return &d.node.Prefix +} + +// SwitchToSharding returns a HAMT implementation of this directory. +func (d *BasicDirectory) SwitchToSharding(ctx context.Context) (Directory, error) { + hamtDir := new(HAMTDirectory) + hamtDir.dserv = d.dserv + + shard, err := hamt.NewShard(d.dserv, DefaultShardWidth) + if err != nil { + return nil, err + } + shard.SetPrefix(&d.node.Prefix) + hamtDir.shard = shard + + for _, lnk := range d.node.Links() { + node, err := d.dserv.Get(ctx, lnk.Cid) + if err != nil { + return nil, err + } + + err = hamtDir.shard.Set(ctx, lnk.Name, node) + if err != nil { + return nil, err + } + } + + return hamtDir, nil +} + +// SetPrefix implements the `Directory` interface. +func (d *HAMTDirectory) SetPrefix(prefix *cid.Prefix) { + d.shard.SetPrefix(prefix) +} + +// AddChild implements the `Directory` interface. +func (d *HAMTDirectory) AddChild(ctx context.Context, name string, nd ipld.Node) error { + return d.shard.Set(ctx, name, nd) +} + +// ForEachLink implements the `Directory` interface. +func (d *HAMTDirectory) ForEachLink(ctx context.Context, f func(*ipld.Link) error) error { + return d.shard.ForEachLink(ctx, f) +} + +// Links implements the `Directory` interface. +func (d *HAMTDirectory) Links(ctx context.Context) ([]*ipld.Link, error) { + return d.shard.EnumLinks(ctx) +} + +// Find implements the `Directory` interface. It will traverse the tree. +func (d *HAMTDirectory) Find(ctx context.Context, name string) (ipld.Node, error) { + lnk, err := d.shard.Find(ctx, name) + if err != nil { + return nil, err + } + + return lnk.GetNode(ctx, d.dserv) +} + +// RemoveChild implements the `Directory` interface. +func (d *HAMTDirectory) RemoveChild(ctx context.Context, name string) error { + return d.shard.Remove(ctx, name) +} + +// GetNode implements the `Directory` interface. +func (d *HAMTDirectory) GetNode() (ipld.Node, error) { + return d.shard.Node() +} + +// GetPrefix implements the `Directory` interface. +func (d *HAMTDirectory) GetPrefix() *cid.Prefix { + return d.shard.Prefix() +} diff --git a/unixfs/io/dirbuilder_test.go b/unixfs/io/directory_test.go similarity index 100% rename from unixfs/io/dirbuilder_test.go rename to unixfs/io/directory_test.go From 1446df77d1dd56f12280103ef1966ca284387a4b Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 27 Jun 2018 10:29:03 -0300 Subject: [PATCH 2405/3817] unixfs: add a directory interface Add a UnixFS `Directory` that hides implementation details and helps to distinguish *what* is a UnixFS directory. Replace the `unixfs.io.Directory` structure that contained the HAMT and basic directory implementations (through inner pointers) with an interface containing the same methods. Implement those methods in two clearly distinct structures for each implementation (`BasicDirectory` and `HAMTDirectory`) avoiding pointer logic and clearly differentiating which implementation does what. The potential basic to HAMT transition was being hidden behind the `AddChild` call at the UnixFS layer (changing one implementation pointer with the other one), it is now being explicitly done at the MFS layer. Rename the `dirbuilder.go` file to `directory.go` and change the `Directory` MFS attribute `dirbuilder` to `unixfsDir` to be consistent. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-mfs@c82aac7acc1a326d6917a9f3c5d249d7c55d1e58 --- mfs/dir.go | 65 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 17f09356f..643b024d7 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -33,7 +33,9 @@ type Directory struct { lock sync.Mutex ctx context.Context - dirbuilder *uio.Directory + // UnixFS directory implementation used for creating, + // reading and editing directories. + unixfsDir uio.Directory modTime time.Time @@ -51,25 +53,25 @@ func NewDirectory(ctx context.Context, name string, node ipld.Node, parent child } return &Directory{ - dserv: dserv, - ctx: ctx, - name: name, - dirbuilder: db, - parent: parent, - childDirs: make(map[string]*Directory), - files: make(map[string]*File), - modTime: time.Now(), + dserv: dserv, + ctx: ctx, + name: name, + unixfsDir: db, + parent: parent, + childDirs: make(map[string]*Directory), + files: make(map[string]*File), + modTime: time.Now(), }, nil } // GetPrefix gets the CID prefix of the root node func (d *Directory) GetPrefix() *cid.Prefix { - return d.dirbuilder.GetPrefix() + return d.unixfsDir.GetPrefix() } // SetPrefix sets the CID prefix func (d *Directory) SetPrefix(prefix *cid.Prefix) { - d.dirbuilder.SetPrefix(prefix) + d.unixfsDir.SetPrefix(prefix) } // closeChild updates the child by the given name to the dag node 'nd' @@ -103,7 +105,7 @@ func (d *Directory) closeChildUpdate(name string, nd ipld.Node, sync bool) (*dag } func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) { - nd, err := d.dirbuilder.GetNode() + nd, err := d.unixfsDir.GetNode() if err != nil { return nil, err } @@ -122,7 +124,7 @@ func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) { } func (d *Directory) updateChild(name string, nd ipld.Node) error { - err := d.dirbuilder.AddChild(d.ctx, name, nd) + err := d.AddUnixFSChild(name, nd) if err != nil { return err } @@ -206,7 +208,7 @@ func (d *Directory) Uncache(name string) { // childFromDag searches through this directories dag node for a child link // with the given name func (d *Directory) childFromDag(name string) (ipld.Node, error) { - return d.dirbuilder.Find(d.ctx, name) + return d.unixfsDir.Find(d.ctx, name) } // childUnsync returns the child under this directory by the given name @@ -237,7 +239,7 @@ func (d *Directory) ListNames(ctx context.Context) ([]string, error) { defer d.lock.Unlock() var out []string - err := d.dirbuilder.ForEachLink(ctx, func(l *ipld.Link) error { + err := d.unixfsDir.ForEachLink(ctx, func(l *ipld.Link) error { out = append(out, l.Name) return nil }) @@ -262,7 +264,7 @@ func (d *Directory) List(ctx context.Context) ([]NodeListing, error) { func (d *Directory) ForEachEntry(ctx context.Context, f func(NodeListing) error) error { d.lock.Lock() defer d.lock.Unlock() - return d.dirbuilder.ForEachLink(ctx, func(l *ipld.Link) error { + return d.unixfsDir.ForEachLink(ctx, func(l *ipld.Link) error { c, err := d.childUnsync(l.Name) if err != nil { return err @@ -315,7 +317,7 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { return nil, err } - err = d.dirbuilder.AddChild(d.ctx, name, ndir) + err = d.AddUnixFSChild(name, ndir) if err != nil { return nil, err } @@ -336,7 +338,7 @@ func (d *Directory) Unlink(name string) error { delete(d.childDirs, name) delete(d.files, name) - return d.dirbuilder.RemoveChild(d.ctx, name) + return d.unixfsDir.RemoveChild(d.ctx, name) } func (d *Directory) Flush() error { @@ -363,7 +365,7 @@ func (d *Directory) AddChild(name string, nd ipld.Node) error { return err } - err = d.dirbuilder.AddChild(d.ctx, name, nd) + err = d.AddUnixFSChild(name, nd) if err != nil { return err } @@ -372,6 +374,29 @@ func (d *Directory) AddChild(name string, nd ipld.Node) error { return nil } +// AddUnixFSChild adds a child to the inner UnixFS directory +// and transitions to a HAMT implementation if needed. +func (d *Directory) AddUnixFSChild(name string, node ipld.Node) error { + if uio.UseHAMTSharding { + // If the directory HAMT implementation is being used and this + // directory is actually a basic implementation switch it to HAMT. + if basicDir, ok := d.unixfsDir.(*uio.BasicDirectory); ok { + hamtDir, err := basicDir.SwitchToSharding(d.ctx) + if err != nil { + return err + } + d.unixfsDir = hamtDir + } + } + + err := d.unixfsDir.AddChild(d.ctx, name, node) + if err != nil { + return err + } + + return nil +} + func (d *Directory) sync() error { for name, dir := range d.childDirs { nd, err := dir.GetNode() @@ -426,7 +451,7 @@ func (d *Directory) GetNode() (ipld.Node, error) { return nil, err } - nd, err := d.dirbuilder.GetNode() + nd, err := d.unixfsDir.GetNode() if err != nil { return nil, err } From 747a93727c0df3c01867edfd47ae716996a8653c Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Mon, 9 Jul 2018 12:02:47 -0300 Subject: [PATCH 2406/3817] unixfs: remove unused `ShardSplitThreshold` variable License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-unixfs@ba02f3faa68678b2f22f54d0b93e68e63d62c359 --- unixfs/io/directory.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index 31b2846ac..dd13e6604 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -13,11 +13,6 @@ import ( cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) -// ShardSplitThreshold specifies how large of an unsharded directory -// the Directory code will generate. Adding entries over this value will -// result in the node being restructured into a sharded object. -var ShardSplitThreshold = 1000 - // UseHAMTSharding is a global flag that signifies whether or not to use the // HAMT sharding scheme for directory creation var UseHAMTSharding = false From 931a15e4f50736034ca8e240cfad5368ba25c3bc Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Thu, 12 Jul 2018 11:05:56 -0300 Subject: [PATCH 2407/3817] mfs: remove `sort` from `ListNames()` License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-mfs@7343516356ccd99e6d27c9c8b30dfc2c5285c2cf --- mfs/dir.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 17f09356f..d59915812 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -6,7 +6,6 @@ import ( "fmt" "os" "path" - "sort" "sync" "time" @@ -245,8 +244,6 @@ func (d *Directory) ListNames(ctx context.Context) ([]string, error) { return nil, err } - sort.Strings(out) - return out, nil } From 9bfba3507d9e19cfdd78941deb09c5622a5927df Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 6 Oct 2017 14:17:30 +0300 Subject: [PATCH 2408/3817] filestore: add URLStore License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-filestore@9a4b9875928f566dbacc0b3ac4d6148c3d1e94c5 --- filestore/filestore.go | 2 +- filestore/filestore_test.go | 2 +- filestore/fsrefstore.go | 76 +++++++++++++++++++++++++++++++------ filestore/pb/Makefile | 12 +++--- filestore/pb/dataobj.pb.go | 12 +++++- filestore/pb/dataobj.proto | 1 + 6 files changed, 82 insertions(+), 23 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 66289b068..6140451ed 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,9 +11,9 @@ import ( "context" blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" - posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" + posinfo "gx/ipfs/QmdGSfmN4wWNXVs2XiwHbpjnUikJ7HyrTJNHyYGdodyJDC/go-ipfs-posinfo" blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 9589c3dd5..6323f49c8 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -9,8 +9,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + posinfo "gx/ipfs/QmdGSfmN4wWNXVs2XiwHbpjnUikJ7HyrTJNHyYGdodyJDC/go-ipfs-posinfo" blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 2e481ade9..6d4509876 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "net/http" "os" "path/filepath" @@ -12,8 +13,8 @@ import ( dshelp "gx/ipfs/QmNP2u7bofwUQptHQGPfabGWtTCbxhNLSZKqbf1uzsup9V/go-ipfs-ds-help" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" - posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + posinfo "gx/ipfs/QmdGSfmN4wWNXVs2XiwHbpjnUikJ7HyrTJNHyYGdodyJDC/go-ipfs-posinfo" blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsns "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/namespace" @@ -111,7 +112,6 @@ func (f *FileManager) Get(c *cid.Cid) (blocks.Block, error) { if err != nil { return nil, err } - out, err := f.readDataObj(c, dobj) if err != nil { return nil, err @@ -120,6 +120,14 @@ func (f *FileManager) Get(c *cid.Cid) (blocks.Block, error) { return blocks.NewBlockWithCid(out, c) } +func (f *FileManager) readDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { + if !d.GetURL() { + return f.readFileDataObj(c, d) + } else { + return f.readURLDataObj(c, d) + } +} + func (f *FileManager) getDataObj(c *cid.Cid) (*pb.DataObj, error) { o, err := f.ds.Get(dshelp.CidToDsKey(c)) switch err { @@ -148,8 +156,7 @@ func unmarshalDataObj(o interface{}) (*pb.DataObj, error) { return &dobj, nil } -// reads and verifies the block -func (f *FileManager) readDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { +func (f *FileManager) readFileDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { p := filepath.FromSlash(d.GetFilePath()) abspath := filepath.Join(f.root, p) @@ -187,6 +194,46 @@ func (f *FileManager) readDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { return outbuf, nil } +// reads and verifies the block from URL +func (f *FileManager) readURLDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { + + req, err := http.NewRequest("GET", d.GetFilePath(), nil) + if err != nil { + return nil, err + } + + req.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", d.GetOffset(), d.GetOffset()+d.GetSize_()-1)) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + if res.StatusCode != http.StatusPartialContent { + return nil, fmt.Errorf("expected HTTP 206 got %d", res.StatusCode) + } + + outbuf := make([]byte, d.GetSize_()) + _, err = io.ReadFull(res.Body, outbuf) + if err == io.EOF || err == io.ErrUnexpectedEOF { + return nil, &CorruptReferenceError{StatusFileChanged, err} + } else if err != nil { + return nil, &CorruptReferenceError{StatusFileError, err} + } + res.Body.Close() + + outcid, err := c.Prefix().Sum(outbuf) + if err != nil { + return nil, err + } + + if !c.Equals(outcid) { + return nil, &CorruptReferenceError{StatusFileChanged, + fmt.Errorf("data in file did not match. %s offset %d", d.GetFilePath(), d.GetOffset())} + } + + return outbuf, nil +} + // Has returns if the FileManager is storing a block reference. It does not // validate the data, nor checks if the reference is valid. func (f *FileManager) Has(c *cid.Cid) (bool, error) { @@ -209,16 +256,21 @@ func (f *FileManager) Put(b *posinfo.FilestoreNode) error { func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { var dobj pb.DataObj - if !filepath.HasPrefix(b.PosInfo.FullPath, f.root) { - return fmt.Errorf("cannot add filestore references outside ipfs root (%s)", f.root) - } + if !b.PosInfo.IsURL { + if !filepath.HasPrefix(b.PosInfo.FullPath, f.root) { + return fmt.Errorf("cannot add filestore references outside ipfs root (%s)", f.root) + } - p, err := filepath.Rel(f.root, b.PosInfo.FullPath) - if err != nil { - return err - } + p, err := filepath.Rel(f.root, b.PosInfo.FullPath) + if err != nil { + return err + } - dobj.FilePath = proto.String(filepath.ToSlash(p)) + dobj.FilePath = proto.String(filepath.ToSlash(p)) + } else { + dobj.FilePath = proto.String(b.PosInfo.FullPath) + dobj.URL = proto.Bool(true) + } dobj.Offset = proto.Uint64(b.PosInfo.Offset) dobj.Size_ = proto.Uint64(uint64(len(b.RawData()))) diff --git a/filestore/pb/Makefile b/filestore/pb/Makefile index 5101a482d..505f70e75 100644 --- a/filestore/pb/Makefile +++ b/filestore/pb/Makefile @@ -1,10 +1,8 @@ -PB = $(wildcard *.proto) -GO = $(PB:.proto=.pb.go) +include mk/header.mk -all: $(GO) +PB_$(d) = $(wildcard $(d)/*.proto) +TGTS_$(d) = $(PB_$(d):.proto=.pb.go) -%.pb.go: %.proto - protoc --gogo_out=. $< +#DEPS_GO += $(TGTS_$(d)) -clean: - rm *.pb.go +include mk/footer.mk diff --git a/filestore/pb/dataobj.pb.go b/filestore/pb/dataobj.pb.go index fadd40c1a..517ae1b7f 100644 --- a/filestore/pb/dataobj.pb.go +++ b/filestore/pb/dataobj.pb.go @@ -1,12 +1,12 @@ // Code generated by protoc-gen-gogo. -// source: dataobj.proto +// source: filestore/pb/dataobj.proto // DO NOT EDIT! /* Package datastore_pb is a generated protocol buffer package. It is generated from these files: - dataobj.proto + filestore/pb/dataobj.proto It has these top-level messages: DataObj @@ -26,6 +26,7 @@ type DataObj struct { FilePath *string `protobuf:"bytes,1,opt,name=FilePath" json:"FilePath,omitempty"` Offset *uint64 `protobuf:"varint,2,opt,name=Offset" json:"Offset,omitempty"` Size_ *uint64 `protobuf:"varint,3,opt,name=Size" json:"Size,omitempty"` + URL *bool `protobuf:"varint,4,opt,name=URL" json:"URL,omitempty"` XXX_unrecognized []byte `json:"-"` } @@ -54,6 +55,13 @@ func (m *DataObj) GetSize_() uint64 { return 0 } +func (m *DataObj) GetURL() bool { + if m != nil && m.URL != nil { + return *m.URL + } + return false +} + func init() { proto.RegisterType((*DataObj)(nil), "datastore.pb.DataObj") } diff --git a/filestore/pb/dataobj.proto b/filestore/pb/dataobj.proto index c7d7f0eea..311042a0e 100644 --- a/filestore/pb/dataobj.proto +++ b/filestore/pb/dataobj.proto @@ -4,4 +4,5 @@ message DataObj { optional string FilePath = 1; optional uint64 Offset = 2; optional uint64 Size = 3; + optional bool URL = 4; } From 8a95c61a8914f66d9e5a0c22c7f822b333e101d9 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 22 Jun 2018 21:39:15 -0400 Subject: [PATCH 2409/3817] Simplify code: use prefix instead of flag to determine if a url License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@6e1e3f1fd4a921d1c6d6361aee1f8c41301f0eb8 --- filestore/filestore.go | 2 +- filestore/filestore_test.go | 14 +++++++++++++- filestore/fsrefstore.go | 13 +++++++++---- filestore/pb/Makefile | 12 +++++++----- filestore/pb/dataobj.pb.go | 12 ++---------- filestore/pb/dataobj.proto | 1 - 6 files changed, 32 insertions(+), 22 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 6140451ed..66289b068 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,9 +11,9 @@ import ( "context" blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" + posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" - posinfo "gx/ipfs/QmdGSfmN4wWNXVs2XiwHbpjnUikJ7HyrTJNHyYGdodyJDC/go-ipfs-posinfo" blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 6323f49c8..8336010e5 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -9,8 +9,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" + posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - posinfo "gx/ipfs/QmdGSfmN4wWNXVs2XiwHbpjnUikJ7HyrTJNHyYGdodyJDC/go-ipfs-posinfo" blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) @@ -162,3 +162,15 @@ func TestDeletes(t *testing.T) { } } } + +func TestIsURL(t *testing.T) { + if !IsURL("http://www.example.com") { + t.Fatal("IsURL failed: http://www.example.com") + } + if !IsURL("https://www.example.com") { + t.Fatal("IsURL failed: https://www.example.com") + } + if IsURL("adir/afile") { + t.Fatal("IsURL recognized non-url") + } +} diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 6d4509876..d51139536 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -13,8 +13,8 @@ import ( dshelp "gx/ipfs/QmNP2u7bofwUQptHQGPfabGWtTCbxhNLSZKqbf1uzsup9V/go-ipfs-ds-help" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" + posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - posinfo "gx/ipfs/QmdGSfmN4wWNXVs2XiwHbpjnUikJ7HyrTJNHyYGdodyJDC/go-ipfs-posinfo" blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsns "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/namespace" @@ -121,7 +121,7 @@ func (f *FileManager) Get(c *cid.Cid) (blocks.Block, error) { } func (f *FileManager) readDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { - if !d.GetURL() { + if !IsURL(d.GetFilePath()) { return f.readFileDataObj(c, d) } else { return f.readURLDataObj(c, d) @@ -256,7 +256,7 @@ func (f *FileManager) Put(b *posinfo.FilestoreNode) error { func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { var dobj pb.DataObj - if !b.PosInfo.IsURL { + if !IsURL(b.PosInfo.FullPath) { if !filepath.HasPrefix(b.PosInfo.FullPath, f.root) { return fmt.Errorf("cannot add filestore references outside ipfs root (%s)", f.root) } @@ -269,7 +269,6 @@ func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { dobj.FilePath = proto.String(filepath.ToSlash(p)) } else { dobj.FilePath = proto.String(b.PosInfo.FullPath) - dobj.URL = proto.Bool(true) } dobj.Offset = proto.Uint64(b.PosInfo.Offset) dobj.Size_ = proto.Uint64(uint64(len(b.RawData()))) @@ -298,3 +297,9 @@ func (f *FileManager) PutMany(bs []*posinfo.FilestoreNode) error { return batch.Commit() } + +func IsURL(str string) bool { + return (len(str) > 7 && str[0] == 'h' && str[1] == 't' && str[2] == 't' && str[3] == 'p') && + ((len(str) > 8 && str[4] == 's' && str[5] == ':' && str[6] == '/' && str[7] == '/') || + (str[4] == ':' && str[5] == '/' && str[6] == '/')) +} diff --git a/filestore/pb/Makefile b/filestore/pb/Makefile index 505f70e75..5101a482d 100644 --- a/filestore/pb/Makefile +++ b/filestore/pb/Makefile @@ -1,8 +1,10 @@ -include mk/header.mk +PB = $(wildcard *.proto) +GO = $(PB:.proto=.pb.go) -PB_$(d) = $(wildcard $(d)/*.proto) -TGTS_$(d) = $(PB_$(d):.proto=.pb.go) +all: $(GO) -#DEPS_GO += $(TGTS_$(d)) +%.pb.go: %.proto + protoc --gogo_out=. $< -include mk/footer.mk +clean: + rm *.pb.go diff --git a/filestore/pb/dataobj.pb.go b/filestore/pb/dataobj.pb.go index 517ae1b7f..fadd40c1a 100644 --- a/filestore/pb/dataobj.pb.go +++ b/filestore/pb/dataobj.pb.go @@ -1,12 +1,12 @@ // Code generated by protoc-gen-gogo. -// source: filestore/pb/dataobj.proto +// source: dataobj.proto // DO NOT EDIT! /* Package datastore_pb is a generated protocol buffer package. It is generated from these files: - filestore/pb/dataobj.proto + dataobj.proto It has these top-level messages: DataObj @@ -26,7 +26,6 @@ type DataObj struct { FilePath *string `protobuf:"bytes,1,opt,name=FilePath" json:"FilePath,omitempty"` Offset *uint64 `protobuf:"varint,2,opt,name=Offset" json:"Offset,omitempty"` Size_ *uint64 `protobuf:"varint,3,opt,name=Size" json:"Size,omitempty"` - URL *bool `protobuf:"varint,4,opt,name=URL" json:"URL,omitempty"` XXX_unrecognized []byte `json:"-"` } @@ -55,13 +54,6 @@ func (m *DataObj) GetSize_() uint64 { return 0 } -func (m *DataObj) GetURL() bool { - if m != nil && m.URL != nil { - return *m.URL - } - return false -} - func init() { proto.RegisterType((*DataObj)(nil), "datastore.pb.DataObj") } diff --git a/filestore/pb/dataobj.proto b/filestore/pb/dataobj.proto index 311042a0e..c7d7f0eea 100644 --- a/filestore/pb/dataobj.proto +++ b/filestore/pb/dataobj.proto @@ -4,5 +4,4 @@ message DataObj { optional string FilePath = 1; optional uint64 Offset = 2; optional uint64 Size = 3; - optional bool URL = 4; } From 790940edf22b24941886ea512b96d1ff57eee608 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sat, 23 Jun 2018 17:03:57 -0400 Subject: [PATCH 2410/3817] Add config option to enable urlstore. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@70bef7e2eb7bedce266eed9ea6f94366c1b58e5a --- filestore/filestore_test.go | 1 + filestore/fsrefstore.go | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 8336010e5..7f15e9009 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -23,6 +23,7 @@ func newTestFilestore(t *testing.T) (string, *Filestore) { t.Fatal(err) } fm := NewFileManager(mds, testdir) + fm.AllowFiles = true bs := blockstore.NewBlockstore(mds) fstore := NewFilestore(bs, fm) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index d51139536..02770eee7 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -29,8 +29,10 @@ var FilestorePrefix = ds.NewKey("filestore") // to the actual location of the block data in the filesystem // (a path and an offset). type FileManager struct { - ds ds.Batching - root string + AllowFiles bool + AllowUrls bool + ds ds.Batching + root string } // CorruptReferenceError implements the error interface. @@ -52,7 +54,7 @@ func (c CorruptReferenceError) Error() string { // datastore and root. All FilestoreNodes paths are relative to the // root path given here, which is prepended for any operations. func NewFileManager(ds ds.Batching, root string) *FileManager { - return &FileManager{dsns.Wrap(ds, FilestorePrefix), root} + return &FileManager{ds: dsns.Wrap(ds, FilestorePrefix), root: root} } // AllKeysChan returns a channel from which to read the keys stored in @@ -157,6 +159,10 @@ func unmarshalDataObj(o interface{}) (*pb.DataObj, error) { } func (f *FileManager) readFileDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { + if !f.AllowFiles { + return nil, fmt.Errorf("filestore not enabled") + } + p := filepath.FromSlash(d.GetFilePath()) abspath := filepath.Join(f.root, p) @@ -196,6 +202,9 @@ func (f *FileManager) readFileDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) // reads and verifies the block from URL func (f *FileManager) readURLDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { + if !f.AllowUrls { + return nil, fmt.Errorf("urlstore not enabled") + } req, err := http.NewRequest("GET", d.GetFilePath(), nil) if err != nil { @@ -257,6 +266,9 @@ func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { var dobj pb.DataObj if !IsURL(b.PosInfo.FullPath) { + if !f.AllowFiles { + return fmt.Errorf("filestore not enabled") + } if !filepath.HasPrefix(b.PosInfo.FullPath, f.root) { return fmt.Errorf("cannot add filestore references outside ipfs root (%s)", f.root) } @@ -268,6 +280,9 @@ func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { dobj.FilePath = proto.String(filepath.ToSlash(p)) } else { + if !f.AllowUrls { + return fmt.Errorf("urlstore not enabled") + } dobj.FilePath = proto.String(b.PosInfo.FullPath) } dobj.Offset = proto.Uint64(b.PosInfo.Offset) From ad14507527c1e52be42bfd6c1d3b90f4dbdc3037 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 26 Jun 2018 04:56:03 -0400 Subject: [PATCH 2411/3817] Return better error code when an http request failed. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@d47eee957bb5a6f57010c8f0bc0ab27b047f85c5 --- filestore/fsrefstore.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 02770eee7..710deac03 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -215,10 +215,11 @@ func (f *FileManager) readURLDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) res, err := http.DefaultClient.Do(req) if err != nil { - return nil, err + return nil, &CorruptReferenceError{StatusFileError, err} } if res.StatusCode != http.StatusPartialContent { - return nil, fmt.Errorf("expected HTTP 206 got %d", res.StatusCode) + return nil, &CorruptReferenceError{StatusFileError, + fmt.Errorf("expected HTTP 206 got %d", res.StatusCode)} } outbuf := make([]byte, d.GetSize_()) From 2ff9b7525cb159c94fa83119e04af5f843ef7b4d Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 26 Jun 2018 15:55:36 -0400 Subject: [PATCH 2412/3817] Code cleanups to make code climate happy. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@67b4db46e3c4f0c46289fd41365eb7d4532348a1 --- filestore/fsrefstore.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 710deac03..be0be92c7 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -123,11 +123,10 @@ func (f *FileManager) Get(c *cid.Cid) (blocks.Block, error) { } func (f *FileManager) readDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { - if !IsURL(d.GetFilePath()) { - return f.readFileDataObj(c, d) - } else { + if IsURL(d.GetFilePath()) { return f.readURLDataObj(c, d) } + return f.readFileDataObj(c, d) } func (f *FileManager) getDataObj(c *cid.Cid) (*pb.DataObj, error) { @@ -266,7 +265,12 @@ func (f *FileManager) Put(b *posinfo.FilestoreNode) error { func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { var dobj pb.DataObj - if !IsURL(b.PosInfo.FullPath) { + if IsURL(b.PosInfo.FullPath) { + if !f.AllowUrls { + return fmt.Errorf("urlstore not enabled") + } + dobj.FilePath = proto.String(b.PosInfo.FullPath) + } else { if !f.AllowFiles { return fmt.Errorf("filestore not enabled") } @@ -280,11 +284,6 @@ func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { } dobj.FilePath = proto.String(filepath.ToSlash(p)) - } else { - if !f.AllowUrls { - return fmt.Errorf("urlstore not enabled") - } - dobj.FilePath = proto.String(b.PosInfo.FullPath) } dobj.Offset = proto.Uint64(b.PosInfo.Offset) dobj.Size_ = proto.Uint64(uint64(len(b.RawData()))) @@ -314,6 +313,8 @@ func (f *FileManager) PutMany(bs []*posinfo.FilestoreNode) error { return batch.Commit() } +// IsURL returns true if the string represents a valid URL that the +// urlstore can handle. func IsURL(str string) bool { return (len(str) > 7 && str[0] == 'h' && str[1] == 't' && str[2] == 't' && str[3] == 'p') && ((len(str) > 8 && str[4] == 's' && str[5] == ':' && str[6] == '/' && str[7] == '/') || From 330b3f760c7ae55baf9c6b81d9ec4ca3ba3fb1f9 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 26 Jun 2018 22:30:15 -0400 Subject: [PATCH 2413/3817] filestore: Return consistent err msg. when file/urlstore is not enabled. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@5d1049a8eb14fde4aa7350706603dd996b89ce11 --- filestore/filestore.go | 4 ++++ filestore/fsrefstore.go | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 66289b068..0c80fb1d5 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -9,6 +9,7 @@ package filestore import ( "context" + "errors" blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" @@ -20,6 +21,9 @@ import ( var log = logging.Logger("filestore") +var ErrFilestoreNotEnabled = errors.New("filestore is not enabled, see https://git.io/vNItf") +var ErrUrlstoreNotEnabled = errors.New("urlstore is not enabled") + // Filestore implements a Blockstore by combining a standard Blockstore // to store regular blocks and a special Blockstore called // FileManager to store blocks which data exists in an external file. diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index be0be92c7..98255caa7 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -159,7 +159,7 @@ func unmarshalDataObj(o interface{}) (*pb.DataObj, error) { func (f *FileManager) readFileDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { if !f.AllowFiles { - return nil, fmt.Errorf("filestore not enabled") + return nil, ErrFilestoreNotEnabled } p := filepath.FromSlash(d.GetFilePath()) @@ -202,7 +202,7 @@ func (f *FileManager) readFileDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) // reads and verifies the block from URL func (f *FileManager) readURLDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { if !f.AllowUrls { - return nil, fmt.Errorf("urlstore not enabled") + return nil, ErrUrlstoreNotEnabled } req, err := http.NewRequest("GET", d.GetFilePath(), nil) @@ -267,12 +267,12 @@ func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { if IsURL(b.PosInfo.FullPath) { if !f.AllowUrls { - return fmt.Errorf("urlstore not enabled") + return ErrUrlstoreNotEnabled } dobj.FilePath = proto.String(b.PosInfo.FullPath) } else { if !f.AllowFiles { - return fmt.Errorf("filestore not enabled") + return ErrFilestoreNotEnabled } if !filepath.HasPrefix(b.PosInfo.FullPath, f.root) { return fmt.Errorf("cannot add filestore references outside ipfs root (%s)", f.root) From a7ca17b2959b960399c2c4c514b2d9ae619e3855 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 29 Jun 2018 23:03:51 -0400 Subject: [PATCH 2414/3817] Address c.r. and additional tweaks. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@5693b30379c630eff9e81278e253e7b80b9548c0 --- filestore/filestore_test.go | 2 +- filestore/fsrefstore.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 7f15e9009..279a2bc82 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -171,7 +171,7 @@ func TestIsURL(t *testing.T) { if !IsURL("https://www.example.com") { t.Fatal("IsURL failed: https://www.example.com") } - if IsURL("adir/afile") { + if IsURL("adir/afile") || IsURL("http:/ /afile") || IsURL("http:/a/file") { t.Fatal("IsURL recognized non-url") } } diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 98255caa7..960fc93e8 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -314,7 +314,8 @@ func (f *FileManager) PutMany(bs []*posinfo.FilestoreNode) error { } // IsURL returns true if the string represents a valid URL that the -// urlstore can handle. +// urlstore can handle. More specifically it returns true if a string +// begins with 'http://' or 'https://'. func IsURL(str string) bool { return (len(str) > 7 && str[0] == 'h' && str[1] == 't' && str[2] == 't' && str[3] == 'p') && ((len(str) > 8 && str[4] == 's' && str[5] == ':' && str[6] == '/' && str[7] == '/') || From f6810ece90d6b0fc8d8ae2928fd6b3ce45351c1b Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Mon, 16 Jul 2018 12:27:43 -0300 Subject: [PATCH 2415/3817] unixfs: fix `FSNode` accessor in `NewDirectoryFromNode` License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-unixfs@a64b190a9ae05e5639048cbdcd18952971174e72 --- unixfs/io/directory.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index dd13e6604..5dfa1dfd0 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -107,7 +107,7 @@ func NewDirectoryFromNode(dserv ipld.DAGService, node ipld.Node) (Directory, err return nil, err } - switch fsNode.GetType() { + switch fsNode.Type() { case format.TDirectory: return &BasicDirectory{ dserv: dserv, From 3b9442bc263ec28d0058bb9bb493092371d7218f Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Mon, 16 Jul 2018 10:54:17 -0300 Subject: [PATCH 2416/3817] unixfs: refactor switch in `precalcNextBuf` Do not use `NewDagReader` just for the `RawNode` case. Treat invalid UnixFS types in the same case. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-unixfs@5d5060ddffaa89464c6bd8b45bbdeca76bd962c5 --- unixfs/io/dagreader.go | 4 ++-- unixfs/io/pbdagreader.go | 16 +++++----------- unixfs/unixfs.go | 1 - 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 79cf387b2..867c4b1d9 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -3,7 +3,6 @@ package io import ( "context" "errors" - "fmt" "io" mdag "github.com/ipfs/go-ipfs/merkledag" @@ -17,6 +16,7 @@ import ( var ( ErrIsDir = errors.New("this dag node is a directory") ErrCantReadSymlinks = errors.New("cannot currently read symlinks") + ErrUnkownNodeType = errors.New("unknown node type") ) // A DagReader provides read-only read and seek acess to a unixfs file. @@ -74,6 +74,6 @@ func NewDagReader(ctx context.Context, n ipld.Node, serv ipld.NodeGetter) (DagRe return nil, ft.ErrUnrecognizedType } default: - return nil, fmt.Errorf("unrecognized node type") + return nil, ErrUnkownNodeType } } diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 5ce2cc3bf..5b7bceb9e 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -106,26 +106,20 @@ func (dr *PBDagReader) precalcNextBuf(ctx context.Context) error { } switch fsNode.Type() { - case ftpb.Data_Directory, ftpb.Data_HAMTShard: - // A directory should not exist within a file - return ft.ErrInvalidDirLocation case ftpb.Data_File: dr.buf = NewPBFileReader(dr.ctx, nxt, fsNode, dr.serv) return nil case ftpb.Data_Raw: dr.buf = NewBufDagReader(fsNode.Data()) return nil - case ftpb.Data_Metadata: - return errors.New("shouldnt have had metadata object inside file") - case ftpb.Data_Symlink: - return errors.New("shouldnt have had symlink inside file") default: - return ft.ErrUnrecognizedType + return fmt.Errorf("found %s node in unexpected place", fsNode.Type().String()) } + case *mdag.RawNode: + dr.buf = NewBufDagReader(nxt.RawData()) + return nil default: - var err error - dr.buf, err = NewDagReader(ctx, nxt, dr.serv) - return err + return ErrUnkownNodeType } } diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index 9cd9731ed..bec222f0c 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -25,7 +25,6 @@ const ( // Common errors var ( ErrMalformedFileFormat = errors.New("malformed data in file format") - ErrInvalidDirLocation = errors.New("found directory node in unexpected place") ErrUnrecognizedType = errors.New("unrecognized node type") ) From 11c54768a3a4661b3b8528b0e78221d2b673d19f Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Mon, 16 Jul 2018 11:14:27 -0300 Subject: [PATCH 2417/3817] unixfs: split `precalcNextBuf` Create new `loadBufNode` function to handle the `buf` logic which is unrelated to the main `precalcNextBuf` logic of processing promises to fetch nodes. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-unixfs@0271059bc30a2f778ed9fdae7455a3a5f34185fa --- unixfs/io/pbdagreader.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 5b7bceb9e..53d85c796 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -98,16 +98,20 @@ func (dr *PBDagReader) precalcNextBuf(ctx context.Context) error { dr.promises[dr.linkPosition] = nil dr.linkPosition++ - switch nxt := nxt.(type) { + return dr.loadBufNode(nxt) +} + +func (dr *PBDagReader) loadBufNode(node ipld.Node) error { + switch node := node.(type) { case *mdag.ProtoNode: - fsNode, err := ft.FSNodeFromBytes(nxt.Data()) + fsNode, err := ft.FSNodeFromBytes(node.Data()) if err != nil { return fmt.Errorf("incorrectly formatted protobuf: %s", err) } switch fsNode.Type() { case ftpb.Data_File: - dr.buf = NewPBFileReader(dr.ctx, nxt, fsNode, dr.serv) + dr.buf = NewPBFileReader(dr.ctx, node, fsNode, dr.serv) return nil case ftpb.Data_Raw: dr.buf = NewBufDagReader(fsNode.Data()) @@ -116,7 +120,7 @@ func (dr *PBDagReader) precalcNextBuf(ctx context.Context) error { return fmt.Errorf("found %s node in unexpected place", fsNode.Type().String()) } case *mdag.RawNode: - dr.buf = NewBufDagReader(nxt.RawData()) + dr.buf = NewBufDagReader(node.RawData()) return nil default: return ErrUnkownNodeType From c77bbc913a81e623157de152f1cd155321c1b092 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 16 Jul 2018 15:16:49 -0700 Subject: [PATCH 2418/3817] update go-cid alternative to #5243 that updates go-cid and all packages that depend on it License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@2c11b74e30ce605de87ce6521e48cfff6100a5ac --- unixfs/archive/archive.go | 2 +- unixfs/archive/tar/writer.go | 2 +- unixfs/hamt/hamt.go | 4 ++-- unixfs/hamt/hamt_stress_test.go | 2 +- unixfs/hamt/hamt_test.go | 2 +- unixfs/io/dagreader.go | 2 +- unixfs/io/directory.go | 4 ++-- unixfs/io/pbdagreader.go | 4 ++-- unixfs/io/resolve.go | 2 +- unixfs/mod/dagmodifier.go | 6 +++--- unixfs/test/utils.go | 6 +++--- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index c32715e27..96c12f682 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -11,7 +11,7 @@ import ( tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" uio "github.com/ipfs/go-ipfs/unixfs/io" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 0c2df9fb1..fe5982ff4 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -15,7 +15,7 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" upb "github.com/ipfs/go-ipfs/unixfs/pb" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // Writer is a utility structure that helps to write diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 9d6c00f62..37f0fd78b 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -30,9 +30,9 @@ import ( upb "github.com/ipfs/go-ipfs/unixfs/pb" bitfield "gx/ipfs/QmTbBs3Y3u5F69XNJzdnnc6SP5GKgcXxCDzx6w8m6piVRT/go-bitfield" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" "gx/ipfs/QmfJHywXQu98UeZtGJBQrPAR6AtmDjjbe3qjTo9piXHPnx/murmur3" ) diff --git a/unixfs/hamt/hamt_stress_test.go b/unixfs/hamt/hamt_stress_test.go index c5a4fef9b..a9361e2c2 100644 --- a/unixfs/hamt/hamt_stress_test.go +++ b/unixfs/hamt/hamt_stress_test.go @@ -11,7 +11,7 @@ import ( mdtest "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) func getNames(prefix string, count int) []string { diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 48a45c49d..4cad058ba 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -14,7 +14,7 @@ import ( dagutils "github.com/ipfs/go-ipfs/merkledag/utils" ft "github.com/ipfs/go-ipfs/unixfs" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) func shuffle(seed int64, arr []string) { diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 79cf387b2..1039f694f 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -10,7 +10,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // Common errors diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index 5dfa1dfd0..c9aaa00aa 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -9,8 +9,8 @@ import ( format "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // UseHAMTSharding is a global flag that signifies whether or not to use the diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 5ce2cc3bf..6e74c6a5d 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -10,8 +10,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // PBDagReader provides a way to easily read the data contained in a dag. diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index 087d1b12f..b1e574ae9 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -7,7 +7,7 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // ResolveUnixfsOnce resolves a single hop of a path through a graph in a diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index adc172428..0a1ae0a96 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -14,10 +14,10 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - chunker "gx/ipfs/QmXnzH7wowyLZy8XJxxaQCVTgLMcDXdMBznmsrmQWCyiQV/go-ipfs-chunker" + chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // Common errors diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index a59aeaea5..05d66756e 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -16,9 +16,9 @@ import ( u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - chunker "gx/ipfs/QmXnzH7wowyLZy8XJxxaQCVTgLMcDXdMBznmsrmQWCyiQV/go-ipfs-chunker" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // SizeSplitterGen creates a generator. From c77c8df70d4c804e1f3acd068a8ef58937e7ef9e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 16 Jul 2018 15:16:49 -0700 Subject: [PATCH 2419/3817] update go-cid alternative to #5243 that updates go-cid and all packages that depend on it License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@2c9291a232cb0febaf3bb029694ce2fb3155d3f9 --- coreiface/coreapi.go | 2 +- coreiface/dag.go | 2 +- coreiface/object.go | 4 ++-- coreiface/options/dag.go | 2 +- coreiface/path.go | 2 +- coreiface/unixfs.go | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index 220f8df50..a77ad6367 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -5,7 +5,7 @@ package iface import ( "context" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // CoreAPI defines an unified interface to IPFS for Go programs diff --git a/coreiface/dag.go b/coreiface/dag.go index 3c4dc0c3a..158db7419 100644 --- a/coreiface/dag.go +++ b/coreiface/dag.go @@ -6,7 +6,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // DagAPI specifies the interface to IPLD diff --git a/coreiface/object.go b/coreiface/object.go index d53f4d214..a18a38ebe 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -6,8 +6,8 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // ObjectStat provides information about dag nodes diff --git a/coreiface/options/dag.go b/coreiface/options/dag.go index b5e6dcea1..57465deee 100644 --- a/coreiface/options/dag.go +++ b/coreiface/options/dag.go @@ -3,7 +3,7 @@ package options import ( "math" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ) type DagPutSettings struct { diff --git a/coreiface/path.go b/coreiface/path.go index 929b97bcd..51513772f 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ) // Path is a generic wrapper for paths used in the API. A path can be resolved diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 11e14cc84..c59451d00 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -4,7 +4,7 @@ import ( "context" "io" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // UnixfsAPI is the basic interface to immutable files in IPFS From 811843b35a38608328d446a9e41d20a2f0339f10 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 16 Jul 2018 15:16:49 -0700 Subject: [PATCH 2420/3817] update go-cid alternative to #5243 that updates go-cid and all packages that depend on it License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-path@d1e9c97f47bd19d6f7ee5110f758261b68f3d75a --- path/path.go | 2 +- path/resolver/resolver.go | 4 ++-- path/resolver/resolver_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/path/path.go b/path/path.go index c1b7de2a3..5622a6d05 100644 --- a/path/path.go +++ b/path/path.go @@ -6,7 +6,7 @@ import ( "path" "strings" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ) var ( diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 73ac3fa23..8c7b28b1c 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -10,8 +10,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" ) diff --git a/path/resolver/resolver_test.go b/path/resolver/resolver_test.go index 1c2e0e6b9..149068120 100644 --- a/path/resolver/resolver_test.go +++ b/path/resolver/resolver_test.go @@ -11,7 +11,7 @@ import ( "github.com/ipfs/go-ipfs/path/resolver" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) func randNode() *merkledag.ProtoNode { From 9a31c53cd54528847ffe0593788c29aa9a1a966f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 16 Jul 2018 15:16:49 -0700 Subject: [PATCH 2421/3817] update go-cid alternative to #5243 that updates go-cid and all packages that depend on it License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@1eddb2253315cb2444d4f953d4114098d21119b8 --- mfs/dir.go | 4 ++-- mfs/file.go | 4 ++-- mfs/mfs_test.go | 10 +++++----- mfs/ops.go | 4 ++-- mfs/repub_test.go | 2 +- mfs/system.go | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 643b024d7..9796591d9 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -15,8 +15,8 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) var ErrNotYetImplemented = errors.New("not yet implemented") diff --git a/mfs/file.go b/mfs/file.go index 40106d1da..4ef16ff5d 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -9,8 +9,8 @@ import ( ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - chunker "gx/ipfs/QmXnzH7wowyLZy8XJxxaQCVTgLMcDXdMBznmsrmQWCyiQV/go-ipfs-chunker" + chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 9ba806a03..228c5bdc0 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -22,11 +22,11 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - chunker "gx/ipfs/QmXnzH7wowyLZy8XJxxaQCVTgLMcDXdMBznmsrmQWCyiQV/go-ipfs-chunker" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - bstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" + bstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" + offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/mfs/ops.go b/mfs/ops.go index 20d2c5e74..5ae195651 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -8,8 +8,8 @@ import ( path "github.com/ipfs/go-ipfs/path" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // Mv moves the file or directory at 'src' to 'dst' diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 0782d50c8..bfd21deab 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ci "gx/ipfs/QmcW4FGAt24fdK1jBgWQn3yP4R9ZLyWQqjozv9QK7epRhL/go-testutil/ci" ) diff --git a/mfs/system.go b/mfs/system.go index f87d2cb75..53a1b9eb3 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -19,8 +19,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" ) From d9ba2c68864273c95b8c20e41efec275d14b34fe Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 16 Jul 2018 15:16:49 -0700 Subject: [PATCH 2422/3817] update go-cid alternative to #5243 that updates go-cid and all packages that depend on it License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@84c8ecfd5569b4f5ce199114b8443adb449860ba --- ipld/merkledag/coding.go | 6 +++--- ipld/merkledag/errservice.go | 4 ++-- ipld/merkledag/merkledag.go | 8 ++++---- ipld/merkledag/merkledag_test.go | 8 ++++---- ipld/merkledag/node.go | 4 ++-- ipld/merkledag/node_test.go | 2 +- ipld/merkledag/raw.go | 6 +++--- ipld/merkledag/readonly.go | 2 +- ipld/merkledag/readonly_test.go | 4 ++-- ipld/merkledag/rwservice.go | 4 ++-- ipld/merkledag/session.go | 2 +- ipld/merkledag/test/utils.go | 6 +++--- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/traverse/traverse_test.go | 2 +- ipld/merkledag/utils/diff.go | 4 ++-- ipld/merkledag/utils/diffenum.go | 4 ++-- ipld/merkledag/utils/diffenum_test.go | 4 ++-- ipld/merkledag/utils/utils.go | 6 +++--- ipld/merkledag/utils/utils_test.go | 4 ++-- 19 files changed, 41 insertions(+), 41 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 92a7f135e..6111e2d36 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -5,12 +5,12 @@ import ( "sort" "strings" - "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" + "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" pb "github.com/ipfs/go-ipfs/merkledag/pb" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/errservice.go b/ipld/merkledag/errservice.go index b55172c44..9996ed72d 100644 --- a/ipld/merkledag/errservice.go +++ b/ipld/merkledag/errservice.go @@ -3,8 +3,8 @@ package merkledag import ( "context" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // ErrorService implements ipld.DAGService, returning 'Err' for every call. diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index a59e3469e..4bdf734e8 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -8,10 +8,10 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" - ipldcbor "gx/ipfs/QmSF1Ksgn5d7JCTBt4e1yp4wzs6tpYyweCZ4PcDYp3tNeK/go-ipld-cbor" - blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" + ipldcbor "gx/ipfs/QmWrbExtUaQQHjJ8FVVDAWj5o1MRAELDUV3VmoQsZHHb6L/go-ipld-cbor" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // TODO: We should move these registrations elsewhere. Really, most of the IPLD diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 86cc66040..b26be3d9e 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -20,10 +20,10 @@ import ( dstest "github.com/ipfs/go-ipfs/merkledag/test" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" - blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) func TestNode(t *testing.T) { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 33fa02d69..6f81644ec 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -6,8 +6,8 @@ import ( "fmt" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // Common errors diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index 61277bf0b..be2f43a08 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -8,7 +8,7 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) func TestRemoveLink(t *testing.T) { diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index b9338fe9b..01d6cffff 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -2,11 +2,11 @@ package merkledag import ( "fmt" - "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" + "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // RawNode represents a node which only contains data. diff --git a/ipld/merkledag/readonly.go b/ipld/merkledag/readonly.go index 03a46d0f1..95f4ebc24 100644 --- a/ipld/merkledag/readonly.go +++ b/ipld/merkledag/readonly.go @@ -3,7 +3,7 @@ package merkledag import ( "fmt" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // ErrReadOnly is used when a read-only datastructure is written to. diff --git a/ipld/merkledag/readonly_test.go b/ipld/merkledag/readonly_test.go index 43313ecf5..d57bbe3a6 100644 --- a/ipld/merkledag/readonly_test.go +++ b/ipld/merkledag/readonly_test.go @@ -7,8 +7,8 @@ import ( . "github.com/ipfs/go-ipfs/merkledag" dstest "github.com/ipfs/go-ipfs/merkledag/test" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) func TestReadonlyProperties(t *testing.T) { diff --git a/ipld/merkledag/rwservice.go b/ipld/merkledag/rwservice.go index 44303fc3f..3eb4f414a 100644 --- a/ipld/merkledag/rwservice.go +++ b/ipld/merkledag/rwservice.go @@ -3,8 +3,8 @@ package merkledag import ( "context" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // ComboService implements ipld.DAGService, using 'Read' for all fetch methods, diff --git a/ipld/merkledag/session.go b/ipld/merkledag/session.go index f2aac1286..5731caad3 100644 --- a/ipld/merkledag/session.go +++ b/ipld/merkledag/session.go @@ -3,7 +3,7 @@ package merkledag import ( "context" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // SessionMaker is an object that can generate a new fetching session. diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index e9b07f06c..d38a9d75c 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -4,9 +4,9 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" dag "github.com/ipfs/go-ipfs/merkledag" - offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" + blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" + offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index 683dfc878..c8d9dfdb9 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -5,7 +5,7 @@ import ( "context" "errors" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // Order is an identifier for traversal algorithm orders diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index cf0e97614..4b1f867a0 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -9,7 +9,7 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" mdagtest "github.com/ipfs/go-ipfs/merkledag/test" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) func TestDFSPreNoSkip(t *testing.T) { diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go index 0743d8573..c06f50c5e 100644 --- a/ipld/merkledag/utils/diff.go +++ b/ipld/merkledag/utils/diff.go @@ -7,8 +7,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // These constants define the changes that can be applied to a DAG. diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go index e506e82c7..d2ad1c8ef 100644 --- a/ipld/merkledag/utils/diffenum.go +++ b/ipld/merkledag/utils/diffenum.go @@ -6,8 +6,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) // DiffEnumerate fetches every object in the graph pointed to by 'to' that is diff --git a/ipld/merkledag/utils/diffenum_test.go b/ipld/merkledag/utils/diffenum_test.go index 7ded02483..85bdbb26e 100644 --- a/ipld/merkledag/utils/diffenum_test.go +++ b/ipld/merkledag/utils/diffenum_test.go @@ -8,8 +8,8 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) func buildNode(name string, desc map[string]ndesc, out map[string]ipld.Node) ipld.Node { diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index e527e105a..9a3e667d5 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -8,9 +8,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - bstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" + bstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" + offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" syncds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go index 3f2a6494a..b0df1f3b8 100644 --- a/ipld/merkledag/utils/utils_test.go +++ b/ipld/merkledag/utils/utils_test.go @@ -8,8 +8,8 @@ import ( mdtest "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) func TestAddLink(t *testing.T) { From a777ccb8b6f6be3cf4b038f5965f89423c4d296e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 16 Jul 2018 15:16:49 -0700 Subject: [PATCH 2423/3817] update go-cid alternative to #5243 that updates go-cid and all packages that depend on it License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@79fd48a96b34828bec0d93892d1dea4ec212f68f --- blockservice/blockservice.go | 8 ++++---- blockservice/blockservice_test.go | 8 ++++---- blockservice/test/blocks_test.go | 8 ++++---- blockservice/test/mock.go | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 38c67bbd3..6a1e5ee05 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -11,11 +11,11 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/verifcid" - blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" - exchange "gx/ipfs/QmVSe7YJbPnEmkSUKD3HxSvp8HJoyCU55hQoCMRq7N1jaK/go-ipfs-exchange-interface" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" + blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + exchange "gx/ipfs/Qmc2faLf7URkHpsbfYM4EMbr8iSAcGAe8VPgVi64HVnwji/go-ipfs-exchange-interface" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" - blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" ) var log = logging.Logger("blockservice") diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index d8bdaa9b1..e9fcfe2be 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -3,10 +3,10 @@ package blockservice import ( "testing" - offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" - blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" - butil "gx/ipfs/QmYmE4kxv6uFGaWkeBAFYDuNcxzCn87pzwm6CkBkM9C8BM/go-ipfs-blocksutil" - blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" + blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" + offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" + butil "gx/ipfs/QmYqPGpZ9Yemr55xus9DiEztkns6Jti5XJ7hC94JbvkdqZ/go-ipfs-blocksutil" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index ee63050db..162d573a0 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -10,10 +10,10 @@ import ( . "github.com/ipfs/go-ipfs/blockservice" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" - blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" + blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" + offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index 425cf7659..436fe7d34 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -6,7 +6,7 @@ import ( tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" - mockrouting "gx/ipfs/QmWLQyLU7yopJnwMvpHM5VSMG4xmbKgcq6P246mDy9xy5E/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/mock" ) // Mocks returns |n| connected mock Blockservices From 1b24f4c9f9444b374c2326858d7440b316a69517 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 16 Jul 2018 15:16:49 -0700 Subject: [PATCH 2424/3817] update go-cid alternative to #5243 that updates go-cid and all packages that depend on it License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@65b6015f6d17e9d09102cc8bf81174471bc5ad29 --- pinning/pinner/gc/gc.go | 8 ++++---- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 6 +++--- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index d1dd9a6d9..5c4ce2dee 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -12,11 +12,11 @@ import ( pin "github.com/ipfs/go-ipfs/pin" "github.com/ipfs/go-ipfs/thirdparty/verifcid" - offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + bstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" + offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" - bstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" dstore "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 3bf114bd6..d29c0eb8e 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -12,8 +12,8 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" dutils "github.com/ipfs/go-ipfs/merkledag/utils" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 9f4a397f2..dfd5f31d4 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -9,9 +9,9 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" + blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" + offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 67d3c345a..31ea21bd8 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -12,9 +12,9 @@ import ( "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" - ipld "gx/ipfs/QmWi2BYBL5gJ3CiAiQchg6rn1A8iBsrWy51EYxvHVjFvLb/go-ipld-format" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index fefda87d9..1dbc82092 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -8,9 +8,9 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" dag "github.com/ipfs/go-ipfs/merkledag" - offline "gx/ipfs/QmRCgkkCmf1nMrW2BLZZtjP3Xyw3GfZVYRLix9wrnW4NoR/go-ipfs-exchange-offline" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" + blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" + offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) From 894663564bb892042b7419d29c2725b88f8a3849 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 16 Jul 2018 15:16:49 -0700 Subject: [PATCH 2425/3817] update go-cid alternative to #5243 that updates go-cid and all packages that depend on it License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@8ace587362904dde87cd0d1ac7a821b661af2d4a --- namesys/ipns_resolver_validation_test.go | 8 ++++---- namesys/namesys.go | 2 +- namesys/namesys_test.go | 2 +- namesys/publisher.go | 2 +- namesys/publisher_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 6 +++--- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index faf9d3e3b..fd91af9aa 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -9,12 +9,12 @@ import ( path "github.com/ipfs/go-ipfs/path" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - routing "gx/ipfs/QmPpdpS9fknTBM3qHDcpayU6nYPZQeVjia2fbNrD8YWDe6/go-libp2p-routing" - ropts "gx/ipfs/QmPpdpS9fknTBM3qHDcpayU6nYPZQeVjia2fbNrD8YWDe6/go-libp2p-routing/options" record "gx/ipfs/QmVsp2KdPYE6M8ryzCk5KHLo3zprcY5hBDaYx6uPCFUdxA/go-libp2p-record" - mockrouting "gx/ipfs/QmWLQyLU7yopJnwMvpHM5VSMG4xmbKgcq6P246mDy9xy5E/go-ipfs-routing/mock" - offline "gx/ipfs/QmWLQyLU7yopJnwMvpHM5VSMG4xmbKgcq6P246mDy9xy5E/go-ipfs-routing/offline" + routing "gx/ipfs/QmZ383TySJVeZWzGnWui6pRcKyYZk9VkKTuW7tmKRWk5au/go-libp2p-routing" + ropts "gx/ipfs/QmZ383TySJVeZWzGnWui6pRcKyYZk9VkKTuW7tmKRWk5au/go-libp2p-routing/options" pstore "gx/ipfs/QmZR2XWVVBCtbgBWnQhWk2xcQfaR3W8faQPriAiaaj7rsr/go-libp2p-peerstore" + mockrouting "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/mock" + offline "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/offline" testutil "gx/ipfs/QmcW4FGAt24fdK1jBgWQn3yP4R9ZLyWQqjozv9QK7epRhL/go-testutil" peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" diff --git a/namesys/namesys.go b/namesys/namesys.go index 06bd212fe..6f4b9cf4b 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -9,8 +9,8 @@ import ( path "github.com/ipfs/go-ipfs/path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - routing "gx/ipfs/QmPpdpS9fknTBM3qHDcpayU6nYPZQeVjia2fbNrD8YWDe6/go-libp2p-routing" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" + routing "gx/ipfs/QmZ383TySJVeZWzGnWui6pRcKyYZk9VkKTuW7tmKRWk5au/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index c6584e8fc..76573d6d6 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,8 +10,8 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/unixfs" - offroute "gx/ipfs/QmWLQyLU7yopJnwMvpHM5VSMG4xmbKgcq6P246mDy9xy5E/go-ipfs-routing/offline" pstore "gx/ipfs/QmZR2XWVVBCtbgBWnQhWk2xcQfaR3W8faQPriAiaaj7rsr/go-libp2p-peerstore" + offroute "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/offline" peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" diff --git a/namesys/publisher.go b/namesys/publisher.go index d057dba76..77d593a59 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -11,7 +11,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" ft "github.com/ipfs/go-ipfs/unixfs" - routing "gx/ipfs/QmPpdpS9fknTBM3qHDcpayU6nYPZQeVjia2fbNrD8YWDe6/go-libp2p-routing" + routing "gx/ipfs/QmZ383TySJVeZWzGnWui6pRcKyYZk9VkKTuW7tmKRWk5au/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index c96f38039..7926fd67e 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - dshelp "gx/ipfs/QmNP2u7bofwUQptHQGPfabGWtTCbxhNLSZKqbf1uzsup9V/go-ipfs-ds-help" - mockrouting "gx/ipfs/QmWLQyLU7yopJnwMvpHM5VSMG4xmbKgcq6P246mDy9xy5E/go-ipfs-routing/mock" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" + mockrouting "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/mock" testutil "gx/ipfs/QmcW4FGAt24fdK1jBgWQn3yP4R9ZLyWQqjozv9QK7epRhL/go-testutil" + dshelp "gx/ipfs/Qmd8UZEDddMaCnQ1G5eSrUhN3coX19V7SyXNQGWnAvUsnT/go-ipfs-ds-help" peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 19c5443b3..2809afd35 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -8,7 +8,7 @@ import ( path "github.com/ipfs/go-ipfs/path" - mockrouting "gx/ipfs/QmWLQyLU7yopJnwMvpHM5VSMG4xmbKgcq6P246mDy9xy5E/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/mock" testutil "gx/ipfs/QmcW4FGAt24fdK1jBgWQn3yP4R9ZLyWQqjozv9QK7epRhL/go-testutil" peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" diff --git a/namesys/routing.go b/namesys/routing.go index 0851aaa4f..4598898d2 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -8,11 +8,11 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" path "github.com/ipfs/go-ipfs/path" - dht "gx/ipfs/QmNg6M98bwS97SL9ArvrRxKujFps3eV6XvmKgduiYga8Bn/go-libp2p-kad-dht" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - routing "gx/ipfs/QmPpdpS9fknTBM3qHDcpayU6nYPZQeVjia2fbNrD8YWDe6/go-libp2p-routing" + dht "gx/ipfs/QmQYwRL1T9dJtdCScoeRQwwvScbJTcWqnXhq4dYQ6Cu5vX/go-libp2p-kad-dht" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + routing "gx/ipfs/QmZ383TySJVeZWzGnWui6pRcKyYZk9VkKTuW7tmKRWk5au/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" From 2719ed63ce66a637d543ac23d0befa4c50a46552 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 16 Jul 2018 15:16:49 -0700 Subject: [PATCH 2426/3817] update go-cid alternative to #5243 that updates go-cid and all packages that depend on it License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@db42708627dd1e4dc85a214c626212bc92ecea90 --- filestore/filestore.go | 8 ++++---- filestore/filestore_test.go | 6 +++--- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 0c80fb1d5..bdb893a5a 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,11 +11,11 @@ import ( "context" "errors" - blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" - posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" + posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" + blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" - blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 279a2bc82..dbd384cf1 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -9,9 +9,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" + blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" + posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 960fc93e8..34ddfdec9 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,12 +10,12 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - dshelp "gx/ipfs/QmNP2u7bofwUQptHQGPfabGWtTCbxhNLSZKqbf1uzsup9V/go-ipfs-ds-help" + blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" + posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" - blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format" - posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" + blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + dshelp "gx/ipfs/Qmd8UZEDddMaCnQ1G5eSrUhN3coX19V7SyXNQGWnAvUsnT/go-ipfs-ds-help" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsns "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/namespace" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" diff --git a/filestore/util.go b/filestore/util.go index 55a099859..3827e2ec5 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,9 +6,9 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - dshelp "gx/ipfs/QmNP2u7bofwUQptHQGPfabGWtTCbxhNLSZKqbf1uzsup9V/go-ipfs-ds-help" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" - blockstore "gx/ipfs/QmdpuJBPBZ6sLPj9BQpn3Rpi38BT2cF1QMiUfyzNWeySW4/go-ipfs-blockstore" + blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + dshelp "gx/ipfs/Qmd8UZEDddMaCnQ1G5eSrUhN3coX19V7SyXNQGWnAvUsnT/go-ipfs-ds-help" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) From e825caa2c4ddc242a12613cf6b22a56b72929c2b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 16 Jul 2018 15:16:49 -0700 Subject: [PATCH 2427/3817] update go-cid alternative to #5243 that updates go-cid and all packages that depend on it License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-verifcid@00af99ed88676bf0d3dd1c3dd74a13f24103712d --- verifcid/validate.go | 2 +- verifcid/validate_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/verifcid/validate.go b/verifcid/validate.go index cc4a761bc..6e4c80d61 100644 --- a/verifcid/validate.go +++ b/verifcid/validate.go @@ -4,7 +4,7 @@ import ( "fmt" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ) var ErrPossiblyInsecureHashFunction = fmt.Errorf("potentially insecure hash functions not allowed") diff --git a/verifcid/validate_test.go b/verifcid/validate_test.go index 740707593..a058f312c 100644 --- a/verifcid/validate_test.go +++ b/verifcid/validate_test.go @@ -5,7 +5,7 @@ import ( mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ) func TestValidateCids(t *testing.T) { From 3b1cf7aed3af88561e5b851f79d4b480c59d1ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 2 Feb 2018 16:00:37 +0100 Subject: [PATCH 2428/3817] coreapi: expand public path api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@ad6db2fc4cff3178bb50aacbe30559078661907b --- coreiface/coreapi.go | 13 +++++++++++++ coreiface/options/path.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 coreiface/options/path.go diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index a77ad6367..179531352 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -5,6 +5,9 @@ package iface import ( "context" + options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) @@ -37,4 +40,14 @@ type CoreAPI interface { // ResolveNode resolves the path (if not resolved already) using Unixfs // resolver, gets and returns the resolved Node ResolveNode(context.Context, Path) (ipld.Node, error) + + // ParsePath parses string path to a Path + ParsePath(context.Context, string, ...options.ParsePathOption) (Path, error) + + // WithResolve is an option for ParsePath which when set to true tells + // ParsePath to also resolve the path + WithResolve(bool) options.ParsePathOption + + // ParseCid creates new path from the provided CID + ParseCid(*cid.Cid) Path } diff --git a/coreiface/options/path.go b/coreiface/options/path.go new file mode 100644 index 000000000..bf6eed65b --- /dev/null +++ b/coreiface/options/path.go @@ -0,0 +1,30 @@ +package options + +type ParsePathSettings struct { + Resolve bool +} + +type ParsePathOption func(*ParsePathSettings) error + +func ParsePathOptions(opts ...ParsePathOption) (*ParsePathSettings, error) { + options := &ParsePathSettings{ + Resolve: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type ApiOptions struct{} + +func (api *ApiOptions) WithResolve(r bool) ParsePathOption { + return func(settings *ParsePathSettings) error { + settings.Resolve = r + return nil + } +} From 5a61226d830993d09c7d76d970b1c5a9de94f834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 8 Feb 2018 17:39:05 +0100 Subject: [PATCH 2429/3817] coreapi: separate path into two types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@79875b01300b77c8437ec0ca1b22d497edb79cd9 --- coreiface/block.go | 4 ++-- coreiface/coreapi.go | 14 ++++---------- coreiface/dag.go | 2 +- coreiface/object.go | 10 +++++----- coreiface/options/path.go | 30 ------------------------------ coreiface/path.go | 15 +++++++++++++-- coreiface/pin.go | 4 ++-- coreiface/unixfs.go | 2 +- 8 files changed, 28 insertions(+), 53 deletions(-) delete mode 100644 coreiface/options/path.go diff --git a/coreiface/block.go b/coreiface/block.go index a9e577d76..468c00947 100644 --- a/coreiface/block.go +++ b/coreiface/block.go @@ -13,13 +13,13 @@ type BlockStat interface { Size() int // Path returns path to the block - Path() Path + Path() ResolvedPath } // BlockAPI specifies the interface to the block layer type BlockAPI interface { // Put imports raw block data, hashing it using specified settings. - Put(context.Context, io.Reader, ...options.BlockPutOption) (Path, error) + Put(context.Context, io.Reader, ...options.BlockPutOption) (ResolvedPath, error) // Get attempts to resolve the path and return a reader for data in the block Get(context.Context, Path) (io.Reader, error) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index 179531352..615b039b3 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -5,10 +5,8 @@ package iface import ( "context" - options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // CoreAPI defines an unified interface to IPFS for Go programs @@ -35,19 +33,15 @@ type CoreAPI interface { Object() ObjectAPI // ResolvePath resolves the path using Unixfs resolver - ResolvePath(context.Context, Path) (Path, error) + ResolvePath(context.Context, Path) (ResolvedPath, error) // ResolveNode resolves the path (if not resolved already) using Unixfs // resolver, gets and returns the resolved Node ResolveNode(context.Context, Path) (ipld.Node, error) // ParsePath parses string path to a Path - ParsePath(context.Context, string, ...options.ParsePathOption) (Path, error) - - // WithResolve is an option for ParsePath which when set to true tells - // ParsePath to also resolve the path - WithResolve(bool) options.ParsePathOption + ParsePath(context.Context, string) (Path, error) // ParseCid creates new path from the provided CID - ParseCid(*cid.Cid) Path + ParseCid(*cid.Cid) ResolvedPath } diff --git a/coreiface/dag.go b/coreiface/dag.go index 158db7419..3f92ebab3 100644 --- a/coreiface/dag.go +++ b/coreiface/dag.go @@ -14,7 +14,7 @@ type DagAPI interface { // Put inserts data using specified format and input encoding. // Unless used with WithCodec or WithHash, the defaults "dag-cbor" and // "sha256" are used. - Put(ctx context.Context, src io.Reader, opts ...options.DagPutOption) (Path, error) + Put(ctx context.Context, src io.Reader, opts ...options.DagPutOption) (ResolvedPath, error) // Get attempts to resolve and get the node specified by the path Get(ctx context.Context, path Path) (ipld.Node, error) diff --git a/coreiface/object.go b/coreiface/object.go index a18a38ebe..ea9aa5948 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -38,7 +38,7 @@ type ObjectAPI interface { New(context.Context, ...options.ObjectNewOption) (ipld.Node, error) // Put imports the data into merkledag - Put(context.Context, io.Reader, ...options.ObjectPutOption) (Path, error) + Put(context.Context, io.Reader, ...options.ObjectPutOption) (ResolvedPath, error) // Get returns the node for the path Get(context.Context, Path) (ipld.Node, error) @@ -55,14 +55,14 @@ type ObjectAPI interface { // AddLink adds a link under the specified path. child path can point to a // subdirectory within the patent which must be present (can be overridden // with WithCreate option). - AddLink(ctx context.Context, base Path, name string, child Path, opts ...options.ObjectAddLinkOption) (Path, error) + AddLink(ctx context.Context, base Path, name string, child Path, opts ...options.ObjectAddLinkOption) (ResolvedPath, error) // RmLink removes a link from the node - RmLink(ctx context.Context, base Path, link string) (Path, error) + RmLink(ctx context.Context, base Path, link string) (ResolvedPath, error) // AppendData appends data to the node - AppendData(context.Context, Path, io.Reader) (Path, error) + AppendData(context.Context, Path, io.Reader) (ResolvedPath, error) // SetData sets the data contained in the node - SetData(context.Context, Path, io.Reader) (Path, error) + SetData(context.Context, Path, io.Reader) (ResolvedPath, error) } diff --git a/coreiface/options/path.go b/coreiface/options/path.go deleted file mode 100644 index bf6eed65b..000000000 --- a/coreiface/options/path.go +++ /dev/null @@ -1,30 +0,0 @@ -package options - -type ParsePathSettings struct { - Resolve bool -} - -type ParsePathOption func(*ParsePathSettings) error - -func ParsePathOptions(opts ...ParsePathOption) (*ParsePathSettings, error) { - options := &ParsePathSettings{ - Resolve: false, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - return options, nil -} - -type ApiOptions struct{} - -func (api *ApiOptions) WithResolve(r bool) ParsePathOption { - return func(settings *ParsePathSettings) error { - settings.Resolve = r - return nil - } -} diff --git a/coreiface/path.go b/coreiface/path.go index 51513772f..4cfd916de 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -6,13 +6,24 @@ import ( // Path is a generic wrapper for paths used in the API. A path can be resolved // to a CID using one of Resolve functions in the API. +// TODO: figure out/explain namespaces type Path interface { // String returns the path as a string. String() string + + // Namespace returns the first component of the path + Namespace() string +} + +// ResolvedPath is a resolved Path +type ResolvedPath interface { // Cid returns cid referred to by path Cid() *cid.Cid + // Root returns cid of root path Root() *cid.Cid - // Resolved returns whether path has been fully resolved - Resolved() bool + + //TODO: Path remainder + + Path } diff --git a/coreiface/pin.go b/coreiface/pin.go index 5994c7586..2e119cbea 100644 --- a/coreiface/pin.go +++ b/coreiface/pin.go @@ -9,7 +9,7 @@ import ( // Pin holds information about pinned resource type Pin interface { // Path to the pinned object - Path() Path + Path() ResolvedPath // Type of the pin Type() string @@ -27,7 +27,7 @@ type PinStatus interface { // BadPinNode is a node that has been marked as bad by Pin.Verify type BadPinNode interface { // Path is the path of the node - Path() Path + Path() ResolvedPath // Err is the reason why the node has been marked as bad Err() error diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index c59451d00..1ddc20674 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -10,7 +10,7 @@ import ( // UnixfsAPI is the basic interface to immutable files in IPFS type UnixfsAPI interface { // Add imports the data from the reader into merkledag file - Add(context.Context, io.Reader) (Path, error) + Add(context.Context, io.Reader) (ResolvedPath, error) // Cat returns a reader for the file Cat(context.Context, Path) (Reader, error) From 38520ef87802adf0c77aaece8cc9d203b65461d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 30 Mar 2018 22:21:57 +0200 Subject: [PATCH 2430/3817] coreapi: remove ctx from ParsePath, split ParseCid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@e4a333226c4cff7d76082700c14391133af6a7e5 --- coreiface/coreapi.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index 615b039b3..f5e81adce 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -40,8 +40,11 @@ type CoreAPI interface { ResolveNode(context.Context, Path) (ipld.Node, error) // ParsePath parses string path to a Path - ParsePath(context.Context, string) (Path, error) + ParsePath(string) (Path, error) - // ParseCid creates new path from the provided CID - ParseCid(*cid.Cid) ResolvedPath + // IpfsPath creates new /ipfs path from the provided CID + IpfsPath(*cid.Cid) ResolvedPath + + // IpldPath creates new /ipld path from the provided CID + IpldPath(*cid.Cid) ResolvedPath } From 620bec8b444b2c41107a2aa8a0ed39062be3b877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 30 Mar 2018 22:44:23 +0200 Subject: [PATCH 2431/3817] coreapi: path.Mutable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@bbff408d0ee7a756bce56956d0e72f0a3df6bbbc --- coreiface/path.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/coreiface/path.go b/coreiface/path.go index 4cfd916de..bb87a6b3b 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -6,13 +6,25 @@ import ( // Path is a generic wrapper for paths used in the API. A path can be resolved // to a CID using one of Resolve functions in the API. -// TODO: figure out/explain namespaces +// +// Paths must be prefixed with a valid prefix: +// +// * /ipfs - Immutable unixfs path (files) +// * /ipld - Immutable ipld path (data) +// * /ipns - Mutable names. Usually resolves to one of the immutable paths +//TODO: /local (MFS) type Path interface { // String returns the path as a string. String() string // Namespace returns the first component of the path Namespace() string + + // Mutable returns false if the data pointed to by this path in guaranteed + // to not change. + // + // Note that resolved mutable path can be immutable. + Mutable() bool } // ResolvedPath is a resolved Path From fcfaea81b0b6d78930617e2aea8f67935feb8fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 8 Feb 2018 17:39:05 +0100 Subject: [PATCH 2432/3817] coreapi: separate path into two types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-path@4a6bdd829f772fd51052d778aecc7865cd89938e --- path/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/path.go b/path/path.go index 5622a6d05..cc0ec31b2 100644 --- a/path/path.go +++ b/path/path.go @@ -120,7 +120,7 @@ func ParsePath(txt string) (Path, error) { if _, err := ParseCidToPath(parts[2]); err != nil { return "", err } - } else if parts[1] != "ipns" { + } else if parts[1] != "ipns" && parts[1] != "ipld" { //TODO: make this smarter return "", ErrBadPath } From d0da662cf78f21d6e3d170b11e33da14444f838b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 3 Apr 2018 14:49:33 +0200 Subject: [PATCH 2433/3817] coreapi: path remainders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@a6d0272a575722dbaef8c21c869e7625692fe7f6 --- coreiface/path.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/coreiface/path.go b/coreiface/path.go index bb87a6b3b..b4a9f0dbd 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -29,13 +29,14 @@ type Path interface { // ResolvedPath is a resolved Path type ResolvedPath interface { - // Cid returns cid referred to by path + // Cid returns the CID referred to by path Cid() *cid.Cid - // Root returns cid of root path + // Root returns the CID of root path Root() *cid.Cid - //TODO: Path remainder + // Remainder returns unresolved part of the path + Remainder() string Path } From 6f777edd18d4e9b990b6c449fa1b79a53acef7fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 3 Apr 2018 14:49:33 +0200 Subject: [PATCH 2434/3817] coreapi: path remainders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-path@e0565d2f57ad539632d63f422d657c5ddd6d1c2a --- path/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/path.go b/path/path.go index cc0ec31b2..1c2bd2fe6 100644 --- a/path/path.go +++ b/path/path.go @@ -161,7 +161,7 @@ func SplitList(pth string) []string { // must be a Multihash) and return it separately. func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) { parts := fpath.Segments() - if parts[0] == "ipfs" { + if parts[0] == "ipfs" || parts[0] == "ipld" { parts = parts[1:] } From d1d67409e744a2a697a3c05de0eb34b0bfbed175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 20 Apr 2018 13:56:33 +0200 Subject: [PATCH 2435/3817] coreapi: add more docs for path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@5abaad9e8573557e0bd7f3b1b45838053a85ffd7 --- coreiface/path.go | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/coreiface/path.go b/coreiface/path.go index b4a9f0dbd..d233afae5 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -17,7 +17,9 @@ type Path interface { // String returns the path as a string. String() string - // Namespace returns the first component of the path + // Namespace returns the first component of the path. + // + // For example path "/ipfs/QmHash", calling Namespace() will return "ipfs" Namespace() string // Mutable returns false if the data pointed to by this path in guaranteed @@ -29,13 +31,29 @@ type Path interface { // ResolvedPath is a resolved Path type ResolvedPath interface { - // Cid returns the CID referred to by path + // Cid returns the CID of the object referenced by the path. + // + // Example: + // If you have 3 linked objects: QmRoot -> A -> B, and resolve path + // "/ipfs/QmRoot/A/B", the Cid method will return the CID of object B Cid() *cid.Cid - // Root returns the CID of root path + // Root returns the CID of the root object of the path + // + // Example: + // If you have 3 linked objects: QmRoot -> A -> B, and resolve path + // "/ipfs/QmRoot/A/B", the Root method will return the CID of object QmRoot Root() *cid.Cid // Remainder returns unresolved part of the path + // + // Example: + // If you have 2 linked objects: QmRoot -> A, where A is a CBOR node + // containing the following data: + // + // {"foo": {"bar": 42}} + // + // When resolving "/ipld/QmRoot/A/foo/bar", Remainder will return "foo/bar" Remainder() string Path From 26bcf6458dc875f4f89137e10a3c42efe14ecd18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 20 Apr 2018 13:31:05 +0200 Subject: [PATCH 2436/3817] coreapi: path review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-path@16bf087f856eda0e5ea85e871cc76d5ab47b8bb3 --- path/resolver/resolver.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 8c7b28b1c..05341655a 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -34,6 +34,9 @@ func (e ErrNoLink) Error() string { return fmt.Sprintf("no link named %q under %s", e.Name, e.Node.String()) } +// ResolveOnce resolves path through a single node +type ResolveOnce func(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, names []string) (*ipld.Link, []string, error) + // Resolver provides path resolution to IPFS // It has a pointer to a DAGService, which is uses to resolve nodes. // TODO: now that this is more modular, try to unify this code with the @@ -41,7 +44,7 @@ func (e ErrNoLink) Error() string { type Resolver struct { DAG ipld.NodeGetter - ResolveOnce func(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, names []string) (*ipld.Link, []string, error) + ResolveOnce ResolveOnce } // NewBasicResolver constructs a new basic resolver. From 88c3a9e6cc05757813f6a3266244543529c58cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 12 Jun 2018 02:57:57 +0200 Subject: [PATCH 2437/3817] coreapi: more docs for ResolvedPath MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@1806f0f94a444c88f0c842e03a454619e6d42e56 --- coreiface/path.go | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/coreiface/path.go b/coreiface/path.go index d233afae5..acdcd85da 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -29,13 +29,38 @@ type Path interface { Mutable() bool } -// ResolvedPath is a resolved Path +// ResolvedPath is a path which was resolved to the last resolvable node type ResolvedPath interface { - // Cid returns the CID of the object referenced by the path. + // Cid returns the CID of the node referenced by the path. Remainder of the + // path is guaranteed to be within the node. // - // Example: - // If you have 3 linked objects: QmRoot -> A -> B, and resolve path - // "/ipfs/QmRoot/A/B", the Cid method will return the CID of object B + // Examples: + // If you have 3 linked objects: QmRoot -> A -> B: + // + // cidB := {"foo": {"bar": 42 }} + // cidA := {"B": {"/": cidB }} + // cidRoot := {"A": {"/": cidA }} + // + // And resolve paths: + // * "/ipfs/${cidRoot}" + // * Calling Cid() will return `cidRoot` + // * Calling Root() will return `cidRoot` + // * Calling Remainder() will return `` + // + // * "/ipfs/${cidRoot}/A" + // * Calling Cid() will return `cidA` + // * Calling Root() will return `cidRoot` + // * Calling Remainder() will return `` + // + // * "/ipfs/${cidRoot}/A/B/foo" + // * Calling Cid() will return `cidB` + // * Calling Root() will return `cidRoot` + // * Calling Remainder() will return `foo` + // + // * "/ipfs/${cidRoot}/A/B/foo/bar" + // * Calling Cid() will return `cidB` + // * Calling Root() will return `cidRoot` + // * Calling Remainder() will return `foo/bar` Cid() *cid.Cid // Root returns the CID of the root object of the path @@ -43,6 +68,8 @@ type ResolvedPath interface { // Example: // If you have 3 linked objects: QmRoot -> A -> B, and resolve path // "/ipfs/QmRoot/A/B", the Root method will return the CID of object QmRoot + // + // For more examples see the documentation of Cid() method Root() *cid.Cid // Remainder returns unresolved part of the path @@ -51,9 +78,11 @@ type ResolvedPath interface { // If you have 2 linked objects: QmRoot -> A, where A is a CBOR node // containing the following data: // - // {"foo": {"bar": 42}} + // {"foo": {"bar": 42 }} // // When resolving "/ipld/QmRoot/A/foo/bar", Remainder will return "foo/bar" + // + // For more examples see the documentation of Cid() method Remainder() string Path From dad6a0d08af3a2bbca414a1ea450f760f65831f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 24 Apr 2018 02:11:46 +0200 Subject: [PATCH 2438/3817] path: add tests for ipld paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-path@eb2707ce812eb362b9b1942c3ca8bd6ca06c154c --- path/path.go | 5 +++-- path/path_test.go | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/path/path.go b/path/path.go index 1c2bd2fe6..1608b6ca3 100644 --- a/path/path.go +++ b/path/path.go @@ -59,10 +59,11 @@ func (p Path) String() string { return string(p) } -// IsJustAKey returns true if the path is of the form or /ipfs/. +// IsJustAKey returns true if the path is of the form or /ipfs/, or +// /ipld/ func (p Path) IsJustAKey() bool { parts := p.Segments() - return len(parts) == 2 && parts[0] == "ipfs" + return len(parts) == 2 && (parts[0] == "ipfs" || parts[0] == "ipld") } // PopLastSegment returns a new Path without its final segment, and the final diff --git a/path/path_test.go b/path/path_test.go index b095ffd98..db28193c8 100644 --- a/path/path_test.go +++ b/path/path_test.go @@ -9,6 +9,9 @@ func TestPathParsing(t *testing.T) { "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true, "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": true, "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b/c/d/e/f": true, + "/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true, + "/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": true, + "/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b/c/d/e/f": true, "/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b/c/d/e/f": true, "/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true, "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b/c/d/e/f": true, @@ -36,6 +39,8 @@ func TestIsJustAKey(t *testing.T) { "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": false, "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b": false, "/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": false, + "/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b": false, + "/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true, } for p, expected := range cases { @@ -57,6 +62,7 @@ func TestPopLastSegment(t *testing.T) { "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": []string{"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n", "a"}, "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b": []string{"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a", "b"}, "/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y/z": []string{"/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y", "z"}, + "/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y/z": []string{"/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y", "z"}, } for p, expected := range cases { From 92ebd6eb7be417dec28e3c8fbc0f0bb4db65d268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 12 Jun 2018 03:52:26 +0200 Subject: [PATCH 2439/3817] coreapi: move path utils to interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@0a0e69cc424e6136e8f0a1fecabda94e7854bc7f --- coreiface/coreapi.go | 10 ----- coreiface/path.go | 87 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 10 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index f5e81adce..82a2ebf4e 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -6,7 +6,6 @@ import ( "context" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" - cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid" ) // CoreAPI defines an unified interface to IPFS for Go programs @@ -38,13 +37,4 @@ type CoreAPI interface { // ResolveNode resolves the path (if not resolved already) using Unixfs // resolver, gets and returns the resolved Node ResolveNode(context.Context, Path) (ipld.Node, error) - - // ParsePath parses string path to a Path - ParsePath(string) (Path, error) - - // IpfsPath creates new /ipfs path from the provided CID - IpfsPath(*cid.Cid) ResolvedPath - - // IpldPath creates new /ipld path from the provided CID - IpldPath(*cid.Cid) ResolvedPath } diff --git a/coreiface/path.go b/coreiface/path.go index acdcd85da..e097ea3b6 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,9 +1,13 @@ package iface import ( + ipfspath "github.com/ipfs/go-ipfs/path" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ) +//TODO: merge with ipfspath so we don't depend on it + // Path is a generic wrapper for paths used in the API. A path can be resolved // to a CID using one of Resolve functions in the API. // @@ -87,3 +91,86 @@ type ResolvedPath interface { Path } + +// path implements coreiface.Path +type path struct { + path ipfspath.Path +} + +// resolvedPath implements coreiface.resolvedPath +type resolvedPath struct { + path + cid *cid.Cid + root *cid.Cid + remainder string +} + +// IpfsPath creates new /ipfs path from the provided CID +func IpfsPath(c *cid.Cid) ResolvedPath { + return &resolvedPath{ + path: path{ipfspath.Path("/ipfs/" + c.String())}, + cid: c, + root: c, + remainder: "", + } +} + +// IpldPath creates new /ipld path from the provided CID +func IpldPath(c *cid.Cid) ResolvedPath { + return &resolvedPath{ + path: path{ipfspath.Path("/ipld/" + c.String())}, + cid: c, + root: c, + remainder: "", + } +} + +// ParsePath parses string path to a Path +func ParsePath(p string) (Path, error) { + pp, err := ipfspath.ParsePath(p) + if err != nil { + return nil, err + } + + return &path{path: pp}, nil +} + +// NewResolvedPath creates new ResolvedPath. This function performs no checks +// and is intended to be used by resolver implementations. Incorrect inputs may +// cause panics. Handle with care. +func NewResolvedPath(ipath ipfspath.Path, c *cid.Cid, root *cid.Cid, remainder string) ResolvedPath { + return &resolvedPath{ + path: path{ipath}, + cid: c, + root: root, + remainder: remainder, + } +} + +func (p *path) String() string { + return p.path.String() +} + +func (p *path) Namespace() string { + if len(p.path.Segments()) < 1 { + panic("path without namespace") //this shouldn't happen under any scenario + } + return p.path.Segments()[0] +} + +func (p *path) Mutable() bool { + //TODO: MFS: check for /local + return p.Namespace() == "ipns" +} + +func (p *resolvedPath) Cid() *cid.Cid { + return p.cid +} + +func (p *resolvedPath) Root() *cid.Cid { + return p.root +} + +func (p *resolvedPath) Remainder() string { + return p.remainder +} From e1fc0b91f50a5e707119bf5ca1ef71cbd452b160 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 21 Dec 2017 17:33:27 -0800 Subject: [PATCH 2440/3817] fix truncating when already at the correct size fixes #4518 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-unixfs@c543a5808437fbff3d727f648dafd294f02d477b --- unixfs/mod/dagmodifier.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 0a1ae0a96..28de7228d 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -481,6 +481,9 @@ func (dm *DagModifier) Truncate(size int64) error { if err != nil { return err } + if size == int64(realSize) { + return nil + } // Truncate can also be used to expand the file if size > int64(realSize) { From 1a7b5666f6d9d3a5584b4f7b998396d6049bb801 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 21 Dec 2017 17:33:27 -0800 Subject: [PATCH 2441/3817] fix truncating when already at the correct size fixes #4518 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@ba1bf6d729479d44ff26e9ed476dcf4372f1ab32 --- mfs/mfs_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 228c5bdc0..42a87fde4 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -1111,3 +1111,28 @@ func TestFileDescriptors(t *testing.T) { t.Fatal(err) } } + +func TestTruncateAtSize(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ds, rt := setupRoot(ctx, t) + + dir := rt.GetDirectory() + + nd := dag.NodeWithData(ft.FilePBData(nil, 0)) + fi, err := NewFile("test", nd, dir, ds) + if err != nil { + t.Fatal(err) + } + + fd, err := fi.Open(OpenReadWrite, true) + if err != nil { + t.Fatal(err) + } + defer fd.Close() + _, err = fd.Write([]byte("test")) + if err != nil { + t.Fatal(err) + } + fd.Truncate(4) +} From 3de8b168f7d29d364b48cb0f90d8365c2acf040e Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 11 Jul 2018 12:24:37 -0300 Subject: [PATCH 2442/3817] unixfs: fix `dagTruncate` to preserve node type Extract the original `FSNode` passed inside the `ipld.Node` argument and modify its `Blocksizes` (removing all of them and re-adding the ones that were not truncated). In contrast, the replaced code was creating a new `FSNode` that was not preserving some of the features of the original one. Change `TRUNC_HASH` values in `sharness` that were created with the bug to the correct values. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-unixfs@8e9a367f3200229523ddb08436a97226ad7705bb --- unixfs/mod/dagmodifier.go | 12 +++++++++--- unixfs/unixfs.go | 6 ++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 28de7228d..f8aa5ce63 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -529,7 +529,13 @@ func dagTruncate(ctx context.Context, n ipld.Node, size uint64, ds ipld.DAGServi var cur uint64 end := 0 var modified ipld.Node - ndata := ft.NewFSNode(ft.TRaw) + ndata, err := ft.FSNodeFromBytes(nd.Data()) + if err != nil { + return nil, err + } + // Reset the block sizes of the node to adjust them + // with the new values of the truncated children. + ndata.RemoveAllBlockSizes() for i, lnk := range nd.Links() { child, err := lnk.GetNode(ctx, ds) if err != nil { @@ -558,7 +564,7 @@ func dagTruncate(ctx context.Context, n ipld.Node, size uint64, ds ipld.DAGServi ndata.AddBlockSize(childsize) } - err := ds.Add(ctx, modified) + err = ds.Add(ctx, modified) if err != nil { return nil, err } @@ -573,7 +579,7 @@ func dagTruncate(ctx context.Context, n ipld.Node, size uint64, ds ipld.DAGServi if err != nil { return nil, err } - + // Save the new block sizes to the original node. nd.SetData(d) // invalidate cache and recompute serialized data diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index 9cd9731ed..d048c422b 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -201,6 +201,12 @@ func (n *FSNode) BlockSize(i int) uint64 { return n.format.Blocksizes[i] } +// RemoveAllBlockSizes removes all the child block sizes of this node. +func (n *FSNode) RemoveAllBlockSizes() { + n.format.Blocksizes = []uint64{} + n.format.Filesize = proto.Uint64(uint64(len(n.Data()))) +} + // GetBytes marshals this node as a protobuf message. func (n *FSNode) GetBytes() ([]byte, error) { return proto.Marshal(&n.format) From a5d1ddc7ff9a84593afb7b315e09722c70a40c09 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 11 Jul 2018 12:36:10 -0300 Subject: [PATCH 2443/3817] mfs: add test case for MFS repeated truncation failure License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-mfs@81936112ddd623a9721ac756a06790902010a631 --- mfs/mfs_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 42a87fde4..c91d77168 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -1136,3 +1136,43 @@ func TestTruncateAtSize(t *testing.T) { } fd.Truncate(4) } + +func TestTruncateAndWrite(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ds, rt := setupRoot(ctx, t) + + dir := rt.GetDirectory() + + nd := dag.NodeWithData(ft.FilePBData(nil, 0)) + fi, err := NewFile("test", nd, dir, ds) + if err != nil { + t.Fatal(err) + } + + fd, err := fi.Open(OpenReadWrite, true) + defer fd.Close() + if err != nil { + t.Fatal(err) + } + for i := 0; i < 200; i++ { + err = fd.Truncate(0) + if err != nil { + t.Fatal(err) + } + l, err := fd.Write([]byte("test")) + if err != nil { + t.Fatal(err) + } + if l != len("test") { + t.Fatal("incorrect write length") + } + data, err := ioutil.ReadAll(fd) + if err != nil { + t.Fatal(err) + } + if string(data) != "test" { + t.Errorf("read error at read %d, read: %v", i, data) + } + } +} From 705e48b6ce9277e2c460f6f52b5c2f1797fac4d8 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 18 Jul 2018 11:07:52 -0300 Subject: [PATCH 2444/3817] unixfs/mod: add test to `Truncate` to the same size License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-unixfs@999c66862f8edded0d2d8698fd9150db31f255a2 --- unixfs/mod/dagmodifier_test.go | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 92e4ce2d6..0a5c5a74f 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -406,6 +406,44 @@ func testDagTruncate(t *testing.T, opts testu.NodeOpts) { } } +// TestDagTruncateSameSize tests that a DAG truncated +// to the same size (i.e., doing nothing) doesn't modify +// the DAG (its hash). +func TestDagTruncateSameSize(t *testing.T) { + runAllSubtests(t, testDagTruncateSameSize) +} +func testDagTruncateSameSize(t *testing.T, opts testu.NodeOpts) { + dserv := testu.GetDAGServ() + _, n := testu.GetRandomNode(t, dserv, 50000, opts) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, n, dserv, testu.SizeSplitterGen(512)) + if err != nil { + t.Fatal(err) + } + // Copied from `TestDagTruncate`. + + size, err := dagmod.Size() + if err != nil { + t.Fatal(err) + } + + err = dagmod.Truncate(size) + if err != nil { + t.Fatal(err) + } + + modifiedNode, err := dagmod.GetNode() + if err != nil { + t.Fatal(err) + } + + if modifiedNode.Cid().Equals(n.Cid()) == false { + t.Fatal("the node has been modified!") + } +} + func TestSparseWrite(t *testing.T) { runAllSubtests(t, testSparseWrite) } From 6fe2ecdbdb06e38cb66705da1fc60970463dcfc2 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Thu, 19 Jul 2018 00:18:00 -0300 Subject: [PATCH 2445/3817] mfs: seek to 0 before reading in `TestTruncateAndWrite` License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-mfs@44c3f289034d6aa6b3391a747a8e04d6b284c06a --- mfs/mfs_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index c91d77168..6950d927a 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -1167,12 +1167,18 @@ func TestTruncateAndWrite(t *testing.T) { if l != len("test") { t.Fatal("incorrect write length") } + + _, err = fd.Seek(0, io.SeekStart) + if err != nil { + t.Fatal(err) + } + data, err := ioutil.ReadAll(fd) if err != nil { t.Fatal(err) } if string(data) != "test" { - t.Errorf("read error at read %d, read: %v", i, data) + t.Fatalf("read error at read %d, read: %v", i, data) } } } From 3c2847dd5407669207a145493cb8020486bbe5c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 21 Jul 2018 14:58:16 +0200 Subject: [PATCH 2446/3817] Fix resolving links in sharded directories on gateway MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-path@cfe5d61c926324e0e06f7e452c5268e87e180e2e --- path/resolver/resolver.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 05341655a..2aa247645 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -69,19 +69,25 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (ipld } for len(p) > 0 { - val, rest, err := nd.Resolve(p) + lnk, rest, err := r.ResolveOnce(ctx, r.DAG, nd, p) if err != nil { return nil, nil, err } - switch val := val.(type) { - case *ipld.Link: - next, err := val.GetNode(ctx, r.DAG) + if lnk != nil { + next, err := lnk.GetNode(ctx, r.DAG) if err != nil { return nil, nil, err } nd = next p = rest + continue + } + + val, rest, err := nd.Resolve(p) + switch val.(type) { + case *ipld.Link: + return nil, nil, errors.New("inconsistent ResolveOnce / nd.Resolve") default: return nd, p, nil } From f78c2183b20eb2363faf63ee542615c90271e0ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 21 Jul 2018 17:29:18 +0200 Subject: [PATCH 2447/3817] path: fix dag tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-path@53c234382a1591917281ad7167bf775192ab4607 --- path/resolver/resolver.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 2aa247645..e7a95a83e 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -70,11 +70,11 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (ipld for len(p) > 0 { lnk, rest, err := r.ResolveOnce(ctx, r.DAG, nd, p) - if err != nil { - return nil, nil, err - } - if lnk != nil { + if err != nil { + return nil, nil, err + } + next, err := lnk.GetNode(ctx, r.DAG) if err != nil { return nil, nil, err @@ -85,6 +85,9 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (ipld } val, rest, err := nd.Resolve(p) + if err != nil { + return nil, nil, err + } switch val.(type) { case *ipld.Link: return nil, nil, errors.New("inconsistent ResolveOnce / nd.Resolve") From f79cb379904debf7f4f1600e5503d8fd0638041b Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Sun, 22 Jul 2018 23:02:19 -0300 Subject: [PATCH 2448/3817] test: testLargeWriteChunks: seek before reading License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-unixfs@07e655d8d25637a2bbd700486baa7977628dd529 --- unixfs/mod/dagmodifier_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 0a5c5a74f..3e460aec5 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -323,6 +323,11 @@ func testLargeWriteChunks(t *testing.T, opts testu.NodeOpts) { } } + _, err = dagmod.Seek(0, io.SeekStart) + if err != nil { + t.Fatal(err) + } + out, err := ioutil.ReadAll(dagmod) if err != nil { t.Fatal(err) From dd26bd965008263ee60b4bc4690ad56d5ab98213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 23 Jul 2018 17:37:33 +0200 Subject: [PATCH 2449/3817] path: simplify ResolveToLastNode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-path@797c0c4f99cf6e81d18aa2f3fd144307ee6c24ec --- path/resolver/resolver.go | 46 +++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index e7a95a83e..7bd8caff9 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -70,33 +70,41 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (ipld for len(p) > 0 { lnk, rest, err := r.ResolveOnce(ctx, r.DAG, nd, p) - if lnk != nil { - if err != nil { - return nil, nil, err - } - - next, err := lnk.GetNode(ctx, r.DAG) - if err != nil { - return nil, nil, err - } - nd = next - p = rest - continue + if lnk == nil { + break } - val, rest, err := nd.Resolve(p) if err != nil { return nil, nil, err } - switch val.(type) { - case *ipld.Link: - return nil, nil, errors.New("inconsistent ResolveOnce / nd.Resolve") - default: - return nd, p, nil + + next, err := lnk.GetNode(ctx, r.DAG) + if err != nil { + return nil, nil, err } + nd = next + p = rest } - return nd, nil, nil + if len(p) == 0 { + return nd, nil, nil + } + + // Confirm the path exists within the object + val, rest, err := nd.Resolve(p) + if err != nil { + return nil, nil, err + } + + if len(rest) > 0 { + return nil, nil, errors.New("path failed to resolve fully") + } + switch val.(type) { + case *ipld.Link: + return nil, nil, errors.New("inconsistent ResolveOnce / nd.Resolve") + default: + return nd, p, nil + } } // ResolvePath fetches the node for given path. It returns the last item From 2ba993c8367e25823638587c2a12aebcd7547710 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 23 Jul 2018 11:17:49 -0700 Subject: [PATCH 2450/3817] use ipfs/bbloom gxed is for gxed packages, not full forks This commit was moved from ipfs/go-ipfs-blockstore@480752aa6678f6023e0c5f6633b110a87fbb774f --- blockstore/bloom_cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 7dd0bbe9f..927ad1204 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -5,7 +5,7 @@ import ( "sync/atomic" "time" - bloom "github.com/gxed/bbloom" + bloom "github.com/ipfs/bbloom" blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" metrics "github.com/ipfs/go-metrics-interface" From ddd029560a8e3061cffb465ed9789e6cd14438f3 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 20 Jul 2018 21:07:58 -0700 Subject: [PATCH 2451/3817] gx update deps Updates: * go-net * go-text * dns * prometheus * protobuf (golang, not gogo) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@ba23147771e5b9769f0ff077d9f29892c40ae929 --- filestore/filestore.go | 2 +- filestore/filestore_test.go | 2 +- filestore/fsrefstore.go | 4 ++-- filestore/util.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index bdb893a5a..61562cd12 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,10 +11,10 @@ import ( "context" "errors" - blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index dbd384cf1..de7cf87dd 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -9,9 +9,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" - blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 34ddfdec9..1ea0f359c 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,11 +10,11 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" - proto "gx/ipfs/QmT6n4mspWYEya864BhCUJEgyxiRfmiSY9ruQwTUNpRKaM/protobuf/proto" blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + proto "gx/ipfs/QmZHU2gx42NPTYXzw6pJkuX6xCE7bKECp6e8QcPdoLx8sx/protobuf/proto" + blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" dshelp "gx/ipfs/Qmd8UZEDddMaCnQ1G5eSrUhN3coX19V7SyXNQGWnAvUsnT/go-ipfs-ds-help" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsns "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/namespace" diff --git a/filestore/util.go b/filestore/util.go index 3827e2ec5..96acd2852 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,8 +6,8 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" dshelp "gx/ipfs/Qmd8UZEDddMaCnQ1G5eSrUhN3coX19V7SyXNQGWnAvUsnT/go-ipfs-ds-help" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" From 01af65a16805c18036a3c2918d6ab1835d0f4e0b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 20 Jul 2018 21:07:58 -0700 Subject: [PATCH 2452/3817] gx update deps Updates: * go-net * go-text * dns * prometheus * protobuf (golang, not gogo) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-merkledag@0fa5f2ed6868b6e287789e015282941f48e14eb5 --- ipld/merkledag/merkledag_test.go | 2 +- ipld/merkledag/test/utils.go | 4 ++-- ipld/merkledag/utils/utils.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index b26be3d9e..1153b6989 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -20,7 +20,7 @@ import ( dstest "github.com/ipfs/go-ipfs/merkledag/test" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index d38a9d75c..c161f7c01 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -4,9 +4,9 @@ import ( bsrv "github.com/ipfs/go-ipfs/blockservice" dag "github.com/ipfs/go-ipfs/merkledag" - blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" - offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go index 9a3e667d5..fbd18c725 100644 --- a/ipld/merkledag/utils/utils.go +++ b/ipld/merkledag/utils/utils.go @@ -8,9 +8,9 @@ import ( dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" - bstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" - offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + bstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" syncds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) From 6972bc843e553428e4531c331c24400cf9b091f4 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 20 Jul 2018 21:07:58 -0700 Subject: [PATCH 2453/3817] gx update deps Updates: * go-net * go-text * dns * prometheus * protobuf (golang, not gogo) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-blockservice@f18ba427ca96f0db93d1be8dd47017ac3fe2378b --- blockservice/blockservice.go | 2 +- blockservice/blockservice_test.go | 4 ++-- blockservice/test/blocks_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 6a1e5ee05..c4502c975 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -11,9 +11,9 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/verifcid" - blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" exchange "gx/ipfs/Qmc2faLf7URkHpsbfYM4EMbr8iSAcGAe8VPgVi64HVnwji/go-ipfs-exchange-interface" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" ) diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index e9fcfe2be..e94f4a3fe 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -3,10 +3,10 @@ package blockservice import ( "testing" - blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" - offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" butil "gx/ipfs/QmYqPGpZ9Yemr55xus9DiEztkns6Jti5XJ7hC94JbvkdqZ/go-ipfs-blocksutil" + blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 162d573a0..19a2e54f0 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -10,10 +10,10 @@ import ( . "github.com/ipfs/go-ipfs/blockservice" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" - offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) From 1a9ed1ec26a7fcd8341e94b448c343517173abcd Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 20 Jul 2018 21:07:58 -0700 Subject: [PATCH 2454/3817] gx update deps Updates: * go-net * go-text * dns * prometheus * protobuf (golang, not gogo) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@c74ffe1726e8d3125b987554f1b0a877de4614e1 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set_test.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 5c4ce2dee..e01321c65 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -12,10 +12,10 @@ import ( pin "github.com/ipfs/go-ipfs/pin" "github.com/ipfs/go-ipfs/thirdparty/verifcid" - bstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" - offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + bstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" dstore "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index dfd5f31d4..f832e0a72 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -9,9 +9,9 @@ import ( mdag "github.com/ipfs/go-ipfs/merkledag" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" - offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 1dbc82092..b2fe149a4 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -8,9 +8,9 @@ import ( bserv "github.com/ipfs/go-ipfs/blockservice" dag "github.com/ipfs/go-ipfs/merkledag" - blockstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" - offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) From 37d0438b958107cd5d4823d9c70ca78b889c80e8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 20 Jul 2018 21:07:58 -0700 Subject: [PATCH 2455/3817] gx update deps Updates: * go-net * go-text * dns * prometheus * protobuf (golang, not gogo) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@d482cfb062eef3ad7cab264cae5ab8603e4bf08f --- mfs/mfs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 6950d927a..d2f22bcd4 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -22,11 +22,11 @@ import ( uio "github.com/ipfs/go-ipfs/unixfs/io" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - bstore "gx/ipfs/QmRatnbGjPcoyzVjfixMZnuT1xQbjM7FgnL6FX4CKJeDE2/go-ipfs-blockstore" - offline "gx/ipfs/QmShbyKV9P7QuFecDHXsgrQ4rxxm71MUkGVpwedT4VQ8Bf/go-ipfs-exchange-offline" + offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + bstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) From 2cd26284a6dbb2677dadbf12b1c55ef5d23dd481 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 20 Jul 2018 21:07:58 -0700 Subject: [PATCH 2456/3817] gx update deps Updates: * go-net * go-text * dns * prometheus * protobuf (golang, not gogo) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@fb4197fd6a50c341b5a225a8e44db0c49300d267 --- namesys/republisher/repub_test.go | 2 +- namesys/routing.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 623a1b1e0..d26cccb66 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -13,7 +13,7 @@ import ( path "github.com/ipfs/go-ipfs/path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmZ86eLPtXkQ1Dfa992Q8NpXArUoWWh3y728JDcWvzRrvC/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmY51bqSM5XgxQZqsBrQcRkKTnCb8EKpJpR9K6Qax7Njco/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmZR2XWVVBCtbgBWnQhWk2xcQfaR3W8faQPriAiaaj7rsr/go-libp2p-peerstore" ) diff --git a/namesys/routing.go b/namesys/routing.go index 4598898d2..4204ddcde 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -9,7 +9,7 @@ import ( path "github.com/ipfs/go-ipfs/path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - dht "gx/ipfs/QmQYwRL1T9dJtdCScoeRQwwvScbJTcWqnXhq4dYQ6Cu5vX/go-libp2p-kad-dht" + dht "gx/ipfs/QmTktQYCKzQjhxF6dk5xJPRuhHn3JBiKGvMLoiDy1mYmxC/go-libp2p-kad-dht" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" routing "gx/ipfs/QmZ383TySJVeZWzGnWui6pRcKyYZk9VkKTuW7tmKRWk5au/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" From 9b211137457cf4fd72846380bcde7990a8456c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 23 Jul 2018 21:29:09 +0200 Subject: [PATCH 2457/3817] path: add a comment on dropping error in ResolveToLastNode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-path@55d55685ab606642e44b3f5c21ab1677532bd104 --- path/resolver/resolver.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 7bd8caff9..af6b8628c 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -70,6 +70,10 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (ipld for len(p) > 0 { lnk, rest, err := r.ResolveOnce(ctx, r.DAG, nd, p) + + // Note: have to drop the error here as `ResolveOnce` doesn't handle 'leaf' + // paths (so e.g. for `echo '{"foo":123}' | ipfs dag put` we wouldn't be + // able to resolve `zdpu[...]/foo`) if lnk == nil { break } From 63954a0cb8837c2a523dfd39c48d0def4d0e70d9 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 25 Jul 2018 14:51:49 -0400 Subject: [PATCH 2458/3817] urlstore: Accept "200 OK" in addition to "206 Partial Content". Some servers seem to return 200 OK when range header covers entire file. If the content is wrong we will detect later so there is no harm in accepting either response. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@e0c9aaba529ad36279d108c7c04cf7cc340191c2 --- filestore/fsrefstore.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 34ddfdec9..82922450d 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -216,9 +216,9 @@ func (f *FileManager) readURLDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) if err != nil { return nil, &CorruptReferenceError{StatusFileError, err} } - if res.StatusCode != http.StatusPartialContent { + if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusPartialContent { return nil, &CorruptReferenceError{StatusFileError, - fmt.Errorf("expected HTTP 206 got %d", res.StatusCode)} + fmt.Errorf("expected HTTP 200 or 206 got %d", res.StatusCode)} } outbuf := make([]byte, d.GetSize_()) From e55970df316d4f323640676e2b7e00fe90df9738 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 27 Jul 2018 13:12:13 -0700 Subject: [PATCH 2459/3817] move dagutils package to top level in preparation for merkledag extraction License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-merkledag@692a352018c7c38c9d74a9e5abf7bfe6e602fbd5 --- ipld/merkledag/utils/diff.go | 210 ---------------------- ipld/merkledag/utils/diffenum.go | 99 ---------- ipld/merkledag/utils/diffenum_test.go | 249 -------------------------- ipld/merkledag/utils/utils.go | 234 ------------------------ ipld/merkledag/utils/utils_test.go | 114 ------------ 5 files changed, 906 deletions(-) delete mode 100644 ipld/merkledag/utils/diff.go delete mode 100644 ipld/merkledag/utils/diffenum.go delete mode 100644 ipld/merkledag/utils/diffenum_test.go delete mode 100644 ipld/merkledag/utils/utils.go delete mode 100644 ipld/merkledag/utils/utils_test.go diff --git a/ipld/merkledag/utils/diff.go b/ipld/merkledag/utils/diff.go deleted file mode 100644 index c06f50c5e..000000000 --- a/ipld/merkledag/utils/diff.go +++ /dev/null @@ -1,210 +0,0 @@ -package dagutils - -import ( - "context" - "fmt" - "path" - - dag "github.com/ipfs/go-ipfs/merkledag" - - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" -) - -// These constants define the changes that can be applied to a DAG. -const ( - Add = iota - Remove - Mod -) - -// Change represents a change to a DAG and contains a reference to the old and -// new CIDs. -type Change struct { - Type int - Path string - Before *cid.Cid - After *cid.Cid -} - -// String prints a human-friendly line about a change. -func (c *Change) String() string { - switch c.Type { - case Add: - return fmt.Sprintf("Added %s at %s", c.After.String(), c.Path) - case Remove: - return fmt.Sprintf("Removed %s from %s", c.Before.String(), c.Path) - case Mod: - return fmt.Sprintf("Changed %s to %s at %s", c.Before.String(), c.After.String(), c.Path) - default: - panic("nope") - } -} - -// ApplyChange applies the requested changes to the given node in the given dag. -func ApplyChange(ctx context.Context, ds ipld.DAGService, nd *dag.ProtoNode, cs []*Change) (*dag.ProtoNode, error) { - e := NewDagEditor(nd, ds) - for _, c := range cs { - switch c.Type { - case Add: - child, err := ds.Get(ctx, c.After) - if err != nil { - return nil, err - } - - childpb, ok := child.(*dag.ProtoNode) - if !ok { - return nil, dag.ErrNotProtobuf - } - - err = e.InsertNodeAtPath(ctx, c.Path, childpb, nil) - if err != nil { - return nil, err - } - - case Remove: - err := e.RmLink(ctx, c.Path) - if err != nil { - return nil, err - } - - case Mod: - err := e.RmLink(ctx, c.Path) - if err != nil { - return nil, err - } - child, err := ds.Get(ctx, c.After) - if err != nil { - return nil, err - } - - childpb, ok := child.(*dag.ProtoNode) - if !ok { - return nil, dag.ErrNotProtobuf - } - - err = e.InsertNodeAtPath(ctx, c.Path, childpb, nil) - if err != nil { - return nil, err - } - } - } - - return e.Finalize(ctx, ds) -} - -// Diff returns a set of changes that transform node 'a' into node 'b' -func Diff(ctx context.Context, ds ipld.DAGService, a, b ipld.Node) ([]*Change, error) { - // Base case where both nodes are leaves, just compare - // their CIDs. - if len(a.Links()) == 0 && len(b.Links()) == 0 { - if a.Cid().Equals(b.Cid()) { - return []*Change{}, nil - } - return []*Change{ - &Change{ - Type: Mod, - Before: a.Cid(), - After: b.Cid(), - }, - }, nil - } - - var out []*Change - cleanA := a.Copy().(*dag.ProtoNode) - cleanB := b.Copy().(*dag.ProtoNode) - - // strip out unchanged stuff - for _, lnk := range a.Links() { - l, _, err := b.ResolveLink([]string{lnk.Name}) - if err == nil { - if l.Cid.Equals(lnk.Cid) { - // no change... ignore it - } else { - anode, err := lnk.GetNode(ctx, ds) - if err != nil { - return nil, err - } - - bnode, err := l.GetNode(ctx, ds) - if err != nil { - return nil, err - } - - anodepb, ok := anode.(*dag.ProtoNode) - if !ok { - return nil, dag.ErrNotProtobuf - } - - bnodepb, ok := bnode.(*dag.ProtoNode) - if !ok { - return nil, dag.ErrNotProtobuf - } - - sub, err := Diff(ctx, ds, anodepb, bnodepb) - if err != nil { - return nil, err - } - - for _, subc := range sub { - subc.Path = path.Join(lnk.Name, subc.Path) - out = append(out, subc) - } - } - cleanA.RemoveNodeLink(l.Name) - cleanB.RemoveNodeLink(l.Name) - } - } - - for _, lnk := range cleanA.Links() { - out = append(out, &Change{ - Type: Remove, - Path: lnk.Name, - Before: lnk.Cid, - }) - } - for _, lnk := range cleanB.Links() { - out = append(out, &Change{ - Type: Add, - Path: lnk.Name, - After: lnk.Cid, - }) - } - - return out, nil -} - -// Conflict represents two incompatible changes and is returned by MergeDiffs(). -type Conflict struct { - A *Change - B *Change -} - -// MergeDiffs takes two slice of changes and adds them to a single slice. -// When a Change from b happens to the same path of an existing change in a, -// a conflict is created and b is not added to the merged slice. -// A slice of Conflicts is returned and contains pointers to the -// Changes involved (which share the same path). -func MergeDiffs(a, b []*Change) ([]*Change, []Conflict) { - var out []*Change - var conflicts []Conflict - paths := make(map[string]*Change) - for _, c := range a { - paths[c.Path] = c - } - - for _, c := range b { - if ca, ok := paths[c.Path]; ok { - conflicts = append(conflicts, Conflict{ - A: ca, - B: c, - }) - } else { - out = append(out, c) - } - } - for _, c := range paths { - out = append(out, c) - } - return out, conflicts -} diff --git a/ipld/merkledag/utils/diffenum.go b/ipld/merkledag/utils/diffenum.go deleted file mode 100644 index d2ad1c8ef..000000000 --- a/ipld/merkledag/utils/diffenum.go +++ /dev/null @@ -1,99 +0,0 @@ -package dagutils - -import ( - "context" - "fmt" - - mdag "github.com/ipfs/go-ipfs/merkledag" - - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" -) - -// DiffEnumerate fetches every object in the graph pointed to by 'to' that is -// not in 'from'. This can be used to more efficiently fetch a graph if you can -// guarantee you already have the entirety of 'from' -func DiffEnumerate(ctx context.Context, dserv ipld.NodeGetter, from, to *cid.Cid) error { - fnd, err := dserv.Get(ctx, from) - if err != nil { - return fmt.Errorf("get %s: %s", from, err) - } - - tnd, err := dserv.Get(ctx, to) - if err != nil { - return fmt.Errorf("get %s: %s", to, err) - } - - diff := getLinkDiff(fnd, tnd) - - sset := cid.NewSet() - for _, c := range diff { - // Since we're already assuming we have everything in the 'from' graph, - // add all those cids to our 'already seen' set to avoid potentially - // enumerating them later - if c.bef != nil { - sset.Add(c.bef) - } - } - for _, c := range diff { - if c.bef == nil { - if sset.Has(c.aft) { - continue - } - err := mdag.EnumerateChildrenAsync(ctx, mdag.GetLinksDirect(dserv), c.aft, sset.Visit) - if err != nil { - return err - } - } else { - err := DiffEnumerate(ctx, dserv, c.bef, c.aft) - if err != nil { - return err - } - } - } - - return nil -} - -// if both bef and aft are not nil, then that signifies bef was replaces with aft. -// if bef is nil and aft is not, that means aft was newly added -// if aft is nil and bef is not, that means bef was deleted -type diffpair struct { - bef, aft *cid.Cid -} - -// getLinkDiff returns a changeset between nodes 'a' and 'b'. Currently does -// not log deletions as our usecase doesnt call for this. -func getLinkDiff(a, b ipld.Node) []diffpair { - ina := make(map[string]*ipld.Link) - inb := make(map[string]*ipld.Link) - var aonly []*cid.Cid - for _, l := range b.Links() { - inb[l.Cid.KeyString()] = l - } - for _, l := range a.Links() { - var key = l.Cid.KeyString() - ina[key] = l - if inb[key] == nil { - aonly = append(aonly, l.Cid) - } - } - - var out []diffpair - var aindex int - - for _, l := range b.Links() { - if ina[l.Cid.KeyString()] != nil { - continue - } - - if aindex < len(aonly) { - out = append(out, diffpair{bef: aonly[aindex], aft: l.Cid}) - aindex++ - } else { - out = append(out, diffpair{aft: l.Cid}) - continue - } - } - return out -} diff --git a/ipld/merkledag/utils/diffenum_test.go b/ipld/merkledag/utils/diffenum_test.go deleted file mode 100644 index 85bdbb26e..000000000 --- a/ipld/merkledag/utils/diffenum_test.go +++ /dev/null @@ -1,249 +0,0 @@ -package dagutils - -import ( - "context" - "fmt" - "testing" - - dag "github.com/ipfs/go-ipfs/merkledag" - mdtest "github.com/ipfs/go-ipfs/merkledag/test" - - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" -) - -func buildNode(name string, desc map[string]ndesc, out map[string]ipld.Node) ipld.Node { - this := desc[name] - nd := new(dag.ProtoNode) - nd.SetData([]byte(name)) - for k, v := range this { - child, ok := out[v] - if !ok { - child = buildNode(v, desc, out) - out[v] = child - } - - if err := nd.AddNodeLink(k, child); err != nil { - panic(err) - } - } - - return nd -} - -type ndesc map[string]string - -func mkGraph(desc map[string]ndesc) map[string]ipld.Node { - out := make(map[string]ipld.Node) - for name := range desc { - if _, ok := out[name]; ok { - continue - } - - out[name] = buildNode(name, desc, out) - } - return out -} - -var tg1 = map[string]ndesc{ - "a1": ndesc{ - "foo": "b", - }, - "b": ndesc{}, - "a2": ndesc{ - "foo": "b", - "bar": "c", - }, - "c": ndesc{}, -} - -var tg2 = map[string]ndesc{ - "a1": ndesc{ - "foo": "b", - }, - "b": ndesc{}, - "a2": ndesc{ - "foo": "b", - "bar": "c", - }, - "c": ndesc{"baz": "d"}, - "d": ndesc{}, -} - -var tg3 = map[string]ndesc{ - "a1": ndesc{ - "foo": "b", - "bar": "c", - }, - "b": ndesc{}, - "a2": ndesc{ - "foo": "b", - "bar": "d", - }, - "c": ndesc{}, - "d": ndesc{}, -} - -var tg4 = map[string]ndesc{ - "a1": ndesc{ - "key1": "b", - "key2": "c", - }, - "a2": ndesc{ - "key1": "b", - "key2": "d", - }, -} - -var tg5 = map[string]ndesc{ - "a1": ndesc{ - "key1": "a", - "key2": "b", - }, - "a2": ndesc{ - "key1": "c", - "key2": "d", - }, -} - -func TestNameMatching(t *testing.T) { - nds := mkGraph(tg4) - - diff := getLinkDiff(nds["a1"], nds["a2"]) - if len(diff) != 1 { - t.Fatal(fmt.Errorf("node diff didn't match by name")) - } -} - -func TestNameMatching2(t *testing.T) { - nds := mkGraph(tg5) - - diff := getLinkDiff(nds["a1"], nds["a2"]) - if len(diff) != 2 { - t.Fatal(fmt.Errorf("incorrect number of link diff elements")) - } - if !(diff[0].bef.Equals(nds["a1"].Links()[0].Cid) && diff[0].aft.Equals(nds["a2"].Links()[0].Cid)) { - t.Fatal(fmt.Errorf("node diff didn't match by name")) - } -} - -func TestDiffEnumBasic(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - nds := mkGraph(tg1) - - ds := mdtest.Mock() - lgds := &getLogger{ds: ds} - - for _, nd := range nds { - err := ds.Add(ctx, nd) - if err != nil { - t.Fatal(err) - } - } - - err := DiffEnumerate(ctx, lgds, nds["a1"].Cid(), nds["a2"].Cid()) - if err != nil { - t.Fatal(err) - } - - err = assertCidList(lgds.log, []*cid.Cid{nds["a1"].Cid(), nds["a2"].Cid(), nds["c"].Cid()}) - if err != nil { - t.Fatal(err) - } -} - -type getLogger struct { - ds ipld.NodeGetter - log []*cid.Cid -} - -func (gl *getLogger) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { - nd, err := gl.ds.Get(ctx, c) - if err != nil { - return nil, err - } - gl.log = append(gl.log, c) - return nd, nil -} - -func (gl *getLogger) GetMany(ctx context.Context, cids []*cid.Cid) <-chan *ipld.NodeOption { - outCh := make(chan *ipld.NodeOption, len(cids)) - nds := gl.ds.GetMany(ctx, cids) - for no := range nds { - if no.Err == nil { - gl.log = append(gl.log, no.Node.Cid()) - } - select { - case outCh <- no: - default: - panic("too many responses") - } - } - return nds -} - -func assertCidList(a, b []*cid.Cid) error { - if len(a) != len(b) { - return fmt.Errorf("got different number of cids than expected") - } - for i, c := range a { - if !c.Equals(b[i]) { - return fmt.Errorf("expected %s, got %s", c, b[i]) - } - } - return nil -} - -func TestDiffEnumFail(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - nds := mkGraph(tg2) - - ds := mdtest.Mock() - lgds := &getLogger{ds: ds} - - for _, s := range []string{"a1", "a2", "b", "c"} { - err := ds.Add(ctx, nds[s]) - if err != nil { - t.Fatal(err) - } - } - - err := DiffEnumerate(ctx, lgds, nds["a1"].Cid(), nds["a2"].Cid()) - if err != ipld.ErrNotFound { - t.Fatal("expected err not found") - } - - err = assertCidList(lgds.log, []*cid.Cid{nds["a1"].Cid(), nds["a2"].Cid(), nds["c"].Cid()}) - if err != nil { - t.Fatal(err) - } - -} - -func TestDiffEnumRecurse(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - nds := mkGraph(tg3) - - ds := mdtest.Mock() - lgds := &getLogger{ds: ds} - - for _, s := range []string{"a1", "a2", "b", "c", "d"} { - err := ds.Add(ctx, nds[s]) - if err != nil { - t.Fatal(err) - } - } - - err := DiffEnumerate(ctx, lgds, nds["a1"].Cid(), nds["a2"].Cid()) - if err != nil { - t.Fatal(err) - } - - err = assertCidList(lgds.log, []*cid.Cid{nds["a1"].Cid(), nds["a2"].Cid(), nds["c"].Cid(), nds["d"].Cid()}) - if err != nil { - t.Fatal(err) - } -} diff --git a/ipld/merkledag/utils/utils.go b/ipld/merkledag/utils/utils.go deleted file mode 100644 index fbd18c725..000000000 --- a/ipld/merkledag/utils/utils.go +++ /dev/null @@ -1,234 +0,0 @@ -package dagutils - -import ( - "context" - "errors" - - bserv "github.com/ipfs/go-ipfs/blockservice" - dag "github.com/ipfs/go-ipfs/merkledag" - path "github.com/ipfs/go-ipfs/path" - - offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" - bstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - syncds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" -) - -// Editor represents a ProtoNode tree editor and provides methods to -// modify it. -type Editor struct { - root *dag.ProtoNode - - // tmp is a temporary in memory (for now) dagstore for all of the - // intermediary nodes to be stored in - tmp ipld.DAGService - - // src is the dagstore with *all* of the data on it, it is used to pull - // nodes from for modification (nil is a valid value) - src ipld.DAGService -} - -// NewMemoryDagService returns a new, thread-safe in-memory DAGService. -func NewMemoryDagService() ipld.DAGService { - // build mem-datastore for editor's intermediary nodes - bs := bstore.NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore())) - bsrv := bserv.New(bs, offline.Exchange(bs)) - return dag.NewDAGService(bsrv) -} - -// NewDagEditor returns an ProtoNode editor. -// -// * root is the node to be modified -// * source is the dagstore to pull nodes from (optional) -func NewDagEditor(root *dag.ProtoNode, source ipld.DAGService) *Editor { - return &Editor{ - root: root, - tmp: NewMemoryDagService(), - src: source, - } -} - -// GetNode returns the a copy of the root node being edited. -func (e *Editor) GetNode() *dag.ProtoNode { - return e.root.Copy().(*dag.ProtoNode) -} - -// GetDagService returns the DAGService used by this editor. -func (e *Editor) GetDagService() ipld.DAGService { - return e.tmp -} - -func addLink(ctx context.Context, ds ipld.DAGService, root *dag.ProtoNode, childname string, childnd ipld.Node) (*dag.ProtoNode, error) { - if childname == "" { - return nil, errors.New("cannot create link with no name") - } - - // ensure that the node we are adding is in the dagservice - err := ds.Add(ctx, childnd) - if err != nil { - return nil, err - } - - _ = ds.Remove(ctx, root.Cid()) - - // ensure no link with that name already exists - _ = root.RemoveNodeLink(childname) // ignore error, only option is ErrNotFound - - if err := root.AddNodeLink(childname, childnd); err != nil { - return nil, err - } - - if err := ds.Add(ctx, root); err != nil { - return nil, err - } - return root, nil -} - -// InsertNodeAtPath inserts a new node in the tree and replaces the current root with the new one. -func (e *Editor) InsertNodeAtPath(ctx context.Context, pth string, toinsert ipld.Node, create func() *dag.ProtoNode) error { - splpath := path.SplitList(pth) - nd, err := e.insertNodeAtPath(ctx, e.root, splpath, toinsert, create) - if err != nil { - return err - } - e.root = nd - return nil -} - -func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path []string, toinsert ipld.Node, create func() *dag.ProtoNode) (*dag.ProtoNode, error) { - if len(path) == 1 { - return addLink(ctx, e.tmp, root, path[0], toinsert) - } - - nd, err := root.GetLinkedProtoNode(ctx, e.tmp, path[0]) - if err != nil { - // if 'create' is true, we create directories on the way down as needed - if err == dag.ErrLinkNotFound && create != nil { - nd = create() - err = nil // no longer an error case - } else if err == ipld.ErrNotFound { - // try finding it in our source dagstore - nd, err = root.GetLinkedProtoNode(ctx, e.src, path[0]) - } - - // if we receive an ErrNotFound, then our second 'GetLinkedNode' call - // also fails, we want to error out - if err != nil { - return nil, err - } - } - - ndprime, err := e.insertNodeAtPath(ctx, nd, path[1:], toinsert, create) - if err != nil { - return nil, err - } - - _ = e.tmp.Remove(ctx, root.Cid()) - - _ = root.RemoveNodeLink(path[0]) - err = root.AddNodeLink(path[0], ndprime) - if err != nil { - return nil, err - } - - err = e.tmp.Add(ctx, root) - if err != nil { - return nil, err - } - - return root, nil -} - -// RmLink removes the link with the given name and updates the root node of -// the editor. -func (e *Editor) RmLink(ctx context.Context, pth string) error { - splpath := path.SplitList(pth) - nd, err := e.rmLink(ctx, e.root, splpath) - if err != nil { - return err - } - e.root = nd - return nil -} - -func (e *Editor) rmLink(ctx context.Context, root *dag.ProtoNode, path []string) (*dag.ProtoNode, error) { - if len(path) == 1 { - // base case, remove node in question - err := root.RemoveNodeLink(path[0]) - if err != nil { - return nil, err - } - - err = e.tmp.Add(ctx, root) - if err != nil { - return nil, err - } - - return root, nil - } - - // search for node in both tmp dagstore and source dagstore - nd, err := root.GetLinkedProtoNode(ctx, e.tmp, path[0]) - if err == ipld.ErrNotFound { - nd, err = root.GetLinkedProtoNode(ctx, e.src, path[0]) - } - - if err != nil { - return nil, err - } - - nnode, err := e.rmLink(ctx, nd, path[1:]) - if err != nil { - return nil, err - } - - e.tmp.Remove(ctx, root.Cid()) - - _ = root.RemoveNodeLink(path[0]) - err = root.AddNodeLink(path[0], nnode) - if err != nil { - return nil, err - } - - err = e.tmp.Add(ctx, root) - if err != nil { - return nil, err - } - - return root, nil -} - -// Finalize writes the new DAG to the given DAGService and returns the modified -// root node. -func (e *Editor) Finalize(ctx context.Context, ds ipld.DAGService) (*dag.ProtoNode, error) { - nd := e.GetNode() - err := copyDag(ctx, nd, e.tmp, ds) - return nd, err -} - -func copyDag(ctx context.Context, nd ipld.Node, from, to ipld.DAGService) error { - // TODO(#4609): make this batch. - err := to.Add(ctx, nd) - if err != nil { - return err - } - - for _, lnk := range nd.Links() { - child, err := lnk.GetNode(ctx, from) - if err != nil { - if err == ipld.ErrNotFound { - // not found means we didnt modify it, and it should - // already be in the target datastore - continue - } - return err - } - - err = copyDag(ctx, child, from, to) - if err != nil { - return err - } - } - return nil -} diff --git a/ipld/merkledag/utils/utils_test.go b/ipld/merkledag/utils/utils_test.go deleted file mode 100644 index b0df1f3b8..000000000 --- a/ipld/merkledag/utils/utils_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package dagutils - -import ( - "context" - "testing" - - dag "github.com/ipfs/go-ipfs/merkledag" - mdtest "github.com/ipfs/go-ipfs/merkledag/test" - path "github.com/ipfs/go-ipfs/path" - - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" -) - -func TestAddLink(t *testing.T) { - ctx, context := context.WithCancel(context.Background()) - defer context() - - ds := mdtest.Mock() - fishnode := dag.NodeWithData([]byte("fishcakes!")) - - err := ds.Add(ctx, fishnode) - if err != nil { - t.Fatal(err) - } - - nd := new(dag.ProtoNode) - nnode, err := addLink(ctx, ds, nd, "fish", fishnode) - if err != nil { - t.Fatal(err) - } - - fnprime, err := nnode.GetLinkedNode(ctx, ds, "fish") - if err != nil { - t.Fatal(err) - } - - fnpkey := fnprime.Cid() - if !fnpkey.Equals(fishnode.Cid()) { - t.Fatal("wrong child node found!") - } -} - -func assertNodeAtPath(t *testing.T, ds ipld.DAGService, root *dag.ProtoNode, pth string, exp *cid.Cid) { - parts := path.SplitList(pth) - cur := root - for _, e := range parts { - nxt, err := cur.GetLinkedProtoNode(context.Background(), ds, e) - if err != nil { - t.Fatal(err) - } - - cur = nxt - } - - curc := cur.Cid() - if !curc.Equals(exp) { - t.Fatal("node not as expected at end of path") - } -} - -func TestInsertNode(t *testing.T) { - root := new(dag.ProtoNode) - e := NewDagEditor(root, nil) - - testInsert(t, e, "a", "anodefortesting", false, "") - testInsert(t, e, "a/b", "data", false, "") - testInsert(t, e, "a/b/c/d/e", "blah", false, "no link by that name") - testInsert(t, e, "a/b/c/d/e", "foo", true, "") - testInsert(t, e, "a/b/c/d/f", "baz", true, "") - testInsert(t, e, "a/b/c/d/f", "bar", true, "") - - testInsert(t, e, "", "bar", true, "cannot create link with no name") - testInsert(t, e, "////", "slashes", true, "cannot create link with no name") - - c := e.GetNode().Cid() - - if c.String() != "QmZ8yeT9uD6ouJPNAYt62XffYuXBT6b4mP4obRSE9cJrSt" { - t.Fatal("output was different than expected: ", c) - } -} - -func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr string) { - child := dag.NodeWithData([]byte(data)) - err := e.tmp.Add(context.Background(), child) - if err != nil { - t.Fatal(err) - } - - var c func() *dag.ProtoNode - if create { - c = func() *dag.ProtoNode { - return &dag.ProtoNode{} - } - } - - err = e.InsertNodeAtPath(context.Background(), path, child, c) - if experr != "" { - var got string - if err != nil { - got = err.Error() - } - if got != experr { - t.Fatalf("expected '%s' but got '%s'", experr, got) - } - return - } - - if err != nil { - t.Fatal(err, path, data, create, experr) - } - - assertNodeAtPath(t, e.tmp, e.root, path, child.Cid()) -} From 0d5a02940e34b5644f801f73e86ccfc3e41ebeb9 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 27 Jul 2018 17:48:49 -0700 Subject: [PATCH 2460/3817] gx deps This commit was moved from ipfs/go-verifcid@3bed62765519ddd6b623f3d927817b0740edf89c --- verifcid/validate.go | 4 ++-- verifcid/validate_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/verifcid/validate.go b/verifcid/validate.go index 6e4c80d61..34db44ffc 100644 --- a/verifcid/validate.go +++ b/verifcid/validate.go @@ -3,8 +3,8 @@ package verifcid import ( "fmt" - mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + cid "github.com/ipfs/go-cid" + mh "github.com/multiformats/go-multihash" ) var ErrPossiblyInsecureHashFunction = fmt.Errorf("potentially insecure hash functions not allowed") diff --git a/verifcid/validate_test.go b/verifcid/validate_test.go index a058f312c..4b53ce183 100644 --- a/verifcid/validate_test.go +++ b/verifcid/validate_test.go @@ -3,9 +3,9 @@ package verifcid import ( "testing" - mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + mh "github.com/multiformats/go-multihash" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + cid "github.com/ipfs/go-cid" ) func TestValidateCids(t *testing.T) { From d2d8182055284e9de14ef9447bf2dfb06bc01731 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 27 Jul 2018 18:15:35 -0700 Subject: [PATCH 2461/3817] packageify This commit was moved from ipfs/go-blockservice@1f90737285921227dce9f78b0b9a386182fb5fa3 --- blockservice/LICENSE | 21 ++++++++++++++++++++ blockservice/README.md | 33 +++++++++++++++++++++++++++++++ blockservice/blockservice.go | 10 +++++----- blockservice/blockservice_test.go | 12 +++++------ blockservice/test/blocks_test.go | 16 +++++++-------- blockservice/test/mock.go | 10 +++++----- 6 files changed, 78 insertions(+), 24 deletions(-) create mode 100644 blockservice/LICENSE create mode 100644 blockservice/README.md diff --git a/blockservice/LICENSE b/blockservice/LICENSE new file mode 100644 index 000000000..7d5dcac4d --- /dev/null +++ b/blockservice/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2018 Juan Batiz-Benet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/blockservice/README.md b/blockservice/README.md new file mode 100644 index 000000000..0ec2aef87 --- /dev/null +++ b/blockservice/README.md @@ -0,0 +1,33 @@ +go-blockservice +================== + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) +[![Coverage Status](https://codecov.io/gh/ipfs/go-block-format/branch/master/graph/badge.svg)](https://codecov.io/gh/ipfs/go-block-format/branch/master) +[![Travis CI](https://travis-ci.org/ipfs/go-block-format.svg?branch=master)](https://travis-ci.org/ipfs/go-block-format) + +> go-blockservice provides a seamless interface to both local and remote storage backends. + + +## Table of Contents + +- [TODO](#todo) +- [Contribute](#contribute) +- [License](#license) + +## TODO + +The interfaces here really would like to be merged with the blockstore interfaces. +The 'dagservice' constructor currently takes a blockservice, but it would be really nice +if it could just take a blockstore, and have this package implement a blockstore. + +## Contribute + +PRs are welcome! + +Small note: If editing the Readme, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +MIT © Juan Batiz-Benet diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index c4502c975..818b44827 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -11,11 +11,11 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/verifcid" - blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" - exchange "gx/ipfs/Qmc2faLf7URkHpsbfYM4EMbr8iSAcGAe8VPgVi64HVnwji/go-ipfs-exchange-interface" - logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" + blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + blockstore "github.com/ipfs/go-ipfs-blockstore" + exchange "github.com/ipfs/go-ipfs-exchange-interface" + logging "github.com/ipfs/go-log" ) var log = logging.Logger("blockservice") diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index e94f4a3fe..fd64eb60c 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -3,12 +3,12 @@ package blockservice import ( "testing" - offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" - blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" - butil "gx/ipfs/QmYqPGpZ9Yemr55xus9DiEztkns6Jti5XJ7hC94JbvkdqZ/go-ipfs-blocksutil" - blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" + blocks "github.com/ipfs/go-block-format" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + blockstore "github.com/ipfs/go-ipfs-blockstore" + butil "github.com/ipfs/go-ipfs-blocksutil" + offline "github.com/ipfs/go-ipfs-exchange-offline" ) func TestWriteThroughWorks(t *testing.T) { diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 19a2e54f0..c3faa02c3 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -7,15 +7,15 @@ import ( "testing" "time" - . "github.com/ipfs/go-ipfs/blockservice" + . "github.com/ipfs/go-blockservice" - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" - blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" + blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + blockstore "github.com/ipfs/go-ipfs-blockstore" + offline "github.com/ipfs/go-ipfs-exchange-offline" + u "github.com/ipfs/go-ipfs-util" ) func newObject(data []byte) blocks.Block { diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index 436fe7d34..9638f3cb3 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -1,12 +1,12 @@ package bstest import ( - . "github.com/ipfs/go-ipfs/blockservice" - bitswap "github.com/ipfs/go-ipfs/exchange/bitswap" - tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet" + . "github.com/ipfs/go-blockservice" - delay "gx/ipfs/QmRJVNatYJwTAHgdSM1Xef9QVQ1Ch3XHdmcrykjP5Y4soL/go-ipfs-delay" - mockrouting "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/mock" + bitswap "github.com/ipfs/go-bitswap" + tn "github.com/ipfs/go-bitswap/testnet" + delay "github.com/ipfs/go-ipfs-delay" + mockrouting "github.com/ipfs/go-ipfs-routing/mock" ) // Mocks returns |n| connected mock Blockservices From fe29b758a7aebda5840b9dd6482540cc22119b95 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 27 Jul 2018 18:17:40 -0700 Subject: [PATCH 2462/3817] fix import This commit was moved from ipfs/go-blockservice@e843ff02576f4b9506b28151f970d266054799ec --- blockservice/blockservice.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 818b44827..d5c18824a 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -9,13 +9,12 @@ import ( "fmt" "io" - "github.com/ipfs/go-ipfs/thirdparty/verifcid" - blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" exchange "github.com/ipfs/go-ipfs-exchange-interface" logging "github.com/ipfs/go-log" + "github.com/ipfs/go-verifcid" ) var log = logging.Logger("blockservice") From b476542bc131d8a52e1a80e94b481a26b1371627 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 27 Jul 2018 18:21:06 -0700 Subject: [PATCH 2463/3817] Extract blockservice and verifcid License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@9ec1c02f6731ea52ea137447b6247bf580e8480f --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin_test.go | 2 +- pinning/pinner/set_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index e01321c65..eb30016ee 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -7,11 +7,11 @@ import ( "fmt" "strings" - bserv "github.com/ipfs/go-ipfs/blockservice" dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" - "github.com/ipfs/go-ipfs/thirdparty/verifcid" + bserv "gx/ipfs/QmNqRBAhovtf4jVd5cF7YvHaFSsQHHZBaUFwGQWPM2CV7R/go-blockservice" + "gx/ipfs/QmQwgv79RHrRnoXmhnpC1BPtY55HHeneGMpPwmmBU1fUAG/go-verifcid" offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index f832e0a72..ae6ace111 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - bs "github.com/ipfs/go-ipfs/blockservice" mdag "github.com/ipfs/go-ipfs/merkledag" + bs "gx/ipfs/QmNqRBAhovtf4jVd5cF7YvHaFSsQHHZBaUFwGQWPM2CV7R/go-blockservice" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index b2fe149a4..c477a71d7 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - bserv "github.com/ipfs/go-ipfs/blockservice" dag "github.com/ipfs/go-ipfs/merkledag" + bserv "gx/ipfs/QmNqRBAhovtf4jVd5cF7YvHaFSsQHHZBaUFwGQWPM2CV7R/go-blockservice" offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" From b00462ee170142ad305da15bb3c200f9f0202e4b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 27 Jul 2018 18:21:06 -0700 Subject: [PATCH 2464/3817] Extract blockservice and verifcid License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@a53d46f644a2d95db67ce1161c29b49f720efcae --- mfs/mfs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index d2f22bcd4..e1af70d15 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,12 +14,12 @@ import ( "testing" "time" - bserv "github.com/ipfs/go-ipfs/blockservice" importer "github.com/ipfs/go-ipfs/importer" dag "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/path" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" + bserv "gx/ipfs/QmNqRBAhovtf4jVd5cF7YvHaFSsQHHZBaUFwGQWPM2CV7R/go-blockservice" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" From e1c9f70476930aadbefc503f6cf61145d041e501 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 27 Jul 2018 22:17:11 -0700 Subject: [PATCH 2465/3817] extractify This commit was moved from ipfs/go-merkledag@27709871f4d1db414491c10b4d8848f3e312d033 --- ipld/merkledag/LICENSE | 21 ++++++++++++++ ipld/merkledag/README.md | 36 ++++++++++++++++++++++++ ipld/merkledag/coding.go | 8 +++--- ipld/merkledag/errservice.go | 4 +-- ipld/merkledag/merkledag.go | 11 ++++---- ipld/merkledag/merkledag_test.go | 22 +++++++-------- ipld/merkledag/node.go | 6 ++-- ipld/merkledag/node_test.go | 6 ++-- ipld/merkledag/pb/merkledag.pb.go | 4 +-- ipld/merkledag/pb/merkledagpb_test.go | 2 +- ipld/merkledag/raw.go | 8 +++--- ipld/merkledag/readonly.go | 2 +- ipld/merkledag/readonly_test.go | 8 +++--- ipld/merkledag/rwservice.go | 4 +-- ipld/merkledag/session.go | 2 +- ipld/merkledag/test/utils.go | 14 ++++----- ipld/merkledag/traverse/traverse.go | 2 +- ipld/merkledag/traverse/traverse_test.go | 6 ++-- 18 files changed, 111 insertions(+), 55 deletions(-) create mode 100644 ipld/merkledag/LICENSE create mode 100644 ipld/merkledag/README.md diff --git a/ipld/merkledag/LICENSE b/ipld/merkledag/LICENSE new file mode 100644 index 000000000..7d5dcac4d --- /dev/null +++ b/ipld/merkledag/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2018 Juan Batiz-Benet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ipld/merkledag/README.md b/ipld/merkledag/README.md new file mode 100644 index 000000000..8bf135a4f --- /dev/null +++ b/ipld/merkledag/README.md @@ -0,0 +1,36 @@ +go-merkledag +================== + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) +[![Coverage Status](https://codecov.io/gh/ipfs/go-merkledag/branch/master/graph/badge.svg)](https://codecov.io/gh/ipfs/go-merkledag/branch/master) +[![Travis CI](https://travis-ci.org/ipfs/go-merkledag.svg?branch=master)](https://travis-ci.org/ipfs/go-merkledag) + +> go-merkledag implements the 'DAGService' interface and adds two ipld node types, Protobuf and Raw + + + +## Table of Contents + +- [TODO](#todo) +- [Contribute](#contribute) +- [License](#license) + +## TODO + +- Pull out dag-pb stuff into go-ipld-pb +- Pull 'raw nodes' out into go-ipld-raw (maybe main one instead) +- Move most other logic to go-ipld +- Make dagservice constructor take a 'blockstore' to avoid the blockservice offline nonsense +- deprecate this package + +## Contribute + +PRs are welcome! + +Small note: If editing the Readme, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +MIT © Juan Batiz-Benet diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 6111e2d36..fd204e21c 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -5,12 +5,12 @@ import ( "sort" "strings" - "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" + "github.com/ipfs/go-block-format" - pb "github.com/ipfs/go-ipfs/merkledag/pb" + pb "github.com/ipfs/go-merkledag/pb" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" ) // for now, we use a PBNode intermediate thing. diff --git a/ipld/merkledag/errservice.go b/ipld/merkledag/errservice.go index 9996ed72d..d26c176e2 100644 --- a/ipld/merkledag/errservice.go +++ b/ipld/merkledag/errservice.go @@ -3,8 +3,8 @@ package merkledag import ( "context" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" ) // ErrorService implements ipld.DAGService, returning 'Err' for every call. diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 4bdf734e8..de2f61c95 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -6,12 +6,11 @@ import ( "fmt" "sync" - bserv "github.com/ipfs/go-ipfs/blockservice" - - blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" - ipldcbor "gx/ipfs/QmWrbExtUaQQHjJ8FVVDAWj5o1MRAELDUV3VmoQsZHHb6L/go-ipld-cbor" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + blocks "github.com/ipfs/go-block-format" + bserv "github.com/ipfs/go-blockservice" + cid "github.com/ipfs/go-cid" + ipldcbor "github.com/ipfs/go-ipld-cbor" + ipld "github.com/ipfs/go-ipld-format" ) // TODO: We should move these registrations elsewhere. Really, most of the IPLD diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 1153b6989..36c054857 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -13,17 +13,17 @@ import ( "testing" "time" - bserv "github.com/ipfs/go-ipfs/blockservice" - bstest "github.com/ipfs/go-ipfs/blockservice/test" - . "github.com/ipfs/go-ipfs/merkledag" - mdpb "github.com/ipfs/go-ipfs/merkledag/pb" - dstest "github.com/ipfs/go-ipfs/merkledag/test" - - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" - blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + . "github.com/ipfs/go-merkledag" + mdpb "github.com/ipfs/go-merkledag/pb" + dstest "github.com/ipfs/go-merkledag/test" + + blocks "github.com/ipfs/go-block-format" + bserv "github.com/ipfs/go-blockservice" + bstest "github.com/ipfs/go-blockservice/test" + cid "github.com/ipfs/go-cid" + offline "github.com/ipfs/go-ipfs-exchange-offline" + u "github.com/ipfs/go-ipfs-util" + ipld "github.com/ipfs/go-ipld-format" ) func TestNode(t *testing.T) { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 6f81644ec..876d40441 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -5,9 +5,9 @@ import ( "encoding/json" "fmt" - mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" + mh "github.com/multiformats/go-multihash" ) // Common errors diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index be2f43a08..77597fb5a 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -5,10 +5,10 @@ import ( "context" "testing" - . "github.com/ipfs/go-ipfs/merkledag" - mdtest "github.com/ipfs/go-ipfs/merkledag/test" + . "github.com/ipfs/go-merkledag" + mdtest "github.com/ipfs/go-merkledag/test" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "github.com/ipfs/go-ipld-format" ) func TestRemoveLink(t *testing.T) { diff --git a/ipld/merkledag/pb/merkledag.pb.go b/ipld/merkledag/pb/merkledag.pb.go index a4c73580f..505e0cf22 100644 --- a/ipld/merkledag/pb/merkledag.pb.go +++ b/ipld/merkledag/pb/merkledag.pb.go @@ -14,14 +14,14 @@ */ package merkledag_pb -import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" +import proto "github.com/gogo/protobuf/proto" import math "math" // discarding unused import gogoproto "code.google.com/p/gogoprotobuf/gogoproto/gogo.pb" import io "io" import fmt "fmt" -import github_com_gogo_protobuf_proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" +import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" import strings "strings" import reflect "reflect" diff --git a/ipld/merkledag/pb/merkledagpb_test.go b/ipld/merkledag/pb/merkledagpb_test.go index b59fca7fa..8da923423 100644 --- a/ipld/merkledag/pb/merkledagpb_test.go +++ b/ipld/merkledag/pb/merkledagpb_test.go @@ -17,7 +17,7 @@ package merkledag_pb import testing "testing" import math_rand "math/rand" import time "time" -import github_com_gogo_protobuf_proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" +import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" import encoding_json "encoding/json" import fmt "fmt" import go_parser "go/parser" diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 01d6cffff..4d93b7211 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -2,11 +2,11 @@ package merkledag import ( "fmt" - "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" + "github.com/ipfs/go-block-format" - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + cid "github.com/ipfs/go-cid" + u "github.com/ipfs/go-ipfs-util" + ipld "github.com/ipfs/go-ipld-format" ) // RawNode represents a node which only contains data. diff --git a/ipld/merkledag/readonly.go b/ipld/merkledag/readonly.go index 95f4ebc24..36242fbeb 100644 --- a/ipld/merkledag/readonly.go +++ b/ipld/merkledag/readonly.go @@ -3,7 +3,7 @@ package merkledag import ( "fmt" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "github.com/ipfs/go-ipld-format" ) // ErrReadOnly is used when a read-only datastructure is written to. diff --git a/ipld/merkledag/readonly_test.go b/ipld/merkledag/readonly_test.go index d57bbe3a6..285cc208b 100644 --- a/ipld/merkledag/readonly_test.go +++ b/ipld/merkledag/readonly_test.go @@ -4,11 +4,11 @@ import ( "context" "testing" - . "github.com/ipfs/go-ipfs/merkledag" - dstest "github.com/ipfs/go-ipfs/merkledag/test" + . "github.com/ipfs/go-merkledag" + dstest "github.com/ipfs/go-merkledag/test" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" ) func TestReadonlyProperties(t *testing.T) { diff --git a/ipld/merkledag/rwservice.go b/ipld/merkledag/rwservice.go index 3eb4f414a..4444d9778 100644 --- a/ipld/merkledag/rwservice.go +++ b/ipld/merkledag/rwservice.go @@ -3,8 +3,8 @@ package merkledag import ( "context" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" ) // ComboService implements ipld.DAGService, using 'Read' for all fetch methods, diff --git a/ipld/merkledag/session.go b/ipld/merkledag/session.go index 5731caad3..c7bbff169 100644 --- a/ipld/merkledag/session.go +++ b/ipld/merkledag/session.go @@ -3,7 +3,7 @@ package merkledag import ( "context" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "github.com/ipfs/go-ipld-format" ) // SessionMaker is an object that can generate a new fetching session. diff --git a/ipld/merkledag/test/utils.go b/ipld/merkledag/test/utils.go index c161f7c01..5ef256219 100644 --- a/ipld/merkledag/test/utils.go +++ b/ipld/merkledag/test/utils.go @@ -1,14 +1,14 @@ package mdutils import ( - bsrv "github.com/ipfs/go-ipfs/blockservice" - dag "github.com/ipfs/go-ipfs/merkledag" + dag "github.com/ipfs/go-merkledag" - offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" - blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" + bsrv "github.com/ipfs/go-blockservice" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + blockstore "github.com/ipfs/go-ipfs-blockstore" + offline "github.com/ipfs/go-ipfs-exchange-offline" + ipld "github.com/ipfs/go-ipld-format" ) // Mock returns a new thread-safe, mock DAGService. diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index c8d9dfdb9..7cb2dfe7d 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -5,7 +5,7 @@ import ( "context" "errors" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "github.com/ipfs/go-ipld-format" ) // Order is an identifier for traversal algorithm orders diff --git a/ipld/merkledag/traverse/traverse_test.go b/ipld/merkledag/traverse/traverse_test.go index 4b1f867a0..43932ae76 100644 --- a/ipld/merkledag/traverse/traverse_test.go +++ b/ipld/merkledag/traverse/traverse_test.go @@ -6,10 +6,10 @@ import ( "fmt" "testing" - mdag "github.com/ipfs/go-ipfs/merkledag" - mdagtest "github.com/ipfs/go-ipfs/merkledag/test" + mdag "github.com/ipfs/go-merkledag" + mdagtest "github.com/ipfs/go-merkledag/test" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "github.com/ipfs/go-ipld-format" ) func TestDFSPreNoSkip(t *testing.T) { From c56e4bf63aa342de8146d56cb926f72453ed9fbe Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 27 Jul 2018 13:12:13 -0700 Subject: [PATCH 2466/3817] move dagutils package to top level in preparation for merkledag extraction License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@a5958d1b3604afded9ee5256cc32e84906a3fa2e --- unixfs/hamt/hamt_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 4cad058ba..4265170ef 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -9,9 +9,9 @@ import ( "testing" "time" + "github.com/ipfs/go-ipfs/dagutils" dag "github.com/ipfs/go-ipfs/merkledag" mdtest "github.com/ipfs/go-ipfs/merkledag/test" - dagutils "github.com/ipfs/go-ipfs/merkledag/utils" ft "github.com/ipfs/go-ipfs/unixfs" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" From bf9473ac8dedff247cd6085c64ff195c3d14c773 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 27 Jul 2018 13:12:13 -0700 Subject: [PATCH 2467/3817] move dagutils package to top level in preparation for merkledag extraction License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@a33b22a8440b08e8a23d9abfb36fd0f4ddea9319 --- pinning/pinner/pin.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index d29c0eb8e..faa6e1f9d 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -9,8 +9,8 @@ import ( "sync" "time" + "github.com/ipfs/go-ipfs/dagutils" mdag "github.com/ipfs/go-ipfs/merkledag" - dutils "github.com/ipfs/go-ipfs/merkledag/utils" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" @@ -518,7 +518,7 @@ func (p *pinner) Update(ctx context.Context, from, to *cid.Cid, unpin bool) erro return fmt.Errorf("'from' cid was not recursively pinned already") } - err := dutils.DiffEnumerate(ctx, p.dserv, from, to) + err := dagutils.DiffEnumerate(ctx, p.dserv, from, to) if err != nil { return err } From 949f541960afb20258c72e10ed229bb3b0f615ba Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 28 Jul 2018 00:50:53 -0700 Subject: [PATCH 2468/3817] Extract dagservice, move dagutils to top level License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-path@d0099ffab2e5aac820dec085fa4cf0debf1b2ebe --- path/resolver/resolver.go | 2 +- path/resolver/resolver_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index af6b8628c..252fd48ac 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -7,8 +7,8 @@ import ( "fmt" "time" - dag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/path/resolver/resolver_test.go b/path/resolver/resolver_test.go index 149068120..ccf00a4e3 100644 --- a/path/resolver/resolver_test.go +++ b/path/resolver/resolver_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" - merkledag "github.com/ipfs/go-ipfs/merkledag" - dagmock "github.com/ipfs/go-ipfs/merkledag/test" path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/path/resolver" + merkledag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + dagmock "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" From d993ebc06ae990b35610fe09107c20798c6fbaa7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 28 Jul 2018 00:50:53 -0700 Subject: [PATCH 2469/3817] Extract dagservice, move dagutils to top level License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@063b6bd4a7f0028ff8f9a97bc78248307da25f82 --- mfs/dir.go | 2 +- mfs/file.go | 2 +- mfs/mfs_test.go | 2 +- mfs/system.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index a98ce859e..b2f9f5dff 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -9,10 +9,10 @@ import ( "sync" "time" - dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" ufspb "github.com/ipfs/go-ipfs/unixfs/pb" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/mfs/file.go b/mfs/file.go index 4ef16ff5d..41bd1f2ed 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -5,9 +5,9 @@ import ( "fmt" "sync" - dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" mod "github.com/ipfs/go-ipfs/unixfs/mod" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index e1af70d15..eeef69a58 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -15,11 +15,11 @@ import ( "time" importer "github.com/ipfs/go-ipfs/importer" - dag "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/path" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" bserv "gx/ipfs/QmNqRBAhovtf4jVd5cF7YvHaFSsQHHZBaUFwGQWPM2CV7R/go-blockservice" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" diff --git a/mfs/system.go b/mfs/system.go index 53a1b9eb3..8fe8f3221 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -16,8 +16,8 @@ import ( "sync" "time" - dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" From 0109685612153edb4d0d148145cb3cb96da6fe6f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 28 Jul 2018 00:50:53 -0700 Subject: [PATCH 2470/3817] Extract dagservice, move dagutils to top level License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@c008ff3d33c775ed673ef4d0d12949c761289749 --- unixfs/archive/tar/writer.go | 2 +- unixfs/hamt/hamt.go | 2 +- unixfs/hamt/hamt_stress_test.go | 2 +- unixfs/hamt/hamt_test.go | 4 ++-- unixfs/io/dagreader.go | 2 +- unixfs/io/dagreader_test.go | 2 +- unixfs/io/directory.go | 2 +- unixfs/io/directory_test.go | 2 +- unixfs/io/pbdagreader.go | 2 +- unixfs/io/resolve.go | 2 +- unixfs/mod/dagmodifier.go | 2 +- unixfs/test/utils.go | 4 ++-- unixfs/unixfs.go | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index fe5982ff4..d0a30c0d6 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -10,10 +10,10 @@ import ( "path" "time" - mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" upb "github.com/ipfs/go-ipfs/unixfs/pb" + mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 37f0fd78b..f4013884c 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -25,9 +25,9 @@ import ( "fmt" "os" - dag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" upb "github.com/ipfs/go-ipfs/unixfs/pb" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" bitfield "gx/ipfs/QmTbBs3Y3u5F69XNJzdnnc6SP5GKgcXxCDzx6w8m6piVRT/go-bitfield" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" diff --git a/unixfs/hamt/hamt_stress_test.go b/unixfs/hamt/hamt_stress_test.go index a9361e2c2..311f6f0d3 100644 --- a/unixfs/hamt/hamt_stress_test.go +++ b/unixfs/hamt/hamt_stress_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" - mdtest "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" + mdtest "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 4265170ef..ed6eaf70d 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -10,9 +10,9 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - dag "github.com/ipfs/go-ipfs/merkledag" - mdtest "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + mdtest "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 8a43fc981..0ef962f51 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -5,9 +5,9 @@ import ( "errors" "io" - mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" + mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index 8c73b1948..f2b0b0af6 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -8,8 +8,8 @@ import ( "strings" "testing" - mdag "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/unixfs" + mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" context "context" diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index c9aaa00aa..6f07aff04 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -5,9 +5,9 @@ import ( "fmt" "os" - mdag "github.com/ipfs/go-ipfs/merkledag" format "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" + mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/unixfs/io/directory_test.go b/unixfs/io/directory_test.go index 351897296..4df5a031b 100644 --- a/unixfs/io/directory_test.go +++ b/unixfs/io/directory_test.go @@ -5,8 +5,8 @@ import ( "fmt" "testing" - mdtest "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" + mdtest "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" ) func TestEmptyNode(t *testing.T) { diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 3860b7c96..93c852e62 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -6,9 +6,9 @@ import ( "fmt" "io" - mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" ftpb "github.com/ipfs/go-ipfs/unixfs/pb" + mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index b1e574ae9..68476d9ed 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -3,9 +3,9 @@ package io import ( "context" - dag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" hamt "github.com/ipfs/go-ipfs/unixfs/hamt" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index f8aa5ce63..f87e35534 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -10,9 +10,9 @@ import ( help "github.com/ipfs/go-ipfs/importer/helpers" trickle "github.com/ipfs/go-ipfs/importer/trickle" - mdag "github.com/ipfs/go-ipfs/merkledag" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" + mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 05d66756e..b5e243f3a 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -10,9 +10,9 @@ import ( h "github.com/ipfs/go-ipfs/importer/helpers" trickle "github.com/ipfs/go-ipfs/importer/trickle" - mdag "github.com/ipfs/go-ipfs/merkledag" - mdagmock "github.com/ipfs/go-ipfs/merkledag/test" ft "github.com/ipfs/go-ipfs/unixfs" + mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + mdagmock "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index 3e3344af3..cbae3ea0f 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -8,8 +8,8 @@ import ( proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - dag "github.com/ipfs/go-ipfs/merkledag" pb "github.com/ipfs/go-ipfs/unixfs/pb" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" ) // Shorthands for protobuffer types From b93c24d251ed3f08af48c5a6897555b6fbf8a3f7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 28 Jul 2018 00:50:53 -0700 Subject: [PATCH 2471/3817] Extract dagservice, move dagutils to top level License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-filestore@b48dc8421e69a281c48a416ebd0d1b9031b73bd5 --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index de7cf87dd..596b9ac47 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "github.com/ipfs/go-ipfs/merkledag" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" From fb1f729d4d7c4e1b1c053488c76fdc7479a066f7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 28 Jul 2018 00:50:53 -0700 Subject: [PATCH 2472/3817] Extract dagservice, move dagutils to top level License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@b1b6ab54c0212722dbe69a13012196e7f1e31c27 --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 2 +- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index eb30016ee..abf644a09 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -7,9 +7,9 @@ import ( "fmt" "strings" - dag "github.com/ipfs/go-ipfs/merkledag" pin "github.com/ipfs/go-ipfs/pin" bserv "gx/ipfs/QmNqRBAhovtf4jVd5cF7YvHaFSsQHHZBaUFwGQWPM2CV7R/go-blockservice" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" "gx/ipfs/QmQwgv79RHrRnoXmhnpC1BPtY55HHeneGMpPwmmBU1fUAG/go-verifcid" offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index faa6e1f9d..72407b984 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "github.com/ipfs/go-ipfs/merkledag" + mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index ae6ace111..9e43f5dcf 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - mdag "github.com/ipfs/go-ipfs/merkledag" bs "gx/ipfs/QmNqRBAhovtf4jVd5cF7YvHaFSsQHHZBaUFwGQWPM2CV7R/go-blockservice" + mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 31ea21bd8..a205a4e7e 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -9,8 +9,8 @@ import ( "hash/fnv" "sort" - "github.com/ipfs/go-ipfs/merkledag" "github.com/ipfs/go-ipfs/pin/internal/pb" + "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index c477a71d7..eeb453a19 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - dag "github.com/ipfs/go-ipfs/merkledag" bserv "gx/ipfs/QmNqRBAhovtf4jVd5cF7YvHaFSsQHHZBaUFwGQWPM2CV7R/go-blockservice" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" From c0127306b8eff01fc2073e1dfd144773777e6f07 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 28 Jul 2018 23:37:36 -0700 Subject: [PATCH 2473/3817] extractify This commit was moved from ipfs/go-path@a1a3981476082cc67ebce25af853ee767f3dfc04 --- path/LICENSE | 21 +++++++++++++++++++++ path/README.md | 32 ++++++++++++++++++++++++++++++++ path/resolver/resolver_test.go | 6 ++++-- 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 path/LICENSE create mode 100644 path/README.md diff --git a/path/LICENSE b/path/LICENSE new file mode 100644 index 000000000..7d5dcac4d --- /dev/null +++ b/path/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2018 Juan Batiz-Benet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/path/README.md b/path/README.md new file mode 100644 index 000000000..79dd92892 --- /dev/null +++ b/path/README.md @@ -0,0 +1,32 @@ +go-path +================== + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) +[![Coverage Status](https://codecov.io/gh/ipfs/go-path/branch/master/graph/badge.svg)](https://codecov.io/gh/ipfs/go-path/branch/master) +[![Travis CI](https://travis-ci.org/ipfs/go-path.svg?branch=master)](https://travis-ci.org/ipfs/go-path) + +> go-path is a helper package that provides utilities for parsing and using ipfs paths + + +## Table of Contents + +- [API](#api) +- [Contribute](#contribute) +- [License](#license) + +## TODO + +This package could probably be merged into go-ipld, or something along those lines. It +doesnt really make sense as its own standalone thing. + +## Contribute + +PRs are welcome! + +Small note: If editing the Readme, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +MIT © Juan Batiz-Benet diff --git a/path/resolver/resolver_test.go b/path/resolver/resolver_test.go index ccf00a4e3..fb2406c33 100644 --- a/path/resolver/resolver_test.go +++ b/path/resolver/resolver_test.go @@ -3,21 +3,23 @@ package resolver_test import ( "context" "fmt" + "math/rand" "testing" + "time" path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/path/resolver" merkledag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" dagmock "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" - util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) func randNode() *merkledag.ProtoNode { node := new(merkledag.ProtoNode) node.SetData(make([]byte, 32)) - util.NewTimeSeededRand().Read(node.Data()) + r := rand.New(rand.NewSource(time.Now().UnixNano())) + r.Read(node.Data()) return node } From 0be95a15c101a9272418c871b4a00d623dda2196 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 28 Jul 2018 23:37:46 -0700 Subject: [PATCH 2474/3817] unrewrite deps This commit was moved from ipfs/go-path@e6bfb99959d58db0db6b4ff78ddf0f1855ffaad3 --- path/path.go | 2 +- path/resolver/resolver.go | 8 ++++---- path/resolver/resolver_test.go | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/path/path.go b/path/path.go index 1608b6ca3..18c187bf0 100644 --- a/path/path.go +++ b/path/path.go @@ -6,7 +6,7 @@ import ( "path" "strings" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + cid "github.com/ipfs/go-cid" ) var ( diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 252fd48ac..05f86b57d 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -8,11 +8,11 @@ import ( "time" path "github.com/ipfs/go-ipfs/path" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + dag "github.com/ipfs/go-merkledag" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" - logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" + logging "github.com/ipfs/go-log" ) var log = logging.Logger("pathresolv") diff --git a/path/resolver/resolver_test.go b/path/resolver/resolver_test.go index fb2406c33..81179e2d3 100644 --- a/path/resolver/resolver_test.go +++ b/path/resolver/resolver_test.go @@ -9,10 +9,10 @@ import ( path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/path/resolver" - merkledag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" - dagmock "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" + merkledag "github.com/ipfs/go-merkledag" + dagmock "github.com/ipfs/go-merkledag/test" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "github.com/ipfs/go-ipld-format" ) func randNode() *merkledag.ProtoNode { From 475ee982fb2ba2bf4cc13e649206133f07ccc275 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 28 Jul 2018 23:53:38 -0700 Subject: [PATCH 2475/3817] fix paths This commit was moved from ipfs/go-path@881f9a3edc388404b0921de9075375ba350f7901 --- path/resolver/resolver.go | 4 ++-- path/resolver/resolver_test.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 05f86b57d..f96c79174 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -7,12 +7,12 @@ import ( "fmt" "time" - path "github.com/ipfs/go-ipfs/path" - dag "github.com/ipfs/go-merkledag" + path "github.com/ipfs/go-path" cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" + dag "github.com/ipfs/go-merkledag" ) var log = logging.Logger("pathresolv") diff --git a/path/resolver/resolver_test.go b/path/resolver/resolver_test.go index 81179e2d3..99e26801d 100644 --- a/path/resolver/resolver_test.go +++ b/path/resolver/resolver_test.go @@ -7,12 +7,12 @@ import ( "testing" "time" - path "github.com/ipfs/go-ipfs/path" - "github.com/ipfs/go-ipfs/path/resolver" - merkledag "github.com/ipfs/go-merkledag" - dagmock "github.com/ipfs/go-merkledag/test" + path "github.com/ipfs/go-path" + "github.com/ipfs/go-path/resolver" ipld "github.com/ipfs/go-ipld-format" + merkledag "github.com/ipfs/go-merkledag" + dagmock "github.com/ipfs/go-merkledag/test" ) func randNode() *merkledag.ProtoNode { From 0844abfc8673796a6b3ec03c1d393ccf96558fe8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 28 Jul 2018 23:57:42 -0700 Subject: [PATCH 2476/3817] Extract path and resolver License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@f9c0c50fc7d59a53a86cb9c5e572a1ca8389d9ae --- mfs/mfs_test.go | 2 +- mfs/ops.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index eeef69a58..40124b91c 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -15,11 +15,11 @@ import ( "time" importer "github.com/ipfs/go-ipfs/importer" - "github.com/ipfs/go-ipfs/path" ft "github.com/ipfs/go-ipfs/unixfs" uio "github.com/ipfs/go-ipfs/unixfs/io" bserv "gx/ipfs/QmNqRBAhovtf4jVd5cF7YvHaFSsQHHZBaUFwGQWPM2CV7R/go-blockservice" dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" diff --git a/mfs/ops.go b/mfs/ops.go index 5ae195651..3ae84058a 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -6,7 +6,7 @@ import ( gopath "path" "strings" - path "github.com/ipfs/go-ipfs/path" + path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" From 2f0067fcfccab00cb8634e8161e964b63c38f5d1 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 28 Jul 2018 23:57:42 -0700 Subject: [PATCH 2477/3817] Extract path and resolver License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@8bda85c77054b5e375669d063bce55ef6de033d9 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 2 +- namesys/proquint.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 525a9afb0..61b788d59 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "github.com/ipfs/go-ipfs/path" + path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index 8249fea14..02d0aa928 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "github.com/ipfs/go-ipfs/path" + path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 1591f16e4..0f8933ab3 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "github.com/ipfs/go-ipfs/path" + path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index 6536ac712..39bbc6e03 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "github.com/ipfs/go-ipfs/path" + path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index fd91af9aa..8dd2ab9ec 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "github.com/ipfs/go-ipfs/path" + path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" record "gx/ipfs/QmVsp2KdPYE6M8ryzCk5KHLo3zprcY5hBDaYx6uPCFUdxA/go-libp2p-record" diff --git a/namesys/namesys.go b/namesys/namesys.go index 6f4b9cf4b..8dc7d146b 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "github.com/ipfs/go-ipfs/path" + path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 76573d6d6..19280408a 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,8 +7,8 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/unixfs" + path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" pstore "gx/ipfs/QmZR2XWVVBCtbgBWnQhWk2xcQfaR3W8faQPriAiaaj7rsr/go-libp2p-peerstore" offroute "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/offline" diff --git a/namesys/proquint.go b/namesys/proquint.go index c065db2d7..31fdb7753 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "github.com/ipfs/go-ipfs/path" + path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 77d593a59..a0da61fee 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,9 +7,9 @@ import ( "sync" "time" - path "github.com/ipfs/go-ipfs/path" pin "github.com/ipfs/go-ipfs/pin" ft "github.com/ipfs/go-ipfs/unixfs" + path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" routing "gx/ipfs/QmZ383TySJVeZWzGnWui6pRcKyYZk9VkKTuW7tmKRWk5au/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index abfb283dd..88038d66d 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "github.com/ipfs/go-ipfs/path" + path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index d26cccb66..7c2455b60 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "github.com/ipfs/go-ipfs/path" + path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" mocknet "gx/ipfs/QmY51bqSM5XgxQZqsBrQcRkKTnCb8EKpJpR9K6Qax7Njco/go-libp2p/p2p/net/mock" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 2809afd35..ac3b656d2 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - path "github.com/ipfs/go-ipfs/path" + path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" mockrouting "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/mock" testutil "gx/ipfs/QmcW4FGAt24fdK1jBgWQn3yP4R9ZLyWQqjozv9QK7epRhL/go-testutil" diff --git a/namesys/routing.go b/namesys/routing.go index 4204ddcde..3a355ddf2 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "github.com/ipfs/go-ipfs/path" + path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" dht "gx/ipfs/QmTktQYCKzQjhxF6dk5xJPRuhHn3JBiKGvMLoiDy1mYmxC/go-libp2p-kad-dht" From 069cd0e0d729e11cf7d9a4374b004e528fca18de Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 28 Jul 2018 23:57:42 -0700 Subject: [PATCH 2478/3817] Extract path and resolver License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/interface-go-ipfs-core@ebc2b3570f9c962cc9b9a8440fe1a88fea42628f --- coreiface/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/path.go b/coreiface/path.go index e097ea3b6..49b8cc5ea 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "github.com/ipfs/go-ipfs/path" + ipfspath "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ) From 85e4d20277e5eab84bcccd65faa3ab419d15b82b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 30 Jul 2018 13:38:28 -0700 Subject: [PATCH 2479/3817] move importers to unixfs directory License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-unixfs@b0eb151c7204da06ba01c245384c1917a7bdc01a --- unixfs/importer/balanced/balanced_test.go | 334 +++++++++++ unixfs/importer/balanced/builder.go | 255 +++++++++ unixfs/importer/helpers/dagbuilder.go | 458 ++++++++++++++++ unixfs/importer/helpers/helpers.go | 173 ++++++ unixfs/importer/importer.go | 34 ++ unixfs/importer/importer_test.go | 118 ++++ unixfs/importer/trickle/trickle_test.go | 640 ++++++++++++++++++++++ unixfs/importer/trickle/trickledag.go | 366 +++++++++++++ 8 files changed, 2378 insertions(+) create mode 100644 unixfs/importer/balanced/balanced_test.go create mode 100644 unixfs/importer/balanced/builder.go create mode 100644 unixfs/importer/helpers/dagbuilder.go create mode 100644 unixfs/importer/helpers/helpers.go create mode 100644 unixfs/importer/importer.go create mode 100644 unixfs/importer/importer_test.go create mode 100644 unixfs/importer/trickle/trickle_test.go create mode 100644 unixfs/importer/trickle/trickledag.go diff --git a/unixfs/importer/balanced/balanced_test.go b/unixfs/importer/balanced/balanced_test.go new file mode 100644 index 000000000..f1f636e56 --- /dev/null +++ b/unixfs/importer/balanced/balanced_test.go @@ -0,0 +1,334 @@ +package balanced + +import ( + "bytes" + "context" + "fmt" + "io" + "io/ioutil" + mrand "math/rand" + "testing" + + h "github.com/ipfs/go-ipfs/importer/helpers" + uio "github.com/ipfs/go-ipfs/unixfs/io" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + mdtest "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" + + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" +) + +// TODO: extract these tests and more as a generic layout test suite + +func buildTestDag(ds ipld.DAGService, spl chunker.Splitter) (*dag.ProtoNode, error) { + dbp := h.DagBuilderParams{ + Dagserv: ds, + Maxlinks: h.DefaultLinksPerBlock, + } + + nd, err := Layout(dbp.New(spl)) + if err != nil { + return nil, err + } + + return nd.(*dag.ProtoNode), nil +} + +func getTestDag(t *testing.T, ds ipld.DAGService, size int64, blksize int64) (*dag.ProtoNode, []byte) { + data := make([]byte, size) + u.NewTimeSeededRand().Read(data) + r := bytes.NewReader(data) + + nd, err := buildTestDag(ds, chunker.NewSizeSplitter(r, blksize)) + if err != nil { + t.Fatal(err) + } + + return nd, data +} + +//Test where calls to read are smaller than the chunk size +func TestSizeBasedSplit(t *testing.T) { + if testing.Short() { + t.SkipNow() + } + + testFileConsistency(t, 32*512, 512) + testFileConsistency(t, 32*4096, 4096) + + // Uneven offset + testFileConsistency(t, 31*4095, 4096) +} + +func testFileConsistency(t *testing.T, nbytes int64, blksize int64) { + ds := mdtest.Mock() + nd, should := getTestDag(t, ds, nbytes, blksize) + + r, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + dagrArrComp(t, r, should) +} + +func TestBuilderConsistency(t *testing.T) { + testFileConsistency(t, 100000, chunker.DefaultBlockSize) +} + +func TestNoChunking(t *testing.T) { + ds := mdtest.Mock() + + nd, should := getTestDag(t, ds, 1000, 2000) + r, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + dagrArrComp(t, r, should) +} + +func TestTwoChunks(t *testing.T) { + ds := mdtest.Mock() + + nd, should := getTestDag(t, ds, 2000, 1000) + r, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + dagrArrComp(t, r, should) +} + +func arrComp(a, b []byte) error { + if len(a) != len(b) { + return fmt.Errorf("arrays differ in length. %d != %d", len(a), len(b)) + } + for i, v := range a { + if v != b[i] { + return fmt.Errorf("arrays differ at index: %d", i) + } + } + return nil +} + +func dagrArrComp(t *testing.T, r io.Reader, should []byte) { + out, err := ioutil.ReadAll(r) + if err != nil { + t.Fatal(err) + } + + if err := arrComp(out, should); err != nil { + t.Fatal(err) + } +} + +func TestIndirectBlocks(t *testing.T) { + ds := mdtest.Mock() + dag, buf := getTestDag(t, ds, 1024*1024, 512) + + reader, err := uio.NewDagReader(context.Background(), dag, ds) + if err != nil { + t.Fatal(err) + } + + out, err := ioutil.ReadAll(reader) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(out, buf) { + t.Fatal("Not equal!") + } +} + +func TestSeekingBasic(t *testing.T) { + nbytes := int64(10 * 1024) + ds := mdtest.Mock() + nd, should := getTestDag(t, ds, nbytes, 500) + + rs, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + start := int64(4000) + n, err := rs.Seek(start, io.SeekStart) + if err != nil { + t.Fatal(err) + } + if n != start { + t.Fatal("Failed to seek to correct offset") + } + + dagrArrComp(t, rs, should[start:]) +} + +func TestSeekToBegin(t *testing.T) { + ds := mdtest.Mock() + nd, should := getTestDag(t, ds, 10*1024, 500) + + rs, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + n, err := io.CopyN(ioutil.Discard, rs, 1024*4) + if err != nil { + t.Fatal(err) + } + if n != 4096 { + t.Fatal("Copy didnt copy enough bytes") + } + + seeked, err := rs.Seek(0, io.SeekStart) + if err != nil { + t.Fatal(err) + } + if seeked != 0 { + t.Fatal("Failed to seek to beginning") + } + + dagrArrComp(t, rs, should) +} + +func TestSeekToAlmostBegin(t *testing.T) { + ds := mdtest.Mock() + nd, should := getTestDag(t, ds, 10*1024, 500) + + rs, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + n, err := io.CopyN(ioutil.Discard, rs, 1024*4) + if err != nil { + t.Fatal(err) + } + if n != 4096 { + t.Fatal("Copy didnt copy enough bytes") + } + + seeked, err := rs.Seek(1, io.SeekStart) + if err != nil { + t.Fatal(err) + } + if seeked != 1 { + t.Fatal("Failed to seek to almost beginning") + } + + dagrArrComp(t, rs, should[1:]) +} + +func TestSeekEnd(t *testing.T) { + nbytes := int64(50 * 1024) + ds := mdtest.Mock() + nd, _ := getTestDag(t, ds, nbytes, 500) + + rs, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + seeked, err := rs.Seek(0, io.SeekEnd) + if err != nil { + t.Fatal(err) + } + if seeked != nbytes { + t.Fatal("Failed to seek to end") + } +} + +func TestSeekEndSingleBlockFile(t *testing.T) { + nbytes := int64(100) + ds := mdtest.Mock() + nd, _ := getTestDag(t, ds, nbytes, 5000) + + rs, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + seeked, err := rs.Seek(0, io.SeekEnd) + if err != nil { + t.Fatal(err) + } + if seeked != nbytes { + t.Fatal("Failed to seek to end") + } +} + +func TestSeekingStress(t *testing.T) { + nbytes := int64(1024 * 1024) + ds := mdtest.Mock() + nd, should := getTestDag(t, ds, nbytes, 1000) + + rs, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + testbuf := make([]byte, nbytes) + for i := 0; i < 50; i++ { + offset := mrand.Intn(int(nbytes)) + l := int(nbytes) - offset + n, err := rs.Seek(int64(offset), io.SeekStart) + if err != nil { + t.Fatal(err) + } + if n != int64(offset) { + t.Fatal("Seek failed to move to correct position") + } + + nread, err := rs.Read(testbuf[:l]) + if err != nil { + t.Fatal(err) + } + if nread != l { + t.Fatal("Failed to read enough bytes") + } + + err = arrComp(testbuf[:l], should[offset:offset+l]) + if err != nil { + t.Fatal(err) + } + } + +} + +func TestSeekingConsistency(t *testing.T) { + nbytes := int64(128 * 1024) + ds := mdtest.Mock() + nd, should := getTestDag(t, ds, nbytes, 500) + + rs, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + out := make([]byte, nbytes) + + for coff := nbytes - 4096; coff >= 0; coff -= 4096 { + t.Log(coff) + n, err := rs.Seek(coff, io.SeekStart) + if err != nil { + t.Fatal(err) + } + if n != coff { + t.Fatal("wasnt able to seek to the right position") + } + nread, err := rs.Read(out[coff : coff+4096]) + if err != nil { + t.Fatal(err) + } + if nread != 4096 { + t.Fatal("didnt read the correct number of bytes") + } + } + + err = arrComp(out, should) + if err != nil { + t.Fatal(err) + } +} diff --git a/unixfs/importer/balanced/builder.go b/unixfs/importer/balanced/builder.go new file mode 100644 index 000000000..feab6cf50 --- /dev/null +++ b/unixfs/importer/balanced/builder.go @@ -0,0 +1,255 @@ +// Package balanced provides methods to build balanced DAGs, which are generalistic +// DAGs in which all leaves (nodes representing chunks of data) are at the same +// distance from the root. Nodes can have only a maximum number of children; to be +// able to store more leaf data nodes balanced DAGs are extended by increasing its +// depth (and having more intermediary nodes). +// +// Internal nodes are always represented by UnixFS nodes (of type `File`) encoded +// inside DAG nodes (see the `go-ipfs/unixfs` package for details of UnixFS). In +// contrast, leaf nodes with data have multiple possible representations: UnixFS +// nodes as above, raw nodes with just the file data (no format) and Filestore +// nodes (that directly link to the file on disk using a format stored on a raw +// node, see the `go-ipfs/filestore` package for details of Filestore.) +// +// In the case the entire file fits into just one node it will be formatted as a +// (single) leaf node (without parent) with the possible representations already +// mentioned. This is the only scenario where the root can be of a type different +// that the UnixFS node. +// +// +-------------+ +// | Root 4 | +// +-------------+ +// | +// +--------------------------+----------------------------+ +// | | +// +-------------+ +-------------+ +// | Node 2 | | Node 5 | +// +-------------+ +-------------+ +// | | +// +-------------+-------------+ +-------------+ +// | | | +// +-------------+ +-------------+ +-------------+ +// | Node 1 | | Node 3 | | Node 6 | +// +-------------+ +-------------+ +-------------+ +// | | | +// +------+------+ +------+------+ +------+ +// | | | | | +// +=========+ +=========+ +=========+ +=========+ +=========+ +// | Chunk 1 | | Chunk 2 | | Chunk 3 | | Chunk 4 | | Chunk 5 | +// +=========+ +=========+ +=========+ +=========+ +=========+ +// +package balanced + +import ( + "errors" + + h "github.com/ipfs/go-ipfs/importer/helpers" + ft "github.com/ipfs/go-ipfs/unixfs" + + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" +) + +// Layout builds a balanced DAG layout. In a balanced DAG of depth 1, leaf nodes +// with data are added to a single `root` until the maximum number of links is +// reached. Then, to continue adding more data leaf nodes, a `newRoot` is created +// pointing to the old `root` (which will now become and intermediary node), +// increasing the depth of the DAG to 2. This will increase the maximum number of +// data leaf nodes the DAG can have (`Maxlinks() ^ depth`). The `fillNodeRec` +// function will add more intermediary child nodes to `newRoot` (which already has +// `root` as child) that in turn will have leaf nodes with data added to them. +// After that process is completed (the maximum number of links is reached), +// `fillNodeRec` will return and the loop will be repeated: the `newRoot` created +// will become the old `root` and a new root will be created again to increase the +// depth of the DAG. The process is repeated until there is no more data to add +// (i.e. the DagBuilderHelper’s Done() function returns true). +// +// The nodes are filled recursively, so the DAG is built from the bottom up. Leaf +// nodes are created first using the chunked file data and its size. The size is +// then bubbled up to the parent (internal) node, which aggregates all the sizes of +// its children and bubbles that combined size up to its parent, and so on up to +// the root. This way, a balanced DAG acts like a B-tree when seeking to a byte +// offset in the file the graph represents: each internal node uses the file size +// of its children as an index when seeking. +// +// `Layout` creates a root and hands it off to be filled: +// +// +-------------+ +// | Root 1 | +// +-------------+ +// | +// ( fillNodeRec fills in the ) +// ( chunks on the root. ) +// | +// +------+------+ +// | | +// + - - - - + + - - - - + +// | Chunk 1 | | Chunk 2 | +// + - - - - + + - - - - + +// +// ↓ +// When the root is full but there's more data... +// ↓ +// +// +-------------+ +// | Root 1 | +// +-------------+ +// | +// +------+------+ +// | | +// +=========+ +=========+ + - - - - + +// | Chunk 1 | | Chunk 2 | | Chunk 3 | +// +=========+ +=========+ + - - - - + +// +// ↓ +// ...Layout's job is to create a new root. +// ↓ +// +// +-------------+ +// | Root 2 | +// +-------------+ +// | +// +-------------+ - - - - - - - - + +// | | +// +-------------+ ( fillNodeRec creates the ) +// | Node 1 | ( branch that connects ) +// +-------------+ ( "Root 2" to "Chunk 3." ) +// | | +// +------+------+ + - - - - -+ +// | | | +// +=========+ +=========+ + - - - - + +// | Chunk 1 | | Chunk 2 | | Chunk 3 | +// +=========+ +=========+ + - - - - + +// +func Layout(db *h.DagBuilderHelper) (ipld.Node, error) { + if db.Done() { + // No data, return just an empty node. + root, err := db.NewLeafNode(nil) + if err != nil { + return nil, err + } + // This works without Filestore support (`ProcessFileStore`). + // TODO: Why? Is there a test case missing? + + return db.AddNodeAndClose(root) + } + + // The first `root` will be a single leaf node with data + // (corner case), after that subsequent `root` nodes will + // always be internal nodes (with a depth > 0) that can + // be handled by the loop. + root, fileSize, err := db.NewLeafDataNode() + if err != nil { + return nil, err + } + + // Each time a DAG of a certain `depth` is filled (because it + // has reached its maximum capacity of `db.Maxlinks()` per node) + // extend it by making it a sub-DAG of a bigger DAG with `depth+1`. + for depth := 1; !db.Done(); depth++ { + + // Add the old `root` as a child of the `newRoot`. + newRoot := db.NewFSNodeOverDag(ft.TFile) + newRoot.AddChild(root, fileSize, db) + + // Fill the `newRoot` (that has the old `root` already as child) + // and make it the current `root` for the next iteration (when + // it will become "old"). + root, fileSize, err = fillNodeRec(db, newRoot, depth) + if err != nil { + return nil, err + } + } + + return db.AddNodeAndClose(root) +} + +// fillNodeRec will "fill" the given internal (non-leaf) `node` with data by +// adding child nodes to it, either leaf data nodes (if `depth` is 1) or more +// internal nodes with higher depth (and calling itself recursively on them +// until *they* are filled with data). The data to fill the node with is +// provided by DagBuilderHelper. +// +// `node` represents a (sub-)DAG root that is being filled. If called recursively, +// it is `nil`, a new node is created. If it has been called from `Layout` (see +// diagram below) it points to the new root (that increases the depth of the DAG), +// it already has a child (the old root). New children will be added to this new +// root, and those children will in turn be filled (calling `fillNodeRec` +// recursively). +// +// +-------------+ +// | `node` | +// | (new root) | +// +-------------+ +// | +// +-------------+ - - - - - - + - - - - - - - - - - - + +// | | | +// +--------------+ + - - - - - + + - - - - - + +// | (old root) | | new child | | | +// +--------------+ + - - - - - + + - - - - - + +// | | | +// +------+------+ + - - + - - - + +// | | | | +// +=========+ +=========+ + - - - - + + - - - - + +// | Chunk 1 | | Chunk 2 | | Chunk 3 | | Chunk 4 | +// +=========+ +=========+ + - - - - + + - - - - + +// +// The `node` to be filled uses the `FSNodeOverDag` abstraction that allows adding +// child nodes without packing/unpacking the UnixFS layer node (having an internal +// `ft.FSNode` cache). +// +// It returns the `ipld.Node` representation of the passed `node` filled with +// children and the `nodeFileSize` with the total size of the file chunk (leaf) +// nodes stored under this node (parent nodes store this to enable efficient +// seeking through the DAG when reading data later). +// +// warning: **children** pinned indirectly, but input node IS NOT pinned. +func fillNodeRec(db *h.DagBuilderHelper, node *h.FSNodeOverDag, depth int) (filledNode ipld.Node, nodeFileSize uint64, err error) { + if depth < 1 { + return nil, 0, errors.New("attempt to fillNode at depth < 1") + } + + if node == nil { + node = db.NewFSNodeOverDag(ft.TFile) + } + + // Child node created on every iteration to add to parent `node`. + // It can be a leaf node or another internal node. + var childNode ipld.Node + // File size from the child node needed to update the `FSNode` + // in `node` when adding the child. + var childFileSize uint64 + + // While we have room and there is data available to be added. + for node.NumChildren() < db.Maxlinks() && !db.Done() { + + if depth == 1 { + // Base case: add leaf node with data. + childNode, childFileSize, err = db.NewLeafDataNode() + if err != nil { + return nil, 0, err + } + } else { + // Recursion case: create an internal node to in turn keep + // descending in the DAG and adding child nodes to it. + childNode, childFileSize, err = fillNodeRec(db, nil, depth-1) + if err != nil { + return nil, 0, err + } + } + + err = node.AddChild(childNode, childFileSize, db) + if err != nil { + return nil, 0, err + } + } + + nodeFileSize = node.FileSize() + + // Get the final `dag.ProtoNode` with the `FSNode` data encoded inside. + filledNode, err = node.Commit() + if err != nil { + return nil, 0, err + } + + return filledNode, nodeFileSize, nil +} diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go new file mode 100644 index 000000000..4b37a7f03 --- /dev/null +++ b/unixfs/importer/helpers/dagbuilder.go @@ -0,0 +1,458 @@ +package helpers + +import ( + "context" + "io" + "os" + + ft "github.com/ipfs/go-ipfs/unixfs" + pb "github.com/ipfs/go-ipfs/unixfs/pb" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + + pi "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" + chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + files "gx/ipfs/QmdE4gMduCKCGAcczM2F5ioYDfdeKuPix138wrES1YSr7f/go-ipfs-cmdkit/files" +) + +// DagBuilderHelper wraps together a bunch of objects needed to +// efficiently create unixfs dag trees +type DagBuilderHelper struct { + dserv ipld.DAGService + spl chunker.Splitter + recvdErr error + rawLeaves bool + nextData []byte // the next item to return. + maxlinks int + batch *ipld.Batch + prefix *cid.Prefix + + // Filestore support variables. + // ---------------------------- + // TODO: Encapsulate in `FilestoreNode` (which is basically what they are). + // + // Besides having the path this variable (if set) is used as a flag + // to indicate that Filestore should be used. + fullPath string + stat os.FileInfo + // Keeps track of the current file size added to the DAG (used in + // the balanced builder). It is assumed that the `DagBuilderHelper` + // is not reused to construct another DAG, but a new one (with a + // zero `offset`) is created. + offset uint64 +} + +// DagBuilderParams wraps configuration options to create a DagBuilderHelper +// from a chunker.Splitter. +type DagBuilderParams struct { + // Maximum number of links per intermediate node + Maxlinks int + + // RawLeaves signifies that the importer should use raw ipld nodes as leaves + // instead of using the unixfs TRaw type + RawLeaves bool + + // CID Prefix to use if set + Prefix *cid.Prefix + + // DAGService to write blocks to (required) + Dagserv ipld.DAGService + + // NoCopy signals to the chunker that it should track fileinfo for + // filestore adds + NoCopy bool + + // URL if non-empty (and NoCopy is also true) indicates that the + // file will not be stored in the datastore but instead retrieved + // from this location via the urlstore. + URL string +} + +// New generates a new DagBuilderHelper from the given params and a given +// chunker.Splitter as data source. +func (dbp *DagBuilderParams) New(spl chunker.Splitter) *DagBuilderHelper { + db := &DagBuilderHelper{ + dserv: dbp.Dagserv, + spl: spl, + rawLeaves: dbp.RawLeaves, + prefix: dbp.Prefix, + maxlinks: dbp.Maxlinks, + batch: ipld.NewBatch(context.TODO(), dbp.Dagserv), + } + if fi, ok := spl.Reader().(files.FileInfo); dbp.NoCopy && ok { + db.fullPath = fi.AbsPath() + db.stat = fi.Stat() + } + + if dbp.URL != "" && dbp.NoCopy { + db.fullPath = dbp.URL + } + return db +} + +// prepareNext consumes the next item from the splitter and puts it +// in the nextData field. it is idempotent-- if nextData is full +// it will do nothing. +func (db *DagBuilderHelper) prepareNext() { + // if we already have data waiting to be consumed, we're ready + if db.nextData != nil || db.recvdErr != nil { + return + } + + db.nextData, db.recvdErr = db.spl.NextBytes() + if db.recvdErr == io.EOF { + db.recvdErr = nil + } +} + +// Done returns whether or not we're done consuming the incoming data. +func (db *DagBuilderHelper) Done() bool { + // ensure we have an accurate perspective on data + // as `done` this may be called before `next`. + db.prepareNext() // idempotent + if db.recvdErr != nil { + return false + } + return db.nextData == nil +} + +// Next returns the next chunk of data to be inserted into the dag +// if it returns nil, that signifies that the stream is at an end, and +// that the current building operation should finish. +func (db *DagBuilderHelper) Next() ([]byte, error) { + db.prepareNext() // idempotent + d := db.nextData + db.nextData = nil // signal we've consumed it + if db.recvdErr != nil { + return nil, db.recvdErr + } + return d, nil +} + +// GetDagServ returns the dagservice object this Helper is using +func (db *DagBuilderHelper) GetDagServ() ipld.DAGService { + return db.dserv +} + +// NewUnixfsNode creates a new Unixfs node to represent a file. +func (db *DagBuilderHelper) NewUnixfsNode() *UnixfsNode { + n := &UnixfsNode{ + node: new(dag.ProtoNode), + ufmt: ft.NewFSNode(ft.TFile), + } + n.SetPrefix(db.prefix) + return n +} + +// GetPrefix returns the internal `cid.Prefix` set in the builder. +func (db *DagBuilderHelper) GetPrefix() *cid.Prefix { + return db.prefix +} + +// NewLeaf creates a leaf node filled with data. If rawLeaves is +// defined than a raw leaf will be returned. Otherwise, if data is +// nil the type field will be TRaw (for backwards compatibility), if +// data is defined (but possibly empty) the type field will be TRaw. +func (db *DagBuilderHelper) NewLeaf(data []byte) (*UnixfsNode, error) { + if len(data) > BlockSizeLimit { + return nil, ErrSizeLimitExceeded + } + + if db.rawLeaves { + if db.prefix == nil { + return &UnixfsNode{ + rawnode: dag.NewRawNode(data), + raw: true, + }, nil + } + rawnode, err := dag.NewRawNodeWPrefix(data, *db.prefix) + if err != nil { + return nil, err + } + return &UnixfsNode{ + rawnode: rawnode, + raw: true, + }, nil + } + + if data == nil { + return db.NewUnixfsNode(), nil + } + + blk := db.newUnixfsBlock() + blk.SetData(data) + return blk, nil +} + +// NewLeafNode is a variation from `NewLeaf` (see its description) that +// returns an `ipld.Node` instead. +func (db *DagBuilderHelper) NewLeafNode(data []byte) (ipld.Node, error) { + if len(data) > BlockSizeLimit { + return nil, ErrSizeLimitExceeded + } + + if db.rawLeaves { + // Encapsulate the data in a raw node. + if db.prefix == nil { + return dag.NewRawNode(data), nil + } + rawnode, err := dag.NewRawNodeWPrefix(data, *db.prefix) + if err != nil { + return nil, err + } + return rawnode, nil + } + + // Encapsulate the data in UnixFS node (instead of a raw node). + fsNodeOverDag := db.NewFSNodeOverDag(ft.TFile) + fsNodeOverDag.SetFileData(data) + node, err := fsNodeOverDag.Commit() + if err != nil { + return nil, err + } + // TODO: Encapsulate this sequence of calls into a function that + // just returns the final `ipld.Node` avoiding going through + // `FSNodeOverDag`. + // TODO: Using `TFile` for backwards-compatibility, a bug in the + // balanced builder was causing the leaf nodes to be generated + // with this type instead of `TRaw`, the one that should be used + // (like the trickle builder does). + // (See https://github.com/ipfs/go-ipfs/pull/5120.) + + return node, nil +} + +// newUnixfsBlock creates a new Unixfs node to represent a raw data block +func (db *DagBuilderHelper) newUnixfsBlock() *UnixfsNode { + n := &UnixfsNode{ + node: new(dag.ProtoNode), + ufmt: ft.NewFSNode(ft.TRaw), + } + n.SetPrefix(db.prefix) + return n +} + +// FillNodeLayer will add datanodes as children to the give node until +// at most db.indirSize nodes are added. +func (db *DagBuilderHelper) FillNodeLayer(node *UnixfsNode) error { + + // while we have room AND we're not done + for node.NumChildren() < db.maxlinks && !db.Done() { + child, err := db.GetNextDataNode() + if err != nil { + return err + } + + if err := node.AddChild(child, db); err != nil { + return err + } + } + + return nil +} + +// GetNextDataNode builds a UnixFsNode with the data obtained from the +// Splitter, given the constraints (BlockSizeLimit, RawLeaves) specified +// when creating the DagBuilderHelper. +func (db *DagBuilderHelper) GetNextDataNode() (*UnixfsNode, error) { + data, err := db.Next() + if err != nil { + return nil, err + } + + if data == nil { // we're done! + return nil, nil + } + + return db.NewLeaf(data) +} + +// NewLeafDataNode is a variation of `GetNextDataNode` that returns +// an `ipld.Node` instead. It builds the `node` with the data obtained +// from the Splitter and returns it with the `dataSize` (that will be +// used to keep track of the DAG file size). The size of the data is +// computed here because after that it will be hidden by `NewLeafNode` +// inside a generic `ipld.Node` representation. +func (db *DagBuilderHelper) NewLeafDataNode() (node ipld.Node, dataSize uint64, err error) { + fileData, err := db.Next() + if err != nil { + return nil, 0, err + } + dataSize = uint64(len(fileData)) + + // Create a new leaf node containing the file chunk data. + node, err = db.NewLeafNode(fileData) + if err != nil { + return nil, 0, err + } + + // Convert this leaf to a `FilestoreNode` if needed. + node = db.ProcessFileStore(node, dataSize) + + return node, dataSize, nil +} + +// ProcessFileStore generates, if Filestore is being used, the +// `FilestoreNode` representation of the `ipld.Node` that +// contains the file data. If Filestore is not being used just +// return the same node to continue with its addition to the DAG. +// +// The `db.offset` is updated at this point (instead of when +// `NewLeafDataNode` is called, both work in tandem but the +// offset is more related to this function). +func (db *DagBuilderHelper) ProcessFileStore(node ipld.Node, dataSize uint64) ipld.Node { + // Check if Filestore is being used. + if db.fullPath != "" { + // Check if the node is actually a raw node (needed for + // Filestore support). + if _, ok := node.(*dag.RawNode); ok { + fn := &pi.FilestoreNode{ + Node: node, + PosInfo: &pi.PosInfo{ + Offset: db.offset, + FullPath: db.fullPath, + Stat: db.stat, + }, + } + + // Update `offset` with the size of the data generated by `db.Next`. + db.offset += dataSize + + return fn + } + } + + // Filestore is not used, return the same `node` argument. + return node +} + +// Add sends a node to the DAGService, and returns it. +func (db *DagBuilderHelper) Add(node *UnixfsNode) (ipld.Node, error) { + dn, err := node.GetDagNode() + if err != nil { + return nil, err + } + + err = db.dserv.Add(context.TODO(), dn) + if err != nil { + return nil, err + } + + return dn, nil +} + +// Maxlinks returns the configured maximum number for links +// for nodes built with this helper. +func (db *DagBuilderHelper) Maxlinks() int { + return db.maxlinks +} + +// Close has the DAGService perform a batch Commit operation. +// It should be called at the end of the building process to make +// sure all data is persisted. +func (db *DagBuilderHelper) Close() error { + return db.batch.Commit() +} + +// AddNodeAndClose adds the last `ipld.Node` from the DAG and +// closes the builder. It returns the same `node` passed as +// argument. +func (db *DagBuilderHelper) AddNodeAndClose(node ipld.Node) (ipld.Node, error) { + err := db.batch.Add(node) + if err != nil { + return nil, err + } + + err = db.Close() + if err != nil { + return nil, err + } + + return node, nil +} + +// FSNodeOverDag encapsulates an `unixfs.FSNode` that will be stored in a +// `dag.ProtoNode`. Instead of just having a single `ipld.Node` that +// would need to be constantly (un)packed to access and modify its +// internal `FSNode` in the process of creating a UnixFS DAG, this +// structure stores an `FSNode` cache to manipulate it (add child nodes) +// directly , and only when the node has reached its final (immutable) state +// (signaled by calling `Commit()`) is it committed to a single (indivisible) +// `ipld.Node`. +// +// It is used mainly for internal (non-leaf) nodes, and for some +// representations of data leaf nodes (that don't use raw nodes or +// Filestore). +// +// It aims to replace the `UnixfsNode` structure which encapsulated too +// many possible node state combinations. +// +// TODO: Revisit the name. +type FSNodeOverDag struct { + dag *dag.ProtoNode + file *ft.FSNode +} + +// NewFSNodeOverDag creates a new `dag.ProtoNode` and `ft.FSNode` +// decoupled from one onther (and will continue in that way until +// `Commit` is called), with `fsNodeType` specifying the type of +// the UnixFS layer node (either `File` or `Raw`). +func (db *DagBuilderHelper) NewFSNodeOverDag(fsNodeType pb.Data_DataType) *FSNodeOverDag { + node := new(FSNodeOverDag) + node.dag = new(dag.ProtoNode) + node.dag.SetPrefix(db.GetPrefix()) + + node.file = ft.NewFSNode(fsNodeType) + + return node +} + +// AddChild adds a `child` `ipld.Node` to both node layers. The +// `dag.ProtoNode` creates a link to the child node while the +// `ft.FSNode` stores its file size (that is, not the size of the +// node but the size of the file data that it is storing at the +// UnixFS layer). The child is also stored in the `DAGService`. +func (n *FSNodeOverDag) AddChild(child ipld.Node, fileSize uint64, db *DagBuilderHelper) error { + err := n.dag.AddNodeLink("", child) + if err != nil { + return err + } + + n.file.AddBlockSize(fileSize) + + return db.batch.Add(child) +} + +// Commit unifies (resolves) the cache nodes into a single `ipld.Node` +// that represents them: the `ft.FSNode` is encoded inside the +// `dag.ProtoNode`. +// +// TODO: Evaluate making it read-only after committing. +func (n *FSNodeOverDag) Commit() (ipld.Node, error) { + fileData, err := n.file.GetBytes() + if err != nil { + return nil, err + } + n.dag.SetData(fileData) + + return n.dag, nil +} + +// NumChildren returns the number of children of the `ft.FSNode`. +func (n *FSNodeOverDag) NumChildren() int { + return n.file.NumChildren() +} + +// FileSize returns the `Filesize` attribute from the underlying +// representation of the `ft.FSNode`. +func (n *FSNodeOverDag) FileSize() uint64 { + return n.file.FileSize() +} + +// SetFileData stores the `fileData` in the `ft.FSNode`. It +// should be used only when `FSNodeOverDag` represents a leaf +// node (internal nodes don't carry data, just file sizes). +func (n *FSNodeOverDag) SetFileData(fileData []byte) { + n.file.SetData(fileData) +} diff --git a/unixfs/importer/helpers/helpers.go b/unixfs/importer/helpers/helpers.go new file mode 100644 index 000000000..462dc1dcf --- /dev/null +++ b/unixfs/importer/helpers/helpers.go @@ -0,0 +1,173 @@ +package helpers + +import ( + "context" + "fmt" + "os" + + ft "github.com/ipfs/go-ipfs/unixfs" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + + pi "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" +) + +// BlockSizeLimit specifies the maximum size an imported block can have. +var BlockSizeLimit = 1048576 // 1 MB + +// rough estimates on expected sizes +var roughLinkBlockSize = 1 << 13 // 8KB +var roughLinkSize = 34 + 8 + 5 // sha256 multihash + size + no name + protobuf framing + +// DefaultLinksPerBlock governs how the importer decides how many links there +// will be per block. This calculation is based on expected distributions of: +// * the expected distribution of block sizes +// * the expected distribution of link sizes +// * desired access speed +// For now, we use: +// +// var roughLinkBlockSize = 1 << 13 // 8KB +// var roughLinkSize = 288 // sha256 + framing + name +// var DefaultLinksPerBlock = (roughLinkBlockSize / roughLinkSize) +// +// See calc_test.go +var DefaultLinksPerBlock = roughLinkBlockSize / roughLinkSize + +// ErrSizeLimitExceeded signals that a block is larger than BlockSizeLimit. +var ErrSizeLimitExceeded = fmt.Errorf("object size limit exceeded") + +// UnixfsNode is a struct created to aid in the generation +// of unixfs DAG trees +type UnixfsNode struct { + raw bool + rawnode *dag.RawNode + node *dag.ProtoNode + ufmt *ft.FSNode + posInfo *pi.PosInfo +} + +// NewUnixfsNodeFromDag reconstructs a Unixfs node from a given dag node +func NewUnixfsNodeFromDag(nd *dag.ProtoNode) (*UnixfsNode, error) { + mb, err := ft.FSNodeFromBytes(nd.Data()) + if err != nil { + return nil, err + } + + return &UnixfsNode{ + node: nd, + ufmt: mb, + }, nil +} + +// SetPrefix sets the CID Prefix +func (n *UnixfsNode) SetPrefix(prefix *cid.Prefix) { + n.node.SetPrefix(prefix) +} + +// NumChildren returns the number of children referenced by this UnixfsNode. +func (n *UnixfsNode) NumChildren() int { + return n.ufmt.NumChildren() +} + +// GetChild gets the ith child of this node from the given DAGService. +func (n *UnixfsNode) GetChild(ctx context.Context, i int, ds ipld.DAGService) (*UnixfsNode, error) { + nd, err := n.node.Links()[i].GetNode(ctx, ds) + if err != nil { + return nil, err + } + + pbn, ok := nd.(*dag.ProtoNode) + if !ok { + return nil, dag.ErrNotProtobuf + } + + return NewUnixfsNodeFromDag(pbn) +} + +// AddChild adds the given UnixfsNode as a child of the receiver. +// The passed in DagBuilderHelper is used to store the child node an +// pin it locally so it doesnt get lost. +func (n *UnixfsNode) AddChild(child *UnixfsNode, db *DagBuilderHelper) error { + n.ufmt.AddBlockSize(child.FileSize()) + + childnode, err := child.GetDagNode() + if err != nil { + return err + } + + // Add a link to this node without storing a reference to the memory + // This way, we avoid nodes building up and consuming all of our RAM + err = n.node.AddNodeLink("", childnode) + if err != nil { + return err + } + + err = db.batch.Add(childnode) + + return err +} + +// RemoveChild deletes the child node at the given index. +func (n *UnixfsNode) RemoveChild(index int, dbh *DagBuilderHelper) { + n.ufmt.RemoveBlockSize(index) + n.node.SetLinks(append(n.node.Links()[:index], n.node.Links()[index+1:]...)) +} + +// SetData stores data in this node. +func (n *UnixfsNode) SetData(data []byte) { + n.ufmt.SetData(data) +} + +// FileSize returns the total file size of this tree (including children) +// In the case of raw nodes, it returns the length of the +// raw data. +func (n *UnixfsNode) FileSize() uint64 { + if n.raw { + return uint64(len(n.rawnode.RawData())) + } + return n.ufmt.FileSize() +} + +// SetPosInfo sets information about the offset of the data of this node in a +// filesystem file. +func (n *UnixfsNode) SetPosInfo(offset uint64, fullPath string, stat os.FileInfo) { + n.posInfo = &pi.PosInfo{ + Offset: offset, + FullPath: fullPath, + Stat: stat, + } +} + +// GetDagNode fills out the proper formatting for the unixfs node +// inside of a DAG node and returns the dag node. +func (n *UnixfsNode) GetDagNode() (ipld.Node, error) { + nd, err := n.getBaseDagNode() + if err != nil { + return nil, err + } + + if n.posInfo != nil { + if rn, ok := nd.(*dag.RawNode); ok { + return &pi.FilestoreNode{ + Node: rn, + PosInfo: n.posInfo, + }, nil + } + } + + return nd, nil +} + +func (n *UnixfsNode) getBaseDagNode() (ipld.Node, error) { + if n.raw { + return n.rawnode, nil + } + + data, err := n.ufmt.GetBytes() + if err != nil { + return nil, err + } + n.node.SetData(data) + return n.node, nil +} diff --git a/unixfs/importer/importer.go b/unixfs/importer/importer.go new file mode 100644 index 000000000..cc5028ee2 --- /dev/null +++ b/unixfs/importer/importer.go @@ -0,0 +1,34 @@ +// Package importer implements utilities used to create IPFS DAGs from files +// and readers. +package importer + +import ( + chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + + bal "github.com/ipfs/go-ipfs/importer/balanced" + h "github.com/ipfs/go-ipfs/importer/helpers" + trickle "github.com/ipfs/go-ipfs/importer/trickle" +) + +// BuildDagFromReader creates a DAG given a DAGService and a Splitter +// implementation (Splitters are io.Readers), using a Balanced layout. +func BuildDagFromReader(ds ipld.DAGService, spl chunker.Splitter) (ipld.Node, error) { + dbp := h.DagBuilderParams{ + Dagserv: ds, + Maxlinks: h.DefaultLinksPerBlock, + } + + return bal.Layout(dbp.New(spl)) +} + +// BuildTrickleDagFromReader creates a DAG given a DAGService and a Splitter +// implementation (Splitters are io.Readers), using a Trickle Layout. +func BuildTrickleDagFromReader(ds ipld.DAGService, spl chunker.Splitter) (ipld.Node, error) { + dbp := h.DagBuilderParams{ + Dagserv: ds, + Maxlinks: h.DefaultLinksPerBlock, + } + + return trickle.Layout(dbp.New(spl)) +} diff --git a/unixfs/importer/importer_test.go b/unixfs/importer/importer_test.go new file mode 100644 index 000000000..0b9c75ce8 --- /dev/null +++ b/unixfs/importer/importer_test.go @@ -0,0 +1,118 @@ +package importer + +import ( + "bytes" + "context" + "io" + "io/ioutil" + "testing" + + uio "github.com/ipfs/go-ipfs/unixfs/io" + mdtest "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" + + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" +) + +func getBalancedDag(t testing.TB, size int64, blksize int64) (ipld.Node, ipld.DAGService) { + ds := mdtest.Mock() + r := io.LimitReader(u.NewTimeSeededRand(), size) + nd, err := BuildDagFromReader(ds, chunker.NewSizeSplitter(r, blksize)) + if err != nil { + t.Fatal(err) + } + return nd, ds +} + +func getTrickleDag(t testing.TB, size int64, blksize int64) (ipld.Node, ipld.DAGService) { + ds := mdtest.Mock() + r := io.LimitReader(u.NewTimeSeededRand(), size) + nd, err := BuildTrickleDagFromReader(ds, chunker.NewSizeSplitter(r, blksize)) + if err != nil { + t.Fatal(err) + } + return nd, ds +} + +func TestBalancedDag(t *testing.T) { + ds := mdtest.Mock() + buf := make([]byte, 10000) + u.NewTimeSeededRand().Read(buf) + r := bytes.NewReader(buf) + + nd, err := BuildDagFromReader(ds, chunker.DefaultSplitter(r)) + if err != nil { + t.Fatal(err) + } + + dr, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + out, err := ioutil.ReadAll(dr) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(out, buf) { + t.Fatal("bad read") + } +} + +func BenchmarkBalancedReadSmallBlock(b *testing.B) { + b.StopTimer() + nbytes := int64(10000000) + nd, ds := getBalancedDag(b, nbytes, 4096) + + b.SetBytes(nbytes) + b.StartTimer() + runReadBench(b, nd, ds) +} + +func BenchmarkTrickleReadSmallBlock(b *testing.B) { + b.StopTimer() + nbytes := int64(10000000) + nd, ds := getTrickleDag(b, nbytes, 4096) + + b.SetBytes(nbytes) + b.StartTimer() + runReadBench(b, nd, ds) +} + +func BenchmarkBalancedReadFull(b *testing.B) { + b.StopTimer() + nbytes := int64(10000000) + nd, ds := getBalancedDag(b, nbytes, chunker.DefaultBlockSize) + + b.SetBytes(nbytes) + b.StartTimer() + runReadBench(b, nd, ds) +} + +func BenchmarkTrickleReadFull(b *testing.B) { + b.StopTimer() + nbytes := int64(10000000) + nd, ds := getTrickleDag(b, nbytes, chunker.DefaultBlockSize) + + b.SetBytes(nbytes) + b.StartTimer() + runReadBench(b, nd, ds) +} + +func runReadBench(b *testing.B, nd ipld.Node, ds ipld.DAGService) { + for i := 0; i < b.N; i++ { + ctx, cancel := context.WithCancel(context.Background()) + read, err := uio.NewDagReader(ctx, nd, ds) + if err != nil { + b.Fatal(err) + } + + _, err = read.WriteTo(ioutil.Discard) + if err != nil && err != io.EOF { + b.Fatal(err) + } + cancel() + } +} diff --git a/unixfs/importer/trickle/trickle_test.go b/unixfs/importer/trickle/trickle_test.go new file mode 100644 index 000000000..097ae32e9 --- /dev/null +++ b/unixfs/importer/trickle/trickle_test.go @@ -0,0 +1,640 @@ +package trickle + +import ( + "bytes" + "context" + "fmt" + "io" + "io/ioutil" + mrand "math/rand" + "testing" + + h "github.com/ipfs/go-ipfs/importer/helpers" + ft "github.com/ipfs/go-ipfs/unixfs" + uio "github.com/ipfs/go-ipfs/unixfs/io" + merkledag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + mdtest "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" + + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" +) + +type UseRawLeaves bool + +const ( + ProtoBufLeaves UseRawLeaves = false + RawLeaves UseRawLeaves = true +) + +func runBothSubtests(t *testing.T, tfunc func(*testing.T, UseRawLeaves)) { + t.Run("leaves=ProtoBuf", func(t *testing.T) { tfunc(t, ProtoBufLeaves) }) + t.Run("leaves=Raw", func(t *testing.T) { tfunc(t, RawLeaves) }) +} + +func buildTestDag(ds ipld.DAGService, spl chunker.Splitter, rawLeaves UseRawLeaves) (*merkledag.ProtoNode, error) { + dbp := h.DagBuilderParams{ + Dagserv: ds, + Maxlinks: h.DefaultLinksPerBlock, + RawLeaves: bool(rawLeaves), + } + + nd, err := Layout(dbp.New(spl)) + if err != nil { + return nil, err + } + + pbnd, ok := nd.(*merkledag.ProtoNode) + if !ok { + return nil, merkledag.ErrNotProtobuf + } + + return pbnd, VerifyTrickleDagStructure(pbnd, VerifyParams{ + Getter: ds, + Direct: dbp.Maxlinks, + LayerRepeat: layerRepeat, + RawLeaves: bool(rawLeaves), + }) +} + +//Test where calls to read are smaller than the chunk size +func TestSizeBasedSplit(t *testing.T) { + runBothSubtests(t, testSizeBasedSplit) +} + +func testSizeBasedSplit(t *testing.T, rawLeaves UseRawLeaves) { + if testing.Short() { + t.SkipNow() + } + bs := chunker.SizeSplitterGen(512) + testFileConsistency(t, bs, 32*512, rawLeaves) + + bs = chunker.SizeSplitterGen(4096) + testFileConsistency(t, bs, 32*4096, rawLeaves) + + // Uneven offset + testFileConsistency(t, bs, 31*4095, rawLeaves) +} + +func dup(b []byte) []byte { + o := make([]byte, len(b)) + copy(o, b) + return o +} + +func testFileConsistency(t *testing.T, bs chunker.SplitterGen, nbytes int, rawLeaves UseRawLeaves) { + should := make([]byte, nbytes) + u.NewTimeSeededRand().Read(should) + + read := bytes.NewReader(should) + ds := mdtest.Mock() + nd, err := buildTestDag(ds, bs(read), rawLeaves) + if err != nil { + t.Fatal(err) + } + + r, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + out, err := ioutil.ReadAll(r) + if err != nil { + t.Fatal(err) + } + + err = arrComp(out, should) + if err != nil { + t.Fatal(err) + } +} + +func TestBuilderConsistency(t *testing.T) { + runBothSubtests(t, testBuilderConsistency) +} + +func testBuilderConsistency(t *testing.T, rawLeaves UseRawLeaves) { + nbytes := 100000 + buf := new(bytes.Buffer) + io.CopyN(buf, u.NewTimeSeededRand(), int64(nbytes)) + should := dup(buf.Bytes()) + dagserv := mdtest.Mock() + nd, err := buildTestDag(dagserv, chunker.DefaultSplitter(buf), rawLeaves) + if err != nil { + t.Fatal(err) + } + r, err := uio.NewDagReader(context.Background(), nd, dagserv) + if err != nil { + t.Fatal(err) + } + + out, err := ioutil.ReadAll(r) + if err != nil { + t.Fatal(err) + } + + err = arrComp(out, should) + if err != nil { + t.Fatal(err) + } +} + +func arrComp(a, b []byte) error { + if len(a) != len(b) { + return fmt.Errorf("arrays differ in length. %d != %d", len(a), len(b)) + } + for i, v := range a { + if v != b[i] { + return fmt.Errorf("arrays differ at index: %d", i) + } + } + return nil +} + +func TestIndirectBlocks(t *testing.T) { + runBothSubtests(t, testIndirectBlocks) +} + +func testIndirectBlocks(t *testing.T, rawLeaves UseRawLeaves) { + splitter := chunker.SizeSplitterGen(512) + nbytes := 1024 * 1024 + buf := make([]byte, nbytes) + u.NewTimeSeededRand().Read(buf) + + read := bytes.NewReader(buf) + + ds := mdtest.Mock() + dag, err := buildTestDag(ds, splitter(read), rawLeaves) + if err != nil { + t.Fatal(err) + } + + reader, err := uio.NewDagReader(context.Background(), dag, ds) + if err != nil { + t.Fatal(err) + } + + out, err := ioutil.ReadAll(reader) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(out, buf) { + t.Fatal("Not equal!") + } +} + +func TestSeekingBasic(t *testing.T) { + runBothSubtests(t, testSeekingBasic) +} + +func testSeekingBasic(t *testing.T, rawLeaves UseRawLeaves) { + nbytes := int64(10 * 1024) + should := make([]byte, nbytes) + u.NewTimeSeededRand().Read(should) + + read := bytes.NewReader(should) + ds := mdtest.Mock() + nd, err := buildTestDag(ds, chunker.NewSizeSplitter(read, 512), rawLeaves) + if err != nil { + t.Fatal(err) + } + + rs, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + start := int64(4000) + n, err := rs.Seek(start, io.SeekStart) + if err != nil { + t.Fatal(err) + } + if n != start { + t.Fatal("Failed to seek to correct offset") + } + + out, err := ioutil.ReadAll(rs) + if err != nil { + t.Fatal(err) + } + + err = arrComp(out, should[start:]) + if err != nil { + t.Fatal(err) + } +} + +func TestSeekToBegin(t *testing.T) { + runBothSubtests(t, testSeekToBegin) +} + +func testSeekToBegin(t *testing.T, rawLeaves UseRawLeaves) { + nbytes := int64(10 * 1024) + should := make([]byte, nbytes) + u.NewTimeSeededRand().Read(should) + + read := bytes.NewReader(should) + ds := mdtest.Mock() + nd, err := buildTestDag(ds, chunker.NewSizeSplitter(read, 500), rawLeaves) + if err != nil { + t.Fatal(err) + } + + rs, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + n, err := io.CopyN(ioutil.Discard, rs, 1024*4) + if err != nil { + t.Fatal(err) + } + if n != 4096 { + t.Fatal("Copy didnt copy enough bytes") + } + + seeked, err := rs.Seek(0, io.SeekStart) + if err != nil { + t.Fatal(err) + } + if seeked != 0 { + t.Fatal("Failed to seek to beginning") + } + + out, err := ioutil.ReadAll(rs) + if err != nil { + t.Fatal(err) + } + + err = arrComp(out, should) + if err != nil { + t.Fatal(err) + } +} + +func TestSeekToAlmostBegin(t *testing.T) { + runBothSubtests(t, testSeekToAlmostBegin) +} + +func testSeekToAlmostBegin(t *testing.T, rawLeaves UseRawLeaves) { + nbytes := int64(10 * 1024) + should := make([]byte, nbytes) + u.NewTimeSeededRand().Read(should) + + read := bytes.NewReader(should) + ds := mdtest.Mock() + nd, err := buildTestDag(ds, chunker.NewSizeSplitter(read, 500), rawLeaves) + if err != nil { + t.Fatal(err) + } + + rs, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + n, err := io.CopyN(ioutil.Discard, rs, 1024*4) + if err != nil { + t.Fatal(err) + } + if n != 4096 { + t.Fatal("Copy didnt copy enough bytes") + } + + seeked, err := rs.Seek(1, io.SeekStart) + if err != nil { + t.Fatal(err) + } + if seeked != 1 { + t.Fatal("Failed to seek to almost beginning") + } + + out, err := ioutil.ReadAll(rs) + if err != nil { + t.Fatal(err) + } + + err = arrComp(out, should[1:]) + if err != nil { + t.Fatal(err) + } +} + +func TestSeekEnd(t *testing.T) { + runBothSubtests(t, testSeekEnd) +} + +func testSeekEnd(t *testing.T, rawLeaves UseRawLeaves) { + nbytes := int64(50 * 1024) + should := make([]byte, nbytes) + u.NewTimeSeededRand().Read(should) + + read := bytes.NewReader(should) + ds := mdtest.Mock() + nd, err := buildTestDag(ds, chunker.NewSizeSplitter(read, 500), rawLeaves) + if err != nil { + t.Fatal(err) + } + + rs, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + seeked, err := rs.Seek(0, io.SeekEnd) + if err != nil { + t.Fatal(err) + } + if seeked != nbytes { + t.Fatal("Failed to seek to end") + } +} + +func TestSeekEndSingleBlockFile(t *testing.T) { + runBothSubtests(t, testSeekEndSingleBlockFile) +} + +func testSeekEndSingleBlockFile(t *testing.T, rawLeaves UseRawLeaves) { + nbytes := int64(100) + should := make([]byte, nbytes) + u.NewTimeSeededRand().Read(should) + + read := bytes.NewReader(should) + ds := mdtest.Mock() + nd, err := buildTestDag(ds, chunker.NewSizeSplitter(read, 5000), rawLeaves) + if err != nil { + t.Fatal(err) + } + + rs, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + seeked, err := rs.Seek(0, io.SeekEnd) + if err != nil { + t.Fatal(err) + } + if seeked != nbytes { + t.Fatal("Failed to seek to end") + } +} + +func TestSeekingStress(t *testing.T) { + runBothSubtests(t, testSeekingStress) +} + +func testSeekingStress(t *testing.T, rawLeaves UseRawLeaves) { + nbytes := int64(1024 * 1024) + should := make([]byte, nbytes) + u.NewTimeSeededRand().Read(should) + + read := bytes.NewReader(should) + ds := mdtest.Mock() + nd, err := buildTestDag(ds, chunker.NewSizeSplitter(read, 1000), rawLeaves) + if err != nil { + t.Fatal(err) + } + + rs, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + testbuf := make([]byte, nbytes) + for i := 0; i < 50; i++ { + offset := mrand.Intn(int(nbytes)) + l := int(nbytes) - offset + n, err := rs.Seek(int64(offset), io.SeekStart) + if err != nil { + t.Fatal(err) + } + if n != int64(offset) { + t.Fatal("Seek failed to move to correct position") + } + + nread, err := rs.Read(testbuf[:l]) + if err != nil { + t.Fatal(err) + } + if nread != l { + t.Fatal("Failed to read enough bytes") + } + + err = arrComp(testbuf[:l], should[offset:offset+l]) + if err != nil { + t.Fatal(err) + } + } + +} + +func TestSeekingConsistency(t *testing.T) { + runBothSubtests(t, testSeekingConsistency) +} + +func testSeekingConsistency(t *testing.T, rawLeaves UseRawLeaves) { + nbytes := int64(128 * 1024) + should := make([]byte, nbytes) + u.NewTimeSeededRand().Read(should) + + read := bytes.NewReader(should) + ds := mdtest.Mock() + nd, err := buildTestDag(ds, chunker.NewSizeSplitter(read, 500), rawLeaves) + if err != nil { + t.Fatal(err) + } + + rs, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + out := make([]byte, nbytes) + + for coff := nbytes - 4096; coff >= 0; coff -= 4096 { + t.Log(coff) + n, err := rs.Seek(coff, io.SeekStart) + if err != nil { + t.Fatal(err) + } + if n != coff { + t.Fatal("wasnt able to seek to the right position") + } + nread, err := rs.Read(out[coff : coff+4096]) + if err != nil { + t.Fatal(err) + } + if nread != 4096 { + t.Fatal("didnt read the correct number of bytes") + } + } + + err = arrComp(out, should) + if err != nil { + t.Fatal(err) + } +} + +func TestAppend(t *testing.T) { + runBothSubtests(t, testAppend) +} + +func testAppend(t *testing.T, rawLeaves UseRawLeaves) { + nbytes := int64(128 * 1024) + should := make([]byte, nbytes) + u.NewTimeSeededRand().Read(should) + + // Reader for half the bytes + read := bytes.NewReader(should[:nbytes/2]) + ds := mdtest.Mock() + nd, err := buildTestDag(ds, chunker.NewSizeSplitter(read, 500), rawLeaves) + if err != nil { + t.Fatal(err) + } + + dbp := &h.DagBuilderParams{ + Dagserv: ds, + Maxlinks: h.DefaultLinksPerBlock, + RawLeaves: bool(rawLeaves), + } + + r := bytes.NewReader(should[nbytes/2:]) + + ctx := context.Background() + nnode, err := Append(ctx, nd, dbp.New(chunker.NewSizeSplitter(r, 500))) + if err != nil { + t.Fatal(err) + } + + err = VerifyTrickleDagStructure(nnode, VerifyParams{ + Getter: ds, + Direct: dbp.Maxlinks, + LayerRepeat: layerRepeat, + RawLeaves: bool(rawLeaves), + }) + if err != nil { + t.Fatal(err) + } + + fread, err := uio.NewDagReader(ctx, nnode, ds) + if err != nil { + t.Fatal(err) + } + + out, err := ioutil.ReadAll(fread) + if err != nil { + t.Fatal(err) + } + + err = arrComp(out, should) + if err != nil { + t.Fatal(err) + } +} + +// This test appends one byte at a time to an empty file +func TestMultipleAppends(t *testing.T) { + runBothSubtests(t, testMultipleAppends) +} + +func testMultipleAppends(t *testing.T, rawLeaves UseRawLeaves) { + ds := mdtest.Mock() + + // TODO: fix small size appends and make this number bigger + nbytes := int64(1000) + should := make([]byte, nbytes) + u.NewTimeSeededRand().Read(should) + + read := bytes.NewReader(nil) + nd, err := buildTestDag(ds, chunker.NewSizeSplitter(read, 500), rawLeaves) + if err != nil { + t.Fatal(err) + } + + dbp := &h.DagBuilderParams{ + Dagserv: ds, + Maxlinks: 4, + RawLeaves: bool(rawLeaves), + } + + spl := chunker.SizeSplitterGen(500) + + ctx := context.Background() + for i := 0; i < len(should); i++ { + + nnode, err := Append(ctx, nd, dbp.New(spl(bytes.NewReader(should[i:i+1])))) + if err != nil { + t.Fatal(err) + } + + err = VerifyTrickleDagStructure(nnode, VerifyParams{ + Getter: ds, + Direct: dbp.Maxlinks, + LayerRepeat: layerRepeat, + RawLeaves: bool(rawLeaves), + }) + if err != nil { + t.Fatal(err) + } + + fread, err := uio.NewDagReader(ctx, nnode, ds) + if err != nil { + t.Fatal(err) + } + + out, err := ioutil.ReadAll(fread) + if err != nil { + t.Fatal(err) + } + + err = arrComp(out, should[:i+1]) + if err != nil { + t.Fatal(err) + } + } +} + +func TestAppendSingleBytesToEmpty(t *testing.T) { + ds := mdtest.Mock() + + data := []byte("AB") + + nd := new(merkledag.ProtoNode) + nd.SetData(ft.FilePBData(nil, 0)) + + dbp := &h.DagBuilderParams{ + Dagserv: ds, + Maxlinks: 4, + } + + spl := chunker.SizeSplitterGen(500) + + ctx := context.Background() + nnode, err := Append(ctx, nd, dbp.New(spl(bytes.NewReader(data[:1])))) + if err != nil { + t.Fatal(err) + } + + nnode, err = Append(ctx, nnode, dbp.New(spl(bytes.NewReader(data[1:])))) + if err != nil { + t.Fatal(err) + } + + fread, err := uio.NewDagReader(ctx, nnode, ds) + if err != nil { + t.Fatal(err) + } + + out, err := ioutil.ReadAll(fread) + if err != nil { + t.Fatal(err) + } + + fmt.Println(out, data) + err = arrComp(out, data) + if err != nil { + t.Fatal(err) + } +} diff --git a/unixfs/importer/trickle/trickledag.go b/unixfs/importer/trickle/trickledag.go new file mode 100644 index 000000000..1bba05f71 --- /dev/null +++ b/unixfs/importer/trickle/trickledag.go @@ -0,0 +1,366 @@ +// Package trickle allows to build trickle DAGs. +// In this type of DAG, non-leave nodes are first filled +// with data leaves, and then incorporate "layers" of subtrees +// as additional links. +// +// Each layer is a trickle sub-tree and is limited by an increasing +// maximum depth. Thus, the nodes first layer +// can only hold leaves (depth 1) but subsequent layers can grow deeper. +// By default, this module places 4 nodes per layer (that is, 4 subtrees +// of the same maximum depth before increasing it). +// +// Trickle DAGs are very good for sequentially reading data, as the +// first data leaves are directly reachable from the root and those +// coming next are always nearby. They are +// suited for things like streaming applications. +package trickle + +import ( + "context" + "errors" + "fmt" + + h "github.com/ipfs/go-ipfs/importer/helpers" + ft "github.com/ipfs/go-ipfs/unixfs" + dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" +) + +// layerRepeat specifies how many times to append a child tree of a +// given depth. Higher values increase the width of a given node, which +// improves seek speeds. +const layerRepeat = 4 + +// Layout builds a new DAG with the trickle format using the provided +// DagBuilderHelper. See the module's description for a more detailed +// explanation. +func Layout(db *h.DagBuilderHelper) (ipld.Node, error) { + root := db.NewUnixfsNode() + if err := fillTrickleRec(db, root, -1); err != nil { + return nil, err + } + + out, err := db.Add(root) + if err != nil { + return nil, err + } + + if err := db.Close(); err != nil { + return nil, err + } + + return out, nil +} + +// fillTrickleRec creates a trickle (sub-)tree with an optional maximum specified depth +// in the case maxDepth is greater than zero, or with unlimited depth otherwise +// (where the DAG builder will signal the end of data to end the function). +func fillTrickleRec(db *h.DagBuilderHelper, node *h.UnixfsNode, maxDepth int) error { + // Always do this, even in the base case + if err := db.FillNodeLayer(node); err != nil { + return err + } + + for depth := 1; ; depth++ { + // Apply depth limit only if the parameter is set (> 0). + if maxDepth > 0 && depth == maxDepth { + return nil + } + for layer := 0; layer < layerRepeat; layer++ { + if db.Done() { + return nil + } + + nextChild := db.NewUnixfsNode() + if err := fillTrickleRec(db, nextChild, depth); err != nil { + return err + } + + if err := node.AddChild(nextChild, db); err != nil { + return err + } + } + } +} + +// Append appends the data in `db` to the dag, using the Trickledag format +func Append(ctx context.Context, basen ipld.Node, db *h.DagBuilderHelper) (out ipld.Node, errOut error) { + base, ok := basen.(*dag.ProtoNode) + if !ok { + return nil, dag.ErrNotProtobuf + } + + defer func() { + if errOut == nil { + if err := db.Close(); err != nil { + errOut = err + } + } + }() + + // Convert to unixfs node for working with easily + ufsn, err := h.NewUnixfsNodeFromDag(base) + if err != nil { + return nil, err + } + + // Get depth of this 'tree' + n, layerProgress := trickleDepthInfo(ufsn, db.Maxlinks()) + if n == 0 { + // If direct blocks not filled... + if err := db.FillNodeLayer(ufsn); err != nil { + return nil, err + } + + if db.Done() { + return ufsn.GetDagNode() + } + + // If continuing, our depth has increased by one + n++ + } + + // Last child in this node may not be a full tree, lets file it up + if err := appendFillLastChild(ctx, ufsn, n-1, layerProgress, db); err != nil { + return nil, err + } + + // after appendFillLastChild, our depth is now increased by one + if !db.Done() { + n++ + } + + // Now, continue filling out tree like normal + for i := n; !db.Done(); i++ { + for j := 0; j < layerRepeat && !db.Done(); j++ { + next := db.NewUnixfsNode() + err := fillTrickleRec(db, next, i) + if err != nil { + return nil, err + } + + err = ufsn.AddChild(next, db) + if err != nil { + return nil, err + } + } + } + + return ufsn.GetDagNode() +} + +// appendFillLastChild will take in an incomplete trickledag node (uncomplete meaning, not full) and +// fill it out to the specified depth with blocks from the given DagBuilderHelper +func appendFillLastChild(ctx context.Context, ufsn *h.UnixfsNode, depth int, layerFill int, db *h.DagBuilderHelper) error { + if ufsn.NumChildren() <= db.Maxlinks() { + return nil + } + // Recursive step, grab last child + last := ufsn.NumChildren() - 1 + lastChild, err := ufsn.GetChild(ctx, last, db.GetDagServ()) + if err != nil { + return err + } + + // Fill out last child (may not be full tree) + nchild, err := appendRec(ctx, lastChild, db, depth-1) + if err != nil { + return err + } + + // Update changed child in parent node + ufsn.RemoveChild(last, db) + err = ufsn.AddChild(nchild, db) + if err != nil { + return err + } + + // Partially filled depth layer + if layerFill != 0 { + for ; layerFill < layerRepeat && !db.Done(); layerFill++ { + next := db.NewUnixfsNode() + err := fillTrickleRec(db, next, depth) + if err != nil { + return err + } + + err = ufsn.AddChild(next, db) + if err != nil { + return err + } + } + } + + return nil +} + +// recursive call for Append +func appendRec(ctx context.Context, ufsn *h.UnixfsNode, db *h.DagBuilderHelper, depth int) (*h.UnixfsNode, error) { + if depth == 0 || db.Done() { + return ufsn, nil + } + + // Get depth of this 'tree' + n, layerProgress := trickleDepthInfo(ufsn, db.Maxlinks()) + if n == 0 { + // If direct blocks not filled... + if err := db.FillNodeLayer(ufsn); err != nil { + return nil, err + } + n++ + } + + // If at correct depth, no need to continue + if n == depth { + return ufsn, nil + } + + if err := appendFillLastChild(ctx, ufsn, n, layerProgress, db); err != nil { + return nil, err + } + + // after appendFillLastChild, our depth is now increased by one + if !db.Done() { + n++ + } + + // Now, continue filling out tree like normal + for i := n; i < depth && !db.Done(); i++ { + for j := 0; j < layerRepeat && !db.Done(); j++ { + next := db.NewUnixfsNode() + if err := fillTrickleRec(db, next, i); err != nil { + return nil, err + } + + if err := ufsn.AddChild(next, db); err != nil { + return nil, err + } + } + } + + return ufsn, nil +} + +func trickleDepthInfo(node *h.UnixfsNode, maxlinks int) (int, int) { + n := node.NumChildren() + if n < maxlinks { + return 0, 0 + } + + return ((n - maxlinks) / layerRepeat) + 1, (n - maxlinks) % layerRepeat +} + +// VerifyParams is used by VerifyTrickleDagStructure +type VerifyParams struct { + Getter ipld.NodeGetter + Direct int + LayerRepeat int + Prefix *cid.Prefix + RawLeaves bool +} + +// VerifyTrickleDagStructure checks that the given dag matches exactly the trickle dag datastructure +// layout +func VerifyTrickleDagStructure(nd ipld.Node, p VerifyParams) error { + return verifyTDagRec(nd, -1, p) +} + +// Recursive call for verifying the structure of a trickledag +func verifyTDagRec(n ipld.Node, depth int, p VerifyParams) error { + codec := cid.DagProtobuf + if depth == 0 { + if len(n.Links()) > 0 { + return errors.New("expected direct block") + } + // zero depth dag is raw data block + switch nd := n.(type) { + case *dag.ProtoNode: + pbn, err := ft.FromBytes(nd.Data()) + if err != nil { + return err + } + + if pbn.GetType() != ft.TRaw { + return errors.New("expected raw block") + } + + if p.RawLeaves { + return errors.New("expected raw leaf, got a protobuf node") + } + case *dag.RawNode: + if !p.RawLeaves { + return errors.New("expected protobuf node as leaf") + } + codec = cid.Raw + default: + return errors.New("expected ProtoNode or RawNode") + } + } + + // verify prefix + if p.Prefix != nil { + prefix := n.Cid().Prefix() + expect := *p.Prefix // make a copy + expect.Codec = uint64(codec) + if codec == cid.Raw && expect.Version == 0 { + expect.Version = 1 + } + if expect.MhLength == -1 { + expect.MhLength = prefix.MhLength + } + if prefix != expect { + return fmt.Errorf("unexpected cid prefix: expected: %v; got %v", expect, prefix) + } + } + + if depth == 0 { + return nil + } + + nd, ok := n.(*dag.ProtoNode) + if !ok { + return errors.New("expected ProtoNode") + } + + // Verify this is a branch node + pbn, err := ft.FromBytes(nd.Data()) + if err != nil { + return err + } + + if pbn.GetType() != ft.TFile { + return fmt.Errorf("expected file as branch node, got: %s", pbn.GetType()) + } + + if len(pbn.Data) > 0 { + return errors.New("branch node should not have data") + } + + for i := 0; i < len(nd.Links()); i++ { + child, err := nd.Links()[i].GetNode(context.TODO(), p.Getter) + if err != nil { + return err + } + + if i < p.Direct { + // Direct blocks + err := verifyTDagRec(child, 0, p) + if err != nil { + return err + } + } else { + // Recursive trickle dags + rdepth := ((i - p.Direct) / p.LayerRepeat) + 1 + if rdepth >= depth && depth > 0 { + return errors.New("child dag was too deep") + } + err := verifyTDagRec(child, rdepth, p) + if err != nil { + return err + } + } + } + return nil +} From ece006dc44a122bf762187c57b1a9db0f2a25fcb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 30 Jul 2018 14:08:11 -0700 Subject: [PATCH 2480/3817] packageify This commit was moved from ipfs/go-unixfs@827e1b6e1cbeaa83ea98fa4ff657909df7acc2db --- unixfs/LICENSE | 21 ++++++++ unixfs/README.md | 63 +++++++++++++++++++++++ unixfs/archive/archive.go | 6 +-- unixfs/archive/tar/writer.go | 10 ++-- unixfs/hamt/hamt.go | 18 +++---- unixfs/hamt/hamt_stress_test.go | 6 +-- unixfs/hamt/hamt_test.go | 8 +-- unixfs/importer/balanced/balanced_test.go | 16 +++--- unixfs/importer/balanced/builder.go | 8 +-- unixfs/importer/helpers/dagbuilder.go | 18 +++---- unixfs/importer/helpers/helpers.go | 10 ++-- unixfs/importer/importer.go | 10 ++-- unixfs/importer/importer_test.go | 10 ++-- unixfs/importer/trickle/trickle_test.go | 18 +++---- unixfs/importer/trickle/trickledag.go | 10 ++-- unixfs/io/dagreader.go | 8 +-- unixfs/io/dagreader_test.go | 6 +-- unixfs/io/directory.go | 10 ++-- unixfs/io/directory_test.go | 4 +- unixfs/io/pbdagreader.go | 10 ++-- unixfs/io/resolve.go | 8 +-- unixfs/mod/dagmodifier.go | 20 +++---- unixfs/mod/dagmodifier_test.go | 11 ++-- unixfs/pb/unixfs.pb.go | 2 +- unixfs/test/utils.go | 22 ++++---- unixfs/unixfs.go | 6 +-- unixfs/unixfs_test.go | 4 +- 27 files changed, 213 insertions(+), 130 deletions(-) create mode 100644 unixfs/LICENSE create mode 100644 unixfs/README.md diff --git a/unixfs/LICENSE b/unixfs/LICENSE new file mode 100644 index 000000000..7d5dcac4d --- /dev/null +++ b/unixfs/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2018 Juan Batiz-Benet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/unixfs/README.md b/unixfs/README.md new file mode 100644 index 000000000..9a22fbd3a --- /dev/null +++ b/unixfs/README.md @@ -0,0 +1,63 @@ +go-unixfs +================== + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) +[![Coverage Status](https://codecov.io/gh/ipfs/go-unixfs/branch/master/graph/badge.svg)](https://codecov.io/gh/ipfs/go-unixfs/branch/master) +[![Travis CI](https://travis-ci.org/ipfs/go-unixfs.svg?branch=master)](https://travis-ci.org/ipfs/go-unixfs) + +> go-unixfs implements unix-like filesystem utilities on top of an ipld merkledag + + +## Table of Contents + +- [Directory](#directory) +- [Install](#install) +- [Contribute](#contribute) +- [License](#license) + +## Package Directory +This package contains many subpackages, each of which can be very large on its own. + +### Top Level +The top level unixfs package defines the unixfs format datastructures, and some helper methods around it. + +### importers +The `importer` subpackage is what you'll use when you want to turn a normal file into a unixfs file. + +### io +The `io` subpackage provides helpers for reading files and manipulating directories. The `DagReader` takes a +reference to a unixfs file and returns a file handle that can be read from and seeked through. The `Directory` +interface allows you to easily read items in a directory, add items to a directory, and do lookups. + +### mod +The `mod` subpackage implements a `DagModifier` type that can be used to write to an existing unixfs file, or +create a new one. The logic for this is significantly more complicated than for the dagreader, so its a separate +type. (TODO: maybe it still belongs in the `io` subpackage though?) + +### hamt +The `hamt` subpackage implements a CHAMP hamt that is used in unixfs directory sharding. + +### archive +The `archive` subpackage implements a `tar` importer and exporter. The objects created here are not officially unixfs, +but in the future, this may be integrated more directly. + +### test +The `test` subpackage provides several utilities to make testing unixfs related things easier. + +## Install + +```sh +go get github.com/ipfs/go-unixfs +``` + +## Contribute + +PRs are welcome! + +Small note: If editing the Readme, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +MIT © Juan Batiz-Benet diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index 96c12f682..6396ca0ae 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -8,10 +8,10 @@ import ( "io" "path" - tar "github.com/ipfs/go-ipfs/unixfs/archive/tar" - uio "github.com/ipfs/go-ipfs/unixfs/io" + tar "github.com/ipfs/go-unixfs/archive/tar" + uio "github.com/ipfs/go-unixfs/io" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "github.com/ipfs/go-ipld-format" ) // DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index d0a30c0d6..7e20e6d77 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -10,12 +10,12 @@ import ( "path" "time" - ft "github.com/ipfs/go-ipfs/unixfs" - uio "github.com/ipfs/go-ipfs/unixfs/io" - upb "github.com/ipfs/go-ipfs/unixfs/pb" - mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + mdag "github.com/ipfs/go-merkledag" + ft "github.com/ipfs/go-unixfs" + uio "github.com/ipfs/go-unixfs/io" + upb "github.com/ipfs/go-unixfs/pb" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "github.com/ipfs/go-ipld-format" ) // Writer is a utility structure that helps to write diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index f4013884c..6887a148d 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -25,15 +25,15 @@ import ( "fmt" "os" - format "github.com/ipfs/go-ipfs/unixfs" - upb "github.com/ipfs/go-ipfs/unixfs/pb" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" - - bitfield "gx/ipfs/QmTbBs3Y3u5F69XNJzdnnc6SP5GKgcXxCDzx6w8m6piVRT/go-bitfield" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" - "gx/ipfs/QmfJHywXQu98UeZtGJBQrPAR6AtmDjjbe3qjTo9piXHPnx/murmur3" + dag "github.com/ipfs/go-merkledag" + format "github.com/ipfs/go-unixfs" + upb "github.com/ipfs/go-unixfs/pb" + + bitfield "github.com/Stebalien/go-bitfield" + proto "github.com/gogo/protobuf/proto" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" + "github.com/spaolacci/murmur3" ) const ( diff --git a/unixfs/hamt/hamt_stress_test.go b/unixfs/hamt/hamt_stress_test.go index 311f6f0d3..10eb15281 100644 --- a/unixfs/hamt/hamt_stress_test.go +++ b/unixfs/hamt/hamt_stress_test.go @@ -8,10 +8,10 @@ import ( "testing" "time" - ft "github.com/ipfs/go-ipfs/unixfs" - mdtest "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" + mdtest "github.com/ipfs/go-merkledag/test" + ft "github.com/ipfs/go-unixfs" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "github.com/ipfs/go-ipld-format" ) func getNames(prefix string, count int) []string { diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index ed6eaf70d..655365261 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -10,11 +10,11 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - ft "github.com/ipfs/go-ipfs/unixfs" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" - mdtest "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" + dag "github.com/ipfs/go-merkledag" + mdtest "github.com/ipfs/go-merkledag/test" + ft "github.com/ipfs/go-unixfs" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "github.com/ipfs/go-ipld-format" ) func shuffle(seed int64, arr []string) { diff --git a/unixfs/importer/balanced/balanced_test.go b/unixfs/importer/balanced/balanced_test.go index f1f636e56..1f135b781 100644 --- a/unixfs/importer/balanced/balanced_test.go +++ b/unixfs/importer/balanced/balanced_test.go @@ -9,14 +9,14 @@ import ( mrand "math/rand" "testing" - h "github.com/ipfs/go-ipfs/importer/helpers" - uio "github.com/ipfs/go-ipfs/unixfs/io" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" - mdtest "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" - - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + h "github.com/ipfs/go-unixfs/importer/helpers" + uio "github.com/ipfs/go-unixfs/io" + + chunker "github.com/ipfs/go-ipfs-chunker" + u "github.com/ipfs/go-ipfs-util" + ipld "github.com/ipfs/go-ipld-format" + dag "github.com/ipfs/go-merkledag" + mdtest "github.com/ipfs/go-merkledag/test" ) // TODO: extract these tests and more as a generic layout test suite diff --git a/unixfs/importer/balanced/builder.go b/unixfs/importer/balanced/builder.go index feab6cf50..55b9ccb36 100644 --- a/unixfs/importer/balanced/builder.go +++ b/unixfs/importer/balanced/builder.go @@ -5,7 +5,7 @@ // depth (and having more intermediary nodes). // // Internal nodes are always represented by UnixFS nodes (of type `File`) encoded -// inside DAG nodes (see the `go-ipfs/unixfs` package for details of UnixFS). In +// inside DAG nodes (see the `go-unixfs` package for details of UnixFS). In // contrast, leaf nodes with data have multiple possible representations: UnixFS // nodes as above, raw nodes with just the file data (no format) and Filestore // nodes (that directly link to the file on disk using a format stored on a raw @@ -43,10 +43,10 @@ package balanced import ( "errors" - h "github.com/ipfs/go-ipfs/importer/helpers" - ft "github.com/ipfs/go-ipfs/unixfs" + ft "github.com/ipfs/go-unixfs" + h "github.com/ipfs/go-unixfs/importer/helpers" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "github.com/ipfs/go-ipld-format" ) // Layout builds a balanced DAG layout. In a balanced DAG of depth 1, leaf nodes diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index 4b37a7f03..387b26304 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -5,15 +5,15 @@ import ( "io" "os" - ft "github.com/ipfs/go-ipfs/unixfs" - pb "github.com/ipfs/go-ipfs/unixfs/pb" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" - - pi "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" - chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" - files "gx/ipfs/QmdE4gMduCKCGAcczM2F5ioYDfdeKuPix138wrES1YSr7f/go-ipfs-cmdkit/files" + dag "github.com/ipfs/go-merkledag" + ft "github.com/ipfs/go-unixfs" + pb "github.com/ipfs/go-unixfs/pb" + + cid "github.com/ipfs/go-cid" + chunker "github.com/ipfs/go-ipfs-chunker" + files "github.com/ipfs/go-ipfs-cmdkit/files" + pi "github.com/ipfs/go-ipfs-posinfo" + ipld "github.com/ipfs/go-ipld-format" ) // DagBuilderHelper wraps together a bunch of objects needed to diff --git a/unixfs/importer/helpers/helpers.go b/unixfs/importer/helpers/helpers.go index 462dc1dcf..6fb0f83c8 100644 --- a/unixfs/importer/helpers/helpers.go +++ b/unixfs/importer/helpers/helpers.go @@ -5,12 +5,12 @@ import ( "fmt" "os" - ft "github.com/ipfs/go-ipfs/unixfs" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + dag "github.com/ipfs/go-merkledag" + ft "github.com/ipfs/go-unixfs" - pi "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + cid "github.com/ipfs/go-cid" + pi "github.com/ipfs/go-ipfs-posinfo" + ipld "github.com/ipfs/go-ipld-format" ) // BlockSizeLimit specifies the maximum size an imported block can have. diff --git a/unixfs/importer/importer.go b/unixfs/importer/importer.go index cc5028ee2..ecf016854 100644 --- a/unixfs/importer/importer.go +++ b/unixfs/importer/importer.go @@ -3,12 +3,12 @@ package importer import ( - chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + bal "github.com/ipfs/go-unixfs/importer/balanced" + h "github.com/ipfs/go-unixfs/importer/helpers" + trickle "github.com/ipfs/go-unixfs/importer/trickle" - bal "github.com/ipfs/go-ipfs/importer/balanced" - h "github.com/ipfs/go-ipfs/importer/helpers" - trickle "github.com/ipfs/go-ipfs/importer/trickle" + chunker "github.com/ipfs/go-ipfs-chunker" + ipld "github.com/ipfs/go-ipld-format" ) // BuildDagFromReader creates a DAG given a DAGService and a Splitter diff --git a/unixfs/importer/importer_test.go b/unixfs/importer/importer_test.go index 0b9c75ce8..55f8a9480 100644 --- a/unixfs/importer/importer_test.go +++ b/unixfs/importer/importer_test.go @@ -7,12 +7,12 @@ import ( "io/ioutil" "testing" - uio "github.com/ipfs/go-ipfs/unixfs/io" - mdtest "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" + mdtest "github.com/ipfs/go-merkledag/test" + uio "github.com/ipfs/go-unixfs/io" - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + chunker "github.com/ipfs/go-ipfs-chunker" + u "github.com/ipfs/go-ipfs-util" + ipld "github.com/ipfs/go-ipld-format" ) func getBalancedDag(t testing.TB, size int64, blksize int64) (ipld.Node, ipld.DAGService) { diff --git a/unixfs/importer/trickle/trickle_test.go b/unixfs/importer/trickle/trickle_test.go index 097ae32e9..9c568c986 100644 --- a/unixfs/importer/trickle/trickle_test.go +++ b/unixfs/importer/trickle/trickle_test.go @@ -9,15 +9,15 @@ import ( mrand "math/rand" "testing" - h "github.com/ipfs/go-ipfs/importer/helpers" - ft "github.com/ipfs/go-ipfs/unixfs" - uio "github.com/ipfs/go-ipfs/unixfs/io" - merkledag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" - mdtest "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" - - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ft "github.com/ipfs/go-unixfs" + h "github.com/ipfs/go-unixfs/importer/helpers" + uio "github.com/ipfs/go-unixfs/io" + + chunker "github.com/ipfs/go-ipfs-chunker" + u "github.com/ipfs/go-ipfs-util" + ipld "github.com/ipfs/go-ipld-format" + merkledag "github.com/ipfs/go-merkledag" + mdtest "github.com/ipfs/go-merkledag/test" ) type UseRawLeaves bool diff --git a/unixfs/importer/trickle/trickledag.go b/unixfs/importer/trickle/trickledag.go index 1bba05f71..30c961861 100644 --- a/unixfs/importer/trickle/trickledag.go +++ b/unixfs/importer/trickle/trickledag.go @@ -20,12 +20,12 @@ import ( "errors" "fmt" - h "github.com/ipfs/go-ipfs/importer/helpers" - ft "github.com/ipfs/go-ipfs/unixfs" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + ft "github.com/ipfs/go-unixfs" + h "github.com/ipfs/go-unixfs/importer/helpers" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" + dag "github.com/ipfs/go-merkledag" ) // layerRepeat specifies how many times to append a child tree of a diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 0ef962f51..02bb64afd 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -5,11 +5,11 @@ import ( "errors" "io" - ft "github.com/ipfs/go-ipfs/unixfs" - ftpb "github.com/ipfs/go-ipfs/unixfs/pb" - mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + mdag "github.com/ipfs/go-merkledag" + ft "github.com/ipfs/go-unixfs" + ftpb "github.com/ipfs/go-unixfs/pb" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "github.com/ipfs/go-ipld-format" ) // Common errors diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index f2b0b0af6..6e1fef8d0 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -8,12 +8,12 @@ import ( "strings" "testing" - "github.com/ipfs/go-ipfs/unixfs" - mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + mdag "github.com/ipfs/go-merkledag" + "github.com/ipfs/go-unixfs" context "context" - testu "github.com/ipfs/go-ipfs/unixfs/test" + testu "github.com/ipfs/go-unixfs/test" ) func TestBasicRead(t *testing.T) { diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index 6f07aff04..89864566e 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -5,12 +5,12 @@ import ( "fmt" "os" - format "github.com/ipfs/go-ipfs/unixfs" - hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + mdag "github.com/ipfs/go-merkledag" + format "github.com/ipfs/go-unixfs" + hamt "github.com/ipfs/go-unixfs/hamt" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" ) // UseHAMTSharding is a global flag that signifies whether or not to use the diff --git a/unixfs/io/directory_test.go b/unixfs/io/directory_test.go index 4df5a031b..64a1ef2c6 100644 --- a/unixfs/io/directory_test.go +++ b/unixfs/io/directory_test.go @@ -5,8 +5,8 @@ import ( "fmt" "testing" - ft "github.com/ipfs/go-ipfs/unixfs" - mdtest "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" + mdtest "github.com/ipfs/go-merkledag/test" + ft "github.com/ipfs/go-unixfs" ) func TestEmptyNode(t *testing.T) { diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 93c852e62..8e7872e8e 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -6,12 +6,12 @@ import ( "fmt" "io" - ft "github.com/ipfs/go-ipfs/unixfs" - ftpb "github.com/ipfs/go-ipfs/unixfs/pb" - mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + mdag "github.com/ipfs/go-merkledag" + ft "github.com/ipfs/go-unixfs" + ftpb "github.com/ipfs/go-unixfs/pb" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" ) // PBDagReader provides a way to easily read the data contained in a dag. diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index 68476d9ed..5b0e6783a 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -3,11 +3,11 @@ package io import ( "context" - ft "github.com/ipfs/go-ipfs/unixfs" - hamt "github.com/ipfs/go-ipfs/unixfs/hamt" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + dag "github.com/ipfs/go-merkledag" + ft "github.com/ipfs/go-unixfs" + hamt "github.com/ipfs/go-unixfs/hamt" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "github.com/ipfs/go-ipld-format" ) // ResolveUnixfsOnce resolves a single hop of a path through a graph in a diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index f87e35534..09665b80c 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -8,16 +8,16 @@ import ( "errors" "io" - help "github.com/ipfs/go-ipfs/importer/helpers" - trickle "github.com/ipfs/go-ipfs/importer/trickle" - ft "github.com/ipfs/go-ipfs/unixfs" - uio "github.com/ipfs/go-ipfs/unixfs/io" - mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" - - chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ft "github.com/ipfs/go-unixfs" + help "github.com/ipfs/go-unixfs/importer/helpers" + trickle "github.com/ipfs/go-unixfs/importer/trickle" + uio "github.com/ipfs/go-unixfs/io" + + proto "github.com/gogo/protobuf/proto" + cid "github.com/ipfs/go-cid" + chunker "github.com/ipfs/go-ipfs-chunker" + ipld "github.com/ipfs/go-ipld-format" + mdag "github.com/ipfs/go-merkledag" ) // Common errors diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 3e460aec5..f9e302ee8 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -7,13 +7,12 @@ import ( "io/ioutil" "testing" - h "github.com/ipfs/go-ipfs/importer/helpers" - trickle "github.com/ipfs/go-ipfs/importer/trickle" + h "github.com/ipfs/go-unixfs/importer/helpers" + trickle "github.com/ipfs/go-unixfs/importer/trickle" + uio "github.com/ipfs/go-unixfs/io" + testu "github.com/ipfs/go-unixfs/test" - uio "github.com/ipfs/go-ipfs/unixfs/io" - testu "github.com/ipfs/go-ipfs/unixfs/test" - - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + u "github.com/ipfs/go-ipfs-util" ) func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier, opts testu.NodeOpts) []byte { diff --git a/unixfs/pb/unixfs.pb.go b/unixfs/pb/unixfs.pb.go index e28053031..648b10716 100644 --- a/unixfs/pb/unixfs.pb.go +++ b/unixfs/pb/unixfs.pb.go @@ -14,7 +14,7 @@ It has these top-level messages: */ package unixfs_pb -import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" +import proto "github.com/gogo/protobuf/proto" import fmt "fmt" import math "math" diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index b5e243f3a..cc8fe4dd0 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -8,17 +8,17 @@ import ( "io/ioutil" "testing" - h "github.com/ipfs/go-ipfs/importer/helpers" - trickle "github.com/ipfs/go-ipfs/importer/trickle" - ft "github.com/ipfs/go-ipfs/unixfs" - mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" - mdagmock "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag/test" - - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ft "github.com/ipfs/go-unixfs" + h "github.com/ipfs/go-unixfs/importer/helpers" + trickle "github.com/ipfs/go-unixfs/importer/trickle" + + cid "github.com/ipfs/go-cid" + chunker "github.com/ipfs/go-ipfs-chunker" + u "github.com/ipfs/go-ipfs-util" + ipld "github.com/ipfs/go-ipld-format" + mdag "github.com/ipfs/go-merkledag" + mdagmock "github.com/ipfs/go-merkledag/test" + mh "github.com/multiformats/go-multihash" ) // SizeSplitterGen creates a generator. diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index cbae3ea0f..21f643520 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -6,10 +6,10 @@ package unixfs import ( "errors" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + proto "github.com/gogo/protobuf/proto" - pb "github.com/ipfs/go-ipfs/unixfs/pb" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + dag "github.com/ipfs/go-merkledag" + pb "github.com/ipfs/go-unixfs/pb" ) // Shorthands for protobuffer types diff --git a/unixfs/unixfs_test.go b/unixfs/unixfs_test.go index 967ee0ca8..e04682864 100644 --- a/unixfs/unixfs_test.go +++ b/unixfs/unixfs_test.go @@ -4,9 +4,9 @@ import ( "bytes" "testing" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" + proto "github.com/gogo/protobuf/proto" - pb "github.com/ipfs/go-ipfs/unixfs/pb" + pb "github.com/ipfs/go-unixfs/pb" ) func TestFSNode(t *testing.T) { From 12003c11b12283ef5739ba7e35b5a7400a5f6a7f Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 30 Jul 2018 14:09:25 -0700 Subject: [PATCH 2481/3817] fix import in test This commit was moved from ipfs/go-unixfs@d9052086a249eaf276edfaa687b76bf590732810 --- unixfs/hamt/hamt_test.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 655365261..e56d9363c 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -9,7 +9,6 @@ import ( "testing" "time" - "github.com/ipfs/go-ipfs/dagutils" dag "github.com/ipfs/go-merkledag" mdtest "github.com/ipfs/go-merkledag/test" ft "github.com/ipfs/go-unixfs" @@ -213,7 +212,6 @@ func TestShardReload(t *testing.T) { ndk := nd.Cid() if !outk.Equals(ndk) { - printDiff(ds, nd.(*dag.ProtoNode), ond.(*dag.ProtoNode)) t.Fatal("roundtrip serialization failed") } } @@ -512,17 +510,6 @@ func TestSetHamtChild(t *testing.T) { } } -func printDiff(ds ipld.DAGService, a, b *dag.ProtoNode) { - diff, err := dagutils.Diff(context.TODO(), ds, a, b) - if err != nil { - panic(err) - } - - for _, d := range diff { - fmt.Println(d) - } -} - func BenchmarkHAMTWalk(b *testing.B) { ctx := context.Background() From 18972c643f1c9e89479d5607668c172f571eaa39 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 30 Jul 2018 14:35:15 -0700 Subject: [PATCH 2482/3817] extract go-unixfs (importers and unixfs) License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@9be67fa391514813f8e3668af9370e5269bba490 --- mfs/dir.go | 6 +++--- mfs/fd.go | 2 +- mfs/file.go | 4 ++-- mfs/mfs_test.go | 6 +++--- mfs/system.go | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index b2f9f5dff..c73fbd3c4 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -9,10 +9,10 @@ import ( "sync" "time" - ft "github.com/ipfs/go-ipfs/unixfs" - uio "github.com/ipfs/go-ipfs/unixfs/io" - ufspb "github.com/ipfs/go-ipfs/unixfs/pb" dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + ft "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs" + uio "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs/io" + ufspb "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs/pb" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/mfs/fd.go b/mfs/fd.go index a93a9bb42..e77ccca55 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -4,7 +4,7 @@ import ( "fmt" "io" - mod "github.com/ipfs/go-ipfs/unixfs/mod" + mod "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs/mod" context "context" ) diff --git a/mfs/file.go b/mfs/file.go index 41bd1f2ed..6cbff0182 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -5,9 +5,9 @@ import ( "fmt" "sync" - ft "github.com/ipfs/go-ipfs/unixfs" - mod "github.com/ipfs/go-ipfs/unixfs/mod" dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + ft "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs" + mod "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs/mod" chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 40124b91c..c5fca3ee9 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,11 +14,11 @@ import ( "testing" "time" - importer "github.com/ipfs/go-ipfs/importer" - ft "github.com/ipfs/go-ipfs/unixfs" - uio "github.com/ipfs/go-ipfs/unixfs/io" bserv "gx/ipfs/QmNqRBAhovtf4jVd5cF7YvHaFSsQHHZBaUFwGQWPM2CV7R/go-blockservice" dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + ft "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs" + importer "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs/importer" + uio "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs/io" "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" diff --git a/mfs/system.go b/mfs/system.go index 8fe8f3221..c67192de4 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -16,8 +16,8 @@ import ( "sync" "time" - ft "github.com/ipfs/go-ipfs/unixfs" dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + ft "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" From 559e22355b593605eaebfd21079c2147b342fceb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 30 Jul 2018 14:35:15 -0700 Subject: [PATCH 2483/3817] extract go-unixfs (importers and unixfs) License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@7cf4d7d9019a59b6bc8721cb6d357e631ac53672 --- namesys/namesys_test.go | 2 +- namesys/publisher.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 19280408a..ee9a0b868 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,7 +7,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - "github.com/ipfs/go-ipfs/unixfs" + "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs" path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" pstore "gx/ipfs/QmZR2XWVVBCtbgBWnQhWk2xcQfaR3W8faQPriAiaaj7rsr/go-libp2p-peerstore" diff --git a/namesys/publisher.go b/namesys/publisher.go index a0da61fee..0ab935c92 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -8,7 +8,7 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "github.com/ipfs/go-ipfs/unixfs" + ft "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs" path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" routing "gx/ipfs/QmZ383TySJVeZWzGnWui6pRcKyYZk9VkKTuW7tmKRWk5au/go-libp2p-routing" From 8fa8a1e440ea9d280b5c2b717f1de35602f83f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 2 Feb 2018 16:28:02 +0100 Subject: [PATCH 2484/3817] coreapi: Pin option for Object.Put MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@ca13e9b2ef673c3eb63baecc0cf840036405e214 --- coreiface/options/object.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/coreiface/options/object.go b/coreiface/options/object.go index aca02d672..9257ea607 100644 --- a/coreiface/options/object.go +++ b/coreiface/options/object.go @@ -7,6 +7,7 @@ type ObjectNewSettings struct { type ObjectPutSettings struct { InputEnc string DataType string + Pin bool } type ObjectAddLinkSettings struct { @@ -35,6 +36,7 @@ func ObjectPutOptions(opts ...ObjectPutOption) (*ObjectPutSettings, error) { options := &ObjectPutSettings{ InputEnc: "json", DataType: "text", + Pin: false, } for _, opt := range opts { @@ -103,6 +105,15 @@ func (objectOpts) DataType(t string) ObjectPutOption { } } +// WithPin is an option for Object.Put which specifies whether to pin the added +// objects, default is false +func (objectOpts) WithPin(pin bool) ObjectPutOption { + return func(settings *ObjectPutSettings) error { + settings.Pin = pin + return nil + } +} + // Create is an option for Object.AddLink which specifies whether create required // directories for the child func (objectOpts) Create(create bool) ObjectAddLinkOption { From 83c279f0be6bbc7a498345e8b82b7331b8fb356c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 2 Feb 2018 21:50:22 +0100 Subject: [PATCH 2485/3817] coreapi: implement Object.Diff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@00f6430f32022c0038bb4407cca5d9f136b0ee24 --- coreiface/object.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/coreiface/object.go b/coreiface/object.go index ea9aa5948..0a716dc97 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -31,6 +31,39 @@ type ObjectStat struct { CumulativeSize int } + +const ( + // DiffAdd is a Type of ObjectChange where a link was added to the graph + DiffAdd = iota + + // DiffRemove is a Type of ObjectChange where a link was removed from the graph + DiffRemove + + // DiffMod is a Type of ObjectChange where a link was changed in the graph + DiffMod +) + +// ObjectChange represents a change ia a graph +// TODO: do we want this to be an interface? +type ObjectChange struct { + // Type of the change, either: + // * DiffAdd - Added a link + // * DiffRemove - Removed a link + // * DiffMod - Modified a link + Type int + + // Path to the changed link + Path string + + // Before holds the link path before the change. Note that when a link is + // added, this will be nil. + Before Path + + // After holds the link path after the change. Note that when a link is + // removed, this will be nil. + After Path +} + // ObjectAPI specifies the interface to MerkleDAG and contains useful utilities // for manipulating MerkleDAG data structures. type ObjectAPI interface { @@ -65,4 +98,8 @@ type ObjectAPI interface { // SetData sets the data contained in the node SetData(context.Context, Path, io.Reader) (ResolvedPath, error) + + // Diff returns a set of changes needed to transform the first object into the + // second. + Diff(context.Context, Path, Path) ([]ObjectChange, error) } From 157bb70fa94828c5c37466e6cc75e2058454ed2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 2 Feb 2018 22:36:01 +0100 Subject: [PATCH 2486/3817] commands: switch object commands to CoreAPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@9fe2a845730cb243ba72ceb3a1be7871467b416b --- coreiface/object.go | 5 ++--- coreiface/options/object.go | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/coreiface/object.go b/coreiface/object.go index 0a716dc97..1c7caeb77 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -31,7 +31,6 @@ type ObjectStat struct { CumulativeSize int } - const ( // DiffAdd is a Type of ObjectChange where a link was added to the graph DiffAdd = iota @@ -57,11 +56,11 @@ type ObjectChange struct { // Before holds the link path before the change. Note that when a link is // added, this will be nil. - Before Path + Before ResolvedPath // After holds the link path after the change. Note that when a link is // removed, this will be nil. - After Path + After ResolvedPath } // ObjectAPI specifies the interface to MerkleDAG and contains useful utilities diff --git a/coreiface/options/object.go b/coreiface/options/object.go index 9257ea607..e484a9f36 100644 --- a/coreiface/options/object.go +++ b/coreiface/options/object.go @@ -105,9 +105,9 @@ func (objectOpts) DataType(t string) ObjectPutOption { } } -// WithPin is an option for Object.Put which specifies whether to pin the added +// Pin is an option for Object.Put which specifies whether to pin the added // objects, default is false -func (objectOpts) WithPin(pin bool) ObjectPutOption { +func (objectOpts) Pin(pin bool) ObjectPutOption { return func(settings *ObjectPutSettings) error { settings.Pin = pin return nil From c1cdbfdc59df5b72610ae52ff80a44cb81f679c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 25 Jul 2018 22:49:17 +0200 Subject: [PATCH 2487/3817] object coreapi: Address review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@af43bf0a5b8a8eb409dc5e3731cf86afd5b67304 --- coreiface/object.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/coreiface/object.go b/coreiface/object.go index 1c7caeb77..3eb0ea9ef 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -31,25 +31,27 @@ type ObjectStat struct { CumulativeSize int } +// ChangeType denotes type of change in ObjectChange +type ChangeType int + const ( - // DiffAdd is a Type of ObjectChange where a link was added to the graph - DiffAdd = iota + // DiffAdd is set when a link was added to the graph + DiffAdd ChangeType = iota - // DiffRemove is a Type of ObjectChange where a link was removed from the graph + // DiffRemove is set when a link was removed from the graph DiffRemove - // DiffMod is a Type of ObjectChange where a link was changed in the graph + // DiffMod is set when a link was changed in the graph DiffMod ) // ObjectChange represents a change ia a graph -// TODO: do we want this to be an interface? type ObjectChange struct { // Type of the change, either: // * DiffAdd - Added a link // * DiffRemove - Removed a link // * DiffMod - Modified a link - Type int + Type ChangeType // Path to the changed link Path string From 022346517979b347d5e63be3ac101fbd48ce17e4 Mon Sep 17 00:00:00 2001 From: taylor Date: Thu, 2 Aug 2018 00:29:31 -0400 Subject: [PATCH 2488/3817] blockstore: Adding GetSize method to map from Cid to BlockSize Performant way to map from Cid to BlockSize. License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-blockstore@d021381f9a85cb0ac23c5ca617d0b2efd0d5b2f3 --- blockstore/arc_cache.go | 55 +++++++++++++++++++++------------- blockstore/arc_cache_test.go | 45 +++++++++++++++++++++++++++- blockstore/blockstore.go | 15 ++++++++++ blockstore/blockstore_test.go | 33 ++++++++++++++++++++ blockstore/bloom_cache.go | 4 +++ blockstore/bloom_cache_test.go | 27 +++++++++++++++-- blockstore/idstore.go | 8 +++++ blockstore/idstore_test.go | 32 ++++++++++++++++++++ 8 files changed, 194 insertions(+), 25 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 3a79c4e59..d8e718082 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -34,7 +34,7 @@ func newARCCachedBS(ctx context.Context, bs Blockstore, lruSize int) (*arccache, } func (b *arccache) DeleteBlock(k *cid.Cid) error { - if has, ok := b.hasCached(k); ok && !has { + if has, _, ok := b.hasCached(k); ok && !has { return ErrNotFound } @@ -42,7 +42,7 @@ func (b *arccache) DeleteBlock(k *cid.Cid) error { err := b.blockstore.DeleteBlock(k) switch err { case nil, ds.ErrNotFound, ErrNotFound: - b.addCache(k, false) + b.addCache(k, -1) return err default: return err @@ -51,33 +51,46 @@ func (b *arccache) DeleteBlock(k *cid.Cid) error { // if ok == false has is inconclusive // if ok == true then has respons to question: is it contained -func (b *arccache) hasCached(k *cid.Cid) (has bool, ok bool) { +func (b *arccache) hasCached(k *cid.Cid) (has bool, size int, ok bool) { b.total.Inc() if k == nil { log.Error("nil cid in arccache") // Return cache invalid so the call to blockstore happens // in case of invalid key and correct error is created. - return false, false + return false, -1, false } h, ok := b.arc.Get(k.KeyString()) if ok { b.hits.Inc() - return h.(bool), true + if h.(int) > -1 { + return true, h.(int), true + } else { + return false, h.(int), true + } } - return false, false + return false, -1, false } func (b *arccache) Has(k *cid.Cid) (bool, error) { - if has, ok := b.hasCached(k); ok { - return has, nil + blockSize, err := b.GetSize(k) + if err == ds.ErrNotFound { + return false, nil } + return blockSize > -1, err +} - res, err := b.blockstore.Has(k) - if err == nil { - b.addCache(k, res) +func (b *arccache) GetSize(k *cid.Cid) (int, error) { + if _, blockSize, ok := b.hasCached(k); ok { + return blockSize, nil + } + blockSize, err := b.blockstore.GetSize(k) + if err == ds.ErrNotFound { + b.addCache(k, -1) + } else if err == nil { + b.addCache(k, blockSize) } - return res, err + return blockSize, err } func (b *arccache) Get(k *cid.Cid) (blocks.Block, error) { @@ -86,27 +99,27 @@ func (b *arccache) Get(k *cid.Cid) (blocks.Block, error) { return nil, ErrNotFound } - if has, ok := b.hasCached(k); ok && !has { + if has, _, ok := b.hasCached(k); ok && !has { return nil, ErrNotFound } bl, err := b.blockstore.Get(k) if bl == nil && err == ErrNotFound { - b.addCache(k, false) + b.addCache(k, -1) } else if bl != nil { - b.addCache(k, true) + b.addCache(k, len(bl.RawData())) } return bl, err } func (b *arccache) Put(bl blocks.Block) error { - if has, ok := b.hasCached(bl.Cid()); ok && has { + if has, _, ok := b.hasCached(bl.Cid()); ok && has { return nil } err := b.blockstore.Put(bl) if err == nil { - b.addCache(bl.Cid(), true) + b.addCache(bl.Cid(), len(bl.RawData())) } return err } @@ -116,7 +129,7 @@ func (b *arccache) PutMany(bs []blocks.Block) error { for _, block := range bs { // call put on block if result is inconclusive or we are sure that // the block isn't in storage - if has, ok := b.hasCached(block.Cid()); !ok || (ok && !has) { + if has, _, ok := b.hasCached(block.Cid()); !ok || (ok && !has) { good = append(good, block) } } @@ -125,7 +138,7 @@ func (b *arccache) PutMany(bs []blocks.Block) error { return err } for _, block := range good { - b.addCache(block.Cid(), true) + b.addCache(block.Cid(), len(block.RawData())) } return nil } @@ -134,8 +147,8 @@ func (b *arccache) HashOnRead(enabled bool) { b.blockstore.HashOnRead(enabled) } -func (b *arccache) addCache(c *cid.Cid, has bool) { - b.arc.Add(c.KeyString(), has) +func (b *arccache) addCache(c *cid.Cid, blockSize int) { + b.arc.Add(c.KeyString(), blockSize) } func (b *arccache) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 84789e7e8..2f8081957 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/ipfs/go-block-format" + blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" @@ -107,6 +107,9 @@ func TestGetFillsCache(t *testing.T) { if has, err := arc.Has(exampleBlock.Cid()); has || err != nil { t.Fatal("has was true but there is no such block") } + if blockSize, err := arc.GetSize(exampleBlock.Cid()); blockSize > -1 || err != nil { + t.Fatal("getsize was true but there is no such block") + } untrap(cd) @@ -119,12 +122,16 @@ func TestGetFillsCache(t *testing.T) { if has, err := arc.Has(exampleBlock.Cid()); !has || err != nil { t.Fatal("has returned invalid result") } + if blockSize, err := arc.GetSize(exampleBlock.Cid()); blockSize == -1 || err != nil { + t.Fatal("getsize returned invalid result") + } } func TestGetAndDeleteFalseShortCircuit(t *testing.T) { arc, _, cd := createStores(t) arc.Has(exampleBlock.Cid()) + arc.GetSize(exampleBlock.Cid()) trap("get hit datastore", cd, t) @@ -167,6 +174,41 @@ func TestHasAfterSucessfulGetIsCached(t *testing.T) { arc.Has(exampleBlock.Cid()) } +func TestGetSizeAfterSucessfulGetIsCached(t *testing.T) { + arc, bs, cd := createStores(t) + + bs.Put(exampleBlock) + + arc.Get(exampleBlock.Cid()) + + trap("has hit datastore", cd, t) + arc.GetSize(exampleBlock.Cid()) +} + +func TestGetSizeMissingZeroSizeBlock(t *testing.T) { + arc, bs, cd := createStores(t) + emptyBlock := blocks.NewBlock([]byte{}) + missingBlock := blocks.NewBlock([]byte("missingBlock")) + + bs.Put(emptyBlock) + + arc.Get(emptyBlock.Cid()) + + trap("has hit datastore", cd, t) + if blockSize, err := arc.GetSize(emptyBlock.Cid()); blockSize != 0 || err != nil { + t.Fatal("getsize returned invalid result") + } + untrap(cd) + + arc.Get(missingBlock.Cid()) + + trap("has hit datastore", cd, t) + if blockSize, err := arc.GetSize(missingBlock.Cid()); blockSize != -1 || err != nil { + t.Fatal("getsize returned invalid result") + } +} + + func TestDifferentKeyObjectsWork(t *testing.T) { arc, bs, cd := createStores(t) @@ -191,6 +233,7 @@ func TestPutManyCaches(t *testing.T) { trap("has hit datastore", cd, t) arc.Has(exampleBlock.Cid()) + arc.GetSize(exampleBlock.Cid()) untrap(cd) arc.DeleteBlock(exampleBlock.Cid()) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 748387c00..521e82dd7 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -40,6 +40,9 @@ type Blockstore interface { Has(*cid.Cid) (bool, error) Get(*cid.Cid) (blocks.Block, error) + // GetSize returns the CIDs mapped BlockSize + GetSize(*cid.Cid) (int, error) + // Put puts a given block to the underlying datastore Put(blocks.Block) error @@ -183,6 +186,18 @@ func (bs *blockstore) Has(k *cid.Cid) (bool, error) { return bs.datastore.Has(dshelp.CidToDsKey(k)) } +func (bs *blockstore) GetSize(k *cid.Cid) (int, error) { + maybeData, err := bs.datastore.Get(dshelp.CidToDsKey(k)) + if err != nil { + return -1, err + } + bdata, ok := maybeData.([]byte) + if !ok { + return -1, ErrValueTypeMismatch + } + return len(bdata), nil +} + func (bs *blockstore) DeleteBlock(k *cid.Cid) error { err := bs.datastore.Delete(dshelp.CidToDsKey(k)) if err == ds.ErrNotFound { diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 7def52eb0..2fc1c9452 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -54,6 +54,39 @@ func TestPutThenGetBlock(t *testing.T) { } } +func TestPutThenGetSizeBlock(t *testing.T) { + bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) + block := blocks.NewBlock([]byte("some data")) + missingBlock := blocks.NewBlock([]byte("missingBlock")) + emptyBlock := blocks.NewBlock([]byte{}) + + err := bs.Put(block) + if err != nil { + t.Fatal(err) + } + + blockSize, err := bs.GetSize(block.Cid()) + if err != nil { + t.Fatal(err) + } + if len(block.RawData()) != blockSize { + t.Fail() + } + + err = bs.Put(emptyBlock) + if err != nil { + t.Fatal(err) + } + + if blockSize, err := bs.GetSize(emptyBlock.Cid()); blockSize != 0 || err != nil { + t.Fatal(err) + } + + if blockSize, err := bs.GetSize(missingBlock.Cid()); blockSize != -1 || err == nil { + t.Fatal("getsize returned invalid result") + } +} + func TestHashOnRead(t *testing.T) { orginalDebug := u.Debug defer (func() { diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 7dd0bbe9f..403fd6d14 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -133,6 +133,10 @@ func (b *bloomcache) Has(k *cid.Cid) (bool, error) { return b.blockstore.Has(k) } +func (b *bloomcache) GetSize(k *cid.Cid) (int, error) { + return b.blockstore.GetSize(k) +} + func (b *bloomcache) Get(k *cid.Cid) (blocks.Block, error) { if has, ok := b.hasCached(k); ok && !has { return nil, ErrNotFound diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index c165eee6e..a452e049d 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -45,13 +45,18 @@ func TestPutManyAddsToBloom(t *testing.T) { block1 := blocks.NewBlock([]byte("foo")) block2 := blocks.NewBlock([]byte("bar")) + emptyBlock := blocks.NewBlock([]byte{}) - cachedbs.PutMany([]blocks.Block{block1}) + cachedbs.PutMany([]blocks.Block{block1, emptyBlock}) has, err := cachedbs.Has(block1.Cid()) if err != nil { t.Fatal(err) } - if !has { + blockSize, err := cachedbs.GetSize(block1.Cid()) + if err != nil { + t.Fatal(err) + } + if blockSize == -1 || !has { t.Fatal("added block is reported missing") } @@ -59,9 +64,25 @@ func TestPutManyAddsToBloom(t *testing.T) { if err != nil { t.Fatal(err) } - if has { + blockSize, err = cachedbs.GetSize(block2.Cid()) + if err != nil && err != ds.ErrNotFound { + t.Fatal(err) + } + if blockSize > -1 || has { t.Fatal("not added block is reported to be in blockstore") } + + has, err = cachedbs.Has(emptyBlock.Cid()) + if err != nil { + t.Fatal(err) + } + blockSize, err = cachedbs.GetSize(emptyBlock.Cid()) + if err != nil { + t.Fatal(err) + } + if blockSize != 0 || !has { + t.Fatal("added block is reported missing") + } } func TestReturnsErrorWhenSizeNegative(t *testing.T) { diff --git a/blockstore/idstore.go b/blockstore/idstore.go index 5b31b3f8b..a1ef4d60b 100644 --- a/blockstore/idstore.go +++ b/blockstore/idstore.go @@ -41,6 +41,14 @@ func (b *idstore) Has(k *cid.Cid) (bool, error) { return b.bs.Has(k) } +func (b *idstore) GetSize(k *cid.Cid) (int, error) { + isId, bdata := extractContents(k) + if isId { + return len(bdata), nil + } + return b.bs.GetSize(k) +} + func (b *idstore) Get(k *cid.Cid) (blocks.Block, error) { isId, bdata := extractContents(k) if isId { diff --git a/blockstore/idstore_test.go b/blockstore/idstore_test.go index 5a8861990..321d5ec77 100644 --- a/blockstore/idstore_test.go +++ b/blockstore/idstore_test.go @@ -21,6 +21,8 @@ func TestIdStore(t *testing.T) { idblock1, _ := blk.NewBlockWithCid([]byte("idhash1"), idhash1) hash1, _ := cid.NewPrefixV1(cid.Raw, mh.SHA2_256).Sum([]byte("hash1")) block1, _ := blk.NewBlockWithCid([]byte("hash1"), hash1) + emptyHash, _ := cid.NewPrefixV1(cid.Raw, mh.SHA2_256).Sum([]byte("emptyHash")) + emptyBlock, _ := blk.NewBlockWithCid([]byte{}, emptyHash) ids, cb := createTestStores() @@ -56,11 +58,31 @@ func TestIdStore(t *testing.T) { t.Fatal("normal block not added to datastore") } + blockSize, _ := ids.GetSize(hash1) + if blockSize == -1 { + t.Fatal("normal block not added to datastore") + } + _, err = ids.Get(hash1) if err != nil { t.Fatal(err) } + err = ids.Put(emptyBlock) + if err != nil { + t.Fatalf("Put() failed on normal block: %v", err) + } + + have, _ = ids.Has(emptyHash) + if !have { + t.Fatal("normal block not added to datastore") + } + + blockSize, _ = ids.GetSize(emptyHash) + if blockSize != 0 { + t.Fatal("normal block not added to datastore") + } + cb.f = failIfPassThough err = ids.DeleteBlock(idhash1) if err != nil { @@ -78,6 +100,16 @@ func TestIdStore(t *testing.T) { t.Fatal("normal block not deleted from datastore") } + blockSize, _ = ids.GetSize(hash1) + if blockSize > -1 { + t.Fatal("normal block not deleted from datastore") + } + + err = ids.DeleteBlock(emptyHash) + if err != nil { + t.Fatal(err) + } + idhash2, _ := cid.NewPrefixV1(cid.Raw, mh.ID).Sum([]byte("idhash2")) idblock2, _ := blk.NewBlockWithCid([]byte("idhash2"), idhash2) hash2, _ := cid.NewPrefixV1(cid.Raw, mh.SHA2_256).Sum([]byte("hash2")) From 24ed898c0af7c1aab3a966a2e773fde0ec7c4db6 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 2 Aug 2018 17:17:38 +0200 Subject: [PATCH 2489/3817] Add FetchGraphWithDepthLimit to specify depth-limited graph fetching. This adds also EnumerateChildren*Depth which call a visit function which receives the Cid and the depth in the dag at which it has been seen. This commit was moved from ipfs/go-merkledag@8393bc02ed034b5b5a51b9df9395d84b22e4a835 --- ipld/merkledag/merkledag.go | 120 +++++++++++++++++++++++++----- ipld/merkledag/merkledag_test.go | 124 ++++++++++++++++++++++++++----- 2 files changed, 208 insertions(+), 36 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index de2f61c95..fe68ce421 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -158,25 +158,57 @@ func (n *dagService) Session(ctx context.Context) ipld.NodeGetter { // FetchGraph fetches all nodes that are children of the given node func FetchGraph(ctx context.Context, root *cid.Cid, serv ipld.DAGService) error { + return FetchGraphWithDepthLimit(ctx, root, -1, serv) +} + +// FetchGraphWithDepthLimit fetches all nodes that are children to the given +// node down to the given depth. maxDetph=0 means "only fetch root", +// maxDepth=1 means "fetch root and its direct children" and so on... +// maxDepth=-1 means unlimited. +func FetchGraphWithDepthLimit(ctx context.Context, root *cid.Cid, depthLim int, serv ipld.DAGService) error { var ng ipld.NodeGetter = serv ds, ok := serv.(*dagService) if ok { ng = &sesGetter{bserv.NewSession(ctx, ds.Blocks)} } + set := make(map[string]int) + + // Visit function returns true when: + // * The element is not in the set and we're not over depthLim + // * The element is in the set but recorded depth is deeper + // than currently seen (if we find it higher in the tree we'll need + // to explore deeper than before). + // depthLim = -1 means we only return true if the element is not in the + // set. + visit := func(c *cid.Cid, depth int) bool { + key := string(c.Bytes()) + oldDepth, ok := set[key] + + if (ok && depthLim < 0) || (depthLim >= 0 && depth > depthLim) { + return false + } + + if !ok || oldDepth > depth { + set[key] = depth + return true + } + return false + } + v, _ := ctx.Value(progressContextKey).(*ProgressTracker) if v == nil { - return EnumerateChildrenAsync(ctx, GetLinksDirect(ng), root, cid.NewSet().Visit) + return EnumerateChildrenAsyncDepth(ctx, GetLinksDirect(ng), root, 0, visit) } - set := cid.NewSet() - visit := func(c *cid.Cid) bool { - if set.Visit(c) { + + visitProgress := func(c *cid.Cid, depth int) bool { + if visit(c, depth) { v.Increment() return true } return false } - return EnumerateChildrenAsync(ctx, GetLinksDirect(ng), root, visit) + return EnumerateChildrenAsyncDepth(ctx, GetLinksDirect(ng), root, 0, visitProgress) } // GetMany gets many nodes from the DAG at once. @@ -254,14 +286,26 @@ func GetLinksWithDAG(ng ipld.NodeGetter) GetLinks { // unseen children to the passed in set. // TODO: parallelize to avoid disk latency perf hits? func EnumerateChildren(ctx context.Context, getLinks GetLinks, root *cid.Cid, visit func(*cid.Cid) bool) error { + visitDepth := func(c *cid.Cid, depth int) bool { + return visit(c) + } + + return EnumerateChildrenDepth(ctx, getLinks, root, 0, visitDepth) +} + +// EnumerateChildrenDepth walks the dag below the given root and passes the +// current depth to a given visit function. The visit function can be used to +// limit DAG exploration. +func EnumerateChildrenDepth(ctx context.Context, getLinks GetLinks, root *cid.Cid, depth int, visit func(*cid.Cid, int) bool) error { links, err := getLinks(ctx, root) if err != nil { return err } + for _, lnk := range links { c := lnk.Cid - if visit(c) { - err = EnumerateChildren(ctx, getLinks, c, visit) + if visit(c, depth+1) { + err = EnumerateChildrenDepth(ctx, getLinks, c, depth+1, visit) if err != nil { return err } @@ -305,8 +349,30 @@ var FetchGraphConcurrency = 8 // // NOTE: It *does not* make multiple concurrent calls to the passed `visit` function. func EnumerateChildrenAsync(ctx context.Context, getLinks GetLinks, c *cid.Cid, visit func(*cid.Cid) bool) error { - feed := make(chan *cid.Cid) - out := make(chan []*ipld.Link) + visitDepth := func(c *cid.Cid, depth int) bool { + return visit(c) + } + + return EnumerateChildrenAsyncDepth(ctx, getLinks, c, 0, visitDepth) +} + +// EnumerateChildrenAsyncDepth is equivalent to EnumerateChildrenDepth *except* +// that it fetches children in parallel (down to a maximum depth in the graph). +// +// NOTE: It *does not* make multiple concurrent calls to the passed `visit` function. +func EnumerateChildrenAsyncDepth(ctx context.Context, getLinks GetLinks, c *cid.Cid, startDepth int, visit func(*cid.Cid, int) bool) error { + type cidDepth struct { + cid *cid.Cid + depth int + } + + type linksDepth struct { + links []*ipld.Link + depth int + } + + feed := make(chan *cidDepth) + out := make(chan *linksDepth) done := make(chan struct{}) var setlk sync.Mutex @@ -318,20 +384,28 @@ func EnumerateChildrenAsync(ctx context.Context, getLinks GetLinks, c *cid.Cid, for i := 0; i < FetchGraphConcurrency; i++ { go func() { - for ic := range feed { + for cdepth := range feed { + ci := cdepth.cid + depth := cdepth.depth + setlk.Lock() - shouldVisit := visit(ic) + shouldVisit := visit(ci, depth) setlk.Unlock() if shouldVisit { - links, err := getLinks(ctx, ic) + links, err := getLinks(ctx, ci) if err != nil { errChan <- err return } + outLinks := &linksDepth{ + links: links, + depth: depth + 1, + } + select { - case out <- links: + case out <- outLinks: case <-fetchersCtx.Done(): return } @@ -346,10 +420,13 @@ func EnumerateChildrenAsync(ctx context.Context, getLinks GetLinks, c *cid.Cid, defer close(feed) send := feed - var todobuffer []*cid.Cid + var todobuffer []*cidDepth var inProgress int - next := c + next := &cidDepth{ + cid: c, + depth: startDepth, + } for { select { case send <- next: @@ -366,13 +443,18 @@ func EnumerateChildrenAsync(ctx context.Context, getLinks GetLinks, c *cid.Cid, if inProgress == 0 && next == nil { return nil } - case links := <-out: - for _, lnk := range links { + case linksDepth := <-out: + for _, lnk := range linksDepth.links { + cd := &cidDepth{ + cid: lnk.Cid, + depth: linksDepth.depth, + } + if next == nil { - next = lnk.Cid + next = cd send = feed } else { - todobuffer = append(todobuffer, lnk.Cid) + todobuffer = append(todobuffer, cd) } } case err := <-errChan: diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 36c054857..cffaf20f0 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -26,6 +26,52 @@ import ( ipld "github.com/ipfs/go-ipld-format" ) +// makeDepthTestingGraph makes a small DAG with two levels. The level-two +// nodes are both children of the root and of one of the level 1 nodes. +// This is meant to test the EnumerateChildren*Depth functions. +func makeDepthTestingGraph(t *testing.T, ds ipld.DAGService) ipld.Node { + root := NodeWithData(nil) + l11 := NodeWithData([]byte("leve1_node1")) + l12 := NodeWithData([]byte("leve1_node2")) + l21 := NodeWithData([]byte("leve2_node1")) + l22 := NodeWithData([]byte("leve2_node2")) + l23 := NodeWithData([]byte("leve2_node3")) + + l11.AddNodeLink(l21.Cid().String(), l21) + l11.AddNodeLink(l22.Cid().String(), l22) + l11.AddNodeLink(l23.Cid().String(), l23) + + root.AddNodeLink(l11.Cid().String(), l11) + root.AddNodeLink(l12.Cid().String(), l12) + root.AddNodeLink(l23.Cid().String(), l23) + + ctx := context.Background() + for _, n := range []ipld.Node{l23, l22, l21, l12, l11, root} { + err := ds.Add(ctx, n) + if err != nil { + t.Fatal(err) + } + } + + return root +} + +// Check that all children of root are in the given set and in the datastore +func traverseAndCheck(t *testing.T, root ipld.Node, ds ipld.DAGService, hasF func(c *cid.Cid) bool) { + // traverse dag and check + for _, lnk := range root.Links() { + c := lnk.Cid + if !hasF(c) { + t.Fatal("missing key in set! ", lnk.Cid.String()) + } + child, err := ds.Get(context.Background(), c) + if err != nil { + t.Fatal(err) + } + traverseAndCheck(t, child, ds, hasF) + } +} + func TestNode(t *testing.T) { n1 := NodeWithData([]byte("beep")) @@ -293,6 +339,66 @@ func TestFetchGraph(t *testing.T) { } } +func TestFetchGraphWithDepthLimit(t *testing.T) { + type testcase struct { + depthLim int + setLen int + } + + tests := []testcase{ + testcase{1, 3}, + testcase{0, 0}, + testcase{-1, 5}, + testcase{2, 5}, + testcase{3, 5}, + } + + testF := func(t *testing.T, tc testcase) { + var dservs []ipld.DAGService + bsis := bstest.Mocks(2) + for _, bsi := range bsis { + dservs = append(dservs, NewDAGService(bsi)) + } + + root := makeDepthTestingGraph(t, dservs[0]) + + err := FetchGraphWithDepthLimit(context.TODO(), root.Cid(), tc.depthLim, dservs[1]) + if err != nil { + t.Fatal(err) + } + + // create an offline dagstore and ensure all blocks were fetched + bs := bserv.New(bsis[1].Blockstore(), offline.Exchange(bsis[1].Blockstore())) + + offlineDS := NewDAGService(bs) + + set := make(map[string]int) + visitF := func(c *cid.Cid, depth int) bool { + if tc.depthLim < 0 || depth <= tc.depthLim { + set[string(c.Bytes())] = depth + return true + } + return false + + } + + err = EnumerateChildrenDepth(context.Background(), offlineDS.GetLinks, root.Cid(), 0, visitF) + if err != nil { + t.Fatal(err) + } + + if len(set) != tc.setLen { + t.Fatalf("expected %d nodes but visited %d", tc.setLen, len(set)) + } + } + + for _, tc := range tests { + t.Run(fmt.Sprintf("depth limit %d", tc.depthLim), func(t *testing.T) { + testF(t, tc) + }) + } +} + func TestEnumerateChildren(t *testing.T) { bsi := bstest.Mocks(1) ds := NewDAGService(bsi[0]) @@ -307,23 +413,7 @@ func TestEnumerateChildren(t *testing.T) { t.Fatal(err) } - var traverse func(n ipld.Node) - traverse = func(n ipld.Node) { - // traverse dag and check - for _, lnk := range n.Links() { - c := lnk.Cid - if !set.Has(c) { - t.Fatal("missing key in set! ", lnk.Cid.String()) - } - child, err := ds.Get(context.Background(), c) - if err != nil { - t.Fatal(err) - } - traverse(child) - } - } - - traverse(root) + traverseAndCheck(t, root, ds, set.Has) } func TestFetchFailure(t *testing.T) { From 83081a0c1b91bb9554d56ea074ef28ca02b38ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 3 Aug 2018 18:06:57 +0200 Subject: [PATCH 2490/3817] coreapi: dag: Batching interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@f4b74679d1eb5597e430a52f4cca826d511898c3 --- coreiface/dag.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/coreiface/dag.go b/coreiface/dag.go index 3f92ebab3..a128e97c5 100644 --- a/coreiface/dag.go +++ b/coreiface/dag.go @@ -4,21 +4,36 @@ import ( "context" "io" - options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" ) -// DagAPI specifies the interface to IPLD -type DagAPI interface { +// DagOps groups operations that can be batched together +type DagOps interface { // Put inserts data using specified format and input encoding. // Unless used with WithCodec or WithHash, the defaults "dag-cbor" and // "sha256" are used. Put(ctx context.Context, src io.Reader, opts ...options.DagPutOption) (ResolvedPath, error) +} + +// DagBatch is the batching version of DagAPI. All implementations of DagBatch +// should be threadsafe +type DagBatch interface { + DagOps + + Commit(ctx context.Context) error +} + +// DagAPI specifies the interface to IPLD +type DagAPI interface { + DagOps // Get attempts to resolve and get the node specified by the path Get(ctx context.Context, path Path) (ipld.Node, error) // Tree returns list of paths within a node specified by the path. Tree(ctx context.Context, path Path, opts ...options.DagTreeOption) ([]Path, error) + + Batch(ctx context.Context) DagBatch } From de98b9bd4549e5e475d3d096c519ae685ac008cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 3 Aug 2018 18:19:45 +0200 Subject: [PATCH 2491/3817] coreapi: dag: Missing batch docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@77b458e7426ae384692ed7c70a3a6179f4adc1b3 --- coreiface/dag.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coreiface/dag.go b/coreiface/dag.go index a128e97c5..01d6112e7 100644 --- a/coreiface/dag.go +++ b/coreiface/dag.go @@ -22,6 +22,7 @@ type DagOps interface { type DagBatch interface { DagOps + // Commit commits nodes to the datastore and announces them to the network Commit(ctx context.Context) error } @@ -35,5 +36,6 @@ type DagAPI interface { // Tree returns list of paths within a node specified by the path. Tree(ctx context.Context, path Path, opts ...options.DagTreeOption) ([]Path, error) + // Batch creates new DagBatch Batch(ctx context.Context) DagBatch } From 3bb8c057c6fe89c2bcc4824581cb3e444f0459c8 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 5 Aug 2018 13:22:51 -0700 Subject: [PATCH 2492/3817] Update bitswap deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/interface-go-ipfs-core@24dde48825577a7f5e052be01025f22a413cc57b --- coreiface/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/path.go b/coreiface/path.go index 49b8cc5ea..069474c23 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" + ipfspath "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ) From aca64d670ba3338b7a21c393f18d7b9514ada884 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 5 Aug 2018 13:22:51 -0700 Subject: [PATCH 2493/3817] Update bitswap deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-filestore@75aaa7f39e17ebde68ca12afc14c6fdb6f63b412 --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 596b9ac47..12c5a4df3 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + dag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" From 3813eb45f4cc5f42f53ecdaa8ddb6bbb73ed1d83 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 5 Aug 2018 13:22:51 -0700 Subject: [PATCH 2494/3817] Update bitswap deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-mfs@b07cd2729e0d4c539e490b93368503a154012bc4 --- mfs/dir.go | 8 ++++---- mfs/fd.go | 2 +- mfs/file.go | 6 +++--- mfs/mfs_test.go | 12 ++++++------ mfs/ops.go | 2 +- mfs/system.go | 4 ++-- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index c73fbd3c4..f110cec56 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -9,10 +9,10 @@ import ( "sync" "time" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" - ft "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs" - uio "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs/io" - ufspb "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs/pb" + ft "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs" + uio "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs/io" + ufspb "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs/pb" + dag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/mfs/fd.go b/mfs/fd.go index e77ccca55..263bc4d48 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -4,7 +4,7 @@ import ( "fmt" "io" - mod "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs/mod" + mod "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs/mod" context "context" ) diff --git a/mfs/file.go b/mfs/file.go index 6cbff0182..44efc447f 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -5,9 +5,9 @@ import ( "fmt" "sync" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" - ft "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs" - mod "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs/mod" + ft "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs" + mod "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs/mod" + dag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index c5fca3ee9..38401e4df 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,12 +14,12 @@ import ( "testing" "time" - bserv "gx/ipfs/QmNqRBAhovtf4jVd5cF7YvHaFSsQHHZBaUFwGQWPM2CV7R/go-blockservice" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" - ft "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs" - importer "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs/importer" - uio "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs/io" - "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" + ft "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs" + importer "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs/importer" + uio "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs/io" + dag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" + "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" + bserv "gx/ipfs/QmfZ5oGGgsx71QcHb6junfFCMGhYWkK8VV61nkCFyt8e5Q/go-blockservice" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" diff --git a/mfs/ops.go b/mfs/ops.go index 3ae84058a..01d291f8a 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -6,7 +6,7 @@ import ( gopath "path" "strings" - path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" + path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/mfs/system.go b/mfs/system.go index c67192de4..069cefbfe 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -16,8 +16,8 @@ import ( "sync" "time" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" - ft "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs" + ft "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs" + dag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" From 317d1a293b256726ba9ff253bdb78aa64a489ced Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 5 Aug 2018 13:22:51 -0700 Subject: [PATCH 2495/3817] Update bitswap deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-namesys@f073cbcb306eed92eb2eed6f9ff71c29d433b5bf --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 4 ++-- namesys/proquint.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 61b788d59..4bb96b3a6 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" + path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index 02d0aa928..bb877be66 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" + path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 0f8933ab3..4cea9162a 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,8 +8,8 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" + path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" ) type LookupTXTFunc func(name string) (txt []string, err error) diff --git a/namesys/interface.go b/namesys/interface.go index 39bbc6e03..739fbafb9 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" + path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 8dd2ab9ec..2f7f09f41 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" + path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" record "gx/ipfs/QmVsp2KdPYE6M8ryzCk5KHLo3zprcY5hBDaYx6uPCFUdxA/go-libp2p-record" diff --git a/namesys/namesys.go b/namesys/namesys.go index 8dc7d146b..0487cbcec 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" + path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index ee9a0b868..d4956aed4 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,8 +7,8 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs" - path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" + "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs" + path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" pstore "gx/ipfs/QmZR2XWVVBCtbgBWnQhWk2xcQfaR3W8faQPriAiaaj7rsr/go-libp2p-peerstore" offroute "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/offline" diff --git a/namesys/proquint.go b/namesys/proquint.go index 31fdb7753..ea97c7a99 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,8 +7,8 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" + path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index 0ab935c92..e5b0fa51f 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -8,8 +8,8 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmSaz8Qg77gGqvDvLKeSAY7ivDEnramSWF6T7TcRwFpHtP/go-unixfs" - path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" + ft "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs" + path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" routing "gx/ipfs/QmZ383TySJVeZWzGnWui6pRcKyYZk9VkKTuW7tmKRWk5au/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 88038d66d..5755e3f71 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" + path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 7c2455b60..eb11da5e7 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" + path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" mocknet "gx/ipfs/QmY51bqSM5XgxQZqsBrQcRkKTnCb8EKpJpR9K6Qax7Njco/go-libp2p/p2p/net/mock" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index ac3b656d2..93b1fd0b2 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" + path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" mockrouting "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/mock" testutil "gx/ipfs/QmcW4FGAt24fdK1jBgWQn3yP4R9ZLyWQqjozv9QK7epRhL/go-testutil" diff --git a/namesys/routing.go b/namesys/routing.go index 3a355ddf2..aa9924644 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmYKNMEUK7nCVAefgXF1LVtZEZg3uRmBqiae4FJRXDNAyJ/go-path" + path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" dht "gx/ipfs/QmTktQYCKzQjhxF6dk5xJPRuhHn3JBiKGvMLoiDy1mYmxC/go-libp2p-kad-dht" From a496d8e584578d5f8cdaebb0bf850339b1eecf60 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 5 Aug 2018 13:22:51 -0700 Subject: [PATCH 2496/3817] Update bitswap deps License: MIT Signed-off-by: Jeromy This commit was moved from ipfs/go-ipfs-pinner@ec377733e157528c694c7ffda8825c62718be99d --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index abf644a09..90321cc4e 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmNqRBAhovtf4jVd5cF7YvHaFSsQHHZBaUFwGQWPM2CV7R/go-blockservice" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + dag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" + bserv "gx/ipfs/QmfZ5oGGgsx71QcHb6junfFCMGhYWkK8VV61nkCFyt8e5Q/go-blockservice" "gx/ipfs/QmQwgv79RHrRnoXmhnpC1BPtY55HHeneGMpPwmmBU1fUAG/go-verifcid" offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 72407b984..d05a7730a 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + mdag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 9e43f5dcf..7db07915d 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - bs "gx/ipfs/QmNqRBAhovtf4jVd5cF7YvHaFSsQHHZBaUFwGQWPM2CV7R/go-blockservice" - mdag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + mdag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" + bs "gx/ipfs/QmfZ5oGGgsx71QcHb6junfFCMGhYWkK8VV61nkCFyt8e5Q/go-blockservice" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index a205a4e7e..36180b4d9 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index eeb453a19..b6a651bd1 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmNqRBAhovtf4jVd5cF7YvHaFSsQHHZBaUFwGQWPM2CV7R/go-blockservice" - dag "gx/ipfs/QmRy4Qk9hbgFX9NGJRm8rBThrA8PZhNCitMgeRYyZ67s59/go-merkledag" + dag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" + bserv "gx/ipfs/QmfZ5oGGgsx71QcHb6junfFCMGhYWkK8VV61nkCFyt8e5Q/go-blockservice" offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" From 4112eb16cb9491820a00d18bc9a287e5cfc77472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 2 Aug 2018 10:47:12 +0200 Subject: [PATCH 2497/3817] fix: don't dag.Get in ResolveToLastNode when not needed This commit was moved from ipfs/go-path@29e9e4c0a9e3d4df4ec26f0c68a54ce8ca7c73e3 --- path/resolver/resolver.go | 14 ++++++++----- path/resolver/resolver_test.go | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index f96c79174..f5e3862a6 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -55,14 +55,18 @@ func NewBasicResolver(ds ipld.DAGService) *Resolver { } } -// ResolveToLastNode walks the given path and returns the ipld.Node -// referenced by the last element in it. -func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (ipld.Node, []string, error) { +// ResolveToLastNode walks the given path and returns the cid of the last node +// referenced by the path +func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (*cid.Cid, []string, error) { c, p, err := path.SplitAbsPath(fpath) if err != nil { return nil, nil, err } + if len(p) == 0 { + return c, nil, nil + } + nd, err := r.DAG.Get(ctx, c) if err != nil { return nil, nil, err @@ -91,7 +95,7 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (ipld } if len(p) == 0 { - return nd, nil, nil + return nd.Cid(), nil, nil } // Confirm the path exists within the object @@ -107,7 +111,7 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (ipld case *ipld.Link: return nil, nil, errors.New("inconsistent ResolveOnce / nd.Resolve") default: - return nd, p, nil + return nd.Cid(), p, nil } } diff --git a/path/resolver/resolver_test.go b/path/resolver/resolver_test.go index 99e26801d..cec160fe7 100644 --- a/path/resolver/resolver_test.go +++ b/path/resolver/resolver_test.go @@ -69,4 +69,40 @@ func TestRecurivePathResolution(t *testing.T) { "recursive path resolution failed for %s: %s != %s", p.String(), key.String(), cKey.String())) } + + rCid, rest, err := resolver.ResolveToLastNode(ctx, p) + if err != nil { + t.Fatal(err) + } + + if len(rest) != 0 { + t.Error("expected rest to be empty") + } + + if rCid.String() != cKey.String() { + t.Fatal(fmt.Errorf( + "ResolveToLastNode failed for %s: %s != %s", + p.String(), rCid.String(), cKey.String())) + } + + p2, err := path.FromSegments("/ipfs/", aKey.String()) + if err != nil { + t.Fatal(err) + } + + rCid, rest, err = resolver.ResolveToLastNode(ctx, p2) + if err != nil { + t.Fatal(err) + } + + + if len(rest) != 0 { + t.Error("expected rest to be empty") + } + + if rCid.String() != aKey.String() { + t.Fatal(fmt.Errorf( + "ResolveToLastNode failed for %s: %s != %s", + p.String(), rCid.String(), cKey.String())) + } } From fb6331dda4838b7598b72348fc6ed9dff82dd502 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 6 Aug 2018 11:53:05 -0700 Subject: [PATCH 2498/3817] fix test race condition Also stop exposing functions that don't do what they claim to do: * Invalidate does, technically invalidate. However, it's not threadsafe. * Rebuild doesn't *clear* the filter so it's only useful for the initial build. It's also not threadsafe. We can restore these functions later if we need them (but we'll have to change a few things to make them work properly). Also adds a `Wait` function to allow waiting for the bloom filter to finish building. fixes #6 This commit was moved from ipfs/go-ipfs-blockstore@5e44d7b4d329486dd147a63fa5e957121ec655ed --- blockstore/bloom_cache.go | 88 ++++++++++++++++++++-------------- blockstore/bloom_cache_test.go | 14 ++---- 2 files changed, 56 insertions(+), 46 deletions(-) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 927ad1204..86c0190ed 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -2,6 +2,7 @@ package blockstore import ( "context" + "fmt" "sync/atomic" "time" @@ -19,82 +20,95 @@ func bloomCached(ctx context.Context, bs Blockstore, bloomSize, hashCount int) ( if err != nil { return nil, err } - bc := &bloomcache{blockstore: bs, bloom: bl} - bc.hits = metrics.NewCtx(ctx, "bloom.hits_total", - "Number of cache hits in bloom cache").Counter() - bc.total = metrics.NewCtx(ctx, "bloom_total", - "Total number of requests to bloom cache").Counter() - - bc.Invalidate() - go bc.Rebuild(ctx) - if metrics.Active() { - go func() { + bc := &bloomcache{ + blockstore: bs, + bloom: bl, + hits: metrics.NewCtx(ctx, "bloom.hits_total", + "Number of cache hits in bloom cache").Counter(), + total: metrics.NewCtx(ctx, "bloom_total", + "Total number of requests to bloom cache").Counter(), + buildChan: make(chan struct{}), + } + go func() { + err := bc.build(ctx) + if err != nil { + select { + case <-ctx.Done(): + log.Warning("Cache rebuild closed by context finishing: ", err) + default: + log.Error(err) + } + return + } + if metrics.Active() { fill := metrics.NewCtx(ctx, "bloom_fill_ratio", "Ratio of bloom filter fullnes, (updated once a minute)").Gauge() - <-bc.rebuildChan t := time.NewTicker(1 * time.Minute) + defer t.Stop() for { select { case <-ctx.Done(): - t.Stop() return case <-t.C: fill.Set(bc.bloom.FillRatio()) } } - }() - } + } + }() return bc, nil } type bloomcache struct { - bloom *bloom.Bloom active int32 - // This chan is only used for testing to wait for bloom to enable - rebuildChan chan struct{} - blockstore Blockstore + bloom *bloom.Bloom + buildErr error + + buildChan chan struct{} + blockstore Blockstore // Statistics hits metrics.Counter total metrics.Counter } -func (b *bloomcache) Invalidate() { - b.rebuildChan = make(chan struct{}) - atomic.StoreInt32(&b.active, 0) -} - func (b *bloomcache) BloomActive() bool { return atomic.LoadInt32(&b.active) != 0 } -func (b *bloomcache) Rebuild(ctx context.Context) { - evt := log.EventBegin(ctx, "bloomcache.Rebuild") +func (b *bloomcache) Wait(ctx context.Context) error { + select { + case <-ctx.Done(): + return ctx.Err() + case <-b.buildChan: + return b.buildErr + } +} + +func (b *bloomcache) build(ctx context.Context) error { + evt := log.EventBegin(ctx, "bloomcache.build") defer evt.Done() + defer close(b.buildChan) ch, err := b.blockstore.AllKeysChan(ctx) if err != nil { - log.Errorf("AllKeysChan failed in bloomcache rebuild with: %v", err) - return + b.buildErr = fmt.Errorf("AllKeysChan failed in bloomcache rebuild with: %v", err) + return b.buildErr } - finish := false - for !finish { + for { select { case key, ok := <-ch: - if ok { - b.bloom.AddTS(key.Bytes()) // Use binary key, the more compact the better - } else { - finish = true + if !ok { + atomic.StoreInt32(&b.active, 1) + return nil } + b.bloom.AddTS(key.Bytes()) // Use binary key, the more compact the better case <-ctx.Done(): - log.Warning("Cache rebuild closed by context finishing.") - return + b.buildErr = ctx.Err() + return b.buildErr } } - close(b.rebuildChan) - atomic.StoreInt32(&b.active, 1) } func (b *bloomcache) DeleteBlock(k *cid.Cid) error { diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index c165eee6e..86c6d794c 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -37,10 +37,8 @@ func TestPutManyAddsToBloom(t *testing.T) { t.Fatal(err) } - select { - case <-cachedbs.rebuildChan: - case <-ctx.Done(): - t.Fatalf("Timeout wating for rebuild: %d", cachedbs.bloom.ElementsAdded()) + if err := cachedbs.Wait(ctx); err != nil { + t.Fatalf("Failed while waiting for the filter to build: %d", cachedbs.bloom.ElementsAdded()) } block1 := blocks.NewBlock([]byte("foo")) @@ -86,10 +84,8 @@ func TestHasIsBloomCached(t *testing.T) { t.Fatal(err) } - select { - case <-cachedbs.rebuildChan: - case <-ctx.Done(): - t.Fatalf("Timeout wating for rebuild: %d", cachedbs.bloom.ElementsAdded()) + if err := cachedbs.Wait(ctx); err != nil { + t.Fatalf("Failed while waiting for the filter to build: %d", cachedbs.bloom.ElementsAdded()) } cacheFails := 0 @@ -102,7 +98,7 @@ func TestHasIsBloomCached(t *testing.T) { } if float64(cacheFails)/float64(1000) > float64(0.05) { - t.Fatal("Bloom filter has cache miss rate of more than 5%") + t.Fatalf("Bloom filter has cache miss rate of more than 5%%") } cacheFails = 0 From cfbfd82c0ece721606d94245c7a281c7cf0bc8e2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Mon, 6 Aug 2018 12:29:37 -0700 Subject: [PATCH 2499/3817] it might work, idk yet This commit was moved from ipld/go-car@6dbe39639e51fe746484d45737435ef93fb6be02 --- ipld/car/README.md | 28 ++++++++++ ipld/car/car.go | 131 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 ipld/car/README.md create mode 100644 ipld/car/car.go diff --git a/ipld/car/README.md b/ipld/car/README.md new file mode 100644 index 000000000..ffd258c9e --- /dev/null +++ b/ipld/car/README.md @@ -0,0 +1,28 @@ +go-car (go!) +================== + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) +[![Coverage Status](https://codecov.io/gh/ipfs/go-car/branch/master/graph/badge.svg)](https://codecov.io/gh/ipfs/go-car/branch/master) +[![Travis CI](https://travis-ci.org/ipfs/go-car.svg?branch=master)](https://travis-ci.org/ipfs/go-car) + +> go-car is a simple way of packing a merkledag into a single file + + +## Table of Contents + +- [Install](#install) +- [Contribute](#contribute) +- [License](#license) + + +## Contribute + +PRs are welcome! + +Small note: If editing the Readme, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +MIT © Whyrusleeping diff --git a/ipld/car/car.go b/ipld/car/car.go new file mode 100644 index 000000000..dfac12109 --- /dev/null +++ b/ipld/car/car.go @@ -0,0 +1,131 @@ +package car + +import ( + "archive/tar" + "context" + "fmt" + "io" + "io/ioutil" + + "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + bstore "github.com/ipfs/go-ipfs-blockstore" + format "github.com/ipfs/go-ipld-format" + dag "github.com/ipfs/go-merkledag" +) + +func WriteCar(ctx context.Context, ds format.DAGService, root *cid.Cid, w io.Writer) error { + tw := tar.NewWriter(w) + + rh := &tar.Header{ + Typeflag: tar.TypeSymlink, + Name: "root", + Linkname: root.String(), + } + if err := tw.WriteHeader(rh); err != nil { + return err + } + + cw := &carWriter{ds: ds, tw: tw} + + seen := cid.NewSet() + if err := dag.EnumerateChildren(ctx, cw.enumGetLinks, root, seen.Visit); err != nil { + return err + } + + return tw.Flush() +} + +func LoadCar(ctx context.Context, bs bstore.Blockstore, r io.Reader) (*cid.Cid, error) { + tr := tar.NewReader(r) + root, err := tr.Next() + if err != nil { + return nil, err + } + + if root.Name != "root" || root.Typeflag != tar.TypeSymlink { + return nil, fmt.Errorf("expected first entry in CAR to by symlink named 'root'") + } + + rootcid, err := cid.Decode(root.Linkname) + if err != nil { + return nil, err + } + + for { + obj, err := tr.Next() + if err != nil { + if err == io.EOF { + break + } + return nil, err + } + + c, err := cid.Decode(obj.Name) + if err != nil { + return nil, err + } + + // safety 1st + limr := io.LimitReader(tr, 2<<20) + data, err := ioutil.ReadAll(limr) + if err != nil { + return nil, err + } + + hashed, err := c.Prefix().Sum(data) + if err != nil { + return nil, err + } + + if !hashed.Equals(c) { + return nil, fmt.Errorf("mismatch in content integrity, name: %s, data: %s", c, hashed) + } + + blk, err := blocks.NewBlockWithCid(data, c) + if err != nil { + return nil, err + } + + if err := bs.Put(blk); err != nil { + return nil, err + } + } + + return rootcid, nil +} + +type carWriter struct { + ds format.DAGService + tw *tar.Writer +} + +func (cw *carWriter) enumGetLinks(ctx context.Context, c *cid.Cid) ([]*format.Link, error) { + nd, err := cw.ds.Get(ctx, c) + if err != nil { + return nil, err + } + + if err := cw.writeNode(ctx, nd); err != nil { + return nil, err + } + + return nd.Links(), nil +} + +func (cw *carWriter) writeNode(ctx context.Context, nd format.Node) error { + hdr := &tar.Header{ + Name: nd.Cid().String(), + Typeflag: tar.TypeReg, + } + + if err := cw.tw.WriteHeader(hdr); err != nil { + return err + } + + if _, err := cw.tw.Write(nd.RawData()); err != nil { + return err + } + + return nil +} From f9afe981175685c29705b480a2a68688d14c3a3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 3 Aug 2018 16:46:25 +0200 Subject: [PATCH 2500/3817] coreapi: key: some changes to match command functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@12537a60f5318fec2fdf2c02a3afd3786b673f07 --- coreiface/key.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/coreiface/key.go b/coreiface/key.go index 928aa265f..9e9c7e400 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -4,14 +4,20 @@ import ( "context" options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + + "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" ) // Key specifies the interface to Keys in KeyAPI Keystore type Key interface { // Key returns key name Name() string + // Path returns key path Path() Path + + // Id returns key PeerID + Id() peer.ID } // KeyAPI specifies the interface to Keystore @@ -28,5 +34,5 @@ type KeyAPI interface { List(ctx context.Context) ([]Key, error) // Remove removes keys from keystore. Returns ipns path of the removed key - Remove(ctx context.Context, name string) (Path, error) + Remove(ctx context.Context, name string) (Key, error) } From 257b7bd3172adb6368a0fe2e404651d1a8d8d99b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 6 Aug 2018 22:49:31 -0700 Subject: [PATCH 2501/3817] correctly convert the datastore not found errors This commit was moved from ipfs/go-ipfs-blockstore@0d5887bd3d11fbb593f89cac0d5760d08e2e9af3 --- blockstore/arc_cache.go | 7 +++---- blockstore/blockstore.go | 3 +++ blockstore/bloom_cache_test.go | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index d8e718082..0d4fbcbed 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -6,7 +6,6 @@ import ( lru "github.com/hashicorp/golang-lru" blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" - ds "github.com/ipfs/go-datastore" metrics "github.com/ipfs/go-metrics-interface" ) @@ -41,7 +40,7 @@ func (b *arccache) DeleteBlock(k *cid.Cid) error { b.arc.Remove(k) // Invalidate cache before deleting. err := b.blockstore.DeleteBlock(k) switch err { - case nil, ds.ErrNotFound, ErrNotFound: + case nil, ErrNotFound: b.addCache(k, -1) return err default: @@ -74,7 +73,7 @@ func (b *arccache) hasCached(k *cid.Cid) (has bool, size int, ok bool) { func (b *arccache) Has(k *cid.Cid) (bool, error) { blockSize, err := b.GetSize(k) - if err == ds.ErrNotFound { + if err == ErrNotFound { return false, nil } return blockSize > -1, err @@ -85,7 +84,7 @@ func (b *arccache) GetSize(k *cid.Cid) (int, error) { return blockSize, nil } blockSize, err := b.blockstore.GetSize(k) - if err == ds.ErrNotFound { + if err == ErrNotFound { b.addCache(k, -1) } else if err == nil { b.addCache(k, blockSize) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 521e82dd7..c4475da9a 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -188,6 +188,9 @@ func (bs *blockstore) Has(k *cid.Cid) (bool, error) { func (bs *blockstore) GetSize(k *cid.Cid) (int, error) { maybeData, err := bs.datastore.Get(dshelp.CidToDsKey(k)) + if err == ds.ErrNotFound { + return -1, ErrNotFound + } if err != nil { return -1, err } diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index a452e049d..5fc831ec6 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -65,7 +65,7 @@ func TestPutManyAddsToBloom(t *testing.T) { t.Fatal(err) } blockSize, err = cachedbs.GetSize(block2.Cid()) - if err != nil && err != ds.ErrNotFound { + if err != nil && err != ErrNotFound { t.Fatal(err) } if blockSize > -1 || has { From 5db7e835214806092af065be6e3f306c70215edb Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 6 Aug 2018 23:31:01 -0700 Subject: [PATCH 2502/3817] avoid fetching the size when not requested This commit was moved from ipfs/go-ipfs-blockstore@5d1a33c542e43d8e3f04dfe6d0ce22f667aa7450 --- blockstore/arc_cache.go | 46 ++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 0d4fbcbed..4339bb51f 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -9,6 +9,9 @@ import ( metrics "github.com/ipfs/go-metrics-interface" ) +type cacheHave bool +type cacheSize int + // arccache wraps a BlockStore with an Adaptive Replacement Cache (ARC) for // block Cids. This provides block access-time improvements, allowing // to short-cut many searches without query-ing the underlying datastore. @@ -41,7 +44,7 @@ func (b *arccache) DeleteBlock(k *cid.Cid) error { err := b.blockstore.DeleteBlock(k) switch err { case nil, ErrNotFound: - b.addCache(k, -1) + b.cacheHave(k, false) return err default: return err @@ -62,21 +65,26 @@ func (b *arccache) hasCached(k *cid.Cid) (has bool, size int, ok bool) { h, ok := b.arc.Get(k.KeyString()) if ok { b.hits.Inc() - if h.(int) > -1 { - return true, h.(int), true - } else { - return false, h.(int), true + switch h := h.(type) { + case cacheHave: + return bool(h), -1, true + case cacheSize: + return true, int(h), true } } return false, -1, false } func (b *arccache) Has(k *cid.Cid) (bool, error) { - blockSize, err := b.GetSize(k) - if err == ErrNotFound { - return false, nil + if has, _, ok := b.hasCached(k); ok { + return has, nil + } + has, err := b.blockstore.Has(k) + if err != nil { + return false, err } - return blockSize > -1, err + b.cacheHave(k, has) + return has, nil } func (b *arccache) GetSize(k *cid.Cid) (int, error) { @@ -85,9 +93,9 @@ func (b *arccache) GetSize(k *cid.Cid) (int, error) { } blockSize, err := b.blockstore.GetSize(k) if err == ErrNotFound { - b.addCache(k, -1) + b.cacheHave(k, false) } else if err == nil { - b.addCache(k, blockSize) + b.cacheSize(k, blockSize) } return blockSize, err } @@ -104,9 +112,9 @@ func (b *arccache) Get(k *cid.Cid) (blocks.Block, error) { bl, err := b.blockstore.Get(k) if bl == nil && err == ErrNotFound { - b.addCache(k, -1) + b.cacheHave(k, false) } else if bl != nil { - b.addCache(k, len(bl.RawData())) + b.cacheSize(k, len(bl.RawData())) } return bl, err } @@ -118,7 +126,7 @@ func (b *arccache) Put(bl blocks.Block) error { err := b.blockstore.Put(bl) if err == nil { - b.addCache(bl.Cid(), len(bl.RawData())) + b.cacheSize(bl.Cid(), len(bl.RawData())) } return err } @@ -137,7 +145,7 @@ func (b *arccache) PutMany(bs []blocks.Block) error { return err } for _, block := range good { - b.addCache(block.Cid(), len(block.RawData())) + b.cacheSize(block.Cid(), len(block.RawData())) } return nil } @@ -146,8 +154,12 @@ func (b *arccache) HashOnRead(enabled bool) { b.blockstore.HashOnRead(enabled) } -func (b *arccache) addCache(c *cid.Cid, blockSize int) { - b.arc.Add(c.KeyString(), blockSize) +func (b *arccache) cacheHave(c *cid.Cid, have bool) { + b.arc.Add(c.KeyString(), cacheHave(have)) +} + +func (b *arccache) cacheSize(c *cid.Cid, blockSize int) { + b.arc.Add(c.KeyString(), cacheSize(blockSize)) } func (b *arccache) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { From 55223e9427daacfa41800357a551aad8bce06ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 7 Aug 2018 15:10:34 +0200 Subject: [PATCH 2503/3817] key cmd: fix codeclimate warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@814f82cf806d4fefc9f5158e85807b4c96751637 --- coreiface/key.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/key.go b/coreiface/key.go index 9e9c7e400..2abf3559b 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -16,8 +16,8 @@ type Key interface { // Path returns key path Path() Path - // Id returns key PeerID - Id() peer.ID + // ID returns key PeerID + ID() peer.ID } // KeyAPI specifies the interface to Keystore From f7eaccb941c88c64af08e1196c7fcd7decef2f61 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 6 Aug 2018 22:10:53 -0700 Subject: [PATCH 2504/3817] gx: update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@8897b2812f25c973c2e10a277248a594df8dd0f2 --- filestore/filestore.go | 2 +- filestore/filestore_test.go | 4 ++-- filestore/fsrefstore.go | 2 +- filestore/util.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 61562cd12..e15d9acca 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -12,9 +12,9 @@ import ( "errors" posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" + blockstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 12c5a4df3..bc5322ad0 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,11 +7,11 @@ import ( "math/rand" "testing" - dag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" + dag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" + blockstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 5301eab0c..2b1698c7e 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -11,10 +11,10 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" + blockstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" proto "gx/ipfs/QmZHU2gx42NPTYXzw6pJkuX6xCE7bKECp6e8QcPdoLx8sx/protobuf/proto" - blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" dshelp "gx/ipfs/Qmd8UZEDddMaCnQ1G5eSrUhN3coX19V7SyXNQGWnAvUsnT/go-ipfs-ds-help" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsns "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/namespace" diff --git a/filestore/util.go b/filestore/util.go index 96acd2852..1d6b723e8 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,8 +6,8 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" + blockstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" dshelp "gx/ipfs/Qmd8UZEDddMaCnQ1G5eSrUhN3coX19V7SyXNQGWnAvUsnT/go-ipfs-ds-help" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" From aa067a121b31b26ae87a5a2494f574c55e2e7edc Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 6 Aug 2018 22:10:53 -0700 Subject: [PATCH 2505/3817] gx: update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@365f5eb472016cac6e893ccd44a114eb9b8cfcd5 --- coreiface/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/path.go b/coreiface/path.go index 069474c23..4c4c51532 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" + ipfspath "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ) From a84b72e49f8f039b47db86a8b3ac5e1a8298a4c1 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 6 Aug 2018 22:10:53 -0700 Subject: [PATCH 2506/3817] gx: update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@db692243ad75fb856a52443b4b1bb2024963b377 --- mfs/dir.go | 8 ++++---- mfs/fd.go | 2 +- mfs/file.go | 6 +++--- mfs/mfs_test.go | 18 +++++++++--------- mfs/ops.go | 2 +- mfs/system.go | 4 ++-- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index f110cec56..8d1e0069f 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -9,10 +9,10 @@ import ( "sync" "time" - ft "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs" - uio "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs/io" - ufspb "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs/pb" - dag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" + ft "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs" + uio "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs/io" + ufspb "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs/pb" + dag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/mfs/fd.go b/mfs/fd.go index 263bc4d48..b2975d7ea 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -4,7 +4,7 @@ import ( "fmt" "io" - mod "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs/mod" + mod "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs/mod" context "context" ) diff --git a/mfs/file.go b/mfs/file.go index 44efc447f..424a6f1c3 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -5,9 +5,9 @@ import ( "fmt" "sync" - ft "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs" - mod "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs/mod" - dag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" + ft "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs" + mod "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs/mod" + dag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 38401e4df..7230391ee 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,19 +14,19 @@ import ( "testing" "time" - ft "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs" - importer "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs/importer" - uio "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs/io" - dag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" - "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" - bserv "gx/ipfs/QmfZ5oGGgsx71QcHb6junfFCMGhYWkK8VV61nkCFyt8e5Q/go-blockservice" - + bserv "gx/ipfs/QmPkMDBc7pSitAf2uixsNyZ53uheBjcwFTGLtXKpgdNcP4/go-blockservice" + ft "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs" + importer "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs/importer" + uio "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs/io" + "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" + dag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" + + offline "gx/ipfs/QmP9jV1GQzpEhLScrYZ1YWHnZMfdc7x13TwybM73FcuN8k/go-ipfs-exchange-offline" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" + bstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" - bstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/mfs/ops.go b/mfs/ops.go index 01d291f8a..eff8b3032 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -6,7 +6,7 @@ import ( gopath "path" "strings" - path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" + path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/mfs/system.go b/mfs/system.go index 069cefbfe..520fecda3 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -16,8 +16,8 @@ import ( "sync" "time" - ft "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs" - dag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" + ft "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs" + dag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" From 2b17ca2859975ff118f6b7bd5a48574e5e6a7bf6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 6 Aug 2018 22:10:53 -0700 Subject: [PATCH 2507/3817] gx: update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@9ead3b049e72967f7e3ab36cde6191bc0c060fb2 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 4 ++-- namesys/proquint.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 4bb96b3a6..96bcea796 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" + path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index bb877be66..b93562cf8 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" + path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 4cea9162a..8b9aa530f 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,8 +8,8 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" + path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" ) type LookupTXTFunc func(name string) (txt []string, err error) diff --git a/namesys/interface.go b/namesys/interface.go index 739fbafb9..ac99ad49e 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" + path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 2f7f09f41..60b5c9e0e 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" + path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" record "gx/ipfs/QmVsp2KdPYE6M8ryzCk5KHLo3zprcY5hBDaYx6uPCFUdxA/go-libp2p-record" diff --git a/namesys/namesys.go b/namesys/namesys.go index 0487cbcec..4585885a6 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" + path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index d4956aed4..092f3629a 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,8 +7,8 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs" - path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" + "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs" + path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" pstore "gx/ipfs/QmZR2XWVVBCtbgBWnQhWk2xcQfaR3W8faQPriAiaaj7rsr/go-libp2p-peerstore" offroute "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/offline" diff --git a/namesys/proquint.go b/namesys/proquint.go index ea97c7a99..4ed5a79bf 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,8 +7,8 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" + path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" - path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index e5b0fa51f..ae6669bec 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -8,8 +8,8 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmWdTRLi3H7ZJQ8s7NYo8oitz5JHEEPKLn1QPMsJVWg2Ew/go-unixfs" - path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" + ft "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs" + path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" routing "gx/ipfs/QmZ383TySJVeZWzGnWui6pRcKyYZk9VkKTuW7tmKRWk5au/go-libp2p-routing" proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 5755e3f71..9eadcd92a 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" + path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index eb11da5e7..dd1766120 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" + path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" mocknet "gx/ipfs/QmY51bqSM5XgxQZqsBrQcRkKTnCb8EKpJpR9K6Qax7Njco/go-libp2p/p2p/net/mock" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 93b1fd0b2..a60e6d67e 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" + path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" mockrouting "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/mock" testutil "gx/ipfs/QmcW4FGAt24fdK1jBgWQn3yP4R9ZLyWQqjozv9QK7epRhL/go-testutil" diff --git a/namesys/routing.go b/namesys/routing.go index aa9924644..1bc5c813c 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/Qme34dT9spiPgunbueNtziRX4SvfLHDFZQvmTBVK8p4qNT/go-path" + path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" dht "gx/ipfs/QmTktQYCKzQjhxF6dk5xJPRuhHn3JBiKGvMLoiDy1mYmxC/go-libp2p-kad-dht" From 280e76ac5540f3d2cdfac68f7d2b2eaa669a1c91 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 6 Aug 2018 22:10:53 -0700 Subject: [PATCH 2508/3817] gx: update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@2f20e4f2e763016e8b7b1a2e6f30bdc1d12a3d5f --- pinning/pinner/gc/gc.go | 8 ++++---- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 8 ++++---- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 90321cc4e..6b19d0e40 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,14 +8,14 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" - bserv "gx/ipfs/QmfZ5oGGgsx71QcHb6junfFCMGhYWkK8VV61nkCFyt8e5Q/go-blockservice" + bserv "gx/ipfs/QmPkMDBc7pSitAf2uixsNyZ53uheBjcwFTGLtXKpgdNcP4/go-blockservice" + dag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" + offline "gx/ipfs/QmP9jV1GQzpEhLScrYZ1YWHnZMfdc7x13TwybM73FcuN8k/go-ipfs-exchange-offline" "gx/ipfs/QmQwgv79RHrRnoXmhnpC1BPtY55HHeneGMpPwmmBU1fUAG/go-verifcid" - offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" + bstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" - bstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" dstore "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index d05a7730a..77c9d6b1f 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" + mdag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 7db07915d..51795a50f 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,13 +5,13 @@ import ( "testing" "time" - mdag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" - bs "gx/ipfs/QmfZ5oGGgsx71QcHb6junfFCMGhYWkK8VV61nkCFyt8e5Q/go-blockservice" + bs "gx/ipfs/QmPkMDBc7pSitAf2uixsNyZ53uheBjcwFTGLtXKpgdNcP4/go-blockservice" + mdag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" + offline "gx/ipfs/QmP9jV1GQzpEhLScrYZ1YWHnZMfdc7x13TwybM73FcuN8k/go-ipfs-exchange-offline" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 36180b4d9..c2c111c15 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" + "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index b6a651bd1..ef0cbe9d0 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,12 +5,12 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/Qma2BR57Wqp8w9vPreK4dEzoXXk8DFFRL3LresMZg4QpzN/go-merkledag" - bserv "gx/ipfs/QmfZ5oGGgsx71QcHb6junfFCMGhYWkK8VV61nkCFyt8e5Q/go-blockservice" + bserv "gx/ipfs/QmPkMDBc7pSitAf2uixsNyZ53uheBjcwFTGLtXKpgdNcP4/go-blockservice" + dag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" - offline "gx/ipfs/QmS6mo1dPpHdYsVkm27BRZDLxpKBCiJKUH8fHX15XFfMez/go-ipfs-exchange-offline" + offline "gx/ipfs/QmP9jV1GQzpEhLScrYZ1YWHnZMfdc7x13TwybM73FcuN8k/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - blockstore "gx/ipfs/QmadMhXJLHMFjpRmh85XjpmVDkEtQpNYEZNRpWRvYVLrvb/go-ipfs-blockstore" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) From 4f9100b14eceee106d457ef953e4f813f1ac3b16 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 6 Aug 2018 22:12:45 -0700 Subject: [PATCH 2509/3817] implement the new GetSize methods for the filestore blockservices License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@8f4f71d79beef9603d1ea696951ffbef8e8173a9 --- filestore/filestore.go | 16 ++++++++++++++++ filestore/fsrefstore.go | 12 ++++++++++++ 2 files changed, 28 insertions(+) diff --git a/filestore/filestore.go b/filestore/filestore.go index e15d9acca..f0d26259a 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -154,6 +154,22 @@ func (f *Filestore) Get(c *cid.Cid) (blocks.Block, error) { return f.fm.Get(c) } +// GetSize returns the size of the requested block. It may return ErrNotFound +// when the block is not stored. +func (f *Filestore) GetSize(c *cid.Cid) (int, error) { + size, err := f.bs.GetSize(c) + switch err { + default: + return -1, err + case nil: + return size, nil + case blockstore.ErrNotFound: + // try filestore + } + + return f.fm.GetSize(c) +} + // Has returns true if the block with the given Cid is // stored in the Filestore. func (f *Filestore) Has(c *cid.Cid) (bool, error) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 2b1698c7e..146d018b9 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -122,6 +122,18 @@ func (f *FileManager) Get(c *cid.Cid) (blocks.Block, error) { return blocks.NewBlockWithCid(out, c) } +// GetSize gets the size of the block from the datastore. +// +// This method may successfully return the size even if returning the block +// would fail because the associated file is no longer available. +func (f *FileManager) GetSize(c *cid.Cid) (int, error) { + dobj, err := f.getDataObj(c) + if err != nil { + return -1, err + } + return int(dobj.GetSize_()), nil +} + func (f *FileManager) readDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { if IsURL(d.GetFilePath()) { return f.readURLDataObj(c, d) From 1bc5b89036f1f068abda00d1d452eeecd3d48045 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 7 Aug 2018 14:59:21 -0700 Subject: [PATCH 2510/3817] add two checks to make sure we never update the protobuf files This commit was moved from ipfs/go-merkledag@34af3382746f032f8165075390d1e413a7061171 --- ipld/merkledag/coding.go | 5 +++++ ipld/merkledag/pb/merkledag.pb.go | 9 +++++++++ ipld/merkledag/pb/upgrade_check.go | 5 +++++ 3 files changed, 19 insertions(+) create mode 100644 ipld/merkledag/pb/upgrade_check.go diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index fd204e21c..c880316e3 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -13,6 +13,11 @@ import ( ipld "github.com/ipfs/go-ipld-format" ) +// Make sure the user doesn't upgrade this file. +// We need to check *here* as well as inside the `pb` package *just* in case the +// user replaces *all* go files in that package. +const _ = pb.DoNotUpgradeFileEverItWillChangeYourHashes + // for now, we use a PBNode intermediate thing. // because native go objects are nice. diff --git a/ipld/merkledag/pb/merkledag.pb.go b/ipld/merkledag/pb/merkledag.pb.go index 505e0cf22..811dd27d6 100644 --- a/ipld/merkledag/pb/merkledag.pb.go +++ b/ipld/merkledag/pb/merkledag.pb.go @@ -31,6 +31,15 @@ import strconv "strconv" import bytes "bytes" +// DoNotUpgradeFileEverItWillChangeYourHashes warns users about not breaking +// their file hashes. +const DoNotUpgradeFileEverItWillChangeYourHashes = ` +This file does not produce canonical protobufs. Unfortunately, if we change it, +we'll change the hashes of the files we produce. + +Do *not regenerate this file. +` + // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = math.Inf diff --git a/ipld/merkledag/pb/upgrade_check.go b/ipld/merkledag/pb/upgrade_check.go new file mode 100644 index 000000000..e5a6473c1 --- /dev/null +++ b/ipld/merkledag/pb/upgrade_check.go @@ -0,0 +1,5 @@ +package merkledag_pb + +// Make sure the user doesn't upgrade this package! +// This will fail to build if the user does. +const _ = DoNotUpgradeFileEverItWillChangeYourHashes From 4b2572564e793f2dc2a7f58bb24a3942f636833f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 7 Aug 2018 15:24:55 -0700 Subject: [PATCH 2511/3817] add a stable-cid test This test fails if the protobuf file is regenerated. This commit was moved from ipfs/go-merkledag@4726b1ee14a0523099a2a23833572b2af5c8d2da --- ipld/merkledag/node_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index 77597fb5a..ffb996ba2 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -9,8 +9,26 @@ import ( mdtest "github.com/ipfs/go-merkledag/test" ipld "github.com/ipfs/go-ipld-format" + cid "github.com/ipfs/go-cid" ) +func TestStableCID(t *testing.T) { + nd := &ProtoNode{} + nd.SetData([]byte("foobar")) + nd.SetLinks([]*ipld.Link{ + {Name: "a"}, + {Name: "b"}, + {Name: "c"}, + }) + expected, err := cid.Decode("QmSN3WED2xPLbYvBbfvew2ZLtui8EbFYYcbfkpKH5jwG9C") + if err != nil { + t.Fatal(err) + } + if !nd.Cid().Equals(expected) { + t.Fatalf("Got CID %s, expected CID %s", nd.Cid(), expected) + } +} + func TestRemoveLink(t *testing.T) { nd := &ProtoNode{} nd.SetLinks([]*ipld.Link{ From 30d9c52111e8ff3821bc73af601de3267d802cab Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 7 Aug 2018 13:25:07 -0700 Subject: [PATCH 2512/3817] cleanup filestore switch statements (address CR) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@5f3615d9bf85406a61d4dd2ecd9a765119b3e27a --- filestore/filestore.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index f0d26259a..f1cd4a4f7 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -143,15 +143,13 @@ func (f *Filestore) DeleteBlock(c *cid.Cid) error { func (f *Filestore) Get(c *cid.Cid) (blocks.Block, error) { blk, err := f.bs.Get(c) switch err { - default: - return nil, err case nil: return blk, nil case blockstore.ErrNotFound: - // try filestore + return f.fm.Get(c) + default: + return nil, err } - - return f.fm.Get(c) } // GetSize returns the size of the requested block. It may return ErrNotFound @@ -159,15 +157,13 @@ func (f *Filestore) Get(c *cid.Cid) (blocks.Block, error) { func (f *Filestore) GetSize(c *cid.Cid) (int, error) { size, err := f.bs.GetSize(c) switch err { - default: - return -1, err case nil: return size, nil case blockstore.ErrNotFound: - // try filestore + return f.fm.GetSize(c) + default: + return -1, err } - - return f.fm.GetSize(c) } // Has returns true if the block with the given Cid is From faeca948589301b0cbaae0fefd6fa612601f0d03 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 7 Aug 2018 17:02:55 -0700 Subject: [PATCH 2513/3817] fix the protobuf import removes all `// import` directives from the protobuf source This commit was moved from ipfs/go-merkledag@b540cf7e1041f064ad7c8f922aa2160dc06df530 --- ipld/merkledag/node_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index ffb996ba2..4ee59b93b 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -8,8 +8,8 @@ import ( . "github.com/ipfs/go-merkledag" mdtest "github.com/ipfs/go-merkledag/test" - ipld "github.com/ipfs/go-ipld-format" cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" ) func TestStableCID(t *testing.T) { From eb119d4942ab6615504a5dc5da7eb464d411dc56 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 7 Aug 2018 18:21:41 -0700 Subject: [PATCH 2514/3817] update gogo protobuf This commit was moved from ipfs/go-unixfs@1000cfd8959cc21b553b44c8e41abc8d21a9ee08 --- unixfs/pb/Makefile | 11 +++++ unixfs/pb/unixfs.pb.go | 106 +++++++++++++++++++++++++++++++++-------- unixfs/pb/unixfs.proto | 2 + 3 files changed, 98 insertions(+), 21 deletions(-) create mode 100644 unixfs/pb/Makefile diff --git a/unixfs/pb/Makefile b/unixfs/pb/Makefile new file mode 100644 index 000000000..51552a096 --- /dev/null +++ b/unixfs/pb/Makefile @@ -0,0 +1,11 @@ +PB = $(wildcard *.proto) +GO = $(PB:.proto=.pb.go) + +all: $(GO) + +%.pb.go: %.proto + protoc --proto_path=$(GOPATH)/src:. --gogo_out=. $< + +clean: + rm -f *.pb.go + rm -f *.go diff --git a/unixfs/pb/unixfs.pb.go b/unixfs/pb/unixfs.pb.go index 648b10716..0ec0617e7 100644 --- a/unixfs/pb/unixfs.pb.go +++ b/unixfs/pb/unixfs.pb.go @@ -1,17 +1,6 @@ -// Code generated by protoc-gen-gogo. +// Code generated by protoc-gen-gogo. DO NOT EDIT. // source: unixfs.proto -// DO NOT EDIT! -/* -Package unixfs_pb is a generated protocol buffer package. - -It is generated from these files: - unixfs.proto - -It has these top-level messages: - Data - Metadata -*/ package unixfs_pb import proto "github.com/gogo/protobuf/proto" @@ -23,6 +12,12 @@ var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + type Data_DataType int32 const ( @@ -67,20 +62,45 @@ func (x *Data_DataType) UnmarshalJSON(data []byte) error { *x = Data_DataType(value) return nil } +func (Data_DataType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_unixfs_768dd0381a72e0c6, []int{0, 0} +} type Data struct { - Type *Data_DataType `protobuf:"varint,1,req,name=Type,enum=unixfs.pb.Data_DataType" json:"Type,omitempty"` - Data []byte `protobuf:"bytes,2,opt,name=Data" json:"Data,omitempty"` - Filesize *uint64 `protobuf:"varint,3,opt,name=filesize" json:"filesize,omitempty"` - Blocksizes []uint64 `protobuf:"varint,4,rep,name=blocksizes" json:"blocksizes,omitempty"` - HashType *uint64 `protobuf:"varint,5,opt,name=hashType" json:"hashType,omitempty"` - Fanout *uint64 `protobuf:"varint,6,opt,name=fanout" json:"fanout,omitempty"` - XXX_unrecognized []byte `json:"-"` + Type *Data_DataType `protobuf:"varint,1,req,name=Type,enum=unixfs.pb.Data_DataType" json:"Type,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=Data" json:"Data,omitempty"` + Filesize *uint64 `protobuf:"varint,3,opt,name=filesize" json:"filesize,omitempty"` + Blocksizes []uint64 `protobuf:"varint,4,rep,name=blocksizes" json:"blocksizes,omitempty"` + HashType *uint64 `protobuf:"varint,5,opt,name=hashType" json:"hashType,omitempty"` + Fanout *uint64 `protobuf:"varint,6,opt,name=fanout" json:"fanout,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Data) Reset() { *m = Data{} } func (m *Data) String() string { return proto.CompactTextString(m) } func (*Data) ProtoMessage() {} +func (*Data) Descriptor() ([]byte, []int) { + return fileDescriptor_unixfs_768dd0381a72e0c6, []int{0} +} +func (m *Data) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Data.Unmarshal(m, b) +} +func (m *Data) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Data.Marshal(b, m, deterministic) +} +func (dst *Data) XXX_Merge(src proto.Message) { + xxx_messageInfo_Data.Merge(dst, src) +} +func (m *Data) XXX_Size() int { + return xxx_messageInfo_Data.Size(m) +} +func (m *Data) XXX_DiscardUnknown() { + xxx_messageInfo_Data.DiscardUnknown(m) +} + +var xxx_messageInfo_Data proto.InternalMessageInfo func (m *Data) GetType() Data_DataType { if m != nil && m.Type != nil { @@ -125,13 +145,35 @@ func (m *Data) GetFanout() uint64 { } type Metadata struct { - MimeType *string `protobuf:"bytes,1,opt,name=MimeType" json:"MimeType,omitempty"` - XXX_unrecognized []byte `json:"-"` + MimeType *string `protobuf:"bytes,1,opt,name=MimeType" json:"MimeType,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Metadata) Reset() { *m = Metadata{} } func (m *Metadata) String() string { return proto.CompactTextString(m) } func (*Metadata) ProtoMessage() {} +func (*Metadata) Descriptor() ([]byte, []int) { + return fileDescriptor_unixfs_768dd0381a72e0c6, []int{1} +} +func (m *Metadata) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Metadata.Unmarshal(m, b) +} +func (m *Metadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Metadata.Marshal(b, m, deterministic) +} +func (dst *Metadata) XXX_Merge(src proto.Message) { + xxx_messageInfo_Metadata.Merge(dst, src) +} +func (m *Metadata) XXX_Size() int { + return xxx_messageInfo_Metadata.Size(m) +} +func (m *Metadata) XXX_DiscardUnknown() { + xxx_messageInfo_Metadata.DiscardUnknown(m) +} + +var xxx_messageInfo_Metadata proto.InternalMessageInfo func (m *Metadata) GetMimeType() string { if m != nil && m.MimeType != nil { @@ -145,3 +187,25 @@ func init() { proto.RegisterType((*Metadata)(nil), "unixfs.pb.Metadata") proto.RegisterEnum("unixfs.pb.Data_DataType", Data_DataType_name, Data_DataType_value) } + +func init() { proto.RegisterFile("unixfs.proto", fileDescriptor_unixfs_768dd0381a72e0c6) } + +var fileDescriptor_unixfs_768dd0381a72e0c6 = []byte{ + // 254 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x90, 0xb1, 0x6a, 0xeb, 0x30, + 0x18, 0x85, 0xaf, 0x6c, 0x25, 0xb1, 0xff, 0xeb, 0x16, 0xf1, 0x0f, 0x45, 0x74, 0x28, 0xc6, 0x43, + 0xd1, 0x50, 0x3c, 0xf4, 0x0d, 0x0a, 0xa1, 0x74, 0xf1, 0xa2, 0x84, 0xee, 0x4a, 0x22, 0x63, 0x11, + 0xc7, 0x0a, 0xb6, 0x42, 0xeb, 0x3e, 0x45, 0x1f, 0xb9, 0xc8, 0x8e, 0xdd, 0x2e, 0x82, 0x4f, 0xe7, + 0x7c, 0xe2, 0x20, 0x48, 0x2e, 0x8d, 0xf9, 0x2c, 0xbb, 0xfc, 0xdc, 0x5a, 0x67, 0x31, 0x9e, 0x68, + 0x97, 0x7d, 0x07, 0x40, 0xd7, 0xca, 0x29, 0x7c, 0x02, 0xba, 0xed, 0xcf, 0x9a, 0x93, 0x34, 0x10, + 0xb7, 0xcf, 0x3c, 0x9f, 0x2b, 0xb9, 0x8f, 0x87, 0xc3, 0xe7, 0x72, 0x68, 0x21, 0x8e, 0x16, 0x0f, + 0x52, 0x22, 0x12, 0x39, 0xbe, 0x70, 0x0f, 0x51, 0x69, 0x6a, 0xdd, 0x99, 0x2f, 0xcd, 0xc3, 0x94, + 0x08, 0x2a, 0x67, 0xc6, 0x07, 0x80, 0x5d, 0x6d, 0xf7, 0x47, 0x0f, 0x1d, 0xa7, 0x69, 0x28, 0xa8, + 0xfc, 0x73, 0xe3, 0xdd, 0x4a, 0x75, 0xd5, 0xb0, 0x60, 0x31, 0xba, 0x13, 0xe3, 0x1d, 0x2c, 0x4b, + 0xd5, 0xd8, 0x8b, 0xe3, 0xcb, 0x21, 0xb9, 0x52, 0xf6, 0x0e, 0xd1, 0xb4, 0x0a, 0x57, 0x10, 0x4a, + 0xf5, 0xc1, 0xfe, 0xe1, 0x0d, 0xc4, 0x6b, 0xd3, 0xea, 0xbd, 0xb3, 0x6d, 0xcf, 0x08, 0x46, 0x40, + 0x5f, 0x4d, 0xad, 0x59, 0x80, 0x09, 0x44, 0x85, 0x76, 0xea, 0xa0, 0x9c, 0x62, 0x21, 0xfe, 0x87, + 0xd5, 0xa6, 0x3f, 0xd5, 0xa6, 0x39, 0x32, 0xea, 0x9d, 0xb7, 0x97, 0x62, 0xbb, 0xa9, 0x54, 0x7b, + 0x60, 0x8b, 0xec, 0xf1, 0xb7, 0xe9, 0x77, 0x15, 0xe6, 0xa4, 0xaf, 0x3f, 0x43, 0x44, 0x2c, 0x67, + 0xfe, 0x09, 0x00, 0x00, 0xff, 0xff, 0xe9, 0xa0, 0x51, 0x10, 0x54, 0x01, 0x00, 0x00, +} diff --git a/unixfs/pb/unixfs.proto b/unixfs/pb/unixfs.proto index 6feb7aad6..ffc059e8b 100644 --- a/unixfs/pb/unixfs.proto +++ b/unixfs/pb/unixfs.proto @@ -1,3 +1,5 @@ +syntax = "proto2"; + package unixfs.pb; message Data { From 80723b2b339c90d1f4f4dd0035aa6f6c31ed9fd2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 7 Aug 2018 18:26:33 -0700 Subject: [PATCH 2515/3817] add stablecid test This commit was moved from ipfs/go-unixfs@8ec3fa71923aa65bcc604025882a52260e41778d --- unixfs/importer/importer_test.go | 37 +++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/unixfs/importer/importer_test.go b/unixfs/importer/importer_test.go index 55f8a9480..823abaa4b 100644 --- a/unixfs/importer/importer_test.go +++ b/unixfs/importer/importer_test.go @@ -7,12 +7,13 @@ import ( "io/ioutil" "testing" - mdtest "github.com/ipfs/go-merkledag/test" uio "github.com/ipfs/go-unixfs/io" + cid "github.com/ipfs/go-cid" chunker "github.com/ipfs/go-ipfs-chunker" u "github.com/ipfs/go-ipfs-util" ipld "github.com/ipfs/go-ipld-format" + mdtest "github.com/ipfs/go-merkledag/test" ) func getBalancedDag(t testing.TB, size int64, blksize int64) (ipld.Node, ipld.DAGService) { @@ -35,6 +36,40 @@ func getTrickleDag(t testing.TB, size int64, blksize int64) (ipld.Node, ipld.DAG return nd, ds } +func TestStableCid(t *testing.T) { + ds := mdtest.Mock() + buf := make([]byte, 10 * 1024 * 1024) + u.NewSeededRand(0xdeadbeef).Read(buf) + r := bytes.NewReader(buf) + + nd, err := BuildDagFromReader(ds, chunker.DefaultSplitter(r)) + if err != nil { + t.Fatal(err) + } + + expected, err := cid.Decode("QmZN1qquw84zhV4j6vT56tCcmFxaDaySL1ezTXFvMdNmrK") + if err != nil { + t.Fatal(err) + } + if !expected.Equals(nd.Cid()) { + t.Fatalf("expected CID %s, got CID %s", expected, nd) + } + + dr, err := uio.NewDagReader(context.Background(), nd, ds) + if err != nil { + t.Fatal(err) + } + + out, err := ioutil.ReadAll(dr) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(out, buf) { + t.Fatal("bad read") + } +} + func TestBalancedDag(t *testing.T) { ds := mdtest.Mock() buf := make([]byte, 10000) From 24dc2c1d02fb8ec5f0eb66d8156f8d4e79a98069 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 7 Aug 2018 17:53:20 -0700 Subject: [PATCH 2516/3817] update gogo protobuf And regenerate the protobuf definitions. This commit was moved from ipfs/go-ipns@1f0ee15d7416522f55b7731c556fec128378ddd6 --- ipns/ipns.go | 3 +- ipns/pb/Makefile | 11 + ipns/pb/ipns.pb.go | 550 +++++++++++++++++++++++++++++++++++++++++++-- ipns/pb/ipns.proto | 2 + 4 files changed, 550 insertions(+), 16 deletions(-) create mode 100644 ipns/pb/Makefile diff --git a/ipns/ipns.go b/ipns/ipns.go index 38b50764b..7bab52478 100644 --- a/ipns/ipns.go +++ b/ipns/ipns.go @@ -7,7 +7,6 @@ import ( pb "github.com/ipfs/go-ipns/pb" - proto "github.com/gogo/protobuf/proto" u "github.com/ipfs/go-ipfs-util" ic "github.com/libp2p/go-libp2p-crypto" peer "github.com/libp2p/go-libp2p-peer" @@ -23,7 +22,7 @@ func Create(sk ic.PrivKey, val []byte, seq uint64, eol time.Time) (*pb.IpnsEntry entry.Value = val typ := pb.IpnsEntry_EOL entry.ValidityType = &typ - entry.Sequence = proto.Uint64(seq) + entry.Sequence = &seq entry.Validity = []byte(u.FormatRFC3339(eol)) sig, err := sk.Sign(ipnsEntryDataForSig(entry)) diff --git a/ipns/pb/Makefile b/ipns/pb/Makefile new file mode 100644 index 000000000..eb14b5768 --- /dev/null +++ b/ipns/pb/Makefile @@ -0,0 +1,11 @@ +PB = $(wildcard *.proto) +GO = $(PB:.proto=.pb.go) + +all: $(GO) + +%.pb.go: %.proto + protoc --proto_path=$(GOPATH)/src:. --gogofast_out=. $< + +clean: + rm -f *.pb.go + rm -f *.go diff --git a/ipns/pb/ipns.pb.go b/ipns/pb/ipns.pb.go index 692ba564c..5a6a0bebb 100644 --- a/ipns/pb/ipns.pb.go +++ b/ipns/pb/ipns.pb.go @@ -1,27 +1,27 @@ -// Code generated by protoc-gen-gogo. -// source: pb/ipns.proto -// DO NOT EDIT! +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ipns.proto -/* -Package ipns_pb is a generated protocol buffer package. - -It is generated from these files: - pb/ipns.proto - -It has these top-level messages: - IpnsEntry -*/ package ipns_pb import proto "github.com/gogo/protobuf/proto" import fmt "fmt" import math "math" +import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" + +import io "io" + // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + type IpnsEntry_ValidityType int32 const ( @@ -52,6 +52,9 @@ func (x *IpnsEntry_ValidityType) UnmarshalJSON(data []byte) error { *x = IpnsEntry_ValidityType(value) return nil } +func (IpnsEntry_ValidityType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_ipns_02f6be73595bcc54, []int{0, 0} +} type IpnsEntry struct { Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"` @@ -64,13 +67,44 @@ type IpnsEntry struct { // key associated with it. For old RSA keys, its easiest if we just send this as part of // the record itself. For newer ed25519 keys, the public key can be embedded in the // peerID, making this field unnecessary. - PubKey []byte `protobuf:"bytes,7,opt,name=pubKey" json:"pubKey,omitempty"` - XXX_unrecognized []byte `json:"-"` + PubKey []byte `protobuf:"bytes,7,opt,name=pubKey" json:"pubKey,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *IpnsEntry) Reset() { *m = IpnsEntry{} } func (m *IpnsEntry) String() string { return proto.CompactTextString(m) } func (*IpnsEntry) ProtoMessage() {} +func (*IpnsEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_ipns_02f6be73595bcc54, []int{0} +} +func (m *IpnsEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IpnsEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IpnsEntry.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalTo(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (dst *IpnsEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_IpnsEntry.Merge(dst, src) +} +func (m *IpnsEntry) XXX_Size() int { + return m.Size() +} +func (m *IpnsEntry) XXX_DiscardUnknown() { + xxx_messageInfo_IpnsEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_IpnsEntry proto.InternalMessageInfo func (m *IpnsEntry) GetValue() []byte { if m != nil { @@ -125,3 +159,491 @@ func init() { proto.RegisterType((*IpnsEntry)(nil), "ipns.pb.IpnsEntry") proto.RegisterEnum("ipns.pb.IpnsEntry_ValidityType", IpnsEntry_ValidityType_name, IpnsEntry_ValidityType_value) } +func (m *IpnsEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *IpnsEntry) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Value == nil { + return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("value") + } else { + dAtA[i] = 0xa + i++ + i = encodeVarintIpns(dAtA, i, uint64(len(m.Value))) + i += copy(dAtA[i:], m.Value) + } + if m.Signature == nil { + return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("signature") + } else { + dAtA[i] = 0x12 + i++ + i = encodeVarintIpns(dAtA, i, uint64(len(m.Signature))) + i += copy(dAtA[i:], m.Signature) + } + if m.ValidityType != nil { + dAtA[i] = 0x18 + i++ + i = encodeVarintIpns(dAtA, i, uint64(*m.ValidityType)) + } + if m.Validity != nil { + dAtA[i] = 0x22 + i++ + i = encodeVarintIpns(dAtA, i, uint64(len(m.Validity))) + i += copy(dAtA[i:], m.Validity) + } + if m.Sequence != nil { + dAtA[i] = 0x28 + i++ + i = encodeVarintIpns(dAtA, i, uint64(*m.Sequence)) + } + if m.Ttl != nil { + dAtA[i] = 0x30 + i++ + i = encodeVarintIpns(dAtA, i, uint64(*m.Ttl)) + } + if m.PubKey != nil { + dAtA[i] = 0x3a + i++ + i = encodeVarintIpns(dAtA, i, uint64(len(m.PubKey))) + i += copy(dAtA[i:], m.PubKey) + } + if m.XXX_unrecognized != nil { + i += copy(dAtA[i:], m.XXX_unrecognized) + } + return i, nil +} + +func encodeVarintIpns(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *IpnsEntry) Size() (n int) { + var l int + _ = l + if m.Value != nil { + l = len(m.Value) + n += 1 + l + sovIpns(uint64(l)) + } + if m.Signature != nil { + l = len(m.Signature) + n += 1 + l + sovIpns(uint64(l)) + } + if m.ValidityType != nil { + n += 1 + sovIpns(uint64(*m.ValidityType)) + } + if m.Validity != nil { + l = len(m.Validity) + n += 1 + l + sovIpns(uint64(l)) + } + if m.Sequence != nil { + n += 1 + sovIpns(uint64(*m.Sequence)) + } + if m.Ttl != nil { + n += 1 + sovIpns(uint64(*m.Ttl)) + } + if m.PubKey != nil { + l = len(m.PubKey) + n += 1 + l + sovIpns(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovIpns(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozIpns(x uint64) (n int) { + return sovIpns(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *IpnsEntry) Unmarshal(dAtA []byte) error { + var hasFields [1]uint64 + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIpns + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: IpnsEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IpnsEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIpns + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthIpns + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) + if m.Value == nil { + m.Value = []byte{} + } + iNdEx = postIndex + hasFields[0] |= uint64(0x00000001) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIpns + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthIpns + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + hasFields[0] |= uint64(0x00000002) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidityType", wireType) + } + var v IpnsEntry_ValidityType + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIpns + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (IpnsEntry_ValidityType(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.ValidityType = &v + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Validity", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIpns + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthIpns + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Validity = append(m.Validity[:0], dAtA[iNdEx:postIndex]...) + if m.Validity == nil { + m.Validity = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIpns + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Sequence = &v + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ttl", wireType) + } + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIpns + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Ttl = &v + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PubKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIpns + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthIpns + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PubKey = append(m.PubKey[:0], dAtA[iNdEx:postIndex]...) + if m.PubKey == nil { + m.PubKey = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipIpns(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthIpns + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + if hasFields[0]&uint64(0x00000001) == 0 { + return github_com_gogo_protobuf_proto.NewRequiredNotSetError("value") + } + if hasFields[0]&uint64(0x00000002) == 0 { + return github_com_gogo_protobuf_proto.NewRequiredNotSetError("signature") + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipIpns(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowIpns + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowIpns + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowIpns + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthIpns + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowIpns + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipIpns(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthIpns = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowIpns = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("ipns.proto", fileDescriptor_ipns_02f6be73595bcc54) } + +var fileDescriptor_ipns_02f6be73595bcc54 = []byte{ + // 221 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xca, 0x2c, 0xc8, 0x2b, + 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0xb0, 0x93, 0x94, 0xfe, 0x33, 0x72, 0x71, + 0x7a, 0x16, 0xe4, 0x15, 0xbb, 0xe6, 0x95, 0x14, 0x55, 0x0a, 0x89, 0x70, 0xb1, 0x96, 0x25, 0xe6, + 0x94, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x69, 0xf0, 0x04, 0x41, 0x38, 0x42, 0x32, 0x5c, 0x9c, 0xc5, + 0x99, 0xe9, 0x79, 0x89, 0x25, 0xa5, 0x45, 0xa9, 0x12, 0x4c, 0x60, 0x19, 0x84, 0x80, 0x90, 0x33, + 0x17, 0x4f, 0x59, 0x62, 0x4e, 0x66, 0x4a, 0x66, 0x49, 0x65, 0x48, 0x65, 0x41, 0xaa, 0x04, 0xb3, + 0x02, 0xa3, 0x06, 0x9f, 0x91, 0xbc, 0x1e, 0xd4, 0x06, 0x3d, 0xb8, 0xe9, 0x7a, 0x61, 0x48, 0xca, + 0x82, 0x50, 0x34, 0x09, 0x49, 0x71, 0x71, 0xc0, 0xf8, 0x12, 0x2c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, + 0x70, 0x3e, 0x48, 0xae, 0x38, 0xb5, 0xb0, 0x34, 0x35, 0x2f, 0x39, 0x55, 0x82, 0x55, 0x81, 0x51, + 0x83, 0x25, 0x08, 0xce, 0x17, 0x12, 0xe0, 0x62, 0x2e, 0x29, 0xc9, 0x91, 0x60, 0x03, 0x0b, 0x83, + 0x98, 0x42, 0x62, 0x5c, 0x6c, 0x05, 0xa5, 0x49, 0xde, 0xa9, 0x95, 0x12, 0xec, 0x60, 0x73, 0xa0, + 0x3c, 0x25, 0x71, 0x2e, 0x1e, 0x64, 0xfb, 0x85, 0xd8, 0xb9, 0x98, 0x5d, 0xfd, 0x7d, 0x04, 0x18, + 0x9c, 0x78, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0x46, 0x40, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x32, 0x35, 0xc7, 0xf2, 0x25, 0x01, 0x00, 0x00, +} diff --git a/ipns/pb/ipns.proto b/ipns/pb/ipns.proto index a59cfcf29..f2e79feff 100644 --- a/ipns/pb/ipns.proto +++ b/ipns/pb/ipns.proto @@ -1,3 +1,5 @@ +syntax = "proto2"; + package ipns.pb; message IpnsEntry { From 9657a72c82daf4c056f084b1539076bf1adb6b06 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 8 Aug 2018 18:51:17 -0700 Subject: [PATCH 2517/3817] update gogo-protobuf fixes #3214 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@efaa955240090becba324ed8c87feadc275a6fb5 --- mfs/dir.go | 8 ++++---- mfs/fd.go | 2 +- mfs/file.go | 8 ++++---- mfs/mfs_test.go | 20 ++++++++++---------- mfs/ops.go | 2 +- mfs/repub_test.go | 2 +- mfs/system.go | 6 +++--- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 8d1e0069f..00133a2b8 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -9,10 +9,10 @@ import ( "sync" "time" - ft "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs" - uio "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs/io" - ufspb "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs/pb" - dag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" + ft "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs" + uio "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs/io" + ufspb "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs/pb" + dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/mfs/fd.go b/mfs/fd.go index b2975d7ea..c7a795c07 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -4,7 +4,7 @@ import ( "fmt" "io" - mod "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs/mod" + mod "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs/mod" context "context" ) diff --git a/mfs/file.go b/mfs/file.go index 424a6f1c3..422de66cc 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -5,12 +5,12 @@ import ( "fmt" "sync" - ft "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs" - mod "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs/mod" - dag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" + ft "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs" + mod "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs/mod" + dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" - chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + chunker "gx/ipfs/Qmc3UwSvJkntxu2gKDPCqJEzmhqVeJtTbrVxJm6tsdmMF1/go-ipfs-chunker" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 7230391ee..e4da56d57 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,19 +14,19 @@ import ( "testing" "time" - bserv "gx/ipfs/QmPkMDBc7pSitAf2uixsNyZ53uheBjcwFTGLtXKpgdNcP4/go-blockservice" - ft "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs" - importer "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs/importer" - uio "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs/io" - "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" - dag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" - - offline "gx/ipfs/QmP9jV1GQzpEhLScrYZ1YWHnZMfdc7x13TwybM73FcuN8k/go-ipfs-exchange-offline" + "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + ft "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs" + importer "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs/importer" + uio "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs/io" + dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" + bserv "gx/ipfs/QmeZMtdkNG7u2CohGSL8mzAdZY2c3B1coYE91wvbzip1pF/go-blockservice" + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - bstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" - chunker "gx/ipfs/QmVDjhUMtkRskBFAVNwyXuLSKbeAya7JKPnzAxMKDaK4x4/go-ipfs-chunker" + bstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" + offline "gx/ipfs/QmWURzU3XRY4wYBsu2LHukKKHp5skkYB1K357nzpbEvRY4/go-ipfs-exchange-offline" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + chunker "gx/ipfs/Qmc3UwSvJkntxu2gKDPCqJEzmhqVeJtTbrVxJm6tsdmMF1/go-ipfs-chunker" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/mfs/ops.go b/mfs/ops.go index eff8b3032..c2c2d0e54 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -6,7 +6,7 @@ import ( gopath "path" "strings" - path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" + path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/mfs/repub_test.go b/mfs/repub_test.go index bfd21deab..cb1e4437f 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" + ci "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil/ci" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ci "gx/ipfs/QmcW4FGAt24fdK1jBgWQn3yP4R9ZLyWQqjozv9QK7epRhL/go-testutil/ci" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index 520fecda3..33845c302 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -16,12 +16,12 @@ import ( "sync" "time" - ft "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs" - dag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" + ft "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs" + dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" + logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" - logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" ) var ErrNotExist = errors.New("no such rootfs") From 7e6bd471cf38959b5b2a4c4302415d71e46053db Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 8 Aug 2018 18:51:17 -0700 Subject: [PATCH 2518/3817] update gogo-protobuf fixes #3214 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@7af80f20fb728a0550b7e5f11877fb697cb6a03a --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 4 ++-- namesys/ipns_resolver_validation_test.go | 22 +++++++++++----------- namesys/namesys.go | 8 ++++---- namesys/namesys_test.go | 14 +++++++------- namesys/proquint.go | 2 +- namesys/publisher.go | 18 +++++++++--------- namesys/publisher_test.go | 10 +++++----- namesys/republisher/repub.go | 12 ++++++------ namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 10 +++++----- namesys/routing.go | 16 ++++++++-------- 14 files changed, 64 insertions(+), 64 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 96bcea796..96f10c80f 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" + path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index b93562cf8..154c629c3 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" + path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 8b9aa530f..05e93c1ef 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" + path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index ac99ad49e..1e0c083ce 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,9 +36,9 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" + path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" - ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 60b5c9e0e..3b5194a23 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,21 +6,21 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" + path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - record "gx/ipfs/QmVsp2KdPYE6M8ryzCk5KHLo3zprcY5hBDaYx6uPCFUdxA/go-libp2p-record" - routing "gx/ipfs/QmZ383TySJVeZWzGnWui6pRcKyYZk9VkKTuW7tmKRWk5au/go-libp2p-routing" - ropts "gx/ipfs/QmZ383TySJVeZWzGnWui6pRcKyYZk9VkKTuW7tmKRWk5au/go-libp2p-routing/options" - pstore "gx/ipfs/QmZR2XWVVBCtbgBWnQhWk2xcQfaR3W8faQPriAiaaj7rsr/go-libp2p-peerstore" - mockrouting "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/mock" - offline "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/offline" - testutil "gx/ipfs/QmcW4FGAt24fdK1jBgWQn3yP4R9ZLyWQqjozv9QK7epRhL/go-testutil" - peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" - ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" - ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + record "gx/ipfs/QmUTQSGgjs8CHm9yBcUHicpRs7C9abhyZiBwjzCUp1pNgX/go-libp2p-record" + ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" + testutil "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil" + pstore "gx/ipfs/QmYLXCWN2myozZpx8Wx4UjrRuQuhY3YtWoMi6SHaXii6aM/go-libp2p-peerstore" + mockrouting "gx/ipfs/Qma19TdQ7W26jbfuPgdo9Zi4qtjks1zeXzX86mtEYWYCiw/go-ipfs-routing/mock" + offline "gx/ipfs/Qma19TdQ7W26jbfuPgdo9Zi4qtjks1zeXzX86mtEYWYCiw/go-ipfs-routing/offline" + peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" + routing "gx/ipfs/QmewrvpGvgK9qkCtXsGNwXiQzyux4jcHNjoyVrGdsgtNK5/go-libp2p-routing" + ropts "gx/ipfs/QmewrvpGvgK9qkCtXsGNwXiQzyux4jcHNjoyVrGdsgtNK5/go-libp2p-routing/options" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 4585885a6..216525ee3 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,15 +6,15 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" + path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" - routing "gx/ipfs/QmZ383TySJVeZWzGnWui6pRcKyYZk9VkKTuW7tmKRWk5au/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" - ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + routing "gx/ipfs/QmewrvpGvgK9qkCtXsGNwXiQzyux4jcHNjoyVrGdsgtNK5/go-libp2p-routing" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 092f3629a..25d07976e 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,14 +7,14 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs" - path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" + path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs" - pstore "gx/ipfs/QmZR2XWVVBCtbgBWnQhWk2xcQfaR3W8faQPriAiaaj7rsr/go-libp2p-peerstore" - offroute "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/offline" - peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" - ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" - ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" + pstore "gx/ipfs/QmYLXCWN2myozZpx8Wx4UjrRuQuhY3YtWoMi6SHaXii6aM/go-libp2p-peerstore" + offroute "gx/ipfs/Qma19TdQ7W26jbfuPgdo9Zi4qtjks1zeXzX86mtEYWYCiw/go-ipfs-routing/offline" + peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/namesys/proquint.go b/namesys/proquint.go index 4ed5a79bf..7e571b42b 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" + path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index ae6669bec..df47a1501 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -8,17 +8,17 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmVxjT67BU1QZUPzSLNZT6DkDzVNfPfkzqNyJYFXxSH2hA/go-unixfs" - path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" - - routing "gx/ipfs/QmZ383TySJVeZWzGnWui6pRcKyYZk9VkKTuW7tmKRWk5au/go-libp2p-routing" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" - ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" - pb "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns/pb" - ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + ft "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs" + + ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" + pb "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns/pb" + peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" + proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsquery "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" + routing "gx/ipfs/QmewrvpGvgK9qkCtXsGNwXiQzyux4jcHNjoyVrGdsgtNK5/go-libp2p-routing" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 7926fd67e..42cb85973 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -6,13 +6,13 @@ import ( "testing" "time" + ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" + testutil "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" - mockrouting "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/mock" - testutil "gx/ipfs/QmcW4FGAt24fdK1jBgWQn3yP4R9ZLyWQqjozv9QK7epRhL/go-testutil" + mockrouting "gx/ipfs/Qma19TdQ7W26jbfuPgdo9Zi4qtjks1zeXzX86mtEYWYCiw/go-ipfs-routing/mock" + peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" dshelp "gx/ipfs/Qmd8UZEDddMaCnQ1G5eSrUhN3coX19V7SyXNQGWnAvUsnT/go-ipfs-ds-help" - peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" - ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" - ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 9eadcd92a..57be87ae9 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,15 +7,15 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" + path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" - peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" - pb "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns/pb" - ic "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + pb "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns/pb" + peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" + proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index dd1766120..b51bbcdc8 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" + path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmY51bqSM5XgxQZqsBrQcRkKTnCb8EKpJpR9K6Qax7Njco/go-libp2p/p2p/net/mock" - pstore "gx/ipfs/QmZR2XWVVBCtbgBWnQhWk2xcQfaR3W8faQPriAiaaj7rsr/go-libp2p-peerstore" + mocknet "gx/ipfs/QmUDzeFgYrRmHL2hUB6NZmqcBVQtUzETwmFRUc9onfSSHr/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmYLXCWN2myozZpx8Wx4UjrRuQuhY3YtWoMi6SHaXii6aM/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index a60e6d67e..623f6213d 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,12 +6,12 @@ import ( "testing" "time" - path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" + path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" - mockrouting "gx/ipfs/QmbFRJeEmEU16y3BmKKaD4a9fm5oHsEAMHe2vSB1UnfLMi/go-ipfs-routing/mock" - testutil "gx/ipfs/QmcW4FGAt24fdK1jBgWQn3yP4R9ZLyWQqjozv9QK7epRhL/go-testutil" - peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" - ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" + ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" + testutil "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil" + mockrouting "gx/ipfs/Qma19TdQ7W26jbfuPgdo9Zi4qtjks1zeXzX86mtEYWYCiw/go-ipfs-routing/mock" + peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/namesys/routing.go b/namesys/routing.go index 1bc5c813c..3a4711af4 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,17 +6,17 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" + path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - dht "gx/ipfs/QmTktQYCKzQjhxF6dk5xJPRuhHn3JBiKGvMLoiDy1mYmxC/go-libp2p-kad-dht" + logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" + ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" + pb "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns/pb" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - routing "gx/ipfs/QmZ383TySJVeZWzGnWui6pRcKyYZk9VkKTuW7tmKRWk5au/go-libp2p-routing" - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" - logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" - peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" - ipns "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns" - pb "gx/ipfs/Qmdue1XShFNi3mpizGx9NR9hyNEj6U2wEW93yGhKqKCFGN/go-ipns/pb" + dht "gx/ipfs/QmZAsayEQakfFbHyakgHRKHwBTWrwuSBTfaMyxJZUG97VC/go-libp2p-kad-dht" + peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" + proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" + routing "gx/ipfs/QmewrvpGvgK9qkCtXsGNwXiQzyux4jcHNjoyVrGdsgtNK5/go-libp2p-routing" ) var log = logging.Logger("namesys") From a4e11c495ba27b3258d914374998bb860293a3d1 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 8 Aug 2018 18:51:17 -0700 Subject: [PATCH 2519/3817] update gogo-protobuf fixes #3214 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@14e074fe622e697fcd93f3853d01c6b2a5df96a1 --- pinning/pinner/gc/gc.go | 10 +++++----- pinning/pinner/internal/pb/header.pb.go | 2 +- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 8 ++++---- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 8 ++++---- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 6b19d0e40..a785d0776 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,15 +8,15 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmPkMDBc7pSitAf2uixsNyZ53uheBjcwFTGLtXKpgdNcP4/go-blockservice" - dag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" + dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" + bserv "gx/ipfs/QmeZMtdkNG7u2CohGSL8mzAdZY2c3B1coYE91wvbzip1pF/go-blockservice" - offline "gx/ipfs/QmP9jV1GQzpEhLScrYZ1YWHnZMfdc7x13TwybM73FcuN8k/go-ipfs-exchange-offline" "gx/ipfs/QmQwgv79RHrRnoXmhnpC1BPtY55HHeneGMpPwmmBU1fUAG/go-verifcid" - bstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" + bstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" + logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" + offline "gx/ipfs/QmWURzU3XRY4wYBsu2LHukKKHp5skkYB1K357nzpbEvRY4/go-ipfs-exchange-offline" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" - logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" dstore "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/pinning/pinner/internal/pb/header.pb.go b/pinning/pinner/internal/pb/header.pb.go index b77d37743..6a620c9e7 100644 --- a/pinning/pinner/internal/pb/header.pb.go +++ b/pinning/pinner/internal/pb/header.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package pb -import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" +import proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" import math "math" // Reference imports to suppress errors if they are not otherwise used. diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 77c9d6b1f..e60c80eee 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,11 +10,11 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" + mdag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" + logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" - logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 51795a50f..dfb6b75eb 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,12 +5,12 @@ import ( "testing" "time" - bs "gx/ipfs/QmPkMDBc7pSitAf2uixsNyZ53uheBjcwFTGLtXKpgdNcP4/go-blockservice" - mdag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" + mdag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" + bs "gx/ipfs/QmeZMtdkNG7u2CohGSL8mzAdZY2c3B1coYE91wvbzip1pF/go-blockservice" - offline "gx/ipfs/QmP9jV1GQzpEhLScrYZ1YWHnZMfdc7x13TwybM73FcuN8k/go-ipfs-exchange-offline" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - blockstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" + blockstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" + offline "gx/ipfs/QmWURzU3XRY4wYBsu2LHukKKHp5skkYB1K357nzpbEvRY4/go-ipfs-exchange-offline" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index c2c111c15..d3b57e818 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,11 +10,11 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" + "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index ef0cbe9d0..3b095c4cc 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,11 +5,11 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmPkMDBc7pSitAf2uixsNyZ53uheBjcwFTGLtXKpgdNcP4/go-blockservice" - dag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" + dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" + bserv "gx/ipfs/QmeZMtdkNG7u2CohGSL8mzAdZY2c3B1coYE91wvbzip1pF/go-blockservice" - offline "gx/ipfs/QmP9jV1GQzpEhLScrYZ1YWHnZMfdc7x13TwybM73FcuN8k/go-ipfs-exchange-offline" - blockstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" + blockstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" + offline "gx/ipfs/QmWURzU3XRY4wYBsu2LHukKKHp5skkYB1K357nzpbEvRY4/go-ipfs-exchange-offline" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" From f6cac1c4f16f90c59d123296bb820fa0faa8e9fa Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 8 Aug 2018 18:51:17 -0700 Subject: [PATCH 2520/3817] update gogo-protobuf fixes #3214 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@2d4003fdec14cc4bfb89247f2557a7723efa0f18 --- filestore/filestore.go | 4 ++-- filestore/filestore_test.go | 4 ++-- filestore/fsrefstore.go | 2 +- filestore/pb/dataobj.pb.go | 2 +- filestore/util.go | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index f1cd4a4f7..29e966491 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,11 +11,11 @@ import ( "context" "errors" + blockstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" + logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" - blockstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index bc5322ad0..77ed25a4a 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,10 +7,10 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmfKKGzisaoP4oiHQSHz1zLbXDCTeXe7NVfX1FAMKzcHmt/go-merkledag" + dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" + blockstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" - blockstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 146d018b9..67d657a8c 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,8 +10,8 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" + blockstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" - blockstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" proto "gx/ipfs/QmZHU2gx42NPTYXzw6pJkuX6xCE7bKECp6e8QcPdoLx8sx/protobuf/proto" diff --git a/filestore/pb/dataobj.pb.go b/filestore/pb/dataobj.pb.go index fadd40c1a..3ddf73081 100644 --- a/filestore/pb/dataobj.pb.go +++ b/filestore/pb/dataobj.pb.go @@ -13,7 +13,7 @@ It has these top-level messages: */ package datastore_pb -import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto" +import proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" import fmt "fmt" import math "math" diff --git a/filestore/util.go b/filestore/util.go index 1d6b723e8..3eed174c5 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,7 +6,7 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - blockstore "gx/ipfs/QmTCHqj6s51pDu1GaPGyBW2VdmCUvtzLCF6nWykfX9ZYRt/go-ipfs-blockstore" + blockstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" dshelp "gx/ipfs/Qmd8UZEDddMaCnQ1G5eSrUhN3coX19V7SyXNQGWnAvUsnT/go-ipfs-ds-help" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" From e415aa12146cdbaffdc1619687c307cd9de09842 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 8 Aug 2018 18:51:17 -0700 Subject: [PATCH 2521/3817] update gogo-protobuf fixes #3214 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@1ef16343d8d91bd685c8b4dc6554fda5e4531b36 --- coreiface/key.go | 2 +- coreiface/path.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/key.go b/coreiface/key.go index 2abf3559b..3730f3592 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -5,7 +5,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer" + "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" ) // Key specifies the interface to Keys in KeyAPI Keystore diff --git a/coreiface/path.go b/coreiface/path.go index 4c4c51532..c51d31645 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmY2QaawxgJw2rn7WsFNkWEYph3z2azpyYdrhAc1JctDmE/go-path" + ipfspath "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ) From 0911f2f1a0aa8c9253ed10e1178259a91ba6dcd5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 8 Aug 2018 18:51:17 -0700 Subject: [PATCH 2522/3817] update gogo-protobuf fixes #3214 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-keystore@f71e2d9b0da189872d7528337c3d48c26b231c0c --- keystore/keystore.go | 4 ++-- keystore/keystore_test.go | 2 +- keystore/memkeystore.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index e16dc265e..38c0869ef 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -7,8 +7,8 @@ import ( "path/filepath" "strings" - logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" - ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" ) var log = logging.Logger("keystore") diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index f8ef62f49..751a2e39d 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -9,7 +9,7 @@ import ( "sort" "testing" - ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" + ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) type rr struct{} diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index 4a525ce59..a89a1ae7f 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -1,6 +1,6 @@ package keystore -import ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto" +import ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" // MemKeystore is an in memory keystore implementation that is not persisted to // any backing storage. From d9b1bb9fb86bc18bab3eb6c99d03bbba69e5704b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 9 Aug 2018 23:22:06 -0700 Subject: [PATCH 2523/3817] gx publish 1.0.1 This commit was moved from ipld/go-car@ba1ec22f5cf62d8732da86eee6974f079fe8d2fb --- ipld/car/car.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ipld/car/car.go b/ipld/car/car.go index dfac12109..3f1996aac 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -117,6 +117,7 @@ func (cw *carWriter) writeNode(ctx context.Context, nd format.Node) error { hdr := &tar.Header{ Name: nd.Cid().String(), Typeflag: tar.TypeReg, + Size: int64(len(nd.RawData())), } if err := cw.tw.WriteHeader(hdr); err != nil { From 7de2f3a66ad8ef3ad575391f66d08bbce9b8159a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 10 Aug 2018 17:21:28 -0700 Subject: [PATCH 2524/3817] update cmdkit to fix the progress bar The progressbar should now correctly calculate the size of a directory (by ignoring the directory sizes). fixes #5288 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@252006ff2334b98b0d085e249f3005277544655a --- mfs/dir.go | 6 +++--- mfs/fd.go | 2 +- mfs/file.go | 4 ++-- mfs/mfs_test.go | 6 +++--- mfs/system.go | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 00133a2b8..37d88620a 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -9,10 +9,10 @@ import ( "sync" "time" - ft "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs" - uio "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs/io" - ufspb "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs/pb" dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" + ft "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs" + uio "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs/io" + ufspb "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs/pb" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" diff --git a/mfs/fd.go b/mfs/fd.go index c7a795c07..1298c618c 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -4,7 +4,7 @@ import ( "fmt" "io" - mod "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs/mod" + mod "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs/mod" context "context" ) diff --git a/mfs/file.go b/mfs/file.go index 422de66cc..f577e11de 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -5,9 +5,9 @@ import ( "fmt" "sync" - ft "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs" - mod "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs/mod" dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" + ft "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs" + mod "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs/mod" ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" chunker "gx/ipfs/Qmc3UwSvJkntxu2gKDPCqJEzmhqVeJtTbrVxJm6tsdmMF1/go-ipfs-chunker" diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index e4da56d57..a8aa80981 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -15,10 +15,10 @@ import ( "time" "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" - ft "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs" - importer "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs/importer" - uio "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs/io" dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" + ft "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs" + importer "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs/importer" + uio "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs/io" bserv "gx/ipfs/QmeZMtdkNG7u2CohGSL8mzAdZY2c3B1coYE91wvbzip1pF/go-blockservice" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" diff --git a/mfs/system.go b/mfs/system.go index 33845c302..cbeac035e 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -16,8 +16,8 @@ import ( "sync" "time" - ft "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs" dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" + ft "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" From 588995f705d3fbdadcd6cf9a4ab7adffdba79600 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 10 Aug 2018 17:21:28 -0700 Subject: [PATCH 2525/3817] update cmdkit to fix the progress bar The progressbar should now correctly calculate the size of a directory (by ignoring the directory sizes). fixes #5288 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@51021785d8c0db8304e1a5e0c95fa3186812b8ed --- namesys/namesys_test.go | 2 +- namesys/publisher.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 25d07976e..3ab9bd49b 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -8,7 +8,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" - "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs" + "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" diff --git a/namesys/publisher.go b/namesys/publisher.go index df47a1501..b50f54185 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -9,7 +9,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" - ft "gx/ipfs/QmWJRM6rLjXGEXb5JkKu17Y68eJtCFcKPyRhb8JH2ELZ2Q/go-unixfs" + ft "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" From 37669fb2976806d68401b27bade24561dfd56724 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 10 Aug 2018 16:07:02 -0400 Subject: [PATCH 2526/3817] Update to use new Builder interface for creating CIDs. This commit was moved from ipfs/go-merkledag@ff4fe8a1d4986a5d577ac443f22dd06c7cb694ee --- ipld/merkledag/coding.go | 7 ++----- ipld/merkledag/node.go | 27 +++++++++++++++------------ ipld/merkledag/raw.go | 7 ++----- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index c880316e3..2f24ca138 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -93,10 +93,7 @@ func (n *ProtoNode) EncodeProtobuf(force bool) ([]byte, error) { } if n.cached == nil { - if n.Prefix.Codec == 0 { // unset - n.Prefix = v0CidPrefix - } - c, err := n.Prefix.Sum(n.encoded) + c, err := n.Prefix().Sum(n.encoded) if err != nil { return nil, err } @@ -134,7 +131,7 @@ func DecodeProtobufBlock(b blocks.Block) (ipld.Node, error) { } decnd.cached = c - decnd.Prefix = c.Prefix() + decnd.SetPrefix(c.Prefix()) return decnd, nil } diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 876d40441..1f8761295 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -27,8 +27,8 @@ type ProtoNode struct { cached *cid.Cid - // Prefix specifies cid version and hashing function - Prefix cid.Prefix + // builder specifies cid version and hashing function + builder cid.Builder } var v0CidPrefix = cid.Prefix{ @@ -63,14 +63,21 @@ func PrefixForCidVersion(version int) (cid.Prefix, error) { } } +// Prefix returns the CID Prefix for this ProtoNode, it is never nil +func (n *ProtoNode) Prefix() cid.Builder { + if n.builder == nil { + n.builder = v0CidPrefix + } + return n.builder +} + // SetPrefix sets the CID prefix if it is non nil, if prefix is nil then // it resets it the default value -func (n *ProtoNode) SetPrefix(prefix *cid.Prefix) { +func (n *ProtoNode) SetPrefix(prefix cid.Builder) { if prefix == nil { - n.Prefix = v0CidPrefix + n.builder = v0CidPrefix } else { - n.Prefix = *prefix - n.Prefix.Codec = cid.DagProtobuf + n.builder = prefix.WithCodec(cid.DagProtobuf) n.encoded = nil n.cached = nil } @@ -191,7 +198,7 @@ func (n *ProtoNode) Copy() ipld.Node { copy(nnode.links, n.links) } - nnode.Prefix = n.Prefix + nnode.builder = n.builder return nnode } @@ -301,11 +308,7 @@ func (n *ProtoNode) Cid() *cid.Cid { return n.cached } - if n.Prefix.Codec == 0 { - n.SetPrefix(nil) - } - - c, err := n.Prefix.Sum(n.RawData()) + c, err := n.builder.Sum(n.RawData()) if err != nil { // programmer error err = fmt.Errorf("invalid CID of length %d: %x: %v", len(n.RawData()), n.RawData(), err) diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 4d93b7211..ebfaba7c2 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -36,11 +36,8 @@ var _ ipld.DecodeBlockFunc = DecodeRawBlock // NewRawNodeWPrefix creates a RawNode with the hash function // specified in prefix. -func NewRawNodeWPrefix(data []byte, prefix cid.Prefix) (*RawNode, error) { - prefix.Codec = cid.Raw - if prefix.Version == 0 { - prefix.Version = 1 - } +func NewRawNodeWPrefix(data []byte, prefix cid.Builder) (*RawNode, error) { + prefix = prefix.WithCodec(cid.Raw) c, err := prefix.Sum(data) if err != nil { return nil, err From 4d7a04b9e5b974a0258ec34898053ecd9de25c72 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 10 Aug 2018 22:15:45 -0400 Subject: [PATCH 2527/3817] Rename Prefix() and SetPrefix() to CidPrefix() and SetCidPrefix(). This commit was moved from ipfs/go-merkledag@70cb3dd8eaba22c01cb7629c77eae412cc51e311 --- ipld/merkledag/coding.go | 4 ++-- ipld/merkledag/node.go | 14 +++++++------- ipld/merkledag/raw.go | 9 ++++----- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 2f24ca138..efb5dc224 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -93,7 +93,7 @@ func (n *ProtoNode) EncodeProtobuf(force bool) ([]byte, error) { } if n.cached == nil { - c, err := n.Prefix().Sum(n.encoded) + c, err := n.CidBuilder().Sum(n.encoded) if err != nil { return nil, err } @@ -131,7 +131,7 @@ func DecodeProtobufBlock(b blocks.Block) (ipld.Node, error) { } decnd.cached = c - decnd.SetPrefix(c.Prefix()) + decnd.SetCidBuilder(c.Prefix()) return decnd, nil } diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 1f8761295..7cca8c25d 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -63,21 +63,21 @@ func PrefixForCidVersion(version int) (cid.Prefix, error) { } } -// Prefix returns the CID Prefix for this ProtoNode, it is never nil -func (n *ProtoNode) Prefix() cid.Builder { +// CidBuilder returns the CID Builder for this ProtoNode, it is never nil +func (n *ProtoNode) CidBuilder() cid.Builder { if n.builder == nil { n.builder = v0CidPrefix } return n.builder } -// SetPrefix sets the CID prefix if it is non nil, if prefix is nil then -// it resets it the default value -func (n *ProtoNode) SetPrefix(prefix cid.Builder) { - if prefix == nil { +// SetCidBuilder sets the CID builder if it is non nil, if nil then it +// is reset to the default value +func (n *ProtoNode) SetCidBuilder(builder cid.Builder) { + if builder == nil { n.builder = v0CidPrefix } else { - n.builder = prefix.WithCodec(cid.DagProtobuf) + n.builder = builder.WithCodec(cid.DagProtobuf) n.encoded = nil n.cached = nil } diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index ebfaba7c2..d0e456a0b 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -34,11 +34,10 @@ func DecodeRawBlock(block blocks.Block) (ipld.Node, error) { var _ ipld.DecodeBlockFunc = DecodeRawBlock -// NewRawNodeWPrefix creates a RawNode with the hash function -// specified in prefix. -func NewRawNodeWPrefix(data []byte, prefix cid.Builder) (*RawNode, error) { - prefix = prefix.WithCodec(cid.Raw) - c, err := prefix.Sum(data) +// NewRawNodeWPrefix creates a RawNode using the provided cid builder +func NewRawNodeWPrefix(data []byte, builder cid.Builder) (*RawNode, error) { + builder = builder.WithCodec(cid.Raw) + c, err := builder.Sum(data) if err != nil { return nil, err } From 4d34e888043cb79f78513a3c090934cc16231c61 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 10 Aug 2018 16:09:09 -0400 Subject: [PATCH 2528/3817] gx updates This commit was moved from ipfs/go-unixfs@f84f499c6ef52e9c5c788626747c1e8e08ed5c1d --- unixfs/importer/importer_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/importer/importer_test.go b/unixfs/importer/importer_test.go index 823abaa4b..b39aff57d 100644 --- a/unixfs/importer/importer_test.go +++ b/unixfs/importer/importer_test.go @@ -38,7 +38,7 @@ func getTrickleDag(t testing.TB, size int64, blksize int64) (ipld.Node, ipld.DAG func TestStableCid(t *testing.T) { ds := mdtest.Mock() - buf := make([]byte, 10 * 1024 * 1024) + buf := make([]byte, 10*1024*1024) u.NewSeededRand(0xdeadbeef).Read(buf) r := bytes.NewReader(buf) From dd6b99bf49a7a8c833904ca1f43b138a1d1949ae Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 10 Aug 2018 23:43:14 -0400 Subject: [PATCH 2529/3817] Update to use new Builder interface for creating CIDs. This commit was moved from ipfs/go-unixfs@dc95f6c3f0ac4ab13ebbbae213d3334ce9c23c13 --- unixfs/hamt/hamt.go | 10 +++++----- unixfs/importer/helpers/dagbuilder.go | 12 ++++++------ unixfs/importer/helpers/helpers.go | 4 ++-- unixfs/io/directory.go | 18 +++++++++--------- unixfs/mod/dagmodifier.go | 2 +- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 6887a148d..a28bf0725 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -52,7 +52,7 @@ type Shard struct { tableSize int tableSizeLg2 int - prefix *cid.Prefix + prefix cid.Builder hashFunc uint64 prefixPadStr string @@ -124,25 +124,25 @@ func NewHamtFromDag(dserv ipld.DAGService, nd ipld.Node) (*Shard, error) { ds.children = make([]child, len(pbnd.Links())) ds.bitfield.SetBytes(pbd.GetData()) ds.hashFunc = pbd.GetHashType() - ds.prefix = &ds.nd.Prefix + ds.prefix = ds.nd.CidBuilder() return ds, nil } // SetPrefix sets the CID Prefix -func (ds *Shard) SetPrefix(prefix *cid.Prefix) { +func (ds *Shard) SetPrefix(prefix cid.Builder) { ds.prefix = prefix } // Prefix gets the CID Prefix, may be nil if unset -func (ds *Shard) Prefix() *cid.Prefix { +func (ds *Shard) Prefix() cid.Builder { return ds.prefix } // Node serializes the HAMT structure into a merkledag node with unixfs formatting func (ds *Shard) Node() (ipld.Node, error) { out := new(dag.ProtoNode) - out.SetPrefix(ds.prefix) + out.SetCidBuilder(ds.prefix) cindex := 0 // TODO: optimized 'for each set bit' diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index 387b26304..1331ddbfa 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -26,7 +26,7 @@ type DagBuilderHelper struct { nextData []byte // the next item to return. maxlinks int batch *ipld.Batch - prefix *cid.Prefix + prefix cid.Builder // Filestore support variables. // ---------------------------- @@ -54,7 +54,7 @@ type DagBuilderParams struct { RawLeaves bool // CID Prefix to use if set - Prefix *cid.Prefix + Prefix cid.Builder // DAGService to write blocks to (required) Dagserv ipld.DAGService @@ -146,7 +146,7 @@ func (db *DagBuilderHelper) NewUnixfsNode() *UnixfsNode { } // GetPrefix returns the internal `cid.Prefix` set in the builder. -func (db *DagBuilderHelper) GetPrefix() *cid.Prefix { +func (db *DagBuilderHelper) GetPrefix() cid.Builder { return db.prefix } @@ -166,7 +166,7 @@ func (db *DagBuilderHelper) NewLeaf(data []byte) (*UnixfsNode, error) { raw: true, }, nil } - rawnode, err := dag.NewRawNodeWPrefix(data, *db.prefix) + rawnode, err := dag.NewRawNodeWPrefix(data, db.prefix) if err != nil { return nil, err } @@ -197,7 +197,7 @@ func (db *DagBuilderHelper) NewLeafNode(data []byte) (ipld.Node, error) { if db.prefix == nil { return dag.NewRawNode(data), nil } - rawnode, err := dag.NewRawNodeWPrefix(data, *db.prefix) + rawnode, err := dag.NewRawNodeWPrefix(data, db.prefix) if err != nil { return nil, err } @@ -401,7 +401,7 @@ type FSNodeOverDag struct { func (db *DagBuilderHelper) NewFSNodeOverDag(fsNodeType pb.Data_DataType) *FSNodeOverDag { node := new(FSNodeOverDag) node.dag = new(dag.ProtoNode) - node.dag.SetPrefix(db.GetPrefix()) + node.dag.SetCidBuilder(db.GetPrefix()) node.file = ft.NewFSNode(fsNodeType) diff --git a/unixfs/importer/helpers/helpers.go b/unixfs/importer/helpers/helpers.go index 6fb0f83c8..c8aca63d1 100644 --- a/unixfs/importer/helpers/helpers.go +++ b/unixfs/importer/helpers/helpers.go @@ -61,8 +61,8 @@ func NewUnixfsNodeFromDag(nd *dag.ProtoNode) (*UnixfsNode, error) { } // SetPrefix sets the CID Prefix -func (n *UnixfsNode) SetPrefix(prefix *cid.Prefix) { - n.node.SetPrefix(prefix) +func (n *UnixfsNode) SetPrefix(prefix cid.Builder) { + n.node.SetCidBuilder(prefix) } // NumChildren returns the number of children referenced by this UnixfsNode. diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index 89864566e..64960531f 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -30,7 +30,7 @@ var DefaultShardWidth = 256 type Directory interface { // SetPrefix sets the CID prefix of the root node. - SetPrefix(*cid.Prefix) + SetPrefix(cid.Builder) // AddChild adds a (name, key) pair to the root node. AddChild(context.Context, string, ipld.Node) error @@ -52,7 +52,7 @@ type Directory interface { GetNode() (ipld.Node, error) // GetPrefix returns the CID Prefix used. - GetPrefix() *cid.Prefix + GetPrefix() cid.Builder } // TODO: Evaluate removing `dserv` from this layer and providing it in MFS. @@ -128,8 +128,8 @@ func NewDirectoryFromNode(dserv ipld.DAGService, node ipld.Node) (Directory, err } // SetPrefix implements the `Directory` interface. -func (d *BasicDirectory) SetPrefix(prefix *cid.Prefix) { - d.node.SetPrefix(prefix) +func (d *BasicDirectory) SetPrefix(prefix cid.Builder) { + d.node.SetCidBuilder(prefix) } // AddChild implements the `Directory` interface. It adds (or replaces) @@ -180,8 +180,8 @@ func (d *BasicDirectory) GetNode() (ipld.Node, error) { } // GetPrefix implements the `Directory` interface. -func (d *BasicDirectory) GetPrefix() *cid.Prefix { - return &d.node.Prefix +func (d *BasicDirectory) GetPrefix() cid.Builder { + return d.node.CidBuilder() } // SwitchToSharding returns a HAMT implementation of this directory. @@ -193,7 +193,7 @@ func (d *BasicDirectory) SwitchToSharding(ctx context.Context) (Directory, error if err != nil { return nil, err } - shard.SetPrefix(&d.node.Prefix) + shard.SetPrefix(d.node.CidBuilder()) hamtDir.shard = shard for _, lnk := range d.node.Links() { @@ -212,7 +212,7 @@ func (d *BasicDirectory) SwitchToSharding(ctx context.Context) (Directory, error } // SetPrefix implements the `Directory` interface. -func (d *HAMTDirectory) SetPrefix(prefix *cid.Prefix) { +func (d *HAMTDirectory) SetPrefix(prefix cid.Builder) { d.shard.SetPrefix(prefix) } @@ -252,6 +252,6 @@ func (d *HAMTDirectory) GetNode() (ipld.Node, error) { } // GetPrefix implements the `Directory` interface. -func (d *HAMTDirectory) GetPrefix() *cid.Prefix { +func (d *HAMTDirectory) GetPrefix() cid.Builder { return d.shard.Prefix() } diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 09665b80c..509b3cc81 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -256,7 +256,7 @@ func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64) (*cid.Cid, error) { nd := new(mdag.ProtoNode) nd.SetData(b) - nd.SetPrefix(&nd0.Prefix) + nd.SetCidBuilder(nd0.CidBuilder()) err = dm.dagserv.Add(dm.ctx, nd) if err != nil { return nil, err From ebe84ceeef20365248dd74cd605fdc39da1a962e Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sat, 11 Aug 2018 00:13:57 -0400 Subject: [PATCH 2530/3817] Rename Prefix in names to CidBuilder when a builder is used. This commit was moved from ipfs/go-unixfs@b12f2d836ee0ad9bc8b4b80fa15e1a8fc561a0ca --- unixfs/hamt/hamt.go | 20 +++++------ unixfs/importer/helpers/dagbuilder.go | 52 +++++++++++++-------------- unixfs/importer/helpers/helpers.go | 6 ++-- unixfs/io/directory.go | 32 ++++++++--------- unixfs/mod/dagmodifier.go | 8 ++--- unixfs/test/utils.go | 8 ++--- 6 files changed, 63 insertions(+), 63 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index a28bf0725..4d3bd3b8e 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -52,7 +52,7 @@ type Shard struct { tableSize int tableSizeLg2 int - prefix cid.Builder + builder cid.Builder hashFunc uint64 prefixPadStr string @@ -124,25 +124,25 @@ func NewHamtFromDag(dserv ipld.DAGService, nd ipld.Node) (*Shard, error) { ds.children = make([]child, len(pbnd.Links())) ds.bitfield.SetBytes(pbd.GetData()) ds.hashFunc = pbd.GetHashType() - ds.prefix = ds.nd.CidBuilder() + ds.builder = ds.nd.CidBuilder() return ds, nil } -// SetPrefix sets the CID Prefix -func (ds *Shard) SetPrefix(prefix cid.Builder) { - ds.prefix = prefix +// SetCidBuilder sets the CID Builder +func (ds *Shard) SetCidBuilder(builder cid.Builder) { + ds.builder = builder } -// Prefix gets the CID Prefix, may be nil if unset -func (ds *Shard) Prefix() cid.Builder { - return ds.prefix +// CidBuilder gets the CID Builder, may be nil if unset +func (ds *Shard) CidBuilder() cid.Builder { + return ds.builder } // Node serializes the HAMT structure into a merkledag node with unixfs formatting func (ds *Shard) Node() (ipld.Node, error) { out := new(dag.ProtoNode) - out.SetCidBuilder(ds.prefix) + out.SetCidBuilder(ds.builder) cindex := 0 // TODO: optimized 'for each set bit' @@ -494,7 +494,7 @@ func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val if err != nil { return err } - ns.prefix = ds.prefix + ns.builder = ds.builder chhv := &hashBits{ b: hash([]byte(child.key)), consumed: hv.consumed, diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index 1331ddbfa..4c897fd48 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -19,14 +19,14 @@ import ( // DagBuilderHelper wraps together a bunch of objects needed to // efficiently create unixfs dag trees type DagBuilderHelper struct { - dserv ipld.DAGService - spl chunker.Splitter - recvdErr error - rawLeaves bool - nextData []byte // the next item to return. - maxlinks int - batch *ipld.Batch - prefix cid.Builder + dserv ipld.DAGService + spl chunker.Splitter + recvdErr error + rawLeaves bool + nextData []byte // the next item to return. + maxlinks int + batch *ipld.Batch + cidBuilder cid.Builder // Filestore support variables. // ---------------------------- @@ -53,8 +53,8 @@ type DagBuilderParams struct { // instead of using the unixfs TRaw type RawLeaves bool - // CID Prefix to use if set - Prefix cid.Builder + // CID Builder to use if set + CidBuilder cid.Builder // DAGService to write blocks to (required) Dagserv ipld.DAGService @@ -73,12 +73,12 @@ type DagBuilderParams struct { // chunker.Splitter as data source. func (dbp *DagBuilderParams) New(spl chunker.Splitter) *DagBuilderHelper { db := &DagBuilderHelper{ - dserv: dbp.Dagserv, - spl: spl, - rawLeaves: dbp.RawLeaves, - prefix: dbp.Prefix, - maxlinks: dbp.Maxlinks, - batch: ipld.NewBatch(context.TODO(), dbp.Dagserv), + dserv: dbp.Dagserv, + spl: spl, + rawLeaves: dbp.RawLeaves, + cidBuilder: dbp.CidBuilder, + maxlinks: dbp.Maxlinks, + batch: ipld.NewBatch(context.TODO(), dbp.Dagserv), } if fi, ok := spl.Reader().(files.FileInfo); dbp.NoCopy && ok { db.fullPath = fi.AbsPath() @@ -141,13 +141,13 @@ func (db *DagBuilderHelper) NewUnixfsNode() *UnixfsNode { node: new(dag.ProtoNode), ufmt: ft.NewFSNode(ft.TFile), } - n.SetPrefix(db.prefix) + n.SetCidBuilder(db.cidBuilder) return n } -// GetPrefix returns the internal `cid.Prefix` set in the builder. -func (db *DagBuilderHelper) GetPrefix() cid.Builder { - return db.prefix +// GetCidBuilder returns the internal `cid.CidBuilder` set in the builder. +func (db *DagBuilderHelper) GetCidBuilder() cid.Builder { + return db.cidBuilder } // NewLeaf creates a leaf node filled with data. If rawLeaves is @@ -160,13 +160,13 @@ func (db *DagBuilderHelper) NewLeaf(data []byte) (*UnixfsNode, error) { } if db.rawLeaves { - if db.prefix == nil { + if db.cidBuilder == nil { return &UnixfsNode{ rawnode: dag.NewRawNode(data), raw: true, }, nil } - rawnode, err := dag.NewRawNodeWPrefix(data, db.prefix) + rawnode, err := dag.NewRawNodeWPrefix(data, db.cidBuilder) if err != nil { return nil, err } @@ -194,10 +194,10 @@ func (db *DagBuilderHelper) NewLeafNode(data []byte) (ipld.Node, error) { if db.rawLeaves { // Encapsulate the data in a raw node. - if db.prefix == nil { + if db.cidBuilder == nil { return dag.NewRawNode(data), nil } - rawnode, err := dag.NewRawNodeWPrefix(data, db.prefix) + rawnode, err := dag.NewRawNodeWPrefix(data, db.cidBuilder) if err != nil { return nil, err } @@ -229,7 +229,7 @@ func (db *DagBuilderHelper) newUnixfsBlock() *UnixfsNode { node: new(dag.ProtoNode), ufmt: ft.NewFSNode(ft.TRaw), } - n.SetPrefix(db.prefix) + n.SetCidBuilder(db.cidBuilder) return n } @@ -401,7 +401,7 @@ type FSNodeOverDag struct { func (db *DagBuilderHelper) NewFSNodeOverDag(fsNodeType pb.Data_DataType) *FSNodeOverDag { node := new(FSNodeOverDag) node.dag = new(dag.ProtoNode) - node.dag.SetCidBuilder(db.GetPrefix()) + node.dag.SetCidBuilder(db.GetCidBuilder()) node.file = ft.NewFSNode(fsNodeType) diff --git a/unixfs/importer/helpers/helpers.go b/unixfs/importer/helpers/helpers.go index c8aca63d1..a2e443ea3 100644 --- a/unixfs/importer/helpers/helpers.go +++ b/unixfs/importer/helpers/helpers.go @@ -60,9 +60,9 @@ func NewUnixfsNodeFromDag(nd *dag.ProtoNode) (*UnixfsNode, error) { }, nil } -// SetPrefix sets the CID Prefix -func (n *UnixfsNode) SetPrefix(prefix cid.Builder) { - n.node.SetCidBuilder(prefix) +// SetCidBuilder sets the CID Builder +func (n *UnixfsNode) SetCidBuilder(builder cid.Builder) { + n.node.SetCidBuilder(builder) } // NumChildren returns the number of children referenced by this UnixfsNode. diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index 64960531f..aa1ec8de7 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -29,8 +29,8 @@ var DefaultShardWidth = 256 // (which is the main consumer of this interface). type Directory interface { - // SetPrefix sets the CID prefix of the root node. - SetPrefix(cid.Builder) + // SetCidBuilder sets the CID Builder of the root node. + SetCidBuilder(cid.Builder) // AddChild adds a (name, key) pair to the root node. AddChild(context.Context, string, ipld.Node) error @@ -51,8 +51,8 @@ type Directory interface { // GetNode returns the root of this directory. GetNode() (ipld.Node, error) - // GetPrefix returns the CID Prefix used. - GetPrefix() cid.Builder + // GetCidBuilder returns the CID Builder used. + GetCidBuilder() cid.Builder } // TODO: Evaluate removing `dserv` from this layer and providing it in MFS. @@ -127,9 +127,9 @@ func NewDirectoryFromNode(dserv ipld.DAGService, node ipld.Node) (Directory, err return nil, ErrNotADir } -// SetPrefix implements the `Directory` interface. -func (d *BasicDirectory) SetPrefix(prefix cid.Builder) { - d.node.SetCidBuilder(prefix) +// SetCidBuilder implements the `Directory` interface. +func (d *BasicDirectory) SetCidBuilder(builder cid.Builder) { + d.node.SetCidBuilder(builder) } // AddChild implements the `Directory` interface. It adds (or replaces) @@ -179,8 +179,8 @@ func (d *BasicDirectory) GetNode() (ipld.Node, error) { return d.node, nil } -// GetPrefix implements the `Directory` interface. -func (d *BasicDirectory) GetPrefix() cid.Builder { +// GetCidBuilder implements the `Directory` interface. +func (d *BasicDirectory) GetCidBuilder() cid.Builder { return d.node.CidBuilder() } @@ -193,7 +193,7 @@ func (d *BasicDirectory) SwitchToSharding(ctx context.Context) (Directory, error if err != nil { return nil, err } - shard.SetPrefix(d.node.CidBuilder()) + shard.SetCidBuilder(d.node.CidBuilder()) hamtDir.shard = shard for _, lnk := range d.node.Links() { @@ -211,9 +211,9 @@ func (d *BasicDirectory) SwitchToSharding(ctx context.Context) (Directory, error return hamtDir, nil } -// SetPrefix implements the `Directory` interface. -func (d *HAMTDirectory) SetPrefix(prefix cid.Builder) { - d.shard.SetPrefix(prefix) +// SetCidBuilder implements the `Directory` interface. +func (d *HAMTDirectory) SetCidBuilder(builder cid.Builder) { + d.shard.SetCidBuilder(builder) } // AddChild implements the `Directory` interface. @@ -251,7 +251,7 @@ func (d *HAMTDirectory) GetNode() (ipld.Node, error) { return d.shard.Node() } -// GetPrefix implements the `Directory` interface. -func (d *HAMTDirectory) GetPrefix() cid.Builder { - return d.shard.Prefix() +// GetCidBuilder implements the `Directory` interface. +func (d *HAMTDirectory) GetCidBuilder() cid.Builder { + return d.shard.CidBuilder() } diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 509b3cc81..f6e5f4820 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -345,10 +345,10 @@ func (dm *DagModifier) appendData(nd ipld.Node, spl chunker.Splitter) (ipld.Node switch nd := nd.(type) { case *mdag.ProtoNode, *mdag.RawNode: dbp := &help.DagBuilderParams{ - Dagserv: dm.dagserv, - Maxlinks: help.DefaultLinksPerBlock, - Prefix: &dm.Prefix, - RawLeaves: dm.RawLeaves, + Dagserv: dm.dagserv, + Maxlinks: help.DefaultLinksPerBlock, + CidBuilder: dm.Prefix, + RawLeaves: dm.RawLeaves, } return trickle.Append(dm.ctx, nd, dbp.New(spl)) default: diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index cc8fe4dd0..fdd307c56 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -61,10 +61,10 @@ func GetNode(t testing.TB, dserv ipld.DAGService, data []byte, opts NodeOpts) ip in := bytes.NewReader(data) dbp := h.DagBuilderParams{ - Dagserv: dserv, - Maxlinks: h.DefaultLinksPerBlock, - Prefix: &opts.Prefix, - RawLeaves: opts.RawLeavesUsed, + Dagserv: dserv, + Maxlinks: h.DefaultLinksPerBlock, + CidBuilder: opts.Prefix, + RawLeaves: opts.RawLeavesUsed, } node, err := trickle.Layout(dbp.New(SizeSplitterGen(500)(in))) From 1750c87f90ea50ee435f24b3d16a263ce67da2ca Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sun, 12 Aug 2018 19:15:07 -0400 Subject: [PATCH 2531/3817] Gx updates and fixes to use new cid.Builder interface. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-mfs@3c4ed3ae040c6f05c5510d4e93ce48a325f06550 --- mfs/dir.go | 22 +++++++++++----------- mfs/fd.go | 2 +- mfs/file.go | 10 +++++----- mfs/mfs_test.go | 24 ++++++++++++------------ mfs/ops.go | 8 ++++---- mfs/repub_test.go | 2 +- mfs/system.go | 8 ++++---- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 37d88620a..eafff388d 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -9,13 +9,13 @@ import ( "sync" "time" - dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" - ft "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs" - uio "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs/io" - ufspb "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs/pb" + ft "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs" + uio "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs/io" + ufspb "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs/pb" + dag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" ) var ErrNotYetImplemented = errors.New("not yet implemented") @@ -64,13 +64,13 @@ func NewDirectory(ctx context.Context, name string, node ipld.Node, parent child } // GetPrefix gets the CID prefix of the root node -func (d *Directory) GetPrefix() *cid.Prefix { - return d.unixfsDir.GetPrefix() +func (d *Directory) GetPrefix() cid.Builder { + return d.unixfsDir.GetCidBuilder() } // SetPrefix sets the CID prefix -func (d *Directory) SetPrefix(prefix *cid.Prefix) { - d.unixfsDir.SetPrefix(prefix) +func (d *Directory) SetPrefix(prefix cid.Builder) { + d.unixfsDir.SetCidBuilder(prefix) } // closeChild updates the child by the given name to the dag node 'nd' @@ -307,7 +307,7 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { } ndir := ft.EmptyDirNode() - ndir.SetPrefix(d.GetPrefix()) + ndir.SetCidBuilder(d.GetPrefix()) err = d.dserv.Add(d.ctx, ndir) if err != nil { diff --git a/mfs/fd.go b/mfs/fd.go index 1298c618c..114314adc 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -4,7 +4,7 @@ import ( "fmt" "io" - mod "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs/mod" + mod "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs/mod" context "context" ) diff --git a/mfs/file.go b/mfs/file.go index f577e11de..f680a58c1 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -5,12 +5,12 @@ import ( "fmt" "sync" - dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" - ft "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs" - mod "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs/mod" + ft "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs" + mod "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs/mod" + dag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" - chunker "gx/ipfs/Qmc3UwSvJkntxu2gKDPCqJEzmhqVeJtTbrVxJm6tsdmMF1/go-ipfs-chunker" + ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" + chunker "gx/ipfs/Qme4ThG6LN6EMrMYyf2AMywAZaGbTYxQu4njfcSSkcisLi/go-ipfs-chunker" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index a8aa80981..3f94577c8 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,19 +14,19 @@ import ( "testing" "time" - "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" - dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" - ft "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs" - importer "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs/importer" - uio "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs/io" - bserv "gx/ipfs/QmeZMtdkNG7u2CohGSL8mzAdZY2c3B1coYE91wvbzip1pF/go-blockservice" - + bserv "gx/ipfs/QmSQDddUJRZCBEcEKp3icdiRUrVofvMSWmGyMXSysqjNCL/go-blockservice" + "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + ft "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs" + importer "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs/importer" + uio "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs/io" + dag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" + + offline "gx/ipfs/QmNxamk8jB6saNF5G37Tiipf2JEnxt7qvesbngPMe24wMS/go-ipfs-exchange-offline" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - bstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" - offline "gx/ipfs/QmWURzU3XRY4wYBsu2LHukKKHp5skkYB1K357nzpbEvRY4/go-ipfs-exchange-offline" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" - chunker "gx/ipfs/Qmc3UwSvJkntxu2gKDPCqJEzmhqVeJtTbrVxJm6tsdmMF1/go-ipfs-chunker" + ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" + bstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" + chunker "gx/ipfs/Qme4ThG6LN6EMrMYyf2AMywAZaGbTYxQu4njfcSSkcisLi/go-ipfs-chunker" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/mfs/ops.go b/mfs/ops.go index c2c2d0e54..4bed33ed0 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -6,10 +6,10 @@ import ( gopath "path" "strings" - path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" ) // Mv moves the file or directory at 'src' to 'dst' @@ -101,7 +101,7 @@ func PutNode(r *Root, path string, nd ipld.Node) error { type MkdirOpts struct { Mkparents bool Flush bool - Prefix *cid.Prefix + Prefix cid.Builder } // Mkdir creates a directory at 'path' under the directory 'd', creating diff --git a/mfs/repub_test.go b/mfs/repub_test.go index cb1e4437f..106de1b2f 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -6,7 +6,7 @@ import ( "time" ci "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil/ci" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index cbeac035e..c8792a3be 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -16,12 +16,12 @@ import ( "sync" "time" - dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" - ft "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs" + ft "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs" + dag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" ) var ErrNotExist = errors.New("no such rootfs") From 2f477f93e968fcf885fd51f9a109ac184f650a51 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sun, 12 Aug 2018 19:15:07 -0400 Subject: [PATCH 2532/3817] Gx updates and fixes to use new cid.Builder interface. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@7ea841164baf1b0a3d951332fabfd0b52b2c8d03 --- pinning/pinner/gc/gc.go | 14 +++++++------- pinning/pinner/pin.go | 6 +++--- pinning/pinner/pin_test.go | 10 +++++----- pinning/pinner/set.go | 6 +++--- pinning/pinner/set_test.go | 10 +++++----- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index a785d0776..ba031f90b 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,15 +8,15 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" - bserv "gx/ipfs/QmeZMtdkNG7u2CohGSL8mzAdZY2c3B1coYE91wvbzip1pF/go-blockservice" + bserv "gx/ipfs/QmSQDddUJRZCBEcEKp3icdiRUrVofvMSWmGyMXSysqjNCL/go-blockservice" + dag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" - "gx/ipfs/QmQwgv79RHrRnoXmhnpC1BPtY55HHeneGMpPwmmBU1fUAG/go-verifcid" - bstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" + offline "gx/ipfs/QmNxamk8jB6saNF5G37Tiipf2JEnxt7qvesbngPMe24wMS/go-ipfs-exchange-offline" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - offline "gx/ipfs/QmWURzU3XRY4wYBsu2LHukKKHp5skkYB1K357nzpbEvRY4/go-ipfs-exchange-offline" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" + "gx/ipfs/QmV1pEFHk8ijeessqG52SjHuxuehahbeHrxXk4QEkgfPHj/go-verifcid" + bstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" dstore "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index e60c80eee..28401ea8d 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,11 +10,11 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" + mdag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index dfb6b75eb..96110530e 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,13 +5,13 @@ import ( "testing" "time" - mdag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" - bs "gx/ipfs/QmeZMtdkNG7u2CohGSL8mzAdZY2c3B1coYE91wvbzip1pF/go-blockservice" + bs "gx/ipfs/QmSQDddUJRZCBEcEKp3icdiRUrVofvMSWmGyMXSysqjNCL/go-blockservice" + mdag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" + offline "gx/ipfs/QmNxamk8jB6saNF5G37Tiipf2JEnxt7qvesbngPMe24wMS/go-ipfs-exchange-offline" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - blockstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" - offline "gx/ipfs/QmWURzU3XRY4wYBsu2LHukKKHp5skkYB1K357nzpbEvRY4/go-ipfs-exchange-offline" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + blockstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index d3b57e818..6a293d60b 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,10 +10,10 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" + "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 3b095c4cc..8911f9f3c 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,12 +5,12 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" - bserv "gx/ipfs/QmeZMtdkNG7u2CohGSL8mzAdZY2c3B1coYE91wvbzip1pF/go-blockservice" + bserv "gx/ipfs/QmSQDddUJRZCBEcEKp3icdiRUrVofvMSWmGyMXSysqjNCL/go-blockservice" + dag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" - blockstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" - offline "gx/ipfs/QmWURzU3XRY4wYBsu2LHukKKHp5skkYB1K357nzpbEvRY4/go-ipfs-exchange-offline" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + offline "gx/ipfs/QmNxamk8jB6saNF5G37Tiipf2JEnxt7qvesbngPMe24wMS/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) From d4d147327462b157f9ec3854038363e196cc1bc1 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sun, 12 Aug 2018 19:15:07 -0400 Subject: [PATCH 2533/3817] Gx updates and fixes to use new cid.Builder interface. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@7dc90915bc611f90e1ade9ef64640bc3ec0b6402 --- filestore/filestore.go | 8 ++++---- filestore/filestore_test.go | 8 ++++---- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 29e966491..a571e7eec 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,11 +11,11 @@ import ( "context" "errors" - blockstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" - blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + posinfo "gx/ipfs/QmWjvVuzD3Dqf3VznGAXUs1VqpG4dd35ounVCnLam8v7Xt/go-ipfs-posinfo" + blockstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" + blocks "gx/ipfs/QmZXvzTJTiN6p469osBUtEwm4WwhXXoWcHC8aTS1cAJkjy/go-block-format" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 77ed25a4a..5df2496ec 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,11 +7,11 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmXkZeJmx4c3ddjw81DQMUpM1e5LjAack5idzZYWUb2qAJ/go-merkledag" + dag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" - blockstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" - posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + posinfo "gx/ipfs/QmWjvVuzD3Dqf3VznGAXUs1VqpG4dd35ounVCnLam8v7Xt/go-ipfs-posinfo" + blockstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 67d657a8c..5866d8286 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,12 +10,12 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - blockstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" - posinfo "gx/ipfs/QmSHjPDw8yNgLZ7cBfX7w3Smn7PHwYhNEpd4LHQQxUg35L/go-ipfs-posinfo" - blocks "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + dshelp "gx/ipfs/QmWf5idGZ55KWUUo2EKM3BTeunjSiSgWbo4bLpYafENBgp/go-ipfs-ds-help" + posinfo "gx/ipfs/QmWjvVuzD3Dqf3VznGAXUs1VqpG4dd35ounVCnLam8v7Xt/go-ipfs-posinfo" + blockstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" proto "gx/ipfs/QmZHU2gx42NPTYXzw6pJkuX6xCE7bKECp6e8QcPdoLx8sx/protobuf/proto" - dshelp "gx/ipfs/Qmd8UZEDddMaCnQ1G5eSrUhN3coX19V7SyXNQGWnAvUsnT/go-ipfs-ds-help" + blocks "gx/ipfs/QmZXvzTJTiN6p469osBUtEwm4WwhXXoWcHC8aTS1cAJkjy/go-block-format" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsns "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/namespace" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" diff --git a/filestore/util.go b/filestore/util.go index 3eed174c5..2529a6c3d 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,9 +6,9 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - blockstore "gx/ipfs/QmRNFh4wm6FgTDrtsWmnvEP9NTuEa3Ykf72y1LXCyevbGW/go-ipfs-blockstore" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - dshelp "gx/ipfs/Qmd8UZEDddMaCnQ1G5eSrUhN3coX19V7SyXNQGWnAvUsnT/go-ipfs-ds-help" + dshelp "gx/ipfs/QmWf5idGZ55KWUUo2EKM3BTeunjSiSgWbo4bLpYafENBgp/go-ipfs-ds-help" + blockstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" ) From d62606c1628b0835394ea936033895f9a128b3f4 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sun, 12 Aug 2018 19:15:07 -0400 Subject: [PATCH 2534/3817] Gx updates and fixes to use new cid.Builder interface. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-namesys@467c63e7d5058ffb40a496e5def50a284701a828 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 10 +++++----- namesys/namesys.go | 4 ++-- namesys/namesys_test.go | 6 +++--- namesys/proquint.go | 2 +- namesys/publisher.go | 6 +++--- namesys/publisher_test.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 4 ++-- namesys/routing.go | 8 ++++---- 14 files changed, 28 insertions(+), 28 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 96f10c80f..46f2953c3 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index 154c629c3..f424de7f8 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 05e93c1ef..fa9ad29b0 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index 1e0c083ce..0d771e708 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 3b5194a23..ca280ac56 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,21 +6,21 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" record "gx/ipfs/QmUTQSGgjs8CHm9yBcUHicpRs7C9abhyZiBwjzCUp1pNgX/go-libp2p-record" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" testutil "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil" + mockrouting "gx/ipfs/QmYAC1wnzfs7LwgrNU5K5MK2rnisjF7kAxWrwzZTLNNjVe/go-ipfs-routing/mock" + offline "gx/ipfs/QmYAC1wnzfs7LwgrNU5K5MK2rnisjF7kAxWrwzZTLNNjVe/go-ipfs-routing/offline" pstore "gx/ipfs/QmYLXCWN2myozZpx8Wx4UjrRuQuhY3YtWoMi6SHaXii6aM/go-libp2p-peerstore" - mockrouting "gx/ipfs/Qma19TdQ7W26jbfuPgdo9Zi4qtjks1zeXzX86mtEYWYCiw/go-ipfs-routing/mock" - offline "gx/ipfs/Qma19TdQ7W26jbfuPgdo9Zi4qtjks1zeXzX86mtEYWYCiw/go-ipfs-routing/offline" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" - routing "gx/ipfs/QmewrvpGvgK9qkCtXsGNwXiQzyux4jcHNjoyVrGdsgtNK5/go-libp2p-routing" - ropts "gx/ipfs/QmewrvpGvgK9qkCtXsGNwXiQzyux4jcHNjoyVrGdsgtNK5/go-libp2p-routing/options" + routing "gx/ipfs/QmfGeECX7CxJAFXwGKx2cn7csnxAtwJw8e3XtoNVf6bqcv/go-libp2p-routing" + ropts "gx/ipfs/QmfGeECX7CxJAFXwGKx2cn7csnxAtwJw8e3XtoNVf6bqcv/go-libp2p-routing/options" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 216525ee3..b52390836 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" @@ -14,7 +14,7 @@ import ( isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - routing "gx/ipfs/QmewrvpGvgK9qkCtXsGNwXiQzyux4jcHNjoyVrGdsgtNK5/go-libp2p-routing" + routing "gx/ipfs/QmfGeECX7CxJAFXwGKx2cn7csnxAtwJw8e3XtoNVf6bqcv/go-libp2p-routing" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 3ab9bd49b..9e6e7fd0f 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,13 +7,13 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" - "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs" + path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" + offroute "gx/ipfs/QmYAC1wnzfs7LwgrNU5K5MK2rnisjF7kAxWrwzZTLNNjVe/go-ipfs-routing/offline" pstore "gx/ipfs/QmYLXCWN2myozZpx8Wx4UjrRuQuhY3YtWoMi6SHaXii6aM/go-libp2p-peerstore" - offroute "gx/ipfs/Qma19TdQ7W26jbfuPgdo9Zi4qtjks1zeXzX86mtEYWYCiw/go-ipfs-routing/offline" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" diff --git a/namesys/proquint.go b/namesys/proquint.go index 7e571b42b..d0a488636 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index b50f54185..04d57c19c 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -8,8 +8,8 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" - ft "gx/ipfs/Qmdqe1sKBpz6W8xFDptGfmzgCPQ5CXNuQPhZeELqMowgsQ/go-unixfs" + path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + ft "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" @@ -18,7 +18,7 @@ import ( proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dsquery "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" - routing "gx/ipfs/QmewrvpGvgK9qkCtXsGNwXiQzyux4jcHNjoyVrGdsgtNK5/go-libp2p-routing" + routing "gx/ipfs/QmfGeECX7CxJAFXwGKx2cn7csnxAtwJw8e3XtoNVf6bqcv/go-libp2p-routing" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 42cb85973..e40d9be5e 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -8,11 +8,11 @@ import ( ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" + dshelp "gx/ipfs/QmWf5idGZ55KWUUo2EKM3BTeunjSiSgWbo4bLpYafENBgp/go-ipfs-ds-help" testutil "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil" + mockrouting "gx/ipfs/QmYAC1wnzfs7LwgrNU5K5MK2rnisjF7kAxWrwzZTLNNjVe/go-ipfs-routing/mock" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" - mockrouting "gx/ipfs/Qma19TdQ7W26jbfuPgdo9Zi4qtjks1zeXzX86mtEYWYCiw/go-ipfs-routing/mock" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" - dshelp "gx/ipfs/Qmd8UZEDddMaCnQ1G5eSrUhN3coX19V7SyXNQGWnAvUsnT/go-ipfs-ds-help" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 57be87ae9..1b221df11 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index b51bbcdc8..82b60b36a 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" mocknet "gx/ipfs/QmUDzeFgYrRmHL2hUB6NZmqcBVQtUzETwmFRUc9onfSSHr/go-libp2p/p2p/net/mock" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 623f6213d..906ecac04 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" - path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" testutil "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil" - mockrouting "gx/ipfs/Qma19TdQ7W26jbfuPgdo9Zi4qtjks1zeXzX86mtEYWYCiw/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmYAC1wnzfs7LwgrNU5K5MK2rnisjF7kAxWrwzZTLNNjVe/go-ipfs-routing/mock" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" diff --git a/namesys/routing.go b/namesys/routing.go index 3a4711af4..5af625b8e 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,17 +6,17 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" + dht "gx/ipfs/QmSRMxYCadAPQrCT38qFttGvE77bXYZfkQK7vLAgzj8r9K/go-libp2p-kad-dht" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" pb "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns/pb" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - dht "gx/ipfs/QmZAsayEQakfFbHyakgHRKHwBTWrwuSBTfaMyxJZUG97VC/go-libp2p-kad-dht" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" - routing "gx/ipfs/QmewrvpGvgK9qkCtXsGNwXiQzyux4jcHNjoyVrGdsgtNK5/go-libp2p-routing" + routing "gx/ipfs/QmfGeECX7CxJAFXwGKx2cn7csnxAtwJw8e3XtoNVf6bqcv/go-libp2p-routing" ) var log = logging.Logger("namesys") From d91c0d7ac499f29b5b49219df4c1fdbf2e222bcd Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sun, 12 Aug 2018 19:15:07 -0400 Subject: [PATCH 2535/3817] Gx updates and fixes to use new cid.Builder interface. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/interface-go-ipfs-core@05f527a4ab81c36f8965de35bdb8352a92a61718 --- coreiface/coreapi.go | 2 +- coreiface/dag.go | 2 +- coreiface/object.go | 4 ++-- coreiface/options/dag.go | 2 +- coreiface/path.go | 4 ++-- coreiface/unixfs.go | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index 82a2ebf4e..d7614f01d 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -5,7 +5,7 @@ package iface import ( "context" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" ) // CoreAPI defines an unified interface to IPFS for Go programs diff --git a/coreiface/dag.go b/coreiface/dag.go index 01d6112e7..77577d0fc 100644 --- a/coreiface/dag.go +++ b/coreiface/dag.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" ) // DagOps groups operations that can be batched together diff --git a/coreiface/object.go b/coreiface/object.go index 3eb0ea9ef..dc86f46c1 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -6,8 +6,8 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" ) // ObjectStat provides information about dag nodes diff --git a/coreiface/options/dag.go b/coreiface/options/dag.go index 57465deee..a43a144fc 100644 --- a/coreiface/options/dag.go +++ b/coreiface/options/dag.go @@ -3,7 +3,7 @@ package options import ( "math" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" ) type DagPutSettings struct { diff --git a/coreiface/path.go b/coreiface/path.go index c51d31645..28e0f431c 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,9 +1,9 @@ package iface import ( - ipfspath "gx/ipfs/QmPqCBrmkm7jNfYi7xFS7mUZsrN6DEumBMrxLnL7axNJx1/go-path" + ipfspath "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" ) //TODO: merge with ipfspath so we don't depend on it diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 1ddc20674..0ec63d516 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -4,7 +4,7 @@ import ( "context" "io" - ipld "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" ) // UnixfsAPI is the basic interface to immutable files in IPFS From 3bac30d60d41143a515fcac2711143200cb0d994 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sun, 12 Aug 2018 20:49:49 -0400 Subject: [PATCH 2536/3817] Rename prefix to CidBuilder in names when a cid.Builder is used. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-mfs@81cd16a2854aef3b830924e1d2e19ff7fc1991f9 --- mfs/dir.go | 12 ++++++------ mfs/ops.go | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index eafff388d..ea5e9c847 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -63,14 +63,14 @@ func NewDirectory(ctx context.Context, name string, node ipld.Node, parent child }, nil } -// GetPrefix gets the CID prefix of the root node -func (d *Directory) GetPrefix() cid.Builder { +// GetCidBuilder gets the CID builder of the root node +func (d *Directory) GetCidBuilder() cid.Builder { return d.unixfsDir.GetCidBuilder() } -// SetPrefix sets the CID prefix -func (d *Directory) SetPrefix(prefix cid.Builder) { - d.unixfsDir.SetCidBuilder(prefix) +// SetCidBuilder sets the CID builder +func (d *Directory) SetCidBuilder(b cid.Builder) { + d.unixfsDir.SetCidBuilder(b) } // closeChild updates the child by the given name to the dag node 'nd' @@ -307,7 +307,7 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { } ndir := ft.EmptyDirNode() - ndir.SetCidBuilder(d.GetPrefix()) + ndir.SetCidBuilder(d.GetCidBuilder()) err = d.dserv.Add(d.ctx, ndir) if err != nil { diff --git a/mfs/ops.go b/mfs/ops.go index 4bed33ed0..8b14abc8d 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -99,9 +99,9 @@ func PutNode(r *Root, path string, nd ipld.Node) error { // MkdirOpts is used by Mkdir type MkdirOpts struct { - Mkparents bool - Flush bool - Prefix cid.Builder + Mkparents bool + Flush bool + CidBuilder cid.Builder } // Mkdir creates a directory at 'path' under the directory 'd', creating @@ -136,8 +136,8 @@ func Mkdir(r *Root, pth string, opts MkdirOpts) error { if err != nil { return err } - if opts.Prefix != nil { - mkd.SetPrefix(opts.Prefix) + if opts.CidBuilder != nil { + mkd.SetCidBuilder(opts.CidBuilder) } fsn = mkd } else if err != nil { @@ -157,8 +157,8 @@ func Mkdir(r *Root, pth string, opts MkdirOpts) error { return err } } - if opts.Prefix != nil { - final.SetPrefix(opts.Prefix) + if opts.CidBuilder != nil { + final.SetCidBuilder(opts.CidBuilder) } if opts.Flush { From d1dad64b1c2d50bd0f05c37cfec880bb8226ba92 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 12 Aug 2018 18:12:19 -0700 Subject: [PATCH 2537/3817] Refactor to new algorithm, add tests and tool This commit was moved from ipld/go-car@d936d8ce635bc303e4c8993dc40c65c70f074113 --- ipld/car/car.go | 188 ++++++++++++++++++++++++------------------ ipld/car/car/main.go | 116 ++++++++++++++++++++++++++ ipld/car/car_test.go | 71 ++++++++++++++++ ipld/car/util/util.go | 104 +++++++++++++++++++++++ 4 files changed, 398 insertions(+), 81 deletions(-) create mode 100644 ipld/car/car/main.go create mode 100644 ipld/car/car_test.go create mode 100644 ipld/car/util/util.go diff --git a/ipld/car/car.go b/ipld/car/car.go index 3f1996aac..2ab1a3b20 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -1,132 +1,158 @@ package car import ( - "archive/tar" + "bufio" "context" "fmt" "io" - "io/ioutil" - "github.com/ipfs/go-block-format" - cid "github.com/ipfs/go-cid" - bstore "github.com/ipfs/go-ipfs-blockstore" - format "github.com/ipfs/go-ipld-format" - dag "github.com/ipfs/go-merkledag" + util "github.com/ipfs/go-car/util" + + cbor "gx/ipfs/QmSyK1ZiAP98YvnxsTfQpb669V2xeTHRbG4Y6fgKS3vVSd/go-ipld-cbor" + "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + format "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + bstore "gx/ipfs/QmcD7SqfyQyA91TZUQ7VPRYbGarxmY7EsQewVYMuN5LNSv/go-ipfs-blockstore" + dag "gx/ipfs/QmeCaeBmCCEJrZahwXY4G2G8zRaNBWskrfKWoQ6Xv6c1DR/go-merkledag" ) -func WriteCar(ctx context.Context, ds format.DAGService, root *cid.Cid, w io.Writer) error { - tw := tar.NewWriter(w) +func init() { + cbor.RegisterCborType(CarHeader{}) +} - rh := &tar.Header{ - Typeflag: tar.TypeSymlink, - Name: "root", - Linkname: root.String(), - } - if err := tw.WriteHeader(rh); err != nil { - return err +type CarHeader struct { + Roots []*cid.Cid + Version uint64 +} + +type carWriter struct { + ds format.DAGService + w io.Writer +} + +func WriteCar(ctx context.Context, ds format.DAGService, roots []*cid.Cid, w io.Writer) error { + cw := &carWriter{ds: ds, w: w} + + h := &CarHeader{ + Roots: roots, + Version: 1, } - cw := &carWriter{ds: ds, tw: tw} + if err := cw.WriteHeader(h); err != nil { + return fmt.Errorf("failed to write car header: %s", err) + } seen := cid.NewSet() - if err := dag.EnumerateChildren(ctx, cw.enumGetLinks, root, seen.Visit); err != nil { - return err + for _, r := range roots { + if err := dag.EnumerateChildren(ctx, cw.enumGetLinks, r, seen.Visit); err != nil { + return err + } } - - return tw.Flush() + return nil } -func LoadCar(ctx context.Context, bs bstore.Blockstore, r io.Reader) (*cid.Cid, error) { - tr := tar.NewReader(r) - root, err := tr.Next() +func ReadHeader(br *bufio.Reader) (*CarHeader, error) { + hb, err := util.LdRead(br) if err != nil { return nil, err } - if root.Name != "root" || root.Typeflag != tar.TypeSymlink { - return nil, fmt.Errorf("expected first entry in CAR to by symlink named 'root'") + var ch CarHeader + if err := cbor.DecodeInto(hb, &ch); err != nil { + return nil, err } - rootcid, err := cid.Decode(root.Linkname) + return &ch, nil +} + +func (cw *carWriter) WriteHeader(h *CarHeader) error { + hb, err := cbor.DumpObject(h) if err != nil { - return nil, err + return err } - for { - obj, err := tr.Next() - if err != nil { - if err == io.EOF { - break - } - return nil, err - } + return util.LdWrite(cw.w, hb) +} - c, err := cid.Decode(obj.Name) - if err != nil { - return nil, err - } +func (cw *carWriter) enumGetLinks(ctx context.Context, c *cid.Cid) ([]*format.Link, error) { + nd, err := cw.ds.Get(ctx, c) + if err != nil { + return nil, err + } - // safety 1st - limr := io.LimitReader(tr, 2<<20) - data, err := ioutil.ReadAll(limr) - if err != nil { - return nil, err - } + if err := cw.writeNode(ctx, nd); err != nil { + return nil, err + } - hashed, err := c.Prefix().Sum(data) - if err != nil { - return nil, err - } + return nd.Links(), nil +} - if !hashed.Equals(c) { - return nil, fmt.Errorf("mismatch in content integrity, name: %s, data: %s", c, hashed) - } +func (cw *carWriter) writeNode(ctx context.Context, nd format.Node) error { + return util.LdWrite(cw.w, nd.Cid().Bytes(), nd.RawData()) +} - blk, err := blocks.NewBlockWithCid(data, c) - if err != nil { - return nil, err - } +type carReader struct { + br *bufio.Reader + Header *CarHeader +} - if err := bs.Put(blk); err != nil { - return nil, err - } +func NewCarReader(r io.Reader) (*carReader, error) { + br := bufio.NewReader(r) + ch, err := ReadHeader(br) + if err != nil { + return nil, err } - return rootcid, nil -} + if len(ch.Roots) == 0 { + return nil, fmt.Errorf("empty car") + } -type carWriter struct { - ds format.DAGService - tw *tar.Writer + if ch.Version != 1 { + return nil, fmt.Errorf("invalid car version: %d", ch.Version) + } + + return &carReader{ + br: br, + Header: ch, + }, nil } -func (cw *carWriter) enumGetLinks(ctx context.Context, c *cid.Cid) ([]*format.Link, error) { - nd, err := cw.ds.Get(ctx, c) +func (cr *carReader) Next() (blocks.Block, error) { + c, data, err := util.ReadNode(cr.br) if err != nil { return nil, err } - if err := cw.writeNode(ctx, nd); err != nil { + hashed, err := c.Prefix().Sum(data) + if err != nil { return nil, err } - return nd.Links(), nil + if !hashed.Equals(c) { + return nil, fmt.Errorf("mismatch in content integrity, name: %s, data: %s", c, hashed) + } + + return blocks.NewBlockWithCid(data, c) } -func (cw *carWriter) writeNode(ctx context.Context, nd format.Node) error { - hdr := &tar.Header{ - Name: nd.Cid().String(), - Typeflag: tar.TypeReg, - Size: int64(len(nd.RawData())), +func LoadCar(bs bstore.Blockstore, r io.Reader) (*CarHeader, error) { + cr, err := NewCarReader(r) + if err != nil { + return nil, err } - if err := cw.tw.WriteHeader(hdr); err != nil { - return err - } + for { + blk, err := cr.Next() + switch err { + case io.EOF: + return cr.Header, nil + default: + return nil, err + case nil: + } - if _, err := cw.tw.Write(nd.RawData()); err != nil { - return err + if err := bs.Put(blk); err != nil { + return nil, err + } } - - return nil } diff --git a/ipld/car/car/main.go b/ipld/car/car/main.go new file mode 100644 index 000000000..97127ca7c --- /dev/null +++ b/ipld/car/car/main.go @@ -0,0 +1,116 @@ +package main + +import ( + "bufio" + "encoding/json" + "fmt" + "io" + "os" + + "github.com/ipfs/go-car" + + cli "github.com/urfave/cli" +) + +var headerCmd = cli.Command{ + Name: "header", + Action: func(c *cli.Context) error { + if !c.Args().Present() { + return fmt.Errorf("must pass a car file to inspect") + } + arg := c.Args().First() + + fi, err := os.Open(arg) + if err != nil { + return err + } + defer fi.Close() + + ch, err := car.ReadHeader(bufio.NewReader(fi)) + if err != nil { + return err + } + + b, err := json.MarshalIndent(ch, "", " ") + if err != nil { + return err + } + fmt.Println(string(b)) + return nil + }, +} + +var verifyCmd = cli.Command{ + Name: "verify", + Action: func(c *cli.Context) error { + if !c.Args().Present() { + return fmt.Errorf("must pass a car file to inspect") + } + arg := c.Args().First() + + fi, err := os.Open(arg) + if err != nil { + return err + } + defer fi.Close() + + cr, err := car.NewCarReader(fi) + if err != nil { + return err + } + + for { + _, err := cr.Next() + switch err { + case io.EOF: + return nil + default: + return err + case nil: + } + } + }, +} + +var lsCmd = cli.Command{ + Name: "ls", + Action: func(c *cli.Context) error { + if !c.Args().Present() { + return fmt.Errorf("must pass a car file to inspect") + } + arg := c.Args().First() + + fi, err := os.Open(arg) + if err != nil { + return err + } + defer fi.Close() + + cr, err := car.NewCarReader(fi) + if err != nil { + return err + } + + for { + blk, err := cr.Next() + switch err { + case io.EOF: + return nil + default: + return err + case nil: + } + fmt.Println(blk.Cid()) + } + }, +} + +func main() { + app := cli.NewApp() + app.Commands = []cli.Command{ + headerCmd, + lsCmd, + verifyCmd, + } + app.RunAndExitOnError() +} diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go new file mode 100644 index 000000000..169c00520 --- /dev/null +++ b/ipld/car/car_test.go @@ -0,0 +1,71 @@ +package car + +import ( + "bytes" + "context" + "testing" + + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + format "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" + dag "gx/ipfs/QmeCaeBmCCEJrZahwXY4G2G8zRaNBWskrfKWoQ6Xv6c1DR/go-merkledag" + dstest "gx/ipfs/QmeCaeBmCCEJrZahwXY4G2G8zRaNBWskrfKWoQ6Xv6c1DR/go-merkledag/test" +) + +func assertAddNodes(t *testing.T, ds format.DAGService, nds ...format.Node) { + for _, nd := range nds { + if err := ds.Add(context.Background(), nd); err != nil { + t.Fatal(err) + } + } +} + +func TestRoundtrip(t *testing.T) { + dserv := dstest.Mock() + a := dag.NewRawNode([]byte("aaaa")) + b := dag.NewRawNode([]byte("bbbb")) + c := dag.NewRawNode([]byte("cccc")) + + nd1 := &dag.ProtoNode{} + nd1.AddNodeLink("cat", a) + + nd2 := &dag.ProtoNode{} + nd2.AddNodeLink("first", nd1) + nd2.AddNodeLink("dog", b) + + nd3 := &dag.ProtoNode{} + nd3.AddNodeLink("second", nd2) + nd3.AddNodeLink("bear", c) + + assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) + + buf := new(bytes.Buffer) + if err := WriteCar(context.Background(), dserv, []*cid.Cid{nd3.Cid()}, buf); err != nil { + t.Fatal(err) + } + + bserv := dstest.Bserv() + ch, err := LoadCar(bserv.Blockstore(), buf) + if err != nil { + t.Fatal(err) + } + + if len(ch.Roots) != 1 { + t.Fatal("should have one root") + } + + if !ch.Roots[0].Equals(nd3.Cid()) { + t.Fatal("got wrong cid") + } + + bs := bserv.Blockstore() + for _, nd := range []format.Node{a, b, c, nd1, nd2, nd3} { + has, err := bs.Has(nd.Cid()) + if err != nil { + t.Fatal(err) + } + + if !has { + t.Fatal("should have cid in blockstore") + } + } +} diff --git a/ipld/car/util/util.go b/ipld/car/util/util.go new file mode 100644 index 000000000..e39aaee08 --- /dev/null +++ b/ipld/car/util/util.go @@ -0,0 +1,104 @@ +package util + +import ( + "bufio" + "bytes" + "encoding/binary" + "fmt" + "io" + + mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" +) + +var cidv0Pref = []byte{0x12, 0x20} + +type BytesReader interface { + io.Reader + io.ByteReader +} + +// TODO: this belongs in the go-cid package +func ReadCid(buf []byte) (*cid.Cid, int, error) { + if bytes.Equal(buf[:2], cidv0Pref) { + c, err := cid.Cast(buf[:34]) + return c, 34, err + } + + br := bytes.NewReader(buf) + + // assume cidv1 + vers, err := binary.ReadUvarint(br) + if err != nil { + return nil, 0, err + } + + // TODO: the go-cid package allows version 0 here as well + if vers != 1 { + return nil, 0, fmt.Errorf("invalid cid version number") + } + + codec, err := binary.ReadUvarint(br) + if err != nil { + return nil, 0, err + } + + mhr := mh.NewReader(br) + h, err := mhr.ReadMultihash() + if err != nil { + return nil, 0, err + } + + return cid.NewCidV1(codec, h), len(buf) - br.Len(), nil +} + +func ReadNode(br *bufio.Reader) (*cid.Cid, []byte, error) { + data, err := LdRead(br) + if err != nil { + return nil, nil, err + } + + c, n, err := ReadCid(data) + if err != nil { + return nil, nil, err + } + + return c, data[n:], nil +} + +func LdWrite(w io.Writer, d ...[]byte) error { + var sum uint64 + for _, s := range d { + sum += uint64(len(s)) + } + + buf := make([]byte, 8) + n := binary.PutUvarint(buf, sum) + _, err := w.Write(buf[:n]) + if err != nil { + return err + } + + for _, s := range d { + _, err = w.Write(s) + if err != nil { + return err + } + } + + return nil +} + +func LdRead(r *bufio.Reader) ([]byte, error) { + l, err := binary.ReadUvarint(r) + if err != nil { + return nil, err + } + + buf := make([]byte, l) + if _, err := io.ReadFull(r, buf); err != nil { + return nil, err + } + + return buf, nil +} From a3a153ab6a86b463168faf8818082c81a8f9132a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 13 Aug 2018 14:05:49 -0700 Subject: [PATCH 2538/3817] use the new datastore interface This commit was moved from ipfs/go-ipfs-routing@6488c6d5d70030b7baee95428efd155d896b1229 --- routing/offline/offline.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 1be28bcf4..ebc96ef20 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -71,17 +71,13 @@ func (c *offlineRouting) PutValue(ctx context.Context, key string, val []byte, _ } func (c *offlineRouting) GetValue(ctx context.Context, key string, _ ...ropts.Option) ([]byte, error) { - v, err := c.datastore.Get(dshelp.NewKeyFromBinary([]byte(key))) + buf, err := c.datastore.Get(dshelp.NewKeyFromBinary([]byte(key))) if err != nil { return nil, err } - byt, ok := v.([]byte) - if !ok { - return nil, errors.New("value stored in datastore not []byte") - } rec := new(pb.Record) - err = proto.Unmarshal(byt, rec) + err = proto.Unmarshal(buf, rec) if err != nil { return nil, err } From 3eb2bddf2378fdcae037fea39276596780e1f5a7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 13 Aug 2018 14:10:21 -0700 Subject: [PATCH 2539/3817] update to the new datastore interface This commit was moved from ipfs/go-ipfs-blockstore@bae26a9594c68b7c7de558fe0ac51e51b9ed8c6d --- blockstore/blockstore.go | 17 ++--------------- blockstore/blockstore_test.go | 20 ++------------------ blockstore/bloom_cache_test.go | 4 ++-- 3 files changed, 6 insertions(+), 35 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index c4475da9a..f5cbc4c0f 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -22,10 +22,6 @@ var log = logging.Logger("blockstore") // BlockPrefix namespaces blockstore datastores var BlockPrefix = ds.NewKey("blocks") -// ErrValueTypeMismatch is an error returned when the item retrieved from -// the datatstore is not a block. -var ErrValueTypeMismatch = errors.New("the retrieved value is not a Block") - // ErrHashMismatch is an error returned when the hash of a block // is different than expected. var ErrHashMismatch = errors.New("block in storage has different hash than requested") @@ -124,18 +120,13 @@ func (bs *blockstore) Get(k *cid.Cid) (blocks.Block, error) { return nil, ErrNotFound } - maybeData, err := bs.datastore.Get(dshelp.CidToDsKey(k)) + bdata, err := bs.datastore.Get(dshelp.CidToDsKey(k)) if err == ds.ErrNotFound { return nil, ErrNotFound } if err != nil { return nil, err } - bdata, ok := maybeData.([]byte) - if !ok { - return nil, ErrValueTypeMismatch - } - if bs.rehash { rbcid, err := k.Prefix().Sum(bdata) if err != nil { @@ -187,17 +178,13 @@ func (bs *blockstore) Has(k *cid.Cid) (bool, error) { } func (bs *blockstore) GetSize(k *cid.Cid) (int, error) { - maybeData, err := bs.datastore.Get(dshelp.CidToDsKey(k)) + bdata, err := bs.datastore.Get(dshelp.CidToDsKey(k)) if err == ds.ErrNotFound { return -1, ErrNotFound } if err != nil { return -1, err } - bdata, ok := maybeData.([]byte) - if !ok { - return -1, ErrValueTypeMismatch - } return len(bdata), nil } diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 2fc1c9452..ae71b541a 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -11,7 +11,6 @@ import ( ds "github.com/ipfs/go-datastore" dsq "github.com/ipfs/go-datastore/query" ds_sync "github.com/ipfs/go-datastore/sync" - dshelp "github.com/ipfs/go-ipfs-ds-help" u "github.com/ipfs/go-ipfs-util" ) @@ -218,21 +217,6 @@ func TestAllKeysRespectsContext(t *testing.T) { } -func TestErrValueTypeMismatch(t *testing.T) { - block := blocks.NewBlock([]byte("some data")) - - datastore := ds.NewMapDatastore() - k := BlockPrefix.Child(dshelp.CidToDsKey(block.Cid())) - datastore.Put(k, "data that isn't a block!") - - blockstore := NewBlockstore(ds_sync.MutexWrap(datastore)) - - _, err := blockstore.Get(block.Cid()) - if err != ErrValueTypeMismatch { - t.Fatal(err) - } -} - func expectMatches(t *testing.T, expect, actual []*cid.Cid) { if len(expect) != len(actual) { @@ -258,11 +242,11 @@ type queryTestDS struct { func (c *queryTestDS) SetFunc(f func(dsq.Query) (dsq.Results, error)) { c.cb = f } -func (c *queryTestDS) Put(key ds.Key, value interface{}) (err error) { +func (c *queryTestDS) Put(key ds.Key, value []byte) (err error) { return c.ds.Put(key, value) } -func (c *queryTestDS) Get(key ds.Key) (value interface{}, err error) { +func (c *queryTestDS) Get(key ds.Key) (value []byte, err error) { return c.ds.Get(key) } diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 5fc831ec6..514ae82cd 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -170,12 +170,12 @@ func (c *callbackDatastore) CallF() { c.f() } -func (c *callbackDatastore) Put(key ds.Key, value interface{}) (err error) { +func (c *callbackDatastore) Put(key ds.Key, value []byte) (err error) { c.CallF() return c.ds.Put(key, value) } -func (c *callbackDatastore) Get(key ds.Key) (value interface{}, err error) { +func (c *callbackDatastore) Get(key ds.Key) (value []byte, err error) { c.CallF() return c.ds.Get(key) } From fba432804354fed60017ac0916f06eb7e3a96250 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 13 Aug 2018 14:29:22 -0700 Subject: [PATCH 2540/3817] update go-datastore to use []byte values instead of {}interface values * Most of our datastores barf on non []byte values. * We have to have a bunch of "is this a []byte" checks. * Saves some allocations. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@737db43c5002726c676dc8a0285fb6db74a222a9 --- pinning/pinner/gc/gc.go | 10 +++++----- pinning/pinner/pin.go | 13 ++++--------- pinning/pinner/pin_test.go | 12 ++++++------ pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 12 ++++++------ 5 files changed, 22 insertions(+), 27 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index ba031f90b..03e156e03 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,16 +8,16 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmSQDddUJRZCBEcEKp3icdiRUrVofvMSWmGyMXSysqjNCL/go-blockservice" - dag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" + bserv "gx/ipfs/QmQrLuAVriwcPQpqn15GU7gjZGKKa45Hmdj9JCG3Cc45CC/go-blockservice" + dag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" - offline "gx/ipfs/QmNxamk8jB6saNF5G37Tiipf2JEnxt7qvesbngPMe24wMS/go-ipfs-exchange-offline" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" + offline "gx/ipfs/QmTMhpt5sH5yup2RU5qeFDMZcDf4RaHqba4ztEp7yrzese/go-ipfs-exchange-offline" ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" "gx/ipfs/QmV1pEFHk8ijeessqG52SjHuxuehahbeHrxXk4QEkgfPHj/go-verifcid" - bstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" + dstore "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - dstore "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + bstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 28401ea8d..d2dea5767 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,12 +10,12 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" + mdag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" + ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) var log = logging.Logger("pin") @@ -440,16 +440,11 @@ func cidSetWithValues(cids []*cid.Cid) *cid.Set { func LoadPinner(d ds.Datastore, dserv, internal ipld.DAGService) (Pinner, error) { p := new(pinner) - rootKeyI, err := d.Get(pinDatastoreKey) + rootKey, err := d.Get(pinDatastoreKey) if err != nil { return nil, fmt.Errorf("cannot load pin state: %v", err) } - rootKeyBytes, ok := rootKeyI.([]byte) - if !ok { - return nil, fmt.Errorf("cannot load pin state: %s was not bytes", pinDatastoreKey) - } - - rootCid, err := cid.Cast(rootKeyBytes) + rootCid, err := cid.Cast(rootKey) if err != nil { return nil, err } diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 96110530e..7d3a87e3e 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" - bs "gx/ipfs/QmSQDddUJRZCBEcEKp3icdiRUrVofvMSWmGyMXSysqjNCL/go-blockservice" - mdag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" + bs "gx/ipfs/QmQrLuAVriwcPQpqn15GU7gjZGKKa45Hmdj9JCG3Cc45CC/go-blockservice" + mdag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" - offline "gx/ipfs/QmNxamk8jB6saNF5G37Tiipf2JEnxt7qvesbngPMe24wMS/go-ipfs-exchange-offline" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - blockstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" + offline "gx/ipfs/QmTMhpt5sH5yup2RU5qeFDMZcDf4RaHqba4ztEp7yrzese/go-ipfs-exchange-offline" + ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" + dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" + blockstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" ) var rand = util.NewTimeSeededRand() diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 6a293d60b..83c940fc2 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" + "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 8911f9f3c..6b80d2d7d 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,14 +5,14 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmSQDddUJRZCBEcEKp3icdiRUrVofvMSWmGyMXSysqjNCL/go-blockservice" - dag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" + bserv "gx/ipfs/QmQrLuAVriwcPQpqn15GU7gjZGKKa45Hmdj9JCG3Cc45CC/go-blockservice" + dag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" - offline "gx/ipfs/QmNxamk8jB6saNF5G37Tiipf2JEnxt7qvesbngPMe24wMS/go-ipfs-exchange-offline" - blockstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" + offline "gx/ipfs/QmTMhpt5sH5yup2RU5qeFDMZcDf4RaHqba4ztEp7yrzese/go-ipfs-exchange-offline" + ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" + dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" + blockstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" ) func ignoreCids(_ *cid.Cid) {} From 7c98289de98b37e52521cff6bbc238eb5a0e0d67 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 13 Aug 2018 14:29:22 -0700 Subject: [PATCH 2541/3817] update go-datastore to use []byte values instead of {}interface values * Most of our datastores barf on non []byte values. * We have to have a bunch of "is this a []byte" checks. * Saves some allocations. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@e65a11245b87a543faa4ef9eff9ac9e556e93d28 --- filestore/filestore.go | 4 ++-- filestore/filestore_test.go | 6 +++--- filestore/fsrefstore.go | 19 +++++++------------ filestore/util.go | 8 ++++---- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index a571e7eec..f48fa77dd 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -12,11 +12,11 @@ import ( "errors" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" + dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" posinfo "gx/ipfs/QmWjvVuzD3Dqf3VznGAXUs1VqpG4dd35ounVCnLam8v7Xt/go-ipfs-posinfo" - blockstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" blocks "gx/ipfs/QmZXvzTJTiN6p469osBUtEwm4WwhXXoWcHC8aTS1cAJkjy/go-block-format" cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" + blockstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 5df2496ec..76f1dc359 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,12 +7,12 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" + dag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" + ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" posinfo "gx/ipfs/QmWjvVuzD3Dqf3VznGAXUs1VqpG4dd35ounVCnLam8v7Xt/go-ipfs-posinfo" - blockstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" + blockstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 5866d8286..658ec0cee 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,15 +10,15 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - dshelp "gx/ipfs/QmWf5idGZ55KWUUo2EKM3BTeunjSiSgWbo4bLpYafENBgp/go-ipfs-ds-help" + ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" + dsns "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/namespace" + dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" posinfo "gx/ipfs/QmWjvVuzD3Dqf3VznGAXUs1VqpG4dd35ounVCnLam8v7Xt/go-ipfs-posinfo" - blockstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" proto "gx/ipfs/QmZHU2gx42NPTYXzw6pJkuX6xCE7bKECp6e8QcPdoLx8sx/protobuf/proto" blocks "gx/ipfs/QmZXvzTJTiN6p469osBUtEwm4WwhXXoWcHC8aTS1cAJkjy/go-block-format" cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - dsns "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/namespace" - dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" + blockstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" + dshelp "gx/ipfs/Qmeuoyt6qXqqaPeuZJwExzQzFTLC6epTAz3wFUK6jGbJjP/go-ipfs-ds-help" ) // FilestorePrefix identifies the key prefix for FileManager blocks. @@ -155,12 +155,7 @@ func (f *FileManager) getDataObj(c *cid.Cid) (*pb.DataObj, error) { return unmarshalDataObj(o) } -func unmarshalDataObj(o interface{}) (*pb.DataObj, error) { - data, ok := o.([]byte) - if !ok { - return nil, fmt.Errorf("stored filestore dataobj was not a []byte") - } - +func unmarshalDataObj(data []byte) (*pb.DataObj, error) { var dobj pb.DataObj if err := proto.Unmarshal(data, &dobj); err != nil { return nil, err @@ -265,7 +260,7 @@ func (f *FileManager) Has(c *cid.Cid) (bool, error) { } type putter interface { - Put(ds.Key, interface{}) error + Put(ds.Key, []byte) error } // Put adds a new reference block to the FileManager. It does not check diff --git a/filestore/util.go b/filestore/util.go index 2529a6c3d..051cdf568 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,11 +6,11 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - dshelp "gx/ipfs/QmWf5idGZ55KWUUo2EKM3BTeunjSiSgWbo4bLpYafENBgp/go-ipfs-ds-help" - blockstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" + ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" + dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - dsq "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" + blockstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" + dshelp "gx/ipfs/Qmeuoyt6qXqqaPeuZJwExzQzFTLC6epTAz3wFUK6jGbJjP/go-ipfs-ds-help" ) // Status is used to identify the state of the block data referenced From 88487701db3d228e830b0b673967f411c55b01eb Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 13 Aug 2018 14:29:22 -0700 Subject: [PATCH 2542/3817] update go-datastore to use []byte values instead of {}interface values * Most of our datastores barf on non []byte values. * We have to have a bunch of "is this a []byte" checks. * Saves some allocations. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@d7f784454a891c5347e5f7c0bf311e049d896798 --- mfs/dir.go | 8 ++++---- mfs/fd.go | 2 +- mfs/file.go | 6 +++--- mfs/mfs_test.go | 22 +++++++++++----------- mfs/ops.go | 2 +- mfs/system.go | 4 ++-- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index ea5e9c847..0b304f8cb 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -9,10 +9,10 @@ import ( "sync" "time" - ft "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs" - uio "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs/io" - ufspb "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs/pb" - dag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" + dag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" + ft "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs" + uio "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs/io" + ufspb "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs/pb" ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" diff --git a/mfs/fd.go b/mfs/fd.go index 114314adc..cfb233ed6 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -4,7 +4,7 @@ import ( "fmt" "io" - mod "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs/mod" + mod "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs/mod" context "context" ) diff --git a/mfs/file.go b/mfs/file.go index f680a58c1..e851f7c6c 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -5,9 +5,9 @@ import ( "fmt" "sync" - ft "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs" - mod "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs/mod" - dag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" + dag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" + ft "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs" + mod "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs/mod" ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" chunker "gx/ipfs/Qme4ThG6LN6EMrMYyf2AMywAZaGbTYxQu4njfcSSkcisLi/go-ipfs-chunker" diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 3f94577c8..1cd1e1da1 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,21 +14,21 @@ import ( "testing" "time" - bserv "gx/ipfs/QmSQDddUJRZCBEcEKp3icdiRUrVofvMSWmGyMXSysqjNCL/go-blockservice" - "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" - ft "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs" - importer "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs/importer" - uio "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs/io" - dag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" - - offline "gx/ipfs/QmNxamk8jB6saNF5G37Tiipf2JEnxt7qvesbngPMe24wMS/go-ipfs-exchange-offline" + bserv "gx/ipfs/QmQrLuAVriwcPQpqn15GU7gjZGKKa45Hmdj9JCG3Cc45CC/go-blockservice" + "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + dag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" + ft "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs" + importer "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs/importer" + uio "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs/io" + u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + offline "gx/ipfs/QmTMhpt5sH5yup2RU5qeFDMZcDf4RaHqba4ztEp7yrzese/go-ipfs-exchange-offline" ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" - bstore "gx/ipfs/QmYir8arYJK1RMzDBZU1dqscLorWvthWU8aStL4xhxdYeT/go-ipfs-blockstore" + ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" + dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" chunker "gx/ipfs/Qme4ThG6LN6EMrMYyf2AMywAZaGbTYxQu4njfcSSkcisLi/go-ipfs-chunker" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" + bstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" ) func emptyDirNode() *dag.ProtoNode { diff --git a/mfs/ops.go b/mfs/ops.go index 8b14abc8d..dbb8b1e44 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -6,7 +6,7 @@ import ( gopath "path" "strings" - path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" diff --git a/mfs/system.go b/mfs/system.go index c8792a3be..69a863b69 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -16,8 +16,8 @@ import ( "sync" "time" - ft "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs" - dag "gx/ipfs/QmXhrNaxjxNLwAHnWQScc6GxvpJMyn8wfdRmGDbUQwpfth/go-merkledag" + dag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" + ft "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" From cf393a59a74c806621cc10ccef216eb1e22c3d74 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 13 Aug 2018 14:29:22 -0700 Subject: [PATCH 2543/3817] update go-datastore to use []byte values instead of {}interface values * Most of our datastores barf on non []byte values. * We have to have a bunch of "is this a []byte" checks. * Saves some allocations. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@754c3c5432a4ae8dfaf59410bc01dcdd947ab7f2 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 10 +++++----- namesys/namesys.go | 4 ++-- namesys/namesys_test.go | 10 +++++----- namesys/proquint.go | 2 +- namesys/publisher.go | 24 ++++++------------------ namesys/publisher_test.go | 8 ++++---- namesys/republisher/repub.go | 8 +++----- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 8 ++++---- namesys/routing.go | 4 ++-- 14 files changed, 37 insertions(+), 51 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 46f2953c3..ebab2ecb1 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index f424de7f8..286801f76 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index fa9ad29b0..7cf0ecbdc 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index 0d771e708..e81e51621 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index ca280ac56..127e76fc3 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,19 +6,19 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" record "gx/ipfs/QmUTQSGgjs8CHm9yBcUHicpRs7C9abhyZiBwjzCUp1pNgX/go-libp2p-record" + ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" + dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" testutil "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil" - mockrouting "gx/ipfs/QmYAC1wnzfs7LwgrNU5K5MK2rnisjF7kAxWrwzZTLNNjVe/go-ipfs-routing/mock" - offline "gx/ipfs/QmYAC1wnzfs7LwgrNU5K5MK2rnisjF7kAxWrwzZTLNNjVe/go-ipfs-routing/offline" pstore "gx/ipfs/QmYLXCWN2myozZpx8Wx4UjrRuQuhY3YtWoMi6SHaXii6aM/go-libp2p-peerstore" + mockrouting "gx/ipfs/QmYey7kzAAqmXXbr38qH4oGGkB5m5swJeJCQfHgt8nrDES/go-ipfs-routing/mock" + offline "gx/ipfs/QmYey7kzAAqmXXbr38qH4oGGkB5m5swJeJCQfHgt8nrDES/go-ipfs-routing/offline" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" routing "gx/ipfs/QmfGeECX7CxJAFXwGKx2cn7csnxAtwJw8e3XtoNVf6bqcv/go-libp2p-routing" ropts "gx/ipfs/QmfGeECX7CxJAFXwGKx2cn7csnxAtwJw8e3XtoNVf6bqcv/go-libp2p-routing/options" ) diff --git a/namesys/namesys.go b/namesys/namesys.go index b52390836..9d267e6c0 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,14 +6,14 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" routing "gx/ipfs/QmfGeECX7CxJAFXwGKx2cn7csnxAtwJw8e3XtoNVf6bqcv/go-libp2p-routing" ) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 9e6e7fd0f..7eff60a50 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,16 +7,16 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" - "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs" + path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" + dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" - offroute "gx/ipfs/QmYAC1wnzfs7LwgrNU5K5MK2rnisjF7kAxWrwzZTLNNjVe/go-ipfs-routing/offline" pstore "gx/ipfs/QmYLXCWN2myozZpx8Wx4UjrRuQuhY3YtWoMi6SHaXii6aM/go-libp2p-peerstore" + offroute "gx/ipfs/QmYey7kzAAqmXXbr38qH4oGGkB5m5swJeJCQfHgt8nrDES/go-ipfs-routing/offline" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index d0a488636..7ab74be35 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 04d57c19c..0299614b6 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -2,22 +2,21 @@ package namesys import ( "context" - "fmt" "strings" "sync" "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" - ft "gx/ipfs/QmTbas51oodp3ZJrqsWYs1yqSxcD7LEJBv4djRV2VrY8wv/go-unixfs" + path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + ft "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" + dsquery "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" pb "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns/pb" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - dsquery "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query" routing "gx/ipfs/QmfGeECX7CxJAFXwGKx2cn7csnxAtwJw8e3XtoNVf6bqcv/go-libp2p-routing" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) @@ -80,13 +79,8 @@ func (p *IpnsPublisher) ListPublished(ctx context.Context) (map[peer.ID]*pb.Ipns if result.Error != nil { return nil, result.Error } - value, ok := result.Value.([]byte) - if !ok { - log.Error("found ipns record that we couldn't convert to a value") - continue - } e := new(pb.IpnsEntry) - if err := proto.Unmarshal(value, e); err != nil { + if err := proto.Unmarshal(result.Value, e); err != nil { // Might as well return what we can. log.Error("found an invalid IPNS entry:", err) continue @@ -117,15 +111,9 @@ func (p *IpnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouti ctx, cancel := context.WithTimeout(ctx, time.Second*30) defer cancel() - dsVal, err := p.ds.Get(IpnsDsKey(id)) - var value []byte + value, err := p.ds.Get(IpnsDsKey(id)) switch err { case nil: - var ok bool - value, ok = dsVal.([]byte) - if !ok { - return nil, fmt.Errorf("found ipns record that we couldn't convert to a value") - } case ds.ErrNotFound: if !checkRouting { return nil, nil diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index e40d9be5e..2e6b9b448 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -7,14 +7,14 @@ import ( "time" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" + dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" - dshelp "gx/ipfs/QmWf5idGZ55KWUUo2EKM3BTeunjSiSgWbo4bLpYafENBgp/go-ipfs-ds-help" testutil "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil" - mockrouting "gx/ipfs/QmYAC1wnzfs7LwgrNU5K5MK2rnisjF7kAxWrwzZTLNNjVe/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmYey7kzAAqmXXbr38qH4oGGkB5m5swJeJCQfHgt8nrDES/go-ipfs-routing/mock" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" + dshelp "gx/ipfs/Qmeuoyt6qXqqaPeuZJwExzQzFTLC6epTAz3wFUK6jGbJjP/go-ipfs-ds-help" ) type identity struct { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 1b221df11..b9851875b 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,16 +7,16 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" + ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" pb "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns/pb" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" ) var errNoEntry = errors.New("no previous entry") @@ -141,7 +141,7 @@ func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) erro func (rp *Republisher) getLastVal(id peer.ID) (path.Path, error) { // Look for it locally only - vali, err := rp.ds.Get(namesys.IpnsDsKey(id)) + val, err := rp.ds.Get(namesys.IpnsDsKey(id)) switch err { case nil: case ds.ErrNotFound: @@ -150,8 +150,6 @@ func (rp *Republisher) getLastVal(id peer.ID) (path.Path, error) { return "", err } - val := vali.([]byte) - e := new(pb.IpnsEntry) if err := proto.Unmarshal(val, e); err != nil { return "", err diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 82b60b36a..1425f8848 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" mocknet "gx/ipfs/QmUDzeFgYrRmHL2hUB6NZmqcBVQtUzETwmFRUc9onfSSHr/go-libp2p/p2p/net/mock" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 906ecac04..26aa29786 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,14 +6,14 @@ import ( "testing" "time" - path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" + dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" testutil "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil" - mockrouting "gx/ipfs/QmYAC1wnzfs7LwgrNU5K5MK2rnisjF7kAxWrwzZTLNNjVe/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmYey7kzAAqmXXbr38qH4oGGkB5m5swJeJCQfHgt8nrDES/go-ipfs-routing/mock" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" - ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore" - dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index 5af625b8e..ea6cb16f8 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,11 +6,11 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - dht "gx/ipfs/QmSRMxYCadAPQrCT38qFttGvE77bXYZfkQK7vLAgzj8r9K/go-libp2p-kad-dht" + dht "gx/ipfs/QmRcGfNWgt1tEbRosuQZ5DXkgG4UPSZGuE5ZiohEW47TFU/go-libp2p-kad-dht" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" pb "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns/pb" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" From 454f0f425c406d96427b7e0a262a33081c308dbf Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 13 Aug 2018 14:29:22 -0700 Subject: [PATCH 2544/3817] update go-datastore to use []byte values instead of {}interface values * Most of our datastores barf on non []byte values. * We have to have a bunch of "is this a []byte" checks. * Saves some allocations. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@bb2d55c26ae125a50a6250df13479d70a84e4073 --- coreiface/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/path.go b/coreiface/path.go index 28e0f431c..d2933ece5 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmTG5WFmAM4uAnqGskeAPijdpTmmNDLJNCQ71NqfdvC6hV/go-path" + ipfspath "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" ) From 0b65bd825890b06ebb4693026ead177a00b2b15d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 15 Aug 2018 08:30:22 -0700 Subject: [PATCH 2545/3817] gx: update go-cid License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@b5d2df24d80a657b3db1ef8528c41cc03815ee6e --- pinning/pinner/gc/gc.go | 14 +++++++------- pinning/pinner/pin.go | 6 +++--- pinning/pinner/pin_test.go | 10 +++++----- pinning/pinner/set.go | 6 +++--- pinning/pinner/set_test.go | 10 +++++----- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 03e156e03..456aca9ba 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,16 +8,16 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmQrLuAVriwcPQpqn15GU7gjZGKKa45Hmdj9JCG3Cc45CC/go-blockservice" - dag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" + dag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" + bserv "gx/ipfs/QmTZZrpd9o4vpYr9TEADW2EoJ9fzUtAgpXqjxZHbKR2T15/go-blockservice" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - offline "gx/ipfs/QmTMhpt5sH5yup2RU5qeFDMZcDf4RaHqba4ztEp7yrzese/go-ipfs-exchange-offline" - ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" - "gx/ipfs/QmV1pEFHk8ijeessqG52SjHuxuehahbeHrxXk4QEkgfPHj/go-verifcid" dstore "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - bstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" + offline "gx/ipfs/QmVozMmsgK2PYyaHQsrcWLBYigb1m6mW8YhCBG2Cb4Uxq9/go-ipfs-exchange-offline" + bstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" + "gx/ipfs/QmfMirfpEKQFctVpBYTvETxxLoU5q4ZJWsAMrtwSSE2bkn/go-verifcid" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index d2dea5767..effb0f10f 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,12 +10,12 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" + mdag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 7d3a87e3e..a715b79f8 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" - bs "gx/ipfs/QmQrLuAVriwcPQpqn15GU7gjZGKKa45Hmdj9JCG3Cc45CC/go-blockservice" - mdag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" + mdag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" + bs "gx/ipfs/QmTZZrpd9o4vpYr9TEADW2EoJ9fzUtAgpXqjxZHbKR2T15/go-blockservice" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmTMhpt5sH5yup2RU5qeFDMZcDf4RaHqba4ztEp7yrzese/go-ipfs-exchange-offline" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - blockstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" + offline "gx/ipfs/QmVozMmsgK2PYyaHQsrcWLBYigb1m6mW8YhCBG2Cb4Uxq9/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" ) var rand = util.NewTimeSeededRand() diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 83c940fc2..19accabe6 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,10 +10,10 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" + "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" - ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 6b80d2d7d..62a8a9a25 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,14 +5,14 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmQrLuAVriwcPQpqn15GU7gjZGKKa45Hmdj9JCG3Cc45CC/go-blockservice" - dag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" + dag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" + bserv "gx/ipfs/QmTZZrpd9o4vpYr9TEADW2EoJ9fzUtAgpXqjxZHbKR2T15/go-blockservice" - offline "gx/ipfs/QmTMhpt5sH5yup2RU5qeFDMZcDf4RaHqba4ztEp7yrzese/go-ipfs-exchange-offline" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - blockstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" + offline "gx/ipfs/QmVozMmsgK2PYyaHQsrcWLBYigb1m6mW8YhCBG2Cb4Uxq9/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" ) func ignoreCids(_ *cid.Cid) {} From a99b0fd37147e4b3659ac696771af58a5d415550 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 15 Aug 2018 08:30:22 -0700 Subject: [PATCH 2546/3817] gx: update go-cid License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@788f9e916ad482e47405641dcd934c18d934e411 --- filestore/filestore.go | 8 ++++---- filestore/filestore_test.go | 8 ++++---- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index f48fa77dd..4a1f66df6 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,12 +11,12 @@ import ( "context" "errors" + blocks "gx/ipfs/QmR54CzE4UcdFAZDehj6HFyy3eSHhVsJUpjfnhCmscuStS/go-block-format" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" - posinfo "gx/ipfs/QmWjvVuzD3Dqf3VznGAXUs1VqpG4dd35ounVCnLam8v7Xt/go-ipfs-posinfo" - blocks "gx/ipfs/QmZXvzTJTiN6p469osBUtEwm4WwhXXoWcHC8aTS1cAJkjy/go-block-format" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - blockstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" + blockstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + posinfo "gx/ipfs/QmdBpJ5VTfL79VwKDU93z7fyZJ3mm4UaBHrE73CWRw2Bjd/go-ipfs-posinfo" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 76f1dc359..bb7278620 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,12 +7,12 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" + dag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - posinfo "gx/ipfs/QmWjvVuzD3Dqf3VznGAXUs1VqpG4dd35ounVCnLam8v7Xt/go-ipfs-posinfo" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - blockstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" + blockstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + posinfo "gx/ipfs/QmdBpJ5VTfL79VwKDU93z7fyZJ3mm4UaBHrE73CWRw2Bjd/go-ipfs-posinfo" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 658ec0cee..eb9c7d7e1 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,15 +10,15 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" + blocks "gx/ipfs/QmR54CzE4UcdFAZDehj6HFyy3eSHhVsJUpjfnhCmscuStS/go-block-format" + dshelp "gx/ipfs/QmSLS8mMWsm54vdQuwgde9wBgLg5usVQY4i9r8kXhfje8g/go-ipfs-ds-help" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dsns "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/namespace" dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" - posinfo "gx/ipfs/QmWjvVuzD3Dqf3VznGAXUs1VqpG4dd35ounVCnLam8v7Xt/go-ipfs-posinfo" + blockstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" proto "gx/ipfs/QmZHU2gx42NPTYXzw6pJkuX6xCE7bKECp6e8QcPdoLx8sx/protobuf/proto" - blocks "gx/ipfs/QmZXvzTJTiN6p469osBUtEwm4WwhXXoWcHC8aTS1cAJkjy/go-block-format" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - blockstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" - dshelp "gx/ipfs/Qmeuoyt6qXqqaPeuZJwExzQzFTLC6epTAz3wFUK6jGbJjP/go-ipfs-ds-help" + posinfo "gx/ipfs/QmdBpJ5VTfL79VwKDU93z7fyZJ3mm4UaBHrE73CWRw2Bjd/go-ipfs-posinfo" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index 051cdf568..0cdbad8de 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,11 +6,11 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" + dshelp "gx/ipfs/QmSLS8mMWsm54vdQuwgde9wBgLg5usVQY4i9r8kXhfje8g/go-ipfs-ds-help" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - blockstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" - dshelp "gx/ipfs/Qmeuoyt6qXqqaPeuZJwExzQzFTLC6epTAz3wFUK6jGbJjP/go-ipfs-ds-help" + blockstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" ) // Status is used to identify the state of the block data referenced From cc621da776c69db472ebc1534b6741865d438e5c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 15 Aug 2018 08:30:22 -0700 Subject: [PATCH 2547/3817] gx: update go-cid License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-mfs@8de082c4fa06c42025d0b0b10f7f21bb35a4f25d --- mfs/dir.go | 12 ++++++------ mfs/fd.go | 2 +- mfs/file.go | 10 +++++----- mfs/mfs_test.go | 22 +++++++++++----------- mfs/ops.go | 6 +++--- mfs/repub_test.go | 2 +- mfs/system.go | 8 ++++---- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 0b304f8cb..50d3747c7 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -9,13 +9,13 @@ import ( "sync" "time" - dag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" - ft "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs" - uio "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs/io" - ufspb "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs/pb" + dag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" + ft "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs" + uio "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs/io" + ufspb "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs/pb" - ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" ) var ErrNotYetImplemented = errors.New("not yet implemented") diff --git a/mfs/fd.go b/mfs/fd.go index cfb233ed6..8b84000fd 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -4,7 +4,7 @@ import ( "fmt" "io" - mod "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs/mod" + mod "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs/mod" context "context" ) diff --git a/mfs/file.go b/mfs/file.go index e851f7c6c..fec9c2125 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -5,12 +5,12 @@ import ( "fmt" "sync" - dag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" - ft "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs" - mod "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs/mod" + dag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" + ft "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs" + mod "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs/mod" - ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" - chunker "gx/ipfs/Qme4ThG6LN6EMrMYyf2AMywAZaGbTYxQu4njfcSSkcisLi/go-ipfs-chunker" + chunker "gx/ipfs/QmWbCAB5f3LDumj4ncz1UCHSiyXrXxkMxZB6Wv35xi4P8z/go-ipfs-chunker" + ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 1cd1e1da1..524732f2c 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,21 +14,21 @@ import ( "testing" "time" - bserv "gx/ipfs/QmQrLuAVriwcPQpqn15GU7gjZGKKa45Hmdj9JCG3Cc45CC/go-blockservice" - "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" - dag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" - ft "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs" - importer "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs/importer" - uio "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs/io" + dag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" + bserv "gx/ipfs/QmTZZrpd9o4vpYr9TEADW2EoJ9fzUtAgpXqjxZHbKR2T15/go-blockservice" + "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" + ft "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs" + importer "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs/importer" + uio "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs/io" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmTMhpt5sH5yup2RU5qeFDMZcDf4RaHqba4ztEp7yrzese/go-ipfs-exchange-offline" - ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" - chunker "gx/ipfs/Qme4ThG6LN6EMrMYyf2AMywAZaGbTYxQu4njfcSSkcisLi/go-ipfs-chunker" - bstore "gx/ipfs/QmeFZ47hGe5T8nSUjwd6zf6ikzFWYEzWsb1e4Q2r6n1w9z/go-ipfs-blockstore" + offline "gx/ipfs/QmVozMmsgK2PYyaHQsrcWLBYigb1m6mW8YhCBG2Cb4Uxq9/go-ipfs-exchange-offline" + chunker "gx/ipfs/QmWbCAB5f3LDumj4ncz1UCHSiyXrXxkMxZB6Wv35xi4P8z/go-ipfs-chunker" + bstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" ) func emptyDirNode() *dag.ProtoNode { diff --git a/mfs/ops.go b/mfs/ops.go index dbb8b1e44..c90071fb8 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -6,10 +6,10 @@ import ( gopath "path" "strings" - path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" - ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" ) // Mv moves the file or directory at 'src' to 'dst' diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 106de1b2f..123c20859 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -6,7 +6,7 @@ import ( "time" ci "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil/ci" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index 69a863b69..2e7c400c9 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -16,12 +16,12 @@ import ( "sync" "time" - dag "gx/ipfs/QmYxX4VfVcxmfsj8U6T5kVtFvHsSidy9tmPyPTW5fy7H3q/go-merkledag" - ft "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs" + dag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" + ft "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" ) var ErrNotExist = errors.New("no such rootfs") From 1306564b6d6dafdc38235c925b3f0261ce77eb81 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 15 Aug 2018 08:30:22 -0700 Subject: [PATCH 2548/3817] gx: update go-cid License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@fb68517642db3052d32d30b3f5f784a6d2a5c77e --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 10 +++++----- namesys/namesys.go | 4 ++-- namesys/namesys_test.go | 6 +++--- namesys/proquint.go | 2 +- namesys/publisher.go | 6 +++--- namesys/publisher_test.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 4 ++-- namesys/routing.go | 8 ++++---- 14 files changed, 28 insertions(+), 28 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index ebab2ecb1..7ac2f9e06 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index 286801f76..2d2e829b9 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 7cf0ecbdc..5541f191c 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index e81e51621..48333e7f2 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 127e76fc3..187daeefb 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,21 +6,21 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + mockrouting "gx/ipfs/QmRr8DpNhQMzsoqAitUrw43D82pyPXZkyUqarhSAfkrdaQ/go-ipfs-routing/mock" + offline "gx/ipfs/QmRr8DpNhQMzsoqAitUrw43D82pyPXZkyUqarhSAfkrdaQ/go-ipfs-routing/offline" + routing "gx/ipfs/QmSD6bSPcXaaR7LpQHjytLWQD7DrCsb415CWfpbd9Szemb/go-libp2p-routing" + ropts "gx/ipfs/QmSD6bSPcXaaR7LpQHjytLWQD7DrCsb415CWfpbd9Szemb/go-libp2p-routing/options" record "gx/ipfs/QmUTQSGgjs8CHm9yBcUHicpRs7C9abhyZiBwjzCUp1pNgX/go-libp2p-record" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" testutil "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil" pstore "gx/ipfs/QmYLXCWN2myozZpx8Wx4UjrRuQuhY3YtWoMi6SHaXii6aM/go-libp2p-peerstore" - mockrouting "gx/ipfs/QmYey7kzAAqmXXbr38qH4oGGkB5m5swJeJCQfHgt8nrDES/go-ipfs-routing/mock" - offline "gx/ipfs/QmYey7kzAAqmXXbr38qH4oGGkB5m5swJeJCQfHgt8nrDES/go-ipfs-routing/offline" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" - routing "gx/ipfs/QmfGeECX7CxJAFXwGKx2cn7csnxAtwJw8e3XtoNVf6bqcv/go-libp2p-routing" - ropts "gx/ipfs/QmfGeECX7CxJAFXwGKx2cn7csnxAtwJw8e3XtoNVf6bqcv/go-libp2p-routing/options" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 9d267e6c0..d733b8db3 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,15 +6,15 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + routing "gx/ipfs/QmSD6bSPcXaaR7LpQHjytLWQD7DrCsb415CWfpbd9Szemb/go-libp2p-routing" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" - routing "gx/ipfs/QmfGeECX7CxJAFXwGKx2cn7csnxAtwJw8e3XtoNVf6bqcv/go-libp2p-routing" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 7eff60a50..25c2bc72b 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,15 +7,15 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" - "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs" + path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" + "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + offroute "gx/ipfs/QmRr8DpNhQMzsoqAitUrw43D82pyPXZkyUqarhSAfkrdaQ/go-ipfs-routing/offline" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" pstore "gx/ipfs/QmYLXCWN2myozZpx8Wx4UjrRuQuhY3YtWoMi6SHaXii6aM/go-libp2p-peerstore" - offroute "gx/ipfs/QmYey7kzAAqmXXbr38qH4oGGkB5m5swJeJCQfHgt8nrDES/go-ipfs-routing/offline" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" ) diff --git a/namesys/proquint.go b/namesys/proquint.go index 7ab74be35..eb8ae3621 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 0299614b6..4d3c7abd4 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,17 +7,17 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" - ft "gx/ipfs/QmagwbbPqiN1oa3SDMZvpTFE5tNuegF1ULtuJvA9EVzsJv/go-unixfs" + path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" + ft "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + routing "gx/ipfs/QmSD6bSPcXaaR7LpQHjytLWQD7DrCsb415CWfpbd9Szemb/go-libp2p-routing" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dsquery "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" pb "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns/pb" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" - routing "gx/ipfs/QmfGeECX7CxJAFXwGKx2cn7csnxAtwJw8e3XtoNVf6bqcv/go-libp2p-routing" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 2e6b9b448..98f17b74f 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -7,14 +7,14 @@ import ( "time" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + mockrouting "gx/ipfs/QmRr8DpNhQMzsoqAitUrw43D82pyPXZkyUqarhSAfkrdaQ/go-ipfs-routing/mock" + dshelp "gx/ipfs/QmSLS8mMWsm54vdQuwgde9wBgLg5usVQY4i9r8kXhfje8g/go-ipfs-ds-help" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" testutil "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil" - mockrouting "gx/ipfs/QmYey7kzAAqmXXbr38qH4oGGkB5m5swJeJCQfHgt8nrDES/go-ipfs-routing/mock" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" - dshelp "gx/ipfs/Qmeuoyt6qXqqaPeuZJwExzQzFTLC6epTAz3wFUK6jGbJjP/go-ipfs-ds-help" ) type identity struct { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index b9851875b..4adbcb308 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 1425f8848..1aa3d9f85 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" mocknet "gx/ipfs/QmUDzeFgYrRmHL2hUB6NZmqcBVQtUzETwmFRUc9onfSSHr/go-libp2p/p2p/net/mock" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 26aa29786..27e3b5fca 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,13 +6,13 @@ import ( "testing" "time" - path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" + mockrouting "gx/ipfs/QmRr8DpNhQMzsoqAitUrw43D82pyPXZkyUqarhSAfkrdaQ/go-ipfs-routing/mock" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" testutil "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil" - mockrouting "gx/ipfs/QmYey7kzAAqmXXbr38qH4oGGkB5m5swJeJCQfHgt8nrDES/go-ipfs-routing/mock" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" ) diff --git a/namesys/routing.go b/namesys/routing.go index ea6cb16f8..5bdd67efd 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,17 +6,17 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - dht "gx/ipfs/QmRcGfNWgt1tEbRosuQZ5DXkgG4UPSZGuE5ZiohEW47TFU/go-libp2p-kad-dht" + routing "gx/ipfs/QmSD6bSPcXaaR7LpQHjytLWQD7DrCsb415CWfpbd9Szemb/go-libp2p-routing" ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" pb "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns/pb" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" + dht "gx/ipfs/QmdP3wKxB6x6vJ57tDrewAJF2qv4ULejCZ6dspJRnk3993/go-libp2p-kad-dht" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" - routing "gx/ipfs/QmfGeECX7CxJAFXwGKx2cn7csnxAtwJw8e3XtoNVf6bqcv/go-libp2p-routing" ) var log = logging.Logger("namesys") From aac8a89a8ac8584416db46bc1968cd3e185241db Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 15 Aug 2018 08:30:22 -0700 Subject: [PATCH 2549/3817] gx: update go-cid License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@9d5772a1cf4ffcf6d9c7717e074d30540d36f263 --- coreiface/coreapi.go | 2 +- coreiface/dag.go | 2 +- coreiface/object.go | 4 ++-- coreiface/options/dag.go | 2 +- coreiface/path.go | 4 ++-- coreiface/unixfs.go | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index d7614f01d..ab5374069 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -5,7 +5,7 @@ package iface import ( "context" - ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" + ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" ) // CoreAPI defines an unified interface to IPFS for Go programs diff --git a/coreiface/dag.go b/coreiface/dag.go index 77577d0fc..d547e8531 100644 --- a/coreiface/dag.go +++ b/coreiface/dag.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" + ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" ) // DagOps groups operations that can be batched together diff --git a/coreiface/object.go b/coreiface/object.go index dc86f46c1..812b69a64 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -6,8 +6,8 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" ) // ObjectStat provides information about dag nodes diff --git a/coreiface/options/dag.go b/coreiface/options/dag.go index a43a144fc..e89e4d707 100644 --- a/coreiface/options/dag.go +++ b/coreiface/options/dag.go @@ -3,7 +3,7 @@ package options import ( "math" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" ) type DagPutSettings struct { diff --git a/coreiface/path.go b/coreiface/path.go index d2933ece5..49cea48cc 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,9 +1,9 @@ package iface import ( - ipfspath "gx/ipfs/QmV1W98rBAovVJGkeYHfqJ19JdT9dQbbWsCq9zPaMyrxYx/go-path" + ipfspath "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" - cid "gx/ipfs/Qmdu2AYUV7yMoVBQPxXNfe7FJcdx16kYtsx6jAPKWQYF1y/go-cid" + cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" ) //TODO: merge with ipfspath so we don't depend on it diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 0ec63d516..b42f56ba8 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -4,7 +4,7 @@ import ( "context" "io" - ipld "gx/ipfs/QmUSyMZ8Vt4vTZr5HdDEgEfpwAXfQRuDdfCFTt7XBzhxpQ/go-ipld-format" + ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" ) // UnixfsAPI is the basic interface to immutable files in IPFS From 55671e1d5ad94123f55d3bab19e1b3596f166878 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 8 Aug 2018 20:14:18 -0700 Subject: [PATCH 2550/3817] update protobuf files in go-ipfs Also: * Switch to gogo for filestore for consistency. * Use the "faster" codegen for fewer allocations. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@74d8b04cf8e1ccdcd9ba8949da5d58fd1b07e8cb --- pinning/pinner/internal/pb/header.pb.go | 356 ++++++++++++++++++++++-- pinning/pinner/set.go | 6 +- 2 files changed, 337 insertions(+), 25 deletions(-) diff --git a/pinning/pinner/internal/pb/header.pb.go b/pinning/pinner/internal/pb/header.pb.go index 6a620c9e7..ca4173c3e 100644 --- a/pinning/pinner/internal/pb/header.pb.go +++ b/pinning/pinner/internal/pb/header.pb.go @@ -1,59 +1,371 @@ -// Code generated by protoc-gen-gogo. -// source: header.proto -// DO NOT EDIT! +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: pin/internal/pb/header.proto -/* -Package pb is a generated protocol buffer package. - -It is generated from these files: - header.proto - -It has these top-level messages: - Set -*/ package pb import proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" +import fmt "fmt" import math "math" +import encoding_binary "encoding/binary" + +import io "io" + // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal +var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + type Set struct { // 1 for now, library will refuse to handle entries with an unrecognized version. - Version *uint32 `protobuf:"varint,1,opt,name=version" json:"version,omitempty"` + Version uint32 `protobuf:"varint,1,opt,name=version" json:"version"` // how many of the links are subtrees - Fanout *uint32 `protobuf:"varint,2,opt,name=fanout" json:"fanout,omitempty"` + Fanout uint32 `protobuf:"varint,2,opt,name=fanout" json:"fanout"` // hash seed for subtree selection, a random number - Seed *uint32 `protobuf:"fixed32,3,opt,name=seed" json:"seed,omitempty"` - XXX_unrecognized []byte `json:"-"` + Seed uint32 `protobuf:"fixed32,3,opt,name=seed" json:"seed"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Set) Reset() { *m = Set{} } func (m *Set) String() string { return proto.CompactTextString(m) } func (*Set) ProtoMessage() {} +func (*Set) Descriptor() ([]byte, []int) { + return fileDescriptor_header_778100e52d428560, []int{0} +} +func (m *Set) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Set) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Set.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalTo(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (dst *Set) XXX_Merge(src proto.Message) { + xxx_messageInfo_Set.Merge(dst, src) +} +func (m *Set) XXX_Size() int { + return m.Size() +} +func (m *Set) XXX_DiscardUnknown() { + xxx_messageInfo_Set.DiscardUnknown(m) +} + +var xxx_messageInfo_Set proto.InternalMessageInfo func (m *Set) GetVersion() uint32 { - if m != nil && m.Version != nil { - return *m.Version + if m != nil { + return m.Version } return 0 } func (m *Set) GetFanout() uint32 { - if m != nil && m.Fanout != nil { - return *m.Fanout + if m != nil { + return m.Fanout } return 0 } func (m *Set) GetSeed() uint32 { - if m != nil && m.Seed != nil { - return *m.Seed + if m != nil { + return m.Seed } return 0 } func init() { + proto.RegisterType((*Set)(nil), "ipfs.pin.Set") +} +func (m *Set) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Set) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + dAtA[i] = 0x8 + i++ + i = encodeVarintHeader(dAtA, i, uint64(m.Version)) + dAtA[i] = 0x10 + i++ + i = encodeVarintHeader(dAtA, i, uint64(m.Fanout)) + dAtA[i] = 0x1d + i++ + encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(m.Seed)) + i += 4 + return i, nil +} + +func encodeVarintHeader(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *Set) Size() (n int) { + var l int + _ = l + n += 1 + sovHeader(uint64(m.Version)) + n += 1 + sovHeader(uint64(m.Fanout)) + n += 5 + return n +} + +func sovHeader(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozHeader(x uint64) (n int) { + return sovHeader(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Set) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowHeader + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Set: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Set: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + m.Version = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowHeader + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Version |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Fanout", wireType) + } + m.Fanout = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowHeader + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Fanout |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 5 { + return fmt.Errorf("proto: wrong wireType = %d for field Seed", wireType) + } + m.Seed = 0 + if (iNdEx + 4) > l { + return io.ErrUnexpectedEOF + } + m.Seed = uint32(encoding_binary.LittleEndian.Uint32(dAtA[iNdEx:])) + iNdEx += 4 + default: + iNdEx = preIndex + skippy, err := skipHeader(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthHeader + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipHeader(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowHeader + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowHeader + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowHeader + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthHeader + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowHeader + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipHeader(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthHeader = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowHeader = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("pin/internal/pb/header.proto", fileDescriptor_header_778100e52d428560) +} + +var fileDescriptor_header_778100e52d428560 = []byte{ + // 154 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x29, 0xc8, 0xcc, 0xd3, + 0xcf, 0xcc, 0x2b, 0x49, 0x2d, 0xca, 0x4b, 0xcc, 0xd1, 0x2f, 0x48, 0xd2, 0xcf, 0x48, 0x4d, 0x4c, + 0x49, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0xc8, 0x2c, 0x48, 0x2b, 0xd6, 0x2b, + 0xc8, 0xcc, 0x53, 0x8a, 0xe5, 0x62, 0x0e, 0x4e, 0x2d, 0x11, 0x92, 0xe3, 0x62, 0x2f, 0x4b, 0x2d, + 0x2a, 0xce, 0xcc, 0xcf, 0x93, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x75, 0x62, 0x39, 0x71, 0x4f, 0x9e, + 0x21, 0x08, 0x26, 0x28, 0x24, 0xc3, 0xc5, 0x96, 0x96, 0x98, 0x97, 0x5f, 0x5a, 0x22, 0xc1, 0x84, + 0x24, 0x0d, 0x15, 0x13, 0x92, 0xe0, 0x62, 0x29, 0x4e, 0x4d, 0x4d, 0x91, 0x60, 0x56, 0x60, 0xd4, + 0x60, 0x87, 0xca, 0x81, 0x45, 0x9c, 0x44, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, + 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0xa2, 0x98, 0x0a, 0x92, 0x00, 0x01, 0x00, 0x00, + 0xff, 0xff, 0xc3, 0xf9, 0x7f, 0x24, 0x9d, 0x00, 0x00, 0x00, } diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 19accabe6..a95978f98 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -67,9 +67,9 @@ func storeItems(ctx context.Context, dag ipld.DAGService, estimatedLen uint64, d internalKeys(emptyKey) hdr := &pb.Set{ - Version: proto.Uint32(1), - Fanout: proto.Uint32(defaultFanout), - Seed: proto.Uint32(depth), + Version: 1, + Fanout: defaultFanout, + Seed: depth, } if err := writeHdr(n, hdr); err != nil { return nil, err From 4181a888e63f202af6fe8e139b5cc4b30067a090 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 8 Aug 2018 20:14:18 -0700 Subject: [PATCH 2551/3817] update protobuf files in go-ipfs Also: * Switch to gogo for filestore for consistency. * Use the "faster" codegen for fewer allocations. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@38adf1202dbbdd600bfd65b659fc6ce0e6325d71 --- filestore/fsrefstore.go | 10 +- filestore/pb/Makefile | 10 - filestore/pb/dataobj.pb.go | 369 ++++++++++++++++++++++++++++++++++--- filestore/pb/dataobj.proto | 2 + filestore/util.go | 12 +- 5 files changed, 360 insertions(+), 43 deletions(-) delete mode 100644 filestore/pb/Makefile diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index eb9c7d7e1..81b687537 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -17,8 +17,8 @@ import ( dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" blockstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" - proto "gx/ipfs/QmZHU2gx42NPTYXzw6pJkuX6xCE7bKECp6e8QcPdoLx8sx/protobuf/proto" posinfo "gx/ipfs/QmdBpJ5VTfL79VwKDU93z7fyZJ3mm4UaBHrE73CWRw2Bjd/go-ipfs-posinfo" + proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) // FilestorePrefix identifies the key prefix for FileManager blocks. @@ -276,7 +276,7 @@ func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { if !f.AllowUrls { return ErrUrlstoreNotEnabled } - dobj.FilePath = proto.String(b.PosInfo.FullPath) + dobj.FilePath = b.PosInfo.FullPath } else { if !f.AllowFiles { return ErrFilestoreNotEnabled @@ -290,10 +290,10 @@ func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { return err } - dobj.FilePath = proto.String(filepath.ToSlash(p)) + dobj.FilePath = filepath.ToSlash(p) } - dobj.Offset = proto.Uint64(b.PosInfo.Offset) - dobj.Size_ = proto.Uint64(uint64(len(b.RawData()))) + dobj.Offset = b.PosInfo.Offset + dobj.Size_ = uint64(len(b.RawData())) data, err := proto.Marshal(&dobj) if err != nil { diff --git a/filestore/pb/Makefile b/filestore/pb/Makefile deleted file mode 100644 index 5101a482d..000000000 --- a/filestore/pb/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -PB = $(wildcard *.proto) -GO = $(PB:.proto=.pb.go) - -all: $(GO) - -%.pb.go: %.proto - protoc --gogo_out=. $< - -clean: - rm *.pb.go diff --git a/filestore/pb/dataobj.pb.go b/filestore/pb/dataobj.pb.go index 3ddf73081..6046acbd6 100644 --- a/filestore/pb/dataobj.pb.go +++ b/filestore/pb/dataobj.pb.go @@ -1,55 +1,83 @@ -// Code generated by protoc-gen-gogo. -// source: dataobj.proto -// DO NOT EDIT! +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: filestore/pb/dataobj.proto -/* -Package datastore_pb is a generated protocol buffer package. - -It is generated from these files: - dataobj.proto - -It has these top-level messages: - DataObj -*/ package datastore_pb import proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" import fmt "fmt" import math "math" +import io "io" + // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + type DataObj struct { - FilePath *string `protobuf:"bytes,1,opt,name=FilePath" json:"FilePath,omitempty"` - Offset *uint64 `protobuf:"varint,2,opt,name=Offset" json:"Offset,omitempty"` - Size_ *uint64 `protobuf:"varint,3,opt,name=Size" json:"Size,omitempty"` - XXX_unrecognized []byte `json:"-"` + FilePath string `protobuf:"bytes,1,opt,name=FilePath" json:"FilePath"` + Offset uint64 `protobuf:"varint,2,opt,name=Offset" json:"Offset"` + Size_ uint64 `protobuf:"varint,3,opt,name=Size" json:"Size"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *DataObj) Reset() { *m = DataObj{} } func (m *DataObj) String() string { return proto.CompactTextString(m) } func (*DataObj) ProtoMessage() {} +func (*DataObj) Descriptor() ([]byte, []int) { + return fileDescriptor_dataobj_216c555249812eeb, []int{0} +} +func (m *DataObj) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DataObj) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DataObj.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalTo(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (dst *DataObj) XXX_Merge(src proto.Message) { + xxx_messageInfo_DataObj.Merge(dst, src) +} +func (m *DataObj) XXX_Size() int { + return m.Size() +} +func (m *DataObj) XXX_DiscardUnknown() { + xxx_messageInfo_DataObj.DiscardUnknown(m) +} + +var xxx_messageInfo_DataObj proto.InternalMessageInfo func (m *DataObj) GetFilePath() string { - if m != nil && m.FilePath != nil { - return *m.FilePath + if m != nil { + return m.FilePath } return "" } func (m *DataObj) GetOffset() uint64 { - if m != nil && m.Offset != nil { - return *m.Offset + if m != nil { + return m.Offset } return 0 } func (m *DataObj) GetSize_() uint64 { - if m != nil && m.Size_ != nil { - return *m.Size_ + if m != nil { + return m.Size_ } return 0 } @@ -57,3 +85,300 @@ func (m *DataObj) GetSize_() uint64 { func init() { proto.RegisterType((*DataObj)(nil), "datastore.pb.DataObj") } +func (m *DataObj) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DataObj) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + dAtA[i] = 0xa + i++ + i = encodeVarintDataobj(dAtA, i, uint64(len(m.FilePath))) + i += copy(dAtA[i:], m.FilePath) + dAtA[i] = 0x10 + i++ + i = encodeVarintDataobj(dAtA, i, uint64(m.Offset)) + dAtA[i] = 0x18 + i++ + i = encodeVarintDataobj(dAtA, i, uint64(m.Size_)) + return i, nil +} + +func encodeVarintDataobj(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *DataObj) Size() (n int) { + var l int + _ = l + l = len(m.FilePath) + n += 1 + l + sovDataobj(uint64(l)) + n += 1 + sovDataobj(uint64(m.Offset)) + n += 1 + sovDataobj(uint64(m.Size_)) + return n +} + +func sovDataobj(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozDataobj(x uint64) (n int) { + return sovDataobj(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *DataObj) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDataobj + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DataObj: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DataObj: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FilePath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDataobj + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDataobj + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FilePath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType) + } + m.Offset = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDataobj + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Offset |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Size_", wireType) + } + m.Size_ = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDataobj + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Size_ |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipDataobj(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDataobj + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipDataobj(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDataobj + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDataobj + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDataobj + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthDataobj + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDataobj + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipDataobj(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthDataobj = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowDataobj = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("filestore/pb/dataobj.proto", fileDescriptor_dataobj_216c555249812eeb) } + +var fileDescriptor_dataobj_216c555249812eeb = []byte{ + // 151 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4a, 0xcb, 0xcc, 0x49, + 0x2d, 0x2e, 0xc9, 0x2f, 0x4a, 0xd5, 0x2f, 0x48, 0xd2, 0x4f, 0x49, 0x2c, 0x49, 0xcc, 0x4f, 0xca, + 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x01, 0x71, 0xc1, 0x72, 0x7a, 0x05, 0x49, 0x4a, + 0xc9, 0x5c, 0xec, 0x2e, 0x89, 0x25, 0x89, 0xfe, 0x49, 0x59, 0x42, 0x0a, 0x5c, 0x1c, 0x6e, 0x99, + 0x39, 0xa9, 0x01, 0x89, 0x25, 0x19, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x4e, 0x2c, 0x27, 0xee, + 0xc9, 0x33, 0x04, 0xc1, 0x45, 0x85, 0x64, 0xb8, 0xd8, 0xfc, 0xd3, 0xd2, 0x8a, 0x53, 0x4b, 0x24, + 0x98, 0x14, 0x18, 0x35, 0x58, 0xa0, 0xf2, 0x50, 0x31, 0x21, 0x09, 0x2e, 0x96, 0xe0, 0xcc, 0xaa, + 0x54, 0x09, 0x66, 0x24, 0x39, 0xb0, 0x88, 0x93, 0xc0, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, + 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0x03, 0x20, 0x00, 0x00, 0xff, 0xff, 0x8c, + 0xe7, 0x83, 0xa2, 0xa1, 0x00, 0x00, 0x00, +} diff --git a/filestore/pb/dataobj.proto b/filestore/pb/dataobj.proto index c7d7f0eea..909d22b77 100644 --- a/filestore/pb/dataobj.proto +++ b/filestore/pb/dataobj.proto @@ -1,3 +1,5 @@ +syntax = "proto2"; + package datastore.pb; message DataObj { diff --git a/filestore/util.go b/filestore/util.go index 0cdbad8de..d0e25c8d4 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -212,9 +212,9 @@ func listAllFileOrder(fs *Filestore, verify bool) (func() *ListRes, error) { } // now reconstruct the DataObj dobj := pb.DataObj{ - FilePath: &v.filePath, - Offset: &v.offset, - Size_: &v.size, + FilePath: v.filePath, + Offset: v.offset, + Size_: v.size, } // now if we could not convert the datastore key return that // error @@ -277,8 +277,8 @@ func mkListRes(c *cid.Cid, d *pb.DataObj, err error) *ListRes { Status: status, ErrorMsg: errorMsg, Key: c, - FilePath: *d.FilePath, - Size: *d.Size_, - Offset: *d.Offset, + FilePath: d.FilePath, + Size: d.Size_, + Offset: d.Offset, } } From a8eca1817ad6630e0029685b52a48bb06a9f1cab Mon Sep 17 00:00:00 2001 From: Jeromy Date: Thu, 16 Aug 2018 11:58:14 -0700 Subject: [PATCH 2552/3817] gx publish 1.1.1 This commit was moved from ipld/go-car@72913caaf56152e92ec9a7b8977349234c15576c --- ipld/car/car.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index 2ab1a3b20..1d68e92a7 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -8,7 +8,7 @@ import ( util "github.com/ipfs/go-car/util" - cbor "gx/ipfs/QmSyK1ZiAP98YvnxsTfQpb669V2xeTHRbG4Y6fgKS3vVSd/go-ipld-cbor" + cbor "gx/ipfs/QmPbqRavwDZLfmpeW6eoyAoQ5rT2LoCW98JhvRc22CqkZS/go-ipld-cbor" "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" format "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" From 1b26542344b174050f520426fc8ed5dca898f548 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 20 Aug 2018 15:18:20 +0200 Subject: [PATCH 2553/3817] Extract from go-ipfs. Add README, LICENSE, ci. This commit was moved from ipfs/go-mfs@c3b2cc4c04dab696bbaef7a2215b3444477039b6 --- mfs/LICENSE | 21 +++++++++++++++++++++ mfs/Makefile | 18 ++++++++++++++++++ mfs/README.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 mfs/LICENSE create mode 100644 mfs/Makefile create mode 100644 mfs/README.md diff --git a/mfs/LICENSE b/mfs/LICENSE new file mode 100644 index 000000000..e4224df5b --- /dev/null +++ b/mfs/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 IPFS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/mfs/Makefile b/mfs/Makefile new file mode 100644 index 000000000..73f2841f6 --- /dev/null +++ b/mfs/Makefile @@ -0,0 +1,18 @@ +all: deps +gx: + go get github.com/whyrusleeping/gx + go get github.com/whyrusleeping/gx-go +deps: gx + gx --verbose install --global + gx-go rewrite +test: deps + gx test -v -race -coverprofile=coverage.txt -covermode=atomic . +rw: + gx-go rewrite +rwundo: + gx-go rewrite --undo +publish: rwundo + gx publish +.PHONY: all gx deps test rw rwundo publish + + diff --git a/mfs/README.md b/mfs/README.md new file mode 100644 index 000000000..d8247a5b6 --- /dev/null +++ b/mfs/README.md @@ -0,0 +1,44 @@ +# go-mfs + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![GoDoc](https://godoc.org/github.com/ipfs/go-mfs?status.svg)](https://godoc.org/github.com/ipfs/go-mfs) +[![Build Status](https://travis-ci.org/ipfs/go-mfs.svg?branch=master)](https://travis-ci.org/ipfs/go-mfs) + +> go-mfs implements an in-memory model of a mutable IPFS filesystem. + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Contribute](#contribute) +- [License](#license) + +## Install + +`go-mfs` works like a regular Go module: + +``` +> go get github.com/ipfs/go-mfs +``` + +It uses [Gx](https://github.com/whyrusleeping/gx) to manage dependencies. You can use `make all` to build it with the `gx` dependencies. + +## Usage + +``` +import "github.com/ipfs/go-mfs" +``` + +Check the [GoDoc documentation](https://godoc.org/github.com/ipfs/go-mfs) + +## Contribute + +PRs accepted. + +Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +MIT © Protocol Labs, Inc. From 09c10efc44df4d479ca787e522940e858225194d Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 20 Aug 2018 15:23:31 +0200 Subject: [PATCH 2554/3817] gx publish 0.0.1 This commit was moved from ipfs/go-mfs@9ee29333acb16626c0e69b85521b76f5bc5eb1a8 --- mfs/dir.go | 12 ++++++------ mfs/fd.go | 2 +- mfs/file.go | 10 +++++----- mfs/mfs_test.go | 30 +++++++++++++++--------------- mfs/ops.go | 6 +++--- mfs/repub_test.go | 4 ++-- mfs/system.go | 10 +++++----- 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 50d3747c7..676bf97d6 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -9,13 +9,13 @@ import ( "sync" "time" - dag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" - ft "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs" - uio "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs/io" - ufspb "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs/pb" + dag "github.com/ipfs/go-merkledag" + ft "github.com/ipfs/go-unixfs" + uio "github.com/ipfs/go-unixfs/io" + ufspb "github.com/ipfs/go-unixfs/pb" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" - ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" ) var ErrNotYetImplemented = errors.New("not yet implemented") diff --git a/mfs/fd.go b/mfs/fd.go index 8b84000fd..0f0d3d426 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -4,7 +4,7 @@ import ( "fmt" "io" - mod "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs/mod" + mod "github.com/ipfs/go-unixfs/mod" context "context" ) diff --git a/mfs/file.go b/mfs/file.go index fec9c2125..00c70ae4b 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -5,12 +5,12 @@ import ( "fmt" "sync" - dag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" - ft "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs" - mod "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs/mod" + dag "github.com/ipfs/go-merkledag" + ft "github.com/ipfs/go-unixfs" + mod "github.com/ipfs/go-unixfs/mod" - chunker "gx/ipfs/QmWbCAB5f3LDumj4ncz1UCHSiyXrXxkMxZB6Wv35xi4P8z/go-ipfs-chunker" - ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" + chunker "github.com/ipfs/go-ipfs-chunker" + ipld "github.com/ipfs/go-ipld-format" ) type File struct { diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 524732f2c..63c9bff63 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,21 +14,21 @@ import ( "testing" "time" - dag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" - bserv "gx/ipfs/QmTZZrpd9o4vpYr9TEADW2EoJ9fzUtAgpXqjxZHbKR2T15/go-blockservice" - "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" - ft "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs" - importer "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs/importer" - uio "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs/io" - - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" - offline "gx/ipfs/QmVozMmsgK2PYyaHQsrcWLBYigb1m6mW8YhCBG2Cb4Uxq9/go-ipfs-exchange-offline" - chunker "gx/ipfs/QmWbCAB5f3LDumj4ncz1UCHSiyXrXxkMxZB6Wv35xi4P8z/go-ipfs-chunker" - bstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" - ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" + bserv "github.com/ipfs/go-blockservice" + dag "github.com/ipfs/go-merkledag" + "github.com/ipfs/go-path" + ft "github.com/ipfs/go-unixfs" + importer "github.com/ipfs/go-unixfs/importer" + uio "github.com/ipfs/go-unixfs/io" + + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + bstore "github.com/ipfs/go-ipfs-blockstore" + chunker "github.com/ipfs/go-ipfs-chunker" + offline "github.com/ipfs/go-ipfs-exchange-offline" + u "github.com/ipfs/go-ipfs-util" + ipld "github.com/ipfs/go-ipld-format" ) func emptyDirNode() *dag.ProtoNode { diff --git a/mfs/ops.go b/mfs/ops.go index c90071fb8..656b8dff9 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -6,10 +6,10 @@ import ( gopath "path" "strings" - path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" + path "github.com/ipfs/go-path" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" - ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" ) // Mv moves the file or directory at 'src' to 'dst' diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 123c20859..cfc056a59 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - ci "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil/ci" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + cid "github.com/ipfs/go-cid" + ci "github.com/libp2p/go-testutil/ci" ) func TestRepublisher(t *testing.T) { diff --git a/mfs/system.go b/mfs/system.go index 2e7c400c9..bd799880e 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -16,12 +16,12 @@ import ( "sync" "time" - dag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" - ft "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs" + dag "github.com/ipfs/go-merkledag" + ft "github.com/ipfs/go-unixfs" - logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" - ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" + logging "github.com/ipfs/go-log" ) var ErrNotExist = errors.New("no such rootfs") From ea07441966a85162c95835a20b06a1825d9e51b9 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 21 Aug 2018 17:10:11 -0700 Subject: [PATCH 2555/3817] gx: update go-cid, go-libp2p-peer, go-ipfs-cmds, go-ipfs-cmdkit License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@77f5c2bef18eed346196081226b440f8d3f9f266 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 20 ++++++++++---------- namesys/namesys.go | 6 +++--- namesys/namesys_test.go | 12 ++++++------ namesys/proquint.go | 2 +- namesys/publisher.go | 12 ++++++------ namesys/publisher_test.go | 10 +++++----- namesys/republisher/repub.go | 6 +++--- namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 10 +++++----- namesys/routing.go | 14 +++++++------- 14 files changed, 53 insertions(+), 53 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 7ac2f9e06..b88668dcc 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" + path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index 2d2e829b9..3d7fc51e6 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" + path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 5541f191c..d329875a1 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,8 +8,8 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" + path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" ) type LookupTXTFunc func(name string) (txt []string, err error) diff --git a/namesys/interface.go b/namesys/interface.go index 48333e7f2..f2a2c868b 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" + path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 187daeefb..dd84308f2 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,21 +6,21 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" + path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - mockrouting "gx/ipfs/QmRr8DpNhQMzsoqAitUrw43D82pyPXZkyUqarhSAfkrdaQ/go-ipfs-routing/mock" - offline "gx/ipfs/QmRr8DpNhQMzsoqAitUrw43D82pyPXZkyUqarhSAfkrdaQ/go-ipfs-routing/offline" - routing "gx/ipfs/QmSD6bSPcXaaR7LpQHjytLWQD7DrCsb415CWfpbd9Szemb/go-libp2p-routing" - ropts "gx/ipfs/QmSD6bSPcXaaR7LpQHjytLWQD7DrCsb415CWfpbd9Szemb/go-libp2p-routing/options" - record "gx/ipfs/QmUTQSGgjs8CHm9yBcUHicpRs7C9abhyZiBwjzCUp1pNgX/go-libp2p-record" + peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" + testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" + routing "gx/ipfs/QmS4niovD1U6pRjUBXivr1zvvLBqiTKbERjFo994JU7oQS/go-libp2p-routing" + ropts "gx/ipfs/QmS4niovD1U6pRjUBXivr1zvvLBqiTKbERjFo994JU7oQS/go-libp2p-routing/options" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" - ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" - testutil "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil" - pstore "gx/ipfs/QmYLXCWN2myozZpx8Wx4UjrRuQuhY3YtWoMi6SHaXii6aM/go-libp2p-peerstore" - peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" + mockrouting "gx/ipfs/Qmd45r5jHr1PKMNQqifnbZy1ZQwHdtXUDJFamUEvUJE544/go-ipfs-routing/mock" + offline "gx/ipfs/Qmd45r5jHr1PKMNQqifnbZy1ZQwHdtXUDJFamUEvUJE544/go-ipfs-routing/offline" + record "gx/ipfs/QmdHb9aBELnQKTVhvvA3hsQbRgUAwsWUzBP2vZ6Y5FBYvE/go-libp2p-record" + pstore "gx/ipfs/QmeKD8YT7887Xu6Z86iZmpYNxrLogJexqxEugSmaf14k64/go-libp2p-peerstore" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index d733b8db3..ac5faeaeb 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,15 +6,15 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" + path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - routing "gx/ipfs/QmSD6bSPcXaaR7LpQHjytLWQD7DrCsb415CWfpbd9Szemb/go-libp2p-routing" + peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" + routing "gx/ipfs/QmS4niovD1U6pRjUBXivr1zvvLBqiTKbERjFo994JU7oQS/go-libp2p-routing" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 25c2bc72b..f1cfa4d48 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,16 +7,16 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" - "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs" + "gx/ipfs/QmQjEpRiwVvtowhq69dAtB4jhioPVFXiCcWZm9Sfgn7eqc/go-unixfs" + path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - offroute "gx/ipfs/QmRr8DpNhQMzsoqAitUrw43D82pyPXZkyUqarhSAfkrdaQ/go-ipfs-routing/offline" + peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" - ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" - pstore "gx/ipfs/QmYLXCWN2myozZpx8Wx4UjrRuQuhY3YtWoMi6SHaXii6aM/go-libp2p-peerstore" - peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" + offroute "gx/ipfs/Qmd45r5jHr1PKMNQqifnbZy1ZQwHdtXUDJFamUEvUJE544/go-ipfs-routing/offline" + pstore "gx/ipfs/QmeKD8YT7887Xu6Z86iZmpYNxrLogJexqxEugSmaf14k64/go-libp2p-peerstore" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index eb8ae3621..d869ce01e 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,8 +7,8 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" + path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index 4d3c7abd4..8a4c4002a 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,16 +7,16 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" - ft "gx/ipfs/QmWv8MYwgPK4zXYv1et1snWJ6FWGqaL6xY2y9X1bRSKBxk/go-unixfs" + ft "gx/ipfs/QmQjEpRiwVvtowhq69dAtB4jhioPVFXiCcWZm9Sfgn7eqc/go-unixfs" + path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" + pb "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns/pb" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - routing "gx/ipfs/QmSD6bSPcXaaR7LpQHjytLWQD7DrCsb415CWfpbd9Szemb/go-libp2p-routing" + peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" + routing "gx/ipfs/QmS4niovD1U6pRjUBXivr1zvvLBqiTKbERjFo994JU7oQS/go-libp2p-routing" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dsquery "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" - ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" - pb "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns/pb" - peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 98f17b74f..5b33940f3 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -6,15 +6,15 @@ import ( "testing" "time" + ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - mockrouting "gx/ipfs/QmRr8DpNhQMzsoqAitUrw43D82pyPXZkyUqarhSAfkrdaQ/go-ipfs-routing/mock" - dshelp "gx/ipfs/QmSLS8mMWsm54vdQuwgde9wBgLg5usVQY4i9r8kXhfje8g/go-ipfs-ds-help" + peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" + testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" - ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" - testutil "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" - peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" + dshelp "gx/ipfs/Qmd39D2vUhmPKQA2fgykjo2JXwekHKeJUggmGRpYuVMA2Z/go-ipfs-ds-help" + mockrouting "gx/ipfs/Qmd45r5jHr1PKMNQqifnbZy1ZQwHdtXUDJFamUEvUJE544/go-ipfs-routing/mock" ) type identity struct { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 4adbcb308..90fab3f23 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,15 +7,15 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" + path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + pb "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns/pb" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - pb "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns/pb" - peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 1aa3d9f85..387db7f90 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" + path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + mocknet "gx/ipfs/QmQiaskfWpdRJ4x2spEQjPFTUkEB87KDYu91qnNYBqvvcX/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmUDzeFgYrRmHL2hUB6NZmqcBVQtUzETwmFRUc9onfSSHr/go-libp2p/p2p/net/mock" - pstore "gx/ipfs/QmYLXCWN2myozZpx8Wx4UjrRuQuhY3YtWoMi6SHaXii6aM/go-libp2p-peerstore" + pstore "gx/ipfs/QmeKD8YT7887Xu6Z86iZmpYNxrLogJexqxEugSmaf14k64/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 27e3b5fca..b2a4e23ac 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,14 +6,14 @@ import ( "testing" "time" - path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" + path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" - mockrouting "gx/ipfs/QmRr8DpNhQMzsoqAitUrw43D82pyPXZkyUqarhSAfkrdaQ/go-ipfs-routing/mock" + ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" + peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" + testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" - ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" - testutil "gx/ipfs/QmXG74iiKQnDstVQq9fPFQEB6JTNSWBbAWE1qsq6L4E5sR/go-testutil" - peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" + mockrouting "gx/ipfs/Qmd45r5jHr1PKMNQqifnbZy1ZQwHdtXUDJFamUEvUJE544/go-ipfs-routing/mock" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index 5bdd67efd..c533daada 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,16 +6,16 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" + path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" + pb "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns/pb" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - routing "gx/ipfs/QmSD6bSPcXaaR7LpQHjytLWQD7DrCsb415CWfpbd9Szemb/go-libp2p-routing" - ipns "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns" - pb "gx/ipfs/QmVHij7PuWUFeLcmRbD1ykDwB1WZMYP8yixo9bprUb3QHG/go-ipns/pb" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" - peer "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" - dht "gx/ipfs/QmdP3wKxB6x6vJ57tDrewAJF2qv4ULejCZ6dspJRnk3993/go-libp2p-kad-dht" + routing "gx/ipfs/QmS4niovD1U6pRjUBXivr1zvvLBqiTKbERjFo994JU7oQS/go-libp2p-routing" + dht "gx/ipfs/QmTRj8mj6X5LtjVochPPSNX6MTbJ6iVojcfakWJKG13re7/go-libp2p-kad-dht" + cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) From 66c959b336ec55e8b2f9fbb8bd17f0922fdc0473 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 21 Aug 2018 17:10:11 -0700 Subject: [PATCH 2556/3817] gx: update go-cid, go-libp2p-peer, go-ipfs-cmds, go-ipfs-cmdkit License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@14b366d332ad7524863889d6eddc696b550c57dd --- coreiface/coreapi.go | 2 +- coreiface/dag.go | 2 +- coreiface/key.go | 2 +- coreiface/object.go | 4 ++-- coreiface/options/dag.go | 2 +- coreiface/path.go | 4 ++-- coreiface/unixfs.go | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index ab5374069..696eefbaf 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -5,7 +5,7 @@ package iface import ( "context" - ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" + ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" ) // CoreAPI defines an unified interface to IPFS for Go programs diff --git a/coreiface/dag.go b/coreiface/dag.go index d547e8531..d3270928c 100644 --- a/coreiface/dag.go +++ b/coreiface/dag.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" + ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" ) // DagOps groups operations that can be batched together diff --git a/coreiface/key.go b/coreiface/key.go index 3730f3592..cc7c409fd 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -5,7 +5,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - "gx/ipfs/QmcZSzKEM5yDfpZbeEEZaVmaZ1zXm6JWTbrQZSB8hCVPzk/go-libp2p-peer" + "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" ) // Key specifies the interface to Keys in KeyAPI Keystore diff --git a/coreiface/object.go b/coreiface/object.go index 812b69a64..750638a33 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -6,8 +6,8 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" - ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" + ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" + cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" ) // ObjectStat provides information about dag nodes diff --git a/coreiface/options/dag.go b/coreiface/options/dag.go index e89e4d707..689bb5c53 100644 --- a/coreiface/options/dag.go +++ b/coreiface/options/dag.go @@ -3,7 +3,7 @@ package options import ( "math" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" ) type DagPutSettings struct { diff --git a/coreiface/path.go b/coreiface/path.go index 49cea48cc..25b09f486 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,9 +1,9 @@ package iface import ( - ipfspath "gx/ipfs/QmWMcvZbNvk5codeqbm7L89C9kqSwka4KaHnDb8HRnxsSL/go-path" + ipfspath "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" ) //TODO: merge with ipfspath so we don't depend on it diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index b42f56ba8..80f7ba396 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -4,7 +4,7 @@ import ( "context" "io" - ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" + ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" ) // UnixfsAPI is the basic interface to immutable files in IPFS From 84a85aade52c72deb3769a5de206c8308a5e5cb5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 21 Aug 2018 17:10:11 -0700 Subject: [PATCH 2557/3817] gx: update go-cid, go-libp2p-peer, go-ipfs-cmds, go-ipfs-cmdkit License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@ec65f27baef1c7ba3db7213f08f2f46885067f28 --- pinning/pinner/gc/gc.go | 14 +++++++------- pinning/pinner/pin.go | 6 +++--- pinning/pinner/pin_test.go | 10 +++++----- pinning/pinner/set.go | 6 +++--- pinning/pinner/set_test.go | 10 +++++----- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 456aca9ba..30fe9de94 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,16 +8,16 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" - bserv "gx/ipfs/QmTZZrpd9o4vpYr9TEADW2EoJ9fzUtAgpXqjxZHbKR2T15/go-blockservice" + dag "gx/ipfs/QmRiQCJZ91B7VNmLvA6sxzDuBJGSojS3uXHHVuNr3iueNZ/go-merkledag" + bserv "gx/ipfs/QmbSB9Uh3wVgmiCb1fAb8zuC3qAE6un4kd1jvatUurfAmB/go-blockservice" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" dstore "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - offline "gx/ipfs/QmVozMmsgK2PYyaHQsrcWLBYigb1m6mW8YhCBG2Cb4Uxq9/go-ipfs-exchange-offline" - bstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" - ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" - "gx/ipfs/QmfMirfpEKQFctVpBYTvETxxLoU5q4ZJWsAMrtwSSE2bkn/go-verifcid" + "gx/ipfs/QmVUhfewLZpSaAiBYCpw2krYMaiVmFuhr2iurQLuRoU6sD/go-verifcid" + ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" + cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" + offline "gx/ipfs/QmZxjqR9Qgompju73kakSoUj3rbVndAzky3oCDiBNCxPs1/go-ipfs-exchange-offline" + bstore "gx/ipfs/QmcmpX42gtDv1fz24kau4wjS9hfwWj5VexWBKgGnWzsyag/go-ipfs-blockstore" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index effb0f10f..3a36946c7 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,12 +10,12 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" + mdag "gx/ipfs/QmRiQCJZ91B7VNmLvA6sxzDuBJGSojS3uXHHVuNr3iueNZ/go-merkledag" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" - ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" + ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" + cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index a715b79f8..590a2e069 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" - mdag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" - bs "gx/ipfs/QmTZZrpd9o4vpYr9TEADW2EoJ9fzUtAgpXqjxZHbKR2T15/go-blockservice" + mdag "gx/ipfs/QmRiQCJZ91B7VNmLvA6sxzDuBJGSojS3uXHHVuNr3iueNZ/go-merkledag" + bs "gx/ipfs/QmbSB9Uh3wVgmiCb1fAb8zuC3qAE6un4kd1jvatUurfAmB/go-blockservice" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" - offline "gx/ipfs/QmVozMmsgK2PYyaHQsrcWLBYigb1m6mW8YhCBG2Cb4Uxq9/go-ipfs-exchange-offline" - blockstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" + offline "gx/ipfs/QmZxjqR9Qgompju73kakSoUj3rbVndAzky3oCDiBNCxPs1/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmcmpX42gtDv1fz24kau4wjS9hfwWj5VexWBKgGnWzsyag/go-ipfs-blockstore" ) var rand = util.NewTimeSeededRand() diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index a95978f98..27c5a9271 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,10 +10,10 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" + "gx/ipfs/QmRiQCJZ91B7VNmLvA6sxzDuBJGSojS3uXHHVuNr3iueNZ/go-merkledag" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" - ipld "gx/ipfs/QmaA8GkXUYinkkndvg7T6Tx7gYXemhxjaxLisEPes7Rf1P/go-ipld-format" + ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" + cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 62a8a9a25..cc0bfcb4a 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,14 +5,14 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" - bserv "gx/ipfs/QmTZZrpd9o4vpYr9TEADW2EoJ9fzUtAgpXqjxZHbKR2T15/go-blockservice" + dag "gx/ipfs/QmRiQCJZ91B7VNmLvA6sxzDuBJGSojS3uXHHVuNr3iueNZ/go-merkledag" + bserv "gx/ipfs/QmbSB9Uh3wVgmiCb1fAb8zuC3qAE6un4kd1jvatUurfAmB/go-blockservice" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" - offline "gx/ipfs/QmVozMmsgK2PYyaHQsrcWLBYigb1m6mW8YhCBG2Cb4Uxq9/go-ipfs-exchange-offline" - blockstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" + offline "gx/ipfs/QmZxjqR9Qgompju73kakSoUj3rbVndAzky3oCDiBNCxPs1/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmcmpX42gtDv1fz24kau4wjS9hfwWj5VexWBKgGnWzsyag/go-ipfs-blockstore" ) func ignoreCids(_ *cid.Cid) {} From 153c2ced3e698282fe64ca349e9bbec8c7c9b24d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 21 Aug 2018 17:10:11 -0700 Subject: [PATCH 2558/3817] gx: update go-cid, go-libp2p-peer, go-ipfs-cmds, go-ipfs-cmdkit License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@5b958477f9f38aec44ee660309904f2e234450b5 --- filestore/filestore.go | 8 ++++---- filestore/filestore_test.go | 8 ++++---- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 4a1f66df6..dfbcf9ed6 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,12 +11,12 @@ import ( "context" "errors" - blocks "gx/ipfs/QmR54CzE4UcdFAZDehj6HFyy3eSHhVsJUpjfnhCmscuStS/go-block-format" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" - blockstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" - posinfo "gx/ipfs/QmdBpJ5VTfL79VwKDU93z7fyZJ3mm4UaBHrE73CWRw2Bjd/go-ipfs-posinfo" + blocks "gx/ipfs/QmWAzSEoqZ6xU6pu8yL8e5WaMb7wtbfbhhN4p1DknUPtr3/go-block-format" + posinfo "gx/ipfs/QmXD4grfThQ4LwVoEEfe4dgR7ukmbV9TppM5Q4SPowp7hU/go-ipfs-posinfo" + cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" + blockstore "gx/ipfs/QmcmpX42gtDv1fz24kau4wjS9hfwWj5VexWBKgGnWzsyag/go-ipfs-blockstore" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index bb7278620..e7c362f49 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,12 +7,12 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmQzSpSjkdGHW6WFBhUG6P3t9K8yv7iucucT1cQaqJ6tgd/go-merkledag" + dag "gx/ipfs/QmRiQCJZ91B7VNmLvA6sxzDuBJGSojS3uXHHVuNr3iueNZ/go-merkledag" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - blockstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" - posinfo "gx/ipfs/QmdBpJ5VTfL79VwKDU93z7fyZJ3mm4UaBHrE73CWRw2Bjd/go-ipfs-posinfo" + posinfo "gx/ipfs/QmXD4grfThQ4LwVoEEfe4dgR7ukmbV9TppM5Q4SPowp7hU/go-ipfs-posinfo" + cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" + blockstore "gx/ipfs/QmcmpX42gtDv1fz24kau4wjS9hfwWj5VexWBKgGnWzsyag/go-ipfs-blockstore" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 81b687537..265874be0 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,14 +10,14 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - blocks "gx/ipfs/QmR54CzE4UcdFAZDehj6HFyy3eSHhVsJUpjfnhCmscuStS/go-block-format" - dshelp "gx/ipfs/QmSLS8mMWsm54vdQuwgde9wBgLg5usVQY4i9r8kXhfje8g/go-ipfs-ds-help" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dsns "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/namespace" dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" - blockstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" - posinfo "gx/ipfs/QmdBpJ5VTfL79VwKDU93z7fyZJ3mm4UaBHrE73CWRw2Bjd/go-ipfs-posinfo" + blocks "gx/ipfs/QmWAzSEoqZ6xU6pu8yL8e5WaMb7wtbfbhhN4p1DknUPtr3/go-block-format" + posinfo "gx/ipfs/QmXD4grfThQ4LwVoEEfe4dgR7ukmbV9TppM5Q4SPowp7hU/go-ipfs-posinfo" + cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" + blockstore "gx/ipfs/QmcmpX42gtDv1fz24kau4wjS9hfwWj5VexWBKgGnWzsyag/go-ipfs-blockstore" + dshelp "gx/ipfs/Qmd39D2vUhmPKQA2fgykjo2JXwekHKeJUggmGRpYuVMA2Z/go-ipfs-ds-help" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) diff --git a/filestore/util.go b/filestore/util.go index d0e25c8d4..fa81127d8 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,11 +6,11 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - dshelp "gx/ipfs/QmSLS8mMWsm54vdQuwgde9wBgLg5usVQY4i9r8kXhfje8g/go-ipfs-ds-help" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" - blockstore "gx/ipfs/QmYBEfMSquSGnuxBthUoBJNs3F6p4VAPPvAgxq6XXGvTPh/go-ipfs-blockstore" - cid "gx/ipfs/QmYjnkEL7i731PirfVH1sis89evN7jt4otSHw5D2xXXwUV/go-cid" + cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" + blockstore "gx/ipfs/QmcmpX42gtDv1fz24kau4wjS9hfwWj5VexWBKgGnWzsyag/go-ipfs-blockstore" + dshelp "gx/ipfs/Qmd39D2vUhmPKQA2fgykjo2JXwekHKeJUggmGRpYuVMA2Z/go-ipfs-ds-help" ) // Status is used to identify the state of the block data referenced From e0f2bd3ae8cc797a0cdb789d8d7166b21d0264e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 2 Aug 2018 09:48:52 +0200 Subject: [PATCH 2559/3817] block cmd: use coreapi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@f4e38c0970c260e17d89348001eb15769e4650d8 --- coreiface/block.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/block.go b/coreiface/block.go index 468c00947..b99b05fdb 100644 --- a/coreiface/block.go +++ b/coreiface/block.go @@ -19,7 +19,7 @@ type BlockStat interface { // BlockAPI specifies the interface to the block layer type BlockAPI interface { // Put imports raw block data, hashing it using specified settings. - Put(context.Context, io.Reader, ...options.BlockPutOption) (ResolvedPath, error) + Put(context.Context, io.Reader, ...options.BlockPutOption) (BlockStat, error) // Get attempts to resolve the path and return a reader for data in the block Get(context.Context, Path) (io.Reader, error) From 1c96fc32d211d18837b63a3befd65a24c600e430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 13 Aug 2018 21:39:41 +0200 Subject: [PATCH 2560/3817] coreapi: block: don't allow creation of invalid cidv0s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@f9d6dcc420879110f6d332e3de5a2048c07497e9 --- coreiface/options/block.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/options/block.go b/coreiface/options/block.go index d6da99774..99445cca3 100644 --- a/coreiface/options/block.go +++ b/coreiface/options/block.go @@ -19,7 +19,7 @@ type BlockRmOption func(*BlockRmSettings) error func BlockPutOptions(opts ...BlockPutOption) (*BlockPutSettings, error) { options := &BlockPutSettings{ - Codec: "v0", + Codec: "", MhType: multihash.SHA2_256, MhLength: -1, } From 571c6def44851832a3617c699f1b166d1c9dc959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 15 Aug 2018 14:01:19 +0200 Subject: [PATCH 2561/3817] coreapi: block: move option logic to options package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@68340710253f13b5c5376a9f882608cb0ab17f8c --- coreiface/options/block.go | 44 +++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/coreiface/options/block.go b/coreiface/options/block.go index 99445cca3..36b3baa0e 100644 --- a/coreiface/options/block.go +++ b/coreiface/options/block.go @@ -1,7 +1,9 @@ package options import ( - "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + "fmt" + mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" ) type BlockPutSettings struct { @@ -17,20 +19,52 @@ type BlockRmSettings struct { type BlockPutOption func(*BlockPutSettings) error type BlockRmOption func(*BlockRmSettings) error -func BlockPutOptions(opts ...BlockPutOption) (*BlockPutSettings, error) { +func BlockPutOptions(opts ...BlockPutOption) (*BlockPutSettings, cid.Prefix, error) { options := &BlockPutSettings{ Codec: "", - MhType: multihash.SHA2_256, + MhType: mh.SHA2_256, MhLength: -1, } for _, opt := range opts { err := opt(options) if err != nil { - return nil, err + return nil, cid.Prefix{}, err } } - return options, nil + + 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") + } + } + + pref.Codec = formatval + + pref.MhType = options.MhType + pref.MhLength = options.MhLength + + return options, pref, nil } func BlockRmOptions(opts ...BlockRmOption) (*BlockRmSettings, error) { From 8f9f6a12e56ac2b203c71f843ba4c2feb02c34e3 Mon Sep 17 00:00:00 2001 From: Shaoxiong Li Date: Tue, 28 Aug 2018 10:25:42 +0800 Subject: [PATCH 2562/3817] Fix typo: Change 'should not' to 'should' This commit was moved from ipfs/go-ipfs-blockstore@2c327f3fa3892e63814117c14167a0053c354709 --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index f5cbc4c0f..4dd670c9a 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -66,7 +66,7 @@ type GCLocker interface { // PinLock locks the blockstore for sequences of puts expected to finish // with a pin (before GC). Multiple put->pin sequences can write through - // at the same time, but no GC should not happen simulatenously. + // at the same time, but no GC should happen simulatenously. // Reading during Pinning is safe, and requires no lock. PinLock() Unlocker From 94f1ce6e63af15c491b066d0f93e578b05809360 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 28 Aug 2018 13:52:49 +0200 Subject: [PATCH 2563/3817] chore: update deps + gx-go uw This commit was moved from ipld/go-car@112b7904144b67e952d0c0ebb56dee8ffc089dc4 --- ipld/car/car.go | 16 ++++++++-------- ipld/car/car/main.go | 2 +- ipld/car/car_test.go | 8 ++++---- ipld/car/util/util.go | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index 1d68e92a7..1f2bb0f7a 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -6,14 +6,14 @@ import ( "fmt" "io" - util "github.com/ipfs/go-car/util" - - cbor "gx/ipfs/QmPbqRavwDZLfmpeW6eoyAoQ5rT2LoCW98JhvRc22CqkZS/go-ipld-cbor" - "gx/ipfs/QmVzK524a2VWLqyvtBeiHKsUAWYgeAk4DBeZoY7vpNPNRx/go-block-format" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - format "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" - bstore "gx/ipfs/QmcD7SqfyQyA91TZUQ7VPRYbGarxmY7EsQewVYMuN5LNSv/go-ipfs-blockstore" - dag "gx/ipfs/QmeCaeBmCCEJrZahwXY4G2G8zRaNBWskrfKWoQ6Xv6c1DR/go-merkledag" + "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + bstore "github.com/ipfs/go-ipfs-blockstore" + cbor "github.com/ipfs/go-ipld-cbor" + format "github.com/ipfs/go-ipld-format" + dag "github.com/ipfs/go-merkledag" + + util "gx/ipfs/QmU9oYpqJsNWwAAJju8CzE7mv4NHAJUDWhoKHqgnhMCBy5/go-car/util" ) func init() { diff --git a/ipld/car/car/main.go b/ipld/car/car/main.go index 97127ca7c..877f692f1 100644 --- a/ipld/car/car/main.go +++ b/ipld/car/car/main.go @@ -7,7 +7,7 @@ import ( "io" "os" - "github.com/ipfs/go-car" + "gx/ipfs/QmU9oYpqJsNWwAAJju8CzE7mv4NHAJUDWhoKHqgnhMCBy5/go-car" cli "github.com/urfave/cli" ) diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 169c00520..c4a9f6cc1 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -5,10 +5,10 @@ import ( "context" "testing" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" - format "gx/ipfs/QmZtNq8dArGfnpCZfx2pUNY7UcjGhVp5qqwQ4hH6mpTMRQ/go-ipld-format" - dag "gx/ipfs/QmeCaeBmCCEJrZahwXY4G2G8zRaNBWskrfKWoQ6Xv6c1DR/go-merkledag" - dstest "gx/ipfs/QmeCaeBmCCEJrZahwXY4G2G8zRaNBWskrfKWoQ6Xv6c1DR/go-merkledag/test" + cid "github.com/ipfs/go-cid" + format "github.com/ipfs/go-ipld-format" + dag "github.com/ipfs/go-merkledag" + dstest "github.com/ipfs/go-merkledag/test" ) func assertAddNodes(t *testing.T, ds format.DAGService, nds ...format.Node) { diff --git a/ipld/car/util/util.go b/ipld/car/util/util.go index e39aaee08..a5ce11d77 100644 --- a/ipld/car/util/util.go +++ b/ipld/car/util/util.go @@ -7,8 +7,8 @@ import ( "fmt" "io" - mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - cid "gx/ipfs/QmYVNvtQkeZ6AKSwDrjQTs432QtL6umrrK41EBq3cu7iSP/go-cid" + cid "github.com/ipfs/go-cid" + mh "github.com/multiformats/go-multihash" ) var cidv0Pref = []byte{0x12, 0x20} From 5f606e36adf4dc80d5e3c91dd2eb4ffc587c3fc8 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 28 Aug 2018 13:57:09 +0200 Subject: [PATCH 2564/3817] chore: fix rewrite This commit was moved from ipld/go-car@e66d5d2c2b7d529864ce5618d2c2db4c0efaade7 --- ipld/car/car.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index 1f2bb0f7a..2568c0bc4 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -13,7 +13,7 @@ import ( format "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" - util "gx/ipfs/QmU9oYpqJsNWwAAJju8CzE7mv4NHAJUDWhoKHqgnhMCBy5/go-car/util" + util "github.com/ipfs/go-car/util" ) func init() { From 2cfbc84fff6f6d5c814d981930304aaa08081b60 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 28 Aug 2018 21:36:13 +0200 Subject: [PATCH 2565/3817] perf: avoid allocations when filtering nodes This commit was moved from ipfs/go-merkledag@fc8b2d42a68d36dac6de125fe626486f2fdb08da --- ipld/merkledag/node.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 7cca8c25d..c3e005d16 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -126,22 +126,26 @@ func (n *ProtoNode) AddRawLink(name string, l *ipld.Link) error { // RemoveNodeLink removes a link on this node by the given name. func (n *ProtoNode) RemoveNodeLink(name string) error { n.encoded = nil - good := make([]*ipld.Link, 0, len(n.links)) - var found bool - for _, l := range n.links { - if l.Name != name { - good = append(good, l) + ref := &n.links + filterPos := 0 + found := false + + for i := 0; i < len(*ref); i++ { + if v := (*ref)[i]; v.Name != name { + (*ref)[filterPos] = v + filterPos++ } else { found = true } } - n.links = good if !found { return ipld.ErrNotFound } + n.links = (*ref)[:filterPos] + return nil } From a44b59e236dd8d95c3342e9c745669ba3a5fd67c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 29 Aug 2018 16:08:25 -0700 Subject: [PATCH 2566/3817] directly parse peer IDs as peer IDs No need to parse it as a hash first. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@c11cb0f591519b9a7611b5c8a3dbaffd88eb8081 --- namesys/routing.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/namesys/routing.go b/namesys/routing.go index c533daada..65da47a2f 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -55,19 +55,13 @@ func (r *IpnsResolver) resolveOnce(ctx context.Context, name string, options *op } name = strings.TrimPrefix(name, "/ipns/") - hash, err := mh.FromB58String(name) + pid, err := peer.IDB58Decode(name) if err != nil { // name should be a multihash. if it isn't, error out here. log.Debugf("RoutingResolver: bad input hash: [%s]\n", name) return "", 0, err } - pid, err := peer.IDFromBytes(hash) - if err != nil { - log.Debugf("RoutingResolver: could not convert public key hash %s to peer ID: %s\n", name, err) - return "", 0, err - } - // Name should be the hash of a public key retrievable from ipfs. // We retrieve the public key here to make certain that it's in the peer // store before calling GetValue() on the DHT - the DHT will call the From a431982246605b43095d384e0b159973557e87e2 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 29 Aug 2018 17:33:48 -0700 Subject: [PATCH 2567/3817] fix bad rewrite This commit was moved from ipld/go-car@916320ff443cb8cf8ea6b3ad2709d93fa974265f --- ipld/car/car/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/car/car/main.go b/ipld/car/car/main.go index 877f692f1..97127ca7c 100644 --- a/ipld/car/car/main.go +++ b/ipld/car/car/main.go @@ -7,7 +7,7 @@ import ( "io" "os" - "gx/ipfs/QmU9oYpqJsNWwAAJju8CzE7mv4NHAJUDWhoKHqgnhMCBy5/go-car" + "github.com/ipfs/go-car" cli "github.com/urfave/cli" ) From 3f8b3c1e9456b22d73e787051648378368bdfd94 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 29 Aug 2018 18:30:46 -0700 Subject: [PATCH 2568/3817] namesys: fix debug message License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@d4bb442dc39bcec1a977b843d96bf86f602c74cc --- namesys/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/routing.go b/namesys/routing.go index 65da47a2f..4c1ce80b4 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -58,7 +58,7 @@ func (r *IpnsResolver) resolveOnce(ctx context.Context, name string, options *op pid, err := peer.IDB58Decode(name) if err != nil { // name should be a multihash. if it isn't, error out here. - log.Debugf("RoutingResolver: bad input hash: [%s]\n", name) + log.Debugf("RoutingResolver: IPNS address not a valid peer ID: [%s]\n", name) return "", 0, err } From d6a07980ee882f2088c5ba709c43096518147d64 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 30 Aug 2018 12:10:34 +0200 Subject: [PATCH 2569/3817] use more idiomatic append version This commit was moved from ipfs/go-merkledag@9e4c9b93701bf849f6cb947c188898267f1ee3b0 --- ipld/merkledag/node.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index c3e005d16..cc028537f 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -127,14 +127,12 @@ func (n *ProtoNode) AddRawLink(name string, l *ipld.Link) error { func (n *ProtoNode) RemoveNodeLink(name string) error { n.encoded = nil - ref := &n.links - filterPos := 0 + ref := n.links[:0] found := false - for i := 0; i < len(*ref); i++ { - if v := (*ref)[i]; v.Name != name { - (*ref)[filterPos] = v - filterPos++ + for _, v := range n.links { + if v.Name != name { + ref = append(ref, v) } else { found = true } @@ -144,7 +142,7 @@ func (n *ProtoNode) RemoveNodeLink(name string) error { return ipld.ErrNotFound } - n.links = (*ref)[:filterPos] + n.links = ref return nil } From 8202290be000a922507bd9b834bd0366b8d2263f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 30 Aug 2018 18:51:18 -0700 Subject: [PATCH 2570/3817] nit: make dagTruncate a method on DagModifier This commit was moved from ipfs/go-unixfs@9e50901d14e46535eea7b00f3868ed90130930a1 --- unixfs/mod/dagmodifier.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index f6e5f4820..0f03cb6d3 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -490,7 +490,7 @@ func (dm *DagModifier) Truncate(size int64) error { return dm.expandSparse(int64(size) - realSize) } - nnode, err := dagTruncate(dm.ctx, dm.curNode, uint64(size), dm.dagserv) + nnode, err := dm.dagTruncate(dm.ctx, dm.curNode, uint64(size)) if err != nil { return err } @@ -505,7 +505,7 @@ func (dm *DagModifier) Truncate(size int64) error { } // dagTruncate truncates the given node to 'size' and returns the modified Node -func dagTruncate(ctx context.Context, n ipld.Node, size uint64, ds ipld.DAGService) (ipld.Node, error) { +func (dm *DagModifier) dagTruncate(ctx context.Context, n ipld.Node, size uint64) (ipld.Node, error) { if len(n.Links()) == 0 { switch nd := n.(type) { case *mdag.ProtoNode: @@ -537,7 +537,7 @@ func dagTruncate(ctx context.Context, n ipld.Node, size uint64, ds ipld.DAGServi // with the new values of the truncated children. ndata.RemoveAllBlockSizes() for i, lnk := range nd.Links() { - child, err := lnk.GetNode(ctx, ds) + child, err := lnk.GetNode(ctx, dm.dagserv) if err != nil { return nil, err } @@ -549,7 +549,7 @@ func dagTruncate(ctx context.Context, n ipld.Node, size uint64, ds ipld.DAGServi // found the child we want to cut if size < cur+childsize { - nchild, err := dagTruncate(ctx, child, size-cur, ds) + nchild, err := dm.dagTruncate(ctx, child, size-cur) if err != nil { return nil, err } @@ -564,7 +564,7 @@ func dagTruncate(ctx context.Context, n ipld.Node, size uint64, ds ipld.DAGServi ndata.AddBlockSize(childsize) } - err = ds.Add(ctx, modified) + err = dm.dagserv.Add(ctx, modified) if err != nil { return nil, err } From 5e6e8bb19504af02a1539ea8258566d5b6d6fa7a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 29 Aug 2018 21:04:56 -0700 Subject: [PATCH 2571/3817] gx update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@f239caf86096752f443bb0a7100fc8a653ade4ed --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 6 +++--- namesys/namesys.go | 2 +- namesys/namesys_test.go | 6 +++--- namesys/proquint.go | 2 +- namesys/publisher.go | 4 ++-- namesys/publisher_test.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 4 ++-- namesys/routing.go | 4 ++-- 14 files changed, 22 insertions(+), 22 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index b88668dcc..0ba9fc130 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index 3d7fc51e6..dcbb0f966 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index d329875a1..6d454f70f 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,8 +8,8 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" + path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" ) type LookupTXTFunc func(name string) (txt []string, err error) diff --git a/namesys/interface.go b/namesys/interface.go index f2a2c868b..8602aa381 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index dd84308f2..79901c48b 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" @@ -17,8 +17,8 @@ import ( ropts "gx/ipfs/QmS4niovD1U6pRjUBXivr1zvvLBqiTKbERjFo994JU7oQS/go-libp2p-routing/options" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" - mockrouting "gx/ipfs/Qmd45r5jHr1PKMNQqifnbZy1ZQwHdtXUDJFamUEvUJE544/go-ipfs-routing/mock" - offline "gx/ipfs/Qmd45r5jHr1PKMNQqifnbZy1ZQwHdtXUDJFamUEvUJE544/go-ipfs-routing/offline" + mockrouting "gx/ipfs/QmZdn8S4FLTfDrmLZb7JoLkrRvTYnyuMWEG6ZGZ3YKwEiK/go-ipfs-routing/mock" + offline "gx/ipfs/QmZdn8S4FLTfDrmLZb7JoLkrRvTYnyuMWEG6ZGZ3YKwEiK/go-ipfs-routing/offline" record "gx/ipfs/QmdHb9aBELnQKTVhvvA3hsQbRgUAwsWUzBP2vZ6Y5FBYvE/go-libp2p-record" pstore "gx/ipfs/QmeKD8YT7887Xu6Z86iZmpYNxrLogJexqxEugSmaf14k64/go-libp2p-peerstore" ) diff --git a/namesys/namesys.go b/namesys/namesys.go index ac5faeaeb..eef05a198 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index f1cfa4d48..ab099f267 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,15 +7,15 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - "gx/ipfs/QmQjEpRiwVvtowhq69dAtB4jhioPVFXiCcWZm9Sfgn7eqc/go-unixfs" - path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" + "gx/ipfs/QmVNEJ5Vk1e2G5kHMiuVbpD6VQZiK1oS6aWZKjcUQW7hEy/go-unixfs" ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" - offroute "gx/ipfs/Qmd45r5jHr1PKMNQqifnbZy1ZQwHdtXUDJFamUEvUJE544/go-ipfs-routing/offline" + offroute "gx/ipfs/QmZdn8S4FLTfDrmLZb7JoLkrRvTYnyuMWEG6ZGZ3YKwEiK/go-ipfs-routing/offline" pstore "gx/ipfs/QmeKD8YT7887Xu6Z86iZmpYNxrLogJexqxEugSmaf14k64/go-libp2p-peerstore" ) diff --git a/namesys/proquint.go b/namesys/proquint.go index d869ce01e..ba57915cc 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,8 +7,8 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" + path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" - path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index 8a4c4002a..9acab7390 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,8 +7,8 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmQjEpRiwVvtowhq69dAtB4jhioPVFXiCcWZm9Sfgn7eqc/go-unixfs" - path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" + ft "gx/ipfs/QmVNEJ5Vk1e2G5kHMiuVbpD6VQZiK1oS6aWZKjcUQW7hEy/go-unixfs" ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" pb "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns/pb" diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 5b33940f3..44009dda5 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -13,8 +13,8 @@ import ( ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" + mockrouting "gx/ipfs/QmZdn8S4FLTfDrmLZb7JoLkrRvTYnyuMWEG6ZGZ3YKwEiK/go-ipfs-routing/mock" dshelp "gx/ipfs/Qmd39D2vUhmPKQA2fgykjo2JXwekHKeJUggmGRpYuVMA2Z/go-ipfs-ds-help" - mockrouting "gx/ipfs/Qmd45r5jHr1PKMNQqifnbZy1ZQwHdtXUDJFamUEvUJE544/go-ipfs-routing/mock" ) type identity struct { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 90fab3f23..2196e989b 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" pb "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns/pb" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 387db7f90..e35ba4028 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" - mocknet "gx/ipfs/QmQiaskfWpdRJ4x2spEQjPFTUkEB87KDYu91qnNYBqvvcX/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" pstore "gx/ipfs/QmeKD8YT7887Xu6Z86iZmpYNxrLogJexqxEugSmaf14k64/go-libp2p-peerstore" + mocknet "gx/ipfs/Qmf1u2efhjXYtuyP8SMHYtw4dCkbghnniex2PSp7baA7FP/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index b2a4e23ac..90d22d48e 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,14 +6,14 @@ import ( "testing" "time" - path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" - mockrouting "gx/ipfs/Qmd45r5jHr1PKMNQqifnbZy1ZQwHdtXUDJFamUEvUJE544/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmZdn8S4FLTfDrmLZb7JoLkrRvTYnyuMWEG6ZGZ3YKwEiK/go-ipfs-routing/mock" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index c533daada..e0817517d 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,15 +6,15 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" + dht "gx/ipfs/QmNesMxTot4Spt6qZkT45DWMSniPJgUfc4BprhbCpPi6Qk/go-libp2p-kad-dht" ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" pb "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns/pb" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" routing "gx/ipfs/QmS4niovD1U6pRjUBXivr1zvvLBqiTKbERjFo994JU7oQS/go-libp2p-routing" - dht "gx/ipfs/QmTRj8mj6X5LtjVochPPSNX6MTbJ6iVojcfakWJKG13re7/go-libp2p-kad-dht" cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) From 15e2043dc4f58e44d5676386753c2b8ec5be5229 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 29 Aug 2018 21:04:56 -0700 Subject: [PATCH 2572/3817] gx update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@db3f7c2c47e384fabdc30a1b9141b92f56375f5d --- coreiface/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/path.go b/coreiface/path.go index 25b09f486..7873fe2bf 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path" + ipfspath "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" ) From b0fc9ff1437db91a331d17186261d6aa5c04f656 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 29 Aug 2018 21:04:56 -0700 Subject: [PATCH 2573/3817] gx update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@39e67f1dcbfeb90f0cbcbba74e5ae24f2ef30b08 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 30fe9de94..672b6a556 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmRiQCJZ91B7VNmLvA6sxzDuBJGSojS3uXHHVuNr3iueNZ/go-merkledag" - bserv "gx/ipfs/QmbSB9Uh3wVgmiCb1fAb8zuC3qAE6un4kd1jvatUurfAmB/go-blockservice" + dag "gx/ipfs/QmRDaC5z6yXkXTTSWzaxs2sSVBon5RRCN6eNtMmpuHtKCr/go-merkledag" + bserv "gx/ipfs/QmdHqV7L4bpmMtEXVCrgn8RN6CXqMr3aUeogSkXbJGRtwk/go-blockservice" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" dstore "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 3a36946c7..11a61292d 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmRiQCJZ91B7VNmLvA6sxzDuBJGSojS3uXHHVuNr3iueNZ/go-merkledag" + mdag "gx/ipfs/QmRDaC5z6yXkXTTSWzaxs2sSVBon5RRCN6eNtMmpuHtKCr/go-merkledag" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 590a2e069..64fd44a0a 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - mdag "gx/ipfs/QmRiQCJZ91B7VNmLvA6sxzDuBJGSojS3uXHHVuNr3iueNZ/go-merkledag" - bs "gx/ipfs/QmbSB9Uh3wVgmiCb1fAb8zuC3qAE6un4kd1jvatUurfAmB/go-blockservice" + mdag "gx/ipfs/QmRDaC5z6yXkXTTSWzaxs2sSVBon5RRCN6eNtMmpuHtKCr/go-merkledag" + bs "gx/ipfs/QmdHqV7L4bpmMtEXVCrgn8RN6CXqMr3aUeogSkXbJGRtwk/go-blockservice" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 27c5a9271..5b8889ee5 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmRiQCJZ91B7VNmLvA6sxzDuBJGSojS3uXHHVuNr3iueNZ/go-merkledag" + "gx/ipfs/QmRDaC5z6yXkXTTSWzaxs2sSVBon5RRCN6eNtMmpuHtKCr/go-merkledag" ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index cc0bfcb4a..69724f4cd 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmRiQCJZ91B7VNmLvA6sxzDuBJGSojS3uXHHVuNr3iueNZ/go-merkledag" - bserv "gx/ipfs/QmbSB9Uh3wVgmiCb1fAb8zuC3qAE6un4kd1jvatUurfAmB/go-blockservice" + dag "gx/ipfs/QmRDaC5z6yXkXTTSWzaxs2sSVBon5RRCN6eNtMmpuHtKCr/go-merkledag" + bserv "gx/ipfs/QmdHqV7L4bpmMtEXVCrgn8RN6CXqMr3aUeogSkXbJGRtwk/go-blockservice" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" From 7bd1b46b9b33be615a21089f05819c527fc2ee86 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 29 Aug 2018 21:04:56 -0700 Subject: [PATCH 2574/3817] gx update deps License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@62ab85e8674135e44ef0a2f7caf8afc67228d9cf --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index e7c362f49..0ae740982 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmRiQCJZ91B7VNmLvA6sxzDuBJGSojS3uXHHVuNr3iueNZ/go-merkledag" + dag "gx/ipfs/QmRDaC5z6yXkXTTSWzaxs2sSVBon5RRCN6eNtMmpuHtKCr/go-merkledag" ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" posinfo "gx/ipfs/QmXD4grfThQ4LwVoEEfe4dgR7ukmbV9TppM5Q4SPowp7hU/go-ipfs-posinfo" From c195c52ee9be357b800089917e0189b608f7883c Mon Sep 17 00:00:00 2001 From: Kejie Zhang Date: Fri, 7 Sep 2018 13:49:21 +0800 Subject: [PATCH 2575/3817] return error if rabin min is less than 16 This commit was moved from ipfs/go-ipfs-chunker@f4ac7aefc4551ca96e5aff464c4a3ff3a1f84801 --- chunker/parse.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/chunker/parse.go b/chunker/parse.go index 7d511c217..6c725bf92 100644 --- a/chunker/parse.go +++ b/chunker/parse.go @@ -49,13 +49,15 @@ func parseRabinString(r io.Reader, chunker string) (Splitter, error) { return nil, errors.New("first label must be min") } min, err := strconv.Atoi(sub[len(sub)-1]) - if err != nil { + if err != nil || min < 16{ return nil, err } - + if min < 16 { + return nil,errors.New("the rabin min should not less not 16") + } sub = strings.Split(parts[2], ":") if len(sub) > 1 && sub[0] != "avg" { - log.Error("sub == ", sub) + //log.Error("sub == ", sub) return nil, errors.New("second label must be avg") } avg, err := strconv.Atoi(sub[len(sub)-1]) From b2593d444d46abe87ebbc7070a4caf7ed91f93f6 Mon Sep 17 00:00:00 2001 From: Kejie Zhang Date: Fri, 7 Sep 2018 14:15:54 +0800 Subject: [PATCH 2576/3817] add parse test in chunkers This commit was moved from ipfs/go-ipfs-chunker@56321ac98c7264d429a6d06ff2e68f4cf9c6190c --- chunker/parse.go | 8 +++++--- chunker/parse_test.go | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 chunker/parse_test.go diff --git a/chunker/parse.go b/chunker/parse.go index 6c725bf92..2adf64c7a 100644 --- a/chunker/parse.go +++ b/chunker/parse.go @@ -8,6 +8,8 @@ import ( "strings" ) +var ErrRabinMin = errors.New("the rabin min should not less not 16") + // FromString returns a Splitter depending on the given string: // it supports "default" (""), "size-{size}", "rabin", "rabin-{blocksize}" and // "rabin-{min}-{avg}-{max}". @@ -49,15 +51,15 @@ func parseRabinString(r io.Reader, chunker string) (Splitter, error) { return nil, errors.New("first label must be min") } min, err := strconv.Atoi(sub[len(sub)-1]) - if err != nil || min < 16{ + if err != nil { return nil, err } if min < 16 { - return nil,errors.New("the rabin min should not less not 16") + return nil,ErrRabinMin } sub = strings.Split(parts[2], ":") if len(sub) > 1 && sub[0] != "avg" { - //log.Error("sub == ", sub) + log.Error("sub == ", sub) return nil, errors.New("second label must be avg") } avg, err := strconv.Atoi(sub[len(sub)-1]) diff --git a/chunker/parse_test.go b/chunker/parse_test.go new file mode 100644 index 000000000..ab2cb1e78 --- /dev/null +++ b/chunker/parse_test.go @@ -0,0 +1,21 @@ +package chunk + +import ( + "testing" + "bytes" +) + +func TestParse(t *testing.T) { + max := 1000 + r := bytes.NewReader(randBuf(t, max)) + chk1 := "rabin-18-25-32" + chk2 := "rabin-15-23-31" + _, err := parseRabinString(r, chk1) + if err != nil { + t.Errorf(err.Error()) + } + _, err = parseRabinString(r, chk2) + if err == nil || err.Error() != ErrRabinMin.Error() { + t.Errorf("it should be a ErrRabinMin here.") + } +} \ No newline at end of file From e7910f1e715ee9f85f95f3b2a583fdade9c15f77 Mon Sep 17 00:00:00 2001 From: Kejie Zhang Date: Fri, 7 Sep 2018 18:03:09 +0800 Subject: [PATCH 2577/3817] update test and fmt code This commit was moved from ipfs/go-ipfs-chunker@0bee44abde34bb7f6b58e1a66bfe5e3aab8bc740 --- chunker/parse.go | 2 +- chunker/parse_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/chunker/parse.go b/chunker/parse.go index 2adf64c7a..d69da1681 100644 --- a/chunker/parse.go +++ b/chunker/parse.go @@ -55,7 +55,7 @@ func parseRabinString(r io.Reader, chunker string) (Splitter, error) { return nil, err } if min < 16 { - return nil,ErrRabinMin + return nil, ErrRabinMin } sub = strings.Split(parts[2], ":") if len(sub) > 1 && sub[0] != "avg" { diff --git a/chunker/parse_test.go b/chunker/parse_test.go index ab2cb1e78..4dd8afc2d 100644 --- a/chunker/parse_test.go +++ b/chunker/parse_test.go @@ -1,8 +1,8 @@ package chunk import ( - "testing" "bytes" + "testing" ) func TestParse(t *testing.T) { @@ -15,7 +15,7 @@ func TestParse(t *testing.T) { t.Errorf(err.Error()) } _, err = parseRabinString(r, chk2) - if err == nil || err.Error() != ErrRabinMin.Error() { - t.Errorf("it should be a ErrRabinMin here.") + if err == ErrRabinMin { + t.Log("it should be ErrRabinMin here.") } -} \ No newline at end of file +} From 42685aa018f60874753a0a9148a0e7e03c01d03b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 7 Sep 2018 23:05:23 +0000 Subject: [PATCH 2578/3817] improve error message and fix grammar In general, it's easier for people to parse positive statements. This commit was moved from ipfs/go-ipfs-chunker@b18fd483cd016514a93cbb5662b0c6f1a6fecfe2 --- chunker/parse.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chunker/parse.go b/chunker/parse.go index d69da1681..d7764d7b4 100644 --- a/chunker/parse.go +++ b/chunker/parse.go @@ -8,7 +8,7 @@ import ( "strings" ) -var ErrRabinMin = errors.New("the rabin min should not less not 16") +var ErrRabinMin = errors.New("rabin min must be greater than 16") // FromString returns a Splitter depending on the given string: // it supports "default" (""), "size-{size}", "rabin", "rabin-{blocksize}" and From 093d964389cd95c2b48e5f1dd9864654725817f7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 7 Sep 2018 22:17:09 -0700 Subject: [PATCH 2579/3817] gx: update go-peerstore This commit was moved from ipfs/go-ipns@caf9f8b789d8aa348eb9b6e7d47f26f14b5a1b0b --- ipns/validate_test.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ipns/validate_test.go b/ipns/validate_test.go index 634636844..0ef9d00c5 100644 --- a/ipns/validate_test.go +++ b/ipns/validate_test.go @@ -14,6 +14,7 @@ import ( ci "github.com/libp2p/go-libp2p-crypto" peer "github.com/libp2p/go-libp2p-peer" pstore "github.com/libp2p/go-libp2p-peerstore" + pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem" ) func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, key string, val []byte, eol time.Time, exp error) { @@ -62,9 +63,9 @@ func TestValidator(t *testing.T) { priv, id, _ := genKeys(t) priv2, id2, _ := genKeys(t) - kbook := pstore.NewPeerstore() + kbook := pstoremem.NewPeerstore() kbook.AddPubKey(id, priv.GetPublic()) - emptyKbook := pstore.NewPeerstore() + emptyKbook := pstoremem.NewPeerstore() testValidatorCase(t, priv, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour), nil) testValidatorCase(t, priv, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour*-1), ErrExpiredRecord) @@ -88,7 +89,7 @@ func mustMarshal(t *testing.T, entry *pb.IpnsEntry) []byte { func TestEmbeddedPubKeyValidate(t *testing.T) { goodeol := time.Now().Add(time.Hour) - kbook := pstore.NewPeerstore() + kbook := pstoremem.NewPeerstore() pth := []byte("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") @@ -128,7 +129,7 @@ func TestEmbeddedPubKeyValidate(t *testing.T) { func TestPeerIDPubKeyValidate(t *testing.T) { goodeol := time.Now().Add(time.Hour) - kbook := pstore.NewPeerstore() + kbook := pstoremem.NewPeerstore() pth := []byte("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") From a7890b7bc7a84c7ef18ea7a979928432ef6ee800 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 7 Sep 2018 23:40:08 -0700 Subject: [PATCH 2580/3817] gx: update peerstore Also: * Updates go-floodsub to fix a data race. * Updates golang-lru License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@745b4d2660cde7d5f54b86035bbaebf6b710561f --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 18 +++++++++--------- namesys/namesys.go | 8 ++++---- namesys/namesys_test.go | 14 +++++++------- namesys/proquint.go | 2 +- namesys/publisher.go | 14 +++++++------- namesys/publisher_test.go | 10 +++++----- namesys/republisher/repub.go | 6 +++--- namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 10 +++++----- namesys/routing.go | 10 +++++----- 14 files changed, 53 insertions(+), 53 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 0ba9fc130..0047b434e 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" + path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index dcbb0f966..451521be4 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" + path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 6d454f70f..4961c72d9 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" + path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index 8602aa381..a638b5f81 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" + path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 79901c48b..bfa3cdc8d 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,21 +6,21 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" + path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" - ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" - routing "gx/ipfs/QmS4niovD1U6pRjUBXivr1zvvLBqiTKbERjFo994JU7oQS/go-libp2p-routing" - ropts "gx/ipfs/QmS4niovD1U6pRjUBXivr1zvvLBqiTKbERjFo994JU7oQS/go-libp2p-routing/options" - ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" - mockrouting "gx/ipfs/QmZdn8S4FLTfDrmLZb7JoLkrRvTYnyuMWEG6ZGZ3YKwEiK/go-ipfs-routing/mock" - offline "gx/ipfs/QmZdn8S4FLTfDrmLZb7JoLkrRvTYnyuMWEG6ZGZ3YKwEiK/go-ipfs-routing/offline" + mockrouting "gx/ipfs/QmRuUsZEg2WLCuidJGHVmE1NreHDmXWKLS466PKyDpXMhN/go-ipfs-routing/mock" + offline "gx/ipfs/QmRuUsZEg2WLCuidJGHVmE1NreHDmXWKLS466PKyDpXMhN/go-ipfs-routing/offline" + ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" + dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" + routing "gx/ipfs/QmY9JUvS8kbgao3XbPh6WAV3ChE2nxGKhcGTHiwMC4gmcU/go-libp2p-routing" + ropts "gx/ipfs/QmY9JUvS8kbgao3XbPh6WAV3ChE2nxGKhcGTHiwMC4gmcU/go-libp2p-routing/options" + ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" record "gx/ipfs/QmdHb9aBELnQKTVhvvA3hsQbRgUAwsWUzBP2vZ6Y5FBYvE/go-libp2p-record" - pstore "gx/ipfs/QmeKD8YT7887Xu6Z86iZmpYNxrLogJexqxEugSmaf14k64/go-libp2p-peerstore" + pstore "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index eef05a198..9b8e6bff6 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,14 +6,14 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" + path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - routing "gx/ipfs/QmS4niovD1U6pRjUBXivr1zvvLBqiTKbERjFo994JU7oQS/go-libp2p-routing" - ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru" + ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" + routing "gx/ipfs/QmY9JUvS8kbgao3XbPh6WAV3ChE2nxGKhcGTHiwMC4gmcU/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index ab099f267..a4011795b 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,16 +7,16 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" - "gx/ipfs/QmVNEJ5Vk1e2G5kHMiuVbpD6VQZiK1oS6aWZKjcUQW7hEy/go-unixfs" + path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" + "gx/ipfs/QmWAfTyD6KEBm7bzqNRBPvqKrZCDtn5PGbs9V1DKfnVK59/go-unixfs" - ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" - offroute "gx/ipfs/QmZdn8S4FLTfDrmLZb7JoLkrRvTYnyuMWEG6ZGZ3YKwEiK/go-ipfs-routing/offline" - pstore "gx/ipfs/QmeKD8YT7887Xu6Z86iZmpYNxrLogJexqxEugSmaf14k64/go-libp2p-peerstore" + offroute "gx/ipfs/QmRuUsZEg2WLCuidJGHVmE1NreHDmXWKLS466PKyDpXMhN/go-ipfs-routing/offline" + ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" + dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" + ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" + pstore "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index ba57915cc..2778590e5 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" + path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 9acab7390..79be29e0d 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,16 +7,16 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" - ft "gx/ipfs/QmVNEJ5Vk1e2G5kHMiuVbpD6VQZiK1oS6aWZKjcUQW7hEy/go-unixfs" + path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" + ft "gx/ipfs/QmWAfTyD6KEBm7bzqNRBPvqKrZCDtn5PGbs9V1DKfnVK59/go-unixfs" - ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" - pb "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns/pb" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - routing "gx/ipfs/QmS4niovD1U6pRjUBXivr1zvvLBqiTKbERjFo994JU7oQS/go-libp2p-routing" - ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - dsquery "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" + ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" + dsquery "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" + routing "gx/ipfs/QmY9JUvS8kbgao3XbPh6WAV3ChE2nxGKhcGTHiwMC4gmcU/go-libp2p-routing" + ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" + pb "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns/pb" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 44009dda5..5df344af9 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -6,15 +6,15 @@ import ( "testing" "time" - ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" - ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" + mockrouting "gx/ipfs/QmRuUsZEg2WLCuidJGHVmE1NreHDmXWKLS466PKyDpXMhN/go-ipfs-routing/mock" + ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" + dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" - mockrouting "gx/ipfs/QmZdn8S4FLTfDrmLZb7JoLkrRvTYnyuMWEG6ZGZ3YKwEiK/go-ipfs-routing/mock" - dshelp "gx/ipfs/Qmd39D2vUhmPKQA2fgykjo2JXwekHKeJUggmGRpYuVMA2Z/go-ipfs-ds-help" + ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" + dshelp "gx/ipfs/Qmf1xGr3SyBpiPp3ZDuKkYMh4gnRk9K4QnbL17kstnf35h/go-ipfs-ds-help" ) type identity struct { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 2196e989b..29a5fa745 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,15 +7,15 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" + path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" - pb "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns/pb" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" + ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" + pb "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns/pb" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index e35ba4028..be62d10cb 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" + path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - pstore "gx/ipfs/QmeKD8YT7887Xu6Z86iZmpYNxrLogJexqxEugSmaf14k64/go-libp2p-peerstore" - mocknet "gx/ipfs/Qmf1u2efhjXYtuyP8SMHYtw4dCkbghnniex2PSp7baA7FP/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmUEqyXr97aUbNmQADHYNknjwjjdVpJXEt1UZXmSG81EV4/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 90d22d48e..b86598827 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,14 +6,14 @@ import ( "testing" "time" - path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" + path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" - ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" - ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" - mockrouting "gx/ipfs/QmZdn8S4FLTfDrmLZb7JoLkrRvTYnyuMWEG6ZGZ3YKwEiK/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmRuUsZEg2WLCuidJGHVmE1NreHDmXWKLS466PKyDpXMhN/go-ipfs-routing/mock" + ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" + dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" + ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index f759546d2..493657281 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,16 +6,16 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" + path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" - dht "gx/ipfs/QmNesMxTot4Spt6qZkT45DWMSniPJgUfc4BprhbCpPi6Qk/go-libp2p-kad-dht" - ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns" - pb "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns/pb" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" + dht "gx/ipfs/QmRNxiPpZf3skMAtmDJpgHuW9uj1ukqV1zjANj9d6bmHfE/go-libp2p-kad-dht" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - routing "gx/ipfs/QmS4niovD1U6pRjUBXivr1zvvLBqiTKbERjFo994JU7oQS/go-libp2p-routing" + routing "gx/ipfs/QmY9JUvS8kbgao3XbPh6WAV3ChE2nxGKhcGTHiwMC4gmcU/go-libp2p-routing" cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" + ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" + pb "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns/pb" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) From 55f24526833cddd68b49a7858a48d8019b00a617 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 7 Sep 2018 23:40:08 -0700 Subject: [PATCH 2581/3817] gx: update peerstore Also: * Updates go-floodsub to fix a data race. * Updates golang-lru License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@618aaa45543916248c209032a2e0309793666dd3 --- coreiface/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/path.go b/coreiface/path.go index 7873fe2bf..c2b4cd869 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmTKaiDxQqVxmA1bRipSuP7hnTSgnMSmEa98NYeS6fcoiv/go-path" + ipfspath "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" ) From 4b07f99c07d30b9537c6f6a9bec6df489e318a56 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 7 Sep 2018 23:40:08 -0700 Subject: [PATCH 2582/3817] gx: update peerstore Also: * Updates go-floodsub to fix a data race. * Updates golang-lru License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@1002faaa0560848a359837619cc76a4bcff0341f --- pinning/pinner/gc/gc.go | 10 +++++----- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 12 ++++++------ pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 12 ++++++------ 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 672b6a556..9628f7ad7 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,16 +8,16 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmRDaC5z6yXkXTTSWzaxs2sSVBon5RRCN6eNtMmpuHtKCr/go-merkledag" - bserv "gx/ipfs/QmdHqV7L4bpmMtEXVCrgn8RN6CXqMr3aUeogSkXbJGRtwk/go-blockservice" + dag "gx/ipfs/QmNr4E8z9bGTztvHJktp7uQaMdx9p3r9Asrq6eYk7iCh4a/go-merkledag" + bserv "gx/ipfs/QmQLG22wSEStiociTSKQpZAuuaaWoF1B3iKyjPFvWiTQ77/go-blockservice" + offline "gx/ipfs/QmPuLWvxK1vg6ckKUpT53Dow9VLCcQGdL5Trwxa8PTLp7r/go-ipfs-exchange-offline" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - dstore "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" + dstore "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" "gx/ipfs/QmVUhfewLZpSaAiBYCpw2krYMaiVmFuhr2iurQLuRoU6sD/go-verifcid" ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" - offline "gx/ipfs/QmZxjqR9Qgompju73kakSoUj3rbVndAzky3oCDiBNCxPs1/go-ipfs-exchange-offline" - bstore "gx/ipfs/QmcmpX42gtDv1fz24kau4wjS9hfwWj5VexWBKgGnWzsyag/go-ipfs-blockstore" + bstore "gx/ipfs/Qmeg56ecxRnVv7VWViMrDeEMoBHaNFMs4vQnyQrJ79Zz7i/go-ipfs-blockstore" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 11a61292d..cff9e4ae5 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,10 +10,10 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmRDaC5z6yXkXTTSWzaxs2sSVBon5RRCN6eNtMmpuHtKCr/go-merkledag" + mdag "gx/ipfs/QmNr4E8z9bGTztvHJktp7uQaMdx9p3r9Asrq6eYk7iCh4a/go-merkledag" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" + ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 64fd44a0a..4dc5e3565 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" - mdag "gx/ipfs/QmRDaC5z6yXkXTTSWzaxs2sSVBon5RRCN6eNtMmpuHtKCr/go-merkledag" - bs "gx/ipfs/QmdHqV7L4bpmMtEXVCrgn8RN6CXqMr3aUeogSkXbJGRtwk/go-blockservice" + mdag "gx/ipfs/QmNr4E8z9bGTztvHJktp7uQaMdx9p3r9Asrq6eYk7iCh4a/go-merkledag" + bs "gx/ipfs/QmQLG22wSEStiociTSKQpZAuuaaWoF1B3iKyjPFvWiTQ77/go-blockservice" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - dssync "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/sync" + offline "gx/ipfs/QmPuLWvxK1vg6ckKUpT53Dow9VLCcQGdL5Trwxa8PTLp7r/go-ipfs-exchange-offline" + ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" + dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" - offline "gx/ipfs/QmZxjqR9Qgompju73kakSoUj3rbVndAzky3oCDiBNCxPs1/go-ipfs-exchange-offline" - blockstore "gx/ipfs/QmcmpX42gtDv1fz24kau4wjS9hfwWj5VexWBKgGnWzsyag/go-ipfs-blockstore" + blockstore "gx/ipfs/Qmeg56ecxRnVv7VWViMrDeEMoBHaNFMs4vQnyQrJ79Zz7i/go-ipfs-blockstore" ) var rand = util.NewTimeSeededRand() diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 5b8889ee5..f0853d53a 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmRDaC5z6yXkXTTSWzaxs2sSVBon5RRCN6eNtMmpuHtKCr/go-merkledag" + "gx/ipfs/QmNr4E8z9bGTztvHJktp7uQaMdx9p3r9Asrq6eYk7iCh4a/go-merkledag" ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 69724f4cd..e98025ed1 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,14 +5,14 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmRDaC5z6yXkXTTSWzaxs2sSVBon5RRCN6eNtMmpuHtKCr/go-merkledag" - bserv "gx/ipfs/QmdHqV7L4bpmMtEXVCrgn8RN6CXqMr3aUeogSkXbJGRtwk/go-blockservice" + dag "gx/ipfs/QmNr4E8z9bGTztvHJktp7uQaMdx9p3r9Asrq6eYk7iCh4a/go-merkledag" + bserv "gx/ipfs/QmQLG22wSEStiociTSKQpZAuuaaWoF1B3iKyjPFvWiTQ77/go-blockservice" - ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" + offline "gx/ipfs/QmPuLWvxK1vg6ckKUpT53Dow9VLCcQGdL5Trwxa8PTLp7r/go-ipfs-exchange-offline" + ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" + dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" - offline "gx/ipfs/QmZxjqR9Qgompju73kakSoUj3rbVndAzky3oCDiBNCxPs1/go-ipfs-exchange-offline" - blockstore "gx/ipfs/QmcmpX42gtDv1fz24kau4wjS9hfwWj5VexWBKgGnWzsyag/go-ipfs-blockstore" + blockstore "gx/ipfs/Qmeg56ecxRnVv7VWViMrDeEMoBHaNFMs4vQnyQrJ79Zz7i/go-ipfs-blockstore" ) func ignoreCids(_ *cid.Cid) {} From a5268fe78463e5cb1ef9febe0638d36d81fd72ef Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 7 Sep 2018 23:40:08 -0700 Subject: [PATCH 2583/3817] gx: update peerstore Also: * Updates go-floodsub to fix a data race. * Updates golang-lru License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@3e3f82a754830ada6b84e5740c7ebf91c765e989 --- filestore/filestore.go | 4 ++-- filestore/filestore_test.go | 6 +++--- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index dfbcf9ed6..a24839d2e 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -12,11 +12,11 @@ import ( "errors" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" + dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" blocks "gx/ipfs/QmWAzSEoqZ6xU6pu8yL8e5WaMb7wtbfbhhN4p1DknUPtr3/go-block-format" posinfo "gx/ipfs/QmXD4grfThQ4LwVoEEfe4dgR7ukmbV9TppM5Q4SPowp7hU/go-ipfs-posinfo" cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" - blockstore "gx/ipfs/QmcmpX42gtDv1fz24kau4wjS9hfwWj5VexWBKgGnWzsyag/go-ipfs-blockstore" + blockstore "gx/ipfs/Qmeg56ecxRnVv7VWViMrDeEMoBHaNFMs4vQnyQrJ79Zz7i/go-ipfs-blockstore" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 0ae740982..83eeb69b9 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,12 +7,12 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmRDaC5z6yXkXTTSWzaxs2sSVBon5RRCN6eNtMmpuHtKCr/go-merkledag" + dag "gx/ipfs/QmNr4E8z9bGTztvHJktp7uQaMdx9p3r9Asrq6eYk7iCh4a/go-merkledag" - ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" + ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" posinfo "gx/ipfs/QmXD4grfThQ4LwVoEEfe4dgR7ukmbV9TppM5Q4SPowp7hU/go-ipfs-posinfo" cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" - blockstore "gx/ipfs/QmcmpX42gtDv1fz24kau4wjS9hfwWj5VexWBKgGnWzsyag/go-ipfs-blockstore" + blockstore "gx/ipfs/Qmeg56ecxRnVv7VWViMrDeEMoBHaNFMs4vQnyQrJ79Zz7i/go-ipfs-blockstore" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 265874be0..87ceda15d 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,15 +10,15 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - dsns "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/namespace" - dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" + ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" + dsns "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/namespace" + dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" blocks "gx/ipfs/QmWAzSEoqZ6xU6pu8yL8e5WaMb7wtbfbhhN4p1DknUPtr3/go-block-format" posinfo "gx/ipfs/QmXD4grfThQ4LwVoEEfe4dgR7ukmbV9TppM5Q4SPowp7hU/go-ipfs-posinfo" cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" - blockstore "gx/ipfs/QmcmpX42gtDv1fz24kau4wjS9hfwWj5VexWBKgGnWzsyag/go-ipfs-blockstore" - dshelp "gx/ipfs/Qmd39D2vUhmPKQA2fgykjo2JXwekHKeJUggmGRpYuVMA2Z/go-ipfs-ds-help" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" + blockstore "gx/ipfs/Qmeg56ecxRnVv7VWViMrDeEMoBHaNFMs4vQnyQrJ79Zz7i/go-ipfs-blockstore" + dshelp "gx/ipfs/Qmf1xGr3SyBpiPp3ZDuKkYMh4gnRk9K4QnbL17kstnf35h/go-ipfs-ds-help" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index fa81127d8..ea7f06ff0 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,11 +6,11 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - ds "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore" - dsq "gx/ipfs/QmVG5gxteQNEMhrS8prJSmU2C9rebtFuTd3SYZ5kE3YZ5k/go-datastore/query" + ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" + dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" - blockstore "gx/ipfs/QmcmpX42gtDv1fz24kau4wjS9hfwWj5VexWBKgGnWzsyag/go-ipfs-blockstore" - dshelp "gx/ipfs/Qmd39D2vUhmPKQA2fgykjo2JXwekHKeJUggmGRpYuVMA2Z/go-ipfs-ds-help" + blockstore "gx/ipfs/Qmeg56ecxRnVv7VWViMrDeEMoBHaNFMs4vQnyQrJ79Zz7i/go-ipfs-blockstore" + dshelp "gx/ipfs/Qmf1xGr3SyBpiPp3ZDuKkYMh4gnRk9K4QnbL17kstnf35h/go-ipfs-ds-help" ) // Status is used to identify the state of the block data referenced From 7e2429da1b34da9ae3a4607b1960fb5cb7b28e2c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 7 Sep 2018 23:46:12 -0700 Subject: [PATCH 2584/3817] fix peerstore constructor calls License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@984e3c40bc6785ec67455512634a941ec8f69c35 --- namesys/ipns_resolver_validation_test.go | 3 ++- namesys/namesys_test.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index bfa3cdc8d..c5eda1737 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -21,13 +21,14 @@ import ( ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" record "gx/ipfs/QmdHb9aBELnQKTVhvvA3hsQbRgUAwsWUzBP2vZ6Y5FBYvE/go-libp2p-record" pstore "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore" + pstoremem "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore/pstoremem" ) func TestResolverValidation(t *testing.T) { ctx := context.Background() rid := testutil.RandIdentityOrFatal(t) dstore := dssync.MutexWrap(ds.NewMapDatastore()) - peerstore := pstore.NewPeerstore() + peerstore := pstoremem.NewPeerstore() vstore := newMockValueStore(rid, dstore, peerstore) resolver := NewIpnsResolver(vstore) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index a4011795b..cc213c9c5 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -16,7 +16,7 @@ import ( ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" - pstore "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore" + pstoremem "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore/pstoremem" ) type mockResolver struct { @@ -86,7 +86,7 @@ func TestPublishWithCache0(t *testing.T) { if err != nil { t.Fatal(err) } - ps := pstore.NewPeerstore() + ps := pstoremem.NewPeerstore() pid, err := peer.IDFromPrivateKey(priv) if err != nil { t.Fatal(err) From 6c40247cc1931a891782c579d48f517a7945569b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 19:07:50 +0100 Subject: [PATCH 2585/3817] coreapi: dht interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@bbd736fdecec3e18d61e72b5a4beb9234f66c1ec --- coreiface/dht.go | 28 ++++++++++++++++++++++++++++ coreiface/options/dht.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 coreiface/dht.go create mode 100644 coreiface/options/dht.go diff --git a/coreiface/dht.go b/coreiface/dht.go new file mode 100644 index 000000000..1c8e68bd1 --- /dev/null +++ b/coreiface/dht.go @@ -0,0 +1,28 @@ +package iface + +import ( + "context" + + options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + + ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" +) + +// DhtAPI specifies the interface to the DHT +type DhtAPI interface { + // FindPeer queries the DHT for all of the multiaddresses associated with a + // Peer ID + FindPeer(context.Context, peer.ID) (<-chan ma.Multiaddr, error) + + // FindProviders finds peers in the DHT who can provide a specific value + // given a key. + FindProviders(context.Context, Path) (<-chan peer.ID, error) //TODO: is path the right choice here? + + // Provide announces to the network that you are providing given values + Provide(context.Context, Path, ...options.DhtProvideOption) error + + // WithRecursive is an option for Provide which specifies whether to provide + // the given path recursively + WithRecursive(recursive bool) options.DhtProvideOption +} diff --git a/coreiface/options/dht.go b/coreiface/options/dht.go new file mode 100644 index 000000000..92fd14f4a --- /dev/null +++ b/coreiface/options/dht.go @@ -0,0 +1,30 @@ +package options + +type DhtProvideSettings struct { + Recursive bool +} + +type DhtProvideOption func(*DhtProvideSettings) error + +func DhtProvideOptions(opts ...DhtProvideOption) (*DhtProvideSettings, error) { + options := &DhtProvideSettings{ + Recursive: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type DhtOptions struct{} + +func (api *DhtOptions) WithRecursive(recursive bool) DhtProvideOption { + return func(settings *DhtProvideSettings) error { + settings.Recursive = recursive + return nil + } +} From 1ca0f8b291354c50b54f51c7afd42bafcee8a0a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 19:12:54 +0100 Subject: [PATCH 2586/3817] coreapi: implement dht api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@db9865b9ee918e7db54e6cffa41a0e8950b3e170 --- coreiface/coreapi.go | 3 +++ coreiface/dht.go | 6 +++++- coreiface/options/dht.go | 26 ++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index 696eefbaf..9811b75be 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -31,6 +31,9 @@ type CoreAPI interface { // ObjectAPI returns an implementation of Object API Object() ObjectAPI + // Dht returns an implementation of Dht API + Dht() DhtAPI + // ResolvePath resolves the path using Unixfs resolver ResolvePath(context.Context, Path) (ResolvedPath, error) diff --git a/coreiface/dht.go b/coreiface/dht.go index 1c8e68bd1..ce8509e01 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -17,7 +17,11 @@ type DhtAPI interface { // FindProviders finds peers in the DHT who can provide a specific value // given a key. - FindProviders(context.Context, Path) (<-chan peer.ID, error) //TODO: is path the right choice here? + FindProviders(context.Context, Path, ...options.DhtFindProvidersOption) (<-chan peer.ID, error) //TODO: is path the right choice here? + + // WithNumProviders is an option for FindProviders which specifies the + // number of peers to look for. Default is 20 + WithNumProviders(numProviders int) options.DhtFindProvidersOption // Provide announces to the network that you are providing given values Provide(context.Context, Path, ...options.DhtProvideOption) error diff --git a/coreiface/options/dht.go b/coreiface/options/dht.go index 92fd14f4a..3867e32c0 100644 --- a/coreiface/options/dht.go +++ b/coreiface/options/dht.go @@ -4,7 +4,12 @@ type DhtProvideSettings struct { Recursive bool } +type DhtFindProvidersSettings struct { + NumProviders int +} + type DhtProvideOption func(*DhtProvideSettings) error +type DhtFindProvidersOption func(*DhtFindProvidersSettings) error func DhtProvideOptions(opts ...DhtProvideOption) (*DhtProvideSettings, error) { options := &DhtProvideSettings{ @@ -20,6 +25,20 @@ func DhtProvideOptions(opts ...DhtProvideOption) (*DhtProvideSettings, error) { return options, nil } +func DhtFindProvidersOptions(opts ...DhtFindProvidersOption) (*DhtFindProvidersSettings, error) { + options := &DhtFindProvidersSettings{ + NumProviders: 20, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + type DhtOptions struct{} func (api *DhtOptions) WithRecursive(recursive bool) DhtProvideOption { @@ -28,3 +47,10 @@ func (api *DhtOptions) WithRecursive(recursive bool) DhtProvideOption { return nil } } + +func (api *DhtOptions) WithNumProviders(numProviders int) DhtFindProvidersOption { + return func(settings *DhtFindProvidersSettings) error { + settings.NumProviders = numProviders + return nil + } +} From 0e411a61d02330a3b2544fb300e4577c1e0a1087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 19:17:30 +0100 Subject: [PATCH 2587/3817] coreapi: test using mock swarm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@db721461cc7c24fba56f74cfd6eb9c8064d0f8c5 --- coreiface/dht.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/dht.go b/coreiface/dht.go index ce8509e01..1d23ece1f 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -5,8 +5,8 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" + peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" + ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" ) // DhtAPI specifies the interface to the DHT From 78b1cf5653e1cc2d8192db2b541fa09e2dbb2c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 19 Jul 2018 13:18:05 +0200 Subject: [PATCH 2588/3817] coreapi: dht: simplify the implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@22a12c2c233473aafa66ced10e222162a7711868 --- coreiface/dht.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/coreiface/dht.go b/coreiface/dht.go index 1d23ece1f..01b7d7367 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -3,21 +3,21 @@ package iface import ( "context" - options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + "github.com/ipfs/go-ipfs/core/coreapi/interface/options" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" + pstore "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore" ) // DhtAPI specifies the interface to the DHT type DhtAPI interface { // FindPeer queries the DHT for all of the multiaddresses associated with a // Peer ID - FindPeer(context.Context, peer.ID) (<-chan ma.Multiaddr, error) + FindPeer(context.Context, peer.ID) (pstore.PeerInfo, error) // FindProviders finds peers in the DHT who can provide a specific value // given a key. - FindProviders(context.Context, Path, ...options.DhtFindProvidersOption) (<-chan peer.ID, error) //TODO: is path the right choice here? + FindProviders(context.Context, Path, ...options.DhtFindProvidersOption) (<-chan pstore.PeerInfo, error) // WithNumProviders is an option for FindProviders which specifies the // number of peers to look for. Default is 20 From 2bef47921404e491af08c3f66fc8475f53526685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 19 Jul 2018 13:27:06 +0200 Subject: [PATCH 2589/3817] coreapi: dht: refactor options after rebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@656fc75b6f65435e224b7bd1e392ee8daf90f9b2 --- coreiface/dht.go | 8 -------- coreiface/options/dht.go | 12 +++++++++--- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/coreiface/dht.go b/coreiface/dht.go index 01b7d7367..f9a08df34 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -19,14 +19,6 @@ type DhtAPI interface { // given a key. FindProviders(context.Context, Path, ...options.DhtFindProvidersOption) (<-chan pstore.PeerInfo, error) - // WithNumProviders is an option for FindProviders which specifies the - // number of peers to look for. Default is 20 - WithNumProviders(numProviders int) options.DhtFindProvidersOption - // Provide announces to the network that you are providing given values Provide(context.Context, Path, ...options.DhtProvideOption) error - - // WithRecursive is an option for Provide which specifies whether to provide - // the given path recursively - WithRecursive(recursive bool) options.DhtProvideOption } diff --git a/coreiface/options/dht.go b/coreiface/options/dht.go index 3867e32c0..f989fa5e7 100644 --- a/coreiface/options/dht.go +++ b/coreiface/options/dht.go @@ -39,16 +39,22 @@ func DhtFindProvidersOptions(opts ...DhtFindProvidersOption) (*DhtFindProvidersS return options, nil } -type DhtOptions struct{} +type dhtOpts struct{} -func (api *DhtOptions) WithRecursive(recursive bool) DhtProvideOption { +var Dht dhtOpts + +// WithRecursive is an option for Dht.Provide which specifies whether to provide +// the given path recursively +func (dhtOpts) WithRecursive(recursive bool) DhtProvideOption { return func(settings *DhtProvideSettings) error { settings.Recursive = recursive return nil } } -func (api *DhtOptions) WithNumProviders(numProviders int) DhtFindProvidersOption { +// WithNumProviders is an option for Dht.FindProviders which specifies the +// number of peers to look for. Default is 20 +func (dhtOpts) WithNumProviders(numProviders int) DhtFindProvidersOption { return func(settings *DhtFindProvidersSettings) error { settings.NumProviders = numProviders return nil From 1ee72b14142c20703fe9bd9690543d42491b5a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 26 Jul 2018 15:09:26 +0200 Subject: [PATCH 2590/3817] coreapi dht: add a note on name change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@da52b4bfdb425964a3b5909bdbeacdfdb57cef40 --- coreiface/dht.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coreiface/dht.go b/coreiface/dht.go index f9a08df34..cd704c3e3 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -10,6 +10,8 @@ import ( ) // DhtAPI specifies the interface to the DHT +// Note: This API will likely get renamed in near future, see +// https://github.com/ipfs/interface-ipfs-core/issues/249 for more context. type DhtAPI interface { // FindPeer queries the DHT for all of the multiaddresses associated with a // Peer ID From 8de59ef7854d5f337c333acc9902715e274cf2dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 10 Aug 2018 13:24:33 +0200 Subject: [PATCH 2591/3817] move streaming set to thirdparty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@03a210bb130d8d46bc9d8c8bbac953edff53f32f --- coreiface/dht.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/dht.go b/coreiface/dht.go index cd704c3e3..7b8119e44 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -10,7 +10,7 @@ import ( ) // DhtAPI specifies the interface to the DHT -// Note: This API will likely get renamed in near future, see +// Note: This API will likely get deprecated in near future, see // https://github.com/ipfs/interface-ipfs-core/issues/249 for more context. type DhtAPI interface { // FindPeer queries the DHT for all of the multiaddresses associated with a From 4390a24ff2b4e85d7c999f3a1066c55bb2fa1cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 28 Aug 2018 02:22:09 +0200 Subject: [PATCH 2592/3817] coreapi: dht: remove option prefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@a288d2c93dd22c48050ffb8cadce259377fd2125 --- coreiface/options/dht.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/coreiface/options/dht.go b/coreiface/options/dht.go index f989fa5e7..e13e16020 100644 --- a/coreiface/options/dht.go +++ b/coreiface/options/dht.go @@ -43,18 +43,18 @@ type dhtOpts struct{} var Dht dhtOpts -// WithRecursive is an option for Dht.Provide which specifies whether to provide +// Recursive is an option for Dht.Provide which specifies whether to provide // the given path recursively -func (dhtOpts) WithRecursive(recursive bool) DhtProvideOption { +func (dhtOpts) Recursive(recursive bool) DhtProvideOption { return func(settings *DhtProvideSettings) error { settings.Recursive = recursive return nil } } -// WithNumProviders is an option for Dht.FindProviders which specifies the +// NumProviders is an option for Dht.FindProviders which specifies the // number of peers to look for. Default is 20 -func (dhtOpts) WithNumProviders(numProviders int) DhtFindProvidersOption { +func (dhtOpts) NumProviders(numProviders int) DhtFindProvidersOption { return func(settings *DhtFindProvidersSettings) error { settings.NumProviders = numProviders return nil From 1a84075e8fd665ea8211b6d31bc90ecbfddc8718 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 7 Sep 2018 15:01:40 -0700 Subject: [PATCH 2593/3817] remove Godeps fixes #2722 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@00d1e8df514cd3582fde66485d10190032be2ec9 --- pinning/pinner/internal/pb/doc.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pinning/pinner/internal/pb/doc.go b/pinning/pinner/internal/pb/doc.go index 1143a4d83..95d4afe67 100644 --- a/pinning/pinner/internal/pb/doc.go +++ b/pinning/pinner/internal/pb/doc.go @@ -1,6 +1,3 @@ package pb //go:generate protoc --gogo_out=. header.proto - -// kludge to get vendoring right in protobuf output -//go:generate sed -i s,github.com/,github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/,g header.pb.go From 1afc5947f42b15f69855412884d87806bd014764 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 5 Sep 2018 01:59:19 -0400 Subject: [PATCH 2594/3817] gx update go-cid and fix code to use new Cid type This commit was moved from ipfs/go-verifcid@280ec1f79326f5f215afc8f5cec1061189ee9107 --- verifcid/validate.go | 2 +- verifcid/validate_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/verifcid/validate.go b/verifcid/validate.go index 34db44ffc..8a76e4933 100644 --- a/verifcid/validate.go +++ b/verifcid/validate.go @@ -48,7 +48,7 @@ func IsGoodHash(code uint64) bool { return false } -func ValidateCid(c *cid.Cid) error { +func ValidateCid(c cid.Cid) error { pref := c.Prefix() if !IsGoodHash(pref.MhType) { return ErrPossiblyInsecureHashFunction diff --git a/verifcid/validate_test.go b/verifcid/validate_test.go index 4b53ce183..1d31e5464 100644 --- a/verifcid/validate_test.go +++ b/verifcid/validate_test.go @@ -32,7 +32,7 @@ func TestValidateCids(t *testing.T) { assertFalse(IsGoodHash(mh.BLAKE2B_MIN + 5)) - mhcid := func(code uint64, length int) *cid.Cid { + mhcid := func(code uint64, length int) cid.Cid { mhash, err := mh.Sum([]byte{}, code, length) if err != nil { t.Fatal(err) @@ -41,7 +41,7 @@ func TestValidateCids(t *testing.T) { } cases := []struct { - cid *cid.Cid + cid cid.Cid err error }{ {mhcid(mh.SHA2_256, 32), nil}, From 8870fa2f6cffc1e05b610495ca87c4b740a59856 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 5 Sep 2018 02:22:06 -0400 Subject: [PATCH 2595/3817] gx update and fix code to use new Cid type This commit was moved from ipfs/go-ipfs-exchange-interface@91dc3c5ff63431d23400cd98b7130fec86c7011d --- exchange/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index 675592ffd..42fe6a80b 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -25,8 +25,8 @@ type Interface interface { // type Exchanger interface // Fetcher is an object that can be used to retrieve blocks type Fetcher interface { // GetBlock returns the block associated with a given key. - GetBlock(context.Context, *cid.Cid) (blocks.Block, error) - GetBlocks(context.Context, []*cid.Cid) (<-chan blocks.Block, error) + GetBlock(context.Context, cid.Cid) (blocks.Block, error) + GetBlocks(context.Context, []cid.Cid) (<-chan blocks.Block, error) } // SessionExchange is an exchange.Interface which supports From 436182628bf7237738f96b956a7e62ad07271cd3 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 6 Sep 2018 07:50:35 -0400 Subject: [PATCH 2596/3817] gx update go-cid and fix code to use new Cid type This commit was moved from ipfs/go-ipfs-ds-help@d7a1220589d9e516524318594c57fccb682e5e25 --- datastore/dshelp/key.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index b4ee6743c..b4fff9891 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -22,15 +22,15 @@ func BinaryFromDsKey(k datastore.Key) ([]byte, error) { } // CidToDsKey creates a Key from the given Cid. -func CidToDsKey(k *cid.Cid) datastore.Key { +func CidToDsKey(k cid.Cid) datastore.Key { return NewKeyFromBinary(k.Bytes()) } // DsKeyToCid converts the given Key to its corresponding Cid. -func DsKeyToCid(dsKey datastore.Key) (*cid.Cid, error) { +func DsKeyToCid(dsKey datastore.Key) (cid.Cid, error) { kb, err := BinaryFromDsKey(dsKey) if err != nil { - return nil, err + return cid.Cid{}, err } return cid.Cast(kb) } From aa38d5382e4849e10d4fce530ee5a0feda1e3c4f Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 5 Sep 2018 02:54:36 -0400 Subject: [PATCH 2597/3817] gx update and fix code to use new Cid type This commit was moved from ipfs/go-ipfs-blockstore@3666fad3f1e86477e13b7daf1ebe46e458e61d96 --- blockstore/arc_cache.go | 24 ++++++++++++------------ blockstore/arc_cache_test.go | 2 +- blockstore/blockstore.go | 26 +++++++++++++------------- blockstore/blockstore_test.go | 12 ++++++------ blockstore/bloom_cache.go | 16 ++++++++-------- blockstore/idstore.go | 12 ++++++------ 6 files changed, 46 insertions(+), 46 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 4339bb51f..78e313512 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -35,7 +35,7 @@ func newARCCachedBS(ctx context.Context, bs Blockstore, lruSize int) (*arccache, return c, nil } -func (b *arccache) DeleteBlock(k *cid.Cid) error { +func (b *arccache) DeleteBlock(k cid.Cid) error { if has, _, ok := b.hasCached(k); ok && !has { return ErrNotFound } @@ -53,10 +53,10 @@ func (b *arccache) DeleteBlock(k *cid.Cid) error { // if ok == false has is inconclusive // if ok == true then has respons to question: is it contained -func (b *arccache) hasCached(k *cid.Cid) (has bool, size int, ok bool) { +func (b *arccache) hasCached(k cid.Cid) (has bool, size int, ok bool) { b.total.Inc() - if k == nil { - log.Error("nil cid in arccache") + if !k.Defined() { + log.Error("undefined cid in arccache") // Return cache invalid so the call to blockstore happens // in case of invalid key and correct error is created. return false, -1, false @@ -75,7 +75,7 @@ func (b *arccache) hasCached(k *cid.Cid) (has bool, size int, ok bool) { return false, -1, false } -func (b *arccache) Has(k *cid.Cid) (bool, error) { +func (b *arccache) Has(k cid.Cid) (bool, error) { if has, _, ok := b.hasCached(k); ok { return has, nil } @@ -87,7 +87,7 @@ func (b *arccache) Has(k *cid.Cid) (bool, error) { return has, nil } -func (b *arccache) GetSize(k *cid.Cid) (int, error) { +func (b *arccache) GetSize(k cid.Cid) (int, error) { if _, blockSize, ok := b.hasCached(k); ok { return blockSize, nil } @@ -100,9 +100,9 @@ func (b *arccache) GetSize(k *cid.Cid) (int, error) { return blockSize, err } -func (b *arccache) Get(k *cid.Cid) (blocks.Block, error) { - if k == nil { - log.Error("nil cid in arc cache") +func (b *arccache) Get(k cid.Cid) (blocks.Block, error) { + if !k.Defined() { + log.Error("undefined cid in arc cache") return nil, ErrNotFound } @@ -154,15 +154,15 @@ func (b *arccache) HashOnRead(enabled bool) { b.blockstore.HashOnRead(enabled) } -func (b *arccache) cacheHave(c *cid.Cid, have bool) { +func (b *arccache) cacheHave(c cid.Cid, have bool) { b.arc.Add(c.KeyString(), cacheHave(have)) } -func (b *arccache) cacheSize(c *cid.Cid, blockSize int) { +func (b *arccache) cacheSize(c cid.Cid, blockSize int) { b.arc.Add(c.KeyString(), cacheSize(blockSize)) } -func (b *arccache) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { +func (b *arccache) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return b.blockstore.AllKeysChan(ctx) } diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 2f8081957..facbf3473 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -153,7 +153,7 @@ func TestArcCreationFailure(t *testing.T) { func TestInvalidKey(t *testing.T) { arc, _, _ := createStores(t) - bl, err := arc.Get(nil) + bl, err := arc.Get(cid.Cid{}) if bl != nil { t.Fatal("blocks should be nil") diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 4dd670c9a..6bb8e399d 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -32,12 +32,12 @@ var ErrNotFound = errors.New("blockstore: block not found") // Blockstore wraps a Datastore block-centered methods and provides a layer // of abstraction which allows to add different caching strategies. type Blockstore interface { - DeleteBlock(*cid.Cid) error - Has(*cid.Cid) (bool, error) - Get(*cid.Cid) (blocks.Block, error) + DeleteBlock(cid.Cid) error + Has(cid.Cid) (bool, error) + Get(cid.Cid) (blocks.Block, error) // GetSize returns the CIDs mapped BlockSize - GetSize(*cid.Cid) (int, error) + GetSize(cid.Cid) (int, error) // Put puts a given block to the underlying datastore Put(blocks.Block) error @@ -49,7 +49,7 @@ type Blockstore interface { // AllKeysChan returns a channel from which // the CIDs in the Blockstore can be read. It should respect // the given context, closing the channel if it becomes Done. - AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) + AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) // HashOnRead specifies if every read block should be // rehashed to make sure it matches its CID. @@ -114,9 +114,9 @@ func (bs *blockstore) HashOnRead(enabled bool) { bs.rehash = enabled } -func (bs *blockstore) Get(k *cid.Cid) (blocks.Block, error) { - if k == nil { - log.Error("nil cid in blockstore") +func (bs *blockstore) Get(k cid.Cid) (blocks.Block, error) { + if !k.Defined() { + log.Error("undefined cid in blockstore") return nil, ErrNotFound } @@ -173,11 +173,11 @@ func (bs *blockstore) PutMany(blocks []blocks.Block) error { return t.Commit() } -func (bs *blockstore) Has(k *cid.Cid) (bool, error) { +func (bs *blockstore) Has(k cid.Cid) (bool, error) { return bs.datastore.Has(dshelp.CidToDsKey(k)) } -func (bs *blockstore) GetSize(k *cid.Cid) (int, error) { +func (bs *blockstore) GetSize(k cid.Cid) (int, error) { bdata, err := bs.datastore.Get(dshelp.CidToDsKey(k)) if err == ds.ErrNotFound { return -1, ErrNotFound @@ -188,7 +188,7 @@ func (bs *blockstore) GetSize(k *cid.Cid) (int, error) { return len(bdata), nil } -func (bs *blockstore) DeleteBlock(k *cid.Cid) error { +func (bs *blockstore) DeleteBlock(k cid.Cid) error { err := bs.datastore.Delete(dshelp.CidToDsKey(k)) if err == ds.ErrNotFound { return ErrNotFound @@ -200,7 +200,7 @@ func (bs *blockstore) DeleteBlock(k *cid.Cid) error { // this is very simplistic, in the future, take dsq.Query as a param? // // AllKeysChan respects context. -func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { +func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // KeysOnly, because that would be _a lot_ of data. q := dsq.Query{KeysOnly: true} @@ -209,7 +209,7 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) return nil, err } - output := make(chan *cid.Cid, dsq.KeysOnlyBufSize) + output := make(chan cid.Cid, dsq.KeysOnlyBufSize) go func() { defer func() { res.Close() // ensure exit (signals early exit, too) diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index ae71b541a..d0fa739c2 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -29,7 +29,7 @@ func TestGetWhenKeyNotPresent(t *testing.T) { func TestGetWhenKeyIsNil(t *testing.T) { bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) - _, err := bs.Get(nil) + _, err := bs.Get(cid.Cid{}) if err != ErrNotFound { t.Fail() } @@ -113,13 +113,13 @@ func TestHashOnRead(t *testing.T) { } } -func newBlockStoreWithKeys(t *testing.T, d ds.Datastore, N int) (Blockstore, []*cid.Cid) { +func newBlockStoreWithKeys(t *testing.T, d ds.Datastore, N int) (Blockstore, []cid.Cid) { if d == nil { d = ds.NewMapDatastore() } bs := NewBlockstore(ds_sync.MutexWrap(d)) - keys := make([]*cid.Cid, N) + keys := make([]cid.Cid, N) for i := 0; i < N; i++ { block := blocks.NewBlock([]byte(fmt.Sprintf("some data %d", i))) err := bs.Put(block) @@ -131,8 +131,8 @@ func newBlockStoreWithKeys(t *testing.T, d ds.Datastore, N int) (Blockstore, []* return bs, keys } -func collect(ch <-chan *cid.Cid) []*cid.Cid { - var keys []*cid.Cid +func collect(ch <-chan cid.Cid) []cid.Cid { + var keys []cid.Cid for k := range ch { keys = append(keys, k) } @@ -217,7 +217,7 @@ func TestAllKeysRespectsContext(t *testing.T) { } -func expectMatches(t *testing.T, expect, actual []*cid.Cid) { +func expectMatches(t *testing.T, expect, actual []cid.Cid) { if len(expect) != len(actual) { t.Errorf("expect and actual differ: %d != %d", len(expect), len(actual)) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 7e116890a..c58120c36 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -97,7 +97,7 @@ func (b *bloomcache) Rebuild(ctx context.Context) { atomic.StoreInt32(&b.active, 1) } -func (b *bloomcache) DeleteBlock(k *cid.Cid) error { +func (b *bloomcache) DeleteBlock(k cid.Cid) error { if has, ok := b.hasCached(k); ok && !has { return ErrNotFound } @@ -107,10 +107,10 @@ func (b *bloomcache) DeleteBlock(k *cid.Cid) error { // if ok == false has is inconclusive // if ok == true then has respons to question: is it contained -func (b *bloomcache) hasCached(k *cid.Cid) (has bool, ok bool) { +func (b *bloomcache) hasCached(k cid.Cid) (has bool, ok bool) { b.total.Inc() - if k == nil { - log.Error("nil cid in bloom cache") + if !k.Defined() { + log.Error("undefined in bloom cache") // Return cache invalid so call to blockstore // in case of invalid key is forwarded deeper return false, false @@ -125,7 +125,7 @@ func (b *bloomcache) hasCached(k *cid.Cid) (has bool, ok bool) { return false, false } -func (b *bloomcache) Has(k *cid.Cid) (bool, error) { +func (b *bloomcache) Has(k cid.Cid) (bool, error) { if has, ok := b.hasCached(k); ok { return has, nil } @@ -133,11 +133,11 @@ func (b *bloomcache) Has(k *cid.Cid) (bool, error) { return b.blockstore.Has(k) } -func (b *bloomcache) GetSize(k *cid.Cid) (int, error) { +func (b *bloomcache) GetSize(k cid.Cid) (int, error) { return b.blockstore.GetSize(k) } -func (b *bloomcache) Get(k *cid.Cid) (blocks.Block, error) { +func (b *bloomcache) Get(k cid.Cid) (blocks.Block, error) { if has, ok := b.hasCached(k); ok && !has { return nil, ErrNotFound } @@ -173,7 +173,7 @@ func (b *bloomcache) HashOnRead(enabled bool) { b.blockstore.HashOnRead(enabled) } -func (b *bloomcache) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { +func (b *bloomcache) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return b.blockstore.AllKeysChan(ctx) } diff --git a/blockstore/idstore.go b/blockstore/idstore.go index a1ef4d60b..2a5bf8415 100644 --- a/blockstore/idstore.go +++ b/blockstore/idstore.go @@ -17,7 +17,7 @@ func NewIdStore(bs Blockstore) Blockstore { return &idstore{bs} } -func extractContents(k *cid.Cid) (bool, []byte) { +func extractContents(k cid.Cid) (bool, []byte) { dmh, err := mh.Decode(k.Hash()) if err != nil || dmh.Code != mh.ID { return false, nil @@ -25,7 +25,7 @@ func extractContents(k *cid.Cid) (bool, []byte) { return true, dmh.Digest } -func (b *idstore) DeleteBlock(k *cid.Cid) error { +func (b *idstore) DeleteBlock(k cid.Cid) error { isId, _ := extractContents(k) if isId { return nil @@ -33,7 +33,7 @@ func (b *idstore) DeleteBlock(k *cid.Cid) error { return b.bs.DeleteBlock(k) } -func (b *idstore) Has(k *cid.Cid) (bool, error) { +func (b *idstore) Has(k cid.Cid) (bool, error) { isId, _ := extractContents(k) if isId { return true, nil @@ -41,7 +41,7 @@ func (b *idstore) Has(k *cid.Cid) (bool, error) { return b.bs.Has(k) } -func (b *idstore) GetSize(k *cid.Cid) (int, error) { +func (b *idstore) GetSize(k cid.Cid) (int, error) { isId, bdata := extractContents(k) if isId { return len(bdata), nil @@ -49,7 +49,7 @@ func (b *idstore) GetSize(k *cid.Cid) (int, error) { return b.bs.GetSize(k) } -func (b *idstore) Get(k *cid.Cid) (blocks.Block, error) { +func (b *idstore) Get(k cid.Cid) (blocks.Block, error) { isId, bdata := extractContents(k) if isId { return blocks.NewBlockWithCid(bdata, k) @@ -81,6 +81,6 @@ func (b *idstore) HashOnRead(enabled bool) { b.bs.HashOnRead(enabled) } -func (b *idstore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { +func (b *idstore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return b.bs.AllKeysChan(ctx) } From 6eda42b200bd4634e48eda602d64277c5d7febe1 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 5 Sep 2018 03:06:35 -0400 Subject: [PATCH 2598/3817] gx update and fix code to use new Cid type This commit was moved from ipfs/go-ipfs-routing@4570f1ef2fd8509405ec81482927e3defb497b2a --- routing/mock/centralized_client.go | 6 +++--- routing/mock/centralized_server.go | 8 ++++---- routing/none/none_client.go | 4 ++-- routing/offline/offline.go | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index e3d488240..49a363300 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -34,7 +34,7 @@ func (c *client) GetValue(ctx context.Context, key string, opts ...ropts.Option) return c.vs.GetValue(ctx, key, opts...) } -func (c *client) FindProviders(ctx context.Context, key *cid.Cid) ([]pstore.PeerInfo, error) { +func (c *client) FindProviders(ctx context.Context, key cid.Cid) ([]pstore.PeerInfo, error) { return c.server.Providers(key), nil } @@ -43,7 +43,7 @@ func (c *client) FindPeer(ctx context.Context, pid peer.ID) (pstore.PeerInfo, er return pstore.PeerInfo{}, nil } -func (c *client) FindProvidersAsync(ctx context.Context, k *cid.Cid, max int) <-chan pstore.PeerInfo { +func (c *client) FindProvidersAsync(ctx context.Context, k cid.Cid, max int) <-chan pstore.PeerInfo { out := make(chan pstore.PeerInfo) go func() { defer close(out) @@ -63,7 +63,7 @@ func (c *client) FindProvidersAsync(ctx context.Context, k *cid.Cid, max int) <- // Provide returns once the message is on the network. Value is not necessarily // visible yet. -func (c *client) Provide(_ context.Context, key *cid.Cid, brd bool) error { +func (c *client) Provide(_ context.Context, key cid.Cid, brd bool) error { if !brd { return nil } diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index b869a4f43..a223f911b 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -18,8 +18,8 @@ import ( // server is the mockrouting.Client's private interface to the routing server type server interface { - Announce(pstore.PeerInfo, *cid.Cid) error - Providers(*cid.Cid) []pstore.PeerInfo + Announce(pstore.PeerInfo, cid.Cid) error + Providers(cid.Cid) []pstore.PeerInfo Server } @@ -37,7 +37,7 @@ type providerRecord struct { Created time.Time } -func (rs *s) Announce(p pstore.PeerInfo, c *cid.Cid) error { +func (rs *s) Announce(p pstore.PeerInfo, c cid.Cid) error { rs.lock.Lock() defer rs.lock.Unlock() @@ -54,7 +54,7 @@ func (rs *s) Announce(p pstore.PeerInfo, c *cid.Cid) error { return nil } -func (rs *s) Providers(c *cid.Cid) []pstore.PeerInfo { +func (rs *s) Providers(c cid.Cid) []pstore.PeerInfo { rs.delayConf.Query.Wait() // before locking rs.lock.RLock() diff --git a/routing/none/none_client.go b/routing/none/none_client.go index a7c1f8fc9..e29ef36af 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -30,13 +30,13 @@ func (c *nilclient) FindPeer(_ context.Context, _ peer.ID) (pstore.PeerInfo, err return pstore.PeerInfo{}, nil } -func (c *nilclient) FindProvidersAsync(_ context.Context, _ *cid.Cid, _ int) <-chan pstore.PeerInfo { +func (c *nilclient) FindProvidersAsync(_ context.Context, _ cid.Cid, _ int) <-chan pstore.PeerInfo { out := make(chan pstore.PeerInfo) defer close(out) return out } -func (c *nilclient) Provide(_ context.Context, _ *cid.Cid, _ bool) error { +func (c *nilclient) Provide(_ context.Context, _ cid.Cid, _ bool) error { return nil } diff --git a/routing/offline/offline.go b/routing/offline/offline.go index ebc96ef20..9b94176cc 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -94,13 +94,13 @@ func (c *offlineRouting) FindPeer(ctx context.Context, pid peer.ID) (pstore.Peer return pstore.PeerInfo{}, ErrOffline } -func (c *offlineRouting) FindProvidersAsync(ctx context.Context, k *cid.Cid, max int) <-chan pstore.PeerInfo { +func (c *offlineRouting) FindProvidersAsync(ctx context.Context, k cid.Cid, max int) <-chan pstore.PeerInfo { out := make(chan pstore.PeerInfo) close(out) return out } -func (c *offlineRouting) Provide(_ context.Context, k *cid.Cid, _ bool) error { +func (c *offlineRouting) Provide(_ context.Context, k cid.Cid, _ bool) error { return ErrOffline } From dac30f5abb5428d69c1505c8ec20df0495683c6c Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 5 Sep 2018 03:10:18 -0400 Subject: [PATCH 2599/3817] gx update and fix code to use new Cid type This commit was moved from ipfs/go-ipfs-exchange-offline@8704c1c197cf00bc5c2ecdef45307331001d8ad3 --- exchange/offline/offline.go | 6 +++--- exchange/offline/offline_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index c3a284c7e..cb82b8a0a 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -24,7 +24,7 @@ type offlineExchange struct { // GetBlock returns nil to signal that a block could not be retrieved for the // given key. // NB: This function may return before the timeout expires. -func (e *offlineExchange) GetBlock(_ context.Context, k *cid.Cid) (blocks.Block, error) { +func (e *offlineExchange) GetBlock(_ context.Context, k cid.Cid) (blocks.Block, error) { return e.bs.Get(k) } @@ -40,11 +40,11 @@ func (_ *offlineExchange) Close() error { return nil } -func (e *offlineExchange) GetBlocks(ctx context.Context, ks []*cid.Cid) (<-chan blocks.Block, error) { +func (e *offlineExchange) GetBlocks(ctx context.Context, ks []cid.Cid) (<-chan blocks.Block, error) { out := make(chan blocks.Block) go func() { defer close(out) - var misses []*cid.Cid + var misses []cid.Cid for _, k := range ks { hit, err := e.bs.Get(k) if err != nil { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 159208621..3b84b8c1e 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -51,8 +51,8 @@ func TestGetBlocks(t *testing.T) { } } - request := func() []*cid.Cid { - var ks []*cid.Cid + request := func() []cid.Cid { + var ks []cid.Cid for _, b := range expected { ks = append(ks, b.Cid()) From 0f8e872f07b7b8a0aa03b9d626ea026b9edf6e54 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 5 Sep 2018 03:12:54 -0400 Subject: [PATCH 2600/3817] gx update and fix code to use new Cid type This commit was moved from ipfs/go-blockservice@4ba51bde1832b5a073f32a3438b4fc26eedf229c --- blockservice/blockservice.go | 22 +++++++++++----------- blockservice/test/blocks_test.go | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index d5c18824a..a8d72e0d9 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -25,7 +25,7 @@ var ErrNotFound = errors.New("blockservice: key not found") // the blockservice. type BlockGetter interface { // GetBlock gets the requested block. - GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) + GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) // GetBlocks does a batch request for the given cids, returning blocks as // they are found, in no particular order. @@ -34,7 +34,7 @@ type BlockGetter interface { // be canceled). In that case, it will close the channel early. It is up // to the consumer to detect this situation and keep track which blocks // it has received and which it hasn't. - GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block + GetBlocks(ctx context.Context, ks []cid.Cid) <-chan blocks.Block } // BlockService is a hybrid block datastore. It stores data in a local @@ -58,7 +58,7 @@ type BlockService interface { AddBlocks(bs []blocks.Block) error // DeleteBlock deletes the given block from the blockservice. - DeleteBlock(o *cid.Cid) error + DeleteBlock(o cid.Cid) error } type blockService struct { @@ -196,7 +196,7 @@ func (s *blockService) AddBlocks(bs []blocks.Block) error { // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). -func (s *blockService) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) { +func (s *blockService) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) { log.Debugf("BlockService GetBlock: '%s'", c) var f exchange.Fetcher @@ -207,7 +207,7 @@ func (s *blockService) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, return getBlock(ctx, c, s.blockstore, f) // hash security } -func getBlock(ctx context.Context, c *cid.Cid, bs blockstore.Blockstore, f exchange.Fetcher) (blocks.Block, error) { +func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, f exchange.Fetcher) (blocks.Block, error) { err := verifcid.ValidateCid(c) // hash security if err != nil { return nil, err @@ -244,11 +244,11 @@ func getBlock(ctx context.Context, c *cid.Cid, bs blockstore.Blockstore, f excha // GetBlocks gets a list of blocks asynchronously and returns through // the returned channel. // NB: No guarantees are made about order. -func (s *blockService) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block { +func (s *blockService) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan blocks.Block { return getBlocks(ctx, ks, s.blockstore, s.exchange) // hash security } -func getBlocks(ctx context.Context, ks []*cid.Cid, bs blockstore.Blockstore, f exchange.Fetcher) <-chan blocks.Block { +func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, f exchange.Fetcher) <-chan blocks.Block { out := make(chan blocks.Block) go func() { @@ -266,7 +266,7 @@ func getBlocks(ctx context.Context, ks []*cid.Cid, bs blockstore.Blockstore, f e } ks = ks[:k] - var misses []*cid.Cid + var misses []cid.Cid for _, c := range ks { hit, err := bs.Get(c) if err != nil { @@ -303,7 +303,7 @@ func getBlocks(ctx context.Context, ks []*cid.Cid, bs blockstore.Blockstore, f e } // DeleteBlock deletes a block in the blockservice from the datastore -func (s *blockService) DeleteBlock(c *cid.Cid) error { +func (s *blockService) DeleteBlock(c cid.Cid) error { err := s.blockstore.DeleteBlock(c) if err == nil { log.Event(context.TODO(), "BlockService.BlockDeleted", c) @@ -323,12 +323,12 @@ type Session struct { } // GetBlock gets a block in the context of a request session -func (s *Session) GetBlock(ctx context.Context, c *cid.Cid) (blocks.Block, error) { +func (s *Session) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) { return getBlock(ctx, c, s.bs, s.ses) // hash security } // GetBlocks gets blocks in the context of a request session -func (s *Session) GetBlocks(ctx context.Context, ks []*cid.Cid) <-chan blocks.Block { +func (s *Session) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan blocks.Block { return getBlocks(ctx, ks, s.bs, s.ses) // hash security } diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index c3faa02c3..95f552d21 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -71,7 +71,7 @@ func TestGetBlocksSequential(t *testing.T) { } objs := makeObjects(50) - var cids []*cid.Cid + var cids []cid.Cid for _, o := range objs { cids = append(cids, o.Cid()) servs[0].AddBlock(o) From 784ec7d4b75f28fe1942b5843937afa70d85fecc Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 11 Sep 2018 19:22:01 -0400 Subject: [PATCH 2601/3817] gx update and fix code to use new Cid type This commit was moved from ipfs/go-merkledag@4132fbd13c85bc8f086b587111d5281d692122d0 --- ipld/merkledag/coding.go | 6 ++--- ipld/merkledag/errservice.go | 8 +++--- ipld/merkledag/merkledag.go | 46 ++++++++++++++++---------------- ipld/merkledag/merkledag_test.go | 10 +++---- ipld/merkledag/node.go | 10 +++---- ipld/merkledag/readonly_test.go | 2 +- ipld/merkledag/rwservice.go | 8 +++--- 7 files changed, 45 insertions(+), 45 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index efb5dc224..4b1738bfd 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -68,7 +68,7 @@ func (n *ProtoNode) getPBNode() *pb.PBNode { pbn.Links[i] = &pb.PBLink{} pbn.Links[i].Name = &l.Name pbn.Links[i].Tsize = &l.Size - if l.Cid != nil { + if l.Cid.Defined() { pbn.Links[i].Hash = l.Cid.Bytes() } } @@ -84,7 +84,7 @@ func (n *ProtoNode) getPBNode() *pb.PBNode { func (n *ProtoNode) EncodeProtobuf(force bool) ([]byte, error) { sort.Stable(LinkSlice(n.links)) // keep links sorted if n.encoded == nil || force { - n.cached = nil + n.cached = cid.Undef var err error n.encoded, err = n.Marshal() if err != nil { @@ -92,7 +92,7 @@ func (n *ProtoNode) EncodeProtobuf(force bool) ([]byte, error) { } } - if n.cached == nil { + if !n.cached.Defined() { c, err := n.CidBuilder().Sum(n.encoded) if err != nil { return nil, err diff --git a/ipld/merkledag/errservice.go b/ipld/merkledag/errservice.go index d26c176e2..f4607615a 100644 --- a/ipld/merkledag/errservice.go +++ b/ipld/merkledag/errservice.go @@ -25,23 +25,23 @@ func (cs *ErrorService) AddMany(ctx context.Context, nds []ipld.Node) error { } // Get returns the cs.Err. -func (cs *ErrorService) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { +func (cs *ErrorService) Get(ctx context.Context, c cid.Cid) (ipld.Node, error) { return nil, cs.Err } // GetMany many returns the cs.Err. -func (cs *ErrorService) GetMany(ctx context.Context, cids []*cid.Cid) <-chan *ipld.NodeOption { +func (cs *ErrorService) GetMany(ctx context.Context, cids []cid.Cid) <-chan *ipld.NodeOption { ch := make(chan *ipld.NodeOption) close(ch) return ch } // Remove returns the cs.Err. -func (cs *ErrorService) Remove(ctx context.Context, c *cid.Cid) error { +func (cs *ErrorService) Remove(ctx context.Context, c cid.Cid) error { return cs.Err } // RemoveMany returns the cs.Err. -func (cs *ErrorService) RemoveMany(ctx context.Context, cids []*cid.Cid) error { +func (cs *ErrorService) RemoveMany(ctx context.Context, cids []cid.Cid) error { return cs.Err } diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index fe68ce421..8b522650f 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -60,7 +60,7 @@ func (n *dagService) AddMany(ctx context.Context, nds []ipld.Node) error { } // Get retrieves a node from the dagService, fetching the block in the BlockService -func (n *dagService) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { +func (n *dagService) Get(ctx context.Context, c cid.Cid) (ipld.Node, error) { if n == nil { return nil, fmt.Errorf("dagService is nil") } @@ -81,7 +81,7 @@ func (n *dagService) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { // GetLinks return the links for the node, the node doesn't necessarily have // to exist locally. -func (n *dagService) GetLinks(ctx context.Context, c *cid.Cid) ([]*ipld.Link, error) { +func (n *dagService) GetLinks(ctx context.Context, c cid.Cid) ([]*ipld.Link, error) { if c.Type() == cid.Raw { return nil, nil } @@ -92,7 +92,7 @@ func (n *dagService) GetLinks(ctx context.Context, c *cid.Cid) ([]*ipld.Link, er return node.Links(), nil } -func (n *dagService) Remove(ctx context.Context, c *cid.Cid) error { +func (n *dagService) Remove(ctx context.Context, c cid.Cid) error { return n.Blocks.DeleteBlock(c) } @@ -101,7 +101,7 @@ func (n *dagService) Remove(ctx context.Context, c *cid.Cid) error { // // This operation is not atomic. If it returns an error, some nodes may or may // not have been removed. -func (n *dagService) RemoveMany(ctx context.Context, cids []*cid.Cid) error { +func (n *dagService) RemoveMany(ctx context.Context, cids []cid.Cid) error { // TODO(#4608): make this batch all the way down. for _, c := range cids { if err := n.Blocks.DeleteBlock(c); err != nil { @@ -115,7 +115,7 @@ func (n *dagService) RemoveMany(ctx context.Context, cids []*cid.Cid) error { // the node, bypassing the LinkService. If the node does not exist // locally (and can not be retrieved) an error will be returned. func GetLinksDirect(serv ipld.NodeGetter) GetLinks { - return func(ctx context.Context, c *cid.Cid) ([]*ipld.Link, error) { + return func(ctx context.Context, c cid.Cid) ([]*ipld.Link, error) { nd, err := serv.Get(ctx, c) if err != nil { if err == bserv.ErrNotFound { @@ -132,7 +132,7 @@ type sesGetter struct { } // Get gets a single node from the DAG. -func (sg *sesGetter) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { +func (sg *sesGetter) Get(ctx context.Context, c cid.Cid) (ipld.Node, error) { blk, err := sg.bs.GetBlock(ctx, c) switch err { case bserv.ErrNotFound: @@ -147,7 +147,7 @@ func (sg *sesGetter) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { } // GetMany gets many nodes at once, batching the request if possible. -func (sg *sesGetter) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *ipld.NodeOption { +func (sg *sesGetter) GetMany(ctx context.Context, keys []cid.Cid) <-chan *ipld.NodeOption { return getNodesFromBG(ctx, sg.bs, keys) } @@ -157,7 +157,7 @@ func (n *dagService) Session(ctx context.Context) ipld.NodeGetter { } // FetchGraph fetches all nodes that are children of the given node -func FetchGraph(ctx context.Context, root *cid.Cid, serv ipld.DAGService) error { +func FetchGraph(ctx context.Context, root cid.Cid, serv ipld.DAGService) error { return FetchGraphWithDepthLimit(ctx, root, -1, serv) } @@ -165,7 +165,7 @@ func FetchGraph(ctx context.Context, root *cid.Cid, serv ipld.DAGService) error // node down to the given depth. maxDetph=0 means "only fetch root", // maxDepth=1 means "fetch root and its direct children" and so on... // maxDepth=-1 means unlimited. -func FetchGraphWithDepthLimit(ctx context.Context, root *cid.Cid, depthLim int, serv ipld.DAGService) error { +func FetchGraphWithDepthLimit(ctx context.Context, root cid.Cid, depthLim int, serv ipld.DAGService) error { var ng ipld.NodeGetter = serv ds, ok := serv.(*dagService) if ok { @@ -181,7 +181,7 @@ func FetchGraphWithDepthLimit(ctx context.Context, root *cid.Cid, depthLim int, // to explore deeper than before). // depthLim = -1 means we only return true if the element is not in the // set. - visit := func(c *cid.Cid, depth int) bool { + visit := func(c cid.Cid, depth int) bool { key := string(c.Bytes()) oldDepth, ok := set[key] @@ -201,7 +201,7 @@ func FetchGraphWithDepthLimit(ctx context.Context, root *cid.Cid, depthLim int, return EnumerateChildrenAsyncDepth(ctx, GetLinksDirect(ng), root, 0, visit) } - visitProgress := func(c *cid.Cid, depth int) bool { + visitProgress := func(c cid.Cid, depth int) bool { if visit(c, depth) { v.Increment() return true @@ -216,11 +216,11 @@ func FetchGraphWithDepthLimit(ctx context.Context, root *cid.Cid, depthLim int, // This method may not return all requested nodes (and may or may not return an // error indicating that it failed to do so. It is up to the caller to verify // that it received all nodes. -func (n *dagService) GetMany(ctx context.Context, keys []*cid.Cid) <-chan *ipld.NodeOption { +func (n *dagService) GetMany(ctx context.Context, keys []cid.Cid) <-chan *ipld.NodeOption { return getNodesFromBG(ctx, n.Blocks, keys) } -func dedupKeys(keys []*cid.Cid) []*cid.Cid { +func dedupKeys(keys []cid.Cid) []cid.Cid { set := cid.NewSet() for _, c := range keys { set.Add(c) @@ -231,7 +231,7 @@ func dedupKeys(keys []*cid.Cid) []*cid.Cid { return set.Keys() } -func getNodesFromBG(ctx context.Context, bs bserv.BlockGetter, keys []*cid.Cid) <-chan *ipld.NodeOption { +func getNodesFromBG(ctx context.Context, bs bserv.BlockGetter, keys []cid.Cid) <-chan *ipld.NodeOption { keys = dedupKeys(keys) out := make(chan *ipld.NodeOption, len(keys)) @@ -270,14 +270,14 @@ func getNodesFromBG(ctx context.Context, bs bserv.BlockGetter, keys []*cid.Cid) // GetLinks is the type of function passed to the EnumerateChildren function(s) // for getting the children of an IPLD node. -type GetLinks func(context.Context, *cid.Cid) ([]*ipld.Link, error) +type GetLinks func(context.Context, cid.Cid) ([]*ipld.Link, error) // GetLinksWithDAG returns a GetLinks function that tries to use the given // NodeGetter as a LinkGetter to get the children of a given IPLD node. This may // allow us to traverse the DAG without actually loading and parsing the node in // question (if we already have the links cached). func GetLinksWithDAG(ng ipld.NodeGetter) GetLinks { - return func(ctx context.Context, c *cid.Cid) ([]*ipld.Link, error) { + return func(ctx context.Context, c cid.Cid) ([]*ipld.Link, error) { return ipld.GetLinks(ctx, ng, c) } } @@ -285,8 +285,8 @@ func GetLinksWithDAG(ng ipld.NodeGetter) GetLinks { // EnumerateChildren will walk the dag below the given root node and add all // unseen children to the passed in set. // TODO: parallelize to avoid disk latency perf hits? -func EnumerateChildren(ctx context.Context, getLinks GetLinks, root *cid.Cid, visit func(*cid.Cid) bool) error { - visitDepth := func(c *cid.Cid, depth int) bool { +func EnumerateChildren(ctx context.Context, getLinks GetLinks, root cid.Cid, visit func(cid.Cid) bool) error { + visitDepth := func(c cid.Cid, depth int) bool { return visit(c) } @@ -296,7 +296,7 @@ func EnumerateChildren(ctx context.Context, getLinks GetLinks, root *cid.Cid, vi // EnumerateChildrenDepth walks the dag below the given root and passes the // current depth to a given visit function. The visit function can be used to // limit DAG exploration. -func EnumerateChildrenDepth(ctx context.Context, getLinks GetLinks, root *cid.Cid, depth int, visit func(*cid.Cid, int) bool) error { +func EnumerateChildrenDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, depth int, visit func(cid.Cid, int) bool) error { links, err := getLinks(ctx, root) if err != nil { return err @@ -348,8 +348,8 @@ var FetchGraphConcurrency = 8 // fetches children in parallel. // // NOTE: It *does not* make multiple concurrent calls to the passed `visit` function. -func EnumerateChildrenAsync(ctx context.Context, getLinks GetLinks, c *cid.Cid, visit func(*cid.Cid) bool) error { - visitDepth := func(c *cid.Cid, depth int) bool { +func EnumerateChildrenAsync(ctx context.Context, getLinks GetLinks, c cid.Cid, visit func(cid.Cid) bool) error { + visitDepth := func(c cid.Cid, depth int) bool { return visit(c) } @@ -360,9 +360,9 @@ func EnumerateChildrenAsync(ctx context.Context, getLinks GetLinks, c *cid.Cid, // that it fetches children in parallel (down to a maximum depth in the graph). // // NOTE: It *does not* make multiple concurrent calls to the passed `visit` function. -func EnumerateChildrenAsyncDepth(ctx context.Context, getLinks GetLinks, c *cid.Cid, startDepth int, visit func(*cid.Cid, int) bool) error { +func EnumerateChildrenAsyncDepth(ctx context.Context, getLinks GetLinks, c cid.Cid, startDepth int, visit func(cid.Cid, int) bool) error { type cidDepth struct { - cid *cid.Cid + cid cid.Cid depth int } diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index cffaf20f0..a56aca586 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -57,7 +57,7 @@ func makeDepthTestingGraph(t *testing.T, ds ipld.DAGService) ipld.Node { } // Check that all children of root are in the given set and in the datastore -func traverseAndCheck(t *testing.T, root ipld.Node, ds ipld.DAGService, hasF func(c *cid.Cid) bool) { +func traverseAndCheck(t *testing.T, root ipld.Node, ds ipld.DAGService, hasF func(c cid.Cid) bool) { // traverse dag and check for _, lnk := range root.Links() { c := lnk.Cid @@ -333,7 +333,7 @@ func TestFetchGraph(t *testing.T) { offlineDS := NewDAGService(bs) - err = EnumerateChildren(context.Background(), offlineDS.GetLinks, root.Cid(), func(_ *cid.Cid) bool { return true }) + err = EnumerateChildren(context.Background(), offlineDS.GetLinks, root.Cid(), func(_ cid.Cid) bool { return true }) if err != nil { t.Fatal(err) } @@ -373,7 +373,7 @@ func TestFetchGraphWithDepthLimit(t *testing.T) { offlineDS := NewDAGService(bs) set := make(map[string]int) - visitF := func(c *cid.Cid, depth int) bool { + visitF := func(c cid.Cid, depth int) bool { if tc.depthLim < 0 || depth <= tc.depthLim { set[string(c.Bytes())] = depth return true @@ -649,7 +649,7 @@ func TestGetManyDuplicate(t *testing.T) { if err := srv.Add(ctx, nd); err != nil { t.Fatal(err) } - nds := srv.GetMany(ctx, []*cid.Cid{nd.Cid(), nd.Cid(), nd.Cid()}) + nds := srv.GetMany(ctx, []cid.Cid{nd.Cid(), nd.Cid(), nd.Cid()}) out, ok := <-nds if !ok { t.Fatal("expecting node foo") @@ -742,7 +742,7 @@ func testProgressIndicator(t *testing.T, depth int) { } } -func mkDag(ds ipld.DAGService, depth int) (*cid.Cid, int) { +func mkDag(ds ipld.DAGService, depth int) (cid.Cid, int) { ctx := context.Background() totalChildren := 0 diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index cc028537f..fb96fc65e 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -25,7 +25,7 @@ type ProtoNode struct { // cache encoded/marshaled value encoded []byte - cached *cid.Cid + cached cid.Cid // builder specifies cid version and hashing function builder cid.Builder @@ -79,7 +79,7 @@ func (n *ProtoNode) SetCidBuilder(builder cid.Builder) { } else { n.builder = builder.WithCodec(cid.DagProtobuf) n.encoded = nil - n.cached = nil + n.cached = cid.Undef } } @@ -219,7 +219,7 @@ func (n *ProtoNode) Data() []byte { // SetData stores data in this nodes. func (n *ProtoNode) SetData(d []byte) { n.encoded = nil - n.cached = nil + n.cached = cid.Undef n.data = d } @@ -305,8 +305,8 @@ func (n *ProtoNode) MarshalJSON() ([]byte, error) { // Cid returns the node's Cid, calculated according to its prefix // and raw data contents. -func (n *ProtoNode) Cid() *cid.Cid { - if n.encoded != nil && n.cached != nil { +func (n *ProtoNode) Cid() cid.Cid { + if n.encoded != nil && n.cached.Defined() { return n.cached } diff --git a/ipld/merkledag/readonly_test.go b/ipld/merkledag/readonly_test.go index 285cc208b..8beb8d50e 100644 --- a/ipld/merkledag/readonly_test.go +++ b/ipld/merkledag/readonly_test.go @@ -22,7 +22,7 @@ func TestReadonlyProperties(t *testing.T) { NewRawNode([]byte("foo3")), NewRawNode([]byte("foo4")), } - cids := []*cid.Cid{ + cids := []cid.Cid{ nds[0].Cid(), nds[1].Cid(), nds[2].Cid(), diff --git a/ipld/merkledag/rwservice.go b/ipld/merkledag/rwservice.go index 4444d9778..a916350a6 100644 --- a/ipld/merkledag/rwservice.go +++ b/ipld/merkledag/rwservice.go @@ -27,21 +27,21 @@ func (cs *ComboService) AddMany(ctx context.Context, nds []ipld.Node) error { } // Get fetches a node using the Read DAGService. -func (cs *ComboService) Get(ctx context.Context, c *cid.Cid) (ipld.Node, error) { +func (cs *ComboService) Get(ctx context.Context, c cid.Cid) (ipld.Node, error) { return cs.Read.Get(ctx, c) } // GetMany fetches nodes using the Read DAGService. -func (cs *ComboService) GetMany(ctx context.Context, cids []*cid.Cid) <-chan *ipld.NodeOption { +func (cs *ComboService) GetMany(ctx context.Context, cids []cid.Cid) <-chan *ipld.NodeOption { return cs.Read.GetMany(ctx, cids) } // Remove deletes a node using the Write DAGService. -func (cs *ComboService) Remove(ctx context.Context, c *cid.Cid) error { +func (cs *ComboService) Remove(ctx context.Context, c cid.Cid) error { return cs.Write.Remove(ctx, c) } // RemoveMany deletes nodes using the Write DAGService. -func (cs *ComboService) RemoveMany(ctx context.Context, cids []*cid.Cid) error { +func (cs *ComboService) RemoveMany(ctx context.Context, cids []cid.Cid) error { return cs.Write.RemoveMany(ctx, cids) } From d0d8aabc09a4de680b8f7ae00dd0fabdf164d295 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 7 Sep 2018 14:53:33 -0400 Subject: [PATCH 2602/3817] gx update and fix code to use new Cid type This commit was moved from ipfs/go-path@5b015d978be6dc402ea14ba8e58a3f7547780f03 --- path/path.go | 8 ++++---- path/resolver/resolver.go | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/path/path.go b/path/path.go index 18c187bf0..7754ef1ea 100644 --- a/path/path.go +++ b/path/path.go @@ -36,7 +36,7 @@ func FromString(s string) Path { } // FromCid safely converts a cid.Cid type to a Path type. -func FromCid(c *cid.Cid) Path { +func FromCid(c cid.Cid) Path { return Path("/ipfs/" + c.String()) } @@ -160,7 +160,7 @@ func SplitList(pth string) []string { // SplitAbsPath clean up and split fpath. It extracts the first component (which // must be a Multihash) and return it separately. -func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) { +func SplitAbsPath(fpath Path) (cid.Cid, []string, error) { parts := fpath.Segments() if parts[0] == "ipfs" || parts[0] == "ipld" { parts = parts[1:] @@ -168,13 +168,13 @@ func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) { // if nothing, bail. if len(parts) == 0 { - return nil, nil, ErrNoComponents + return cid.Cid{}, nil, ErrNoComponents } c, err := cid.Decode(parts[0]) // first element in the path is a cid if err != nil { - return nil, nil, err + return cid.Cid{}, nil, err } return c, parts[1:], nil diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index f5e3862a6..352004f52 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -25,7 +25,7 @@ var ErrNoComponents = errors.New( // ErrNoLink is returned when a link is not found in a path type ErrNoLink struct { Name string - Node *cid.Cid + Node cid.Cid } // Error implements the Error interface for ErrNoLink with a useful @@ -57,10 +57,10 @@ func NewBasicResolver(ds ipld.DAGService) *Resolver { // ResolveToLastNode walks the given path and returns the cid of the last node // referenced by the path -func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (*cid.Cid, []string, error) { +func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (cid.Cid, []string, error) { c, p, err := path.SplitAbsPath(fpath) if err != nil { - return nil, nil, err + return cid.Cid{}, nil, err } if len(p) == 0 { @@ -69,7 +69,7 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (*cid nd, err := r.DAG.Get(ctx, c) if err != nil { - return nil, nil, err + return cid.Cid{}, nil, err } for len(p) > 0 { @@ -83,12 +83,12 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (*cid } if err != nil { - return nil, nil, err + return cid.Cid{}, nil, err } next, err := lnk.GetNode(ctx, r.DAG) if err != nil { - return nil, nil, err + return cid.Cid{}, nil, err } nd = next p = rest @@ -101,15 +101,15 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (*cid // Confirm the path exists within the object val, rest, err := nd.Resolve(p) if err != nil { - return nil, nil, err + return cid.Cid{}, nil, err } if len(rest) > 0 { - return nil, nil, errors.New("path failed to resolve fully") + return cid.Cid{}, nil, errors.New("path failed to resolve fully") } switch val.(type) { case *ipld.Link: - return nil, nil, errors.New("inconsistent ResolveOnce / nd.Resolve") + return cid.Cid{}, nil, errors.New("inconsistent ResolveOnce / nd.Resolve") default: return nd.Cid(), p, nil } From 6df2631e8d955c0d977b246d8034ce62c0f0546e Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 7 Sep 2018 14:56:35 -0400 Subject: [PATCH 2603/3817] gx update and fix code to use new Cid type This commit was moved from ipfs/go-unixfs@ba2e130f78a97b0f27f55758c4a75f4fb05d907d --- unixfs/io/pbdagreader.go | 6 +++--- unixfs/mod/dagmodifier.go | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 8e7872e8e..5c4462850 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -29,7 +29,7 @@ type PBDagReader struct { promises []*ipld.NodePromise // the cid of each child of the current node - links []*cid.Cid + links []cid.Cid // the index of the child link currently being read from linkPosition int @@ -151,9 +151,9 @@ func (dr *PBDagReader) loadBufNode(node ipld.Node) error { } } -func getLinkCids(n ipld.Node) []*cid.Cid { +func getLinkCids(n ipld.Node) []cid.Cid { links := n.Links() - out := make([]*cid.Cid, 0, len(links)) + out := make([]cid.Cid, 0, len(links)) for _, l := range links { out = append(out, l.Cid) } diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index 0f03cb6d3..c217be553 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -233,25 +233,25 @@ func (dm *DagModifier) Sync() error { // modifyDag writes the data in 'dm.wrBuf' over the data in 'node' starting at 'offset' // returns the new key of the passed in node. -func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64) (*cid.Cid, error) { +func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64) (cid.Cid, error) { // If we've reached a leaf node. if len(n.Links()) == 0 { switch nd0 := n.(type) { case *mdag.ProtoNode: f, err := ft.FromBytes(nd0.Data()) if err != nil { - return nil, err + return cid.Cid{}, err } _, err = dm.wrBuf.Read(f.Data[offset:]) if err != nil && err != io.EOF { - return nil, err + return cid.Cid{}, err } // Update newly written node.. b, err := proto.Marshal(f) if err != nil { - return nil, err + return cid.Cid{}, err } nd := new(mdag.ProtoNode) @@ -259,7 +259,7 @@ func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64) (*cid.Cid, error) { nd.SetCidBuilder(nd0.CidBuilder()) err = dm.dagserv.Add(dm.ctx, nd) if err != nil { - return nil, err + return cid.Cid{}, err } return nd.Cid(), nil @@ -273,7 +273,7 @@ func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64) (*cid.Cid, error) { // copy in new data n, err := dm.wrBuf.Read(bytes[offset:]) if err != nil && err != io.EOF { - return nil, err + return cid.Cid{}, err } // copy remaining data @@ -284,11 +284,11 @@ func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64) (*cid.Cid, error) { nd, err := mdag.NewRawNodeWPrefix(bytes, nd0.Cid().Prefix()) if err != nil { - return nil, err + return cid.Cid{}, err } err = dm.dagserv.Add(dm.ctx, nd) if err != nil { - return nil, err + return cid.Cid{}, err } return nd.Cid(), nil @@ -297,12 +297,12 @@ func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64) (*cid.Cid, error) { node, ok := n.(*mdag.ProtoNode) if !ok { - return nil, ErrNotUnixfs + return cid.Cid{}, ErrNotUnixfs } f, err := ft.FromBytes(node.Data()) if err != nil { - return nil, err + return cid.Cid{}, err } var cur uint64 @@ -311,12 +311,12 @@ func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64) (*cid.Cid, error) { if cur+bs > offset { child, err := node.Links()[i].GetNode(dm.ctx, dm.dagserv) if err != nil { - return nil, err + return cid.Cid{}, err } k, err := dm.modifyDag(child, offset-cur) if err != nil { - return nil, err + return cid.Cid{}, err } node.Links()[i].Cid = k @@ -324,7 +324,7 @@ func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64) (*cid.Cid, error) { // Recache serialized node _, err = node.EncodeProtobuf(true) if err != nil { - return nil, err + return cid.Cid{}, err } if dm.wrBuf.Len() == 0 { From bdc5f751aefcd5da3f0f80cd126c21a3bbeabf54 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Fri, 7 Sep 2018 15:00:42 -0400 Subject: [PATCH 2604/3817] gx update and fix code to use new Cid type This commit was moved from ipfs/go-mfs@8f366a5bd91f4ee9af08a5065fec5be704e388c2 --- mfs/mfs_test.go | 2 +- mfs/repub_test.go | 6 +++--- mfs/system.go | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 63c9bff63..e840f6c06 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -196,7 +196,7 @@ func setupRoot(ctx context.Context, t *testing.T) (ipld.DAGService, *Root) { ds := getDagserv(t) root := emptyDirNode() - rt, err := NewRoot(ctx, ds, root, func(ctx context.Context, c *cid.Cid) error { + rt, err := NewRoot(ctx, ds, root, func(ctx context.Context, c cid.Cid) error { fmt.Println("PUBLISHED: ", c) return nil }) diff --git a/mfs/repub_test.go b/mfs/repub_test.go index cfc056a59..d81ffd04e 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -18,7 +18,7 @@ func TestRepublisher(t *testing.T) { pub := make(chan struct{}) - pf := func(ctx context.Context, c *cid.Cid) error { + pf := func(ctx context.Context, c cid.Cid) error { pub <- struct{}{} return nil } @@ -29,7 +29,7 @@ func TestRepublisher(t *testing.T) { rp := NewRepublisher(ctx, pf, tshort, tlong) go rp.Run() - rp.Update(nil) + rp.Update(cid.Undef) // should hit short timeout select { @@ -42,7 +42,7 @@ func TestRepublisher(t *testing.T) { go func() { for { - rp.Update(nil) + rp.Update(cid.Undef) time.Sleep(time.Millisecond * 10) select { case <-cctx.Done(): diff --git a/mfs/system.go b/mfs/system.go index bd799880e..100cfa412 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -58,7 +58,7 @@ type Root struct { } // PubFunc is the function used by the `publish()` method. -type PubFunc func(context.Context, *cid.Cid) error +type PubFunc func(context.Context, cid.Cid) error // NewRoot creates a new Root and starts up a republisher routine for it. func NewRoot(parent context.Context, ds ipld.DAGService, node *dag.ProtoNode, pf PubFunc) (*Root, error) { @@ -182,8 +182,8 @@ type Republisher struct { cancel func() lk sync.Mutex - val *cid.Cid - lastpub *cid.Cid + val cid.Cid + lastpub cid.Cid } // NewRepublisher creates a new Republisher object to republish the given root @@ -201,7 +201,7 @@ func NewRepublisher(ctx context.Context, pf PubFunc, tshort, tlong time.Duration } } -func (p *Republisher) setVal(c *cid.Cid) { +func (p *Republisher) setVal(c cid.Cid) { p.lk.Lock() defer p.lk.Unlock() p.val = c @@ -231,7 +231,7 @@ func (p *Republisher) Close() error { // Touch signals that an update has occurred since the last publish. // Multiple consecutive touches may extend the time period before // the next Publish occurs in order to more efficiently batch updates. -func (np *Republisher) Update(c *cid.Cid) { +func (np *Republisher) Update(c cid.Cid) { np.setVal(c) select { case np.Publish <- struct{}{}: From ad5f80b075dabfcbe5d6949626c9e0e7b1e0563f Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 11 Sep 2018 22:19:44 -0400 Subject: [PATCH 2605/3817] gx update and fix code to use new Cid type License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@e6e069367f018770ecaa997ba0d5f34e6c362319 --- pinning/pinner/gc/gc.go | 32 +++++++++--------- pinning/pinner/pin.go | 66 +++++++++++++++++++------------------- pinning/pinner/pin_test.go | 18 +++++------ pinning/pinner/set.go | 26 +++++++-------- pinning/pinner/set_test.go | 14 ++++---- 5 files changed, 78 insertions(+), 78 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 9628f7ad7..abb05f93c 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,16 +8,16 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmNr4E8z9bGTztvHJktp7uQaMdx9p3r9Asrq6eYk7iCh4a/go-merkledag" - bserv "gx/ipfs/QmQLG22wSEStiociTSKQpZAuuaaWoF1B3iKyjPFvWiTQ77/go-blockservice" + dag "gx/ipfs/QmURqt1jB9Yu3X4Tr9WQJf36QGN7vi8mGTzjnX2ij1CJwC/go-merkledag" + bserv "gx/ipfs/QmYHXfGs5GVxXN233aFr5Jenvd7NG4qZ7pmjfyz7yvG93m/go-blockservice" - offline "gx/ipfs/QmPuLWvxK1vg6ckKUpT53Dow9VLCcQGdL5Trwxa8PTLp7r/go-ipfs-exchange-offline" + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" dstore "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - "gx/ipfs/QmVUhfewLZpSaAiBYCpw2krYMaiVmFuhr2iurQLuRoU6sD/go-verifcid" - ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" - cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" - bstore "gx/ipfs/Qmeg56ecxRnVv7VWViMrDeEMoBHaNFMs4vQnyQrJ79Zz7i/go-ipfs-blockstore" + "gx/ipfs/QmVkMRSkXrpjqrroEXWuYBvDBnXCdMMY6gsKicBGVGUqKT/go-verifcid" + offline "gx/ipfs/QmXHsHBveZF6ueKzDJbUg476gmrbzoR1yijiyH5SZAEuDT/go-ipfs-exchange-offline" + ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" + bstore "gx/ipfs/QmeMussyD8s3fQ3pM19ZsfbxvomEqPV9FvczLMWyBDYSnS/go-ipfs-blockstore" ) var log = logging.Logger("gc") @@ -25,7 +25,7 @@ var log = logging.Logger("gc") // Result represents an incremental output from a garbage collection // run. It contains either an error, or the cid of a removed object. type Result struct { - KeyRemoved *cid.Cid + KeyRemoved cid.Cid Error error } @@ -38,7 +38,7 @@ type Result struct { // // The routine then iterates over every block in the blockstore and // deletes any block that is not found in the marked set. -func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn pin.Pinner, bestEffortRoots []*cid.Cid) <-chan Result { +func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn pin.Pinner, bestEffortRoots []cid.Cid) <-chan Result { elock := log.EventBegin(ctx, "GC.lockWait") unlocker := bs.GCLock() @@ -130,8 +130,8 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn // Descendants recursively finds all the descendants of the given roots and // adds them to the given cid.Set, using the provided dag.GetLinks function // to walk the tree. -func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots []*cid.Cid) error { - verifyGetLinks := func(ctx context.Context, c *cid.Cid) ([]*ipld.Link, error) { +func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots []cid.Cid) error { + verifyGetLinks := func(ctx context.Context, c cid.Cid) ([]*ipld.Link, error) { err := verifcid.ValidateCid(c) if err != nil { return nil, err @@ -168,12 +168,12 @@ func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots // ColoredSet computes the set of nodes in the graph that are pinned by the // pins in the given pinner. -func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffortRoots []*cid.Cid, output chan<- Result) (*cid.Set, error) { +func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffortRoots []cid.Cid, output chan<- Result) (*cid.Set, error) { // KeySet currently implemented in memory, in the future, may be bloom filter or // disk backed to conserve memory. errors := false gcs := cid.NewSet() - getLinks := func(ctx context.Context, cid *cid.Cid) ([]*ipld.Link, error) { + getLinks := func(ctx context.Context, cid cid.Cid) ([]*ipld.Link, error) { links, err := ipld.GetLinks(ctx, ng, cid) if err != nil { errors = true @@ -187,7 +187,7 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo output <- Result{Error: err} } - bestEffortGetLinks := func(ctx context.Context, cid *cid.Cid) ([]*ipld.Link, error) { + bestEffortGetLinks := func(ctx context.Context, cid cid.Cid) ([]*ipld.Link, error) { links, err := ipld.GetLinks(ctx, ng, cid) if err != nil && err != ipld.ErrNotFound { errors = true @@ -230,7 +230,7 @@ var ErrCannotDeleteSomeBlocks = errors.New("garbage collection incomplete: could // CannotFetchLinksError provides detailed information about which links // could not be fetched and can appear as a Result in the GC output channel. type CannotFetchLinksError struct { - Key *cid.Cid + Key cid.Cid Err error } @@ -244,7 +244,7 @@ func (e *CannotFetchLinksError) Error() string { // blocks could not be deleted and can appear as a Result in the GC output // channel. type CannotDeleteBlockError struct { - Key *cid.Cid + Key cid.Cid Err error } diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index cff9e4ae5..f4281667e 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,19 +10,19 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmNr4E8z9bGTztvHJktp7uQaMdx9p3r9Asrq6eYk7iCh4a/go-merkledag" + mdag "gx/ipfs/QmURqt1jB9Yu3X4Tr9WQJf36QGN7vi8mGTzjnX2ij1CJwC/go-merkledag" + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" - cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" + ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) var log = logging.Logger("pin") var pinDatastoreKey = ds.NewKey("/local/pins") -var emptyKey *cid.Cid +var emptyKey cid.Cid func init() { e, err := cid.Decode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n") @@ -105,50 +105,50 @@ func StringToMode(s string) (Mode, bool) { type Pinner interface { // IsPinned returns whether or not the given cid is pinned // and an explanation of why its pinned - IsPinned(*cid.Cid) (string, bool, error) + IsPinned(cid.Cid) (string, bool, error) // IsPinnedWithType returns whether or not the given cid is pinned with the // given pin type, as well as returning the type of pin its pinned with. - IsPinnedWithType(*cid.Cid, Mode) (string, bool, error) + IsPinnedWithType(cid.Cid, Mode) (string, bool, error) // Pin the given node, optionally recursively. Pin(ctx context.Context, node ipld.Node, recursive bool) error // Unpin the given cid. If recursive is true, removes either a recursive or // a direct pin. If recursive is false, only removes a direct pin. - Unpin(ctx context.Context, cid *cid.Cid, recursive bool) error + Unpin(ctx context.Context, cid cid.Cid, recursive bool) error // Update updates a recursive pin from one cid to another // this is more efficient than simply pinning the new one and unpinning the // old one - Update(ctx context.Context, from, to *cid.Cid, unpin bool) error + Update(ctx context.Context, from, to cid.Cid, unpin bool) error // Check if a set of keys are pinned, more efficient than // calling IsPinned for each key - CheckIfPinned(cids ...*cid.Cid) ([]Pinned, error) + CheckIfPinned(cids ...cid.Cid) ([]Pinned, error) // PinWithMode is for manually editing the pin structure. Use with // care! If used improperly, garbage collection may not be // successful. - PinWithMode(*cid.Cid, Mode) + PinWithMode(cid.Cid, Mode) // RemovePinWithMode is for manually editing the pin structure. // Use with care! If used improperly, garbage collection may not // be successful. - RemovePinWithMode(*cid.Cid, Mode) + RemovePinWithMode(cid.Cid, Mode) // Flush writes the pin state to the backing datastore Flush() error // DirectKeys returns all directly pinned cids - DirectKeys() []*cid.Cid + DirectKeys() []cid.Cid // DirectKeys returns all recursively pinned cids - RecursiveKeys() []*cid.Cid + RecursiveKeys() []cid.Cid // InternalPins returns all cids kept pinned for the internal state of the // pinner - InternalPins() []*cid.Cid + InternalPins() []cid.Cid } // Pinned represents CID which has been pinned with a pinning strategy. @@ -156,9 +156,9 @@ type Pinner interface { // case that the item is not pinned directly (but rather pinned recursively // by some ascendant). type Pinned struct { - Key *cid.Cid + Key cid.Cid Mode Mode - Via *cid.Cid + Via cid.Cid } // Pinned returns whether or not the given cid is pinned @@ -254,7 +254,7 @@ func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { var ErrNotPinned = fmt.Errorf("not pinned") // Unpin a given key -func (p *pinner) Unpin(ctx context.Context, c *cid.Cid, recursive bool) error { +func (p *pinner) Unpin(ctx context.Context, c cid.Cid, recursive bool) error { p.lock.Lock() defer p.lock.Unlock() reason, pinned, err := p.isPinnedWithType(c, Any) @@ -279,13 +279,13 @@ func (p *pinner) Unpin(ctx context.Context, c *cid.Cid, recursive bool) error { } } -func (p *pinner) isInternalPin(c *cid.Cid) bool { +func (p *pinner) isInternalPin(c cid.Cid) bool { return p.internalPin.Has(c) } // IsPinned returns whether or not the given key is pinned // and an explanation of why its pinned -func (p *pinner) IsPinned(c *cid.Cid) (string, bool, error) { +func (p *pinner) IsPinned(c cid.Cid) (string, bool, error) { p.lock.RLock() defer p.lock.RUnlock() return p.isPinnedWithType(c, Any) @@ -293,7 +293,7 @@ func (p *pinner) IsPinned(c *cid.Cid) (string, bool, error) { // IsPinnedWithType returns whether or not the given cid is pinned with the // given pin type, as well as returning the type of pin its pinned with. -func (p *pinner) IsPinnedWithType(c *cid.Cid, mode Mode) (string, bool, error) { +func (p *pinner) IsPinnedWithType(c cid.Cid, mode Mode) (string, bool, error) { p.lock.RLock() defer p.lock.RUnlock() return p.isPinnedWithType(c, mode) @@ -301,7 +301,7 @@ func (p *pinner) IsPinnedWithType(c *cid.Cid, mode Mode) (string, bool, error) { // isPinnedWithType is the implementation of IsPinnedWithType that does not lock. // intended for use by other pinned methods that already take locks -func (p *pinner) isPinnedWithType(c *cid.Cid, mode Mode) (string, bool, error) { +func (p *pinner) isPinnedWithType(c cid.Cid, mode Mode) (string, bool, error) { switch mode { case Any, Direct, Indirect, Recursive, Internal: default: @@ -346,7 +346,7 @@ func (p *pinner) isPinnedWithType(c *cid.Cid, mode Mode) (string, bool, error) { // CheckIfPinned Checks if a set of keys are pinned, more efficient than // calling IsPinned for each key, returns the pinned status of cid(s) -func (p *pinner) CheckIfPinned(cids ...*cid.Cid) ([]Pinned, error) { +func (p *pinner) CheckIfPinned(cids ...cid.Cid) ([]Pinned, error) { p.lock.RLock() defer p.lock.RUnlock() pinned := make([]Pinned, 0, len(cids)) @@ -366,8 +366,8 @@ func (p *pinner) CheckIfPinned(cids ...*cid.Cid) ([]Pinned, error) { } // Now walk all recursive pins to check for indirect pins - var checkChildren func(*cid.Cid, *cid.Cid) error - checkChildren = func(rk, parentKey *cid.Cid) error { + var checkChildren func(cid.Cid, cid.Cid) error + checkChildren = func(rk, parentKey cid.Cid) error { links, err := ipld.GetLinks(context.TODO(), p.dserv, parentKey) if err != nil { return err @@ -414,7 +414,7 @@ func (p *pinner) CheckIfPinned(cids ...*cid.Cid) ([]Pinned, error) { // RemovePinWithMode is for manually editing the pin structure. // Use with care! If used improperly, garbage collection may not // be successful. -func (p *pinner) RemovePinWithMode(c *cid.Cid, mode Mode) { +func (p *pinner) RemovePinWithMode(c cid.Cid, mode Mode) { p.lock.Lock() defer p.lock.Unlock() switch mode { @@ -428,7 +428,7 @@ func (p *pinner) RemovePinWithMode(c *cid.Cid, mode Mode) { } } -func cidSetWithValues(cids []*cid.Cid) *cid.Set { +func cidSetWithValues(cids []cid.Cid) *cid.Set { out := cid.NewSet() for _, c := range cids { out.Add(c) @@ -493,19 +493,19 @@ func LoadPinner(d ds.Datastore, dserv, internal ipld.DAGService) (Pinner, error) } // DirectKeys returns a slice containing the directly pinned keys -func (p *pinner) DirectKeys() []*cid.Cid { +func (p *pinner) DirectKeys() []cid.Cid { return p.directPin.Keys() } // RecursiveKeys returns a slice containing the recursively pinned keys -func (p *pinner) RecursiveKeys() []*cid.Cid { +func (p *pinner) RecursiveKeys() []cid.Cid { return p.recursePin.Keys() } // Update updates a recursive pin from one cid to another // this is more efficient than simply pinning the new one and unpinning the // old one -func (p *pinner) Update(ctx context.Context, from, to *cid.Cid, unpin bool) error { +func (p *pinner) Update(ctx context.Context, from, to cid.Cid, unpin bool) error { p.lock.Lock() defer p.lock.Unlock() @@ -579,17 +579,17 @@ func (p *pinner) Flush() error { // InternalPins returns all cids kept pinned for the internal state of the // pinner -func (p *pinner) InternalPins() []*cid.Cid { +func (p *pinner) InternalPins() []cid.Cid { p.lock.Lock() defer p.lock.Unlock() - var out []*cid.Cid + var out []cid.Cid out = append(out, p.internalPin.Keys()...) return out } // PinWithMode allows the user to have fine grained control over pin // counts -func (p *pinner) PinWithMode(c *cid.Cid, mode Mode) { +func (p *pinner) PinWithMode(c cid.Cid, mode Mode) { p.lock.Lock() defer p.lock.Unlock() switch mode { @@ -602,7 +602,7 @@ func (p *pinner) PinWithMode(c *cid.Cid, mode Mode) { // hasChild recursively looks for a Cid among the children of a root Cid. // The visit function can be used to shortcut already-visited branches. -func hasChild(ng ipld.NodeGetter, root *cid.Cid, child *cid.Cid, visit func(*cid.Cid) bool) (bool, error) { +func hasChild(ng ipld.NodeGetter, root cid.Cid, child cid.Cid, visit func(cid.Cid) bool) (bool, error) { links, err := ipld.GetLinks(context.TODO(), ng, root) if err != nil { return false, err diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 4dc5e3565..70bcd722b 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,20 +5,20 @@ import ( "testing" "time" - mdag "gx/ipfs/QmNr4E8z9bGTztvHJktp7uQaMdx9p3r9Asrq6eYk7iCh4a/go-merkledag" - bs "gx/ipfs/QmQLG22wSEStiociTSKQpZAuuaaWoF1B3iKyjPFvWiTQ77/go-blockservice" + mdag "gx/ipfs/QmURqt1jB9Yu3X4Tr9WQJf36QGN7vi8mGTzjnX2ij1CJwC/go-merkledag" + bs "gx/ipfs/QmYHXfGs5GVxXN233aFr5Jenvd7NG4qZ7pmjfyz7yvG93m/go-blockservice" + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmPuLWvxK1vg6ckKUpT53Dow9VLCcQGdL5Trwxa8PTLp7r/go-ipfs-exchange-offline" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" - cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" - blockstore "gx/ipfs/Qmeg56ecxRnVv7VWViMrDeEMoBHaNFMs4vQnyQrJ79Zz7i/go-ipfs-blockstore" + offline "gx/ipfs/QmXHsHBveZF6ueKzDJbUg476gmrbzoR1yijiyH5SZAEuDT/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmeMussyD8s3fQ3pM19ZsfbxvomEqPV9FvczLMWyBDYSnS/go-ipfs-blockstore" ) var rand = util.NewTimeSeededRand() -func randNode() (*mdag.ProtoNode, *cid.Cid) { +func randNode() (*mdag.ProtoNode, cid.Cid) { nd := new(mdag.ProtoNode) nd.SetData(make([]byte, 32)) rand.Read(nd.Data()) @@ -26,7 +26,7 @@ func randNode() (*mdag.ProtoNode, *cid.Cid) { return nd, k } -func assertPinned(t *testing.T, p Pinner, c *cid.Cid, failmsg string) { +func assertPinned(t *testing.T, p Pinner, c cid.Cid, failmsg string) { _, pinned, err := p.IsPinned(c) if err != nil { t.Fatal(err) @@ -37,7 +37,7 @@ func assertPinned(t *testing.T, p Pinner, c *cid.Cid, failmsg string) { } } -func assertUnpinned(t *testing.T, p Pinner, c *cid.Cid, failmsg string) { +func assertUnpinned(t *testing.T, p Pinner, c cid.Cid, failmsg string) { _, pinned, err := p.IsPinned(c) if err != nil { t.Fatal(err) @@ -187,7 +187,7 @@ func TestIsPinnedLookup(t *testing.T) { p := NewPinner(dstore, dserv, dserv) aNodes := make([]*mdag.ProtoNode, aBranchLen) - aKeys := make([]*cid.Cid, aBranchLen) + aKeys := make([]cid.Cid, aBranchLen) for i := 0; i < aBranchLen; i++ { a, _ := randNode() if i >= 1 { diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index f0853d53a..53d51d156 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,10 +10,10 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmNr4E8z9bGTztvHJktp7uQaMdx9p3r9Asrq6eYk7iCh4a/go-merkledag" + "gx/ipfs/QmURqt1jB9Yu3X4Tr9WQJf36QGN7vi8mGTzjnX2ij1CJwC/go-merkledag" - ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" - cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" + ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) @@ -25,7 +25,7 @@ const ( maxItems = 8192 ) -func hash(seed uint32, c *cid.Cid) uint32 { +func hash(seed uint32, c cid.Cid) uint32 { var buf [4]byte binary.LittleEndian.PutUint32(buf[:], seed) h := fnv.New32a() @@ -34,9 +34,9 @@ func hash(seed uint32, c *cid.Cid) uint32 { return h.Sum32() } -type itemIterator func() (c *cid.Cid, ok bool) +type itemIterator func() (c cid.Cid, ok bool) -type keyObserver func(*cid.Cid) +type keyObserver func(cid.Cid) type sortByHash struct { links []*ipld.Link @@ -97,7 +97,7 @@ func storeItems(ctx context.Context, dag ipld.DAGService, estimatedLen uint64, d sort.Stable(s) } - hashed := make([][]*cid.Cid, defaultFanout) + hashed := make([][]cid.Cid, defaultFanout) for { // This loop essentially enumerates every single item in the set // and maps them all into a set of buckets. Each bucket will be recursively @@ -238,7 +238,7 @@ func walkItems(ctx context.Context, dag ipld.DAGService, n *merkledag.ProtoNode, return nil } -func loadSet(ctx context.Context, dag ipld.DAGService, root *merkledag.ProtoNode, name string, internalKeys keyObserver) ([]*cid.Cid, error) { +func loadSet(ctx context.Context, dag ipld.DAGService, root *merkledag.ProtoNode, name string, internalKeys keyObserver) ([]cid.Cid, error) { l, err := root.GetNodeLink(name) if err != nil { return nil, err @@ -257,7 +257,7 @@ func loadSet(ctx context.Context, dag ipld.DAGService, root *merkledag.ProtoNode return nil, merkledag.ErrNotProtobuf } - var res []*cid.Cid + var res []cid.Cid walk := func(idx int, link *ipld.Link) error { res = append(res, link.Cid) return nil @@ -269,10 +269,10 @@ func loadSet(ctx context.Context, dag ipld.DAGService, root *merkledag.ProtoNode return res, nil } -func getCidListIterator(cids []*cid.Cid) itemIterator { - return func() (c *cid.Cid, ok bool) { +func getCidListIterator(cids []cid.Cid) itemIterator { + return func() (c cid.Cid, ok bool) { if len(cids) == 0 { - return nil, false + return cid.Cid{}, false } first := cids[0] @@ -281,7 +281,7 @@ func getCidListIterator(cids []*cid.Cid) itemIterator { } } -func storeSet(ctx context.Context, dag ipld.DAGService, cids []*cid.Cid, internalKeys keyObserver) (*merkledag.ProtoNode, error) { +func storeSet(ctx context.Context, dag ipld.DAGService, cids []cid.Cid, internalKeys keyObserver) (*merkledag.ProtoNode, error) { iter := getCidListIterator(cids) n, err := storeItems(ctx, dag, uint64(len(cids)), 0, iter, internalKeys) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index e98025ed1..4a9f66d9c 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,17 +5,17 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmNr4E8z9bGTztvHJktp7uQaMdx9p3r9Asrq6eYk7iCh4a/go-merkledag" - bserv "gx/ipfs/QmQLG22wSEStiociTSKQpZAuuaaWoF1B3iKyjPFvWiTQ77/go-blockservice" + dag "gx/ipfs/QmURqt1jB9Yu3X4Tr9WQJf36QGN7vi8mGTzjnX2ij1CJwC/go-merkledag" + bserv "gx/ipfs/QmYHXfGs5GVxXN233aFr5Jenvd7NG4qZ7pmjfyz7yvG93m/go-blockservice" - offline "gx/ipfs/QmPuLWvxK1vg6ckKUpT53Dow9VLCcQGdL5Trwxa8PTLp7r/go-ipfs-exchange-offline" + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" - cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" - blockstore "gx/ipfs/Qmeg56ecxRnVv7VWViMrDeEMoBHaNFMs4vQnyQrJ79Zz7i/go-ipfs-blockstore" + offline "gx/ipfs/QmXHsHBveZF6ueKzDJbUg476gmrbzoR1yijiyH5SZAEuDT/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmeMussyD8s3fQ3pM19ZsfbxvomEqPV9FvczLMWyBDYSnS/go-ipfs-blockstore" ) -func ignoreCids(_ *cid.Cid) {} +func ignoreCids(_ cid.Cid) {} func objCount(d ds.Datastore) int { q := dsq.Query{KeysOnly: true} @@ -46,7 +46,7 @@ func TestSet(t *testing.T) { // an infinite recursion and crash (OOM) limit := uint32((defaultFanout * maxItems) + 1) - var inputs []*cid.Cid + var inputs []cid.Cid buf := make([]byte, 4) for i := uint32(0); i < limit; i++ { binary.BigEndian.PutUint32(buf, i) From a48e694f9d8c710e4354c9818c46ae76d5fece00 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 11 Sep 2018 22:19:44 -0400 Subject: [PATCH 2606/3817] gx update and fix code to use new Cid type License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/interface-go-ipfs-core@bdbcb4cff02fb5502fc59563e5cb51c641261390 --- coreiface/coreapi.go | 2 +- coreiface/dag.go | 2 +- coreiface/object.go | 6 +++--- coreiface/options/block.go | 2 +- coreiface/options/dag.go | 2 +- coreiface/path.go | 22 +++++++++++----------- coreiface/unixfs.go | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index 9811b75be..0053d472e 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -5,7 +5,7 @@ package iface import ( "context" - ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" + ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) // CoreAPI defines an unified interface to IPFS for Go programs diff --git a/coreiface/dag.go b/coreiface/dag.go index d3270928c..06bb91dce 100644 --- a/coreiface/dag.go +++ b/coreiface/dag.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" + ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) // DagOps groups operations that can be batched together diff --git a/coreiface/object.go b/coreiface/object.go index 750638a33..6b355a302 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -6,14 +6,14 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" - cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" + ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) // ObjectStat provides information about dag nodes type ObjectStat struct { // Cid is the CID of the node - Cid *cid.Cid + Cid cid.Cid // NumLinks is number of links the node contains NumLinks int diff --git a/coreiface/options/block.go b/coreiface/options/block.go index 36b3baa0e..6603136f3 100644 --- a/coreiface/options/block.go +++ b/coreiface/options/block.go @@ -2,8 +2,8 @@ package options import ( "fmt" + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" ) type BlockPutSettings struct { diff --git a/coreiface/options/dag.go b/coreiface/options/dag.go index 689bb5c53..4fdff0489 100644 --- a/coreiface/options/dag.go +++ b/coreiface/options/dag.go @@ -3,7 +3,7 @@ package options import ( "math" - cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) type DagPutSettings struct { diff --git a/coreiface/path.go b/coreiface/path.go index c2b4cd869..e11e20cf0 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,9 +1,9 @@ package iface import ( - ipfspath "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" + ipfspath "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" - cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) //TODO: merge with ipfspath so we don't depend on it @@ -65,7 +65,7 @@ type ResolvedPath interface { // * Calling Cid() will return `cidB` // * Calling Root() will return `cidRoot` // * Calling Remainder() will return `foo/bar` - Cid() *cid.Cid + Cid() cid.Cid // Root returns the CID of the root object of the path // @@ -74,7 +74,7 @@ type ResolvedPath interface { // "/ipfs/QmRoot/A/B", the Root method will return the CID of object QmRoot // // For more examples see the documentation of Cid() method - Root() *cid.Cid + Root() cid.Cid // Remainder returns unresolved part of the path // @@ -100,13 +100,13 @@ type path struct { // resolvedPath implements coreiface.resolvedPath type resolvedPath struct { path - cid *cid.Cid - root *cid.Cid + cid cid.Cid + root cid.Cid remainder string } // IpfsPath creates new /ipfs path from the provided CID -func IpfsPath(c *cid.Cid) ResolvedPath { +func IpfsPath(c cid.Cid) ResolvedPath { return &resolvedPath{ path: path{ipfspath.Path("/ipfs/" + c.String())}, cid: c, @@ -116,7 +116,7 @@ func IpfsPath(c *cid.Cid) ResolvedPath { } // IpldPath creates new /ipld path from the provided CID -func IpldPath(c *cid.Cid) ResolvedPath { +func IpldPath(c cid.Cid) ResolvedPath { return &resolvedPath{ path: path{ipfspath.Path("/ipld/" + c.String())}, cid: c, @@ -138,7 +138,7 @@ func ParsePath(p string) (Path, error) { // NewResolvedPath creates new ResolvedPath. This function performs no checks // and is intended to be used by resolver implementations. Incorrect inputs may // cause panics. Handle with care. -func NewResolvedPath(ipath ipfspath.Path, c *cid.Cid, root *cid.Cid, remainder string) ResolvedPath { +func NewResolvedPath(ipath ipfspath.Path, c cid.Cid, root cid.Cid, remainder string) ResolvedPath { return &resolvedPath{ path: path{ipath}, cid: c, @@ -163,11 +163,11 @@ func (p *path) Mutable() bool { return p.Namespace() == "ipns" } -func (p *resolvedPath) Cid() *cid.Cid { +func (p *resolvedPath) Cid() cid.Cid { return p.cid } -func (p *resolvedPath) Root() *cid.Cid { +func (p *resolvedPath) Root() cid.Cid { return p.root } diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 80f7ba396..4a3aff6fc 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -4,7 +4,7 @@ import ( "context" "io" - ipld "gx/ipfs/QmX5CsuHyVZeTLxgRSYkgLSDQKb9UjE8xnhQzCEJWWWFsC/go-ipld-format" + ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) // UnixfsAPI is the basic interface to immutable files in IPFS From 5507f3210c0b78dce571d0a0b9b20011bfd8c77a Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 11 Sep 2018 22:19:44 -0400 Subject: [PATCH 2607/3817] gx update and fix code to use new Cid type License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-namesys@0d3a5189bb9a920740be4b94201a5165c8670131 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 10 +++++----- namesys/namesys.go | 4 ++-- namesys/namesys_test.go | 6 +++--- namesys/proquint.go | 2 +- namesys/publisher.go | 6 +++--- namesys/publisher_test.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 4 ++-- namesys/routing.go | 8 ++++---- 14 files changed, 28 insertions(+), 28 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 0047b434e..fe044ffb4 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" + path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index 451521be4..9151ed64a 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" + path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 4961c72d9..e5e2ea159 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" + path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index a638b5f81..221500e1c 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" + path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index c5eda1737..7da72593c 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,20 +6,20 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" + path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" - mockrouting "gx/ipfs/QmRuUsZEg2WLCuidJGHVmE1NreHDmXWKLS466PKyDpXMhN/go-ipfs-routing/mock" - offline "gx/ipfs/QmRuUsZEg2WLCuidJGHVmE1NreHDmXWKLS466PKyDpXMhN/go-ipfs-routing/offline" + mockrouting "gx/ipfs/QmSbZCrt5cSiCNcXFZKoGjukcEf4DRdTzexqzEWATZDdz6/go-ipfs-routing/mock" + offline "gx/ipfs/QmSbZCrt5cSiCNcXFZKoGjukcEf4DRdTzexqzEWATZDdz6/go-ipfs-routing/offline" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" - routing "gx/ipfs/QmY9JUvS8kbgao3XbPh6WAV3ChE2nxGKhcGTHiwMC4gmcU/go-libp2p-routing" - ropts "gx/ipfs/QmY9JUvS8kbgao3XbPh6WAV3ChE2nxGKhcGTHiwMC4gmcU/go-libp2p-routing/options" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" record "gx/ipfs/QmdHb9aBELnQKTVhvvA3hsQbRgUAwsWUzBP2vZ6Y5FBYvE/go-libp2p-record" + routing "gx/ipfs/QmdKS5YtmuSWKuLLgbHG176mS3VX3AKiyVmaaiAfvgcuch/go-libp2p-routing" + ropts "gx/ipfs/QmdKS5YtmuSWKuLLgbHG176mS3VX3AKiyVmaaiAfvgcuch/go-libp2p-routing/options" pstore "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore" pstoremem "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore/pstoremem" ) diff --git a/namesys/namesys.go b/namesys/namesys.go index 9b8e6bff6..410c7e65c 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,15 +6,15 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" + path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - routing "gx/ipfs/QmY9JUvS8kbgao3XbPh6WAV3ChE2nxGKhcGTHiwMC4gmcU/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" + routing "gx/ipfs/QmdKS5YtmuSWKuLLgbHG176mS3VX3AKiyVmaaiAfvgcuch/go-libp2p-routing" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index cc213c9c5..ac4887e36 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,12 +7,12 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" - "gx/ipfs/QmWAfTyD6KEBm7bzqNRBPvqKrZCDtn5PGbs9V1DKfnVK59/go-unixfs" + "gx/ipfs/QmPXzQ9LAFGZjcifFANCQFQiYt5SXgJziGoxUfJULVpHyA/go-unixfs" + path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - offroute "gx/ipfs/QmRuUsZEg2WLCuidJGHVmE1NreHDmXWKLS466PKyDpXMhN/go-ipfs-routing/offline" + offroute "gx/ipfs/QmSbZCrt5cSiCNcXFZKoGjukcEf4DRdTzexqzEWATZDdz6/go-ipfs-routing/offline" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" diff --git a/namesys/proquint.go b/namesys/proquint.go index 2778590e5..dc2f8c287 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" + path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 79be29e0d..850f0bc94 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,16 +7,16 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" - ft "gx/ipfs/QmWAfTyD6KEBm7bzqNRBPvqKrZCDtn5PGbs9V1DKfnVK59/go-unixfs" + ft "gx/ipfs/QmPXzQ9LAFGZjcifFANCQFQiYt5SXgJziGoxUfJULVpHyA/go-unixfs" + path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dsquery "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" - routing "gx/ipfs/QmY9JUvS8kbgao3XbPh6WAV3ChE2nxGKhcGTHiwMC4gmcU/go-libp2p-routing" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" pb "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns/pb" + routing "gx/ipfs/QmdKS5YtmuSWKuLLgbHG176mS3VX3AKiyVmaaiAfvgcuch/go-libp2p-routing" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 5df344af9..268a22893 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -9,12 +9,12 @@ import ( ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" - mockrouting "gx/ipfs/QmRuUsZEg2WLCuidJGHVmE1NreHDmXWKLS466PKyDpXMhN/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmSbZCrt5cSiCNcXFZKoGjukcEf4DRdTzexqzEWATZDdz6/go-ipfs-routing/mock" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" + dshelp "gx/ipfs/QmXejiSr776HgKLEGSs7unW7GT82AgfMbQX5crfSybGU8b/go-ipfs-ds-help" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" - dshelp "gx/ipfs/Qmf1xGr3SyBpiPp3ZDuKkYMh4gnRk9K4QnbL17kstnf35h/go-ipfs-ds-help" ) type identity struct { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 29a5fa745..17f5c5d30 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" + path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index be62d10cb..83e294d89 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" + path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" mocknet "gx/ipfs/QmUEqyXr97aUbNmQADHYNknjwjjdVpJXEt1UZXmSG81EV4/go-libp2p/p2p/net/mock" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index b86598827..41821a2bf 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" - path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" + path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" - mockrouting "gx/ipfs/QmRuUsZEg2WLCuidJGHVmE1NreHDmXWKLS466PKyDpXMhN/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmSbZCrt5cSiCNcXFZKoGjukcEf4DRdTzexqzEWATZDdz6/go-ipfs-routing/mock" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" diff --git a/namesys/routing.go b/namesys/routing.go index 493657281..b02ffc155 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,16 +6,16 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmNgXoHgXU1HzNb2HEZmRww9fDKE9NfDsvQwWLHiKHpvKM/go-path" + path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - dht "gx/ipfs/QmRNxiPpZf3skMAtmDJpgHuW9uj1ukqV1zjANj9d6bmHfE/go-libp2p-kad-dht" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - routing "gx/ipfs/QmY9JUvS8kbgao3XbPh6WAV3ChE2nxGKhcGTHiwMC4gmcU/go-libp2p-routing" - cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" + dht "gx/ipfs/QmaXYSwxqJsX3EoGb1ZV2toZ9fXc8hWJPaBW1XAp1h2Tsp/go-libp2p-kad-dht" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" pb "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns/pb" + routing "gx/ipfs/QmdKS5YtmuSWKuLLgbHG176mS3VX3AKiyVmaaiAfvgcuch/go-libp2p-routing" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) From 749717e16376ae14c664b7468fb0138a21ae81f6 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 11 Sep 2018 22:19:44 -0400 Subject: [PATCH 2608/3817] gx update and fix code to use new Cid type License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@4baf8a5628fdacb38b57678326aa434c1de0f279 --- filestore/filestore.go | 20 ++++++++++---------- filestore/filestore_test.go | 14 +++++++------- filestore/fsrefstore.go | 30 +++++++++++++++--------------- filestore/util.go | 24 ++++++++++++------------ 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index a24839d2e..345aaa2bc 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,12 +11,12 @@ import ( "context" "errors" + posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" + blocks "gx/ipfs/QmRcHuYzAyswytBuMF78rj3LTChYszomRFXNg4685ZN1WM/go-block-format" dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" - blocks "gx/ipfs/QmWAzSEoqZ6xU6pu8yL8e5WaMb7wtbfbhhN4p1DknUPtr3/go-block-format" - posinfo "gx/ipfs/QmXD4grfThQ4LwVoEEfe4dgR7ukmbV9TppM5Q4SPowp7hU/go-ipfs-posinfo" - cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" - blockstore "gx/ipfs/Qmeg56ecxRnVv7VWViMrDeEMoBHaNFMs4vQnyQrJ79Zz7i/go-ipfs-blockstore" + blockstore "gx/ipfs/QmeMussyD8s3fQ3pM19ZsfbxvomEqPV9FvczLMWyBDYSnS/go-ipfs-blockstore" ) var log = logging.Logger("filestore") @@ -49,7 +49,7 @@ func NewFilestore(bs blockstore.Blockstore, fm *FileManager) *Filestore { // AllKeysChan returns a channel from which to read the keys stored in // the blockstore. If the given context is cancelled the channel will be closed. -func (f *Filestore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { +func (f *Filestore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { ctx, cancel := context.WithCancel(ctx) a, err := f.bs.AllKeysChan(ctx) @@ -58,7 +58,7 @@ func (f *Filestore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { return nil, err } - out := make(chan *cid.Cid, dsq.KeysOnlyBufSize) + out := make(chan cid.Cid, dsq.KeysOnlyBufSize) go func() { defer cancel() defer close(out) @@ -115,7 +115,7 @@ func (f *Filestore) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { // blockstore. As expected, in the case of FileManager blocks, only the // reference is deleted, not its contents. It may return // ErrNotFound when the block is not stored. -func (f *Filestore) DeleteBlock(c *cid.Cid) error { +func (f *Filestore) DeleteBlock(c cid.Cid) error { err1 := f.bs.DeleteBlock(c) if err1 != nil && err1 != blockstore.ErrNotFound { return err1 @@ -140,7 +140,7 @@ func (f *Filestore) DeleteBlock(c *cid.Cid) error { // Get retrieves the block with the given Cid. It may return // ErrNotFound when the block is not stored. -func (f *Filestore) Get(c *cid.Cid) (blocks.Block, error) { +func (f *Filestore) Get(c cid.Cid) (blocks.Block, error) { blk, err := f.bs.Get(c) switch err { case nil: @@ -154,7 +154,7 @@ func (f *Filestore) Get(c *cid.Cid) (blocks.Block, error) { // GetSize returns the size of the requested block. It may return ErrNotFound // when the block is not stored. -func (f *Filestore) GetSize(c *cid.Cid) (int, error) { +func (f *Filestore) GetSize(c cid.Cid) (int, error) { size, err := f.bs.GetSize(c) switch err { case nil: @@ -168,7 +168,7 @@ func (f *Filestore) GetSize(c *cid.Cid) (int, error) { // Has returns true if the block with the given Cid is // stored in the Filestore. -func (f *Filestore) Has(c *cid.Cid) (bool, error) { +func (f *Filestore) Has(c cid.Cid) (bool, error) { has, err := f.bs.Has(c) if err != nil { return false, err diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 83eeb69b9..4c1e3f5c1 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,12 +7,12 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmNr4E8z9bGTztvHJktp7uQaMdx9p3r9Asrq6eYk7iCh4a/go-merkledag" + dag "gx/ipfs/QmURqt1jB9Yu3X4Tr9WQJf36QGN7vi8mGTzjnX2ij1CJwC/go-merkledag" + posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - posinfo "gx/ipfs/QmXD4grfThQ4LwVoEEfe4dgR7ukmbV9TppM5Q4SPowp7hU/go-ipfs-posinfo" - cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" - blockstore "gx/ipfs/Qmeg56ecxRnVv7VWViMrDeEMoBHaNFMs4vQnyQrJ79Zz7i/go-ipfs-blockstore" + blockstore "gx/ipfs/QmeMussyD8s3fQ3pM19ZsfbxvomEqPV9FvczLMWyBDYSnS/go-ipfs-blockstore" ) func newTestFilestore(t *testing.T) (string, *Filestore) { @@ -55,7 +55,7 @@ func TestBasicFilestore(t *testing.T) { t.Fatal(err) } - var cids []*cid.Cid + var cids []cid.Cid for i := 0; i < 100; i++ { n := &posinfo.FilestoreNode{ PosInfo: &posinfo.PosInfo{ @@ -104,7 +104,7 @@ func TestBasicFilestore(t *testing.T) { } } -func randomFileAdd(t *testing.T, fs *Filestore, dir string, size int) (string, []*cid.Cid) { +func randomFileAdd(t *testing.T, fs *Filestore, dir string, size int) (string, []cid.Cid) { buf := make([]byte, size) rand.Read(buf) @@ -113,7 +113,7 @@ func randomFileAdd(t *testing.T, fs *Filestore, dir string, size int) (string, [ t.Fatal(err) } - var out []*cid.Cid + var out []cid.Cid for i := 0; i < size/10; i++ { n := &posinfo.FilestoreNode{ PosInfo: &posinfo.PosInfo{ diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 87ceda15d..9608e3af0 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,15 +10,15 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" + posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" + blocks "gx/ipfs/QmRcHuYzAyswytBuMF78rj3LTChYszomRFXNg4685ZN1WM/go-block-format" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dsns "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/namespace" dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" - blocks "gx/ipfs/QmWAzSEoqZ6xU6pu8yL8e5WaMb7wtbfbhhN4p1DknUPtr3/go-block-format" - posinfo "gx/ipfs/QmXD4grfThQ4LwVoEEfe4dgR7ukmbV9TppM5Q4SPowp7hU/go-ipfs-posinfo" - cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" + dshelp "gx/ipfs/QmXejiSr776HgKLEGSs7unW7GT82AgfMbQX5crfSybGU8b/go-ipfs-ds-help" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" - blockstore "gx/ipfs/Qmeg56ecxRnVv7VWViMrDeEMoBHaNFMs4vQnyQrJ79Zz7i/go-ipfs-blockstore" - dshelp "gx/ipfs/Qmf1xGr3SyBpiPp3ZDuKkYMh4gnRk9K4QnbL17kstnf35h/go-ipfs-ds-help" + blockstore "gx/ipfs/QmeMussyD8s3fQ3pM19ZsfbxvomEqPV9FvczLMWyBDYSnS/go-ipfs-blockstore" ) // FilestorePrefix identifies the key prefix for FileManager blocks. @@ -60,7 +60,7 @@ func NewFileManager(ds ds.Batching, root string) *FileManager { // AllKeysChan returns a channel from which to read the keys stored in // the FileManager. If the given context is cancelled the channel will be // closed. -func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) { +func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { q := dsq.Query{KeysOnly: true} res, err := f.ds.Query(q) @@ -68,7 +68,7 @@ func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) return nil, err } - out := make(chan *cid.Cid, dsq.KeysOnlyBufSize) + out := make(chan cid.Cid, dsq.KeysOnlyBufSize) go func() { defer close(out) for { @@ -97,7 +97,7 @@ func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan *cid.Cid, error) // DeleteBlock deletes the reference-block from the underlying // datastore. It does not touch the referenced data. -func (f *FileManager) DeleteBlock(c *cid.Cid) error { +func (f *FileManager) DeleteBlock(c cid.Cid) error { err := f.ds.Delete(dshelp.CidToDsKey(c)) if err == ds.ErrNotFound { return blockstore.ErrNotFound @@ -109,7 +109,7 @@ func (f *FileManager) DeleteBlock(c *cid.Cid) error { // is done in two steps: the first step retrieves the reference // block from the datastore. The second step uses the stored // path and offsets to read the raw block data directly from disk. -func (f *FileManager) Get(c *cid.Cid) (blocks.Block, error) { +func (f *FileManager) Get(c cid.Cid) (blocks.Block, error) { dobj, err := f.getDataObj(c) if err != nil { return nil, err @@ -126,7 +126,7 @@ func (f *FileManager) Get(c *cid.Cid) (blocks.Block, error) { // // This method may successfully return the size even if returning the block // would fail because the associated file is no longer available. -func (f *FileManager) GetSize(c *cid.Cid) (int, error) { +func (f *FileManager) GetSize(c cid.Cid) (int, error) { dobj, err := f.getDataObj(c) if err != nil { return -1, err @@ -134,14 +134,14 @@ func (f *FileManager) GetSize(c *cid.Cid) (int, error) { return int(dobj.GetSize_()), nil } -func (f *FileManager) readDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { +func (f *FileManager) readDataObj(c cid.Cid, d *pb.DataObj) ([]byte, error) { if IsURL(d.GetFilePath()) { return f.readURLDataObj(c, d) } return f.readFileDataObj(c, d) } -func (f *FileManager) getDataObj(c *cid.Cid) (*pb.DataObj, error) { +func (f *FileManager) getDataObj(c cid.Cid) (*pb.DataObj, error) { o, err := f.ds.Get(dshelp.CidToDsKey(c)) switch err { case ds.ErrNotFound: @@ -164,7 +164,7 @@ func unmarshalDataObj(data []byte) (*pb.DataObj, error) { return &dobj, nil } -func (f *FileManager) readFileDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { +func (f *FileManager) readFileDataObj(c cid.Cid, d *pb.DataObj) ([]byte, error) { if !f.AllowFiles { return nil, ErrFilestoreNotEnabled } @@ -207,7 +207,7 @@ func (f *FileManager) readFileDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) } // reads and verifies the block from URL -func (f *FileManager) readURLDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) { +func (f *FileManager) readURLDataObj(c cid.Cid, d *pb.DataObj) ([]byte, error) { if !f.AllowUrls { return nil, ErrUrlstoreNotEnabled } @@ -252,7 +252,7 @@ func (f *FileManager) readURLDataObj(c *cid.Cid, d *pb.DataObj) ([]byte, error) // Has returns if the FileManager is storing a block reference. It does not // validate the data, nor checks if the reference is valid. -func (f *FileManager) Has(c *cid.Cid) (bool, error) { +func (f *FileManager) Has(c cid.Cid) (bool, error) { // NOTE: interesting thing to consider. Has doesnt validate the data. // So the data on disk could be invalid, and we could think we have it. dsk := dshelp.CidToDsKey(c) diff --git a/filestore/util.go b/filestore/util.go index ea7f06ff0..8defb2c04 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,11 +6,11 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" - cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid" - blockstore "gx/ipfs/Qmeg56ecxRnVv7VWViMrDeEMoBHaNFMs4vQnyQrJ79Zz7i/go-ipfs-blockstore" - dshelp "gx/ipfs/Qmf1xGr3SyBpiPp3ZDuKkYMh4gnRk9K4QnbL17kstnf35h/go-ipfs-ds-help" + dshelp "gx/ipfs/QmXejiSr776HgKLEGSs7unW7GT82AgfMbQX5crfSybGU8b/go-ipfs-ds-help" + blockstore "gx/ipfs/QmeMussyD8s3fQ3pM19ZsfbxvomEqPV9FvczLMWyBDYSnS/go-ipfs-blockstore" ) // Status is used to identify the state of the block data referenced @@ -60,7 +60,7 @@ func (s Status) Format() string { type ListRes struct { Status Status ErrorMsg string - Key *cid.Cid + Key cid.Cid FilePath string Offset uint64 Size uint64 @@ -69,7 +69,7 @@ type ListRes struct { // FormatLong returns a human readable string for a ListRes object. func (r *ListRes) FormatLong() string { switch { - case r.Key == nil: + case !r.Key.Defined(): return "" case r.FilePath == "": return r.Key.String() @@ -82,7 +82,7 @@ func (r *ListRes) FormatLong() string { // of the given Filestore and returns a ListRes object with the information. // List does not verify that the reference is valid or whether the // raw data is accesible. See Verify(). -func List(fs *Filestore, key *cid.Cid) *ListRes { +func List(fs *Filestore, key cid.Cid) *ListRes { return list(fs, false, key) } @@ -101,7 +101,7 @@ func ListAll(fs *Filestore, fileOrder bool) (func() *ListRes, error) { // of the given Filestore and returns a ListRes object with the information. // Verify makes sure that the reference is valid and the block data can be // read. -func Verify(fs *Filestore, key *cid.Cid) *ListRes { +func Verify(fs *Filestore, key cid.Cid) *ListRes { return list(fs, true, key) } @@ -116,7 +116,7 @@ func VerifyAll(fs *Filestore, fileOrder bool) (func() *ListRes, error) { return listAll(fs, true) } -func list(fs *Filestore, verify bool, key *cid.Cid) *ListRes { +func list(fs *Filestore, verify bool, key cid.Cid) *ListRes { dobj, err := fs.fm.getDataObj(key) if err != nil { return mkListRes(key, nil, err) @@ -145,16 +145,16 @@ func listAll(fs *Filestore, verify bool) (func() *ListRes, error) { }, nil } -func next(qr dsq.Results) (*cid.Cid, *pb.DataObj, error) { +func next(qr dsq.Results) (cid.Cid, *pb.DataObj, error) { v, ok := qr.NextSync() if !ok { - return nil, nil, nil + return cid.Cid{}, nil, nil } k := ds.RawKey(v.Key) c, err := dshelp.DsKeyToCid(k) if err != nil { - return nil, nil, fmt.Errorf("decoding cid from filestore: %s", err) + return cid.Cid{}, nil, fmt.Errorf("decoding cid from filestore: %s", err) } dobj, err := unmarshalDataObj(v.Value) @@ -252,7 +252,7 @@ func (l listEntries) Less(i, j int) bool { return l[i].filePath < l[j].filePath } -func mkListRes(c *cid.Cid, d *pb.DataObj, err error) *ListRes { +func mkListRes(c cid.Cid, d *pb.DataObj, err error) *ListRes { status := StatusOk errorMsg := "" if err != nil { From 5051644a10e53ba10a145b6dc513433aaab62102 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 11 Sep 2018 20:34:52 -0700 Subject: [PATCH 2609/3817] gx: fix hashes (some extra files got committed) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@1390dbff290f62170fac61abe78094383ec27c22 --- pinning/pinner/gc/gc.go | 8 ++++---- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 8 ++++---- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index abb05f93c..f79d11ebd 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,16 +8,16 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmURqt1jB9Yu3X4Tr9WQJf36QGN7vi8mGTzjnX2ij1CJwC/go-merkledag" - bserv "gx/ipfs/QmYHXfGs5GVxXN233aFr5Jenvd7NG4qZ7pmjfyz7yvG93m/go-blockservice" + dag "gx/ipfs/QmXv5mwmQ74r4aiHcNeQ4GAmfB3aWJuqaE4WyDfDfvkgLM/go-merkledag" + bserv "gx/ipfs/Qma2KhbQarYTkmSJAeaMGRAg8HAXAhEWK8ge4SReG7ZSD3/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" dstore "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" "gx/ipfs/QmVkMRSkXrpjqrroEXWuYBvDBnXCdMMY6gsKicBGVGUqKT/go-verifcid" - offline "gx/ipfs/QmXHsHBveZF6ueKzDJbUg476gmrbzoR1yijiyH5SZAEuDT/go-ipfs-exchange-offline" + offline "gx/ipfs/QmcRC35JF2pJQneAxa5LdQBQRumWggccWErogSrCkS1h8T/go-ipfs-exchange-offline" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" - bstore "gx/ipfs/QmeMussyD8s3fQ3pM19ZsfbxvomEqPV9FvczLMWyBDYSnS/go-ipfs-blockstore" + bstore "gx/ipfs/QmegPGspn3RpTMQ23Fd3GVVMopo1zsEMurudbFMZ5UXBLH/go-ipfs-blockstore" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index f4281667e..692ae24d7 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmURqt1jB9Yu3X4Tr9WQJf36QGN7vi8mGTzjnX2ij1CJwC/go-merkledag" + mdag "gx/ipfs/QmXv5mwmQ74r4aiHcNeQ4GAmfB3aWJuqaE4WyDfDfvkgLM/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 70bcd722b..b54f8a132 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" - mdag "gx/ipfs/QmURqt1jB9Yu3X4Tr9WQJf36QGN7vi8mGTzjnX2ij1CJwC/go-merkledag" - bs "gx/ipfs/QmYHXfGs5GVxXN233aFr5Jenvd7NG4qZ7pmjfyz7yvG93m/go-blockservice" + mdag "gx/ipfs/QmXv5mwmQ74r4aiHcNeQ4GAmfB3aWJuqaE4WyDfDfvkgLM/go-merkledag" + bs "gx/ipfs/Qma2KhbQarYTkmSJAeaMGRAg8HAXAhEWK8ge4SReG7ZSD3/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" - offline "gx/ipfs/QmXHsHBveZF6ueKzDJbUg476gmrbzoR1yijiyH5SZAEuDT/go-ipfs-exchange-offline" - blockstore "gx/ipfs/QmeMussyD8s3fQ3pM19ZsfbxvomEqPV9FvczLMWyBDYSnS/go-ipfs-blockstore" + offline "gx/ipfs/QmcRC35JF2pJQneAxa5LdQBQRumWggccWErogSrCkS1h8T/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmegPGspn3RpTMQ23Fd3GVVMopo1zsEMurudbFMZ5UXBLH/go-ipfs-blockstore" ) var rand = util.NewTimeSeededRand() diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 53d51d156..9d76177c7 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmURqt1jB9Yu3X4Tr9WQJf36QGN7vi8mGTzjnX2ij1CJwC/go-merkledag" + "gx/ipfs/QmXv5mwmQ74r4aiHcNeQ4GAmfB3aWJuqaE4WyDfDfvkgLM/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 4a9f66d9c..e9aacf6aa 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,14 +5,14 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmURqt1jB9Yu3X4Tr9WQJf36QGN7vi8mGTzjnX2ij1CJwC/go-merkledag" - bserv "gx/ipfs/QmYHXfGs5GVxXN233aFr5Jenvd7NG4qZ7pmjfyz7yvG93m/go-blockservice" + dag "gx/ipfs/QmXv5mwmQ74r4aiHcNeQ4GAmfB3aWJuqaE4WyDfDfvkgLM/go-merkledag" + bserv "gx/ipfs/Qma2KhbQarYTkmSJAeaMGRAg8HAXAhEWK8ge4SReG7ZSD3/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" - offline "gx/ipfs/QmXHsHBveZF6ueKzDJbUg476gmrbzoR1yijiyH5SZAEuDT/go-ipfs-exchange-offline" - blockstore "gx/ipfs/QmeMussyD8s3fQ3pM19ZsfbxvomEqPV9FvczLMWyBDYSnS/go-ipfs-blockstore" + offline "gx/ipfs/QmcRC35JF2pJQneAxa5LdQBQRumWggccWErogSrCkS1h8T/go-ipfs-exchange-offline" + blockstore "gx/ipfs/QmegPGspn3RpTMQ23Fd3GVVMopo1zsEMurudbFMZ5UXBLH/go-ipfs-blockstore" ) func ignoreCids(_ cid.Cid) {} From 91297a54c6b70a51fdea8f98c646e4191fc5d9ae Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 11 Sep 2018 20:34:52 -0700 Subject: [PATCH 2610/3817] gx: fix hashes (some extra files got committed) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@dec563d93e818189ec52f7eeeb70128c1c62deae --- coreiface/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/path.go b/coreiface/path.go index e11e20cf0..c61b33533 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" + ipfspath "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) From b34ab7a8f20637097a5387924e1114e523d3b534 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 11 Sep 2018 20:34:52 -0700 Subject: [PATCH 2611/3817] gx: fix hashes (some extra files got committed) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@973f7804e616373794f97793a397714ffb3627e5 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 6 +++--- namesys/namesys.go | 2 +- namesys/namesys_test.go | 6 +++--- namesys/proquint.go | 2 +- namesys/publisher.go | 4 ++-- namesys/publisher_test.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 4 ++-- namesys/routing.go | 2 +- 14 files changed, 21 insertions(+), 21 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index fe044ffb4..d7b87b36a 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" + path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index 9151ed64a..f90827091 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" + path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index e5e2ea159..ec36b7f1a 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" + path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index 221500e1c..87e0f1abf 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" + path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 7da72593c..f796f7ac2 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,14 +6,14 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" + path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" - mockrouting "gx/ipfs/QmSbZCrt5cSiCNcXFZKoGjukcEf4DRdTzexqzEWATZDdz6/go-ipfs-routing/mock" - offline "gx/ipfs/QmSbZCrt5cSiCNcXFZKoGjukcEf4DRdTzexqzEWATZDdz6/go-ipfs-routing/offline" + mockrouting "gx/ipfs/QmSNe4MWVxZWk6UxxW2z2EKofFo4GdFzud1vfn1iVby3mj/go-ipfs-routing/mock" + offline "gx/ipfs/QmSNe4MWVxZWk6UxxW2z2EKofFo4GdFzud1vfn1iVby3mj/go-ipfs-routing/offline" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" diff --git a/namesys/namesys.go b/namesys/namesys.go index 410c7e65c..1b9a1574e 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" + path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index ac4887e36..e4ee7b2d3 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,12 +7,12 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - "gx/ipfs/QmPXzQ9LAFGZjcifFANCQFQiYt5SXgJziGoxUfJULVpHyA/go-unixfs" - path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" + "gx/ipfs/QmPL8bYtbACcSFFiSr4s2du7Na382NxRADR8hC7D9FkEA2/go-unixfs" + path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - offroute "gx/ipfs/QmSbZCrt5cSiCNcXFZKoGjukcEf4DRdTzexqzEWATZDdz6/go-ipfs-routing/offline" + offroute "gx/ipfs/QmSNe4MWVxZWk6UxxW2z2EKofFo4GdFzud1vfn1iVby3mj/go-ipfs-routing/offline" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" diff --git a/namesys/proquint.go b/namesys/proquint.go index dc2f8c287..a0ad3ce65 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" + path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 850f0bc94..8655d4ac7 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,8 +7,8 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmPXzQ9LAFGZjcifFANCQFQiYt5SXgJziGoxUfJULVpHyA/go-unixfs" - path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" + ft "gx/ipfs/QmPL8bYtbACcSFFiSr4s2du7Na382NxRADR8hC7D9FkEA2/go-unixfs" + path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 268a22893..3b444cab9 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -6,13 +6,13 @@ import ( "testing" "time" + dshelp "gx/ipfs/QmPQ7bVbZAbGaJkBVJeTkkKXvLLZeN9CLWTf5fzUQ8yeWs/go-ipfs-ds-help" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" - mockrouting "gx/ipfs/QmSbZCrt5cSiCNcXFZKoGjukcEf4DRdTzexqzEWATZDdz6/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmSNe4MWVxZWk6UxxW2z2EKofFo4GdFzud1vfn1iVby3mj/go-ipfs-routing/mock" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" - dshelp "gx/ipfs/QmXejiSr776HgKLEGSs7unW7GT82AgfMbQX5crfSybGU8b/go-ipfs-ds-help" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 17f5c5d30..8ee200295 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" + path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 83e294d89..babdd9d54 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" + path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" mocknet "gx/ipfs/QmUEqyXr97aUbNmQADHYNknjwjjdVpJXEt1UZXmSG81EV4/go-libp2p/p2p/net/mock" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 41821a2bf..b0351214b 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" - path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" + path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" - mockrouting "gx/ipfs/QmSbZCrt5cSiCNcXFZKoGjukcEf4DRdTzexqzEWATZDdz6/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmSNe4MWVxZWk6UxxW2z2EKofFo4GdFzud1vfn1iVby3mj/go-ipfs-routing/mock" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" diff --git a/namesys/routing.go b/namesys/routing.go index b02ffc155..8d7861799 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmRYx6fJzTWFoeTo3qQn64iDrVC154Gy9waQDhvKRr2ND3/go-path" + path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" From bcd6c971f9fd61568aea1b7fa49d5e514cb14de6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 11 Sep 2018 20:34:52 -0700 Subject: [PATCH 2612/3817] gx: fix hashes (some extra files got committed) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@131564f7eba468da4e1658bac299c1a9252ac602 --- filestore/filestore.go | 2 +- filestore/filestore_test.go | 4 ++-- filestore/fsrefstore.go | 4 ++-- filestore/util.go | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 345aaa2bc..596e2b488 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -16,7 +16,7 @@ import ( logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" blocks "gx/ipfs/QmRcHuYzAyswytBuMF78rj3LTChYszomRFXNg4685ZN1WM/go-block-format" dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" - blockstore "gx/ipfs/QmeMussyD8s3fQ3pM19ZsfbxvomEqPV9FvczLMWyBDYSnS/go-ipfs-blockstore" + blockstore "gx/ipfs/QmegPGspn3RpTMQ23Fd3GVVMopo1zsEMurudbFMZ5UXBLH/go-ipfs-blockstore" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 4c1e3f5c1..425bbf554 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,12 +7,12 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmURqt1jB9Yu3X4Tr9WQJf36QGN7vi8mGTzjnX2ij1CJwC/go-merkledag" + dag "gx/ipfs/QmXv5mwmQ74r4aiHcNeQ4GAmfB3aWJuqaE4WyDfDfvkgLM/go-merkledag" posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - blockstore "gx/ipfs/QmeMussyD8s3fQ3pM19ZsfbxvomEqPV9FvczLMWyBDYSnS/go-ipfs-blockstore" + blockstore "gx/ipfs/QmegPGspn3RpTMQ23Fd3GVVMopo1zsEMurudbFMZ5UXBLH/go-ipfs-blockstore" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 9608e3af0..8bf001af9 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -11,14 +11,14 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" + dshelp "gx/ipfs/QmPQ7bVbZAbGaJkBVJeTkkKXvLLZeN9CLWTf5fzUQ8yeWs/go-ipfs-ds-help" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" blocks "gx/ipfs/QmRcHuYzAyswytBuMF78rj3LTChYszomRFXNg4685ZN1WM/go-block-format" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dsns "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/namespace" dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" - dshelp "gx/ipfs/QmXejiSr776HgKLEGSs7unW7GT82AgfMbQX5crfSybGU8b/go-ipfs-ds-help" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" - blockstore "gx/ipfs/QmeMussyD8s3fQ3pM19ZsfbxvomEqPV9FvczLMWyBDYSnS/go-ipfs-blockstore" + blockstore "gx/ipfs/QmegPGspn3RpTMQ23Fd3GVVMopo1zsEMurudbFMZ5UXBLH/go-ipfs-blockstore" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index 8defb2c04..60a2a72d7 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,11 +6,11 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" + dshelp "gx/ipfs/QmPQ7bVbZAbGaJkBVJeTkkKXvLLZeN9CLWTf5fzUQ8yeWs/go-ipfs-ds-help" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" - dshelp "gx/ipfs/QmXejiSr776HgKLEGSs7unW7GT82AgfMbQX5crfSybGU8b/go-ipfs-ds-help" - blockstore "gx/ipfs/QmeMussyD8s3fQ3pM19ZsfbxvomEqPV9FvczLMWyBDYSnS/go-ipfs-blockstore" + blockstore "gx/ipfs/QmegPGspn3RpTMQ23Fd3GVVMopo1zsEMurudbFMZ5UXBLH/go-ipfs-blockstore" ) // Status is used to identify the state of the block data referenced From 2274369ddc5a1637c99e46b563d047dc3491dd6b Mon Sep 17 00:00:00 2001 From: Overbool Date: Thu, 13 Sep 2018 13:53:08 +0800 Subject: [PATCH 2613/3817] fix(pin): goroutine leaks License: MIT Signed-off-by: Overbool This commit was moved from ipfs/go-ipfs-pinner@b451f6fa3ad78821564fdf94135f11d600f85645 --- pinning/pinner/gc/gc.go | 46 +++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index f79d11ebd..d3c8d40f2 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -58,7 +58,10 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn gcs, err := ColoredSet(ctx, pn, ds, bestEffortRoots, output) if err != nil { - output <- Result{Error: err} + select { + case output <- Result{Error: err}: + case <-ctx.Done(): + } return } emark.Append(logging.LoggableMap{ @@ -69,7 +72,10 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn keychan, err := bs.AllKeysChan(ctx) if err != nil { - output <- Result{Error: err} + select { + case output <- Result{Error: err}: + case <-ctx.Done(): + } return } @@ -108,7 +114,11 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn }) esweep.Done() if errors { - output <- Result{Error: ErrCannotDeleteSomeBlocks} + select { + case output <- Result{Error: ErrCannotDeleteSomeBlocks}: + case <-ctx.Done(): + return + } } defer log.EventBegin(ctx, "GC.datastore").Done() @@ -119,7 +129,10 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn err = gds.CollectGarbage() if err != nil { - output <- Result{Error: err} + select { + case output <- Result{Error: err}: + case <-ctx.Done(): + } return } }() @@ -177,28 +190,40 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo links, err := ipld.GetLinks(ctx, ng, cid) if err != nil { errors = true - output <- Result{Error: &CannotFetchLinksError{cid, err}} + select { + case output <- Result{Error: &CannotFetchLinksError{cid, err}}: + case <-ctx.Done(): + } } return links, nil } err := Descendants(ctx, getLinks, gcs, pn.RecursiveKeys()) if err != nil { errors = true - output <- Result{Error: err} + select { + case output <- Result{Error: err}: + case <-ctx.Done(): + } } bestEffortGetLinks := func(ctx context.Context, cid cid.Cid) ([]*ipld.Link, error) { links, err := ipld.GetLinks(ctx, ng, cid) if err != nil && err != ipld.ErrNotFound { errors = true - output <- Result{Error: &CannotFetchLinksError{cid, err}} + select { + case output <- Result{Error: &CannotFetchLinksError{cid, err}}: + case <-ctx.Done(): + } } return links, nil } err = Descendants(ctx, bestEffortGetLinks, gcs, bestEffortRoots) if err != nil { errors = true - output <- Result{Error: err} + select { + case output <- Result{Error: err}: + case <-ctx.Done(): + } } for _, k := range pn.DirectKeys() { @@ -208,7 +233,10 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo err = Descendants(ctx, getLinks, gcs, pn.InternalPins()) if err != nil { errors = true - output <- Result{Error: err} + select { + case output <- Result{Error: err}: + case <-ctx.Done(): + } } if errors { From 01423f6df03c1418e758da67d4658d42830b0c09 Mon Sep 17 00:00:00 2001 From: Overbool Date: Fri, 14 Sep 2018 09:07:14 +0800 Subject: [PATCH 2614/3817] feat(pin): return err when ctx.Done License: MIT Signed-off-by: Overbool This commit was moved from ipfs/go-ipfs-pinner@303cae9acea6685b7f73f8d651ff413a0ece197b --- pinning/pinner/gc/gc.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index d3c8d40f2..7c196009f 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -193,6 +193,7 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo select { case output <- Result{Error: &CannotFetchLinksError{cid, err}}: case <-ctx.Done(): + return nil, ctx.Err() } } return links, nil @@ -203,6 +204,7 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo select { case output <- Result{Error: err}: case <-ctx.Done(): + return nil, ctx.Err() } } @@ -213,6 +215,7 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo select { case output <- Result{Error: &CannotFetchLinksError{cid, err}}: case <-ctx.Done(): + return nil, ctx.Err() } } return links, nil @@ -223,6 +226,7 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo select { case output <- Result{Error: err}: case <-ctx.Done(): + return nil, ctx.Err() } } @@ -236,6 +240,7 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo select { case output <- Result{Error: err}: case <-ctx.Done(): + return nil, ctx.Err() } } From c717873fb58d91ac1fb4665628f351092d7c6675 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sun, 16 Sep 2018 00:11:00 -0700 Subject: [PATCH 2615/3817] Avoid allocating a session unless we need it This commit was moved from ipfs/go-blockservice@8819f05e9f18ea89cf7f125edac566529d76f703 --- blockservice/blockservice.go | 46 ++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index a8d72e0d9..2e1de3f7a 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "io" + "sync" blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" @@ -116,8 +117,9 @@ func NewSession(ctx context.Context, bs BlockService) *Session { if sessEx, ok := exch.(exchange.SessionExchange); ok { ses := sessEx.NewSession(ctx) return &Session{ - ses: ses, - bs: bs.Blockstore(), + ses: ses, + sessEx: sessEx, + bs: bs.Blockstore(), } } return &Session{ @@ -199,15 +201,19 @@ func (s *blockService) AddBlocks(bs []blocks.Block) error { func (s *blockService) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) { log.Debugf("BlockService GetBlock: '%s'", c) - var f exchange.Fetcher + var f func() exchange.Fetcher if s.exchange != nil { - f = s.exchange + f = s.getExchange } return getBlock(ctx, c, s.blockstore, f) // hash security } -func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, f exchange.Fetcher) (blocks.Block, error) { +func (s *blockService) getExchange() exchange.Fetcher { + return s.exchange +} + +func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget func() exchange.Fetcher) (blocks.Block, error) { err := verifcid.ValidateCid(c) // hash security if err != nil { return nil, err @@ -218,7 +224,9 @@ func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, f exchan return block, nil } - if err == blockstore.ErrNotFound && f != nil { + if err == blockstore.ErrNotFound && fget != nil { + f := fget() // Don't load the exchange until we have to + // TODO be careful checking ErrNotFound. If the underlying // implementation changes, this will break. log.Debug("Blockservice: Searching bitswap") @@ -245,10 +253,10 @@ func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, f exchan // the returned channel. // NB: No guarantees are made about order. func (s *blockService) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan blocks.Block { - return getBlocks(ctx, ks, s.blockstore, s.exchange) // hash security + return getBlocks(ctx, ks, s.blockstore, s.getExchange) // hash security } -func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, f exchange.Fetcher) <-chan blocks.Block { +func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget func() exchange.Fetcher) <-chan blocks.Block { out := make(chan blocks.Block) go func() { @@ -284,6 +292,7 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, f ex return } + f := fget() // don't load exchange unless we have to rblocks, err := f.GetBlocks(ctx, misses) if err != nil { log.Debugf("Error with GetBlocks: %s", err) @@ -318,18 +327,31 @@ func (s *blockService) Close() error { // Session is a helper type to provide higher level access to bitswap sessions type Session struct { - bs blockstore.Blockstore - ses exchange.Fetcher + bs blockstore.Blockstore + ses exchange.Fetcher + sessEx exchange.SessionExchange + sessCtx context.Context + lk sync.Mutex +} + +func (s *Session) getSession() exchange.Fetcher { + s.lk.Lock() + defer s.lk.Unlock() + if s.ses == nil { + s.ses = s.sessEx.NewSession(s.sessCtx) + } + + return s.ses } // GetBlock gets a block in the context of a request session func (s *Session) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) { - return getBlock(ctx, c, s.bs, s.ses) // hash security + return getBlock(ctx, c, s.bs, s.getSession) // hash security } // GetBlocks gets blocks in the context of a request session func (s *Session) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan blocks.Block { - return getBlocks(ctx, ks, s.bs, s.ses) // hash security + return getBlocks(ctx, ks, s.bs, s.getSession) // hash security } var _ BlockGetter = (*Session)(nil) From 28fe139695b6494eb257aca0d7e21741497a640a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 18 Sep 2018 04:30:33 +0200 Subject: [PATCH 2616/3817] resolve cmd: use coreapi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@c3053e816481188f8b20f1d56c3cfa648d4f39c8 --- coreiface/options/name.go | 43 +++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/coreiface/options/name.go b/coreiface/options/name.go index 48aecf18b..9ba4a8770 100644 --- a/coreiface/options/name.go +++ b/coreiface/options/name.go @@ -14,9 +14,12 @@ type NamePublishSettings struct { } type NameResolveSettings struct { - Recursive bool - Local bool - Cache bool + Depth int + Local bool + Cache bool + + DhtRecordCount int + DhtTimeout time.Duration } type NamePublishOption func(*NamePublishSettings) error @@ -40,9 +43,12 @@ func NamePublishOptions(opts ...NamePublishOption) (*NamePublishSettings, error) func NameResolveOptions(opts ...NameResolveOption) (*NameResolveSettings, error) { options := &NameResolveSettings{ - Recursive: false, - Local: false, - Cache: true, + Depth: 1, + Local: false, + Cache: true, + + DhtRecordCount: 16, + DhtTimeout: time.Minute, } for _, opt := range opts { @@ -80,11 +86,11 @@ func (nameOpts) Key(key string) NamePublishOption { } } -// Recursive is an option for Name.Resolve which specifies whether to perform a +// Depth is an option for Name.Resolve which specifies the maximum depth of a // recursive lookup. Default value is false -func (nameOpts) Recursive(recursive bool) NameResolveOption { +func (nameOpts) Depth(depth int) NameResolveOption { return func(settings *NameResolveSettings) error { - settings.Recursive = recursive + settings.Depth = depth return nil } } @@ -106,3 +112,22 @@ func (nameOpts) Cache(cache bool) NameResolveOption { return nil } } + +// DhtRecordCount is an option for Name.Resolve which specifies how many records +// we want to validate before selecting the best one (newest). Note that setting +// this value too low will have security implications +func (nameOpts) DhtRecordCount(rc int) NameResolveOption { + return func(settings *NameResolveSettings) error { + settings.DhtRecordCount = rc + return nil + } +} + +// DhtTimeout is an option for Name.Resolve which specifies timeout for +// DHT lookup +func (nameOpts) DhtTimeout(timeout time.Duration) NameResolveOption { + return func(settings *NameResolveSettings) error { + settings.DhtTimeout = timeout + return nil + } +} From cd88b19ccb6d4ff617ea5e4c329c284315fc8707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 19 Sep 2018 11:51:42 +0200 Subject: [PATCH 2617/3817] coreapi name: accept namesys options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@ca81d824222df0ed2c1ede6a3e166f7aa420b4f7 --- coreiface/options/name.go | 36 ++++++------------------------------ 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/coreiface/options/name.go b/coreiface/options/name.go index 9ba4a8770..ba3691b03 100644 --- a/coreiface/options/name.go +++ b/coreiface/options/name.go @@ -2,6 +2,8 @@ package options import ( "time" + + ropts "github.com/ipfs/go-ipfs/namesys/opts" ) const ( @@ -14,12 +16,10 @@ type NamePublishSettings struct { } type NameResolveSettings struct { - Depth int Local bool Cache bool - DhtRecordCount int - DhtTimeout time.Duration + ResolveOpts []ropts.ResolveOpt } type NamePublishOption func(*NamePublishSettings) error @@ -43,12 +43,8 @@ func NamePublishOptions(opts ...NamePublishOption) (*NamePublishSettings, error) func NameResolveOptions(opts ...NameResolveOption) (*NameResolveSettings, error) { options := &NameResolveSettings{ - Depth: 1, Local: false, Cache: true, - - DhtRecordCount: 16, - DhtTimeout: time.Minute, } for _, opt := range opts { @@ -86,15 +82,6 @@ func (nameOpts) Key(key string) NamePublishOption { } } -// Depth is an option for Name.Resolve which specifies the maximum depth of a -// recursive lookup. Default value is false -func (nameOpts) Depth(depth int) NameResolveOption { - return func(settings *NameResolveSettings) error { - settings.Depth = depth - return nil - } -} - // Local is an option for Name.Resolve which specifies if the lookup should be // offline. Default value is false func (nameOpts) Local(local bool) NameResolveOption { @@ -113,21 +100,10 @@ func (nameOpts) Cache(cache bool) NameResolveOption { } } -// DhtRecordCount is an option for Name.Resolve which specifies how many records -// we want to validate before selecting the best one (newest). Note that setting -// this value too low will have security implications -func (nameOpts) DhtRecordCount(rc int) NameResolveOption { - return func(settings *NameResolveSettings) error { - settings.DhtRecordCount = rc - return nil - } -} - -// DhtTimeout is an option for Name.Resolve which specifies timeout for -// DHT lookup -func (nameOpts) DhtTimeout(timeout time.Duration) NameResolveOption { +// +func (nameOpts) ResolveOption(opt ropts.ResolveOpt) NameResolveOption { return func(settings *NameResolveSettings) error { - settings.DhtTimeout = timeout + settings.ResolveOpts = append(settings.ResolveOpts, opt) return nil } } From 9ebf36799e947803b77b11a58799bc25055ea3b6 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 19 Sep 2018 14:35:16 -0700 Subject: [PATCH 2618/3817] gx publish 0.1.1 This commit was moved from ipfs/go-ipfs-exchange-interface@615ac03eb55d9595690c78799641d8f2c9a6c16f --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index 42fe6a80b..c3032b235 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -33,5 +33,5 @@ type Fetcher interface { // sessions. type SessionExchange interface { Interface - NewSession(context.Context) Interface + NewSession(context.Context) Fetcher } From b9a81c74dba6085872473642a4ee273bba1bee31 Mon Sep 17 00:00:00 2001 From: Overbool Date: Fri, 21 Sep 2018 15:57:11 +0800 Subject: [PATCH 2619/3817] fix(unixfs): issue #6 This commit was moved from ipfs/go-mfs@820510f794b9c570993802203c62dfa322e10111 --- mfs/dir.go | 4 ++-- mfs/file.go | 4 ++-- mfs/system.go | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 676bf97d6..5a1161c22 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -152,12 +152,12 @@ func (d *Directory) childNode(name string) (FSNode, error) { func (d *Directory) cacheNode(name string, nd ipld.Node) (FSNode, error) { switch nd := nd.(type) { case *dag.ProtoNode: - i, err := ft.FromBytes(nd.Data()) + fsn, err := ft.FSNodeFromBytes(nd.Data()) if err != nil { return nil, err } - switch i.GetType() { + switch fsn.Type() { case ufspb.Data_Directory, ufspb.Data_HAMTShard: ndir, err := NewDirectory(d.ctx, name, nd, d, d.dserv) if err != nil { diff --git a/mfs/file.go b/mfs/file.go index 00c70ae4b..0a49646fd 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -102,11 +102,11 @@ func (fi *File) Size() (int64, error) { defer fi.nodelk.Unlock() switch nd := fi.node.(type) { case *dag.ProtoNode: - pbd, err := ft.FromBytes(nd.Data()) + fsn, err := ft.FSNodeFromBytes(nd.Data()) if err != nil { return 0, err } - return int64(pbd.GetFilesize()), nil + return int64(fsn.FileSize()), nil case *dag.RawNode: return int64(len(nd.RawData())), nil default: diff --git a/mfs/system.go b/mfs/system.go index 100cfa412..cc7e65d3a 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -74,13 +74,13 @@ func NewRoot(parent context.Context, ds ipld.DAGService, node *dag.ProtoNode, pf repub: repub, } - pbn, err := ft.FromBytes(node.Data()) + fsn, err := ft.FSNodeFromBytes(node.Data()) if err != nil { log.Error("IPNS pointer was not unixfs node") return nil, err } - switch pbn.GetType() { + switch fsn.Type() { case ft.TDirectory, ft.THAMTShard: newDir, err := NewDirectory(parent, node.String(), node, root, ds) if err != nil { @@ -89,9 +89,9 @@ func NewRoot(parent context.Context, ds ipld.DAGService, node *dag.ProtoNode, pf root.dir = newDir case ft.TFile, ft.TMetadata, ft.TRaw: - return nil, fmt.Errorf("root can't be a file (unixfs type: %s)", pbn.GetType()) + return nil, fmt.Errorf("root can't be a file (unixfs type: %s)", fsn.Type()) default: - return nil, fmt.Errorf("unrecognized unixfs type: %s", pbn.GetType()) + return nil, fmt.Errorf("unrecognized unixfs type: %s", fsn.Type()) } return root, nil } From e146ae1219c27b524cbe95286f9cd210994803fa Mon Sep 17 00:00:00 2001 From: Overbool Date: Thu, 20 Sep 2018 09:12:10 +0800 Subject: [PATCH 2620/3817] feat(io): add IsDir function This commit was moved from ipfs/go-unixfs@087af1f883fe820b40078f5ac3db97f80b0ccddc --- unixfs/unixfs.go | 10 ++++++++++ unixfs/unixfs_test.go | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index 21f643520..1263d82a6 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -247,6 +247,16 @@ func (n *FSNode) Type() pb.Data_DataType { return n.format.GetType() } +// IsDir checks whether the node represents a directory +func (n *FSNode) IsDir() bool { + switch n.Type() { + case pb.Data_Directory, pb.Data_HAMTShard: + return true + default: + return false + } +} + // Metadata is used to store additional FSNode information. type Metadata struct { MimeType string diff --git a/unixfs/unixfs_test.go b/unixfs/unixfs_test.go index e04682864..ef0b6be97 100644 --- a/unixfs/unixfs_test.go +++ b/unixfs/unixfs_test.go @@ -158,3 +158,20 @@ func TestMetadata(t *testing.T) { } } + +func TestIsDir(t *testing.T) { + prepares := map[pb.Data_DataType]bool{ + TDirectory: true, + THAMTShard: true, + TFile: false, + TMetadata: false, + TRaw: false, + TSymlink: false, + } + for typ, v := range prepares { + fsn := NewFSNode(typ) + if fsn.IsDir() != v { + t.Fatalf("type %v, IsDir() should be %v, but %v", typ, v, fsn.IsDir()) + } + } +} From d909a8ea01d60b69082352a21c5b822cf9106b34 Mon Sep 17 00:00:00 2001 From: Overbool Date: Fri, 21 Sep 2018 07:00:55 +0800 Subject: [PATCH 2621/3817] fix(fsnode): issue #17 This commit was moved from ipfs/go-unixfs@8bea61e6e6681700f08291282d2b37517f5225c5 --- unixfs/hamt/hamt.go | 13 +++++++------ unixfs/importer/trickle/trickledag.go | 12 ++++++------ unixfs/io/resolve.go | 4 ++-- unixfs/mod/dagmodifier.go | 19 +++++++++---------- unixfs/test/utils.go | 4 ++-- unixfs/unixfs.go | 16 ++++++++++++++++ unixfs/unixfs_test.go | 12 ++++++------ 7 files changed, 48 insertions(+), 32 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 4d3bd3b8e..bd2144214 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -102,28 +102,29 @@ func NewHamtFromDag(dserv ipld.DAGService, nd ipld.Node) (*Shard, error) { return nil, dag.ErrNotProtobuf } - pbd, err := format.FromBytes(pbnd.Data()) + fsn, err := format.FSNodeFromBytes(pbnd.Data()) if err != nil { return nil, err } - if pbd.GetType() != upb.Data_HAMTShard { + + if fsn.Type() != upb.Data_HAMTShard { return nil, fmt.Errorf("node was not a dir shard") } - if pbd.GetHashType() != HashMurmur3 { + if fsn.HashType() != HashMurmur3 { return nil, fmt.Errorf("only murmur3 supported as hash function") } - ds, err := makeShard(dserv, int(pbd.GetFanout())) + ds, err := makeShard(dserv, int(fsn.Fanout())) if err != nil { return nil, err } ds.nd = pbnd.Copy().(*dag.ProtoNode) ds.children = make([]child, len(pbnd.Links())) - ds.bitfield.SetBytes(pbd.GetData()) - ds.hashFunc = pbd.GetHashType() + ds.bitfield.SetBytes(fsn.Data()) + ds.hashFunc = fsn.HashType() ds.builder = ds.nd.CidBuilder() return ds, nil diff --git a/unixfs/importer/trickle/trickledag.go b/unixfs/importer/trickle/trickledag.go index 30c961861..bdc72e8bf 100644 --- a/unixfs/importer/trickle/trickledag.go +++ b/unixfs/importer/trickle/trickledag.go @@ -277,12 +277,12 @@ func verifyTDagRec(n ipld.Node, depth int, p VerifyParams) error { // zero depth dag is raw data block switch nd := n.(type) { case *dag.ProtoNode: - pbn, err := ft.FromBytes(nd.Data()) + fsn, err := ft.FSNodeFromBytes(nd.Data()) if err != nil { return err } - if pbn.GetType() != ft.TRaw { + if fsn.Type() != ft.TRaw { return errors.New("expected raw block") } @@ -325,16 +325,16 @@ func verifyTDagRec(n ipld.Node, depth int, p VerifyParams) error { } // Verify this is a branch node - pbn, err := ft.FromBytes(nd.Data()) + fsn, err := ft.FSNodeFromBytes(nd.Data()) if err != nil { return err } - if pbn.GetType() != ft.TFile { - return fmt.Errorf("expected file as branch node, got: %s", pbn.GetType()) + if fsn.Type() != ft.TFile { + return fmt.Errorf("expected file as branch node, got: %s", fsn.Type()) } - if len(pbn.Data) > 0 { + if len(fsn.Data()) > 0 { return errors.New("branch node should not have data") } diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index 5b0e6783a..3181097f3 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -15,7 +15,7 @@ import ( func ResolveUnixfsOnce(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, names []string) (*ipld.Link, []string, error) { switch nd := nd.(type) { case *dag.ProtoNode: - upb, err := ft.FromBytes(nd.Data()) + fsn, err := ft.FSNodeFromBytes(nd.Data()) if err != nil { // Not a unixfs node, use standard object traversal code lnk, err := nd.GetNodeLink(names[0]) @@ -26,7 +26,7 @@ func ResolveUnixfsOnce(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, na return lnk, names[1:], nil } - switch upb.GetType() { + switch fsn.Type() { case ft.THAMTShard: rods := dag.NewReadOnlyDagService(ds) s, err := hamt.NewHamtFromDag(rods, nd) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index c217be553..be9b07ea7 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -13,7 +13,6 @@ import ( trickle "github.com/ipfs/go-unixfs/importer/trickle" uio "github.com/ipfs/go-unixfs/io" - proto "github.com/gogo/protobuf/proto" cid "github.com/ipfs/go-cid" chunker "github.com/ipfs/go-ipfs-chunker" ipld "github.com/ipfs/go-ipld-format" @@ -173,11 +172,11 @@ func (dm *DagModifier) Size() (int64, error) { func fileSize(n ipld.Node) (uint64, error) { switch nd := n.(type) { case *mdag.ProtoNode: - f, err := ft.FromBytes(nd.Data()) + fsn, err := ft.FSNodeFromBytes(nd.Data()) if err != nil { return 0, err } - return f.GetFilesize(), nil + return fsn.FileSize(), nil case *mdag.RawNode: return uint64(len(nd.RawData())), nil default: @@ -238,18 +237,18 @@ func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64) (cid.Cid, error) { if len(n.Links()) == 0 { switch nd0 := n.(type) { case *mdag.ProtoNode: - f, err := ft.FromBytes(nd0.Data()) + fsn, err := ft.FSNodeFromBytes(nd0.Data()) if err != nil { return cid.Cid{}, err } - _, err = dm.wrBuf.Read(f.Data[offset:]) + _, err = dm.wrBuf.Read(fsn.Data()[offset:]) if err != nil && err != io.EOF { return cid.Cid{}, err } // Update newly written node.. - b, err := proto.Marshal(f) + b, err := fsn.GetBytes() if err != nil { return cid.Cid{}, err } @@ -300,13 +299,13 @@ func (dm *DagModifier) modifyDag(n ipld.Node, offset uint64) (cid.Cid, error) { return cid.Cid{}, ErrNotUnixfs } - f, err := ft.FromBytes(node.Data()) + fsn, err := ft.FSNodeFromBytes(node.Data()) if err != nil { return cid.Cid{}, err } var cur uint64 - for i, bs := range f.GetBlocksizes() { + for i, bs := range fsn.BlockSizes() { // We found the correct child to write into if cur+bs > offset { child, err := node.Links()[i].GetNode(dm.ctx, dm.dagserv) @@ -510,11 +509,11 @@ func (dm *DagModifier) dagTruncate(ctx context.Context, n ipld.Node, size uint64 switch nd := n.(type) { case *mdag.ProtoNode: // TODO: this can likely be done without marshaling and remarshaling - pbn, err := ft.FromBytes(nd.Data()) + fsn, err := ft.FSNodeFromBytes(nd.Data()) if err != nil { return nil, err } - nd.SetData(ft.WrapData(pbn.Data[:size])) + nd.SetData(ft.WrapData(fsn.Data()[:size])) return nd, nil case *mdag.RawNode: return mdag.NewRawNodeWPrefix(nd.RawData()[:size], nd.Cid().Prefix()) diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index fdd307c56..98bce14cf 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -107,7 +107,7 @@ func ArrComp(a, b []byte) error { // PrintDag pretty-prints the given dag to stdout. func PrintDag(nd *mdag.ProtoNode, ds ipld.DAGService, indent int) { - pbd, err := ft.FromBytes(nd.Data()) + fsn, err := ft.FSNodeFromBytes(nd.Data()) if err != nil { panic(err) } @@ -115,7 +115,7 @@ func PrintDag(nd *mdag.ProtoNode, ds ipld.DAGService, indent int) { for i := 0; i < indent; i++ { fmt.Print(" ") } - fmt.Printf("{size = %d, type = %s, children = %d", pbd.GetFilesize(), pbd.GetType().String(), len(pbd.GetBlocksizes())) + fmt.Printf("{size = %d, type = %s, children = %d", fsn.FileSize(), fsn.Type().String(), fsn.NumChildren()) if len(nd.Links()) > 0 { fmt.Println() } diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index 21f643520..db5b72f3e 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -29,6 +29,7 @@ var ( ) // FromBytes unmarshals a byte slice as protobuf Data. +// Deprecated: Use `FSNodeFromBytes` instead to avoid direct manipulation of `pb.Data`. func FromBytes(data []byte) (*pb.Data, error) { pbdata := new(pb.Data) err := proto.Unmarshal(data, pbdata) @@ -182,6 +183,16 @@ func NewFSNode(dataType pb.Data_DataType) *FSNode { return n } +// HashType gets hash type of format +func (n *FSNode) HashType() uint64 { + return n.format.GetHashType() +} + +// Fanout gets fanout of format +func (n *FSNode) Fanout() uint64 { + return n.format.GetFanout() +} + // AddBlockSize adds the size of the next child block of this node func (n *FSNode) AddBlockSize(s uint64) { n.UpdateFilesize(int64(s)) @@ -200,6 +211,11 @@ func (n *FSNode) BlockSize(i int) uint64 { return n.format.Blocksizes[i] } +// BlockSizes gets blocksizes of format +func (n *FSNode) BlockSizes() []uint64 { + return n.format.GetBlocksizes() +} + // RemoveAllBlockSizes removes all the child block sizes of this node. func (n *FSNode) RemoveAllBlockSizes() { n.format.Blocksizes = []uint64{} diff --git a/unixfs/unixfs_test.go b/unixfs/unixfs_test.go index e04682864..11fa918ff 100644 --- a/unixfs/unixfs_test.go +++ b/unixfs/unixfs_test.go @@ -76,12 +76,12 @@ func TestPBdataTools(t *testing.T) { t.Fatal("Unwrap failed to produce the correct wrapped data.") } - rawPBdata, err := FromBytes(rawPB) + rawPBdata, err := FSNodeFromBytes(rawPB) if err != nil { t.Fatal(err) } - isRaw := rawPBdata.GetType() == TRaw + isRaw := rawPBdata.Type() == TRaw if !isRaw { t.Fatal("WrapData does not create pb.Data_Raw!") } @@ -97,8 +97,8 @@ func TestPBdataTools(t *testing.T) { } dirPB := FolderPBData() - dir, err := FromBytes(dirPB) - isDir := dir.GetType() == TDirectory + dir, err := FSNodeFromBytes(dirPB) + isDir := dir.Type() == TDirectory if !isDir { t.Fatal("FolderPBData does not create a directory!") } @@ -115,8 +115,8 @@ func TestPBdataTools(t *testing.T) { t.Fatal(err) } - catSymPB, err := FromBytes(catSym) - isSym := catSymPB.GetType() == TSymlink + catSymPB, err := FSNodeFromBytes(catSym) + isSym := catSymPB.Type() == TSymlink if !isSym { t.Fatal("Failed to make a Symlink.") } From 25706d9f80ba3782c6957e31e71da20e3696fb32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 31 Aug 2018 14:46:09 +0200 Subject: [PATCH 2622/3817] Implement SearchValue This commit was moved from ipfs/go-ipfs-routing@991f2c382b451e5335fcdfdfbfe7fcac3b0dde2e --- routing/mock/centralized_client.go | 7 ++++++- routing/none/none_client.go | 4 ++++ routing/offline/offline.go | 13 +++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 49a363300..7faad9ab1 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -34,7 +34,12 @@ func (c *client) GetValue(ctx context.Context, key string, opts ...ropts.Option) return c.vs.GetValue(ctx, key, opts...) } -func (c *client) FindProviders(ctx context.Context, key cid.Cid) ([]pstore.PeerInfo, error) { +func (c *client) SearchValue(ctx context.Context, key string, opts ...ropts.Option) (<-chan []byte, error) { + log.Debugf("SearchValue: %s", key) + return c.vs.SearchValue(ctx, key, opts...) +} + +func (c *client) FindProviders(ctx context.Context, key *cid.Cid) ([]pstore.PeerInfo, error) { return c.server.Providers(key), nil } diff --git a/routing/none/none_client.go b/routing/none/none_client.go index e29ef36af..45febc554 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -26,6 +26,10 @@ func (c *nilclient) GetValue(_ context.Context, _ string, _ ...ropts.Option) ([] return nil, errors.New("tried GetValue from nil routing") } +func (c *nilclient) SearchValue(_ context.Context, _ string, _ ...ropts.Option) (<-chan []byte, error) { + return nil, errors.New("tried SearchValue from nil routing") +} + func (c *nilclient) FindPeer(_ context.Context, _ peer.ID) (pstore.PeerInfo, error) { return pstore.PeerInfo{}, nil } diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 9b94176cc..d2011fdaa 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -90,6 +90,19 @@ func (c *offlineRouting) GetValue(ctx context.Context, key string, _ ...ropts.Op return val, nil } +func (c *offlineRouting) SearchValue(ctx context.Context, key string, _ ...ropts.Option) (<-chan []byte, error) { + out := make(chan []byte) + go func() { + defer close(out) + v, _ := c.GetValue(ctx, key) + select { + case out <- v: + case <-ctx.Done(): + } + }() + return out, nil +} + func (c *offlineRouting) FindPeer(ctx context.Context, pid peer.ID) (pstore.PeerInfo, error) { return pstore.PeerInfo{}, ErrOffline } From 235b4322bf682212ad4daf9cd8faa24ee8097ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 1 Sep 2018 14:07:40 +0200 Subject: [PATCH 2623/3817] Optimize offline SearchValue slightly This commit was moved from ipfs/go-ipfs-routing@25a826923088dc4ac7b1143c807f5186af70f2f2 --- routing/offline/offline.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index d2011fdaa..1627490c2 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -91,13 +91,12 @@ func (c *offlineRouting) GetValue(ctx context.Context, key string, _ ...ropts.Op } func (c *offlineRouting) SearchValue(ctx context.Context, key string, _ ...ropts.Option) (<-chan []byte, error) { - out := make(chan []byte) + out := make(chan []byte, 1) go func() { defer close(out) - v, _ := c.GetValue(ctx, key) - select { - case out <- v: - case <-ctx.Done(): + v, err := c.GetValue(ctx, key) + if err == nil { + out <- v } }() return out, nil From 1c6f3dc18081a12be6f880da798403977a8da8e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 24 Sep 2018 13:10:35 +0200 Subject: [PATCH 2624/3817] Fix cid after rebase This commit was moved from ipfs/go-ipfs-routing@f18df505124381d560ed8764b84e4549b9c52369 --- routing/mock/centralized_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index 7faad9ab1..e09350da5 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -39,7 +39,7 @@ func (c *client) SearchValue(ctx context.Context, key string, opts ...ropts.Opti return c.vs.SearchValue(ctx, key, opts...) } -func (c *client) FindProviders(ctx context.Context, key *cid.Cid) ([]pstore.PeerInfo, error) { +func (c *client) FindProviders(ctx context.Context, key cid.Cid) ([]pstore.PeerInfo, error) { return c.server.Providers(key), nil } From a45848c57216369dfac2a17e81aa8b717bf8745b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 24 Sep 2018 14:03:57 +0200 Subject: [PATCH 2625/3817] gx: update go-libp2p-routing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@d37dcabfb128deb0ac57d7320ca827e37dcd28f8 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 10 +++++----- namesys/namesys.go | 4 ++-- namesys/namesys_test.go | 6 +++--- namesys/proquint.go | 2 +- namesys/publisher.go | 6 +++--- namesys/publisher_test.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 4 ++-- namesys/routing.go | 6 +++--- 14 files changed, 26 insertions(+), 26 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index d7b87b36a..1ffa3f8c6 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" + path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index f90827091..4d79e68d3 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" + path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index ec36b7f1a..f102e36f9 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,8 +8,8 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" + path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" ) type LookupTXTFunc func(name string) (txt []string, err error) diff --git a/namesys/interface.go b/namesys/interface.go index 87e0f1abf..fede7005c 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" + path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index f796f7ac2..0ad2e922c 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,20 +6,20 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" + path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + mockrouting "gx/ipfs/QmPKLDMELt3Z1g4fHZoE8HEj3THpTnpynHUQDjW1gDMUdk/go-ipfs-routing/mock" + offline "gx/ipfs/QmPKLDMELt3Z1g4fHZoE8HEj3THpTnpynHUQDjW1gDMUdk/go-ipfs-routing/offline" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" - mockrouting "gx/ipfs/QmSNe4MWVxZWk6UxxW2z2EKofFo4GdFzud1vfn1iVby3mj/go-ipfs-routing/mock" - offline "gx/ipfs/QmSNe4MWVxZWk6UxxW2z2EKofFo4GdFzud1vfn1iVby3mj/go-ipfs-routing/offline" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" + routing "gx/ipfs/QmaM261ArNTmKMybV4LKy68JTZrf5CkCbRGDxsZwYHgqDS/go-libp2p-routing" + ropts "gx/ipfs/QmaM261ArNTmKMybV4LKy68JTZrf5CkCbRGDxsZwYHgqDS/go-libp2p-routing/options" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" record "gx/ipfs/QmdHb9aBELnQKTVhvvA3hsQbRgUAwsWUzBP2vZ6Y5FBYvE/go-libp2p-record" - routing "gx/ipfs/QmdKS5YtmuSWKuLLgbHG176mS3VX3AKiyVmaaiAfvgcuch/go-libp2p-routing" - ropts "gx/ipfs/QmdKS5YtmuSWKuLLgbHG176mS3VX3AKiyVmaaiAfvgcuch/go-libp2p-routing/options" pstore "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore" pstoremem "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore/pstoremem" ) diff --git a/namesys/namesys.go b/namesys/namesys.go index 1b9a1574e..99a9bf441 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" + path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" @@ -14,7 +14,7 @@ import ( peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - routing "gx/ipfs/QmdKS5YtmuSWKuLLgbHG176mS3VX3AKiyVmaaiAfvgcuch/go-libp2p-routing" + routing "gx/ipfs/QmaM261ArNTmKMybV4LKy68JTZrf5CkCbRGDxsZwYHgqDS/go-libp2p-routing" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index e4ee7b2d3..ab71026a9 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,12 +7,12 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - "gx/ipfs/QmPL8bYtbACcSFFiSr4s2du7Na382NxRADR8hC7D9FkEA2/go-unixfs" - path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" + "gx/ipfs/QmWqiuwk7ZzUFQvfBuQDwxPxyAQtNMxGYwZkjJuF6GgWQk/go-unixfs" + path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + offroute "gx/ipfs/QmPKLDMELt3Z1g4fHZoE8HEj3THpTnpynHUQDjW1gDMUdk/go-ipfs-routing/offline" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - offroute "gx/ipfs/QmSNe4MWVxZWk6UxxW2z2EKofFo4GdFzud1vfn1iVby3mj/go-ipfs-routing/offline" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" diff --git a/namesys/proquint.go b/namesys/proquint.go index a0ad3ce65..2fa85b8d2 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,8 +7,8 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" + path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index 8655d4ac7..8cb54921d 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,16 +7,16 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmPL8bYtbACcSFFiSr4s2du7Na382NxRADR8hC7D9FkEA2/go-unixfs" - path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" + ft "gx/ipfs/QmWqiuwk7ZzUFQvfBuQDwxPxyAQtNMxGYwZkjJuF6GgWQk/go-unixfs" + path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dsquery "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" + routing "gx/ipfs/QmaM261ArNTmKMybV4LKy68JTZrf5CkCbRGDxsZwYHgqDS/go-libp2p-routing" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" pb "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns/pb" - routing "gx/ipfs/QmdKS5YtmuSWKuLLgbHG176mS3VX3AKiyVmaaiAfvgcuch/go-libp2p-routing" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 3b444cab9..37af07af3 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" + mockrouting "gx/ipfs/QmPKLDMELt3Z1g4fHZoE8HEj3THpTnpynHUQDjW1gDMUdk/go-ipfs-routing/mock" dshelp "gx/ipfs/QmPQ7bVbZAbGaJkBVJeTkkKXvLLZeN9CLWTf5fzUQ8yeWs/go-ipfs-ds-help" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" - mockrouting "gx/ipfs/QmSNe4MWVxZWk6UxxW2z2EKofFo4GdFzud1vfn1iVby3mj/go-ipfs-routing/mock" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 8ee200295..10c58b2da 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" + path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index babdd9d54..b2f006ee7 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" + path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" mocknet "gx/ipfs/QmUEqyXr97aUbNmQADHYNknjwjjdVpJXEt1UZXmSG81EV4/go-libp2p/p2p/net/mock" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index b0351214b..38b6c5c78 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" - path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" + path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + mockrouting "gx/ipfs/QmPKLDMELt3Z1g4fHZoE8HEj3THpTnpynHUQDjW1gDMUdk/go-ipfs-routing/mock" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" - mockrouting "gx/ipfs/QmSNe4MWVxZWk6UxxW2z2EKofFo4GdFzud1vfn1iVby3mj/go-ipfs-routing/mock" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" diff --git a/namesys/routing.go b/namesys/routing.go index 8d7861799..0a24122d5 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,16 +6,16 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" + path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - dht "gx/ipfs/QmaXYSwxqJsX3EoGb1ZV2toZ9fXc8hWJPaBW1XAp1h2Tsp/go-libp2p-kad-dht" + routing "gx/ipfs/QmaM261ArNTmKMybV4LKy68JTZrf5CkCbRGDxsZwYHgqDS/go-libp2p-routing" ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" pb "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns/pb" - routing "gx/ipfs/QmdKS5YtmuSWKuLLgbHG176mS3VX3AKiyVmaaiAfvgcuch/go-libp2p-routing" + dht "gx/ipfs/QmdB3eTAndZ1rqGTtUVwVmxdctb46C1hLfgdsbLHzJRDSr/go-libp2p-kad-dht" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) From da192643fa1850d58894f2f967d8cf4d1815e209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 24 Sep 2018 14:03:57 +0200 Subject: [PATCH 2626/3817] gx: update go-libp2p-routing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-pinner@d25fb74fa9641f306898689e6abbb5d041e5a63c --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 7c196009f..523430909 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmXv5mwmQ74r4aiHcNeQ4GAmfB3aWJuqaE4WyDfDfvkgLM/go-merkledag" - bserv "gx/ipfs/Qma2KhbQarYTkmSJAeaMGRAg8HAXAhEWK8ge4SReG7ZSD3/go-blockservice" + dag "gx/ipfs/QmVB15p7qNVQQGC5RQcAQAovDFaQpRNqorbRwpvdNjHmVC/go-merkledag" + bserv "gx/ipfs/QmWAU1Etv448cx9GLohtr27vnVC87amqE7fN4Hf4hcLDQY/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 692ae24d7..6b5ef6019 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmXv5mwmQ74r4aiHcNeQ4GAmfB3aWJuqaE4WyDfDfvkgLM/go-merkledag" + mdag "gx/ipfs/QmVB15p7qNVQQGC5RQcAQAovDFaQpRNqorbRwpvdNjHmVC/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index b54f8a132..6d8f2ecb6 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - mdag "gx/ipfs/QmXv5mwmQ74r4aiHcNeQ4GAmfB3aWJuqaE4WyDfDfvkgLM/go-merkledag" - bs "gx/ipfs/Qma2KhbQarYTkmSJAeaMGRAg8HAXAhEWK8ge4SReG7ZSD3/go-blockservice" + mdag "gx/ipfs/QmVB15p7qNVQQGC5RQcAQAovDFaQpRNqorbRwpvdNjHmVC/go-merkledag" + bs "gx/ipfs/QmWAU1Etv448cx9GLohtr27vnVC87amqE7fN4Hf4hcLDQY/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 9d76177c7..1a96037bb 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmXv5mwmQ74r4aiHcNeQ4GAmfB3aWJuqaE4WyDfDfvkgLM/go-merkledag" + "gx/ipfs/QmVB15p7qNVQQGC5RQcAQAovDFaQpRNqorbRwpvdNjHmVC/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index e9aacf6aa..d3776444f 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmXv5mwmQ74r4aiHcNeQ4GAmfB3aWJuqaE4WyDfDfvkgLM/go-merkledag" - bserv "gx/ipfs/Qma2KhbQarYTkmSJAeaMGRAg8HAXAhEWK8ge4SReG7ZSD3/go-blockservice" + dag "gx/ipfs/QmVB15p7qNVQQGC5RQcAQAovDFaQpRNqorbRwpvdNjHmVC/go-merkledag" + bserv "gx/ipfs/QmWAU1Etv448cx9GLohtr27vnVC87amqE7fN4Hf4hcLDQY/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" From 8e4bc5a6889bab0dacad05807d617347d4c52e17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 24 Sep 2018 14:03:57 +0200 Subject: [PATCH 2627/3817] gx: update go-libp2p-routing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-filestore@c2396f24fbf5a9d6e1623f1a31a88d8769e11268 --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 425bbf554..37ca215ab 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmXv5mwmQ74r4aiHcNeQ4GAmfB3aWJuqaE4WyDfDfvkgLM/go-merkledag" + dag "gx/ipfs/QmVB15p7qNVQQGC5RQcAQAovDFaQpRNqorbRwpvdNjHmVC/go-merkledag" posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" From fdaee8e479945dfe29bec1b56e575b5c9622a454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 24 Sep 2018 14:03:57 +0200 Subject: [PATCH 2628/3817] gx: update go-libp2p-routing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@143c911f1528ad45768b9ea1dd180b865c229fd1 --- coreiface/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/path.go b/coreiface/path.go index c61b33533..75901eaa4 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" + ipfspath "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) From 102bddb39cf0a630fdfe98b4e683f4e287e477c6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 24 Sep 2018 05:24:30 -0700 Subject: [PATCH 2629/3817] update for interface changes License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@ad3f9825dcd86d53bbc2e5c56fe335c6a30d9a96 --- namesys/ipns_resolver_validation_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 0ad2e922c..c97432c6c 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -174,6 +174,10 @@ func (m *mockValueStore) GetValue(ctx context.Context, k string, opts ...ropts.O return m.r.GetValue(ctx, k, opts...) } +func (m *mockValueStore) SearchValue(ctx context.Context, k string, opts ...ropts.Option) (<-chan []byte, error) { + return m.r.SearchValue(ctx, k, opts...) +} + func (m *mockValueStore) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey, error) { pk := m.kbook.PubKey(p) if pk != nil { From e35a0499f345067d363d2068f82c078834e09708 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 24 Sep 2018 05:36:29 -0700 Subject: [PATCH 2630/3817] gx: update go-log go-ipld-cbor (and friends) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@801e10810b0e22872c11733e5838c7b6b9a88908 --- pinning/pinner/gc/gc.go | 12 ++++++------ pinning/pinner/pin.go | 6 +++--- pinning/pinner/pin_test.go | 12 ++++++------ pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 12 ++++++------ 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 523430909..f5ca72547 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,16 +8,16 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmVB15p7qNVQQGC5RQcAQAovDFaQpRNqorbRwpvdNjHmVC/go-merkledag" - bserv "gx/ipfs/QmWAU1Etv448cx9GLohtr27vnVC87amqE7fN4Hf4hcLDQY/go-blockservice" + dag "gx/ipfs/QmcBoNcAP6qDjgRBew7yjvCqHq7p5jMstE44jPUBWBxzsV/go-merkledag" + bserv "gx/ipfs/QmcRecCZWM2NZfCQrCe97Ch3Givv8KKEP82tGUDntzdLFe/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - dstore "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" + offline "gx/ipfs/QmR5miWuikPxWyUrzMYJVmFUcD44pGdtc98h9Qsbp4YcJw/go-ipfs-exchange-offline" + dstore "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" "gx/ipfs/QmVkMRSkXrpjqrroEXWuYBvDBnXCdMMY6gsKicBGVGUqKT/go-verifcid" - offline "gx/ipfs/QmcRC35JF2pJQneAxa5LdQBQRumWggccWErogSrCkS1h8T/go-ipfs-exchange-offline" + logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" - bstore "gx/ipfs/QmegPGspn3RpTMQ23Fd3GVVMopo1zsEMurudbFMZ5UXBLH/go-ipfs-blockstore" + bstore "gx/ipfs/QmdriVJgKx4JADRgh3cYPXqXmsa1A45SvFki1nDWHhQNtC/go-ipfs-blockstore" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 6b5ef6019..cadc4530a 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,11 +10,11 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmVB15p7qNVQQGC5RQcAQAovDFaQpRNqorbRwpvdNjHmVC/go-merkledag" + mdag "gx/ipfs/QmcBoNcAP6qDjgRBew7yjvCqHq7p5jMstE44jPUBWBxzsV/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" + ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" + logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 6d8f2ecb6..2549084f5 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" - mdag "gx/ipfs/QmVB15p7qNVQQGC5RQcAQAovDFaQpRNqorbRwpvdNjHmVC/go-merkledag" - bs "gx/ipfs/QmWAU1Etv448cx9GLohtr27vnVC87amqE7fN4Hf4hcLDQY/go-blockservice" + mdag "gx/ipfs/QmcBoNcAP6qDjgRBew7yjvCqHq7p5jMstE44jPUBWBxzsV/go-merkledag" + bs "gx/ipfs/QmcRecCZWM2NZfCQrCe97Ch3Givv8KKEP82tGUDntzdLFe/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" - offline "gx/ipfs/QmcRC35JF2pJQneAxa5LdQBQRumWggccWErogSrCkS1h8T/go-ipfs-exchange-offline" - blockstore "gx/ipfs/QmegPGspn3RpTMQ23Fd3GVVMopo1zsEMurudbFMZ5UXBLH/go-ipfs-blockstore" + offline "gx/ipfs/QmR5miWuikPxWyUrzMYJVmFUcD44pGdtc98h9Qsbp4YcJw/go-ipfs-exchange-offline" + ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" + dssync "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/sync" + blockstore "gx/ipfs/QmdriVJgKx4JADRgh3cYPXqXmsa1A45SvFki1nDWHhQNtC/go-ipfs-blockstore" ) var rand = util.NewTimeSeededRand() diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 1a96037bb..9b4446e37 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmVB15p7qNVQQGC5RQcAQAovDFaQpRNqorbRwpvdNjHmVC/go-merkledag" + "gx/ipfs/QmcBoNcAP6qDjgRBew7yjvCqHq7p5jMstE44jPUBWBxzsV/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index d3776444f..e366eac70 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,14 +5,14 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmVB15p7qNVQQGC5RQcAQAovDFaQpRNqorbRwpvdNjHmVC/go-merkledag" - bserv "gx/ipfs/QmWAU1Etv448cx9GLohtr27vnVC87amqE7fN4Hf4hcLDQY/go-blockservice" + dag "gx/ipfs/QmcBoNcAP6qDjgRBew7yjvCqHq7p5jMstE44jPUBWBxzsV/go-merkledag" + bserv "gx/ipfs/QmcRecCZWM2NZfCQrCe97Ch3Givv8KKEP82tGUDntzdLFe/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" - offline "gx/ipfs/QmcRC35JF2pJQneAxa5LdQBQRumWggccWErogSrCkS1h8T/go-ipfs-exchange-offline" - blockstore "gx/ipfs/QmegPGspn3RpTMQ23Fd3GVVMopo1zsEMurudbFMZ5UXBLH/go-ipfs-blockstore" + offline "gx/ipfs/QmR5miWuikPxWyUrzMYJVmFUcD44pGdtc98h9Qsbp4YcJw/go-ipfs-exchange-offline" + ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" + dsq "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/query" + blockstore "gx/ipfs/QmdriVJgKx4JADRgh3cYPXqXmsa1A45SvFki1nDWHhQNtC/go-ipfs-blockstore" ) func ignoreCids(_ cid.Cid) {} From 59fe7844a9b7a03c04e58dd0ab74c16b9cc24f3d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 24 Sep 2018 05:36:29 -0700 Subject: [PATCH 2631/3817] gx: update go-log go-ipld-cbor (and friends) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@c96a43ec6e904a78eb6a7e6ff2b105e2d9ae3289 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 26 ++++++++++++------------ namesys/namesys.go | 8 ++++---- namesys/namesys_test.go | 16 +++++++-------- namesys/proquint.go | 2 +- namesys/publisher.go | 16 +++++++-------- namesys/publisher_test.go | 14 ++++++------- namesys/republisher/repub.go | 10 ++++----- namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 16 +++++++-------- namesys/routing.go | 14 ++++++------- 14 files changed, 68 insertions(+), 68 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 1ffa3f8c6..afdc0a468 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index 4d79e68d3..f18c6e8aa 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index f102e36f9..5ca7323a9 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -9,7 +9,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" ) type LookupTXTFunc func(name string) (txt []string, err error) diff --git a/namesys/interface.go b/namesys/interface.go index fede7005c..fcc956cb1 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index c97432c6c..cc99bf1d7 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,22 +6,22 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" - mockrouting "gx/ipfs/QmPKLDMELt3Z1g4fHZoE8HEj3THpTnpynHUQDjW1gDMUdk/go-ipfs-routing/mock" - offline "gx/ipfs/QmPKLDMELt3Z1g4fHZoE8HEj3THpTnpynHUQDjW1gDMUdk/go-ipfs-routing/offline" + testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" - ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" - routing "gx/ipfs/QmaM261ArNTmKMybV4LKy68JTZrf5CkCbRGDxsZwYHgqDS/go-libp2p-routing" - ropts "gx/ipfs/QmaM261ArNTmKMybV4LKy68JTZrf5CkCbRGDxsZwYHgqDS/go-libp2p-routing/options" - ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" - record "gx/ipfs/QmdHb9aBELnQKTVhvvA3hsQbRgUAwsWUzBP2vZ6Y5FBYvE/go-libp2p-record" - pstore "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore" - pstoremem "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore/pstoremem" + record "gx/ipfs/QmSb4B8ZAAj5ALe9LjfzPyF8Ma6ezC1NTnDF2JQPUJxEXb/go-libp2p-record" + mockrouting "gx/ipfs/QmScZySgru9jaoDa12sSfvh21sWbqF5eXkieTmJzAHJXkQ/go-ipfs-routing/mock" + offline "gx/ipfs/QmScZySgru9jaoDa12sSfvh21sWbqF5eXkieTmJzAHJXkQ/go-ipfs-routing/offline" + ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" + dssync "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/sync" + routing "gx/ipfs/QmVBnJDKhtFXTRVjXKinqpwGu8t1DyNqPKan2iGX8PR8xG/go-libp2p-routing" + ropts "gx/ipfs/QmVBnJDKhtFXTRVjXKinqpwGu8t1DyNqPKan2iGX8PR8xG/go-libp2p-routing/options" + ipns "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns" + peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + pstore "gx/ipfs/QmfAQMFpgDU2U4BXG64qVr8HSiictfWvkSBz7Y2oDj65st/go-libp2p-peerstore" + pstoremem "gx/ipfs/QmfAQMFpgDU2U4BXG64qVr8HSiictfWvkSBz7Y2oDj65st/go-libp2p-peerstore/pstoremem" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 99a9bf441..054a51bbd 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,15 +6,15 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" - peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" + ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" + routing "gx/ipfs/QmVBnJDKhtFXTRVjXKinqpwGu8t1DyNqPKan2iGX8PR8xG/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - routing "gx/ipfs/QmaM261ArNTmKMybV4LKy68JTZrf5CkCbRGDxsZwYHgqDS/go-libp2p-routing" + peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index ab71026a9..c2a530c26 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,16 +7,16 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - "gx/ipfs/QmWqiuwk7ZzUFQvfBuQDwxPxyAQtNMxGYwZkjJuF6GgWQk/go-unixfs" - path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + "gx/ipfs/QmU4x3742bvgfxJsByEDpBnifJqjJdV6x528co4hwKCn46/go-unixfs" + path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" - offroute "gx/ipfs/QmPKLDMELt3Z1g4fHZoE8HEj3THpTnpynHUQDjW1gDMUdk/go-ipfs-routing/offline" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" - ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" - pstoremem "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore/pstoremem" + offroute "gx/ipfs/QmScZySgru9jaoDa12sSfvh21sWbqF5eXkieTmJzAHJXkQ/go-ipfs-routing/offline" + ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" + dssync "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/sync" + ipns "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns" + peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + pstoremem "gx/ipfs/QmfAQMFpgDU2U4BXG64qVr8HSiictfWvkSBz7Y2oDj65st/go-libp2p-peerstore/pstoremem" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index 2fa85b8d2..4ba505dc8 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -8,7 +8,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" - path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index 8cb54921d..b85420675 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,16 +7,16 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmWqiuwk7ZzUFQvfBuQDwxPxyAQtNMxGYwZkjJuF6GgWQk/go-unixfs" - path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + ft "gx/ipfs/QmU4x3742bvgfxJsByEDpBnifJqjJdV6x528co4hwKCn46/go-unixfs" + path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - dsquery "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" - routing "gx/ipfs/QmaM261ArNTmKMybV4LKy68JTZrf5CkCbRGDxsZwYHgqDS/go-libp2p-routing" - ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" - pb "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns/pb" + ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" + dsquery "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/query" + routing "gx/ipfs/QmVBnJDKhtFXTRVjXKinqpwGu8t1DyNqPKan2iGX8PR8xG/go-libp2p-routing" + ipns "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns" + pb "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns/pb" + peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 37af07af3..3f6e6f02e 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -6,15 +6,15 @@ import ( "testing" "time" - mockrouting "gx/ipfs/QmPKLDMELt3Z1g4fHZoE8HEj3THpTnpynHUQDjW1gDMUdk/go-ipfs-routing/mock" - dshelp "gx/ipfs/QmPQ7bVbZAbGaJkBVJeTkkKXvLLZeN9CLWTf5fzUQ8yeWs/go-ipfs-ds-help" + testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" - ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" + mockrouting "gx/ipfs/QmScZySgru9jaoDa12sSfvh21sWbqF5eXkieTmJzAHJXkQ/go-ipfs-routing/mock" + dshelp "gx/ipfs/QmUDTSi6zJ6ACyQaKtxscCUxrg5DaXs9r4RQUPFQXGPHpo/go-ipfs-ds-help" + ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" + dssync "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/sync" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" - ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" + ipns "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns" + peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" ) type identity struct { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 10c58b2da..ebe17c776 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,15 +7,15 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - pb "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns/pb" + ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" + logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" + pb "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns/pb" + peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index b2f006ee7..dd8693965 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmUEqyXr97aUbNmQADHYNknjwjjdVpJXEt1UZXmSG81EV4/go-libp2p/p2p/net/mock" - pstore "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore" + mocknet "gx/ipfs/QmVsVARb86uSe1qYouewFMNd2p2sp2NGWm1JGPReVDWchW/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmfAQMFpgDU2U4BXG64qVr8HSiictfWvkSBz7Y2oDj65st/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 38b6c5c78..82e3f06da 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,14 +6,14 @@ import ( "testing" "time" - path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" - - mockrouting "gx/ipfs/QmPKLDMELt3Z1g4fHZoE8HEj3THpTnpynHUQDjW1gDMUdk/go-ipfs-routing/mock" - peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - testutil "gx/ipfs/QmRNhSdqzMcuRxX9A1egBeQ3BhDTguDV5HPwi8wRykkPU8/go-testutil" - ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - dssync "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/sync" - ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" + path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" + + testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" + mockrouting "gx/ipfs/QmScZySgru9jaoDa12sSfvh21sWbqF5eXkieTmJzAHJXkQ/go-ipfs-routing/mock" + ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" + dssync "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/sync" + ipns "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns" + peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index 0a24122d5..dc53868ad 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,16 +6,16 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" - routing "gx/ipfs/QmaM261ArNTmKMybV4LKy68JTZrf5CkCbRGDxsZwYHgqDS/go-libp2p-routing" - ipns "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns" - pb "gx/ipfs/QmbUUxB9ErnEQdwTzy6HTxucnBvAH4am6vsfbD8CiqKhi9/go-ipns/pb" - dht "gx/ipfs/QmdB3eTAndZ1rqGTtUVwVmxdctb46C1hLfgdsbLHzJRDSr/go-libp2p-kad-dht" + routing "gx/ipfs/QmVBnJDKhtFXTRVjXKinqpwGu8t1DyNqPKan2iGX8PR8xG/go-libp2p-routing" + logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" + dht "gx/ipfs/QmZVakpN44VAUxs9eXAuUGLFYTCGmSyqSy6hyEKfMv68ME/go-libp2p-kad-dht" + ipns "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns" + pb "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns/pb" + peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) From 57e242bc65e0b3bb7f88ee65b74be7c085f4a84c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 24 Sep 2018 05:36:29 -0700 Subject: [PATCH 2632/3817] gx: update go-log go-ipld-cbor (and friends) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@88c0e44182776acd62e3ad3ec2a726ccc7721b71 --- filestore/filestore.go | 6 +++--- filestore/filestore_test.go | 6 +++--- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 596e2b488..b7dad3c55 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -13,10 +13,10 @@ import ( posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" blocks "gx/ipfs/QmRcHuYzAyswytBuMF78rj3LTChYszomRFXNg4685ZN1WM/go-block-format" - dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" - blockstore "gx/ipfs/QmegPGspn3RpTMQ23Fd3GVVMopo1zsEMurudbFMZ5UXBLH/go-ipfs-blockstore" + dsq "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/query" + logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" + blockstore "gx/ipfs/QmdriVJgKx4JADRgh3cYPXqXmsa1A45SvFki1nDWHhQNtC/go-ipfs-blockstore" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 37ca215ab..42681c64a 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,12 +7,12 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmVB15p7qNVQQGC5RQcAQAovDFaQpRNqorbRwpvdNjHmVC/go-merkledag" + dag "gx/ipfs/QmcBoNcAP6qDjgRBew7yjvCqHq7p5jMstE44jPUBWBxzsV/go-merkledag" posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - blockstore "gx/ipfs/QmegPGspn3RpTMQ23Fd3GVVMopo1zsEMurudbFMZ5UXBLH/go-ipfs-blockstore" + ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" + blockstore "gx/ipfs/QmdriVJgKx4JADRgh3cYPXqXmsa1A45SvFki1nDWHhQNtC/go-ipfs-blockstore" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 8bf001af9..5e8bf6396 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -11,14 +11,14 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" - dshelp "gx/ipfs/QmPQ7bVbZAbGaJkBVJeTkkKXvLLZeN9CLWTf5fzUQ8yeWs/go-ipfs-ds-help" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" blocks "gx/ipfs/QmRcHuYzAyswytBuMF78rj3LTChYszomRFXNg4685ZN1WM/go-block-format" - ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - dsns "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/namespace" - dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" + dshelp "gx/ipfs/QmUDTSi6zJ6ACyQaKtxscCUxrg5DaXs9r4RQUPFQXGPHpo/go-ipfs-ds-help" + ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" + dsns "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/namespace" + dsq "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/query" + blockstore "gx/ipfs/QmdriVJgKx4JADRgh3cYPXqXmsa1A45SvFki1nDWHhQNtC/go-ipfs-blockstore" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" - blockstore "gx/ipfs/QmegPGspn3RpTMQ23Fd3GVVMopo1zsEMurudbFMZ5UXBLH/go-ipfs-blockstore" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index 60a2a72d7..6213b0f10 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,11 +6,11 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - dshelp "gx/ipfs/QmPQ7bVbZAbGaJkBVJeTkkKXvLLZeN9CLWTf5fzUQ8yeWs/go-ipfs-ds-help" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - ds "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore" - dsq "gx/ipfs/QmSpg1CvpXQQow5ernt1gNBXaXV6yxyNqi7XoeerWfzB5w/go-datastore/query" - blockstore "gx/ipfs/QmegPGspn3RpTMQ23Fd3GVVMopo1zsEMurudbFMZ5UXBLH/go-ipfs-blockstore" + dshelp "gx/ipfs/QmUDTSi6zJ6ACyQaKtxscCUxrg5DaXs9r4RQUPFQXGPHpo/go-ipfs-ds-help" + ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" + dsq "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/query" + blockstore "gx/ipfs/QmdriVJgKx4JADRgh3cYPXqXmsa1A45SvFki1nDWHhQNtC/go-ipfs-blockstore" ) // Status is used to identify the state of the block data referenced From 3bff21ef38fb40c5920de6e475607cd6e31bf0e5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 24 Sep 2018 05:36:29 -0700 Subject: [PATCH 2633/3817] gx: update go-log go-ipld-cbor (and friends) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@c1e241f73b853d5b8626125d24f8346c6331622b --- coreiface/dht.go | 4 ++-- coreiface/key.go | 2 +- coreiface/path.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/coreiface/dht.go b/coreiface/dht.go index 7b8119e44..2309ceb90 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -5,8 +5,8 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" - pstore "gx/ipfs/Qmda4cPRvSRyox3SqgJN6DfSZGU5TtHufPTp9uXjFj71X6/go-libp2p-peerstore" + peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + pstore "gx/ipfs/QmfAQMFpgDU2U4BXG64qVr8HSiictfWvkSBz7Y2oDj65st/go-libp2p-peerstore" ) // DhtAPI specifies the interface to the DHT diff --git a/coreiface/key.go b/coreiface/key.go index cc7c409fd..4305ae20d 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -5,7 +5,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" + "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" ) // Key specifies the interface to Keys in KeyAPI Keystore diff --git a/coreiface/path.go b/coreiface/path.go index 75901eaa4..0beab0663 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/Qmc17MNY1xUgiE2nopbi6KATWau9qcGZtdmKKuXvFMVUgc/go-path" + ipfspath "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) From 61fe74f129def8c38c1f8e8ab50f941f30736c6a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 24 Sep 2018 05:36:29 -0700 Subject: [PATCH 2634/3817] gx: update go-log go-ipld-cbor (and friends) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-keystore@049e8aa8c3b7ca0dc069c6072fb83359a8b6d6fd --- keystore/keystore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index 38c0869ef..ba43da47f 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -8,7 +8,7 @@ import ( "strings" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log" + logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" ) var log = logging.Logger("keystore") From 451aabcb39147615261f83f1bfca711812188875 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 25 Sep 2018 15:56:40 -0700 Subject: [PATCH 2635/3817] switch to go-buffer-pool It has a nicer interface and we don't even need the rest of the msgio Prereq for: https://github.com/libp2p/go-msgio/pull/9 This commit was moved from ipfs/go-ipfs-chunker@c10b0781c1d4e7026ddaa22a2f7c4e2416156bce --- chunker/splitting.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chunker/splitting.go b/chunker/splitting.go index 6a10de07a..2b2373992 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -8,7 +8,7 @@ import ( "io" logging "github.com/ipfs/go-log" - mpool "github.com/libp2p/go-msgio/mpool" + pool "github.com/libp2p/go-buffer-pool" ) var log = logging.Logger("chunk") @@ -82,19 +82,19 @@ func (ss *sizeSplitterv2) NextBytes() ([]byte, error) { return nil, ss.err } - full := mpool.ByteSlicePool.Get(ss.size).([]byte)[:ss.size] + full := pool.Get(int(ss.size)) n, err := io.ReadFull(ss.r, full) switch err { case io.ErrUnexpectedEOF: ss.err = io.EOF small := make([]byte, n) copy(small, full) - mpool.ByteSlicePool.Put(ss.size, full) + pool.Put(full) return small, nil case nil: return full, nil default: - mpool.ByteSlicePool.Put(ss.size, full) + pool.Put(full) return nil, err } } From 17066a977e973631cc233abc4b2ce2105bd193d8 Mon Sep 17 00:00:00 2001 From: Overbool Date: Wed, 26 Sep 2018 18:26:50 +0800 Subject: [PATCH 2636/3817] fix(type): issue #13 This commit was moved from ipfs/go-mfs@3365c5172fb2b9dcc76aadcfa7bc99b8fdfdf345 --- mfs/dir.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 5a1161c22..f26b13601 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -12,7 +12,6 @@ import ( dag "github.com/ipfs/go-merkledag" ft "github.com/ipfs/go-unixfs" uio "github.com/ipfs/go-unixfs/io" - ufspb "github.com/ipfs/go-unixfs/pb" cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" @@ -158,7 +157,7 @@ func (d *Directory) cacheNode(name string, nd ipld.Node) (FSNode, error) { } switch fsn.Type() { - case ufspb.Data_Directory, ufspb.Data_HAMTShard: + case ft.TDirectory, ft.THAMTShard: ndir, err := NewDirectory(d.ctx, name, nd, d, d.dserv) if err != nil { return nil, err @@ -166,14 +165,14 @@ func (d *Directory) cacheNode(name string, nd ipld.Node) (FSNode, error) { d.childDirs[name] = ndir return ndir, nil - case ufspb.Data_File, ufspb.Data_Raw, ufspb.Data_Symlink: + case ft.TFile, ft.TRaw, ft.TSymlink: nfi, err := NewFile(name, nd, d, d.dserv) if err != nil { return nil, err } d.files[name] = nfi return nfi, nil - case ufspb.Data_Metadata: + case ft.TMetadata: return nil, ErrNotYetImplemented default: return nil, ErrInvalidChild From e6145cab9b5c6f9595b22797d813db25275d88fc Mon Sep 17 00:00:00 2001 From: Overbool Date: Wed, 26 Sep 2018 15:03:08 +0800 Subject: [PATCH 2637/3817] fix(type): issue #23 This commit was moved from ipfs/go-unixfs@06009a2f87105928776bc9994cd712b034df01bd --- unixfs/archive/tar/writer.go | 11 +++++------ unixfs/hamt/hamt.go | 13 ++----------- unixfs/io/dagreader.go | 10 ++++------ unixfs/io/pbdagreader.go | 8 +++----- unixfs/unixfs.go | 17 +++++++++++++++++ 5 files changed, 31 insertions(+), 28 deletions(-) diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index 7e20e6d77..bc1253d3d 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -13,7 +13,6 @@ import ( mdag "github.com/ipfs/go-merkledag" ft "github.com/ipfs/go-unixfs" uio "github.com/ipfs/go-unixfs/io" - upb "github.com/ipfs/go-unixfs/pb" ipld "github.com/ipfs/go-ipld-format" ) @@ -79,15 +78,15 @@ func (w *Writer) WriteNode(nd ipld.Node, fpath string) error { } switch fsNode.Type() { - case upb.Data_Metadata: + case ft.TMetadata: fallthrough - case upb.Data_Directory, upb.Data_HAMTShard: + case ft.TDirectory, ft.THAMTShard: return w.writeDir(nd, fpath) - case upb.Data_Raw: + case ft.TRaw: fallthrough - case upb.Data_File: + case ft.TFile: return w.writeFile(nd, fsNode, fpath) - case upb.Data_Symlink: + case ft.TSymlink: return writeSymlinkHeader(w.TarW, string(fsNode.Data()), fpath) default: return ft.ErrUnrecognizedType diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index bd2144214..323929997 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -27,10 +27,7 @@ import ( dag "github.com/ipfs/go-merkledag" format "github.com/ipfs/go-unixfs" - upb "github.com/ipfs/go-unixfs/pb" - bitfield "github.com/Stebalien/go-bitfield" - proto "github.com/gogo/protobuf/proto" cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" "github.com/spaolacci/murmur3" @@ -108,7 +105,7 @@ func NewHamtFromDag(dserv ipld.DAGService, nd ipld.Node) (*Shard, error) { } - if fsn.Type() != upb.Data_HAMTShard { + if fsn.Type() != format.THAMTShard { return nil, fmt.Errorf("node was not a dir shard") } @@ -176,13 +173,7 @@ func (ds *Shard) Node() (ipld.Node, error) { cindex++ } - typ := upb.Data_HAMTShard - data, err := proto.Marshal(&upb.Data{ - Type: &typ, - Fanout: proto.Uint64(uint64(ds.tableSize)), - HashType: proto.Uint64(HashMurmur3), - Data: ds.bitfield.Bytes(), - }) + data, err := format.HAMTShardData(ds.bitfield.Bytes(), uint64(ds.tableSize), HashMurmur3) if err != nil { return nil, err } diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 02bb64afd..8d491db1b 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -7,8 +7,6 @@ import ( mdag "github.com/ipfs/go-merkledag" ft "github.com/ipfs/go-unixfs" - ftpb "github.com/ipfs/go-unixfs/pb" - ipld "github.com/ipfs/go-ipld-format" ) @@ -49,12 +47,12 @@ func NewDagReader(ctx context.Context, n ipld.Node, serv ipld.NodeGetter) (DagRe } switch fsNode.Type() { - case ftpb.Data_Directory, ftpb.Data_HAMTShard: + case ft.TDirectory, ft.THAMTShard: // Dont allow reading directories return nil, ErrIsDir - case ftpb.Data_File, ftpb.Data_Raw: + case ft.TFile, ft.TRaw: return NewPBFileReader(ctx, n, fsNode, serv), nil - case ftpb.Data_Metadata: + case ft.TMetadata: if len(n.Links()) == 0 { return nil, errors.New("incorrectly formatted metadata object") } @@ -68,7 +66,7 @@ func NewDagReader(ctx context.Context, n ipld.Node, serv ipld.NodeGetter) (DagRe return nil, mdag.ErrNotProtobuf } return NewDagReader(ctx, childpb, serv) - case ftpb.Data_Symlink: + case ft.TSymlink: return nil, ErrCantReadSymlinks default: return nil, ft.ErrUnrecognizedType diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index 5c4462850..a84f239ff 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -8,8 +8,6 @@ import ( mdag "github.com/ipfs/go-merkledag" ft "github.com/ipfs/go-unixfs" - ftpb "github.com/ipfs/go-unixfs/pb" - cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" ) @@ -134,10 +132,10 @@ func (dr *PBDagReader) loadBufNode(node ipld.Node) error { } switch fsNode.Type() { - case ftpb.Data_File: + case ft.TFile: dr.buf = NewPBFileReader(dr.ctx, node, fsNode, dr.serv) return nil - case ftpb.Data_Raw: + case ft.TRaw: dr.buf = NewBufDagReader(fsNode.Data()) return nil default: @@ -318,7 +316,7 @@ func (dr *PBDagReader) Seek(offset int64, whence int) (int64, error) { // for this seems to be good(-enough) solution as it's only returned by // precalcNextBuf when we step out of file range. // This is needed for gateway to function properly - if err == io.EOF && dr.file.Type() == ftpb.Data_File { + if err == io.EOF && dr.file.Type() == ft.TFile { return -1, nil } return n, err diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index db05fcc2f..7b4189153 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -106,6 +106,23 @@ func SymlinkData(path string) ([]byte, error) { return out, nil } +// HAMTShardData return a `Data_HAMTShard` protobuf message +func HAMTShardData(data []byte, fanout uint64, hashType uint64) ([]byte, error) { + pbdata := new(pb.Data) + typ := pb.Data_HAMTShard + pbdata.Type = &typ + pbdata.HashType = proto.Uint64(hashType) + pbdata.Data = data + pbdata.Fanout = proto.Uint64(fanout) + + out, err := proto.Marshal(pbdata) + if err != nil { + return nil, err + } + + return out, nil +} + // UnwrapData unmarshals a protobuf messages and returns the contents. func UnwrapData(data []byte) ([]byte, error) { pbdata := new(pb.Data) From 398a8ffdf544dac64be994ce4a34ef3013d9d584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 18:54:58 +0100 Subject: [PATCH 2638/3817] coreapi: swarm interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@bc2ae0a441ed2da875bf2d815766e90b2c2a4d9f --- coreiface/swarm.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 coreiface/swarm.go diff --git a/coreiface/swarm.go b/coreiface/swarm.go new file mode 100644 index 000000000..1ec260e07 --- /dev/null +++ b/coreiface/swarm.go @@ -0,0 +1,37 @@ +package iface + +import ( + "time" + + "context" + ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" +) + +// PeerInfo contains information about a peer +type PeerInfo interface { + // ID returns PeerID + ID() peer.ID + + // Address returns the multiaddress via which we are connected with the peer + Address() ma.Multiaddr + + // Latency returns last known round trip time to the peer + Latency() time.Duration + + // Streams returns list of streams established with the peer + // TODO: should this return multicodecs? + Streams() []string +} + +// SwarmAPI specifies the interface to libp2p swarm +type SwarmAPI interface { + // Connect to a given address + Connect(context.Context, ma.Multiaddr) error + + // Disconnect from a given address + Disconnect(context.Context, ma.Multiaddr) error + + // Peers returns the list of peers we are connected to + Peers(context.Context) ([]PeerInfo, error) +} From 81615a9f48e0bba90d04b4ac389948124aeff053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 19:02:57 +0100 Subject: [PATCH 2639/3817] coreapi: implement swarm api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@3ab7f14240700d0e64e011e5fca7c847afc9ce9e --- coreiface/coreapi.go | 3 +++ coreiface/swarm.go | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index 0053d472e..0b153b6f9 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -34,6 +34,9 @@ type CoreAPI interface { // Dht returns an implementation of Dht API Dht() DhtAPI + // Swarm returns an implementation of Swarm API + Swarm() SwarmAPI + // ResolvePath resolves the path using Unixfs resolver ResolvePath(context.Context, Path) (ResolvedPath, error) diff --git a/coreiface/swarm.go b/coreiface/swarm.go index 1ec260e07..1f0b1216f 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -1,9 +1,9 @@ package iface import ( + "context" "time" - "context" ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" ) @@ -17,11 +17,11 @@ type PeerInfo interface { Address() ma.Multiaddr // Latency returns last known round trip time to the peer - Latency() time.Duration + Latency(context.Context) (time.Duration, error) // Streams returns list of streams established with the peer // TODO: should this return multicodecs? - Streams() []string + Streams(context.Context) ([]string, error) } // SwarmAPI specifies the interface to libp2p swarm From 7e79c2365ce422f3c26401f2a0c8e1da99515aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 5 Apr 2018 20:22:49 +0200 Subject: [PATCH 2640/3817] fix infinite loop in connInfo.ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@89ce6041ad9b851959cdd2434d4f7ab375459599 --- coreiface/swarm.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/swarm.go b/coreiface/swarm.go index 1f0b1216f..92817e6f4 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -4,8 +4,8 @@ import ( "context" "time" - ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" + peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" + ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" ) // PeerInfo contains information about a peer From 6e9149c6f900ec598b0345cc97b0979b53ba8cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 11 Sep 2018 20:50:15 +0200 Subject: [PATCH 2641/3817] swarm cmd: port to new cmd lib MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@59f549fe3aa60f48303f6283e09ae05c9224a84c --- coreiface/swarm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/swarm.go b/coreiface/swarm.go index 92817e6f4..2492f2696 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -4,8 +4,8 @@ import ( "context" "time" - peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" + peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" ) // PeerInfo contains information about a peer From 0f6f6ec8a4e5b41f01c658bf46f4d30b69a7ade5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 17 Sep 2018 16:45:59 +0200 Subject: [PATCH 2642/3817] coreapi swarm: rewire connect/disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@a48b2a807b98f5fe7eb4c1148bf938498a4836ce --- coreiface/swarm.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/coreiface/swarm.go b/coreiface/swarm.go index 2492f2696..7bd009f16 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -2,14 +2,22 @@ package iface import ( "context" + "errors" "time" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" - peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol" + "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + pstore "gx/ipfs/QmfAQMFpgDU2U4BXG64qVr8HSiictfWvkSBz7Y2oDj65st/go-libp2p-peerstore" ) -// PeerInfo contains information about a peer -type PeerInfo interface { +var ( + ErrNotConnected = errors.New("not connected") + ErrConnNotFound = errors.New("conn not found") + ) + +// ConnectionInfo contains information about a peer +type ConnectionInfo interface { // ID returns PeerID ID() peer.ID @@ -20,18 +28,17 @@ type PeerInfo interface { Latency(context.Context) (time.Duration, error) // Streams returns list of streams established with the peer - // TODO: should this return multicodecs? - Streams(context.Context) ([]string, error) + Streams(context.Context) ([]protocol.ID, error) } // SwarmAPI specifies the interface to libp2p swarm type SwarmAPI interface { - // Connect to a given address - Connect(context.Context, ma.Multiaddr) error + // Connect to a given peer + Connect(context.Context, pstore.PeerInfo) error // Disconnect from a given address Disconnect(context.Context, ma.Multiaddr) error // Peers returns the list of peers we are connected to - Peers(context.Context) ([]PeerInfo, error) + Peers(context.Context) ([]ConnectionInfo, error) } From 4020059a81eb3f31fec83c825d6ce4d9dd8c65c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 17 Sep 2018 19:53:15 +0200 Subject: [PATCH 2643/3817] coreapi swarm: rewire address listing cmds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@8c35696f74f086858b6acaf8ee6a0ffe570e46cb --- coreiface/key.go | 3 +++ coreiface/swarm.go | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/coreiface/key.go b/coreiface/key.go index 4305ae20d..cc6dc8900 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -33,6 +33,9 @@ type KeyAPI interface { // List lists keys stored in keystore List(ctx context.Context) ([]Key, error) + // Self returns the 'main' node key + Self(ctx context.Context) (Key, error) + // Remove removes keys from keystore. Returns ipns path of the removed key Remove(ctx context.Context, name string) (Key, error) } diff --git a/coreiface/swarm.go b/coreiface/swarm.go index 7bd009f16..caa6a70e3 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -9,12 +9,13 @@ import ( "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol" "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" pstore "gx/ipfs/QmfAQMFpgDU2U4BXG64qVr8HSiictfWvkSBz7Y2oDj65st/go-libp2p-peerstore" + net "gx/ipfs/QmfDPh144WGBqRxZb1TGDHerbMnZATrHZggAPw7putNnBq/go-libp2p-net" ) var ( ErrNotConnected = errors.New("not connected") ErrConnNotFound = errors.New("conn not found") - ) +) // ConnectionInfo contains information about a peer type ConnectionInfo interface { @@ -24,6 +25,9 @@ type ConnectionInfo interface { // Address returns the multiaddress via which we are connected with the peer Address() ma.Multiaddr + // Direction returns which way the connection was established + Direction() net.Direction + // Latency returns last known round trip time to the peer Latency(context.Context) (time.Duration, error) @@ -41,4 +45,8 @@ type SwarmAPI interface { // Peers returns the list of peers we are connected to Peers(context.Context) ([]ConnectionInfo, error) + + KnownAddrs(context.Context) (map[peer.ID][]ma.Multiaddr, error) + LocalAddrs(context.Context) ([]ma.Multiaddr, error) + ListenAddrs(context.Context) ([]ma.Multiaddr, error) } From ed664f50df387068ee2582e24c8682877e62d4ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 28 Aug 2018 00:44:51 +0200 Subject: [PATCH 2644/3817] namesys: Implement async methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@25d4dea9e0aa21895e388e5e7510c5e1ecac9e0c --- namesys/base.go | 95 +++++++++++++++++++++++++----- namesys/dns.go | 57 +++++++++++++++++- namesys/interface.go | 11 ++++ namesys/namesys.go | 88 +++++++++++++++++++++++++++- namesys/opts/opts.go | 8 +-- namesys/proquint.go | 16 ++++- namesys/routing.go | 135 ++++++++++++++++++++++++++++++++++++++++++- 7 files changed, 388 insertions(+), 22 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index afdc0a468..6e5dc70e0 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -10,13 +10,21 @@ import ( path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" ) +type onceResult struct { + value path.Path + ttl time.Duration + err error +} + type resolver interface { // resolveOnce looks up a name once (without recursion). - resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (value path.Path, ttl time.Duration, err error) + resolveOnce(ctx context.Context, name string, options opts.ResolveOpts) (value path.Path, ttl time.Duration, err error) + + resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult } // resolve is a helper for implementing Resolver.ResolveN using resolveOnce. -func resolve(ctx context.Context, r resolver, name string, options *opts.ResolveOpts, prefixes ...string) (path.Path, error) { +func resolve(ctx context.Context, r resolver, name string, options opts.ResolveOpts, prefix string) (path.Path, error) { depth := options.Depth for { p, _, err := r.resolveOnce(ctx, name, options) @@ -34,23 +42,82 @@ func resolve(ctx context.Context, r resolver, name string, options *opts.Resolve return p, ErrResolveRecursion } - matched := false - for _, prefix := range prefixes { - if strings.HasPrefix(p.String(), prefix) { - matched = true - if len(prefixes) == 1 { - name = strings.TrimPrefix(p.String(), prefix) - } - break - } - } - - if !matched { + if !strings.HasPrefix(p.String(), prefix) { return p, nil } + name = strings.TrimPrefix(p.String(), prefix) if depth > 1 { depth-- } } } + +//TODO: +// - better error handling +func resolveAsyncDo(ctx context.Context, r resolver, name string, options opts.ResolveOpts, prefix string) <-chan Result { + resCh := r.resolveOnceAsync(ctx, name, options) + depth := options.Depth + outCh := make(chan Result) + + go func() { + defer close(outCh) + var subCh <-chan Result + var cancelSub context.CancelFunc + + for { + select { + case res, ok := <-resCh: + if res.err != nil { + outCh <- Result{err: res.err} + return + } + if !ok { + resCh = nil + continue + } + log.Debugf("resolved %s to %s", name, res.value.String()) + if strings.HasPrefix(res.value.String(), "/ipfs/") { + outCh <- Result{err: res.err} + continue + } + p := strings.TrimPrefix(res.value.String(), prefix) + + if depth == 1 { + outCh <- Result{err: ErrResolveRecursion} + continue + } + + subopts := options + if subopts.Depth > 1 { + subopts.Depth-- + } + + var subCtx context.Context + if subCh != nil { + // Cancel previous recursive resolve since it won't be used anyways + cancelSub() + } + subCtx, cancelSub = context.WithCancel(ctx) + + subCh = resolveAsyncDo(subCtx, r, p, subopts, prefix) + case res, ok := <-subCh: + if res.err != nil { + outCh <- Result{err: res.err} + return + } + if !ok { + subCh = nil + continue + } + outCh <- res + case <-ctx.Done(): + } + } + }() + return outCh +} + +func resolveAsync(ctx context.Context, r resolver, name string, options opts.ResolveOpts, prefix string) <-chan Result { + return resolveAsyncDo(ctx, r, name, options, prefix) +} diff --git a/namesys/dns.go b/namesys/dns.go index 5ca7323a9..a8e0b0fc2 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -39,7 +39,7 @@ type lookupRes struct { // resolveOnce implements resolver. // TXT records for a given domain name should contain a b58 // encoded multihash. -func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) { +func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options opts.ResolveOpts) (path.Path, time.Duration, error) { segments := strings.SplitN(name, "/", 2) domain := segments[0] @@ -84,6 +84,61 @@ func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options *opt return p, 0, err } +func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { + out := make(chan onceResult, 1) + segments := strings.SplitN(name, "/", 2) + domain := segments[0] + + if !isd.IsDomain(domain) { + out <- onceResult{err: errors.New("not a valid domain name")} + close(out) + return out + } + log.Debugf("DNSResolver resolving %s", domain) + + rootChan := make(chan lookupRes, 1) + go workDomain(r, domain, rootChan) + + subChan := make(chan lookupRes, 1) + go workDomain(r, "_dnslink."+domain, subChan) + + go func() { + defer close(out) + for { + select { + case subRes, ok := <-subChan: + if !ok { + subChan = nil + } + if subRes.error == nil { + select { + case out <- onceResult{value: subRes.path}: + case <-ctx.Done(): + } + return + } + case rootRes, ok := <-rootChan: + if !ok { + subChan = nil + } + if rootRes.error == nil { + select { + case out <- onceResult{value: rootRes.path}: + case <-ctx.Done(): + } + } + case <-ctx.Done(): + return + } + if subChan == nil && rootChan == nil { + return + } + } + }() + + return out +} + func workDomain(r *DNSResolver, name string, res chan lookupRes) { txt, err := r.lookupTXT(name) diff --git a/namesys/interface.go b/namesys/interface.go index fcc956cb1..aac3f324a 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -63,6 +63,12 @@ type NameSystem interface { Publisher } +// Result is the return type for Resolver.ResolveAsync. +type Result struct { + path path.Path + err error +} + // Resolver is an object capable of resolving names. type Resolver interface { @@ -81,6 +87,11 @@ type Resolver interface { // users will be fine with this default limit, but if you need to // adjust the limit you can specify it as an option. Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (value path.Path, err error) + + // ResolveAsync performs recursive name lookup, like Resolve, but it returns + // entries as they are discovered in the DHT. Each returned result is guaranteed + // to be "better" (which usually means newer) than the previous one. + ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result } // Publisher is an object capable of publishing particular names. diff --git a/namesys/namesys.go b/namesys/namesys.go index 054a51bbd..0842486ea 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -64,8 +64,25 @@ func (ns *mpns) Resolve(ctx context.Context, name string, options ...opts.Resolv return resolve(ctx, ns, name, opts.ProcessOpts(options), "/ipns/") } +func (ns *mpns) ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result { + res := make(chan Result, 1) + if strings.HasPrefix(name, "/ipfs/") { + p, err := path.ParsePath(name) + res <- Result{p, err} + return res + } + + if !strings.HasPrefix(name, "/") { + p, err := path.ParsePath("/ipfs/" + name) + res <- Result{p, err} + return res + } + + return resolveAsync(ctx, ns, name, opts.ProcessOpts(options), "/ipns/") +} + // resolveOnce implements resolver. -func (ns *mpns) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) { +func (ns *mpns) resolveOnce(ctx context.Context, name string, options opts.ResolveOpts) (path.Path, time.Duration, error) { if !strings.HasPrefix(name, "/ipns/") { name = "/ipns/" + name } @@ -107,6 +124,75 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string, options *opts.Reso return p, 0, err } +func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { + out := make(chan onceResult, 1) + + if !strings.HasPrefix(name, "/ipns/") { + name = "/ipns/" + name + } + segments := strings.SplitN(name, "/", 4) + if len(segments) < 3 || segments[0] != "" { + log.Debugf("invalid name syntax for %s", name) + out <- onceResult{err: ErrResolveFailed} + close(out) + return out + } + + key := segments[2] + + if p, ok := ns.cacheGet(key); ok { + out <- onceResult{value: p} + close(out) + return out + } + + // Resolver selection: + // 1. if it is a multihash resolve through "ipns". + // 2. if it is a domain name, resolve through "dns" + // 3. otherwise resolve through the "proquint" resolver + + var res resolver + if _, err := mh.FromB58String(key); err == nil { + res = ns.ipnsResolver + } else if isd.IsDomain(key) { + res = ns.dnsResolver + } else { + res = ns.proquintResolver + } + + resCh := res.resolveOnceAsync(ctx, key, options) + var best onceResult + go func() { + defer close(out) + for { + select { + case res, ok := <-resCh: + if !ok { + if best != (onceResult{}) { + ns.cacheSet(key, best.value, best.ttl) + } + return + } + if res.err == nil { + best = res + } + p := res.value + + // Attach rest of the path + if len(segments) > 3 { + p, _ = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) + } + + out <- onceResult{value: p, err: res.err} + case <-ctx.Done(): + return + } + } + }() + + return out +} + // Publish implements Publisher func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error { return ns.PublishWithEOL(ctx, name, value, time.Now().Add(DefaultRecordTTL)) diff --git a/namesys/opts/opts.go b/namesys/opts/opts.go index 6690cf779..ee2bd5ac2 100644 --- a/namesys/opts/opts.go +++ b/namesys/opts/opts.go @@ -31,8 +31,8 @@ type ResolveOpts struct { // DefaultResolveOpts returns the default options for resolving // an IPNS path -func DefaultResolveOpts() *ResolveOpts { - return &ResolveOpts{ +func DefaultResolveOpts() ResolveOpts { + return ResolveOpts{ Depth: DefaultDepthLimit, DhtRecordCount: 16, DhtTimeout: time.Minute, @@ -65,10 +65,10 @@ func DhtTimeout(timeout time.Duration) ResolveOpt { } // ProcessOpts converts an array of ResolveOpt into a ResolveOpts object -func ProcessOpts(opts []ResolveOpt) *ResolveOpts { +func ProcessOpts(opts []ResolveOpt) ResolveOpts { rsopts := DefaultResolveOpts() for _, option := range opts { - option(rsopts) + option(&rsopts) } return rsopts } diff --git a/namesys/proquint.go b/namesys/proquint.go index 4ba505dc8..279c361fd 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -19,7 +19,7 @@ func (r *ProquintResolver) Resolve(ctx context.Context, name string, options ... } // resolveOnce implements resolver. Decodes the proquint string. -func (r *ProquintResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) { +func (r *ProquintResolver) resolveOnce(ctx context.Context, name string, options opts.ResolveOpts) (path.Path, time.Duration, error) { ok, err := proquint.IsProquint(name) if err != nil || !ok { return "", 0, errors.New("not a valid proquint string") @@ -27,3 +27,17 @@ func (r *ProquintResolver) resolveOnce(ctx context.Context, name string, options // Return a 0 TTL as caching this result is pointless. return path.FromString(string(proquint.Decode(name))), 0, nil } + +func (r *ProquintResolver) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { + out := make(chan onceResult, 1) + defer close(out) + + ok, err := proquint.IsProquint(name) + if err != nil || !ok { + out <- onceResult{err: errors.New("not a valid proquint string")} + return out + } + // Return a 0 TTL as caching this result is pointless. + out <- onceResult{value: path.FromString(string(proquint.Decode(name)))} + return out +} diff --git a/namesys/routing.go b/namesys/routing.go index dc53868ad..d633c2d8d 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -42,9 +42,13 @@ func (r *IpnsResolver) Resolve(ctx context.Context, name string, options ...opts return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/") } +func (r *IpnsResolver) ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result { + return resolveAsync(ctx, r, name, opts.ProcessOpts(options), "/ipns/") +} + // resolveOnce implements resolver. Uses the IPFS routing system to // resolve SFS-like names. -func (r *IpnsResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) { +func (r *IpnsResolver) resolveOnce(ctx context.Context, name string, options opts.ResolveOpts) (path.Path, time.Duration, error) { log.Debugf("RoutingResolver resolving %s", name) if options.DhtTimeout != 0 { @@ -126,3 +130,132 @@ func (r *IpnsResolver) resolveOnce(ctx context.Context, name string, options *op return p, ttl, nil } + +func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { + out := make(chan onceResult, 1) + log.Debugf("RoutingResolver resolving %s", name) + if options.DhtTimeout != 0 { + // Resolution must complete within the timeout + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, options.DhtTimeout) + defer cancel() + } + + name = strings.TrimPrefix(name, "/ipns/") + hash, err := mh.FromB58String(name) + if err != nil { + // name should be a multihash. if it isn't, error out here. + log.Debugf("RoutingResolver: bad input hash: [%s]\n", name) + out <- onceResult{err: err} + close(out) + return out + } + + pid, err := peer.IDFromBytes(hash) + if err != nil { + log.Debugf("RoutingResolver: could not convert public key hash %s to peer ID: %s\n", name, err) + out <- onceResult{err: err} + close(out) + return out + } + + // Name should be the hash of a public key retrievable from ipfs. + // We retrieve the public key here to make certain that it's in the peer + // store before calling GetValue() on the DHT - the DHT will call the + // ipns validator, which in turn will get the public key from the peer + // store to verify the record signature + _, err = routing.GetPublicKey(r.routing, ctx, pid) + if err != nil { + log.Debugf("RoutingResolver: could not retrieve public key %s: %s\n", name, err) + out <- onceResult{err: err} + close(out) + return out + } + + // Use the routing system to get the name. + // Note that the DHT will call the ipns validator when retrieving + // the value, which in turn verifies the ipns record signature + ipnsKey := ipns.RecordKey(pid) + + vals, err := r.routing.(*dht.IpfsDHT).SearchValue(ctx, ipnsKey, dht.Quorum(int(options.DhtRecordCount))) + if err != nil { + log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err) + out <- onceResult{err: err} + close(out) + return out + } + + go func() { + defer close(out) + for { + select { + case val, ok := <-vals: + if !ok { + return + } + + entry := new(pb.IpnsEntry) + err = proto.Unmarshal(val, entry) + if err != nil { + log.Debugf("RoutingResolver: could not unmarshal value for name %s: %s", name, err) + select { + case out <- onceResult{err: err}: + case <-ctx.Done(): + } + return + } + + var p path.Path + // check for old style record: + if valh, err := mh.Cast(entry.GetValue()); err == nil { + // Its an old style multihash record + log.Debugf("encountered CIDv0 ipns entry: %s", valh) + p = path.FromCid(cid.NewCidV0(valh)) + } else { + // Not a multihash, probably a new style record + p, err = path.ParsePath(string(entry.GetValue())) + if err != nil { + select { + case out <- onceResult{err: err}: + case <-ctx.Done(): + } + return + } + } + + ttl := DefaultResolverCacheTTL + if entry.Ttl != nil { + ttl = time.Duration(*entry.Ttl) + } + switch eol, err := ipns.GetEOL(entry); err { + case ipns.ErrUnrecognizedValidity: + // No EOL. + case nil: + ttEol := eol.Sub(time.Now()) + if ttEol < 0 { + // It *was* valid when we first resolved it. + ttl = 0 + } else if ttEol < ttl { + ttl = ttEol + } + default: + log.Errorf("encountered error when parsing EOL: %s", err) + select { + case out <- onceResult{err: err}: + case <-ctx.Done(): + } + return + } + + select { + case out <- onceResult{value: p, ttl: ttl}: + case <-ctx.Done(): + } + case <-ctx.Done(): + return + } + } + }() + + return out +} From 0fda20a6c294db3ec3d7447a040cf4d5846be8e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 28 Aug 2018 01:06:12 +0200 Subject: [PATCH 2645/3817] namesys: async: go vet fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@e848eb7e776f179c27930488b20534c19dddacf1 --- namesys/base.go | 24 ++++++++++++++---------- namesys/dns.go | 8 ++++++-- namesys/interface.go | 4 ++-- namesys/namesys_test.go | 6 +++++- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 6e5dc70e0..b3610cc0d 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -68,23 +68,24 @@ func resolveAsyncDo(ctx context.Context, r resolver, name string, options opts.R for { select { case res, ok := <-resCh: - if res.err != nil { - outCh <- Result{err: res.err} - return - } if !ok { resCh = nil continue } + + if res.err != nil { + outCh <- Result{Err: res.err} + return + } log.Debugf("resolved %s to %s", name, res.value.String()) if strings.HasPrefix(res.value.String(), "/ipfs/") { - outCh <- Result{err: res.err} + outCh <- Result{Err: res.err} continue } p := strings.TrimPrefix(res.value.String(), prefix) if depth == 1 { - outCh <- Result{err: ErrResolveRecursion} + outCh <- Result{Err: ErrResolveRecursion} continue } @@ -99,17 +100,20 @@ func resolveAsyncDo(ctx context.Context, r resolver, name string, options opts.R cancelSub() } subCtx, cancelSub = context.WithCancel(ctx) + defer cancelSub() subCh = resolveAsyncDo(subCtx, r, p, subopts, prefix) case res, ok := <-subCh: - if res.err != nil { - outCh <- Result{err: res.err} - return - } if !ok { subCh = nil continue } + + if res.Err != nil { + outCh <- Result{Err: res.Err} + return + } + outCh <- res case <-ctx.Done(): } diff --git a/namesys/dns.go b/namesys/dns.go index a8e0b0fc2..d92ce8fa7 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -31,6 +31,10 @@ func (r *DNSResolver) Resolve(ctx context.Context, name string, options ...opts. return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/") } +func (r *DNSResolver) ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result { + return resolveAsync(ctx, r, name, opts.ProcessOpts(options), "/ipns/") +} + type lookupRes struct { path path.Path error error @@ -112,8 +116,8 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options } if subRes.error == nil { select { - case out <- onceResult{value: subRes.path}: - case <-ctx.Done(): + case out <- onceResult{value: subRes.path}: + case <-ctx.Done(): } return } diff --git a/namesys/interface.go b/namesys/interface.go index aac3f324a..dd195cc9a 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -65,8 +65,8 @@ type NameSystem interface { // Result is the return type for Resolver.ResolveAsync. type Result struct { - path path.Path - err error + Path path.Path + Err error } // Resolver is an object capable of resolving names. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index c2a530c26..23a1852f8 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -38,11 +38,15 @@ func testResolution(t *testing.T, resolver Resolver, name string, depth uint, ex } } -func (r *mockResolver) resolveOnce(ctx context.Context, name string, opts *opts.ResolveOpts) (path.Path, time.Duration, error) { +func (r *mockResolver) resolveOnce(ctx context.Context, name string, opts opts.ResolveOpts) (path.Path, time.Duration, error) { p, err := path.ParsePath(r.entries[name]) return p, 0, err } +func (r *mockResolver) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { + panic("stub") +} + func mockResolverOne() *mockResolver { return &mockResolver{ entries: map[string]string{ From 98a9b5448695c282b7278aac2f98477c3f35977c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 28 Aug 2018 15:37:12 +0200 Subject: [PATCH 2646/3817] namesys: switch to async code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@314cdfecb6942f50ec0772c932b8d9736ed98a54 --- namesys/base.go | 66 +++++-------- namesys/dns.go | 67 ++++--------- namesys/ipns_resolver_validation_test.go | 11 +-- namesys/namesys.go | 42 -------- namesys/namesys_test.go | 14 ++- namesys/proquint.go | 13 +-- namesys/routing.go | 117 ++++------------------- 7 files changed, 74 insertions(+), 256 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index b3610cc0d..c24832799 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -17,45 +17,33 @@ type onceResult struct { } type resolver interface { - // resolveOnce looks up a name once (without recursion). - resolveOnce(ctx context.Context, name string, options opts.ResolveOpts) (value path.Path, ttl time.Duration, err error) - resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult } // resolve is a helper for implementing Resolver.ResolveN using resolveOnce. func resolve(ctx context.Context, r resolver, name string, options opts.ResolveOpts, prefix string) (path.Path, error) { - depth := options.Depth - for { - p, _, err := r.resolveOnce(ctx, name, options) - if err != nil { - return "", err - } - log.Debugf("resolved %s to %s", name, p.String()) + ctx, cancel := context.WithCancel(ctx) + defer cancel() - if strings.HasPrefix(p.String(), "/ipfs/") { - // we've bottomed out with an IPFS path - return p, nil - } + err := ErrResolveFailed + var p path.Path - if depth == 1 { - return p, ErrResolveRecursion - } + resCh := resolveAsync(ctx, r, name, options, prefix) - if !strings.HasPrefix(p.String(), prefix) { - return p, nil - } - name = strings.TrimPrefix(p.String(), prefix) - - if depth > 1 { - depth-- + for res := range resCh { + p, err = res.Path, res.Err + if err != nil { + break } } + + return p, err } //TODO: // - better error handling -func resolveAsyncDo(ctx context.Context, r resolver, name string, options opts.ResolveOpts, prefix string) <-chan Result { +// - select on writes +func resolveAsync(ctx context.Context, r resolver, name string, options opts.ResolveOpts, prefix string) <-chan Result { resCh := r.resolveOnceAsync(ctx, name, options) depth := options.Depth outCh := make(chan Result) @@ -70,7 +58,7 @@ func resolveAsyncDo(ctx context.Context, r resolver, name string, options opts.R case res, ok := <-resCh: if !ok { resCh = nil - continue + break } if res.err != nil { @@ -79,14 +67,13 @@ func resolveAsyncDo(ctx context.Context, r resolver, name string, options opts.R } log.Debugf("resolved %s to %s", name, res.value.String()) if strings.HasPrefix(res.value.String(), "/ipfs/") { - outCh <- Result{Err: res.err} - continue + outCh <- Result{Path: res.value} + break } - p := strings.TrimPrefix(res.value.String(), prefix) if depth == 1 { - outCh <- Result{Err: ErrResolveRecursion} - continue + outCh <- Result{Path: res.value, Err: ErrResolveRecursion} + break } subopts := options @@ -102,26 +89,21 @@ func resolveAsyncDo(ctx context.Context, r resolver, name string, options opts.R subCtx, cancelSub = context.WithCancel(ctx) defer cancelSub() - subCh = resolveAsyncDo(subCtx, r, p, subopts, prefix) + p := strings.TrimPrefix(res.value.String(), prefix) + subCh = resolveAsync(subCtx, r, p, subopts, prefix) case res, ok := <-subCh: if !ok { subCh = nil - continue - } - - if res.Err != nil { - outCh <- Result{Err: res.Err} - return + break } outCh <- res case <-ctx.Done(): } + if resCh == nil && subCh == nil { + return + } } }() return outCh } - -func resolveAsync(ctx context.Context, r resolver, name string, options opts.ResolveOpts, prefix string) <-chan Result { - return resolveAsyncDo(ctx, r, name, options, prefix) -} diff --git a/namesys/dns.go b/namesys/dns.go index d92ce8fa7..f90880de9 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -5,7 +5,6 @@ import ( "errors" "net" "strings" - "time" opts "github.com/ipfs/go-ipfs/namesys/opts" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" @@ -31,6 +30,7 @@ func (r *DNSResolver) Resolve(ctx context.Context, name string, options ...opts. return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/") } +// ResolveAsync implements Resolver. func (r *DNSResolver) ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result { return resolveAsync(ctx, r, name, opts.ProcessOpts(options), "/ipns/") } @@ -43,51 +43,6 @@ type lookupRes struct { // resolveOnce implements resolver. // TXT records for a given domain name should contain a b58 // encoded multihash. -func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options opts.ResolveOpts) (path.Path, time.Duration, error) { - segments := strings.SplitN(name, "/", 2) - domain := segments[0] - - if !isd.IsDomain(domain) { - return "", 0, errors.New("not a valid domain name") - } - log.Debugf("DNSResolver resolving %s", domain) - - rootChan := make(chan lookupRes, 1) - go workDomain(r, domain, rootChan) - - subChan := make(chan lookupRes, 1) - go workDomain(r, "_dnslink."+domain, subChan) - - var subRes lookupRes - select { - case subRes = <-subChan: - case <-ctx.Done(): - return "", 0, ctx.Err() - } - - var p path.Path - if subRes.error == nil { - p = subRes.path - } else { - var rootRes lookupRes - select { - case rootRes = <-rootChan: - case <-ctx.Done(): - return "", 0, ctx.Err() - } - if rootRes.error == nil { - p = rootRes.path - } else { - return "", 0, ErrResolveFailed - } - } - var err error - if len(segments) > 1 { - p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[1]) - } - return p, 0, err -} - func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { out := make(chan onceResult, 1) segments := strings.SplitN(name, "/", 2) @@ -106,6 +61,13 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options subChan := make(chan lookupRes, 1) go workDomain(r, "_dnslink."+domain, subChan) + appendPath := func(p path.Path) (path.Path, error) { + if len(segments) > 1 { + return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[1]) + } + return p, nil + } + go func() { defer close(out) for { @@ -113,21 +75,25 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options case subRes, ok := <-subChan: if !ok { subChan = nil + break } if subRes.error == nil { + p, err := appendPath(subRes.path) select { - case out <- onceResult{value: subRes.path}: + case out <- onceResult{value: p, err: err}: case <-ctx.Done(): } return } case rootRes, ok := <-rootChan: if !ok { - subChan = nil + rootChan = nil + break } if rootRes.error == nil { + p, err := appendPath(rootRes.path) select { - case out <- onceResult{value: rootRes.path}: + case out <- onceResult{value: p, err: err}: case <-ctx.Done(): } } @@ -144,8 +110,9 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options } func workDomain(r *DNSResolver, name string, res chan lookupRes) { - txt, err := r.lookupTXT(name) + defer close(res) + txt, err := r.lookupTXT(name) if err != nil { // Error is != nil res <- lookupRes{"", err} diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index cc99bf1d7..36e5fdc67 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -57,14 +57,13 @@ func TestResolverValidation(t *testing.T) { } // Resolve entry - resp, _, err := resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts()) + resp, err := resolve(ctx, resolver, id.Pretty(), opts.DefaultResolveOpts(), "/ipns/") if err != nil { t.Fatal(err) } if resp != path.Path(p) { t.Fatalf("Mismatch between published path %s and resolved path %s", p, resp) } - // Create expired entry expiredEntry, err := ipns.Create(priv, p, 1, ts.Add(-1*time.Hour)) if err != nil { @@ -78,7 +77,7 @@ func TestResolverValidation(t *testing.T) { } // Record should fail validation because entry is expired - _, _, err = resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts()) + _, err = resolve(ctx, resolver, id.Pretty(), opts.DefaultResolveOpts(), "/ipns/") if err == nil { t.Fatal("ValidateIpnsRecord should have returned error") } @@ -100,7 +99,7 @@ func TestResolverValidation(t *testing.T) { // Record should fail validation because public key defined by // ipns path doesn't match record signature - _, _, err = resolver.resolveOnce(ctx, id2.Pretty(), opts.DefaultResolveOpts()) + _, err = resolve(ctx, resolver, id2.Pretty(), opts.DefaultResolveOpts(), "/ipns/") if err == nil { t.Fatal("ValidateIpnsRecord should have failed signature verification") } @@ -118,7 +117,7 @@ func TestResolverValidation(t *testing.T) { // Record should fail validation because public key is not available // in peer store or on network - _, _, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts()) + _, err = resolve(ctx, resolver, id3.Pretty(), opts.DefaultResolveOpts(), "/ipns/") if err == nil { t.Fatal("ValidateIpnsRecord should have failed because public key was not found") } @@ -133,7 +132,7 @@ func TestResolverValidation(t *testing.T) { // public key is available in the peer store by looking it up in // the DHT, which causes the DHT to fetch it and cache it in the // peer store - _, _, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts()) + _, err = resolve(ctx, resolver, id3.Pretty(), opts.DefaultResolveOpts(), "/ipns/") if err != nil { t.Fatal(err) } diff --git a/namesys/namesys.go b/namesys/namesys.go index 0842486ea..1099997b2 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -82,48 +82,6 @@ func (ns *mpns) ResolveAsync(ctx context.Context, name string, options ...opts.R } // resolveOnce implements resolver. -func (ns *mpns) resolveOnce(ctx context.Context, name string, options opts.ResolveOpts) (path.Path, time.Duration, error) { - if !strings.HasPrefix(name, "/ipns/") { - name = "/ipns/" + name - } - segments := strings.SplitN(name, "/", 4) - if len(segments) < 3 || segments[0] != "" { - log.Debugf("invalid name syntax for %s", name) - return "", 0, ErrResolveFailed - } - - key := segments[2] - - p, ok := ns.cacheGet(key) - var err error - if !ok { - // Resolver selection: - // 1. if it is a multihash resolve through "ipns". - // 2. if it is a domain name, resolve through "dns" - // 3. otherwise resolve through the "proquint" resolver - var res resolver - if _, err := mh.FromB58String(key); err == nil { - res = ns.ipnsResolver - } else if isd.IsDomain(key) { - res = ns.dnsResolver - } else { - res = ns.proquintResolver - } - - var ttl time.Duration - p, ttl, err = res.resolveOnce(ctx, key, options) - if err != nil { - return "", 0, ErrResolveFailed - } - ns.cacheSet(key, p, ttl) - } - - if len(segments) > 3 { - p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) - } - return p, 0, err -} - func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { out := make(chan onceResult, 1) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 23a1852f8..09d5cf81e 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -4,12 +4,11 @@ import ( "context" "fmt" "testing" - "time" opts "github.com/ipfs/go-ipfs/namesys/opts" + "gx/ipfs/QmU4x3742bvgfxJsByEDpBnifJqjJdV6x528co4hwKCn46/go-unixfs" path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" - ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" offroute "gx/ipfs/QmScZySgru9jaoDa12sSfvh21sWbqF5eXkieTmJzAHJXkQ/go-ipfs-routing/offline" ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" @@ -38,13 +37,12 @@ func testResolution(t *testing.T, resolver Resolver, name string, depth uint, ex } } -func (r *mockResolver) resolveOnce(ctx context.Context, name string, opts opts.ResolveOpts) (path.Path, time.Duration, error) { - p, err := path.ParsePath(r.entries[name]) - return p, 0, err -} - func (r *mockResolver) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { - panic("stub") + p, err := path.ParsePath(r.entries[name]) + out := make(chan onceResult, 1) + out <- onceResult{value: p, err: err} + close(out) + return out } func mockResolverOne() *mockResolver { diff --git a/namesys/proquint.go b/namesys/proquint.go index 279c361fd..ad09fd48c 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -1,10 +1,8 @@ package namesys import ( + "context" "errors" - "time" - - context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" @@ -19,15 +17,6 @@ func (r *ProquintResolver) Resolve(ctx context.Context, name string, options ... } // resolveOnce implements resolver. Decodes the proquint string. -func (r *ProquintResolver) resolveOnce(ctx context.Context, name string, options opts.ResolveOpts) (path.Path, time.Duration, error) { - ok, err := proquint.IsProquint(name) - if err != nil || !ok { - return "", 0, errors.New("not a valid proquint string") - } - // Return a 0 TTL as caching this result is pointless. - return path.FromString(string(proquint.Decode(name))), 0, nil -} - func (r *ProquintResolver) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { out := make(chan onceResult, 1) defer close(out) diff --git a/namesys/routing.go b/namesys/routing.go index d633c2d8d..c591d5662 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -11,6 +11,7 @@ import ( cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" routing "gx/ipfs/QmVBnJDKhtFXTRVjXKinqpwGu8t1DyNqPKan2iGX8PR8xG/go-libp2p-routing" + ropts "gx/ipfs/QmVBnJDKhtFXTRVjXKinqpwGu8t1DyNqPKan2iGX8PR8xG/go-libp2p-routing/options" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" dht "gx/ipfs/QmZVakpN44VAUxs9eXAuUGLFYTCGmSyqSy6hyEKfMv68ME/go-libp2p-kad-dht" ipns "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns" @@ -42,120 +43,30 @@ func (r *IpnsResolver) Resolve(ctx context.Context, name string, options ...opts return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/") } +// ResolveAsync implements Resolver. func (r *IpnsResolver) ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result { return resolveAsync(ctx, r, name, opts.ProcessOpts(options), "/ipns/") } // resolveOnce implements resolver. Uses the IPFS routing system to // resolve SFS-like names. -func (r *IpnsResolver) resolveOnce(ctx context.Context, name string, options opts.ResolveOpts) (path.Path, time.Duration, error) { - log.Debugf("RoutingResolver resolving %s", name) - - if options.DhtTimeout != 0 { - // Resolution must complete within the timeout - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, options.DhtTimeout) - defer cancel() - } - - name = strings.TrimPrefix(name, "/ipns/") - pid, err := peer.IDB58Decode(name) - if err != nil { - // name should be a multihash. if it isn't, error out here. - log.Debugf("RoutingResolver: IPNS address not a valid peer ID: [%s]\n", name) - return "", 0, err - } - - // Name should be the hash of a public key retrievable from ipfs. - // We retrieve the public key here to make certain that it's in the peer - // store before calling GetValue() on the DHT - the DHT will call the - // ipns validator, which in turn will get the public key from the peer - // store to verify the record signature - _, err = routing.GetPublicKey(r.routing, ctx, pid) - if err != nil { - log.Debugf("RoutingResolver: could not retrieve public key %s: %s\n", name, err) - return "", 0, err - } - - // Use the routing system to get the name. - // Note that the DHT will call the ipns validator when retrieving - // the value, which in turn verifies the ipns record signature - ipnsKey := ipns.RecordKey(pid) - val, err := r.routing.GetValue(ctx, ipnsKey, dht.Quorum(int(options.DhtRecordCount))) - if err != nil { - log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err) - return "", 0, err - } - - entry := new(pb.IpnsEntry) - err = proto.Unmarshal(val, entry) - if err != nil { - log.Debugf("RoutingResolver: could not unmarshal value for name %s: %s", name, err) - return "", 0, err - } - - var p path.Path - // check for old style record: - if valh, err := mh.Cast(entry.GetValue()); err == nil { - // Its an old style multihash record - log.Debugf("encountered CIDv0 ipns entry: %s", valh) - p = path.FromCid(cid.NewCidV0(valh)) - } else { - // Not a multihash, probably a new record - p, err = path.ParsePath(string(entry.GetValue())) - if err != nil { - return "", 0, err - } - } - - ttl := DefaultResolverCacheTTL - if entry.Ttl != nil { - ttl = time.Duration(*entry.Ttl) - } - switch eol, err := ipns.GetEOL(entry); err { - case ipns.ErrUnrecognizedValidity: - // No EOL. - case nil: - ttEol := eol.Sub(time.Now()) - if ttEol < 0 { - // It *was* valid when we first resolved it. - ttl = 0 - } else if ttEol < ttl { - ttl = ttEol - } - default: - log.Errorf("encountered error when parsing EOL: %s", err) - return "", 0, err - } - - return p, ttl, nil -} - func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { out := make(chan onceResult, 1) log.Debugf("RoutingResolver resolving %s", name) + cancel := func() {} + if options.DhtTimeout != 0 { // Resolution must complete within the timeout - var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, options.DhtTimeout) - defer cancel() } name = strings.TrimPrefix(name, "/ipns/") - hash, err := mh.FromB58String(name) - if err != nil { - // name should be a multihash. if it isn't, error out here. - log.Debugf("RoutingResolver: bad input hash: [%s]\n", name) - out <- onceResult{err: err} - close(out) - return out - } - - pid, err := peer.IDFromBytes(hash) + pid, err := peer.IDB58Decode(name) if err != nil { log.Debugf("RoutingResolver: could not convert public key hash %s to peer ID: %s\n", name, err) out <- onceResult{err: err} close(out) + cancel() return out } @@ -169,6 +80,7 @@ func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, option log.Debugf("RoutingResolver: could not retrieve public key %s: %s\n", name, err) out <- onceResult{err: err} close(out) + cancel() return out } @@ -177,15 +89,17 @@ func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, option // the value, which in turn verifies the ipns record signature ipnsKey := ipns.RecordKey(pid) - vals, err := r.routing.(*dht.IpfsDHT).SearchValue(ctx, ipnsKey, dht.Quorum(int(options.DhtRecordCount))) + vals, err := r.searchValue(ctx, ipnsKey, dht.Quorum(int(options.DhtRecordCount))) if err != nil { log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err) out <- onceResult{err: err} close(out) + cancel() return out } go func() { + defer cancel() defer close(out) for { select { @@ -259,3 +173,14 @@ func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, option return out } + +func (r *IpnsResolver) searchValue(ctx context.Context, key string, opts ...ropts.Option) (<-chan []byte, error) { + if ir, ok := r.routing.(*dht.IpfsDHT); ok { + return ir.SearchValue(ctx, key, opts...) + } + out := make(chan []byte, 1) + val, err := r.routing.GetValue(ctx, key, opts...) + out <- val + close(out) + return out, err +} From 5956870df8f178cbd4e67a4751c45ae99c352b91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 29 Aug 2018 01:03:56 +0200 Subject: [PATCH 2647/3817] ipfs name resolve --stream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@0a8d77f746f8a2cdcb54aa8232cbc31c1fca32e0 --- namesys/namesys_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 09d5cf81e..301c98a5f 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,14 +7,14 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - "gx/ipfs/QmU4x3742bvgfxJsByEDpBnifJqjJdV6x528co4hwKCn46/go-unixfs" - path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" offroute "gx/ipfs/QmScZySgru9jaoDa12sSfvh21sWbqF5eXkieTmJzAHJXkQ/go-ipfs-routing/offline" + "gx/ipfs/QmU4x3742bvgfxJsByEDpBnifJqjJdV6x528co4hwKCn46/go-unixfs" ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" dssync "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/sync" ipns "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" pstoremem "gx/ipfs/QmfAQMFpgDU2U4BXG64qVr8HSiictfWvkSBz7Y2oDj65st/go-libp2p-peerstore/pstoremem" ) From f7d2c1743e944c95c5b7b47cc23fcc191abd5779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 24 Sep 2018 16:12:47 +0200 Subject: [PATCH 2648/3817] namesys: use routing.SearchValue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@c4dd494eed5de7a93bf091ef31e83ffaa125e3d4 --- namesys/routing.go | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/namesys/routing.go b/namesys/routing.go index c591d5662..6ac9f081a 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -11,7 +11,6 @@ import ( cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" routing "gx/ipfs/QmVBnJDKhtFXTRVjXKinqpwGu8t1DyNqPKan2iGX8PR8xG/go-libp2p-routing" - ropts "gx/ipfs/QmVBnJDKhtFXTRVjXKinqpwGu8t1DyNqPKan2iGX8PR8xG/go-libp2p-routing/options" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" dht "gx/ipfs/QmZVakpN44VAUxs9eXAuUGLFYTCGmSyqSy6hyEKfMv68ME/go-libp2p-kad-dht" ipns "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns" @@ -89,7 +88,7 @@ func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, option // the value, which in turn verifies the ipns record signature ipnsKey := ipns.RecordKey(pid) - vals, err := r.searchValue(ctx, ipnsKey, dht.Quorum(int(options.DhtRecordCount))) + vals, err := r.routing.SearchValue(ctx, ipnsKey, dht.Quorum(int(options.DhtRecordCount))) if err != nil { log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err) out <- onceResult{err: err} @@ -173,14 +172,3 @@ func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, option return out } - -func (r *IpnsResolver) searchValue(ctx context.Context, key string, opts ...ropts.Option) (<-chan []byte, error) { - if ir, ok := r.routing.(*dht.IpfsDHT); ok { - return ir.SearchValue(ctx, key, opts...) - } - out := make(chan []byte, 1) - val, err := r.routing.GetValue(ctx, key, opts...) - out <- val - close(out) - return out, err -} From c7ea08749d5f72a135c0dadced5e10bf7d25798d Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Sat, 22 Sep 2018 17:36:14 -0700 Subject: [PATCH 2649/3817] Fix inability to pin two things at once License: MIT Signed-off-by: Michael Avila This commit was moved from ipfs/go-ipfs-pinner@b252b7c8ed8bcbcbf4ff9da591a9b321d00218f5 --- pinning/pinner/pin.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index cadc4530a..233e99623 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -228,16 +228,20 @@ func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { if p.directPin.Has(c) { p.directPin.Remove(c) } - + p.lock.Unlock() // fetch entire graph err := mdag.FetchGraph(ctx, c, p.dserv) + p.lock.Lock() if err != nil { return err } p.recursePin.Add(c) } else { - if _, err := p.dserv.Get(ctx, c); err != nil { + p.lock.Unlock() + _, err := p.dserv.Get(ctx, c) + p.lock.Lock() + if err != nil { return err } From 4a2dcd53eb1ce42345c8fe39a2ecc9ccfe736a91 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Sun, 23 Sep 2018 09:29:00 -0700 Subject: [PATCH 2650/3817] Repeat recurse/direct pin checks since they could have changed License: MIT Signed-off-by: Michael Avila This commit was moved from ipfs/go-ipfs-pinner@56bb470b00e4ef486874c45fc939f2363bcec110 --- pinning/pinner/pin.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 233e99623..f6100749f 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -236,6 +236,14 @@ func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { return err } + if p.recursePin.Has(c) { + return nil + } + + if p.directPin.Has(c) { + p.directPin.Remove(c) + } + p.recursePin.Add(c) } else { p.lock.Unlock() From 9576321a91091b921b67af6ce21dcfa0130b2ca4 Mon Sep 17 00:00:00 2001 From: Kejie Zhang Date: Sat, 29 Sep 2018 13:48:30 +0800 Subject: [PATCH 2651/3817] add check chunker size This commit was moved from ipfs/go-ipfs-chunker@449ca4407c2086378566610ff4e81d9c3d297fe3 --- chunker/parse.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/chunker/parse.go b/chunker/parse.go index d7764d7b4..af0a31e80 100644 --- a/chunker/parse.go +++ b/chunker/parse.go @@ -8,7 +8,10 @@ import ( "strings" ) -var ErrRabinMin = errors.New("rabin min must be greater than 16") +var ( + ErrRabinMin = errors.New("rabin min must be greater than 16") + ErrSize = errors.New("chunker size muster greater than 0") +) // FromString returns a Splitter depending on the given string: // it supports "default" (""), "size-{size}", "rabin", "rabin-{blocksize}" and @@ -23,6 +26,8 @@ func FromString(r io.Reader, chunker string) (Splitter, error) { size, err := strconv.Atoi(sizeStr) if err != nil { return nil, err + } else if size <= 0 { + return nil, ErrSize } return NewSizeSplitter(r, int64(size)), nil From 113a55fc341560ded8620c02c7f634c2a44bc29c Mon Sep 17 00:00:00 2001 From: Kejie Zhang Date: Sat, 29 Sep 2018 13:53:07 +0800 Subject: [PATCH 2652/3817] update parse test This commit was moved from ipfs/go-ipfs-chunker@4a54cc588f3c7ce3ec0e5bc4c78e9f5d5a8618b2 --- chunker/parse_test.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/chunker/parse_test.go b/chunker/parse_test.go index 4dd8afc2d..f82aba5f2 100644 --- a/chunker/parse_test.go +++ b/chunker/parse_test.go @@ -5,7 +5,7 @@ import ( "testing" ) -func TestParse(t *testing.T) { +func TestParseRabin(t *testing.T) { max := 1000 r := bytes.NewReader(randBuf(t, max)) chk1 := "rabin-18-25-32" @@ -19,3 +19,18 @@ func TestParse(t *testing.T) { t.Log("it should be ErrRabinMin here.") } } + +func TestParseSize(t *testing.T) { + max := 1000 + r := bytes.NewReader(randBuf(t, max)) + size1 := "size-0" + size2 := "size-32" + _, err := FromString(r, size1) + if err == ErrSize { + t.Log("it should be ErrSize here.") + } + _, err = FromString(r, size2) + if err == ErrSize { + t.Fatal(err) + } +} From 9df903d67183fbb9c9ae66de11a4c62f92f7c1ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 2 Oct 2018 12:31:50 +0200 Subject: [PATCH 2653/3817] coreapi swarm: missing docs, review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@162cac0144b96596576bc9ec7389afaa8eb56135 --- coreiface/swarm.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/coreiface/swarm.go b/coreiface/swarm.go index caa6a70e3..8b464e5c1 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -29,10 +29,10 @@ type ConnectionInfo interface { Direction() net.Direction // Latency returns last known round trip time to the peer - Latency(context.Context) (time.Duration, error) + Latency() (time.Duration, error) // Streams returns list of streams established with the peer - Streams(context.Context) ([]protocol.ID, error) + Streams() ([]protocol.ID, error) } // SwarmAPI specifies the interface to libp2p swarm @@ -46,7 +46,12 @@ type SwarmAPI interface { // Peers returns the list of peers we are connected to Peers(context.Context) ([]ConnectionInfo, error) + // KnownAddrs returns the list of all addresses this node is aware of KnownAddrs(context.Context) (map[peer.ID][]ma.Multiaddr, error) + + // LocalAddrs returns the list of announced listening addresses LocalAddrs(context.Context) ([]ma.Multiaddr, error) + + // ListenAddrs returns the list of all listening addresses ListenAddrs(context.Context) ([]ma.Multiaddr, error) } From b7e28a936c5c9edee31e262c366c30fde7d872ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 19 Sep 2018 23:40:45 +0200 Subject: [PATCH 2654/3817] Cleanup instances of manual resolver construction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@47db102ccd13e62f40e7372b56b33dcf33e63c60 --- coreiface/util.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/coreiface/util.go b/coreiface/util.go index 8fd3e058f..6d58bf40d 100644 --- a/coreiface/util.go +++ b/coreiface/util.go @@ -1,10 +1,20 @@ package iface import ( + "context" "io" ) type Reader interface { - io.ReadSeeker + ReadSeekCloser + Size() uint64 + CtxReadFull(context.Context, []byte) (int, error) +} + +// A ReadSeekCloser implements interfaces to read, copy, seek and close. +type ReadSeekCloser interface { + io.Reader + io.Seeker io.Closer + io.WriterTo } From 60c358b08ca8bbb2773d19533ddd9b9864f3d360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 Sep 2018 15:00:51 +0200 Subject: [PATCH 2655/3817] coreapi unixfs: use fileAdder directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@78136afef00445d43c96ec7c66249b881dcf95ff --- coreiface/options/unixfs.go | 50 +++++++++++++++++++++++++++++++++++++ coreiface/unixfs.go | 4 ++- 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 coreiface/options/unixfs.go diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go new file mode 100644 index 000000000..8dc9806a7 --- /dev/null +++ b/coreiface/options/unixfs.go @@ -0,0 +1,50 @@ +package options + +import ( + mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" +) + +type UnixfsAddSettings struct { + CidVersion int + MhType uint64 + + InlineLimit int +} + +type UnixfsAddOption func(*UnixfsAddSettings) error + +func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, error) { + options := &UnixfsAddSettings{ + CidVersion: -1, + MhType: mh.SHA2_256, + + InlineLimit: 0, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +type unixfsOpts struct{} + +var Unixfs unixfsOpts + +func (unixfsOpts) CidVersion(version int) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.CidVersion = version + return nil + } +} + +func (unixfsOpts) Hash(mhtype uint64) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.MhType = mhtype + return nil + } +} diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 4a3aff6fc..10febd9fa 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -4,13 +4,15 @@ import ( "context" "io" + options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) // UnixfsAPI is the basic interface to immutable files in IPFS type UnixfsAPI interface { // Add imports the data from the reader into merkledag file - Add(context.Context, io.Reader) (ResolvedPath, error) + Add(context.Context, io.ReadCloser, ...options.UnixfsAddOption) (ResolvedPath, error) // Cat returns a reader for the file Cat(context.Context, Path) (Reader, error) From 0df36df660ece10808c7af055b59cd7052aaa7cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 Sep 2018 15:59:53 +0200 Subject: [PATCH 2656/3817] coreapi unixfs: cid prefix options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@6c7f760b5d8ae479518992b322a7bd1ccc406247 --- coreiface/options/unixfs.go | 8 ++++++-- coreiface/unixfs.go | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 8dc9806a7..ffed75577 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -8,7 +8,9 @@ type UnixfsAddSettings struct { CidVersion int MhType uint64 - InlineLimit int + InlineLimit int + RawLeaves bool + RawLeavesSet bool } type UnixfsAddOption func(*UnixfsAddSettings) error @@ -18,7 +20,9 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, error) { CidVersion: -1, MhType: mh.SHA2_256, - InlineLimit: 0, + InlineLimit: 0, + RawLeaves: false, + RawLeavesSet: false, } for _, opt := range opts { diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 10febd9fa..acc3b960c 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -10,6 +10,7 @@ import ( ) // UnixfsAPI is the basic interface to immutable files in IPFS +// NOTE: This API is heavily WIP, things are guaranteed to break frequently type UnixfsAPI interface { // Add imports the data from the reader into merkledag file Add(context.Context, io.ReadCloser, ...options.UnixfsAddOption) (ResolvedPath, error) From 6a3bc40fc4c131b19837c1007ba3e65a51de6ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 Sep 2018 16:19:32 +0200 Subject: [PATCH 2657/3817] coreapi unixfs: options for RawLeaves / Inline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@dbdf6fd63ad66e3b5e5649a349ee9ba9534590a0 --- coreiface/options/unixfs.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index ffed75577..3c46ed086 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -52,3 +52,18 @@ func (unixfsOpts) Hash(mhtype uint64) UnixfsAddOption { return nil } } + +func (unixfsOpts) RawLeaves(enable bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.RawLeaves = enable + settings.RawLeavesSet = true + return nil + } +} + +func (unixfsOpts) InlineLimit(limit int) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.InlineLimit = limit + return nil + } +} From f858a5213f527b1ff67c45692c9e481301678398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 Sep 2018 16:40:31 +0200 Subject: [PATCH 2658/3817] coreapi unixfs: layout/chunker options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@ee22ac438536720b2646e6071ecc6519f4161a0c --- coreiface/options/unixfs.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 3c46ed086..fe41af9a8 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -4,6 +4,13 @@ import ( mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ) +type Layout int + +const ( + BalancedLayout Layout = iota + TrickleLeyout +) + type UnixfsAddSettings struct { CidVersion int MhType uint64 @@ -11,6 +18,9 @@ type UnixfsAddSettings struct { InlineLimit int RawLeaves bool RawLeavesSet bool + + Chunker string + Layout Layout } type UnixfsAddOption func(*UnixfsAddSettings) error @@ -23,6 +33,9 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, error) { InlineLimit: 0, RawLeaves: false, RawLeavesSet: false, + + Chunker: "size-262144", + Layout: BalancedLayout, } for _, opt := range opts { @@ -67,3 +80,17 @@ func (unixfsOpts) InlineLimit(limit int) UnixfsAddOption { return nil } } + +func (unixfsOpts) Chunker(chunker string) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Chunker = chunker + return nil + } +} + +func (unixfsOpts) Layout(layout Layout) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Layout = layout + return nil + } +} From 27ded0dea2d8b2ee4c115c9cd40c2d1514927162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 Sep 2018 23:05:22 +0200 Subject: [PATCH 2659/3817] coreapi unixfs: pin/local/hash-only options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@8521907e1a8ac009252669ad3e40be2d93438ad3 --- coreiface/options/unixfs.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index fe41af9a8..6012ce77b 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -21,6 +21,10 @@ type UnixfsAddSettings struct { Chunker string Layout Layout + + Pin bool + OnlyHash bool + Local bool } type UnixfsAddOption func(*UnixfsAddSettings) error @@ -36,6 +40,10 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, error) { Chunker: "size-262144", Layout: BalancedLayout, + + Pin: false, + OnlyHash: false, + Local: false, } for _, opt := range opts { @@ -94,3 +102,24 @@ func (unixfsOpts) Layout(layout Layout) UnixfsAddOption { return nil } } + +func (unixfsOpts) Pin(pin bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Pin = pin + return nil + } +} + +func (unixfsOpts) HashOnly(hashOnly bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.OnlyHash = hashOnly + return nil + } +} + +func (unixfsOpts) Local(local bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Local = local + return nil + } +} From c3289a5e7d437768d5f1ea158a9e093034097c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 Sep 2018 23:15:07 +0200 Subject: [PATCH 2660/3817] coreapi unixfs: cleanup options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@e62f26507f22f00426172384461c53c7d14b702f --- coreiface/options/unixfs.go | 41 ++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 6012ce77b..6abfd9622 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -1,7 +1,12 @@ package options import ( + "errors" + "fmt" + + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + dag "gx/ipfs/QmcBoNcAP6qDjgRBew7yjvCqHq7p5jMstE44jPUBWBxzsV/go-merkledag" ) type Layout int @@ -29,7 +34,7 @@ type UnixfsAddSettings struct { type UnixfsAddOption func(*UnixfsAddSettings) error -func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, error) { +func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, error) { options := &UnixfsAddSettings{ CidVersion: -1, MhType: mh.SHA2_256, @@ -49,11 +54,41 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, error) { for _, opt := range opts { err := opt(options) if err != nil { - return nil, err + return nil, cid.Prefix{}, err + } + } + + // (hash != "sha2-256") -> CIDv1 + if options.MhType != mh.SHA2_256 { + switch options.CidVersion { + case 0: + return nil, cid.Prefix{}, errors.New("CIDv0 only supports sha2-256") + case 1, -1: + options.CidVersion = 1 + default: + return nil, cid.Prefix{}, fmt.Errorf("unknown CID version: %d", options.CidVersion) + } + } else { + if options.CidVersion < 0 { + // Default to CIDv0 + options.CidVersion = 0 } } - return options, nil + // cidV1 -> raw blocks (by default) + if options.CidVersion > 0 && !options.RawLeavesSet { + options.RawLeaves = true + } + + prefix, err := dag.PrefixForCidVersion(options.CidVersion) + if err != nil { + return nil, cid.Prefix{}, err + } + + prefix.MhType = options.MhType + prefix.MhLength = -1 + + return options, prefix, nil } type unixfsOpts struct{} From 0039c7d460983de5c06f86459a0fb00b81ed0505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 Sep 2018 23:44:49 +0200 Subject: [PATCH 2661/3817] coreapi unixfs: docs on options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@eeb50d8e478fbaff90d4ef5a434834abfff408ad --- coreiface/options/unixfs.go | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 6abfd9622..9b003e1af 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -13,7 +13,7 @@ type Layout int const ( BalancedLayout Layout = iota - TrickleLeyout + TrickleLayout ) type UnixfsAddSettings struct { @@ -95,6 +95,8 @@ type unixfsOpts struct{} var Unixfs unixfsOpts +// CidVersion specifies which CID version to use. Defaults to 0 unless an option +// that depends on CIDv1 is passed. func (unixfsOpts) CidVersion(version int) UnixfsAddOption { return func(settings *UnixfsAddSettings) error { settings.CidVersion = version @@ -102,6 +104,9 @@ func (unixfsOpts) CidVersion(version int) UnixfsAddOption { } } +// Hash function to use. Implies CIDv1 if not set to sha2-256 (default). +// +// Table of functions is declared in https://github.com/multiformats/go-multihash/blob/master/multihash.go func (unixfsOpts) Hash(mhtype uint64) UnixfsAddOption { return func(settings *UnixfsAddSettings) error { settings.MhType = mhtype @@ -109,6 +114,8 @@ func (unixfsOpts) Hash(mhtype uint64) UnixfsAddOption { } } +// RawLeaves specifies whether to use raw blocks for leaves (data nodes with no +// links) instead of wrapping them with unixfs structures. func (unixfsOpts) RawLeaves(enable bool) UnixfsAddOption { return func(settings *UnixfsAddSettings) error { settings.RawLeaves = enable @@ -117,6 +124,11 @@ func (unixfsOpts) RawLeaves(enable bool) UnixfsAddOption { } } +// InlineLimit sets the amount of bytes below which blocks will be encoded +// directly into CID instead of being stored and addressed by it's hash +// +// Note that while there is no hard limit on the number of bytes here, it should +// be kept at something reasonably low like 32b (default for 'ipfs add') func (unixfsOpts) InlineLimit(limit int) UnixfsAddOption { return func(settings *UnixfsAddSettings) error { settings.InlineLimit = limit @@ -124,6 +136,11 @@ func (unixfsOpts) InlineLimit(limit int) UnixfsAddOption { } } +// Chunker specifies settings for the chunking algorithm to use. +// +// Default: size-262144, formats: +// size-[bytes] - Simple chunker splitting data into blocks of n bytes +// rabin-[min]-[avg]-[max] - Rabin chunker func (unixfsOpts) Chunker(chunker string) UnixfsAddOption { return func(settings *UnixfsAddSettings) error { settings.Chunker = chunker @@ -131,6 +148,10 @@ func (unixfsOpts) Chunker(chunker string) UnixfsAddOption { } } +// Layout tells the adder how to balance data between leaves. +// options.BalancedLayout is the default, it's optimized for static seekable +// files. +// options.TrickleLayout is optimized for streaming data, func (unixfsOpts) Layout(layout Layout) UnixfsAddOption { return func(settings *UnixfsAddSettings) error { settings.Layout = layout @@ -138,6 +159,7 @@ func (unixfsOpts) Layout(layout Layout) UnixfsAddOption { } } +// Pin tells the adder to pin the file root recursively after adding func (unixfsOpts) Pin(pin bool) UnixfsAddOption { return func(settings *UnixfsAddSettings) error { settings.Pin = pin @@ -145,6 +167,8 @@ func (unixfsOpts) Pin(pin bool) UnixfsAddOption { } } +// HashOnly will make the adder calculate data hash without storing it in the +// blockstore or announcing it to the network func (unixfsOpts) HashOnly(hashOnly bool) UnixfsAddOption { return func(settings *UnixfsAddSettings) error { settings.OnlyHash = hashOnly @@ -152,6 +176,9 @@ func (unixfsOpts) HashOnly(hashOnly bool) UnixfsAddOption { } } +// Local will add the data to blockstore without announcing it to the network +// +// Note that this doesn't prevent other nodes from getting this data func (unixfsOpts) Local(local bool) UnixfsAddOption { return func(settings *UnixfsAddSettings) error { settings.Local = local From 80a937abb79a84f303465f05d86b206e1e90ff75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 2 Oct 2018 09:42:50 +0200 Subject: [PATCH 2662/3817] coreapi unixfs: separate option to enable inlining MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@88ca0a07599ddd51c3db3864b99c1da87bf87eee --- coreiface/options/unixfs.go | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 9b003e1af..df6f4fc71 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -20,6 +20,7 @@ type UnixfsAddSettings struct { CidVersion int MhType uint64 + Inline bool InlineLimit int RawLeaves bool RawLeavesSet bool @@ -39,7 +40,8 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, CidVersion: -1, MhType: mh.SHA2_256, - InlineLimit: 0, + Inline: false, + InlineLimit: 32, RawLeaves: false, RawLeavesSet: false, @@ -124,11 +126,26 @@ func (unixfsOpts) RawLeaves(enable bool) UnixfsAddOption { } } +// Inline tells the adder to inline small blocks into CIDs +func (unixfsOpts) Inline(enable bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Inline = enable + return nil + } +} + // InlineLimit sets the amount of bytes below which blocks will be encoded -// directly into CID instead of being stored and addressed by it's hash +// directly into CID instead of being stored and addressed by it's hash. +// Specifying this option won't enable block inlining. For that use `Inline` +// option. Default: 32 bytes +// +// Note that while there is no hard limit on the number of bytes, it should +// be kept at a reasonably low value, like 64 bytes if you intend to display +// these hashes. Larger values like 256 bytes will work fine, but may affect +// de-duplication of smaller blocks. // -// Note that while there is no hard limit on the number of bytes here, it should -// be kept at something reasonably low like 32b (default for 'ipfs add') +// Setting this value too high may cause various problems, such as render some +// blocks unfetchable func (unixfsOpts) InlineLimit(limit int) UnixfsAddOption { return func(settings *UnixfsAddSettings) error { settings.InlineLimit = limit From e50aac4cc71c5272b5a53994e958198286a585bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 3 Oct 2018 15:05:46 +0200 Subject: [PATCH 2663/3817] coreapi unixfs: multi file support in unixfs coreapi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@9a760d89b5db34fa0fb7be8c71876e2f18dd8fa2 --- coreiface/unixfs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index acc3b960c..92168503e 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -2,10 +2,10 @@ package iface import ( "context" - "io" options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + files "gx/ipfs/QmSP88ryZkHSRn1fnngAaV2Vcn63WUJzAavnRM9CVdU1Ky/go-ipfs-cmdkit/files" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) @@ -13,7 +13,7 @@ import ( // NOTE: This API is heavily WIP, things are guaranteed to break frequently type UnixfsAPI interface { // Add imports the data from the reader into merkledag file - Add(context.Context, io.ReadCloser, ...options.UnixfsAddOption) (ResolvedPath, error) + Add(context.Context, files.File, ...options.UnixfsAddOption) (ResolvedPath, error) // Cat returns a reader for the file Cat(context.Context, Path) (Reader, error) From 15a4331a185c812bab3e229758bcfc79d682ac5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 3 Oct 2018 17:21:07 +0200 Subject: [PATCH 2664/3817] coreapi unixfs: unixfs.Get MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@7b2fdc90ee0528d4c0a437d95b1cb0d10c3e8aa5 --- coreiface/unixfs.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 92168503e..69e731822 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -13,8 +13,16 @@ import ( // NOTE: This API is heavily WIP, things are guaranteed to break frequently type UnixfsAPI interface { // Add imports the data from the reader into merkledag file + // + // TODO: a long useful comment on how to use this for many different scenarios Add(context.Context, files.File, ...options.UnixfsAddOption) (ResolvedPath, error) + // Get returns a read-only handle to a file tree referenced by a path + // + // Note that some implementations of this API may apply the specified context + // to operations performed on the returned file + Get(context.Context, Path) (files.File, error) + // Cat returns a reader for the file Cat(context.Context, Path) (Reader, error) From 59fd418b398d9011c79167b82af869751acf2640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 3 Oct 2018 22:30:45 +0200 Subject: [PATCH 2665/3817] coreapi unixfs: wrap option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@b977abfc696b22775fa68736c144760113b27af4 --- coreiface/options/unixfs.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index df6f4fc71..abbea9681 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -31,6 +31,8 @@ type UnixfsAddSettings struct { Pin bool OnlyHash bool Local bool + + Wrap bool } type UnixfsAddOption func(*UnixfsAddSettings) error @@ -51,6 +53,8 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, Pin: false, OnlyHash: false, Local: false, + + Wrap: false, } for _, opt := range opts { @@ -202,3 +206,12 @@ func (unixfsOpts) Local(local bool) UnixfsAddOption { return nil } } + +// Wrap tells the adder to wrap the added file structure with an additional +// directory. +func (unixfsOpts) Wrap(wrap bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Wrap = wrap + return nil + } +} From 1019fcea4b88f3a45cf81429f81066db1918771f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 3 Oct 2018 22:49:42 +0200 Subject: [PATCH 2666/3817] coreapi unixfs: hidden opiton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@cb84af4b44e4660029e87a1c82a3d2ab35acb2cd --- coreiface/options/unixfs.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index abbea9681..7d7af5b81 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -32,7 +32,8 @@ type UnixfsAddSettings struct { OnlyHash bool Local bool - Wrap bool + Wrap bool + Hidden bool } type UnixfsAddOption func(*UnixfsAddSettings) error @@ -54,7 +55,8 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, OnlyHash: false, Local: false, - Wrap: false, + Wrap: false, + Hidden: false, } for _, opt := range opts { @@ -215,3 +217,11 @@ func (unixfsOpts) Wrap(wrap bool) UnixfsAddOption { return nil } } + +// Hidden enables adding of hidden files (files prefixed with '.') +func (unixfsOpts) Hidden(hidden bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Hidden = hidden + return nil + } +} From 907d2f239738971d246b9ff9ce5add004adad2c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 3 Oct 2018 23:17:18 +0200 Subject: [PATCH 2667/3817] coreapi unixfs: stdin-name option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@aa7a877686c76f0767fa6fb618972b25f1545bb6 --- coreiface/options/unixfs.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 7d7af5b81..90ad53e9e 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -32,8 +32,9 @@ type UnixfsAddSettings struct { OnlyHash bool Local bool - Wrap bool - Hidden bool + Wrap bool + Hidden bool + StdinName string } type UnixfsAddOption func(*UnixfsAddSettings) error @@ -55,8 +56,9 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, OnlyHash: false, Local: false, - Wrap: false, - Hidden: false, + Wrap: false, + Hidden: false, + StdinName: "", } for _, opt := range opts { @@ -225,3 +227,12 @@ func (unixfsOpts) Hidden(hidden bool) UnixfsAddOption { return nil } } + +// StdinName is the name set for files which don specify FilePath as +// os.Stdin.Name() +func (unixfsOpts) StdinName(name string) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.StdinName = name + return nil + } +} From 56ef5340a321a06604f4f98efcba4da03b6a4358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 4 Oct 2018 01:00:26 +0200 Subject: [PATCH 2668/3817] coreapi unixfs: progress events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@df1797113e1dc062a9e53af11b8ea4268c855ef9 --- coreiface/options/unixfs.go | 35 +++++++++++++++++++++++++++++++++++ coreiface/unixfs.go | 9 +++++++++ 2 files changed, 44 insertions(+) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 90ad53e9e..da99b42f6 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -35,6 +35,10 @@ type UnixfsAddSettings struct { Wrap bool Hidden bool StdinName string + + Events chan<- interface{} + Silent bool + Progress bool } type UnixfsAddOption func(*UnixfsAddSettings) error @@ -59,6 +63,10 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, Wrap: false, Hidden: false, StdinName: "", + + Events: nil, + Silent: false, + Progress: false, } for _, opt := range opts { @@ -236,3 +244,30 @@ func (unixfsOpts) StdinName(name string) UnixfsAddOption { return nil } } + +// Events specifies channel which will be used to report events about ongoing +// Add operation. +// +// Note that if this channel blocks it may slowdown the adder +func (unixfsOpts) Events(sink chan<- interface{}) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Events = sink + return nil + } +} + +// Silent reduces event output +func (unixfsOpts) Silent(silent bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Silent = silent + return nil + } +} + +// Progress tells the adder whether to enable progress events +func (unixfsOpts) Progress(enable bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Progress = enable + return nil + } +} diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 69e731822..c622e210e 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -9,6 +9,14 @@ import ( ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) +// TODO: ideas on making this more coreapi-ish without breaking the http API? +type AddEvent struct { + Name string + Hash string `json:",omitempty"` + Bytes int64 `json:",omitempty"` + Size string `json:",omitempty"` +} + // UnixfsAPI is the basic interface to immutable files in IPFS // NOTE: This API is heavily WIP, things are guaranteed to break frequently type UnixfsAPI interface { @@ -24,6 +32,7 @@ type UnixfsAPI interface { Get(context.Context, Path) (files.File, error) // Cat returns a reader for the file + // TODO: Remove in favour of Get (if we use Get on a file we still have reader directly, so..) Cat(context.Context, Path) (Reader, error) // Ls returns the list of links in a directory From 90e8604702cde52200882080a6a8b5b3e0818d13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 4 Oct 2018 01:24:57 +0200 Subject: [PATCH 2669/3817] coreapi unixfs: filestore opts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@97fb3e4d819ee3ee511a719da6d1bef4695a2dad --- coreiface/options/unixfs.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index da99b42f6..810e3c6e8 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -31,6 +31,8 @@ type UnixfsAddSettings struct { Pin bool OnlyHash bool Local bool + FsCache bool + NoCopy bool Wrap bool Hidden bool @@ -59,6 +61,8 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, Pin: false, OnlyHash: false, Local: false, + FsCache: false, + NoCopy: false, Wrap: false, Hidden: false, @@ -76,6 +80,17 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, } } + // nocopy -> rawblocks + if options.NoCopy && !options.RawLeaves { + // fixed? + if options.RawLeavesSet { + return nil, cid.Prefix{}, fmt.Errorf("nocopy option requires '--raw-leaves' to be enabled as well") + } + + // No, satisfy mandatory constraint. + options.RawLeaves = true + } + // (hash != "sha2-256") -> CIDv1 if options.MhType != mh.SHA2_256 { switch options.CidVersion { @@ -271,3 +286,23 @@ func (unixfsOpts) Progress(enable bool) UnixfsAddOption { return nil } } + +// FsCache tells the adder to check the filestore for pre-existing blocks +// +// Experimental +func (unixfsOpts) FsCache(enable bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.FsCache = enable + return nil + } +} + +// NoCopy tells the adder to add the files using filestore. Implies RawLeaves. +// +// Experimental +func (unixfsOpts) Nocopy(enable bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.NoCopy = enable + return nil + } +} From e69f95f43aaaf9dca795d0ada51a76a6158c78e4 Mon Sep 17 00:00:00 2001 From: Overbool Date: Tue, 25 Sep 2018 16:35:08 +0800 Subject: [PATCH 2670/3817] feat(inode): add inode struct This commit was moved from ipfs/go-mfs@90acd8823e6b4f093c1fbed00eb61dd28cac61df --- mfs/dir.go | 25 ++++++++++--------------- mfs/fd.go | 2 +- mfs/file.go | 13 ++++--------- mfs/inode.go | 30 ++++++++++++++++++++++++++++++ mfs/mfs_test.go | 2 +- mfs/system.go | 2 +- 6 files changed, 47 insertions(+), 27 deletions(-) create mode 100644 mfs/inode.go diff --git a/mfs/dir.go b/mfs/dir.go index f26b13601..2532b861b 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -22,8 +22,7 @@ var ErrInvalidChild = errors.New("invalid child node") var ErrDirExists = errors.New("directory already has entry by that name") type Directory struct { - dserv ipld.DAGService - parent childCloser + *inode childDirs map[string]*Directory files map[string]*File @@ -36,8 +35,6 @@ type Directory struct { unixfsDir uio.Directory modTime time.Time - - name string } // NewDirectory constructs a new MFS directory. @@ -51,11 +48,9 @@ func NewDirectory(ctx context.Context, name string, node ipld.Node, parent child } return &Directory{ - dserv: dserv, + inode: NewInode(name, parent, dserv), ctx: ctx, - name: name, unixfsDir: db, - parent: parent, childDirs: make(map[string]*Directory), files: make(map[string]*File), modTime: time.Now(), @@ -108,7 +103,7 @@ func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) { return nil, err } - err = d.dserv.Add(d.ctx, nd) + err = d.dagService.Add(d.ctx, nd) if err != nil { return nil, err } @@ -158,7 +153,7 @@ func (d *Directory) cacheNode(name string, nd ipld.Node) (FSNode, error) { switch fsn.Type() { case ft.TDirectory, ft.THAMTShard: - ndir, err := NewDirectory(d.ctx, name, nd, d, d.dserv) + ndir, err := NewDirectory(d.ctx, name, nd, d, d.dagService) if err != nil { return nil, err } @@ -166,7 +161,7 @@ func (d *Directory) cacheNode(name string, nd ipld.Node) (FSNode, error) { d.childDirs[name] = ndir return ndir, nil case ft.TFile, ft.TRaw, ft.TSymlink: - nfi, err := NewFile(name, nd, d, d.dserv) + nfi, err := NewFile(name, nd, d, d.dagService) if err != nil { return nil, err } @@ -178,7 +173,7 @@ func (d *Directory) cacheNode(name string, nd ipld.Node) (FSNode, error) { return nil, ErrInvalidChild } case *dag.RawNode: - nfi, err := NewFile(name, nd, d, d.dserv) + nfi, err := NewFile(name, nd, d, d.dagService) if err != nil { return nil, err } @@ -308,7 +303,7 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { ndir := ft.EmptyDirNode() ndir.SetCidBuilder(d.GetCidBuilder()) - err = d.dserv.Add(d.ctx, ndir) + err = d.dagService.Add(d.ctx, ndir) if err != nil { return nil, err } @@ -318,7 +313,7 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { return nil, err } - dirobj, err := NewDirectory(d.ctx, name, ndir, d, d.dserv) + dirobj, err := NewDirectory(d.ctx, name, ndir, d, d.dagService) if err != nil { return nil, err } @@ -356,7 +351,7 @@ func (d *Directory) AddChild(name string, nd ipld.Node) error { return ErrDirExists } - err = d.dserv.Add(d.ctx, nd) + err = d.dagService.Add(d.ctx, nd) if err != nil { return err } @@ -452,7 +447,7 @@ func (d *Directory) GetNode() (ipld.Node, error) { return nil, err } - err = d.dserv.Add(d.ctx, nd) + err = d.dagService.Add(d.ctx, nd) if err != nil { return nil, err } diff --git a/mfs/fd.go b/mfs/fd.go index 0f0d3d426..fd4351b1a 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -122,7 +122,7 @@ func (fi *fileDescriptor) flushUp(fullsync bool) error { return err } - err = fi.inode.dserv.Add(context.TODO(), nd) + err = fi.inode.dagService.Add(context.TODO(), nd) if err != nil { return err } diff --git a/mfs/file.go b/mfs/file.go index 0a49646fd..86e00713b 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -14,13 +14,10 @@ import ( ) type File struct { - parent childCloser - - name string + *inode desclock sync.RWMutex - dserv ipld.DAGService node ipld.Node nodelk sync.Mutex @@ -31,10 +28,8 @@ type File struct { // Cid version is non-zero RawLeaves will be enabled. func NewFile(name string, node ipld.Node, parent childCloser, dserv ipld.DAGService) (*File, error) { fi := &File{ - dserv: dserv, - parent: parent, - name: name, - node: node, + inode: NewInode(name, parent, dserv), + node: node, } if node.Cid().Prefix().Version > 0 { fi.RawLeaves = true @@ -82,7 +77,7 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { return nil, fmt.Errorf("mode not supported") } - dmod, err := mod.NewDagModifier(context.TODO(), node, fi.dserv, chunker.DefaultSplitter) + dmod, err := mod.NewDagModifier(context.TODO(), node, fi.dagService, chunker.DefaultSplitter) if err != nil { return nil, err } diff --git a/mfs/inode.go b/mfs/inode.go new file mode 100644 index 000000000..54f064c7b --- /dev/null +++ b/mfs/inode.go @@ -0,0 +1,30 @@ +package mfs + +import ( + ipld "github.com/ipfs/go-ipld-format" +) + +// inode abstracts the common characteristics of the MFS `File` +// and `Directory`. All of its attributes are initialized at +// creation. +type inode struct { + // name of this `inode` in the MFS path (the same value + // is also stored as the name of the DAG link). + name string + + // parent directory of this `inode` (which may be the `Root`). + parent childCloser + + // dagService used to store modifications made to the contents + // of the file or directory the `inode` belongs to. + dagService ipld.DAGService +} + +// NewInode creates a new `inode` structure and return it's pointer. +func NewInode(name string, parent childCloser, dagService ipld.DAGService) *inode { + return &inode{ + name: name, + parent: parent, + dagService: dagService, + } +} diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index e840f6c06..456d00505 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -555,7 +555,7 @@ func actorMakeFile(d *Directory) error { } name := randomName() - f, err := NewFile(name, dag.NodeWithData(ft.FilePBData(nil, 0)), d, d.dserv) + f, err := NewFile(name, dag.NodeWithData(ft.FilePBData(nil, 0)), d, d.dagService) if err != nil { return err } diff --git a/mfs/system.go b/mfs/system.go index cc7e65d3a..bc0bafa5d 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -145,7 +145,7 @@ func (kr *Root) FlushMemFree(ctx context.Context) error { // closeChild implements the childCloser interface, and signals to the publisher that // there are changes ready to be published. func (kr *Root) closeChild(name string, nd ipld.Node, sync bool) error { - err := kr.GetDirectory().dserv.Add(context.TODO(), nd) + err := kr.GetDirectory().dagService.Add(context.TODO(), nd) if err != nil { return err } From 8020a00a75371a2ea3919d53d30b4c4e27f14898 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 4 Oct 2018 10:03:38 -0700 Subject: [PATCH 2671/3817] make arccache.GetSize return ErrNotFound when not found (and fix the tests) The semantics were changed during PR review but the test never got updated. This commit was moved from ipfs/go-ipfs-blockstore@79b1edf413189c02d08a3692a1f5ceecb5aad42c --- blockstore/arc_cache.go | 7 +++++-- blockstore/arc_cache_test.go | 5 ++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 78e313512..231fd8555 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -88,8 +88,11 @@ func (b *arccache) Has(k cid.Cid) (bool, error) { } func (b *arccache) GetSize(k cid.Cid) (int, error) { - if _, blockSize, ok := b.hasCached(k); ok { - return blockSize, nil + if has, blockSize, ok := b.hasCached(k); ok { + if has { + return blockSize, nil + } + return -1, ErrNotFound } blockSize, err := b.blockstore.GetSize(k) if err == ErrNotFound { diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index facbf3473..6911db769 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -107,7 +107,7 @@ func TestGetFillsCache(t *testing.T) { if has, err := arc.Has(exampleBlock.Cid()); has || err != nil { t.Fatal("has was true but there is no such block") } - if blockSize, err := arc.GetSize(exampleBlock.Cid()); blockSize > -1 || err != nil { + if _, err := arc.GetSize(exampleBlock.Cid()); err != ErrNotFound { t.Fatal("getsize was true but there is no such block") } @@ -203,12 +203,11 @@ func TestGetSizeMissingZeroSizeBlock(t *testing.T) { arc.Get(missingBlock.Cid()) trap("has hit datastore", cd, t) - if blockSize, err := arc.GetSize(missingBlock.Cid()); blockSize != -1 || err != nil { + if _, err := arc.GetSize(missingBlock.Cid()); err != ErrNotFound { t.Fatal("getsize returned invalid result") } } - func TestDifferentKeyObjectsWork(t *testing.T) { arc, bs, cd := createStores(t) From f46082072957d0f4634a70baec4f0ef573c89408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 4 Oct 2018 20:39:43 +0200 Subject: [PATCH 2672/3817] coreapi unixfs: fix inline doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@641542e1596817f70eeac14f87b3c49ef2d5e80a --- coreiface/options/unixfs.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 810e3c6e8..9249af895 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -170,13 +170,10 @@ func (unixfsOpts) Inline(enable bool) UnixfsAddOption { // Specifying this option won't enable block inlining. For that use `Inline` // option. Default: 32 bytes // -// Note that while there is no hard limit on the number of bytes, it should -// be kept at a reasonably low value, like 64 bytes if you intend to display -// these hashes. Larger values like 256 bytes will work fine, but may affect -// de-duplication of smaller blocks. -// -// Setting this value too high may cause various problems, such as render some -// blocks unfetchable +// Note that while there is no hard limit on the number of bytes, it should be +// kept at a reasonably low value, such as 64 and no more than 1k. Setting this +// value too high may cause various problems, such as render some +// blocks unfetchable. func (unixfsOpts) InlineLimit(limit int) UnixfsAddOption { return func(settings *UnixfsAddSettings) error { settings.InlineLimit = limit From 34497dfcf9b4083f51925ee9e7b9ccd14c8f69ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 4 Oct 2018 22:11:17 +0200 Subject: [PATCH 2673/3817] coreapi name: add some missing options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@0ad4722d65f9b8a3021320835291d2161091fd63 --- coreiface/options/name.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/coreiface/options/name.go b/coreiface/options/name.go index ba3691b03..c614db3ab 100644 --- a/coreiface/options/name.go +++ b/coreiface/options/name.go @@ -13,6 +13,10 @@ const ( type NamePublishSettings struct { ValidTime time.Duration Key string + + TTL *time.Duration + + AllowOffline bool } type NameResolveSettings struct { @@ -29,6 +33,8 @@ func NamePublishOptions(opts ...NamePublishOption) (*NamePublishSettings, error) options := &NamePublishSettings{ ValidTime: DefaultNameValidTime, Key: "self", + + AllowOffline: false, } for _, opt := range opts { @@ -82,6 +88,24 @@ func (nameOpts) Key(key string) NamePublishOption { } } +// AllowOffline is an option for Name.Publish which specifies whether to allow +// publishing when the node is offline. Default value is false +func (nameOpts) AllowOffline(allow bool) NamePublishOption { + return func(settings *NamePublishSettings) error { + settings.AllowOffline = allow + return nil + } +} + +// TTL is an option for Name.Publish which specifies the time duration the +// published record should be cached for (caution: experimental). +func (nameOpts) TTL(ttl time.Duration) NamePublishOption { + return func(settings *NamePublishSettings) error { + settings.TTL = &ttl + return nil + } +} + // Local is an option for Name.Resolve which specifies if the lookup should be // offline. Default value is false func (nameOpts) Local(local bool) NameResolveOption { From 37b8aefc423bb9aa2f21007f76bb9749dfdaec46 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 4 Oct 2018 19:08:29 -0400 Subject: [PATCH 2674/3817] gx publish 1.1.4 This commit was moved from ipfs/go-unixfs@5d8e6747b5c4d81de36dd5939963f0912997a3ae --- unixfs/hamt/hamt.go | 4 ++-- unixfs/io/dagreader.go | 2 +- unixfs/io/pbdagreader.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 323929997..0b49e9696 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -25,11 +25,11 @@ import ( "fmt" "os" - dag "github.com/ipfs/go-merkledag" - format "github.com/ipfs/go-unixfs" bitfield "github.com/Stebalien/go-bitfield" cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" + dag "github.com/ipfs/go-merkledag" + format "github.com/ipfs/go-unixfs" "github.com/spaolacci/murmur3" ) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 8d491db1b..cf980d2be 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -5,9 +5,9 @@ import ( "errors" "io" + ipld "github.com/ipfs/go-ipld-format" mdag "github.com/ipfs/go-merkledag" ft "github.com/ipfs/go-unixfs" - ipld "github.com/ipfs/go-ipld-format" ) // Common errors diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go index a84f239ff..bea5f496e 100644 --- a/unixfs/io/pbdagreader.go +++ b/unixfs/io/pbdagreader.go @@ -6,10 +6,10 @@ import ( "fmt" "io" - mdag "github.com/ipfs/go-merkledag" - ft "github.com/ipfs/go-unixfs" cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" + mdag "github.com/ipfs/go-merkledag" + ft "github.com/ipfs/go-unixfs" ) // PBDagReader provides a way to easily read the data contained in a dag. From 35d120747487987f474b02cba3b7232729263947 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Fri, 21 Sep 2018 13:11:54 -0700 Subject: [PATCH 2675/3817] Use EnumerateChildrenAsync in EnumLinks - makeAsyncTrieGetLinks -- returns a function that can be used as a GetLinks function with EnumerateChildrenAsyc - Mutex on appending links in EnumLinks This commit was moved from ipfs/go-unixfs@edbb7a341212cae92ec93ae0e8dac0e2b7b7aef6 --- unixfs/hamt/hamt.go | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 0b49e9696..8b240b876 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -24,6 +24,7 @@ import ( "context" "fmt" "os" + "sync" bitfield "github.com/Stebalien/go-bitfield" cid "github.com/ipfs/go-cid" @@ -383,10 +384,16 @@ func (ds *Shard) getValue(ctx context.Context, hv *hashBits, key string, cb func // EnumLinks collects all links in the Shard. func (ds *Shard) EnumLinks(ctx context.Context) ([]*ipld.Link, error) { var links []*ipld.Link - err := ds.ForEachLink(ctx, func(l *ipld.Link) error { + var setlk sync.Mutex + + getLinks := ds.makeAsyncTrieGetLinks(func(l *ipld.Link) error { + setlk.Lock() links = append(links, l) + setlk.Unlock() return nil }) + + err := dag.EnumerateChildrenAsync(ctx, getLinks, ds.nd.Cid(), func(c cid.Cid) bool { return true }) return links, err } @@ -400,6 +407,35 @@ func (ds *Shard) ForEachLink(ctx context.Context, f func(*ipld.Link) error) erro }) } +func (ds *Shard) makeAsyncTrieGetLinks(cb func(*ipld.Link) error) dag.GetLinks { + + return func(ctx context.Context, c cid.Cid) ([]*ipld.Link, error) { + node, err := ds.dserv.Get(ctx, c) + if err != nil { + return nil, err + } + cds, err := NewHamtFromDag(ds.dserv, node) + if err != nil { + return nil, err + } + + childShards := make([]*ipld.Link, 0, len(cds.children)) + for idx := range cds.children { + lnk := cds.nd.Links()[idx] + + if len(lnk.Name) < cds.maxpadlen { + return nil, fmt.Errorf("invalid link name '%s'", lnk.Name) + } + if len(lnk.Name) == cds.maxpadlen { + childShards = append(childShards, lnk) + } else { + cb(lnk) + } + } + return childShards, nil + } +} + func (ds *Shard) walkTrie(ctx context.Context, cb func(*shardValue) error) error { for idx := range ds.children { c, err := ds.getChild(ctx, idx) From 9a2296f4956e469b22316433fe2ee20d95c3a917 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Thu, 27 Sep 2018 11:30:21 -0700 Subject: [PATCH 2676/3817] Fix tests to pass Due to new architecture of EnumLinks, remove order concerns and prevent crashes on calls to fetch CID This commit was moved from ipfs/go-unixfs@ed9d23b6bd5fb9964aab066e0b3e55e7b5f0bad5 --- unixfs/hamt/hamt_test.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index e56d9363c..ffbb676eb 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -11,6 +11,7 @@ import ( dag "github.com/ipfs/go-merkledag" mdtest "github.com/ipfs/go-merkledag/test" + ft "github.com/ipfs/go-unixfs" ipld "github.com/ipfs/go-ipld-format" @@ -100,6 +101,8 @@ func assertSerializationWorks(ds ipld.DAGService, s *Shard) error { return fmt.Errorf("links arrays are different sizes") } + sort.Stable(dag.LinkSlice(linksA)) + sort.Stable(dag.LinkSlice(linksB)) for i, a := range linksA { b := linksB[i] if a.Name != b.Name { @@ -280,14 +283,17 @@ func TestSetAfterMarshal(t *testing.T) { t.Fatal(err) } - empty := ft.EmptyDirNode() for i := 0; i < 100; i++ { + empty := ft.EmptyDirNode() err := nds.Set(ctx, fmt.Sprintf("moredirs%d", i), empty) if err != nil { t.Fatal(err) } } + nd, err = nds.Node() + nds, err = NewHamtFromDag(ds, nd) + links, err := nds.EnumLinks(ctx) if err != nil { t.Fatal(err) @@ -319,6 +325,9 @@ func TestDuplicateAddShard(t *testing.T) { t.Fatal(err) } + node, err := dir.Node() + dir, err = NewHamtFromDag(ds, node) + lnks, err := dir.EnumLinks(ctx) if err != nil { t.Fatal(err) @@ -411,6 +420,9 @@ func TestRemoveElemsAfterMarshal(t *testing.T) { } } + nd, err = nds.Node() + nds, err = NewHamtFromDag(ds, nd) + links, err := nds.EnumLinks(ctx) if err != nil { t.Fatal(err) From 994ad13f2c12105af0f5f50f61a2d393fa87c637 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Mon, 1 Oct 2018 16:31:35 -0700 Subject: [PATCH 2677/3817] Add CID set tracking to shard enumeration This commit was moved from ipfs/go-unixfs@ddf48b0c75e58de4043e3d84910255f3c1898e9c --- unixfs/hamt/hamt.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 8b240b876..f1a6729db 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -393,7 +393,9 @@ func (ds *Shard) EnumLinks(ctx context.Context) ([]*ipld.Link, error) { return nil }) - err := dag.EnumerateChildrenAsync(ctx, getLinks, ds.nd.Cid(), func(c cid.Cid) bool { return true }) + cset := cid.NewSet() + + err := dag.EnumerateChildrenAsync(ctx, getLinks, ds.nd.Cid(), cset.Visit) return links, err } From 12154d9d59ae924b8fd6d09878424f03bea940b9 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Tue, 2 Oct 2018 16:04:23 -0700 Subject: [PATCH 2678/3817] Refactor for readability, consistency This commit was moved from ipfs/go-unixfs@3844b681096b78dfa24e44741199bc51c7891615 --- unixfs/hamt/hamt.go | 73 +++++++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 22 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index f1a6729db..f68b802b4 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -105,7 +105,6 @@ func NewHamtFromDag(dserv ipld.DAGService, nd ipld.Node) (*Shard, error) { return nil, err } - if fsn.Type() != format.THAMTShard { return nil, fmt.Errorf("node was not a dir shard") } @@ -203,6 +202,14 @@ func (sv *shardValue) Label() string { return sv.key } +func (ds *Shard) makeShardValue(lnk *ipld.Link) *shardValue { + lnk2 := *lnk + return &shardValue{ + key: lnk.Name[ds.maxpadlen:], + val: &lnk2, + } +} + func hash(val []byte) []byte { h := murmur3.New64() h.Write(val) @@ -254,6 +261,24 @@ func (ds *Shard) Find(ctx context.Context, name string) (*ipld.Link, error) { return out, nil } +type linkType int + +const ( + invalidLink linkType = iota + shardLink + shardValueLink +) + +func (ds *Shard) childLinkType(lnk *ipld.Link) (linkType, error) { + if len(lnk.Name) < ds.maxpadlen { + return invalidLink, fmt.Errorf("invalid link name '%s'", lnk.Name) + } + if len(lnk.Name) == ds.maxpadlen { + return shardLink, nil + } + return shardValueLink, nil +} + // getChild returns the i'th child of this shard. If it is cached in the // children array, it will return it from there. Otherwise, it loads the child // node from disk. @@ -278,12 +303,13 @@ func (ds *Shard) getChild(ctx context.Context, i int) (child, error) { // as a 'child' interface func (ds *Shard) loadChild(ctx context.Context, i int) (child, error) { lnk := ds.nd.Links()[i] - if len(lnk.Name) < ds.maxpadlen { - return nil, fmt.Errorf("invalid link name '%s'", lnk.Name) + lnkLinkType, err := ds.childLinkType(lnk) + if err != nil { + return nil, err } var c child - if len(lnk.Name) == ds.maxpadlen { + if lnkLinkType == shardLink { nd, err := lnk.GetNode(ctx, ds.dserv) if err != nil { return nil, err @@ -295,11 +321,7 @@ func (ds *Shard) loadChild(ctx context.Context, i int) (child, error) { c = cds } else { - lnk2 := *lnk - c = &shardValue{ - key: lnk.Name[ds.maxpadlen:], - val: &lnk2, - } + c = ds.makeShardValue(lnk) } ds.children[i] = c @@ -386,9 +408,11 @@ func (ds *Shard) EnumLinks(ctx context.Context) ([]*ipld.Link, error) { var links []*ipld.Link var setlk sync.Mutex - getLinks := ds.makeAsyncTrieGetLinks(func(l *ipld.Link) error { + getLinks := makeAsyncTrieGetLinks(ds.dserv, func(sv *shardValue) error { + lnk := sv.val + lnk.Name = sv.key setlk.Lock() - links = append(links, l) + links = append(links, lnk) setlk.Unlock() return nil }) @@ -409,29 +433,34 @@ func (ds *Shard) ForEachLink(ctx context.Context, f func(*ipld.Link) error) erro }) } -func (ds *Shard) makeAsyncTrieGetLinks(cb func(*ipld.Link) error) dag.GetLinks { +// makeAsyncTrieGetLinks builds a getLinks function that can be used with EnumerateChildrenAsync +// to iterate a HAMT shard. It takes an IPLD Dag Service to fetch nodes, and a call back that will get called +// on all links to leaf nodes in a HAMT tree, so they can be collected for an EnumLinks operation +func makeAsyncTrieGetLinks(dagService ipld.DAGService, onShardValue func(*shardValue) error) dag.GetLinks { - return func(ctx context.Context, c cid.Cid) ([]*ipld.Link, error) { - node, err := ds.dserv.Get(ctx, c) + return func(ctx context.Context, currentCid cid.Cid) ([]*ipld.Link, error) { + node, err := dagService.Get(ctx, currentCid) if err != nil { return nil, err } - cds, err := NewHamtFromDag(ds.dserv, node) + directoryShard, err := NewHamtFromDag(dagService, node) if err != nil { return nil, err } - childShards := make([]*ipld.Link, 0, len(cds.children)) - for idx := range cds.children { - lnk := cds.nd.Links()[idx] + childShards := make([]*ipld.Link, 0, len(directoryShard.children)) + for idx := range directoryShard.children { + lnk := directoryShard.nd.Links()[idx] + lnkLinkType, err := directoryShard.childLinkType(lnk) - if len(lnk.Name) < cds.maxpadlen { - return nil, fmt.Errorf("invalid link name '%s'", lnk.Name) + if err != nil { + return nil, err } - if len(lnk.Name) == cds.maxpadlen { + if lnkLinkType == shardLink { childShards = append(childShards, lnk) } else { - cb(lnk) + sv := directoryShard.makeShardValue(lnk) + onShardValue(sv) } } return childShards, nil From e35e15a1fd1e005bc2e8409320b384a79a40756c Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Wed, 3 Oct 2018 17:41:41 -0700 Subject: [PATCH 2679/3817] Minor optimizations and error checks This commit was moved from ipfs/go-unixfs@94b01349973005cf777911a9c51026a182d1f46f --- unixfs/hamt/hamt.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index f68b802b4..b7ac0a2f4 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -449,8 +449,9 @@ func makeAsyncTrieGetLinks(dagService ipld.DAGService, onShardValue func(*shardV } childShards := make([]*ipld.Link, 0, len(directoryShard.children)) + links := directoryShard.nd.Links() for idx := range directoryShard.children { - lnk := directoryShard.nd.Links()[idx] + lnk := links[idx] lnkLinkType, err := directoryShard.childLinkType(lnk) if err != nil { @@ -460,7 +461,10 @@ func makeAsyncTrieGetLinks(dagService ipld.DAGService, onShardValue func(*shardV childShards = append(childShards, lnk) } else { sv := directoryShard.makeShardValue(lnk) - onShardValue(sv) + err := onShardValue(sv) + if err != nil { + return nil, err + } } } return childShards, nil From b00a9c91219246fb5cb954b1a50578eeb009dcae Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Wed, 3 Oct 2018 07:35:57 +0200 Subject: [PATCH 2680/3817] gx: update go-datastore, go-libp2p-swarm License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/interface-go-ipfs-core@b9309ab1a51ec1b68e8c5561b0f02a610fd93f97 --- coreiface/dht.go | 2 +- coreiface/path.go | 2 +- coreiface/swarm.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/coreiface/dht.go b/coreiface/dht.go index 2309ceb90..4a76f4d39 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -5,8 +5,8 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + pstore "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" - pstore "gx/ipfs/QmfAQMFpgDU2U4BXG64qVr8HSiictfWvkSBz7Y2oDj65st/go-libp2p-peerstore" ) // DhtAPI specifies the interface to the DHT diff --git a/coreiface/path.go b/coreiface/path.go index 0beab0663..0f06e8cc2 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" + ipfspath "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) diff --git a/coreiface/swarm.go b/coreiface/swarm.go index 8b464e5c1..58caf6759 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -5,11 +5,11 @@ import ( "errors" "time" + net "gx/ipfs/QmQdLcvoy3JuSqhV6iwQ9T6Cv7hWLAdzob4jUZRPqFL67Z/go-libp2p-net" + pstore "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol" "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" - pstore "gx/ipfs/QmfAQMFpgDU2U4BXG64qVr8HSiictfWvkSBz7Y2oDj65st/go-libp2p-peerstore" - net "gx/ipfs/QmfDPh144WGBqRxZb1TGDHerbMnZATrHZggAPw7putNnBq/go-libp2p-net" ) var ( From 3e36d074884b63fca53070bfcdfbb8a8d2f6d601 Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Wed, 3 Oct 2018 07:35:57 +0200 Subject: [PATCH 2681/3817] gx: update go-datastore, go-libp2p-swarm License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/go-namesys@6ec50cb952f4d4e7f1ea669c2930ea2ea6b2a1f9 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 20 ++++++++++---------- namesys/namesys.go | 6 +++--- namesys/namesys_test.go | 14 +++++++------- namesys/proquint.go | 2 +- namesys/publisher.go | 14 +++++++------- namesys/publisher_test.go | 10 +++++----- namesys/republisher/repub.go | 6 +++--- namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 10 +++++----- namesys/routing.go | 10 +++++----- 14 files changed, 53 insertions(+), 53 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index afdc0a468..20e035650 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" + path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index f18c6e8aa..bc34266ef 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" + path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 5ca7323a9..29d90497b 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -9,7 +9,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" + path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" ) type LookupTXTFunc func(name string) (txt []string, err error) diff --git a/namesys/interface.go b/namesys/interface.go index fcc956cb1..d3a2bf980 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" + path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index cc99bf1d7..58f0fd21a 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,22 +6,22 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" + path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + routing "gx/ipfs/QmQRfifvvbJ8xTKj4KX1VvGWK26hnPiy8eQvW1hmjc82nD/go-libp2p-routing" + ropts "gx/ipfs/QmQRfifvvbJ8xTKj4KX1VvGWK26hnPiy8eQvW1hmjc82nD/go-libp2p-routing/options" + pstore "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore" + pstoremem "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore/pstoremem" record "gx/ipfs/QmSb4B8ZAAj5ALe9LjfzPyF8Ma6ezC1NTnDF2JQPUJxEXb/go-libp2p-record" - mockrouting "gx/ipfs/QmScZySgru9jaoDa12sSfvh21sWbqF5eXkieTmJzAHJXkQ/go-ipfs-routing/mock" - offline "gx/ipfs/QmScZySgru9jaoDa12sSfvh21sWbqF5eXkieTmJzAHJXkQ/go-ipfs-routing/offline" - ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" - dssync "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/sync" - routing "gx/ipfs/QmVBnJDKhtFXTRVjXKinqpwGu8t1DyNqPKan2iGX8PR8xG/go-libp2p-routing" - ropts "gx/ipfs/QmVBnJDKhtFXTRVjXKinqpwGu8t1DyNqPKan2iGX8PR8xG/go-libp2p-routing/options" - ipns "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns" + mockrouting "gx/ipfs/QmYKPBQpSSWwmgNTvVE3vQdPoeqxwudPQnXJ4hU383RsSA/go-ipfs-routing/mock" + offline "gx/ipfs/QmYKPBQpSSWwmgNTvVE3vQdPoeqxwudPQnXJ4hU383RsSA/go-ipfs-routing/offline" + ipns "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" - pstore "gx/ipfs/QmfAQMFpgDU2U4BXG64qVr8HSiictfWvkSBz7Y2oDj65st/go-libp2p-peerstore" - pstoremem "gx/ipfs/QmfAQMFpgDU2U4BXG64qVr8HSiictfWvkSBz7Y2oDj65st/go-libp2p-peerstore/pstoremem" + ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" + dssync "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/sync" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 054a51bbd..c7a37f850 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,15 +6,15 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" + path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + routing "gx/ipfs/QmQRfifvvbJ8xTKj4KX1VvGWK26hnPiy8eQvW1hmjc82nD/go-libp2p-routing" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" - ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" - routing "gx/ipfs/QmVBnJDKhtFXTRVjXKinqpwGu8t1DyNqPKan2iGX8PR8xG/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index c2a530c26..3584ae0a5 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,16 +7,16 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - "gx/ipfs/QmU4x3742bvgfxJsByEDpBnifJqjJdV6x528co4hwKCn46/go-unixfs" - path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" + "gx/ipfs/QmXYXeWXMa6XaqLthwc9gYzBdobRGBemWNv228XnAwqW9q/go-unixfs" + path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - offroute "gx/ipfs/QmScZySgru9jaoDa12sSfvh21sWbqF5eXkieTmJzAHJXkQ/go-ipfs-routing/offline" - ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" - dssync "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/sync" - ipns "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns" + pstoremem "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore/pstoremem" + offroute "gx/ipfs/QmYKPBQpSSWwmgNTvVE3vQdPoeqxwudPQnXJ4hU383RsSA/go-ipfs-routing/offline" + ipns "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" - pstoremem "gx/ipfs/QmfAQMFpgDU2U4BXG64qVr8HSiictfWvkSBz7Y2oDj65st/go-libp2p-peerstore/pstoremem" + ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" + dssync "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/sync" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index 4ba505dc8..751977098 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -8,7 +8,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" - path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" + path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index b85420675..951974515 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,16 +7,16 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmU4x3742bvgfxJsByEDpBnifJqjJdV6x528co4hwKCn46/go-unixfs" - path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" + ft "gx/ipfs/QmXYXeWXMa6XaqLthwc9gYzBdobRGBemWNv228XnAwqW9q/go-unixfs" + path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" - dsquery "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/query" - routing "gx/ipfs/QmVBnJDKhtFXTRVjXKinqpwGu8t1DyNqPKan2iGX8PR8xG/go-libp2p-routing" - ipns "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns" - pb "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns/pb" + routing "gx/ipfs/QmQRfifvvbJ8xTKj4KX1VvGWK26hnPiy8eQvW1hmjc82nD/go-libp2p-routing" + ipns "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns" + pb "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns/pb" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" + dsquery "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/query" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 3f6e6f02e..a95867d8c 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -8,13 +8,13 @@ import ( testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - mockrouting "gx/ipfs/QmScZySgru9jaoDa12sSfvh21sWbqF5eXkieTmJzAHJXkQ/go-ipfs-routing/mock" - dshelp "gx/ipfs/QmUDTSi6zJ6ACyQaKtxscCUxrg5DaXs9r4RQUPFQXGPHpo/go-ipfs-ds-help" - ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" - dssync "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/sync" + mockrouting "gx/ipfs/QmYKPBQpSSWwmgNTvVE3vQdPoeqxwudPQnXJ4hU383RsSA/go-ipfs-routing/mock" + ipns "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" - ipns "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns" + dshelp "gx/ipfs/QmafbjCxKZEU87RYYCHX2pFP9Q6uLSLAScLr2VUErNTH1f/go-ipfs-ds-help" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" + dssync "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/sync" ) type identity struct { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index ebe17c776..ae1e3ef59 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,15 +7,15 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" + path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" + pb "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns/pb" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" - pb "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns/pb" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index dd8693965..4cd43e710 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" + path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmVsVARb86uSe1qYouewFMNd2p2sp2NGWm1JGPReVDWchW/go-libp2p/p2p/net/mock" - pstore "gx/ipfs/QmfAQMFpgDU2U4BXG64qVr8HSiictfWvkSBz7Y2oDj65st/go-libp2p-peerstore" + pstore "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore" + mocknet "gx/ipfs/Qmd9zWxAeeDJoLdxqvaDXAGtoafX5cc9Tp25DNm9W7fVnB/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 82e3f06da..32728128f 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,14 +6,14 @@ import ( "testing" "time" - path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" + path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" - mockrouting "gx/ipfs/QmScZySgru9jaoDa12sSfvh21sWbqF5eXkieTmJzAHJXkQ/go-ipfs-routing/mock" - ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" - dssync "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/sync" - ipns "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns" + mockrouting "gx/ipfs/QmYKPBQpSSWwmgNTvVE3vQdPoeqxwudPQnXJ4hU383RsSA/go-ipfs-routing/mock" + ipns "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" + dssync "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/sync" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index dc53868ad..4d12c2713 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,15 +6,15 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path" + path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - routing "gx/ipfs/QmVBnJDKhtFXTRVjXKinqpwGu8t1DyNqPKan2iGX8PR8xG/go-libp2p-routing" + routing "gx/ipfs/QmQRfifvvbJ8xTKj4KX1VvGWK26hnPiy8eQvW1hmjc82nD/go-libp2p-routing" + dht "gx/ipfs/QmXBVGodwBx38Pvx6x8nt9mtCWbiDcNv7XWDGuvkajELvK/go-libp2p-kad-dht" + ipns "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns" + pb "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns/pb" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" - dht "gx/ipfs/QmZVakpN44VAUxs9eXAuUGLFYTCGmSyqSy6hyEKfMv68ME/go-libp2p-kad-dht" - ipns "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns" - pb "gx/ipfs/QmZrmn2BPZbSviQAWeyY2iXkCukmJHv9n7zrLgWU5KgbTb/go-ipns/pb" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) From 3861b47fe9a0ff2723540066f4db41a9c382402d Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Wed, 3 Oct 2018 07:35:57 +0200 Subject: [PATCH 2682/3817] gx: update go-datastore, go-libp2p-swarm License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/go-filestore@df5b402ad81e07835fb6254415d8b1d0af5635d4 --- filestore/filestore.go | 4 ++-- filestore/filestore_test.go | 6 +++--- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index b7dad3c55..d1e9ee9bf 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -14,9 +14,9 @@ import ( posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" blocks "gx/ipfs/QmRcHuYzAyswytBuMF78rj3LTChYszomRFXNg4685ZN1WM/go-block-format" - dsq "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/query" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" - blockstore "gx/ipfs/QmdriVJgKx4JADRgh3cYPXqXmsa1A45SvFki1nDWHhQNtC/go-ipfs-blockstore" + dsq "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/query" + blockstore "gx/ipfs/QmfUhZX9KpvJiuiziUzP2cjhRAyqHJURsPgRKn1cdDZMKa/go-ipfs-blockstore" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 42681c64a..e65bbf310 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,12 +7,12 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmcBoNcAP6qDjgRBew7yjvCqHq7p5jMstE44jPUBWBxzsV/go-merkledag" + dag "gx/ipfs/QmRfWhkc5eHLzZ1FActaXNeThijM2CY6JWc2qynQExFFJm/go-merkledag" posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" - blockstore "gx/ipfs/QmdriVJgKx4JADRgh3cYPXqXmsa1A45SvFki1nDWHhQNtC/go-ipfs-blockstore" + ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" + blockstore "gx/ipfs/QmfUhZX9KpvJiuiziUzP2cjhRAyqHJURsPgRKn1cdDZMKa/go-ipfs-blockstore" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 5e8bf6396..1f5baf24e 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -13,12 +13,12 @@ import ( posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" blocks "gx/ipfs/QmRcHuYzAyswytBuMF78rj3LTChYszomRFXNg4685ZN1WM/go-block-format" - dshelp "gx/ipfs/QmUDTSi6zJ6ACyQaKtxscCUxrg5DaXs9r4RQUPFQXGPHpo/go-ipfs-ds-help" - ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" - dsns "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/namespace" - dsq "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/query" - blockstore "gx/ipfs/QmdriVJgKx4JADRgh3cYPXqXmsa1A45SvFki1nDWHhQNtC/go-ipfs-blockstore" + dshelp "gx/ipfs/QmafbjCxKZEU87RYYCHX2pFP9Q6uLSLAScLr2VUErNTH1f/go-ipfs-ds-help" + ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" + dsns "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/namespace" + dsq "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/query" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" + blockstore "gx/ipfs/QmfUhZX9KpvJiuiziUzP2cjhRAyqHJURsPgRKn1cdDZMKa/go-ipfs-blockstore" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index 6213b0f10..ddc3c225e 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -7,10 +7,10 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - dshelp "gx/ipfs/QmUDTSi6zJ6ACyQaKtxscCUxrg5DaXs9r4RQUPFQXGPHpo/go-ipfs-ds-help" - ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" - dsq "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/query" - blockstore "gx/ipfs/QmdriVJgKx4JADRgh3cYPXqXmsa1A45SvFki1nDWHhQNtC/go-ipfs-blockstore" + dshelp "gx/ipfs/QmafbjCxKZEU87RYYCHX2pFP9Q6uLSLAScLr2VUErNTH1f/go-ipfs-ds-help" + ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" + dsq "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/query" + blockstore "gx/ipfs/QmfUhZX9KpvJiuiziUzP2cjhRAyqHJURsPgRKn1cdDZMKa/go-ipfs-blockstore" ) // Status is used to identify the state of the block data referenced From cd64a76e71c2edb1809a7201994ffb816aa1d67c Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Wed, 3 Oct 2018 07:35:57 +0200 Subject: [PATCH 2683/3817] gx: update go-datastore, go-libp2p-swarm License: MIT Signed-off-by: Lars Gierth This commit was moved from ipfs/go-ipfs-pinner@ee102bdb042c59c83c1bd77339fa09334f929b75 --- pinning/pinner/gc/gc.go | 10 +++++----- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 12 ++++++------ pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 12 ++++++------ 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index f5ca72547..bb55de95c 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,16 +8,16 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmcBoNcAP6qDjgRBew7yjvCqHq7p5jMstE44jPUBWBxzsV/go-merkledag" - bserv "gx/ipfs/QmcRecCZWM2NZfCQrCe97Ch3Givv8KKEP82tGUDntzdLFe/go-blockservice" + bserv "gx/ipfs/QmQPHB5JqEtHV819eBL8dX2uDfxpCz27oEEEsfUnFpfEdF/go-blockservice" + dag "gx/ipfs/QmRfWhkc5eHLzZ1FActaXNeThijM2CY6JWc2qynQExFFJm/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - offline "gx/ipfs/QmR5miWuikPxWyUrzMYJVmFUcD44pGdtc98h9Qsbp4YcJw/go-ipfs-exchange-offline" - dstore "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" + offline "gx/ipfs/QmPXcrGQQEEPswwg6YiE2WLk8qkmvncZ7zphMKKP8bXqY3/go-ipfs-exchange-offline" "gx/ipfs/QmVkMRSkXrpjqrroEXWuYBvDBnXCdMMY6gsKicBGVGUqKT/go-verifcid" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" + dstore "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" - bstore "gx/ipfs/QmdriVJgKx4JADRgh3cYPXqXmsa1A45SvFki1nDWHhQNtC/go-ipfs-blockstore" + bstore "gx/ipfs/QmfUhZX9KpvJiuiziUzP2cjhRAyqHJURsPgRKn1cdDZMKa/go-ipfs-blockstore" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index f6100749f..033f46989 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,11 +10,11 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmcBoNcAP6qDjgRBew7yjvCqHq7p5jMstE44jPUBWBxzsV/go-merkledag" + mdag "gx/ipfs/QmRfWhkc5eHLzZ1FActaXNeThijM2CY6JWc2qynQExFFJm/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" + ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 2549084f5..9917d35ca 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" - mdag "gx/ipfs/QmcBoNcAP6qDjgRBew7yjvCqHq7p5jMstE44jPUBWBxzsV/go-merkledag" - bs "gx/ipfs/QmcRecCZWM2NZfCQrCe97Ch3Givv8KKEP82tGUDntzdLFe/go-blockservice" + bs "gx/ipfs/QmQPHB5JqEtHV819eBL8dX2uDfxpCz27oEEEsfUnFpfEdF/go-blockservice" + mdag "gx/ipfs/QmRfWhkc5eHLzZ1FActaXNeThijM2CY6JWc2qynQExFFJm/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" + offline "gx/ipfs/QmPXcrGQQEEPswwg6YiE2WLk8qkmvncZ7zphMKKP8bXqY3/go-ipfs-exchange-offline" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmR5miWuikPxWyUrzMYJVmFUcD44pGdtc98h9Qsbp4YcJw/go-ipfs-exchange-offline" - ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" - dssync "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/sync" - blockstore "gx/ipfs/QmdriVJgKx4JADRgh3cYPXqXmsa1A45SvFki1nDWHhQNtC/go-ipfs-blockstore" + ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" + dssync "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/sync" + blockstore "gx/ipfs/QmfUhZX9KpvJiuiziUzP2cjhRAyqHJURsPgRKn1cdDZMKa/go-ipfs-blockstore" ) var rand = util.NewTimeSeededRand() diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 9b4446e37..1e5bcfe42 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmcBoNcAP6qDjgRBew7yjvCqHq7p5jMstE44jPUBWBxzsV/go-merkledag" + "gx/ipfs/QmRfWhkc5eHLzZ1FActaXNeThijM2CY6JWc2qynQExFFJm/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index e366eac70..a1dc36f3b 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,14 +5,14 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmcBoNcAP6qDjgRBew7yjvCqHq7p5jMstE44jPUBWBxzsV/go-merkledag" - bserv "gx/ipfs/QmcRecCZWM2NZfCQrCe97Ch3Givv8KKEP82tGUDntzdLFe/go-blockservice" + bserv "gx/ipfs/QmQPHB5JqEtHV819eBL8dX2uDfxpCz27oEEEsfUnFpfEdF/go-blockservice" + dag "gx/ipfs/QmRfWhkc5eHLzZ1FActaXNeThijM2CY6JWc2qynQExFFJm/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - offline "gx/ipfs/QmR5miWuikPxWyUrzMYJVmFUcD44pGdtc98h9Qsbp4YcJw/go-ipfs-exchange-offline" - ds "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore" - dsq "gx/ipfs/QmUyz7JTJzgegC6tiJrfby3mPhzcdswVtG4x58TQ6pq8jV/go-datastore/query" - blockstore "gx/ipfs/QmdriVJgKx4JADRgh3cYPXqXmsa1A45SvFki1nDWHhQNtC/go-ipfs-blockstore" + offline "gx/ipfs/QmPXcrGQQEEPswwg6YiE2WLk8qkmvncZ7zphMKKP8bXqY3/go-ipfs-exchange-offline" + ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" + dsq "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/query" + blockstore "gx/ipfs/QmfUhZX9KpvJiuiziUzP2cjhRAyqHJURsPgRKn1cdDZMKa/go-ipfs-blockstore" ) func ignoreCids(_ cid.Cid) {} From 0ef081eae1b8bdaf52870fe9f483ba05c7ca69ef Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 4 Oct 2018 19:11:27 -0400 Subject: [PATCH 2684/3817] gx update go-libp2p-peerstore License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/interface-go-ipfs-core@c8a6219cf25b4dfc1208523ec0bb2ee2efc2d85d --- coreiface/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/path.go b/coreiface/path.go index 0f06e8cc2..5748bf465 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" + ipfspath "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) From 3c1dfb400bb099a94a29290a0a4aa69ee831a79d Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 4 Oct 2018 19:11:27 -0400 Subject: [PATCH 2685/3817] gx update go-libp2p-peerstore License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-namesys@7422b4a10070877e87596e243123b149f6e71f3d --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 4 ++-- namesys/proquint.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 20e035650..d77ca168d 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" + path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index bc34266ef..85784238a 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" + path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 29d90497b..b4e3111d4 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,8 +8,8 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" + path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" ) type LookupTXTFunc func(name string) (txt []string, err error) diff --git a/namesys/interface.go b/namesys/interface.go index d3a2bf980..eb82f1860 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" + path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 58f0fd21a..9c3bf7227 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" + path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" diff --git a/namesys/namesys.go b/namesys/namesys.go index c7a37f850..1d38ac6da 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" + path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 3584ae0a5..7bb60170e 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,8 +7,8 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - "gx/ipfs/QmXYXeWXMa6XaqLthwc9gYzBdobRGBemWNv228XnAwqW9q/go-unixfs" - path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" + path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" + "gx/ipfs/QmdF8ovKr2zNMWi2hiDMqQAQeNw35xwytb1W7Ydwsf1ufx/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" pstoremem "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore/pstoremem" diff --git a/namesys/proquint.go b/namesys/proquint.go index 751977098..051f616e1 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,8 +7,8 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" + path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" - path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index 951974515..39ba878a5 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,8 +7,8 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmXYXeWXMa6XaqLthwc9gYzBdobRGBemWNv228XnAwqW9q/go-unixfs" - path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" + path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" + ft "gx/ipfs/QmdF8ovKr2zNMWi2hiDMqQAQeNw35xwytb1W7Ydwsf1ufx/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" routing "gx/ipfs/QmQRfifvvbJ8xTKj4KX1VvGWK26hnPiy8eQvW1hmjc82nD/go-libp2p-routing" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index ae1e3ef59..4cb5830f0 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" + path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 4cd43e710..399aadea8 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" + path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" pstore "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 32728128f..9983bb8e5 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" + path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" mockrouting "gx/ipfs/QmYKPBQpSSWwmgNTvVE3vQdPoeqxwudPQnXJ4hU383RsSA/go-ipfs-routing/mock" diff --git a/namesys/routing.go b/namesys/routing.go index 4d12c2713..788bbc60e 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmbE9gr6c2FomTgc2pRZRTooHpchJ1uaZKremypyWJMV4t/go-path" + path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" From 2d6cfbf64037fc43a5e9a8343e765f53a7aca796 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 4 Oct 2018 19:11:27 -0400 Subject: [PATCH 2686/3817] gx update go-libp2p-peerstore License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@758ac4ba2e6a3ec6575f65493b7545ed9f2b9912 --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index e65bbf310..e9d017a4d 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmRfWhkc5eHLzZ1FActaXNeThijM2CY6JWc2qynQExFFJm/go-merkledag" + dag "gx/ipfs/Qmb5kvkvMyuaJ9e58vLh3TdRWgH9CCQPLJD4BgvNQvQFwf/go-merkledag" posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" From ea46d80c33802c4ad173b16d6afe1ce35c8a2cf6 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 4 Oct 2018 19:11:27 -0400 Subject: [PATCH 2687/3817] gx update go-libp2p-peerstore License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@b87157487dc18fc97a538261e3f7a6d0ed9daf59 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index bb55de95c..e396d4074 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmQPHB5JqEtHV819eBL8dX2uDfxpCz27oEEEsfUnFpfEdF/go-blockservice" - dag "gx/ipfs/QmRfWhkc5eHLzZ1FActaXNeThijM2CY6JWc2qynQExFFJm/go-merkledag" + dag "gx/ipfs/Qmb5kvkvMyuaJ9e58vLh3TdRWgH9CCQPLJD4BgvNQvQFwf/go-merkledag" + bserv "gx/ipfs/QmewCqLsNJ2j7xrbEk8nYoCCMtBMSK3Eq6pdNdNPogdehi/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmPXcrGQQEEPswwg6YiE2WLk8qkmvncZ7zphMKKP8bXqY3/go-ipfs-exchange-offline" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 033f46989..a466c788d 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmRfWhkc5eHLzZ1FActaXNeThijM2CY6JWc2qynQExFFJm/go-merkledag" + mdag "gx/ipfs/Qmb5kvkvMyuaJ9e58vLh3TdRWgH9CCQPLJD4BgvNQvQFwf/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 9917d35ca..8014958b2 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - bs "gx/ipfs/QmQPHB5JqEtHV819eBL8dX2uDfxpCz27oEEEsfUnFpfEdF/go-blockservice" - mdag "gx/ipfs/QmRfWhkc5eHLzZ1FActaXNeThijM2CY6JWc2qynQExFFJm/go-merkledag" + mdag "gx/ipfs/Qmb5kvkvMyuaJ9e58vLh3TdRWgH9CCQPLJD4BgvNQvQFwf/go-merkledag" + bs "gx/ipfs/QmewCqLsNJ2j7xrbEk8nYoCCMtBMSK3Eq6pdNdNPogdehi/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmPXcrGQQEEPswwg6YiE2WLk8qkmvncZ7zphMKKP8bXqY3/go-ipfs-exchange-offline" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 1e5bcfe42..c71c284f0 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmRfWhkc5eHLzZ1FActaXNeThijM2CY6JWc2qynQExFFJm/go-merkledag" + "gx/ipfs/Qmb5kvkvMyuaJ9e58vLh3TdRWgH9CCQPLJD4BgvNQvQFwf/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index a1dc36f3b..6d33a5cbb 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmQPHB5JqEtHV819eBL8dX2uDfxpCz27oEEEsfUnFpfEdF/go-blockservice" - dag "gx/ipfs/QmRfWhkc5eHLzZ1FActaXNeThijM2CY6JWc2qynQExFFJm/go-merkledag" + dag "gx/ipfs/Qmb5kvkvMyuaJ9e58vLh3TdRWgH9CCQPLJD4BgvNQvQFwf/go-merkledag" + bserv "gx/ipfs/QmewCqLsNJ2j7xrbEk8nYoCCMtBMSK3Eq6pdNdNPogdehi/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmPXcrGQQEEPswwg6YiE2WLk8qkmvncZ7zphMKKP8bXqY3/go-ipfs-exchange-offline" From 3f8596df8b398139db13cb06f2c55c6738fc6528 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 4 Oct 2018 19:32:33 -0400 Subject: [PATCH 2688/3817] gx update libp2p/go-buffer-pool License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/interface-go-ipfs-core@349cbd1463198c9a811b6cc3c09c44608cdd486d --- coreiface/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/path.go b/coreiface/path.go index 5748bf465..63b15fb50 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" + ipfspath "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) From fad379d726947e062c6518f9ec2de58112b6d261 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 4 Oct 2018 19:32:33 -0400 Subject: [PATCH 2689/3817] gx update libp2p/go-buffer-pool License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-namesys@69b1f2324bda8d0bc1cd5782069734fc44aeefe1 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 4 ++-- namesys/proquint.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 4 ++-- 13 files changed, 17 insertions(+), 17 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index d77ca168d..5db5948ae 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" + path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index 85784238a..6e2345876 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" + path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index b4e3111d4..9696136d2 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" + path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index eb82f1860..aad69952c 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" + path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 9c3bf7227..eddd64da9 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" + path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" diff --git a/namesys/namesys.go b/namesys/namesys.go index 1d38ac6da..0371174fe 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" + path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 7bb60170e..a77306a40 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,8 +7,8 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" - "gx/ipfs/QmdF8ovKr2zNMWi2hiDMqQAQeNw35xwytb1W7Ydwsf1ufx/go-unixfs" + path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" + "gx/ipfs/QmavvHwEZTkNShKWK1jRejv2Y8oF6ZYxdGxytL3Mwvices/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" pstoremem "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore/pstoremem" diff --git a/namesys/proquint.go b/namesys/proquint.go index 051f616e1..cd29bff25 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" + path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 39ba878a5..412b65dbb 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,8 +7,8 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" - ft "gx/ipfs/QmdF8ovKr2zNMWi2hiDMqQAQeNw35xwytb1W7Ydwsf1ufx/go-unixfs" + path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" + ft "gx/ipfs/QmavvHwEZTkNShKWK1jRejv2Y8oF6ZYxdGxytL3Mwvices/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" routing "gx/ipfs/QmQRfifvvbJ8xTKj4KX1VvGWK26hnPiy8eQvW1hmjc82nD/go-libp2p-routing" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 4cb5830f0..8124ec3ad 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" + path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 399aadea8..54ec416fc 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" + path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" + mocknet "gx/ipfs/QmNmj2AeM46ZQqHARnWidb5qqHoZJFeYWzmG65jviJDRQY/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" pstore "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore" - mocknet "gx/ipfs/Qmd9zWxAeeDJoLdxqvaDXAGtoafX5cc9Tp25DNm9W7fVnB/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 9983bb8e5..5e7feb9ed 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" + path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" mockrouting "gx/ipfs/QmYKPBQpSSWwmgNTvVE3vQdPoeqxwudPQnXJ4hU383RsSA/go-ipfs-routing/mock" diff --git a/namesys/routing.go b/namesys/routing.go index 788bbc60e..f7b7c9e9a 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,12 +6,12 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmYh33CFYYEgQNSZ9PEP7ZN57dhErRZ7NfLS1BUA9GBBRk/go-path" + path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" routing "gx/ipfs/QmQRfifvvbJ8xTKj4KX1VvGWK26hnPiy8eQvW1hmjc82nD/go-libp2p-routing" - dht "gx/ipfs/QmXBVGodwBx38Pvx6x8nt9mtCWbiDcNv7XWDGuvkajELvK/go-libp2p-kad-dht" + dht "gx/ipfs/QmXKSnQHAihkcGgk8ZXz7FgZgHboXeRduuEz9FkAddJK6P/go-libp2p-kad-dht" ipns "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns" pb "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns/pb" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" From c7981f31fd6b60e9a10ccc989bf661bec39faa7f Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 4 Oct 2018 19:32:33 -0400 Subject: [PATCH 2690/3817] gx update libp2p/go-buffer-pool License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@cf4176704f499b9350e793ef42193a1fb424e885 --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index e9d017a4d..72f159fc9 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/Qmb5kvkvMyuaJ9e58vLh3TdRWgH9CCQPLJD4BgvNQvQFwf/go-merkledag" + dag "gx/ipfs/QmTGpm48qm4fUZ9E5hMXy4ZngJUYCMKu15rTMVR3BSEnPm/go-merkledag" posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" From 8468a34d9148f0ca5f72d3aa6f0a3751154dcfab Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 4 Oct 2018 19:32:33 -0400 Subject: [PATCH 2691/3817] gx update libp2p/go-buffer-pool License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@4cd7edc1cbae9c004d2ac8c6671f05fef26a9b9b --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index e396d4074..2be1333d5 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/Qmb5kvkvMyuaJ9e58vLh3TdRWgH9CCQPLJD4BgvNQvQFwf/go-merkledag" - bserv "gx/ipfs/QmewCqLsNJ2j7xrbEk8nYoCCMtBMSK3Eq6pdNdNPogdehi/go-blockservice" + bserv "gx/ipfs/QmNozJswSuwiZspexEHcQo5GMqpzM5exUGjNW6s4AAipUX/go-blockservice" + dag "gx/ipfs/QmTGpm48qm4fUZ9E5hMXy4ZngJUYCMKu15rTMVR3BSEnPm/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmPXcrGQQEEPswwg6YiE2WLk8qkmvncZ7zphMKKP8bXqY3/go-ipfs-exchange-offline" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index a466c788d..2ed476414 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/Qmb5kvkvMyuaJ9e58vLh3TdRWgH9CCQPLJD4BgvNQvQFwf/go-merkledag" + mdag "gx/ipfs/QmTGpm48qm4fUZ9E5hMXy4ZngJUYCMKu15rTMVR3BSEnPm/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 8014958b2..4890872ab 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - mdag "gx/ipfs/Qmb5kvkvMyuaJ9e58vLh3TdRWgH9CCQPLJD4BgvNQvQFwf/go-merkledag" - bs "gx/ipfs/QmewCqLsNJ2j7xrbEk8nYoCCMtBMSK3Eq6pdNdNPogdehi/go-blockservice" + bs "gx/ipfs/QmNozJswSuwiZspexEHcQo5GMqpzM5exUGjNW6s4AAipUX/go-blockservice" + mdag "gx/ipfs/QmTGpm48qm4fUZ9E5hMXy4ZngJUYCMKu15rTMVR3BSEnPm/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmPXcrGQQEEPswwg6YiE2WLk8qkmvncZ7zphMKKP8bXqY3/go-ipfs-exchange-offline" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index c71c284f0..8bccc8503 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/Qmb5kvkvMyuaJ9e58vLh3TdRWgH9CCQPLJD4BgvNQvQFwf/go-merkledag" + "gx/ipfs/QmTGpm48qm4fUZ9E5hMXy4ZngJUYCMKu15rTMVR3BSEnPm/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 6d33a5cbb..0502880ff 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/Qmb5kvkvMyuaJ9e58vLh3TdRWgH9CCQPLJD4BgvNQvQFwf/go-merkledag" - bserv "gx/ipfs/QmewCqLsNJ2j7xrbEk8nYoCCMtBMSK3Eq6pdNdNPogdehi/go-blockservice" + bserv "gx/ipfs/QmNozJswSuwiZspexEHcQo5GMqpzM5exUGjNW6s4AAipUX/go-blockservice" + dag "gx/ipfs/QmTGpm48qm4fUZ9E5hMXy4ZngJUYCMKu15rTMVR3BSEnPm/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmPXcrGQQEEPswwg6YiE2WLk8qkmvncZ7zphMKKP8bXqY3/go-ipfs-exchange-offline" From b06611e7da0ebaef24e0b3db614716c940882793 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 4 Oct 2018 21:12:53 -0700 Subject: [PATCH 2692/3817] update unixfs inline option comment to give us room to change things (addressing CR) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@7d577641499b51b4d98ae7e10fed2fd5bf9d516a --- coreiface/options/unixfs.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 9249af895..d486981c3 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -171,9 +171,8 @@ func (unixfsOpts) Inline(enable bool) UnixfsAddOption { // option. Default: 32 bytes // // Note that while there is no hard limit on the number of bytes, it should be -// kept at a reasonably low value, such as 64 and no more than 1k. Setting this -// value too high may cause various problems, such as render some -// blocks unfetchable. +// kept at a reasonably low value, such as 64; implementations may choose to +// reject anything larger. func (unixfsOpts) InlineLimit(limit int) UnixfsAddOption { return func(settings *UnixfsAddSettings) error { settings.InlineLimit = limit From f06c01e06b04ec7a27b0738f984e0f30b005c711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 19:23:38 +0100 Subject: [PATCH 2693/3817] coreapi: pubsub interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@ccaec46f3deaf46e20f9c265d5920f68251e4da4 --- coreiface/options/pubsub.go | 56 +++++++++++++++++++++++++++++++++++++ coreiface/pubsub.go | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 coreiface/options/pubsub.go create mode 100644 coreiface/pubsub.go diff --git a/coreiface/options/pubsub.go b/coreiface/options/pubsub.go new file mode 100644 index 000000000..e276d7e4a --- /dev/null +++ b/coreiface/options/pubsub.go @@ -0,0 +1,56 @@ +package options + +type PubSubPeersSettings struct { + Topic string +} + +type PubSubSubscribeSettings struct { + Discover bool +} + +type PubSubPeersOption func(*PubSubPeersSettings) error +type PubSubSubscribeOption func(*PubSubSubscribeSettings) error + +func PubSubPeersOptions(opts ...PubSubPeersOption) (*PubSubPeersSettings, error) { + options := &PubSubPeersSettings{ + Topic: "", + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +func PubSubSubscribeOptions(opts ...PubSubSubscribeOption) (*PubSubSubscribeSettings, error) { + options := &PubSubSubscribeSettings{ + Discover: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type PubSubOptions struct{} + +func (api *PubSubOptions) WithTopic(topic string) PubSubPeersOption { + return func(settings *PubSubPeersSettings) error { + settings.Topic = topic + return nil + } +} + +func (api *PubSubOptions) WithDiscover(discover bool) PubSubSubscribeOption { + return func(settings *PubSubSubscribeSettings) error { + settings.Discover = discover + return nil + } +} diff --git a/coreiface/pubsub.go b/coreiface/pubsub.go new file mode 100644 index 000000000..f78734a09 --- /dev/null +++ b/coreiface/pubsub.go @@ -0,0 +1,51 @@ +package iface + +import ( + "context" + "io" + + options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + + peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" +) + +// PubSubSubscription is an active PubSub subscription +type PubSubSubscription interface { + io.Closer + + // Chan return incoming message channel + Chan(context.Context) <-chan PubSubMessage +} + +// PubSubMessage is a single PubSub message +type PubSubMessage interface { + // From returns id of a peer from which the message has arrived + From() peer.ID + + // Data returns the message body + Data() []byte +} + +// PubSubAPI specifies the interface to PubSub +type PubSubAPI interface { + // Ls lists subscribed topics by name + Ls(context.Context) ([]string, error) + + // Peers list peers we are currently pubsubbing with + // TODO: WithTopic + Peers(context.Context, ...options.PubSubPeersOption) ([]peer.ID, error) + + // WithTopic is an option for peers which specifies a topic filter for the + // function + WithTopic(topic string) options.PubSubPeersOption + + // Publish a message to a given pubsub topic + Publish(context.Context, string, []byte) error + + // Subscribe to messages on a given topic + Subscribe(context.Context, string) (PubSubSubscription, error) + + // WithDiscover is an option for Subscribe which specifies whether to try to + // discover other peers subscribed to the same topic + WithDiscover(discover bool) options.PubSubSubscribeOption +} From 1b8732304c12b825e9a8fd69b51494c9c67f39d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 10 Mar 2018 19:28:22 +0100 Subject: [PATCH 2694/3817] coreapi: implement pubsub api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@0d0069ff23a4dd26fa62fd91e5875b2526742da5 --- coreiface/coreapi.go | 3 +++ coreiface/errors.go | 2 +- coreiface/options/pubsub.go | 8 +++++--- coreiface/pubsub.go | 16 ++++------------ 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index 0b153b6f9..bc889237b 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -37,6 +37,9 @@ type CoreAPI interface { // Swarm returns an implementation of Swarm API Swarm() SwarmAPI + // PubSub returns an implementation of PubSub API + PubSub() PubSubAPI + // ResolvePath resolves the path using Unixfs resolver ResolvePath(context.Context, Path) (ResolvedPath, error) diff --git a/coreiface/errors.go b/coreiface/errors.go index 81f978971..072275409 100644 --- a/coreiface/errors.go +++ b/coreiface/errors.go @@ -4,5 +4,5 @@ import "errors" var ( ErrIsDir = errors.New("object is a directory") - ErrOffline = errors.New("can't resolve, ipfs node is offline") + ErrOffline = errors.New("this action must be run in online mode, try running 'ipfs daemon' first") ) diff --git a/coreiface/options/pubsub.go b/coreiface/options/pubsub.go index e276d7e4a..f0a614d58 100644 --- a/coreiface/options/pubsub.go +++ b/coreiface/options/pubsub.go @@ -39,16 +39,18 @@ func PubSubSubscribeOptions(opts ...PubSubSubscribeOption) (*PubSubSubscribeSett return options, nil } -type PubSubOptions struct{} +type pubsubOpts struct{} -func (api *PubSubOptions) WithTopic(topic string) PubSubPeersOption { +var PubBub nameOpts + +func (pubsubOpts) Topic(topic string) PubSubPeersOption { return func(settings *PubSubPeersSettings) error { settings.Topic = topic return nil } } -func (api *PubSubOptions) WithDiscover(discover bool) PubSubSubscribeOption { +func (pubsubOpts) Discover(discover bool) PubSubSubscribeOption { return func(settings *PubSubSubscribeSettings) error { settings.Discover = discover return nil diff --git a/coreiface/pubsub.go b/coreiface/pubsub.go index f78734a09..4b52ed6d6 100644 --- a/coreiface/pubsub.go +++ b/coreiface/pubsub.go @@ -6,15 +6,15 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - peer "gx/ipfs/QmZoWKhxUmZ2seW4BzX6fJkNR8hh9PsGModr7q171yq2SS/go-libp2p-peer" + peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" ) // PubSubSubscription is an active PubSub subscription type PubSubSubscription interface { io.Closer - // Chan return incoming message channel - Chan(context.Context) <-chan PubSubMessage + // Next return the next incoming message + Next(context.Context) (PubSubMessage, error) } // PubSubMessage is a single PubSub message @@ -35,17 +35,9 @@ type PubSubAPI interface { // TODO: WithTopic Peers(context.Context, ...options.PubSubPeersOption) ([]peer.ID, error) - // WithTopic is an option for peers which specifies a topic filter for the - // function - WithTopic(topic string) options.PubSubPeersOption - // Publish a message to a given pubsub topic Publish(context.Context, string, []byte) error // Subscribe to messages on a given topic - Subscribe(context.Context, string) (PubSubSubscription, error) - - // WithDiscover is an option for Subscribe which specifies whether to try to - // discover other peers subscribed to the same topic - WithDiscover(discover bool) options.PubSubSubscribeOption + Subscribe(context.Context, string, ...options.PubSubSubscribeOption) (PubSubSubscription, error) } From d23f749f57daa3b22c91036530b9430bfebec610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 11 Sep 2018 05:43:54 +0200 Subject: [PATCH 2695/3817] coreapi pubsub: add tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@3f9a6ce3446d2d5746c6d9976c9325f1673c8e99 --- coreiface/options/pubsub.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/options/pubsub.go b/coreiface/options/pubsub.go index f0a614d58..c387d613d 100644 --- a/coreiface/options/pubsub.go +++ b/coreiface/options/pubsub.go @@ -41,7 +41,7 @@ func PubSubSubscribeOptions(opts ...PubSubSubscribeOption) (*PubSubSubscribeSett type pubsubOpts struct{} -var PubBub nameOpts +var PubSub pubsubOpts func (pubsubOpts) Topic(topic string) PubSubPeersOption { return func(settings *PubSubPeersSettings) error { From add2ae0705df01044239e9ed5691974847332408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 11 Sep 2018 12:52:40 +0200 Subject: [PATCH 2696/3817] pubsub cmd: switch to coreapi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@706e552037bd75687b5ff0cedb9802bc7f2d4617 --- coreiface/pubsub.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/coreiface/pubsub.go b/coreiface/pubsub.go index 4b52ed6d6..4c9a1d73e 100644 --- a/coreiface/pubsub.go +++ b/coreiface/pubsub.go @@ -6,7 +6,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer" + peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" ) // PubSubSubscription is an active PubSub subscription @@ -24,6 +24,12 @@ type PubSubMessage interface { // Data returns the message body Data() []byte + + // Seq returns message identifier + Seq() []byte + + // Topics returns list of topics this message was set to + Topics() []string } // PubSubAPI specifies the interface to PubSub From 0373c3c9aee429624a99c6a1c080027923ec6e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 26 Sep 2018 18:24:35 +0200 Subject: [PATCH 2697/3817] coreapi pubsub: fix review nits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@162e182a2c2f67c227b7dd4b3d661d3b15ca1d7b --- coreiface/pubsub.go | 1 - 1 file changed, 1 deletion(-) diff --git a/coreiface/pubsub.go b/coreiface/pubsub.go index 4c9a1d73e..d7a21e02f 100644 --- a/coreiface/pubsub.go +++ b/coreiface/pubsub.go @@ -38,7 +38,6 @@ type PubSubAPI interface { Ls(context.Context) ([]string, error) // Peers list peers we are currently pubsubbing with - // TODO: WithTopic Peers(context.Context, ...options.PubSubPeersOption) ([]peer.ID, error) // Publish a message to a given pubsub topic From 8c4523ce0bbc6ce3cd779bdaae5450ffd039c295 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 5 Oct 2018 13:01:55 -0700 Subject: [PATCH 2698/3817] gx: update go-datastore This commit was moved from ipfs/go-ipfs-blockstore@09cd28eb268c46a70a1118c72072e8536f0fd212 --- blockstore/blockstore_test.go | 4 ++++ blockstore/bloom_cache_test.go | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index d0fa739c2..50b8ae055 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -254,6 +254,10 @@ func (c *queryTestDS) Has(key ds.Key) (exists bool, err error) { return c.ds.Has(key) } +func (c *queryTestDS) GetSize(key ds.Key) (size int, err error) { + return c.ds.GetSize(key) +} + func (c *queryTestDS) Delete(key ds.Key) (err error) { return c.ds.Delete(key) } diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 7c61ac181..d9474ada1 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -181,6 +181,11 @@ func (c *callbackDatastore) Has(key ds.Key) (exists bool, err error) { return c.ds.Has(key) } +func (c *callbackDatastore) GetSize(key ds.Key) (size int, err error) { + c.CallF() + return c.ds.GetSize(key) +} + func (c *callbackDatastore) Delete(key ds.Key) (err error) { c.CallF() return c.ds.Delete(key) From db833c767c14389f8f12a8f29877d9d1f9045f2a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 5 Oct 2018 13:03:48 -0700 Subject: [PATCH 2699/3817] use datastore.GetSize This commit was moved from ipfs/go-ipfs-blockstore@a11eacf44448935286565c3cc822e671d2b2f011 --- blockstore/blockstore.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 6bb8e399d..f57a90af6 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -178,14 +178,11 @@ func (bs *blockstore) Has(k cid.Cid) (bool, error) { } func (bs *blockstore) GetSize(k cid.Cid) (int, error) { - bdata, err := bs.datastore.Get(dshelp.CidToDsKey(k)) + size, err := bs.datastore.GetSize(dshelp.CidToDsKey(k)) if err == ds.ErrNotFound { return -1, ErrNotFound } - if err != nil { - return -1, err - } - return len(bdata), nil + return size, err } func (bs *blockstore) DeleteBlock(k cid.Cid) error { From 8c56ae47a347f4f0ae28786eb3176e3d29566718 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 5 Oct 2018 14:11:56 -0700 Subject: [PATCH 2700/3817] gx: update stuff * go-datastore and friends: GetSize * badger: new release, fewer allocations * go-mplex: send fewer packets * go-bitswap: pack multiple blocks in a single message, fewer allocations * go-buffer-pool: replace the buffer pool from go-msgio * yamux: fixed data race and uses go-buffer-pool for stream read-buffers to reduce memory and allocations. * go-libp2p-secio: get rid of a hot-spot allocation * go-libp2p-peerstore: reduced allocations (at the cost of some memory) More? License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@505067f9e4e7bad7a5f4c0eeb76becd205be4fde --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 20 ++++++++++---------- namesys/namesys.go | 6 +++--- namesys/namesys_test.go | 14 +++++++------- namesys/proquint.go | 2 +- namesys/publisher.go | 14 +++++++------- namesys/publisher_test.go | 10 +++++----- namesys/republisher/repub.go | 6 +++--- namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 10 +++++----- namesys/routing.go | 10 +++++----- 14 files changed, 53 insertions(+), 53 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 5db5948ae..705cf9fae 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" + path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index 6e2345876..8272c2bdb 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" + path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 9696136d2..247ea97b1 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" + path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index aad69952c..142dfea9c 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" + path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index eddd64da9..23a069569 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,22 +6,22 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" + path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - routing "gx/ipfs/QmQRfifvvbJ8xTKj4KX1VvGWK26hnPiy8eQvW1hmjc82nD/go-libp2p-routing" - ropts "gx/ipfs/QmQRfifvvbJ8xTKj4KX1VvGWK26hnPiy8eQvW1hmjc82nD/go-libp2p-routing/options" - pstore "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore" - pstoremem "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore/pstoremem" + mockrouting "gx/ipfs/QmQqPdGQfsfQTpRUUzgD3d2RzWHJffZsSqwopnZ2BkiezW/go-ipfs-routing/mock" + offline "gx/ipfs/QmQqPdGQfsfQTpRUUzgD3d2RzWHJffZsSqwopnZ2BkiezW/go-ipfs-routing/offline" record "gx/ipfs/QmSb4B8ZAAj5ALe9LjfzPyF8Ma6ezC1NTnDF2JQPUJxEXb/go-libp2p-record" - mockrouting "gx/ipfs/QmYKPBQpSSWwmgNTvVE3vQdPoeqxwudPQnXJ4hU383RsSA/go-ipfs-routing/mock" - offline "gx/ipfs/QmYKPBQpSSWwmgNTvVE3vQdPoeqxwudPQnXJ4hU383RsSA/go-ipfs-routing/offline" - ipns "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns" + routing "gx/ipfs/QmVQPj6rHdqz6dDQrjcdP36zDYLaoB7xwqRg39kx2PqqKU/go-libp2p-routing" + ropts "gx/ipfs/QmVQPj6rHdqz6dDQrjcdP36zDYLaoB7xwqRg39kx2PqqKU/go-libp2p-routing/options" + ipns "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns" + pstore "gx/ipfs/QmXEyLwySuDMXejWBu8XwdkX2WuGKk8x9jFwz8js7j72UX/go-libp2p-peerstore" + pstoremem "gx/ipfs/QmXEyLwySuDMXejWBu8XwdkX2WuGKk8x9jFwz8js7j72UX/go-libp2p-peerstore/pstoremem" + ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" + dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" - ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" - dssync "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/sync" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 0371174fe..7da1f4c71 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,15 +6,15 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" + path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - routing "gx/ipfs/QmQRfifvvbJ8xTKj4KX1VvGWK26hnPiy8eQvW1hmjc82nD/go-libp2p-routing" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" + routing "gx/ipfs/QmVQPj6rHdqz6dDQrjcdP36zDYLaoB7xwqRg39kx2PqqKU/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" + ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" - ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index a77306a40..5ee9ebeb6 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,16 +7,16 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" - "gx/ipfs/QmavvHwEZTkNShKWK1jRejv2Y8oF6ZYxdGxytL3Mwvices/go-unixfs" + "gx/ipfs/QmQDcPcBH8nfz3JB4K4oEvxhRmBwCrMgvG966XpExEWexf/go-unixfs" + path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - pstoremem "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore/pstoremem" - offroute "gx/ipfs/QmYKPBQpSSWwmgNTvVE3vQdPoeqxwudPQnXJ4hU383RsSA/go-ipfs-routing/offline" - ipns "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns" + offroute "gx/ipfs/QmQqPdGQfsfQTpRUUzgD3d2RzWHJffZsSqwopnZ2BkiezW/go-ipfs-routing/offline" + ipns "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns" + pstoremem "gx/ipfs/QmXEyLwySuDMXejWBu8XwdkX2WuGKk8x9jFwz8js7j72UX/go-libp2p-peerstore/pstoremem" + ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" + dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" - ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" - dssync "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/sync" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index cd29bff25..9647f8152 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" + path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 412b65dbb..a5e2eae7e 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,16 +7,16 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" - ft "gx/ipfs/QmavvHwEZTkNShKWK1jRejv2Y8oF6ZYxdGxytL3Mwvices/go-unixfs" + ft "gx/ipfs/QmQDcPcBH8nfz3JB4K4oEvxhRmBwCrMgvG966XpExEWexf/go-unixfs" + path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - routing "gx/ipfs/QmQRfifvvbJ8xTKj4KX1VvGWK26hnPiy8eQvW1hmjc82nD/go-libp2p-routing" - ipns "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns" - pb "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns/pb" + routing "gx/ipfs/QmVQPj6rHdqz6dDQrjcdP36zDYLaoB7xwqRg39kx2PqqKU/go-libp2p-routing" + ipns "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns" + pb "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns/pb" + ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" + dsquery "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" - ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" - dsquery "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/query" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index a95867d8c..b55742e14 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -8,13 +8,13 @@ import ( testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - mockrouting "gx/ipfs/QmYKPBQpSSWwmgNTvVE3vQdPoeqxwudPQnXJ4hU383RsSA/go-ipfs-routing/mock" - ipns "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns" + mockrouting "gx/ipfs/QmQqPdGQfsfQTpRUUzgD3d2RzWHJffZsSqwopnZ2BkiezW/go-ipfs-routing/mock" + dshelp "gx/ipfs/QmS73grfbWgWrNztd8Lns9GCG3jjRNDfcPYg2VYQzKDZSt/go-ipfs-ds-help" + ipns "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" - dshelp "gx/ipfs/QmafbjCxKZEU87RYYCHX2pFP9Q6uLSLAScLr2VUErNTH1f/go-ipfs-ds-help" + ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" + dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" - ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" - dssync "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/sync" ) type identity struct { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 8124ec3ad..e2e650c46 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,15 +7,15 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" + path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - pb "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns/pb" + pb "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns/pb" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" + ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" - ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 54ec416fc..0a3afbf0b 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" + path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" - mocknet "gx/ipfs/QmNmj2AeM46ZQqHARnWidb5qqHoZJFeYWzmG65jviJDRQY/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - pstore "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore" + mocknet "gx/ipfs/QmU9Cf9q5TBCAC3kg74Fqr6K7DQTwa41C44YypYqB2GfR8/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmXEyLwySuDMXejWBu8XwdkX2WuGKk8x9jFwz8js7j72UX/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 5e7feb9ed..18abb6897 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,14 +6,14 @@ import ( "testing" "time" - path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" + path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" - mockrouting "gx/ipfs/QmYKPBQpSSWwmgNTvVE3vQdPoeqxwudPQnXJ4hU383RsSA/go-ipfs-routing/mock" - ipns "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns" + mockrouting "gx/ipfs/QmQqPdGQfsfQTpRUUzgD3d2RzWHJffZsSqwopnZ2BkiezW/go-ipfs-routing/mock" + ipns "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns" + ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" + dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" - ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" - dssync "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/sync" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index f7b7c9e9a..b65c0fa59 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,14 +6,14 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" + path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - routing "gx/ipfs/QmQRfifvvbJ8xTKj4KX1VvGWK26hnPiy8eQvW1hmjc82nD/go-libp2p-routing" - dht "gx/ipfs/QmXKSnQHAihkcGgk8ZXz7FgZgHboXeRduuEz9FkAddJK6P/go-libp2p-kad-dht" - ipns "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns" - pb "gx/ipfs/QmYZdD9dRfHtoYt4qAFgtKoiPBAoXntmM3ZcktZVvAgB4s/go-ipns/pb" + dht "gx/ipfs/QmTdMq4uYZXmGW3u6KgnpCRWjo1Y7dWjRuAaGPw7Qxqr1s/go-libp2p-kad-dht" + routing "gx/ipfs/QmVQPj6rHdqz6dDQrjcdP36zDYLaoB7xwqRg39kx2PqqKU/go-libp2p-routing" + ipns "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns" + pb "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns/pb" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" From b77e7021ee4b2093d2e2a98f5274ace254a537e1 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 5 Oct 2018 14:11:56 -0700 Subject: [PATCH 2701/3817] gx: update stuff * go-datastore and friends: GetSize * badger: new release, fewer allocations * go-mplex: send fewer packets * go-bitswap: pack multiple blocks in a single message, fewer allocations * go-buffer-pool: replace the buffer pool from go-msgio * yamux: fixed data race and uses go-buffer-pool for stream read-buffers to reduce memory and allocations. * go-libp2p-secio: get rid of a hot-spot allocation * go-libp2p-peerstore: reduced allocations (at the cost of some memory) More? License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@144aa5240ff7e379b34dd0e1adf9f8c1e991b3cc --- coreiface/dht.go | 2 +- coreiface/options/unixfs.go | 2 +- coreiface/path.go | 2 +- coreiface/swarm.go | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/coreiface/dht.go b/coreiface/dht.go index 4a76f4d39..3096c8fb7 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -5,7 +5,7 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - pstore "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore" + pstore "gx/ipfs/QmXEyLwySuDMXejWBu8XwdkX2WuGKk8x9jFwz8js7j72UX/go-libp2p-peerstore" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" ) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 6a54b2d39..b4661b793 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -6,7 +6,7 @@ import ( cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - dag "gx/ipfs/QmTGpm48qm4fUZ9E5hMXy4ZngJUYCMKu15rTMVR3BSEnPm/go-merkledag" + dag "gx/ipfs/QmXTw4By9FMZAt7qJm4JoJuNBrBgqMMzkS4AjKc4zqTUVd/go-merkledag" ) type Layout int diff --git a/coreiface/path.go b/coreiface/path.go index 63b15fb50..9bb46b4b4 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmV4QxScV9Y7LbaWhHazFfRd8uyeUd4pAH8a7fFFbi5odJ/go-path" + ipfspath "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) diff --git a/coreiface/swarm.go b/coreiface/swarm.go index 58caf6759..d8bca395c 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -5,8 +5,8 @@ import ( "errors" "time" - net "gx/ipfs/QmQdLcvoy3JuSqhV6iwQ9T6Cv7hWLAdzob4jUZRPqFL67Z/go-libp2p-net" - pstore "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore" + net "gx/ipfs/QmWUPYHpNv4YahaBYXovuEJttgfqcNcN9Gg4arhQYcRoqa/go-libp2p-net" + pstore "gx/ipfs/QmXEyLwySuDMXejWBu8XwdkX2WuGKk8x9jFwz8js7j72UX/go-libp2p-peerstore" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol" "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" From b2c8ae0a97d26c1a7da4e1c7e8e58f65729014db Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 5 Oct 2018 14:11:56 -0700 Subject: [PATCH 2702/3817] gx: update stuff * go-datastore and friends: GetSize * badger: new release, fewer allocations * go-mplex: send fewer packets * go-bitswap: pack multiple blocks in a single message, fewer allocations * go-buffer-pool: replace the buffer pool from go-msgio * yamux: fixed data race and uses go-buffer-pool for stream read-buffers to reduce memory and allocations. * go-libp2p-secio: get rid of a hot-spot allocation * go-libp2p-peerstore: reduced allocations (at the cost of some memory) More? License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@d721b445f8339be1eb4f65df1c312342a1268094 --- filestore/filestore.go | 4 ++-- filestore/filestore_test.go | 6 +++--- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index d1e9ee9bf..1ad7471fb 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -15,8 +15,8 @@ import ( cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" blocks "gx/ipfs/QmRcHuYzAyswytBuMF78rj3LTChYszomRFXNg4685ZN1WM/go-block-format" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" - dsq "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/query" - blockstore "gx/ipfs/QmfUhZX9KpvJiuiziUzP2cjhRAyqHJURsPgRKn1cdDZMKa/go-ipfs-blockstore" + dsq "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" + blockstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 72f159fc9..98861cf3f 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,12 +7,12 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmTGpm48qm4fUZ9E5hMXy4ZngJUYCMKu15rTMVR3BSEnPm/go-merkledag" + dag "gx/ipfs/QmXTw4By9FMZAt7qJm4JoJuNBrBgqMMzkS4AjKc4zqTUVd/go-merkledag" posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" - blockstore "gx/ipfs/QmfUhZX9KpvJiuiziUzP2cjhRAyqHJURsPgRKn1cdDZMKa/go-ipfs-blockstore" + ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" + blockstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 1f5baf24e..7ecfaae8c 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -13,12 +13,12 @@ import ( posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" blocks "gx/ipfs/QmRcHuYzAyswytBuMF78rj3LTChYszomRFXNg4685ZN1WM/go-block-format" - dshelp "gx/ipfs/QmafbjCxKZEU87RYYCHX2pFP9Q6uLSLAScLr2VUErNTH1f/go-ipfs-ds-help" - ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" - dsns "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/namespace" - dsq "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/query" + dshelp "gx/ipfs/QmS73grfbWgWrNztd8Lns9GCG3jjRNDfcPYg2VYQzKDZSt/go-ipfs-ds-help" + ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" + dsns "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/namespace" + dsq "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" + blockstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" - blockstore "gx/ipfs/QmfUhZX9KpvJiuiziUzP2cjhRAyqHJURsPgRKn1cdDZMKa/go-ipfs-blockstore" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index ddc3c225e..39df78c6f 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -7,10 +7,10 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - dshelp "gx/ipfs/QmafbjCxKZEU87RYYCHX2pFP9Q6uLSLAScLr2VUErNTH1f/go-ipfs-ds-help" - ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" - dsq "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/query" - blockstore "gx/ipfs/QmfUhZX9KpvJiuiziUzP2cjhRAyqHJURsPgRKn1cdDZMKa/go-ipfs-blockstore" + dshelp "gx/ipfs/QmS73grfbWgWrNztd8Lns9GCG3jjRNDfcPYg2VYQzKDZSt/go-ipfs-ds-help" + ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" + dsq "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" + blockstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" ) // Status is used to identify the state of the block data referenced From 71601211f4d4499181b721737c0756ec159a40a7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 5 Oct 2018 14:11:56 -0700 Subject: [PATCH 2703/3817] gx: update stuff * go-datastore and friends: GetSize * badger: new release, fewer allocations * go-mplex: send fewer packets * go-bitswap: pack multiple blocks in a single message, fewer allocations * go-buffer-pool: replace the buffer pool from go-msgio * yamux: fixed data race and uses go-buffer-pool for stream read-buffers to reduce memory and allocations. * go-libp2p-secio: get rid of a hot-spot allocation * go-libp2p-peerstore: reduced allocations (at the cost of some memory) More? License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@395dd6a582d8b71decf2a4c4835df778857e15df --- pinning/pinner/gc/gc.go | 10 +++++----- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 12 ++++++------ pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 12 ++++++------ 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 2be1333d5..c392eee9d 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,16 +8,16 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmNozJswSuwiZspexEHcQo5GMqpzM5exUGjNW6s4AAipUX/go-blockservice" - dag "gx/ipfs/QmTGpm48qm4fUZ9E5hMXy4ZngJUYCMKu15rTMVR3BSEnPm/go-merkledag" + dag "gx/ipfs/QmXTw4By9FMZAt7qJm4JoJuNBrBgqMMzkS4AjKc4zqTUVd/go-merkledag" + bserv "gx/ipfs/QmY1fUNoXjC8sH86kyaK8BWFGaU6MmH4AJfF1w4sKjmtRZ/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - offline "gx/ipfs/QmPXcrGQQEEPswwg6YiE2WLk8qkmvncZ7zphMKKP8bXqY3/go-ipfs-exchange-offline" + offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" "gx/ipfs/QmVkMRSkXrpjqrroEXWuYBvDBnXCdMMY6gsKicBGVGUqKT/go-verifcid" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" - dstore "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" + dstore "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" + bstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" - bstore "gx/ipfs/QmfUhZX9KpvJiuiziUzP2cjhRAyqHJURsPgRKn1cdDZMKa/go-ipfs-blockstore" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 2ed476414..4b91fa407 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,11 +10,11 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmTGpm48qm4fUZ9E5hMXy4ZngJUYCMKu15rTMVR3BSEnPm/go-merkledag" + mdag "gx/ipfs/QmXTw4By9FMZAt7qJm4JoJuNBrBgqMMzkS4AjKc4zqTUVd/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" - ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" + ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 4890872ab..91efc24b6 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" - bs "gx/ipfs/QmNozJswSuwiZspexEHcQo5GMqpzM5exUGjNW6s4AAipUX/go-blockservice" - mdag "gx/ipfs/QmTGpm48qm4fUZ9E5hMXy4ZngJUYCMKu15rTMVR3BSEnPm/go-merkledag" + mdag "gx/ipfs/QmXTw4By9FMZAt7qJm4JoJuNBrBgqMMzkS4AjKc4zqTUVd/go-merkledag" + bs "gx/ipfs/QmY1fUNoXjC8sH86kyaK8BWFGaU6MmH4AJfF1w4sKjmtRZ/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - offline "gx/ipfs/QmPXcrGQQEEPswwg6YiE2WLk8qkmvncZ7zphMKKP8bXqY3/go-ipfs-exchange-offline" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" - dssync "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/sync" - blockstore "gx/ipfs/QmfUhZX9KpvJiuiziUzP2cjhRAyqHJURsPgRKn1cdDZMKa/go-ipfs-blockstore" + offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" + ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" + dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" + blockstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" ) var rand = util.NewTimeSeededRand() diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 8bccc8503..0b50c23a0 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmTGpm48qm4fUZ9E5hMXy4ZngJUYCMKu15rTMVR3BSEnPm/go-merkledag" + "gx/ipfs/QmXTw4By9FMZAt7qJm4JoJuNBrBgqMMzkS4AjKc4zqTUVd/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 0502880ff..1fcfb1ad1 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,14 +5,14 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmNozJswSuwiZspexEHcQo5GMqpzM5exUGjNW6s4AAipUX/go-blockservice" - dag "gx/ipfs/QmTGpm48qm4fUZ9E5hMXy4ZngJUYCMKu15rTMVR3BSEnPm/go-merkledag" + dag "gx/ipfs/QmXTw4By9FMZAt7qJm4JoJuNBrBgqMMzkS4AjKc4zqTUVd/go-merkledag" + bserv "gx/ipfs/QmY1fUNoXjC8sH86kyaK8BWFGaU6MmH4AJfF1w4sKjmtRZ/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - offline "gx/ipfs/QmPXcrGQQEEPswwg6YiE2WLk8qkmvncZ7zphMKKP8bXqY3/go-ipfs-exchange-offline" - ds "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore" - dsq "gx/ipfs/QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP/go-datastore/query" - blockstore "gx/ipfs/QmfUhZX9KpvJiuiziUzP2cjhRAyqHJURsPgRKn1cdDZMKa/go-ipfs-blockstore" + offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" + ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" + dsq "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" + blockstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" ) func ignoreCids(_ cid.Cid) {} From 27097d47b308f4dd810a1f3f7b06d4133999eb13 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 10 Oct 2018 14:06:57 +0100 Subject: [PATCH 2704/3817] gx: update go-buffer-pool Turns out that `pool.Put(buf)` had to *allocate* because we needed to turn `[]byte` into `interface{}`. Apparently, we've never done this correctly we just never noticed because we never really used buffer pools extensively. However, since migrating yamux to a buffer-pool backed buffer, this started showing up in allocation profiles. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@a043e5a8d0e8b8445a6e711faa593c2cee911632 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 16 ++++++++-------- namesys/namesys.go | 4 ++-- namesys/namesys_test.go | 10 +++++----- namesys/proquint.go | 2 +- namesys/publisher.go | 10 +++++----- namesys/publisher_test.go | 4 ++-- namesys/republisher/repub.go | 4 ++-- namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 6 +++--- namesys/routing.go | 10 +++++----- 14 files changed, 40 insertions(+), 40 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 705cf9fae..0b6e7840b 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" + path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index 8272c2bdb..d187021a4 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" + path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 247ea97b1..9c64ab913 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" + path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index 142dfea9c..2dae7d0f9 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" + path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 23a069569..50c0bfa90 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,19 +6,19 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" + path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" + routing "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing" + ropts "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing/options" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - mockrouting "gx/ipfs/QmQqPdGQfsfQTpRUUzgD3d2RzWHJffZsSqwopnZ2BkiezW/go-ipfs-routing/mock" - offline "gx/ipfs/QmQqPdGQfsfQTpRUUzgD3d2RzWHJffZsSqwopnZ2BkiezW/go-ipfs-routing/offline" + mockrouting "gx/ipfs/QmQ9PR61a8rwEFuFNs7JMA1QtQC9yZnBwoDn51JWXDbaTd/go-ipfs-routing/mock" + offline "gx/ipfs/QmQ9PR61a8rwEFuFNs7JMA1QtQC9yZnBwoDn51JWXDbaTd/go-ipfs-routing/offline" record "gx/ipfs/QmSb4B8ZAAj5ALe9LjfzPyF8Ma6ezC1NTnDF2JQPUJxEXb/go-libp2p-record" - routing "gx/ipfs/QmVQPj6rHdqz6dDQrjcdP36zDYLaoB7xwqRg39kx2PqqKU/go-libp2p-routing" - ropts "gx/ipfs/QmVQPj6rHdqz6dDQrjcdP36zDYLaoB7xwqRg39kx2PqqKU/go-libp2p-routing/options" - ipns "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns" - pstore "gx/ipfs/QmXEyLwySuDMXejWBu8XwdkX2WuGKk8x9jFwz8js7j72UX/go-libp2p-peerstore" - pstoremem "gx/ipfs/QmXEyLwySuDMXejWBu8XwdkX2WuGKk8x9jFwz8js7j72UX/go-libp2p-peerstore/pstoremem" + pstore "gx/ipfs/QmWtCpWB39Rzc2xTB75MKorsxNpo3TyecTEN24CJ3KVohE/go-libp2p-peerstore" + pstoremem "gx/ipfs/QmWtCpWB39Rzc2xTB75MKorsxNpo3TyecTEN24CJ3KVohE/go-libp2p-peerstore/pstoremem" + ipns "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" diff --git a/namesys/namesys.go b/namesys/namesys.go index 7da1f4c71..45dfe66e0 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,12 +6,12 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" + path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" + routing "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" - routing "gx/ipfs/QmVQPj6rHdqz6dDQrjcdP36zDYLaoB7xwqRg39kx2PqqKU/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 5ee9ebeb6..f42477d46 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,13 +7,13 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - "gx/ipfs/QmQDcPcBH8nfz3JB4K4oEvxhRmBwCrMgvG966XpExEWexf/go-unixfs" - path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" + "gx/ipfs/QmNWmxWDZjv1dMUvz3sgThydJfeNUxxXaaZptF6E9b59vQ/go-unixfs" + path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - offroute "gx/ipfs/QmQqPdGQfsfQTpRUUzgD3d2RzWHJffZsSqwopnZ2BkiezW/go-ipfs-routing/offline" - ipns "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns" - pstoremem "gx/ipfs/QmXEyLwySuDMXejWBu8XwdkX2WuGKk8x9jFwz8js7j72UX/go-libp2p-peerstore/pstoremem" + offroute "gx/ipfs/QmQ9PR61a8rwEFuFNs7JMA1QtQC9yZnBwoDn51JWXDbaTd/go-ipfs-routing/offline" + pstoremem "gx/ipfs/QmWtCpWB39Rzc2xTB75MKorsxNpo3TyecTEN24CJ3KVohE/go-libp2p-peerstore/pstoremem" + ipns "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" diff --git a/namesys/proquint.go b/namesys/proquint.go index 9647f8152..6aa38b4c7 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" + path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index a5e2eae7e..94ea8fd07 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,13 +7,13 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmQDcPcBH8nfz3JB4K4oEvxhRmBwCrMgvG966XpExEWexf/go-unixfs" - path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" + ft "gx/ipfs/QmNWmxWDZjv1dMUvz3sgThydJfeNUxxXaaZptF6E9b59vQ/go-unixfs" + path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" + routing "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - routing "gx/ipfs/QmVQPj6rHdqz6dDQrjcdP36zDYLaoB7xwqRg39kx2PqqKU/go-libp2p-routing" - ipns "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns" - pb "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns/pb" + ipns "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns" + pb "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns/pb" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dsquery "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index b55742e14..e29881004 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -8,9 +8,9 @@ import ( testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - mockrouting "gx/ipfs/QmQqPdGQfsfQTpRUUzgD3d2RzWHJffZsSqwopnZ2BkiezW/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmQ9PR61a8rwEFuFNs7JMA1QtQC9yZnBwoDn51JWXDbaTd/go-ipfs-routing/mock" dshelp "gx/ipfs/QmS73grfbWgWrNztd8Lns9GCG3jjRNDfcPYg2VYQzKDZSt/go-ipfs-ds-help" - ipns "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns" + ipns "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index e2e650c46..5adaa62db 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,12 +7,12 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" + path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - pb "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns/pb" + pb "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns/pb" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 0a3afbf0b..40ec6d910 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" + path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmU9Cf9q5TBCAC3kg74Fqr6K7DQTwa41C44YypYqB2GfR8/go-libp2p/p2p/net/mock" - pstore "gx/ipfs/QmXEyLwySuDMXejWBu8XwdkX2WuGKk8x9jFwz8js7j72UX/go-libp2p-peerstore" + pstore "gx/ipfs/QmWtCpWB39Rzc2xTB75MKorsxNpo3TyecTEN24CJ3KVohE/go-libp2p-peerstore" + mocknet "gx/ipfs/QmcmNfbQznhk66ipFiaRHmZU8DVpvDKFfHrRo9q5wsHzZP/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 18abb6897..6ed4613b8 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" - path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" + path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" - mockrouting "gx/ipfs/QmQqPdGQfsfQTpRUUzgD3d2RzWHJffZsSqwopnZ2BkiezW/go-ipfs-routing/mock" - ipns "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns" + mockrouting "gx/ipfs/QmQ9PR61a8rwEFuFNs7JMA1QtQC9yZnBwoDn51JWXDbaTd/go-ipfs-routing/mock" + ipns "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" diff --git a/namesys/routing.go b/namesys/routing.go index b65c0fa59..d61fdb9c6 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,16 +6,16 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" + path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" + routing "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - dht "gx/ipfs/QmTdMq4uYZXmGW3u6KgnpCRWjo1Y7dWjRuAaGPw7Qxqr1s/go-libp2p-kad-dht" - routing "gx/ipfs/QmVQPj6rHdqz6dDQrjcdP36zDYLaoB7xwqRg39kx2PqqKU/go-libp2p-routing" - ipns "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns" - pb "gx/ipfs/QmWhm9qS3NZdvTgAsB1cX4q9UbNu8yFybCcAMchCN88w7o/go-ipns/pb" + ipns "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns" + pb "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns/pb" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + dht "gx/ipfs/QmcZE4Q9J9YXhmKcweaMtxoLVzGSoySqLd88m6qBKFRiNy/go-libp2p-kad-dht" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) From 2bda31a93a5070433a41cff4799c0012f4d69460 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 10 Oct 2018 14:06:57 +0100 Subject: [PATCH 2705/3817] gx: update go-buffer-pool Turns out that `pool.Put(buf)` had to *allocate* because we needed to turn `[]byte` into `interface{}`. Apparently, we've never done this correctly we just never noticed because we never really used buffer pools extensively. However, since migrating yamux to a buffer-pool backed buffer, this started showing up in allocation profiles. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@54fd5775db59e6c0113851fb7556dd09adac4215 --- coreiface/dht.go | 2 +- coreiface/options/unixfs.go | 2 +- coreiface/path.go | 2 +- coreiface/swarm.go | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/coreiface/dht.go b/coreiface/dht.go index 3096c8fb7..cb3362bb9 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -5,7 +5,7 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - pstore "gx/ipfs/QmXEyLwySuDMXejWBu8XwdkX2WuGKk8x9jFwz8js7j72UX/go-libp2p-peerstore" + pstore "gx/ipfs/QmWtCpWB39Rzc2xTB75MKorsxNpo3TyecTEN24CJ3KVohE/go-libp2p-peerstore" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" ) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index b4661b793..aaed6024e 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -6,7 +6,7 @@ import ( cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - dag "gx/ipfs/QmXTw4By9FMZAt7qJm4JoJuNBrBgqMMzkS4AjKc4zqTUVd/go-merkledag" + dag "gx/ipfs/QmTpyXP1bsqJvyW5VcNmALPCb47VPJFy2T8icGASNy4ML1/go-merkledag" ) type Layout int diff --git a/coreiface/path.go b/coreiface/path.go index 9bb46b4b4..a11a46324 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmQmMu1vsgsjxyB8tzrA6ZTCTCLDLVaXMb4Q57r2v886Sx/go-path" + ipfspath "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) diff --git a/coreiface/swarm.go b/coreiface/swarm.go index d8bca395c..ba1a55698 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -5,8 +5,8 @@ import ( "errors" "time" - net "gx/ipfs/QmWUPYHpNv4YahaBYXovuEJttgfqcNcN9Gg4arhQYcRoqa/go-libp2p-net" - pstore "gx/ipfs/QmXEyLwySuDMXejWBu8XwdkX2WuGKk8x9jFwz8js7j72UX/go-libp2p-peerstore" + net "gx/ipfs/QmSTaEYUgDe1r581hxyd2u9582Hgp3KX4wGwYbRqz2u9Qh/go-libp2p-net" + pstore "gx/ipfs/QmWtCpWB39Rzc2xTB75MKorsxNpo3TyecTEN24CJ3KVohE/go-libp2p-peerstore" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol" "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" From 0c8d7140d9120dc15bbab647b57678018689d62e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 10 Oct 2018 14:06:57 +0100 Subject: [PATCH 2706/3817] gx: update go-buffer-pool Turns out that `pool.Put(buf)` had to *allocate* because we needed to turn `[]byte` into `interface{}`. Apparently, we've never done this correctly we just never noticed because we never really used buffer pools extensively. However, since migrating yamux to a buffer-pool backed buffer, this started showing up in allocation profiles. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@68253f6178b62f30ef9cba9683a0be65961e847e --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 98861cf3f..994d358bf 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmXTw4By9FMZAt7qJm4JoJuNBrBgqMMzkS4AjKc4zqTUVd/go-merkledag" + dag "gx/ipfs/QmTpyXP1bsqJvyW5VcNmALPCb47VPJFy2T8icGASNy4ML1/go-merkledag" posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" From 3ba7edf7d01d8f387122c1d0bf124ea6bfdd39ad Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 10 Oct 2018 14:06:57 +0100 Subject: [PATCH 2707/3817] gx: update go-buffer-pool Turns out that `pool.Put(buf)` had to *allocate* because we needed to turn `[]byte` into `interface{}`. Apparently, we've never done this correctly we just never noticed because we never really used buffer pools extensively. However, since migrating yamux to a buffer-pool backed buffer, this started showing up in allocation profiles. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@843897ca7c48ce43846957ac676c0ef7d4c11e6b --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index c392eee9d..76ea2a7d8 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmXTw4By9FMZAt7qJm4JoJuNBrBgqMMzkS4AjKc4zqTUVd/go-merkledag" - bserv "gx/ipfs/QmY1fUNoXjC8sH86kyaK8BWFGaU6MmH4AJfF1w4sKjmtRZ/go-blockservice" + dag "gx/ipfs/QmTpyXP1bsqJvyW5VcNmALPCb47VPJFy2T8icGASNy4ML1/go-merkledag" + bserv "gx/ipfs/QmVgTAMesq25DK9JuPVZAohx2aRkx1s7n8obmDsA2ipd1u/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 4b91fa407..3987a2dfd 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmXTw4By9FMZAt7qJm4JoJuNBrBgqMMzkS4AjKc4zqTUVd/go-merkledag" + mdag "gx/ipfs/QmTpyXP1bsqJvyW5VcNmALPCb47VPJFy2T8icGASNy4ML1/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 91efc24b6..3cd02b307 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - mdag "gx/ipfs/QmXTw4By9FMZAt7qJm4JoJuNBrBgqMMzkS4AjKc4zqTUVd/go-merkledag" - bs "gx/ipfs/QmY1fUNoXjC8sH86kyaK8BWFGaU6MmH4AJfF1w4sKjmtRZ/go-blockservice" + mdag "gx/ipfs/QmTpyXP1bsqJvyW5VcNmALPCb47VPJFy2T8icGASNy4ML1/go-merkledag" + bs "gx/ipfs/QmVgTAMesq25DK9JuPVZAohx2aRkx1s7n8obmDsA2ipd1u/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 0b50c23a0..c60f18f9b 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmXTw4By9FMZAt7qJm4JoJuNBrBgqMMzkS4AjKc4zqTUVd/go-merkledag" + "gx/ipfs/QmTpyXP1bsqJvyW5VcNmALPCb47VPJFy2T8icGASNy4ML1/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 1fcfb1ad1..1e49392b0 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmXTw4By9FMZAt7qJm4JoJuNBrBgqMMzkS4AjKc4zqTUVd/go-merkledag" - bserv "gx/ipfs/QmY1fUNoXjC8sH86kyaK8BWFGaU6MmH4AJfF1w4sKjmtRZ/go-blockservice" + dag "gx/ipfs/QmTpyXP1bsqJvyW5VcNmALPCb47VPJFy2T8icGASNy4ML1/go-merkledag" + bserv "gx/ipfs/QmVgTAMesq25DK9JuPVZAohx2aRkx1s7n8obmDsA2ipd1u/go-blockservice" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" From 89d1401a99a9a5179f26b02d9be255974b34cee0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 12 Oct 2018 16:15:40 +0100 Subject: [PATCH 2708/3817] gx: update yamux and refmt * yamux: fix memory leak. * refmt: obey the "empty" tag. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@7fa3f8c8d6978540c53b5b3d54a40e88ce1c525f --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 4 ++-- namesys/proquint.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 4 ++-- 13 files changed, 17 insertions(+), 17 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 0b6e7840b..a650f094a 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" + path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" ) type resolver interface { diff --git a/namesys/cache.go b/namesys/cache.go index d187021a4..8a96d90fd 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" + path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 9c64ab913..7aa4e24d2 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,8 +8,8 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" + path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" ) type LookupTXTFunc func(name string) (txt []string, err error) diff --git a/namesys/interface.go b/namesys/interface.go index 2dae7d0f9..c8413fbce 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" + path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 50c0bfa90..5504bdea1 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" + path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" diff --git a/namesys/namesys.go b/namesys/namesys.go index 45dfe66e0..fd3853371 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,7 +6,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" + path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" routing "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index f42477d46..30e13c72d 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,8 +7,8 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - "gx/ipfs/QmNWmxWDZjv1dMUvz3sgThydJfeNUxxXaaZptF6E9b59vQ/go-unixfs" - path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" + "gx/ipfs/QmRX6WZhMinQrQhyuwaqNHYQtNPhtBwzxKFySzNMaJmW9v/go-unixfs" + path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" offroute "gx/ipfs/QmQ9PR61a8rwEFuFNs7JMA1QtQC9yZnBwoDn51JWXDbaTd/go-ipfs-routing/offline" diff --git a/namesys/proquint.go b/namesys/proquint.go index 6aa38b4c7..c382d8a26 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,8 +7,8 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" + path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index 94ea8fd07..684caa655 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,8 +7,8 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmNWmxWDZjv1dMUvz3sgThydJfeNUxxXaaZptF6E9b59vQ/go-unixfs" - path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" + ft "gx/ipfs/QmRX6WZhMinQrQhyuwaqNHYQtNPhtBwzxKFySzNMaJmW9v/go-unixfs" + path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" routing "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 5adaa62db..2ea8d4633 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" + path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 40ec6d910..a982b2df1 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" + path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + mocknet "gx/ipfs/QmPL3AKtiaQyYpchZceXBZhZ3MSnoGqJvLZrc7fzDTTQdJ/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" pstore "gx/ipfs/QmWtCpWB39Rzc2xTB75MKorsxNpo3TyecTEN24CJ3KVohE/go-libp2p-peerstore" - mocknet "gx/ipfs/QmcmNfbQznhk66ipFiaRHmZU8DVpvDKFfHrRo9q5wsHzZP/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 6ed4613b8..3fd8f2c97 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" + path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" mockrouting "gx/ipfs/QmQ9PR61a8rwEFuFNs7JMA1QtQC9yZnBwoDn51JWXDbaTd/go-ipfs-routing/mock" diff --git a/namesys/routing.go b/namesys/routing.go index d61fdb9c6..13c554b72 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -6,16 +6,16 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" + path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" routing "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + dht "gx/ipfs/QmSteomMgXnSQxLEY5UpxmkYAd8QF9JuLLeLYBokTHxFru/go-libp2p-kad-dht" ipns "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns" pb "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns/pb" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" - dht "gx/ipfs/QmcZE4Q9J9YXhmKcweaMtxoLVzGSoySqLd88m6qBKFRiNy/go-libp2p-kad-dht" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) From f3066c9cd309e9af638a8c03d9967776254d5d65 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 12 Oct 2018 16:15:40 +0100 Subject: [PATCH 2709/3817] gx: update yamux and refmt * yamux: fix memory leak. * refmt: obey the "empty" tag. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@cb4d2fb72ce804cc5c277ac8d6b1b10a63943072 --- coreiface/options/unixfs.go | 2 +- coreiface/path.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index aaed6024e..307d618de 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -6,7 +6,7 @@ import ( cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - dag "gx/ipfs/QmTpyXP1bsqJvyW5VcNmALPCb47VPJFy2T8icGASNy4ML1/go-merkledag" + dag "gx/ipfs/QmVvNkTCx8V9Zei8xuTYTBdUXmbnDRS4iNuw1SztYyhQwQ/go-merkledag" ) type Layout int diff --git a/coreiface/path.go b/coreiface/path.go index a11a46324..d90f04aa1 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmTy6VoHV2E5baEFDXbp1xmHhxSVff5qTSrTsoXxD1eB2P/go-path" + ipfspath "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) From 07c1e752d682d3f4494d83cc392e0282b9f1535e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 12 Oct 2018 16:15:40 +0100 Subject: [PATCH 2710/3817] gx: update yamux and refmt * yamux: fix memory leak. * refmt: obey the "empty" tag. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@aabb6089a342b430a02e40796349797db12447cb --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 994d358bf..7828fdb17 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmTpyXP1bsqJvyW5VcNmALPCb47VPJFy2T8icGASNy4ML1/go-merkledag" + dag "gx/ipfs/QmVvNkTCx8V9Zei8xuTYTBdUXmbnDRS4iNuw1SztYyhQwQ/go-merkledag" posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" From 5d8c47257f7465fdc039c4a982425d415745284e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 12 Oct 2018 16:15:40 +0100 Subject: [PATCH 2711/3817] gx: update yamux and refmt * yamux: fix memory leak. * refmt: obey the "empty" tag. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@40e648173af792f2f96bbb0aebc5d04e2a9fee19 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 76ea2a7d8..a7bd364db 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmTpyXP1bsqJvyW5VcNmALPCb47VPJFy2T8icGASNy4ML1/go-merkledag" - bserv "gx/ipfs/QmVgTAMesq25DK9JuPVZAohx2aRkx1s7n8obmDsA2ipd1u/go-blockservice" + bserv "gx/ipfs/QmSU7Nx5eUHWkc9zCTiXDu3ZkdXAZdRgRGRaKM86VjGU4m/go-blockservice" + dag "gx/ipfs/QmVvNkTCx8V9Zei8xuTYTBdUXmbnDRS4iNuw1SztYyhQwQ/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 3987a2dfd..5736e5597 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmTpyXP1bsqJvyW5VcNmALPCb47VPJFy2T8icGASNy4ML1/go-merkledag" + mdag "gx/ipfs/QmVvNkTCx8V9Zei8xuTYTBdUXmbnDRS4iNuw1SztYyhQwQ/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 3cd02b307..ba83bdeb5 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - mdag "gx/ipfs/QmTpyXP1bsqJvyW5VcNmALPCb47VPJFy2T8icGASNy4ML1/go-merkledag" - bs "gx/ipfs/QmVgTAMesq25DK9JuPVZAohx2aRkx1s7n8obmDsA2ipd1u/go-blockservice" + bs "gx/ipfs/QmSU7Nx5eUHWkc9zCTiXDu3ZkdXAZdRgRGRaKM86VjGU4m/go-blockservice" + mdag "gx/ipfs/QmVvNkTCx8V9Zei8xuTYTBdUXmbnDRS4iNuw1SztYyhQwQ/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index c60f18f9b..dbd57d15e 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmTpyXP1bsqJvyW5VcNmALPCb47VPJFy2T8icGASNy4ML1/go-merkledag" + "gx/ipfs/QmVvNkTCx8V9Zei8xuTYTBdUXmbnDRS4iNuw1SztYyhQwQ/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 1e49392b0..a99bc7ef5 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmTpyXP1bsqJvyW5VcNmALPCb47VPJFy2T8icGASNy4ML1/go-merkledag" - bserv "gx/ipfs/QmVgTAMesq25DK9JuPVZAohx2aRkx1s7n8obmDsA2ipd1u/go-blockservice" + bserv "gx/ipfs/QmSU7Nx5eUHWkc9zCTiXDu3ZkdXAZdRgRGRaKM86VjGU4m/go-blockservice" + dag "gx/ipfs/QmVvNkTCx8V9Zei8xuTYTBdUXmbnDRS4iNuw1SztYyhQwQ/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" From 25e7fdbb9ea34eb633196e5345fe29003b9a4c34 Mon Sep 17 00:00:00 2001 From: Overbool Date: Wed, 10 Oct 2018 18:09:33 +0800 Subject: [PATCH 2712/3817] fix(resolve): issue #31 This commit was moved from ipfs/go-unixfs@3512ccf8d99e8e7c949b4a4d60b0667a244a9fae --- unixfs/io/resolve.go | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index 3181097f3..dbcd159d6 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -18,12 +18,7 @@ func ResolveUnixfsOnce(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, na fsn, err := ft.FSNodeFromBytes(nd.Data()) if err != nil { // Not a unixfs node, use standard object traversal code - lnk, err := nd.GetNodeLink(names[0]) - if err != nil { - return nil, nil, err - } - - return lnk, names[1:], nil + return nd.ResolveLink(names) } switch fsn.Type() { @@ -40,19 +35,7 @@ func ResolveUnixfsOnce(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, na } return out, names[1:], nil - default: - lnk, err := nd.GetNodeLink(names[0]) - if err != nil { - return nil, nil, err - } - - return lnk, names[1:], nil - } - default: - lnk, rest, err := nd.ResolveLink(names) - if err != nil { - return nil, nil, err } - return lnk, rest, nil } + return nd.ResolveLink(names) } From 02fbac0484c3adafc30042e3f420287bafa96b47 Mon Sep 17 00:00:00 2001 From: Overbool Date: Wed, 10 Oct 2018 23:54:20 +0800 Subject: [PATCH 2713/3817] fix(resolve): replace switch with if This commit was moved from ipfs/go-unixfs@b561810616c00589daf0495c78ed0778e443fccf --- unixfs/io/resolve.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go index dbcd159d6..c3dcffc24 100644 --- a/unixfs/io/resolve.go +++ b/unixfs/io/resolve.go @@ -13,16 +13,15 @@ import ( // ResolveUnixfsOnce resolves a single hop of a path through a graph in a // unixfs context. This includes handling traversing sharded directories. func ResolveUnixfsOnce(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, names []string) (*ipld.Link, []string, error) { - switch nd := nd.(type) { - case *dag.ProtoNode: - fsn, err := ft.FSNodeFromBytes(nd.Data()) + pn, ok := nd.(*dag.ProtoNode) + if ok { + fsn, err := ft.FSNodeFromBytes(pn.Data()) if err != nil { // Not a unixfs node, use standard object traversal code return nd.ResolveLink(names) } - switch fsn.Type() { - case ft.THAMTShard: + if fsn.Type() == ft.THAMTShard { rods := dag.NewReadOnlyDagService(ds) s, err := hamt.NewHamtFromDag(rods, nd) if err != nil { @@ -37,5 +36,6 @@ func ResolveUnixfsOnce(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, na return out, names[1:], nil } } + return nd.ResolveLink(names) } From 2623cc400f7d76c24dc6d3b6c5832d55780ee659 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 16 Oct 2018 12:05:13 +0100 Subject: [PATCH 2714/3817] use new ExtractPublicKey signature The new version returns an error if it fails to extract the public key, instead of just `nil, nil`. This is significantly more "go-like" and less likely to cause confusion. This commit was moved from ipfs/go-ipns@9ed4416569b9e86a2a17079a9a7f25e17a47b35d --- ipns/ipns.go | 7 ++----- ipns/record.go | 10 +++++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/ipns/ipns.go b/ipns/ipns.go index 7bab52478..f145333b1 100644 --- a/ipns/ipns.go +++ b/ipns/ipns.go @@ -73,13 +73,10 @@ func EmbedPublicKey(pk ic.PubKey, entry *pb.IpnsEntry) error { if err != nil { return err } - extracted, err := id.ExtractPublicKey() - if err != nil { + if _, err := id.ExtractPublicKey(); err != peer.ErrNoPublicKey { + // Either a *real* error or nil. return err } - if extracted != nil { - return nil - } // We failed to extract the public key from the peer ID, embed it in the // record. diff --git a/ipns/record.go b/ipns/record.go index 56e221948..eb60ce6f8 100644 --- a/ipns/record.go +++ b/ipns/record.go @@ -61,12 +61,12 @@ func (v Validator) Validate(key string, value []byte) error { } func (v Validator) getPublicKey(pid peer.ID, entry *pb.IpnsEntry) (ic.PubKey, error) { - pk, err := ExtractPublicKey(pid, entry) - if err != nil { - return nil, err - } - if pk != nil { + switch pk, err := ExtractPublicKey(pid, entry); err { + case peer.ErrNoPublicKey: + case nil: return pk, nil + default: + return nil, err } if v.KeyBook == nil { From 724c9ff3e08d4eb4ffba0a34245c489b93ecf3db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 16 Oct 2018 13:50:25 +0200 Subject: [PATCH 2715/3817] gx: update to use extracted go-ipfs-files This commit was moved from ipfs/go-unixfs@727c7d446f500f1b9cabe92a4b49b21062c10654 --- unixfs/importer/helpers/dagbuilder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index 4c897fd48..9c0dd437c 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -11,7 +11,7 @@ import ( cid "github.com/ipfs/go-cid" chunker "github.com/ipfs/go-ipfs-chunker" - files "github.com/ipfs/go-ipfs-cmdkit/files" + files "github.com/ipfs/go-ipfs-files" pi "github.com/ipfs/go-ipfs-posinfo" ipld "github.com/ipfs/go-ipld-format" ) From 60aa1853647ac4cab317726b1cdd512096719472 Mon Sep 17 00:00:00 2001 From: Overbool Date: Tue, 9 Oct 2018 15:51:34 +0800 Subject: [PATCH 2716/3817] refactor(hamt): remove child interface from hamt pkg This commit was moved from ipfs/go-unixfs@7262fe1795a120870c39265c3348f5a311c24a19 --- unixfs/hamt/hamt.go | 152 +++++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 85 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index b7ac0a2f4..dbdcac109 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -39,13 +39,20 @@ const ( HashMurmur3 uint64 = 0x22 ) +func (ds *Shard) isValueNode() bool { + if ds.key != "" && ds.val != nil { + return true + } + return false +} + // A Shard represents the HAMT. It should be initialized with NewShard(). type Shard struct { nd *dag.ProtoNode bitfield bitfield.Bitfield - children []child + children []*Shard tableSize int tableSizeLg2 int @@ -57,12 +64,10 @@ type Shard struct { maxpadlen int dserv ipld.DAGService -} -// child can either be another shard, or a leaf node value -type child interface { - Link() (*ipld.Link, error) - Label() string + // leaf node + key string + val *ipld.Link } // NewShard creates a new, empty HAMT shard with the given size. @@ -119,7 +124,7 @@ func NewHamtFromDag(dserv ipld.DAGService, nd ipld.Node) (*Shard, error) { } ds.nd = pbnd.Copy().(*dag.ProtoNode) - ds.children = make([]child, len(pbnd.Links())) + ds.children = make([]*Shard, len(pbnd.Links())) ds.bitfield.SetBytes(fsn.Data()) ds.hashFunc = fsn.HashType() ds.builder = ds.nd.CidBuilder() @@ -156,7 +161,7 @@ func (ds *Shard) Node() (ipld.Node, error) { return nil, err } - err = out.AddRawLink(ds.linkNamePrefix(i)+ch.Label(), clnk) + err = out.AddRawLink(ds.linkNamePrefix(i)+ch.key, clnk) if err != nil { return nil, err } @@ -188,26 +193,14 @@ func (ds *Shard) Node() (ipld.Node, error) { return out, nil } -type shardValue struct { - key string - val *ipld.Link -} - -// Link returns a link to this node -func (sv *shardValue) Link() (*ipld.Link, error) { - return sv.val, nil -} +func (ds *Shard) makeShardValue(lnk *ipld.Link) *Shard { + lnk2 := *lnk + s, _ := makeShard(ds.dserv, ds.tableSize) -func (sv *shardValue) Label() string { - return sv.key -} + s.key = lnk.Name[ds.maxpadlen:] + s.val = &lnk2 -func (ds *Shard) makeShardValue(lnk *ipld.Link) *shardValue { - lnk2 := *lnk - return &shardValue{ - key: lnk.Name[ds.maxpadlen:], - val: &lnk2, - } + return s } func hash(val []byte) []byte { @@ -216,12 +209,6 @@ func hash(val []byte) []byte { return h.Sum(nil) } -// Label for Shards is the empty string, this is used to differentiate them from -// value entries -func (ds *Shard) Label() string { - return "" -} - // Set sets 'name' = nd in the HAMT func (ds *Shard) Set(ctx context.Context, name string, nd ipld.Node) error { hv := &hashBits{b: hash([]byte(name))} @@ -250,7 +237,7 @@ func (ds *Shard) Find(ctx context.Context, name string) (*ipld.Link, error) { hv := &hashBits{b: hash([]byte(name))} var out *ipld.Link - err := ds.getValue(ctx, hv, name, func(sv *shardValue) error { + err := ds.getValue(ctx, hv, name, func(sv *Shard) error { out = sv.val return nil }) @@ -282,7 +269,7 @@ func (ds *Shard) childLinkType(lnk *ipld.Link) (linkType, error) { // getChild returns the i'th child of this shard. If it is cached in the // children array, it will return it from there. Otherwise, it loads the child // node from disk. -func (ds *Shard) getChild(ctx context.Context, i int) (child, error) { +func (ds *Shard) getChild(ctx context.Context, i int) (*Shard, error) { if i >= len(ds.children) || i < 0 { return nil, fmt.Errorf("invalid index passed to getChild (likely corrupt bitfield)") } @@ -301,14 +288,14 @@ func (ds *Shard) getChild(ctx context.Context, i int) (child, error) { // loadChild reads the i'th child node of this shard from disk and returns it // as a 'child' interface -func (ds *Shard) loadChild(ctx context.Context, i int) (child, error) { +func (ds *Shard) loadChild(ctx context.Context, i int) (*Shard, error) { lnk := ds.nd.Links()[i] lnkLinkType, err := ds.childLinkType(lnk) if err != nil { return nil, err } - var c child + var c *Shard if lnkLinkType == shardLink { nd, err := lnk.GetNode(ctx, ds.dserv) if err != nil { @@ -328,12 +315,16 @@ func (ds *Shard) loadChild(ctx context.Context, i int) (child, error) { return c, nil } -func (ds *Shard) setChild(i int, c child) { +func (ds *Shard) setChild(i int, c *Shard) { ds.children[i] = c } // Link returns a merklelink to this shard node func (ds *Shard) Link() (*ipld.Link, error) { + if ds.isValueNode() { + return ds.val, nil + } + nd, err := ds.Node() if err != nil { return nil, err @@ -356,12 +347,12 @@ func (ds *Shard) insertChild(idx int, key string, lnk *ipld.Link) error { ds.bitfield.SetBit(idx) lnk.Name = ds.linkNamePrefix(idx) + key - sv := &shardValue{ + sv := &Shard{ key: key, val: lnk, } - ds.children = append(ds.children[:i], append([]child{sv}, ds.children[i:]...)...) + ds.children = append(ds.children[:i], append([]*Shard{sv}, ds.children[i:]...)...) ds.nd.SetLinks(append(ds.nd.Links()[:i], append([]*ipld.Link{nil}, ds.nd.Links()[i:]...)...)) return nil } @@ -380,7 +371,7 @@ func (ds *Shard) rmChild(i int) error { return nil } -func (ds *Shard) getValue(ctx context.Context, hv *hashBits, key string, cb func(*shardValue) error) error { +func (ds *Shard) getValue(ctx context.Context, hv *hashBits, key string, cb func(*Shard) error) error { idx := hv.Next(ds.tableSizeLg2) if ds.bitfield.Bit(int(idx)) { cindex := ds.indexForBitPos(idx) @@ -390,13 +381,12 @@ func (ds *Shard) getValue(ctx context.Context, hv *hashBits, key string, cb func return err } - switch child := child.(type) { - case *Shard: - return child.getValue(ctx, hv, key, cb) - case *shardValue: + if child.isValueNode() { if child.key == key { return cb(child) } + } else { + return child.getValue(ctx, hv, key, cb) } } @@ -408,7 +398,7 @@ func (ds *Shard) EnumLinks(ctx context.Context) ([]*ipld.Link, error) { var links []*ipld.Link var setlk sync.Mutex - getLinks := makeAsyncTrieGetLinks(ds.dserv, func(sv *shardValue) error { + getLinks := makeAsyncTrieGetLinks(ds.dserv, func(sv *Shard) error { lnk := sv.val lnk.Name = sv.key setlk.Lock() @@ -425,7 +415,7 @@ func (ds *Shard) EnumLinks(ctx context.Context) ([]*ipld.Link, error) { // ForEachLink walks the Shard and calls the given function. func (ds *Shard) ForEachLink(ctx context.Context, f func(*ipld.Link) error) error { - return ds.walkTrie(ctx, func(sv *shardValue) error { + return ds.walkTrie(ctx, func(sv *Shard) error { lnk := sv.val lnk.Name = sv.key @@ -436,7 +426,7 @@ func (ds *Shard) ForEachLink(ctx context.Context, f func(*ipld.Link) error) erro // makeAsyncTrieGetLinks builds a getLinks function that can be used with EnumerateChildrenAsync // to iterate a HAMT shard. It takes an IPLD Dag Service to fetch nodes, and a call back that will get called // on all links to leaf nodes in a HAMT tree, so they can be collected for an EnumLinks operation -func makeAsyncTrieGetLinks(dagService ipld.DAGService, onShardValue func(*shardValue) error) dag.GetLinks { +func makeAsyncTrieGetLinks(dagService ipld.DAGService, onShardValue func(shard *Shard) error) dag.GetLinks { return func(ctx context.Context, currentCid cid.Cid) ([]*ipld.Link, error) { node, err := dagService.Get(ctx, currentCid) @@ -471,25 +461,21 @@ func makeAsyncTrieGetLinks(dagService ipld.DAGService, onShardValue func(*shardV } } -func (ds *Shard) walkTrie(ctx context.Context, cb func(*shardValue) error) error { +func (ds *Shard) walkTrie(ctx context.Context, cb func(*Shard) error) error { for idx := range ds.children { c, err := ds.getChild(ctx, idx) if err != nil { return err } - switch c := c.(type) { - case *shardValue: + if c.isValueNode() { if err := cb(c); err != nil { return err } - - case *Shard: + } else { if err := c.walkTrie(ctx, cb); err != nil { return err } - default: - return fmt.Errorf("unexpected child type: %#v", c) } } return nil @@ -497,7 +483,6 @@ func (ds *Shard) walkTrie(ctx context.Context, cb func(*shardValue) error) error func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val *ipld.Link) error { idx := hv.Next(ds.tableSizeLg2) - if !ds.bitfield.Bit(idx) { return ds.insertChild(idx, key, val) } @@ -509,34 +494,7 @@ func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val return err } - switch child := child.(type) { - case *Shard: - err := child.modifyValue(ctx, hv, key, val) - if err != nil { - return err - } - - if val == nil { - switch len(child.children) { - case 0: - // empty sub-shard, prune it - // Note: this shouldnt normally ever happen - // in the event of another implementation creates flawed - // structures, this will help to normalize them. - ds.bitfield.UnsetBit(idx) - return ds.rmChild(cindex) - case 1: - nchild, ok := child.children[0].(*shardValue) - if ok { - // sub-shard with a single value element, collapse it - ds.setChild(cindex, nchild) - } - return nil - } - } - - return nil - case *shardValue: + if child.isValueNode() { if child.key == key { // value modification if val == nil { @@ -575,8 +533,32 @@ func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val ds.setChild(cindex, ns) return nil - default: - return fmt.Errorf("unexpected type for child: %#v", child) + } else { + err := child.modifyValue(ctx, hv, key, val) + if err != nil { + return err + } + + if val == nil { + switch len(child.children) { + case 0: + // empty sub-shard, prune it + // Note: this shouldnt normally ever happen + // in the event of another implementation creates flawed + // structures, this will help to normalize them. + ds.bitfield.UnsetBit(idx) + return ds.rmChild(cindex) + case 1: + nchild := child.children[0] + if nchild.isValueNode() { + // sub-shard with a single value element, collapse it + ds.setChild(cindex, nchild) + } + return nil + } + } + + return nil } } From db25253b47cb6eae894da91dabe71182e5092c44 Mon Sep 17 00:00:00 2001 From: Overbool Date: Tue, 16 Oct 2018 17:03:30 +0800 Subject: [PATCH 2717/3817] fix(hamt): modify isValueNode This commit was moved from ipfs/go-unixfs@9e75a2ecf857b646115dba5b88d0857336a4b4a6 --- unixfs/hamt/hamt.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index dbdcac109..e4c8ff839 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -40,10 +40,7 @@ const ( ) func (ds *Shard) isValueNode() bool { - if ds.key != "" && ds.val != nil { - return true - } - return false + return ds.key != "" && ds.val != nil } // A Shard represents the HAMT. It should be initialized with NewShard(). From 4804cc97b89142c8f537f9cbe537d21f9078f76f Mon Sep 17 00:00:00 2001 From: Overbool Date: Tue, 16 Oct 2018 18:25:22 +0800 Subject: [PATCH 2718/3817] fix(hamt): add error in makeShardValue This commit was moved from ipfs/go-unixfs@3cc73ee497524c938f0eb6c5a23de0a04d7f48fc --- unixfs/hamt/hamt.go | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index e4c8ff839..0b474289b 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -190,14 +190,17 @@ func (ds *Shard) Node() (ipld.Node, error) { return out, nil } -func (ds *Shard) makeShardValue(lnk *ipld.Link) *Shard { +func (ds *Shard) makeShardValue(lnk *ipld.Link) (*Shard, error) { lnk2 := *lnk - s, _ := makeShard(ds.dserv, ds.tableSize) + s, err := makeShard(ds.dserv, ds.tableSize) + if err != nil { + return nil, err + } s.key = lnk.Name[ds.maxpadlen:] s.val = &lnk2 - return s + return s, nil } func hash(val []byte) []byte { @@ -305,7 +308,11 @@ func (ds *Shard) loadChild(ctx context.Context, i int) (*Shard, error) { c = cds } else { - c = ds.makeShardValue(lnk) + s, err := ds.makeShardValue(lnk) + if err != nil { + return nil, err + } + c = s } ds.children[i] = c @@ -447,8 +454,11 @@ func makeAsyncTrieGetLinks(dagService ipld.DAGService, onShardValue func(shard * if lnkLinkType == shardLink { childShards = append(childShards, lnk) } else { - sv := directoryShard.makeShardValue(lnk) - err := onShardValue(sv) + sv, err := directoryShard.makeShardValue(lnk) + if err != nil { + return nil, err + } + err = onShardValue(sv) if err != nil { return nil, err } From a8bae11046745cf6446295ad58f1dbfd5f1b221e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 16 Oct 2018 11:41:00 +0200 Subject: [PATCH 2719/3817] namesys: review fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@ea466fec47fdfef343e1a05fc5e43a54c2fabd47 --- namesys/base.go | 16 +++++++++------- namesys/dns.go | 1 + namesys/namesys.go | 18 +++++++++++++++--- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 56cfd03a2..508847ac3 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -1,12 +1,12 @@ package namesys import ( + "context" "strings" "time" - context "context" - opts "github.com/ipfs/go-ipfs/namesys/opts" + path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" ) @@ -40,13 +40,10 @@ func resolve(ctx context.Context, r resolver, name string, options opts.ResolveO return p, err } -//TODO: -// - better error handling -// - select on writes func resolveAsync(ctx context.Context, r resolver, name string, options opts.ResolveOpts, prefix string) <-chan Result { resCh := r.resolveOnceAsync(ctx, name, options) depth := options.Depth - outCh := make(chan Result) + outCh := make(chan Result, 1) go func() { defer close(outCh) @@ -97,8 +94,13 @@ func resolveAsync(ctx context.Context, r resolver, name string, options opts.Res break } - outCh <- res + select { + case outCh <- res: + case <-ctx.Done(): + return + } case <-ctx.Done(): + return } if resCh == nil && subCh == nil { return diff --git a/namesys/dns.go b/namesys/dns.go index 81eef07da..f4d37e654 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -7,6 +7,7 @@ import ( "strings" opts "github.com/ipfs/go-ipfs/namesys/opts" + isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" ) diff --git a/namesys/namesys.go b/namesys/namesys.go index 2e71b3003..83cd7dbcc 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,9 +5,10 @@ import ( "strings" "time" - opts "github.com/ipfs/go-ipfs/namesys/opts" path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + opts "github.com/ipfs/go-ipfs/namesys/opts" + routing "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" @@ -138,10 +139,21 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. // Attach rest of the path if len(segments) > 3 { - p, _ = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) + p, err := path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) + if err != nil { + select { + case out <- onceResult{value: p, err: err}: + case <-ctx.Done(): + } + return + } } - out <- onceResult{value: p, err: res.err} + select { + case out <- onceResult{value: p, ttl: res.ttl, err: res.err}: + case <-ctx.Done(): + return + } case <-ctx.Done(): return } From 6aaeb7276d4aa93de22390a6abf730231d75bbf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 16 Oct 2018 11:41:00 +0200 Subject: [PATCH 2720/3817] namesys: review fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@f660979841b69c6f91ff258b2eb90695dae98e75 --- coreiface/name.go | 1 - 1 file changed, 1 deletion(-) diff --git a/coreiface/name.go b/coreiface/name.go index 14127ac27..782f68351 100644 --- a/coreiface/name.go +++ b/coreiface/name.go @@ -2,7 +2,6 @@ package iface import ( "context" - "errors" options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" From 6e882475240a0cf4454828cb9e3b7ec196a92106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 16 Oct 2018 16:35:31 +0200 Subject: [PATCH 2721/3817] namesys: drop prefix args MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@5f1e0f0ba3aa670ce3b6d2767fe89b90426b605c --- namesys/base.go | 10 +++++----- namesys/dns.go | 4 ++-- namesys/ipns_resolver_validation_test.go | 13 +++++++------ namesys/namesys.go | 4 ++-- namesys/proquint.go | 5 +++-- namesys/routing.go | 7 ++++--- 6 files changed, 23 insertions(+), 20 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 508847ac3..1906bdf5d 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -21,14 +21,14 @@ type resolver interface { } // resolve is a helper for implementing Resolver.ResolveN using resolveOnce. -func resolve(ctx context.Context, r resolver, name string, options opts.ResolveOpts, prefix string) (path.Path, error) { +func resolve(ctx context.Context, r resolver, name string, options opts.ResolveOpts) (path.Path, error) { ctx, cancel := context.WithCancel(ctx) defer cancel() err := ErrResolveFailed var p path.Path - resCh := resolveAsync(ctx, r, name, options, prefix) + resCh := resolveAsync(ctx, r, name, options) for res := range resCh { p, err = res.Path, res.Err @@ -40,7 +40,7 @@ func resolve(ctx context.Context, r resolver, name string, options opts.ResolveO return p, err } -func resolveAsync(ctx context.Context, r resolver, name string, options opts.ResolveOpts, prefix string) <-chan Result { +func resolveAsync(ctx context.Context, r resolver, name string, options opts.ResolveOpts) <-chan Result { resCh := r.resolveOnceAsync(ctx, name, options) depth := options.Depth outCh := make(chan Result, 1) @@ -86,8 +86,8 @@ func resolveAsync(ctx context.Context, r resolver, name string, options opts.Res subCtx, cancelSub = context.WithCancel(ctx) defer cancelSub() - p := strings.TrimPrefix(res.value.String(), prefix) - subCh = resolveAsync(subCtx, r, p, subopts, prefix) + p := strings.TrimPrefix(res.value.String(), ipnsPrefix) + subCh = resolveAsync(subCtx, r, p, subopts) case res, ok := <-subCh: if !ok { subCh = nil diff --git a/namesys/dns.go b/namesys/dns.go index f4d37e654..d3f9e0956 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -28,12 +28,12 @@ func NewDNSResolver() *DNSResolver { // Resolve implements Resolver. func (r *DNSResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) { - return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/") + return resolve(ctx, r, name, opts.ProcessOpts(options)) } // ResolveAsync implements Resolver. func (r *DNSResolver) ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result { - return resolveAsync(ctx, r, name, opts.ProcessOpts(options), "/ipns/") + return resolveAsync(ctx, r, name, opts.ProcessOpts(options)) } type lookupRes struct { diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index e12841421..5bcef4e14 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,9 +5,10 @@ import ( "testing" "time" - opts "github.com/ipfs/go-ipfs/namesys/opts" path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + opts "github.com/ipfs/go-ipfs/namesys/opts" + testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" routing "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing" @@ -57,7 +58,7 @@ func TestResolverValidation(t *testing.T) { } // Resolve entry - resp, err := resolve(ctx, resolver, id.Pretty(), opts.DefaultResolveOpts(), "/ipns/") + resp, err := resolve(ctx, resolver, id.Pretty(), opts.DefaultResolveOpts()) if err != nil { t.Fatal(err) } @@ -77,7 +78,7 @@ func TestResolverValidation(t *testing.T) { } // Record should fail validation because entry is expired - _, err = resolve(ctx, resolver, id.Pretty(), opts.DefaultResolveOpts(), "/ipns/") + _, err = resolve(ctx, resolver, id.Pretty(), opts.DefaultResolveOpts()) if err == nil { t.Fatal("ValidateIpnsRecord should have returned error") } @@ -99,7 +100,7 @@ func TestResolverValidation(t *testing.T) { // Record should fail validation because public key defined by // ipns path doesn't match record signature - _, err = resolve(ctx, resolver, id2.Pretty(), opts.DefaultResolveOpts(), "/ipns/") + _, err = resolve(ctx, resolver, id2.Pretty(), opts.DefaultResolveOpts()) if err == nil { t.Fatal("ValidateIpnsRecord should have failed signature verification") } @@ -117,7 +118,7 @@ func TestResolverValidation(t *testing.T) { // Record should fail validation because public key is not available // in peer store or on network - _, err = resolve(ctx, resolver, id3.Pretty(), opts.DefaultResolveOpts(), "/ipns/") + _, err = resolve(ctx, resolver, id3.Pretty(), opts.DefaultResolveOpts()) if err == nil { t.Fatal("ValidateIpnsRecord should have failed because public key was not found") } @@ -132,7 +133,7 @@ func TestResolverValidation(t *testing.T) { // public key is available in the peer store by looking it up in // the DHT, which causes the DHT to fetch it and cache it in the // peer store - _, err = resolve(ctx, resolver, id3.Pretty(), opts.DefaultResolveOpts(), "/ipns/") + _, err = resolve(ctx, resolver, id3.Pretty(), opts.DefaultResolveOpts()) if err != nil { t.Fatal(err) } diff --git a/namesys/namesys.go b/namesys/namesys.go index 83cd7dbcc..50d302079 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -62,7 +62,7 @@ func (ns *mpns) Resolve(ctx context.Context, name string, options ...opts.Resolv return path.ParsePath("/ipfs/" + name) } - return resolve(ctx, ns, name, opts.ProcessOpts(options), "/ipns/") + return resolve(ctx, ns, name, opts.ProcessOpts(options)) } func (ns *mpns) ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result { @@ -79,7 +79,7 @@ func (ns *mpns) ResolveAsync(ctx context.Context, name string, options ...opts.R return res } - return resolveAsync(ctx, ns, name, opts.ProcessOpts(options), "/ipns/") + return resolveAsync(ctx, ns, name, opts.ProcessOpts(options)) } // resolveOnce implements resolver. diff --git a/namesys/proquint.go b/namesys/proquint.go index 0caaf9497..894579313 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,16 +4,17 @@ import ( "context" "errors" - opts "github.com/ipfs/go-ipfs/namesys/opts" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + + opts "github.com/ipfs/go-ipfs/namesys/opts" ) type ProquintResolver struct{} // Resolve implements Resolver. func (r *ProquintResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) { - return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/") + return resolve(ctx, r, name, opts.ProcessOpts(options)) } // resolveOnce implements resolver. Decodes the proquint string. diff --git a/namesys/routing.go b/namesys/routing.go index ef7e376e6..25daafff4 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,9 +5,10 @@ import ( "strings" "time" - opts "github.com/ipfs/go-ipfs/namesys/opts" path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + opts "github.com/ipfs/go-ipfs/namesys/opts" + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" routing "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" @@ -39,12 +40,12 @@ func NewIpnsResolver(route routing.ValueStore) *IpnsResolver { // Resolve implements Resolver. func (r *IpnsResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) { - return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/") + return resolve(ctx, r, name, opts.ProcessOpts(options)) } // ResolveAsync implements Resolver. func (r *IpnsResolver) ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result { - return resolveAsync(ctx, r, name, opts.ProcessOpts(options), "/ipns/") + return resolveAsync(ctx, r, name, opts.ProcessOpts(options)) } // resolveOnce implements resolver. Uses the IPFS routing system to From 183798effb213f1913b8c3702a89b5a026077e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 16 Oct 2018 16:35:31 +0200 Subject: [PATCH 2722/3817] namesys: drop prefix args MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@1308d71ad0a2b782fd9b7f481ffe7789b30b8a29 --- coreiface/name.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/name.go b/coreiface/name.go index 782f68351..a02bc0787 100644 --- a/coreiface/name.go +++ b/coreiface/name.go @@ -40,7 +40,7 @@ type NameAPI interface { // Search is a version of Resolve which outputs paths as they are discovered, // reducing the time to first entry // - // Note that by default only the last path returned before the channel closes - // can be considered 'safe'. + // Note: by default, all paths read from the channel are considered unsafe, + // except the latest (last path in channel read buffer). Search(ctx context.Context, name string, opts ...options.NameResolveOption) (<-chan IpnsResult, error) } From 6fa3260a83b1442bfaa93b9296b6391666e9000c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 16 Oct 2018 16:37:15 +0200 Subject: [PATCH 2723/3817] namesys: allow non /ipfs paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@4d118239ef2517bc657188ae0d5737c5ea9dafe9 --- namesys/base.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/base.go b/namesys/base.go index 1906bdf5d..a523a10bf 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -63,7 +63,7 @@ func resolveAsync(ctx context.Context, r resolver, name string, options opts.Res return } log.Debugf("resolved %s to %s", name, res.value.String()) - if strings.HasPrefix(res.value.String(), "/ipfs/") { + if !strings.HasPrefix(res.value.String(), ipnsPrefix) { outCh <- Result{Path: res.value} break } From 54896c36516a84d4c406afec9cc8e8d5b6e8b35a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 16 Oct 2018 16:53:45 +0200 Subject: [PATCH 2724/3817] namesys: avoid defer in loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@6474166878cadc9da48210e53b50f157dc14e451 --- namesys/base.go | 15 +++++++++++++-- namesys/namesys.go | 4 ++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index a523a10bf..064286ab4 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -60,6 +60,9 @@ func resolveAsync(ctx context.Context, r resolver, name string, options opts.Res if res.err != nil { outCh <- Result{Err: res.err} + if cancelSub != nil { + cancelSub() + } return } log.Debugf("resolved %s to %s", name, res.value.String()) @@ -79,12 +82,11 @@ func resolveAsync(ctx context.Context, r resolver, name string, options opts.Res } var subCtx context.Context - if subCh != nil { + if cancelSub != nil { // Cancel previous recursive resolve since it won't be used anyways cancelSub() } subCtx, cancelSub = context.WithCancel(ctx) - defer cancelSub() p := strings.TrimPrefix(res.value.String(), ipnsPrefix) subCh = resolveAsync(subCtx, r, p, subopts) @@ -97,12 +99,21 @@ func resolveAsync(ctx context.Context, r resolver, name string, options opts.Res select { case outCh <- res: case <-ctx.Done(): + if cancelSub != nil { + cancelSub() + } return } case <-ctx.Done(): + if cancelSub != nil { + cancelSub() + } return } if resCh == nil && subCh == nil { + if cancelSub != nil { + cancelSub() + } return } } diff --git a/namesys/namesys.go b/namesys/namesys.go index 50d302079..674146e57 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -86,8 +86,8 @@ func (ns *mpns) ResolveAsync(ctx context.Context, name string, options ...opts.R func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { out := make(chan onceResult, 1) - if !strings.HasPrefix(name, "/ipns/") { - name = "/ipns/" + name + if !strings.HasPrefix(name, ipnsPrefix) { + name = ipnsPrefix + name } segments := strings.SplitN(name, "/", 4) if len(segments) < 3 || segments[0] != "" { From 91e92b9527e630055bba19195a72b722fe14ff19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 16 Oct 2018 17:45:13 +0200 Subject: [PATCH 2725/3817] namesys: select on output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@b3d0160412ab6f3460d3822d8919afbc36f7f85d --- namesys/base.go | 37 +++++++++++++++++-------------------- namesys/dns.go | 10 ++-------- namesys/namesys.go | 19 +++++++++---------- namesys/routing.go | 20 ++++---------------- 4 files changed, 32 insertions(+), 54 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 064286ab4..28bc87dad 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -49,6 +49,11 @@ func resolveAsync(ctx context.Context, r resolver, name string, options opts.Res defer close(outCh) var subCh <-chan Result var cancelSub context.CancelFunc + defer func() { + if cancelSub != nil { + cancelSub() + } + }() for { select { @@ -59,20 +64,17 @@ func resolveAsync(ctx context.Context, r resolver, name string, options opts.Res } if res.err != nil { - outCh <- Result{Err: res.err} - if cancelSub != nil { - cancelSub() - } + emitResult(ctx, outCh, Result{Err: res.err}) return } log.Debugf("resolved %s to %s", name, res.value.String()) if !strings.HasPrefix(res.value.String(), ipnsPrefix) { - outCh <- Result{Path: res.value} + emitResult(ctx, outCh, Result{Path: res.value}) break } if depth == 1 { - outCh <- Result{Path: res.value, Err: ErrResolveRecursion} + emitResult(ctx, outCh, Result{Path: res.value, Err: ErrResolveRecursion}) break } @@ -87,6 +89,7 @@ func resolveAsync(ctx context.Context, r resolver, name string, options opts.Res cancelSub() } subCtx, cancelSub = context.WithCancel(ctx) + _ = cancelSub p := strings.TrimPrefix(res.value.String(), ipnsPrefix) subCh = resolveAsync(subCtx, r, p, subopts) @@ -96,27 +99,21 @@ func resolveAsync(ctx context.Context, r resolver, name string, options opts.Res break } - select { - case outCh <- res: - case <-ctx.Done(): - if cancelSub != nil { - cancelSub() - } - return - } + emitResult(ctx, outCh, res) case <-ctx.Done(): - if cancelSub != nil { - cancelSub() - } return } if resCh == nil && subCh == nil { - if cancelSub != nil { - cancelSub() - } return } } }() return outCh } + +func emitResult(ctx context.Context, outCh chan<- Result, r Result) { + select { + case outCh <- r: + case <-ctx.Done(): + } +} diff --git a/namesys/dns.go b/namesys/dns.go index d3f9e0956..bd62b7d22 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -80,10 +80,7 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options } if subRes.error == nil { p, err := appendPath(subRes.path) - select { - case out <- onceResult{value: p, err: err}: - case <-ctx.Done(): - } + emitOnceResult(ctx, out, onceResult{value: p, err: err}) return } case rootRes, ok := <-rootChan: @@ -93,10 +90,7 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options } if rootRes.error == nil { p, err := appendPath(rootRes.path) - select { - case out <- onceResult{value: p, err: err}: - case <-ctx.Done(): - } + emitOnceResult(ctx, out, onceResult{value: p, err: err}) } case <-ctx.Done(): return diff --git a/namesys/namesys.go b/namesys/namesys.go index 674146e57..aa37a93fe 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -141,19 +141,11 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. if len(segments) > 3 { p, err := path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) if err != nil { - select { - case out <- onceResult{value: p, err: err}: - case <-ctx.Done(): - } - return + emitOnceResult(ctx, out, onceResult{value: p, ttl: res.ttl, err: err}) } } - select { - case out <- onceResult{value: p, ttl: res.ttl, err: res.err}: - case <-ctx.Done(): - return - } + emitOnceResult(ctx, out, onceResult{value: p, ttl: res.ttl, err: res.err}) case <-ctx.Done(): return } @@ -163,6 +155,13 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. return out } +func emitOnceResult(ctx context.Context, outCh chan<- onceResult, r onceResult) { + select { + case outCh <- r: + case <-ctx.Done(): + } +} + // Publish implements Publisher func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error { return ns.PublishWithEOL(ctx, name, value, time.Now().Add(DefaultRecordTTL)) diff --git a/namesys/routing.go b/namesys/routing.go index 25daafff4..76aa86034 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -112,10 +112,7 @@ func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, option err = proto.Unmarshal(val, entry) if err != nil { log.Debugf("RoutingResolver: could not unmarshal value for name %s: %s", name, err) - select { - case out <- onceResult{err: err}: - case <-ctx.Done(): - } + emitOnceResult(ctx, out, onceResult{err: err}) return } @@ -129,10 +126,7 @@ func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, option // Not a multihash, probably a new style record p, err = path.ParsePath(string(entry.GetValue())) if err != nil { - select { - case out <- onceResult{err: err}: - case <-ctx.Done(): - } + emitOnceResult(ctx, out, onceResult{err: err}) return } } @@ -154,17 +148,11 @@ func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, option } default: log.Errorf("encountered error when parsing EOL: %s", err) - select { - case out <- onceResult{err: err}: - case <-ctx.Done(): - } + emitOnceResult(ctx, out, onceResult{err: err}) return } - select { - case out <- onceResult{value: p, ttl: ttl}: - case <-ctx.Done(): - } + emitOnceResult(ctx, out, onceResult{value: p, ttl: ttl}) case <-ctx.Done(): return } From bdbaca61fa7bbc195fa51cb919ef030b1a9f82a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 18 Oct 2018 10:16:31 +0200 Subject: [PATCH 2726/3817] gx: update to use extracted go-ipfs-files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@13aa36cfc0d055f8be28f8fd4230982da760cab8 --- namesys/namesys_test.go | 2 +- namesys/publisher.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 30e13c72d..8be28a7d3 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,7 +7,7 @@ import ( "time" opts "github.com/ipfs/go-ipfs/namesys/opts" - "gx/ipfs/QmRX6WZhMinQrQhyuwaqNHYQtNPhtBwzxKFySzNMaJmW9v/go-unixfs" + "gx/ipfs/QmWE6Ftsk98cG2MTVgH4wJT8VP2nL9TuBkYTrz9GSqcsh5/go-unixfs" path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" diff --git a/namesys/publisher.go b/namesys/publisher.go index 684caa655..cff6e0e3d 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,7 +7,7 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmRX6WZhMinQrQhyuwaqNHYQtNPhtBwzxKFySzNMaJmW9v/go-unixfs" + ft "gx/ipfs/QmWE6Ftsk98cG2MTVgH4wJT8VP2nL9TuBkYTrz9GSqcsh5/go-unixfs" path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" routing "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing" From 7af2ed03ee8171a3a4bf3d943fc50953f5390acd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 18 Oct 2018 10:16:31 +0200 Subject: [PATCH 2727/3817] gx: update to use extracted go-ipfs-files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@a945dc346666180846b543c15cf1a7cb3d25d7bd --- coreiface/unixfs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index c622e210e..078d648bc 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -5,7 +5,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - files "gx/ipfs/QmSP88ryZkHSRn1fnngAaV2Vcn63WUJzAavnRM9CVdU1Ky/go-ipfs-cmdkit/files" + files "gx/ipfs/QmZMWMvWMVKCbHetJ4RgndbuEF1io2UpUxwQwtNjtYPzSC/go-ipfs-files" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) From e0ff2439719bcd359efca38f44b86fedb1929813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 18 Oct 2018 15:02:05 +0200 Subject: [PATCH 2728/3817] namesys: doc on emitResult MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@81709d0a2f5afacc37a2d8c7e98b8cb5f2105fa6 --- namesys/base.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/namesys/base.go b/namesys/base.go index 28bc87dad..f90e8add1 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -99,6 +99,8 @@ func resolveAsync(ctx context.Context, r resolver, name string, options opts.Res break } + // We don't bother returning here in case of context timeout as there is + // no good reason to do that, and we may still be able to emit a result emitResult(ctx, outCh, res) case <-ctx.Done(): return From e10f78b7c555bad18926d523049dce92bc3bf778 Mon Sep 17 00:00:00 2001 From: Overbool Date: Fri, 19 Oct 2018 13:29:29 +0800 Subject: [PATCH 2729/3817] feat(fsnode): add type helper This commit was moved from ipfs/go-mfs@c9235a35b096b9862f98eacccdd60b4fc3459dca --- mfs/mfs_test.go | 37 +++++++++++++++++++++++++++++++++++++ mfs/system.go | 10 ++++++++++ 2 files changed, 47 insertions(+) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index e840f6c06..dc417b9d7 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -1182,3 +1182,40 @@ func TestTruncateAndWrite(t *testing.T) { } } } + +func TestIsDir(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ds, rt := setupRoot(ctx, t) + + dir := rt.GetDirectory() + + nd := dag.NodeWithData(ft.FolderPBData()) + di, err := NewDirectory(ctx, "test", nd, dir, ds) + if err != nil { + t.Fatal(err) + } + ret := IsDir(di) + if !ret { + t.Fatal("FSNode type should be dir, but not") + } +} + +func TestIsFile(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ds, rt := setupRoot(ctx, t) + + dir := rt.GetDirectory() + + nd := dag.NodeWithData(ft.FilePBData(nil, 0)) + fi, err := NewFile("test", nd, dir, ds) + if err != nil { + t.Fatal(err) + } + + ret := IsFile(fi) + if !ret { + t.Fatal("FSNode type should be file, but not") + } +} diff --git a/mfs/system.go b/mfs/system.go index cc7e65d3a..704e5b57f 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -48,6 +48,16 @@ type FSNode interface { Type() NodeType } +// IsDir checks whether the FSNode is dir type +func IsDir(fsn FSNode) bool { + return fsn.Type() == TDir +} + +// IsFile checks whether the FSNode is file type +func IsFile(fsn FSNode) bool { + return fsn.Type() == TFile +} + // Root represents the root of a filesystem tree. type Root struct { From 17e26a71e253fd9e5b78b53ec2e25397aca17a1b Mon Sep 17 00:00:00 2001 From: Overbool Date: Sat, 20 Oct 2018 09:52:09 +0800 Subject: [PATCH 2730/3817] test(fsnode): modify test This commit was moved from ipfs/go-mfs@8c63df0e0438669ed1d70a10216f813d289ff605 --- mfs/mfs_test.go | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index dc417b9d7..fd9990bdd 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -1183,15 +1183,14 @@ func TestTruncateAndWrite(t *testing.T) { } } -func TestIsDir(t *testing.T) { +func TestFSNodeType(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ds, rt := setupRoot(ctx, t) - dir := rt.GetDirectory() - + // check for IsDir nd := dag.NodeWithData(ft.FolderPBData()) - di, err := NewDirectory(ctx, "test", nd, dir, ds) + di, err := NewDirectory(ctx, "test", nd, rt.GetDirectory(), ds) if err != nil { t.Fatal(err) } @@ -1199,22 +1198,14 @@ func TestIsDir(t *testing.T) { if !ret { t.Fatal("FSNode type should be dir, but not") } -} - -func TestIsFile(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - ds, rt := setupRoot(ctx, t) - - dir := rt.GetDirectory() - nd := dag.NodeWithData(ft.FilePBData(nil, 0)) - fi, err := NewFile("test", nd, dir, ds) + // check for IsFile + fnd := dag.NodeWithData(ft.FilePBData(nil, 0)) + fi, err := NewFile("test", fnd, rt.GetDirectory(), ds) if err != nil { t.Fatal(err) } - - ret := IsFile(fi) + ret = IsFile(fi) if !ret { t.Fatal("FSNode type should be file, but not") } From 83af9fbde0491d60155e511ad14d2e20068fd753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 9 Oct 2018 18:57:25 +0200 Subject: [PATCH 2731/3817] coreapi unixfs: remove Cat, use sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@26985dbeb71dc2999d2640c301cc88b2e1e56b72 --- coreiface/unixfs.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 078d648bc..4a6c956a0 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -31,10 +31,6 @@ type UnixfsAPI interface { // to operations performed on the returned file Get(context.Context, Path) (files.File, error) - // Cat returns a reader for the file - // TODO: Remove in favour of Get (if we use Get on a file we still have reader directly, so..) - Cat(context.Context, Path) (Reader, error) - // Ls returns the list of links in a directory Ls(context.Context, Path) ([]*ipld.Link, error) } From 8d4196176a99ab51bfd521afbd061bfcf456354f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 15 Oct 2018 12:45:49 +0200 Subject: [PATCH 2732/3817] coreapi unixfs: Return seeker from get MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@7fad9653969f1e71a85778e3e4e225c97d71558b --- coreiface/unixfs.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 4a6c956a0..dd7e5a392 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -2,6 +2,7 @@ package iface import ( "context" + "io" options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" @@ -17,6 +18,11 @@ type AddEvent struct { Size string `json:",omitempty"` } +type UnixfsFile interface { + files.SizeFile + io.Seeker +} + // UnixfsAPI is the basic interface to immutable files in IPFS // NOTE: This API is heavily WIP, things are guaranteed to break frequently type UnixfsAPI interface { @@ -29,7 +35,7 @@ type UnixfsAPI interface { // // Note that some implementations of this API may apply the specified context // to operations performed on the returned file - Get(context.Context, Path) (files.File, error) + Get(context.Context, Path) (UnixfsFile, error) // Ls returns the list of links in a directory Ls(context.Context, Path) ([]*ipld.Link, error) From 994594072487cf471da5d4c8af49cf99250b26bb Mon Sep 17 00:00:00 2001 From: Jeromy Date: Tue, 23 Oct 2018 12:54:20 -0700 Subject: [PATCH 2733/3817] gx publish 1.1.6 This commit was moved from ipld/go-car@0c4f3fcea33d34eb1883669ee3ac6f0676ea015c --- ipld/car/car.go | 6 +++--- ipld/car/car_test.go | 2 +- ipld/car/util/util.go | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index 2568c0bc4..69ad7410d 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -21,7 +21,7 @@ func init() { } type CarHeader struct { - Roots []*cid.Cid + Roots []cid.Cid Version uint64 } @@ -30,7 +30,7 @@ type carWriter struct { w io.Writer } -func WriteCar(ctx context.Context, ds format.DAGService, roots []*cid.Cid, w io.Writer) error { +func WriteCar(ctx context.Context, ds format.DAGService, roots []cid.Cid, w io.Writer) error { cw := &carWriter{ds: ds, w: w} h := &CarHeader{ @@ -74,7 +74,7 @@ func (cw *carWriter) WriteHeader(h *CarHeader) error { return util.LdWrite(cw.w, hb) } -func (cw *carWriter) enumGetLinks(ctx context.Context, c *cid.Cid) ([]*format.Link, error) { +func (cw *carWriter) enumGetLinks(ctx context.Context, c cid.Cid) ([]*format.Link, error) { nd, err := cw.ds.Get(ctx, c) if err != nil { return nil, err diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index c4a9f6cc1..01288fea8 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -39,7 +39,7 @@ func TestRoundtrip(t *testing.T) { assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) buf := new(bytes.Buffer) - if err := WriteCar(context.Background(), dserv, []*cid.Cid{nd3.Cid()}, buf); err != nil { + if err := WriteCar(context.Background(), dserv, []cid.Cid{nd3.Cid()}, buf); err != nil { t.Fatal(err) } diff --git a/ipld/car/util/util.go b/ipld/car/util/util.go index a5ce11d77..c53d59506 100644 --- a/ipld/car/util/util.go +++ b/ipld/car/util/util.go @@ -19,7 +19,7 @@ type BytesReader interface { } // TODO: this belongs in the go-cid package -func ReadCid(buf []byte) (*cid.Cid, int, error) { +func ReadCid(buf []byte) (cid.Cid, int, error) { if bytes.Equal(buf[:2], cidv0Pref) { c, err := cid.Cast(buf[:34]) return c, 34, err @@ -30,37 +30,37 @@ func ReadCid(buf []byte) (*cid.Cid, int, error) { // assume cidv1 vers, err := binary.ReadUvarint(br) if err != nil { - return nil, 0, err + return cid.Cid{}, 0, err } // TODO: the go-cid package allows version 0 here as well if vers != 1 { - return nil, 0, fmt.Errorf("invalid cid version number") + return cid.Cid{}, 0, fmt.Errorf("invalid cid version number") } codec, err := binary.ReadUvarint(br) if err != nil { - return nil, 0, err + return cid.Cid{}, 0, err } mhr := mh.NewReader(br) h, err := mhr.ReadMultihash() if err != nil { - return nil, 0, err + return cid.Cid{}, 0, err } return cid.NewCidV1(codec, h), len(buf) - br.Len(), nil } -func ReadNode(br *bufio.Reader) (*cid.Cid, []byte, error) { +func ReadNode(br *bufio.Reader) (cid.Cid, []byte, error) { data, err := LdRead(br) if err != nil { - return nil, nil, err + return cid.Cid{}, nil, err } c, n, err := ReadCid(data) if err != nil { - return nil, nil, err + return cid.Cid{}, nil, err } return c, data[n:], nil From 43a63d6df5aa862019072b8ddbdd957b933a4d89 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Oct 2018 09:59:18 -0700 Subject: [PATCH 2734/3817] gx update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@fffcec9dbcd3479773eb8e3606727c9d1fa12396 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 22 +++++++++++----------- namesys/namesys.go | 6 +++--- namesys/namesys_test.go | 12 ++++++------ namesys/proquint.go | 2 +- namesys/publisher.go | 12 ++++++------ namesys/publisher_test.go | 10 +++++----- namesys/republisher/repub.go | 6 +++--- namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 10 +++++----- namesys/routing.go | 12 ++++++------ 14 files changed, 53 insertions(+), 53 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index f90e8add1..d6124410c 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 8a96d90fd..9f364ffd3 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index bd62b7d22..bef6bad0d 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -9,7 +9,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" ) type LookupTXTFunc func(name string) (txt []string, err error) diff --git a/namesys/interface.go b/namesys/interface.go index a1b1308ca..3c03c0f52 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 5bcef4e14..59fda0546 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,24 +5,24 @@ import ( "testing" "time" - path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" - testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - routing "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing" - ropts "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing/options" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - mockrouting "gx/ipfs/QmQ9PR61a8rwEFuFNs7JMA1QtQC9yZnBwoDn51JWXDbaTd/go-ipfs-routing/mock" - offline "gx/ipfs/QmQ9PR61a8rwEFuFNs7JMA1QtQC9yZnBwoDn51JWXDbaTd/go-ipfs-routing/offline" - record "gx/ipfs/QmSb4B8ZAAj5ALe9LjfzPyF8Ma6ezC1NTnDF2JQPUJxEXb/go-libp2p-record" - pstore "gx/ipfs/QmWtCpWB39Rzc2xTB75MKorsxNpo3TyecTEN24CJ3KVohE/go-libp2p-peerstore" - pstoremem "gx/ipfs/QmWtCpWB39Rzc2xTB75MKorsxNpo3TyecTEN24CJ3KVohE/go-libp2p-peerstore/pstoremem" - ipns "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns" + peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" + pstore "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore" + pstoremem "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore/pstoremem" + testutil "gx/ipfs/Qma6ESRQTf1ZLPgzpCwDTqQJefPnU6uLvMjP18vK8EWp8L/go-testutil" + record "gx/ipfs/Qma9Eqp16mNHDX1EL73pcxhFfzbyXVcAYtaDd1xdmDRDtL/go-libp2p-record" + ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" - peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + routing "gx/ipfs/QmcQ81jSyWCp1jpkQ8CMbtpXT3jK7Wg6ZtYmoyWFgBoF9c/go-libp2p-routing" + ropts "gx/ipfs/QmcQ81jSyWCp1jpkQ8CMbtpXT3jK7Wg6ZtYmoyWFgBoF9c/go-libp2p-routing/options" + mockrouting "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/mock" + offline "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/offline" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index aa37a93fe..ac9f0ea30 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,17 +5,17 @@ import ( "strings" "time" - path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" - routing "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" + peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" - peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + routing "gx/ipfs/QmcQ81jSyWCp1jpkQ8CMbtpXT3jK7Wg6ZtYmoyWFgBoF9c/go-libp2p-routing" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index e088b2933..8d3e7a28e 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -8,14 +8,14 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - offroute "gx/ipfs/QmQ9PR61a8rwEFuFNs7JMA1QtQC9yZnBwoDn51JWXDbaTd/go-ipfs-routing/offline" - "gx/ipfs/QmWE6Ftsk98cG2MTVgH4wJT8VP2nL9TuBkYTrz9GSqcsh5/go-unixfs" - pstoremem "gx/ipfs/QmWtCpWB39Rzc2xTB75MKorsxNpo3TyecTEN24CJ3KVohE/go-libp2p-peerstore/pstoremem" - ipns "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns" + peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" + pstoremem "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore/pstoremem" + "gx/ipfs/QmU7HFzvfEvimC6wJehti4rcEkvQhvtgo1koHhPN4TXav4/go-unixfs" + ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" - peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" - path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" + offroute "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/offline" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index 894579313..08b451a6f 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -5,7 +5,7 @@ import ( "errors" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" - path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index cff6e0e3d..40eb627f2 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,16 +7,16 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmWE6Ftsk98cG2MTVgH4wJT8VP2nL9TuBkYTrz9GSqcsh5/go-unixfs" - path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + ft "gx/ipfs/QmU7HFzvfEvimC6wJehti4rcEkvQhvtgo1koHhPN4TXav4/go-unixfs" + path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" - routing "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - ipns "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns" - pb "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns/pb" + peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" + ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" + pb "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns/pb" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dsquery "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" - peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + routing "gx/ipfs/QmcQ81jSyWCp1jpkQ8CMbtpXT3jK7Wg6ZtYmoyWFgBoF9c/go-libp2p-routing" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index e29881004..4f5927206 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -6,15 +6,15 @@ import ( "testing" "time" - testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - mockrouting "gx/ipfs/QmQ9PR61a8rwEFuFNs7JMA1QtQC9yZnBwoDn51JWXDbaTd/go-ipfs-routing/mock" dshelp "gx/ipfs/QmS73grfbWgWrNztd8Lns9GCG3jjRNDfcPYg2VYQzKDZSt/go-ipfs-ds-help" - ipns "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns" - ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" + ma "gx/ipfs/QmT4U94DnD8FRfqr21obWY32HLM5VExccPKMjQHofeYqr9/go-multiaddr" + peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" + testutil "gx/ipfs/Qma6ESRQTf1ZLPgzpCwDTqQJefPnU6uLvMjP18vK8EWp8L/go-testutil" + ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" - peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + mockrouting "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/mock" ) type identity struct { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 2ea8d4633..66c96ac75 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,15 +7,15 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - pb "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns/pb" + peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" + pb "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns/pb" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" - peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index a982b2df1..4d375567f 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" - mocknet "gx/ipfs/QmPL3AKtiaQyYpchZceXBZhZ3MSnoGqJvLZrc7fzDTTQdJ/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - pstore "gx/ipfs/QmWtCpWB39Rzc2xTB75MKorsxNpo3TyecTEN24CJ3KVohE/go-libp2p-peerstore" + pstore "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore" + mocknet "gx/ipfs/QmabWrc5aEQ36iWgJZonKgHpttvyDhHoWBoCtesuyMn9XF/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 3fd8f2c97..925e8f2f8 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,14 +6,14 @@ import ( "testing" "time" - path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" - testutil "gx/ipfs/QmNfQbgBfARAtrYsBguChX6VJ5nbjeoYy1KdC36aaYWqG8/go-testutil" - mockrouting "gx/ipfs/QmQ9PR61a8rwEFuFNs7JMA1QtQC9yZnBwoDn51JWXDbaTd/go-ipfs-routing/mock" - ipns "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns" + peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" + testutil "gx/ipfs/Qma6ESRQTf1ZLPgzpCwDTqQJefPnU6uLvMjP18vK8EWp8L/go-testutil" + ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" - peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + mockrouting "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/mock" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index 76aa86034..ca5701b3e 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,18 +5,18 @@ import ( "strings" "time" - path "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - routing "gx/ipfs/QmPmFeQ5oY5G6M7aBWggi5phxEPXwsQntE1DFcUzETULdp/go-libp2p-routing" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - dht "gx/ipfs/QmSteomMgXnSQxLEY5UpxmkYAd8QF9JuLLeLYBokTHxFru/go-libp2p-kad-dht" - ipns "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns" - pb "gx/ipfs/QmX72XT6sSQRkNHKcAFLM2VqB3B4bWPetgWnHY8LgsUVeT/go-ipns/pb" + dht "gx/ipfs/QmRMohiAZU9231TVUydLJfyiiEmXRJYpGVLDarhsLy4FU3/go-libp2p-kad-dht" + peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" - peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" + pb "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns/pb" + routing "gx/ipfs/QmcQ81jSyWCp1jpkQ8CMbtpXT3jK7Wg6ZtYmoyWFgBoF9c/go-libp2p-routing" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) From d897fb4b7e8aa1cdf7e3edeeaee1869bb2a1b3dd Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Oct 2018 09:59:18 -0700 Subject: [PATCH 2735/3817] gx update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@622611f19a4564d40673d7effedc2c805e89d8ef --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 7828fdb17..ce05d3b65 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmVvNkTCx8V9Zei8xuTYTBdUXmbnDRS4iNuw1SztYyhQwQ/go-merkledag" + dag "gx/ipfs/QmY5xpETYHq3PPvaJnafyLWKqk5y7cZnUeBqLRtLUpEV3s/go-merkledag" posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" From 0efdaa0010b4058f2049e049148457d40d63467d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Oct 2018 09:59:18 -0700 Subject: [PATCH 2736/3817] gx update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@a9c40b603bd0d1e144493df5e6eb63012e1861e5 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index a7bd364db..ae9c892d6 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmSU7Nx5eUHWkc9zCTiXDu3ZkdXAZdRgRGRaKM86VjGU4m/go-blockservice" - dag "gx/ipfs/QmVvNkTCx8V9Zei8xuTYTBdUXmbnDRS4iNuw1SztYyhQwQ/go-merkledag" + bserv "gx/ipfs/QmTdoqcwpxSgzUSzX9ZGj6RFsZ28A5SLqsJRUgdFvGQbFC/go-blockservice" + dag "gx/ipfs/QmY5xpETYHq3PPvaJnafyLWKqk5y7cZnUeBqLRtLUpEV3s/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 5736e5597..1ca50402b 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmVvNkTCx8V9Zei8xuTYTBdUXmbnDRS4iNuw1SztYyhQwQ/go-merkledag" + mdag "gx/ipfs/QmY5xpETYHq3PPvaJnafyLWKqk5y7cZnUeBqLRtLUpEV3s/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index ba83bdeb5..3821ff637 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - bs "gx/ipfs/QmSU7Nx5eUHWkc9zCTiXDu3ZkdXAZdRgRGRaKM86VjGU4m/go-blockservice" - mdag "gx/ipfs/QmVvNkTCx8V9Zei8xuTYTBdUXmbnDRS4iNuw1SztYyhQwQ/go-merkledag" + bs "gx/ipfs/QmTdoqcwpxSgzUSzX9ZGj6RFsZ28A5SLqsJRUgdFvGQbFC/go-blockservice" + mdag "gx/ipfs/QmY5xpETYHq3PPvaJnafyLWKqk5y7cZnUeBqLRtLUpEV3s/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index dbd57d15e..6c082095b 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmVvNkTCx8V9Zei8xuTYTBdUXmbnDRS4iNuw1SztYyhQwQ/go-merkledag" + "gx/ipfs/QmY5xpETYHq3PPvaJnafyLWKqk5y7cZnUeBqLRtLUpEV3s/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index a99bc7ef5..ba50b975b 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmSU7Nx5eUHWkc9zCTiXDu3ZkdXAZdRgRGRaKM86VjGU4m/go-blockservice" - dag "gx/ipfs/QmVvNkTCx8V9Zei8xuTYTBdUXmbnDRS4iNuw1SztYyhQwQ/go-merkledag" + bserv "gx/ipfs/QmTdoqcwpxSgzUSzX9ZGj6RFsZ28A5SLqsJRUgdFvGQbFC/go-blockservice" + dag "gx/ipfs/QmY5xpETYHq3PPvaJnafyLWKqk5y7cZnUeBqLRtLUpEV3s/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" From 10afc1c6e7946a55faeacc943f1aa6802530796b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Oct 2018 09:59:18 -0700 Subject: [PATCH 2737/3817] gx update License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@c9eb6014a863a96f762fd87274a63ff906da0c65 --- coreiface/dht.go | 4 ++-- coreiface/key.go | 2 +- coreiface/options/unixfs.go | 2 +- coreiface/path.go | 2 +- coreiface/pubsub.go | 2 +- coreiface/swarm.go | 8 ++++---- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/coreiface/dht.go b/coreiface/dht.go index cb3362bb9..38a0f7348 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -5,8 +5,8 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - pstore "gx/ipfs/QmWtCpWB39Rzc2xTB75MKorsxNpo3TyecTEN24CJ3KVohE/go-libp2p-peerstore" - peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" + pstore "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore" ) // DhtAPI specifies the interface to the DHT diff --git a/coreiface/key.go b/coreiface/key.go index cc6dc8900..9e7bfee28 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -5,7 +5,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" ) // Key specifies the interface to Keys in KeyAPI Keystore diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 307d618de..8fe172fea 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -6,7 +6,7 @@ import ( cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - dag "gx/ipfs/QmVvNkTCx8V9Zei8xuTYTBdUXmbnDRS4iNuw1SztYyhQwQ/go-merkledag" + dag "gx/ipfs/QmY5xpETYHq3PPvaJnafyLWKqk5y7cZnUeBqLRtLUpEV3s/go-merkledag" ) type Layout int diff --git a/coreiface/path.go b/coreiface/path.go index d90f04aa1..53938c3de 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmdrpbDgeYH3VxkCciQCJY5LkDYdXtig6unDzQmMxFtWEw/go-path" + ipfspath "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) diff --git a/coreiface/pubsub.go b/coreiface/pubsub.go index d7a21e02f..b3f3f6b76 100644 --- a/coreiface/pubsub.go +++ b/coreiface/pubsub.go @@ -6,7 +6,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" + peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" ) // PubSubSubscription is an active PubSub subscription diff --git a/coreiface/swarm.go b/coreiface/swarm.go index ba1a55698..d4b92c017 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -5,11 +5,11 @@ import ( "errors" "time" - net "gx/ipfs/QmSTaEYUgDe1r581hxyd2u9582Hgp3KX4wGwYbRqz2u9Qh/go-libp2p-net" - pstore "gx/ipfs/QmWtCpWB39Rzc2xTB75MKorsxNpo3TyecTEN24CJ3KVohE/go-libp2p-peerstore" - ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" + ma "gx/ipfs/QmT4U94DnD8FRfqr21obWY32HLM5VExccPKMjQHofeYqr9/go-multiaddr" + "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" + pstore "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore" + net "gx/ipfs/QmXuRkCR7BNQa9uqfpTiFWsTQLzmTWYg91Ja1w95gnqb6u/go-libp2p-net" "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol" - "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" ) var ( From 5dd448a0a4dbb4b327cdccd0272dfb63a3189c90 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Oct 2018 12:49:25 -0700 Subject: [PATCH 2738/3817] gx update go-libp2p License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@d4356645b79ee3bf9b968560f710d49f2f69270e --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 4 ++-- namesys/proquint.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 4 ++-- 13 files changed, 17 insertions(+), 17 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index d6124410c..654db8ba8 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" + path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 9f364ffd3..33e347b2f 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" + path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index bef6bad0d..b1c7f6c8c 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,8 +8,8 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" + path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" ) type LookupTXTFunc func(name string) (txt []string, err error) diff --git a/namesys/interface.go b/namesys/interface.go index 3c03c0f52..f20be0102 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" + path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 59fda0546..a326949bd 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" + path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/namesys.go b/namesys/namesys.go index ac9f0ea30..55ab9edbf 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,7 +5,7 @@ import ( "strings" "time" - path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" + path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 8d3e7a28e..760eeacc6 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -8,14 +8,14 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" pstoremem "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore/pstoremem" - "gx/ipfs/QmU7HFzvfEvimC6wJehti4rcEkvQhvtgo1koHhPN4TXav4/go-unixfs" ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" - path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" offroute "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/offline" + "gx/ipfs/QmeA7Cd6kMzQNDFzfXXhe64jX1XufcL8B79hwguu5v6ib9/go-unixfs" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index 08b451a6f..9a5f0b31e 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,8 +4,8 @@ import ( "context" "errors" + path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" - path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 40eb627f2..e48dce3d8 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,8 +7,8 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmU7HFzvfEvimC6wJehti4rcEkvQhvtgo1koHhPN4TXav4/go-unixfs" - path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" + path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" + ft "gx/ipfs/QmeA7Cd6kMzQNDFzfXXhe64jX1XufcL8B79hwguu5v6ib9/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 66c96ac75..026e16e66 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" + path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 4d375567f..f17877d7a 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" + path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" + mocknet "gx/ipfs/QmRqtXHu5gsCLpf2s1R2jQuKJBowYKkg6FGQiGCbzttSd1/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" pstore "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore" - mocknet "gx/ipfs/QmabWrc5aEQ36iWgJZonKgHpttvyDhHoWBoCtesuyMn9XF/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 925e8f2f8..97e7f71d5 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" + path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" testutil "gx/ipfs/Qma6ESRQTf1ZLPgzpCwDTqQJefPnU6uLvMjP18vK8EWp8L/go-testutil" diff --git a/namesys/routing.go b/namesys/routing.go index ca5701b3e..416c0b286 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,14 +5,14 @@ import ( "strings" "time" - path "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" + path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - dht "gx/ipfs/QmRMohiAZU9231TVUydLJfyiiEmXRJYpGVLDarhsLy4FU3/go-libp2p-kad-dht" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" + dht "gx/ipfs/QmVMa2b3qsZCWFSfpU7Q7ci57q3o8rQzfWdS8c1yqqL4US/go-libp2p-kad-dht" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" pb "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns/pb" From 958723131ca74c84f0c579b39fcdfb5c909d1afa Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Oct 2018 12:49:25 -0700 Subject: [PATCH 2739/3817] gx update go-libp2p License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@6f0e229891a43bd4c6e596fd72bcd609be74e69b --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index ce05d3b65..de1d82d5a 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmY5xpETYHq3PPvaJnafyLWKqk5y7cZnUeBqLRtLUpEV3s/go-merkledag" + dag "gx/ipfs/QmcescwzzD86xrxoXNJ6VwSw46wLC91QzFDnozYRVf4KnX/go-merkledag" posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" From 33041a8b67bf58006b86654fa105a52b735400e9 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Oct 2018 12:49:25 -0700 Subject: [PATCH 2740/3817] gx update go-libp2p License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@b7d9a81432d7a9ec65c1e01338e3f8ec6316358c --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index ae9c892d6..a188a23f7 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmTdoqcwpxSgzUSzX9ZGj6RFsZ28A5SLqsJRUgdFvGQbFC/go-blockservice" - dag "gx/ipfs/QmY5xpETYHq3PPvaJnafyLWKqk5y7cZnUeBqLRtLUpEV3s/go-merkledag" + bserv "gx/ipfs/QmbZbNRg1x28X9ayEG1ZgEuSXcryGPcdEtWN5k6sNz4aqz/go-blockservice" + dag "gx/ipfs/QmcescwzzD86xrxoXNJ6VwSw46wLC91QzFDnozYRVf4KnX/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 1ca50402b..95764ce96 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmY5xpETYHq3PPvaJnafyLWKqk5y7cZnUeBqLRtLUpEV3s/go-merkledag" + mdag "gx/ipfs/QmcescwzzD86xrxoXNJ6VwSw46wLC91QzFDnozYRVf4KnX/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 3821ff637..097428fd2 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - bs "gx/ipfs/QmTdoqcwpxSgzUSzX9ZGj6RFsZ28A5SLqsJRUgdFvGQbFC/go-blockservice" - mdag "gx/ipfs/QmY5xpETYHq3PPvaJnafyLWKqk5y7cZnUeBqLRtLUpEV3s/go-merkledag" + bs "gx/ipfs/QmbZbNRg1x28X9ayEG1ZgEuSXcryGPcdEtWN5k6sNz4aqz/go-blockservice" + mdag "gx/ipfs/QmcescwzzD86xrxoXNJ6VwSw46wLC91QzFDnozYRVf4KnX/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 6c082095b..da91cbbac 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmY5xpETYHq3PPvaJnafyLWKqk5y7cZnUeBqLRtLUpEV3s/go-merkledag" + "gx/ipfs/QmcescwzzD86xrxoXNJ6VwSw46wLC91QzFDnozYRVf4KnX/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index ba50b975b..990c74709 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmTdoqcwpxSgzUSzX9ZGj6RFsZ28A5SLqsJRUgdFvGQbFC/go-blockservice" - dag "gx/ipfs/QmY5xpETYHq3PPvaJnafyLWKqk5y7cZnUeBqLRtLUpEV3s/go-merkledag" + bserv "gx/ipfs/QmbZbNRg1x28X9ayEG1ZgEuSXcryGPcdEtWN5k6sNz4aqz/go-blockservice" + dag "gx/ipfs/QmcescwzzD86xrxoXNJ6VwSw46wLC91QzFDnozYRVf4KnX/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" From 8cdb97d783313ce8276610860ec005d66e395cb5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Oct 2018 12:49:25 -0700 Subject: [PATCH 2741/3817] gx update go-libp2p License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@da1f68d4e43463145f44894cbb8cf5d5a992f8d3 --- coreiface/options/unixfs.go | 2 +- coreiface/path.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 8fe172fea..4d179f9ff 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -6,7 +6,7 @@ import ( cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - dag "gx/ipfs/QmY5xpETYHq3PPvaJnafyLWKqk5y7cZnUeBqLRtLUpEV3s/go-merkledag" + dag "gx/ipfs/QmcescwzzD86xrxoXNJ6VwSw46wLC91QzFDnozYRVf4KnX/go-merkledag" ) type Layout int diff --git a/coreiface/path.go b/coreiface/path.go index 53938c3de..44d53f23c 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmayGyPXjTt3cGzjCR3wb5HsHQX7LaJcWUbZemGDn6rKWq/go-path" + ipfspath "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) From 0105ad9624082ca3a8b9e162976d2e53ebf7d593 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Oct 2018 15:01:31 -0700 Subject: [PATCH 2742/3817] gx: update yamux (fixes a panic due to a race) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@39b130f100cc230fe00509ee44161ed2eb0ce872 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 4 ++-- namesys/proquint.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 4 ++-- 13 files changed, 17 insertions(+), 17 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 654db8ba8..5bae57561 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" + path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 33e347b2f..349a44e6e 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" + path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index b1c7f6c8c..3c9a58bc4 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" + path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index f20be0102..281c36d62 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" + path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index a326949bd..e885b2f13 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" + path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/namesys.go b/namesys/namesys.go index 55ab9edbf..8d486cdfb 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,7 +5,7 @@ import ( "strings" "time" - path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" + path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 760eeacc6..39100c4f2 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -8,14 +8,14 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" + path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" + "gx/ipfs/QmTJUySFxXjh54zEoFbzQEmGD3yj89XKS3A28y7Nqsn1TC/go-unixfs" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" pstoremem "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore/pstoremem" ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" offroute "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/offline" - "gx/ipfs/QmeA7Cd6kMzQNDFzfXXhe64jX1XufcL8B79hwguu5v6ib9/go-unixfs" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index 9a5f0b31e..4ec9cb1ea 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,7 +4,7 @@ import ( "context" "errors" - path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" + path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/publisher.go b/namesys/publisher.go index e48dce3d8..899617d0c 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,8 +7,8 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" - ft "gx/ipfs/QmeA7Cd6kMzQNDFzfXXhe64jX1XufcL8B79hwguu5v6ib9/go-unixfs" + path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" + ft "gx/ipfs/QmTJUySFxXjh54zEoFbzQEmGD3yj89XKS3A28y7Nqsn1TC/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 026e16e66..b6fe6334b 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" + path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index f17877d7a..8ac797a2f 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" + path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" - mocknet "gx/ipfs/QmRqtXHu5gsCLpf2s1R2jQuKJBowYKkg6FGQiGCbzttSd1/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" pstore "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore" + mocknet "gx/ipfs/QmUDTcnDp2WssbmiDLC6aYurUeyt7QeRakHUQMxA2mZ5iB/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 97e7f71d5..07d1053ef 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" + path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" testutil "gx/ipfs/Qma6ESRQTf1ZLPgzpCwDTqQJefPnU6uLvMjP18vK8EWp8L/go-testutil" diff --git a/namesys/routing.go b/namesys/routing.go index 416c0b286..e3873ef3c 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,14 +5,14 @@ import ( "strings" "time" - path "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" + path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + dht "gx/ipfs/QmQHnqaNULV8WeUGgh97o9K3KAW6kWQmDyNf9UuikgnPTe/go-libp2p-kad-dht" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" - dht "gx/ipfs/QmVMa2b3qsZCWFSfpU7Q7ci57q3o8rQzfWdS8c1yqqL4US/go-libp2p-kad-dht" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" pb "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns/pb" From 1c174821c82b543817a8463db7feee5e1deef238 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Oct 2018 15:01:31 -0700 Subject: [PATCH 2743/3817] gx: update yamux (fixes a panic due to a race) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@e1d4b14b61c357963b58ac8af62df05d309c2b52 --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index de1d82d5a..9c3bf48aa 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmcescwzzD86xrxoXNJ6VwSw46wLC91QzFDnozYRVf4KnX/go-merkledag" + dag "gx/ipfs/QmY8BMUSpCwNiTmFhACmC9Bt1qT63cHP35AoQAus4x14qH/go-merkledag" posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" From cbed8c00664814a0b201aef13c27c35aa4c3b107 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Oct 2018 15:01:31 -0700 Subject: [PATCH 2744/3817] gx: update yamux (fixes a panic due to a race) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@65b1e941472a3e52dad23ab16f7a5ff0a5b9b95b --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index a188a23f7..dab01e7d1 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmbZbNRg1x28X9ayEG1ZgEuSXcryGPcdEtWN5k6sNz4aqz/go-blockservice" - dag "gx/ipfs/QmcescwzzD86xrxoXNJ6VwSw46wLC91QzFDnozYRVf4KnX/go-merkledag" + bserv "gx/ipfs/QmWfhv1D18DRSiSm73r4QGcByspzPtxxRTcmHW3axFXZo8/go-blockservice" + dag "gx/ipfs/QmY8BMUSpCwNiTmFhACmC9Bt1qT63cHP35AoQAus4x14qH/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 95764ce96..20d0ff957 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmcescwzzD86xrxoXNJ6VwSw46wLC91QzFDnozYRVf4KnX/go-merkledag" + mdag "gx/ipfs/QmY8BMUSpCwNiTmFhACmC9Bt1qT63cHP35AoQAus4x14qH/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 097428fd2..936967768 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - bs "gx/ipfs/QmbZbNRg1x28X9ayEG1ZgEuSXcryGPcdEtWN5k6sNz4aqz/go-blockservice" - mdag "gx/ipfs/QmcescwzzD86xrxoXNJ6VwSw46wLC91QzFDnozYRVf4KnX/go-merkledag" + bs "gx/ipfs/QmWfhv1D18DRSiSm73r4QGcByspzPtxxRTcmHW3axFXZo8/go-blockservice" + mdag "gx/ipfs/QmY8BMUSpCwNiTmFhACmC9Bt1qT63cHP35AoQAus4x14qH/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index da91cbbac..bab412f1d 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmcescwzzD86xrxoXNJ6VwSw46wLC91QzFDnozYRVf4KnX/go-merkledag" + "gx/ipfs/QmY8BMUSpCwNiTmFhACmC9Bt1qT63cHP35AoQAus4x14qH/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 990c74709..3d59b4c0a 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmbZbNRg1x28X9ayEG1ZgEuSXcryGPcdEtWN5k6sNz4aqz/go-blockservice" - dag "gx/ipfs/QmcescwzzD86xrxoXNJ6VwSw46wLC91QzFDnozYRVf4KnX/go-merkledag" + bserv "gx/ipfs/QmWfhv1D18DRSiSm73r4QGcByspzPtxxRTcmHW3axFXZo8/go-blockservice" + dag "gx/ipfs/QmY8BMUSpCwNiTmFhACmC9Bt1qT63cHP35AoQAus4x14qH/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" From 7e7e70c7ef59b3024e7030f93d9b24f7697a4b51 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Oct 2018 15:01:31 -0700 Subject: [PATCH 2745/3817] gx: update yamux (fixes a panic due to a race) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@edab11e8dd939a1f41567c663c20e79a334c38f3 --- coreiface/options/unixfs.go | 2 +- coreiface/path.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 4d179f9ff..f564d2427 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -6,7 +6,7 @@ import ( cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - dag "gx/ipfs/QmcescwzzD86xrxoXNJ6VwSw46wLC91QzFDnozYRVf4KnX/go-merkledag" + dag "gx/ipfs/QmY8BMUSpCwNiTmFhACmC9Bt1qT63cHP35AoQAus4x14qH/go-merkledag" ) type Layout int diff --git a/coreiface/path.go b/coreiface/path.go index 44d53f23c..eb976ebd8 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmQ4sKWqHhSYekzST5RwT4VHdQB4df6JWLHNy7tuWTo8uY/go-path" + ipfspath "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) From b74f92ef615dd95e75ed8c6fa983cea3565a3c41 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 19 Oct 2018 11:44:28 +0200 Subject: [PATCH 2746/3817] Fix #27: Remove batching from importers A batching DAG service is forced onto the users of the importers, but they could just wrap the given DAGSerice in the batching one to get the same functionality (remembering to Close it at the end of the proccess). As detailed in #27, the importers should not be making choices about what DAGService is the right one to use and wrapping the given one. This change requires wrapping the DAGService in go-ipfs into ipld.Batch. and closing it when Finishing the adding process. This commit was moved from ipfs/go-unixfs@60781411d2fb5cb1e660deff21ac0915c38c0019 --- unixfs/importer/balanced/builder.go | 4 +-- unixfs/importer/helpers/dagbuilder.go | 38 +++++++-------------------- unixfs/importer/helpers/helpers.go | 4 +-- unixfs/importer/trickle/trickledag.go | 19 +------------- 4 files changed, 14 insertions(+), 51 deletions(-) diff --git a/unixfs/importer/balanced/builder.go b/unixfs/importer/balanced/builder.go index 55b9ccb36..c1a3e8640 100644 --- a/unixfs/importer/balanced/builder.go +++ b/unixfs/importer/balanced/builder.go @@ -130,7 +130,7 @@ func Layout(db *h.DagBuilderHelper) (ipld.Node, error) { // This works without Filestore support (`ProcessFileStore`). // TODO: Why? Is there a test case missing? - return db.AddNodeAndClose(root) + return root, db.Add(root) } // The first `root` will be a single leaf node with data @@ -160,7 +160,7 @@ func Layout(db *h.DagBuilderHelper) (ipld.Node, error) { } } - return db.AddNodeAndClose(root) + return root, db.Add(root) } // fillNodeRec will "fill" the given internal (non-leaf) `node` with data by diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index 9c0dd437c..24896cd1b 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -6,6 +6,7 @@ import ( "os" dag "github.com/ipfs/go-merkledag" + ft "github.com/ipfs/go-unixfs" pb "github.com/ipfs/go-unixfs/pb" @@ -25,7 +26,6 @@ type DagBuilderHelper struct { rawLeaves bool nextData []byte // the next item to return. maxlinks int - batch *ipld.Batch cidBuilder cid.Builder // Filestore support variables. @@ -78,7 +78,6 @@ func (dbp *DagBuilderParams) New(spl chunker.Splitter) *DagBuilderHelper { rawLeaves: dbp.RawLeaves, cidBuilder: dbp.CidBuilder, maxlinks: dbp.Maxlinks, - batch: ipld.NewBatch(context.TODO(), dbp.Dagserv), } if fi, ok := spl.Reader().(files.FileInfo); dbp.NoCopy && ok { db.fullPath = fi.AbsPath() @@ -327,8 +326,8 @@ func (db *DagBuilderHelper) ProcessFileStore(node ipld.Node, dataSize uint64) ip return node } -// Add sends a node to the DAGService, and returns it. -func (db *DagBuilderHelper) Add(node *UnixfsNode) (ipld.Node, error) { +// AddUnixfsNode sends a node to the DAGService, and returns it as ipld.Node. +func (db *DagBuilderHelper) AddUnixfsNode(node *UnixfsNode) (ipld.Node, error) { dn, err := node.GetDagNode() if err != nil { return nil, err @@ -342,36 +341,17 @@ func (db *DagBuilderHelper) Add(node *UnixfsNode) (ipld.Node, error) { return dn, nil } +// Add inserts the given node in the DAGService. +func (db *DagBuilderHelper) Add(node ipld.Node) error { + return db.dserv.Add(context.TODO(), node) +} + // Maxlinks returns the configured maximum number for links // for nodes built with this helper. func (db *DagBuilderHelper) Maxlinks() int { return db.maxlinks } -// Close has the DAGService perform a batch Commit operation. -// It should be called at the end of the building process to make -// sure all data is persisted. -func (db *DagBuilderHelper) Close() error { - return db.batch.Commit() -} - -// AddNodeAndClose adds the last `ipld.Node` from the DAG and -// closes the builder. It returns the same `node` passed as -// argument. -func (db *DagBuilderHelper) AddNodeAndClose(node ipld.Node) (ipld.Node, error) { - err := db.batch.Add(node) - if err != nil { - return nil, err - } - - err = db.Close() - if err != nil { - return nil, err - } - - return node, nil -} - // FSNodeOverDag encapsulates an `unixfs.FSNode` that will be stored in a // `dag.ProtoNode`. Instead of just having a single `ipld.Node` that // would need to be constantly (un)packed to access and modify its @@ -421,7 +401,7 @@ func (n *FSNodeOverDag) AddChild(child ipld.Node, fileSize uint64, db *DagBuilde n.file.AddBlockSize(fileSize) - return db.batch.Add(child) + return db.Add(child) } // Commit unifies (resolves) the cache nodes into a single `ipld.Node` diff --git a/unixfs/importer/helpers/helpers.go b/unixfs/importer/helpers/helpers.go index a2e443ea3..ba6d51826 100644 --- a/unixfs/importer/helpers/helpers.go +++ b/unixfs/importer/helpers/helpers.go @@ -6,6 +6,7 @@ import ( "os" dag "github.com/ipfs/go-merkledag" + ft "github.com/ipfs/go-unixfs" cid "github.com/ipfs/go-cid" @@ -103,8 +104,7 @@ func (n *UnixfsNode) AddChild(child *UnixfsNode, db *DagBuilderHelper) error { return err } - err = db.batch.Add(childnode) - + _, err = db.AddUnixfsNode(child) return err } diff --git a/unixfs/importer/trickle/trickledag.go b/unixfs/importer/trickle/trickledag.go index bdc72e8bf..70a953825 100644 --- a/unixfs/importer/trickle/trickledag.go +++ b/unixfs/importer/trickle/trickledag.go @@ -42,16 +42,7 @@ func Layout(db *h.DagBuilderHelper) (ipld.Node, error) { return nil, err } - out, err := db.Add(root) - if err != nil { - return nil, err - } - - if err := db.Close(); err != nil { - return nil, err - } - - return out, nil + return db.AddUnixfsNode(root) } // fillTrickleRec creates a trickle (sub-)tree with an optional maximum specified depth @@ -92,14 +83,6 @@ func Append(ctx context.Context, basen ipld.Node, db *h.DagBuilderHelper) (out i return nil, dag.ErrNotProtobuf } - defer func() { - if errOut == nil { - if err := db.Close(); err != nil { - errOut = err - } - } - }() - // Convert to unixfs node for working with easily ufsn, err := h.NewUnixfsNodeFromDag(base) if err != nil { From c0c8c3333ebebcd5eb58219dd07a0b676bdf02ee Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 26 Oct 2018 16:07:05 +0200 Subject: [PATCH 2747/3817] Add travis and makefile This commit was moved from ipfs/go-unixfs@18a59cc4222193a669d9b42d3362089c60b630b9 --- unixfs/Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 unixfs/Makefile diff --git a/unixfs/Makefile b/unixfs/Makefile new file mode 100644 index 000000000..20619413c --- /dev/null +++ b/unixfs/Makefile @@ -0,0 +1,11 @@ +gx: + go get github.com/whyrusleeping/gx + go get github.com/whyrusleeping/gx-go + +deps: gx + gx --verbose install --global + gx-go rewrite + +publish: + gx-go rewrite --undo + From 3a41d40d2229201163ac22f565bc25eca4e69229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 26 Oct 2018 18:41:03 +0200 Subject: [PATCH 2748/3817] namesys: properly attach path in name.Resolve MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@5f6c06c0734b37681c8f60be030277af8ff3c4cf --- namesys/namesys.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 8d486cdfb..dddb0dff3 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -100,6 +100,14 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. key := segments[2] if p, ok := ns.cacheGet(key); ok { + if len(segments) > 3 { + var err error + p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) + if err != nil { + emitOnceResult(ctx, out, onceResult{value: p, err: err}) + } + } + out <- onceResult{value: p} close(out) return out @@ -139,7 +147,8 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. // Attach rest of the path if len(segments) > 3 { - p, err := path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) + var err error + p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) if err != nil { emitOnceResult(ctx, out, onceResult{value: p, ttl: res.ttl, err: err}) } From cf080549beaa04ea4d675d9ca8a7c0cafd2ac4d2 Mon Sep 17 00:00:00 2001 From: Kejie Zhang Date: Fri, 12 Oct 2018 14:29:11 +0800 Subject: [PATCH 2749/3817] correctly handle offsets bigger than file size This commit was moved from ipfs/go-unixfs@f573b7b58ee5861b30763f05b4f6f07913db480b --- unixfs/mod/dagmodifier.go | 12 ++++++++- unixfs/mod/dagmodifier_test.go | 49 ++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index be9b07ea7..a4c098052 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -200,6 +200,16 @@ func (dm *DagModifier) Sync() error { // Number of bytes we're going to write buflen := dm.wrBuf.Len() + fs, err := fileSize(dm.curNode) + if err != nil { + return err + } + if fs < dm.writeStart { + if err := dm.expandSparse(int64(dm.writeStart - fs)); err != nil { + return err + } + } + // overwrite existing dag nodes thisc, err := dm.modifyDag(dm.curNode, dm.writeStart) if err != nil { @@ -225,8 +235,8 @@ func (dm *DagModifier) Sync() error { } dm.writeStart += uint64(buflen) - dm.wrBuf = nil + return nil } diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index f9e302ee8..1e192e720 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -7,12 +7,14 @@ import ( "io/ioutil" "testing" + dag "github.com/ipfs/go-merkledag" h "github.com/ipfs/go-unixfs/importer/helpers" trickle "github.com/ipfs/go-unixfs/importer/trickle" uio "github.com/ipfs/go-unixfs/io" testu "github.com/ipfs/go-unixfs/test" u "github.com/ipfs/go-ipfs-util" + "github.com/ipfs/go-unixfs" ) func testModWrite(t *testing.T, beg, size uint64, orig []byte, dm *DagModifier, opts testu.NodeOpts) []byte { @@ -410,6 +412,53 @@ func testDagTruncate(t *testing.T, opts testu.NodeOpts) { } } +func TestDagSync(t *testing.T) { + dserv := testu.GetDAGServ() + nd := dag.NodeWithData(unixfs.FilePBData(nil, 0)) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dagmod, err := NewDagModifier(ctx, nd, dserv, testu.SizeSplitterGen(512)) + if err != nil { + t.Fatal(err) + } + + _, err = dagmod.Write([]byte("test1")) + if err != nil { + t.Fatal(err) + } + + err = dagmod.Sync() + if err != nil { + t.Fatal(err) + } + + err = dagmod.Truncate(0) + if err != nil { + t.Fatal(err) + } + + _, err = dagmod.Write([]byte("test2")) + if err != nil { + t.Fatal(err) + } + + err = dagmod.Sync() + if err != nil { + t.Fatal(err) + } + + out, err := ioutil.ReadAll(dagmod) + if err != nil { + t.Fatal(err) + } + + if err = testu.ArrComp(out[5:], []byte("test2")); err != nil { + t.Fatal(err) + } +} + // TestDagTruncateSameSize tests that a DAG truncated // to the same size (i.e., doing nothing) doesn't modify // the DAG (its hash). From e813f09e43adccefa60420f55204b737370edced Mon Sep 17 00:00:00 2001 From: Kejie Zhang Date: Wed, 24 Oct 2018 21:34:44 +0800 Subject: [PATCH 2750/3817] document testDagSync function This commit was moved from ipfs/go-unixfs@e8af7a6b5b5588835fb856e35024d9163361c7ab --- unixfs/mod/dagmodifier_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 1e192e720..b61369362 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -412,6 +412,8 @@ func testDagTruncate(t *testing.T, opts testu.NodeOpts) { } } +// TestDagSync tests that a DAG will expand sparse during sync +// if offset > curNode's size. func TestDagSync(t *testing.T) { dserv := testu.GetDAGServ() nd := dag.NodeWithData(unixfs.FilePBData(nil, 0)) @@ -434,6 +436,7 @@ func TestDagSync(t *testing.T) { t.Fatal(err) } + // Truncate leave the offset at 5 and filesize at 0 err = dagmod.Truncate(0) if err != nil { t.Fatal(err) @@ -444,6 +447,7 @@ func TestDagSync(t *testing.T) { t.Fatal(err) } + // When Offset > filesize , Sync will call enpandSparse err = dagmod.Sync() if err != nil { t.Fatal(err) From 95c39b6f72f1d32c0e68731b2a02e031ac941e02 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Sat, 27 Oct 2018 03:30:18 +0200 Subject: [PATCH 2751/3817] Bubble deps License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/interface-go-ipfs-core@c21b863fa15557b64d4bdcc99a7134b28ac3ca05 --- coreiface/coreapi.go | 2 +- coreiface/dag.go | 2 +- coreiface/object.go | 2 +- coreiface/options/unixfs.go | 2 +- coreiface/path.go | 2 +- coreiface/unixfs.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index bc889237b..b744a207a 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -5,7 +5,7 @@ package iface import ( "context" - ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" + ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" ) // CoreAPI defines an unified interface to IPFS for Go programs diff --git a/coreiface/dag.go b/coreiface/dag.go index 06bb91dce..6cca5b9e6 100644 --- a/coreiface/dag.go +++ b/coreiface/dag.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" + ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" ) // DagOps groups operations that can be batched together diff --git a/coreiface/object.go b/coreiface/object.go index 6b355a302..229f69869 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -7,7 +7,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" + ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" ) // ObjectStat provides information about dag nodes diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index f564d2427..8a9137887 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -6,7 +6,7 @@ import ( cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - dag "gx/ipfs/QmY8BMUSpCwNiTmFhACmC9Bt1qT63cHP35AoQAus4x14qH/go-merkledag" + dag "gx/ipfs/QmSei8kFMfqdJq7Q68d2LMnHbTWKKg2daA29ezUYFAUNgc/go-merkledag" ) type Layout int diff --git a/coreiface/path.go b/coreiface/path.go index eb976ebd8..5a3d1128b 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" + ipfspath "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index dd7e5a392..6fd33ad2c 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -6,8 +6,8 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" files "gx/ipfs/QmZMWMvWMVKCbHetJ4RgndbuEF1io2UpUxwQwtNjtYPzSC/go-ipfs-files" - ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) // TODO: ideas on making this more coreapi-ish without breaking the http API? From f0351b2a56fb13426ed8f0a2bc9b61373437f19a Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Sat, 27 Oct 2018 03:30:18 +0200 Subject: [PATCH 2752/3817] Bubble deps License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-namesys@0066fb584454d5654f9b3408f717ebbecdc075e4 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 4 ++-- namesys/proquint.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 5bae57561..4aa4fcfbd 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" + path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 349a44e6e..78ca295a8 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" + path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 3c9a58bc4..7b1b9e35c 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" + path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index 281c36d62..093798faf 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" + path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index e885b2f13..2bd3c42ef 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" + path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/namesys.go b/namesys/namesys.go index dddb0dff3..0080533ba 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,7 +5,7 @@ import ( "strings" "time" - path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" + path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 39100c4f2..1e92547e3 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -8,14 +8,14 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" - "gx/ipfs/QmTJUySFxXjh54zEoFbzQEmGD3yj89XKS3A28y7Nqsn1TC/go-unixfs" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" pstoremem "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore/pstoremem" + path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" offroute "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/offline" + "gx/ipfs/QmfB3oNXGGq9S4B2a9YeCajoATms3Zw2VvDm8fK7VeLSV8/go-unixfs" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index 4ec9cb1ea..c906b1861 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,8 +4,8 @@ import ( "context" "errors" - path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" + path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 899617d0c..76d65a42b 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,8 +7,8 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" - ft "gx/ipfs/QmTJUySFxXjh54zEoFbzQEmGD3yj89XKS3A28y7Nqsn1TC/go-unixfs" + path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" + ft "gx/ipfs/QmfB3oNXGGq9S4B2a9YeCajoATms3Zw2VvDm8fK7VeLSV8/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index b6fe6334b..e8a9c3631 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" + path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 8ac797a2f..08ac7b7b1 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" + path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" pstore "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 07d1053ef..24e93112c 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" + path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" testutil "gx/ipfs/Qma6ESRQTf1ZLPgzpCwDTqQJefPnU6uLvMjP18vK8EWp8L/go-testutil" diff --git a/namesys/routing.go b/namesys/routing.go index e3873ef3c..073a8341b 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,7 +5,7 @@ import ( "strings" "time" - path "gx/ipfs/QmRKuTyCzg7HFBcV1YUhzStroGtJSb8iWgyxfsDCwFhWTS/go-path" + path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" From 3fb782e2d07114c8b0b4c4b08294c6e6f214502a Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Sat, 27 Oct 2018 03:30:18 +0200 Subject: [PATCH 2753/3817] Bubble deps License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-filestore@837f8f56a066c7180417876069c3a0e21a09f202 --- filestore/filestore.go | 2 +- filestore/filestore_test.go | 4 ++-- filestore/fsrefstore.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 1ad7471fb..19f5fd209 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,8 +11,8 @@ import ( "context" "errors" - posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" + posinfo "gx/ipfs/QmQyUyYcpKG1u53V7N25qRTGw5XwaAxTMKXbduqHotQztg/go-ipfs-posinfo" blocks "gx/ipfs/QmRcHuYzAyswytBuMF78rj3LTChYszomRFXNg4685ZN1WM/go-block-format" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" dsq "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 9c3bf48aa..f3813f625 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,10 +7,10 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmY8BMUSpCwNiTmFhACmC9Bt1qT63cHP35AoQAus4x14qH/go-merkledag" + dag "gx/ipfs/QmSei8kFMfqdJq7Q68d2LMnHbTWKKg2daA29ezUYFAUNgc/go-merkledag" - posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" + posinfo "gx/ipfs/QmQyUyYcpKG1u53V7N25qRTGw5XwaAxTMKXbduqHotQztg/go-ipfs-posinfo" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" blockstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" ) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 7ecfaae8c..74b0131b8 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,8 +10,8 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - posinfo "gx/ipfs/QmPG32VXR5jmpo9q8R9FNdR4Ae97Ky9CiZE6SctJLUB79H/go-ipfs-posinfo" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" + posinfo "gx/ipfs/QmQyUyYcpKG1u53V7N25qRTGw5XwaAxTMKXbduqHotQztg/go-ipfs-posinfo" blocks "gx/ipfs/QmRcHuYzAyswytBuMF78rj3LTChYszomRFXNg4685ZN1WM/go-block-format" dshelp "gx/ipfs/QmS73grfbWgWrNztd8Lns9GCG3jjRNDfcPYg2VYQzKDZSt/go-ipfs-ds-help" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" From ff999c03bea447a3646f9331bf2e06debe7037c9 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Sat, 27 Oct 2018 03:30:18 +0200 Subject: [PATCH 2754/3817] Bubble deps License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-pinner@7d1a930ce30b402d87339fd6efd598b08d5331c9 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 2 +- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index dab01e7d1..a1bfa0278 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,16 +8,16 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" + dag "gx/ipfs/QmSei8kFMfqdJq7Q68d2LMnHbTWKKg2daA29ezUYFAUNgc/go-merkledag" bserv "gx/ipfs/QmWfhv1D18DRSiSm73r4QGcByspzPtxxRTcmHW3axFXZo8/go-blockservice" - dag "gx/ipfs/QmY8BMUSpCwNiTmFhACmC9Bt1qT63cHP35AoQAus4x14qH/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" + ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" "gx/ipfs/QmVkMRSkXrpjqrroEXWuYBvDBnXCdMMY6gsKicBGVGUqKT/go-verifcid" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" dstore "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" bstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" - ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 20d0ff957..88bd1aec6 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,12 +10,12 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmY8BMUSpCwNiTmFhACmC9Bt1qT63cHP35AoQAus4x14qH/go-merkledag" + mdag "gx/ipfs/QmSei8kFMfqdJq7Q68d2LMnHbTWKKg2daA29ezUYFAUNgc/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" + ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" - ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 936967768..d92d8a837 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" + mdag "gx/ipfs/QmSei8kFMfqdJq7Q68d2LMnHbTWKKg2daA29ezUYFAUNgc/go-merkledag" bs "gx/ipfs/QmWfhv1D18DRSiSm73r4QGcByspzPtxxRTcmHW3axFXZo8/go-blockservice" - mdag "gx/ipfs/QmY8BMUSpCwNiTmFhACmC9Bt1qT63cHP35AoQAus4x14qH/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index bab412f1d..439f4384e 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,10 +10,10 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmY8BMUSpCwNiTmFhACmC9Bt1qT63cHP35AoQAus4x14qH/go-merkledag" + "gx/ipfs/QmSei8kFMfqdJq7Q68d2LMnHbTWKKg2daA29ezUYFAUNgc/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format" + ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 3d59b4c0a..919efe6d0 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" + dag "gx/ipfs/QmSei8kFMfqdJq7Q68d2LMnHbTWKKg2daA29ezUYFAUNgc/go-merkledag" bserv "gx/ipfs/QmWfhv1D18DRSiSm73r4QGcByspzPtxxRTcmHW3axFXZo8/go-blockservice" - dag "gx/ipfs/QmY8BMUSpCwNiTmFhACmC9Bt1qT63cHP35AoQAus4x14qH/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" From b7d8e3af3df705851341fb1d270272d21f4d5ff3 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Wed, 17 Oct 2018 03:53:01 +0100 Subject: [PATCH 2755/3817] feat(Directory): Add EnumLinksAsync method - Add LinkResult type to unix-fs - is an ipld Link or an error - Add EnumLinksAsync method to Directory interface, returns channel of directory links or error - Add EnumLinksAsync method to Shard interface in HAMT, returns channel of directory links or error - EnumLinks method in Shard interface in HAMT uses EnumLinksAsync now - modify makeAsyncTrieGetLinks to use channel This commit was moved from ipfs/go-unixfs@0e51ad49eac10f04f32f9232738b94121ac72627 --- unixfs/hamt/hamt.go | 56 +++++++++++++---------- unixfs/hamt/hamt_test.go | 91 ++++++++++++++++++++++++++++--------- unixfs/io/directory.go | 27 +++++++++++ unixfs/io/directory_test.go | 29 ++++++++++++ unixfs/unixfs.go | 9 ++++ 5 files changed, 165 insertions(+), 47 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 0b474289b..9de7fb3ab 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -24,14 +24,14 @@ import ( "context" "fmt" "os" - "sync" bitfield "github.com/Stebalien/go-bitfield" cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" - format "github.com/ipfs/go-unixfs" "github.com/spaolacci/murmur3" + + format "github.com/ipfs/go-unixfs" ) const ( @@ -400,21 +400,18 @@ func (ds *Shard) getValue(ctx context.Context, hv *hashBits, key string, cb func // EnumLinks collects all links in the Shard. func (ds *Shard) EnumLinks(ctx context.Context) ([]*ipld.Link, error) { var links []*ipld.Link - var setlk sync.Mutex - - getLinks := makeAsyncTrieGetLinks(ds.dserv, func(sv *Shard) error { - lnk := sv.val - lnk.Name = sv.key - setlk.Lock() - links = append(links, lnk) - setlk.Unlock() - return nil - }) - - cset := cid.NewSet() - err := dag.EnumerateChildrenAsync(ctx, getLinks, ds.nd.Cid(), cset.Visit) - return links, err + linkResults, err := ds.EnumLinksAsync(ctx) + if err != nil { + return nil, err + } + for linkResult := range linkResults { + if linkResult.Err != nil { + return links, linkResult.Err + } + links = append(links, linkResult.Link) + } + return links, nil } // ForEachLink walks the Shard and calls the given function. @@ -427,18 +424,33 @@ func (ds *Shard) ForEachLink(ctx context.Context, f func(*ipld.Link) error) erro }) } +// EnumLinksAsync returns a channel which will receive Links in the directory +// as they are enumerated, where order is not gauranteed +func (ds *Shard) EnumLinksAsync(ctx context.Context) (<-chan format.LinkResult, error) { + linkResults := make(chan format.LinkResult) + go func() { + defer close(linkResults) + getLinks := makeAsyncTrieGetLinks(ds.dserv, linkResults) + cset := cid.NewSet() + dag.EnumerateChildrenAsync(ctx, getLinks, ds.nd.Cid(), cset.Visit) + }() + return linkResults, nil +} + // makeAsyncTrieGetLinks builds a getLinks function that can be used with EnumerateChildrenAsync // to iterate a HAMT shard. It takes an IPLD Dag Service to fetch nodes, and a call back that will get called // on all links to leaf nodes in a HAMT tree, so they can be collected for an EnumLinks operation -func makeAsyncTrieGetLinks(dagService ipld.DAGService, onShardValue func(shard *Shard) error) dag.GetLinks { +func makeAsyncTrieGetLinks(dagService ipld.DAGService, linkResults chan<- format.LinkResult) dag.GetLinks { return func(ctx context.Context, currentCid cid.Cid) ([]*ipld.Link, error) { node, err := dagService.Get(ctx, currentCid) if err != nil { + linkResults <- format.LinkResult{Link: nil, Err: err} return nil, err } directoryShard, err := NewHamtFromDag(dagService, node) if err != nil { + linkResults <- format.LinkResult{Link: nil, Err: err} return nil, err } @@ -449,19 +461,13 @@ func makeAsyncTrieGetLinks(dagService ipld.DAGService, onShardValue func(shard * lnkLinkType, err := directoryShard.childLinkType(lnk) if err != nil { + linkResults <- format.LinkResult{Link: nil, Err: err} return nil, err } if lnkLinkType == shardLink { childShards = append(childShards, lnk) } else { - sv, err := directoryShard.makeShardValue(lnk) - if err != nil { - return nil, err - } - err = onShardValue(sv) - if err != nil { - return nil, err - } + linkResults <- format.LinkResult{Link: lnk, Err: nil} } } return childShards, nil diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index ffbb676eb..077976051 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -74,28 +74,7 @@ func assertLink(s *Shard, name string, found bool) error { } } -func assertSerializationWorks(ds ipld.DAGService, s *Shard) error { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - nd, err := s.Node() - if err != nil { - return err - } - - nds, err := NewHamtFromDag(ds, nd) - if err != nil { - return err - } - - linksA, err := s.EnumLinks(ctx) - if err != nil { - return err - } - - linksB, err := nds.EnumLinks(ctx) - if err != nil { - return err - } +func assertLinksEqual(linksA []*ipld.Link, linksB []*ipld.Link) error { if len(linksA) != len(linksB) { return fmt.Errorf("links arrays are different sizes") @@ -121,6 +100,32 @@ func assertSerializationWorks(ds ipld.DAGService, s *Shard) error { return nil } +func assertSerializationWorks(ds ipld.DAGService, s *Shard) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nd, err := s.Node() + if err != nil { + return err + } + + nds, err := NewHamtFromDag(ds, nd) + if err != nil { + return err + } + + linksA, err := s.EnumLinks(ctx) + if err != nil { + return err + } + + linksB, err := nds.EnumLinks(ctx) + if err != nil { + return err + } + + return assertLinksEqual(linksA, linksB) +} + func TestBasicSet(t *testing.T) { ds := mdtest.Mock() for _, w := range []int{128, 256, 512, 1024, 2048, 4096} { @@ -309,6 +314,48 @@ func TestSetAfterMarshal(t *testing.T) { } } +func TestEnumLinksAsync(t *testing.T) { + ds := mdtest.Mock() + _, s, err := makeDir(ds, 300) + if err != nil { + t.Fatal(err) + } + ctx := context.Background() + + nd, err := s.Node() + if err != nil { + t.Fatal(err) + } + + nds, err := NewHamtFromDag(ds, nd) + if err != nil { + t.Fatal(err) + } + + linksA, err := nds.EnumLinks(ctx) + if err != nil { + t.Fatal(err) + } + + linkResults, err := nds.EnumLinksAsync(ctx) + if err != nil { + t.Fatal(err) + } + var linksB []*ipld.Link + + for linkResult := range linkResults { + if linkResult.Err != nil { + t.Fatal(linkResult.Err) + } + linksB = append(linksB, linkResult.Link) + } + + err = assertLinksEqual(linksA, linksB) + if err != nil { + t.Fatal(err) + } +} + func TestDuplicateAddShard(t *testing.T) { ds := mdtest.Mock() dir, _ := NewShard(ds, 256) diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index aa1ec8de7..26bd2b241 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -6,6 +6,7 @@ import ( "os" mdag "github.com/ipfs/go-merkledag" + format "github.com/ipfs/go-unixfs" hamt "github.com/ipfs/go-unixfs/hamt" @@ -38,6 +39,10 @@ type Directory interface { // ForEachLink applies the given function to Links in the directory. ForEachLink(context.Context, func(*ipld.Link) error) error + // EnumLinksAsync returns a channel which will receive Links in the directory + // as they are enumerated, where order is not gauranteed + EnumLinksAsync(context.Context) (<-chan format.LinkResult, error) + // Links returns the all the links in the directory node. Links(context.Context) ([]*ipld.Link, error) @@ -141,6 +146,22 @@ func (d *BasicDirectory) AddChild(ctx context.Context, name string, node ipld.No return d.node.AddNodeLink(name, node) } +// EnumLinksAsync returns a channel which will receive Links in the directory +// as they are enumerated, where order is not gauranteed +func (d *BasicDirectory) EnumLinksAsync(ctx context.Context) (<-chan format.LinkResult, error) { + linkResults := make(chan format.LinkResult) + go func() { + defer close(linkResults) + for _, l := range d.node.Links() { + linkResults <- format.LinkResult{ + Link: l, + Err: nil, + } + } + }() + return linkResults, nil +} + // ForEachLink implements the `Directory` interface. func (d *BasicDirectory) ForEachLink(ctx context.Context, f func(*ipld.Link) error) error { for _, l := range d.node.Links() { @@ -226,6 +247,12 @@ func (d *HAMTDirectory) ForEachLink(ctx context.Context, f func(*ipld.Link) erro return d.shard.ForEachLink(ctx, f) } +// EnumLinksAsync returns a channel which will receive Links in the directory +// as they are enumerated, where order is not gauranteed +func (d *HAMTDirectory) EnumLinksAsync(ctx context.Context) (<-chan format.LinkResult, error) { + return d.shard.EnumLinksAsync(ctx) +} + // Links implements the `Directory` interface. func (d *HAMTDirectory) Links(ctx context.Context) ([]*ipld.Link, error) { return d.shard.EnumLinks(ctx) diff --git a/unixfs/io/directory_test.go b/unixfs/io/directory_test.go index 64a1ef2c6..6f621e977 100644 --- a/unixfs/io/directory_test.go +++ b/unixfs/io/directory_test.go @@ -5,7 +5,9 @@ import ( "fmt" "testing" + ipld "github.com/ipfs/go-ipld-format" mdtest "github.com/ipfs/go-merkledag/test" + ft "github.com/ipfs/go-unixfs" ) @@ -155,4 +157,31 @@ func TestDirBuilder(t *testing.T) { if len(links) != count { t.Fatal("wrong number of links", len(links), count) } + + linkResults, err := dir.EnumLinksAsync(ctx) + if err != nil { + t.Fatal(err) + } + + asyncNames := make(map[string]bool) + var asyncLinks []*ipld.Link + + for linkResult := range linkResults { + if linkResult.Err != nil { + t.Fatal(linkResult.Err) + } + asyncNames[linkResult.Link.Name] = true + asyncLinks = append(asyncLinks, linkResult.Link) + } + + for i := 0; i < count; i++ { + n := fmt.Sprintf("entry %d", i) + if !asyncNames[n] { + t.Fatal("COULDNT FIND: ", n) + } + } + + if len(asyncLinks) != count { + t.Fatal("wrong number of links", len(asyncLinks), count) + } } diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index 7b4189153..4ee755186 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -9,9 +9,18 @@ import ( proto "github.com/gogo/protobuf/proto" dag "github.com/ipfs/go-merkledag" + + ipld "github.com/ipfs/go-ipld-format" pb "github.com/ipfs/go-unixfs/pb" ) +// A LinkResult for any parallel enumeration of links +// TODO: Should this live in go-ipld-format? +type LinkResult struct { + Link *ipld.Link + Err error +} + // Shorthands for protobuffer types const ( TRaw = pb.Data_Raw From 6d2b748c71bbebcc0bf35c92cebfaf5406ee94e9 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Wed, 17 Oct 2018 21:30:12 +0100 Subject: [PATCH 2756/3817] Add context cancelling logic This commit was moved from ipfs/go-unixfs@87012196a81907dcba6408feab5cc6ca627ed263 --- unixfs/hamt/hamt.go | 17 +++++++++++++---- unixfs/io/directory.go | 6 +++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 9de7fb3ab..1908526a0 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -428,8 +428,10 @@ func (ds *Shard) ForEachLink(ctx context.Context, f func(*ipld.Link) error) erro // as they are enumerated, where order is not gauranteed func (ds *Shard) EnumLinksAsync(ctx context.Context) (<-chan format.LinkResult, error) { linkResults := make(chan format.LinkResult) + ctx, cancel := context.WithCancel(ctx) go func() { defer close(linkResults) + defer cancel() getLinks := makeAsyncTrieGetLinks(ds.dserv, linkResults) cset := cid.NewSet() dag.EnumerateChildrenAsync(ctx, getLinks, ds.nd.Cid(), cset.Visit) @@ -445,12 +447,12 @@ func makeAsyncTrieGetLinks(dagService ipld.DAGService, linkResults chan<- format return func(ctx context.Context, currentCid cid.Cid) ([]*ipld.Link, error) { node, err := dagService.Get(ctx, currentCid) if err != nil { - linkResults <- format.LinkResult{Link: nil, Err: err} + emitResult(ctx, linkResults, format.LinkResult{Link: nil, Err: err}) return nil, err } directoryShard, err := NewHamtFromDag(dagService, node) if err != nil { - linkResults <- format.LinkResult{Link: nil, Err: err} + emitResult(ctx, linkResults, format.LinkResult{Link: nil, Err: err}) return nil, err } @@ -461,19 +463,26 @@ func makeAsyncTrieGetLinks(dagService ipld.DAGService, linkResults chan<- format lnkLinkType, err := directoryShard.childLinkType(lnk) if err != nil { - linkResults <- format.LinkResult{Link: nil, Err: err} + emitResult(ctx, linkResults, format.LinkResult{Link: nil, Err: err}) return nil, err } if lnkLinkType == shardLink { childShards = append(childShards, lnk) } else { - linkResults <- format.LinkResult{Link: lnk, Err: nil} + emitResult(ctx, linkResults, format.LinkResult{Link: lnk, Err: nil}) } } return childShards, nil } } +func emitResult(ctx context.Context, linkResults chan<- format.LinkResult, r format.LinkResult) { + select { + case linkResults <- r: + case <-ctx.Done(): + } +} + func (ds *Shard) walkTrie(ctx context.Context, cb func(*Shard) error) error { for idx := range ds.children { c, err := ds.getChild(ctx, idx) diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index 26bd2b241..5a4f638b9 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -153,9 +153,13 @@ func (d *BasicDirectory) EnumLinksAsync(ctx context.Context) (<-chan format.Link go func() { defer close(linkResults) for _, l := range d.node.Links() { - linkResults <- format.LinkResult{ + select { + case linkResults <- format.LinkResult{ Link: l, Err: nil, + }: + case <-ctx.Done(): + return } } }() From 9a6f776b1c7123cf43cd6bd941d99e7aeef26bf1 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 18 Oct 2018 10:46:01 +0100 Subject: [PATCH 2757/3817] Convert formatting in hamt links to remove bit prefix This commit was moved from ipfs/go-unixfs@fa995d36c0c5522a8a575b6792d8dd74e6a3eefd --- unixfs/hamt/hamt.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 1908526a0..33b706c00 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -469,7 +469,13 @@ func makeAsyncTrieGetLinks(dagService ipld.DAGService, linkResults chan<- format if lnkLinkType == shardLink { childShards = append(childShards, lnk) } else { - emitResult(ctx, linkResults, format.LinkResult{Link: lnk, Err: nil}) + sv, err := directoryShard.makeShardValue(lnk) + if err != nil { + return nil, err + } + formattedLink := sv.val + formattedLink.Name = sv.key + emitResult(ctx, linkResults, format.LinkResult{Link: formattedLink, Err: nil}) } } return childShards, nil From 6b813c9b37efea1271bc192d55afe18554f2469f Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 18 Oct 2018 15:32:36 +0100 Subject: [PATCH 2758/3817] Force processing of context cancellation first This commit was moved from ipfs/go-unixfs@a5528c4a3478e76fac3838fcb5253f27594ee3e9 --- unixfs/hamt/hamt.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 33b706c00..b1d4c34e6 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -483,6 +483,15 @@ func makeAsyncTrieGetLinks(dagService ipld.DAGService, linkResults chan<- format } func emitResult(ctx context.Context, linkResults chan<- format.LinkResult, r format.LinkResult) { + // make sure that context cancel is processed first + // the reason is due to the concurrency of EnumerateChildrenAsync + // it's possible for EnumLinksAsync to complete and close the linkResults + // channel before this code runs + select { + case <-ctx.Done(): + return + default: + } select { case linkResults <- r: case <-ctx.Done(): From 2cdbd7aa250d4b951c11037f2944d1444789fad3 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 18 Oct 2018 16:34:29 +0100 Subject: [PATCH 2759/3817] Emit errors once at end This commit was moved from ipfs/go-unixfs@269f6d222e845017bdd44b0254f0275b59a0cd35 --- unixfs/hamt/hamt.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index b1d4c34e6..a823fa301 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -434,7 +434,10 @@ func (ds *Shard) EnumLinksAsync(ctx context.Context) (<-chan format.LinkResult, defer cancel() getLinks := makeAsyncTrieGetLinks(ds.dserv, linkResults) cset := cid.NewSet() - dag.EnumerateChildrenAsync(ctx, getLinks, ds.nd.Cid(), cset.Visit) + err := dag.EnumerateChildrenAsync(ctx, getLinks, ds.nd.Cid(), cset.Visit) + if err != nil { + emitResult(ctx, linkResults, format.LinkResult{Link: nil, Err: err}) + } }() return linkResults, nil } @@ -447,12 +450,10 @@ func makeAsyncTrieGetLinks(dagService ipld.DAGService, linkResults chan<- format return func(ctx context.Context, currentCid cid.Cid) ([]*ipld.Link, error) { node, err := dagService.Get(ctx, currentCid) if err != nil { - emitResult(ctx, linkResults, format.LinkResult{Link: nil, Err: err}) return nil, err } directoryShard, err := NewHamtFromDag(dagService, node) if err != nil { - emitResult(ctx, linkResults, format.LinkResult{Link: nil, Err: err}) return nil, err } @@ -463,7 +464,6 @@ func makeAsyncTrieGetLinks(dagService ipld.DAGService, linkResults chan<- format lnkLinkType, err := directoryShard.childLinkType(lnk) if err != nil { - emitResult(ctx, linkResults, format.LinkResult{Link: nil, Err: err}) return nil, err } if lnkLinkType == shardLink { From 6b818489a139004c9878ef637d676056dbd4db92 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Mon, 29 Oct 2018 15:05:38 -0700 Subject: [PATCH 2760/3817] feat(EnumLinksAsync): Remove error since it's unused This commit was moved from ipfs/go-unixfs@c54d0e47ad42f03de7736c2826c315de9f63b5a6 --- unixfs/hamt/hamt.go | 13 +++++-------- unixfs/hamt/hamt_test.go | 6 ++---- unixfs/io/directory.go | 8 ++++---- unixfs/io/directory_test.go | 5 +---- 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index a823fa301..3714c30a2 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -29,9 +29,8 @@ import ( cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" - "github.com/spaolacci/murmur3" - format "github.com/ipfs/go-unixfs" + "github.com/spaolacci/murmur3" ) const ( @@ -401,10 +400,8 @@ func (ds *Shard) getValue(ctx context.Context, hv *hashBits, key string, cb func func (ds *Shard) EnumLinks(ctx context.Context) ([]*ipld.Link, error) { var links []*ipld.Link - linkResults, err := ds.EnumLinksAsync(ctx) - if err != nil { - return nil, err - } + linkResults := ds.EnumLinksAsync(ctx) + for linkResult := range linkResults { if linkResult.Err != nil { return links, linkResult.Err @@ -426,7 +423,7 @@ func (ds *Shard) ForEachLink(ctx context.Context, f func(*ipld.Link) error) erro // EnumLinksAsync returns a channel which will receive Links in the directory // as they are enumerated, where order is not gauranteed -func (ds *Shard) EnumLinksAsync(ctx context.Context) (<-chan format.LinkResult, error) { +func (ds *Shard) EnumLinksAsync(ctx context.Context) <-chan format.LinkResult { linkResults := make(chan format.LinkResult) ctx, cancel := context.WithCancel(ctx) go func() { @@ -439,7 +436,7 @@ func (ds *Shard) EnumLinksAsync(ctx context.Context) (<-chan format.LinkResult, emitResult(ctx, linkResults, format.LinkResult{Link: nil, Err: err}) } }() - return linkResults, nil + return linkResults } // makeAsyncTrieGetLinks builds a getLinks function that can be used with EnumerateChildrenAsync diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 077976051..1483fcd9f 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -337,10 +337,8 @@ func TestEnumLinksAsync(t *testing.T) { t.Fatal(err) } - linkResults, err := nds.EnumLinksAsync(ctx) - if err != nil { - t.Fatal(err) - } + linkResults := nds.EnumLinksAsync(ctx) + var linksB []*ipld.Link for linkResult := range linkResults { diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index 5a4f638b9..2e0227623 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -41,7 +41,7 @@ type Directory interface { // EnumLinksAsync returns a channel which will receive Links in the directory // as they are enumerated, where order is not gauranteed - EnumLinksAsync(context.Context) (<-chan format.LinkResult, error) + EnumLinksAsync(context.Context) <-chan format.LinkResult // Links returns the all the links in the directory node. Links(context.Context) ([]*ipld.Link, error) @@ -148,7 +148,7 @@ func (d *BasicDirectory) AddChild(ctx context.Context, name string, node ipld.No // EnumLinksAsync returns a channel which will receive Links in the directory // as they are enumerated, where order is not gauranteed -func (d *BasicDirectory) EnumLinksAsync(ctx context.Context) (<-chan format.LinkResult, error) { +func (d *BasicDirectory) EnumLinksAsync(ctx context.Context) <-chan format.LinkResult { linkResults := make(chan format.LinkResult) go func() { defer close(linkResults) @@ -163,7 +163,7 @@ func (d *BasicDirectory) EnumLinksAsync(ctx context.Context) (<-chan format.Link } } }() - return linkResults, nil + return linkResults } // ForEachLink implements the `Directory` interface. @@ -253,7 +253,7 @@ func (d *HAMTDirectory) ForEachLink(ctx context.Context, f func(*ipld.Link) erro // EnumLinksAsync returns a channel which will receive Links in the directory // as they are enumerated, where order is not gauranteed -func (d *HAMTDirectory) EnumLinksAsync(ctx context.Context) (<-chan format.LinkResult, error) { +func (d *HAMTDirectory) EnumLinksAsync(ctx context.Context) <-chan format.LinkResult { return d.shard.EnumLinksAsync(ctx) } diff --git a/unixfs/io/directory_test.go b/unixfs/io/directory_test.go index 6f621e977..12c481753 100644 --- a/unixfs/io/directory_test.go +++ b/unixfs/io/directory_test.go @@ -158,10 +158,7 @@ func TestDirBuilder(t *testing.T) { t.Fatal("wrong number of links", len(links), count) } - linkResults, err := dir.EnumLinksAsync(ctx) - if err != nil { - t.Fatal(err) - } + linkResults := dir.EnumLinksAsync(ctx) asyncNames := make(map[string]bool) var asyncLinks []*ipld.Link From abd0d4dddf5f23500d65ce04ab82a0dd8b7baa74 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 30 Oct 2018 08:48:49 -0700 Subject: [PATCH 2761/3817] coreapi: fix errisdir JavaScript expects this to be "this dag node is a directory". I'm almost of a mind to say "don't parse errors" but, well, we don't give any better alternatives. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@dc979581ae4496d6e2c57a019cb133d780585df7 --- coreiface/errors.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/errors.go b/coreiface/errors.go index 072275409..4ee3026ff 100644 --- a/coreiface/errors.go +++ b/coreiface/errors.go @@ -3,6 +3,6 @@ package iface import "errors" var ( - ErrIsDir = errors.New("object is a directory") + ErrIsDir = errors.New("this dag node is a directory") ErrOffline = errors.New("this action must be run in online mode, try running 'ipfs daemon' first") ) From b5f7f3348973656cc732acb7414b28f8b1326156 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 30 Oct 2018 08:42:23 -0700 Subject: [PATCH 2762/3817] use a consistent error across ResolveToLastNode and ResolveLinks Really, we just need to get rid of some of this code but this is *a* fix. This commit was moved from ipfs/go-path@cc0c0f32286165c2f65793cbb64bae10b2b3f3d4 --- path/resolver/resolver.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 352004f52..67bb9f6fb 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -83,6 +83,9 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (cid. } if err != nil { + if err == dag.ErrLinkNotFound { + err = ErrNoLink{Name: p[0], Node: nd.Cid()} + } return cid.Cid{}, nil, err } @@ -101,6 +104,9 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (cid. // Confirm the path exists within the object val, rest, err := nd.Resolve(p) if err != nil { + if err == dag.ErrLinkNotFound { + err = ErrNoLink{Name: p[0], Node: nd.Cid()} + } return cid.Cid{}, nil, err } From 150d354c2f40a1cbb95d84d4ae3f60c882ab9bb0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 30 Oct 2018 09:27:41 -0700 Subject: [PATCH 2763/3817] gx: update go-path fixes the changed path cat error causing the js-ipfs-api tests to fail License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@cc45cf0ac009f5b7f4c6f55cf3d4636389b3198f --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 2 +- namesys/proquint.go | 2 +- namesys/publisher.go | 2 +- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 4aa4fcfbd..7923241ed 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" + path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 78ca295a8..c083c35b2 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" + path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 7b1b9e35c..fb76a060f 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" + path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index 093798faf..47b4e0c48 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" + path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 2bd3c42ef..896801671 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" + path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/namesys.go b/namesys/namesys.go index 0080533ba..4cbaf261e 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,7 +5,7 @@ import ( "strings" "time" - path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" + path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 1e92547e3..856be40ed 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -8,9 +8,9 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" pstoremem "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore/pstoremem" - path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" diff --git a/namesys/proquint.go b/namesys/proquint.go index c906b1861..8693f55dc 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,8 +4,8 @@ import ( "context" "errors" + path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" - path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 76d65a42b..95d0e358b 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,7 +7,7 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" + path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" ft "gx/ipfs/QmfB3oNXGGq9S4B2a9YeCajoATms3Zw2VvDm8fK7VeLSV8/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index e8a9c3631..c2fc7242b 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" + path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 08ac7b7b1..8b406e644 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" + path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" pstore "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 24e93112c..8694e2bdd 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" + path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" testutil "gx/ipfs/Qma6ESRQTf1ZLPgzpCwDTqQJefPnU6uLvMjP18vK8EWp8L/go-testutil" diff --git a/namesys/routing.go b/namesys/routing.go index 073a8341b..403df4ff6 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,7 +5,7 @@ import ( "strings" "time" - path "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" + path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" From 41c1c071623c7781041fb09e69c641db9642bc1d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 30 Oct 2018 09:27:41 -0700 Subject: [PATCH 2764/3817] gx: update go-path fixes the changed path cat error causing the js-ipfs-api tests to fail License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@6fcff53bc29de056f4b948bb0bfaa468546d1c66 --- coreiface/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/path.go b/coreiface/path.go index 5a3d1128b..79dac201d 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmZbQUht8hzKmQxLHnzY14WSuoQqYkR5mn4cunchyWPmhn/go-path" + ipfspath "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) From 905993f35b75a302e90a1d30adfdeff75ee4fca1 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 1 Nov 2018 14:06:19 -0700 Subject: [PATCH 2765/3817] Update go-mfs and go-unixfs So we can get go-unixfs v1.2.0 License: MIT Signed-off-by: hannahhoward This commit was moved from ipfs/go-namesys@9fe2d78555f93787db860593a0356080b259ae03 --- namesys/namesys_test.go | 2 +- namesys/publisher.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 856be40ed..b32d5d663 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -11,11 +11,11 @@ import ( path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" pstoremem "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore/pstoremem" + "gx/ipfs/QmUaZkqxmKvUX16F8XeAAk9LVvmNMktvbhcx4PG4s8SqDG/go-unixfs" ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" offroute "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/offline" - "gx/ipfs/QmfB3oNXGGq9S4B2a9YeCajoATms3Zw2VvDm8fK7VeLSV8/go-unixfs" ) type mockResolver struct { diff --git a/namesys/publisher.go b/namesys/publisher.go index 95d0e358b..b1a6c3679 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -8,7 +8,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" - ft "gx/ipfs/QmfB3oNXGGq9S4B2a9YeCajoATms3Zw2VvDm8fK7VeLSV8/go-unixfs" + ft "gx/ipfs/QmUaZkqxmKvUX16F8XeAAk9LVvmNMktvbhcx4PG4s8SqDG/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" From 6cc67d0eb2b92bd082ebc731d0dfbf304deafdbb Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Nov 2018 13:16:34 -0700 Subject: [PATCH 2766/3817] gx: update go-ipld-cbor (might as well do this at the same time) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@689ef22607a8138744624af78279e6dc141390da --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 4 ++-- namesys/proquint.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 7923241ed..9c691b428 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" + path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index c083c35b2..a52afcfcc 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" + path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index fb76a060f..894ebb8c7 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" + path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index 47b4e0c48..b81b689a4 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" + path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 896801671..0600b24cc 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" + path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/namesys.go b/namesys/namesys.go index 4cbaf261e..6493e5dcd 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,7 +5,7 @@ import ( "strings" "time" - path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" + path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index b32d5d663..693889670 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -8,13 +8,13 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" pstoremem "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore/pstoremem" - "gx/ipfs/QmUaZkqxmKvUX16F8XeAAk9LVvmNMktvbhcx4PG4s8SqDG/go-unixfs" + path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" + "gx/ipfs/Qmcba8ak38WXFWuaLak5pfJPUDFxcSWbkycBNQfmarpuTv/go-unixfs" offroute "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/offline" ) diff --git a/namesys/proquint.go b/namesys/proquint.go index 8693f55dc..6fa5c5211 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,7 +4,7 @@ import ( "context" "errors" - path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" + path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/publisher.go b/namesys/publisher.go index b1a6c3679..7a0b3c19c 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,8 +7,8 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" - ft "gx/ipfs/QmUaZkqxmKvUX16F8XeAAk9LVvmNMktvbhcx4PG4s8SqDG/go-unixfs" + path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" + ft "gx/ipfs/Qmcba8ak38WXFWuaLak5pfJPUDFxcSWbkycBNQfmarpuTv/go-unixfs" ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index c2fc7242b..ea5968161 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" + path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 8b406e644..766aaa6f5 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" + path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" pstore "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 8694e2bdd..f61219cd7 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" + path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" testutil "gx/ipfs/Qma6ESRQTf1ZLPgzpCwDTqQJefPnU6uLvMjP18vK8EWp8L/go-testutil" diff --git a/namesys/routing.go b/namesys/routing.go index 403df4ff6..40f235c4e 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,7 +5,7 @@ import ( "strings" "time" - path "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" + path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" From 9054f4c4c977dda2774bcf8493b2087fbf097d4d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Nov 2018 13:16:34 -0700 Subject: [PATCH 2767/3817] gx: update go-ipld-cbor (might as well do this at the same time) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@bca0017a7a95666fff03c83e4a8e38b51d6158f3 --- coreiface/options/unixfs.go | 2 +- coreiface/path.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 8a9137887..d541adac7 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -6,7 +6,7 @@ import ( cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - dag "gx/ipfs/QmSei8kFMfqdJq7Q68d2LMnHbTWKKg2daA29ezUYFAUNgc/go-merkledag" + dag "gx/ipfs/QmXyuFW7at4r9dxRAbrPU9JpHW5aqngAFyxvyhvYjzxRKS/go-merkledag" ) type Layout int diff --git a/coreiface/path.go b/coreiface/path.go index 79dac201d..034bfffc3 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmT3rzed1ppXefourpmoZ7tyVQfsGPQZ1pHDngLmCvXxd3/go-path" + ipfspath "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ) From 09a5c1460bd7b60023135a7e7207b2f4aa11e48c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Nov 2018 13:16:34 -0700 Subject: [PATCH 2768/3817] gx: update go-ipld-cbor (might as well do this at the same time) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@bdacbf7d68a53592f27dc50a541158fa5c41544b --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index f3813f625..f3516b02f 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmSei8kFMfqdJq7Q68d2LMnHbTWKKg2daA29ezUYFAUNgc/go-merkledag" + dag "gx/ipfs/QmXyuFW7at4r9dxRAbrPU9JpHW5aqngAFyxvyhvYjzxRKS/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" posinfo "gx/ipfs/QmQyUyYcpKG1u53V7N25qRTGw5XwaAxTMKXbduqHotQztg/go-ipfs-posinfo" From 8cd9c4ba747494f3ab2e2a256c317ed2aa49829e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Nov 2018 13:16:34 -0700 Subject: [PATCH 2769/3817] gx: update go-ipld-cbor (might as well do this at the same time) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@10d16d09bf21592116489851669a435bc2b304d1 --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 2 +- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index a1bfa0278..9d4563643 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmSei8kFMfqdJq7Q68d2LMnHbTWKKg2daA29ezUYFAUNgc/go-merkledag" bserv "gx/ipfs/QmWfhv1D18DRSiSm73r4QGcByspzPtxxRTcmHW3axFXZo8/go-blockservice" + dag "gx/ipfs/QmXyuFW7at4r9dxRAbrPU9JpHW5aqngAFyxvyhvYjzxRKS/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 88bd1aec6..af6a365ba 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmSei8kFMfqdJq7Q68d2LMnHbTWKKg2daA29ezUYFAUNgc/go-merkledag" + mdag "gx/ipfs/QmXyuFW7at4r9dxRAbrPU9JpHW5aqngAFyxvyhvYjzxRKS/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index d92d8a837..bd23db462 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - mdag "gx/ipfs/QmSei8kFMfqdJq7Q68d2LMnHbTWKKg2daA29ezUYFAUNgc/go-merkledag" bs "gx/ipfs/QmWfhv1D18DRSiSm73r4QGcByspzPtxxRTcmHW3axFXZo8/go-blockservice" + mdag "gx/ipfs/QmXyuFW7at4r9dxRAbrPU9JpHW5aqngAFyxvyhvYjzxRKS/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 439f4384e..d235960bd 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmSei8kFMfqdJq7Q68d2LMnHbTWKKg2daA29ezUYFAUNgc/go-merkledag" + "gx/ipfs/QmXyuFW7at4r9dxRAbrPU9JpHW5aqngAFyxvyhvYjzxRKS/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 919efe6d0..d59f5ea6c 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmSei8kFMfqdJq7Q68d2LMnHbTWKKg2daA29ezUYFAUNgc/go-merkledag" bserv "gx/ipfs/QmWfhv1D18DRSiSm73r4QGcByspzPtxxRTcmHW3axFXZo8/go-blockservice" + dag "gx/ipfs/QmXyuFW7at4r9dxRAbrPU9JpHW5aqngAFyxvyhvYjzxRKS/go-merkledag" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" From bc68b9db0609bc8f54d487f4f2637af4a8ee01dd Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Nov 2018 17:15:21 -0700 Subject: [PATCH 2770/3817] gx: update go-log and sha256 fixes #5709 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@3df5b272a73be70bf4cc49ec2251ddee25f95689 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 4 ++-- namesys/ipns_resolver_validation_test.go | 26 ++++++++++++------------ namesys/namesys.go | 10 ++++----- namesys/namesys_test.go | 14 ++++++------- namesys/proquint.go | 2 +- namesys/publisher.go | 14 ++++++------- namesys/publisher_test.go | 14 ++++++------- namesys/republisher/repub.go | 10 ++++----- namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 10 ++++----- namesys/routing.go | 18 ++++++++-------- 14 files changed, 67 insertions(+), 67 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 9c691b428..3e46548ca 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" + path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index a52afcfcc..2cb5e462d 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" + path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 894ebb8c7..bba548afc 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" + path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index b81b689a4..a5ed85605 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,9 +36,9 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" + path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" - ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 0600b24cc..5ebb64189 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,24 +5,24 @@ import ( "testing" "time" - path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" + path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" - pstore "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore" - pstoremem "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore/pstoremem" - testutil "gx/ipfs/Qma6ESRQTf1ZLPgzpCwDTqQJefPnU6uLvMjP18vK8EWp8L/go-testutil" - record "gx/ipfs/Qma9Eqp16mNHDX1EL73pcxhFfzbyXVcAYtaDd1xdmDRDtL/go-libp2p-record" - ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" + ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" + u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" + mockrouting "gx/ipfs/QmNuVissmH2ftUd4ADvhm9WER3351wTYduY1EeDDGtP1tM/go-ipfs-routing/mock" + offline "gx/ipfs/QmNuVissmH2ftUd4ADvhm9WER3351wTYduY1EeDDGtP1tM/go-ipfs-routing/offline" + record "gx/ipfs/QmSoeYGNm8v8jAF49hX7UwHwkXjoeobSrn9sya5NPPsxXP/go-libp2p-record" + pstore "gx/ipfs/QmUymf8fJtideyv3z727BcZUifGBjMZMpCJqu3Gxk5aRUk/go-libp2p-peerstore" + pstoremem "gx/ipfs/QmUymf8fJtideyv3z727BcZUifGBjMZMpCJqu3Gxk5aRUk/go-libp2p-peerstore/pstoremem" + routing "gx/ipfs/QmYyg3UnyiQubxjs4uhKixPxR7eeKrhJ5Vyz6Et4Tet18B/go-libp2p-routing" + ropts "gx/ipfs/QmYyg3UnyiQubxjs4uhKixPxR7eeKrhJ5Vyz6Et4Tet18B/go-libp2p-routing/options" + ipns "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns" + testutil "gx/ipfs/QmZXjR5X1p4KrQ967cTsy4MymMzUM8mZECF3PV8UcN4o3g/go-testutil" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" - routing "gx/ipfs/QmcQ81jSyWCp1jpkQ8CMbtpXT3jK7Wg6ZtYmoyWFgBoF9c/go-libp2p-routing" - ropts "gx/ipfs/QmcQ81jSyWCp1jpkQ8CMbtpXT3jK7Wg6ZtYmoyWFgBoF9c/go-libp2p-routing/options" - mockrouting "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/mock" - offline "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/offline" + peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 6493e5dcd..4b45025a4 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,17 +5,17 @@ import ( "strings" "time" - path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" + path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" - mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" - peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" + routing "gx/ipfs/QmYyg3UnyiQubxjs4uhKixPxR7eeKrhJ5Vyz6Et4Tet18B/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" - routing "gx/ipfs/QmcQ81jSyWCp1jpkQ8CMbtpXT3jK7Wg6ZtYmoyWFgBoF9c/go-libp2p-routing" + peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" + mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 693889670..17f8277b3 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,15 +7,15 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" - pstoremem "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore/pstoremem" - path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" - ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" + ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" + offroute "gx/ipfs/QmNuVissmH2ftUd4ADvhm9WER3351wTYduY1EeDDGtP1tM/go-ipfs-routing/offline" + path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" + pstoremem "gx/ipfs/QmUymf8fJtideyv3z727BcZUifGBjMZMpCJqu3Gxk5aRUk/go-libp2p-peerstore/pstoremem" + "gx/ipfs/QmXLCwhHh7bxRsBnCKNE9BAN87V44aSxXLquZYTtjr6fZ3/go-unixfs" + ipns "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" - "gx/ipfs/Qmcba8ak38WXFWuaLak5pfJPUDFxcSWbkycBNQfmarpuTv/go-unixfs" - offroute "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/offline" + peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index 6fa5c5211..3849b937a 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,7 +4,7 @@ import ( "context" "errors" - path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" + path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/publisher.go b/namesys/publisher.go index 7a0b3c19c..9da516903 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,16 +7,16 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" - ft "gx/ipfs/Qmcba8ak38WXFWuaLak5pfJPUDFxcSWbkycBNQfmarpuTv/go-unixfs" + path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" + ft "gx/ipfs/QmXLCwhHh7bxRsBnCKNE9BAN87V44aSxXLquZYTtjr6fZ3/go-unixfs" - ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" - ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" - pb "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns/pb" + ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" + routing "gx/ipfs/QmYyg3UnyiQubxjs4uhKixPxR7eeKrhJ5Vyz6Et4Tet18B/go-libp2p-routing" + ipns "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns" + pb "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns/pb" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dsquery "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" - routing "gx/ipfs/QmcQ81jSyWCp1jpkQ8CMbtpXT3jK7Wg6ZtYmoyWFgBoF9c/go-libp2p-routing" + peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 4f5927206..987493cc2 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -6,15 +6,15 @@ import ( "testing" "time" - ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - dshelp "gx/ipfs/QmS73grfbWgWrNztd8Lns9GCG3jjRNDfcPYg2VYQzKDZSt/go-ipfs-ds-help" - ma "gx/ipfs/QmT4U94DnD8FRfqr21obWY32HLM5VExccPKMjQHofeYqr9/go-multiaddr" - peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" - testutil "gx/ipfs/Qma6ESRQTf1ZLPgzpCwDTqQJefPnU6uLvMjP18vK8EWp8L/go-testutil" - ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" + ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" + mockrouting "gx/ipfs/QmNuVissmH2ftUd4ADvhm9WER3351wTYduY1EeDDGtP1tM/go-ipfs-routing/mock" + ma "gx/ipfs/QmRKLtwMw131aK7ugC3G7ybpumMz78YrJe5dzneyindvG1/go-multiaddr" + ipns "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns" + testutil "gx/ipfs/QmZXjR5X1p4KrQ967cTsy4MymMzUM8mZECF3PV8UcN4o3g/go-testutil" + dshelp "gx/ipfs/QmaHSUAhuf9WG3mzJUd1fLDsQGvjsaQdUE7w5cZncz9AcB/go-ipfs-ds-help" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" - mockrouting "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/mock" + peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" ) type identity struct { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index ea5968161..649613c5c 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,15 +7,15 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" + path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" - ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + ic "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" - logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" - pb "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns/pb" + pb "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns/pb" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" + peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" + logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 766aaa6f5..b2c1b29be 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" + path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - pstore "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore" - mocknet "gx/ipfs/QmUDTcnDp2WssbmiDLC6aYurUeyt7QeRakHUQMxA2mZ5iB/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmUymf8fJtideyv3z727BcZUifGBjMZMpCJqu3Gxk5aRUk/go-libp2p-peerstore" + mocknet "gx/ipfs/QmXnpYYg2onGLXVxM4Q5PEFcx29k8zeJQkPeLAk9h9naxg/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index f61219cd7..f3556c4c2 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,14 +6,14 @@ import ( "testing" "time" - path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" + path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" - peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" - testutil "gx/ipfs/Qma6ESRQTf1ZLPgzpCwDTqQJefPnU6uLvMjP18vK8EWp8L/go-testutil" - ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" + mockrouting "gx/ipfs/QmNuVissmH2ftUd4ADvhm9WER3351wTYduY1EeDDGtP1tM/go-ipfs-routing/mock" + ipns "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns" + testutil "gx/ipfs/QmZXjR5X1p4KrQ967cTsy4MymMzUM8mZECF3PV8UcN4o3g/go-testutil" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" - mockrouting "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/mock" + peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index 40f235c4e..aabbc9511 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,19 +5,19 @@ import ( "strings" "time" - path "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" + path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - dht "gx/ipfs/QmQHnqaNULV8WeUGgh97o9K3KAW6kWQmDyNf9UuikgnPTe/go-libp2p-kad-dht" - peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" - logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" - ipns "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns" - pb "gx/ipfs/QmaRFtZhVAwXBk4Z3zEsvjScH9fjsDZmhXfa1Gm8eMb9cg/go-ipns/pb" - routing "gx/ipfs/QmcQ81jSyWCp1jpkQ8CMbtpXT3jK7Wg6ZtYmoyWFgBoF9c/go-libp2p-routing" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + routing "gx/ipfs/QmYyg3UnyiQubxjs4uhKixPxR7eeKrhJ5Vyz6Et4Tet18B/go-libp2p-routing" + ipns "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns" + pb "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns/pb" + dht "gx/ipfs/QmadRyQYRn64xHb5HKy2jRFp2Der643Cgo7NEjFgs4MX2k/go-libp2p-kad-dht" + peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" + logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" + mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) var log = logging.Logger("namesys") From 93332aaa1821369c585b86e957acd42a09d3f1dd Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Nov 2018 17:15:21 -0700 Subject: [PATCH 2771/3817] gx: update go-log and sha256 fixes #5709 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@1c6351bc2b6ca351b0aaf8f3a6eca43714af21e1 --- coreiface/coreapi.go | 2 +- coreiface/dag.go | 2 +- coreiface/dht.go | 4 ++-- coreiface/key.go | 2 +- coreiface/object.go | 4 ++-- coreiface/options/block.go | 4 ++-- coreiface/options/dag.go | 2 +- coreiface/options/unixfs.go | 6 +++--- coreiface/path.go | 4 ++-- coreiface/pubsub.go | 2 +- coreiface/swarm.go | 8 ++++---- coreiface/unixfs.go | 2 +- 12 files changed, 21 insertions(+), 21 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index b744a207a..bab4fc13b 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -5,7 +5,7 @@ package iface import ( "context" - ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" + ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" ) // CoreAPI defines an unified interface to IPFS for Go programs diff --git a/coreiface/dag.go b/coreiface/dag.go index 6cca5b9e6..eb9e2da4a 100644 --- a/coreiface/dag.go +++ b/coreiface/dag.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" + ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" ) // DagOps groups operations that can be batched together diff --git a/coreiface/dht.go b/coreiface/dht.go index 38a0f7348..c4eef9379 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -5,8 +5,8 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" - pstore "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore" + pstore "gx/ipfs/QmUymf8fJtideyv3z727BcZUifGBjMZMpCJqu3Gxk5aRUk/go-libp2p-peerstore" + peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" ) // DhtAPI specifies the interface to the DHT diff --git a/coreiface/key.go b/coreiface/key.go index 9e7bfee28..36a74688b 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -5,7 +5,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" + "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" ) // Key specifies the interface to Keys in KeyAPI Keystore diff --git a/coreiface/object.go b/coreiface/object.go index 229f69869..ba6f5a95d 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -6,8 +6,8 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" ) // ObjectStat provides information about dag nodes diff --git a/coreiface/options/block.go b/coreiface/options/block.go index 6603136f3..ea4ae26bb 100644 --- a/coreiface/options/block.go +++ b/coreiface/options/block.go @@ -2,8 +2,8 @@ package options import ( "fmt" - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) type BlockPutSettings struct { diff --git a/coreiface/options/dag.go b/coreiface/options/dag.go index 4fdff0489..9cccba585 100644 --- a/coreiface/options/dag.go +++ b/coreiface/options/dag.go @@ -3,7 +3,7 @@ package options import ( "math" - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ) type DagPutSettings struct { diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index d541adac7..9b0683a11 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -4,9 +4,9 @@ import ( "errors" "fmt" - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash" - dag "gx/ipfs/QmXyuFW7at4r9dxRAbrPU9JpHW5aqngAFyxvyhvYjzxRKS/go-merkledag" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + dag "gx/ipfs/QmaDBne4KeY3UepeqSVKYpSmQGa3q9zP6x3LfVF2UjF3Hc/go-merkledag" + mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) type Layout int diff --git a/coreiface/path.go b/coreiface/path.go index 034bfffc3..f5e7aeb4c 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,9 +1,9 @@ package iface import ( - ipfspath "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path" + ipfspath "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ) //TODO: merge with ipfspath so we don't depend on it diff --git a/coreiface/pubsub.go b/coreiface/pubsub.go index b3f3f6b76..93e429574 100644 --- a/coreiface/pubsub.go +++ b/coreiface/pubsub.go @@ -6,7 +6,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" + peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" ) // PubSubSubscription is an active PubSub subscription diff --git a/coreiface/swarm.go b/coreiface/swarm.go index d4b92c017..b830a0817 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -5,11 +5,11 @@ import ( "errors" "time" - ma "gx/ipfs/QmT4U94DnD8FRfqr21obWY32HLM5VExccPKMjQHofeYqr9/go-multiaddr" - "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer" - pstore "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore" - net "gx/ipfs/QmXuRkCR7BNQa9uqfpTiFWsTQLzmTWYg91Ja1w95gnqb6u/go-libp2p-net" + ma "gx/ipfs/QmRKLtwMw131aK7ugC3G7ybpumMz78YrJe5dzneyindvG1/go-multiaddr" + net "gx/ipfs/QmRKbEchaYADxSCyyjhDh4cTrUby8ftXUb8MRLBTHQYupw/go-libp2p-net" + pstore "gx/ipfs/QmUymf8fJtideyv3z727BcZUifGBjMZMpCJqu3Gxk5aRUk/go-libp2p-peerstore" "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol" + "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" ) var ( diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 6fd33ad2c..002635d99 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -6,8 +6,8 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" files "gx/ipfs/QmZMWMvWMVKCbHetJ4RgndbuEF1io2UpUxwQwtNjtYPzSC/go-ipfs-files" + ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" ) // TODO: ideas on making this more coreapi-ish without breaking the http API? From 0fe9d3d93426ed44410f2c74ef96fd105ac43de7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Nov 2018 17:15:21 -0700 Subject: [PATCH 2772/3817] gx: update go-log and sha256 fixes #5709 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@a9ac2cfb972a3c0c4f6d6dc79ebdf42c60abc1ec --- filestore/filestore.go | 10 +++++----- filestore/filestore_test.go | 8 ++++---- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 6 +++--- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 19f5fd209..04f3ef683 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,12 +11,12 @@ import ( "context" "errors" - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - posinfo "gx/ipfs/QmQyUyYcpKG1u53V7N25qRTGw5XwaAxTMKXbduqHotQztg/go-ipfs-posinfo" - blocks "gx/ipfs/QmRcHuYzAyswytBuMF78rj3LTChYszomRFXNg4685ZN1WM/go-block-format" - logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" + posinfo "gx/ipfs/QmR6YMs8EkXQLXNwQKxLnQp2VBZSepoEJ8KCZAyanJHhJu/go-ipfs-posinfo" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + blockstore "gx/ipfs/QmSNLNnL3kq3A1NGdQA9AtgxM9CWKiiSEup3W435jCkRQS/go-ipfs-blockstore" + blocks "gx/ipfs/QmWoXtvgC8inqFkAATB7cp2Dax7XBi9VDvSg9RCCZufmRk/go-block-format" dsq "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" - blockstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" + logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index f3516b02f..bfe6b1354 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,12 +7,12 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmXyuFW7at4r9dxRAbrPU9JpHW5aqngAFyxvyhvYjzxRKS/go-merkledag" + dag "gx/ipfs/QmaDBne4KeY3UepeqSVKYpSmQGa3q9zP6x3LfVF2UjF3Hc/go-merkledag" - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - posinfo "gx/ipfs/QmQyUyYcpKG1u53V7N25qRTGw5XwaAxTMKXbduqHotQztg/go-ipfs-posinfo" + posinfo "gx/ipfs/QmR6YMs8EkXQLXNwQKxLnQp2VBZSepoEJ8KCZAyanJHhJu/go-ipfs-posinfo" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + blockstore "gx/ipfs/QmSNLNnL3kq3A1NGdQA9AtgxM9CWKiiSEup3W435jCkRQS/go-ipfs-blockstore" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" - blockstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 74b0131b8..fa4ba0dcc 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,14 +10,14 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - posinfo "gx/ipfs/QmQyUyYcpKG1u53V7N25qRTGw5XwaAxTMKXbduqHotQztg/go-ipfs-posinfo" - blocks "gx/ipfs/QmRcHuYzAyswytBuMF78rj3LTChYszomRFXNg4685ZN1WM/go-block-format" - dshelp "gx/ipfs/QmS73grfbWgWrNztd8Lns9GCG3jjRNDfcPYg2VYQzKDZSt/go-ipfs-ds-help" + posinfo "gx/ipfs/QmR6YMs8EkXQLXNwQKxLnQp2VBZSepoEJ8KCZAyanJHhJu/go-ipfs-posinfo" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + blockstore "gx/ipfs/QmSNLNnL3kq3A1NGdQA9AtgxM9CWKiiSEup3W435jCkRQS/go-ipfs-blockstore" + blocks "gx/ipfs/QmWoXtvgC8inqFkAATB7cp2Dax7XBi9VDvSg9RCCZufmRk/go-block-format" + dshelp "gx/ipfs/QmaHSUAhuf9WG3mzJUd1fLDsQGvjsaQdUE7w5cZncz9AcB/go-ipfs-ds-help" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dsns "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/namespace" dsq "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" - blockstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) diff --git a/filestore/util.go b/filestore/util.go index 39df78c6f..f7af7f601 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,11 +6,11 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - dshelp "gx/ipfs/QmS73grfbWgWrNztd8Lns9GCG3jjRNDfcPYg2VYQzKDZSt/go-ipfs-ds-help" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + blockstore "gx/ipfs/QmSNLNnL3kq3A1NGdQA9AtgxM9CWKiiSEup3W435jCkRQS/go-ipfs-blockstore" + dshelp "gx/ipfs/QmaHSUAhuf9WG3mzJUd1fLDsQGvjsaQdUE7w5cZncz9AcB/go-ipfs-ds-help" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dsq "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" - blockstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" ) // Status is used to identify the state of the block data referenced From e2c9f0f0998c03b902a20ef43e783a6c2e952769 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Nov 2018 17:15:21 -0700 Subject: [PATCH 2773/3817] gx: update go-log and sha256 fixes #5709 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@f5ffd000e4b0d6c48a3b12ad8a2376a05ba28c05 --- pinning/pinner/gc/gc.go | 18 +++++++++--------- pinning/pinner/pin.go | 8 ++++---- pinning/pinner/pin_test.go | 12 ++++++------ pinning/pinner/set.go | 6 +++--- pinning/pinner/set_test.go | 10 +++++----- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 9d4563643..8c0b2e879 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,16 +8,16 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmWfhv1D18DRSiSm73r4QGcByspzPtxxRTcmHW3axFXZo8/go-blockservice" - dag "gx/ipfs/QmXyuFW7at4r9dxRAbrPU9JpHW5aqngAFyxvyhvYjzxRKS/go-merkledag" - - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" - offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" - "gx/ipfs/QmVkMRSkXrpjqrroEXWuYBvDBnXCdMMY6gsKicBGVGUqKT/go-verifcid" - logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" + bserv "gx/ipfs/QmVPeMNK9DfGLXDZzs2W4RoFWC9Zq1EnLGmLXtYtWrNdcW/go-blockservice" + dag "gx/ipfs/QmaDBne4KeY3UepeqSVKYpSmQGa3q9zP6x3LfVF2UjF3Hc/go-merkledag" + + offline "gx/ipfs/QmPpnbwgAuvhUkA9jGooR88ZwZtTUHXXvoQNKdjZC6nYku/go-ipfs-exchange-offline" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + bstore "gx/ipfs/QmSNLNnL3kq3A1NGdQA9AtgxM9CWKiiSEup3W435jCkRQS/go-ipfs-blockstore" + "gx/ipfs/QmYMQuypUbgsdNHmuCBSUJV6wdQVsBHRivNAp3efHJwZJD/go-verifcid" dstore "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" - bstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" + ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" + logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index af6a365ba..e8ebe8c3e 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,12 +10,12 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmXyuFW7at4r9dxRAbrPU9JpHW5aqngAFyxvyhvYjzxRKS/go-merkledag" + mdag "gx/ipfs/QmaDBne4KeY3UepeqSVKYpSmQGa3q9zP6x3LfVF2UjF3Hc/go-merkledag" - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" - logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" + ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" + logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index bd23db462..b58691860 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" - bs "gx/ipfs/QmWfhv1D18DRSiSm73r4QGcByspzPtxxRTcmHW3axFXZo8/go-blockservice" - mdag "gx/ipfs/QmXyuFW7at4r9dxRAbrPU9JpHW5aqngAFyxvyhvYjzxRKS/go-merkledag" + bs "gx/ipfs/QmVPeMNK9DfGLXDZzs2W4RoFWC9Zq1EnLGmLXtYtWrNdcW/go-blockservice" + mdag "gx/ipfs/QmaDBne4KeY3UepeqSVKYpSmQGa3q9zP6x3LfVF2UjF3Hc/go-merkledag" - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - util "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" - offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" + util "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" + offline "gx/ipfs/QmPpnbwgAuvhUkA9jGooR88ZwZtTUHXXvoQNKdjZC6nYku/go-ipfs-exchange-offline" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + blockstore "gx/ipfs/QmSNLNnL3kq3A1NGdQA9AtgxM9CWKiiSEup3W435jCkRQS/go-ipfs-blockstore" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" - blockstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" ) var rand = util.NewTimeSeededRand() diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index d235960bd..35b76eb29 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,10 +10,10 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmXyuFW7at4r9dxRAbrPU9JpHW5aqngAFyxvyhvYjzxRKS/go-merkledag" + "gx/ipfs/QmaDBne4KeY3UepeqSVKYpSmQGa3q9zP6x3LfVF2UjF3Hc/go-merkledag" - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index d59f5ea6c..82e4679b1 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,14 +5,14 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmWfhv1D18DRSiSm73r4QGcByspzPtxxRTcmHW3axFXZo8/go-blockservice" - dag "gx/ipfs/QmXyuFW7at4r9dxRAbrPU9JpHW5aqngAFyxvyhvYjzxRKS/go-merkledag" + bserv "gx/ipfs/QmVPeMNK9DfGLXDZzs2W4RoFWC9Zq1EnLGmLXtYtWrNdcW/go-blockservice" + dag "gx/ipfs/QmaDBne4KeY3UepeqSVKYpSmQGa3q9zP6x3LfVF2UjF3Hc/go-merkledag" - cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" - offline "gx/ipfs/QmT6dHGp3UYd3vUMpy7rzX2CXQv7HLcj42Vtq8qwwjgASb/go-ipfs-exchange-offline" + offline "gx/ipfs/QmPpnbwgAuvhUkA9jGooR88ZwZtTUHXXvoQNKdjZC6nYku/go-ipfs-exchange-offline" + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + blockstore "gx/ipfs/QmSNLNnL3kq3A1NGdQA9AtgxM9CWKiiSEup3W435jCkRQS/go-ipfs-blockstore" ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" dsq "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" - blockstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore" ) func ignoreCids(_ cid.Cid) {} From 4309543c6072a930005d7d4f81b446fdc0558a40 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 2 Nov 2018 17:15:21 -0700 Subject: [PATCH 2774/3817] gx: update go-log and sha256 fixes #5709 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-keystore@33935f4f62f67439a75f5ccea0cfc2f3990f14fd --- keystore/keystore.go | 4 ++-- keystore/keystore_test.go | 2 +- keystore/memkeystore.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index ba43da47f..7c41b36ed 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -7,8 +7,8 @@ import ( "path/filepath" "strings" - ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" - logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log" + ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" + logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" ) var log = logging.Logger("keystore") diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index 751a2e39d..1d2005e9e 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -9,7 +9,7 @@ import ( "sort" "testing" - ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" + ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" ) type rr struct{} diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index a89a1ae7f..0c8f8861f 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -1,6 +1,6 @@ package keystore -import ci "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto" +import ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" // MemKeystore is an in memory keystore implementation that is not persisted to // any backing storage. From f7a75ea5068633f821367cdd2d090c5ebf41d3aa Mon Sep 17 00:00:00 2001 From: songjiayang Date: Thu, 8 Nov 2018 14:50:32 +0800 Subject: [PATCH 2775/3817] make blockservice AddBlocks return more quickly This commit was moved from ipfs/go-blockservice@13325361e5c2eefd8a688a3addc4f95ad57a4a8e --- blockservice/blockservice.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 2e1de3f7a..926b7208c 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -181,6 +181,10 @@ func (s *blockService) AddBlocks(bs []blocks.Block) error { toput = bs } + if len(toput) == 0 { + return nil + } + err := s.blockstore.PutMany(toput) if err != nil { return err From 87ed5c261b5e0d0c386fef575628f45339355dd9 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 8 Nov 2018 13:49:54 -0800 Subject: [PATCH 2776/3817] Wait for all go routines to finish before function returns This commit was moved from ipfs/go-merkledag@d845d6a26f0967506f6562e5de05a71e7e9f4d1a --- ipld/merkledag/merkledag.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 8b522650f..f2965ee6f 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -376,14 +376,16 @@ func EnumerateChildrenAsyncDepth(ctx context.Context, getLinks GetLinks, c cid.C done := make(chan struct{}) var setlk sync.Mutex + var wg sync.WaitGroup errChan := make(chan error) fetchersCtx, cancel := context.WithCancel(ctx) - + defer wg.Wait() defer cancel() - for i := 0; i < FetchGraphConcurrency; i++ { + wg.Add(1) go func() { + defer wg.Done() for cdepth := range feed { ci := cdepth.cid depth := cdepth.depth From 4d4382b88abed0d1af232aa66e573416a8c1185b Mon Sep 17 00:00:00 2001 From: Bamvor Zhang Date: Thu, 27 Sep 2018 17:04:56 +0800 Subject: [PATCH 2777/3817] Fix comments in helpers Fix value of roughLinkSize. Add default value of DefaultLinksPerBlock. And delete calc_test.go as it is deleted in commit bc79ae17a1987 ("refactor importer package with trickle and balanced dag generation") License: MIT Signed-off-by: Bamvor Zhang This commit was moved from ipfs/go-unixfs@e163417e64556ee6abd6930adc4f967d3bcaaff1 --- unixfs/importer/helpers/helpers.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/unixfs/importer/helpers/helpers.go b/unixfs/importer/helpers/helpers.go index ba6d51826..5bf72bc86 100644 --- a/unixfs/importer/helpers/helpers.go +++ b/unixfs/importer/helpers/helpers.go @@ -29,10 +29,11 @@ var roughLinkSize = 34 + 8 + 5 // sha256 multihash + size + no name + protobuf // For now, we use: // // var roughLinkBlockSize = 1 << 13 // 8KB -// var roughLinkSize = 288 // sha256 + framing + name +// var roughLinkSize = 34 + 8 + 5 // sha256 multihash + size + no name +// // + protobuf framing // var DefaultLinksPerBlock = (roughLinkBlockSize / roughLinkSize) -// -// See calc_test.go +// = ( 8192 / 47 ) +// = (approximately) 174 var DefaultLinksPerBlock = roughLinkBlockSize / roughLinkSize // ErrSizeLimitExceeded signals that a block is larger than BlockSizeLimit. From faac94fac8a13913bc291aceebddff36d6229806 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Sat, 10 Nov 2018 18:37:06 -0800 Subject: [PATCH 2778/3817] Update go-ipfs-delay and assoc deps License: MIT Signed-off-by: hannahhoward This commit was moved from ipfs/go-namesys@2f6e6b1d4e7b2331877561706d111b95a7dc92ba --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 20 ++++++++++---------- namesys/namesys.go | 6 +++--- namesys/namesys_test.go | 14 +++++++------- namesys/proquint.go | 2 +- namesys/publisher.go | 14 +++++++------- namesys/publisher_test.go | 10 +++++----- namesys/republisher/repub.go | 6 +++--- namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 10 +++++----- namesys/routing.go | 10 +++++----- 14 files changed, 53 insertions(+), 53 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 3e46548ca..2581d84a1 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" + path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 2cb5e462d..171a99de2 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" + path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index bba548afc..33aff5aa7 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" + path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index a5ed85605..1fb417e5f 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" + path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 5ebb64189..a2144dbda 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,24 +5,24 @@ import ( "testing" "time" - path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" + path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" - mockrouting "gx/ipfs/QmNuVissmH2ftUd4ADvhm9WER3351wTYduY1EeDDGtP1tM/go-ipfs-routing/mock" - offline "gx/ipfs/QmNuVissmH2ftUd4ADvhm9WER3351wTYduY1EeDDGtP1tM/go-ipfs-routing/offline" + pstore "gx/ipfs/QmQAGG1zxfePqj2t7bLxyN8AFccZ889DDR9Gn8kVLDrGZo/go-libp2p-peerstore" + pstoremem "gx/ipfs/QmQAGG1zxfePqj2t7bLxyN8AFccZ889DDR9Gn8kVLDrGZo/go-libp2p-peerstore/pstoremem" + ipns "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns" record "gx/ipfs/QmSoeYGNm8v8jAF49hX7UwHwkXjoeobSrn9sya5NPPsxXP/go-libp2p-record" - pstore "gx/ipfs/QmUymf8fJtideyv3z727BcZUifGBjMZMpCJqu3Gxk5aRUk/go-libp2p-peerstore" - pstoremem "gx/ipfs/QmUymf8fJtideyv3z727BcZUifGBjMZMpCJqu3Gxk5aRUk/go-libp2p-peerstore/pstoremem" - routing "gx/ipfs/QmYyg3UnyiQubxjs4uhKixPxR7eeKrhJ5Vyz6Et4Tet18B/go-libp2p-routing" - ropts "gx/ipfs/QmYyg3UnyiQubxjs4uhKixPxR7eeKrhJ5Vyz6Et4Tet18B/go-libp2p-routing/options" - ipns "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns" + routing "gx/ipfs/QmZBH87CAPFHcc7cYmBqeSQ98zQ3SX9KUxiYgzPmLWNVKz/go-libp2p-routing" + ropts "gx/ipfs/QmZBH87CAPFHcc7cYmBqeSQ98zQ3SX9KUxiYgzPmLWNVKz/go-libp2p-routing/options" testutil "gx/ipfs/QmZXjR5X1p4KrQ967cTsy4MymMzUM8mZECF3PV8UcN4o3g/go-testutil" - ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" - dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" + mockrouting "gx/ipfs/QmdxhyAwBrnmJFsYPK6tyHh4Yy3gK8gbULErX1dRnpUMqu/go-ipfs-routing/mock" + offline "gx/ipfs/QmdxhyAwBrnmJFsYPK6tyHh4Yy3gK8gbULErX1dRnpUMqu/go-ipfs-routing/offline" + ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" + dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 4b45025a4..6fae36c7a 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,17 +5,17 @@ import ( "strings" "time" - path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" + path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" - routing "gx/ipfs/QmYyg3UnyiQubxjs4uhKixPxR7eeKrhJ5Vyz6Et4Tet18B/go-libp2p-routing" + routing "gx/ipfs/QmZBH87CAPFHcc7cYmBqeSQ98zQ3SX9KUxiYgzPmLWNVKz/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" + ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 17f8277b3..cd1cbdcf4 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -8,14 +8,14 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - offroute "gx/ipfs/QmNuVissmH2ftUd4ADvhm9WER3351wTYduY1EeDDGtP1tM/go-ipfs-routing/offline" - path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" - pstoremem "gx/ipfs/QmUymf8fJtideyv3z727BcZUifGBjMZMpCJqu3Gxk5aRUk/go-libp2p-peerstore/pstoremem" - "gx/ipfs/QmXLCwhHh7bxRsBnCKNE9BAN87V44aSxXLquZYTtjr6fZ3/go-unixfs" - ipns "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns" - ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" - dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" + pstoremem "gx/ipfs/QmQAGG1zxfePqj2t7bLxyN8AFccZ889DDR9Gn8kVLDrGZo/go-libp2p-peerstore/pstoremem" + ipns "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns" + "gx/ipfs/QmUnHNqhSB1JgzVCxL1Kz3yb4bdyB4q1Z9AD5AUBVmt3fZ/go-unixfs" + path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" + offroute "gx/ipfs/QmdxhyAwBrnmJFsYPK6tyHh4Yy3gK8gbULErX1dRnpUMqu/go-ipfs-routing/offline" + ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" + dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index 3849b937a..27bef3e92 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,7 +4,7 @@ import ( "context" "errors" - path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" + path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/publisher.go b/namesys/publisher.go index 9da516903..a1ce79c6f 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,17 +7,17 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" - ft "gx/ipfs/QmXLCwhHh7bxRsBnCKNE9BAN87V44aSxXLquZYTtjr6fZ3/go-unixfs" + ft "gx/ipfs/QmUnHNqhSB1JgzVCxL1Kz3yb4bdyB4q1Z9AD5AUBVmt3fZ/go-unixfs" + path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - routing "gx/ipfs/QmYyg3UnyiQubxjs4uhKixPxR7eeKrhJ5Vyz6Et4Tet18B/go-libp2p-routing" - ipns "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns" - pb "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns/pb" - ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" - dsquery "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" + ipns "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns" + pb "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns/pb" + routing "gx/ipfs/QmZBH87CAPFHcc7cYmBqeSQ98zQ3SX9KUxiYgzPmLWNVKz/go-libp2p-routing" peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" + ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" + dsquery "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 987493cc2..bb24ab353 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -7,14 +7,14 @@ import ( "time" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - mockrouting "gx/ipfs/QmNuVissmH2ftUd4ADvhm9WER3351wTYduY1EeDDGtP1tM/go-ipfs-routing/mock" + ipns "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns" ma "gx/ipfs/QmRKLtwMw131aK7ugC3G7ybpumMz78YrJe5dzneyindvG1/go-multiaddr" - ipns "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns" testutil "gx/ipfs/QmZXjR5X1p4KrQ967cTsy4MymMzUM8mZECF3PV8UcN4o3g/go-testutil" - dshelp "gx/ipfs/QmaHSUAhuf9WG3mzJUd1fLDsQGvjsaQdUE7w5cZncz9AcB/go-ipfs-ds-help" - ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" - dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" + dshelp "gx/ipfs/QmauEMWPoSqggfpSDHMMXuDn12DTd7TaFBvn39eeurzKT2/go-ipfs-ds-help" peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" + mockrouting "gx/ipfs/QmdxhyAwBrnmJFsYPK6tyHh4Yy3gK8gbULErX1dRnpUMqu/go-ipfs-routing/mock" + ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" + dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) type identity struct { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 649613c5c..171416c9b 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,16 +7,16 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" + path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" ic "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" + pb "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns/pb" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - pb "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns/pb" - ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" + ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index b2c1b29be..82597a966 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" + path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" + pstore "gx/ipfs/QmQAGG1zxfePqj2t7bLxyN8AFccZ889DDR9Gn8kVLDrGZo/go-libp2p-peerstore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - pstore "gx/ipfs/QmUymf8fJtideyv3z727BcZUifGBjMZMpCJqu3Gxk5aRUk/go-libp2p-peerstore" - mocknet "gx/ipfs/QmXnpYYg2onGLXVxM4Q5PEFcx29k8zeJQkPeLAk9h9naxg/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmVvV8JQmmqPCwXAaesWJPheUiEFQJ9HWRhWhuFuxVQxpR/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index f3556c4c2..1376ea779 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,14 +6,14 @@ import ( "testing" "time" - path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" + path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" - mockrouting "gx/ipfs/QmNuVissmH2ftUd4ADvhm9WER3351wTYduY1EeDDGtP1tM/go-ipfs-routing/mock" - ipns "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns" + ipns "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns" testutil "gx/ipfs/QmZXjR5X1p4KrQ967cTsy4MymMzUM8mZECF3PV8UcN4o3g/go-testutil" - ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" - dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" + mockrouting "gx/ipfs/QmdxhyAwBrnmJFsYPK6tyHh4Yy3gK8gbULErX1dRnpUMqu/go-ipfs-routing/mock" + ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" + dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index aabbc9511..f4e115686 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,15 +5,15 @@ import ( "strings" "time" - path "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" + path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" + dht "gx/ipfs/QmQsw6Nq2A345PqChdtbWVoYbSno7uqRDHwYmYpbPHmZNc/go-libp2p-kad-dht" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - routing "gx/ipfs/QmYyg3UnyiQubxjs4uhKixPxR7eeKrhJ5Vyz6Et4Tet18B/go-libp2p-routing" - ipns "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns" - pb "gx/ipfs/QmZMJfrt7fU33oFQ9WvWnovhiiZ8T6qkWkFXNCFreJTzgT/go-ipns/pb" - dht "gx/ipfs/QmadRyQYRn64xHb5HKy2jRFp2Der643Cgo7NEjFgs4MX2k/go-libp2p-kad-dht" + ipns "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns" + pb "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns/pb" + routing "gx/ipfs/QmZBH87CAPFHcc7cYmBqeSQ98zQ3SX9KUxiYgzPmLWNVKz/go-libp2p-routing" peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" From cd374fff52aec080e9faa17753d6fc104c1bd4ae Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Sat, 10 Nov 2018 18:37:06 -0800 Subject: [PATCH 2779/3817] Update go-ipfs-delay and assoc deps License: MIT Signed-off-by: hannahhoward This commit was moved from ipfs/interface-go-ipfs-core@20ca7387bd766e8a03ba3d7675d4d611a3a3fdab --- coreiface/dht.go | 2 +- coreiface/options/unixfs.go | 2 +- coreiface/path.go | 2 +- coreiface/swarm.go | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/coreiface/dht.go b/coreiface/dht.go index c4eef9379..243f1292c 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -5,7 +5,7 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - pstore "gx/ipfs/QmUymf8fJtideyv3z727BcZUifGBjMZMpCJqu3Gxk5aRUk/go-libp2p-peerstore" + pstore "gx/ipfs/QmQAGG1zxfePqj2t7bLxyN8AFccZ889DDR9Gn8kVLDrGZo/go-libp2p-peerstore" peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" ) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 9b0683a11..742aa6f9e 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -5,7 +5,7 @@ import ( "fmt" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - dag "gx/ipfs/QmaDBne4KeY3UepeqSVKYpSmQGa3q9zP6x3LfVF2UjF3Hc/go-merkledag" + dag "gx/ipfs/QmcGt25mrjuB2kKW2zhPbXVZNHc4yoTDQ65NA8m6auP2f1/go-merkledag" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) diff --git a/coreiface/path.go b/coreiface/path.go index f5e7aeb4c..0545c30d7 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path" + ipfspath "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ) diff --git a/coreiface/swarm.go b/coreiface/swarm.go index b830a0817..1ecb0bb5e 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -5,11 +5,11 @@ import ( "errors" "time" + pstore "gx/ipfs/QmQAGG1zxfePqj2t7bLxyN8AFccZ889DDR9Gn8kVLDrGZo/go-libp2p-peerstore" ma "gx/ipfs/QmRKLtwMw131aK7ugC3G7ybpumMz78YrJe5dzneyindvG1/go-multiaddr" - net "gx/ipfs/QmRKbEchaYADxSCyyjhDh4cTrUby8ftXUb8MRLBTHQYupw/go-libp2p-net" - pstore "gx/ipfs/QmUymf8fJtideyv3z727BcZUifGBjMZMpCJqu3Gxk5aRUk/go-libp2p-peerstore" "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol" "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" + net "gx/ipfs/QmenvQQy4bFGSiHJUGupVmCRHfetg5rH3vTp9Z2f6v2KXR/go-libp2p-net" ) var ( From 8b8b993d4727724ba49becf8333a31411fceeb61 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Sat, 10 Nov 2018 18:37:06 -0800 Subject: [PATCH 2780/3817] Update go-ipfs-delay and assoc deps License: MIT Signed-off-by: hannahhoward This commit was moved from ipfs/go-filestore@bdaaf12e092d59f55f5d9a540a58b35d5be4ba04 --- filestore/filestore.go | 4 ++-- filestore/filestore_test.go | 6 +++--- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 04f3ef683..d9632d9f9 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -13,10 +13,10 @@ import ( posinfo "gx/ipfs/QmR6YMs8EkXQLXNwQKxLnQp2VBZSepoEJ8KCZAyanJHhJu/go-ipfs-posinfo" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmSNLNnL3kq3A1NGdQA9AtgxM9CWKiiSEup3W435jCkRQS/go-ipfs-blockstore" + blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" blocks "gx/ipfs/QmWoXtvgC8inqFkAATB7cp2Dax7XBi9VDvSg9RCCZufmRk/go-block-format" - dsq "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" + dsq "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index bfe6b1354..b40941e2e 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,12 +7,12 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmaDBne4KeY3UepeqSVKYpSmQGa3q9zP6x3LfVF2UjF3Hc/go-merkledag" + dag "gx/ipfs/QmcGt25mrjuB2kKW2zhPbXVZNHc4yoTDQ65NA8m6auP2f1/go-merkledag" posinfo "gx/ipfs/QmR6YMs8EkXQLXNwQKxLnQp2VBZSepoEJ8KCZAyanJHhJu/go-ipfs-posinfo" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmSNLNnL3kq3A1NGdQA9AtgxM9CWKiiSEup3W435jCkRQS/go-ipfs-blockstore" - ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" + blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" + ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index fa4ba0dcc..f6b39e27e 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -12,13 +12,13 @@ import ( posinfo "gx/ipfs/QmR6YMs8EkXQLXNwQKxLnQp2VBZSepoEJ8KCZAyanJHhJu/go-ipfs-posinfo" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmSNLNnL3kq3A1NGdQA9AtgxM9CWKiiSEup3W435jCkRQS/go-ipfs-blockstore" + blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" blocks "gx/ipfs/QmWoXtvgC8inqFkAATB7cp2Dax7XBi9VDvSg9RCCZufmRk/go-block-format" - dshelp "gx/ipfs/QmaHSUAhuf9WG3mzJUd1fLDsQGvjsaQdUE7w5cZncz9AcB/go-ipfs-ds-help" - ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" - dsns "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/namespace" - dsq "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" + dshelp "gx/ipfs/QmauEMWPoSqggfpSDHMMXuDn12DTd7TaFBvn39eeurzKT2/go-ipfs-ds-help" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" + ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" + dsns "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/namespace" + dsq "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index f7af7f601..af25da272 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -7,10 +7,10 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmSNLNnL3kq3A1NGdQA9AtgxM9CWKiiSEup3W435jCkRQS/go-ipfs-blockstore" - dshelp "gx/ipfs/QmaHSUAhuf9WG3mzJUd1fLDsQGvjsaQdUE7w5cZncz9AcB/go-ipfs-ds-help" - ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" - dsq "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" + blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" + dshelp "gx/ipfs/QmauEMWPoSqggfpSDHMMXuDn12DTd7TaFBvn39eeurzKT2/go-ipfs-ds-help" + ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" + dsq "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" ) // Status is used to identify the state of the block data referenced From 788a6eb0c8a0ac40e4cb01c1bc31bc57be9bd403 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Sat, 10 Nov 2018 18:37:06 -0800 Subject: [PATCH 2781/3817] Update go-ipfs-delay and assoc deps License: MIT Signed-off-by: hannahhoward This commit was moved from ipfs/go-ipfs-pinner@c4286905469afeae1a3daab881fb07f0dbcfbd98 --- pinning/pinner/gc/gc.go | 10 +++++----- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 12 ++++++------ pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 12 ++++++------ 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 8c0b2e879..0e291e39c 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,16 +8,16 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmVPeMNK9DfGLXDZzs2W4RoFWC9Zq1EnLGmLXtYtWrNdcW/go-blockservice" - dag "gx/ipfs/QmaDBne4KeY3UepeqSVKYpSmQGa3q9zP6x3LfVF2UjF3Hc/go-merkledag" + bserv "gx/ipfs/QmVDTbzzTwnuBwNbJdhW3u7LoBQp46bezm9yp4z1RoEepM/go-blockservice" + dag "gx/ipfs/QmcGt25mrjuB2kKW2zhPbXVZNHc4yoTDQ65NA8m6auP2f1/go-merkledag" - offline "gx/ipfs/QmPpnbwgAuvhUkA9jGooR88ZwZtTUHXXvoQNKdjZC6nYku/go-ipfs-exchange-offline" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - bstore "gx/ipfs/QmSNLNnL3kq3A1NGdQA9AtgxM9CWKiiSEup3W435jCkRQS/go-ipfs-blockstore" + bstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" "gx/ipfs/QmYMQuypUbgsdNHmuCBSUJV6wdQVsBHRivNAp3efHJwZJD/go-verifcid" - dstore "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" + offline "gx/ipfs/QmYZwey1thDTynSrvd6qQkX24UpTka6TFhQ2v569UpoqxD/go-ipfs-exchange-offline" ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" + dstore "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index e8ebe8c3e..75a7d0362 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,12 +10,12 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmaDBne4KeY3UepeqSVKYpSmQGa3q9zP6x3LfVF2UjF3Hc/go-merkledag" + mdag "gx/ipfs/QmcGt25mrjuB2kKW2zhPbXVZNHc4yoTDQ65NA8m6auP2f1/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" + ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index b58691860..2b05b0f77 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" - bs "gx/ipfs/QmVPeMNK9DfGLXDZzs2W4RoFWC9Zq1EnLGmLXtYtWrNdcW/go-blockservice" - mdag "gx/ipfs/QmaDBne4KeY3UepeqSVKYpSmQGa3q9zP6x3LfVF2UjF3Hc/go-merkledag" + bs "gx/ipfs/QmVDTbzzTwnuBwNbJdhW3u7LoBQp46bezm9yp4z1RoEepM/go-blockservice" + mdag "gx/ipfs/QmcGt25mrjuB2kKW2zhPbXVZNHc4yoTDQ65NA8m6auP2f1/go-merkledag" util "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" - offline "gx/ipfs/QmPpnbwgAuvhUkA9jGooR88ZwZtTUHXXvoQNKdjZC6nYku/go-ipfs-exchange-offline" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmSNLNnL3kq3A1NGdQA9AtgxM9CWKiiSEup3W435jCkRQS/go-ipfs-blockstore" - ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" - dssync "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/sync" + blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" + offline "gx/ipfs/QmYZwey1thDTynSrvd6qQkX24UpTka6TFhQ2v569UpoqxD/go-ipfs-exchange-offline" + ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" + dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) var rand = util.NewTimeSeededRand() diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 35b76eb29..9c7d1eb7a 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmaDBne4KeY3UepeqSVKYpSmQGa3q9zP6x3LfVF2UjF3Hc/go-merkledag" + "gx/ipfs/QmcGt25mrjuB2kKW2zhPbXVZNHc4yoTDQ65NA8m6auP2f1/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 82e4679b1..de7205dac 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,14 +5,14 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmVPeMNK9DfGLXDZzs2W4RoFWC9Zq1EnLGmLXtYtWrNdcW/go-blockservice" - dag "gx/ipfs/QmaDBne4KeY3UepeqSVKYpSmQGa3q9zP6x3LfVF2UjF3Hc/go-merkledag" + bserv "gx/ipfs/QmVDTbzzTwnuBwNbJdhW3u7LoBQp46bezm9yp4z1RoEepM/go-blockservice" + dag "gx/ipfs/QmcGt25mrjuB2kKW2zhPbXVZNHc4yoTDQ65NA8m6auP2f1/go-merkledag" - offline "gx/ipfs/QmPpnbwgAuvhUkA9jGooR88ZwZtTUHXXvoQNKdjZC6nYku/go-ipfs-exchange-offline" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmSNLNnL3kq3A1NGdQA9AtgxM9CWKiiSEup3W435jCkRQS/go-ipfs-blockstore" - ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore" - dsq "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore/query" + blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" + offline "gx/ipfs/QmYZwey1thDTynSrvd6qQkX24UpTka6TFhQ2v569UpoqxD/go-ipfs-exchange-offline" + ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" + dsq "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" ) func ignoreCids(_ cid.Cid) {} From 4560d081abea695e6ba8991fdeec69467e2d11c6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 27 Nov 2018 21:54:55 -0800 Subject: [PATCH 2782/3817] fix a fetch deadlock on error fixes https://github.com/ipfs/go-ipfs/issues/5793 This commit was moved from ipfs/go-merkledag@3b8c0ad40d539e62db8660fc01533180d726dcbf --- ipld/merkledag/merkledag.go | 5 ++++- ipld/merkledag/merkledag_test.go | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index f2965ee6f..295f899e3 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -397,7 +397,10 @@ func EnumerateChildrenAsyncDepth(ctx context.Context, getLinks GetLinks, c cid.C if shouldVisit { links, err := getLinks(ctx, ci) if err != nil { - errChan <- err + select { + case errChan <- err: + case <-fetchersCtx.Done(): + } return } diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index a56aca586..d222ce873 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -677,6 +677,7 @@ func TestEnumerateAsyncFailsNotFound(t *testing.T) { b := NodeWithData([]byte("foo2")) c := NodeWithData([]byte("foo3")) d := NodeWithData([]byte("foo4")) + e := NodeWithData([]byte("foo5")) ds := dstest.Mock() for _, n := range []ipld.Node{a, b, c} { @@ -703,6 +704,10 @@ func TestEnumerateAsyncFailsNotFound(t *testing.T) { t.Fatal(err) } + if err := parent.AddNodeLink("e", e); err != nil { + t.Fatal(err) + } + err := ds.Add(ctx, parent) if err != nil { t.Fatal(err) From d4bc803bd72426b3d69aaceb631db961c4338b36 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 28 Nov 2018 17:21:36 -0500 Subject: [PATCH 2783/3817] Gx update go-merkledag and related deps. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-namesys@53d23c6b0765b76529700ebfaeb07b0b1b75eca9 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 4 ++-- namesys/proquint.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 2581d84a1..c3b939a18 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" + path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 171a99de2..74cc31db3 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" + path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 33aff5aa7..3df35142a 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" + path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index 1fb417e5f..1df11aaec 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" + path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index a2144dbda..64ec3079b 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" + path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/namesys.go b/namesys/namesys.go index 6fae36c7a..c9c937c30 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,7 +5,7 @@ import ( "strings" "time" - path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" + path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index cd1cbdcf4..976939c05 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -9,9 +9,9 @@ import ( ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" pstoremem "gx/ipfs/QmQAGG1zxfePqj2t7bLxyN8AFccZ889DDR9Gn8kVLDrGZo/go-libp2p-peerstore/pstoremem" + path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" ipns "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns" - "gx/ipfs/QmUnHNqhSB1JgzVCxL1Kz3yb4bdyB4q1Z9AD5AUBVmt3fZ/go-unixfs" - path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" + "gx/ipfs/QmXAFxWtAB9YAMzMy9op6m95hWYu2CC5rmTsijkYL12Kvu/go-unixfs" peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" offroute "gx/ipfs/QmdxhyAwBrnmJFsYPK6tyHh4Yy3gK8gbULErX1dRnpUMqu/go-ipfs-routing/offline" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" diff --git a/namesys/proquint.go b/namesys/proquint.go index 27bef3e92..8151f8f54 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,7 +4,7 @@ import ( "context" "errors" - path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" + path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/publisher.go b/namesys/publisher.go index a1ce79c6f..d7d8d3a37 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,8 +7,8 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmUnHNqhSB1JgzVCxL1Kz3yb4bdyB4q1Z9AD5AUBVmt3fZ/go-unixfs" - path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" + path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" + ft "gx/ipfs/QmXAFxWtAB9YAMzMy9op6m95hWYu2CC5rmTsijkYL12Kvu/go-unixfs" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" ipns "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 171416c9b..c460a333e 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" + path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" ic "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" pb "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns/pb" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 82597a966..2ef2e6c82 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" + path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" pstore "gx/ipfs/QmQAGG1zxfePqj2t7bLxyN8AFccZ889DDR9Gn8kVLDrGZo/go-libp2p-peerstore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 1376ea779..d59614fbc 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" + path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" ipns "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns" testutil "gx/ipfs/QmZXjR5X1p4KrQ967cTsy4MymMzUM8mZECF3PV8UcN4o3g/go-testutil" diff --git a/namesys/routing.go b/namesys/routing.go index f4e115686..84a418f09 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,7 +5,7 @@ import ( "strings" "time" - path "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" + path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" From 33840cd7fb98e69e4a3795ad744faf3135dc9a9b Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 28 Nov 2018 17:21:36 -0500 Subject: [PATCH 2784/3817] Gx update go-merkledag and related deps. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/interface-go-ipfs-core@1f51fd41ce7d6160fa741a5c7699a3b5bb305540 --- coreiface/options/unixfs.go | 2 +- coreiface/path.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 742aa6f9e..4d7b61a93 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -5,7 +5,7 @@ import ( "fmt" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - dag "gx/ipfs/QmcGt25mrjuB2kKW2zhPbXVZNHc4yoTDQ65NA8m6auP2f1/go-merkledag" + dag "gx/ipfs/QmdURv6Sbob8TVW2tFFve9vcEWrSUgwPqeqnXyvYhLrkyd/go-merkledag" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) diff --git a/coreiface/path.go b/coreiface/path.go index 0545c30d7..aa3b2d0c6 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path" + ipfspath "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ) From d083cb0c746e9c7f588d59c3de663f5893cea100 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 28 Nov 2018 17:21:36 -0500 Subject: [PATCH 2785/3817] Gx update go-merkledag and related deps. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@a0b864f8f877bf048c154ef072454f858fa1a6a6 --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index b40941e2e..ea3f2d744 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmcGt25mrjuB2kKW2zhPbXVZNHc4yoTDQ65NA8m6auP2f1/go-merkledag" + dag "gx/ipfs/QmdURv6Sbob8TVW2tFFve9vcEWrSUgwPqeqnXyvYhLrkyd/go-merkledag" posinfo "gx/ipfs/QmR6YMs8EkXQLXNwQKxLnQp2VBZSepoEJ8KCZAyanJHhJu/go-ipfs-posinfo" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" From c6332756df0b72c07286b0ebc827bcbf2c53030f Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 28 Nov 2018 17:21:36 -0500 Subject: [PATCH 2786/3817] Gx update go-merkledag and related deps. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-ipfs-pinner@8e78bd723dd02ce328136d37f7a4e44b6ef3ce05 --- pinning/pinner/gc/gc.go | 2 +- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 2 +- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 0e291e39c..3ab3dcdc0 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -9,7 +9,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" bserv "gx/ipfs/QmVDTbzzTwnuBwNbJdhW3u7LoBQp46bezm9yp4z1RoEepM/go-blockservice" - dag "gx/ipfs/QmcGt25mrjuB2kKW2zhPbXVZNHc4yoTDQ65NA8m6auP2f1/go-merkledag" + dag "gx/ipfs/QmdURv6Sbob8TVW2tFFve9vcEWrSUgwPqeqnXyvYhLrkyd/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" bstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 75a7d0362..ae8e35d74 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmcGt25mrjuB2kKW2zhPbXVZNHc4yoTDQ65NA8m6auP2f1/go-merkledag" + mdag "gx/ipfs/QmdURv6Sbob8TVW2tFFve9vcEWrSUgwPqeqnXyvYhLrkyd/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 2b05b0f77..70e1dbc25 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -6,7 +6,7 @@ import ( "time" bs "gx/ipfs/QmVDTbzzTwnuBwNbJdhW3u7LoBQp46bezm9yp4z1RoEepM/go-blockservice" - mdag "gx/ipfs/QmcGt25mrjuB2kKW2zhPbXVZNHc4yoTDQ65NA8m6auP2f1/go-merkledag" + mdag "gx/ipfs/QmdURv6Sbob8TVW2tFFve9vcEWrSUgwPqeqnXyvYhLrkyd/go-merkledag" util "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 9c7d1eb7a..f37d6a347 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmcGt25mrjuB2kKW2zhPbXVZNHc4yoTDQ65NA8m6auP2f1/go-merkledag" + "gx/ipfs/QmdURv6Sbob8TVW2tFFve9vcEWrSUgwPqeqnXyvYhLrkyd/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index de7205dac..1d8e65594 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -6,7 +6,7 @@ import ( "testing" bserv "gx/ipfs/QmVDTbzzTwnuBwNbJdhW3u7LoBQp46bezm9yp4z1RoEepM/go-blockservice" - dag "gx/ipfs/QmcGt25mrjuB2kKW2zhPbXVZNHc4yoTDQ65NA8m6auP2f1/go-merkledag" + dag "gx/ipfs/QmdURv6Sbob8TVW2tFFve9vcEWrSUgwPqeqnXyvYhLrkyd/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" From 1928f10ac3cf6902f8ef99f9159627522563eb9c Mon Sep 17 00:00:00 2001 From: Bamvor Zhang Date: Tue, 4 Dec 2018 16:09:34 +0800 Subject: [PATCH 2787/3817] Fix typo in helpers License: MIT Signed-off-by: Bamvor Zhang This commit was moved from ipfs/go-unixfs@b56f85fe528a11d29bff58287c6ffdfd2e819c96 --- unixfs/importer/helpers/helpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/importer/helpers/helpers.go b/unixfs/importer/helpers/helpers.go index 5bf72bc86..75d013090 100644 --- a/unixfs/importer/helpers/helpers.go +++ b/unixfs/importer/helpers/helpers.go @@ -88,7 +88,7 @@ func (n *UnixfsNode) GetChild(ctx context.Context, i int, ds ipld.DAGService) (* } // AddChild adds the given UnixfsNode as a child of the receiver. -// The passed in DagBuilderHelper is used to store the child node an +// The passed in DagBuilderHelper is used to store the child node and // pin it locally so it doesnt get lost. func (n *UnixfsNode) AddChild(child *UnixfsNode, db *DagBuilderHelper) error { n.ufmt.AddBlockSize(child.FileSize()) From 032060566e194f69bb3fd22c6d238eed9a10dcf6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 7 Dec 2018 15:01:22 -0800 Subject: [PATCH 2788/3817] testing: disable inline peer ID test We're disabling these until we can properly specify the hash function in the key itself. This commit was moved from ipfs/go-ipns@39adaba0123da75ac33636c9fa9745d6f4eaee15 --- ipns/validate_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ipns/validate_test.go b/ipns/validate_test.go index 0ef9d00c5..1e10249b6 100644 --- a/ipns/validate_test.go +++ b/ipns/validate_test.go @@ -128,6 +128,8 @@ func TestEmbeddedPubKeyValidate(t *testing.T) { } func TestPeerIDPubKeyValidate(t *testing.T) { + t.Skip("disabled until libp2p/go-libp2p-crypto#51 is fixed") + goodeol := time.Now().Add(time.Hour) kbook := pstoremem.NewPeerstore() From c884a163c3cad6f1c0d28270dc1b9ccb739859fe Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 7 Dec 2018 15:37:23 -0800 Subject: [PATCH 2789/3817] gx: update go-libp2p-peer Reverts the changes that allowed small keys (ed25519 keys) to be inlined. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@0c0c90a487f0e0b0d02e19ea0462f1ead4305077 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 22 +++++++++++----------- namesys/namesys.go | 6 +++--- namesys/namesys_test.go | 12 ++++++------ namesys/proquint.go | 2 +- namesys/publisher.go | 12 ++++++------ namesys/publisher_test.go | 8 ++++---- namesys/republisher/repub.go | 6 +++--- namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 10 +++++----- namesys/routing.go | 12 ++++++------ 14 files changed, 52 insertions(+), 52 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index c3b939a18..e956f7653 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" + path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 74cc31db3..653580586 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" + path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 3df35142a..b92a78029 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" + path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index 1df11aaec..e330a83cb 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" + path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 64ec3079b..840bd064a 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,24 +5,24 @@ import ( "testing" "time" - path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" + path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" - pstore "gx/ipfs/QmQAGG1zxfePqj2t7bLxyN8AFccZ889DDR9Gn8kVLDrGZo/go-libp2p-peerstore" - pstoremem "gx/ipfs/QmQAGG1zxfePqj2t7bLxyN8AFccZ889DDR9Gn8kVLDrGZo/go-libp2p-peerstore/pstoremem" - ipns "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns" - record "gx/ipfs/QmSoeYGNm8v8jAF49hX7UwHwkXjoeobSrn9sya5NPPsxXP/go-libp2p-record" - routing "gx/ipfs/QmZBH87CAPFHcc7cYmBqeSQ98zQ3SX9KUxiYgzPmLWNVKz/go-libp2p-routing" - ropts "gx/ipfs/QmZBH87CAPFHcc7cYmBqeSQ98zQ3SX9KUxiYgzPmLWNVKz/go-libp2p-routing/options" - testutil "gx/ipfs/QmZXjR5X1p4KrQ967cTsy4MymMzUM8mZECF3PV8UcN4o3g/go-testutil" - peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" - mockrouting "gx/ipfs/QmdxhyAwBrnmJFsYPK6tyHh4Yy3gK8gbULErX1dRnpUMqu/go-ipfs-routing/mock" - offline "gx/ipfs/QmdxhyAwBrnmJFsYPK6tyHh4Yy3gK8gbULErX1dRnpUMqu/go-ipfs-routing/offline" + ipns "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns" + testutil "gx/ipfs/QmPuhRE325DR8ChNcFtgd6F1eANCHy1oohXZPpYop4xsK6/go-testutil" + routing "gx/ipfs/QmRASJXJUFygM5qU4YrH7k7jD6S4Hg8nJmgqJ4bYJvLatd/go-libp2p-routing" + ropts "gx/ipfs/QmRASJXJUFygM5qU4YrH7k7jD6S4Hg8nJmgqJ4bYJvLatd/go-libp2p-routing/options" + peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + pstore "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore" + pstoremem "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore/pstoremem" + mockrouting "gx/ipfs/QmdmWkx54g7VfVyxeG8ic84uf4G6Eq1GohuyKA3XDuJ8oC/go-ipfs-routing/mock" + offline "gx/ipfs/QmdmWkx54g7VfVyxeG8ic84uf4G6Eq1GohuyKA3XDuJ8oC/go-ipfs-routing/offline" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" + record "gx/ipfs/QmfARXVCzpwFXQdepAJZuqyNDgV9doEsMnVCo1ssmuSe1U/go-libp2p-record" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index c9c937c30..303413d29 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,15 +5,15 @@ import ( "strings" "time" - path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" + path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" - routing "gx/ipfs/QmZBH87CAPFHcc7cYmBqeSQ98zQ3SX9KUxiYgzPmLWNVKz/go-libp2p-routing" + routing "gx/ipfs/QmRASJXJUFygM5qU4YrH7k7jD6S4Hg8nJmgqJ4bYJvLatd/go-libp2p-routing" + peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" ) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 976939c05..e97933afd 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -8,12 +8,12 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - pstoremem "gx/ipfs/QmQAGG1zxfePqj2t7bLxyN8AFccZ889DDR9Gn8kVLDrGZo/go-libp2p-peerstore/pstoremem" - path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" - ipns "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns" - "gx/ipfs/QmXAFxWtAB9YAMzMy9op6m95hWYu2CC5rmTsijkYL12Kvu/go-unixfs" - peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" - offroute "gx/ipfs/QmdxhyAwBrnmJFsYPK6tyHh4Yy3gK8gbULErX1dRnpUMqu/go-ipfs-routing/offline" + ipns "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns" + peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + pstoremem "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore/pstoremem" + path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" + "gx/ipfs/QmdYvDbHp7qAhZ7GsCj6e1cMo55ND6y2mjWVzwdvcv4f12/go-unixfs" + offroute "gx/ipfs/QmdmWkx54g7VfVyxeG8ic84uf4G6Eq1GohuyKA3XDuJ8oC/go-ipfs-routing/offline" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) diff --git a/namesys/proquint.go b/namesys/proquint.go index 8151f8f54..a2c8c6f08 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,8 +4,8 @@ import ( "context" "errors" - path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" + path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index d7d8d3a37..78bfd160f 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,14 +7,14 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" - ft "gx/ipfs/QmXAFxWtAB9YAMzMy9op6m95hWYu2CC5rmTsijkYL12Kvu/go-unixfs" + path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" + ft "gx/ipfs/QmdYvDbHp7qAhZ7GsCj6e1cMo55ND6y2mjWVzwdvcv4f12/go-unixfs" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - ipns "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns" - pb "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns/pb" - routing "gx/ipfs/QmZBH87CAPFHcc7cYmBqeSQ98zQ3SX9KUxiYgzPmLWNVKz/go-libp2p-routing" - peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" + ipns "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns" + pb "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns/pb" + routing "gx/ipfs/QmRASJXJUFygM5qU4YrH7k7jD6S4Hg8nJmgqJ4bYJvLatd/go-libp2p-routing" + peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dsquery "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index bb24ab353..5f0c9867c 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -7,12 +7,12 @@ import ( "time" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - ipns "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns" + ipns "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns" + testutil "gx/ipfs/QmPuhRE325DR8ChNcFtgd6F1eANCHy1oohXZPpYop4xsK6/go-testutil" ma "gx/ipfs/QmRKLtwMw131aK7ugC3G7ybpumMz78YrJe5dzneyindvG1/go-multiaddr" - testutil "gx/ipfs/QmZXjR5X1p4KrQ967cTsy4MymMzUM8mZECF3PV8UcN4o3g/go-testutil" + peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" dshelp "gx/ipfs/QmauEMWPoSqggfpSDHMMXuDn12DTd7TaFBvn39eeurzKT2/go-ipfs-ds-help" - peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" - mockrouting "gx/ipfs/QmdxhyAwBrnmJFsYPK6tyHh4Yy3gK8gbULErX1dRnpUMqu/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmdmWkx54g7VfVyxeG8ic84uf4G6Eq1GohuyKA3XDuJ8oC/go-ipfs-routing/mock" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index c460a333e..7fb593a68 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,13 +7,13 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" + path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" ic "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - pb "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns/pb" + pb "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns/pb" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" + peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 2ef2e6c82..26d3fa807 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" + path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" - pstore "gx/ipfs/QmQAGG1zxfePqj2t7bLxyN8AFccZ889DDR9Gn8kVLDrGZo/go-libp2p-peerstore" + mocknet "gx/ipfs/QmRBaUEQEeFWywfrZJ64QgsmvcqgLSK3VbvGMR2NM2Edpf/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmVvV8JQmmqPCwXAaesWJPheUiEFQJ9HWRhWhuFuxVQxpR/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index d59614fbc..32bc6dddf 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,12 +6,12 @@ import ( "testing" "time" - path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" + path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" - ipns "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns" - testutil "gx/ipfs/QmZXjR5X1p4KrQ967cTsy4MymMzUM8mZECF3PV8UcN4o3g/go-testutil" - peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" - mockrouting "gx/ipfs/QmdxhyAwBrnmJFsYPK6tyHh4Yy3gK8gbULErX1dRnpUMqu/go-ipfs-routing/mock" + ipns "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns" + testutil "gx/ipfs/QmPuhRE325DR8ChNcFtgd6F1eANCHy1oohXZPpYop4xsK6/go-testutil" + peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + mockrouting "gx/ipfs/QmdmWkx54g7VfVyxeG8ic84uf4G6Eq1GohuyKA3XDuJ8oC/go-ipfs-routing/mock" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) diff --git a/namesys/routing.go b/namesys/routing.go index 84a418f09..3398e0e7f 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,16 +5,16 @@ import ( "strings" "time" - path "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" + path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" - dht "gx/ipfs/QmQsw6Nq2A345PqChdtbWVoYbSno7uqRDHwYmYpbPHmZNc/go-libp2p-kad-dht" + ipns "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns" + pb "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns/pb" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - ipns "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns" - pb "gx/ipfs/QmR9UpasSQR4Mqq1qiJAfnY4SVBxJn7r639CxiLjx8dYGm/go-ipns/pb" - routing "gx/ipfs/QmZBH87CAPFHcc7cYmBqeSQ98zQ3SX9KUxiYgzPmLWNVKz/go-libp2p-routing" - peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" + routing "gx/ipfs/QmRASJXJUFygM5qU4YrH7k7jD6S4Hg8nJmgqJ4bYJvLatd/go-libp2p-routing" + dht "gx/ipfs/QmXbPygnUKAPMwseE5U3hQA7Thn59GVm7pQrhkFV63umT8/go-libp2p-kad-dht" + peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" From 21152b6074f3bf08068a4f980775194769ba63e5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 7 Dec 2018 15:37:23 -0800 Subject: [PATCH 2790/3817] gx: update go-libp2p-peer Reverts the changes that allowed small keys (ed25519 keys) to be inlined. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@2d2e05fe7cc9680e2a06a5f752e76666a17b5944 --- coreiface/dht.go | 4 ++-- coreiface/key.go | 2 +- coreiface/options/unixfs.go | 2 +- coreiface/path.go | 2 +- coreiface/pubsub.go | 2 +- coreiface/swarm.go | 6 +++--- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/coreiface/dht.go b/coreiface/dht.go index 243f1292c..e39be92c5 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -5,8 +5,8 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - pstore "gx/ipfs/QmQAGG1zxfePqj2t7bLxyN8AFccZ889DDR9Gn8kVLDrGZo/go-libp2p-peerstore" - peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" + peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + pstore "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore" ) // DhtAPI specifies the interface to the DHT diff --git a/coreiface/key.go b/coreiface/key.go index 36a74688b..f310c3cc2 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -5,7 +5,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" + "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" ) // Key specifies the interface to Keys in KeyAPI Keystore diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 4d7b61a93..b771896bc 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -5,7 +5,7 @@ import ( "fmt" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - dag "gx/ipfs/QmdURv6Sbob8TVW2tFFve9vcEWrSUgwPqeqnXyvYhLrkyd/go-merkledag" + dag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) diff --git a/coreiface/path.go b/coreiface/path.go index aa3b2d0c6..57ef4c21b 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,7 @@ package iface import ( - ipfspath "gx/ipfs/QmQtg7N4XjAk2ZYpBjjv8B6gQprsRekabHBCnF6i46JYKJ/go-path" + ipfspath "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ) diff --git a/coreiface/pubsub.go b/coreiface/pubsub.go index 93e429574..867c8adc4 100644 --- a/coreiface/pubsub.go +++ b/coreiface/pubsub.go @@ -6,7 +6,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - peer "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" + peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" ) // PubSubSubscription is an active PubSub subscription diff --git a/coreiface/swarm.go b/coreiface/swarm.go index 1ecb0bb5e..63d20f035 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -5,11 +5,11 @@ import ( "errors" "time" - pstore "gx/ipfs/QmQAGG1zxfePqj2t7bLxyN8AFccZ889DDR9Gn8kVLDrGZo/go-libp2p-peerstore" + net "gx/ipfs/QmPtFaR7BWHLAjSwLh9kXcyrgTzDpuhcWLkx8ioa9RMYnx/go-libp2p-net" ma "gx/ipfs/QmRKLtwMw131aK7ugC3G7ybpumMz78YrJe5dzneyindvG1/go-multiaddr" + "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + pstore "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore" "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol" - "gx/ipfs/QmcqU6QUDSXprb1518vYDGczrTJTyGwLG9eUa5iNX4xUtS/go-libp2p-peer" - net "gx/ipfs/QmenvQQy4bFGSiHJUGupVmCRHfetg5rH3vTp9Z2f6v2KXR/go-libp2p-net" ) var ( From 87d7dd8741c99d5223c1a240c5c86a8126b15d5a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 7 Dec 2018 15:37:23 -0800 Subject: [PATCH 2791/3817] gx: update go-libp2p-peer Reverts the changes that allowed small keys (ed25519 keys) to be inlined. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@62dc882c631232e9e54ff95f3c7dad7d72704049 --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index ea3f2d744..474c7ecfa 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmdURv6Sbob8TVW2tFFve9vcEWrSUgwPqeqnXyvYhLrkyd/go-merkledag" + dag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" posinfo "gx/ipfs/QmR6YMs8EkXQLXNwQKxLnQp2VBZSepoEJ8KCZAyanJHhJu/go-ipfs-posinfo" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" From 4f65e0d6d0ad82358c5fc0036a044dec82eaf64b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 7 Dec 2018 15:37:23 -0800 Subject: [PATCH 2792/3817] gx: update go-libp2p-peer Reverts the changes that allowed small keys (ed25519 keys) to be inlined. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@bcb5927195dc40e461a267d18d58bf395c2ffc32 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 3ab3dcdc0..8a04d7fc1 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmVDTbzzTwnuBwNbJdhW3u7LoBQp46bezm9yp4z1RoEepM/go-blockservice" - dag "gx/ipfs/QmdURv6Sbob8TVW2tFFve9vcEWrSUgwPqeqnXyvYhLrkyd/go-merkledag" + bserv "gx/ipfs/QmPoh3SrQzFBWtdGK6qmHDV4EanKR6kYPj4DD3J2NLoEmZ/go-blockservice" + dag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" bstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index ae8e35d74..d9a21f1ca 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmdURv6Sbob8TVW2tFFve9vcEWrSUgwPqeqnXyvYhLrkyd/go-merkledag" + mdag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 70e1dbc25..ce13cb844 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - bs "gx/ipfs/QmVDTbzzTwnuBwNbJdhW3u7LoBQp46bezm9yp4z1RoEepM/go-blockservice" - mdag "gx/ipfs/QmdURv6Sbob8TVW2tFFve9vcEWrSUgwPqeqnXyvYhLrkyd/go-merkledag" + bs "gx/ipfs/QmPoh3SrQzFBWtdGK6qmHDV4EanKR6kYPj4DD3J2NLoEmZ/go-blockservice" + mdag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" util "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index f37d6a347..66ae325c8 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmdURv6Sbob8TVW2tFFve9vcEWrSUgwPqeqnXyvYhLrkyd/go-merkledag" + "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 1d8e65594..da964e37e 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmVDTbzzTwnuBwNbJdhW3u7LoBQp46bezm9yp4z1RoEepM/go-blockservice" - dag "gx/ipfs/QmdURv6Sbob8TVW2tFFve9vcEWrSUgwPqeqnXyvYhLrkyd/go-merkledag" + bserv "gx/ipfs/QmPoh3SrQzFBWtdGK6qmHDV4EanKR6kYPj4DD3J2NLoEmZ/go-blockservice" + dag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" From 59e6b21a1ab34a2e442f1697cd632481292d4661 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 7 Dec 2018 15:44:08 -0800 Subject: [PATCH 2793/3817] fix ed25519 test(s) License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@9abd586f2f3a549075e5c7df1cb227f42efbca8e --- namesys/publisher_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 5f0c9867c..d872ec324 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -108,5 +108,5 @@ func TestRSAPublisher(t *testing.T) { } func TestEd22519Publisher(t *testing.T) { - testNamekeyPublisher(t, ci.Ed25519, ds.ErrNotFound, false) + testNamekeyPublisher(t, ci.Ed25519, nil, true) } From a632c4c8b4bc4708bb45cdab8a6d5b18f5423f1b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 11 Dec 2018 12:43:14 -0800 Subject: [PATCH 2794/3817] go fmt This commit was moved from ipfs/go-mfs@06fd27e475c85a704f5c80fffaca4d5d7baea6c5 --- mfs/inode.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/inode.go b/mfs/inode.go index 54f064c7b..e2b591cb3 100644 --- a/mfs/inode.go +++ b/mfs/inode.go @@ -10,10 +10,10 @@ import ( type inode struct { // name of this `inode` in the MFS path (the same value // is also stored as the name of the DAG link). - name string + name string // parent directory of this `inode` (which may be the `Root`). - parent childCloser + parent childCloser // dagService used to store modifications made to the contents // of the file or directory the `inode` belongs to. From 6f8aeeb4e1dc34c4bf9807a3216e8bb7918d4d2e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 11 Dec 2018 12:43:22 -0800 Subject: [PATCH 2795/3817] avoid unecessary constructors (also avoid exposing a public constructor for a private datastructure) This commit was moved from ipfs/go-mfs@0ae12b2070af108bc56c14cb8b9b6555d2c0fd79 --- mfs/dir.go | 6 +++++- mfs/file.go | 8 ++++++-- mfs/inode.go | 9 --------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 2532b861b..51a00bd48 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -48,7 +48,11 @@ func NewDirectory(ctx context.Context, name string, node ipld.Node, parent child } return &Directory{ - inode: NewInode(name, parent, dserv), + inode: &inode{ + name: name, + parent: parent, + dagService: dserv, + }, ctx: ctx, unixfsDir: db, childDirs: make(map[string]*Directory), diff --git a/mfs/file.go b/mfs/file.go index 86e00713b..00f483448 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -28,8 +28,12 @@ type File struct { // Cid version is non-zero RawLeaves will be enabled. func NewFile(name string, node ipld.Node, parent childCloser, dserv ipld.DAGService) (*File, error) { fi := &File{ - inode: NewInode(name, parent, dserv), - node: node, + inode: &inode{ + name: name, + parent: parent, + dagService: dserv, + }, + node: node, } if node.Cid().Prefix().Version > 0 { fi.RawLeaves = true diff --git a/mfs/inode.go b/mfs/inode.go index e2b591cb3..f0330a222 100644 --- a/mfs/inode.go +++ b/mfs/inode.go @@ -19,12 +19,3 @@ type inode struct { // of the file or directory the `inode` belongs to. dagService ipld.DAGService } - -// NewInode creates a new `inode` structure and return it's pointer. -func NewInode(name string, parent childCloser, dagService ipld.DAGService) *inode { - return &inode{ - name: name, - parent: parent, - dagService: dagService, - } -} From 7babe52bdadc118847ed33881c3e0a6726fee6bd Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 11 Dec 2018 12:45:52 -0800 Subject: [PATCH 2796/3817] avoid unecessary indirection This commit was moved from ipfs/go-mfs@ec0acc9eae643b3694dac17bf88c4ff298a825ab --- mfs/dir.go | 4 ++-- mfs/file.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 51a00bd48..e42400a35 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -22,7 +22,7 @@ var ErrInvalidChild = errors.New("invalid child node") var ErrDirExists = errors.New("directory already has entry by that name") type Directory struct { - *inode + inode childDirs map[string]*Directory files map[string]*File @@ -48,7 +48,7 @@ func NewDirectory(ctx context.Context, name string, node ipld.Node, parent child } return &Directory{ - inode: &inode{ + inode: inode{ name: name, parent: parent, dagService: dserv, diff --git a/mfs/file.go b/mfs/file.go index 00f483448..e0be55089 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -14,7 +14,7 @@ import ( ) type File struct { - *inode + inode desclock sync.RWMutex @@ -28,7 +28,7 @@ type File struct { // Cid version is non-zero RawLeaves will be enabled. func NewFile(name string, node ipld.Node, parent childCloser, dserv ipld.DAGService) (*File, error) { fi := &File{ - inode: &inode{ + inode: inode{ name: name, parent: parent, dagService: dserv, From 040dc65c17ddfd95c769332e89f701d571344c52 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 31 Oct 2018 11:37:33 -0300 Subject: [PATCH 2797/3817] documentation notes This commit was moved from ipfs/go-mfs@0167a27fc5cce23a6b5f2efa18c3f2d597a0c9a6 --- mfs/README.md | 11 +++++++++++ mfs/dir.go | 37 +++++++++++++++++++++++++++++++++++++ mfs/fd.go | 16 ++++++++++++++++ mfs/file.go | 31 +++++++++++++++++++++++++++++++ mfs/ops.go | 17 +++++++++++++++++ mfs/system.go | 38 ++++++++++++++++++++++++++++++++++---- 6 files changed, 146 insertions(+), 4 deletions(-) diff --git a/mfs/README.md b/mfs/README.md index d8247a5b6..86c9d9b5d 100644 --- a/mfs/README.md +++ b/mfs/README.md @@ -33,6 +33,17 @@ import "github.com/ipfs/go-mfs" Check the [GoDoc documentation](https://godoc.org/github.com/ipfs/go-mfs) +## Repository Structure +This repository contains many files, all belonging to the root `mfs` package. + +* `file.go`: MFS `File`. +* `dir.go`: MFS `Directory`. +* `fd.go`: `FileDescriptor` used to operate on `File`s. +* `ops.go`: Functions that do not belong to either `File` nor `Directory` (although they mostly operate on them) that contain common operations to the MFS, e.g., find, move, add a file, make a directory. +* `system.go`: Made up of two parts, the MFS `Root` and the `Republisher`. +* `mfs_test.go`: General tests (needs a [revision](https://github.com/ipfs/go-mfs/issues/9)). +* `repub_test.go`: Republisher-specific tests (contains only the `TestRepublisher` function). + ## Contribute PRs accepted. diff --git a/mfs/dir.go b/mfs/dir.go index e42400a35..0b93aa997 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -21,13 +21,20 @@ var ErrNotYetImplemented = errors.New("not yet implemented") var ErrInvalidChild = errors.New("invalid child node") var ErrDirExists = errors.New("directory already has entry by that name") +// TODO: There's too much functionality associated with this structure, +// let's organize it (and if possible extract part of it elsewhere) +// and document the main features of `Directory` here. type Directory struct { inode + // Cache. + // TODO: Should this be a single cache of `FSNode`s? childDirs map[string]*Directory files map[string]*File lock sync.Mutex + // TODO: What content is being protected here exactly? The entire directory? + ctx context.Context // UnixFS directory implementation used for creating, @@ -73,12 +80,24 @@ func (d *Directory) SetCidBuilder(b cid.Builder) { // closeChild updates the child by the given name to the dag node 'nd' // and changes its own dag node +// `sync` (alias `fullsync`): has two uses, propagate the update upwards +// (in which case we wouldn't want this?) and in `closeChildUpdate`. +// TODO: Find *all* the places where `sync`/`fullsync` is evaluated. func (d *Directory) closeChild(name string, nd ipld.Node, sync bool) error { + + // There's a local flush (`closeChildUpdate`) and a propagated flush (`closeChild`). + mynd, err := d.closeChildUpdate(name, nd, sync) if err != nil { return err } + // TODO: The `sync` seems to be tightly coupling this two pieces of code, + // we use the node returned by `closeChildUpdate` (which entails a copy) + // only if `sync` is set, and we are discarding it otherwise. At the very + // least the `if sync {` clause at the end of `closeChildUpdate` should + // be merged with this one. + if sync { return d.parent.closeChild(d.name, mynd, true) } @@ -86,10 +105,22 @@ func (d *Directory) closeChild(name string, nd ipld.Node, sync bool) error { } // closeChildUpdate is the portion of closeChild that needs to be locked around +// TODO: Definitely document this. +// Updates the child entry under `name` with the node `nd` and if `sync` +// is set it "flushes" the node (adding it to the `DAGService`) that +// represents this directory. +// TODO: As mentioned elsewhere "flush" sometimes means persist the node in the +// DAG service and other update the parent node pointing to it. +// +// So, calling this with `sync`/`fullsync` off (this is pretty much the only +// place where `fullsync` seems to matter) will just update the file entry in +// this directory without updating the parent and without saving the node. func (d *Directory) closeChildUpdate(name string, nd ipld.Node, sync bool) (*dag.ProtoNode, error) { d.lock.Lock() defer d.lock.Unlock() + // TODO: Clearly define how are we propagating changes to lower layers + // like UnixFS. err := d.updateChild(name, nd) if err != nil { return nil, err @@ -118,6 +149,7 @@ func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) { } return pbnd.Copy().(*dag.ProtoNode), nil + // TODO: Why do we need a copy? } func (d *Directory) updateChild(name string, nd ipld.Node) error { @@ -200,6 +232,8 @@ func (d *Directory) Uncache(name string) { defer d.lock.Unlock() delete(d.files, name) delete(d.childDirs, name) + // TODO: We definitely need to join these maps if we are manipulating + // them like this. } // childFromDag searches through this directories dag node for a child link @@ -392,6 +426,9 @@ func (d *Directory) AddUnixFSChild(name string, node ipld.Node) error { return nil } +// TODO: Difference between `sync` and `Flush`? This seems +// to be related to the internal cache and not to the MFS +// hierarchy update. func (d *Directory) sync() error { for name, dir := range d.childDirs { nd, err := dir.GetNode() diff --git a/mfs/fd.go b/mfs/fd.go index fd4351b1a..45a715877 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -9,6 +9,12 @@ import ( context "context" ) +// One `File` can have many `FileDescriptor`s associated to it +// (only one if it's RW, many if they are RO, see `File.desclock`). +// A `FileDescriptor` contains the "view" of the file (through an +// instance of a `DagModifier`), that's why it (and not the `File`) +// has the responsibility to `Flush` (which crystallizes that view +// in the `File`'s `Node`). type FileDescriptor interface { io.Reader CtxReadFull(context.Context, []byte) (int, error) @@ -32,6 +38,7 @@ type fileDescriptor struct { sync bool hasChanges bool + // TODO: Where is this variable set? closed bool } @@ -84,6 +91,7 @@ func (fi *fileDescriptor) Close() error { case OpenWriteOnly, OpenReadWrite: fi.inode.desclock.Unlock() } + // TODO: `closed` should be set here. }() if fi.closed { @@ -106,6 +114,8 @@ func (fi *fileDescriptor) Close() error { return nil } +// TODO: Who uses `Sync` and who `Flush`? Do the consumers of this API +// know about the (undocumented) `fullsync` argument? func (fi *fileDescriptor) Sync() error { return fi.flushUp(false) } @@ -116,6 +126,9 @@ func (fi *fileDescriptor) Flush() error { // flushUp syncs the file and adds it to the dagservice // it *must* be called with the File's lock taken +// TODO: What is `fullsync`? Propagate the changes upward +// to the root flushing every node in the path (the "up" +// part of `flushUp`). func (fi *fileDescriptor) flushUp(fullsync bool) error { nd, err := fi.mod.GetNode() if err != nil { @@ -129,9 +142,12 @@ func (fi *fileDescriptor) flushUp(fullsync bool) error { fi.inode.nodelk.Lock() fi.inode.node = nd + // TODO: Create a `SetNode` method. name := fi.inode.name parent := fi.inode.parent + // TODO: Can the parent be modified? Do we need to do this inside the lock? fi.inode.nodelk.Unlock() + // TODO: Maybe all this logic should happen in `File`. return parent.closeChild(name, nd, fullsync) } diff --git a/mfs/file.go b/mfs/file.go index e0be55089..2c6912551 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -13,12 +13,22 @@ import ( ipld "github.com/ipfs/go-ipld-format" ) +// File represents a file in the MFS, its logic its mainly targeted +// to coordinating (potentially many) `FileDescriptor`s pointing to +// it. type File struct { inode + // Lock to coordinate the `FileDescriptor`s associated to this file. desclock sync.RWMutex + // This isn't any node, it's the root node that represents the + // entire DAG of nodes that comprise the file. + // TODO: Rename, there should be an explicit term for these root nodes + // of a particular sub-DAG that abstract an upper layer's entity. node ipld.Node + + // TODO: Rename. nodelk sync.Mutex RawLeaves bool @@ -52,6 +62,10 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { node := fi.node fi.nodelk.Unlock() + // TODO: Move this `switch` logic outside (maybe even + // to another package, this seems like a job of UnixFS), + // `NewDagModifier` uses the IPLD node, we're not + // extracting anything just doing a safety check. switch node := node.(type) { case *dag.ProtoNode: fsn, err := ft.FSNodeFromBytes(node.Data()) @@ -82,6 +96,8 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { } dmod, err := mod.NewDagModifier(context.TODO(), node, fi.dagService, chunker.DefaultSplitter) + // TODO: Remove the use of the `chunker` package here, add a new `NewDagModifier` in + // `go-unixfs` with the `DefaultSplitter` already included. if err != nil { return nil, err } @@ -96,6 +112,11 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { } // Size returns the size of this file +// TODO: Should we be providing this API? +// TODO: There's already a `FileDescriptor.Size()` that +// through the `DagModifier`'s `fileSize` function is doing +// pretty much the same thing as here, we should at least call +// that function and wrap the `ErrNotUnixfs` with an MFS text. func (fi *File) Size() (int64, error) { fi.nodelk.Lock() defer fi.nodelk.Unlock() @@ -114,12 +135,21 @@ func (fi *File) Size() (int64, error) { } // GetNode returns the dag node associated with this file +// TODO: Use this method and do not access the `nodelk` directly anywhere else. func (fi *File) GetNode() (ipld.Node, error) { fi.nodelk.Lock() defer fi.nodelk.Unlock() return fi.node, nil } +// TODO: Tight coupling with the `FileDescriptor`, at the +// very least this should be an independent function that +// takes a `File` argument and automates the open/flush/close +// operations. +// TODO: Why do we need to flush a file that isn't opened? +// (the `OpenWriteOnly` seems to implicitly be targeting a +// closed file, a file we forgot to flush? can we close +// a file without flushing?) func (fi *File) Flush() error { // open the file in fullsync mode fd, err := fi.Open(OpenWriteOnly, true) @@ -134,6 +164,7 @@ func (fi *File) Flush() error { func (fi *File) Sync() error { // just being able to take the writelock means the descriptor is synced + // TODO: Why? fi.desclock.Lock() fi.desclock.Unlock() return nil diff --git a/mfs/ops.go b/mfs/ops.go index 656b8dff9..dc3da4ca2 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -12,7 +12,13 @@ import ( ipld "github.com/ipfs/go-ipld-format" ) +// TODO: Evaluate moving all this operations to as `Root` +// methods, since all of them use it as its first argument +// and there is no clear documentation that explains this +// separation. + // Mv moves the file or directory at 'src' to 'dst' +// TODO: Document what the strings 'src' and 'dst' represent. func Mv(r *Root, src, dst string) error { srcDir, srcFname := gopath.Split(src) @@ -83,6 +89,15 @@ func lookupDir(r *Root, path string) (*Directory, error) { } // PutNode inserts 'nd' at 'path' in the given mfs +// TODO: Rename or clearly document that this is not about nodes but actually +// MFS files/directories (that in the underlying representation can be +// considered as just nodes). +// TODO: Document why are we handling IPLD nodes in the first place when we +// are actually referring to files/directories (that is, it can't be any +// node, it has to have a specific format). +// TODO: Can this function add directories or just files? What would be the +// difference between adding a directory with this method and creating it +// with `Mkdir`. func PutNode(r *Root, path string, nd ipld.Node) error { dirp, filename := gopath.Split(path) if filename == "" { @@ -207,6 +222,8 @@ func DirLookup(d *Directory, pth string) (FSNode, error) { return cur, nil } +// TODO: Document this function and link its functionality +// with the republisher. func FlushPath(rt *Root, pth string) error { nd, err := Lookup(rt, pth) if err != nil { diff --git a/mfs/system.go b/mfs/system.go index cc66aa91e..d7c103e79 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -1,4 +1,5 @@ // package mfs implements an in memory model of a mutable IPFS filesystem. +// TODO: Develop on this line (and move it elsewhere), delete the rest. // // It consists of four main structs: // 1) The Filesystem @@ -24,12 +25,23 @@ import ( logging "github.com/ipfs/go-log" ) +// TODO: Remove if not used. var ErrNotExist = errors.New("no such rootfs") var log = logging.Logger("mfs") +// TODO: Remove if not used. var ErrIsDirectory = errors.New("error: is a directory") +// TODO: Rename (avoid "close" terminology, if anything +// we are persisting/flushing changes). +// This is always a directory (since we are referring to the parent), +// can be an intermediate directory in the filesystem or the `Root`. +// TODO: What is `fullsync`? (unnamed `bool` argument) +// TODO: There are two types of persistence/flush that need to be +// distinguished here, one at the DAG level (when I store the modified +// nodes in the DAG service) and one in the UnixFS/MFS level (when I modify +// the entry/link of the directory that pointed to the modified node). type childCloser interface { closeChild(string, ipld.Node, bool) error } @@ -41,7 +53,8 @@ const ( TDir ) -// FSNode represents any node (directory, root, or file) in the mfs filesystem. +// FSNode represents any node (directory, or file) in the MFS filesystem. +// Not to be confused with the `unixfs.FSNode`. type FSNode interface { GetNode() (ipld.Node, error) Flush() error @@ -67,9 +80,6 @@ type Root struct { repub *Republisher } -// PubFunc is the function used by the `publish()` method. -type PubFunc func(context.Context, cid.Cid) error - // NewRoot creates a new Root and starts up a republisher routine for it. func NewRoot(parent context.Context, ds ipld.DAGService, node *dag.ProtoNode, pf PubFunc) (*Root, error) { @@ -87,6 +97,7 @@ func NewRoot(parent context.Context, ds ipld.DAGService, node *dag.ProtoNode, pf fsn, err := ft.FSNodeFromBytes(node.Data()) if err != nil { log.Error("IPNS pointer was not unixfs node") + // TODO: IPNS pointer? return nil, err } @@ -100,6 +111,8 @@ func NewRoot(parent context.Context, ds ipld.DAGService, node *dag.ProtoNode, pf root.dir = newDir case ft.TFile, ft.TMetadata, ft.TRaw: return nil, fmt.Errorf("root can't be a file (unixfs type: %s)", fsn.Type()) + // TODO: This special error reporting case doesn't seem worth it, we either + // have a UnixFS directory or we don't. default: return nil, fmt.Errorf("unrecognized unixfs type: %s", fsn.Type()) } @@ -113,6 +126,7 @@ func (kr *Root) GetDirectory() *Directory { // Flush signals that an update has occurred since the last publish, // and updates the Root republisher. +// TODO: We are definitely abusing the "flush" terminology here. func (kr *Root) Flush() error { nd, err := kr.GetDirectory().GetNode() if err != nil { @@ -133,6 +147,8 @@ func (kr *Root) Flush() error { // may have unintended racy side effects. // A better implemented mfs system (one that does smarter internal caching and // refcounting) shouldnt need this method. +// TODO: Review the motivation behind this method once the cache system is +// refactored. func (kr *Root) FlushMemFree(ctx context.Context) error { dir := kr.GetDirectory() @@ -142,18 +158,25 @@ func (kr *Root) FlushMemFree(ctx context.Context) error { dir.lock.Lock() defer dir.lock.Unlock() + for name := range dir.files { delete(dir.files, name) } for name := range dir.childDirs { delete(dir.childDirs, name) } + // TODO: Can't we just create new maps? return nil } // closeChild implements the childCloser interface, and signals to the publisher that // there are changes ready to be published. +// This is the only thing that separates a `Root` from a `Directory`. +// TODO: Evaluate merging both. +// TODO: The `sync` argument isn't used here (we've already reached +// the top), document it and maybe make it an anonymous variable (if +// that's possible). func (kr *Root) closeChild(name string, nd ipld.Node, sync bool) error { err := kr.GetDirectory().dagService.Add(context.TODO(), nd) if err != nil { @@ -180,6 +203,11 @@ func (kr *Root) Close() error { return nil } +// TODO: Separate the remaining code in another file: `repub.go`. + +// PubFunc is the function used by the `publish()` method. +type PubFunc func(context.Context, cid.Cid) error + // Republisher manages when to publish a given entry. type Republisher struct { TimeoutLong time.Duration @@ -250,6 +278,8 @@ func (np *Republisher) Update(c cid.Cid) { } // Run is the main republisher loop. +// TODO: Document according to: +// https://github.com/ipfs/go-ipfs/issues/5092#issuecomment-398524255. func (np *Republisher) Run() { for { select { From d21436a80c30a282a5881f80bdf2b69f0bbdea4f Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Sun, 16 Dec 2018 13:25:24 -0300 Subject: [PATCH 2798/3817] add a `child` structure Unify the `string`/`ipld.Node` information a parent has about its children when updating a modified entry. This will be used to simplify the code around the `childCloser` interface and related logic. This commit was moved from ipfs/go-mfs@32ca97a1a6878236ea20bd4ce11413db596ce49f --- mfs/system.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mfs/system.go b/mfs/system.go index d7c103e79..83325679e 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -33,6 +33,15 @@ var log = logging.Logger("mfs") // TODO: Remove if not used. var ErrIsDirectory = errors.New("error: is a directory") +// The information that an MFS `Directory` has about its children +// when updating one of its entries: when a child mutates it signals +// its parent directory to update its entry (under `Name`) with the +// new content (in `Node`). +type child struct { + Name string + Node ipld.Node +} + // TODO: Rename (avoid "close" terminology, if anything // we are persisting/flushing changes). // This is always a directory (since we are referring to the parent), From 3c4791d0ab95ce6075d2859de7d973ec75cadeac Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Sun, 16 Dec 2018 13:47:43 -0300 Subject: [PATCH 2799/3817] use new `child` structure Use newly introduced `child` structure whenever the logically tied `string`/`ipld.Node` pair was used to represent a child entry in a directory operation. This commit was moved from ipfs/go-mfs@54062f44a4af9c4aa78699a4e6d7fb8862b7627b --- mfs/dir.go | 30 +++++++++++++++--------------- mfs/fd.go | 2 +- mfs/system.go | 8 ++++---- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 0b93aa997..4a82a1939 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -35,7 +35,7 @@ type Directory struct { lock sync.Mutex // TODO: What content is being protected here exactly? The entire directory? - ctx context.Context + ctx context.Context // UnixFS directory implementation used for creating, // reading and editing directories. @@ -83,11 +83,11 @@ func (d *Directory) SetCidBuilder(b cid.Builder) { // `sync` (alias `fullsync`): has two uses, propagate the update upwards // (in which case we wouldn't want this?) and in `closeChildUpdate`. // TODO: Find *all* the places where `sync`/`fullsync` is evaluated. -func (d *Directory) closeChild(name string, nd ipld.Node, sync bool) error { +func (d *Directory) closeChild(c child, sync bool) error { // There's a local flush (`closeChildUpdate`) and a propagated flush (`closeChild`). - mynd, err := d.closeChildUpdate(name, nd, sync) + mynd, err := d.closeChildUpdate(c, sync) if err != nil { return err } @@ -99,7 +99,7 @@ func (d *Directory) closeChild(name string, nd ipld.Node, sync bool) error { // be merged with this one. if sync { - return d.parent.closeChild(d.name, mynd, true) + return d.parent.closeChild(child{d.name, mynd}, true) } return nil } @@ -115,13 +115,13 @@ func (d *Directory) closeChild(name string, nd ipld.Node, sync bool) error { // So, calling this with `sync`/`fullsync` off (this is pretty much the only // place where `fullsync` seems to matter) will just update the file entry in // this directory without updating the parent and without saving the node. -func (d *Directory) closeChildUpdate(name string, nd ipld.Node, sync bool) (*dag.ProtoNode, error) { +func (d *Directory) closeChildUpdate(c child, sync bool) (*dag.ProtoNode, error) { d.lock.Lock() defer d.lock.Unlock() // TODO: Clearly define how are we propagating changes to lower layers // like UnixFS. - err := d.updateChild(name, nd) + err := d.updateChild(c) if err != nil { return nil, err } @@ -152,8 +152,8 @@ func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) { // TODO: Why do we need a copy? } -func (d *Directory) updateChild(name string, nd ipld.Node) error { - err := d.AddUnixFSChild(name, nd) +func (d *Directory) updateChild(c child) error { + err := d.AddUnixFSChild(c) if err != nil { return err } @@ -346,7 +346,7 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { return nil, err } - err = d.AddUnixFSChild(name, ndir) + err = d.AddUnixFSChild(child{name, ndir}) if err != nil { return nil, err } @@ -376,7 +376,7 @@ func (d *Directory) Flush() error { return err } - return d.parent.closeChild(d.name, nd, true) + return d.parent.closeChild(child{d.name, nd}, true) } // AddChild adds the node 'nd' under this directory giving it the name 'name' @@ -394,7 +394,7 @@ func (d *Directory) AddChild(name string, nd ipld.Node) error { return err } - err = d.AddUnixFSChild(name, nd) + err = d.AddUnixFSChild(child{name, nd}) if err != nil { return err } @@ -405,7 +405,7 @@ func (d *Directory) AddChild(name string, nd ipld.Node) error { // AddUnixFSChild adds a child to the inner UnixFS directory // and transitions to a HAMT implementation if needed. -func (d *Directory) AddUnixFSChild(name string, node ipld.Node) error { +func (d *Directory) AddUnixFSChild(c child) error { if uio.UseHAMTSharding { // If the directory HAMT implementation is being used and this // directory is actually a basic implementation switch it to HAMT. @@ -418,7 +418,7 @@ func (d *Directory) AddUnixFSChild(name string, node ipld.Node) error { } } - err := d.unixfsDir.AddChild(d.ctx, name, node) + err := d.unixfsDir.AddChild(d.ctx, c.Name, c.Node) if err != nil { return err } @@ -436,7 +436,7 @@ func (d *Directory) sync() error { return err } - err = d.updateChild(name, nd) + err = d.updateChild(child{name, nd}) if err != nil { return err } @@ -448,7 +448,7 @@ func (d *Directory) sync() error { return err } - err = d.updateChild(name, nd) + err = d.updateChild(child{name, nd}) if err != nil { return err } diff --git a/mfs/fd.go b/mfs/fd.go index 45a715877..7bcc15efb 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -149,7 +149,7 @@ func (fi *fileDescriptor) flushUp(fullsync bool) error { fi.inode.nodelk.Unlock() // TODO: Maybe all this logic should happen in `File`. - return parent.closeChild(name, nd, fullsync) + return parent.closeChild(child{name, nd}, fullsync) } // Seek implements io.Seeker diff --git a/mfs/system.go b/mfs/system.go index 83325679e..d56b87ee2 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -52,7 +52,7 @@ type child struct { // nodes in the DAG service) and one in the UnixFS/MFS level (when I modify // the entry/link of the directory that pointed to the modified node). type childCloser interface { - closeChild(string, ipld.Node, bool) error + closeChild(child, bool) error } type NodeType int @@ -186,14 +186,14 @@ func (kr *Root) FlushMemFree(ctx context.Context) error { // TODO: The `sync` argument isn't used here (we've already reached // the top), document it and maybe make it an anonymous variable (if // that's possible). -func (kr *Root) closeChild(name string, nd ipld.Node, sync bool) error { - err := kr.GetDirectory().dagService.Add(context.TODO(), nd) +func (kr *Root) closeChild(c child, sync bool) error { + err := kr.GetDirectory().dagService.Add(context.TODO(), c.Node) if err != nil { return err } if kr.repub != nil { - kr.repub.Update(nd.Cid()) + kr.repub.Update(c.Node.Cid()) } return nil } From f28dd6f67c2cac52109841e745dd4f3374da1683 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Sun, 16 Dec 2018 14:06:23 -0300 Subject: [PATCH 2800/3817] rename the `childCloser` interface Rename the `childCloser` interface to `mutableParent` to better reflect the fact that the structures that implement it (`Directory` and `Root`) have the function of parents in the MFS hierarchy. Rename also the method of the interface from `closeChild` to `updateChildEntry` shifting the focus away from in which circumstance is the method being called (when closing a child) to actually what operation is being performed by the method (updating an entry in the parent to the new content). This commit was moved from ipfs/go-mfs@e87a272a8780e1f2ed784ffd33a18030316ad6ed --- mfs/dir.go | 18 +++++++++--------- mfs/fd.go | 2 +- mfs/file.go | 2 +- mfs/inode.go | 2 +- mfs/system.go | 26 ++++++++++++++++++-------- 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 4a82a1939..31c884836 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -48,7 +48,7 @@ type Directory struct { // // You probably don't want to call this directly. Instead, construct a new root // using NewRoot. -func NewDirectory(ctx context.Context, name string, node ipld.Node, parent childCloser, dserv ipld.DAGService) (*Directory, error) { +func NewDirectory(ctx context.Context, name string, node ipld.Node, parent mutableParent, dserv ipld.DAGService) (*Directory, error) { db, err := uio.NewDirectoryFromNode(dserv, node) if err != nil { return nil, err @@ -78,16 +78,16 @@ func (d *Directory) SetCidBuilder(b cid.Builder) { d.unixfsDir.SetCidBuilder(b) } -// closeChild updates the child by the given name to the dag node 'nd' +// updateChildEntry updates the child by the given name to the dag node 'nd' // and changes its own dag node // `sync` (alias `fullsync`): has two uses, propagate the update upwards // (in which case we wouldn't want this?) and in `closeChildUpdate`. // TODO: Find *all* the places where `sync`/`fullsync` is evaluated. -func (d *Directory) closeChild(c child, sync bool) error { +func (d *Directory) updateChildEntry(c child, fullSync bool) error { - // There's a local flush (`closeChildUpdate`) and a propagated flush (`closeChild`). + // There's a local flush (`closeChildUpdate`) and a propagated flush (`updateChildEntry`). - mynd, err := d.closeChildUpdate(c, sync) + mynd, err := d.closeChildUpdate(c, fullSync) if err != nil { return err } @@ -98,13 +98,13 @@ func (d *Directory) closeChild(c child, sync bool) error { // least the `if sync {` clause at the end of `closeChildUpdate` should // be merged with this one. - if sync { - return d.parent.closeChild(child{d.name, mynd}, true) + if fullSync { + return d.parent.updateChildEntry(child{d.name, mynd}, true) } return nil } -// closeChildUpdate is the portion of closeChild that needs to be locked around +// closeChildUpdate is the portion of updateChildEntry that needs to be locked around // TODO: Definitely document this. // Updates the child entry under `name` with the node `nd` and if `sync` // is set it "flushes" the node (adding it to the `DAGService`) that @@ -376,7 +376,7 @@ func (d *Directory) Flush() error { return err } - return d.parent.closeChild(child{d.name, nd}, true) + return d.parent.updateChildEntry(child{d.name, nd}, true) } // AddChild adds the node 'nd' under this directory giving it the name 'name' diff --git a/mfs/fd.go b/mfs/fd.go index 7bcc15efb..d4a767c32 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -149,7 +149,7 @@ func (fi *fileDescriptor) flushUp(fullsync bool) error { fi.inode.nodelk.Unlock() // TODO: Maybe all this logic should happen in `File`. - return parent.closeChild(child{name, nd}, fullsync) + return parent.updateChildEntry(child{name, nd}, fullsync) } // Seek implements io.Seeker diff --git a/mfs/file.go b/mfs/file.go index 2c6912551..43713925e 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -36,7 +36,7 @@ type File struct { // NewFile returns a NewFile object with the given parameters. If the // Cid version is non-zero RawLeaves will be enabled. -func NewFile(name string, node ipld.Node, parent childCloser, dserv ipld.DAGService) (*File, error) { +func NewFile(name string, node ipld.Node, parent mutableParent, dserv ipld.DAGService) (*File, error) { fi := &File{ inode: inode{ name: name, diff --git a/mfs/inode.go b/mfs/inode.go index f0330a222..8f65672aa 100644 --- a/mfs/inode.go +++ b/mfs/inode.go @@ -13,7 +13,7 @@ type inode struct { name string // parent directory of this `inode` (which may be the `Root`). - parent childCloser + parent mutableParent // dagService used to store modifications made to the contents // of the file or directory the `inode` belongs to. diff --git a/mfs/system.go b/mfs/system.go index d56b87ee2..32940fd7a 100644 --- a/mfs/system.go +++ b/mfs/system.go @@ -42,17 +42,25 @@ type child struct { Node ipld.Node } -// TODO: Rename (avoid "close" terminology, if anything -// we are persisting/flushing changes). -// This is always a directory (since we are referring to the parent), -// can be an intermediate directory in the filesystem or the `Root`. +// This interface represents the basic property of MFS directories of updating +// children entries with modified content. Implemented by both the MFS +// `Directory` and `Root` (which is basically a `Directory` with republishing +// support). +// // TODO: What is `fullsync`? (unnamed `bool` argument) // TODO: There are two types of persistence/flush that need to be // distinguished here, one at the DAG level (when I store the modified // nodes in the DAG service) and one in the UnixFS/MFS level (when I modify // the entry/link of the directory that pointed to the modified node). -type childCloser interface { - closeChild(child, bool) error +type mutableParent interface { + // Method called by a child to its parent to signal to update the content + // pointed to in the entry by that child's name. The child sends as + // arguments its own information (under the `child` structure) and a flag + // (`fullsync`) indicating whether or not to propagate the update upwards: + // modifying a directory entry entails modifying its contents which means + // that its parent (the parent's parent) will also need to be updated (and + // so on). + updateChildEntry(c child, fullSync bool) error } type NodeType int @@ -179,18 +187,20 @@ func (kr *Root) FlushMemFree(ctx context.Context) error { return nil } -// closeChild implements the childCloser interface, and signals to the publisher that +// updateChildEntry implements the mutableParent interface, and signals to the publisher that // there are changes ready to be published. // This is the only thing that separates a `Root` from a `Directory`. // TODO: Evaluate merging both. // TODO: The `sync` argument isn't used here (we've already reached // the top), document it and maybe make it an anonymous variable (if // that's possible). -func (kr *Root) closeChild(c child, sync bool) error { +func (kr *Root) updateChildEntry(c child, fullSync bool) error { err := kr.GetDirectory().dagService.Add(context.TODO(), c.Node) if err != nil { return err } + // TODO: Why are we not using the inner directory lock nor + // applying the same procedure as `Directory.updateChildEntry`? if kr.repub != nil { kr.repub.Update(c.Node.Cid()) From 484df5468a8726ed1331211eb1f38150871e53d4 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Sun, 16 Dec 2018 14:59:49 -0300 Subject: [PATCH 2801/3817] document code around `Directory.updateChildEntry` This commit was moved from ipfs/go-mfs@ef5e1192db40164649f73fa4acd212eb9a45b366 --- mfs/dir.go | 58 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 31c884836..b3c8b3322 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -78,16 +78,20 @@ func (d *Directory) SetCidBuilder(b cid.Builder) { d.unixfsDir.SetCidBuilder(b) } -// updateChildEntry updates the child by the given name to the dag node 'nd' -// and changes its own dag node -// `sync` (alias `fullsync`): has two uses, propagate the update upwards -// (in which case we wouldn't want this?) and in `closeChildUpdate`. -// TODO: Find *all* the places where `sync`/`fullsync` is evaluated. +// This method implements the `mutableParent` interface. It first updates +// the child entry in the underlying UnixFS directory and then if `fullSync` +// is set it saves the new content through the internal DAG service. Then, +// also if `fullSync` is set, it propagates the update to its parent (through +// this same interface) with the new node already updated with the new entry. +// So, `fullSync` entails operations at two different layers: +// 1. DAG: save the newly created directory node with the updated entry. +// 2. MFS: propagate the update upwards repeating the whole process in the +// parent. func (d *Directory) updateChildEntry(c child, fullSync bool) error { // There's a local flush (`closeChildUpdate`) and a propagated flush (`updateChildEntry`). - mynd, err := d.closeChildUpdate(c, fullSync) + newDirNode, err := d.closeChildUpdate(c, fullSync) if err != nil { return err } @@ -96,42 +100,43 @@ func (d *Directory) updateChildEntry(c child, fullSync bool) error { // we use the node returned by `closeChildUpdate` (which entails a copy) // only if `sync` is set, and we are discarding it otherwise. At the very // least the `if sync {` clause at the end of `closeChildUpdate` should - // be merged with this one. + // be merged with this one (the use of the `lock` is stopping this at the + // moment, re-evaluate when its purpose has been better understood). if fullSync { - return d.parent.updateChildEntry(child{d.name, mynd}, true) + return d.parent.updateChildEntry(child{d.name, newDirNode}, true) + // Setting `fullSync` to true here means, if the original child that + // initiated the update process wanted to propagate it upwards then + // continue to do so all the way up to the root, that is, the only + // time `fullSync` can be false is in the first call (which will be + // the *only* call), we either update the first parent entry or *all* + // the parent's. } + return nil } -// closeChildUpdate is the portion of updateChildEntry that needs to be locked around -// TODO: Definitely document this. -// Updates the child entry under `name` with the node `nd` and if `sync` -// is set it "flushes" the node (adding it to the `DAGService`) that -// represents this directory. -// TODO: As mentioned elsewhere "flush" sometimes means persist the node in the -// DAG service and other update the parent node pointing to it. -// -// So, calling this with `sync`/`fullsync` off (this is pretty much the only -// place where `fullsync` seems to matter) will just update the file entry in -// this directory without updating the parent and without saving the node. -func (d *Directory) closeChildUpdate(c child, sync bool) (*dag.ProtoNode, error) { +// This method implements the part of `updateChildEntry` that needs +// to be locked around: in charge of updating the UnixFS layer and +// generating the new node reflecting the update. +func (d *Directory) closeChildUpdate(c child, fullSync bool) (*dag.ProtoNode, error) { d.lock.Lock() defer d.lock.Unlock() - // TODO: Clearly define how are we propagating changes to lower layers - // like UnixFS. err := d.updateChild(c) if err != nil { return nil, err } + // TODO: Clearly define how are we propagating changes to lower layers + // like UnixFS. - if sync { + if fullSync { return d.flushCurrentNode() } return nil, nil } +// Recreate the underlying UnixFS directory node and save it in the DAG layer. func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) { nd, err := d.unixfsDir.GetNode() if err != nil { @@ -142,16 +147,23 @@ func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) { if err != nil { return nil, err } + // TODO: This method is called in `closeChildUpdate` while the lock is + // taken, we need the lock while operating on `unixfsDir` to create the + // new node but do we also need to keep the lock while adding it to the + // DAG service? Evaluate refactoring these two methods together and better + // redistributing the node. pbnd, ok := nd.(*dag.ProtoNode) if !ok { return nil, dag.ErrNotProtobuf } + // TODO: Why do we check the node *after* adding it to the DAG service? return pbnd.Copy().(*dag.ProtoNode), nil // TODO: Why do we need a copy? } +// Update child entry in the underlying UnixFS directory. func (d *Directory) updateChild(c child) error { err := d.AddUnixFSChild(c) if err != nil { From 25b91e030f375e884af370b597dfa4c9643bbd73 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Sun, 16 Dec 2018 15:06:48 -0300 Subject: [PATCH 2802/3817] move `Republisher` to a separate file Extract the `Republisher` structure and related logic from `system.go` (leaving that file mainly with the `Root` logic, and hence now renamed as `root.go`) to a new `repub.go` file. Even though the MFS root has support for republishing updates the logic behind those two structures is mostly decoupled, that should be reflected in the source code organization. This commit was moved from ipfs/go-mfs@8539b814f6e3b863062dd43fccd988ebb34a1110 --- mfs/README.md | 3 +- mfs/repub.go | 135 +++++++++++++++++++++++++++++++++++ mfs/{system.go => root.go} | 141 +------------------------------------ 3 files changed, 139 insertions(+), 140 deletions(-) create mode 100644 mfs/repub.go rename mfs/{system.go => root.go} (64%) diff --git a/mfs/README.md b/mfs/README.md index 86c9d9b5d..084c74919 100644 --- a/mfs/README.md +++ b/mfs/README.md @@ -40,7 +40,8 @@ This repository contains many files, all belonging to the root `mfs` package. * `dir.go`: MFS `Directory`. * `fd.go`: `FileDescriptor` used to operate on `File`s. * `ops.go`: Functions that do not belong to either `File` nor `Directory` (although they mostly operate on them) that contain common operations to the MFS, e.g., find, move, add a file, make a directory. -* `system.go`: Made up of two parts, the MFS `Root` and the `Republisher`. +* `root.go`: MFS `Root` (a `Directory` with republishing support). +* `repub.go`: `Republisher`. * `mfs_test.go`: General tests (needs a [revision](https://github.com/ipfs/go-mfs/issues/9)). * `repub_test.go`: Republisher-specific tests (contains only the `TestRepublisher` function). diff --git a/mfs/repub.go b/mfs/repub.go new file mode 100644 index 000000000..3ffcb9758 --- /dev/null +++ b/mfs/repub.go @@ -0,0 +1,135 @@ +package mfs + +import ( + "context" + "sync" + "time" + + cid "github.com/ipfs/go-cid" +) + +// PubFunc is the function used by the `publish()` method. +type PubFunc func(context.Context, cid.Cid) error + +// Republisher manages when to publish a given entry. +type Republisher struct { + TimeoutLong time.Duration + TimeoutShort time.Duration + Publish chan struct{} + pubfunc PubFunc + pubnowch chan chan struct{} + + ctx context.Context + cancel func() + + lk sync.Mutex + val cid.Cid + lastpub cid.Cid +} + +// NewRepublisher creates a new Republisher object to republish the given root +// using the given short and long time intervals. +func NewRepublisher(ctx context.Context, pf PubFunc, tshort, tlong time.Duration) *Republisher { + ctx, cancel := context.WithCancel(ctx) + return &Republisher{ + TimeoutShort: tshort, + TimeoutLong: tlong, + Publish: make(chan struct{}, 1), + pubfunc: pf, + pubnowch: make(chan chan struct{}), + ctx: ctx, + cancel: cancel, + } +} + +func (p *Republisher) setVal(c cid.Cid) { + p.lk.Lock() + defer p.lk.Unlock() + p.val = c +} + +// WaitPub Returns immediately if `lastpub` value is consistent with the +// current value `val`, else will block until `val` has been published. +func (p *Republisher) WaitPub() { + p.lk.Lock() + consistent := p.lastpub == p.val + p.lk.Unlock() + if consistent { + return + } + + wait := make(chan struct{}) + p.pubnowch <- wait + <-wait +} + +func (p *Republisher) Close() error { + err := p.publish(p.ctx) + p.cancel() + return err +} + +// Touch signals that an update has occurred since the last publish. +// Multiple consecutive touches may extend the time period before +// the next Publish occurs in order to more efficiently batch updates. +func (np *Republisher) Update(c cid.Cid) { + np.setVal(c) + select { + case np.Publish <- struct{}{}: + default: + } +} + +// Run is the main republisher loop. +// TODO: Document according to: +// https://github.com/ipfs/go-ipfs/issues/5092#issuecomment-398524255. +func (np *Republisher) Run() { + for { + select { + case <-np.Publish: + quick := time.After(np.TimeoutShort) + longer := time.After(np.TimeoutLong) + + wait: + var pubnowresp chan struct{} + + select { + case <-np.ctx.Done(): + return + case <-np.Publish: + quick = time.After(np.TimeoutShort) + goto wait + case <-quick: + case <-longer: + case pubnowresp = <-np.pubnowch: + } + + err := np.publish(np.ctx) + if pubnowresp != nil { + pubnowresp <- struct{}{} + } + if err != nil { + log.Errorf("republishRoot error: %s", err) + } + + case <-np.ctx.Done(): + return + } + } +} + +// publish calls the `PubFunc`. +func (np *Republisher) publish(ctx context.Context) error { + np.lk.Lock() + topub := np.val + np.lk.Unlock() + + err := np.pubfunc(ctx, topub) + if err != nil { + return err + } + np.lk.Lock() + np.lastpub = topub + np.lk.Unlock() + return nil +} diff --git a/mfs/system.go b/mfs/root.go similarity index 64% rename from mfs/system.go rename to mfs/root.go index 32940fd7a..d92f1837b 100644 --- a/mfs/system.go +++ b/mfs/root.go @@ -1,26 +1,17 @@ // package mfs implements an in memory model of a mutable IPFS filesystem. -// TODO: Develop on this line (and move it elsewhere), delete the rest. -// -// It consists of four main structs: -// 1) The Filesystem -// The filesystem serves as a container and entry point for various mfs filesystems -// 2) Root -// Root represents an individual filesystem mounted within the mfs system as a whole -// 3) Directories -// 4) Files +// TODO: Develop on this line (and move it to `doc.go`). + package mfs import ( "context" "errors" "fmt" - "sync" "time" dag "github.com/ipfs/go-merkledag" ft "github.com/ipfs/go-unixfs" - cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" ) @@ -221,131 +212,3 @@ func (kr *Root) Close() error { return nil } - -// TODO: Separate the remaining code in another file: `repub.go`. - -// PubFunc is the function used by the `publish()` method. -type PubFunc func(context.Context, cid.Cid) error - -// Republisher manages when to publish a given entry. -type Republisher struct { - TimeoutLong time.Duration - TimeoutShort time.Duration - Publish chan struct{} - pubfunc PubFunc - pubnowch chan chan struct{} - - ctx context.Context - cancel func() - - lk sync.Mutex - val cid.Cid - lastpub cid.Cid -} - -// NewRepublisher creates a new Republisher object to republish the given root -// using the given short and long time intervals. -func NewRepublisher(ctx context.Context, pf PubFunc, tshort, tlong time.Duration) *Republisher { - ctx, cancel := context.WithCancel(ctx) - return &Republisher{ - TimeoutShort: tshort, - TimeoutLong: tlong, - Publish: make(chan struct{}, 1), - pubfunc: pf, - pubnowch: make(chan chan struct{}), - ctx: ctx, - cancel: cancel, - } -} - -func (p *Republisher) setVal(c cid.Cid) { - p.lk.Lock() - defer p.lk.Unlock() - p.val = c -} - -// WaitPub Returns immediately if `lastpub` value is consistent with the -// current value `val`, else will block until `val` has been published. -func (p *Republisher) WaitPub() { - p.lk.Lock() - consistent := p.lastpub == p.val - p.lk.Unlock() - if consistent { - return - } - - wait := make(chan struct{}) - p.pubnowch <- wait - <-wait -} - -func (p *Republisher) Close() error { - err := p.publish(p.ctx) - p.cancel() - return err -} - -// Touch signals that an update has occurred since the last publish. -// Multiple consecutive touches may extend the time period before -// the next Publish occurs in order to more efficiently batch updates. -func (np *Republisher) Update(c cid.Cid) { - np.setVal(c) - select { - case np.Publish <- struct{}{}: - default: - } -} - -// Run is the main republisher loop. -// TODO: Document according to: -// https://github.com/ipfs/go-ipfs/issues/5092#issuecomment-398524255. -func (np *Republisher) Run() { - for { - select { - case <-np.Publish: - quick := time.After(np.TimeoutShort) - longer := time.After(np.TimeoutLong) - - wait: - var pubnowresp chan struct{} - - select { - case <-np.ctx.Done(): - return - case <-np.Publish: - quick = time.After(np.TimeoutShort) - goto wait - case <-quick: - case <-longer: - case pubnowresp = <-np.pubnowch: - } - - err := np.publish(np.ctx) - if pubnowresp != nil { - pubnowresp <- struct{}{} - } - if err != nil { - log.Errorf("republishRoot error: %s", err) - } - - case <-np.ctx.Done(): - return - } - } -} - -// publish calls the `PubFunc`. -func (np *Republisher) publish(ctx context.Context) error { - np.lk.Lock() - topub := np.val - np.lk.Unlock() - - err := np.pubfunc(ctx, topub) - if err != nil { - return err - } - np.lk.Lock() - np.lastpub = topub - np.lk.Unlock() - return nil -} From 1a74873444314107ba5d45fab8031b14c60c1b0c Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Sun, 16 Dec 2018 20:49:26 -0300 Subject: [PATCH 2803/3817] remove `Sync` from `FileDescriptor` Remove the `Sync()` method from the `FileDescriptor` and also its implementation. Two different undocumented methods in `FileDescriptor` are offered to update the state of the MFS: `Flush()` and `Sync()`, both calling `updateChildEntry`. The only difference (the user is not aware of) is that one sets the `fullSync` argument and the other doesn't, that is, one does a full update of the filesystem and the other just updates the parent directory. This current situation is not clear to the consumer who may use one desiring the effect of the other. As a precautionary measure while redesigning the MFS API one method is removed to simplify its usage. The `Sync()` was chosen as the less "safe" alternative which didn't do a full update of the MFS and also doesn't seem to be used in the `go-ipfs` repository (the main consumer of the `mfs` package). This commit was moved from ipfs/go-mfs@2f52311782f3441d5c871283b6cb3ab4a8545594 --- mfs/fd.go | 20 +++++++++++--------- mfs/mfs_test.go | 6 ------ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/mfs/fd.go b/mfs/fd.go index d4a767c32..8a27cb57e 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -27,7 +27,6 @@ type FileDescriptor interface { Truncate(int64) error Size() (int64, error) - Sync() error Flush() error } @@ -114,12 +113,10 @@ func (fi *fileDescriptor) Close() error { return nil } -// TODO: Who uses `Sync` and who `Flush`? Do the consumers of this API -// know about the (undocumented) `fullsync` argument? -func (fi *fileDescriptor) Sync() error { - return fi.flushUp(false) -} - +// Flush generates a new version of the node of the underlying +// UnixFS directory (adding it to the DAG service) and updates +// the entry in the parent directory (setting `fullSync` to +// propagate the update all the way to the root). func (fi *fileDescriptor) Flush() error { return fi.flushUp(true) } @@ -129,7 +126,7 @@ func (fi *fileDescriptor) Flush() error { // TODO: What is `fullsync`? Propagate the changes upward // to the root flushing every node in the path (the "up" // part of `flushUp`). -func (fi *fileDescriptor) flushUp(fullsync bool) error { +func (fi *fileDescriptor) flushUp(fullSync bool) error { nd, err := fi.mod.GetNode() if err != nil { return err @@ -139,6 +136,11 @@ func (fi *fileDescriptor) flushUp(fullsync bool) error { if err != nil { return err } + // TODO: Very similar logic to the update process in + // `Directory`, the logic should be unified, both structures + // (`File` and `Directory`) are backed by a IPLD node with + // a UnixFS format that is the actual target of the update + // (regenerating it and adding it to the DAG service). fi.inode.nodelk.Lock() fi.inode.node = nd @@ -149,7 +151,7 @@ func (fi *fileDescriptor) flushUp(fullsync bool) error { fi.inode.nodelk.Unlock() // TODO: Maybe all this logic should happen in `File`. - return parent.updateChildEntry(child{name, nd}, fullsync) + return parent.updateChildEntry(child{name, nd}, fullSync) } // Seek implements io.Seeker diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index ee53d35bf..20753466a 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -419,12 +419,6 @@ func TestMfsFile(t *testing.T) { t.Fatal("didnt write correct number of bytes") } - // sync file - err = wfd.Sync() - if err != nil { - t.Fatal(err) - } - // make sure size hasnt changed size, err = wfd.Size() if err != nil { From 970544a1e1c6949dc06516e23078e064c66467c5 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Sun, 16 Dec 2018 22:05:44 -0300 Subject: [PATCH 2804/3817] unify the caches in `Directory` Unify the two caches in `Directory` that discriminated between files and directories (`files` and `childDirs`) into a single `entriesCache` since that distinction wasn't used anywhere and made the code unnecessarily more complex. This commit was moved from ipfs/go-mfs@925c0a80bcfb03de907bef2299a6d0c06403429c --- mfs/dir.go | 60 ++++++++++++++++------------------------------------- mfs/root.go | 14 ++++++------- 2 files changed, 25 insertions(+), 49 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index b3c8b3322..80ead54b9 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -27,10 +27,9 @@ var ErrDirExists = errors.New("directory already has entry by that name") type Directory struct { inode - // Cache. - // TODO: Should this be a single cache of `FSNode`s? - childDirs map[string]*Directory - files map[string]*File + // Internal cache with added entries to the directory, its cotents + // are synched with the underlying `unixfsDir` node in `sync()`. + entriesCache map[string]FSNode lock sync.Mutex // TODO: What content is being protected here exactly? The entire directory? @@ -60,11 +59,10 @@ func NewDirectory(ctx context.Context, name string, node ipld.Node, parent mutab parent: parent, dagService: dserv, }, - ctx: ctx, - unixfsDir: db, - childDirs: make(map[string]*Directory), - files: make(map[string]*File), - modTime: time.Now(), + ctx: ctx, + unixfsDir: db, + entriesCache: make(map[string]FSNode), + modTime: time.Now(), }, nil } @@ -206,14 +204,14 @@ func (d *Directory) cacheNode(name string, nd ipld.Node) (FSNode, error) { return nil, err } - d.childDirs[name] = ndir + d.entriesCache[name] = ndir return ndir, nil case ft.TFile, ft.TRaw, ft.TSymlink: nfi, err := NewFile(name, nd, d, d.dagService) if err != nil { return nil, err } - d.files[name] = nfi + d.entriesCache[name] = nfi return nfi, nil case ft.TMetadata: return nil, ErrNotYetImplemented @@ -225,7 +223,7 @@ func (d *Directory) cacheNode(name string, nd ipld.Node) (FSNode, error) { if err != nil { return nil, err } - d.files[name] = nfi + d.entriesCache[name] = nfi return nfi, nil default: return nil, fmt.Errorf("unrecognized node type in cache node") @@ -242,10 +240,7 @@ func (d *Directory) Child(name string) (FSNode, error) { func (d *Directory) Uncache(name string) { d.lock.Lock() defer d.lock.Unlock() - delete(d.files, name) - delete(d.childDirs, name) - // TODO: We definitely need to join these maps if we are manipulating - // them like this. + delete(d.entriesCache, name) } // childFromDag searches through this directories dag node for a child link @@ -257,14 +252,9 @@ func (d *Directory) childFromDag(name string) (ipld.Node, error) { // childUnsync returns the child under this directory by the given name // without locking, useful for operations which already hold a lock func (d *Directory) childUnsync(name string) (FSNode, error) { - cdir, ok := d.childDirs[name] + entry, ok := d.entriesCache[name] if ok { - return cdir, nil - } - - cfile, ok := d.files[name] - if ok { - return cfile, nil + return entry, nil } return d.childNode(name) @@ -368,7 +358,7 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { return nil, err } - d.childDirs[name] = dirobj + d.entriesCache[name] = dirobj return dirobj, nil } @@ -376,8 +366,7 @@ func (d *Directory) Unlink(name string) error { d.lock.Lock() defer d.lock.Unlock() - delete(d.childDirs, name) - delete(d.files, name) + delete(d.entriesCache, name) return d.unixfsDir.RemoveChild(d.ctx, name) } @@ -438,12 +427,9 @@ func (d *Directory) AddUnixFSChild(c child) error { return nil } -// TODO: Difference between `sync` and `Flush`? This seems -// to be related to the internal cache and not to the MFS -// hierarchy update. func (d *Directory) sync() error { - for name, dir := range d.childDirs { - nd, err := dir.GetNode() + for name, entry := range d.entriesCache { + nd, err := entry.GetNode() if err != nil { return err } @@ -454,17 +440,7 @@ func (d *Directory) sync() error { } } - for name, file := range d.files { - nd, err := file.GetNode() - if err != nil { - return err - } - - err = d.updateChild(child{name, nd}) - if err != nil { - return err - } - } + // TODO: Should we clean the cache here? return nil } diff --git a/mfs/root.go b/mfs/root.go index d92f1837b..4831aba15 100644 --- a/mfs/root.go +++ b/mfs/root.go @@ -61,8 +61,11 @@ const ( TDir ) -// FSNode represents any node (directory, or file) in the MFS filesystem. -// Not to be confused with the `unixfs.FSNode`. +// FSNode abstracts the `Directory` and `File` structures, it represents +// any child node in the MFS (i.e., all the nodes besides the `Root`). It +// is the counterpart of the `mutableParent` interface which represents any +// parent node in the MFS (`Root` and `Directory`). +// (Not to be confused with the `unixfs.FSNode`.) type FSNode interface { GetNode() (ipld.Node, error) Flush() error @@ -167,11 +170,8 @@ func (kr *Root) FlushMemFree(ctx context.Context) error { dir.lock.Lock() defer dir.lock.Unlock() - for name := range dir.files { - delete(dir.files, name) - } - for name := range dir.childDirs { - delete(dir.childDirs, name) + for name := range dir.entriesCache { + delete(dir.entriesCache, name) } // TODO: Can't we just create new maps? From e456ec5feb819d56826361dccd729580e01bf3a1 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Tue, 18 Dec 2018 12:02:19 -0300 Subject: [PATCH 2805/3817] add documentation links in README This commit was moved from ipfs/go-mfs@777ea2151c870dbf4d2c44f998e3fd23f9d9e00d --- mfs/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mfs/README.md b/mfs/README.md index 86c9d9b5d..da0dcdf49 100644 --- a/mfs/README.md +++ b/mfs/README.md @@ -33,6 +33,14 @@ import "github.com/ipfs/go-mfs" Check the [GoDoc documentation](https://godoc.org/github.com/ipfs/go-mfs) +## Documentation + +Documentation around the MFS and the Files API in general around IPFS is a work in progress the following links may be of use: + +* [UnixFS](https://docs.ipfs.io/guides/concepts/unixfs/) +* [MFS](https://docs.ipfs.io/guides/concepts/mfs/) +* [General concept document about how are files handled in IPFS (WIP)](https://github.com/ipfs/docs/issues/133) + ## Repository Structure This repository contains many files, all belonging to the root `mfs` package. From bbe5ff6adbc01fb33b4a2d7928cd22ade2825657 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Mon, 17 Dec 2018 01:09:50 -0300 Subject: [PATCH 2806/3817] document republisher logic Document the `Republisher` code in `repub.go` according to the explanation in https://github.com/ipfs/go-ipfs/issues/5092#issuecomment-398524255. Erring on the side of verbosity since this has been a part of the code with very little review up until now. This commit was moved from ipfs/go-mfs@0abe7695d65d99f7c6bcfb38aee6110a24918727 --- mfs/repub.go | 128 +++++++++++++++++++++++++++++---------------------- mfs/root.go | 6 ++- 2 files changed, 79 insertions(+), 55 deletions(-) diff --git a/mfs/repub.go b/mfs/repub.go index 3ffcb9758..e96f04472 100644 --- a/mfs/repub.go +++ b/mfs/repub.go @@ -8,23 +8,24 @@ import ( cid "github.com/ipfs/go-cid" ) -// PubFunc is the function used by the `publish()` method. +// PubFunc is the user-defined function that determines exactly what +// logic entails "publishing" a `Cid` value. type PubFunc func(context.Context, cid.Cid) error // Republisher manages when to publish a given entry. type Republisher struct { - TimeoutLong time.Duration - TimeoutShort time.Duration - Publish chan struct{} - pubfunc PubFunc - pubnowch chan chan struct{} + TimeoutLong time.Duration + TimeoutShort time.Duration + valueHasBeenUpdated chan struct{} + pubfunc PubFunc + immediatePublish chan chan struct{} + + valueLock sync.Mutex + valueToPublish cid.Cid + lastValuePublished cid.Cid ctx context.Context cancel func() - - lk sync.Mutex - val cid.Cid - lastpub cid.Cid } // NewRepublisher creates a new Republisher object to republish the given root @@ -32,34 +33,28 @@ type Republisher struct { func NewRepublisher(ctx context.Context, pf PubFunc, tshort, tlong time.Duration) *Republisher { ctx, cancel := context.WithCancel(ctx) return &Republisher{ - TimeoutShort: tshort, - TimeoutLong: tlong, - Publish: make(chan struct{}, 1), - pubfunc: pf, - pubnowch: make(chan chan struct{}), - ctx: ctx, - cancel: cancel, + TimeoutShort: tshort, + TimeoutLong: tlong, + valueHasBeenUpdated: make(chan struct{}, 1), + pubfunc: pf, + immediatePublish: make(chan chan struct{}), + ctx: ctx, + cancel: cancel, } } -func (p *Republisher) setVal(c cid.Cid) { - p.lk.Lock() - defer p.lk.Unlock() - p.val = c -} - -// WaitPub Returns immediately if `lastpub` value is consistent with the -// current value `val`, else will block until `val` has been published. +// WaitPub waits for the current value to be published (or returns early +// if it already has). func (p *Republisher) WaitPub() { - p.lk.Lock() - consistent := p.lastpub == p.val - p.lk.Unlock() - if consistent { + p.valueLock.Lock() + valueHasBeenPublished := p.lastValuePublished == p.valueToPublish + p.valueLock.Unlock() + if valueHasBeenPublished { return } wait := make(chan struct{}) - p.pubnowch <- wait + p.immediatePublish <- wait <-wait } @@ -69,67 +64,92 @@ func (p *Republisher) Close() error { return err } -// Touch signals that an update has occurred since the last publish. -// Multiple consecutive touches may extend the time period before -// the next Publish occurs in order to more efficiently batch updates. +// Update the `valueToPublish` and signal it in the `valueHasBeenUpdated` +// channel. Multiple consecutive updates may extend the time period before +// the next publish occurs in order to more efficiently batch updates. func (np *Republisher) Update(c cid.Cid) { - np.setVal(c) + np.valueLock.Lock() + np.valueToPublish = c + np.valueLock.Unlock() + select { - case np.Publish <- struct{}{}: + case np.valueHasBeenUpdated <- struct{}{}: default: } } -// Run is the main republisher loop. -// TODO: Document according to: -// https://github.com/ipfs/go-ipfs/issues/5092#issuecomment-398524255. +// Run contains the core logic of the `Republisher`. It calls the user-defined +// `pubfunc` function whenever the `Cid` value is updated. The complexity comes +// from the fact that `pubfunc` may be slow so we need to batch updates. +// Algorithm: +// 1. When we receive the first update after publishing, we set a `longer` timer. +// 2. When we receive any update, we reset the `quick` timer. +// 3. If either the `quick` timeout or the `longer` timeout elapses, +// we call `publish` with the latest updated value. +// +// The `longer` timer ensures that we delay publishing by at most +// `TimeoutLong`. The `quick` timer allows us to publish sooner if +// it looks like there are no more updates coming down the pipe. func (np *Republisher) Run() { for { select { - case <-np.Publish: + case <-np.ctx.Done(): + return + case <-np.valueHasBeenUpdated: + // Fast timeout, a `publish` will be issued if there are + // no more updates before it expires (restarted every time + // the `valueHasBeenUpdated` is signaled). quick := time.After(np.TimeoutShort) + // Long timeout that guarantees a `publish` after it expires + // even if the value keeps being updated (and `quick` is + // restarted). longer := time.After(np.TimeoutLong) wait: - var pubnowresp chan struct{} + var valueHasBeenPublished chan struct{} select { case <-np.ctx.Done(): return - case <-np.Publish: + case <-np.valueHasBeenUpdated: + // The `valueToPublish` has been updated *again* since + // the last time we checked and we still haven't published + // it, restart the `quick` timer allowing for some more + // time to see if the `valueToPublish` changes again. quick = time.After(np.TimeoutShort) goto wait + case <-quick: case <-longer: - case pubnowresp = <-np.pubnowch: + case valueHasBeenPublished = <-np.immediatePublish: } err := np.publish(np.ctx) - if pubnowresp != nil { - pubnowresp <- struct{}{} + if valueHasBeenPublished != nil { + // The user is waiting in `WaitPub` with this channel, signal + // that the `publish` has happened. + valueHasBeenPublished <- struct{}{} } if err != nil { log.Errorf("republishRoot error: %s", err) } - - case <-np.ctx.Done(): - return } } } -// publish calls the `PubFunc`. +// Wrapper function around the user-defined `pubfunc`. It publishes +// the (last) `valueToPublish` set and registers it in `lastValuePublished`. func (np *Republisher) publish(ctx context.Context) error { - np.lk.Lock() - topub := np.val - np.lk.Unlock() + np.valueLock.Lock() + topub := np.valueToPublish + np.valueLock.Unlock() err := np.pubfunc(ctx, topub) if err != nil { return err } - np.lk.Lock() - np.lastpub = topub - np.lk.Unlock() + np.valueLock.Lock() + np.lastValuePublished = topub + np.valueLock.Unlock() return nil } diff --git a/mfs/root.go b/mfs/root.go index 4831aba15..b3c311ef5 100644 --- a/mfs/root.go +++ b/mfs/root.go @@ -97,7 +97,11 @@ func NewRoot(parent context.Context, ds ipld.DAGService, node *dag.ProtoNode, pf var repub *Republisher if pf != nil { repub = NewRepublisher(parent, pf, time.Millisecond*300, time.Second*3) - repub.setVal(node.Cid()) + + repub.valueToPublish = node.Cid() + // No need to take the lock here since we just created + // the `Republisher` and no one has access to it yet. + go repub.Run() } From 9363790c2c8d2d0b7afb29ab991ea52a410c755c Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Mon, 17 Dec 2018 01:22:33 -0300 Subject: [PATCH 2807/3817] rename all `Republisher` receivers Unify all the `Republisher` receivers names to `rp` (avoiding `r` to distinguish it from the `Root` receivers). This commit was moved from ipfs/go-mfs@4809106df1424c32acc9dcf13742b9ff445c464b --- mfs/repub.go | 62 ++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/mfs/repub.go b/mfs/repub.go index e96f04472..1efda7b5c 100644 --- a/mfs/repub.go +++ b/mfs/repub.go @@ -45,35 +45,35 @@ func NewRepublisher(ctx context.Context, pf PubFunc, tshort, tlong time.Duration // WaitPub waits for the current value to be published (or returns early // if it already has). -func (p *Republisher) WaitPub() { - p.valueLock.Lock() - valueHasBeenPublished := p.lastValuePublished == p.valueToPublish - p.valueLock.Unlock() +func (rp *Republisher) WaitPub() { + rp.valueLock.Lock() + valueHasBeenPublished := rp.lastValuePublished == rp.valueToPublish + rp.valueLock.Unlock() if valueHasBeenPublished { return } wait := make(chan struct{}) - p.immediatePublish <- wait + rp.immediatePublish <- wait <-wait } -func (p *Republisher) Close() error { - err := p.publish(p.ctx) - p.cancel() +func (rp *Republisher) Close() error { + err := rp.publish(rp.ctx) + rp.cancel() return err } // Update the `valueToPublish` and signal it in the `valueHasBeenUpdated` // channel. Multiple consecutive updates may extend the time period before // the next publish occurs in order to more efficiently batch updates. -func (np *Republisher) Update(c cid.Cid) { - np.valueLock.Lock() - np.valueToPublish = c - np.valueLock.Unlock() +func (rp *Republisher) Update(c cid.Cid) { + rp.valueLock.Lock() + rp.valueToPublish = c + rp.valueLock.Unlock() select { - case np.valueHasBeenUpdated <- struct{}{}: + case rp.valueHasBeenUpdated <- struct{}{}: default: } } @@ -90,41 +90,41 @@ func (np *Republisher) Update(c cid.Cid) { // The `longer` timer ensures that we delay publishing by at most // `TimeoutLong`. The `quick` timer allows us to publish sooner if // it looks like there are no more updates coming down the pipe. -func (np *Republisher) Run() { +func (rp *Republisher) Run() { for { select { - case <-np.ctx.Done(): + case <-rp.ctx.Done(): return - case <-np.valueHasBeenUpdated: + case <-rp.valueHasBeenUpdated: // Fast timeout, a `publish` will be issued if there are // no more updates before it expires (restarted every time // the `valueHasBeenUpdated` is signaled). - quick := time.After(np.TimeoutShort) + quick := time.After(rp.TimeoutShort) // Long timeout that guarantees a `publish` after it expires // even if the value keeps being updated (and `quick` is // restarted). - longer := time.After(np.TimeoutLong) + longer := time.After(rp.TimeoutLong) wait: var valueHasBeenPublished chan struct{} select { - case <-np.ctx.Done(): + case <-rp.ctx.Done(): return - case <-np.valueHasBeenUpdated: + case <-rp.valueHasBeenUpdated: // The `valueToPublish` has been updated *again* since // the last time we checked and we still haven't published // it, restart the `quick` timer allowing for some more // time to see if the `valueToPublish` changes again. - quick = time.After(np.TimeoutShort) + quick = time.After(rp.TimeoutShort) goto wait case <-quick: case <-longer: - case valueHasBeenPublished = <-np.immediatePublish: + case valueHasBeenPublished = <-rp.immediatePublish: } - err := np.publish(np.ctx) + err := rp.publish(rp.ctx) if valueHasBeenPublished != nil { // The user is waiting in `WaitPub` with this channel, signal // that the `publish` has happened. @@ -139,17 +139,17 @@ func (np *Republisher) Run() { // Wrapper function around the user-defined `pubfunc`. It publishes // the (last) `valueToPublish` set and registers it in `lastValuePublished`. -func (np *Republisher) publish(ctx context.Context) error { - np.valueLock.Lock() - topub := np.valueToPublish - np.valueLock.Unlock() +func (rp *Republisher) publish(ctx context.Context) error { + rp.valueLock.Lock() + topub := rp.valueToPublish + rp.valueLock.Unlock() - err := np.pubfunc(ctx, topub) + err := rp.pubfunc(ctx, topub) if err != nil { return err } - np.valueLock.Lock() - np.lastValuePublished = topub - np.valueLock.Unlock() + rp.valueLock.Lock() + rp.lastValuePublished = topub + rp.valueLock.Unlock() return nil } From 1e8201dd237f06370d0f5a6859b4974687786048 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 19 Dec 2018 13:58:03 -0300 Subject: [PATCH 2808/3817] rename and document `File`'s node lock This commit was moved from ipfs/go-mfs@36363fb4b864ae44cfaa9e52cd7719a807c02f26 --- mfs/fd.go | 4 ++-- mfs/file.go | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/mfs/fd.go b/mfs/fd.go index 45a715877..53275c4b7 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -140,13 +140,13 @@ func (fi *fileDescriptor) flushUp(fullsync bool) error { return err } - fi.inode.nodelk.Lock() + fi.inode.nodeLock.Lock() fi.inode.node = nd // TODO: Create a `SetNode` method. name := fi.inode.name parent := fi.inode.parent // TODO: Can the parent be modified? Do we need to do this inside the lock? - fi.inode.nodelk.Unlock() + fi.inode.nodeLock.Unlock() // TODO: Maybe all this logic should happen in `File`. return parent.closeChild(name, nd, fullsync) diff --git a/mfs/file.go b/mfs/file.go index 2c6912551..963428aa7 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -28,8 +28,9 @@ type File struct { // of a particular sub-DAG that abstract an upper layer's entity. node ipld.Node - // TODO: Rename. - nodelk sync.Mutex + // Lock around the `node` that represents this file, necessary because + // there may be many `FileDescriptor`s operating on this `File`. + nodeLock sync.Mutex RawLeaves bool } @@ -58,9 +59,9 @@ const ( ) func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { - fi.nodelk.Lock() + fi.nodeLock.Lock() node := fi.node - fi.nodelk.Unlock() + fi.nodeLock.Unlock() // TODO: Move this `switch` logic outside (maybe even // to another package, this seems like a job of UnixFS), @@ -118,8 +119,8 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { // pretty much the same thing as here, we should at least call // that function and wrap the `ErrNotUnixfs` with an MFS text. func (fi *File) Size() (int64, error) { - fi.nodelk.Lock() - defer fi.nodelk.Unlock() + fi.nodeLock.Lock() + defer fi.nodeLock.Unlock() switch nd := fi.node.(type) { case *dag.ProtoNode: fsn, err := ft.FSNodeFromBytes(nd.Data()) @@ -135,10 +136,10 @@ func (fi *File) Size() (int64, error) { } // GetNode returns the dag node associated with this file -// TODO: Use this method and do not access the `nodelk` directly anywhere else. +// TODO: Use this method and do not access the `nodeLock` directly anywhere else. func (fi *File) GetNode() (ipld.Node, error) { - fi.nodelk.Lock() - defer fi.nodelk.Unlock() + fi.nodeLock.Lock() + defer fi.nodeLock.Unlock() return fi.node, nil } From 717ba6e6d333619cb248a614b7af9b9aada71bce Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 19 Dec 2018 14:04:35 -0300 Subject: [PATCH 2809/3817] use RW lock for the `File`'s node This allows us to, e.g., get the size, etc. in parallel. This commit was moved from ipfs/go-mfs@00b7d5c5388270eb1865b8c9e3500320597e2f34 --- mfs/file.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mfs/file.go b/mfs/file.go index 963428aa7..f75723ada 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -30,7 +30,7 @@ type File struct { // Lock around the `node` that represents this file, necessary because // there may be many `FileDescriptor`s operating on this `File`. - nodeLock sync.Mutex + nodeLock sync.RWMutex RawLeaves bool } @@ -59,9 +59,9 @@ const ( ) func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { - fi.nodeLock.Lock() + fi.nodeLock.RLock() node := fi.node - fi.nodeLock.Unlock() + fi.nodeLock.RUnlock() // TODO: Move this `switch` logic outside (maybe even // to another package, this seems like a job of UnixFS), @@ -119,8 +119,8 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { // pretty much the same thing as here, we should at least call // that function and wrap the `ErrNotUnixfs` with an MFS text. func (fi *File) Size() (int64, error) { - fi.nodeLock.Lock() - defer fi.nodeLock.Unlock() + fi.nodeLock.RLock() + defer fi.nodeLock.RUnlock() switch nd := fi.node.(type) { case *dag.ProtoNode: fsn, err := ft.FSNodeFromBytes(nd.Data()) @@ -138,8 +138,8 @@ func (fi *File) Size() (int64, error) { // GetNode returns the dag node associated with this file // TODO: Use this method and do not access the `nodeLock` directly anywhere else. func (fi *File) GetNode() (ipld.Node, error) { - fi.nodeLock.Lock() - defer fi.nodeLock.Unlock() + fi.nodeLock.RLock() + defer fi.nodeLock.RUnlock() return fi.node, nil } From 609021953cf5b46ecb02f9f3af59abae357d79a0 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 19 Dec 2018 12:30:06 -0300 Subject: [PATCH 2810/3817] rename the `mutableParent` interface to just `parent` The name `mutableParent` signals "this is a mutable parent, there are immutable versions". This commit was moved from ipfs/go-mfs@d1471dab14dddcfa65b530706181533d76014c2c --- mfs/dir.go | 4 ++-- mfs/file.go | 2 +- mfs/inode.go | 2 +- mfs/root.go | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 80ead54b9..d01aa07cf 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -47,7 +47,7 @@ type Directory struct { // // You probably don't want to call this directly. Instead, construct a new root // using NewRoot. -func NewDirectory(ctx context.Context, name string, node ipld.Node, parent mutableParent, dserv ipld.DAGService) (*Directory, error) { +func NewDirectory(ctx context.Context, name string, node ipld.Node, parent parent, dserv ipld.DAGService) (*Directory, error) { db, err := uio.NewDirectoryFromNode(dserv, node) if err != nil { return nil, err @@ -76,7 +76,7 @@ func (d *Directory) SetCidBuilder(b cid.Builder) { d.unixfsDir.SetCidBuilder(b) } -// This method implements the `mutableParent` interface. It first updates +// This method implements the `parent` interface. It first updates // the child entry in the underlying UnixFS directory and then if `fullSync` // is set it saves the new content through the internal DAG service. Then, // also if `fullSync` is set, it propagates the update to its parent (through diff --git a/mfs/file.go b/mfs/file.go index 43713925e..ac36441e4 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -36,7 +36,7 @@ type File struct { // NewFile returns a NewFile object with the given parameters. If the // Cid version is non-zero RawLeaves will be enabled. -func NewFile(name string, node ipld.Node, parent mutableParent, dserv ipld.DAGService) (*File, error) { +func NewFile(name string, node ipld.Node, parent parent, dserv ipld.DAGService) (*File, error) { fi := &File{ inode: inode{ name: name, diff --git a/mfs/inode.go b/mfs/inode.go index 8f65672aa..50bed0b38 100644 --- a/mfs/inode.go +++ b/mfs/inode.go @@ -13,7 +13,7 @@ type inode struct { name string // parent directory of this `inode` (which may be the `Root`). - parent mutableParent + parent parent // dagService used to store modifications made to the contents // of the file or directory the `inode` belongs to. diff --git a/mfs/root.go b/mfs/root.go index b3c311ef5..6e91d0285 100644 --- a/mfs/root.go +++ b/mfs/root.go @@ -43,7 +43,7 @@ type child struct { // distinguished here, one at the DAG level (when I store the modified // nodes in the DAG service) and one in the UnixFS/MFS level (when I modify // the entry/link of the directory that pointed to the modified node). -type mutableParent interface { +type parent interface { // Method called by a child to its parent to signal to update the content // pointed to in the entry by that child's name. The child sends as // arguments its own information (under the `child` structure) and a flag @@ -63,7 +63,7 @@ const ( // FSNode abstracts the `Directory` and `File` structures, it represents // any child node in the MFS (i.e., all the nodes besides the `Root`). It -// is the counterpart of the `mutableParent` interface which represents any +// is the counterpart of the `parent` interface which represents any // parent node in the MFS (`Root` and `Directory`). // (Not to be confused with the `unixfs.FSNode`.) type FSNode interface { @@ -182,8 +182,8 @@ func (kr *Root) FlushMemFree(ctx context.Context) error { return nil } -// updateChildEntry implements the mutableParent interface, and signals to the publisher that -// there are changes ready to be published. +// updateChildEntry implements the `parent` interface, and signals +// to the publisher that there are changes ready to be published. // This is the only thing that separates a `Root` from a `Directory`. // TODO: Evaluate merging both. // TODO: The `sync` argument isn't used here (we've already reached From 0bae804eba521257199997b046053f83663040df Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 19 Dec 2018 12:34:04 -0300 Subject: [PATCH 2811/3817] reword `updateChildEntry` method documentation This commit was moved from ipfs/go-mfs@25dd99b483cd0604f6bfde73e0e923553bc100f2 --- mfs/dir.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index d01aa07cf..ab2158b6c 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -77,14 +77,11 @@ func (d *Directory) SetCidBuilder(b cid.Builder) { } // This method implements the `parent` interface. It first updates -// the child entry in the underlying UnixFS directory and then if `fullSync` -// is set it saves the new content through the internal DAG service. Then, -// also if `fullSync` is set, it propagates the update to its parent (through -// this same interface) with the new node already updated with the new entry. -// So, `fullSync` entails operations at two different layers: -// 1. DAG: save the newly created directory node with the updated entry. -// 2. MFS: propagate the update upwards repeating the whole process in the -// parent. +// the child entry in the underlying UnixFS directory and then, if `fullSync` +// is set, it: +// 1. DAG: saves the newly created directory node with the updated entry. +// 2. MFS: propagates the update upwards (through this same interface) +// repeating the whole process in the parent. func (d *Directory) updateChildEntry(c child, fullSync bool) error { // There's a local flush (`closeChildUpdate`) and a propagated flush (`updateChildEntry`). From 4cb1cc1f14d6ae7c86336cffaed14c0bde9899d7 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 19 Dec 2018 14:41:01 -0300 Subject: [PATCH 2812/3817] unexport `AddUnixFSChild` This commit was moved from ipfs/go-mfs@6c4a00ac702565c1a5c7254561744dbe02ba4c2a --- mfs/dir.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index ab2158b6c..d488eb272 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -160,7 +160,7 @@ func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) { // Update child entry in the underlying UnixFS directory. func (d *Directory) updateChild(c child) error { - err := d.AddUnixFSChild(c) + err := d.addUnixFSChild(c) if err != nil { return err } @@ -345,7 +345,7 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { return nil, err } - err = d.AddUnixFSChild(child{name, ndir}) + err = d.addUnixFSChild(child{name, ndir}) if err != nil { return nil, err } @@ -392,7 +392,7 @@ func (d *Directory) AddChild(name string, nd ipld.Node) error { return err } - err = d.AddUnixFSChild(child{name, nd}) + err = d.addUnixFSChild(child{name, nd}) if err != nil { return err } @@ -401,9 +401,9 @@ func (d *Directory) AddChild(name string, nd ipld.Node) error { return nil } -// AddUnixFSChild adds a child to the inner UnixFS directory +// addUnixFSChild adds a child to the inner UnixFS directory // and transitions to a HAMT implementation if needed. -func (d *Directory) AddUnixFSChild(c child) error { +func (d *Directory) addUnixFSChild(c child) error { if uio.UseHAMTSharding { // If the directory HAMT implementation is being used and this // directory is actually a basic implementation switch it to HAMT. From 5a85a431f81b0ba3feb64c8adc814b09b7ebe4da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 26 Oct 2018 14:14:49 +0200 Subject: [PATCH 2813/3817] gx: update go-ipfs-files to 2.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@1dc26f7a3fc0c1992e3c005177cecddb3fa8e14a --- coreiface/unixfs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 002635d99..3a214085c 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -6,7 +6,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - files "gx/ipfs/QmZMWMvWMVKCbHetJ4RgndbuEF1io2UpUxwQwtNjtYPzSC/go-ipfs-files" + files "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files" ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" ) From d4352730fe649983c5c9099d4ba2fba2170451f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 26 Oct 2018 15:56:30 +0200 Subject: [PATCH 2814/3817] files2.0: fix build errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@072259200527f4b78299675bb5efbbaa715ca821 --- coreiface/unixfs.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 3a214085c..773b36dc0 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -2,9 +2,8 @@ package iface import ( "context" - "io" - options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + "github.com/ipfs/go-ipfs/core/coreapi/interface/options" files "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files" ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" @@ -18,11 +17,6 @@ type AddEvent struct { Size string `json:",omitempty"` } -type UnixfsFile interface { - files.SizeFile - io.Seeker -} - // UnixfsAPI is the basic interface to immutable files in IPFS // NOTE: This API is heavily WIP, things are guaranteed to break frequently type UnixfsAPI interface { @@ -35,7 +29,7 @@ type UnixfsAPI interface { // // Note that some implementations of this API may apply the specified context // to operations performed on the returned file - Get(context.Context, Path) (UnixfsFile, error) + Get(context.Context, Path) (files.File, error) // Ls returns the list of links in a directory Ls(context.Context, Path) ([]*ipld.Link, error) From 2904a69ad87f9a72ce9598c08536212a6d19609d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 19 Nov 2018 03:24:42 +0100 Subject: [PATCH 2815/3817] files2.0: updates for file type split MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@65f7599676af200b0b87cee47292e6a48b1c14eb --- coreiface/unixfs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 773b36dc0..589083c6b 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -23,13 +23,13 @@ type UnixfsAPI interface { // Add imports the data from the reader into merkledag file // // TODO: a long useful comment on how to use this for many different scenarios - Add(context.Context, files.File, ...options.UnixfsAddOption) (ResolvedPath, error) + Add(context.Context, files.Node, ...options.UnixfsAddOption) (ResolvedPath, error) // Get returns a read-only handle to a file tree referenced by a path // // Note that some implementations of this API may apply the specified context // to operations performed on the returned file - Get(context.Context, Path) (files.File, error) + Get(context.Context, Path) (files.Node, error) // Ls returns the list of links in a directory Ls(context.Context, Path) ([]*ipld.Link, error) From 0e2ab3797e3ea5f3d78d72f7f8b646e22772e2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 18 Dec 2018 02:09:43 +0100 Subject: [PATCH 2816/3817] files2.0: address review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@d68c62b157814c099608799f543888595fdb8fd5 --- coreiface/errors.go | 1 + 1 file changed, 1 insertion(+) diff --git a/coreiface/errors.go b/coreiface/errors.go index 4ee3026ff..234abe566 100644 --- a/coreiface/errors.go +++ b/coreiface/errors.go @@ -4,5 +4,6 @@ import "errors" var ( ErrIsDir = errors.New("this dag node is a directory") + ErrNotFile = errors.New("this dag node is not a regular file") ErrOffline = errors.New("this action must be run in online mode, try running 'ipfs daemon' first") ) From 49c1363b243d972fc350a3e294658a68cfdbf494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 26 Oct 2018 14:14:49 +0200 Subject: [PATCH 2817/3817] gx: update go-ipfs-files to 2.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@be43fa36d61a6fe2971048974884cb3859573053 --- namesys/namesys_test.go | 2 +- namesys/publisher.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index e97933afd..4e398485f 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -12,7 +12,7 @@ import ( peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" pstoremem "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore/pstoremem" path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" - "gx/ipfs/QmdYvDbHp7qAhZ7GsCj6e1cMo55ND6y2mjWVzwdvcv4f12/go-unixfs" + "gx/ipfs/Qmbvw7kpSM2p6rbQ57WGRhhqNfCiNGW6EKH4xgHLw4bsnB/go-unixfs" offroute "gx/ipfs/QmdmWkx54g7VfVyxeG8ic84uf4G6Eq1GohuyKA3XDuJ8oC/go-ipfs-routing/offline" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" diff --git a/namesys/publisher.go b/namesys/publisher.go index 78bfd160f..64bea6714 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -8,7 +8,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" - ft "gx/ipfs/QmdYvDbHp7qAhZ7GsCj6e1cMo55ND6y2mjWVzwdvcv4f12/go-unixfs" + ft "gx/ipfs/Qmbvw7kpSM2p6rbQ57WGRhhqNfCiNGW6EKH4xgHLw4bsnB/go-unixfs" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" ipns "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns" From 3d132ddab18ee64dc731918e08fd4548d1b335dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 6 Dec 2018 10:47:51 +0100 Subject: [PATCH 2818/3817] coreapi: Global options for api constructor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@84509e38781da3257c7a82d09483b4c7d9188a10 --- coreiface/options/global.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 coreiface/options/global.go diff --git a/coreiface/options/global.go b/coreiface/options/global.go new file mode 100644 index 000000000..f43965229 --- /dev/null +++ b/coreiface/options/global.go @@ -0,0 +1,32 @@ +package options + +type ApiSettings struct { + Offline bool +} + +type ApiOption func(*ApiSettings) error + +func ApiOptions(opts ...ApiOption) (*ApiSettings, error) { + options := &ApiSettings{ + Offline: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type apiOpts struct{} + +var Api dagOpts + +func (dagOpts) Offline(offline bool) ApiOption { + return func(settings *ApiSettings) error { + settings.Offline = offline + return nil + } +} From 6d52ba5bb4ccdda79fb3a55fbc1d024bee208c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 10 Dec 2018 14:21:19 +0100 Subject: [PATCH 2819/3817] coreapi.WithOptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@7b478b88d1f788045344cede17f73e2f9b21d680 --- coreiface/coreapi.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index bab4fc13b..226399967 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -5,6 +5,8 @@ package iface import ( "context" + "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" ) @@ -46,4 +48,8 @@ type CoreAPI interface { // ResolveNode resolves the path (if not resolved already) using Unixfs // resolver, gets and returns the resolved Node ResolveNode(context.Context, Path) (ipld.Node, error) + + // WithOptions creates new instance of CoreAPI based on this instance with + // a set of options applied + WithOptions(...options.ApiOption) (CoreAPI, error) } From 57bf11c1602f87c424c2b0309d89642ef6d7d0d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 10 Dec 2018 14:31:19 +0100 Subject: [PATCH 2820/3817] coreapi: drop nameopt.Local in favour of api.Offline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@42d3b2edfa9d76d00e9fb2b86f6ab35ee15a0c0d --- coreiface/options/name.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/coreiface/options/name.go b/coreiface/options/name.go index c614db3ab..e2a0fc164 100644 --- a/coreiface/options/name.go +++ b/coreiface/options/name.go @@ -20,7 +20,6 @@ type NamePublishSettings struct { } type NameResolveSettings struct { - Local bool Cache bool ResolveOpts []ropts.ResolveOpt @@ -49,7 +48,6 @@ func NamePublishOptions(opts ...NamePublishOption) (*NamePublishSettings, error) func NameResolveOptions(opts ...NameResolveOption) (*NameResolveSettings, error) { options := &NameResolveSettings{ - Local: false, Cache: true, } @@ -106,15 +104,6 @@ func (nameOpts) TTL(ttl time.Duration) NamePublishOption { } } -// Local is an option for Name.Resolve which specifies if the lookup should be -// offline. Default value is false -func (nameOpts) Local(local bool) NameResolveOption { - return func(settings *NameResolveSettings) error { - settings.Local = local - return nil - } -} - // Cache is an option for Name.Resolve which specifies if cache should be used. // Default value is true func (nameOpts) Cache(cache bool) NameResolveOption { From 3b8f8c7c8238d5fbd913ab0fb13f466ec3313e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 10 Dec 2018 15:26:27 +0100 Subject: [PATCH 2821/3817] coreapi: implement --local with Offline option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@286ea29c95a4d398c9a4b08fc5d24494e0c5d7c1 --- coreiface/options/unixfs.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index b771896bc..fd748bb4a 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -30,7 +30,6 @@ type UnixfsAddSettings struct { Pin bool OnlyHash bool - Local bool FsCache bool NoCopy bool @@ -60,7 +59,6 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, Pin: false, OnlyHash: false, - Local: false, FsCache: false, NoCopy: false, @@ -220,16 +218,6 @@ func (unixfsOpts) HashOnly(hashOnly bool) UnixfsAddOption { } } -// Local will add the data to blockstore without announcing it to the network -// -// Note that this doesn't prevent other nodes from getting this data -func (unixfsOpts) Local(local bool) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.Local = local - return nil - } -} - // Wrap tells the adder to wrap the added file structure with an additional // directory. func (unixfsOpts) Wrap(wrap bool) UnixfsAddOption { From e84f354e2490ba03d16798213311feb0ef47b59c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 11 Dec 2018 22:24:40 +0100 Subject: [PATCH 2822/3817] coreapi WithOptions: apply on top of parent options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@3956a72f07973de428f73776571af0518872e87c --- coreiface/options/global.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/coreiface/options/global.go b/coreiface/options/global.go index f43965229..93d635e41 100644 --- a/coreiface/options/global.go +++ b/coreiface/options/global.go @@ -11,6 +11,10 @@ func ApiOptions(opts ...ApiOption) (*ApiSettings, error) { Offline: false, } + return ApiOptionsTo(options, opts...) +} + +func ApiOptionsTo(options *ApiSettings, opts ...ApiOption) (*ApiSettings, error) { for _, opt := range opts { err := opt(options) if err != nil { @@ -22,9 +26,9 @@ func ApiOptions(opts ...ApiOption) (*ApiSettings, error) { type apiOpts struct{} -var Api dagOpts +var Api apiOpts -func (dagOpts) Offline(offline bool) ApiOption { +func (apiOpts) Offline(offline bool) ApiOption { return func(settings *ApiSettings) error { settings.Offline = offline return nil From 4ca66099f8d8cf3985e847cd719b6990dbe09edb Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 20 Dec 2018 18:23:10 -0800 Subject: [PATCH 2823/3817] fix a potential overflow bug If we have a deep enough directory, we could walk off the end of the bit array. This commit was moved from ipfs/go-unixfs@68e91bf352e567fec43ca73dadcaeb2a37fdd322 --- unixfs/hamt/hamt.go | 10 ++++++++-- unixfs/hamt/util.go | 14 +++++++++++--- unixfs/hamt/util_test.go | 35 +++++++++++++++++++++++++++-------- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 3714c30a2..8108da3d3 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -375,7 +375,10 @@ func (ds *Shard) rmChild(i int) error { } func (ds *Shard) getValue(ctx context.Context, hv *hashBits, key string, cb func(*Shard) error) error { - idx := hv.Next(ds.tableSizeLg2) + idx, err := hv.Next(ds.tableSizeLg2) + if err != nil { + return fmt.Errorf("sharded directory too deep") + } if ds.bitfield.Bit(int(idx)) { cindex := ds.indexForBitPos(idx) @@ -516,7 +519,10 @@ func (ds *Shard) walkTrie(ctx context.Context, cb func(*Shard) error) error { } func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val *ipld.Link) error { - idx := hv.Next(ds.tableSizeLg2) + idx, err := hv.Next(ds.tableSizeLg2) + if err != nil { + return fmt.Errorf("sharded directory too deep") + } if !ds.bitfield.Bit(idx) { return ds.insertChild(idx, key, val) } diff --git a/unixfs/hamt/util.go b/unixfs/hamt/util.go index 5f684a21a..927d44964 100644 --- a/unixfs/hamt/util.go +++ b/unixfs/hamt/util.go @@ -15,8 +15,16 @@ func mkmask(n int) byte { return (1 << uint(n)) - 1 } -// Next returns the next 'i' bits of the hashBits value as an integer -func (hb *hashBits) Next(i int) int { +// Next returns the next 'i' bits of the hashBits value as an integer, or an +// error if there aren't enough bits. +func (hb *hashBits) Next(i int) (int, error) { + if hb.consumed+i > len(hb.b)*8 { + return 0, fmt.Errorf("not enough bits remaining") + } + return hb.next(i), nil +} + +func (hb *hashBits) next(i int) int { curbi := hb.consumed / 8 leftb := 8 - (hb.consumed % 8) @@ -35,7 +43,7 @@ func (hb *hashBits) Next(i int) int { out := int(mkmask(leftb) & curb) out <<= uint(i - leftb) hb.consumed += leftb - out += hb.Next(i - leftb) + out += hb.next(i - leftb) return out } } diff --git a/unixfs/hamt/util_test.go b/unixfs/hamt/util_test.go index b1cbc5217..835c17428 100644 --- a/unixfs/hamt/util_test.go +++ b/unixfs/hamt/util_test.go @@ -9,37 +9,56 @@ func TestHashBitsEvenSizes(t *testing.T) { hb := hashBits{b: buf} for _, v := range buf { - if hb.Next(8) != int(v) { - t.Fatal("got wrong numbers back") + if a, _ := hb.Next(8); a != int(v) { + t.Fatalf("got wrong numbers back: expected %d, got %d", v, a) } } } +func TestHashBitsOverflow(t *testing.T) { + buf := []byte{255} + hb := hashBits{b: buf} + + for i := 0; i < 8; i++ { + bit, err := hb.Next(1) + if err != nil { + t.Fatalf("got %d bits back, expected 8: %s", i, err) + } + if bit != 1 { + t.Fatal("expected all one bits") + } + } + _, err := hb.Next(1) + if err == nil { + t.Error("overflowed the bit vector") + } +} + func TestHashBitsUneven(t *testing.T) { buf := []byte{255, 127, 79, 45, 116, 99, 35, 17} hb := hashBits{b: buf} - v := hb.Next(4) + v, _ := hb.Next(4) if v != 15 { t.Fatal("should have gotten 15: ", v) } - v = hb.Next(4) + v, _ = hb.Next(4) if v != 15 { t.Fatal("should have gotten 15: ", v) } - if v := hb.Next(3); v != 3 { + if v, _ := hb.Next(3); v != 3 { t.Fatalf("expected 3, but got %b", v) } - if v := hb.Next(3); v != 7 { + if v, _ := hb.Next(3); v != 7 { t.Fatalf("expected 7, but got %b", v) } - if v := hb.Next(3); v != 6 { + if v, _ := hb.Next(3); v != 6 { t.Fatalf("expected 6, but got %b", v) } - if v := hb.Next(15); v != 20269 { + if v, _ := hb.Next(15); v != 20269 { t.Fatalf("expected 20269, but got %b (%d)", v, v) } } From 2fefa38f508742783bf1f14accf22288a37f2741 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 20 Dec 2018 18:26:59 -0800 Subject: [PATCH 2824/3817] keep the full murmur128 hash 1. It's less confusing. Murmur64 is something our library supports by truncating a Murmur128 hash. 2. We'll only use the part we need anyways. directory trees. This commit was moved from ipfs/go-unixfs@6e4a0b54ebf90cd60ef35971dc2eb568db192536 --- unixfs/hamt/hamt.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 8108da3d3..e59f88173 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -203,9 +203,9 @@ func (ds *Shard) makeShardValue(lnk *ipld.Link) (*Shard, error) { } func hash(val []byte) []byte { - h := murmur3.New64() + h := murmur3.New128() h.Write(val) - return h.Sum(nil) + return h.Sum(make([]byte, 0, 128/8)) } // Set sets 'name' = nd in the HAMT From e5c6bfca93f033def1cf9269e44af166cb0ed836 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 20 Dec 2018 19:23:56 -0800 Subject: [PATCH 2825/3817] return the sharded directory error from the hash bits helper I didn't do this before because this datastructure *technically* isn't specific to sharded directories but, really, that was just even more confusing. This commit was moved from ipfs/go-unixfs@f3b122d2a5e64be72d9b9db8d0c63fe82b022c18 --- unixfs/hamt/hamt.go | 4 ++-- unixfs/hamt/util.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index e59f88173..fdaf5be9b 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -377,7 +377,7 @@ func (ds *Shard) rmChild(i int) error { func (ds *Shard) getValue(ctx context.Context, hv *hashBits, key string, cb func(*Shard) error) error { idx, err := hv.Next(ds.tableSizeLg2) if err != nil { - return fmt.Errorf("sharded directory too deep") + return err } if ds.bitfield.Bit(int(idx)) { cindex := ds.indexForBitPos(idx) @@ -521,7 +521,7 @@ func (ds *Shard) walkTrie(ctx context.Context, cb func(*Shard) error) error { func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val *ipld.Link) error { idx, err := hv.Next(ds.tableSizeLg2) if err != nil { - return fmt.Errorf("sharded directory too deep") + return err } if !ds.bitfield.Bit(idx) { return ds.insertChild(idx, key, val) diff --git a/unixfs/hamt/util.go b/unixfs/hamt/util.go index 927d44964..c2b33bc22 100644 --- a/unixfs/hamt/util.go +++ b/unixfs/hamt/util.go @@ -19,7 +19,7 @@ func mkmask(n int) byte { // error if there aren't enough bits. func (hb *hashBits) Next(i int) (int, error) { if hb.consumed+i > len(hb.b)*8 { - return 0, fmt.Errorf("not enough bits remaining") + return 0, fmt.Errorf("sharded directory too deep") } return hb.next(i), nil } From 3a93f3a16133ff7c0f177d80b8b9149313ce3d82 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Fri, 21 Dec 2018 15:26:05 -0300 Subject: [PATCH 2826/3817] remove the `fullSync` option from `updateChildEntry` Make `updateChildEntry` always propagate the update all the way up to the root, the equivalent of calling it always with `fullSync` set. The case of calling it without setting `fullSync` (a kind of "half-update") where only the parent's directory UnixFS node was updated (but nothing else, leaving the root outdated) seemed of little used. This helps to simplify the logic around the update mechanism in MFS. This commit was moved from ipfs/go-mfs@1bbc52db970c95c1c51cf4f096b14d0fd8bf12a8 --- mfs/dir.go | 30 +++++++++--------------------- mfs/fd.go | 11 +++++++---- mfs/root.go | 15 +++++++-------- 3 files changed, 23 insertions(+), 33 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index d488eb272..20272d898 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -77,16 +77,15 @@ func (d *Directory) SetCidBuilder(b cid.Builder) { } // This method implements the `parent` interface. It first updates -// the child entry in the underlying UnixFS directory and then, if `fullSync` -// is set, it: +// the child entry in the underlying UnixFS directory and then it: // 1. DAG: saves the newly created directory node with the updated entry. // 2. MFS: propagates the update upwards (through this same interface) // repeating the whole process in the parent. -func (d *Directory) updateChildEntry(c child, fullSync bool) error { +func (d *Directory) updateChildEntry(c child) error { // There's a local flush (`closeChildUpdate`) and a propagated flush (`updateChildEntry`). - newDirNode, err := d.closeChildUpdate(c, fullSync) + newDirNode, err := d.closeChildUpdate(c) if err != nil { return err } @@ -98,23 +97,15 @@ func (d *Directory) updateChildEntry(c child, fullSync bool) error { // be merged with this one (the use of the `lock` is stopping this at the // moment, re-evaluate when its purpose has been better understood). - if fullSync { - return d.parent.updateChildEntry(child{d.name, newDirNode}, true) - // Setting `fullSync` to true here means, if the original child that - // initiated the update process wanted to propagate it upwards then - // continue to do so all the way up to the root, that is, the only - // time `fullSync` can be false is in the first call (which will be - // the *only* call), we either update the first parent entry or *all* - // the parent's. - } - - return nil + // Continue to propagate the update process upwards + // (all the way up to the root). + return d.parent.updateChildEntry(child{d.name, newDirNode}) } // This method implements the part of `updateChildEntry` that needs // to be locked around: in charge of updating the UnixFS layer and // generating the new node reflecting the update. -func (d *Directory) closeChildUpdate(c child, fullSync bool) (*dag.ProtoNode, error) { +func (d *Directory) closeChildUpdate(c child) (*dag.ProtoNode, error) { d.lock.Lock() defer d.lock.Unlock() @@ -125,10 +116,7 @@ func (d *Directory) closeChildUpdate(c child, fullSync bool) (*dag.ProtoNode, er // TODO: Clearly define how are we propagating changes to lower layers // like UnixFS. - if fullSync { - return d.flushCurrentNode() - } - return nil, nil + return d.flushCurrentNode() } // Recreate the underlying UnixFS directory node and save it in the DAG layer. @@ -374,7 +362,7 @@ func (d *Directory) Flush() error { return err } - return d.parent.updateChildEntry(child{d.name, nd}, true) + return d.parent.updateChildEntry(child{d.name, nd}) } // AddChild adds the node 'nd' under this directory giving it the name 'name' diff --git a/mfs/fd.go b/mfs/fd.go index e260a60ab..ea04bc968 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -123,9 +123,8 @@ func (fi *fileDescriptor) Flush() error { // flushUp syncs the file and adds it to the dagservice // it *must* be called with the File's lock taken -// TODO: What is `fullsync`? Propagate the changes upward -// to the root flushing every node in the path (the "up" -// part of `flushUp`). +// If `fullSync` is set the changes are propagated upwards +// (the `Up` part of `flushUp`). func (fi *fileDescriptor) flushUp(fullSync bool) error { nd, err := fi.mod.GetNode() if err != nil { @@ -151,7 +150,11 @@ func (fi *fileDescriptor) flushUp(fullSync bool) error { fi.inode.nodeLock.Unlock() // TODO: Maybe all this logic should happen in `File`. - return parent.updateChildEntry(child{name, nd}, fullSync) + if fullSync { + return parent.updateChildEntry(child{name, nd}) + } + + return nil } // Seek implements io.Seeker diff --git a/mfs/root.go b/mfs/root.go index 6e91d0285..9810961ff 100644 --- a/mfs/root.go +++ b/mfs/root.go @@ -45,13 +45,12 @@ type child struct { // the entry/link of the directory that pointed to the modified node). type parent interface { // Method called by a child to its parent to signal to update the content - // pointed to in the entry by that child's name. The child sends as - // arguments its own information (under the `child` structure) and a flag - // (`fullsync`) indicating whether or not to propagate the update upwards: - // modifying a directory entry entails modifying its contents which means - // that its parent (the parent's parent) will also need to be updated (and - // so on). - updateChildEntry(c child, fullSync bool) error + // pointed to in the entry by that child's name. The child sends its own + // information in the `child` structure. As modifying a directory entry + // entails modifying its contents the parent will also call *its* parent's + // `updateChildEntry` to update the entry pointing to the new directory, + // this mechanism is in turn repeated until reaching the `Root`. + updateChildEntry(c child) error } type NodeType int @@ -189,7 +188,7 @@ func (kr *Root) FlushMemFree(ctx context.Context) error { // TODO: The `sync` argument isn't used here (we've already reached // the top), document it and maybe make it an anonymous variable (if // that's possible). -func (kr *Root) updateChildEntry(c child, fullSync bool) error { +func (kr *Root) updateChildEntry(c child) error { err := kr.GetDirectory().dagService.Add(context.TODO(), c.Node) if err != nil { return err From 1bfbf8f984f20ad46113f56d42353ec262c61e17 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Fri, 21 Dec 2018 16:11:18 -0300 Subject: [PATCH 2827/3817] merge `closeChildUpdate` with `flushCurrentNode` Without the `sync` logic (now removed) the code is simplified for these tightly coupled methods (the first one was the only caller of the second) to be merged in a single `localUpdate` method. This commit was moved from ipfs/go-mfs@8d2621030188477ab59ecc2aa47b967c12b1547d --- mfs/dir.go | 48 ++++++++++++++---------------------------------- 1 file changed, 14 insertions(+), 34 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 20272d898..61f85d064 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -76,27 +76,17 @@ func (d *Directory) SetCidBuilder(b cid.Builder) { d.unixfsDir.SetCidBuilder(b) } -// This method implements the `parent` interface. It first updates -// the child entry in the underlying UnixFS directory and then it: -// 1. DAG: saves the newly created directory node with the updated entry. -// 2. MFS: propagates the update upwards (through this same interface) -// repeating the whole process in the parent. +// This method implements the `parent` interface. It first does the local +// update of the child entry in the underlying UnixFS directory and saves +// the newly created directory node with the updated entry in the DAG +// service. Then it propagates the update upwards (through this same +// interface) repeating the whole process in the parent. func (d *Directory) updateChildEntry(c child) error { - - // There's a local flush (`closeChildUpdate`) and a propagated flush (`updateChildEntry`). - - newDirNode, err := d.closeChildUpdate(c) + newDirNode, err := d.localUpdate(c) if err != nil { return err } - // TODO: The `sync` seems to be tightly coupling this two pieces of code, - // we use the node returned by `closeChildUpdate` (which entails a copy) - // only if `sync` is set, and we are discarding it otherwise. At the very - // least the `if sync {` clause at the end of `closeChildUpdate` should - // be merged with this one (the use of the `lock` is stopping this at the - // moment, re-evaluate when its purpose has been better understood). - // Continue to propagate the update process upwards // (all the way up to the root). return d.parent.updateChildEntry(child{d.name, newDirNode}) @@ -104,8 +94,9 @@ func (d *Directory) updateChildEntry(c child) error { // This method implements the part of `updateChildEntry` that needs // to be locked around: in charge of updating the UnixFS layer and -// generating the new node reflecting the update. -func (d *Directory) closeChildUpdate(c child) (*dag.ProtoNode, error) { +// generating the new node reflecting the update. It also stores the +// new node in the DAG layer. +func (d *Directory) localUpdate(c child) (*dag.ProtoNode, error) { d.lock.Lock() defer d.lock.Unlock() @@ -116,31 +107,20 @@ func (d *Directory) closeChildUpdate(c child) (*dag.ProtoNode, error) { // TODO: Clearly define how are we propagating changes to lower layers // like UnixFS. - return d.flushCurrentNode() -} - -// Recreate the underlying UnixFS directory node and save it in the DAG layer. -func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) { nd, err := d.unixfsDir.GetNode() if err != nil { return nil, err } - err = d.dagService.Add(d.ctx, nd) - if err != nil { - return nil, err - } - // TODO: This method is called in `closeChildUpdate` while the lock is - // taken, we need the lock while operating on `unixfsDir` to create the - // new node but do we also need to keep the lock while adding it to the - // DAG service? Evaluate refactoring these two methods together and better - // redistributing the node. - pbnd, ok := nd.(*dag.ProtoNode) if !ok { return nil, dag.ErrNotProtobuf } - // TODO: Why do we check the node *after* adding it to the DAG service? + + err = d.dagService.Add(d.ctx, nd) + if err != nil { + return nil, err + } return pbnd.Copy().(*dag.ProtoNode), nil // TODO: Why do we need a copy? From b5e06d34876da3759fa39fb18f557085e27f76d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 18 Dec 2018 14:23:55 +0100 Subject: [PATCH 2828/3817] coreapi/unixfs: Use path instead of raw hash in AddEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@a1cf89b78a61aef35a4893f916e14e39cd5c9157 --- coreiface/path.go | 1 + coreiface/unixfs.go | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/coreiface/path.go b/coreiface/path.go index 57ef4c21b..01dda97d5 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -46,6 +46,7 @@ type ResolvedPath interface { // cidRoot := {"A": {"/": cidA }} // // And resolve paths: + // // * "/ipfs/${cidRoot}" // * Calling Cid() will return `cidRoot` // * Calling Root() will return `cidRoot` diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 589083c6b..b42b454cc 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -9,12 +9,11 @@ import ( ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" ) -// TODO: ideas on making this more coreapi-ish without breaking the http API? type AddEvent struct { Name string - Hash string `json:",omitempty"` - Bytes int64 `json:",omitempty"` - Size string `json:",omitempty"` + Path ResolvedPath `json:",omitempty"` + Bytes int64 `json:",omitempty"` + Size string `json:",omitempty"` } // UnixfsAPI is the basic interface to immutable files in IPFS From 69e80ca212839db8ad52e9959504afaedc8d8a74 Mon Sep 17 00:00:00 2001 From: nmalhotra Date: Tue, 25 Dec 2018 11:23:34 -0500 Subject: [PATCH 2829/3817] fix/32/pr-ports : Pulled changes from PR 4517 Pulled changes from https://github.com/ipfs/go-ipfs/pull/4517, on top of, https://github.com/ipfs/go-mfs/pull/45. Change added to unblock the `waitPub()` call. With the elimination of stateSync cause a `updateChildEntry` to happen for `stateFlushed` as well, causing it to propogate upwards to the parent(s) [fullSync] and force a publish to happen, hence unblocking `waitPub`. This commit was moved from ipfs/go-mfs@2642dbfee4da72b535fb3ab1485e391779555749 --- mfs/fd.go | 168 +++++++++++++++++++++++++++--------------------- mfs/file.go | 41 ++++++------ mfs/mfs_test.go | 29 +++++---- mfs/options.go | 7 ++ mfs/repub.go | 7 -- mfs/root.go | 1 + 6 files changed, 138 insertions(+), 115 deletions(-) create mode 100644 mfs/options.go diff --git a/mfs/fd.go b/mfs/fd.go index ea04bc968..35b945c24 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -7,6 +7,16 @@ import ( mod "github.com/ipfs/go-unixfs/mod" context "context" + + ipld "github.com/ipfs/go-ipld-format" +) + +type state uint8 + +const ( + stateFlushed state = iota + stateDirty + stateClosed ) // One `File` can have many `FileDescriptor`s associated to it @@ -31,14 +41,31 @@ type FileDescriptor interface { } type fileDescriptor struct { - inode *File - mod *mod.DagModifier - perms int - sync bool - hasChanges bool - - // TODO: Where is this variable set? - closed bool + inode *File + mod *mod.DagModifier + flags Flags + + state state +} + +func (fi *fileDescriptor) checkWrite() error { + if fi.state == stateClosed { + return ErrClosed + } + if !fi.flags.Write { + return fmt.Errorf("file is read-only") + } + return nil +} + +func (fi *fileDescriptor) checkRead() error { + if fi.state == stateClosed { + return ErrClosed + } + if !fi.flags.Read { + return fmt.Errorf("file is write-only") + } + return nil } // Size returns the size of the file referred to by this descriptor @@ -48,34 +75,34 @@ func (fi *fileDescriptor) Size() (int64, error) { // Truncate truncates the file to size func (fi *fileDescriptor) Truncate(size int64) error { - if fi.perms == OpenReadOnly { - return fmt.Errorf("cannot call truncate on readonly file descriptor") + if err := fi.checkWrite(); err != nil { + return fmt.Errorf("truncate failed: %s", err) } - fi.hasChanges = true + fi.state = stateDirty return fi.mod.Truncate(size) } // Write writes the given data to the file at its current offset func (fi *fileDescriptor) Write(b []byte) (int, error) { - if fi.perms == OpenReadOnly { - return 0, fmt.Errorf("cannot write on not writeable descriptor") + if err := fi.checkWrite(); err != nil { + return 0, fmt.Errorf("write failed: %s", err) } - fi.hasChanges = true + fi.state = stateDirty return fi.mod.Write(b) } // Read reads into the given buffer from the current offset func (fi *fileDescriptor) Read(b []byte) (int, error) { - if fi.perms == OpenWriteOnly { - return 0, fmt.Errorf("cannot read on write-only descriptor") + if err := fi.checkRead(); err != nil { + return 0, fmt.Errorf("read failed: %s", err) } return fi.mod.Read(b) } // Read reads into the given buffer from the current offset func (fi *fileDescriptor) CtxReadFull(ctx context.Context, b []byte) (int, error) { - if fi.perms == OpenWriteOnly { - return 0, fmt.Errorf("cannot read on write-only descriptor") + if err := fi.checkRead(); err != nil { + return 0, fmt.Errorf("read failed: %s", err) } return fi.mod.CtxReadFull(ctx, b) } @@ -83,34 +110,17 @@ func (fi *fileDescriptor) CtxReadFull(ctx context.Context, b []byte) (int, error // Close flushes, then propogates the modified dag node up the directory structure // and signals a republish to occur func (fi *fileDescriptor) Close() error { - defer func() { - switch fi.perms { - case OpenReadOnly: - fi.inode.desclock.RUnlock() - case OpenWriteOnly, OpenReadWrite: - fi.inode.desclock.Unlock() - } - // TODO: `closed` should be set here. - }() - - if fi.closed { - panic("attempted to close file descriptor twice!") + if fi.state == stateClosed { + return ErrClosed } - - if fi.hasChanges { - err := fi.mod.Sync() - if err != nil { - return err - } - - fi.hasChanges = false - - // explicitly stay locked for flushUp call, - // it will manage the lock for us - return fi.flushUp(fi.sync) + if fi.flags.Write { + defer fi.inode.desclock.Unlock() + } else if fi.flags.Read { + defer fi.inode.desclock.RUnlock() } - - return nil + err := fi.flushUp(fi.flags.Sync) + fi.state = stateClosed + return err } // Flush generates a new version of the node of the underlying @@ -126,47 +136,57 @@ func (fi *fileDescriptor) Flush() error { // If `fullSync` is set the changes are propagated upwards // (the `Up` part of `flushUp`). func (fi *fileDescriptor) flushUp(fullSync bool) error { - nd, err := fi.mod.GetNode() - if err != nil { - return err - } + var nd ipld.Node + switch fi.state { + case stateDirty: + // calls mod.Sync internally. + var err error + nd, err = fi.mod.GetNode() + if err != nil { + return err + } + err = fi.inode.dagService.Add(context.TODO(), nd) + if err != nil { + return err + } + fi.inode.nodeLock.Lock() + fi.inode.node = nd + fi.inode.nodeLock.Unlock() + fallthrough + case stateFlushed: + if !fullSync { + return nil + } - err = fi.inode.dagService.Add(context.TODO(), nd) - if err != nil { - return err - } - // TODO: Very similar logic to the update process in - // `Directory`, the logic should be unified, both structures - // (`File` and `Directory`) are backed by a IPLD node with - // a UnixFS format that is the actual target of the update - // (regenerating it and adding it to the DAG service). - - fi.inode.nodeLock.Lock() - fi.inode.node = nd - // TODO: Create a `SetNode` method. - name := fi.inode.name - parent := fi.inode.parent - // TODO: Can the parent be modified? Do we need to do this inside the lock? - fi.inode.nodeLock.Unlock() - // TODO: Maybe all this logic should happen in `File`. - - if fullSync { - return parent.updateChildEntry(child{name, nd}) - } + fi.inode.nodeLock.Lock() + nd = fi.inode.node + parent := fi.inode.parent + name := fi.inode.name + fi.inode.nodeLock.Unlock() - return nil + if err := parent.updateChildEntry(child{name, nd}); err != nil { + return err + } + fi.state = stateFlushed + return nil + default: + panic("invalid state") + } } // Seek implements io.Seeker func (fi *fileDescriptor) Seek(offset int64, whence int) (int64, error) { + if fi.state == stateClosed { + return 0, fmt.Errorf("seek failed: %s", ErrClosed) + } return fi.mod.Seek(offset, whence) } // Write At writes the given bytes at the offset 'at' func (fi *fileDescriptor) WriteAt(b []byte, at int64) (int, error) { - if fi.perms == OpenReadOnly { - return 0, fmt.Errorf("cannot write on not writeable descriptor") + if err := fi.checkWrite(); err != nil { + return 0, fmt.Errorf("write-at failed: %s", err) } - fi.hasChanges = true + fi.state = stateDirty return fi.mod.WriteAt(b, at) } diff --git a/mfs/file.go b/mfs/file.go index 7a20fdf9a..fd2eb28b5 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -26,7 +26,7 @@ type File struct { // entire DAG of nodes that comprise the file. // TODO: Rename, there should be an explicit term for these root nodes // of a particular sub-DAG that abstract an upper layer's entity. - node ipld.Node + node ipld.Node // Lock around the `node` that represents this file, necessary because // there may be many `FileDescriptor`s operating on this `File`. @@ -52,13 +52,25 @@ func NewFile(name string, node ipld.Node, parent parent, dserv ipld.DAGService) return fi, nil } -const ( - OpenReadOnly = iota - OpenWriteOnly - OpenReadWrite -) +func (fi *File) Open(flags Flags) (_ FileDescriptor, _retErr error) { + if flags.Write { + fi.desclock.Lock() + defer func() { + if _retErr != nil { + fi.desclock.Unlock() + } + }() + } else if flags.Read { + fi.desclock.RLock() + defer func() { + if _retErr != nil { + fi.desclock.Unlock() + } + }() + } else { + return nil, fmt.Errorf("file opened for neither reading nor writing") + } -func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { fi.nodeLock.RLock() node := fi.node fi.nodeLock.RUnlock() @@ -86,16 +98,6 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { // Ok as well. } - switch flags { - case OpenReadOnly: - fi.desclock.RLock() - case OpenWriteOnly, OpenReadWrite: - fi.desclock.Lock() - default: - // TODO: support other modes - return nil, fmt.Errorf("mode not supported") - } - dmod, err := mod.NewDagModifier(context.TODO(), node, fi.dagService, chunker.DefaultSplitter) // TODO: Remove the use of the `chunker` package here, add a new `NewDagModifier` in // `go-unixfs` with the `DefaultSplitter` already included. @@ -106,8 +108,7 @@ func (fi *File) Open(flags int, sync bool) (FileDescriptor, error) { return &fileDescriptor{ inode: fi, - perms: flags, - sync: sync, + flags: flags, mod: dmod, }, nil } @@ -153,7 +154,7 @@ func (fi *File) GetNode() (ipld.Node, error) { // a file without flushing?) func (fi *File) Flush() error { // open the file in fullsync mode - fd, err := fi.Open(OpenWriteOnly, true) + fd, err := fi.Open(Flags{Write: true, Sync: true}) if err != nil { return err } diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 20753466a..8112d8a3f 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -14,9 +14,10 @@ import ( "testing" "time" + path "github.com/ipfs/go-path" + bserv "github.com/ipfs/go-blockservice" dag "github.com/ipfs/go-merkledag" - "github.com/ipfs/go-path" ft "github.com/ipfs/go-unixfs" importer "github.com/ipfs/go-unixfs/importer" uio "github.com/ipfs/go-unixfs/io" @@ -161,7 +162,7 @@ func assertFileAtPath(ds ipld.DAGService, root *Directory, expn ipld.Node, pth s return fmt.Errorf("%s was not a file", pth) } - rfd, err := file.Open(OpenReadOnly, false) + rfd, err := file.Open(Flags{Read: true}) if err != nil { return err } @@ -394,7 +395,7 @@ func TestMfsFile(t *testing.T) { t.Fatal("some is seriously wrong here") } - wfd, err := fi.Open(OpenReadWrite, true) + wfd, err := fi.Open(Flags{Read: true, Write: true, Sync: true}) if err != nil { t.Fatal(err) } @@ -554,7 +555,7 @@ func actorMakeFile(d *Directory) error { return err } - wfd, err := f.Open(OpenWriteOnly, true) + wfd, err := f.Open(Flags{Write: true, Sync: true}) if err != nil { return err } @@ -634,7 +635,7 @@ func actorWriteFile(d *Directory) error { return err } - wfd, err := fi.Open(OpenWriteOnly, true) + wfd, err := fi.Open(Flags{Write: true, Sync: true}) if err != nil { return err } @@ -666,7 +667,7 @@ func actorReadFile(d *Directory) error { return err } - rfd, err := fi.Open(OpenReadOnly, false) + rfd, err := fi.Open(Flags{Read: true}) if err != nil { return err } @@ -868,7 +869,7 @@ func readFile(rt *Root, path string, offset int64, buf []byte) error { return fmt.Errorf("%s was not a file", path) } - fd, err := fi.Open(OpenReadOnly, false) + fd, err := fi.Open(Flags{Read: true}) if err != nil { return err } @@ -946,7 +947,7 @@ func writeFile(rt *Root, path string, data []byte) error { return fmt.Errorf("expected to receive a file, but didnt get one") } - fd, err := fi.Open(OpenWriteOnly, true) + fd, err := fi.Open(Flags{Write: true, Sync: true}) if err != nil { return err } @@ -1014,7 +1015,7 @@ func TestFileDescriptors(t *testing.T) { } // test read only - rfd1, err := fi.Open(OpenReadOnly, false) + rfd1, err := fi.Open(Flags{Read: true}) if err != nil { t.Fatal(err) } @@ -1038,7 +1039,7 @@ func TestFileDescriptors(t *testing.T) { go func() { defer close(done) // can open second readonly file descriptor - rfd2, err := fi.Open(OpenReadOnly, false) + rfd2, err := fi.Open(Flags{Read: true}) if err != nil { t.Error(err) return @@ -1061,7 +1062,7 @@ func TestFileDescriptors(t *testing.T) { done = make(chan struct{}) go func() { defer close(done) - wfd1, err := fi.Open(OpenWriteOnly, true) + wfd1, err := fi.Open(Flags{Write: true, Sync: true}) if err != nil { t.Error(err) } @@ -1090,7 +1091,7 @@ func TestFileDescriptors(t *testing.T) { case <-done: } - wfd, err := fi.Open(OpenWriteOnly, true) + wfd, err := fi.Open(Flags{Write: true, Sync: true}) if err != nil { t.Fatal(err) } @@ -1119,7 +1120,7 @@ func TestTruncateAtSize(t *testing.T) { t.Fatal(err) } - fd, err := fi.Open(OpenReadWrite, true) + fd, err := fi.Open(Flags{Read: true, Write: true, Sync: true}) if err != nil { t.Fatal(err) } @@ -1144,7 +1145,7 @@ func TestTruncateAndWrite(t *testing.T) { t.Fatal(err) } - fd, err := fi.Open(OpenReadWrite, true) + fd, err := fi.Open(Flags{Read: true, Write: true, Sync: true}) defer fd.Close() if err != nil { t.Fatal(err) diff --git a/mfs/options.go b/mfs/options.go new file mode 100644 index 000000000..1edb99e11 --- /dev/null +++ b/mfs/options.go @@ -0,0 +1,7 @@ +package mfs + +type Flags struct { + Read bool + Write bool + Sync bool +} diff --git a/mfs/repub.go b/mfs/repub.go index 1efda7b5c..12738fa48 100644 --- a/mfs/repub.go +++ b/mfs/repub.go @@ -46,13 +46,6 @@ func NewRepublisher(ctx context.Context, pf PubFunc, tshort, tlong time.Duration // WaitPub waits for the current value to be published (or returns early // if it already has). func (rp *Republisher) WaitPub() { - rp.valueLock.Lock() - valueHasBeenPublished := rp.lastValuePublished == rp.valueToPublish - rp.valueLock.Unlock() - if valueHasBeenPublished { - return - } - wait := make(chan struct{}) rp.immediatePublish <- wait <-wait diff --git a/mfs/root.go b/mfs/root.go index 9810961ff..cbce68df9 100644 --- a/mfs/root.go +++ b/mfs/root.go @@ -18,6 +18,7 @@ import ( // TODO: Remove if not used. var ErrNotExist = errors.New("no such rootfs") +var ErrClosed = errors.New("file closed") var log = logging.Logger("mfs") From 65e3df92ff61b14e2b32bfffff8b51e2d2783b23 Mon Sep 17 00:00:00 2001 From: nmalhotra Date: Tue, 25 Dec 2018 13:47:00 -0500 Subject: [PATCH 2830/3817] Add `stateCreated` as default fd state Added a new state for a freshly opened `fileDescriptor`. In `flushUp` only bubble up update if state is either `stateDirty` or `stateCreated`. `stateFlushed` should prevent bubble up. This commit was moved from ipfs/go-mfs@1cf41ee0c147a022a930d6fc0ae1011e44b78fb1 --- mfs/fd.go | 7 +++++-- mfs/file.go | 1 + mfs/root.go | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mfs/fd.go b/mfs/fd.go index 35b945c24..7e2ccc655 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -14,7 +14,8 @@ import ( type state uint8 const ( - stateFlushed state = iota + stateCreated state = iota + stateFlushed stateDirty stateClosed ) @@ -153,7 +154,7 @@ func (fi *fileDescriptor) flushUp(fullSync bool) error { fi.inode.node = nd fi.inode.nodeLock.Unlock() fallthrough - case stateFlushed: + case stateCreated: if !fullSync { return nil } @@ -169,6 +170,8 @@ func (fi *fileDescriptor) flushUp(fullSync bool) error { } fi.state = stateFlushed return nil + case stateFlushed: + return nil default: panic("invalid state") } diff --git a/mfs/file.go b/mfs/file.go index fd2eb28b5..280bf93ab 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -110,6 +110,7 @@ func (fi *File) Open(flags Flags) (_ FileDescriptor, _retErr error) { inode: fi, flags: flags, mod: dmod, + state: stateCreated, }, nil } diff --git a/mfs/root.go b/mfs/root.go index cbce68df9..ef1d9bcbe 100644 --- a/mfs/root.go +++ b/mfs/root.go @@ -68,6 +68,7 @@ const ( // (Not to be confused with the `unixfs.FSNode`.) type FSNode interface { GetNode() (ipld.Node, error) + Flush() error Type() NodeType } From 056171009263d2055f75626ef1a973ad8d2833e1 Mon Sep 17 00:00:00 2001 From: nmalhotra Date: Tue, 25 Dec 2018 21:02:46 -0500 Subject: [PATCH 2831/3817] Add 1 to rand func in `actorMakeFile` test helper Add 1 to rand.Intn() to prevent 0 size reader for being created. This commit was moved from ipfs/go-mfs@0baeab22d465bf591302a3ee7069c6fdeb134656 --- mfs/mfs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 8112d8a3f..0274e17a6 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -561,7 +561,7 @@ func actorMakeFile(d *Directory) error { } rread := rand.New(rand.NewSource(time.Now().UnixNano())) - r := io.LimitReader(rread, int64(77*rand.Intn(123))) + r := io.LimitReader(rread, int64(77*rand.Intn(123)+1)) _, err = io.Copy(wfd, r) if err != nil { return err From 2ece99e82932c11c97d3b451b2221f6f4d20f061 Mon Sep 17 00:00:00 2001 From: nmalhotra Date: Tue, 25 Dec 2018 23:28:15 -0500 Subject: [PATCH 2832/3817] Update unflushed fd's inode during `flushUp` During `flushUp()` of an un-flushed fd, be it with freshly created or modified content, always update the file descriptors inode, with the contents of the node. This commit was moved from ipfs/go-mfs@1c807139bc9a7178d861a164a00f57767a783086 --- mfs/fd.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mfs/fd.go b/mfs/fd.go index 7e2ccc655..756e999f7 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -139,8 +139,7 @@ func (fi *fileDescriptor) Flush() error { func (fi *fileDescriptor) flushUp(fullSync bool) error { var nd ipld.Node switch fi.state { - case stateDirty: - // calls mod.Sync internally. + case stateCreated, stateDirty: var err error nd, err = fi.mod.GetNode() if err != nil { @@ -150,15 +149,17 @@ func (fi *fileDescriptor) flushUp(fullSync bool) error { if err != nil { return err } + + // Always update the file descriptor's inode with the created/modified node. fi.inode.nodeLock.Lock() fi.inode.node = nd fi.inode.nodeLock.Unlock() - fallthrough - case stateCreated: + if !fullSync { return nil } + // Bubble up the update's to the parent, only if fullSync is set to true. fi.inode.nodeLock.Lock() nd = fi.inode.node parent := fi.inode.parent From 28f06fc05ebce5dd32ccbde5cf7836a20a357f40 Mon Sep 17 00:00:00 2001 From: nmalhotra Date: Wed, 26 Dec 2018 20:21:35 -0500 Subject: [PATCH 2833/3817] Moved all RW ops between same locks in flushUp This commit was moved from ipfs/go-mfs@ed06dbe0ea16e1288bcee7a0975f26f4d5023e97 --- mfs/fd.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/mfs/fd.go b/mfs/fd.go index 756e999f7..77a82d692 100644 --- a/mfs/fd.go +++ b/mfs/fd.go @@ -150,25 +150,26 @@ func (fi *fileDescriptor) flushUp(fullSync bool) error { return err } - // Always update the file descriptor's inode with the created/modified node. + // TODO: Very similar logic to the update process in + // `Directory`, the logic should be unified, both structures + // (`File` and `Directory`) are backed by a IPLD node with + // a UnixFS format that is the actual target of the update + // (regenerating it and adding it to the DAG service). fi.inode.nodeLock.Lock() + // Always update the file descriptor's inode with the created/modified node. fi.inode.node = nd - fi.inode.nodeLock.Unlock() - - if !fullSync { - return nil - } - - // Bubble up the update's to the parent, only if fullSync is set to true. - fi.inode.nodeLock.Lock() - nd = fi.inode.node + // Save the members to be used for subsequent calls parent := fi.inode.parent name := fi.inode.name fi.inode.nodeLock.Unlock() - if err := parent.updateChildEntry(child{name, nd}); err != nil { - return err + // Bubble up the update's to the parent, only if fullSync is set to true. + if fullSync { + if err := parent.updateChildEntry(child{name, nd}); err != nil { + return err + } } + fi.state = stateFlushed return nil case stateFlushed: From 6fb480340c6a13f6613829e2fbebf06ed758bbae Mon Sep 17 00:00:00 2001 From: nmalhotra Date: Thu, 27 Dec 2018 18:17:48 -0500 Subject: [PATCH 2834/3817] Merge unit test changes from issue #32 Pull in commit from go-ipfs commit https://github.com/ipfs/go-ipfs/commit/f4dc9a41bc5eb3ecae77554c4a75b50865fc57ec Dropped nloop to 500 (from 1000) in `TestConcurrentWriteAndFlush` to reduce testing time. All unittests pass. This commit was moved from ipfs/go-mfs@40c7e34f271676119c464dd110ed439be5300d29 --- mfs/mfs_test.go | 61 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 0274e17a6..dde556283 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -3,6 +3,7 @@ package mfs import ( "bytes" "context" + "encoding/binary" "errors" "fmt" "io" @@ -765,14 +766,14 @@ func TestConcurrentWriteAndFlush(t *testing.T) { t.Fatal(err) } - nloops := 5000 + nloops := 500 wg := new(sync.WaitGroup) wg.Add(1) go func() { defer wg.Done() for i := 0; i < nloops; i++ { - err := writeFile(rt, "/foo/bar/baz/file", []byte("STUFF")) + err := writeFile(rt, "/foo/bar/baz/file", func(_ []byte) []byte { return []byte("STUFF") }) if err != nil { t.Error("file write failed: ", err) return @@ -936,7 +937,7 @@ func TestConcurrentReads(t *testing.T) { wg.Wait() } -func writeFile(rt *Root, path string, data []byte) error { +func writeFile(rt *Root, path string, transform func([]byte) []byte) error { n, err := Lookup(rt, path) if err != nil { return err @@ -947,12 +948,27 @@ func writeFile(rt *Root, path string, data []byte) error { return fmt.Errorf("expected to receive a file, but didnt get one") } - fd, err := fi.Open(Flags{Write: true, Sync: true}) + fd, err := fi.Open(Flags{Read: true, Write: true, Sync: true}) if err != nil { return err } defer fd.Close() + data, err := ioutil.ReadAll(fd) + if err != nil { + return err + } + data = transform(data) + + _, err = fd.Seek(0, io.SeekStart) + if err != nil { + return err + } + err = fd.Truncate(0) + if err != nil { + return err + } + nw, err := fd.Write(data) if err != nil { return err @@ -986,19 +1002,48 @@ func TestConcurrentWrites(t *testing.T) { nloops := 100 for i := 0; i < 10; i++ { wg.Add(1) - go func(me int) { + go func() { defer wg.Done() - mybuf := bytes.Repeat([]byte{byte(me)}, 10) + var lastSeen uint64 for j := 0; j < nloops; j++ { - err := writeFile(rt, "a/b/c/afile", mybuf) + err := writeFile(rt, "a/b/c/afile", func(buf []byte) []byte { + if len(buf) == 0 { + if lastSeen > 0 { + t.Fatalf("file corrupted, last seen: %d", lastSeen) + } + buf = make([]byte, 8) + } else if len(buf) != 8 { + t.Fatal("buf not the right size") + } + + num := binary.LittleEndian.Uint64(buf) + if num < lastSeen { + t.Fatalf("count decreased: was %d, is %d", lastSeen, num) + } else { + t.Logf("count correct: was %d, is %d", lastSeen, num) + } + num++ + binary.LittleEndian.PutUint64(buf, num) + lastSeen = num + return buf + }) if err != nil { t.Error("writefile failed: ", err) return } } - }(i) + }() } wg.Wait() + buf := make([]byte, 8) + if err := readFile(rt, "a/b/c/afile", 0, buf); err != nil { + t.Fatal(err) + } + actual := binary.LittleEndian.Uint64(buf) + expected := uint64(10 * nloops) + if actual != expected { + t.Fatalf("iteration mismatch: expect %d, got %d", expected, actual) + } } func TestFileDescriptors(t *testing.T) { From 3301b037c33fd42e0deb10965447a7e26e1e86fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 Dec 2018 19:45:58 +0100 Subject: [PATCH 2835/3817] coreapi: move tests to interface subpackage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@a479105a40eddefc84ca1de9aaaf12cbe2e13e56 --- coreiface/tests/block_test.go | 183 +++++++ coreiface/tests/dag_test.go | 151 ++++++ coreiface/tests/dht_test.go | 126 +++++ coreiface/tests/key_test.go | 475 ++++++++++++++++ coreiface/tests/name_test.go | 262 +++++++++ coreiface/tests/object_test.go | 427 +++++++++++++++ coreiface/tests/path_test.go | 154 ++++++ coreiface/tests/pin_test.go | 214 ++++++++ coreiface/tests/pubsub_test.go | 106 ++++ coreiface/tests/unixfs_test.go | 963 +++++++++++++++++++++++++++++++++ 10 files changed, 3061 insertions(+) create mode 100644 coreiface/tests/block_test.go create mode 100644 coreiface/tests/dag_test.go create mode 100644 coreiface/tests/dht_test.go create mode 100644 coreiface/tests/key_test.go create mode 100644 coreiface/tests/name_test.go create mode 100644 coreiface/tests/object_test.go create mode 100644 coreiface/tests/path_test.go create mode 100644 coreiface/tests/pin_test.go create mode 100644 coreiface/tests/pubsub_test.go create mode 100644 coreiface/tests/unixfs_test.go diff --git a/coreiface/tests/block_test.go b/coreiface/tests/block_test.go new file mode 100644 index 000000000..81360b150 --- /dev/null +++ b/coreiface/tests/block_test.go @@ -0,0 +1,183 @@ +package tests_test + +import ( + "context" + "io/ioutil" + "strings" + "testing" + + coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" + opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + + mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" +) + +func TestBlockPut(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + res, err := api.Block().Put(ctx, strings.NewReader(`Hello`)) + if err != nil { + t.Error(err) + } + + if res.Path().Cid().String() != "QmPyo15ynbVrSTVdJL9th7JysHaAbXt9dM9tXk1bMHbRtk" { + t.Errorf("got wrong cid: %s", res.Path().Cid().String()) + } +} + +func TestBlockPutFormat(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("cbor")) + if err != nil { + t.Error(err) + } + + if res.Path().Cid().String() != "zdpuAn4amuLWo8Widi5v6VQpuo2dnpnwbVE3oB6qqs7mDSeoa" { + t.Errorf("got wrong cid: %s", res.Path().Cid().String()) + } +} + +func TestBlockPutHash(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Hash(mh.KECCAK_512, -1)) + if err != nil { + t.Fatal(err) + } + + if res.Path().Cid().String() != "zBurKB9YZkcDf6xa53WBE8CFX4ydVqAyf9KPXBFZt5stJzEstaS8Hukkhu4gwpMtc1xHNDbzP7sPtQKyWsP3C8fbhkmrZ" { + t.Errorf("got wrong cid: %s", res.Path().Cid().String()) + } +} + +func TestBlockGet(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Hash(mh.KECCAK_512, -1)) + if err != nil { + t.Error(err) + } + + r, err := api.Block().Get(ctx, res.Path()) + if err != nil { + t.Error(err) + } + + d, err := ioutil.ReadAll(r) + if err != nil { + t.Error(err) + } + + if string(d) != "Hello" { + t.Error("didn't get correct data back") + } + + p, err := coreiface.ParsePath("/ipfs/" + res.Path().Cid().String()) + if err != nil { + t.Error(err) + } + + rp, err := api.ResolvePath(ctx, p) + if err != nil { + t.Fatal(err) + } + if rp.Cid().String() != res.Path().Cid().String() { + t.Error("paths didn't match") + } +} + +func TestBlockRm(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + res, err := api.Block().Put(ctx, strings.NewReader(`Hello`)) + if err != nil { + t.Error(err) + } + + r, err := api.Block().Get(ctx, res.Path()) + if err != nil { + t.Error(err) + } + + d, err := ioutil.ReadAll(r) + if err != nil { + t.Error(err) + } + + if string(d) != "Hello" { + t.Error("didn't get correct data back") + } + + err = api.Block().Rm(ctx, res.Path()) + if err != nil { + t.Error(err) + } + + _, err = api.Block().Get(ctx, res.Path()) + if err == nil { + t.Error("expected err to exist") + } + if err.Error() != "blockservice: key not found" { + t.Errorf("unexpected error; %s", err.Error()) + } + + err = api.Block().Rm(ctx, res.Path()) + if err == nil { + t.Error("expected err to exist") + } + if err.Error() != "blockstore: block not found" { + t.Errorf("unexpected error; %s", err.Error()) + } + + err = api.Block().Rm(ctx, res.Path(), opt.Block.Force(true)) + if err != nil { + t.Error(err) + } +} + +func TestBlockStat(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + res, err := api.Block().Put(ctx, strings.NewReader(`Hello`)) + if err != nil { + t.Error(err) + } + + stat, err := api.Block().Stat(ctx, res.Path()) + if err != nil { + t.Error(err) + } + + if stat.Path().String() != res.Path().String() { + t.Error("paths don't match") + } + + if stat.Size() != len("Hello") { + t.Error("length doesn't match") + } +} diff --git a/coreiface/tests/dag_test.go b/coreiface/tests/dag_test.go new file mode 100644 index 000000000..17059192b --- /dev/null +++ b/coreiface/tests/dag_test.go @@ -0,0 +1,151 @@ +package tests_test + +import ( + "context" + "path" + "strings" + "testing" + + coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" + opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + + mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" +) + +var ( + treeExpected = map[string]struct{}{ + "a": {}, + "b": {}, + "c": {}, + "c/d": {}, + "c/e": {}, + } +) + +func TestPut(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + res, err := api.Dag().Put(ctx, strings.NewReader(`"Hello"`)) + if err != nil { + t.Error(err) + } + + if res.Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" { + t.Errorf("got wrong cid: %s", res.Cid().String()) + } +} + +func TestPutWithHash(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + res, err := api.Dag().Put(ctx, strings.NewReader(`"Hello"`), opt.Dag.Hash(mh.ID, -1)) + if err != nil { + t.Error(err) + } + + if res.Cid().String() != "z5hRLNd2sv4z1c" { + t.Errorf("got wrong cid: %s", res.Cid().String()) + } +} + +func TestPath(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + sub, err := api.Dag().Put(ctx, strings.NewReader(`"foo"`)) + if err != nil { + t.Error(err) + } + + res, err := api.Dag().Put(ctx, strings.NewReader(`{"lnk": {"/": "`+sub.Cid().String()+`"}}`)) + if err != nil { + t.Error(err) + } + + p, err := coreiface.ParsePath(path.Join(res.Cid().String(), "lnk")) + if err != nil { + t.Error(err) + } + + nd, err := api.Dag().Get(ctx, p) + if err != nil { + t.Error(err) + } + + if nd.Cid().String() != sub.Cid().String() { + t.Errorf("got unexpected cid %s, expected %s", nd.Cid().String(), sub.Cid().String()) + } +} + +func TestTree(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + c, err := api.Dag().Put(ctx, strings.NewReader(`{"a": 123, "b": "foo", "c": {"d": 321, "e": 111}}`)) + if err != nil { + t.Error(err) + } + + res, err := api.Dag().Get(ctx, c) + if err != nil { + t.Error(err) + } + + lst := res.Tree("", -1) + if len(lst) != len(treeExpected) { + t.Errorf("tree length of %d doesn't match expected %d", len(lst), len(treeExpected)) + } + + for _, ent := range lst { + if _, ok := treeExpected[ent]; !ok { + t.Errorf("unexpected tree entry %s", ent) + } + } +} + +func TestBatch(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + batch := api.Dag().Batch(ctx) + + c, err := batch.Put(ctx, strings.NewReader(`"Hello"`)) + if err != nil { + t.Error(err) + } + + if c.Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" { + t.Errorf("got wrong cid: %s", c.Cid().String()) + } + + _, err = api.Dag().Get(ctx, c) + if err == nil || err.Error() != "merkledag: not found" { + t.Error(err) + } + + if err := batch.Commit(ctx); err != nil { + t.Error(err) + } + + _, err = api.Dag().Get(ctx, c) + if err != nil { + t.Error(err) + } +} diff --git a/coreiface/tests/dht_test.go b/coreiface/tests/dht_test.go new file mode 100644 index 000000000..be16bb083 --- /dev/null +++ b/coreiface/tests/dht_test.go @@ -0,0 +1,126 @@ +package tests_test + +import ( + "context" + "io" + "testing" + + "github.com/ipfs/go-ipfs/core/coreapi/interface/options" +) + +func TestDhtFindPeer(t *testing.T) { + ctx := context.Background() + apis, err := makeAPISwarm(ctx, true, 5) + if err != nil { + t.Fatal(err) + } + + self0, err := apis[0].Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + pi, err := apis[2].Dht().FindPeer(ctx, self0.ID()) + if err != nil { + t.Fatal(err) + } + + if pi.Addrs[0].String() != "/ip4/127.0.0.1/tcp/4001" { + t.Errorf("got unexpected address from FindPeer: %s", pi.Addrs[0].String()) + } + + self2, err := apis[2].Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + pi, err = apis[1].Dht().FindPeer(ctx, self2.ID()) + if err != nil { + t.Fatal(err) + } + + if pi.Addrs[0].String() != "/ip4/127.0.2.1/tcp/4001" { + t.Errorf("got unexpected address from FindPeer: %s", pi.Addrs[0].String()) + } +} + +func TestDhtFindProviders(t *testing.T) { + ctx := context.Background() + apis, err := makeAPISwarm(ctx, true, 5) + if err != nil { + t.Fatal(err) + } + + p, err := addTestObject(ctx, apis[0]) + if err != nil { + t.Fatal(err) + } + + out, err := apis[2].Dht().FindProviders(ctx, p, options.Dht.NumProviders(1)) + if err != nil { + t.Fatal(err) + } + + provider := <-out + + self0, err := apis[0].Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + if provider.ID.String() != self0.ID().String() { + t.Errorf("got wrong provider: %s != %s", provider.ID.String(), self0.ID().String()) + } +} + +func TestDhtProvide(t *testing.T) { + ctx := context.Background() + apis, err := makeAPISwarm(ctx, true, 5) + if err != nil { + t.Fatal(err) + } + + off0, err := apis[0].WithOptions(options.Api.Offline(true)) + if err != nil { + t.Fatal(err) + } + + s, err := off0.Block().Put(ctx, &io.LimitedReader{R: rnd, N: 4092}) + if err != nil { + t.Fatal(err) + } + + p := s.Path() + + out, err := apis[2].Dht().FindProviders(ctx, p, options.Dht.NumProviders(1)) + if err != nil { + t.Fatal(err) + } + + provider := <-out + + self0, err := apis[0].Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + if provider.ID.String() != "" { + t.Errorf("got wrong provider: %s != %s", provider.ID.String(), self0.ID().String()) + } + + err = apis[0].Dht().Provide(ctx, p) + if err != nil { + t.Fatal(err) + } + + out, err = apis[2].Dht().FindProviders(ctx, p, options.Dht.NumProviders(1)) + if err != nil { + t.Fatal(err) + } + + provider = <-out + + if provider.ID.String() != self0.ID().String() { + t.Errorf("got wrong provider: %s != %s", provider.ID.String(), self0.ID().String()) + } +} diff --git a/coreiface/tests/key_test.go b/coreiface/tests/key_test.go new file mode 100644 index 000000000..21884e448 --- /dev/null +++ b/coreiface/tests/key_test.go @@ -0,0 +1,475 @@ +package tests_test + +import ( + "context" + "strings" + "testing" + + opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" +) + +func TestListSelf(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + return + } + + keys, err := api.Key().List(ctx) + if err != nil { + t.Fatalf("failed to list keys: %s", err) + return + } + + if len(keys) != 1 { + t.Fatalf("there should be 1 key (self), got %d", len(keys)) + return + } + + if keys[0].Name() != "self" { + t.Errorf("expected the key to be called 'self', got '%s'", keys[0].Name()) + } + + if keys[0].Path().String() != "/ipns/"+testPeerID { + t.Errorf("expected the key to have path '/ipns/%s', got '%s'", testPeerID, keys[0].Path().String()) + } +} + +func TestRenameSelf(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + return + } + + _, _, err = api.Key().Rename(ctx, "self", "foo") + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "cannot rename key with name 'self'" { + t.Fatalf("expected error 'cannot rename key with name 'self'', got '%s'", err.Error()) + } + } + + _, _, err = api.Key().Rename(ctx, "self", "foo", opt.Key.Force(true)) + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "cannot rename key with name 'self'" { + t.Fatalf("expected error 'cannot rename key with name 'self'', got '%s'", err.Error()) + } + } +} + +func TestRemoveSelf(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + return + } + + _, err = api.Key().Remove(ctx, "self") + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "cannot remove key with name 'self'" { + t.Fatalf("expected error 'cannot remove key with name 'self'', got '%s'", err.Error()) + } + } +} + +func TestGenerate(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + k, err := api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + if k.Name() != "foo" { + t.Errorf("expected the key to be called 'foo', got '%s'", k.Name()) + } + + if !strings.HasPrefix(k.Path().String(), "/ipns/Qm") { + t.Errorf("expected the key to be prefixed with '/ipns/Qm', got '%s'", k.Path().String()) + } +} + +func TestGenerateSize(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + k, err := api.Key().Generate(ctx, "foo", opt.Key.Size(1024)) + if err != nil { + t.Fatal(err) + return + } + + if k.Name() != "foo" { + t.Errorf("expected the key to be called 'foo', got '%s'", k.Name()) + } + + if !strings.HasPrefix(k.Path().String(), "/ipns/Qm") { + t.Errorf("expected the key to be prefixed with '/ipns/Qm', got '%s'", k.Path().String()) + } +} + +func TestGenerateType(t *testing.T) { + ctx := context.Background() + t.Skip("disabled until libp2p/specs#111 is fixed") + + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + k, err := api.Key().Generate(ctx, "bar", opt.Key.Type(opt.Ed25519Key)) + if err != nil { + t.Fatal(err) + return + } + + if k.Name() != "bar" { + t.Errorf("expected the key to be called 'foo', got '%s'", k.Name()) + } + + // Expected to be an inlined identity hash. + if !strings.HasPrefix(k.Path().String(), "/ipns/12") { + t.Errorf("expected the key to be prefixed with '/ipns/12', got '%s'", k.Path().String()) + } +} + +func TestGenerateExisting(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + _, err = api.Key().Generate(ctx, "foo") + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "key with name 'foo' already exists" { + t.Fatalf("expected error 'key with name 'foo' already exists', got '%s'", err.Error()) + } + } + + _, err = api.Key().Generate(ctx, "self") + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "cannot create key with name 'self'" { + t.Fatalf("expected error 'cannot create key with name 'self'', got '%s'", err.Error()) + } + } +} + +func TestList(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + l, err := api.Key().List(ctx) + if err != nil { + t.Fatal(err) + return + } + + if len(l) != 2 { + t.Fatalf("expected to get 2 keys, got %d", len(l)) + return + } + + if l[0].Name() != "self" { + t.Fatalf("expected key 0 to be called 'self', got '%s'", l[0].Name()) + return + } + + if l[1].Name() != "foo" { + t.Fatalf("expected key 1 to be called 'foo', got '%s'", l[1].Name()) + return + } + + if !strings.HasPrefix(l[0].Path().String(), "/ipns/Qm") { + t.Fatalf("expected key 0 to be prefixed with '/ipns/Qm', got '%s'", l[0].Name()) + return + } + + if !strings.HasPrefix(l[1].Path().String(), "/ipns/Qm") { + t.Fatalf("expected key 1 to be prefixed with '/ipns/Qm', got '%s'", l[1].Name()) + return + } +} + +func TestRename(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + k, overwrote, err := api.Key().Rename(ctx, "foo", "bar") + if err != nil { + t.Fatal(err) + return + } + + if overwrote { + t.Error("overwrote should be false") + } + + if k.Name() != "bar" { + t.Errorf("returned key should be called 'bar', got '%s'", k.Name()) + } +} + +func TestRenameToSelf(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + _, _, err = api.Key().Rename(ctx, "foo", "self") + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "cannot overwrite key with name 'self'" { + t.Fatalf("expected error 'cannot overwrite key with name 'self'', got '%s'", err.Error()) + } + } +} + +func TestRenameToSelfForce(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + _, _, err = api.Key().Rename(ctx, "foo", "self", opt.Key.Force(true)) + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "cannot overwrite key with name 'self'" { + t.Fatalf("expected error 'cannot overwrite key with name 'self'', got '%s'", err.Error()) + } + } +} + +func TestRenameOverwriteNoForce(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + _, err = api.Key().Generate(ctx, "bar") + if err != nil { + t.Fatal(err) + return + } + + _, _, err = api.Key().Rename(ctx, "foo", "bar") + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "key by that name already exists, refusing to overwrite" { + t.Fatalf("expected error 'key by that name already exists, refusing to overwrite', got '%s'", err.Error()) + } + } +} + +func TestRenameOverwrite(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + kfoo, err := api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + _, err = api.Key().Generate(ctx, "bar") + if err != nil { + t.Fatal(err) + return + } + + k, overwrote, err := api.Key().Rename(ctx, "foo", "bar", opt.Key.Force(true)) + if err != nil { + t.Fatal(err) + return + } + + if !overwrote { + t.Error("overwrote should be true") + } + + if k.Name() != "bar" { + t.Errorf("returned key should be called 'bar', got '%s'", k.Name()) + } + + if k.Path().String() != kfoo.Path().String() { + t.Errorf("k and kfoo should have equal paths, '%s'!='%s'", k.Path().String(), kfoo.Path().String()) + } +} + +func TestRenameSameNameNoForce(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + k, overwrote, err := api.Key().Rename(ctx, "foo", "foo") + if err != nil { + t.Fatal(err) + return + } + + if overwrote { + t.Error("overwrote should be false") + } + + if k.Name() != "foo" { + t.Errorf("returned key should be called 'foo', got '%s'", k.Name()) + } +} + +func TestRenameSameName(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + k, overwrote, err := api.Key().Rename(ctx, "foo", "foo", opt.Key.Force(true)) + if err != nil { + t.Fatal(err) + return + } + + if overwrote { + t.Error("overwrote should be false") + } + + if k.Name() != "foo" { + t.Errorf("returned key should be called 'foo', got '%s'", k.Name()) + } +} + +func TestRemove(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + k, err := api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + l, err := api.Key().List(ctx) + if err != nil { + t.Fatal(err) + return + } + + if len(l) != 2 { + t.Fatalf("expected to get 2 keys, got %d", len(l)) + return + } + + p, err := api.Key().Remove(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + if k.Path().String() != p.Path().String() { + t.Errorf("k and p should have equal paths, '%s'!='%s'", k.Path().String(), p.Path().String()) + } + + l, err = api.Key().List(ctx) + if err != nil { + t.Fatal(err) + return + } + + if len(l) != 1 { + t.Fatalf("expected to get 1 key, got %d", len(l)) + return + } + + if l[0].Name() != "self" { + t.Errorf("expected the key to be called 'self', got '%s'", l[0].Name()) + } +} diff --git a/coreiface/tests/name_test.go b/coreiface/tests/name_test.go new file mode 100644 index 000000000..a3514e051 --- /dev/null +++ b/coreiface/tests/name_test.go @@ -0,0 +1,262 @@ +package tests_test + +import ( + "context" + "io" + "math/rand" + "path" + "testing" + "time" + + "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files" + ipath "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" + + coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" + opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" +) + +var rnd = rand.New(rand.NewSource(0x62796532303137)) + +func addTestObject(ctx context.Context, api coreiface.CoreAPI) (coreiface.Path, error) { + return api.Unixfs().Add(ctx, files.NewReaderFile(&io.LimitedReader{R: rnd, N: 4092})) +} + +func appendPath(p coreiface.Path, sub string) coreiface.Path { + p, err := coreiface.ParsePath(path.Join(p.String(), sub)) + if err != nil { + panic(err) + } + return p +} + +func TestPublishResolve(t *testing.T) { + ctx := context.Background() + init := func() (coreiface.CoreAPI, coreiface.Path) { + apis, err := makeAPISwarm(ctx, true, 5) + if err != nil { + t.Fatal(err) + return nil, nil + } + api := apis[0] + + p, err := addTestObject(ctx, api) + if err != nil { + t.Fatal(err) + return nil, nil + } + return api, p + } + + run := func(t *testing.T, ropts []opt.NameResolveOption) { + t.Run("basic", func(t *testing.T) { + api, p := init() + e, err := api.Name().Publish(ctx, p) + if err != nil { + t.Fatal(err) + } + + self, err := api.Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + if e.Name() != self.ID().Pretty() { + t.Errorf("expected e.Name to equal '%s', got '%s'", self.ID().Pretty(), e.Name()) + } + + if e.Value().String() != p.String() { + t.Errorf("expected paths to match, '%s'!='%s'", e.Value().String(), p.String()) + } + + resPath, err := api.Name().Resolve(ctx, e.Name(), ropts...) + if err != nil { + t.Fatal(err) + } + + if resPath.String() != p.String() { + t.Errorf("expected paths to match, '%s'!='%s'", resPath.String(), p.String()) + } + }) + + t.Run("publishPath", func(t *testing.T) { + api, p := init() + e, err := api.Name().Publish(ctx, appendPath(p, "/test")) + if err != nil { + t.Fatal(err) + } + + self, err := api.Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + if e.Name() != self.ID().Pretty() { + t.Errorf("expected e.Name to equal '%s', got '%s'", self.ID().Pretty(), e.Name()) + } + + if e.Value().String() != p.String()+"/test" { + t.Errorf("expected paths to match, '%s'!='%s'", e.Value().String(), p.String()) + } + + resPath, err := api.Name().Resolve(ctx, e.Name(), ropts...) + if err != nil { + t.Fatal(err) + } + + if resPath.String() != p.String()+"/test" { + t.Errorf("expected paths to match, '%s'!='%s'", resPath.String(), p.String()+"/test") + } + }) + + t.Run("revolvePath", func(t *testing.T) { + api, p := init() + e, err := api.Name().Publish(ctx, p) + if err != nil { + t.Fatal(err) + } + + self, err := api.Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + if e.Name() != self.ID().Pretty() { + t.Errorf("expected e.Name to equal '%s', got '%s'", self.ID().Pretty(), e.Name()) + } + + if e.Value().String() != p.String() { + t.Errorf("expected paths to match, '%s'!='%s'", e.Value().String(), p.String()) + } + + resPath, err := api.Name().Resolve(ctx, e.Name()+"/test", ropts...) + if err != nil { + t.Fatal(err) + } + + if resPath.String() != p.String()+"/test" { + t.Errorf("expected paths to match, '%s'!='%s'", resPath.String(), p.String()+"/test") + } + }) + + t.Run("publishRevolvePath", func(t *testing.T) { + api, p := init() + e, err := api.Name().Publish(ctx, appendPath(p, "/a")) + if err != nil { + t.Fatal(err) + } + + self, err := api.Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + if e.Name() != self.ID().Pretty() { + t.Errorf("expected e.Name to equal '%s', got '%s'", self.ID().Pretty(), e.Name()) + } + + if e.Value().String() != p.String()+"/a" { + t.Errorf("expected paths to match, '%s'!='%s'", e.Value().String(), p.String()) + } + + resPath, err := api.Name().Resolve(ctx, e.Name()+"/b", ropts...) + if err != nil { + t.Fatal(err) + } + + if resPath.String() != p.String()+"/a/b" { + t.Errorf("expected paths to match, '%s'!='%s'", resPath.String(), p.String()+"/a/b") + } + }) + } + + t.Run("default", func(t *testing.T) { + run(t, []opt.NameResolveOption{}) + }) + + t.Run("nocache", func(t *testing.T) { + run(t, []opt.NameResolveOption{opt.Name.Cache(false)}) + }) +} + +func TestBasicPublishResolveKey(t *testing.T) { + ctx := context.Background() + apis, err := makeAPISwarm(ctx, true, 5) + if err != nil { + t.Fatal(err) + } + api := apis[0] + + k, err := api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + } + + p, err := addTestObject(ctx, api) + if err != nil { + t.Fatal(err) + } + + e, err := api.Name().Publish(ctx, p, opt.Name.Key(k.Name())) + if err != nil { + t.Fatal(err) + } + + if ipath.Join([]string{"/ipns", e.Name()}) != k.Path().String() { + t.Errorf("expected e.Name to equal '%s', got '%s'", e.Name(), k.Path().String()) + } + + if e.Value().String() != p.String() { + t.Errorf("expected paths to match, '%s'!='%s'", e.Value().String(), p.String()) + } + + resPath, err := api.Name().Resolve(ctx, e.Name()) + if err != nil { + t.Fatal(err) + } + + if resPath.String() != p.String() { + t.Errorf("expected paths to match, '%s'!='%s'", resPath.String(), p.String()) + } +} + +func TestBasicPublishResolveTimeout(t *testing.T) { + t.Skip("ValidTime doesn't appear to work at this time resolution") + + ctx := context.Background() + apis, err := makeAPISwarm(ctx, true, 5) + if err != nil { + t.Fatal(err) + } + api := apis[0] + p, err := addTestObject(ctx, api) + if err != nil { + t.Fatal(err) + } + + e, err := api.Name().Publish(ctx, p, opt.Name.ValidTime(time.Millisecond*100)) + if err != nil { + t.Fatal(err) + } + + self, err := api.Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + if e.Name() != self.ID().Pretty() { + t.Errorf("expected e.Name to equal '%s', got '%s'", self.ID().Pretty(), e.Name()) + } + + if e.Value().String() != p.String() { + t.Errorf("expected paths to match, '%s'!='%s'", e.Value().String(), p.String()) + } + + time.Sleep(time.Second) + + _, err = api.Name().Resolve(ctx, e.Name()) + if err == nil { + t.Fatal("Expected an error") + } +} + +//TODO: When swarm api is created, add multinode tests diff --git a/coreiface/tests/object_test.go b/coreiface/tests/object_test.go new file mode 100644 index 000000000..ac9e1d5f3 --- /dev/null +++ b/coreiface/tests/object_test.go @@ -0,0 +1,427 @@ +package tests_test + +import ( + "bytes" + "context" + "encoding/hex" + "io/ioutil" + "strings" + "testing" + + "github.com/ipfs/go-ipfs/core/coreapi/interface" + opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" +) + +func TestNew(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + emptyNode, err := api.Object().New(ctx) + if err != nil { + t.Fatal(err) + } + + dirNode, err := api.Object().New(ctx, opt.Object.Type("unixfs-dir")) + if err != nil { + t.Fatal(err) + } + + if emptyNode.String() != "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" { + t.Errorf("Unexpected emptyNode path: %s", emptyNode.String()) + } + + if dirNode.String() != "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn" { + t.Errorf("Unexpected dirNode path: %s", dirNode.String()) + } +} + +func TestObjectPut(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) + if err != nil { + t.Fatal(err) + } + + p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"YmFy"}`), opt.Object.DataType("base64")) //bar + if err != nil { + t.Fatal(err) + } + + pbBytes, err := hex.DecodeString("0a0362617a") + if err != nil { + t.Fatal(err) + } + + p3, err := api.Object().Put(ctx, bytes.NewReader(pbBytes), opt.Object.InputEnc("protobuf")) + if err != nil { + t.Fatal(err) + } + + if p1.String() != "/ipfs/QmQeGyS87nyijii7kFt1zbe4n2PsXTFimzsdxyE9qh9TST" { + t.Errorf("unexpected path: %s", p1.String()) + } + + if p2.String() != "/ipfs/QmNeYRbCibmaMMK6Du6ChfServcLqFvLJF76PzzF76SPrZ" { + t.Errorf("unexpected path: %s", p2.String()) + } + + if p3.String() != "/ipfs/QmZreR7M2t7bFXAdb1V5FtQhjk4t36GnrvueLJowJbQM9m" { + t.Errorf("unexpected path: %s", p3.String()) + } +} + +func TestObjectGet(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) + if err != nil { + t.Fatal(err) + } + + nd, err := api.Object().Get(ctx, p1) + if err != nil { + t.Fatal(err) + } + + if string(nd.RawData()[len(nd.RawData())-3:]) != "foo" { + t.Fatal("got non-matching data") + } +} + +func TestObjectData(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) + if err != nil { + t.Fatal(err) + } + + r, err := api.Object().Data(ctx, p1) + if err != nil { + t.Fatal(err) + } + + data, err := ioutil.ReadAll(r) + if err != nil { + t.Fatal(err) + } + + if string(data) != "foo" { + t.Fatal("got non-matching data") + } +} + +func TestObjectLinks(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) + if err != nil { + t.Fatal(err) + } + + p2, err := api.Object().Put(ctx, strings.NewReader(`{"Links":[{"Name":"bar", "Hash":"`+p1.Cid().String()+`"}]}`)) + if err != nil { + t.Fatal(err) + } + + links, err := api.Object().Links(ctx, p2) + if err != nil { + t.Fatal(err) + } + + if len(links) != 1 { + t.Errorf("unexpected number of links: %d", len(links)) + } + + if links[0].Cid.String() != p1.Cid().String() { + t.Fatal("cids didn't batch") + } + + if links[0].Name != "bar" { + t.Fatal("unexpected link name") + } +} + +func TestObjectStat(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) + if err != nil { + t.Fatal(err) + } + + p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.Cid().String()+`", "Size":3}]}`)) + if err != nil { + t.Fatal(err) + } + + stat, err := api.Object().Stat(ctx, p2) + if err != nil { + t.Fatal(err) + } + + if stat.Cid.String() != p2.Cid().String() { + t.Error("unexpected stat.Cid") + } + + if stat.NumLinks != 1 { + t.Errorf("unexpected stat.NumLinks") + } + + if stat.BlockSize != 51 { + t.Error("unexpected stat.BlockSize") + } + + if stat.LinksSize != 47 { + t.Errorf("unexpected stat.LinksSize: %d", stat.LinksSize) + } + + if stat.DataSize != 4 { + t.Error("unexpected stat.DataSize") + } + + if stat.CumulativeSize != 54 { + t.Error("unexpected stat.DataSize") + } +} + +func TestObjectAddLink(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) + if err != nil { + t.Fatal(err) + } + + p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.Cid().String()+`", "Size":3}]}`)) + if err != nil { + t.Fatal(err) + } + + p3, err := api.Object().AddLink(ctx, p2, "abc", p2) + if err != nil { + t.Fatal(err) + } + + links, err := api.Object().Links(ctx, p3) + if err != nil { + t.Fatal(err) + } + + if len(links) != 2 { + t.Errorf("unexpected number of links: %d", len(links)) + } + + if links[0].Name != "abc" { + t.Errorf("unexpected link 0 name: %s", links[0].Name) + } + + if links[1].Name != "bar" { + t.Errorf("unexpected link 1 name: %s", links[1].Name) + } +} + +func TestObjectAddLinkCreate(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) + if err != nil { + t.Fatal(err) + } + + p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.Cid().String()+`", "Size":3}]}`)) + if err != nil { + t.Fatal(err) + } + + p3, err := api.Object().AddLink(ctx, p2, "abc/d", p2) + if err == nil { + t.Fatal("expected an error") + } + if err.Error() != "no link by that name" { + t.Fatalf("unexpected error: %s", err.Error()) + } + + p3, err = api.Object().AddLink(ctx, p2, "abc/d", p2, opt.Object.Create(true)) + if err != nil { + t.Fatal(err) + } + + links, err := api.Object().Links(ctx, p3) + if err != nil { + t.Fatal(err) + } + + if len(links) != 2 { + t.Errorf("unexpected number of links: %d", len(links)) + } + + if links[0].Name != "abc" { + t.Errorf("unexpected link 0 name: %s", links[0].Name) + } + + if links[1].Name != "bar" { + t.Errorf("unexpected link 1 name: %s", links[1].Name) + } +} + +func TestObjectRmLink(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) + if err != nil { + t.Fatal(err) + } + + p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.Cid().String()+`", "Size":3}]}`)) + if err != nil { + t.Fatal(err) + } + + p3, err := api.Object().RmLink(ctx, p2, "bar") + if err != nil { + t.Fatal(err) + } + + links, err := api.Object().Links(ctx, p3) + if err != nil { + t.Fatal(err) + } + + if len(links) != 0 { + t.Errorf("unexpected number of links: %d", len(links)) + } +} + +func TestObjectAddData(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) + if err != nil { + t.Fatal(err) + } + + p2, err := api.Object().AppendData(ctx, p1, strings.NewReader("bar")) + if err != nil { + t.Fatal(err) + } + + r, err := api.Object().Data(ctx, p2) + if err != nil { + t.Fatal(err) + } + + data, err := ioutil.ReadAll(r) + + if string(data) != "foobar" { + t.Error("unexpected data") + } +} + +func TestObjectSetData(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) + if err != nil { + t.Fatal(err) + } + + p2, err := api.Object().SetData(ctx, p1, strings.NewReader("bar")) + if err != nil { + t.Fatal(err) + } + + r, err := api.Object().Data(ctx, p2) + if err != nil { + t.Fatal(err) + } + + data, err := ioutil.ReadAll(r) + + if string(data) != "bar" { + t.Error("unexpected data") + } +} + +func TestDiffTest(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) + if err != nil { + t.Fatal(err) + } + + p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bar"}`)) + if err != nil { + t.Fatal(err) + } + + changes, err := api.Object().Diff(ctx, p1, p2) + if err != nil { + t.Fatal(err) + } + + if len(changes) != 1 { + t.Fatal("unexpected changes len") + } + + if changes[0].Type != iface.DiffMod { + t.Fatal("unexpected change type") + } + + if changes[0].Before.String() != p1.String() { + t.Fatal("unexpected before path") + } + + if changes[0].After.String() != p2.String() { + t.Fatal("unexpected before path") + } +} diff --git a/coreiface/tests/path_test.go b/coreiface/tests/path_test.go new file mode 100644 index 000000000..e05428073 --- /dev/null +++ b/coreiface/tests/path_test.go @@ -0,0 +1,154 @@ +package tests_test + +import ( + "context" + "strings" + "testing" + + coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" + "github.com/ipfs/go-ipfs/core/coreapi/interface/options" +) + +func TestMutablePath(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + // get self /ipns path + keys, err := api.Key().List(ctx) + if err != nil { + t.Fatal(err) + } + + if !keys[0].Path().Mutable() { + t.Error("expected self /ipns path to be mutable") + } + + blk, err := api.Block().Put(ctx, strings.NewReader(`foo`)) + if err != nil { + t.Error(err) + } + + if blk.Path().Mutable() { + t.Error("expected /ipld path to be immutable") + } +} + +func TestPathRemainder(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"bar": "baz"}}`)) + if err != nil { + t.Fatal(err) + } + + p1, err := coreiface.ParsePath(obj.String() + "/foo/bar") + if err != nil { + t.Error(err) + } + + rp1, err := api.ResolvePath(ctx, p1) + if err != nil { + t.Fatal(err) + } + + if rp1.Remainder() != "foo/bar" { + t.Error("expected to get path remainder") + } +} + +func TestEmptyPathRemainder(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"bar": "baz"}}`)) + if err != nil { + t.Fatal(err) + } + + if obj.Remainder() != "" { + t.Error("expected the resolved path to not have a remainder") + } + + p1, err := coreiface.ParsePath(obj.String()) + if err != nil { + t.Error(err) + } + + rp1, err := api.ResolvePath(ctx, p1) + if err != nil { + t.Fatal(err) + } + + if rp1.Remainder() != "" { + t.Error("expected the resolved path to not have a remainder") + } +} + +func TestInvalidPathRemainder(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"bar": "baz"}}`)) + if err != nil { + t.Fatal(err) + } + + p1, err := coreiface.ParsePath(obj.String() + "/bar/baz") + if err != nil { + t.Error(err) + } + + _, err = api.ResolvePath(ctx, p1) + if err == nil || err.Error() != "no such link found" { + t.Fatalf("unexpected error: %s", err) + } +} + +func TestPathRoot(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + blk, err := api.Block().Put(ctx, strings.NewReader(`foo`), options.Block.Format("raw")) + if err != nil { + t.Error(err) + } + + obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"/": "`+blk.Path().Cid().String()+`"}}`)) + if err != nil { + t.Fatal(err) + } + + p1, err := coreiface.ParsePath(obj.String() + "/foo") + if err != nil { + t.Error(err) + } + + rp, err := api.ResolvePath(ctx, p1) + if err != nil { + t.Fatal(err) + } + + if rp.Root().String() != obj.Cid().String() { + t.Error("unexpected path root") + } + + if rp.Cid().String() != blk.Path().Cid().String() { + t.Error("unexpected path cid") + } +} diff --git a/coreiface/tests/pin_test.go b/coreiface/tests/pin_test.go new file mode 100644 index 000000000..5c4b82bc2 --- /dev/null +++ b/coreiface/tests/pin_test.go @@ -0,0 +1,214 @@ +package tests_test + +import ( + "context" + "strings" + "testing" + + opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" +) + +func TestPinAdd(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + p, err := api.Unixfs().Add(ctx, strFile("foo")()) + if err != nil { + t.Error(err) + } + + err = api.Pin().Add(ctx, p) + if err != nil { + t.Error(err) + } +} + +func TestPinSimple(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + p, err := api.Unixfs().Add(ctx, strFile("foo")()) + if err != nil { + t.Error(err) + } + + err = api.Pin().Add(ctx, p) + if err != nil { + t.Error(err) + } + + list, err := api.Pin().Ls(ctx) + if err != nil { + t.Fatal(err) + } + + if len(list) != 1 { + t.Errorf("unexpected pin list len: %d", len(list)) + } + + if list[0].Path().Cid().String() != p.Cid().String() { + t.Error("paths don't match") + } + + if list[0].Type() != "recursive" { + t.Error("unexpected pin type") + } + + err = api.Pin().Rm(ctx, p) + if err != nil { + t.Fatal(err) + } + + list, err = api.Pin().Ls(ctx) + if err != nil { + t.Fatal(err) + } + + if len(list) != 0 { + t.Errorf("unexpected pin list len: %d", len(list)) + } +} + +func TestPinRecursive(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + p0, err := api.Unixfs().Add(ctx, strFile("foo")()) + if err != nil { + t.Error(err) + } + + p1, err := api.Unixfs().Add(ctx, strFile("bar")()) + if err != nil { + t.Error(err) + } + + p2, err := api.Dag().Put(ctx, strings.NewReader(`{"lnk": {"/": "`+p0.Cid().String()+`"}}`)) + if err != nil { + t.Error(err) + } + + p3, err := api.Dag().Put(ctx, strings.NewReader(`{"lnk": {"/": "`+p1.Cid().String()+`"}}`)) + if err != nil { + t.Error(err) + } + + err = api.Pin().Add(ctx, p2) + if err != nil { + t.Error(err) + } + + err = api.Pin().Add(ctx, p3, opt.Pin.Recursive(false)) + if err != nil { + t.Error(err) + } + + list, err := api.Pin().Ls(ctx) + if err != nil { + t.Fatal(err) + } + + if len(list) != 3 { + t.Errorf("unexpected pin list len: %d", len(list)) + } + + list, err = api.Pin().Ls(ctx, opt.Pin.Type.Direct()) + if err != nil { + t.Fatal(err) + } + + if len(list) != 1 { + t.Errorf("unexpected pin list len: %d", len(list)) + } + + if list[0].Path().String() != p3.String() { + t.Error("unexpected path") + } + + list, err = api.Pin().Ls(ctx, opt.Pin.Type.Recursive()) + if err != nil { + t.Fatal(err) + } + + if len(list) != 1 { + t.Errorf("unexpected pin list len: %d", len(list)) + } + + if list[0].Path().String() != p2.String() { + t.Error("unexpected path") + } + + list, err = api.Pin().Ls(ctx, opt.Pin.Type.Indirect()) + if err != nil { + t.Fatal(err) + } + + if len(list) != 1 { + t.Errorf("unexpected pin list len: %d", len(list)) + } + + if list[0].Path().Cid().String() != p0.Cid().String() { + t.Error("unexpected path") + } + + res, err := api.Pin().Verify(ctx) + if err != nil { + t.Fatal(err) + } + n := 0 + for r := range res { + if !r.Ok() { + t.Error("expected pin to be ok") + } + n++ + } + + if n != 1 { + t.Errorf("unexpected verify result count: %d", n) + } + + //TODO: figure out a way to test verify without touching IpfsNode + /* + err = api.Block().Rm(ctx, p0, opt.Block.Force(true)) + if err != nil { + t.Fatal(err) + } + + res, err = api.Pin().Verify(ctx) + if err != nil { + t.Fatal(err) + } + n = 0 + for r := range res { + if r.Ok() { + t.Error("expected pin to not be ok") + } + + if len(r.BadNodes()) != 1 { + t.Fatalf("unexpected badNodes len") + } + + if r.BadNodes()[0].Path().Cid().String() != p0.Cid().String() { + t.Error("unexpected badNode path") + } + + if r.BadNodes()[0].Err().Error() != "merkledag: not found" { + t.Errorf("unexpected badNode error: %s", r.BadNodes()[0].Err().Error()) + } + n++ + } + + if n != 1 { + t.Errorf("unexpected verify result count: %d", n) + } + */ +} diff --git a/coreiface/tests/pubsub_test.go b/coreiface/tests/pubsub_test.go new file mode 100644 index 000000000..19a1eba52 --- /dev/null +++ b/coreiface/tests/pubsub_test.go @@ -0,0 +1,106 @@ +package tests_test + +import ( + "context" + "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + "testing" + "time" +) + +func TestBasicPubSub(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + apis, err := makeAPISwarm(ctx, true, 2) + if err != nil { + t.Fatal(err) + } + + sub, err := apis[0].PubSub().Subscribe(ctx, "testch") + if err != nil { + t.Fatal(err) + } + + go func() { + tick := time.Tick(100 * time.Millisecond) + + for { + err = apis[1].PubSub().Publish(ctx, "testch", []byte("hello world")) + if err != nil { + t.Fatal(err) + } + select { + case <-tick: + case <-ctx.Done(): + return + } + } + }() + + m, err := sub.Next(ctx) + if err != nil { + t.Fatal(err) + } + + if string(m.Data()) != "hello world" { + t.Errorf("got invalid data: %s", string(m.Data())) + } + + self1, err := apis[1].Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + if m.From() != self1.ID() { + t.Errorf("m.From didn't match") + } + + peers, err := apis[1].PubSub().Peers(ctx, options.PubSub.Topic("testch")) + if err != nil { + t.Fatal(err) + } + + if len(peers) != 1 { + t.Fatalf("got incorrect number of peers: %d", len(peers)) + } + + self0, err := apis[0].Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + if peers[0] != self0.ID() { + t.Errorf("peer didn't match") + } + + peers, err = apis[1].PubSub().Peers(ctx, options.PubSub.Topic("nottestch")) + if err != nil { + t.Fatal(err) + } + + if len(peers) != 0 { + t.Fatalf("got incorrect number of peers: %d", len(peers)) + } + + topics, err := apis[0].PubSub().Ls(ctx) + if err != nil { + t.Fatal(err) + } + + if len(topics) != 1 { + t.Fatalf("got incorrect number of topics: %d", len(peers)) + } + + if topics[0] != "testch" { + t.Errorf("topic didn't match") + } + + topics, err = apis[1].PubSub().Ls(ctx) + if err != nil { + t.Fatal(err) + } + + if len(topics) != 0 { + t.Fatalf("got incorrect number of topics: %d", len(peers)) + } +} diff --git a/coreiface/tests/unixfs_test.go b/coreiface/tests/unixfs_test.go new file mode 100644 index 000000000..d7ddae963 --- /dev/null +++ b/coreiface/tests/unixfs_test.go @@ -0,0 +1,963 @@ +package tests_test + +import ( + "bytes" + "context" + "encoding/base64" + "fmt" + "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + "io" + "io/ioutil" + "math" + "os" + "strconv" + "strings" + "sync" + "testing" + + "github.com/ipfs/go-ipfs/core" + "github.com/ipfs/go-ipfs/core/coreapi" + coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" + "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + mock "github.com/ipfs/go-ipfs/core/mock" + "github.com/ipfs/go-ipfs/keystore" + "github.com/ipfs/go-ipfs/repo" + + ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" + "gx/ipfs/QmRBaUEQEeFWywfrZJ64QgsmvcqgLSK3VbvGMR2NM2Edpf/go-libp2p/p2p/net/mock" + cbor "gx/ipfs/QmRoARq3nkUb13HSKZGepCZSWe5GrVPwx7xURJGZ7KWv9V/go-ipld-cbor" + "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files" + "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + pstore "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore" + "gx/ipfs/Qmbvw7kpSM2p6rbQ57WGRhhqNfCiNGW6EKH4xgHLw4bsnB/go-unixfs" + "gx/ipfs/QmcZfkbgwwwH5ZLTQRHkSQBDiDqd3skY2eU6MZRgWuXcse/go-ipfs-config" + mdag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" + mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" + "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" + syncds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" +) + +const testPeerID = "QmTFauExutTsy4XP6JbMFcw2Wa9645HJt2bTqL6qYDCKfe" + +// `echo -n 'hello, world!' | ipfs add` +var hello = "/ipfs/QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk" +var helloStr = "hello, world!" + +// `echo -n | ipfs add` +var emptyFile = "/ipfs/QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH" + +func makeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]coreiface.CoreAPI, error) { + mn := mocknet.New(ctx) + + nodes := make([]*core.IpfsNode, n) + apis := make([]coreiface.CoreAPI, n) + + for i := 0; i < n; i++ { + var ident config.Identity + if fullIdentity { + sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) + if err != nil { + return nil, err + } + + id, err := peer.IDFromPublicKey(pk) + if err != nil { + return nil, err + } + + kbytes, err := sk.Bytes() + if err != nil { + return nil, err + } + + ident = config.Identity{ + PeerID: id.Pretty(), + PrivKey: base64.StdEncoding.EncodeToString(kbytes), + } + } else { + ident = config.Identity{ + PeerID: testPeerID, + } + } + + c := config.Config{} + c.Addresses.Swarm = []string{fmt.Sprintf("/ip4/127.0.%d.1/tcp/4001", i)} + c.Identity = ident + + r := &repo.Mock{ + C: c, + D: syncds.MutexWrap(datastore.NewMapDatastore()), + K: keystore.NewMemKeystore(), + } + + node, err := core.NewNode(ctx, &core.BuildCfg{ + Repo: r, + Host: mock.MockHostOption(mn), + Online: fullIdentity, + ExtraOpts: map[string]bool{ + "pubsub": true, + }, + }) + if err != nil { + return nil, err + } + nodes[i] = node + apis[i], err = coreapi.NewCoreAPI(node) + if err != nil { + return nil, err + } + } + + err := mn.LinkAll() + if err != nil { + return nil, err + } + + bsinf := core.BootstrapConfigWithPeers( + []pstore.PeerInfo{ + nodes[0].Peerstore.PeerInfo(nodes[0].Identity), + }, + ) + + for _, n := range nodes[1:] { + if err := n.Bootstrap(bsinf); err != nil { + return nil, err + } + } + + return apis, nil +} + +func makeAPI(ctx context.Context) (coreiface.CoreAPI, error) { + api, err := makeAPISwarm(ctx, false, 1) + if err != nil { + return nil, err + } + + return api[0], nil +} + +func strFile(data string) func() files.Node { + return func() files.Node { + return files.NewBytesFile([]byte(data)) + } +} + +func twoLevelDir() func() files.Node { + return func() files.Node { + return files.NewMapDirectory(map[string]files.Node{ + "abc": files.NewMapDirectory(map[string]files.Node{ + "def": files.NewBytesFile([]byte("world")), + }), + + "bar": files.NewBytesFile([]byte("hello2")), + "foo": files.NewBytesFile([]byte("hello1")), + }) + } +} + +func flatDir() files.Node { + return files.NewMapDirectory(map[string]files.Node{ + "bar": files.NewBytesFile([]byte("hello2")), + "foo": files.NewBytesFile([]byte("hello1")), + }) +} + +func wrapped(name string) func(f files.Node) files.Node { + return func(f files.Node) files.Node { + return files.NewMapDirectory(map[string]files.Node{ + name: f, + }) + } +} + +func TestAdd(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + p := func(h string) coreiface.ResolvedPath { + c, err := cid.Parse(h) + if err != nil { + t.Fatal(err) + } + return coreiface.IpfsPath(c) + } + + cases := []struct { + name string + data func() files.Node + expect func(files.Node) files.Node + + apiOpts []options.ApiOption + + path string + err string + + wrap string + + events []coreiface.AddEvent + + opts []options.UnixfsAddOption + }{ + // Simple cases + { + name: "simpleAdd", + data: strFile(helloStr), + path: hello, + opts: []options.UnixfsAddOption{}, + }, + { + name: "addEmpty", + data: strFile(""), + path: emptyFile, + }, + // CIDv1 version / rawLeaves + { + name: "addCidV1", + data: strFile(helloStr), + path: "/ipfs/zb2rhdhmJjJZs9qkhQCpCQ7VREFkqWw3h1r8utjVvQugwHPFd", + opts: []options.UnixfsAddOption{options.Unixfs.CidVersion(1)}, + }, + { + name: "addCidV1NoLeaves", + data: strFile(helloStr), + path: "/ipfs/zdj7WY4GbN8NDbTW1dfCShAQNVovams2xhq9hVCx5vXcjvT8g", + opts: []options.UnixfsAddOption{options.Unixfs.CidVersion(1), options.Unixfs.RawLeaves(false)}, + }, + // Non sha256 hash vs CID + { + name: "addCidSha3", + data: strFile(helloStr), + path: "/ipfs/zb2wwnYtXBxpndNABjtYxWAPt3cwWNRnc11iT63fvkYV78iRb", + opts: []options.UnixfsAddOption{options.Unixfs.Hash(mh.SHA3_256)}, + }, + { + name: "addCidSha3Cid0", + data: strFile(helloStr), + err: "CIDv0 only supports sha2-256", + opts: []options.UnixfsAddOption{options.Unixfs.CidVersion(0), options.Unixfs.Hash(mh.SHA3_256)}, + }, + // Inline + { + name: "addInline", + data: strFile(helloStr), + path: "/ipfs/zaYomJdLndMku8P9LHngHB5w2CQ7NenLbv", + opts: []options.UnixfsAddOption{options.Unixfs.Inline(true)}, + }, + { + name: "addInlineLimit", + data: strFile(helloStr), + path: "/ipfs/zaYomJdLndMku8P9LHngHB5w2CQ7NenLbv", + opts: []options.UnixfsAddOption{options.Unixfs.InlineLimit(32), options.Unixfs.Inline(true)}, + }, + { + name: "addInlineZero", + data: strFile(""), + path: "/ipfs/z2yYDV", + opts: []options.UnixfsAddOption{options.Unixfs.InlineLimit(0), options.Unixfs.Inline(true), options.Unixfs.RawLeaves(true)}, + }, + { //TODO: after coreapi add is used in `ipfs add`, consider making this default for inline + name: "addInlineRaw", + data: strFile(helloStr), + path: "/ipfs/zj7Gr8AcBreqGEfrnR5kPFe", + opts: []options.UnixfsAddOption{options.Unixfs.InlineLimit(32), options.Unixfs.Inline(true), options.Unixfs.RawLeaves(true)}, + }, + // Chunker / Layout + { + name: "addChunks", + data: strFile(strings.Repeat("aoeuidhtns", 200)), + path: "/ipfs/QmRo11d4QJrST47aaiGVJYwPhoNA4ihRpJ5WaxBWjWDwbX", + opts: []options.UnixfsAddOption{options.Unixfs.Chunker("size-4")}, + }, + { + name: "addChunksTrickle", + data: strFile(strings.Repeat("aoeuidhtns", 200)), + path: "/ipfs/QmNNhDGttafX3M1wKWixGre6PrLFGjnoPEDXjBYpTv93HP", + opts: []options.UnixfsAddOption{options.Unixfs.Chunker("size-4"), options.Unixfs.Layout(options.TrickleLayout)}, + }, + // Local + { + name: "addLocal", // better cases in sharness + data: strFile(helloStr), + path: hello, + apiOpts: []options.ApiOption{options.Api.Offline(true)}, + }, + { + name: "hashOnly", // test (non)fetchability + data: strFile(helloStr), + path: hello, + opts: []options.UnixfsAddOption{options.Unixfs.HashOnly(true)}, + }, + // multi file + { + name: "simpleDir", + data: flatDir, + wrap: "t", + path: "/ipfs/QmRKGpFfR32FVXdvJiHfo4WJ5TDYBsM1P9raAp1p6APWSp", + }, + { + name: "twoLevelDir", + data: twoLevelDir(), + wrap: "t", + path: "/ipfs/QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr", + }, + // wrapped + { + name: "addWrapped", + path: "/ipfs/QmVE9rNpj5doj7XHzp5zMUxD7BJgXEqx4pe3xZ3JBReWHE", + data: func() files.Node { + return files.NewBytesFile([]byte(helloStr)) + }, + wrap: "foo", + expect: wrapped("foo"), + opts: []options.UnixfsAddOption{options.Unixfs.Wrap(true)}, + }, + { + name: "addNotWrappedDirFile", + path: hello, + data: func() files.Node { + return files.NewBytesFile([]byte(helloStr)) + }, + wrap: "foo", + }, + { + name: "stdinWrapped", + path: "/ipfs/QmU3r81oZycjHS9oaSHw37ootMFuFUw1DvMLKXPsezdtqU", + data: func() files.Node { + return files.NewBytesFile([]byte(helloStr)) + }, + expect: func(files.Node) files.Node { + return files.NewMapDirectory(map[string]files.Node{ + "QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk": files.NewBytesFile([]byte(helloStr)), + }) + }, + opts: []options.UnixfsAddOption{options.Unixfs.Wrap(true)}, + }, + { + name: "stdinNamed", + path: "/ipfs/QmQ6cGBmb3ZbdrQW1MRm1RJnYnaxCqfssz7CrTa9NEhQyS", + data: func() files.Node { + rf, err := files.NewReaderPathFile(os.Stdin.Name(), ioutil.NopCloser(strings.NewReader(helloStr)), nil) + if err != nil { + panic(err) + } + + return rf + }, + expect: func(files.Node) files.Node { + return files.NewMapDirectory(map[string]files.Node{ + "test": files.NewBytesFile([]byte(helloStr)), + }) + }, + opts: []options.UnixfsAddOption{options.Unixfs.Wrap(true), options.Unixfs.StdinName("test")}, + }, + { + name: "twoLevelDirWrapped", + data: twoLevelDir(), + wrap: "t", + expect: wrapped("t"), + path: "/ipfs/QmPwsL3T5sWhDmmAWZHAzyjKtMVDS9a11aHNRqb3xoVnmg", + opts: []options.UnixfsAddOption{options.Unixfs.Wrap(true)}, + }, + { + name: "twoLevelInlineHash", + data: twoLevelDir(), + wrap: "t", + expect: wrapped("t"), + path: "/ipfs/zBunoruKoyCHKkALNSWxDvj4L7yuQnMgQ4hUa9j1Z64tVcDEcu6Zdetyu7eeFCxMPfxb7YJvHeFHoFoHMkBUQf6vfdhmi", + opts: []options.UnixfsAddOption{options.Unixfs.Wrap(true), options.Unixfs.Inline(true), options.Unixfs.RawLeaves(true), options.Unixfs.Hash(mh.SHA3)}, + }, + // hidden + { + name: "hiddenFiles", + data: func() files.Node { + return files.NewMapDirectory(map[string]files.Node{ + ".bar": files.NewBytesFile([]byte("hello2")), + "bar": files.NewBytesFile([]byte("hello2")), + "foo": files.NewBytesFile([]byte("hello1")), + }) + }, + wrap: "t", + path: "/ipfs/QmehGvpf2hY196MzDFmjL8Wy27S4jbgGDUAhBJyvXAwr3g", + opts: []options.UnixfsAddOption{options.Unixfs.Hidden(true)}, + }, + { + name: "hiddenFileAlwaysAdded", + data: func() files.Node { + return files.NewBytesFile([]byte(helloStr)) + }, + wrap: ".foo", + path: hello, + }, + { + name: "hiddenFilesNotAdded", + data: func() files.Node { + return files.NewMapDirectory(map[string]files.Node{ + ".bar": files.NewBytesFile([]byte("hello2")), + "bar": files.NewBytesFile([]byte("hello2")), + "foo": files.NewBytesFile([]byte("hello1")), + }) + }, + expect: func(files.Node) files.Node { + return flatDir() + }, + wrap: "t", + path: "/ipfs/QmRKGpFfR32FVXdvJiHfo4WJ5TDYBsM1P9raAp1p6APWSp", + opts: []options.UnixfsAddOption{options.Unixfs.Hidden(false)}, + }, + // Events / Progress + { + name: "simpleAddEvent", + data: strFile(helloStr), + path: "/ipfs/zb2rhdhmJjJZs9qkhQCpCQ7VREFkqWw3h1r8utjVvQugwHPFd", + events: []coreiface.AddEvent{ + {Name: "zb2rhdhmJjJZs9qkhQCpCQ7VREFkqWw3h1r8utjVvQugwHPFd", Path: p("zb2rhdhmJjJZs9qkhQCpCQ7VREFkqWw3h1r8utjVvQugwHPFd"), Size: strconv.Itoa(len(helloStr))}, + }, + opts: []options.UnixfsAddOption{options.Unixfs.RawLeaves(true)}, + }, + { + name: "silentAddEvent", + data: twoLevelDir(), + path: "/ipfs/QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr", + events: []coreiface.AddEvent{ + {Name: "t/abc", Path: p("QmU7nuGs2djqK99UNsNgEPGh6GV4662p6WtsgccBNGTDxt"), Size: "62"}, + {Name: "t", Path: p("QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr"), Size: "229"}, + }, + wrap: "t", + opts: []options.UnixfsAddOption{options.Unixfs.Silent(true)}, + }, + { + name: "dirAddEvents", + data: twoLevelDir(), + path: "/ipfs/QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr", + events: []coreiface.AddEvent{ + {Name: "t/abc/def", Path: p("QmNyJpQkU1cEkBwMDhDNFstr42q55mqG5GE5Mgwug4xyGk"), Size: "13"}, + {Name: "t/bar", Path: p("QmS21GuXiRMvJKHos4ZkEmQDmRBqRaF5tQS2CQCu2ne9sY"), Size: "14"}, + {Name: "t/foo", Path: p("QmfAjGiVpTN56TXi6SBQtstit5BEw3sijKj1Qkxn6EXKzJ"), Size: "14"}, + {Name: "t/abc", Path: p("QmU7nuGs2djqK99UNsNgEPGh6GV4662p6WtsgccBNGTDxt"), Size: "62"}, + {Name: "t", Path: p("QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr"), Size: "229"}, + }, + wrap: "t", + }, + { + name: "progress1M", + data: func() files.Node { + return files.NewReaderFile(bytes.NewReader(bytes.Repeat([]byte{0}, 1000000))) + }, + path: "/ipfs/QmXXNNbwe4zzpdMg62ZXvnX1oU7MwSrQ3vAEtuwFKCm1oD", + events: []coreiface.AddEvent{ + {Name: "", Bytes: 262144}, + {Name: "", Bytes: 524288}, + {Name: "", Bytes: 786432}, + {Name: "", Bytes: 1000000}, + {Name: "QmXXNNbwe4zzpdMg62ZXvnX1oU7MwSrQ3vAEtuwFKCm1oD", Path: p("QmXXNNbwe4zzpdMg62ZXvnX1oU7MwSrQ3vAEtuwFKCm1oD"), Size: "1000256"}, + }, + wrap: "", + opts: []options.UnixfsAddOption{options.Unixfs.Progress(true)}, + }, + } + + for _, testCase := range cases { + t.Run(testCase.name, func(t *testing.T) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + // recursive logic + + data := testCase.data() + if testCase.wrap != "" { + data = files.NewMapDirectory(map[string]files.Node{ + testCase.wrap: data, + }) + } + + // handle events if relevant to test case + + opts := testCase.opts + eventOut := make(chan interface{}) + var evtWg sync.WaitGroup + if len(testCase.events) > 0 { + opts = append(opts, options.Unixfs.Events(eventOut)) + evtWg.Add(1) + + go func() { + defer evtWg.Done() + expected := testCase.events + + for evt := range eventOut { + event, ok := evt.(*coreiface.AddEvent) + if !ok { + t.Fatal("unexpected event type") + } + + if len(expected) < 1 { + t.Fatal("got more events than expected") + } + + if expected[0].Size != event.Size { + t.Errorf("Event.Size didn't match, %s != %s", expected[0].Size, event.Size) + } + + if expected[0].Name != event.Name { + t.Errorf("Event.Name didn't match, %s != %s", expected[0].Name, event.Name) + } + + if expected[0].Path != nil && event.Path != nil { + if expected[0].Path.Cid().String() != event.Path.Cid().String() { + t.Errorf("Event.Hash didn't match, %s != %s", expected[0].Path, event.Path) + } + } else if event.Path != expected[0].Path { + t.Errorf("Event.Hash didn't match, %s != %s", expected[0].Path, event.Path) + } + if expected[0].Bytes != event.Bytes { + t.Errorf("Event.Bytes didn't match, %d != %d", expected[0].Bytes, event.Bytes) + } + + expected = expected[1:] + } + + if len(expected) > 0 { + t.Fatalf("%d event(s) didn't arrive", len(expected)) + } + }() + } + + tapi, err := api.WithOptions(testCase.apiOpts...) + if err != nil { + t.Fatal(err) + } + + // Add! + + p, err := tapi.Unixfs().Add(ctx, data, opts...) + close(eventOut) + evtWg.Wait() + if testCase.err != "" { + if err == nil { + t.Fatalf("expected an error: %s", testCase.err) + } + if err.Error() != testCase.err { + t.Fatalf("expected an error: '%s' != '%s'", err.Error(), testCase.err) + } + return + } + if err != nil { + t.Fatal(err) + } + + if p.String() != testCase.path { + t.Errorf("expected path %s, got: %s", testCase.path, p) + } + + // compare file structure with Unixfs().Get + + var cmpFile func(origName string, orig files.Node, gotName string, got files.Node) + cmpFile = func(origName string, orig files.Node, gotName string, got files.Node) { + _, origDir := orig.(files.Directory) + _, gotDir := got.(files.Directory) + + if origDir != gotDir { + t.Fatal("file type mismatch") + } + + if origName != gotName { + t.Errorf("file name mismatch, orig='%s', got='%s'", origName, gotName) + } + + if !gotDir { + defer orig.Close() + defer got.Close() + + do, err := ioutil.ReadAll(orig.(files.File)) + if err != nil { + t.Fatal(err) + } + + dg, err := ioutil.ReadAll(got.(files.File)) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(do, dg) { + t.Fatal("data not equal") + } + + return + } + + origIt := orig.(files.Directory).Entries() + gotIt := got.(files.Directory).Entries() + + for { + if origIt.Next() { + if !gotIt.Next() { + t.Fatal("gotIt out of entries before origIt") + } + } else { + if gotIt.Next() { + t.Fatal("origIt out of entries before gotIt") + } + break + } + + cmpFile(origIt.Name(), origIt.Node(), gotIt.Name(), gotIt.Node()) + } + if origIt.Err() != nil { + t.Fatal(origIt.Err()) + } + if gotIt.Err() != nil { + t.Fatal(gotIt.Err()) + } + } + + f, err := tapi.Unixfs().Get(ctx, p) + if err != nil { + t.Fatal(err) + } + + orig := testCase.data() + if testCase.expect != nil { + orig = testCase.expect(orig) + } + + cmpFile("", orig, "", f) + }) + } +} + +func TestAddPinned(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Unixfs().Add(ctx, strFile(helloStr)(), options.Unixfs.Pin(true)) + if err != nil { + t.Error(err) + } + + pins, err := api.Pin().Ls(ctx) + if len(pins) != 1 { + t.Fatalf("expected 1 pin, got %d", len(pins)) + } + + if pins[0].Path().String() != "/ipld/QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk" { + t.Fatalf("got unexpected pin: %s", pins[0].Path().String()) + } +} + +func TestAddHashOnly(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + p, err := api.Unixfs().Add(ctx, strFile(helloStr)(), options.Unixfs.HashOnly(true)) + if err != nil { + t.Error(err) + } + + if p.String() != hello { + t.Errorf("unxepected path: %s", p.String()) + } + + _, err = api.Block().Get(ctx, p) + if err == nil { + t.Fatal("expected an error") + } + if err.Error() != "blockservice: key not found" { + t.Errorf("unxepected error: %s", err.Error()) + } +} + +func TestGetEmptyFile(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + _, err = api.Unixfs().Add(ctx, files.NewBytesFile([]byte{})) + if err != nil { + t.Fatal(err) + } + + emptyFilePath, err := coreiface.ParsePath(emptyFile) + if err != nil { + t.Fatal(err) + } + + r, err := api.Unixfs().Get(ctx, emptyFilePath) + if err != nil { + t.Fatal(err) + } + + buf := make([]byte, 1) // non-zero so that Read() actually tries to read + n, err := io.ReadFull(r.(files.File), buf) + if err != nil && err != io.EOF { + t.Error(err) + } + if !bytes.HasPrefix(buf, []byte{0x00}) { + t.Fatalf("expected empty data, got [%s] [read=%d]", buf, n) + } +} + +func TestGetDir(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + edir := unixfs.EmptyDirNode() + _, err = api.Dag().Put(ctx, bytes.NewReader(edir.RawData()), options.Dag.Codec(cid.DagProtobuf), options.Dag.InputEnc("raw")) + if err != nil { + t.Error(err) + } + p := coreiface.IpfsPath(edir.Cid()) + + emptyDir, err := api.Object().New(ctx, options.Object.Type("unixfs-dir")) + if err != nil { + t.Error(err) + } + + if p.String() != coreiface.IpfsPath(emptyDir.Cid()).String() { + t.Fatalf("expected path %s, got: %s", emptyDir.Cid(), p.String()) + } + + r, err := api.Unixfs().Get(ctx, coreiface.IpfsPath(emptyDir.Cid())) + if err != nil { + t.Error(err) + } + + if _, ok := r.(files.Directory); !ok { + t.Fatalf("expected a directory") + } +} + +func TestGetNonUnixfs(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + nd := new(mdag.ProtoNode) + _, err = api.Dag().Put(ctx, bytes.NewReader(nd.RawData()), options.Dag.Codec(nd.CidBuilder().GetCodec()), options.Dag.InputEnc("raw")) + if err != nil { + t.Error(err) + } + + _, err = api.Unixfs().Get(ctx, coreiface.IpfsPath(nd.Cid())) + if !strings.Contains(err.Error(), "proto: required field") { + t.Fatalf("expected protobuf error, got: %s", err) + } +} + +func TestLs(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + r := strings.NewReader("content-of-file") + p, err := api.Unixfs().Add(ctx, files.NewMapDirectory(map[string]files.Node{ + "0": files.NewMapDirectory(map[string]files.Node{ + "name-of-file": files.NewReaderFile(r), + }), + })) + if err != nil { + t.Error(err) + } + + links, err := api.Unixfs().Ls(ctx, p) + if err != nil { + t.Error(err) + } + + if len(links) != 1 { + t.Fatalf("expected 1 link, got %d", len(links)) + } + if links[0].Size != 23 { + t.Fatalf("expected size = 23, got %d", links[0].Size) + } + if links[0].Name != "name-of-file" { + t.Fatalf("expected name = name-of-file, got %s", links[0].Name) + } + if links[0].Cid.String() != "QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr" { + t.Fatalf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", links[0].Cid) + } +} + +func TestEntriesExpired(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + r := strings.NewReader("content-of-file") + p, err := api.Unixfs().Add(ctx, files.NewMapDirectory(map[string]files.Node{ + "0": files.NewMapDirectory(map[string]files.Node{ + "name-of-file": files.NewReaderFile(r), + }), + })) + if err != nil { + t.Error(err) + } + + ctx, cancel := context.WithCancel(ctx) + + nd, err := api.Unixfs().Get(ctx, p) + if err != nil { + t.Error(err) + } + cancel() + + it := files.ToDir(nd).Entries() + if it == nil { + t.Fatal("it was nil") + } + + if it.Next() { + t.Fatal("Next succeeded") + } + + if it.Err() != context.Canceled { + t.Fatalf("unexpected error %s", it.Err()) + } + + if it.Next() { + t.Fatal("Next succeeded") + } +} + +func TestLsEmptyDir(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Unixfs().Add(ctx, files.NewMapDirectory(map[string]files.Node{"0": files.NewSliceDirectory([]files.DirEntry{})})) + if err != nil { + t.Error(err) + } + + emptyDir, err := api.Object().New(ctx, options.Object.Type("unixfs-dir")) + if err != nil { + t.Error(err) + } + + links, err := api.Unixfs().Ls(ctx, coreiface.IpfsPath(emptyDir.Cid())) + if err != nil { + t.Error(err) + } + + if len(links) != 0 { + t.Fatalf("expected 0 links, got %d", len(links)) + } +} + +// TODO(lgierth) this should test properly, with len(links) > 0 +func TestLsNonUnixfs(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + nd, err := cbor.WrapObject(map[string]interface{}{"foo": "bar"}, math.MaxUint64, -1) + if err != nil { + t.Fatal(err) + } + + _, err = api.Dag().Put(ctx, bytes.NewReader(nd.RawData()), options.Dag.Codec(cid.DagCBOR), options.Dag.InputEnc("raw")) + if err != nil { + t.Error(err) + } + + links, err := api.Unixfs().Ls(ctx, coreiface.IpfsPath(nd.Cid())) + if err != nil { + t.Error(err) + } + + if len(links) != 0 { + t.Fatalf("expected 0 links, got %d", len(links)) + } +} + +type closeTestF struct { + files.File + closed bool + + t *testing.T +} + +type closeTestD struct { + files.Directory + closed bool + + t *testing.T +} + +func (f *closeTestD) Close() error { + if f.closed { + f.t.Fatal("already closed") + } + f.closed = true + return nil +} + +func (f *closeTestF) Close() error { + if f.closed { + f.t.Fatal("already closed") + } + f.closed = true + return nil +} + +func TestAddCloses(t *testing.T) { + ctx := context.Background() + api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + n4 := &closeTestF{files.NewBytesFile([]byte("foo")), false, t} + d3 := &closeTestD{files.NewMapDirectory(map[string]files.Node{ + "sub": n4, + }), false, t} + n2 := &closeTestF{files.NewBytesFile([]byte("bar")), false, t} + n1 := &closeTestF{files.NewBytesFile([]byte("baz")), false, t} + d0 := &closeTestD{files.NewMapDirectory(map[string]files.Node{ + "a": d3, + "b": n1, + "c": n2, + }), false, t} + + _, err = api.Unixfs().Add(ctx, d0) + if err != nil { + t.Error(err) + } + + d0.Close() // Adder doesn't close top-level file + + for i, n := range []*closeTestF{n1, n2, n4} { + if !n.closed { + t.Errorf("file %d not closed!", i) + } + } + + for i, n := range []*closeTestD{d0, d3} { + if !n.closed { + t.Errorf("dir %d not closed!", i) + } + } + +} From 6649a031fab719883b2f507b4191538d61d183bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 Dec 2018 20:11:37 +0100 Subject: [PATCH 2836/3817] coreapi: run tests from interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@88e58e6b286d55882958c68525d69c4a3dd213b8 --- coreiface/tests/api.go | 129 ++++++++++++++++++ coreiface/tests/{block_test.go => block.go} | 11 +- coreiface/tests/{dag_test.go => dag.go} | 12 +- coreiface/tests/{dht_test.go => dht.go} | 8 +- coreiface/tests/{key_test.go => key.go} | 19 ++- coreiface/tests/{name_test.go => name.go} | 7 +- coreiface/tests/{object_test.go => object.go} | 17 ++- coreiface/tests/{path_test.go => path.go} | 10 +- coreiface/tests/{pin_test.go => pin.go} | 8 +- coreiface/tests/{pubsub_test.go => pubsub.go} | 6 +- coreiface/tests/{unixfs_test.go => unixfs.go} | 123 +++-------------- 11 files changed, 233 insertions(+), 117 deletions(-) create mode 100644 coreiface/tests/api.go rename coreiface/tests/{block_test.go => block.go} (92%) rename coreiface/tests/{dag_test.go => dag.go} (92%) rename coreiface/tests/{dht_test.go => dht.go} (93%) rename coreiface/tests/{key_test.go => key.go} (93%) rename coreiface/tests/{name_test.go => name.go} (97%) rename coreiface/tests/{object_test.go => object.go} (93%) rename coreiface/tests/{path_test.go => path.go} (91%) rename coreiface/tests/{pin_test.go => pin.go} (95%) rename coreiface/tests/{pubsub_test.go => pubsub.go} (95%) rename coreiface/tests/{unixfs_test.go => unixfs.go} (89%) diff --git a/coreiface/tests/api.go b/coreiface/tests/api.go new file mode 100644 index 000000000..8baa869dd --- /dev/null +++ b/coreiface/tests/api.go @@ -0,0 +1,129 @@ +package tests + +import ( + "context" + "encoding/base64" + "fmt" + "testing" + + "github.com/ipfs/go-ipfs/core" + "github.com/ipfs/go-ipfs/core/coreapi" + coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" + mock "github.com/ipfs/go-ipfs/core/mock" + "github.com/ipfs/go-ipfs/keystore" + "github.com/ipfs/go-ipfs/repo" + + ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" + "gx/ipfs/QmRBaUEQEeFWywfrZJ64QgsmvcqgLSK3VbvGMR2NM2Edpf/go-libp2p/p2p/net/mock" + "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + pstore "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore" + "gx/ipfs/QmcZfkbgwwwH5ZLTQRHkSQBDiDqd3skY2eU6MZRgWuXcse/go-ipfs-config" + "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" + syncds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" +) + + +func makeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]coreiface.CoreAPI, error) { + mn := mocknet.New(ctx) + + nodes := make([]*core.IpfsNode, n) + apis := make([]coreiface.CoreAPI, n) + + for i := 0; i < n; i++ { + var ident config.Identity + if fullIdentity { + sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) + if err != nil { + return nil, err + } + + id, err := peer.IDFromPublicKey(pk) + if err != nil { + return nil, err + } + + kbytes, err := sk.Bytes() + if err != nil { + return nil, err + } + + ident = config.Identity{ + PeerID: id.Pretty(), + PrivKey: base64.StdEncoding.EncodeToString(kbytes), + } + } else { + ident = config.Identity{ + PeerID: testPeerID, + } + } + + c := config.Config{} + c.Addresses.Swarm = []string{fmt.Sprintf("/ip4/127.0.%d.1/tcp/4001", i)} + c.Identity = ident + + r := &repo.Mock{ + C: c, + D: syncds.MutexWrap(datastore.NewMapDatastore()), + K: keystore.NewMemKeystore(), + } + + node, err := core.NewNode(ctx, &core.BuildCfg{ + Repo: r, + Host: mock.MockHostOption(mn), + Online: fullIdentity, + ExtraOpts: map[string]bool{ + "pubsub": true, + }, + }) + if err != nil { + return nil, err + } + nodes[i] = node + apis[i], err = coreapi.NewCoreAPI(node) + if err != nil { + return nil, err + } + } + + err := mn.LinkAll() + if err != nil { + return nil, err + } + + bsinf := core.BootstrapConfigWithPeers( + []pstore.PeerInfo{ + nodes[0].Peerstore.PeerInfo(nodes[0].Identity), + }, + ) + + for _, n := range nodes[1:] { + if err := n.Bootstrap(bsinf); err != nil { + return nil, err + } + } + + return apis, nil +} + +func makeAPI(ctx context.Context) (coreiface.CoreAPI, error) { + api, err := makeAPISwarm(ctx, false, 1) + if err != nil { + return nil, err + } + + return api[0], nil +} + + +func TestApi(t *testing.T) { + t.Run("Block", TestBlock) + t.Run("TestDag", TestDag) + t.Run("TestDht", TestDht) + t.Run("TestKey", TestKey) + t.Run("TestName", TestName) + t.Run("TestObject", TestObject) + t.Run("TestPath", TestPath) + t.Run("TestPin", TestPin) + t.Run("TestPubSub", TestPubSub) + t.Run("TestUnixfs", TestUnixfs) +} diff --git a/coreiface/tests/block_test.go b/coreiface/tests/block.go similarity index 92% rename from coreiface/tests/block_test.go rename to coreiface/tests/block.go index 81360b150..07679a926 100644 --- a/coreiface/tests/block_test.go +++ b/coreiface/tests/block.go @@ -1,4 +1,4 @@ -package tests_test +package tests import ( "context" @@ -12,6 +12,15 @@ import ( mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) +func TestBlock(t *testing.T) { + t.Run("TestBlockPut", TestBlockPut) + t.Run("TestBlockPutFormat", TestBlockPutFormat) + t.Run("TestBlockPutHash", TestBlockPutHash) + t.Run("TestBlockGet", TestBlockGet) + t.Run("TestBlockRm", TestBlockRm) + t.Run("TestBlockStat", TestBlockStat) +} + func TestBlockPut(t *testing.T) { ctx := context.Background() api, err := makeAPI(ctx) diff --git a/coreiface/tests/dag_test.go b/coreiface/tests/dag.go similarity index 92% rename from coreiface/tests/dag_test.go rename to coreiface/tests/dag.go index 17059192b..a75438ab1 100644 --- a/coreiface/tests/dag_test.go +++ b/coreiface/tests/dag.go @@ -1,4 +1,4 @@ -package tests_test +package tests import ( "context" @@ -12,6 +12,14 @@ import ( mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) +func TestDag(t *testing.T) { + t.Run("TestPut", TestPut) + t.Run("TestPutWithHash", TestPutWithHash) + t.Run("TestPath", TestDagPath) + t.Run("TestTree", TestTree) + t.Run("TestBatch", TestBatch) +} + var ( treeExpected = map[string]struct{}{ "a": {}, @@ -56,7 +64,7 @@ func TestPutWithHash(t *testing.T) { } } -func TestPath(t *testing.T) { +func TestDagPath(t *testing.T) { ctx := context.Background() api, err := makeAPI(ctx) if err != nil { diff --git a/coreiface/tests/dht_test.go b/coreiface/tests/dht.go similarity index 93% rename from coreiface/tests/dht_test.go rename to coreiface/tests/dht.go index be16bb083..429197f70 100644 --- a/coreiface/tests/dht_test.go +++ b/coreiface/tests/dht.go @@ -1,4 +1,4 @@ -package tests_test +package tests import ( "context" @@ -8,6 +8,12 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) +func TestDht(t *testing.T) { + t.Run("TestDhtFindPeer", TestDhtFindPeer) + t.Run("TestDhtFindProviders", TestDhtFindProviders) + t.Run("TestDhtProvide", TestDhtProvide) +} + func TestDhtFindPeer(t *testing.T) { ctx := context.Background() apis, err := makeAPISwarm(ctx, true, 5) diff --git a/coreiface/tests/key_test.go b/coreiface/tests/key.go similarity index 93% rename from coreiface/tests/key_test.go rename to coreiface/tests/key.go index 21884e448..b08f56a4f 100644 --- a/coreiface/tests/key_test.go +++ b/coreiface/tests/key.go @@ -1,4 +1,4 @@ -package tests_test +package tests import ( "context" @@ -8,6 +8,23 @@ import ( opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) +func TestKey(t *testing.T) { + t.Run("TestListSelf", TestListSelf) + t.Run("TestRenameSelf", TestRenameSelf) + t.Run("TestRemoveSelf", TestRemoveSelf) + t.Run("TestGenerateSize", TestGenerateSize) + t.Run("TestGenerateExisting", TestGenerateExisting) + t.Run("TestList", TestList) + t.Run("TestRename", TestRename) + t.Run("TestRenameToSelf", TestRenameToSelf) + t.Run("TestRenameToSelfForce", TestRenameToSelfForce) + t.Run("TestRenameOverwriteNoForce", TestRenameOverwriteNoForce) + t.Run("TestRenameOverwrite", TestRenameOverwrite) + t.Run("TestRenameSameNameNoForce", TestRenameSameNameNoForce) + t.Run("TestRenameSameName", TestRenameSameName) + t.Run("TestRemove", TestRemove) +} + func TestListSelf(t *testing.T) { ctx := context.Background() api, err := makeAPI(ctx) diff --git a/coreiface/tests/name_test.go b/coreiface/tests/name.go similarity index 97% rename from coreiface/tests/name_test.go rename to coreiface/tests/name.go index a3514e051..154c1d444 100644 --- a/coreiface/tests/name_test.go +++ b/coreiface/tests/name.go @@ -1,4 +1,4 @@ -package tests_test +package tests import ( "context" @@ -15,6 +15,11 @@ import ( opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) +func TestName(t *testing.T) { + t.Run("TestPublishResolve", TestPublishResolve) + t.Run("TestBasicPublishResolveKey", TestBasicPublishResolveKey) +} + var rnd = rand.New(rand.NewSource(0x62796532303137)) func addTestObject(ctx context.Context, api coreiface.CoreAPI) (coreiface.Path, error) { diff --git a/coreiface/tests/object_test.go b/coreiface/tests/object.go similarity index 93% rename from coreiface/tests/object_test.go rename to coreiface/tests/object.go index ac9e1d5f3..7d4243bca 100644 --- a/coreiface/tests/object_test.go +++ b/coreiface/tests/object.go @@ -1,4 +1,4 @@ -package tests_test +package tests import ( "bytes" @@ -12,6 +12,21 @@ import ( opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) +func TestObject(t *testing.T) { + t.Run("TestNew", TestNew) + t.Run("TestObjectPut", TestObjectPut) + t.Run("TestObjectGet", TestObjectGet) + t.Run("TestObjectData", TestObjectData) + t.Run("TestObjectLinks", TestObjectLinks) + t.Run("TestObjectStat", TestObjectStat) + t.Run("TestObjectAddLink", TestObjectAddLink) + t.Run("TestObjectAddLinkCreate", TestObjectAddLinkCreate) + t.Run("TestObjectRmLink", TestObjectRmLink) + t.Run("TestObjectAddData", TestObjectAddData) + t.Run("TestObjectSetData", TestObjectSetData) + t.Run("TestDiffTest", TestDiffTest) +} + func TestNew(t *testing.T) { ctx := context.Background() api, err := makeAPI(ctx) diff --git a/coreiface/tests/path_test.go b/coreiface/tests/path.go similarity index 91% rename from coreiface/tests/path_test.go rename to coreiface/tests/path.go index e05428073..efbacd29f 100644 --- a/coreiface/tests/path_test.go +++ b/coreiface/tests/path.go @@ -1,4 +1,4 @@ -package tests_test +package tests import ( "context" @@ -9,6 +9,14 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) +func TestPath(t *testing.T) { + t.Run("TestMutablePath", TestMutablePath) + t.Run("TestPathRemainder", TestPathRemainder) + t.Run("TestEmptyPathRemainder", TestEmptyPathRemainder) + t.Run("TestInvalidPathRemainder", TestInvalidPathRemainder) + t.Run("TestPathRoot", TestPathRoot) +} + func TestMutablePath(t *testing.T) { ctx := context.Background() api, err := makeAPI(ctx) diff --git a/coreiface/tests/pin_test.go b/coreiface/tests/pin.go similarity index 95% rename from coreiface/tests/pin_test.go rename to coreiface/tests/pin.go index 5c4b82bc2..344cd0db7 100644 --- a/coreiface/tests/pin_test.go +++ b/coreiface/tests/pin.go @@ -1,4 +1,4 @@ -package tests_test +package tests import ( "context" @@ -8,6 +8,12 @@ import ( opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) +func TestPin(t *testing.T) { + t.Run("TestPinAdd", TestPinAdd) + t.Run("TestPinSimple", TestPinSimple) + t.Run("TestPinRecursive", TestPinRecursive) +} + func TestPinAdd(t *testing.T) { ctx := context.Background() api, err := makeAPI(ctx) diff --git a/coreiface/tests/pubsub_test.go b/coreiface/tests/pubsub.go similarity index 95% rename from coreiface/tests/pubsub_test.go rename to coreiface/tests/pubsub.go index 19a1eba52..3ecd80274 100644 --- a/coreiface/tests/pubsub_test.go +++ b/coreiface/tests/pubsub.go @@ -1,4 +1,4 @@ -package tests_test +package tests import ( "context" @@ -7,6 +7,10 @@ import ( "time" ) +func TestPubSub(t *testing.T) { + t.Run("TestBasicPubSub", TestBasicPubSub) +} + func TestBasicPubSub(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/coreiface/tests/unixfs_test.go b/coreiface/tests/unixfs.go similarity index 89% rename from coreiface/tests/unixfs_test.go rename to coreiface/tests/unixfs.go index d7ddae963..1ca0b282a 100644 --- a/coreiface/tests/unixfs_test.go +++ b/coreiface/tests/unixfs.go @@ -1,11 +1,8 @@ -package tests_test +package tests import ( "bytes" "context" - "encoding/base64" - "fmt" - "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" "io" "io/ioutil" "math" @@ -15,28 +12,31 @@ import ( "sync" "testing" - "github.com/ipfs/go-ipfs/core" - "github.com/ipfs/go-ipfs/core/coreapi" coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - mock "github.com/ipfs/go-ipfs/core/mock" - "github.com/ipfs/go-ipfs/keystore" - "github.com/ipfs/go-ipfs/repo" - ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - "gx/ipfs/QmRBaUEQEeFWywfrZJ64QgsmvcqgLSK3VbvGMR2NM2Edpf/go-libp2p/p2p/net/mock" + "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" cbor "gx/ipfs/QmRoARq3nkUb13HSKZGepCZSWe5GrVPwx7xURJGZ7KWv9V/go-ipld-cbor" "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files" - "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" - pstore "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore" "gx/ipfs/Qmbvw7kpSM2p6rbQ57WGRhhqNfCiNGW6EKH4xgHLw4bsnB/go-unixfs" - "gx/ipfs/QmcZfkbgwwwH5ZLTQRHkSQBDiDqd3skY2eU6MZRgWuXcse/go-ipfs-config" mdag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" - "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" - syncds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) +func TestUnixfs(t *testing.T) { + t.Run("TestAdd", TestAdd) + t.Run("TestAddPinned", TestAddPinned) + t.Run("TestAddHashOnly", TestAddHashOnly) + t.Run("TestGetEmptyFile", TestGetEmptyFile) + t.Run("TestGetDir", TestGetDir) + t.Run("TestGetNonUnixfs", TestGetNonUnixfs) + t.Run("TestLs", TestLs) + t.Run("TestEntriesExpired", TestEntriesExpired) + t.Run("TestLsEmptyDir", TestLsEmptyDir) + t.Run("TestLsNonUnixfs", TestLsNonUnixfs) + t.Run("TestAddCloses", TestAddCloses) +} + const testPeerID = "QmTFauExutTsy4XP6JbMFcw2Wa9645HJt2bTqL6qYDCKfe" // `echo -n 'hello, world!' | ipfs add` @@ -46,97 +46,6 @@ var helloStr = "hello, world!" // `echo -n | ipfs add` var emptyFile = "/ipfs/QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH" -func makeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]coreiface.CoreAPI, error) { - mn := mocknet.New(ctx) - - nodes := make([]*core.IpfsNode, n) - apis := make([]coreiface.CoreAPI, n) - - for i := 0; i < n; i++ { - var ident config.Identity - if fullIdentity { - sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) - if err != nil { - return nil, err - } - - id, err := peer.IDFromPublicKey(pk) - if err != nil { - return nil, err - } - - kbytes, err := sk.Bytes() - if err != nil { - return nil, err - } - - ident = config.Identity{ - PeerID: id.Pretty(), - PrivKey: base64.StdEncoding.EncodeToString(kbytes), - } - } else { - ident = config.Identity{ - PeerID: testPeerID, - } - } - - c := config.Config{} - c.Addresses.Swarm = []string{fmt.Sprintf("/ip4/127.0.%d.1/tcp/4001", i)} - c.Identity = ident - - r := &repo.Mock{ - C: c, - D: syncds.MutexWrap(datastore.NewMapDatastore()), - K: keystore.NewMemKeystore(), - } - - node, err := core.NewNode(ctx, &core.BuildCfg{ - Repo: r, - Host: mock.MockHostOption(mn), - Online: fullIdentity, - ExtraOpts: map[string]bool{ - "pubsub": true, - }, - }) - if err != nil { - return nil, err - } - nodes[i] = node - apis[i], err = coreapi.NewCoreAPI(node) - if err != nil { - return nil, err - } - } - - err := mn.LinkAll() - if err != nil { - return nil, err - } - - bsinf := core.BootstrapConfigWithPeers( - []pstore.PeerInfo{ - nodes[0].Peerstore.PeerInfo(nodes[0].Identity), - }, - ) - - for _, n := range nodes[1:] { - if err := n.Bootstrap(bsinf); err != nil { - return nil, err - } - } - - return apis, nil -} - -func makeAPI(ctx context.Context) (coreiface.CoreAPI, error) { - api, err := makeAPISwarm(ctx, false, 1) - if err != nil { - return nil, err - } - - return api[0], nil -} - func strFile(data string) func() files.Node { return func() files.Node { return files.NewBytesFile([]byte(data)) From 9e88033a986fbd3454d5b438e3590b871ae0987d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 Dec 2018 21:01:00 +0100 Subject: [PATCH 2837/3817] coreapi: Interface for external test providers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@1532d2602204f7c279b22db1ebcb960f82e67050 --- coreiface/tests/api.go | 134 +++++++------------------------------- coreiface/tests/block.go | 38 +++++------ coreiface/tests/dag.go | 32 ++++----- coreiface/tests/dht.go | 20 +++--- coreiface/tests/key.go | 105 +++++++++++++++-------------- coreiface/tests/name.go | 19 +++--- coreiface/tests/object.go | 74 ++++++++++----------- coreiface/tests/path.go | 32 ++++----- coreiface/tests/pin.go | 20 +++--- coreiface/tests/pubsub.go | 8 +-- coreiface/tests/unixfs.go | 70 ++++++++++---------- 11 files changed, 236 insertions(+), 316 deletions(-) diff --git a/coreiface/tests/api.go b/coreiface/tests/api.go index 8baa869dd..a4b0312f6 100644 --- a/coreiface/tests/api.go +++ b/coreiface/tests/api.go @@ -2,128 +2,42 @@ package tests import ( "context" - "encoding/base64" - "fmt" "testing" - "github.com/ipfs/go-ipfs/core" - "github.com/ipfs/go-ipfs/core/coreapi" coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" - mock "github.com/ipfs/go-ipfs/core/mock" - "github.com/ipfs/go-ipfs/keystore" - "github.com/ipfs/go-ipfs/repo" - - ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - "gx/ipfs/QmRBaUEQEeFWywfrZJ64QgsmvcqgLSK3VbvGMR2NM2Edpf/go-libp2p/p2p/net/mock" - "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" - pstore "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore" - "gx/ipfs/QmcZfkbgwwwH5ZLTQRHkSQBDiDqd3skY2eU6MZRgWuXcse/go-ipfs-config" - "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" - syncds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) - -func makeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]coreiface.CoreAPI, error) { - mn := mocknet.New(ctx) - - nodes := make([]*core.IpfsNode, n) - apis := make([]coreiface.CoreAPI, n) - - for i := 0; i < n; i++ { - var ident config.Identity - if fullIdentity { - sk, pk, err := ci.GenerateKeyPair(ci.RSA, 512) - if err != nil { - return nil, err - } - - id, err := peer.IDFromPublicKey(pk) - if err != nil { - return nil, err - } - - kbytes, err := sk.Bytes() - if err != nil { - return nil, err - } - - ident = config.Identity{ - PeerID: id.Pretty(), - PrivKey: base64.StdEncoding.EncodeToString(kbytes), - } - } else { - ident = config.Identity{ - PeerID: testPeerID, - } - } - - c := config.Config{} - c.Addresses.Swarm = []string{fmt.Sprintf("/ip4/127.0.%d.1/tcp/4001", i)} - c.Identity = ident - - r := &repo.Mock{ - C: c, - D: syncds.MutexWrap(datastore.NewMapDatastore()), - K: keystore.NewMemKeystore(), - } - - node, err := core.NewNode(ctx, &core.BuildCfg{ - Repo: r, - Host: mock.MockHostOption(mn), - Online: fullIdentity, - ExtraOpts: map[string]bool{ - "pubsub": true, - }, - }) - if err != nil { - return nil, err - } - nodes[i] = node - apis[i], err = coreapi.NewCoreAPI(node) - if err != nil { - return nil, err - } - } - - err := mn.LinkAll() +func (tp *provider) makeAPI(ctx context.Context) (coreiface.CoreAPI, error) { + api, err := tp.MakeAPISwarm(ctx, false, 1) if err != nil { return nil, err } - bsinf := core.BootstrapConfigWithPeers( - []pstore.PeerInfo{ - nodes[0].Peerstore.PeerInfo(nodes[0].Identity), - }, - ) - - for _, n := range nodes[1:] { - if err := n.Bootstrap(bsinf); err != nil { - return nil, err - } - } - - return apis, nil + return api[0], nil } -func makeAPI(ctx context.Context) (coreiface.CoreAPI, error) { - api, err := makeAPISwarm(ctx, false, 1) - if err != nil { - return nil, err - } - - return api[0], nil +type Provider interface { + // Make creates n nodes. fullIdentity set to false can be ignored + MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]coreiface.CoreAPI, error) } +type provider struct { + Provider +} -func TestApi(t *testing.T) { - t.Run("Block", TestBlock) - t.Run("TestDag", TestDag) - t.Run("TestDht", TestDht) - t.Run("TestKey", TestKey) - t.Run("TestName", TestName) - t.Run("TestObject", TestObject) - t.Run("TestPath", TestPath) - t.Run("TestPin", TestPin) - t.Run("TestPubSub", TestPubSub) - t.Run("TestUnixfs", TestUnixfs) +func TestApi(p Provider) func(t *testing.T) { + tp := &provider{p} + + return func(t *testing.T) { + t.Run("Block", tp.TestBlock) + t.Run("Dag", tp.TestDag) + t.Run("Dht", tp.TestDht) + t.Run("Key", tp.TestKey) + t.Run("Name", tp.TestName) + t.Run("Object", tp.TestObject) + t.Run("Path", tp.TestPath) + t.Run("Pin", tp.TestPin) + t.Run("PubSub", tp.TestPubSub) + t.Run("Unixfs", tp.TestUnixfs) + } } diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 07679a926..0ee968860 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -12,18 +12,18 @@ import ( mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) -func TestBlock(t *testing.T) { - t.Run("TestBlockPut", TestBlockPut) - t.Run("TestBlockPutFormat", TestBlockPutFormat) - t.Run("TestBlockPutHash", TestBlockPutHash) - t.Run("TestBlockGet", TestBlockGet) - t.Run("TestBlockRm", TestBlockRm) - t.Run("TestBlockStat", TestBlockStat) +func (tp *provider) TestBlock(t *testing.T) { + t.Run("TestBlockPut", tp.TestBlockPut) + t.Run("TestBlockPutFormat", tp.TestBlockPutFormat) + t.Run("TestBlockPutHash", tp.TestBlockPutHash) + t.Run("TestBlockGet", tp.TestBlockGet) + t.Run("TestBlockRm", tp.TestBlockRm) + t.Run("TestBlockStat", tp.TestBlockStat) } -func TestBlockPut(t *testing.T) { +func (tp *provider) TestBlockPut(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -38,9 +38,9 @@ func TestBlockPut(t *testing.T) { } } -func TestBlockPutFormat(t *testing.T) { +func (tp *provider) TestBlockPutFormat(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -55,9 +55,9 @@ func TestBlockPutFormat(t *testing.T) { } } -func TestBlockPutHash(t *testing.T) { +func (tp *provider) TestBlockPutHash(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -72,9 +72,9 @@ func TestBlockPutHash(t *testing.T) { } } -func TestBlockGet(t *testing.T) { +func (tp *provider) TestBlockGet(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -112,9 +112,9 @@ func TestBlockGet(t *testing.T) { } } -func TestBlockRm(t *testing.T) { +func (tp *provider) TestBlockRm(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -165,9 +165,9 @@ func TestBlockRm(t *testing.T) { } } -func TestBlockStat(t *testing.T) { +func (tp *provider) TestBlockStat(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index a75438ab1..f19216221 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -12,12 +12,12 @@ import ( mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) -func TestDag(t *testing.T) { - t.Run("TestPut", TestPut) - t.Run("TestPutWithHash", TestPutWithHash) - t.Run("TestPath", TestDagPath) - t.Run("TestTree", TestTree) - t.Run("TestBatch", TestBatch) +func (tp *provider) TestDag(t *testing.T) { + t.Run("TestPut", tp.TestPut) + t.Run("TestPutWithHash", tp.TestPutWithHash) + t.Run("TestPath", tp.TestDagPath) + t.Run("TestTree", tp.TestTree) + t.Run("TestBatch", tp.TestBatch) } var ( @@ -30,9 +30,9 @@ var ( } ) -func TestPut(t *testing.T) { +func (tp *provider) TestPut(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -47,9 +47,9 @@ func TestPut(t *testing.T) { } } -func TestPutWithHash(t *testing.T) { +func (tp *provider) TestPutWithHash(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -64,9 +64,9 @@ func TestPutWithHash(t *testing.T) { } } -func TestDagPath(t *testing.T) { +func (tp *provider) TestDagPath(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -96,9 +96,9 @@ func TestDagPath(t *testing.T) { } } -func TestTree(t *testing.T) { +func (tp *provider) TestTree(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -125,9 +125,9 @@ func TestTree(t *testing.T) { } } -func TestBatch(t *testing.T) { +func (tp *provider) TestBatch(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } diff --git a/coreiface/tests/dht.go b/coreiface/tests/dht.go index 429197f70..9269bc4c5 100644 --- a/coreiface/tests/dht.go +++ b/coreiface/tests/dht.go @@ -8,15 +8,15 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) -func TestDht(t *testing.T) { - t.Run("TestDhtFindPeer", TestDhtFindPeer) - t.Run("TestDhtFindProviders", TestDhtFindProviders) - t.Run("TestDhtProvide", TestDhtProvide) +func (tp *provider) TestDht(t *testing.T) { + t.Run("TestDhtFindPeer", tp.TestDhtFindPeer) + t.Run("TestDhtFindProviders", tp.TestDhtFindProviders) + t.Run("TestDhtProvide", tp.TestDhtProvide) } -func TestDhtFindPeer(t *testing.T) { +func (tp *provider) TestDhtFindPeer(t *testing.T) { ctx := context.Background() - apis, err := makeAPISwarm(ctx, true, 5) + apis, err := tp.MakeAPISwarm(ctx, true, 5) if err != nil { t.Fatal(err) } @@ -50,9 +50,9 @@ func TestDhtFindPeer(t *testing.T) { } } -func TestDhtFindProviders(t *testing.T) { +func (tp *provider) TestDhtFindProviders(t *testing.T) { ctx := context.Background() - apis, err := makeAPISwarm(ctx, true, 5) + apis, err := tp.MakeAPISwarm(ctx, true, 5) if err != nil { t.Fatal(err) } @@ -79,9 +79,9 @@ func TestDhtFindProviders(t *testing.T) { } } -func TestDhtProvide(t *testing.T) { +func (tp *provider) TestDhtProvide(t *testing.T) { ctx := context.Background() - apis, err := makeAPISwarm(ctx, true, 5) + apis, err := tp.MakeAPISwarm(ctx, true, 5) if err != nil { t.Fatal(err) } diff --git a/coreiface/tests/key.go b/coreiface/tests/key.go index b08f56a4f..c2b892599 100644 --- a/coreiface/tests/key.go +++ b/coreiface/tests/key.go @@ -8,31 +8,38 @@ import ( opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) -func TestKey(t *testing.T) { - t.Run("TestListSelf", TestListSelf) - t.Run("TestRenameSelf", TestRenameSelf) - t.Run("TestRemoveSelf", TestRemoveSelf) - t.Run("TestGenerateSize", TestGenerateSize) - t.Run("TestGenerateExisting", TestGenerateExisting) - t.Run("TestList", TestList) - t.Run("TestRename", TestRename) - t.Run("TestRenameToSelf", TestRenameToSelf) - t.Run("TestRenameToSelfForce", TestRenameToSelfForce) - t.Run("TestRenameOverwriteNoForce", TestRenameOverwriteNoForce) - t.Run("TestRenameOverwrite", TestRenameOverwrite) - t.Run("TestRenameSameNameNoForce", TestRenameSameNameNoForce) - t.Run("TestRenameSameName", TestRenameSameName) - t.Run("TestRemove", TestRemove) +func (tp *provider) TestKey(t *testing.T) { + t.Run("TestListSelf", tp.TestListSelf) + t.Run("TestRenameSelf", tp.TestRenameSelf) + t.Run("TestRemoveSelf", tp.TestRemoveSelf) + t.Run("TestGenerate", tp.TestGenerate) + t.Run("TestGenerateSize", tp.TestGenerateSize) + t.Run("TestGenerateType", tp.TestGenerateType) + t.Run("TestGenerateExisting", tp.TestGenerateExisting) + t.Run("TestList", tp.TestList) + t.Run("TestRename", tp.TestRename) + t.Run("TestRenameToSelf", tp.TestRenameToSelf) + t.Run("TestRenameToSelfForce", tp.TestRenameToSelfForce) + t.Run("TestRenameOverwriteNoForce", tp.TestRenameOverwriteNoForce) + t.Run("TestRenameOverwrite", tp.TestRenameOverwrite) + t.Run("TestRenameSameNameNoForce", tp.TestRenameSameNameNoForce) + t.Run("TestRenameSameName", tp.TestRenameSameName) + t.Run("TestRemove", tp.TestRemove) } -func TestListSelf(t *testing.T) { +func (tp *provider) TestListSelf(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) return } + self, err := api.Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + keys, err := api.Key().List(ctx) if err != nil { t.Fatalf("failed to list keys: %s", err) @@ -48,14 +55,14 @@ func TestListSelf(t *testing.T) { t.Errorf("expected the key to be called 'self', got '%s'", keys[0].Name()) } - if keys[0].Path().String() != "/ipns/"+testPeerID { - t.Errorf("expected the key to have path '/ipns/%s', got '%s'", testPeerID, keys[0].Path().String()) + if keys[0].Path().String() != "/ipns/"+self.ID().Pretty() { + t.Errorf("expected the key to have path '/ipns/%s', got '%s'", self.ID().Pretty(), keys[0].Path().String()) } } -func TestRenameSelf(t *testing.T) { +func (tp *provider) TestRenameSelf(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) return @@ -80,9 +87,9 @@ func TestRenameSelf(t *testing.T) { } } -func TestRemoveSelf(t *testing.T) { +func (tp *provider) TestRemoveSelf(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) return @@ -98,9 +105,9 @@ func TestRemoveSelf(t *testing.T) { } } -func TestGenerate(t *testing.T) { +func (tp *provider) TestGenerate(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -120,9 +127,9 @@ func TestGenerate(t *testing.T) { } } -func TestGenerateSize(t *testing.T) { +func (tp *provider) TestGenerateSize(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -142,11 +149,11 @@ func TestGenerateSize(t *testing.T) { } } -func TestGenerateType(t *testing.T) { +func (tp *provider) TestGenerateType(t *testing.T) { ctx := context.Background() t.Skip("disabled until libp2p/specs#111 is fixed") - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -167,9 +174,9 @@ func TestGenerateType(t *testing.T) { } } -func TestGenerateExisting(t *testing.T) { +func (tp *provider) TestGenerateExisting(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -199,9 +206,9 @@ func TestGenerateExisting(t *testing.T) { } } -func TestList(t *testing.T) { +func (tp *provider) TestList(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -244,9 +251,9 @@ func TestList(t *testing.T) { } } -func TestRename(t *testing.T) { +func (tp *provider) TestRename(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -272,9 +279,9 @@ func TestRename(t *testing.T) { } } -func TestRenameToSelf(t *testing.T) { +func (tp *provider) TestRenameToSelf(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -295,9 +302,9 @@ func TestRenameToSelf(t *testing.T) { } } -func TestRenameToSelfForce(t *testing.T) { +func (tp *provider) TestRenameToSelfForce(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -318,9 +325,9 @@ func TestRenameToSelfForce(t *testing.T) { } } -func TestRenameOverwriteNoForce(t *testing.T) { +func (tp *provider) TestRenameOverwriteNoForce(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -347,9 +354,9 @@ func TestRenameOverwriteNoForce(t *testing.T) { } } -func TestRenameOverwrite(t *testing.T) { +func (tp *provider) TestRenameOverwrite(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -385,9 +392,9 @@ func TestRenameOverwrite(t *testing.T) { } } -func TestRenameSameNameNoForce(t *testing.T) { +func (tp *provider) TestRenameSameNameNoForce(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -413,9 +420,9 @@ func TestRenameSameNameNoForce(t *testing.T) { } } -func TestRenameSameName(t *testing.T) { +func (tp *provider) TestRenameSameName(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -441,9 +448,9 @@ func TestRenameSameName(t *testing.T) { } } -func TestRemove(t *testing.T) { +func (tp *provider) TestRemove(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index 154c1d444..ecb06ddde 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -15,9 +15,10 @@ import ( opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) -func TestName(t *testing.T) { - t.Run("TestPublishResolve", TestPublishResolve) - t.Run("TestBasicPublishResolveKey", TestBasicPublishResolveKey) +func (tp *provider) TestName(t *testing.T) { + t.Run("TestPublishResolve", tp.TestPublishResolve) + t.Run("TestBasicPublishResolveKey", tp.TestBasicPublishResolveKey) + t.Run("TestBasicPublishResolveTimeout", tp.TestBasicPublishResolveTimeout) } var rnd = rand.New(rand.NewSource(0x62796532303137)) @@ -34,10 +35,10 @@ func appendPath(p coreiface.Path, sub string) coreiface.Path { return p } -func TestPublishResolve(t *testing.T) { +func (tp *provider) TestPublishResolve(t *testing.T) { ctx := context.Background() init := func() (coreiface.CoreAPI, coreiface.Path) { - apis, err := makeAPISwarm(ctx, true, 5) + apis, err := tp.MakeAPISwarm(ctx, true, 5) if err != nil { t.Fatal(err) return nil, nil @@ -183,9 +184,9 @@ func TestPublishResolve(t *testing.T) { }) } -func TestBasicPublishResolveKey(t *testing.T) { +func (tp *provider) TestBasicPublishResolveKey(t *testing.T) { ctx := context.Background() - apis, err := makeAPISwarm(ctx, true, 5) + apis, err := tp.MakeAPISwarm(ctx, true, 5) if err != nil { t.Fatal(err) } @@ -224,11 +225,11 @@ func TestBasicPublishResolveKey(t *testing.T) { } } -func TestBasicPublishResolveTimeout(t *testing.T) { +func (tp *provider) TestBasicPublishResolveTimeout(t *testing.T) { t.Skip("ValidTime doesn't appear to work at this time resolution") ctx := context.Background() - apis, err := makeAPISwarm(ctx, true, 5) + apis, err := tp.MakeAPISwarm(ctx, true, 5) if err != nil { t.Fatal(err) } diff --git a/coreiface/tests/object.go b/coreiface/tests/object.go index 7d4243bca..349b4a8f5 100644 --- a/coreiface/tests/object.go +++ b/coreiface/tests/object.go @@ -12,24 +12,24 @@ import ( opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) -func TestObject(t *testing.T) { - t.Run("TestNew", TestNew) - t.Run("TestObjectPut", TestObjectPut) - t.Run("TestObjectGet", TestObjectGet) - t.Run("TestObjectData", TestObjectData) - t.Run("TestObjectLinks", TestObjectLinks) - t.Run("TestObjectStat", TestObjectStat) - t.Run("TestObjectAddLink", TestObjectAddLink) - t.Run("TestObjectAddLinkCreate", TestObjectAddLinkCreate) - t.Run("TestObjectRmLink", TestObjectRmLink) - t.Run("TestObjectAddData", TestObjectAddData) - t.Run("TestObjectSetData", TestObjectSetData) - t.Run("TestDiffTest", TestDiffTest) +func (tp *provider) TestObject(t *testing.T) { + t.Run("TestNew", tp.TestNew) + t.Run("TestObjectPut", tp.TestObjectPut) + t.Run("TestObjectGet", tp.TestObjectGet) + t.Run("TestObjectData", tp.TestObjectData) + t.Run("TestObjectLinks", tp.TestObjectLinks) + t.Run("TestObjectStat", tp.TestObjectStat) + t.Run("TestObjectAddLink", tp.TestObjectAddLink) + t.Run("TestObjectAddLinkCreate", tp.TestObjectAddLinkCreate) + t.Run("TestObjectRmLink", tp.TestObjectRmLink) + t.Run("TestObjectAddData", tp.TestObjectAddData) + t.Run("TestObjectSetData", tp.TestObjectSetData) + t.Run("TestDiffTest", tp.TestDiffTest) } -func TestNew(t *testing.T) { +func (tp *provider) TestNew(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -53,9 +53,9 @@ func TestNew(t *testing.T) { } } -func TestObjectPut(t *testing.T) { +func (tp *provider) TestObjectPut(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -93,9 +93,9 @@ func TestObjectPut(t *testing.T) { } } -func TestObjectGet(t *testing.T) { +func (tp *provider) TestObjectGet(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -115,9 +115,9 @@ func TestObjectGet(t *testing.T) { } } -func TestObjectData(t *testing.T) { +func (tp *provider) TestObjectData(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -142,9 +142,9 @@ func TestObjectData(t *testing.T) { } } -func TestObjectLinks(t *testing.T) { +func (tp *provider) TestObjectLinks(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -177,9 +177,9 @@ func TestObjectLinks(t *testing.T) { } } -func TestObjectStat(t *testing.T) { +func (tp *provider) TestObjectStat(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -224,9 +224,9 @@ func TestObjectStat(t *testing.T) { } } -func TestObjectAddLink(t *testing.T) { +func (tp *provider) TestObjectAddLink(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -264,9 +264,9 @@ func TestObjectAddLink(t *testing.T) { } } -func TestObjectAddLinkCreate(t *testing.T) { +func (tp *provider) TestObjectAddLinkCreate(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -312,9 +312,9 @@ func TestObjectAddLinkCreate(t *testing.T) { } } -func TestObjectRmLink(t *testing.T) { +func (tp *provider) TestObjectRmLink(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -344,9 +344,9 @@ func TestObjectRmLink(t *testing.T) { } } -func TestObjectAddData(t *testing.T) { +func (tp *provider) TestObjectAddData(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -373,9 +373,9 @@ func TestObjectAddData(t *testing.T) { } } -func TestObjectSetData(t *testing.T) { +func (tp *provider) TestObjectSetData(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -402,9 +402,9 @@ func TestObjectSetData(t *testing.T) { } } -func TestDiffTest(t *testing.T) { +func (tp *provider) TestDiffTest(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index efbacd29f..cb1246366 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -9,17 +9,17 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) -func TestPath(t *testing.T) { - t.Run("TestMutablePath", TestMutablePath) - t.Run("TestPathRemainder", TestPathRemainder) - t.Run("TestEmptyPathRemainder", TestEmptyPathRemainder) - t.Run("TestInvalidPathRemainder", TestInvalidPathRemainder) - t.Run("TestPathRoot", TestPathRoot) +func (tp *provider) TestPath(t *testing.T) { + t.Run("TestMutablePath", tp.TestMutablePath) + t.Run("TestPathRemainder", tp.TestPathRemainder) + t.Run("TestEmptyPathRemainder", tp.TestEmptyPathRemainder) + t.Run("TestInvalidPathRemainder", tp.TestInvalidPathRemainder) + t.Run("TestPathRoot", tp.TestPathRoot) } -func TestMutablePath(t *testing.T) { +func (tp *provider) TestMutablePath(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -44,9 +44,9 @@ func TestMutablePath(t *testing.T) { } } -func TestPathRemainder(t *testing.T) { +func (tp *provider) TestPathRemainder(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -71,9 +71,9 @@ func TestPathRemainder(t *testing.T) { } } -func TestEmptyPathRemainder(t *testing.T) { +func (tp *provider) TestEmptyPathRemainder(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -102,9 +102,9 @@ func TestEmptyPathRemainder(t *testing.T) { } } -func TestInvalidPathRemainder(t *testing.T) { +func (tp *provider) TestInvalidPathRemainder(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -125,9 +125,9 @@ func TestInvalidPathRemainder(t *testing.T) { } } -func TestPathRoot(t *testing.T) { +func (tp *provider) TestPathRoot(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index 344cd0db7..8c659ba35 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -8,15 +8,15 @@ import ( opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) -func TestPin(t *testing.T) { - t.Run("TestPinAdd", TestPinAdd) - t.Run("TestPinSimple", TestPinSimple) - t.Run("TestPinRecursive", TestPinRecursive) +func (tp *provider) TestPin(t *testing.T) { + t.Run("TestPinAdd", tp.TestPinAdd) + t.Run("TestPinSimple", tp.TestPinSimple) + t.Run("TestPinRecursive", tp.TestPinRecursive) } -func TestPinAdd(t *testing.T) { +func (tp *provider) TestPinAdd(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -32,9 +32,9 @@ func TestPinAdd(t *testing.T) { } } -func TestPinSimple(t *testing.T) { +func (tp *provider) TestPinSimple(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -81,9 +81,9 @@ func TestPinSimple(t *testing.T) { } } -func TestPinRecursive(t *testing.T) { +func (tp *provider) TestPinRecursive(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } diff --git a/coreiface/tests/pubsub.go b/coreiface/tests/pubsub.go index 3ecd80274..3462b4755 100644 --- a/coreiface/tests/pubsub.go +++ b/coreiface/tests/pubsub.go @@ -7,15 +7,15 @@ import ( "time" ) -func TestPubSub(t *testing.T) { - t.Run("TestBasicPubSub", TestBasicPubSub) +func (tp *provider) TestPubSub(t *testing.T) { + t.Run("TestBasicPubSub", tp.TestBasicPubSub) } -func TestBasicPubSub(t *testing.T) { +func (tp *provider) TestBasicPubSub(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - apis, err := makeAPISwarm(ctx, true, 2) + apis, err := tp.MakeAPISwarm(ctx, true, 2) if err != nil { t.Fatal(err) } diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 1ca0b282a..b31a55d4c 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -23,22 +23,20 @@ import ( mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) -func TestUnixfs(t *testing.T) { - t.Run("TestAdd", TestAdd) - t.Run("TestAddPinned", TestAddPinned) - t.Run("TestAddHashOnly", TestAddHashOnly) - t.Run("TestGetEmptyFile", TestGetEmptyFile) - t.Run("TestGetDir", TestGetDir) - t.Run("TestGetNonUnixfs", TestGetNonUnixfs) - t.Run("TestLs", TestLs) - t.Run("TestEntriesExpired", TestEntriesExpired) - t.Run("TestLsEmptyDir", TestLsEmptyDir) - t.Run("TestLsNonUnixfs", TestLsNonUnixfs) - t.Run("TestAddCloses", TestAddCloses) +func (tp *provider) TestUnixfs(t *testing.T) { + t.Run("TestAdd", tp.TestAdd) + t.Run("TestAddPinned", tp.TestAddPinned) + t.Run("TestAddHashOnly", tp.TestAddHashOnly) + t.Run("TestGetEmptyFile", tp.TestGetEmptyFile) + t.Run("TestGetDir", tp.TestGetDir) + t.Run("TestGetNonUnixfs", tp.TestGetNonUnixfs) + t.Run("TestLs", tp.TestLs) + t.Run("TestEntriesExpired", tp.TestEntriesExpired) + t.Run("TestLsEmptyDir", tp.TestLsEmptyDir) + t.Run("TestLsNonUnixfs", tp.TestLsNonUnixfs) + t.Run("TestAddCloses", tp.TestAddCloses) } -const testPeerID = "QmTFauExutTsy4XP6JbMFcw2Wa9645HJt2bTqL6qYDCKfe" - // `echo -n 'hello, world!' | ipfs add` var hello = "/ipfs/QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk" var helloStr = "hello, world!" @@ -80,9 +78,9 @@ func wrapped(name string) func(f files.Node) files.Node { } } -func TestAdd(t *testing.T) { +func (tp *provider) TestAdd(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -537,9 +535,9 @@ func TestAdd(t *testing.T) { } } -func TestAddPinned(t *testing.T) { +func (tp *provider) TestAddPinned(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -559,9 +557,9 @@ func TestAddPinned(t *testing.T) { } } -func TestAddHashOnly(t *testing.T) { +func (tp *provider) TestAddHashOnly(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -584,9 +582,9 @@ func TestAddHashOnly(t *testing.T) { } } -func TestGetEmptyFile(t *testing.T) { +func (tp *provider) TestGetEmptyFile(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) } @@ -616,9 +614,9 @@ func TestGetEmptyFile(t *testing.T) { } } -func TestGetDir(t *testing.T) { +func (tp *provider) TestGetDir(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -648,9 +646,9 @@ func TestGetDir(t *testing.T) { } } -func TestGetNonUnixfs(t *testing.T) { +func (tp *provider) TestGetNonUnixfs(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -667,9 +665,9 @@ func TestGetNonUnixfs(t *testing.T) { } } -func TestLs(t *testing.T) { +func (tp *provider) TestLs(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -703,9 +701,9 @@ func TestLs(t *testing.T) { } } -func TestEntriesExpired(t *testing.T) { +func (tp *provider) TestEntriesExpired(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -746,9 +744,9 @@ func TestEntriesExpired(t *testing.T) { } } -func TestLsEmptyDir(t *testing.T) { +func (tp *provider) TestLsEmptyDir(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -774,9 +772,9 @@ func TestLsEmptyDir(t *testing.T) { } // TODO(lgierth) this should test properly, with len(links) > 0 -func TestLsNonUnixfs(t *testing.T) { +func (tp *provider) TestLsNonUnixfs(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } @@ -831,9 +829,9 @@ func (f *closeTestF) Close() error { return nil } -func TestAddCloses(t *testing.T) { +func (tp *provider) TestAddCloses(t *testing.T) { ctx := context.Background() - api, err := makeAPI(ctx) + api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) } From 23069a4c459b67e966fa5370e980aaab410e718b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 Dec 2018 22:27:59 +0100 Subject: [PATCH 2838/3817] coreapi: make sure to cancel context in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@aa64f771136226accbcae0213eb929ff79697046 --- coreiface/tests/api.go | 37 +++++++++++++++++++++++++++++- coreiface/tests/block.go | 18 ++++++++++----- coreiface/tests/dag.go | 15 ++++++++---- coreiface/tests/dht.go | 9 +++++--- coreiface/tests/key.go | 48 ++++++++++++++++++++++++++------------- coreiface/tests/name.go | 9 +++++--- coreiface/tests/object.go | 36 +++++++++++++++++++---------- coreiface/tests/path.go | 15 ++++++++---- coreiface/tests/pin.go | 9 +++++--- coreiface/tests/unixfs.go | 35 ++++++++++++++++++---------- 10 files changed, 165 insertions(+), 66 deletions(-) diff --git a/coreiface/tests/api.go b/coreiface/tests/api.go index a4b0312f6..ab1feff5b 100644 --- a/coreiface/tests/api.go +++ b/coreiface/tests/api.go @@ -3,6 +3,7 @@ package tests import ( "context" "testing" + "time" coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" ) @@ -21,12 +22,37 @@ type Provider interface { MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]coreiface.CoreAPI, error) } +func (tp *provider) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]coreiface.CoreAPI, error) { + tp.apis <- 1 + go func() { + <-ctx.Done() + tp.apis <- -1 + }() + + return tp.Provider.MakeAPISwarm(ctx, fullIdentity, n) +} + type provider struct { Provider + + apis chan int } func TestApi(p Provider) func(t *testing.T) { - tp := &provider{p} + running := 1 + apis := make(chan int) + zeroRunning := make(chan struct{}) + go func() { + for i := range apis { + running += i + if running < 1 { + close(zeroRunning) + return + } + } + }() + + tp := &provider{Provider: p, apis: apis} return func(t *testing.T) { t.Run("Block", tp.TestBlock) @@ -39,5 +65,14 @@ func TestApi(p Provider) func(t *testing.T) { t.Run("Pin", tp.TestPin) t.Run("PubSub", tp.TestPubSub) t.Run("Unixfs", tp.TestUnixfs) + + apis <- -1 + t.Run("TestsCancelCtx", func(t *testing.T) { + select { + case <-zeroRunning: + case <-time.After(time.Second): + t.Errorf("%d node(s) not closed", running) + } + }) } } diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 0ee968860..a3a5f93aa 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -22,7 +22,8 @@ func (tp *provider) TestBlock(t *testing.T) { } func (tp *provider) TestBlockPut(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -39,7 +40,8 @@ func (tp *provider) TestBlockPut(t *testing.T) { } func (tp *provider) TestBlockPutFormat(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -56,7 +58,8 @@ func (tp *provider) TestBlockPutFormat(t *testing.T) { } func (tp *provider) TestBlockPutHash(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -73,7 +76,8 @@ func (tp *provider) TestBlockPutHash(t *testing.T) { } func (tp *provider) TestBlockGet(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -113,7 +117,8 @@ func (tp *provider) TestBlockGet(t *testing.T) { } func (tp *provider) TestBlockRm(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -166,7 +171,8 @@ func (tp *provider) TestBlockRm(t *testing.T) { } func (tp *provider) TestBlockStat(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index f19216221..636c38699 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -31,7 +31,8 @@ var ( ) func (tp *provider) TestPut(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -48,7 +49,8 @@ func (tp *provider) TestPut(t *testing.T) { } func (tp *provider) TestPutWithHash(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -65,7 +67,8 @@ func (tp *provider) TestPutWithHash(t *testing.T) { } func (tp *provider) TestDagPath(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -97,7 +100,8 @@ func (tp *provider) TestDagPath(t *testing.T) { } func (tp *provider) TestTree(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -126,7 +130,8 @@ func (tp *provider) TestTree(t *testing.T) { } func (tp *provider) TestBatch(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) diff --git a/coreiface/tests/dht.go b/coreiface/tests/dht.go index 9269bc4c5..9b77f1679 100644 --- a/coreiface/tests/dht.go +++ b/coreiface/tests/dht.go @@ -15,7 +15,8 @@ func (tp *provider) TestDht(t *testing.T) { } func (tp *provider) TestDhtFindPeer(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() apis, err := tp.MakeAPISwarm(ctx, true, 5) if err != nil { t.Fatal(err) @@ -51,7 +52,8 @@ func (tp *provider) TestDhtFindPeer(t *testing.T) { } func (tp *provider) TestDhtFindProviders(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() apis, err := tp.MakeAPISwarm(ctx, true, 5) if err != nil { t.Fatal(err) @@ -80,7 +82,8 @@ func (tp *provider) TestDhtFindProviders(t *testing.T) { } func (tp *provider) TestDhtProvide(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() apis, err := tp.MakeAPISwarm(ctx, true, 5) if err != nil { t.Fatal(err) diff --git a/coreiface/tests/key.go b/coreiface/tests/key.go index c2b892599..99c30c302 100644 --- a/coreiface/tests/key.go +++ b/coreiface/tests/key.go @@ -28,7 +28,8 @@ func (tp *provider) TestKey(t *testing.T) { } func (tp *provider) TestListSelf(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -61,7 +62,8 @@ func (tp *provider) TestListSelf(t *testing.T) { } func (tp *provider) TestRenameSelf(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -88,7 +90,8 @@ func (tp *provider) TestRenameSelf(t *testing.T) { } func (tp *provider) TestRemoveSelf(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -106,7 +109,8 @@ func (tp *provider) TestRemoveSelf(t *testing.T) { } func (tp *provider) TestGenerate(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -128,7 +132,8 @@ func (tp *provider) TestGenerate(t *testing.T) { } func (tp *provider) TestGenerateSize(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -150,7 +155,8 @@ func (tp *provider) TestGenerateSize(t *testing.T) { } func (tp *provider) TestGenerateType(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() t.Skip("disabled until libp2p/specs#111 is fixed") api, err := tp.makeAPI(ctx) @@ -175,7 +181,8 @@ func (tp *provider) TestGenerateType(t *testing.T) { } func (tp *provider) TestGenerateExisting(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -207,7 +214,8 @@ func (tp *provider) TestGenerateExisting(t *testing.T) { } func (tp *provider) TestList(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -252,7 +260,8 @@ func (tp *provider) TestList(t *testing.T) { } func (tp *provider) TestRename(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -280,7 +289,8 @@ func (tp *provider) TestRename(t *testing.T) { } func (tp *provider) TestRenameToSelf(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -303,7 +313,8 @@ func (tp *provider) TestRenameToSelf(t *testing.T) { } func (tp *provider) TestRenameToSelfForce(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -326,7 +337,8 @@ func (tp *provider) TestRenameToSelfForce(t *testing.T) { } func (tp *provider) TestRenameOverwriteNoForce(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -355,7 +367,8 @@ func (tp *provider) TestRenameOverwriteNoForce(t *testing.T) { } func (tp *provider) TestRenameOverwrite(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -393,7 +406,8 @@ func (tp *provider) TestRenameOverwrite(t *testing.T) { } func (tp *provider) TestRenameSameNameNoForce(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -421,7 +435,8 @@ func (tp *provider) TestRenameSameNameNoForce(t *testing.T) { } func (tp *provider) TestRenameSameName(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -449,7 +464,8 @@ func (tp *provider) TestRenameSameName(t *testing.T) { } func (tp *provider) TestRemove(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index ecb06ddde..e114b26de 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -36,7 +36,8 @@ func appendPath(p coreiface.Path, sub string) coreiface.Path { } func (tp *provider) TestPublishResolve(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() init := func() (coreiface.CoreAPI, coreiface.Path) { apis, err := tp.MakeAPISwarm(ctx, true, 5) if err != nil { @@ -185,7 +186,8 @@ func (tp *provider) TestPublishResolve(t *testing.T) { } func (tp *provider) TestBasicPublishResolveKey(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() apis, err := tp.MakeAPISwarm(ctx, true, 5) if err != nil { t.Fatal(err) @@ -228,7 +230,8 @@ func (tp *provider) TestBasicPublishResolveKey(t *testing.T) { func (tp *provider) TestBasicPublishResolveTimeout(t *testing.T) { t.Skip("ValidTime doesn't appear to work at this time resolution") - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() apis, err := tp.MakeAPISwarm(ctx, true, 5) if err != nil { t.Fatal(err) diff --git a/coreiface/tests/object.go b/coreiface/tests/object.go index 349b4a8f5..1cd24aac2 100644 --- a/coreiface/tests/object.go +++ b/coreiface/tests/object.go @@ -28,7 +28,8 @@ func (tp *provider) TestObject(t *testing.T) { } func (tp *provider) TestNew(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -54,7 +55,8 @@ func (tp *provider) TestNew(t *testing.T) { } func (tp *provider) TestObjectPut(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -94,7 +96,8 @@ func (tp *provider) TestObjectPut(t *testing.T) { } func (tp *provider) TestObjectGet(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -116,7 +119,8 @@ func (tp *provider) TestObjectGet(t *testing.T) { } func (tp *provider) TestObjectData(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -143,7 +147,8 @@ func (tp *provider) TestObjectData(t *testing.T) { } func (tp *provider) TestObjectLinks(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -178,7 +183,8 @@ func (tp *provider) TestObjectLinks(t *testing.T) { } func (tp *provider) TestObjectStat(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -225,7 +231,8 @@ func (tp *provider) TestObjectStat(t *testing.T) { } func (tp *provider) TestObjectAddLink(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -265,7 +272,8 @@ func (tp *provider) TestObjectAddLink(t *testing.T) { } func (tp *provider) TestObjectAddLinkCreate(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -313,7 +321,8 @@ func (tp *provider) TestObjectAddLinkCreate(t *testing.T) { } func (tp *provider) TestObjectRmLink(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -345,7 +354,8 @@ func (tp *provider) TestObjectRmLink(t *testing.T) { } func (tp *provider) TestObjectAddData(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -374,7 +384,8 @@ func (tp *provider) TestObjectAddData(t *testing.T) { } func (tp *provider) TestObjectSetData(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -403,7 +414,8 @@ func (tp *provider) TestObjectSetData(t *testing.T) { } func (tp *provider) TestDiffTest(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index cb1246366..e74053c04 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -18,7 +18,8 @@ func (tp *provider) TestPath(t *testing.T) { } func (tp *provider) TestMutablePath(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -45,7 +46,8 @@ func (tp *provider) TestMutablePath(t *testing.T) { } func (tp *provider) TestPathRemainder(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -72,7 +74,8 @@ func (tp *provider) TestPathRemainder(t *testing.T) { } func (tp *provider) TestEmptyPathRemainder(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -103,7 +106,8 @@ func (tp *provider) TestEmptyPathRemainder(t *testing.T) { } func (tp *provider) TestInvalidPathRemainder(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -126,7 +130,8 @@ func (tp *provider) TestInvalidPathRemainder(t *testing.T) { } func (tp *provider) TestPathRoot(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index 8c659ba35..823281ab1 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -15,7 +15,8 @@ func (tp *provider) TestPin(t *testing.T) { } func (tp *provider) TestPinAdd(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -33,7 +34,8 @@ func (tp *provider) TestPinAdd(t *testing.T) { } func (tp *provider) TestPinSimple(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -82,7 +84,8 @@ func (tp *provider) TestPinSimple(t *testing.T) { } func (tp *provider) TestPinRecursive(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index b31a55d4c..f411ad24d 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -79,7 +79,8 @@ func wrapped(name string) func(f files.Node) files.Node { } func (tp *provider) TestAdd(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -536,7 +537,8 @@ func (tp *provider) TestAdd(t *testing.T) { } func (tp *provider) TestAddPinned(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -558,7 +560,8 @@ func (tp *provider) TestAddPinned(t *testing.T) { } func (tp *provider) TestAddHashOnly(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -583,7 +586,8 @@ func (tp *provider) TestAddHashOnly(t *testing.T) { } func (tp *provider) TestGetEmptyFile(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Fatal(err) @@ -615,7 +619,8 @@ func (tp *provider) TestGetEmptyFile(t *testing.T) { } func (tp *provider) TestGetDir(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -647,7 +652,8 @@ func (tp *provider) TestGetDir(t *testing.T) { } func (tp *provider) TestGetNonUnixfs(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -666,7 +672,8 @@ func (tp *provider) TestGetNonUnixfs(t *testing.T) { } func (tp *provider) TestLs(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -702,7 +709,8 @@ func (tp *provider) TestLs(t *testing.T) { } func (tp *provider) TestEntriesExpired(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -718,7 +726,7 @@ func (tp *provider) TestEntriesExpired(t *testing.T) { t.Error(err) } - ctx, cancel := context.WithCancel(ctx) + ctx, cancel = context.WithCancel(ctx) nd, err := api.Unixfs().Get(ctx, p) if err != nil { @@ -745,7 +753,8 @@ func (tp *provider) TestEntriesExpired(t *testing.T) { } func (tp *provider) TestLsEmptyDir(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -773,7 +782,8 @@ func (tp *provider) TestLsEmptyDir(t *testing.T) { // TODO(lgierth) this should test properly, with len(links) > 0 func (tp *provider) TestLsNonUnixfs(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) @@ -830,7 +840,8 @@ func (f *closeTestF) Close() error { } func (tp *provider) TestAddCloses(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() api, err := tp.makeAPI(ctx) if err != nil { t.Error(err) From 27609c7a833ec02858ad16464eb40946f7ddeae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 2 Jan 2019 17:41:12 +0100 Subject: [PATCH 2839/3817] coreapi: don't panic as much in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@89157f98eeb09c4653c098eb4e438741cbdbd21a --- coreiface/tests/api.go | 2 +- coreiface/tests/block.go | 12 ++++++------ coreiface/tests/dag.go | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/coreiface/tests/api.go b/coreiface/tests/api.go index ab1feff5b..23ec0612b 100644 --- a/coreiface/tests/api.go +++ b/coreiface/tests/api.go @@ -71,7 +71,7 @@ func TestApi(p Provider) func(t *testing.T) { select { case <-zeroRunning: case <-time.After(time.Second): - t.Errorf("%d node(s) not closed", running) + t.Errorf("%d test swarms(s) not closed", running) } }) } diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index a3a5f93aa..d1117cc50 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -26,12 +26,12 @@ func (tp *provider) TestBlockPut(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } res, err := api.Block().Put(ctx, strings.NewReader(`Hello`)) if err != nil { - t.Error(err) + t.Fatal(err) } if res.Path().Cid().String() != "QmPyo15ynbVrSTVdJL9th7JysHaAbXt9dM9tXk1bMHbRtk" { @@ -49,7 +49,7 @@ func (tp *provider) TestBlockPutFormat(t *testing.T) { res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("cbor")) if err != nil { - t.Error(err) + t.Fatal(err) } if res.Path().Cid().String() != "zdpuAn4amuLWo8Widi5v6VQpuo2dnpnwbVE3oB6qqs7mDSeoa" { @@ -85,7 +85,7 @@ func (tp *provider) TestBlockGet(t *testing.T) { res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Hash(mh.KECCAK_512, -1)) if err != nil { - t.Error(err) + t.Fatal(err) } r, err := api.Block().Get(ctx, res.Path()) @@ -126,7 +126,7 @@ func (tp *provider) TestBlockRm(t *testing.T) { res, err := api.Block().Put(ctx, strings.NewReader(`Hello`)) if err != nil { - t.Error(err) + t.Fatal(err) } r, err := api.Block().Get(ctx, res.Path()) @@ -180,7 +180,7 @@ func (tp *provider) TestBlockStat(t *testing.T) { res, err := api.Block().Put(ctx, strings.NewReader(`Hello`)) if err != nil { - t.Error(err) + t.Fatal(err) } stat, err := api.Block().Stat(ctx, res.Path()) diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index 636c38699..d50263943 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -40,7 +40,7 @@ func (tp *provider) TestPut(t *testing.T) { res, err := api.Dag().Put(ctx, strings.NewReader(`"Hello"`)) if err != nil { - t.Error(err) + t.Fatal(err) } if res.Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" { @@ -58,7 +58,7 @@ func (tp *provider) TestPutWithHash(t *testing.T) { res, err := api.Dag().Put(ctx, strings.NewReader(`"Hello"`), opt.Dag.Hash(mh.ID, -1)) if err != nil { - t.Error(err) + t.Fatal(err) } if res.Cid().String() != "z5hRLNd2sv4z1c" { @@ -76,12 +76,12 @@ func (tp *provider) TestDagPath(t *testing.T) { sub, err := api.Dag().Put(ctx, strings.NewReader(`"foo"`)) if err != nil { - t.Error(err) + t.Fatal(err) } res, err := api.Dag().Put(ctx, strings.NewReader(`{"lnk": {"/": "`+sub.Cid().String()+`"}}`)) if err != nil { - t.Error(err) + t.Fatal(err) } p, err := coreiface.ParsePath(path.Join(res.Cid().String(), "lnk")) @@ -109,7 +109,7 @@ func (tp *provider) TestTree(t *testing.T) { c, err := api.Dag().Put(ctx, strings.NewReader(`{"a": 123, "b": "foo", "c": {"d": 321, "e": 111}}`)) if err != nil { - t.Error(err) + t.Fatal(err) } res, err := api.Dag().Get(ctx, c) @@ -141,7 +141,7 @@ func (tp *provider) TestBatch(t *testing.T) { c, err := batch.Put(ctx, strings.NewReader(`"Hello"`)) if err != nil { - t.Error(err) + t.Fatal(err) } if c.Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" { From aecc5aa1ac7d14502faf74530e73fb801aed4158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 4 Jan 2019 02:35:40 +0100 Subject: [PATCH 2840/3817] Fix offline gateway directory logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@360e8bbf0b798f4c52541d9116e3b2d240c71a9a --- coreiface/path.go | 9 +++++++-- coreiface/tests/path.go | 12 ++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/coreiface/path.go b/coreiface/path.go index 01dda97d5..96f30852d 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,9 +1,8 @@ package iface import ( + "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipfspath "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" - - cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ) //TODO: merge with ipfspath so we don't depend on it @@ -106,6 +105,12 @@ type resolvedPath struct { remainder string } +// Join appends provided segments to the base path +func Join(base Path, a ...string) Path { + s := ipfspath.Join(append([]string{base.String()}, a...)) + return &path{path: ipfspath.FromString(s)} +} + // IpfsPath creates new /ipfs path from the provided CID func IpfsPath(c cid.Cid) ResolvedPath { return &resolvedPath{ diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index e74053c04..7057d6286 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -15,6 +15,7 @@ func (tp *provider) TestPath(t *testing.T) { t.Run("TestEmptyPathRemainder", tp.TestEmptyPathRemainder) t.Run("TestInvalidPathRemainder", tp.TestInvalidPathRemainder) t.Run("TestPathRoot", tp.TestPathRoot) + t.Run("TestPathJoin", tp.TestPathJoin) } func (tp *provider) TestMutablePath(t *testing.T) { @@ -165,3 +166,14 @@ func (tp *provider) TestPathRoot(t *testing.T) { t.Error("unexpected path cid") } } + +func (tp *provider) TestPathJoin(t *testing.T) { + p1, err := coreiface.ParsePath("/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz") + if err != nil { + t.Error(err) + } + + if coreiface.Join(p1, "foo").String() != "/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz/foo" { + t.Error("unexpected path") + } +} From 030083beef42fd48df466a4a97ea0f51fb1dfbc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 4 Jan 2019 15:40:15 +0100 Subject: [PATCH 2841/3817] coreapi: FetchBlocks option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@6800f56736680add20352d2348a59a1a41f7808e --- coreiface/options/global.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/coreiface/options/global.go b/coreiface/options/global.go index 93d635e41..90e2586f1 100644 --- a/coreiface/options/global.go +++ b/coreiface/options/global.go @@ -1,14 +1,16 @@ package options type ApiSettings struct { - Offline bool + Offline bool + FetchBlocks bool } type ApiOption func(*ApiSettings) error func ApiOptions(opts ...ApiOption) (*ApiSettings, error) { options := &ApiSettings{ - Offline: false, + Offline: false, + FetchBlocks: true, } return ApiOptionsTo(options, opts...) @@ -34,3 +36,12 @@ func (apiOpts) Offline(offline bool) ApiOption { return nil } } + +// FetchBlocks when set to false prevents api from fetching blocks from the +// network while allowing other services such as IPNS to still be online +func (apiOpts) FetchBlocks(fetch bool) ApiOption { + return func(settings *ApiSettings) error { + settings.FetchBlocks = fetch + return nil + } +} From 9845df2662de30cb8d0030bd280e2b58247a0ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 7 Jan 2019 16:19:55 +0100 Subject: [PATCH 2842/3817] CoreAPI: Don't panic when testing incomplete implementions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@d7a89ddb0258cbc54631924688e6f23bc02709d5 --- coreiface/tests/api.go | 16 +++++++++++++++ coreiface/tests/block.go | 7 +++++++ coreiface/tests/dag.go | 7 +++++++ coreiface/tests/dht.go | 8 ++++++++ coreiface/tests/key.go | 8 ++++++++ coreiface/tests/name.go | 7 +++++++ coreiface/tests/object.go | 7 +++++++ coreiface/tests/path.go | 42 ++++++++++++++++++++++++++++++--------- coreiface/tests/pin.go | 8 ++++++++ coreiface/tests/pubsub.go | 8 ++++++++ coreiface/tests/unixfs.go | 7 +++++++ 11 files changed, 116 insertions(+), 9 deletions(-) diff --git a/coreiface/tests/api.go b/coreiface/tests/api.go index 23ec0612b..7a4bd7386 100644 --- a/coreiface/tests/api.go +++ b/coreiface/tests/api.go @@ -2,12 +2,15 @@ package tests import ( "context" + "errors" "testing" "time" coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" ) +var apiNotImplemented = errors.New("api not implemented") + func (tp *provider) makeAPI(ctx context.Context) (coreiface.CoreAPI, error) { api, err := tp.MakeAPISwarm(ctx, false, 1) if err != nil { @@ -76,3 +79,16 @@ func TestApi(p Provider) func(t *testing.T) { }) } } + +func (tp *provider) hasApi(t *testing.T, tf func(coreiface.CoreAPI) error) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + if err := tf(api); err != nil { + t.Fatal(api) + } +} diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index d1117cc50..81a6fb061 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -13,6 +13,13 @@ import ( ) func (tp *provider) TestBlock(t *testing.T) { + tp.hasApi(t, func(api coreiface.CoreAPI) error { + if api.Block() == nil { + return apiNotImplemented + } + return nil + }) + t.Run("TestBlockPut", tp.TestBlockPut) t.Run("TestBlockPutFormat", tp.TestBlockPutFormat) t.Run("TestBlockPutHash", tp.TestBlockPutHash) diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index d50263943..70a45aa20 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -13,6 +13,13 @@ import ( ) func (tp *provider) TestDag(t *testing.T) { + tp.hasApi(t, func(api coreiface.CoreAPI) error { + if api.Dag() == nil { + return apiNotImplemented + } + return nil + }) + t.Run("TestPut", tp.TestPut) t.Run("TestPutWithHash", tp.TestPutWithHash) t.Run("TestPath", tp.TestDagPath) diff --git a/coreiface/tests/dht.go b/coreiface/tests/dht.go index 9b77f1679..3ec77d33b 100644 --- a/coreiface/tests/dht.go +++ b/coreiface/tests/dht.go @@ -5,10 +5,18 @@ import ( "io" "testing" + "github.com/ipfs/go-ipfs/core/coreapi/interface" "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) func (tp *provider) TestDht(t *testing.T) { + tp.hasApi(t, func(api iface.CoreAPI) error { + if api.Dht() == nil { + return apiNotImplemented + } + return nil + }) + t.Run("TestDhtFindPeer", tp.TestDhtFindPeer) t.Run("TestDhtFindProviders", tp.TestDhtFindProviders) t.Run("TestDhtProvide", tp.TestDhtProvide) diff --git a/coreiface/tests/key.go b/coreiface/tests/key.go index 99c30c302..8dd6af57f 100644 --- a/coreiface/tests/key.go +++ b/coreiface/tests/key.go @@ -5,10 +5,18 @@ import ( "strings" "testing" + "github.com/ipfs/go-ipfs/core/coreapi/interface" opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ) func (tp *provider) TestKey(t *testing.T) { + tp.hasApi(t, func(api iface.CoreAPI) error { + if api.Key() == nil { + return apiNotImplemented + } + return nil + }) + t.Run("TestListSelf", tp.TestListSelf) t.Run("TestRenameSelf", tp.TestRenameSelf) t.Run("TestRemoveSelf", tp.TestRemoveSelf) diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index e114b26de..639e72c3d 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -16,6 +16,13 @@ import ( ) func (tp *provider) TestName(t *testing.T) { + tp.hasApi(t, func(api coreiface.CoreAPI) error { + if api.Name() == nil { + return apiNotImplemented + } + return nil + }) + t.Run("TestPublishResolve", tp.TestPublishResolve) t.Run("TestBasicPublishResolveKey", tp.TestBasicPublishResolveKey) t.Run("TestBasicPublishResolveTimeout", tp.TestBasicPublishResolveTimeout) diff --git a/coreiface/tests/object.go b/coreiface/tests/object.go index 1cd24aac2..81d5b4117 100644 --- a/coreiface/tests/object.go +++ b/coreiface/tests/object.go @@ -13,6 +13,13 @@ import ( ) func (tp *provider) TestObject(t *testing.T) { + tp.hasApi(t, func(api iface.CoreAPI) error { + if api.Object() == nil { + return apiNotImplemented + } + return nil + }) + t.Run("TestNew", tp.TestNew) t.Run("TestObjectPut", tp.TestObjectPut) t.Run("TestObjectGet", tp.TestObjectGet) diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index 7057d6286..50a1977d5 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -26,23 +26,27 @@ func (tp *provider) TestMutablePath(t *testing.T) { t.Fatal(err) } - // get self /ipns path - keys, err := api.Key().List(ctx) + blk, err := api.Block().Put(ctx, strings.NewReader(`foo`)) if err != nil { t.Fatal(err) } - if !keys[0].Path().Mutable() { - t.Error("expected self /ipns path to be mutable") + if blk.Path().Mutable() { + t.Error("expected /ipld path to be immutable") } - blk, err := api.Block().Put(ctx, strings.NewReader(`foo`)) + // get self /ipns path + if api.Key() == nil { + t.Fatal(".Key not implemented") + } + + keys, err := api.Key().List(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } - if blk.Path().Mutable() { - t.Error("expected /ipld path to be immutable") + if !keys[0].Path().Mutable() { + t.Error("expected self /ipns path to be mutable") } } @@ -54,6 +58,10 @@ func (tp *provider) TestPathRemainder(t *testing.T) { t.Fatal(err) } + if api.Dag() == nil { + t.Fatal(".Dag not implemented") + } + obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"bar": "baz"}}`)) if err != nil { t.Fatal(err) @@ -82,6 +90,10 @@ func (tp *provider) TestEmptyPathRemainder(t *testing.T) { t.Fatal(err) } + if api.Dag() == nil { + t.Fatal(".Dag not implemented") + } + obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"bar": "baz"}}`)) if err != nil { t.Fatal(err) @@ -114,6 +126,10 @@ func (tp *provider) TestInvalidPathRemainder(t *testing.T) { t.Fatal(err) } + if api.Dag() == nil { + t.Fatal(".Dag not implemented") + } + obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"bar": "baz"}}`)) if err != nil { t.Fatal(err) @@ -138,9 +154,17 @@ func (tp *provider) TestPathRoot(t *testing.T) { t.Fatal(err) } + if api.Block() == nil { + t.Fatal(".Block not implemented") + } + blk, err := api.Block().Put(ctx, strings.NewReader(`foo`), options.Block.Format("raw")) if err != nil { - t.Error(err) + t.Fatal(err) + } + + if api.Dag() == nil { + t.Fatal(".Dag not implemented") } obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"/": "`+blk.Path().Cid().String()+`"}}`)) diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index 823281ab1..87ad8a004 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -2,6 +2,7 @@ package tests import ( "context" + "github.com/ipfs/go-ipfs/core/coreapi/interface" "strings" "testing" @@ -9,6 +10,13 @@ import ( ) func (tp *provider) TestPin(t *testing.T) { + tp.hasApi(t, func(api iface.CoreAPI) error { + if api.Pin() == nil { + return apiNotImplemented + } + return nil + }) + t.Run("TestPinAdd", tp.TestPinAdd) t.Run("TestPinSimple", tp.TestPinSimple) t.Run("TestPinRecursive", tp.TestPinRecursive) diff --git a/coreiface/tests/pubsub.go b/coreiface/tests/pubsub.go index 3462b4755..b993f51dc 100644 --- a/coreiface/tests/pubsub.go +++ b/coreiface/tests/pubsub.go @@ -2,12 +2,20 @@ package tests import ( "context" + "github.com/ipfs/go-ipfs/core/coreapi/interface" "github.com/ipfs/go-ipfs/core/coreapi/interface/options" "testing" "time" ) func (tp *provider) TestPubSub(t *testing.T) { + tp.hasApi(t, func(api iface.CoreAPI) error { + if api.PubSub() == nil { + return apiNotImplemented + } + return nil + }) + t.Run("TestBasicPubSub", tp.TestBasicPubSub) } diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index f411ad24d..ddb3145f7 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -24,6 +24,13 @@ import ( ) func (tp *provider) TestUnixfs(t *testing.T) { + tp.hasApi(t, func(api coreiface.CoreAPI) error { + if api.Unixfs() == nil { + return apiNotImplemented + } + return nil + }) + t.Run("TestAdd", tp.TestAdd) t.Run("TestAddPinned", tp.TestAddPinned) t.Run("TestAddHashOnly", tp.TestAddHashOnly) From 551b466a611343bda0eb37d646ef753153919d2a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 8 Jan 2019 19:19:34 -0800 Subject: [PATCH 2843/3817] gx: update deps Importantly: * fixes a bunch of MFS bugs * pulls in some bitswap improvements License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@e51dd550f4404781592f8d911ba822512039f304 --- coreiface/dht.go | 2 +- coreiface/options/unixfs.go | 2 +- coreiface/path.go | 2 +- coreiface/swarm.go | 6 +++--- coreiface/tests/name.go | 2 +- coreiface/tests/unixfs.go | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/coreiface/dht.go b/coreiface/dht.go index e39be92c5..ec8bd92c3 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -5,8 +5,8 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + pstore "gx/ipfs/QmPiemjiKBC9VA7vZF82m4x1oygtg2c2YVqag8PX7dN1BD/go-libp2p-peerstore" peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" - pstore "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore" ) // DhtAPI specifies the interface to the DHT diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index fd748bb4a..5f92f3eea 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -5,7 +5,7 @@ import ( "fmt" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - dag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" + dag "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) diff --git a/coreiface/path.go b/coreiface/path.go index 96f30852d..580703a73 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,8 +1,8 @@ package iface import ( + ipfspath "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - ipfspath "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" ) //TODO: merge with ipfspath so we don't depend on it diff --git a/coreiface/swarm.go b/coreiface/swarm.go index 63d20f035..83e207282 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -5,10 +5,10 @@ import ( "errors" "time" - net "gx/ipfs/QmPtFaR7BWHLAjSwLh9kXcyrgTzDpuhcWLkx8ioa9RMYnx/go-libp2p-net" - ma "gx/ipfs/QmRKLtwMw131aK7ugC3G7ybpumMz78YrJe5dzneyindvG1/go-multiaddr" + ma "gx/ipfs/QmNTCey11oxhb1AxDnQBRHtdhap6Ctud872NjAYPYYXPuc/go-multiaddr" + net "gx/ipfs/QmNgLg1NTw37iWbYPKcyK85YJ9Whs1MkPtJwhfqbNYAyKg/go-libp2p-net" + pstore "gx/ipfs/QmPiemjiKBC9VA7vZF82m4x1oygtg2c2YVqag8PX7dN1BD/go-libp2p-peerstore" "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" - pstore "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore" "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol" ) diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index 639e72c3d..2e43a12ee 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -8,8 +8,8 @@ import ( "testing" "time" + ipath "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files" - ipath "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index ddb3145f7..e8a1aba32 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -15,11 +15,11 @@ import ( coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + "gx/ipfs/QmQXze9tG878pa4Euya4rrDpyTNX3kQe4dhCaBzBozGgpe/go-unixfs" "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" cbor "gx/ipfs/QmRoARq3nkUb13HSKZGepCZSWe5GrVPwx7xURJGZ7KWv9V/go-ipld-cbor" + mdag "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files" - "gx/ipfs/Qmbvw7kpSM2p6rbQ57WGRhhqNfCiNGW6EKH4xgHLw4bsnB/go-unixfs" - mdag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) From d34b0ea4adb45156bd973dea8923e03eca96dde3 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 8 Jan 2019 19:19:34 -0800 Subject: [PATCH 2844/3817] gx: update deps Importantly: * fixes a bunch of MFS bugs * pulls in some bitswap improvements License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@8f0ee3d5cc4ea9d113661292f14a3bd27e87d6d5 --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 474c7ecfa..a39d6f906 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" + dag "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" posinfo "gx/ipfs/QmR6YMs8EkXQLXNwQKxLnQp2VBZSepoEJ8KCZAyanJHhJu/go-ipfs-posinfo" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" From 3046c1a64078f3c690b346bde5e9c6bb68da7689 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 8 Jan 2019 19:19:34 -0800 Subject: [PATCH 2845/3817] gx: update deps Importantly: * fixes a bunch of MFS bugs * pulls in some bitswap improvements License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@d6ba56e752325a91f2e1a991c137f2f818e84028 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 18 +++++++++--------- namesys/namesys.go | 4 ++-- namesys/namesys_test.go | 10 +++++----- namesys/proquint.go | 2 +- namesys/publisher.go | 10 +++++----- namesys/publisher_test.go | 8 ++++---- namesys/republisher/repub.go | 4 ++-- namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 8 ++++---- namesys/routing.go | 10 +++++----- 14 files changed, 44 insertions(+), 44 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index e956f7653..9fbaa06b9 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" + path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 653580586..51bb0c0b0 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" + path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index b92a78029..526794174 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" + path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index e330a83cb..64690cd3d 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" + path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 840bd064a..181f227f0 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,21 +5,21 @@ import ( "testing" "time" - path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" + path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" - ipns "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns" - testutil "gx/ipfs/QmPuhRE325DR8ChNcFtgd6F1eANCHy1oohXZPpYop4xsK6/go-testutil" - routing "gx/ipfs/QmRASJXJUFygM5qU4YrH7k7jD6S4Hg8nJmgqJ4bYJvLatd/go-libp2p-routing" - ropts "gx/ipfs/QmRASJXJUFygM5qU4YrH7k7jD6S4Hg8nJmgqJ4bYJvLatd/go-libp2p-routing/options" + testutil "gx/ipfs/QmNvHv84aH2qZafDuSdKJCQ1cvPZ1kmQmyD4YtzjUHuk9v/go-testutil" + pstore "gx/ipfs/QmPiemjiKBC9VA7vZF82m4x1oygtg2c2YVqag8PX7dN1BD/go-libp2p-peerstore" + pstoremem "gx/ipfs/QmPiemjiKBC9VA7vZF82m4x1oygtg2c2YVqag8PX7dN1BD/go-libp2p-peerstore/pstoremem" + routing "gx/ipfs/QmTiRqrF5zkdZyrdsL5qndG1UbeWi8k8N2pYxCtXWrahR2/go-libp2p-routing" + ropts "gx/ipfs/QmTiRqrF5zkdZyrdsL5qndG1UbeWi8k8N2pYxCtXWrahR2/go-libp2p-routing/options" + mockrouting "gx/ipfs/QmVZ6cQXHoTQja4oo9GhhHZi7dThi4x98mRKgGtKnTy37u/go-ipfs-routing/mock" + offline "gx/ipfs/QmVZ6cQXHoTQja4oo9GhhHZi7dThi4x98mRKgGtKnTy37u/go-ipfs-routing/offline" + ipns "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns" peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" - pstore "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore" - pstoremem "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore/pstoremem" - mockrouting "gx/ipfs/QmdmWkx54g7VfVyxeG8ic84uf4G6Eq1GohuyKA3XDuJ8oC/go-ipfs-routing/mock" - offline "gx/ipfs/QmdmWkx54g7VfVyxeG8ic84uf4G6Eq1GohuyKA3XDuJ8oC/go-ipfs-routing/offline" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" record "gx/ipfs/QmfARXVCzpwFXQdepAJZuqyNDgV9doEsMnVCo1ssmuSe1U/go-libp2p-record" diff --git a/namesys/namesys.go b/namesys/namesys.go index 303413d29..d944b650f 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,13 +5,13 @@ import ( "strings" "time" - path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" + path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" - routing "gx/ipfs/QmRASJXJUFygM5qU4YrH7k7jD6S4Hg8nJmgqJ4bYJvLatd/go-libp2p-routing" + routing "gx/ipfs/QmTiRqrF5zkdZyrdsL5qndG1UbeWi8k8N2pYxCtXWrahR2/go-libp2p-routing" peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 4e398485f..bbbf632d0 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,13 +7,13 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" + path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - ipns "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns" + pstoremem "gx/ipfs/QmPiemjiKBC9VA7vZF82m4x1oygtg2c2YVqag8PX7dN1BD/go-libp2p-peerstore/pstoremem" + "gx/ipfs/QmQXze9tG878pa4Euya4rrDpyTNX3kQe4dhCaBzBozGgpe/go-unixfs" + offroute "gx/ipfs/QmVZ6cQXHoTQja4oo9GhhHZi7dThi4x98mRKgGtKnTy37u/go-ipfs-routing/offline" + ipns "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns" peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" - pstoremem "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore/pstoremem" - path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" - "gx/ipfs/Qmbvw7kpSM2p6rbQ57WGRhhqNfCiNGW6EKH4xgHLw4bsnB/go-unixfs" - offroute "gx/ipfs/QmdmWkx54g7VfVyxeG8ic84uf4G6Eq1GohuyKA3XDuJ8oC/go-ipfs-routing/offline" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) diff --git a/namesys/proquint.go b/namesys/proquint.go index a2c8c6f08..5e8d52fe7 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,8 +4,8 @@ import ( "context" "errors" + path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" - path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 64bea6714..63cae54ce 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,13 +7,13 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" - ft "gx/ipfs/Qmbvw7kpSM2p6rbQ57WGRhhqNfCiNGW6EKH4xgHLw4bsnB/go-unixfs" + path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" + ft "gx/ipfs/QmQXze9tG878pa4Euya4rrDpyTNX3kQe4dhCaBzBozGgpe/go-unixfs" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - ipns "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns" - pb "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns/pb" - routing "gx/ipfs/QmRASJXJUFygM5qU4YrH7k7jD6S4Hg8nJmgqJ4bYJvLatd/go-libp2p-routing" + routing "gx/ipfs/QmTiRqrF5zkdZyrdsL5qndG1UbeWi8k8N2pYxCtXWrahR2/go-libp2p-routing" + ipns "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns" + pb "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns/pb" peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index d872ec324..0d82cc0c2 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -6,13 +6,13 @@ import ( "testing" "time" + ma "gx/ipfs/QmNTCey11oxhb1AxDnQBRHtdhap6Ctud872NjAYPYYXPuc/go-multiaddr" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - ipns "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns" - testutil "gx/ipfs/QmPuhRE325DR8ChNcFtgd6F1eANCHy1oohXZPpYop4xsK6/go-testutil" - ma "gx/ipfs/QmRKLtwMw131aK7ugC3G7ybpumMz78YrJe5dzneyindvG1/go-multiaddr" + testutil "gx/ipfs/QmNvHv84aH2qZafDuSdKJCQ1cvPZ1kmQmyD4YtzjUHuk9v/go-testutil" + mockrouting "gx/ipfs/QmVZ6cQXHoTQja4oo9GhhHZi7dThi4x98mRKgGtKnTy37u/go-ipfs-routing/mock" + ipns "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns" peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" dshelp "gx/ipfs/QmauEMWPoSqggfpSDHMMXuDn12DTd7TaFBvn39eeurzKT2/go-ipfs-ds-help" - mockrouting "gx/ipfs/QmdmWkx54g7VfVyxeG8ic84uf4G6Eq1GohuyKA3XDuJ8oC/go-ipfs-routing/mock" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 7fb593a68..52d90629b 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,12 +7,12 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" + path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" ic "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - pb "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns/pb" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" + pb "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns/pb" peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 26d3fa807..f55fd2984 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" + path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" - mocknet "gx/ipfs/QmRBaUEQEeFWywfrZJ64QgsmvcqgLSK3VbvGMR2NM2Edpf/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmPiemjiKBC9VA7vZF82m4x1oygtg2c2YVqag8PX7dN1BD/go-libp2p-peerstore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - pstore "gx/ipfs/QmZ9zH2FnLcxv1xyzFeUpDUeo55xEhZQHgveZijcxr7TLj/go-libp2p-peerstore" + mocknet "gx/ipfs/QmYxivS34F2M2n44WQQnRHGAKS8aoRUxwGpi9wk4Cdn4Jf/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 32bc6dddf..c157f7de3 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,12 +6,12 @@ import ( "testing" "time" - path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" + path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" - ipns "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns" - testutil "gx/ipfs/QmPuhRE325DR8ChNcFtgd6F1eANCHy1oohXZPpYop4xsK6/go-testutil" + testutil "gx/ipfs/QmNvHv84aH2qZafDuSdKJCQ1cvPZ1kmQmyD4YtzjUHuk9v/go-testutil" + mockrouting "gx/ipfs/QmVZ6cQXHoTQja4oo9GhhHZi7dThi4x98mRKgGtKnTy37u/go-ipfs-routing/mock" + ipns "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns" peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" - mockrouting "gx/ipfs/QmdmWkx54g7VfVyxeG8ic84uf4G6Eq1GohuyKA3XDuJ8oC/go-ipfs-routing/mock" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) diff --git a/namesys/routing.go b/namesys/routing.go index 3398e0e7f..029eaeb83 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,15 +5,15 @@ import ( "strings" "time" - path "gx/ipfs/QmZErC2Ay6WuGi96CPg316PwitdwgLo6RxZRqVjJjRj2MR/go-path" + path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" - ipns "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns" - pb "gx/ipfs/QmPrt2JqvtFcgMBmYBjtZ5jFzq6HoFXy8PTwLb2Dpm2cGf/go-ipns/pb" + dht "gx/ipfs/QmNoNExMdWrYSPZDiJJTVmxSh6uKLN26xYVzbLzBLedRcv/go-libp2p-kad-dht" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - routing "gx/ipfs/QmRASJXJUFygM5qU4YrH7k7jD6S4Hg8nJmgqJ4bYJvLatd/go-libp2p-routing" - dht "gx/ipfs/QmXbPygnUKAPMwseE5U3hQA7Thn59GVm7pQrhkFV63umT8/go-libp2p-kad-dht" + routing "gx/ipfs/QmTiRqrF5zkdZyrdsL5qndG1UbeWi8k8N2pYxCtXWrahR2/go-libp2p-routing" + ipns "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns" + pb "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns/pb" peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" From 4a963093010d26c72a10506c90b61d367c42f174 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 8 Jan 2019 19:19:34 -0800 Subject: [PATCH 2846/3817] gx: update deps Importantly: * fixes a bunch of MFS bugs * pulls in some bitswap improvements License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@20a477e2e65bee7aa49290236b1d0ead47eb6f9f --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 8a04d7fc1..1c5c6a7af 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmPoh3SrQzFBWtdGK6qmHDV4EanKR6kYPj4DD3J2NLoEmZ/go-blockservice" - dag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" + dag "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" + bserv "gx/ipfs/QmYPZzd9VqmJDwxUnThfeSbV1Y5o53aVPDijTB7j7rS9Ep/go-blockservice" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" bstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index d9a21f1ca..11fb21039 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" + mdag "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index ce13cb844..e37773e76 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - bs "gx/ipfs/QmPoh3SrQzFBWtdGK6qmHDV4EanKR6kYPj4DD3J2NLoEmZ/go-blockservice" - mdag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" + mdag "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" + bs "gx/ipfs/QmYPZzd9VqmJDwxUnThfeSbV1Y5o53aVPDijTB7j7rS9Ep/go-blockservice" util "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 66ae325c8..ff7152685 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" + "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index da964e37e..184041f36 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmPoh3SrQzFBWtdGK6qmHDV4EanKR6kYPj4DD3J2NLoEmZ/go-blockservice" - dag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag" + dag "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" + bserv "gx/ipfs/QmYPZzd9VqmJDwxUnThfeSbV1Y5o53aVPDijTB7j7rS9Ep/go-blockservice" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" From 8181fdcf87df81e45b7abf051bbb82d0ad5c8ad2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 7 Jan 2019 17:17:05 -0800 Subject: [PATCH 2847/3817] fix over-wait in WaitPub Before, WaitPub could wait forever if a value was published at the same time as the call to WaitPub. This patch also avoids republishing the same value multiple times and allows setting an initial value without reaching in and modifying internal state. fixes #38 This commit was moved from ipfs/go-mfs@740d0589f0524f824d1e06a6ebefd425519f5051 --- mfs/ops.go | 3 +- mfs/repub.go | 211 ++++++++++++++++++++++++++++------------------ mfs/repub_test.go | 22 +++-- mfs/root.go | 3 +- 4 files changed, 143 insertions(+), 96 deletions(-) diff --git a/mfs/ops.go b/mfs/ops.go index dc3da4ca2..031a77d46 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -1,6 +1,7 @@ package mfs import ( + "context" "fmt" "os" gopath "path" @@ -235,6 +236,6 @@ func FlushPath(rt *Root, pth string) error { return err } - rt.repub.WaitPub() + rt.repub.WaitPub(context.TODO()) return nil } diff --git a/mfs/repub.go b/mfs/repub.go index 12738fa48..de1a3c57c 100644 --- a/mfs/repub.go +++ b/mfs/repub.go @@ -2,7 +2,6 @@ package mfs import ( "context" - "sync" "time" cid "github.com/ipfs/go-cid" @@ -14,15 +13,13 @@ type PubFunc func(context.Context, cid.Cid) error // Republisher manages when to publish a given entry. type Republisher struct { - TimeoutLong time.Duration - TimeoutShort time.Duration - valueHasBeenUpdated chan struct{} - pubfunc PubFunc - immediatePublish chan chan struct{} + TimeoutLong time.Duration + TimeoutShort time.Duration + RetryTimeout time.Duration + pubfunc PubFunc - valueLock sync.Mutex - valueToPublish cid.Cid - lastValuePublished cid.Cid + update chan cid.Cid + immediatePublish chan chan struct{} ctx context.Context cancel func() @@ -33,47 +30,62 @@ type Republisher struct { func NewRepublisher(ctx context.Context, pf PubFunc, tshort, tlong time.Duration) *Republisher { ctx, cancel := context.WithCancel(ctx) return &Republisher{ - TimeoutShort: tshort, - TimeoutLong: tlong, - valueHasBeenUpdated: make(chan struct{}, 1), - pubfunc: pf, - immediatePublish: make(chan chan struct{}), - ctx: ctx, - cancel: cancel, + TimeoutShort: tshort, + TimeoutLong: tlong, + RetryTimeout: tlong, + update: make(chan cid.Cid, 1), + pubfunc: pf, + immediatePublish: make(chan chan struct{}), + ctx: ctx, + cancel: cancel, } } // WaitPub waits for the current value to be published (or returns early // if it already has). -func (rp *Republisher) WaitPub() { +func (rp *Republisher) WaitPub(ctx context.Context) error { wait := make(chan struct{}) - rp.immediatePublish <- wait - <-wait + select { + case rp.immediatePublish <- wait: + case <-ctx.Done(): + return ctx.Err() + } + select { + case <-wait: + return nil + case <-ctx.Done(): + return ctx.Err() + } } func (rp *Republisher) Close() error { - err := rp.publish(rp.ctx) + // TODO(steb): Wait for `Run` to stop + err := rp.WaitPub(rp.ctx) rp.cancel() return err } -// Update the `valueToPublish` and signal it in the `valueHasBeenUpdated` -// channel. Multiple consecutive updates may extend the time period before -// the next publish occurs in order to more efficiently batch updates. +// Update the current value. The value will be published after a delay but each +// consecutive call to Update may extend this delay up to TimeoutLong. func (rp *Republisher) Update(c cid.Cid) { - rp.valueLock.Lock() - rp.valueToPublish = c - rp.valueLock.Unlock() - select { - case rp.valueHasBeenUpdated <- struct{}{}: - default: + case <-rp.update: + select { + case rp.update <- c: + default: + // Don't try again. If we hit this case, there's a + // concurrent publish and we can safely let that + // concurrent publish win. + } + case rp.update <- c: } } // Run contains the core logic of the `Republisher`. It calls the user-defined -// `pubfunc` function whenever the `Cid` value is updated. The complexity comes -// from the fact that `pubfunc` may be slow so we need to batch updates. +// `pubfunc` function whenever the `Cid` value is updated to a *new* value. The +// complexity comes from the fact that `pubfunc` may be slow so we need to batch +// updates. +// // Algorithm: // 1. When we receive the first update after publishing, we set a `longer` timer. // 2. When we receive any update, we reset the `quick` timer. @@ -83,66 +95,103 @@ func (rp *Republisher) Update(c cid.Cid) { // The `longer` timer ensures that we delay publishing by at most // `TimeoutLong`. The `quick` timer allows us to publish sooner if // it looks like there are no more updates coming down the pipe. -func (rp *Republisher) Run() { - for { +// +// Note: If a publish fails, we retry repeatedly every TimeoutRetry. +func (rp *Republisher) Run(lastPublished cid.Cid) { + quick := time.NewTimer(0) + if !quick.Stop() { + <-quick.C + } + longer := time.NewTimer(0) + if !longer.Stop() { + <-longer.C + } + + var toPublish cid.Cid + for rp.ctx.Err() == nil { + var waiter chan struct{} + select { case <-rp.ctx.Done(): return - case <-rp.valueHasBeenUpdated: - // Fast timeout, a `publish` will be issued if there are - // no more updates before it expires (restarted every time - // the `valueHasBeenUpdated` is signaled). - quick := time.After(rp.TimeoutShort) - // Long timeout that guarantees a `publish` after it expires - // even if the value keeps being updated (and `quick` is - // restarted). - longer := time.After(rp.TimeoutLong) - - wait: - var valueHasBeenPublished chan struct{} + case newValue := <-rp.update: + // Skip already published values. + if lastPublished.Equals(newValue) { + // Break to the end of the switch to cleanup any + // timers. + toPublish = cid.Undef + break + } - select { - case <-rp.ctx.Done(): - return - case <-rp.valueHasBeenUpdated: - // The `valueToPublish` has been updated *again* since - // the last time we checked and we still haven't published - // it, restart the `quick` timer allowing for some more - // time to see if the `valueToPublish` changes again. - quick = time.After(rp.TimeoutShort) - goto wait - - case <-quick: - case <-longer: - case valueHasBeenPublished = <-rp.immediatePublish: + // If we aren't already waiting to publish something, + // reset the long timeout. + if !toPublish.Defined() { + longer.Reset(rp.TimeoutLong) } - err := rp.publish(rp.ctx) - if valueHasBeenPublished != nil { - // The user is waiting in `WaitPub` with this channel, signal - // that the `publish` has happened. - valueHasBeenPublished <- struct{}{} + // Always reset the short timeout. + quick.Reset(rp.TimeoutShort) + + // Finally, set the new value to publish. + toPublish = newValue + continue + case waiter = <-rp.immediatePublish: + // Make sure to grab the *latest* value to publish. + select { + case toPublish = <-rp.update: + default: } - if err != nil { - log.Errorf("republishRoot error: %s", err) + + // Avoid publishing duplicate values + if !lastPublished.Equals(toPublish) { + toPublish = cid.Undef } + case <-quick.C: + case <-longer.C: } - } -} -// Wrapper function around the user-defined `pubfunc`. It publishes -// the (last) `valueToPublish` set and registers it in `lastValuePublished`. -func (rp *Republisher) publish(ctx context.Context) error { - rp.valueLock.Lock() - topub := rp.valueToPublish - rp.valueLock.Unlock() + // Cleanup, publish, and close waiters. + + // 1. Stop any timers. Don't use the `if !t.Stop() { ... }` + // idiom as these timers may not be running. + + quick.Stop() + select { + case <-quick.C: + default: + } - err := rp.pubfunc(ctx, topub) - if err != nil { - return err + longer.Stop() + select { + case <-longer.C: + default: + } + + // 2. If we have a value to publish, publish it now. + if toPublish.Defined() { + for { + err := rp.pubfunc(rp.ctx, toPublish) + if err == nil { + break + } + // Keep retrying until we succeed or we abort. + // TODO(steb): We could try pulling new values + // off `update` but that's not critical (and + // complicates this code a bit). We'll pull off + // a new value on the next loop through. + select { + case <-time.After(rp.RetryTimeout): + case <-rp.ctx.Done(): + return + } + } + lastPublished = toPublish + toPublish = cid.Undef + } + + // 3. Trigger anything waiting in `WaitPub`. + if waiter != nil { + close(waiter) + } } - rp.valueLock.Lock() - rp.lastValuePublished = topub - rp.valueLock.Unlock() - return nil } diff --git a/mfs/repub_test.go b/mfs/repub_test.go index d81ffd04e..3a7eaaf70 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -23,13 +23,16 @@ func TestRepublisher(t *testing.T) { return nil } + testCid1, _ := cid.Parse("QmeomffUNfmQy76CQGy9NdmqEnnHU9soCexBnGU3ezPHVH") + testCid2, _ := cid.Parse("QmeomffUNfmQy76CQGy9NdmqEnnHU9soCexBnGU3ezPHVX") + tshort := time.Millisecond * 50 tlong := time.Second / 2 rp := NewRepublisher(ctx, pf, tshort, tlong) - go rp.Run() + go rp.Run(cid.Undef) - rp.Update(cid.Undef) + rp.Update(testCid1) // should hit short timeout select { @@ -42,7 +45,7 @@ func TestRepublisher(t *testing.T) { go func() { for { - rp.Update(cid.Undef) + rp.Update(testCid2) time.Sleep(time.Millisecond * 10) select { case <-cctx.Done(): @@ -65,13 +68,8 @@ func TestRepublisher(t *testing.T) { cancel() - go func() { - err := rp.Close() - if err != nil { - t.Fatal(err) - } - }() - - // final pub from closing - <-pub + err := rp.Close() + if err != nil { + t.Fatal(err) + } } diff --git a/mfs/root.go b/mfs/root.go index ef1d9bcbe..026a3202d 100644 --- a/mfs/root.go +++ b/mfs/root.go @@ -99,11 +99,10 @@ func NewRoot(parent context.Context, ds ipld.DAGService, node *dag.ProtoNode, pf if pf != nil { repub = NewRepublisher(parent, pf, time.Millisecond*300, time.Second*3) - repub.valueToPublish = node.Cid() // No need to take the lock here since we just created // the `Republisher` and no one has access to it yet. - go repub.Run() + go repub.Run(node.Cid()) } root := &Root{ From 30ccbef95ef833214b85ad077614b4b07b85c9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 12 Jan 2019 15:41:19 +0100 Subject: [PATCH 2848/3817] coreapi: replace coreiface.DagAPI with ipld.DAGService MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@8e049b49d6e97f38c682aed4b2c18cdb557b3b84 --- coreiface/coreapi.go | 2 +- coreiface/dag.go | 41 ----------------- coreiface/options/dag.go | 95 --------------------------------------- coreiface/tests/dag.go | 77 +++++++++++++++++++++---------- coreiface/tests/path.go | 38 +++++++++++----- coreiface/tests/pin.go | 24 ++++++---- coreiface/tests/unixfs.go | 6 +-- 7 files changed, 98 insertions(+), 185 deletions(-) delete mode 100644 coreiface/dag.go delete mode 100644 coreiface/options/dag.go diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index 226399967..9d2100fcc 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -19,7 +19,7 @@ type CoreAPI interface { Block() BlockAPI // Dag returns an implementation of Dag API - Dag() DagAPI + Dag() ipld.DAGService // Name returns an implementation of Name API Name() NameAPI diff --git a/coreiface/dag.go b/coreiface/dag.go deleted file mode 100644 index eb9e2da4a..000000000 --- a/coreiface/dag.go +++ /dev/null @@ -1,41 +0,0 @@ -package iface - -import ( - "context" - "io" - - "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - - ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" -) - -// DagOps groups operations that can be batched together -type DagOps interface { - // Put inserts data using specified format and input encoding. - // Unless used with WithCodec or WithHash, the defaults "dag-cbor" and - // "sha256" are used. - Put(ctx context.Context, src io.Reader, opts ...options.DagPutOption) (ResolvedPath, error) -} - -// DagBatch is the batching version of DagAPI. All implementations of DagBatch -// should be threadsafe -type DagBatch interface { - DagOps - - // Commit commits nodes to the datastore and announces them to the network - Commit(ctx context.Context) error -} - -// DagAPI specifies the interface to IPLD -type DagAPI interface { - DagOps - - // Get attempts to resolve and get the node specified by the path - Get(ctx context.Context, path Path) (ipld.Node, error) - - // Tree returns list of paths within a node specified by the path. - Tree(ctx context.Context, path Path, opts ...options.DagTreeOption) ([]Path, error) - - // Batch creates new DagBatch - Batch(ctx context.Context) DagBatch -} diff --git a/coreiface/options/dag.go b/coreiface/options/dag.go deleted file mode 100644 index 9cccba585..000000000 --- a/coreiface/options/dag.go +++ /dev/null @@ -1,95 +0,0 @@ -package options - -import ( - "math" - - cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" -) - -type DagPutSettings struct { - InputEnc string - Codec uint64 - MhType uint64 - MhLength int -} - -type DagTreeSettings struct { - Depth int -} - -type DagPutOption func(*DagPutSettings) error -type DagTreeOption func(*DagTreeSettings) error - -func DagPutOptions(opts ...DagPutOption) (*DagPutSettings, error) { - options := &DagPutSettings{ - InputEnc: "json", - Codec: cid.DagCBOR, - MhType: math.MaxUint64, - MhLength: -1, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - return options, nil -} - -func DagTreeOptions(opts ...DagTreeOption) (*DagTreeSettings, error) { - options := &DagTreeSettings{ - Depth: -1, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - return options, nil -} - -type dagOpts struct{} - -var Dag dagOpts - -// InputEnc is an option for Dag.Put which specifies the input encoding of the -// data. Default is "json", most formats/codecs support "raw" -func (dagOpts) InputEnc(enc string) DagPutOption { - return func(settings *DagPutSettings) error { - settings.InputEnc = enc - return nil - } -} - -// Codec is an option for Dag.Put which specifies the multicodec to use to -// serialize the object. Default is cid.DagCBOR (0x71) -func (dagOpts) Codec(codec uint64) DagPutOption { - return func(settings *DagPutSettings) error { - settings.Codec = codec - return nil - } -} - -// Hash is an option for Dag.Put which specifies the multihash settings to use -// when hashing the object. Default is based on the codec used -// (mh.SHA2_256 (0x12) for DagCBOR). If mhLen is set to -1, default length for -// the hash will be used -func (dagOpts) Hash(mhType uint64, mhLen int) DagPutOption { - return func(settings *DagPutSettings) error { - settings.MhType = mhType - settings.MhLength = mhLen - return nil - } -} - -// Depth is an option for Dag.Tree which specifies maximum depth of the -// returned tree. Default is -1 (no depth limit) -func (dagOpts) Depth(depth int) DagTreeOption { - return func(settings *DagTreeSettings) error { - settings.Depth = depth - return nil - } -} diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index 70a45aa20..e66106c33 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -2,12 +2,13 @@ package tests import ( "context" + "math" "path" "strings" "testing" coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" - opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + coredag "github.com/ipfs/go-ipfs/core/coredag" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) @@ -45,13 +46,18 @@ func (tp *provider) TestPut(t *testing.T) { t.Error(err) } - res, err := api.Dag().Put(ctx, strings.NewReader(`"Hello"`)) + nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`"Hello"`), math.MaxUint64, -1) + if err != nil { + t.Error(err) + } + + err = api.Dag().Add(ctx, nds[0]) if err != nil { t.Fatal(err) } - if res.Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" { - t.Errorf("got wrong cid: %s", res.Cid().String()) + if nds[0].Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" { + t.Errorf("got wrong cid: %s", nds[0].Cid().String()) } } @@ -63,13 +69,18 @@ func (tp *provider) TestPutWithHash(t *testing.T) { t.Error(err) } - res, err := api.Dag().Put(ctx, strings.NewReader(`"Hello"`), opt.Dag.Hash(mh.ID, -1)) + nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`"Hello"`), mh.ID, -1) + if err != nil { + t.Error(err) + } + + err = api.Dag().Add(ctx, nds[0]) if err != nil { t.Fatal(err) } - if res.Cid().String() != "z5hRLNd2sv4z1c" { - t.Errorf("got wrong cid: %s", res.Cid().String()) + if nds[0].Cid().String() != "z5hRLNd2sv4z1c" { + t.Errorf("got wrong cid: %s", nds[0].Cid().String()) } } @@ -81,28 +92,43 @@ func (tp *provider) TestDagPath(t *testing.T) { t.Error(err) } - sub, err := api.Dag().Put(ctx, strings.NewReader(`"foo"`)) + snds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`"foo"`), math.MaxUint64, -1) + if err != nil { + t.Error(err) + } + + err = api.Dag().Add(ctx, snds[0]) if err != nil { t.Fatal(err) } - res, err := api.Dag().Put(ctx, strings.NewReader(`{"lnk": {"/": "`+sub.Cid().String()+`"}}`)) + nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"lnk": {"/": "`+snds[0].Cid().String()+`"}}`), math.MaxUint64, -1) + if err != nil { + t.Error(err) + } + + err = api.Dag().Add(ctx, nds[0]) if err != nil { t.Fatal(err) } - p, err := coreiface.ParsePath(path.Join(res.Cid().String(), "lnk")) + p, err := coreiface.ParsePath(path.Join(nds[0].Cid().String(), "lnk")) if err != nil { t.Error(err) } - nd, err := api.Dag().Get(ctx, p) + rp, err := api.ResolvePath(ctx, p) if err != nil { t.Error(err) } - if nd.Cid().String() != sub.Cid().String() { - t.Errorf("got unexpected cid %s, expected %s", nd.Cid().String(), sub.Cid().String()) + nd, err := api.Dag().Get(ctx, rp.Cid()) + if err != nil { + t.Error(err) + } + + if nd.Cid().String() != snds[0].Cid().String() { + t.Errorf("got unexpected cid %s, expected %s", nd.Cid().String(), snds[0].Cid().String()) } } @@ -114,12 +140,17 @@ func (tp *provider) TestTree(t *testing.T) { t.Error(err) } - c, err := api.Dag().Put(ctx, strings.NewReader(`{"a": 123, "b": "foo", "c": {"d": 321, "e": 111}}`)) + nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"a": 123, "b": "foo", "c": {"d": 321, "e": 111}}`), math.MaxUint64, -1) + if err != nil { + t.Error(err) + } + + err = api.Dag().Add(ctx, nds[0]) if err != nil { t.Fatal(err) } - res, err := api.Dag().Get(ctx, c) + res, err := api.Dag().Get(ctx, nds[0].Cid()) if err != nil { t.Error(err) } @@ -144,27 +175,25 @@ func (tp *provider) TestBatch(t *testing.T) { t.Error(err) } - batch := api.Dag().Batch(ctx) - - c, err := batch.Put(ctx, strings.NewReader(`"Hello"`)) + nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`"Hello"`), math.MaxUint64, -1) if err != nil { - t.Fatal(err) + t.Error(err) } - if c.Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" { - t.Errorf("got wrong cid: %s", c.Cid().String()) + if nds[0].Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" { + t.Errorf("got wrong cid: %s", nds[0].Cid().String()) } - _, err = api.Dag().Get(ctx, c) + _, err = api.Dag().Get(ctx, nds[0].Cid()) if err == nil || err.Error() != "merkledag: not found" { t.Error(err) } - if err := batch.Commit(ctx); err != nil { + if err := api.Dag().AddMany(ctx, nds); err != nil { t.Error(err) } - _, err = api.Dag().Get(ctx, c) + _, err = api.Dag().Get(ctx, nds[0].Cid()) if err != nil { t.Error(err) } diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index 50a1977d5..01f2e6f36 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -2,11 +2,13 @@ package tests import ( "context" + "math" "strings" "testing" coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + "github.com/ipfs/go-ipfs/core/coredag" ) func (tp *provider) TestPath(t *testing.T) { @@ -62,12 +64,16 @@ func (tp *provider) TestPathRemainder(t *testing.T) { t.Fatal(".Dag not implemented") } - obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"bar": "baz"}}`)) + nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) if err != nil { + t.Error(err) + } + + if err := api.Dag().AddMany(ctx, nds); err != nil { t.Fatal(err) } - p1, err := coreiface.ParsePath(obj.String() + "/foo/bar") + p1, err := coreiface.ParsePath(nds[0].String() + "/foo/bar") if err != nil { t.Error(err) } @@ -94,16 +100,16 @@ func (tp *provider) TestEmptyPathRemainder(t *testing.T) { t.Fatal(".Dag not implemented") } - obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"bar": "baz"}}`)) + nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) if err != nil { - t.Fatal(err) + t.Error(err) } - if obj.Remainder() != "" { - t.Error("expected the resolved path to not have a remainder") + if err := api.Dag().AddMany(ctx, nds); err != nil { + t.Fatal(err) } - p1, err := coreiface.ParsePath(obj.String()) + p1, err := coreiface.ParsePath(nds[0].Cid().String()) if err != nil { t.Error(err) } @@ -130,12 +136,16 @@ func (tp *provider) TestInvalidPathRemainder(t *testing.T) { t.Fatal(".Dag not implemented") } - obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"bar": "baz"}}`)) + nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) if err != nil { + t.Error(err) + } + + if err := api.Dag().AddMany(ctx, nds); err != nil { t.Fatal(err) } - p1, err := coreiface.ParsePath(obj.String() + "/bar/baz") + p1, err := coreiface.ParsePath("/ipld/" + nds[0].Cid().String() + "/bar/baz") if err != nil { t.Error(err) } @@ -167,12 +177,16 @@ func (tp *provider) TestPathRoot(t *testing.T) { t.Fatal(".Dag not implemented") } - obj, err := api.Dag().Put(ctx, strings.NewReader(`{"foo": {"/": "`+blk.Path().Cid().String()+`"}}`)) + nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"foo": {"/": "`+blk.Path().Cid().String()+`"}}`), math.MaxUint64, -1) if err != nil { + t.Error(err) + } + + if err := api.Dag().AddMany(ctx, nds); err != nil { t.Fatal(err) } - p1, err := coreiface.ParsePath(obj.String() + "/foo") + p1, err := coreiface.ParsePath("/ipld/" + nds[0].Cid().String() + "/foo") if err != nil { t.Error(err) } @@ -182,7 +196,7 @@ func (tp *provider) TestPathRoot(t *testing.T) { t.Fatal(err) } - if rp.Root().String() != obj.Cid().String() { + if rp.Root().String() != nds[0].Cid().String() { t.Error("unexpected path root") } diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index 87ad8a004..250799222 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -2,11 +2,13 @@ package tests import ( "context" - "github.com/ipfs/go-ipfs/core/coreapi/interface" + "math" "strings" "testing" + "github.com/ipfs/go-ipfs/core/coreapi/interface" opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + "github.com/ipfs/go-ipfs/core/coredag" ) func (tp *provider) TestPin(t *testing.T) { @@ -109,22 +111,26 @@ func (tp *provider) TestPinRecursive(t *testing.T) { t.Error(err) } - p2, err := api.Dag().Put(ctx, strings.NewReader(`{"lnk": {"/": "`+p0.Cid().String()+`"}}`)) + nd2, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"lnk": {"/": "`+p0.Cid().String()+`"}}`), math.MaxUint64, -1) if err != nil { t.Error(err) } - p3, err := api.Dag().Put(ctx, strings.NewReader(`{"lnk": {"/": "`+p1.Cid().String()+`"}}`)) + nd3, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"lnk": {"/": "`+p1.Cid().String()+`"}}`), math.MaxUint64, -1) if err != nil { t.Error(err) } - err = api.Pin().Add(ctx, p2) + if err := api.Dag().AddMany(ctx, append(nd2, nd3...)); err != nil { + t.Fatal(err) + } + + err = api.Pin().Add(ctx, iface.IpldPath(nd2[0].Cid())) if err != nil { t.Error(err) } - err = api.Pin().Add(ctx, p3, opt.Pin.Recursive(false)) + err = api.Pin().Add(ctx, iface.IpldPath(nd3[0].Cid()), opt.Pin.Recursive(false)) if err != nil { t.Error(err) } @@ -147,8 +153,8 @@ func (tp *provider) TestPinRecursive(t *testing.T) { t.Errorf("unexpected pin list len: %d", len(list)) } - if list[0].Path().String() != p3.String() { - t.Error("unexpected path") + if list[0].Path().String() != iface.IpldPath(nd3[0].Cid()).String() { + t.Errorf("unexpected path, %s != %s", list[0].Path().String(), iface.IpfsPath(nd2[0].Cid()).String()) } list, err = api.Pin().Ls(ctx, opt.Pin.Type.Recursive()) @@ -160,8 +166,8 @@ func (tp *provider) TestPinRecursive(t *testing.T) { t.Errorf("unexpected pin list len: %d", len(list)) } - if list[0].Path().String() != p2.String() { - t.Error("unexpected path") + if list[0].Path().String() != iface.IpldPath(nd2[0].Cid()).String() { + t.Errorf("unexpected path, %s != %s", list[0].Path().String(), iface.IpldPath(nd3[0].Cid()).String()) } list, err = api.Pin().Ls(ctx, opt.Pin.Type.Indirect()) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index e8a1aba32..fce41ae84 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -633,7 +633,7 @@ func (tp *provider) TestGetDir(t *testing.T) { t.Error(err) } edir := unixfs.EmptyDirNode() - _, err = api.Dag().Put(ctx, bytes.NewReader(edir.RawData()), options.Dag.Codec(cid.DagProtobuf), options.Dag.InputEnc("raw")) + err = api.Dag().Add(ctx, edir) if err != nil { t.Error(err) } @@ -667,7 +667,7 @@ func (tp *provider) TestGetNonUnixfs(t *testing.T) { } nd := new(mdag.ProtoNode) - _, err = api.Dag().Put(ctx, bytes.NewReader(nd.RawData()), options.Dag.Codec(nd.CidBuilder().GetCodec()), options.Dag.InputEnc("raw")) + err = api.Dag().Add(ctx, nd) if err != nil { t.Error(err) } @@ -801,7 +801,7 @@ func (tp *provider) TestLsNonUnixfs(t *testing.T) { t.Fatal(err) } - _, err = api.Dag().Put(ctx, bytes.NewReader(nd.RawData()), options.Dag.Codec(cid.DagCBOR), options.Dag.InputEnc("raw")) + err = api.Dag().Add(ctx, nd) if err != nil { t.Error(err) } From ce6145c5f966f889afe10c4d610fc73a26dc2b5f Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Mon, 14 Jan 2019 21:22:02 -0300 Subject: [PATCH 2849/3817] repub: fix typo in comparison We only unset `toPublish` if it was a repeated value. This commit was moved from ipfs/go-mfs@2e0c3bc16e67617135127f7436343cff4a33b1dd --- mfs/repub.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/repub.go b/mfs/repub.go index de1a3c57c..2c9dbd25d 100644 --- a/mfs/repub.go +++ b/mfs/repub.go @@ -143,7 +143,7 @@ func (rp *Republisher) Run(lastPublished cid.Cid) { } // Avoid publishing duplicate values - if !lastPublished.Equals(toPublish) { + if lastPublished.Equals(toPublish) { toPublish = cid.Undef } case <-quick.C: From 6d44270b3a49474f67a51f0fcddfbf40766363be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 15 Jan 2019 18:51:29 +0100 Subject: [PATCH 2850/3817] coreapi: adjust some tests for go-ipfs-http-api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@fb15570caa8ff7cb9defce24198738273b23aa16 --- coreiface/tests/block.go | 4 ++-- coreiface/tests/dht.go | 20 ++++++++++++++++++-- coreiface/tests/key.go | 16 ++++++++-------- coreiface/tests/object.go | 2 +- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 81a6fb061..427ad3357 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -159,7 +159,7 @@ func (tp *provider) TestBlockRm(t *testing.T) { if err == nil { t.Error("expected err to exist") } - if err.Error() != "blockservice: key not found" { + if !strings.Contains(err.Error(), "blockservice: key not found") { t.Errorf("unexpected error; %s", err.Error()) } @@ -167,7 +167,7 @@ func (tp *provider) TestBlockRm(t *testing.T) { if err == nil { t.Error("expected err to exist") } - if err.Error() != "blockstore: block not found" { + if !strings.Contains(err.Error(), "blockstore: block not found") { t.Errorf("unexpected error; %s", err.Error()) } diff --git a/coreiface/tests/dht.go b/coreiface/tests/dht.go index 3ec77d33b..d2eae1af4 100644 --- a/coreiface/tests/dht.go +++ b/coreiface/tests/dht.go @@ -35,12 +35,20 @@ func (tp *provider) TestDhtFindPeer(t *testing.T) { t.Fatal(err) } + laddrs0, err := apis[0].Swarm().LocalAddrs(ctx) + if err != nil { + t.Fatal(err) + } + if len(laddrs0) != 1 { + t.Fatal("unexpected number of local addrs") + } + pi, err := apis[2].Dht().FindPeer(ctx, self0.ID()) if err != nil { t.Fatal(err) } - if pi.Addrs[0].String() != "/ip4/127.0.0.1/tcp/4001" { + if pi.Addrs[0].String() != laddrs0[0].String() { t.Errorf("got unexpected address from FindPeer: %s", pi.Addrs[0].String()) } @@ -54,7 +62,15 @@ func (tp *provider) TestDhtFindPeer(t *testing.T) { t.Fatal(err) } - if pi.Addrs[0].String() != "/ip4/127.0.2.1/tcp/4001" { + laddrs2, err := apis[2].Swarm().LocalAddrs(ctx) + if err != nil { + t.Fatal(err) + } + if len(laddrs2) != 1 { + t.Fatal("unexpected number of local addrs") + } + + if pi.Addrs[0].String() != laddrs2[0].String() { t.Errorf("got unexpected address from FindPeer: %s", pi.Addrs[0].String()) } } diff --git a/coreiface/tests/key.go b/coreiface/tests/key.go index 8dd6af57f..66011f99f 100644 --- a/coreiface/tests/key.go +++ b/coreiface/tests/key.go @@ -82,7 +82,7 @@ func (tp *provider) TestRenameSelf(t *testing.T) { if err == nil { t.Error("expected error to not be nil") } else { - if err.Error() != "cannot rename key with name 'self'" { + if !strings.Contains(err.Error(), "cannot rename key with name 'self'") { t.Fatalf("expected error 'cannot rename key with name 'self'', got '%s'", err.Error()) } } @@ -91,7 +91,7 @@ func (tp *provider) TestRenameSelf(t *testing.T) { if err == nil { t.Error("expected error to not be nil") } else { - if err.Error() != "cannot rename key with name 'self'" { + if !strings.Contains(err.Error(), "cannot rename key with name 'self'") { t.Fatalf("expected error 'cannot rename key with name 'self'', got '%s'", err.Error()) } } @@ -110,7 +110,7 @@ func (tp *provider) TestRemoveSelf(t *testing.T) { if err == nil { t.Error("expected error to not be nil") } else { - if err.Error() != "cannot remove key with name 'self'" { + if !strings.Contains(err.Error(), "cannot remove key with name 'self'") { t.Fatalf("expected error 'cannot remove key with name 'self'', got '%s'", err.Error()) } } @@ -206,7 +206,7 @@ func (tp *provider) TestGenerateExisting(t *testing.T) { if err == nil { t.Error("expected error to not be nil") } else { - if err.Error() != "key with name 'foo' already exists" { + if !strings.Contains(err.Error(), "key with name 'foo' already exists") { t.Fatalf("expected error 'key with name 'foo' already exists', got '%s'", err.Error()) } } @@ -215,7 +215,7 @@ func (tp *provider) TestGenerateExisting(t *testing.T) { if err == nil { t.Error("expected error to not be nil") } else { - if err.Error() != "cannot create key with name 'self'" { + if !strings.Contains(err.Error(), "cannot create key with name 'self'") { t.Fatalf("expected error 'cannot create key with name 'self'', got '%s'", err.Error()) } } @@ -314,7 +314,7 @@ func (tp *provider) TestRenameToSelf(t *testing.T) { if err == nil { t.Error("expected error to not be nil") } else { - if err.Error() != "cannot overwrite key with name 'self'" { + if !strings.Contains(err.Error(), "cannot overwrite key with name 'self'") { t.Fatalf("expected error 'cannot overwrite key with name 'self'', got '%s'", err.Error()) } } @@ -338,7 +338,7 @@ func (tp *provider) TestRenameToSelfForce(t *testing.T) { if err == nil { t.Error("expected error to not be nil") } else { - if err.Error() != "cannot overwrite key with name 'self'" { + if !strings.Contains(err.Error(), "cannot overwrite key with name 'self'") { t.Fatalf("expected error 'cannot overwrite key with name 'self'', got '%s'", err.Error()) } } @@ -368,7 +368,7 @@ func (tp *provider) TestRenameOverwriteNoForce(t *testing.T) { if err == nil { t.Error("expected error to not be nil") } else { - if err.Error() != "key by that name already exists, refusing to overwrite" { + if !strings.Contains(err.Error(), "key by that name already exists, refusing to overwrite") { t.Fatalf("expected error 'key by that name already exists, refusing to overwrite', got '%s'", err.Error()) } } diff --git a/coreiface/tests/object.go b/coreiface/tests/object.go index 81d5b4117..2a3b1bd5c 100644 --- a/coreiface/tests/object.go +++ b/coreiface/tests/object.go @@ -300,7 +300,7 @@ func (tp *provider) TestObjectAddLinkCreate(t *testing.T) { if err == nil { t.Fatal("expected an error") } - if err.Error() != "no link by that name" { + if !strings.Contains(err.Error(), "no link by that name") { t.Fatalf("unexpected error: %s", err.Error()) } From 40c3900dd0eb8a88e013751d40e2f84f47ab3b0e Mon Sep 17 00:00:00 2001 From: Overbool Date: Fri, 19 Oct 2018 22:58:58 +0800 Subject: [PATCH 2851/3817] refactor(hamt): remove protonode This commit was moved from ipfs/go-unixfs@71ede54b4fa611a6e6ccc501bf6b1d7a456ebf84 --- unixfs/hamt/hamt.go | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index fdaf5be9b..f09dfc285 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -44,10 +44,11 @@ func (ds *Shard) isValueNode() bool { // A Shard represents the HAMT. It should be initialized with NewShard(). type Shard struct { - nd *dag.ProtoNode + cid cid.Cid bitfield bitfield.Bitfield + links []*ipld.Link children []*Shard tableSize int @@ -73,7 +74,7 @@ func NewShard(dserv ipld.DAGService, size int) (*Shard, error) { return nil, err } - ds.nd = new(dag.ProtoNode) + ds.links = make([]*ipld.Link, 0) ds.hashFunc = HashMurmur3 return ds, nil } @@ -119,11 +120,16 @@ func NewHamtFromDag(dserv ipld.DAGService, nd ipld.Node) (*Shard, error) { return nil, err } - ds.nd = pbnd.Copy().(*dag.ProtoNode) + if len(pbnd.Links()) > 0 { + ds.links = make([]*ipld.Link, len(pbnd.Links())) + copy(ds.links, pbnd.Links()) + } + + ds.cid = pbnd.Cid() ds.children = make([]*Shard, len(pbnd.Links())) ds.bitfield.SetBytes(fsn.Data()) ds.hashFunc = fsn.HashType() - ds.builder = ds.nd.CidBuilder() + ds.builder = pbnd.CidBuilder() return ds, nil } @@ -163,7 +169,7 @@ func (ds *Shard) Node() (ipld.Node, error) { } } else { // child unloaded, just copy in link with updated name - lnk := ds.nd.Links()[cindex] + lnk := ds.links[cindex] label := lnk.Name[ds.maxpadlen:] err := out.AddRawLink(ds.linkNamePrefix(i)+label, lnk) @@ -273,7 +279,7 @@ func (ds *Shard) getChild(ctx context.Context, i int) (*Shard, error) { return nil, fmt.Errorf("invalid index passed to getChild (likely corrupt bitfield)") } - if len(ds.children) != len(ds.nd.Links()) { + if len(ds.children) != len(ds.links) { return nil, fmt.Errorf("inconsistent lengths between children array and Links array") } @@ -288,7 +294,7 @@ func (ds *Shard) getChild(ctx context.Context, i int) (*Shard, error) { // loadChild reads the i'th child node of this shard from disk and returns it // as a 'child' interface func (ds *Shard) loadChild(ctx context.Context, i int) (*Shard, error) { - lnk := ds.nd.Links()[i] + lnk := ds.links[i] lnkLinkType, err := ds.childLinkType(lnk) if err != nil { return nil, err @@ -356,20 +362,20 @@ func (ds *Shard) insertChild(idx int, key string, lnk *ipld.Link) error { } ds.children = append(ds.children[:i], append([]*Shard{sv}, ds.children[i:]...)...) - ds.nd.SetLinks(append(ds.nd.Links()[:i], append([]*ipld.Link{nil}, ds.nd.Links()[i:]...)...)) + ds.links = append(ds.links[:i], append([]*ipld.Link{nil}, ds.links[i:]...)...) return nil } func (ds *Shard) rmChild(i int) error { - if i < 0 || i >= len(ds.children) || i >= len(ds.nd.Links()) { + if i < 0 || i >= len(ds.children) || i >= len(ds.links) { return fmt.Errorf("hamt: attempted to remove child with out of range index") } copy(ds.children[i:], ds.children[i+1:]) ds.children = ds.children[:len(ds.children)-1] - copy(ds.nd.Links()[i:], ds.nd.Links()[i+1:]) - ds.nd.SetLinks(ds.nd.Links()[:len(ds.nd.Links())-1]) + copy(ds.links[i:], ds.links[i+1:]) + ds.links = ds.links[:len(ds.links)-1] return nil } @@ -458,7 +464,7 @@ func makeAsyncTrieGetLinks(dagService ipld.DAGService, linkResults chan<- format } childShards := make([]*ipld.Link, 0, len(directoryShard.children)) - links := directoryShard.nd.Links() + links := directoryShard.links for idx := range directoryShard.children { lnk := links[idx] lnkLinkType, err := directoryShard.childLinkType(lnk) From f0fc602c5a605aafd5e276ea31a4e6fa91f42792 Mon Sep 17 00:00:00 2001 From: Overbool Date: Mon, 5 Nov 2018 16:02:28 +0800 Subject: [PATCH 2852/3817] hamt: wrap the manipulation about child and link This commit was moved from ipfs/go-unixfs@332e82f2d7733368887cf5f154094a424422d094 --- unixfs/hamt/hamt.go | 356 ++++++++++++++++++++++----------------- unixfs/hamt/hamt_test.go | 8 +- unixfs/hamt/util.go | 8 + 3 files changed, 213 insertions(+), 159 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index f09dfc285..17e031502 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -30,7 +30,6 @@ import ( ipld "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" format "github.com/ipfs/go-unixfs" - "github.com/spaolacci/murmur3" ) const ( @@ -46,10 +45,7 @@ func (ds *Shard) isValueNode() bool { type Shard struct { cid cid.Cid - bitfield bitfield.Bitfield - - links []*ipld.Link - children []*Shard + childer *childer tableSize int tableSizeLg2 int @@ -74,7 +70,6 @@ func NewShard(dserv ipld.DAGService, size int) (*Shard, error) { return nil, err } - ds.links = make([]*ipld.Link, 0) ds.hashFunc = HashMurmur3 return ds, nil } @@ -85,14 +80,18 @@ func makeShard(ds ipld.DAGService, size int) (*Shard, error) { return nil, err } maxpadding := fmt.Sprintf("%X", size-1) - return &Shard{ + s := &Shard{ tableSizeLg2: lg2s, prefixPadStr: fmt.Sprintf("%%0%dX", len(maxpadding)), maxpadlen: len(maxpadding), - bitfield: bitfield.NewBitfield(size), + childer: newChilder(ds, size), tableSize: size, dserv: ds, - }, nil + } + + s.childer.sd = s + + return s, nil } // NewHamtFromDag creates new a HAMT shard from the given DAG. @@ -115,19 +114,16 @@ func NewHamtFromDag(dserv ipld.DAGService, nd ipld.Node) (*Shard, error) { return nil, fmt.Errorf("only murmur3 supported as hash function") } - ds, err := makeShard(dserv, int(fsn.Fanout())) + size := int(fsn.Fanout()) + + ds, err := makeShard(dserv, size) if err != nil { return nil, err } - if len(pbnd.Links()) > 0 { - ds.links = make([]*ipld.Link, len(pbnd.Links())) - copy(ds.links, pbnd.Links()) - } + ds.childer.makeChilder(fsn.Data(), pbnd.Links()) ds.cid = pbnd.Cid() - ds.children = make([]*Shard, len(pbnd.Links())) - ds.bitfield.SetBytes(fsn.Data()) ds.hashFunc = fsn.HashType() ds.builder = pbnd.CidBuilder() @@ -152,11 +148,11 @@ func (ds *Shard) Node() (ipld.Node, error) { cindex := 0 // TODO: optimized 'for each set bit' for i := 0; i < ds.tableSize; i++ { - if !ds.bitfield.Bit(i) { + if !ds.childer.has(i) { continue } - ch := ds.children[cindex] + ch := ds.childer.child(cindex) if ch != nil { clnk, err := ch.Link() if err != nil { @@ -169,7 +165,7 @@ func (ds *Shard) Node() (ipld.Node, error) { } } else { // child unloaded, just copy in link with updated name - lnk := ds.links[cindex] + lnk := ds.childer.link(cindex) label := lnk.Name[ds.maxpadlen:] err := out.AddRawLink(ds.linkNamePrefix(i)+label, lnk) @@ -180,7 +176,7 @@ func (ds *Shard) Node() (ipld.Node, error) { cindex++ } - data, err := format.HAMTShardData(ds.bitfield.Bytes(), uint64(ds.tableSize), HashMurmur3) + data, err := format.HAMTShardData(ds.childer.bitfield.Bytes(), uint64(ds.tableSize), HashMurmur3) if err != nil { return nil, err } @@ -208,12 +204,6 @@ func (ds *Shard) makeShardValue(lnk *ipld.Link) (*Shard, error) { return s, nil } -func hash(val []byte) []byte { - h := murmur3.New128() - h.Write(val) - return h.Sum(make([]byte, 0, 128/8)) -} - // Set sets 'name' = nd in the HAMT func (ds *Shard) Set(ctx context.Context, name string, nd ipld.Node) error { hv := &hashBits{b: hash([]byte(name))} @@ -271,63 +261,6 @@ func (ds *Shard) childLinkType(lnk *ipld.Link) (linkType, error) { return shardValueLink, nil } -// getChild returns the i'th child of this shard. If it is cached in the -// children array, it will return it from there. Otherwise, it loads the child -// node from disk. -func (ds *Shard) getChild(ctx context.Context, i int) (*Shard, error) { - if i >= len(ds.children) || i < 0 { - return nil, fmt.Errorf("invalid index passed to getChild (likely corrupt bitfield)") - } - - if len(ds.children) != len(ds.links) { - return nil, fmt.Errorf("inconsistent lengths between children array and Links array") - } - - c := ds.children[i] - if c != nil { - return c, nil - } - - return ds.loadChild(ctx, i) -} - -// loadChild reads the i'th child node of this shard from disk and returns it -// as a 'child' interface -func (ds *Shard) loadChild(ctx context.Context, i int) (*Shard, error) { - lnk := ds.links[i] - lnkLinkType, err := ds.childLinkType(lnk) - if err != nil { - return nil, err - } - - var c *Shard - if lnkLinkType == shardLink { - nd, err := lnk.GetNode(ctx, ds.dserv) - if err != nil { - return nil, err - } - cds, err := NewHamtFromDag(ds.dserv, nd) - if err != nil { - return nil, err - } - - c = cds - } else { - s, err := ds.makeShardValue(lnk) - if err != nil { - return nil, err - } - c = s - } - - ds.children[i] = c - return c, nil -} - -func (ds *Shard) setChild(i int, c *Shard) { - ds.children[i] = c -} - // Link returns a merklelink to this shard node func (ds *Shard) Link() (*ipld.Link, error) { if ds.isValueNode() { @@ -347,48 +280,13 @@ func (ds *Shard) Link() (*ipld.Link, error) { return ipld.MakeLink(nd) } -func (ds *Shard) insertChild(idx int, key string, lnk *ipld.Link) error { - if lnk == nil { - return os.ErrNotExist - } - - i := ds.indexForBitPos(idx) - ds.bitfield.SetBit(idx) - - lnk.Name = ds.linkNamePrefix(idx) + key - sv := &Shard{ - key: key, - val: lnk, - } - - ds.children = append(ds.children[:i], append([]*Shard{sv}, ds.children[i:]...)...) - ds.links = append(ds.links[:i], append([]*ipld.Link{nil}, ds.links[i:]...)...) - return nil -} - -func (ds *Shard) rmChild(i int) error { - if i < 0 || i >= len(ds.children) || i >= len(ds.links) { - return fmt.Errorf("hamt: attempted to remove child with out of range index") - } - - copy(ds.children[i:], ds.children[i+1:]) - ds.children = ds.children[:len(ds.children)-1] - - copy(ds.links[i:], ds.links[i+1:]) - ds.links = ds.links[:len(ds.links)-1] - - return nil -} - func (ds *Shard) getValue(ctx context.Context, hv *hashBits, key string, cb func(*Shard) error) error { idx, err := hv.Next(ds.tableSizeLg2) if err != nil { return err } - if ds.bitfield.Bit(int(idx)) { - cindex := ds.indexForBitPos(idx) - - child, err := ds.getChild(ctx, cindex) + if ds.childer.has(idx) { + child, err := ds.childer.get(ctx, ds.childer.index(idx)) if err != nil { return err } @@ -440,7 +338,7 @@ func (ds *Shard) EnumLinksAsync(ctx context.Context) <-chan format.LinkResult { defer cancel() getLinks := makeAsyncTrieGetLinks(ds.dserv, linkResults) cset := cid.NewSet() - err := dag.EnumerateChildrenAsync(ctx, getLinks, ds.nd.Cid(), cset.Visit) + err := dag.EnumerateChildrenAsync(ctx, getLinks, ds.cid, cset.Visit) if err != nil { emitResult(ctx, linkResults, format.LinkResult{Link: nil, Err: err}) } @@ -463,9 +361,9 @@ func makeAsyncTrieGetLinks(dagService ipld.DAGService, linkResults chan<- format return nil, err } - childShards := make([]*ipld.Link, 0, len(directoryShard.children)) - links := directoryShard.links - for idx := range directoryShard.children { + childShards := make([]*ipld.Link, 0, directoryShard.childer.length()) + links := directoryShard.childer.links + for idx := range directoryShard.childer.children { lnk := links[idx] lnkLinkType, err := directoryShard.childLinkType(lnk) @@ -505,23 +403,18 @@ func emitResult(ctx context.Context, linkResults chan<- format.LinkResult, r for } func (ds *Shard) walkTrie(ctx context.Context, cb func(*Shard) error) error { - for idx := range ds.children { - c, err := ds.getChild(ctx, idx) - if err != nil { - return err - } - - if c.isValueNode() { - if err := cb(c); err != nil { + return ds.childer.each(ctx, func(s *Shard) error { + if s.isValueNode() { + if err := cb(s); err != nil { return err } } else { - if err := c.walkTrie(ctx, cb); err != nil { + if err := s.walkTrie(ctx, cb); err != nil { return err } } - } - return nil + return nil + }) } func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val *ipld.Link) error { @@ -529,13 +422,14 @@ func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val if err != nil { return err } - if !ds.bitfield.Bit(idx) { - return ds.insertChild(idx, key, val) + + if !ds.childer.has(idx) { + return ds.childer.insert(key, val, idx) } - cindex := ds.indexForBitPos(idx) + i := ds.childer.index(idx) - child, err := ds.getChild(ctx, cindex) + child, err := ds.childer.get(ctx, i) if err != nil { return err } @@ -544,8 +438,7 @@ func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val if child.key == key { // value modification if val == nil { - ds.bitfield.UnsetBit(idx) - return ds.rmChild(cindex) + return ds.childer.rm(idx) } child.val = val @@ -577,7 +470,7 @@ func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val return err } - ds.setChild(cindex, ns) + ds.childer.set(ns, i) return nil } else { err := child.modifyValue(ctx, hv, key, val) @@ -586,19 +479,18 @@ func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val } if val == nil { - switch len(child.children) { + switch child.childer.length() { case 0: // empty sub-shard, prune it // Note: this shouldnt normally ever happen // in the event of another implementation creates flawed // structures, this will help to normalize them. - ds.bitfield.UnsetBit(idx) - return ds.rmChild(cindex) + return ds.childer.rm(idx) case 1: - nchild := child.children[0] + nchild := child.childer.children[0] if nchild.isValueNode() { // sub-shard with a single value element, collapse it - ds.setChild(cindex, nchild) + ds.childer.set(nchild, i) } return nil } @@ -608,14 +500,170 @@ func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val } } -// indexForBitPos returns the index within the collapsed array corresponding to -// the given bit in the bitset. The collapsed array contains only one entry -// per bit set in the bitfield, and this function is used to map the indices. -func (ds *Shard) indexForBitPos(bp int) int { - return ds.bitfield.OnesBefore(bp) -} - // linkNamePrefix takes in the bitfield index of an entry and returns its hex prefix func (ds *Shard) linkNamePrefix(idx int) string { return fmt.Sprintf(ds.prefixPadStr, idx) } + +// childer wraps the links, children and bitfield +// and provides basic operation (get, rm, insert and set) of manipulating children. +type childer struct { + sd *Shard + dserv ipld.DAGService + bitfield bitfield.Bitfield + links []*ipld.Link + children []*Shard +} + +func newChilder(ds ipld.DAGService, size int) *childer { + return &childer{ + dserv: ds, + bitfield: bitfield.NewBitfield(size), + } +} + +func (s *childer) makeChilder(data []byte, links []*ipld.Link) *childer { + s.children = make([]*Shard, len(links)) + s.bitfield.SetBytes(data) + if len(links) > 0 { + s.links = make([]*ipld.Link, len(links)) + copy(s.links, links) + } + + return s +} + +func (s *childer) index(idx int) int { + return s.bitfield.OnesBefore(idx) +} + +func (s *childer) child(i int) *Shard { + return s.children[i] +} + +func (s *childer) link(i int) *ipld.Link { + return s.links[i] +} + +func (s *childer) insert(key string, lnk *ipld.Link, idx int) error { + if lnk == nil { + return os.ErrNotExist + } + + lnk.Name = s.sd.linkNamePrefix(idx) + key + i := s.index(idx) + sd := &Shard{key: key, val: lnk} + + s.children = append(s.children[:i], append([]*Shard{sd}, s.children[i:]...)...) + s.links = append(s.links[:i], append([]*ipld.Link{nil}, s.links[i:]...)...) + s.bitfield.SetBit(idx) + + return nil +} + +func (s *childer) set(sd *Shard, i int) { + s.children[i] = sd +} + +func (s *childer) rm(idx int) error { + i := s.index(idx) + + if err := s.check(i); err != nil { + return err + } + + copy(s.children[i:], s.children[i+1:]) + s.children = s.children[:len(s.children)-1] + + copy(s.links[i:], s.links[i+1:]) + s.links = s.links[:len(s.links)-1] + + s.bitfield.UnsetBit(idx) + + return nil +} + +// get returns the i'th child of this shard. If it is cached in the +// children array, it will return it from there. Otherwise, it loads the child +// node from disk. +func (s *childer) get(ctx context.Context, i int) (*Shard, error) { + if err := s.check(i); err != nil { + return nil, err + } + + c := s.child(i) + if c != nil { + return c, nil + } + + return s.loadChild(ctx, i) +} + +// loadChild reads the i'th child node of this shard from disk and returns it +// as a 'child' interface +func (s *childer) loadChild(ctx context.Context, i int) (*Shard, error) { + lnk := s.link(i) + lnkLinkType, err := s.sd.childLinkType(lnk) + if err != nil { + return nil, err + } + + var c *Shard + if lnkLinkType == shardLink { + nd, err := lnk.GetNode(ctx, s.dserv) + if err != nil { + return nil, err + } + cds, err := NewHamtFromDag(s.dserv, nd) + if err != nil { + return nil, err + } + + c = cds + } else { + s, err := s.sd.makeShardValue(lnk) + if err != nil { + return nil, err + } + c = s + } + + s.set(c, i) + + return c, nil +} + +func (s *childer) has(idx int) bool { + return s.bitfield.Bit(idx) +} + +func (s *childer) length() int { + return len(s.children) +} + +func (s *childer) each(ctx context.Context, cb func(*Shard) error) error { + for i := range s.children { + c, err := s.get(ctx, i) + if err != nil { + return err + } + + if err := cb(c); err != nil { + return err + } + } + + return nil +} + +func (s *childer) check(i int) error { + if i >= len(s.children) || i < 0 { + return fmt.Errorf("invalid index passed to operate children (likely corrupt bitfield)") + } + + if len(s.children) != len(s.links) { + return fmt.Errorf("inconsistent lengths between children array and Links array") + } + + return nil +} diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 1483fcd9f..65b79931e 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -9,12 +9,10 @@ import ( "testing" "time" + ipld "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" mdtest "github.com/ipfs/go-merkledag/test" - ft "github.com/ipfs/go-unixfs" - - ipld "github.com/ipfs/go-ipld-format" ) func shuffle(seed int64, arr []string) { @@ -488,11 +486,11 @@ func TestBitfieldIndexing(t *testing.T) { s, _ := NewShard(ds, 256) set := func(i int) { - s.bitfield.SetBit(i) + s.childer.bitfield.SetBit(i) } assert := func(i int, val int) { - if s.indexForBitPos(i) != val { + if s.childer.index(i) != val { t.Fatalf("expected index %d to be %d", i, val) } } diff --git a/unixfs/hamt/util.go b/unixfs/hamt/util.go index c2b33bc22..7ae02dfb3 100644 --- a/unixfs/hamt/util.go +++ b/unixfs/hamt/util.go @@ -2,6 +2,8 @@ package hamt import ( "fmt" + + "github.com/spaolacci/murmur3" "math/bits" ) @@ -58,3 +60,9 @@ func logtwo(v int) (int, error) { } return lg2, nil } + +func hash(val []byte) []byte { + h := murmur3.New64() + h.Write(val) + return h.Sum(nil) +} From f67b0fdc7ce2d99b89455cde3a80db0afed8275e Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Tue, 15 Jan 2019 15:00:32 -0300 Subject: [PATCH 2853/3817] hamt: rename to distinguish between child and slice indexes This commit was moved from ipfs/go-unixfs@0691474159bdefa862a6877f97a769ef8ee425da --- unixfs/hamt/hamt.go | 74 ++++++++++++++++++++++------------------ unixfs/hamt/hamt_test.go | 2 +- 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 17e031502..7947a2aa0 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -145,35 +145,35 @@ func (ds *Shard) Node() (ipld.Node, error) { out := new(dag.ProtoNode) out.SetCidBuilder(ds.builder) - cindex := 0 + sliceIndex := 0 // TODO: optimized 'for each set bit' - for i := 0; i < ds.tableSize; i++ { - if !ds.childer.has(i) { + for childIndex := 0; childIndex < ds.tableSize; childIndex++ { + if !ds.childer.has(childIndex) { continue } - ch := ds.childer.child(cindex) + ch := ds.childer.child(sliceIndex) if ch != nil { clnk, err := ch.Link() if err != nil { return nil, err } - err = out.AddRawLink(ds.linkNamePrefix(i)+ch.key, clnk) + err = out.AddRawLink(ds.linkNamePrefix(childIndex)+ch.key, clnk) if err != nil { return nil, err } } else { // child unloaded, just copy in link with updated name - lnk := ds.childer.link(cindex) + lnk := ds.childer.link(sliceIndex) label := lnk.Name[ds.maxpadlen:] - err := out.AddRawLink(ds.linkNamePrefix(i)+label, lnk) + err := out.AddRawLink(ds.linkNamePrefix(childIndex)+label, lnk) if err != nil { return nil, err } } - cindex++ + sliceIndex++ } data, err := format.HAMTShardData(ds.childer.bitfield.Bytes(), uint64(ds.tableSize), HashMurmur3) @@ -281,12 +281,13 @@ func (ds *Shard) Link() (*ipld.Link, error) { } func (ds *Shard) getValue(ctx context.Context, hv *hashBits, key string, cb func(*Shard) error) error { - idx, err := hv.Next(ds.tableSizeLg2) + childIndex, err := hv.Next(ds.tableSizeLg2) if err != nil { return err } - if ds.childer.has(idx) { - child, err := ds.childer.get(ctx, ds.childer.index(idx)) + + if ds.childer.has(childIndex) { + child, err := ds.childer.get(ctx, ds.childer.sliceIndex(childIndex)) if err != nil { return err } @@ -427,7 +428,7 @@ func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val return ds.childer.insert(key, val, idx) } - i := ds.childer.index(idx) + i := ds.childer.sliceIndex(idx) child, err := ds.childer.get(ctx, i) if err != nil { @@ -507,6 +508,10 @@ func (ds *Shard) linkNamePrefix(idx int) string { // childer wraps the links, children and bitfield // and provides basic operation (get, rm, insert and set) of manipulating children. +// The slices `links` and `children` are always coordinated to have the entries +// in the same index. A `childIndex` belonging to one of the original `Shard.size` +// entries corresponds to a `sliceIndex` in `links` and `children` (the conversion +// is done through `bitfield`). type childer struct { sd *Shard dserv ipld.DAGService @@ -533,16 +538,17 @@ func (s *childer) makeChilder(data []byte, links []*ipld.Link) *childer { return s } -func (s *childer) index(idx int) int { - return s.bitfield.OnesBefore(idx) +// Return the `sliceIndex` associated with a child. +func (s *childer) sliceIndex(childIndex int) (sliceIndex int) { + return s.bitfield.OnesBefore(childIndex) } -func (s *childer) child(i int) *Shard { - return s.children[i] +func (s *childer) child(sliceIndex int) *Shard { + return s.children[sliceIndex] } -func (s *childer) link(i int) *ipld.Link { - return s.links[i] +func (s *childer) link(sliceIndex int) *ipld.Link { + return s.links[sliceIndex] } func (s *childer) insert(key string, lnk *ipld.Link, idx int) error { @@ -551,11 +557,13 @@ func (s *childer) insert(key string, lnk *ipld.Link, idx int) error { } lnk.Name = s.sd.linkNamePrefix(idx) + key - i := s.index(idx) + i := s.sliceIndex(idx) sd := &Shard{key: key, val: lnk} s.children = append(s.children[:i], append([]*Shard{sd}, s.children[i:]...)...) s.links = append(s.links[:i], append([]*ipld.Link{nil}, s.links[i:]...)...) + // Add a `nil` placeholder in `links` so the rest of the entries keep the same + // index as `children`. s.bitfield.SetBit(idx) return nil @@ -565,8 +573,8 @@ func (s *childer) set(sd *Shard, i int) { s.children[i] = sd } -func (s *childer) rm(idx int) error { - i := s.index(idx) +func (s *childer) rm(childIndex int) error { + i := s.sliceIndex(childIndex) if err := s.check(i); err != nil { return err @@ -578,7 +586,7 @@ func (s *childer) rm(idx int) error { copy(s.links[i:], s.links[i+1:]) s.links = s.links[:len(s.links)-1] - s.bitfield.UnsetBit(idx) + s.bitfield.UnsetBit(childIndex) return nil } @@ -586,23 +594,23 @@ func (s *childer) rm(idx int) error { // get returns the i'th child of this shard. If it is cached in the // children array, it will return it from there. Otherwise, it loads the child // node from disk. -func (s *childer) get(ctx context.Context, i int) (*Shard, error) { - if err := s.check(i); err != nil { +func (s *childer) get(ctx context.Context, sliceIndex int) (*Shard, error) { + if err := s.check(sliceIndex); err != nil { return nil, err } - c := s.child(i) + c := s.child(sliceIndex) if c != nil { return c, nil } - return s.loadChild(ctx, i) + return s.loadChild(ctx, sliceIndex) } // loadChild reads the i'th child node of this shard from disk and returns it // as a 'child' interface -func (s *childer) loadChild(ctx context.Context, i int) (*Shard, error) { - lnk := s.link(i) +func (s *childer) loadChild(ctx context.Context, sliceIndex int) (*Shard, error) { + lnk := s.link(sliceIndex) lnkLinkType, err := s.sd.childLinkType(lnk) if err != nil { return nil, err @@ -628,13 +636,13 @@ func (s *childer) loadChild(ctx context.Context, i int) (*Shard, error) { c = s } - s.set(c, i) + s.set(c, sliceIndex) return c, nil } -func (s *childer) has(idx int) bool { - return s.bitfield.Bit(idx) +func (s *childer) has(childIndex int) bool { + return s.bitfield.Bit(childIndex) } func (s *childer) length() int { @@ -656,8 +664,8 @@ func (s *childer) each(ctx context.Context, cb func(*Shard) error) error { return nil } -func (s *childer) check(i int) error { - if i >= len(s.children) || i < 0 { +func (s *childer) check(sliceIndex int) error { + if sliceIndex >= len(s.children) || sliceIndex < 0 { return fmt.Errorf("invalid index passed to operate children (likely corrupt bitfield)") } diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 65b79931e..4025bfb37 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -490,7 +490,7 @@ func TestBitfieldIndexing(t *testing.T) { } assert := func(i int, val int) { - if s.childer.index(i) != val { + if s.childer.sliceIndex(i) != val { t.Fatalf("expected index %d to be %d", i, val) } } From 06e2cf72f2cda060ddb4e60d078e193a65d02177 Mon Sep 17 00:00:00 2001 From: Bamvor Zhang Date: Thu, 16 Aug 2018 16:20:04 +0800 Subject: [PATCH 2854/3817] dag: add fsNodeType in NewLeafNode and NewLeafDataNode NewLeafNode and NewLeafDataNode is introduced in commit 474b77a2bdb1c ("importer: remove `UnixfsNode` from the balanced builder"). It is intended to return ipfs.Node instead of UnixfsNode. But it only support creating the TFile leaf node for merkledag. This commit add fsNodeType to above two functions and update the code in dagbuild.go. Further patches of trickledag will make use of them and pass TRaw to create leaf node. License: MIT Signed-off-by: Bamvor Zhang This commit was moved from ipfs/go-unixfs@c7228b92ac2c00f80ee35c1a21ef2227ad353a87 --- unixfs/importer/balanced/builder.go | 6 +++--- unixfs/importer/helpers/dagbuilder.go | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/unixfs/importer/balanced/builder.go b/unixfs/importer/balanced/builder.go index c1a3e8640..760ab320e 100644 --- a/unixfs/importer/balanced/builder.go +++ b/unixfs/importer/balanced/builder.go @@ -123,7 +123,7 @@ import ( func Layout(db *h.DagBuilderHelper) (ipld.Node, error) { if db.Done() { // No data, return just an empty node. - root, err := db.NewLeafNode(nil) + root, err := db.NewLeafNode(nil, ft.TFile) if err != nil { return nil, err } @@ -137,7 +137,7 @@ func Layout(db *h.DagBuilderHelper) (ipld.Node, error) { // (corner case), after that subsequent `root` nodes will // always be internal nodes (with a depth > 0) that can // be handled by the loop. - root, fileSize, err := db.NewLeafDataNode() + root, fileSize, err := db.NewLeafDataNode(ft.TFile) if err != nil { return nil, err } @@ -224,7 +224,7 @@ func fillNodeRec(db *h.DagBuilderHelper, node *h.FSNodeOverDag, depth int) (fill if depth == 1 { // Base case: add leaf node with data. - childNode, childFileSize, err = db.NewLeafDataNode() + childNode, childFileSize, err = db.NewLeafDataNode(ft.TFile) if err != nil { return nil, 0, err } diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index 24896cd1b..85c8b70aa 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -186,7 +186,7 @@ func (db *DagBuilderHelper) NewLeaf(data []byte) (*UnixfsNode, error) { // NewLeafNode is a variation from `NewLeaf` (see its description) that // returns an `ipld.Node` instead. -func (db *DagBuilderHelper) NewLeafNode(data []byte) (ipld.Node, error) { +func (db *DagBuilderHelper) NewLeafNode(data []byte, fsNodeType pb.Data_DataType) (ipld.Node, error) { if len(data) > BlockSizeLimit { return nil, ErrSizeLimitExceeded } @@ -204,7 +204,7 @@ func (db *DagBuilderHelper) NewLeafNode(data []byte) (ipld.Node, error) { } // Encapsulate the data in UnixFS node (instead of a raw node). - fsNodeOverDag := db.NewFSNodeOverDag(ft.TFile) + fsNodeOverDag := db.NewFSNodeOverDag(fsNodeType) fsNodeOverDag.SetFileData(data) node, err := fsNodeOverDag.Commit() if err != nil { @@ -273,7 +273,7 @@ func (db *DagBuilderHelper) GetNextDataNode() (*UnixfsNode, error) { // used to keep track of the DAG file size). The size of the data is // computed here because after that it will be hidden by `NewLeafNode` // inside a generic `ipld.Node` representation. -func (db *DagBuilderHelper) NewLeafDataNode() (node ipld.Node, dataSize uint64, err error) { +func (db *DagBuilderHelper) NewLeafDataNode(fsNodeType pb.Data_DataType) (node ipld.Node, dataSize uint64, err error) { fileData, err := db.Next() if err != nil { return nil, 0, err @@ -281,7 +281,7 @@ func (db *DagBuilderHelper) NewLeafDataNode() (node ipld.Node, dataSize uint64, dataSize = uint64(len(fileData)) // Create a new leaf node containing the file chunk data. - node, err = db.NewLeafNode(fileData) + node, err = db.NewLeafNode(fileData, fsNodeType) if err != nil { return nil, 0, err } From 1edc96a53c398ad7268b934fa2ac52b17cf75eb9 Mon Sep 17 00:00:00 2001 From: Bamvor Zhang Date: Thu, 16 Aug 2018 16:20:16 +0800 Subject: [PATCH 2855/3817] Docs: update balanced builder document After fsNodeType in NewLeafNode is supported by commit 85897b3f89301 ("dag: add fsNodeType in NewLeafNode and NewLeafDataNode"). Move comments in NewLeafNode to importer/balanced/builder.go to clarify why TFile is used by balanced builder as leaves. License: MIT Signed-off-by: Bamvor Zhang This commit was moved from ipfs/go-unixfs@b56bc9553d3d071808826534c60556156437e866 --- unixfs/importer/balanced/builder.go | 9 +++++++++ unixfs/importer/helpers/dagbuilder.go | 5 ----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/unixfs/importer/balanced/builder.go b/unixfs/importer/balanced/builder.go index 760ab320e..407117dad 100644 --- a/unixfs/importer/balanced/builder.go +++ b/unixfs/importer/balanced/builder.go @@ -16,6 +16,15 @@ // mentioned. This is the only scenario where the root can be of a type different // that the UnixFS node. // +// Notes: +// 1. In the implementation. `FSNodeOverDag` structure is used for representing +// the UnixFS node encoded inside the DAG node. +// (see https://github.com/ipfs/go-ipfs/pull/5118.) +// 2. `TFile` is used for backwards-compatibility. It was a bug causing the leaf +// nodes to be generated with this type instead of `TRaw`. The former one +// should be used (like the trickle builder does). +// (See https://github.com/ipfs/go-ipfs/pull/5120.) +// // +-------------+ // | Root 4 | // +-------------+ diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index 85c8b70aa..be381dc04 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -213,11 +213,6 @@ func (db *DagBuilderHelper) NewLeafNode(data []byte, fsNodeType pb.Data_DataType // TODO: Encapsulate this sequence of calls into a function that // just returns the final `ipld.Node` avoiding going through // `FSNodeOverDag`. - // TODO: Using `TFile` for backwards-compatibility, a bug in the - // balanced builder was causing the leaf nodes to be generated - // with this type instead of `TRaw`, the one that should be used - // (like the trickle builder does). - // (See https://github.com/ipfs/go-ipfs/pull/5120.) return node, nil } From ce96228b9fc547c7b4a54d1c4f39ae3c1b779e3c Mon Sep 17 00:00:00 2001 From: Bamvor Zhang Date: Thu, 16 Aug 2018 16:23:45 +0800 Subject: [PATCH 2856/3817] dag: remove `UnixfsNode` in Layout of trickledag This patch is the part of trickledag work which is similar to the merkledag work in commit 474b77a2bdb1c ("importer: remove `UnixfsNode` from the balanced builder"). Two helper functions(fillTrickleRecFSNode and FillFSNodeLayer) is introduced temporarily for modifing the Layout functions. These two funtions will be removed when all the code of UnixfsNode is removed in trickledag.go. Test ipfs add and get commands to check whether get the same hash of file after the code changes. License: MIT Signed-off-by: Bamvor Zhang This commit was moved from ipfs/go-unixfs@6aa0d7fdd4182374137c8f31cf0b609e458f93aa --- unixfs/importer/helpers/dagbuilder.go | 19 +++++++++++ unixfs/importer/trickle/trickledag.go | 48 +++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index be381dc04..e68f71f7b 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -246,6 +246,25 @@ func (db *DagBuilderHelper) FillNodeLayer(node *UnixfsNode) error { return nil } +// FillFSNodeLayer do the same thing as FillNodeLayer. +func (db *DagBuilderHelper) FillFSNodeLayer(node *FSNodeOverDag) error { + + // while we have room AND we're not done + for node.NumChildren() < db.maxlinks && !db.Done() { + child, childFileSize, err := db.NewLeafDataNode(ft.TRaw) + if err != nil { + return err + } + + if err := node.AddChild(child, childFileSize, db); err != nil { + return err + } + } + node.Commit() + + return nil +} + // GetNextDataNode builds a UnixFsNode with the data obtained from the // Splitter, given the constraints (BlockSizeLimit, RawLeaves) specified // when creating the DagBuilderHelper. diff --git a/unixfs/importer/trickle/trickledag.go b/unixfs/importer/trickle/trickledag.go index 70a953825..626044887 100644 --- a/unixfs/importer/trickle/trickledag.go +++ b/unixfs/importer/trickle/trickledag.go @@ -37,12 +37,13 @@ const layerRepeat = 4 // DagBuilderHelper. See the module's description for a more detailed // explanation. func Layout(db *h.DagBuilderHelper) (ipld.Node, error) { - root := db.NewUnixfsNode() - if err := fillTrickleRec(db, root, -1); err != nil { + newRoot := db.NewFSNodeOverDag(ft.TFile) + root, _, err := fillTrickleRecFSNode(db, newRoot, -1) + if err != nil { return nil, err } - return db.AddUnixfsNode(root) + return root, db.Add(root) } // fillTrickleRec creates a trickle (sub-)tree with an optional maximum specified depth @@ -76,6 +77,47 @@ func fillTrickleRec(db *h.DagBuilderHelper, node *h.UnixfsNode, maxDepth int) er } } +// fillTrickleRecFSNode creates a trickle (sub-)tree with an optional maximum specified depth +// in the case maxDepth is greater than zero, or with unlimited depth otherwise +// (where the DAG builder will signal the end of data to end the function). +func fillTrickleRecFSNode(db *h.DagBuilderHelper, node *h.FSNodeOverDag, maxDepth int) (filledNode ipld.Node, nodeFileSize uint64, err error) { + // Always do this, even in the base case + if err := db.FillFSNodeLayer(node); err != nil { + return nil, 0, err + } + + for depth := 1; ; depth++ { + // Apply depth limit only if the parameter is set (> 0). + if db.Done() || (maxDepth > 0 && depth == maxDepth) { + break + } + for layer := 0; layer < layerRepeat; layer++ { + if db.Done() { + break + } + + nextChild := db.NewFSNodeOverDag(ft.TFile) + childNode, childFileSize, err := fillTrickleRecFSNode(db, nextChild, depth) + if err != nil { + return nil, 0, err + } + + if err := node.AddChild(childNode, childFileSize, db); err != nil { + return nil, 0, err + } + } + } + nodeFileSize = node.FileSize() + + // Get the final `dag.ProtoNode` with the `FSNode` data encoded inside. + filledNode, err = node.Commit() + if err != nil { + return nil, 0, err + } + + return filledNode, nodeFileSize, nil +} + // Append appends the data in `db` to the dag, using the Trickledag format func Append(ctx context.Context, basen ipld.Node, db *h.DagBuilderHelper) (out ipld.Node, errOut error) { base, ok := basen.(*dag.ProtoNode) From b23a60a11ffa64c5d01c096ee29aed86af421a51 Mon Sep 17 00:00:00 2001 From: Bamvor Zhang Date: Mon, 12 Nov 2018 19:33:03 +0800 Subject: [PATCH 2857/3817] Trickle: add new functions for FSNodeOverDag Signed-off-by: Bamvor Zhang This commit was moved from ipfs/go-unixfs@bb3e55c6f23f5f4501a23bd1f05357ca4c2d84d9 --- unixfs/importer/helpers/dagbuilder.go | 39 +++++++++++++++++++++++++++ unixfs/importer/trickle/trickledag.go | 9 +++++++ 2 files changed, 48 insertions(+) diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index e68f71f7b..cb77d07f5 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -402,6 +402,24 @@ func (db *DagBuilderHelper) NewFSNodeOverDag(fsNodeType pb.Data_DataType) *FSNod return node } +// NewFSNFromDag reconstructs a FSNodeOverDag node from a given dag node +func (db *DagBuilderHelper) NewFSNFromDag(nd *dag.ProtoNode) (*FSNodeOverDag, error) { + return NewFSNFromDag(nd) +} + +// NewFSNFromDag reconstructs a FSNodeOverDag node from a given dag node +func NewFSNFromDag(nd *dag.ProtoNode) (*FSNodeOverDag, error) { + mb, err := ft.FSNodeFromBytes(nd.Data()) + if err != nil { + return nil, err + } + + return &FSNodeOverDag{ + dag: nd, + file: mb, + }, nil +} + // AddChild adds a `child` `ipld.Node` to both node layers. The // `dag.ProtoNode` creates a link to the child node while the // `ft.FSNode` stores its file size (that is, not the size of the @@ -450,3 +468,24 @@ func (n *FSNodeOverDag) FileSize() uint64 { func (n *FSNodeOverDag) SetFileData(fileData []byte) { n.file.SetData(fileData) } + +// GetDagNode fills out the proper formatting for the FSNodeOverDag node +// inside of a DAG node and returns the dag node. +func (n *FSNodeOverDag) GetDagNode() (ipld.Node, error) { + return n.dag, nil +} + +// GetChild gets the ith child of this node from the given DAGService. +func (n *FSNodeOverDag) GetChild(ctx context.Context, i int, ds ipld.DAGService) (*FSNodeOverDag, error) { + nd, err := n.dag.Links()[i].GetNode(ctx, ds) + if err != nil { + return nil, err + } + + pbn, ok := nd.(*dag.ProtoNode) + if !ok { + return nil, dag.ErrNotProtobuf + } + + return NewFSNFromDag(pbn) +} diff --git a/unixfs/importer/trickle/trickledag.go b/unixfs/importer/trickle/trickledag.go index 626044887..8eb81ad74 100644 --- a/unixfs/importer/trickle/trickledag.go +++ b/unixfs/importer/trickle/trickledag.go @@ -277,6 +277,15 @@ func trickleDepthInfo(node *h.UnixfsNode, maxlinks int) (int, int) { return ((n - maxlinks) / layerRepeat) + 1, (n - maxlinks) % layerRepeat } +func trickleDepthInfoFSNode(node *h.FSNodeOverDag, maxlinks int) (int, int) { + n := node.NumChildren() + if n < maxlinks { + return 0, 0 + } + + return ((n - maxlinks) / layerRepeat) + 1, (n - maxlinks) % layerRepeat +} + // VerifyParams is used by VerifyTrickleDagStructure type VerifyParams struct { Getter ipld.NodeGetter From 94374fa931e90b9d8be307c5a61a3a5c2bbbda10 Mon Sep 17 00:00:00 2001 From: Bamvor Zhang Date: Sat, 8 Dec 2018 18:29:22 +0800 Subject: [PATCH 2858/3817] dag: remove `UnixfsNode` in Append of trickledag License: MIT Signed-off-by: Bamvor Zhang This commit was moved from ipfs/go-unixfs@a1e4554728693e2e2cea0fcb044ced46a3c251cf --- unixfs/importer/helpers/dagbuilder.go | 6 ++ unixfs/importer/trickle/trickledag.go | 82 ++++++++++++++------------- 2 files changed, 49 insertions(+), 39 deletions(-) diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index cb77d07f5..583fb162d 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -436,6 +436,12 @@ func (n *FSNodeOverDag) AddChild(child ipld.Node, fileSize uint64, db *DagBuilde return db.Add(child) } +// RemoveChild deletes the child node at the given index. +func (n *FSNodeOverDag) RemoveChild(index int, dbh *DagBuilderHelper) { + n.file.RemoveBlockSize(index) + n.dag.SetLinks(append(n.dag.Links()[:index], n.dag.Links()[index+1:]...)) +} + // Commit unifies (resolves) the cache nodes into a single `ipld.Node` // that represents them: the `ft.FSNode` is encoded inside the // `dag.ProtoNode`. diff --git a/unixfs/importer/trickle/trickledag.go b/unixfs/importer/trickle/trickledag.go index 8eb81ad74..840fe0e8b 100644 --- a/unixfs/importer/trickle/trickledag.go +++ b/unixfs/importer/trickle/trickledag.go @@ -126,21 +126,22 @@ func Append(ctx context.Context, basen ipld.Node, db *h.DagBuilderHelper) (out i } // Convert to unixfs node for working with easily - ufsn, err := h.NewUnixfsNodeFromDag(base) + + fsn, err := h.NewFSNFromDag(base) if err != nil { return nil, err } // Get depth of this 'tree' - n, layerProgress := trickleDepthInfo(ufsn, db.Maxlinks()) + n, layerProgress := trickleDepthInfoFSNode(fsn, db.Maxlinks()) if n == 0 { // If direct blocks not filled... - if err := db.FillNodeLayer(ufsn); err != nil { + if err := db.FillFSNodeLayer(fsn); err != nil { return nil, err } if db.Done() { - return ufsn.GetDagNode() + return fsn.GetDagNode() } // If continuing, our depth has increased by one @@ -148,7 +149,7 @@ func Append(ctx context.Context, basen ipld.Node, db *h.DagBuilderHelper) (out i } // Last child in this node may not be a full tree, lets file it up - if err := appendFillLastChild(ctx, ufsn, n-1, layerProgress, db); err != nil { + if err := appendFillLastChild(ctx, fsn, n-1, layerProgress, db); err != nil { return nil, err } @@ -160,44 +161,48 @@ func Append(ctx context.Context, basen ipld.Node, db *h.DagBuilderHelper) (out i // Now, continue filling out tree like normal for i := n; !db.Done(); i++ { for j := 0; j < layerRepeat && !db.Done(); j++ { - next := db.NewUnixfsNode() - err := fillTrickleRec(db, next, i) + nextChild := db.NewFSNodeOverDag(ft.TFile) + childNode, childFileSize, err := fillTrickleRecFSNode(db, nextChild, i) if err != nil { return nil, err } - - err = ufsn.AddChild(next, db) + err = fsn.AddChild(childNode, childFileSize, db) if err != nil { return nil, err } } } - - return ufsn.GetDagNode() + _, err = fsn.Commit() + if err != nil { + return nil, err + } + return fsn.GetDagNode() } -// appendFillLastChild will take in an incomplete trickledag node (uncomplete meaning, not full) and -// fill it out to the specified depth with blocks from the given DagBuilderHelper -func appendFillLastChild(ctx context.Context, ufsn *h.UnixfsNode, depth int, layerFill int, db *h.DagBuilderHelper) error { - if ufsn.NumChildren() <= db.Maxlinks() { +func appendFillLastChild(ctx context.Context, fsn *h.FSNodeOverDag, depth int, layerFill int, db *h.DagBuilderHelper) error { + if fsn.NumChildren() <= db.Maxlinks() { return nil } // Recursive step, grab last child - last := ufsn.NumChildren() - 1 - lastChild, err := ufsn.GetChild(ctx, last, db.GetDagServ()) + last := fsn.NumChildren() - 1 + lastChild, err := fsn.GetChild(ctx, last, db.GetDagServ()) if err != nil { return err } // Fill out last child (may not be full tree) - nchild, err := appendRec(ctx, lastChild, db, depth-1) + nchild, nchildSize, err := appendRec(ctx, lastChild, db, depth-1) if err != nil { return err } // Update changed child in parent node - ufsn.RemoveChild(last, db) - err = ufsn.AddChild(nchild, db) + fsn.RemoveChild(last, db) + filledNode, err := nchild.Commit() + if err != nil { + return err + } + err = fsn.AddChild(filledNode, nchildSize, db) if err != nil { return err } @@ -205,14 +210,13 @@ func appendFillLastChild(ctx context.Context, ufsn *h.UnixfsNode, depth int, lay // Partially filled depth layer if layerFill != 0 { for ; layerFill < layerRepeat && !db.Done(); layerFill++ { - next := db.NewUnixfsNode() - err := fillTrickleRec(db, next, depth) + nextChild := db.NewFSNodeOverDag(ft.TFile) + childNode, childFileSize, err := fillTrickleRecFSNode(db, nextChild, depth) if err != nil { return err } - err = ufsn.AddChild(next, db) - if err != nil { + if err := fsn.AddChild(childNode, childFileSize, db); err != nil { return err } } @@ -222,28 +226,28 @@ func appendFillLastChild(ctx context.Context, ufsn *h.UnixfsNode, depth int, lay } // recursive call for Append -func appendRec(ctx context.Context, ufsn *h.UnixfsNode, db *h.DagBuilderHelper, depth int) (*h.UnixfsNode, error) { +func appendRec(ctx context.Context, fsn *h.FSNodeOverDag, db *h.DagBuilderHelper, depth int) (*h.FSNodeOverDag, uint64, error) { if depth == 0 || db.Done() { - return ufsn, nil + return fsn, fsn.FileSize(), nil } // Get depth of this 'tree' - n, layerProgress := trickleDepthInfo(ufsn, db.Maxlinks()) + n, layerProgress := trickleDepthInfoFSNode(fsn, db.Maxlinks()) if n == 0 { // If direct blocks not filled... - if err := db.FillNodeLayer(ufsn); err != nil { - return nil, err + if err := db.FillFSNodeLayer(fsn); err != nil { + return nil, 0, err } n++ } // If at correct depth, no need to continue if n == depth { - return ufsn, nil + return fsn, fsn.FileSize(), nil } - if err := appendFillLastChild(ctx, ufsn, n, layerProgress, db); err != nil { - return nil, err + if err := appendFillLastChild(ctx, fsn, n, layerProgress, db); err != nil { + return nil, 0, err } // after appendFillLastChild, our depth is now increased by one @@ -254,20 +258,20 @@ func appendRec(ctx context.Context, ufsn *h.UnixfsNode, db *h.DagBuilderHelper, // Now, continue filling out tree like normal for i := n; i < depth && !db.Done(); i++ { for j := 0; j < layerRepeat && !db.Done(); j++ { - next := db.NewUnixfsNode() - if err := fillTrickleRec(db, next, i); err != nil { - return nil, err + nextChild := db.NewFSNodeOverDag(ft.TFile) + childNode, childFileSize, err := fillTrickleRecFSNode(db, nextChild, i) + if err != nil { + return nil, 0, err } - if err := ufsn.AddChild(next, db); err != nil { - return nil, err + if err := fsn.AddChild(childNode, childFileSize, db); err != nil { + return nil, 0, err } } } - return ufsn, nil + return fsn, fsn.FileSize(), nil } - func trickleDepthInfo(node *h.UnixfsNode, maxlinks int) (int, int) { n := node.NumChildren() if n < maxlinks { From d45000d65a121cd11dd5cbaed1d768334691b255 Mon Sep 17 00:00:00 2001 From: Bamvor Zhang Date: Tue, 25 Dec 2018 15:05:57 +0800 Subject: [PATCH 2859/3817] dag: remove fillTrickleRec License: MIT Signed-off-by: Bamvor Zhang This commit was moved from ipfs/go-unixfs@c2ac0aa23df9b09ef2b4d225380b863824b929c8 --- unixfs/importer/trickle/trickledag.go | 43 ++++----------------------- 1 file changed, 6 insertions(+), 37 deletions(-) diff --git a/unixfs/importer/trickle/trickledag.go b/unixfs/importer/trickle/trickledag.go index 840fe0e8b..538ff889c 100644 --- a/unixfs/importer/trickle/trickledag.go +++ b/unixfs/importer/trickle/trickledag.go @@ -38,7 +38,7 @@ const layerRepeat = 4 // explanation. func Layout(db *h.DagBuilderHelper) (ipld.Node, error) { newRoot := db.NewFSNodeOverDag(ft.TFile) - root, _, err := fillTrickleRecFSNode(db, newRoot, -1) + root, _, err := fillTrickleRec(db, newRoot, -1) if err != nil { return nil, err } @@ -49,38 +49,7 @@ func Layout(db *h.DagBuilderHelper) (ipld.Node, error) { // fillTrickleRec creates a trickle (sub-)tree with an optional maximum specified depth // in the case maxDepth is greater than zero, or with unlimited depth otherwise // (where the DAG builder will signal the end of data to end the function). -func fillTrickleRec(db *h.DagBuilderHelper, node *h.UnixfsNode, maxDepth int) error { - // Always do this, even in the base case - if err := db.FillNodeLayer(node); err != nil { - return err - } - - for depth := 1; ; depth++ { - // Apply depth limit only if the parameter is set (> 0). - if maxDepth > 0 && depth == maxDepth { - return nil - } - for layer := 0; layer < layerRepeat; layer++ { - if db.Done() { - return nil - } - - nextChild := db.NewUnixfsNode() - if err := fillTrickleRec(db, nextChild, depth); err != nil { - return err - } - - if err := node.AddChild(nextChild, db); err != nil { - return err - } - } - } -} - -// fillTrickleRecFSNode creates a trickle (sub-)tree with an optional maximum specified depth -// in the case maxDepth is greater than zero, or with unlimited depth otherwise -// (where the DAG builder will signal the end of data to end the function). -func fillTrickleRecFSNode(db *h.DagBuilderHelper, node *h.FSNodeOverDag, maxDepth int) (filledNode ipld.Node, nodeFileSize uint64, err error) { +func fillTrickleRec(db *h.DagBuilderHelper, node *h.FSNodeOverDag, maxDepth int) (filledNode ipld.Node, nodeFileSize uint64, err error) { // Always do this, even in the base case if err := db.FillFSNodeLayer(node); err != nil { return nil, 0, err @@ -97,7 +66,7 @@ func fillTrickleRecFSNode(db *h.DagBuilderHelper, node *h.FSNodeOverDag, maxDept } nextChild := db.NewFSNodeOverDag(ft.TFile) - childNode, childFileSize, err := fillTrickleRecFSNode(db, nextChild, depth) + childNode, childFileSize, err := fillTrickleRec(db, nextChild, depth) if err != nil { return nil, 0, err } @@ -162,7 +131,7 @@ func Append(ctx context.Context, basen ipld.Node, db *h.DagBuilderHelper) (out i for i := n; !db.Done(); i++ { for j := 0; j < layerRepeat && !db.Done(); j++ { nextChild := db.NewFSNodeOverDag(ft.TFile) - childNode, childFileSize, err := fillTrickleRecFSNode(db, nextChild, i) + childNode, childFileSize, err := fillTrickleRec(db, nextChild, i) if err != nil { return nil, err } @@ -211,7 +180,7 @@ func appendFillLastChild(ctx context.Context, fsn *h.FSNodeOverDag, depth int, l if layerFill != 0 { for ; layerFill < layerRepeat && !db.Done(); layerFill++ { nextChild := db.NewFSNodeOverDag(ft.TFile) - childNode, childFileSize, err := fillTrickleRecFSNode(db, nextChild, depth) + childNode, childFileSize, err := fillTrickleRec(db, nextChild, depth) if err != nil { return err } @@ -259,7 +228,7 @@ func appendRec(ctx context.Context, fsn *h.FSNodeOverDag, db *h.DagBuilderHelper for i := n; i < depth && !db.Done(); i++ { for j := 0; j < layerRepeat && !db.Done(); j++ { nextChild := db.NewFSNodeOverDag(ft.TFile) - childNode, childFileSize, err := fillTrickleRecFSNode(db, nextChild, i) + childNode, childFileSize, err := fillTrickleRec(db, nextChild, i) if err != nil { return nil, 0, err } From 3ba76e6f81c4c24fdf5161ebfaa52ddc9ff8adb8 Mon Sep 17 00:00:00 2001 From: Bamvor Zhang Date: Tue, 25 Dec 2018 15:12:33 +0800 Subject: [PATCH 2860/3817] dag: remove trickleDepthInfo License: MIT Signed-off-by: Bamvor Zhang This commit was moved from ipfs/go-unixfs@032dcd44067124c84c11a8fd1150b9dc9ecba0c3 --- unixfs/importer/trickle/trickledag.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/unixfs/importer/trickle/trickledag.go b/unixfs/importer/trickle/trickledag.go index 538ff889c..85dcbd9c1 100644 --- a/unixfs/importer/trickle/trickledag.go +++ b/unixfs/importer/trickle/trickledag.go @@ -102,7 +102,7 @@ func Append(ctx context.Context, basen ipld.Node, db *h.DagBuilderHelper) (out i } // Get depth of this 'tree' - n, layerProgress := trickleDepthInfoFSNode(fsn, db.Maxlinks()) + n, layerProgress := trickleDepthInfo(fsn, db.Maxlinks()) if n == 0 { // If direct blocks not filled... if err := db.FillFSNodeLayer(fsn); err != nil { @@ -201,7 +201,7 @@ func appendRec(ctx context.Context, fsn *h.FSNodeOverDag, db *h.DagBuilderHelper } // Get depth of this 'tree' - n, layerProgress := trickleDepthInfoFSNode(fsn, db.Maxlinks()) + n, layerProgress := trickleDepthInfo(fsn, db.Maxlinks()) if n == 0 { // If direct blocks not filled... if err := db.FillFSNodeLayer(fsn); err != nil { @@ -241,16 +241,8 @@ func appendRec(ctx context.Context, fsn *h.FSNodeOverDag, db *h.DagBuilderHelper return fsn, fsn.FileSize(), nil } -func trickleDepthInfo(node *h.UnixfsNode, maxlinks int) (int, int) { - n := node.NumChildren() - if n < maxlinks { - return 0, 0 - } - - return ((n - maxlinks) / layerRepeat) + 1, (n - maxlinks) % layerRepeat -} -func trickleDepthInfoFSNode(node *h.FSNodeOverDag, maxlinks int) (int, int) { +func trickleDepthInfo(node *h.FSNodeOverDag, maxlinks int) (int, int) { n := node.NumChildren() if n < maxlinks { return 0, 0 From bf16decbe43918df20a67d81b9ceb53b4c37fcee Mon Sep 17 00:00:00 2001 From: Bamvor Zhang Date: Wed, 26 Dec 2018 11:05:03 +0800 Subject: [PATCH 2861/3817] dag: remove the old FillNodeLayer License: MIT Signed-off-by: Bamvor Zhang This commit was moved from ipfs/go-unixfs@183a92b6055d46604946286bb308d998de1b4562 --- unixfs/importer/helpers/dagbuilder.go | 22 ++-------------------- unixfs/importer/trickle/trickledag.go | 6 +++--- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index 583fb162d..5c5e7536a 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -228,26 +228,8 @@ func (db *DagBuilderHelper) newUnixfsBlock() *UnixfsNode { } // FillNodeLayer will add datanodes as children to the give node until -// at most db.indirSize nodes are added. -func (db *DagBuilderHelper) FillNodeLayer(node *UnixfsNode) error { - - // while we have room AND we're not done - for node.NumChildren() < db.maxlinks && !db.Done() { - child, err := db.GetNextDataNode() - if err != nil { - return err - } - - if err := node.AddChild(child, db); err != nil { - return err - } - } - - return nil -} - -// FillFSNodeLayer do the same thing as FillNodeLayer. -func (db *DagBuilderHelper) FillFSNodeLayer(node *FSNodeOverDag) error { +// it is full in this layer or no more data. +func (db *DagBuilderHelper) FillNodeLayer(node *FSNodeOverDag) error { // while we have room AND we're not done for node.NumChildren() < db.maxlinks && !db.Done() { diff --git a/unixfs/importer/trickle/trickledag.go b/unixfs/importer/trickle/trickledag.go index 85dcbd9c1..f798c0eab 100644 --- a/unixfs/importer/trickle/trickledag.go +++ b/unixfs/importer/trickle/trickledag.go @@ -51,7 +51,7 @@ func Layout(db *h.DagBuilderHelper) (ipld.Node, error) { // (where the DAG builder will signal the end of data to end the function). func fillTrickleRec(db *h.DagBuilderHelper, node *h.FSNodeOverDag, maxDepth int) (filledNode ipld.Node, nodeFileSize uint64, err error) { // Always do this, even in the base case - if err := db.FillFSNodeLayer(node); err != nil { + if err := db.FillNodeLayer(node); err != nil { return nil, 0, err } @@ -105,7 +105,7 @@ func Append(ctx context.Context, basen ipld.Node, db *h.DagBuilderHelper) (out i n, layerProgress := trickleDepthInfo(fsn, db.Maxlinks()) if n == 0 { // If direct blocks not filled... - if err := db.FillFSNodeLayer(fsn); err != nil { + if err := db.FillNodeLayer(fsn); err != nil { return nil, err } @@ -204,7 +204,7 @@ func appendRec(ctx context.Context, fsn *h.FSNodeOverDag, db *h.DagBuilderHelper n, layerProgress := trickleDepthInfo(fsn, db.Maxlinks()) if n == 0 { // If direct blocks not filled... - if err := db.FillFSNodeLayer(fsn); err != nil { + if err := db.FillNodeLayer(fsn); err != nil { return nil, 0, err } n++ From c3a67909bfcff15b5a9e6826e3d0af6cbe7a809c Mon Sep 17 00:00:00 2001 From: Bamvor Zhang Date: Wed, 26 Dec 2018 11:12:59 +0800 Subject: [PATCH 2862/3817] dag: Remove UnixfsNode Notes of removing UnixfsNode: - `NewLeafNode` will return the `FSNodeOverDag` with the given `fsNodeType`. While the old `NewLeaf`: `if data is nil the type field will be TRaw (for backwards compatibility), if data is defined (but possibly empty) the type field will be TRaw.`. Not sure if I should follow this. And because of this, I keep the `NewLeafNode` and `NewLeafDataNode`, not rename them to `NewLeaf` and `GetNextDataNode`. - There is no functions in importer/helpers/helpers.go. I am thinking if I should move the `FSNodeOverDag` part of importer/helpers/dagbuilder.go into importer/helpers/helpers.go. - `GetDagNode` return FilestoreNode for RawNode. But I do not understand how it is used in the `DagBuilderHelper.AddChild`. And `FileSize` do not calculate the size of RawNode because there is no flag of Raw in `FSNodeOverDag`. License: MIT Signed-off-by: Bamvor Zhang This commit was moved from ipfs/go-unixfs@c8ae0ec6aa32691397bc0ec68ccce1730d98bd5a --- unixfs/importer/helpers/dagbuilder.go | 104 ++----------------- unixfs/importer/helpers/helpers.go | 144 -------------------------- 2 files changed, 10 insertions(+), 238 deletions(-) diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index 5c5e7536a..ad390d1f5 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -134,58 +134,14 @@ func (db *DagBuilderHelper) GetDagServ() ipld.DAGService { return db.dserv } -// NewUnixfsNode creates a new Unixfs node to represent a file. -func (db *DagBuilderHelper) NewUnixfsNode() *UnixfsNode { - n := &UnixfsNode{ - node: new(dag.ProtoNode), - ufmt: ft.NewFSNode(ft.TFile), - } - n.SetCidBuilder(db.cidBuilder) - return n -} - // GetCidBuilder returns the internal `cid.CidBuilder` set in the builder. func (db *DagBuilderHelper) GetCidBuilder() cid.Builder { return db.cidBuilder } -// NewLeaf creates a leaf node filled with data. If rawLeaves is -// defined than a raw leaf will be returned. Otherwise, if data is -// nil the type field will be TRaw (for backwards compatibility), if -// data is defined (but possibly empty) the type field will be TRaw. -func (db *DagBuilderHelper) NewLeaf(data []byte) (*UnixfsNode, error) { - if len(data) > BlockSizeLimit { - return nil, ErrSizeLimitExceeded - } - - if db.rawLeaves { - if db.cidBuilder == nil { - return &UnixfsNode{ - rawnode: dag.NewRawNode(data), - raw: true, - }, nil - } - rawnode, err := dag.NewRawNodeWPrefix(data, db.cidBuilder) - if err != nil { - return nil, err - } - return &UnixfsNode{ - rawnode: rawnode, - raw: true, - }, nil - } - - if data == nil { - return db.NewUnixfsNode(), nil - } - - blk := db.newUnixfsBlock() - blk.SetData(data) - return blk, nil -} - -// NewLeafNode is a variation from `NewLeaf` (see its description) that -// returns an `ipld.Node` instead. +// NewLeafNode creates a leaf node filled with data. If rawLeaves is +// defined then a raw leaf will be returned. Otherwise, it will create +// and return `FSNodeOverDag` with `fsNodeType`. func (db *DagBuilderHelper) NewLeafNode(data []byte, fsNodeType pb.Data_DataType) (ipld.Node, error) { if len(data) > BlockSizeLimit { return nil, ErrSizeLimitExceeded @@ -217,16 +173,6 @@ func (db *DagBuilderHelper) NewLeafNode(data []byte, fsNodeType pb.Data_DataType return node, nil } -// newUnixfsBlock creates a new Unixfs node to represent a raw data block -func (db *DagBuilderHelper) newUnixfsBlock() *UnixfsNode { - n := &UnixfsNode{ - node: new(dag.ProtoNode), - ufmt: ft.NewFSNode(ft.TRaw), - } - n.SetCidBuilder(db.cidBuilder) - return n -} - // FillNodeLayer will add datanodes as children to the give node until // it is full in this layer or no more data. func (db *DagBuilderHelper) FillNodeLayer(node *FSNodeOverDag) error { @@ -247,28 +193,13 @@ func (db *DagBuilderHelper) FillNodeLayer(node *FSNodeOverDag) error { return nil } -// GetNextDataNode builds a UnixFsNode with the data obtained from the -// Splitter, given the constraints (BlockSizeLimit, RawLeaves) specified -// when creating the DagBuilderHelper. -func (db *DagBuilderHelper) GetNextDataNode() (*UnixfsNode, error) { - data, err := db.Next() - if err != nil { - return nil, err - } - - if data == nil { // we're done! - return nil, nil - } - - return db.NewLeaf(data) -} - -// NewLeafDataNode is a variation of `GetNextDataNode` that returns -// an `ipld.Node` instead. It builds the `node` with the data obtained -// from the Splitter and returns it with the `dataSize` (that will be -// used to keep track of the DAG file size). The size of the data is -// computed here because after that it will be hidden by `NewLeafNode` -// inside a generic `ipld.Node` representation. +// NewLeafDataNode builds the `node` with the data obtained from the +// Splitter with the given constraints (BlockSizeLimit, RawLeaves) +// specified when creating the DagBuilderHelper. It returns +// `ipld.Node` with the `dataSize` (that will be used to keep track of +// the DAG file size). The size of the data is computed here because +// after that it will be hidden by `NewLeafNode` inside a generic +// `ipld.Node` representation. func (db *DagBuilderHelper) NewLeafDataNode(fsNodeType pb.Data_DataType) (node ipld.Node, dataSize uint64, err error) { fileData, err := db.Next() if err != nil { @@ -322,21 +253,6 @@ func (db *DagBuilderHelper) ProcessFileStore(node ipld.Node, dataSize uint64) ip return node } -// AddUnixfsNode sends a node to the DAGService, and returns it as ipld.Node. -func (db *DagBuilderHelper) AddUnixfsNode(node *UnixfsNode) (ipld.Node, error) { - dn, err := node.GetDagNode() - if err != nil { - return nil, err - } - - err = db.dserv.Add(context.TODO(), dn) - if err != nil { - return nil, err - } - - return dn, nil -} - // Add inserts the given node in the DAGService. func (db *DagBuilderHelper) Add(node ipld.Node) error { return db.dserv.Add(context.TODO(), node) diff --git a/unixfs/importer/helpers/helpers.go b/unixfs/importer/helpers/helpers.go index 75d013090..075b2d2d2 100644 --- a/unixfs/importer/helpers/helpers.go +++ b/unixfs/importer/helpers/helpers.go @@ -1,17 +1,7 @@ package helpers import ( - "context" "fmt" - "os" - - dag "github.com/ipfs/go-merkledag" - - ft "github.com/ipfs/go-unixfs" - - cid "github.com/ipfs/go-cid" - pi "github.com/ipfs/go-ipfs-posinfo" - ipld "github.com/ipfs/go-ipld-format" ) // BlockSizeLimit specifies the maximum size an imported block can have. @@ -38,137 +28,3 @@ var DefaultLinksPerBlock = roughLinkBlockSize / roughLinkSize // ErrSizeLimitExceeded signals that a block is larger than BlockSizeLimit. var ErrSizeLimitExceeded = fmt.Errorf("object size limit exceeded") - -// UnixfsNode is a struct created to aid in the generation -// of unixfs DAG trees -type UnixfsNode struct { - raw bool - rawnode *dag.RawNode - node *dag.ProtoNode - ufmt *ft.FSNode - posInfo *pi.PosInfo -} - -// NewUnixfsNodeFromDag reconstructs a Unixfs node from a given dag node -func NewUnixfsNodeFromDag(nd *dag.ProtoNode) (*UnixfsNode, error) { - mb, err := ft.FSNodeFromBytes(nd.Data()) - if err != nil { - return nil, err - } - - return &UnixfsNode{ - node: nd, - ufmt: mb, - }, nil -} - -// SetCidBuilder sets the CID Builder -func (n *UnixfsNode) SetCidBuilder(builder cid.Builder) { - n.node.SetCidBuilder(builder) -} - -// NumChildren returns the number of children referenced by this UnixfsNode. -func (n *UnixfsNode) NumChildren() int { - return n.ufmt.NumChildren() -} - -// GetChild gets the ith child of this node from the given DAGService. -func (n *UnixfsNode) GetChild(ctx context.Context, i int, ds ipld.DAGService) (*UnixfsNode, error) { - nd, err := n.node.Links()[i].GetNode(ctx, ds) - if err != nil { - return nil, err - } - - pbn, ok := nd.(*dag.ProtoNode) - if !ok { - return nil, dag.ErrNotProtobuf - } - - return NewUnixfsNodeFromDag(pbn) -} - -// AddChild adds the given UnixfsNode as a child of the receiver. -// The passed in DagBuilderHelper is used to store the child node and -// pin it locally so it doesnt get lost. -func (n *UnixfsNode) AddChild(child *UnixfsNode, db *DagBuilderHelper) error { - n.ufmt.AddBlockSize(child.FileSize()) - - childnode, err := child.GetDagNode() - if err != nil { - return err - } - - // Add a link to this node without storing a reference to the memory - // This way, we avoid nodes building up and consuming all of our RAM - err = n.node.AddNodeLink("", childnode) - if err != nil { - return err - } - - _, err = db.AddUnixfsNode(child) - return err -} - -// RemoveChild deletes the child node at the given index. -func (n *UnixfsNode) RemoveChild(index int, dbh *DagBuilderHelper) { - n.ufmt.RemoveBlockSize(index) - n.node.SetLinks(append(n.node.Links()[:index], n.node.Links()[index+1:]...)) -} - -// SetData stores data in this node. -func (n *UnixfsNode) SetData(data []byte) { - n.ufmt.SetData(data) -} - -// FileSize returns the total file size of this tree (including children) -// In the case of raw nodes, it returns the length of the -// raw data. -func (n *UnixfsNode) FileSize() uint64 { - if n.raw { - return uint64(len(n.rawnode.RawData())) - } - return n.ufmt.FileSize() -} - -// SetPosInfo sets information about the offset of the data of this node in a -// filesystem file. -func (n *UnixfsNode) SetPosInfo(offset uint64, fullPath string, stat os.FileInfo) { - n.posInfo = &pi.PosInfo{ - Offset: offset, - FullPath: fullPath, - Stat: stat, - } -} - -// GetDagNode fills out the proper formatting for the unixfs node -// inside of a DAG node and returns the dag node. -func (n *UnixfsNode) GetDagNode() (ipld.Node, error) { - nd, err := n.getBaseDagNode() - if err != nil { - return nil, err - } - - if n.posInfo != nil { - if rn, ok := nd.(*dag.RawNode); ok { - return &pi.FilestoreNode{ - Node: rn, - PosInfo: n.posInfo, - }, nil - } - } - - return nd, nil -} - -func (n *UnixfsNode) getBaseDagNode() (ipld.Node, error) { - if n.raw { - return n.rawnode, nil - } - - data, err := n.ufmt.GetBytes() - if err != nil { - return nil, err - } - n.node.SetData(data) - return n.node, nil -} From 70a49ca7e634c87f018df336e95cdbf5ac2ffc4d Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Tue, 15 Jan 2019 16:58:41 -0300 Subject: [PATCH 2863/3817] trickle: document `fillTrickleRec` and `trickleDepthInfo` This commit was moved from ipfs/go-unixfs@207a325720b4c4fa8bc2fa365206a641d0e1b836 --- unixfs/importer/trickle/trickle_test.go | 6 +-- unixfs/importer/trickle/trickledag.go | 54 ++++++++++++++++--------- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/unixfs/importer/trickle/trickle_test.go b/unixfs/importer/trickle/trickle_test.go index 9c568c986..2067a24e3 100644 --- a/unixfs/importer/trickle/trickle_test.go +++ b/unixfs/importer/trickle/trickle_test.go @@ -52,7 +52,7 @@ func buildTestDag(ds ipld.DAGService, spl chunker.Splitter, rawLeaves UseRawLeav return pbnd, VerifyTrickleDagStructure(pbnd, VerifyParams{ Getter: ds, Direct: dbp.Maxlinks, - LayerRepeat: layerRepeat, + LayerRepeat: depthRepeat, RawLeaves: bool(rawLeaves), }) } @@ -511,7 +511,7 @@ func testAppend(t *testing.T, rawLeaves UseRawLeaves) { err = VerifyTrickleDagStructure(nnode, VerifyParams{ Getter: ds, Direct: dbp.Maxlinks, - LayerRepeat: layerRepeat, + LayerRepeat: depthRepeat, RawLeaves: bool(rawLeaves), }) if err != nil { @@ -572,7 +572,7 @@ func testMultipleAppends(t *testing.T, rawLeaves UseRawLeaves) { err = VerifyTrickleDagStructure(nnode, VerifyParams{ Getter: ds, Direct: dbp.Maxlinks, - LayerRepeat: layerRepeat, + LayerRepeat: depthRepeat, RawLeaves: bool(rawLeaves), }) if err != nil { diff --git a/unixfs/importer/trickle/trickledag.go b/unixfs/importer/trickle/trickledag.go index f798c0eab..d975de909 100644 --- a/unixfs/importer/trickle/trickledag.go +++ b/unixfs/importer/trickle/trickledag.go @@ -28,10 +28,10 @@ import ( dag "github.com/ipfs/go-merkledag" ) -// layerRepeat specifies how many times to append a child tree of a +// depthRepeat specifies how many times to append a child tree of a // given depth. Higher values increase the width of a given node, which // improves seek speeds. -const layerRepeat = 4 +const depthRepeat = 4 // Layout builds a new DAG with the trickle format using the provided // DagBuilderHelper. See the module's description for a more detailed @@ -55,18 +55,18 @@ func fillTrickleRec(db *h.DagBuilderHelper, node *h.FSNodeOverDag, maxDepth int) return nil, 0, err } - for depth := 1; ; depth++ { - // Apply depth limit only if the parameter is set (> 0). - if db.Done() || (maxDepth > 0 && depth == maxDepth) { + // For each depth in [1, `maxDepth`) (or without limit if `maxDepth` is -1, + // initial call from `Layout`) add `depthRepeat` sub-graphs of that depth. + for depth := 1; maxDepth == -1 || depth < maxDepth; depth++ { + if db.Done() { break + // No more data, stop here, posterior append calls will figure out + // where we left off. } - for layer := 0; layer < layerRepeat; layer++ { - if db.Done() { - break - } - nextChild := db.NewFSNodeOverDag(ft.TFile) - childNode, childFileSize, err := fillTrickleRec(db, nextChild, depth) + for repeatIndex := 0; repeatIndex < depthRepeat && !db.Done(); repeatIndex++ { + + childNode, childFileSize, err := fillTrickleRec(db, db.NewFSNodeOverDag(ft.TFile), depth) if err != nil { return nil, 0, err } @@ -76,7 +76,6 @@ func fillTrickleRec(db *h.DagBuilderHelper, node *h.FSNodeOverDag, maxDepth int) } } } - nodeFileSize = node.FileSize() // Get the final `dag.ProtoNode` with the `FSNode` data encoded inside. filledNode, err = node.Commit() @@ -84,7 +83,7 @@ func fillTrickleRec(db *h.DagBuilderHelper, node *h.FSNodeOverDag, maxDepth int) return nil, 0, err } - return filledNode, nodeFileSize, nil + return filledNode, node.FileSize(), nil } // Append appends the data in `db` to the dag, using the Trickledag format @@ -129,7 +128,7 @@ func Append(ctx context.Context, basen ipld.Node, db *h.DagBuilderHelper) (out i // Now, continue filling out tree like normal for i := n; !db.Done(); i++ { - for j := 0; j < layerRepeat && !db.Done(); j++ { + for j := 0; j < depthRepeat && !db.Done(); j++ { nextChild := db.NewFSNodeOverDag(ft.TFile) childNode, childFileSize, err := fillTrickleRec(db, nextChild, i) if err != nil { @@ -178,7 +177,7 @@ func appendFillLastChild(ctx context.Context, fsn *h.FSNodeOverDag, depth int, l // Partially filled depth layer if layerFill != 0 { - for ; layerFill < layerRepeat && !db.Done(); layerFill++ { + for ; layerFill < depthRepeat && !db.Done(); layerFill++ { nextChild := db.NewFSNodeOverDag(ft.TFile) childNode, childFileSize, err := fillTrickleRec(db, nextChild, depth) if err != nil { @@ -226,7 +225,7 @@ func appendRec(ctx context.Context, fsn *h.FSNodeOverDag, db *h.DagBuilderHelper // Now, continue filling out tree like normal for i := n; i < depth && !db.Done(); i++ { - for j := 0; j < layerRepeat && !db.Done(); j++ { + for j := 0; j < depthRepeat && !db.Done(); j++ { nextChild := db.NewFSNodeOverDag(ft.TFile) childNode, childFileSize, err := fillTrickleRec(db, nextChild, i) if err != nil { @@ -242,13 +241,32 @@ func appendRec(ctx context.Context, fsn *h.FSNodeOverDag, db *h.DagBuilderHelper return fsn, fsn.FileSize(), nil } -func trickleDepthInfo(node *h.FSNodeOverDag, maxlinks int) (int, int) { +// Deduce where we left off in `fillTrickleRec`, returns the `depth` +// with which new sub-graphs were being added and, within that depth, +// in which `repeatNumber` of the total `depthRepeat` we should add. +func trickleDepthInfo(node *h.FSNodeOverDag, maxlinks int) (depth int, repeatNumber int) { n := node.NumChildren() + if n < maxlinks { + // We didn't even added the initial `maxlinks` leaf nodes (`FillNodeLayer`). return 0, 0 } - return ((n - maxlinks) / layerRepeat) + 1, (n - maxlinks) % layerRepeat + nonLeafChildren := n - maxlinks + // The number of non-leaf child nodes added in `fillTrickleRec` (after + // the `FillNodeLayer` call). + + depth = nonLeafChildren/depthRepeat + 1 + // "Deduplicate" the added `depthRepeat` sub-graphs at each depth + // (rounding it up since we may be on an unfinished depth with less + // than `depthRepeat` sub-graphs). + + repeatNumber = nonLeafChildren % depthRepeat + // What's left after taking full depths of `depthRepeat` sub-graphs + // is the current `repeatNumber` we're at (this fractional part is + // what we rounded up before). + + return } // VerifyParams is used by VerifyTrickleDagStructure From 58dbc6a5af3ae21ca2b56325bfddd34475a64891 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Tue, 15 Jan 2019 21:25:22 -0300 Subject: [PATCH 2864/3817] trickle: rename variables according to new `trickleDepthInfo` names This commit was moved from ipfs/go-unixfs@b1b1f17daaff16eebe63e6e6a22cdc6cbf4b38e1 --- unixfs/importer/trickle/trickledag.go | 48 +++++++++++++++------------ 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/unixfs/importer/trickle/trickledag.go b/unixfs/importer/trickle/trickledag.go index d975de909..3a631adb8 100644 --- a/unixfs/importer/trickle/trickledag.go +++ b/unixfs/importer/trickle/trickledag.go @@ -101,33 +101,35 @@ func Append(ctx context.Context, basen ipld.Node, db *h.DagBuilderHelper) (out i } // Get depth of this 'tree' - n, layerProgress := trickleDepthInfo(fsn, db.Maxlinks()) - if n == 0 { + depth, repeatNumber := trickleDepthInfo(fsn, db.Maxlinks()) + if depth == 0 { // If direct blocks not filled... if err := db.FillNodeLayer(fsn); err != nil { return nil, err } if db.Done() { + // TODO: If `FillNodeLayer` stop `Commit`ing this should be + // the place (besides the function end) to call it. return fsn.GetDagNode() } // If continuing, our depth has increased by one - n++ + depth++ } - // Last child in this node may not be a full tree, lets file it up - if err := appendFillLastChild(ctx, fsn, n-1, layerProgress, db); err != nil { + // Last child in this node may not be a full tree, lets fill it up. + if err := appendFillLastChild(ctx, fsn, depth-1, repeatNumber, db); err != nil { return nil, err } // after appendFillLastChild, our depth is now increased by one if !db.Done() { - n++ + depth++ } // Now, continue filling out tree like normal - for i := n; !db.Done(); i++ { + for i := depth; !db.Done(); i++ { for j := 0; j < depthRepeat && !db.Done(); j++ { nextChild := db.NewFSNodeOverDag(ft.TFile) childNode, childFileSize, err := fillTrickleRec(db, nextChild, i) @@ -147,10 +149,13 @@ func Append(ctx context.Context, basen ipld.Node, db *h.DagBuilderHelper) (out i return fsn.GetDagNode() } -func appendFillLastChild(ctx context.Context, fsn *h.FSNodeOverDag, depth int, layerFill int, db *h.DagBuilderHelper) error { +func appendFillLastChild(ctx context.Context, fsn *h.FSNodeOverDag, depth int, repeatNumber int, db *h.DagBuilderHelper) error { if fsn.NumChildren() <= db.Maxlinks() { return nil } + // TODO: Why do we need this check, didn't the caller already take + // care of this? + // Recursive step, grab last child last := fsn.NumChildren() - 1 lastChild, err := fsn.GetChild(ctx, last, db.GetDagServ()) @@ -159,14 +164,14 @@ func appendFillLastChild(ctx context.Context, fsn *h.FSNodeOverDag, depth int, l } // Fill out last child (may not be full tree) - nchild, nchildSize, err := appendRec(ctx, lastChild, db, depth-1) + newChild, nchildSize, err := appendRec(ctx, lastChild, db, depth-1) if err != nil { return err } // Update changed child in parent node fsn.RemoveChild(last, db) - filledNode, err := nchild.Commit() + filledNode, err := newChild.Commit() if err != nil { return err } @@ -176,8 +181,8 @@ func appendFillLastChild(ctx context.Context, fsn *h.FSNodeOverDag, depth int, l } // Partially filled depth layer - if layerFill != 0 { - for ; layerFill < depthRepeat && !db.Done(); layerFill++ { + if repeatNumber != 0 { + for ; repeatNumber < depthRepeat && !db.Done(); repeatNumber++ { nextChild := db.NewFSNodeOverDag(ft.TFile) childNode, childFileSize, err := fillTrickleRec(db, nextChild, depth) if err != nil { @@ -194,37 +199,38 @@ func appendFillLastChild(ctx context.Context, fsn *h.FSNodeOverDag, depth int, l } // recursive call for Append -func appendRec(ctx context.Context, fsn *h.FSNodeOverDag, db *h.DagBuilderHelper, depth int) (*h.FSNodeOverDag, uint64, error) { - if depth == 0 || db.Done() { +func appendRec(ctx context.Context, fsn *h.FSNodeOverDag, db *h.DagBuilderHelper, maxDepth int) (*h.FSNodeOverDag, uint64, error) { + if maxDepth == 0 || db.Done() { return fsn, fsn.FileSize(), nil } // Get depth of this 'tree' - n, layerProgress := trickleDepthInfo(fsn, db.Maxlinks()) - if n == 0 { + depth, repeatNumber := trickleDepthInfo(fsn, db.Maxlinks()) + if depth == 0 { // If direct blocks not filled... if err := db.FillNodeLayer(fsn); err != nil { return nil, 0, err } - n++ + depth++ } + // TODO: Same as `appendFillLastChild`, when is this case possible? // If at correct depth, no need to continue - if n == depth { + if depth == maxDepth { return fsn, fsn.FileSize(), nil } - if err := appendFillLastChild(ctx, fsn, n, layerProgress, db); err != nil { + if err := appendFillLastChild(ctx, fsn, depth, repeatNumber, db); err != nil { return nil, 0, err } // after appendFillLastChild, our depth is now increased by one if !db.Done() { - n++ + depth++ } // Now, continue filling out tree like normal - for i := n; i < depth && !db.Done(); i++ { + for i := depth; i < maxDepth && !db.Done(); i++ { for j := 0; j < depthRepeat && !db.Done(); j++ { nextChild := db.NewFSNodeOverDag(ft.TFile) childNode, childFileSize, err := fillTrickleRec(db, nextChild, i) From 5d0cb2f837f9ce4809aad56beb526888a291f48d Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Tue, 15 Jan 2019 21:37:18 -0300 Subject: [PATCH 2865/3817] helpers: doc and TODOs This commit was moved from ipfs/go-unixfs@2ea1b470b1d8d6418fc0236df2937b126b2183e2 --- unixfs/importer/helpers/dagbuilder.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index ad390d1f5..891cdaa4d 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -175,6 +175,8 @@ func (db *DagBuilderHelper) NewLeafNode(data []byte, fsNodeType pb.Data_DataType // FillNodeLayer will add datanodes as children to the give node until // it is full in this layer or no more data. +// NOTE: This function creates raw data nodes so it only works +// for the `trickle.Layout`. func (db *DagBuilderHelper) FillNodeLayer(node *FSNodeOverDag) error { // while we have room AND we're not done @@ -189,6 +191,8 @@ func (db *DagBuilderHelper) FillNodeLayer(node *FSNodeOverDag) error { } } node.Commit() + // TODO: Do we need to commit here? The caller who created the + // `FSNodeOverDag` should be in charge of that. return nil } @@ -344,7 +348,7 @@ func (n *FSNodeOverDag) RemoveChild(index int, dbh *DagBuilderHelper) { // that represents them: the `ft.FSNode` is encoded inside the // `dag.ProtoNode`. // -// TODO: Evaluate making it read-only after committing. +// TODO: Make it read-only after committing, allow to commit only once. func (n *FSNodeOverDag) Commit() (ipld.Node, error) { fileData, err := n.file.GetBytes() if err != nil { @@ -375,6 +379,8 @@ func (n *FSNodeOverDag) SetFileData(fileData []byte) { // GetDagNode fills out the proper formatting for the FSNodeOverDag node // inside of a DAG node and returns the dag node. +// TODO: Check if we have committed (passed the UnixFS information +// to the DAG layer) before returning this. func (n *FSNodeOverDag) GetDagNode() (ipld.Node, error) { return n.dag, nil } From 9d818692576545efe3be8a5c775156e48766367e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 17 Jan 2019 16:53:12 +0000 Subject: [PATCH 2866/3817] nit: validate CIDs in IPLD paths This commit was moved from ipfs/go-path@261f0f7e43da7b2529505608d1818be74deb0d83 --- path/path.go | 14 ++++++++++---- path/path_test.go | 9 +++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/path/path.go b/path/path.go index 7754ef1ea..eada90d8f 100644 --- a/path/path.go +++ b/path/path.go @@ -106,7 +106,7 @@ func ParsePath(txt string) (Path, error) { // if the path doesnt begin with a '/' // we expect this to start with a hash, and be an 'ipfs' path if parts[0] != "" { - if _, err := ParseCidToPath(parts[0]); err != nil { + if _, err := cid.Decode(parts[0]); err != nil { return "", ErrBadPath } // The case when the path starts with hash without a protocol prefix @@ -117,11 +117,17 @@ func ParsePath(txt string) (Path, error) { return "", ErrBadPath } - if parts[1] == "ipfs" { - if _, err := ParseCidToPath(parts[2]); err != nil { + //TODO: make this smarter + switch parts[1] { + case "ipfs", "ipld": + // Validate Cid. + _, err := cid.Decode(parts[2]) + if err != nil { return "", err } - } else if parts[1] != "ipns" && parts[1] != "ipld" { //TODO: make this smarter + case "ipns": + // No validation. + default: return "", ErrBadPath } diff --git a/path/path_test.go b/path/path_test.go index db28193c8..a166e713d 100644 --- a/path/path_test.go +++ b/path/path_test.go @@ -18,9 +18,14 @@ func TestPathParsing(t *testing.T) { "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true, "/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": false, "/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": false, - "/ipfs/": false, - "ipfs/": false, + "/ipfs/foo": false, + "/ipfs/": false, + "ipfs/": false, "ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": false, + "/ipld/foo": false, + "/ipld/": false, + "ipld/": false, + "ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": false, } for p, expected := range cases { From b1caa58eae01f9d6623c504be4085c32ebc675f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 11 Jan 2019 12:49:50 +0100 Subject: [PATCH 2867/3817] ls: report real size by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@d48b9e1c1f2c70766f5fd1cb13f872ec86075d4d --- coreiface/tests/unixfs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index e8a1aba32..9e1454c41 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -704,8 +704,8 @@ func (tp *provider) TestLs(t *testing.T) { if len(links) != 1 { t.Fatalf("expected 1 link, got %d", len(links)) } - if links[0].Size != 23 { - t.Fatalf("expected size = 23, got %d", links[0].Size) + if links[0].Size != 15 { + t.Fatalf("expected size = 15, got %d", links[0].Size) } if links[0].Name != "name-of-file" { t.Fatalf("expected name = name-of-file, got %s", links[0].Name) From 100779d936550209ee7020525cb7b3ff0639787f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 11 Jan 2019 13:16:47 +0100 Subject: [PATCH 2868/3817] ls: skip size for directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@6c323ff16d39002eec98b8ae6337f7050937e0d8 --- coreiface/tests/unixfs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 9e1454c41..e8a1aba32 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -704,8 +704,8 @@ func (tp *provider) TestLs(t *testing.T) { if len(links) != 1 { t.Fatalf("expected 1 link, got %d", len(links)) } - if links[0].Size != 15 { - t.Fatalf("expected size = 15, got %d", links[0].Size) + if links[0].Size != 23 { + t.Fatalf("expected size = 23, got %d", links[0].Size) } if links[0].Name != "name-of-file" { t.Fatalf("expected name = name-of-file, got %s", links[0].Name) From 7448e658e8687d8976bef3a42bbc1d1aa69ad969 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 22 Nov 2018 04:16:45 -0500 Subject: [PATCH 2869/3817] Add global --cid-base option and enable it for most commands. This does it on ther server side for most commands. This also adds a global --output-cidv1 option. License: MIT Signed-off-by: Kevin Atkinson This commit was moved from ipfs/go-filestore@682a30e88ed7b65560b4cc2a3a301244c48afc31 --- filestore/util.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/filestore/util.go b/filestore/util.go index af25da272..a4f1b9732 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -66,15 +66,18 @@ type ListRes struct { Size uint64 } -// FormatLong returns a human readable string for a ListRes object. -func (r *ListRes) FormatLong() string { +// FormatLong returns a human readable string for a ListRes object +func (r *ListRes) FormatLong(enc func(cid.Cid) string) string { + if enc == nil { + enc = (cid.Cid).String + } switch { case !r.Key.Defined(): return "" case r.FilePath == "": return r.Key.String() default: - return fmt.Sprintf("%-50s %6d %s %d", r.Key, r.Size, r.FilePath, r.Offset) + return fmt.Sprintf("%-50s %6d %s %d", enc(r.Key), r.Size, r.FilePath, r.Offset) } } From 14775e570f295908e29ec9ecb04acb8237435c72 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Thu, 17 Jan 2019 01:30:27 -0300 Subject: [PATCH 2870/3817] mod: `TestDagSync`, seek before reading This commit was moved from ipfs/go-unixfs@63cc1b69aabd36604b0f7f249c006c45866a81eb --- unixfs/mod/dagmodifier_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index b61369362..9870b2022 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -453,6 +453,11 @@ func TestDagSync(t *testing.T) { t.Fatal(err) } + _, err = dagmod.Seek(0, io.SeekStart) + if err != nil { + t.Fatal(err) + } + out, err := ioutil.ReadAll(dagmod) if err != nil { t.Fatal(err) From 3d978107f2e1b6ffe0d6183bdefeeb5ddbd7905e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 20 Jan 2019 16:59:22 +0100 Subject: [PATCH 2871/3817] Don't error on closed exchange This commit was moved from ipfs/go-blockservice@3d57ac5823071e4249e896a5bcc759613e71ca88 --- blockservice/blockservice.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 2e1de3f7a..3b5a1df6b 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -6,7 +6,6 @@ package blockservice import ( "context" "errors" - "fmt" "io" "sync" @@ -150,8 +149,7 @@ func (s *blockService) AddBlock(o blocks.Block) error { log.Event(context.TODO(), "BlockService.BlockAdded", c) if err := s.exchange.HasBlock(o); err != nil { - // TODO(#4623): really an error? - return errors.New("blockservice is closed") + log.Errorf("HasBlock: %s", err.Error()) } return nil @@ -189,8 +187,7 @@ func (s *blockService) AddBlocks(bs []blocks.Block) error { for _, o := range toput { log.Event(context.TODO(), "BlockService.BlockAdded", o.Cid()) if err := s.exchange.HasBlock(o); err != nil { - // TODO(#4623): Should this really *return*? - return fmt.Errorf("blockservice is closed (%s)", err) + log.Errorf("HasBlock: %s", err.Error()) } } return nil From b92dcf5817ba9154db34f6156ad70836b7669b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 21 Jan 2019 21:31:52 +0100 Subject: [PATCH 2872/3817] coreapi: few more error check fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@b4e7753bacca7684d5e70294f766948fd77bf1c7 --- coreiface/tests/dag.go | 2 +- coreiface/tests/path.go | 2 +- coreiface/tests/unixfs.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index e66106c33..10fab125a 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -185,7 +185,7 @@ func (tp *provider) TestBatch(t *testing.T) { } _, err = api.Dag().Get(ctx, nds[0].Cid()) - if err == nil || err.Error() != "merkledag: not found" { + if err == nil || !strings.Contains(err.Error(), "not found") { t.Error(err) } diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index 01f2e6f36..e7df6f1fb 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -151,7 +151,7 @@ func (tp *provider) TestInvalidPathRemainder(t *testing.T) { } _, err = api.ResolvePath(ctx, p1) - if err == nil || err.Error() != "no such link found" { + if err == nil || !strings.Contains(err.Error(), "no such link found") { t.Fatalf("unexpected error: %s", err) } } diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index fce41ae84..0ef3f031e 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -587,7 +587,7 @@ func (tp *provider) TestAddHashOnly(t *testing.T) { if err == nil { t.Fatal("expected an error") } - if err.Error() != "blockservice: key not found" { + if !strings.Contains(err.Error(), "blockservice: key not found") { t.Errorf("unxepected error: %s", err.Error()) } } From 9eb0432c770d33d2b1c15ea25e5d3974654e843f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 22 Jan 2019 21:01:19 +0100 Subject: [PATCH 2873/3817] Port dag commansds to CoreAPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@f40d44ddbfd929eb84316d66e895f93dedf47424 --- coreiface/coreapi.go | 2 +- coreiface/dag.go | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 coreiface/dag.go diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index 9d2100fcc..16b28182e 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -19,7 +19,7 @@ type CoreAPI interface { Block() BlockAPI // Dag returns an implementation of Dag API - Dag() ipld.DAGService + Dag() APIDagService // Name returns an implementation of Name API Name() NameAPI diff --git a/coreiface/dag.go b/coreiface/dag.go new file mode 100644 index 000000000..455d00450 --- /dev/null +++ b/coreiface/dag.go @@ -0,0 +1,13 @@ +package iface + +import ( + ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" +) + +// APIDagService extends ipld.DAGService +type APIDagService interface { + ipld.DAGService + + // Pinning returns special NodeAdder which recursively pins added nodes + Pinning() ipld.NodeAdder +} From a833f0e460c7148714560fdf58c39fb15d130cff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 14 Jan 2019 22:02:51 +0100 Subject: [PATCH 2874/3817] Enforce refs on files when using nocopy This commit was moved from ipfs/go-unixfs@7eb118dfd0dc36cf44fcf53bc3f3558dac169eb8 --- unixfs/importer/helpers/dagbuilder.go | 12 ++++++++++-- unixfs/importer/importer.go | 13 ++++++++++--- unixfs/mod/dagmodifier.go | 6 +++++- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index 891cdaa4d..a624217f8 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -2,6 +2,7 @@ package helpers import ( "context" + "errors" "io" "os" @@ -17,6 +18,8 @@ import ( ipld "github.com/ipfs/go-ipld-format" ) +var ErrMissingFsRef = errors.New("missing file path or URL, can't create filestore reference") + // DagBuilderHelper wraps together a bunch of objects needed to // efficiently create unixfs dag trees type DagBuilderHelper struct { @@ -71,7 +74,7 @@ type DagBuilderParams struct { // New generates a new DagBuilderHelper from the given params and a given // chunker.Splitter as data source. -func (dbp *DagBuilderParams) New(spl chunker.Splitter) *DagBuilderHelper { +func (dbp *DagBuilderParams) New(spl chunker.Splitter) (*DagBuilderHelper, error) { db := &DagBuilderHelper{ dserv: dbp.Dagserv, spl: spl, @@ -87,7 +90,12 @@ func (dbp *DagBuilderParams) New(spl chunker.Splitter) *DagBuilderHelper { if dbp.URL != "" && dbp.NoCopy { db.fullPath = dbp.URL } - return db + + if dbp.NoCopy && db.fullPath == "" { // Enforce NoCopy + return nil, ErrMissingFsRef + } + + return db, nil } // prepareNext consumes the next item from the splitter and puts it diff --git a/unixfs/importer/importer.go b/unixfs/importer/importer.go index ecf016854..03f1c6048 100644 --- a/unixfs/importer/importer.go +++ b/unixfs/importer/importer.go @@ -18,8 +18,11 @@ func BuildDagFromReader(ds ipld.DAGService, spl chunker.Splitter) (ipld.Node, er Dagserv: ds, Maxlinks: h.DefaultLinksPerBlock, } - - return bal.Layout(dbp.New(spl)) + db, err := dbp.New(spl) + if err != nil { + return nil, err + } + return bal.Layout(db) } // BuildTrickleDagFromReader creates a DAG given a DAGService and a Splitter @@ -30,5 +33,9 @@ func BuildTrickleDagFromReader(ds ipld.DAGService, spl chunker.Splitter) (ipld.N Maxlinks: h.DefaultLinksPerBlock, } - return trickle.Layout(dbp.New(spl)) + db, err := dbp.New(spl) + if err != nil { + return nil, err + } + return trickle.Layout(db) } diff --git a/unixfs/mod/dagmodifier.go b/unixfs/mod/dagmodifier.go index a4c098052..1e2b2dcca 100644 --- a/unixfs/mod/dagmodifier.go +++ b/unixfs/mod/dagmodifier.go @@ -359,7 +359,11 @@ func (dm *DagModifier) appendData(nd ipld.Node, spl chunker.Splitter) (ipld.Node CidBuilder: dm.Prefix, RawLeaves: dm.RawLeaves, } - return trickle.Append(dm.ctx, nd, dbp.New(spl)) + db, err := dbp.New(spl) + if err != nil { + return nil, err + } + return trickle.Append(dm.ctx, nd, db) default: return nil, ErrNotUnixfs } From fe4bf0a3c8b521c17dce892b3fe8db7610768ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 14 Jan 2019 22:14:06 +0100 Subject: [PATCH 2875/3817] fix tests after DagBuilder changes This commit was moved from ipfs/go-unixfs@9e866f18e27d2c59de70057aab1f92afced0717e --- unixfs/importer/balanced/balanced_test.go | 7 ++++- unixfs/importer/trickle/trickle_test.go | 37 ++++++++++++++++++++--- unixfs/test/utils.go | 6 +++- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/unixfs/importer/balanced/balanced_test.go b/unixfs/importer/balanced/balanced_test.go index 1f135b781..b2069e3a9 100644 --- a/unixfs/importer/balanced/balanced_test.go +++ b/unixfs/importer/balanced/balanced_test.go @@ -27,7 +27,12 @@ func buildTestDag(ds ipld.DAGService, spl chunker.Splitter) (*dag.ProtoNode, err Maxlinks: h.DefaultLinksPerBlock, } - nd, err := Layout(dbp.New(spl)) + db, err := dbp.New(spl) + if err != nil { + return nil, err + } + + nd, err := Layout(db) if err != nil { return nil, err } diff --git a/unixfs/importer/trickle/trickle_test.go b/unixfs/importer/trickle/trickle_test.go index 2067a24e3..2b6e0bd46 100644 --- a/unixfs/importer/trickle/trickle_test.go +++ b/unixfs/importer/trickle/trickle_test.go @@ -39,7 +39,12 @@ func buildTestDag(ds ipld.DAGService, spl chunker.Splitter, rawLeaves UseRawLeav RawLeaves: bool(rawLeaves), } - nd, err := Layout(dbp.New(spl)) + db, err := dbp.New(spl) + if err != nil { + return nil, err + } + + nd, err := Layout(db) if err != nil { return nil, err } @@ -503,7 +508,13 @@ func testAppend(t *testing.T, rawLeaves UseRawLeaves) { r := bytes.NewReader(should[nbytes/2:]) ctx := context.Background() - nnode, err := Append(ctx, nd, dbp.New(chunker.NewSizeSplitter(r, 500))) + + db, err := dbp.New(chunker.NewSizeSplitter(r, 500)) + if err != nil { + t.Fatal(err) + } + + nnode, err := Append(ctx, nd, db) if err != nil { t.Fatal(err) } @@ -564,7 +575,12 @@ func testMultipleAppends(t *testing.T, rawLeaves UseRawLeaves) { ctx := context.Background() for i := 0; i < len(should); i++ { - nnode, err := Append(ctx, nd, dbp.New(spl(bytes.NewReader(should[i:i+1])))) + db, err := dbp.New(spl(bytes.NewReader(should[i : i+1]))) + if err != nil { + t.Fatal(err) + } + + nnode, err := Append(ctx, nd, db) if err != nil { t.Fatal(err) } @@ -612,12 +628,23 @@ func TestAppendSingleBytesToEmpty(t *testing.T) { spl := chunker.SizeSplitterGen(500) ctx := context.Background() - nnode, err := Append(ctx, nd, dbp.New(spl(bytes.NewReader(data[:1])))) + + db, err := dbp.New(spl(bytes.NewReader(data[:1]))) + if err != nil { + t.Fatal(err) + } + + nnode, err := Append(ctx, nd, db) + if err != nil { + t.Fatal(err) + } + + db, err = dbp.New(spl(bytes.NewReader(data[1:]))) if err != nil { t.Fatal(err) } - nnode, err = Append(ctx, nnode, dbp.New(spl(bytes.NewReader(data[1:])))) + nnode, err = Append(ctx, nnode, db) if err != nil { t.Fatal(err) } diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index 98bce14cf..bb251bc11 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -67,7 +67,11 @@ func GetNode(t testing.TB, dserv ipld.DAGService, data []byte, opts NodeOpts) ip RawLeaves: opts.RawLeavesUsed, } - node, err := trickle.Layout(dbp.New(SizeSplitterGen(500)(in))) + db, err := dbp.New(SizeSplitterGen(500)(in)) + if err != nil { + t.Fatal(err) + } + node, err := trickle.Layout(db) if err != nil { t.Fatal(err) } From f542e9c788b6601c072ec7408e73a9057921a0b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 14 Jan 2019 21:01:39 +0100 Subject: [PATCH 2876/3817] Unixfs.Add nocopy test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@e49c3d2211a09a5bfa5c5eceeeaf08715b230313 --- coreiface/tests/unixfs.go | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 0ef3f031e..0ceae06e1 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -101,6 +101,34 @@ func (tp *provider) TestAdd(t *testing.T) { return coreiface.IpfsPath(c) } + rf, err := ioutil.TempFile(os.TempDir(), "unixfs-add-real") + if err != nil { + t.Fatal(err) + } + rfp := rf.Name() + + if _, err := rf.Write([]byte(helloStr)); err != nil { + t.Fatal(err) + } + + stat, err := rf.Stat() + if err != nil { + t.Fatal(err) + } + + if err := rf.Close(); err != nil { + t.Fatal(err) + } + defer os.Remove(rfp) + + realFile := func() files.Node { + n, err := files.NewReaderPathFile(rfp, ioutil.NopCloser(strings.NewReader(helloStr)), stat) + if err != nil { + t.Fatal(err) + } + return n + } + cases := []struct { name string data func() files.Node @@ -323,6 +351,20 @@ func (tp *provider) TestAdd(t *testing.T) { path: "/ipfs/QmRKGpFfR32FVXdvJiHfo4WJ5TDYBsM1P9raAp1p6APWSp", opts: []options.UnixfsAddOption{options.Unixfs.Hidden(false)}, }, + // NoCopy + { + name: "simpleNoCopy", + data: realFile, + path: "/ipfs/zb2rhdhmJjJZs9qkhQCpCQ7VREFkqWw3h1r8utjVvQugwHPFd", + opts: []options.UnixfsAddOption{options.Unixfs.Nocopy(true)}, + }, + { + name: "noCopyNoRaw", + data: realFile, + path: "/ipfs/zb2rhdhmJjJZs9qkhQCpCQ7VREFkqWw3h1r8utjVvQugwHPFd", + opts: []options.UnixfsAddOption{options.Unixfs.Nocopy(true), options.Unixfs.RawLeaves(false)}, + err: "nocopy option requires '--raw-leaves' to be enabled as well", + }, // Events / Progress { name: "simpleAddEvent", From 089430e39729baec439315f08826de38643a99fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 14 Jan 2019 22:00:57 +0100 Subject: [PATCH 2877/3817] Unixfs: enforce refs on files when using nocopy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@3de10ba1bd3faefa63d1c9353a7ac47c5de45d2d --- coreiface/tests/unixfs.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 0ceae06e1..fdc1a08cd 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -16,6 +16,7 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" "gx/ipfs/QmQXze9tG878pa4Euya4rrDpyTNX3kQe4dhCaBzBozGgpe/go-unixfs" + "gx/ipfs/QmQXze9tG878pa4Euya4rrDpyTNX3kQe4dhCaBzBozGgpe/go-unixfs/importer/helpers" "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" cbor "gx/ipfs/QmRoARq3nkUb13HSKZGepCZSWe5GrVPwx7xURJGZ7KWv9V/go-ipld-cbor" mdag "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" @@ -365,6 +366,13 @@ func (tp *provider) TestAdd(t *testing.T) { opts: []options.UnixfsAddOption{options.Unixfs.Nocopy(true), options.Unixfs.RawLeaves(false)}, err: "nocopy option requires '--raw-leaves' to be enabled as well", }, + { + name: "noCopyNoPath", + data: strFile(helloStr), + path: "/ipfs/zb2rhdhmJjJZs9qkhQCpCQ7VREFkqWw3h1r8utjVvQugwHPFd", + opts: []options.UnixfsAddOption{options.Unixfs.Nocopy(true)}, + err: helpers.ErrMissingFsRef.Error(), + }, // Events / Progress { name: "simpleAddEvent", From c4c8f78fbaabeefdd34da61134117fb4fac3485a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 23 Jan 2019 05:52:50 -0800 Subject: [PATCH 2878/3817] fix: no components error We need to return "ErrNoComponents" when the part after `/{ipld,ipfs,ipns}/` is empty. This commit was moved from ipfs/go-path@3a06fd5efac9babd9d45f58d4118370ba940cc43 --- path/path.go | 7 ++++++- path/path_test.go | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/path/path.go b/path/path.go index eada90d8f..b0d6cdcde 100644 --- a/path/path.go +++ b/path/path.go @@ -120,13 +120,18 @@ func ParsePath(txt string) (Path, error) { //TODO: make this smarter switch parts[1] { case "ipfs", "ipld": + if parts[2] == "" { + return "", ErrNoComponents + } // Validate Cid. _, err := cid.Decode(parts[2]) if err != nil { return "", err } case "ipns": - // No validation. + if parts[2] == "" { + return "", ErrNoComponents + } default: return "", ErrBadPath } diff --git a/path/path_test.go b/path/path_test.go index a166e713d..657c58c75 100644 --- a/path/path_test.go +++ b/path/path_test.go @@ -37,6 +37,19 @@ func TestPathParsing(t *testing.T) { } } +func TestNoComponents(t *testing.T) { + for _, s := range []string{ + "/ipfs/", + "/ipns/", + "/ipld/", + } { + _, err := ParsePath(s) + if err != ErrNoComponents { + t.Errorf("expected ErrNoComponents, got %s", err) + } + } +} + func TestIsJustAKey(t *testing.T) { cases := map[string]bool{ "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true, From d8eb73b5b2bb26bff6da6249a4ea3b2dd4dc0b49 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 23 Jan 2019 05:57:52 -0800 Subject: [PATCH 2879/3817] ci: add travis This commit was moved from ipfs/go-path@ab96f1839f90066285f4b1eaa7a0f70716947b5d --- path/Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 path/Makefile diff --git a/path/Makefile b/path/Makefile new file mode 100644 index 000000000..20619413c --- /dev/null +++ b/path/Makefile @@ -0,0 +1,11 @@ +gx: + go get github.com/whyrusleeping/gx + go get github.com/whyrusleeping/gx-go + +deps: gx + gx --verbose install --global + gx-go rewrite + +publish: + gx-go rewrite --undo + From 0607c70fc34030eeef947bb4b338fdc65ac45aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 14 Jan 2019 22:05:10 +0100 Subject: [PATCH 2880/3817] gx: update go-unixfs to 1.2.14 and go-bitswap to 1.1.21 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (and everything else...) License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@ac501b69c4b9fd4e05d39d2c8ff0eb1bfbea8c7b --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 4 ++-- namesys/proquint.go | 2 +- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- namesys/routing.go | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 9fbaa06b9..bc6d4dd2c 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" + path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 51bb0c0b0..8f41c64d3 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" + path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 526794174..769a9d5d7 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" + path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index 64690cd3d..0b047eec9 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" + path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 181f227f0..47c0bcd04 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" + path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/namesys.go b/namesys/namesys.go index d944b650f..ac3caa328 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,7 +5,7 @@ import ( "strings" "time" - path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" + path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index bbbf632d0..8f6da5d71 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -7,12 +7,12 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" pstoremem "gx/ipfs/QmPiemjiKBC9VA7vZF82m4x1oygtg2c2YVqag8PX7dN1BD/go-libp2p-peerstore/pstoremem" - "gx/ipfs/QmQXze9tG878pa4Euya4rrDpyTNX3kQe4dhCaBzBozGgpe/go-unixfs" + "gx/ipfs/QmSMJ4rZbCJaih3y82Ebq7BZqK6vU2FHsKcWKQiE1DPTpS/go-unixfs" offroute "gx/ipfs/QmVZ6cQXHoTQja4oo9GhhHZi7dThi4x98mRKgGtKnTy37u/go-ipfs-routing/offline" ipns "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns" + path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" diff --git a/namesys/proquint.go b/namesys/proquint.go index 5e8d52fe7..703bbb3cc 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,7 +4,7 @@ import ( "context" "errors" - path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" + path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/publisher.go b/namesys/publisher.go index 63cae54ce..ef536a7e9 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,8 +7,8 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" - ft "gx/ipfs/QmQXze9tG878pa4Euya4rrDpyTNX3kQe4dhCaBzBozGgpe/go-unixfs" + ft "gx/ipfs/QmSMJ4rZbCJaih3y82Ebq7BZqK6vU2FHsKcWKQiE1DPTpS/go-unixfs" + path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" routing "gx/ipfs/QmTiRqrF5zkdZyrdsL5qndG1UbeWi8k8N2pYxCtXWrahR2/go-libp2p-routing" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 52d90629b..429f41f8c 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" + path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" ic "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index f55fd2984..a7530b1e9 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" + path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" pstore "gx/ipfs/QmPiemjiKBC9VA7vZF82m4x1oygtg2c2YVqag8PX7dN1BD/go-libp2p-peerstore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index c157f7de3..fe0a81125 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" + path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" testutil "gx/ipfs/QmNvHv84aH2qZafDuSdKJCQ1cvPZ1kmQmyD4YtzjUHuk9v/go-testutil" mockrouting "gx/ipfs/QmVZ6cQXHoTQja4oo9GhhHZi7dThi4x98mRKgGtKnTy37u/go-ipfs-routing/mock" diff --git a/namesys/routing.go b/namesys/routing.go index 029eaeb83..9ac7c086a 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,7 +5,7 @@ import ( "strings" "time" - path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" + path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" From 99fe5e7502f9c05b67e338490b47816d4434aa71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 14 Jan 2019 22:05:10 +0100 Subject: [PATCH 2881/3817] gx: update go-unixfs to 1.2.14 and go-bitswap to 1.1.21 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (and everything else...) License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@3cc6578657ac8580d77f93a681f80ceeba1d6389 --- coreiface/coreapi.go | 2 +- coreiface/dag.go | 2 +- coreiface/object.go | 2 +- coreiface/options/unixfs.go | 2 +- coreiface/path.go | 2 +- coreiface/tests/name.go | 2 +- coreiface/tests/unixfs.go | 8 ++++---- coreiface/unixfs.go | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index 16b28182e..d26ec4f7d 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -7,7 +7,7 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" + ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" ) // CoreAPI defines an unified interface to IPFS for Go programs diff --git a/coreiface/dag.go b/coreiface/dag.go index 455d00450..d15e24360 100644 --- a/coreiface/dag.go +++ b/coreiface/dag.go @@ -1,7 +1,7 @@ package iface import ( - ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" + ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" ) // APIDagService extends ipld.DAGService diff --git a/coreiface/object.go b/coreiface/object.go index ba6f5a95d..2ed357cb6 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -7,7 +7,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" + ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" ) // ObjectStat provides information about dag nodes diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 5f92f3eea..109a63f1d 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -5,7 +5,7 @@ import ( "fmt" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - dag "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" + dag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) diff --git a/coreiface/path.go b/coreiface/path.go index 580703a73..b96e0e775 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,8 +1,8 @@ package iface import ( - ipfspath "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + ipfspath "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" ) //TODO: merge with ipfspath so we don't depend on it diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index 2e43a12ee..7b0a5d8f0 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -8,7 +8,7 @@ import ( "testing" "time" - ipath "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" + ipath "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files" coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index fdc1a08cd..6f10406eb 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -15,12 +15,12 @@ import ( coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - "gx/ipfs/QmQXze9tG878pa4Euya4rrDpyTNX3kQe4dhCaBzBozGgpe/go-unixfs" - "gx/ipfs/QmQXze9tG878pa4Euya4rrDpyTNX3kQe4dhCaBzBozGgpe/go-unixfs/importer/helpers" "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - cbor "gx/ipfs/QmRoARq3nkUb13HSKZGepCZSWe5GrVPwx7xURJGZ7KWv9V/go-ipld-cbor" - mdag "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" + cbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" + "gx/ipfs/QmSMJ4rZbCJaih3y82Ebq7BZqK6vU2FHsKcWKQiE1DPTpS/go-unixfs" + "gx/ipfs/QmSMJ4rZbCJaih3y82Ebq7BZqK6vU2FHsKcWKQiE1DPTpS/go-unixfs/importer/helpers" "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files" + mdag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index b42b454cc..3c2788196 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -5,8 +5,8 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" files "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files" - ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" ) type AddEvent struct { From 24a1bb8996f847f83fae360f99977f76ebb39260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 14 Jan 2019 22:05:10 +0100 Subject: [PATCH 2882/3817] gx: update go-unixfs to 1.2.14 and go-bitswap to 1.1.21 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (and everything else...) License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-ipfs-pinner@3845684e5ca294d5c69992c65abcb312a1dcd681 --- pinning/pinner/gc/gc.go | 6 +++--- pinning/pinner/pin.go | 4 ++-- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 4 ++-- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 1c5c6a7af..5e09f5078 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,14 +8,14 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" - bserv "gx/ipfs/QmYPZzd9VqmJDwxUnThfeSbV1Y5o53aVPDijTB7j7rS9Ep/go-blockservice" + bserv "gx/ipfs/QmVKQHuzni68SWByzJgBUCwHvvr4TWiXfutNWWwpZpp4rE/go-blockservice" + dag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" bstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" "gx/ipfs/QmYMQuypUbgsdNHmuCBSUJV6wdQVsBHRivNAp3efHJwZJD/go-verifcid" offline "gx/ipfs/QmYZwey1thDTynSrvd6qQkX24UpTka6TFhQ2v569UpoqxD/go-ipfs-exchange-offline" - ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" dstore "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" ) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 11fb21039..8a1a18fe1 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,10 +10,10 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" + mdag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" + ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index e37773e76..9660d7339 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - mdag "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" - bs "gx/ipfs/QmYPZzd9VqmJDwxUnThfeSbV1Y5o53aVPDijTB7j7rS9Ep/go-blockservice" + bs "gx/ipfs/QmVKQHuzni68SWByzJgBUCwHvvr4TWiXfutNWWwpZpp4rE/go-blockservice" + mdag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" util "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index ff7152685..f7e457315 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,10 +10,10 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" + "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" + ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 184041f36..52b55b9ff 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" - bserv "gx/ipfs/QmYPZzd9VqmJDwxUnThfeSbV1Y5o53aVPDijTB7j7rS9Ep/go-blockservice" + bserv "gx/ipfs/QmVKQHuzni68SWByzJgBUCwHvvr4TWiXfutNWWwpZpp4rE/go-blockservice" + dag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" From 0e53a875d30352b1421cec5b4a8529f97ffa63f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 14 Jan 2019 22:05:10 +0100 Subject: [PATCH 2883/3817] gx: update go-unixfs to 1.2.14 and go-bitswap to 1.1.21 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (and everything else...) License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-filestore@4c773744e5cc90f162f00b84dd5fbbb85eacdb83 --- filestore/filestore.go | 2 +- filestore/filestore_test.go | 4 ++-- filestore/fsrefstore.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index d9632d9f9..0ee63e52e 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,9 +11,9 @@ import ( "context" "errors" - posinfo "gx/ipfs/QmR6YMs8EkXQLXNwQKxLnQp2VBZSepoEJ8KCZAyanJHhJu/go-ipfs-posinfo" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" + posinfo "gx/ipfs/QmUhHBdzfNb9FQPDtKwhghVoR3zwkbXzFJ1uJyEMYUpFSd/go-ipfs-posinfo" blocks "gx/ipfs/QmWoXtvgC8inqFkAATB7cp2Dax7XBi9VDvSg9RCCZufmRk/go-block-format" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" dsq "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index a39d6f906..9f8bdb26b 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,11 +7,11 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmTQdH4848iTVCJmKXYyRiK72HufWTLYQQ8iN3JaQ8K1Hq/go-merkledag" + dag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" - posinfo "gx/ipfs/QmR6YMs8EkXQLXNwQKxLnQp2VBZSepoEJ8KCZAyanJHhJu/go-ipfs-posinfo" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" + posinfo "gx/ipfs/QmUhHBdzfNb9FQPDtKwhghVoR3zwkbXzFJ1uJyEMYUpFSd/go-ipfs-posinfo" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" ) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index f6b39e27e..a7e34bb77 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,9 +10,9 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - posinfo "gx/ipfs/QmR6YMs8EkXQLXNwQKxLnQp2VBZSepoEJ8KCZAyanJHhJu/go-ipfs-posinfo" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" + posinfo "gx/ipfs/QmUhHBdzfNb9FQPDtKwhghVoR3zwkbXzFJ1uJyEMYUpFSd/go-ipfs-posinfo" blocks "gx/ipfs/QmWoXtvgC8inqFkAATB7cp2Dax7XBi9VDvSg9RCCZufmRk/go-block-format" dshelp "gx/ipfs/QmauEMWPoSqggfpSDHMMXuDn12DTd7TaFBvn39eeurzKT2/go-ipfs-ds-help" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" From 71502ba7d5e8ec823510861f127f395db821f791 Mon Sep 17 00:00:00 2001 From: Daniel Aleksandersen Date: Mon, 28 Jan 2019 07:28:30 +0100 Subject: [PATCH 2884/3817] Only perform DNSLink lookups on fully qualified domain names (FQDN) This change halves the number of DNS queries requires to lookup DNSLink information for "example.com" by forcing the use of a FQDN. * example.com * example.com.local (removed) * _dnslink.example.com * _dnslink.example.com.local (removed) Where .local is the local system's organization/domain name. License: MIT Signed-off-by: Daniel Aleksandersen This commit was moved from ipfs/go-namesys@48bc858f19bbbd4431efffa93b63e4f78072b3d0 --- namesys/dns.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 769a9d5d7..f0cd63c70 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -45,6 +45,7 @@ type lookupRes struct { // TXT records for a given domain name should contain a b58 // encoded multihash. func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { + var fqdn string out := make(chan onceResult, 1) segments := strings.SplitN(name, "/", 2) domain := segments[0] @@ -56,11 +57,17 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options } log.Debugf("DNSResolver resolving %s", domain) + if strings.HasSuffix(domain, ".") { + fqdn = domain + } else { + fqdn = domain + "." + } + rootChan := make(chan lookupRes, 1) - go workDomain(r, domain, rootChan) + go workDomain(r, fqdn, rootChan) subChan := make(chan lookupRes, 1) - go workDomain(r, "_dnslink."+domain, subChan) + go workDomain(r, "_dnslink."+fqdn, subChan) appendPath := func(p path.Path) (path.Path, error) { if len(segments) > 1 { From 04686cc6ec5c43db753e3c8ac2a7da1f6a33c4fc Mon Sep 17 00:00:00 2001 From: Daniel Aleksandersen Date: Mon, 28 Jan 2019 08:13:14 +0100 Subject: [PATCH 2885/3817] Update mockDNS to use FQDNs License: MIT Signed-off-by: Daniel Aleksandersen This commit was moved from ipfs/go-namesys@e3e8ab9a5048d3d2b1732acc3bd7ef2a9e1b9f1b --- namesys/dns_test.go | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 2a58124ed..282906998 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -61,66 +61,66 @@ func TestDnsEntryParsing(t *testing.T) { func newMockDNS() *mockDNS { return &mockDNS{ entries: map[string][]string{ - "multihash.example.com": []string{ + "multihash.example.com.": []string{ "dnslink=QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", }, - "ipfs.example.com": []string{ + "ipfs.example.com.": []string{ "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", }, - "_dnslink.dipfs.example.com": []string{ + "_dnslink.dipfs.example.com.": []string{ "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", }, - "dns1.example.com": []string{ + "dns1.example.com.": []string{ "dnslink=/ipns/ipfs.example.com", }, - "dns2.example.com": []string{ + "dns2.example.com.": []string{ "dnslink=/ipns/dns1.example.com", }, - "multi.example.com": []string{ + "multi.example.com.": []string{ "some stuff", "dnslink=/ipns/dns1.example.com", "masked dnslink=/ipns/example.invalid", }, - "equals.example.com": []string{ + "equals.example.com.": []string{ "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/=equals", }, - "loop1.example.com": []string{ + "loop1.example.com.": []string{ "dnslink=/ipns/loop2.example.com", }, - "loop2.example.com": []string{ + "loop2.example.com.": []string{ "dnslink=/ipns/loop1.example.com", }, - "_dnslink.dloop1.example.com": []string{ + "_dnslink.dloop1.example.com.": []string{ "dnslink=/ipns/loop2.example.com", }, - "_dnslink.dloop2.example.com": []string{ + "_dnslink.dloop2.example.com.": []string{ "dnslink=/ipns/loop1.example.com", }, - "bad.example.com": []string{ + "bad.example.com.": []string{ "dnslink=", }, - "withsegment.example.com": []string{ + "withsegment.example.com.": []string{ "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment", }, - "withrecsegment.example.com": []string{ + "withrecsegment.example.com.": []string{ "dnslink=/ipns/withsegment.example.com/subsub", }, - "withtrailing.example.com": []string{ + "withtrailing.example.com.": []string{ "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/", }, - "withtrailingrec.example.com": []string{ + "withtrailingrec.example.com.": []string{ "dnslink=/ipns/withtrailing.example.com/segment/", }, - "double.example.com": []string{ + "double.example.com.": []string{ "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", }, - "_dnslink.double.example.com": []string{ + "_dnslink.double.example.com.": []string{ "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", }, - "double.conflict.com": []string{ + "double.conflict.com.": []string{ "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", }, - "_dnslink.conflict.example.com": []string{ + "_dnslink.conflict.example.com.": []string{ "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjE", }, }, From 3558ff5bddae35f9cf69d7484502fafb65791b27 Mon Sep 17 00:00:00 2001 From: Daniel Aleksandersen Date: Mon, 28 Jan 2019 08:47:51 +0100 Subject: [PATCH 2886/3817] Add FQDN name test License: MIT Signed-off-by: Daniel Aleksandersen This commit was moved from ipfs/go-namesys@3bf774ef781e831fe6c810e1a5f521f0da624e23 --- namesys/dns_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 282906998..ed28aa945 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -123,6 +123,9 @@ func newMockDNS() *mockDNS { "_dnslink.conflict.example.com.": []string{ "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjE", }, + "fqdn.example.com.": []string{ + "dnslink=/ipfs/QmYvMB9yrsSf7RKBghkfwmHJkzJhW2ZgVwq3LxBXXPasFr", + }, }, } } @@ -159,4 +162,5 @@ func TestDNSResolution(t *testing.T) { testResolution(t, r, "withtrailingrec.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/", nil) testResolution(t, r, "double.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) testResolution(t, r, "conflict.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjE", nil) + testResolution(t, r, "fqdn.example.com.", opts.DefaultDepthLimit, "/ipfs/QmYvMB9yrsSf7RKBghkfwmHJkzJhW2ZgVwq3LxBXXPasFr", nil) } From 6b2d6ab3cdddb3db4b653e82ac78560e2c1b842f Mon Sep 17 00:00:00 2001 From: Overbool Date: Thu, 13 Dec 2018 23:02:55 +0800 Subject: [PATCH 2887/3817] cmds/pin: use coreapi/pin License: MIT Signed-off-by: Overbool This commit was moved from ipfs/interface-go-ipfs-core@3e1cd71bb97f70a6309fa31f3d9e719c7b38f254 --- coreiface/options/pin.go | 36 +++++++++++++++++++++++++++++++++++- coreiface/pin.go | 2 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/coreiface/options/pin.go b/coreiface/options/pin.go index 9d1107f92..630b561de 100644 --- a/coreiface/options/pin.go +++ b/coreiface/options/pin.go @@ -8,12 +8,23 @@ type PinLsSettings struct { Type string } +// PinRmSettings represents the settings of pin rm command +type PinRmSettings struct { + Recursive bool + Force bool +} + type PinUpdateSettings struct { Unpin bool } type PinAddOption func(*PinAddSettings) error -type PinLsOption func(settings *PinLsSettings) error + +// PinRmOption pin rm option func +type PinRmOption func(*PinRmSettings) error + +// PinLsOption pin ls option func +type PinLsOption func(*PinLsSettings) error type PinUpdateOption func(*PinUpdateSettings) error func PinAddOptions(opts ...PinAddOption) (*PinAddSettings, error) { @@ -31,6 +42,21 @@ func PinAddOptions(opts ...PinAddOption) (*PinAddSettings, error) { return options, nil } +// PinRmOptions pin rm options +func PinRmOptions(opts ...PinRmOption) (*PinRmSettings, error) { + options := &PinRmSettings{ + Recursive: true, + } + + for _, opt := range opts { + if err := opt(options); err != nil { + return nil, err + } + } + + return options, nil +} + func PinLsOptions(opts ...PinLsOption) (*PinLsSettings, error) { options := &PinLsSettings{ Type: "all", @@ -102,6 +128,14 @@ func (pinOpts) Recursive(recursive bool) PinAddOption { } } +// RmRecursive is an option for Pin.Rm +func (pinOpts) RmRecursive(recursive bool) PinRmOption { + return func(settings *PinRmSettings) error { + settings.Recursive = recursive + return nil + } +} + // Type is an option for Pin.Ls which allows to specify which pin types should // be returned // diff --git a/coreiface/pin.go b/coreiface/pin.go index 2e119cbea..6e13def8f 100644 --- a/coreiface/pin.go +++ b/coreiface/pin.go @@ -43,7 +43,7 @@ type PinAPI interface { Ls(context.Context, ...options.PinLsOption) ([]Pin, error) // Rm removes pin for object specified by the path - Rm(context.Context, Path) error + Rm(context.Context, Path, ...options.PinRmOption) error // Update changes one pin to another, skipping checks for matching paths in // the old tree From e3960a2b9f8f40403f8ffbce29e576a217dbbf74 Mon Sep 17 00:00:00 2001 From: Overbool Date: Sat, 15 Dec 2018 11:14:29 +0800 Subject: [PATCH 2888/3817] cmds/pin: modify test License: MIT Signed-off-by: Overbool This commit was moved from ipfs/interface-go-ipfs-core@8e9e8d1b419aa93da6f3573bf424db57a60399ae --- coreiface/options/pin.go | 1 - 1 file changed, 1 deletion(-) diff --git a/coreiface/options/pin.go b/coreiface/options/pin.go index 630b561de..cc4a8ef29 100644 --- a/coreiface/options/pin.go +++ b/coreiface/options/pin.go @@ -11,7 +11,6 @@ type PinLsSettings struct { // PinRmSettings represents the settings of pin rm command type PinRmSettings struct { Recursive bool - Force bool } type PinUpdateSettings struct { From 11fbccb95bed15f9a03ecb3763c80e858c38f16f Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Fri, 13 Jul 2018 12:07:17 -0300 Subject: [PATCH 2889/3817] unixfs: decouple the DAG traversal logic from the DAG reader Decouple the DAG traversal logic from the `PBDagReader` encapsulating it in the (new) `Walker` structure. Collapse PB and Buffer DAG readers into one (`dagReader`) removing the `bufdagreader.go` and `pbdagreader.go` files, moving all the code to `dagreader.go`. Remove `TestSeekAndReadLarge` and `TestReadAndCancel` which operated directly on the `NodePromise` structure that is now abstracted away in `NavigableIPLDNode`, in the `go-ipld-format` repo, where they should be recreated. License: MIT Signed-off-by: Lucas Molas This commit was moved from ipfs/go-unixfs@ecd031d34eda4b9f03e0306931308a82b6338420 --- unixfs/archive/tar/writer.go | 6 +- unixfs/io/bufdagreader.go | 39 ---- unixfs/io/dagreader.go | 372 ++++++++++++++++++++++++++++++++++- unixfs/io/dagreader_test.go | 85 -------- unixfs/io/pbdagreader.go | 326 ------------------------------ unixfs/unixfs.go | 53 ++++- 6 files changed, 419 insertions(+), 462 deletions(-) delete mode 100644 unixfs/io/bufdagreader.go delete mode 100644 unixfs/io/pbdagreader.go diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go index bc1253d3d..0e91ba9b0 100644 --- a/unixfs/archive/tar/writer.go +++ b/unixfs/archive/tar/writer.go @@ -60,7 +60,11 @@ func (w *Writer) writeFile(nd *mdag.ProtoNode, fsNode *ft.FSNode, fpath string) return err } - dagr := uio.NewPBFileReader(w.ctx, nd, fsNode, w.Dag) + dagr, err := uio.NewDagReader(w.ctx, nd, w.Dag) + if err != nil { + return err + } + if _, err := dagr.WriteTo(w.TarW); err != nil { return err } diff --git a/unixfs/io/bufdagreader.go b/unixfs/io/bufdagreader.go deleted file mode 100644 index 48efe98ad..000000000 --- a/unixfs/io/bufdagreader.go +++ /dev/null @@ -1,39 +0,0 @@ -package io - -import ( - "bytes" - "context" -) - -// BufDagReader implements a DagReader that reads from a byte slice -// using a bytes.Reader. It is used for RawNodes. -type BufDagReader struct { - *bytes.Reader -} - -// NewBufDagReader returns a DAG reader for the given byte slice. -// BufDagReader is used to read RawNodes. -func NewBufDagReader(b []byte) *BufDagReader { - return &BufDagReader{bytes.NewReader(b)} -} - -var _ DagReader = (*BufDagReader)(nil) - -// Close is a nop. -func (*BufDagReader) Close() error { - return nil -} - -// CtxReadFull reads the slice onto b. -func (rd *BufDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { - return rd.Read(b) -} - -// Size returns the size of the buffer. -func (rd *BufDagReader) Size() uint64 { - s := rd.Reader.Size() - if s < 0 { - panic("size smaller than 0 (impossible!!)") - } - return uint64(s) -} diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index cf980d2be..333f7999f 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -1,13 +1,15 @@ package io import ( + "bytes" "context" "errors" "io" + "io/ioutil" ipld "github.com/ipfs/go-ipld-format" mdag "github.com/ipfs/go-merkledag" - ft "github.com/ipfs/go-unixfs" + unixfs "github.com/ipfs/go-unixfs" ) // Common errors @@ -17,6 +19,10 @@ var ( ErrUnkownNodeType = errors.New("unknown node type") ) +// TODO: Rename the `DagReader` interface, this doesn't read *any* DAG, just +// DAGs with UnixFS node (and it *belongs* to the `unixfs` package). Some +// alternatives: `FileReader`, `UnixFSFileReader`, `UnixFSReader`. + // A DagReader provides read-only read and seek acess to a unixfs file. // Different implementations of readers are used for the different // types of unixfs/protobuf-encoded nodes. @@ -35,24 +41,29 @@ type ReadSeekCloser interface { } // NewDagReader creates a new reader object that reads the data represented by -// the given node, using the passed in DAGService for data retrieval +// the given node, using the passed in DAGService for data retrieval. func NewDagReader(ctx context.Context, n ipld.Node, serv ipld.NodeGetter) (DagReader, error) { + var size uint64 + switch n := n.(type) { case *mdag.RawNode: - return NewBufDagReader(n.RawData()), nil + size = uint64(len(n.RawData())) + case *mdag.ProtoNode: - fsNode, err := ft.FSNodeFromBytes(n.Data()) + fsNode, err := unixfs.FSNodeFromBytes(n.Data()) if err != nil { return nil, err } switch fsNode.Type() { - case ft.TDirectory, ft.THAMTShard: + case unixfs.TFile, unixfs.TRaw: + size = fsNode.FileSize() + + case unixfs.TDirectory, unixfs.THAMTShard: // Dont allow reading directories return nil, ErrIsDir - case ft.TFile, ft.TRaw: - return NewPBFileReader(ctx, n, fsNode, serv), nil - case ft.TMetadata: + + case unixfs.TMetadata: if len(n.Links()) == 0 { return nil, errors.New("incorrectly formatted metadata object") } @@ -66,12 +77,353 @@ func NewDagReader(ctx context.Context, n ipld.Node, serv ipld.NodeGetter) (DagRe return nil, mdag.ErrNotProtobuf } return NewDagReader(ctx, childpb, serv) - case ft.TSymlink: + case unixfs.TSymlink: return nil, ErrCantReadSymlinks default: - return nil, ft.ErrUnrecognizedType + return nil, unixfs.ErrUnrecognizedType } default: return nil, ErrUnkownNodeType } + + ctxWithCancel, cancel := context.WithCancel(ctx) + + return &dagReader{ + ctx: ctxWithCancel, + cancel: cancel, + serv: serv, + size: size, + rootNode: n, + dagWalker: ipld.NewWalker(ctxWithCancel, ipld.NewNavigableIPLDNode(n, serv)), + }, nil +} + +// dagReader provides a way to easily read the data contained in a dag. +type dagReader struct { + + // Structure to perform the DAG iteration and search, the reader + // just needs to add logic to the `Visitor` callback passed to + // `Iterate` and `Seek`. + dagWalker *ipld.Walker + + // Buffer with the data extracted from the current node being visited. + // To avoid revisiting a node to complete a (potential) partial read + // (or read after seek) the node's data is fully extracted in a single + // `readNodeDataBuffer` operation. + currentNodeData *bytes.Reader + + // Implements the `Size()` API. + size uint64 + + // Current offset for the read head within the DAG file. + offset int64 + + // Root node of the DAG, stored to re-create the `dagWalker` (effectively + // re-setting the position of the reader, used during `Seek`). + rootNode ipld.Node + + // Context passed to the `dagWalker`, the `cancel` function is used to + // cancel read operations (cancelling requested child node promises, + // see `ipld.NavigableIPLDNode.FetchChild` for details). + ctx context.Context + cancel func() + + // Passed to the `dagWalker` that will use it to request nodes. + // TODO: Revisit name. + serv ipld.NodeGetter +} + +// Size returns the total size of the data from the DAG structured file. +func (dr *dagReader) Size() uint64 { + return dr.size +} + +// Read implements the `io.Reader` interface through the `CtxReadFull` +// method using the DAG reader's internal context. +func (dr *dagReader) Read(b []byte) (int, error) { + return dr.CtxReadFull(dr.ctx, b) +} + +// CtxReadFull reads data from the DAG structured file. It always +// attempts a full read of the DAG until the `out` buffer is full. +// It uses the `Walker` structure to iterate the file DAG and read +// every node's data into the `out` buffer. +func (dr *dagReader) CtxReadFull(ctx context.Context, out []byte) (n int, err error) { + // Set the `dagWalker`'s context to the `ctx` argument, it will be used + // to fetch the child node promises (see + // `ipld.NavigableIPLDNode.FetchChild` for details). + dr.dagWalker.SetContext(ctx) + + // If there was a partially read buffer from the last visited + // node read it before visiting a new one. + if dr.currentNodeData != nil { + // TODO: Move this check inside `readNodeDataBuffer`? + n = dr.readNodeDataBuffer(out) + + if n == len(out) { + return n, nil + // Output buffer full, no need to traverse the DAG. + } + } + + // Iterate the DAG calling the passed `Visitor` function on every node + // to read its data into the `out` buffer, stop if there is an error or + // if the entire DAG is traversed (`EndOfDag`). + err = dr.dagWalker.Iterate(func(visitedNode ipld.NavigableNode) error { + node := ipld.ExtractIPLDNode(visitedNode) + + // Skip internal nodes, they shouldn't have any file data + // (see the `balanced` package for more details). + if len(node.Links()) > 0 { + return nil + } + + err = dr.saveNodeData(node) + if err != nil { + return err + } + // Save the leaf node file data in a buffer in case it is only + // partially read now and future `CtxReadFull` calls reclaim the + // rest (as each node is visited only once during `Iterate`). + // + // TODO: We could check if the entire node's data can fit in the + // remaining `out` buffer free space to skip this intermediary step. + + n += dr.readNodeDataBuffer(out[n:]) + + if n == len(out) { + // Output buffer full, no need to keep traversing the DAG, + // signal the `Walker` to pause the iteration. + dr.dagWalker.Pause() + } + + return nil + }) + + if err == ipld.EndOfDag { + return n, io.EOF + // Reached the end of the (DAG) file, no more data to read. + } else if err != nil { + return n, err + // Pass along any other errors from the `Visitor`. + } + + return n, nil +} + +// Save the UnixFS `node`'s data into the internal `currentNodeData` buffer to +// later move it to the output buffer (`Read`) or seek into it (`Seek`). +func (dr *dagReader) saveNodeData(node ipld.Node) error { + extractedNodeData, err := unixfs.ReadUnixFSNodeData(node) + if err != nil { + return err + } + + dr.currentNodeData = bytes.NewReader(extractedNodeData) + return nil +} + +// Read the `currentNodeData` buffer into `out`. This function can't have +// any errors as it's always reading from a `bytes.Reader` and asking only +// the available data in it. +func (dr *dagReader) readNodeDataBuffer(out []byte) int { + + n, _ := dr.currentNodeData.Read(out) + // Ignore the error as the EOF may not be returned in the first + // `Read` call, explicitly ask for an empty buffer below to check + // if we've reached the end. + + if dr.currentNodeData.Len() == 0 { + dr.currentNodeData = nil + // Signal that the buffer was consumed (for later `Read` calls). + // This shouldn't return an EOF error as it's just the end of a + // single node's data, not the entire DAG. + } + + dr.offset += int64(n) + // TODO: Should `offset` be incremented here or in the calling function? + // (Doing it here saves LoC but may be confusing as it's more hidden). + + return n +} + +// WriteTo writes to the given writer. +// +// TODO: Improve performance. It would be better to progressively +// write each node to the writer on every visit instead of allocating +// a huge buffer, that would imply defining a `Visitor` very similar +// to the one used in `CtxReadFull` (that would write to the `io.Writer` +// instead of the reading into the `currentNodeData` buffer). More +// consideration is needed to restructure those two `Visitor` functions +// to avoid repeating code. +func (dr *dagReader) WriteTo(w io.Writer) (int64, error) { + writeBuf, err := ioutil.ReadAll(dr) + if err != nil { + return 0, err + } + return bytes.NewReader(writeBuf).WriteTo(w) +} + +// Close the reader (cancelling fetch node operations requested with +// the internal context, that is, `Read` calls but not `CtxReadFull` +// with user-supplied contexts). +func (dr *dagReader) Close() error { + dr.cancel() + return nil +} + +// Seek implements `io.Seeker` seeking to a given offset in the DAG file, +// it matches the standard unix `seek`. It moves the position of the internal +// `dagWalker` and may also leave a `currentNodeData` buffer loaded in case +// the seek is performed to the middle of the data in a node. +// +// TODO: Support seeking from the current position (relative seek) +// through the `dagWalker` in `io.SeekCurrent`. +func (dr *dagReader) Seek(offset int64, whence int) (int64, error) { + switch whence { + case io.SeekStart: + if offset < 0 { + return -1, errors.New("invalid offset") + } + + if offset == dr.offset { + return offset, nil + // Already at the requested `offset`, nothing to do. + } + + left := offset + // Amount left to seek. + + // Seek from the beginning of the DAG. + dr.resetPosition() + + // Use the internal reader's context to fetch the child node promises + // (see `ipld.NavigableIPLDNode.FetchChild` for details). + dr.dagWalker.SetContext(dr.ctx) + // TODO: Performance: we could adjust here `preloadSize` of + // `ipld.NavigableIPLDNode` also, when seeking we only want + // to fetch one child at a time. + + // Seek the DAG by calling the provided `Visitor` function on every + // node the `dagWalker` descends to while searching which can be + // either an internal or leaf node. In the internal node case, check + // the child node sizes and set the corresponding child index to go + // down to next. In the leaf case (last visit of the search), if there + // is still an amount `left` to seek do it inside the node's data + // saved in the `currentNodeData` buffer, leaving it ready for a `Read` + // call. + err := dr.dagWalker.Seek(func(visitedNode ipld.NavigableNode) error { + node := ipld.ExtractIPLDNode(visitedNode) + + if len(node.Links()) > 0 { + // Internal node, should be a `mdag.ProtoNode` containing a + // `unixfs.FSNode` (see the `balanced` package for more details). + fsNode, err := unixfs.ExtractFSNode(node) + if err != nil { + return err + } + + // If there aren't enough size hints don't seek + // (see the `io.EOF` handling error comment below). + if fsNode.NumChildren() != len(node.Links()) { + return io.EOF + } + + // Internal nodes have no data, so just iterate through the + // sizes of its children (advancing the child index of the + // `dagWalker`) to find where we need to go down to next in + // the search. + for { + childSize := fsNode.BlockSize(int(dr.dagWalker.ActiveChildIndex())) + + if childSize > uint64(left) { + // This child's data contains the position requested + // in `offset`, go down this child. + return nil + } + + // Else, skip this child. + left -= int64(childSize) + err := dr.dagWalker.NextChild() + if err == ipld.ErrNextNoChild { + // No more child nodes available, nothing to do, + // the `Seek` will stop on its own. + return nil + } else if err != nil { + return err + // Pass along any other errors (that may in future + // implementations be returned by `Next`) to stop + // the search. + } + } + + } else { + // Leaf node, seek inside its data. + err := dr.saveNodeData(node) + if err != nil { + return err + } + + _, err = dr.currentNodeData.Seek(left, io.SeekStart) + if err != nil { + return err + } + // The corner case of a DAG consisting only of a single (leaf) + // node should make no difference here. In that case, where the + // node doesn't have a parent UnixFS node with size hints, this + // implementation would allow this `Seek` to be called with an + // argument larger than the buffer size which normally wouldn't + // happen (because we would skip the node based on the size + // hint) but that would just mean that a future `CtxReadFull` + // call would read no data from the `currentNodeData` buffer. + // TODO: Re-check this reasoning. + + return nil + // In the leaf node case the search will stop here. + } + }) + + if err == io.EOF { + // TODO: Taken from https://github.com/ipfs/go-ipfs/pull/4320, + // check if still valid. + // Return negative number if we can't figure out the file size. Using io.EOF + // for this seems to be good(-enough) solution as it's only returned by + // precalcNextBuf when we step out of file range. + // This is needed for gateway to function properly + return -1, nil + } + + if err != nil { + return 0, err + } + + dr.offset = offset + return dr.offset, nil + + case io.SeekCurrent: + if offset == 0 { + return dr.offset, nil + } + + return dr.Seek(dr.offset+offset, io.SeekStart) + // TODO: Performance. This can be improved supporting relative + // searches in the `Walker` (see `Walker.Seek`). + + case io.SeekEnd: + return dr.Seek(int64(dr.Size())-offset, io.SeekStart) + + default: + return 0, errors.New("invalid whence") + } +} + +// Reset the reader position by resetting the `dagWalker` and discarding +// any partially used node's data in the `currentNodeData` buffer, used +// in the `SeekStart` case. +func (dr *dagReader) resetPosition() { + dr.currentNodeData = nil + + dr.dagWalker = ipld.NewWalker(dr.ctx, ipld.NewNavigableIPLDNode(dr.rootNode, dr.serv)) + // TODO: This could be avoided (along with storing the `dr.rootNode` and + // `dr.serv` just for this call) if `Reset` is supported in the `Walker`. } diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index 6e1fef8d0..d79d2d1f5 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -4,7 +4,6 @@ import ( "bytes" "io" "io/ioutil" - "math/rand" "strings" "testing" @@ -73,90 +72,6 @@ func TestSeekAndRead(t *testing.T) { } } -func TestSeekAndReadLarge(t *testing.T) { - dserv := testu.GetDAGServ() - inbuf := make([]byte, 20000) - rand.Read(inbuf) - - node := testu.GetNode(t, dserv, inbuf, testu.UseProtoBufLeaves) - ctx, closer := context.WithCancel(context.Background()) - defer closer() - - reader, err := NewDagReader(ctx, node, dserv) - if err != nil { - t.Fatal(err) - } - - _, err = reader.Seek(10000, io.SeekStart) - if err != nil { - t.Fatal(err) - } - - buf := make([]byte, 100) - _, err = io.ReadFull(reader, buf) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(buf, inbuf[10000:10100]) { - t.Fatal("seeked read failed") - } - - pbdr := reader.(*PBDagReader) - var count int - for i, p := range pbdr.promises { - if i > 20 && i < 30 { - if p == nil { - t.Fatal("expected index to be not nil: ", i) - } - count++ - } else { - if p != nil { - t.Fatal("expected index to be nil: ", i) - } - } - } - // -1 because we read some and it cleared one - if count != preloadSize-1 { - t.Fatalf("expected %d preloaded promises, got %d", preloadSize-1, count) - } -} - -func TestReadAndCancel(t *testing.T) { - dserv := testu.GetDAGServ() - inbuf := make([]byte, 20000) - rand.Read(inbuf) - - node := testu.GetNode(t, dserv, inbuf, testu.UseProtoBufLeaves) - ctx, closer := context.WithCancel(context.Background()) - defer closer() - - reader, err := NewDagReader(ctx, node, dserv) - if err != nil { - t.Fatal(err) - } - - ctx, cancel := context.WithCancel(context.Background()) - buf := make([]byte, 100) - _, err = reader.CtxReadFull(ctx, buf) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(buf, inbuf[0:100]) { - t.Fatal("read failed") - } - cancel() - - b, err := ioutil.ReadAll(reader) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(inbuf[100:], b) { - t.Fatal("buffers not equal") - } -} - func TestRelativeSeek(t *testing.T) { dserv := testu.GetDAGServ() ctx, closer := context.WithCancel(context.Background()) diff --git a/unixfs/io/pbdagreader.go b/unixfs/io/pbdagreader.go deleted file mode 100644 index bea5f496e..000000000 --- a/unixfs/io/pbdagreader.go +++ /dev/null @@ -1,326 +0,0 @@ -package io - -import ( - "context" - "errors" - "fmt" - "io" - - cid "github.com/ipfs/go-cid" - ipld "github.com/ipfs/go-ipld-format" - mdag "github.com/ipfs/go-merkledag" - ft "github.com/ipfs/go-unixfs" -) - -// PBDagReader provides a way to easily read the data contained in a dag. -type PBDagReader struct { - serv ipld.NodeGetter - - // UnixFS file (it should be of type `Data_File` or `Data_Raw` only). - file *ft.FSNode - - // the current data buffer to be read from - // will either be a bytes.Reader or a child DagReader - buf ReadSeekCloser - - // NodePromises for each of 'nodes' child links - promises []*ipld.NodePromise - - // the cid of each child of the current node - links []cid.Cid - - // the index of the child link currently being read from - linkPosition int - - // current offset for the read head within the 'file' - offset int64 - - // Our context - ctx context.Context - - // context cancel for children - cancel func() -} - -var _ DagReader = (*PBDagReader)(nil) - -// NewPBFileReader constructs a new PBFileReader. -func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, file *ft.FSNode, serv ipld.NodeGetter) *PBDagReader { - fctx, cancel := context.WithCancel(ctx) - curLinks := getLinkCids(n) - return &PBDagReader{ - serv: serv, - buf: NewBufDagReader(file.Data()), - promises: make([]*ipld.NodePromise, len(curLinks)), - links: curLinks, - ctx: fctx, - cancel: cancel, - file: file, - } -} - -const preloadSize = 10 - -func (dr *PBDagReader) preload(ctx context.Context, beg int) { - end := beg + preloadSize - if end >= len(dr.links) { - end = len(dr.links) - } - - copy(dr.promises[beg:], ipld.GetNodes(ctx, dr.serv, dr.links[beg:end])) -} - -// precalcNextBuf follows the next link in line and loads it from the -// DAGService, setting the next buffer to read from -func (dr *PBDagReader) precalcNextBuf(ctx context.Context) error { - if dr.buf != nil { - dr.buf.Close() // Just to make sure - dr.buf = nil - } - - if dr.linkPosition >= len(dr.promises) { - return io.EOF - } - - // If we drop to <= preloadSize/2 preloading nodes, preload the next 10. - for i := dr.linkPosition; i < dr.linkPosition+preloadSize/2 && i < len(dr.promises); i++ { - // TODO: check if canceled. - if dr.promises[i] == nil { - dr.preload(ctx, i) - break - } - } - - nxt, err := dr.promises[dr.linkPosition].Get(ctx) - dr.promises[dr.linkPosition] = nil - switch err { - case nil: - case context.DeadlineExceeded, context.Canceled: - err = ctx.Err() - if err != nil { - return ctx.Err() - } - // In this case, the context used to *preload* the node has been canceled. - // We need to retry the load with our context and we might as - // well preload some extra nodes while we're at it. - // - // Note: When using `Read`, this code will never execute as - // `Read` will use the global context. It only runs if the user - // explicitly reads with a custom context (e.g., by calling - // `CtxReadFull`). - dr.preload(ctx, dr.linkPosition) - nxt, err = dr.promises[dr.linkPosition].Get(ctx) - dr.promises[dr.linkPosition] = nil - if err != nil { - return err - } - default: - return err - } - - dr.linkPosition++ - - return dr.loadBufNode(nxt) -} - -func (dr *PBDagReader) loadBufNode(node ipld.Node) error { - switch node := node.(type) { - case *mdag.ProtoNode: - fsNode, err := ft.FSNodeFromBytes(node.Data()) - if err != nil { - return fmt.Errorf("incorrectly formatted protobuf: %s", err) - } - - switch fsNode.Type() { - case ft.TFile: - dr.buf = NewPBFileReader(dr.ctx, node, fsNode, dr.serv) - return nil - case ft.TRaw: - dr.buf = NewBufDagReader(fsNode.Data()) - return nil - default: - return fmt.Errorf("found %s node in unexpected place", fsNode.Type().String()) - } - case *mdag.RawNode: - dr.buf = NewBufDagReader(node.RawData()) - return nil - default: - return ErrUnkownNodeType - } -} - -func getLinkCids(n ipld.Node) []cid.Cid { - links := n.Links() - out := make([]cid.Cid, 0, len(links)) - for _, l := range links { - out = append(out, l.Cid) - } - return out -} - -// Size return the total length of the data from the DAG structured file. -func (dr *PBDagReader) Size() uint64 { - return dr.file.FileSize() -} - -// Read reads data from the DAG structured file -func (dr *PBDagReader) Read(b []byte) (int, error) { - return dr.CtxReadFull(dr.ctx, b) -} - -// CtxReadFull reads data from the DAG structured file -func (dr *PBDagReader) CtxReadFull(ctx context.Context, b []byte) (int, error) { - if dr.buf == nil { - if err := dr.precalcNextBuf(ctx); err != nil { - return 0, err - } - } - - // If no cached buffer, load one - total := 0 - for { - // Attempt to fill bytes from cached buffer - n, err := io.ReadFull(dr.buf, b[total:]) - total += n - dr.offset += int64(n) - switch err { - // io.EOF will happen is dr.buf had noting more to read (n == 0) - case io.EOF, io.ErrUnexpectedEOF: - // do nothing - case nil: - return total, nil - default: - return total, err - } - - // if we are not done with the output buffer load next block - err = dr.precalcNextBuf(ctx) - if err != nil { - return total, err - } - } -} - -// WriteTo writes to the given writer. -func (dr *PBDagReader) WriteTo(w io.Writer) (int64, error) { - if dr.buf == nil { - if err := dr.precalcNextBuf(dr.ctx); err != nil { - return 0, err - } - } - - // If no cached buffer, load one - total := int64(0) - for { - // Attempt to write bytes from cached buffer - n, err := dr.buf.WriteTo(w) - total += n - dr.offset += n - if err != nil { - if err != io.EOF { - return total, err - } - } - - // Otherwise, load up the next block - err = dr.precalcNextBuf(dr.ctx) - if err != nil { - if err == io.EOF { - return total, nil - } - return total, err - } - } -} - -// Close closes the reader. -func (dr *PBDagReader) Close() error { - dr.cancel() - return nil -} - -// Seek implements io.Seeker, and will seek to a given offset in the file -// interface matches standard unix seek -// TODO: check if we can do relative seeks, to reduce the amount of dagreader -// recreations that need to happen. -func (dr *PBDagReader) Seek(offset int64, whence int) (int64, error) { - switch whence { - case io.SeekStart: - if offset < 0 { - return -1, errors.New("invalid offset") - } - if offset == dr.offset { - return offset, nil - } - - // left represents the number of bytes remaining to seek to (from beginning) - left := offset - if int64(len(dr.file.Data())) >= offset { - // Close current buf to close potential child dagreader - if dr.buf != nil { - dr.buf.Close() - } - dr.buf = NewBufDagReader(dr.file.Data()[offset:]) - - // start reading links from the beginning - dr.linkPosition = 0 - dr.offset = offset - return offset, nil - } - - // skip past root block data - left -= int64(len(dr.file.Data())) - - // iterate through links and find where we need to be - for i := 0; i < dr.file.NumChildren(); i++ { - if dr.file.BlockSize(i) > uint64(left) { - dr.linkPosition = i - break - } else { - left -= int64(dr.file.BlockSize(i)) - } - } - - // start sub-block request - err := dr.precalcNextBuf(dr.ctx) - if err != nil { - return 0, err - } - - // set proper offset within child readseeker - n, err := dr.buf.Seek(left, io.SeekStart) - if err != nil { - return -1, err - } - - // sanity - left -= n - if left != 0 { - return -1, errors.New("failed to seek properly") - } - dr.offset = offset - return offset, nil - case io.SeekCurrent: - // TODO: be smarter here - if offset == 0 { - return dr.offset, nil - } - - noffset := dr.offset + offset - return dr.Seek(noffset, io.SeekStart) - case io.SeekEnd: - noffset := int64(dr.file.FileSize()) - offset - n, err := dr.Seek(noffset, io.SeekStart) - - // Return negative number if we can't figure out the file size. Using io.EOF - // for this seems to be good(-enough) solution as it's only returned by - // precalcNextBuf when we step out of file range. - // This is needed for gateway to function properly - if err == io.EOF && dr.file.Type() == ft.TFile { - return -1, nil - } - return n, err - default: - return 0, errors.New("invalid whence") - } -} diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index 4ee755186..84caf6f44 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -5,9 +5,9 @@ package unixfs import ( "errors" + "fmt" proto "github.com/gogo/protobuf/proto" - dag "github.com/ipfs/go-merkledag" ipld "github.com/ipfs/go-ipld-format" @@ -355,3 +355,54 @@ func BytesForMetadata(m *Metadata) ([]byte, error) { func EmptyDirNode() *dag.ProtoNode { return dag.NodeWithData(FolderPBData()) } + +// ReadUnixFSNodeData extracts the UnixFS data from an IPLD node. +// Raw nodes are (also) processed because they are used as leaf +// nodes containing (only) UnixFS data. +func ReadUnixFSNodeData(node ipld.Node) (data []byte, err error) { + switch node := node.(type) { + + case *dag.ProtoNode: + fsNode, err := FSNodeFromBytes(node.Data()) + if err != nil { + return nil, fmt.Errorf("incorrectly formatted protobuf: %s", err) + } + + switch fsNode.Type() { + case pb.Data_File, pb.Data_Raw: + return fsNode.Data(), nil + // Only leaf nodes (of type `Data_Raw`) contain data but due to a + // bug the `Data_File` type (normally used for internal nodes) is + // also used for leaf nodes, so both types are accepted here + // (see the `balanced` package for more details). + default: + return nil, fmt.Errorf("found %s node in unexpected place", + fsNode.Type().String()) + } + + case *dag.RawNode: + return node.RawData(), nil + + default: + return nil, ErrUnrecognizedType + // TODO: To avoid rewriting the error message, but a different error from + // `unixfs.ErrUnrecognizedType` should be used (defining it in the + // `merkledag` or `go-ipld-format` packages). + } +} + +// Extract the `unixfs.FSNode` from the `ipld.Node` (assuming this +// was implemented by a `mdag.ProtoNode`). +func ExtractFSNode(node ipld.Node) (*FSNode, error) { + protoNode, ok := node.(*dag.ProtoNode) + if !ok { + return nil, errors.New("expected a ProtoNode as internal node") + } + + fsNode, err := FSNodeFromBytes(protoNode.Data()) + if err != nil { + return nil, err + } + + return fsNode, nil +} From 5dcc2c65010d385c0c5de4c0bab81b8d3fce7ff9 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Fri, 18 Jan 2019 13:14:04 -0300 Subject: [PATCH 2890/3817] io: implement a performant `WriteTo` version in the DAG reader This version writes one node at a time instead of first buffering the entire file contents in a single array (taking up too much memory) before actually writing it. This commit was moved from ipfs/go-unixfs@ac466e6663cc3b3615991235ceccc3ba42477cb2 --- unixfs/io/dagreader.go | 93 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 333f7999f..d5de73661 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -5,7 +5,6 @@ import ( "context" "errors" "io" - "io/ioutil" ipld "github.com/ipfs/go-ipld-format" mdag "github.com/ipfs/go-merkledag" @@ -247,21 +246,89 @@ func (dr *dagReader) readNodeDataBuffer(out []byte) int { return n } -// WriteTo writes to the given writer. +// Similar to `readNodeDataBuffer` but it writes the contents to +// an `io.Writer` argument. // -// TODO: Improve performance. It would be better to progressively -// write each node to the writer on every visit instead of allocating -// a huge buffer, that would imply defining a `Visitor` very similar -// to the one used in `CtxReadFull` (that would write to the `io.Writer` -// instead of the reading into the `currentNodeData` buffer). More -// consideration is needed to restructure those two `Visitor` functions -// to avoid repeating code. -func (dr *dagReader) WriteTo(w io.Writer) (int64, error) { - writeBuf, err := ioutil.ReadAll(dr) +// TODO: Check what part of the logic between the two functions +// can be extracted away. +func (dr *dagReader) writeNodeDataBuffer(w io.Writer) (int64, error) { + + n, err := dr.currentNodeData.WriteTo(w) if err != nil { - return 0, err + return n, err + } + + if dr.currentNodeData.Len() == 0 { + dr.currentNodeData = nil + // Signal that the buffer was consumed (for later `Read` calls). + // This shouldn't return an EOF error as it's just the end of a + // single node's data, not the entire DAG. } - return bytes.NewReader(writeBuf).WriteTo(w) + + dr.offset += int64(n) + return n, nil +} + +// WriteTo writes to the given writer. +// This follows the `bytes.Reader.WriteTo` implementation +// where it starts from the internal index that may have +// been modified by other `Read` calls. +// +// TODO: This implementation is very similar to `CtxReadFull`, +// the common parts should be abstracted away. +func (dr *dagReader) WriteTo(w io.Writer) (n int64, err error) { + // Use the internal reader's context to fetch the child node promises + // (see `ipld.NavigableIPLDNode.FetchChild` for details). + dr.dagWalker.SetContext(dr.ctx) + + // If there was a partially read buffer from the last visited + // node read it before visiting a new one. + if dr.currentNodeData != nil { + n, err = dr.writeNodeDataBuffer(w) + if err != nil { + return n, err + } + } + + // Iterate the DAG calling the passed `Visitor` function on every node + // to read its data into the `out` buffer, stop if there is an error or + // if the entire DAG is traversed (`EndOfDag`). + err = dr.dagWalker.Iterate(func(visitedNode ipld.NavigableNode) error { + node := ipld.ExtractIPLDNode(visitedNode) + + // Skip internal nodes, they shouldn't have any file data + // (see the `balanced` package for more details). + if len(node.Links()) > 0 { + return nil + } + + err = dr.saveNodeData(node) + if err != nil { + return err + } + // Save the leaf node file data in a buffer in case it is only + // partially read now and future `CtxReadFull` calls reclaim the + // rest (as each node is visited only once during `Iterate`). + + written, err := dr.writeNodeDataBuffer(w) + n += written + if err != nil { + return err + } + + return nil + }) + + if err == ipld.EndOfDag { + return n, io.EOF + // Reached the end of the (DAG) file, no more data to read. + // TODO: Is this a correct return error for `WriteTo`? + } else if err != nil { + return n, err + // Pass along any other errors from the `Visitor`. + } + + return n, nil } // Close the reader (cancelling fetch node operations requested with From 112da791a6dc321029c2cb34adb7f139553be427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 28 Jan 2019 18:13:57 +0100 Subject: [PATCH 2891/3817] io: return nil when EOF is reached in WriteTo This commit was moved from ipfs/go-unixfs@9dacd09aadb6b5e9110838bb503e6b949ca2fce9 --- unixfs/io/dagreader.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index d5de73661..8a6475ab1 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -320,15 +320,10 @@ func (dr *dagReader) WriteTo(w io.Writer) (n int64, err error) { }) if err == ipld.EndOfDag { - return n, io.EOF - // Reached the end of the (DAG) file, no more data to read. - // TODO: Is this a correct return error for `WriteTo`? - } else if err != nil { - return n, err - // Pass along any other errors from the `Visitor`. + return n, nil } - return n, nil + return n, err } // Close the reader (cancelling fetch node operations requested with From ca2496aff20dde4e4f7a7d994a2e780835a4925a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 28 Jan 2019 18:16:11 +0100 Subject: [PATCH 2892/3817] io: correctly handle seek offset with io.SeekEnd This commit was moved from ipfs/go-unixfs@cdb64d5a2a22520819745e256a6eb70b4309bd09 --- unixfs/io/dagreader.go | 2 +- unixfs/io/dagreader_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 8a6475ab1..75fb5c714 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -472,7 +472,7 @@ func (dr *dagReader) Seek(offset int64, whence int) (int64, error) { // searches in the `Walker` (see `Walker.Seek`). case io.SeekEnd: - return dr.Seek(int64(dr.Size())-offset, io.SeekStart) + return dr.Seek(int64(dr.Size())+offset, io.SeekStart) default: return 0, errors.New("invalid whence") diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index d79d2d1f5..884ae332d 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -107,7 +107,7 @@ func TestRelativeSeek(t *testing.T) { } } - _, err = reader.Seek(4, io.SeekEnd) + _, err = reader.Seek(-4, io.SeekEnd) if err != nil { t.Fatal(err) } From 620b4e2181ea25c55d05a6914e3d87894c2146f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 24 Jan 2019 19:24:18 +0100 Subject: [PATCH 2893/3817] archive: use files instead of ipld This commit was moved from ipfs/go-unixfs@cf8e7c8a2424273a6076472151b93436df4c5151 --- unixfs/archive/archive.go | 118 ++++++++++++++++++++++++---- unixfs/archive/tar/writer.go | 146 ----------------------------------- 2 files changed, 105 insertions(+), 159 deletions(-) delete mode 100644 unixfs/archive/tar/writer.go diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index 6396ca0ae..19af431a0 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -2,16 +2,16 @@ package archive import ( + "archive/tar" "bufio" "compress/gzip" - "context" + "errors" + "fmt" "io" "path" + "time" - tar "github.com/ipfs/go-unixfs/archive/tar" - uio "github.com/ipfs/go-unixfs/io" - - ipld "github.com/ipfs/go-ipld-format" + files "github.com/ipfs/go-ipfs-files" ) // DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. @@ -31,8 +31,7 @@ func (i *identityWriteCloser) Close() error { } // DagArchive is equivalent to `ipfs getdag $hash | maybe_tar | maybe_gzip` -func DagArchive(ctx context.Context, nd ipld.Node, name string, dag ipld.DAGService, archive bool, compression int) (io.Reader, error) { - +func FileArchive(f files.Node, name string, archive bool, compression int) (io.Reader, error) { cleaned := path.Clean(name) _, filename := path.Split(cleaned) @@ -67,13 +66,13 @@ func DagArchive(ctx context.Context, nd ipld.Node, name string, dag ipld.DAGServ if !archive && compression != gzip.NoCompression { // the case when the node is a file - dagr, err := uio.NewDagReader(ctx, nd, dag) - if checkErrAndClosePipe(err) { - return nil, err + r := files.ToFile(f) + if r == nil { + return nil, errors.New("file is not regular") } go func() { - if _, err := dagr.WriteTo(maybeGzw); checkErrAndClosePipe(err) { + if _, err := io.Copy(maybeGzw, r); checkErrAndClosePipe(err) { return } closeGzwAndPipe() // everything seems to be ok @@ -82,14 +81,14 @@ func DagArchive(ctx context.Context, nd ipld.Node, name string, dag ipld.DAGServ // the case for 1. archive, and 2. not archived and not compressed, in which tar is used anyway as a transport format // construct the tar writer - w, err := tar.NewWriter(ctx, dag, maybeGzw) + w, err := NewWriter(maybeGzw) if checkErrAndClosePipe(err) { return nil, err } go func() { // write all the nodes recursively - if err := w.WriteNode(nd, filename); checkErrAndClosePipe(err) { + if err := w.WriteFile(f, filename); checkErrAndClosePipe(err) { return } w.Close() // close tar writer @@ -106,3 +105,96 @@ func newMaybeGzWriter(w io.Writer, compression int) (io.WriteCloser, error) { } return &identityWriteCloser{w}, nil } + +type Writer struct { + + TarW *tar.Writer +} + +// NewWriter wraps given io.Writer. +func NewWriter(w io.Writer) (*Writer, error) { + return &Writer{ + TarW: tar.NewWriter(w), + }, nil +} + +func (w *Writer) writeDir(f files.Directory, fpath string) error { + if err := writeDirHeader(w.TarW, fpath); err != nil { + return err + } + + it := f.Entries() + for it.Next() { + if err := w.WriteFile(it.Node(), path.Join(fpath, it.Name())); err != nil { + return err + } + } + return it.Err() +} + +func (w *Writer) writeFile(f files.File, fpath string) error { + size, err := f.Size() + if err != nil { + return err + } + + if err := writeFileHeader(w.TarW, fpath, uint64(size)); err != nil { + return err + } + + if _, err := io.Copy(w.TarW, f); err != nil { + return err + } + w.TarW.Flush() + return nil +} + +// WriteNode adds a node to the archive. +func (w *Writer) WriteFile(nd files.Node, fpath string) error { + switch nd := nd.(type) { + case *files.Symlink: + return writeSymlinkHeader(w.TarW, nd.Target, fpath) + case files.File: + return w.writeFile(nd, fpath) + case files.Directory: + return w.writeDir(nd, fpath) + default: + return fmt.Errorf("file type %T is not supported", nd) + } +} + +// Close closes the tar writer. +func (w *Writer) Close() error { + return w.TarW.Close() +} + +func writeDirHeader(w *tar.Writer, fpath string) error { + return w.WriteHeader(&tar.Header{ + Name: fpath, + Typeflag: tar.TypeDir, + Mode: 0777, + ModTime: time.Now(), + // TODO: set mode, dates, etc. when added to unixFS + }) +} + +func writeFileHeader(w *tar.Writer, fpath string, size uint64) error { + return w.WriteHeader(&tar.Header{ + Name: fpath, + Size: int64(size), + Typeflag: tar.TypeReg, + Mode: 0644, + ModTime: time.Now(), + // TODO: set mode, dates, etc. when added to unixFS + }) +} + +func writeSymlinkHeader(w *tar.Writer, target, fpath string) error { + return w.WriteHeader(&tar.Header{ + Name: fpath, + Linkname: target, + Mode: 0777, + Typeflag: tar.TypeSymlink, + }) +} + diff --git a/unixfs/archive/tar/writer.go b/unixfs/archive/tar/writer.go deleted file mode 100644 index 0e91ba9b0..000000000 --- a/unixfs/archive/tar/writer.go +++ /dev/null @@ -1,146 +0,0 @@ -// Package tar provides functionality to write a unixfs merkledag -// as a tar archive. -package tar - -import ( - "archive/tar" - "context" - "fmt" - "io" - "path" - "time" - - mdag "github.com/ipfs/go-merkledag" - ft "github.com/ipfs/go-unixfs" - uio "github.com/ipfs/go-unixfs/io" - - ipld "github.com/ipfs/go-ipld-format" -) - -// Writer is a utility structure that helps to write -// unixfs merkledag nodes as a tar archive format. -// It wraps any io.Writer. -type Writer struct { - Dag ipld.DAGService - TarW *tar.Writer - - ctx context.Context -} - -// NewWriter wraps given io.Writer. -func NewWriter(ctx context.Context, dag ipld.DAGService, w io.Writer) (*Writer, error) { - return &Writer{ - Dag: dag, - TarW: tar.NewWriter(w), - ctx: ctx, - }, nil -} - -func (w *Writer) writeDir(nd *mdag.ProtoNode, fpath string) error { - dir, err := uio.NewDirectoryFromNode(w.Dag, nd) - if err != nil { - return err - } - if err := writeDirHeader(w.TarW, fpath); err != nil { - return err - } - - return dir.ForEachLink(w.ctx, func(l *ipld.Link) error { - child, err := w.Dag.Get(w.ctx, l.Cid) - if err != nil { - return err - } - npath := path.Join(fpath, l.Name) - return w.WriteNode(child, npath) - }) -} - -func (w *Writer) writeFile(nd *mdag.ProtoNode, fsNode *ft.FSNode, fpath string) error { - if err := writeFileHeader(w.TarW, fpath, fsNode.FileSize()); err != nil { - return err - } - - dagr, err := uio.NewDagReader(w.ctx, nd, w.Dag) - if err != nil { - return err - } - - if _, err := dagr.WriteTo(w.TarW); err != nil { - return err - } - w.TarW.Flush() - return nil -} - -// WriteNode adds a node to the archive. -func (w *Writer) WriteNode(nd ipld.Node, fpath string) error { - switch nd := nd.(type) { - case *mdag.ProtoNode: - fsNode, err := ft.FSNodeFromBytes(nd.Data()) - if err != nil { - return err - } - - switch fsNode.Type() { - case ft.TMetadata: - fallthrough - case ft.TDirectory, ft.THAMTShard: - return w.writeDir(nd, fpath) - case ft.TRaw: - fallthrough - case ft.TFile: - return w.writeFile(nd, fsNode, fpath) - case ft.TSymlink: - return writeSymlinkHeader(w.TarW, string(fsNode.Data()), fpath) - default: - return ft.ErrUnrecognizedType - } - case *mdag.RawNode: - if err := writeFileHeader(w.TarW, fpath, uint64(len(nd.RawData()))); err != nil { - return err - } - - if _, err := w.TarW.Write(nd.RawData()); err != nil { - return err - } - w.TarW.Flush() - return nil - default: - return fmt.Errorf("nodes of type %T are not supported in unixfs", nd) - } -} - -// Close closes the tar writer. -func (w *Writer) Close() error { - return w.TarW.Close() -} - -func writeDirHeader(w *tar.Writer, fpath string) error { - return w.WriteHeader(&tar.Header{ - Name: fpath, - Typeflag: tar.TypeDir, - Mode: 0777, - ModTime: time.Now(), - // TODO: set mode, dates, etc. when added to unixFS - }) -} - -func writeFileHeader(w *tar.Writer, fpath string, size uint64) error { - return w.WriteHeader(&tar.Header{ - Name: fpath, - Size: int64(size), - Typeflag: tar.TypeReg, - Mode: 0644, - ModTime: time.Now(), - // TODO: set mode, dates, etc. when added to unixFS - }) -} - -func writeSymlinkHeader(w *tar.Writer, target, fpath string) error { - return w.WriteHeader(&tar.Header{ - Name: fpath, - Linkname: target, - Mode: 0777, - Typeflag: tar.TypeSymlink, - }) -} From e5205c486694dad9f040999867c99472a72fdc01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 24 Jan 2019 20:36:18 +0100 Subject: [PATCH 2894/3817] generate archives from files instead of dag nodes This commit was moved from ipfs/go-unixfs@de8cb1497905d51fd13d6165ba0069a596ccbe70 --- unixfs/archive/archive.go | 97 +-------------------------------------- 1 file changed, 1 insertion(+), 96 deletions(-) diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go index 19af431a0..c90e3e859 100644 --- a/unixfs/archive/archive.go +++ b/unixfs/archive/archive.go @@ -2,14 +2,11 @@ package archive import ( - "archive/tar" "bufio" "compress/gzip" "errors" - "fmt" "io" "path" - "time" files "github.com/ipfs/go-ipfs-files" ) @@ -81,7 +78,7 @@ func FileArchive(f files.Node, name string, archive bool, compression int) (io.R // the case for 1. archive, and 2. not archived and not compressed, in which tar is used anyway as a transport format // construct the tar writer - w, err := NewWriter(maybeGzw) + w, err := files.NewTarWriter(maybeGzw) if checkErrAndClosePipe(err) { return nil, err } @@ -106,95 +103,3 @@ func newMaybeGzWriter(w io.Writer, compression int) (io.WriteCloser, error) { return &identityWriteCloser{w}, nil } -type Writer struct { - - TarW *tar.Writer -} - -// NewWriter wraps given io.Writer. -func NewWriter(w io.Writer) (*Writer, error) { - return &Writer{ - TarW: tar.NewWriter(w), - }, nil -} - -func (w *Writer) writeDir(f files.Directory, fpath string) error { - if err := writeDirHeader(w.TarW, fpath); err != nil { - return err - } - - it := f.Entries() - for it.Next() { - if err := w.WriteFile(it.Node(), path.Join(fpath, it.Name())); err != nil { - return err - } - } - return it.Err() -} - -func (w *Writer) writeFile(f files.File, fpath string) error { - size, err := f.Size() - if err != nil { - return err - } - - if err := writeFileHeader(w.TarW, fpath, uint64(size)); err != nil { - return err - } - - if _, err := io.Copy(w.TarW, f); err != nil { - return err - } - w.TarW.Flush() - return nil -} - -// WriteNode adds a node to the archive. -func (w *Writer) WriteFile(nd files.Node, fpath string) error { - switch nd := nd.(type) { - case *files.Symlink: - return writeSymlinkHeader(w.TarW, nd.Target, fpath) - case files.File: - return w.writeFile(nd, fpath) - case files.Directory: - return w.writeDir(nd, fpath) - default: - return fmt.Errorf("file type %T is not supported", nd) - } -} - -// Close closes the tar writer. -func (w *Writer) Close() error { - return w.TarW.Close() -} - -func writeDirHeader(w *tar.Writer, fpath string) error { - return w.WriteHeader(&tar.Header{ - Name: fpath, - Typeflag: tar.TypeDir, - Mode: 0777, - ModTime: time.Now(), - // TODO: set mode, dates, etc. when added to unixFS - }) -} - -func writeFileHeader(w *tar.Writer, fpath string, size uint64) error { - return w.WriteHeader(&tar.Header{ - Name: fpath, - Size: int64(size), - Typeflag: tar.TypeReg, - Mode: 0644, - ModTime: time.Now(), - // TODO: set mode, dates, etc. when added to unixFS - }) -} - -func writeSymlinkHeader(w *tar.Writer, target, fpath string) error { - return w.WriteHeader(&tar.Header{ - Name: fpath, - Linkname: target, - Mode: 0777, - Typeflag: tar.TypeSymlink, - }) -} - From 69fd88577161414157919a1c82f1c8aa5cedb3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 24 Jan 2019 20:36:44 +0100 Subject: [PATCH 2895/3817] move unixfile here from go-ipfs This commit was moved from ipfs/go-unixfs@7dbe59d5c78caa2e588768899a6b10b0edc96656 --- unixfs/file/unixfile.go | 181 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 unixfs/file/unixfile.go diff --git a/unixfs/file/unixfile.go b/unixfs/file/unixfile.go new file mode 100644 index 000000000..6617591fc --- /dev/null +++ b/unixfs/file/unixfile.go @@ -0,0 +1,181 @@ +package unixfile + +import ( + "context" + "errors" + + ft "github.com/ipfs/go-unixfs" + uio "github.com/ipfs/go-unixfs/io" + + files "github.com/ipfs/go-ipfs-files" + ipld "github.com/ipfs/go-ipld-format" + dag "github.com/ipfs/go-merkledag" +) + +// Number to file to prefetch in directories +// TODO: should we allow setting this via context hint? +const prefetchFiles = 4 + +type ufsDirectory struct { + ctx context.Context + dserv ipld.DAGService + dir uio.Directory +} + +type ufsIterator struct { + ctx context.Context + files chan *ipld.Link + dserv ipld.DAGService + + curName string + curFile files.Node + + err error + errCh chan error +} + +func (it *ufsIterator) Name() string { + return it.curName +} + +func (it *ufsIterator) Node() files.Node { + return it.curFile +} + +func (it *ufsIterator) Next() bool { + if it.err != nil { + return false + } + + var l *ipld.Link + var ok bool + for !ok { + if it.files == nil && it.errCh == nil { + return false + } + select { + case l, ok = <-it.files: + if !ok { + it.files = nil + } + case err := <-it.errCh: + it.errCh = nil + it.err = err + + if err != nil { + return false + } + } + } + + it.curFile = nil + + nd, err := l.GetNode(it.ctx, it.dserv) + if err != nil { + it.err = err + return false + } + + it.curName = l.Name + it.curFile, it.err = NewUnixfsFile(it.ctx, it.dserv, nd) + return it.err == nil +} + +func (it *ufsIterator) Err() error { + return it.err +} + +func (d *ufsDirectory) Close() error { + return nil +} + +func (d *ufsDirectory) Entries() files.DirIterator { + fileCh := make(chan *ipld.Link, prefetchFiles) + errCh := make(chan error, 1) + go func() { + errCh <- d.dir.ForEachLink(d.ctx, func(link *ipld.Link) error { + if d.ctx.Err() != nil { + return d.ctx.Err() + } + select { + case fileCh <- link: + case <-d.ctx.Done(): + return d.ctx.Err() + } + return nil + }) + + close(errCh) + close(fileCh) + }() + + return &ufsIterator{ + ctx: d.ctx, + files: fileCh, + errCh: errCh, + dserv: d.dserv, + } +} + +func (d *ufsDirectory) Size() (int64, error) { + n, err := d.dir.GetNode() + if err != nil { + return 0, err + } + s, err := n.Size() + return int64(s), err +} + +type ufsFile struct { + uio.DagReader +} + +func (f *ufsFile) Size() (int64, error) { + return int64(f.DagReader.Size()), nil +} + +func newUnixfsDir(ctx context.Context, dserv ipld.DAGService, nd ipld.Node) (files.Directory, error) { + dir, err := uio.NewDirectoryFromNode(dserv, nd) + if err != nil { + return nil, err + } + + return &ufsDirectory{ + ctx: ctx, + dserv: dserv, + + dir: dir, + }, nil +} + +func NewUnixfsFile(ctx context.Context, dserv ipld.DAGService, nd ipld.Node) (files.Node, error) { + switch dn := nd.(type) { + case *dag.ProtoNode: + fsn, err := ft.FSNodeFromBytes(dn.Data()) + if err != nil { + return nil, err + } + if fsn.IsDir() { + return newUnixfsDir(ctx, dserv, nd) + } + if fsn.Type() == ft.TSymlink { + return files.NewLinkFile(string(fsn.Data()), nil), nil + } + + case *dag.RawNode: + default: + return nil, errors.New("unknown node type") + } + + dr, err := uio.NewDagReader(ctx, nd, dserv) + if err != nil { + return nil, err + } + + return &ufsFile{ + DagReader: dr, + }, nil +} + +var _ files.Directory = &ufsDirectory{} +var _ files.File = &ufsFile{} From 699671692038e5a44f40ce7e80b3ec7c6cf3490d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 30 Jan 2019 16:14:09 +0100 Subject: [PATCH 2896/3817] archiwe: move to go-ipfs get command This commit was moved from ipfs/go-unixfs@94dca9ceff20878acd96922890a39743c43f7d0a --- unixfs/archive/archive.go | 105 -------------------------------------- 1 file changed, 105 deletions(-) delete mode 100644 unixfs/archive/archive.go diff --git a/unixfs/archive/archive.go b/unixfs/archive/archive.go deleted file mode 100644 index c90e3e859..000000000 --- a/unixfs/archive/archive.go +++ /dev/null @@ -1,105 +0,0 @@ -// Package archive provides utilities to archive and compress a [Unixfs] DAG. -package archive - -import ( - "bufio" - "compress/gzip" - "errors" - "io" - "path" - - files "github.com/ipfs/go-ipfs-files" -) - -// DefaultBufSize is the buffer size for gets. for now, 1MB, which is ~4 blocks. -// TODO: does this need to be configurable? -var DefaultBufSize = 1048576 - -type identityWriteCloser struct { - w io.Writer -} - -func (i *identityWriteCloser) Write(p []byte) (int, error) { - return i.w.Write(p) -} - -func (i *identityWriteCloser) Close() error { - return nil -} - -// DagArchive is equivalent to `ipfs getdag $hash | maybe_tar | maybe_gzip` -func FileArchive(f files.Node, name string, archive bool, compression int) (io.Reader, error) { - cleaned := path.Clean(name) - _, filename := path.Split(cleaned) - - // need to connect a writer to a reader - piper, pipew := io.Pipe() - checkErrAndClosePipe := func(err error) bool { - if err != nil { - pipew.CloseWithError(err) - return true - } - return false - } - - // use a buffered writer to parallelize task - bufw := bufio.NewWriterSize(pipew, DefaultBufSize) - - // compression determines whether to use gzip compression. - maybeGzw, err := newMaybeGzWriter(bufw, compression) - if checkErrAndClosePipe(err) { - return nil, err - } - - closeGzwAndPipe := func() { - if err := maybeGzw.Close(); checkErrAndClosePipe(err) { - return - } - if err := bufw.Flush(); checkErrAndClosePipe(err) { - return - } - pipew.Close() // everything seems to be ok. - } - - if !archive && compression != gzip.NoCompression { - // the case when the node is a file - r := files.ToFile(f) - if r == nil { - return nil, errors.New("file is not regular") - } - - go func() { - if _, err := io.Copy(maybeGzw, r); checkErrAndClosePipe(err) { - return - } - closeGzwAndPipe() // everything seems to be ok - }() - } else { - // the case for 1. archive, and 2. not archived and not compressed, in which tar is used anyway as a transport format - - // construct the tar writer - w, err := files.NewTarWriter(maybeGzw) - if checkErrAndClosePipe(err) { - return nil, err - } - - go func() { - // write all the nodes recursively - if err := w.WriteFile(f, filename); checkErrAndClosePipe(err) { - return - } - w.Close() // close tar writer - closeGzwAndPipe() // everything seems to be ok - }() - } - - return piper, nil -} - -func newMaybeGzWriter(w io.Writer, compression int) (io.WriteCloser, error) { - if compression != gzip.NoCompression { - return gzip.NewWriterLevel(w, compression) - } - return &identityWriteCloser{w}, nil -} - From 24673b7fcdcbdbdf7191ded6851f8cc02ccffefe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 30 Jan 2019 19:09:17 +0100 Subject: [PATCH 2897/3817] unixfile: precalc dir size This commit was moved from ipfs/go-unixfs@89d0dca4d95e102e6f9e7cb12e5c6889eed2c460 --- unixfs/file/unixfile.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/unixfs/file/unixfile.go b/unixfs/file/unixfile.go index 6617591fc..df3ce9e89 100644 --- a/unixfs/file/unixfile.go +++ b/unixfs/file/unixfile.go @@ -20,6 +20,7 @@ type ufsDirectory struct { ctx context.Context dserv ipld.DAGService dir uio.Directory + size int64 } type ufsIterator struct { @@ -118,12 +119,7 @@ func (d *ufsDirectory) Entries() files.DirIterator { } func (d *ufsDirectory) Size() (int64, error) { - n, err := d.dir.GetNode() - if err != nil { - return 0, err - } - s, err := n.Size() - return int64(s), err + return d.size, nil } type ufsFile struct { @@ -134,17 +130,23 @@ func (f *ufsFile) Size() (int64, error) { return int64(f.DagReader.Size()), nil } -func newUnixfsDir(ctx context.Context, dserv ipld.DAGService, nd ipld.Node) (files.Directory, error) { +func newUnixfsDir(ctx context.Context, dserv ipld.DAGService, nd *dag.ProtoNode) (files.Directory, error) { dir, err := uio.NewDirectoryFromNode(dserv, nd) if err != nil { return nil, err } + size, err := nd.Size() + if err != nil { + return nil, err + } + return &ufsDirectory{ ctx: ctx, dserv: dserv, - dir: dir, + dir: dir, + size: int64(size), }, nil } @@ -156,7 +158,7 @@ func NewUnixfsFile(ctx context.Context, dserv ipld.DAGService, nd ipld.Node) (fi return nil, err } if fsn.IsDir() { - return newUnixfsDir(ctx, dserv, nd) + return newUnixfsDir(ctx, dserv, dn) } if fsn.Type() == ft.TSymlink { return files.NewLinkFile(string(fsn.Data()), nil), nil From c6503a1ba9fa4f111fd0ca2975a9f7285d5d4768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 30 Jan 2019 17:42:14 +0100 Subject: [PATCH 2898/3817] gx: update go-unixfs to propagate archive changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@fbde8e2781a69d29530a3a55c8007f135d65e25e --- coreiface/tests/name.go | 2 +- coreiface/tests/unixfs.go | 6 +++--- coreiface/unixfs.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index 7b0a5d8f0..8690f22c3 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -9,7 +9,7 @@ import ( "time" ipath "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" - "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files" + "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 6f10406eb..2f1ab90a4 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -15,11 +15,11 @@ import ( coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs" + "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs/importer/helpers" "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" cbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" - "gx/ipfs/QmSMJ4rZbCJaih3y82Ebq7BZqK6vU2FHsKcWKQiE1DPTpS/go-unixfs" - "gx/ipfs/QmSMJ4rZbCJaih3y82Ebq7BZqK6vU2FHsKcWKQiE1DPTpS/go-unixfs/importer/helpers" - "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files" + "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" mdag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 3c2788196..408280cbc 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" - files "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files" + files "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" ) type AddEvent struct { From 109cd040aa9efeb7d55cadc80bb4faed604ae349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 30 Jan 2019 17:42:14 +0100 Subject: [PATCH 2899/3817] gx: update go-unixfs to propagate archive changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@1b992b836ecabea7c8c3f86ff72e0fe9f4e70ac6 --- namesys/namesys_test.go | 2 +- namesys/publisher.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 8f6da5d71..a73785b59 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -9,7 +9,7 @@ import ( ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" pstoremem "gx/ipfs/QmPiemjiKBC9VA7vZF82m4x1oygtg2c2YVqag8PX7dN1BD/go-libp2p-peerstore/pstoremem" - "gx/ipfs/QmSMJ4rZbCJaih3y82Ebq7BZqK6vU2FHsKcWKQiE1DPTpS/go-unixfs" + "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs" offroute "gx/ipfs/QmVZ6cQXHoTQja4oo9GhhHZi7dThi4x98mRKgGtKnTy37u/go-ipfs-routing/offline" ipns "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns" path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" diff --git a/namesys/publisher.go b/namesys/publisher.go index ef536a7e9..49cc93b47 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,7 +7,7 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmSMJ4rZbCJaih3y82Ebq7BZqK6vU2FHsKcWKQiE1DPTpS/go-unixfs" + ft "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs" path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" From ac20c6ad8101683bb28e0a57955bcefc454a959f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 21 Jan 2019 12:30:34 +0100 Subject: [PATCH 2900/3817] coreapi: add some seeker tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@be8e8d1aebcd3b544b0b9c345338ed9c55bbfe1c --- coreiface/tests/unixfs.go | 105 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 2f1ab90a4..5ae273987 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -3,9 +3,11 @@ package tests import ( "bytes" "context" + "fmt" "io" "io/ioutil" "math" + "math/rand" "os" "strconv" "strings" @@ -43,6 +45,7 @@ func (tp *provider) TestUnixfs(t *testing.T) { t.Run("TestLsEmptyDir", tp.TestLsEmptyDir) t.Run("TestLsNonUnixfs", tp.TestLsNonUnixfs) t.Run("TestAddCloses", tp.TestAddCloses) + t.Run("TestGetSeek", tp.TestGetSeek) } // `echo -n 'hello, world!' | ipfs add` @@ -934,5 +937,107 @@ func (tp *provider) TestAddCloses(t *testing.T) { t.Errorf("dir %d not closed!", i) } } +} + +func (tp *provider) TestGetSeek(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(ctx) + if err != nil { + t.Error(err) + } + + dataSize := int64(100000) + tf := files.NewReaderFile(io.LimitReader(rand.New(rand.NewSource(1403768328)), dataSize)) + + p, err := api.Unixfs().Add(ctx, tf, options.Unixfs.Chunker("size-100")) + if err != nil { + t.Fatal(err) + } + + r, err := api.Unixfs().Get(ctx, p) + if err != nil { + t.Fatal(err) + } + + f := files.ToFile(r) + if f == nil { + t.Fatal("not a file") + } + + orig := make([]byte, dataSize) + if _, err := f.Read(orig); err != nil { + t.Fatal(err) + } + f.Close() + + origR := bytes.NewReader(orig) + + r, err = api.Unixfs().Get(ctx, p) + if err != nil { + t.Fatal(err) + } + + f = files.ToFile(r) + if f == nil { + t.Fatal("not a file") + } + + test := func(offset int64, whence int, read int, expect int64, shouldEof bool) { + t.Run(fmt.Sprintf("seek%d+%d-r%d-%d", whence, offset, read, expect), func(t *testing.T) { + n, err := f.Seek(offset, whence) + if err != nil { + t.Fatal(err) + } + origN, err := origR.Seek(offset, whence) + if err != nil { + t.Fatal(err) + } + + if n != origN { + t.Fatalf("offsets didn't match, expected %d, got %d", origN, n) + } + + buf := make([]byte, read) + origBuf := make([]byte, read) + origRead, err := origR.Read(origBuf) + if err != nil { + t.Fatalf("orig: %s", err) + } + r, err := f.Read(buf) + switch { + case shouldEof && err != nil && err != io.EOF: + fallthrough + case !shouldEof && err != nil: + t.Fatalf("f: %s", err) + case shouldEof: + _, err := f.Read([]byte{0}) + if err != io.EOF { + t.Fatal("expected EOF") + } + _, err = origR.Read([]byte{0}) + if err != io.EOF { + t.Fatal("expected EOF (orig)") + } + } + + if int64(r) != expect { + t.Fatal("read wrong amount of data") + } + if r != origRead { + t.Fatal("read different amount of data than bytes.Reader") + } + if !bytes.Equal(buf, origBuf) { + t.Fatal("data didn't match") + } + }) + } + test(3, io.SeekCurrent, 10, 10, false) + test(3, io.SeekCurrent, 10, 10, false) + test(500, io.SeekCurrent, 10, 10, false) + test(350, io.SeekStart, 100, 100, false) + test(-123, io.SeekCurrent, 100, 100, false) + test(dataSize-50, io.SeekStart, 100, 50, true) + test(-5, io.SeekEnd, 100, 5, true) } From 3afaf889d4d49000482655a90591acdd3eb16349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 1 Feb 2019 19:48:43 +0100 Subject: [PATCH 2901/3817] coreapi: use chan for returning results in Unixfs.Ls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@93175e9900f58425d3868381c3a8668500ac39a9 --- coreiface/tests/unixfs.go | 18 ++++++++++-------- coreiface/unixfs.go | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 5ae273987..68d408e6c 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -754,18 +754,20 @@ func (tp *provider) TestLs(t *testing.T) { t.Error(err) } - if len(links) != 1 { - t.Fatalf("expected 1 link, got %d", len(links)) + link := <- links + if link.Size != 23 { + t.Fatalf("expected size = 23, got %d", link.Size) } - if links[0].Size != 23 { - t.Fatalf("expected size = 23, got %d", links[0].Size) + if link.Name != "name-of-file" { + t.Fatalf("expected name = name-of-file, got %s", link.Name) } - if links[0].Name != "name-of-file" { - t.Fatalf("expected name = name-of-file, got %s", links[0].Name) + if link.Cid.String() != "QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr" { + t.Fatalf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", link.Cid) } - if links[0].Cid.String() != "QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr" { - t.Fatalf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", links[0].Cid) + if _, ok := <-links; ok { + t.Errorf("didn't expect a second link") } + } func (tp *provider) TestEntriesExpired(t *testing.T) { diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 408280cbc..cdb6a1e0c 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -31,5 +31,5 @@ type UnixfsAPI interface { Get(context.Context, Path) (files.Node, error) // Ls returns the list of links in a directory - Ls(context.Context, Path) ([]*ipld.Link, error) + Ls(context.Context, Path) (<-chan *ipld.Link, error) } From db66d03977366d25aa6b91f30a6ec7a9fcae9037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 1 Feb 2019 20:12:48 +0100 Subject: [PATCH 2902/3817] coreapi: asunc ls option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@72006bfe2d78cd6cb507ff0265cd1844521d190e --- coreiface/options/unixfs.go | 30 ++++++++++++++++++++++++++++++ coreiface/tests/unixfs.go | 22 ++++++++++++++++++++-- coreiface/unixfs.go | 6 +++--- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 109a63f1d..819cc3b6b 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -42,7 +42,12 @@ type UnixfsAddSettings struct { Progress bool } +type UnixfsLsSettings struct { + Async bool +} + type UnixfsAddOption func(*UnixfsAddSettings) error +type UnixfsLsOption func(*UnixfsLsSettings) error func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, error) { options := &UnixfsAddSettings{ @@ -122,6 +127,21 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, return options, prefix, nil } +func UnixfsLsOptions(opts ...UnixfsLsOption) (*UnixfsLsSettings, error) { + options := &UnixfsLsSettings{ + Async: true, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + type unixfsOpts struct{} var Unixfs unixfsOpts @@ -290,3 +310,13 @@ func (unixfsOpts) Nocopy(enable bool) UnixfsAddOption { return nil } } + +// Async tells ls to return results as soon as they are available, which can be +// useful for listing HAMT directories. When this option is set to true returned +// results won't be returned in order +func (unixfsOpts) Async(async bool) UnixfsLsOption { + return func(settings *UnixfsLsSettings) error { + settings.Async = async + return nil + } +} diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 68d408e6c..b2b5a9ebb 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -749,12 +749,12 @@ func (tp *provider) TestLs(t *testing.T) { t.Error(err) } - links, err := api.Unixfs().Ls(ctx, p) + links, err := api.Unixfs().Ls(ctx, p, options.Unixfs.Async(false)) if err != nil { t.Error(err) } - link := <- links + link := (<-links).Link if link.Size != 23 { t.Fatalf("expected size = 23, got %d", link.Size) } @@ -768,6 +768,24 @@ func (tp *provider) TestLs(t *testing.T) { t.Errorf("didn't expect a second link") } + links, err = api.Unixfs().Ls(ctx, p, options.Unixfs.Async(true)) + if err != nil { + t.Error(err) + } + + link = (<-links).Link + if link.Size != 23 { + t.Fatalf("expected size = 23, got %d", link.Size) + } + if link.Name != "name-of-file" { + t.Fatalf("expected name = name-of-file, got %s", link.Name) + } + if link.Cid.String() != "QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr" { + t.Fatalf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", link.Cid) + } + if _, ok := <-links; ok { + t.Errorf("didn't expect a second link") + } } func (tp *provider) TestEntriesExpired(t *testing.T) { diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index cdb6a1e0c..ba2673fee 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -5,8 +5,8 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" - files "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" + ft "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs" + "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" ) type AddEvent struct { @@ -31,5 +31,5 @@ type UnixfsAPI interface { Get(context.Context, Path) (files.Node, error) // Ls returns the list of links in a directory - Ls(context.Context, Path) (<-chan *ipld.Link, error) + Ls(context.Context, Path, ...options.UnixfsLsOption) (<-chan ft.LinkResult, error) } From 54f7855257d69f5f79fe37d7896ddff4e0d1c2f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 1 Feb 2019 23:07:19 +0100 Subject: [PATCH 2903/3817] coreapi: resolve type/size in Unixfs.Ls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@01bbf29cf470532d94aba2bc5a912eb44d9997d0 --- coreiface/options/unixfs.go | 17 +++++++++++++++++ coreiface/unixfs.go | 14 +++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 819cc3b6b..6dbab93b6 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -44,6 +44,9 @@ type UnixfsAddSettings struct { type UnixfsLsSettings struct { Async bool + + ResolveType bool + ResolveSize bool } type UnixfsAddOption func(*UnixfsAddSettings) error @@ -320,3 +323,17 @@ func (unixfsOpts) Async(async bool) UnixfsLsOption { return nil } } + +func (unixfsOpts) ResolveSize(resolve bool) UnixfsLsOption { + return func(settings *UnixfsLsSettings) error { + settings.ResolveSize = resolve + return nil + } +} + +func (unixfsOpts) ResolveType(resolve bool) UnixfsLsOption { + return func(settings *UnixfsLsSettings) error { + settings.ResolveSize = resolve + return nil + } +} \ No newline at end of file diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index ba2673fee..846b74629 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -2,10 +2,10 @@ package iface import ( "context" - "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - ft "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs" + "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs/pb" + ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" ) @@ -16,6 +16,14 @@ type AddEvent struct { Size string `json:",omitempty"` } +type LsLink struct { + Link *ipld.Link + Size uint64 + Type unixfs_pb.Data_DataType + + Err error +} + // UnixfsAPI is the basic interface to immutable files in IPFS // NOTE: This API is heavily WIP, things are guaranteed to break frequently type UnixfsAPI interface { @@ -31,5 +39,5 @@ type UnixfsAPI interface { Get(context.Context, Path) (files.Node, error) // Ls returns the list of links in a directory - Ls(context.Context, Path, ...options.UnixfsLsOption) (<-chan ft.LinkResult, error) + Ls(context.Context, Path, ...options.UnixfsLsOption) (<-chan LsLink, error) } From 966d0008c10b29c04075261e3c59b0cab8953faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 2 Feb 2019 00:18:44 +0100 Subject: [PATCH 2904/3817] ls: use CoreAPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@fad4bd392abb9eb689687497d89d9e51e56486fb --- coreiface/options/unixfs.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 6dbab93b6..4ff5cdb3f 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -133,6 +133,9 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, func UnixfsLsOptions(opts ...UnixfsLsOption) (*UnixfsLsSettings, error) { options := &UnixfsLsSettings{ Async: true, + + ResolveSize: true, + ResolveType: true, } for _, opt := range opts { @@ -333,7 +336,7 @@ func (unixfsOpts) ResolveSize(resolve bool) UnixfsLsOption { func (unixfsOpts) ResolveType(resolve bool) UnixfsLsOption { return func(settings *UnixfsLsSettings) error { - settings.ResolveSize = resolve + settings.ResolveType = resolve return nil } -} \ No newline at end of file +} From 328e6f8ac9b4229e990201a93954213be992c45d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 2 Feb 2019 03:42:00 +0100 Subject: [PATCH 2905/3817] coreapi: stream only ls, handle storting in command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@a62acc82d4b5f8135f1d1249a22e91572a9a03c0 --- coreiface/options/unixfs.go | 14 -------------- coreiface/tests/unixfs.go | 21 +-------------------- coreiface/unixfs.go | 3 ++- 3 files changed, 3 insertions(+), 35 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 4ff5cdb3f..7e77410bc 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -43,8 +43,6 @@ type UnixfsAddSettings struct { } type UnixfsLsSettings struct { - Async bool - ResolveType bool ResolveSize bool } @@ -132,8 +130,6 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, func UnixfsLsOptions(opts ...UnixfsLsOption) (*UnixfsLsSettings, error) { options := &UnixfsLsSettings{ - Async: true, - ResolveSize: true, ResolveType: true, } @@ -317,16 +313,6 @@ func (unixfsOpts) Nocopy(enable bool) UnixfsAddOption { } } -// Async tells ls to return results as soon as they are available, which can be -// useful for listing HAMT directories. When this option is set to true returned -// results won't be returned in order -func (unixfsOpts) Async(async bool) UnixfsLsOption { - return func(settings *UnixfsLsSettings) error { - settings.Async = async - return nil - } -} - func (unixfsOpts) ResolveSize(resolve bool) UnixfsLsOption { return func(settings *UnixfsLsSettings) error { settings.ResolveSize = resolve diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index b2b5a9ebb..054461de1 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -749,7 +749,7 @@ func (tp *provider) TestLs(t *testing.T) { t.Error(err) } - links, err := api.Unixfs().Ls(ctx, p, options.Unixfs.Async(false)) + links, err := api.Unixfs().Ls(ctx, p) if err != nil { t.Error(err) } @@ -767,25 +767,6 @@ func (tp *provider) TestLs(t *testing.T) { if _, ok := <-links; ok { t.Errorf("didn't expect a second link") } - - links, err = api.Unixfs().Ls(ctx, p, options.Unixfs.Async(true)) - if err != nil { - t.Error(err) - } - - link = (<-links).Link - if link.Size != 23 { - t.Fatalf("expected size = 23, got %d", link.Size) - } - if link.Name != "name-of-file" { - t.Fatalf("expected name = name-of-file, got %s", link.Name) - } - if link.Cid.String() != "QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr" { - t.Fatalf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", link.Cid) - } - if _, ok := <-links; ok { - t.Errorf("didn't expect a second link") - } } func (tp *provider) TestEntriesExpired(t *testing.T) { diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 846b74629..a77011988 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -38,6 +38,7 @@ type UnixfsAPI interface { // to operations performed on the returned file Get(context.Context, Path) (files.Node, error) - // Ls returns the list of links in a directory + // Ls returns the list of links in a directory. Links aren't guaranteed to be + // returned in order Ls(context.Context, Path, ...options.UnixfsLsOption) (<-chan LsLink, error) } From 793d38f83e3036e51781126e23040e397f1ac8d8 Mon Sep 17 00:00:00 2001 From: chenminjian <727180553@qq.com> Date: Sat, 2 Feb 2019 11:47:02 +0800 Subject: [PATCH 2906/3817] fix(mv): dst path error This commit was moved from ipfs/go-mfs@1836bf0d8cdd7b165a78e98b27699e718982d8e2 --- mfs/ops.go | 1 + 1 file changed, 1 insertion(+) diff --git a/mfs/ops.go b/mfs/ops.go index 031a77d46..d989bb5f0 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -60,6 +60,7 @@ func Mv(r *Root, src, dst string) error { _ = dstDir.Unlink(filename) case *Directory: dstDir = n + filename = srcFname default: return fmt.Errorf("unexpected type at path: %s", dst) } From 8d88635d4a9ae6462528d7a6cd7737ecbd5716de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 2 Feb 2019 17:13:28 +0100 Subject: [PATCH 2907/3817] coreapi ls: merge ResolveType and ResolveSize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@d93b9f110ec9df2fb0e4f840974243ae878ffdf6 --- coreiface/options/unixfs.go | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 7e77410bc..015c2dca3 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -43,8 +43,7 @@ type UnixfsAddSettings struct { } type UnixfsLsSettings struct { - ResolveType bool - ResolveSize bool + ResolveChildren bool } type UnixfsAddOption func(*UnixfsAddSettings) error @@ -130,8 +129,7 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, func UnixfsLsOptions(opts ...UnixfsLsOption) (*UnixfsLsSettings, error) { options := &UnixfsLsSettings{ - ResolveSize: true, - ResolveType: true, + ResolveChildren: true, } for _, opt := range opts { @@ -313,16 +311,9 @@ func (unixfsOpts) Nocopy(enable bool) UnixfsAddOption { } } -func (unixfsOpts) ResolveSize(resolve bool) UnixfsLsOption { +func (unixfsOpts) ResolveChildren(resolve bool) UnixfsLsOption { return func(settings *UnixfsLsSettings) error { - settings.ResolveSize = resolve - return nil - } -} - -func (unixfsOpts) ResolveType(resolve bool) UnixfsLsOption { - return func(settings *UnixfsLsSettings) error { - settings.ResolveType = resolve + settings.ResolveChildren = resolve return nil } } From e47af31e2d3e5b497f91392603ed5f6b760f6eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 2 Feb 2019 17:27:54 +0100 Subject: [PATCH 2908/3817] coreapi: mirror unixfs file types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@e8196db410d2fc38fb64613bebedccc79c1ecaec --- coreiface/unixfs.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index a77011988..1fb07638f 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -4,7 +4,7 @@ import ( "context" "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs/pb" + "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" ) @@ -16,10 +16,21 @@ type AddEvent struct { Size string `json:",omitempty"` } +type FileType int32 + +const ( + TRaw = FileType(unixfs.TRaw) + TFile = FileType(unixfs.TFile) + TDirectory = FileType(unixfs.TDirectory) + TMetadata = FileType(unixfs.TMetadata) + TSymlink = FileType(unixfs.TSymlink) + THAMTShard = FileType(unixfs.THAMTShard) +) + type LsLink struct { Link *ipld.Link Size uint64 - Type unixfs_pb.Data_DataType + Type FileType Err error } From 9ea89f38600dab60d9a09740c28d935ef9bfc202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 4 Feb 2019 18:05:05 +0100 Subject: [PATCH 2909/3817] block put --pin option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@005752045c872e4dabb17e5c9ba1732f3cf04ea6 --- coreiface/options/block.go | 11 +++++++++++ coreiface/tests/block.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/coreiface/options/block.go b/coreiface/options/block.go index ea4ae26bb..40dfba79a 100644 --- a/coreiface/options/block.go +++ b/coreiface/options/block.go @@ -10,6 +10,7 @@ type BlockPutSettings struct { Codec string MhType uint64 MhLength int + Pin bool } type BlockRmSettings struct { @@ -24,6 +25,7 @@ func BlockPutOptions(opts ...BlockPutOption) (*BlockPutSettings, cid.Prefix, err Codec: "", MhType: mh.SHA2_256, MhLength: -1, + Pin: false, } for _, opt := range opts { @@ -105,6 +107,15 @@ func (blockOpts) Hash(mhType uint64, mhLen int) BlockPutOption { } } +// Pin is an option for Block.Put which specifies whether to (recursively) pin +// added blocks +func (blockOpts) Pin(pin bool) BlockPutOption { + return func(settings *BlockPutSettings) error { + settings.Pin = pin + return nil + } +} + // Force is an option for Block.Rm which, when set to true, will ignore // non-existing blocks func (blockOpts) Force(force bool) BlockRmOption { diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 427ad3357..c2ee70a3a 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -26,6 +26,7 @@ func (tp *provider) TestBlock(t *testing.T) { t.Run("TestBlockGet", tp.TestBlockGet) t.Run("TestBlockRm", tp.TestBlockRm) t.Run("TestBlockStat", tp.TestBlockStat) + t.Run("TestBlockPin", tp.TestBlockPin) } func (tp *provider) TestBlockPut(t *testing.T) { @@ -203,3 +204,40 @@ func (tp *provider) TestBlockStat(t *testing.T) { t.Error("length doesn't match") } } + +func (tp *provider) TestBlockPin(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Block().Put(ctx, strings.NewReader(`Hello`)) + if err != nil { + t.Fatal(err) + } + + if pins, err := api.Pin().Ls(ctx); err != nil || len(pins) != 0 { + t.Fatal("expected 0 pins") + } + + res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Pin(true)) + if err != nil { + t.Fatal(err) + } + + pins, err := api.Pin().Ls(ctx) + if err != nil { + return + } + if len(pins) != 1 { + t.Fatal("expected 1 pin") + } + if pins[0].Type() != "recursive" { + t.Error("expected a recursive pin") + } + if pins[0].Path().String() != res.Path().String() { + t.Error("pin path didn't match") + } +} From 4a549b9da6016451497081912b388caa71b865b4 Mon Sep 17 00:00:00 2001 From: roignpar <47150492+roignpar@users.noreply.github.com> Date: Mon, 4 Feb 2019 22:38:29 +0200 Subject: [PATCH 2910/3817] fix community/CONTRIBUTING.md link in README.md This commit was moved from ipfs/go-ipns@b9006c464449dacdcf8a29bdf386797b38b26906 --- ipns/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipns/README.md b/ipns/README.md index efd897a25..1fbb36d1f 100644 --- a/ipns/README.md +++ b/ipns/README.md @@ -54,7 +54,7 @@ This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/c ### Want to hack on IPFS? -[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/contributing.md) +[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) ## License From 301ad37db0af794d974c8583b2d4ee24b7dbc596 Mon Sep 17 00:00:00 2001 From: roignpar <47150492+roignpar@users.noreply.github.com> Date: Mon, 4 Feb 2019 22:48:13 +0200 Subject: [PATCH 2911/3817] fix typo in README.md This commit was moved from ipfs/go-ipns@a35ea72a32cf387b2429f28555606d445894039a --- ipns/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipns/README.md b/ipns/README.md index efd897a25..72b9e6f9f 100644 --- a/ipns/README.md +++ b/ipns/README.md @@ -8,7 +8,7 @@ > ipns record definitions -This package contains all of components necessary to create, understand, and validate IPNS records. It does *not* publish or resolve those records. [`go-ipfs`](https://github.com/ipfs/go-ipfs) uses this package internally to manipulate records. +This package contains all of the components necessary to create, understand, and validate IPNS records. It does *not* publish or resolve those records. [`go-ipfs`](https://github.com/ipfs/go-ipfs) uses this package internally to manipulate records. ## Usage From 1e59d281dfae25d81b2f2dba9ca6138537c431a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 5 Feb 2019 20:20:27 +0100 Subject: [PATCH 2912/3817] coreapi: fix seek test on http impl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@3291f565631f8ccbb1d09bb71e686265cc00803d --- coreiface/tests/unixfs.go | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 054461de1..1c21f4fd0 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -3,6 +3,7 @@ package tests import ( "bytes" "context" + "encoding/hex" "fmt" "io" "io/ioutil" @@ -754,9 +755,13 @@ func (tp *provider) TestLs(t *testing.T) { t.Error(err) } - link := (<-links).Link - if link.Size != 23 { - t.Fatalf("expected size = 23, got %d", link.Size) + linkRes := <-links + if linkRes.Err != nil { + t.Fatal(linkRes.Err) + } + link := linkRes.Link + if linkRes.Size != 15 { + t.Fatalf("expected size = 15, got %d", link.Size) } if link.Name != "name-of-file" { t.Fatalf("expected name = name-of-file, got %s", link.Name) @@ -764,8 +769,11 @@ func (tp *provider) TestLs(t *testing.T) { if link.Cid.String() != "QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr" { t.Fatalf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", link.Cid) } - if _, ok := <-links; ok { + if l, ok := <-links; ok { t.Errorf("didn't expect a second link") + if l.Err != nil { + t.Error(l.Err) + } } } @@ -967,7 +975,7 @@ func (tp *provider) TestGetSeek(t *testing.T) { } orig := make([]byte, dataSize) - if _, err := f.Read(orig); err != nil { + if _, err := io.ReadFull(f, orig); err != nil { t.Fatal(err) } f.Close() @@ -1005,9 +1013,9 @@ func (tp *provider) TestGetSeek(t *testing.T) { if err != nil { t.Fatalf("orig: %s", err) } - r, err := f.Read(buf) + r, err := io.ReadFull(f, buf) switch { - case shouldEof && err != nil && err != io.EOF: + case shouldEof && err != nil && err != io.ErrUnexpectedEOF: fallthrough case !shouldEof && err != nil: t.Fatalf("f: %s", err) @@ -1029,6 +1037,8 @@ func (tp *provider) TestGetSeek(t *testing.T) { t.Fatal("read different amount of data than bytes.Reader") } if !bytes.Equal(buf, origBuf) { + fmt.Fprintf(os.Stderr, "original:\n%s\n", hex.Dump(origBuf)) + fmt.Fprintf(os.Stderr, "got:\n%s\n", hex.Dump(buf)) t.Fatal("data didn't match") } }) @@ -1039,6 +1049,7 @@ func (tp *provider) TestGetSeek(t *testing.T) { test(500, io.SeekCurrent, 10, 10, false) test(350, io.SeekStart, 100, 100, false) test(-123, io.SeekCurrent, 100, 100, false) + test(0, io.SeekStart, int(dataSize), dataSize, false) test(dataSize-50, io.SeekStart, 100, 50, true) test(-5, io.SeekEnd, 100, 5, true) } From 912c1a08af7de98f6350eb595caa62894c50af5e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 7 Feb 2019 16:54:18 -0800 Subject: [PATCH 2913/3817] chore: go fmt This commit was moved from ipfs/go-path@05fabccd1fea9d47be7ad3b47c914ca667288815 --- path/resolver/resolver_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/path/resolver/resolver_test.go b/path/resolver/resolver_test.go index cec160fe7..480ccdf1d 100644 --- a/path/resolver/resolver_test.go +++ b/path/resolver/resolver_test.go @@ -95,7 +95,6 @@ func TestRecurivePathResolution(t *testing.T) { t.Fatal(err) } - if len(rest) != 0 { t.Error("expected rest to be empty") } From 266c4e727b378a07cbabc9cf7db1ff13fb997998 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 7 Feb 2019 17:11:29 -0800 Subject: [PATCH 2914/3817] gx: update go-libp2p-peer Switch _back_ to the 0.4.18 style of peer IDs while we figure things out. See https://github.com/libp2p/specs/issues/138. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@6913730770e58c55b6bdb99c62205e11ee0f6fe2 --- namesys/base.go | 2 +- namesys/cache.go | 2 +- namesys/dns.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 22 +++++++++++----------- namesys/namesys.go | 6 +++--- namesys/namesys_test.go | 12 ++++++------ namesys/proquint.go | 2 +- namesys/publisher.go | 12 ++++++------ namesys/publisher_test.go | 8 ++++---- namesys/republisher/repub.go | 6 +++--- namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 10 +++++----- namesys/routing.go | 12 ++++++------ 14 files changed, 52 insertions(+), 52 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index bc6d4dd2c..de71ff345 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -7,7 +7,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 8f41c64d3..44ecaab4d 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index f0cd63c70..6eb08ed80 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -8,7 +8,7 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index 0b047eec9..88c48efd8 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,7 +36,7 @@ import ( context "context" opts "github.com/ipfs/go-ipfs/namesys/opts" - path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 47c0bcd04..0a9e31634 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,24 +5,24 @@ import ( "testing" "time" - path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" - testutil "gx/ipfs/QmNvHv84aH2qZafDuSdKJCQ1cvPZ1kmQmyD4YtzjUHuk9v/go-testutil" - pstore "gx/ipfs/QmPiemjiKBC9VA7vZF82m4x1oygtg2c2YVqag8PX7dN1BD/go-libp2p-peerstore" - pstoremem "gx/ipfs/QmPiemjiKBC9VA7vZF82m4x1oygtg2c2YVqag8PX7dN1BD/go-libp2p-peerstore/pstoremem" - routing "gx/ipfs/QmTiRqrF5zkdZyrdsL5qndG1UbeWi8k8N2pYxCtXWrahR2/go-libp2p-routing" - ropts "gx/ipfs/QmTiRqrF5zkdZyrdsL5qndG1UbeWi8k8N2pYxCtXWrahR2/go-libp2p-routing/options" - mockrouting "gx/ipfs/QmVZ6cQXHoTQja4oo9GhhHZi7dThi4x98mRKgGtKnTy37u/go-ipfs-routing/mock" - offline "gx/ipfs/QmVZ6cQXHoTQja4oo9GhhHZi7dThi4x98mRKgGtKnTy37u/go-ipfs-routing/offline" - ipns "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns" - peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" + pstore "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore" + pstoremem "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore/pstoremem" + mockrouting "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/mock" + offline "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/offline" + routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" + ropts "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing/options" + testutil "gx/ipfs/QmVnJMgafh5MBYiyqbvDtoCL8pcQvbEGD2k9o9GFpBWPzY/go-testutil" + ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" + record "gx/ipfs/QmexPd3srWxHC76gW2p5j5tQvwpPuCoW7b9vFhJ8BRPyh9/go-libp2p-record" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" - record "gx/ipfs/QmfARXVCzpwFXQdepAJZuqyNDgV9doEsMnVCo1ssmuSe1U/go-libp2p-record" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index ac3caa328..a16cf1388 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,14 +5,14 @@ import ( "strings" "time" - path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" + peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" - routing "gx/ipfs/QmTiRqrF5zkdZyrdsL5qndG1UbeWi8k8N2pYxCtXWrahR2/go-libp2p-routing" - peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index a73785b59..3b3c1e255 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -8,12 +8,12 @@ import ( opts "github.com/ipfs/go-ipfs/namesys/opts" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - pstoremem "gx/ipfs/QmPiemjiKBC9VA7vZF82m4x1oygtg2c2YVqag8PX7dN1BD/go-libp2p-peerstore/pstoremem" - "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs" - offroute "gx/ipfs/QmVZ6cQXHoTQja4oo9GhhHZi7dThi4x98mRKgGtKnTy37u/go-ipfs-routing/offline" - ipns "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns" - path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" - peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" + pstoremem "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore/pstoremem" + offroute "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/offline" + ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" + "gx/ipfs/QmZArMcsVDsXdcLbUx4844CuqKXBpbxdeiryM4cnmGTNRq/go-unixfs" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) diff --git a/namesys/proquint.go b/namesys/proquint.go index 703bbb3cc..75d56c80f 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,7 +4,7 @@ import ( "context" "errors" - path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" opts "github.com/ipfs/go-ipfs/namesys/opts" diff --git a/namesys/publisher.go b/namesys/publisher.go index 49cc93b47..d20cda5a0 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,14 +7,14 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - ft "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs" - path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" + ft "gx/ipfs/QmZArMcsVDsXdcLbUx4844CuqKXBpbxdeiryM4cnmGTNRq/go-unixfs" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - routing "gx/ipfs/QmTiRqrF5zkdZyrdsL5qndG1UbeWi8k8N2pYxCtXWrahR2/go-libp2p-routing" - ipns "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns" - pb "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns/pb" - peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" + routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" + ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" + pb "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns/pb" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dsquery "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 0d82cc0c2..7a8cf7ede 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -8,10 +8,10 @@ import ( ma "gx/ipfs/QmNTCey11oxhb1AxDnQBRHtdhap6Ctud872NjAYPYYXPuc/go-multiaddr" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - testutil "gx/ipfs/QmNvHv84aH2qZafDuSdKJCQ1cvPZ1kmQmyD4YtzjUHuk9v/go-testutil" - mockrouting "gx/ipfs/QmVZ6cQXHoTQja4oo9GhhHZi7dThi4x98mRKgGtKnTy37u/go-ipfs-routing/mock" - ipns "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns" - peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" + mockrouting "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/mock" + testutil "gx/ipfs/QmVnJMgafh5MBYiyqbvDtoCL8pcQvbEGD2k9o9GFpBWPzY/go-testutil" + ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" dshelp "gx/ipfs/QmauEMWPoSqggfpSDHMMXuDn12DTd7TaFBvn39eeurzKT2/go-ipfs-ds-help" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 429f41f8c..46d381a5d 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,13 +7,13 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" ic "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" + peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - pb "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns/pb" - peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + pb "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns/pb" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index a7530b1e9..738a3301d 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - pstore "gx/ipfs/QmPiemjiKBC9VA7vZF82m4x1oygtg2c2YVqag8PX7dN1BD/go-libp2p-peerstore" + pstore "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmYxivS34F2M2n44WQQnRHGAKS8aoRUxwGpi9wk4Cdn4Jf/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmSgtf5vHyugoxcwMbyNy6bZ9qPDDTJSYEED2GkWjLwitZ/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index fe0a81125..54cc4c8d6 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,12 +6,12 @@ import ( "testing" "time" - path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - testutil "gx/ipfs/QmNvHv84aH2qZafDuSdKJCQ1cvPZ1kmQmyD4YtzjUHuk9v/go-testutil" - mockrouting "gx/ipfs/QmVZ6cQXHoTQja4oo9GhhHZi7dThi4x98mRKgGtKnTy37u/go-ipfs-routing/mock" - ipns "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns" - peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" + mockrouting "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/mock" + testutil "gx/ipfs/QmVnJMgafh5MBYiyqbvDtoCL8pcQvbEGD2k9o9GFpBWPzY/go-testutil" + ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) diff --git a/namesys/routing.go b/namesys/routing.go index 9ac7c086a..bfe1520e7 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,18 +5,18 @@ import ( "strings" "time" - path "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" opts "github.com/ipfs/go-ipfs/namesys/opts" - dht "gx/ipfs/QmNoNExMdWrYSPZDiJJTVmxSh6uKLN26xYVzbLzBLedRcv/go-libp2p-kad-dht" + peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - routing "gx/ipfs/QmTiRqrF5zkdZyrdsL5qndG1UbeWi8k8N2pYxCtXWrahR2/go-libp2p-routing" - ipns "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns" - pb "gx/ipfs/QmWPFehHmySCdaGttQ48iwF7M6mBRrGE5GSPWKCuMWqJDR/go-ipns/pb" - peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" + ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" + pb "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns/pb" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" + dht "gx/ipfs/Qmeh1RJ3kvEXgmuEmbNLwZ9wVUDuaqE7BhhEngd8aXV8tf/go-libp2p-kad-dht" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) From 132387d33db2093e89e1b97dbe292ba780debf0f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 7 Feb 2019 17:11:29 -0800 Subject: [PATCH 2915/3817] gx: update go-libp2p-peer Switch _back_ to the 0.4.18 style of peer IDs while we figure things out. See https://github.com/libp2p/specs/issues/138. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/interface-go-ipfs-core@67fd754fced65b8d75a92217fe265af48822cef1 --- coreiface/dht.go | 4 ++-- coreiface/key.go | 2 +- coreiface/options/unixfs.go | 2 +- coreiface/path.go | 2 +- coreiface/pubsub.go | 2 +- coreiface/swarm.go | 6 +++--- coreiface/tests/name.go | 2 +- coreiface/tests/unixfs.go | 6 +++--- coreiface/unixfs.go | 2 +- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/coreiface/dht.go b/coreiface/dht.go index ec8bd92c3..94fb3779f 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -5,8 +5,8 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - pstore "gx/ipfs/QmPiemjiKBC9VA7vZF82m4x1oygtg2c2YVqag8PX7dN1BD/go-libp2p-peerstore" - peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" + pstore "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore" ) // DhtAPI specifies the interface to the DHT diff --git a/coreiface/key.go b/coreiface/key.go index f310c3cc2..69857e613 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -5,7 +5,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" ) // Key specifies the interface to Keys in KeyAPI Keystore diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 015c2dca3..0dd129609 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -5,7 +5,7 @@ import ( "fmt" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - dag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" + dag "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) diff --git a/coreiface/path.go b/coreiface/path.go index b96e0e775..d59a851b4 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,8 +1,8 @@ package iface import ( + ipfspath "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - ipfspath "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" ) //TODO: merge with ipfspath so we don't depend on it diff --git a/coreiface/pubsub.go b/coreiface/pubsub.go index 867c8adc4..933673826 100644 --- a/coreiface/pubsub.go +++ b/coreiface/pubsub.go @@ -6,7 +6,7 @@ import ( options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - peer "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" ) // PubSubSubscription is an active PubSub subscription diff --git a/coreiface/swarm.go b/coreiface/swarm.go index 83e207282..3af078f17 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -6,9 +6,9 @@ import ( "time" ma "gx/ipfs/QmNTCey11oxhb1AxDnQBRHtdhap6Ctud872NjAYPYYXPuc/go-multiaddr" - net "gx/ipfs/QmNgLg1NTw37iWbYPKcyK85YJ9Whs1MkPtJwhfqbNYAyKg/go-libp2p-net" - pstore "gx/ipfs/QmPiemjiKBC9VA7vZF82m4x1oygtg2c2YVqag8PX7dN1BD/go-libp2p-peerstore" - "gx/ipfs/QmY5Grm8pJdiSSVsYxx4uNRgweY72EmYwuSDbRnbFok3iY/go-libp2p-peer" + "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" + pstore "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore" + net "gx/ipfs/QmZ7cBWUXkyWTMN4qH6NGoyMVs7JugyFChBNP4ZUp5rJHH/go-libp2p-net" "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol" ) diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index 8690f22c3..8d87bd495 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -8,7 +8,7 @@ import ( "testing" "time" - ipath "gx/ipfs/QmWqh9oob7ZHQRwU5CdTqpnC8ip8BEkFNrwXRxeNo5Y7vA/go-path" + ipath "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 1c21f4fd0..f5ce85b78 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -18,12 +18,12 @@ import ( coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs" - "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs/importer/helpers" "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" cbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" + mdag "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" + "gx/ipfs/QmZArMcsVDsXdcLbUx4844CuqKXBpbxdeiryM4cnmGTNRq/go-unixfs" + "gx/ipfs/QmZArMcsVDsXdcLbUx4844CuqKXBpbxdeiryM4cnmGTNRq/go-unixfs/importer/helpers" "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" - mdag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 1fb07638f..8e559022c 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -4,8 +4,8 @@ import ( "context" "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - "gx/ipfs/QmQ1JnYpnzkaurjW1yxkQxC2w3K1PorNE1nv1vaP5Le7sq/go-unixfs" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" + "gx/ipfs/QmZArMcsVDsXdcLbUx4844CuqKXBpbxdeiryM4cnmGTNRq/go-unixfs" "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" ) From d1cddb3cdb1e0accb3567b3ec9323a98aeb57ac6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 7 Feb 2019 17:11:29 -0800 Subject: [PATCH 2916/3817] gx: update go-libp2p-peer Switch _back_ to the 0.4.18 style of peer IDs while we figure things out. See https://github.com/libp2p/specs/issues/138. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@3ecaee5b84ca4e8ebce7db4f1c36f82ccd34db26 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 5e09f5078..eebfb1f41 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmVKQHuzni68SWByzJgBUCwHvvr4TWiXfutNWWwpZpp4rE/go-blockservice" - dag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" + dag "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" + bserv "gx/ipfs/QmbgbNxC1PMyS2gbx7nf2jKNG7bZAfYJJebdK4ptBBWCz1/go-blockservice" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 8a1a18fe1..bac29feb7 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" + mdag "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 9660d7339..763643fea 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - bs "gx/ipfs/QmVKQHuzni68SWByzJgBUCwHvvr4TWiXfutNWWwpZpp4rE/go-blockservice" - mdag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" + mdag "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" + bs "gx/ipfs/QmbgbNxC1PMyS2gbx7nf2jKNG7bZAfYJJebdK4ptBBWCz1/go-blockservice" util "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index f7e457315..982f7d22a 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" + "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 52b55b9ff..b81f9aad2 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmVKQHuzni68SWByzJgBUCwHvvr4TWiXfutNWWwpZpp4rE/go-blockservice" - dag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" + dag "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" + bserv "gx/ipfs/QmbgbNxC1PMyS2gbx7nf2jKNG7bZAfYJJebdK4ptBBWCz1/go-blockservice" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" From 506f026f27bd7f23a314fcc4e098e45055630de1 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 7 Feb 2019 17:11:29 -0800 Subject: [PATCH 2917/3817] gx: update go-libp2p-peer Switch _back_ to the 0.4.18 style of peer IDs while we figure things out. See https://github.com/libp2p/specs/issues/138. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@daa03129ed43c436ab2299548b06f25dd17eff8b --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 9f8bdb26b..8205bd6cc 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/Qmb2UEG2TAeVrEJSjqsZF7Y2he7wRDkrdt6c3bECxwZf4k/go-merkledag" + dag "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" From b3a8575241b2bb5970ad56c80d49308b6cec5853 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 7 Feb 2019 17:45:09 -0800 Subject: [PATCH 2918/3817] namesys: fix ed25519 test for peer ID inlining License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@d725e654f7de83a4f41a52899fc59042332250df --- namesys/publisher_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 7a8cf7ede..0e9ef1d4e 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -108,5 +108,5 @@ func TestRSAPublisher(t *testing.T) { } func TestEd22519Publisher(t *testing.T) { - testNamekeyPublisher(t, ci.Ed25519, nil, true) + testNamekeyPublisher(t, ci.Ed25519, ds.ErrNotFound, false) } From d15daa504fd1884c0e2a9c57ae3ae42343ec482e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 8 Feb 2019 17:58:56 +0100 Subject: [PATCH 2919/3817] coreapi: cleanup coredag references in interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@c3fa814784e5a96d5baeb5722bc51da7750a09ce --- coreiface/tests/dag.go | 53 +++++++++++++++++++++-------------------- coreiface/tests/path.go | 32 +++++++++++++------------ coreiface/tests/pin.go | 22 +++++++++-------- 3 files changed, 56 insertions(+), 51 deletions(-) diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index 10fab125a..4decfebb4 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -8,8 +8,9 @@ import ( "testing" coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" - coredag "github.com/ipfs/go-ipfs/core/coredag" + ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" + ipldcbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) @@ -46,18 +47,18 @@ func (tp *provider) TestPut(t *testing.T) { t.Error(err) } - nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`"Hello"`), math.MaxUint64, -1) + nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), math.MaxUint64, -1) if err != nil { t.Error(err) } - err = api.Dag().Add(ctx, nds[0]) + err = api.Dag().Add(ctx, nd) if err != nil { t.Fatal(err) } - if nds[0].Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" { - t.Errorf("got wrong cid: %s", nds[0].Cid().String()) + if nd.Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" { + t.Errorf("got wrong cid: %s", nd.Cid().String()) } } @@ -69,18 +70,18 @@ func (tp *provider) TestPutWithHash(t *testing.T) { t.Error(err) } - nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`"Hello"`), mh.ID, -1) + nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), mh.ID, -1) if err != nil { t.Error(err) } - err = api.Dag().Add(ctx, nds[0]) + err = api.Dag().Add(ctx, nd) if err != nil { t.Fatal(err) } - if nds[0].Cid().String() != "z5hRLNd2sv4z1c" { - t.Errorf("got wrong cid: %s", nds[0].Cid().String()) + if nd.Cid().String() != "z5hRLNd2sv4z1c" { + t.Errorf("got wrong cid: %s", nd.Cid().String()) } } @@ -92,27 +93,27 @@ func (tp *provider) TestDagPath(t *testing.T) { t.Error(err) } - snds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`"foo"`), math.MaxUint64, -1) + snd, err := ipldcbor.FromJSON(strings.NewReader(`"foo"`), math.MaxUint64, -1) if err != nil { t.Error(err) } - err = api.Dag().Add(ctx, snds[0]) + err = api.Dag().Add(ctx, snd) if err != nil { t.Fatal(err) } - nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"lnk": {"/": "`+snds[0].Cid().String()+`"}}`), math.MaxUint64, -1) + nd, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+snd.Cid().String()+`"}}`), math.MaxUint64, -1) if err != nil { t.Error(err) } - err = api.Dag().Add(ctx, nds[0]) + err = api.Dag().Add(ctx, nd) if err != nil { t.Fatal(err) } - p, err := coreiface.ParsePath(path.Join(nds[0].Cid().String(), "lnk")) + p, err := coreiface.ParsePath(path.Join(nd.Cid().String(), "lnk")) if err != nil { t.Error(err) } @@ -122,13 +123,13 @@ func (tp *provider) TestDagPath(t *testing.T) { t.Error(err) } - nd, err := api.Dag().Get(ctx, rp.Cid()) + ndd, err := api.Dag().Get(ctx, rp.Cid()) if err != nil { t.Error(err) } - if nd.Cid().String() != snds[0].Cid().String() { - t.Errorf("got unexpected cid %s, expected %s", nd.Cid().String(), snds[0].Cid().String()) + if nd.Cid().String() != snd.Cid().String() { + t.Errorf("got unexpected cid %s, expected %s", ndd.Cid().String(), snd.Cid().String()) } } @@ -140,17 +141,17 @@ func (tp *provider) TestTree(t *testing.T) { t.Error(err) } - nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"a": 123, "b": "foo", "c": {"d": 321, "e": 111}}`), math.MaxUint64, -1) + nd, err := ipldcbor.FromJSON(strings.NewReader(`{"a": 123, "b": "foo", "c": {"d": 321, "e": 111}}`), math.MaxUint64, -1) if err != nil { t.Error(err) } - err = api.Dag().Add(ctx, nds[0]) + err = api.Dag().Add(ctx, nd) if err != nil { t.Fatal(err) } - res, err := api.Dag().Get(ctx, nds[0].Cid()) + res, err := api.Dag().Get(ctx, nd.Cid()) if err != nil { t.Error(err) } @@ -175,25 +176,25 @@ func (tp *provider) TestBatch(t *testing.T) { t.Error(err) } - nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`"Hello"`), math.MaxUint64, -1) + nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), math.MaxUint64, -1) if err != nil { t.Error(err) } - if nds[0].Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" { - t.Errorf("got wrong cid: %s", nds[0].Cid().String()) + if nd.Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" { + t.Errorf("got wrong cid: %s", nd.Cid().String()) } - _, err = api.Dag().Get(ctx, nds[0].Cid()) + _, err = api.Dag().Get(ctx, nd.Cid()) if err == nil || !strings.Contains(err.Error(), "not found") { t.Error(err) } - if err := api.Dag().AddMany(ctx, nds); err != nil { + if err := api.Dag().AddMany(ctx, []ipld.Node{nd}); err != nil { t.Error(err) } - _, err = api.Dag().Get(ctx, nds[0].Cid()) + _, err = api.Dag().Get(ctx, nd.Cid()) if err != nil { t.Error(err) } diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index e7df6f1fb..5594cf0da 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -8,7 +8,8 @@ import ( coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - "github.com/ipfs/go-ipfs/core/coredag" + + ipldcbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" ) func (tp *provider) TestPath(t *testing.T) { @@ -37,7 +38,8 @@ func (tp *provider) TestMutablePath(t *testing.T) { t.Error("expected /ipld path to be immutable") } - // get self /ipns path + // get self /ipns path ipldcbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" + if api.Key() == nil { t.Fatal(".Key not implemented") } @@ -64,16 +66,16 @@ func (tp *provider) TestPathRemainder(t *testing.T) { t.Fatal(".Dag not implemented") } - nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) + nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) if err != nil { t.Error(err) } - if err := api.Dag().AddMany(ctx, nds); err != nil { + if err := api.Dag().Add(ctx, nd); err != nil { t.Fatal(err) } - p1, err := coreiface.ParsePath(nds[0].String() + "/foo/bar") + p1, err := coreiface.ParsePath(nd.String() + "/foo/bar") if err != nil { t.Error(err) } @@ -100,16 +102,16 @@ func (tp *provider) TestEmptyPathRemainder(t *testing.T) { t.Fatal(".Dag not implemented") } - nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) + nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) if err != nil { t.Error(err) } - if err := api.Dag().AddMany(ctx, nds); err != nil { + if err := api.Dag().Add(ctx, nd); err != nil { t.Fatal(err) } - p1, err := coreiface.ParsePath(nds[0].Cid().String()) + p1, err := coreiface.ParsePath(nd.Cid().String()) if err != nil { t.Error(err) } @@ -136,16 +138,16 @@ func (tp *provider) TestInvalidPathRemainder(t *testing.T) { t.Fatal(".Dag not implemented") } - nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) + nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) if err != nil { t.Error(err) } - if err := api.Dag().AddMany(ctx, nds); err != nil { + if err := api.Dag().Add(ctx, nd); err != nil { t.Fatal(err) } - p1, err := coreiface.ParsePath("/ipld/" + nds[0].Cid().String() + "/bar/baz") + p1, err := coreiface.ParsePath("/ipld/" + nd.Cid().String() + "/bar/baz") if err != nil { t.Error(err) } @@ -177,16 +179,16 @@ func (tp *provider) TestPathRoot(t *testing.T) { t.Fatal(".Dag not implemented") } - nds, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"foo": {"/": "`+blk.Path().Cid().String()+`"}}`), math.MaxUint64, -1) + nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"/": "`+blk.Path().Cid().String()+`"}}`), math.MaxUint64, -1) if err != nil { t.Error(err) } - if err := api.Dag().AddMany(ctx, nds); err != nil { + if err := api.Dag().Add(ctx, nd); err != nil { t.Fatal(err) } - p1, err := coreiface.ParsePath("/ipld/" + nds[0].Cid().String() + "/foo") + p1, err := coreiface.ParsePath("/ipld/" + nd.Cid().String() + "/foo") if err != nil { t.Error(err) } @@ -196,7 +198,7 @@ func (tp *provider) TestPathRoot(t *testing.T) { t.Fatal(err) } - if rp.Root().String() != nds[0].Cid().String() { + if rp.Root().String() != nd.Cid().String() { t.Error("unexpected path root") } diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index 250799222..35c913618 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -8,7 +8,9 @@ import ( "github.com/ipfs/go-ipfs/core/coreapi/interface" opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" - "github.com/ipfs/go-ipfs/core/coredag" + + ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" + ipldcbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" ) func (tp *provider) TestPin(t *testing.T) { @@ -111,26 +113,26 @@ func (tp *provider) TestPinRecursive(t *testing.T) { t.Error(err) } - nd2, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"lnk": {"/": "`+p0.Cid().String()+`"}}`), math.MaxUint64, -1) + nd2, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+p0.Cid().String()+`"}}`), math.MaxUint64, -1) if err != nil { t.Error(err) } - nd3, err := coredag.ParseInputs("json", "dag-cbor", strings.NewReader(`{"lnk": {"/": "`+p1.Cid().String()+`"}}`), math.MaxUint64, -1) + nd3, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+p1.Cid().String()+`"}}`), math.MaxUint64, -1) if err != nil { t.Error(err) } - if err := api.Dag().AddMany(ctx, append(nd2, nd3...)); err != nil { + if err := api.Dag().AddMany(ctx, []ipld.Node{nd2, nd3}); err != nil { t.Fatal(err) } - err = api.Pin().Add(ctx, iface.IpldPath(nd2[0].Cid())) + err = api.Pin().Add(ctx, iface.IpldPath(nd2.Cid())) if err != nil { t.Error(err) } - err = api.Pin().Add(ctx, iface.IpldPath(nd3[0].Cid()), opt.Pin.Recursive(false)) + err = api.Pin().Add(ctx, iface.IpldPath(nd3.Cid()), opt.Pin.Recursive(false)) if err != nil { t.Error(err) } @@ -153,8 +155,8 @@ func (tp *provider) TestPinRecursive(t *testing.T) { t.Errorf("unexpected pin list len: %d", len(list)) } - if list[0].Path().String() != iface.IpldPath(nd3[0].Cid()).String() { - t.Errorf("unexpected path, %s != %s", list[0].Path().String(), iface.IpfsPath(nd2[0].Cid()).String()) + if list[0].Path().String() != iface.IpldPath(nd3.Cid()).String() { + t.Errorf("unexpected path, %s != %s", list[0].Path().String(), iface.IpfsPath(nd2.Cid()).String()) } list, err = api.Pin().Ls(ctx, opt.Pin.Type.Recursive()) @@ -166,8 +168,8 @@ func (tp *provider) TestPinRecursive(t *testing.T) { t.Errorf("unexpected pin list len: %d", len(list)) } - if list[0].Path().String() != iface.IpldPath(nd2[0].Cid()).String() { - t.Errorf("unexpected path, %s != %s", list[0].Path().String(), iface.IpldPath(nd3[0].Cid()).String()) + if list[0].Path().String() != iface.IpldPath(nd2.Cid()).String() { + t.Errorf("unexpected path, %s != %s", list[0].Path().String(), iface.IpldPath(nd3.Cid()).String()) } list, err = api.Pin().Ls(ctx, opt.Pin.Type.Indirect()) From a2f6e434b94663fc953c37e74156ee27b8ab73fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 8 Feb 2019 19:23:01 +0100 Subject: [PATCH 2920/3817] coreapi: move namesys options to coreapi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@263199d56ec6e7f2dab7c8619b75e2a6fbcf5f15 --- coreiface/options/name.go | 2 +- coreiface/options/namesys/opts.go | 74 +++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 coreiface/options/namesys/opts.go diff --git a/coreiface/options/name.go b/coreiface/options/name.go index e2a0fc164..e07ef8a59 100644 --- a/coreiface/options/name.go +++ b/coreiface/options/name.go @@ -3,7 +3,7 @@ package options import ( "time" - ropts "github.com/ipfs/go-ipfs/namesys/opts" + ropts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" ) const ( diff --git a/coreiface/options/namesys/opts.go b/coreiface/options/namesys/opts.go new file mode 100644 index 000000000..ee2bd5ac2 --- /dev/null +++ b/coreiface/options/namesys/opts.go @@ -0,0 +1,74 @@ +package nsopts + +import ( + "time" +) + +const ( + // DefaultDepthLimit is the default depth limit used by Resolve. + DefaultDepthLimit = 32 + + // UnlimitedDepth allows infinite recursion in Resolve. You + // probably don't want to use this, but it's here if you absolutely + // trust resolution to eventually complete and can't put an upper + // limit on how many steps it will take. + UnlimitedDepth = 0 +) + +// ResolveOpts specifies options for resolving an IPNS path +type ResolveOpts struct { + // Recursion depth limit + Depth uint + // The number of IPNS records to retrieve from the DHT + // (the best record is selected from this set) + DhtRecordCount uint + // The amount of time to wait for DHT records to be fetched + // and verified. A zero value indicates that there is no explicit + // timeout (although there is an implicit timeout due to dial + // timeouts within the DHT) + DhtTimeout time.Duration +} + +// DefaultResolveOpts returns the default options for resolving +// an IPNS path +func DefaultResolveOpts() ResolveOpts { + return ResolveOpts{ + Depth: DefaultDepthLimit, + DhtRecordCount: 16, + DhtTimeout: time.Minute, + } +} + +// ResolveOpt is used to set an option +type ResolveOpt func(*ResolveOpts) + +// Depth is the recursion depth limit +func Depth(depth uint) ResolveOpt { + return func(o *ResolveOpts) { + o.Depth = depth + } +} + +// DhtRecordCount is the number of IPNS records to retrieve from the DHT +func DhtRecordCount(count uint) ResolveOpt { + return func(o *ResolveOpts) { + o.DhtRecordCount = count + } +} + +// DhtTimeout is the amount of time to wait for DHT records to be fetched +// and verified. A zero value indicates that there is no explicit timeout +func DhtTimeout(timeout time.Duration) ResolveOpt { + return func(o *ResolveOpts) { + o.DhtTimeout = timeout + } +} + +// ProcessOpts converts an array of ResolveOpt into a ResolveOpts object +func ProcessOpts(opts []ResolveOpt) ResolveOpts { + rsopts := DefaultResolveOpts() + for _, option := range opts { + option(&rsopts) + } + return rsopts +} From 94470d9da65a3bcd624efa7d09ae945c48cd0566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 8 Feb 2019 19:23:01 +0100 Subject: [PATCH 2921/3817] coreapi: move namesys options to coreapi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@3ff5fdd4d17f76497d5f93837441446c037bc07f --- namesys/base.go | 2 +- namesys/dns.go | 2 +- namesys/dns_test.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 2 +- namesys/opts/opts.go | 74 ------------------------ namesys/proquint.go | 2 +- namesys/routing.go | 2 +- 10 files changed, 9 insertions(+), 83 deletions(-) delete mode 100644 namesys/opts/opts.go diff --git a/namesys/base.go b/namesys/base.go index de71ff345..79cb65be9 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -5,7 +5,7 @@ import ( "strings" "time" - opts "github.com/ipfs/go-ipfs/namesys/opts" + opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" ) diff --git a/namesys/dns.go b/namesys/dns.go index 6eb08ed80..5a34d5e25 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -6,7 +6,7 @@ import ( "net" "strings" - opts "github.com/ipfs/go-ipfs/namesys/opts" + opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" diff --git a/namesys/dns_test.go b/namesys/dns_test.go index ed28aa945..e434e19f8 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - opts "github.com/ipfs/go-ipfs/namesys/opts" + opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" ) type mockDNS struct { diff --git a/namesys/interface.go b/namesys/interface.go index 88c48efd8..96fbb35b3 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,7 +35,7 @@ import ( context "context" - opts "github.com/ipfs/go-ipfs/namesys/opts" + opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 0a9e31634..1e5c0d04c 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -7,7 +7,7 @@ import ( path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - opts "github.com/ipfs/go-ipfs/namesys/opts" + opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" diff --git a/namesys/namesys.go b/namesys/namesys.go index a16cf1388..6a1a495ae 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,7 +7,7 @@ import ( path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - opts "github.com/ipfs/go-ipfs/namesys/opts" + opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 3b3c1e255..1e0c173b6 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - opts "github.com/ipfs/go-ipfs/namesys/opts" + opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" diff --git a/namesys/opts/opts.go b/namesys/opts/opts.go deleted file mode 100644 index ee2bd5ac2..000000000 --- a/namesys/opts/opts.go +++ /dev/null @@ -1,74 +0,0 @@ -package nsopts - -import ( - "time" -) - -const ( - // DefaultDepthLimit is the default depth limit used by Resolve. - DefaultDepthLimit = 32 - - // UnlimitedDepth allows infinite recursion in Resolve. You - // probably don't want to use this, but it's here if you absolutely - // trust resolution to eventually complete and can't put an upper - // limit on how many steps it will take. - UnlimitedDepth = 0 -) - -// ResolveOpts specifies options for resolving an IPNS path -type ResolveOpts struct { - // Recursion depth limit - Depth uint - // The number of IPNS records to retrieve from the DHT - // (the best record is selected from this set) - DhtRecordCount uint - // The amount of time to wait for DHT records to be fetched - // and verified. A zero value indicates that there is no explicit - // timeout (although there is an implicit timeout due to dial - // timeouts within the DHT) - DhtTimeout time.Duration -} - -// DefaultResolveOpts returns the default options for resolving -// an IPNS path -func DefaultResolveOpts() ResolveOpts { - return ResolveOpts{ - Depth: DefaultDepthLimit, - DhtRecordCount: 16, - DhtTimeout: time.Minute, - } -} - -// ResolveOpt is used to set an option -type ResolveOpt func(*ResolveOpts) - -// Depth is the recursion depth limit -func Depth(depth uint) ResolveOpt { - return func(o *ResolveOpts) { - o.Depth = depth - } -} - -// DhtRecordCount is the number of IPNS records to retrieve from the DHT -func DhtRecordCount(count uint) ResolveOpt { - return func(o *ResolveOpts) { - o.DhtRecordCount = count - } -} - -// DhtTimeout is the amount of time to wait for DHT records to be fetched -// and verified. A zero value indicates that there is no explicit timeout -func DhtTimeout(timeout time.Duration) ResolveOpt { - return func(o *ResolveOpts) { - o.DhtTimeout = timeout - } -} - -// ProcessOpts converts an array of ResolveOpt into a ResolveOpts object -func ProcessOpts(opts []ResolveOpt) ResolveOpts { - rsopts := DefaultResolveOpts() - for _, option := range opts { - option(&rsopts) - } - return rsopts -} diff --git a/namesys/proquint.go b/namesys/proquint.go index 75d56c80f..850eb398e 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,7 +7,7 @@ import ( path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" - opts "github.com/ipfs/go-ipfs/namesys/opts" + opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" ) type ProquintResolver struct{} diff --git a/namesys/routing.go b/namesys/routing.go index bfe1520e7..66220aba9 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -7,7 +7,7 @@ import ( path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - opts "github.com/ipfs/go-ipfs/namesys/opts" + opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" From ea6b30e219991d9a4125537ac16e3e50df0650d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 8 Feb 2019 20:38:21 +0100 Subject: [PATCH 2922/3817] coreapi: fix failing dag test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/interface-go-ipfs-core@268b4fdbf1604d9296e09fe2cf1cf3328f498898 --- coreiface/tests/dag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index 4decfebb4..cf332027c 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -128,7 +128,7 @@ func (tp *provider) TestDagPath(t *testing.T) { t.Error(err) } - if nd.Cid().String() != snd.Cid().String() { + if ndd.Cid().String() != snd.Cid().String() { t.Errorf("got unexpected cid %s, expected %s", ndd.Cid().String(), snd.Cid().String()) } } From 2009531b47971008a2798aee1d0436182f8d6f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 9 Feb 2019 01:11:25 +0100 Subject: [PATCH 2923/3817] Add License This commit was moved from ipfs/interface-go-ipfs-core@6595d29079aa84f2e45e5cfd5bb0dce067ae9158 --- coreiface/LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 coreiface/LICENSE diff --git a/coreiface/LICENSE b/coreiface/LICENSE new file mode 100644 index 000000000..14121ca71 --- /dev/null +++ b/coreiface/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Protocol Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. From 5c537a46d37b548c6a830a7ea9ca8aca833e9acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 9 Feb 2019 01:15:09 +0100 Subject: [PATCH 2924/3817] Update imports This commit was moved from ipfs/interface-go-ipfs-core@515a114be219fdfdeed3f26c56c91fb7477439bb --- coreiface/block.go | 2 +- coreiface/coreapi.go | 2 +- coreiface/dht.go | 2 +- coreiface/key.go | 2 +- coreiface/name.go | 2 +- coreiface/object.go | 2 +- coreiface/options/name.go | 2 +- coreiface/pin.go | 2 +- coreiface/pubsub.go | 2 +- coreiface/tests/api.go | 2 +- coreiface/tests/block.go | 4 ++-- coreiface/tests/dag.go | 2 +- coreiface/tests/dht.go | 4 ++-- coreiface/tests/key.go | 4 ++-- coreiface/tests/name.go | 4 ++-- coreiface/tests/object.go | 4 ++-- coreiface/tests/path.go | 4 ++-- coreiface/tests/pin.go | 4 ++-- coreiface/tests/pubsub.go | 5 +++-- coreiface/tests/unixfs.go | 4 ++-- coreiface/unixfs.go | 2 +- 21 files changed, 31 insertions(+), 30 deletions(-) diff --git a/coreiface/block.go b/coreiface/block.go index b99b05fdb..587ad339f 100644 --- a/coreiface/block.go +++ b/coreiface/block.go @@ -4,7 +4,7 @@ import ( "context" "io" - options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + options "github.com/ipfs/interface-go-ipfs-core/options" ) // BlockStat contains information about a block diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index d26ec4f7d..651af8bf0 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -5,7 +5,7 @@ package iface import ( "context" - "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + "github.com/ipfs/interface-go-ipfs-core/options" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" ) diff --git a/coreiface/dht.go b/coreiface/dht.go index 94fb3779f..b3f7879e3 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -3,7 +3,7 @@ package iface import ( "context" - "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + "github.com/ipfs/interface-go-ipfs-core/options" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" pstore "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore" diff --git a/coreiface/key.go b/coreiface/key.go index 69857e613..154f82b66 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -3,7 +3,7 @@ package iface import ( "context" - options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + options "github.com/ipfs/interface-go-ipfs-core/options" "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" ) diff --git a/coreiface/name.go b/coreiface/name.go index a02bc0787..51b005b7e 100644 --- a/coreiface/name.go +++ b/coreiface/name.go @@ -4,7 +4,7 @@ import ( "context" "errors" - options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + options "github.com/ipfs/interface-go-ipfs-core/options" ) var ErrResolveFailed = errors.New("could not resolve name") diff --git a/coreiface/object.go b/coreiface/object.go index 2ed357cb6..28613aaa0 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -4,7 +4,7 @@ import ( "context" "io" - options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + options "github.com/ipfs/interface-go-ipfs-core/options" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" diff --git a/coreiface/options/name.go b/coreiface/options/name.go index e07ef8a59..59aaf2ca3 100644 --- a/coreiface/options/name.go +++ b/coreiface/options/name.go @@ -3,7 +3,7 @@ package options import ( "time" - ropts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" + ropts "github.com/ipfs/interface-go-ipfs-core/options/namesys" ) const ( diff --git a/coreiface/pin.go b/coreiface/pin.go index 6e13def8f..6a7dab413 100644 --- a/coreiface/pin.go +++ b/coreiface/pin.go @@ -3,7 +3,7 @@ package iface import ( "context" - options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + options "github.com/ipfs/interface-go-ipfs-core/options" ) // Pin holds information about pinned resource diff --git a/coreiface/pubsub.go b/coreiface/pubsub.go index 933673826..40cea689a 100644 --- a/coreiface/pubsub.go +++ b/coreiface/pubsub.go @@ -4,7 +4,7 @@ import ( "context" "io" - options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + options "github.com/ipfs/interface-go-ipfs-core/options" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" ) diff --git a/coreiface/tests/api.go b/coreiface/tests/api.go index 7a4bd7386..5e7c1f541 100644 --- a/coreiface/tests/api.go +++ b/coreiface/tests/api.go @@ -6,7 +6,7 @@ import ( "testing" "time" - coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" + coreiface "github.com/ipfs/interface-go-ipfs-core" ) var apiNotImplemented = errors.New("api not implemented") diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index c2ee70a3a..2e0a84b40 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -6,8 +6,8 @@ import ( "strings" "testing" - coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" - opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + coreiface "github.com/ipfs/interface-go-ipfs-core" + opt "github.com/ipfs/interface-go-ipfs-core/options" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index cf332027c..9e0bc34ba 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" + coreiface "github.com/ipfs/interface-go-ipfs-core" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" ipldcbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" diff --git a/coreiface/tests/dht.go b/coreiface/tests/dht.go index d2eae1af4..1793cd738 100644 --- a/coreiface/tests/dht.go +++ b/coreiface/tests/dht.go @@ -5,8 +5,8 @@ import ( "io" "testing" - "github.com/ipfs/go-ipfs/core/coreapi/interface" - "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + "github.com/ipfs/interface-go-ipfs-core" + "github.com/ipfs/interface-go-ipfs-core/options" ) func (tp *provider) TestDht(t *testing.T) { diff --git a/coreiface/tests/key.go b/coreiface/tests/key.go index 66011f99f..dbbfce059 100644 --- a/coreiface/tests/key.go +++ b/coreiface/tests/key.go @@ -5,8 +5,8 @@ import ( "strings" "testing" - "github.com/ipfs/go-ipfs/core/coreapi/interface" - opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + "github.com/ipfs/interface-go-ipfs-core" + opt "github.com/ipfs/interface-go-ipfs-core/options" ) func (tp *provider) TestKey(t *testing.T) { diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index 8d87bd495..eb5cd1e3a 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -11,8 +11,8 @@ import ( ipath "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" - coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" - opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + coreiface "github.com/ipfs/interface-go-ipfs-core" + opt "github.com/ipfs/interface-go-ipfs-core/options" ) func (tp *provider) TestName(t *testing.T) { diff --git a/coreiface/tests/object.go b/coreiface/tests/object.go index 2a3b1bd5c..026def73b 100644 --- a/coreiface/tests/object.go +++ b/coreiface/tests/object.go @@ -8,8 +8,8 @@ import ( "strings" "testing" - "github.com/ipfs/go-ipfs/core/coreapi/interface" - opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + "github.com/ipfs/interface-go-ipfs-core" + opt "github.com/ipfs/interface-go-ipfs-core/options" ) func (tp *provider) TestObject(t *testing.T) { diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index 5594cf0da..01841d869 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -6,8 +6,8 @@ import ( "strings" "testing" - coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" - "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + coreiface "github.com/ipfs/interface-go-ipfs-core" + "github.com/ipfs/interface-go-ipfs-core/options" ipldcbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" ) diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index 35c913618..27ed2ad5d 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -6,8 +6,8 @@ import ( "strings" "testing" - "github.com/ipfs/go-ipfs/core/coreapi/interface" - opt "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + "github.com/ipfs/interface-go-ipfs-core" + opt "github.com/ipfs/interface-go-ipfs-core/options" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" ipldcbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" diff --git a/coreiface/tests/pubsub.go b/coreiface/tests/pubsub.go index b993f51dc..14e3545a9 100644 --- a/coreiface/tests/pubsub.go +++ b/coreiface/tests/pubsub.go @@ -2,10 +2,11 @@ package tests import ( "context" - "github.com/ipfs/go-ipfs/core/coreapi/interface" - "github.com/ipfs/go-ipfs/core/coreapi/interface/options" "testing" "time" + + "github.com/ipfs/interface-go-ipfs-core" + "github.com/ipfs/interface-go-ipfs-core/options" ) func (tp *provider) TestPubSub(t *testing.T) { diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index f5ce85b78..cb5897b69 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -15,8 +15,8 @@ import ( "sync" "testing" - coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" - "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + coreiface "github.com/ipfs/interface-go-ipfs-core" + "github.com/ipfs/interface-go-ipfs-core/options" "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" cbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 8e559022c..c01ccde78 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -2,7 +2,7 @@ package iface import ( "context" - "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + "github.com/ipfs/interface-go-ipfs-core/options" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" "gx/ipfs/QmZArMcsVDsXdcLbUx4844CuqKXBpbxdeiryM4cnmGTNRq/go-unixfs" From def3b3d8058572379b772d2db227011fd45662ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 9 Feb 2019 01:23:13 +0100 Subject: [PATCH 2925/3817] gx-go uw This commit was moved from ipfs/interface-go-ipfs-core@93299fcb14d845e3ed4c128f0792f18458794c62 --- coreiface/coreapi.go | 2 +- coreiface/dag.go | 2 +- coreiface/dht.go | 4 ++-- coreiface/key.go | 2 +- coreiface/object.go | 4 ++-- coreiface/options/block.go | 4 ++-- coreiface/options/unixfs.go | 6 +++--- coreiface/path.go | 4 ++-- coreiface/pubsub.go | 2 +- coreiface/swarm.go | 10 +++++----- coreiface/tests/block.go | 2 +- coreiface/tests/dag.go | 6 +++--- coreiface/tests/name.go | 4 ++-- coreiface/tests/path.go | 4 ++-- coreiface/tests/pin.go | 4 ++-- coreiface/tests/unixfs.go | 14 +++++++------- coreiface/unixfs.go | 6 +++--- 17 files changed, 40 insertions(+), 40 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index 651af8bf0..f3433c089 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -7,7 +7,7 @@ import ( "github.com/ipfs/interface-go-ipfs-core/options" - ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" + ipld "github.com/ipfs/go-ipld-format" ) // CoreAPI defines an unified interface to IPFS for Go programs diff --git a/coreiface/dag.go b/coreiface/dag.go index d15e24360..3cc3aeb4d 100644 --- a/coreiface/dag.go +++ b/coreiface/dag.go @@ -1,7 +1,7 @@ package iface import ( - ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" + ipld "github.com/ipfs/go-ipld-format" ) // APIDagService extends ipld.DAGService diff --git a/coreiface/dht.go b/coreiface/dht.go index b3f7879e3..d1ae05125 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -5,8 +5,8 @@ import ( "github.com/ipfs/interface-go-ipfs-core/options" - peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" - pstore "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore" + peer "github.com/libp2p/go-libp2p-peer" + pstore "github.com/libp2p/go-libp2p-peerstore" ) // DhtAPI specifies the interface to the DHT diff --git a/coreiface/key.go b/coreiface/key.go index 154f82b66..78c29d268 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -5,7 +5,7 @@ import ( options "github.com/ipfs/interface-go-ipfs-core/options" - "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" + "github.com/libp2p/go-libp2p-peer" ) // Key specifies the interface to Keys in KeyAPI Keystore diff --git a/coreiface/object.go b/coreiface/object.go index 28613aaa0..4f9652fb1 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -6,8 +6,8 @@ import ( options "github.com/ipfs/interface-go-ipfs-core/options" - cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" ) // ObjectStat provides information about dag nodes diff --git a/coreiface/options/block.go b/coreiface/options/block.go index 40dfba79a..043dfdea4 100644 --- a/coreiface/options/block.go +++ b/coreiface/options/block.go @@ -2,8 +2,8 @@ package options import ( "fmt" - cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" + cid "github.com/ipfs/go-cid" + mh "github.com/multiformats/go-multihash" ) type BlockPutSettings struct { diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 0dd129609..b76b01adf 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -4,9 +4,9 @@ import ( "errors" "fmt" - cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - dag "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" - mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" + cid "github.com/ipfs/go-cid" + dag "github.com/ipfs/go-merkledag" + mh "github.com/multiformats/go-multihash" ) type Layout int diff --git a/coreiface/path.go b/coreiface/path.go index d59a851b4..4e86172ac 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,8 +1,8 @@ package iface import ( - ipfspath "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + "github.com/ipfs/go-cid" + ipfspath "github.com/ipfs/go-path" ) //TODO: merge with ipfspath so we don't depend on it diff --git a/coreiface/pubsub.go b/coreiface/pubsub.go index 40cea689a..212e77225 100644 --- a/coreiface/pubsub.go +++ b/coreiface/pubsub.go @@ -6,7 +6,7 @@ import ( options "github.com/ipfs/interface-go-ipfs-core/options" - peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" + peer "github.com/libp2p/go-libp2p-peer" ) // PubSubSubscription is an active PubSub subscription diff --git a/coreiface/swarm.go b/coreiface/swarm.go index 3af078f17..2e00ecbd3 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -5,11 +5,11 @@ import ( "errors" "time" - ma "gx/ipfs/QmNTCey11oxhb1AxDnQBRHtdhap6Ctud872NjAYPYYXPuc/go-multiaddr" - "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" - pstore "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore" - net "gx/ipfs/QmZ7cBWUXkyWTMN4qH6NGoyMVs7JugyFChBNP4ZUp5rJHH/go-libp2p-net" - "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol" + net "github.com/libp2p/go-libp2p-net" + "github.com/libp2p/go-libp2p-peer" + pstore "github.com/libp2p/go-libp2p-peerstore" + "github.com/libp2p/go-libp2p-protocol" + ma "github.com/multiformats/go-multiaddr" ) var ( diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 2e0a84b40..3cd74358d 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -9,7 +9,7 @@ import ( coreiface "github.com/ipfs/interface-go-ipfs-core" opt "github.com/ipfs/interface-go-ipfs-core/options" - mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" + mh "github.com/multiformats/go-multihash" ) func (tp *provider) TestBlock(t *testing.T) { diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index 9e0bc34ba..7446c20de 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -9,9 +9,9 @@ import ( coreiface "github.com/ipfs/interface-go-ipfs-core" - ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" - ipldcbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" - mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" + ipldcbor "github.com/ipfs/go-ipld-cbor" + ipld "github.com/ipfs/go-ipld-format" + mh "github.com/multiformats/go-multihash" ) func (tp *provider) TestDag(t *testing.T) { diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index eb5cd1e3a..1eb2dd513 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -8,8 +8,8 @@ import ( "testing" "time" - ipath "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" + "github.com/ipfs/go-ipfs-files" + ipath "github.com/ipfs/go-path" coreiface "github.com/ipfs/interface-go-ipfs-core" opt "github.com/ipfs/interface-go-ipfs-core/options" diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index 01841d869..4da1a5181 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -9,7 +9,7 @@ import ( coreiface "github.com/ipfs/interface-go-ipfs-core" "github.com/ipfs/interface-go-ipfs-core/options" - ipldcbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" + ipldcbor "github.com/ipfs/go-ipld-cbor" ) func (tp *provider) TestPath(t *testing.T) { @@ -38,7 +38,7 @@ func (tp *provider) TestMutablePath(t *testing.T) { t.Error("expected /ipld path to be immutable") } - // get self /ipns path ipldcbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" + // get self /ipns path if api.Key() == nil { t.Fatal(".Key not implemented") diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index 27ed2ad5d..eed542283 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -9,8 +9,8 @@ import ( "github.com/ipfs/interface-go-ipfs-core" opt "github.com/ipfs/interface-go-ipfs-core/options" - ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" - ipldcbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" + ipldcbor "github.com/ipfs/go-ipld-cbor" + ipld "github.com/ipfs/go-ipld-format" ) func (tp *provider) TestPin(t *testing.T) { diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index cb5897b69..bcb5331d5 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -18,13 +18,13 @@ import ( coreiface "github.com/ipfs/interface-go-ipfs-core" "github.com/ipfs/interface-go-ipfs-core/options" - "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - cbor "gx/ipfs/QmRZxJ7oybgnnwriuRub9JXp5YdFM9wiGSyRq38QC7swpS/go-ipld-cbor" - mdag "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" - "gx/ipfs/QmZArMcsVDsXdcLbUx4844CuqKXBpbxdeiryM4cnmGTNRq/go-unixfs" - "gx/ipfs/QmZArMcsVDsXdcLbUx4844CuqKXBpbxdeiryM4cnmGTNRq/go-unixfs/importer/helpers" - "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" - mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-ipfs-files" + cbor "github.com/ipfs/go-ipld-cbor" + mdag "github.com/ipfs/go-merkledag" + "github.com/ipfs/go-unixfs" + "github.com/ipfs/go-unixfs/importer/helpers" + mh "github.com/multiformats/go-multihash" ) func (tp *provider) TestUnixfs(t *testing.T) { diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index c01ccde78..5aae00dc4 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -4,9 +4,9 @@ import ( "context" "github.com/ipfs/interface-go-ipfs-core/options" - ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" - "gx/ipfs/QmZArMcsVDsXdcLbUx4844CuqKXBpbxdeiryM4cnmGTNRq/go-unixfs" - "gx/ipfs/QmaXvvAVAQ5ABqM5xtjYmV85xmN5MkWAZsX9H9Fwo4FVXp/go-ipfs-files" + "github.com/ipfs/go-ipfs-files" + ipld "github.com/ipfs/go-ipld-format" + "github.com/ipfs/go-unixfs" ) type AddEvent struct { From a2c6f1f76b1fa4725a2408e49538da8975553fa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 9 Feb 2019 01:40:57 +0100 Subject: [PATCH 2926/3817] coreapi: update imports to updated interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@a7de936a5e314ef01ef91eb9eac9088d15070192 --- namesys/base.go | 2 +- namesys/dns.go | 2 +- namesys/dns_test.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 2 +- namesys/proquint.go | 2 +- namesys/routing.go | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 79cb65be9..91fda7301 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -5,7 +5,7 @@ import ( "strings" "time" - opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" + opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" ) diff --git a/namesys/dns.go b/namesys/dns.go index 5a34d5e25..fcc93b712 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -6,7 +6,7 @@ import ( "net" "strings" - opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" + opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" diff --git a/namesys/dns_test.go b/namesys/dns_test.go index e434e19f8..4fdc46c35 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" + opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" ) type mockDNS struct { diff --git a/namesys/interface.go b/namesys/interface.go index 96fbb35b3..10009bb82 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,8 +35,8 @@ import ( context "context" - opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" + opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 1e5c0d04c..0e94abfdb 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -7,7 +7,7 @@ import ( path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" + opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" diff --git a/namesys/namesys.go b/namesys/namesys.go index 6a1a495ae..4eba5bae5 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,7 +7,7 @@ import ( path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" + opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 1e0c173b6..45314bebd 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" + opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" diff --git a/namesys/proquint.go b/namesys/proquint.go index 850eb398e..f488ba6b4 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -7,7 +7,7 @@ import ( path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" - opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" + opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" ) type ProquintResolver struct{} diff --git a/namesys/routing.go b/namesys/routing.go index 66220aba9..f8c6f9a2c 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -7,7 +7,7 @@ import ( path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options/namesys" + opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" From 9bd5f6cfb14fcf95e83363f511f2f1408e594700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 9 Feb 2019 01:57:26 +0100 Subject: [PATCH 2927/3817] coreapi: fix import grouping after extracting iface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@9fec2b36bbaf843a3f457506c1136af90569995d --- namesys/base.go | 3 +-- namesys/dns.go | 3 +-- namesys/interface.go | 3 +-- namesys/ipns_resolver_validation_test.go | 6 ++---- namesys/namesys.go | 6 ++---- namesys/namesys_test.go | 3 +-- namesys/proquint.go | 3 +-- namesys/publisher.go | 4 ++-- namesys/resolve_test.go | 3 +-- namesys/routing.go | 6 ++---- 10 files changed, 14 insertions(+), 26 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 91fda7301..4818cfbaf 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -5,9 +5,8 @@ import ( "strings" "time" - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" + opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" ) type onceResult struct { diff --git a/namesys/dns.go b/namesys/dns.go index fcc93b712..868d0a9b3 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -6,9 +6,8 @@ import ( "net" "strings" - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" + opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/interface.go b/namesys/interface.go index 10009bb82..556775122 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,10 +35,9 @@ import ( context "context" + ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" - - ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 0e94abfdb..af1980145 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,13 +5,10 @@ import ( "testing" "time" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" - ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" pstore "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore" pstoremem "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore/pstoremem" mockrouting "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/mock" @@ -20,6 +17,7 @@ import ( ropts "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing/options" testutil "gx/ipfs/QmVnJMgafh5MBYiyqbvDtoCL8pcQvbEGD2k9o9GFpBWPzY/go-testutil" ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" + opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" record "gx/ipfs/QmexPd3srWxHC76gW2p5j5tQvwpPuCoW7b9vFhJ8BRPyh9/go-libp2p-record" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" diff --git a/namesys/namesys.go b/namesys/namesys.go index 4eba5bae5..8e39b0bfa 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,14 +5,12 @@ import ( "strings" "time" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" - ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" + opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 45314bebd..9cb17fa7e 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -5,14 +5,13 @@ import ( "fmt" "testing" - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" - ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" pstoremem "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore/pstoremem" offroute "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/offline" ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" + opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" "gx/ipfs/QmZArMcsVDsXdcLbUx4844CuqKXBpbxdeiryM4cnmGTNRq/go-unixfs" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" diff --git a/namesys/proquint.go b/namesys/proquint.go index f488ba6b4..c63d7e03d 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -5,9 +5,8 @@ import ( "errors" path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" + proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index d20cda5a0..19b49e6d1 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -7,14 +7,14 @@ import ( "time" pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - ft "gx/ipfs/QmZArMcsVDsXdcLbUx4844CuqKXBpbxdeiryM4cnmGTNRq/go-unixfs" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" pb "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns/pb" + ft "gx/ipfs/QmZArMcsVDsXdcLbUx4844CuqKXBpbxdeiryM4cnmGTNRq/go-unixfs" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dsquery "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 54cc4c8d6..368e52698 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,9 +6,8 @@ import ( "testing" "time" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" mockrouting "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/mock" testutil "gx/ipfs/QmVnJMgafh5MBYiyqbvDtoCL8pcQvbEGD2k9o9GFpBWPzY/go-testutil" ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" diff --git a/namesys/routing.go b/namesys/routing.go index f8c6f9a2c..6ab72f2f7 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,15 +5,13 @@ import ( "strings" "time" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" - peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" + path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" pb "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns/pb" + opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" dht "gx/ipfs/Qmeh1RJ3kvEXgmuEmbNLwZ9wVUDuaqE7BhhEngd8aXV8tf/go-libp2p-kad-dht" From 3559740e16963ab8d004a003575fe3e70c2a170c Mon Sep 17 00:00:00 2001 From: Adam Uhlir Date: Fri, 8 Feb 2019 17:06:03 -0800 Subject: [PATCH 2928/3817] Rename DefaultRecordTTL into DefaultRecordEOL License: MIT Signed-off-by: Adam Uhlir This commit was moved from ipfs/go-namesys@3c318b843063d461e4236de8d918b9019c437dde --- namesys/namesys.go | 2 +- namesys/publisher.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 6a1a495ae..ae8f422d4 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -173,7 +173,7 @@ func emitOnceResult(ctx context.Context, outCh chan<- onceResult, r onceResult) // Publish implements Publisher func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error { - return ns.PublishWithEOL(ctx, name, value, time.Now().Add(DefaultRecordTTL)) + return ns.PublishWithEOL(ctx, name, value, time.Now().Add(DefaultRecordEOL)) } func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error { diff --git a/namesys/publisher.go b/namesys/publisher.go index d20cda5a0..b4f9eafe4 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -24,7 +24,7 @@ import ( const ipnsPrefix = "/ipns/" const PublishPutValTimeout = time.Minute -const DefaultRecordTTL = 24 * time.Hour +const DefaultRecordEOL = 24 * time.Hour // IpnsPublisher is capable of publishing and resolving names to the IPFS // routing system. @@ -48,7 +48,7 @@ func NewIpnsPublisher(route routing.ValueStore, ds ds.Datastore) *IpnsPublisher // and publishes it out to the routing system func (p *IpnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error { log.Debugf("Publish %s", value) - return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordTTL)) + return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordEOL)) } func IpnsDsKey(id peer.ID) ds.Key { From c426f90634a506cc46bf18d55ca59fe2883cd00a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 11 Feb 2019 08:46:22 -0800 Subject: [PATCH 2929/3817] gx: update go-ipfs-files fix compatibility issue with js-ipfs License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@d0ac8b42160cba98ecaec1ae4f8fcd9a1f108eab --- namesys/base.go | 2 +- namesys/dns.go | 2 +- namesys/dns_test.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 4 ++-- namesys/proquint.go | 2 +- namesys/publisher.go | 2 +- namesys/routing.go | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 4818cfbaf..682b78f2c 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -6,7 +6,7 @@ import ( "time" path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" ) type onceResult struct { diff --git a/namesys/dns.go b/namesys/dns.go index 868d0a9b3..1898453a6 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -7,7 +7,7 @@ import ( "strings" path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 4fdc46c35..07bc8c66e 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" ) type mockDNS struct { diff --git a/namesys/interface.go b/namesys/interface.go index 556775122..a7a277cbd 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -37,7 +37,7 @@ import ( ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index af1980145..6a1095b37 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -15,9 +15,9 @@ import ( offline "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/offline" routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" ropts "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing/options" + opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" testutil "gx/ipfs/QmVnJMgafh5MBYiyqbvDtoCL8pcQvbEGD2k9o9GFpBWPzY/go-testutil" ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" record "gx/ipfs/QmexPd3srWxHC76gW2p5j5tQvwpPuCoW7b9vFhJ8BRPyh9/go-libp2p-record" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" diff --git a/namesys/namesys.go b/namesys/namesys.go index 8e39b0bfa..ae96b696c 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -10,7 +10,7 @@ import ( path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 9cb17fa7e..0a885ae1f 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,9 +10,9 @@ import ( path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" pstoremem "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore/pstoremem" offroute "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/offline" + opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" - "gx/ipfs/QmZArMcsVDsXdcLbUx4844CuqKXBpbxdeiryM4cnmGTNRq/go-unixfs" + "gx/ipfs/QmetDvVkKzbr8PYuBV6S48q5DU9EUQktYjo9KdkA3zbQgK/go-unixfs" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) diff --git a/namesys/proquint.go b/namesys/proquint.go index c63d7e03d..b49be8b00 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -5,7 +5,7 @@ import ( "errors" path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 19b49e6d1..064b4e087 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -14,8 +14,8 @@ import ( routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" pb "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns/pb" - ft "gx/ipfs/QmZArMcsVDsXdcLbUx4844CuqKXBpbxdeiryM4cnmGTNRq/go-unixfs" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" + ft "gx/ipfs/QmetDvVkKzbr8PYuBV6S48q5DU9EUQktYjo9KdkA3zbQgK/go-unixfs" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dsquery "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" diff --git a/namesys/routing.go b/namesys/routing.go index 6ab72f2f7..4a8671f62 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -9,9 +9,9 @@ import ( path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" + opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" pb "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns/pb" - opts "gx/ipfs/QmWqb6eEpQ2qtu2jmcDWJXebP7YS14fwor8562g795ZxjH/interface-go-ipfs-core/options/namesys" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" dht "gx/ipfs/Qmeh1RJ3kvEXgmuEmbNLwZ9wVUDuaqE7BhhEngd8aXV8tf/go-libp2p-kad-dht" From bc7e36143ffe315dc853eecd39d4be5e9b1ab425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 12 Feb 2019 13:30:07 +0100 Subject: [PATCH 2930/3817] pubsub: fix race in test This commit was moved from ipfs/interface-go-ipfs-core@a84bfa1f4055bb15e3727841df03c3f306ed5cfc --- coreiface/tests/pubsub.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/tests/pubsub.go b/coreiface/tests/pubsub.go index 14e3545a9..bb870de6c 100644 --- a/coreiface/tests/pubsub.go +++ b/coreiface/tests/pubsub.go @@ -38,7 +38,7 @@ func (tp *provider) TestBasicPubSub(t *testing.T) { tick := time.Tick(100 * time.Millisecond) for { - err = apis[1].PubSub().Publish(ctx, "testch", []byte("hello world")) + err := apis[1].PubSub().Publish(ctx, "testch", []byte("hello world")) if err != nil { t.Fatal(err) } From dc402e7962af0a940097f8bfa959036d12db424a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 14 Feb 2019 14:58:35 -0800 Subject: [PATCH 2931/3817] gx: update libp2p stuff License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@8e9d688d14bc48ffbb204c2d051bc3017949ca1d --- namesys/base.go | 4 ++-- namesys/cache.go | 2 +- namesys/dns.go | 4 ++-- namesys/dns_test.go | 2 +- namesys/interface.go | 4 ++-- namesys/ipns_resolver_validation_test.go | 4 ++-- namesys/namesys.go | 4 ++-- namesys/namesys_test.go | 6 +++--- namesys/proquint.go | 4 ++-- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 6 +++--- 14 files changed, 26 insertions(+), 26 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 682b78f2c..6b6de3d04 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -5,8 +5,8 @@ import ( "strings" "time" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" + path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 44ecaab4d..0dc79d87a 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" + path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 1898453a6..e2c3202ef 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -6,8 +6,8 @@ import ( "net" "strings" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" + path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 07bc8c66e..29fce1507 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" ) type mockDNS struct { diff --git a/namesys/interface.go b/namesys/interface.go index a7a277cbd..137a08e87 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -36,8 +36,8 @@ import ( context "context" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" + path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 6a1095b37..9ab672dce 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,16 +6,16 @@ import ( "time" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" + opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" pstore "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore" pstoremem "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore/pstoremem" + path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" mockrouting "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/mock" offline "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/offline" routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" ropts "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing/options" - opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" testutil "gx/ipfs/QmVnJMgafh5MBYiyqbvDtoCL8pcQvbEGD2k9o9GFpBWPzY/go-testutil" ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" record "gx/ipfs/QmexPd3srWxHC76gW2p5j5tQvwpPuCoW7b9vFhJ8BRPyh9/go-libp2p-record" diff --git a/namesys/namesys.go b/namesys/namesys.go index 9192a300f..a74e94f77 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,11 +6,11 @@ import ( "time" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" + opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" + path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" - opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 0a885ae1f..3223697a4 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -6,13 +6,13 @@ import ( "testing" ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" + opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" pstoremem "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore/pstoremem" + path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" offroute "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/offline" - opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" + "gx/ipfs/QmSygPSC63Uka8z9PYokAS4thiMAor17vhXUTi4qmKHh6P/go-unixfs" ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" - "gx/ipfs/QmetDvVkKzbr8PYuBV6S48q5DU9EUQktYjo9KdkA3zbQgK/go-unixfs" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" ) diff --git a/namesys/proquint.go b/namesys/proquint.go index b49be8b00..37df4aba0 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,8 +4,8 @@ import ( "context" "errors" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" - opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" + path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 0583a4d98..69eb52258 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -10,12 +10,12 @@ import ( ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" + path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" + ft "gx/ipfs/QmSygPSC63Uka8z9PYokAS4thiMAor17vhXUTi4qmKHh6P/go-unixfs" ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" pb "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns/pb" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" - ft "gx/ipfs/QmetDvVkKzbr8PYuBV6S48q5DU9EUQktYjo9KdkA3zbQgK/go-unixfs" ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" dsquery "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 46d381a5d..fb7ea3ded 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" + path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" ic "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 738a3301d..2ae20392e 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" + path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" pstore "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmSgtf5vHyugoxcwMbyNy6bZ9qPDDTJSYEED2GkWjLwitZ/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmebEmt23jQxrwnqBkFL4qbpE8EnnQunpv5U32LS5ESus1/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 368e52698..930d5219a 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -7,7 +7,7 @@ import ( "time" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" + path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" mockrouting "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/mock" testutil "gx/ipfs/QmVnJMgafh5MBYiyqbvDtoCL8pcQvbEGD2k9o9GFpBWPzY/go-testutil" ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" diff --git a/namesys/routing.go b/namesys/routing.go index 4a8671f62..d08bb5103 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,16 +5,16 @@ import ( "strings" "time" + opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" - path "gx/ipfs/QmQ3YSqfxunT5QBg6KBVskKyRE26q6hjSMyhpxchpm7jEN/go-path" + path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" - opts "gx/ipfs/QmVSbopkxvLSRFuUn1SeHoEcArhCLn2okUbVpLvhQ1pm1X/interface-go-ipfs-core/options/namesys" + dht "gx/ipfs/QmS5Tvk8Adz1qPkCBCbiScty9KPbMSMCSTbFK4TVvatKqi/go-libp2p-kad-dht" ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" pb "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns/pb" logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" - dht "gx/ipfs/Qmeh1RJ3kvEXgmuEmbNLwZ9wVUDuaqE7BhhEngd8aXV8tf/go-libp2p-kad-dht" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) From 608db31d87f6b819235388eeae8bd20c86f14599 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 14 Feb 2019 14:58:35 -0800 Subject: [PATCH 2932/3817] gx: update libp2p stuff License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@7efadfb8a743832e11515e05b813116dcc3f4216 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index eebfb1f41..86bc86f1d 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" - bserv "gx/ipfs/QmbgbNxC1PMyS2gbx7nf2jKNG7bZAfYJJebdK4ptBBWCz1/go-blockservice" + dag "gx/ipfs/QmQvMsV5aPyd7eMd3U1hvAUhZEupG3rXbVZn7ppU5RE6bt/go-merkledag" + bserv "gx/ipfs/QmZuPasxd7fSgtzRzCL7Z8J8QwDJML2fgBUExRbQCqb4BT/go-blockservice" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index bac29feb7..bf4a71957 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" + mdag "gx/ipfs/QmQvMsV5aPyd7eMd3U1hvAUhZEupG3rXbVZn7ppU5RE6bt/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 763643fea..f7da55269 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - mdag "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" - bs "gx/ipfs/QmbgbNxC1PMyS2gbx7nf2jKNG7bZAfYJJebdK4ptBBWCz1/go-blockservice" + mdag "gx/ipfs/QmQvMsV5aPyd7eMd3U1hvAUhZEupG3rXbVZn7ppU5RE6bt/go-merkledag" + bs "gx/ipfs/QmZuPasxd7fSgtzRzCL7Z8J8QwDJML2fgBUExRbQCqb4BT/go-blockservice" util "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 982f7d22a..846e2013e 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" + "gx/ipfs/QmQvMsV5aPyd7eMd3U1hvAUhZEupG3rXbVZn7ppU5RE6bt/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index b81f9aad2..96340bed1 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" - bserv "gx/ipfs/QmbgbNxC1PMyS2gbx7nf2jKNG7bZAfYJJebdK4ptBBWCz1/go-blockservice" + dag "gx/ipfs/QmQvMsV5aPyd7eMd3U1hvAUhZEupG3rXbVZn7ppU5RE6bt/go-merkledag" + bserv "gx/ipfs/QmZuPasxd7fSgtzRzCL7Z8J8QwDJML2fgBUExRbQCqb4BT/go-blockservice" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" From 00f24d271d3f65b0fd3cf847b18d2d934166523d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 14 Feb 2019 14:58:35 -0800 Subject: [PATCH 2933/3817] gx: update libp2p stuff License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@87f6409caeff502c8679040b57ef95100ceb953f --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 8205bd6cc..2d06a9ac2 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmUtsx89yiCY6F8mbpP6ecXckiSzCBH7EvkKZuZEHBcr1m/go-merkledag" + dag "gx/ipfs/QmQvMsV5aPyd7eMd3U1hvAUhZEupG3rXbVZn7ppU5RE6bt/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" From c61f71bc165f808660def06678c026d75e0ae7a0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 14 Feb 2019 15:05:15 -0800 Subject: [PATCH 2934/3817] Increase FetchGraphConcurrency to 32 Given all the bitswap session improvements, I think it's time to give our users some candy. This should allow us to actually take advantage of the increased wantlist sizes. This commit was moved from ipfs/go-merkledag@56bc5a45da2a91f35cf66072d690a7dcdd1b5cb1 --- ipld/merkledag/merkledag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 295f899e3..3153cf41e 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -342,7 +342,7 @@ func (p *ProgressTracker) Value() int { // FetchGraphConcurrency is total number of concurrent fetches that // 'fetchNodes' will start at a time -var FetchGraphConcurrency = 8 +var FetchGraphConcurrency = 32 // EnumerateChildrenAsync is equivalent to EnumerateChildren *except* that it // fetches children in parallel. From ea3c527fd71ae88eecfbf03380759594b9934a83 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 18 Feb 2019 16:24:47 +0100 Subject: [PATCH 2935/3817] gx publish 0.1.7 This commit was moved from ipfs/go-ipfs-blockstore@fd2101897a88d0a9649aa61c902282455c544d46 --- blockstore/blockstore_test.go | 3 +++ blockstore/bloom_cache_test.go | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 50b8ae055..b01574a44 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -272,3 +272,6 @@ func (c *queryTestDS) Query(q dsq.Query) (dsq.Results, error) { func (c *queryTestDS) Batch() (ds.Batch, error) { return ds.NewBasicBatch(c), nil } +func (c *queryTestDS) Close() error { + return nil +} diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index d9474ada1..fd65c0b28 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -148,6 +148,8 @@ func TestHasIsBloomCached(t *testing.T) { } } +var _ ds.Batching = (*callbackDatastore)(nil) + type callbackDatastore struct { sync.Mutex f func() @@ -186,6 +188,10 @@ func (c *callbackDatastore) GetSize(key ds.Key) (size int, err error) { return c.ds.GetSize(key) } +func (c *callbackDatastore) Close() error { + return nil +} + func (c *callbackDatastore) Delete(key ds.Key) (err error) { c.CallF() return c.ds.Delete(key) From 6f16fbbde6d84491210bf8c2442e5de52821524e Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 18 Feb 2019 17:08:41 +0100 Subject: [PATCH 2936/3817] gx publish 0.1.21 This commit was moved from ipfs/go-ipns@00a0e62c4a22e61f1de061f7d0249febce82f09f --- ipns/pb/ipns.pb.go | 111 ++++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 41 deletions(-) diff --git a/ipns/pb/ipns.pb.go b/ipns/pb/ipns.pb.go index 5a6a0bebb..b38ce4ea9 100644 --- a/ipns/pb/ipns.pb.go +++ b/ipns/pb/ipns.pb.go @@ -3,13 +3,13 @@ package ipns_pb -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" - -import io "io" +import ( + fmt "fmt" + github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -32,6 +32,7 @@ const ( var IpnsEntry_ValidityType_name = map[int32]string{ 0: "EOL", } + var IpnsEntry_ValidityType_value = map[string]int32{ "EOL": 0, } @@ -41,9 +42,11 @@ func (x IpnsEntry_ValidityType) Enum() *IpnsEntry_ValidityType { *p = x return p } + func (x IpnsEntry_ValidityType) String() string { return proto.EnumName(IpnsEntry_ValidityType_name, int32(x)) } + func (x *IpnsEntry_ValidityType) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(IpnsEntry_ValidityType_value, data, "IpnsEntry_ValidityType") if err != nil { @@ -52,8 +55,9 @@ func (x *IpnsEntry_ValidityType) UnmarshalJSON(data []byte) error { *x = IpnsEntry_ValidityType(value) return nil } + func (IpnsEntry_ValidityType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_ipns_02f6be73595bcc54, []int{0, 0} + return fileDescriptor_4d5b16fb32bfe8ea, []int{0, 0} } type IpnsEntry struct { @@ -77,7 +81,7 @@ func (m *IpnsEntry) Reset() { *m = IpnsEntry{} } func (m *IpnsEntry) String() string { return proto.CompactTextString(m) } func (*IpnsEntry) ProtoMessage() {} func (*IpnsEntry) Descriptor() ([]byte, []int) { - return fileDescriptor_ipns_02f6be73595bcc54, []int{0} + return fileDescriptor_4d5b16fb32bfe8ea, []int{0} } func (m *IpnsEntry) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -94,8 +98,8 @@ func (m *IpnsEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return b[:n], nil } } -func (dst *IpnsEntry) XXX_Merge(src proto.Message) { - xxx_messageInfo_IpnsEntry.Merge(dst, src) +func (m *IpnsEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_IpnsEntry.Merge(m, src) } func (m *IpnsEntry) XXX_Size() int { return m.Size() @@ -156,9 +160,30 @@ func (m *IpnsEntry) GetPubKey() []byte { } func init() { - proto.RegisterType((*IpnsEntry)(nil), "ipns.pb.IpnsEntry") proto.RegisterEnum("ipns.pb.IpnsEntry_ValidityType", IpnsEntry_ValidityType_name, IpnsEntry_ValidityType_value) + proto.RegisterType((*IpnsEntry)(nil), "ipns.pb.IpnsEntry") +} + +func init() { proto.RegisterFile("ipns.proto", fileDescriptor_4d5b16fb32bfe8ea) } + +var fileDescriptor_4d5b16fb32bfe8ea = []byte{ + // 221 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xca, 0x2c, 0xc8, 0x2b, + 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0xb0, 0x93, 0x94, 0xfe, 0x33, 0x72, 0x71, + 0x7a, 0x16, 0xe4, 0x15, 0xbb, 0xe6, 0x95, 0x14, 0x55, 0x0a, 0x89, 0x70, 0xb1, 0x96, 0x25, 0xe6, + 0x94, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x69, 0xf0, 0x04, 0x41, 0x38, 0x42, 0x32, 0x5c, 0x9c, 0xc5, + 0x99, 0xe9, 0x79, 0x89, 0x25, 0xa5, 0x45, 0xa9, 0x12, 0x4c, 0x60, 0x19, 0x84, 0x80, 0x90, 0x33, + 0x17, 0x4f, 0x59, 0x62, 0x4e, 0x66, 0x4a, 0x66, 0x49, 0x65, 0x48, 0x65, 0x41, 0xaa, 0x04, 0xb3, + 0x02, 0xa3, 0x06, 0x9f, 0x91, 0xbc, 0x1e, 0xd4, 0x06, 0x3d, 0xb8, 0xe9, 0x7a, 0x61, 0x48, 0xca, + 0x82, 0x50, 0x34, 0x09, 0x49, 0x71, 0x71, 0xc0, 0xf8, 0x12, 0x2c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, + 0x70, 0x3e, 0x48, 0xae, 0x38, 0xb5, 0xb0, 0x34, 0x35, 0x2f, 0x39, 0x55, 0x82, 0x55, 0x81, 0x51, + 0x83, 0x25, 0x08, 0xce, 0x17, 0x12, 0xe0, 0x62, 0x2e, 0x29, 0xc9, 0x91, 0x60, 0x03, 0x0b, 0x83, + 0x98, 0x42, 0x62, 0x5c, 0x6c, 0x05, 0xa5, 0x49, 0xde, 0xa9, 0x95, 0x12, 0xec, 0x60, 0x73, 0xa0, + 0x3c, 0x25, 0x71, 0x2e, 0x1e, 0x64, 0xfb, 0x85, 0xd8, 0xb9, 0x98, 0x5d, 0xfd, 0x7d, 0x04, 0x18, + 0x9c, 0x78, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0x46, 0x40, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x32, 0x35, 0xc7, 0xf2, 0x25, 0x01, 0x00, 0x00, } + func (m *IpnsEntry) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -233,6 +258,9 @@ func encodeVarintIpns(dAtA []byte, offset int, v uint64) int { return offset + 1 } func (m *IpnsEntry) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Value != nil { @@ -295,7 +323,7 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -323,7 +351,7 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -332,6 +360,9 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { return ErrInvalidLengthIpns } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthIpns + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -355,7 +386,7 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -364,6 +395,9 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { return ErrInvalidLengthIpns } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthIpns + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -387,7 +421,7 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (IpnsEntry_ValidityType(b) & 0x7F) << shift + v |= IpnsEntry_ValidityType(b&0x7F) << shift if b < 0x80 { break } @@ -407,7 +441,7 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -416,6 +450,9 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { return ErrInvalidLengthIpns } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthIpns + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -438,7 +475,7 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (uint64(b) & 0x7F) << shift + v |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -458,7 +495,7 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (uint64(b) & 0x7F) << shift + v |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -478,7 +515,7 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -487,6 +524,9 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { return ErrInvalidLengthIpns } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthIpns + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -504,6 +544,9 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthIpns } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthIpns + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } @@ -577,10 +620,13 @@ func skipIpns(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthIpns } + iNdEx += length + if iNdEx < 0 { + return 0, ErrInvalidLengthIpns + } return iNdEx, nil case 3: for { @@ -609,6 +655,9 @@ func skipIpns(dAtA []byte) (n int, err error) { return 0, err } iNdEx = start + next + if iNdEx < 0 { + return 0, ErrInvalidLengthIpns + } } return iNdEx, nil case 4: @@ -627,23 +676,3 @@ var ( ErrInvalidLengthIpns = fmt.Errorf("proto: negative length found during unmarshaling") ErrIntOverflowIpns = fmt.Errorf("proto: integer overflow") ) - -func init() { proto.RegisterFile("ipns.proto", fileDescriptor_ipns_02f6be73595bcc54) } - -var fileDescriptor_ipns_02f6be73595bcc54 = []byte{ - // 221 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xca, 0x2c, 0xc8, 0x2b, - 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0xb0, 0x93, 0x94, 0xfe, 0x33, 0x72, 0x71, - 0x7a, 0x16, 0xe4, 0x15, 0xbb, 0xe6, 0x95, 0x14, 0x55, 0x0a, 0x89, 0x70, 0xb1, 0x96, 0x25, 0xe6, - 0x94, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x69, 0xf0, 0x04, 0x41, 0x38, 0x42, 0x32, 0x5c, 0x9c, 0xc5, - 0x99, 0xe9, 0x79, 0x89, 0x25, 0xa5, 0x45, 0xa9, 0x12, 0x4c, 0x60, 0x19, 0x84, 0x80, 0x90, 0x33, - 0x17, 0x4f, 0x59, 0x62, 0x4e, 0x66, 0x4a, 0x66, 0x49, 0x65, 0x48, 0x65, 0x41, 0xaa, 0x04, 0xb3, - 0x02, 0xa3, 0x06, 0x9f, 0x91, 0xbc, 0x1e, 0xd4, 0x06, 0x3d, 0xb8, 0xe9, 0x7a, 0x61, 0x48, 0xca, - 0x82, 0x50, 0x34, 0x09, 0x49, 0x71, 0x71, 0xc0, 0xf8, 0x12, 0x2c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, - 0x70, 0x3e, 0x48, 0xae, 0x38, 0xb5, 0xb0, 0x34, 0x35, 0x2f, 0x39, 0x55, 0x82, 0x55, 0x81, 0x51, - 0x83, 0x25, 0x08, 0xce, 0x17, 0x12, 0xe0, 0x62, 0x2e, 0x29, 0xc9, 0x91, 0x60, 0x03, 0x0b, 0x83, - 0x98, 0x42, 0x62, 0x5c, 0x6c, 0x05, 0xa5, 0x49, 0xde, 0xa9, 0x95, 0x12, 0xec, 0x60, 0x73, 0xa0, - 0x3c, 0x25, 0x71, 0x2e, 0x1e, 0x64, 0xfb, 0x85, 0xd8, 0xb9, 0x98, 0x5d, 0xfd, 0x7d, 0x04, 0x18, - 0x9c, 0x78, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0x46, 0x40, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x32, 0x35, 0xc7, 0xf2, 0x25, 0x01, 0x00, 0x00, -} From c481c99cbed30c5a0f7d72c59f27c1b275da7c6f Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 18 Feb 2019 17:16:50 +0100 Subject: [PATCH 2937/3817] gx publish 1.1.31 This commit was moved from ipfs/go-merkledag@a56cbf989dec0b9b752a47ba4e11463bdb83a821 --- ipld/merkledag/pb/merkledag.pb.go | 1398 +++++++++++++++---------- ipld/merkledag/pb/merkledag.proto | 2 +- ipld/merkledag/pb/merkledagpb_test.go | 385 +++---- ipld/merkledag/pb/stability_test.go | 24 + 4 files changed, 1065 insertions(+), 744 deletions(-) create mode 100644 ipld/merkledag/pb/stability_test.go diff --git a/ipld/merkledag/pb/merkledag.pb.go b/ipld/merkledag/pb/merkledag.pb.go index 811dd27d6..c6fc43608 100644 --- a/ipld/merkledag/pb/merkledag.pb.go +++ b/ipld/merkledag/pb/merkledag.pb.go @@ -1,35 +1,19 @@ -// Code generated by protoc-gen-gogo. +// Code generated by protoc-gen-gogo. DO NOT EDIT. // source: merkledag.proto -// DO NOT EDIT! -/* - Package merkledag_pb is a generated protocol buffer package. - - It is generated from these files: - merkledag.proto - - It has these top-level messages: - PBLink - PBNode -*/ package merkledag_pb -import proto "github.com/gogo/protobuf/proto" -import math "math" - -// discarding unused import gogoproto "code.google.com/p/gogoprotobuf/gogoproto/gogo.pb" - -import io "io" -import fmt "fmt" -import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" +import ( + bytes "bytes" + fmt "fmt" + io "io" + math "math" + reflect "reflect" + strings "strings" -import strings "strings" -import reflect "reflect" - -import sort "sort" -import strconv "strconv" - -import bytes "bytes" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" +) // DoNotUpgradeFileEverItWillChangeYourHashes warns users about not breaking // their file hashes. @@ -42,21 +26,59 @@ Do *not regenerate this file. // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal +var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + // An IPFS MerkleDAG Link type PBLink struct { // multihash of the target object - Hash []byte `protobuf:"bytes,1,opt" json:"Hash,omitempty"` + Hash []byte `protobuf:"bytes,1,opt,name=Hash" json:"Hash,omitempty"` // utf string name. should be unique per object - Name *string `protobuf:"bytes,2,opt" json:"Name,omitempty"` + Name *string `protobuf:"bytes,2,opt,name=Name" json:"Name,omitempty"` // cumulative size of target object - Tsize *uint64 `protobuf:"varint,3,opt" json:"Tsize,omitempty"` - XXX_unrecognized []byte `json:"-"` + Tsize *uint64 `protobuf:"varint,3,opt,name=Tsize" json:"Tsize,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *PBLink) Reset() { *m = PBLink{} } func (*PBLink) ProtoMessage() {} +func (*PBLink) Descriptor() ([]byte, []int) { + return fileDescriptor_10837cc3557cec00, []int{0} +} +func (m *PBLink) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PBLink) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PBLink.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalTo(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PBLink) XXX_Merge(src proto.Message) { + xxx_messageInfo_PBLink.Merge(m, src) +} +func (m *PBLink) XXX_Size() int { + return m.Size() +} +func (m *PBLink) XXX_DiscardUnknown() { + xxx_messageInfo_PBLink.DiscardUnknown(m) +} + +var xxx_messageInfo_PBLink proto.InternalMessageInfo func (m *PBLink) GetHash() []byte { if m != nil { @@ -82,14 +104,45 @@ func (m *PBLink) GetTsize() uint64 { // An IPFS MerkleDAG Node type PBNode struct { // refs to other objects - Links []*PBLink `protobuf:"bytes,2,rep" json:"Links,omitempty"` + Links []*PBLink `protobuf:"bytes,2,rep,name=Links" json:"Links,omitempty"` // opaque user data - Data []byte `protobuf:"bytes,1,opt" json:"Data,omitempty"` - XXX_unrecognized []byte `json:"-"` + Data []byte `protobuf:"bytes,1,opt,name=Data" json:"Data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *PBNode) Reset() { *m = PBNode{} } func (*PBNode) ProtoMessage() {} +func (*PBNode) Descriptor() ([]byte, []int) { + return fileDescriptor_10837cc3557cec00, []int{1} +} +func (m *PBNode) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PBNode) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PBNode.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalTo(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PBNode) XXX_Merge(src proto.Message) { + xxx_messageInfo_PBNode.Merge(m, src) +} +func (m *PBNode) XXX_Size() int { + return m.Size() +} +func (m *PBNode) XXX_DiscardUnknown() { + xxx_messageInfo_PBNode.DiscardUnknown(m) +} + +var xxx_messageInfo_PBNode proto.InternalMessageInfo func (m *PBNode) GetLinks() []*PBLink { if m != nil { @@ -106,282 +159,335 @@ func (m *PBNode) GetData() []byte { } func init() { + proto.RegisterType((*PBLink)(nil), "merkledag.pb.PBLink") + proto.RegisterType((*PBNode)(nil), "merkledag.pb.PBNode") } -func (m *PBLink) Unmarshal(data []byte) error { - l := len(data) - index := 0 - for index < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if index >= l { - return io.ErrUnexpectedEOF - } - b := data[index] - index++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if index >= l { - return io.ErrUnexpectedEOF - } - b := data[index] - index++ - byteLen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - postIndex := index + byteLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Hash = append([]byte{}, data[index:postIndex]...) - index = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if index >= l { - return io.ErrUnexpectedEOF - } - b := data[index] - index++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - postIndex := index + int(stringLen) - if postIndex > l { - return io.ErrUnexpectedEOF - } - s := string(data[index:postIndex]) - m.Name = &s - index = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Tsize", wireType) - } - var v uint64 - for shift := uint(0); ; shift += 7 { - if index >= l { - return io.ErrUnexpectedEOF - } - b := data[index] - index++ - v |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - m.Tsize = &v - default: - var sizeOfWire int - for { - sizeOfWire++ - wire >>= 7 - if wire == 0 { - break - } - } - index -= sizeOfWire - skippy, err := github_com_gogo_protobuf_proto.Skip(data[index:]) - if err != nil { - return err - } - if (index + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, data[index:index+skippy]...) - index += skippy - } - } - return nil + +func init() { proto.RegisterFile("merkledag.proto", fileDescriptor_10837cc3557cec00) } + +var fileDescriptor_10837cc3557cec00 = []byte{ + // 227 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcf, 0x4d, 0x2d, 0xca, + 0xce, 0x49, 0x4d, 0x49, 0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x41, 0x12, 0x48, + 0x92, 0xd2, 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0x4f, + 0xcf, 0xd7, 0x07, 0x2b, 0x4a, 0x2a, 0x4d, 0x03, 0xf3, 0xc0, 0x1c, 0x30, 0x0b, 0xa2, 0x59, 0xc9, + 0x8d, 0x8b, 0x2d, 0xc0, 0xc9, 0x27, 0x33, 0x2f, 0x5b, 0x48, 0x88, 0x8b, 0xc5, 0x23, 0xb1, 0x38, + 0x43, 0x82, 0x51, 0x81, 0x51, 0x83, 0x27, 0x08, 0xcc, 0x06, 0x89, 0xf9, 0x25, 0xe6, 0xa6, 0x4a, + 0x30, 0x29, 0x30, 0x6a, 0x70, 0x06, 0x81, 0xd9, 0x42, 0x22, 0x5c, 0xac, 0x21, 0xc5, 0x99, 0x55, + 0xa9, 0x12, 0xcc, 0x0a, 0x8c, 0x1a, 0x2c, 0x41, 0x10, 0x8e, 0x92, 0x07, 0xc8, 0x1c, 0xbf, 0xfc, + 0x94, 0x54, 0x21, 0x2d, 0x2e, 0x56, 0x90, 0x79, 0xc5, 0x12, 0x4c, 0x0a, 0xcc, 0x1a, 0xdc, 0x46, + 0x22, 0x7a, 0xc8, 0xce, 0xd3, 0x83, 0x58, 0x16, 0x04, 0x51, 0x02, 0x32, 0xdf, 0x25, 0xb1, 0x24, + 0x11, 0x66, 0x27, 0x88, 0xed, 0xa4, 0x73, 0xe3, 0xa1, 0x1c, 0xc3, 0x83, 0x87, 0x72, 0x8c, 0x1f, + 0x1e, 0xca, 0x31, 0xfe, 0x78, 0x28, 0xc7, 0xd8, 0xf0, 0x48, 0x8e, 0x71, 0xc5, 0x23, 0x39, 0xc6, + 0x1d, 0x8f, 0xe4, 0x18, 0x0f, 0x3c, 0x92, 0x63, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, + 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x1d, 0x56, 0xb5, 0x6e, 0x0e, + 0x01, 0x00, 0x00, } -func (m *PBNode) Unmarshal(data []byte) error { - l := len(data) - index := 0 - for index < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if index >= l { - return io.ErrUnexpectedEOF - } - b := data[index] - index++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - switch fieldNum { - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Links", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if index >= l { - return io.ErrUnexpectedEOF - } - b := data[index] - index++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - postIndex := index + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Links = append(m.Links, &PBLink{}) - m.Links[len(m.Links)-1].Unmarshal(data[index:postIndex]) - index = postIndex - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if index >= l { - return io.ErrUnexpectedEOF - } - b := data[index] - index++ - byteLen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - postIndex := index + byteLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Data = append([]byte{}, data[index:postIndex]...) - index = postIndex - default: - var sizeOfWire int - for { - sizeOfWire++ - wire >>= 7 - if wire == 0 { - break - } - } - index -= sizeOfWire - skippy, err := github_com_gogo_protobuf_proto.Skip(data[index:]) - if err != nil { - return err - } - if (index + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, data[index:index+skippy]...) - index += skippy + +func (this *PBLink) VerboseEqual(that interface{}) error { + if that == nil { + if this == nil { + return nil } + return fmt.Errorf("that == nil && this != nil") } - return nil -} -func (this *PBLink) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&PBLink{`, - `Hash:` + valueToStringMerkledag(this.Hash) + `,`, - `Name:` + valueToStringMerkledag(this.Name) + `,`, - `Tsize:` + valueToStringMerkledag(this.Tsize) + `,`, - `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, - `}`, - }, "") - return s -} -func (this *PBNode) String() string { - if this == nil { - return "nil" + + that1, ok := that.(*PBLink) + if !ok { + that2, ok := that.(PBLink) + if ok { + that1 = &that2 + } else { + return fmt.Errorf("that is not of type *PBLink") + } } - s := strings.Join([]string{`&PBNode{`, - `Links:` + strings.Replace(fmt.Sprintf("%v", this.Links), "PBLink", "PBLink", 1) + `,`, - `Data:` + valueToStringMerkledag(this.Data) + `,`, - `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, - `}`, - }, "") - return s -} -func valueToStringMerkledag(v interface{}) string { - rv := reflect.ValueOf(v) - if rv.IsNil() { - return "nil" + if that1 == nil { + if this == nil { + return nil + } + return fmt.Errorf("that is type *PBLink but is nil && this != nil") + } else if this == nil { + return fmt.Errorf("that is type *PBLink but is not nil && this == nil") } - pv := reflect.Indirect(rv).Interface() - return fmt.Sprintf("*%v", pv) -} -func (m *PBLink) Size() (n int) { - var l int - _ = l - if m.Hash != nil { - l = len(m.Hash) - n += 1 + l + sovMerkledag(uint64(l)) + if !bytes.Equal(this.Hash, that1.Hash) { + return fmt.Errorf("Hash this(%v) Not Equal that(%v)", this.Hash, that1.Hash) } - if m.Name != nil { - l = len(*m.Name) - n += 1 + l + sovMerkledag(uint64(l)) + if this.Name != nil && that1.Name != nil { + if *this.Name != *that1.Name { + return fmt.Errorf("Name this(%v) Not Equal that(%v)", *this.Name, *that1.Name) + } + } else if this.Name != nil { + return fmt.Errorf("this.Name == nil && that.Name != nil") + } else if that1.Name != nil { + return fmt.Errorf("Name this(%v) Not Equal that(%v)", this.Name, that1.Name) } - if m.Tsize != nil { - n += 1 + sovMerkledag(uint64(*m.Tsize)) + if this.Tsize != nil && that1.Tsize != nil { + if *this.Tsize != *that1.Tsize { + return fmt.Errorf("Tsize this(%v) Not Equal that(%v)", *this.Tsize, *that1.Tsize) + } + } else if this.Tsize != nil { + return fmt.Errorf("this.Tsize == nil && that.Tsize != nil") + } else if that1.Tsize != nil { + return fmt.Errorf("Tsize this(%v) Not Equal that(%v)", this.Tsize, that1.Tsize) } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return fmt.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) } - return n + return nil } +func (this *PBLink) Equal(that interface{}) bool { + if that == nil { + return this == nil + } -func (m *PBNode) Size() (n int) { - var l int - _ = l - if len(m.Links) > 0 { - for _, e := range m.Links { - l = e.Size() - n += 1 + l + sovMerkledag(uint64(l)) + that1, ok := that.(*PBLink) + if !ok { + that2, ok := that.(PBLink) + if ok { + that1 = &that2 + } else { + return false } } - if m.Data != nil { - l = len(m.Data) - n += 1 + l + sovMerkledag(uint64(l)) + if that1 == nil { + return this == nil + } else if this == nil { + return false } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) + if !bytes.Equal(this.Hash, that1.Hash) { + return false } - return n + if this.Name != nil && that1.Name != nil { + if *this.Name != *that1.Name { + return false + } + } else if this.Name != nil { + return false + } else if that1.Name != nil { + return false + } + if this.Tsize != nil && that1.Tsize != nil { + if *this.Tsize != *that1.Tsize { + return false + } + } else if this.Tsize != nil { + return false + } else if that1.Tsize != nil { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true } +func (this *PBNode) VerboseEqual(that interface{}) error { + if that == nil { + if this == nil { + return nil + } + return fmt.Errorf("that == nil && this != nil") + } -func sovMerkledag(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break + that1, ok := that.(*PBNode) + if !ok { + that2, ok := that.(PBNode) + if ok { + that1 = &that2 + } else { + return fmt.Errorf("that is not of type *PBNode") } } - return n + if that1 == nil { + if this == nil { + return nil + } + return fmt.Errorf("that is type *PBNode but is nil && this != nil") + } else if this == nil { + return fmt.Errorf("that is type *PBNode but is not nil && this == nil") + } + if len(this.Links) != len(that1.Links) { + return fmt.Errorf("Links this(%v) Not Equal that(%v)", len(this.Links), len(that1.Links)) + } + for i := range this.Links { + if !this.Links[i].Equal(that1.Links[i]) { + return fmt.Errorf("Links this[%v](%v) Not Equal that[%v](%v)", i, this.Links[i], i, that1.Links[i]) + } + } + if !bytes.Equal(this.Data, that1.Data) { + return fmt.Errorf("Data this(%v) Not Equal that(%v)", this.Data, that1.Data) + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return fmt.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) + } + return nil } -func sozMerkledag(x uint64) (n int) { - return sovMerkledag(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +func (this *PBNode) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*PBNode) + if !ok { + that2, ok := that.(PBNode) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.Links) != len(that1.Links) { + return false + } + for i := range this.Links { + if !this.Links[i].Equal(that1.Links[i]) { + return false + } + } + if !bytes.Equal(this.Data, that1.Data) { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *PBLink) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 7) + s = append(s, "&merkledag_pb.PBLink{") + if this.Hash != nil { + s = append(s, "Hash: "+valueToGoStringMerkledag(this.Hash, "byte")+",\n") + } + if this.Name != nil { + s = append(s, "Name: "+valueToGoStringMerkledag(this.Name, "string")+",\n") + } + if this.Tsize != nil { + s = append(s, "Tsize: "+valueToGoStringMerkledag(this.Tsize, "uint64")+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *PBNode) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 6) + s = append(s, "&merkledag_pb.PBNode{") + if this.Links != nil { + s = append(s, "Links: "+fmt.Sprintf("%#v", this.Links)+",\n") + } + if this.Data != nil { + s = append(s, "Data: "+valueToGoStringMerkledag(this.Data, "byte")+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func valueToGoStringMerkledag(v interface{}, typ string) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) +} +func (m *PBLink) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PBLink) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Hash != nil { + dAtA[i] = 0xa + i++ + i = encodeVarintMerkledag(dAtA, i, uint64(len(m.Hash))) + i += copy(dAtA[i:], m.Hash) + } + if m.Name != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintMerkledag(dAtA, i, uint64(len(*m.Name))) + i += copy(dAtA[i:], *m.Name) + } + if m.Tsize != nil { + dAtA[i] = 0x18 + i++ + i = encodeVarintMerkledag(dAtA, i, uint64(*m.Tsize)) + } + if m.XXX_unrecognized != nil { + i += copy(dAtA[i:], m.XXX_unrecognized) + } + return i, nil +} + +func (m *PBNode) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PBNode) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Links) > 0 { + for _, msg := range m.Links { + dAtA[i] = 0x12 + i++ + i = encodeVarintMerkledag(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if m.Data != nil { + dAtA[i] = 0xa + i++ + i = encodeVarintMerkledag(dAtA, i, uint64(len(m.Data))) + i += copy(dAtA[i:], m.Data) + } + if m.XXX_unrecognized != nil { + i += copy(dAtA[i:], m.XXX_unrecognized) + } + return i, nil +} + +func encodeVarintMerkledag(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 } func NewPopulatedPBLink(r randyMerkledag, easy bool) *PBLink { this := &PBLink{} @@ -393,11 +499,11 @@ func NewPopulatedPBLink(r randyMerkledag, easy bool) *PBLink { } } if r.Intn(10) != 0 { - v2 := randStringMerkledag(r) + v2 := string(randStringMerkledag(r)) this.Name = &v2 } if r.Intn(10) != 0 { - v3 := uint64(r.Uint32()) + v3 := uint64(uint64(r.Uint32())) this.Tsize = &v3 } if !easy && r.Intn(10) != 0 { @@ -409,17 +515,17 @@ func NewPopulatedPBLink(r randyMerkledag, easy bool) *PBLink { func NewPopulatedPBNode(r randyMerkledag, easy bool) *PBNode { this := &PBNode{} if r.Intn(10) != 0 { - v4 := r.Intn(10) - this.Links = make([]*PBLink, v4) + v4 := r.Intn(100) + this.Data = make([]byte, v4) for i := 0; i < v4; i++ { - this.Links[i] = NewPopulatedPBLink(r, easy) + this.Data[i] = byte(r.Intn(256)) } } if r.Intn(10) != 0 { - v5 := r.Intn(100) - this.Data = make([]byte, v5) + v5 := r.Intn(5) + this.Links = make([]*PBLink, v5) for i := 0; i < v5; i++ { - this.Data[i] = byte(r.Intn(256)) + this.Links[i] = NewPopulatedPBLink(r, easy) } } if !easy && r.Intn(10) != 0 { @@ -438,7 +544,13 @@ type randyMerkledag interface { } func randUTF8RuneMerkledag(r randyMerkledag) rune { - return rune(r.Intn(126-43) + 43) + ru := r.Intn(62) + if ru < 10 { + return rune(ru + 48) + } else if ru < 36 { + return rune(ru + 55) + } + return rune(ru + 61) } func randStringMerkledag(r randyMerkledag) string { v6 := r.Intn(100) @@ -448,7 +560,7 @@ func randStringMerkledag(r randyMerkledag) string { } return string(tmps) } -func randUnrecognizedMerkledag(r randyMerkledag, maxFieldNumber int) (data []byte) { +func randUnrecognizedMerkledag(r randyMerkledag, maxFieldNumber int) (dAtA []byte) { l := r.Intn(5) for i := 0; i < l; i++ { wire := r.Intn(4) @@ -456,355 +568,505 @@ func randUnrecognizedMerkledag(r randyMerkledag, maxFieldNumber int) (data []byt wire = 5 } fieldNumber := maxFieldNumber + r.Intn(100) - data = randFieldMerkledag(data, r, fieldNumber, wire) + dAtA = randFieldMerkledag(dAtA, r, fieldNumber, wire) } - return data + return dAtA } -func randFieldMerkledag(data []byte, r randyMerkledag, fieldNumber int, wire int) []byte { +func randFieldMerkledag(dAtA []byte, r randyMerkledag, fieldNumber int, wire int) []byte { key := uint32(fieldNumber)<<3 | uint32(wire) switch wire { case 0: - data = encodeVarintPopulateMerkledag(data, uint64(key)) + dAtA = encodeVarintPopulateMerkledag(dAtA, uint64(key)) v7 := r.Int63() if r.Intn(2) == 0 { v7 *= -1 } - data = encodeVarintPopulateMerkledag(data, uint64(v7)) + dAtA = encodeVarintPopulateMerkledag(dAtA, uint64(v7)) case 1: - data = encodeVarintPopulateMerkledag(data, uint64(key)) - data = append(data, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) + dAtA = encodeVarintPopulateMerkledag(dAtA, uint64(key)) + dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) case 2: - data = encodeVarintPopulateMerkledag(data, uint64(key)) + dAtA = encodeVarintPopulateMerkledag(dAtA, uint64(key)) ll := r.Intn(100) - data = encodeVarintPopulateMerkledag(data, uint64(ll)) + dAtA = encodeVarintPopulateMerkledag(dAtA, uint64(ll)) for j := 0; j < ll; j++ { - data = append(data, byte(r.Intn(256))) + dAtA = append(dAtA, byte(r.Intn(256))) } default: - data = encodeVarintPopulateMerkledag(data, uint64(key)) - data = append(data, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) + dAtA = encodeVarintPopulateMerkledag(dAtA, uint64(key)) + dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) } - return data + return dAtA } -func encodeVarintPopulateMerkledag(data []byte, v uint64) []byte { +func encodeVarintPopulateMerkledag(dAtA []byte, v uint64) []byte { for v >= 1<<7 { - data = append(data, uint8(uint64(v)&0x7f|0x80)) + dAtA = append(dAtA, uint8(uint64(v)&0x7f|0x80)) v >>= 7 } - data = append(data, uint8(v)) - return data + dAtA = append(dAtA, uint8(v)) + return dAtA } -func (m *PBLink) Marshal() (data []byte, err error) { - size := m.Size() - data = make([]byte, size) - n, err := m.MarshalTo(data) - if err != nil { - return nil, err +func (m *PBLink) Size() (n int) { + if m == nil { + return 0 } - return data[:n], nil -} - -func (m *PBLink) MarshalTo(data []byte) (n int, err error) { - var i int - _ = i var l int _ = l if m.Hash != nil { - data[i] = 0xa - i++ - i = encodeVarintMerkledag(data, i, uint64(len(m.Hash))) - i += copy(data[i:], m.Hash) + l = len(m.Hash) + n += 1 + l + sovMerkledag(uint64(l)) } if m.Name != nil { - data[i] = 0x12 - i++ - i = encodeVarintMerkledag(data, i, uint64(len(*m.Name))) - i += copy(data[i:], *m.Name) + l = len(*m.Name) + n += 1 + l + sovMerkledag(uint64(l)) } if m.Tsize != nil { - data[i] = 0x18 - i++ - i = encodeVarintMerkledag(data, i, uint64(*m.Tsize)) + n += 1 + sovMerkledag(uint64(*m.Tsize)) } if m.XXX_unrecognized != nil { - i += copy(data[i:], m.XXX_unrecognized) + n += len(m.XXX_unrecognized) } - return i, nil + return n } -func (m *PBNode) Marshal() (data []byte, err error) { - size := m.Size() - data = make([]byte, size) - n, err := m.MarshalTo(data) - if err != nil { - return nil, err +func (m *PBNode) Size() (n int) { + if m == nil { + return 0 } - return data[:n], nil -} - -func (m *PBNode) MarshalTo(data []byte) (n int, err error) { - var i int - _ = i var l int _ = l + if m.Data != nil { + l = len(m.Data) + n += 1 + l + sovMerkledag(uint64(l)) + } if len(m.Links) > 0 { - for _, msg := range m.Links { - data[i] = 0x12 - i++ - i = encodeVarintMerkledag(data, i, uint64(msg.Size())) - n, err := msg.MarshalTo(data[i:]) - if err != nil { - return 0, err - } - i += n + for _, e := range m.Links { + l = e.Size() + n += 1 + l + sovMerkledag(uint64(l)) } } - if m.Data != nil { - data[i] = 0xa - i++ - i = encodeVarintMerkledag(data, i, uint64(len(m.Data))) - i += copy(data[i:], m.Data) - } if m.XXX_unrecognized != nil { - i += copy(data[i:], m.XXX_unrecognized) + n += len(m.XXX_unrecognized) } - return i, nil + return n } -func encodeFixed64Merkledag(data []byte, offset int, v uint64) int { - data[offset] = uint8(v) - data[offset+1] = uint8(v >> 8) - data[offset+2] = uint8(v >> 16) - data[offset+3] = uint8(v >> 24) - data[offset+4] = uint8(v >> 32) - data[offset+5] = uint8(v >> 40) - data[offset+6] = uint8(v >> 48) - data[offset+7] = uint8(v >> 56) - return offset + 8 -} -func encodeFixed32Merkledag(data []byte, offset int, v uint32) int { - data[offset] = uint8(v) - data[offset+1] = uint8(v >> 8) - data[offset+2] = uint8(v >> 16) - data[offset+3] = uint8(v >> 24) - return offset + 4 -} -func encodeVarintMerkledag(data []byte, offset int, v uint64) int { - for v >= 1<<7 { - data[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ +func sovMerkledag(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } } - data[offset] = uint8(v) - return offset + 1 + return n } -func (this *PBLink) GoString() string { +func sozMerkledag(x uint64) (n int) { + return sovMerkledag(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *PBLink) String() string { if this == nil { return "nil" } - s := strings.Join([]string{`&merkledag_pb.PBLink{` + - `Hash:` + valueToGoStringMerkledag(this.Hash, "byte"), - `Name:` + valueToGoStringMerkledag(this.Name, "string"), - `Tsize:` + valueToGoStringMerkledag(this.Tsize, "uint64"), - `XXX_unrecognized:` + fmt.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") + s := strings.Join([]string{`&PBLink{`, + `Hash:` + valueToStringMerkledag(this.Hash) + `,`, + `Name:` + valueToStringMerkledag(this.Name) + `,`, + `Tsize:` + valueToStringMerkledag(this.Tsize) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") return s } -func (this *PBNode) GoString() string { +func (this *PBNode) String() string { if this == nil { return "nil" } - s := strings.Join([]string{`&merkledag_pb.PBNode{` + - `Links:` + fmt.Sprintf("%#v", this.Links), - `Data:` + valueToGoStringMerkledag(this.Data, "byte"), - `XXX_unrecognized:` + fmt.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ") + s := strings.Join([]string{`&PBNode{`, + `Data:` + valueToStringMerkledag(this.Data) + `,`, + `Links:` + strings.Replace(fmt.Sprintf("%v", this.Links), "PBLink", "PBLink", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") return s } -func valueToGoStringMerkledag(v interface{}, typ string) string { +func valueToStringMerkledag(v interface{}) string { rv := reflect.ValueOf(v) if rv.IsNil() { return "nil" } pv := reflect.Indirect(rv).Interface() - return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) -} -func extensionToGoStringMerkledag(e map[int32]github_com_gogo_protobuf_proto.Extension) string { - if e == nil { - return "nil" - } - s := "map[int32]proto.Extension{" - keys := make([]int, 0, len(e)) - for k := range e { - keys = append(keys, int(k)) - } - sort.Ints(keys) - ss := []string{} - for _, k := range keys { - ss = append(ss, strconv.Itoa(k)+": "+e[int32(k)].GoString()) - } - s += strings.Join(ss, ",") + "}" - return s + return fmt.Sprintf("*%v", pv) } -func (this *PBLink) VerboseEqual(that interface{}) error { - if that == nil { - if this == nil { - return nil +func (m *PBLink) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMerkledag + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } } - return fmt.Errorf("that == nil && this != nil") - } - - that1, ok := that.(*PBLink) - if !ok { - return fmt.Errorf("that is not of type *PBLink") - } - if that1 == nil { - if this == nil { - return nil + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PBLink: wiretype end group for non-group") } - return fmt.Errorf("that is type *PBLink but is nil && this != nil") - } else if this == nil { - return fmt.Errorf("that is type *PBLinkbut is not nil && this == nil") - } - if !bytes.Equal(this.Hash, that1.Hash) { - return fmt.Errorf("Hash this(%v) Not Equal that(%v)", this.Hash, that1.Hash) - } - if this.Name != nil && that1.Name != nil { - if *this.Name != *that1.Name { - return fmt.Errorf("Name this(%v) Not Equal that(%v)", *this.Name, *that1.Name) + if fieldNum <= 0 { + return fmt.Errorf("proto: PBLink: illegal tag %d (wire type %d)", fieldNum, wire) } - } else if this.Name != nil { - return fmt.Errorf("this.Name == nil && that.Name != nil") - } else if that1.Name != nil { - return fmt.Errorf("Name this(%v) Not Equal that(%v)", this.Name, that1.Name) - } - if this.Tsize != nil && that1.Tsize != nil { - if *this.Tsize != *that1.Tsize { - return fmt.Errorf("Tsize this(%v) Not Equal that(%v)", *this.Tsize, *that1.Tsize) + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMerkledag + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthMerkledag + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthMerkledag + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hash = append(m.Hash[:0], dAtA[iNdEx:postIndex]...) + if m.Hash == nil { + m.Hash = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMerkledag + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMerkledag + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMerkledag + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Name = &s + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Tsize", wireType) + } + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMerkledag + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Tsize = &v + default: + iNdEx = preIndex + skippy, err := skipMerkledag(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMerkledag + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthMerkledag + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy } - } else if this.Tsize != nil { - return fmt.Errorf("this.Tsize == nil && that.Tsize != nil") - } else if that1.Tsize != nil { - return fmt.Errorf("Tsize this(%v) Not Equal that(%v)", this.Tsize, that1.Tsize) } - if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { - return fmt.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) + + if iNdEx > l { + return io.ErrUnexpectedEOF } return nil } -func (this *PBLink) Equal(that interface{}) bool { - if that == nil { - if this == nil { - return true - } - return false - } - - that1, ok := that.(*PBLink) - if !ok { - return false - } - if that1 == nil { - if this == nil { - return true +func (m *PBNode) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMerkledag + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } } - return false - } else if this == nil { - return false - } - if !bytes.Equal(this.Hash, that1.Hash) { - return false - } - if this.Name != nil && that1.Name != nil { - if *this.Name != *that1.Name { - return false + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PBNode: wiretype end group for non-group") } - } else if this.Name != nil { - return false - } else if that1.Name != nil { - return false - } - if this.Tsize != nil && that1.Tsize != nil { - if *this.Tsize != *that1.Tsize { - return false + if fieldNum <= 0 { + return fmt.Errorf("proto: PBNode: illegal tag %d (wire type %d)", fieldNum, wire) } - } else if this.Tsize != nil { - return false - } else if that1.Tsize != nil { - return false - } - if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { - return false - } - return true -} -func (this *PBNode) VerboseEqual(that interface{}) error { - if that == nil { - if this == nil { - return nil + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMerkledag + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthMerkledag + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthMerkledag + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Links", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMerkledag + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMerkledag + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMerkledag + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Links = append(m.Links, &PBLink{}) + if err := m.Links[len(m.Links)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMerkledag(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMerkledag + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthMerkledag + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy } - return fmt.Errorf("that == nil && this != nil") } - that1, ok := that.(*PBNode) - if !ok { - return fmt.Errorf("that is not of type *PBNode") - } - if that1 == nil { - if this == nil { - return nil - } - return fmt.Errorf("that is type *PBNode but is nil && this != nil") - } else if this == nil { - return fmt.Errorf("that is type *PBNodebut is not nil && this == nil") - } - if len(this.Links) != len(that1.Links) { - return fmt.Errorf("Links this(%v) Not Equal that(%v)", len(this.Links), len(that1.Links)) - } - for i := range this.Links { - if !this.Links[i].Equal(that1.Links[i]) { - return fmt.Errorf("Links this[%v](%v) Not Equal that[%v](%v)", i, this.Links[i], i, that1.Links[i]) - } - } - if !bytes.Equal(this.Data, that1.Data) { - return fmt.Errorf("Data this(%v) Not Equal that(%v)", this.Data, that1.Data) - } - if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { - return fmt.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) + if iNdEx > l { + return io.ErrUnexpectedEOF } return nil } -func (this *PBNode) Equal(that interface{}) bool { - if that == nil { - if this == nil { - return true - } - return false - } - - that1, ok := that.(*PBNode) - if !ok { - return false - } - if that1 == nil { - if this == nil { - return true +func skipMerkledag(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMerkledag + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } } - return false - } else if this == nil { - return false - } - if len(this.Links) != len(that1.Links) { - return false - } - for i := range this.Links { - if !this.Links[i].Equal(that1.Links[i]) { - return false + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMerkledag + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMerkledag + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthMerkledag + } + iNdEx += length + if iNdEx < 0 { + return 0, ErrInvalidLengthMerkledag + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMerkledag + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipMerkledag(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + if iNdEx < 0 { + return 0, ErrInvalidLengthMerkledag + } + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } } - if !bytes.Equal(this.Data, that1.Data) { - return false - } - if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { - return false - } - return true + panic("unreachable") } + +var ( + ErrInvalidLengthMerkledag = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMerkledag = fmt.Errorf("proto: integer overflow") +) diff --git a/ipld/merkledag/pb/merkledag.proto b/ipld/merkledag/pb/merkledag.proto index d0d47f5a3..012195901 100644 --- a/ipld/merkledag/pb/merkledag.proto +++ b/ipld/merkledag/pb/merkledag.proto @@ -1,6 +1,6 @@ package merkledag.pb; -import "code.google.com/p/gogoprotobuf/gogoproto/gogo.proto"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; option (gogoproto.gostring_all) = true; option (gogoproto.equal_all) = true; diff --git a/ipld/merkledag/pb/merkledagpb_test.go b/ipld/merkledag/pb/merkledagpb_test.go index 8da923423..20fe06bd8 100644 --- a/ipld/merkledag/pb/merkledagpb_test.go +++ b/ipld/merkledag/pb/merkledagpb_test.go @@ -1,73 +1,85 @@ -// Code generated by protoc-gen-gogo. +// Code generated by protoc-gen-gogo. DO NOT EDIT. // source: merkledag.proto -// DO NOT EDIT! -/* -Package merkledag_pb is a generated protocol buffer package. - -It is generated from these files: - merkledag.proto - -It has these top-level messages: - PBLink - PBNode -*/ package merkledag_pb -import testing "testing" -import math_rand "math/rand" -import time "time" -import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" -import encoding_json "encoding/json" -import fmt "fmt" -import go_parser "go/parser" +import ( + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + github_com_gogo_protobuf_jsonpb "github.com/gogo/protobuf/jsonpb" + github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" + proto "github.com/gogo/protobuf/proto" + go_parser "go/parser" + math "math" + math_rand "math/rand" + testing "testing" + time "time" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf func TestPBLinkProto(t *testing.T) { - popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) p := NewPopulatedPBLink(popr, false) - data, err := github_com_gogo_protobuf_proto.Marshal(p) + dAtA, err := github_com_gogo_protobuf_proto.Marshal(p) if err != nil { - panic(err) + t.Fatalf("seed = %d, err = %v", seed, err) } msg := &PBLink{} - if err := github_com_gogo_protobuf_proto.Unmarshal(data, msg); err != nil { - panic(err) + if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) } - for i := range data { - data[i] = byte(popr.Intn(256)) + littlefuzz := make([]byte, len(dAtA)) + copy(littlefuzz, dAtA) + for i := range dAtA { + dAtA[i] = byte(popr.Intn(256)) } if err := p.VerboseEqual(msg); err != nil { - t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err) } if !p.Equal(msg) { - t.Fatalf("%#v !Proto %#v", msg, p) + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) + } + if len(littlefuzz) > 0 { + fuzzamount := 100 + for i := 0; i < fuzzamount; i++ { + littlefuzz[popr.Intn(len(littlefuzz))] = byte(popr.Intn(256)) + littlefuzz = append(littlefuzz, byte(popr.Intn(256))) + } + // shouldn't panic + _ = github_com_gogo_protobuf_proto.Unmarshal(littlefuzz, msg) } } func TestPBLinkMarshalTo(t *testing.T) { - popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) p := NewPopulatedPBLink(popr, false) size := p.Size() - data := make([]byte, size) - for i := range data { - data[i] = byte(popr.Intn(256)) + dAtA := make([]byte, size) + for i := range dAtA { + dAtA[i] = byte(popr.Intn(256)) } - _, err := p.MarshalTo(data) + _, err := p.MarshalTo(dAtA) if err != nil { - panic(err) + t.Fatalf("seed = %d, err = %v", seed, err) } msg := &PBLink{} - if err := github_com_gogo_protobuf_proto.Unmarshal(data, msg); err != nil { - panic(err) + if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) } - for i := range data { - data[i] = byte(popr.Intn(256)) + for i := range dAtA { + dAtA[i] = byte(popr.Intn(256)) } if err := p.VerboseEqual(msg); err != nil { - t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err) } if !p.Equal(msg) { - t.Fatalf("%#v !Proto %#v", msg, p) + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) } } @@ -80,11 +92,11 @@ func BenchmarkPBLinkProtoMarshal(b *testing.B) { } b.ResetTimer() for i := 0; i < b.N; i++ { - data, err := github_com_gogo_protobuf_proto.Marshal(pops[i%10000]) + dAtA, err := github_com_gogo_protobuf_proto.Marshal(pops[i%10000]) if err != nil { panic(err) } - total += len(data) + total += len(dAtA) } b.SetBytes(int64(total / b.N)) } @@ -94,11 +106,11 @@ func BenchmarkPBLinkProtoUnmarshal(b *testing.B) { total := 0 datas := make([][]byte, 10000) for i := 0; i < 10000; i++ { - data, err := github_com_gogo_protobuf_proto.Marshal(NewPopulatedPBLink(popr, false)) + dAtA, err := github_com_gogo_protobuf_proto.Marshal(NewPopulatedPBLink(popr, false)) if err != nil { panic(err) } - datas[i] = data + datas[i] = dAtA } msg := &PBLink{} b.ResetTimer() @@ -112,51 +124,64 @@ func BenchmarkPBLinkProtoUnmarshal(b *testing.B) { } func TestPBNodeProto(t *testing.T) { - popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) p := NewPopulatedPBNode(popr, false) - data, err := github_com_gogo_protobuf_proto.Marshal(p) + dAtA, err := github_com_gogo_protobuf_proto.Marshal(p) if err != nil { - panic(err) + t.Fatalf("seed = %d, err = %v", seed, err) } msg := &PBNode{} - if err := github_com_gogo_protobuf_proto.Unmarshal(data, msg); err != nil { - panic(err) + if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) } - for i := range data { - data[i] = byte(popr.Intn(256)) + littlefuzz := make([]byte, len(dAtA)) + copy(littlefuzz, dAtA) + for i := range dAtA { + dAtA[i] = byte(popr.Intn(256)) } if err := p.VerboseEqual(msg); err != nil { - t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err) } if !p.Equal(msg) { - t.Fatalf("%#v !Proto %#v", msg, p) + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) + } + if len(littlefuzz) > 0 { + fuzzamount := 100 + for i := 0; i < fuzzamount; i++ { + littlefuzz[popr.Intn(len(littlefuzz))] = byte(popr.Intn(256)) + littlefuzz = append(littlefuzz, byte(popr.Intn(256))) + } + // shouldn't panic + _ = github_com_gogo_protobuf_proto.Unmarshal(littlefuzz, msg) } } func TestPBNodeMarshalTo(t *testing.T) { - popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) p := NewPopulatedPBNode(popr, false) size := p.Size() - data := make([]byte, size) - for i := range data { - data[i] = byte(popr.Intn(256)) + dAtA := make([]byte, size) + for i := range dAtA { + dAtA[i] = byte(popr.Intn(256)) } - _, err := p.MarshalTo(data) + _, err := p.MarshalTo(dAtA) if err != nil { - panic(err) + t.Fatalf("seed = %d, err = %v", seed, err) } msg := &PBNode{} - if err := github_com_gogo_protobuf_proto.Unmarshal(data, msg); err != nil { - panic(err) + if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) } - for i := range data { - data[i] = byte(popr.Intn(256)) + for i := range dAtA { + dAtA[i] = byte(popr.Intn(256)) } if err := p.VerboseEqual(msg); err != nil { - t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err) } if !p.Equal(msg) { - t.Fatalf("%#v !Proto %#v", msg, p) + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) } } @@ -169,11 +194,11 @@ func BenchmarkPBNodeProtoMarshal(b *testing.B) { } b.ResetTimer() for i := 0; i < b.N; i++ { - data, err := github_com_gogo_protobuf_proto.Marshal(pops[i%10000]) + dAtA, err := github_com_gogo_protobuf_proto.Marshal(pops[i%10000]) if err != nil { panic(err) } - total += len(data) + total += len(dAtA) } b.SetBytes(int64(total / b.N)) } @@ -183,11 +208,11 @@ func BenchmarkPBNodeProtoUnmarshal(b *testing.B) { total := 0 datas := make([][]byte, 10000) for i := 0; i < 10000; i++ { - data, err := github_com_gogo_protobuf_proto.Marshal(NewPopulatedPBNode(popr, false)) + dAtA, err := github_com_gogo_protobuf_proto.Marshal(NewPopulatedPBNode(popr, false)) if err != nil { panic(err) } - datas[i] = data + datas[i] = dAtA } msg := &PBNode{} b.ResetTimer() @@ -201,143 +226,190 @@ func BenchmarkPBNodeProtoUnmarshal(b *testing.B) { } func TestPBLinkJSON(t *testing.T) { - popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) p := NewPopulatedPBLink(popr, true) - jsondata, err := encoding_json.Marshal(p) + marshaler := github_com_gogo_protobuf_jsonpb.Marshaler{} + jsondata, err := marshaler.MarshalToString(p) if err != nil { - panic(err) + t.Fatalf("seed = %d, err = %v", seed, err) } msg := &PBLink{} - err = encoding_json.Unmarshal(jsondata, msg) + err = github_com_gogo_protobuf_jsonpb.UnmarshalString(jsondata, msg) if err != nil { - panic(err) + t.Fatalf("seed = %d, err = %v", seed, err) } if err := p.VerboseEqual(msg); err != nil { - t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err) } if !p.Equal(msg) { - t.Fatalf("%#v !Json Equal %#v", msg, p) + t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p) } } func TestPBNodeJSON(t *testing.T) { - popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) p := NewPopulatedPBNode(popr, true) - jsondata, err := encoding_json.Marshal(p) + marshaler := github_com_gogo_protobuf_jsonpb.Marshaler{} + jsondata, err := marshaler.MarshalToString(p) if err != nil { - panic(err) + t.Fatalf("seed = %d, err = %v", seed, err) } msg := &PBNode{} - err = encoding_json.Unmarshal(jsondata, msg) + err = github_com_gogo_protobuf_jsonpb.UnmarshalString(jsondata, msg) if err != nil { - panic(err) + t.Fatalf("seed = %d, err = %v", seed, err) } if err := p.VerboseEqual(msg); err != nil { - t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err) } if !p.Equal(msg) { - t.Fatalf("%#v !Json Equal %#v", msg, p) + t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p) } } func TestPBLinkProtoText(t *testing.T) { - popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) p := NewPopulatedPBLink(popr, true) - data := github_com_gogo_protobuf_proto.MarshalTextString(p) + dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p) msg := &PBLink{} - if err := github_com_gogo_protobuf_proto.UnmarshalText(data, msg); err != nil { - panic(err) + if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) } if err := p.VerboseEqual(msg); err != nil { - t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err) } if !p.Equal(msg) { - t.Fatalf("%#v !Proto %#v", msg, p) + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) } } func TestPBLinkProtoCompactText(t *testing.T) { - popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) p := NewPopulatedPBLink(popr, true) - data := github_com_gogo_protobuf_proto.CompactTextString(p) + dAtA := github_com_gogo_protobuf_proto.CompactTextString(p) msg := &PBLink{} - if err := github_com_gogo_protobuf_proto.UnmarshalText(data, msg); err != nil { - panic(err) + if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) } if err := p.VerboseEqual(msg); err != nil { - t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err) } if !p.Equal(msg) { - t.Fatalf("%#v !Proto %#v", msg, p) + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) } } func TestPBNodeProtoText(t *testing.T) { - popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) p := NewPopulatedPBNode(popr, true) - data := github_com_gogo_protobuf_proto.MarshalTextString(p) + dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p) msg := &PBNode{} - if err := github_com_gogo_protobuf_proto.UnmarshalText(data, msg); err != nil { - panic(err) + if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) } if err := p.VerboseEqual(msg); err != nil { - t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err) } if !p.Equal(msg) { - t.Fatalf("%#v !Proto %#v", msg, p) + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) } } func TestPBNodeProtoCompactText(t *testing.T) { - popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) p := NewPopulatedPBNode(popr, true) - data := github_com_gogo_protobuf_proto.CompactTextString(p) + dAtA := github_com_gogo_protobuf_proto.CompactTextString(p) msg := &PBNode{} - if err := github_com_gogo_protobuf_proto.UnmarshalText(data, msg); err != nil { - panic(err) + if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) } if err := p.VerboseEqual(msg); err != nil { - t.Fatalf("%#v !VerboseProto %#v, since %v", msg, p, err) + t.Fatalf("seed = %d, %#v !VerboseProto %#v, since %v", seed, msg, p, err) } if !p.Equal(msg) { - t.Fatalf("%#v !Proto %#v", msg, p) + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) } } -func TestPBLinkStringer(t *testing.T) { +func TestPBLinkVerboseEqual(t *testing.T) { popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBLink(popr, false) - s1 := p.String() - s2 := fmt.Sprintf("%v", p) + dAtA, err := github_com_gogo_protobuf_proto.Marshal(p) + if err != nil { + panic(err) + } + msg := &PBLink{} + if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil { + panic(err) + } + if err := p.VerboseEqual(msg); err != nil { + t.Fatalf("%#v !VerboseEqual %#v, since %v", msg, p, err) + } +} +func TestPBNodeVerboseEqual(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + p := NewPopulatedPBNode(popr, false) + dAtA, err := github_com_gogo_protobuf_proto.Marshal(p) + if err != nil { + panic(err) + } + msg := &PBNode{} + if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil { + panic(err) + } + if err := p.VerboseEqual(msg); err != nil { + t.Fatalf("%#v !VerboseEqual %#v, since %v", msg, p, err) + } +} +func TestPBLinkGoString(t *testing.T) { + popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + p := NewPopulatedPBLink(popr, false) + s1 := p.GoString() + s2 := fmt.Sprintf("%#v", p) if s1 != s2 { - t.Fatalf("String want %v got %v", s1, s2) + t.Fatalf("GoString want %v got %v", s1, s2) + } + _, err := go_parser.ParseExpr(s1) + if err != nil { + t.Fatal(err) } } -func TestPBNodeStringer(t *testing.T) { +func TestPBNodeGoString(t *testing.T) { popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBNode(popr, false) - s1 := p.String() - s2 := fmt.Sprintf("%v", p) + s1 := p.GoString() + s2 := fmt.Sprintf("%#v", p) if s1 != s2 { - t.Fatalf("String want %v got %v", s1, s2) + t.Fatalf("GoString want %v got %v", s1, s2) + } + _, err := go_parser.ParseExpr(s1) + if err != nil { + t.Fatal(err) } } func TestPBLinkSize(t *testing.T) { - popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) p := NewPopulatedPBLink(popr, true) size2 := github_com_gogo_protobuf_proto.Size(p) - data, err := github_com_gogo_protobuf_proto.Marshal(p) + dAtA, err := github_com_gogo_protobuf_proto.Marshal(p) if err != nil { - panic(err) + t.Fatalf("seed = %d, err = %v", seed, err) } size := p.Size() - if len(data) != size { - t.Fatalf("size %v != marshalled size %v", size, len(data)) + if len(dAtA) != size { + t.Errorf("seed = %d, size %v != marshalled size %v", seed, size, len(dAtA)) } if size2 != size { - t.Fatalf("size %v != before marshal proto.Size %v", size, size2) + t.Errorf("seed = %d, size %v != before marshal proto.Size %v", seed, size, size2) } size3 := github_com_gogo_protobuf_proto.Size(p) if size3 != size { - t.Fatalf("size %v != after marshal proto.Size %v", size, size3) + t.Errorf("seed = %d, size %v != after marshal proto.Size %v", seed, size, size3) } } @@ -356,23 +428,24 @@ func BenchmarkPBLinkSize(b *testing.B) { } func TestPBNodeSize(t *testing.T) { - popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) p := NewPopulatedPBNode(popr, true) size2 := github_com_gogo_protobuf_proto.Size(p) - data, err := github_com_gogo_protobuf_proto.Marshal(p) + dAtA, err := github_com_gogo_protobuf_proto.Marshal(p) if err != nil { - panic(err) + t.Fatalf("seed = %d, err = %v", seed, err) } size := p.Size() - if len(data) != size { - t.Fatalf("size %v != marshalled size %v", size, len(data)) + if len(dAtA) != size { + t.Errorf("seed = %d, size %v != marshalled size %v", seed, size, len(dAtA)) } if size2 != size { - t.Fatalf("size %v != before marshal proto.Size %v", size, size2) + t.Errorf("seed = %d, size %v != before marshal proto.Size %v", seed, size, size2) } size3 := github_com_gogo_protobuf_proto.Size(p) if size3 != size { - t.Fatalf("size %v != after marshal proto.Size %v", size, size3) + t.Errorf("seed = %d, size %v != after marshal proto.Size %v", seed, size, size3) } } @@ -390,60 +463,22 @@ func BenchmarkPBNodeSize(b *testing.B) { b.SetBytes(int64(total / b.N)) } -func TestPBLinkGoString(t *testing.T) { +func TestPBLinkStringer(t *testing.T) { popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBLink(popr, false) - s1 := p.GoString() - s2 := fmt.Sprintf("%#v", p) + s1 := p.String() + s2 := fmt.Sprintf("%v", p) if s1 != s2 { - t.Fatalf("GoString want %v got %v", s1, s2) - } - _, err := go_parser.ParseExpr(s1) - if err != nil { - panic(err) + t.Fatalf("String want %v got %v", s1, s2) } } -func TestPBNodeGoString(t *testing.T) { +func TestPBNodeStringer(t *testing.T) { popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) p := NewPopulatedPBNode(popr, false) - s1 := p.GoString() - s2 := fmt.Sprintf("%#v", p) + s1 := p.String() + s2 := fmt.Sprintf("%v", p) if s1 != s2 { - t.Fatalf("GoString want %v got %v", s1, s2) - } - _, err := go_parser.ParseExpr(s1) - if err != nil { - panic(err) - } -} -func TestPBLinkVerboseEqual(t *testing.T) { - popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) - p := NewPopulatedPBLink(popr, false) - data, err := github_com_gogo_protobuf_proto.Marshal(p) - if err != nil { - panic(err) - } - msg := &PBLink{} - if err := github_com_gogo_protobuf_proto.Unmarshal(data, msg); err != nil { - panic(err) - } - if err := p.VerboseEqual(msg); err != nil { - t.Fatalf("%#v !VerboseEqual %#v, since %v", msg, p, err) - } -} -func TestPBNodeVerboseEqual(t *testing.T) { - popr := math_rand.New(math_rand.NewSource(time.Now().UnixNano())) - p := NewPopulatedPBNode(popr, false) - data, err := github_com_gogo_protobuf_proto.Marshal(p) - if err != nil { - panic(err) - } - msg := &PBNode{} - if err := github_com_gogo_protobuf_proto.Unmarshal(data, msg); err != nil { - panic(err) - } - if err := p.VerboseEqual(msg); err != nil { - t.Fatalf("%#v !VerboseEqual %#v, since %v", msg, p, err) + t.Fatalf("String want %v got %v", s1, s2) } } diff --git a/ipld/merkledag/pb/stability_test.go b/ipld/merkledag/pb/stability_test.go new file mode 100644 index 000000000..5da833713 --- /dev/null +++ b/ipld/merkledag/pb/stability_test.go @@ -0,0 +1,24 @@ +package merkledag_pb + +import ( + bytes "bytes" + "testing" +) + +func TestStability(t *testing.T) { + correct := []byte("\x12\x87\x01\n;\x81\x869\xacH\xa4Ư\xa2\xf1X\x1a\x8b\x95%\xe2\x0f\xdah\x92\u007f+/\xf86\xf75x\xdb\x0f\xa5L)\xf7\xfd\x92\x8d\x92\xcaC\xf1\x93\xde\xe4\u007fY\x15I\xf5\x97\xa8\x11\xc8\xfag\xab\x03\x1e\xbd\x12B|CMw`mHq{>?|vd{0F7>8m[C`HSg3UcXmGs-qp-z6{Kc.tGX->H07\x18\xeaئ\xd0\b\x129\x121LZ3,V9jnmk^veYEV71EMLt9;6]}bnkU2e7GXmqisoCPV0C+ni\x18Ô±\xfe\x8b\f\x12u\n'\xf2#\xc1\xc0nQ\xf9\xb5\x19\x80\xcd\xf8\x06k1\xf6#\x84\x1c\xb6\xbf\xeaY\x9b\xd8O\x84\x04\xdbKq\xe4\xae\xf2\xd6\xe9*\x16B\x12D[gVeg4=t}EGSu82+dmgvQ+Tr>_sLUJ|iZ[P2y2T67ilvEikK}\\iru?IF?mVS[Mv9KG8+\x18\x92\xa0\xf9\xa1\n\n\x11?ÌŽ\v\x06Ñ£\x80nH\x12\x00\xa7\xd2wÍ") + n := new(PBNode) + err := n.Unmarshal(correct) + if err != nil { + t.Fatal(err) + } + d, err := n.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(d, correct) { + t.Logf("%q", d) + t.Fatal("protobuf not stable") + } + +} From cf950c2acfcbf76caf93e96d80c227ba00286334 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 18 Feb 2019 17:19:12 +0100 Subject: [PATCH 2938/3817] gx publish 1.3.6 This commit was moved from ipfs/go-unixfs@8a24c3802dd689dac7d3e176c75bd003a779aeac --- unixfs/pb/unixfs.pb.go | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/unixfs/pb/unixfs.pb.go b/unixfs/pb/unixfs.pb.go index 0ec0617e7..6f1c8fe83 100644 --- a/unixfs/pb/unixfs.pb.go +++ b/unixfs/pb/unixfs.pb.go @@ -3,9 +3,11 @@ package unixfs_pb -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -37,6 +39,7 @@ var Data_DataType_name = map[int32]string{ 4: "Symlink", 5: "HAMTShard", } + var Data_DataType_value = map[string]int32{ "Raw": 0, "Directory": 1, @@ -51,9 +54,11 @@ func (x Data_DataType) Enum() *Data_DataType { *p = x return p } + func (x Data_DataType) String() string { return proto.EnumName(Data_DataType_name, int32(x)) } + func (x *Data_DataType) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(Data_DataType_value, data, "Data_DataType") if err != nil { @@ -62,8 +67,9 @@ func (x *Data_DataType) UnmarshalJSON(data []byte) error { *x = Data_DataType(value) return nil } + func (Data_DataType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_unixfs_768dd0381a72e0c6, []int{0, 0} + return fileDescriptor_e2fd76cc44dfc7c3, []int{0, 0} } type Data struct { @@ -82,7 +88,7 @@ func (m *Data) Reset() { *m = Data{} } func (m *Data) String() string { return proto.CompactTextString(m) } func (*Data) ProtoMessage() {} func (*Data) Descriptor() ([]byte, []int) { - return fileDescriptor_unixfs_768dd0381a72e0c6, []int{0} + return fileDescriptor_e2fd76cc44dfc7c3, []int{0} } func (m *Data) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Data.Unmarshal(m, b) @@ -90,8 +96,8 @@ func (m *Data) XXX_Unmarshal(b []byte) error { func (m *Data) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_Data.Marshal(b, m, deterministic) } -func (dst *Data) XXX_Merge(src proto.Message) { - xxx_messageInfo_Data.Merge(dst, src) +func (m *Data) XXX_Merge(src proto.Message) { + xxx_messageInfo_Data.Merge(m, src) } func (m *Data) XXX_Size() int { return xxx_messageInfo_Data.Size(m) @@ -155,7 +161,7 @@ func (m *Metadata) Reset() { *m = Metadata{} } func (m *Metadata) String() string { return proto.CompactTextString(m) } func (*Metadata) ProtoMessage() {} func (*Metadata) Descriptor() ([]byte, []int) { - return fileDescriptor_unixfs_768dd0381a72e0c6, []int{1} + return fileDescriptor_e2fd76cc44dfc7c3, []int{1} } func (m *Metadata) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Metadata.Unmarshal(m, b) @@ -163,8 +169,8 @@ func (m *Metadata) XXX_Unmarshal(b []byte) error { func (m *Metadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_Metadata.Marshal(b, m, deterministic) } -func (dst *Metadata) XXX_Merge(src proto.Message) { - xxx_messageInfo_Metadata.Merge(dst, src) +func (m *Metadata) XXX_Merge(src proto.Message) { + xxx_messageInfo_Metadata.Merge(m, src) } func (m *Metadata) XXX_Size() int { return xxx_messageInfo_Metadata.Size(m) @@ -183,14 +189,14 @@ func (m *Metadata) GetMimeType() string { } func init() { + proto.RegisterEnum("unixfs.pb.Data_DataType", Data_DataType_name, Data_DataType_value) proto.RegisterType((*Data)(nil), "unixfs.pb.Data") proto.RegisterType((*Metadata)(nil), "unixfs.pb.Metadata") - proto.RegisterEnum("unixfs.pb.Data_DataType", Data_DataType_name, Data_DataType_value) } -func init() { proto.RegisterFile("unixfs.proto", fileDescriptor_unixfs_768dd0381a72e0c6) } +func init() { proto.RegisterFile("unixfs.proto", fileDescriptor_e2fd76cc44dfc7c3) } -var fileDescriptor_unixfs_768dd0381a72e0c6 = []byte{ +var fileDescriptor_e2fd76cc44dfc7c3 = []byte{ // 254 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x90, 0xb1, 0x6a, 0xeb, 0x30, 0x18, 0x85, 0xaf, 0x6c, 0x25, 0xb1, 0xff, 0xeb, 0x16, 0xf1, 0x0f, 0x45, 0x74, 0x28, 0xc6, 0x43, From c364eb32133d29e22b45483c9b442d88171a7cc6 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 18 Feb 2019 17:37:09 +0100 Subject: [PATCH 2939/3817] Update protobuf License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@7ed9abe9e7cc59d7f5f8fe0f429b86d83da62674 --- namesys/base.go | 4 ++-- namesys/cache.go | 2 +- namesys/dns.go | 4 ++-- namesys/dns_test.go | 2 +- namesys/interface.go | 6 ++--- namesys/ipns_resolver_validation_test.go | 30 ++++++++++++------------ namesys/namesys.go | 12 +++++----- namesys/namesys_test.go | 20 ++++++++-------- namesys/proquint.go | 4 ++-- namesys/publisher.go | 20 ++++++++-------- namesys/publisher_test.go | 16 ++++++------- namesys/republisher/repub.go | 14 +++++------ namesys/republisher/repub_test.go | 6 ++--- namesys/resolve_test.go | 14 +++++------ namesys/routing.go | 18 +++++++------- 15 files changed, 86 insertions(+), 86 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 6b6de3d04..7b242801a 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -5,8 +5,8 @@ import ( "strings" "time" - opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" - path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" + opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" + path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 0dc79d87a..67a8e79a7 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" + path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index e2c3202ef..e5af51654 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -6,8 +6,8 @@ import ( "net" "strings" - opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" - path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" + opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" + path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 29fce1507..3353403c7 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" ) type mockDNS struct { diff --git a/namesys/interface.go b/namesys/interface.go index 137a08e87..631d93db4 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,9 +35,9 @@ import ( context "context" - ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" - path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" + opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" + ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" + path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 9ab672dce..41ed8bed9 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,22 +5,22 @@ import ( "testing" "time" - ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" - peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" - pstore "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore" - pstoremem "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore/pstoremem" - path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" - mockrouting "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/mock" - offline "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/offline" - routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" - ropts "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing/options" - testutil "gx/ipfs/QmVnJMgafh5MBYiyqbvDtoCL8pcQvbEGD2k9o9GFpBWPzY/go-testutil" - ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" - record "gx/ipfs/QmexPd3srWxHC76gW2p5j5tQvwpPuCoW7b9vFhJ8BRPyh9/go-libp2p-record" - ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" - dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" + opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" + pstore "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore" + pstoremem "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore/pstoremem" + ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" + peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" + ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" + dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" + routing "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing" + ropts "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing/options" + record "gx/ipfs/QmX1vqjTLTP6pepDi2uiaGxwKbQbem2PD88nGCZrwxKPEW/go-libp2p-record" + path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" + mockrouting "gx/ipfs/QmcjqHcsk8E1Gd8RbuaUawWC7ogDtaVcdjLvZF8ysCCiPn/go-ipfs-routing/mock" + offline "gx/ipfs/QmcjqHcsk8E1Gd8RbuaUawWC7ogDtaVcdjLvZF8ysCCiPn/go-ipfs-routing/offline" + ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" + testutil "gx/ipfs/QmeFVdhzY13YZPWxCiQvmLercrumFRoQZFQEYw2BtzyiQc/go-testutil" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index a74e94f77..97608b5e2 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,15 +5,15 @@ import ( "strings" "time" - ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" - peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" - path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" + opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" - routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" + ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" + peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" + ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" + routing "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing" + path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" - ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 3223697a4..d7ae27ec5 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -5,16 +5,16 @@ import ( "fmt" "testing" - ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" - peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" - pstoremem "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore/pstoremem" - path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" - offroute "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/offline" - "gx/ipfs/QmSygPSC63Uka8z9PYokAS4thiMAor17vhXUTi4qmKHh6P/go-unixfs" - ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" - ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" - dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" + opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" + pstoremem "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore/pstoremem" + ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" + peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" + ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" + dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" + path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" + "gx/ipfs/QmcTSz9ByVBLGkQBNgxFPvKAjMFriKX8PiyhHfjXQzPN23/go-unixfs" + offroute "gx/ipfs/QmcjqHcsk8E1Gd8RbuaUawWC7ogDtaVcdjLvZF8ysCCiPn/go-ipfs-routing/offline" + ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index 37df4aba0..0d5175656 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,8 +4,8 @@ import ( "context" "errors" - opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" - path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" + opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" + path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 69eb52258..46a91aa7b 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -8,16 +8,16 @@ import ( pin "github.com/ipfs/go-ipfs/pin" - ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" - path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" - routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" - ft "gx/ipfs/QmSygPSC63Uka8z9PYokAS4thiMAor17vhXUTi4qmKHh6P/go-unixfs" - ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" - pb "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns/pb" - proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" - ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" - dsquery "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" + ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" + peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" + ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" + dsquery "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" + routing "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing" + path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" + ft "gx/ipfs/QmcTSz9ByVBLGkQBNgxFPvKAjMFriKX8PiyhHfjXQzPN23/go-unixfs" + ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" + pb "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns/pb" + proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 0e9ef1d4e..2c58f4c23 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -7,14 +7,14 @@ import ( "time" ma "gx/ipfs/QmNTCey11oxhb1AxDnQBRHtdhap6Ctud872NjAYPYYXPuc/go-multiaddr" - ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" - mockrouting "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/mock" - testutil "gx/ipfs/QmVnJMgafh5MBYiyqbvDtoCL8pcQvbEGD2k9o9GFpBWPzY/go-testutil" - ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" - dshelp "gx/ipfs/QmauEMWPoSqggfpSDHMMXuDn12DTd7TaFBvn39eeurzKT2/go-ipfs-ds-help" - ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" - dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" + ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" + peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" + ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" + dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" + dshelp "gx/ipfs/QmXx5R5qwsVxbhFogLNzU8A2tch9SZpJJgMUinfpHZsC9C/go-ipfs-ds-help" + mockrouting "gx/ipfs/QmcjqHcsk8E1Gd8RbuaUawWC7ogDtaVcdjLvZF8ysCCiPn/go-ipfs-routing/mock" + ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" + testutil "gx/ipfs/QmeFVdhzY13YZPWxCiQvmLercrumFRoQZFQEYw2BtzyiQc/go-testutil" ) type identity struct { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index fb7ea3ded..2959cdccc 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,16 +7,16 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" + path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" - ic "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - pb "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns/pb" - logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" - proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" - ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" + ic "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" + peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" + ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" + logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" + pb "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns/pb" + proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 2ae20392e..728423998 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" + path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" - pstore "gx/ipfs/QmQFFp4ntkd4C14sP3FaH9WJyBuetuGUVo6dShNHvnoEvC/go-libp2p-peerstore" + pstore "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmebEmt23jQxrwnqBkFL4qbpE8EnnQunpv5U32LS5ESus1/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmcNGX5RaxPPCYwa6yGXM1EcUbrreTTinixLcYGmMwf1sx/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 930d5219a..4d0f94f51 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,13 +6,13 @@ import ( "testing" "time" - peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" - path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" - mockrouting "gx/ipfs/QmRJvdmKJoDcQEhhTt5NYXJPQFnJYPo1kfapxtjZLfDDqH/go-ipfs-routing/mock" - testutil "gx/ipfs/QmVnJMgafh5MBYiyqbvDtoCL8pcQvbEGD2k9o9GFpBWPzY/go-testutil" - ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" - ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" - dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" + peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" + ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" + dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" + path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" + mockrouting "gx/ipfs/QmcjqHcsk8E1Gd8RbuaUawWC7ogDtaVcdjLvZF8ysCCiPn/go-ipfs-routing/mock" + ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" + testutil "gx/ipfs/QmeFVdhzY13YZPWxCiQvmLercrumFRoQZFQEYw2BtzyiQc/go-testutil" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index d08bb5103..ffd958292 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,17 +5,17 @@ import ( "strings" "time" - opts "gx/ipfs/QmNmqKNivNTN11HrKWJYt29n6Z2fuzkeDheQV62dbxNuLb/interface-go-ipfs-core/options/namesys" - peer "gx/ipfs/QmPJxxDsX2UbchSHobbYuvz7qnyJTFKvaKMzE2rZWJ4x5B/go-libp2p-peer" - path "gx/ipfs/QmQiXYqcxU5AvpAJkfbXUEZgUYKog1Pd2Cv3WBiW2Hpe8M/go-path" + opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - routing "gx/ipfs/QmRjT8Bkut84fHf9nxMQBxGsqLAkqzMdFaemDK7e61dBNZ/go-libp2p-routing" - dht "gx/ipfs/QmS5Tvk8Adz1qPkCBCbiScty9KPbMSMCSTbFK4TVvatKqi/go-libp2p-kad-dht" - ipns "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns" - pb "gx/ipfs/QmVpC4PPSaoqZzWYEnQURnsQagimcWEzNKZouZyd7sNJdZ/go-ipns/pb" - logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" - proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" + peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" + routing "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing" + path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" + logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" + ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" + pb "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns/pb" + proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" + dht "gx/ipfs/QmfM7kwroZsKhKFmnJagPvM28MZMyKxG3QV2AqfvZvEEqS/go-libp2p-kad-dht" ) var log = logging.Logger("namesys") From 923bf724f755800bb8fd46e538c9670985ce317c Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 18 Feb 2019 17:37:09 +0100 Subject: [PATCH 2940/3817] Update protobuf License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-pinner@5cd19525cab999f733fc0838f7e62c77d9f0abfb --- pinning/pinner/gc/gc.go | 12 ++-- pinning/pinner/internal/pb/header.pb.go | 80 ++++++++++++++----------- pinning/pinner/pin.go | 6 +- pinning/pinner/pin_test.go | 12 ++-- pinning/pinner/set.go | 4 +- pinning/pinner/set_test.go | 12 ++-- 6 files changed, 68 insertions(+), 58 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 86bc86f1d..ef8eace64 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,16 +8,16 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmQvMsV5aPyd7eMd3U1hvAUhZEupG3rXbVZn7ppU5RE6bt/go-merkledag" - bserv "gx/ipfs/QmZuPasxd7fSgtzRzCL7Z8J8QwDJML2fgBUExRbQCqb4BT/go-blockservice" + bserv "gx/ipfs/QmZsGVGCqMCNzHLNMB6q4F6yyvomqf1VxwhJwSfgo1NGaF/go-blockservice" + dag "gx/ipfs/Qmccmovpo9isKeaaDzcxvT7mVJN1uKwn2xzSs1y8hb6PEs/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" - bstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" + bstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" + offline "gx/ipfs/QmSz8kAe2JCKp2dWSG8gHSWnwSmne8YfRXTeK5HBmc9L7t/go-ipfs-exchange-offline" + dstore "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" "gx/ipfs/QmYMQuypUbgsdNHmuCBSUJV6wdQVsBHRivNAp3efHJwZJD/go-verifcid" - offline "gx/ipfs/QmYZwey1thDTynSrvd6qQkX24UpTka6TFhQ2v569UpoqxD/go-ipfs-exchange-offline" - logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" - dstore "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" + logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/internal/pb/header.pb.go b/pinning/pinner/internal/pb/header.pb.go index ca4173c3e..dd215e126 100644 --- a/pinning/pinner/internal/pb/header.pb.go +++ b/pinning/pinner/internal/pb/header.pb.go @@ -3,13 +3,13 @@ package pb -import proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" -import fmt "fmt" -import math "math" - -import encoding_binary "encoding/binary" - -import io "io" +import ( + encoding_binary "encoding/binary" + fmt "fmt" + proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" + io "io" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -28,16 +28,14 @@ type Set struct { // how many of the links are subtrees Fanout uint32 `protobuf:"varint,2,opt,name=fanout" json:"fanout"` // hash seed for subtree selection, a random number - Seed uint32 `protobuf:"fixed32,3,opt,name=seed" json:"seed"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_sizecache int32 `json:"-"` + Seed uint32 `protobuf:"fixed32,3,opt,name=seed" json:"seed"` } func (m *Set) Reset() { *m = Set{} } func (m *Set) String() string { return proto.CompactTextString(m) } func (*Set) ProtoMessage() {} func (*Set) Descriptor() ([]byte, []int) { - return fileDescriptor_header_778100e52d428560, []int{0} + return fileDescriptor_cda303a5a3ed87e7, []int{0} } func (m *Set) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -54,8 +52,8 @@ func (m *Set) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return b[:n], nil } } -func (dst *Set) XXX_Merge(src proto.Message) { - xxx_messageInfo_Set.Merge(dst, src) +func (m *Set) XXX_Merge(src proto.Message) { + xxx_messageInfo_Set.Merge(m, src) } func (m *Set) XXX_Size() int { return m.Size() @@ -90,6 +88,24 @@ func (m *Set) GetSeed() uint32 { func init() { proto.RegisterType((*Set)(nil), "ipfs.pin.Set") } + +func init() { proto.RegisterFile("pin/internal/pb/header.proto", fileDescriptor_cda303a5a3ed87e7) } + +var fileDescriptor_cda303a5a3ed87e7 = []byte{ + // 162 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x29, 0xc8, 0xcc, 0xd3, + 0xcf, 0xcc, 0x2b, 0x49, 0x2d, 0xca, 0x4b, 0xcc, 0xd1, 0x2f, 0x48, 0xd2, 0xcf, 0x48, 0x4d, 0x4c, + 0x49, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0xc8, 0x2c, 0x48, 0x2b, 0xd6, 0x2b, + 0xc8, 0xcc, 0x53, 0x8a, 0xe5, 0x62, 0x0e, 0x4e, 0x2d, 0x11, 0x92, 0xe3, 0x62, 0x2f, 0x4b, 0x2d, + 0x2a, 0xce, 0xcc, 0xcf, 0x93, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x75, 0x62, 0x39, 0x71, 0x4f, 0x9e, + 0x21, 0x08, 0x26, 0x28, 0x24, 0xc3, 0xc5, 0x96, 0x96, 0x98, 0x97, 0x5f, 0x5a, 0x22, 0xc1, 0x84, + 0x24, 0x0d, 0x15, 0x13, 0x92, 0xe0, 0x62, 0x29, 0x4e, 0x4d, 0x4d, 0x91, 0x60, 0x56, 0x60, 0xd4, + 0x60, 0x87, 0xca, 0x81, 0x45, 0x9c, 0x64, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, + 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, + 0x21, 0x8a, 0xa9, 0x20, 0x09, 0x10, 0x00, 0x00, 0xff, 0xff, 0x20, 0x85, 0x2f, 0x24, 0xa5, 0x00, + 0x00, 0x00, +} + func (m *Set) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -128,6 +144,9 @@ func encodeVarintHeader(dAtA []byte, offset int, v uint64) int { return offset + 1 } func (m *Set) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l n += 1 + sovHeader(uint64(m.Version)) @@ -164,7 +183,7 @@ func (m *Set) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -192,7 +211,7 @@ func (m *Set) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Version |= (uint32(b) & 0x7F) << shift + m.Version |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -211,7 +230,7 @@ func (m *Set) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Fanout |= (uint32(b) & 0x7F) << shift + m.Fanout |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -235,6 +254,9 @@ func (m *Set) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthHeader } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthHeader + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } @@ -301,10 +323,13 @@ func skipHeader(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthHeader } + iNdEx += length + if iNdEx < 0 { + return 0, ErrInvalidLengthHeader + } return iNdEx, nil case 3: for { @@ -333,6 +358,9 @@ func skipHeader(dAtA []byte) (n int, err error) { return 0, err } iNdEx = start + next + if iNdEx < 0 { + return 0, ErrInvalidLengthHeader + } } return iNdEx, nil case 4: @@ -351,21 +379,3 @@ var ( ErrInvalidLengthHeader = fmt.Errorf("proto: negative length found during unmarshaling") ErrIntOverflowHeader = fmt.Errorf("proto: integer overflow") ) - -func init() { - proto.RegisterFile("pin/internal/pb/header.proto", fileDescriptor_header_778100e52d428560) -} - -var fileDescriptor_header_778100e52d428560 = []byte{ - // 154 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x29, 0xc8, 0xcc, 0xd3, - 0xcf, 0xcc, 0x2b, 0x49, 0x2d, 0xca, 0x4b, 0xcc, 0xd1, 0x2f, 0x48, 0xd2, 0xcf, 0x48, 0x4d, 0x4c, - 0x49, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0xc8, 0x2c, 0x48, 0x2b, 0xd6, 0x2b, - 0xc8, 0xcc, 0x53, 0x8a, 0xe5, 0x62, 0x0e, 0x4e, 0x2d, 0x11, 0x92, 0xe3, 0x62, 0x2f, 0x4b, 0x2d, - 0x2a, 0xce, 0xcc, 0xcf, 0x93, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x75, 0x62, 0x39, 0x71, 0x4f, 0x9e, - 0x21, 0x08, 0x26, 0x28, 0x24, 0xc3, 0xc5, 0x96, 0x96, 0x98, 0x97, 0x5f, 0x5a, 0x22, 0xc1, 0x84, - 0x24, 0x0d, 0x15, 0x13, 0x92, 0xe0, 0x62, 0x29, 0x4e, 0x4d, 0x4d, 0x91, 0x60, 0x56, 0x60, 0xd4, - 0x60, 0x87, 0xca, 0x81, 0x45, 0x9c, 0x44, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, - 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0xa2, 0x98, 0x0a, 0x92, 0x00, 0x01, 0x00, 0x00, - 0xff, 0xff, 0xc3, 0xf9, 0x7f, 0x24, 0x9d, 0x00, 0x00, 0x00, -} diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index bf4a71957..2cb1bee03 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,12 +10,12 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmQvMsV5aPyd7eMd3U1hvAUhZEupG3rXbVZn7ppU5RE6bt/go-merkledag" + mdag "gx/ipfs/Qmccmovpo9isKeaaDzcxvT7mVJN1uKwn2xzSs1y8hb6PEs/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" - logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" - ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" + ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" + logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index f7da55269..89065b4e4 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" - mdag "gx/ipfs/QmQvMsV5aPyd7eMd3U1hvAUhZEupG3rXbVZn7ppU5RE6bt/go-merkledag" - bs "gx/ipfs/QmZuPasxd7fSgtzRzCL7Z8J8QwDJML2fgBUExRbQCqb4BT/go-blockservice" + bs "gx/ipfs/QmZsGVGCqMCNzHLNMB6q4F6yyvomqf1VxwhJwSfgo1NGaF/go-blockservice" + mdag "gx/ipfs/Qmccmovpo9isKeaaDzcxvT7mVJN1uKwn2xzSs1y8hb6PEs/go-merkledag" util "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" - offline "gx/ipfs/QmYZwey1thDTynSrvd6qQkX24UpTka6TFhQ2v569UpoqxD/go-ipfs-exchange-offline" - ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" - dssync "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/sync" + blockstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" + offline "gx/ipfs/QmSz8kAe2JCKp2dWSG8gHSWnwSmne8YfRXTeK5HBmc9L7t/go-ipfs-exchange-offline" + ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" + dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" ) var rand = util.NewTimeSeededRand() diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 846e2013e..70b46ec9d 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,11 +10,11 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmQvMsV5aPyd7eMd3U1hvAUhZEupG3rXbVZn7ppU5RE6bt/go-merkledag" + "gx/ipfs/Qmccmovpo9isKeaaDzcxvT7mVJN1uKwn2xzSs1y8hb6PEs/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" - "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" + "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 96340bed1..ba05d356a 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,14 +5,14 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmQvMsV5aPyd7eMd3U1hvAUhZEupG3rXbVZn7ppU5RE6bt/go-merkledag" - bserv "gx/ipfs/QmZuPasxd7fSgtzRzCL7Z8J8QwDJML2fgBUExRbQCqb4BT/go-blockservice" + bserv "gx/ipfs/QmZsGVGCqMCNzHLNMB6q4F6yyvomqf1VxwhJwSfgo1NGaF/go-blockservice" + dag "gx/ipfs/Qmccmovpo9isKeaaDzcxvT7mVJN1uKwn2xzSs1y8hb6PEs/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" - offline "gx/ipfs/QmYZwey1thDTynSrvd6qQkX24UpTka6TFhQ2v569UpoqxD/go-ipfs-exchange-offline" - ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" - dsq "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" + blockstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" + offline "gx/ipfs/QmSz8kAe2JCKp2dWSG8gHSWnwSmne8YfRXTeK5HBmc9L7t/go-ipfs-exchange-offline" + ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" + dsq "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" ) func ignoreCids(_ cid.Cid) {} From 2936ea0db358718f34aff4b6fc0df0f3c71b6dba Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 18 Feb 2019 17:37:09 +0100 Subject: [PATCH 2941/3817] Update protobuf License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-filestore@ad56e7d3f88b8fb02a4816ff2e38bdaca74f9fa1 --- filestore/filestore.go | 6 +-- filestore/filestore_test.go | 6 +-- filestore/fsrefstore.go | 12 +++--- filestore/pb/dataobj.pb.go | 83 ++++++++++++++++++++++--------------- filestore/util.go | 8 ++-- 5 files changed, 65 insertions(+), 50 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 0ee63e52e..3b0deb43d 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -12,11 +12,11 @@ import ( "errors" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" + blockstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" + dsq "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" posinfo "gx/ipfs/QmUhHBdzfNb9FQPDtKwhghVoR3zwkbXzFJ1uJyEMYUpFSd/go-ipfs-posinfo" blocks "gx/ipfs/QmWoXtvgC8inqFkAATB7cp2Dax7XBi9VDvSg9RCCZufmRk/go-block-format" - logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" - dsq "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" + logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 2d06a9ac2..eaa38217c 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,12 +7,12 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmQvMsV5aPyd7eMd3U1hvAUhZEupG3rXbVZn7ppU5RE6bt/go-merkledag" + dag "gx/ipfs/Qmccmovpo9isKeaaDzcxvT7mVJN1uKwn2xzSs1y8hb6PEs/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" + blockstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" + ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" posinfo "gx/ipfs/QmUhHBdzfNb9FQPDtKwhghVoR3zwkbXzFJ1uJyEMYUpFSd/go-ipfs-posinfo" - ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index a7e34bb77..81aea7e6c 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -11,14 +11,14 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" + blockstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" + ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" + dsns "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/namespace" + dsq "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" posinfo "gx/ipfs/QmUhHBdzfNb9FQPDtKwhghVoR3zwkbXzFJ1uJyEMYUpFSd/go-ipfs-posinfo" blocks "gx/ipfs/QmWoXtvgC8inqFkAATB7cp2Dax7XBi9VDvSg9RCCZufmRk/go-block-format" - dshelp "gx/ipfs/QmauEMWPoSqggfpSDHMMXuDn12DTd7TaFBvn39eeurzKT2/go-ipfs-ds-help" - proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" - ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" - dsns "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/namespace" - dsq "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" + dshelp "gx/ipfs/QmXx5R5qwsVxbhFogLNzU8A2tch9SZpJJgMUinfpHZsC9C/go-ipfs-ds-help" + proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/pb/dataobj.pb.go b/filestore/pb/dataobj.pb.go index 6046acbd6..cf1da1513 100644 --- a/filestore/pb/dataobj.pb.go +++ b/filestore/pb/dataobj.pb.go @@ -3,11 +3,12 @@ package datastore_pb -import proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto" -import fmt "fmt" -import math "math" - -import io "io" +import ( + fmt "fmt" + proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" + io "io" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -21,18 +22,16 @@ var _ = math.Inf const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package type DataObj struct { - FilePath string `protobuf:"bytes,1,opt,name=FilePath" json:"FilePath"` - Offset uint64 `protobuf:"varint,2,opt,name=Offset" json:"Offset"` - Size_ uint64 `protobuf:"varint,3,opt,name=Size" json:"Size"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_sizecache int32 `json:"-"` + FilePath string `protobuf:"bytes,1,opt,name=FilePath" json:"FilePath"` + Offset uint64 `protobuf:"varint,2,opt,name=Offset" json:"Offset"` + Size_ uint64 `protobuf:"varint,3,opt,name=Size" json:"Size"` } func (m *DataObj) Reset() { *m = DataObj{} } func (m *DataObj) String() string { return proto.CompactTextString(m) } func (*DataObj) ProtoMessage() {} func (*DataObj) Descriptor() ([]byte, []int) { - return fileDescriptor_dataobj_216c555249812eeb, []int{0} + return fileDescriptor_86a3613fbaff9a6c, []int{0} } func (m *DataObj) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -49,8 +48,8 @@ func (m *DataObj) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return b[:n], nil } } -func (dst *DataObj) XXX_Merge(src proto.Message) { - xxx_messageInfo_DataObj.Merge(dst, src) +func (m *DataObj) XXX_Merge(src proto.Message) { + xxx_messageInfo_DataObj.Merge(m, src) } func (m *DataObj) XXX_Size() int { return m.Size() @@ -85,6 +84,23 @@ func (m *DataObj) GetSize_() uint64 { func init() { proto.RegisterType((*DataObj)(nil), "datastore.pb.DataObj") } + +func init() { proto.RegisterFile("filestore/pb/dataobj.proto", fileDescriptor_86a3613fbaff9a6c) } + +var fileDescriptor_86a3613fbaff9a6c = []byte{ + // 160 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4a, 0xcb, 0xcc, 0x49, + 0x2d, 0x2e, 0xc9, 0x2f, 0x4a, 0xd5, 0x2f, 0x48, 0xd2, 0x4f, 0x49, 0x2c, 0x49, 0xcc, 0x4f, 0xca, + 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x01, 0x71, 0xc1, 0x72, 0x7a, 0x05, 0x49, 0x4a, + 0xc9, 0x5c, 0xec, 0x2e, 0x89, 0x25, 0x89, 0xfe, 0x49, 0x59, 0x42, 0x0a, 0x5c, 0x1c, 0x6e, 0x99, + 0x39, 0xa9, 0x01, 0x89, 0x25, 0x19, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x4e, 0x2c, 0x27, 0xee, + 0xc9, 0x33, 0x04, 0xc1, 0x45, 0x85, 0x64, 0xb8, 0xd8, 0xfc, 0xd3, 0xd2, 0x8a, 0x53, 0x4b, 0x24, + 0x98, 0x14, 0x18, 0x35, 0x58, 0xa0, 0xf2, 0x50, 0x31, 0x21, 0x09, 0x2e, 0x96, 0xe0, 0xcc, 0xaa, + 0x54, 0x09, 0x66, 0x24, 0x39, 0xb0, 0x88, 0x93, 0xc4, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, + 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, + 0xcb, 0x31, 0x00, 0x02, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x87, 0xf5, 0x88, 0xa9, 0x00, 0x00, 0x00, +} + func (m *DataObj) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -123,6 +139,9 @@ func encodeVarintDataobj(dAtA []byte, offset int, v uint64) int { return offset + 1 } func (m *DataObj) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.FilePath) @@ -160,7 +179,7 @@ func (m *DataObj) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -188,7 +207,7 @@ func (m *DataObj) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -198,6 +217,9 @@ func (m *DataObj) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDataobj } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDataobj + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -217,7 +239,7 @@ func (m *DataObj) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Offset |= (uint64(b) & 0x7F) << shift + m.Offset |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -236,7 +258,7 @@ func (m *DataObj) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Size_ |= (uint64(b) & 0x7F) << shift + m.Size_ |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -250,6 +272,9 @@ func (m *DataObj) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthDataobj } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDataobj + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } @@ -316,10 +341,13 @@ func skipDataobj(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthDataobj } + iNdEx += length + if iNdEx < 0 { + return 0, ErrInvalidLengthDataobj + } return iNdEx, nil case 3: for { @@ -348,6 +376,9 @@ func skipDataobj(dAtA []byte) (n int, err error) { return 0, err } iNdEx = start + next + if iNdEx < 0 { + return 0, ErrInvalidLengthDataobj + } } return iNdEx, nil case 4: @@ -366,19 +397,3 @@ var ( ErrInvalidLengthDataobj = fmt.Errorf("proto: negative length found during unmarshaling") ErrIntOverflowDataobj = fmt.Errorf("proto: integer overflow") ) - -func init() { proto.RegisterFile("filestore/pb/dataobj.proto", fileDescriptor_dataobj_216c555249812eeb) } - -var fileDescriptor_dataobj_216c555249812eeb = []byte{ - // 151 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4a, 0xcb, 0xcc, 0x49, - 0x2d, 0x2e, 0xc9, 0x2f, 0x4a, 0xd5, 0x2f, 0x48, 0xd2, 0x4f, 0x49, 0x2c, 0x49, 0xcc, 0x4f, 0xca, - 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x01, 0x71, 0xc1, 0x72, 0x7a, 0x05, 0x49, 0x4a, - 0xc9, 0x5c, 0xec, 0x2e, 0x89, 0x25, 0x89, 0xfe, 0x49, 0x59, 0x42, 0x0a, 0x5c, 0x1c, 0x6e, 0x99, - 0x39, 0xa9, 0x01, 0x89, 0x25, 0x19, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x4e, 0x2c, 0x27, 0xee, - 0xc9, 0x33, 0x04, 0xc1, 0x45, 0x85, 0x64, 0xb8, 0xd8, 0xfc, 0xd3, 0xd2, 0x8a, 0x53, 0x4b, 0x24, - 0x98, 0x14, 0x18, 0x35, 0x58, 0xa0, 0xf2, 0x50, 0x31, 0x21, 0x09, 0x2e, 0x96, 0xe0, 0xcc, 0xaa, - 0x54, 0x09, 0x66, 0x24, 0x39, 0xb0, 0x88, 0x93, 0xc0, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, - 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0x03, 0x20, 0x00, 0x00, 0xff, 0xff, 0x8c, - 0xe7, 0x83, 0xa2, 0xa1, 0x00, 0x00, 0x00, -} diff --git a/filestore/util.go b/filestore/util.go index a4f1b9732..3d4d4552b 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -7,10 +7,10 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmS2aqUZLJp8kF1ihE5rvDGE5LvmKDPnx32w9Z1BW9xLV5/go-ipfs-blockstore" - dshelp "gx/ipfs/QmauEMWPoSqggfpSDHMMXuDn12DTd7TaFBvn39eeurzKT2/go-ipfs-ds-help" - ds "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore" - dsq "gx/ipfs/Qmf4xQhNomPNhrtZc67qSnfJSjxjXs9LWvknJtSXwimPrM/go-datastore/query" + blockstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" + ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" + dsq "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" + dshelp "gx/ipfs/QmXx5R5qwsVxbhFogLNzU8A2tch9SZpJJgMUinfpHZsC9C/go-ipfs-ds-help" ) // Status is used to identify the state of the block data referenced From 2863f1ff76057b034b1bbf56fda41f6dc5ec87b5 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 18 Feb 2019 17:37:09 +0100 Subject: [PATCH 2942/3817] Update protobuf License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-keystore@5e1d11f77cfea0319e38a183a7590b0e94cb0033 --- keystore/keystore.go | 4 ++-- keystore/keystore_test.go | 2 +- keystore/memkeystore.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index 7c41b36ed..bafc859b9 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -7,8 +7,8 @@ import ( "path/filepath" "strings" - ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" - logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log" + ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" + logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" ) var log = logging.Logger("keystore") diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index 1d2005e9e..fe8276872 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -9,7 +9,7 @@ import ( "sort" "testing" - ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" + ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" ) type rr struct{} diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index 0c8f8861f..6983100f9 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -1,6 +1,6 @@ package keystore -import ci "gx/ipfs/QmNiJiXwWE3kRhZrC5ej3kSjWHm337pYfhjLGSCDNKJP2s/go-libp2p-crypto" +import ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" // MemKeystore is an in memory keystore implementation that is not persisted to // any backing storage. From 2c70ad1761014168a0bdd27b6ec4628cb08e1001 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 19 Feb 2019 02:39:21 -0800 Subject: [PATCH 2943/3817] errors: introduce a 'not supported' error This commit was moved from ipfs/interface-go-ipfs-core@a81e4359ce5808c1de22b5ec3c6f05b83d86499d --- coreiface/errors.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/coreiface/errors.go b/coreiface/errors.go index 234abe566..e0bd7805d 100644 --- a/coreiface/errors.go +++ b/coreiface/errors.go @@ -3,7 +3,8 @@ package iface import "errors" var ( - ErrIsDir = errors.New("this dag node is a directory") - ErrNotFile = errors.New("this dag node is not a regular file") - ErrOffline = errors.New("this action must be run in online mode, try running 'ipfs daemon' first") + ErrIsDir = errors.New("this dag node is a directory") + ErrNotFile = errors.New("this dag node is not a regular file") + ErrOffline = errors.New("this action must be run in online mode, try running 'ipfs daemon' first") + ErrNotSupported = errors.New("operation not supported") ) From 88537c541c069f797717ea553b2be01f33f65a27 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 19 Feb 2019 03:48:04 -0800 Subject: [PATCH 2944/3817] coreapi: return coreiface.ErrNotSupported when "catting" symlinks. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@4d15a8bc10456a7433ee005a4267887a126a13ce --- namesys/base.go | 2 +- namesys/dns.go | 2 +- namesys/dns_test.go | 2 +- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 2 +- namesys/namesys.go | 2 +- namesys/namesys_test.go | 2 +- namesys/proquint.go | 2 +- namesys/routing.go | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 7b242801a..1f645b554 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -5,7 +5,7 @@ import ( "strings" "time" - opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" ) diff --git a/namesys/dns.go b/namesys/dns.go index e5af51654..c4e095f07 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -6,7 +6,7 @@ import ( "net" "strings" - opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 3353403c7..126f67a2c 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" ) type mockDNS struct { diff --git a/namesys/interface.go b/namesys/interface.go index 631d93db4..0c9c33bf1 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,7 +35,7 @@ import ( context "context" - opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" ) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 41ed8bed9..4a6f70497 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,7 +6,7 @@ import ( "time" u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" - opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" pstore "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore" pstoremem "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore/pstoremem" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" diff --git a/namesys/namesys.go b/namesys/namesys.go index 97608b5e2..b945c39d6 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,7 +5,7 @@ import ( "strings" "time" - opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index d7ae27ec5..e8cbf0b1f 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" pstoremem "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore/pstoremem" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" diff --git a/namesys/proquint.go b/namesys/proquint.go index 0d5175656..054fdc931 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,7 +4,7 @@ import ( "context" "errors" - opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/routing.go b/namesys/routing.go index ffd958292..d907e0db3 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,7 +5,7 @@ import ( "strings" "time" - opts "gx/ipfs/QmNt8iUv3uJoVrJ3Ls4cgMLk124V4Bt1JxuUMWbjULt9ns/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" routing "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing" From 1e563751d6b51e485919044ba12db6d0ea4c7c12 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 20 Feb 2019 17:19:54 -0800 Subject: [PATCH 2945/3817] gx: update go-bitswap and go-libp2p-kad-dht * go-bitswap: fix some race conditions. * go-libp2p-kad-dht: fix a goroutine leak. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@7bb0ebcef5e84d83449b7ccbea00ad5a299f7672 --- namesys/base.go | 4 ++-- namesys/cache.go | 2 +- namesys/dns.go | 4 ++-- namesys/dns_test.go | 2 +- namesys/interface.go | 4 ++-- namesys/ipns_resolver_validation_test.go | 4 ++-- namesys/namesys.go | 4 ++-- namesys/namesys_test.go | 6 +++--- namesys/proquint.go | 4 ++-- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- namesys/resolve_test.go | 2 +- namesys/routing.go | 6 +++--- 14 files changed, 25 insertions(+), 25 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 1f645b554..6f25cf2e6 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -5,8 +5,8 @@ import ( "strings" "time" - opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" - path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" + opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" + path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 67a8e79a7..6ae568f54 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" + path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index c4e095f07..c86729972 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -6,9 +6,9 @@ import ( "net" "strings" - opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" - path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" + opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" + path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" ) type LookupTXTFunc func(name string) (txt []string, err error) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 126f67a2c..90da2ebdc 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" ) type mockDNS struct { diff --git a/namesys/interface.go b/namesys/interface.go index 0c9c33bf1..e1e6f3fd5 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,9 +35,9 @@ import ( context "context" - opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" + opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" + path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 4a6f70497..252fbb005 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,17 +6,17 @@ import ( "time" u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" - opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" pstore "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore" pstoremem "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore/pstoremem" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" + opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" routing "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing" ropts "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing/options" record "gx/ipfs/QmX1vqjTLTP6pepDi2uiaGxwKbQbem2PD88nGCZrwxKPEW/go-libp2p-record" - path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" + path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" mockrouting "gx/ipfs/QmcjqHcsk8E1Gd8RbuaUawWC7ogDtaVcdjLvZF8ysCCiPn/go-ipfs-routing/mock" offline "gx/ipfs/QmcjqHcsk8E1Gd8RbuaUawWC7ogDtaVcdjLvZF8ysCCiPn/go-ipfs-routing/offline" ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" diff --git a/namesys/namesys.go b/namesys/namesys.go index b945c39d6..81418a9c8 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,14 +5,14 @@ import ( "strings" "time" - opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" + opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" routing "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing" - path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" + path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index e8cbf0b1f..c8cf228b1 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -5,16 +5,16 @@ import ( "fmt" "testing" - opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" pstoremem "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore/pstoremem" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" + opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" - path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" - "gx/ipfs/QmcTSz9ByVBLGkQBNgxFPvKAjMFriKX8PiyhHfjXQzPN23/go-unixfs" + path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" offroute "gx/ipfs/QmcjqHcsk8E1Gd8RbuaUawWC7ogDtaVcdjLvZF8ysCCiPn/go-ipfs-routing/offline" ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" + "gx/ipfs/QmfKmRac17N7UmsV16vifHtB5Eqz3hTyKcu22Qb2oFoZyR/go-unixfs" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index 054fdc931..6ffd479c6 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,9 +4,9 @@ import ( "context" "errors" - opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" - path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" + opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" + path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index 46a91aa7b..8c75694fe 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -13,11 +13,11 @@ import ( ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dsquery "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" routing "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing" - path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" - ft "gx/ipfs/QmcTSz9ByVBLGkQBNgxFPvKAjMFriKX8PiyhHfjXQzPN23/go-unixfs" + path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" pb "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns/pb" proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" + ft "gx/ipfs/QmfKmRac17N7UmsV16vifHtB5Eqz3hTyKcu22Qb2oFoZyR/go-unixfs" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 2959cdccc..504858805 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" + path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 728423998..9d2188c5e 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,7 +10,7 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" + path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" pstore "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 4d0f94f51..91f9a7bb0 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -9,7 +9,7 @@ import ( peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" - path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" + path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" mockrouting "gx/ipfs/QmcjqHcsk8E1Gd8RbuaUawWC7ogDtaVcdjLvZF8ysCCiPn/go-ipfs-routing/mock" ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" testutil "gx/ipfs/QmeFVdhzY13YZPWxCiQvmLercrumFRoQZFQEYw2BtzyiQc/go-testutil" diff --git a/namesys/routing.go b/namesys/routing.go index d907e0db3..b03f602b0 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,17 +5,17 @@ import ( "strings" "time" - opts "gx/ipfs/QmNuVxikLRanHXwpw3sy58Vt5yMd4K5BRmWsUqYCctRRVE/interface-go-ipfs-core/options/namesys" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" + opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" routing "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing" - path "gx/ipfs/QmXgYy6EgbKrpXFtfdHumWsEMd1HrsqGXtovrfBnMgbpfy/go-path" + path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" + dht "gx/ipfs/QmbMXtieVFmFfT5AzVEP9RUbq7dYiWCZjz8KWa5hsr8kSE/go-libp2p-kad-dht" logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" pb "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns/pb" proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" - dht "gx/ipfs/QmfM7kwroZsKhKFmnJagPvM28MZMyKxG3QV2AqfvZvEEqS/go-libp2p-kad-dht" ) var log = logging.Logger("namesys") From 233ef9a959aaf0e9b72c6c96af4ea2237b6cf0ea Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 20 Feb 2019 17:19:54 -0800 Subject: [PATCH 2946/3817] gx: update go-bitswap and go-libp2p-kad-dht * go-bitswap: fix some race conditions. * go-libp2p-kad-dht: fix a goroutine leak. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@1b127cc93facfb086597d2ef502f1d3ac746adb4 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index ef8eace64..3db3336f0 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmZsGVGCqMCNzHLNMB6q4F6yyvomqf1VxwhJwSfgo1NGaF/go-blockservice" - dag "gx/ipfs/Qmccmovpo9isKeaaDzcxvT7mVJN1uKwn2xzSs1y8hb6PEs/go-merkledag" + bserv "gx/ipfs/QmRCLtpTSWPmh4QK3TJ4rHCBSH7thYNZMWZG2PZvVrb8KJ/go-blockservice" + dag "gx/ipfs/QmUsnWBZaqpZ8i2wX37V2wC5hcB7VWB3zsUcNsyQ1Be5AX/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 2cb1bee03..dce6df803 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/Qmccmovpo9isKeaaDzcxvT7mVJN1uKwn2xzSs1y8hb6PEs/go-merkledag" + mdag "gx/ipfs/QmUsnWBZaqpZ8i2wX37V2wC5hcB7VWB3zsUcNsyQ1Be5AX/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 89065b4e4..84314e412 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - bs "gx/ipfs/QmZsGVGCqMCNzHLNMB6q4F6yyvomqf1VxwhJwSfgo1NGaF/go-blockservice" - mdag "gx/ipfs/Qmccmovpo9isKeaaDzcxvT7mVJN1uKwn2xzSs1y8hb6PEs/go-merkledag" + bs "gx/ipfs/QmRCLtpTSWPmh4QK3TJ4rHCBSH7thYNZMWZG2PZvVrb8KJ/go-blockservice" + mdag "gx/ipfs/QmUsnWBZaqpZ8i2wX37V2wC5hcB7VWB3zsUcNsyQ1Be5AX/go-merkledag" util "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 70b46ec9d..7cebac76b 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/Qmccmovpo9isKeaaDzcxvT7mVJN1uKwn2xzSs1y8hb6PEs/go-merkledag" + "gx/ipfs/QmUsnWBZaqpZ8i2wX37V2wC5hcB7VWB3zsUcNsyQ1Be5AX/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index ba05d356a..85e3807ee 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmZsGVGCqMCNzHLNMB6q4F6yyvomqf1VxwhJwSfgo1NGaF/go-blockservice" - dag "gx/ipfs/Qmccmovpo9isKeaaDzcxvT7mVJN1uKwn2xzSs1y8hb6PEs/go-merkledag" + bserv "gx/ipfs/QmRCLtpTSWPmh4QK3TJ4rHCBSH7thYNZMWZG2PZvVrb8KJ/go-blockservice" + dag "gx/ipfs/QmUsnWBZaqpZ8i2wX37V2wC5hcB7VWB3zsUcNsyQ1Be5AX/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" blockstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" From dc7339793d39a3fc86af226ab8f17240e6cd7079 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 20 Feb 2019 17:19:54 -0800 Subject: [PATCH 2947/3817] gx: update go-bitswap and go-libp2p-kad-dht * go-bitswap: fix some race conditions. * go-libp2p-kad-dht: fix a goroutine leak. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@b5d1bfb927f22666795a9852738fd3638601c600 --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index eaa38217c..9cc559919 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/Qmccmovpo9isKeaaDzcxvT7mVJN1uKwn2xzSs1y8hb6PEs/go-merkledag" + dag "gx/ipfs/QmUsnWBZaqpZ8i2wX37V2wC5hcB7VWB3zsUcNsyQ1Be5AX/go-merkledag" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" blockstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" From 19fac3d8b187daf4af1fc206a6f18df44bd25e6a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 20 Feb 2019 20:29:06 -0800 Subject: [PATCH 2948/3817] gx: update go-cid License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@3868a4b01d44a72e0aab614d38247f868e058804 --- namesys/base.go | 4 ++-- namesys/cache.go | 2 +- namesys/dns.go | 4 ++-- namesys/dns_test.go | 2 +- namesys/interface.go | 4 ++-- namesys/ipns_resolver_validation_test.go | 12 ++++++------ namesys/namesys.go | 6 +++--- namesys/namesys_test.go | 8 ++++---- namesys/proquint.go | 4 ++-- namesys/publisher.go | 6 +++--- namesys/publisher_test.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 4 ++-- namesys/routing.go | 10 +++++----- 15 files changed, 38 insertions(+), 38 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 6f25cf2e6..5f81262c2 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -5,8 +5,8 @@ import ( "strings" "time" - opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" - path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" + path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" + opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 6ae568f54..31e16e3cd 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" + path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index c86729972..234b8257f 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -6,9 +6,9 @@ import ( "net" "strings" - opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" + path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" + opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" ) type LookupTXTFunc func(name string) (txt []string, err error) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 90da2ebdc..6c2700178 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" ) type mockDNS struct { diff --git a/namesys/interface.go b/namesys/interface.go index e1e6f3fd5..8fd2efaaa 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,9 +35,9 @@ import ( context "context" + path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" - path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" + opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 252fbb005..ab1851d0a 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,21 +6,21 @@ import ( "time" u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" + path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" pstore "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore" pstoremem "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore/pstoremem" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" - opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" - routing "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing" - ropts "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing/options" record "gx/ipfs/QmX1vqjTLTP6pepDi2uiaGxwKbQbem2PD88nGCZrwxKPEW/go-libp2p-record" - path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" - mockrouting "gx/ipfs/QmcjqHcsk8E1Gd8RbuaUawWC7ogDtaVcdjLvZF8ysCCiPn/go-ipfs-routing/mock" - offline "gx/ipfs/QmcjqHcsk8E1Gd8RbuaUawWC7ogDtaVcdjLvZF8ysCCiPn/go-ipfs-routing/offline" + mockrouting "gx/ipfs/QmbRWQPxtwacR1M8phDY5Y2jJbS96HXZAsK5dKD7s12Wob/go-ipfs-routing/mock" + offline "gx/ipfs/QmbRWQPxtwacR1M8phDY5Y2jJbS96HXZAsK5dKD7s12Wob/go-ipfs-routing/offline" + routing "gx/ipfs/QmcxZXMqFu4vjLQRfG2tAcg6DPQNurgZ2SQ5iQVk6dXQjn/go-libp2p-routing" + ropts "gx/ipfs/QmcxZXMqFu4vjLQRfG2tAcg6DPQNurgZ2SQ5iQVk6dXQjn/go-libp2p-routing/options" ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" testutil "gx/ipfs/QmeFVdhzY13YZPWxCiQvmLercrumFRoQZFQEYw2BtzyiQc/go-testutil" + opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 81418a9c8..36db21447 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -6,13 +6,13 @@ import ( "time" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" + path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" - opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - routing "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" + routing "gx/ipfs/QmcxZXMqFu4vjLQRfG2tAcg6DPQNurgZ2SQ5iQVk6dXQjn/go-libp2p-routing" + opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index c8cf228b1..f5a80f1e6 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -5,16 +5,16 @@ import ( "fmt" "testing" + path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" pstoremem "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore/pstoremem" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" - opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" - path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" - offroute "gx/ipfs/QmcjqHcsk8E1Gd8RbuaUawWC7ogDtaVcdjLvZF8ysCCiPn/go-ipfs-routing/offline" + "gx/ipfs/QmVytt7Vtb9jbGN2uNfEm15t78pBUjw1SS72nkJFcWYZwe/go-unixfs" + offroute "gx/ipfs/QmbRWQPxtwacR1M8phDY5Y2jJbS96HXZAsK5dKD7s12Wob/go-ipfs-routing/offline" ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" - "gx/ipfs/QmfKmRac17N7UmsV16vifHtB5Eqz3hTyKcu22Qb2oFoZyR/go-unixfs" + opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index 6ffd479c6..deaf717c9 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,9 +4,9 @@ import ( "context" "errors" - opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" + path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" - path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" + opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index 8c75694fe..382288142 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -8,16 +8,16 @@ import ( pin "github.com/ipfs/go-ipfs/pin" + path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dsquery "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" - routing "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing" - path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" + ft "gx/ipfs/QmVytt7Vtb9jbGN2uNfEm15t78pBUjw1SS72nkJFcWYZwe/go-unixfs" + routing "gx/ipfs/QmcxZXMqFu4vjLQRfG2tAcg6DPQNurgZ2SQ5iQVk6dXQjn/go-libp2p-routing" ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" pb "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns/pb" proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" - ft "gx/ipfs/QmfKmRac17N7UmsV16vifHtB5Eqz3hTyKcu22Qb2oFoZyR/go-unixfs" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 2c58f4c23..0e8899217 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -11,8 +11,8 @@ import ( peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" - dshelp "gx/ipfs/QmXx5R5qwsVxbhFogLNzU8A2tch9SZpJJgMUinfpHZsC9C/go-ipfs-ds-help" - mockrouting "gx/ipfs/QmcjqHcsk8E1Gd8RbuaUawWC7ogDtaVcdjLvZF8ysCCiPn/go-ipfs-routing/mock" + dshelp "gx/ipfs/QmXSEqXLCzpCByJU4wqbJ37TcBEj77FKMUWUP1qLh56847/go-ipfs-ds-help" + mockrouting "gx/ipfs/QmbRWQPxtwacR1M8phDY5Y2jJbS96HXZAsK5dKD7s12Wob/go-ipfs-routing/mock" ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" testutil "gx/ipfs/QmeFVdhzY13YZPWxCiQvmLercrumFRoQZFQEYw2BtzyiQc/go-testutil" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 504858805..939354379 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" + path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 9d2188c5e..3a1243ccf 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" + path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" pstore "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmcNGX5RaxPPCYwa6yGXM1EcUbrreTTinixLcYGmMwf1sx/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmcZUhA1xQo8meuYBFLcTHqQb2ogpDCTZUTfTrko7PUeHs/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 91f9a7bb0..9274861ee 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" + path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" - path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" - mockrouting "gx/ipfs/QmcjqHcsk8E1Gd8RbuaUawWC7ogDtaVcdjLvZF8ysCCiPn/go-ipfs-routing/mock" + mockrouting "gx/ipfs/QmbRWQPxtwacR1M8phDY5Y2jJbS96HXZAsK5dKD7s12Wob/go-ipfs-routing/mock" ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" testutil "gx/ipfs/QmeFVdhzY13YZPWxCiQvmLercrumFRoQZFQEYw2BtzyiQc/go-testutil" ) diff --git a/namesys/routing.go b/namesys/routing.go index b03f602b0..fb32811de 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,16 +5,16 @@ import ( "strings" "time" - cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" + path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" + cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" - opts "gx/ipfs/QmUM3JbzMPPVpsUvUcfCdmeU2tssrdVPnUn5E6RawFjDLC/interface-go-ipfs-core/options/namesys" - routing "gx/ipfs/QmWaDSNoSdSXU9b6udyaq9T8y6LkzMwqWxECznFqvtcTsk/go-libp2p-routing" - path "gx/ipfs/QmaVydE6qtiiMhRUraPrzL6dvtfHPNR6Y9JJZcTwDAw9dY/go-path" - dht "gx/ipfs/QmbMXtieVFmFfT5AzVEP9RUbq7dYiWCZjz8KWa5hsr8kSE/go-libp2p-kad-dht" + dht "gx/ipfs/QmbKppYPA1bmEGpqmA4QGZZNRGFcJo2Z4KjYRQWJDzbD8P/go-libp2p-kad-dht" logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" + routing "gx/ipfs/QmcxZXMqFu4vjLQRfG2tAcg6DPQNurgZ2SQ5iQVk6dXQjn/go-libp2p-routing" ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" pb "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns/pb" proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" + opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) From 43ebc5edb5fcdb02260c6e1c848d7e77a9289e4d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 20 Feb 2019 20:29:06 -0800 Subject: [PATCH 2949/3817] gx: update go-cid License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@1beea9fe241b682c1892bac0afa87fcb35a1af52 --- pinning/pinner/gc/gc.go | 14 +++++++------- pinning/pinner/pin.go | 6 +++--- pinning/pinner/pin_test.go | 10 +++++----- pinning/pinner/set.go | 6 +++--- pinning/pinner/set_test.go | 10 +++++----- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 3db3336f0..919c96f79 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,16 +8,16 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - bserv "gx/ipfs/QmRCLtpTSWPmh4QK3TJ4rHCBSH7thYNZMWZG2PZvVrb8KJ/go-blockservice" - dag "gx/ipfs/QmUsnWBZaqpZ8i2wX37V2wC5hcB7VWB3zsUcNsyQ1Be5AX/go-merkledag" + dag "gx/ipfs/QmScf5hnTEK8fDpRJAbcdMnKXpKUp1ytdymzXUbXDCFssp/go-merkledag" + bserv "gx/ipfs/QmXBjp9iatjaiEpRqrEZpUuKVWTc71vuSUYoPQ5rRQ3SUU/go-blockservice" - cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" - bstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" - offline "gx/ipfs/QmSz8kAe2JCKp2dWSG8gHSWnwSmne8YfRXTeK5HBmc9L7t/go-ipfs-exchange-offline" + cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" dstore "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - "gx/ipfs/QmYMQuypUbgsdNHmuCBSUJV6wdQVsBHRivNAp3efHJwZJD/go-verifcid" + bstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore" + ipld "gx/ipfs/QmZ6nzCLwGLVfRzYLpD7pW6UNuBDKEcA2imJtVpbEx2rxy/go-ipld-format" + offline "gx/ipfs/Qmb9fkAWgcyVRnFdXGqA6jcWGFj6q35oJjwRAYRhfEboGS/go-ipfs-exchange-offline" logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" + "gx/ipfs/QmcVd2ApQdbfaYPKhCjj4WoQuxk4CMxPqmNpijKmFLh6qa/go-verifcid" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index dce6df803..ecc0c7835 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,11 +10,11 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmUsnWBZaqpZ8i2wX37V2wC5hcB7VWB3zsUcNsyQ1Be5AX/go-merkledag" + mdag "gx/ipfs/QmScf5hnTEK8fDpRJAbcdMnKXpKUp1ytdymzXUbXDCFssp/go-merkledag" - cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" + cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" + ipld "gx/ipfs/QmZ6nzCLwGLVfRzYLpD7pW6UNuBDKEcA2imJtVpbEx2rxy/go-ipld-format" logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" ) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 84314e412..35efd2b14 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" - bs "gx/ipfs/QmRCLtpTSWPmh4QK3TJ4rHCBSH7thYNZMWZG2PZvVrb8KJ/go-blockservice" - mdag "gx/ipfs/QmUsnWBZaqpZ8i2wX37V2wC5hcB7VWB3zsUcNsyQ1Be5AX/go-merkledag" + mdag "gx/ipfs/QmScf5hnTEK8fDpRJAbcdMnKXpKUp1ytdymzXUbXDCFssp/go-merkledag" + bs "gx/ipfs/QmXBjp9iatjaiEpRqrEZpUuKVWTc71vuSUYoPQ5rRQ3SUU/go-blockservice" util "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" - cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" - offline "gx/ipfs/QmSz8kAe2JCKp2dWSG8gHSWnwSmne8YfRXTeK5HBmc9L7t/go-ipfs-exchange-offline" + cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" + blockstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore" + offline "gx/ipfs/Qmb9fkAWgcyVRnFdXGqA6jcWGFj6q35oJjwRAYRhfEboGS/go-ipfs-exchange-offline" ) var rand = util.NewTimeSeededRand() diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 7cebac76b..cd0e0ffc3 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,10 +10,10 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmUsnWBZaqpZ8i2wX37V2wC5hcB7VWB3zsUcNsyQ1Be5AX/go-merkledag" + "gx/ipfs/QmScf5hnTEK8fDpRJAbcdMnKXpKUp1ytdymzXUbXDCFssp/go-merkledag" - cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format" + cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" + ipld "gx/ipfs/QmZ6nzCLwGLVfRzYLpD7pW6UNuBDKEcA2imJtVpbEx2rxy/go-ipld-format" "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" ) diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 85e3807ee..bdc084391 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,14 +5,14 @@ import ( "encoding/binary" "testing" - bserv "gx/ipfs/QmRCLtpTSWPmh4QK3TJ4rHCBSH7thYNZMWZG2PZvVrb8KJ/go-blockservice" - dag "gx/ipfs/QmUsnWBZaqpZ8i2wX37V2wC5hcB7VWB3zsUcNsyQ1Be5AX/go-merkledag" + dag "gx/ipfs/QmScf5hnTEK8fDpRJAbcdMnKXpKUp1ytdymzXUbXDCFssp/go-merkledag" + bserv "gx/ipfs/QmXBjp9iatjaiEpRqrEZpUuKVWTc71vuSUYoPQ5rRQ3SUU/go-blockservice" - cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" - offline "gx/ipfs/QmSz8kAe2JCKp2dWSG8gHSWnwSmne8YfRXTeK5HBmc9L7t/go-ipfs-exchange-offline" + cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dsq "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" + blockstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore" + offline "gx/ipfs/Qmb9fkAWgcyVRnFdXGqA6jcWGFj6q35oJjwRAYRhfEboGS/go-ipfs-exchange-offline" ) func ignoreCids(_ cid.Cid) {} From e80725d6dc59637fd594badd94729f6680d99c47 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 20 Feb 2019 20:29:06 -0800 Subject: [PATCH 2950/3817] gx: update go-cid License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@17b80629e2234a2720307044f211bb50694f03fa --- filestore/filestore.go | 8 ++++---- filestore/filestore_test.go | 8 ++++---- filestore/fsrefstore.go | 10 +++++----- filestore/util.go | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 3b0deb43d..69021b7b7 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,12 +11,12 @@ import ( "context" "errors" - cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" + cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" dsq "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" - posinfo "gx/ipfs/QmUhHBdzfNb9FQPDtKwhghVoR3zwkbXzFJ1uJyEMYUpFSd/go-ipfs-posinfo" - blocks "gx/ipfs/QmWoXtvgC8inqFkAATB7cp2Dax7XBi9VDvSg9RCCZufmRk/go-block-format" + blockstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore" + blocks "gx/ipfs/QmYYLnAzR28nAQ4U5MFniLprnktu6eTFKibeNt96V21EZK/go-block-format" logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" + posinfo "gx/ipfs/QmdiZuFuiFD1Gbuu8PdqmsfrCR3z4QKSR2bN1NAvnJgTY7/go-ipfs-posinfo" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 9cc559919..62ea45403 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,12 +7,12 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmUsnWBZaqpZ8i2wX37V2wC5hcB7VWB3zsUcNsyQ1Be5AX/go-merkledag" + dag "gx/ipfs/QmScf5hnTEK8fDpRJAbcdMnKXpKUp1ytdymzXUbXDCFssp/go-merkledag" - cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" + cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - posinfo "gx/ipfs/QmUhHBdzfNb9FQPDtKwhghVoR3zwkbXzFJ1uJyEMYUpFSd/go-ipfs-posinfo" + blockstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore" + posinfo "gx/ipfs/QmdiZuFuiFD1Gbuu8PdqmsfrCR3z4QKSR2bN1NAvnJgTY7/go-ipfs-posinfo" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 81aea7e6c..190061017 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,15 +10,15 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" + cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dsns "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/namespace" dsq "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" - posinfo "gx/ipfs/QmUhHBdzfNb9FQPDtKwhghVoR3zwkbXzFJ1uJyEMYUpFSd/go-ipfs-posinfo" - blocks "gx/ipfs/QmWoXtvgC8inqFkAATB7cp2Dax7XBi9VDvSg9RCCZufmRk/go-block-format" - dshelp "gx/ipfs/QmXx5R5qwsVxbhFogLNzU8A2tch9SZpJJgMUinfpHZsC9C/go-ipfs-ds-help" + dshelp "gx/ipfs/QmXSEqXLCzpCByJU4wqbJ37TcBEj77FKMUWUP1qLh56847/go-ipfs-ds-help" + blockstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore" + blocks "gx/ipfs/QmYYLnAzR28nAQ4U5MFniLprnktu6eTFKibeNt96V21EZK/go-block-format" proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" + posinfo "gx/ipfs/QmdiZuFuiFD1Gbuu8PdqmsfrCR3z4QKSR2bN1NAvnJgTY7/go-ipfs-posinfo" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/util.go b/filestore/util.go index 3d4d4552b..45accb3a3 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,11 +6,11 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" - blockstore "gx/ipfs/QmRu7tiRnFk9mMPpVECQTBQJqXtmG132jJxA1w9A7TtpBz/go-ipfs-blockstore" + cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dsq "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" - dshelp "gx/ipfs/QmXx5R5qwsVxbhFogLNzU8A2tch9SZpJJgMUinfpHZsC9C/go-ipfs-ds-help" + dshelp "gx/ipfs/QmXSEqXLCzpCByJU4wqbJ37TcBEj77FKMUWUP1qLh56847/go-ipfs-ds-help" + blockstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore" ) // Status is used to identify the state of the block data referenced From 265faed09f52664f4155f176de18b0199a9e4cf8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 20 Feb 2019 20:41:00 -0800 Subject: [PATCH 2951/3817] ci: modernize This commit was moved from ipfs/go-ipfs-exchange-interface@2970ce71247805ebf47a1c76c823e83dad4252da --- exchange/Makefile | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/exchange/Makefile b/exchange/Makefile index 73f2841f6..20619413c 100644 --- a/exchange/Makefile +++ b/exchange/Makefile @@ -1,18 +1,11 @@ -all: deps gx: go get github.com/whyrusleeping/gx go get github.com/whyrusleeping/gx-go -deps: gx + +deps: gx gx --verbose install --global gx-go rewrite -test: deps - gx test -v -race -coverprofile=coverage.txt -covermode=atomic . -rw: - gx-go rewrite -rwundo: - gx-go rewrite --undo -publish: rwundo - gx publish -.PHONY: all gx deps test rw rwundo publish +publish: + gx-go rewrite --undo From 1fbf1ae368eacc8098ff48071f14ef5f273db296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 21 Feb 2019 16:03:42 +0100 Subject: [PATCH 2952/3817] Enable Travis This commit was moved from ipfs/interface-go-ipfs-core@4bf61d8680c9c9bd878b6deec2b946a5030c738e --- coreiface/Makefile | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 coreiface/Makefile diff --git a/coreiface/Makefile b/coreiface/Makefile new file mode 100644 index 000000000..89fc88d7f --- /dev/null +++ b/coreiface/Makefile @@ -0,0 +1,16 @@ +all: deps +gx: + go get github.com/whyrusleeping/gx + go get github.com/whyrusleeping/gx-go +deps: gx + gx --verbose install --global + gx-go rewrite +test: deps + gx test -v -race -coverprofile=coverage.txt -covermode=atomic . +rw: + gx-go rewrite +rwundo: + gx-go rewrite --undo +publish: rwundo + gx publish +.PHONY: all gx deps test rw rwundo publish From d497db124cf910eb5b4395dbbdd1a57d8cfb7126 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 27 Feb 2019 01:10:59 +0000 Subject: [PATCH 2953/3817] Gx Bubble. libp2p-6.0.38 License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-namesys@d15cb70753a77360abc8c87e9fdb736e205fdd46 --- namesys/base.go | 4 ++-- namesys/cache.go | 2 +- namesys/dns.go | 4 ++-- namesys/dns_test.go | 2 +- namesys/interface.go | 4 ++-- namesys/ipns_resolver_validation_test.go | 24 ++++++++++++------------ namesys/namesys.go | 8 ++++---- namesys/namesys_test.go | 14 +++++++------- namesys/proquint.go | 4 ++-- namesys/publisher.go | 12 ++++++------ namesys/publisher_test.go | 10 +++++----- namesys/republisher/repub.go | 6 +++--- namesys/republisher/repub_test.go | 6 +++--- namesys/resolve_test.go | 10 +++++----- namesys/routing.go | 14 +++++++------- 15 files changed, 62 insertions(+), 62 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index 5f81262c2..b8ca81076 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -5,8 +5,8 @@ import ( "strings" "time" - path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" - opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" + path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" + opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 31e16e3cd..3fa1ee50e 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" + path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 234b8257f..8f78dd3d4 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -6,9 +6,9 @@ import ( "net" "strings" - path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" + path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" + opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" ) type LookupTXTFunc func(name string) (txt []string, err error) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 6c2700178..12f7ae9a0 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" ) type mockDNS struct { diff --git a/namesys/interface.go b/namesys/interface.go index 8fd2efaaa..08228c1b1 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,9 +35,9 @@ import ( context "context" - path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" + path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index ab1851d0a..7f4f5107f 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,21 +6,21 @@ import ( "time" u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" - path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" - pstore "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore" - pstoremem "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore/pstoremem" + path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" - record "gx/ipfs/QmX1vqjTLTP6pepDi2uiaGxwKbQbem2PD88nGCZrwxKPEW/go-libp2p-record" - mockrouting "gx/ipfs/QmbRWQPxtwacR1M8phDY5Y2jJbS96HXZAsK5dKD7s12Wob/go-ipfs-routing/mock" - offline "gx/ipfs/QmbRWQPxtwacR1M8phDY5Y2jJbS96HXZAsK5dKD7s12Wob/go-ipfs-routing/offline" - routing "gx/ipfs/QmcxZXMqFu4vjLQRfG2tAcg6DPQNurgZ2SQ5iQVk6dXQjn/go-libp2p-routing" - ropts "gx/ipfs/QmcxZXMqFu4vjLQRfG2tAcg6DPQNurgZ2SQ5iQVk6dXQjn/go-libp2p-routing/options" - ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" - testutil "gx/ipfs/QmeFVdhzY13YZPWxCiQvmLercrumFRoQZFQEYw2BtzyiQc/go-testutil" - opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" + ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" + opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" + testutil "gx/ipfs/QmWapVoHjtKhn4MhvKNoPTkJKADFGACfXPFnt7combwp5W/go-testutil" + peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" + routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing" + ropts "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing/options" + mockrouting "gx/ipfs/QmZ22s3UgNi5vvYNH79jWJ63NPyQGiv4mdNaWCz4WKqMTZ/go-ipfs-routing/mock" + offline "gx/ipfs/QmZ22s3UgNi5vvYNH79jWJ63NPyQGiv4mdNaWCz4WKqMTZ/go-ipfs-routing/offline" + pstore "gx/ipfs/QmaCTz9RkrU13bm9kMB54f7atgqM4qkjDZpRwRoJiWXEqs/go-libp2p-peerstore" + pstoremem "gx/ipfs/QmaCTz9RkrU13bm9kMB54f7atgqM4qkjDZpRwRoJiWXEqs/go-libp2p-peerstore/pstoremem" + record "gx/ipfs/QmbeHtaBy9nZsW4cHRcvgVY4CnDhXudE2Dr6qDxS7yg9rX/go-libp2p-record" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 36db21447..e6122dc44 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,14 +5,14 @@ import ( "strings" "time" + path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" - path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" + opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" + peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" + routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - routing "gx/ipfs/QmcxZXMqFu4vjLQRfG2tAcg6DPQNurgZ2SQ5iQVk6dXQjn/go-libp2p-routing" - opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index f5a80f1e6..c3b1e9dc0 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -5,16 +5,16 @@ import ( "fmt" "testing" - path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" - pstoremem "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore/pstoremem" + path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" + "gx/ipfs/QmSbCXEwpsog4vBf53YntmGk9uHsgZNuU5oBKv3o2kkTSe/go-unixfs" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" - "gx/ipfs/QmVytt7Vtb9jbGN2uNfEm15t78pBUjw1SS72nkJFcWYZwe/go-unixfs" - offroute "gx/ipfs/QmbRWQPxtwacR1M8phDY5Y2jJbS96HXZAsK5dKD7s12Wob/go-ipfs-routing/offline" - ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" - opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" + ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" + opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" + peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" + offroute "gx/ipfs/QmZ22s3UgNi5vvYNH79jWJ63NPyQGiv4mdNaWCz4WKqMTZ/go-ipfs-routing/offline" + pstoremem "gx/ipfs/QmaCTz9RkrU13bm9kMB54f7atgqM4qkjDZpRwRoJiWXEqs/go-libp2p-peerstore/pstoremem" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index deaf717c9..a416bc2f3 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,9 +4,9 @@ import ( "context" "errors" - path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" + path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" + opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" - opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index 382288142..64bacc6fd 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -8,15 +8,15 @@ import ( pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" + path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" + ft "gx/ipfs/QmSbCXEwpsog4vBf53YntmGk9uHsgZNuU5oBKv3o2kkTSe/go-unixfs" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dsquery "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" - ft "gx/ipfs/QmVytt7Vtb9jbGN2uNfEm15t78pBUjw1SS72nkJFcWYZwe/go-unixfs" - routing "gx/ipfs/QmcxZXMqFu4vjLQRfG2tAcg6DPQNurgZ2SQ5iQVk6dXQjn/go-libp2p-routing" - ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" - pb "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns/pb" + ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" + pb "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns/pb" + peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" + routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing" proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 0e8899217..8b7481d59 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -6,15 +6,15 @@ import ( "testing" "time" - ma "gx/ipfs/QmNTCey11oxhb1AxDnQBRHtdhap6Ctud872NjAYPYYXPuc/go-multiaddr" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" + ma "gx/ipfs/QmTZBfrPJmjWsCvHEtX5FE6KimVJhsJg5sBbqEFYf4UZtL/go-multiaddr" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" + ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" + testutil "gx/ipfs/QmWapVoHjtKhn4MhvKNoPTkJKADFGACfXPFnt7combwp5W/go-testutil" dshelp "gx/ipfs/QmXSEqXLCzpCByJU4wqbJ37TcBEj77FKMUWUP1qLh56847/go-ipfs-ds-help" - mockrouting "gx/ipfs/QmbRWQPxtwacR1M8phDY5Y2jJbS96HXZAsK5dKD7s12Wob/go-ipfs-routing/mock" - ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" - testutil "gx/ipfs/QmeFVdhzY13YZPWxCiQvmLercrumFRoQZFQEYw2BtzyiQc/go-testutil" + peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" + mockrouting "gx/ipfs/QmZ22s3UgNi5vvYNH79jWJ63NPyQGiv4mdNaWCz4WKqMTZ/go-ipfs-routing/mock" ) type identity struct { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 939354379..4dfd2e491 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,15 +7,15 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" + path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" ic "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" + pb "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns/pb" + peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" - pb "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns/pb" proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" ) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 3a1243ccf..09c778e90 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" + path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" - pstore "gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmcZUhA1xQo8meuYBFLcTHqQb2ogpDCTZUTfTrko7PUeHs/go-libp2p/p2p/net/mock" + mocknet "gx/ipfs/QmWRUZmLb9qEpwuHTtrzbdE5LQxm64qftncw5o8tBVPobL/go-libp2p/p2p/net/mock" + pstore "gx/ipfs/QmaCTz9RkrU13bm9kMB54f7atgqM4qkjDZpRwRoJiWXEqs/go-libp2p-peerstore" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 9274861ee..4e9dcbd4a 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,13 +6,13 @@ import ( "testing" "time" - path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" - peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" + path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" - mockrouting "gx/ipfs/QmbRWQPxtwacR1M8phDY5Y2jJbS96HXZAsK5dKD7s12Wob/go-ipfs-routing/mock" - ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" - testutil "gx/ipfs/QmeFVdhzY13YZPWxCiQvmLercrumFRoQZFQEYw2BtzyiQc/go-testutil" + ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" + testutil "gx/ipfs/QmWapVoHjtKhn4MhvKNoPTkJKADFGACfXPFnt7combwp5W/go-testutil" + peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" + mockrouting "gx/ipfs/QmZ22s3UgNi5vvYNH79jWJ63NPyQGiv4mdNaWCz4WKqMTZ/go-ipfs-routing/mock" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index fb32811de..843061b21 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,16 +5,16 @@ import ( "strings" "time" - path "gx/ipfs/QmR3bNAtBoTN6xZ2HQNqpRQARcDoazH9jU6zKUNjFyQKWS/go-path" + dht "gx/ipfs/QmNqZEo6SByQ5zMn8FuWPKo17pGn26D8fruHxpRcog8usL/go-libp2p-kad-dht" + path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" - peer "gx/ipfs/QmTu65MVbemtUxJEWgsTtzv9Zv9P8rvmqNA4eG9TrTRGYc/go-libp2p-peer" - dht "gx/ipfs/QmbKppYPA1bmEGpqmA4QGZZNRGFcJo2Z4KjYRQWJDzbD8P/go-libp2p-kad-dht" + ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" + pb "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns/pb" + opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" + peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" + routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing" logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" - routing "gx/ipfs/QmcxZXMqFu4vjLQRfG2tAcg6DPQNurgZ2SQ5iQVk6dXQjn/go-libp2p-routing" - ipns "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns" - pb "gx/ipfs/QmdboayjE53q27kq6zGk5vkx4u7LDGdcbVoi5NXMCtfiKS/go-ipns/pb" proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" - opts "gx/ipfs/QmeWKXQfEqbtUDCiQBAHzSZDja9br5LdPgk8eHu86oJxgr/interface-go-ipfs-core/options/namesys" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) From 1c314606d744f30c09e016f4708921fc52bed777 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 27 Feb 2019 01:10:59 +0000 Subject: [PATCH 2954/3817] Gx Bubble. libp2p-6.0.38 License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-ipfs-pinner@fb8c0d56aded05ee794f5418beb7040989be82ee --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 919c96f79..71bda14a8 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmScf5hnTEK8fDpRJAbcdMnKXpKUp1ytdymzXUbXDCFssp/go-merkledag" - bserv "gx/ipfs/QmXBjp9iatjaiEpRqrEZpUuKVWTc71vuSUYoPQ5rRQ3SUU/go-blockservice" + dag "gx/ipfs/QmP9i4G9nRcfKBnpk1A7CwU7ppLkSn2j6vJeWn2AJ8rfcN/go-merkledag" + bserv "gx/ipfs/Qmdvbc3xsufJasP1idu6dZKiLLfEzuaLpuriCyUK7Aukje/go-blockservice" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" dstore "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index ecc0c7835..371f6171a 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmScf5hnTEK8fDpRJAbcdMnKXpKUp1ytdymzXUbXDCFssp/go-merkledag" + mdag "gx/ipfs/QmP9i4G9nRcfKBnpk1A7CwU7ppLkSn2j6vJeWn2AJ8rfcN/go-merkledag" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 35efd2b14..794552644 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - mdag "gx/ipfs/QmScf5hnTEK8fDpRJAbcdMnKXpKUp1ytdymzXUbXDCFssp/go-merkledag" - bs "gx/ipfs/QmXBjp9iatjaiEpRqrEZpUuKVWTc71vuSUYoPQ5rRQ3SUU/go-blockservice" + mdag "gx/ipfs/QmP9i4G9nRcfKBnpk1A7CwU7ppLkSn2j6vJeWn2AJ8rfcN/go-merkledag" + bs "gx/ipfs/Qmdvbc3xsufJasP1idu6dZKiLLfEzuaLpuriCyUK7Aukje/go-blockservice" util "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index cd0e0ffc3..55103a736 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmScf5hnTEK8fDpRJAbcdMnKXpKUp1ytdymzXUbXDCFssp/go-merkledag" + "gx/ipfs/QmP9i4G9nRcfKBnpk1A7CwU7ppLkSn2j6vJeWn2AJ8rfcN/go-merkledag" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" ipld "gx/ipfs/QmZ6nzCLwGLVfRzYLpD7pW6UNuBDKEcA2imJtVpbEx2rxy/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index bdc084391..7976fe82e 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmScf5hnTEK8fDpRJAbcdMnKXpKUp1ytdymzXUbXDCFssp/go-merkledag" - bserv "gx/ipfs/QmXBjp9iatjaiEpRqrEZpUuKVWTc71vuSUYoPQ5rRQ3SUU/go-blockservice" + dag "gx/ipfs/QmP9i4G9nRcfKBnpk1A7CwU7ppLkSn2j6vJeWn2AJ8rfcN/go-merkledag" + bserv "gx/ipfs/Qmdvbc3xsufJasP1idu6dZKiLLfEzuaLpuriCyUK7Aukje/go-blockservice" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" From 75ac23d9f73d1b87d5d610bb79e4f33e6838cd83 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 27 Feb 2019 01:10:59 +0000 Subject: [PATCH 2955/3817] Gx Bubble. libp2p-6.0.38 License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-filestore@88923c9f23c55554d7a565943fba0a54538373f9 --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 62ea45403..8dde19de0 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmScf5hnTEK8fDpRJAbcdMnKXpKUp1ytdymzXUbXDCFssp/go-merkledag" + dag "gx/ipfs/QmP9i4G9nRcfKBnpk1A7CwU7ppLkSn2j6vJeWn2AJ8rfcN/go-merkledag" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" From 0d7b0f46acf5f8e0a5757be5c33ca389e46121b2 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 27 Feb 2019 05:30:15 +0000 Subject: [PATCH 2956/3817] Fix a problem with go-libp2p-kad-dht License: MIT Signed-off-by: Hector Sanjuan This commit was moved from ipfs/go-namesys@2c5e8411f746c95aa8efcd762c9ec41000d91b5a --- namesys/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/routing.go b/namesys/routing.go index 843061b21..5f2a731a8 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,9 +5,9 @@ import ( "strings" "time" - dht "gx/ipfs/QmNqZEo6SByQ5zMn8FuWPKo17pGn26D8fruHxpRcog8usL/go-libp2p-kad-dht" path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" + dht "gx/ipfs/QmUTc27ifFbaTWZBCKFxuMfWfB1jy88MtYtB37vZ9saaXo/go-libp2p-kad-dht" ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" pb "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns/pb" opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" From 04e8baf657f98ac130f5eb22e9aeb4afbbe7df50 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 27 Feb 2019 19:08:11 +0100 Subject: [PATCH 2957/3817] Remove verifcid as it is handled in go-cid This commit was moved from ipfs/go-blockservice@f4c929d46b6d7ec6a31a8f5cbdc33b72ab0fbf5f --- blockservice/blockservice.go | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 3b5a1df6b..23dcd0c33 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -14,7 +14,6 @@ import ( blockstore "github.com/ipfs/go-ipfs-blockstore" exchange "github.com/ipfs/go-ipfs-exchange-interface" logging "github.com/ipfs/go-log" - "github.com/ipfs/go-verifcid" ) var log = logging.Logger("blockservice") @@ -131,11 +130,6 @@ func NewSession(ctx context.Context, bs BlockService) *Session { // TODO pass a context into this if the remote.HasBlock is going to remain here. func (s *blockService) AddBlock(o blocks.Block) error { c := o.Cid() - // hash security - err := verifcid.ValidateCid(c) - if err != nil { - return err - } if s.checkFirst { if has, err := s.blockstore.Has(c); has || err != nil { return err @@ -156,13 +150,6 @@ func (s *blockService) AddBlock(o blocks.Block) error { } func (s *blockService) AddBlocks(bs []blocks.Block) error { - // hash security - for _, b := range bs { - err := verifcid.ValidateCid(b.Cid()) - if err != nil { - return err - } - } var toput []blocks.Block if s.checkFirst { toput = make([]blocks.Block, 0, len(bs)) @@ -211,11 +198,6 @@ func (s *blockService) getExchange() exchange.Fetcher { } func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget func() exchange.Fetcher) (blocks.Block, error) { - err := verifcid.ValidateCid(c) // hash security - if err != nil { - return nil, err - } - block, err := bs.Get(c) if err == nil { return block, nil @@ -259,18 +241,6 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget go func() { defer close(out) - k := 0 - for _, c := range ks { - // hash security - if err := verifcid.ValidateCid(c); err == nil { - ks[k] = c - k++ - } else { - log.Errorf("unsafe CID (%s) passed to blockService.GetBlocks: %s", c, err) - } - } - ks = ks[:k] - var misses []cid.Cid for _, c := range ks { hit, err := bs.Get(c) From a743193b9afc05fd5d9ba97efc6408517334bada Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 27 Feb 2019 12:39:24 -0800 Subject: [PATCH 2958/3817] gx: update go-ipfs-cmds, go-bitswap, go-libp2p-kad-dht, and go-mplex Fixes the latest batch of bugs found in RC testing. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@7bcfde87b7c13fb848c2989bf325876ed8f3ccf2 --- pinning/pinner/gc/gc.go | 4 ++-- pinning/pinner/pin.go | 2 +- pinning/pinner/pin_test.go | 4 ++-- pinning/pinner/set.go | 2 +- pinning/pinner/set_test.go | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 71bda14a8..97ea9a81e 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -8,8 +8,8 @@ import ( "strings" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmP9i4G9nRcfKBnpk1A7CwU7ppLkSn2j6vJeWn2AJ8rfcN/go-merkledag" - bserv "gx/ipfs/Qmdvbc3xsufJasP1idu6dZKiLLfEzuaLpuriCyUK7Aukje/go-blockservice" + dag "gx/ipfs/QmPJNbVw8o3ohC43ppSXyNXwYKsWShG4zygnirHptfbHri/go-merkledag" + bserv "gx/ipfs/QmUEXNytX2q9g9xtdfHRVYfsvjw5V9FQ32vE9ZRYFAxFoy/go-blockservice" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" dstore "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 371f6171a..e60dc18be 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmP9i4G9nRcfKBnpk1A7CwU7ppLkSn2j6vJeWn2AJ8rfcN/go-merkledag" + mdag "gx/ipfs/QmPJNbVw8o3ohC43ppSXyNXwYKsWShG4zygnirHptfbHri/go-merkledag" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 794552644..775800372 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - mdag "gx/ipfs/QmP9i4G9nRcfKBnpk1A7CwU7ppLkSn2j6vJeWn2AJ8rfcN/go-merkledag" - bs "gx/ipfs/Qmdvbc3xsufJasP1idu6dZKiLLfEzuaLpuriCyUK7Aukje/go-blockservice" + mdag "gx/ipfs/QmPJNbVw8o3ohC43ppSXyNXwYKsWShG4zygnirHptfbHri/go-merkledag" + bs "gx/ipfs/QmUEXNytX2q9g9xtdfHRVYfsvjw5V9FQ32vE9ZRYFAxFoy/go-blockservice" util "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index 55103a736..f3fcac1f7 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,7 +10,7 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmP9i4G9nRcfKBnpk1A7CwU7ppLkSn2j6vJeWn2AJ8rfcN/go-merkledag" + "gx/ipfs/QmPJNbVw8o3ohC43ppSXyNXwYKsWShG4zygnirHptfbHri/go-merkledag" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" ipld "gx/ipfs/QmZ6nzCLwGLVfRzYLpD7pW6UNuBDKEcA2imJtVpbEx2rxy/go-ipld-format" diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index 7976fe82e..ea6df844e 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmP9i4G9nRcfKBnpk1A7CwU7ppLkSn2j6vJeWn2AJ8rfcN/go-merkledag" - bserv "gx/ipfs/Qmdvbc3xsufJasP1idu6dZKiLLfEzuaLpuriCyUK7Aukje/go-blockservice" + dag "gx/ipfs/QmPJNbVw8o3ohC43ppSXyNXwYKsWShG4zygnirHptfbHri/go-merkledag" + bserv "gx/ipfs/QmUEXNytX2q9g9xtdfHRVYfsvjw5V9FQ32vE9ZRYFAxFoy/go-blockservice" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" From 9397bfa012b29c80430821655e9fb4f259d558a3 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 27 Feb 2019 12:39:24 -0800 Subject: [PATCH 2959/3817] gx: update go-ipfs-cmds, go-bitswap, go-libp2p-kad-dht, and go-mplex Fixes the latest batch of bugs found in RC testing. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@f64185838ddade6885d0415e4478c1bc7d9ed5eb --- namesys/base.go | 4 ++-- namesys/cache.go | 2 +- namesys/dns.go | 4 ++-- namesys/dns_test.go | 2 +- namesys/interface.go | 4 ++-- namesys/ipns_resolver_validation_test.go | 4 ++-- namesys/namesys.go | 4 ++-- namesys/namesys_test.go | 6 +++--- namesys/proquint.go | 4 ++-- namesys/publisher.go | 4 ++-- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 2 +- namesys/routing.go | 6 +++--- 14 files changed, 26 insertions(+), 26 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index b8ca81076..ebeb86c31 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -5,8 +5,8 @@ import ( "strings" "time" - path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" - opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" + path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" + opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index 3fa1ee50e..d27775669 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" + path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 8f78dd3d4..2c31fcae7 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -6,8 +6,8 @@ import ( "net" "strings" - path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" - opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" + path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" + opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" ) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 12f7ae9a0..460afb023 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" ) type mockDNS struct { diff --git a/namesys/interface.go b/namesys/interface.go index 08228c1b1..04ca1eeb7 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,9 +35,9 @@ import ( context "context" - path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" + path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 7f4f5107f..3c583a973 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -6,13 +6,13 @@ import ( "time" u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" - path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" + path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" - opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" testutil "gx/ipfs/QmWapVoHjtKhn4MhvKNoPTkJKADFGACfXPFnt7combwp5W/go-testutil" + opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing" ropts "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing/options" diff --git a/namesys/namesys.go b/namesys/namesys.go index e6122dc44..b8ccdaafb 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,11 +5,11 @@ import ( "strings" "time" - path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" + path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing" isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index c3b1e9dc0..3da16d238 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -5,16 +5,16 @@ import ( "fmt" "testing" - path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" - "gx/ipfs/QmSbCXEwpsog4vBf53YntmGk9uHsgZNuU5oBKv3o2kkTSe/go-unixfs" + path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" - opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" offroute "gx/ipfs/QmZ22s3UgNi5vvYNH79jWJ63NPyQGiv4mdNaWCz4WKqMTZ/go-ipfs-routing/offline" pstoremem "gx/ipfs/QmaCTz9RkrU13bm9kMB54f7atgqM4qkjDZpRwRoJiWXEqs/go-libp2p-peerstore/pstoremem" + "gx/ipfs/QmcYUTQ7tBZeH1CLsZM2S3xhMEZdvUgXvbjhpMsLDpk3oJ/go-unixfs" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index a416bc2f3..e36865903 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,8 +4,8 @@ import ( "context" "errors" - path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" - opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" + path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" + opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index 64bacc6fd..126bcd1ce 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -8,8 +8,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" - ft "gx/ipfs/QmSbCXEwpsog4vBf53YntmGk9uHsgZNuU5oBKv3o2kkTSe/go-unixfs" + path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dsquery "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" @@ -17,6 +16,7 @@ import ( pb "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns/pb" peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing" + ft "gx/ipfs/QmcYUTQ7tBZeH1CLsZM2S3xhMEZdvUgXvbjhpMsLDpk3oJ/go-unixfs" proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 4dfd2e491..c7a7f4442 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,7 +7,7 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" + path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 09c778e90..1f582a44d 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,10 +10,10 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" + path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" + mocknet "gx/ipfs/QmRxk6AUaGaKCfzS1xSNRojiAPd7h2ih8GuCdjJBF3Y6GK/go-libp2p/p2p/net/mock" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - mocknet "gx/ipfs/QmWRUZmLb9qEpwuHTtrzbdE5LQxm64qftncw5o8tBVPobL/go-libp2p/p2p/net/mock" pstore "gx/ipfs/QmaCTz9RkrU13bm9kMB54f7atgqM4qkjDZpRwRoJiWXEqs/go-libp2p-peerstore" ) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 4e9dcbd4a..80eb7d83f 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" + path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" diff --git a/namesys/routing.go b/namesys/routing.go index 5f2a731a8..315a281bb 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,15 +5,15 @@ import ( "strings" "time" - path "gx/ipfs/QmQ8Y8pKbrdAFuoHnnduWp81qS5LESWATnjmUEqhopRbJo/go-path" + path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" - dht "gx/ipfs/QmUTc27ifFbaTWZBCKFxuMfWfB1jy88MtYtB37vZ9saaXo/go-libp2p-kad-dht" ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" pb "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns/pb" - opts "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options/namesys" + opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing" logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" + dht "gx/ipfs/QmdR6WN3TUEAVQ9KWE2UiFJikWTbUvgBJay6mjB4yUJebq/go-libp2p-kad-dht" proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" ) From 6f8b262d1281225ab1a0fa427fcf965fef81ce60 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 27 Feb 2019 12:39:24 -0800 Subject: [PATCH 2960/3817] gx: update go-ipfs-cmds, go-bitswap, go-libp2p-kad-dht, and go-mplex Fixes the latest batch of bugs found in RC testing. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@fdbb00728fa8c795ce35b4a0263b20be3e4d8294 --- filestore/filestore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 8dde19de0..b6fdf3507 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,7 +7,7 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmP9i4G9nRcfKBnpk1A7CwU7ppLkSn2j6vJeWn2AJ8rfcN/go-merkledag" + dag "gx/ipfs/QmPJNbVw8o3ohC43ppSXyNXwYKsWShG4zygnirHptfbHri/go-merkledag" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" From ffaaac69e0faf7a3f2048946268806fdb29b2896 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 27 Feb 2019 13:40:38 -0800 Subject: [PATCH 2961/3817] don't clear the cached node when changing the CID builder This commit was moved from ipfs/go-merkledag@5f44a52a8031162b20688b77cb32e6a7f403aa58 --- ipld/merkledag/node.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index fb96fc65e..09789040d 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -78,7 +78,6 @@ func (n *ProtoNode) SetCidBuilder(builder cid.Builder) { n.builder = v0CidPrefix } else { n.builder = builder.WithCodec(cid.DagProtobuf) - n.encoded = nil n.cached = cid.Undef } } From 4deccc7af306cf1c1fd2d0973ca141a1df3ffadc Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 27 Feb 2019 13:41:01 -0800 Subject: [PATCH 2962/3817] don't clear the cache CID when decoding a cached protobuf node This commit was moved from ipfs/go-merkledag@3bdd47fcdf7620979157eea03fa34ad3a8277fb4 --- ipld/merkledag/coding.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 4b1738bfd..8b4192813 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -131,7 +131,7 @@ func DecodeProtobufBlock(b blocks.Block) (ipld.Node, error) { } decnd.cached = c - decnd.SetCidBuilder(c.Prefix()) + decnd.builder = c.Prefix() return decnd, nil } From 367840e237d1c5d7b906f1ef9bd51ece01bb4278 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 28 Feb 2019 18:46:16 +0100 Subject: [PATCH 2963/3817] Add gomod, use multiformats/go-base32, change travis This commit was moved from ipfs/go-ipfs-ds-help@4a69beb72c1b4e6e7ed62c5c5cbc1bd7a3d219b2 --- datastore/dshelp/key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index b4fff9891..1f47023fe 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -5,7 +5,7 @@ package dshelp import ( cid "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" - "github.com/whyrusleeping/base32" + "github.com/multiformats/go-base32" ) // NewKeyFromBinary creates a new key from a byte slice. From fdca4bff9e1ab55eecb93a3ad2e6e6adf145ea84 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 28 Feb 2019 11:17:34 -0800 Subject: [PATCH 2964/3817] Revert "Remove verifcid as it is handled in go-cid" This commit was moved from ipfs/go-blockservice@0a3d4f7b4befaecaa3677f407c51b8aa40c48a6e --- blockservice/blockservice.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 23dcd0c33..3b5a1df6b 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -14,6 +14,7 @@ import ( blockstore "github.com/ipfs/go-ipfs-blockstore" exchange "github.com/ipfs/go-ipfs-exchange-interface" logging "github.com/ipfs/go-log" + "github.com/ipfs/go-verifcid" ) var log = logging.Logger("blockservice") @@ -130,6 +131,11 @@ func NewSession(ctx context.Context, bs BlockService) *Session { // TODO pass a context into this if the remote.HasBlock is going to remain here. func (s *blockService) AddBlock(o blocks.Block) error { c := o.Cid() + // hash security + err := verifcid.ValidateCid(c) + if err != nil { + return err + } if s.checkFirst { if has, err := s.blockstore.Has(c); has || err != nil { return err @@ -150,6 +156,13 @@ func (s *blockService) AddBlock(o blocks.Block) error { } func (s *blockService) AddBlocks(bs []blocks.Block) error { + // hash security + for _, b := range bs { + err := verifcid.ValidateCid(b.Cid()) + if err != nil { + return err + } + } var toput []blocks.Block if s.checkFirst { toput = make([]blocks.Block, 0, len(bs)) @@ -198,6 +211,11 @@ func (s *blockService) getExchange() exchange.Fetcher { } func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget func() exchange.Fetcher) (blocks.Block, error) { + err := verifcid.ValidateCid(c) // hash security + if err != nil { + return nil, err + } + block, err := bs.Get(c) if err == nil { return block, nil @@ -241,6 +259,18 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget go func() { defer close(out) + k := 0 + for _, c := range ks { + // hash security + if err := verifcid.ValidateCid(c); err == nil { + ks[k] = c + k++ + } else { + log.Errorf("unsafe CID (%s) passed to blockService.GetBlocks: %s", c, err) + } + } + ks = ks[:k] + var misses []cid.Cid for _, c := range ks { hit, err := bs.Get(c) From 916e539eccbdafc65f2b177eeb1b7040e0f795dc Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 2 Mar 2019 18:41:53 +0100 Subject: [PATCH 2965/3817] Fmt options.go License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-mfs@f2af04af36884d527059509ec84482a82cd011e5 --- mfs/options.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mfs/options.go b/mfs/options.go index 1edb99e11..6bdcd7100 100644 --- a/mfs/options.go +++ b/mfs/options.go @@ -1,7 +1,7 @@ package mfs type Flags struct { - Read bool + Read bool Write bool - Sync bool + Sync bool } From 9c3cf70c5f23696257374f5f42212364d706d427 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 4 Mar 2019 20:11:38 -0800 Subject: [PATCH 2966/3817] tweak the Ls interface 1. Avoid `ipld.Link`. This is a protodag specific thing that will go away in future IPLD versions. 2. Avoid exposing the underlying file types. The user shouldn't care if they're dealing with a hamt, etc. 3. Add a field for a symlink's target. 4. Rename LsLink to DirEntry to better this type's role. This commit was moved from ipfs/interface-go-ipfs-core@dbee8cc1adb3b53a10ea33add0584b030f92106a --- coreiface/tests/unixfs.go | 23 +++++++++++------------ coreiface/unixfs.go | 36 ++++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index bcb5331d5..a0c33c0b0 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -750,26 +750,25 @@ func (tp *provider) TestLs(t *testing.T) { t.Error(err) } - links, err := api.Unixfs().Ls(ctx, p) + entries, err := api.Unixfs().Ls(ctx, p) if err != nil { t.Error(err) } - linkRes := <-links - if linkRes.Err != nil { - t.Fatal(linkRes.Err) + entry := <-entries + if entry.Err != nil { + t.Fatal(entry.Err) } - link := linkRes.Link - if linkRes.Size != 15 { - t.Fatalf("expected size = 15, got %d", link.Size) + if entry.Size != 15 { + t.Fatalf("expected size = 15, got %d", entry.Size) } - if link.Name != "name-of-file" { - t.Fatalf("expected name = name-of-file, got %s", link.Name) + if entry.Name != "name-of-file" { + t.Fatalf("expected name = name-of-file, got %s", entry.Name) } - if link.Cid.String() != "QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr" { - t.Fatalf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", link.Cid) + if entry.Cid.String() != "QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr" { + t.Fatalf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", entry.Cid) } - if l, ok := <-links; ok { + if l, ok := <-entries; ok { t.Errorf("didn't expect a second link") if l.Err != nil { t.Error(l.Err) diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 5aae00dc4..bdf08b5c3 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -4,9 +4,8 @@ import ( "context" "github.com/ipfs/interface-go-ipfs-core/options" - "github.com/ipfs/go-ipfs-files" - ipld "github.com/ipfs/go-ipld-format" - "github.com/ipfs/go-unixfs" + cid "github.com/ipfs/go-cid" + files "github.com/ipfs/go-ipfs-files" ) type AddEvent struct { @@ -16,21 +15,30 @@ type AddEvent struct { Size string `json:",omitempty"` } +// FileType is an enum of possible UnixFS file types. type FileType int32 const ( - TRaw = FileType(unixfs.TRaw) - TFile = FileType(unixfs.TFile) - TDirectory = FileType(unixfs.TDirectory) - TMetadata = FileType(unixfs.TMetadata) - TSymlink = FileType(unixfs.TSymlink) - THAMTShard = FileType(unixfs.THAMTShard) + // TUnknown means the file type isn't known (e.g., it hasn't been + // resolved). + TUnknown FileType = iota + // TFile is a regular file. + TFile + // TDirectory is a directory. + TDirectory + // TSymlink is a symlink. + TSymlink ) -type LsLink struct { - Link *ipld.Link - Size uint64 - Type FileType +// DirEntry is a directory entry returned by `Ls`. +type DirEntry struct { + Name string + Cid cid.Cid + + // Only filled when asked to resolve the directory entry. + Size uint64 // The size of the file in bytes (or the size of the symlink). + Type FileType // The type of the file. + Target Path // The symlink target (if a symlink). Err error } @@ -51,5 +59,5 @@ type UnixfsAPI interface { // Ls returns the list of links in a directory. Links aren't guaranteed to be // returned in order - Ls(context.Context, Path, ...options.UnixfsLsOption) (<-chan LsLink, error) + Ls(context.Context, Path, ...options.UnixfsLsOption) (<-chan DirEntry, error) } From 528e82bcf9b1dd1cf9ebe33ee5c256268792d41c Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 2 Mar 2019 19:26:36 +0100 Subject: [PATCH 2967/3817] gx: unrewrite License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-pinner@8721ce0afa4cfd9f3e6b1a896c13d9f8b5528035 --- pinning/pinner/gc/gc.go | 20 ++++++++++---------- pinning/pinner/internal/pb/header.pb.go | 2 +- pinning/pinner/pin.go | 10 +++++----- pinning/pinner/pin_test.go | 18 +++++++++--------- pinning/pinner/set.go | 8 ++++---- pinning/pinner/set_test.go | 16 ++++++++-------- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 97ea9a81e..9234d4368 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -7,17 +7,17 @@ import ( "fmt" "strings" + bserv "github.com/ipfs/go-blockservice" pin "github.com/ipfs/go-ipfs/pin" - dag "gx/ipfs/QmPJNbVw8o3ohC43ppSXyNXwYKsWShG4zygnirHptfbHri/go-merkledag" - bserv "gx/ipfs/QmUEXNytX2q9g9xtdfHRVYfsvjw5V9FQ32vE9ZRYFAxFoy/go-blockservice" - - cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" - dstore "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - bstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore" - ipld "gx/ipfs/QmZ6nzCLwGLVfRzYLpD7pW6UNuBDKEcA2imJtVpbEx2rxy/go-ipld-format" - offline "gx/ipfs/Qmb9fkAWgcyVRnFdXGqA6jcWGFj6q35oJjwRAYRhfEboGS/go-ipfs-exchange-offline" - logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" - "gx/ipfs/QmcVd2ApQdbfaYPKhCjj4WoQuxk4CMxPqmNpijKmFLh6qa/go-verifcid" + dag "github.com/ipfs/go-merkledag" + + cid "github.com/ipfs/go-cid" + dstore "github.com/ipfs/go-datastore" + bstore "github.com/ipfs/go-ipfs-blockstore" + offline "github.com/ipfs/go-ipfs-exchange-offline" + ipld "github.com/ipfs/go-ipld-format" + logging "github.com/ipfs/go-log" + "github.com/ipfs/go-verifcid" ) var log = logging.Logger("gc") diff --git a/pinning/pinner/internal/pb/header.pb.go b/pinning/pinner/internal/pb/header.pb.go index dd215e126..71196b263 100644 --- a/pinning/pinner/internal/pb/header.pb.go +++ b/pinning/pinner/internal/pb/header.pb.go @@ -6,7 +6,7 @@ package pb import ( encoding_binary "encoding/binary" fmt "fmt" - proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" + proto "github.com/gogo/protobuf/proto" io "io" math "math" ) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index e60dc18be..24dbf4653 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -10,12 +10,12 @@ import ( "time" "github.com/ipfs/go-ipfs/dagutils" - mdag "gx/ipfs/QmPJNbVw8o3ohC43ppSXyNXwYKsWShG4zygnirHptfbHri/go-merkledag" + mdag "github.com/ipfs/go-merkledag" - cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" - ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - ipld "gx/ipfs/QmZ6nzCLwGLVfRzYLpD7pW6UNuBDKEcA2imJtVpbEx2rxy/go-ipld-format" - logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + ipld "github.com/ipfs/go-ipld-format" + logging "github.com/ipfs/go-log" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 775800372..6f9914ef3 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -5,15 +5,15 @@ import ( "testing" "time" - mdag "gx/ipfs/QmPJNbVw8o3ohC43ppSXyNXwYKsWShG4zygnirHptfbHri/go-merkledag" - bs "gx/ipfs/QmUEXNytX2q9g9xtdfHRVYfsvjw5V9FQ32vE9ZRYFAxFoy/go-blockservice" - - util "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" - cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" - ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" - blockstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore" - offline "gx/ipfs/Qmb9fkAWgcyVRnFdXGqA6jcWGFj6q35oJjwRAYRhfEboGS/go-ipfs-exchange-offline" + bs "github.com/ipfs/go-blockservice" + mdag "github.com/ipfs/go-merkledag" + + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + blockstore "github.com/ipfs/go-ipfs-blockstore" + offline "github.com/ipfs/go-ipfs-exchange-offline" + util "github.com/ipfs/go-ipfs-util" ) var rand = util.NewTimeSeededRand() diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index f3fcac1f7..b050c31c4 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -10,11 +10,11 @@ import ( "sort" "github.com/ipfs/go-ipfs/pin/internal/pb" - "gx/ipfs/QmPJNbVw8o3ohC43ppSXyNXwYKsWShG4zygnirHptfbHri/go-merkledag" + "github.com/ipfs/go-merkledag" - cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" - ipld "gx/ipfs/QmZ6nzCLwGLVfRzYLpD7pW6UNuBDKEcA2imJtVpbEx2rxy/go-ipld-format" - "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" + "github.com/gogo/protobuf/proto" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index ea6df844e..d9a573c5f 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -5,14 +5,14 @@ import ( "encoding/binary" "testing" - dag "gx/ipfs/QmPJNbVw8o3ohC43ppSXyNXwYKsWShG4zygnirHptfbHri/go-merkledag" - bserv "gx/ipfs/QmUEXNytX2q9g9xtdfHRVYfsvjw5V9FQ32vE9ZRYFAxFoy/go-blockservice" - - cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" - ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - dsq "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" - blockstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore" - offline "gx/ipfs/Qmb9fkAWgcyVRnFdXGqA6jcWGFj6q35oJjwRAYRhfEboGS/go-ipfs-exchange-offline" + bserv "github.com/ipfs/go-blockservice" + dag "github.com/ipfs/go-merkledag" + + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + dsq "github.com/ipfs/go-datastore/query" + blockstore "github.com/ipfs/go-ipfs-blockstore" + offline "github.com/ipfs/go-ipfs-exchange-offline" ) func ignoreCids(_ cid.Cid) {} From 33e89f90a8cda173c71b5ef00157c21486bf7fb2 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 2 Mar 2019 19:26:36 +0100 Subject: [PATCH 2968/3817] gx: unrewrite License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-namesys@c204b8318ffe68ebc3c7c50b2a3bf1f83cc4692d --- namesys/base.go | 4 +-- namesys/cache.go | 2 +- namesys/dns.go | 6 ++--- namesys/dns_test.go | 2 +- namesys/interface.go | 6 ++--- namesys/ipns_resolver_validation_test.go | 32 ++++++++++++------------ namesys/namesys.go | 18 ++++++------- namesys/namesys_test.go | 20 +++++++-------- namesys/proquint.go | 6 ++--- namesys/publisher.go | 22 ++++++++-------- namesys/publisher_test.go | 18 ++++++------- namesys/republisher/repub.go | 20 +++++++-------- namesys/republisher/repub_test.go | 8 +++--- namesys/resolve_test.go | 14 +++++------ namesys/routing.go | 22 ++++++++-------- 15 files changed, 100 insertions(+), 100 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index ebeb86c31..27cc38f88 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -5,8 +5,8 @@ import ( "strings" "time" - path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" - opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" + path "github.com/ipfs/go-path" + opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" ) type onceResult struct { diff --git a/namesys/cache.go b/namesys/cache.go index d27775669..4a5cb5113 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -3,7 +3,7 @@ package namesys import ( "time" - path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" + path "github.com/ipfs/go-path" ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { diff --git a/namesys/dns.go b/namesys/dns.go index 2c31fcae7..931edec00 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -6,9 +6,9 @@ import ( "net" "strings" - path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" - opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" - isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" + path "github.com/ipfs/go-path" + opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" + isd "github.com/jbenet/go-is-domain" ) type LookupTXTFunc func(name string) (txt []string, err error) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 460afb023..8d53887be 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" + opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" ) type mockDNS struct { diff --git a/namesys/interface.go b/namesys/interface.go index 04ca1eeb7..4db95ab3c 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -35,9 +35,9 @@ import ( context "context" - path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" - ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" + path "github.com/ipfs/go-path" + opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" + ci "github.com/libp2p/go-libp2p-crypto" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 3c583a973..71335b522 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,22 +5,22 @@ import ( "testing" "time" - u "gx/ipfs/QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz/go-ipfs-util" - path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" - ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" - ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" - testutil "gx/ipfs/QmWapVoHjtKhn4MhvKNoPTkJKADFGACfXPFnt7combwp5W/go-testutil" - opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" - peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" - routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing" - ropts "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing/options" - mockrouting "gx/ipfs/QmZ22s3UgNi5vvYNH79jWJ63NPyQGiv4mdNaWCz4WKqMTZ/go-ipfs-routing/mock" - offline "gx/ipfs/QmZ22s3UgNi5vvYNH79jWJ63NPyQGiv4mdNaWCz4WKqMTZ/go-ipfs-routing/offline" - pstore "gx/ipfs/QmaCTz9RkrU13bm9kMB54f7atgqM4qkjDZpRwRoJiWXEqs/go-libp2p-peerstore" - pstoremem "gx/ipfs/QmaCTz9RkrU13bm9kMB54f7atgqM4qkjDZpRwRoJiWXEqs/go-libp2p-peerstore/pstoremem" - record "gx/ipfs/QmbeHtaBy9nZsW4cHRcvgVY4CnDhXudE2Dr6qDxS7yg9rX/go-libp2p-record" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + mockrouting "github.com/ipfs/go-ipfs-routing/mock" + offline "github.com/ipfs/go-ipfs-routing/offline" + u "github.com/ipfs/go-ipfs-util" + ipns "github.com/ipfs/go-ipns" + path "github.com/ipfs/go-path" + opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" + ci "github.com/libp2p/go-libp2p-crypto" + peer "github.com/libp2p/go-libp2p-peer" + pstore "github.com/libp2p/go-libp2p-peerstore" + pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem" + record "github.com/libp2p/go-libp2p-record" + routing "github.com/libp2p/go-libp2p-routing" + ropts "github.com/libp2p/go-libp2p-routing/options" + testutil "github.com/libp2p/go-testutil" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index b8ccdaafb..94d498992 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -5,15 +5,15 @@ import ( "strings" "time" - path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" - lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru" - ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" - peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" - routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing" - isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain" - mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" + lru "github.com/hashicorp/golang-lru" + ds "github.com/ipfs/go-datastore" + path "github.com/ipfs/go-path" + opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" + isd "github.com/jbenet/go-is-domain" + ci "github.com/libp2p/go-libp2p-crypto" + peer "github.com/libp2p/go-libp2p-peer" + routing "github.com/libp2p/go-libp2p-routing" + mh "github.com/multiformats/go-multihash" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 3da16d238..09c5a39c2 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -5,16 +5,16 @@ import ( "fmt" "testing" - path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" - ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" - ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" - opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" - peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" - offroute "gx/ipfs/QmZ22s3UgNi5vvYNH79jWJ63NPyQGiv4mdNaWCz4WKqMTZ/go-ipfs-routing/offline" - pstoremem "gx/ipfs/QmaCTz9RkrU13bm9kMB54f7atgqM4qkjDZpRwRoJiWXEqs/go-libp2p-peerstore/pstoremem" - "gx/ipfs/QmcYUTQ7tBZeH1CLsZM2S3xhMEZdvUgXvbjhpMsLDpk3oJ/go-unixfs" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + offroute "github.com/ipfs/go-ipfs-routing/offline" + ipns "github.com/ipfs/go-ipns" + path "github.com/ipfs/go-path" + "github.com/ipfs/go-unixfs" + opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" + ci "github.com/libp2p/go-libp2p-crypto" + peer "github.com/libp2p/go-libp2p-peer" + pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem" ) type mockResolver struct { diff --git a/namesys/proquint.go b/namesys/proquint.go index e36865903..63cb62a04 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -4,9 +4,9 @@ import ( "context" "errors" - path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" - opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" - proquint "gx/ipfs/QmYnf27kzqR2cxt6LFZdrAFJuQd6785fTkBvMuEj9EeRxM/proquint" + proquint "github.com/bren2010/proquint" + path "github.com/ipfs/go-path" + opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" ) type ProquintResolver struct{} diff --git a/namesys/publisher.go b/namesys/publisher.go index 126bcd1ce..e43858d02 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -8,17 +8,17 @@ import ( pin "github.com/ipfs/go-ipfs/pin" - path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" - ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - dsquery "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" - ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" - pb "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns/pb" - peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" - routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing" - ft "gx/ipfs/QmcYUTQ7tBZeH1CLsZM2S3xhMEZdvUgXvbjhpMsLDpk3oJ/go-unixfs" - proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" - base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" + proto "github.com/gogo/protobuf/proto" + ds "github.com/ipfs/go-datastore" + dsquery "github.com/ipfs/go-datastore/query" + ipns "github.com/ipfs/go-ipns" + pb "github.com/ipfs/go-ipns/pb" + path "github.com/ipfs/go-path" + ft "github.com/ipfs/go-unixfs" + ci "github.com/libp2p/go-libp2p-crypto" + peer "github.com/libp2p/go-libp2p-peer" + routing "github.com/libp2p/go-libp2p-routing" + base32 "github.com/whyrusleeping/base32" ) const ipnsPrefix = "/ipns/" diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 8b7481d59..53cc6735e 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -6,15 +6,15 @@ import ( "testing" "time" - ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - ma "gx/ipfs/QmTZBfrPJmjWsCvHEtX5FE6KimVJhsJg5sBbqEFYf4UZtL/go-multiaddr" - ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" - ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" - testutil "gx/ipfs/QmWapVoHjtKhn4MhvKNoPTkJKADFGACfXPFnt7combwp5W/go-testutil" - dshelp "gx/ipfs/QmXSEqXLCzpCByJU4wqbJ37TcBEj77FKMUWUP1qLh56847/go-ipfs-ds-help" - peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" - mockrouting "gx/ipfs/QmZ22s3UgNi5vvYNH79jWJ63NPyQGiv4mdNaWCz4WKqMTZ/go-ipfs-routing/mock" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + dshelp "github.com/ipfs/go-ipfs-ds-help" + mockrouting "github.com/ipfs/go-ipfs-routing/mock" + ipns "github.com/ipfs/go-ipns" + ci "github.com/libp2p/go-libp2p-crypto" + peer "github.com/libp2p/go-libp2p-peer" + testutil "github.com/libp2p/go-testutil" + ma "github.com/multiformats/go-multiaddr" ) type identity struct { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index c7a7f4442..1092ba3a5 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -7,16 +7,16 @@ import ( keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-ipfs/namesys" - path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" - - goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context" - ic "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - pb "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns/pb" - peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" - logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" - proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" + path "github.com/ipfs/go-path" + + proto "github.com/gogo/protobuf/proto" + ds "github.com/ipfs/go-datastore" + pb "github.com/ipfs/go-ipns/pb" + logging "github.com/ipfs/go-log" + goprocess "github.com/jbenet/goprocess" + gpctx "github.com/jbenet/goprocess/context" + ic "github.com/libp2p/go-libp2p-crypto" + peer "github.com/libp2p/go-libp2p-peer" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 1f582a44d..22d69e254 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -10,11 +10,11 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" + path "github.com/ipfs/go-path" - mocknet "gx/ipfs/QmRxk6AUaGaKCfzS1xSNRojiAPd7h2ih8GuCdjJBF3Y6GK/go-libp2p/p2p/net/mock" - goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" - pstore "gx/ipfs/QmaCTz9RkrU13bm9kMB54f7atgqM4qkjDZpRwRoJiWXEqs/go-libp2p-peerstore" + goprocess "github.com/jbenet/goprocess" + pstore "github.com/libp2p/go-libp2p-peerstore" + mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 80eb7d83f..882061448 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -6,13 +6,13 @@ import ( "testing" "time" - path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" - ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - dssync "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/sync" - ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" - testutil "gx/ipfs/QmWapVoHjtKhn4MhvKNoPTkJKADFGACfXPFnt7combwp5W/go-testutil" - peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" - mockrouting "gx/ipfs/QmZ22s3UgNi5vvYNH79jWJ63NPyQGiv4mdNaWCz4WKqMTZ/go-ipfs-routing/mock" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + mockrouting "github.com/ipfs/go-ipfs-routing/mock" + ipns "github.com/ipfs/go-ipns" + path "github.com/ipfs/go-path" + peer "github.com/libp2p/go-libp2p-peer" + testutil "github.com/libp2p/go-testutil" ) func TestRoutingResolve(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index 315a281bb..d58133775 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,17 +5,17 @@ import ( "strings" "time" - path "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" - cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" - ipns "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns" - pb "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns/pb" - opts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" - peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" - routing "gx/ipfs/QmYxUdYY9S6yg5tSPVin5GFTvtfsLauVcr7reHDD3dM8xf/go-libp2p-routing" - logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" - dht "gx/ipfs/QmdR6WN3TUEAVQ9KWE2UiFJikWTbUvgBJay6mjB4yUJebq/go-libp2p-kad-dht" - proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" - mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" + proto "github.com/gogo/protobuf/proto" + cid "github.com/ipfs/go-cid" + ipns "github.com/ipfs/go-ipns" + pb "github.com/ipfs/go-ipns/pb" + logging "github.com/ipfs/go-log" + path "github.com/ipfs/go-path" + opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" + dht "github.com/libp2p/go-libp2p-kad-dht" + peer "github.com/libp2p/go-libp2p-peer" + routing "github.com/libp2p/go-libp2p-routing" + mh "github.com/multiformats/go-multihash" ) var log = logging.Logger("namesys") From 9b48524e90cf6a1a78f16a9ca5bc8678f7296f4e Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 2 Mar 2019 19:26:36 +0100 Subject: [PATCH 2969/3817] gx: unrewrite License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-keystore@519c2134b72dfab9451ec5fb00ab4f34a99895a8 --- keystore/keystore.go | 4 ++-- keystore/keystore_test.go | 2 +- keystore/memkeystore.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index bafc859b9..d9467f263 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -7,8 +7,8 @@ import ( "path/filepath" "strings" - ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" - logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" + logging "github.com/ipfs/go-log" + ci "github.com/libp2p/go-libp2p-crypto" ) var log = logging.Logger("keystore") diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index fe8276872..c69fd6a05 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -9,7 +9,7 @@ import ( "sort" "testing" - ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" + ci "github.com/libp2p/go-libp2p-crypto" ) type rr struct{} diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index 6983100f9..4f505a995 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -1,6 +1,6 @@ package keystore -import ci "gx/ipfs/QmTW4SdgBWq9GjsBsHeUx8WuGxzhgzAf88UMH2w62PC8yK/go-libp2p-crypto" +import ci "github.com/libp2p/go-libp2p-crypto" // MemKeystore is an in memory keystore implementation that is not persisted to // any backing storage. From d042bd4cf142c251e8ab147bfb768cc3d27b45da Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 2 Mar 2019 19:26:36 +0100 Subject: [PATCH 2970/3817] gx: unrewrite License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-filestore@13954a4a034df7278f440033badc2acf44307fe0 --- filestore/filestore.go | 12 ++++++------ filestore/filestore_test.go | 10 +++++----- filestore/fsrefstore.go | 18 +++++++++--------- filestore/pb/dataobj.pb.go | 2 +- filestore/util.go | 10 +++++----- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 69021b7b7..be4d954be 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -11,12 +11,12 @@ import ( "context" "errors" - cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" - dsq "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" - blockstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore" - blocks "gx/ipfs/QmYYLnAzR28nAQ4U5MFniLprnktu6eTFKibeNt96V21EZK/go-block-format" - logging "gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log" - posinfo "gx/ipfs/QmdiZuFuiFD1Gbuu8PdqmsfrCR3z4QKSR2bN1NAvnJgTY7/go-ipfs-posinfo" + blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + dsq "github.com/ipfs/go-datastore/query" + blockstore "github.com/ipfs/go-ipfs-blockstore" + posinfo "github.com/ipfs/go-ipfs-posinfo" + logging "github.com/ipfs/go-log" ) var log = logging.Logger("filestore") diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index b6fdf3507..783dc86f9 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -7,12 +7,12 @@ import ( "math/rand" "testing" - dag "gx/ipfs/QmPJNbVw8o3ohC43ppSXyNXwYKsWShG4zygnirHptfbHri/go-merkledag" + dag "github.com/ipfs/go-merkledag" - cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" - ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - blockstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore" - posinfo "gx/ipfs/QmdiZuFuiFD1Gbuu8PdqmsfrCR3z4QKSR2bN1NAvnJgTY7/go-ipfs-posinfo" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + blockstore "github.com/ipfs/go-ipfs-blockstore" + posinfo "github.com/ipfs/go-ipfs-posinfo" ) func newTestFilestore(t *testing.T) (string, *Filestore) { diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 190061017..b4c66a32d 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -10,15 +10,15 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" - ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - dsns "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/namespace" - dsq "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" - dshelp "gx/ipfs/QmXSEqXLCzpCByJU4wqbJ37TcBEj77FKMUWUP1qLh56847/go-ipfs-ds-help" - blockstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore" - blocks "gx/ipfs/QmYYLnAzR28nAQ4U5MFniLprnktu6eTFKibeNt96V21EZK/go-block-format" - proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" - posinfo "gx/ipfs/QmdiZuFuiFD1Gbuu8PdqmsfrCR3z4QKSR2bN1NAvnJgTY7/go-ipfs-posinfo" + proto "github.com/gogo/protobuf/proto" + blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + dsns "github.com/ipfs/go-datastore/namespace" + dsq "github.com/ipfs/go-datastore/query" + blockstore "github.com/ipfs/go-ipfs-blockstore" + dshelp "github.com/ipfs/go-ipfs-ds-help" + posinfo "github.com/ipfs/go-ipfs-posinfo" ) // FilestorePrefix identifies the key prefix for FileManager blocks. diff --git a/filestore/pb/dataobj.pb.go b/filestore/pb/dataobj.pb.go index cf1da1513..59650a11c 100644 --- a/filestore/pb/dataobj.pb.go +++ b/filestore/pb/dataobj.pb.go @@ -5,7 +5,7 @@ package datastore_pb import ( fmt "fmt" - proto "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" + proto "github.com/gogo/protobuf/proto" io "io" math "math" ) diff --git a/filestore/util.go b/filestore/util.go index 45accb3a3..4f3949591 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -6,11 +6,11 @@ import ( pb "github.com/ipfs/go-ipfs/filestore/pb" - cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" - ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" - dsq "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore/query" - dshelp "gx/ipfs/QmXSEqXLCzpCByJU4wqbJ37TcBEj77FKMUWUP1qLh56847/go-ipfs-ds-help" - blockstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + dsq "github.com/ipfs/go-datastore/query" + blockstore "github.com/ipfs/go-ipfs-blockstore" + dshelp "github.com/ipfs/go-ipfs-ds-help" ) // Status is used to identify the state of the block data referenced From ee697a3b097c25457cccf63c42ba03fadde9d4ef Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 5 Mar 2019 09:36:58 -0800 Subject: [PATCH 2971/3817] file type: add stringer This commit was moved from ipfs/interface-go-ipfs-core@4e99a8e9250040b9cfc9600641d138fca8ff01f9 --- coreiface/unixfs.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index bdf08b5c3..d0e3ec572 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -30,6 +30,21 @@ const ( TSymlink ) +func (t FileType) String() string { + switch t { + case TUnknown: + return "unknown" + case TFile: + return "file" + case TDirectory: + return "directory" + case TSymlink: + return "symlink" + default: + return "" + } +} + // DirEntry is a directory entry returned by `Ls`. type DirEntry struct { Name string From 4be6e60dbea5e9dd8cf37d1f7a5b038e2a18dbf7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 5 Mar 2019 09:37:40 -0800 Subject: [PATCH 2972/3817] tests: add symlink target test (also, fix some error versus fatal nits) This commit was moved from ipfs/interface-go-ipfs-core@5c6a751986f6d5fe1174819442fcd5f60e0a6f7d --- coreiface/tests/unixfs.go | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index a0c33c0b0..b8b22e50a 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -737,22 +737,23 @@ func (tp *provider) TestLs(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } r := strings.NewReader("content-of-file") p, err := api.Unixfs().Add(ctx, files.NewMapDirectory(map[string]files.Node{ "0": files.NewMapDirectory(map[string]files.Node{ - "name-of-file": files.NewReaderFile(r), + "name-of-file": files.NewReaderFile(r), + "name-of-symlink": files.NewLinkFile("/foo/bar", nil), }), })) if err != nil { - t.Error(err) + t.Fatal(err) } entries, err := api.Unixfs().Ls(ctx, p) if err != nil { - t.Error(err) + t.Fatal(err) } entry := <-entries @@ -760,13 +761,33 @@ func (tp *provider) TestLs(t *testing.T) { t.Fatal(entry.Err) } if entry.Size != 15 { - t.Fatalf("expected size = 15, got %d", entry.Size) + t.Errorf("expected size = 15, got %d", entry.Size) } if entry.Name != "name-of-file" { - t.Fatalf("expected name = name-of-file, got %s", entry.Name) + t.Errorf("expected name = name-of-file, got %s", entry.Name) + } + if entry.Type != coreiface.TFile { + t.Errorf("wrong type %s", entry.Type) } if entry.Cid.String() != "QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr" { - t.Fatalf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", entry.Cid) + t.Errorf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", entry.Cid) + } + entry = <-entries + if entry.Err != nil { + t.Fatal(entry.Err) + } + if entry.Type != coreiface.TSymlink { + t.Errorf("wrong type %s", entry.Type) + } + if entry.Name != "name-of-symlink" { + t.Errorf("expected name = name-of-symlink, got %s", entry.Name) + } + if entry.Target.String() != "/foo/bar" { + t.Errorf("expected symlink target to be /foo/bar, got %s", entry.Target) + } + + if int(entry.Size) != len(entry.Target.String()) { + t.Errorf("expected size = %d, got %d", len(entry.Target.String()), entry.Size) } if l, ok := <-entries; ok { t.Errorf("didn't expect a second link") From e2900773a64d532ccbbabd5ed23c048270bfbefe Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 5 Mar 2019 09:54:43 -0800 Subject: [PATCH 2973/3817] switch symlink target type to string (path can't represent relative paths) This commit was moved from ipfs/interface-go-ipfs-core@368881fa4a30814112d1b2096c37c91f5fd16976 --- coreiface/tests/unixfs.go | 6 +++--- coreiface/unixfs.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index b8b22e50a..79dedf155 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -782,12 +782,12 @@ func (tp *provider) TestLs(t *testing.T) { if entry.Name != "name-of-symlink" { t.Errorf("expected name = name-of-symlink, got %s", entry.Name) } - if entry.Target.String() != "/foo/bar" { + if entry.Target != "/foo/bar" { t.Errorf("expected symlink target to be /foo/bar, got %s", entry.Target) } - if int(entry.Size) != len(entry.Target.String()) { - t.Errorf("expected size = %d, got %d", len(entry.Target.String()), entry.Size) + if int(entry.Size) != len(entry.Target) { + t.Errorf("expected size = %d, got %d", len(entry.Target), entry.Size) } if l, ok := <-entries; ok { t.Errorf("didn't expect a second link") diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index d0e3ec572..f9508f138 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -53,7 +53,7 @@ type DirEntry struct { // Only filled when asked to resolve the directory entry. Size uint64 // The size of the file in bytes (or the size of the symlink). Type FileType // The type of the file. - Target Path // The symlink target (if a symlink). + Target string // The symlink target (if a symlink). Err error } From f5117736743ee8753e359aa7e7dbcaf602d75224 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 6 Mar 2019 16:41:05 -0800 Subject: [PATCH 2974/3817] remove target size requirement It's complicated. We need to carefully think through how sizes work. This commit was moved from ipfs/interface-go-ipfs-core@7a7cf9694be27b62820f05e2ac11bb4a57bab982 --- coreiface/tests/unixfs.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 79dedf155..bbcb66899 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -786,9 +786,6 @@ func (tp *provider) TestLs(t *testing.T) { t.Errorf("expected symlink target to be /foo/bar, got %s", entry.Target) } - if int(entry.Size) != len(entry.Target) { - t.Errorf("expected size = %d, got %d", len(entry.Target), entry.Size) - } if l, ok := <-entries; ok { t.Errorf("didn't expect a second link") if l.Err != nil { From 5d1baca6923535dacdb0dcd2da5c6968c8870c61 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Fri, 8 Mar 2019 14:19:29 -0800 Subject: [PATCH 2975/3817] Provide root node immediately when add and pin add License: MIT Signed-off-by: Michael Avila This commit was moved from ipfs/go-ipfs-provider@931c253ea481289ffd83709494cad630e46a36f7 --- provider/provider.go | 88 ++++++++++++++++ provider/queue.go | 235 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+) create mode 100644 provider/provider.go create mode 100644 provider/queue.go diff --git a/provider/provider.go b/provider/provider.go new file mode 100644 index 000000000..ee0481a0f --- /dev/null +++ b/provider/provider.go @@ -0,0 +1,88 @@ +// Package provider implements structures and methods to provide blocks, +// keep track of which blocks are provided, and to allow those blocks to +// be reprovided. +package provider + +import ( + "context" + "github.com/ipfs/go-cid" + logging "github.com/ipfs/go-log" + "github.com/libp2p/go-libp2p-routing" + "time" +) + +var ( + log = logging.Logger("provider") +) + +const ( + provideOutgoingWorkerLimit = 8 + provideOutgoingTimeout = 15 * time.Second +) + +// Provider announces blocks to the network, tracks which blocks are +// being provided, and untracks blocks when they're no longer in the blockstore. +type Provider struct { + ctx context.Context + // the CIDs for which provide announcements should be made + queue *Queue + // used to announce providing to the network + contentRouting routing.ContentRouting +} + +func NewProvider(ctx context.Context, queue *Queue, contentRouting routing.ContentRouting) *Provider { + return &Provider{ + ctx: ctx, + queue: queue, + contentRouting: contentRouting, + } +} + +// Start workers to handle provide requests. +func (p *Provider) Run() { + p.queue.Run() + p.handleAnnouncements() +} + +// Provide the given cid using specified strategy. +func (p *Provider) Provide(root cid.Cid) error { + return p.queue.Enqueue(root) +} + +// Handle all outgoing cids by providing (announcing) them +func (p *Provider) handleAnnouncements() { + for workers := 0; workers < provideOutgoingWorkerLimit; workers++ { + go func() { + for { + select { + case <-p.ctx.Done(): + return + case entry := <-p.queue.Dequeue(): + if err := doProvide(p.ctx, p.contentRouting, entry.cid); err != nil { + log.Warningf("Unable to provide entry: %s, %s", entry.cid, err) + } + + if err := entry.Complete(); err != nil { + log.Warningf("Unable to complete queue entry when providing: %s, %s", entry.cid, err) + } + } + } + }() + } +} + +// TODO: better document this provide logic +func doProvide(ctx context.Context, contentRouting routing.ContentRouting, key cid.Cid) error { + // announce + log.Info("announce - start - ", key) + ctx, cancel := context.WithTimeout(ctx, provideOutgoingTimeout) + if err := contentRouting.Provide(ctx, key, true); err != nil { + log.Warningf("Failed to provide cid: %s", err) + // TODO: Maybe put these failures onto a failures queue? + cancel() + return err + } + cancel() + log.Info("announce - end - ", key) + return nil +} diff --git a/provider/queue.go b/provider/queue.go new file mode 100644 index 000000000..65656450a --- /dev/null +++ b/provider/queue.go @@ -0,0 +1,235 @@ +package provider + +import ( + "context" + "errors" + "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/namespace" + "github.com/ipfs/go-datastore/query" + "math" + "strconv" + "strings" + "sync" +) + +// Entry allows for the durability in the queue. When a cid is dequeued it is +// not removed from the datastore until you call Complete() on the entry you +// receive. +type Entry struct { + cid cid.Cid + key ds.Key + queue *Queue +} + +func (e *Entry) Complete() error { + return e.queue.remove(e.key) +} + +// Queue provides a durable, FIFO interface to the datastore for storing cids +// +// Durability just means that cids in the process of being provided when a +// crash or shutdown occurs will still be in the queue when the node is +// brought back online. +type Queue struct { + // used to differentiate queues in datastore + // e.g. provider vs reprovider + name string + + ctx context.Context + + tail uint64 + head uint64 + + lock sync.Mutex + datastore ds.Datastore + + dequeue chan *Entry + notEmpty chan struct{} + + isRunning bool +} + +func NewQueue(name string, ctx context.Context, datastore ds.Datastore) (*Queue, error) { + namespaced := namespace.Wrap(datastore, ds.NewKey("/" + name + "/queue/")) + head, tail, err := getQueueHeadTail(name, ctx, namespaced) + if err != nil { + return nil, err + } + q := &Queue{ + name: name, + ctx: ctx, + head: head, + tail: tail, + lock: sync.Mutex{}, + datastore: namespaced, + dequeue: make(chan *Entry), + notEmpty: make(chan struct{}), + isRunning: false, + } + return q, nil +} + +// Put a cid in the queue +func (q *Queue) Enqueue(cid cid.Cid) error { + q.lock.Lock() + defer q.lock.Unlock() + + wasEmpty := q.IsEmpty() + + nextKey := q.queueKey(q.tail) + + if err := q.datastore.Put(nextKey, cid.Bytes()); err != nil { + return err + } + + q.tail++ + + if q.isRunning && wasEmpty { + select { + case q.notEmpty <- struct{}{}: + case <-q.ctx.Done(): + } + } + + return nil +} + +// Remove an entry from the queue. +func (q *Queue) Dequeue() <-chan *Entry { + return q.dequeue +} + +func (q *Queue) IsEmpty() bool { + return (q.tail - q.head) == 0 +} + +func (q *Queue) remove(key ds.Key) error { + return q.datastore.Delete(key) +} + +// dequeue items when the dequeue channel is available to +// be written to +func (q *Queue) Run() { + q.isRunning = true + go func() { + for { + select { + case <-q.ctx.Done(): + return + default: + } + if q.IsEmpty() { + select { + case <-q.ctx.Done(): + return + // wait for a notEmpty message + case <-q.notEmpty: + } + } + + entry, err := q.next() + if err != nil { + log.Warningf("Error Dequeue()-ing: %s, %s", entry, err) + continue + } + + select { + case <-q.ctx.Done(): + return + case q.dequeue <- entry: + } + } + }() +} + +// Find the next item in the queue, crawl forward if an entry is not +// found in the next spot. +func (q *Queue) next() (*Entry, error) { + q.lock.Lock() + defer q.lock.Unlock() + + var nextKey ds.Key + var value []byte + var err error + for { + if q.head >= q.tail { + return nil, errors.New("no more entries in queue") + } + select { + case <-q.ctx.Done(): + return nil, nil + default: + } + nextKey = q.queueKey(q.head) + value, err = q.datastore.Get(nextKey) + if err == ds.ErrNotFound { + q.head++ + continue + } else if err != nil { + return nil, err + } else { + break + } + } + + id, err := cid.Parse(value) + if err != nil { + return nil, err + } + + entry := &Entry { + cid: id, + key: nextKey, + queue: q, + } + + q.head++ + + return entry, nil +} + +func (q *Queue) queueKey(id uint64) ds.Key { + return ds.NewKey(strconv.FormatUint(id, 10)) +} + +// crawl over the queue entries to find the head and tail +func getQueueHeadTail(name string, ctx context.Context, datastore ds.Datastore) (uint64, uint64, error) { + query := query.Query{} + results, err := datastore.Query(query) + if err != nil { + return 0, 0, err + } + + var tail uint64 = 0 + var head uint64 = math.MaxUint64 + for entry := range results.Next() { + select { + case <-ctx.Done(): + return 0, 0, nil + default: + } + trimmed := strings.TrimPrefix(entry.Key, "/") + id, err := strconv.ParseUint(trimmed, 10, 64) + if err != nil { + return 0, 0, err + } + + if id < head { + head = id + } + + if (id+1) > tail { + tail = (id+1) + } + } + if err := results.Close(); err != nil { + return 0, 0, err + } + if head == math.MaxUint64 { + head = 0 + } + + return head, tail, nil +} + From a0ccc85a802798410c2d587a96157da9b65ef8ff Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Fri, 8 Mar 2019 15:49:25 -0800 Subject: [PATCH 2976/3817] Remove timeout from provide context This is being removed because it appears that the provide announcements go out regardless of the timeout. License: MIT Signed-off-by: Michael Avila This commit was moved from ipfs/go-ipfs-provider@c5f00613d8f8eaa1b08011ba28f9b06336614055 --- provider/provider.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/provider/provider.go b/provider/provider.go index ee0481a0f..e43058b39 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -8,7 +8,6 @@ import ( "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log" "github.com/libp2p/go-libp2p-routing" - "time" ) var ( @@ -17,7 +16,6 @@ var ( const ( provideOutgoingWorkerLimit = 8 - provideOutgoingTimeout = 15 * time.Second ) // Provider announces blocks to the network, tracks which blocks are @@ -75,14 +73,11 @@ func (p *Provider) handleAnnouncements() { func doProvide(ctx context.Context, contentRouting routing.ContentRouting, key cid.Cid) error { // announce log.Info("announce - start - ", key) - ctx, cancel := context.WithTimeout(ctx, provideOutgoingTimeout) if err := contentRouting.Provide(ctx, key, true); err != nil { log.Warningf("Failed to provide cid: %s", err) // TODO: Maybe put these failures onto a failures queue? - cancel() return err } - cancel() log.Info("announce - end - ", key) return nil } From 95ff711ff0709e816700f23725ef54fea34a668b Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Fri, 8 Mar 2019 16:31:51 -0800 Subject: [PATCH 2977/3817] Use offlineProvider when --offline License: MIT Signed-off-by: Michael Avila This commit was moved from ipfs/go-ipfs-provider@90d30898c4fbbb1344286f620f68221a0f05cfa3 --- provider/offline.go | 15 +++++++++++++++ provider/provider.go | 17 +++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 provider/offline.go diff --git a/provider/offline.go b/provider/offline.go new file mode 100644 index 000000000..f7b9603b9 --- /dev/null +++ b/provider/offline.go @@ -0,0 +1,15 @@ +package provider + +import "github.com/ipfs/go-cid" + +type offlineProvider struct {} + +func NewOfflineProvider() Provider { + return &offlineProvider{} +} + +func (op *offlineProvider) Run() {} + +func (op *offlineProvider) Provide(cid cid.Cid) error { + return nil +} diff --git a/provider/provider.go b/provider/provider.go index e43058b39..e4ee6d9ff 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -18,9 +18,14 @@ const ( provideOutgoingWorkerLimit = 8 ) +type Provider interface { + Run() + Provide(cid.Cid) error +} + // Provider announces blocks to the network, tracks which blocks are // being provided, and untracks blocks when they're no longer in the blockstore. -type Provider struct { +type provider struct { ctx context.Context // the CIDs for which provide announcements should be made queue *Queue @@ -28,8 +33,8 @@ type Provider struct { contentRouting routing.ContentRouting } -func NewProvider(ctx context.Context, queue *Queue, contentRouting routing.ContentRouting) *Provider { - return &Provider{ +func NewProvider(ctx context.Context, queue *Queue, contentRouting routing.ContentRouting) Provider { + return &provider{ ctx: ctx, queue: queue, contentRouting: contentRouting, @@ -37,18 +42,18 @@ func NewProvider(ctx context.Context, queue *Queue, contentRouting routing.Conte } // Start workers to handle provide requests. -func (p *Provider) Run() { +func (p *provider) Run() { p.queue.Run() p.handleAnnouncements() } // Provide the given cid using specified strategy. -func (p *Provider) Provide(root cid.Cid) error { +func (p *provider) Provide(root cid.Cid) error { return p.queue.Enqueue(root) } // Handle all outgoing cids by providing (announcing) them -func (p *Provider) handleAnnouncements() { +func (p *provider) handleAnnouncements() { for workers := 0; workers < provideOutgoingWorkerLimit; workers++ { go func() { for { From 52598b7b12e9659e4a0b9cad99543e8e9d6e0a96 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sun, 10 Mar 2019 15:22:42 +0100 Subject: [PATCH 2978/3817] Add license License: MIT License: Apache 2.0 Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-verifcid@11ea67429882fb057e0c7f239583f5edf522ed5a --- verifcid/LICENSE-APACHE | 15 +++++++++++++++ verifcid/LICENSE-MIT | 21 +++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 verifcid/LICENSE-APACHE create mode 100644 verifcid/LICENSE-MIT diff --git a/verifcid/LICENSE-APACHE b/verifcid/LICENSE-APACHE new file mode 100644 index 000000000..324ca36e9 --- /dev/null +++ b/verifcid/LICENSE-APACHE @@ -0,0 +1,15 @@ +APACHE License + +Copyright 2018 Protocol Labs, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/verifcid/LICENSE-MIT b/verifcid/LICENSE-MIT new file mode 100644 index 000000000..5b0bba394 --- /dev/null +++ b/verifcid/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright 2018 Protocol Labs, Inc + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 0a145b6e0db0262e547f518fb4559c348cc95da1 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Fri, 8 Mar 2019 16:58:17 -0800 Subject: [PATCH 2979/3817] Refactor per code climate rules License: MIT Signed-off-by: Michael Avila This commit was moved from ipfs/go-ipfs-provider@7bdb5546328bf765897153f86cc136ee14863b6b --- provider/offline.go | 3 ++- provider/provider.go | 4 +-- provider/queue.go | 60 +++++++++++++++++++++++--------------------- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/provider/offline.go b/provider/offline.go index f7b9603b9..029ddfa98 100644 --- a/provider/offline.go +++ b/provider/offline.go @@ -2,8 +2,9 @@ package provider import "github.com/ipfs/go-cid" -type offlineProvider struct {} +type offlineProvider struct{} +// NewOfflineProvider creates a Provider that does nothing func NewOfflineProvider() Provider { return &offlineProvider{} } diff --git a/provider/provider.go b/provider/provider.go index e4ee6d9ff..76004f51a 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -18,13 +18,12 @@ const ( provideOutgoingWorkerLimit = 8 ) +// Provider announces blocks to the network type Provider interface { Run() Provide(cid.Cid) error } -// Provider announces blocks to the network, tracks which blocks are -// being provided, and untracks blocks when they're no longer in the blockstore. type provider struct { ctx context.Context // the CIDs for which provide announcements should be made @@ -33,6 +32,7 @@ type provider struct { contentRouting routing.ContentRouting } +// NewProvider creates a provider that announces blocks to the network using a content router func NewProvider(ctx context.Context, queue *Queue, contentRouting routing.ContentRouting) Provider { return &provider{ ctx: ctx, diff --git a/provider/queue.go b/provider/queue.go index 65656450a..cc756366d 100644 --- a/provider/queue.go +++ b/provider/queue.go @@ -17,11 +17,12 @@ import ( // not removed from the datastore until you call Complete() on the entry you // receive. type Entry struct { - cid cid.Cid - key ds.Key + cid cid.Cid + key ds.Key queue *Queue } +// Complete the entry by removing it from the queue func (e *Entry) Complete() error { return e.queue.remove(e.key) } @@ -41,36 +42,37 @@ type Queue struct { tail uint64 head uint64 - lock sync.Mutex + lock sync.Mutex datastore ds.Datastore - dequeue chan *Entry + dequeue chan *Entry notEmpty chan struct{} isRunning bool } -func NewQueue(name string, ctx context.Context, datastore ds.Datastore) (*Queue, error) { - namespaced := namespace.Wrap(datastore, ds.NewKey("/" + name + "/queue/")) - head, tail, err := getQueueHeadTail(name, ctx, namespaced) +// NewQueue creates a queue for cids +func NewQueue(ctx context.Context, name string, datastore ds.Datastore) (*Queue, error) { + namespaced := namespace.Wrap(datastore, ds.NewKey("/"+name+"/queue/")) + head, tail, err := getQueueHeadTail(ctx, name, namespaced) if err != nil { return nil, err } q := &Queue{ - name: name, - ctx: ctx, - head: head, - tail: tail, - lock: sync.Mutex{}, + name: name, + ctx: ctx, + head: head, + tail: tail, + lock: sync.Mutex{}, datastore: namespaced, - dequeue: make(chan *Entry), - notEmpty: make(chan struct{}), + dequeue: make(chan *Entry), + notEmpty: make(chan struct{}), isRunning: false, } return q, nil } -// Put a cid in the queue +// Enqueue puts a cid in the queue func (q *Queue) Enqueue(cid cid.Cid) error { q.lock.Lock() defer q.lock.Unlock() @@ -95,21 +97,18 @@ func (q *Queue) Enqueue(cid cid.Cid) error { return nil } -// Remove an entry from the queue. +// Dequeue returns a channel that if listened to will remove entries from the queue func (q *Queue) Dequeue() <-chan *Entry { return q.dequeue } +// IsEmpty returns whether or not the queue has any items func (q *Queue) IsEmpty() bool { return (q.tail - q.head) == 0 } -func (q *Queue) remove(key ds.Key) error { - return q.datastore.Delete(key) -} - -// dequeue items when the dequeue channel is available to -// be written to +// Run dequeues items when the dequeue channel is available to +// be written to. func (q *Queue) Run() { q.isRunning = true go func() { @@ -178,9 +177,9 @@ func (q *Queue) next() (*Entry, error) { return nil, err } - entry := &Entry { - cid: id, - key: nextKey, + entry := &Entry{ + cid: id, + key: nextKey, queue: q, } @@ -194,14 +193,14 @@ func (q *Queue) queueKey(id uint64) ds.Key { } // crawl over the queue entries to find the head and tail -func getQueueHeadTail(name string, ctx context.Context, datastore ds.Datastore) (uint64, uint64, error) { +func getQueueHeadTail(ctx context.Context, name string, datastore ds.Datastore) (uint64, uint64, error) { query := query.Query{} results, err := datastore.Query(query) if err != nil { return 0, 0, err } - var tail uint64 = 0 + var tail uint64 var head uint64 = math.MaxUint64 for entry := range results.Next() { select { @@ -219,8 +218,8 @@ func getQueueHeadTail(name string, ctx context.Context, datastore ds.Datastore) head = id } - if (id+1) > tail { - tail = (id+1) + if (id + 1) > tail { + tail = (id + 1) } } if err := results.Close(); err != nil { @@ -233,3 +232,6 @@ func getQueueHeadTail(name string, ctx context.Context, datastore ds.Datastore) return head, tail, nil } +func (q *Queue) remove(key ds.Key) error { + return q.datastore.Delete(key) +} From f609ab5d3f1238c0c9b1b889512e8932852882d7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 13 Mar 2019 12:56:30 -0700 Subject: [PATCH 2980/3817] add function to marshal raw nodes to json fixes https://github.com/ipfs/go-ipfs/issues/6076 This commit was moved from ipfs/go-merkledag@9f35e3ef0e90ef948f05aa9e13d763b5cbb27340 --- ipld/merkledag/merkledag_test.go | 22 ++++++++++++++++++++++ ipld/merkledag/raw.go | 6 ++++++ 2 files changed, 28 insertions(+) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index d222ce873..bc87f3bb5 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -3,6 +3,7 @@ package merkledag_test import ( "bytes" "context" + "encoding/json" "errors" "fmt" "io" @@ -640,6 +641,27 @@ func TestCidRawDoesnNeedData(t *testing.T) { } } +func TestRawToJson(t *testing.T) { + rawData := []byte{1, 2, 3, 4} + nd := NewRawNode(rawData) + encoded, err := nd.MarshalJSON() + if err != nil { + t.Fatal(err) + } + var res interface{} + err = json.Unmarshal(encoded, &res) + if err != nil { + t.Fatal(err) + } + resBytes, ok := res.(string) + if !ok { + t.Fatal("expected to marshal to a string") + } + if string(rawData) != resBytes { + t.Fatal("failed to round-trip bytes") + } +} + func TestGetManyDuplicate(t *testing.T) { ctx := context.Background() diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index d0e456a0b..a0adb4a1d 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -1,6 +1,7 @@ package merkledag import ( + "encoding/json" "fmt" "github.com/ipfs/go-block-format" @@ -94,4 +95,9 @@ func (rn *RawNode) Stat() (*ipld.NodeStat, error) { }, nil } +// MarshalJSON is required for our "ipfs dag" commands. +func (rn *RawNode) MarshalJSON() ([]byte, error) { + return json.Marshal(string(rn.RawData())) +} + var _ ipld.Node = (*RawNode)(nil) From 547c85784ffe37f1cbcfd9824ae1f94389f6d3f9 Mon Sep 17 00:00:00 2001 From: Erik Ingenito Date: Thu, 14 Mar 2019 16:48:17 -0700 Subject: [PATCH 2981/3817] Provider queue updates to address deadlocks License: MIT Signed-off-by: Erik Ingenito This commit was moved from ipfs/go-ipfs-provider@925e6c88572021b5f34e92a5398da99904d28b25 --- provider/provider.go | 5 +++-- provider/queue.go | 38 +++++++++++++++----------------------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/provider/provider.go b/provider/provider.go index 76004f51a..28fed7649 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -5,9 +5,10 @@ package provider import ( "context" - "github.com/ipfs/go-cid" + + cid "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log" - "github.com/libp2p/go-libp2p-routing" + routing "github.com/libp2p/go-libp2p-routing" ) var ( diff --git a/provider/queue.go b/provider/queue.go index cc756366d..4219cc80d 100644 --- a/provider/queue.go +++ b/provider/queue.go @@ -24,6 +24,8 @@ type Entry struct { // Complete the entry by removing it from the queue func (e *Entry) Complete() error { + e.queue.lock.Lock() + defer e.queue.lock.Unlock() return e.queue.remove(e.key) } @@ -46,9 +48,7 @@ type Queue struct { datastore ds.Datastore dequeue chan *Entry - notEmpty chan struct{} - - isRunning bool + added chan struct{} } // NewQueue creates a queue for cids @@ -66,8 +66,7 @@ func NewQueue(ctx context.Context, name string, datastore ds.Datastore) (*Queue, lock: sync.Mutex{}, datastore: namespaced, dequeue: make(chan *Entry), - notEmpty: make(chan struct{}), - isRunning: false, + added: make(chan struct{}), } return q, nil } @@ -77,8 +76,6 @@ func (q *Queue) Enqueue(cid cid.Cid) error { q.lock.Lock() defer q.lock.Unlock() - wasEmpty := q.IsEmpty() - nextKey := q.queueKey(q.tail) if err := q.datastore.Put(nextKey, cid.Bytes()); err != nil { @@ -87,11 +84,10 @@ func (q *Queue) Enqueue(cid cid.Cid) error { q.tail++ - if q.isRunning && wasEmpty { - select { - case q.notEmpty <- struct{}{}: + select { + case q.added <- struct{}{}: case <-q.ctx.Done(): - } + default: } return nil @@ -110,20 +106,13 @@ func (q *Queue) IsEmpty() bool { // Run dequeues items when the dequeue channel is available to // be written to. func (q *Queue) Run() { - q.isRunning = true go func() { for { - select { - case <-q.ctx.Done(): - return - default: - } if q.IsEmpty() { select { case <-q.ctx.Done(): return - // wait for a notEmpty message - case <-q.notEmpty: + case <-q.added: } } @@ -138,6 +127,7 @@ func (q *Queue) Run() { return case q.dequeue <- entry: } + } }() } @@ -146,14 +136,16 @@ func (q *Queue) Run() { // found in the next spot. func (q *Queue) next() (*Entry, error) { q.lock.Lock() - defer q.lock.Unlock() + defer func() { + q.lock.Unlock() + }() var nextKey ds.Key var value []byte var err error for { if q.head >= q.tail { - return nil, errors.New("no more entries in queue") + return nil, errors.New("next: no more entries in queue returning") } select { case <-q.ctx.Done(): @@ -194,8 +186,8 @@ func (q *Queue) queueKey(id uint64) ds.Key { // crawl over the queue entries to find the head and tail func getQueueHeadTail(ctx context.Context, name string, datastore ds.Datastore) (uint64, uint64, error) { - query := query.Query{} - results, err := datastore.Query(query) + q := query.Query{} + results, err := datastore.Query(q) if err != nil { return 0, 0, err } From 2e9554e05ca286c0c642822e1b476df5fdf3aa9c Mon Sep 17 00:00:00 2001 From: Erik Ingenito Date: Thu, 14 Mar 2019 16:49:15 -0700 Subject: [PATCH 2982/3817] Provider tests License: MIT Signed-off-by: Erik Ingenito This commit was moved from ipfs/go-ipfs-provider@3c0793a512181e3a3b53652730b8583d9b4d1d58 --- provider/provider_test.go | 79 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 provider/provider_test.go diff --git a/provider/provider_test.go b/provider/provider_test.go new file mode 100644 index 000000000..bacb73944 --- /dev/null +++ b/provider/provider_test.go @@ -0,0 +1,79 @@ +package provider + +import ( + "context" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-ipfs-blocksutil" + pstore "github.com/libp2p/go-libp2p-peerstore" + "math/rand" + "testing" + "time" +) + +var blockGenerator = blocksutil.NewBlockGenerator() + +type mockRouting struct { + provided chan cid.Cid +} + +func mockContentRouting() *mockRouting { + r := mockRouting{} + r.provided = make(chan cid.Cid) + return &r +} + +func TestAnnouncement(t *testing.T) { + ctx := context.Background() + defer func() { + ctx.Done() + }() + + queue, err := NewQueue(ctx, "test", datastore.NewMapDatastore()) + if err != nil { + t.Fatal(err) + } + + r := mockContentRouting() + + provider := NewProvider(ctx, queue, r) + provider.Run() + + cids := cid.NewSet() + + for i := 0; i < 100; i++ { + c := blockGenerator.Next().Cid() + cids.Add(c) + } + + go func() { + for _, c := range cids.Keys() { + err = provider.Provide(c) + // A little goroutine stirring to exercise some different states + r := rand.Intn(10) + time.Sleep(time.Microsecond * time.Duration(r)) + } + }() + + for cids.Len() > 0 { + select { + case cp := <-r.provided: + if !cids.Has(cp) { + t.Fatal("Wrong CID provided") + } + cids.Remove(cp) + case <-time.After(time.Second * 1): + t.Fatal("Timeout waiting for cids to be provided.") + } + } +} + +func (r *mockRouting) Provide(ctx context.Context, cid cid.Cid, recursive bool) error { + r.provided <- cid + return nil +} + +// Search for peers who are able to provide a given key +func (r *mockRouting) FindProvidersAsync(ctx context.Context, cid cid.Cid, timeout int) <-chan pstore.PeerInfo { + return nil +} \ No newline at end of file From 554655f8b2723a8e915b4e2bd122f4e6dfc53857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 15 Mar 2019 14:06:31 +0100 Subject: [PATCH 2983/3817] Wire up context to FlushPath This commit was moved from ipfs/go-mfs@9843dad7802b324847201298802bd7594452ecea --- mfs/mfs_test.go | 8 ++++---- mfs/ops.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index dde556283..539b71a32 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -817,19 +817,19 @@ func TestFlushing(t *testing.T) { t.Fatal(err) } - if err := FlushPath(rt, "/a/b/c/TEST"); err != nil { + if err := FlushPath(ctx, rt, "/a/b/c/TEST"); err != nil { t.Fatal(err) } - if err := FlushPath(rt, "/a/b/d/TEST"); err != nil { + if err := FlushPath(ctx, rt, "/a/b/d/TEST"); err != nil { t.Fatal(err) } - if err := FlushPath(rt, "/a/b/e/TEST"); err != nil { + if err := FlushPath(ctx, rt, "/a/b/e/TEST"); err != nil { t.Fatal(err) } - if err := FlushPath(rt, "/FILE"); err != nil { + if err := FlushPath(ctx, rt, "/FILE"); err != nil { t.Fatal(err) } diff --git a/mfs/ops.go b/mfs/ops.go index d989bb5f0..6e99e23f2 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -226,7 +226,7 @@ func DirLookup(d *Directory, pth string) (FSNode, error) { // TODO: Document this function and link its functionality // with the republisher. -func FlushPath(rt *Root, pth string) error { +func FlushPath(ctx context.Context, rt *Root, pth string) error { nd, err := Lookup(rt, pth) if err != nil { return err @@ -237,6 +237,6 @@ func FlushPath(rt *Root, pth string) error { return err } - rt.repub.WaitPub(context.TODO()) + rt.repub.WaitPub(ctx) return nil } From 137b23bc0aa1c8c2e285dcdf0ca33936d2fc2625 Mon Sep 17 00:00:00 2001 From: Erik Ingenito Date: Fri, 15 Mar 2019 11:04:31 -0700 Subject: [PATCH 2984/3817] Cleanup, fix broken restart, and more tests. License: MIT Signed-off-by: Erik Ingenito This commit was moved from ipfs/go-ipfs-provider@0200a0e94b54396075d6eef98e49d4a96d8562b1 --- provider/provider.go | 21 ++------- provider/provider_test.go | 17 ++++---- provider/queue.go | 83 ++++++++++++++++-------------------- provider/queue_test.go | 89 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 72 deletions(-) create mode 100644 provider/queue_test.go diff --git a/provider/provider.go b/provider/provider.go index 28fed7649..7a30f6d27 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -62,28 +62,13 @@ func (p *provider) handleAnnouncements() { case <-p.ctx.Done(): return case entry := <-p.queue.Dequeue(): - if err := doProvide(p.ctx, p.contentRouting, entry.cid); err != nil { + log.Info("announce - start - ", entry.cid) + if err := p.contentRouting.Provide(p.ctx, entry.cid, true); err != nil { log.Warningf("Unable to provide entry: %s, %s", entry.cid, err) } - - if err := entry.Complete(); err != nil { - log.Warningf("Unable to complete queue entry when providing: %s, %s", entry.cid, err) - } + log.Info("announce - end - ", entry.cid) } } }() } } - -// TODO: better document this provide logic -func doProvide(ctx context.Context, contentRouting routing.ContentRouting, key cid.Cid) error { - // announce - log.Info("announce - start - ", key) - if err := contentRouting.Provide(ctx, key, true); err != nil { - log.Warningf("Failed to provide cid: %s", err) - // TODO: Maybe put these failures onto a failures queue? - return err - } - log.Info("announce - end - ", key) - return nil -} diff --git a/provider/provider_test.go b/provider/provider_test.go index bacb73944..464d73d9a 100644 --- a/provider/provider_test.go +++ b/provider/provider_test.go @@ -2,13 +2,15 @@ package provider import ( "context" - "github.com/ipfs/go-cid" - "github.com/ipfs/go-datastore" - "github.com/ipfs/go-ipfs-blocksutil" - pstore "github.com/libp2p/go-libp2p-peerstore" "math/rand" "testing" "time" + + blocksutil "github.com/ipfs/go-ipfs-blocksutil" + cid "github.com/ipfs/go-cid" + datastore "github.com/ipfs/go-datastore" + pstore "github.com/libp2p/go-libp2p-peerstore" + sync "github.com/ipfs/go-datastore/sync" ) var blockGenerator = blocksutil.NewBlockGenerator() @@ -25,11 +27,10 @@ func mockContentRouting() *mockRouting { func TestAnnouncement(t *testing.T) { ctx := context.Background() - defer func() { - ctx.Done() - }() + defer ctx.Done() - queue, err := NewQueue(ctx, "test", datastore.NewMapDatastore()) + ds := sync.MutexWrap(datastore.NewMapDatastore()) + queue, err := NewQueue(ctx, "test", ds) if err != nil { t.Fatal(err) } diff --git a/provider/queue.go b/provider/queue.go index 4219cc80d..f1c9945cd 100644 --- a/provider/queue.go +++ b/provider/queue.go @@ -3,14 +3,15 @@ package provider import ( "context" "errors" - "github.com/ipfs/go-cid" - ds "github.com/ipfs/go-datastore" - "github.com/ipfs/go-datastore/namespace" - "github.com/ipfs/go-datastore/query" "math" "strconv" "strings" "sync" + + cid "github.com/ipfs/go-cid" + datastore "github.com/ipfs/go-datastore" + namespace "github.com/ipfs/go-datastore/namespace" + query "github.com/ipfs/go-datastore/query" ) // Entry allows for the durability in the queue. When a cid is dequeued it is @@ -18,17 +19,10 @@ import ( // receive. type Entry struct { cid cid.Cid - key ds.Key + key datastore.Key queue *Queue } -// Complete the entry by removing it from the queue -func (e *Entry) Complete() error { - e.queue.lock.Lock() - defer e.queue.lock.Unlock() - return e.queue.remove(e.key) -} - // Queue provides a durable, FIFO interface to the datastore for storing cids // // Durability just means that cids in the process of being provided when a @@ -44,41 +38,41 @@ type Queue struct { tail uint64 head uint64 - lock sync.Mutex - datastore ds.Datastore + enqueueLock sync.Mutex + ds datastore.Datastore // Must be threadsafe dequeue chan *Entry added chan struct{} } // NewQueue creates a queue for cids -func NewQueue(ctx context.Context, name string, datastore ds.Datastore) (*Queue, error) { - namespaced := namespace.Wrap(datastore, ds.NewKey("/"+name+"/queue/")) +func NewQueue(ctx context.Context, name string, ds datastore.Datastore) (*Queue, error) { + namespaced := namespace.Wrap(ds, datastore.NewKey("/"+name+"/queue/")) head, tail, err := getQueueHeadTail(ctx, name, namespaced) if err != nil { return nil, err } q := &Queue{ - name: name, - ctx: ctx, - head: head, - tail: tail, - lock: sync.Mutex{}, - datastore: namespaced, - dequeue: make(chan *Entry), - added: make(chan struct{}), + name: name, + ctx: ctx, + head: head, + tail: tail, + enqueueLock: sync.Mutex{}, + ds: namespaced, + dequeue: make(chan *Entry), + added: make(chan struct{}), } return q, nil } // Enqueue puts a cid in the queue func (q *Queue) Enqueue(cid cid.Cid) error { - q.lock.Lock() - defer q.lock.Unlock() + q.enqueueLock.Lock() + defer q.enqueueLock.Unlock() nextKey := q.queueKey(q.tail) - if err := q.datastore.Put(nextKey, cid.Bytes()); err != nil { + if err := q.ds.Put(nextKey, cid.Bytes()); err != nil { return err } @@ -126,8 +120,9 @@ func (q *Queue) Run() { case <-q.ctx.Done(): return case q.dequeue <- entry: + q.head++ + err = q.ds.Delete(entry.key) } - } }() } @@ -135,12 +130,7 @@ func (q *Queue) Run() { // Find the next item in the queue, crawl forward if an entry is not // found in the next spot. func (q *Queue) next() (*Entry, error) { - q.lock.Lock() - defer func() { - q.lock.Unlock() - }() - - var nextKey ds.Key + var key datastore.Key var value []byte var err error for { @@ -152,9 +142,12 @@ func (q *Queue) next() (*Entry, error) { return nil, nil default: } - nextKey = q.queueKey(q.head) - value, err = q.datastore.Get(nextKey) - if err == ds.ErrNotFound { + key = q.queueKey(q.head) + + value, err = q.ds.Get(key) + + value, err = q.ds.Get(key) + if err == datastore.ErrNotFound { q.head++ continue } else if err != nil { @@ -171,21 +164,23 @@ func (q *Queue) next() (*Entry, error) { entry := &Entry{ cid: id, - key: nextKey, + key: key, queue: q, } - q.head++ + if err != nil { + return nil, err + } return entry, nil } -func (q *Queue) queueKey(id uint64) ds.Key { - return ds.NewKey(strconv.FormatUint(id, 10)) +func (q *Queue) queueKey(id uint64) datastore.Key { + return datastore.NewKey(strconv.FormatUint(id, 10)) } // crawl over the queue entries to find the head and tail -func getQueueHeadTail(ctx context.Context, name string, datastore ds.Datastore) (uint64, uint64, error) { +func getQueueHeadTail(ctx context.Context, name string, datastore datastore.Datastore) (uint64, uint64, error) { q := query.Query{} results, err := datastore.Query(q) if err != nil { @@ -223,7 +218,3 @@ func getQueueHeadTail(ctx context.Context, name string, datastore ds.Datastore) return head, tail, nil } - -func (q *Queue) remove(key ds.Key) error { - return q.datastore.Delete(key) -} diff --git a/provider/queue_test.go b/provider/queue_test.go new file mode 100644 index 000000000..724eca4ee --- /dev/null +++ b/provider/queue_test.go @@ -0,0 +1,89 @@ +package provider + +import ( + "context" + "testing" + "time" + + cid "github.com/ipfs/go-cid" + datastore "github.com/ipfs/go-datastore" + sync "github.com/ipfs/go-datastore/sync" +) + +func makeCids(n int) []cid.Cid { + cids := make([]cid.Cid, 0, 10) + for i := 0; i < 10; i++ { + c := blockGenerator.Next().Cid() + cids = append(cids, c) + } + return cids +} + +func assertOrdered(cids []cid.Cid, q *Queue, t *testing.T) { + for _, c := range cids { + select { + case dequeued := <- q.dequeue: + if c != dequeued.cid { + t.Fatalf("Error in ordering of CIDs retrieved from queue. Expected: %s, got: %s", c, dequeued.cid) + } + + case <-time.After(time.Second * 1): + t.Fatal("Timeout waiting for cids to be provided.") + } + } +} + +func TestBasicOperation(t *testing.T) { + ctx := context.Background() + defer ctx.Done() + + ds := sync.MutexWrap(datastore.NewMapDatastore()) + queue, err := NewQueue(ctx, "test", ds) + if err != nil { + t.Fatal(err) + } + queue.Run() + + cids := makeCids(10) + + for _, c := range cids { + err = queue.Enqueue(c) + if err != nil { + t.Fatal("Failed to enqueue CID") + } + } + + assertOrdered(cids, queue, t) +} + +func TestInitialization(t *testing.T) { + ctx := context.Background() + defer ctx.Done() + + ds := sync.MutexWrap(datastore.NewMapDatastore()) + queue, err := NewQueue(ctx, "test", ds) + if err != nil { + t.Fatal(err) + } + queue.Run() + + cids := makeCids(10) + + for _, c := range cids { + err = queue.Enqueue(c) + if err != nil { + t.Fatal("Failed to enqueue CID") + } + } + + assertOrdered(cids[:5], queue, t) + + // make a new queue, same data + queue, err = NewQueue(ctx, "test", ds) + if err != nil { + t.Fatal(err) + } + queue.Run() + + assertOrdered(cids[5:], queue, t) +} From 3e36d7771e6109d0aff228d43dce0dc0d0626a33 Mon Sep 17 00:00:00 2001 From: Erik Ingenito Date: Fri, 15 Mar 2019 14:19:19 -0700 Subject: [PATCH 2985/3817] Remove locking entirely License: MIT Signed-off-by: Erik Ingenito This commit was moved from ipfs/go-ipfs-provider@e5715368caea9f8fa35f41a6cfacd25a58710d11 --- provider/provider.go | 22 ++--- provider/provider_test.go | 4 +- provider/queue.go | 168 +++++++++++++------------------------- provider/queue_test.go | 19 ++--- 4 files changed, 71 insertions(+), 142 deletions(-) diff --git a/provider/provider.go b/provider/provider.go index 7a30f6d27..7e149f777 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -11,13 +11,9 @@ import ( routing "github.com/libp2p/go-libp2p-routing" ) -var ( - log = logging.Logger("provider") -) +var log = logging.Logger("provider") -const ( - provideOutgoingWorkerLimit = 8 -) +const provideOutgoingWorkerLimit = 8 // Provider announces blocks to the network type Provider interface { @@ -44,13 +40,13 @@ func NewProvider(ctx context.Context, queue *Queue, contentRouting routing.Conte // Start workers to handle provide requests. func (p *provider) Run() { - p.queue.Run() p.handleAnnouncements() } // Provide the given cid using specified strategy. func (p *provider) Provide(root cid.Cid) error { - return p.queue.Enqueue(root) + p.queue.Enqueue(root) + return nil } // Handle all outgoing cids by providing (announcing) them @@ -61,12 +57,12 @@ func (p *provider) handleAnnouncements() { select { case <-p.ctx.Done(): return - case entry := <-p.queue.Dequeue(): - log.Info("announce - start - ", entry.cid) - if err := p.contentRouting.Provide(p.ctx, entry.cid, true); err != nil { - log.Warningf("Unable to provide entry: %s, %s", entry.cid, err) + case c := <-p.queue.Dequeue(): + log.Info("announce - start - ", c) + if err := p.contentRouting.Provide(p.ctx, c, true); err != nil { + log.Warningf("Unable to provide entry: %s, %s", c, err) } - log.Info("announce - end - ", entry.cid) + log.Info("announce - end - ", c) } } }() diff --git a/provider/provider_test.go b/provider/provider_test.go index 464d73d9a..95282e38a 100644 --- a/provider/provider_test.go +++ b/provider/provider_test.go @@ -42,7 +42,7 @@ func TestAnnouncement(t *testing.T) { cids := cid.NewSet() - for i := 0; i < 100; i++ { + for i := 0; i < 1000; i++ { c := blockGenerator.Next().Cid() cids.Add(c) } @@ -63,7 +63,7 @@ func TestAnnouncement(t *testing.T) { t.Fatal("Wrong CID provided") } cids.Remove(cp) - case <-time.After(time.Second * 1): + case <-time.After(time.Second * 5): t.Fatal("Timeout waiting for cids to be provided.") } } diff --git a/provider/queue.go b/provider/queue.go index f1c9945cd..3f7115f68 100644 --- a/provider/queue.go +++ b/provider/queue.go @@ -2,27 +2,15 @@ package provider import ( "context" - "errors" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/namespace" + "github.com/ipfs/go-datastore/query" "math" "strconv" "strings" - "sync" - - cid "github.com/ipfs/go-cid" - datastore "github.com/ipfs/go-datastore" - namespace "github.com/ipfs/go-datastore/namespace" - query "github.com/ipfs/go-datastore/query" ) -// Entry allows for the durability in the queue. When a cid is dequeued it is -// not removed from the datastore until you call Complete() on the entry you -// receive. -type Entry struct { - cid cid.Cid - key datastore.Key - queue *Queue -} - // Queue provides a durable, FIFO interface to the datastore for storing cids // // Durability just means that cids in the process of being provided when a @@ -32,17 +20,15 @@ type Queue struct { // used to differentiate queues in datastore // e.g. provider vs reprovider name string - ctx context.Context tail uint64 head uint64 - enqueueLock sync.Mutex ds datastore.Datastore // Must be threadsafe - dequeue chan *Entry - added chan struct{} + dequeue chan cid.Cid + enqueue chan cid.Cid } // NewQueue creates a queue for cids @@ -57,124 +43,85 @@ func NewQueue(ctx context.Context, name string, ds datastore.Datastore) (*Queue, ctx: ctx, head: head, tail: tail, - enqueueLock: sync.Mutex{}, ds: namespaced, - dequeue: make(chan *Entry), - added: make(chan struct{}), + dequeue: make(chan cid.Cid), + enqueue: make(chan cid.Cid), } + q.work() return q, nil } // Enqueue puts a cid in the queue -func (q *Queue) Enqueue(cid cid.Cid) error { - q.enqueueLock.Lock() - defer q.enqueueLock.Unlock() - - nextKey := q.queueKey(q.tail) - - if err := q.ds.Put(nextKey, cid.Bytes()); err != nil { - return err - } - - q.tail++ - +func (q *Queue) Enqueue(cid cid.Cid) { select { - case q.added <- struct{}{}: + case q.enqueue <- cid: case <-q.ctx.Done(): - default: } - - return nil } // Dequeue returns a channel that if listened to will remove entries from the queue -func (q *Queue) Dequeue() <-chan *Entry { +func (q *Queue) Dequeue() <-chan cid.Cid { return q.dequeue } -// IsEmpty returns whether or not the queue has any items -func (q *Queue) IsEmpty() bool { - return (q.tail - q.head) == 0 -} - -// Run dequeues items when the dequeue channel is available to -// be written to. -func (q *Queue) Run() { +// Run dequeues and enqueues when available. +func (q *Queue) work() { go func() { for { - if q.IsEmpty() { - select { - case <-q.ctx.Done(): - return - case <-q.added: + var c cid.Cid = cid.Undef + var key datastore.Key + var dequeue chan cid.Cid + + // If we're not empty dequeue a cid and ship it + if q.head < q.tail { + key = q.queueKey(q.head) + value, err := q.ds.Get(key) + + if err == datastore.ErrNotFound { + log.Warningf("Missing entry in queue: %s", err) + q.head++ + continue + } else if err != nil { + log.Warningf("Error fetching from queue: %s", err) + continue + } + + c, err = cid.Parse(value) + if err != nil { + log.Warningf("Error marshalling Cid from queue: ", err) + q.head++ + err = q.ds.Delete(key) + continue } } - entry, err := q.next() - if err != nil { - log.Warningf("Error Dequeue()-ing: %s, %s", entry, err) - continue + if c != cid.Undef { + dequeue = q.dequeue } select { + case toQueue := <-q.enqueue: + nextKey := q.queueKey(q.tail) + + if err := q.ds.Put(nextKey, toQueue.Bytes()); err != nil { + log.Errorf("Failed to enqueue cid: %s", err) + } + + q.tail++ + case dequeue <- c: + q.head++ + err := q.ds.Delete(key) + + if err != nil { + log.Errorf("Failed to delete queued cid: %s", err) + } case <-q.ctx.Done(): return - case q.dequeue <- entry: - q.head++ - err = q.ds.Delete(entry.key) } } }() } -// Find the next item in the queue, crawl forward if an entry is not -// found in the next spot. -func (q *Queue) next() (*Entry, error) { - var key datastore.Key - var value []byte - var err error - for { - if q.head >= q.tail { - return nil, errors.New("next: no more entries in queue returning") - } - select { - case <-q.ctx.Done(): - return nil, nil - default: - } - key = q.queueKey(q.head) - - value, err = q.ds.Get(key) - - value, err = q.ds.Get(key) - if err == datastore.ErrNotFound { - q.head++ - continue - } else if err != nil { - return nil, err - } else { - break - } - } - - id, err := cid.Parse(value) - if err != nil { - return nil, err - } - - entry := &Entry{ - cid: id, - key: key, - queue: q, - } - - if err != nil { - return nil, err - } - - return entry, nil -} - func (q *Queue) queueKey(id uint64) datastore.Key { return datastore.NewKey(strconv.FormatUint(id, 10)) } @@ -190,11 +137,6 @@ func getQueueHeadTail(ctx context.Context, name string, datastore datastore.Data var tail uint64 var head uint64 = math.MaxUint64 for entry := range results.Next() { - select { - case <-ctx.Done(): - return 0, 0, nil - default: - } trimmed := strings.TrimPrefix(entry.Key, "/") id, err := strconv.ParseUint(trimmed, 10, 64) if err != nil { diff --git a/provider/queue_test.go b/provider/queue_test.go index 724eca4ee..2ac2de288 100644 --- a/provider/queue_test.go +++ b/provider/queue_test.go @@ -11,7 +11,7 @@ import ( ) func makeCids(n int) []cid.Cid { - cids := make([]cid.Cid, 0, 10) + cids := make([]cid.Cid, 0, n) for i := 0; i < 10; i++ { c := blockGenerator.Next().Cid() cids = append(cids, c) @@ -23,8 +23,8 @@ func assertOrdered(cids []cid.Cid, q *Queue, t *testing.T) { for _, c := range cids { select { case dequeued := <- q.dequeue: - if c != dequeued.cid { - t.Fatalf("Error in ordering of CIDs retrieved from queue. Expected: %s, got: %s", c, dequeued.cid) + if c != dequeued { + t.Fatalf("Error in ordering of CIDs retrieved from queue. Expected: %s, got: %s", c, dequeued) } case <-time.After(time.Second * 1): @@ -42,15 +42,11 @@ func TestBasicOperation(t *testing.T) { if err != nil { t.Fatal(err) } - queue.Run() cids := makeCids(10) for _, c := range cids { - err = queue.Enqueue(c) - if err != nil { - t.Fatal("Failed to enqueue CID") - } + queue.Enqueue(c) } assertOrdered(cids, queue, t) @@ -65,15 +61,11 @@ func TestInitialization(t *testing.T) { if err != nil { t.Fatal(err) } - queue.Run() cids := makeCids(10) for _, c := range cids { - err = queue.Enqueue(c) - if err != nil { - t.Fatal("Failed to enqueue CID") - } + queue.Enqueue(c) } assertOrdered(cids[:5], queue, t) @@ -83,7 +75,6 @@ func TestInitialization(t *testing.T) { if err != nil { t.Fatal(err) } - queue.Run() assertOrdered(cids[5:], queue, t) } From fcb323293ff1605f7f75b75d5e55beeeaa28584b Mon Sep 17 00:00:00 2001 From: Erik Ingenito Date: Fri, 15 Mar 2019 16:36:43 -0700 Subject: [PATCH 2986/3817] Gofmt License: MIT Signed-off-by: Erik Ingenito This commit was moved from ipfs/go-ipfs-provider@4f00ef1bf2a154f62b355b86cc12d8c463158529 --- provider/provider.go | 2 +- provider/provider_test.go | 20 ++++++++++---------- provider/queue.go | 31 ++++++++++++++----------------- provider/queue_test.go | 2 +- 4 files changed, 26 insertions(+), 29 deletions(-) diff --git a/provider/provider.go b/provider/provider.go index 7e149f777..a5093d65b 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -11,7 +11,7 @@ import ( routing "github.com/libp2p/go-libp2p-routing" ) -var log = logging.Logger("provider") +var log = logging.Logger("provider") const provideOutgoingWorkerLimit = 8 diff --git a/provider/provider_test.go b/provider/provider_test.go index 95282e38a..7836f04ce 100644 --- a/provider/provider_test.go +++ b/provider/provider_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" - blocksutil "github.com/ipfs/go-ipfs-blocksutil" cid "github.com/ipfs/go-cid" datastore "github.com/ipfs/go-datastore" - pstore "github.com/libp2p/go-libp2p-peerstore" sync "github.com/ipfs/go-datastore/sync" + blocksutil "github.com/ipfs/go-ipfs-blocksutil" + pstore "github.com/libp2p/go-libp2p-peerstore" ) var blockGenerator = blocksutil.NewBlockGenerator() @@ -58,13 +58,13 @@ func TestAnnouncement(t *testing.T) { for cids.Len() > 0 { select { - case cp := <-r.provided: - if !cids.Has(cp) { - t.Fatal("Wrong CID provided") - } - cids.Remove(cp) - case <-time.After(time.Second * 5): - t.Fatal("Timeout waiting for cids to be provided.") + case cp := <-r.provided: + if !cids.Has(cp) { + t.Fatal("Wrong CID provided") + } + cids.Remove(cp) + case <-time.After(time.Second * 5): + t.Fatal("Timeout waiting for cids to be provided.") } } } @@ -77,4 +77,4 @@ func (r *mockRouting) Provide(ctx context.Context, cid cid.Cid, recursive bool) // Search for peers who are able to provide a given key func (r *mockRouting) FindProvidersAsync(ctx context.Context, cid cid.Cid, timeout int) <-chan pstore.PeerInfo { return nil -} \ No newline at end of file +} diff --git a/provider/queue.go b/provider/queue.go index 3f7115f68..b5b7ba709 100644 --- a/provider/queue.go +++ b/provider/queue.go @@ -19,14 +19,11 @@ import ( type Queue struct { // used to differentiate queues in datastore // e.g. provider vs reprovider - name string - ctx context.Context - - tail uint64 - head uint64 - - ds datastore.Datastore // Must be threadsafe - + name string + ctx context.Context + tail uint64 + head uint64 + ds datastore.Datastore // Must be threadsafe dequeue chan cid.Cid enqueue chan cid.Cid } @@ -39,13 +36,13 @@ func NewQueue(ctx context.Context, name string, ds datastore.Datastore) (*Queue, return nil, err } q := &Queue{ - name: name, - ctx: ctx, - head: head, - tail: tail, - ds: namespaced, - dequeue: make(chan cid.Cid), - enqueue: make(chan cid.Cid), + name: name, + ctx: ctx, + head: head, + tail: tail, + ds: namespaced, + dequeue: make(chan cid.Cid), + enqueue: make(chan cid.Cid), } q.work() return q, nil @@ -54,8 +51,8 @@ func NewQueue(ctx context.Context, name string, ds datastore.Datastore) (*Queue, // Enqueue puts a cid in the queue func (q *Queue) Enqueue(cid cid.Cid) { select { - case q.enqueue <- cid: - case <-q.ctx.Done(): + case q.enqueue <- cid: + case <-q.ctx.Done(): } } diff --git a/provider/queue_test.go b/provider/queue_test.go index 2ac2de288..0dd3bab09 100644 --- a/provider/queue_test.go +++ b/provider/queue_test.go @@ -22,7 +22,7 @@ func makeCids(n int) []cid.Cid { func assertOrdered(cids []cid.Cid, q *Queue, t *testing.T) { for _, c := range cids { select { - case dequeued := <- q.dequeue: + case dequeued := <-q.dequeue: if c != dequeued { t.Fatalf("Error in ordering of CIDs retrieved from queue. Expected: %s, got: %s", c, dequeued) } From 12a43f1b1bf8876d45b32ff9c9cf0934a37d864a Mon Sep 17 00:00:00 2001 From: Erik Ingenito Date: Fri, 15 Mar 2019 20:45:59 -0700 Subject: [PATCH 2987/3817] Make queue operation more clear License: MIT Signed-off-by: Erik Ingenito This commit was moved from ipfs/go-ipfs-provider@25a24e573442724e5343c4d77750fd19b9382111 --- provider/provider_test.go | 2 +- provider/queue.go | 72 ++++++++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/provider/provider_test.go b/provider/provider_test.go index 7836f04ce..14cd68521 100644 --- a/provider/provider_test.go +++ b/provider/provider_test.go @@ -42,7 +42,7 @@ func TestAnnouncement(t *testing.T) { cids := cid.NewSet() - for i := 0; i < 1000; i++ { + for i := 0; i < 100; i++ { c := blockGenerator.Next().Cid() cids.Add(c) } diff --git a/provider/queue.go b/provider/queue.go index b5b7ba709..122077954 100644 --- a/provider/queue.go +++ b/provider/queue.go @@ -61,37 +61,50 @@ func (q *Queue) Dequeue() <-chan cid.Cid { return q.dequeue } +type entry struct { + cid cid.Cid + key datastore.Key +} + +// Look for next Cid in the queue and return it. Skip over gaps and mangled data +func (q *Queue) nextEntry() (datastore.Key, cid.Cid) { + for { + if q.head >= q.tail { + return datastore.Key{}, cid.Undef + } + + key := q.queueKey(q.head) + value, err := q.ds.Get(key) + + if err == datastore.ErrNotFound { + log.Warningf("Error missing entry in queue: %s", key) + q.head++ // move on + continue + } else if err != nil { + log.Warningf("Error fetching from queue: %s", err) + continue + } + + c, err := cid.Parse(value) + if err != nil { + log.Warningf("Error marshalling Cid from queue: ", err) + q.head++ + err = q.ds.Delete(key) + continue + } + + return key, c + } +} + // Run dequeues and enqueues when available. func (q *Queue) work() { go func() { + for { - var c cid.Cid = cid.Undef - var key datastore.Key + k, c := q.nextEntry() var dequeue chan cid.Cid - // If we're not empty dequeue a cid and ship it - if q.head < q.tail { - key = q.queueKey(q.head) - value, err := q.ds.Get(key) - - if err == datastore.ErrNotFound { - log.Warningf("Missing entry in queue: %s", err) - q.head++ - continue - } else if err != nil { - log.Warningf("Error fetching from queue: %s", err) - continue - } - - c, err = cid.Parse(value) - if err != nil { - log.Warningf("Error marshalling Cid from queue: ", err) - q.head++ - err = q.ds.Delete(key) - continue - } - } - if c != cid.Undef { dequeue = q.dequeue } @@ -102,16 +115,19 @@ func (q *Queue) work() { if err := q.ds.Put(nextKey, toQueue.Bytes()); err != nil { log.Errorf("Failed to enqueue cid: %s", err) + continue } q.tail++ case dequeue <- c: - q.head++ - err := q.ds.Delete(key) + err := q.ds.Delete(k) if err != nil { - log.Errorf("Failed to delete queued cid: %s", err) + log.Errorf("Failed to delete queued cid %s with key %s: %s", c, k, err) + continue } + + q.head++ case <-q.ctx.Done(): return } From 003c27468eaf1543f70ace2f746ccfbbd75007e7 Mon Sep 17 00:00:00 2001 From: Erik Ingenito Date: Sat, 16 Mar 2019 09:52:24 -0700 Subject: [PATCH 2988/3817] Don't do extra work in provider queue loop License: MIT Signed-off-by: Erik Ingenito This commit was moved from ipfs/go-ipfs-provider@74884fbe8db563c9d1970a51fdaf683497ceee04 --- provider/queue.go | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/provider/queue.go b/provider/queue.go index 122077954..a3268e109 100644 --- a/provider/queue.go +++ b/provider/queue.go @@ -2,13 +2,14 @@ package provider import ( "context" - "github.com/ipfs/go-cid" - "github.com/ipfs/go-datastore" - "github.com/ipfs/go-datastore/namespace" - "github.com/ipfs/go-datastore/query" "math" "strconv" "strings" + + cid "github.com/ipfs/go-cid" + datastore "github.com/ipfs/go-datastore" + namespace "github.com/ipfs/go-datastore/namespace" + query "github.com/ipfs/go-datastore/query" ) // Queue provides a durable, FIFO interface to the datastore for storing cids @@ -100,11 +101,16 @@ func (q *Queue) nextEntry() (datastore.Key, cid.Cid) { // Run dequeues and enqueues when available. func (q *Queue) work() { go func() { + var k datastore.Key = datastore.Key{} + var c cid.Cid = cid.Undef for { - k, c := q.nextEntry() - var dequeue chan cid.Cid + if c == cid.Undef { + k, c = q.nextEntry() + } + // If c != cid.Undef set dequeue and attempt write, otherwise wait for enqueue + var dequeue chan cid.Cid if c != cid.Undef { dequeue = q.dequeue } @@ -126,7 +132,7 @@ func (q *Queue) work() { log.Errorf("Failed to delete queued cid %s with key %s: %s", c, k, err) continue } - + c = cid.Undef q.head++ case <-q.ctx.Done(): return From d9a3636df6eaabe502d3a5111e378cc5aee9d215 Mon Sep 17 00:00:00 2001 From: Erik Ingenito Date: Sat, 16 Mar 2019 09:52:57 -0700 Subject: [PATCH 2989/3817] Additional provider tests License: MIT Signed-off-by: Erik Ingenito This commit was moved from ipfs/go-ipfs-provider@96406fe08f4fc1249fbd6f361ccb7e745e13477b --- provider/provider_test.go | 19 +++++++------- provider/queue_test.go | 53 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/provider/provider_test.go b/provider/provider_test.go index 14cd68521..7ef007b03 100644 --- a/provider/provider_test.go +++ b/provider/provider_test.go @@ -19,6 +19,15 @@ type mockRouting struct { provided chan cid.Cid } +func (r *mockRouting) Provide(ctx context.Context, cid cid.Cid, recursive bool) error { + r.provided <- cid + return nil +} + +func (r *mockRouting) FindProvidersAsync(ctx context.Context, cid cid.Cid, timeout int) <-chan pstore.PeerInfo { + return nil +} + func mockContentRouting() *mockRouting { r := mockRouting{} r.provided = make(chan cid.Cid) @@ -68,13 +77,3 @@ func TestAnnouncement(t *testing.T) { } } } - -func (r *mockRouting) Provide(ctx context.Context, cid cid.Cid, recursive bool) error { - r.provided <- cid - return nil -} - -// Search for peers who are able to provide a given key -func (r *mockRouting) FindProvidersAsync(ctx context.Context, cid cid.Cid, timeout int) <-chan pstore.PeerInfo { - return nil -} diff --git a/provider/queue_test.go b/provider/queue_test.go index 0dd3bab09..e1b74878e 100644 --- a/provider/queue_test.go +++ b/provider/queue_test.go @@ -52,7 +52,37 @@ func TestBasicOperation(t *testing.T) { assertOrdered(cids, queue, t) } -func TestInitialization(t *testing.T) { +func TestSparseDatastore(t *testing.T) { + ctx := context.Background() + defer ctx.Done() + + ds := sync.MutexWrap(datastore.NewMapDatastore()) + queue, err := NewQueue(ctx, "test", ds) + if err != nil { + t.Fatal(err) + } + + cids := makeCids(10) + for _, c := range cids { + queue.Enqueue(c) + } + + // remove entries in the middle + err = queue.ds.Delete(queue.queueKey(5)) + if err != nil { + t.Fatal(err) + } + + err = queue.ds.Delete(queue.queueKey(6)) + if err != nil { + t.Fatal(err) + } + + expected := append(cids[:5], cids[7:]...) + assertOrdered(expected, queue, t) +} + +func TestMangledData(t *testing.T) { ctx := context.Background() defer ctx.Done() @@ -63,7 +93,28 @@ func TestInitialization(t *testing.T) { } cids := makeCids(10) + for _, c := range cids { + queue.Enqueue(c) + } + + // remove entries in the middle + err = queue.ds.Put(queue.queueKey(5), []byte("borked")) + expected := append(cids[:5], cids[6:]...) + assertOrdered(expected, queue, t) +} + +func TestInitialization(t *testing.T) { + ctx := context.Background() + defer ctx.Done() + + ds := sync.MutexWrap(datastore.NewMapDatastore()) + queue, err := NewQueue(ctx, "test", ds) + if err != nil { + t.Fatal(err) + } + + cids := makeCids(10) for _, c := range cids { queue.Enqueue(c) } From 8ddd53922d9de50f79438685a45641d30aaf3ee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 19 Mar 2019 18:01:12 +0100 Subject: [PATCH 2990/3817] return flushed node from FlushPath This commit was moved from ipfs/go-mfs@d9d9b305eb473d0bee8b3c987fa817273a6d9ad8 --- mfs/mfs_test.go | 12 ++++++++---- mfs/ops.go | 8 ++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 539b71a32..8d64fe581 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -817,19 +817,23 @@ func TestFlushing(t *testing.T) { t.Fatal(err) } - if err := FlushPath(ctx, rt, "/a/b/c/TEST"); err != nil { + nd, err := FlushPath(ctx, rt, "/a/b/c/TEST") + if err != nil { t.Fatal(err) } + if nd.Cid().String() != "QmYi7wrRFKVCcTB56A6Pep2j31Q5mHfmmu21RzHXu25RVR" { + t.Fatalf("unexpected node from FlushPath: %s", nd.Cid()) + } - if err := FlushPath(ctx, rt, "/a/b/d/TEST"); err != nil { + if _, err := FlushPath(ctx, rt, "/a/b/d/TEST"); err != nil { t.Fatal(err) } - if err := FlushPath(ctx, rt, "/a/b/e/TEST"); err != nil { + if _, err := FlushPath(ctx, rt, "/a/b/e/TEST"); err != nil { t.Fatal(err) } - if err := FlushPath(ctx, rt, "/FILE"); err != nil { + if _, err := FlushPath(ctx, rt, "/FILE"); err != nil { t.Fatal(err) } diff --git a/mfs/ops.go b/mfs/ops.go index 6e99e23f2..bf05cd443 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -226,17 +226,17 @@ func DirLookup(d *Directory, pth string) (FSNode, error) { // TODO: Document this function and link its functionality // with the republisher. -func FlushPath(ctx context.Context, rt *Root, pth string) error { +func FlushPath(ctx context.Context, rt *Root, pth string) (ipld.Node, error) { nd, err := Lookup(rt, pth) if err != nil { - return err + return nil, err } err = nd.Flush() if err != nil { - return err + return nil, err } rt.repub.WaitPub(ctx) - return nil + return nd.GetNode() } From 25f76b08a84f0e1e84990e55b11c6821f12b00f8 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Wed, 20 Mar 2019 14:13:30 -0700 Subject: [PATCH 2991/3817] Add comments; Check ctx.Err(); Move import License: MIT Signed-off-by: Michael Avila This commit was moved from ipfs/go-ipfs-provider@b34d1787a57c9a6c1c8c4dea9599041cf6db41c9 --- provider/provider.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/provider/provider.go b/provider/provider.go index a5093d65b..f9aa4ed78 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -5,10 +5,9 @@ package provider import ( "context" - - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log" - routing "github.com/libp2p/go-libp2p-routing" + "github.com/libp2p/go-libp2p-routing" ) var log = logging.Logger("provider") @@ -17,7 +16,9 @@ const provideOutgoingWorkerLimit = 8 // Provider announces blocks to the network type Provider interface { + // Run is used to begin processing the provider work Run() + // Provide takes a cid and makes an attempt to announce it to the network Provide(cid.Cid) error } @@ -53,7 +54,7 @@ func (p *provider) Provide(root cid.Cid) error { func (p *provider) handleAnnouncements() { for workers := 0; workers < provideOutgoingWorkerLimit; workers++ { go func() { - for { + for p.ctx.Err() == nil { select { case <-p.ctx.Done(): return From eef47968f53038f837a20a7fb4a5da6074b763a1 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 20 Mar 2019 19:10:44 -0700 Subject: [PATCH 2992/3817] gc: fix a potential deadlock Events: 1. User triggers a GC. 2. User aborts the GC. 3. We fail to delete a block when the output channel is already full. This is really unlikely to happen in practice but it's still incorrect. Could be related to #6107 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@eb33dc1f4c645e264052f50f4231e737cbacddc8 --- pinning/pinner/gc/gc.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 9234d4368..12b0fadb2 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -83,7 +83,7 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn var removed uint64 loop: - for { + for ctx.Err() == nil { // select may not notice that we're "done". select { case k, ok := <-keychan: if !ok { @@ -94,8 +94,11 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn removed++ if err != nil { errors = true - output <- Result{Error: &CannotDeleteBlockError{k, err}} - //log.Errorf("Error removing key from blockstore: %s", err) + select { + case output <- Result{Error: &CannotDeleteBlockError{k, err}}: + case <-ctx.Done(): + break loop + } // continue as error is non-fatal continue loop } From 1ec848f9a24d9c67927520cedcce7c2393893b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 25 Feb 2019 17:10:23 +0100 Subject: [PATCH 2993/3817] unixfs add: Changes for fixed wrap logic This commit was moved from ipfs/interface-go-ipfs-core@e87318a2c3620d2402517a6833e21749c065a397 --- coreiface/options/unixfs.go | 11 ++++ coreiface/tests/unixfs.go | 116 +++++++++++++++++++++--------------- 2 files changed, 80 insertions(+), 47 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index b76b01adf..44ba8c7cd 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -35,6 +35,7 @@ type UnixfsAddSettings struct { Wrap bool Hidden bool + TopHidden bool StdinName string Events chan<- interface{} @@ -69,6 +70,7 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, Wrap: false, Hidden: false, + TopHidden: false, StdinName: "", Events: nil, @@ -255,6 +257,15 @@ func (unixfsOpts) Hidden(hidden bool) UnixfsAddOption { } } +// TopHidden enables adding of hidden files in top-level directory (files +// prefixed with '.') +func (unixfsOpts) TopHidden(hidden bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.TopHidden = hidden + return nil + } +} + // StdinName is the name set for files which don specify FilePath as // os.Stdin.Name() func (unixfsOpts) StdinName(name string) UnixfsAddOption { diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index bbcb66899..1ad319333 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -82,11 +82,14 @@ func flatDir() files.Node { }) } -func wrapped(name string) func(f files.Node) files.Node { +func wrapped(names ...string) func(f files.Node) files.Node { return func(f files.Node) files.Node { - return files.NewMapDirectory(map[string]files.Node{ - name: f, - }) + for i := range names { + f = files.NewMapDirectory(map[string]files.Node{ + names[len(names)-i-1]: f, + }) + } + return f } } @@ -241,16 +244,30 @@ func (tp *provider) TestAdd(t *testing.T) { }, // multi file { - name: "simpleDir", + name: "simpleDirNoWrap", data: flatDir, - wrap: "t", path: "/ipfs/QmRKGpFfR32FVXdvJiHfo4WJ5TDYBsM1P9raAp1p6APWSp", }, { - name: "twoLevelDir", - data: twoLevelDir(), - wrap: "t", - path: "/ipfs/QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr", + name: "simpleDirWrap", + data: flatDir, + expect: wrapped("QmRKGpFfR32FVXdvJiHfo4WJ5TDYBsM1P9raAp1p6APWSp"), + path: "/ipfs/QmXxCaQkC8Z6Qws1nTkTQfCsL9y4XvWXnrPokp9bhmjC1L", + opts: []options.UnixfsAddOption{options.Unixfs.Wrap(true)}, + }, + { + name: "simpleDir", + data: flatDir, + wrap: "t", + expect: wrapped("t"), + path: "/ipfs/Qmc3nGXm1HtUVCmnXLQHvWcNwfdZGpfg2SRm1CxLf7Q2Rm", + }, + { + name: "twoLevelDir", + data: twoLevelDir(), + wrap: "t", + expect: wrapped("t"), + path: "/ipfs/QmPwsL3T5sWhDmmAWZHAzyjKtMVDS9a11aHNRqb3xoVnmg", }, // wrapped { @@ -261,15 +278,6 @@ func (tp *provider) TestAdd(t *testing.T) { }, wrap: "foo", expect: wrapped("foo"), - opts: []options.UnixfsAddOption{options.Unixfs.Wrap(true)}, - }, - { - name: "addNotWrappedDirFile", - path: hello, - data: func() files.Node { - return files.NewBytesFile([]byte(helloStr)) - }, - wrap: "foo", }, { name: "stdinWrapped", @@ -306,16 +314,16 @@ func (tp *provider) TestAdd(t *testing.T) { name: "twoLevelDirWrapped", data: twoLevelDir(), wrap: "t", - expect: wrapped("t"), - path: "/ipfs/QmPwsL3T5sWhDmmAWZHAzyjKtMVDS9a11aHNRqb3xoVnmg", + expect: wrapped("QmPwsL3T5sWhDmmAWZHAzyjKtMVDS9a11aHNRqb3xoVnmg", "t"), + path: "/ipfs/QmXzZwAh34pmNjuKsVGZfpbByis5S5qeZjCCUxa1ajZqzH", opts: []options.UnixfsAddOption{options.Unixfs.Wrap(true)}, }, { name: "twoLevelInlineHash", data: twoLevelDir(), wrap: "t", - expect: wrapped("t"), - path: "/ipfs/zBunoruKoyCHKkALNSWxDvj4L7yuQnMgQ4hUa9j1Z64tVcDEcu6Zdetyu7eeFCxMPfxb7YJvHeFHoFoHMkBUQf6vfdhmi", + expect: wrapped("zBunoruKoyCHKkALNSWxDvj4L7yuQnMgQ4hUa9j1Z64tVcDEcu6Zdetyu7eeFCxMPfxb7YJvHeFHoFoHMkBUQf6vfdhmi", "t"), + path: "/ipfs/QmUX6GykDGHTMtLmDkfjqs48QwQK82vou51xwaY9TSU7Zo", opts: []options.UnixfsAddOption{options.Unixfs.Wrap(true), options.Unixfs.Inline(true), options.Unixfs.RawLeaves(true), options.Unixfs.Hash(mh.SHA3)}, }, // hidden @@ -328,17 +336,20 @@ func (tp *provider) TestAdd(t *testing.T) { "foo": files.NewBytesFile([]byte("hello1")), }) }, - wrap: "t", - path: "/ipfs/QmehGvpf2hY196MzDFmjL8Wy27S4jbgGDUAhBJyvXAwr3g", - opts: []options.UnixfsAddOption{options.Unixfs.Hidden(true)}, + wrap: "t", + expect: wrapped("t"), + path: "/ipfs/QmPXLSBX382vJDLrGakcbrZDkU3grfkjMox7EgSC9KFbtQ", + opts: []options.UnixfsAddOption{options.Unixfs.Hidden(true)}, }, { - name: "hiddenFileAlwaysAdded", + name: "topHiddenFileAdded", data: func() files.Node { return files.NewBytesFile([]byte(helloStr)) }, - wrap: ".foo", - path: hello, + wrap: ".foo", + expect: wrapped(".foo"), + path: "/ipfs/QmciAVG3krCbvzUaK9gr6jUgfEjQtYmuuXi1n67teQ4Ni2", + opts: []options.UnixfsAddOption{options.Unixfs.TopHidden(true)}, }, { name: "hiddenFilesNotAdded", @@ -352,10 +363,25 @@ func (tp *provider) TestAdd(t *testing.T) { expect: func(files.Node) files.Node { return flatDir() }, - wrap: "t", path: "/ipfs/QmRKGpFfR32FVXdvJiHfo4WJ5TDYBsM1P9raAp1p6APWSp", opts: []options.UnixfsAddOption{options.Unixfs.Hidden(false)}, }, + { + name: "hiddenFilesWrappedNotAdded", + data: func() files.Node { + return files.NewMapDirectory(map[string]files.Node{ + ".bar": files.NewBytesFile([]byte("hello2")), + "bar": files.NewBytesFile([]byte("hello2")), + "foo": files.NewBytesFile([]byte("hello1")), + }) + }, + expect: func(files.Node) files.Node { + return wrapped("t")(flatDir()) + }, + wrap: "t", + path: "/ipfs/Qmc3nGXm1HtUVCmnXLQHvWcNwfdZGpfg2SRm1CxLf7Q2Rm", + opts: []options.UnixfsAddOption{options.Unixfs.Hidden(false)}, + }, // NoCopy { name: "simpleNoCopy", @@ -392,10 +418,9 @@ func (tp *provider) TestAdd(t *testing.T) { data: twoLevelDir(), path: "/ipfs/QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr", events: []coreiface.AddEvent{ - {Name: "t/abc", Path: p("QmU7nuGs2djqK99UNsNgEPGh6GV4662p6WtsgccBNGTDxt"), Size: "62"}, - {Name: "t", Path: p("QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr"), Size: "229"}, + {Name: "abc", Path: p("QmU7nuGs2djqK99UNsNgEPGh6GV4662p6WtsgccBNGTDxt"), Size: "62"}, + {Name: "", Path: p("QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr"), Size: "229"}, }, - wrap: "t", opts: []options.UnixfsAddOption{options.Unixfs.Silent(true)}, }, { @@ -403,13 +428,12 @@ func (tp *provider) TestAdd(t *testing.T) { data: twoLevelDir(), path: "/ipfs/QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr", events: []coreiface.AddEvent{ - {Name: "t/abc/def", Path: p("QmNyJpQkU1cEkBwMDhDNFstr42q55mqG5GE5Mgwug4xyGk"), Size: "13"}, - {Name: "t/bar", Path: p("QmS21GuXiRMvJKHos4ZkEmQDmRBqRaF5tQS2CQCu2ne9sY"), Size: "14"}, - {Name: "t/foo", Path: p("QmfAjGiVpTN56TXi6SBQtstit5BEw3sijKj1Qkxn6EXKzJ"), Size: "14"}, - {Name: "t/abc", Path: p("QmU7nuGs2djqK99UNsNgEPGh6GV4662p6WtsgccBNGTDxt"), Size: "62"}, - {Name: "t", Path: p("QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr"), Size: "229"}, + {Name: "abc/def", Path: p("QmNyJpQkU1cEkBwMDhDNFstr42q55mqG5GE5Mgwug4xyGk"), Size: "13"}, + {Name: "bar", Path: p("QmS21GuXiRMvJKHos4ZkEmQDmRBqRaF5tQS2CQCu2ne9sY"), Size: "14"}, + {Name: "foo", Path: p("QmfAjGiVpTN56TXi6SBQtstit5BEw3sijKj1Qkxn6EXKzJ"), Size: "14"}, + {Name: "abc", Path: p("QmU7nuGs2djqK99UNsNgEPGh6GV4662p6WtsgccBNGTDxt"), Size: "62"}, + {Name: "", Path: p("QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr"), Size: "229"}, }, - wrap: "t", }, { name: "progress1M", @@ -528,14 +552,14 @@ func (tp *provider) TestAdd(t *testing.T) { _, origDir := orig.(files.Directory) _, gotDir := got.(files.Directory) - if origDir != gotDir { - t.Fatal("file type mismatch") - } - if origName != gotName { t.Errorf("file name mismatch, orig='%s', got='%s'", origName, gotName) } + if origDir != gotDir { + t.Fatalf("file type mismatch on %s", origName) + } + if !gotDir { defer orig.Close() defer got.Close() @@ -804,9 +828,7 @@ func (tp *provider) TestEntriesExpired(t *testing.T) { r := strings.NewReader("content-of-file") p, err := api.Unixfs().Add(ctx, files.NewMapDirectory(map[string]files.Node{ - "0": files.NewMapDirectory(map[string]files.Node{ - "name-of-file": files.NewReaderFile(r), - }), + "name-of-file": files.NewReaderFile(r), })) if err != nil { t.Error(err) @@ -846,7 +868,7 @@ func (tp *provider) TestLsEmptyDir(t *testing.T) { t.Error(err) } - _, err = api.Unixfs().Add(ctx, files.NewMapDirectory(map[string]files.Node{"0": files.NewSliceDirectory([]files.DirEntry{})})) + _, err = api.Unixfs().Add(ctx, files.NewSliceDirectory([]files.DirEntry{})) if err != nil { t.Error(err) } From 4274224bd095962491241080f0b1fc794d999239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 10 Mar 2019 22:10:02 +0100 Subject: [PATCH 2994/3817] unixfs add: Remove hidden file handling This commit was moved from ipfs/interface-go-ipfs-core@56944d64d1ad4bb349a3d1a30633d5bea06d6a2e --- coreiface/options/unixfs.go | 21 ------------------ coreiface/tests/unixfs.go | 44 +------------------------------------ 2 files changed, 1 insertion(+), 64 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 44ba8c7cd..574d46b98 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -34,8 +34,6 @@ type UnixfsAddSettings struct { NoCopy bool Wrap bool - Hidden bool - TopHidden bool StdinName string Events chan<- interface{} @@ -69,8 +67,6 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, NoCopy: false, Wrap: false, - Hidden: false, - TopHidden: false, StdinName: "", Events: nil, @@ -249,23 +245,6 @@ func (unixfsOpts) Wrap(wrap bool) UnixfsAddOption { } } -// Hidden enables adding of hidden files (files prefixed with '.') -func (unixfsOpts) Hidden(hidden bool) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.Hidden = hidden - return nil - } -} - -// TopHidden enables adding of hidden files in top-level directory (files -// prefixed with '.') -func (unixfsOpts) TopHidden(hidden bool) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.TopHidden = hidden - return nil - } -} - // StdinName is the name set for files which don specify FilePath as // os.Stdin.Name() func (unixfsOpts) StdinName(name string) UnixfsAddOption { diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 1ad319333..0defd2f32 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -328,7 +328,7 @@ func (tp *provider) TestAdd(t *testing.T) { }, // hidden { - name: "hiddenFiles", + name: "hiddenFilesAdded", data: func() files.Node { return files.NewMapDirectory(map[string]files.Node{ ".bar": files.NewBytesFile([]byte("hello2")), @@ -339,48 +339,6 @@ func (tp *provider) TestAdd(t *testing.T) { wrap: "t", expect: wrapped("t"), path: "/ipfs/QmPXLSBX382vJDLrGakcbrZDkU3grfkjMox7EgSC9KFbtQ", - opts: []options.UnixfsAddOption{options.Unixfs.Hidden(true)}, - }, - { - name: "topHiddenFileAdded", - data: func() files.Node { - return files.NewBytesFile([]byte(helloStr)) - }, - wrap: ".foo", - expect: wrapped(".foo"), - path: "/ipfs/QmciAVG3krCbvzUaK9gr6jUgfEjQtYmuuXi1n67teQ4Ni2", - opts: []options.UnixfsAddOption{options.Unixfs.TopHidden(true)}, - }, - { - name: "hiddenFilesNotAdded", - data: func() files.Node { - return files.NewMapDirectory(map[string]files.Node{ - ".bar": files.NewBytesFile([]byte("hello2")), - "bar": files.NewBytesFile([]byte("hello2")), - "foo": files.NewBytesFile([]byte("hello1")), - }) - }, - expect: func(files.Node) files.Node { - return flatDir() - }, - path: "/ipfs/QmRKGpFfR32FVXdvJiHfo4WJ5TDYBsM1P9raAp1p6APWSp", - opts: []options.UnixfsAddOption{options.Unixfs.Hidden(false)}, - }, - { - name: "hiddenFilesWrappedNotAdded", - data: func() files.Node { - return files.NewMapDirectory(map[string]files.Node{ - ".bar": files.NewBytesFile([]byte("hello2")), - "bar": files.NewBytesFile([]byte("hello2")), - "foo": files.NewBytesFile([]byte("hello1")), - }) - }, - expect: func(files.Node) files.Node { - return wrapped("t")(flatDir()) - }, - wrap: "t", - path: "/ipfs/Qmc3nGXm1HtUVCmnXLQHvWcNwfdZGpfg2SRm1CxLf7Q2Rm", - opts: []options.UnixfsAddOption{options.Unixfs.Hidden(false)}, }, // NoCopy { From bd1689b886cda8cd5095b711df1eba9d15cabc7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 11 Mar 2019 13:36:57 +0100 Subject: [PATCH 2995/3817] unixfs: fix ls test for new add This commit was moved from ipfs/interface-go-ipfs-core@91f8aac428155f9f302c3d6327c5f8659742013f --- coreiface/tests/unixfs.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 0defd2f32..d4af7c3f0 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -724,10 +724,8 @@ func (tp *provider) TestLs(t *testing.T) { r := strings.NewReader("content-of-file") p, err := api.Unixfs().Add(ctx, files.NewMapDirectory(map[string]files.Node{ - "0": files.NewMapDirectory(map[string]files.Node{ - "name-of-file": files.NewReaderFile(r), - "name-of-symlink": files.NewLinkFile("/foo/bar", nil), - }), + "name-of-file": files.NewReaderFile(r), + "name-of-symlink": files.NewLinkFile("/foo/bar", nil), })) if err != nil { t.Fatal(err) From f3f74adfdbb7715898650b91985c61e3ba7a2788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 11 Mar 2019 15:58:40 +0100 Subject: [PATCH 2996/3817] unixfs add: remove StdinName This commit was moved from ipfs/interface-go-ipfs-core@e12c21afc03931525ceefc18be0bda8c71818d29 --- coreiface/options/unixfs.go | 15 ++------------- coreiface/tests/unixfs.go | 18 ------------------ 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 574d46b98..578eb5320 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -33,8 +33,7 @@ type UnixfsAddSettings struct { FsCache bool NoCopy bool - Wrap bool - StdinName string + Wrap bool Events chan<- interface{} Silent bool @@ -66,8 +65,7 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, FsCache: false, NoCopy: false, - Wrap: false, - StdinName: "", + Wrap: false, Events: nil, Silent: false, @@ -245,15 +243,6 @@ func (unixfsOpts) Wrap(wrap bool) UnixfsAddOption { } } -// StdinName is the name set for files which don specify FilePath as -// os.Stdin.Name() -func (unixfsOpts) StdinName(name string) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.StdinName = name - return nil - } -} - // Events specifies channel which will be used to report events about ongoing // Add operation. // diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index d4af7c3f0..c27826b51 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -292,24 +292,6 @@ func (tp *provider) TestAdd(t *testing.T) { }, opts: []options.UnixfsAddOption{options.Unixfs.Wrap(true)}, }, - { - name: "stdinNamed", - path: "/ipfs/QmQ6cGBmb3ZbdrQW1MRm1RJnYnaxCqfssz7CrTa9NEhQyS", - data: func() files.Node { - rf, err := files.NewReaderPathFile(os.Stdin.Name(), ioutil.NopCloser(strings.NewReader(helloStr)), nil) - if err != nil { - panic(err) - } - - return rf - }, - expect: func(files.Node) files.Node { - return files.NewMapDirectory(map[string]files.Node{ - "test": files.NewBytesFile([]byte(helloStr)), - }) - }, - opts: []options.UnixfsAddOption{options.Unixfs.Wrap(true), options.Unixfs.StdinName("test")}, - }, { name: "twoLevelDirWrapped", data: twoLevelDir(), From a0c8ed395649c7fcda2ceaccdd65aa13feb2d2ae Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 22 Mar 2019 15:05:41 -0700 Subject: [PATCH 2997/3817] remove Wrap This can be trivially implemented by the end-user if desired. The best the CoreAPI can do is name the file with it's own hash so this isn't really all that helpful either. Note: This differs from js-ipfs because _there_, all files have paths (even outside directories). This commit was moved from ipfs/interface-go-ipfs-core@ac37dde21aaeea010bbe50c8c37155e4471c0000 --- coreiface/options/unixfs.go | 13 ------------- coreiface/tests/unixfs.go | 36 ------------------------------------ 2 files changed, 49 deletions(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 578eb5320..3fd96f772 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -33,8 +33,6 @@ type UnixfsAddSettings struct { FsCache bool NoCopy bool - Wrap bool - Events chan<- interface{} Silent bool Progress bool @@ -65,8 +63,6 @@ func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, FsCache: false, NoCopy: false, - Wrap: false, - Events: nil, Silent: false, Progress: false, @@ -234,15 +230,6 @@ func (unixfsOpts) HashOnly(hashOnly bool) UnixfsAddOption { } } -// Wrap tells the adder to wrap the added file structure with an additional -// directory. -func (unixfsOpts) Wrap(wrap bool) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.Wrap = wrap - return nil - } -} - // Events specifies channel which will be used to report events about ongoing // Add operation. // diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index c27826b51..0fd494f66 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -248,13 +248,6 @@ func (tp *provider) TestAdd(t *testing.T) { data: flatDir, path: "/ipfs/QmRKGpFfR32FVXdvJiHfo4WJ5TDYBsM1P9raAp1p6APWSp", }, - { - name: "simpleDirWrap", - data: flatDir, - expect: wrapped("QmRKGpFfR32FVXdvJiHfo4WJ5TDYBsM1P9raAp1p6APWSp"), - path: "/ipfs/QmXxCaQkC8Z6Qws1nTkTQfCsL9y4XvWXnrPokp9bhmjC1L", - opts: []options.UnixfsAddOption{options.Unixfs.Wrap(true)}, - }, { name: "simpleDir", data: flatDir, @@ -279,35 +272,6 @@ func (tp *provider) TestAdd(t *testing.T) { wrap: "foo", expect: wrapped("foo"), }, - { - name: "stdinWrapped", - path: "/ipfs/QmU3r81oZycjHS9oaSHw37ootMFuFUw1DvMLKXPsezdtqU", - data: func() files.Node { - return files.NewBytesFile([]byte(helloStr)) - }, - expect: func(files.Node) files.Node { - return files.NewMapDirectory(map[string]files.Node{ - "QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk": files.NewBytesFile([]byte(helloStr)), - }) - }, - opts: []options.UnixfsAddOption{options.Unixfs.Wrap(true)}, - }, - { - name: "twoLevelDirWrapped", - data: twoLevelDir(), - wrap: "t", - expect: wrapped("QmPwsL3T5sWhDmmAWZHAzyjKtMVDS9a11aHNRqb3xoVnmg", "t"), - path: "/ipfs/QmXzZwAh34pmNjuKsVGZfpbByis5S5qeZjCCUxa1ajZqzH", - opts: []options.UnixfsAddOption{options.Unixfs.Wrap(true)}, - }, - { - name: "twoLevelInlineHash", - data: twoLevelDir(), - wrap: "t", - expect: wrapped("zBunoruKoyCHKkALNSWxDvj4L7yuQnMgQ4hUa9j1Z64tVcDEcu6Zdetyu7eeFCxMPfxb7YJvHeFHoFoHMkBUQf6vfdhmi", "t"), - path: "/ipfs/QmUX6GykDGHTMtLmDkfjqs48QwQK82vou51xwaY9TSU7Zo", - opts: []options.UnixfsAddOption{options.Unixfs.Wrap(true), options.Unixfs.Inline(true), options.Unixfs.RawLeaves(true), options.Unixfs.Hash(mh.SHA3)}, - }, // hidden { name: "hiddenFilesAdded", From 39e93487470af2692b0bd9c9f9b25a2de98615a5 Mon Sep 17 00:00:00 2001 From: chenminjian <727180553@qq.com> Date: Tue, 26 Mar 2019 14:18:00 +0800 Subject: [PATCH 2998/3817] fix: not remove file by mistakes This commit was moved from ipfs/go-mfs@c322bfadadf72dce54570e9f408900806f631877 --- mfs/ops.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mfs/ops.go b/mfs/ops.go index bf05cd443..90e229d4e 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -73,6 +73,10 @@ func Mv(r *Root, src, dst string) error { return err } + if srcDirObj == dstDir && srcFname == filename { + return nil + } + return srcDirObj.Unlink(srcFname) } From 1a828e67296415ef8bbeb4d9e07fc994f9633748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 26 Mar 2019 19:48:30 +0800 Subject: [PATCH 2999/3817] Update ops.go Co-Authored-By: chenminjian <727180553@qq.com> This commit was moved from ipfs/go-mfs@9fe9f96862eb074ff2ef0ebf7c63bc82a3ba2f50 --- mfs/ops.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/ops.go b/mfs/ops.go index 90e229d4e..3232f8103 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -73,7 +73,7 @@ func Mv(r *Root, src, dst string) error { return err } - if srcDirObj == dstDir && srcFname == filename { + if srcDir == dstDirStr && srcFname == filename { return nil } From 35272d3a3b89759460bf3fb96c15e36019036da4 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 26 Mar 2019 18:27:35 +0000 Subject: [PATCH 3000/3817] make unrecoverable test errors fatal Otherwise, we can get random panics form dereferencing nil pointers. This commit was moved from ipfs/interface-go-ipfs-core@6d166d40d8d347faa4a10aec30444999d5d7b85b --- coreiface/tests/block.go | 28 ++++++++++----------- coreiface/tests/dag.go | 36 +++++++++++++-------------- coreiface/tests/key.go | 26 ++++++++++---------- coreiface/tests/path.go | 18 +++++++------- coreiface/tests/pin.go | 26 ++++++++++---------- coreiface/tests/unixfs.go | 51 +++++++++++++++++++++------------------ 6 files changed, 94 insertions(+), 91 deletions(-) diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 3cd74358d..d584ac98a 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -52,7 +52,7 @@ func (tp *provider) TestBlockPutFormat(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("cbor")) @@ -70,7 +70,7 @@ func (tp *provider) TestBlockPutHash(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Hash(mh.KECCAK_512, -1)) @@ -88,7 +88,7 @@ func (tp *provider) TestBlockGet(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Hash(mh.KECCAK_512, -1)) @@ -98,12 +98,12 @@ func (tp *provider) TestBlockGet(t *testing.T) { r, err := api.Block().Get(ctx, res.Path()) if err != nil { - t.Error(err) + t.Fatal(err) } d, err := ioutil.ReadAll(r) if err != nil { - t.Error(err) + t.Fatal(err) } if string(d) != "Hello" { @@ -112,7 +112,7 @@ func (tp *provider) TestBlockGet(t *testing.T) { p, err := coreiface.ParsePath("/ipfs/" + res.Path().Cid().String()) if err != nil { - t.Error(err) + t.Fatal(err) } rp, err := api.ResolvePath(ctx, p) @@ -129,7 +129,7 @@ func (tp *provider) TestBlockRm(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } res, err := api.Block().Put(ctx, strings.NewReader(`Hello`)) @@ -139,12 +139,12 @@ func (tp *provider) TestBlockRm(t *testing.T) { r, err := api.Block().Get(ctx, res.Path()) if err != nil { - t.Error(err) + t.Fatal(err) } d, err := ioutil.ReadAll(r) if err != nil { - t.Error(err) + t.Fatal(err) } if string(d) != "Hello" { @@ -153,7 +153,7 @@ func (tp *provider) TestBlockRm(t *testing.T) { err = api.Block().Rm(ctx, res.Path()) if err != nil { - t.Error(err) + t.Fatal(err) } _, err = api.Block().Get(ctx, res.Path()) @@ -174,7 +174,7 @@ func (tp *provider) TestBlockRm(t *testing.T) { err = api.Block().Rm(ctx, res.Path(), opt.Block.Force(true)) if err != nil { - t.Error(err) + t.Fatal(err) } } @@ -183,7 +183,7 @@ func (tp *provider) TestBlockStat(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } res, err := api.Block().Put(ctx, strings.NewReader(`Hello`)) @@ -193,7 +193,7 @@ func (tp *provider) TestBlockStat(t *testing.T) { stat, err := api.Block().Stat(ctx, res.Path()) if err != nil { - t.Error(err) + t.Fatal(err) } if stat.Path().String() != res.Path().String() { @@ -210,7 +210,7 @@ func (tp *provider) TestBlockPin(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } _, err = api.Block().Put(ctx, strings.NewReader(`Hello`)) diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index 7446c20de..ff034beec 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -44,12 +44,12 @@ func (tp *provider) TestPut(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), math.MaxUint64, -1) if err != nil { - t.Error(err) + t.Fatal(err) } err = api.Dag().Add(ctx, nd) @@ -67,12 +67,12 @@ func (tp *provider) TestPutWithHash(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), mh.ID, -1) if err != nil { - t.Error(err) + t.Fatal(err) } err = api.Dag().Add(ctx, nd) @@ -90,12 +90,12 @@ func (tp *provider) TestDagPath(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } snd, err := ipldcbor.FromJSON(strings.NewReader(`"foo"`), math.MaxUint64, -1) if err != nil { - t.Error(err) + t.Fatal(err) } err = api.Dag().Add(ctx, snd) @@ -105,7 +105,7 @@ func (tp *provider) TestDagPath(t *testing.T) { nd, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+snd.Cid().String()+`"}}`), math.MaxUint64, -1) if err != nil { - t.Error(err) + t.Fatal(err) } err = api.Dag().Add(ctx, nd) @@ -115,17 +115,17 @@ func (tp *provider) TestDagPath(t *testing.T) { p, err := coreiface.ParsePath(path.Join(nd.Cid().String(), "lnk")) if err != nil { - t.Error(err) + t.Fatal(err) } rp, err := api.ResolvePath(ctx, p) if err != nil { - t.Error(err) + t.Fatal(err) } ndd, err := api.Dag().Get(ctx, rp.Cid()) if err != nil { - t.Error(err) + t.Fatal(err) } if ndd.Cid().String() != snd.Cid().String() { @@ -138,12 +138,12 @@ func (tp *provider) TestTree(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } nd, err := ipldcbor.FromJSON(strings.NewReader(`{"a": 123, "b": "foo", "c": {"d": 321, "e": 111}}`), math.MaxUint64, -1) if err != nil { - t.Error(err) + t.Fatal(err) } err = api.Dag().Add(ctx, nd) @@ -153,7 +153,7 @@ func (tp *provider) TestTree(t *testing.T) { res, err := api.Dag().Get(ctx, nd.Cid()) if err != nil { - t.Error(err) + t.Fatal(err) } lst := res.Tree("", -1) @@ -173,12 +173,12 @@ func (tp *provider) TestBatch(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), math.MaxUint64, -1) if err != nil { - t.Error(err) + t.Fatal(err) } if nd.Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" { @@ -187,15 +187,15 @@ func (tp *provider) TestBatch(t *testing.T) { _, err = api.Dag().Get(ctx, nd.Cid()) if err == nil || !strings.Contains(err.Error(), "not found") { - t.Error(err) + t.Fatal(err) } if err := api.Dag().AddMany(ctx, []ipld.Node{nd}); err != nil { - t.Error(err) + t.Fatal(err) } _, err = api.Dag().Get(ctx, nd.Cid()) if err != nil { - t.Error(err) + t.Fatal(err) } } diff --git a/coreiface/tests/key.go b/coreiface/tests/key.go index dbbfce059..7ff5f3330 100644 --- a/coreiface/tests/key.go +++ b/coreiface/tests/key.go @@ -121,7 +121,7 @@ func (tp *provider) TestGenerate(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } k, err := api.Key().Generate(ctx, "foo") @@ -144,7 +144,7 @@ func (tp *provider) TestGenerateSize(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } k, err := api.Key().Generate(ctx, "foo", opt.Key.Size(1024)) @@ -169,7 +169,7 @@ func (tp *provider) TestGenerateType(t *testing.T) { api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } k, err := api.Key().Generate(ctx, "bar", opt.Key.Type(opt.Ed25519Key)) @@ -193,7 +193,7 @@ func (tp *provider) TestGenerateExisting(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } _, err = api.Key().Generate(ctx, "foo") @@ -226,7 +226,7 @@ func (tp *provider) TestList(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } _, err = api.Key().Generate(ctx, "foo") @@ -272,7 +272,7 @@ func (tp *provider) TestRename(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } _, err = api.Key().Generate(ctx, "foo") @@ -301,7 +301,7 @@ func (tp *provider) TestRenameToSelf(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } _, err = api.Key().Generate(ctx, "foo") @@ -325,7 +325,7 @@ func (tp *provider) TestRenameToSelfForce(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } _, err = api.Key().Generate(ctx, "foo") @@ -349,7 +349,7 @@ func (tp *provider) TestRenameOverwriteNoForce(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } _, err = api.Key().Generate(ctx, "foo") @@ -379,7 +379,7 @@ func (tp *provider) TestRenameOverwrite(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } kfoo, err := api.Key().Generate(ctx, "foo") @@ -418,7 +418,7 @@ func (tp *provider) TestRenameSameNameNoForce(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } _, err = api.Key().Generate(ctx, "foo") @@ -447,7 +447,7 @@ func (tp *provider) TestRenameSameName(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } _, err = api.Key().Generate(ctx, "foo") @@ -476,7 +476,7 @@ func (tp *provider) TestRemove(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } k, err := api.Key().Generate(ctx, "foo") diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index 4da1a5181..b99e8ab9c 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -68,7 +68,7 @@ func (tp *provider) TestPathRemainder(t *testing.T) { nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) if err != nil { - t.Error(err) + t.Fatal(err) } if err := api.Dag().Add(ctx, nd); err != nil { @@ -77,7 +77,7 @@ func (tp *provider) TestPathRemainder(t *testing.T) { p1, err := coreiface.ParsePath(nd.String() + "/foo/bar") if err != nil { - t.Error(err) + t.Fatal(err) } rp1, err := api.ResolvePath(ctx, p1) @@ -104,7 +104,7 @@ func (tp *provider) TestEmptyPathRemainder(t *testing.T) { nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) if err != nil { - t.Error(err) + t.Fatal(err) } if err := api.Dag().Add(ctx, nd); err != nil { @@ -113,7 +113,7 @@ func (tp *provider) TestEmptyPathRemainder(t *testing.T) { p1, err := coreiface.ParsePath(nd.Cid().String()) if err != nil { - t.Error(err) + t.Fatal(err) } rp1, err := api.ResolvePath(ctx, p1) @@ -140,7 +140,7 @@ func (tp *provider) TestInvalidPathRemainder(t *testing.T) { nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) if err != nil { - t.Error(err) + t.Fatal(err) } if err := api.Dag().Add(ctx, nd); err != nil { @@ -149,7 +149,7 @@ func (tp *provider) TestInvalidPathRemainder(t *testing.T) { p1, err := coreiface.ParsePath("/ipld/" + nd.Cid().String() + "/bar/baz") if err != nil { - t.Error(err) + t.Fatal(err) } _, err = api.ResolvePath(ctx, p1) @@ -181,7 +181,7 @@ func (tp *provider) TestPathRoot(t *testing.T) { nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"/": "`+blk.Path().Cid().String()+`"}}`), math.MaxUint64, -1) if err != nil { - t.Error(err) + t.Fatal(err) } if err := api.Dag().Add(ctx, nd); err != nil { @@ -190,7 +190,7 @@ func (tp *provider) TestPathRoot(t *testing.T) { p1, err := coreiface.ParsePath("/ipld/" + nd.Cid().String() + "/foo") if err != nil { - t.Error(err) + t.Fatal(err) } rp, err := api.ResolvePath(ctx, p1) @@ -210,7 +210,7 @@ func (tp *provider) TestPathRoot(t *testing.T) { func (tp *provider) TestPathJoin(t *testing.T) { p1, err := coreiface.ParsePath("/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz") if err != nil { - t.Error(err) + t.Fatal(err) } if coreiface.Join(p1, "foo").String() != "/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz/foo" { diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index eed542283..ff6f98e35 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -31,17 +31,17 @@ func (tp *provider) TestPinAdd(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } p, err := api.Unixfs().Add(ctx, strFile("foo")()) if err != nil { - t.Error(err) + t.Fatal(err) } err = api.Pin().Add(ctx, p) if err != nil { - t.Error(err) + t.Fatal(err) } } @@ -50,17 +50,17 @@ func (tp *provider) TestPinSimple(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } p, err := api.Unixfs().Add(ctx, strFile("foo")()) if err != nil { - t.Error(err) + t.Fatal(err) } err = api.Pin().Add(ctx, p) if err != nil { - t.Error(err) + t.Fatal(err) } list, err := api.Pin().Ls(ctx) @@ -100,27 +100,27 @@ func (tp *provider) TestPinRecursive(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } p0, err := api.Unixfs().Add(ctx, strFile("foo")()) if err != nil { - t.Error(err) + t.Fatal(err) } p1, err := api.Unixfs().Add(ctx, strFile("bar")()) if err != nil { - t.Error(err) + t.Fatal(err) } nd2, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+p0.Cid().String()+`"}}`), math.MaxUint64, -1) if err != nil { - t.Error(err) + t.Fatal(err) } nd3, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+p1.Cid().String()+`"}}`), math.MaxUint64, -1) if err != nil { - t.Error(err) + t.Fatal(err) } if err := api.Dag().AddMany(ctx, []ipld.Node{nd2, nd3}); err != nil { @@ -129,12 +129,12 @@ func (tp *provider) TestPinRecursive(t *testing.T) { err = api.Pin().Add(ctx, iface.IpldPath(nd2.Cid())) if err != nil { - t.Error(err) + t.Fatal(err) } err = api.Pin().Add(ctx, iface.IpldPath(nd3.Cid()), opt.Pin.Recursive(false)) if err != nil { - t.Error(err) + t.Fatal(err) } list, err := api.Pin().Ls(ctx) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index c27826b51..e99bf4429 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -98,7 +98,7 @@ func (tp *provider) TestAdd(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } p := func(h string) coreiface.ResolvedPath { @@ -566,15 +566,18 @@ func (tp *provider) TestAddPinned(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } _, err = api.Unixfs().Add(ctx, strFile(helloStr)(), options.Unixfs.Pin(true)) if err != nil { - t.Error(err) + t.Fatal(err) } pins, err := api.Pin().Ls(ctx) + if err != nil { + t.Fatal(err) + } if len(pins) != 1 { t.Fatalf("expected 1 pin, got %d", len(pins)) } @@ -589,12 +592,12 @@ func (tp *provider) TestAddHashOnly(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } p, err := api.Unixfs().Add(ctx, strFile(helloStr)(), options.Unixfs.HashOnly(true)) if err != nil { - t.Error(err) + t.Fatal(err) } if p.String() != hello { @@ -648,18 +651,18 @@ func (tp *provider) TestGetDir(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } edir := unixfs.EmptyDirNode() err = api.Dag().Add(ctx, edir) if err != nil { - t.Error(err) + t.Fatal(err) } p := coreiface.IpfsPath(edir.Cid()) emptyDir, err := api.Object().New(ctx, options.Object.Type("unixfs-dir")) if err != nil { - t.Error(err) + t.Fatal(err) } if p.String() != coreiface.IpfsPath(emptyDir.Cid()).String() { @@ -668,7 +671,7 @@ func (tp *provider) TestGetDir(t *testing.T) { r, err := api.Unixfs().Get(ctx, coreiface.IpfsPath(emptyDir.Cid())) if err != nil { - t.Error(err) + t.Fatal(err) } if _, ok := r.(files.Directory); !ok { @@ -681,13 +684,13 @@ func (tp *provider) TestGetNonUnixfs(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } nd := new(mdag.ProtoNode) err = api.Dag().Add(ctx, nd) if err != nil { - t.Error(err) + t.Fatal(err) } _, err = api.Unixfs().Get(ctx, coreiface.IpfsPath(nd.Cid())) @@ -761,7 +764,7 @@ func (tp *provider) TestEntriesExpired(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } r := strings.NewReader("content-of-file") @@ -769,14 +772,14 @@ func (tp *provider) TestEntriesExpired(t *testing.T) { "name-of-file": files.NewReaderFile(r), })) if err != nil { - t.Error(err) + t.Fatal(err) } ctx, cancel = context.WithCancel(ctx) nd, err := api.Unixfs().Get(ctx, p) if err != nil { - t.Error(err) + t.Fatal(err) } cancel() @@ -803,22 +806,22 @@ func (tp *provider) TestLsEmptyDir(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } _, err = api.Unixfs().Add(ctx, files.NewSliceDirectory([]files.DirEntry{})) if err != nil { - t.Error(err) + t.Fatal(err) } emptyDir, err := api.Object().New(ctx, options.Object.Type("unixfs-dir")) if err != nil { - t.Error(err) + t.Fatal(err) } links, err := api.Unixfs().Ls(ctx, coreiface.IpfsPath(emptyDir.Cid())) if err != nil { - t.Error(err) + t.Fatal(err) } if len(links) != 0 { @@ -832,7 +835,7 @@ func (tp *provider) TestLsNonUnixfs(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } nd, err := cbor.WrapObject(map[string]interface{}{"foo": "bar"}, math.MaxUint64, -1) @@ -842,12 +845,12 @@ func (tp *provider) TestLsNonUnixfs(t *testing.T) { err = api.Dag().Add(ctx, nd) if err != nil { - t.Error(err) + t.Fatal(err) } links, err := api.Unixfs().Ls(ctx, coreiface.IpfsPath(nd.Cid())) if err != nil { - t.Error(err) + t.Fatal(err) } if len(links) != 0 { @@ -890,7 +893,7 @@ func (tp *provider) TestAddCloses(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } n4 := &closeTestF{files.NewBytesFile([]byte("foo")), false, t} @@ -907,7 +910,7 @@ func (tp *provider) TestAddCloses(t *testing.T) { _, err = api.Unixfs().Add(ctx, d0) if err != nil { - t.Error(err) + t.Fatal(err) } d0.Close() // Adder doesn't close top-level file @@ -930,7 +933,7 @@ func (tp *provider) TestGetSeek(t *testing.T) { defer cancel() api, err := tp.makeAPI(ctx) if err != nil { - t.Error(err) + t.Fatal(err) } dataSize := int64(100000) From ccb4a5c183b98b9dcf40eee0c0ac8e4a317b4ed8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 26 Mar 2019 18:35:13 +0000 Subject: [PATCH 3001/3817] tests: remove t.Fatal from goroutines This commit was moved from ipfs/interface-go-ipfs-core@5f17f8346b441a6105b569084fa020af989b0f4c --- coreiface/tests/pubsub.go | 4 +++- coreiface/tests/unixfs.go | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/coreiface/tests/pubsub.go b/coreiface/tests/pubsub.go index bb870de6c..dd05b73cf 100644 --- a/coreiface/tests/pubsub.go +++ b/coreiface/tests/pubsub.go @@ -40,7 +40,9 @@ func (tp *provider) TestBasicPubSub(t *testing.T) { for { err := apis[1].PubSub().Publish(ctx, "testch", []byte("hello world")) if err != nil { - t.Fatal(err) + t.Error(err) + cancel() + return } select { case <-tick: diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index e99bf4429..576160500 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -423,11 +423,13 @@ func (tp *provider) TestAdd(t *testing.T) { for evt := range eventOut { event, ok := evt.(*coreiface.AddEvent) if !ok { - t.Fatal("unexpected event type") + t.Error("unexpected event type") + continue } if len(expected) < 1 { - t.Fatal("got more events than expected") + t.Error("got more events than expected") + continue } if expected[0].Size != event.Size { @@ -453,7 +455,7 @@ func (tp *provider) TestAdd(t *testing.T) { } if len(expected) > 0 { - t.Fatalf("%d event(s) didn't arrive", len(expected)) + t.Errorf("%d event(s) didn't arrive", len(expected)) } }() } From b75b1243fb9738e1b5eb6fd19bca980bd156299c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 26 Mar 2019 18:37:40 +0000 Subject: [PATCH 3002/3817] tests: remove ticker leak This commit was moved from ipfs/interface-go-ipfs-core@a7d4a7199895a4bd66fa655b73f94ecea4540fdf --- coreiface/tests/pubsub.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/coreiface/tests/pubsub.go b/coreiface/tests/pubsub.go index dd05b73cf..418fc4867 100644 --- a/coreiface/tests/pubsub.go +++ b/coreiface/tests/pubsub.go @@ -35,7 +35,8 @@ func (tp *provider) TestBasicPubSub(t *testing.T) { } go func() { - tick := time.Tick(100 * time.Millisecond) + ticker := time.NewTicker(100 * time.Millisecond) + defer ticker.Stop() for { err := apis[1].PubSub().Publish(ctx, "testch", []byte("hello world")) @@ -45,7 +46,7 @@ func (tp *provider) TestBasicPubSub(t *testing.T) { return } select { - case <-tick: + case <-ticker.C: case <-ctx.Done(): return } From bbf450e3e44f7595a128eba9b7578a412ddcd551 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 26 Mar 2019 18:38:02 +0000 Subject: [PATCH 3003/3817] tests: fix unused variable lints This commit was moved from ipfs/interface-go-ipfs-core@5d6a474f3191362120268fa1b0396823013fbe41 --- coreiface/tests/object.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/coreiface/tests/object.go b/coreiface/tests/object.go index 026def73b..8682a2edc 100644 --- a/coreiface/tests/object.go +++ b/coreiface/tests/object.go @@ -296,7 +296,7 @@ func (tp *provider) TestObjectAddLinkCreate(t *testing.T) { t.Fatal(err) } - p3, err := api.Object().AddLink(ctx, p2, "abc/d", p2) + _, err = api.Object().AddLink(ctx, p2, "abc/d", p2) if err == nil { t.Fatal("expected an error") } @@ -304,7 +304,7 @@ func (tp *provider) TestObjectAddLinkCreate(t *testing.T) { t.Fatalf("unexpected error: %s", err.Error()) } - p3, err = api.Object().AddLink(ctx, p2, "abc/d", p2, opt.Object.Create(true)) + p3, err := api.Object().AddLink(ctx, p2, "abc/d", p2, opt.Object.Create(true)) if err != nil { t.Fatal(err) } @@ -384,6 +384,9 @@ func (tp *provider) TestObjectAddData(t *testing.T) { } data, err := ioutil.ReadAll(r) + if err != nil { + t.Fatal(err) + } if string(data) != "foobar" { t.Error("unexpected data") @@ -414,6 +417,9 @@ func (tp *provider) TestObjectSetData(t *testing.T) { } data, err := ioutil.ReadAll(r) + if err != nil { + t.Fatal(err) + } if string(data) != "bar" { t.Error("unexpected data") From 21ade61b10b9757f9dfd2dafdb3c95a0ec00ccb1 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 26 Mar 2019 19:01:23 +0000 Subject: [PATCH 3004/3817] don't close the top-level addr See https://github.com/ipfs/go-ipfs-http-client/pull/10/files#r269268326 This commit was moved from ipfs/interface-go-ipfs-core@1b707f294336a6eaf3274e27ab0ce85a2b374fbe --- coreiface/tests/unixfs.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 0fd494f66..ea36b7330 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -834,6 +834,7 @@ type closeTestD struct { } func (f *closeTestD) Close() error { + f.t.Helper() if f.closed { f.t.Fatal("already closed") } @@ -874,8 +875,6 @@ func (tp *provider) TestAddCloses(t *testing.T) { t.Error(err) } - d0.Close() // Adder doesn't close top-level file - for i, n := range []*closeTestF{n1, n2, n4} { if !n.closed { t.Errorf("file %d not closed!", i) From e6ed297b25353687011dacb6bcfec85a297719d8 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Mon, 25 Mar 2019 09:03:09 -0700 Subject: [PATCH 3005/3817] Query for provider head/tail License: MIT Signed-off-by: Michael Avila This commit was moved from ipfs/go-ipfs-provider@cb07b21213d8bca40ed95354cd3b356bf239a1ee --- provider/queue.go | 66 +++++++++++++++++++++++++----------------- provider/queue_test.go | 26 ++++++++++++++++- 2 files changed, 64 insertions(+), 28 deletions(-) diff --git a/provider/queue.go b/provider/queue.go index a3268e109..918bdcbd7 100644 --- a/provider/queue.go +++ b/provider/queue.go @@ -2,7 +2,7 @@ package provider import ( "context" - "math" + "fmt" "strconv" "strings" @@ -32,7 +32,7 @@ type Queue struct { // NewQueue creates a queue for cids func NewQueue(ctx context.Context, name string, ds datastore.Datastore) (*Queue, error) { namespaced := namespace.Wrap(ds, datastore.NewKey("/"+name+"/queue/")) - head, tail, err := getQueueHeadTail(ctx, name, namespaced) + head, tail, err := getQueueHeadTail(ctx, namespaced) if err != nil { return nil, err } @@ -142,40 +142,52 @@ func (q *Queue) work() { } func (q *Queue) queueKey(id uint64) datastore.Key { - return datastore.NewKey(strconv.FormatUint(id, 10)) + s := fmt.Sprintf("%016X", id) + return datastore.NewKey(s) } -// crawl over the queue entries to find the head and tail -func getQueueHeadTail(ctx context.Context, name string, datastore datastore.Datastore) (uint64, uint64, error) { - q := query.Query{} - results, err := datastore.Query(q) +func getQueueHeadTail(ctx context.Context, datastore datastore.Datastore) (uint64, uint64, error) { + head, err := getQueueHead(datastore) if err != nil { return 0, 0, err } + tail, err := getQueueTail(datastore) + if err != nil { + return 0, 0, err + } + return head, tail, nil +} - var tail uint64 - var head uint64 = math.MaxUint64 - for entry := range results.Next() { - trimmed := strings.TrimPrefix(entry.Key, "/") - id, err := strconv.ParseUint(trimmed, 10, 64) - if err != nil { - return 0, 0, err - } +func getQueueHead(ds datastore.Datastore) (uint64, error) { + return getFirstIDByOrder(ds, query.OrderByKey{}) +} - if id < head { - head = id - } +func getQueueTail(ds datastore.Datastore) (uint64, error) { + tail, err := getFirstIDByOrder(ds, query.OrderByKeyDescending{}) + if err != nil { + return 0, err + } + if tail > 0 { + tail++ + } + return tail, nil +} - if (id + 1) > tail { - tail = (id + 1) - } +func getFirstIDByOrder(ds datastore.Datastore, order query.Order) (uint64, error) { + q := query.Query{Orders: []query.Order{order}} + results, err := ds.Query(q) + if err != nil { + return 0, err } - if err := results.Close(); err != nil { - return 0, 0, err + defer results.Close() + r, ok := results.NextSync() + if !ok { + return 0, nil } - if head == math.MaxUint64 { - head = 0 + trimmed := strings.TrimPrefix(r.Key, "/") + id, err := strconv.ParseUint(trimmed, 16, 64) + if err != nil { + return 0, err } - - return head, tail, nil + return id, nil } diff --git a/provider/queue_test.go b/provider/queue_test.go index e1b74878e..2857da0a9 100644 --- a/provider/queue_test.go +++ b/provider/queue_test.go @@ -12,7 +12,7 @@ import ( func makeCids(n int) []cid.Cid { cids := make([]cid.Cid, 0, n) - for i := 0; i < 10; i++ { + for i := 0; i < n; i++ { c := blockGenerator.Next().Cid() cids = append(cids, c) } @@ -129,3 +129,27 @@ func TestInitialization(t *testing.T) { assertOrdered(cids[5:], queue, t) } + +func TestInitializationWithManyCids(t *testing.T) { + ctx := context.Background() + defer ctx.Done() + + ds := sync.MutexWrap(datastore.NewMapDatastore()) + queue, err := NewQueue(ctx, "test", ds) + if err != nil { + t.Fatal(err) + } + + cids := makeCids(25) + for _, c := range cids { + queue.Enqueue(c) + } + + // make a new queue, same data + queue, err = NewQueue(ctx, "test", ds) + if err != nil { + t.Fatal(err) + } + + assertOrdered(cids, queue, t) +} From f440f3ba86bbc5fe4bb7db919f306e6f0949f46c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 27 Mar 2019 14:46:29 +0000 Subject: [PATCH 3006/3817] chore: fix a bunch of issues caught by golangci-lint Most of these are probably harmless but a few looked like they might actually be bugs. Most of them are just faulty tests. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@c2d9637ee6d2005d7a3ca04318e48c06598b8578 --- namesys/namesys.go | 2 +- namesys/namesys_test.go | 5 ++++- namesys/republisher/repub_test.go | 4 +++- namesys/routing.go | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 94d498992..f8b8c6d12 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -183,7 +183,7 @@ func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path. return err } ttl := DefaultResolverCacheTTL - if ttEol := eol.Sub(time.Now()); ttEol < ttl { + if ttEol := time.Until(eol); ttEol < ttl { ttl = ttEol } ns.cacheSet(peer.IDB58Encode(id), value, ttl) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 09c5a39c2..38d6c4abb 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -104,5 +104,8 @@ func TestPublishWithCache0(t *testing.T) { if err != nil { t.Fatal(err) } - nsys.Publish(context.Background(), priv, p) + err = nsys.Publish(context.Background(), priv, p) + if err != nil { + t.Fatal(err) + } } diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 22d69e254..8f0048c4c 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -41,7 +41,9 @@ func TestRepublish(t *testing.T) { nodes = append(nodes, nd) } - mn.LinkAll() + if err := mn.LinkAll(); err != nil { + t.Fatal(err) + } bsinf := core.BootstrapConfigWithPeers( []pstore.PeerInfo{ diff --git a/namesys/routing.go b/namesys/routing.go index d58133775..e89dd9c9d 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -137,7 +137,7 @@ func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, option case ipns.ErrUnrecognizedValidity: // No EOL. case nil: - ttEol := eol.Sub(time.Now()) + ttEol := time.Until(eol) if ttEol < 0 { // It *was* valid when we first resolved it. ttl = 0 From 62dc10386ca75de1c48ce267d08080e7519d0ef2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 27 Mar 2019 14:46:29 +0000 Subject: [PATCH 3007/3817] chore: fix a bunch of issues caught by golangci-lint Most of these are probably harmless but a few looked like they might actually be bugs. Most of them are just faulty tests. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-provider@13f94287dae70589f680c90275a790a3d74c5b17 --- provider/queue.go | 8 +++----- provider/queue_test.go | 3 +++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/provider/queue.go b/provider/queue.go index 918bdcbd7..c8982ff10 100644 --- a/provider/queue.go +++ b/provider/queue.go @@ -62,11 +62,6 @@ func (q *Queue) Dequeue() <-chan cid.Cid { return q.dequeue } -type entry struct { - cid cid.Cid - key datastore.Key -} - // Look for next Cid in the queue and return it. Skip over gaps and mangled data func (q *Queue) nextEntry() (datastore.Key, cid.Cid) { for { @@ -91,6 +86,9 @@ func (q *Queue) nextEntry() (datastore.Key, cid.Cid) { log.Warningf("Error marshalling Cid from queue: ", err) q.head++ err = q.ds.Delete(key) + if err != nil { + log.Warningf("Provider queue failed to delete: %s", key) + } continue } diff --git a/provider/queue_test.go b/provider/queue_test.go index 2857da0a9..e151478d9 100644 --- a/provider/queue_test.go +++ b/provider/queue_test.go @@ -99,6 +99,9 @@ func TestMangledData(t *testing.T) { // remove entries in the middle err = queue.ds.Put(queue.queueKey(5), []byte("borked")) + if err != nil { + t.Fatal(err) + } expected := append(cids[:5], cids[6:]...) assertOrdered(expected, queue, t) From 641cd1d4ca96323a69aeebeadb9038b12aa207f3 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 27 Mar 2019 14:46:29 +0000 Subject: [PATCH 3008/3817] chore: fix a bunch of issues caught by golangci-lint Most of these are probably harmless but a few looked like they might actually be bugs. Most of them are just faulty tests. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@1aabde8dd166fd4b0078008993fba6fb972d76ac --- pinning/pinner/pin_test.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 6f9914ef3..27e4c71de 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -2,6 +2,7 @@ package pin import ( "context" + "io" "testing" "time" @@ -21,7 +22,10 @@ var rand = util.NewTimeSeededRand() func randNode() (*mdag.ProtoNode, cid.Cid) { nd := new(mdag.ProtoNode) nd.SetData(make([]byte, 32)) - rand.Read(nd.Data()) + _, err := io.ReadFull(rand, nd.Data()) + if err != nil { + panic(err) + } k := nd.Cid() return nd, k } @@ -111,11 +115,11 @@ func TestPinnerBasic(t *testing.T) { assertPinned(t, p, bk, "Recursively pinned node not found..") d, _ := randNode() - d.AddNodeLink("a", a) - d.AddNodeLink("c", c) + _ = d.AddNodeLink("a", a) + _ = d.AddNodeLink("c", c) e, _ := randNode() - d.AddNodeLink("e", e) + _ = d.AddNodeLink("e", e) // Must be in dagserv for unpin to work err = dserv.Add(ctx, e) @@ -385,8 +389,12 @@ func TestPinUpdate(t *testing.T) { n1, c1 := randNode() n2, c2 := randNode() - dserv.Add(ctx, n1) - dserv.Add(ctx, n2) + if err := dserv.Add(ctx, n1); err != nil { + t.Fatal(err) + } + if err := dserv.Add(ctx, n2); err != nil { + t.Fatal(err) + } if err := p.Pin(ctx, n1, true); err != nil { t.Fatal(err) From c3616aece419def1698ba872af8fba464da26cd2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 27 Mar 2019 14:46:29 +0000 Subject: [PATCH 3009/3817] chore: fix a bunch of issues caught by golangci-lint Most of these are probably harmless but a few looked like they might actually be bugs. Most of them are just faulty tests. License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-keystore@104cf5bfc98d7b419bf26f73c36e53b176d055b8 --- keystore/keystore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index c69fd6a05..d7118d756 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -198,7 +198,7 @@ func TestInvalidKeyFiles(t *testing.T) { t.Fatal(err) } - if exist, err = ks.Has(".invalid"); err == nil { + if _, err = ks.Has(".invalid"); err == nil { t.Fatal("shouldnt be able to put a key with a 'hidden' name") } } From b04e8b8ac05474b627488dc4e4c390afd5959fd7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 27 Mar 2019 15:26:31 +0000 Subject: [PATCH 3010/3817] test: fix namesys test License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-namesys@8fe329d6f9979f3b14034799e020449c09ac7d25 --- namesys/namesys_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 38d6c4abb..2cf316cf3 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -15,6 +15,7 @@ import ( ci "github.com/libp2p/go-libp2p-crypto" peer "github.com/libp2p/go-libp2p-peer" pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem" + record "github.com/libp2p/go-libp2p-record" ) type mockResolver struct { @@ -97,7 +98,10 @@ func TestPublishWithCache0(t *testing.T) { t.Fatal(err) } - routing := offroute.NewOfflineRouter(dst, ipns.Validator{KeyBook: ps}) + routing := offroute.NewOfflineRouter(dst, record.NamespacedValidator{ + "ipns": ipns.Validator{KeyBook: ps}, + "pk": record.PublicKeyValidator{}, + }) nsys := NewNameSystem(routing, dst, 0) p, err := path.ParsePath(unixfs.EmptyDirNode().Cid().String()) From ea26ae5e0535dc2429c8c2797b79982317cc9bb3 Mon Sep 17 00:00:00 2001 From: Edgar Lee Date: Fri, 29 Mar 2019 16:16:16 -0700 Subject: [PATCH 3011/3817] Update Pin.RmRecursive docs to clarify shared indirect pins are not removed This commit was moved from ipfs/interface-go-ipfs-core@c908a059feab33b74ce66dca01fd521389372942 --- coreiface/options/pin.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/coreiface/options/pin.go b/coreiface/options/pin.go index cc4a8ef29..6b211bb73 100644 --- a/coreiface/options/pin.go +++ b/coreiface/options/pin.go @@ -127,7 +127,9 @@ func (pinOpts) Recursive(recursive bool) PinAddOption { } } -// RmRecursive is an option for Pin.Rm +// RmRecursive is an option for Pin.Rm which specifies whether to recursively +// unpin the object linked to by the specified object(s). This does not remove +// indirect pins referenced by other recursive pins. func (pinOpts) RmRecursive(recursive bool) PinRmOption { return func(settings *PinRmSettings) error { settings.Recursive = recursive From 31071e1f5e59a62d53e2c2c7e259eedff6d49f6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 25 Mar 2019 17:03:44 +0100 Subject: [PATCH 3012/3817] path: drop error from ParsePath This commit was moved from ipfs/interface-go-ipfs-core@2b9bff7523c812447641aa70c39ec0b096f5b5c4 --- coreiface/path.go | 51 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/coreiface/path.go b/coreiface/path.go index 4e86172ac..ede190df7 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,7 +1,9 @@ package iface import ( - "github.com/ipfs/go-cid" + "strings" + + cid "github.com/ipfs/go-cid" ipfspath "github.com/ipfs/go-path" ) @@ -23,6 +25,9 @@ type Path interface { // Namespace returns the first component of the path. // // For example path "/ipfs/QmHash", calling Namespace() will return "ipfs" + // + // Calling this method on invalid paths (IsValid() != nil) will result in + // empty string Namespace() string // Mutable returns false if the data pointed to by this path in guaranteed @@ -30,9 +35,14 @@ type Path interface { // // Note that resolved mutable path can be immutable. Mutable() bool + + // IsValid checks if this path is a valid ipfs Path, returning nil iff it is + // valid + IsValid() error } -// ResolvedPath is a path which was resolved to the last resolvable node +// ResolvedPath is a path which was resolved to the last resolvable node. +// ResolvedPaths are guaranteed to return nil from `IsValid` type ResolvedPath interface { // Cid returns the CID of the node referenced by the path. Remainder of the // path is guaranteed to be within the node. @@ -94,7 +104,7 @@ type ResolvedPath interface { // path implements coreiface.Path type path struct { - path ipfspath.Path + path string } // resolvedPath implements coreiface.resolvedPath @@ -107,14 +117,14 @@ type resolvedPath struct { // Join appends provided segments to the base path func Join(base Path, a ...string) Path { - s := ipfspath.Join(append([]string{base.String()}, a...)) - return &path{path: ipfspath.FromString(s)} + s := strings.Join(append([]string{base.String()}, a...), "/") + return &path{path: s} } // IpfsPath creates new /ipfs path from the provided CID func IpfsPath(c cid.Cid) ResolvedPath { return &resolvedPath{ - path: path{ipfspath.Path("/ipfs/" + c.String())}, + path: path{"/ipfs/" + c.String()}, cid: c, root: c, remainder: "", @@ -124,7 +134,7 @@ func IpfsPath(c cid.Cid) ResolvedPath { // IpldPath creates new /ipld path from the provided CID func IpldPath(c cid.Cid) ResolvedPath { return &resolvedPath{ - path: path{ipfspath.Path("/ipld/" + c.String())}, + path: path{"/ipld/" + c.String()}, cid: c, root: c, remainder: "", @@ -132,13 +142,12 @@ func IpldPath(c cid.Cid) ResolvedPath { } // ParsePath parses string path to a Path -func ParsePath(p string) (Path, error) { - pp, err := ipfspath.ParsePath(p) - if err != nil { - return nil, err +func ParsePath(p string) Path { + if pp, err := ipfspath.ParsePath(p); err == nil { + p = pp.String() } - return &path{path: pp}, nil + return &path{path: p} } // NewResolvedPath creates new ResolvedPath. This function performs no checks @@ -146,7 +155,7 @@ func ParsePath(p string) (Path, error) { // cause panics. Handle with care. func NewResolvedPath(ipath ipfspath.Path, c cid.Cid, root cid.Cid, remainder string) ResolvedPath { return &resolvedPath{ - path: path{ipath}, + path: path{ipath.String()}, cid: c, root: root, remainder: remainder, @@ -154,14 +163,19 @@ func NewResolvedPath(ipath ipfspath.Path, c cid.Cid, root cid.Cid, remainder str } func (p *path) String() string { - return p.path.String() + return p.path } func (p *path) Namespace() string { - if len(p.path.Segments()) < 1 { + ip, err := ipfspath.ParsePath(p.path) + if err != nil { + return "" + } + + if len(ip.Segments()) < 1 { panic("path without namespace") //this shouldn't happen under any scenario } - return p.path.Segments()[0] + return ip.Segments()[0] } func (p *path) Mutable() bool { @@ -169,6 +183,11 @@ func (p *path) Mutable() bool { return p.Namespace() == "ipns" } +func (p *path) IsValid() error { + _, err := ipfspath.ParsePath(p.path) + return err +} + func (p *resolvedPath) Cid() cid.Cid { return p.cid } From 1497150b1f90816ac7b117fbe0b2fb6aacaa7968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 25 Mar 2019 17:03:53 +0100 Subject: [PATCH 3013/3817] path: fix tests This commit was moved from ipfs/interface-go-ipfs-core@33d445a6140b26da90a07d2bf86c8827d74284b6 --- coreiface/tests/block.go | 5 +---- coreiface/tests/dag.go | 5 +---- coreiface/tests/name.go | 6 +----- coreiface/tests/path.go | 33 +++++---------------------------- coreiface/tests/unixfs.go | 5 +---- 5 files changed, 9 insertions(+), 45 deletions(-) diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index d584ac98a..96319b488 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -110,10 +110,7 @@ func (tp *provider) TestBlockGet(t *testing.T) { t.Error("didn't get correct data back") } - p, err := coreiface.ParsePath("/ipfs/" + res.Path().Cid().String()) - if err != nil { - t.Fatal(err) - } + p := coreiface.ParsePath("/ipfs/" + res.Path().Cid().String()) rp, err := api.ResolvePath(ctx, p) if err != nil { diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index ff034beec..a17296d1d 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -113,10 +113,7 @@ func (tp *provider) TestDagPath(t *testing.T) { t.Fatal(err) } - p, err := coreiface.ParsePath(path.Join(nd.Cid().String(), "lnk")) - if err != nil { - t.Fatal(err) - } + p := coreiface.ParsePath(path.Join(nd.Cid().String(), "lnk")) rp, err := api.ResolvePath(ctx, p) if err != nil { diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index 1eb2dd513..c9e99a584 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -35,11 +35,7 @@ func addTestObject(ctx context.Context, api coreiface.CoreAPI) (coreiface.Path, } func appendPath(p coreiface.Path, sub string) coreiface.Path { - p, err := coreiface.ParsePath(path.Join(p.String(), sub)) - if err != nil { - panic(err) - } - return p + return coreiface.ParsePath(path.Join(p.String(), sub)) } func (tp *provider) TestPublishResolve(t *testing.T) { diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index b99e8ab9c..f5b0ee348 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -75,12 +75,7 @@ func (tp *provider) TestPathRemainder(t *testing.T) { t.Fatal(err) } - p1, err := coreiface.ParsePath(nd.String() + "/foo/bar") - if err != nil { - t.Fatal(err) - } - - rp1, err := api.ResolvePath(ctx, p1) + rp1, err := api.ResolvePath(ctx, coreiface.ParsePath(nd.String()+"/foo/bar")) if err != nil { t.Fatal(err) } @@ -111,12 +106,7 @@ func (tp *provider) TestEmptyPathRemainder(t *testing.T) { t.Fatal(err) } - p1, err := coreiface.ParsePath(nd.Cid().String()) - if err != nil { - t.Fatal(err) - } - - rp1, err := api.ResolvePath(ctx, p1) + rp1, err := api.ResolvePath(ctx, coreiface.ParsePath(nd.Cid().String())) if err != nil { t.Fatal(err) } @@ -147,12 +137,7 @@ func (tp *provider) TestInvalidPathRemainder(t *testing.T) { t.Fatal(err) } - p1, err := coreiface.ParsePath("/ipld/" + nd.Cid().String() + "/bar/baz") - if err != nil { - t.Fatal(err) - } - - _, err = api.ResolvePath(ctx, p1) + _, err = api.ResolvePath(ctx, coreiface.ParsePath("/ipld/"+nd.Cid().String()+"/bar/baz")) if err == nil || !strings.Contains(err.Error(), "no such link found") { t.Fatalf("unexpected error: %s", err) } @@ -188,12 +173,7 @@ func (tp *provider) TestPathRoot(t *testing.T) { t.Fatal(err) } - p1, err := coreiface.ParsePath("/ipld/" + nd.Cid().String() + "/foo") - if err != nil { - t.Fatal(err) - } - - rp, err := api.ResolvePath(ctx, p1) + rp, err := api.ResolvePath(ctx, coreiface.ParsePath("/ipld/"+nd.Cid().String()+"/foo")) if err != nil { t.Fatal(err) } @@ -208,10 +188,7 @@ func (tp *provider) TestPathRoot(t *testing.T) { } func (tp *provider) TestPathJoin(t *testing.T) { - p1, err := coreiface.ParsePath("/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz") - if err != nil { - t.Fatal(err) - } + p1 := coreiface.ParsePath("/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz") if coreiface.Join(p1, "foo").String() != "/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz/foo" { t.Error("unexpected path") diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 611ea5476..15cb8abc8 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -592,10 +592,7 @@ func (tp *provider) TestGetEmptyFile(t *testing.T) { t.Fatal(err) } - emptyFilePath, err := coreiface.ParsePath(emptyFile) - if err != nil { - t.Fatal(err) - } + emptyFilePath := coreiface.ParsePath(emptyFile) r, err := api.Unixfs().Get(ctx, emptyFilePath) if err != nil { From b8463e7c123e4ff15fd8fdd33a02a6414682ca9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 25 Mar 2019 19:37:28 +0100 Subject: [PATCH 3014/3817] path: WIP This commit was moved from ipfs/interface-go-ipfs-core@5a836515396273412794edaaba72ef0cf3ead46d --- coreiface/block.go | 11 ++- coreiface/coreapi.go | 5 +- coreiface/dht.go | 7 +- coreiface/key.go | 5 +- coreiface/name.go | 11 ++- coreiface/object.go | 29 +++--- coreiface/path.go | 197 ------------------------------------- coreiface/path/path.go | 199 ++++++++++++++++++++++++++++++++++++++ coreiface/pin.go | 13 +-- coreiface/tests/block.go | 3 +- coreiface/tests/dag.go | 5 +- coreiface/tests/name.go | 12 +-- coreiface/tests/path.go | 14 +-- coreiface/tests/pin.go | 13 +-- coreiface/tests/unixfs.go | 19 ++-- coreiface/unixfs.go | 17 ++-- 16 files changed, 287 insertions(+), 273 deletions(-) create mode 100644 coreiface/path/path.go diff --git a/coreiface/block.go b/coreiface/block.go index 587ad339f..9f0ad9cbb 100644 --- a/coreiface/block.go +++ b/coreiface/block.go @@ -2,9 +2,10 @@ package iface import ( "context" + path "github.com/ipfs/interface-go-ipfs-core/path" "io" - options "github.com/ipfs/interface-go-ipfs-core/options" + "github.com/ipfs/interface-go-ipfs-core/options" ) // BlockStat contains information about a block @@ -13,7 +14,7 @@ type BlockStat interface { Size() int // Path returns path to the block - Path() ResolvedPath + Path() path.ResolvedPath } // BlockAPI specifies the interface to the block layer @@ -22,15 +23,15 @@ type BlockAPI interface { Put(context.Context, io.Reader, ...options.BlockPutOption) (BlockStat, error) // Get attempts to resolve the path and return a reader for data in the block - Get(context.Context, Path) (io.Reader, error) + Get(context.Context, path.Path) (io.Reader, error) // Rm removes the block specified by the path from local blockstore. // By default an error will be returned if the block can't be found locally. // // NOTE: If the specified block is pinned it won't be removed and no error // will be returned - Rm(context.Context, Path, ...options.BlockRmOption) error + Rm(context.Context, path.Path, ...options.BlockRmOption) error // Stat returns information on - Stat(context.Context, Path) (BlockStat, error) + Stat(context.Context, path.Path) (BlockStat, error) } diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index f3433c089..bef3ce01f 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -4,6 +4,7 @@ package iface import ( "context" + path "github.com/ipfs/interface-go-ipfs-core/path" "github.com/ipfs/interface-go-ipfs-core/options" @@ -43,11 +44,11 @@ type CoreAPI interface { PubSub() PubSubAPI // ResolvePath resolves the path using Unixfs resolver - ResolvePath(context.Context, Path) (ResolvedPath, error) + ResolvePath(context.Context, path.Path) (path.ResolvedPath, error) // ResolveNode resolves the path (if not resolved already) using Unixfs // resolver, gets and returns the resolved Node - ResolveNode(context.Context, Path) (ipld.Node, error) + ResolveNode(context.Context, path.Path) (ipld.Node, error) // WithOptions creates new instance of CoreAPI based on this instance with // a set of options applied diff --git a/coreiface/dht.go b/coreiface/dht.go index d1ae05125..0cb7893ef 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -2,10 +2,11 @@ package iface import ( "context" + path "github.com/ipfs/interface-go-ipfs-core/path" "github.com/ipfs/interface-go-ipfs-core/options" - peer "github.com/libp2p/go-libp2p-peer" + "github.com/libp2p/go-libp2p-peer" pstore "github.com/libp2p/go-libp2p-peerstore" ) @@ -19,8 +20,8 @@ type DhtAPI interface { // FindProviders finds peers in the DHT who can provide a specific value // given a key. - FindProviders(context.Context, Path, ...options.DhtFindProvidersOption) (<-chan pstore.PeerInfo, error) + FindProviders(context.Context, path.Path, ...options.DhtFindProvidersOption) (<-chan pstore.PeerInfo, error) // Provide announces to the network that you are providing given values - Provide(context.Context, Path, ...options.DhtProvideOption) error + Provide(context.Context, path.Path, ...options.DhtProvideOption) error } diff --git a/coreiface/key.go b/coreiface/key.go index 78c29d268..e7fb3f442 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -2,8 +2,9 @@ package iface import ( "context" + path "github.com/ipfs/interface-go-ipfs-core/path" - options "github.com/ipfs/interface-go-ipfs-core/options" + "github.com/ipfs/interface-go-ipfs-core/options" "github.com/libp2p/go-libp2p-peer" ) @@ -14,7 +15,7 @@ type Key interface { Name() string // Path returns key path - Path() Path + Path() path.Path // ID returns key PeerID ID() peer.ID diff --git a/coreiface/name.go b/coreiface/name.go index 51b005b7e..3dc9f6878 100644 --- a/coreiface/name.go +++ b/coreiface/name.go @@ -3,8 +3,9 @@ package iface import ( "context" "errors" + path "github.com/ipfs/interface-go-ipfs-core/path" - options "github.com/ipfs/interface-go-ipfs-core/options" + "github.com/ipfs/interface-go-ipfs-core/options" ) var ErrResolveFailed = errors.New("could not resolve name") @@ -14,11 +15,11 @@ type IpnsEntry interface { // Name returns IpnsEntry name Name() string // Value returns IpnsEntry value - Value() Path + Value() path.Path } type IpnsResult struct { - Path + path.Path Err error } @@ -32,10 +33,10 @@ type IpnsResult struct { // You can use .Key API to list and generate more names and their respective keys. type NameAPI interface { // Publish announces new IPNS name - Publish(ctx context.Context, path Path, opts ...options.NamePublishOption) (IpnsEntry, error) + Publish(ctx context.Context, path path.Path, opts ...options.NamePublishOption) (IpnsEntry, error) // Resolve attempts to resolve the newest version of the specified name - Resolve(ctx context.Context, name string, opts ...options.NameResolveOption) (Path, error) + Resolve(ctx context.Context, name string, opts ...options.NameResolveOption) (path.Path, error) // Search is a version of Resolve which outputs paths as they are discovered, // reducing the time to first entry diff --git a/coreiface/object.go b/coreiface/object.go index 4f9652fb1..3e4b7e087 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -2,11 +2,12 @@ package iface import ( "context" + path "github.com/ipfs/interface-go-ipfs-core/path" "io" - options "github.com/ipfs/interface-go-ipfs-core/options" + "github.com/ipfs/interface-go-ipfs-core/options" - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" ) @@ -58,11 +59,11 @@ type ObjectChange struct { // Before holds the link path before the change. Note that when a link is // added, this will be nil. - Before ResolvedPath + Before path.ResolvedPath // After holds the link path after the change. Note that when a link is // removed, this will be nil. - After ResolvedPath + After path.ResolvedPath } // ObjectAPI specifies the interface to MerkleDAG and contains useful utilities @@ -72,35 +73,35 @@ type ObjectAPI interface { New(context.Context, ...options.ObjectNewOption) (ipld.Node, error) // Put imports the data into merkledag - Put(context.Context, io.Reader, ...options.ObjectPutOption) (ResolvedPath, error) + Put(context.Context, io.Reader, ...options.ObjectPutOption) (path.ResolvedPath, error) // Get returns the node for the path - Get(context.Context, Path) (ipld.Node, error) + Get(context.Context, path.Path) (ipld.Node, error) // Data returns reader for data of the node - Data(context.Context, Path) (io.Reader, error) + Data(context.Context, path.Path) (io.Reader, error) // Links returns lint or links the node contains - Links(context.Context, Path) ([]*ipld.Link, error) + Links(context.Context, path.Path) ([]*ipld.Link, error) // Stat returns information about the node - Stat(context.Context, Path) (*ObjectStat, error) + Stat(context.Context, path.Path) (*ObjectStat, error) // AddLink adds a link under the specified path. child path can point to a // subdirectory within the patent which must be present (can be overridden // with WithCreate option). - AddLink(ctx context.Context, base Path, name string, child Path, opts ...options.ObjectAddLinkOption) (ResolvedPath, error) + AddLink(ctx context.Context, base path.Path, name string, child path.Path, opts ...options.ObjectAddLinkOption) (path.ResolvedPath, error) // RmLink removes a link from the node - RmLink(ctx context.Context, base Path, link string) (ResolvedPath, error) + RmLink(ctx context.Context, base path.Path, link string) (path.ResolvedPath, error) // AppendData appends data to the node - AppendData(context.Context, Path, io.Reader) (ResolvedPath, error) + AppendData(context.Context, path.Path, io.Reader) (path.ResolvedPath, error) // SetData sets the data contained in the node - SetData(context.Context, Path, io.Reader) (ResolvedPath, error) + SetData(context.Context, path.Path, io.Reader) (path.ResolvedPath, error) // Diff returns a set of changes needed to transform the first object into the // second. - Diff(context.Context, Path, Path) ([]ObjectChange, error) + Diff(context.Context, path.Path, path.Path) ([]ObjectChange, error) } diff --git a/coreiface/path.go b/coreiface/path.go index ede190df7..198651129 100644 --- a/coreiface/path.go +++ b/coreiface/path.go @@ -1,201 +1,4 @@ package iface -import ( - "strings" - - cid "github.com/ipfs/go-cid" - ipfspath "github.com/ipfs/go-path" -) - //TODO: merge with ipfspath so we don't depend on it -// Path is a generic wrapper for paths used in the API. A path can be resolved -// to a CID using one of Resolve functions in the API. -// -// Paths must be prefixed with a valid prefix: -// -// * /ipfs - Immutable unixfs path (files) -// * /ipld - Immutable ipld path (data) -// * /ipns - Mutable names. Usually resolves to one of the immutable paths -//TODO: /local (MFS) -type Path interface { - // String returns the path as a string. - String() string - - // Namespace returns the first component of the path. - // - // For example path "/ipfs/QmHash", calling Namespace() will return "ipfs" - // - // Calling this method on invalid paths (IsValid() != nil) will result in - // empty string - Namespace() string - - // Mutable returns false if the data pointed to by this path in guaranteed - // to not change. - // - // Note that resolved mutable path can be immutable. - Mutable() bool - - // IsValid checks if this path is a valid ipfs Path, returning nil iff it is - // valid - IsValid() error -} - -// ResolvedPath is a path which was resolved to the last resolvable node. -// ResolvedPaths are guaranteed to return nil from `IsValid` -type ResolvedPath interface { - // Cid returns the CID of the node referenced by the path. Remainder of the - // path is guaranteed to be within the node. - // - // Examples: - // If you have 3 linked objects: QmRoot -> A -> B: - // - // cidB := {"foo": {"bar": 42 }} - // cidA := {"B": {"/": cidB }} - // cidRoot := {"A": {"/": cidA }} - // - // And resolve paths: - // - // * "/ipfs/${cidRoot}" - // * Calling Cid() will return `cidRoot` - // * Calling Root() will return `cidRoot` - // * Calling Remainder() will return `` - // - // * "/ipfs/${cidRoot}/A" - // * Calling Cid() will return `cidA` - // * Calling Root() will return `cidRoot` - // * Calling Remainder() will return `` - // - // * "/ipfs/${cidRoot}/A/B/foo" - // * Calling Cid() will return `cidB` - // * Calling Root() will return `cidRoot` - // * Calling Remainder() will return `foo` - // - // * "/ipfs/${cidRoot}/A/B/foo/bar" - // * Calling Cid() will return `cidB` - // * Calling Root() will return `cidRoot` - // * Calling Remainder() will return `foo/bar` - Cid() cid.Cid - - // Root returns the CID of the root object of the path - // - // Example: - // If you have 3 linked objects: QmRoot -> A -> B, and resolve path - // "/ipfs/QmRoot/A/B", the Root method will return the CID of object QmRoot - // - // For more examples see the documentation of Cid() method - Root() cid.Cid - - // Remainder returns unresolved part of the path - // - // Example: - // If you have 2 linked objects: QmRoot -> A, where A is a CBOR node - // containing the following data: - // - // {"foo": {"bar": 42 }} - // - // When resolving "/ipld/QmRoot/A/foo/bar", Remainder will return "foo/bar" - // - // For more examples see the documentation of Cid() method - Remainder() string - - Path -} - -// path implements coreiface.Path -type path struct { - path string -} - -// resolvedPath implements coreiface.resolvedPath -type resolvedPath struct { - path - cid cid.Cid - root cid.Cid - remainder string -} - -// Join appends provided segments to the base path -func Join(base Path, a ...string) Path { - s := strings.Join(append([]string{base.String()}, a...), "/") - return &path{path: s} -} - -// IpfsPath creates new /ipfs path from the provided CID -func IpfsPath(c cid.Cid) ResolvedPath { - return &resolvedPath{ - path: path{"/ipfs/" + c.String()}, - cid: c, - root: c, - remainder: "", - } -} - -// IpldPath creates new /ipld path from the provided CID -func IpldPath(c cid.Cid) ResolvedPath { - return &resolvedPath{ - path: path{"/ipld/" + c.String()}, - cid: c, - root: c, - remainder: "", - } -} - -// ParsePath parses string path to a Path -func ParsePath(p string) Path { - if pp, err := ipfspath.ParsePath(p); err == nil { - p = pp.String() - } - - return &path{path: p} -} - -// NewResolvedPath creates new ResolvedPath. This function performs no checks -// and is intended to be used by resolver implementations. Incorrect inputs may -// cause panics. Handle with care. -func NewResolvedPath(ipath ipfspath.Path, c cid.Cid, root cid.Cid, remainder string) ResolvedPath { - return &resolvedPath{ - path: path{ipath.String()}, - cid: c, - root: root, - remainder: remainder, - } -} - -func (p *path) String() string { - return p.path -} - -func (p *path) Namespace() string { - ip, err := ipfspath.ParsePath(p.path) - if err != nil { - return "" - } - - if len(ip.Segments()) < 1 { - panic("path without namespace") //this shouldn't happen under any scenario - } - return ip.Segments()[0] -} - -func (p *path) Mutable() bool { - //TODO: MFS: check for /local - return p.Namespace() == "ipns" -} - -func (p *path) IsValid() error { - _, err := ipfspath.ParsePath(p.path) - return err -} - -func (p *resolvedPath) Cid() cid.Cid { - return p.cid -} - -func (p *resolvedPath) Root() cid.Cid { - return p.root -} - -func (p *resolvedPath) Remainder() string { - return p.remainder -} diff --git a/coreiface/path/path.go b/coreiface/path/path.go new file mode 100644 index 000000000..414d454fa --- /dev/null +++ b/coreiface/path/path.go @@ -0,0 +1,199 @@ +package path + +import ( + "strings" + + cid "github.com/ipfs/go-cid" + ipfspath "github.com/ipfs/go-path" +) + +// Path is a generic wrapper for paths used in the API. A path can be resolved +// to a CID using one of Resolve functions in the API. +// +// Paths must be prefixed with a valid prefix: +// +// * /ipfs - Immutable unixfs path (files) +// * /ipld - Immutable ipld path (data) +// * /ipns - Mutable names. Usually resolves to one of the immutable paths +//TODO: /local (MFS) +type Path interface { + // String returns the path as a string. + String() string + + // Namespace returns the first component of the path. + // + // For example path "/ipfs/QmHash", calling Namespace() will return "ipfs" + // + // Calling this method on invalid paths (IsValid() != nil) will result in + // empty string + Namespace() string + + // Mutable returns false if the data pointed to by this path in guaranteed + // to not change. + // + // Note that resolved mutable path can be immutable. + Mutable() bool + + // IsValid checks if this path is a valid ipfs Path, returning nil iff it is + // valid + IsValid() error +} + +// ResolvedPath is a path which was resolved to the last resolvable node. +// ResolvedPaths are guaranteed to return nil from `IsValid` +type ResolvedPath interface { + // Cid returns the CID of the node referenced by the path. Remainder of the + // path is guaranteed to be within the node. + // + // Examples: + // If you have 3 linked objects: QmRoot -> A -> B: + // + // cidB := {"foo": {"bar": 42 }} + // cidA := {"B": {"/": cidB }} + // cidRoot := {"A": {"/": cidA }} + // + // And resolve paths: + // + // * "/ipfs/${cidRoot}" + // * Calling Cid() will return `cidRoot` + // * Calling Root() will return `cidRoot` + // * Calling Remainder() will return `` + // + // * "/ipfs/${cidRoot}/A" + // * Calling Cid() will return `cidA` + // * Calling Root() will return `cidRoot` + // * Calling Remainder() will return `` + // + // * "/ipfs/${cidRoot}/A/B/foo" + // * Calling Cid() will return `cidB` + // * Calling Root() will return `cidRoot` + // * Calling Remainder() will return `foo` + // + // * "/ipfs/${cidRoot}/A/B/foo/bar" + // * Calling Cid() will return `cidB` + // * Calling Root() will return `cidRoot` + // * Calling Remainder() will return `foo/bar` + Cid() cid.Cid + + // Root returns the CID of the root object of the path + // + // Example: + // If you have 3 linked objects: QmRoot -> A -> B, and resolve path + // "/ipfs/QmRoot/A/B", the Root method will return the CID of object QmRoot + // + // For more examples see the documentation of Cid() method + Root() cid.Cid + + // Remainder returns unresolved part of the path + // + // Example: + // If you have 2 linked objects: QmRoot -> A, where A is a CBOR node + // containing the following data: + // + // {"foo": {"bar": 42 }} + // + // When resolving "/ipld/QmRoot/A/foo/bar", Remainder will return "foo/bar" + // + // For more examples see the documentation of Cid() method + Remainder() string + + Path +} + +// path implements coreiface.Path +type path struct { + path string +} + +// resolvedPath implements coreiface.resolvedPath +type resolvedPath struct { + path + cid cid.Cid + root cid.Cid + remainder string +} + +// Join appends provided segments to the base path +func Join(base Path, a ...string) Path { + s := strings.Join(append([]string{base.String()}, a...), "/") + return &path{path: s} +} + +// IpfsPath creates new /ipfs path from the provided CID +func IpfsPath(c cid.Cid) ResolvedPath { + return &resolvedPath{ + path: path{"/ipfs/" + c.String()}, + cid: c, + root: c, + remainder: "", + } +} + +// IpldPath creates new /ipld path from the provided CID +func IpldPath(c cid.Cid) ResolvedPath { + return &resolvedPath{ + path: path{"/ipld/" + c.String()}, + cid: c, + root: c, + remainder: "", + } +} + +// ParsePath parses string path to a Path +func ParsePath(p string) Path { + if pp, err := ipfspath.ParsePath(p); err == nil { + p = pp.String() + } + + return &path{path: p} +} + +// NewResolvedPath creates new ResolvedPath. This function performs no checks +// and is intended to be used by resolver implementations. Incorrect inputs may +// cause panics. Handle with care. +func NewResolvedPath(ipath ipfspath.Path, c cid.Cid, root cid.Cid, remainder string) ResolvedPath { + return &resolvedPath{ + path: path{ipath.String()}, + cid: c, + root: root, + remainder: remainder, + } +} + +func (p *path) String() string { + return p.path +} + +func (p *path) Namespace() string { + ip, err := ipfspath.ParsePath(p.path) + if err != nil { + return "" + } + + if len(ip.Segments()) < 1 { + panic("path without namespace") // this shouldn't happen under any scenario + } + return ip.Segments()[0] +} + +func (p *path) Mutable() bool { + // TODO: MFS: check for /local + return p.Namespace() == "ipns" +} + +func (p *path) IsValid() error { + _, err := ipfspath.ParsePath(p.path) + return err +} + +func (p *resolvedPath) Cid() cid.Cid { + return p.cid +} + +func (p *resolvedPath) Root() cid.Cid { + return p.root +} + +func (p *resolvedPath) Remainder() string { + return p.remainder +} diff --git a/coreiface/pin.go b/coreiface/pin.go index 6a7dab413..736b2d68b 100644 --- a/coreiface/pin.go +++ b/coreiface/pin.go @@ -2,14 +2,15 @@ package iface import ( "context" + path "github.com/ipfs/interface-go-ipfs-core/path" - options "github.com/ipfs/interface-go-ipfs-core/options" + "github.com/ipfs/interface-go-ipfs-core/options" ) // Pin holds information about pinned resource type Pin interface { // Path to the pinned object - Path() ResolvedPath + Path() path.ResolvedPath // Type of the pin Type() string @@ -27,7 +28,7 @@ type PinStatus interface { // BadPinNode is a node that has been marked as bad by Pin.Verify type BadPinNode interface { // Path is the path of the node - Path() ResolvedPath + Path() path.ResolvedPath // Err is the reason why the node has been marked as bad Err() error @@ -37,17 +38,17 @@ type BadPinNode interface { type PinAPI interface { // Add creates new pin, be default recursive - pinning the whole referenced // tree - Add(context.Context, Path, ...options.PinAddOption) error + Add(context.Context, path.Path, ...options.PinAddOption) error // Ls returns list of pinned objects on this node Ls(context.Context, ...options.PinLsOption) ([]Pin, error) // Rm removes pin for object specified by the path - Rm(context.Context, Path, ...options.PinRmOption) error + Rm(context.Context, path.Path, ...options.PinRmOption) error // Update changes one pin to another, skipping checks for matching paths in // the old tree - Update(ctx context.Context, from Path, to Path, opts ...options.PinUpdateOption) error + Update(ctx context.Context, from path.Path, to path.Path, opts ...options.PinUpdateOption) error // Verify verifies the integrity of pinned objects Verify(context.Context) (<-chan PinStatus, error) diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 96319b488..59b49d567 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -2,6 +2,7 @@ package tests import ( "context" + "github.com/ipfs/interface-go-ipfs-core/path" "io/ioutil" "strings" "testing" @@ -110,7 +111,7 @@ func (tp *provider) TestBlockGet(t *testing.T) { t.Error("didn't get correct data back") } - p := coreiface.ParsePath("/ipfs/" + res.Path().Cid().String()) + p := path.ParsePath("/ipfs/" + res.Path().Cid().String()) rp, err := api.ResolvePath(ctx, p) if err != nil { diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index a17296d1d..0abcee32f 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -2,8 +2,9 @@ package tests import ( "context" + path "github.com/ipfs/interface-go-ipfs-core/path" "math" - "path" + gopath "path" "strings" "testing" @@ -113,7 +114,7 @@ func (tp *provider) TestDagPath(t *testing.T) { t.Fatal(err) } - p := coreiface.ParsePath(path.Join(nd.Cid().String(), "lnk")) + p := path.ParsePath(gopath.Join(nd.Cid().String(), "lnk")) rp, err := api.ResolvePath(ctx, p) if err != nil { diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index c9e99a584..98ae6853e 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -2,9 +2,10 @@ package tests import ( "context" + path "github.com/ipfs/interface-go-ipfs-core/path" "io" "math/rand" - "path" + gopath "path" "testing" "time" @@ -30,18 +31,18 @@ func (tp *provider) TestName(t *testing.T) { var rnd = rand.New(rand.NewSource(0x62796532303137)) -func addTestObject(ctx context.Context, api coreiface.CoreAPI) (coreiface.Path, error) { +func addTestObject(ctx context.Context, api coreiface.CoreAPI) (path.Path, error) { return api.Unixfs().Add(ctx, files.NewReaderFile(&io.LimitedReader{R: rnd, N: 4092})) } -func appendPath(p coreiface.Path, sub string) coreiface.Path { - return coreiface.ParsePath(path.Join(p.String(), sub)) +func appendPath(p path.Path, sub string) path.Path { + return path.ParsePath(gopath.Join(p.String(), sub)) } func (tp *provider) TestPublishResolve(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - init := func() (coreiface.CoreAPI, coreiface.Path) { + init := func() (coreiface.CoreAPI, path.Path) { apis, err := tp.MakeAPISwarm(ctx, true, 5) if err != nil { t.Fatal(err) @@ -56,7 +57,6 @@ func (tp *provider) TestPublishResolve(t *testing.T) { } return api, p } - run := func(t *testing.T, ropts []opt.NameResolveOption) { t.Run("basic", func(t *testing.T) { api, p := init() diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index f5b0ee348..685f46998 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -2,11 +2,11 @@ package tests import ( "context" + "github.com/ipfs/interface-go-ipfs-core/path" "math" "strings" "testing" - coreiface "github.com/ipfs/interface-go-ipfs-core" "github.com/ipfs/interface-go-ipfs-core/options" ipldcbor "github.com/ipfs/go-ipld-cbor" @@ -75,7 +75,7 @@ func (tp *provider) TestPathRemainder(t *testing.T) { t.Fatal(err) } - rp1, err := api.ResolvePath(ctx, coreiface.ParsePath(nd.String()+"/foo/bar")) + rp1, err := api.ResolvePath(ctx, path.ParsePath(nd.String()+"/foo/bar")) if err != nil { t.Fatal(err) } @@ -106,7 +106,7 @@ func (tp *provider) TestEmptyPathRemainder(t *testing.T) { t.Fatal(err) } - rp1, err := api.ResolvePath(ctx, coreiface.ParsePath(nd.Cid().String())) + rp1, err := api.ResolvePath(ctx, path.ParsePath(nd.Cid().String())) if err != nil { t.Fatal(err) } @@ -137,7 +137,7 @@ func (tp *provider) TestInvalidPathRemainder(t *testing.T) { t.Fatal(err) } - _, err = api.ResolvePath(ctx, coreiface.ParsePath("/ipld/"+nd.Cid().String()+"/bar/baz")) + _, err = api.ResolvePath(ctx, path.ParsePath("/ipld/"+nd.Cid().String()+"/bar/baz")) if err == nil || !strings.Contains(err.Error(), "no such link found") { t.Fatalf("unexpected error: %s", err) } @@ -173,7 +173,7 @@ func (tp *provider) TestPathRoot(t *testing.T) { t.Fatal(err) } - rp, err := api.ResolvePath(ctx, coreiface.ParsePath("/ipld/"+nd.Cid().String()+"/foo")) + rp, err := api.ResolvePath(ctx, path.ParsePath("/ipld/"+nd.Cid().String()+"/foo")) if err != nil { t.Fatal(err) } @@ -188,9 +188,9 @@ func (tp *provider) TestPathRoot(t *testing.T) { } func (tp *provider) TestPathJoin(t *testing.T) { - p1 := coreiface.ParsePath("/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz") + p1 := path.ParsePath("/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz") - if coreiface.Join(p1, "foo").String() != "/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz/foo" { + if path.Join(p1, "foo").String() != "/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz/foo" { t.Error("unexpected path") } } diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index ff6f98e35..344db65e2 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -2,6 +2,7 @@ package tests import ( "context" + "github.com/ipfs/interface-go-ipfs-core/path" "math" "strings" "testing" @@ -127,12 +128,12 @@ func (tp *provider) TestPinRecursive(t *testing.T) { t.Fatal(err) } - err = api.Pin().Add(ctx, iface.IpldPath(nd2.Cid())) + err = api.Pin().Add(ctx, path.IpldPath(nd2.Cid())) if err != nil { t.Fatal(err) } - err = api.Pin().Add(ctx, iface.IpldPath(nd3.Cid()), opt.Pin.Recursive(false)) + err = api.Pin().Add(ctx, path.IpldPath(nd3.Cid()), opt.Pin.Recursive(false)) if err != nil { t.Fatal(err) } @@ -155,8 +156,8 @@ func (tp *provider) TestPinRecursive(t *testing.T) { t.Errorf("unexpected pin list len: %d", len(list)) } - if list[0].Path().String() != iface.IpldPath(nd3.Cid()).String() { - t.Errorf("unexpected path, %s != %s", list[0].Path().String(), iface.IpfsPath(nd2.Cid()).String()) + if list[0].Path().String() != path.IpldPath(nd3.Cid()).String() { + t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.IpfsPath(nd2.Cid()).String()) } list, err = api.Pin().Ls(ctx, opt.Pin.Type.Recursive()) @@ -168,8 +169,8 @@ func (tp *provider) TestPinRecursive(t *testing.T) { t.Errorf("unexpected pin list len: %d", len(list)) } - if list[0].Path().String() != iface.IpldPath(nd2.Cid()).String() { - t.Errorf("unexpected path, %s != %s", list[0].Path().String(), iface.IpldPath(nd3.Cid()).String()) + if list[0].Path().String() != path.IpldPath(nd2.Cid()).String() { + t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.IpldPath(nd3.Cid()).String()) } list, err = api.Pin().Ls(ctx, opt.Pin.Type.Indirect()) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 15cb8abc8..d2d9f85b8 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -5,6 +5,7 @@ import ( "context" "encoding/hex" "fmt" + "github.com/ipfs/interface-go-ipfs-core/path" "io" "io/ioutil" "math" @@ -101,12 +102,12 @@ func (tp *provider) TestAdd(t *testing.T) { t.Fatal(err) } - p := func(h string) coreiface.ResolvedPath { + p := func(h string) path.ResolvedPath { c, err := cid.Parse(h) if err != nil { t.Fatal(err) } - return coreiface.IpfsPath(c) + return path.IpfsPath(c) } rf, err := ioutil.TempFile(os.TempDir(), "unixfs-add-real") @@ -592,7 +593,7 @@ func (tp *provider) TestGetEmptyFile(t *testing.T) { t.Fatal(err) } - emptyFilePath := coreiface.ParsePath(emptyFile) + emptyFilePath := path.ParsePath(emptyFile) r, err := api.Unixfs().Get(ctx, emptyFilePath) if err != nil { @@ -621,18 +622,18 @@ func (tp *provider) TestGetDir(t *testing.T) { if err != nil { t.Fatal(err) } - p := coreiface.IpfsPath(edir.Cid()) + p := path.IpfsPath(edir.Cid()) emptyDir, err := api.Object().New(ctx, options.Object.Type("unixfs-dir")) if err != nil { t.Fatal(err) } - if p.String() != coreiface.IpfsPath(emptyDir.Cid()).String() { + if p.String() != path.IpfsPath(emptyDir.Cid()).String() { t.Fatalf("expected path %s, got: %s", emptyDir.Cid(), p.String()) } - r, err := api.Unixfs().Get(ctx, coreiface.IpfsPath(emptyDir.Cid())) + r, err := api.Unixfs().Get(ctx, path.IpfsPath(emptyDir.Cid())) if err != nil { t.Fatal(err) } @@ -656,7 +657,7 @@ func (tp *provider) TestGetNonUnixfs(t *testing.T) { t.Fatal(err) } - _, err = api.Unixfs().Get(ctx, coreiface.IpfsPath(nd.Cid())) + _, err = api.Unixfs().Get(ctx, path.IpfsPath(nd.Cid())) if !strings.Contains(err.Error(), "proto: required field") { t.Fatalf("expected protobuf error, got: %s", err) } @@ -782,7 +783,7 @@ func (tp *provider) TestLsEmptyDir(t *testing.T) { t.Fatal(err) } - links, err := api.Unixfs().Ls(ctx, coreiface.IpfsPath(emptyDir.Cid())) + links, err := api.Unixfs().Ls(ctx, path.IpfsPath(emptyDir.Cid())) if err != nil { t.Fatal(err) } @@ -811,7 +812,7 @@ func (tp *provider) TestLsNonUnixfs(t *testing.T) { t.Fatal(err) } - links, err := api.Unixfs().Ls(ctx, coreiface.IpfsPath(nd.Cid())) + links, err := api.Unixfs().Ls(ctx, path.IpfsPath(nd.Cid())) if err != nil { t.Fatal(err) } diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index f9508f138..0b27519f3 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -3,16 +3,17 @@ package iface import ( "context" "github.com/ipfs/interface-go-ipfs-core/options" + path "github.com/ipfs/interface-go-ipfs-core/path" - cid "github.com/ipfs/go-cid" - files "github.com/ipfs/go-ipfs-files" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-ipfs-files" ) type AddEvent struct { Name string - Path ResolvedPath `json:",omitempty"` - Bytes int64 `json:",omitempty"` - Size string `json:",omitempty"` + Path path.ResolvedPath `json:",omitempty"` + Bytes int64 `json:",omitempty"` + Size string `json:",omitempty"` } // FileType is an enum of possible UnixFS file types. @@ -64,15 +65,15 @@ type UnixfsAPI interface { // Add imports the data from the reader into merkledag file // // TODO: a long useful comment on how to use this for many different scenarios - Add(context.Context, files.Node, ...options.UnixfsAddOption) (ResolvedPath, error) + Add(context.Context, files.Node, ...options.UnixfsAddOption) (path.ResolvedPath, error) // Get returns a read-only handle to a file tree referenced by a path // // Note that some implementations of this API may apply the specified context // to operations performed on the returned file - Get(context.Context, Path) (files.Node, error) + Get(context.Context, path.Path) (files.Node, error) // Ls returns the list of links in a directory. Links aren't guaranteed to be // returned in order - Ls(context.Context, Path, ...options.UnixfsLsOption) (<-chan DirEntry, error) + Ls(context.Context, path.Path, ...options.UnixfsLsOption) (<-chan DirEntry, error) } From fbc9ab8769cbaac70a6459c33973c0d894e85126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 26 Mar 2019 15:09:08 +0100 Subject: [PATCH 3015/3817] path: rename ParsePath and ResolvedPath This commit was moved from ipfs/interface-go-ipfs-core@21a72398d98125cae4fcef33cc80ed4a1f3be22c --- coreiface/block.go | 2 +- coreiface/coreapi.go | 2 +- coreiface/object.go | 14 +++++++------- coreiface/path.go | 4 ---- coreiface/path/path.go | 16 ++++++++-------- coreiface/pin.go | 4 ++-- coreiface/tests/block.go | 2 +- coreiface/tests/dag.go | 2 +- coreiface/tests/name.go | 2 +- coreiface/tests/path.go | 10 +++++----- coreiface/tests/unixfs.go | 4 ++-- coreiface/unixfs.go | 8 ++++---- 12 files changed, 33 insertions(+), 37 deletions(-) delete mode 100644 coreiface/path.go diff --git a/coreiface/block.go b/coreiface/block.go index 9f0ad9cbb..b105b079d 100644 --- a/coreiface/block.go +++ b/coreiface/block.go @@ -14,7 +14,7 @@ type BlockStat interface { Size() int // Path returns path to the block - Path() path.ResolvedPath + Path() path.Resolved } // BlockAPI specifies the interface to the block layer diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index bef3ce01f..12cb166a8 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -44,7 +44,7 @@ type CoreAPI interface { PubSub() PubSubAPI // ResolvePath resolves the path using Unixfs resolver - ResolvePath(context.Context, path.Path) (path.ResolvedPath, error) + ResolvePath(context.Context, path.Path) (path.Resolved, error) // ResolveNode resolves the path (if not resolved already) using Unixfs // resolver, gets and returns the resolved Node diff --git a/coreiface/object.go b/coreiface/object.go index 3e4b7e087..86536d421 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -59,11 +59,11 @@ type ObjectChange struct { // Before holds the link path before the change. Note that when a link is // added, this will be nil. - Before path.ResolvedPath + Before path.Resolved // After holds the link path after the change. Note that when a link is // removed, this will be nil. - After path.ResolvedPath + After path.Resolved } // ObjectAPI specifies the interface to MerkleDAG and contains useful utilities @@ -73,7 +73,7 @@ type ObjectAPI interface { New(context.Context, ...options.ObjectNewOption) (ipld.Node, error) // Put imports the data into merkledag - Put(context.Context, io.Reader, ...options.ObjectPutOption) (path.ResolvedPath, error) + Put(context.Context, io.Reader, ...options.ObjectPutOption) (path.Resolved, error) // Get returns the node for the path Get(context.Context, path.Path) (ipld.Node, error) @@ -90,16 +90,16 @@ type ObjectAPI interface { // AddLink adds a link under the specified path. child path can point to a // subdirectory within the patent which must be present (can be overridden // with WithCreate option). - AddLink(ctx context.Context, base path.Path, name string, child path.Path, opts ...options.ObjectAddLinkOption) (path.ResolvedPath, error) + AddLink(ctx context.Context, base path.Path, name string, child path.Path, opts ...options.ObjectAddLinkOption) (path.Resolved, error) // RmLink removes a link from the node - RmLink(ctx context.Context, base path.Path, link string) (path.ResolvedPath, error) + RmLink(ctx context.Context, base path.Path, link string) (path.Resolved, error) // AppendData appends data to the node - AppendData(context.Context, path.Path, io.Reader) (path.ResolvedPath, error) + AppendData(context.Context, path.Path, io.Reader) (path.Resolved, error) // SetData sets the data contained in the node - SetData(context.Context, path.Path, io.Reader) (path.ResolvedPath, error) + SetData(context.Context, path.Path, io.Reader) (path.Resolved, error) // Diff returns a set of changes needed to transform the first object into the // second. diff --git a/coreiface/path.go b/coreiface/path.go deleted file mode 100644 index 198651129..000000000 --- a/coreiface/path.go +++ /dev/null @@ -1,4 +0,0 @@ -package iface - -//TODO: merge with ipfspath so we don't depend on it - diff --git a/coreiface/path/path.go b/coreiface/path/path.go index 414d454fa..01b1673b1 100644 --- a/coreiface/path/path.go +++ b/coreiface/path/path.go @@ -39,9 +39,9 @@ type Path interface { IsValid() error } -// ResolvedPath is a path which was resolved to the last resolvable node. +// Resolved is a path which was resolved to the last resolvable node. // ResolvedPaths are guaranteed to return nil from `IsValid` -type ResolvedPath interface { +type Resolved interface { // Cid returns the CID of the node referenced by the path. Remainder of the // path is guaranteed to be within the node. // @@ -120,7 +120,7 @@ func Join(base Path, a ...string) Path { } // IpfsPath creates new /ipfs path from the provided CID -func IpfsPath(c cid.Cid) ResolvedPath { +func IpfsPath(c cid.Cid) Resolved { return &resolvedPath{ path: path{"/ipfs/" + c.String()}, cid: c, @@ -130,7 +130,7 @@ func IpfsPath(c cid.Cid) ResolvedPath { } // IpldPath creates new /ipld path from the provided CID -func IpldPath(c cid.Cid) ResolvedPath { +func IpldPath(c cid.Cid) Resolved { return &resolvedPath{ path: path{"/ipld/" + c.String()}, cid: c, @@ -139,8 +139,8 @@ func IpldPath(c cid.Cid) ResolvedPath { } } -// ParsePath parses string path to a Path -func ParsePath(p string) Path { +// New parses string path to a Path +func New(p string) Path { if pp, err := ipfspath.ParsePath(p); err == nil { p = pp.String() } @@ -148,10 +148,10 @@ func ParsePath(p string) Path { return &path{path: p} } -// NewResolvedPath creates new ResolvedPath. This function performs no checks +// NewResolvedPath creates new Resolved path. This function performs no checks // and is intended to be used by resolver implementations. Incorrect inputs may // cause panics. Handle with care. -func NewResolvedPath(ipath ipfspath.Path, c cid.Cid, root cid.Cid, remainder string) ResolvedPath { +func NewResolvedPath(ipath ipfspath.Path, c cid.Cid, root cid.Cid, remainder string) Resolved { return &resolvedPath{ path: path{ipath.String()}, cid: c, diff --git a/coreiface/pin.go b/coreiface/pin.go index 736b2d68b..7df2956f0 100644 --- a/coreiface/pin.go +++ b/coreiface/pin.go @@ -10,7 +10,7 @@ import ( // Pin holds information about pinned resource type Pin interface { // Path to the pinned object - Path() path.ResolvedPath + Path() path.Resolved // Type of the pin Type() string @@ -28,7 +28,7 @@ type PinStatus interface { // BadPinNode is a node that has been marked as bad by Pin.Verify type BadPinNode interface { // Path is the path of the node - Path() path.ResolvedPath + Path() path.Resolved // Err is the reason why the node has been marked as bad Err() error diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 59b49d567..961ac722d 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -111,7 +111,7 @@ func (tp *provider) TestBlockGet(t *testing.T) { t.Error("didn't get correct data back") } - p := path.ParsePath("/ipfs/" + res.Path().Cid().String()) + p := path.New("/ipfs/" + res.Path().Cid().String()) rp, err := api.ResolvePath(ctx, p) if err != nil { diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index 0abcee32f..fe92641f4 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -114,7 +114,7 @@ func (tp *provider) TestDagPath(t *testing.T) { t.Fatal(err) } - p := path.ParsePath(gopath.Join(nd.Cid().String(), "lnk")) + p := path.New(gopath.Join(nd.Cid().String(), "lnk")) rp, err := api.ResolvePath(ctx, p) if err != nil { diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index 98ae6853e..efaf1d3ae 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -36,7 +36,7 @@ func addTestObject(ctx context.Context, api coreiface.CoreAPI) (path.Path, error } func appendPath(p path.Path, sub string) path.Path { - return path.ParsePath(gopath.Join(p.String(), sub)) + return path.New(gopath.Join(p.String(), sub)) } func (tp *provider) TestPublishResolve(t *testing.T) { diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index 685f46998..4fd18bd20 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -75,7 +75,7 @@ func (tp *provider) TestPathRemainder(t *testing.T) { t.Fatal(err) } - rp1, err := api.ResolvePath(ctx, path.ParsePath(nd.String()+"/foo/bar")) + rp1, err := api.ResolvePath(ctx, path.New(nd.String()+"/foo/bar")) if err != nil { t.Fatal(err) } @@ -106,7 +106,7 @@ func (tp *provider) TestEmptyPathRemainder(t *testing.T) { t.Fatal(err) } - rp1, err := api.ResolvePath(ctx, path.ParsePath(nd.Cid().String())) + rp1, err := api.ResolvePath(ctx, path.New(nd.Cid().String())) if err != nil { t.Fatal(err) } @@ -137,7 +137,7 @@ func (tp *provider) TestInvalidPathRemainder(t *testing.T) { t.Fatal(err) } - _, err = api.ResolvePath(ctx, path.ParsePath("/ipld/"+nd.Cid().String()+"/bar/baz")) + _, err = api.ResolvePath(ctx, path.New("/ipld/"+nd.Cid().String()+"/bar/baz")) if err == nil || !strings.Contains(err.Error(), "no such link found") { t.Fatalf("unexpected error: %s", err) } @@ -173,7 +173,7 @@ func (tp *provider) TestPathRoot(t *testing.T) { t.Fatal(err) } - rp, err := api.ResolvePath(ctx, path.ParsePath("/ipld/"+nd.Cid().String()+"/foo")) + rp, err := api.ResolvePath(ctx, path.New("/ipld/"+nd.Cid().String()+"/foo")) if err != nil { t.Fatal(err) } @@ -188,7 +188,7 @@ func (tp *provider) TestPathRoot(t *testing.T) { } func (tp *provider) TestPathJoin(t *testing.T) { - p1 := path.ParsePath("/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz") + p1 := path.New("/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz") if path.Join(p1, "foo").String() != "/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz/foo" { t.Error("unexpected path") diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index d2d9f85b8..38fab7cd8 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -102,7 +102,7 @@ func (tp *provider) TestAdd(t *testing.T) { t.Fatal(err) } - p := func(h string) path.ResolvedPath { + p := func(h string) path.Resolved { c, err := cid.Parse(h) if err != nil { t.Fatal(err) @@ -593,7 +593,7 @@ func (tp *provider) TestGetEmptyFile(t *testing.T) { t.Fatal(err) } - emptyFilePath := path.ParsePath(emptyFile) + emptyFilePath := path.New(emptyFile) r, err := api.Unixfs().Get(ctx, emptyFilePath) if err != nil { diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 0b27519f3..686c40298 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -11,9 +11,9 @@ import ( type AddEvent struct { Name string - Path path.ResolvedPath `json:",omitempty"` - Bytes int64 `json:",omitempty"` - Size string `json:",omitempty"` + Path path.Resolved `json:",omitempty"` + Bytes int64 `json:",omitempty"` + Size string `json:",omitempty"` } // FileType is an enum of possible UnixFS file types. @@ -65,7 +65,7 @@ type UnixfsAPI interface { // Add imports the data from the reader into merkledag file // // TODO: a long useful comment on how to use this for many different scenarios - Add(context.Context, files.Node, ...options.UnixfsAddOption) (path.ResolvedPath, error) + Add(context.Context, files.Node, ...options.UnixfsAddOption) (path.Resolved, error) // Get returns a read-only handle to a file tree referenced by a path // From a80c6053a9c9141c9eb3ee878a30af0cb74ee7d7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 4 Apr 2019 04:34:50 -0700 Subject: [PATCH 3016/3817] provider queue: don't repeatedly retry the same item if we fail License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-provider@00f05cb1d43629ce0f98fa134b1866ff2f2ab5a8 --- provider/queue.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/provider/queue.go b/provider/queue.go index c8982ff10..b1d899cbf 100644 --- a/provider/queue.go +++ b/provider/queue.go @@ -72,13 +72,14 @@ func (q *Queue) nextEntry() (datastore.Key, cid.Cid) { key := q.queueKey(q.head) value, err := q.ds.Get(key) - if err == datastore.ErrNotFound { - log.Warningf("Error missing entry in queue: %s", key) + if err != nil { + if err == datastore.ErrNotFound { + log.Warningf("Error missing entry in queue: %s", key) + } else { + log.Errorf("Error fetching from queue: %s", err) + } q.head++ // move on continue - } else if err != nil { - log.Warningf("Error fetching from queue: %s", err) - continue } c, err := cid.Parse(value) From a30c9f6649da45a4a7edba09364e11c6d3c5ef30 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Fri, 5 Apr 2019 09:52:12 -0700 Subject: [PATCH 3017/3817] Close provider on ipfs shutdown License: MIT Signed-off-by: Michael Avila This commit was moved from ipfs/go-ipfs-provider@dbfc1c39018ca456607615c030853eb9172f7eaf --- provider/offline.go | 4 ++++ provider/provider.go | 8 ++++++++ provider/queue.go | 18 +++++++++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/provider/offline.go b/provider/offline.go index 029ddfa98..0c91ed2af 100644 --- a/provider/offline.go +++ b/provider/offline.go @@ -14,3 +14,7 @@ func (op *offlineProvider) Run() {} func (op *offlineProvider) Provide(cid cid.Cid) error { return nil } + +func (op *offlineProvider) Close() error { + return nil +} diff --git a/provider/provider.go b/provider/provider.go index f9aa4ed78..67c5c6b6b 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -20,6 +20,8 @@ type Provider interface { Run() // Provide takes a cid and makes an attempt to announce it to the network Provide(cid.Cid) error + // Close stops the provider + Close() error } type provider struct { @@ -39,6 +41,12 @@ func NewProvider(ctx context.Context, queue *Queue, contentRouting routing.Conte } } +// Close stops the provider +func (p *provider) Close() error { + p.queue.Close() + return nil +} + // Start workers to handle provide requests. func (p *provider) Run() { p.handleAnnouncements() diff --git a/provider/queue.go b/provider/queue.go index b1d899cbf..8fdfca815 100644 --- a/provider/queue.go +++ b/provider/queue.go @@ -27,6 +27,8 @@ type Queue struct { ds datastore.Datastore // Must be threadsafe dequeue chan cid.Cid enqueue chan cid.Cid + close context.CancelFunc + closed chan struct{} } // NewQueue creates a queue for cids @@ -36,19 +38,29 @@ func NewQueue(ctx context.Context, name string, ds datastore.Datastore) (*Queue, if err != nil { return nil, err } + cancelCtx, cancel := context.WithCancel(ctx) q := &Queue{ name: name, - ctx: ctx, + ctx: cancelCtx, head: head, tail: tail, ds: namespaced, dequeue: make(chan cid.Cid), enqueue: make(chan cid.Cid), + close: cancel, + closed: make(chan struct{}, 1), } q.work() return q, nil } +// Close stops the queue +func (q *Queue) Close() error { + q.close() + <-q.closed + return nil +} + // Enqueue puts a cid in the queue func (q *Queue) Enqueue(cid cid.Cid) { select { @@ -103,6 +115,10 @@ func (q *Queue) work() { var k datastore.Key = datastore.Key{} var c cid.Cid = cid.Undef + defer func() { + close(q.closed) + }() + for { if c == cid.Undef { k, c = q.nextEntry() From d493b701942234de0168890c2625082ff382e1b3 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 15 Apr 2019 21:58:51 -0700 Subject: [PATCH 3018/3817] fix: cleanup TestDhtProvide And fix for peer ID formatting changes. fixes https://github.com/ipfs/go-ipfs/pull/6222#issuecomment-483479039 This commit was moved from ipfs/interface-go-ipfs-core@29b26f5bcb322e936e67dfbb7b0a5264b7e23089 --- coreiface/tests/dht.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/coreiface/tests/dht.go b/coreiface/tests/dht.go index 1793cd738..5482b50b1 100644 --- a/coreiface/tests/dht.go +++ b/coreiface/tests/dht.go @@ -130,17 +130,17 @@ func (tp *provider) TestDhtProvide(t *testing.T) { t.Fatal(err) } - provider := <-out + _, ok := <-out + + if ok { + t.Fatal("did not expect to find any providers") + } self0, err := apis[0].Key().Self(ctx) if err != nil { t.Fatal(err) } - if provider.ID.String() != "" { - t.Errorf("got wrong provider: %s != %s", provider.ID.String(), self0.ID().String()) - } - err = apis[0].Dht().Provide(ctx, p) if err != nil { t.Fatal(err) @@ -151,7 +151,7 @@ func (tp *provider) TestDhtProvide(t *testing.T) { t.Fatal(err) } - provider = <-out + provider := <-out if provider.ID.String() != self0.ID().String() { t.Errorf("got wrong provider: %s != %s", provider.ID.String(), self0.ID().String()) From 597a5cc370527406a23d325644cf1321e449f12b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 3 Apr 2019 03:44:32 +0200 Subject: [PATCH 3019/3817] Cleanup core package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@454a1503e463566e564b8aeb0af2bbac35f89d88 --- namesys/republisher/repub_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 8f0048c4c..48a0b086f 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/ipfs/go-ipfs/core" + "github.com/ipfs/go-ipfs/core/bootstrap" mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" @@ -45,7 +46,7 @@ func TestRepublish(t *testing.T) { t.Fatal(err) } - bsinf := core.BootstrapConfigWithPeers( + bsinf := bootstrap.BootstrapConfigWithPeers( []pstore.PeerInfo{ nodes[0].Peerstore.PeerInfo(nodes[0].Identity), }, From f30c65a3877c3a9f7be87adbdf54a92554f85c6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 3 Apr 2019 03:51:45 +0200 Subject: [PATCH 3020/3817] Move pathresolve MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@2479384173ecb39a5b133b1a6178e6f82ee324b8 --- namesys/resolve/pathresolver_test.go | 32 ++++++++++++ namesys/resolve/resolve.go | 78 ++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 namesys/resolve/pathresolver_test.go create mode 100644 namesys/resolve/resolve.go diff --git a/namesys/resolve/pathresolver_test.go b/namesys/resolve/pathresolver_test.go new file mode 100644 index 000000000..fe578b5d3 --- /dev/null +++ b/namesys/resolve/pathresolver_test.go @@ -0,0 +1,32 @@ +package resolve_test + +import ( + "testing" + + coremock "github.com/ipfs/go-ipfs/core/mock" + "github.com/ipfs/go-ipfs/namesys/resolve" + + path "github.com/ipfs/go-path" +) + +func TestResolveNoComponents(t *testing.T) { + n, err := coremock.NewMockNode() + if n == nil || err != nil { + t.Fatal("Should have constructed a mock node", err) + } + + _, err = resolve.Resolve(n.Context(), n.Namesys, n.Resolver, path.Path("/ipns/")) + if err != path.ErrNoComponents { + t.Fatal("Should error with no components (/ipns/).", err) + } + + _, err = resolve.Resolve(n.Context(), n.Namesys, n.Resolver, path.Path("/ipfs/")) + if err != path.ErrNoComponents { + t.Fatal("Should error with no components (/ipfs/).", err) + } + + _, err = resolve.Resolve(n.Context(), n.Namesys, n.Resolver, path.Path("/../..")) + if err != path.ErrBadPath { + t.Fatal("Should error with invalid path.", err) + } +} diff --git a/namesys/resolve/resolve.go b/namesys/resolve/resolve.go new file mode 100644 index 000000000..bd1667fa4 --- /dev/null +++ b/namesys/resolve/resolve.go @@ -0,0 +1,78 @@ +package resolve + +import ( + "context" + "errors" + "strings" + + "github.com/ipfs/go-ipld-format" + log2 "github.com/ipfs/go-log" + logging "github.com/ipfs/go-log" + "github.com/ipfs/go-path" + "github.com/ipfs/go-path/resolver" + + "github.com/ipfs/go-ipfs/namesys" +) + +var log = logging.Logger("nsresolv") + +// ErrNoNamesys is an explicit error for when an IPFS node doesn't +// (yet) have a name system +var ErrNoNamesys = errors.New( + "core/resolve: no Namesys on IpfsNode - can't resolve ipns entry") + +// ResolveIPNS resolves /ipns paths +func ResolveIPNS(ctx context.Context, nsys namesys.NameSystem, p path.Path) (path.Path, error) { + if strings.HasPrefix(p.String(), "/ipns/") { + evt := log.EventBegin(ctx, "resolveIpnsPath") + defer evt.Done() + // resolve ipns paths + + // TODO(cryptix): we should be able to query the local cache for the path + if nsys == nil { + evt.Append(log2.LoggableMap{"error": ErrNoNamesys.Error()}) + return "", ErrNoNamesys + } + + seg := p.Segments() + + if len(seg) < 2 || seg[1] == "" { // just "/" without further segments + evt.Append(log2.LoggableMap{"error": path.ErrNoComponents.Error()}) + return "", path.ErrNoComponents + } + + extensions := seg[2:] + resolvable, err := path.FromSegments("/", seg[0], seg[1]) + if err != nil { + evt.Append(log2.LoggableMap{"error": err.Error()}) + return "", err + } + + respath, err := nsys.Resolve(ctx, resolvable.String()) + if err != nil { + evt.Append(log2.LoggableMap{"error": err.Error()}) + return "", err + } + + segments := append(respath.Segments(), extensions...) + p, err = path.FromSegments("/", segments...) + if err != nil { + evt.Append(log2.LoggableMap{"error": err.Error()}) + return "", err + } + } + return p, nil +} + +// Resolve resolves the given path by parsing out protocol-specific +// entries (e.g. /ipns/) and then going through the /ipfs/ +// entries and returning the final node. +func Resolve(ctx context.Context, nsys namesys.NameSystem, r *resolver.Resolver, p path.Path) (format.Node, error) { + p, err := ResolveIPNS(ctx, nsys, p) + if err != nil { + return nil, err + } + + // ok, we have an IPFS path now (or what we'll treat as one) + return r.ResolvePath(ctx, p) +} From 20e11ccf03fb918347952186e34e439575bbca73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 3 Apr 2019 16:56:45 +0200 Subject: [PATCH 3021/3817] Move option parsing to BuildCfg; fix imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit License: MIT Signed-off-by: Åukasz Magiera This commit was moved from ipfs/go-namesys@38d5b8ede7111687c4c20f5c31ba068baa51d9eb --- namesys/resolve/resolve.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/namesys/resolve/resolve.go b/namesys/resolve/resolve.go index bd1667fa4..128619c65 100644 --- a/namesys/resolve/resolve.go +++ b/namesys/resolve/resolve.go @@ -6,7 +6,6 @@ import ( "strings" "github.com/ipfs/go-ipld-format" - log2 "github.com/ipfs/go-log" logging "github.com/ipfs/go-log" "github.com/ipfs/go-path" "github.com/ipfs/go-path/resolver" @@ -30,34 +29,34 @@ func ResolveIPNS(ctx context.Context, nsys namesys.NameSystem, p path.Path) (pat // TODO(cryptix): we should be able to query the local cache for the path if nsys == nil { - evt.Append(log2.LoggableMap{"error": ErrNoNamesys.Error()}) + evt.Append(logging.LoggableMap{"error": ErrNoNamesys.Error()}) return "", ErrNoNamesys } seg := p.Segments() if len(seg) < 2 || seg[1] == "" { // just "/" without further segments - evt.Append(log2.LoggableMap{"error": path.ErrNoComponents.Error()}) + evt.Append(logging.LoggableMap{"error": path.ErrNoComponents.Error()}) return "", path.ErrNoComponents } extensions := seg[2:] resolvable, err := path.FromSegments("/", seg[0], seg[1]) if err != nil { - evt.Append(log2.LoggableMap{"error": err.Error()}) + evt.Append(logging.LoggableMap{"error": err.Error()}) return "", err } respath, err := nsys.Resolve(ctx, resolvable.String()) if err != nil { - evt.Append(log2.LoggableMap{"error": err.Error()}) + evt.Append(logging.LoggableMap{"error": err.Error()}) return "", err } segments := append(respath.Segments(), extensions...) p, err = path.FromSegments("/", segments...) if err != nil { - evt.Append(log2.LoggableMap{"error": err.Error()}) + evt.Append(logging.LoggableMap{"error": err.Error()}) return "", err } } From 2afcfc942636613bd75d607bfe83a97de89020b7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 30 Apr 2019 11:34:01 -0700 Subject: [PATCH 3022/3817] gc: cancel context We were canceling the context in `GarbageCollect` but some functions call `GC` directly. Move the context cancelation down to where we actually _need_ it. fixes #6279 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@8fd812606e1f74c79512ce0b4d2986063fa4493b --- pinning/pinner/gc/gc.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 12b0fadb2..bf8b7b10f 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -39,6 +39,7 @@ type Result struct { // The routine then iterates over every block in the blockstore and // deletes any block that is not found in the marked set. func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn pin.Pinner, bestEffortRoots []cid.Cid) <-chan Result { + ctx, cancel := context.WithCancel(ctx) elock := log.EventBegin(ctx, "GC.lockWait") unlocker := bs.GCLock() @@ -52,6 +53,7 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn output := make(chan Result, 128) go func() { + defer cancel() defer close(output) defer unlocker.Unlock() defer elock.Done() From 763b3d8a00d19fd1ba9d5b5b8825cbd7a7f01d1e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 7 May 2019 00:57:21 -0700 Subject: [PATCH 3023/3817] switch to base32 cidv1 by default This commit was moved from ipfs/interface-go-ipfs-core@6287246646853656271cbd190acab071950d4060 --- coreiface/tests/block.go | 4 ++-- coreiface/tests/dag.go | 6 +++--- coreiface/tests/unixfs.go | 24 ++++++++++++------------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 961ac722d..34e47e90c 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -61,7 +61,7 @@ func (tp *provider) TestBlockPutFormat(t *testing.T) { t.Fatal(err) } - if res.Path().Cid().String() != "zdpuAn4amuLWo8Widi5v6VQpuo2dnpnwbVE3oB6qqs7mDSeoa" { + if res.Path().Cid().String() != "bafyreiayl6g3gitr7ys7kyng7sjywlrgimdoymco3jiyab6rozecmoazne" { t.Errorf("got wrong cid: %s", res.Path().Cid().String()) } } @@ -79,7 +79,7 @@ func (tp *provider) TestBlockPutHash(t *testing.T) { t.Fatal(err) } - if res.Path().Cid().String() != "zBurKB9YZkcDf6xa53WBE8CFX4ydVqAyf9KPXBFZt5stJzEstaS8Hukkhu4gwpMtc1xHNDbzP7sPtQKyWsP3C8fbhkmrZ" { + if res.Path().Cid().String() != "bafyb2qgdh7w6dcq24u65xbtdoehyavegnpvxcqce7ttvs6ielgmwdfxrahmu37d33atik57x5y6s7d7qz32aasuwgirh3ocn6ywswqdifvu6e" { t.Errorf("got wrong cid: %s", res.Path().Cid().String()) } } diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index fe92641f4..0bb3aa487 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -58,7 +58,7 @@ func (tp *provider) TestPut(t *testing.T) { t.Fatal(err) } - if nd.Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" { + if nd.Cid().String() != "bafyreicnga62zhxnmnlt6ymq5hcbsg7gdhqdu6z4ehu3wpjhvqnflfy6nm" { t.Errorf("got wrong cid: %s", nd.Cid().String()) } } @@ -81,7 +81,7 @@ func (tp *provider) TestPutWithHash(t *testing.T) { t.Fatal(err) } - if nd.Cid().String() != "z5hRLNd2sv4z1c" { + if nd.Cid().String() != "bafyqabtfjbswy3dp" { t.Errorf("got wrong cid: %s", nd.Cid().String()) } } @@ -179,7 +179,7 @@ func (tp *provider) TestBatch(t *testing.T) { t.Fatal(err) } - if nd.Cid().String() != "zdpuAqckYF3ToF3gcJNxPZXmnmGuXd3gxHCXhq81HGxBejEvv" { + if nd.Cid().String() != "bafyreicnga62zhxnmnlt6ymq5hcbsg7gdhqdu6z4ehu3wpjhvqnflfy6nm" { t.Errorf("got wrong cid: %s", nd.Cid().String()) } diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 38fab7cd8..c810167e8 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -170,20 +170,20 @@ func (tp *provider) TestAdd(t *testing.T) { { name: "addCidV1", data: strFile(helloStr), - path: "/ipfs/zb2rhdhmJjJZs9qkhQCpCQ7VREFkqWw3h1r8utjVvQugwHPFd", + path: "/ipfs/bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", opts: []options.UnixfsAddOption{options.Unixfs.CidVersion(1)}, }, { name: "addCidV1NoLeaves", data: strFile(helloStr), - path: "/ipfs/zdj7WY4GbN8NDbTW1dfCShAQNVovams2xhq9hVCx5vXcjvT8g", + path: "/ipfs/bafybeibhbcn7k7o2m6xsqkrlfiokod3nxwe47viteynhruh6uqx7hvkjfu", opts: []options.UnixfsAddOption{options.Unixfs.CidVersion(1), options.Unixfs.RawLeaves(false)}, }, // Non sha256 hash vs CID { name: "addCidSha3", data: strFile(helloStr), - path: "/ipfs/zb2wwnYtXBxpndNABjtYxWAPt3cwWNRnc11iT63fvkYV78iRb", + path: "/ipfs/bafkrmichjflejeh6aren53o7pig7zk3m3vxqcoc2i5dv326k3x6obh7jry", opts: []options.UnixfsAddOption{options.Unixfs.Hash(mh.SHA3_256)}, }, { @@ -196,25 +196,25 @@ func (tp *provider) TestAdd(t *testing.T) { { name: "addInline", data: strFile(helloStr), - path: "/ipfs/zaYomJdLndMku8P9LHngHB5w2CQ7NenLbv", + path: "/ipfs/bafyaafikcmeaeeqnnbswy3dpfqqho33snrsccgan", opts: []options.UnixfsAddOption{options.Unixfs.Inline(true)}, }, { name: "addInlineLimit", data: strFile(helloStr), - path: "/ipfs/zaYomJdLndMku8P9LHngHB5w2CQ7NenLbv", + path: "/ipfs/bafyaafikcmeaeeqnnbswy3dpfqqho33snrsccgan", opts: []options.UnixfsAddOption{options.Unixfs.InlineLimit(32), options.Unixfs.Inline(true)}, }, { name: "addInlineZero", data: strFile(""), - path: "/ipfs/z2yYDV", + path: "/ipfs/bafkqaaa", opts: []options.UnixfsAddOption{options.Unixfs.InlineLimit(0), options.Unixfs.Inline(true), options.Unixfs.RawLeaves(true)}, }, { //TODO: after coreapi add is used in `ipfs add`, consider making this default for inline name: "addInlineRaw", data: strFile(helloStr), - path: "/ipfs/zj7Gr8AcBreqGEfrnR5kPFe", + path: "/ipfs/bafkqadlimvwgy3zmeb3w64tmmqqq", opts: []options.UnixfsAddOption{options.Unixfs.InlineLimit(32), options.Unixfs.Inline(true), options.Unixfs.RawLeaves(true)}, }, // Chunker / Layout @@ -291,20 +291,20 @@ func (tp *provider) TestAdd(t *testing.T) { { name: "simpleNoCopy", data: realFile, - path: "/ipfs/zb2rhdhmJjJZs9qkhQCpCQ7VREFkqWw3h1r8utjVvQugwHPFd", + path: "/ipfs/bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", opts: []options.UnixfsAddOption{options.Unixfs.Nocopy(true)}, }, { name: "noCopyNoRaw", data: realFile, - path: "/ipfs/zb2rhdhmJjJZs9qkhQCpCQ7VREFkqWw3h1r8utjVvQugwHPFd", + path: "/ipfs/bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", opts: []options.UnixfsAddOption{options.Unixfs.Nocopy(true), options.Unixfs.RawLeaves(false)}, err: "nocopy option requires '--raw-leaves' to be enabled as well", }, { name: "noCopyNoPath", data: strFile(helloStr), - path: "/ipfs/zb2rhdhmJjJZs9qkhQCpCQ7VREFkqWw3h1r8utjVvQugwHPFd", + path: "/ipfs/bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", opts: []options.UnixfsAddOption{options.Unixfs.Nocopy(true)}, err: helpers.ErrMissingFsRef.Error(), }, @@ -312,9 +312,9 @@ func (tp *provider) TestAdd(t *testing.T) { { name: "simpleAddEvent", data: strFile(helloStr), - path: "/ipfs/zb2rhdhmJjJZs9qkhQCpCQ7VREFkqWw3h1r8utjVvQugwHPFd", + path: "/ipfs/bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", events: []coreiface.AddEvent{ - {Name: "zb2rhdhmJjJZs9qkhQCpCQ7VREFkqWw3h1r8utjVvQugwHPFd", Path: p("zb2rhdhmJjJZs9qkhQCpCQ7VREFkqWw3h1r8utjVvQugwHPFd"), Size: strconv.Itoa(len(helloStr))}, + {Name: "bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", Path: p("bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa"), Size: strconv.Itoa(len(helloStr))}, }, opts: []options.UnixfsAddOption{options.Unixfs.RawLeaves(true)}, }, From a5254e2962046f64c7905dc8853388f8c26ef004 Mon Sep 17 00:00:00 2001 From: Erik Ingenito Date: Wed, 8 May 2019 12:14:25 -0700 Subject: [PATCH 3024/3817] Fix directory mv and add tests * Naming changes for my personal comprehension * Add a few tests covering simple mv cases (including our bug) * Fix bug in Mv that would leave fail to delete src directories (when named without a trailing slash) after copying them to their new location This commit was moved from ipfs/go-mfs@2b77b0a36fb40e40ec6fa6f9aea50a73061e16e3 --- mfs/mfs_test.go | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ mfs/ops.go | 30 ++++++------- 2 files changed, 131 insertions(+), 15 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 8d64fe581..81c63f95c 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -83,6 +83,14 @@ func mkdirP(t *testing.T, root *Directory, pth string) *Directory { return cur } +func assertDirNotAtPath(root *Directory, pth string) error { + _, err := DirLookup(root, pth) + if err == nil { + return fmt.Errorf("%s exists in %s", pth, root.name) + } + return nil +} + func assertDirAtPath(root *Directory, pth string, children []string) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -370,6 +378,114 @@ func TestDirectoryLoadFromDag(t *testing.T) { } } +func TestMvFile(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + dagService, rt := setupRoot(ctx, t) + rootDir := rt.GetDirectory() + + fi := getRandFile(t, dagService, 1000) + + err := rootDir.AddChild("afile", fi) + if err != nil { + t.Fatal(err) + } + + err = Mv(rt, "/afile", "/bfile") + if err != nil { + t.Fatal(err) + } + + err = assertFileAtPath(dagService, rootDir, fi, "bfile") + if err != nil { + t.Fatal(err) + } +} + +func TestMvFileToSubdir(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + dagService, rt := setupRoot(ctx, t) + rootDir := rt.GetDirectory() + + _ = mkdirP(t, rootDir, "test1") + + fi := getRandFile(t, dagService, 1000) + + err := rootDir.AddChild("afile", fi) + if err != nil { + t.Fatal(err) + } + + err = Mv(rt, "/afile", "/test1") + if err != nil { + t.Fatal(err) + } + + err = assertFileAtPath(dagService, rootDir, fi, "test1/afile") + if err != nil { + t.Fatal(err) + } +} + +func TestMvFileToSubdirWithRename(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + dagService, rt := setupRoot(ctx, t) + rootDir := rt.GetDirectory() + + _ = mkdirP(t, rootDir, "test1") + + fi := getRandFile(t, dagService, 1000) + + err := rootDir.AddChild("afile", fi) + if err != nil { + t.Fatal(err) + } + + err = Mv(rt, "/afile", "/test1/bfile") + if err != nil { + t.Fatal(err) + } + + err = assertFileAtPath(dagService, rootDir, fi, "test1/bfile") + if err != nil { + t.Fatal(err) + } +} + +func TestMvDir(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + dagService, rt := setupRoot(ctx, t) + rootDir := rt.GetDirectory() + + _ = mkdirP(t, rootDir, "test1") + d2 := mkdirP(t, rootDir, "test2") + + fi := getRandFile(t, dagService, 1000) + + err := d2.AddChild("afile", fi) + if err != nil { + t.Fatal(err) + } + + err = Mv(rt, "/test2", "/test1") + if err != nil { + t.Fatal(err) + } + + err = assertDirNotAtPath(rootDir, "test2") + if err != nil { + t.Fatal(err) + } + + err = assertFileAtPath(dagService, rootDir, fi, "test1/test2/afile") + if err != nil { + t.Fatal(err) + } +} + func TestMfsFile(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/mfs/ops.go b/mfs/ops.go index 3232f8103..2b2907289 100644 --- a/mfs/ops.go +++ b/mfs/ops.go @@ -21,29 +21,29 @@ import ( // Mv moves the file or directory at 'src' to 'dst' // TODO: Document what the strings 'src' and 'dst' represent. func Mv(r *Root, src, dst string) error { - srcDir, srcFname := gopath.Split(src) + srcDirName, srcFname := gopath.Split(src) - var dstDirStr string - var filename string + var dstDirName string + var dstFname string if dst[len(dst)-1] == '/' { - dstDirStr = dst - filename = srcFname + dstDirName = dst + dstFname = srcFname } else { - dstDirStr, filename = gopath.Split(dst) + dstDirName, dstFname = gopath.Split(dst) } // get parent directories of both src and dest first - dstDir, err := lookupDir(r, dstDirStr) + dstDir, err := lookupDir(r, dstDirName) if err != nil { return err } - srcDirObj, err := lookupDir(r, srcDir) + srcDir, err := lookupDir(r, srcDirName) if err != nil { return err } - srcObj, err := srcDirObj.Child(srcFname) + srcObj, err := srcDir.Child(srcFname) if err != nil { return err } @@ -53,14 +53,14 @@ func Mv(r *Root, src, dst string) error { return err } - fsn, err := dstDir.Child(filename) + fsn, err := dstDir.Child(dstFname) if err == nil { switch n := fsn.(type) { case *File: - _ = dstDir.Unlink(filename) + _ = dstDir.Unlink(dstFname) case *Directory: dstDir = n - filename = srcFname + dstFname = srcFname default: return fmt.Errorf("unexpected type at path: %s", dst) } @@ -68,16 +68,16 @@ func Mv(r *Root, src, dst string) error { return err } - err = dstDir.AddChild(filename, nd) + err = dstDir.AddChild(dstFname, nd) if err != nil { return err } - if srcDir == dstDirStr && srcFname == filename { + if srcDir.name == dstDir.name && srcFname == dstFname { return nil } - return srcDirObj.Unlink(srcFname) + return srcDir.Unlink(srcFname) } func lookupDir(r *Root, path string) (*Directory, error) { From 931e619ba6b32a99a9f05681f4fe1e6bd1d2ffef Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 8 May 2019 21:40:17 -0700 Subject: [PATCH 3025/3817] pin: don't walk all pinned blocks when removing a non-existent pin We do this _just_ to make the error nicer but it's really slow. Additionally, we do it while holding the pin lock, blocking all other pin operations. fixes #6295 License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-ipfs-pinner@4e5b0e8d1d6fd813513000efcef1a9607b16e46b --- pinning/pinner/pin.go | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 24dbf4653..8df21ee1c 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -263,32 +263,24 @@ func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { } // ErrNotPinned is returned when trying to unpin items which are not pinned. -var ErrNotPinned = fmt.Errorf("not pinned") +var ErrNotPinned = fmt.Errorf("not pinned or pinned indirectly") // Unpin a given key func (p *pinner) Unpin(ctx context.Context, c cid.Cid, recursive bool) error { p.lock.Lock() defer p.lock.Unlock() - reason, pinned, err := p.isPinnedWithType(c, Any) - if err != nil { - return err - } - if !pinned { - return ErrNotPinned - } - switch reason { - case "recursive": - if recursive { - p.recursePin.Remove(c) - return nil + if p.recursePin.Has(c) { + if !recursive { + return fmt.Errorf("%s is pinned recursively", c) } - return fmt.Errorf("%s is pinned recursively", c) - case "direct": + p.recursePin.Remove(c) + return nil + } + if p.directPin.Has(c) { p.directPin.Remove(c) return nil - default: - return fmt.Errorf("%s is pinned indirectly under %s", c, reason) } + return ErrNotPinned } func (p *pinner) isInternalPin(c cid.Cid) bool { From c638646a28cd994932e454f10381d2573323ffc6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 10 May 2019 23:53:44 -0700 Subject: [PATCH 3026/3817] chore: fix linter nits License: MIT Signed-off-by: Steven Allen This commit was moved from ipfs/go-filestore@2f0edb371cf3d24eb1a0535b988bd4de8e462cce --- filestore/fsrefstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index b4c66a32d..320ee5928 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -281,7 +281,7 @@ func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { if !f.AllowFiles { return ErrFilestoreNotEnabled } - if !filepath.HasPrefix(b.PosInfo.FullPath, f.root) { + if !filepath.HasPrefix(b.PosInfo.FullPath, f.root) { //nolint:staticcheck return fmt.Errorf("cannot add filestore references outside ipfs root (%s)", f.root) } From 229d33682eef59eaca67454e537f4f806e75ca2b Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Wed, 15 May 2019 17:21:14 -0700 Subject: [PATCH 3027/3817] feat(session): instantiated sessions lazily Do not instantiate a bitswap session if all operations are local This commit was moved from ipfs/go-blockservice@4b230aa46c2fff0df0e6be96c99ea1a76aed85a7 --- blockservice/blockservice.go | 3 +- blockservice/blockservice_test.go | 59 +++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 3b5a1df6b..0a442c8a5 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -114,9 +114,8 @@ func (s *blockService) Exchange() exchange.Interface { func NewSession(ctx context.Context, bs BlockService) *Session { exch := bs.Exchange() if sessEx, ok := exch.(exchange.SessionExchange); ok { - ses := sessEx.NewSession(ctx) return &Session{ - ses: ses, + ses: nil, sessEx: sessEx, bs: bs.Blockstore(), } diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index fd64eb60c..4f093d7fc 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -1,6 +1,7 @@ package blockservice import ( + "context" "testing" blocks "github.com/ipfs/go-block-format" @@ -8,6 +9,7 @@ import ( dssync "github.com/ipfs/go-datastore/sync" blockstore "github.com/ipfs/go-ipfs-blockstore" butil "github.com/ipfs/go-ipfs-blocksutil" + exchange "github.com/ipfs/go-ipfs-exchange-interface" offline "github.com/ipfs/go-ipfs-exchange-offline" ) @@ -35,6 +37,52 @@ func TestWriteThroughWorks(t *testing.T) { } } +func TestLazySessionInitialization(t *testing.T) { + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) + bstore2 := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) + bstore3 := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) + session := offline.Exchange(bstore2) + exchange := offline.Exchange(bstore3) + sessionExch := &fakeSessionExchange{Interface: exchange, session: session} + bservSessEx := NewWriteThrough(bstore, sessionExch) + bgen := butil.NewBlockGenerator() + + block := bgen.Next() + bstore.Put(block) + + block2 := bgen.Next() + session.HasBlock(block2) + + bsession := NewSession(ctx, bservSessEx) + if bsession.ses != nil { + t.Fatal("Session exchange should not instantiated session immediately") + } + returnedBlock, err := bsession.GetBlock(ctx, block.Cid()) + if err != nil { + t.Fatal("Should have fetched block locally") + } + if returnedBlock.Cid() != block.Cid() { + t.Fatal("Got incorrect block") + } + if bsession.ses != nil { + t.Fatal("Session exchange should not instantiated session if local store had block") + } + returnedBlock, err = bsession.GetBlock(ctx, block2.Cid()) + if err != nil { + t.Fatal("Should have fetched block remotely") + } + if returnedBlock.Cid() != block2.Cid() { + t.Fatal("Got incorrect block") + } + if bsession.ses != session { + t.Fatal("Should have initialized session to fetch block") + } +} + var _ blockstore.Blockstore = (*PutCountingBlockstore)(nil) type PutCountingBlockstore struct { @@ -46,3 +94,14 @@ func (bs *PutCountingBlockstore) Put(block blocks.Block) error { bs.PutCounter++ return bs.Blockstore.Put(block) } + +var _ exchange.SessionExchange = (*fakeSessionExchange)(nil) + +type fakeSessionExchange struct { + exchange.Interface + session exchange.Fetcher +} + +func (fe *fakeSessionExchange) NewSession(context.Context) exchange.Fetcher { + return fe.session +} From 4509f7cf0aa1c689f9735d4fe64bfc5c6ae7fba6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 16 May 2019 14:38:57 -0700 Subject: [PATCH 3028/3817] chore: remove URL field We now just use the AbsPath field for both URLs and file paths. This commit was moved from ipfs/go-unixfs@dd66a0cfb22ee91cbefc4eb2e13bcd842aa5432b --- unixfs/importer/helpers/dagbuilder.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index a624217f8..e3cf7b44f 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -65,11 +65,6 @@ type DagBuilderParams struct { // NoCopy signals to the chunker that it should track fileinfo for // filestore adds NoCopy bool - - // URL if non-empty (and NoCopy is also true) indicates that the - // file will not be stored in the datastore but instead retrieved - // from this location via the urlstore. - URL string } // New generates a new DagBuilderHelper from the given params and a given @@ -87,10 +82,6 @@ func (dbp *DagBuilderParams) New(spl chunker.Splitter) (*DagBuilderHelper, error db.stat = fi.Stat() } - if dbp.URL != "" && dbp.NoCopy { - db.fullPath = dbp.URL - } - if dbp.NoCopy && db.fullPath == "" { // Enforce NoCopy return nil, ErrMissingFsRef } From 218696c5a3ac4d5f8f2bb8c26de3576972b3519a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 16 May 2019 19:06:08 -0700 Subject: [PATCH 3029/3817] include the path in path errors This should improve UX by telling the user the path we failed to parse. This commit was moved from ipfs/go-path@c13536f13f6030c7d5a8280fc96f1378c5daad7b --- path/error.go | 23 +++++++++++++++++++++++ path/path.go | 32 +++++++++++--------------------- path/path_test.go | 5 +++-- 3 files changed, 37 insertions(+), 23 deletions(-) create mode 100644 path/error.go diff --git a/path/error.go b/path/error.go new file mode 100644 index 000000000..ca2e8416d --- /dev/null +++ b/path/error.go @@ -0,0 +1,23 @@ +package path + +import ( + "fmt" +) + +// helper type so path parsing errors include the path +type pathError struct { + error error + path string +} + +func (e *pathError) Error() string { + return fmt.Sprintf("invalid path %q: %s", e.path, e.error) +} + +func (e *pathError) Unwrap() error { + return e.error +} + +func (e *pathError) Path() string { + return e.path +} diff --git a/path/path.go b/path/path.go index b0d6cdcde..18a85a902 100644 --- a/path/path.go +++ b/path/path.go @@ -2,23 +2,13 @@ package path import ( - "errors" + "fmt" "path" "strings" cid "github.com/ipfs/go-cid" ) -var ( - // ErrBadPath is returned when a given path is incorrectly formatted - ErrBadPath = errors.New("invalid 'ipfs ref' path") - - // ErrNoComponents is used when Paths after a protocol - // do not contain at least one component - ErrNoComponents = errors.New( - "path must contain at least one component") -) - // A Path represents an ipfs content path: // * //path/to/file // * /ipfs/ @@ -107,33 +97,33 @@ func ParsePath(txt string) (Path, error) { // we expect this to start with a hash, and be an 'ipfs' path if parts[0] != "" { if _, err := cid.Decode(parts[0]); err != nil { - return "", ErrBadPath + return "", &pathError{error: err, path: txt} } // The case when the path starts with hash without a protocol prefix return Path("/ipfs/" + txt), nil } if len(parts) < 3 { - return "", ErrBadPath + return "", &pathError{error: fmt.Errorf("path does not begin with '/'"), path: txt} } //TODO: make this smarter switch parts[1] { case "ipfs", "ipld": if parts[2] == "" { - return "", ErrNoComponents + return "", &pathError{error: fmt.Errorf("not enough path components"), path: txt} } // Validate Cid. _, err := cid.Decode(parts[2]) if err != nil { - return "", err + return "", &pathError{error: fmt.Errorf("invalid CID: %s", err), path: txt} } case "ipns": if parts[2] == "" { - return "", ErrNoComponents + return "", &pathError{error: fmt.Errorf("not enough path components"), path: txt} } default: - return "", ErrBadPath + return "", &pathError{error: fmt.Errorf("unknown namespace %q", parts[1]), path: txt} } return Path(txt), nil @@ -142,12 +132,12 @@ func ParsePath(txt string) (Path, error) { // ParseCidToPath takes a CID in string form and returns a valid ipfs Path. func ParseCidToPath(txt string) (Path, error) { if txt == "" { - return "", ErrNoComponents + return "", &pathError{error: fmt.Errorf("empty"), path: txt} } c, err := cid.Decode(txt) if err != nil { - return "", err + return "", &pathError{error: err, path: txt} } return FromCid(c), nil @@ -179,13 +169,13 @@ func SplitAbsPath(fpath Path) (cid.Cid, []string, error) { // if nothing, bail. if len(parts) == 0 { - return cid.Cid{}, nil, ErrNoComponents + return cid.Cid{}, nil, &pathError{error: fmt.Errorf("empty"), path: string(fpath)} } c, err := cid.Decode(parts[0]) // first element in the path is a cid if err != nil { - return cid.Cid{}, nil, err + return cid.Cid{}, nil, &pathError{error: fmt.Errorf("invalid CID: %s", err), path: string(fpath)} } return c, parts[1:], nil diff --git a/path/path_test.go b/path/path_test.go index 657c58c75..fdd71fc0c 100644 --- a/path/path_test.go +++ b/path/path_test.go @@ -1,6 +1,7 @@ package path import ( + "strings" "testing" ) @@ -44,8 +45,8 @@ func TestNoComponents(t *testing.T) { "/ipld/", } { _, err := ParsePath(s) - if err != ErrNoComponents { - t.Errorf("expected ErrNoComponents, got %s", err) + if err == nil || !strings.Contains(err.Error(), "not enough path components") || !strings.Contains(err.Error(), s) { + t.Error("wrong error") } } } From 3d72707f576f881c01bc5fc9ffb97ece3456c014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 17 May 2019 18:57:15 +0200 Subject: [PATCH 3030/3817] tests: expose TestSuite This commit was moved from ipfs/interface-go-ipfs-core@6fe8577a835a24bf8a38de0d5ee9d1fe6ca9e913 --- coreiface/tests/api.go | 22 ++++++++++++---------- coreiface/tests/block.go | 16 ++++++++-------- coreiface/tests/dag.go | 12 ++++++------ coreiface/tests/dht.go | 8 ++++---- coreiface/tests/key.go | 34 +++++++++++++++++----------------- coreiface/tests/name.go | 8 ++++---- coreiface/tests/object.go | 26 +++++++++++++------------- coreiface/tests/path.go | 14 +++++++------- coreiface/tests/pin.go | 8 ++++---- coreiface/tests/pubsub.go | 4 ++-- coreiface/tests/unixfs.go | 26 +++++++++++++------------- 11 files changed, 90 insertions(+), 88 deletions(-) diff --git a/coreiface/tests/api.go b/coreiface/tests/api.go index 5e7c1f541..1af3a83b3 100644 --- a/coreiface/tests/api.go +++ b/coreiface/tests/api.go @@ -11,7 +11,7 @@ import ( var apiNotImplemented = errors.New("api not implemented") -func (tp *provider) makeAPI(ctx context.Context) (coreiface.CoreAPI, error) { +func (tp *TestSuite) makeAPI(ctx context.Context) (coreiface.CoreAPI, error) { api, err := tp.MakeAPISwarm(ctx, false, 1) if err != nil { return nil, err @@ -25,17 +25,19 @@ type Provider interface { MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]coreiface.CoreAPI, error) } -func (tp *provider) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]coreiface.CoreAPI, error) { - tp.apis <- 1 - go func() { - <-ctx.Done() - tp.apis <- -1 - }() +func (tp *TestSuite) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]coreiface.CoreAPI, error) { + if tp.apis != nil { + tp.apis <- 1 + go func() { + <-ctx.Done() + tp.apis <- -1 + }() + } return tp.Provider.MakeAPISwarm(ctx, fullIdentity, n) } -type provider struct { +type TestSuite struct { Provider apis chan int @@ -55,7 +57,7 @@ func TestApi(p Provider) func(t *testing.T) { } }() - tp := &provider{Provider: p, apis: apis} + tp := &TestSuite{Provider: p, apis: apis} return func(t *testing.T) { t.Run("Block", tp.TestBlock) @@ -80,7 +82,7 @@ func TestApi(p Provider) func(t *testing.T) { } } -func (tp *provider) hasApi(t *testing.T, tf func(coreiface.CoreAPI) error) { +func (tp *TestSuite) hasApi(t *testing.T, tf func(coreiface.CoreAPI) error) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 34e47e90c..6b648f394 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -13,7 +13,7 @@ import ( mh "github.com/multiformats/go-multihash" ) -func (tp *provider) TestBlock(t *testing.T) { +func (tp *TestSuite) TestBlock(t *testing.T) { tp.hasApi(t, func(api coreiface.CoreAPI) error { if api.Block() == nil { return apiNotImplemented @@ -30,7 +30,7 @@ func (tp *provider) TestBlock(t *testing.T) { t.Run("TestBlockPin", tp.TestBlockPin) } -func (tp *provider) TestBlockPut(t *testing.T) { +func (tp *TestSuite) TestBlockPut(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -48,7 +48,7 @@ func (tp *provider) TestBlockPut(t *testing.T) { } } -func (tp *provider) TestBlockPutFormat(t *testing.T) { +func (tp *TestSuite) TestBlockPutFormat(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -66,7 +66,7 @@ func (tp *provider) TestBlockPutFormat(t *testing.T) { } } -func (tp *provider) TestBlockPutHash(t *testing.T) { +func (tp *TestSuite) TestBlockPutHash(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -84,7 +84,7 @@ func (tp *provider) TestBlockPutHash(t *testing.T) { } } -func (tp *provider) TestBlockGet(t *testing.T) { +func (tp *TestSuite) TestBlockGet(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -122,7 +122,7 @@ func (tp *provider) TestBlockGet(t *testing.T) { } } -func (tp *provider) TestBlockRm(t *testing.T) { +func (tp *TestSuite) TestBlockRm(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -176,7 +176,7 @@ func (tp *provider) TestBlockRm(t *testing.T) { } } -func (tp *provider) TestBlockStat(t *testing.T) { +func (tp *TestSuite) TestBlockStat(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -203,7 +203,7 @@ func (tp *provider) TestBlockStat(t *testing.T) { } } -func (tp *provider) TestBlockPin(t *testing.T) { +func (tp *TestSuite) TestBlockPin(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index 0bb3aa487..1ccd45d59 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -15,7 +15,7 @@ import ( mh "github.com/multiformats/go-multihash" ) -func (tp *provider) TestDag(t *testing.T) { +func (tp *TestSuite) TestDag(t *testing.T) { tp.hasApi(t, func(api coreiface.CoreAPI) error { if api.Dag() == nil { return apiNotImplemented @@ -40,7 +40,7 @@ var ( } ) -func (tp *provider) TestPut(t *testing.T) { +func (tp *TestSuite) TestPut(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -63,7 +63,7 @@ func (tp *provider) TestPut(t *testing.T) { } } -func (tp *provider) TestPutWithHash(t *testing.T) { +func (tp *TestSuite) TestPutWithHash(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -86,7 +86,7 @@ func (tp *provider) TestPutWithHash(t *testing.T) { } } -func (tp *provider) TestDagPath(t *testing.T) { +func (tp *TestSuite) TestDagPath(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -131,7 +131,7 @@ func (tp *provider) TestDagPath(t *testing.T) { } } -func (tp *provider) TestTree(t *testing.T) { +func (tp *TestSuite) TestTree(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -166,7 +166,7 @@ func (tp *provider) TestTree(t *testing.T) { } } -func (tp *provider) TestBatch(t *testing.T) { +func (tp *TestSuite) TestBatch(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) diff --git a/coreiface/tests/dht.go b/coreiface/tests/dht.go index 5482b50b1..33b4ff14c 100644 --- a/coreiface/tests/dht.go +++ b/coreiface/tests/dht.go @@ -9,7 +9,7 @@ import ( "github.com/ipfs/interface-go-ipfs-core/options" ) -func (tp *provider) TestDht(t *testing.T) { +func (tp *TestSuite) TestDht(t *testing.T) { tp.hasApi(t, func(api iface.CoreAPI) error { if api.Dht() == nil { return apiNotImplemented @@ -22,7 +22,7 @@ func (tp *provider) TestDht(t *testing.T) { t.Run("TestDhtProvide", tp.TestDhtProvide) } -func (tp *provider) TestDhtFindPeer(t *testing.T) { +func (tp *TestSuite) TestDhtFindPeer(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() apis, err := tp.MakeAPISwarm(ctx, true, 5) @@ -75,7 +75,7 @@ func (tp *provider) TestDhtFindPeer(t *testing.T) { } } -func (tp *provider) TestDhtFindProviders(t *testing.T) { +func (tp *TestSuite) TestDhtFindProviders(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() apis, err := tp.MakeAPISwarm(ctx, true, 5) @@ -105,7 +105,7 @@ func (tp *provider) TestDhtFindProviders(t *testing.T) { } } -func (tp *provider) TestDhtProvide(t *testing.T) { +func (tp *TestSuite) TestDhtProvide(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() apis, err := tp.MakeAPISwarm(ctx, true, 5) diff --git a/coreiface/tests/key.go b/coreiface/tests/key.go index 7ff5f3330..e3461f971 100644 --- a/coreiface/tests/key.go +++ b/coreiface/tests/key.go @@ -9,7 +9,7 @@ import ( opt "github.com/ipfs/interface-go-ipfs-core/options" ) -func (tp *provider) TestKey(t *testing.T) { +func (tp *TestSuite) TestKey(t *testing.T) { tp.hasApi(t, func(api iface.CoreAPI) error { if api.Key() == nil { return apiNotImplemented @@ -35,7 +35,7 @@ func (tp *provider) TestKey(t *testing.T) { t.Run("TestRemove", tp.TestRemove) } -func (tp *provider) TestListSelf(t *testing.T) { +func (tp *TestSuite) TestListSelf(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -69,7 +69,7 @@ func (tp *provider) TestListSelf(t *testing.T) { } } -func (tp *provider) TestRenameSelf(t *testing.T) { +func (tp *TestSuite) TestRenameSelf(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -97,7 +97,7 @@ func (tp *provider) TestRenameSelf(t *testing.T) { } } -func (tp *provider) TestRemoveSelf(t *testing.T) { +func (tp *TestSuite) TestRemoveSelf(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -116,7 +116,7 @@ func (tp *provider) TestRemoveSelf(t *testing.T) { } } -func (tp *provider) TestGenerate(t *testing.T) { +func (tp *TestSuite) TestGenerate(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -139,7 +139,7 @@ func (tp *provider) TestGenerate(t *testing.T) { } } -func (tp *provider) TestGenerateSize(t *testing.T) { +func (tp *TestSuite) TestGenerateSize(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -162,7 +162,7 @@ func (tp *provider) TestGenerateSize(t *testing.T) { } } -func (tp *provider) TestGenerateType(t *testing.T) { +func (tp *TestSuite) TestGenerateType(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() t.Skip("disabled until libp2p/specs#111 is fixed") @@ -188,7 +188,7 @@ func (tp *provider) TestGenerateType(t *testing.T) { } } -func (tp *provider) TestGenerateExisting(t *testing.T) { +func (tp *TestSuite) TestGenerateExisting(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -221,7 +221,7 @@ func (tp *provider) TestGenerateExisting(t *testing.T) { } } -func (tp *provider) TestList(t *testing.T) { +func (tp *TestSuite) TestList(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -267,7 +267,7 @@ func (tp *provider) TestList(t *testing.T) { } } -func (tp *provider) TestRename(t *testing.T) { +func (tp *TestSuite) TestRename(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -296,7 +296,7 @@ func (tp *provider) TestRename(t *testing.T) { } } -func (tp *provider) TestRenameToSelf(t *testing.T) { +func (tp *TestSuite) TestRenameToSelf(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -320,7 +320,7 @@ func (tp *provider) TestRenameToSelf(t *testing.T) { } } -func (tp *provider) TestRenameToSelfForce(t *testing.T) { +func (tp *TestSuite) TestRenameToSelfForce(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -344,7 +344,7 @@ func (tp *provider) TestRenameToSelfForce(t *testing.T) { } } -func (tp *provider) TestRenameOverwriteNoForce(t *testing.T) { +func (tp *TestSuite) TestRenameOverwriteNoForce(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -374,7 +374,7 @@ func (tp *provider) TestRenameOverwriteNoForce(t *testing.T) { } } -func (tp *provider) TestRenameOverwrite(t *testing.T) { +func (tp *TestSuite) TestRenameOverwrite(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -413,7 +413,7 @@ func (tp *provider) TestRenameOverwrite(t *testing.T) { } } -func (tp *provider) TestRenameSameNameNoForce(t *testing.T) { +func (tp *TestSuite) TestRenameSameNameNoForce(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -442,7 +442,7 @@ func (tp *provider) TestRenameSameNameNoForce(t *testing.T) { } } -func (tp *provider) TestRenameSameName(t *testing.T) { +func (tp *TestSuite) TestRenameSameName(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -471,7 +471,7 @@ func (tp *provider) TestRenameSameName(t *testing.T) { } } -func (tp *provider) TestRemove(t *testing.T) { +func (tp *TestSuite) TestRemove(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index efaf1d3ae..31a5c1466 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -16,7 +16,7 @@ import ( opt "github.com/ipfs/interface-go-ipfs-core/options" ) -func (tp *provider) TestName(t *testing.T) { +func (tp *TestSuite) TestName(t *testing.T) { tp.hasApi(t, func(api coreiface.CoreAPI) error { if api.Name() == nil { return apiNotImplemented @@ -39,7 +39,7 @@ func appendPath(p path.Path, sub string) path.Path { return path.New(gopath.Join(p.String(), sub)) } -func (tp *provider) TestPublishResolve(t *testing.T) { +func (tp *TestSuite) TestPublishResolve(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() init := func() (coreiface.CoreAPI, path.Path) { @@ -188,7 +188,7 @@ func (tp *provider) TestPublishResolve(t *testing.T) { }) } -func (tp *provider) TestBasicPublishResolveKey(t *testing.T) { +func (tp *TestSuite) TestBasicPublishResolveKey(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() apis, err := tp.MakeAPISwarm(ctx, true, 5) @@ -230,7 +230,7 @@ func (tp *provider) TestBasicPublishResolveKey(t *testing.T) { } } -func (tp *provider) TestBasicPublishResolveTimeout(t *testing.T) { +func (tp *TestSuite) TestBasicPublishResolveTimeout(t *testing.T) { t.Skip("ValidTime doesn't appear to work at this time resolution") ctx, cancel := context.WithCancel(context.Background()) diff --git a/coreiface/tests/object.go b/coreiface/tests/object.go index 8682a2edc..2e066ca71 100644 --- a/coreiface/tests/object.go +++ b/coreiface/tests/object.go @@ -12,7 +12,7 @@ import ( opt "github.com/ipfs/interface-go-ipfs-core/options" ) -func (tp *provider) TestObject(t *testing.T) { +func (tp *TestSuite) TestObject(t *testing.T) { tp.hasApi(t, func(api iface.CoreAPI) error { if api.Object() == nil { return apiNotImplemented @@ -34,7 +34,7 @@ func (tp *provider) TestObject(t *testing.T) { t.Run("TestDiffTest", tp.TestDiffTest) } -func (tp *provider) TestNew(t *testing.T) { +func (tp *TestSuite) TestNew(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -61,7 +61,7 @@ func (tp *provider) TestNew(t *testing.T) { } } -func (tp *provider) TestObjectPut(t *testing.T) { +func (tp *TestSuite) TestObjectPut(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -102,7 +102,7 @@ func (tp *provider) TestObjectPut(t *testing.T) { } } -func (tp *provider) TestObjectGet(t *testing.T) { +func (tp *TestSuite) TestObjectGet(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -125,7 +125,7 @@ func (tp *provider) TestObjectGet(t *testing.T) { } } -func (tp *provider) TestObjectData(t *testing.T) { +func (tp *TestSuite) TestObjectData(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -153,7 +153,7 @@ func (tp *provider) TestObjectData(t *testing.T) { } } -func (tp *provider) TestObjectLinks(t *testing.T) { +func (tp *TestSuite) TestObjectLinks(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -189,7 +189,7 @@ func (tp *provider) TestObjectLinks(t *testing.T) { } } -func (tp *provider) TestObjectStat(t *testing.T) { +func (tp *TestSuite) TestObjectStat(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -237,7 +237,7 @@ func (tp *provider) TestObjectStat(t *testing.T) { } } -func (tp *provider) TestObjectAddLink(t *testing.T) { +func (tp *TestSuite) TestObjectAddLink(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -278,7 +278,7 @@ func (tp *provider) TestObjectAddLink(t *testing.T) { } } -func (tp *provider) TestObjectAddLinkCreate(t *testing.T) { +func (tp *TestSuite) TestObjectAddLinkCreate(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -327,7 +327,7 @@ func (tp *provider) TestObjectAddLinkCreate(t *testing.T) { } } -func (tp *provider) TestObjectRmLink(t *testing.T) { +func (tp *TestSuite) TestObjectRmLink(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -360,7 +360,7 @@ func (tp *provider) TestObjectRmLink(t *testing.T) { } } -func (tp *provider) TestObjectAddData(t *testing.T) { +func (tp *TestSuite) TestObjectAddData(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -393,7 +393,7 @@ func (tp *provider) TestObjectAddData(t *testing.T) { } } -func (tp *provider) TestObjectSetData(t *testing.T) { +func (tp *TestSuite) TestObjectSetData(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -426,7 +426,7 @@ func (tp *provider) TestObjectSetData(t *testing.T) { } } -func (tp *provider) TestDiffTest(t *testing.T) { +func (tp *TestSuite) TestDiffTest(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index 4fd18bd20..2d9497244 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -12,7 +12,7 @@ import ( ipldcbor "github.com/ipfs/go-ipld-cbor" ) -func (tp *provider) TestPath(t *testing.T) { +func (tp *TestSuite) TestPath(t *testing.T) { t.Run("TestMutablePath", tp.TestMutablePath) t.Run("TestPathRemainder", tp.TestPathRemainder) t.Run("TestEmptyPathRemainder", tp.TestEmptyPathRemainder) @@ -21,7 +21,7 @@ func (tp *provider) TestPath(t *testing.T) { t.Run("TestPathJoin", tp.TestPathJoin) } -func (tp *provider) TestMutablePath(t *testing.T) { +func (tp *TestSuite) TestMutablePath(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -54,7 +54,7 @@ func (tp *provider) TestMutablePath(t *testing.T) { } } -func (tp *provider) TestPathRemainder(t *testing.T) { +func (tp *TestSuite) TestPathRemainder(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -85,7 +85,7 @@ func (tp *provider) TestPathRemainder(t *testing.T) { } } -func (tp *provider) TestEmptyPathRemainder(t *testing.T) { +func (tp *TestSuite) TestEmptyPathRemainder(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -116,7 +116,7 @@ func (tp *provider) TestEmptyPathRemainder(t *testing.T) { } } -func (tp *provider) TestInvalidPathRemainder(t *testing.T) { +func (tp *TestSuite) TestInvalidPathRemainder(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -143,7 +143,7 @@ func (tp *provider) TestInvalidPathRemainder(t *testing.T) { } } -func (tp *provider) TestPathRoot(t *testing.T) { +func (tp *TestSuite) TestPathRoot(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -187,7 +187,7 @@ func (tp *provider) TestPathRoot(t *testing.T) { } } -func (tp *provider) TestPathJoin(t *testing.T) { +func (tp *TestSuite) TestPathJoin(t *testing.T) { p1 := path.New("/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz") if path.Join(p1, "foo").String() != "/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz/foo" { diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index 344db65e2..9b28a682a 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -14,7 +14,7 @@ import ( ipld "github.com/ipfs/go-ipld-format" ) -func (tp *provider) TestPin(t *testing.T) { +func (tp *TestSuite) TestPin(t *testing.T) { tp.hasApi(t, func(api iface.CoreAPI) error { if api.Pin() == nil { return apiNotImplemented @@ -27,7 +27,7 @@ func (tp *provider) TestPin(t *testing.T) { t.Run("TestPinRecursive", tp.TestPinRecursive) } -func (tp *provider) TestPinAdd(t *testing.T) { +func (tp *TestSuite) TestPinAdd(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -46,7 +46,7 @@ func (tp *provider) TestPinAdd(t *testing.T) { } } -func (tp *provider) TestPinSimple(t *testing.T) { +func (tp *TestSuite) TestPinSimple(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -96,7 +96,7 @@ func (tp *provider) TestPinSimple(t *testing.T) { } } -func (tp *provider) TestPinRecursive(t *testing.T) { +func (tp *TestSuite) TestPinRecursive(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) diff --git a/coreiface/tests/pubsub.go b/coreiface/tests/pubsub.go index 418fc4867..e66291572 100644 --- a/coreiface/tests/pubsub.go +++ b/coreiface/tests/pubsub.go @@ -9,7 +9,7 @@ import ( "github.com/ipfs/interface-go-ipfs-core/options" ) -func (tp *provider) TestPubSub(t *testing.T) { +func (tp *TestSuite) TestPubSub(t *testing.T) { tp.hasApi(t, func(api iface.CoreAPI) error { if api.PubSub() == nil { return apiNotImplemented @@ -20,7 +20,7 @@ func (tp *provider) TestPubSub(t *testing.T) { t.Run("TestBasicPubSub", tp.TestBasicPubSub) } -func (tp *provider) TestBasicPubSub(t *testing.T) { +func (tp *TestSuite) TestBasicPubSub(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index c810167e8..47ce505c8 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -28,7 +28,7 @@ import ( mh "github.com/multiformats/go-multihash" ) -func (tp *provider) TestUnixfs(t *testing.T) { +func (tp *TestSuite) TestUnixfs(t *testing.T) { tp.hasApi(t, func(api coreiface.CoreAPI) error { if api.Unixfs() == nil { return apiNotImplemented @@ -94,7 +94,7 @@ func wrapped(names ...string) func(f files.Node) files.Node { } } -func (tp *provider) TestAdd(t *testing.T) { +func (tp *TestSuite) TestAdd(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -528,7 +528,7 @@ func (tp *provider) TestAdd(t *testing.T) { } } -func (tp *provider) TestAddPinned(t *testing.T) { +func (tp *TestSuite) TestAddPinned(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -554,7 +554,7 @@ func (tp *provider) TestAddPinned(t *testing.T) { } } -func (tp *provider) TestAddHashOnly(t *testing.T) { +func (tp *TestSuite) TestAddHashOnly(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -580,7 +580,7 @@ func (tp *provider) TestAddHashOnly(t *testing.T) { } } -func (tp *provider) TestGetEmptyFile(t *testing.T) { +func (tp *TestSuite) TestGetEmptyFile(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -610,7 +610,7 @@ func (tp *provider) TestGetEmptyFile(t *testing.T) { } } -func (tp *provider) TestGetDir(t *testing.T) { +func (tp *TestSuite) TestGetDir(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -643,7 +643,7 @@ func (tp *provider) TestGetDir(t *testing.T) { } } -func (tp *provider) TestGetNonUnixfs(t *testing.T) { +func (tp *TestSuite) TestGetNonUnixfs(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -663,7 +663,7 @@ func (tp *provider) TestGetNonUnixfs(t *testing.T) { } } -func (tp *provider) TestLs(t *testing.T) { +func (tp *TestSuite) TestLs(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -723,7 +723,7 @@ func (tp *provider) TestLs(t *testing.T) { } } -func (tp *provider) TestEntriesExpired(t *testing.T) { +func (tp *TestSuite) TestEntriesExpired(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -765,7 +765,7 @@ func (tp *provider) TestEntriesExpired(t *testing.T) { } } -func (tp *provider) TestLsEmptyDir(t *testing.T) { +func (tp *TestSuite) TestLsEmptyDir(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -794,7 +794,7 @@ func (tp *provider) TestLsEmptyDir(t *testing.T) { } // TODO(lgierth) this should test properly, with len(links) > 0 -func (tp *provider) TestLsNonUnixfs(t *testing.T) { +func (tp *TestSuite) TestLsNonUnixfs(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -853,7 +853,7 @@ func (f *closeTestF) Close() error { return nil } -func (tp *provider) TestAddCloses(t *testing.T) { +func (tp *TestSuite) TestAddCloses(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) @@ -891,7 +891,7 @@ func (tp *provider) TestAddCloses(t *testing.T) { } } -func (tp *provider) TestGetSeek(t *testing.T) { +func (tp *TestSuite) TestGetSeek(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() api, err := tp.makeAPI(ctx) From 152af791452b6d1b03e9b76c5e2dac1213e182fb Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 16 May 2019 22:57:32 -0700 Subject: [PATCH 3031/3817] feat: improve errors when a path fails to parse This helps with issues like #4190 by telling the user the path that failed to parse. fixes #4190 This commit was moved from ipfs/go-namesys@b65e2dbe722dab7d559ee52e453c2d8fb1c29727 --- namesys/resolve/resolve.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/namesys/resolve/resolve.go b/namesys/resolve/resolve.go index 128619c65..44f735a1f 100644 --- a/namesys/resolve/resolve.go +++ b/namesys/resolve/resolve.go @@ -3,6 +3,7 @@ package resolve import ( "context" "errors" + "fmt" "strings" "github.com/ipfs/go-ipld-format" @@ -36,8 +37,9 @@ func ResolveIPNS(ctx context.Context, nsys namesys.NameSystem, p path.Path) (pat seg := p.Segments() if len(seg) < 2 || seg[1] == "" { // just "/" without further segments - evt.Append(logging.LoggableMap{"error": path.ErrNoComponents.Error()}) - return "", path.ErrNoComponents + err := fmt.Errorf("invalid path %q: ipns path missing IPNS ID", p) + evt.Append(logging.LoggableMap{"error": err}) + return "", err } extensions := seg[2:] From cda470a4223903b7e22946736655f10dfc03ed41 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 21 May 2019 10:22:58 -0700 Subject: [PATCH 3032/3817] chore: fix linter nits and tests that don't compile This commit was moved from ipfs/go-namesys@6a45588750d568d5dd2dec274dadd86c12bcb5a3 --- namesys/resolve/pathresolver_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/namesys/resolve/pathresolver_test.go b/namesys/resolve/pathresolver_test.go index fe578b5d3..3d9c04fa2 100644 --- a/namesys/resolve/pathresolver_test.go +++ b/namesys/resolve/pathresolver_test.go @@ -16,17 +16,17 @@ func TestResolveNoComponents(t *testing.T) { } _, err = resolve.Resolve(n.Context(), n.Namesys, n.Resolver, path.Path("/ipns/")) - if err != path.ErrNoComponents { - t.Fatal("Should error with no components (/ipns/).", err) + if err.Error() != "invalid path \"/ipns/\": ipns path missing IPNS ID" { + t.Error("Should error with no components (/ipns/).", err) } _, err = resolve.Resolve(n.Context(), n.Namesys, n.Resolver, path.Path("/ipfs/")) - if err != path.ErrNoComponents { - t.Fatal("Should error with no components (/ipfs/).", err) + if err.Error() != "invalid path \"/ipfs/\": not enough path components" { + t.Error("Should error with no components (/ipfs/).", err) } _, err = resolve.Resolve(n.Context(), n.Namesys, n.Resolver, path.Path("/../..")) - if err != path.ErrBadPath { - t.Fatal("Should error with invalid path.", err) + if err.Error() != "invalid path \"/../..\": unknown namespace \"..\"" { + t.Error("Should error with invalid path.", err) } } From ff7a1034b726238764149e59eca90838cf93749b Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 24 May 2019 20:44:39 +0200 Subject: [PATCH 3033/3817] Fix travis CI link in README This commit was moved from ipfs/go-blockservice@b46b687fa3e5c09875391efb5bae7e01f7e9c941 --- blockservice/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/README.md b/blockservice/README.md index 0ec2aef87..3df67fdec 100644 --- a/blockservice/README.md +++ b/blockservice/README.md @@ -5,7 +5,7 @@ go-blockservice [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) [![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) [![Coverage Status](https://codecov.io/gh/ipfs/go-block-format/branch/master/graph/badge.svg)](https://codecov.io/gh/ipfs/go-block-format/branch/master) -[![Travis CI](https://travis-ci.org/ipfs/go-block-format.svg?branch=master)](https://travis-ci.org/ipfs/go-block-format) +[![Travis CI](https://travis-ci.com/ipfs/go-blockservice.svg?branch=master)](https://travis-ci.com/ipfs/go-blockservice) > go-blockservice provides a seamless interface to both local and remote storage backends. From 41486008be5e0d01f6e132a2b5ee59b1063be146 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 24 May 2019 11:59:30 -0700 Subject: [PATCH 3034/3817] set the session context fixes https://github.com/ipfs/go-bitswap/issues/131 This commit was moved from ipfs/go-blockservice@6013394c0f5b4df756a927018c530a3231fb7197 --- blockservice/blockservice.go | 12 +++++++----- blockservice/blockservice_test.go | 5 ++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 0a442c8a5..845506534 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -115,14 +115,16 @@ func NewSession(ctx context.Context, bs BlockService) *Session { exch := bs.Exchange() if sessEx, ok := exch.(exchange.SessionExchange); ok { return &Session{ - ses: nil, - sessEx: sessEx, - bs: bs.Blockstore(), + sessCtx: ctx, + ses: nil, + sessEx: sessEx, + bs: bs.Blockstore(), } } return &Session{ - ses: exch, - bs: bs.Blockstore(), + ses: exch, + sessCtx: ctx, + bs: bs.Blockstore(), } } diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index 4f093d7fc..a94b672cf 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -102,6 +102,9 @@ type fakeSessionExchange struct { session exchange.Fetcher } -func (fe *fakeSessionExchange) NewSession(context.Context) exchange.Fetcher { +func (fe *fakeSessionExchange) NewSession(ctx context.Context) exchange.Fetcher { + if ctx == nil { + panic("nil context") + } return fe.session } From 6bc65525abe5a9f425bbb22f40e915e4f4b27cc5 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 24 May 2019 21:23:39 +0200 Subject: [PATCH 3035/3817] Fix tests Method was renamed This commit was moved from ipfs/go-blockservice@fa6461310b2de7f0e5b7933d0bab28b127a08158 --- blockservice/test/mock.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index 9638f3cb3..a6eba6911 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -3,7 +3,7 @@ package bstest import ( . "github.com/ipfs/go-blockservice" - bitswap "github.com/ipfs/go-bitswap" + testinstance "github.com/ipfs/go-bitswap/testinstance" tn "github.com/ipfs/go-bitswap/testnet" delay "github.com/ipfs/go-ipfs-delay" mockrouting "github.com/ipfs/go-ipfs-routing/mock" @@ -12,7 +12,7 @@ import ( // Mocks returns |n| connected mock Blockservices func Mocks(n int) []BlockService { net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(0)) - sg := bitswap.NewTestSessionGenerator(net) + sg := testinstance.NewTestInstanceGenerator(net) instances := sg.Instances(n) From fc50e9a9b642135cf46602d2b695a81d59b4aec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Tue, 28 May 2019 16:42:41 +0100 Subject: [PATCH 3036/3817] migrate to go-libp2p-core. This commit was moved from ipfs/go-ipfs-routing@f5e0203fa1b87188bfe03a6ff3915e05a8afe5de --- routing/mock/centralized_client.go | 32 ++++++++++++++-------------- routing/mock/centralized_server.go | 22 +++++++++---------- routing/mock/centralized_test.go | 21 +++++++++--------- routing/mock/interface.go | 17 ++++++++------- routing/none/none_client.go | 30 +++++++++++++------------- routing/offline/offline.go | 34 +++++++++++++++--------------- routing/offline/offline_test.go | 12 ++++++----- 7 files changed, 86 insertions(+), 82 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index e09350da5..e57d03239 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -6,11 +6,11 @@ import ( cid "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log" - peer "github.com/libp2p/go-libp2p-peer" - pstore "github.com/libp2p/go-libp2p-peerstore" - routing "github.com/libp2p/go-libp2p-routing" - ropts "github.com/libp2p/go-libp2p-routing/options" - "github.com/libp2p/go-testutil" + + "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p-core/routing" + "github.com/libp2p/go-libp2p-testing/net" + ma "github.com/multiformats/go-multiaddr" ) @@ -19,37 +19,37 @@ var log = logging.Logger("mockrouter") type client struct { vs routing.ValueStore server server - peer testutil.Identity + peer tnet.Identity } // FIXME(brian): is this method meant to simulate putting a value into the network? -func (c *client) PutValue(ctx context.Context, key string, val []byte, opts ...ropts.Option) error { +func (c *client) PutValue(ctx context.Context, key string, val []byte, opts ...routing.Option) error { log.Debugf("PutValue: %s", key) return c.vs.PutValue(ctx, key, val, opts...) } // FIXME(brian): is this method meant to simulate getting a value from the network? -func (c *client) GetValue(ctx context.Context, key string, opts ...ropts.Option) ([]byte, error) { +func (c *client) GetValue(ctx context.Context, key string, opts ...routing.Option) ([]byte, error) { log.Debugf("GetValue: %s", key) return c.vs.GetValue(ctx, key, opts...) } -func (c *client) SearchValue(ctx context.Context, key string, opts ...ropts.Option) (<-chan []byte, error) { +func (c *client) SearchValue(ctx context.Context, key string, opts ...routing.Option) (<-chan []byte, error) { log.Debugf("SearchValue: %s", key) return c.vs.SearchValue(ctx, key, opts...) } -func (c *client) FindProviders(ctx context.Context, key cid.Cid) ([]pstore.PeerInfo, error) { +func (c *client) FindProviders(ctx context.Context, key cid.Cid) ([]peer.AddrInfo, error) { return c.server.Providers(key), nil } -func (c *client) FindPeer(ctx context.Context, pid peer.ID) (pstore.PeerInfo, error) { +func (c *client) FindPeer(ctx context.Context, pid peer.ID) (peer.AddrInfo, error) { log.Debugf("FindPeer: %s", pid) - return pstore.PeerInfo{}, nil + return peer.AddrInfo{}, nil } -func (c *client) FindProvidersAsync(ctx context.Context, k cid.Cid, max int) <-chan pstore.PeerInfo { - out := make(chan pstore.PeerInfo) +func (c *client) FindProvidersAsync(ctx context.Context, k cid.Cid, max int) <-chan peer.AddrInfo { + out := make(chan peer.AddrInfo) go func() { defer close(out) for i, p := range c.server.Providers(k) { @@ -72,7 +72,7 @@ func (c *client) Provide(_ context.Context, key cid.Cid, brd bool) error { if !brd { return nil } - info := pstore.PeerInfo{ + info := peer.AddrInfo{ ID: c.peer.ID(), Addrs: []ma.Multiaddr{c.peer.Address()}, } @@ -87,4 +87,4 @@ func (c *client) Bootstrap(context.Context) error { return nil } -var _ routing.IpfsRouting = &client{} +var _ routing.Routing = &client{} diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index a223f911b..9c8bd853c 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -9,17 +9,17 @@ import ( cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" - peer "github.com/libp2p/go-libp2p-peer" - pstore "github.com/libp2p/go-libp2p-peerstore" - "github.com/libp2p/go-testutil" + + "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p-testing/net" offline "github.com/ipfs/go-ipfs-routing/offline" ) // server is the mockrouting.Client's private interface to the routing server type server interface { - Announce(pstore.PeerInfo, cid.Cid) error - Providers(cid.Cid) []pstore.PeerInfo + Announce(peer.AddrInfo, cid.Cid) error + Providers(cid.Cid) []peer.AddrInfo Server } @@ -33,11 +33,11 @@ type s struct { } type providerRecord struct { - Peer pstore.PeerInfo + Peer peer.AddrInfo Created time.Time } -func (rs *s) Announce(p pstore.PeerInfo, c cid.Cid) error { +func (rs *s) Announce(p peer.AddrInfo, c cid.Cid) error { rs.lock.Lock() defer rs.lock.Unlock() @@ -54,14 +54,14 @@ func (rs *s) Announce(p pstore.PeerInfo, c cid.Cid) error { return nil } -func (rs *s) Providers(c cid.Cid) []pstore.PeerInfo { +func (rs *s) Providers(c cid.Cid) []peer.AddrInfo { rs.delayConf.Query.Wait() // before locking rs.lock.RLock() defer rs.lock.RUnlock() k := c.KeyString() - var ret []pstore.PeerInfo + var ret []peer.AddrInfo records, ok := rs.providers[k] if !ok { return ret @@ -80,11 +80,11 @@ func (rs *s) Providers(c cid.Cid) []pstore.PeerInfo { return ret } -func (rs *s) Client(p testutil.Identity) Client { +func (rs *s) Client(p tnet.Identity) Client { return rs.ClientWithDatastore(context.Background(), p, dssync.MutexWrap(ds.NewMapDatastore())) } -func (rs *s) ClientWithDatastore(_ context.Context, p testutil.Identity, datastore ds.Datastore) Client { +func (rs *s) ClientWithDatastore(_ context.Context, p tnet.Identity, datastore ds.Datastore) Client { return &client{ peer: p, vs: offline.NewOfflineRouter(datastore, MockValidator{}), diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 704557a66..2767ff1a2 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,13 +8,14 @@ import ( cid "github.com/ipfs/go-cid" delay "github.com/ipfs/go-ipfs-delay" u "github.com/ipfs/go-ipfs-util" - pstore "github.com/libp2p/go-libp2p-peerstore" - testutil "github.com/libp2p/go-testutil" + + "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p-testing/net" ) func TestKeyNotFound(t *testing.T) { - var pi = testutil.RandIdentityOrFatal(t) + var pi = tnet.RandIdentityOrFatal(t) var key = cid.NewCidV0(u.Hash([]byte("mock key"))) var ctx = context.Background() @@ -27,7 +28,7 @@ func TestKeyNotFound(t *testing.T) { } func TestClientFindProviders(t *testing.T) { - pi := testutil.RandIdentityOrFatal(t) + pi := tnet.RandIdentityOrFatal(t) rs := NewServer() client := rs.Client(pi) @@ -58,7 +59,7 @@ func TestClientOverMax(t *testing.T) { k := cid.NewCidV0(u.Hash([]byte("hello"))) numProvidersForHelloKey := 100 for i := 0; i < numProvidersForHelloKey; i++ { - pi := testutil.RandIdentityOrFatal(t) + pi := tnet.RandIdentityOrFatal(t) err := rs.Client(pi).Provide(context.Background(), k, true) if err != nil { t.Fatal(err) @@ -66,7 +67,7 @@ func TestClientOverMax(t *testing.T) { } max := 10 - pi := testutil.RandIdentityOrFatal(t) + pi := tnet.RandIdentityOrFatal(t) client := rs.Client(pi) providersFromClient := client.FindProvidersAsync(context.Background(), k, max) @@ -101,7 +102,7 @@ func TestCanceledContext(t *testing.T) { default: } - pi, err := testutil.RandIdentity() + pi, err := tnet.RandIdentity() if err != nil { t.Error(err) } @@ -113,7 +114,7 @@ func TestCanceledContext(t *testing.T) { } }() - local := testutil.RandIdentityOrFatal(t) + local := tnet.RandIdentityOrFatal(t) client := rs.Client(local) t.Log("warning: max is finite so this test is non-deterministic") @@ -141,7 +142,7 @@ func TestValidAfter(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - pi := testutil.RandIdentityOrFatal(t) + pi := tnet.RandIdentityOrFatal(t) key := cid.NewCidV0(u.Hash([]byte("mock key"))) conf := DelayConfig{ ValueVisibility: delay.Fixed(1 * time.Hour), @@ -152,7 +153,7 @@ func TestValidAfter(t *testing.T) { rs.Client(pi).Provide(ctx, key, true) - var providers []pstore.PeerInfo + var providers []peer.AddrInfo max := 100 providersChan := rs.Client(pi).FindProvidersAsync(ctx, key, max) for p := range providersChan { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 5d4e9f9aa..6b0206534 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -1,6 +1,6 @@ // Package mockrouting provides a virtual routing server. To use it, // create a virtual routing server and use the Client() method to get a -// routing client (IpfsRouting). The server quacks like a DHT but is +// routing client (Routing). The server quacks like a DHT but is // really a local in-memory hash table. package mockrouting @@ -9,9 +9,10 @@ import ( ds "github.com/ipfs/go-datastore" delay "github.com/ipfs/go-ipfs-delay" - peer "github.com/libp2p/go-libp2p-peer" - routing "github.com/libp2p/go-libp2p-routing" - "github.com/libp2p/go-testutil" + + "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p-core/routing" + "github.com/libp2p/go-libp2p-testing/net" ) // MockValidator is a record validator that always returns success. @@ -22,13 +23,13 @@ func (MockValidator) Select(_ string, _ [][]byte) (int, error) { return 0, nil } // Server provides mockrouting Clients type Server interface { - Client(p testutil.Identity) Client - ClientWithDatastore(context.Context, testutil.Identity, ds.Datastore) Client + Client(p tnet.Identity) Client + ClientWithDatastore(context.Context, tnet.Identity, ds.Datastore) Client } -// Client implements IpfsRouting +// Client implements Routing type Client interface { - routing.IpfsRouting + routing.Routing } // NewServer returns a mockrouting Server diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 45febc554..9604ab07c 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -7,35 +7,35 @@ import ( cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" - p2phost "github.com/libp2p/go-libp2p-host" - peer "github.com/libp2p/go-libp2p-peer" - pstore "github.com/libp2p/go-libp2p-peerstore" + + "github.com/libp2p/go-libp2p-core/host" + "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p-core/routing" + record "github.com/libp2p/go-libp2p-record" - routing "github.com/libp2p/go-libp2p-routing" - ropts "github.com/libp2p/go-libp2p-routing/options" ) type nilclient struct { } -func (c *nilclient) PutValue(_ context.Context, _ string, _ []byte, _ ...ropts.Option) error { +func (c *nilclient) PutValue(_ context.Context, _ string, _ []byte, _ ...routing.Option) error { return nil } -func (c *nilclient) GetValue(_ context.Context, _ string, _ ...ropts.Option) ([]byte, error) { +func (c *nilclient) GetValue(_ context.Context, _ string, _ ...routing.Option) ([]byte, error) { return nil, errors.New("tried GetValue from nil routing") } -func (c *nilclient) SearchValue(_ context.Context, _ string, _ ...ropts.Option) (<-chan []byte, error) { +func (c *nilclient) SearchValue(_ context.Context, _ string, _ ...routing.Option) (<-chan []byte, error) { return nil, errors.New("tried SearchValue from nil routing") } -func (c *nilclient) FindPeer(_ context.Context, _ peer.ID) (pstore.PeerInfo, error) { - return pstore.PeerInfo{}, nil +func (c *nilclient) FindPeer(_ context.Context, _ peer.ID) (peer.AddrInfo, error) { + return peer.AddrInfo{}, nil } -func (c *nilclient) FindProvidersAsync(_ context.Context, _ cid.Cid, _ int) <-chan pstore.PeerInfo { - out := make(chan pstore.PeerInfo) +func (c *nilclient) FindProvidersAsync(_ context.Context, _ cid.Cid, _ int) <-chan peer.AddrInfo { + out := make(chan peer.AddrInfo) defer close(out) return out } @@ -48,10 +48,10 @@ func (c *nilclient) Bootstrap(_ context.Context) error { return nil } -// ConstructNilRouting creates an IpfsRouting client which does nothing. -func ConstructNilRouting(_ context.Context, _ p2phost.Host, _ ds.Batching, _ record.Validator) (routing.IpfsRouting, error) { +// ConstructNilRouting creates an Routing client which does nothing. +func ConstructNilRouting(_ context.Context, _ host.Host, _ ds.Batching, _ record.Validator) (routing.Routing, error) { return &nilclient{}, nil } // ensure nilclient satisfies interface -var _ routing.IpfsRouting = &nilclient{} +var _ routing.Routing = &nilclient{} diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 1627490c2..c76f92098 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -1,4 +1,4 @@ -// Package offline implements IpfsRouting with a client which +// Package offline implements Routing with a client which // is only able to perform offline operations. package offline @@ -12,29 +12,29 @@ import ( cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dshelp "github.com/ipfs/go-ipfs-ds-help" - "github.com/libp2p/go-libp2p-peer" - pstore "github.com/libp2p/go-libp2p-peerstore" + + "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p-core/routing" + record "github.com/libp2p/go-libp2p-record" pb "github.com/libp2p/go-libp2p-record/pb" - routing "github.com/libp2p/go-libp2p-routing" - ropts "github.com/libp2p/go-libp2p-routing/options" ) // ErrOffline is returned when trying to perform operations that // require connectivity. var ErrOffline = errors.New("routing system in offline mode") -// NewOfflineRouter returns an IpfsRouting implementation which only performs +// NewOfflineRouter returns an Routing implementation which only performs // offline operations. It allows to Put and Get signed dht // records to and from the local datastore. -func NewOfflineRouter(dstore ds.Datastore, validator record.Validator) routing.IpfsRouting { +func NewOfflineRouter(dstore ds.Datastore, validator record.Validator) routing.Routing { return &offlineRouting{ datastore: dstore, validator: validator, } } -// offlineRouting implements the IpfsRouting interface, +// offlineRouting implements the Routing interface, // but only provides the capability to Put and Get signed dht // records to and from the local datastore. type offlineRouting struct { @@ -42,7 +42,7 @@ type offlineRouting struct { validator record.Validator } -func (c *offlineRouting) PutValue(ctx context.Context, key string, val []byte, _ ...ropts.Option) error { +func (c *offlineRouting) PutValue(ctx context.Context, key string, val []byte, _ ...routing.Option) error { if err := c.validator.Validate(key, val); err != nil { return err } @@ -70,7 +70,7 @@ func (c *offlineRouting) PutValue(ctx context.Context, key string, val []byte, _ return c.datastore.Put(dshelp.NewKeyFromBinary([]byte(key)), data) } -func (c *offlineRouting) GetValue(ctx context.Context, key string, _ ...ropts.Option) ([]byte, error) { +func (c *offlineRouting) GetValue(ctx context.Context, key string, _ ...routing.Option) ([]byte, error) { buf, err := c.datastore.Get(dshelp.NewKeyFromBinary([]byte(key))) if err != nil { return nil, err @@ -90,7 +90,7 @@ func (c *offlineRouting) GetValue(ctx context.Context, key string, _ ...ropts.Op return val, nil } -func (c *offlineRouting) SearchValue(ctx context.Context, key string, _ ...ropts.Option) (<-chan []byte, error) { +func (c *offlineRouting) SearchValue(ctx context.Context, key string, _ ...routing.Option) (<-chan []byte, error) { out := make(chan []byte, 1) go func() { defer close(out) @@ -102,12 +102,12 @@ func (c *offlineRouting) SearchValue(ctx context.Context, key string, _ ...ropts return out, nil } -func (c *offlineRouting) FindPeer(ctx context.Context, pid peer.ID) (pstore.PeerInfo, error) { - return pstore.PeerInfo{}, ErrOffline +func (c *offlineRouting) FindPeer(ctx context.Context, pid peer.ID) (peer.AddrInfo, error) { + return peer.AddrInfo{}, ErrOffline } -func (c *offlineRouting) FindProvidersAsync(ctx context.Context, k cid.Cid, max int) <-chan pstore.PeerInfo { - out := make(chan pstore.PeerInfo) +func (c *offlineRouting) FindProvidersAsync(ctx context.Context, k cid.Cid, max int) <-chan peer.AddrInfo { + out := make(chan peer.AddrInfo) close(out) return out } @@ -124,5 +124,5 @@ func (c *offlineRouting) Bootstrap(context.Context) error { return nil } -// ensure offlineRouting matches the IpfsRouting interface -var _ routing.IpfsRouting = &offlineRouting{} +// ensure offlineRouting matches the Routing interface +var _ routing.Routing = &offlineRouting{} diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index 9703bac57..00e0174ba 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -7,8 +7,10 @@ import ( cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" - ropt "github.com/libp2p/go-libp2p-routing/options" - testutil "github.com/libp2p/go-testutil" + + "github.com/libp2p/go-libp2p-core/routing" + "github.com/libp2p/go-libp2p-core/test" + mh "github.com/multiformats/go-multihash" ) @@ -40,12 +42,12 @@ func TestOfflineRouterStorage(t *testing.T) { t.Fatal("Router should throw errors for unfound records") } - local, err := offline.GetValue(ctx, "key", ropt.Offline) + local, err := offline.GetValue(ctx, "key", routing.Offline) if err != nil { t.Fatal(err) } - _, err = offline.GetValue(ctx, "notHere", ropt.Offline) + _, err = offline.GetValue(ctx, "notHere", routing.Offline) if err == nil { t.Fatal("Router should throw errors for unfound records") } @@ -61,7 +63,7 @@ func TestOfflineRouterLocal(t *testing.T) { nds := ds.NewMapDatastore() offline := NewOfflineRouter(nds, blankValidator{}) - id, _ := testutil.RandPeerID() + id, _ := test.RandPeerID() _, err := offline.FindPeer(ctx, id) if err != ErrOffline { t.Fatal("OfflineRouting should alert that its offline") From 1f2b5ca04f4cb9d5fe9b11d4c58edb91bab8b2a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Tue, 28 May 2019 17:09:34 +0100 Subject: [PATCH 3037/3817] migrate to go-libp2p-core. This commit was moved from ipfs/interface-go-ipfs-core@2cc0c497f2b0b14a4ac22989a3861c1a2b0038c4 --- coreiface/dht.go | 7 +++---- coreiface/key.go | 2 +- coreiface/pubsub.go | 2 +- coreiface/swarm.go | 12 ++++++------ 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/coreiface/dht.go b/coreiface/dht.go index 0cb7893ef..5f49e74a3 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -6,8 +6,7 @@ import ( "github.com/ipfs/interface-go-ipfs-core/options" - "github.com/libp2p/go-libp2p-peer" - pstore "github.com/libp2p/go-libp2p-peerstore" + "github.com/libp2p/go-libp2p-core/peer" ) // DhtAPI specifies the interface to the DHT @@ -16,11 +15,11 @@ import ( type DhtAPI interface { // FindPeer queries the DHT for all of the multiaddresses associated with a // Peer ID - FindPeer(context.Context, peer.ID) (pstore.PeerInfo, error) + FindPeer(context.Context, peer.ID) (peer.AddrInfo, error) // FindProviders finds peers in the DHT who can provide a specific value // given a key. - FindProviders(context.Context, path.Path, ...options.DhtFindProvidersOption) (<-chan pstore.PeerInfo, error) + FindProviders(context.Context, path.Path, ...options.DhtFindProvidersOption) (<-chan peer.AddrInfo, error) // Provide announces to the network that you are providing given values Provide(context.Context, path.Path, ...options.DhtProvideOption) error diff --git a/coreiface/key.go b/coreiface/key.go index e7fb3f442..db729b3b4 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/interface-go-ipfs-core/options" - "github.com/libp2p/go-libp2p-peer" + "github.com/libp2p/go-libp2p-core/peer" ) // Key specifies the interface to Keys in KeyAPI Keystore diff --git a/coreiface/pubsub.go b/coreiface/pubsub.go index 212e77225..d9826551d 100644 --- a/coreiface/pubsub.go +++ b/coreiface/pubsub.go @@ -6,7 +6,7 @@ import ( options "github.com/ipfs/interface-go-ipfs-core/options" - peer "github.com/libp2p/go-libp2p-peer" + "github.com/libp2p/go-libp2p-core/peer" ) // PubSubSubscription is an active PubSub subscription diff --git a/coreiface/swarm.go b/coreiface/swarm.go index 2e00ecbd3..d7b25d5e8 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -5,10 +5,10 @@ import ( "errors" "time" - net "github.com/libp2p/go-libp2p-net" - "github.com/libp2p/go-libp2p-peer" - pstore "github.com/libp2p/go-libp2p-peerstore" - "github.com/libp2p/go-libp2p-protocol" + "github.com/libp2p/go-libp2p-core/network" + "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p-core/protocol" + ma "github.com/multiformats/go-multiaddr" ) @@ -26,7 +26,7 @@ type ConnectionInfo interface { Address() ma.Multiaddr // Direction returns which way the connection was established - Direction() net.Direction + Direction() network.Direction // Latency returns last known round trip time to the peer Latency() (time.Duration, error) @@ -38,7 +38,7 @@ type ConnectionInfo interface { // SwarmAPI specifies the interface to libp2p swarm type SwarmAPI interface { // Connect to a given peer - Connect(context.Context, pstore.PeerInfo) error + Connect(context.Context, peer.AddrInfo) error // Disconnect from a given address Disconnect(context.Context, ma.Multiaddr) error From 71415df55b5d8e528ce29aa9626cd2e145b66207 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Wed, 17 Apr 2019 10:08:08 -0700 Subject: [PATCH 3038/3817] Introduce first strategic provider: do nothing License: MIT Signed-off-by: Michael Avila This commit was moved from ipfs/go-ipfs-provider@e3c70dfd532e93a554431d9c0a9782cbd252f445 --- provider/offline.go | 20 ++- provider/provider.go | 70 ++------ provider/{ => queue}/queue.go | 5 +- provider/{ => queue}/queue_test.go | 5 +- provider/simple/provider.go | 72 ++++++++ provider/{ => simple}/provider_test.go | 14 +- provider/simple/reprovide.go | 225 +++++++++++++++++++++++++ provider/simple/reprovide_test.go | 61 +++++++ provider/system.go | 47 ++++++ 9 files changed, 448 insertions(+), 71 deletions(-) rename provider/{ => queue}/queue.go (98%) rename provider/{ => queue}/queue_test.go (96%) create mode 100644 provider/simple/provider.go rename provider/{ => simple}/provider_test.go (86%) create mode 100644 provider/simple/reprovide.go create mode 100644 provider/simple/reprovide_test.go create mode 100644 provider/system.go diff --git a/provider/offline.go b/provider/offline.go index 0c91ed2af..eb1d1b9ac 100644 --- a/provider/offline.go +++ b/provider/offline.go @@ -1,20 +1,28 @@ package provider -import "github.com/ipfs/go-cid" +import ( + "context" + "github.com/ipfs/go-cid" +) type offlineProvider struct{} -// NewOfflineProvider creates a Provider that does nothing -func NewOfflineProvider() Provider { +// NewOfflineProvider creates a ProviderSystem that does nothing +func NewOfflineProvider() System { return &offlineProvider{} } -func (op *offlineProvider) Run() {} +func (op *offlineProvider) Run() { +} -func (op *offlineProvider) Provide(cid cid.Cid) error { +func (op *offlineProvider) Close() error { return nil } -func (op *offlineProvider) Close() error { +func (op *offlineProvider) Provide(_ cid.Cid) error { + return nil +} + +func (op *offlineProvider) Reprovide(_ context.Context) error { return nil } diff --git a/provider/provider.go b/provider/provider.go index 67c5c6b6b..e8939ba6f 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -1,18 +1,18 @@ -// Package provider implements structures and methods to provide blocks, -// keep track of which blocks are provided, and to allow those blocks to -// be reprovided. package provider import ( "context" "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log" - "github.com/libp2p/go-libp2p-routing" ) -var log = logging.Logger("provider") +var ( + // StrategicProvidingEnabled toggles between the original providing mechanism + // and the new strategic providing system + StrategicProvidingEnabled = false -const provideOutgoingWorkerLimit = 8 + log = logging.Logger("provider") +) // Provider announces blocks to the network type Provider interface { @@ -24,56 +24,10 @@ type Provider interface { Close() error } -type provider struct { - ctx context.Context - // the CIDs for which provide announcements should be made - queue *Queue - // used to announce providing to the network - contentRouting routing.ContentRouting -} - -// NewProvider creates a provider that announces blocks to the network using a content router -func NewProvider(ctx context.Context, queue *Queue, contentRouting routing.ContentRouting) Provider { - return &provider{ - ctx: ctx, - queue: queue, - contentRouting: contentRouting, - } -} - -// Close stops the provider -func (p *provider) Close() error { - p.queue.Close() - return nil -} - -// Start workers to handle provide requests. -func (p *provider) Run() { - p.handleAnnouncements() -} - -// Provide the given cid using specified strategy. -func (p *provider) Provide(root cid.Cid) error { - p.queue.Enqueue(root) - return nil -} - -// Handle all outgoing cids by providing (announcing) them -func (p *provider) handleAnnouncements() { - for workers := 0; workers < provideOutgoingWorkerLimit; workers++ { - go func() { - for p.ctx.Err() == nil { - select { - case <-p.ctx.Done(): - return - case c := <-p.queue.Dequeue(): - log.Info("announce - start - ", c) - if err := p.contentRouting.Provide(p.ctx, c, true); err != nil { - log.Warningf("Unable to provide entry: %s, %s", c, err) - } - log.Info("announce - end - ", c) - } - } - }() - } +// Reprovider reannounces blocks to the network +type Reprovider interface { + // Run is used to begin processing the reprovider work and waiting for reprovide triggers + Run() + // Trigger a reprovide + Trigger(context.Context) error } diff --git a/provider/queue.go b/provider/queue/queue.go similarity index 98% rename from provider/queue.go rename to provider/queue/queue.go index 8fdfca815..2afbc81ee 100644 --- a/provider/queue.go +++ b/provider/queue/queue.go @@ -1,4 +1,4 @@ -package provider +package queue import ( "context" @@ -10,8 +10,11 @@ import ( datastore "github.com/ipfs/go-datastore" namespace "github.com/ipfs/go-datastore/namespace" query "github.com/ipfs/go-datastore/query" + logging "github.com/ipfs/go-log" ) +var log = logging.Logger("provider.queue") + // Queue provides a durable, FIFO interface to the datastore for storing cids // // Durability just means that cids in the process of being provided when a diff --git a/provider/queue_test.go b/provider/queue/queue_test.go similarity index 96% rename from provider/queue_test.go rename to provider/queue/queue_test.go index e151478d9..c8fb8682e 100644 --- a/provider/queue_test.go +++ b/provider/queue/queue_test.go @@ -1,4 +1,4 @@ -package provider +package queue import ( "context" @@ -8,8 +8,11 @@ import ( cid "github.com/ipfs/go-cid" datastore "github.com/ipfs/go-datastore" sync "github.com/ipfs/go-datastore/sync" + "github.com/ipfs/go-ipfs-blocksutil" ) +var blockGenerator = blocksutil.NewBlockGenerator() + func makeCids(n int) []cid.Cid { cids := make([]cid.Cid, 0, n) for i := 0; i < n; i++ { diff --git a/provider/simple/provider.go b/provider/simple/provider.go new file mode 100644 index 000000000..8310cebb3 --- /dev/null +++ b/provider/simple/provider.go @@ -0,0 +1,72 @@ +// Package simple implements structures and methods to provide blocks, +// keep track of which blocks are provided, and to allow those blocks to +// be reprovided. +package simple + +import ( + "context" + + cid "github.com/ipfs/go-cid" + q "github.com/ipfs/go-ipfs/provider/queue" + logging "github.com/ipfs/go-log" + routing "github.com/libp2p/go-libp2p-routing" +) + +var logP = logging.Logger("provider.simple") + +const provideOutgoingWorkerLimit = 8 + +// Provider announces blocks to the network +type Provider struct { + ctx context.Context + // the CIDs for which provide announcements should be made + queue *q.Queue + // used to announce providing to the network + contentRouting routing.ContentRouting +} + +// NewProvider creates a provider that announces blocks to the network using a content router +func NewProvider(ctx context.Context, queue *q.Queue, contentRouting routing.ContentRouting) *Provider { + return &Provider{ + ctx: ctx, + queue: queue, + contentRouting: contentRouting, + } +} + +// Close stops the provider +func (p *Provider) Close() error { + p.queue.Close() + return nil +} + +// Run workers to handle provide requests. +func (p *Provider) Run() { + p.handleAnnouncements() +} + +// Provide the given cid using specified strategy. +func (p *Provider) Provide(root cid.Cid) error { + p.queue.Enqueue(root) + return nil +} + +// Handle all outgoing cids by providing (announcing) them +func (p *Provider) handleAnnouncements() { + for workers := 0; workers < provideOutgoingWorkerLimit; workers++ { + go func() { + for p.ctx.Err() == nil { + select { + case <-p.ctx.Done(): + return + case c := <-p.queue.Dequeue(): + logP.Info("announce - start - ", c) + if err := p.contentRouting.Provide(p.ctx, c, true); err != nil { + logP.Warningf("Unable to provide entry: %s, %s", c, err) + } + logP.Info("announce - end - ", c) + } + } + }() + } +} diff --git a/provider/provider_test.go b/provider/simple/provider_test.go similarity index 86% rename from provider/provider_test.go rename to provider/simple/provider_test.go index 7ef007b03..6f70a41d7 100644 --- a/provider/provider_test.go +++ b/provider/simple/provider_test.go @@ -1,4 +1,4 @@ -package provider +package simple_test import ( "context" @@ -11,6 +11,10 @@ import ( sync "github.com/ipfs/go-datastore/sync" blocksutil "github.com/ipfs/go-ipfs-blocksutil" pstore "github.com/libp2p/go-libp2p-peerstore" + + q "github.com/ipfs/go-ipfs/provider/queue" + + . "github.com/ipfs/go-ipfs/provider/simple" ) var blockGenerator = blocksutil.NewBlockGenerator() @@ -39,15 +43,15 @@ func TestAnnouncement(t *testing.T) { defer ctx.Done() ds := sync.MutexWrap(datastore.NewMapDatastore()) - queue, err := NewQueue(ctx, "test", ds) + queue, err := q.NewQueue(ctx, "test", ds) if err != nil { t.Fatal(err) } r := mockContentRouting() - provider := NewProvider(ctx, queue, r) - provider.Run() + prov := NewProvider(ctx, queue, r) + prov.Run() cids := cid.NewSet() @@ -58,7 +62,7 @@ func TestAnnouncement(t *testing.T) { go func() { for _, c := range cids.Keys() { - err = provider.Provide(c) + err = prov.Provide(c) // A little goroutine stirring to exercise some different states r := rand.Intn(10) time.Sleep(time.Microsecond * time.Duration(r)) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go new file mode 100644 index 000000000..73b733ce2 --- /dev/null +++ b/provider/simple/reprovide.go @@ -0,0 +1,225 @@ +package simple + +import ( + "context" + "fmt" + "time" + + backoff "github.com/cenkalti/backoff" + cid "github.com/ipfs/go-cid" + cidutil "github.com/ipfs/go-cidutil" + blocks "github.com/ipfs/go-ipfs-blockstore" + pin "github.com/ipfs/go-ipfs/pin" + ipld "github.com/ipfs/go-ipld-format" + logging "github.com/ipfs/go-log" + merkledag "github.com/ipfs/go-merkledag" + verifcid "github.com/ipfs/go-verifcid" + routing "github.com/libp2p/go-libp2p-routing" +) + +var logR = logging.Logger("reprovider.simple") + +//KeyChanFunc is function streaming CIDs to pass to content routing +type KeyChanFunc func(context.Context) (<-chan cid.Cid, error) +type doneFunc func(error) + +// Reprovider reannounces blocks to the network +type Reprovider struct { + ctx context.Context + trigger chan doneFunc + + // The routing system to provide values through + rsys routing.ContentRouting + + keyProvider KeyChanFunc + + tick time.Duration +} + +// NewReprovider creates new Reprovider instance. +func NewReprovider(ctx context.Context, reprovideIniterval time.Duration, rsys routing.ContentRouting, keyProvider KeyChanFunc) *Reprovider { + return &Reprovider{ + ctx: ctx, + trigger: make(chan doneFunc), + + rsys: rsys, + keyProvider: keyProvider, + tick: reprovideIniterval, + } +} + +// Close the reprovider +func (rp *Reprovider) Close() error { + return nil +} + +// Run re-provides keys with 'tick' interval or when triggered +func (rp *Reprovider) Run() { + // dont reprovide immediately. + // may have just started the daemon and shutting it down immediately. + // probability( up another minute | uptime ) increases with uptime. + after := time.After(time.Minute) + var done doneFunc + for { + if rp.tick == 0 { + after = make(chan time.Time) + } + + select { + case <-rp.ctx.Done(): + return + case done = <-rp.trigger: + case <-after: + } + + //'mute' the trigger channel so when `ipfs bitswap reprovide` is called + //a 'reprovider is already running' error is returned + unmute := rp.muteTrigger() + + err := rp.Reprovide() + if err != nil { + logR.Debug(err) + } + + if done != nil { + done(err) + } + + unmute() + + after = time.After(rp.tick) + } +} + +// Reprovide registers all keys given by rp.keyProvider to libp2p content routing +func (rp *Reprovider) Reprovide() error { + keychan, err := rp.keyProvider(rp.ctx) + if err != nil { + return fmt.Errorf("failed to get key chan: %s", err) + } + for c := range keychan { + // hash security + if err := verifcid.ValidateCid(c); err != nil { + logR.Errorf("insecure hash in reprovider, %s (%s)", c, err) + continue + } + op := func() error { + err := rp.rsys.Provide(rp.ctx, c, true) + if err != nil { + logR.Debugf("Failed to provide key: %s", err) + } + return err + } + + // TODO: this backoff library does not respect our context, we should + // eventually work contexts into it. low priority. + err := backoff.Retry(op, backoff.NewExponentialBackOff()) + if err != nil { + logR.Debugf("Providing failed after number of retries: %s", err) + return err + } + } + return nil +} + +// Trigger starts reprovision process in rp.Run and waits for it +func (rp *Reprovider) Trigger(ctx context.Context) error { + progressCtx, done := context.WithCancel(ctx) + + var err error + df := func(e error) { + err = e + done() + } + + select { + case <-rp.ctx.Done(): + return context.Canceled + case <-ctx.Done(): + return context.Canceled + case rp.trigger <- df: + <-progressCtx.Done() + return err + } +} + +func (rp *Reprovider) muteTrigger() context.CancelFunc { + ctx, cf := context.WithCancel(rp.ctx) + go func() { + defer cf() + for { + select { + case <-ctx.Done(): + return + case done := <-rp.trigger: + done(fmt.Errorf("reprovider is already running")) + } + } + }() + + return cf +} + +// Strategies + +// NewBlockstoreProvider returns key provider using bstore.AllKeysChan +func NewBlockstoreProvider(bstore blocks.Blockstore) KeyChanFunc { + return func(ctx context.Context) (<-chan cid.Cid, error) { + return bstore.AllKeysChan(ctx) + } +} + +// NewPinnedProvider returns provider supplying pinned keys +func NewPinnedProvider(onlyRoots bool) func(pin.Pinner, ipld.DAGService) KeyChanFunc { + return func(pinning pin.Pinner, dag ipld.DAGService) KeyChanFunc { + return func(ctx context.Context) (<-chan cid.Cid, error) { + set, err := pinSet(ctx, pinning, dag, onlyRoots) + if err != nil { + return nil, err + } + + outCh := make(chan cid.Cid) + go func() { + defer close(outCh) + for c := range set.New { + select { + case <-ctx.Done(): + return + case outCh <- c: + } + } + + }() + + return outCh, nil + } + } +} + +func pinSet(ctx context.Context, pinning pin.Pinner, dag ipld.DAGService, onlyRoots bool) (*cidutil.StreamingSet, error) { + set := cidutil.NewStreamingSet() + + go func() { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + defer close(set.New) + + for _, key := range pinning.DirectKeys() { + set.Visitor(ctx)(key) + } + + for _, key := range pinning.RecursiveKeys() { + set.Visitor(ctx)(key) + + if !onlyRoots { + err := merkledag.EnumerateChildren(ctx, merkledag.GetLinksWithDAG(dag), key, set.Visitor(ctx)) + if err != nil { + logR.Errorf("reprovide indirect pins: %s", err) + return + } + } + } + }() + + return set, nil +} diff --git a/provider/simple/reprovide_test.go b/provider/simple/reprovide_test.go new file mode 100644 index 000000000..17c626c5b --- /dev/null +++ b/provider/simple/reprovide_test.go @@ -0,0 +1,61 @@ +package simple_test + +import ( + "context" + "testing" + "time" + + blocks "github.com/ipfs/go-block-format" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + "github.com/ipfs/go-ipfs-blockstore" + mock "github.com/ipfs/go-ipfs-routing/mock" + pstore "github.com/libp2p/go-libp2p-peerstore" + "github.com/libp2p/go-testutil" + + . "github.com/ipfs/go-ipfs/provider/simple" +) + +func TestReprovide(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + mrserv := mock.NewServer() + + idA := testutil.RandIdentityOrFatal(t) + idB := testutil.RandIdentityOrFatal(t) + + clA := mrserv.Client(idA) + clB := mrserv.Client(idB) + + bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) + + blk := blocks.NewBlock([]byte("this is a test")) + err := bstore.Put(blk) + if err != nil { + t.Fatal(err) + } + + keyProvider := NewBlockstoreProvider(bstore) + reprov := NewReprovider(ctx, time.Hour, clA, keyProvider) + err = reprov.Reprovide() + if err != nil { + t.Fatal(err) + } + + var providers []pstore.PeerInfo + maxProvs := 100 + + provChan := clB.FindProvidersAsync(ctx, blk.Cid(), maxProvs) + for p := range provChan { + providers = append(providers, p) + } + + if len(providers) == 0 { + t.Fatal("Should have gotten a provider") + } + + if providers[0].ID != idA.ID() { + t.Fatal("Somehow got the wrong peer back as a provider.") + } +} diff --git a/provider/system.go b/provider/system.go new file mode 100644 index 000000000..6bc1d357c --- /dev/null +++ b/provider/system.go @@ -0,0 +1,47 @@ +package provider + +import ( + "context" + "github.com/ipfs/go-cid" +) + +// System defines the interface for interacting with the value +// provider system +type System interface { + Run() + Close() error + Provide(cid.Cid) error + Reprovide(context.Context) error +} + +type system struct { + provider Provider + reprovider Reprovider +} + +// NewSystem constructs a new provider system from a provider and reprovider +func NewSystem(provider Provider, reprovider Reprovider) System { + return &system{provider, reprovider} +} + +// Run the provider system by running the provider and reprovider +func (s *system) Run() { + go s.provider.Run() + go s.reprovider.Run() +} + +// Close the provider and reprovider +func (s *system) Close() error { + // TODO: Close reprovider here + return s.provider.Close() +} + +// Provide a value +func (s *system) Provide(cid cid.Cid) error { + return s.provider.Provide(cid) +} + +// Reprovide all the previously provided values +func (s *system) Reprovide(ctx context.Context) error { + return s.reprovider.Trigger(ctx) +} From be52bbe69ede891acb30ff9adcf75ac5532853a3 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Mon, 6 May 2019 11:24:35 -0700 Subject: [PATCH 3039/3817] Remove unnecessary _ License: MIT Signed-off-by: Michael Avila This commit was moved from ipfs/go-ipfs-provider@7d7655da46fbe7b03da2aaf9f3676a1616d5acd6 --- provider/offline.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/provider/offline.go b/provider/offline.go index eb1d1b9ac..5511364ed 100644 --- a/provider/offline.go +++ b/provider/offline.go @@ -19,10 +19,10 @@ func (op *offlineProvider) Close() error { return nil } -func (op *offlineProvider) Provide(_ cid.Cid) error { +func (op *offlineProvider) Provide(cid.Cid) error { return nil } -func (op *offlineProvider) Reprovide(_ context.Context) error { +func (op *offlineProvider) Reprovide(context.Context) error { return nil } From 4f924d987cf4042397c6200b8e2376625574ceb1 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Mon, 6 May 2019 11:46:26 -0700 Subject: [PATCH 3040/3817] Close reprovider in the provider system License: MIT Signed-off-by: Michael Avila This commit was moved from ipfs/go-ipfs-provider@29621ab7788e322f42784a39131dfb2e5609eeaa --- provider/provider.go | 2 ++ provider/system.go | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/provider/provider.go b/provider/provider.go index e8939ba6f..689bfeb1b 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -30,4 +30,6 @@ type Reprovider interface { Run() // Trigger a reprovide Trigger(context.Context) error + // Close stops the reprovider + Close() error } diff --git a/provider/system.go b/provider/system.go index 6bc1d357c..b3e17ee40 100644 --- a/provider/system.go +++ b/provider/system.go @@ -32,8 +32,20 @@ func (s *system) Run() { // Close the provider and reprovider func (s *system) Close() error { - // TODO: Close reprovider here - return s.provider.Close() + var errs []error + + if err := s.provider.Close(); err != nil { + errs = append(errs, err) + } + + if err := s.reprovider.Close(); err != nil { + errs = append(errs, err) + } + + if len(errs) > 0 { + return errs[0] + } + return nil } // Provide a value From c704a5b39ec54b7d6581c7290f12d0de929b98fd Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Mon, 6 May 2019 14:54:41 -0700 Subject: [PATCH 3041/3817] Remove unused strategic providing feature flag License: MIT Signed-off-by: Michael Avila This commit was moved from ipfs/go-ipfs-provider@313c41c327fd3000d0c6a027333d66693880c3c9 --- provider/provider.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/provider/provider.go b/provider/provider.go index 689bfeb1b..751d7cd63 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -6,13 +6,7 @@ import ( logging "github.com/ipfs/go-log" ) -var ( - // StrategicProvidingEnabled toggles between the original providing mechanism - // and the new strategic providing system - StrategicProvidingEnabled = false - - log = logging.Logger("provider") -) +var log = logging.Logger("provider") // Provider announces blocks to the network type Provider interface { From e32e76160186688dbdcfbb6eb61e7c51246f0915 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Wed, 22 May 2019 14:04:59 -0700 Subject: [PATCH 3042/3817] Remove unused logger in provider This commit was moved from ipfs/go-ipfs-provider@f8ac9dae80c6f1ce42f240f2f4e12835fc202af1 --- provider/provider.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/provider/provider.go b/provider/provider.go index 751d7cd63..7dec4c172 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -3,11 +3,8 @@ package provider import ( "context" "github.com/ipfs/go-cid" - logging "github.com/ipfs/go-log" ) -var log = logging.Logger("provider") - // Provider announces blocks to the network type Provider interface { // Run is used to begin processing the provider work From 8fa0e7f946f4130e3e92f550f3e426a7399a683c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 31 May 2019 18:19:29 -0700 Subject: [PATCH 3043/3817] migrate to go-libp2p-core This commit was moved from ipfs/go-mfs@a6cdb51b96dbc76be9e28440be5c7de62bf2d77e --- mfs/repub_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/repub_test.go b/mfs/repub_test.go index 3a7eaaf70..6be5624ab 100644 --- a/mfs/repub_test.go +++ b/mfs/repub_test.go @@ -6,7 +6,7 @@ import ( "time" cid "github.com/ipfs/go-cid" - ci "github.com/libp2p/go-testutil/ci" + ci "github.com/libp2p/go-libp2p-testing/ci" ) func TestRepublisher(t *testing.T) { From 97950e8f04f3d9908dd86d99db0d61aa2ba177d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Tue, 28 May 2019 17:21:57 +0100 Subject: [PATCH 3044/3817] migrate to go-libp2p-core. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #6391 License: MIT Signed-off-by: Raúl Kripalani This commit was moved from ipfs/go-ipfs-provider@095af8f253aed3b34efebeba4e3a4c5ed5b755b7 --- provider/simple/provider.go | 2 +- provider/simple/provider_test.go | 4 ++-- provider/simple/reprovide.go | 2 +- provider/simple/reprovide_test.go | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/provider/simple/provider.go b/provider/simple/provider.go index 8310cebb3..abe13ce59 100644 --- a/provider/simple/provider.go +++ b/provider/simple/provider.go @@ -9,7 +9,7 @@ import ( cid "github.com/ipfs/go-cid" q "github.com/ipfs/go-ipfs/provider/queue" logging "github.com/ipfs/go-log" - routing "github.com/libp2p/go-libp2p-routing" + routing "github.com/libp2p/go-libp2p-core/routing" ) var logP = logging.Logger("provider.simple") diff --git a/provider/simple/provider_test.go b/provider/simple/provider_test.go index 6f70a41d7..4922958c8 100644 --- a/provider/simple/provider_test.go +++ b/provider/simple/provider_test.go @@ -10,7 +10,7 @@ import ( datastore "github.com/ipfs/go-datastore" sync "github.com/ipfs/go-datastore/sync" blocksutil "github.com/ipfs/go-ipfs-blocksutil" - pstore "github.com/libp2p/go-libp2p-peerstore" + peer "github.com/libp2p/go-libp2p-core/peer" q "github.com/ipfs/go-ipfs/provider/queue" @@ -28,7 +28,7 @@ func (r *mockRouting) Provide(ctx context.Context, cid cid.Cid, recursive bool) return nil } -func (r *mockRouting) FindProvidersAsync(ctx context.Context, cid cid.Cid, timeout int) <-chan pstore.PeerInfo { +func (r *mockRouting) FindProvidersAsync(ctx context.Context, cid cid.Cid, timeout int) <-chan peer.AddrInfo { return nil } diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index 73b733ce2..ce5c71812 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -14,7 +14,7 @@ import ( logging "github.com/ipfs/go-log" merkledag "github.com/ipfs/go-merkledag" verifcid "github.com/ipfs/go-verifcid" - routing "github.com/libp2p/go-libp2p-routing" + routing "github.com/libp2p/go-libp2p-core/routing" ) var logR = logging.Logger("reprovider.simple") diff --git a/provider/simple/reprovide_test.go b/provider/simple/reprovide_test.go index 17c626c5b..e9925e55e 100644 --- a/provider/simple/reprovide_test.go +++ b/provider/simple/reprovide_test.go @@ -10,8 +10,8 @@ import ( dssync "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs-blockstore" mock "github.com/ipfs/go-ipfs-routing/mock" - pstore "github.com/libp2p/go-libp2p-peerstore" - "github.com/libp2p/go-testutil" + peer "github.com/libp2p/go-libp2p-core/peer" + testutil "github.com/libp2p/go-libp2p-testing/net" . "github.com/ipfs/go-ipfs/provider/simple" ) @@ -43,7 +43,7 @@ func TestReprovide(t *testing.T) { t.Fatal(err) } - var providers []pstore.PeerInfo + var providers []peer.AddrInfo maxProvs := 100 provChan := clB.FindProvidersAsync(ctx, blk.Cid(), maxProvs) From 53c3e0cfdf4577b5e2e776ae5b7b919fe9b85304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Tue, 28 May 2019 17:21:57 +0100 Subject: [PATCH 3045/3817] migrate to go-libp2p-core. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #6391 License: MIT Signed-off-by: Raúl Kripalani This commit was moved from ipfs/go-namesys@3c8536299a1ec2c8c86303ea45057622190072b6 --- namesys/interface.go | 2 +- namesys/ipns_resolver_validation_test.go | 17 ++++++++--------- namesys/namesys.go | 6 +++--- namesys/namesys_test.go | 4 ++-- namesys/publisher.go | 6 +++--- namesys/publisher_test.go | 6 +++--- namesys/republisher/repub.go | 4 ++-- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve_test.go | 12 +++++++----- namesys/routing.go | 4 ++-- 10 files changed, 33 insertions(+), 32 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index 4db95ab3c..ecd80943b 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -37,7 +37,7 @@ import ( path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" - ci "github.com/libp2p/go-libp2p-crypto" + ci "github.com/libp2p/go-libp2p-core/crypto" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 71335b522..9eed8375b 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -13,14 +13,13 @@ import ( ipns "github.com/ipfs/go-ipns" path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" - ci "github.com/libp2p/go-libp2p-crypto" - peer "github.com/libp2p/go-libp2p-peer" - pstore "github.com/libp2p/go-libp2p-peerstore" + ci "github.com/libp2p/go-libp2p-core/crypto" + peer "github.com/libp2p/go-libp2p-core/peer" + pstore "github.com/libp2p/go-libp2p-core/peerstore" + routing "github.com/libp2p/go-libp2p-core/routing" pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem" record "github.com/libp2p/go-libp2p-record" - routing "github.com/libp2p/go-libp2p-routing" - ropts "github.com/libp2p/go-libp2p-routing/options" - testutil "github.com/libp2p/go-testutil" + testutil "github.com/libp2p/go-libp2p-testing/net" ) func TestResolverValidation(t *testing.T) { @@ -168,11 +167,11 @@ func newMockValueStore(id testutil.Identity, dstore ds.Datastore, kbook pstore.K } } -func (m *mockValueStore) GetValue(ctx context.Context, k string, opts ...ropts.Option) ([]byte, error) { +func (m *mockValueStore) GetValue(ctx context.Context, k string, opts ...routing.Option) ([]byte, error) { return m.r.GetValue(ctx, k, opts...) } -func (m *mockValueStore) SearchValue(ctx context.Context, k string, opts ...ropts.Option) (<-chan []byte, error) { +func (m *mockValueStore) SearchValue(ctx context.Context, k string, opts ...routing.Option) (<-chan []byte, error) { return m.r.SearchValue(ctx, k, opts...) } @@ -196,6 +195,6 @@ func (m *mockValueStore) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey return pk, m.kbook.AddPubKey(p, pk) } -func (m *mockValueStore) PutValue(ctx context.Context, k string, d []byte, opts ...ropts.Option) error { +func (m *mockValueStore) PutValue(ctx context.Context, k string, d []byte, opts ...routing.Option) error { return m.r.PutValue(ctx, k, d, opts...) } diff --git a/namesys/namesys.go b/namesys/namesys.go index f8b8c6d12..6d59c62e3 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -10,9 +10,9 @@ import ( path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" isd "github.com/jbenet/go-is-domain" - ci "github.com/libp2p/go-libp2p-crypto" - peer "github.com/libp2p/go-libp2p-peer" - routing "github.com/libp2p/go-libp2p-routing" + ci "github.com/libp2p/go-libp2p-core/crypto" + peer "github.com/libp2p/go-libp2p-core/peer" + routing "github.com/libp2p/go-libp2p-core/routing" mh "github.com/multiformats/go-multihash" ) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 2cf316cf3..031ae833a 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -12,8 +12,8 @@ import ( path "github.com/ipfs/go-path" "github.com/ipfs/go-unixfs" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" - ci "github.com/libp2p/go-libp2p-crypto" - peer "github.com/libp2p/go-libp2p-peer" + ci "github.com/libp2p/go-libp2p-core/crypto" + peer "github.com/libp2p/go-libp2p-core/peer" pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem" record "github.com/libp2p/go-libp2p-record" ) diff --git a/namesys/publisher.go b/namesys/publisher.go index e43858d02..c06deb795 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -15,9 +15,9 @@ import ( pb "github.com/ipfs/go-ipns/pb" path "github.com/ipfs/go-path" ft "github.com/ipfs/go-unixfs" - ci "github.com/libp2p/go-libp2p-crypto" - peer "github.com/libp2p/go-libp2p-peer" - routing "github.com/libp2p/go-libp2p-routing" + ci "github.com/libp2p/go-libp2p-core/crypto" + peer "github.com/libp2p/go-libp2p-core/peer" + routing "github.com/libp2p/go-libp2p-core/routing" base32 "github.com/whyrusleeping/base32" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 53cc6735e..0b7b2c939 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -11,9 +11,9 @@ import ( dshelp "github.com/ipfs/go-ipfs-ds-help" mockrouting "github.com/ipfs/go-ipfs-routing/mock" ipns "github.com/ipfs/go-ipns" - ci "github.com/libp2p/go-libp2p-crypto" - peer "github.com/libp2p/go-libp2p-peer" - testutil "github.com/libp2p/go-testutil" + ci "github.com/libp2p/go-libp2p-core/crypto" + peer "github.com/libp2p/go-libp2p-core/peer" + testutil "github.com/libp2p/go-libp2p-testing/net" ma "github.com/multiformats/go-multiaddr" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 1092ba3a5..9e7272d32 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -15,8 +15,8 @@ import ( logging "github.com/ipfs/go-log" goprocess "github.com/jbenet/goprocess" gpctx "github.com/jbenet/goprocess/context" - ic "github.com/libp2p/go-libp2p-crypto" - peer "github.com/libp2p/go-libp2p-peer" + ic "github.com/libp2p/go-libp2p-core/crypto" + peer "github.com/libp2p/go-libp2p-core/peer" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 48a0b086f..5fedc3907 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -14,7 +14,7 @@ import ( path "github.com/ipfs/go-path" goprocess "github.com/jbenet/goprocess" - pstore "github.com/libp2p/go-libp2p-peerstore" + peer "github.com/libp2p/go-libp2p-core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" ) @@ -47,7 +47,7 @@ func TestRepublish(t *testing.T) { } bsinf := bootstrap.BootstrapConfigWithPeers( - []pstore.PeerInfo{ + []peer.AddrInfo{ nodes[0].Peerstore.PeerInfo(nodes[0].Identity), }, ) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 882061448..814bf5973 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -11,8 +11,10 @@ import ( mockrouting "github.com/ipfs/go-ipfs-routing/mock" ipns "github.com/ipfs/go-ipns" path "github.com/ipfs/go-path" - peer "github.com/libp2p/go-libp2p-peer" - testutil "github.com/libp2p/go-testutil" + ci "github.com/libp2p/go-libp2p-core/crypto" + peer "github.com/libp2p/go-libp2p-core/peer" + test "github.com/libp2p/go-libp2p-core/test" + testutil "github.com/libp2p/go-libp2p-testing/net" ) func TestRoutingResolve(t *testing.T) { @@ -24,7 +26,7 @@ func TestRoutingResolve(t *testing.T) { resolver := NewIpnsResolver(d) publisher := NewIpnsPublisher(d, dstore) - privk, pubk, err := testutil.RandTestKeyPair(512) + privk, pubk, err := test.RandTestKeyPair(ci.RSA, 512) if err != nil { t.Fatal(err) } @@ -57,7 +59,7 @@ func TestPrexistingExpiredRecord(t *testing.T) { resolver := NewIpnsResolver(d) publisher := NewIpnsPublisher(d, dstore) - privk, pubk, err := testutil.RandTestKeyPair(512) + privk, pubk, err := test.RandTestKeyPair(ci.RSA, 512) if err != nil { t.Fatal(err) } @@ -99,7 +101,7 @@ func TestPrexistingRecord(t *testing.T) { resolver := NewIpnsResolver(d) publisher := NewIpnsPublisher(d, dstore) - privk, pubk, err := testutil.RandTestKeyPair(512) + privk, pubk, err := test.RandTestKeyPair(ci.RSA, 512) if err != nil { t.Fatal(err) } diff --git a/namesys/routing.go b/namesys/routing.go index e89dd9c9d..94c12a726 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -12,9 +12,9 @@ import ( logging "github.com/ipfs/go-log" path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" + peer "github.com/libp2p/go-libp2p-core/peer" + routing "github.com/libp2p/go-libp2p-core/routing" dht "github.com/libp2p/go-libp2p-kad-dht" - peer "github.com/libp2p/go-libp2p-peer" - routing "github.com/libp2p/go-libp2p-routing" mh "github.com/multiformats/go-multihash" ) From 1761264ece49ba93aad1c293100fd7bd3fb89131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Tue, 28 May 2019 17:21:57 +0100 Subject: [PATCH 3046/3817] migrate to go-libp2p-core. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #6391 License: MIT Signed-off-by: Raúl Kripalani This commit was moved from ipfs/go-ipfs-keystore@b10263a4cef90848a63d25b02271dbd695a11254 --- keystore/keystore.go | 2 +- keystore/keystore_test.go | 2 +- keystore/memkeystore.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index d9467f263..237d4b05c 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -8,7 +8,7 @@ import ( "strings" logging "github.com/ipfs/go-log" - ci "github.com/libp2p/go-libp2p-crypto" + ci "github.com/libp2p/go-libp2p-core/crypto" ) var log = logging.Logger("keystore") diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index d7118d756..37f59ebff 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -9,7 +9,7 @@ import ( "sort" "testing" - ci "github.com/libp2p/go-libp2p-crypto" + ci "github.com/libp2p/go-libp2p-core/crypto" ) type rr struct{} diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index 4f505a995..4067bbce2 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -1,6 +1,6 @@ package keystore -import ci "github.com/libp2p/go-libp2p-crypto" +import ci "github.com/libp2p/go-libp2p-core/crypto" // MemKeystore is an in memory keystore implementation that is not persisted to // any backing storage. From 74df8aef3bf365fcbf3b219cce8f546e0a190b01 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Thu, 6 Jun 2019 11:21:30 -0700 Subject: [PATCH 3047/3817] Fix references and add go.mod This commit was moved from ipfs/go-ipfs-provider@d9e78687ab9de19171a207b3f1cb95a793d7057b --- provider/simple/provider.go | 2 +- provider/simple/provider_test.go | 4 ++-- provider/simple/reprovide_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/provider/simple/provider.go b/provider/simple/provider.go index abe13ce59..b7da4b245 100644 --- a/provider/simple/provider.go +++ b/provider/simple/provider.go @@ -7,7 +7,7 @@ import ( "context" cid "github.com/ipfs/go-cid" - q "github.com/ipfs/go-ipfs/provider/queue" + q "github.com/ipfs/go-ipfs-provider/queue" logging "github.com/ipfs/go-log" routing "github.com/libp2p/go-libp2p-core/routing" ) diff --git a/provider/simple/provider_test.go b/provider/simple/provider_test.go index 4922958c8..6fbc528ba 100644 --- a/provider/simple/provider_test.go +++ b/provider/simple/provider_test.go @@ -12,9 +12,9 @@ import ( blocksutil "github.com/ipfs/go-ipfs-blocksutil" peer "github.com/libp2p/go-libp2p-core/peer" - q "github.com/ipfs/go-ipfs/provider/queue" + q "github.com/ipfs/go-ipfs-provider/queue" - . "github.com/ipfs/go-ipfs/provider/simple" + . "github.com/ipfs/go-ipfs-provider/simple" ) var blockGenerator = blocksutil.NewBlockGenerator() diff --git a/provider/simple/reprovide_test.go b/provider/simple/reprovide_test.go index e9925e55e..c86372000 100644 --- a/provider/simple/reprovide_test.go +++ b/provider/simple/reprovide_test.go @@ -13,7 +13,7 @@ import ( peer "github.com/libp2p/go-libp2p-core/peer" testutil "github.com/libp2p/go-libp2p-testing/net" - . "github.com/ipfs/go-ipfs/provider/simple" + . "github.com/ipfs/go-ipfs-provider/simple" ) func TestReprovide(t *testing.T) { From 05e5b0c51914debea72dffb32a720e4d96fcdadf Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Thu, 6 Jun 2019 14:46:37 -0700 Subject: [PATCH 3048/3817] Remove dependency on go-ipfs pinner This commit was moved from ipfs/go-ipfs-provider@245e9651e80d5df45db9889848a390bd3c15d29f --- provider/simple/reprovide.go | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index ce5c71812..6511b33a7 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -5,16 +5,15 @@ import ( "fmt" "time" - backoff "github.com/cenkalti/backoff" - cid "github.com/ipfs/go-cid" - cidutil "github.com/ipfs/go-cidutil" + "github.com/cenkalti/backoff" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-cidutil" blocks "github.com/ipfs/go-ipfs-blockstore" - pin "github.com/ipfs/go-ipfs/pin" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" - merkledag "github.com/ipfs/go-merkledag" - verifcid "github.com/ipfs/go-verifcid" - routing "github.com/libp2p/go-libp2p-core/routing" + "github.com/ipfs/go-merkledag" + "github.com/ipfs/go-verifcid" + "github.com/libp2p/go-libp2p-core/routing" ) var logR = logging.Logger("reprovider.simple") @@ -169,9 +168,16 @@ func NewBlockstoreProvider(bstore blocks.Blockstore) KeyChanFunc { } } +// Pinner interface defines how the simple.Reprovider wants to interact +// with a Pinning service +type Pinner interface { + DirectKeys() []cid.Cid + RecursiveKeys() []cid.Cid +} + // NewPinnedProvider returns provider supplying pinned keys -func NewPinnedProvider(onlyRoots bool) func(pin.Pinner, ipld.DAGService) KeyChanFunc { - return func(pinning pin.Pinner, dag ipld.DAGService) KeyChanFunc { +func NewPinnedProvider(onlyRoots bool) func(Pinner, ipld.DAGService) KeyChanFunc { + return func(pinning Pinner, dag ipld.DAGService) KeyChanFunc { return func(ctx context.Context) (<-chan cid.Cid, error) { set, err := pinSet(ctx, pinning, dag, onlyRoots) if err != nil { @@ -196,7 +202,7 @@ func NewPinnedProvider(onlyRoots bool) func(pin.Pinner, ipld.DAGService) KeyChan } } -func pinSet(ctx context.Context, pinning pin.Pinner, dag ipld.DAGService, onlyRoots bool) (*cidutil.StreamingSet, error) { +func pinSet(ctx context.Context, pinning Pinner, dag ipld.DAGService, onlyRoots bool) (*cidutil.StreamingSet, error) { set := cidutil.NewStreamingSet() go func() { From b2e20be5acbb9c8e9586504ede7dc35b9b5e2c2e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 6 Jun 2019 15:42:03 -0700 Subject: [PATCH 3049/3817] pin: fix concurrent map access race Not sure why this didn't show up sooner. fixes #6418 This commit was moved from ipfs/go-ipfs-pinner@4eaa9166a097c9d4e8800e0ac44092182d7f5710 --- pinning/pinner/pin.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 8df21ee1c..48a16f84e 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -498,11 +498,17 @@ func LoadPinner(d ds.Datastore, dserv, internal ipld.DAGService) (Pinner, error) // DirectKeys returns a slice containing the directly pinned keys func (p *pinner) DirectKeys() []cid.Cid { + p.lock.RLock() + defer p.lock.RUnlock() + return p.directPin.Keys() } // RecursiveKeys returns a slice containing the recursively pinned keys func (p *pinner) RecursiveKeys() []cid.Cid { + p.lock.RLock() + defer p.lock.RUnlock() + return p.recursePin.Keys() } From a7795d1022888bd01927cb8d032e6b232e7b6edb Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Mon, 10 Jun 2019 11:47:52 -0700 Subject: [PATCH 3050/3817] Refactor NewPinnedProvider to be created all at once This commit was moved from ipfs/go-ipfs-provider@d7bb85e89013a89c088194e91d9d871c959b2ce4 --- provider/simple/reprovide.go | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index 6511b33a7..4b5c83369 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -176,29 +176,27 @@ type Pinner interface { } // NewPinnedProvider returns provider supplying pinned keys -func NewPinnedProvider(onlyRoots bool) func(Pinner, ipld.DAGService) KeyChanFunc { - return func(pinning Pinner, dag ipld.DAGService) KeyChanFunc { - return func(ctx context.Context) (<-chan cid.Cid, error) { - set, err := pinSet(ctx, pinning, dag, onlyRoots) - if err != nil { - return nil, err - } +func NewPinnedProvider(onlyRoots bool, pinning Pinner, dag ipld.DAGService) KeyChanFunc { + return func(ctx context.Context) (<-chan cid.Cid, error) { + set, err := pinSet(ctx, pinning, dag, onlyRoots) + if err != nil { + return nil, err + } - outCh := make(chan cid.Cid) - go func() { - defer close(outCh) - for c := range set.New { - select { - case <-ctx.Done(): - return - case outCh <- c: - } + outCh := make(chan cid.Cid) + go func() { + defer close(outCh) + for c := range set.New { + select { + case <-ctx.Done(): + return + case outCh <- c: } + } - }() + }() - return outCh, nil - } + return outCh, nil } } From 4d3e5a45e10bf75c507b882face79b0cb33f7393 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Tue, 11 Jun 2019 09:42:06 -0700 Subject: [PATCH 3051/3817] Add basic README This commit was moved from ipfs/go-ipfs-provider@a95551d21c0ccd67a6f2064c7cccc6ea85fcff3a --- provider/LICENSE-APACHE | 13 +++++++++ provider/LICENSE-MIT | 19 ++++++++++++ provider/README.md | 64 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 provider/LICENSE-APACHE create mode 100644 provider/LICENSE-MIT create mode 100644 provider/README.md diff --git a/provider/LICENSE-APACHE b/provider/LICENSE-APACHE new file mode 100644 index 000000000..546514363 --- /dev/null +++ b/provider/LICENSE-APACHE @@ -0,0 +1,13 @@ +Copyright 2019. Protocol Labs, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/provider/LICENSE-MIT b/provider/LICENSE-MIT new file mode 100644 index 000000000..ea532a830 --- /dev/null +++ b/provider/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright 2019. Protocol Labs, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/provider/README.md b/provider/README.md new file mode 100644 index 000000000..d5f5aadc5 --- /dev/null +++ b/provider/README.md @@ -0,0 +1,64 @@ +# go-ipfs-provider + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](https://protocol.ai) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) +[![Coverage Status](https://codecov.io/gh/ipfs/go-ipfs-provider/branch/master/graph/badge.svg)](https://codecov.io/gh/ipfs/go-ipfs-provider) +[![Travis CI](https://travis-ci.org/ipfs/go-ipfs-provider.svg?branch=master)](https://travis-ci.org/ipfs/go-ipfs-provider) + +## Background + +The provider system is responsible for announcing and reannouncing to the ipfs network that a node has content. + +## Install + +Via `go get`: + +```sh +$ go get github.com/ipfs/go-ipfs-provider +``` + +> Requires Go 1.12 + +## Usage + +Here's how you create, start, interact with, and stop the provider system: + +```golang +import ( + "context" + "time" + + "github.com/ipfs/go-ipfs-provider" + "github.com/ipfs/go-ipfs-provider/queue" + "github.com/ipfs/go-ipfs-provider/simple" +) + +rsys := (your routing system here) +dstore := (your datastore here) +cid := (your cid to provide here) + +q := queue.NewQueue(context.Background(), "example", dstore) + +reprov := simple.NewReprovider(context.Background(), time.Hour * 12, rsys, simple.NewBlockstoreProvider) +prov := simple.NewProvider(context.Background(), q, rsys) +sys := provider.NewSystem(prov, reprov) + +sys.Run() + +sys.Provide(cid) + +sys.Close() +``` + +## Contribute + +PRs are welcome! + +Small note: If editing the Readme, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +This library is dual-licensed under Apache 2.0 and MIT terms. + +Copyright 2019. Protocol Labs, Inc. From 77c632e602260f1d97e57fe2287ac129d9edd150 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Wed, 12 Jun 2019 11:09:13 -0700 Subject: [PATCH 3052/3817] Add 3 minute timeout to Provide call This commit was moved from ipfs/go-ipfs-provider@c2e647a1581108d8f5da479f0d550268a5712c46 --- provider/simple/provider.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/provider/simple/provider.go b/provider/simple/provider.go index b7da4b245..8fa9c108e 100644 --- a/provider/simple/provider.go +++ b/provider/simple/provider.go @@ -5,6 +5,7 @@ package simple import ( "context" + "time" cid "github.com/ipfs/go-cid" q "github.com/ipfs/go-ipfs-provider/queue" @@ -14,7 +15,10 @@ import ( var logP = logging.Logger("provider.simple") -const provideOutgoingWorkerLimit = 8 +const ( + provideOutgoingWorkerLimit = 8 + provideTimeout = 3 * time.Minute +) // Provider announces blocks to the network type Provider struct { @@ -60,8 +64,10 @@ func (p *Provider) handleAnnouncements() { case <-p.ctx.Done(): return case c := <-p.queue.Dequeue(): + ctx, cancel := context.WithTimeout(p.ctx, provideTimeout) + defer cancel() logP.Info("announce - start - ", c) - if err := p.contentRouting.Provide(p.ctx, c, true); err != nil { + if err := p.contentRouting.Provide(ctx, c, true); err != nil { logP.Warningf("Unable to provide entry: %s, %s", c, err) } logP.Info("announce - end - ", c) From 5eadc76a32f0ea3e33072b90df58266250f7681f Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Wed, 12 Jun 2019 12:38:25 -0700 Subject: [PATCH 3053/3817] Make timeout configurable This commit was moved from ipfs/go-ipfs-provider@8e30fff33017288e116ff62d25c1534b30e74634 --- provider/simple/provider.go | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/provider/simple/provider.go b/provider/simple/provider.go index 8fa9c108e..3b4c88237 100644 --- a/provider/simple/provider.go +++ b/provider/simple/provider.go @@ -7,17 +7,16 @@ import ( "context" "time" - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" q "github.com/ipfs/go-ipfs-provider/queue" logging "github.com/ipfs/go-log" - routing "github.com/libp2p/go-libp2p-core/routing" + "github.com/libp2p/go-libp2p-core/routing" ) var logP = logging.Logger("provider.simple") const ( provideOutgoingWorkerLimit = 8 - provideTimeout = 3 * time.Minute ) // Provider announces blocks to the network @@ -27,15 +26,31 @@ type Provider struct { queue *q.Queue // used to announce providing to the network contentRouting routing.ContentRouting + // how long to wait for announce to complete before giving up + timeout time.Duration +} + +type Option func(*Provider) + +func WithTimeout(timeout time.Duration) Option { + return func(p *Provider) { + p.timeout = timeout + } } // NewProvider creates a provider that announces blocks to the network using a content router -func NewProvider(ctx context.Context, queue *q.Queue, contentRouting routing.ContentRouting) *Provider { - return &Provider{ +func NewProvider(ctx context.Context, queue *q.Queue, contentRouting routing.ContentRouting, options ...Option) *Provider { + p := &Provider{ ctx: ctx, queue: queue, contentRouting: contentRouting, } + + for _, option := range options { + option(p) + } + + return p } // Close stops the provider @@ -64,7 +79,7 @@ func (p *Provider) handleAnnouncements() { case <-p.ctx.Done(): return case c := <-p.queue.Dequeue(): - ctx, cancel := context.WithTimeout(p.ctx, provideTimeout) + ctx, cancel := context.WithTimeout(p.ctx, p.timeout) defer cancel() logP.Info("announce - start - ", c) if err := p.contentRouting.Provide(ctx, c, true); err != nil { From 00a447c67e4480f430ba7c41af43286ffb7cc8f6 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Wed, 12 Jun 2019 12:43:47 -0700 Subject: [PATCH 3054/3817] Make worker count configurable This commit was moved from ipfs/go-ipfs-provider@c3bccce5e62351f4fb09763fbe7b325275b97f44 --- provider/simple/provider.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/provider/simple/provider.go b/provider/simple/provider.go index 3b4c88237..9b374a043 100644 --- a/provider/simple/provider.go +++ b/provider/simple/provider.go @@ -15,10 +15,6 @@ import ( var logP = logging.Logger("provider.simple") -const ( - provideOutgoingWorkerLimit = 8 -) - // Provider announces blocks to the network type Provider struct { ctx context.Context @@ -28,22 +24,35 @@ type Provider struct { contentRouting routing.ContentRouting // how long to wait for announce to complete before giving up timeout time.Duration + // how many workers concurrently work through thhe queue + workerLimit int } +// Option defines the functional option type that can be used to configure +// provider instances type Option func(*Provider) +// WithTimeout is an option to set a timeout on a provider func WithTimeout(timeout time.Duration) Option { return func(p *Provider) { p.timeout = timeout } } +// MaxWorkers is an option to set the max workers on a provider +func MaxWorkers(count int) Option { + return func(p *Provider) { + p.workerLimit = count + } +} + // NewProvider creates a provider that announces blocks to the network using a content router func NewProvider(ctx context.Context, queue *q.Queue, contentRouting routing.ContentRouting, options ...Option) *Provider { p := &Provider{ ctx: ctx, queue: queue, contentRouting: contentRouting, + workerLimit: 8, } for _, option := range options { @@ -72,7 +81,7 @@ func (p *Provider) Provide(root cid.Cid) error { // Handle all outgoing cids by providing (announcing) them func (p *Provider) handleAnnouncements() { - for workers := 0; workers < provideOutgoingWorkerLimit; workers++ { + for workers := 0; workers < p.workerLimit; workers++ { go func() { for p.ctx.Err() == nil { select { From 17d15f0faa9cd777bbf4f595aa8c884fcaf9e800 Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Wed, 12 Jun 2019 13:02:41 -0700 Subject: [PATCH 3055/3817] Either timeout or not This commit was moved from ipfs/go-ipfs-provider@5f6a572aacdcfbdfe7f4f399eab628f0b585e099 --- provider/simple/provider.go | 11 +++++-- provider/simple/provider_test.go | 50 +++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/provider/simple/provider.go b/provider/simple/provider.go index 9b374a043..9302a2749 100644 --- a/provider/simple/provider.go +++ b/provider/simple/provider.go @@ -88,8 +88,15 @@ func (p *Provider) handleAnnouncements() { case <-p.ctx.Done(): return case c := <-p.queue.Dequeue(): - ctx, cancel := context.WithTimeout(p.ctx, p.timeout) - defer cancel() + var ctx context.Context + var cancel context.CancelFunc + if p.timeout > 0 { + ctx, cancel = context.WithTimeout(p.ctx, p.timeout) + defer cancel() + } else { + ctx = p.ctx + } + logP.Info("announce - start - ", c) if err := p.contentRouting.Provide(ctx, c, true); err != nil { logP.Warningf("Unable to provide entry: %s, %s", c, err) diff --git a/provider/simple/provider_test.go b/provider/simple/provider_test.go index 6fbc528ba..deb0032ec 100644 --- a/provider/simple/provider_test.go +++ b/provider/simple/provider_test.go @@ -24,7 +24,11 @@ type mockRouting struct { } func (r *mockRouting) Provide(ctx context.Context, cid cid.Cid, recursive bool) error { - r.provided <- cid + select { + case r.provided <- cid: + case <-ctx.Done(): + panic("context cancelled, but shouldn't have") + } return nil } @@ -81,3 +85,47 @@ func TestAnnouncement(t *testing.T) { } } } + +func TestAnnouncementTimeout(t *testing.T) { + ctx := context.Background() + defer ctx.Done() + + ds := sync.MutexWrap(datastore.NewMapDatastore()) + queue, err := q.NewQueue(ctx, "test", ds) + if err != nil { + t.Fatal(err) + } + + r := mockContentRouting() + + prov := NewProvider(ctx, queue, r, WithTimeout(1*time.Second)) + prov.Run() + + cids := cid.NewSet() + + for i := 0; i < 100; i++ { + c := blockGenerator.Next().Cid() + cids.Add(c) + } + + go func() { + for _, c := range cids.Keys() { + err = prov.Provide(c) + // A little goroutine stirring to exercise some different states + r := rand.Intn(10) + time.Sleep(time.Microsecond * time.Duration(r)) + } + }() + + for cids.Len() > 0 { + select { + case cp := <-r.provided: + if !cids.Has(cp) { + t.Fatal("Wrong CID provided") + } + cids.Remove(cp) + case <-time.After(time.Second * 5): + t.Fatal("Timeout waiting for cids to be provided.") + } + } +} From 5a625d0bf87ae1717f07b318497c417b8170fbdb Mon Sep 17 00:00:00 2001 From: Jim McDonald Date: Thu, 13 Jun 2019 15:08:36 +0100 Subject: [PATCH 3056/3817] Allow resolution of .eth names via .eth.link This commit was moved from ipfs/go-namesys@da12604bf0f1a554f787008df4a4ddd4ec8bf8dc --- namesys/namesys.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/namesys/namesys.go b/namesys/namesys.go index 6d59c62e3..cf944001d 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -80,6 +80,9 @@ func (ns *mpns) ResolveAsync(ctx context.Context, name string, options ...opts.R return resolveAsync(ctx, ns, name, opts.ProcessOpts(options)) } +const ethTLD = ".eth" +const linkTLD = ".link" + // resolveOnce implements resolver. func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { out := make(chan onceResult, 1) @@ -87,6 +90,11 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. if !strings.HasPrefix(name, ipnsPrefix) { name = ipnsPrefix + name } + if strings.HasSuffix(name, ethTLD) { + // This is an ENS name. As we're resolving via an arbitrary DNS server + // that may not know about .eth we need to add our link domain suffix. + name = name + linkTLD + } segments := strings.SplitN(name, "/", 4) if len(segments) < 3 || segments[0] != "" { log.Debugf("invalid name syntax for %s", name) From 92c99eb29dfc56e7054e2722589483773fbd7729 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 30 Jun 2019 10:26:24 +0200 Subject: [PATCH 3057/3817] fix defer in a loop This commit was moved from ipfs/go-ipfs-provider@55dd24d1b9b3e3071d241ff47ab2ddf97b607cb2 --- provider/simple/provider.go | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/provider/simple/provider.go b/provider/simple/provider.go index 9302a2749..3993d4509 100644 --- a/provider/simple/provider.go +++ b/provider/simple/provider.go @@ -88,22 +88,26 @@ func (p *Provider) handleAnnouncements() { case <-p.ctx.Done(): return case c := <-p.queue.Dequeue(): - var ctx context.Context - var cancel context.CancelFunc - if p.timeout > 0 { - ctx, cancel = context.WithTimeout(p.ctx, p.timeout) - defer cancel() - } else { - ctx = p.ctx - } - - logP.Info("announce - start - ", c) - if err := p.contentRouting.Provide(ctx, c, true); err != nil { - logP.Warningf("Unable to provide entry: %s, %s", c, err) - } - logP.Info("announce - end - ", c) + p.doProvide(c) } } }() } } + +func (p *Provider) doProvide(c cid.Cid) { + ctx := p.ctx + if p.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, p.timeout) + defer cancel() + } else { + ctx = p.ctx + } + + logP.Info("announce - start - ", c) + if err := p.contentRouting.Provide(ctx, c, true); err != nil { + logP.Warningf("Unable to provide entry: %s, %s", c, err) + } + logP.Info("announce - end - ", c) +} From 603c772ec458f347db0e4dc9d1af33f4bad49c9c Mon Sep 17 00:00:00 2001 From: Michael Avila Date: Wed, 3 Jul 2019 16:15:48 -0700 Subject: [PATCH 3058/3817] Change queue to be timestamp based; avoid collisions using cids; limit to 1 This commit was moved from ipfs/go-ipfs-provider@ee20a1ecbde67c2e1804b93e2ce5ec93a3a07f7d --- provider/queue/queue.go | 123 +++++++++-------------------------- provider/queue/queue_test.go | 44 +++---------- 2 files changed, 38 insertions(+), 129 deletions(-) diff --git a/provider/queue/queue.go b/provider/queue/queue.go index 2afbc81ee..ddaa97582 100644 --- a/provider/queue/queue.go +++ b/provider/queue/queue.go @@ -3,8 +3,7 @@ package queue import ( "context" "fmt" - "strconv" - "strings" + "time" cid "github.com/ipfs/go-cid" datastore "github.com/ipfs/go-datastore" @@ -25,8 +24,6 @@ type Queue struct { // e.g. provider vs reprovider name string ctx context.Context - tail uint64 - head uint64 ds datastore.Datastore // Must be threadsafe dequeue chan cid.Cid enqueue chan cid.Cid @@ -37,16 +34,10 @@ type Queue struct { // NewQueue creates a queue for cids func NewQueue(ctx context.Context, name string, ds datastore.Datastore) (*Queue, error) { namespaced := namespace.Wrap(ds, datastore.NewKey("/"+name+"/queue/")) - head, tail, err := getQueueHeadTail(ctx, namespaced) - if err != nil { - return nil, err - } cancelCtx, cancel := context.WithCancel(ctx) q := &Queue{ name: name, ctx: cancelCtx, - head: head, - tail: tail, ds: namespaced, dequeue: make(chan cid.Cid), enqueue: make(chan cid.Cid), @@ -77,41 +68,6 @@ func (q *Queue) Dequeue() <-chan cid.Cid { return q.dequeue } -// Look for next Cid in the queue and return it. Skip over gaps and mangled data -func (q *Queue) nextEntry() (datastore.Key, cid.Cid) { - for { - if q.head >= q.tail { - return datastore.Key{}, cid.Undef - } - - key := q.queueKey(q.head) - value, err := q.ds.Get(key) - - if err != nil { - if err == datastore.ErrNotFound { - log.Warningf("Error missing entry in queue: %s", key) - } else { - log.Errorf("Error fetching from queue: %s", err) - } - q.head++ // move on - continue - } - - c, err := cid.Parse(value) - if err != nil { - log.Warningf("Error marshalling Cid from queue: ", err) - q.head++ - err = q.ds.Delete(key) - if err != nil { - log.Warningf("Provider queue failed to delete: %s", key) - } - continue - } - - return key, c - } -} - // Run dequeues and enqueues when available. func (q *Queue) work() { go func() { @@ -124,7 +80,26 @@ func (q *Queue) work() { for { if c == cid.Undef { - k, c = q.nextEntry() + head, e := q.getQueueHead() + + if e != nil { + log.Errorf("error querying for head of queue: %s, stopping provider", e) + return + } else if head != nil { + k = datastore.NewKey(head.Key) + c, e = cid.Parse(head.Value) + if e != nil { + log.Warningf("error parsing queue entry cid with key (%s), removing it from queue: %s", head.Key, e) + err := q.ds.Delete(k) + if err != nil { + log.Errorf("error deleting queue entry with key (%s), due to error (%s), stopping provider", head.Key, err) + return + } + continue + } + } else { + c = cid.Undef + } } // If c != cid.Undef set dequeue and attempt write, otherwise wait for enqueue @@ -135,14 +110,13 @@ func (q *Queue) work() { select { case toQueue := <-q.enqueue: - nextKey := q.queueKey(q.tail) + keyPath := fmt.Sprintf("%d/%s", time.Now().UnixNano(), c.String()) + nextKey := datastore.NewKey(keyPath) if err := q.ds.Put(nextKey, toQueue.Bytes()); err != nil { log.Errorf("Failed to enqueue cid: %s", err) continue } - - q.tail++ case dequeue <- c: err := q.ds.Delete(k) @@ -151,7 +125,6 @@ func (q *Queue) work() { continue } c = cid.Undef - q.head++ case <-q.ctx.Done(): return } @@ -159,53 +132,17 @@ func (q *Queue) work() { }() } -func (q *Queue) queueKey(id uint64) datastore.Key { - s := fmt.Sprintf("%016X", id) - return datastore.NewKey(s) -} - -func getQueueHeadTail(ctx context.Context, datastore datastore.Datastore) (uint64, uint64, error) { - head, err := getQueueHead(datastore) - if err != nil { - return 0, 0, err - } - tail, err := getQueueTail(datastore) - if err != nil { - return 0, 0, err - } - return head, tail, nil -} - -func getQueueHead(ds datastore.Datastore) (uint64, error) { - return getFirstIDByOrder(ds, query.OrderByKey{}) -} - -func getQueueTail(ds datastore.Datastore) (uint64, error) { - tail, err := getFirstIDByOrder(ds, query.OrderByKeyDescending{}) +func (q *Queue) getQueueHead() (*query.Result, error) { + qry := query.Query{Orders: []query.Order{query.OrderByKey{}}, Limit: 1} + results, err := q.ds.Query(qry) if err != nil { - return 0, err - } - if tail > 0 { - tail++ - } - return tail, nil -} - -func getFirstIDByOrder(ds datastore.Datastore, order query.Order) (uint64, error) { - q := query.Query{Orders: []query.Order{order}} - results, err := ds.Query(q) - if err != nil { - return 0, err + return nil, err } defer results.Close() r, ok := results.NextSync() if !ok { - return 0, nil - } - trimmed := strings.TrimPrefix(r.Key, "/") - id, err := strconv.ParseUint(trimmed, 16, 64) - if err != nil { - return 0, err + return nil, nil } - return id, nil + + return &r, nil } diff --git a/provider/queue/queue_test.go b/provider/queue/queue_test.go index c8fb8682e..819fa90f9 100644 --- a/provider/queue/queue_test.go +++ b/provider/queue/queue_test.go @@ -5,9 +5,9 @@ import ( "testing" "time" - cid "github.com/ipfs/go-cid" - datastore "github.com/ipfs/go-datastore" - sync "github.com/ipfs/go-datastore/sync" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs-blocksutil" ) @@ -55,36 +55,6 @@ func TestBasicOperation(t *testing.T) { assertOrdered(cids, queue, t) } -func TestSparseDatastore(t *testing.T) { - ctx := context.Background() - defer ctx.Done() - - ds := sync.MutexWrap(datastore.NewMapDatastore()) - queue, err := NewQueue(ctx, "test", ds) - if err != nil { - t.Fatal(err) - } - - cids := makeCids(10) - for _, c := range cids { - queue.Enqueue(c) - } - - // remove entries in the middle - err = queue.ds.Delete(queue.queueKey(5)) - if err != nil { - t.Fatal(err) - } - - err = queue.ds.Delete(queue.queueKey(6)) - if err != nil { - t.Fatal(err) - } - - expected := append(cids[:5], cids[7:]...) - assertOrdered(expected, queue, t) -} - func TestMangledData(t *testing.T) { ctx := context.Background() defer ctx.Done() @@ -100,13 +70,15 @@ func TestMangledData(t *testing.T) { queue.Enqueue(c) } - // remove entries in the middle - err = queue.ds.Put(queue.queueKey(5), []byte("borked")) + // put bad data in the queue + queueKey := datastore.NewKey("/test/0") + err = queue.ds.Put(queueKey, []byte("borked")) if err != nil { t.Fatal(err) } - expected := append(cids[:5], cids[6:]...) + // expect to only see the valid cids we entered + expected := cids assertOrdered(expected, queue, t) } From 0b7ea10c6d7b6c51772c6a742383c2d93515b024 Mon Sep 17 00:00:00 2001 From: Jim McDonald Date: Thu, 4 Jul 2019 18:44:17 +0100 Subject: [PATCH 3059/3817] Add test for resolution of .eth names This commit was moved from ipfs/go-namesys@35fbeb6c31b16941e9f6ce7dc93e074a4a48dd98 --- namesys/namesys_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 031ae833a..d1ecf49e8 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -51,6 +51,7 @@ func mockResolverOne() *mockResolver { "QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy": "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", "QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n": "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", "QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD": "/ipns/ipfs.io", + "QmQ4QZh8nrsczdUEwTyfBope4THUhqxqc1fx6qYhhzZQei": "/ipfs/QmP3ouCnU8NNLsW6261pAx2pNLV2E4dQoisB1sgda12Act", }, } } @@ -58,7 +59,8 @@ func mockResolverOne() *mockResolver { func mockResolverTwo() *mockResolver { return &mockResolver{ entries: map[string]string{ - "ipfs.io": "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", + "ipfs.io": "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", + "www.wealdtech.eth.link": "/ipns/QmQ4QZh8nrsczdUEwTyfBope4THUhqxqc1fx6qYhhzZQei", }, } } @@ -80,6 +82,8 @@ func TestNamesysResolution(t *testing.T) { testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 1, "/ipns/ipfs.io", ErrResolveRecursion) testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 2, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", ErrResolveRecursion) testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 3, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", ErrResolveRecursion) + testResolution(t, r, "/ipns/www.wealdtech.eth", 1, "/ipns/QmQ4QZh8nrsczdUEwTyfBope4THUhqxqc1fx6qYhhzZQei", ErrResolveRecursion) + testResolution(t, r, "/ipns/www.wealdtech.eth", 2, "/ipfs/QmP3ouCnU8NNLsW6261pAx2pNLV2E4dQoisB1sgda12Act", nil) } func TestPublishWithCache0(t *testing.T) { From 554517f960692f6221483b98e2e36e7e908f8b2c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 11 Jul 2019 14:44:23 -0700 Subject: [PATCH 3060/3817] fix inconsistent EnumerateChildrenAsync behavior This fixes the inconsistent EnumerateChildrenAsync behavior by renaming everything. 1. EnumerateChildrenAsync has been renamed to WalkParallel because (a) it visited the root so EnumerateChildren was incorrect and (b) it isn't async. 2. EnumerateChildren has been renamed to Walk and now _also_ visits the root for consistency. Anyone needing to skip the root can do so manually (`if c.Equals(root)` ...). This commit was moved from ipfs/go-merkledag@5d52f0271fd2f8d7380152da605430dc271bb8fd --- ipld/merkledag/merkledag.go | 46 +++++++++++++++----------------- ipld/merkledag/merkledag_test.go | 22 +++++++-------- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 3153cf41e..7bd52b90e 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -198,7 +198,7 @@ func FetchGraphWithDepthLimit(ctx context.Context, root cid.Cid, depthLim int, s v, _ := ctx.Value(progressContextKey).(*ProgressTracker) if v == nil { - return EnumerateChildrenAsyncDepth(ctx, GetLinksDirect(ng), root, 0, visit) + return WalkParallelDepth(ctx, GetLinksDirect(ng), root, 0, visit) } visitProgress := func(c cid.Cid, depth int) bool { @@ -208,7 +208,7 @@ func FetchGraphWithDepthLimit(ctx context.Context, root cid.Cid, depthLim int, s } return false } - return EnumerateChildrenAsyncDepth(ctx, GetLinksDirect(ng), root, 0, visitProgress) + return WalkParallelDepth(ctx, GetLinksDirect(ng), root, 0, visitProgress) } // GetMany gets many nodes from the DAG at once. @@ -282,33 +282,31 @@ func GetLinksWithDAG(ng ipld.NodeGetter) GetLinks { } } -// EnumerateChildren will walk the dag below the given root node and add all -// unseen children to the passed in set. -// TODO: parallelize to avoid disk latency perf hits? -func EnumerateChildren(ctx context.Context, getLinks GetLinks, root cid.Cid, visit func(cid.Cid) bool) error { +// WalkGraph will walk the dag in order (depth first) starting at the given root. +func Walk(ctx context.Context, getLinks GetLinks, root cid.Cid, visit func(cid.Cid) bool) error { visitDepth := func(c cid.Cid, depth int) bool { return visit(c) } - return EnumerateChildrenDepth(ctx, getLinks, root, 0, visitDepth) + return WalkDepth(ctx, getLinks, root, 0, visitDepth) } -// EnumerateChildrenDepth walks the dag below the given root and passes the -// current depth to a given visit function. The visit function can be used to -// limit DAG exploration. -func EnumerateChildrenDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, depth int, visit func(cid.Cid, int) bool) error { +// WalkDepth walks the dag starting at the given root and passes the current +// depth to a given visit function. The visit function can be used to limit DAG +// exploration. +func WalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, depth int, visit func(cid.Cid, int) bool) error { + if !visit(root, depth) { + return nil + } + links, err := getLinks(ctx, root) if err != nil { return err } for _, lnk := range links { - c := lnk.Cid - if visit(c, depth+1) { - err = EnumerateChildrenDepth(ctx, getLinks, c, depth+1, visit) - if err != nil { - return err - } + if err := WalkDepth(ctx, getLinks, lnk.Cid, depth+1, visit); err != nil { + return err } } return nil @@ -344,23 +342,23 @@ func (p *ProgressTracker) Value() int { // 'fetchNodes' will start at a time var FetchGraphConcurrency = 32 -// EnumerateChildrenAsync is equivalent to EnumerateChildren *except* that it -// fetches children in parallel. +// WalkParallel is equivalent to Walk *except* that it explores multiple paths +// in parallel. // // NOTE: It *does not* make multiple concurrent calls to the passed `visit` function. -func EnumerateChildrenAsync(ctx context.Context, getLinks GetLinks, c cid.Cid, visit func(cid.Cid) bool) error { +func WalkParallel(ctx context.Context, getLinks GetLinks, c cid.Cid, visit func(cid.Cid) bool) error { visitDepth := func(c cid.Cid, depth int) bool { return visit(c) } - return EnumerateChildrenAsyncDepth(ctx, getLinks, c, 0, visitDepth) + return WalkParallelDepth(ctx, getLinks, c, 0, visitDepth) } -// EnumerateChildrenAsyncDepth is equivalent to EnumerateChildrenDepth *except* -// that it fetches children in parallel (down to a maximum depth in the graph). +// WalkParallelDepth is equivalent to WalkDepth *except* that it fetches +// children in parallel. // // NOTE: It *does not* make multiple concurrent calls to the passed `visit` function. -func EnumerateChildrenAsyncDepth(ctx context.Context, getLinks GetLinks, c cid.Cid, startDepth int, visit func(cid.Cid, int) bool) error { +func WalkParallelDepth(ctx context.Context, getLinks GetLinks, c cid.Cid, startDepth int, visit func(cid.Cid, int) bool) error { type cidDepth struct { cid cid.Cid depth int diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index bc87f3bb5..e56bb52d1 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -29,7 +29,7 @@ import ( // makeDepthTestingGraph makes a small DAG with two levels. The level-two // nodes are both children of the root and of one of the level 1 nodes. -// This is meant to test the EnumerateChildren*Depth functions. +// This is meant to test the Walk*Depth functions. func makeDepthTestingGraph(t *testing.T, ds ipld.DAGService) ipld.Node { root := NodeWithData(nil) l11 := NodeWithData([]byte("leve1_node1")) @@ -334,7 +334,7 @@ func TestFetchGraph(t *testing.T) { offlineDS := NewDAGService(bs) - err = EnumerateChildren(context.Background(), offlineDS.GetLinks, root.Cid(), func(_ cid.Cid) bool { return true }) + err = Walk(context.Background(), offlineDS.GetLinks, root.Cid(), func(_ cid.Cid) bool { return true }) if err != nil { t.Fatal(err) } @@ -347,11 +347,11 @@ func TestFetchGraphWithDepthLimit(t *testing.T) { } tests := []testcase{ - testcase{1, 3}, - testcase{0, 0}, - testcase{-1, 5}, - testcase{2, 5}, - testcase{3, 5}, + testcase{1, 4}, + testcase{0, 1}, + testcase{-1, 6}, + testcase{2, 6}, + testcase{3, 6}, } testF := func(t *testing.T, tc testcase) { @@ -383,7 +383,7 @@ func TestFetchGraphWithDepthLimit(t *testing.T) { } - err = EnumerateChildrenDepth(context.Background(), offlineDS.GetLinks, root.Cid(), 0, visitF) + err = WalkDepth(context.Background(), offlineDS.GetLinks, root.Cid(), 0, visitF) if err != nil { t.Fatal(err) } @@ -400,7 +400,7 @@ func TestFetchGraphWithDepthLimit(t *testing.T) { } } -func TestEnumerateChildren(t *testing.T) { +func TestWalk(t *testing.T) { bsi := bstest.Mocks(1) ds := NewDAGService(bsi[0]) @@ -409,7 +409,7 @@ func TestEnumerateChildren(t *testing.T) { set := cid.NewSet() - err := EnumerateChildren(context.Background(), ds.GetLinks, root.Cid(), set.Visit) + err := Walk(context.Background(), ds.GetLinks, root.Cid(), set.Visit) if err != nil { t.Fatal(err) } @@ -736,7 +736,7 @@ func TestEnumerateAsyncFailsNotFound(t *testing.T) { } cset := cid.NewSet() - err = EnumerateChildrenAsync(ctx, GetLinksDirect(ds), parent.Cid(), cset.Visit) + err = WalkParallel(ctx, GetLinksDirect(ds), parent.Cid(), cset.Visit) if err == nil { t.Fatal("this should have failed") } From be989276c6c3e70160473d567ff475a9e3b2f897 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 11 Jul 2019 14:49:43 -0700 Subject: [PATCH 3061/3817] nit: avoid copying CIDs This commit was moved from ipfs/go-merkledag@0db465ea2c348f9061079121c4b342863ce85b98 --- ipld/merkledag/merkledag.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 7bd52b90e..c035dd424 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -172,7 +172,7 @@ func FetchGraphWithDepthLimit(ctx context.Context, root cid.Cid, depthLim int, s ng = &sesGetter{bserv.NewSession(ctx, ds.Blocks)} } - set := make(map[string]int) + set := make(map[cid.Cid]int) // Visit function returns true when: // * The element is not in the set and we're not over depthLim @@ -182,15 +182,14 @@ func FetchGraphWithDepthLimit(ctx context.Context, root cid.Cid, depthLim int, s // depthLim = -1 means we only return true if the element is not in the // set. visit := func(c cid.Cid, depth int) bool { - key := string(c.Bytes()) - oldDepth, ok := set[key] + oldDepth, ok := set[c] if (ok && depthLim < 0) || (depthLim >= 0 && depth > depthLim) { return false } if !ok || oldDepth > depth { - set[key] = depth + set[c] = depth return true } return false From 78dd059cbc8ae90120740e2dbd0cc92711e0e71a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 11 Jul 2019 15:49:18 -0700 Subject: [PATCH 3062/3817] fix: enumerate children renamed to walk parallel as: 1. It also visited the root. 2. It walked in parallel and wasn't async. This commit was moved from ipfs/go-unixfs@72e2c7f97ba53d55cd23fb054b30cf4954b0c32a --- unixfs/hamt/hamt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 7947a2aa0..c44b1789f 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -339,7 +339,7 @@ func (ds *Shard) EnumLinksAsync(ctx context.Context) <-chan format.LinkResult { defer cancel() getLinks := makeAsyncTrieGetLinks(ds.dserv, linkResults) cset := cid.NewSet() - err := dag.EnumerateChildrenAsync(ctx, getLinks, ds.cid, cset.Visit) + err := dag.WalkParallel(ctx, getLinks, ds.cid, cset.Visit) if err != nil { emitResult(ctx, linkResults, format.LinkResult{Link: nil, Err: err}) } From 753a258464b02139b4dc0ec292dd29d01e116917 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 11 Jul 2019 16:02:55 -0700 Subject: [PATCH 3063/3817] ci: update This commit was moved from ipfs/go-unixfs@076d317ec94861d82397b0e0abcb84e22f5cf247 --- unixfs/Makefile | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 unixfs/Makefile diff --git a/unixfs/Makefile b/unixfs/Makefile deleted file mode 100644 index 20619413c..000000000 --- a/unixfs/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -gx: - go get github.com/whyrusleeping/gx - go get github.com/whyrusleeping/gx-go - -deps: gx - gx --verbose install --global - gx-go rewrite - -publish: - gx-go rewrite --undo - From 7c5c1e9b5016d34a2126fbf8bf1cccb406857101 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 11 Jul 2019 16:23:55 -0700 Subject: [PATCH 3064/3817] fix: update go-merkledag to use new Walk function This commit was moved from ipfs/go-ipfs-provider@8c4e1b3ff051e474585512c0aa5ba578c7807d43 --- provider/simple/reprovide.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index 4b5c83369..d2a229d66 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -213,10 +213,8 @@ func pinSet(ctx context.Context, pinning Pinner, dag ipld.DAGService, onlyRoots } for _, key := range pinning.RecursiveKeys() { - set.Visitor(ctx)(key) - if !onlyRoots { - err := merkledag.EnumerateChildren(ctx, merkledag.GetLinksWithDAG(dag), key, set.Visitor(ctx)) + err := merkledag.Walk(ctx, merkledag.GetLinksWithDAG(dag), key, set.Visitor(ctx)) if err != nil { logR.Errorf("reprovide indirect pins: %s", err) return From ae8b80e7ddf7e8a745065c7f53abade82a8656c5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 11 Jul 2019 18:15:33 -0700 Subject: [PATCH 3065/3817] fix reproviding only roots And improve the tests. This commit was moved from ipfs/go-ipfs-provider@dc4005bc786c429e876dea8ab83fbcadd9466c2c --- provider/simple/reprovide.go | 4 +- provider/simple/reprovide_test.go | 149 +++++++++++++++++++++++++----- 2 files changed, 129 insertions(+), 24 deletions(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index d2a229d66..590b9d1ab 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -213,7 +213,9 @@ func pinSet(ctx context.Context, pinning Pinner, dag ipld.DAGService, onlyRoots } for _, key := range pinning.RecursiveKeys() { - if !onlyRoots { + if onlyRoots { + set.Visitor(ctx)(key) + } else { err := merkledag.Walk(ctx, merkledag.GetLinksWithDAG(dag), key, set.Visitor(ctx)) if err != nil { logR.Errorf("reprovide indirect pins: %s", err) diff --git a/provider/simple/reprovide_test.go b/provider/simple/reprovide_test.go index c86372000..efe906f01 100644 --- a/provider/simple/reprovide_test.go +++ b/provider/simple/reprovide_test.go @@ -5,40 +5,73 @@ import ( "testing" "time" - blocks "github.com/ipfs/go-block-format" + bsrv "github.com/ipfs/go-blockservice" + "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipfs-blockstore" + offline "github.com/ipfs/go-ipfs-exchange-offline" mock "github.com/ipfs/go-ipfs-routing/mock" + cbor "github.com/ipfs/go-ipld-cbor" + merkledag "github.com/ipfs/go-merkledag" peer "github.com/libp2p/go-libp2p-core/peer" testutil "github.com/libp2p/go-libp2p-testing/net" + mh "github.com/multiformats/go-multihash" . "github.com/ipfs/go-ipfs-provider/simple" ) -func TestReprovide(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - +func setupRouting(t *testing.T) (clA, clB mock.Client, idA, idB peer.ID) { mrserv := mock.NewServer() - idA := testutil.RandIdentityOrFatal(t) - idB := testutil.RandIdentityOrFatal(t) + iidA := testutil.RandIdentityOrFatal(t) + iidB := testutil.RandIdentityOrFatal(t) - clA := mrserv.Client(idA) - clB := mrserv.Client(idB) + clA = mrserv.Client(iidA) + clB = mrserv.Client(iidB) - bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) + return clA, clB, iidA.ID(), iidB.ID() +} - blk := blocks.NewBlock([]byte("this is a test")) - err := bstore.Put(blk) - if err != nil { - t.Fatal(err) +func setupDag(t *testing.T) (nodes []cid.Cid, bstore blockstore.Blockstore) { + bstore = blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) + for _, data := range []string{"foo", "bar"} { + blk, err := cbor.WrapObject(data, mh.SHA2_256, -1) + if err != nil { + t.Fatal(err) + } + err = bstore.Put(blk) + if err != nil { + t.Fatal(err) + } + nodes = append(nodes, blk.Cid()) + + blk, err = cbor.WrapObject(map[string]interface{}{ + "child": blk.Cid(), + }, mh.SHA2_256, -1) + if err != nil { + t.Fatal(err) + } + err = bstore.Put(blk) + if err != nil { + t.Fatal(err) + } + nodes = append(nodes, blk.Cid()) } + return nodes, bstore +} + +func TestReprovide(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + clA, clB, idA, _ := setupRouting(t) + nodes, bstore := setupDag(t) + keyProvider := NewBlockstoreProvider(bstore) reprov := NewReprovider(ctx, time.Hour, clA, keyProvider) - err = reprov.Reprovide() + err := reprov.Reprovide() if err != nil { t.Fatal(err) } @@ -46,16 +79,86 @@ func TestReprovide(t *testing.T) { var providers []peer.AddrInfo maxProvs := 100 - provChan := clB.FindProvidersAsync(ctx, blk.Cid(), maxProvs) - for p := range provChan { - providers = append(providers, p) - } + for _, c := range nodes { + provChan := clB.FindProvidersAsync(ctx, c, maxProvs) + for p := range provChan { + providers = append(providers, p) + } - if len(providers) == 0 { - t.Fatal("Should have gotten a provider") + if len(providers) == 0 { + t.Fatal("Should have gotten a provider") + } + + if providers[0].ID != idA { + t.Fatal("Somehow got the wrong peer back as a provider.") + } } +} + +type mockPinner struct { + recursive []cid.Cid + direct []cid.Cid +} + +func (mp *mockPinner) DirectKeys() []cid.Cid { + return mp.direct +} + +func (mp *mockPinner) RecursiveKeys() []cid.Cid { + return mp.recursive +} + +func TestReprovidePinned(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + nodes, bstore := setupDag(t) + + dag := merkledag.NewDAGService(bsrv.New(bstore, offline.Exchange(bstore))) + + for i := 0; i < 2; i++ { + clA, clB, idA, _ := setupRouting(t) + + onlyRoots := i == 0 + t.Logf("only roots: %v", onlyRoots) + + var provide, dont []cid.Cid + if onlyRoots { + provide = []cid.Cid{nodes[1], nodes[3]} + dont = []cid.Cid{nodes[0], nodes[2]} + } else { + provide = []cid.Cid{nodes[0], nodes[1], nodes[3]} + dont = []cid.Cid{nodes[2]} + } + + keyProvider := NewPinnedProvider(onlyRoots, &mockPinner{ + recursive: []cid.Cid{nodes[1]}, + direct: []cid.Cid{nodes[3]}, + }, dag) + + reprov := NewReprovider(ctx, time.Hour, clA, keyProvider) + err := reprov.Reprovide() + if err != nil { + t.Fatal(err) + } + + for i, c := range provide { + prov, ok := <-clB.FindProvidersAsync(ctx, c, 1) + if !ok { + t.Errorf("Should have gotten a provider for %d", i) + continue + } - if providers[0].ID != idA.ID() { - t.Fatal("Somehow got the wrong peer back as a provider.") + if prov.ID != idA { + t.Errorf("Somehow got the wrong peer back as a provider.") + continue + } + } + for i, c := range dont { + prov, ok := <-clB.FindProvidersAsync(ctx, c, 1) + if ok { + t.Fatalf("found provider %s for %d, expected none", prov.ID, i) + } + } } } From 64144f604a03f8cecd4ac3f12bdd76ae7440b0b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 15 Jul 2019 15:45:03 +0200 Subject: [PATCH 3066/3817] Fix local imports This commit was moved from ipfs/go-filestore@6837d5d7c5a955568ff33760a58b78ecc5e663ea --- filestore/fsrefstore.go | 2 +- filestore/util.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 320ee5928..19927e0ef 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -8,7 +8,7 @@ import ( "os" "path/filepath" - pb "github.com/ipfs/go-ipfs/filestore/pb" + pb "github.com/ipfs/go-filestore/pb" proto "github.com/gogo/protobuf/proto" blocks "github.com/ipfs/go-block-format" diff --git a/filestore/util.go b/filestore/util.go index 4f3949591..bfb240c55 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -4,7 +4,7 @@ import ( "fmt" "sort" - pb "github.com/ipfs/go-ipfs/filestore/pb" + pb "github.com/ipfs/go-filestore/pb" cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" From 92005547d627509e1d381d6f1edbb8a99fb6819a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 15 Jul 2019 15:47:03 +0200 Subject: [PATCH 3067/3817] Copy licenses from go-ipfs This commit was moved from ipfs/go-filestore@87b7259b5615ca10a0cbf805bd70f24d4d099055 --- filestore/LICENSE-APACHE | 5 +++++ filestore/LICENSE-MIT | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 filestore/LICENSE-APACHE create mode 100644 filestore/LICENSE-MIT diff --git a/filestore/LICENSE-APACHE b/filestore/LICENSE-APACHE new file mode 100644 index 000000000..14478a3b6 --- /dev/null +++ b/filestore/LICENSE-APACHE @@ -0,0 +1,5 @@ +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/filestore/LICENSE-MIT b/filestore/LICENSE-MIT new file mode 100644 index 000000000..72dc60d84 --- /dev/null +++ b/filestore/LICENSE-MIT @@ -0,0 +1,19 @@ +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. From 92892ab8995b540e4c423ec7c704cbda772afbb4 Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Mon, 15 Jul 2019 14:42:40 -0700 Subject: [PATCH 3068/3817] nil exchange is okay This commit was moved from ipfs/go-blockservice@2ec77aa648213c2c7c2849116b9261a516c90b91 --- blockservice/blockservice.go | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 3b5a1df6b..9d8884936 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -148,8 +148,10 @@ func (s *blockService) AddBlock(o blocks.Block) error { log.Event(context.TODO(), "BlockService.BlockAdded", c) - if err := s.exchange.HasBlock(o); err != nil { - log.Errorf("HasBlock: %s", err.Error()) + if s.exchange != nil { + if err := s.exchange.HasBlock(o); err != nil { + log.Errorf("HasBlock: %s", err.Error()) + } } return nil @@ -184,10 +186,12 @@ func (s *blockService) AddBlocks(bs []blocks.Block) error { return err } - for _, o := range toput { - log.Event(context.TODO(), "BlockService.BlockAdded", o.Cid()) - if err := s.exchange.HasBlock(o); err != nil { - log.Errorf("HasBlock: %s", err.Error()) + if s.exchange != nil { + for _, o := range toput { + log.Event(context.TODO(), "BlockService.BlockAdded", o.Cid()) + if err := s.exchange.HasBlock(o); err != nil { + log.Errorf("HasBlock: %s", err.Error()) + } } } return nil @@ -250,7 +254,12 @@ func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget fun // the returned channel. // NB: No guarantees are made about order. func (s *blockService) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan blocks.Block { - return getBlocks(ctx, ks, s.blockstore, s.getExchange) // hash security + var f func() exchange.Fetcher + if s.exchange != nil { + f = s.getExchange + } + + return getBlocks(ctx, ks, s.blockstore, f) // hash security } func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget func() exchange.Fetcher) <-chan blocks.Block { @@ -285,7 +294,7 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget } } - if len(misses) == 0 { + if len(misses) == 0 || fget == nil { return } From b768808ef9740aad1019a757c9ebeeeba120c7d2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 11 Jul 2019 18:21:25 -0700 Subject: [PATCH 3069/3817] switch to new merkledag walk functions EnumerateChildrenAsync has been renamed to WalkParallel to reflect the fact that: 1. It visits the root. 2. It's parallel, not async. To mirror this change, EnumerateChildren has also been renamed to Walk and now behaves the same (except that it's not parallel). This commit was moved from ipfs/go-ipfs-pinner@9e2284b1a2b6ba65b8cc2dadd7f55514e2ba0433 --- pinning/pinner/gc/gc.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index bf8b7b10f..7190b1e01 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -170,10 +170,8 @@ func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots } for _, c := range roots { - set.Add(c) - - // EnumerateChildren recursively walks the dag and adds the keys to the given set - err := dag.EnumerateChildren(ctx, verifyGetLinks, c, set.Visit) + // Walk recursively walks the dag and adds the keys to the given set + err := dag.Walk(ctx, verifyGetLinks, c, set.Visit) if err != nil { err = verboseCidError(err) From d94f5ed9543f2bb79e82e9ff5f1e38eb604fffaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Thu, 18 Jul 2019 15:12:45 +0200 Subject: [PATCH 3070/3817] rework the graph walking functions with functional options This commit: - reduce the API to 2 simpler functions - add consistent and clear control over if the root should be visited - add control over the concurrency factor This commit was moved from ipfs/go-merkledag@1ee27334b5c01645e7a3d492bff4be9cffb0e991 --- ipld/merkledag/merkledag.go | 130 +++++++++++++++++++++---------- ipld/merkledag/merkledag_test.go | 11 ++- 2 files changed, 94 insertions(+), 47 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index c035dd424..253ee350e 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -162,7 +162,7 @@ func FetchGraph(ctx context.Context, root cid.Cid, serv ipld.DAGService) error { } // FetchGraphWithDepthLimit fetches all nodes that are children to the given -// node down to the given depth. maxDetph=0 means "only fetch root", +// node down to the given depth. maxDepth=0 means "only fetch root", // maxDepth=1 means "fetch root and its direct children" and so on... // maxDepth=-1 means unlimited. func FetchGraphWithDepthLimit(ctx context.Context, root cid.Cid, depthLim int, serv ipld.DAGService) error { @@ -195,9 +195,10 @@ func FetchGraphWithDepthLimit(ctx context.Context, root cid.Cid, depthLim int, s return false } + // If we have a ProgressTracker, we wrap the visit function to handle it v, _ := ctx.Value(progressContextKey).(*ProgressTracker) if v == nil { - return WalkParallelDepth(ctx, GetLinksDirect(ng), root, 0, visit) + return WalkDepth(ctx, GetLinksDirect(ng), root, visit, Concurrent(), WithRoot()) } visitProgress := func(c cid.Cid, depth int) bool { @@ -207,7 +208,7 @@ func FetchGraphWithDepthLimit(ctx context.Context, root cid.Cid, depthLim int, s } return false } - return WalkParallelDepth(ctx, GetLinksDirect(ng), root, 0, visitProgress) + return WalkDepth(ctx, GetLinksDirect(ng), root, visitProgress, Concurrent(), WithRoot()) } // GetMany gets many nodes from the DAG at once. @@ -281,21 +282,77 @@ func GetLinksWithDAG(ng ipld.NodeGetter) GetLinks { } } +// defaultConcurrentFetch is the default maximum number of concurrent fetches +// that 'fetchNodes' will start at a time +const defaultConcurrentFetch = 32 + +// WalkOptions represent the parameters of a graph walking algorithm +type WalkOptions struct { + WithRoot bool + IgnoreBadBlock bool + Concurrency int +} + +// WalkOption is a setter for WalkOptions +type WalkOption func(*WalkOptions) + +// WithRoot is a WalkOption indicating that the root node should be visited +func WithRoot() WalkOption { + return func(walkOptions *WalkOptions) { + walkOptions.WithRoot = true + } +} + +// Concurrent is a WalkOption indicating that node fetching should be done in +// parallel, with the default concurrency factor. +// NOTE: When using that option, the walk order is *not* guarantee. +// NOTE: It *does not* make multiple concurrent calls to the passed `visit` function. +func Concurrent() WalkOption { + return func(walkOptions *WalkOptions) { + walkOptions.Concurrency = defaultConcurrentFetch + } +} + +// Concurrency is a WalkOption indicating that node fetching should be done in +// parallel, with a specific concurrency factor. +// NOTE: When using that option, the walk order is *not* guarantee. +// NOTE: It *does not* make multiple concurrent calls to the passed `visit` function. +func Concurrency(worker int) WalkOption { + return func(walkOptions *WalkOptions) { + walkOptions.Concurrency = worker + } +} + // WalkGraph will walk the dag in order (depth first) starting at the given root. -func Walk(ctx context.Context, getLinks GetLinks, root cid.Cid, visit func(cid.Cid) bool) error { +func Walk(ctx context.Context, getLinks GetLinks, c cid.Cid, visit func(cid.Cid) bool, options ...WalkOption) error { visitDepth := func(c cid.Cid, depth int) bool { return visit(c) } - return WalkDepth(ctx, getLinks, root, 0, visitDepth) + return WalkDepth(ctx, getLinks, c, visitDepth, options...) } // WalkDepth walks the dag starting at the given root and passes the current // depth to a given visit function. The visit function can be used to limit DAG // exploration. -func WalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, depth int, visit func(cid.Cid, int) bool) error { - if !visit(root, depth) { - return nil +func WalkDepth(ctx context.Context, getLinks GetLinks, c cid.Cid, visit func(cid.Cid, int) bool, options ...WalkOption) error { + opts := &WalkOptions{} + for _, opt := range options { + opt(opts) + } + + if opts.Concurrency > 1 { + return parallelWalkDepth(ctx, getLinks, c, visit, opts) + } else { + return sequentialWalkDepth(ctx, getLinks, c, 0, visit, opts) + } +} + +func sequentialWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, depth int, visit func(cid.Cid, int) bool, options *WalkOptions) error { + if depth != 0 || options.WithRoot { + if !visit(root, depth) { + return nil + } } links, err := getLinks(ctx, root) @@ -304,7 +361,7 @@ func WalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, depth int, } for _, lnk := range links { - if err := WalkDepth(ctx, getLinks, lnk.Cid, depth+1, visit); err != nil { + if err := sequentialWalkDepth(ctx, getLinks, lnk.Cid, depth+1, visit, options); err != nil { return err } } @@ -337,27 +394,7 @@ func (p *ProgressTracker) Value() int { return p.Total } -// FetchGraphConcurrency is total number of concurrent fetches that -// 'fetchNodes' will start at a time -var FetchGraphConcurrency = 32 - -// WalkParallel is equivalent to Walk *except* that it explores multiple paths -// in parallel. -// -// NOTE: It *does not* make multiple concurrent calls to the passed `visit` function. -func WalkParallel(ctx context.Context, getLinks GetLinks, c cid.Cid, visit func(cid.Cid) bool) error { - visitDepth := func(c cid.Cid, depth int) bool { - return visit(c) - } - - return WalkParallelDepth(ctx, getLinks, c, 0, visitDepth) -} - -// WalkParallelDepth is equivalent to WalkDepth *except* that it fetches -// children in parallel. -// -// NOTE: It *does not* make multiple concurrent calls to the passed `visit` function. -func WalkParallelDepth(ctx context.Context, getLinks GetLinks, c cid.Cid, startDepth int, visit func(cid.Cid, int) bool) error { +func parallelWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, visit func(cid.Cid, int) bool, options *WalkOptions) error { type cidDepth struct { cid cid.Cid depth int @@ -372,14 +409,14 @@ func WalkParallelDepth(ctx context.Context, getLinks GetLinks, c cid.Cid, startD out := make(chan *linksDepth) done := make(chan struct{}) - var setlk sync.Mutex + var visitlk sync.Mutex var wg sync.WaitGroup errChan := make(chan error) fetchersCtx, cancel := context.WithCancel(ctx) defer wg.Wait() defer cancel() - for i := 0; i < FetchGraphConcurrency; i++ { + for i := 0; i < options.Concurrency; i++ { wg.Add(1) go func() { defer wg.Done() @@ -387,9 +424,16 @@ func WalkParallelDepth(ctx context.Context, getLinks GetLinks, c cid.Cid, startD ci := cdepth.cid depth := cdepth.depth - setlk.Lock() - shouldVisit := visit(ci, depth) - setlk.Unlock() + var shouldVisit bool + + // bypass the root if needed + if depth != 0 || options.WithRoot { + visitlk.Lock() + shouldVisit = visit(ci, depth) + visitlk.Unlock() + } else { + shouldVisit = true + } if shouldVisit { links, err := getLinks(ctx, ci) @@ -422,20 +466,21 @@ func WalkParallelDepth(ctx context.Context, getLinks GetLinks, c cid.Cid, startD defer close(feed) send := feed - var todobuffer []*cidDepth + var todoQueue []*cidDepth var inProgress int next := &cidDepth{ - cid: c, - depth: startDepth, + cid: root, + depth: 0, } + for { select { case send <- next: inProgress++ - if len(todobuffer) > 0 { - next = todobuffer[0] - todobuffer = todobuffer[1:] + if len(todoQueue) > 0 { + next = todoQueue[0] + todoQueue = todoQueue[1:] } else { next = nil send = nil @@ -456,7 +501,7 @@ func WalkParallelDepth(ctx context.Context, getLinks GetLinks, c cid.Cid, startD next = cd send = feed } else { - todobuffer = append(todobuffer, cd) + todoQueue = append(todoQueue, cd) } } case err := <-errChan: @@ -466,7 +511,6 @@ func WalkParallelDepth(ctx context.Context, getLinks GetLinks, c cid.Cid, startD return ctx.Err() } } - } var _ ipld.LinkGetter = &dagService{} diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index e56bb52d1..045a880ff 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -203,8 +203,11 @@ func makeTestDAG(t *testing.T, read io.Reader, ds ipld.DAGService) ipld.Node { // Add a root referencing all created nodes root := NodeWithData(nil) for _, n := range nodes { - root.AddNodeLink(n.Cid().String(), n) - err := ds.Add(ctx, n) + err := root.AddNodeLink(n.Cid().String(), n) + if err != nil { + t.Fatal(err) + } + err = ds.Add(ctx, n) if err != nil { t.Fatal(err) } @@ -383,7 +386,7 @@ func TestFetchGraphWithDepthLimit(t *testing.T) { } - err = WalkDepth(context.Background(), offlineDS.GetLinks, root.Cid(), 0, visitF) + err = WalkDepth(context.Background(), offlineDS.GetLinks, root.Cid(), visitF, WithRoot()) if err != nil { t.Fatal(err) } @@ -736,7 +739,7 @@ func TestEnumerateAsyncFailsNotFound(t *testing.T) { } cset := cid.NewSet() - err = WalkParallel(ctx, GetLinksDirect(ds), parent.Cid(), cset.Visit) + err = Walk(ctx, GetLinksDirect(ds), parent.Cid(), cset.Visit) if err == nil { t.Fatal("this should have failed") } From c678a2bacbab8543aca9fa237c1417d3f9230da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Thu, 18 Jul 2019 16:40:02 +0200 Subject: [PATCH 3071/3817] add a IgnoreErrors() walking option This commit was moved from ipfs/go-merkledag@86e5652465a6e36e6f92ea01d73bebf7eb050aaf --- ipld/merkledag/merkledag.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 253ee350e..6420e69e6 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -288,9 +288,9 @@ const defaultConcurrentFetch = 32 // WalkOptions represent the parameters of a graph walking algorithm type WalkOptions struct { - WithRoot bool - IgnoreBadBlock bool - Concurrency int + WithRoot bool + IgnoreErrors bool + Concurrency int } // WalkOption is a setter for WalkOptions @@ -323,6 +323,14 @@ func Concurrency(worker int) WalkOption { } } +// IgnoreErrors is a WalkOption indicating that the walk should attempt to +// continue even when an error occur. +func IgnoreErrors() WalkOption { + return func(walkOptions *WalkOptions) { + walkOptions.IgnoreErrors = true + } +} + // WalkGraph will walk the dag in order (depth first) starting at the given root. func Walk(ctx context.Context, getLinks GetLinks, c cid.Cid, visit func(cid.Cid) bool, options ...WalkOption) error { visitDepth := func(c cid.Cid, depth int) bool { @@ -356,7 +364,7 @@ func sequentialWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, d } links, err := getLinks(ctx, root) - if err != nil { + if err != nil && !options.IgnoreErrors { return err } @@ -437,7 +445,7 @@ func parallelWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, vis if shouldVisit { links, err := getLinks(ctx, ci) - if err != nil { + if err != nil && !options.IgnoreErrors { select { case errChan <- err: case <-fetchersCtx.Done(): From d86df8361481224d154164024584ea2d3df3e10d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Fri, 19 Jul 2019 12:49:23 +0200 Subject: [PATCH 3072/3817] more generalized/powerful error handling in the walk functions This commit was moved from ipfs/go-merkledag@27ea6f85c717d67b53caaef3c3e30b67c31b8ebe --- ipld/merkledag/merkledag.go | 82 ++++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 15 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 6420e69e6..568d44543 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -286,19 +286,29 @@ func GetLinksWithDAG(ng ipld.NodeGetter) GetLinks { // that 'fetchNodes' will start at a time const defaultConcurrentFetch = 32 -// WalkOptions represent the parameters of a graph walking algorithm -type WalkOptions struct { +// walkOptions represent the parameters of a graph walking algorithm +type walkOptions struct { WithRoot bool - IgnoreErrors bool Concurrency int + ErrorHandler func(c cid.Cid, err error) error } -// WalkOption is a setter for WalkOptions -type WalkOption func(*WalkOptions) +// WalkOption is a setter for walkOptions +type WalkOption func(*walkOptions) + +func (wo *walkOptions) addHandler(handler func(c cid.Cid, err error) error) { + if wo.ErrorHandler != nil { + wo.ErrorHandler = func(c cid.Cid, err error) error { + return handler(c, wo.ErrorHandler(c, err)) + } + } else { + wo.ErrorHandler = handler + } +} // WithRoot is a WalkOption indicating that the root node should be visited func WithRoot() WalkOption { - return func(walkOptions *WalkOptions) { + return func(walkOptions *walkOptions) { walkOptions.WithRoot = true } } @@ -308,7 +318,7 @@ func WithRoot() WalkOption { // NOTE: When using that option, the walk order is *not* guarantee. // NOTE: It *does not* make multiple concurrent calls to the passed `visit` function. func Concurrent() WalkOption { - return func(walkOptions *WalkOptions) { + return func(walkOptions *walkOptions) { walkOptions.Concurrency = defaultConcurrentFetch } } @@ -318,7 +328,7 @@ func Concurrent() WalkOption { // NOTE: When using that option, the walk order is *not* guarantee. // NOTE: It *does not* make multiple concurrent calls to the passed `visit` function. func Concurrency(worker int) WalkOption { - return func(walkOptions *WalkOptions) { + return func(walkOptions *walkOptions) { walkOptions.Concurrency = worker } } @@ -326,8 +336,44 @@ func Concurrency(worker int) WalkOption { // IgnoreErrors is a WalkOption indicating that the walk should attempt to // continue even when an error occur. func IgnoreErrors() WalkOption { - return func(walkOptions *WalkOptions) { - walkOptions.IgnoreErrors = true + return func(walkOptions *walkOptions) { + walkOptions.addHandler(func(c cid.Cid, err error) error { + return nil + }) + } +} + +// IgnoreMissing is a WalkOption indicating that the walk should continue when +// a node is missing. +func IgnoreMissing() WalkOption { + return func(walkOptions *walkOptions) { + walkOptions.addHandler(func(c cid.Cid, err error) error { + if err == ipld.ErrNotFound { + return nil + } + return err + }) + } +} + +// OnMissing is a WalkOption adding a callback that will be triggered on a missing +// node. +func OnMissing(callback func(c cid.Cid)) WalkOption { + return func(walkOptions *walkOptions) { + walkOptions.addHandler(func(c cid.Cid, err error) error { + if err == ipld.ErrNotFound { + callback(c) + } + return err + }) + } +} + +// OnError is a WalkOption adding a custom error handler. +// If this handler return a nil error, the walk will continue. +func OnError(handler func(c cid.Cid, err error) error) WalkOption { + return func(walkOptions *walkOptions) { + walkOptions.addHandler(handler) } } @@ -344,7 +390,7 @@ func Walk(ctx context.Context, getLinks GetLinks, c cid.Cid, visit func(cid.Cid) // depth to a given visit function. The visit function can be used to limit DAG // exploration. func WalkDepth(ctx context.Context, getLinks GetLinks, c cid.Cid, visit func(cid.Cid, int) bool, options ...WalkOption) error { - opts := &WalkOptions{} + opts := &walkOptions{} for _, opt := range options { opt(opts) } @@ -356,7 +402,7 @@ func WalkDepth(ctx context.Context, getLinks GetLinks, c cid.Cid, visit func(cid } } -func sequentialWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, depth int, visit func(cid.Cid, int) bool, options *WalkOptions) error { +func sequentialWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, depth int, visit func(cid.Cid, int) bool, options *walkOptions) error { if depth != 0 || options.WithRoot { if !visit(root, depth) { return nil @@ -364,7 +410,10 @@ func sequentialWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, d } links, err := getLinks(ctx, root) - if err != nil && !options.IgnoreErrors { + if err != nil && options.ErrorHandler != nil { + err = options.ErrorHandler(root, err) + } + if err != nil { return err } @@ -402,7 +451,7 @@ func (p *ProgressTracker) Value() int { return p.Total } -func parallelWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, visit func(cid.Cid, int) bool, options *WalkOptions) error { +func parallelWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, visit func(cid.Cid, int) bool, options *walkOptions) error { type cidDepth struct { cid cid.Cid depth int @@ -445,7 +494,10 @@ func parallelWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, vis if shouldVisit { links, err := getLinks(ctx, ci) - if err != nil && !options.IgnoreErrors { + if err != nil && options.ErrorHandler != nil { + err = options.ErrorHandler(root, err) + } + if err != nil { select { case errChan <- err: case <-fetchersCtx.Done(): From 7bd84ea3603603f88db6c5cc9d639d0d35c9851a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 22 Jul 2019 16:43:58 -0700 Subject: [PATCH 3073/3817] fix: parallel walk in gc & pin ls This commit was moved from ipfs/go-ipfs-pinner@a8c6bed75e1da9ca0c461bf772461340961db2e0 --- pinning/pinner/gc/gc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index 7190b1e01..ad3c4149d 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -171,7 +171,7 @@ func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots for _, c := range roots { // Walk recursively walks the dag and adds the keys to the given set - err := dag.Walk(ctx, verifyGetLinks, c, set.Visit) + err := dag.WalkParallel(ctx, verifyGetLinks, c, set.Visit) if err != nil { err = verboseCidError(err) From 368a8fcff94366a3f3ee2b983bcfb979c2f27c49 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Jul 2019 09:25:46 -0700 Subject: [PATCH 3074/3817] fix: include root in searches by default 1. Walk implies walking the entire graph. 2. Avoids a _silent_ breaking change. This commit was moved from ipfs/go-merkledag@f457eb47d10f28512595de1fc915947730a42452 --- ipld/merkledag/merkledag.go | 16 ++++++++-------- ipld/merkledag/merkledag_test.go | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 568d44543..07aae7884 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -198,7 +198,7 @@ func FetchGraphWithDepthLimit(ctx context.Context, root cid.Cid, depthLim int, s // If we have a ProgressTracker, we wrap the visit function to handle it v, _ := ctx.Value(progressContextKey).(*ProgressTracker) if v == nil { - return WalkDepth(ctx, GetLinksDirect(ng), root, visit, Concurrent(), WithRoot()) + return WalkDepth(ctx, GetLinksDirect(ng), root, visit, Concurrent()) } visitProgress := func(c cid.Cid, depth int) bool { @@ -208,7 +208,7 @@ func FetchGraphWithDepthLimit(ctx context.Context, root cid.Cid, depthLim int, s } return false } - return WalkDepth(ctx, GetLinksDirect(ng), root, visitProgress, Concurrent(), WithRoot()) + return WalkDepth(ctx, GetLinksDirect(ng), root, visitProgress, Concurrent()) } // GetMany gets many nodes from the DAG at once. @@ -288,7 +288,7 @@ const defaultConcurrentFetch = 32 // walkOptions represent the parameters of a graph walking algorithm type walkOptions struct { - WithRoot bool + SkipRoot bool Concurrency int ErrorHandler func(c cid.Cid, err error) error } @@ -306,10 +306,10 @@ func (wo *walkOptions) addHandler(handler func(c cid.Cid, err error) error) { } } -// WithRoot is a WalkOption indicating that the root node should be visited -func WithRoot() WalkOption { +// SkipRoot is a WalkOption indicating that the root node should skipped +func SkipRoot() WalkOption { return func(walkOptions *walkOptions) { - walkOptions.WithRoot = true + walkOptions.SkipRoot = true } } @@ -403,7 +403,7 @@ func WalkDepth(ctx context.Context, getLinks GetLinks, c cid.Cid, visit func(cid } func sequentialWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, depth int, visit func(cid.Cid, int) bool, options *walkOptions) error { - if depth != 0 || options.WithRoot { + if !(options.SkipRoot && depth == 0) { if !visit(root, depth) { return nil } @@ -484,7 +484,7 @@ func parallelWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, vis var shouldVisit bool // bypass the root if needed - if depth != 0 || options.WithRoot { + if !(options.SkipRoot && depth == 0) { visitlk.Lock() shouldVisit = visit(ci, depth) visitlk.Unlock() diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 045a880ff..83af3f02c 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -386,7 +386,7 @@ func TestFetchGraphWithDepthLimit(t *testing.T) { } - err = WalkDepth(context.Background(), offlineDS.GetLinks, root.Cid(), visitF, WithRoot()) + err = WalkDepth(context.Background(), offlineDS.GetLinks, root.Cid(), visitF) if err != nil { t.Fatal(err) } From a7f09407bf0712261d6aad697f7eb0775ebcf55b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Wed, 24 Jul 2019 12:33:53 +0200 Subject: [PATCH 3075/3817] update to the latest go-merkledag This commit was moved from ipfs/go-ipfs-provider@746e31eee0516d72f85c937d703a2e925500620d --- provider/simple/reprovide.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index 590b9d1ab..9b751f337 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -18,7 +18,7 @@ import ( var logR = logging.Logger("reprovider.simple") -//KeyChanFunc is function streaming CIDs to pass to content routing +// KeyChanFunc is function streaming CIDs to pass to content routing type KeyChanFunc func(context.Context) (<-chan cid.Cid, error) type doneFunc func(error) From f3b59bef5af97a250e7d8416a5ad7a05568ea52f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Wed, 24 Jul 2019 12:21:59 +0200 Subject: [PATCH 3076/3817] update the the last go-merkledag This commit was moved from ipfs/go-unixfs@e1cb36fcbf74a306e11e5b27525aedca3150f3c3 --- unixfs/hamt/hamt.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index c44b1789f..9bf67505b 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -330,7 +330,7 @@ func (ds *Shard) ForEachLink(ctx context.Context, f func(*ipld.Link) error) erro } // EnumLinksAsync returns a channel which will receive Links in the directory -// as they are enumerated, where order is not gauranteed +// as they are enumerated, where order is not guaranteed func (ds *Shard) EnumLinksAsync(ctx context.Context) <-chan format.LinkResult { linkResults := make(chan format.LinkResult) ctx, cancel := context.WithCancel(ctx) @@ -339,7 +339,7 @@ func (ds *Shard) EnumLinksAsync(ctx context.Context) <-chan format.LinkResult { defer cancel() getLinks := makeAsyncTrieGetLinks(ds.dserv, linkResults) cset := cid.NewSet() - err := dag.WalkParallel(ctx, getLinks, ds.cid, cset.Visit) + err := dag.Walk(ctx, getLinks, ds.cid, cset.Visit, dag.Concurrent()) if err != nil { emitResult(ctx, linkResults, format.LinkResult{Link: nil, Err: err}) } From 13ac30921b02e5c2604e67c44c1264d87a1d5f53 Mon Sep 17 00:00:00 2001 From: Ganesh Prasad Kumble <11145839+0zAND1z@users.noreply.github.com> Date: Thu, 25 Jul 2019 19:56:40 +0530 Subject: [PATCH 3077/3817] Update README.md - updated the travis build link This commit was moved from ipfs/go-mfs@0205117f00b6ad37d5b70dbd1e78d60c4957e124 --- mfs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/README.md b/mfs/README.md index 4beaa6612..20c6e4834 100644 --- a/mfs/README.md +++ b/mfs/README.md @@ -4,7 +4,7 @@ [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) [![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) [![GoDoc](https://godoc.org/github.com/ipfs/go-mfs?status.svg)](https://godoc.org/github.com/ipfs/go-mfs) -[![Build Status](https://travis-ci.org/ipfs/go-mfs.svg?branch=master)](https://travis-ci.org/ipfs/go-mfs) +[![Build Status](https://travis-ci.com/ipfs/go-mfs.svg?branch=master)](https://travis-ci.com/ipfs/go-mfs) > go-mfs implements an in-memory model of a mutable IPFS filesystem. From 6deac2d64808d009892a999cf14f684009ea6d92 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 25 Jul 2019 18:49:10 -0700 Subject: [PATCH 3078/3817] fix: return ErrLinkNotFound when the _link_ isn't found ipld.ErrNotFound should only be used when the underlying node isn't found This commit was moved from ipfs/go-merkledag@f48088f750700b4cc9d74ac357a73ee827d3a186 --- ipld/merkledag/node.go | 2 +- ipld/merkledag/node_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 09789040d..3478a02e0 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -138,7 +138,7 @@ func (n *ProtoNode) RemoveNodeLink(name string) error { } if !found { - return ipld.ErrNotFound + return ErrLinkNotFound } n.links = ref diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index 4ee59b93b..2ae75e774 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -59,7 +59,7 @@ func TestRemoveLink(t *testing.T) { // should fail err = nd.RemoveNodeLink("a") - if err != ipld.ErrNotFound { + if err != ErrLinkNotFound { t.Fatal("should have failed to remove link") } From bf9239fe1bf9f3b6e8014900912437e42ad9b5d2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 25 Jul 2019 19:06:15 -0700 Subject: [PATCH 3079/3817] fix: return the correct error from RemoveChild This commit was moved from ipfs/go-unixfs@c5e8d46322d98839db850f1031824ab1cb0ed912 --- unixfs/hamt/hamt.go | 3 ++- unixfs/io/directory.go | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 9bf67505b..6b89b47c7 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -221,7 +221,8 @@ func (ds *Shard) Set(ctx context.Context, name string, nd ipld.Node) error { return ds.modifyValue(ctx, hv, name, lnk) } -// Remove deletes the named entry if it exists, this operation is idempotent. +// Remove deletes the named entry if it exists. Otherwise, it returns +// os.ErrNotExist. func (ds *Shard) Remove(ctx context.Context, name string) error { hv := &hashBits{b: hash([]byte(name))} return ds.modifyValue(ctx, hv, name, nil) diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index 2e0227623..f773704a2 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -48,9 +48,13 @@ type Directory interface { // Find returns the root node of the file named 'name' within this directory. // In the case of HAMT-directories, it will traverse the tree. + // + // Returns os.ErrNotExist if the child does not exist. Find(context.Context, string) (ipld.Node, error) // RemoveChild removes the child with the given name. + // + // Returns os.ErrNotExist if the child doesn't exist. RemoveChild(context.Context, string) error // GetNode returns the root of this directory. @@ -196,7 +200,11 @@ func (d *BasicDirectory) Find(ctx context.Context, name string) (ipld.Node, erro // RemoveChild implements the `Directory` interface. func (d *BasicDirectory) RemoveChild(ctx context.Context, name string) error { - return d.node.RemoveNodeLink(name) + err := d.node.RemoveNodeLink(name) + if err == mdag.ErrLinkNotFound { + err = os.ErrNotExist + } + return err } // GetNode implements the `Directory` interface. From 30fa491fc1f3a52ee6320ffcd5d956359275eee2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 25 Jul 2019 19:26:06 -0700 Subject: [PATCH 3080/3817] fix: update for merkledag API changes This commit was moved from ipfs/go-ipfs-pinner@91a73c74183f5d257b2d5d1eaecbad8bc280f199 --- pinning/pinner/gc/gc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index ad3c4149d..e03072770 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -171,7 +171,7 @@ func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots for _, c := range roots { // Walk recursively walks the dag and adds the keys to the given set - err := dag.WalkParallel(ctx, verifyGetLinks, c, set.Visit) + err := dag.Walk(ctx, verifyGetLinks, c, set.Visit, dag.Concurrent()) if err != nil { err = verboseCidError(err) From 496f766cadb55f559163e108500c43471467b086 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 1 Aug 2019 13:04:15 -0700 Subject: [PATCH 3081/3817] fix: slightly reduce memory usage when walking large directory trees This commit was moved from ipfs/go-merkledag@663be66fae93fccec98d63128c8b8d498609010a --- ipld/merkledag/merkledag.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 07aae7884..1f5bcb4e3 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -462,8 +462,8 @@ func parallelWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, vis depth int } - feed := make(chan *cidDepth) - out := make(chan *linksDepth) + feed := make(chan cidDepth) + out := make(chan linksDepth) done := make(chan struct{}) var visitlk sync.Mutex @@ -505,7 +505,7 @@ func parallelWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, vis return } - outLinks := &linksDepth{ + outLinks := linksDepth{ links: links, depth: depth + 1, } @@ -526,10 +526,10 @@ func parallelWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, vis defer close(feed) send := feed - var todoQueue []*cidDepth + var todoQueue []cidDepth var inProgress int - next := &cidDepth{ + next := cidDepth{ cid: root, depth: 0, } @@ -542,22 +542,22 @@ func parallelWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, vis next = todoQueue[0] todoQueue = todoQueue[1:] } else { - next = nil + next = cidDepth{} send = nil } case <-done: inProgress-- - if inProgress == 0 && next == nil { + if inProgress == 0 && !next.cid.Defined() { return nil } case linksDepth := <-out: for _, lnk := range linksDepth.links { - cd := &cidDepth{ + cd := cidDepth{ cid: lnk.Cid, depth: linksDepth.depth, } - if next == nil { + if !next.cid.Defined() { next = cd send = feed } else { From 98cec339880e00290049245fc50acf6fc2874701 Mon Sep 17 00:00:00 2001 From: Cole Brown Date: Fri, 2 Aug 2019 21:09:17 -0400 Subject: [PATCH 3082/3817] Bump go-libp2p-core, up test key size to 2048 This commit was moved from ipfs/interface-go-ipfs-core@6ba366dd626d3b605a67d7ade24b3065bc4d9694 --- coreiface/tests/key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/tests/key.go b/coreiface/tests/key.go index e3461f971..265a8f060 100644 --- a/coreiface/tests/key.go +++ b/coreiface/tests/key.go @@ -147,7 +147,7 @@ func (tp *TestSuite) TestGenerateSize(t *testing.T) { t.Fatal(err) } - k, err := api.Key().Generate(ctx, "foo", opt.Key.Size(1024)) + k, err := api.Key().Generate(ctx, "foo", opt.Key.Size(2048)) if err != nil { t.Fatal(err) return From 1c203d9d620ee407bbe372e4025dc98ac53dc47f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 20 Aug 2019 08:59:45 -0700 Subject: [PATCH 3083/3817] cache: switch to 2q Due to patent concerns in closed-source downstream products: https://github.com/ipfs/go-ipfs/issues/6590 This commit was moved from ipfs/go-ipfs-blockstore@82da4c45720e76cd2831e8c6f1c9c145d7f76803 --- blockstore/arc_cache.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 231fd8555..ca491e532 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -16,7 +16,7 @@ type cacheSize int // block Cids. This provides block access-time improvements, allowing // to short-cut many searches without query-ing the underlying datastore. type arccache struct { - arc *lru.ARCCache + arc *lru.TwoQueueCache blockstore Blockstore hits metrics.Counter @@ -24,7 +24,7 @@ type arccache struct { } func newARCCachedBS(ctx context.Context, bs Blockstore, lruSize int) (*arccache, error) { - arc, err := lru.NewARC(lruSize) + arc, err := lru.New2Q(lruSize) if err != nil { return nil, err } From 0e2dd27b7657379f7837e02bfe2bacb2bb132eb2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 20 Aug 2019 13:39:54 -0700 Subject: [PATCH 3084/3817] chore: fmt This commit was moved from ipfs/go-ipfs-blockstore@91f191931176d9cd06c6092c25b19c2c13b7055d --- blockstore/bloom_cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 3720d20d8..f99bb2b4b 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -148,7 +148,7 @@ func (b *bloomcache) Has(k cid.Cid) (bool, error) { } func (b *bloomcache) GetSize(k cid.Cid) (int, error) { - return b.blockstore.GetSize(k) + return b.blockstore.GetSize(k) } func (b *bloomcache) Get(k cid.Cid) (blocks.Block, error) { From d507153cd7d2fd3adf1300706d5ee94bfd3f4327 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 20 Aug 2019 13:40:26 -0700 Subject: [PATCH 3085/3817] chore: remove gx support This commit was moved from ipfs/go-ipfs-blockstore@b40a71c749f24ccefced7e410dcfd69c7471838b --- blockstore/Makefile | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 blockstore/Makefile diff --git a/blockstore/Makefile b/blockstore/Makefile deleted file mode 100644 index 73f2841f6..000000000 --- a/blockstore/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -all: deps -gx: - go get github.com/whyrusleeping/gx - go get github.com/whyrusleeping/gx-go -deps: gx - gx --verbose install --global - gx-go rewrite -test: deps - gx test -v -race -coverprofile=coverage.txt -covermode=atomic . -rw: - gx-go rewrite -rwundo: - gx-go rewrite --undo -publish: rwundo - gx publish -.PHONY: all gx deps test rw rwundo publish - - From 1a40a254afa44a5d8e1dddf8c2396e0c622c2e91 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 23 Aug 2019 11:26:49 -0700 Subject: [PATCH 3086/3817] dep: update bbloom again This commit was moved from ipfs/go-ipfs-blockstore@f9647c539cdc21456734b5b352d73c14cf39aca6 --- blockstore/bloom_cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index f99bb2b4b..bd3c611c3 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -51,7 +51,7 @@ func bloomCached(ctx context.Context, bs Blockstore, bloomSize, hashCount int) ( case <-ctx.Done(): return case <-t.C: - fill.Set(bc.bloom.FillRatio()) + fill.Set(bc.bloom.FillRatioTS()) } } } From 218207c69acbca4ce5de08d10325c0eaf43c30ce Mon Sep 17 00:00:00 2001 From: Cole Brown Date: Fri, 2 Aug 2019 21:35:00 -0400 Subject: [PATCH 3087/3817] Update go-libp2p, fix tests with weak RSA keys This commit was moved from ipfs/go-namesys@f6aa2bd8857dc44ebd1ebf590f09a6359e3da7bd --- namesys/ipns_resolver_validation_test.go | 13 +++--- namesys/namesys_test.go | 2 +- namesys/resolve_test.go | 54 ++++++------------------ 3 files changed, 20 insertions(+), 49 deletions(-) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 9eed8375b..76cd22365 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,11 +5,12 @@ import ( "testing" "time" + "github.com/libp2p/go-libp2p-core/test" + ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" mockrouting "github.com/ipfs/go-ipfs-routing/mock" offline "github.com/ipfs/go-ipfs-routing/offline" - u "github.com/ipfs/go-ipfs-util" ipns "github.com/ipfs/go-ipns" path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" @@ -137,19 +138,15 @@ func TestResolverValidation(t *testing.T) { } func genKeys(t *testing.T) (ci.PrivKey, peer.ID, string, string) { - sr := u.NewTimeSeededRand() - priv, _, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, sr) + sk, pk, err := test.RandTestKeyPair(ci.RSA, 2048) if err != nil { t.Fatal(err) } - - // Create entry with expiry in one hour - pid, err := peer.IDFromPrivateKey(priv) + id, err := peer.IDFromPublicKey(pk) if err != nil { t.Fatal(err) } - - return priv, pid, PkKeyForID(pid), ipns.RecordKey(pid) + return sk, id, PkKeyForID(id), ipns.RecordKey(id) } type mockValueStore struct { diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index d1ecf49e8..4da3b17cb 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -88,7 +88,7 @@ func TestNamesysResolution(t *testing.T) { func TestPublishWithCache0(t *testing.T) { dst := dssync.MutexWrap(ds.NewMapDatastore()) - priv, _, err := ci.GenerateKeyPair(ci.RSA, 1024) + priv, _, err := ci.GenerateKeyPair(ci.RSA, 2048) if err != nil { t.Fatal(err) } diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 814bf5973..4f92a2d0d 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -11,10 +11,8 @@ import ( mockrouting "github.com/ipfs/go-ipfs-routing/mock" ipns "github.com/ipfs/go-ipns" path "github.com/ipfs/go-path" - ci "github.com/libp2p/go-libp2p-core/crypto" - peer "github.com/libp2p/go-libp2p-core/peer" - test "github.com/libp2p/go-libp2p-core/test" testutil "github.com/libp2p/go-libp2p-testing/net" + tnet "github.com/libp2p/go-libp2p-testing/net" ) func TestRoutingResolve(t *testing.T) { @@ -26,23 +24,15 @@ func TestRoutingResolve(t *testing.T) { resolver := NewIpnsResolver(d) publisher := NewIpnsPublisher(d, dstore) - privk, pubk, err := test.RandTestKeyPair(ci.RSA, 512) - if err != nil { - t.Fatal(err) - } + identity := tnet.RandIdentityOrFatal(t) h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN") - err = publisher.Publish(context.Background(), privk, h) + err := publisher.Publish(context.Background(), identity.PrivateKey(), h) if err != nil { t.Fatal(err) } - pid, err := peer.IDFromPublicKey(pubk) - if err != nil { - t.Fatal(err) - } - - res, err := resolver.Resolve(context.Background(), pid.Pretty()) + res, err := resolver.Resolve(context.Background(), identity.ID().Pretty()) if err != nil { t.Fatal(err) } @@ -59,36 +49,28 @@ func TestPrexistingExpiredRecord(t *testing.T) { resolver := NewIpnsResolver(d) publisher := NewIpnsPublisher(d, dstore) - privk, pubk, err := test.RandTestKeyPair(ci.RSA, 512) - if err != nil { - t.Fatal(err) - } - - id, err := peer.IDFromPublicKey(pubk) - if err != nil { - t.Fatal(err) - } + identity := tnet.RandIdentityOrFatal(t) // Make an expired record and put it in the datastore h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN") eol := time.Now().Add(time.Hour * -1) - entry, err := ipns.Create(privk, []byte(h), 0, eol) + entry, err := ipns.Create(identity.PrivateKey(), []byte(h), 0, eol) if err != nil { t.Fatal(err) } - err = PutRecordToRouting(context.Background(), d, pubk, entry) + err = PutRecordToRouting(context.Background(), d, identity.PublicKey(), entry) if err != nil { t.Fatal(err) } // Now, with an old record in the system already, try and publish a new one - err = publisher.Publish(context.Background(), privk, h) + err = publisher.Publish(context.Background(), identity.PrivateKey(), h) if err != nil { t.Fatal(err) } - err = verifyCanResolve(resolver, id.Pretty(), h) + err = verifyCanResolve(resolver, identity.ID().Pretty(), h) if err != nil { t.Fatal(err) } @@ -101,35 +83,27 @@ func TestPrexistingRecord(t *testing.T) { resolver := NewIpnsResolver(d) publisher := NewIpnsPublisher(d, dstore) - privk, pubk, err := test.RandTestKeyPair(ci.RSA, 512) - if err != nil { - t.Fatal(err) - } - - id, err := peer.IDFromPublicKey(pubk) - if err != nil { - t.Fatal(err) - } + identity := tnet.RandIdentityOrFatal(t) // Make a good record and put it in the datastore h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN") eol := time.Now().Add(time.Hour) - entry, err := ipns.Create(privk, []byte(h), 0, eol) + entry, err := ipns.Create(identity.PrivateKey(), []byte(h), 0, eol) if err != nil { t.Fatal(err) } - err = PutRecordToRouting(context.Background(), d, pubk, entry) + err = PutRecordToRouting(context.Background(), d, identity.PublicKey(), entry) if err != nil { t.Fatal(err) } // Now, with an old record in the system already, try and publish a new one - err = publisher.Publish(context.Background(), privk, h) + err = publisher.Publish(context.Background(), identity.PrivateKey(), h) if err != nil { t.Fatal(err) } - err = verifyCanResolve(resolver, id.Pretty(), h) + err = verifyCanResolve(resolver, identity.ID().Pretty(), h) if err != nil { t.Fatal(err) } From 042f8534001f0fd55474615cadec1b05ccaa75bc Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 23 Aug 2019 14:27:59 -0700 Subject: [PATCH 3088/3817] chore: fix import grouping This commit was moved from ipfs/go-namesys@87436bfad00748844aec8ffa9f0844eb64845383 --- namesys/ipns_resolver_validation_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 76cd22365..1fd7488b9 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -5,8 +5,6 @@ import ( "testing" "time" - "github.com/libp2p/go-libp2p-core/test" - ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" mockrouting "github.com/ipfs/go-ipfs-routing/mock" @@ -18,6 +16,7 @@ import ( peer "github.com/libp2p/go-libp2p-core/peer" pstore "github.com/libp2p/go-libp2p-core/peerstore" routing "github.com/libp2p/go-libp2p-core/routing" + "github.com/libp2p/go-libp2p-core/test" pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem" record "github.com/libp2p/go-libp2p-record" testutil "github.com/libp2p/go-libp2p-testing/net" From 3b9a723861fa7b8069ac6a0c698f83a8b6e82237 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 23 Aug 2019 16:22:22 -0700 Subject: [PATCH 3089/3817] test: fix put with hash test We just changed ID/"id" to IDENTITY/"identity" to match the multicodec table and avoid confusion with peer IDs, CIDs, etc. Unfortunately, this breaks the `--hash=id` flag. Luckily, I'm pretty sure nobody's actually using this. As putting a block with an identity hash is useless. If they are users, we can go about adding in backwards compatibility hacks later. This commit was moved from ipfs/interface-go-ipfs-core@6ebdbe7ef3eadc7f832592abb3b1b151d9b4febf --- coreiface/tests/dag.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index 1ccd45d59..2f68bbf05 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -71,7 +71,7 @@ func (tp *TestSuite) TestPutWithHash(t *testing.T) { t.Fatal(err) } - nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), mh.ID, -1) + nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), mh.SHA3_256, -1) if err != nil { t.Fatal(err) } @@ -81,7 +81,7 @@ func (tp *TestSuite) TestPutWithHash(t *testing.T) { t.Fatal(err) } - if nd.Cid().String() != "bafyqabtfjbswy3dp" { + if nd.Cid().String() != "bafyrmifu7haikttpqqgc5ewvmp76z3z4ebp7h2ph4memw7dq4nt6btmxny" { t.Errorf("got wrong cid: %s", nd.Cid().String()) } } From 37112eccdef45f4825447149539071493a3a8001 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 23 Aug 2019 18:05:12 -0700 Subject: [PATCH 3090/3817] dep: update go-datastore Deletes are now idempotent. This commit was moved from ipfs/go-ipfs-blockstore@9666db5dad64c8da12a29ba8e39a30b95feb661c --- blockstore/arc_cache.go | 9 +++------ blockstore/arc_cache_test.go | 4 ++-- blockstore/blockstore.go | 6 +----- blockstore/bloom_cache.go | 2 +- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index ca491e532..b2ba82105 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -37,18 +37,15 @@ func newARCCachedBS(ctx context.Context, bs Blockstore, lruSize int) (*arccache, func (b *arccache) DeleteBlock(k cid.Cid) error { if has, _, ok := b.hasCached(k); ok && !has { - return ErrNotFound + return nil } b.arc.Remove(k) // Invalidate cache before deleting. err := b.blockstore.DeleteBlock(k) - switch err { - case nil, ErrNotFound: + if err == nil { b.cacheHave(k, false) - return err - default: - return err } + return err } // if ok == false has is inconclusive diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 6911db769..b72c84853 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -139,8 +139,8 @@ func TestGetAndDeleteFalseShortCircuit(t *testing.T) { t.Fatal("get returned invalid result") } - if arc.DeleteBlock(exampleBlock.Cid()) != ErrNotFound { - t.Fatal("expected ErrNotFound error") + if arc.DeleteBlock(exampleBlock.Cid()) != nil { + t.Fatal("expected deletes to be idempotent") } } diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index f57a90af6..03004b592 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -186,11 +186,7 @@ func (bs *blockstore) GetSize(k cid.Cid) (int, error) { } func (bs *blockstore) DeleteBlock(k cid.Cid) error { - err := bs.datastore.Delete(dshelp.CidToDsKey(k)) - if err == ds.ErrNotFound { - return ErrNotFound - } - return err + return bs.datastore.Delete(dshelp.CidToDsKey(k)) } // AllKeysChan runs a query for keys from the blockstore. diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index bd3c611c3..6e28ecec6 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -113,7 +113,7 @@ func (b *bloomcache) build(ctx context.Context) error { func (b *bloomcache) DeleteBlock(k cid.Cid) error { if has, ok := b.hasCached(k); ok && !has { - return ErrNotFound + return nil } return b.blockstore.DeleteBlock(k) From 500a693599d25bfb30c0b15d88d22f9dffa6acce Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 2 Sep 2019 08:53:23 -0700 Subject: [PATCH 3091/3817] readme: add a lead maintainer See: https://github.com/ipfs/team-mgmt/blob/master/LEAD_MAINTAINER_PROTOCOL.md This commit was moved from ipfs/go-ipns@1255134543a382caa20d14f3ef5418f4f28e36b0 --- ipns/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ipns/README.md b/ipns/README.md index 2ae0846a1..eb1158e2a 100644 --- a/ipns/README.md +++ b/ipns/README.md @@ -10,6 +10,10 @@ This package contains all of the components necessary to create, understand, and validate IPNS records. It does *not* publish or resolve those records. [`go-ipfs`](https://github.com/ipfs/go-ipfs) uses this package internally to manipulate records. +## Lead Maintainer + +[Adin Schmahmann](https://github.com/aschmahmann) + ## Usage To create a new IPNS record: From 437c65f17ea9c5d9b4754841752677f7047629b3 Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Sun, 8 Sep 2019 12:53:13 -0700 Subject: [PATCH 3092/3817] demote warning to debug log This commit was moved from ipfs/go-blockservice@1a0d1fd7edcaa41da7829ff7064be29d6cc0053a --- blockservice/blockservice.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 5486c0e85..ba0ab4183 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -72,7 +72,7 @@ type blockService struct { // NewBlockService creates a BlockService with given datastore instance. func New(bs blockstore.Blockstore, rem exchange.Interface) BlockService { if rem == nil { - log.Warning("blockservice running in local (offline) mode.") + log.Debug("blockservice running in local (offline) mode.") } return &blockService{ @@ -86,7 +86,7 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) BlockService { // through to the blockstore and are not skipped by cache checks. func NewWriteThrough(bs blockstore.Blockstore, rem exchange.Interface) BlockService { if rem == nil { - log.Warning("blockservice running in local (offline) mode.") + log.Debug("blockservice running in local (offline) mode.") } return &blockService{ From a8c5ec606246224d3f0dfadfc627a9f90d9b53df Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 10 Sep 2019 18:23:14 -0700 Subject: [PATCH 3093/3817] test: test ReadAt if implemented (I plan on adding support to the http client, at least) This commit was moved from ipfs/interface-go-ipfs-core@ae838686170af209e0a9b29aa1a59b64346b5de1 --- coreiface/tests/unixfs.go | 82 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 47ce505c8..aac7fa92f 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -48,6 +48,7 @@ func (tp *TestSuite) TestUnixfs(t *testing.T) { t.Run("TestLsNonUnixfs", tp.TestLsNonUnixfs) t.Run("TestAddCloses", tp.TestAddCloses) t.Run("TestGetSeek", tp.TestGetSeek) + t.Run("TestGetReadAt", tp.TestGetReadAt) } // `echo -n 'hello, world!' | ipfs add` @@ -996,3 +997,84 @@ func (tp *TestSuite) TestGetSeek(t *testing.T) { test(dataSize-50, io.SeekStart, 100, 50, true) test(-5, io.SeekEnd, 100, 5, true) } + +func (tp *TestSuite) TestGetReadAt(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + dataSize := int64(100000) + tf := files.NewReaderFile(io.LimitReader(rand.New(rand.NewSource(1403768328)), dataSize)) + + p, err := api.Unixfs().Add(ctx, tf, options.Unixfs.Chunker("size-100")) + if err != nil { + t.Fatal(err) + } + + r, err := api.Unixfs().Get(ctx, p) + if err != nil { + t.Fatal(err) + } + + f, ok := r.(interface { + files.File + io.ReaderAt + }) + if !ok { + t.Skip("ReaderAt not implemented") + } + + orig := make([]byte, dataSize) + if _, err := io.ReadFull(f, orig); err != nil { + t.Fatal(err) + } + f.Close() + + origR := bytes.NewReader(orig) + + r, err = api.Unixfs().Get(ctx, p) + if err != nil { + t.Fatal(err) + } + + test := func(offset int64, read int, expect int64, shouldEof bool) { + t.Run(fmt.Sprintf("readat%d-r%d-%d", offset, read, expect), func(t *testing.T) { + origBuf := make([]byte, read) + origRead, err := origR.ReadAt(origBuf, offset) + if err != nil && err != io.EOF { + t.Fatalf("orig: %s", err) + } + buf := make([]byte, read) + r, err := f.ReadAt(buf, offset) + if shouldEof { + if err != io.EOF { + t.Fatal("expected EOF, got: ", err) + } + } else if err != nil { + t.Fatal("got: ", err) + } + + if int64(r) != expect { + t.Fatal("read wrong amount of data") + } + if r != origRead { + t.Fatal("read different amount of data than bytes.Reader") + } + if !bytes.Equal(buf, origBuf) { + fmt.Fprintf(os.Stderr, "original:\n%s\n", hex.Dump(origBuf)) + fmt.Fprintf(os.Stderr, "got:\n%s\n", hex.Dump(buf)) + t.Fatal("data didn't match") + } + }) + } + + test(3, 10, 10, false) + test(13, 10, 10, false) + test(513, 10, 10, false) + test(350, 100, 100, false) + test(0, int(dataSize), dataSize, false) + test(dataSize-50, 100, 50, true) +} From a242bd1bea25e20c710696536c76791e79723fb6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 18 Sep 2019 22:00:46 -0700 Subject: [PATCH 3094/3817] doc: add a lead maintainer This commit was moved from ipfs/go-ipfs-util@6b60f8d74b09bac9d7bef7a7ffb795efff749c0a --- util/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/README.md b/util/README.md index 33bff12cd..92619b477 100644 --- a/util/README.md +++ b/util/README.md @@ -8,6 +8,10 @@ > Common utilities used by go-ipfs and other related go packages +## Lead Maintainer + +[Steven Allen](https://github.com/Stebalien) + ## Install This is a Go module which can be installed with `go get github.com/ipfs/go-ipfs-util`. `go-ipfs-util` is however packaged with Gx, so it is recommended to use Gx to install it (see Usage section). From d51e93e43d47e942c1266242ab56b478e15eb135 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 18 Sep 2019 22:14:49 -0700 Subject: [PATCH 3095/3817] doc: add a lead maintainer This commit was moved from ipfs/go-blockservice@4f137b54d1697542e5e47bcd9bed2d8bad5f0616 --- blockservice/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blockservice/README.md b/blockservice/README.md index 3df67fdec..11d814af2 100644 --- a/blockservice/README.md +++ b/blockservice/README.md @@ -9,6 +9,9 @@ go-blockservice > go-blockservice provides a seamless interface to both local and remote storage backends. +## Lead Maintainer + +[Steven Allen](https://github.com/Stebalien) ## Table of Contents From 116d2eb66cfc2c671467b8d3e16435f1055ef747 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 18 Sep 2019 22:17:42 -0700 Subject: [PATCH 3096/3817] doc: add a lead maintainer This commit was moved from ipfs/go-ipfs-exchange-interface@aab2e33aa212746a05fc05ca0433428c69ce0d8c --- exchange/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/exchange/README.md b/exchange/README.md index 8dbcfe1c3..563ca6a27 100644 --- a/exchange/README.md +++ b/exchange/README.md @@ -8,6 +8,10 @@ > go-ipfs-exchange-interface defines the IPFS exchange interface +## Lead Maintainer + +[Steven Allen](https://github.com/Stebalien) + ## Table of Contents - [Install](#install) From bc81914caf95d4aa5a4eb372085e5b13d3ac539b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 18 Sep 2019 22:18:57 -0700 Subject: [PATCH 3097/3817] doc: add a lead maintainer This commit was moved from ipfs/go-ipfs-blockstore@e4a281ad7e051428514dcf7bd7758abff54f4fc2 --- blockstore/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/blockstore/README.md b/blockstore/README.md index 446a95e25..e63410183 100644 --- a/blockstore/README.md +++ b/blockstore/README.md @@ -8,6 +8,11 @@ > go-ipfs-blockstore implements a thin wrapper over a datastore, giving a clean interface for Getting and Putting block objects. +## Lead Maintainer + +[Steven Allen](https://github.com/Stebalien) + + ## Table of Contents - [Install](#install) From 5ec56f46a2932dfd1d7dd0006386cf6a88abe279 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 18 Sep 2019 22:20:34 -0700 Subject: [PATCH 3098/3817] doc: add a lead maintainer This commit was moved from ipfs/go-ipfs-chunker@f9e6481534099152243fd8cbf436198843a2aecd --- chunker/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/chunker/README.md b/chunker/README.md index 96faa3de3..84161c546 100644 --- a/chunker/README.md +++ b/chunker/README.md @@ -12,6 +12,10 @@ The package provides a `SizeSplitter` which creates chunks of equal size and it is used by default in most cases, and a `rabin` fingerprint chunker. This chunker will attempt to split data in a way that the resulting blocks are the same when the data has repetitive patterns, thus optimizing the resulting DAGs. +## Lead Maintainer + +[Steven Allen](https://github.com/Stebalien) + ## Table of Contents - [Install](#install) From cbfeae5cbc430bd36168fd4889962fc15831bfd6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 18 Sep 2019 22:22:25 -0700 Subject: [PATCH 3099/3817] doc: add a lead maintainer This commit was moved from ipfs/go-mfs@a4d6b7f53d7e78609d438997ca64bbba841eff2b --- mfs/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mfs/README.md b/mfs/README.md index 20c6e4834..1688a1ff7 100644 --- a/mfs/README.md +++ b/mfs/README.md @@ -8,6 +8,10 @@ > go-mfs implements an in-memory model of a mutable IPFS filesystem. +## Lead Maintainer + +[Steven Allen](https://github.com/Stebalien) + ## Table of Contents - [Install](#install) From 4dbca3f8132ba92529ffcf76a472ed648ab6b1f8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 18 Sep 2019 22:22:49 -0700 Subject: [PATCH 3100/3817] doc: add a lead maintainer This commit was moved from ipfs/go-unixfs@5567923f05b59bfa0a93b87f6db96c397920666d --- unixfs/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/unixfs/README.md b/unixfs/README.md index 9a22fbd3a..4fd85e4f9 100644 --- a/unixfs/README.md +++ b/unixfs/README.md @@ -9,6 +9,9 @@ go-unixfs > go-unixfs implements unix-like filesystem utilities on top of an ipld merkledag +## Lead Maintainer + +[Steven Allen](https://github.com/Stebalien) ## Table of Contents From 50ed821830864b091eab0e0a881cce2b157e2bf9 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 18 Sep 2019 22:23:17 -0700 Subject: [PATCH 3101/3817] doc: add a lead maintainer This commit was moved from ipfs/go-merkledag@3ecad70614944b3a157a2b13e1eab82be879439a --- ipld/merkledag/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ipld/merkledag/README.md b/ipld/merkledag/README.md index 8bf135a4f..e099f3a90 100644 --- a/ipld/merkledag/README.md +++ b/ipld/merkledag/README.md @@ -9,7 +9,9 @@ go-merkledag > go-merkledag implements the 'DAGService' interface and adds two ipld node types, Protobuf and Raw +## Lead Maintainer +[Steven Allen](https://github.com/Stebalien) ## Table of Contents From 2285f7d555878c455c38528e37d427d9a57d18a7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 18 Sep 2019 22:27:03 -0700 Subject: [PATCH 3102/3817] README: stub This commit was moved from ipfs/interface-go-ipfs-core@bfec0ce2ae7f0a693589ebb68110b68e80c2bc7a --- coreiface/README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 coreiface/README.md diff --git a/coreiface/README.md b/coreiface/README.md new file mode 100644 index 000000000..60ea79cae --- /dev/null +++ b/coreiface/README.md @@ -0,0 +1,33 @@ +interface-go-ipfs-core +================== + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.io/) +[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs) +[![Coverage Status](https://codecov.io/gh/ipfs/interface-go-ipfs-core/branch/master/graph/badge.svg)](https://codecov.io/gh/ipfs/interface-go-ipfs-core/branch/master) + +> The CoreAPI interfaces for go-ipfs. + +## Lead Maintainer + +[Steven Allen](https://github.com/Stebalien) + +## Table of Contents + +- [Background](#background) +- [Contribute](#contribute) +- [License](#license) + +## Documentation + +TODO + +## Contribute + +PRs are welcome! + +Small note: If editing the Readme, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +MIT © Protocol Labs From c32b81c4184e0730972b7617e45b1505cafbd68a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 18 Sep 2019 22:29:14 -0700 Subject: [PATCH 3103/3817] doc: add a lead maintainer This commit was moved from ipfs/go-path@472cfe2c2b4c89542b460764dfc782b7be1805bd --- path/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/path/README.md b/path/README.md index 79dd92892..3edbd8c5d 100644 --- a/path/README.md +++ b/path/README.md @@ -9,6 +9,9 @@ go-path > go-path is a helper package that provides utilities for parsing and using ipfs paths +## Lead Maintainer + +[Steven Allen](https://github.com/Stebalien) ## Table of Contents From 5821710d0b24df88596eb3bbefb4ae9943b36815 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 23 Sep 2019 14:05:37 -0700 Subject: [PATCH 3104/3817] namesys: set the correct cache TTL on publish fixes https://github.com/ipfs/go-ipfs/issues/6656#issuecomment-534252128 This commit was moved from ipfs/go-namesys@ac0ea1aa4fadb3fdad8a2aaa4ff4a0a6218e8a85 --- namesys/namesys.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/namesys/namesys.go b/namesys/namesys.go index cf944001d..0646fef7b 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -191,6 +191,9 @@ func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path. return err } ttl := DefaultResolverCacheTTL + if setTTL, ok := checkCtxTTL(ctx); ok { + ttl = setTTL + } if ttEol := time.Until(eol); ttEol < ttl { ttl = ttEol } From 5083a7e3ae08d8bf29b6eeb672c7e2b41ee968fc Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 23 Sep 2019 16:29:26 -0700 Subject: [PATCH 3105/3817] pin: fix pin update X Y where X==Y We were pining Y then removing the pin for X. When X == Y, we'd remove the new pin. fixes #6648 This commit was moved from ipfs/go-ipfs-pinner@550ed3b399036aa2507cc36f29f6cab690f7b2c0 --- pinning/pinner/pin.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 48a16f84e..975245e06 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -516,6 +516,14 @@ func (p *pinner) RecursiveKeys() []cid.Cid { // this is more efficient than simply pinning the new one and unpinning the // old one func (p *pinner) Update(ctx context.Context, from, to cid.Cid, unpin bool) error { + if from == to { + // Nothing to do. Don't remove this check or we'll end up + // _removing_ the pin. + // + // See #6648 + return nil + } + p.lock.Lock() defer p.lock.Unlock() From 46205d28eb9d4957f099f17ef9a19172e4fc9525 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 23 Sep 2019 17:52:17 -0700 Subject: [PATCH 3106/3817] namesys(test): test TTL on publish No fix is complete without a test. fixes #6670 This commit was moved from ipfs/go-namesys@246219d9fa17be3e8dc9170dbfe66fc84da14999 --- namesys/namesys_test.go | 49 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 4da3b17cb..524037dcf 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "testing" + "time" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" @@ -117,3 +118,51 @@ func TestPublishWithCache0(t *testing.T) { t.Fatal(err) } } + +func TestPublishWithTTL(t *testing.T) { + dst := dssync.MutexWrap(ds.NewMapDatastore()) + priv, _, err := ci.GenerateKeyPair(ci.RSA, 2048) + if err != nil { + t.Fatal(err) + } + ps := pstoremem.NewPeerstore() + pid, err := peer.IDFromPrivateKey(priv) + if err != nil { + t.Fatal(err) + } + err = ps.AddPrivKey(pid, priv) + if err != nil { + t.Fatal(err) + } + + routing := offroute.NewOfflineRouter(dst, record.NamespacedValidator{ + "ipns": ipns.Validator{KeyBook: ps}, + "pk": record.PublicKeyValidator{}, + }) + + nsys := NewNameSystem(routing, dst, 128) + p, err := path.ParsePath(unixfs.EmptyDirNode().Cid().String()) + if err != nil { + t.Fatal(err) + } + + ttl := 1 * time.Second + eol := time.Now().Add(2 * time.Second) + + ctx := context.WithValue(context.Background(), "ipns-publish-ttl", ttl) + err = nsys.Publish(ctx, priv, p) + if err != nil { + t.Fatal(err) + } + ientry, ok := nsys.(*mpns).cache.Get(pid.Pretty()) + if !ok { + t.Fatal("cache get failed") + } + entry, ok := ientry.(cacheEntry) + if !ok { + t.Fatal("bad cache item returned") + } + if entry.eol.Sub(eol) > 10*time.Millisecond { + t.Fatalf("bad cache ttl: expected %s, got %s", eol, entry.eol) + } +} From 57733596d9a6d7c4ff76a0f17c6602bf9c0227c8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 24 Sep 2019 16:44:22 -0700 Subject: [PATCH 3107/3817] doc: README This commit was moved from ipfs/go-filestore@9c3e8bfc0d030ac33f2e48b53261f222a8f49b21 --- filestore/README.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 filestore/README.md diff --git a/filestore/README.md b/filestore/README.md new file mode 100644 index 000000000..cf6940ef4 --- /dev/null +++ b/filestore/README.md @@ -0,0 +1,38 @@ +# go-filestore + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](https://protocol.ai) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.io/) +[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) +[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![GoDoc](https://godoc.org/github.com/ipfs/go-filestore?status.svg)](https://godoc.org/github.com/ipfs/go-filestore) + +> a by-reference file-backed blockstore + +## Lead Maintainer + +[Steven Allen](https://github.com/Stebalien) + +## Table of Contents + +- [Documentation](#documentation) +- [Contribute](#contribute) +- [License](#license) + +## Documentation + +https://godoc.org/github.com/ipfs/go-filestore + +## Contribute + +Feel free to join in. All welcome. Open an [issue](https://github.com/ipfs/go-filestore/issues)! + +This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). + +### Want to hack on IPFS? + +[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/contributing.md) + +## License + +MIT + From ab218255fb7488a719c3d1743194b0fb4f52cf2c Mon Sep 17 00:00:00 2001 From: frrist Date: Tue, 24 Sep 2019 12:23:59 +1000 Subject: [PATCH 3108/3817] define general Store interface replaces Blockstore - LoadCar accepts Store interface for putting blocks. This commit was moved from ipld/go-car@a64e86334410e380ad2165e2ec5bff15ea62c7e0 --- ipld/car/car.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index 69ad7410d..7a32b4073 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -8,7 +8,6 @@ import ( "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" - bstore "github.com/ipfs/go-ipfs-blockstore" cbor "github.com/ipfs/go-ipld-cbor" format "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" @@ -20,6 +19,10 @@ func init() { cbor.RegisterCborType(CarHeader{}) } +type Store interface { + Put(blocks.Block) error +} + type CarHeader struct { Roots []cid.Cid Version uint64 @@ -135,7 +138,7 @@ func (cr *carReader) Next() (blocks.Block, error) { return blocks.NewBlockWithCid(data, c) } -func LoadCar(bs bstore.Blockstore, r io.Reader) (*CarHeader, error) { +func LoadCar(s Store, r io.Reader) (*CarHeader, error) { cr, err := NewCarReader(r) if err != nil { return nil, err @@ -151,7 +154,7 @@ func LoadCar(bs bstore.Blockstore, r io.Reader) (*CarHeader, error) { case nil: } - if err := bs.Put(blk); err != nil { + if err := s.Put(blk); err != nil { return nil, err } } From 4182cedde6c7165b2028de6d09831394d9a481c6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 26 Sep 2019 12:00:06 -0700 Subject: [PATCH 3109/3817] fix: correctly handle symlink file sizes The file size of a symlink is the symlink data. This commit was moved from ipfs/go-unixfs@94a9c5d7befb25d85b162cd9b731030f31428865 --- unixfs/unixfs.go | 15 ++++++++++----- unixfs/unixfs_test.go | 17 +++++++++++++---- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index 84caf6f44..05abf6576 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -152,13 +152,16 @@ func DataSize(data []byte) (uint64, error) { if err != nil { return 0, err } + return size(pbdata) +} +func size(pbdata *pb.Data) (uint64, error) { switch pbdata.GetType() { - case pb.Data_Directory: + case pb.Data_Directory, pb.Data_HAMTShard: return 0, errors.New("can't get data size of directory") case pb.Data_File: return pbdata.GetFilesize(), nil - case pb.Data_Raw: + case pb.Data_Symlink, pb.Data_Raw: return uint64(len(pbdata.GetData())), nil default: return 0, errors.New("unrecognized node data type") @@ -253,10 +256,12 @@ func (n *FSNode) GetBytes() ([]byte, error) { return proto.Marshal(&n.format) } -// FileSize returns the total size of this tree. That is, the size of -// the data in this node plus the size of all its children. +// FileSize returns the size of the file. func (n *FSNode) FileSize() uint64 { - return n.format.GetFilesize() + // XXX: This needs to be able to return an error when we don't know the + // size. + size, _ := size(&n.format) + return size } // NumChildren returns the number of child blocks of this node diff --git a/unixfs/unixfs_test.go b/unixfs/unixfs_test.go index 79267133d..cf9e8548b 100644 --- a/unixfs/unixfs_test.go +++ b/unixfs/unixfs_test.go @@ -123,12 +123,21 @@ func TestPBdataTools(t *testing.T) { if err != nil { t.Fatal(err) } +} - _, sizeErr := DataSize(catSym) - if sizeErr == nil { - t.Fatal("DataSize didn't throw an error when taking the size of a Symlink.") +func TestSymlinkFilesize(t *testing.T) { + path := "/ipfs/adad123123/meowgie.gif" + sym, err := SymlinkData(path) + if err != nil { + t.Fatal(err) + } + size, err := DataSize(sym) + if err != nil { + t.Fatal(err) + } + if int(size) != len(path) { + t.Fatalf("size mismatch: %d != %d", size, len(path)) } - } func TestMetadata(t *testing.T) { From 125f06e6af82e7502de7596d9cfb5a3b560df4e5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 27 Sep 2019 16:21:44 -0700 Subject: [PATCH 3110/3817] fix(test): fix a flaky pubsub test This commit was moved from ipfs/interface-go-ipfs-core@00de46e290cf0a47bb78a9e8aa264f4b6ce941cd --- coreiface/tests/pubsub.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/coreiface/tests/pubsub.go b/coreiface/tests/pubsub.go index e66291572..36353f836 100644 --- a/coreiface/tests/pubsub.go +++ b/coreiface/tests/pubsub.go @@ -34,13 +34,20 @@ func (tp *TestSuite) TestBasicPubSub(t *testing.T) { t.Fatal(err) } + done := make(chan struct{}) go func() { + defer close(done) + ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() for { err := apis[1].PubSub().Publish(ctx, "testch", []byte("hello world")) - if err != nil { + switch err { + case nil: + case context.Canceled: + return + default: t.Error(err) cancel() return @@ -53,6 +60,13 @@ func (tp *TestSuite) TestBasicPubSub(t *testing.T) { } }() + // Wait for the sender to finish before we return. + // Otherwise, we can get random errors as publish fails. + defer func() { + cancel() + <-done + }() + m, err := sub.Next(ctx) if err != nil { t.Fatal(err) From 4a99c6144cc4178a9e7e4d3b2fab3275736d6c1d Mon Sep 17 00:00:00 2001 From: postables Date: Mon, 30 Sep 2019 17:55:57 -0700 Subject: [PATCH 3111/3817] coding.go: make getPBNode() public This commit was moved from ipfs/go-merkledag@c8d632c516bca74630d7bda1543e5094e0935a8a --- ipld/merkledag/coding.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 8b4192813..f0d6d69f0 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -5,8 +5,7 @@ import ( "sort" "strings" - "github.com/ipfs/go-block-format" - + blocks "github.com/ipfs/go-block-format" pb "github.com/ipfs/go-merkledag/pb" cid "github.com/ipfs/go-cid" @@ -49,7 +48,7 @@ func (n *ProtoNode) unmarshal(encoded []byte) error { // Marshal encodes a *Node instance into a new byte slice. // The conversion uses an intermediate PBNode. func (n *ProtoNode) Marshal() ([]byte, error) { - pbn := n.getPBNode() + pbn := n.GetPBNode() data, err := pbn.Marshal() if err != nil { return data, fmt.Errorf("marshal failed. %v", err) @@ -57,7 +56,10 @@ func (n *ProtoNode) Marshal() ([]byte, error) { return data, nil } -func (n *ProtoNode) getPBNode() *pb.PBNode { +// GetPBNode converts *ProtoNode into it's protocol buffer variant. +// If you plan on mutating the data of the original node, it is recommended +// that you call ProtoNode.Copy() before calling ProtoNode.GetPBNode() +func (n *ProtoNode) GetPBNode() *pb.PBNode { pbn := &pb.PBNode{} if len(n.links) > 0 { pbn.Links = make([]*pb.PBLink, len(n.links)) From b631e177d1667dcb91803f0e947fe7a73a6d4ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Sun, 22 Sep 2019 10:19:23 +0900 Subject: [PATCH 3112/3817] log keyProvider failure as an error If an error bubble up here, the Reprovider is effectively disabled. Error level is justified IMHO. This commit was moved from ipfs/go-ipfs-provider@65391ec748de992abe08765fe99932c30903a59b --- provider/simple/reprovide.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index 9b751f337..4cc48ec2a 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -77,7 +77,7 @@ func (rp *Reprovider) Run() { err := rp.Reprovide() if err != nil { - logR.Debug(err) + logR.Errorf("failed to reprovide: %s", err) } if done != nil { From a6ab260e316058b854d94e85861815ee18e97dc6 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 5 Oct 2019 21:50:29 +0200 Subject: [PATCH 3113/3817] Add benchmarks License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-chunker@cd78345010dd145d466cba3a3ad224a373e22b86 --- chunker/rabin_test.go | 29 +++++++++++++++++++++++++++++ chunker/splitting_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index f267db41a..140c0c4fa 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -79,3 +79,32 @@ func TestRabinChunkReuse(t *testing.T) { t.Log("too many spare chunks made") } } + +var Res uint64 + +func BenchmarkRabin(b *testing.B) { + data := make([]byte, 16<<20) + util.NewTimeSeededRand().Read(data) + + b.SetBytes(16 << 20) + b.ReportAllocs() + b.ResetTimer() + + var res uint64 + + for i := 0; i < b.N; i++ { + r := NewRabin(bytes.NewReader(data), 1024*256) + + for { + chunk, err := r.NextBytes() + if err != nil { + if err == io.EOF { + break + } + b.Fatal(err) + } + res = res + uint64(len(chunk)) + } + } + Res = Res + res +} diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index 3153427ed..a05504b10 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -6,6 +6,7 @@ import ( "testing" u "github.com/ipfs/go-ipfs-util" + util "github.com/ipfs/go-ipfs-util" ) func randBuf(t *testing.T, size int) []byte { @@ -118,3 +119,30 @@ func (s *clipReader) Read(buf []byte) (int, error) { return s.r.Read(buf) } + +func BenchmarkDefault(b *testing.B) { + data := make([]byte, 16<<20) + util.NewTimeSeededRand().Read(data) + + b.SetBytes(16 << 20) + b.ReportAllocs() + b.ResetTimer() + + var res uint64 + + for i := 0; i < b.N; i++ { + r := DefaultSplitter(bytes.NewReader(data)) + + for { + chunk, err := r.NextBytes() + if err != nil { + if err == io.EOF { + break + } + b.Fatal(err) + } + res = res + uint64(len(chunk)) + } + } + Res = Res + res +} From 8b25c4354f7fce466d5dd724aa68d735fe66762b Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sat, 5 Oct 2019 21:53:36 +0200 Subject: [PATCH 3114/3817] Fix pool usage in benchmark License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-chunker@52ca04ea25ced5b8caba1755f1555c25e5082ff1 --- chunker/splitting_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index a05504b10..27afe5967 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -7,6 +7,7 @@ import ( u "github.com/ipfs/go-ipfs-util" util "github.com/ipfs/go-ipfs-util" + pool "github.com/libp2p/go-buffer-pool" ) func randBuf(t *testing.T, size int) []byte { @@ -142,6 +143,7 @@ func BenchmarkDefault(b *testing.B) { b.Fatal(err) } res = res + uint64(len(chunk)) + pool.Put(chunk) } } Res = Res + res From 4dddab8b3a8520f908a31dc69774ef1829ce9958 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sun, 6 Oct 2019 12:28:43 +0200 Subject: [PATCH 3115/3817] Implement buzhash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It has the same properites as Rabin. Benchmark results: ``` name time/op Buzhash-4 14.3ms ± 7% Rabin-4 94.1ms ± 3% Default-4 1.74ms ± 7% name speed Buzhash-4 1.18GB/s ± 7% Rabin-4 178MB/s ± 3% Default-4 9.63GB/s ± 6% name alloc/op Buzhash-4 14.0kB ±48% Rabin-4 19.2MB ± 0% Default-4 474B ± 6% name allocs/op Buzhash-4 1.00 ± 0% Rabin-4 196 ±12% Default-4 2.00 ± 0% ``` License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-chunker@8dc885906ee5dce97bfbcea705de28900f1c732b --- chunker/buzhash.go | 115 ++++++++++++++++++++++++++++++++++++++++ chunker/buzhash_test.go | 94 ++++++++++++++++++++++++++++++++ chunker/gen/main.go | 33 ++++++++++++ chunker/rabin_test.go | 25 ++++++--- 4 files changed, 261 insertions(+), 6 deletions(-) create mode 100644 chunker/buzhash.go create mode 100644 chunker/buzhash_test.go create mode 100644 chunker/gen/main.go diff --git a/chunker/buzhash.go b/chunker/buzhash.go new file mode 100644 index 000000000..dbec13ccf --- /dev/null +++ b/chunker/buzhash.go @@ -0,0 +1,115 @@ +package chunk + +import ( + "io" + "math/bits" + + pool "github.com/libp2p/go-buffer-pool" +) + +const ( + buzMin = 128 << 10 + buzMax = 512 << 10 + buzMask = 1<<17 - 1 +) + +type Buzhash struct { + r io.Reader + buf []byte + n int + + err error +} + +func NewBuzhash(r io.Reader) *Buzhash { + return &Buzhash{ + r: r, + buf: pool.Get(buzMax), + } +} + +func (b *Buzhash) NextBytes() ([]byte, error) { + if b.err != nil { + return nil, b.err + } + + buf := b.buf + n, err := io.ReadFull(b.r, buf[b.n:]) + if err != nil { + if err == io.ErrUnexpectedEOF { + b.err = io.EOF + return buf[:n+b.n], nil + } else { + b.err = err + pool.Put(buf) + return nil, err + } + } + + i := buzMin - 32 + + var state uint32 = 0 + + for ; i < buzMin; i++ { + state = bits.RotateLeft32(state, 1) + state = state ^ bytehash[buf[i]] + } + + for ; state&buzMask != 0; i++ { + if i >= buzMax { + break + } + state = bits.RotateLeft32(state, 1) ^ bytehash[buf[i-32]] ^ bytehash[buf[i]] + } + + res := buf[:i] + b.buf = pool.Get(buzMax) + b.n = copy(b.buf, buf[i:]) + + return res, nil +} + +var bytehash = [256]uint32{ + 0x6236e7d5, 0x10279b0b, 0x72818182, 0xdc526514, 0x2fd41e3d, 0x777ef8c8, + 0x83ee5285, 0x2c8f3637, 0x2f049c1a, 0x57df9791, 0x9207151f, 0x9b544818, + 0x74eef658, 0x2028ca60, 0x0271d91a, 0x27ae587e, 0xecf9fa5f, 0x236e71cd, + 0xf43a8a2e, 0xbb13380, 0x9e57912c, 0x89a26cdb, 0x9fcf3d71, 0xa86da6f1, + 0x9c49f376, 0x346aecc7, 0xf094a9ee, 0xea99e9cb, 0xb01713c6, 0x88acffb, + 0x2960a0fb, 0x344a626c, 0x7ff22a46, 0x6d7a1aa5, 0x6a714916, 0x41d454ca, + 0x8325b830, 0xb65f563, 0x447fecca, 0xf9d0ea5e, 0xc1d9d3d4, 0xcb5ec574, + 0x55aae902, 0x86edc0e7, 0xd3a9e33, 0xe70dc1e1, 0xe3c5f639, 0x9b43140a, + 0xc6490ac5, 0x5e4030fb, 0x8e976dd5, 0xa87468ea, 0xf830ef6f, 0xcc1ed5a5, + 0x611f4e78, 0xddd11905, 0xf2613904, 0x566c67b9, 0x905a5ccc, 0x7b37b3a4, + 0x4b53898a, 0x6b8fd29d, 0xaad81575, 0x511be414, 0x3cfac1e7, 0x8029a179, + 0xd40efeda, 0x7380e02, 0xdc9beffd, 0x2d049082, 0x99bc7831, 0xff5002a8, + 0x21ce7646, 0x1cd049b, 0xf43994f, 0xc3c6c5a5, 0xbbda5f50, 0xec15ec7, + 0x9adb19b6, 0xc1e80b9, 0xb9b52968, 0xae162419, 0x2542b405, 0x91a42e9d, + 0x6be0f668, 0x6ed7a6b9, 0xbc2777b4, 0xe162ce56, 0x4266aad5, 0x60fdb704, + 0x66f832a5, 0x9595f6ca, 0xfee83ced, 0x55228d99, 0x12bf0e28, 0x66896459, + 0x789afda, 0x282baa8, 0x2367a343, 0x591491b0, 0x2ff1a4b1, 0x410739b6, + 0x9b7055a0, 0x2e0eb229, 0x24fc8252, 0x3327d3df, 0xb0782669, 0x1c62e069, + 0x7f503101, 0xf50593ae, 0xd9eb275d, 0xe00eb678, 0x5917ccde, 0x97b9660a, + 0xdd06202d, 0xed229e22, 0xa9c735bf, 0xd6316fe6, 0x6fc72e4c, 0x206dfa2, + 0xd6b15c5a, 0x69d87b49, 0x9c97745, 0x13445d61, 0x35a975aa, 0x859aa9b9, + 0x65380013, 0xd1fb6391, 0xc29255fd, 0x784a3b91, 0xb9e74c26, 0x63ce4d40, + 0xc07cbe9e, 0xe6e4529e, 0xfb3632f, 0x9438d9c9, 0x682f94a8, 0xf8fd4611, + 0x257ec1ed, 0x475ce3d6, 0x60ee2db1, 0x2afab002, 0x2b9e4878, 0x86b340de, + 0x1482fdca, 0xfe41b3bf, 0xd4a412b0, 0xe09db98c, 0xc1af5d53, 0x7e55e25f, + 0xd3346b38, 0xb7a12cbd, 0x9c6827ba, 0x71f78bee, 0x8c3a0f52, 0x150491b0, + 0xf26de912, 0x233e3a4e, 0xd309ebba, 0xa0a9e0ff, 0xca2b5921, 0xeeb9893c, + 0x33829e88, 0x9870cc2a, 0x23c4b9d0, 0xeba32ea3, 0xbdac4d22, 0x3bc8c44c, + 0x1e8d0397, 0xf9327735, 0x783b009f, 0xeb83742, 0x2621dc71, 0xed017d03, + 0x5c760aa1, 0x5a69814b, 0x96e3047f, 0xa93c9cde, 0x615c86f5, 0xb4322aa5, + 0x4225534d, 0xd2e2de3, 0xccfccc4b, 0xbac2a57, 0xf0a06d04, 0xbc78d737, + 0xf2d1f766, 0xf5a7953c, 0xbcdfda85, 0x5213b7d5, 0xbce8a328, 0xd38f5f18, + 0xdb094244, 0xfe571253, 0x317fa7ee, 0x4a324f43, 0x3ffc39d9, 0x51b3fa8e, + 0x7a4bee9f, 0x78bbc682, 0x9f5c0350, 0x2fe286c, 0x245ab686, 0xed6bf7d7, + 0xac4988a, 0x3fe010fa, 0xc65fe369, 0xa45749cb, 0x2b84e537, 0xde9ff363, + 0x20540f9a, 0xaa8c9b34, 0x5bc476b3, 0x1d574bd7, 0x929100ad, 0x4721de4d, + 0x27df1b05, 0x58b18546, 0xb7e76764, 0xdf904e58, 0x97af57a1, 0xbd4dc433, + 0xa6256dfd, 0xf63998f3, 0xf1e05833, 0xe20acf26, 0xf57fd9d6, 0x90300b4d, + 0x89df4290, 0x68d01cbc, 0xcf893ee3, 0xcc42a046, 0x778e181b, 0x67265c76, + 0xe981a4c4, 0x82991da1, 0x708f7294, 0xe6e2ae62, 0xfc441870, 0x95e1b0b6, + 0x445f825, 0x5a93b47f, 0x5e9cf4be, 0x84da71e7, 0x9d9582b0, 0x9bf835ef, + 0x591f61e2, 0x43325985, 0x5d2de32e, 0x8d8fbf0f, 0x95b30f38, 0x7ad5b6e, + 0x4e934edf, 0x3cd4990e, 0x9053e259, 0x5c41857d} diff --git a/chunker/buzhash_test.go b/chunker/buzhash_test.go new file mode 100644 index 000000000..15b46b7a2 --- /dev/null +++ b/chunker/buzhash_test.go @@ -0,0 +1,94 @@ +package chunk + +import ( + "bytes" + "fmt" + "io" + "testing" + + util "github.com/ipfs/go-ipfs-util" + pool "github.com/libp2p/go-buffer-pool" +) + +func TestBuzhashChunking(t *testing.T) { + data := make([]byte, 1024*1024*16) + util.NewTimeSeededRand().Read(data) + + r := NewBuzhash(bytes.NewReader(data)) + + var chunks [][]byte + + for { + chunk, err := r.NextBytes() + if err != nil { + if err == io.EOF { + break + } + t.Fatal(err) + } + + chunks = append(chunks, chunk) + } + + t.Logf("average block size: %d\n", len(data)/len(chunks)) + + unchunked := bytes.Join(chunks, nil) + if !bytes.Equal(unchunked, data) { + fmt.Printf("%d %d\n", len(unchunked), len(data)) + //ioutil.WriteFile("./incorrect", unchunked, 0777) + //ioutil.WriteFile("./correct", data, 0777) + t.Fatal("data was chunked incorrectly") + } +} + +func TestBuzhashChunkReuse(t *testing.T) { + newBuzhash := func(r io.Reader) cher { + return NewBuzhash(r) + } + testReuse(t, newBuzhash) +} + +func BenchmarkBuzhash(b *testing.B) { + data := make([]byte, 16<<20) + util.NewTimeSeededRand().Read(data) + + b.SetBytes(16 << 20) + b.ReportAllocs() + b.ResetTimer() + + var res uint64 + + for i := 0; i < b.N; i++ { + r := NewBuzhash(bytes.NewReader(data)) + + for { + chunk, err := r.NextBytes() + if err != nil { + if err == io.EOF { + break + } + b.Fatal(err) + } + res = res + uint64(len(chunk)) + pool.Put(chunk) + } + } + Res = Res + res +} + +func TestBuzhashBitsHash(t *testing.T) { + counts := make([]byte, 32) + for _, h := range bytehash { + for i := 0; i < 32; i++ { + if h&1 == 1 { + counts[i]++ + } + h = h >> 1 + } + } + for i, c := range counts { + if c != 128 { + t.Errorf("Bit balance in position %d broken, %d ones", i, c) + } + } +} diff --git a/chunker/gen/main.go b/chunker/gen/main.go new file mode 100644 index 000000000..9d908544b --- /dev/null +++ b/chunker/gen/main.go @@ -0,0 +1,33 @@ +// This file generates bytehash LUT +package main + +import ( + "fmt" + "math/rand" +) + +const nRounds = 200 + +func main() { + rnd := rand.New(rand.NewSource(0)) + + lut := make([]uint32, 256) + for i := 0; i < 256/2; i++ { + lut[i] = 1<<32 - 1 + } + + for r := 0; r < nRounds; r++ { + for b := uint32(0); b < 32; b++ { + mask := uint32(1) << b + nmask := ^mask + for i, j := range rnd.Perm(256) { + li := lut[i] + lj := lut[j] + lut[i] = li&nmask | (lj & mask) + lut[j] = lj&nmask | (li & mask) + } + } + } + + fmt.Printf("%#v", lut) +} diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 140c0c4fa..7aa8a1387 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -39,8 +39,14 @@ func TestRabinChunking(t *testing.T) { } } -func chunkData(t *testing.T, data []byte) map[string]blocks.Block { - r := NewRabin(bytes.NewReader(data), 1024*256) +type cher interface { + NextBytes() ([]byte, error) +} + +type newChunker func(io.Reader) cher + +func chunkData(t *testing.T, newC newChunker, data []byte) map[string]blocks.Block { + r := newC(bytes.NewReader(data)) blkmap := make(map[string]blocks.Block) @@ -60,12 +66,12 @@ func chunkData(t *testing.T, data []byte) map[string]blocks.Block { return blkmap } -func TestRabinChunkReuse(t *testing.T) { +func testReuse(t *testing.T, cr newChunker) { data := make([]byte, 1024*1024*16) util.NewTimeSeededRand().Read(data) - ch1 := chunkData(t, data[1000:]) - ch2 := chunkData(t, data) + ch1 := chunkData(t, cr, data[1000:]) + ch2 := chunkData(t, cr, data) var extra int for k := range ch2 { @@ -76,8 +82,15 @@ func TestRabinChunkReuse(t *testing.T) { } if extra > 2 { - t.Log("too many spare chunks made") + t.Logf("too many spare chunks made: %d", extra) + } +} + +func TestRabinChunkReuse(t *testing.T) { + newRabin := func(r io.Reader) cher { + return NewRabin(r, 256*1024) } + testReuse(t, newRabin) } var Res uint64 From 4d9433179dcf0fe858bc761aabfbc509a3da4733 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sun, 6 Oct 2019 13:53:24 +0200 Subject: [PATCH 3116/3817] Combine if conditions License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-chunker@29aeb2c8a218c8b1478500b8d6f60f8a14cad487 --- chunker/buzhash.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/chunker/buzhash.go b/chunker/buzhash.go index dbec13ccf..4bbe009d8 100644 --- a/chunker/buzhash.go +++ b/chunker/buzhash.go @@ -55,10 +55,7 @@ func (b *Buzhash) NextBytes() ([]byte, error) { state = state ^ bytehash[buf[i]] } - for ; state&buzMask != 0; i++ { - if i >= buzMax { - break - } + for ; state&buzMask != 0 && i < buzMax; i++ { state = bits.RotateLeft32(state, 1) ^ bytehash[buf[i-32]] ^ bytehash[buf[i]] } From 725a707ac834a1cd71674c33a2fcc28dfefb46a8 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sun, 6 Oct 2019 13:56:15 +0200 Subject: [PATCH 3117/3817] Invalidate buf pointer after returning it to the pool License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-chunker@9e34967aef4741b48009afa8d40e31981880ad83 --- chunker/buzhash.go | 1 + 1 file changed, 1 insertion(+) diff --git a/chunker/buzhash.go b/chunker/buzhash.go index 4bbe009d8..40b2e9301 100644 --- a/chunker/buzhash.go +++ b/chunker/buzhash.go @@ -42,6 +42,7 @@ func (b *Buzhash) NextBytes() ([]byte, error) { } else { b.err = err pool.Put(buf) + b.buf = nil return nil, err } } From e9870b762c80dd63136dedc4945c1458ef137bcd Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Sun, 6 Oct 2019 23:34:06 +0200 Subject: [PATCH 3118/3817] Don't use pools for result buffers Don't return them either in benchmarks License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-chunker@b1c398a9fac50efee5232df2c9a09ba6da7af448 --- chunker/buzhash.go | 12 +++++++++--- chunker/buzhash_test.go | 6 ++---- chunker/rabin_test.go | 5 +++-- chunker/splitting_test.go | 7 +++---- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/chunker/buzhash.go b/chunker/buzhash.go index 40b2e9301..edfc4c03d 100644 --- a/chunker/buzhash.go +++ b/chunker/buzhash.go @@ -38,7 +38,12 @@ func (b *Buzhash) NextBytes() ([]byte, error) { if err != nil { if err == io.ErrUnexpectedEOF { b.err = io.EOF - return buf[:n+b.n], nil + res := make([]byte, n+b.n) + copy(res, buf) + + pool.Put(b.buf) + b.buf = nil + return res, nil } else { b.err = err pool.Put(buf) @@ -60,8 +65,9 @@ func (b *Buzhash) NextBytes() ([]byte, error) { state = bits.RotateLeft32(state, 1) ^ bytehash[buf[i-32]] ^ bytehash[buf[i]] } - res := buf[:i] - b.buf = pool.Get(buzMax) + res := make([]byte, i) + copy(res, b.buf) + b.n = copy(b.buf, buf[i:]) return res, nil diff --git a/chunker/buzhash_test.go b/chunker/buzhash_test.go index 15b46b7a2..8e2b16632 100644 --- a/chunker/buzhash_test.go +++ b/chunker/buzhash_test.go @@ -7,7 +7,6 @@ import ( "testing" util "github.com/ipfs/go-ipfs-util" - pool "github.com/libp2p/go-buffer-pool" ) func TestBuzhashChunking(t *testing.T) { @@ -49,10 +48,10 @@ func TestBuzhashChunkReuse(t *testing.T) { } func BenchmarkBuzhash(b *testing.B) { - data := make([]byte, 16<<20) + data := make([]byte, 1<<10) util.NewTimeSeededRand().Read(data) - b.SetBytes(16 << 20) + b.SetBytes(int64(len(data))) b.ReportAllocs() b.ResetTimer() @@ -70,7 +69,6 @@ func BenchmarkBuzhash(b *testing.B) { b.Fatal(err) } res = res + uint64(len(chunk)) - pool.Put(chunk) } } Res = Res + res diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 7aa8a1387..9eb8c6693 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -96,10 +96,11 @@ func TestRabinChunkReuse(t *testing.T) { var Res uint64 func BenchmarkRabin(b *testing.B) { - data := make([]byte, 16<<20) + const size = 1 << 10 + data := make([]byte, size) util.NewTimeSeededRand().Read(data) - b.SetBytes(16 << 20) + b.SetBytes(size) b.ReportAllocs() b.ResetTimer() diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index 27afe5967..f2de77442 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -7,7 +7,6 @@ import ( u "github.com/ipfs/go-ipfs-util" util "github.com/ipfs/go-ipfs-util" - pool "github.com/libp2p/go-buffer-pool" ) func randBuf(t *testing.T, size int) []byte { @@ -122,10 +121,11 @@ func (s *clipReader) Read(buf []byte) (int, error) { } func BenchmarkDefault(b *testing.B) { - data := make([]byte, 16<<20) + const size = 1 << 10 + data := make([]byte, size) util.NewTimeSeededRand().Read(data) - b.SetBytes(16 << 20) + b.SetBytes(size) b.ReportAllocs() b.ResetTimer() @@ -143,7 +143,6 @@ func BenchmarkDefault(b *testing.B) { b.Fatal(err) } res = res + uint64(len(chunk)) - pool.Put(chunk) } } Res = Res + res From d0ead241b9e0b77e4158048ece31f70a2316686d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 7 Oct 2019 17:07:31 +0900 Subject: [PATCH 3119/3817] fix(resolve): correctly handle .eth domains This should have been handled down inside the DNSLink resolver. Otherwise, we'll break any name happens to end in `.eth`. also fixes #6699 This commit was moved from ipfs/go-namesys@af26558d3f2c3cc666bf118ea7ea42da93328d2e --- namesys/dns.go | 9 +++++++++ namesys/namesys.go | 8 -------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 931edec00..984a27aa8 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -11,6 +11,9 @@ import ( isd "github.com/jbenet/go-is-domain" ) +const ethTLD = "eth" +const linkTLD = "link" + type LookupTXTFunc func(name string) (txt []string, err error) // DNSResolver implements a Resolver on DNS domains @@ -62,6 +65,12 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options fqdn = domain + "." } + if strings.HasSuffix(fqdn, "."+ethTLD+".") { + // This is an ENS name. As we're resolving via an arbitrary DNS server + // that may not know about .eth we need to add our link domain suffix. + fqdn += linkTLD + "." + } + rootChan := make(chan lookupRes, 1) go workDomain(r, fqdn, rootChan) diff --git a/namesys/namesys.go b/namesys/namesys.go index 0646fef7b..7ae93e3e2 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -80,9 +80,6 @@ func (ns *mpns) ResolveAsync(ctx context.Context, name string, options ...opts.R return resolveAsync(ctx, ns, name, opts.ProcessOpts(options)) } -const ethTLD = ".eth" -const linkTLD = ".link" - // resolveOnce implements resolver. func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { out := make(chan onceResult, 1) @@ -90,11 +87,6 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. if !strings.HasPrefix(name, ipnsPrefix) { name = ipnsPrefix + name } - if strings.HasSuffix(name, ethTLD) { - // This is an ENS name. As we're resolving via an arbitrary DNS server - // that may not know about .eth we need to add our link domain suffix. - name = name + linkTLD - } segments := strings.SplitN(name, "/", 4) if len(segments) < 3 || segments[0] != "" { log.Debugf("invalid name syntax for %s", name) From a9af64d2b56abea15af597fd03c30a827c157938 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 7 Oct 2019 17:33:38 +0900 Subject: [PATCH 3120/3817] test(namesys): remove broken test This commit was moved from ipfs/go-namesys@bb18e632518df51fbe4e3bcf11ecb52cb565bd4e --- namesys/namesys_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 524037dcf..a0ffbc50d 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -60,8 +60,7 @@ func mockResolverOne() *mockResolver { func mockResolverTwo() *mockResolver { return &mockResolver{ entries: map[string]string{ - "ipfs.io": "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", - "www.wealdtech.eth.link": "/ipns/QmQ4QZh8nrsczdUEwTyfBope4THUhqxqc1fx6qYhhzZQei", + "ipfs.io": "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", }, } } @@ -83,8 +82,6 @@ func TestNamesysResolution(t *testing.T) { testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 1, "/ipns/ipfs.io", ErrResolveRecursion) testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 2, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", ErrResolveRecursion) testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 3, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", ErrResolveRecursion) - testResolution(t, r, "/ipns/www.wealdtech.eth", 1, "/ipns/QmQ4QZh8nrsczdUEwTyfBope4THUhqxqc1fx6qYhhzZQei", ErrResolveRecursion) - testResolution(t, r, "/ipns/www.wealdtech.eth", 2, "/ipfs/QmP3ouCnU8NNLsW6261pAx2pNLV2E4dQoisB1sgda12Act", nil) } func TestPublishWithCache0(t *testing.T) { From 6a4535dfcb578cba16b6cc0dbb6991dcebf58351 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 7 Oct 2019 01:21:46 +0200 Subject: [PATCH 3121/3817] Improve benchmarks License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-chunker@25cb45d1068c0b537f335900e897721c72c10920 --- chunker/benchmark_test.go | 59 +++++++++++++++++++++++++++++++++++++++ chunker/buzhash.go | 4 +++ chunker/buzhash_test.go | 31 ++++---------------- chunker/rabin_test.go | 40 ++++---------------------- chunker/splitting_test.go | 29 ++----------------- 5 files changed, 77 insertions(+), 86 deletions(-) create mode 100644 chunker/benchmark_test.go diff --git a/chunker/benchmark_test.go b/chunker/benchmark_test.go new file mode 100644 index 000000000..5069b0653 --- /dev/null +++ b/chunker/benchmark_test.go @@ -0,0 +1,59 @@ +package chunk + +import ( + "bytes" + "io" + "math/rand" + "testing" +) + +type newSplitter func(io.Reader) Splitter + +type bencSpec struct { + size int + name string +} + +var bSizes = []bencSpec{ + {1 << 10, "1K"}, + {1 << 20, "1M"}, + {16 << 20, "16M"}, + {100 << 20, "100M"}, +} + +func benchmarkChunker(b *testing.B, ns newSplitter) { + for _, s := range bSizes { + s := s + b.Run(s.name, func(b *testing.B) { + benchmarkChunkerSize(b, ns, s.size) + }) + } +} + +func benchmarkChunkerSize(b *testing.B, ns newSplitter, size int) { + rng := rand.New(rand.NewSource(1)) + data := make([]byte, size) + rng.Read(data) + + b.SetBytes(int64(size)) + b.ReportAllocs() + b.ResetTimer() + + var res uint64 + + for i := 0; i < b.N; i++ { + r := ns(bytes.NewReader(data)) + + for { + chunk, err := r.NextBytes() + if err != nil { + if err == io.EOF { + break + } + b.Fatal(err) + } + res = res + uint64(len(chunk)) + } + } + Res = Res + res +} diff --git a/chunker/buzhash.go b/chunker/buzhash.go index edfc4c03d..099b723b1 100644 --- a/chunker/buzhash.go +++ b/chunker/buzhash.go @@ -28,6 +28,10 @@ func NewBuzhash(r io.Reader) *Buzhash { } } +func (b *Buzhash) Reader() io.Reader { + return b.r +} + func (b *Buzhash) NextBytes() ([]byte, error) { if b.err != nil { return nil, b.err diff --git a/chunker/buzhash_test.go b/chunker/buzhash_test.go index 8e2b16632..6e59d6be8 100644 --- a/chunker/buzhash_test.go +++ b/chunker/buzhash_test.go @@ -41,37 +41,16 @@ func TestBuzhashChunking(t *testing.T) { } func TestBuzhashChunkReuse(t *testing.T) { - newBuzhash := func(r io.Reader) cher { + newBuzhash := func(r io.Reader) Splitter { return NewBuzhash(r) } testReuse(t, newBuzhash) } -func BenchmarkBuzhash(b *testing.B) { - data := make([]byte, 1<<10) - util.NewTimeSeededRand().Read(data) - - b.SetBytes(int64(len(data))) - b.ReportAllocs() - b.ResetTimer() - - var res uint64 - - for i := 0; i < b.N; i++ { - r := NewBuzhash(bytes.NewReader(data)) - - for { - chunk, err := r.NextBytes() - if err != nil { - if err == io.EOF { - break - } - b.Fatal(err) - } - res = res + uint64(len(chunk)) - } - } - Res = Res + res +func BenchmarkBuzhash2(b *testing.B) { + benchmarkChunker(b, func(r io.Reader) Splitter { + return NewBuzhash(r) + }) } func TestBuzhashBitsHash(t *testing.T) { diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 9eb8c6693..857e97c2c 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -39,13 +39,7 @@ func TestRabinChunking(t *testing.T) { } } -type cher interface { - NextBytes() ([]byte, error) -} - -type newChunker func(io.Reader) cher - -func chunkData(t *testing.T, newC newChunker, data []byte) map[string]blocks.Block { +func chunkData(t *testing.T, newC newSplitter, data []byte) map[string]blocks.Block { r := newC(bytes.NewReader(data)) blkmap := make(map[string]blocks.Block) @@ -66,7 +60,7 @@ func chunkData(t *testing.T, newC newChunker, data []byte) map[string]blocks.Blo return blkmap } -func testReuse(t *testing.T, cr newChunker) { +func testReuse(t *testing.T, cr newSplitter) { data := make([]byte, 1024*1024*16) util.NewTimeSeededRand().Read(data) @@ -87,7 +81,7 @@ func testReuse(t *testing.T, cr newChunker) { } func TestRabinChunkReuse(t *testing.T) { - newRabin := func(r io.Reader) cher { + newRabin := func(r io.Reader) Splitter { return NewRabin(r, 256*1024) } testReuse(t, newRabin) @@ -96,29 +90,7 @@ func TestRabinChunkReuse(t *testing.T) { var Res uint64 func BenchmarkRabin(b *testing.B) { - const size = 1 << 10 - data := make([]byte, size) - util.NewTimeSeededRand().Read(data) - - b.SetBytes(size) - b.ReportAllocs() - b.ResetTimer() - - var res uint64 - - for i := 0; i < b.N; i++ { - r := NewRabin(bytes.NewReader(data), 1024*256) - - for { - chunk, err := r.NextBytes() - if err != nil { - if err == io.EOF { - break - } - b.Fatal(err) - } - res = res + uint64(len(chunk)) - } - } - Res = Res + res + benchmarkChunker(b, func(r io.Reader) Splitter { + return NewRabin(r, 256<<10) + }) } diff --git a/chunker/splitting_test.go b/chunker/splitting_test.go index f2de77442..d6498fcbe 100644 --- a/chunker/splitting_test.go +++ b/chunker/splitting_test.go @@ -6,7 +6,6 @@ import ( "testing" u "github.com/ipfs/go-ipfs-util" - util "github.com/ipfs/go-ipfs-util" ) func randBuf(t *testing.T, size int) []byte { @@ -121,29 +120,7 @@ func (s *clipReader) Read(buf []byte) (int, error) { } func BenchmarkDefault(b *testing.B) { - const size = 1 << 10 - data := make([]byte, size) - util.NewTimeSeededRand().Read(data) - - b.SetBytes(size) - b.ReportAllocs() - b.ResetTimer() - - var res uint64 - - for i := 0; i < b.N; i++ { - r := DefaultSplitter(bytes.NewReader(data)) - - for { - chunk, err := r.NextBytes() - if err != nil { - if err == io.EOF { - break - } - b.Fatal(err) - } - res = res + uint64(len(chunk)) - } - } - Res = Res + res + benchmarkChunker(b, func(r io.Reader) Splitter { + return DefaultSplitter(r) + }) } From 9f68dfe9ca69ebc09c78c9dd226c0a9d7f1a5df8 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 7 Oct 2019 11:49:08 +0200 Subject: [PATCH 3122/3817] Do not exit if buffer is not full. License: MIT Signed-off-by: Jakub Sztandera License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-chunker@5ac3082b5ba63dd0dd1cdac6dc2d532d86d83b26 --- chunker/buzhash.go | 26 ++++++++++++++++---------- chunker/buzhash_test.go | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/chunker/buzhash.go b/chunker/buzhash.go index 099b723b1..1a9b747de 100644 --- a/chunker/buzhash.go +++ b/chunker/buzhash.go @@ -40,14 +40,16 @@ func (b *Buzhash) NextBytes() ([]byte, error) { buf := b.buf n, err := io.ReadFull(b.r, buf[b.n:]) if err != nil { - if err == io.ErrUnexpectedEOF { - b.err = io.EOF - res := make([]byte, n+b.n) - copy(res, buf) - - pool.Put(b.buf) - b.buf = nil - return res, nil + if err == io.ErrUnexpectedEOF || err == io.EOF { + if b.n+n < buzMin { + b.err = io.EOF + res := make([]byte, b.n+n) + copy(res, buf) + + pool.Put(b.buf) + b.buf = nil + return res, nil + } } else { b.err = err pool.Put(buf) @@ -65,14 +67,18 @@ func (b *Buzhash) NextBytes() ([]byte, error) { state = state ^ bytehash[buf[i]] } - for ; state&buzMask != 0 && i < buzMax; i++ { + if b.n+n > len(buf) { + panic("this is impossible, but gives +9 to performance") + } + + for ; state&buzMask != 0 && i < b.n+n; i++ { state = bits.RotateLeft32(state, 1) ^ bytehash[buf[i-32]] ^ bytehash[buf[i]] } res := make([]byte, i) copy(res, b.buf) - b.n = copy(b.buf, buf[i:]) + b.n = copy(b.buf, buf[i:b.n+n]) return res, nil } diff --git a/chunker/buzhash_test.go b/chunker/buzhash_test.go index 6e59d6be8..f630cef89 100644 --- a/chunker/buzhash_test.go +++ b/chunker/buzhash_test.go @@ -53,7 +53,7 @@ func BenchmarkBuzhash2(b *testing.B) { }) } -func TestBuzhashBitsHash(t *testing.T) { +func TestBuzhashBitsHashBias(t *testing.T) { counts := make([]byte, 32) for _, h := range bytehash { for i := 0; i < 32; i++ { From a9276c6ee9bc00f722bfdb77280cba00cf3041e2 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 7 Oct 2019 11:53:44 +0200 Subject: [PATCH 3123/3817] Cleanup buf usage License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-chunker@462a6eb4ba7783e59120cd9656dae712fd12218f --- chunker/buzhash.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/chunker/buzhash.go b/chunker/buzhash.go index 1a9b747de..54115d59b 100644 --- a/chunker/buzhash.go +++ b/chunker/buzhash.go @@ -37,14 +37,13 @@ func (b *Buzhash) NextBytes() ([]byte, error) { return nil, b.err } - buf := b.buf - n, err := io.ReadFull(b.r, buf[b.n:]) + n, err := io.ReadFull(b.r, b.buf[b.n:]) if err != nil { if err == io.ErrUnexpectedEOF || err == io.EOF { if b.n+n < buzMin { b.err = io.EOF res := make([]byte, b.n+n) - copy(res, buf) + copy(res, b.buf) pool.Put(b.buf) b.buf = nil @@ -52,7 +51,7 @@ func (b *Buzhash) NextBytes() ([]byte, error) { } } else { b.err = err - pool.Put(buf) + pool.Put(b.buf) b.buf = nil return nil, err } @@ -64,21 +63,21 @@ func (b *Buzhash) NextBytes() ([]byte, error) { for ; i < buzMin; i++ { state = bits.RotateLeft32(state, 1) - state = state ^ bytehash[buf[i]] + state = state ^ bytehash[b.buf[i]] } - if b.n+n > len(buf) { + if b.n+n > len(b.buf) { panic("this is impossible, but gives +9 to performance") } for ; state&buzMask != 0 && i < b.n+n; i++ { - state = bits.RotateLeft32(state, 1) ^ bytehash[buf[i-32]] ^ bytehash[buf[i]] + state = bits.RotateLeft32(state, 1) ^ bytehash[b.buf[i-32]] ^ bytehash[b.buf[i]] } res := make([]byte, i) copy(res, b.buf) - b.n = copy(b.buf, buf[i:b.n+n]) + b.n = copy(b.buf, b.buf[i:b.n+n]) return res, nil } From f66d4a0d2b0d94dedea4571fc70e66361f520759 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 7 Oct 2019 12:01:18 +0200 Subject: [PATCH 3124/3817] Add buzhash to parsers list License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-chunker@57fa65915807a087b69fa6c7e3442cbe56413960 --- chunker/parse.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/chunker/parse.go b/chunker/parse.go index af0a31e80..5d472b709 100644 --- a/chunker/parse.go +++ b/chunker/parse.go @@ -14,8 +14,8 @@ var ( ) // FromString returns a Splitter depending on the given string: -// it supports "default" (""), "size-{size}", "rabin", "rabin-{blocksize}" and -// "rabin-{min}-{avg}-{max}". +// it supports "default" (""), "size-{size}", "rabin", "rabin-{blocksize}", +// "rabin-{min}-{avg}-{max}" and "buzhash". func FromString(r io.Reader, chunker string) (Splitter, error) { switch { case chunker == "" || chunker == "default": @@ -34,6 +34,9 @@ func FromString(r io.Reader, chunker string) (Splitter, error) { case strings.HasPrefix(chunker, "rabin"): return parseRabinString(r, chunker) + case chunker == "buzhash": + return NewBuzhash(r), nil + default: return nil, fmt.Errorf("unrecognized chunker option: %s", chunker) } From 293d260dad40d3d21adfe33093515ef4be3f1d3d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 7 Oct 2019 22:23:35 +0900 Subject: [PATCH 3125/3817] test(namesys): add fixed eth.link test This commit was moved from ipfs/go-namesys@37fb4f7d8c901a9c068353cdab2f279f60bc6e48 --- namesys/dns_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 8d53887be..653c3c788 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -126,6 +126,9 @@ func newMockDNS() *mockDNS { "fqdn.example.com.": []string{ "dnslink=/ipfs/QmYvMB9yrsSf7RKBghkfwmHJkzJhW2ZgVwq3LxBXXPasFr", }, + "www.wealdtech.eth.link.": []string{ + "dnslink=/ipns/ipfs.example.com", + }, }, } } @@ -163,4 +166,7 @@ func TestDNSResolution(t *testing.T) { testResolution(t, r, "double.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) testResolution(t, r, "conflict.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjE", nil) testResolution(t, r, "fqdn.example.com.", opts.DefaultDepthLimit, "/ipfs/QmYvMB9yrsSf7RKBghkfwmHJkzJhW2ZgVwq3LxBXXPasFr", nil) + testResolution(t, r, "www.wealdtech.eth", 1, "/ipns/ipfs.example.com", ErrResolveRecursion) + testResolution(t, r, "www.wealdtech.eth", 2, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "www.wealdtech.eth.link", 2, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) } From eabc6d1e93bb724832193b725a65cbe683c6f8e3 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 8 Oct 2019 16:50:24 +0900 Subject: [PATCH 3126/3817] fix(pin): wait till after fetching to remove direct pin Otherwise, we could abort while fetching the graph and stay in a state where the direct pin is removed. fixes #4650 This commit was moved from ipfs/go-ipfs-pinner@c53b7d39f7366427b21016474d8c5dce7a4ffd65 --- pinning/pinner/pin.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 975245e06..63fa663c1 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -225,9 +225,6 @@ func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { return nil } - if p.directPin.Has(c) { - p.directPin.Remove(c) - } p.lock.Unlock() // fetch entire graph err := mdag.FetchGraph(ctx, c, p.dserv) From 30eb9c7c432be938b40951a4c98b8cfbe67899cc Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Fri, 11 Oct 2019 11:14:39 -0400 Subject: [PATCH 3127/3817] test(pinning): add pin ls tests for indirect pin traversal and pin type precedence This commit was moved from ipfs/interface-go-ipfs-core@cd7be61c71d8169a47604247588b258699f45b5d --- coreiface/tests/pin.go | 269 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 268 insertions(+), 1 deletion(-) diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index 9b28a682a..7e574fa0d 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -2,14 +2,15 @@ package tests import ( "context" - "github.com/ipfs/interface-go-ipfs-core/path" "math" "strings" "testing" "github.com/ipfs/interface-go-ipfs-core" opt "github.com/ipfs/interface-go-ipfs-core/options" + "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/go-cid" ipldcbor "github.com/ipfs/go-ipld-cbor" ipld "github.com/ipfs/go-ipld-format" ) @@ -25,6 +26,8 @@ func (tp *TestSuite) TestPin(t *testing.T) { t.Run("TestPinAdd", tp.TestPinAdd) t.Run("TestPinSimple", tp.TestPinSimple) t.Run("TestPinRecursive", tp.TestPinRecursive) + t.Run("TestPinLsIndirect", tp.TestPinLsIndirect) + t.Run("TestPinLsPrecedence", tp.TestPinLsPrecedence) } func (tp *TestSuite) TestPinAdd(t *testing.T) { @@ -238,3 +241,267 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) { } */ } + +// TestPinLsIndirect verifies that indirect nodes are listed by pin ls even if a parent node is directly pinned +func (tp *TestSuite) TestPinLsIndirect(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "foo") + + err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid())) + if err != nil { + t.Fatal(err) + } + + err = api.Pin().Add(ctx, path.IpldPath(parent.Cid()), opt.Pin.Recursive(false)) + if err != nil { + t.Fatal(err) + } + + assertPinTypes(t, ctx, api, []cidContainer{grandparent}, []cidContainer{parent}, []cidContainer{leaf}) +} + +// TestPinLsPrecedence verifies the precedence of pins (recursive > direct > indirect) +func (tp *TestSuite) TestPinLsPrecedence(t *testing.T) { + // Testing precedence of recursive, direct and indirect pins + // Results should be recursive > indirect, direct > indirect, and recursive > direct + + t.Run("TestPinLsPredenceRecursiveIndirect", tp.TestPinLsPredenceRecursiveIndirect) + t.Run("TestPinLsPrecedenceDirectIndirect", tp.TestPinLsPrecedenceDirectIndirect) + t.Run("TestPinLsPrecedenceRecursiveDirect", tp.TestPinLsPrecedenceRecursiveDirect) +} + +func (tp *TestSuite) TestPinLsPredenceRecursiveIndirect(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + // Test recursive > indirect + leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "recursive > indirect") + + err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid())) + if err != nil { + t.Fatal(err) + } + + err = api.Pin().Add(ctx, path.IpldPath(parent.Cid())) + if err != nil { + t.Fatal(err) + } + + assertPinTypes(t, ctx, api, []cidContainer{grandparent, parent}, []cidContainer{}, []cidContainer{leaf}) +} + +func (tp *TestSuite) TestPinLsPrecedenceDirectIndirect(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + // Test direct > indirect + leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "direct > indirect") + + err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid())) + if err != nil { + t.Fatal(err) + } + + err = api.Pin().Add(ctx, path.IpldPath(parent.Cid()), opt.Pin.Recursive(false)) + if err != nil { + t.Fatal(err) + } + + assertPinTypes(t, ctx, api, []cidContainer{grandparent}, []cidContainer{parent}, []cidContainer{leaf}) +} + +func (tp *TestSuite) TestPinLsPrecedenceRecursiveDirect(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + // Test recursive > direct + leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "recursive + direct = error") + + err = api.Pin().Add(ctx, path.IpldPath(parent.Cid())) + if err != nil { + t.Fatal(err) + } + + err = api.Pin().Add(ctx, path.IpldPath(parent.Cid()), opt.Pin.Recursive(false)) + if err == nil { + t.Fatal("expected error directly pinning a recursively pinned node") + } + + assertPinTypes(t, ctx, api, []cidContainer{parent}, []cidContainer{}, []cidContainer{leaf}) + + err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid()), opt.Pin.Recursive(false)) + if err != nil { + t.Fatal(err) + } + + err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid())) + if err != nil { + t.Fatal(err) + } + + assertPinTypes(t, ctx, api, []cidContainer{grandparent, parent}, []cidContainer{}, []cidContainer{leaf}) +} + +type cidContainer interface { + Cid() cid.Cid +} + +func getThreeChainedNodes(t *testing.T, ctx context.Context, api iface.CoreAPI, leafData string) (cidContainer, cidContainer, cidContainer) { + leaf, err := api.Unixfs().Add(ctx, strFile(leafData)()) + if err != nil { + t.Fatal(err) + } + + parent, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+leaf.Cid().String()+`"}}`), math.MaxUint64, -1) + if err != nil { + t.Fatal(err) + } + + grandparent, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+parent.Cid().String()+`"}}`), math.MaxUint64, -1) + if err != nil { + t.Fatal(err) + } + + if err := api.Dag().AddMany(ctx, []ipld.Node{parent, grandparent}); err != nil { + t.Fatal(err) + } + + return leaf, parent, grandparent +} + +func assertPinTypes(t *testing.T, ctx context.Context, api iface.CoreAPI, recusive, direct, indirect []cidContainer) { + assertPinLsAllConsistency(t, ctx, api) + + list, err := api.Pin().Ls(ctx, opt.Pin.Type.Recursive()) + if err != nil { + t.Fatal(err) + } + + assertPinCids(t, list, recusive...) + + list, err = api.Pin().Ls(ctx, opt.Pin.Type.Direct()) + if err != nil { + t.Fatal(err) + } + + assertPinCids(t, list, direct...) + + list, err = api.Pin().Ls(ctx, opt.Pin.Type.Indirect()) + if err != nil { + t.Fatal(err) + } + + assertPinCids(t, list, indirect...) +} + +// assertPinCids verifies that the pins match the expected cids +func assertPinCids(t *testing.T, pins []iface.Pin, cids ...cidContainer) { + t.Helper() + + if expected, actual := len(cids), len(pins); expected != actual { + t.Fatalf("expected pin list to have len %d, was %d", expected, actual) + } + + cSet := cid.NewSet() + for _, c := range cids { + cSet.Add(c.Cid()) + } + + valid := true + for _, p := range pins { + c := p.Path().Cid() + if cSet.Has(c) { + cSet.Remove(c) + } else { + valid = false + break + } + } + + valid = valid && cSet.Len() == 0 + + if !valid { + pinStrs := make([]string, len(pins)) + for i, p := range pins { + pinStrs[i] = p.Path().Cid().String() + } + pathStrs := make([]string, len(cids)) + for i, c := range cids { + pathStrs[i] = c.Cid().String() + } + t.Fatalf("expected: %s \nactual: %s", strings.Join(pathStrs, ", "), strings.Join(pinStrs, ", ")) + } +} + +// assertPinLsAllConsistency verifies that listing all pins gives the same result as listing the pin types individually +func assertPinLsAllConsistency(t *testing.T, ctx context.Context, api iface.CoreAPI) { + t.Helper() + allPins, err := api.Pin().Ls(ctx) + if err != nil { + t.Fatal(err) + } + + type pinTypeProps struct { + *cid.Set + opt.PinLsOption + } + + all, recursive, direct, indirect := cid.NewSet(), cid.NewSet(), cid.NewSet(), cid.NewSet() + typeMap := map[string]*pinTypeProps{ + "recursive": {recursive, opt.Pin.Type.Recursive()}, + "direct": {direct, opt.Pin.Type.Direct()}, + "indirect": {indirect, opt.Pin.Type.Indirect()}, + } + + for _, p := range allPins { + if !all.Visit(p.Path().Cid()) { + t.Fatalf("pin ls returned the same cid multiple times") + } + + typeStr := p.Type() + if typeSet, ok := typeMap[p.Type()]; ok { + typeSet.Add(p.Path().Cid()) + } else { + t.Fatalf("unknown pin type: %s", typeStr) + } + } + + for typeStr, pinProps := range typeMap { + pins, err := api.Pin().Ls(ctx, pinProps.PinLsOption) + if err != nil { + t.Fatal(err) + } + + if expected, actual := len(pins), pinProps.Set.Len(); expected != actual { + t.Fatalf("pin ls all has %d pins of type %s, but pin ls for the type has %d", expected, typeStr, actual) + } + + for _, p := range pins { + if pinType := p.Type(); pinType != typeStr { + t.Fatalf("returned wrong pin type: expected %s, got %s", typeStr, pinType) + } + + if c := p.Path().Cid(); !pinProps.Has(c) { + t.Fatalf("%s expected to be in pin ls all as type %s", c.String(), typeStr) + } + } + } +} From 1cb0a281f44440e83bbe3fc3d04d609f937e8087 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 15 Oct 2019 22:48:08 +0900 Subject: [PATCH 3128/3817] chore(dep): update autogenerated protobuf code This commit was moved from ipfs/go-filestore@fb13425f8a5bd9e186d3159c2c0185a5e8eda78a --- filestore/pb/dataobj.pb.go | 136 +++++++++++++++---------------------- 1 file changed, 56 insertions(+), 80 deletions(-) diff --git a/filestore/pb/dataobj.pb.go b/filestore/pb/dataobj.pb.go index 59650a11c..5ecc2489e 100644 --- a/filestore/pb/dataobj.pb.go +++ b/filestore/pb/dataobj.pb.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: filestore/pb/dataobj.proto +// source: dataobj.proto package datastore_pb @@ -8,6 +8,7 @@ import ( proto "github.com/gogo/protobuf/proto" io "io" math "math" + math_bits "math/bits" ) // Reference imports to suppress errors if they are not otherwise used. @@ -19,7 +20,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type DataObj struct { FilePath string `protobuf:"bytes,1,opt,name=FilePath" json:"FilePath"` @@ -31,7 +32,7 @@ func (m *DataObj) Reset() { *m = DataObj{} } func (m *DataObj) String() string { return proto.CompactTextString(m) } func (*DataObj) ProtoMessage() {} func (*DataObj) Descriptor() ([]byte, []int) { - return fileDescriptor_86a3613fbaff9a6c, []int{0} + return fileDescriptor_a76cb282d869d683, []int{0} } func (m *DataObj) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -41,7 +42,7 @@ func (m *DataObj) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_DataObj.Marshal(b, m, deterministic) } else { b = b[:cap(b)] - n, err := m.MarshalTo(b) + n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } @@ -85,26 +86,26 @@ func init() { proto.RegisterType((*DataObj)(nil), "datastore.pb.DataObj") } -func init() { proto.RegisterFile("filestore/pb/dataobj.proto", fileDescriptor_86a3613fbaff9a6c) } +func init() { proto.RegisterFile("dataobj.proto", fileDescriptor_a76cb282d869d683) } -var fileDescriptor_86a3613fbaff9a6c = []byte{ - // 160 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4a, 0xcb, 0xcc, 0x49, - 0x2d, 0x2e, 0xc9, 0x2f, 0x4a, 0xd5, 0x2f, 0x48, 0xd2, 0x4f, 0x49, 0x2c, 0x49, 0xcc, 0x4f, 0xca, - 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x01, 0x71, 0xc1, 0x72, 0x7a, 0x05, 0x49, 0x4a, - 0xc9, 0x5c, 0xec, 0x2e, 0x89, 0x25, 0x89, 0xfe, 0x49, 0x59, 0x42, 0x0a, 0x5c, 0x1c, 0x6e, 0x99, - 0x39, 0xa9, 0x01, 0x89, 0x25, 0x19, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x4e, 0x2c, 0x27, 0xee, - 0xc9, 0x33, 0x04, 0xc1, 0x45, 0x85, 0x64, 0xb8, 0xd8, 0xfc, 0xd3, 0xd2, 0x8a, 0x53, 0x4b, 0x24, - 0x98, 0x14, 0x18, 0x35, 0x58, 0xa0, 0xf2, 0x50, 0x31, 0x21, 0x09, 0x2e, 0x96, 0xe0, 0xcc, 0xaa, - 0x54, 0x09, 0x66, 0x24, 0x39, 0xb0, 0x88, 0x93, 0xc4, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, - 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, - 0xcb, 0x31, 0x00, 0x02, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x87, 0xf5, 0x88, 0xa9, 0x00, 0x00, 0x00, +var fileDescriptor_a76cb282d869d683 = []byte{ + // 150 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4d, 0x49, 0x2c, 0x49, + 0xcc, 0x4f, 0xca, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x01, 0x71, 0x8b, 0x4b, 0xf2, + 0x8b, 0x52, 0xf5, 0x0a, 0x92, 0x94, 0x92, 0xb9, 0xd8, 0x5d, 0x12, 0x4b, 0x12, 0xfd, 0x93, 0xb2, + 0x84, 0x14, 0xb8, 0x38, 0xdc, 0x32, 0x73, 0x52, 0x03, 0x12, 0x4b, 0x32, 0x24, 0x18, 0x15, 0x18, + 0x35, 0x38, 0x9d, 0x58, 0x4e, 0xdc, 0x93, 0x67, 0x08, 0x82, 0x8b, 0x0a, 0xc9, 0x70, 0xb1, 0xf9, + 0xa7, 0xa5, 0x15, 0xa7, 0x96, 0x48, 0x30, 0x29, 0x30, 0x6a, 0xb0, 0x40, 0xe5, 0xa1, 0x62, 0x42, + 0x12, 0x5c, 0x2c, 0xc1, 0x99, 0x55, 0xa9, 0x12, 0xcc, 0x48, 0x72, 0x60, 0x11, 0x27, 0x89, 0x13, + 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, + 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x00, 0x04, 0x00, 0x00, 0xff, 0xff, 0x5d, 0x4a, + 0x76, 0xa0, 0x9c, 0x00, 0x00, 0x00, } func (m *DataObj) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -112,31 +113,39 @@ func (m *DataObj) Marshal() (dAtA []byte, err error) { } func (m *DataObj) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DataObj) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintDataobj(dAtA, i, uint64(len(m.FilePath))) - i += copy(dAtA[i:], m.FilePath) - dAtA[i] = 0x10 - i++ - i = encodeVarintDataobj(dAtA, i, uint64(m.Offset)) - dAtA[i] = 0x18 - i++ i = encodeVarintDataobj(dAtA, i, uint64(m.Size_)) - return i, nil + i-- + dAtA[i] = 0x18 + i = encodeVarintDataobj(dAtA, i, uint64(m.Offset)) + i-- + dAtA[i] = 0x10 + i -= len(m.FilePath) + copy(dAtA[i:], m.FilePath) + i = encodeVarintDataobj(dAtA, i, uint64(len(m.FilePath))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func encodeVarintDataobj(dAtA []byte, offset int, v uint64) int { + offset -= sovDataobj(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *DataObj) Size() (n int) { if m == nil { @@ -152,14 +161,7 @@ func (m *DataObj) Size() (n int) { } func sovDataobj(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozDataobj(x uint64) (n int) { return sovDataobj(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -290,6 +292,7 @@ func (m *DataObj) Unmarshal(dAtA []byte) error { func skipDataobj(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -321,10 +324,8 @@ func skipDataobj(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -345,55 +346,30 @@ func skipDataobj(dAtA []byte) (n int, err error) { return 0, ErrInvalidLengthDataobj } iNdEx += length - if iNdEx < 0 { - return 0, ErrInvalidLengthDataobj - } - return iNdEx, nil case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowDataobj - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipDataobj(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - if iNdEx < 0 { - return 0, ErrInvalidLengthDataobj - } - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupDataobj + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthDataobj + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthDataobj = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowDataobj = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthDataobj = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowDataobj = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupDataobj = fmt.Errorf("proto: unexpected end of group") ) From 5ef6d0b270e746103855f344be5bf9f423d391e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Thu, 17 Oct 2019 18:16:55 +0200 Subject: [PATCH 3129/3817] adapt to go-ipfs Pinner interface changes with context and error This commit was moved from ipfs/go-ipfs-provider@f2597dc7065a64896c4f693056aa21fa6de0c8d7 --- provider/simple/reprovide.go | 18 ++++++++++++++---- provider/simple/reprovide_test.go | 8 ++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index 4cc48ec2a..e5afe80d2 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -171,8 +171,8 @@ func NewBlockstoreProvider(bstore blocks.Blockstore) KeyChanFunc { // Pinner interface defines how the simple.Reprovider wants to interact // with a Pinning service type Pinner interface { - DirectKeys() []cid.Cid - RecursiveKeys() []cid.Cid + DirectKeys(ctx context.Context) ([]cid.Cid, error) + RecursiveKeys(ctx context.Context) ([]cid.Cid, error) } // NewPinnedProvider returns provider supplying pinned keys @@ -208,11 +208,21 @@ func pinSet(ctx context.Context, pinning Pinner, dag ipld.DAGService, onlyRoots defer cancel() defer close(set.New) - for _, key := range pinning.DirectKeys() { + dkeys, err := pinning.DirectKeys(ctx) + if err != nil { + logR.Errorf("reprovide direct pins: %s", err) + return + } + for _, key := range dkeys { set.Visitor(ctx)(key) } - for _, key := range pinning.RecursiveKeys() { + rkeys, err := pinning.RecursiveKeys(ctx) + if err != nil { + logR.Errorf("reprovide indirect pins: %s", err) + return + } + for _, key := range rkeys { if onlyRoots { set.Visitor(ctx)(key) } else { diff --git a/provider/simple/reprovide_test.go b/provider/simple/reprovide_test.go index efe906f01..322e4c10a 100644 --- a/provider/simple/reprovide_test.go +++ b/provider/simple/reprovide_test.go @@ -100,12 +100,12 @@ type mockPinner struct { direct []cid.Cid } -func (mp *mockPinner) DirectKeys() []cid.Cid { - return mp.direct +func (mp *mockPinner) DirectKeys(ctx context.Context) ([]cid.Cid, error) { + return mp.direct, nil } -func (mp *mockPinner) RecursiveKeys() []cid.Cid { - return mp.recursive +func (mp *mockPinner) RecursiveKeys(ctx context.Context) ([]cid.Cid, error) { + return mp.recursive, nil } func TestReprovidePinned(t *testing.T) { From 1a94e111b02109ba9da507b22f50dba9d8c8a415 Mon Sep 17 00:00:00 2001 From: frrist Date: Thu, 17 Oct 2019 13:56:32 -0700 Subject: [PATCH 3130/3817] update go-merkledag too v0.2.4 This commit was moved from ipld/go-car@6bca8656ee37dcc2ed011117958f64427c2ef2ab --- ipld/car/car.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index 7a32b4073..f24c42fae 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -47,7 +47,7 @@ func WriteCar(ctx context.Context, ds format.DAGService, roots []cid.Cid, w io.W seen := cid.NewSet() for _, r := range roots { - if err := dag.EnumerateChildren(ctx, cw.enumGetLinks, r, seen.Visit); err != nil { + if err := dag.Walk(ctx, cw.enumGetLinks, r, seen.Visit); err != nil { return err } } From fb1137a7d4fa1bc7b4856e20728f259f5d8efd82 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 6 Nov 2019 18:08:07 +0100 Subject: [PATCH 3131/3817] Improve performance of buzhash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` name old time/op new time/op delta Buzhash2/1K-4 610ns ± 4% 643ns ±16% ~ (p=0.421 n=8+10) Buzhash2/1M-4 1.25ms ± 5% 1.16ms ± 4% -7.31% (p=0.000 n=10+10) Buzhash2/16M-4 19.2ms ± 2% 17.5ms ± 2% -8.73% (p=0.000 n=9+9) Buzhash2/100M-4 117ms ± 1% 107ms ± 3% -8.26% (p=0.000 n=10+10) name old speed new speed delta Buzhash2/1K-4 1.68GB/s ± 4% 1.60GB/s ±14% ~ (p=0.408 n=8+10) Buzhash2/1M-4 842MB/s ± 5% 908MB/s ± 3% +7.86% (p=0.000 n=10+10) Buzhash2/16M-4 875MB/s ± 2% 959MB/s ± 2% +9.57% (p=0.000 n=9+9) Buzhash2/100M-4 897MB/s ± 1% 977MB/s ± 3% +9.02% (p=0.000 n=10+10) name old alloc/op new alloc/op delta Buzhash2/1K-4 1.17kB ± 1% 1.17kB ± 0% -0.50% (p=0.006 n=10+10) Buzhash2/1M-4 1.08MB ± 1% 1.07MB ± 0% ~ (p=0.739 n=10+10) Buzhash2/16M-4 17.1MB ± 0% 17.1MB ± 0% ~ (p=0.579 n=10+10) Buzhash2/100M-4 106MB ± 0% 106MB ± 0% -0.01% (p=0.000 n=9+7) name old allocs/op new allocs/op delta Buzhash2/1K-4 3.00 ± 0% 3.00 ± 0% ~ (all equal) Buzhash2/1M-4 8.00 ± 0% 8.00 ± 0% ~ (all equal) Buzhash2/16M-4 72.0 ± 0% 72.0 ± 0% ~ (all equal) Buzhash2/100M-4 406 ± 0% 406 ± 0% ~ (all equal) ``` License: MIT Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-ipfs-chunker@79bdab24e1ecceaadf619ec33a56cadb9760e5c7 --- chunker/buzhash.go | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/chunker/buzhash.go b/chunker/buzhash.go index 54115d59b..b3de95f12 100644 --- a/chunker/buzhash.go +++ b/chunker/buzhash.go @@ -61,17 +61,33 @@ func (b *Buzhash) NextBytes() ([]byte, error) { var state uint32 = 0 + if buzMin > len(b.buf) { + panic("this is impossible") + } + for ; i < buzMin; i++ { state = bits.RotateLeft32(state, 1) state = state ^ bytehash[b.buf[i]] } - if b.n+n > len(b.buf) { - panic("this is impossible, but gives +9 to performance") - } + { + max := b.n + n - 32 - 1 - for ; state&buzMask != 0 && i < b.n+n; i++ { - state = bits.RotateLeft32(state, 1) ^ bytehash[b.buf[i-32]] ^ bytehash[b.buf[i]] + buf := b.buf + bufshf := b.buf[32:] + i = buzMin - 32 + _ = buf[max] + _ = bufshf[max] + + for ; i <= max; i++ { + if state&buzMask == 0 { + break + } + state = bits.RotateLeft32(state, 1) ^ + bytehash[buf[i]] ^ + bytehash[bufshf[i]] + } + i += 32 } res := make([]byte, i) From f2c8939c6e04cbd55a468aeefae68d2a5e3c0305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Sun, 15 Sep 2019 14:14:42 +0200 Subject: [PATCH 3132/3817] pin: add context and error return to most of the Pinner functions This commit was moved from ipfs/go-ipfs-pinner@ba91b68a6557d9af4017b7b4eff089d4a155c36b --- pinning/pinner/gc/gc.go | 18 ++++++++++--- pinning/pinner/pin.go | 52 +++++++++++++++----------------------- pinning/pinner/pin_test.go | 8 +++--- 3 files changed, 40 insertions(+), 38 deletions(-) diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go index e03072770..a8309aeac 100644 --- a/pinning/pinner/gc/gc.go +++ b/pinning/pinner/gc/gc.go @@ -201,7 +201,11 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo } return links, nil } - err := Descendants(ctx, getLinks, gcs, pn.RecursiveKeys()) + rkeys, err := pn.RecursiveKeys(ctx) + if err != nil { + return nil, err + } + err = Descendants(ctx, getLinks, gcs, rkeys) if err != nil { errors = true select { @@ -233,11 +237,19 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo } } - for _, k := range pn.DirectKeys() { + dkeys, err := pn.DirectKeys(ctx) + if err != nil { + return nil, err + } + for _, k := range dkeys { gcs.Add(k) } - err = Descendants(ctx, getLinks, gcs, pn.InternalPins()) + ikeys, err := pn.InternalPins(ctx) + if err != nil { + return nil, err + } + err = Descendants(ctx, getLinks, gcs, ikeys) if err != nil { errors = true select { diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 63fa663c1..85a25c8ca 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -105,11 +105,11 @@ func StringToMode(s string) (Mode, bool) { type Pinner interface { // IsPinned returns whether or not the given cid is pinned // and an explanation of why its pinned - IsPinned(cid.Cid) (string, bool, error) + IsPinned(ctx context.Context, c cid.Cid) (string, bool, error) // IsPinnedWithType returns whether or not the given cid is pinned with the // given pin type, as well as returning the type of pin its pinned with. - IsPinnedWithType(cid.Cid, Mode) (string, bool, error) + IsPinnedWithType(ctx context.Context, c cid.Cid, mode Mode) (string, bool, error) // Pin the given node, optionally recursively. Pin(ctx context.Context, node ipld.Node, recursive bool) error @@ -125,7 +125,7 @@ type Pinner interface { // Check if a set of keys are pinned, more efficient than // calling IsPinned for each key - CheckIfPinned(cids ...cid.Cid) ([]Pinned, error) + CheckIfPinned(ctx context.Context, cids ...cid.Cid) ([]Pinned, error) // PinWithMode is for manually editing the pin structure. Use with // care! If used improperly, garbage collection may not be @@ -138,17 +138,17 @@ type Pinner interface { RemovePinWithMode(cid.Cid, Mode) // Flush writes the pin state to the backing datastore - Flush() error + Flush(ctx context.Context) error // DirectKeys returns all directly pinned cids - DirectKeys() []cid.Cid + DirectKeys(ctx context.Context) ([]cid.Cid, error) // DirectKeys returns all recursively pinned cids - RecursiveKeys() []cid.Cid + RecursiveKeys(ctx context.Context) ([]cid.Cid, error) // InternalPins returns all cids kept pinned for the internal state of the // pinner - InternalPins() []cid.Cid + InternalPins(ctx context.Context) ([]cid.Cid, error) } // Pinned represents CID which has been pinned with a pinning strategy. @@ -211,8 +211,6 @@ func NewPinner(dstore ds.Datastore, serv, internal ipld.DAGService) Pinner { // Pin the given node, optionally recursive func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { - p.lock.Lock() - defer p.lock.Unlock() err := p.dserv.Add(ctx, node) if err != nil { return err @@ -220,13 +218,16 @@ func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { c := node.Cid() + p.lock.Lock() + defer p.lock.Unlock() + if recurse { if p.recursePin.Has(c) { return nil } p.lock.Unlock() - // fetch entire graph + // temporary unlock to fetch the entire graph err := mdag.FetchGraph(ctx, c, p.dserv) p.lock.Lock() if err != nil { @@ -243,13 +244,6 @@ func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { p.recursePin.Add(c) } else { - p.lock.Unlock() - _, err := p.dserv.Get(ctx, c) - p.lock.Lock() - if err != nil { - return err - } - if p.recursePin.Has(c) { return fmt.Errorf("%s already pinned recursively", c.String()) } @@ -286,15 +280,13 @@ func (p *pinner) isInternalPin(c cid.Cid) bool { // IsPinned returns whether or not the given key is pinned // and an explanation of why its pinned -func (p *pinner) IsPinned(c cid.Cid) (string, bool, error) { - p.lock.RLock() - defer p.lock.RUnlock() +func (p *pinner) IsPinned(ctx context.Context, c cid.Cid) (string, bool, error) { return p.isPinnedWithType(c, Any) } // IsPinnedWithType returns whether or not the given cid is pinned with the // given pin type, as well as returning the type of pin its pinned with. -func (p *pinner) IsPinnedWithType(c cid.Cid, mode Mode) (string, bool, error) { +func (p *pinner) IsPinnedWithType(ctx context.Context, c cid.Cid, mode Mode) (string, bool, error) { p.lock.RLock() defer p.lock.RUnlock() return p.isPinnedWithType(c, mode) @@ -347,7 +339,7 @@ func (p *pinner) isPinnedWithType(c cid.Cid, mode Mode) (string, bool, error) { // CheckIfPinned Checks if a set of keys are pinned, more efficient than // calling IsPinned for each key, returns the pinned status of cid(s) -func (p *pinner) CheckIfPinned(cids ...cid.Cid) ([]Pinned, error) { +func (p *pinner) CheckIfPinned(ctx context.Context, cids ...cid.Cid) ([]Pinned, error) { p.lock.RLock() defer p.lock.RUnlock() pinned := make([]Pinned, 0, len(cids)) @@ -494,19 +486,19 @@ func LoadPinner(d ds.Datastore, dserv, internal ipld.DAGService) (Pinner, error) } // DirectKeys returns a slice containing the directly pinned keys -func (p *pinner) DirectKeys() []cid.Cid { +func (p *pinner) DirectKeys(ctx context.Context) ([]cid.Cid, error) { p.lock.RLock() defer p.lock.RUnlock() - return p.directPin.Keys() + return p.directPin.Keys(), nil } // RecursiveKeys returns a slice containing the recursively pinned keys -func (p *pinner) RecursiveKeys() []cid.Cid { +func (p *pinner) RecursiveKeys(ctx context.Context) ([]cid.Cid, error) { p.lock.RLock() defer p.lock.RUnlock() - return p.recursePin.Keys() + return p.recursePin.Keys(), nil } // Update updates a recursive pin from one cid to another @@ -541,12 +533,10 @@ func (p *pinner) Update(ctx context.Context, from, to cid.Cid, unpin bool) error } // Flush encodes and writes pinner keysets to the datastore -func (p *pinner) Flush() error { +func (p *pinner) Flush(ctx context.Context) error { p.lock.Lock() defer p.lock.Unlock() - ctx := context.TODO() - internalset := cid.NewSet() recordInternal := internalset.Add @@ -594,12 +584,12 @@ func (p *pinner) Flush() error { // InternalPins returns all cids kept pinned for the internal state of the // pinner -func (p *pinner) InternalPins() []cid.Cid { +func (p *pinner) InternalPins(ctx context.Context) ([]cid.Cid, error) { p.lock.Lock() defer p.lock.Unlock() var out []cid.Cid out = append(out, p.internalPin.Keys()...) - return out + return out, nil } // PinWithMode allows the user to have fine grained control over pin diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/pin_test.go index 27e4c71de..e477ac07f 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/pin_test.go @@ -31,7 +31,7 @@ func randNode() (*mdag.ProtoNode, cid.Cid) { } func assertPinned(t *testing.T, p Pinner, c cid.Cid, failmsg string) { - _, pinned, err := p.IsPinned(c) + _, pinned, err := p.IsPinned(context.Background(), c) if err != nil { t.Fatal(err) } @@ -42,7 +42,7 @@ func assertPinned(t *testing.T, p Pinner, c cid.Cid, failmsg string) { } func assertUnpinned(t *testing.T, p Pinner, c cid.Cid, failmsg string) { - _, pinned, err := p.IsPinned(c) + _, pinned, err := p.IsPinned(context.Background(), c) if err != nil { t.Fatal(err) } @@ -146,7 +146,7 @@ func TestPinnerBasic(t *testing.T) { t.Fatal(err) } - err = p.Flush() + err = p.Flush(ctx) if err != nil { t.Fatal(err) } @@ -327,7 +327,7 @@ func TestFlush(t *testing.T) { _, k := randNode() p.PinWithMode(k, Recursive) - if err := p.Flush(); err != nil { + if err := p.Flush(context.Background()); err != nil { t.Fatal(err) } assertPinned(t, p, k, "expected key to still be pinned") From 51832d4b35cb69388b14b12bd3b09d070ac2c841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Mon, 18 Nov 2019 18:26:20 +0100 Subject: [PATCH 3133/3817] pin: fix a too aggressive refactor and connect some contexts This commit was moved from ipfs/go-ipfs-pinner@56115933a32b02fa8896abaf77256956041f3900 --- pinning/pinner/pin.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 85a25c8ca..15b2396b5 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -281,7 +281,9 @@ func (p *pinner) isInternalPin(c cid.Cid) bool { // IsPinned returns whether or not the given key is pinned // and an explanation of why its pinned func (p *pinner) IsPinned(ctx context.Context, c cid.Cid) (string, bool, error) { - return p.isPinnedWithType(c, Any) + p.lock.RLock() + defer p.lock.RUnlock() + return p.isPinnedWithType(ctx, c, Any) } // IsPinnedWithType returns whether or not the given cid is pinned with the @@ -289,12 +291,12 @@ func (p *pinner) IsPinned(ctx context.Context, c cid.Cid) (string, bool, error) func (p *pinner) IsPinnedWithType(ctx context.Context, c cid.Cid, mode Mode) (string, bool, error) { p.lock.RLock() defer p.lock.RUnlock() - return p.isPinnedWithType(c, mode) + return p.isPinnedWithType(ctx, c, mode) } // isPinnedWithType is the implementation of IsPinnedWithType that does not lock. // intended for use by other pinned methods that already take locks -func (p *pinner) isPinnedWithType(c cid.Cid, mode Mode) (string, bool, error) { +func (p *pinner) isPinnedWithType(ctx context.Context, c cid.Cid, mode Mode) (string, bool, error) { switch mode { case Any, Direct, Indirect, Recursive, Internal: default: @@ -326,7 +328,7 @@ func (p *pinner) isPinnedWithType(c cid.Cid, mode Mode) (string, bool, error) { // Default is Indirect visitedSet := cid.NewSet() for _, rc := range p.recursePin.Keys() { - has, err := hasChild(p.dserv, rc, c, visitedSet.Visit) + has, err := hasChild(ctx, p.dserv, rc, c, visitedSet.Visit) if err != nil { return "", false, err } @@ -361,7 +363,7 @@ func (p *pinner) CheckIfPinned(ctx context.Context, cids ...cid.Cid) ([]Pinned, // Now walk all recursive pins to check for indirect pins var checkChildren func(cid.Cid, cid.Cid) error checkChildren = func(rk, parentKey cid.Cid) error { - links, err := ipld.GetLinks(context.TODO(), p.dserv, parentKey) + links, err := ipld.GetLinks(ctx, p.dserv, parentKey) if err != nil { return err } @@ -607,8 +609,8 @@ func (p *pinner) PinWithMode(c cid.Cid, mode Mode) { // hasChild recursively looks for a Cid among the children of a root Cid. // The visit function can be used to shortcut already-visited branches. -func hasChild(ng ipld.NodeGetter, root cid.Cid, child cid.Cid, visit func(cid.Cid) bool) (bool, error) { - links, err := ipld.GetLinks(context.TODO(), ng, root) +func hasChild(ctx context.Context, ng ipld.NodeGetter, root cid.Cid, child cid.Cid, visit func(cid.Cid) bool) (bool, error) { + links, err := ipld.GetLinks(ctx, ng, root) if err != nil { return false, err } @@ -618,7 +620,7 @@ func hasChild(ng ipld.NodeGetter, root cid.Cid, child cid.Cid, visit func(cid.Ci return true, nil } if visit(c) { - has, err := hasChild(ng, c, child, visit) + has, err := hasChild(ctx, ng, c, child, visit) if err != nil { return false, err } From bb3c51169b41c7ac2d3a6b3782531d806db98b0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Sun, 15 Sep 2019 14:14:42 +0200 Subject: [PATCH 3134/3817] pin: add context and error return to most of the Pinner functions This commit was moved from ipfs/go-namesys@52519e5f689478d70c3b3feb7acd25eeeb6ff0c0 --- namesys/publisher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index c06deb795..17c4e1b3f 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -298,7 +298,7 @@ func InitializeKeyspace(ctx context.Context, pub Publisher, pins pin.Pinner, key return err } - err = pins.Flush() + err = pins.Flush(ctx) if err != nil { return err } From 55e626aa3003307be0654d3a1ef421c50a07582b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Mon, 18 Nov 2019 19:42:51 +0100 Subject: [PATCH 3135/3817] remove the GC code This commit was moved from ipfs/go-ipfs-pinner@3aa7e5e6954c444965e88de4eb609ee1757694f3 --- pinning/pinner/gc/gc.go | 303 ---------------------------------------- 1 file changed, 303 deletions(-) delete mode 100644 pinning/pinner/gc/gc.go diff --git a/pinning/pinner/gc/gc.go b/pinning/pinner/gc/gc.go deleted file mode 100644 index a8309aeac..000000000 --- a/pinning/pinner/gc/gc.go +++ /dev/null @@ -1,303 +0,0 @@ -// Package gc provides garbage collection for go-ipfs. -package gc - -import ( - "context" - "errors" - "fmt" - "strings" - - bserv "github.com/ipfs/go-blockservice" - pin "github.com/ipfs/go-ipfs/pin" - dag "github.com/ipfs/go-merkledag" - - cid "github.com/ipfs/go-cid" - dstore "github.com/ipfs/go-datastore" - bstore "github.com/ipfs/go-ipfs-blockstore" - offline "github.com/ipfs/go-ipfs-exchange-offline" - ipld "github.com/ipfs/go-ipld-format" - logging "github.com/ipfs/go-log" - "github.com/ipfs/go-verifcid" -) - -var log = logging.Logger("gc") - -// Result represents an incremental output from a garbage collection -// run. It contains either an error, or the cid of a removed object. -type Result struct { - KeyRemoved cid.Cid - Error error -} - -// GC performs a mark and sweep garbage collection of the blocks in the blockstore -// first, it creates a 'marked' set and adds to it the following: -// - all recursively pinned blocks, plus all of their descendants (recursively) -// - bestEffortRoots, plus all of its descendants (recursively) -// - all directly pinned blocks -// - all blocks utilized internally by the pinner -// -// The routine then iterates over every block in the blockstore and -// deletes any block that is not found in the marked set. -func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn pin.Pinner, bestEffortRoots []cid.Cid) <-chan Result { - ctx, cancel := context.WithCancel(ctx) - - elock := log.EventBegin(ctx, "GC.lockWait") - unlocker := bs.GCLock() - elock.Done() - elock = log.EventBegin(ctx, "GC.locked") - emark := log.EventBegin(ctx, "GC.mark") - - bsrv := bserv.New(bs, offline.Exchange(bs)) - ds := dag.NewDAGService(bsrv) - - output := make(chan Result, 128) - - go func() { - defer cancel() - defer close(output) - defer unlocker.Unlock() - defer elock.Done() - - gcs, err := ColoredSet(ctx, pn, ds, bestEffortRoots, output) - if err != nil { - select { - case output <- Result{Error: err}: - case <-ctx.Done(): - } - return - } - emark.Append(logging.LoggableMap{ - "blackSetSize": fmt.Sprintf("%d", gcs.Len()), - }) - emark.Done() - esweep := log.EventBegin(ctx, "GC.sweep") - - keychan, err := bs.AllKeysChan(ctx) - if err != nil { - select { - case output <- Result{Error: err}: - case <-ctx.Done(): - } - return - } - - errors := false - var removed uint64 - - loop: - for ctx.Err() == nil { // select may not notice that we're "done". - select { - case k, ok := <-keychan: - if !ok { - break loop - } - if !gcs.Has(k) { - err := bs.DeleteBlock(k) - removed++ - if err != nil { - errors = true - select { - case output <- Result{Error: &CannotDeleteBlockError{k, err}}: - case <-ctx.Done(): - break loop - } - // continue as error is non-fatal - continue loop - } - select { - case output <- Result{KeyRemoved: k}: - case <-ctx.Done(): - break loop - } - } - case <-ctx.Done(): - break loop - } - } - esweep.Append(logging.LoggableMap{ - "whiteSetSize": fmt.Sprintf("%d", removed), - }) - esweep.Done() - if errors { - select { - case output <- Result{Error: ErrCannotDeleteSomeBlocks}: - case <-ctx.Done(): - return - } - } - - defer log.EventBegin(ctx, "GC.datastore").Done() - gds, ok := dstor.(dstore.GCDatastore) - if !ok { - return - } - - err = gds.CollectGarbage() - if err != nil { - select { - case output <- Result{Error: err}: - case <-ctx.Done(): - } - return - } - }() - - return output -} - -// Descendants recursively finds all the descendants of the given roots and -// adds them to the given cid.Set, using the provided dag.GetLinks function -// to walk the tree. -func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots []cid.Cid) error { - verifyGetLinks := func(ctx context.Context, c cid.Cid) ([]*ipld.Link, error) { - err := verifcid.ValidateCid(c) - if err != nil { - return nil, err - } - - return getLinks(ctx, c) - } - - verboseCidError := func(err error) error { - if strings.Contains(err.Error(), verifcid.ErrBelowMinimumHashLength.Error()) || - strings.Contains(err.Error(), verifcid.ErrPossiblyInsecureHashFunction.Error()) { - err = fmt.Errorf("\"%s\"\nPlease run 'ipfs pin verify'"+ - " to list insecure hashes. If you want to read them,"+ - " please downgrade your go-ipfs to 0.4.13\n", err) - log.Error(err) - } - return err - } - - for _, c := range roots { - // Walk recursively walks the dag and adds the keys to the given set - err := dag.Walk(ctx, verifyGetLinks, c, set.Visit, dag.Concurrent()) - - if err != nil { - err = verboseCidError(err) - return err - } - } - - return nil -} - -// ColoredSet computes the set of nodes in the graph that are pinned by the -// pins in the given pinner. -func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffortRoots []cid.Cid, output chan<- Result) (*cid.Set, error) { - // KeySet currently implemented in memory, in the future, may be bloom filter or - // disk backed to conserve memory. - errors := false - gcs := cid.NewSet() - getLinks := func(ctx context.Context, cid cid.Cid) ([]*ipld.Link, error) { - links, err := ipld.GetLinks(ctx, ng, cid) - if err != nil { - errors = true - select { - case output <- Result{Error: &CannotFetchLinksError{cid, err}}: - case <-ctx.Done(): - return nil, ctx.Err() - } - } - return links, nil - } - rkeys, err := pn.RecursiveKeys(ctx) - if err != nil { - return nil, err - } - err = Descendants(ctx, getLinks, gcs, rkeys) - if err != nil { - errors = true - select { - case output <- Result{Error: err}: - case <-ctx.Done(): - return nil, ctx.Err() - } - } - - bestEffortGetLinks := func(ctx context.Context, cid cid.Cid) ([]*ipld.Link, error) { - links, err := ipld.GetLinks(ctx, ng, cid) - if err != nil && err != ipld.ErrNotFound { - errors = true - select { - case output <- Result{Error: &CannotFetchLinksError{cid, err}}: - case <-ctx.Done(): - return nil, ctx.Err() - } - } - return links, nil - } - err = Descendants(ctx, bestEffortGetLinks, gcs, bestEffortRoots) - if err != nil { - errors = true - select { - case output <- Result{Error: err}: - case <-ctx.Done(): - return nil, ctx.Err() - } - } - - dkeys, err := pn.DirectKeys(ctx) - if err != nil { - return nil, err - } - for _, k := range dkeys { - gcs.Add(k) - } - - ikeys, err := pn.InternalPins(ctx) - if err != nil { - return nil, err - } - err = Descendants(ctx, getLinks, gcs, ikeys) - if err != nil { - errors = true - select { - case output <- Result{Error: err}: - case <-ctx.Done(): - return nil, ctx.Err() - } - } - - if errors { - return nil, ErrCannotFetchAllLinks - } - - return gcs, nil -} - -// ErrCannotFetchAllLinks is returned as the last Result in the GC output -// channel when there was a error creating the marked set because of a -// problem when finding descendants. -var ErrCannotFetchAllLinks = errors.New("garbage collection aborted: could not retrieve some links") - -// ErrCannotDeleteSomeBlocks is returned when removing blocks marked for -// deletion fails as the last Result in GC output channel. -var ErrCannotDeleteSomeBlocks = errors.New("garbage collection incomplete: could not delete some blocks") - -// CannotFetchLinksError provides detailed information about which links -// could not be fetched and can appear as a Result in the GC output channel. -type CannotFetchLinksError struct { - Key cid.Cid - Err error -} - -// Error implements the error interface for this type with a useful -// message. -func (e *CannotFetchLinksError) Error() string { - return fmt.Sprintf("could not retrieve links for %s: %s", e.Key, e.Err) -} - -// CannotDeleteBlockError provides detailed information about which -// blocks could not be deleted and can appear as a Result in the GC output -// channel. -type CannotDeleteBlockError struct { - Key cid.Cid - Err error -} - -// Error implements the error interface for this type with a -// useful message. -func (e *CannotDeleteBlockError) Error() string { - return fmt.Sprintf("could not remove %s: %s", e.Key, e.Err) -} From 6511e93bd9af73a859258c48821f76a63e444d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Tue, 19 Nov 2019 14:27:30 +0100 Subject: [PATCH 3136/3817] extraction from go-ipfs This commit was moved from ipfs/go-ipfs-pinner@0ee3f875037cbe987f6c812efd6f05c57fcdbdd3 --- pinning/pinner/LICENSE-APACHE | 13 ++++++++++++ pinning/pinner/LICENSE-MIT | 19 ++++++++++++++++++ pinning/pinner/README.md | 37 +++++++++++++++++++++++++++++++++++ pinning/pinner/pin.go | 5 ++--- pinning/pinner/set.go | 6 +++--- pinning/pinner/set_test.go | 3 +-- 6 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 pinning/pinner/LICENSE-APACHE create mode 100644 pinning/pinner/LICENSE-MIT create mode 100644 pinning/pinner/README.md diff --git a/pinning/pinner/LICENSE-APACHE b/pinning/pinner/LICENSE-APACHE new file mode 100644 index 000000000..546514363 --- /dev/null +++ b/pinning/pinner/LICENSE-APACHE @@ -0,0 +1,13 @@ +Copyright 2019. Protocol Labs, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/pinning/pinner/LICENSE-MIT b/pinning/pinner/LICENSE-MIT new file mode 100644 index 000000000..ea532a830 --- /dev/null +++ b/pinning/pinner/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright 2019. Protocol Labs, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pinning/pinner/README.md b/pinning/pinner/README.md new file mode 100644 index 000000000..e2f733171 --- /dev/null +++ b/pinning/pinner/README.md @@ -0,0 +1,37 @@ +# go-ipfs-pinner + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](https://protocol.ai) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) +[![Coverage Status](https://codecov.io/gh/ipfs/go-ipfs-pinner/branch/master/graph/badge.svg)](https://codecov.io/gh/ipfs/go-ipfs-pinner) +[![Travis CI](https://travis-ci.org/ipfs/go-ipfs-pinner.svg?branch=master)](https://travis-ci.org/ipfs/go-ipfs-pinner) + +## Background + +The pinner system is responsible for keeping track of which objects a user wants to keep stored locally + +## Install + +Via `go get`: + +```sh +$ go get github.com/ipfs/go-ipfs-pinner +``` + +> Requires Go 1.13 + +## Documentation + +https://godoc.org/github.com/ipfs/go-ipfs-pinner + +## Contribute + +PRs are welcome! + +Small note: If editing the Readme, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +This library is dual-licensed under Apache 2.0 and MIT terms. + +Copyright 2019. Protocol Labs, Inc. diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 15b2396b5..7902478cb 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -9,13 +9,12 @@ import ( "sync" "time" - "github.com/ipfs/go-ipfs/dagutils" - mdag "github.com/ipfs/go-merkledag" - cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" + "github.com/ipfs/go-ipfs/dagutils" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" + mdag "github.com/ipfs/go-merkledag" ) var log = logging.Logger("pin") diff --git a/pinning/pinner/set.go b/pinning/pinner/set.go index b050c31c4..ca437974f 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/set.go @@ -9,12 +9,12 @@ import ( "hash/fnv" "sort" - "github.com/ipfs/go-ipfs/pin/internal/pb" - "github.com/ipfs/go-merkledag" - "github.com/gogo/protobuf/proto" cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" + "github.com/ipfs/go-merkledag" + + "github.com/ipfs/go-ipfs-pinner/internal/pb" ) const ( diff --git a/pinning/pinner/set_test.go b/pinning/pinner/set_test.go index d9a573c5f..61a3118b2 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/set_test.go @@ -6,13 +6,12 @@ import ( "testing" bserv "github.com/ipfs/go-blockservice" - dag "github.com/ipfs/go-merkledag" - cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dsq "github.com/ipfs/go-datastore/query" blockstore "github.com/ipfs/go-ipfs-blockstore" offline "github.com/ipfs/go-ipfs-exchange-offline" + dag "github.com/ipfs/go-merkledag" ) func ignoreCids(_ cid.Cid) {} From ae9864628926d0dd60dc23ee0d7f1daef72cc567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Tue, 19 Nov 2019 17:04:43 +0100 Subject: [PATCH 3137/3817] import as is the dagutils package from go-ipfs to untangle the import graph This commit was moved from ipfs/go-merkledag@74471d62287697d98d310308d4c2babe3314fb2f --- ipld/merkledag/dagutils/diff.go | 211 +++++++++++++++++++ ipld/merkledag/dagutils/diffenum.go | 99 +++++++++ ipld/merkledag/dagutils/diffenum_test.go | 249 +++++++++++++++++++++++ ipld/merkledag/dagutils/utils.go | 234 +++++++++++++++++++++ ipld/merkledag/dagutils/utils_test.go | 114 +++++++++++ 5 files changed, 907 insertions(+) create mode 100644 ipld/merkledag/dagutils/diff.go create mode 100644 ipld/merkledag/dagutils/diffenum.go create mode 100644 ipld/merkledag/dagutils/diffenum_test.go create mode 100644 ipld/merkledag/dagutils/utils.go create mode 100644 ipld/merkledag/dagutils/utils_test.go diff --git a/ipld/merkledag/dagutils/diff.go b/ipld/merkledag/dagutils/diff.go new file mode 100644 index 000000000..a43756ffd --- /dev/null +++ b/ipld/merkledag/dagutils/diff.go @@ -0,0 +1,211 @@ +package dagutils + +import ( + "context" + "fmt" + "path" + + "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" + dag "github.com/ipfs/go-merkledag" + coreiface "github.com/ipfs/interface-go-ipfs-core" +) + +// These constants define the changes that can be applied to a DAG. +const ( + Add = iota + Remove + Mod +) + +// Change represents a change to a DAG and contains a reference to the old and +// new CIDs. +type Change struct { + Type coreiface.ChangeType + Path string + Before cid.Cid + After cid.Cid +} + +// String prints a human-friendly line about a change. +func (c *Change) String() string { + switch c.Type { + case Add: + return fmt.Sprintf("Added %s at %s", c.After.String(), c.Path) + case Remove: + return fmt.Sprintf("Removed %s from %s", c.Before.String(), c.Path) + case Mod: + return fmt.Sprintf("Changed %s to %s at %s", c.Before.String(), c.After.String(), c.Path) + default: + panic("nope") + } +} + +// ApplyChange applies the requested changes to the given node in the given dag. +func ApplyChange(ctx context.Context, ds ipld.DAGService, nd *dag.ProtoNode, cs []*Change) (*dag.ProtoNode, error) { + e := NewDagEditor(nd, ds) + for _, c := range cs { + switch c.Type { + case Add: + child, err := ds.Get(ctx, c.After) + if err != nil { + return nil, err + } + + childpb, ok := child.(*dag.ProtoNode) + if !ok { + return nil, dag.ErrNotProtobuf + } + + err = e.InsertNodeAtPath(ctx, c.Path, childpb, nil) + if err != nil { + return nil, err + } + + case Remove: + err := e.RmLink(ctx, c.Path) + if err != nil { + return nil, err + } + + case Mod: + err := e.RmLink(ctx, c.Path) + if err != nil { + return nil, err + } + child, err := ds.Get(ctx, c.After) + if err != nil { + return nil, err + } + + childpb, ok := child.(*dag.ProtoNode) + if !ok { + return nil, dag.ErrNotProtobuf + } + + err = e.InsertNodeAtPath(ctx, c.Path, childpb, nil) + if err != nil { + return nil, err + } + } + } + + return e.Finalize(ctx, ds) +} + +// Diff returns a set of changes that transform node 'a' into node 'b'. +// It only traverses links in the following cases: +// 1. two node's links number are greater than 0. +// 2. both of two nodes are ProtoNode. +// Otherwise, it compares the cid and emits a Mod change object. +func Diff(ctx context.Context, ds ipld.DAGService, a, b ipld.Node) ([]*Change, error) { + // Base case where both nodes are leaves, just compare + // their CIDs. + if len(a.Links()) == 0 && len(b.Links()) == 0 { + return getChange(a, b) + } + + var out []*Change + cleanA, okA := a.Copy().(*dag.ProtoNode) + cleanB, okB := b.Copy().(*dag.ProtoNode) + if !okA || !okB { + return getChange(a, b) + } + + // strip out unchanged stuff + for _, lnk := range a.Links() { + l, _, err := b.ResolveLink([]string{lnk.Name}) + if err == nil { + if l.Cid.Equals(lnk.Cid) { + // no change... ignore it + } else { + anode, err := lnk.GetNode(ctx, ds) + if err != nil { + return nil, err + } + + bnode, err := l.GetNode(ctx, ds) + if err != nil { + return nil, err + } + + sub, err := Diff(ctx, ds, anode, bnode) + if err != nil { + return nil, err + } + + for _, subc := range sub { + subc.Path = path.Join(lnk.Name, subc.Path) + out = append(out, subc) + } + } + _ = cleanA.RemoveNodeLink(l.Name) + _ = cleanB.RemoveNodeLink(l.Name) + } + } + + for _, lnk := range cleanA.Links() { + out = append(out, &Change{ + Type: Remove, + Path: lnk.Name, + Before: lnk.Cid, + }) + } + for _, lnk := range cleanB.Links() { + out = append(out, &Change{ + Type: Add, + Path: lnk.Name, + After: lnk.Cid, + }) + } + + return out, nil +} + +// Conflict represents two incompatible changes and is returned by MergeDiffs(). +type Conflict struct { + A *Change + B *Change +} + +// MergeDiffs takes two slice of changes and adds them to a single slice. +// When a Change from b happens to the same path of an existing change in a, +// a conflict is created and b is not added to the merged slice. +// A slice of Conflicts is returned and contains pointers to the +// Changes involved (which share the same path). +func MergeDiffs(a, b []*Change) ([]*Change, []Conflict) { + var out []*Change + var conflicts []Conflict + paths := make(map[string]*Change) + for _, c := range a { + paths[c.Path] = c + } + + for _, c := range b { + if ca, ok := paths[c.Path]; ok { + conflicts = append(conflicts, Conflict{ + A: ca, + B: c, + }) + } else { + out = append(out, c) + } + } + for _, c := range paths { + out = append(out, c) + } + return out, conflicts +} + +func getChange(a, b ipld.Node) ([]*Change, error) { + if a.Cid().Equals(b.Cid()) { + return []*Change{}, nil + } + return []*Change{ + { + Type: Mod, + Before: a.Cid(), + After: b.Cid(), + }, + }, nil +} diff --git a/ipld/merkledag/dagutils/diffenum.go b/ipld/merkledag/dagutils/diffenum.go new file mode 100644 index 000000000..fdab772c8 --- /dev/null +++ b/ipld/merkledag/dagutils/diffenum.go @@ -0,0 +1,99 @@ +package dagutils + +import ( + "context" + "fmt" + + mdag "github.com/ipfs/go-merkledag" + + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" +) + +// DiffEnumerate fetches every object in the graph pointed to by 'to' that is +// not in 'from'. This can be used to more efficiently fetch a graph if you can +// guarantee you already have the entirety of 'from' +func DiffEnumerate(ctx context.Context, dserv ipld.NodeGetter, from, to cid.Cid) error { + fnd, err := dserv.Get(ctx, from) + if err != nil { + return fmt.Errorf("get %s: %s", from, err) + } + + tnd, err := dserv.Get(ctx, to) + if err != nil { + return fmt.Errorf("get %s: %s", to, err) + } + + diff := getLinkDiff(fnd, tnd) + + sset := cid.NewSet() + for _, c := range diff { + // Since we're already assuming we have everything in the 'from' graph, + // add all those cids to our 'already seen' set to avoid potentially + // enumerating them later + if c.bef.Defined() { + sset.Add(c.bef) + } + } + for _, c := range diff { + if !c.bef.Defined() { + if sset.Has(c.aft) { + continue + } + err := mdag.Walk(ctx, mdag.GetLinksDirect(dserv), c.aft, sset.Visit, mdag.Concurrent()) + if err != nil { + return err + } + } else { + err := DiffEnumerate(ctx, dserv, c.bef, c.aft) + if err != nil { + return err + } + } + } + + return nil +} + +// if both bef and aft are not nil, then that signifies bef was replaces with aft. +// if bef is nil and aft is not, that means aft was newly added +// if aft is nil and bef is not, that means bef was deleted +type diffpair struct { + bef, aft cid.Cid +} + +// getLinkDiff returns a changeset between nodes 'a' and 'b'. Currently does +// not log deletions as our usecase doesnt call for this. +func getLinkDiff(a, b ipld.Node) []diffpair { + ina := make(map[string]*ipld.Link) + inb := make(map[string]*ipld.Link) + var aonly []cid.Cid + for _, l := range b.Links() { + inb[l.Cid.KeyString()] = l + } + for _, l := range a.Links() { + var key = l.Cid.KeyString() + ina[key] = l + if inb[key] == nil { + aonly = append(aonly, l.Cid) + } + } + + var out []diffpair + var aindex int + + for _, l := range b.Links() { + if ina[l.Cid.KeyString()] != nil { + continue + } + + if aindex < len(aonly) { + out = append(out, diffpair{bef: aonly[aindex], aft: l.Cid}) + aindex++ + } else { + out = append(out, diffpair{aft: l.Cid}) + continue + } + } + return out +} diff --git a/ipld/merkledag/dagutils/diffenum_test.go b/ipld/merkledag/dagutils/diffenum_test.go new file mode 100644 index 000000000..e8db27817 --- /dev/null +++ b/ipld/merkledag/dagutils/diffenum_test.go @@ -0,0 +1,249 @@ +package dagutils + +import ( + "context" + "fmt" + "testing" + + dag "github.com/ipfs/go-merkledag" + mdtest "github.com/ipfs/go-merkledag/test" + + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" +) + +func buildNode(name string, desc map[string]ndesc, out map[string]ipld.Node) ipld.Node { + this := desc[name] + nd := new(dag.ProtoNode) + nd.SetData([]byte(name)) + for k, v := range this { + child, ok := out[v] + if !ok { + child = buildNode(v, desc, out) + out[v] = child + } + + if err := nd.AddNodeLink(k, child); err != nil { + panic(err) + } + } + + return nd +} + +type ndesc map[string]string + +func mkGraph(desc map[string]ndesc) map[string]ipld.Node { + out := make(map[string]ipld.Node) + for name := range desc { + if _, ok := out[name]; ok { + continue + } + + out[name] = buildNode(name, desc, out) + } + return out +} + +var tg1 = map[string]ndesc{ + "a1": ndesc{ + "foo": "b", + }, + "b": ndesc{}, + "a2": ndesc{ + "foo": "b", + "bar": "c", + }, + "c": ndesc{}, +} + +var tg2 = map[string]ndesc{ + "a1": ndesc{ + "foo": "b", + }, + "b": ndesc{}, + "a2": ndesc{ + "foo": "b", + "bar": "c", + }, + "c": ndesc{"baz": "d"}, + "d": ndesc{}, +} + +var tg3 = map[string]ndesc{ + "a1": ndesc{ + "foo": "b", + "bar": "c", + }, + "b": ndesc{}, + "a2": ndesc{ + "foo": "b", + "bar": "d", + }, + "c": ndesc{}, + "d": ndesc{}, +} + +var tg4 = map[string]ndesc{ + "a1": ndesc{ + "key1": "b", + "key2": "c", + }, + "a2": ndesc{ + "key1": "b", + "key2": "d", + }, +} + +var tg5 = map[string]ndesc{ + "a1": ndesc{ + "key1": "a", + "key2": "b", + }, + "a2": ndesc{ + "key1": "c", + "key2": "d", + }, +} + +func TestNameMatching(t *testing.T) { + nds := mkGraph(tg4) + + diff := getLinkDiff(nds["a1"], nds["a2"]) + if len(diff) != 1 { + t.Fatal(fmt.Errorf("node diff didn't match by name")) + } +} + +func TestNameMatching2(t *testing.T) { + nds := mkGraph(tg5) + + diff := getLinkDiff(nds["a1"], nds["a2"]) + if len(diff) != 2 { + t.Fatal(fmt.Errorf("incorrect number of link diff elements")) + } + if !(diff[0].bef.Equals(nds["a1"].Links()[0].Cid) && diff[0].aft.Equals(nds["a2"].Links()[0].Cid)) { + t.Fatal(fmt.Errorf("node diff didn't match by name")) + } +} + +func TestDiffEnumBasic(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nds := mkGraph(tg1) + + ds := mdtest.Mock() + lgds := &getLogger{ds: ds} + + for _, nd := range nds { + err := ds.Add(ctx, nd) + if err != nil { + t.Fatal(err) + } + } + + err := DiffEnumerate(ctx, lgds, nds["a1"].Cid(), nds["a2"].Cid()) + if err != nil { + t.Fatal(err) + } + + err = assertCidList(lgds.log, []cid.Cid{nds["a1"].Cid(), nds["a2"].Cid(), nds["c"].Cid()}) + if err != nil { + t.Fatal(err) + } +} + +type getLogger struct { + ds ipld.NodeGetter + log []cid.Cid +} + +func (gl *getLogger) Get(ctx context.Context, c cid.Cid) (ipld.Node, error) { + nd, err := gl.ds.Get(ctx, c) + if err != nil { + return nil, err + } + gl.log = append(gl.log, c) + return nd, nil +} + +func (gl *getLogger) GetMany(ctx context.Context, cids []cid.Cid) <-chan *ipld.NodeOption { + outCh := make(chan *ipld.NodeOption, len(cids)) + nds := gl.ds.GetMany(ctx, cids) + for no := range nds { + if no.Err == nil { + gl.log = append(gl.log, no.Node.Cid()) + } + select { + case outCh <- no: + default: + panic("too many responses") + } + } + return nds +} + +func assertCidList(a, b []cid.Cid) error { + if len(a) != len(b) { + return fmt.Errorf("got different number of cids than expected") + } + for i, c := range a { + if !c.Equals(b[i]) { + return fmt.Errorf("expected %s, got %s", c, b[i]) + } + } + return nil +} + +func TestDiffEnumFail(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nds := mkGraph(tg2) + + ds := mdtest.Mock() + lgds := &getLogger{ds: ds} + + for _, s := range []string{"a1", "a2", "b", "c"} { + err := ds.Add(ctx, nds[s]) + if err != nil { + t.Fatal(err) + } + } + + err := DiffEnumerate(ctx, lgds, nds["a1"].Cid(), nds["a2"].Cid()) + if err != ipld.ErrNotFound { + t.Fatal("expected err not found") + } + + err = assertCidList(lgds.log, []cid.Cid{nds["a1"].Cid(), nds["a2"].Cid(), nds["c"].Cid()}) + if err != nil { + t.Fatal(err) + } + +} + +func TestDiffEnumRecurse(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nds := mkGraph(tg3) + + ds := mdtest.Mock() + lgds := &getLogger{ds: ds} + + for _, s := range []string{"a1", "a2", "b", "c", "d"} { + err := ds.Add(ctx, nds[s]) + if err != nil { + t.Fatal(err) + } + } + + err := DiffEnumerate(ctx, lgds, nds["a1"].Cid(), nds["a2"].Cid()) + if err != nil { + t.Fatal(err) + } + + err = assertCidList(lgds.log, []cid.Cid{nds["a1"].Cid(), nds["a2"].Cid(), nds["c"].Cid(), nds["d"].Cid()}) + if err != nil { + t.Fatal(err) + } +} diff --git a/ipld/merkledag/dagutils/utils.go b/ipld/merkledag/dagutils/utils.go new file mode 100644 index 000000000..3a796a9c5 --- /dev/null +++ b/ipld/merkledag/dagutils/utils.go @@ -0,0 +1,234 @@ +package dagutils + +import ( + "context" + "errors" + + bserv "github.com/ipfs/go-blockservice" + dag "github.com/ipfs/go-merkledag" + path "github.com/ipfs/go-path" + + ds "github.com/ipfs/go-datastore" + syncds "github.com/ipfs/go-datastore/sync" + bstore "github.com/ipfs/go-ipfs-blockstore" + offline "github.com/ipfs/go-ipfs-exchange-offline" + ipld "github.com/ipfs/go-ipld-format" +) + +// Editor represents a ProtoNode tree editor and provides methods to +// modify it. +type Editor struct { + root *dag.ProtoNode + + // tmp is a temporary in memory (for now) dagstore for all of the + // intermediary nodes to be stored in + tmp ipld.DAGService + + // src is the dagstore with *all* of the data on it, it is used to pull + // nodes from for modification (nil is a valid value) + src ipld.DAGService +} + +// NewMemoryDagService returns a new, thread-safe in-memory DAGService. +func NewMemoryDagService() ipld.DAGService { + // build mem-datastore for editor's intermediary nodes + bs := bstore.NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore())) + bsrv := bserv.New(bs, offline.Exchange(bs)) + return dag.NewDAGService(bsrv) +} + +// NewDagEditor returns an ProtoNode editor. +// +// * root is the node to be modified +// * source is the dagstore to pull nodes from (optional) +func NewDagEditor(root *dag.ProtoNode, source ipld.DAGService) *Editor { + return &Editor{ + root: root, + tmp: NewMemoryDagService(), + src: source, + } +} + +// GetNode returns the a copy of the root node being edited. +func (e *Editor) GetNode() *dag.ProtoNode { + return e.root.Copy().(*dag.ProtoNode) +} + +// GetDagService returns the DAGService used by this editor. +func (e *Editor) GetDagService() ipld.DAGService { + return e.tmp +} + +func addLink(ctx context.Context, ds ipld.DAGService, root *dag.ProtoNode, childname string, childnd ipld.Node) (*dag.ProtoNode, error) { + if childname == "" { + return nil, errors.New("cannot create link with no name") + } + + // ensure that the node we are adding is in the dagservice + err := ds.Add(ctx, childnd) + if err != nil { + return nil, err + } + + _ = ds.Remove(ctx, root.Cid()) + + // ensure no link with that name already exists + _ = root.RemoveNodeLink(childname) // ignore error, only option is ErrNotFound + + if err := root.AddNodeLink(childname, childnd); err != nil { + return nil, err + } + + if err := ds.Add(ctx, root); err != nil { + return nil, err + } + return root, nil +} + +// InsertNodeAtPath inserts a new node in the tree and replaces the current root with the new one. +func (e *Editor) InsertNodeAtPath(ctx context.Context, pth string, toinsert ipld.Node, create func() *dag.ProtoNode) error { + splpath := path.SplitList(pth) + nd, err := e.insertNodeAtPath(ctx, e.root, splpath, toinsert, create) + if err != nil { + return err + } + e.root = nd + return nil +} + +func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path []string, toinsert ipld.Node, create func() *dag.ProtoNode) (*dag.ProtoNode, error) { + if len(path) == 1 { + return addLink(ctx, e.tmp, root, path[0], toinsert) + } + + nd, err := root.GetLinkedProtoNode(ctx, e.tmp, path[0]) + if err != nil { + // if 'create' is true, we create directories on the way down as needed + if err == dag.ErrLinkNotFound && create != nil { + nd = create() + err = nil // no longer an error case + } else if err == ipld.ErrNotFound { + // try finding it in our source dagstore + nd, err = root.GetLinkedProtoNode(ctx, e.src, path[0]) + } + + // if we receive an ErrNotFound, then our second 'GetLinkedNode' call + // also fails, we want to error out + if err != nil { + return nil, err + } + } + + ndprime, err := e.insertNodeAtPath(ctx, nd, path[1:], toinsert, create) + if err != nil { + return nil, err + } + + _ = e.tmp.Remove(ctx, root.Cid()) + + _ = root.RemoveNodeLink(path[0]) + err = root.AddNodeLink(path[0], ndprime) + if err != nil { + return nil, err + } + + err = e.tmp.Add(ctx, root) + if err != nil { + return nil, err + } + + return root, nil +} + +// RmLink removes the link with the given name and updates the root node of +// the editor. +func (e *Editor) RmLink(ctx context.Context, pth string) error { + splpath := path.SplitList(pth) + nd, err := e.rmLink(ctx, e.root, splpath) + if err != nil { + return err + } + e.root = nd + return nil +} + +func (e *Editor) rmLink(ctx context.Context, root *dag.ProtoNode, path []string) (*dag.ProtoNode, error) { + if len(path) == 1 { + // base case, remove node in question + err := root.RemoveNodeLink(path[0]) + if err != nil { + return nil, err + } + + err = e.tmp.Add(ctx, root) + if err != nil { + return nil, err + } + + return root, nil + } + + // search for node in both tmp dagstore and source dagstore + nd, err := root.GetLinkedProtoNode(ctx, e.tmp, path[0]) + if err == ipld.ErrNotFound { + nd, err = root.GetLinkedProtoNode(ctx, e.src, path[0]) + } + + if err != nil { + return nil, err + } + + nnode, err := e.rmLink(ctx, nd, path[1:]) + if err != nil { + return nil, err + } + + _ = e.tmp.Remove(ctx, root.Cid()) + + _ = root.RemoveNodeLink(path[0]) + err = root.AddNodeLink(path[0], nnode) + if err != nil { + return nil, err + } + + err = e.tmp.Add(ctx, root) + if err != nil { + return nil, err + } + + return root, nil +} + +// Finalize writes the new DAG to the given DAGService and returns the modified +// root node. +func (e *Editor) Finalize(ctx context.Context, ds ipld.DAGService) (*dag.ProtoNode, error) { + nd := e.GetNode() + err := copyDag(ctx, nd, e.tmp, ds) + return nd, err +} + +func copyDag(ctx context.Context, nd ipld.Node, from, to ipld.DAGService) error { + // TODO(#4609): make this batch. + err := to.Add(ctx, nd) + if err != nil { + return err + } + + for _, lnk := range nd.Links() { + child, err := lnk.GetNode(ctx, from) + if err != nil { + if err == ipld.ErrNotFound { + // not found means we didnt modify it, and it should + // already be in the target datastore + continue + } + return err + } + + err = copyDag(ctx, child, from, to) + if err != nil { + return err + } + } + return nil +} diff --git a/ipld/merkledag/dagutils/utils_test.go b/ipld/merkledag/dagutils/utils_test.go new file mode 100644 index 000000000..c9c55286d --- /dev/null +++ b/ipld/merkledag/dagutils/utils_test.go @@ -0,0 +1,114 @@ +package dagutils + +import ( + "context" + "testing" + + dag "github.com/ipfs/go-merkledag" + mdtest "github.com/ipfs/go-merkledag/test" + path "github.com/ipfs/go-path" + + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" +) + +func TestAddLink(t *testing.T) { + ctx, context := context.WithCancel(context.Background()) + defer context() + + ds := mdtest.Mock() + fishnode := dag.NodeWithData([]byte("fishcakes!")) + + err := ds.Add(ctx, fishnode) + if err != nil { + t.Fatal(err) + } + + nd := new(dag.ProtoNode) + nnode, err := addLink(ctx, ds, nd, "fish", fishnode) + if err != nil { + t.Fatal(err) + } + + fnprime, err := nnode.GetLinkedNode(ctx, ds, "fish") + if err != nil { + t.Fatal(err) + } + + fnpkey := fnprime.Cid() + if !fnpkey.Equals(fishnode.Cid()) { + t.Fatal("wrong child node found!") + } +} + +func assertNodeAtPath(t *testing.T, ds ipld.DAGService, root *dag.ProtoNode, pth string, exp cid.Cid) { + parts := path.SplitList(pth) + cur := root + for _, e := range parts { + nxt, err := cur.GetLinkedProtoNode(context.Background(), ds, e) + if err != nil { + t.Fatal(err) + } + + cur = nxt + } + + curc := cur.Cid() + if !curc.Equals(exp) { + t.Fatal("node not as expected at end of path") + } +} + +func TestInsertNode(t *testing.T) { + root := new(dag.ProtoNode) + e := NewDagEditor(root, nil) + + testInsert(t, e, "a", "anodefortesting", false, "") + testInsert(t, e, "a/b", "data", false, "") + testInsert(t, e, "a/b/c/d/e", "blah", false, "no link by that name") + testInsert(t, e, "a/b/c/d/e", "foo", true, "") + testInsert(t, e, "a/b/c/d/f", "baz", true, "") + testInsert(t, e, "a/b/c/d/f", "bar", true, "") + + testInsert(t, e, "", "bar", true, "cannot create link with no name") + testInsert(t, e, "////", "slashes", true, "cannot create link with no name") + + c := e.GetNode().Cid() + + if c.String() != "QmZ8yeT9uD6ouJPNAYt62XffYuXBT6b4mP4obRSE9cJrSt" { + t.Fatal("output was different than expected: ", c) + } +} + +func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr string) { + child := dag.NodeWithData([]byte(data)) + err := e.tmp.Add(context.Background(), child) + if err != nil { + t.Fatal(err) + } + + var c func() *dag.ProtoNode + if create { + c = func() *dag.ProtoNode { + return &dag.ProtoNode{} + } + } + + err = e.InsertNodeAtPath(context.Background(), path, child, c) + if experr != "" { + var got string + if err != nil { + got = err.Error() + } + if got != experr { + t.Fatalf("expected '%s' but got '%s'", experr, got) + } + return + } + + if err != nil { + t.Fatal(err, path, data, create, experr) + } + + assertNodeAtPath(t, e.tmp, e.root, path, child.Cid()) +} From 502aa44b19511be5d6cb5ca30811f458517ff5ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Tue, 19 Nov 2019 17:07:00 +0100 Subject: [PATCH 3138/3817] sever the dependancy with go-path and interface-go-ipfs-core This commit was moved from ipfs/go-merkledag@c20b9a52f504ac5b13ed51a7256074bff7110c31 --- ipld/merkledag/dagutils/diff.go | 9 ++++++--- ipld/merkledag/dagutils/diffenum.go | 4 ++-- ipld/merkledag/dagutils/diffenum_test.go | 6 +++--- ipld/merkledag/dagutils/utils.go | 10 +++++----- ipld/merkledag/dagutils/utils_test.go | 10 +++++----- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/ipld/merkledag/dagutils/diff.go b/ipld/merkledag/dagutils/diff.go index a43756ffd..501523876 100644 --- a/ipld/merkledag/dagutils/diff.go +++ b/ipld/merkledag/dagutils/diff.go @@ -7,13 +7,16 @@ import ( "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" + dag "github.com/ipfs/go-merkledag" - coreiface "github.com/ipfs/interface-go-ipfs-core" ) +// ChangeType denotes type of change in Change +type ChangeType int + // These constants define the changes that can be applied to a DAG. const ( - Add = iota + Add ChangeType = iota Remove Mod ) @@ -21,7 +24,7 @@ const ( // Change represents a change to a DAG and contains a reference to the old and // new CIDs. type Change struct { - Type coreiface.ChangeType + Type ChangeType Path string Before cid.Cid After cid.Cid diff --git a/ipld/merkledag/dagutils/diffenum.go b/ipld/merkledag/dagutils/diffenum.go index fdab772c8..f53f89ee8 100644 --- a/ipld/merkledag/dagutils/diffenum.go +++ b/ipld/merkledag/dagutils/diffenum.go @@ -4,10 +4,10 @@ import ( "context" "fmt" - mdag "github.com/ipfs/go-merkledag" - cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" + + mdag "github.com/ipfs/go-merkledag" ) // DiffEnumerate fetches every object in the graph pointed to by 'to' that is diff --git a/ipld/merkledag/dagutils/diffenum_test.go b/ipld/merkledag/dagutils/diffenum_test.go index e8db27817..c4181073f 100644 --- a/ipld/merkledag/dagutils/diffenum_test.go +++ b/ipld/merkledag/dagutils/diffenum_test.go @@ -5,11 +5,11 @@ import ( "fmt" "testing" + "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" + dag "github.com/ipfs/go-merkledag" mdtest "github.com/ipfs/go-merkledag/test" - - cid "github.com/ipfs/go-cid" - ipld "github.com/ipfs/go-ipld-format" ) func buildNode(name string, desc map[string]ndesc, out map[string]ipld.Node) ipld.Node { diff --git a/ipld/merkledag/dagutils/utils.go b/ipld/merkledag/dagutils/utils.go index 3a796a9c5..bc80741cd 100644 --- a/ipld/merkledag/dagutils/utils.go +++ b/ipld/merkledag/dagutils/utils.go @@ -3,16 +3,16 @@ package dagutils import ( "context" "errors" + "strings" bserv "github.com/ipfs/go-blockservice" - dag "github.com/ipfs/go-merkledag" - path "github.com/ipfs/go-path" - ds "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" bstore "github.com/ipfs/go-ipfs-blockstore" offline "github.com/ipfs/go-ipfs-exchange-offline" ipld "github.com/ipfs/go-ipld-format" + + dag "github.com/ipfs/go-merkledag" ) // Editor represents a ProtoNode tree editor and provides methods to @@ -87,7 +87,7 @@ func addLink(ctx context.Context, ds ipld.DAGService, root *dag.ProtoNode, child // InsertNodeAtPath inserts a new node in the tree and replaces the current root with the new one. func (e *Editor) InsertNodeAtPath(ctx context.Context, pth string, toinsert ipld.Node, create func() *dag.ProtoNode) error { - splpath := path.SplitList(pth) + splpath := strings.Split(pth, "/") nd, err := e.insertNodeAtPath(ctx, e.root, splpath, toinsert, create) if err != nil { return err @@ -143,7 +143,7 @@ func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path // RmLink removes the link with the given name and updates the root node of // the editor. func (e *Editor) RmLink(ctx context.Context, pth string) error { - splpath := path.SplitList(pth) + splpath := strings.Split(pth, "/") nd, err := e.rmLink(ctx, e.root, splpath) if err != nil { return err diff --git a/ipld/merkledag/dagutils/utils_test.go b/ipld/merkledag/dagutils/utils_test.go index c9c55286d..4f37dba23 100644 --- a/ipld/merkledag/dagutils/utils_test.go +++ b/ipld/merkledag/dagutils/utils_test.go @@ -2,14 +2,14 @@ package dagutils import ( "context" + "strings" "testing" + "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" + dag "github.com/ipfs/go-merkledag" mdtest "github.com/ipfs/go-merkledag/test" - path "github.com/ipfs/go-path" - - cid "github.com/ipfs/go-cid" - ipld "github.com/ipfs/go-ipld-format" ) func TestAddLink(t *testing.T) { @@ -42,7 +42,7 @@ func TestAddLink(t *testing.T) { } func assertNodeAtPath(t *testing.T, ds ipld.DAGService, root *dag.ProtoNode, pth string, exp cid.Cid) { - parts := path.SplitList(pth) + parts := strings.Split(pth, "/") cur := root for _, e := range parts { nxt, err := cur.GetLinkedProtoNode(context.Background(), ds, e) From 1eb5fce5cf5ff2a4e528906149ca1ea02232bf3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Tue, 19 Nov 2019 17:32:51 +0100 Subject: [PATCH 3139/3817] use dagutils migrated in go-merkledag This commit was moved from ipfs/go-ipfs-pinner@fd593c1d435fcce987d3f1ad39454e0120e46f76 --- pinning/pinner/pin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 7902478cb..655e59add 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -11,10 +11,10 @@ import ( cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" - "github.com/ipfs/go-ipfs/dagutils" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" mdag "github.com/ipfs/go-merkledag" + "github.com/ipfs/go-merkledag/dagutils" ) var log = logging.Logger("pin") From 6eb5c9791b91da125d269d84b201d0acc8e7657a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Wed, 27 Nov 2019 21:40:22 +0100 Subject: [PATCH 3140/3817] feat: make the CoreAPI expose a streaming pin interface This commit was moved from ipfs/interface-go-ipfs-core@f976af7ba62d0209b53aeef72fb102c4387d3f00 --- coreiface/pin.go | 5 ++++- coreiface/tests/block.go | 2 +- coreiface/tests/pin.go | 39 ++++++++++++++++++++++++++++----------- coreiface/tests/unixfs.go | 2 +- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/coreiface/pin.go b/coreiface/pin.go index 7df2956f0..27f9355d3 100644 --- a/coreiface/pin.go +++ b/coreiface/pin.go @@ -14,6 +14,9 @@ type Pin interface { // Type of the pin Type() string + + // if not nil, an error happened. Everything else should be ignored. + Err() error } // PinStatus holds information about pin health @@ -41,7 +44,7 @@ type PinAPI interface { Add(context.Context, path.Path, ...options.PinAddOption) error // Ls returns list of pinned objects on this node - Ls(context.Context, ...options.PinLsOption) ([]Pin, error) + Ls(context.Context, ...options.PinLsOption) (<-chan Pin, error) // Rm removes pin for object specified by the path Rm(context.Context, path.Path, ...options.PinRmOption) error diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 6b648f394..2048dd4c2 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -225,7 +225,7 @@ func (tp *TestSuite) TestBlockPin(t *testing.T) { t.Fatal(err) } - pins, err := api.Pin().Ls(ctx) + pins, err := accPins(api.Pin().Ls(ctx)) if err != nil { return } diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index 7e574fa0d..a968490d3 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -67,7 +67,7 @@ func (tp *TestSuite) TestPinSimple(t *testing.T) { t.Fatal(err) } - list, err := api.Pin().Ls(ctx) + list, err := accPins(api.Pin().Ls(ctx)) if err != nil { t.Fatal(err) } @@ -89,7 +89,7 @@ func (tp *TestSuite) TestPinSimple(t *testing.T) { t.Fatal(err) } - list, err = api.Pin().Ls(ctx) + list, err = accPins(api.Pin().Ls(ctx)) if err != nil { t.Fatal(err) } @@ -141,7 +141,7 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) { t.Fatal(err) } - list, err := api.Pin().Ls(ctx) + list, err := accPins(api.Pin().Ls(ctx)) if err != nil { t.Fatal(err) } @@ -150,7 +150,7 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) { t.Errorf("unexpected pin list len: %d", len(list)) } - list, err = api.Pin().Ls(ctx, opt.Pin.Type.Direct()) + list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Type.Direct())) if err != nil { t.Fatal(err) } @@ -163,7 +163,7 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) { t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.IpfsPath(nd2.Cid()).String()) } - list, err = api.Pin().Ls(ctx, opt.Pin.Type.Recursive()) + list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Type.Recursive())) if err != nil { t.Fatal(err) } @@ -176,7 +176,7 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) { t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.IpldPath(nd3.Cid()).String()) } - list, err = api.Pin().Ls(ctx, opt.Pin.Type.Indirect()) + list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Type.Indirect())) if err != nil { t.Fatal(err) } @@ -390,21 +390,21 @@ func getThreeChainedNodes(t *testing.T, ctx context.Context, api iface.CoreAPI, func assertPinTypes(t *testing.T, ctx context.Context, api iface.CoreAPI, recusive, direct, indirect []cidContainer) { assertPinLsAllConsistency(t, ctx, api) - list, err := api.Pin().Ls(ctx, opt.Pin.Type.Recursive()) + list, err := accPins(api.Pin().Ls(ctx, opt.Pin.Type.Recursive())) if err != nil { t.Fatal(err) } assertPinCids(t, list, recusive...) - list, err = api.Pin().Ls(ctx, opt.Pin.Type.Direct()) + list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Type.Direct())) if err != nil { t.Fatal(err) } assertPinCids(t, list, direct...) - list, err = api.Pin().Ls(ctx, opt.Pin.Type.Indirect()) + list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Type.Indirect())) if err != nil { t.Fatal(err) } @@ -454,7 +454,7 @@ func assertPinCids(t *testing.T, pins []iface.Pin, cids ...cidContainer) { // assertPinLsAllConsistency verifies that listing all pins gives the same result as listing the pin types individually func assertPinLsAllConsistency(t *testing.T, ctx context.Context, api iface.CoreAPI) { t.Helper() - allPins, err := api.Pin().Ls(ctx) + allPins, err := accPins(api.Pin().Ls(ctx)) if err != nil { t.Fatal(err) } @@ -485,7 +485,7 @@ func assertPinLsAllConsistency(t *testing.T, ctx context.Context, api iface.Core } for typeStr, pinProps := range typeMap { - pins, err := api.Pin().Ls(ctx, pinProps.PinLsOption) + pins, err := accPins(api.Pin().Ls(ctx, pinProps.PinLsOption)) if err != nil { t.Fatal(err) } @@ -505,3 +505,20 @@ func assertPinLsAllConsistency(t *testing.T, ctx context.Context, api iface.Core } } } + +func accPins(pins <-chan iface.Pin, err error) ([]iface.Pin, error) { + if err != nil { + return nil, err + } + + var result []iface.Pin + + for pin := range pins { + if pin.Err() != nil { + return nil, pin.Err() + } + result = append(result, pin) + } + + return result, nil +} diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index aac7fa92f..1ed80e873 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -542,7 +542,7 @@ func (tp *TestSuite) TestAddPinned(t *testing.T) { t.Fatal(err) } - pins, err := api.Pin().Ls(ctx) + pins, err := accPins(api.Pin().Ls(ctx)) if err != nil { t.Fatal(err) } From 689e92b560ca2404db9edd1e8b65cd1a5e929881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Fri, 29 Nov 2019 22:21:29 +0100 Subject: [PATCH 3141/3817] fix some tests This commit was moved from ipfs/interface-go-ipfs-core@48dcedecd468c06e3321b4217b51f67180d07eec --- coreiface/tests/block.go | 2 +- coreiface/tests/pin.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 2048dd4c2..51c099bd0 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -227,7 +227,7 @@ func (tp *TestSuite) TestBlockPin(t *testing.T) { pins, err := accPins(api.Pin().Ls(ctx)) if err != nil { - return + t.Skip(err) } if len(pins) != 1 { t.Fatal("expected 1 pin") diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index a968490d3..58e812084 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -160,7 +160,7 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) { } if list[0].Path().String() != path.IpldPath(nd3.Cid()).String() { - t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.IpfsPath(nd2.Cid()).String()) + t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.IpfsPath(nd3.Cid()).String()) } list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Type.Recursive())) @@ -173,7 +173,7 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) { } if list[0].Path().String() != path.IpldPath(nd2.Cid()).String() { - t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.IpldPath(nd3.Cid()).String()) + t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.IpldPath(nd2.Cid()).String()) } list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Type.Indirect())) @@ -186,7 +186,7 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) { } if list[0].Path().Cid().String() != p0.Cid().String() { - t.Error("unexpected path") + t.Errorf("unexpected path, %s != %s", list[0].Path().Cid().String(), p0.Cid().String()) } res, err := api.Pin().Verify(ctx) From b1c5044d1541aa39ed8e6fff8f47c71bf012a7eb Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 2 Dec 2019 15:04:28 -0500 Subject: [PATCH 3142/3817] fix(tests): put valid blocks This commit was moved from ipfs/interface-go-ipfs-core@16127b291793c593b78fb2909bbaf106612ffc30 --- coreiface/tests/block.go | 52 ++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 51c099bd0..09a36b5fe 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -1,18 +1,34 @@ package tests import ( + "bytes" "context" - "github.com/ipfs/interface-go-ipfs-core/path" + "io" "io/ioutil" "strings" "testing" coreiface "github.com/ipfs/interface-go-ipfs-core" opt "github.com/ipfs/interface-go-ipfs-core/options" + "github.com/ipfs/interface-go-ipfs-core/path" mh "github.com/multiformats/go-multihash" ) +var ( + pbCid = "QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN" + cborCid = "bafyreicnga62zhxnmnlt6ymq5hcbsg7gdhqdu6z4ehu3wpjhvqnflfy6nm" + cborKCid = "bafyr2qgsohbwdlk7ajmmbb4lhoytmest4wdbe5xnexfvtxeatuyqqmwv3fgxp3pmhpc27gwey2cct56gloqefoqwcf3yqiqzsaqb7p4jefhcw" +) + +func pbBlock() io.Reader { + return bytes.NewReader([]byte{10, 12, 8, 2, 18, 6, 104, 101, 108, 108, 111, 10, 24, 6}) +} + +func cborBlock() io.Reader { + return bytes.NewReader([]byte{101, 72, 101, 108, 108, 111}) +} + func (tp *TestSuite) TestBlock(t *testing.T) { tp.hasApi(t, func(api coreiface.CoreAPI) error { if api.Block() == nil { @@ -38,12 +54,12 @@ func (tp *TestSuite) TestBlockPut(t *testing.T) { t.Fatal(err) } - res, err := api.Block().Put(ctx, strings.NewReader(`Hello`)) + res, err := api.Block().Put(ctx, pbBlock()) if err != nil { t.Fatal(err) } - if res.Path().Cid().String() != "QmPyo15ynbVrSTVdJL9th7JysHaAbXt9dM9tXk1bMHbRtk" { + if res.Path().Cid().String() != pbCid { t.Errorf("got wrong cid: %s", res.Path().Cid().String()) } } @@ -56,12 +72,12 @@ func (tp *TestSuite) TestBlockPutFormat(t *testing.T) { t.Fatal(err) } - res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("cbor")) + res, err := api.Block().Put(ctx, cborBlock(), opt.Block.Format("cbor")) if err != nil { t.Fatal(err) } - if res.Path().Cid().String() != "bafyreiayl6g3gitr7ys7kyng7sjywlrgimdoymco3jiyab6rozecmoazne" { + if res.Path().Cid().String() != cborCid { t.Errorf("got wrong cid: %s", res.Path().Cid().String()) } } @@ -74,12 +90,17 @@ func (tp *TestSuite) TestBlockPutHash(t *testing.T) { t.Fatal(err) } - res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Hash(mh.KECCAK_512, -1)) + res, err := api.Block().Put( + ctx, + cborBlock(), + opt.Block.Hash(mh.KECCAK_512, -1), + opt.Block.Format("cbor"), + ) if err != nil { t.Fatal(err) } - if res.Path().Cid().String() != "bafyb2qgdh7w6dcq24u65xbtdoehyavegnpvxcqce7ttvs6ielgmwdfxrahmu37d33atik57x5y6s7d7qz32aasuwgirh3ocn6ywswqdifvu6e" { + if res.Path().Cid().String() != cborKCid { t.Errorf("got wrong cid: %s", res.Path().Cid().String()) } } @@ -92,7 +113,7 @@ func (tp *TestSuite) TestBlockGet(t *testing.T) { t.Fatal(err) } - res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Hash(mh.KECCAK_512, -1)) + res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("raw")) if err != nil { t.Fatal(err) } @@ -130,7 +151,7 @@ func (tp *TestSuite) TestBlockRm(t *testing.T) { t.Fatal(err) } - res, err := api.Block().Put(ctx, strings.NewReader(`Hello`)) + res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("raw")) if err != nil { t.Fatal(err) } @@ -184,7 +205,7 @@ func (tp *TestSuite) TestBlockStat(t *testing.T) { t.Fatal(err) } - res, err := api.Block().Put(ctx, strings.NewReader(`Hello`)) + res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("raw")) if err != nil { t.Fatal(err) } @@ -211,7 +232,7 @@ func (tp *TestSuite) TestBlockPin(t *testing.T) { t.Fatal(err) } - _, err = api.Block().Put(ctx, strings.NewReader(`Hello`)) + _, err = api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("raw")) if err != nil { t.Fatal(err) } @@ -220,14 +241,19 @@ func (tp *TestSuite) TestBlockPin(t *testing.T) { t.Fatal("expected 0 pins") } - res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Pin(true)) + res, err := api.Block().Put( + ctx, + strings.NewReader(`Hello`), + opt.Block.Pin(true), + opt.Block.Format("raw"), + ) if err != nil { t.Fatal(err) } pins, err := accPins(api.Pin().Ls(ctx)) if err != nil { - t.Skip(err) + t.Fatal(err) } if len(pins) != 1 { t.Fatal("expected 1 pin") From b14ab41d5fff90465ef7c5a2217800f557a22e22 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 2 Dec 2019 15:39:35 -0500 Subject: [PATCH 3143/3817] chore(pb): regenerate protobufs and add a makefile This commit was moved from ipfs/go-ipfs-pinner@05f55f1b4385e71df19b7d545bd685ed27af58bd --- pinning/pinner/internal/pb/Makefile | 11 +++ pinning/pinner/internal/pb/header.pb.go | 120 ++++++++++-------------- 2 files changed, 58 insertions(+), 73 deletions(-) create mode 100644 pinning/pinner/internal/pb/Makefile diff --git a/pinning/pinner/internal/pb/Makefile b/pinning/pinner/internal/pb/Makefile new file mode 100644 index 000000000..df34e54b0 --- /dev/null +++ b/pinning/pinner/internal/pb/Makefile @@ -0,0 +1,11 @@ +PB = $(wildcard *.proto) +GO = $(PB:.proto=.pb.go) + +all: $(GO) + +%.pb.go: %.proto + protoc --proto_path=$(GOPATH)/src:. --gogofaster_out=. $< + +clean: + rm -f *.pb.go + rm -f *.go diff --git a/pinning/pinner/internal/pb/header.pb.go b/pinning/pinner/internal/pb/header.pb.go index 71196b263..b8b3b0e7d 100644 --- a/pinning/pinner/internal/pb/header.pb.go +++ b/pinning/pinner/internal/pb/header.pb.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: pin/internal/pb/header.proto +// source: header.proto package pb @@ -9,6 +9,7 @@ import ( proto "github.com/gogo/protobuf/proto" io "io" math "math" + math_bits "math/bits" ) // Reference imports to suppress errors if they are not otherwise used. @@ -20,7 +21,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Set struct { // 1 for now, library will refuse to handle entries with an unrecognized version. @@ -35,7 +36,7 @@ func (m *Set) Reset() { *m = Set{} } func (m *Set) String() string { return proto.CompactTextString(m) } func (*Set) ProtoMessage() {} func (*Set) Descriptor() ([]byte, []int) { - return fileDescriptor_cda303a5a3ed87e7, []int{0} + return fileDescriptor_6398613e36d6c2ce, []int{0} } func (m *Set) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -45,7 +46,7 @@ func (m *Set) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_Set.Marshal(b, m, deterministic) } else { b = b[:cap(b)] - n, err := m.MarshalTo(b) + n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } @@ -89,12 +90,11 @@ func init() { proto.RegisterType((*Set)(nil), "ipfs.pin.Set") } -func init() { proto.RegisterFile("pin/internal/pb/header.proto", fileDescriptor_cda303a5a3ed87e7) } +func init() { proto.RegisterFile("header.proto", fileDescriptor_6398613e36d6c2ce) } -var fileDescriptor_cda303a5a3ed87e7 = []byte{ - // 162 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x29, 0xc8, 0xcc, 0xd3, - 0xcf, 0xcc, 0x2b, 0x49, 0x2d, 0xca, 0x4b, 0xcc, 0xd1, 0x2f, 0x48, 0xd2, 0xcf, 0x48, 0x4d, 0x4c, +var fileDescriptor_6398613e36d6c2ce = []byte{ + // 146 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xc9, 0x48, 0x4d, 0x4c, 0x49, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0xc8, 0x2c, 0x48, 0x2b, 0xd6, 0x2b, 0xc8, 0xcc, 0x53, 0x8a, 0xe5, 0x62, 0x0e, 0x4e, 0x2d, 0x11, 0x92, 0xe3, 0x62, 0x2f, 0x4b, 0x2d, 0x2a, 0xce, 0xcc, 0xcf, 0x93, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x75, 0x62, 0x39, 0x71, 0x4f, 0x9e, @@ -102,14 +102,14 @@ var fileDescriptor_cda303a5a3ed87e7 = []byte{ 0x24, 0x0d, 0x15, 0x13, 0x92, 0xe0, 0x62, 0x29, 0x4e, 0x4d, 0x4d, 0x91, 0x60, 0x56, 0x60, 0xd4, 0x60, 0x87, 0xca, 0x81, 0x45, 0x9c, 0x64, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, - 0x21, 0x8a, 0xa9, 0x20, 0x09, 0x10, 0x00, 0x00, 0xff, 0xff, 0x20, 0x85, 0x2f, 0x24, 0xa5, 0x00, + 0x21, 0x8a, 0xa9, 0x20, 0x09, 0x10, 0x00, 0x00, 0xff, 0xff, 0x3c, 0x49, 0x19, 0x51, 0x95, 0x00, 0x00, 0x00, } func (m *Set) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -117,31 +117,38 @@ func (m *Set) Marshal() (dAtA []byte, err error) { } func (m *Set) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Set) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0x8 - i++ - i = encodeVarintHeader(dAtA, i, uint64(m.Version)) - dAtA[i] = 0x10 - i++ - i = encodeVarintHeader(dAtA, i, uint64(m.Fanout)) - dAtA[i] = 0x1d - i++ + i -= 4 encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(m.Seed)) - i += 4 - return i, nil + i-- + dAtA[i] = 0x1d + i = encodeVarintHeader(dAtA, i, uint64(m.Fanout)) + i-- + dAtA[i] = 0x10 + i = encodeVarintHeader(dAtA, i, uint64(m.Version)) + i-- + dAtA[i] = 0x8 + return len(dAtA) - i, nil } func encodeVarintHeader(dAtA []byte, offset int, v uint64) int { + offset -= sovHeader(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Set) Size() (n int) { if m == nil { @@ -156,14 +163,7 @@ func (m *Set) Size() (n int) { } func sovHeader(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozHeader(x uint64) (n int) { return sovHeader(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -272,6 +272,7 @@ func (m *Set) Unmarshal(dAtA []byte) error { func skipHeader(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -303,10 +304,8 @@ func skipHeader(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -327,55 +326,30 @@ func skipHeader(dAtA []byte) (n int, err error) { return 0, ErrInvalidLengthHeader } iNdEx += length - if iNdEx < 0 { - return 0, ErrInvalidLengthHeader - } - return iNdEx, nil case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowHeader - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipHeader(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - if iNdEx < 0 { - return 0, ErrInvalidLengthHeader - } - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupHeader + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthHeader + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthHeader = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowHeader = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthHeader = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowHeader = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupHeader = fmt.Errorf("proto: unexpected end of group") ) From a9cd86b7d8e9eac5b4e8bc5715f7a5fcc63d3301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Wed, 20 Nov 2019 16:38:09 +0100 Subject: [PATCH 3144/3817] extract the pinner to go-ipfs-pinner and dagutils into go-merkledag This commit was moved from ipfs/go-namesys@4801adee17192fe145c8d94bb154c6f69a1c83f2 --- namesys/publisher.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 17c4e1b3f..f5e335cd3 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -6,11 +6,10 @@ import ( "sync" "time" - pin "github.com/ipfs/go-ipfs/pin" - proto "github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-datastore" dsquery "github.com/ipfs/go-datastore/query" + pin "github.com/ipfs/go-ipfs-pinner" ipns "github.com/ipfs/go-ipns" pb "github.com/ipfs/go-ipns/pb" path "github.com/ipfs/go-path" From bab395bfb607b3db9e755e0ba0be10dfabe9e118 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 2 Dec 2019 20:59:21 -0500 Subject: [PATCH 3145/3817] chore(dep): update libp2p & protobuf and regenerate protobufs This commit was moved from ipfs/go-ipns@e8c47138fb4e02422e243e79e68c92579c44e157 --- ipns/examples/embed.go | 2 +- ipns/examples/examples_test.go | 6 +- ipns/examples/key.go | 2 +- ipns/ipns.go | 4 +- ipns/ipns_test.go | 6 +- ipns/pb/ipns.pb.go | 160 +++++++++++++++------------------ ipns/record.go | 6 +- ipns/select_test.go | 4 +- ipns/validate_test.go | 8 +- 9 files changed, 88 insertions(+), 110 deletions(-) diff --git a/ipns/examples/embed.go b/ipns/examples/embed.go index ffc635eaf..78ca4595a 100644 --- a/ipns/examples/embed.go +++ b/ipns/examples/embed.go @@ -6,7 +6,7 @@ import ( pb "github.com/ipfs/go-ipns/pb" ipns "github.com/ipfs/go-ipns" - crypto "github.com/libp2p/go-libp2p-crypto" + crypto "github.com/libp2p/go-libp2p-core/crypto" ) // CreateEntryWithEmbed shows how you can create an IPNS entry diff --git a/ipns/examples/examples_test.go b/ipns/examples/examples_test.go index af765f9f9..caa21e34c 100644 --- a/ipns/examples/examples_test.go +++ b/ipns/examples/examples_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/ipfs/go-ipns/examples" - crypto "github.com/libp2p/go-libp2p-crypto" + crypto "github.com/libp2p/go-libp2p-core/crypto" ) var testPath = "/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5" @@ -43,9 +43,7 @@ func TestEmbeddedEntryCreation(t *testing.T) { } func generateRSAKey() (crypto.PrivKey, error) { - // DO NOT USE 1024 BITS IN PRODUCTION - // THIS IS ONLY FOR TESTING PURPOSES - k, err := examples.GenerateRSAKeyPair(1024) + k, err := examples.GenerateRSAKeyPair(2048) if err != nil { return nil, err } diff --git a/ipns/examples/key.go b/ipns/examples/key.go index 408e3da80..6354521ee 100644 --- a/ipns/examples/key.go +++ b/ipns/examples/key.go @@ -1,7 +1,7 @@ package examples import ( - crypto "github.com/libp2p/go-libp2p-crypto" + crypto "github.com/libp2p/go-libp2p-core/crypto" ) // GenerateRSAKeyPair is used to generate an RSA key pair diff --git a/ipns/ipns.go b/ipns/ipns.go index f145333b1..32a6104a7 100644 --- a/ipns/ipns.go +++ b/ipns/ipns.go @@ -8,8 +8,8 @@ import ( pb "github.com/ipfs/go-ipns/pb" u "github.com/ipfs/go-ipfs-util" - ic "github.com/libp2p/go-libp2p-crypto" - peer "github.com/libp2p/go-libp2p-peer" + ic "github.com/libp2p/go-libp2p-core/crypto" + peer "github.com/libp2p/go-libp2p-core/peer" ) // Create creates a new IPNS entry and signs it with the given private key. diff --git a/ipns/ipns_test.go b/ipns/ipns_test.go index 0f2e30d79..84a4b1d80 100644 --- a/ipns/ipns_test.go +++ b/ipns/ipns_test.go @@ -6,14 +6,14 @@ import ( "time" u "github.com/ipfs/go-ipfs-util" - ci "github.com/libp2p/go-libp2p-crypto" - peer "github.com/libp2p/go-libp2p-peer" + ci "github.com/libp2p/go-libp2p-core/crypto" + peer "github.com/libp2p/go-libp2p-core/peer" ) func TestEmbedPublicKey(t *testing.T) { sr := u.NewTimeSeededRand() - priv, pub, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, sr) + priv, pub, err := ci.GenerateKeyPairWithReader(ci.RSA, 2048, sr) if err != nil { t.Fatal(err) } diff --git a/ipns/pb/ipns.pb.go b/ipns/pb/ipns.pb.go index b38ce4ea9..6354831d0 100644 --- a/ipns/pb/ipns.pb.go +++ b/ipns/pb/ipns.pb.go @@ -9,6 +9,7 @@ import ( proto "github.com/gogo/protobuf/proto" io "io" math "math" + math_bits "math/bits" ) // Reference imports to suppress errors if they are not otherwise used. @@ -20,7 +21,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type IpnsEntry_ValidityType int32 @@ -91,7 +92,7 @@ func (m *IpnsEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_IpnsEntry.Marshal(b, m, deterministic) } else { b = b[:cap(b)] - n, err := m.MarshalTo(b) + n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } @@ -187,7 +188,7 @@ var fileDescriptor_4d5b16fb32bfe8ea = []byte{ func (m *IpnsEntry) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -195,67 +196,79 @@ func (m *IpnsEntry) Marshal() (dAtA []byte, err error) { } func (m *IpnsEntry) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IpnsEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Value == nil { - return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("value") - } else { - dAtA[i] = 0xa - i++ - i = encodeVarintIpns(dAtA, i, uint64(len(m.Value))) - i += copy(dAtA[i:], m.Value) - } - if m.Signature == nil { - return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("signature") - } else { - dAtA[i] = 0x12 - i++ - i = encodeVarintIpns(dAtA, i, uint64(len(m.Signature))) - i += copy(dAtA[i:], m.Signature) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if m.ValidityType != nil { - dAtA[i] = 0x18 - i++ - i = encodeVarintIpns(dAtA, i, uint64(*m.ValidityType)) + if m.PubKey != nil { + i -= len(m.PubKey) + copy(dAtA[i:], m.PubKey) + i = encodeVarintIpns(dAtA, i, uint64(len(m.PubKey))) + i-- + dAtA[i] = 0x3a } - if m.Validity != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintIpns(dAtA, i, uint64(len(m.Validity))) - i += copy(dAtA[i:], m.Validity) + if m.Ttl != nil { + i = encodeVarintIpns(dAtA, i, uint64(*m.Ttl)) + i-- + dAtA[i] = 0x30 } if m.Sequence != nil { - dAtA[i] = 0x28 - i++ i = encodeVarintIpns(dAtA, i, uint64(*m.Sequence)) + i-- + dAtA[i] = 0x28 } - if m.Ttl != nil { - dAtA[i] = 0x30 - i++ - i = encodeVarintIpns(dAtA, i, uint64(*m.Ttl)) + if m.Validity != nil { + i -= len(m.Validity) + copy(dAtA[i:], m.Validity) + i = encodeVarintIpns(dAtA, i, uint64(len(m.Validity))) + i-- + dAtA[i] = 0x22 } - if m.PubKey != nil { - dAtA[i] = 0x3a - i++ - i = encodeVarintIpns(dAtA, i, uint64(len(m.PubKey))) - i += copy(dAtA[i:], m.PubKey) + if m.ValidityType != nil { + i = encodeVarintIpns(dAtA, i, uint64(*m.ValidityType)) + i-- + dAtA[i] = 0x18 } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + if m.Signature == nil { + return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("signature") + } else { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintIpns(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x12 } - return i, nil + if m.Value == nil { + return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("value") + } else { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintIpns(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func encodeVarintIpns(dAtA []byte, offset int, v uint64) int { + offset -= sovIpns(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *IpnsEntry) Size() (n int) { if m == nil { @@ -295,14 +308,7 @@ func (m *IpnsEntry) Size() (n int) { } func sovIpns(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozIpns(x uint64) (n int) { return sovIpns(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -569,6 +575,7 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { func skipIpns(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -600,10 +607,8 @@ func skipIpns(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -627,52 +632,27 @@ func skipIpns(dAtA []byte) (n int, err error) { if iNdEx < 0 { return 0, ErrInvalidLengthIpns } - return iNdEx, nil case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowIpns - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipIpns(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - if iNdEx < 0 { - return 0, ErrInvalidLengthIpns - } - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupIpns + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthIpns = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowIpns = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthIpns = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowIpns = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupIpns = fmt.Errorf("proto: unexpected end of group") ) diff --git a/ipns/record.go b/ipns/record.go index eb60ce6f8..cd2ec3cdd 100644 --- a/ipns/record.go +++ b/ipns/record.go @@ -8,9 +8,9 @@ import ( proto "github.com/gogo/protobuf/proto" logging "github.com/ipfs/go-log" - ic "github.com/libp2p/go-libp2p-crypto" - peer "github.com/libp2p/go-libp2p-peer" - pstore "github.com/libp2p/go-libp2p-peerstore" + ic "github.com/libp2p/go-libp2p-core/crypto" + peer "github.com/libp2p/go-libp2p-core/peer" + pstore "github.com/libp2p/go-libp2p-core/peerstore" record "github.com/libp2p/go-libp2p-record" ) diff --git a/ipns/select_test.go b/ipns/select_test.go index a9a34a91d..35fc3f618 100644 --- a/ipns/select_test.go +++ b/ipns/select_test.go @@ -10,7 +10,7 @@ import ( proto "github.com/gogo/protobuf/proto" u "github.com/ipfs/go-ipfs-util" - ci "github.com/libp2p/go-libp2p-crypto" + ci "github.com/libp2p/go-libp2p-core/crypto" ) func shuffle(a []*pb.IpnsEntry) { @@ -51,7 +51,7 @@ func TestOrdering(t *testing.T) { // generate a key for signing the records r := u.NewSeededRand(15) // generate deterministic keypair - priv, _, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, r) + priv, _, err := ci.GenerateKeyPairWithReader(ci.RSA, 2048, r) if err != nil { t.Fatal(err) } diff --git a/ipns/validate_test.go b/ipns/validate_test.go index 1e10249b6..741d20bc1 100644 --- a/ipns/validate_test.go +++ b/ipns/validate_test.go @@ -11,9 +11,9 @@ import ( proto "github.com/gogo/protobuf/proto" u "github.com/ipfs/go-ipfs-util" - ci "github.com/libp2p/go-libp2p-crypto" - peer "github.com/libp2p/go-libp2p-peer" - pstore "github.com/libp2p/go-libp2p-peerstore" + ci "github.com/libp2p/go-libp2p-core/crypto" + peer "github.com/libp2p/go-libp2p-core/peer" + pstore "github.com/libp2p/go-libp2p-core/peerstore" pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem" ) @@ -162,7 +162,7 @@ func TestPeerIDPubKeyValidate(t *testing.T) { func genKeys(t *testing.T) (ci.PrivKey, peer.ID, string) { sr := u.NewTimeSeededRand() - priv, _, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, sr) + priv, _, err := ci.GenerateKeyPairWithReader(ci.RSA, 2048, sr) if err != nil { t.Fatal(err) } From e83e2db7d01d98f5797d09a544b1c6b579e06114 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Tue, 3 Dec 2019 17:19:38 -0500 Subject: [PATCH 3146/3817] update datastore interface to support asynchronous writes to datastores. add datastore Sync during pinner.Flush() This commit was moved from ipfs/go-ipfs-pinner@f462ad6d552c55bbfc197110c81b65e469c06ca5 --- pinning/pinner/pin.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 655e59add..fa17a6b0c 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -579,6 +579,9 @@ func (p *pinner) Flush(ctx context.Context) error { if err := p.dstore.Put(pinDatastoreKey, k.Bytes()); err != nil { return fmt.Errorf("cannot store pin state: %v", err) } + if err := p.dstore.Sync(pinDatastoreKey); err != nil { + return fmt.Errorf("cannot sync pin state: %v", err) + } p.internalPin = internalset return nil } From 1d50d25c7b852f12acab0b81a47a786c9425cc2e Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Thu, 5 Dec 2019 12:15:45 -0500 Subject: [PATCH 3147/3817] add support for asynchronous datastores This commit was moved from ipfs/go-ipfs-pinner@5901eab20ba3c1345107fb9cc15682bf14cf7245 --- pinning/pinner/pin.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index fa17a6b0c..7a3cabdf0 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -192,6 +192,11 @@ type pinner struct { dstore ds.Datastore } +type syncDAGService interface { + ipld.DAGService + Sync() error +} + // NewPinner creates a new pinner using the given datastore as a backend func NewPinner(dstore ds.Datastore, serv, internal ipld.DAGService) Pinner { @@ -576,6 +581,19 @@ func (p *pinner) Flush(ctx context.Context) error { k := root.Cid() internalset.Add(k) + + if syncDServ, ok := p.dserv.(syncDAGService); ok { + if err := syncDServ.Sync(); err != nil { + return fmt.Errorf("cannot sync pinned data: %v", err) + } + } + + if syncInternal, ok := p.internal.(syncDAGService); ok { + if err := syncInternal.Sync(); err != nil { + return fmt.Errorf("cannot sync pinning data: %v", err) + } + } + if err := p.dstore.Put(pinDatastoreKey, k.Bytes()); err != nil { return fmt.Errorf("cannot store pin state: %v", err) } From 65575fb3099dd729b394f512d8884b2e7f9c7cd8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 6 Dec 2019 10:36:38 -0500 Subject: [PATCH 3148/3817] chore(gx): remove gx This commit was moved from ipfs/go-ipfs-exchange-offline@30194c5ef96ac12ff0266cbc1d60c7182ed0e44a --- exchange/offline/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/exchange/offline/README.md b/exchange/offline/README.md index 707099e9a..cd537b302 100644 --- a/exchange/offline/README.md +++ b/exchange/offline/README.md @@ -25,8 +25,6 @@ This is an offline exchange implementation which will not perform any request. > go get github.com/ipfs/go-ipfs-exchange-offline ``` -It uses [Gx](https://github.com/whyrusleeping/gx) to manage dependencies. You can use `make all` to build it with the `gx` dependencies. - ## Usage ``` From f250b40566d6798509485883b9c407102b5d11d7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 6 Dec 2019 11:37:53 -0500 Subject: [PATCH 3149/3817] chore(dep): update gogo 1. And rebuild generated protobuf. 2. Making sure to keep the link/data ordering (we have a test for this and it still passes) This commit was moved from ipfs/go-merkledag@1ef37d25364d71d0f5748fba9f949df73d78532b --- ipld/merkledag/pb/merkledag.pb.go | 185 ++++++++++++++---------------- ipld/merkledag/pb/merkledag.proto | 2 + 2 files changed, 90 insertions(+), 97 deletions(-) diff --git a/ipld/merkledag/pb/merkledag.pb.go b/ipld/merkledag/pb/merkledag.pb.go index c6fc43608..c5d2c7caf 100644 --- a/ipld/merkledag/pb/merkledag.pb.go +++ b/ipld/merkledag/pb/merkledag.pb.go @@ -6,13 +6,13 @@ package merkledag_pb import ( bytes "bytes" fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" io "io" math "math" + math_bits "math/bits" reflect "reflect" strings "strings" - - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" ) // DoNotUpgradeFileEverItWillChangeYourHashes warns users about not breaking @@ -33,7 +33,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // An IPFS MerkleDAG Link type PBLink struct { @@ -61,7 +61,7 @@ func (m *PBLink) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_PBLink.Marshal(b, m, deterministic) } else { b = b[:cap(b)] - n, err := m.MarshalTo(b) + n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } @@ -125,7 +125,7 @@ func (m *PBNode) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_PBNode.Marshal(b, m, deterministic) } else { b = b[:cap(b)] - n, err := m.MarshalTo(b) + n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } @@ -406,7 +406,7 @@ func valueToGoStringMerkledag(v interface{}, typ string) string { func (m *PBLink) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -414,37 +414,45 @@ func (m *PBLink) Marshal() (dAtA []byte, err error) { } func (m *PBLink) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PBLink) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Hash != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintMerkledag(dAtA, i, uint64(len(m.Hash))) - i += copy(dAtA[i:], m.Hash) - } - if m.Name != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintMerkledag(dAtA, i, uint64(len(*m.Name))) - i += copy(dAtA[i:], *m.Name) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Tsize != nil { - dAtA[i] = 0x18 - i++ i = encodeVarintMerkledag(dAtA, i, uint64(*m.Tsize)) + i-- + dAtA[i] = 0x18 } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + if m.Name != nil { + i -= len(*m.Name) + copy(dAtA[i:], *m.Name) + i = encodeVarintMerkledag(dAtA, i, uint64(len(*m.Name))) + i-- + dAtA[i] = 0x12 + } + if m.Hash != nil { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintMerkledag(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *PBNode) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -452,57 +460,68 @@ func (m *PBNode) Marshal() (dAtA []byte, err error) { } func (m *PBNode) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PBNode) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Links) > 0 { - for _, msg := range m.Links { - dAtA[i] = 0x12 - i++ - i = encodeVarintMerkledag(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Data != nil { - dAtA[i] = 0xa - i++ + i -= len(m.Data) + copy(dAtA[i:], m.Data) i = encodeVarintMerkledag(dAtA, i, uint64(len(m.Data))) - i += copy(dAtA[i:], m.Data) + i-- + dAtA[i] = 0xa } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + if len(m.Links) > 0 { + for iNdEx := len(m.Links) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Links[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMerkledag(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } } - return i, nil + return len(dAtA) - i, nil } func encodeVarintMerkledag(dAtA []byte, offset int, v uint64) int { + offset -= sovMerkledag(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func NewPopulatedPBLink(r randyMerkledag, easy bool) *PBLink { this := &PBLink{} - if r.Intn(10) != 0 { + if r.Intn(5) != 0 { v1 := r.Intn(100) this.Hash = make([]byte, v1) for i := 0; i < v1; i++ { this.Hash[i] = byte(r.Intn(256)) } } - if r.Intn(10) != 0 { + if r.Intn(5) != 0 { v2 := string(randStringMerkledag(r)) this.Name = &v2 } - if r.Intn(10) != 0 { + if r.Intn(5) != 0 { v3 := uint64(uint64(r.Uint32())) this.Tsize = &v3 } @@ -514,14 +533,14 @@ func NewPopulatedPBLink(r randyMerkledag, easy bool) *PBLink { func NewPopulatedPBNode(r randyMerkledag, easy bool) *PBNode { this := &PBNode{} - if r.Intn(10) != 0 { + if r.Intn(5) != 0 { v4 := r.Intn(100) this.Data = make([]byte, v4) for i := 0; i < v4; i++ { this.Data[i] = byte(r.Intn(256)) } } - if r.Intn(10) != 0 { + if r.Intn(5) != 0 { v5 := r.Intn(5) this.Links = make([]*PBLink, v5) for i := 0; i < v5; i++ { @@ -652,14 +671,7 @@ func (m *PBNode) Size() (n int) { } func sovMerkledag(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozMerkledag(x uint64) (n int) { return sovMerkledag(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -681,9 +693,14 @@ func (this *PBNode) String() string { if this == nil { return "nil" } + repeatedStringForLinks := "[]*PBLink{" + for _, f := range this.Links { + repeatedStringForLinks += strings.Replace(f.String(), "PBLink", "PBLink", 1) + "," + } + repeatedStringForLinks += "}" s := strings.Join([]string{`&PBNode{`, `Data:` + valueToStringMerkledag(this.Data) + `,`, - `Links:` + strings.Replace(fmt.Sprintf("%v", this.Links), "PBLink", "PBLink", 1) + `,`, + `Links:` + repeatedStringForLinks + `,`, `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") @@ -963,6 +980,7 @@ func (m *PBNode) Unmarshal(dAtA []byte) error { func skipMerkledag(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -994,10 +1012,8 @@ func skipMerkledag(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -1021,52 +1037,27 @@ func skipMerkledag(dAtA []byte) (n int, err error) { if iNdEx < 0 { return 0, ErrInvalidLengthMerkledag } - return iNdEx, nil case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowMerkledag - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipMerkledag(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - if iNdEx < 0 { - return 0, ErrInvalidLengthMerkledag - } - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMerkledag + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthMerkledag = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowMerkledag = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthMerkledag = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMerkledag = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMerkledag = fmt.Errorf("proto: unexpected end of group") ) diff --git a/ipld/merkledag/pb/merkledag.proto b/ipld/merkledag/pb/merkledag.proto index 012195901..ec540e681 100644 --- a/ipld/merkledag/pb/merkledag.proto +++ b/ipld/merkledag/pb/merkledag.proto @@ -1,3 +1,5 @@ +syntax = "proto2"; + package merkledag.pb; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; From 4564bd2d00ce963e11ebb89f7ea5fa195de3f3ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Fri, 6 Dec 2019 18:46:35 +0100 Subject: [PATCH 3150/3817] reprovide: pass the context to the back-off retry This commit was moved from ipfs/go-ipfs-provider@03c62646479e4b133276573fc31a39a5b491f122 --- provider/simple/reprovide.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index e5afe80d2..59b49d807 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -110,9 +110,7 @@ func (rp *Reprovider) Reprovide() error { return err } - // TODO: this backoff library does not respect our context, we should - // eventually work contexts into it. low priority. - err := backoff.Retry(op, backoff.NewExponentialBackOff()) + err := backoff.Retry(op, backoff.WithContext(backoff.NewExponentialBackOff(), rp.ctx)) if err != nil { logR.Debugf("Providing failed after number of retries: %s", err) return err From 674921ba7eaed8dfc75545037a61d235b9efb65d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 9 Dec 2019 13:56:40 +0100 Subject: [PATCH 3151/3817] fix(arc): return the correct size when only "has" is cached This commit was moved from ipfs/go-ipfs-blockstore@5e786b5d8a17fefe58a6309658fa19efa6d6c783 --- blockstore/arc_cache.go | 9 +++++++-- blockstore/arc_cache_test.go | 21 ++++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index b2ba82105..8e88fa81a 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -86,10 +86,15 @@ func (b *arccache) Has(k cid.Cid) (bool, error) { func (b *arccache) GetSize(k cid.Cid) (int, error) { if has, blockSize, ok := b.hasCached(k); ok { - if has { + if !has { + // don't have it, return + return -1, ErrNotFound + } + if blockSize >= 0 { + // have it and we know the size return blockSize, nil } - return -1, ErrNotFound + // we have it but don't know the size, ask the datastore. } blockSize, err := b.blockstore.GetSize(k) if err == ErrNotFound { diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index b72c84853..dcd9c6e30 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -123,7 +123,7 @@ func TestGetFillsCache(t *testing.T) { t.Fatal("has returned invalid result") } if blockSize, err := arc.GetSize(exampleBlock.Cid()); blockSize == -1 || err != nil { - t.Fatal("getsize returned invalid result") + t.Fatal("getsize returned invalid result", blockSize, err) } } @@ -185,6 +185,25 @@ func TestGetSizeAfterSucessfulGetIsCached(t *testing.T) { arc.GetSize(exampleBlock.Cid()) } +func TestGetSizeAfterSucessfulHas(t *testing.T) { + arc, bs, _ := createStores(t) + + bs.Put(exampleBlock) + has, err := arc.Has(exampleBlock.Cid()) + if err != nil { + t.Fatal(err) + } + if !has { + t.Fatal("expected to have block") + } + + if size, err := arc.GetSize(exampleBlock.Cid()); err != nil { + t.Fatal(err) + } else if size != len(exampleBlock.RawData()) { + t.Fatalf("expected size %d, got %d", len(exampleBlock.RawData()), size) + } +} + func TestGetSizeMissingZeroSizeBlock(t *testing.T) { arc, bs, cd := createStores(t) emptyBlock := blocks.NewBlock([]byte{}) From 7540727e98fe10cd67b65d935fba5dd78eed0895 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sat, 14 Dec 2019 19:48:31 +0100 Subject: [PATCH 3152/3817] fix: move away from deprecated peer ID functions This commit was moved from ipfs/go-namesys@d787d3ba83820c2b25b9e105d5f34f5409ff4730 --- namesys/namesys.go | 2 +- namesys/routing.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 7ae93e3e2..079eecccc 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -189,6 +189,6 @@ func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path. if ttEol := time.Until(eol); ttEol < ttl { ttl = ttEol } - ns.cacheSet(peer.IDB58Encode(id), value, ttl) + ns.cacheSet(peer.Encode(id), value, ttl) return nil } diff --git a/namesys/routing.go b/namesys/routing.go index 94c12a726..c2d0d0252 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -59,7 +59,7 @@ func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, option } name = strings.TrimPrefix(name, "/ipns/") - pid, err := peer.IDB58Decode(name) + pid, err := peer.Decode(name) if err != nil { log.Debugf("RoutingResolver: could not convert public key hash %s to peer ID: %s\n", name, err) out <- onceResult{err: err} From 13b86854e783f610220e552fe7a38de90e6aa3d1 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Thu, 5 Dec 2019 13:16:33 -0500 Subject: [PATCH 3153/3817] support async datastores This commit was moved from ipfs/go-namesys@662bbed655965f7c26ebcc663318791e8fbbcde1 --- namesys/publisher.go | 6 +++++- namesys/publisher_test.go | 43 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index f5e335cd3..1fa0c96c9 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -179,7 +179,11 @@ func (p *IpnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value pa } // Put the new record. - if err := p.ds.Put(IpnsDsKey(id), data); err != nil { + key := IpnsDsKey(id) + if err := p.ds.Put(key, data); err != nil { + return nil, err + } + if err := p.ds.Sync(key); err != nil { return nil, err } return entry, nil diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 0b7b2c939..625103383 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -3,6 +3,7 @@ package namesys import ( "context" "crypto/rand" + "github.com/ipfs/go-path" "testing" "time" @@ -110,3 +111,45 @@ func TestRSAPublisher(t *testing.T) { func TestEd22519Publisher(t *testing.T) { testNamekeyPublisher(t, ci.Ed25519, ds.ErrNotFound, false) } + +func TestAsyncDS(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + rt := mockrouting.NewServer().Client(testutil.RandIdentityOrFatal(t)) + ds := &checkSyncDS{ + Datastore: ds.NewMapDatastore(), + syncKeys: make(map[ds.Key]struct{}), + } + publisher := NewIpnsPublisher(rt, ds) + + ipnsFakeID := testutil.RandIdentityOrFatal(t) + ipnsVal, err := path.ParsePath("/ipns/foo.bar") + if err != nil { + t.Fatal(err) + } + + if err := publisher.Publish(ctx, ipnsFakeID.PrivateKey(), ipnsVal); err != nil { + t.Fatal(err) + } + + ipnsKey := IpnsDsKey(ipnsFakeID.ID()) + + for k := range ds.syncKeys { + if k.IsAncestorOf(ipnsKey) || k.Equal(ipnsKey) { + return + } + } + + t.Fatal("ipns key not synced") +} + +type checkSyncDS struct { + ds.Datastore + syncKeys map[ds.Key]struct{} +} + +func (d *checkSyncDS) Sync(prefix ds.Key) error { + d.syncKeys[prefix] = struct{}{} + return d.Datastore.Sync(prefix) +} From 974687971984fc2197734088eef7ea55e990596e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 3 Jan 2020 18:53:00 -0800 Subject: [PATCH 3154/3817] fix(dagreader): remove a buggy workaround for a gateway issue We had a hack that "pretended" seeking worked when it didn't as go's HTTP library uses seeks to determine the file size. However, 1. I'm changing the gateway to actually rely on seeking working as specified. 2. We don't even need this hack. The gateway performs two types of seeks (unless a range query is passed): 1. It seeks to the beginning. We can always shortcut this. 2. It seeks to the end. The gateway now has a special "lazy" seeker to avoid seeking until we actually try to _read_. Therefore, we don't need a hack for that either. This commit was moved from ipfs/go-unixfs@47e41818c092d6478a2ccdb891eff4f3b3544282 --- unixfs/io/dagreader.go | 20 +++++------- unixfs/io/dagreader_test.go | 65 +++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 12 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 75fb5c714..8dcb5c911 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -16,6 +16,7 @@ var ( ErrIsDir = errors.New("this dag node is a directory") ErrCantReadSymlinks = errors.New("cannot currently read symlinks") ErrUnkownNodeType = errors.New("unknown node type") + ErrSeekNotSupported = errors.New("file does not support seeking") ) // TODO: Rename the `DagReader` interface, this doesn't read *any* DAG, just @@ -345,7 +346,7 @@ func (dr *dagReader) Seek(offset int64, whence int) (int64, error) { switch whence { case io.SeekStart: if offset < 0 { - return -1, errors.New("invalid offset") + return dr.offset, errors.New("invalid offset") } if offset == dr.offset { @@ -359,6 +360,11 @@ func (dr *dagReader) Seek(offset int64, whence int) (int64, error) { // Seek from the beginning of the DAG. dr.resetPosition() + // Shortcut seeking to the beginning, we're already there. + if offset == 0 { + return 0, nil + } + // Use the internal reader's context to fetch the child node promises // (see `ipld.NavigableIPLDNode.FetchChild` for details). dr.dagWalker.SetContext(dr.ctx) @@ -388,7 +394,7 @@ func (dr *dagReader) Seek(offset int64, whence int) (int64, error) { // If there aren't enough size hints don't seek // (see the `io.EOF` handling error comment below). if fsNode.NumChildren() != len(node.Links()) { - return io.EOF + return ErrSeekNotSupported } // Internal nodes have no data, so just iterate through the @@ -445,16 +451,6 @@ func (dr *dagReader) Seek(offset int64, whence int) (int64, error) { } }) - if err == io.EOF { - // TODO: Taken from https://github.com/ipfs/go-ipfs/pull/4320, - // check if still valid. - // Return negative number if we can't figure out the file size. Using io.EOF - // for this seems to be good(-enough) solution as it's only returned by - // precalcNextBuf when we step out of file range. - // This is needed for gateway to function properly - return -1, nil - } - if err != nil { return 0, err } diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index 884ae332d..de664370c 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -72,6 +72,71 @@ func TestSeekAndRead(t *testing.T) { } } +func TestSeekWithoutBlocksizes(t *testing.T) { + dserv := testu.GetDAGServ() + ctx, closer := context.WithCancel(context.Background()) + defer closer() + + inbuf := make([]byte, 1024) + + for i := 0; i < 256; i++ { + inbuf[i*4] = byte(i) + } + + inbuf[1023] = 1 // force the reader to be 1024 bytes + node := testu.GetNode(t, dserv, inbuf, testu.UseProtoBufLeaves) + + // remove the blocksizes + pbnode := node.Copy().(*mdag.ProtoNode) + fsnode, err := unixfs.FSNodeFromBytes(pbnode.Data()) + if err != nil { + t.Fatal(err) + } + fsnode.RemoveAllBlockSizes() + newData, err := fsnode.GetBytes() + if err != nil { + t.Fatal(err) + } + pbnode.SetData(newData) + err = dserv.Add(ctx, pbnode) + if err != nil { + t.Fatal(err) + } + node = pbnode + + reader, err := NewDagReader(ctx, node, dserv) + if err != nil { + t.Fatal(err) + } + + _, err = reader.Seek(-4, io.SeekEnd) + if err == nil { + t.Fatal("seeking shouldn't work without blocksizes") + } + + _, err = reader.Seek(4, io.SeekStart) + if err == nil { + t.Fatal("seeking shouldn't work without blocksizes") + } + + _, err = reader.Seek(4, io.SeekCurrent) + if err == nil { + t.Fatal("seeking shouldn't work without blocksizes") + } + + // Seeking to the current position or the end should still work. + + _, err = reader.Seek(0, io.SeekCurrent) + if err != nil { + t.Fatal(err) + } + + _, err = reader.Seek(0, io.SeekStart) + if err != nil { + t.Fatal(err) + } +} + func TestRelativeSeek(t *testing.T) { dserv := testu.GetDAGServ() ctx, closer := context.WithCancel(context.Background()) From b024f2095da39f8766d610e91763caa94782b691 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 5 Jan 2020 16:32:32 -0800 Subject: [PATCH 3155/3817] fix(io): make resetPosition actually reset everything Including the offset. This commit was moved from ipfs/go-unixfs@d1f8577538c558e0dd5abc826a97bdfe715e84c9 --- unixfs/io/dagreader.go | 1 + 1 file changed, 1 insertion(+) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 8dcb5c911..374b50916 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -480,6 +480,7 @@ func (dr *dagReader) Seek(offset int64, whence int) (int64, error) { // in the `SeekStart` case. func (dr *dagReader) resetPosition() { dr.currentNodeData = nil + dr.offset = 0 dr.dagWalker = ipld.NewWalker(dr.ctx, ipld.NewNavigableIPLDNode(dr.rootNode, dr.serv)) // TODO: This could be avoided (along with storing the `dr.rootNode` and From ed4b96d41585ff4159d30c01537a0dbd0d914606 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 7 Jan 2020 09:21:13 -0800 Subject: [PATCH 3156/3817] feat: switch to raw multihashes for blocks Part of: https://github.com/ipfs/go-ipfs/issues/6815 This commit was moved from ipfs/go-ipfs-ds-help@11890cc86e62c173d5159a5c14bd1f528a2b48c1 --- datastore/dshelp/key.go | 23 +++++++++++++++++++---- datastore/dshelp/key_test.go | 5 ++++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 1f47023fe..274da1da4 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -6,6 +6,7 @@ import ( cid "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" "github.com/multiformats/go-base32" + mh "github.com/multiformats/go-multihash" ) // NewKeyFromBinary creates a new key from a byte slice. @@ -21,16 +22,30 @@ func BinaryFromDsKey(k datastore.Key) ([]byte, error) { return base32.RawStdEncoding.DecodeString(k.String()[1:]) } +// MultihashToDsKey creates a Key from the given Multihash. +func MultihashToDsKey(k mh.Multihash) datastore.Key { + return NewKeyFromBinary(k) +} + +// DsKeyToMultihash converts a dsKey to the corresponding Multihash. +func DsKeyToMultihash(dsKey datastore.Key) (mh.Multihash, error) { + kb, err := BinaryFromDsKey(dsKey) + if err != nil { + return nil, err + } + return mh.Cast(kb) +} + // CidToDsKey creates a Key from the given Cid. func CidToDsKey(k cid.Cid) datastore.Key { - return NewKeyFromBinary(k.Bytes()) + return MultihashToDsKey(k.Hash()) } // DsKeyToCid converts the given Key to its corresponding Cid. func DsKeyToCid(dsKey datastore.Key) (cid.Cid, error) { - kb, err := BinaryFromDsKey(dsKey) + hash, err := DsKeyToMultihash(dsKey) if err != nil { - return cid.Cid{}, err + return cid.Cid{}, nil } - return cid.Cast(kb) + return cid.NewCidV1(cid.Raw, hash), nil } diff --git a/datastore/dshelp/key_test.go b/datastore/dshelp/key_test.go index 1f739bf8b..01a463366 100644 --- a/datastore/dshelp/key_test.go +++ b/datastore/dshelp/key_test.go @@ -13,7 +13,10 @@ func TestKey(t *testing.T) { if err != nil { t.Fatal(err) } - if c.String() != c2.String() { + if string(c.Hash()) != string(c2.Hash()) { t.Fatal("should have parsed the same key") } + if c.Equals(c2) || c2.Type() != cid.Raw || c2.Version() != 1 { + t.Fatal("should have been converted to CIDv1-raw") + } } From 04fad5e5f5b11f146f9c9008b5b395f8c9502a42 Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Wed, 15 Jan 2020 12:40:12 -0800 Subject: [PATCH 3157/3817] allow car creation with custom link walker This commit was moved from ipld/go-car@99513a3f029530e0489ad86209e646dd361287da --- ipld/car/car.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index f24c42fae..1ad9c1549 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -6,7 +6,7 @@ import ( "fmt" "io" - "github.com/ipfs/go-block-format" + blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" format "github.com/ipfs/go-ipld-format" @@ -29,12 +29,19 @@ type CarHeader struct { } type carWriter struct { - ds format.DAGService - w io.Writer + ds format.DAGService + w io.Writer + walk WalkFunc } +type WalkFunc func(format.Node) ([]*format.Link, error) + func WriteCar(ctx context.Context, ds format.DAGService, roots []cid.Cid, w io.Writer) error { - cw := &carWriter{ds: ds, w: w} + return WriteCarWithWalker(ctx, ds, roots, w, DefaultWalkFunc) +} + +func WriteCarWithWalker(ctx context.Context, ds format.DAGService, roots []cid.Cid, w io.Writer, walk WalkFunc) error { + cw := &carWriter{ds: ds, w: w, walk: walk} h := &CarHeader{ Roots: roots, @@ -54,6 +61,10 @@ func WriteCar(ctx context.Context, ds format.DAGService, roots []cid.Cid, w io.W return nil } +func DefaultWalkFunc(nd format.Node) ([]*format.Link, error) { + return nd.Links(), nil +} + func ReadHeader(br *bufio.Reader) (*CarHeader, error) { hb, err := util.LdRead(br) if err != nil { From 7166eaf24f82a15aafcae59e754069a3909c4a06 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 23 Sep 2019 14:05:37 -0700 Subject: [PATCH 3158/3817] namesys: set the correct cache TTL on publish fixes https://github.com/ipfs/go-ipfs/issues/6656#issuecomment-534252128 This commit was moved from ipfs/go-namesys@3856c6e677fe80eb10a1bf64dadafa396171f97b --- namesys/namesys.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/namesys/namesys.go b/namesys/namesys.go index f8b8c6d12..31b541538 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -183,6 +183,9 @@ func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path. return err } ttl := DefaultResolverCacheTTL + if setTTL, ok := checkCtxTTL(ctx); ok { + ttl = setTTL + } if ttEol := time.Until(eol); ttEol < ttl { ttl = ttEol } From db9b481249a43a358e2bdb2716cc3efa64b8f7f9 Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Mon, 20 Jan 2020 17:36:34 -0800 Subject: [PATCH 3159/3817] take advantage of batching when loading car, if available This commit was moved from ipld/go-car@f188c0e24291401335b90626aad4ba562948b525 --- ipld/car/car.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/ipld/car/car.go b/ipld/car/car.go index 1ad9c1549..515fc0af7 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -149,12 +149,61 @@ func (cr *carReader) Next() (blocks.Block, error) { return blocks.NewBlockWithCid(data, c) } +type batchStore interface { + PutMany([]blocks.Block) error +} + func LoadCar(s Store, r io.Reader) (*CarHeader, error) { cr, err := NewCarReader(r) if err != nil { return nil, err } + if bs, ok := s.(batchStore); ok { + return loadCarFast(bs, cr) + } + + return loadCarSlow(s, cr) +} + +func loadCarFast(s batchStore, cr *carReader) (*CarHeader, error) { + var buf []blocks.Block + for { + blk, err := cr.Next() + switch err { + case io.EOF: + if len(buf) > 0 { + if err := s.PutMany(buf); err != nil { + return nil, err + } + } + return cr.Header, nil + default: + return nil, err + case nil: + } + + buf = append(buf, blk) + + if len(buf) > 1000 { + if err := s.PutMany(buf); err != nil { + return nil, err + } + buf = buf[:0] + } + } + + if len(buf) > 0 { + if err := s.PutMany(buf); err != nil { + return nil, err + } + } + + return cr.Header, nil +} + +func loadCarSlow(s Store, cr *carReader) (*CarHeader, error) { + for { blk, err := cr.Next() switch err { From 2e4bf45caf6dd57806317ad84d82fbadd823fbc2 Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Tue, 21 Jan 2020 16:05:24 +0100 Subject: [PATCH 3160/3817] Fix preexisting tests - they would never fail as written This commit was moved from ipfs/go-ipfs-chunker@f076b1ef460765220ec72067ea3bbc2ac3976623 --- chunker/parse_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/chunker/parse_test.go b/chunker/parse_test.go index f82aba5f2..f819199d5 100644 --- a/chunker/parse_test.go +++ b/chunker/parse_test.go @@ -15,8 +15,8 @@ func TestParseRabin(t *testing.T) { t.Errorf(err.Error()) } _, err = parseRabinString(r, chk2) - if err == ErrRabinMin { - t.Log("it should be ErrRabinMin here.") + if err != ErrRabinMin { + t.Fatalf("Expected an 'ErrRabinMin' error, got: %#v", err) } } @@ -26,11 +26,11 @@ func TestParseSize(t *testing.T) { size1 := "size-0" size2 := "size-32" _, err := FromString(r, size1) - if err == ErrSize { - t.Log("it should be ErrSize here.") + if err != ErrSize { + t.Fatalf("Expected an 'ErrSize' error, got: %#v", err) } _, err = FromString(r, size2) - if err == ErrSize { + if err != nil { t.Fatal(err) } } From 90e3e8801884cd939fd8a7c9ca2f49f0c1f4f6f9 Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Tue, 21 Jan 2020 16:57:58 +0100 Subject: [PATCH 3161/3817] Add various sanity checks for size specifications This commit was moved from ipfs/go-ipfs-chunker@397f536c853dd5e6145e26c0720a736f18c2e4f2 --- chunker/parse.go | 30 +++++++++++++++++++++++++++++- chunker/splitting.go | 3 --- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/chunker/parse.go b/chunker/parse.go index 5d472b709..59656bf84 100644 --- a/chunker/parse.go +++ b/chunker/parse.go @@ -8,9 +8,25 @@ import ( "strings" ) +const ( + // DefaultBlockSize is the chunk size that splitters produce (or aim to). + DefaultBlockSize int64 = 1024 * 256 + + // 1 MB, on-wire block size for "datablocks ( unixfs, etc )" + // copy of https://github.com/ipfs/go-unixfs/blob/v0.2.3/importer/helpers/helpers.go#L8 + BlockSizeLimit int = 1048576 + + // in case we are using raw-leaves: this would match BlockSizeLimit, but we can't assume that + // be conservative and substract the PB wraping size of a full DAG-PB+UnixFS node describing 1M + // (2b(type2/file)+4b(data-field:3-byte-len-delimited)+4b(size-field:3-byte-varint))+(4b(DAG-type-1:3-byte-len-delimited)) + // FIXME - this calculation will need an update for CBOR + BlockPayloadLimit int = (BlockSizeLimit - (2 + 4 + 4 + 4)) +) + var ( ErrRabinMin = errors.New("rabin min must be greater than 16") - ErrSize = errors.New("chunker size muster greater than 0") + ErrSize = errors.New("chunker size must be greater than 0") + ErrSizeMax = fmt.Errorf("chunker parameters may not exceed the maximum block payload size of %d", BlockPayloadLimit) ) // FromString returns a Splitter depending on the given string: @@ -28,6 +44,8 @@ func FromString(r io.Reader, chunker string) (Splitter, error) { return nil, err } else if size <= 0 { return nil, ErrSize + } else if size > BlockPayloadLimit { + return nil, ErrSizeMax } return NewSizeSplitter(r, int64(size)), nil @@ -51,6 +69,8 @@ func parseRabinString(r io.Reader, chunker string) (Splitter, error) { size, err := strconv.Atoi(parts[1]) if err != nil { return nil, err + } else if int(float32(size)*1.5) > BlockPayloadLimit { // FIXME - there is probably a better way to bubble up this calculation from NewRabin() + return nil, ErrSizeMax } return NewRabin(r, uint64(size)), nil case 4: @@ -84,6 +104,14 @@ func parseRabinString(r io.Reader, chunker string) (Splitter, error) { return nil, err } + if min >= avg { + return nil, errors.New("incorrect format: rabin-min must be smaller than rabin-avg") + } else if avg >= max { + return nil, errors.New("incorrect format: rabin-avg must be smaller than rabin-max") + } else if max > BlockPayloadLimit { + return nil, ErrSizeMax + } + return NewRabinMinMax(r, uint64(min), uint64(avg), uint64(max)), nil default: return nil, errors.New("incorrect format (expected 'rabin' 'rabin-[avg]' or 'rabin-[min]-[avg]-[max]'") diff --git a/chunker/splitting.go b/chunker/splitting.go index 2b2373992..a137820ab 100644 --- a/chunker/splitting.go +++ b/chunker/splitting.go @@ -13,9 +13,6 @@ import ( var log = logging.Logger("chunk") -// DefaultBlockSize is the chunk size that splitters produce (or aim to). -var DefaultBlockSize int64 = 1024 * 256 - // A Splitter reads bytes from a Reader and creates "chunks" (byte slices) // that can be used to build DAG nodes. type Splitter interface { From 641b54b5eaf6ebe73a814e3c17d0d8f30a6b0373 Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Tue, 21 Jan 2020 16:58:17 +0100 Subject: [PATCH 3162/3817] Test all the things This commit was moved from ipfs/go-ipfs-chunker@65d7a6e9ab7c927a2f596590b6eabe00051368e9 --- chunker/parse_test.go | 70 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/chunker/parse_test.go b/chunker/parse_test.go index f819199d5..242d582ee 100644 --- a/chunker/parse_test.go +++ b/chunker/parse_test.go @@ -2,35 +2,79 @@ package chunk import ( "bytes" + "fmt" "testing" ) +const ( + testTwoThirdsOfBlockPayloadLimit = 2 * (float32(BlockPayloadLimit) / float32(3)) +) + func TestParseRabin(t *testing.T) { - max := 1000 - r := bytes.NewReader(randBuf(t, max)) - chk1 := "rabin-18-25-32" - chk2 := "rabin-15-23-31" - _, err := parseRabinString(r, chk1) + r := bytes.NewReader(randBuf(t, 1000)) + + _, err := FromString(r, "rabin-18-25-32") if err != nil { t.Errorf(err.Error()) } - _, err = parseRabinString(r, chk2) + + _, err = FromString(r, "rabin-15-23-31") if err != ErrRabinMin { t.Fatalf("Expected an 'ErrRabinMin' error, got: %#v", err) } + + _, err = FromString(r, "rabin-20-20-21") + if err == nil || err.Error() != "incorrect format: rabin-min must be smaller than rabin-avg" { + t.Fatalf("Expected an arg-out-of-order error, got: %#v", err) + } + + _, err = FromString(r, "rabin-19-21-21") + if err == nil || err.Error() != "incorrect format: rabin-avg must be smaller than rabin-max" { + t.Fatalf("Expected an arg-out-of-order error, got: %#v", err) + } + + _, err = FromString(r, fmt.Sprintf("rabin-19-21-%d", BlockPayloadLimit)) + if err != nil { + t.Fatalf("Expected success, got: %#v", err) + } + + _, err = FromString(r, fmt.Sprintf("rabin-19-21-%d", 1+BlockPayloadLimit)) + if err != ErrSizeMax { + t.Fatalf("Expected 'ErrSizeMax', got: %#v", err) + } + + _, err = FromString(r, fmt.Sprintf("rabin-%.0f", testTwoThirdsOfBlockPayloadLimit)) + if err != nil { + t.Fatalf("Expected success, got: %#v", err) + } + + _, err = FromString(r, fmt.Sprintf("rabin-%.0f", 1+testTwoThirdsOfBlockPayloadLimit)) + if err != ErrSizeMax { + t.Fatalf("Expected 'ErrSizeMax', got: %#v", err) + } + } func TestParseSize(t *testing.T) { - max := 1000 - r := bytes.NewReader(randBuf(t, max)) - size1 := "size-0" - size2 := "size-32" - _, err := FromString(r, size1) + r := bytes.NewReader(randBuf(t, 1000)) + + _, err := FromString(r, "size-0") if err != ErrSize { t.Fatalf("Expected an 'ErrSize' error, got: %#v", err) } - _, err = FromString(r, size2) + + _, err = FromString(r, "size-32") + if err != nil { + t.Fatalf("Expected success, got: %#v", err) + } + + _, err = FromString(r, fmt.Sprintf("size-%d", BlockPayloadLimit)) if err != nil { - t.Fatal(err) + t.Fatalf("Expected success, got: %#v", err) + } + + _, err = FromString(r, fmt.Sprintf("size-%d", 1+BlockPayloadLimit)) + if err != ErrSizeMax { + t.Fatalf("Expected 'ErrSizeMax', got: %#v", err) } } From 65e7f3a2f7a86dd15100c97a527fac7aab116a64 Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Tue, 21 Jan 2020 17:03:50 +0100 Subject: [PATCH 3163/3817] Remove last pieces of gx This commit was moved from ipfs/go-ipfs-chunker@5f9fd98c2afd218261ae9405a8f5d647498a2f88 --- chunker/Makefile | 18 ------------------ chunker/README.md | 2 -- 2 files changed, 20 deletions(-) delete mode 100644 chunker/Makefile diff --git a/chunker/Makefile b/chunker/Makefile deleted file mode 100644 index 73f2841f6..000000000 --- a/chunker/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -all: deps -gx: - go get github.com/whyrusleeping/gx - go get github.com/whyrusleeping/gx-go -deps: gx - gx --verbose install --global - gx-go rewrite -test: deps - gx test -v -race -coverprofile=coverage.txt -covermode=atomic . -rw: - gx-go rewrite -rwundo: - gx-go rewrite --undo -publish: rwundo - gx publish -.PHONY: all gx deps test rw rwundo publish - - diff --git a/chunker/README.md b/chunker/README.md index 84161c546..7b1b5b238 100644 --- a/chunker/README.md +++ b/chunker/README.md @@ -31,8 +31,6 @@ The package provides a `SizeSplitter` which creates chunks of equal size and it > go get github.com/ipfs/go-ipfs-chunker ``` -It uses [Gx](https://github.com/whyrusleeping/gx) to manage dependencies. You can use `make all` to build it with the `gx` dependencies. - ## Usage ``` From 3817b1eb0760edba0d12e7fc527496f5cb0b5081 Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Tue, 21 Jan 2020 21:06:23 +0100 Subject: [PATCH 3164/3817] Address block/chunk logical mismatch, name things correctly This commit was moved from ipfs/go-ipfs-chunker@4414aa269d6baa18717e34aa87f9a72d4948ff4b --- chunker/parse.go | 21 ++++++++------------- chunker/parse_test.go | 14 +++++++------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/chunker/parse.go b/chunker/parse.go index 59656bf84..dee830489 100644 --- a/chunker/parse.go +++ b/chunker/parse.go @@ -12,21 +12,16 @@ const ( // DefaultBlockSize is the chunk size that splitters produce (or aim to). DefaultBlockSize int64 = 1024 * 256 - // 1 MB, on-wire block size for "datablocks ( unixfs, etc )" - // copy of https://github.com/ipfs/go-unixfs/blob/v0.2.3/importer/helpers/helpers.go#L8 - BlockSizeLimit int = 1048576 - - // in case we are using raw-leaves: this would match BlockSizeLimit, but we can't assume that - // be conservative and substract the PB wraping size of a full DAG-PB+UnixFS node describing 1M - // (2b(type2/file)+4b(data-field:3-byte-len-delimited)+4b(size-field:3-byte-varint))+(4b(DAG-type-1:3-byte-len-delimited)) - // FIXME - this calculation will need an update for CBOR - BlockPayloadLimit int = (BlockSizeLimit - (2 + 4 + 4 + 4)) + // No leaf block should contain more than 1MiB of payload data ( wrapping overhead aside ) + // This effectively mandates the maximum chunk size + // See discussion at https://github.com/ipfs/go-ipfs-chunker/pull/21#discussion_r369124879 for background + ChunkSizeLimit int = 1048576 ) var ( ErrRabinMin = errors.New("rabin min must be greater than 16") ErrSize = errors.New("chunker size must be greater than 0") - ErrSizeMax = fmt.Errorf("chunker parameters may not exceed the maximum block payload size of %d", BlockPayloadLimit) + ErrSizeMax = fmt.Errorf("chunker parameters may not exceed the maximum chunk size of %d", ChunkSizeLimit) ) // FromString returns a Splitter depending on the given string: @@ -44,7 +39,7 @@ func FromString(r io.Reader, chunker string) (Splitter, error) { return nil, err } else if size <= 0 { return nil, ErrSize - } else if size > BlockPayloadLimit { + } else if size > ChunkSizeLimit { return nil, ErrSizeMax } return NewSizeSplitter(r, int64(size)), nil @@ -69,7 +64,7 @@ func parseRabinString(r io.Reader, chunker string) (Splitter, error) { size, err := strconv.Atoi(parts[1]) if err != nil { return nil, err - } else if int(float32(size)*1.5) > BlockPayloadLimit { // FIXME - there is probably a better way to bubble up this calculation from NewRabin() + } else if int(float32(size)*1.5) > ChunkSizeLimit { // FIXME - this will be addressed in a subsequent PR return nil, ErrSizeMax } return NewRabin(r, uint64(size)), nil @@ -108,7 +103,7 @@ func parseRabinString(r io.Reader, chunker string) (Splitter, error) { return nil, errors.New("incorrect format: rabin-min must be smaller than rabin-avg") } else if avg >= max { return nil, errors.New("incorrect format: rabin-avg must be smaller than rabin-max") - } else if max > BlockPayloadLimit { + } else if max > ChunkSizeLimit { return nil, ErrSizeMax } diff --git a/chunker/parse_test.go b/chunker/parse_test.go index 242d582ee..237a2b439 100644 --- a/chunker/parse_test.go +++ b/chunker/parse_test.go @@ -7,7 +7,7 @@ import ( ) const ( - testTwoThirdsOfBlockPayloadLimit = 2 * (float32(BlockPayloadLimit) / float32(3)) + testTwoThirdsOfChunkLimit = 2 * (float32(ChunkSizeLimit) / float32(3)) ) func TestParseRabin(t *testing.T) { @@ -33,22 +33,22 @@ func TestParseRabin(t *testing.T) { t.Fatalf("Expected an arg-out-of-order error, got: %#v", err) } - _, err = FromString(r, fmt.Sprintf("rabin-19-21-%d", BlockPayloadLimit)) + _, err = FromString(r, fmt.Sprintf("rabin-19-21-%d", ChunkSizeLimit)) if err != nil { t.Fatalf("Expected success, got: %#v", err) } - _, err = FromString(r, fmt.Sprintf("rabin-19-21-%d", 1+BlockPayloadLimit)) + _, err = FromString(r, fmt.Sprintf("rabin-19-21-%d", 1+ChunkSizeLimit)) if err != ErrSizeMax { t.Fatalf("Expected 'ErrSizeMax', got: %#v", err) } - _, err = FromString(r, fmt.Sprintf("rabin-%.0f", testTwoThirdsOfBlockPayloadLimit)) + _, err = FromString(r, fmt.Sprintf("rabin-%.0f", testTwoThirdsOfChunkLimit)) if err != nil { t.Fatalf("Expected success, got: %#v", err) } - _, err = FromString(r, fmt.Sprintf("rabin-%.0f", 1+testTwoThirdsOfBlockPayloadLimit)) + _, err = FromString(r, fmt.Sprintf("rabin-%.0f", 1+testTwoThirdsOfChunkLimit)) if err != ErrSizeMax { t.Fatalf("Expected 'ErrSizeMax', got: %#v", err) } @@ -68,12 +68,12 @@ func TestParseSize(t *testing.T) { t.Fatalf("Expected success, got: %#v", err) } - _, err = FromString(r, fmt.Sprintf("size-%d", BlockPayloadLimit)) + _, err = FromString(r, fmt.Sprintf("size-%d", ChunkSizeLimit)) if err != nil { t.Fatalf("Expected success, got: %#v", err) } - _, err = FromString(r, fmt.Sprintf("size-%d", 1+BlockPayloadLimit)) + _, err = FromString(r, fmt.Sprintf("size-%d", 1+ChunkSizeLimit)) if err != ErrSizeMax { t.Fatalf("Expected 'ErrSizeMax', got: %#v", err) } From 7a2fc1b8165e4079351f62ed4eec6a3fd9bd0aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Wed, 22 Jan 2020 17:11:19 +0100 Subject: [PATCH 3165/3817] test: fail early on err to avoid an unrelated panic This commit was moved from ipfs/interface-go-ipfs-core@df21c57e0f09b481b02f09cf3da20d17d25414e9 --- coreiface/tests/block.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 6b648f394..3777d2259 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -156,7 +156,7 @@ func (tp *TestSuite) TestBlockRm(t *testing.T) { _, err = api.Block().Get(ctx, res.Path()) if err == nil { - t.Error("expected err to exist") + t.Fatal("expected err to exist") } if !strings.Contains(err.Error(), "blockservice: key not found") { t.Errorf("unexpected error; %s", err.Error()) @@ -164,7 +164,7 @@ func (tp *TestSuite) TestBlockRm(t *testing.T) { err = api.Block().Rm(ctx, res.Path()) if err == nil { - t.Error("expected err to exist") + t.Fatal("expected err to exist") } if !strings.Contains(err.Error(), "blockstore: block not found") { t.Errorf("unexpected error; %s", err.Error()) From a117bd53c6cdccf16623087459749a73413bb430 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 16 Jan 2020 15:48:20 -0800 Subject: [PATCH 3166/3817] fix: migrate from deprecated warning function This commit was moved from ipfs/go-ipfs-keystore@54050cb4f9ab6ca3d62027100d6d740bbe130a44 --- keystore/keystore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index 237d4b05c..991de5dd1 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -168,7 +168,7 @@ func (ks *FSKeystore) List() ([]string, error) { if err == nil { list = append(list, name) } else { - log.Warningf("Ignoring the invalid keyfile: %s", name) + log.Warnf("Ignoring the invalid keyfile: %s", name) } } From 5b021581acfd939301275a761385c6427a1b6660 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 16 Jan 2020 16:18:53 -0800 Subject: [PATCH 3167/3817] fix(tracing): remove event tracing We've deprecated this system and have yet to move to a new system. We might as well remove everything, switch to a new system, then deliberately trace the entire system. This commit was moved from ipfs/go-namesys@f00b18eecc1997b26cc02d527203d0b750446d2c --- namesys/resolve/resolve.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/namesys/resolve/resolve.go b/namesys/resolve/resolve.go index 44f735a1f..5b5dc515e 100644 --- a/namesys/resolve/resolve.go +++ b/namesys/resolve/resolve.go @@ -7,15 +7,12 @@ import ( "strings" "github.com/ipfs/go-ipld-format" - logging "github.com/ipfs/go-log" "github.com/ipfs/go-path" "github.com/ipfs/go-path/resolver" "github.com/ipfs/go-ipfs/namesys" ) -var log = logging.Logger("nsresolv") - // ErrNoNamesys is an explicit error for when an IPFS node doesn't // (yet) have a name system var ErrNoNamesys = errors.New( @@ -24,13 +21,8 @@ var ErrNoNamesys = errors.New( // ResolveIPNS resolves /ipns paths func ResolveIPNS(ctx context.Context, nsys namesys.NameSystem, p path.Path) (path.Path, error) { if strings.HasPrefix(p.String(), "/ipns/") { - evt := log.EventBegin(ctx, "resolveIpnsPath") - defer evt.Done() - // resolve ipns paths - // TODO(cryptix): we should be able to query the local cache for the path if nsys == nil { - evt.Append(logging.LoggableMap{"error": ErrNoNamesys.Error()}) return "", ErrNoNamesys } @@ -38,27 +30,23 @@ func ResolveIPNS(ctx context.Context, nsys namesys.NameSystem, p path.Path) (pat if len(seg) < 2 || seg[1] == "" { // just "/" without further segments err := fmt.Errorf("invalid path %q: ipns path missing IPNS ID", p) - evt.Append(logging.LoggableMap{"error": err}) return "", err } extensions := seg[2:] resolvable, err := path.FromSegments("/", seg[0], seg[1]) if err != nil { - evt.Append(logging.LoggableMap{"error": err.Error()}) return "", err } respath, err := nsys.Resolve(ctx, resolvable.String()) if err != nil { - evt.Append(logging.LoggableMap{"error": err.Error()}) return "", err } segments := append(respath.Segments(), extensions...) p, err = path.FromSegments("/", segments...) if err != nil { - evt.Append(logging.LoggableMap{"error": err.Error()}) return "", err } } From 1cfe8e6646fcb60d612f37808a7d79e3dac2968d Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 31 Jan 2020 12:12:21 +0100 Subject: [PATCH 3168/3817] Remove DsKeyToCid() and CidToDsKey(). Add DsKeyToCidV1Raw() We are deprecating these functions as ipfs strives to store multihashes directly on the datastore, rather than CIDs. Thus multiple CIDs might map to the same multihash. Making this functions reflect that behaviour might cause silent breakage in users. We prefer loud breakage. Users using CidToDsKey(c) should move to MultihashToDsKey(c.Hash()). Users using DsKeyToCid() should move to DsKeyToCidV1Raw() or DsKeyToMultihash(). Note that datastore should handle migrations to replace existing CID-encoded keys with raw multihashes, or preserve DsKeyToCid() and CidToDsKey() in their own codebases. This commit was moved from ipfs/go-ipfs-ds-help@48143f3d8539bdc6b321809c46f06989b1003fab --- datastore/dshelp/key.go | 17 ++++++++--------- datastore/dshelp/key_test.go | 12 +++++++----- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index 274da1da4..db719d7b5 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -3,7 +3,7 @@ package dshelp import ( - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" "github.com/multiformats/go-base32" mh "github.com/multiformats/go-multihash" @@ -23,6 +23,9 @@ func BinaryFromDsKey(k datastore.Key) ([]byte, error) { } // MultihashToDsKey creates a Key from the given Multihash. +// If working with Cids, you can call cid.Hash() to obtain +// the multihash. Note that different CIDs might represent +// the same multihash. func MultihashToDsKey(k mh.Multihash) datastore.Key { return NewKeyFromBinary(k) } @@ -36,16 +39,12 @@ func DsKeyToMultihash(dsKey datastore.Key) (mh.Multihash, error) { return mh.Cast(kb) } -// CidToDsKey creates a Key from the given Cid. -func CidToDsKey(k cid.Cid) datastore.Key { - return MultihashToDsKey(k.Hash()) -} - -// DsKeyToCid converts the given Key to its corresponding Cid. -func DsKeyToCid(dsKey datastore.Key) (cid.Cid, error) { +// DsKeyToCidV1Raw converts the given Key (which should be a raw multihash +// key) to a Cid V1 of raw type. +func DsKeyToCidV1Raw(dsKey datastore.Key) (cid.Cid, error) { hash, err := DsKeyToMultihash(dsKey) if err != nil { - return cid.Cid{}, nil + return cid.Cid{}, err } return cid.NewCidV1(cid.Raw, hash), nil } diff --git a/datastore/dshelp/key_test.go b/datastore/dshelp/key_test.go index 01a463366..ac061f3c7 100644 --- a/datastore/dshelp/key_test.go +++ b/datastore/dshelp/key_test.go @@ -8,15 +8,17 @@ import ( func TestKey(t *testing.T) { c, _ := cid.Decode("QmP63DkAFEnDYNjDYBpyNDfttu1fvUw99x1brscPzpqmmq") - dsKey := CidToDsKey(c) - c2, err := DsKeyToCid(dsKey) + dsKey := MultihashToDsKey(c.Hash()) + mh, err := DsKeyToMultihash(dsKey) if err != nil { t.Fatal(err) } - if string(c.Hash()) != string(c2.Hash()) { - t.Fatal("should have parsed the same key") + if string(c.Hash()) != string(mh) { + t.Fatal("should have parsed the same multihash") } - if c.Equals(c2) || c2.Type() != cid.Raw || c2.Version() != 1 { + + c2, err := DsKeyToCidV1Raw(dsKey) + if err != nil || c.Equals(c2) || c2.Type() != cid.Raw || c2.Version() != 1 { t.Fatal("should have been converted to CIDv1-raw") } } From 7eb7b84111965b7be8590648b776c7dd692a87d5 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 31 Jan 2020 17:12:27 +0100 Subject: [PATCH 3169/3817] Replace DsKeyToCidV1Raw() with DsKeyToCidV1() Allow passing in whatever codec type the user wants This commit was moved from ipfs/go-ipfs-ds-help@0c9f4a7ce67a8ad26723cb73284646dee7bf0651 --- datastore/dshelp/key.go | 7 ++++--- datastore/dshelp/key_test.go | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/datastore/dshelp/key.go b/datastore/dshelp/key.go index db719d7b5..32b73a61e 100644 --- a/datastore/dshelp/key.go +++ b/datastore/dshelp/key.go @@ -40,11 +40,12 @@ func DsKeyToMultihash(dsKey datastore.Key) (mh.Multihash, error) { } // DsKeyToCidV1Raw converts the given Key (which should be a raw multihash -// key) to a Cid V1 of raw type. -func DsKeyToCidV1Raw(dsKey datastore.Key) (cid.Cid, error) { +// key) to a Cid V1 of the given type (see +// https://godoc.org/github.com/ipfs/go-cid#pkg-constants). +func DsKeyToCidV1(dsKey datastore.Key, codecType uint64) (cid.Cid, error) { hash, err := DsKeyToMultihash(dsKey) if err != nil { return cid.Cid{}, err } - return cid.NewCidV1(cid.Raw, hash), nil + return cid.NewCidV1(codecType, hash), nil } diff --git a/datastore/dshelp/key_test.go b/datastore/dshelp/key_test.go index ac061f3c7..ff9fcc7d6 100644 --- a/datastore/dshelp/key_test.go +++ b/datastore/dshelp/key_test.go @@ -17,7 +17,7 @@ func TestKey(t *testing.T) { t.Fatal("should have parsed the same multihash") } - c2, err := DsKeyToCidV1Raw(dsKey) + c2, err := DsKeyToCidV1(dsKey, cid.Raw) if err != nil || c.Equals(c2) || c2.Type() != cid.Raw || c2.Version() != 1 { t.Fatal("should have been converted to CIDv1-raw") } From 5a92dcb3cf10a3eb28813282e7eb5a3444ffeaeb Mon Sep 17 00:00:00 2001 From: Alex Trottier Date: Sat, 1 Feb 2020 15:20:52 -0800 Subject: [PATCH 3170/3817] chore: update dependencies, adjust use of removed functions, go 1.13 This commit was moved from ipfs/go-ipfs-blockstore@7a4b99e54ea86b4c4cc6d8c04fb617b9187c34cb --- blockstore/blockstore.go | 22 ++++++++++++---------- blockstore/blockstore_test.go | 4 ++++ blockstore/bloom_cache_test.go | 5 +++++ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 03004b592..a2eb65c7b 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -119,8 +119,7 @@ func (bs *blockstore) Get(k cid.Cid) (blocks.Block, error) { log.Error("undefined cid in blockstore") return nil, ErrNotFound } - - bdata, err := bs.datastore.Get(dshelp.CidToDsKey(k)) + bdata, err := bs.datastore.Get(dshelp.MultihashToDsKey(k.Hash())) if err == ds.ErrNotFound { return nil, ErrNotFound } @@ -143,7 +142,7 @@ func (bs *blockstore) Get(k cid.Cid) (blocks.Block, error) { } func (bs *blockstore) Put(block blocks.Block) error { - k := dshelp.CidToDsKey(block.Cid()) + k := dshelp.MultihashToDsKey(block.Cid().Hash()) // Has is cheaper than Put, so see if we already have it exists, err := bs.datastore.Has(k) @@ -159,7 +158,7 @@ func (bs *blockstore) PutMany(blocks []blocks.Block) error { return err } for _, b := range blocks { - k := dshelp.CidToDsKey(b.Cid()) + k := dshelp.MultihashToDsKey(b.Cid().Hash()) exists, err := bs.datastore.Has(k) if err == nil && exists { continue @@ -174,11 +173,11 @@ func (bs *blockstore) PutMany(blocks []blocks.Block) error { } func (bs *blockstore) Has(k cid.Cid) (bool, error) { - return bs.datastore.Has(dshelp.CidToDsKey(k)) + return bs.datastore.Has(dshelp.MultihashToDsKey(k.Hash())) } func (bs *blockstore) GetSize(k cid.Cid) (int, error) { - size, err := bs.datastore.GetSize(dshelp.CidToDsKey(k)) + size, err := bs.datastore.GetSize(dshelp.MultihashToDsKey(k.Hash())) if err == ds.ErrNotFound { return -1, ErrNotFound } @@ -186,7 +185,7 @@ func (bs *blockstore) GetSize(k cid.Cid) (int, error) { } func (bs *blockstore) DeleteBlock(k cid.Cid) error { - return bs.datastore.Delete(dshelp.CidToDsKey(k)) + return bs.datastore.Delete(dshelp.MultihashToDsKey(k.Hash())) } // AllKeysChan runs a query for keys from the blockstore. @@ -220,12 +219,15 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { } // need to convert to key.Key using key.KeyFromDsKey. - k, err := dshelp.DsKeyToCid(ds.RawKey(e.Key)) + bk, err := dshelp.BinaryFromDsKey(ds.RawKey(e.Key)) if err != nil { - log.Warningf("error parsing key from DsKey: %s", err) + log.Warning("error pasring key from binary: %s", err) continue } - + k, err := cid.Cast(bk) + if err != nil { + log.Warningf("failed to cast cid: %s", err) + } select { case <-ctx.Done(): return diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index b01574a44..44225593a 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -269,6 +269,10 @@ func (c *queryTestDS) Query(q dsq.Query) (dsq.Results, error) { return c.ds.Query(q) } +func (c *queryTestDS) Sync(key ds.Key) error { + return c.ds.Sync(key) +} + func (c *queryTestDS) Batch() (ds.Batch, error) { return ds.NewBasicBatch(c), nil } diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index fd65c0b28..3b290a0c2 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -202,6 +202,11 @@ func (c *callbackDatastore) Query(q dsq.Query) (dsq.Results, error) { return c.ds.Query(q) } +func (c *callbackDatastore) Sync(key ds.Key) error { + c.CallF() + return c.ds.Sync(key) +} + func (c *callbackDatastore) Batch() (ds.Batch, error) { return ds.NewBasicBatch(c), nil } From bb2ba5c09f380093085a2129b6c47d62eb14311e Mon Sep 17 00:00:00 2001 From: Alex Trottier Date: Sat, 1 Feb 2020 15:24:20 -0800 Subject: [PATCH 3171/3817] fix typo in blockstore.go This commit was moved from ipfs/go-ipfs-blockstore@e62b875f6ff6a9f4230fa569eeef89bf97e7d2b7 --- blockstore/blockstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index a2eb65c7b..a7e8b5daa 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -221,7 +221,7 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // need to convert to key.Key using key.KeyFromDsKey. bk, err := dshelp.BinaryFromDsKey(ds.RawKey(e.Key)) if err != nil { - log.Warning("error pasring key from binary: %s", err) + log.Warningf("error parsing key from binary: %s", err) continue } k, err := cid.Cast(bk) From 0d1cc0714586d95974d0afc23cccbc2891e5c281 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sat, 1 Feb 2020 17:01:48 -0800 Subject: [PATCH 3172/3817] fix: return raw block CIDs instead of V0 CIDs from AllKeysChan V0 CIDs are always "dag-pb". Using the "raw" codec indicates that these blocks are just raw data. This commit was moved from ipfs/go-ipfs-blockstore@5f9214c8db55d92b36a16971d47854e7123f78e7 --- blockstore/blockstore.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index a7e8b5daa..e815642da 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -224,10 +224,7 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { log.Warningf("error parsing key from binary: %s", err) continue } - k, err := cid.Cast(bk) - if err != nil { - log.Warningf("failed to cast cid: %s", err) - } + k := cid.NewCidV1(cid.Raw, bk) select { case <-ctx.Done(): return From c2d202aeca5f1065e3921deca19bab9c7cde1afa Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 7 Jan 2020 09:34:11 -0800 Subject: [PATCH 3173/3817] feat: switch to raw multihashes for blocks Part of: https://github.com/ipfs/go-ipfs/issues/6815 This commit was moved from ipfs/go-ipfs-blockstore@8f266cac109a60c25dd62959cc03127329fc2976 --- blockstore/arc_cache.go | 6 +++--- blockstore/blockstore_test.go | 33 ++++++++++++++++++++++++++------- blockstore/bloom_cache.go | 8 ++++---- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 8e88fa81a..e2b930dca 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -59,7 +59,7 @@ func (b *arccache) hasCached(k cid.Cid) (has bool, size int, ok bool) { return false, -1, false } - h, ok := b.arc.Get(k.KeyString()) + h, ok := b.arc.Get(string(k.Hash())) if ok { b.hits.Inc() switch h := h.(type) { @@ -160,11 +160,11 @@ func (b *arccache) HashOnRead(enabled bool) { } func (b *arccache) cacheHave(c cid.Cid, have bool) { - b.arc.Add(c.KeyString(), cacheHave(have)) + b.arc.Add(string(c.Hash()), cacheHave(have)) } func (b *arccache) cacheSize(c cid.Cid, blockSize int) { - b.arc.Add(c.KeyString(), cacheSize(blockSize)) + b.arc.Add(string(c.Hash()), cacheSize(blockSize)) } func (b *arccache) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 44225593a..a165652f4 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -53,6 +53,24 @@ func TestPutThenGetBlock(t *testing.T) { } } +func TestCidv0v1(t *testing.T) { + bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) + block := blocks.NewBlock([]byte("some data")) + + err := bs.Put(block) + if err != nil { + t.Fatal(err) + } + + blockFromBlockstore, err := bs.Get(cid.NewCidV1(cid.DagProtobuf, block.Cid().Hash())) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(block.RawData(), blockFromBlockstore.RawData()) { + t.Fail() + } +} + func TestPutThenGetSizeBlock(t *testing.T) { bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) block := blocks.NewBlock([]byte("some data")) @@ -218,18 +236,19 @@ func TestAllKeysRespectsContext(t *testing.T) { } func expectMatches(t *testing.T, expect, actual []cid.Cid) { + t.Helper() if len(expect) != len(actual) { t.Errorf("expect and actual differ: %d != %d", len(expect), len(actual)) } + + actualSet := make(map[string]bool, len(actual)) + for _, k := range actual { + actualSet[string(k.Hash())] = true + } + for _, ek := range expect { - found := false - for _, ak := range actual { - if ek.Equals(ak) { - found = true - } - } - if !found { + if !actualSet[string(ek.Hash())] { t.Error("expected key not found: ", ek) } } diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 6e28ecec6..b4fadc2ef 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -103,7 +103,7 @@ func (b *bloomcache) build(ctx context.Context) error { atomic.StoreInt32(&b.active, 1) return nil } - b.bloom.AddTS(key.Bytes()) // Use binary key, the more compact the better + b.bloom.AddTS(key.Hash()) // Use binary key, the more compact the better case <-ctx.Done(): b.buildErr = ctx.Err() return b.buildErr @@ -130,7 +130,7 @@ func (b *bloomcache) hasCached(k cid.Cid) (has bool, ok bool) { return false, false } if b.BloomActive() { - blr := b.bloom.HasTS(k.Bytes()) + blr := b.bloom.HasTS(k.Hash()) if !blr { // not contained in bloom is only conclusive answer bloom gives b.hits.Inc() return false, true @@ -163,7 +163,7 @@ func (b *bloomcache) Put(bl blocks.Block) error { // See comment in PutMany err := b.blockstore.Put(bl) if err == nil { - b.bloom.AddTS(bl.Cid().Bytes()) + b.bloom.AddTS(bl.Cid().Hash()) } return err } @@ -178,7 +178,7 @@ func (b *bloomcache) PutMany(bs []blocks.Block) error { return err } for _, bl := range bs { - b.bloom.AddTS(bl.Cid().Bytes()) + b.bloom.AddTS(bl.Cid().Hash()) } return nil } From 555a0b2ead32da70eb9cc94531ccfb8f682f052a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 7 Feb 2020 13:48:34 -0800 Subject: [PATCH 3174/3817] fix: fix a panic when deleting If we try deleting a bunch of files _before_ reading them, we'll panic because they haven't been loaded. This works around that by using the _link_ directly when the child hasn't been loaded. fixes https://github.com/ipfs/go-ipfs/issues/6860 This commit was moved from ipfs/go-unixfs@af8709db62ebcce86b400fb3e1cd75223ef31a15 --- unixfs/hamt/hamt.go | 32 +++++++++++++++++++++++++++++--- unixfs/hamt/hamt_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 6b89b47c7..374f47df2 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -489,10 +489,28 @@ func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val // structures, this will help to normalize them. return ds.childer.rm(idx) case 1: - nchild := child.childer.children[0] - if nchild.isValueNode() { + // The single child _should_ be a value by + // induction. However, we allow for it to be a + // shard in case an implementation is broken. + + // Have we loaded the child? Prefer that. + schild := child.childer.child(0) + if schild != nil { + if schild.isValueNode() { + ds.childer.set(schild, i) + } + return nil + } + + // Otherwise, work with the link. + slnk := child.childer.link(0) + lnkType, err := child.childer.sd.childLinkType(slnk) + if err != nil { + return err + } + if lnkType == shardValueLink { // sub-shard with a single value element, collapse it - ds.childer.set(nchild, i) + ds.childer.setLink(slnk, i) } return nil } @@ -517,6 +535,8 @@ type childer struct { sd *Shard dserv ipld.DAGService bitfield bitfield.Bitfield + + // Only one of links/children will be non-nil for every child/link. links []*ipld.Link children []*Shard } @@ -572,6 +592,12 @@ func (s *childer) insert(key string, lnk *ipld.Link, idx int) error { func (s *childer) set(sd *Shard, i int) { s.children[i] = sd + s.links[i] = nil +} + +func (s *childer) setLink(lnk *ipld.Link, i int) { + s.children[i] = nil + s.links[i] = lnk } func (s *childer) rm(childIndex int) error { diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 4025bfb37..4d2f64b22 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -268,6 +268,45 @@ func TestRemoveElems(t *testing.T) { } } +func TestRemoveAfterMarshal(t *testing.T) { + ds := mdtest.Mock() + dirs, s, err := makeDir(ds, 500) + if err != nil { + t.Fatal(err) + } + nd, err := s.Node() + if err != nil { + t.Fatal(err) + } + + s, err = NewHamtFromDag(ds, nd) + + ctx := context.Background() + + shuffle(time.Now().UnixNano(), dirs) + + for i, d := range dirs { + err := s.Remove(ctx, d) + if err != nil { + t.Fatalf("%d/%d: %s", i, len(dirs), err) + } + } + + nd, err = s.Node() + if err != nil { + t.Fatal(err) + } + + if len(nd.Links()) > 0 { + t.Fatal("shouldnt have any links here") + } + + err = s.Remove(ctx, "doesnt exist") + if err != os.ErrNotExist { + t.Fatal("expected error does not exist") + } +} + func TestSetAfterMarshal(t *testing.T) { ds := mdtest.Mock() _, s, err := makeDir(ds, 300) From b3dd77e42e188dd0d78a8a62bff49810674d2ec7 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 14 Feb 2020 14:42:13 +0100 Subject: [PATCH 3175/3817] Update readme and license Fix copyright year and holder (Protocol Labs) in LICENSE file. Remove mentions of `gx` and fix link to travis in README file. This commit was moved from ipfs/go-ipfs-blockstore@14b16b1f455798a03a4aa63174d875408a9f4139 --- blockstore/LICENSE | 2 +- blockstore/README.md | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/blockstore/LICENSE b/blockstore/LICENSE index e4224df5b..51d20a894 100644 --- a/blockstore/LICENSE +++ b/blockstore/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 IPFS +Copyright (c) 2020 Protocol Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/blockstore/README.md b/blockstore/README.md index e63410183..f2cec9403 100644 --- a/blockstore/README.md +++ b/blockstore/README.md @@ -4,7 +4,7 @@ [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) [![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) [![GoDoc](https://godoc.org/github.com/ipfs/go-ipfs-blockstore?status.svg)](https://godoc.org/github.com/ipfs/go-ipfs-blockstore) -[![Build Status](https://travis-ci.org/ipfs/go-ipfs-blockstore.svg?branch=master)](https://travis-ci.org/ipfs/go-ipfs-blockstore) +[![Build Status](https://travis-ci.com/ipfs/go-ipfs-blockstore.svg?branch=master)](https://travis-ci.com/ipfs/go-ipfs-blockstore) > go-ipfs-blockstore implements a thin wrapper over a datastore, giving a clean interface for Getting and Putting block objects. @@ -36,8 +36,6 @@ import "github.com/ipfs/go-ipfs-blockstore" Check the [GoDoc documentation](https://godoc.org/github.com/ipfs/go-ipfs-blockstore) -This module uses [Gx](https://github.com/whyrusleeping/gx) to manage dependencies. You can use `make all` to build it with the `gx` dependencies. - ## Contribute PRs accepted. From da00d16bbdd0d855d54e375b7f4b7d317616fe31 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 14 Feb 2020 16:03:17 -0800 Subject: [PATCH 3176/3817] fix: don't hold the pin lock while updating pins fixes https://github.com/ipfs/go-ipfs/issues/6885 This commit was moved from ipfs/go-ipfs-pinner@f08a8a14c54c5d5d1579abceec052b15ecce15ce --- pinning/pinner/pin.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 7a3cabdf0..aa74c5185 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -526,7 +526,11 @@ func (p *pinner) Update(ctx context.Context, from, to cid.Cid, unpin bool) error return fmt.Errorf("'from' cid was not recursively pinned already") } + // Temporarily unlock while we fetch the differences. + p.lock.Unlock() err := dagutils.DiffEnumerate(ctx, p.dserv, from, to) + p.lock.Lock() + if err != nil { return err } From fdc57115401d5713beb37cf79e4940a86dca4210 Mon Sep 17 00:00:00 2001 From: Andrey Petrov Date: Mon, 24 Feb 2020 16:28:22 -0500 Subject: [PATCH 3177/3817] Add test to maintain Put contract of calling Has first This commit was moved from ipfs/go-ipfs-blockstore@9a3ae2aa220739e8c9dda067069dbd965ee293b0 --- blockstore/blockstore_test.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index a165652f4..28f98e14a 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -104,6 +104,38 @@ func TestPutThenGetSizeBlock(t *testing.T) { } } +type countHasDS struct { + ds.Datastore + hasCount int +} + +func (ds *countHasDS) Has(key ds.Key) (exists bool, err error) { + ds.hasCount += 1 + return ds.Datastore.Has(key) +} + +func TestPutUsesHas(t *testing.T) { + // Some datastores rely on the implementation detail that Put checks Has + // first, to avoid overriding existing objects' metadata. This test ensures + // that Blockstore continues to behave this way. + // Please ping https://github.com/ipfs/go-ipfs-blockstore/pull/47 if this + // behavior is being removed. + ds := &countHasDS{ + Datastore: ds.NewMapDatastore(), + } + bs := NewBlockstore(ds_sync.MutexWrap(ds)) + bl := blocks.NewBlock([]byte("some data")) + if err := bs.Put(bl); err != nil { + t.Fatal(err) + } + if err := bs.Put(bl); err != nil { + t.Fatal(err) + } + if ds.hasCount != 2 { + t.Errorf("Blockstore did not call Has before attempting Put, this breaks compatibility") + } +} + func TestHashOnRead(t *testing.T) { orginalDebug := u.Debug defer (func() { From 18e730c28ba8e34b653771c7dad2bb15e6684698 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 28 Feb 2020 08:07:15 -0500 Subject: [PATCH 3178/3817] Work with Multihashes directly (#21) This updates the filestore to work with raw multihashes instead of CIDs, aligning with the latest versions of go-ipfs-ds-help. Every block in the filestore should already be a raw multihash, however, since they were added as merkledag.RawNode, which is a Cidv1 of type Raw, and maps nicely to a raw multihash. Interfaces have been kept without change, but may be updated in the future to expose only Multihashes. This commit was moved from ipfs/go-filestore@c64f2798890d95a0c8e2641648a9454726fc9a73 --- filestore/filestore.go | 4 ++-- filestore/fsrefstore.go | 49 ++++++++++++++++++++++++----------------- filestore/util.go | 42 +++++++++++++++++++---------------- 3 files changed, 54 insertions(+), 41 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index be4d954be..a9c36c5d3 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -19,7 +19,7 @@ import ( logging "github.com/ipfs/go-log" ) -var log = logging.Logger("filestore") +var logger = logging.Logger("filestore") var ErrFilestoreNotEnabled = errors.New("filestore is not enabled, see https://git.io/vNItf") var ErrUrlstoreNotEnabled = errors.New("urlstore is not enabled") @@ -86,7 +86,7 @@ func (f *Filestore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // cant query leveldb concurrently b, err := f.fm.AllKeysChan(ctx) if err != nil { - log.Error("error querying filestore: ", err) + logger.Error("error querying filestore: ", err) return } diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 19927e0ef..bc183fc38 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -19,6 +19,7 @@ import ( blockstore "github.com/ipfs/go-ipfs-blockstore" dshelp "github.com/ipfs/go-ipfs-ds-help" posinfo "github.com/ipfs/go-ipfs-posinfo" + mh "github.com/multiformats/go-multihash" ) // FilestorePrefix identifies the key prefix for FileManager blocks. @@ -60,6 +61,8 @@ func NewFileManager(ds ds.Batching, root string) *FileManager { // AllKeysChan returns a channel from which to read the keys stored in // the FileManager. If the given context is cancelled the channel will be // closed. +// +// All CIDs returned are of type Raw. func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { q := dsq.Query{KeysOnly: true} @@ -78,14 +81,14 @@ func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { } k := ds.RawKey(v.Key) - c, err := dshelp.DsKeyToCid(k) + mhash, err := dshelp.DsKeyToMultihash(k) if err != nil { - log.Errorf("decoding cid from filestore: %s", err) + logger.Errorf("decoding cid from filestore: %s", err) continue } select { - case out <- c: + case out <- cid.NewCidV1(cid.Raw, mhash): case <-ctx.Done(): return } @@ -98,7 +101,7 @@ func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // DeleteBlock deletes the reference-block from the underlying // datastore. It does not touch the referenced data. func (f *FileManager) DeleteBlock(c cid.Cid) error { - err := f.ds.Delete(dshelp.CidToDsKey(c)) + err := f.ds.Delete(dshelp.MultihashToDsKey(c.Hash())) if err == ds.ErrNotFound { return blockstore.ErrNotFound } @@ -110,11 +113,11 @@ func (f *FileManager) DeleteBlock(c cid.Cid) error { // block from the datastore. The second step uses the stored // path and offsets to read the raw block data directly from disk. func (f *FileManager) Get(c cid.Cid) (blocks.Block, error) { - dobj, err := f.getDataObj(c) + dobj, err := f.getDataObj(c.Hash()) if err != nil { return nil, err } - out, err := f.readDataObj(c, dobj) + out, err := f.readDataObj(c.Hash(), dobj) if err != nil { return nil, err } @@ -127,22 +130,22 @@ func (f *FileManager) Get(c cid.Cid) (blocks.Block, error) { // This method may successfully return the size even if returning the block // would fail because the associated file is no longer available. func (f *FileManager) GetSize(c cid.Cid) (int, error) { - dobj, err := f.getDataObj(c) + dobj, err := f.getDataObj(c.Hash()) if err != nil { return -1, err } return int(dobj.GetSize_()), nil } -func (f *FileManager) readDataObj(c cid.Cid, d *pb.DataObj) ([]byte, error) { +func (f *FileManager) readDataObj(m mh.Multihash, d *pb.DataObj) ([]byte, error) { if IsURL(d.GetFilePath()) { - return f.readURLDataObj(c, d) + return f.readURLDataObj(m, d) } - return f.readFileDataObj(c, d) + return f.readFileDataObj(m, d) } -func (f *FileManager) getDataObj(c cid.Cid) (*pb.DataObj, error) { - o, err := f.ds.Get(dshelp.CidToDsKey(c)) +func (f *FileManager) getDataObj(m mh.Multihash) (*pb.DataObj, error) { + o, err := f.ds.Get(dshelp.MultihashToDsKey(m)) switch err { case ds.ErrNotFound: return nil, blockstore.ErrNotFound @@ -164,7 +167,7 @@ func unmarshalDataObj(data []byte) (*pb.DataObj, error) { return &dobj, nil } -func (f *FileManager) readFileDataObj(c cid.Cid, d *pb.DataObj) ([]byte, error) { +func (f *FileManager) readFileDataObj(m mh.Multihash, d *pb.DataObj) ([]byte, error) { if !f.AllowFiles { return nil, ErrFilestoreNotEnabled } @@ -193,12 +196,15 @@ func (f *FileManager) readFileDataObj(c cid.Cid, d *pb.DataObj) ([]byte, error) return nil, &CorruptReferenceError{StatusFileError, err} } - outcid, err := c.Prefix().Sum(outbuf) + // Work with CIDs for this, as they are a nice wrapper and things + // will not break if multihashes underlying types change. + origCid := cid.NewCidV1(cid.Raw, m) + outcid, err := origCid.Prefix().Sum(outbuf) if err != nil { return nil, err } - if !c.Equals(outcid) { + if !origCid.Equals(outcid) { return nil, &CorruptReferenceError{StatusFileChanged, fmt.Errorf("data in file did not match. %s offset %d", d.GetFilePath(), d.GetOffset())} } @@ -207,7 +213,7 @@ func (f *FileManager) readFileDataObj(c cid.Cid, d *pb.DataObj) ([]byte, error) } // reads and verifies the block from URL -func (f *FileManager) readURLDataObj(c cid.Cid, d *pb.DataObj) ([]byte, error) { +func (f *FileManager) readURLDataObj(m mh.Multihash, d *pb.DataObj) ([]byte, error) { if !f.AllowUrls { return nil, ErrUrlstoreNotEnabled } @@ -237,12 +243,15 @@ func (f *FileManager) readURLDataObj(c cid.Cid, d *pb.DataObj) ([]byte, error) { } res.Body.Close() - outcid, err := c.Prefix().Sum(outbuf) + // Work with CIDs for this, as they are a nice wrapper and things + // will not break if multihashes underlying types change. + origCid := cid.NewCidV1(cid.Raw, m) + outcid, err := origCid.Prefix().Sum(outbuf) if err != nil { return nil, err } - if !c.Equals(outcid) { + if !origCid.Equals(outcid) { return nil, &CorruptReferenceError{StatusFileChanged, fmt.Errorf("data in file did not match. %s offset %d", d.GetFilePath(), d.GetOffset())} } @@ -255,7 +264,7 @@ func (f *FileManager) readURLDataObj(c cid.Cid, d *pb.DataObj) ([]byte, error) { func (f *FileManager) Has(c cid.Cid) (bool, error) { // NOTE: interesting thing to consider. Has doesnt validate the data. // So the data on disk could be invalid, and we could think we have it. - dsk := dshelp.CidToDsKey(c) + dsk := dshelp.MultihashToDsKey(c.Hash()) return f.ds.Has(dsk) } @@ -300,7 +309,7 @@ func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { return err } - return to.Put(dshelp.CidToDsKey(b.Cid()), data) + return to.Put(dshelp.MultihashToDsKey(b.Cid().Hash()), data) } // PutMany is like Put() but takes a slice of blocks instead, diff --git a/filestore/util.go b/filestore/util.go index bfb240c55..dc860f735 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -11,6 +11,7 @@ import ( dsq "github.com/ipfs/go-datastore/query" blockstore "github.com/ipfs/go-ipfs-blockstore" dshelp "github.com/ipfs/go-ipfs-ds-help" + mh "github.com/multiformats/go-multihash" ) // Status is used to identify the state of the block data referenced @@ -86,7 +87,7 @@ func (r *ListRes) FormatLong(enc func(cid.Cid) string) string { // List does not verify that the reference is valid or whether the // raw data is accesible. See Verify(). func List(fs *Filestore, key cid.Cid) *ListRes { - return list(fs, false, key) + return list(fs, false, key.Hash()) } // ListAll returns a function as an iterator which, once invoked, returns @@ -105,7 +106,7 @@ func ListAll(fs *Filestore, fileOrder bool) (func() *ListRes, error) { // Verify makes sure that the reference is valid and the block data can be // read. func Verify(fs *Filestore, key cid.Cid) *ListRes { - return list(fs, true, key) + return list(fs, true, key.Hash()) } // VerifyAll returns a function as an iterator which, once invoked, @@ -119,7 +120,7 @@ func VerifyAll(fs *Filestore, fileOrder bool) (func() *ListRes, error) { return listAll(fs, true) } -func list(fs *Filestore, verify bool, key cid.Cid) *ListRes { +func list(fs *Filestore, verify bool, key mh.Multihash) *ListRes { dobj, err := fs.fm.getDataObj(key) if err != nil { return mkListRes(key, nil, err) @@ -138,34 +139,34 @@ func listAll(fs *Filestore, verify bool) (func() *ListRes, error) { } return func() *ListRes { - cid, dobj, err := next(qr) + mhash, dobj, err := next(qr) if dobj == nil && err == nil { return nil } else if err == nil && verify { - _, err = fs.fm.readDataObj(cid, dobj) + _, err = fs.fm.readDataObj(mhash, dobj) } - return mkListRes(cid, dobj, err) + return mkListRes(mhash, dobj, err) }, nil } -func next(qr dsq.Results) (cid.Cid, *pb.DataObj, error) { +func next(qr dsq.Results) (mh.Multihash, *pb.DataObj, error) { v, ok := qr.NextSync() if !ok { - return cid.Cid{}, nil, nil + return nil, nil, nil } k := ds.RawKey(v.Key) - c, err := dshelp.DsKeyToCid(k) + mhash, err := dshelp.DsKeyToMultihash(k) if err != nil { - return cid.Cid{}, nil, fmt.Errorf("decoding cid from filestore: %s", err) + return nil, nil, fmt.Errorf("decoding multihash from filestore: %s", err) } dobj, err := unmarshalDataObj(v.Value) if err != nil { - return c, nil, err + return mhash, nil, err } - return c, dobj, nil + return mhash, dobj, nil } func listAllFileOrder(fs *Filestore, verify bool) (func() *ListRes, error) { @@ -206,12 +207,12 @@ func listAllFileOrder(fs *Filestore, verify bool) (func() *ListRes, error) { } v := entries[i] i++ - // attempt to convert the datastore key to a CID, + // attempt to convert the datastore key to a Multihash, // store the error but don't use it yet - cid, keyErr := dshelp.DsKeyToCid(ds.RawKey(v.dsKey)) + mhash, keyErr := dshelp.DsKeyToMultihash(ds.RawKey(v.dsKey)) // first if they listRes already had an error return that error if v.err != nil { - return mkListRes(cid, nil, v.err) + return mkListRes(mhash, nil, v.err) } // now reconstruct the DataObj dobj := pb.DataObj{ @@ -222,14 +223,14 @@ func listAllFileOrder(fs *Filestore, verify bool) (func() *ListRes, error) { // now if we could not convert the datastore key return that // error if keyErr != nil { - return mkListRes(cid, &dobj, keyErr) + return mkListRes(mhash, &dobj, keyErr) } // finally verify the dataobj if requested var err error if verify { - _, err = fs.fm.readDataObj(cid, &dobj) + _, err = fs.fm.readDataObj(mhash, &dobj) } - return mkListRes(cid, &dobj, err) + return mkListRes(mhash, &dobj, err) }, nil } @@ -255,7 +256,7 @@ func (l listEntries) Less(i, j int) bool { return l[i].filePath < l[j].filePath } -func mkListRes(c cid.Cid, d *pb.DataObj, err error) *ListRes { +func mkListRes(m mh.Multihash, d *pb.DataObj, err error) *ListRes { status := StatusOk errorMsg := "" if err != nil { @@ -268,6 +269,9 @@ func mkListRes(c cid.Cid, d *pb.DataObj, err error) *ListRes { } errorMsg = err.Error() } + + c := cid.NewCidV1(cid.Raw, m) + if d == nil { return &ListRes{ Status: status, From e84cc0569818ce3b28ff7ea27b5077fe17232163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Fri, 29 Nov 2019 22:33:13 +0100 Subject: [PATCH 3179/3817] pin: add a IsPinned method This commit was moved from ipfs/interface-go-ipfs-core@c82db2ef2270a228185aaba6b08ecd7157a7ebee --- coreiface/options/pin.go | 184 +++++++++++++++++++++++++++++++-------- coreiface/pin.go | 4 + coreiface/tests/pin.go | 95 ++++++++++++++++++-- 3 files changed, 237 insertions(+), 46 deletions(-) diff --git a/coreiface/options/pin.go b/coreiface/options/pin.go index 6b211bb73..231f0d11a 100644 --- a/coreiface/options/pin.go +++ b/coreiface/options/pin.go @@ -1,13 +1,23 @@ package options +import "fmt" + type PinAddSettings struct { Recursive bool } +type TypeSettings struct { + Type string +} + type PinLsSettings struct { Type string } +type PinIsPinnedSettings struct { + WithType string +} + // PinRmSettings represents the settings of pin rm command type PinRmSettings struct { Recursive bool @@ -17,13 +27,19 @@ type PinUpdateSettings struct { Unpin bool } +// PinAddOption pin add option func type PinAddOption func(*PinAddSettings) error +// PinLsOption pin ls option func +type PinLsOption func(*PinLsSettings) error + +// PinIsPinnedOption pin isPinned option func +type PinIsPinnedOption func(*PinIsPinnedSettings) error + // PinRmOption pin rm option func type PinRmOption func(*PinRmSettings) error -// PinLsOption pin ls option func -type PinLsOption func(*PinLsSettings) error +// PinUpdateOption pin update option func type PinUpdateOption func(*PinUpdateSettings) error func PinAddOptions(opts ...PinAddOption) (*PinAddSettings, error) { @@ -41,14 +57,14 @@ func PinAddOptions(opts ...PinAddOption) (*PinAddSettings, error) { return options, nil } -// PinRmOptions pin rm options -func PinRmOptions(opts ...PinRmOption) (*PinRmSettings, error) { - options := &PinRmSettings{ - Recursive: true, +func PinLsOptions(opts ...PinLsOption) (*PinLsSettings, error) { + options := &PinLsSettings{ + Type: "all", } for _, opt := range opts { - if err := opt(options); err != nil { + err := opt(options) + if err != nil { return nil, err } } @@ -56,9 +72,9 @@ func PinRmOptions(opts ...PinRmOption) (*PinRmSettings, error) { return options, nil } -func PinLsOptions(opts ...PinLsOption) (*PinLsSettings, error) { - options := &PinLsSettings{ - Type: "all", +func PinIsPinnedOptions(opts ...PinIsPinnedOption) (*PinIsPinnedSettings, error) { + options := &PinIsPinnedSettings{ + WithType: "all", } for _, opt := range opts { @@ -71,6 +87,21 @@ func PinLsOptions(opts ...PinLsOption) (*PinLsSettings, error) { return options, nil } +// PinRmOptions pin rm options +func PinRmOptions(opts ...PinRmOption) (*PinRmSettings, error) { + options := &PinRmSettings{ + Recursive: true, + } + + for _, opt := range opts { + if err := opt(options); err != nil { + return nil, err + } + } + + return options, nil +} + func PinUpdateOptions(opts ...PinUpdateOption) (*PinUpdateSettings, error) { options := &PinUpdateSettings{ Unpin: true, @@ -86,36 +117,131 @@ func PinUpdateOptions(opts ...PinUpdateOption) (*PinUpdateSettings, error) { return options, nil } -type pinType struct{} - type pinOpts struct { - Type pinType + Ls pinLsOpts + IsPinned pinIsPinnedOpts } var Pin pinOpts +type pinLsOpts struct{} + // All is an option for Pin.Ls which will make it return all pins. It is // the default -func (pinType) All() PinLsOption { - return Pin.pinType("all") +func (pinLsOpts) All() PinLsOption { + return Pin.Ls.pinType("all") } // Recursive is an option for Pin.Ls which will make it only return recursive // pins -func (pinType) Recursive() PinLsOption { - return Pin.pinType("recursive") +func (pinLsOpts) Recursive() PinLsOption { + return Pin.Ls.pinType("recursive") } // Direct is an option for Pin.Ls which will make it only return direct (non // recursive) pins -func (pinType) Direct() PinLsOption { - return Pin.pinType("direct") +func (pinLsOpts) Direct() PinLsOption { + return Pin.Ls.pinType("direct") } // Indirect is an option for Pin.Ls which will make it only return indirect pins // (objects referenced by other recursively pinned objects) -func (pinType) Indirect() PinLsOption { - return Pin.pinType("indirect") +func (pinLsOpts) Indirect() PinLsOption { + return Pin.Ls.pinType("indirect") +} + +// Type is an option for Pin.Ls which will make it only return pins of the given +// type. +// +// Supported values: +// * "direct" - directly pinned objects +// * "recursive" - roots of recursive pins +// * "indirect" - indirectly pinned objects (referenced by recursively pinned +// objects) +// * "all" - all pinned objects (default) +func (pinLsOpts) Type(typeStr string) (PinLsOption, error) { + switch typeStr { + case "all", "direct", "indirect", "recursive": + return Pin.Ls.pinType(typeStr), nil + default: + return nil, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", typeStr) + } +} + +// pinType is an option for Pin.Ls which allows to specify which pin types should +// be returned +// +// Supported values: +// * "direct" - directly pinned objects +// * "recursive" - roots of recursive pins +// * "indirect" - indirectly pinned objects (referenced by recursively pinned +// objects) +// * "all" - all pinned objects (default) +func (pinLsOpts) pinType(t string) PinLsOption { + return func(settings *PinLsSettings) error { + settings.Type = t + return nil + } +} + +type pinIsPinnedOpts struct{} + +// All is an option for Pin.IsPinned which will make it search in all type of pins. +// It is the default +func (pinIsPinnedOpts) All() PinIsPinnedOption { + return Pin.IsPinned.pinType("all") +} + +// Recursive is an option for Pin.IsPinned which will make it only search in +// recursive pins +func (pinIsPinnedOpts) Recursive() PinIsPinnedOption { + return Pin.IsPinned.pinType("recursive") +} + +// Direct is an option for Pin.IsPinned which will make it only search in direct +// (non recursive) pins +func (pinIsPinnedOpts) Direct() PinIsPinnedOption { + return Pin.IsPinned.pinType("direct") +} + +// Indirect is an option for Pin.IsPinned which will make it only search indirect +// pins (objects referenced by other recursively pinned objects) +func (pinIsPinnedOpts) Indirect() PinIsPinnedOption { + return Pin.IsPinned.pinType("indirect") +} + +// Type is an option for Pin.IsPinned which will make it only search pins of the given +// type. +// +// Supported values: +// * "direct" - directly pinned objects +// * "recursive" - roots of recursive pins +// * "indirect" - indirectly pinned objects (referenced by recursively pinned +// objects) +// * "all" - all pinned objects (default) +func (pinIsPinnedOpts) Type(typeStr string) (PinIsPinnedOption, error) { + switch typeStr { + case "all", "direct", "indirect", "recursive": + return Pin.IsPinned.pinType(typeStr), nil + default: + return nil, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", typeStr) + } +} + +// pinType is an option for Pin.IsPinned which allows to specify which pin type the given +// pin is expected to be, speeding up the research. +// +// Supported values: +// * "direct" - directly pinned objects +// * "recursive" - roots of recursive pins +// * "indirect" - indirectly pinned objects (referenced by recursively pinned +// objects) +// * "all" - all pinned objects (default) +func (pinIsPinnedOpts) pinType(t string) PinIsPinnedOption { + return func(settings *PinIsPinnedSettings) error { + settings.WithType = t + return nil + } } // Recursive is an option for Pin.Add which specifies whether to pin an entire @@ -137,22 +263,6 @@ func (pinOpts) RmRecursive(recursive bool) PinRmOption { } } -// Type is an option for Pin.Ls which allows to specify which pin types should -// be returned -// -// Supported values: -// * "direct" - directly pinned objects -// * "recursive" - roots of recursive pins -// * "indirect" - indirectly pinned objects (referenced by recursively pinned -// objects) -// * "all" - all pinned objects (default) -func (pinOpts) pinType(t string) PinLsOption { - return func(settings *PinLsSettings) error { - settings.Type = t - return nil - } -} - // Unpin is an option for Pin.Update which specifies whether to remove the old pin. // Default is true. func (pinOpts) Unpin(unpin bool) PinUpdateOption { diff --git a/coreiface/pin.go b/coreiface/pin.go index 27f9355d3..4c1788c68 100644 --- a/coreiface/pin.go +++ b/coreiface/pin.go @@ -46,6 +46,10 @@ type PinAPI interface { // Ls returns list of pinned objects on this node Ls(context.Context, ...options.PinLsOption) (<-chan Pin, error) + // IsPinned returns whether or not the given cid is pinned + // and an explanation of why its pinned + IsPinned(context.Context, path.Path, ...options.PinIsPinnedOption) (string, bool, error) + // Rm removes pin for object specified by the path Rm(context.Context, path.Path, ...options.PinRmOption) error diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index 58e812084..e16d6460b 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -28,6 +28,7 @@ func (tp *TestSuite) TestPin(t *testing.T) { t.Run("TestPinRecursive", tp.TestPinRecursive) t.Run("TestPinLsIndirect", tp.TestPinLsIndirect) t.Run("TestPinLsPrecedence", tp.TestPinLsPrecedence) + t.Run("TestPinIsPinned", tp.TestPinIsPinned) } func (tp *TestSuite) TestPinAdd(t *testing.T) { @@ -84,6 +85,8 @@ func (tp *TestSuite) TestPinSimple(t *testing.T) { t.Error("unexpected pin type") } + assertIsPinned(t, ctx, api, p, "recursive") + err = api.Pin().Rm(ctx, p) if err != nil { t.Fatal(err) @@ -150,7 +153,7 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) { t.Errorf("unexpected pin list len: %d", len(list)) } - list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Type.Direct())) + list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Direct())) if err != nil { t.Fatal(err) } @@ -163,7 +166,7 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) { t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.IpfsPath(nd3.Cid()).String()) } - list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Type.Recursive())) + list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Recursive())) if err != nil { t.Fatal(err) } @@ -176,7 +179,7 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) { t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.IpldPath(nd2.Cid()).String()) } - list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Type.Indirect())) + list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Indirect())) if err != nil { t.Fatal(err) } @@ -360,6 +363,39 @@ func (tp *TestSuite) TestPinLsPrecedenceRecursiveDirect(t *testing.T) { assertPinTypes(t, ctx, api, []cidContainer{grandparent, parent}, []cidContainer{}, []cidContainer{leaf}) } +func (tp *TestSuite) TestPinIsPinned(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(ctx) + if err != nil { + t.Fatal(err) + } + + leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "foofoo") + + assertNotPinned(t, ctx, api, path.IpldPath(grandparent.Cid())) + assertNotPinned(t, ctx, api, path.IpldPath(parent.Cid())) + assertNotPinned(t, ctx, api, path.IpldPath(leaf.Cid())) + + err = api.Pin().Add(ctx, path.IpldPath(parent.Cid()), opt.Pin.Recursive(true)) + if err != nil { + t.Fatal(err) + } + + assertNotPinned(t, ctx, api, path.IpldPath(grandparent.Cid())) + assertIsPinned(t, ctx, api, path.IpldPath(parent.Cid()), "recursive") + assertIsPinned(t, ctx, api, path.IpldPath(leaf.Cid()), "indirect") + + err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid()), opt.Pin.Recursive(false)) + if err != nil { + t.Fatal(err) + } + + assertIsPinned(t, ctx, api, path.IpldPath(grandparent.Cid()), "direct") + assertIsPinned(t, ctx, api, path.IpldPath(parent.Cid()), "recursive") + assertIsPinned(t, ctx, api, path.IpldPath(leaf.Cid()), "indirect") +} + type cidContainer interface { Cid() cid.Cid } @@ -390,21 +426,21 @@ func getThreeChainedNodes(t *testing.T, ctx context.Context, api iface.CoreAPI, func assertPinTypes(t *testing.T, ctx context.Context, api iface.CoreAPI, recusive, direct, indirect []cidContainer) { assertPinLsAllConsistency(t, ctx, api) - list, err := accPins(api.Pin().Ls(ctx, opt.Pin.Type.Recursive())) + list, err := accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Recursive())) if err != nil { t.Fatal(err) } assertPinCids(t, list, recusive...) - list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Type.Direct())) + list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Direct())) if err != nil { t.Fatal(err) } assertPinCids(t, list, direct...) - list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Type.Indirect())) + list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Indirect())) if err != nil { t.Fatal(err) } @@ -466,9 +502,9 @@ func assertPinLsAllConsistency(t *testing.T, ctx context.Context, api iface.Core all, recursive, direct, indirect := cid.NewSet(), cid.NewSet(), cid.NewSet(), cid.NewSet() typeMap := map[string]*pinTypeProps{ - "recursive": {recursive, opt.Pin.Type.Recursive()}, - "direct": {direct, opt.Pin.Type.Direct()}, - "indirect": {indirect, opt.Pin.Type.Indirect()}, + "recursive": {recursive, opt.Pin.Ls.Recursive()}, + "direct": {direct, opt.Pin.Ls.Direct()}, + "indirect": {indirect, opt.Pin.Ls.Indirect()}, } for _, p := range allPins { @@ -506,6 +542,47 @@ func assertPinLsAllConsistency(t *testing.T, ctx context.Context, api iface.Core } } +func assertIsPinned(t *testing.T, ctx context.Context, api iface.CoreAPI, p path.Path, typeStr string) { + t.Helper() + withType, err := opt.Pin.IsPinned.Type(typeStr) + if err != nil { + panic("unhandled pin type") + } + + whyPinned, pinned, err := api.Pin().IsPinned(ctx, p, withType) + if err != nil { + t.Fatal(err) + } + + if !pinned { + t.Fatalf("%s expected to be pinned with type %s", p, typeStr) + } + + switch typeStr { + case "recursive", "direct": + if typeStr != whyPinned { + t.Fatalf("reason for pinning expected to be %s for %s, got %s", typeStr, p, whyPinned) + } + case "indirect": + if whyPinned == "" { + t.Fatalf("expected to have a pin reason for %s", p) + } + } +} + +func assertNotPinned(t *testing.T, ctx context.Context, api iface.CoreAPI, p path.Path) { + t.Helper() + + _, pinned, err := api.Pin().IsPinned(ctx, p) + if err != nil { + t.Fatal(err) + } + + if pinned { + t.Fatalf("%s expected to not be pinned", p) + } +} + func accPins(pins <-chan iface.Pin, err error) ([]iface.Pin, error) { if err != nil { return nil, err From 8e9335f4dff739dffb7b0d8a7a7ba4fb60b4bb48 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 21 Aug 2019 19:25:07 -0700 Subject: [PATCH 3180/3817] resolve: kill off buggy resolve function This resolve function assumed that all paths were of the same type (ipfs, ipld, etc.). The CoreAPI does a much better job. This commit was moved from ipfs/go-namesys@569472a15f20261ebd82e4f910483bb19ec4b693 --- namesys/resolve/pathresolver_test.go | 32 ---------------------------- namesys/resolve/resolve.go | 15 ------------- 2 files changed, 47 deletions(-) delete mode 100644 namesys/resolve/pathresolver_test.go diff --git a/namesys/resolve/pathresolver_test.go b/namesys/resolve/pathresolver_test.go deleted file mode 100644 index 3d9c04fa2..000000000 --- a/namesys/resolve/pathresolver_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package resolve_test - -import ( - "testing" - - coremock "github.com/ipfs/go-ipfs/core/mock" - "github.com/ipfs/go-ipfs/namesys/resolve" - - path "github.com/ipfs/go-path" -) - -func TestResolveNoComponents(t *testing.T) { - n, err := coremock.NewMockNode() - if n == nil || err != nil { - t.Fatal("Should have constructed a mock node", err) - } - - _, err = resolve.Resolve(n.Context(), n.Namesys, n.Resolver, path.Path("/ipns/")) - if err.Error() != "invalid path \"/ipns/\": ipns path missing IPNS ID" { - t.Error("Should error with no components (/ipns/).", err) - } - - _, err = resolve.Resolve(n.Context(), n.Namesys, n.Resolver, path.Path("/ipfs/")) - if err.Error() != "invalid path \"/ipfs/\": not enough path components" { - t.Error("Should error with no components (/ipfs/).", err) - } - - _, err = resolve.Resolve(n.Context(), n.Namesys, n.Resolver, path.Path("/../..")) - if err.Error() != "invalid path \"/../..\": unknown namespace \"..\"" { - t.Error("Should error with invalid path.", err) - } -} diff --git a/namesys/resolve/resolve.go b/namesys/resolve/resolve.go index 5b5dc515e..f838a6611 100644 --- a/namesys/resolve/resolve.go +++ b/namesys/resolve/resolve.go @@ -6,9 +6,7 @@ import ( "fmt" "strings" - "github.com/ipfs/go-ipld-format" "github.com/ipfs/go-path" - "github.com/ipfs/go-path/resolver" "github.com/ipfs/go-ipfs/namesys" ) @@ -52,16 +50,3 @@ func ResolveIPNS(ctx context.Context, nsys namesys.NameSystem, p path.Path) (pat } return p, nil } - -// Resolve resolves the given path by parsing out protocol-specific -// entries (e.g. /ipns/) and then going through the /ipfs/ -// entries and returning the final node. -func Resolve(ctx context.Context, nsys namesys.NameSystem, r *resolver.Resolver, p path.Path) (format.Node, error) { - p, err := ResolveIPNS(ctx, nsys, p) - if err != nil { - return nil, err - } - - // ok, we have an IPFS path now (or what we'll treat as one) - return r.ResolvePath(ctx, p) -} From bbaa90441b1edab9cbd90d2c76b3213ff31dd63e Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Tue, 3 Mar 2020 15:34:20 +0000 Subject: [PATCH 3181/3817] test: add Directory.ListNames test This commit was moved from ipfs/go-mfs@94b7a85ab56f7ad7d02c74b14b01f55c6b7a4dbb --- mfs/mfs_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 81c63f95c..3c08fd9a6 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -620,6 +620,48 @@ func TestMfsFile(t *testing.T) { } } +func TestMfsDirListNames(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ds, rt := setupRoot(ctx, t) + + rootdir := rt.GetDirectory() + + rand.Seed(time.Now().UTC().UnixNano()) + + total := rand.Intn(10) + 1 + fNames := make([]string, 0, total) + + for i := 0; i < total; i++ { + fn := randomName() + fNames = append(fNames, fn) + nd := getRandFile(t, ds, rand.Int63n(1000)+1) + err := rootdir.AddChild(fn, nd) + if err != nil { + t.Fatal(err) + } + } + + list, err := rootdir.ListNames(ctx) + + if err != nil { + t.Fatal(err) + } + + for _, lName := range list { + found := false + for _, fName := range fNames { + if lName == fName { + found = true + break + } + } + if !found { + t.Fatal(lName + " not found in directory listing") + } + } +} + func randomWalk(d *Directory, n int) (*Directory, error) { for i := 0; i < n; i++ { dirents, err := d.List(context.Background()) From f9b70fe85e23ba07c78a5bff4f825c8f133243aa Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 4 Mar 2020 10:44:30 -0800 Subject: [PATCH 3182/3817] migrate to IPLD org This commit was moved from ipld/go-car@da36c369c24a63aedea42e1fe1f8764d28a4cd24 --- ipld/car/README.md | 10 +++++----- ipld/car/car.go | 2 +- ipld/car/car/main.go | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ipld/car/README.md b/ipld/car/README.md index ffd258c9e..1142a2293 100644 --- a/ipld/car/README.md +++ b/ipld/car/README.md @@ -1,11 +1,11 @@ go-car (go!) ================== -[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) -[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) -[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) -[![Coverage Status](https://codecov.io/gh/ipfs/go-car/branch/master/graph/badge.svg)](https://codecov.io/gh/ipfs/go-car/branch/master) -[![Travis CI](https://travis-ci.org/ipfs/go-car.svg?branch=master)](https://travis-ci.org/ipfs/go-car) +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](https://protocol.ai) +[![](https://img.shields.io/badge/project-ipld-orange.svg?style=flat-square)](https://github.com/ipld/ipld) +[![](https://img.shields.io/badge/freenode-%23ipld-orange.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipld) +[![Coverage Status](https://codecov.io/gh/ipld/go-car/branch/master/graph/badge.svg)](https://codecov.io/gh/ipld/go-car/branch/master) +[![Travis CI](https://travis-ci.org/ipld/go-car.svg?branch=master)](https://travis-ci.org/ipld/go-car) > go-car is a simple way of packing a merkledag into a single file diff --git a/ipld/car/car.go b/ipld/car/car.go index 515fc0af7..326f94b2a 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -12,7 +12,7 @@ import ( format "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" - util "github.com/ipfs/go-car/util" + util "github.com/ipld/go-car/util" ) func init() { diff --git a/ipld/car/car/main.go b/ipld/car/car/main.go index 97127ca7c..c88459ade 100644 --- a/ipld/car/car/main.go +++ b/ipld/car/car/main.go @@ -7,7 +7,7 @@ import ( "io" "os" - "github.com/ipfs/go-car" + "github.com/ipld/go-car" cli "github.com/urfave/cli" ) From fa4fac72a9e95146b838ffdff1a6f4ceec20b8ff Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 4 Mar 2020 10:59:10 -0800 Subject: [PATCH 3183/3817] fix: don't use private types in public functions This commit was moved from ipld/go-car@d192da14125f2cd1c153b97ea2cb0d2d8bbcdcfd --- ipld/car/car.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index 326f94b2a..d13f2cbce 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -105,12 +105,12 @@ func (cw *carWriter) writeNode(ctx context.Context, nd format.Node) error { return util.LdWrite(cw.w, nd.Cid().Bytes(), nd.RawData()) } -type carReader struct { +type CarReader struct { br *bufio.Reader Header *CarHeader } -func NewCarReader(r io.Reader) (*carReader, error) { +func NewCarReader(r io.Reader) (*CarReader, error) { br := bufio.NewReader(r) ch, err := ReadHeader(br) if err != nil { @@ -125,13 +125,13 @@ func NewCarReader(r io.Reader) (*carReader, error) { return nil, fmt.Errorf("invalid car version: %d", ch.Version) } - return &carReader{ + return &CarReader{ br: br, Header: ch, }, nil } -func (cr *carReader) Next() (blocks.Block, error) { +func (cr *CarReader) Next() (blocks.Block, error) { c, data, err := util.ReadNode(cr.br) if err != nil { return nil, err @@ -166,7 +166,7 @@ func LoadCar(s Store, r io.Reader) (*CarHeader, error) { return loadCarSlow(s, cr) } -func loadCarFast(s batchStore, cr *carReader) (*CarHeader, error) { +func loadCarFast(s batchStore, cr *CarReader) (*CarHeader, error) { var buf []blocks.Block for { blk, err := cr.Next() @@ -202,7 +202,7 @@ func loadCarFast(s batchStore, cr *carReader) (*CarHeader, error) { return cr.Header, nil } -func loadCarSlow(s Store, cr *carReader) (*CarHeader, error) { +func loadCarSlow(s Store, cr *CarReader) (*CarHeader, error) { for { blk, err := cr.Next() From ccad8106a9084e9fc17f08f326b41a67ddd1fb7a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 4 Mar 2020 10:59:31 -0800 Subject: [PATCH 3184/3817] chore: remove dead code This commit was moved from ipld/go-car@7ff5726c9e54af3067279729ef908265709503c8 --- ipld/car/car.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index d13f2cbce..f98779e9b 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -192,14 +192,6 @@ func loadCarFast(s batchStore, cr *CarReader) (*CarHeader, error) { buf = buf[:0] } } - - if len(buf) > 0 { - if err := s.PutMany(buf); err != nil { - return nil, err - } - } - - return cr.Header, nil } func loadCarSlow(s Store, cr *CarReader) (*CarHeader, error) { From 4abdfc0609e6f64a624769494c85c7ba95dcc4b8 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Mon, 2 Dec 2019 18:18:55 -0800 Subject: [PATCH 3185/3817] feat(car): add selector support Add a mechanism for doing CAR files that write with selectors fix #14 This commit was moved from ipld/go-car@c487ec993500fb7f7e2d5288bc4530332e5568ea --- ipld/car/car.go | 87 ++++++++++++++++++++++++++++++++++++++++++-- ipld/car/car_test.go | 76 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+), 4 deletions(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index f98779e9b..2a7880d2f 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -2,15 +2,24 @@ package car import ( "bufio" + "bytes" "context" + "errors" "fmt" "io" + ipldfree "github.com/ipld/go-ipld-prime/impl/free" + "github.com/ipld/go-ipld-prime/traversal" + blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" format "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" + "github.com/ipld/go-ipld-prime" + dagpb "github.com/ipld/go-ipld-prime-proto" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + "github.com/ipld/go-ipld-prime/traversal/selector" util "github.com/ipld/go-car/util" ) @@ -23,6 +32,15 @@ type Store interface { Put(blocks.Block) error } +type ReadStore interface { + Get(cid.Cid) (blocks.Block, error) +} + +type CarDag struct { + Root cid.Cid + Selector selector.Selector +} + type CarHeader struct { Roots []cid.Cid Version uint64 @@ -41,17 +59,18 @@ func WriteCar(ctx context.Context, ds format.DAGService, roots []cid.Cid, w io.W } func WriteCarWithWalker(ctx context.Context, ds format.DAGService, roots []cid.Cid, w io.Writer, walk WalkFunc) error { - cw := &carWriter{ds: ds, w: w, walk: walk} + h := &CarHeader{ Roots: roots, Version: 1, } - if err := cw.WriteHeader(h); err != nil { + if err := WriteHeader(h, w); err != nil { return fmt.Errorf("failed to write car header: %s", err) } + cw := &carWriter{ds: ds, w: w, walk: walk} seen := cid.NewSet() for _, r := range roots { if err := dag.Walk(ctx, cw.enumGetLinks, r, seen.Visit); err != nil { @@ -65,6 +84,66 @@ func DefaultWalkFunc(nd format.Node) ([]*format.Link, error) { return nd.Links(), nil } +func WriteSelectiveCar(ctx context.Context, store ReadStore, dags []CarDag, w io.Writer) error { + + roots := make([]cid.Cid, 0, len(dags)) + for _, carDag := range dags { + roots = append(roots, carDag.Root) + } + + h := &CarHeader{ + Roots: roots, + Version: 1, + } + + if err := WriteHeader(h, w); err != nil { + return fmt.Errorf("failed to write car header: %s", err) + } + + var loader ipld.Loader = func(lnk ipld.Link, ctx ipld.LinkContext) (io.Reader, error) { + cl, ok := lnk.(cidlink.Link) + if !ok { + return nil, errors.New("Incorrect Link Type") + } + c := cl.Cid + fmt.Println(c) + blk, err := store.Get(c) + if err != nil { + return nil, err + } + raw := blk.RawData() + err = util.LdWrite(w, c.Bytes(), raw) + if err != nil { + return nil, err + } + return bytes.NewReader(raw), nil + } + + nbc := dagpb.AddDagPBSupportToChooser(func(ipld.Link, ipld.LinkContext) ipld.NodeBuilder { + return ipldfree.NodeBuilder() + }) + + for _, carDag := range dags { + lnk := cidlink.Link{Cid: carDag.Root} + nb := nbc(lnk, ipld.LinkContext{}) + nd, err := lnk.Load(ctx, ipld.LinkContext{}, nb, loader) + if err != nil { + return err + } + err = traversal.Progress{ + Cfg: &traversal.Config{ + Ctx: ctx, + LinkLoader: loader, + LinkNodeBuilderChooser: nbc, + }, + }.WalkAdv(nd, carDag.Selector, func(traversal.Progress, ipld.Node, traversal.VisitReason) error { return nil }) + if err != nil { + return err + } + } + return nil +} + func ReadHeader(br *bufio.Reader) (*CarHeader, error) { hb, err := util.LdRead(br) if err != nil { @@ -79,13 +158,13 @@ func ReadHeader(br *bufio.Reader) (*CarHeader, error) { return &ch, nil } -func (cw *carWriter) WriteHeader(h *CarHeader) error { +func WriteHeader(h *CarHeader, w io.Writer) error { hb, err := cbor.DumpObject(h) if err != nil { return err } - return util.LdWrite(cw.w, hb) + return util.LdWrite(w, hb) } func (cw *carWriter) enumGetLinks(ctx context.Context, c cid.Cid) ([]*format.Link, error) { diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 01288fea8..98288e9cb 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -9,6 +9,9 @@ import ( format "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" dstest "github.com/ipfs/go-merkledag/test" + ipldfree "github.com/ipld/go-ipld-prime/impl/free" + "github.com/ipld/go-ipld-prime/traversal/selector" + "github.com/ipld/go-ipld-prime/traversal/selector/builder" ) func assertAddNodes(t *testing.T, ds format.DAGService, nds ...format.Node) { @@ -69,3 +72,76 @@ func TestRoundtrip(t *testing.T) { } } } + +func TestRoundtripSelective(t *testing.T) { + sourceBserv := dstest.Bserv() + sourceBs := sourceBserv.Blockstore() + dserv := dag.NewDAGService(sourceBserv) + a := dag.NewRawNode([]byte("aaaa")) + b := dag.NewRawNode([]byte("bbbb")) + c := dag.NewRawNode([]byte("cccc")) + + nd1 := &dag.ProtoNode{} + nd1.AddNodeLink("cat", a) + + nd2 := &dag.ProtoNode{} + nd2.AddNodeLink("first", nd1) + nd2.AddNodeLink("dog", b) + + nd3 := &dag.ProtoNode{} + nd3.AddNodeLink("second", nd2) + nd3.AddNodeLink("bear", c) + + assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) + + buf := new(bytes.Buffer) + ssb := builder.NewSelectorSpecBuilder(ipldfree.NodeBuilder()) + selector, err := ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) { + efsb.Insert("Links", + ssb.ExploreIndex(1, ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreAll(ssb.ExploreRecursiveEdge())))) + }).Selector() + + if err != nil { + t.Fatal("Did not build selector") + } + if err := WriteSelectiveCar(context.Background(), sourceBs, []CarDag{CarDag{Root: nd3.Cid(), Selector: selector}}, buf); err != nil { + t.Fatal(err) + } + + bserv := dstest.Bserv() + ch, err := LoadCar(bserv.Blockstore(), buf) + if err != nil { + t.Fatal(err) + } + + if len(ch.Roots) != 1 { + t.Fatal("should have one root") + } + + if !ch.Roots[0].Equals(nd3.Cid()) { + t.Fatal("got wrong cid") + } + + bs := bserv.Blockstore() + for _, nd := range []format.Node{a, b, nd1, nd2, nd3} { + has, err := bs.Has(nd.Cid()) + if err != nil { + t.Fatal(err) + } + + if !has { + t.Fatal("should have cid in blockstore") + } + } + + for _, nd := range []format.Node{c} { + has, err := bs.Has(nd.Cid()) + if err != nil { + t.Fatal(err) + } + + if has { + t.Fatal("should NOT have cid in blockstore") + } + } +} From 009237a9ce8f6e507eefc89a24d613d8f7f6d9bb Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 30 Jan 2020 13:47:04 -0800 Subject: [PATCH 3186/3817] feat(util): ldsize function add ldsize function to estimate size of an LdWrite This commit was moved from ipld/go-car@dd7f14e81be301d402efa5635595bb97063b8d88 --- ipld/car/util/util.go | 10 ++++++++++ ipld/car/util/util_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 ipld/car/util/util_test.go diff --git a/ipld/car/util/util.go b/ipld/car/util/util.go index c53d59506..47c7e78d6 100644 --- a/ipld/car/util/util.go +++ b/ipld/car/util/util.go @@ -89,6 +89,16 @@ func LdWrite(w io.Writer, d ...[]byte) error { return nil } +func LdSize(d ...[]byte) uint64 { + var sum uint64 + for _, s := range d { + sum += uint64(len(s)) + } + buf := make([]byte, 8) + n := binary.PutUvarint(buf, sum) + return sum + uint64(n) +} + func LdRead(r *bufio.Reader) ([]byte, error) { l, err := binary.ReadUvarint(r) if err != nil { diff --git a/ipld/car/util/util_test.go b/ipld/car/util/util_test.go new file mode 100644 index 000000000..1e0567c8c --- /dev/null +++ b/ipld/car/util/util_test.go @@ -0,0 +1,26 @@ +package util_test + +import ( + "bytes" + "math/rand" + "testing" + + "github.com/ipfs/go-car/util" + "github.com/stretchr/testify/require" +) + +func TestLdSize(t *testing.T) { + for i := 0; i < 5; i++ { + var buf bytes.Buffer + data := make([][]byte, 5) + for j := 0; j < 5; j++ { + data[j] = make([]byte, rand.Intn(30)) + _, err := rand.Read(data[j]) + require.NoError(t, err) + } + size := util.LdSize(data...) + err := util.LdWrite(&buf, data...) + require.NoError(t, err) + require.Equal(t, uint64(len(buf.Bytes())), size) + } +} From aa603d65b59a40efa856e62f4a67cac6419542f4 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 30 Jan 2020 15:35:54 -0800 Subject: [PATCH 3187/3817] feat(selectivecar): add collection step when writing a car, seperate traversal of the selector from the actual writing of the Car file, which allows us to gather information about what will be written in the car, and to dedupe writes This commit was moved from ipld/go-car@053026ac271b1d8c63615cdc1b9a274c34edb3c8 --- ipld/car/car.go | 78 ++--------------- ipld/car/car_test.go | 47 ++++------ ipld/car/selectivecar.go | 170 +++++++++++++++++++++++++++++++++++++ ipld/car/util/util_test.go | 2 +- 4 files changed, 196 insertions(+), 101 deletions(-) create mode 100644 ipld/car/selectivecar.go diff --git a/ipld/car/car.go b/ipld/car/car.go index 2a7880d2f..71d153c64 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -2,23 +2,15 @@ package car import ( "bufio" - "bytes" "context" - "errors" "fmt" "io" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" - "github.com/ipld/go-ipld-prime/traversal" - blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" format "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" - "github.com/ipld/go-ipld-prime" - dagpb "github.com/ipld/go-ipld-prime-proto" - cidlink "github.com/ipld/go-ipld-prime/linking/cid" "github.com/ipld/go-ipld-prime/traversal/selector" util "github.com/ipld/go-car/util" @@ -60,7 +52,6 @@ func WriteCar(ctx context.Context, ds format.DAGService, roots []cid.Cid, w io.W func WriteCarWithWalker(ctx context.Context, ds format.DAGService, roots []cid.Cid, w io.Writer, walk WalkFunc) error { - h := &CarHeader{ Roots: roots, Version: 1, @@ -84,66 +75,6 @@ func DefaultWalkFunc(nd format.Node) ([]*format.Link, error) { return nd.Links(), nil } -func WriteSelectiveCar(ctx context.Context, store ReadStore, dags []CarDag, w io.Writer) error { - - roots := make([]cid.Cid, 0, len(dags)) - for _, carDag := range dags { - roots = append(roots, carDag.Root) - } - - h := &CarHeader{ - Roots: roots, - Version: 1, - } - - if err := WriteHeader(h, w); err != nil { - return fmt.Errorf("failed to write car header: %s", err) - } - - var loader ipld.Loader = func(lnk ipld.Link, ctx ipld.LinkContext) (io.Reader, error) { - cl, ok := lnk.(cidlink.Link) - if !ok { - return nil, errors.New("Incorrect Link Type") - } - c := cl.Cid - fmt.Println(c) - blk, err := store.Get(c) - if err != nil { - return nil, err - } - raw := blk.RawData() - err = util.LdWrite(w, c.Bytes(), raw) - if err != nil { - return nil, err - } - return bytes.NewReader(raw), nil - } - - nbc := dagpb.AddDagPBSupportToChooser(func(ipld.Link, ipld.LinkContext) ipld.NodeBuilder { - return ipldfree.NodeBuilder() - }) - - for _, carDag := range dags { - lnk := cidlink.Link{Cid: carDag.Root} - nb := nbc(lnk, ipld.LinkContext{}) - nd, err := lnk.Load(ctx, ipld.LinkContext{}, nb, loader) - if err != nil { - return err - } - err = traversal.Progress{ - Cfg: &traversal.Config{ - Ctx: ctx, - LinkLoader: loader, - LinkNodeBuilderChooser: nbc, - }, - }.WalkAdv(nd, carDag.Selector, func(traversal.Progress, ipld.Node, traversal.VisitReason) error { return nil }) - if err != nil { - return err - } - } - return nil -} - func ReadHeader(br *bufio.Reader) (*CarHeader, error) { hb, err := util.LdRead(br) if err != nil { @@ -167,6 +98,15 @@ func WriteHeader(h *CarHeader, w io.Writer) error { return util.LdWrite(w, hb) } +func SizeHeader(h *CarHeader) (uint64, error) { + hb, err := cbor.DumpObject(h) + if err != nil { + return 0, err + } + + return util.LdSize(hb), nil +} + func (cw *carWriter) enumGetLinks(ctx context.Context, c cid.Cid) ([]*format.Link, error) { nd, err := cw.ds.Get(ctx, c) if err != nil { diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 98288e9cb..6f1628d03 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -12,6 +12,7 @@ import ( ipldfree "github.com/ipld/go-ipld-prime/impl/free" "github.com/ipld/go-ipld-prime/traversal/selector" "github.com/ipld/go-ipld-prime/traversal/selector/builder" + "github.com/stretchr/testify/require" ) func assertAddNodes(t *testing.T, ds format.DAGService, nds ...format.Node) { @@ -94,54 +95,38 @@ func TestRoundtripSelective(t *testing.T) { assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) - buf := new(bytes.Buffer) ssb := builder.NewSelectorSpecBuilder(ipldfree.NodeBuilder()) selector, err := ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) { efsb.Insert("Links", ssb.ExploreIndex(1, ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreAll(ssb.ExploreRecursiveEdge())))) }).Selector() + require.NoError(t, err) - if err != nil { - t.Fatal("Did not build selector") - } - if err := WriteSelectiveCar(context.Background(), sourceBs, []CarDag{CarDag{Root: nd3.Cid(), Selector: selector}}, buf); err != nil { - t.Fatal(err) - } + sc := NewSelectiveCar(context.Background(), sourceBs, []CarDag{CarDag{Root: nd3.Cid(), Selector: selector}}) + scr, err := sc.Traverse() + require.NoError(t, err) + buf := new(bytes.Buffer) + err = scr.Write(buf) + require.NoError(t, err) + require.Equal(t, scr.Size(), uint64(buf.Len())) bserv := dstest.Bserv() ch, err := LoadCar(bserv.Blockstore(), buf) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) + require.Equal(t, len(ch.Roots), 1) - if len(ch.Roots) != 1 { - t.Fatal("should have one root") - } - - if !ch.Roots[0].Equals(nd3.Cid()) { - t.Fatal("got wrong cid") - } + require.True(t, ch.Roots[0].Equals(nd3.Cid())) bs := bserv.Blockstore() for _, nd := range []format.Node{a, b, nd1, nd2, nd3} { has, err := bs.Has(nd.Cid()) - if err != nil { - t.Fatal(err) - } - - if !has { - t.Fatal("should have cid in blockstore") - } + require.NoError(t, err) + require.True(t, has) } for _, nd := range []format.Node{c} { has, err := bs.Has(nd.Cid()) - if err != nil { - t.Fatal(err) - } - - if has { - t.Fatal("should NOT have cid in blockstore") - } + require.NoError(t, err) + require.False(t, has) } } diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go new file mode 100644 index 000000000..9f5d037bc --- /dev/null +++ b/ipld/car/selectivecar.go @@ -0,0 +1,170 @@ +package car + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + + cid "github.com/ipfs/go-cid" + util "github.com/ipld/go-car/util" + "github.com/ipld/go-ipld-prime" + dagpb "github.com/ipld/go-ipld-prime-proto" + ipldfree "github.com/ipld/go-ipld-prime/impl/free" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + "github.com/ipld/go-ipld-prime/traversal" +) + +type CarBlock struct { + BlockCID cid.Cid + Offset uint64 + Size uint64 +} + +type SelectiveCar struct { + ctx context.Context + dags []CarDag + store ReadStore +} + +type SelectiveCarResult struct { + SelectiveCar + header CarHeader + carBlocks []CarBlock + size uint64 +} + +func NewSelectiveCar(ctx context.Context, store ReadStore, dags []CarDag) SelectiveCar { + return SelectiveCar{ + ctx: ctx, + store: store, + dags: dags, + } +} + +type selectiveCarTraverser struct { + offset uint64 + cidSet *cid.Set + sc SelectiveCar +} + +func (sc SelectiveCar) Traverse() (SelectiveCarResult, error) { + traverser := &selectiveCarTraverser{0, cid.NewSet(), sc} + return traverser.traverse() +} + +func (sct *selectiveCarTraverser) traverse() (SelectiveCarResult, error) { + header, err := sct.traverseHeader() + if err != nil { + return SelectiveCarResult{}, err + } + carBlocks, err := sct.traverseBlocks() + if err != nil { + return SelectiveCarResult{}, err + } + return SelectiveCarResult{ + sct.sc, + header, + carBlocks, + sct.offset, + }, nil +} + +func (sct *selectiveCarTraverser) traverseHeader() (CarHeader, error) { + roots := make([]cid.Cid, 0, len(sct.sc.dags)) + for _, carDag := range sct.sc.dags { + roots = append(roots, carDag.Root) + } + + header := CarHeader{ + Roots: roots, + Version: 1, + } + + size, err := SizeHeader(&header) + if err != nil { + return CarHeader{}, err + } + + sct.offset += size + + return header, nil +} + +func (sct *selectiveCarTraverser) traverseBlocks() ([]CarBlock, error) { + var carBlocks []CarBlock + var loader ipld.Loader = func(lnk ipld.Link, ctx ipld.LinkContext) (io.Reader, error) { + cl, ok := lnk.(cidlink.Link) + if !ok { + return nil, errors.New("Incorrect Link Type") + } + c := cl.Cid + blk, err := sct.sc.store.Get(c) + if err != nil { + return nil, err + } + raw := blk.RawData() + if !sct.cidSet.Has(c) { + sct.cidSet.Add(c) + size := util.LdSize(c.Bytes(), raw) + carBlocks = append(carBlocks, CarBlock{ + BlockCID: c, + Offset: sct.offset, + Size: size, + }) + sct.offset += size + } + return bytes.NewReader(raw), nil + } + + nbc := dagpb.AddDagPBSupportToChooser(func(ipld.Link, ipld.LinkContext) ipld.NodeBuilder { + return ipldfree.NodeBuilder() + }) + + for _, carDag := range sct.sc.dags { + lnk := cidlink.Link{Cid: carDag.Root} + nb := nbc(lnk, ipld.LinkContext{}) + nd, err := lnk.Load(sct.sc.ctx, ipld.LinkContext{}, nb, loader) + if err != nil { + return nil, err + } + err = traversal.Progress{ + Cfg: &traversal.Config{ + Ctx: sct.sc.ctx, + LinkLoader: loader, + LinkNodeBuilderChooser: nbc, + }, + }.WalkAdv(nd, carDag.Selector, func(traversal.Progress, ipld.Node, traversal.VisitReason) error { return nil }) + if err != nil { + return nil, err + } + } + return carBlocks, nil +} + +func (sc SelectiveCarResult) Size() uint64 { + return sc.size +} + +func (sc SelectiveCarResult) CarBlocks() []CarBlock { + return sc.carBlocks +} + +func (sc SelectiveCarResult) Write(w io.Writer) error { + if err := WriteHeader(&sc.header, w); err != nil { + return fmt.Errorf("failed to write car header: %s", err) + } + for _, carBlock := range sc.carBlocks { + blk, err := sc.store.Get(carBlock.BlockCID) + if err != nil { + return err + } + raw := blk.RawData() + err = util.LdWrite(w, carBlock.BlockCID.Bytes(), raw) + if err != nil { + return err + } + } + return nil +} diff --git a/ipld/car/util/util_test.go b/ipld/car/util/util_test.go index 1e0567c8c..e85aed334 100644 --- a/ipld/car/util/util_test.go +++ b/ipld/car/util/util_test.go @@ -5,7 +5,7 @@ import ( "math/rand" "testing" - "github.com/ipfs/go-car/util" + "github.com/ipld/go-car/util" "github.com/stretchr/testify/require" ) From 78d6bc78ca100dd0ff57a5f4cb70ff0d592e6a80 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 30 Jan 2020 15:53:41 -0800 Subject: [PATCH 3188/3817] feat(selectivecar): switch to nodes Use an ipld.Node for a selector rather than an actual selector operation -- that way it can be serialized as needed This commit was moved from ipld/go-car@e661423c1d0841c890ae15ad258010dcec30c888 --- ipld/car/car.go | 6 ------ ipld/car/car_test.go | 5 ++--- ipld/car/selectivecar.go | 12 +++++++++++- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index 71d153c64..6cb1606f4 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -11,7 +11,6 @@ import ( cbor "github.com/ipfs/go-ipld-cbor" format "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" - "github.com/ipld/go-ipld-prime/traversal/selector" util "github.com/ipld/go-car/util" ) @@ -28,11 +27,6 @@ type ReadStore interface { Get(cid.Cid) (blocks.Block, error) } -type CarDag struct { - Root cid.Cid - Selector selector.Selector -} - type CarHeader struct { Roots []cid.Cid Version uint64 diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 6f1628d03..4acb16100 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -96,11 +96,10 @@ func TestRoundtripSelective(t *testing.T) { assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) ssb := builder.NewSelectorSpecBuilder(ipldfree.NodeBuilder()) - selector, err := ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) { + selector := ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) { efsb.Insert("Links", ssb.ExploreIndex(1, ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreAll(ssb.ExploreRecursiveEdge())))) - }).Selector() - require.NoError(t, err) + }).Node() sc := NewSelectiveCar(context.Background(), sourceBs, []CarDag{CarDag{Root: nd3.Cid(), Selector: selector}}) scr, err := sc.Traverse() diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index 9f5d037bc..19a80f786 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -14,8 +14,14 @@ import ( ipldfree "github.com/ipld/go-ipld-prime/impl/free" cidlink "github.com/ipld/go-ipld-prime/linking/cid" "github.com/ipld/go-ipld-prime/traversal" + "github.com/ipld/go-ipld-prime/traversal/selector" ) +type CarDag struct { + Root cid.Cid + Selector ipld.Node +} + type CarBlock struct { BlockCID cid.Cid Offset uint64 @@ -123,6 +129,10 @@ func (sct *selectiveCarTraverser) traverseBlocks() ([]CarBlock, error) { }) for _, carDag := range sct.sc.dags { + parsed, err := selector.ParseSelector(carDag.Selector) + if err != nil { + return nil, err + } lnk := cidlink.Link{Cid: carDag.Root} nb := nbc(lnk, ipld.LinkContext{}) nd, err := lnk.Load(sct.sc.ctx, ipld.LinkContext{}, nb, loader) @@ -135,7 +145,7 @@ func (sct *selectiveCarTraverser) traverseBlocks() ([]CarBlock, error) { LinkLoader: loader, LinkNodeBuilderChooser: nbc, }, - }.WalkAdv(nd, carDag.Selector, func(traversal.Progress, ipld.Node, traversal.VisitReason) error { return nil }) + }.WalkAdv(nd, parsed, func(traversal.Progress, ipld.Node, traversal.VisitReason) error { return nil }) if err != nil { return nil, err } From f014e0e10e6324cb6c413986d2f01722b6a71215 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Fri, 31 Jan 2020 13:32:08 -0800 Subject: [PATCH 3189/3817] feat(selectivecar): add two step process Allows a selective car to simply be written, or to be prepared then dumped to disk This commit was moved from ipld/go-car@4aa335b14ef95fba8795f8d00a22020c51b64c48 --- ipld/car/car_test.go | 28 ++++- ipld/car/selectivecar.go | 218 ++++++++++++++++++++++++--------------- 2 files changed, 160 insertions(+), 86 deletions(-) diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 4acb16100..1e58417e1 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -88,6 +88,7 @@ func TestRoundtripSelective(t *testing.T) { nd2 := &dag.ProtoNode{} nd2.AddNodeLink("first", nd1) nd2.AddNodeLink("dog", b) + nd2.AddNodeLink("repeat", nd1) nd3 := &dag.ProtoNode{} nd3.AddNodeLink("second", nd2) @@ -101,14 +102,31 @@ func TestRoundtripSelective(t *testing.T) { ssb.ExploreIndex(1, ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreAll(ssb.ExploreRecursiveEdge())))) }).Node() - sc := NewSelectiveCar(context.Background(), sourceBs, []CarDag{CarDag{Root: nd3.Cid(), Selector: selector}}) - scr, err := sc.Traverse() - require.NoError(t, err) + sc := NewSelectiveCar(context.Background(), sourceBs, []Dag{Dag{Root: nd3.Cid(), Selector: selector}}) + // write car in one step buf := new(bytes.Buffer) - err = scr.Write(buf) + blockCount := 0 + err := sc.Write(buf, func(block Block) error { + blockCount++ + return nil + }) + require.Equal(t, blockCount, 5) + require.NoError(t, err) + + // write car in two steps + scp, err := sc.Prepare() require.NoError(t, err) - require.Equal(t, scr.Size(), uint64(buf.Len())) + buf2 := new(bytes.Buffer) + err = scp.Dump(buf2) + require.NoError(t, err) + + // verify preparation step correctly assesed length + require.Equal(t, scp.Size, uint64(buf.Len())) + // verify equal data written by both methods + require.Equal(t, buf.Bytes(), buf2.Bytes()) + + // readout car and verify contents bserv := dstest.Bserv() ch, err := LoadCar(bserv.Blockstore(), buf) require.NoError(t, err) diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index 19a80f786..bc5e176e8 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -17,31 +17,47 @@ import ( "github.com/ipld/go-ipld-prime/traversal/selector" ) -type CarDag struct { +// Dag is a root/selector combo to put into a car +type Dag struct { Root cid.Cid Selector ipld.Node } -type CarBlock struct { +// Block is all information and metadata about a block that is part of a car file +type Block struct { BlockCID cid.Cid + Data []byte Offset uint64 Size uint64 } +// SelectiveCar is a car file based on root + selector combos instead of just +// a single root and complete dag walk type SelectiveCar struct { ctx context.Context - dags []CarDag + dags []Dag store ReadStore } -type SelectiveCarResult struct { +// OnCarHeaderFunc is called during traversal when the header is created +type OnCarHeaderFunc func(CarHeader) error + +// OnNewCarBlockFunc is called during traveral when a new unique block is encountered +type OnNewCarBlockFunc func(Block) error + +// SelectiveCarPrepared is a SelectiveCar that has already been traversed, such that it +// can be written quicker with Dump. It also contains metadata already collection about +// the Car file like size and number of blocks that go into it +type SelectiveCarPrepared struct { SelectiveCar - header CarHeader - carBlocks []CarBlock - size uint64 + Size uint64 + Header CarHeader + Cids []cid.Cid } -func NewSelectiveCar(ctx context.Context, store ReadStore, dags []CarDag) SelectiveCar { +// NewSelectiveCar creates a new SelectiveCar for the given car file based +// a block store and set of root+selector pairs +func NewSelectiveCar(ctx context.Context, store ReadStore, dags []Dag) SelectiveCar { return SelectiveCar{ ctx: ctx, store: store, @@ -49,35 +65,97 @@ func NewSelectiveCar(ctx context.Context, store ReadStore, dags []CarDag) Select } } -type selectiveCarTraverser struct { - offset uint64 - cidSet *cid.Set - sc SelectiveCar +func (sc SelectiveCar) traverse(onCarHeader OnCarHeaderFunc, onNewCarBlock OnNewCarBlockFunc) (uint64, error) { + traverser := &selectiveCarTraverser{onCarHeader, onNewCarBlock, 0, cid.NewSet(), sc} + return traverser.traverse() } -func (sc SelectiveCar) Traverse() (SelectiveCarResult, error) { - traverser := &selectiveCarTraverser{0, cid.NewSet(), sc} - return traverser.traverse() +// Prepare traverse a car file and collects data on what is about to be written, but +// does not actually write the file +func (sc SelectiveCar) Prepare() (SelectiveCarPrepared, error) { + var header CarHeader + var cids []cid.Cid + + onCarHeader := func(h CarHeader) error { + header = h + return nil + } + onNewCarBlock := func(block Block) error { + cids = append(cids, block.BlockCID) + return nil + } + size, err := sc.traverse(onCarHeader, onNewCarBlock) + if err != nil { + return SelectiveCarPrepared{}, err + } + return SelectiveCarPrepared{sc, size, header, cids}, nil +} + +func (sc SelectiveCar) Write(w io.Writer, userOnNewCarBlocks ...OnNewCarBlockFunc) error { + onCarHeader := func(h CarHeader) error { + if err := WriteHeader(&h, w); err != nil { + return fmt.Errorf("failed to write car header: %s", err) + } + return nil + } + onNewCarBlock := func(block Block) error { + err := util.LdWrite(w, block.BlockCID.Bytes(), block.Data) + if err != nil { + return err + } + for _, userOnNewCarBlock := range userOnNewCarBlocks { + err := userOnNewCarBlock(block) + if err != nil { + return err + } + } + return nil + } + _, err := sc.traverse(onCarHeader, onNewCarBlock) + return err +} + +// Dump writes the car file as quickly as possible based on information already +// collected +func (sc SelectiveCarPrepared) Dump(w io.Writer) error { + if err := WriteHeader(&sc.Header, w); err != nil { + return fmt.Errorf("failed to write car header: %s", err) + } + for _, c := range sc.Cids { + blk, err := sc.store.Get(c) + if err != nil { + return err + } + raw := blk.RawData() + err = util.LdWrite(w, c.Bytes(), raw) + if err != nil { + return err + } + } + return nil } -func (sct *selectiveCarTraverser) traverse() (SelectiveCarResult, error) { - header, err := sct.traverseHeader() +type selectiveCarTraverser struct { + onCarHeader OnCarHeaderFunc + onNewCarBlock OnNewCarBlockFunc + offset uint64 + cidSet *cid.Set + sc SelectiveCar +} + +func (sct *selectiveCarTraverser) traverse() (uint64, error) { + err := sct.traverseHeader() if err != nil { - return SelectiveCarResult{}, err + return 0, err } - carBlocks, err := sct.traverseBlocks() + err = sct.traverseBlocks() if err != nil { - return SelectiveCarResult{}, err - } - return SelectiveCarResult{ - sct.sc, - header, - carBlocks, - sct.offset, - }, nil + return 0, err + } + return sct.offset, nil } -func (sct *selectiveCarTraverser) traverseHeader() (CarHeader, error) { +func (sct *selectiveCarTraverser) traverseHeader() error { roots := make([]cid.Cid, 0, len(sct.sc.dags)) for _, carDag := range sct.sc.dags { roots = append(roots, carDag.Root) @@ -90,39 +168,43 @@ func (sct *selectiveCarTraverser) traverseHeader() (CarHeader, error) { size, err := SizeHeader(&header) if err != nil { - return CarHeader{}, err + return err } sct.offset += size - return header, nil + return sct.onCarHeader(header) } -func (sct *selectiveCarTraverser) traverseBlocks() ([]CarBlock, error) { - var carBlocks []CarBlock - var loader ipld.Loader = func(lnk ipld.Link, ctx ipld.LinkContext) (io.Reader, error) { - cl, ok := lnk.(cidlink.Link) - if !ok { - return nil, errors.New("Incorrect Link Type") - } - c := cl.Cid - blk, err := sct.sc.store.Get(c) +func (sct *selectiveCarTraverser) loader(lnk ipld.Link, ctx ipld.LinkContext) (io.Reader, error) { + cl, ok := lnk.(cidlink.Link) + if !ok { + return nil, errors.New("Incorrect Link Type") + } + c := cl.Cid + blk, err := sct.sc.store.Get(c) + if err != nil { + return nil, err + } + raw := blk.RawData() + if !sct.cidSet.Has(c) { + sct.cidSet.Add(c) + size := util.LdSize(c.Bytes(), raw) + err := sct.onNewCarBlock(Block{ + BlockCID: c, + Data: raw, + Offset: sct.offset, + Size: size, + }) if err != nil { return nil, err } - raw := blk.RawData() - if !sct.cidSet.Has(c) { - sct.cidSet.Add(c) - size := util.LdSize(c.Bytes(), raw) - carBlocks = append(carBlocks, CarBlock{ - BlockCID: c, - Offset: sct.offset, - Size: size, - }) - sct.offset += size - } - return bytes.NewReader(raw), nil + sct.offset += size } + return bytes.NewReader(raw), nil +} + +func (sct *selectiveCarTraverser) traverseBlocks() error { nbc := dagpb.AddDagPBSupportToChooser(func(ipld.Link, ipld.LinkContext) ipld.NodeBuilder { return ipldfree.NodeBuilder() @@ -131,47 +213,21 @@ func (sct *selectiveCarTraverser) traverseBlocks() ([]CarBlock, error) { for _, carDag := range sct.sc.dags { parsed, err := selector.ParseSelector(carDag.Selector) if err != nil { - return nil, err + return err } lnk := cidlink.Link{Cid: carDag.Root} nb := nbc(lnk, ipld.LinkContext{}) - nd, err := lnk.Load(sct.sc.ctx, ipld.LinkContext{}, nb, loader) + nd, err := lnk.Load(sct.sc.ctx, ipld.LinkContext{}, nb, sct.loader) if err != nil { - return nil, err + return err } err = traversal.Progress{ Cfg: &traversal.Config{ Ctx: sct.sc.ctx, - LinkLoader: loader, + LinkLoader: sct.loader, LinkNodeBuilderChooser: nbc, }, }.WalkAdv(nd, parsed, func(traversal.Progress, ipld.Node, traversal.VisitReason) error { return nil }) - if err != nil { - return nil, err - } - } - return carBlocks, nil -} - -func (sc SelectiveCarResult) Size() uint64 { - return sc.size -} - -func (sc SelectiveCarResult) CarBlocks() []CarBlock { - return sc.carBlocks -} - -func (sc SelectiveCarResult) Write(w io.Writer) error { - if err := WriteHeader(&sc.header, w); err != nil { - return fmt.Errorf("failed to write car header: %s", err) - } - for _, carBlock := range sc.carBlocks { - blk, err := sc.store.Get(carBlock.BlockCID) - if err != nil { - return err - } - raw := blk.RawData() - err = util.LdWrite(w, carBlock.BlockCID.Bytes(), raw) if err != nil { return err } From 8451f201c27fcd265bf2cda46cea0b998ddd3ba4 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Fri, 31 Jan 2020 14:04:34 -0800 Subject: [PATCH 3190/3817] refactor(selectivecar): convert to accessors convert SelectiveCarPrepared data to accessors This commit was moved from ipld/go-car@53dd8b04794ffe365d919ae79b00e4802024fe16 --- ipld/car/car_test.go | 6 ++++-- ipld/car/selectivecar.go | 25 ++++++++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 1e58417e1..462d30126 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -121,8 +121,10 @@ func TestRoundtripSelective(t *testing.T) { err = scp.Dump(buf2) require.NoError(t, err) - // verify preparation step correctly assesed length - require.Equal(t, scp.Size, uint64(buf.Len())) + // verify preparation step correctly assesed length and blocks + require.Equal(t, scp.Size(), uint64(buf.Len())) + require.Equal(t, len(scp.Cids()), blockCount) + // verify equal data written by both methods require.Equal(t, buf.Bytes(), buf2.Bytes()) diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index bc5e176e8..f98e81bd2 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -50,9 +50,9 @@ type OnNewCarBlockFunc func(Block) error // the Car file like size and number of blocks that go into it type SelectiveCarPrepared struct { SelectiveCar - Size uint64 - Header CarHeader - Cids []cid.Cid + size uint64 + header CarHeader + cids []cid.Cid } // NewSelectiveCar creates a new SelectiveCar for the given car file based @@ -115,13 +115,28 @@ func (sc SelectiveCar) Write(w io.Writer, userOnNewCarBlocks ...OnNewCarBlockFun return err } +// Size returns the total size in bytes of the car file that will be written +func (sc SelectiveCarPrepared) Size() uint64 { + return sc.size +} + +// Header returns the header for the car file that will be written +func (sc SelectiveCarPrepared) Header() CarHeader { + return sc.header +} + +// Cids returns the list of unique block cids that will be written to the car file +func (sc SelectiveCarPrepared) Cids() []cid.Cid { + return sc.cids +} + // Dump writes the car file as quickly as possible based on information already // collected func (sc SelectiveCarPrepared) Dump(w io.Writer) error { - if err := WriteHeader(&sc.Header, w); err != nil { + if err := WriteHeader(&sc.header, w); err != nil { return fmt.Errorf("failed to write car header: %s", err) } - for _, c := range sc.Cids { + for _, c := range sc.cids { blk, err := sc.store.Get(c) if err != nil { return err From 8c25847b697743e5b88499e22f8c0e34efa461a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 21 Feb 2020 20:07:41 +0100 Subject: [PATCH 3191/3817] Use correct walk func in enumGetLinks This commit was moved from ipld/go-car@07620eec177fd87b8f79db2e0e08ddbf8bfe6640 --- ipld/car/car.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index 6cb1606f4..6fc4b6277 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -111,7 +111,7 @@ func (cw *carWriter) enumGetLinks(ctx context.Context, c cid.Cid) ([]*format.Lin return nil, err } - return nd.Links(), nil + return cw.walk(nd) } func (cw *carWriter) writeNode(ctx context.Context, nd format.Node) error { From 2a57a154e34e0477231d774b9b7a02212153d516 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Fri, 6 Mar 2020 15:10:34 -0800 Subject: [PATCH 3192/3817] fix(car): minor cleanups rename SizeHeader to HeaderSize, add comments and cleanups in selective car test This commit was moved from ipld/go-car@6236fbaa4401505430e95f045cf53ebc2bb57015 --- ipld/car/car.go | 2 +- ipld/car/car_test.go | 13 ++++++++++++- ipld/car/selectivecar.go | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index 6fc4b6277..bce59d347 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -92,7 +92,7 @@ func WriteHeader(h *CarHeader, w io.Writer) error { return util.LdWrite(w, hb) } -func SizeHeader(h *CarHeader) (uint64, error) { +func HeaderSize(h *CarHeader) (uint64, error) { hb, err := cbor.DumpObject(h) if err != nil { return 0, err diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 462d30126..4c969626f 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -97,6 +97,14 @@ func TestRoundtripSelective(t *testing.T) { assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) ssb := builder.NewSelectorSpecBuilder(ipldfree.NodeBuilder()) + + // the graph assembled above looks as follows, in order: + // nd3 -> [c, nd2 -> [nd1 -> a, b, nd1 -> a]] + // this selector starts at n3, and traverses a link at index 1 (nd2, the second link, zero indexed) + // it then recursively traverses all of its children + // the only node skipped is 'c' -- link at index 0 immediately below nd3 + // the purpose is simply to show we are not writing the entire dag underneath + // nd3 selector := ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) { efsb.Insert("Links", ssb.ExploreIndex(1, ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreAll(ssb.ExploreRecursiveEdge())))) @@ -114,8 +122,11 @@ func TestRoundtripSelective(t *testing.T) { require.Equal(t, blockCount, 5) require.NoError(t, err) + // create a new builder for two-step write + sc2 := NewSelectiveCar(context.Background(), sourceBs, []Dag{Dag{Root: nd3.Cid(), Selector: selector}}) + // write car in two steps - scp, err := sc.Prepare() + scp, err := sc2.Prepare() require.NoError(t, err) buf2 := new(bytes.Buffer) err = scp.Dump(buf2) diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index f98e81bd2..705cf563f 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -181,7 +181,7 @@ func (sct *selectiveCarTraverser) traverseHeader() error { Version: 1, } - size, err := SizeHeader(&header) + size, err := HeaderSize(&header) if err != nil { return err } From 3dd2e4c9896909bf40232306531865d62334fb3d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 11 Mar 2020 17:01:08 -0700 Subject: [PATCH 3193/3817] fix: handle query errors If we successfully start a query but get an error when reading an entry from the query, return it and don't try to delete an empty key. This commit was moved from ipfs/go-ipfs-provider@d444d40e596a117ebe857775c083cbb635914d55 --- provider/queue/queue.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/provider/queue/queue.go b/provider/queue/queue.go index ddaa97582..da110ce80 100644 --- a/provider/queue/queue.go +++ b/provider/queue/queue.go @@ -132,7 +132,7 @@ func (q *Queue) work() { }() } -func (q *Queue) getQueueHead() (*query.Result, error) { +func (q *Queue) getQueueHead() (*query.Entry, error) { qry := query.Query{Orders: []query.Order{query.OrderByKey{}}, Limit: 1} results, err := q.ds.Query(qry) if err != nil { @@ -144,5 +144,5 @@ func (q *Queue) getQueueHead() (*query.Result, error) { return nil, nil } - return &r, nil + return &r.Entry, r.Error } From d882497aedc6022382e6d042c1630c99c77e894c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 11 Mar 2020 17:03:57 -0700 Subject: [PATCH 3194/3817] nit: rename e to err This commit was moved from ipfs/go-ipfs-provider@de3bf42b2c74c9074134f1527cabc72dc1ddc467 --- provider/queue/queue.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/provider/queue/queue.go b/provider/queue/queue.go index da110ce80..b13f38b20 100644 --- a/provider/queue/queue.go +++ b/provider/queue/queue.go @@ -80,17 +80,17 @@ func (q *Queue) work() { for { if c == cid.Undef { - head, e := q.getQueueHead() + head, err := q.getQueueHead() - if e != nil { - log.Errorf("error querying for head of queue: %s, stopping provider", e) + if err != nil { + log.Errorf("error querying for head of queue: %s, stopping provider", err) return } else if head != nil { k = datastore.NewKey(head.Key) - c, e = cid.Parse(head.Value) - if e != nil { - log.Warningf("error parsing queue entry cid with key (%s), removing it from queue: %s", head.Key, e) - err := q.ds.Delete(k) + c, err = cid.Parse(head.Value) + if err != nil { + log.Warningf("error parsing queue entry cid with key (%s), removing it from queue: %s", head.Key, err) + err = q.ds.Delete(k) if err != nil { log.Errorf("error deleting queue entry with key (%s), due to error (%s), stopping provider", head.Key, err) return From 3925c35b2a3eb156e06cd993fb2c34c200fc1dc8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 11 Mar 2020 17:13:31 -0700 Subject: [PATCH 3195/3817] feat: return errors from enqueue This commit was moved from ipfs/go-ipfs-provider@c835ad80d441cbe8467f385973110d879b064115 --- provider/queue/queue.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/provider/queue/queue.go b/provider/queue/queue.go index b13f38b20..2c3350256 100644 --- a/provider/queue/queue.go +++ b/provider/queue/queue.go @@ -56,10 +56,12 @@ func (q *Queue) Close() error { } // Enqueue puts a cid in the queue -func (q *Queue) Enqueue(cid cid.Cid) { +func (q *Queue) Enqueue(cid cid.Cid) error { select { case q.enqueue <- cid: + return nil case <-q.ctx.Done(): + return fmt.Errorf("failed to enqueue CID: shutting down") } } @@ -75,6 +77,11 @@ func (q *Queue) work() { var c cid.Cid = cid.Undef defer func() { + // also cancels any in-progess enqueue tasks. + q.close() + // unblocks anyone waiting + close(q.dequeue) + // unblocks the close call close(q.closed) }() From 0479ddbb6d5df0a7f10020d0976bd2bbb388ec1d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 11 Mar 2020 18:58:04 -0700 Subject: [PATCH 3196/3817] fix: return errors from the provider This commit was moved from ipfs/go-ipfs-provider@056b8b666d8035e93b3bf7af29dde0f5e6df6774 --- provider/simple/provider.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/provider/simple/provider.go b/provider/simple/provider.go index 3993d4509..f421f6195 100644 --- a/provider/simple/provider.go +++ b/provider/simple/provider.go @@ -64,8 +64,7 @@ func NewProvider(ctx context.Context, queue *q.Queue, contentRouting routing.Con // Close stops the provider func (p *Provider) Close() error { - p.queue.Close() - return nil + return p.queue.Close() } // Run workers to handle provide requests. @@ -75,8 +74,7 @@ func (p *Provider) Run() { // Provide the given cid using specified strategy. func (p *Provider) Provide(root cid.Cid) error { - p.queue.Enqueue(root) - return nil + return p.queue.Enqueue(root) } // Handle all outgoing cids by providing (announcing) them From 3783ec8d18ebee58c7488a3f54fe66112f0fc886 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Sat, 7 Mar 2020 01:56:48 +0100 Subject: [PATCH 3197/3817] feat: IPFS_NS_MAP Allows static DNSLink mappings with IPFS_NS_MAP. License: MIT Signed-off-by: Marcin Rataj This commit was moved from ipfs/go-namesys@cb01c11cb0b115f3ed3c66c2588d1b3605cb8f91 --- namesys/cache.go | 8 ++++++++ namesys/namesys.go | 25 +++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/namesys/cache.go b/namesys/cache.go index 4a5cb5113..a0029829d 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -7,6 +7,14 @@ import ( ) func (ns *mpns) cacheGet(name string) (path.Path, bool) { + // existence of optional mapping defined via IPFS_NS_MAP is checked first + if ns.staticMap != nil { + val, ok := ns.staticMap[name] + if ok { + return val, true + } + } + if ns.cache == nil { return "", false } diff --git a/namesys/namesys.go b/namesys/namesys.go index 079eecccc..11f4646f1 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -2,6 +2,7 @@ package namesys import ( "context" + "os" "strings" "time" @@ -29,25 +30,45 @@ type mpns struct { dnsResolver, proquintResolver, ipnsResolver resolver ipnsPublisher Publisher - cache *lru.Cache + staticMap map[string]path.Path + cache *lru.Cache } // NewNameSystem will construct the IPFS naming system based on Routing func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSystem { - var cache *lru.Cache + var ( + cache *lru.Cache + staticMap map[string]path.Path + ) if cachesize > 0 { cache, _ = lru.New(cachesize) } + // Prewarm namesys cache with static records for deteministic tests and debugging. + // Useful for testing things like DNSLink without real DNS lookup. + // Example: + // IPFS_NS_MAP="dnslink-test.example.com:/ipfs/bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am" + if list := os.Getenv("IPFS_NS_MAP"); list != "" { + staticMap = make(map[string]path.Path) + for _, pair := range strings.Split(list, ",") { + mapping := strings.SplitN(pair, ":", 2) + key := mapping[0] + value := path.FromString(mapping[1]) + staticMap[key] = value + } + } + return &mpns{ dnsResolver: NewDNSResolver(), proquintResolver: new(ProquintResolver), ipnsResolver: NewIpnsResolver(r), ipnsPublisher: NewIpnsPublisher(r, ds), + staticMap: staticMap, cache: cache, } } +// DefaultResolverCacheTTL defines max ttl of a record placed in namesys cache. const DefaultResolverCacheTTL = time.Minute // Resolve implements Resolver. From 6cd6774d63e9296021bb967e593f70028f1608df Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Thu, 14 Mar 2019 17:21:38 -0700 Subject: [PATCH 3198/3817] feat(gateway): subdomain and proxy gateway License: MIT Signed-off-by: Marcin Rataj This commit was moved from ipfs/go-namesys@eda5b9a8ec17e8433876772fc002661b015ffe1d --- namesys/namesys.go | 23 ++++++++++++++++++++--- namesys/namesys_test.go | 14 +++++++++----- namesys/routing.go | 1 + 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 11f4646f1..a486b83b8 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -2,11 +2,13 @@ package namesys import ( "context" + "fmt" "os" "strings" "time" lru "github.com/hashicorp/golang-lru" + cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" @@ -14,7 +16,6 @@ import ( ci "github.com/libp2p/go-libp2p-core/crypto" peer "github.com/libp2p/go-libp2p-core/peer" routing "github.com/libp2p/go-libp2p-core/routing" - mh "github.com/multiformats/go-multihash" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. @@ -133,12 +134,28 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. } // Resolver selection: - // 1. if it is a multihash resolve through "ipns". + // 1. if it is a PeerID/CID/multihash resolve through "ipns". // 2. if it is a domain name, resolve through "dns" // 3. otherwise resolve through the "proquint" resolver var res resolver - if _, err := mh.FromB58String(key); err == nil { + _, err := peer.Decode(key) + + // CIDs in IPNS are expected to have libp2p-key multicodec + // We ease the transition by returning a more meaningful error with a valid CID + if err != nil && err.Error() == "can't convert CID of type protobuf to a peer ID" { + ipnsCid, cidErr := cid.Decode(key) + if cidErr == nil && ipnsCid.Version() == 1 && ipnsCid.Type() != cid.Libp2pKey { + fixedCid := cid.NewCidV1(cid.Libp2pKey, ipnsCid.Hash()).String() + codecErr := fmt.Errorf("peer ID represented as CIDv1 require libp2p-key multicodec: retry with /ipns/%s", fixedCid) + log.Debugf("RoutingResolver: could not convert public key hash %s to peer ID: %s\n", key, codecErr) + out <- onceResult{err: codecErr} + close(out) + return out + } + } + + if err == nil { res = ns.ipnsResolver } else if isd.IsDomain(key) { res = ns.dnsResolver diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index a0ffbc50d..b3e963c9e 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -11,7 +11,7 @@ import ( offroute "github.com/ipfs/go-ipfs-routing/offline" ipns "github.com/ipfs/go-ipns" path "github.com/ipfs/go-path" - "github.com/ipfs/go-unixfs" + unixfs "github.com/ipfs/go-unixfs" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" ci "github.com/libp2p/go-libp2p-core/crypto" peer "github.com/libp2p/go-libp2p-core/peer" @@ -49,10 +49,12 @@ func (r *mockResolver) resolveOnceAsync(ctx context.Context, name string, option func mockResolverOne() *mockResolver { return &mockResolver{ entries: map[string]string{ - "QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy": "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", - "QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n": "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", - "QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD": "/ipns/ipfs.io", - "QmQ4QZh8nrsczdUEwTyfBope4THUhqxqc1fx6qYhhzZQei": "/ipfs/QmP3ouCnU8NNLsW6261pAx2pNLV2E4dQoisB1sgda12Act", + "QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy": "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", + "QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n": "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", + "QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD": "/ipns/ipfs.io", + "QmQ4QZh8nrsczdUEwTyfBope4THUhqxqc1fx6qYhhzZQei": "/ipfs/QmP3ouCnU8NNLsW6261pAx2pNLV2E4dQoisB1sgda12Act", + "12D3KooWFB51PRY9BxcXSH6khFXw1BZeszeLDy7C8GciskqCTZn5": "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", // ed25519+identity multihash + "bafzbeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm": "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", // cidv1 in base32 with libp2p-key multicodec }, } } @@ -82,6 +84,8 @@ func TestNamesysResolution(t *testing.T) { testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 1, "/ipns/ipfs.io", ErrResolveRecursion) testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 2, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", ErrResolveRecursion) testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 3, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", ErrResolveRecursion) + testResolution(t, r, "/ipns/12D3KooWFB51PRY9BxcXSH6khFXw1BZeszeLDy7C8GciskqCTZn5", 1, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", ErrResolveRecursion) + testResolution(t, r, "/ipns/bafzbeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", 1, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", ErrResolveRecursion) } func TestPublishWithCache0(t *testing.T) { diff --git a/namesys/routing.go b/namesys/routing.go index c2d0d0252..60928fbca 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -59,6 +59,7 @@ func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, option } name = strings.TrimPrefix(name, "/ipns/") + pid, err := peer.Decode(name) if err != nil { log.Debugf("RoutingResolver: could not convert public key hash %s to peer ID: %s\n", name, err) From 14036889a512255bf88cf4d0091a65a118b89c69 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 23 Mar 2020 10:56:47 -0700 Subject: [PATCH 3199/3817] doc: fix ci badge in readme This commit was moved from ipfs/go-blockservice@3e11d014808d1708861fbfa7b86bf9977661e96f --- blockservice/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/README.md b/blockservice/README.md index 11d814af2..d36c5cc77 100644 --- a/blockservice/README.md +++ b/blockservice/README.md @@ -5,7 +5,7 @@ go-blockservice [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) [![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) [![Coverage Status](https://codecov.io/gh/ipfs/go-block-format/branch/master/graph/badge.svg)](https://codecov.io/gh/ipfs/go-block-format/branch/master) -[![Travis CI](https://travis-ci.com/ipfs/go-blockservice.svg?branch=master)](https://travis-ci.com/ipfs/go-blockservice) +[![Build Status](https://circleci.com/gh/ipfs/go-blockservice.svg?style=svg)](https://circleci.com/gh/ipfs/go-blockservice) > go-blockservice provides a seamless interface to both local and remote storage backends. From af6b9cf84555d9e0d7db46a2b33e973f321998a3 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 23 Mar 2020 11:00:16 -0700 Subject: [PATCH 3200/3817] chore: fix lint This commit was moved from ipfs/go-blockservice@2bb639009da8f1c8f2ec7e806751869ce01a7ab6 --- blockservice/blockservice_test.go | 21 ++++++++++++++++----- blockservice/test/blocks_test.go | 5 ++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index a94b672cf..dfd12fc43 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -26,12 +26,18 @@ func TestWriteThroughWorks(t *testing.T) { block := bgen.Next() t.Logf("PutCounter: %d", bstore.PutCounter) - bserv.AddBlock(block) + err := bserv.AddBlock(block) + if err != nil { + t.Fatal(err) + } if bstore.PutCounter != 1 { t.Fatalf("expected just one Put call, have: %d", bstore.PutCounter) } - bserv.AddBlock(block) + err = bserv.AddBlock(block) + if err != nil { + t.Fatal(err) + } if bstore.PutCounter != 2 { t.Fatalf("Put should have called again, should be 2 is: %d", bstore.PutCounter) } @@ -52,10 +58,15 @@ func TestLazySessionInitialization(t *testing.T) { bgen := butil.NewBlockGenerator() block := bgen.Next() - bstore.Put(block) - + err := bstore.Put(block) + if err != nil { + t.Fatal(err) + } block2 := bgen.Next() - session.HasBlock(block2) + err = session.HasBlock(block2) + if err != nil { + t.Fatal(err) + } bsession := NewSession(ctx, bservSessEx) if bsession.ses != nil { diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index 95f552d21..ee808e66e 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -74,7 +74,10 @@ func TestGetBlocksSequential(t *testing.T) { var cids []cid.Cid for _, o := range objs { cids = append(cids, o.Cid()) - servs[0].AddBlock(o) + err := servs[0].AddBlock(o) + if err != nil { + t.Fatal(err) + } } t.Log("one instance at a time, get blocks concurrently") From 7402ad31097cee61e1a59469bf82b0081e9170b3 Mon Sep 17 00:00:00 2001 From: Adam Uhlir Date: Thu, 21 Feb 2019 11:53:44 -0800 Subject: [PATCH 3201/3817] Introducing EncodedFSKeystore with base32 encoding (#5947) Encoding the key's filename with base32 introduces coherent behaviour across different platforms and their case-sensitive/case-insensitive file-systems. Moreover it allows wider character set to be used for the name of the keys as the original restriction for special FS's characters (e.g. '/', '.') will not apply. License: MIT Signed-off-by: Adam Uhlir This commit was moved from ipfs/go-ipfs-keystore@5598f9ff89833ecb394d3fff85dce63c01007eb4 --- keystore/keystore.go | 120 ++++++++++++++++++++++++++++++++++++++ keystore/keystore_test.go | 89 ++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+) diff --git a/keystore/keystore.go b/keystore/keystore.go index 991de5dd1..a40ab5cb0 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -9,6 +9,7 @@ import ( logging "github.com/ipfs/go-log" ci "github.com/libp2p/go-libp2p-core/crypto" + base32 "github.com/whyrusleeping/base32" ) var log = logging.Logger("keystore") @@ -52,6 +53,22 @@ func validateName(name string) error { return nil } +// NewKeystore is a factory for getting instance of Keystore interface implementation +func NewKeystore(dir string) (Keystore, error) { + return NewEncodedFSKeystore(dir) +} + +// NewEncodedFSKeystore is a factory for getting instance of EncodedFSKeystore +func NewEncodedFSKeystore(dir string) (*EncodedFSKeystore, error) { + keystore, err := NewFSKeystore(dir) + + if err != nil { + return nil, err + } + + return &EncodedFSKeystore{keystore}, nil +} + func NewFSKeystore(dir string) (*FSKeystore, error) { _, err := os.Stat(dir) if err != nil { @@ -174,3 +191,106 @@ func (ks *FSKeystore) List() ([]string, error) { return list, nil } + +const keyFilenamePrefix = "key_" + +func encode(name string) (string, error) { + if name == "" { + return "", fmt.Errorf("key name must be at least one character") + } + + encodedName := base32.RawStdEncoding.EncodeToString([]byte(name)) + log.Debugf("Encoded key name: %s to: %s", name, encodedName) + + return keyFilenamePrefix + strings.ToLower(encodedName), nil +} + +func decode(name string) (string, error) { + if !strings.HasPrefix(name, keyFilenamePrefix) { + return "", fmt.Errorf("key's filename has unexpected format") + } + + nameWithoutPrefix := strings.ToUpper(name[len(keyFilenamePrefix):]) + data, err := base32.RawStdEncoding.DecodeString(nameWithoutPrefix) + + if err != nil { + return "", err + } + + decodedName := string(data[:]) + + log.Debugf("Decoded key name: %s to: %s", name, decodedName) + + return decodedName, nil +} + +// EncodedFSKeystore is extension of FSKeystore that encodes the key filenames in base32 +type EncodedFSKeystore struct { + *FSKeystore +} + +// Has indicates if key is in keystore +func (ks *EncodedFSKeystore) Has(name string) (bool, error) { + encodedName, err := encode(name) + + if err != nil { + return false, err + } + + return ks.FSKeystore.Has(encodedName) +} + +// Put places key into the keystore +func (ks *EncodedFSKeystore) Put(name string, k ci.PrivKey) error { + encodedName, err := encode(name) + + if err != nil { + return err + } + + return ks.FSKeystore.Put(encodedName, k) +} + +// Get retrieves key by its name from the keystore +func (ks *EncodedFSKeystore) Get(name string) (ci.PrivKey, error) { + encodedName, err := encode(name) + + if err != nil { + return nil, err + } + + return ks.FSKeystore.Get(encodedName) +} + +// Delete removes key from the keystore +func (ks *EncodedFSKeystore) Delete(name string) error { + encodedName, err := encode(name) + + if err != nil { + return err + } + + return ks.FSKeystore.Delete(encodedName) +} + +// List returns list of all keys in keystore +func (ks *EncodedFSKeystore) List() ([]string, error) { + dirs, err := ks.FSKeystore.List() + + if err != nil { + return nil, err + } + + list := make([]string, 0, len(dirs)) + + for _, name := range dirs { + decodedName, err := decode(name) + if err == nil { + list = append(list, decodedName) + } else { + log.Warningf("Ignoring keyfile with invalid encoded filename: %s", name) + } + } + + return list, nil +} diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index 37f59ebff..685e5d942 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -271,3 +271,92 @@ func assertDirContents(dir string, exp []string) error { } return nil } + +func TestEncodedKeystoreBasics(t *testing.T) { + tdir, err := ioutil.TempDir("", "encoded-keystore-test") + if err != nil { + t.Fatal(err) + } + + ks, err := NewEncodedFSKeystore(tdir) + if err != nil { + t.Fatal(err) + } + + l, err := ks.List() + if err != nil { + t.Fatal(err) + } + + if len(l) != 0 { + t.Fatal("expected no keys") + } + + k1 := privKeyOrFatal(t) + k1Name, err := encode("foo") + if err != nil { + t.Fatal(err) + } + + k2 := privKeyOrFatal(t) + k2Name, err := encode("bar") + if err != nil { + t.Fatal(err) + } + + err = ks.Put("foo", k1) + if err != nil { + t.Fatal(err) + } + + err = ks.Put("bar", k2) + if err != nil { + t.Fatal(err) + } + + l, err = ks.List() + if err != nil { + t.Fatal(err) + } + + sort.Strings(l) + if l[0] != "bar" || l[1] != "foo" { + t.Fatal("wrong entries listed") + } + + if err := assertDirContents(tdir, []string{k1Name, k2Name}); err != nil { + t.Fatal(err) + } + + exist, err := ks.Has("foo") + if !exist { + t.Fatal("should know it has a key named foo") + } + if err != nil { + t.Fatal(err) + } + + if err := ks.Delete("bar"); err != nil { + t.Fatal(err) + } + + if err := assertDirContents(tdir, []string{k1Name}); err != nil { + t.Fatal(err) + } + + if err := assertGetKey(ks, "foo", k1); err != nil { + t.Fatal(err) + } + + if err := ks.Put("..///foo/", k1); err != nil { + t.Fatal(err) + } + + if err := ks.Put("", k1); err == nil { + t.Fatal("shouldnt be able to put a key with no name") + } + + if err := ks.Put(".foo", k1); err != nil { + t.Fatal(err) + } +} From dc82d4467cd7ee394fe12e281a7dd2351fba7b96 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 5 Mar 2020 14:28:02 +0100 Subject: [PATCH 3202/3817] keystore: finish addressing encodedFSKeystore * Use Go's base32 library * Set repo to version 9 * Resolve linting problems and docs. * Merge EncodedFSKeystore into FSKeystore * Remove name limitations and adjust tests This commit was moved from ipfs/go-ipfs-keystore@5f369fe96c2ebcf8526893f93236a1794331fb69 --- keystore/keystore.go | 158 +++++++---------------------------- keystore/keystore_test.go | 120 ++++---------------------- keystore/memkeystore.go | 19 ++--- keystore/memkeystore_test.go | 8 +- 4 files changed, 59 insertions(+), 246 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index a40ab5cb0..463f90e00 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -7,13 +7,16 @@ import ( "path/filepath" "strings" + base32 "encoding/base32" + logging "github.com/ipfs/go-log" ci "github.com/libp2p/go-libp2p-core/crypto" - base32 "github.com/whyrusleeping/base32" ) var log = logging.Logger("keystore") +var codec = base32.StdEncoding.WithPadding(base32.NoPadding) + // Keystore provides a key management interface type Keystore interface { // Has returns whether or not a key exist in the Keystore @@ -29,46 +32,20 @@ type Keystore interface { List() ([]string, error) } +// ErrNoSuchKey is an error message returned when no key of a given name was found. var ErrNoSuchKey = fmt.Errorf("no key by the given name was found") + +// ErrKeyExists is an error message returned when a key already exists var ErrKeyExists = fmt.Errorf("key by that name already exists, refusing to overwrite") +const keyFilenamePrefix = "key_" + // FSKeystore is a keystore backed by files in a given directory stored on disk. type FSKeystore struct { dir string } -func validateName(name string) error { - if name == "" { - return fmt.Errorf("key names must be at least one character") - } - - if strings.Contains(name, "/") { - return fmt.Errorf("key names may not contain slashes") - } - - if strings.HasPrefix(name, ".") { - return fmt.Errorf("key names may not begin with a period") - } - - return nil -} - -// NewKeystore is a factory for getting instance of Keystore interface implementation -func NewKeystore(dir string) (Keystore, error) { - return NewEncodedFSKeystore(dir) -} - -// NewEncodedFSKeystore is a factory for getting instance of EncodedFSKeystore -func NewEncodedFSKeystore(dir string) (*EncodedFSKeystore, error) { - keystore, err := NewFSKeystore(dir) - - if err != nil { - return nil, err - } - - return &EncodedFSKeystore{keystore}, nil -} - +// NewFSKeystore returns a new filesystem-backed keystore. func NewFSKeystore(dir string) (*FSKeystore, error) { _, err := os.Stat(dir) if err != nil { @@ -85,28 +62,25 @@ func NewFSKeystore(dir string) (*FSKeystore, error) { // Has returns whether or not a key exist in the Keystore func (ks *FSKeystore) Has(name string) (bool, error) { + name, err := encode(name) + if err != nil { + return false, err + } + kp := filepath.Join(ks.dir, name) - _, err := os.Stat(kp) + _, err = os.Stat(kp) if os.IsNotExist(err) { return false, nil } - - if err != nil { - return false, err - } - - if err := validateName(name); err != nil { - return false, err - } - - return true, nil + return err == nil, err } // Put stores a key in the Keystore, if a key with the same name already exists, returns ErrKeyExists func (ks *FSKeystore) Put(name string, k ci.PrivKey) error { - if err := validateName(name); err != nil { + name, err := encode(name) + if err != nil { return err } @@ -138,7 +112,8 @@ func (ks *FSKeystore) Put(name string, k ci.PrivKey) error { // Get retrieves a key from the Keystore if it exists, and returns ErrNoSuchKey // otherwise. func (ks *FSKeystore) Get(name string) (ci.PrivKey, error) { - if err := validateName(name); err != nil { + name, err := encode(name) + if err != nil { return nil, err } @@ -157,7 +132,8 @@ func (ks *FSKeystore) Get(name string) (ci.PrivKey, error) { // Delete removes a key from the Keystore func (ks *FSKeystore) Delete(name string) error { - if err := validateName(name); err != nil { + name, err := encode(name) + if err != nil { return err } @@ -181,25 +157,23 @@ func (ks *FSKeystore) List() ([]string, error) { list := make([]string, 0, len(dirs)) for _, name := range dirs { - err := validateName(name) + decodedName, err := decode(name) if err == nil { - list = append(list, name) + list = append(list, decodedName) } else { - log.Warnf("Ignoring the invalid keyfile: %s", name) + log.Errorf("Ignoring keyfile with invalid encoded filename: %s", name) } } return list, nil } -const keyFilenamePrefix = "key_" - func encode(name string) (string, error) { if name == "" { return "", fmt.Errorf("key name must be at least one character") } - encodedName := base32.RawStdEncoding.EncodeToString([]byte(name)) + encodedName := codec.EncodeToString([]byte(name)) log.Debugf("Encoded key name: %s to: %s", name, encodedName) return keyFilenamePrefix + strings.ToLower(encodedName), nil @@ -211,86 +185,12 @@ func decode(name string) (string, error) { } nameWithoutPrefix := strings.ToUpper(name[len(keyFilenamePrefix):]) - data, err := base32.RawStdEncoding.DecodeString(nameWithoutPrefix) - + decodedName, err := codec.DecodeString(nameWithoutPrefix) if err != nil { return "", err } - decodedName := string(data[:]) - log.Debugf("Decoded key name: %s to: %s", name, decodedName) - return decodedName, nil -} - -// EncodedFSKeystore is extension of FSKeystore that encodes the key filenames in base32 -type EncodedFSKeystore struct { - *FSKeystore -} - -// Has indicates if key is in keystore -func (ks *EncodedFSKeystore) Has(name string) (bool, error) { - encodedName, err := encode(name) - - if err != nil { - return false, err - } - - return ks.FSKeystore.Has(encodedName) -} - -// Put places key into the keystore -func (ks *EncodedFSKeystore) Put(name string, k ci.PrivKey) error { - encodedName, err := encode(name) - - if err != nil { - return err - } - - return ks.FSKeystore.Put(encodedName, k) -} - -// Get retrieves key by its name from the keystore -func (ks *EncodedFSKeystore) Get(name string) (ci.PrivKey, error) { - encodedName, err := encode(name) - - if err != nil { - return nil, err - } - - return ks.FSKeystore.Get(encodedName) -} - -// Delete removes key from the keystore -func (ks *EncodedFSKeystore) Delete(name string) error { - encodedName, err := encode(name) - - if err != nil { - return err - } - - return ks.FSKeystore.Delete(encodedName) -} - -// List returns list of all keys in keystore -func (ks *EncodedFSKeystore) List() ([]string, error) { - dirs, err := ks.FSKeystore.List() - - if err != nil { - return nil, err - } - - list := make([]string, 0, len(dirs)) - - for _, name := range dirs { - decodedName, err := decode(name) - if err == nil { - list = append(list, decodedName) - } else { - log.Warningf("Ignoring keyfile with invalid encoded filename: %s", name) - } - } - - return list, nil + return string(decodedName), nil } diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index 685e5d942..2a48b43e5 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -132,16 +132,16 @@ func TestKeystoreBasics(t *testing.T) { t.Fatal(err) } - if err := ks.Put("..///foo/", k1); err == nil { - t.Fatal("shouldnt be able to put a poorly named key") + if err := ks.Put("..///foo/", k1); err != nil { + t.Fatal(err) } if err := ks.Put("", k1); err == nil { t.Fatal("shouldnt be able to put a key with no name") } - if err := ks.Put(".foo", k1); err == nil { - t.Fatal("shouldnt be able to put a key with a 'hidden' name") + if err := ks.Put(".foo", k1); err != nil { + t.Fatal(err) } } @@ -166,12 +166,17 @@ func TestInvalidKeyFiles(t *testing.T) { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(ks.dir, "valid"), bytes, 0644) + encodedName, err := encode("valid") + if err != nil { + t.Fatal(err) + } + + err = ioutil.WriteFile(filepath.Join(ks.dir, encodedName), bytes, 0644) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(ks.dir, ".invalid"), bytes, 0644) + err = ioutil.WriteFile(filepath.Join(ks.dir, "z.invalid"), bytes, 0644) if err != nil { t.Fatal(err) } @@ -197,10 +202,6 @@ func TestInvalidKeyFiles(t *testing.T) { if err != nil { t.Fatal(err) } - - if _, err = ks.Has(".invalid"); err == nil { - t.Fatal("shouldnt be able to put a key with a 'hidden' name") - } } func TestNonExistingKey(t *testing.T) { @@ -231,12 +232,12 @@ func TestMakeKeystoreNoDir(t *testing.T) { } func assertGetKey(ks Keystore, name string, exp ci.PrivKey) error { - out_k, err := ks.Get(name) + outK, err := ks.Get(name) if err != nil { return err } - if !out_k.Equals(exp) { + if !outK.Equals(exp) { return fmt.Errorf("key we got out didnt match expectation") } @@ -255,7 +256,11 @@ func assertDirContents(dir string, exp []string) error { var names []string for _, fi := range finfos { - names = append(names, fi.Name()) + decodedName, err := decode(fi.Name()) + if err != nil { + return err + } + names = append(names, decodedName) } sort.Strings(names) @@ -271,92 +276,3 @@ func assertDirContents(dir string, exp []string) error { } return nil } - -func TestEncodedKeystoreBasics(t *testing.T) { - tdir, err := ioutil.TempDir("", "encoded-keystore-test") - if err != nil { - t.Fatal(err) - } - - ks, err := NewEncodedFSKeystore(tdir) - if err != nil { - t.Fatal(err) - } - - l, err := ks.List() - if err != nil { - t.Fatal(err) - } - - if len(l) != 0 { - t.Fatal("expected no keys") - } - - k1 := privKeyOrFatal(t) - k1Name, err := encode("foo") - if err != nil { - t.Fatal(err) - } - - k2 := privKeyOrFatal(t) - k2Name, err := encode("bar") - if err != nil { - t.Fatal(err) - } - - err = ks.Put("foo", k1) - if err != nil { - t.Fatal(err) - } - - err = ks.Put("bar", k2) - if err != nil { - t.Fatal(err) - } - - l, err = ks.List() - if err != nil { - t.Fatal(err) - } - - sort.Strings(l) - if l[0] != "bar" || l[1] != "foo" { - t.Fatal("wrong entries listed") - } - - if err := assertDirContents(tdir, []string{k1Name, k2Name}); err != nil { - t.Fatal(err) - } - - exist, err := ks.Has("foo") - if !exist { - t.Fatal("should know it has a key named foo") - } - if err != nil { - t.Fatal(err) - } - - if err := ks.Delete("bar"); err != nil { - t.Fatal(err) - } - - if err := assertDirContents(tdir, []string{k1Name}); err != nil { - t.Fatal(err) - } - - if err := assertGetKey(ks, "foo", k1); err != nil { - t.Fatal(err) - } - - if err := ks.Put("..///foo/", k1); err != nil { - t.Fatal(err) - } - - if err := ks.Put("", k1); err == nil { - t.Fatal("shouldnt be able to put a key with no name") - } - - if err := ks.Put(".foo", k1); err != nil { - t.Fatal(err) - } -} diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index 4067bbce2..c96985252 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -1,6 +1,10 @@ package keystore -import ci "github.com/libp2p/go-libp2p-core/crypto" +import ( + "errors" + + ci "github.com/libp2p/go-libp2p-core/crypto" +) // MemKeystore is an in memory keystore implementation that is not persisted to // any backing storage. @@ -8,6 +12,7 @@ type MemKeystore struct { keys map[string]ci.PrivKey } +// NewMemKeystore creates a MemKeystore. func NewMemKeystore() *MemKeystore { return &MemKeystore{make(map[string]ci.PrivKey)} } @@ -20,8 +25,8 @@ func (mk *MemKeystore) Has(name string) (bool, error) { // Put store a key in the Keystore func (mk *MemKeystore) Put(name string, k ci.PrivKey) error { - if err := validateName(name); err != nil { - return err + if name == "" { + return errors.New("key name must be at least one character") } _, ok := mk.keys[name] @@ -35,10 +40,6 @@ func (mk *MemKeystore) Put(name string, k ci.PrivKey) error { // Get retrieve a key from the Keystore func (mk *MemKeystore) Get(name string) (ci.PrivKey, error) { - if err := validateName(name); err != nil { - return nil, err - } - k, ok := mk.keys[name] if !ok { return nil, ErrNoSuchKey @@ -49,10 +50,6 @@ func (mk *MemKeystore) Get(name string) (ci.PrivKey, error) { // Delete remove a key from the Keystore func (mk *MemKeystore) Delete(name string) error { - if err := validateName(name); err != nil { - return err - } - delete(mk.keys, name) return nil } diff --git a/keystore/memkeystore_test.go b/keystore/memkeystore_test.go index 62533d54b..a7214893a 100644 --- a/keystore/memkeystore_test.go +++ b/keystore/memkeystore_test.go @@ -85,15 +85,15 @@ func TestMemKeyStoreBasics(t *testing.T) { t.Fatal(err) } - if err := ks.Put("..///foo/", k1); err == nil { - t.Fatal("shouldnt be able to put a poorly named key") + if err := ks.Put("..///foo/", k1); err != nil { + t.Fatal(err) } if err := ks.Put("", k1); err == nil { t.Fatal("shouldnt be able to put a key with no name") } - if err := ks.Put(".foo", k1); err == nil { - t.Fatal("shouldnt be able to put a key with a 'hidden' name") + if err := ks.Put(".foo", k1); err != nil { + t.Fatal(err) } } From bb83dc670859ca43aa219ff6585664adcd32dfe3 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 15 Mar 2020 23:40:01 -0700 Subject: [PATCH 3203/3817] fix(keystore): avoid racy filesystem access Instead of checking then performing a file operation, perform the file operation and check the error. This commit was moved from ipfs/go-ipfs-keystore@67213bfcb798335e8e7613df8bf2ebf76f62c309 --- keystore/keystore.go | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index 463f90e00..a52b4ad17 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -47,16 +47,13 @@ type FSKeystore struct { // NewFSKeystore returns a new filesystem-backed keystore. func NewFSKeystore(dir string) (*FSKeystore, error) { - _, err := os.Stat(dir) - if err != nil { - if !os.IsNotExist(err) { - return nil, err - } - if err := os.Mkdir(dir, 0700); err != nil { - return nil, err - } + err := os.Mkdir(dir, 0700) + switch { + case os.IsExist(err): + case err == nil: + default: + return nil, err } - return &FSKeystore{dir}, nil } @@ -91,15 +88,11 @@ func (ks *FSKeystore) Put(name string, k ci.PrivKey) error { kp := filepath.Join(ks.dir, name) - _, err = os.Stat(kp) - if err == nil { - return ErrKeyExists - } else if !os.IsNotExist(err) { - return err - } - - fi, err := os.Create(kp) + fi, err := os.OpenFile(kp, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600) if err != nil { + if os.IsExist(err) { + err = ErrKeyExists + } return err } defer fi.Close() From 679311d2594a84cc4d389064143a8fd769156f5b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 25 Mar 2020 19:14:24 -0700 Subject: [PATCH 3204/3817] fix: don't return an empty block at the end This commit was moved from ipfs/go-ipfs-chunker@ca4d27be5effeb297326ab2220433069d9d6cb1d --- chunker/buzhash.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/chunker/buzhash.go b/chunker/buzhash.go index b3de95f12..83ab019dd 100644 --- a/chunker/buzhash.go +++ b/chunker/buzhash.go @@ -40,9 +40,16 @@ func (b *Buzhash) NextBytes() ([]byte, error) { n, err := io.ReadFull(b.r, b.buf[b.n:]) if err != nil { if err == io.ErrUnexpectedEOF || err == io.EOF { - if b.n+n < buzMin { + buffered := b.n + n + if buffered < buzMin { b.err = io.EOF - res := make([]byte, b.n+n) + // Read nothing? Don't return an empty block. + if buffered == 0 { + pool.Put(b.buf) + b.buf = nil + return nil, b.err + } + res := make([]byte, buffered) copy(res, b.buf) pool.Put(b.buf) From 358dbca7d9c622fb168965b33256a3fe4a4f14e4 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 25 Mar 2020 19:18:06 -0700 Subject: [PATCH 3205/3817] test(buzhash): fuzz This commit was moved from ipfs/go-ipfs-chunker@b4e4e73e00441494d9240b64386b95aaee77f1a8 --- chunker/buzhash_test.go | 52 ++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/chunker/buzhash_test.go b/chunker/buzhash_test.go index f630cef89..07573bab6 100644 --- a/chunker/buzhash_test.go +++ b/chunker/buzhash_test.go @@ -2,7 +2,6 @@ package chunk import ( "bytes" - "fmt" "io" "testing" @@ -11,33 +10,48 @@ import ( func TestBuzhashChunking(t *testing.T) { data := make([]byte, 1024*1024*16) - util.NewTimeSeededRand().Read(data) - r := NewBuzhash(bytes.NewReader(data)) + chunkCount := 0 + rounds := 100 - var chunks [][]byte + for i := 0; i < rounds; i++ { + util.NewTimeSeededRand().Read(data) - for { - chunk, err := r.NextBytes() - if err != nil { - if err == io.EOF { - break + r := NewBuzhash(bytes.NewReader(data)) + + var chunks [][]byte + + for { + chunk, err := r.NextBytes() + if err != nil { + if err == io.EOF { + break + } + t.Fatal(err) } - t.Fatal(err) + + chunks = append(chunks, chunk) } + chunkCount += len(chunks) - chunks = append(chunks, chunk) - } + for i, chunk := range chunks { + if len(chunk) == 0 { + t.Fatalf("chunk %d/%d is empty", i+1, len(chunks)) + } + } - t.Logf("average block size: %d\n", len(data)/len(chunks)) + for i, chunk := range chunks[:len(chunks)-1] { + if len(chunk) < buzMin { + t.Fatalf("chunk %d/%d is less than the minimum size", i+1, len(chunks)) + } + } - unchunked := bytes.Join(chunks, nil) - if !bytes.Equal(unchunked, data) { - fmt.Printf("%d %d\n", len(unchunked), len(data)) - //ioutil.WriteFile("./incorrect", unchunked, 0777) - //ioutil.WriteFile("./correct", data, 0777) - t.Fatal("data was chunked incorrectly") + unchunked := bytes.Join(chunks, nil) + if !bytes.Equal(unchunked, data) { + t.Fatal("data was chunked incorrectly") + } } + t.Logf("average block size: %d\n", len(data)*rounds/chunkCount) } func TestBuzhashChunkReuse(t *testing.T) { From 1d71592e90a56923f129707d61430355b7e27181 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 26 Mar 2020 13:47:29 -0700 Subject: [PATCH 3206/3817] test: avoid fuzzing while the race detector is enabled It's too slow. This commit was moved from ipfs/go-ipfs-chunker@cbf45fd253b8798d4fa12574260f914ca5d8fc59 --- chunker/buzhash_norace_test.go | 14 ++++++++ chunker/buzhash_test.go | 66 +++++++++++++++++----------------- 2 files changed, 47 insertions(+), 33 deletions(-) create mode 100644 chunker/buzhash_norace_test.go diff --git a/chunker/buzhash_norace_test.go b/chunker/buzhash_norace_test.go new file mode 100644 index 000000000..2565a4c53 --- /dev/null +++ b/chunker/buzhash_norace_test.go @@ -0,0 +1,14 @@ +//+build !race + +package chunk + +import ( + "testing" +) + +func TestFuzzBuzhashChunking(t *testing.T) { + buf := make([]byte, 1024*1024*16) + for i := 0; i < 100; i++ { + testBuzhashChunking(t, buf) + } +} diff --git a/chunker/buzhash_test.go b/chunker/buzhash_test.go index 07573bab6..931f23574 100644 --- a/chunker/buzhash_test.go +++ b/chunker/buzhash_test.go @@ -8,50 +8,50 @@ import ( util "github.com/ipfs/go-ipfs-util" ) -func TestBuzhashChunking(t *testing.T) { - data := make([]byte, 1024*1024*16) - - chunkCount := 0 - rounds := 100 +func testBuzhashChunking(t *testing.T, buf []byte) (chunkCount int) { + util.NewTimeSeededRand().Read(buf) - for i := 0; i < rounds; i++ { - util.NewTimeSeededRand().Read(data) + r := NewBuzhash(bytes.NewReader(buf)) - r := NewBuzhash(bytes.NewReader(data)) + var chunks [][]byte - var chunks [][]byte - - for { - chunk, err := r.NextBytes() - if err != nil { - if err == io.EOF { - break - } - t.Fatal(err) + for { + chunk, err := r.NextBytes() + if err != nil { + if err == io.EOF { + break } - - chunks = append(chunks, chunk) + t.Fatal(err) } - chunkCount += len(chunks) - for i, chunk := range chunks { - if len(chunk) == 0 { - t.Fatalf("chunk %d/%d is empty", i+1, len(chunks)) - } - } + chunks = append(chunks, chunk) + } + chunkCount += len(chunks) - for i, chunk := range chunks[:len(chunks)-1] { - if len(chunk) < buzMin { - t.Fatalf("chunk %d/%d is less than the minimum size", i+1, len(chunks)) - } + for i, chunk := range chunks { + if len(chunk) == 0 { + t.Fatalf("chunk %d/%d is empty", i+1, len(chunks)) } + } - unchunked := bytes.Join(chunks, nil) - if !bytes.Equal(unchunked, data) { - t.Fatal("data was chunked incorrectly") + for i, chunk := range chunks[:len(chunks)-1] { + if len(chunk) < buzMin { + t.Fatalf("chunk %d/%d is less than the minimum size", i+1, len(chunks)) } } - t.Logf("average block size: %d\n", len(data)*rounds/chunkCount) + + unchunked := bytes.Join(chunks, nil) + if !bytes.Equal(unchunked, buf) { + t.Fatal("data was chunked incorrectly") + } + + return chunkCount +} + +func TestBuzhashChunking(t *testing.T) { + buf := make([]byte, 1024*1024*16) + count := testBuzhashChunking(t, buf) + t.Logf("average block size: %d\n", len(buf)/count) } func TestBuzhashChunkReuse(t *testing.T) { From 4892b01788835015cc53c7bf7d337881dcd9ef5f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 26 Mar 2020 13:50:09 -0700 Subject: [PATCH 3207/3817] chore: fix lints This commit was moved from ipfs/go-ipfs-chunker@cbd0b2e188ecd56b6bb350b2901d8d6334d8669f --- chunker/buzhash_test.go | 8 +++++++- chunker/rabin_test.go | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/chunker/buzhash_test.go b/chunker/buzhash_test.go index 931f23574..05ad7c380 100644 --- a/chunker/buzhash_test.go +++ b/chunker/buzhash_test.go @@ -9,7 +9,13 @@ import ( ) func testBuzhashChunking(t *testing.T, buf []byte) (chunkCount int) { - util.NewTimeSeededRand().Read(buf) + n, err := util.NewTimeSeededRand().Read(buf) + if n < len(buf) { + t.Fatalf("expected %d bytes, got %d", len(buf), n) + } + if err != nil { + t.Fatal(err) + } r := NewBuzhash(bytes.NewReader(buf)) diff --git a/chunker/rabin_test.go b/chunker/rabin_test.go index 857e97c2c..2e19a82c7 100644 --- a/chunker/rabin_test.go +++ b/chunker/rabin_test.go @@ -12,7 +12,13 @@ import ( func TestRabinChunking(t *testing.T) { data := make([]byte, 1024*1024*16) - util.NewTimeSeededRand().Read(data) + n, err := util.NewTimeSeededRand().Read(data) + if n < len(data) { + t.Fatalf("expected %d bytes, got %d", len(data), n) + } + if err != nil { + t.Fatal(err) + } r := NewRabin(bytes.NewReader(data), 1024*256) @@ -62,7 +68,13 @@ func chunkData(t *testing.T, newC newSplitter, data []byte) map[string]blocks.Bl func testReuse(t *testing.T, cr newSplitter) { data := make([]byte, 1024*1024*16) - util.NewTimeSeededRand().Read(data) + n, err := util.NewTimeSeededRand().Read(data) + if n < len(data) { + t.Fatalf("expected %d bytes, got %d", len(data), n) + } + if err != nil { + t.Fatal(err) + } ch1 := chunkData(t, cr, data[1000:]) ch2 := chunkData(t, cr, data) From 32c5c58cd8b79c26e1151e045e3455dc6e668f44 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 27 Mar 2020 12:57:53 +0100 Subject: [PATCH 3208/3817] keystore: Switch from Bytes() to MarshalPrivateKey() Bytes is deprecated. This commit was moved from ipfs/go-ipfs-keystore@ba3f6ad628c52ff4437b8ac8ac586cebbcedb385 --- keystore/keystore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index a52b4ad17..ed83c17e6 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -81,7 +81,7 @@ func (ks *FSKeystore) Put(name string, k ci.PrivKey) error { return err } - b, err := k.Bytes() + b, err := ci.MarshalPrivateKey(k) if err != nil { return err } From 44b2237aeb926a4536338395defb278544f0e1be Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 27 Mar 2020 12:59:10 +0100 Subject: [PATCH 3209/3817] keystore: create new keys with 0400 permissions (as spec'ed) Spec is pretty much out of date but specifies this. https://github.com/ipfs/specs/blob/master/KEYSTORE.md This commit was moved from ipfs/go-ipfs-keystore@a8fef3d240997647efd00ce52c26bb543963de38 --- keystore/keystore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index ed83c17e6..0a2fed3bf 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -88,7 +88,7 @@ func (ks *FSKeystore) Put(name string, k ci.PrivKey) error { kp := filepath.Join(ks.dir, name) - fi, err := os.OpenFile(kp, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600) + fi, err := os.OpenFile(kp, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0400) if err != nil { if os.IsExist(err) { err = ErrKeyExists From a5d1ab5c001725e595b97fdce884d8b39db72cec Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 27 Mar 2020 10:32:42 -0700 Subject: [PATCH 3210/3817] feat: only require the NodeGetter interface instead of the DAG interface This makes it possible to pass in a dagservice "session" for better performance when fetching from bitswap. This commit was moved from ipld/go-car@8a3014575e8e7f9c73c9bf7882256b36528b390d --- ipld/car/car.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index bce59d347..9f7e6d10d 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -33,18 +33,18 @@ type CarHeader struct { } type carWriter struct { - ds format.DAGService + ds format.NodeGetter w io.Writer walk WalkFunc } type WalkFunc func(format.Node) ([]*format.Link, error) -func WriteCar(ctx context.Context, ds format.DAGService, roots []cid.Cid, w io.Writer) error { +func WriteCar(ctx context.Context, ds format.NodeGetter, roots []cid.Cid, w io.Writer) error { return WriteCarWithWalker(ctx, ds, roots, w, DefaultWalkFunc) } -func WriteCarWithWalker(ctx context.Context, ds format.DAGService, roots []cid.Cid, w io.Writer, walk WalkFunc) error { +func WriteCarWithWalker(ctx context.Context, ds format.NodeGetter, roots []cid.Cid, w io.Writer, walk WalkFunc) error { h := &CarHeader{ Roots: roots, From bffa011f0a0f01646bcecbc003552941ca31860b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Mon, 30 Mar 2020 16:20:15 +0200 Subject: [PATCH 3211/3817] pin: better doc, small cleaning This commit was moved from ipfs/interface-go-ipfs-core@478caf05ab8fd3b33ae80f8792be2cb7c7a92b45 --- coreiface/options/pin.go | 32 +++++++++++++++++++++----------- coreiface/tests/pin.go | 2 +- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/coreiface/options/pin.go b/coreiface/options/pin.go index 231f0d11a..5014a2d2b 100644 --- a/coreiface/options/pin.go +++ b/coreiface/options/pin.go @@ -2,46 +2,48 @@ package options import "fmt" +// PinAddSettings represent the settings for PinAPI.Add type PinAddSettings struct { Recursive bool } -type TypeSettings struct { - Type string -} - +// PinLsSettings represent the settings for PinAPI.Ls type PinLsSettings struct { Type string } +// PinIsPinnedSettings represent the settings for PinAPI.IsPinned type PinIsPinnedSettings struct { WithType string } -// PinRmSettings represents the settings of pin rm command +// PinRmSettings represents the settings for PinAPI.Rm type PinRmSettings struct { Recursive bool } +// PinUpdateSettings represent the settings for PinAPI.Update type PinUpdateSettings struct { Unpin bool } -// PinAddOption pin add option func +// PinAddOption is the signature of an option for PinAPI.Add type PinAddOption func(*PinAddSettings) error -// PinLsOption pin ls option func +// PinLsOption is the signature of an option for PinAPI.Ls type PinLsOption func(*PinLsSettings) error -// PinIsPinnedOption pin isPinned option func +// PinIsPinnedOption is the signature of an option for PinAPI.IsPinned type PinIsPinnedOption func(*PinIsPinnedSettings) error -// PinRmOption pin rm option func +// PinRmOption is the signature of an option for PinAPI.Rm type PinRmOption func(*PinRmSettings) error -// PinUpdateOption pin update option func +// PinUpdateOption is the signature of an option for PinAPI.Update type PinUpdateOption func(*PinUpdateSettings) error +// PinAddOptions compile a series of PinAddOption into a ready to use +// PinAddSettings and set the default values. func PinAddOptions(opts ...PinAddOption) (*PinAddSettings, error) { options := &PinAddSettings{ Recursive: true, @@ -57,6 +59,8 @@ func PinAddOptions(opts ...PinAddOption) (*PinAddSettings, error) { return options, nil } +// PinLsOptions compile a series of PinLsOption into a ready to use +// PinLsSettings and set the default values. func PinLsOptions(opts ...PinLsOption) (*PinLsSettings, error) { options := &PinLsSettings{ Type: "all", @@ -72,6 +76,8 @@ func PinLsOptions(opts ...PinLsOption) (*PinLsSettings, error) { return options, nil } +// PinIsPinnedOptions compile a series of PinIsPinnedOption into a ready to use +// PinIsPinnedSettings and set the default values. func PinIsPinnedOptions(opts ...PinIsPinnedOption) (*PinIsPinnedSettings, error) { options := &PinIsPinnedSettings{ WithType: "all", @@ -87,7 +93,8 @@ func PinIsPinnedOptions(opts ...PinIsPinnedOption) (*PinIsPinnedSettings, error) return options, nil } -// PinRmOptions pin rm options +// PinRmOptions compile a series of PinRmOption into a ready to use +// PinRmSettings and set the default values. func PinRmOptions(opts ...PinRmOption) (*PinRmSettings, error) { options := &PinRmSettings{ Recursive: true, @@ -102,6 +109,8 @@ func PinRmOptions(opts ...PinRmOption) (*PinRmSettings, error) { return options, nil } +// PinUpdateOptions compile a series of PinUpdateOption into a ready to use +// PinUpdateSettings and set the default values. func PinUpdateOptions(opts ...PinUpdateOption) (*PinUpdateSettings, error) { options := &PinUpdateSettings{ Unpin: true, @@ -122,6 +131,7 @@ type pinOpts struct { IsPinned pinIsPinnedOpts } +// Pin provide an access to all the options for the Pin API. var Pin pinOpts type pinLsOpts struct{} diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index e16d6460b..476bbea6b 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -546,7 +546,7 @@ func assertIsPinned(t *testing.T, ctx context.Context, api iface.CoreAPI, p path t.Helper() withType, err := opt.Pin.IsPinned.Type(typeStr) if err != nil { - panic("unhandled pin type") + t.Fatal("unhandled pin type") } whyPinned, pinned, err := api.Pin().IsPinned(ctx, p, withType) From 315b4efaaf44edd8fce692f5d25a7a1df1a2bcf2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 6 Apr 2020 12:46:59 -0700 Subject: [PATCH 3212/3817] fix: handle closed queue When the queue closes, return instead of providing empty CIDs. This commit was moved from ipfs/go-ipfs-provider@596dc4996bf72ac8bf1d7c7bb3543310b0bad811 --- provider/simple/provider.go | 7 ++++++- provider/simple/provider_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/provider/simple/provider.go b/provider/simple/provider.go index f421f6195..6c50ef925 100644 --- a/provider/simple/provider.go +++ b/provider/simple/provider.go @@ -85,7 +85,12 @@ func (p *Provider) handleAnnouncements() { select { case <-p.ctx.Done(): return - case c := <-p.queue.Dequeue(): + case c, ok := <-p.queue.Dequeue(): + if !ok { + // queue closed. + return + } + p.doProvide(c) } } diff --git a/provider/simple/provider_test.go b/provider/simple/provider_test.go index deb0032ec..d8dbf96f0 100644 --- a/provider/simple/provider_test.go +++ b/provider/simple/provider_test.go @@ -84,6 +84,37 @@ func TestAnnouncement(t *testing.T) { t.Fatal("Timeout waiting for cids to be provided.") } } + prov.Close() + + select { + case cp := <-r.provided: + t.Fatal("did not expect to provide CID: ", cp) + case <-time.After(time.Second * 1): + } +} + +func TestClose(t *testing.T) { + ctx := context.Background() + defer ctx.Done() + + ds := sync.MutexWrap(datastore.NewMapDatastore()) + queue, err := q.NewQueue(ctx, "test", ds) + if err != nil { + t.Fatal(err) + } + + r := mockContentRouting() + + prov := NewProvider(ctx, queue, r) + prov.Run() + + prov.Close() + + select { + case cp := <-r.provided: + t.Fatal("did not expect to provide anything, provided: ", cp) + case <-time.After(time.Second * 1): + } } func TestAnnouncementTimeout(t *testing.T) { From 0f975788ef4dd78c9ac371cae6f247aa8855a518 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 19 Mar 2020 18:38:36 -0700 Subject: [PATCH 3213/3817] assign public IP addresses for tests that need them This commit was moved from ipfs/go-namesys@779a4400ff90cea88214f4ef440475fd06c314f5 --- namesys/republisher/repub_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 5fedc3907..fd946501e 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -29,10 +29,7 @@ func TestRepublish(t *testing.T) { var nodes []*core.IpfsNode for i := 0; i < 10; i++ { - nd, err := core.NewNode(ctx, &core.BuildCfg{ - Online: true, - Host: mock.MockHostOption(mn), - }) + nd, err := mock.MockPublicNode(ctx, mn) if err != nil { t.Fatal(err) } From 960beb2b79e906551a219fdaf70fac7f5015615d Mon Sep 17 00:00:00 2001 From: postables Date: Sun, 12 Apr 2020 00:20:16 -0700 Subject: [PATCH 3214/3817] add race fix for HashOnRead This commit was moved from ipfs/go-ipfs-blockstore@2bd301c7765e953229c91d355d2155c5b93820d7 --- blockstore/blockstore.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index e815642da..f8eb07a7d 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -15,6 +15,7 @@ import ( dsq "github.com/ipfs/go-datastore/query" dshelp "github.com/ipfs/go-ipfs-ds-help" logging "github.com/ipfs/go-log" + uatomic "go.uber.org/atomic" ) var log = logging.Logger("blockstore") @@ -101,17 +102,18 @@ func NewBlockstore(d ds.Batching) Blockstore { dsb = dd return &blockstore{ datastore: dsb, + rehash: uatomic.NewBool(false), } } type blockstore struct { datastore ds.Batching - rehash bool + rehash *uatomic.Bool } func (bs *blockstore) HashOnRead(enabled bool) { - bs.rehash = enabled + bs.rehash.Store(enabled) } func (bs *blockstore) Get(k cid.Cid) (blocks.Block, error) { @@ -126,7 +128,7 @@ func (bs *blockstore) Get(k cid.Cid) (blocks.Block, error) { if err != nil { return nil, err } - if bs.rehash { + if bs.rehash.Load() { rbcid, err := k.Prefix().Sum(bdata) if err != nil { return nil, err From a860f096dc4acd22538da6ad3bfb7385eaf8843d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 13 Apr 2020 18:22:04 -0700 Subject: [PATCH 3215/3817] fix: invalidate cache on failed publish If we fail to publish, _invalidate_ our cache. The publish may have partially succeeded. This commit was moved from ipfs/go-namesys@52d679e87989ab777955b50ad9ecd8095fe5e77d --- namesys/cache.go | 7 +++++++ namesys/namesys.go | 3 +++ 2 files changed, 10 insertions(+) diff --git a/namesys/cache.go b/namesys/cache.go index a0029829d..b2b1f43a8 100644 --- a/namesys/cache.go +++ b/namesys/cache.go @@ -49,6 +49,13 @@ func (ns *mpns) cacheSet(name string, val path.Path, ttl time.Duration) { }) } +func (ns *mpns) cacheInvalidate(name string) { + if ns.cache == nil { + return + } + ns.cache.Remove(name) +} + type cacheEntry struct { val path.Path eol time.Time diff --git a/namesys/namesys.go b/namesys/namesys.go index a486b83b8..0f076ea64 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -218,6 +218,9 @@ func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path. return err } if err := ns.ipnsPublisher.PublishWithEOL(ctx, name, value, eol); err != nil { + // Invalidate the cache. Publishing may _partially_ succeed but + // still return an error. + ns.cacheInvalidate(peer.Encode(id)) return err } ttl := DefaultResolverCacheTTL From 9bc754640c3e76185101fb1078aad50a2e3e2b0b Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Sat, 18 Apr 2020 17:45:01 +0300 Subject: [PATCH 3216/3817] Fix typos and cleanup This commit was moved from ipfs/go-namesys@cf1aba817612f934965c0f770168578ec04b047b --- namesys/namesys.go | 2 +- namesys/republisher/repub_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 0f076ea64..933ce789d 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -45,7 +45,7 @@ func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSys cache, _ = lru.New(cachesize) } - // Prewarm namesys cache with static records for deteministic tests and debugging. + // Prewarm namesys cache with static records for deterministic tests and debugging. // Useful for testing things like DNSLink without real DNS lookup. // Example: // IPFS_NS_MAP="dnslink-test.example.com:/ipfs/bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index fd946501e..470d460ba 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -92,7 +92,7 @@ func TestRepublish(t *testing.T) { // The republishers that are contained within the nodes have their timeout set // to 12 hours. Instead of trying to tweak those, we're just going to pretend - // they dont exist and make our own. + // they don't exist and make our own. repub := NewRepublisher(rp, publisher.Repo.Datastore(), publisher.PrivateKey, publisher.Repo.Keystore()) repub.Interval = time.Second repub.RecordLifetime = time.Second * 5 From 0c84d4315117934b7184e94da69369eb1772b51d Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Sat, 18 Apr 2020 17:45:01 +0300 Subject: [PATCH 3217/3817] Fix typos and cleanup This commit was moved from ipfs/go-ipfs-keystore@311e4de65464c2e89414e8f008768d0088ed80cf --- keystore/keystore.go | 4 ++-- keystore/keystore_test.go | 4 ++-- keystore/memkeystore.go | 2 +- keystore/memkeystore_test.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index 0a2fed3bf..9b2109ccd 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -19,7 +19,7 @@ var codec = base32.StdEncoding.WithPadding(base32.NoPadding) // Keystore provides a key management interface type Keystore interface { - // Has returns whether or not a key exist in the Keystore + // Has returns whether or not a key exists in the Keystore Has(string) (bool, error) // Put stores a key in the Keystore, if a key with the same name already exists, returns ErrKeyExists Put(string, ci.PrivKey) error @@ -57,7 +57,7 @@ func NewFSKeystore(dir string) (*FSKeystore, error) { return &FSKeystore{dir}, nil } -// Has returns whether or not a key exist in the Keystore +// Has returns whether or not a key exists in the Keystore func (ks *FSKeystore) Has(name string) (bool, error) { name, err := encode(name) if err != nil { diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index 2a48b43e5..06f2fccc5 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -137,7 +137,7 @@ func TestKeystoreBasics(t *testing.T) { } if err := ks.Put("", k1); err == nil { - t.Fatal("shouldnt be able to put a key with no name") + t.Fatal("shouldn't be able to put a key with no name") } if err := ks.Put(".foo", k1); err != nil { @@ -238,7 +238,7 @@ func assertGetKey(ks Keystore, name string, exp ci.PrivKey) error { } if !outK.Equals(exp) { - return fmt.Errorf("key we got out didnt match expectation") + return fmt.Errorf("key we got out didn't match expectation") } return nil diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index c96985252..94411144d 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -17,7 +17,7 @@ func NewMemKeystore() *MemKeystore { return &MemKeystore{make(map[string]ci.PrivKey)} } -// Has return whether or not a key exist in the Keystore +// Has return whether or not a key exists in the Keystore func (mk *MemKeystore) Has(name string) (bool, error) { _, ok := mk.keys[name] return ok, nil diff --git a/keystore/memkeystore_test.go b/keystore/memkeystore_test.go index a7214893a..907cbbd0e 100644 --- a/keystore/memkeystore_test.go +++ b/keystore/memkeystore_test.go @@ -90,7 +90,7 @@ func TestMemKeyStoreBasics(t *testing.T) { } if err := ks.Put("", k1); err == nil { - t.Fatal("shouldnt be able to put a key with no name") + t.Fatal("shouldn't be able to put a key with no name") } if err := ks.Put(".foo", k1); err != nil { From 0ed0b6f39d3ab3b20bd6dd2232de03a9513b139c Mon Sep 17 00:00:00 2001 From: Will Scott Date: Tue, 21 Apr 2020 08:40:13 -0700 Subject: [PATCH 3218/3817] extra time for dht spin-up This commit was moved from ipfs/interface-go-ipfs-core@9160e645322d5779c687e0e60cbec5a5932d5c27 --- coreiface/tests/dht.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/coreiface/tests/dht.go b/coreiface/tests/dht.go index 33b4ff14c..a957d66d7 100644 --- a/coreiface/tests/dht.go +++ b/coreiface/tests/dht.go @@ -4,8 +4,9 @@ import ( "context" "io" "testing" + "time" - "github.com/ipfs/interface-go-ipfs-core" + iface "github.com/ipfs/interface-go-ipfs-core" "github.com/ipfs/interface-go-ipfs-core/options" ) @@ -43,6 +44,8 @@ func (tp *TestSuite) TestDhtFindPeer(t *testing.T) { t.Fatal("unexpected number of local addrs") } + time.Sleep(3 * time.Second) + pi, err := apis[2].Dht().FindPeer(ctx, self0.ID()) if err != nil { t.Fatal(err) @@ -88,6 +91,8 @@ func (tp *TestSuite) TestDhtFindProviders(t *testing.T) { t.Fatal(err) } + time.Sleep(3 * time.Second) + out, err := apis[2].Dht().FindProviders(ctx, p, options.Dht.NumProviders(1)) if err != nil { t.Fatal(err) @@ -125,6 +130,8 @@ func (tp *TestSuite) TestDhtProvide(t *testing.T) { p := s.Path() + time.Sleep(3 * time.Second) + out, err := apis[2].Dht().FindProviders(ctx, p, options.Dht.NumProviders(1)) if err != nil { t.Fatal(err) From 87163fb360cb61d196a736b3c4846d4f063c4e05 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 23 Apr 2020 00:14:07 -0700 Subject: [PATCH 3219/3817] fix: correctly construct sessions This way, the caller can pass in any dag service that implements the SessionMaker interface. This commit was moved from ipfs/go-merkledag@5ab627cb02fa185c7bb6b4590de484bf9dff8a4e --- ipld/merkledag/merkledag.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 1f5bcb4e3..a1bbf9711 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -166,11 +166,7 @@ func FetchGraph(ctx context.Context, root cid.Cid, serv ipld.DAGService) error { // maxDepth=1 means "fetch root and its direct children" and so on... // maxDepth=-1 means unlimited. func FetchGraphWithDepthLimit(ctx context.Context, root cid.Cid, depthLim int, serv ipld.DAGService) error { - var ng ipld.NodeGetter = serv - ds, ok := serv.(*dagService) - if ok { - ng = &sesGetter{bserv.NewSession(ctx, ds.Blocks)} - } + var ng ipld.NodeGetter = NewSession(ctx, serv) set := make(map[cid.Cid]int) From c3522b2cc24fb813435eedd9c809ab2c0a633035 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:22:36 +0200 Subject: [PATCH 3220/3817] Add standard issue template This commit was moved from ipfs/go-ipfs-util@5df41752147cfd90051855e38df986c14b0b66be --- util/.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 util/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/util/.github/ISSUE_TEMPLATE/open_an_issue.md b/util/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/util/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From c5b5baded034cae4bbbe293bfce89f94964c6470 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:33:48 +0200 Subject: [PATCH 3221/3817] Add standard issue template This commit was moved from ipfs/go-ipfs-chunker@0f2812fe69c0d6547c5d8f25aec25268153992c6 --- .../.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 chunker/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/chunker/.github/ISSUE_TEMPLATE/open_an_issue.md b/chunker/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/chunker/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From 8aec54ebab324edeee20c97d00aa2aa35d240d1d Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:33:55 +0200 Subject: [PATCH 3222/3817] Add standard issue template This commit was moved from ipfs/go-ipfs-posinfo@fdc2e84e21803946d48d116850ba8e4869c68700 --- .../.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 filestore/posinfo/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/filestore/posinfo/.github/ISSUE_TEMPLATE/open_an_issue.md b/filestore/posinfo/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/filestore/posinfo/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From 2c8dec827506176298f4cb29180c42cb3108f310 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:34:13 +0200 Subject: [PATCH 3223/3817] Add standard issue template This commit was moved from ipfs/go-ipfs-ds-help@f7b131a57a3d1378a0196eb152ad1be557a024c8 --- .../.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 datastore/dshelp/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/datastore/dshelp/.github/ISSUE_TEMPLATE/open_an_issue.md b/datastore/dshelp/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/datastore/dshelp/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From 9b67c100d646eb9518138a44419cb9ae63e1bd57 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:34:30 +0200 Subject: [PATCH 3224/3817] Add standard issue template This commit was moved from ipfs/go-ipfs-routing@0c9de0d77040cd9d2886c509a26d2b72c9d15743 --- .../.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 routing/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/routing/.github/ISSUE_TEMPLATE/open_an_issue.md b/routing/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/routing/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From 59a94b07793e1e8d3b7b96474b208cb7d859431e Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:34:38 +0200 Subject: [PATCH 3225/3817] Add standard issue template This commit was moved from ipfs/go-ipfs-blockstore@cc23c8874e8c1fdc8a75115b2aee8ee1322d0cb5 --- .../.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 blockstore/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/blockstore/.github/ISSUE_TEMPLATE/open_an_issue.md b/blockstore/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/blockstore/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From 0e84cf3d8bfe260b37a7b4db25563b8e60c0e37f Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:35:12 +0200 Subject: [PATCH 3226/3817] Add standard issue template This commit was moved from ipfs/go-ipfs-exchange-interface@73498d2e745f84585bc71bfd77a15cd16d1407f6 --- .../.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 exchange/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/exchange/.github/ISSUE_TEMPLATE/open_an_issue.md b/exchange/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/exchange/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From 834a321166f3d3b765b83de5a95b798cdf0f2f39 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:35:28 +0200 Subject: [PATCH 3227/3817] Add standard issue template This commit was moved from ipfs/go-ipfs-exchange-offline@9ab9d74dce80c393e2b17460c75bc608da38af79 --- .../.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 exchange/offline/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/exchange/offline/.github/ISSUE_TEMPLATE/open_an_issue.md b/exchange/offline/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/exchange/offline/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From 02f78e29078766e79c0bddc0be2865a0593dd900 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:36:07 +0200 Subject: [PATCH 3228/3817] Add standard issue template This commit was moved from ipfs/go-ipns@6e8c75e8c71f6bc3c3e618572fe019a5bfe442c8 --- ipns/.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 ipns/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/ipns/.github/ISSUE_TEMPLATE/open_an_issue.md b/ipns/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/ipns/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From 86d8e7b76ad54d2c3952e3f45a0ffca6faf587d0 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:36:59 +0200 Subject: [PATCH 3229/3817] Add standard issue template This commit was moved from ipfs/go-verifcid@50d3f2f9beaab23b5266e686b94d3e12b35e15e9 --- .../.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 verifcid/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/verifcid/.github/ISSUE_TEMPLATE/open_an_issue.md b/verifcid/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/verifcid/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From bfe5fc1f06c2ab9ee381a8b25ed679229b4f20c4 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:37:07 +0200 Subject: [PATCH 3230/3817] Add standard issue template This commit was moved from ipfs/go-blockservice@e93f46db9bcf3e93b18398f20f6a007daa1f7fec --- .../.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 blockservice/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/blockservice/.github/ISSUE_TEMPLATE/open_an_issue.md b/blockservice/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/blockservice/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From 5d469f5c0e0b9f2f37531178d5faeee92d610ad6 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:37:16 +0200 Subject: [PATCH 3231/3817] Add standard issue template This commit was moved from ipfs/go-merkledag@02b94541364cee26ac87e61176be9bbf996fc82f --- .../.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 ipld/merkledag/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/ipld/merkledag/.github/ISSUE_TEMPLATE/open_an_issue.md b/ipld/merkledag/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/ipld/merkledag/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From bcdda9436e9eea0a5921063c9e174933e319929a Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:37:24 +0200 Subject: [PATCH 3232/3817] Add standard issue template This commit was moved from ipfs/go-path@0a9ded5c825de59a73770c271b9cdd67bf5b1536 --- path/.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 path/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/path/.github/ISSUE_TEMPLATE/open_an_issue.md b/path/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/path/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From aa4387c09790e2f2cb87f8f7dfda2957dcd5d5ba Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:37:49 +0200 Subject: [PATCH 3233/3817] Add standard issue template This commit was moved from ipfs/go-unixfs@4192c7faa79444c30e95992665ae910e1954d429 --- .../.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 unixfs/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/unixfs/.github/ISSUE_TEMPLATE/open_an_issue.md b/unixfs/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/unixfs/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From e0c660e7533498e8283641d9a3d0503af64c3e77 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:38:06 +0200 Subject: [PATCH 3234/3817] Add standard issue template This commit was moved from ipfs/go-mfs@b88bb60748a0be3e37ed83fe8b20524c7cc2f904 --- mfs/.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 mfs/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/mfs/.github/ISSUE_TEMPLATE/open_an_issue.md b/mfs/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/mfs/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From cb9da11e1ce691702055d9c2f28c6177162910a0 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:38:14 +0200 Subject: [PATCH 3235/3817] Add standard issue template This commit was moved from ipfs/go-ipfs-provider@0505db7f693243858a9c3e82a42be491a5faaa04 --- .../.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 provider/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/provider/.github/ISSUE_TEMPLATE/open_an_issue.md b/provider/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/provider/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From 169a00c8557472b4f0dd1d8f17e6883c9ab01898 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:40:20 +0200 Subject: [PATCH 3236/3817] Add standard issue template This commit was moved from ipfs/interface-go-ipfs-core@6ff6ad1717b1ce5b950b1b5abd8f4104deab7d30 --- .../.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 coreiface/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/coreiface/.github/ISSUE_TEMPLATE/open_an_issue.md b/coreiface/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/coreiface/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From 30f1981284603b004478a1abf2ba8de2b096bc50 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:43:11 +0200 Subject: [PATCH 3237/3817] Add standard issue template This commit was moved from ipfs/go-filestore@140842499d824102b6c0200327608a1aab681f95 --- .../.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 filestore/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/filestore/.github/ISSUE_TEMPLATE/open_an_issue.md b/filestore/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/filestore/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From b7869d476cf4b5e2b3abce99f7dc01d8da8efb27 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Apr 2020 11:45:15 +0200 Subject: [PATCH 3238/3817] Add standard issue template This commit was moved from ipfs/go-ipfs-pinner@9e800d1363c2cb23b8f82b3b1f0685b83367103a --- .../.github/ISSUE_TEMPLATE/open_an_issue.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 pinning/pinner/.github/ISSUE_TEMPLATE/open_an_issue.md diff --git a/pinning/pinner/.github/ISSUE_TEMPLATE/open_an_issue.md b/pinning/pinner/.github/ISSUE_TEMPLATE/open_an_issue.md new file mode 100644 index 000000000..4fcbd00ac --- /dev/null +++ b/pinning/pinner/.github/ISSUE_TEMPLATE/open_an_issue.md @@ -0,0 +1,19 @@ +--- +name: Open an issue +about: Only for actionable issues relevant to this repository. +title: '' +labels: need/triage +assignees: '' + +--- + From 145640c6b9c12fb91df9ab0c3c36bb0177b5b26f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 27 Apr 2020 17:21:21 -0700 Subject: [PATCH 3239/3817] fix several bugs 1. Don't log an error when shutting down while reproviding. 2. Reprovide on a fixed interval instead of treating the interval as a delay. 3. Remove trigger muting logic and use the simpler way. 4. Add some tests for triggering. 5. Make sure Reprovider.Close actually, you know, does something. And waits for the reprovider to stop. This commit was moved from ipfs/go-ipfs-provider@e2ee98e12df5fbccb482044e161c4ff877f7e342 --- provider/simple/reprovide.go | 112 ++++++++++++++++-------------- provider/simple/reprovide_test.go | 79 ++++++++++++++++++++- 2 files changed, 137 insertions(+), 54 deletions(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index 59b49d807..9531fd7b3 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -20,12 +20,16 @@ var logR = logging.Logger("reprovider.simple") // KeyChanFunc is function streaming CIDs to pass to content routing type KeyChanFunc func(context.Context) (<-chan cid.Cid, error) -type doneFunc func(error) // Reprovider reannounces blocks to the network type Reprovider struct { - ctx context.Context - trigger chan doneFunc + // Reprovider context. Cancel to stop, then wait on doneCh. + ctx context.Context + cancel context.CancelFunc + doneCh chan struct{} + + // Trigger triggers a reprovide. + trigger chan chan<- error // The routing system to provide values through rsys routing.ContentRouting @@ -37,9 +41,12 @@ type Reprovider struct { // NewReprovider creates new Reprovider instance. func NewReprovider(ctx context.Context, reprovideIniterval time.Duration, rsys routing.ContentRouting, keyProvider KeyChanFunc) *Reprovider { + ctx, cancel := context.WithCancel(ctx) return &Reprovider{ ctx: ctx, - trigger: make(chan doneFunc), + cancel: cancel, + doneCh: make(chan struct{}), + trigger: make(chan chan<- error), rsys: rsys, keyProvider: keyProvider, @@ -49,44 +56,60 @@ func NewReprovider(ctx context.Context, reprovideIniterval time.Duration, rsys r // Close the reprovider func (rp *Reprovider) Close() error { + rp.cancel() + <-rp.doneCh return nil } // Run re-provides keys with 'tick' interval or when triggered func (rp *Reprovider) Run() { - // dont reprovide immediately. - // may have just started the daemon and shutting it down immediately. - // probability( up another minute | uptime ) increases with uptime. - after := time.After(time.Minute) - var done doneFunc - for { - if rp.tick == 0 { - after = make(chan time.Time) + defer close(rp.doneCh) + + var initialReprovideCh, reprovideCh <-chan time.Time + + // If reproviding is enabled (non-zero) + if rp.tick > 0 { + reprovideTicker := time.NewTicker(rp.tick) + defer reprovideTicker.Stop() + reprovideCh = reprovideTicker.C + + // If the reprovide ticker is larger than a minute (likely), + // provide once after we've been up a minute. + // + // Don't provide _immediately_ as we might be just about to stop. + if rp.tick > time.Minute { + initialReprovideTimer := time.NewTimer(time.Minute) + defer initialReprovideTimer.Stop() + + initialReprovideCh = initialReprovideTimer.C } + } + var done chan<- error + for rp.ctx.Err() == nil { select { + case <-initialReprovideCh: + case <-reprovideCh: + case done = <-rp.trigger: case <-rp.ctx.Done(): return - case done = <-rp.trigger: - case <-after: } - //'mute' the trigger channel so when `ipfs bitswap reprovide` is called - //a 'reprovider is already running' error is returned - unmute := rp.muteTrigger() - err := rp.Reprovide() - if err != nil { + + // only log if we've hit an actual error, otherwise just tell the client we're shutting down + if rp.ctx.Err() != nil { + err = fmt.Errorf("shutting down") + } else if err != nil { logR.Errorf("failed to reprovide: %s", err) } if done != nil { - done(err) + if err != nil { + done <- err + } + close(done) } - - unmute() - - after = time.After(rp.tick) } } @@ -119,44 +142,27 @@ func (rp *Reprovider) Reprovide() error { return nil } -// Trigger starts reprovision process in rp.Run and waits for it +// Trigger starts reprovision process in rp.Run and waits for it to finish. +// +// Returns an error if a reprovide is already in progress. func (rp *Reprovider) Trigger(ctx context.Context) error { - progressCtx, done := context.WithCancel(ctx) - - var err error - df := func(e error) { - err = e - done() + doneCh := make(chan error, 1) + select { + case rp.trigger <- doneCh: + default: + return fmt.Errorf("reprovider is already running") } select { + case err := <-doneCh: + return err case <-rp.ctx.Done(): - return context.Canceled + return fmt.Errorf("reprovide service stopping") case <-ctx.Done(): - return context.Canceled - case rp.trigger <- df: - <-progressCtx.Done() - return err + return ctx.Err() } } -func (rp *Reprovider) muteTrigger() context.CancelFunc { - ctx, cf := context.WithCancel(rp.ctx) - go func() { - defer cf() - for { - select { - case <-ctx.Done(): - return - case done := <-rp.trigger: - done(fmt.Errorf("reprovider is already running")) - } - } - }() - - return cf -} - // Strategies // NewBlockstoreProvider returns key provider using bstore.AllKeysChan diff --git a/provider/simple/reprovide_test.go b/provider/simple/reprovide_test.go index 322e4c10a..0b9271d3a 100644 --- a/provider/simple/reprovide_test.go +++ b/provider/simple/reprovide_test.go @@ -63,6 +63,22 @@ func setupDag(t *testing.T) (nodes []cid.Cid, bstore blockstore.Blockstore) { } func TestReprovide(t *testing.T) { + testReprovide(t, func(r *Reprovider, ctx context.Context) error { + return r.Reprovide() + }) +} + +func TestTrigger(t *testing.T) { + testReprovide(t, func(r *Reprovider, ctx context.Context) error { + go r.Run() + time.Sleep(1 * time.Second) + defer r.Close() + err := r.Trigger(ctx) + return err + }) +} + +func testReprovide(t *testing.T, trigger func(r *Reprovider, ctx context.Context) error) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -71,7 +87,7 @@ func TestReprovide(t *testing.T) { keyProvider := NewBlockstoreProvider(bstore) reprov := NewReprovider(ctx, time.Hour, clA, keyProvider) - err := reprov.Reprovide() + err := trigger(reprov, ctx) if err != nil { t.Fatal(err) } @@ -95,6 +111,67 @@ func TestReprovide(t *testing.T) { } } +func TestTriggerTwice(t *testing.T) { + // Ensure we can only trigger once at a time. + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + clA, _, _, _ := setupRouting(t) + + keyCh := make(chan cid.Cid) + startCh := make(chan struct{}) + keyFunc := func(ctx context.Context) (<-chan cid.Cid, error) { + <-startCh + return keyCh, nil + } + + reprov := NewReprovider(ctx, time.Hour, clA, keyFunc) + go reprov.Run() + defer reprov.Close() + + // Wait for the reprovider to start, otherwise, the reprovider will + // think a concurrent reprovide is running. + // + // We _could_ fix this race... but that would be complexity for nothing. + // 1. We start a reprovide 1 minute after startup anyways. + // 2. The window is really narrow. + time.Sleep(1 * time.Second) + + errCh := make(chan error, 2) + + // Trigger in the background + go func() { + errCh <- reprov.Trigger(ctx) + }() + + // Wait for the trigger to really start. + startCh <- struct{}{} + + // Try to trigger again, this should fail immediately. + if err := reprov.Trigger(ctx); err == nil { + t.Fatal("expected an error") + } + + // Let the trigger progress. + close(keyCh) + + // Check the result. + err := <-errCh + if err != nil { + t.Fatal(err) + } + + // Try to trigger again, this should work. + go func() { + errCh <- reprov.Trigger(ctx) + }() + startCh <- struct{}{} + err = <-errCh + if err != nil { + t.Fatal(err) + } +} + type mockPinner struct { recursive []cid.Cid direct []cid.Cid From 2fe44c30b5a0c7fd298b710832f97c5a535a349a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 27 Apr 2020 17:43:59 -0700 Subject: [PATCH 3240/3817] fix comment wording Co-Authored-By: Will This commit was moved from ipfs/go-ipfs-provider@4daf83d0194c8e563f57a07402a49e5031a455ca --- provider/simple/reprovide.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index 9531fd7b3..be804a2a9 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -142,7 +142,7 @@ func (rp *Reprovider) Reprovide() error { return nil } -// Trigger starts reprovision process in rp.Run and waits for it to finish. +// Trigger starts the reprovision process in rp.Run and waits for it to finish. // // Returns an error if a reprovide is already in progress. func (rp *Reprovider) Trigger(ctx context.Context) error { From abe7522a79a79ecbd427bba275094ca0198b4c19 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 27 Apr 2020 17:52:09 -0700 Subject: [PATCH 3241/3817] fix: code review This commit was moved from ipfs/go-ipfs-provider@3c0bbfcc0e503c4c05073aebb792125be25a3591 --- provider/simple/reprovide.go | 34 +++++++++++++++++-------------- provider/simple/reprovide_test.go | 4 ++++ 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index be804a2a9..bfe6173e1 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -2,6 +2,7 @@ package simple import ( "context" + "errors" "fmt" "time" @@ -18,15 +19,18 @@ import ( var logR = logging.Logger("reprovider.simple") +// ErrClosed is returned by Trigger when operating on a closed reprovider. +var ErrClosed = errors.New("reprovider service stopped") + // KeyChanFunc is function streaming CIDs to pass to content routing type KeyChanFunc func(context.Context) (<-chan cid.Cid, error) // Reprovider reannounces blocks to the network type Reprovider struct { - // Reprovider context. Cancel to stop, then wait on doneCh. - ctx context.Context - cancel context.CancelFunc - doneCh chan struct{} + // Reprovider context. Cancel to stop, then wait on closedCh. + ctx context.Context + cancel context.CancelFunc + closedCh chan struct{} // Trigger triggers a reprovide. trigger chan chan<- error @@ -43,10 +47,10 @@ type Reprovider struct { func NewReprovider(ctx context.Context, reprovideIniterval time.Duration, rsys routing.ContentRouting, keyProvider KeyChanFunc) *Reprovider { ctx, cancel := context.WithCancel(ctx) return &Reprovider{ - ctx: ctx, - cancel: cancel, - doneCh: make(chan struct{}), - trigger: make(chan chan<- error), + ctx: ctx, + cancel: cancel, + closedCh: make(chan struct{}), + trigger: make(chan chan<- error), rsys: rsys, keyProvider: keyProvider, @@ -57,13 +61,13 @@ func NewReprovider(ctx context.Context, reprovideIniterval time.Duration, rsys r // Close the reprovider func (rp *Reprovider) Close() error { rp.cancel() - <-rp.doneCh + <-rp.closedCh return nil } // Run re-provides keys with 'tick' interval or when triggered func (rp *Reprovider) Run() { - defer close(rp.doneCh) + defer close(rp.closedCh) var initialReprovideCh, reprovideCh <-chan time.Time @@ -99,7 +103,7 @@ func (rp *Reprovider) Run() { // only log if we've hit an actual error, otherwise just tell the client we're shutting down if rp.ctx.Err() != nil { - err = fmt.Errorf("shutting down") + err = ErrClosed } else if err != nil { logR.Errorf("failed to reprovide: %s", err) } @@ -146,18 +150,18 @@ func (rp *Reprovider) Reprovide() error { // // Returns an error if a reprovide is already in progress. func (rp *Reprovider) Trigger(ctx context.Context) error { - doneCh := make(chan error, 1) + resultCh := make(chan error, 1) select { - case rp.trigger <- doneCh: + case rp.trigger <- resultCh: default: return fmt.Errorf("reprovider is already running") } select { - case err := <-doneCh: + case err := <-resultCh: return err case <-rp.ctx.Done(): - return fmt.Errorf("reprovide service stopping") + return ErrClosed case <-ctx.Done(): return ctx.Err() } diff --git a/provider/simple/reprovide_test.go b/provider/simple/reprovide_test.go index 0b9271d3a..3858baf5e 100644 --- a/provider/simple/reprovide_test.go +++ b/provider/simple/reprovide_test.go @@ -147,10 +147,14 @@ func TestTriggerTwice(t *testing.T) { // Wait for the trigger to really start. startCh <- struct{}{} + start := time.Now() // Try to trigger again, this should fail immediately. if err := reprov.Trigger(ctx); err == nil { t.Fatal("expected an error") } + if time.Since(start) > 10*time.Millisecond { + t.Fatal("expected reprovide to fail instantly") + } // Let the trigger progress. close(keyCh) From 9237b405df0d2baef83ec17b8262084678d68a5b Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Wed, 29 Apr 2020 19:41:19 +0200 Subject: [PATCH 3242/3817] Update go-ipld-prime to the era of NodeAssembler. This commit was moved from ipld/go-car@96126600d5b591231b5aaa29ed3f086cf74c2214 --- ipld/car/car_test.go | 4 ++-- ipld/car/selectivecar.go | 21 +++++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 4c969626f..3265d8374 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -9,7 +9,7 @@ import ( format "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" dstest "github.com/ipfs/go-merkledag/test" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" + basicnode "github.com/ipld/go-ipld-prime/node/basic" "github.com/ipld/go-ipld-prime/traversal/selector" "github.com/ipld/go-ipld-prime/traversal/selector/builder" "github.com/stretchr/testify/require" @@ -96,7 +96,7 @@ func TestRoundtripSelective(t *testing.T) { assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) - ssb := builder.NewSelectorSpecBuilder(ipldfree.NodeBuilder()) + ssb := builder.NewSelectorSpecBuilder(basicnode.Style.Any) // the graph assembled above looks as follows, in order: // nd3 -> [c, nd2 -> [nd1 -> a, b, nd1 -> a]] diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index 705cf563f..0b11fc815 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -11,8 +11,8 @@ import ( util "github.com/ipld/go-car/util" "github.com/ipld/go-ipld-prime" dagpb "github.com/ipld/go-ipld-prime-proto" - ipldfree "github.com/ipld/go-ipld-prime/impl/free" cidlink "github.com/ipld/go-ipld-prime/linking/cid" + basicnode "github.com/ipld/go-ipld-prime/node/basic" "github.com/ipld/go-ipld-prime/traversal" "github.com/ipld/go-ipld-prime/traversal/selector" ) @@ -221,8 +221,8 @@ func (sct *selectiveCarTraverser) loader(lnk ipld.Link, ctx ipld.LinkContext) (i func (sct *selectiveCarTraverser) traverseBlocks() error { - nbc := dagpb.AddDagPBSupportToChooser(func(ipld.Link, ipld.LinkContext) ipld.NodeBuilder { - return ipldfree.NodeBuilder() + nsc := dagpb.AddDagPBSupportToChooser(func(ipld.Link, ipld.LinkContext) (ipld.NodeStyle, error) { + return basicnode.Style.Any, nil }) for _, carDag := range sct.sc.dags { @@ -231,16 +231,21 @@ func (sct *selectiveCarTraverser) traverseBlocks() error { return err } lnk := cidlink.Link{Cid: carDag.Root} - nb := nbc(lnk, ipld.LinkContext{}) - nd, err := lnk.Load(sct.sc.ctx, ipld.LinkContext{}, nb, sct.loader) + ns, err := nsc(lnk, ipld.LinkContext{}) if err != nil { return err } + nb := ns.NewBuilder() + err = lnk.Load(sct.sc.ctx, ipld.LinkContext{}, nb, sct.loader) + if err != nil { + return err + } + nd := nb.Build() err = traversal.Progress{ Cfg: &traversal.Config{ - Ctx: sct.sc.ctx, - LinkLoader: sct.loader, - LinkNodeBuilderChooser: nbc, + Ctx: sct.sc.ctx, + LinkLoader: sct.loader, + LinkTargetNodeStyleChooser: nsc, }, }.WalkAdv(nd, parsed, func(traversal.Progress, ipld.Node, traversal.VisitReason) error { return nil }) if err != nil { From 7d04b4be9f5b4f3b2f090f8ce3d660c00ec41f6f Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Wed, 29 Apr 2020 17:57:38 -0400 Subject: [PATCH 3243/3817] fix: do not use hard coded IPNS Publish maximum timeout duration This commit was moved from ipfs/go-namesys@32b17801321722098b96662139dcf790d4c74b01 --- namesys/publisher.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 1fa0c96c9..f558eaf28 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -22,7 +22,6 @@ import ( const ipnsPrefix = "/ipns/" -const PublishPutValTimeout = time.Minute const DefaultRecordEOL = 24 * time.Hour // IpnsPublisher is capable of publishing and resolving names to the IPFS @@ -269,15 +268,10 @@ func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk } // Store associated public key - timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout) - defer cancel() - return r.PutValue(timectx, k, pkbytes) + return r.PutValue(ctx, k, pkbytes) } func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec *pb.IpnsEntry) error { - timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout) - defer cancel() - data, err := proto.Marshal(rec) if err != nil { return err @@ -285,7 +279,7 @@ func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec log.Debugf("Storing ipns entry at: %s", ipnskey) // Store ipns entry at "/ipns/"+h(pubkey) - return r.PutValue(timectx, ipnskey, data) + return r.PutValue(ctx, ipnskey, data) } // InitializeKeyspace sets the ipns record for the given key to From a40d95cafbd902f90b53dcd512017a2f37a9e03c Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Wed, 29 Apr 2020 17:57:38 -0400 Subject: [PATCH 3244/3817] fix: do not use hard coded IPNS Publish maximum timeout duration This commit was moved from ipfs/go-namesys@6f468ea0034aba4840fc0e4c4106b090a7c7849f --- namesys/publisher.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 1fa0c96c9..f558eaf28 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -22,7 +22,6 @@ import ( const ipnsPrefix = "/ipns/" -const PublishPutValTimeout = time.Minute const DefaultRecordEOL = 24 * time.Hour // IpnsPublisher is capable of publishing and resolving names to the IPFS @@ -269,15 +268,10 @@ func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk } // Store associated public key - timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout) - defer cancel() - return r.PutValue(timectx, k, pkbytes) + return r.PutValue(ctx, k, pkbytes) } func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec *pb.IpnsEntry) error { - timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout) - defer cancel() - data, err := proto.Marshal(rec) if err != nil { return err @@ -285,7 +279,7 @@ func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec log.Debugf("Storing ipns entry at: %s", ipnskey) // Store ipns entry at "/ipns/"+h(pubkey) - return r.PutValue(timectx, ipnskey, data) + return r.PutValue(ctx, ipnskey, data) } // InitializeKeyspace sets the ipns record for the given key to From 21a27def4907d97fdfa64a472ccd1a04ca59f112 Mon Sep 17 00:00:00 2001 From: Dominic Della Valle Date: Thu, 14 May 2020 18:58:30 -0400 Subject: [PATCH 3245/3817] Fix incorrect mutex unlock call in File.Open This commit was moved from ipfs/go-mfs@6163f562f6f2d3848dcf6189080d58194ac898ab --- mfs/file.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfs/file.go b/mfs/file.go index 280bf93ab..bbe508ac3 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -64,7 +64,7 @@ func (fi *File) Open(flags Flags) (_ FileDescriptor, _retErr error) { fi.desclock.RLock() defer func() { if _retErr != nil { - fi.desclock.Unlock() + fi.desclock.RUnlock() } }() } else { From dde007b98dbae91f5f2e03b743ae256dede75087 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 17 Jun 2020 19:42:14 -0700 Subject: [PATCH 3246/3817] fix: close resolve channel before returning it This commit was moved from ipfs/go-namesys@dd76900f0730e6158cf7628051eaff0ea5d6433c --- namesys/namesys.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 933ce789d..bf028c099 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -86,16 +86,19 @@ func (ns *mpns) Resolve(ctx context.Context, name string, options ...opts.Resolv } func (ns *mpns) ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result { - res := make(chan Result, 1) if strings.HasPrefix(name, "/ipfs/") { p, err := path.ParsePath(name) + res := make(chan Result, 1) res <- Result{p, err} + close(res) return res } if !strings.HasPrefix(name, "/") { p, err := path.ParsePath("/ipfs/" + name) + res := make(chan Result, 1) res <- Result{p, err} + close(res) return res } From 2d40fae7c27bd7e573ba7c2f613aa9fc80746a8d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 17 Jun 2020 19:50:51 -0700 Subject: [PATCH 3247/3817] fix: return results from resolve once Previously, we'd return the error + result, then the result. This commit was moved from ipfs/go-namesys@cd4fdab9277b3d15a1b831f0f222034280c0d702 --- namesys/namesys.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index bf028c099..ac7fb0383 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -123,15 +123,12 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. key := segments[2] if p, ok := ns.cacheGet(key); ok { + var err error if len(segments) > 3 { - var err error p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) - if err != nil { - emitOnceResult(ctx, out, onceResult{value: p, err: err}) - } } - out <- onceResult{value: p} + out <- onceResult{value: p, err: err} close(out) return out } @@ -183,17 +180,15 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. best = res } p := res.value + err := res.err + ttl := res.ttl // Attach rest of the path if len(segments) > 3 { - var err error p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) - if err != nil { - emitOnceResult(ctx, out, onceResult{value: p, ttl: res.ttl, err: err}) - } } - emitOnceResult(ctx, out, onceResult{value: p, ttl: res.ttl, err: res.err}) + emitOnceResult(ctx, out, onceResult{value: p, ttl: ttl, err: err}) case <-ctx.Done(): return } From b36e937e33bed6f58ccb4a6035c86d259082d363 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 24 Jun 2020 16:51:38 +0200 Subject: [PATCH 3248/3817] Avoid modifying passed in slice of cids Signed-off-by: Jakub Sztandera This commit was moved from ipfs/go-blockservice@a978cec6e834457d91dc4309fc4d439583faae8b --- blockservice/blockservice.go | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index ba0ab4183..9cf96a866 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -273,17 +273,28 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget go func() { defer close(out) - k := 0 + allValid := true for _, c := range ks { - // hash security - if err := verifcid.ValidateCid(c); err == nil { - ks[k] = c - k++ - } else { - log.Errorf("unsafe CID (%s) passed to blockService.GetBlocks: %s", c, err) + if err := verifcid.ValidateCid(c); err != nil { + allValid = false + break } } - ks = ks[:k] + + if !allValid { + ks2 := make([]cid.Cid, len(ks)) + k := 0 + for _, c := range ks { + // hash security + if err := verifcid.ValidateCid(c); err == nil { + ks2[k] = c + k++ + } else { + log.Errorf("unsafe CID (%s) passed to blockService.GetBlocks: %s", c, err) + } + } + ks = ks2[:k] + } var misses []cid.Cid for _, c := range ks { From ed1f46e620a6b5563707889f4329ee63dc39a458 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 24 Jun 2020 17:23:42 +0200 Subject: [PATCH 3249/3817] Use append instead of index assign Co-authored-by: Steven Allen This commit was moved from ipfs/go-blockservice@5ca73b911fb3769e1376a15a8da1cee22f1207d3 --- blockservice/blockservice.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 9cf96a866..33f69141c 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -282,18 +282,16 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget } if !allValid { - ks2 := make([]cid.Cid, len(ks)) - k := 0 + ks2 := make([]cid.Cid, 0, len(ks)) for _, c := range ks { // hash security if err := verifcid.ValidateCid(c); err == nil { - ks2[k] = c - k++ + ks2 = append(ks2, c) } else { log.Errorf("unsafe CID (%s) passed to blockService.GetBlocks: %s", c, err) } } - ks = ks2[:k] + ks = ks2 } var misses []cid.Cid From f240accde27bd0145587f4ceb7f6a640df95ae53 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 23 Jul 2020 18:53:52 -0700 Subject: [PATCH 3250/3817] Optimize id store Prefix() is now much faster than extracting the hash. This commit was moved from ipfs/go-ipfs-blockstore@8f7c32424c55e6e0cc75a7e743fddaf2329d583f --- blockstore/idstore.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/blockstore/idstore.go b/blockstore/idstore.go index 2a5bf8415..477da70b2 100644 --- a/blockstore/idstore.go +++ b/blockstore/idstore.go @@ -18,6 +18,11 @@ func NewIdStore(bs Blockstore) Blockstore { } func extractContents(k cid.Cid) (bool, []byte) { + // Pre-check by calling Prefix(), this much faster than extracting the hash. + if k.Prefix().MhType != mh.IDENTITY { + return false, nil + } + dmh, err := mh.Decode(k.Hash()) if err != nil || dmh.Code != mh.ID { return false, nil From 7bcd64373825dfda1ef91c9d9157a5328928cc09 Mon Sep 17 00:00:00 2001 From: Petar Maymounkov Date: Wed, 22 Jul 2020 09:05:14 -0700 Subject: [PATCH 3251/3817] add id and key formatting utils; format keys as b36cid by default; update tests This commit was moved from ipfs/interface-go-ipfs-core@c604c5b0338c075046d7ddf0b60c5927500607d3 --- coreiface/idfmt.go | 19 ++++++++++++++++ coreiface/tests/key.go | 49 +++++++++++++++++++++++++++-------------- coreiface/tests/name.go | 30 ++++++++++++------------- 3 files changed, 66 insertions(+), 32 deletions(-) create mode 100644 coreiface/idfmt.go diff --git a/coreiface/idfmt.go b/coreiface/idfmt.go new file mode 100644 index 000000000..1ba79e602 --- /dev/null +++ b/coreiface/idfmt.go @@ -0,0 +1,19 @@ +package iface + +import ( + peer "github.com/libp2p/go-libp2p-core/peer" + mbase "github.com/multiformats/go-multibase" +) + +func FormatKeyID(id peer.ID) string { + if s, err := peer.ToCid(id).StringOfBase(mbase.Base36); err != nil { + panic(err) + } else { + return s + } +} + +// FormatKey formats the given IPNS key in a canonical way. +func FormatKey(key Key) string { + return FormatKeyID(key.ID()) +} diff --git a/coreiface/tests/key.go b/coreiface/tests/key.go index 265a8f060..c3cd8626f 100644 --- a/coreiface/tests/key.go +++ b/coreiface/tests/key.go @@ -5,8 +5,11 @@ import ( "strings" "testing" - "github.com/ipfs/interface-go-ipfs-core" + cid "github.com/ipfs/go-cid" + coreiface "github.com/ipfs/interface-go-ipfs-core" + iface "github.com/ipfs/interface-go-ipfs-core" opt "github.com/ipfs/interface-go-ipfs-core/options" + mbase "github.com/multiformats/go-multibase" ) func (tp *TestSuite) TestKey(t *testing.T) { @@ -64,8 +67,8 @@ func (tp *TestSuite) TestListSelf(t *testing.T) { t.Errorf("expected the key to be called 'self', got '%s'", keys[0].Name()) } - if keys[0].Path().String() != "/ipns/"+self.ID().Pretty() { - t.Errorf("expected the key to have path '/ipns/%s', got '%s'", self.ID().Pretty(), keys[0].Path().String()) + if keys[0].Path().String() != "/ipns/"+coreiface.FormatKeyID(self.ID()) { + t.Errorf("expected the key to have path '/ipns/%s', got '%s'", coreiface.FormatKeyID(self.ID()), keys[0].Path().String()) } } @@ -134,9 +137,30 @@ func (tp *TestSuite) TestGenerate(t *testing.T) { t.Errorf("expected the key to be called 'foo', got '%s'", k.Name()) } - if !strings.HasPrefix(k.Path().String(), "/ipns/Qm") { - t.Errorf("expected the key to be prefixed with '/ipns/Qm', got '%s'", k.Path().String()) + verifyIPNSPath(t, k.Path().String()) +} + +func verifyIPNSPath(t *testing.T, p string) bool { + t.Helper() + if !strings.HasPrefix(p, "/ipns/") { + t.Errorf("path %q does not look like an IPNS path", p) + return false + } + k := p[len("/ipns/"):] + c, err := cid.Decode(k) + if err != nil { + t.Errorf("failed to decode IPNS key %q (%v)", k, err) + return false + } + b36, err := c.StringOfBase(mbase.Base36) + if err != nil { + t.Fatalf("cid cannot format itself in b36") + return false + } + if b36 != k { + t.Errorf("IPNS key is not base36") } + return true } func (tp *TestSuite) TestGenerateSize(t *testing.T) { @@ -157,9 +181,7 @@ func (tp *TestSuite) TestGenerateSize(t *testing.T) { t.Errorf("expected the key to be called 'foo', got '%s'", k.Name()) } - if !strings.HasPrefix(k.Path().String(), "/ipns/Qm") { - t.Errorf("expected the key to be prefixed with '/ipns/Qm', got '%s'", k.Path().String()) - } + verifyIPNSPath(t, k.Path().String()) } func (tp *TestSuite) TestGenerateType(t *testing.T) { @@ -256,15 +278,8 @@ func (tp *TestSuite) TestList(t *testing.T) { return } - if !strings.HasPrefix(l[0].Path().String(), "/ipns/Qm") { - t.Fatalf("expected key 0 to be prefixed with '/ipns/Qm', got '%s'", l[0].Name()) - return - } - - if !strings.HasPrefix(l[1].Path().String(), "/ipns/Qm") { - t.Fatalf("expected key 1 to be prefixed with '/ipns/Qm', got '%s'", l[1].Name()) - return - } + verifyIPNSPath(t, l[0].Path().String()) + verifyIPNSPath(t, l[1].Path().String()) } func (tp *TestSuite) TestRename(t *testing.T) { diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index 31a5c1466..021c1bb97 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -2,15 +2,15 @@ package tests import ( "context" - path "github.com/ipfs/interface-go-ipfs-core/path" "io" "math/rand" gopath "path" "testing" "time" - "github.com/ipfs/go-ipfs-files" - ipath "github.com/ipfs/go-path" + path "github.com/ipfs/interface-go-ipfs-core/path" + + files "github.com/ipfs/go-ipfs-files" coreiface "github.com/ipfs/interface-go-ipfs-core" opt "github.com/ipfs/interface-go-ipfs-core/options" @@ -70,8 +70,8 @@ func (tp *TestSuite) TestPublishResolve(t *testing.T) { t.Fatal(err) } - if e.Name() != self.ID().Pretty() { - t.Errorf("expected e.Name to equal '%s', got '%s'", self.ID().Pretty(), e.Name()) + if e.Name() != coreiface.FormatKeyID(self.ID()) { + t.Errorf("expected e.Name to equal '%s', got '%s'", coreiface.FormatKeyID(self.ID()), e.Name()) } if e.Value().String() != p.String() { @@ -100,8 +100,8 @@ func (tp *TestSuite) TestPublishResolve(t *testing.T) { t.Fatal(err) } - if e.Name() != self.ID().Pretty() { - t.Errorf("expected e.Name to equal '%s', got '%s'", self.ID().Pretty(), e.Name()) + if e.Name() != coreiface.FormatKeyID(self.ID()) { + t.Errorf("expected e.Name to equal '%s', got '%s'", coreiface.FormatKeyID(self.ID()), e.Name()) } if e.Value().String() != p.String()+"/test" { @@ -130,8 +130,8 @@ func (tp *TestSuite) TestPublishResolve(t *testing.T) { t.Fatal(err) } - if e.Name() != self.ID().Pretty() { - t.Errorf("expected e.Name to equal '%s', got '%s'", self.ID().Pretty(), e.Name()) + if e.Name() != coreiface.FormatKeyID(self.ID()) { + t.Errorf("expected e.Name to equal '%s', got '%s'", coreiface.FormatKeyID(self.ID()), e.Name()) } if e.Value().String() != p.String() { @@ -160,8 +160,8 @@ func (tp *TestSuite) TestPublishResolve(t *testing.T) { t.Fatal(err) } - if e.Name() != self.ID().Pretty() { - t.Errorf("expected e.Name to equal '%s', got '%s'", self.ID().Pretty(), e.Name()) + if e.Name() != coreiface.FormatKeyID(self.ID()) { + t.Errorf("expected e.Name to equal '%s', got '%s'", coreiface.FormatKeyID(self.ID()), e.Name()) } if e.Value().String() != p.String()+"/a" { @@ -212,8 +212,8 @@ func (tp *TestSuite) TestBasicPublishResolveKey(t *testing.T) { t.Fatal(err) } - if ipath.Join([]string{"/ipns", e.Name()}) != k.Path().String() { - t.Errorf("expected e.Name to equal '%s', got '%s'", e.Name(), k.Path().String()) + if e.Name() != coreiface.FormatKey(k) { + t.Errorf("expected e.Name to equal %s, got '%s'", e.Name(), coreiface.FormatKey(k)) } if e.Value().String() != p.String() { @@ -255,8 +255,8 @@ func (tp *TestSuite) TestBasicPublishResolveTimeout(t *testing.T) { t.Fatal(err) } - if e.Name() != self.ID().Pretty() { - t.Errorf("expected e.Name to equal '%s', got '%s'", self.ID().Pretty(), e.Name()) + if e.Name() != coreiface.FormatKeyID(self.ID()) { + t.Errorf("expected e.Name to equal '%s', got '%s'", coreiface.FormatKeyID(self.ID()), e.Name()) } if e.Value().String() != p.String() { From 580f84a75f8c3486ad88da5656bc3b43bf50ba4c Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Tue, 11 Aug 2020 18:42:20 -0400 Subject: [PATCH 3252/3817] fix: queue: switch from using a time based counter to a monotonic one This commit was moved from ipfs/go-ipfs-provider@071d037e32b3589f2065568d981f46a999b43a87 --- provider/queue/queue.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/provider/queue/queue.go b/provider/queue/queue.go index 2c3350256..4e31c8cae 100644 --- a/provider/queue/queue.go +++ b/provider/queue/queue.go @@ -3,8 +3,6 @@ package queue import ( "context" "fmt" - "time" - cid "github.com/ipfs/go-cid" datastore "github.com/ipfs/go-datastore" namespace "github.com/ipfs/go-datastore/namespace" @@ -29,6 +27,8 @@ type Queue struct { enqueue chan cid.Cid close context.CancelFunc closed chan struct{} + + counter int } // NewQueue creates a queue for cids @@ -117,7 +117,8 @@ func (q *Queue) work() { select { case toQueue := <-q.enqueue: - keyPath := fmt.Sprintf("%d/%s", time.Now().UnixNano(), c.String()) + keyPath := fmt.Sprintf("%063d/%s", q.counter, c.String()) + q.counter++ nextKey := datastore.NewKey(keyPath) if err := q.ds.Put(nextKey, toQueue.Bytes()); err != nil { From 014af4befcdda83aef977c19059fe3e3d1560019 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Fri, 14 Aug 2020 18:08:09 -0400 Subject: [PATCH 3253/3817] Stop searching for public keys before doing an IPNS Get (#7549) * feat: stop checking the DHT for public keys before doing an IPNS get. It has been many releases since we started adding the public keys into the IPNS records by default. This commit was moved from ipfs/go-namesys@4887042b38faeb44927cf55f4fdbc63821edaa01 --- namesys/ipns_resolver_validation_test.go | 90 +++++++++++++----------- namesys/routing.go | 14 ---- 2 files changed, 50 insertions(+), 54 deletions(-) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 1fd7488b9..1e342f259 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -10,6 +10,7 @@ import ( mockrouting "github.com/ipfs/go-ipfs-routing/mock" offline "github.com/ipfs/go-ipfs-routing/offline" ipns "github.com/ipfs/go-ipns" + ipns_pb "github.com/ipfs/go-ipns/pb" path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" ci "github.com/libp2p/go-libp2p-core/crypto" @@ -23,6 +24,25 @@ import ( ) func TestResolverValidation(t *testing.T) { + t.Run("RSA", + func(t *testing.T) { + testResolverValidation(t, ci.RSA) + }) + t.Run("Ed25519", + func(t *testing.T) { + testResolverValidation(t, ci.Ed25519) + }) + t.Run("ECDSA", + func(t *testing.T) { + testResolverValidation(t, ci.ECDSA) + }) + t.Run("Secp256k1", + func(t *testing.T) { + testResolverValidation(t, ci.Secp256k1) + }) +} + +func testResolverValidation(t *testing.T, keyType int) { ctx := context.Background() rid := testutil.RandIdentityOrFatal(t) dstore := dssync.MutexWrap(ds.NewMapDatastore()) @@ -34,16 +54,10 @@ func TestResolverValidation(t *testing.T) { nvVstore := offline.NewOfflineRouter(dstore, mockrouting.MockValidator{}) // Create entry with expiry in one hour - priv, id, _, ipnsDHTPath := genKeys(t) + priv, id, _, ipnsDHTPath := genKeys(t, keyType) ts := time.Now() p := []byte("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") - entry, err := ipns.Create(priv, p, 1, ts.Add(time.Hour)) - if err != nil { - t.Fatal(err) - } - - // Make peer's public key available in peer store - err = peerstore.AddPubKey(id, priv.GetPublic()) + entry, err := createIPNSRecordWithEmbeddedPublicKey(priv, p, 1, ts.Add(time.Hour)) if err != nil { t.Fatal(err) } @@ -63,7 +77,7 @@ func TestResolverValidation(t *testing.T) { t.Fatalf("Mismatch between published path %s and resolved path %s", p, resp) } // Create expired entry - expiredEntry, err := ipns.Create(priv, p, 1, ts.Add(-1*time.Hour)) + expiredEntry, err := createIPNSRecordWithEmbeddedPublicKey(priv, p, 1, ts.Add(-1*time.Hour)) if err != nil { t.Fatal(err) } @@ -81,13 +95,7 @@ func TestResolverValidation(t *testing.T) { } // Create IPNS record path with a different private key - priv2, id2, _, ipnsDHTPath2 := genKeys(t) - - // Make peer's public key available in peer store - err = peerstore.AddPubKey(id2, priv2.GetPublic()) - if err != nil { - t.Fatal(err) - } + priv2, id2, _, ipnsDHTPath2 := genKeys(t, keyType) // Publish entry err = PublishEntry(ctx, nvVstore, ipnsDHTPath2, entry) @@ -102,50 +110,52 @@ func TestResolverValidation(t *testing.T) { t.Fatal("ValidateIpnsRecord should have failed signature verification") } - // Publish entry without making public key available in peer store - priv3, id3, pubkDHTPath3, ipnsDHTPath3 := genKeys(t) - entry3, err := ipns.Create(priv3, p, 1, ts.Add(time.Hour)) - if err != nil { + // Try embedding the incorrect private key inside the entry + if err := ipns.EmbedPublicKey(priv2.GetPublic(), entry); err != nil { t.Fatal(err) } - err = PublishEntry(ctx, nvVstore, ipnsDHTPath3, entry3) + + // Publish entry + err = PublishEntry(ctx, nvVstore, ipnsDHTPath2, entry) if err != nil { t.Fatal(err) } - // Record should fail validation because public key is not available - // in peer store or on network - _, err = resolve(ctx, resolver, id3.Pretty(), opts.DefaultResolveOpts()) + // Record should fail validation because public key defined by + // ipns path doesn't match record signature + _, err = resolve(ctx, resolver, id2.Pretty(), opts.DefaultResolveOpts()) if err == nil { - t.Fatal("ValidateIpnsRecord should have failed because public key was not found") + t.Fatal("ValidateIpnsRecord should have failed signature verification") + } +} + +func genKeys(t *testing.T, keyType int) (ci.PrivKey, peer.ID, string, string) { + bits := 0 + if keyType == ci.RSA { + bits = 2048 } - // Publish public key to the network - err = PublishPublicKey(ctx, vstore, pubkDHTPath3, priv3.GetPublic()) + sk, pk, err := test.RandTestKeyPair(keyType, bits) if err != nil { t.Fatal(err) } - - // Record should now pass validation because resolver will ensure - // public key is available in the peer store by looking it up in - // the DHT, which causes the DHT to fetch it and cache it in the - // peer store - _, err = resolve(ctx, resolver, id3.Pretty(), opts.DefaultResolveOpts()) + id, err := peer.IDFromPublicKey(pk) if err != nil { t.Fatal(err) } + return sk, id, PkKeyForID(id), ipns.RecordKey(id) } -func genKeys(t *testing.T) (ci.PrivKey, peer.ID, string, string) { - sk, pk, err := test.RandTestKeyPair(ci.RSA, 2048) +func createIPNSRecordWithEmbeddedPublicKey(sk ci.PrivKey, val []byte, seq uint64, eol time.Time) (*ipns_pb.IpnsEntry, error){ + entry, err := ipns.Create(sk, val, seq, eol) if err != nil { - t.Fatal(err) + return nil, err } - id, err := peer.IDFromPublicKey(pk) - if err != nil { - t.Fatal(err) + if err := ipns.EmbedPublicKey(sk.GetPublic(), entry); err != nil { + return nil, err } - return sk, id, PkKeyForID(id), ipns.RecordKey(id) + + return entry, nil } type mockValueStore struct { diff --git a/namesys/routing.go b/namesys/routing.go index 60928fbca..8bdfe21e6 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -69,20 +69,6 @@ func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, option return out } - // Name should be the hash of a public key retrievable from ipfs. - // We retrieve the public key here to make certain that it's in the peer - // store before calling GetValue() on the DHT - the DHT will call the - // ipns validator, which in turn will get the public key from the peer - // store to verify the record signature - _, err = routing.GetPublicKey(r.routing, ctx, pid) - if err != nil { - log.Debugf("RoutingResolver: could not retrieve public key %s: %s\n", name, err) - out <- onceResult{err: err} - close(out) - cancel() - return out - } - // Use the routing system to get the name. // Note that the DHT will call the ipns validator when retrieving // the value, which in turn verifies the ipns record signature From 03b5665a74a1ad6044732da0e83807b91303a477 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Mon, 17 Aug 2020 06:58:57 -0400 Subject: [PATCH 3254/3817] chore: go fmt This commit was moved from ipfs/go-namesys@b5163cdfeaa1825fa5686544ed28aab4c0c8d702 --- namesys/ipns_resolver_validation_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 1e342f259..5dbfabf9c 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -146,7 +146,7 @@ func genKeys(t *testing.T, keyType int) (ci.PrivKey, peer.ID, string, string) { return sk, id, PkKeyForID(id), ipns.RecordKey(id) } -func createIPNSRecordWithEmbeddedPublicKey(sk ci.PrivKey, val []byte, seq uint64, eol time.Time) (*ipns_pb.IpnsEntry, error){ +func createIPNSRecordWithEmbeddedPublicKey(sk ci.PrivKey, val []byte, seq uint64, eol time.Time) (*ipns_pb.IpnsEntry, error) { entry, err := ipns.Create(sk, val, seq, eol) if err != nil { return nil, err From 70bffa80b2f1c200e26cb7750c932aa637fb9f8d Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Fri, 7 Aug 2020 16:49:02 -0400 Subject: [PATCH 3255/3817] Namesys cache uses IPNS keys with their binary representation instead of string representation to avoid encoding mismatches This commit was moved from ipfs/go-namesys@e18c5332c43a010c317a2a94319e97f8855a7826 --- namesys/namesys.go | 35 ++++++++++++++++++++--------------- namesys/namesys_test.go | 2 +- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index ac7fb0383..760d04c17 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -122,24 +122,13 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. key := segments[2] - if p, ok := ns.cacheGet(key); ok { - var err error - if len(segments) > 3 { - p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) - } - - out <- onceResult{value: p, err: err} - close(out) - return out - } - // Resolver selection: // 1. if it is a PeerID/CID/multihash resolve through "ipns". // 2. if it is a domain name, resolve through "dns" // 3. otherwise resolve through the "proquint" resolver var res resolver - _, err := peer.Decode(key) + ipnsKey, err := peer.Decode(key) // CIDs in IPNS are expected to have libp2p-key multicodec // We ease the transition by returning a more meaningful error with a valid CID @@ -155,6 +144,22 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. } } + cacheKey := key + if err == nil { + cacheKey = string(ipnsKey) + } + + if p, ok := ns.cacheGet(cacheKey); ok { + var err error + if len(segments) > 3 { + p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) + } + + out <- onceResult{value: p, err: err} + close(out) + return out + } + if err == nil { res = ns.ipnsResolver } else if isd.IsDomain(key) { @@ -172,7 +177,7 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. case res, ok := <-resCh: if !ok { if best != (onceResult{}) { - ns.cacheSet(key, best.value, best.ttl) + ns.cacheSet(cacheKey, best.value, best.ttl) } return } @@ -218,7 +223,7 @@ func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path. if err := ns.ipnsPublisher.PublishWithEOL(ctx, name, value, eol); err != nil { // Invalidate the cache. Publishing may _partially_ succeed but // still return an error. - ns.cacheInvalidate(peer.Encode(id)) + ns.cacheInvalidate(string(id)) return err } ttl := DefaultResolverCacheTTL @@ -228,6 +233,6 @@ func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path. if ttEol := time.Until(eol); ttEol < ttl { ttl = ttEol } - ns.cacheSet(peer.Encode(id), value, ttl) + ns.cacheSet(string(id), value, ttl) return nil } diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index b3e963c9e..cc0ca6959 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -155,7 +155,7 @@ func TestPublishWithTTL(t *testing.T) { if err != nil { t.Fatal(err) } - ientry, ok := nsys.(*mpns).cache.Get(pid.Pretty()) + ientry, ok := nsys.(*mpns).cache.Get(string(pid)) if !ok { t.Fatal("cache get failed") } From 2aa5ac68046d417aa2c4f92f94503ccb8918d08c Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Wed, 26 Aug 2020 13:17:48 -0400 Subject: [PATCH 3256/3817] ResolveToLastNode no longer fetches nodes it does not need This commit was moved from ipfs/go-path@ac811c4b484b06ea22da1071890e94fb4b7ab9be --- path/resolver/resolver.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 67bb9f6fb..9f153840c 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -89,6 +89,10 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (cid. return cid.Cid{}, nil, err } + if len(rest) == 0 { + return lnk.Cid, nil, nil + } + next, err := lnk.GetNode(ctx, r.DAG) if err != nil { return cid.Cid{}, nil, err From 35e8ffb3dc5c79e7d56367ce8d74bbaa83200aba Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Wed, 26 Aug 2020 13:59:39 -0400 Subject: [PATCH 3257/3817] test: add test that ResolveToLastNode does not perform unncessary fetches This commit was moved from ipfs/go-path@6d87ec04ebe94d4c105b78fe64471ebdb2a26b70 --- path/resolver/resolver_test.go | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/path/resolver/resolver_test.go b/path/resolver/resolver_test.go index 480ccdf1d..d3c6913e7 100644 --- a/path/resolver/resolver_test.go +++ b/path/resolver/resolver_test.go @@ -105,3 +105,43 @@ func TestRecurivePathResolution(t *testing.T) { p.String(), rCid.String(), cKey.String())) } } + +func TestResolveToLastNode_NoUnnecessaryFetching(t *testing.T) { + ctx := context.Background() + dagService := dagmock.Mock() + + a := randNode() + b := randNode() + + err := a.AddNodeLink("child", b) + if err != nil { + t.Fatal(err) + } + + err = dagService.Add(ctx, a) + if err != nil { + t.Fatal(err) + } + + aKey := a.Cid() + + segments := []string{aKey.String(), "child"} + p, err := path.FromSegments("/ipfs/", segments...) + if err != nil { + t.Fatal(err) + } + + resolver := resolver.NewBasicResolver(dagService) + resolvedCID, remainingPath, err := resolver.ResolveToLastNode(ctx, p) + if err != nil { + t.Fatal(err) + } + + if len(remainingPath) > 0 { + t.Fatal("cannot have remaining path") + } + + if !resolvedCID.Equals(b.Cid()) { + t.Fatal("resolved to the wrong CID") + } +} From b1c8f445e005ade7848c153bc48af703dd0eaab6 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Mon, 24 Aug 2020 15:22:37 -0400 Subject: [PATCH 3258/3817] namesys: fixed IPNS republisher to not overwrite IPNS record lifetimes This commit was moved from ipfs/go-namesys@1c7d23b0627b74cd6c0d2a7ccbe96d7c0a14529f --- namesys/republisher/repub.go | 24 +++++-- namesys/republisher/repub_test.go | 116 ++++++++++++++++++++++++++++-- 2 files changed, 129 insertions(+), 11 deletions(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 9e7272d32..ed42fa806 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -11,6 +11,7 @@ import ( proto "github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-datastore" + ipns "github.com/ipfs/go-ipns" pb "github.com/ipfs/go-ipns/pb" logging "github.com/ipfs/go-log" goprocess "github.com/jbenet/goprocess" @@ -126,7 +127,7 @@ func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) erro log.Debugf("republishing ipns entry for %s", id) // Look for it locally only - p, err := rp.getLastVal(id) + e, err := rp.getLastIPNSEntry(id) if err != nil { if err == errNoEntry { return nil @@ -134,25 +135,34 @@ func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) erro return err } + p := path.Path(e.GetValue()) + prevEol, err := ipns.GetEOL(e) + if err != nil { + return err + } + // update record with same sequence number eol := time.Now().Add(rp.RecordLifetime) + if prevEol.After(eol) { + eol = prevEol + } return rp.ns.PublishWithEOL(ctx, priv, p, eol) } -func (rp *Republisher) getLastVal(id peer.ID) (path.Path, error) { +func (rp *Republisher) getLastIPNSEntry(id peer.ID) (*pb.IpnsEntry, error) { // Look for it locally only val, err := rp.ds.Get(namesys.IpnsDsKey(id)) switch err { case nil: case ds.ErrNotFound: - return "", errNoEntry + return nil, errNoEntry default: - return "", err + return nil, err } e := new(pb.IpnsEntry) if err := proto.Unmarshal(val, e); err != nil { - return "", err + return nil, err } - return path.Path(e.Value), nil -} + return e, nil +} \ No newline at end of file diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 470d460ba..c78791397 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -6,16 +6,23 @@ import ( "testing" "time" + "github.com/gogo/protobuf/proto" + + goprocess "github.com/jbenet/goprocess" + peer "github.com/libp2p/go-libp2p-core/peer" + mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" + + ds "github.com/ipfs/go-datastore" + "github.com/ipfs/go-ipns" + "github.com/ipfs/go-ipns/pb" + path "github.com/ipfs/go-path" + "github.com/ipfs/go-ipfs/core" "github.com/ipfs/go-ipfs/core/bootstrap" mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - path "github.com/ipfs/go-path" - goprocess "github.com/jbenet/goprocess" - peer "github.com/libp2p/go-libp2p-core/peer" - mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" ) func TestRepublish(t *testing.T) { @@ -109,6 +116,107 @@ func TestRepublish(t *testing.T) { } } +func TestLongEOLRepublish(t *testing.T) { + // set cache life to zero for testing low-period repubs + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // create network + mn := mocknet.New(ctx) + + var nodes []*core.IpfsNode + for i := 0; i < 10; i++ { + nd, err := mock.MockPublicNode(ctx, mn) + if err != nil { + t.Fatal(err) + } + + nd.Namesys = namesys.NewNameSystem(nd.Routing, nd.Repo.Datastore(), 0) + + nodes = append(nodes, nd) + } + + if err := mn.LinkAll(); err != nil { + t.Fatal(err) + } + + bsinf := bootstrap.BootstrapConfigWithPeers( + []peer.AddrInfo{ + nodes[0].Peerstore.PeerInfo(nodes[0].Identity), + }, + ) + + for _, n := range nodes[1:] { + if err := n.Bootstrap(bsinf); err != nil { + t.Fatal(err) + } + } + + // have one node publish a record that is valid for 1 second + publisher := nodes[3] + p := path.FromString("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") // does not need to be valid + rp := namesys.NewIpnsPublisher(publisher.Routing, publisher.Repo.Datastore()) + name := "/ipns/" + publisher.Identity.Pretty() + + expiration := time.Now().Add(time.Hour) + err := rp.PublishWithEOL(ctx, publisher.PrivateKey, p, expiration) + if err != nil { + t.Fatal(err) + } + + err = verifyResolution(nodes, name, p) + if err != nil { + t.Fatal(err) + } + + // The republishers that are contained within the nodes have their timeout set + // to 12 hours. Instead of trying to tweak those, we're just going to pretend + // they don't exist and make our own. + repub := NewRepublisher(rp, publisher.Repo.Datastore(), publisher.PrivateKey, publisher.Repo.Keystore()) + repub.Interval = time.Millisecond * 500 + repub.RecordLifetime = time.Second + + proc := goprocess.Go(repub.Run) + defer proc.Close() + + // now wait a couple seconds for it to fire a few times + time.Sleep(time.Second * 2) + + err = verifyResolution(nodes, name, p) + if err != nil { + t.Fatal(err) + } + + entry, err := getLastIPNSEntry(publisher.Repo.Datastore(), publisher.Identity) + if err != nil{ + t.Fatal(err) + } + + finalEol, err := ipns.GetEOL(entry) + if err != nil { + t.Fatal(err) + } + + if !finalEol.Equal(expiration) { + t.Fatal("expiration time modified") + } +} + +func getLastIPNSEntry(dstore ds.Datastore, id peer.ID) (*ipns_pb.IpnsEntry, error) { + // Look for it locally only + val, err := dstore.Get(namesys.IpnsDsKey(id)) + if err != nil { + return nil, err + } + + e := new(ipns_pb.IpnsEntry) + if err := proto.Unmarshal(val, e); err != nil { + return nil, err + } + return e, nil +} + func verifyResolution(nodes []*core.IpfsNode, key string, exp path.Path) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() From 37bba12f7d1c2578ee7e12f85a447dcdf215e891 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Wed, 26 Aug 2020 15:33:32 -0400 Subject: [PATCH 3259/3817] chore: cleanup This commit was moved from ipfs/go-namesys@13be1de5b75893aa5306f1ad30daddc7122672a0 --- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index ed42fa806..84dcc911c 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -165,4 +165,4 @@ func (rp *Republisher) getLastIPNSEntry(id peer.ID) (*pb.IpnsEntry, error) { return nil, err } return e, nil -} \ No newline at end of file +} diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index c78791397..c75d7faa9 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -22,7 +22,6 @@ import ( mock "github.com/ipfs/go-ipfs/core/mock" namesys "github.com/ipfs/go-ipfs/namesys" . "github.com/ipfs/go-ipfs/namesys/republisher" - ) func TestRepublish(t *testing.T) { @@ -189,7 +188,7 @@ func TestLongEOLRepublish(t *testing.T) { } entry, err := getLastIPNSEntry(publisher.Repo.Datastore(), publisher.Identity) - if err != nil{ + if err != nil { t.Fatal(err) } From adf1adaa2e195c3ffe0e5be07354e9f866b0e826 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Thu, 27 Aug 2020 15:05:05 -0400 Subject: [PATCH 3260/3817] Initial commit This commit was moved from ipfs/go-pinning-service-http-client@840d21a10adc12cb4b0875b261ae491f4d1f088b --- pinning/remote/client/.gitignore | 15 +++++++++++++++ pinning/remote/client/LICENSE | 21 +++++++++++++++++++++ pinning/remote/client/README.md | 2 ++ 3 files changed, 38 insertions(+) create mode 100644 pinning/remote/client/.gitignore create mode 100644 pinning/remote/client/LICENSE create mode 100644 pinning/remote/client/README.md diff --git a/pinning/remote/client/.gitignore b/pinning/remote/client/.gitignore new file mode 100644 index 000000000..66fd13c90 --- /dev/null +++ b/pinning/remote/client/.gitignore @@ -0,0 +1,15 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ diff --git a/pinning/remote/client/LICENSE b/pinning/remote/client/LICENSE new file mode 100644 index 000000000..2b5d8a7da --- /dev/null +++ b/pinning/remote/client/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 IPFS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pinning/remote/client/README.md b/pinning/remote/client/README.md new file mode 100644 index 000000000..0a2542a8a --- /dev/null +++ b/pinning/remote/client/README.md @@ -0,0 +1,2 @@ +# go-pinning-service-http-client +An IPFS Pinning Service HTTP Client From aaa4dcd596eed9264dd196cb1c1b53b293b9562e Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Mon, 21 Sep 2020 13:07:01 -0400 Subject: [PATCH 3261/3817] init: use template for the dual Apache-MIT license and basic README This commit was moved from ipfs/go-pinning-service-http-client@4ebc72023aa7f7ecb39edcfe7f133ddfb3eceb65 --- pinning/remote/client/LICENSE | 35 +++++++++++++++++---------------- pinning/remote/client/README.md | 20 +++++++++++++++++++ 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/pinning/remote/client/LICENSE b/pinning/remote/client/LICENSE index 2b5d8a7da..1f34f7186 100644 --- a/pinning/remote/client/LICENSE +++ b/pinning/remote/client/LICENSE @@ -1,21 +1,22 @@ -MIT License +The software contents of this repository are Copyright (c) Protocol Labs, +Licensed under the `Permissive License Stack`, meaning either of: -Copyright (c) 2020 IPFS +- Apache-2.0 Software License: https://www.apache.org/licenses/LICENSE-2.0 + ([...4tr2kfsq](https://gateway.ipfs.io/ipfs/bafkreiankqxazcae4onkp436wag2lj3ccso4nawxqkkfckd6cg4tr2kfsq)) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +- MIT Software License: https://opensource.org/licenses/MIT + ([...vljevcba](https://gateway.ipfs.io/ipfs/bafkreiepofszg4gfe2gzuhojmksgemsub2h4uy2gewdnr35kswvljevcba)) -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +You may not use the contents of this repository except in compliance +with one of the listed Licenses. For an extended clarification of the +intent behind the choice of Licensing please refer to +https://protocol.ai/blog/announcing-the-permissive-license-stack/ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Unless required by applicable law or agreed to in writing, software +distributed under the terms listed in this notice is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +either express or implied. See each License for the specific language +governing permissions and limitations under that License. + + +`SPDX-License-Identifier: Apache-2.0 OR MIT` \ No newline at end of file diff --git a/pinning/remote/client/README.md b/pinning/remote/client/README.md index 0a2542a8a..213f0df83 100644 --- a/pinning/remote/client/README.md +++ b/pinning/remote/client/README.md @@ -1,2 +1,22 @@ # go-pinning-service-http-client + + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.io/) +[![](https://img.shields.io/badge/status-draft-yellow.svg?style=flat-square)](https://github.com/ipfs/specs/#understanding-the-meaning-of-the-spec-badges-and-their-lifecycle) + An IPFS Pinning Service HTTP Client + +> This repo is contains a reference implementation of a client for the [IPFS Pinning Services API Spec](https://github.com/ipfs/pinning-services-api-spec) + +## Lead Maintainer + +[Adin Schmahmann](https://github.com/aschmahmann) + +## Contributing + +Contributions are welcome! This repository is part of the IPFS project and therefore governed by our [contributing guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md). + +## License + +[SPDX-License-Identifier: Apache-2.0 OR MIT](LICENSE.md) \ No newline at end of file From 67cf885fb723b295db69cd8d2e1791b4d66c9f39 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Sat, 29 Aug 2020 21:25:37 -0400 Subject: [PATCH 3262/3817] feat: initial implementation This commit was moved from ipfs/go-pinning-service-http-client@08125a6c7413fc6b06db906e9feee0b8eaebb050 --- pinning/remote/client/client.go | 376 +++++++++ pinning/remote/client/cmd/main.go | 77 ++ pinning/remote/client/model.go | 79 ++ pinning/remote/client/openapi/README.md | 205 +++++ pinning/remote/client/openapi/api_pins.go | 781 ++++++++++++++++++ pinning/remote/client/openapi/client.go | 531 ++++++++++++ .../remote/client/openapi/configuration.go | 228 +++++ pinning/remote/client/openapi/docs/Error.md | 72 ++ pinning/remote/client/openapi/docs/Pin.md | 129 +++ .../remote/client/openapi/docs/PinResults.md | 72 ++ .../remote/client/openapi/docs/PinStatus.md | 161 ++++ pinning/remote/client/openapi/docs/PinsApi.md | 367 ++++++++ pinning/remote/client/openapi/docs/Status.md | 11 + pinning/remote/client/openapi/model_error.go | 134 +++ pinning/remote/client/openapi/model_pin.go | 217 +++++ .../client/openapi/model_pin_results.go | 136 +++ .../remote/client/openapi/model_pin_status.go | 262 ++++++ pinning/remote/client/openapi/model_status.go | 84 ++ pinning/remote/client/openapi/response.go | 46 ++ pinning/remote/client/openapi/utils.go | 327 ++++++++ 20 files changed, 4295 insertions(+) create mode 100644 pinning/remote/client/client.go create mode 100644 pinning/remote/client/cmd/main.go create mode 100644 pinning/remote/client/model.go create mode 100644 pinning/remote/client/openapi/README.md create mode 100644 pinning/remote/client/openapi/api_pins.go create mode 100644 pinning/remote/client/openapi/client.go create mode 100644 pinning/remote/client/openapi/configuration.go create mode 100644 pinning/remote/client/openapi/docs/Error.md create mode 100644 pinning/remote/client/openapi/docs/Pin.md create mode 100644 pinning/remote/client/openapi/docs/PinResults.md create mode 100644 pinning/remote/client/openapi/docs/PinStatus.md create mode 100644 pinning/remote/client/openapi/docs/PinsApi.md create mode 100644 pinning/remote/client/openapi/docs/Status.md create mode 100644 pinning/remote/client/openapi/model_error.go create mode 100644 pinning/remote/client/openapi/model_pin.go create mode 100644 pinning/remote/client/openapi/model_pin_results.go create mode 100644 pinning/remote/client/openapi/model_pin_status.go create mode 100644 pinning/remote/client/openapi/model_status.go create mode 100644 pinning/remote/client/openapi/response.go create mode 100644 pinning/remote/client/openapi/utils.go diff --git a/pinning/remote/client/client.go b/pinning/remote/client/client.go new file mode 100644 index 000000000..b4fa37830 --- /dev/null +++ b/pinning/remote/client/client.go @@ -0,0 +1,376 @@ +package go_pinning_service_http_client + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "time" + + "github.com/ipfs/go-cid" + "github.com/ipfs/go-pinning-service-http-client/openapi" + "github.com/multiformats/go-multiaddr" + "github.com/multiformats/go-multibase" + + logging "github.com/ipfs/go-log/v2" +) + +var logger = logging.Logger("pinning-service-http-client") + +const UserAgent = "go-pinning-service-http-client" + +type Client struct { + client *openapi.APIClient +} + +func NewClient(url, bearerToken string) *Client { + config := openapi.NewConfiguration() + config.UserAgent = UserAgent + bearer := fmt.Sprintf("Bearer %s", bearerToken) + config.AddDefaultHeader("Authorization", bearer) + config.Servers = openapi.ServerConfigurations{ + openapi.ServerConfiguration{ + URL: url, + }, + } + + return &Client{client: openapi.NewAPIClient(config)} +} + +func getError(e *openapi.Error) error { + return fmt.Errorf("request error: %d - %s", e.Code, e.Message) +} + +// TODO: We should probably make sure there are no duplicates sent +type lsSettings struct { + cids []string + name string + status []Status + before *time.Time + after *time.Time + limit *int32 + meta map[string]string +} + +type LsOption func(options *lsSettings) error + +var PinOpts = pinOpts{} + +type pinOpts struct { + pinLsOpts + pinAddOpts +} + +type pinLsOpts struct{} + +func (pinLsOpts) FilterCIDs(cids ...cid.Cid) LsOption { + return func(options *lsSettings) error { + enc := getCIDEncoder() + for _, c := range cids { + options.cids = append(options.cids, c.Encode(enc)) + } + return nil + } +} + +const maxNameSize = 255 + +func (pinLsOpts) FilterName(name string) LsOption { + return func(options *lsSettings) error { + if len(name) > maxNameSize { + return fmt.Errorf("name cannot be longer than %d", maxNameSize) + } + options.name = name + return nil + } +} + +func (pinLsOpts) FilterStatus(statuses ...Status) LsOption { + return func(options *lsSettings) error { + for _, s := range statuses { + valid := false + for _, existing := range validStatuses { + if existing == s { + valid = true + break + } + } + if !valid { + return fmt.Errorf("invalid status %s", s) + } + } + options.status = append(options.status, statuses...) + return nil + } +} + +func (pinLsOpts) FilterBefore(t time.Time) LsOption { + return func(options *lsSettings) error { + options.before = &t + return nil + } +} + +func (pinLsOpts) FilterAfter(t time.Time) LsOption { + return func(options *lsSettings) error { + options.after = &t + return nil + } +} + +const recordLimit = 1000 +const defaultLimit = 10 + +func (pinLsOpts) Limit(limit int) LsOption { + return func(options *lsSettings) error { + if limit > recordLimit { + return fmt.Errorf("limit exceeded maximum record limit of %d", recordLimit) + } + limitCasted := int32(limit) + options.limit = &limitCasted + return nil + } +} + +func (pinLsOpts) LsMeta(meta map[string]string) LsOption { + return func(options *lsSettings) error { + options.meta = meta + return nil + } +} + +type pinResults = openapi.PinResults + +func (c *Client) Ls(ctx context.Context, opts ...LsOption) (chan PinStatusGetter, chan error) { + res := make(chan PinStatusGetter, 1) + errs := make(chan error, 1) + + settings := new(lsSettings) + for _, o := range opts { + if err := o(settings); err != nil { + close(res) + errs <- err + close(errs) + return res, errs + } + } + + go func() { + defer close(errs) + defer close(res) + + for { + pinRes, err := c.lsInternal(ctx, settings) + if err != nil { + errs <- err + return + } + + results := pinRes.GetResults() + for _, r := range results { + select { + case res <- &pinStatusObject{r}: + case <-ctx.Done(): + errs <- ctx.Err() + return + } + } + + if int(pinRes.Count) == len(results) { + return + } + + oldestResult := pinRes.Results[len(pinRes.Results)-1] + settings.before = &oldestResult.Created + } + }() + + return res, errs +} + +func (c *Client) LsSync(ctx context.Context, opts ...LsOption) ([]PinStatusGetter, error) { + resCh, errCh := c.Ls(ctx, opts...) + + var res []PinStatusGetter + for r := range resCh { + res = append(res, r) + } + + return res, <-errCh +} + +func (c *Client) lsInternal(ctx context.Context, settings *lsSettings) (pinResults, error) { + getter := c.client.PinsApi.PinsGet(ctx) + if len(settings.cids) > 0 { + getter.Cid(settings.cids) + } + if len(settings.status) > 0 { + getter.Status(settings.status) + } + if settings.limit == nil { + getter.Limit(defaultLimit) + } else { + getter.Limit(*settings.limit) + } + if len(settings.name) > 0 { + getter.Name(settings.name) + } + if settings.before != nil { + getter.Before(*settings.before) + } + if settings.after != nil { + getter.After(*settings.after) + } + if settings.meta != nil { + getter.Meta(settings.meta) + } + + // TODO: Ignoring HTTP Response OK? + results, httpresp, err := getter.Execute() + if err != nil { + err := httperr(httpresp, err) + return pinResults{}, err + } + + return results, nil +} + +// TODO: We should probably make sure there are no duplicates sent +type addSettings struct { + cid string + name string + origins []string + meta map[string]string +} + +type AddOption func(options *addSettings) error + +type pinAddOpts struct{} + +func (pinAddOpts) WithName(name string) AddOption { + return func(options *addSettings) error { + if len(name) > maxNameSize { + return fmt.Errorf("name cannot be longer than %d", maxNameSize) + } + options.name = name + return nil + } +} + +func (pinLsOpts) WithOrigins(origins ...multiaddr.Multiaddr) AddOption { + return func(options *addSettings) error { + for _, o := range origins { + options.origins = append(options.origins, o.String()) + } + return nil + } +} + +func (pinAddOpts) AddMeta(meta map[string]string) AddOption { + return func(options *addSettings) error { + options.meta = meta + return nil + } +} + +func (c *Client) Add(ctx context.Context, cid cid.Cid, opts ...AddOption) (PinStatusGetter, error) { + settings := new(addSettings) + for _, o := range opts { + if err := o(settings); err != nil { + return nil, err + } + } + + adder := c.client.PinsApi.PinsPost(ctx) + p := openapi.Pin{ + Cid: cid.Encode(getCIDEncoder()), + } + + if len(settings.origins) > 0 { + p.SetOrigins(settings.origins) + } + if settings.meta != nil { + p.SetMeta(settings.meta) + } + if len(settings.name) > 0 { + p.SetName(settings.name) + } + + result, httpresp, err := adder.Pin(p).Execute() + if err != nil { + err := httperr(httpresp, err) + return nil, err + } + + return &pinStatusObject{result}, nil +} + +func (c *Client) GetStatusByID(ctx context.Context, pinID string) (PinStatusGetter, error) { + getter := c.client.PinsApi.PinsIdGet(ctx, pinID) + result, httpresp, err := getter.Execute() + if err != nil { + err := httperr(httpresp, err) + return nil, err + } + + return &pinStatusObject{result}, nil +} + +func (c *Client) DeleteByID(ctx context.Context, pinID string) error { + deleter := c.client.PinsApi.PinsIdDelete(ctx, pinID) + httpresp, err := deleter.Execute() + if err != nil { + err := httperr(httpresp, err) + return err + } + return nil +} + +func (c *Client) Modify(ctx context.Context, pinID string, cid cid.Cid, opts ...AddOption) (PinStatusGetter, error) { + settings := new(addSettings) + for _, o := range opts { + if err := o(settings); err != nil { + return nil, err + } + } + + adder := c.client.PinsApi.PinsIdPost(ctx, pinID) + p := openapi.Pin{ + Cid: cid.Encode(getCIDEncoder()), + } + + if len(settings.origins) > 0 { + p.SetOrigins(settings.origins) + } + if settings.meta != nil { + p.SetMeta(settings.meta) + } + if len(settings.name) > 0 { + p.SetName(settings.name) + } + + result, httpresp, err := adder.Pin(p).Execute() + if err != nil { + err := httperr(httpresp, err) + return nil, err + } + + return &pinStatusObject{result}, nil +} + +func getCIDEncoder() multibase.Encoder { + enc, err := multibase.NewEncoder(multibase.Base32) + if err != nil { + panic(err) + } + return enc +} + +func httperr(resp *http.Response, e error) error { + body, err := ioutil.ReadAll(resp.Body) + var bodystr string + if err == nil { + bodystr = string(body) + } + return fmt.Errorf("httpresp code: %d, httpresp: %s, httpbody: %s, err: %w", resp.StatusCode, resp.Status, bodystr, e) +} diff --git a/pinning/remote/client/cmd/main.go b/pinning/remote/client/cmd/main.go new file mode 100644 index 000000000..fefd9503d --- /dev/null +++ b/pinning/remote/client/cmd/main.go @@ -0,0 +1,77 @@ +package main + +import ( + "context" + "fmt" + "github.com/ipfs/go-cid" + pinclient "github.com/ipfs/go-pinning-service-http-client" + "os" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + url, ok := os.LookupEnv("PS_URL") + if !ok { + panic("No Pinning Service URL found") + } + + key, ok := os.LookupEnv("PS_KEY") + if !ok { + panic("No Pinning Service API Key found") + } + + c := pinclient.NewClient(url, key) + + ipfsPgCid, err := cid.Parse("bafybeiayvrj27f65vbecspbnuavehcb3znvnt2strop2rfbczupudoizya") + if err != nil { + panic(err) + } + + libp2pCid, err := cid.Parse("bafybeiejgrxo4p4uofgfzvlg5twrg5w7tfwpf7aciiswfacfbdpevg2xfy") + if err != nil { + panic(err) + } + _ = ipfsPgCid + + fmt.Println("Adding libp2p home page") + ps, err := c.Add(ctx, libp2pCid, pinclient.PinOpts.WithName("libp2p_home")) + if err == nil { + fmt.Println(ps.GetStatus()) + } else { + fmt.Println(err) + } + + fmt.Println("List all pins") + pins, err := c.LsSync(ctx) + fmt.Println(err) + + for _, p := range pins { + fmt.Printf("Pin Name: %s, CID: %s", p.GetPin().GetName(), p.GetPin().GetCid().String()) + } + + fmt.Println("Check on pin status") + status, err := c.GetStatusByID(ctx, ps.GetId()) + if err == nil { + fmt.Println(status.GetStatus()) + } else { + fmt.Println(err) + } + + fmt.Println("Delete pin") + err = c.DeleteByID(ctx, ps.GetId()) + if err == nil { + fmt.Println("Successfully deleted pin") + } else { + fmt.Println(err) + } + + fmt.Println("List all pins") + pins, err = c.LsSync(ctx) + fmt.Println(err) + + for _, p := range pins { + fmt.Printf("Pin Name: %s, CID: %s", p.GetPin().GetName(), p.GetPin().GetCid().String()) + } +} diff --git a/pinning/remote/client/model.go b/pinning/remote/client/model.go new file mode 100644 index 000000000..e6c552d51 --- /dev/null +++ b/pinning/remote/client/model.go @@ -0,0 +1,79 @@ +package go_pinning_service_http_client + +import ( + "github.com/ipfs/go-cid" + "github.com/ipfs/go-pinning-service-http-client/openapi" + "github.com/multiformats/go-multiaddr" + "time" +) + +// PinGetter Getter for Pin object +type PinGetter interface { + // CID to be pinned recursively + GetCid() cid.Cid + // Optional name for pinned data; can be used for lookups later + GetName() string + // Optional list of multiaddrs known to provide the data + GetOrigins() []string + // Optional metadata for pin object + GetMeta() map[string]string +} + +type pinObject struct { + openapi.Pin +} + +func (p *pinObject) GetCid() cid.Cid { + c, err := cid.Parse(p.Pin.Cid) + if err != nil { + return cid.Undef + } + return c +} + +type Status = openapi.Status + +const ( + StatusQueued Status = openapi.QUEUED + StatusPinning Status = openapi.PINNING + StatusPinned Status = openapi.PINNED + StatusFailed Status = openapi.FAILED +) + +var validStatuses = []Status{"queued", "pinning", "pinned", "failed"} + +// PinStatusGetter Getter for Pin object with status +type PinStatusGetter interface { + // Globally unique ID of the pin request; can be used to check the status of ongoing pinning, modification of pin object, or pin removal + GetId() string + GetStatus() Status + // Immutable timestamp indicating when a pin request entered a pinning service; can be used for filtering results and pagination + GetCreated() time.Time + GetPin() PinGetter + // List of multiaddrs designated by pinning service for transferring any new data from external peers + GetDelegates() []multiaddr.Multiaddr + // Optional info for PinStatus response + GetInfo() map[string]string +} + +type pinStatusObject struct { + openapi.PinStatus +} + +func (p *pinStatusObject) GetDelegates() []multiaddr.Multiaddr { + delegates := p.PinStatus.GetDelegates() + addrs := make([]multiaddr.Multiaddr, 0, len(delegates)) + for _, d := range delegates { + a, err := multiaddr.NewMultiaddr(d) + if err != nil { + logger.Errorf("returned delegate is an invalid multiaddr: %w", err) + continue + } + addrs = append(addrs, a) + } + return addrs +} + +func (p *pinStatusObject) GetPin() PinGetter { + return &pinObject{p.Pin} +} diff --git a/pinning/remote/client/openapi/README.md b/pinning/remote/client/openapi/README.md new file mode 100644 index 000000000..64e843f88 --- /dev/null +++ b/pinning/remote/client/openapi/README.md @@ -0,0 +1,205 @@ +# Go API client for openapi + + + +## About this spec +The IPFS Pinning Service API is intended to be an implementation-agnostic API: +- For use and implementation by pinning service providers +- For use in client mode by IPFS nodes and GUI-based applications + +> **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** + +# Schemas +This section describes the most important object types and conventions. + +A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). +## Objects +### Pin object + +![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) + +The `Pin` object is a representation of a pin request. + +It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. + +### Pin status response + +![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) + +The `PinStatus` object is a representation of the current state of a pinning operation. +It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. + +## The pin lifecycle + +![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) + +### Creating a new pin object +The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: +- `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future +- `status` in `PinStatus` indicates the current state of a pin + +### Checking status of in-progress pinning +`status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. + +In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. + +### Modifying an existing pin object +The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. + +### Removing a pin object +A pin object can be removed via `DELETE /pins/{id}`. + + +## Provider hints +Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. + +The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. + +This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. + +## Custom metadata +Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. +### Pin metadata +String keys and values passed in `Pin.meta` are persisted with the pin object. + +Potential uses: +- `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` +- `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) + +Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. + +### Pin status info +Additional `PinStatus.info` can be returned by pinning service. + +Potential uses: +- `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) +- `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead +- `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) +- `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire + +# Pagination and filtering +Pin objects can be listed by executing `GET /pins` with optional parameters: + +- When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. +- The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). +- If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. +- To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. +- Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. + +> **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + + + +## Overview +This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [OpenAPI-spec](https://www.openapis.org/) from a remote server, you can easily generate an API client. + +- API version: 0.0.5 +- Package version: 1.0.0 +- Build package: org.openapitools.codegen.languages.GoClientExperimentalCodegen + +## Installation + +Install the following dependencies: + +```shell +go get github.com/stretchr/testify/assert +go get golang.org/x/oauth2 +go get golang.org/x/net/context +``` + +Put the package under your project folder and add the following in import: + +```golang +import sw "./openapi" +``` + +## Configuration of Server URL + +Default configuration comes with `Servers` field that contains server objects as defined in the OpenAPI specification. + +### Select Server Configuration + +For using other server than the one defined on index 0 set context value `sw.ContextServerIndex` of type `int`. + +```golang +ctx := context.WithValue(context.Background(), sw.ContextServerIndex, 1) +``` + +### Templated Server URL + +Templated server URL is formatted using default variables from configuration or from context value `sw.ContextServerVariables` of type `map[string]string`. + +```golang +ctx := context.WithValue(context.Background(), sw.ContextServerVariables, map[string]string{ + "basePath": "v2", +}) +``` + +Note, enum values are always validated and all unused variables are silently ignored. + +### URLs Configuration per Operation + +Each operation can use different server URL defined using `OperationServers` map in the `Configuration`. +An operation is uniquely identifield by `"{classname}Service.{nickname}"` string. +Similar rules for overriding default operation server index and variables applies by using `sw.ContextOperationServerIndices` and `sw.ContextOperationServerVariables` context maps. + +``` +ctx := context.WithValue(context.Background(), sw.ContextOperationServerIndices, map[string]int{ + "{classname}Service.{nickname}": 2, +}) +ctx = context.WithValue(context.Background(), sw.ContextOperationServerVariables, map[string]map[string]string{ + "{classname}Service.{nickname}": { + "port": "8443", + }, +}) +``` + +## Documentation for API Endpoints + +All URIs are relative to *https://pinning-service.example.com* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +*PinsApi* | [**PinsGet**](docs/PinsApi.md#pinsget) | **Get** /pins | List pin objects +*PinsApi* | [**PinsIdDelete**](docs/PinsApi.md#pinsiddelete) | **Delete** /pins/{id} | Remove pin object +*PinsApi* | [**PinsIdGet**](docs/PinsApi.md#pinsidget) | **Get** /pins/{id} | Get pin object +*PinsApi* | [**PinsIdPost**](docs/PinsApi.md#pinsidpost) | **Post** /pins/{id} | Modify pin object +*PinsApi* | [**PinsPost**](docs/PinsApi.md#pinspost) | **Post** /pins | Add pin object + + +## Documentation For Models + + - [Error](docs/Error.md) + - [Pin](docs/Pin.md) + - [PinResults](docs/PinResults.md) + - [PinStatus](docs/PinStatus.md) + - [Status](docs/Status.md) + + +## Documentation For Authorization + + + +### accessToken + + +## Documentation for Utility Methods + +Due to the fact that model structure members are all pointers, this package contains +a number of utility functions to easily obtain pointers to values of basic types. +Each of these functions takes a value of the given basic type and returns a pointer to it: + +* `PtrBool` +* `PtrInt` +* `PtrInt32` +* `PtrInt64` +* `PtrFloat` +* `PtrFloat32` +* `PtrFloat64` +* `PtrString` +* `PtrTime` + +## Author + + + diff --git a/pinning/remote/client/openapi/api_pins.go b/pinning/remote/client/openapi/api_pins.go new file mode 100644 index 000000000..623862df7 --- /dev/null +++ b/pinning/remote/client/openapi/api_pins.go @@ -0,0 +1,781 @@ +/* + * IPFS Pinning Service API + * + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * API version: 0.0.5 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + _context "context" + _ioutil "io/ioutil" + _nethttp "net/http" + _neturl "net/url" + "strings" + "time" +) + +// Linger please +var ( + _ _context.Context +) + +// PinsApiService PinsApi service +type PinsApiService service + +type apiPinsGetRequest struct { + ctx _context.Context + apiService *PinsApiService + cid *[]string + name *string + status *[]Status + before *time.Time + after *time.Time + limit *int32 + meta *map[string]string +} + +func (r apiPinsGetRequest) Cid(cid []string) apiPinsGetRequest { + r.cid = &cid + return r +} + +func (r apiPinsGetRequest) Name(name string) apiPinsGetRequest { + r.name = &name + return r +} + +func (r apiPinsGetRequest) Status(status []Status) apiPinsGetRequest { + r.status = &status + return r +} + +func (r apiPinsGetRequest) Before(before time.Time) apiPinsGetRequest { + r.before = &before + return r +} + +func (r apiPinsGetRequest) After(after time.Time) apiPinsGetRequest { + r.after = &after + return r +} + +func (r apiPinsGetRequest) Limit(limit int32) apiPinsGetRequest { + r.limit = &limit + return r +} + +func (r apiPinsGetRequest) Meta(meta map[string]string) apiPinsGetRequest { + r.meta = &meta + return r +} + +/* +PinsGet List pin objects +List all the pin objects, matching optional filters; when no filter is provided, only successful pins are returned + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). +@return apiPinsGetRequest +*/ +func (a *PinsApiService) PinsGet(ctx _context.Context) apiPinsGetRequest { + return apiPinsGetRequest{ + apiService: a, + ctx: ctx, + } +} + +/* +Execute executes the request + @return PinResults +*/ +func (r apiPinsGetRequest) Execute() (PinResults, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodGet + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue PinResults + ) + + localBasePath, err := r.apiService.client.cfg.ServerURLWithContext(r.ctx, "PinsApiService.PinsGet") + if err != nil { + return localVarReturnValue, nil, GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/pins" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + if r.cid != nil { + localVarQueryParams.Add("cid", parameterToString(*r.cid, "csv")) + } + if r.name != nil { + localVarQueryParams.Add("name", parameterToString(*r.name, "")) + } + if r.status != nil { + localVarQueryParams.Add("status", parameterToString(*r.status, "csv")) + } + if r.before != nil { + localVarQueryParams.Add("before", parameterToString(*r.before, "")) + } + if r.after != nil { + localVarQueryParams.Add("after", parameterToString(*r.after, "")) + } + if r.limit != nil { + localVarQueryParams.Add("limit", parameterToString(*r.limit, "")) + } + if r.meta != nil { + localVarQueryParams.Add("meta", parameterToString(*r.meta, "")) + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := r.apiService.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := r.apiService.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = r.apiService.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type apiPinsIdDeleteRequest struct { + ctx _context.Context + apiService *PinsApiService + id string +} + +/* +PinsIdDelete Remove pin object +Remove a pin object + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param id +@return apiPinsIdDeleteRequest +*/ +func (a *PinsApiService) PinsIdDelete(ctx _context.Context, id string) apiPinsIdDeleteRequest { + return apiPinsIdDeleteRequest{ + apiService: a, + ctx: ctx, + id: id, + } +} + +/* +Execute executes the request + +*/ +func (r apiPinsIdDeleteRequest) Execute() (*_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodDelete + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + ) + + localBasePath, err := r.apiService.client.cfg.ServerURLWithContext(r.ctx, "PinsApiService.PinsIdDelete") + if err != nil { + return nil, GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/pins/{id}" + localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", _neturl.PathEscape(parameterToString(r.id, "")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := r.apiService.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHTTPResponse, err := r.apiService.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + return localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + return localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + return localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarHTTPResponse, newErr + } + + return localVarHTTPResponse, nil +} + +type apiPinsIdGetRequest struct { + ctx _context.Context + apiService *PinsApiService + id string +} + +/* +PinsIdGet Get pin object +Get a pin object and its status + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param id +@return apiPinsIdGetRequest +*/ +func (a *PinsApiService) PinsIdGet(ctx _context.Context, id string) apiPinsIdGetRequest { + return apiPinsIdGetRequest{ + apiService: a, + ctx: ctx, + id: id, + } +} + +/* +Execute executes the request + @return PinStatus +*/ +func (r apiPinsIdGetRequest) Execute() (PinStatus, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodGet + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue PinStatus + ) + + localBasePath, err := r.apiService.client.cfg.ServerURLWithContext(r.ctx, "PinsApiService.PinsIdGet") + if err != nil { + return localVarReturnValue, nil, GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/pins/{id}" + localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", _neturl.PathEscape(parameterToString(r.id, "")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := r.apiService.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := r.apiService.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = r.apiService.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type apiPinsIdPostRequest struct { + ctx _context.Context + apiService *PinsApiService + id string + pin *Pin +} + +func (r apiPinsIdPostRequest) Pin(pin Pin) apiPinsIdPostRequest { + r.pin = &pin + return r +} + +/* +PinsIdPost Modify pin object +Modify an existing pin object + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param id +@return apiPinsIdPostRequest +*/ +func (a *PinsApiService) PinsIdPost(ctx _context.Context, id string) apiPinsIdPostRequest { + return apiPinsIdPostRequest{ + apiService: a, + ctx: ctx, + id: id, + } +} + +/* +Execute executes the request + @return PinStatus +*/ +func (r apiPinsIdPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodPost + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue PinStatus + ) + + localBasePath, err := r.apiService.client.cfg.ServerURLWithContext(r.ctx, "PinsApiService.PinsIdPost") + if err != nil { + return localVarReturnValue, nil, GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/pins/{id}" + localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", _neturl.PathEscape(parameterToString(r.id, "")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + if r.pin == nil { + return localVarReturnValue, nil, reportError("pin is required and must be specified") + } + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = r.pin + req, err := r.apiService.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := r.apiService.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 409 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = r.apiService.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type apiPinsPostRequest struct { + ctx _context.Context + apiService *PinsApiService + pin *Pin +} + +func (r apiPinsPostRequest) Pin(pin Pin) apiPinsPostRequest { + r.pin = &pin + return r +} + +/* +PinsPost Add pin object +Add a new pin object for the current access token + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). +@return apiPinsPostRequest +*/ +func (a *PinsApiService) PinsPost(ctx _context.Context) apiPinsPostRequest { + return apiPinsPostRequest{ + apiService: a, + ctx: ctx, + } +} + +/* +Execute executes the request + @return PinStatus +*/ +func (r apiPinsPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodPost + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue PinStatus + ) + + localBasePath, err := r.apiService.client.cfg.ServerURLWithContext(r.ctx, "PinsApiService.PinsPost") + if err != nil { + return localVarReturnValue, nil, GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/pins" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + if r.pin == nil { + return localVarReturnValue, nil, reportError("pin is required and must be specified") + } + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = r.pin + req, err := r.apiService.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := r.apiService.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 409 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = r.apiService.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} diff --git a/pinning/remote/client/openapi/client.go b/pinning/remote/client/openapi/client.go new file mode 100644 index 000000000..2fa0b79ab --- /dev/null +++ b/pinning/remote/client/openapi/client.go @@ -0,0 +1,531 @@ +/* + * IPFS Pinning Service API + * + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * API version: 0.0.5 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "bytes" + "context" + "encoding/json" + "encoding/xml" + "errors" + "fmt" + "io" + "log" + "mime/multipart" + "net/http" + "net/http/httputil" + "net/url" + "os" + "path/filepath" + "reflect" + "regexp" + "strconv" + "strings" + "time" + "unicode/utf8" + + "golang.org/x/oauth2" +) + +var ( + jsonCheck = regexp.MustCompile(`(?i:(?:application|text)/(?:vnd\.[^;]+\+)?json)`) + xmlCheck = regexp.MustCompile(`(?i:(?:application|text)/xml)`) +) + +// APIClient manages communication with the IPFS Pinning Service API API v0.0.5 +// In most cases there should be only one, shared, APIClient. +type APIClient struct { + cfg *Configuration + common service // Reuse a single struct instead of allocating one for each service on the heap. + + // API Services + + PinsApi *PinsApiService +} + +type service struct { + client *APIClient +} + +// NewAPIClient creates a new API client. Requires a userAgent string describing your application. +// optionally a custom http.Client to allow for advanced features such as caching. +func NewAPIClient(cfg *Configuration) *APIClient { + if cfg.HTTPClient == nil { + cfg.HTTPClient = http.DefaultClient + } + + c := &APIClient{} + c.cfg = cfg + c.common.client = c + + // API Services + c.PinsApi = (*PinsApiService)(&c.common) + + return c +} + +func atoi(in string) (int, error) { + return strconv.Atoi(in) +} + +// selectHeaderContentType select a content type from the available list. +func selectHeaderContentType(contentTypes []string) string { + if len(contentTypes) == 0 { + return "" + } + if contains(contentTypes, "application/json") { + return "application/json" + } + return contentTypes[0] // use the first content type specified in 'consumes' +} + +// selectHeaderAccept join all accept types and return +func selectHeaderAccept(accepts []string) string { + if len(accepts) == 0 { + return "" + } + + if contains(accepts, "application/json") { + return "application/json" + } + + return strings.Join(accepts, ",") +} + +// contains is a case insenstive match, finding needle in a haystack +func contains(haystack []string, needle string) bool { + for _, a := range haystack { + if strings.ToLower(a) == strings.ToLower(needle) { + return true + } + } + return false +} + +// Verify optional parameters are of the correct type. +func typeCheckParameter(obj interface{}, expected string, name string) error { + // Make sure there is an object. + if obj == nil { + return nil + } + + // Check the type is as expected. + if reflect.TypeOf(obj).String() != expected { + return fmt.Errorf("Expected %s to be of type %s but received %s.", name, expected, reflect.TypeOf(obj).String()) + } + return nil +} + +// parameterToString convert interface{} parameters to string, using a delimiter if format is provided. +func parameterToString(obj interface{}, collectionFormat string) string { + var delimiter string + + switch collectionFormat { + case "pipes": + delimiter = "|" + case "ssv": + delimiter = " " + case "tsv": + delimiter = "\t" + case "csv": + delimiter = "," + } + + if reflect.TypeOf(obj).Kind() == reflect.Slice { + return strings.Trim(strings.Replace(fmt.Sprint(obj), " ", delimiter, -1), "[]") + } else if t, ok := obj.(time.Time); ok { + return t.Format(time.RFC3339) + } + + return fmt.Sprintf("%v", obj) +} + +// helper for converting interface{} parameters to json strings +func parameterToJson(obj interface{}) (string, error) { + jsonBuf, err := json.Marshal(obj) + if err != nil { + return "", err + } + return string(jsonBuf), err +} + +// callAPI do the request. +func (c *APIClient) callAPI(request *http.Request) (*http.Response, error) { + if c.cfg.Debug { + dump, err := httputil.DumpRequestOut(request, true) + if err != nil { + return nil, err + } + log.Printf("\n%s\n", string(dump)) + } + + resp, err := c.cfg.HTTPClient.Do(request) + if err != nil { + return resp, err + } + + if c.cfg.Debug { + dump, err := httputil.DumpResponse(resp, true) + if err != nil { + return resp, err + } + log.Printf("\n%s\n", string(dump)) + } + return resp, err +} + +// Allow modification of underlying config for alternate implementations and testing +// Caution: modifying the configuration while live can cause data races and potentially unwanted behavior +func (c *APIClient) GetConfig() *Configuration { + return c.cfg +} + +// prepareRequest build the request +func (c *APIClient) prepareRequest( + ctx context.Context, + path string, method string, + postBody interface{}, + headerParams map[string]string, + queryParams url.Values, + formParams url.Values, + formFileName string, + fileName string, + fileBytes []byte) (localVarRequest *http.Request, err error) { + + var body *bytes.Buffer + + // Detect postBody type and post. + if postBody != nil { + contentType := headerParams["Content-Type"] + if contentType == "" { + contentType = detectContentType(postBody) + headerParams["Content-Type"] = contentType + } + + body, err = setBody(postBody, contentType) + if err != nil { + return nil, err + } + } + + // add form parameters and file if available. + if strings.HasPrefix(headerParams["Content-Type"], "multipart/form-data") && len(formParams) > 0 || (len(fileBytes) > 0 && fileName != "") { + if body != nil { + return nil, errors.New("Cannot specify postBody and multipart form at the same time.") + } + body = &bytes.Buffer{} + w := multipart.NewWriter(body) + + for k, v := range formParams { + for _, iv := range v { + if strings.HasPrefix(k, "@") { // file + err = addFile(w, k[1:], iv) + if err != nil { + return nil, err + } + } else { // form value + w.WriteField(k, iv) + } + } + } + if len(fileBytes) > 0 && fileName != "" { + w.Boundary() + //_, fileNm := filepath.Split(fileName) + part, err := w.CreateFormFile(formFileName, filepath.Base(fileName)) + if err != nil { + return nil, err + } + _, err = part.Write(fileBytes) + if err != nil { + return nil, err + } + } + + // Set the Boundary in the Content-Type + headerParams["Content-Type"] = w.FormDataContentType() + + // Set Content-Length + headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) + w.Close() + } + + if strings.HasPrefix(headerParams["Content-Type"], "application/x-www-form-urlencoded") && len(formParams) > 0 { + if body != nil { + return nil, errors.New("Cannot specify postBody and x-www-form-urlencoded form at the same time.") + } + body = &bytes.Buffer{} + body.WriteString(formParams.Encode()) + // Set Content-Length + headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) + } + + // Setup path and query parameters + url, err := url.Parse(path) + if err != nil { + return nil, err + } + + // Override request host, if applicable + if c.cfg.Host != "" { + url.Host = c.cfg.Host + } + + // Override request scheme, if applicable + if c.cfg.Scheme != "" { + url.Scheme = c.cfg.Scheme + } + + // Adding Query Param + query := url.Query() + for k, v := range queryParams { + for _, iv := range v { + query.Add(k, iv) + } + } + + // Encode the parameters. + url.RawQuery = query.Encode() + + // Generate a new request + if body != nil { + localVarRequest, err = http.NewRequest(method, url.String(), body) + } else { + localVarRequest, err = http.NewRequest(method, url.String(), nil) + } + if err != nil { + return nil, err + } + + // add header parameters, if any + if len(headerParams) > 0 { + headers := http.Header{} + for h, v := range headerParams { + headers.Set(h, v) + } + localVarRequest.Header = headers + } + + // Add the user agent to the request. + localVarRequest.Header.Add("User-Agent", c.cfg.UserAgent) + + if ctx != nil { + // add context to the request + localVarRequest = localVarRequest.WithContext(ctx) + + // Walk through any authentication. + + // OAuth2 authentication + if tok, ok := ctx.Value(ContextOAuth2).(oauth2.TokenSource); ok { + // We were able to grab an oauth2 token from the context + var latestToken *oauth2.Token + if latestToken, err = tok.Token(); err != nil { + return nil, err + } + + latestToken.SetAuthHeader(localVarRequest) + } + + // Basic HTTP Authentication + if auth, ok := ctx.Value(ContextBasicAuth).(BasicAuth); ok { + localVarRequest.SetBasicAuth(auth.UserName, auth.Password) + } + + // AccessToken Authentication + if auth, ok := ctx.Value(ContextAccessToken).(string); ok { + localVarRequest.Header.Add("Authorization", "Bearer "+auth) + } + } + + for header, value := range c.cfg.DefaultHeader { + localVarRequest.Header.Add(header, value) + } + return localVarRequest, nil +} + +func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err error) { + if len(b) == 0 { + return nil + } + if s, ok := v.(*string); ok { + *s = string(b) + return nil + } + if xmlCheck.MatchString(contentType) { + if err = xml.Unmarshal(b, v); err != nil { + return err + } + return nil + } + if jsonCheck.MatchString(contentType) { + if actualObj, ok := v.(interface{ GetActualInstance() interface{} }); ok { // oneOf, anyOf schemas + if unmarshalObj, ok := actualObj.(interface{ UnmarshalJSON([]byte) error }); ok { // make sure it has UnmarshalJSON defined + if err = unmarshalObj.UnmarshalJSON(b); err != nil { + return err + } + } else { + errors.New("Unknown type with GetActualInstance but no unmarshalObj.UnmarshalJSON defined") + } + } else if err = json.Unmarshal(b, v); err != nil { // simple model + return err + } + return nil + } + return errors.New("undefined response type") +} + +// Add a file to the multipart request +func addFile(w *multipart.Writer, fieldName, path string) error { + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + + part, err := w.CreateFormFile(fieldName, filepath.Base(path)) + if err != nil { + return err + } + _, err = io.Copy(part, file) + + return err +} + +// Prevent trying to import "fmt" +func reportError(format string, a ...interface{}) error { + return fmt.Errorf(format, a...) +} + +// Set request body from an interface{} +func setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err error) { + if bodyBuf == nil { + bodyBuf = &bytes.Buffer{} + } + + if reader, ok := body.(io.Reader); ok { + _, err = bodyBuf.ReadFrom(reader) + } else if b, ok := body.([]byte); ok { + _, err = bodyBuf.Write(b) + } else if s, ok := body.(string); ok { + _, err = bodyBuf.WriteString(s) + } else if s, ok := body.(*string); ok { + _, err = bodyBuf.WriteString(*s) + } else if jsonCheck.MatchString(contentType) { + err = json.NewEncoder(bodyBuf).Encode(body) + } else if xmlCheck.MatchString(contentType) { + err = xml.NewEncoder(bodyBuf).Encode(body) + } + + if err != nil { + return nil, err + } + + if bodyBuf.Len() == 0 { + err = fmt.Errorf("Invalid body type %s\n", contentType) + return nil, err + } + return bodyBuf, nil +} + +// detectContentType method is used to figure out `Request.Body` content type for request header +func detectContentType(body interface{}) string { + contentType := "text/plain; charset=utf-8" + kind := reflect.TypeOf(body).Kind() + + switch kind { + case reflect.Struct, reflect.Map, reflect.Ptr: + contentType = "application/json; charset=utf-8" + case reflect.String: + contentType = "text/plain; charset=utf-8" + default: + if b, ok := body.([]byte); ok { + contentType = http.DetectContentType(b) + } else if kind == reflect.Slice { + contentType = "application/json; charset=utf-8" + } + } + + return contentType +} + +// Ripped from https://github.com/gregjones/httpcache/blob/master/httpcache.go +type cacheControl map[string]string + +func parseCacheControl(headers http.Header) cacheControl { + cc := cacheControl{} + ccHeader := headers.Get("Cache-Control") + for _, part := range strings.Split(ccHeader, ",") { + part = strings.Trim(part, " ") + if part == "" { + continue + } + if strings.ContainsRune(part, '=') { + keyval := strings.Split(part, "=") + cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",") + } else { + cc[part] = "" + } + } + return cc +} + +// CacheExpires helper function to determine remaining time before repeating a request. +func CacheExpires(r *http.Response) time.Time { + // Figure out when the cache expires. + var expires time.Time + now, err := time.Parse(time.RFC1123, r.Header.Get("date")) + if err != nil { + return time.Now() + } + respCacheControl := parseCacheControl(r.Header) + + if maxAge, ok := respCacheControl["max-age"]; ok { + lifetime, err := time.ParseDuration(maxAge + "s") + if err != nil { + expires = now + } else { + expires = now.Add(lifetime) + } + } else { + expiresHeader := r.Header.Get("Expires") + if expiresHeader != "" { + expires, err = time.Parse(time.RFC1123, expiresHeader) + if err != nil { + expires = now + } + } + } + return expires +} + +func strlen(s string) int { + return utf8.RuneCountInString(s) +} + +// GenericOpenAPIError Provides access to the body, error and model on returned errors. +type GenericOpenAPIError struct { + body []byte + error string + model interface{} +} + +// Error returns non-empty string if there was an error. +func (e GenericOpenAPIError) Error() string { + return e.error +} + +// Body returns the raw bytes of the response +func (e GenericOpenAPIError) Body() []byte { + return e.body +} + +// Model returns the unpacked model of the error +func (e GenericOpenAPIError) Model() interface{} { + return e.model +} diff --git a/pinning/remote/client/openapi/configuration.go b/pinning/remote/client/openapi/configuration.go new file mode 100644 index 000000000..2dd763a9d --- /dev/null +++ b/pinning/remote/client/openapi/configuration.go @@ -0,0 +1,228 @@ +/* + * IPFS Pinning Service API + * + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * API version: 0.0.5 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "context" + "fmt" + "net/http" + "strings" +) + +// contextKeys are used to identify the type of value in the context. +// Since these are string, it is possible to get a short description of the +// context key for logging and debugging using key.String(). + +type contextKey string + +func (c contextKey) String() string { + return "auth " + string(c) +} + +var ( + // ContextOAuth2 takes an oauth2.TokenSource as authentication for the request. + ContextOAuth2 = contextKey("token") + + // ContextBasicAuth takes BasicAuth as authentication for the request. + ContextBasicAuth = contextKey("basic") + + // ContextAccessToken takes a string oauth2 access token as authentication for the request. + ContextAccessToken = contextKey("accesstoken") + + // ContextAPIKeys takes a string apikey as authentication for the request + ContextAPIKeys = contextKey("apiKeys") + + // ContextHttpSignatureAuth takes HttpSignatureAuth as authentication for the request. + ContextHttpSignatureAuth = contextKey("httpsignature") + + // ContextServerIndex uses a server configuration from the index. + ContextServerIndex = contextKey("serverIndex") + + // ContextOperationServerIndices uses a server configuration from the index mapping. + ContextOperationServerIndices = contextKey("serverOperationIndices") + + // ContextServerVariables overrides a server configuration variables. + ContextServerVariables = contextKey("serverVariables") + + // ContextOperationServerVariables overrides a server configuration variables using operation specific values. + ContextOperationServerVariables = contextKey("serverOperationVariables") +) + +// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth +type BasicAuth struct { + UserName string `json:"userName,omitempty"` + Password string `json:"password,omitempty"` +} + +// APIKey provides API key based authentication to a request passed via context using ContextAPIKey +type APIKey struct { + Key string + Prefix string +} + +// ServerVariable stores the information about a server variable +type ServerVariable struct { + Description string + DefaultValue string + EnumValues []string +} + +// ServerConfiguration stores the information about a server +type ServerConfiguration struct { + URL string + Description string + Variables map[string]ServerVariable +} + +// ServerConfigurations stores multiple ServerConfiguration items +type ServerConfigurations []ServerConfiguration + +// Configuration stores the configuration of the API client +type Configuration struct { + Host string `json:"host,omitempty"` + Scheme string `json:"scheme,omitempty"` + DefaultHeader map[string]string `json:"defaultHeader,omitempty"` + UserAgent string `json:"userAgent,omitempty"` + Debug bool `json:"debug,omitempty"` + Servers ServerConfigurations + OperationServers map[string]ServerConfigurations + HTTPClient *http.Client +} + +// NewConfiguration returns a new Configuration object +func NewConfiguration() *Configuration { + cfg := &Configuration{ + DefaultHeader: make(map[string]string), + UserAgent: "OpenAPI-Generator/1.0.0/go", + Debug: false, + Servers: ServerConfigurations{ + { + URL: "https://pinning-service.example.com", + Description: "No description provided", + }, + }, + OperationServers: map[string]ServerConfigurations{}, + } + return cfg +} + +// AddDefaultHeader adds a new HTTP header to the default header in the request +func (c *Configuration) AddDefaultHeader(key string, value string) { + c.DefaultHeader[key] = value +} + +// URL formats template on a index using given variables +func (sc ServerConfigurations) URL(index int, variables map[string]string) (string, error) { + if index < 0 || len(sc) <= index { + return "", fmt.Errorf("Index %v out of range %v", index, len(sc)-1) + } + server := sc[index] + url := server.URL + + // go through variables and replace placeholders + for name, variable := range server.Variables { + if value, ok := variables[name]; ok { + found := bool(len(variable.EnumValues) == 0) + for _, enumValue := range variable.EnumValues { + if value == enumValue { + found = true + } + } + if !found { + return "", fmt.Errorf("The variable %s in the server URL has invalid value %v. Must be %v", name, value, variable.EnumValues) + } + url = strings.Replace(url, "{"+name+"}", value, -1) + } else { + url = strings.Replace(url, "{"+name+"}", variable.DefaultValue, -1) + } + } + return url, nil +} + +// ServerURL returns URL based on server settings +func (c *Configuration) ServerURL(index int, variables map[string]string) (string, error) { + return c.Servers.URL(index, variables) +} + +func getServerIndex(ctx context.Context) (int, error) { + si := ctx.Value(ContextServerIndex) + if si != nil { + if index, ok := si.(int); ok { + return index, nil + } + return 0, reportError("Invalid type %T should be int", si) + } + return 0, nil +} + +func getServerOperationIndex(ctx context.Context, endpoint string) (int, error) { + osi := ctx.Value(ContextOperationServerIndices) + if osi != nil { + if operationIndices, ok := osi.(map[string]int); !ok { + return 0, reportError("Invalid type %T should be map[string]int", osi) + } else { + index, ok := operationIndices[endpoint] + if ok { + return index, nil + } + } + } + return getServerIndex(ctx) +} + +func getServerVariables(ctx context.Context) (map[string]string, error) { + sv := ctx.Value(ContextServerVariables) + if sv != nil { + if variables, ok := sv.(map[string]string); ok { + return variables, nil + } + return nil, reportError("ctx value of ContextServerVariables has invalid type %T should be map[string]string", sv) + } + return nil, nil +} + +func getServerOperationVariables(ctx context.Context, endpoint string) (map[string]string, error) { + osv := ctx.Value(ContextOperationServerVariables) + if osv != nil { + if operationVariables, ok := osv.(map[string]map[string]string); !ok { + return nil, reportError("ctx value of ContextOperationServerVariables has invalid type %T should be map[string]map[string]string", osv) + } else { + variables, ok := operationVariables[endpoint] + if ok { + return variables, nil + } + } + } + return getServerVariables(ctx) +} + +// ServerURLWithContext returns a new server URL given an endpoint +func (c *Configuration) ServerURLWithContext(ctx context.Context, endpoint string) (string, error) { + sc, ok := c.OperationServers[endpoint] + if !ok { + sc = c.Servers + } + + if ctx == nil { + return sc.URL(0, nil) + } + + index, err := getServerOperationIndex(ctx, endpoint) + if err != nil { + return "", err + } + + variables, err := getServerOperationVariables(ctx, endpoint) + if err != nil { + return "", err + } + + return sc.URL(index, variables) +} diff --git a/pinning/remote/client/openapi/docs/Error.md b/pinning/remote/client/openapi/docs/Error.md new file mode 100644 index 000000000..e88553965 --- /dev/null +++ b/pinning/remote/client/openapi/docs/Error.md @@ -0,0 +1,72 @@ +# Error + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Code** | **int32** | | +**Message** | **string** | | + +## Methods + +### NewError + +`func NewError(code int32, message string, ) *Error` + +NewError instantiates a new Error object +This constructor will assign default values to properties that have it defined, +and makes sure properties required by API are set, but the set of arguments +will change when the set of required properties is changed + +### NewErrorWithDefaults + +`func NewErrorWithDefaults() *Error` + +NewErrorWithDefaults instantiates a new Error object +This constructor will only assign default values to properties that have it defined, +but it doesn't guarantee that properties required by API are set + +### GetCode + +`func (o *Error) GetCode() int32` + +GetCode returns the Code field if non-nil, zero value otherwise. + +### GetCodeOk + +`func (o *Error) GetCodeOk() (*int32, bool)` + +GetCodeOk returns a tuple with the Code field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetCode + +`func (o *Error) SetCode(v int32)` + +SetCode sets Code field to given value. + + +### GetMessage + +`func (o *Error) GetMessage() string` + +GetMessage returns the Message field if non-nil, zero value otherwise. + +### GetMessageOk + +`func (o *Error) GetMessageOk() (*string, bool)` + +GetMessageOk returns a tuple with the Message field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetMessage + +`func (o *Error) SetMessage(v string)` + +SetMessage sets Message field to given value. + + + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/pinning/remote/client/openapi/docs/Pin.md b/pinning/remote/client/openapi/docs/Pin.md new file mode 100644 index 000000000..cbdf1a0b7 --- /dev/null +++ b/pinning/remote/client/openapi/docs/Pin.md @@ -0,0 +1,129 @@ +# Pin + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Cid** | **string** | CID to be pinned recursively | +**Name** | Pointer to **string** | Optional name for pinned data; can be used for lookups later | [optional] +**Origins** | Pointer to **[]string** | Optional list of multiaddrs known to provide the data | [optional] +**Meta** | Pointer to **map[string]string** | Optional metadata for pin object | [optional] + +## Methods + +### NewPin + +`func NewPin(cid string, ) *Pin` + +NewPin instantiates a new Pin object +This constructor will assign default values to properties that have it defined, +and makes sure properties required by API are set, but the set of arguments +will change when the set of required properties is changed + +### NewPinWithDefaults + +`func NewPinWithDefaults() *Pin` + +NewPinWithDefaults instantiates a new Pin object +This constructor will only assign default values to properties that have it defined, +but it doesn't guarantee that properties required by API are set + +### GetCid + +`func (o *Pin) GetCid() string` + +GetCid returns the Cid field if non-nil, zero value otherwise. + +### GetCidOk + +`func (o *Pin) GetCidOk() (*string, bool)` + +GetCidOk returns a tuple with the Cid field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetCid + +`func (o *Pin) SetCid(v string)` + +SetCid sets Cid field to given value. + + +### GetName + +`func (o *Pin) GetName() string` + +GetName returns the Name field if non-nil, zero value otherwise. + +### GetNameOk + +`func (o *Pin) GetNameOk() (*string, bool)` + +GetNameOk returns a tuple with the Name field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetName + +`func (o *Pin) SetName(v string)` + +SetName sets Name field to given value. + +### HasName + +`func (o *Pin) HasName() bool` + +HasName returns a boolean if a field has been set. + +### GetOrigins + +`func (o *Pin) GetOrigins() []string` + +GetOrigins returns the Origins field if non-nil, zero value otherwise. + +### GetOriginsOk + +`func (o *Pin) GetOriginsOk() (*[]string, bool)` + +GetOriginsOk returns a tuple with the Origins field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetOrigins + +`func (o *Pin) SetOrigins(v []string)` + +SetOrigins sets Origins field to given value. + +### HasOrigins + +`func (o *Pin) HasOrigins() bool` + +HasOrigins returns a boolean if a field has been set. + +### GetMeta + +`func (o *Pin) GetMeta() map[string]string` + +GetMeta returns the Meta field if non-nil, zero value otherwise. + +### GetMetaOk + +`func (o *Pin) GetMetaOk() (*map[string]string, bool)` + +GetMetaOk returns a tuple with the Meta field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetMeta + +`func (o *Pin) SetMeta(v map[string]string)` + +SetMeta sets Meta field to given value. + +### HasMeta + +`func (o *Pin) HasMeta() bool` + +HasMeta returns a boolean if a field has been set. + + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/pinning/remote/client/openapi/docs/PinResults.md b/pinning/remote/client/openapi/docs/PinResults.md new file mode 100644 index 000000000..1982bfddb --- /dev/null +++ b/pinning/remote/client/openapi/docs/PinResults.md @@ -0,0 +1,72 @@ +# PinResults + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Count** | **int32** | The total number of pin objects that exist for passed query filters | +**Results** | [**[]PinStatus**](PinStatus.md) | An array of PinStatus results | + +## Methods + +### NewPinResults + +`func NewPinResults(count int32, results []PinStatus, ) *PinResults` + +NewPinResults instantiates a new PinResults object +This constructor will assign default values to properties that have it defined, +and makes sure properties required by API are set, but the set of arguments +will change when the set of required properties is changed + +### NewPinResultsWithDefaults + +`func NewPinResultsWithDefaults() *PinResults` + +NewPinResultsWithDefaults instantiates a new PinResults object +This constructor will only assign default values to properties that have it defined, +but it doesn't guarantee that properties required by API are set + +### GetCount + +`func (o *PinResults) GetCount() int32` + +GetCount returns the Count field if non-nil, zero value otherwise. + +### GetCountOk + +`func (o *PinResults) GetCountOk() (*int32, bool)` + +GetCountOk returns a tuple with the Count field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetCount + +`func (o *PinResults) SetCount(v int32)` + +SetCount sets Count field to given value. + + +### GetResults + +`func (o *PinResults) GetResults() []PinStatus` + +GetResults returns the Results field if non-nil, zero value otherwise. + +### GetResultsOk + +`func (o *PinResults) GetResultsOk() (*[]PinStatus, bool)` + +GetResultsOk returns a tuple with the Results field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetResults + +`func (o *PinResults) SetResults(v []PinStatus)` + +SetResults sets Results field to given value. + + + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/pinning/remote/client/openapi/docs/PinStatus.md b/pinning/remote/client/openapi/docs/PinStatus.md new file mode 100644 index 000000000..2408abfda --- /dev/null +++ b/pinning/remote/client/openapi/docs/PinStatus.md @@ -0,0 +1,161 @@ +# PinStatus + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Id** | **string** | Globally unique ID of the pin request; can be used to check the status of ongoing pinning, modification of pin object, or pin removal | +**Status** | [**Status**](Status.md) | | +**Created** | [**time.Time**](time.Time.md) | Immutable timestamp indicating when a pin request entered a pinning service; can be used for filtering results and pagination | +**Pin** | [**Pin**](Pin.md) | | +**Delegates** | **[]string** | List of multiaddrs designated by pinning service for transferring any new data from external peers | +**Info** | Pointer to **map[string]string** | Optional info for PinStatus response | [optional] + +## Methods + +### NewPinStatus + +`func NewPinStatus(id string, status Status, created time.Time, pin Pin, delegates []string, ) *PinStatus` + +NewPinStatus instantiates a new PinStatus object +This constructor will assign default values to properties that have it defined, +and makes sure properties required by API are set, but the set of arguments +will change when the set of required properties is changed + +### NewPinStatusWithDefaults + +`func NewPinStatusWithDefaults() *PinStatus` + +NewPinStatusWithDefaults instantiates a new PinStatus object +This constructor will only assign default values to properties that have it defined, +but it doesn't guarantee that properties required by API are set + +### GetId + +`func (o *PinStatus) GetId() string` + +GetId returns the Id field if non-nil, zero value otherwise. + +### GetIdOk + +`func (o *PinStatus) GetIdOk() (*string, bool)` + +GetIdOk returns a tuple with the Id field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetId + +`func (o *PinStatus) SetId(v string)` + +SetId sets Id field to given value. + + +### GetStatus + +`func (o *PinStatus) GetStatus() Status` + +GetStatus returns the Status field if non-nil, zero value otherwise. + +### GetStatusOk + +`func (o *PinStatus) GetStatusOk() (*Status, bool)` + +GetStatusOk returns a tuple with the Status field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetStatus + +`func (o *PinStatus) SetStatus(v Status)` + +SetStatus sets Status field to given value. + + +### GetCreated + +`func (o *PinStatus) GetCreated() time.Time` + +GetCreated returns the Created field if non-nil, zero value otherwise. + +### GetCreatedOk + +`func (o *PinStatus) GetCreatedOk() (*time.Time, bool)` + +GetCreatedOk returns a tuple with the Created field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetCreated + +`func (o *PinStatus) SetCreated(v time.Time)` + +SetCreated sets Created field to given value. + + +### GetPin + +`func (o *PinStatus) GetPin() Pin` + +GetPin returns the Pin field if non-nil, zero value otherwise. + +### GetPinOk + +`func (o *PinStatus) GetPinOk() (*Pin, bool)` + +GetPinOk returns a tuple with the Pin field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetPin + +`func (o *PinStatus) SetPin(v Pin)` + +SetPin sets Pin field to given value. + + +### GetDelegates + +`func (o *PinStatus) GetDelegates() []string` + +GetDelegates returns the Delegates field if non-nil, zero value otherwise. + +### GetDelegatesOk + +`func (o *PinStatus) GetDelegatesOk() (*[]string, bool)` + +GetDelegatesOk returns a tuple with the Delegates field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetDelegates + +`func (o *PinStatus) SetDelegates(v []string)` + +SetDelegates sets Delegates field to given value. + + +### GetInfo + +`func (o *PinStatus) GetInfo() map[string]string` + +GetInfo returns the Info field if non-nil, zero value otherwise. + +### GetInfoOk + +`func (o *PinStatus) GetInfoOk() (*map[string]string, bool)` + +GetInfoOk returns a tuple with the Info field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetInfo + +`func (o *PinStatus) SetInfo(v map[string]string)` + +SetInfo sets Info field to given value. + +### HasInfo + +`func (o *PinStatus) HasInfo() bool` + +HasInfo returns a boolean if a field has been set. + + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/pinning/remote/client/openapi/docs/PinsApi.md b/pinning/remote/client/openapi/docs/PinsApi.md new file mode 100644 index 000000000..0aea62d99 --- /dev/null +++ b/pinning/remote/client/openapi/docs/PinsApi.md @@ -0,0 +1,367 @@ +# \PinsApi + +All URIs are relative to *https://pinning-service.example.com* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**PinsGet**](PinsApi.md#PinsGet) | **Get** /pins | List pin objects +[**PinsIdDelete**](PinsApi.md#PinsIdDelete) | **Delete** /pins/{id} | Remove pin object +[**PinsIdGet**](PinsApi.md#PinsIdGet) | **Get** /pins/{id} | Get pin object +[**PinsIdPost**](PinsApi.md#PinsIdPost) | **Post** /pins/{id} | Modify pin object +[**PinsPost**](PinsApi.md#PinsPost) | **Post** /pins | Add pin object + + + +## PinsGet + +> PinResults PinsGet(ctx).Cid(cid).Name(name).Status(status).Before(before).After(after).Limit(limit).Meta(meta).Execute() + +List pin objects + + + +### Example + +```go +package main + +import ( + "context" + "fmt" + "os" + openapiclient "./openapi" +) + +func main() { + cid := []string{"Inner_example"} // []string | Return pin objects responsible for pinning the specified CID(s) (optional) + name := "name_example" // string | Return pin objects with names that contain provided value (partial or full match) (optional) + status := []Status{openapiclient.Status{}} // []Status | Return pin objects for pins with the specified status (optional) + before := Get-Date // time.Time | Return results created (queued) before provided timestamp (optional) + after := Get-Date // time.Time | Return results created (queued) after provided timestamp (optional) + limit := 987 // int32 | Max records to return (optional) (default to 10) + meta := map[string]string{ "Key" = "Value" } // map[string]string | Return pin objects that match specified metadata (optional) + + configuration := openapiclient.NewConfiguration() + api_client := openapiclient.NewAPIClient(configuration) + resp, r, err := api_client.PinsApi.PinsGet(context.Background(), ).Cid(cid).Name(name).Status(status).Before(before).After(after).Limit(limit).Meta(meta).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `PinsApi.PinsGet``: %v\n", err) + fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) + } + // response from `PinsGet`: PinResults + fmt.Fprintf(os.Stdout, "Response from `PinsApi.PinsGet`: %v\n", resp) +} +``` + +### Path Parameters + + + +### Other Parameters + +Other parameters are passed through a pointer to a apiPinsGetRequest struct via the builder pattern + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **cid** | [**[]string**](string.md) | Return pin objects responsible for pinning the specified CID(s) | + **name** | **string** | Return pin objects with names that contain provided value (partial or full match) | + **status** | [**[]Status**](Status.md) | Return pin objects for pins with the specified status | + **before** | **time.Time** | Return results created (queued) before provided timestamp | + **after** | **time.Time** | Return results created (queued) after provided timestamp | + **limit** | **int32** | Max records to return | [default to 10] + **meta** | [**map[string]string**](string.md) | Return pin objects that match specified metadata | + +### Return type + +[**PinResults**](PinResults.md) + +### Authorization + +[accessToken](../README.md#accessToken) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) +[[Back to Model list]](../README.md#documentation-for-models) +[[Back to README]](../README.md) + + +## PinsIdDelete + +> PinsIdDelete(ctx, id).Execute() + +Remove pin object + + + +### Example + +```go +package main + +import ( + "context" + "fmt" + "os" + openapiclient "./openapi" +) + +func main() { + id := "id_example" // string | + + configuration := openapiclient.NewConfiguration() + api_client := openapiclient.NewAPIClient(configuration) + resp, r, err := api_client.PinsApi.PinsIdDelete(context.Background(), id).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `PinsApi.PinsIdDelete``: %v\n", err) + fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) + } +} +``` + +### Path Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- +**ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. +**id** | **string** | | + +### Other Parameters + +Other parameters are passed through a pointer to a apiPinsIdDeleteRequest struct via the builder pattern + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + + +### Return type + + (empty response body) + +### Authorization + +[accessToken](../README.md#accessToken) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) +[[Back to Model list]](../README.md#documentation-for-models) +[[Back to README]](../README.md) + + +## PinsIdGet + +> PinStatus PinsIdGet(ctx, id).Execute() + +Get pin object + + + +### Example + +```go +package main + +import ( + "context" + "fmt" + "os" + openapiclient "./openapi" +) + +func main() { + id := "id_example" // string | + + configuration := openapiclient.NewConfiguration() + api_client := openapiclient.NewAPIClient(configuration) + resp, r, err := api_client.PinsApi.PinsIdGet(context.Background(), id).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `PinsApi.PinsIdGet``: %v\n", err) + fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) + } + // response from `PinsIdGet`: PinStatus + fmt.Fprintf(os.Stdout, "Response from `PinsApi.PinsIdGet`: %v\n", resp) +} +``` + +### Path Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- +**ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. +**id** | **string** | | + +### Other Parameters + +Other parameters are passed through a pointer to a apiPinsIdGetRequest struct via the builder pattern + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + + +### Return type + +[**PinStatus**](PinStatus.md) + +### Authorization + +[accessToken](../README.md#accessToken) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) +[[Back to Model list]](../README.md#documentation-for-models) +[[Back to README]](../README.md) + + +## PinsIdPost + +> PinStatus PinsIdPost(ctx, id).Pin(pin).Execute() + +Modify pin object + + + +### Example + +```go +package main + +import ( + "context" + "fmt" + "os" + openapiclient "./openapi" +) + +func main() { + id := "id_example" // string | + pin := openapiclient.Pin{Cid: "Cid_example", Name: "Name_example", Origins: []string{"Origins_example"), Meta: map[string]string{ "Key" = "Value" }} // Pin | + + configuration := openapiclient.NewConfiguration() + api_client := openapiclient.NewAPIClient(configuration) + resp, r, err := api_client.PinsApi.PinsIdPost(context.Background(), id, pin).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `PinsApi.PinsIdPost``: %v\n", err) + fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) + } + // response from `PinsIdPost`: PinStatus + fmt.Fprintf(os.Stdout, "Response from `PinsApi.PinsIdPost`: %v\n", resp) +} +``` + +### Path Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- +**ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. +**id** | **string** | | + +### Other Parameters + +Other parameters are passed through a pointer to a apiPinsIdPostRequest struct via the builder pattern + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + + **pin** | [**Pin**](Pin.md) | | + +### Return type + +[**PinStatus**](PinStatus.md) + +### Authorization + +[accessToken](../README.md#accessToken) + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) +[[Back to Model list]](../README.md#documentation-for-models) +[[Back to README]](../README.md) + + +## PinsPost + +> PinStatus PinsPost(ctx).Pin(pin).Execute() + +Add pin object + + + +### Example + +```go +package main + +import ( + "context" + "fmt" + "os" + openapiclient "./openapi" +) + +func main() { + pin := openapiclient.Pin{Cid: "Cid_example", Name: "Name_example", Origins: []string{"Origins_example"), Meta: map[string]string{ "Key" = "Value" }} // Pin | + + configuration := openapiclient.NewConfiguration() + api_client := openapiclient.NewAPIClient(configuration) + resp, r, err := api_client.PinsApi.PinsPost(context.Background(), pin).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `PinsApi.PinsPost``: %v\n", err) + fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) + } + // response from `PinsPost`: PinStatus + fmt.Fprintf(os.Stdout, "Response from `PinsApi.PinsPost`: %v\n", resp) +} +``` + +### Path Parameters + + + +### Other Parameters + +Other parameters are passed through a pointer to a apiPinsPostRequest struct via the builder pattern + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **pin** | [**Pin**](Pin.md) | | + +### Return type + +[**PinStatus**](PinStatus.md) + +### Authorization + +[accessToken](../README.md#accessToken) + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) +[[Back to Model list]](../README.md#documentation-for-models) +[[Back to README]](../README.md) + diff --git a/pinning/remote/client/openapi/docs/Status.md b/pinning/remote/client/openapi/docs/Status.md new file mode 100644 index 000000000..01176af11 --- /dev/null +++ b/pinning/remote/client/openapi/docs/Status.md @@ -0,0 +1,11 @@ +# Status + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/pinning/remote/client/openapi/model_error.go b/pinning/remote/client/openapi/model_error.go new file mode 100644 index 000000000..d97ecaa2c --- /dev/null +++ b/pinning/remote/client/openapi/model_error.go @@ -0,0 +1,134 @@ +/* + * IPFS Pinning Service API + * + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * API version: 0.0.5 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "encoding/json" +) + +// Error Base error object +type Error struct { + Code int32 `json:"code"` + Message string `json:"message"` +} + +// NewError instantiates a new Error object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewError(code int32, message string) *Error { + this := Error{} + this.Code = code + this.Message = message + return &this +} + +// NewErrorWithDefaults instantiates a new Error object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewErrorWithDefaults() *Error { + this := Error{} + return &this +} + +// GetCode returns the Code field value +func (o *Error) GetCode() int32 { + if o == nil { + var ret int32 + return ret + } + + return o.Code +} + +// GetCodeOk returns a tuple with the Code field value +// and a boolean to check if the value has been set. +func (o *Error) GetCodeOk() (*int32, bool) { + if o == nil { + return nil, false + } + return &o.Code, true +} + +// SetCode sets field value +func (o *Error) SetCode(v int32) { + o.Code = v +} + +// GetMessage returns the Message field value +func (o *Error) GetMessage() string { + if o == nil { + var ret string + return ret + } + + return o.Message +} + +// GetMessageOk returns a tuple with the Message field value +// and a boolean to check if the value has been set. +func (o *Error) GetMessageOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Message, true +} + +// SetMessage sets field value +func (o *Error) SetMessage(v string) { + o.Message = v +} + +func (o Error) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["code"] = o.Code + } + if true { + toSerialize["message"] = o.Message + } + return json.Marshal(toSerialize) +} + +type NullableError struct { + value *Error + isSet bool +} + +func (v NullableError) Get() *Error { + return v.value +} + +func (v *NullableError) Set(val *Error) { + v.value = val + v.isSet = true +} + +func (v NullableError) IsSet() bool { + return v.isSet +} + +func (v *NullableError) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableError(val *Error) *NullableError { + return &NullableError{value: val, isSet: true} +} + +func (v NullableError) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableError) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/pinning/remote/client/openapi/model_pin.go b/pinning/remote/client/openapi/model_pin.go new file mode 100644 index 000000000..c4a5e8015 --- /dev/null +++ b/pinning/remote/client/openapi/model_pin.go @@ -0,0 +1,217 @@ +/* + * IPFS Pinning Service API + * + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * API version: 0.0.5 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "encoding/json" +) + +// Pin Pin object +type Pin struct { + // CID to be pinned recursively + Cid string `json:"cid"` + // Optional name for pinned data; can be used for lookups later + Name *string `json:"name,omitempty"` + // Optional list of multiaddrs known to provide the data + Origins *[]string `json:"origins,omitempty"` + // Optional metadata for pin object + Meta *map[string]string `json:"meta,omitempty"` +} + +// NewPin instantiates a new Pin object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewPin(cid string) *Pin { + this := Pin{} + this.Cid = cid + return &this +} + +// NewPinWithDefaults instantiates a new Pin object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewPinWithDefaults() *Pin { + this := Pin{} + return &this +} + +// GetCid returns the Cid field value +func (o *Pin) GetCid() string { + if o == nil { + var ret string + return ret + } + + return o.Cid +} + +// GetCidOk returns a tuple with the Cid field value +// and a boolean to check if the value has been set. +func (o *Pin) GetCidOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Cid, true +} + +// SetCid sets field value +func (o *Pin) SetCid(v string) { + o.Cid = v +} + +// GetName returns the Name field value if set, zero value otherwise. +func (o *Pin) GetName() string { + if o == nil || o.Name == nil { + var ret string + return ret + } + return *o.Name +} + +// GetNameOk returns a tuple with the Name field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Pin) GetNameOk() (*string, bool) { + if o == nil || o.Name == nil { + return nil, false + } + return o.Name, true +} + +// HasName returns a boolean if a field has been set. +func (o *Pin) HasName() bool { + if o != nil && o.Name != nil { + return true + } + + return false +} + +// SetName gets a reference to the given string and assigns it to the Name field. +func (o *Pin) SetName(v string) { + o.Name = &v +} + +// GetOrigins returns the Origins field value if set, zero value otherwise. +func (o *Pin) GetOrigins() []string { + if o == nil || o.Origins == nil { + var ret []string + return ret + } + return *o.Origins +} + +// GetOriginsOk returns a tuple with the Origins field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Pin) GetOriginsOk() (*[]string, bool) { + if o == nil || o.Origins == nil { + return nil, false + } + return o.Origins, true +} + +// HasOrigins returns a boolean if a field has been set. +func (o *Pin) HasOrigins() bool { + if o != nil && o.Origins != nil { + return true + } + + return false +} + +// SetOrigins gets a reference to the given []string and assigns it to the Origins field. +func (o *Pin) SetOrigins(v []string) { + o.Origins = &v +} + +// GetMeta returns the Meta field value if set, zero value otherwise. +func (o *Pin) GetMeta() map[string]string { + if o == nil || o.Meta == nil { + var ret map[string]string + return ret + } + return *o.Meta +} + +// GetMetaOk returns a tuple with the Meta field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Pin) GetMetaOk() (*map[string]string, bool) { + if o == nil || o.Meta == nil { + return nil, false + } + return o.Meta, true +} + +// HasMeta returns a boolean if a field has been set. +func (o *Pin) HasMeta() bool { + if o != nil && o.Meta != nil { + return true + } + + return false +} + +// SetMeta gets a reference to the given map[string]string and assigns it to the Meta field. +func (o *Pin) SetMeta(v map[string]string) { + o.Meta = &v +} + +func (o Pin) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["cid"] = o.Cid + } + if o.Name != nil { + toSerialize["name"] = o.Name + } + if o.Origins != nil { + toSerialize["origins"] = o.Origins + } + if o.Meta != nil { + toSerialize["meta"] = o.Meta + } + return json.Marshal(toSerialize) +} + +type NullablePin struct { + value *Pin + isSet bool +} + +func (v NullablePin) Get() *Pin { + return v.value +} + +func (v *NullablePin) Set(val *Pin) { + v.value = val + v.isSet = true +} + +func (v NullablePin) IsSet() bool { + return v.isSet +} + +func (v *NullablePin) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullablePin(val *Pin) *NullablePin { + return &NullablePin{value: val, isSet: true} +} + +func (v NullablePin) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullablePin) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/pinning/remote/client/openapi/model_pin_results.go b/pinning/remote/client/openapi/model_pin_results.go new file mode 100644 index 000000000..ac8443976 --- /dev/null +++ b/pinning/remote/client/openapi/model_pin_results.go @@ -0,0 +1,136 @@ +/* + * IPFS Pinning Service API + * + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * API version: 0.0.5 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "encoding/json" +) + +// PinResults Response used for listing pin objects matching request +type PinResults struct { + // The total number of pin objects that exist for passed query filters + Count int32 `json:"count"` + // An array of PinStatus results + Results []PinStatus `json:"results"` +} + +// NewPinResults instantiates a new PinResults object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewPinResults(count int32, results []PinStatus) *PinResults { + this := PinResults{} + this.Count = count + this.Results = results + return &this +} + +// NewPinResultsWithDefaults instantiates a new PinResults object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewPinResultsWithDefaults() *PinResults { + this := PinResults{} + return &this +} + +// GetCount returns the Count field value +func (o *PinResults) GetCount() int32 { + if o == nil { + var ret int32 + return ret + } + + return o.Count +} + +// GetCountOk returns a tuple with the Count field value +// and a boolean to check if the value has been set. +func (o *PinResults) GetCountOk() (*int32, bool) { + if o == nil { + return nil, false + } + return &o.Count, true +} + +// SetCount sets field value +func (o *PinResults) SetCount(v int32) { + o.Count = v +} + +// GetResults returns the Results field value +func (o *PinResults) GetResults() []PinStatus { + if o == nil { + var ret []PinStatus + return ret + } + + return o.Results +} + +// GetResultsOk returns a tuple with the Results field value +// and a boolean to check if the value has been set. +func (o *PinResults) GetResultsOk() (*[]PinStatus, bool) { + if o == nil { + return nil, false + } + return &o.Results, true +} + +// SetResults sets field value +func (o *PinResults) SetResults(v []PinStatus) { + o.Results = v +} + +func (o PinResults) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["count"] = o.Count + } + if true { + toSerialize["results"] = o.Results + } + return json.Marshal(toSerialize) +} + +type NullablePinResults struct { + value *PinResults + isSet bool +} + +func (v NullablePinResults) Get() *PinResults { + return v.value +} + +func (v *NullablePinResults) Set(val *PinResults) { + v.value = val + v.isSet = true +} + +func (v NullablePinResults) IsSet() bool { + return v.isSet +} + +func (v *NullablePinResults) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullablePinResults(val *PinResults) *NullablePinResults { + return &NullablePinResults{value: val, isSet: true} +} + +func (v NullablePinResults) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullablePinResults) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/pinning/remote/client/openapi/model_pin_status.go b/pinning/remote/client/openapi/model_pin_status.go new file mode 100644 index 000000000..78f37dcd2 --- /dev/null +++ b/pinning/remote/client/openapi/model_pin_status.go @@ -0,0 +1,262 @@ +/* + * IPFS Pinning Service API + * + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * API version: 0.0.5 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "encoding/json" + "time" +) + +// PinStatus Pin object with status +type PinStatus struct { + // Globally unique ID of the pin request; can be used to check the status of ongoing pinning, modification of pin object, or pin removal + Id string `json:"id"` + Status Status `json:"status"` + // Immutable timestamp indicating when a pin request entered a pinning service; can be used for filtering results and pagination + Created time.Time `json:"created"` + Pin Pin `json:"pin"` + // List of multiaddrs designated by pinning service for transferring any new data from external peers + Delegates []string `json:"delegates"` + // Optional info for PinStatus response + Info *map[string]string `json:"info,omitempty"` +} + +// NewPinStatus instantiates a new PinStatus object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewPinStatus(id string, status Status, created time.Time, pin Pin, delegates []string) *PinStatus { + this := PinStatus{} + this.Id = id + this.Status = status + this.Created = created + this.Pin = pin + this.Delegates = delegates + return &this +} + +// NewPinStatusWithDefaults instantiates a new PinStatus object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewPinStatusWithDefaults() *PinStatus { + this := PinStatus{} + return &this +} + +// GetId returns the Id field value +func (o *PinStatus) GetId() string { + if o == nil { + var ret string + return ret + } + + return o.Id +} + +// GetIdOk returns a tuple with the Id field value +// and a boolean to check if the value has been set. +func (o *PinStatus) GetIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Id, true +} + +// SetId sets field value +func (o *PinStatus) SetId(v string) { + o.Id = v +} + +// GetStatus returns the Status field value +func (o *PinStatus) GetStatus() Status { + if o == nil { + var ret Status + return ret + } + + return o.Status +} + +// GetStatusOk returns a tuple with the Status field value +// and a boolean to check if the value has been set. +func (o *PinStatus) GetStatusOk() (*Status, bool) { + if o == nil { + return nil, false + } + return &o.Status, true +} + +// SetStatus sets field value +func (o *PinStatus) SetStatus(v Status) { + o.Status = v +} + +// GetCreated returns the Created field value +func (o *PinStatus) GetCreated() time.Time { + if o == nil { + var ret time.Time + return ret + } + + return o.Created +} + +// GetCreatedOk returns a tuple with the Created field value +// and a boolean to check if the value has been set. +func (o *PinStatus) GetCreatedOk() (*time.Time, bool) { + if o == nil { + return nil, false + } + return &o.Created, true +} + +// SetCreated sets field value +func (o *PinStatus) SetCreated(v time.Time) { + o.Created = v +} + +// GetPin returns the Pin field value +func (o *PinStatus) GetPin() Pin { + if o == nil { + var ret Pin + return ret + } + + return o.Pin +} + +// GetPinOk returns a tuple with the Pin field value +// and a boolean to check if the value has been set. +func (o *PinStatus) GetPinOk() (*Pin, bool) { + if o == nil { + return nil, false + } + return &o.Pin, true +} + +// SetPin sets field value +func (o *PinStatus) SetPin(v Pin) { + o.Pin = v +} + +// GetDelegates returns the Delegates field value +func (o *PinStatus) GetDelegates() []string { + if o == nil { + var ret []string + return ret + } + + return o.Delegates +} + +// GetDelegatesOk returns a tuple with the Delegates field value +// and a boolean to check if the value has been set. +func (o *PinStatus) GetDelegatesOk() (*[]string, bool) { + if o == nil { + return nil, false + } + return &o.Delegates, true +} + +// SetDelegates sets field value +func (o *PinStatus) SetDelegates(v []string) { + o.Delegates = v +} + +// GetInfo returns the Info field value if set, zero value otherwise. +func (o *PinStatus) GetInfo() map[string]string { + if o == nil || o.Info == nil { + var ret map[string]string + return ret + } + return *o.Info +} + +// GetInfoOk returns a tuple with the Info field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *PinStatus) GetInfoOk() (*map[string]string, bool) { + if o == nil || o.Info == nil { + return nil, false + } + return o.Info, true +} + +// HasInfo returns a boolean if a field has been set. +func (o *PinStatus) HasInfo() bool { + if o != nil && o.Info != nil { + return true + } + + return false +} + +// SetInfo gets a reference to the given map[string]string and assigns it to the Info field. +func (o *PinStatus) SetInfo(v map[string]string) { + o.Info = &v +} + +func (o PinStatus) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["id"] = o.Id + } + if true { + toSerialize["status"] = o.Status + } + if true { + toSerialize["created"] = o.Created + } + if true { + toSerialize["pin"] = o.Pin + } + if true { + toSerialize["delegates"] = o.Delegates + } + if o.Info != nil { + toSerialize["info"] = o.Info + } + return json.Marshal(toSerialize) +} + +type NullablePinStatus struct { + value *PinStatus + isSet bool +} + +func (v NullablePinStatus) Get() *PinStatus { + return v.value +} + +func (v *NullablePinStatus) Set(val *PinStatus) { + v.value = val + v.isSet = true +} + +func (v NullablePinStatus) IsSet() bool { + return v.isSet +} + +func (v *NullablePinStatus) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullablePinStatus(val *PinStatus) *NullablePinStatus { + return &NullablePinStatus{value: val, isSet: true} +} + +func (v NullablePinStatus) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullablePinStatus) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/pinning/remote/client/openapi/model_status.go b/pinning/remote/client/openapi/model_status.go new file mode 100644 index 000000000..fe727407e --- /dev/null +++ b/pinning/remote/client/openapi/model_status.go @@ -0,0 +1,84 @@ +/* + * IPFS Pinning Service API + * + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * API version: 0.0.5 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "encoding/json" + "fmt" +) + +// Status Status a pin object can have at a pinning service +type Status string + +// List of Status +const ( + QUEUED Status = "queued" + PINNING Status = "pinning" + PINNED Status = "pinned" + FAILED Status = "failed" +) + +func (v *Status) UnmarshalJSON(src []byte) error { + var value string + err := json.Unmarshal(src, &value) + if err != nil { + return err + } + enumTypeValue := Status(value) + for _, existing := range []Status{"queued", "pinning", "pinned", "failed"} { + if existing == enumTypeValue { + *v = enumTypeValue + return nil + } + } + + return fmt.Errorf("%+v is not a valid Status", value) +} + +// Ptr returns reference to Status value +func (v Status) Ptr() *Status { + return &v +} + +type NullableStatus struct { + value *Status + isSet bool +} + +func (v NullableStatus) Get() *Status { + return v.value +} + +func (v *NullableStatus) Set(val *Status) { + v.value = val + v.isSet = true +} + +func (v NullableStatus) IsSet() bool { + return v.isSet +} + +func (v *NullableStatus) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableStatus(val *Status) *NullableStatus { + return &NullableStatus{value: val, isSet: true} +} + +func (v NullableStatus) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableStatus) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/pinning/remote/client/openapi/response.go b/pinning/remote/client/openapi/response.go new file mode 100644 index 000000000..d053bb91f --- /dev/null +++ b/pinning/remote/client/openapi/response.go @@ -0,0 +1,46 @@ +/* + * IPFS Pinning Service API + * + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * API version: 0.0.5 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "net/http" +) + +// APIResponse stores the API response returned by the server. +type APIResponse struct { + *http.Response `json:"-"` + Message string `json:"message,omitempty"` + // Operation is the name of the OpenAPI operation. + Operation string `json:"operation,omitempty"` + // RequestURL is the request URL. This value is always available, even if the + // embedded *http.Response is nil. + RequestURL string `json:"url,omitempty"` + // Method is the HTTP method used for the request. This value is always + // available, even if the embedded *http.Response is nil. + Method string `json:"method,omitempty"` + // Payload holds the contents of the response body (which may be nil or empty). + // This is provided here as the raw response.Body() reader will have already + // been drained. + Payload []byte `json:"-"` +} + +// NewAPIResponse returns a new APIResonse object. +func NewAPIResponse(r *http.Response) *APIResponse { + + response := &APIResponse{Response: r} + return response +} + +// NewAPIResponseWithError returns a new APIResponse object with the provided error message. +func NewAPIResponseWithError(errorMessage string) *APIResponse { + + response := &APIResponse{Message: errorMessage} + return response +} diff --git a/pinning/remote/client/openapi/utils.go b/pinning/remote/client/openapi/utils.go new file mode 100644 index 000000000..a5daa9247 --- /dev/null +++ b/pinning/remote/client/openapi/utils.go @@ -0,0 +1,327 @@ +/* + * IPFS Pinning Service API + * + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * API version: 0.0.5 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "encoding/json" + "time" +) + +// PtrBool is a helper routine that returns a pointer to given integer value. +func PtrBool(v bool) *bool { return &v } + +// PtrInt is a helper routine that returns a pointer to given integer value. +func PtrInt(v int) *int { return &v } + +// PtrInt32 is a helper routine that returns a pointer to given integer value. +func PtrInt32(v int32) *int32 { return &v } + +// PtrInt64 is a helper routine that returns a pointer to given integer value. +func PtrInt64(v int64) *int64 { return &v } + +// PtrFloat32 is a helper routine that returns a pointer to given float value. +func PtrFloat32(v float32) *float32 { return &v } + +// PtrFloat64 is a helper routine that returns a pointer to given float value. +func PtrFloat64(v float64) *float64 { return &v } + +// PtrString is a helper routine that returns a pointer to given string value. +func PtrString(v string) *string { return &v } + +// PtrTime is helper routine that returns a pointer to given Time value. +func PtrTime(v time.Time) *time.Time { return &v } + +type NullableBool struct { + value *bool + isSet bool +} + +func (v NullableBool) Get() *bool { + return v.value +} + +func (v *NullableBool) Set(val *bool) { + v.value = val + v.isSet = true +} + +func (v NullableBool) IsSet() bool { + return v.isSet +} + +func (v *NullableBool) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableBool(val *bool) *NullableBool { + return &NullableBool{value: val, isSet: true} +} + +func (v NullableBool) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableBool) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableInt struct { + value *int + isSet bool +} + +func (v NullableInt) Get() *int { + return v.value +} + +func (v *NullableInt) Set(val *int) { + v.value = val + v.isSet = true +} + +func (v NullableInt) IsSet() bool { + return v.isSet +} + +func (v *NullableInt) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableInt(val *int) *NullableInt { + return &NullableInt{value: val, isSet: true} +} + +func (v NullableInt) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableInt) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableInt32 struct { + value *int32 + isSet bool +} + +func (v NullableInt32) Get() *int32 { + return v.value +} + +func (v *NullableInt32) Set(val *int32) { + v.value = val + v.isSet = true +} + +func (v NullableInt32) IsSet() bool { + return v.isSet +} + +func (v *NullableInt32) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableInt32(val *int32) *NullableInt32 { + return &NullableInt32{value: val, isSet: true} +} + +func (v NullableInt32) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableInt32) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableInt64 struct { + value *int64 + isSet bool +} + +func (v NullableInt64) Get() *int64 { + return v.value +} + +func (v *NullableInt64) Set(val *int64) { + v.value = val + v.isSet = true +} + +func (v NullableInt64) IsSet() bool { + return v.isSet +} + +func (v *NullableInt64) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableInt64(val *int64) *NullableInt64 { + return &NullableInt64{value: val, isSet: true} +} + +func (v NullableInt64) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableInt64) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableFloat32 struct { + value *float32 + isSet bool +} + +func (v NullableFloat32) Get() *float32 { + return v.value +} + +func (v *NullableFloat32) Set(val *float32) { + v.value = val + v.isSet = true +} + +func (v NullableFloat32) IsSet() bool { + return v.isSet +} + +func (v *NullableFloat32) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableFloat32(val *float32) *NullableFloat32 { + return &NullableFloat32{value: val, isSet: true} +} + +func (v NullableFloat32) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableFloat32) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableFloat64 struct { + value *float64 + isSet bool +} + +func (v NullableFloat64) Get() *float64 { + return v.value +} + +func (v *NullableFloat64) Set(val *float64) { + v.value = val + v.isSet = true +} + +func (v NullableFloat64) IsSet() bool { + return v.isSet +} + +func (v *NullableFloat64) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableFloat64(val *float64) *NullableFloat64 { + return &NullableFloat64{value: val, isSet: true} +} + +func (v NullableFloat64) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableFloat64) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableString struct { + value *string + isSet bool +} + +func (v NullableString) Get() *string { + return v.value +} + +func (v *NullableString) Set(val *string) { + v.value = val + v.isSet = true +} + +func (v NullableString) IsSet() bool { + return v.isSet +} + +func (v *NullableString) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableString(val *string) *NullableString { + return &NullableString{value: val, isSet: true} +} + +func (v NullableString) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableString) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableTime struct { + value *time.Time + isSet bool +} + +func (v NullableTime) Get() *time.Time { + return v.value +} + +func (v *NullableTime) Set(val *time.Time) { + v.value = val + v.isSet = true +} + +func (v NullableTime) IsSet() bool { + return v.isSet +} + +func (v *NullableTime) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableTime(val *time.Time) *NullableTime { + return &NullableTime{value: val, isSet: true} +} + +func (v NullableTime) MarshalJSON() ([]byte, error) { + return v.value.MarshalJSON() +} + +func (v *NullableTime) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} From 5d1534a2cdaf586b92c1531a2dfe8481e2fc39d3 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Tue, 1 Sep 2020 13:12:49 -0400 Subject: [PATCH 3263/3817] better error logging/debugging + fixed ls bug This commit was moved from ipfs/go-pinning-service-http-client@fa6705a12566cb930bd7e1ef9e7f3ac57dca7f7a --- pinning/remote/client/client.go | 61 +++++++++++++++----- pinning/remote/client/cmd/main.go | 50 +++++++++++------ pinning/remote/client/model.go | 93 +++++++++++++++++++++++++++++-- 3 files changed, 167 insertions(+), 37 deletions(-) diff --git a/pinning/remote/client/client.go b/pinning/remote/client/client.go index b4fa37830..9a0043d7c 100644 --- a/pinning/remote/client/client.go +++ b/pinning/remote/client/client.go @@ -1,9 +1,10 @@ package go_pinning_service_http_client import ( + "bytes" "context" + "encoding/json" "fmt" - "io/ioutil" "net/http" "time" @@ -202,27 +203,31 @@ func (c *Client) LsSync(ctx context.Context, opts ...LsOption) ([]PinStatusGette func (c *Client) lsInternal(ctx context.Context, settings *lsSettings) (pinResults, error) { getter := c.client.PinsApi.PinsGet(ctx) if len(settings.cids) > 0 { - getter.Cid(settings.cids) + getter = getter.Cid(settings.cids) } if len(settings.status) > 0 { - getter.Status(settings.status) + statuses := make([]openapi.Status, len(settings.status)) + for i := 0; i < len(statuses); i++ { + statuses[i] = openapi.Status(settings.status[i]) + } + getter = getter.Status(statuses) } if settings.limit == nil { - getter.Limit(defaultLimit) + getter = getter.Limit(defaultLimit) } else { - getter.Limit(*settings.limit) + getter = getter.Limit(*settings.limit) } if len(settings.name) > 0 { - getter.Name(settings.name) + getter = getter.Name(settings.name) } if settings.before != nil { - getter.Before(*settings.before) + getter = getter.Before(*settings.before) } if settings.after != nil { - getter.After(*settings.after) + getter = getter.After(*settings.after) } if settings.meta != nil { - getter.Meta(settings.meta) + getter = getter.Meta(settings.meta) } // TODO: Ignoring HTTP Response OK? @@ -367,10 +372,38 @@ func getCIDEncoder() multibase.Encoder { } func httperr(resp *http.Response, e error) error { - body, err := ioutil.ReadAll(resp.Body) - var bodystr string - if err == nil { - bodystr = string(body) + oerr, ok := e.(openapi.GenericOpenAPIError) + if !ok { + panic("wrong error type") + } + var buf bytes.Buffer + var err error + + var reqStr string + if resp.Request.GetBody != nil { + resp.Request.Body, err = resp.Request.GetBody() + if err != nil { + reqStr = err.Error() + } else if err := resp.Request.Write(&buf); err != nil { + reqStr = err.Error() + } else { + reqStr = buf.String() + } + } else { + reqStr = resp.Request.URL.String() } - return fmt.Errorf("httpresp code: %d, httpresp: %s, httpbody: %s, err: %w", resp.StatusCode, resp.Status, bodystr, e) + + bodystr := string(oerr.Body()) + //body, err := ioutil.ReadAll(resp.Body) + //var bodystr string + //if err == nil { + // bodystr = string(body) + //} + relevantErr := fmt.Sprintf("{ httpcode: %d, httpresp: %s, httpbody: %s, reqstr: %s }", resp.StatusCode, resp.Status, bodystr, reqStr) + relevantErrBytes, err := json.MarshalIndent(relevantErr, "", "\t") + if err != nil { + return fmt.Errorf("RelevantInfo : %s, MarshalErr: %w, Err: %w", relevantErr, err, e) + } + + return fmt.Errorf("relevantErr: %s, err: %w", relevantErrBytes, e) } diff --git a/pinning/remote/client/cmd/main.go b/pinning/remote/client/cmd/main.go index fefd9503d..d5b74bb2f 100644 --- a/pinning/remote/client/cmd/main.go +++ b/pinning/remote/client/cmd/main.go @@ -6,6 +6,7 @@ import ( "github.com/ipfs/go-cid" pinclient "github.com/ipfs/go-pinning-service-http-client" "os" + "time" ) func main() { @@ -35,30 +36,37 @@ func main() { } _ = ipfsPgCid + listPins(ctx, c) + fmt.Println("Adding libp2p home page") - ps, err := c.Add(ctx, libp2pCid, pinclient.PinOpts.WithName("libp2p_home")) + ps, err := c.Add(ctx, libp2pCid, pinclient.PinOpts.WithName("libp2p")) if err == nil { - fmt.Println(ps.GetStatus()) + fmt.Printf("PinStatus: %v \n", ps) } else { fmt.Println(err) } - fmt.Println("List all pins") - pins, err := c.LsSync(ctx) - fmt.Println(err) + listPins(ctx, c) - for _, p := range pins { - fmt.Printf("Pin Name: %s, CID: %s", p.GetPin().GetName(), p.GetPin().GetCid().String()) + fmt.Println("Check on pin status") + if ps == nil { + panic("Skipping pin status check because the pin is null") } - fmt.Println("Check on pin status") - status, err := c.GetStatusByID(ctx, ps.GetId()) - if err == nil { - fmt.Println(status.GetStatus()) - } else { - fmt.Println(err) + var pinned bool + for !pinned { + status, err := c.GetStatusByID(ctx, ps.GetId()) + if err == nil { + fmt.Println(status.GetStatus()) + pinned = status.GetStatus() == pinclient.StatusPinned + } else { + fmt.Println(err) + } + time.Sleep(time.Millisecond * 500) } + listPins(ctx, c) + fmt.Println("Delete pin") err = c.DeleteByID(ctx, ps.GetId()) if err == nil { @@ -67,11 +75,17 @@ func main() { fmt.Println(err) } - fmt.Println("List all pins") - pins, err = c.LsSync(ctx) - fmt.Println(err) + listPins(ctx, c) +} - for _, p := range pins { - fmt.Printf("Pin Name: %s, CID: %s", p.GetPin().GetName(), p.GetPin().GetCid().String()) +func listPins(ctx context.Context, c *pinclient.Client) { + fmt.Println("List all pins") + pins, err := c.LsSync(ctx) + if err != nil { + fmt.Println(err) + } else { + for _, p := range pins { + fmt.Printf("Pin: %v \n", p) + } } } diff --git a/pinning/remote/client/model.go b/pinning/remote/client/model.go index e6c552d51..cbb4eaeb1 100644 --- a/pinning/remote/client/model.go +++ b/pinning/remote/client/model.go @@ -1,6 +1,8 @@ package go_pinning_service_http_client import ( + "encoding/json" + "fmt" "github.com/ipfs/go-cid" "github.com/ipfs/go-pinning-service-http-client/openapi" "github.com/multiformats/go-multiaddr" @@ -9,6 +11,8 @@ import ( // PinGetter Getter for Pin object type PinGetter interface { + fmt.Stringer + json.Marshaler // CID to be pinned recursively GetCid() cid.Cid // Optional name for pinned data; can be used for lookups later @@ -23,6 +27,37 @@ type pinObject struct { openapi.Pin } +func (p *pinObject) MarshalJSON() ([]byte, error) { + var originsStr string + if o := p.GetOrigins(); o != nil { + originsBytes, err := json.Marshal(o) + if err == nil { + originsStr = string(originsBytes) + } + } + + var metaStr string + if meta := p.GetMeta(); meta != nil { + metaBytes, err := json.Marshal(meta) + if err == nil { + metaStr = string(metaBytes) + } + } + + str := fmt.Sprintf("{ \"Cid\" : \"%v\", \"Name\" : \"%s\", \"Origins\" : %v, \"Meta\" : %v }", + p.GetCid(), p.GetName(), originsStr, metaStr) + return []byte(str), nil +} + +func (p *pinObject) String() string { + marshalled, err := json.MarshalIndent(p, "", "\t") + if err != nil { + return "" + } + + return string(marshalled) +} + func (p *pinObject) GetCid() cid.Cid { c, err := cid.Parse(p.Pin.Cid) if err != nil { @@ -31,19 +66,31 @@ func (p *pinObject) GetCid() cid.Cid { return c } -type Status = openapi.Status +type Status string const ( - StatusQueued Status = openapi.QUEUED - StatusPinning Status = openapi.PINNING - StatusPinned Status = openapi.PINNED - StatusFailed Status = openapi.FAILED + StatusUnknown Status = "" + StatusQueued Status = Status(openapi.QUEUED) + StatusPinning Status = Status(openapi.PINNING) + StatusPinned Status = Status(openapi.PINNED) + StatusFailed Status = Status(openapi.FAILED) ) +func (s Status) String() string { + switch s { + case StatusQueued, StatusPinning, StatusPinned, StatusFailed: + return string(s) + default: + return string(StatusUnknown) + } +} + var validStatuses = []Status{"queued", "pinning", "pinned", "failed"} // PinStatusGetter Getter for Pin object with status type PinStatusGetter interface { + fmt.Stringer + json.Marshaler // Globally unique ID of the pin request; can be used to check the status of ongoing pinning, modification of pin object, or pin removal GetId() string GetStatus() Status @@ -77,3 +124,39 @@ func (p *pinStatusObject) GetDelegates() []multiaddr.Multiaddr { func (p *pinStatusObject) GetPin() PinGetter { return &pinObject{p.Pin} } + +func (p *pinStatusObject) GetStatus() Status { + return Status(p.PinStatus.GetStatus()) +} + +func (p *pinStatusObject) MarshalJSON() ([]byte, error) { + var delegatesStr string + if d := p.GetDelegates(); d != nil { + delegatesBytes, err := json.Marshal(d) + if err == nil { + delegatesStr = string(delegatesBytes) + } + } + + var infoStr string + if info := p.GetInfo(); info != nil { + infoBytes, err := json.Marshal(info) + if err == nil { + infoStr = string(infoBytes) + } + } + + str := fmt.Sprintf("{\"Pin\" : %v, \"ID\" : \"%s\", \"Status\" : \"%s\", \"Created\" : \"%v\", \"Delegates\" : %v, \"Info\" : %v }", + p.GetPin(), p.GetId(), p.GetStatus(), p.GetCreated(), delegatesStr, infoStr) + + return []byte(str), nil +} + +func (p *pinStatusObject) String() string { + marshalled, err := json.MarshalIndent(p, "", "\t") + if err != nil { + return "" + } + + return string(marshalled) +} From cc34a8bfe3877861ee954cc98428ff83d98dd60b Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Thu, 10 Sep 2020 21:26:38 -0400 Subject: [PATCH 3264/3817] fix: properly emit JSON strings for empty maps in pin and pinStatus objects This commit was moved from ipfs/go-pinning-service-http-client@c86fa75aedfb020006ae17b2f49d6eb8842cb6b9 --- pinning/remote/client/model.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pinning/remote/client/model.go b/pinning/remote/client/model.go index cbb4eaeb1..8fb51efff 100644 --- a/pinning/remote/client/model.go +++ b/pinning/remote/client/model.go @@ -36,7 +36,7 @@ func (p *pinObject) MarshalJSON() ([]byte, error) { } } - var metaStr string + metaStr := "{}" if meta := p.GetMeta(); meta != nil { metaBytes, err := json.Marshal(meta) if err == nil { @@ -138,7 +138,7 @@ func (p *pinStatusObject) MarshalJSON() ([]byte, error) { } } - var infoStr string + infoStr := "{}" if info := p.GetInfo(); info != nil { infoBytes, err := json.Marshal(info) if err == nil { From 238bbdfc284823c0e152378022f958c074215dc3 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Tue, 22 Sep 2020 17:43:51 -0700 Subject: [PATCH 3265/3817] feat(deps): update ipld libs update ipld libs to fairly recent version (lastest used by go-ipld-prime-proto This commit was moved from ipld/go-car@3df22374d842b97d8091debdfe1ee731c8dbaaa1 --- ipld/car/car_test.go | 2 +- ipld/car/selectivecar.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 3265d8374..53d59b365 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -96,7 +96,7 @@ func TestRoundtripSelective(t *testing.T) { assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) - ssb := builder.NewSelectorSpecBuilder(basicnode.Style.Any) + ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype.Any) // the graph assembled above looks as follows, in order: // nd3 -> [c, nd2 -> [nd1 -> a, b, nd1 -> a]] diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index 0b11fc815..a41623731 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -221,8 +221,8 @@ func (sct *selectiveCarTraverser) loader(lnk ipld.Link, ctx ipld.LinkContext) (i func (sct *selectiveCarTraverser) traverseBlocks() error { - nsc := dagpb.AddDagPBSupportToChooser(func(ipld.Link, ipld.LinkContext) (ipld.NodeStyle, error) { - return basicnode.Style.Any, nil + nsc := dagpb.AddDagPBSupportToChooser(func(ipld.Link, ipld.LinkContext) (ipld.NodePrototype, error) { + return basicnode.Prototype.Any, nil }) for _, carDag := range sct.sc.dags { @@ -243,9 +243,9 @@ func (sct *selectiveCarTraverser) traverseBlocks() error { nd := nb.Build() err = traversal.Progress{ Cfg: &traversal.Config{ - Ctx: sct.sc.ctx, - LinkLoader: sct.loader, - LinkTargetNodeStyleChooser: nsc, + Ctx: sct.sc.ctx, + LinkLoader: sct.loader, + LinkTargetNodePrototypeChooser: nsc, }, }.WalkAdv(nd, parsed, func(traversal.Progress, ipld.Node, traversal.VisitReason) error { return nil }) if err != nil { From bfaf6220253fff3116f802aaf2a0c34bc8d21e6b Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 17 Sep 2020 17:07:08 +1000 Subject: [PATCH 3266/3817] chore: add test to verify allowable data layouts Ref: https://github.com/ipld/specs/pull/297 This commit was moved from ipfs/go-merkledag@b630d85ee62eaff51aa927169d11078e58e396c5 --- ipld/merkledag/pb/compat_test.go | 299 +++++++++++++++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 ipld/merkledag/pb/compat_test.go diff --git a/ipld/merkledag/pb/compat_test.go b/ipld/merkledag/pb/compat_test.go new file mode 100644 index 000000000..529cee0ca --- /dev/null +++ b/ipld/merkledag/pb/compat_test.go @@ -0,0 +1,299 @@ +package merkledag_pb + +// mirrored in JavaScript @ https://github.com/ipld/js-dag-pb/blob/master/test/test-compat.js + +import ( + "encoding/hex" + "encoding/json" + "testing" +) + +var dataZero []byte = make([]byte, 0) +var dataSome []byte = []byte{0, 1, 2, 3, 4} +var cidBytes []byte = []byte{1, 85, 0, 5, 0, 1, 2, 3, 4} +var zeroName string = "" +var someName string = "some name" +var zeroTsize uint64 = 0 +var someTsize uint64 = 1010 +var largeTsize uint64 = 9007199254740991 // JavaScript Number.MAX_SAFE_INTEGER + +type testCase struct { + name string + node *PBNode + expectedBytes string + expectedForm string +} + +var testCases = []testCase{ + { + name: "empty", + node: &PBNode{}, + expectedBytes: "", + expectedForm: "{}", + }, + { + name: "Data zero", + node: &PBNode{Data: dataZero}, + expectedBytes: "0a00", + expectedForm: `{ + "Data": "" +}`, + }, + { + name: "Data some", + node: &PBNode{Data: dataSome}, + expectedBytes: "0a050001020304", + expectedForm: `{ + "Data": "0001020304" +}`, + }, + { + name: "Links zero", + node: &PBNode{Links: make([]*PBLink, 0)}, + expectedBytes: "", + expectedForm: "{}", + }, + { + name: "Data some Links zero", + node: &PBNode{Data: dataSome, Links: make([]*PBLink, 0)}, + expectedBytes: "0a050001020304", + expectedForm: `{ + "Data": "0001020304" +}`, + }, + { + name: "Links empty", + node: &PBNode{Links: []*PBLink{{}}}, + expectedBytes: "1200", + expectedForm: `{ + "Links": [ + {} + ] +}`, + }, + { + name: "Data some Links empty", + node: &PBNode{Data: dataSome, Links: []*PBLink{{}}}, + expectedBytes: "12000a050001020304", + expectedForm: `{ + "Data": "0001020304", + "Links": [ + {} + ] +}`, + }, + { + name: "Links Hash zero", + node: &PBNode{Links: []*PBLink{{Hash: dataZero}}}, + expectedBytes: "12020a00", + expectedForm: `{ + "Links": [ + { + "Hash": "" + } + ] +}`, + }, + { + name: "Links Hash some", + node: &PBNode{Links: []*PBLink{{Hash: cidBytes}}}, + expectedBytes: "120b0a09015500050001020304", + expectedForm: `{ + "Links": [ + { + "Hash": "015500050001020304" + } + ] +}`, + }, + { + name: "Links Name zero", + node: &PBNode{Links: []*PBLink{{Name: &zeroName}}}, + expectedBytes: "12021200", + expectedForm: `{ + "Links": [ + { + "Name": "" + } + ] +}`, + }, + { + name: "Links Hash some Name zero", + node: &PBNode{Links: []*PBLink{{Hash: cidBytes, Name: &zeroName}}}, + expectedBytes: "120d0a090155000500010203041200", + expectedForm: `{ + "Links": [ + { + "Hash": "015500050001020304", + "Name": "" + } + ] +}`, + }, + { + name: "Links Name some", + node: &PBNode{Links: []*PBLink{{Name: &someName}}}, + expectedBytes: "120b1209736f6d65206e616d65", + expectedForm: `{ + "Links": [ + { + "Name": "some name" + } + ] +}`, + }, + { + name: "Links Hash some Name some", + node: &PBNode{Links: []*PBLink{{Hash: cidBytes, Name: &someName}}}, + expectedBytes: "12160a090155000500010203041209736f6d65206e616d65", + expectedForm: `{ + "Links": [ + { + "Hash": "015500050001020304", + "Name": "some name" + } + ] +}`, + }, + { + name: "Links Tsize zero", + node: &PBNode{Links: []*PBLink{{Tsize: &zeroTsize}}}, + expectedBytes: "12021800", + expectedForm: `{ + "Links": [ + { + "Tsize": 0 + } + ] +}`, + }, + { + name: "Links Hash some Tsize zero", + node: &PBNode{Links: []*PBLink{{Hash: cidBytes, Tsize: &zeroTsize}}}, + expectedBytes: "120d0a090155000500010203041800", + expectedForm: `{ + "Links": [ + { + "Hash": "015500050001020304", + "Tsize": 0 + } + ] +}`, + }, + { + name: "Links Tsize some", + node: &PBNode{Links: []*PBLink{{Tsize: &someTsize}}}, + expectedBytes: "120318f207", + expectedForm: `{ + "Links": [ + { + "Tsize": 1010 + } + ] +}`, + }, + { + name: "Links Hash some Tsize some", + node: &PBNode{Links: []*PBLink{{Hash: cidBytes, Tsize: &largeTsize}}}, + expectedBytes: "12140a0901550005000102030418ffffffffffffff0f", + expectedForm: `{ + "Links": [ + { + "Hash": "015500050001020304", + "Tsize": 9007199254740991 + } + ] +}`, + }, +} + +func TestCompat(t *testing.T) { + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + verifyRoundTrip(t, tc) + }) + } +} + +func verifyRoundTrip(t *testing.T, tc testCase) { + actualBytes, actualForm, err := nodeRoundTripToString(t, tc.node) + if err != nil { + t.Fatal(err) + } + + if actualBytes != tc.expectedBytes { + t.Logf( + "Expected bytes: [%v]\nGot: [%v]\n", + tc.expectedBytes, + actualBytes) + t.Error("Did not match") + } + + if actualForm != tc.expectedForm { + t.Logf( + "Expected form: [%v]\nGot: [%v]\n", + tc.expectedForm, + actualForm) + t.Error("Did not match") + } +} + +func nodeRoundTripToString(t *testing.T, n *PBNode) (string, string, error) { + bytes, err := n.Marshal() + if err != nil { + return "", "", err + } + t.Logf("[%v]\n", hex.EncodeToString(bytes)) + rt := new(PBNode) + if err := rt.Unmarshal(bytes); err != nil { + return "", "", err + } + str, err := json.MarshalIndent(cleanPBNode(t, rt), "", "\t") + if err != nil { + return "", "", err + } + return hex.EncodeToString(bytes), string(str), nil +} + +// convert a PBLink into a map for clean JSON marshalling +func cleanPBLink(t *testing.T, link *PBLink) map[string]interface{} { + if link == nil { + return nil + } + // this would be a bad pb decode + if link.XXX_unrecognized != nil { + t.Fatal("Got unexpected XXX_unrecognized") + } + nl := make(map[string]interface{}) + if link.Hash != nil { + nl["Hash"] = hex.EncodeToString(link.Hash) + } + if link.Name != nil { + nl["Name"] = link.Name + } + if link.Tsize != nil { + nl["Tsize"] = link.Tsize + } + return nl +} + +// convert a PBNode into a map for clean JSON marshalling +func cleanPBNode(t *testing.T, node *PBNode) map[string]interface{} { + // this would be a bad pb decode + if node.XXX_unrecognized != nil { + t.Fatal("Got unexpected XXX_unrecognized") + } + nn := make(map[string]interface{}) + if node.Data != nil { + nn["Data"] = hex.EncodeToString(node.Data) + } + if node.Links != nil { + links := make([]map[string]interface{}, len(node.Links)) + for i, l := range node.Links { + links[i] = cleanPBLink(t, l) + } + nn["Links"] = links + } + return nn +} From bc509bd0c0a1d94b3664a5637568d3d01ab586cc Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Mon, 21 Sep 2020 16:08:11 -0400 Subject: [PATCH 3267/3817] feat: update to pinning service api spec v1.0.0 This commit was moved from ipfs/go-pinning-service-http-client@0f15288be30ead28cf15b210e29fea521e1db8d7 --- pinning/remote/client/client.go | 11 +- pinning/remote/client/cmd/main.go | 4 +- pinning/remote/client/model.go | 10 +- pinning/remote/client/openapi/README.md | 36 +- pinning/remote/client/openapi/api_pins.go | 324 ++++++++++++------ pinning/remote/client/openapi/client.go | 2 +- .../remote/client/openapi/configuration.go | 2 +- pinning/remote/client/openapi/docs/Error.md | 43 +-- .../remote/client/openapi/docs/ErrorError.md | 77 +++++ pinning/remote/client/openapi/docs/Pin.md | 2 +- .../remote/client/openapi/docs/PinStatus.md | 22 +- pinning/remote/client/openapi/docs/PinsApi.md | 106 +++--- pinning/remote/client/openapi/model_error.go | 61 +--- .../client/openapi/model_error_error.go | 143 ++++++++ pinning/remote/client/openapi/model_pin.go | 4 +- .../client/openapi/model_pin_results.go | 2 +- .../remote/client/openapi/model_pin_status.go | 32 +- pinning/remote/client/openapi/model_status.go | 2 +- pinning/remote/client/openapi/response.go | 2 +- pinning/remote/client/openapi/utils.go | 2 +- 20 files changed, 592 insertions(+), 295 deletions(-) create mode 100644 pinning/remote/client/openapi/docs/ErrorError.md create mode 100644 pinning/remote/client/openapi/model_error_error.go diff --git a/pinning/remote/client/client.go b/pinning/remote/client/client.go index 9a0043d7c..f021abb08 100644 --- a/pinning/remote/client/client.go +++ b/pinning/remote/client/client.go @@ -39,7 +39,8 @@ func NewClient(url, bearerToken string) *Client { } func getError(e *openapi.Error) error { - return fmt.Errorf("request error: %d - %s", e.Code, e.Message) + err := e.GetError() + return fmt.Errorf("request error: %s - %s", err.GetReason(), err.GetDetails()) } // TODO: We should probably make sure there are no duplicates sent @@ -311,7 +312,7 @@ func (c *Client) Add(ctx context.Context, cid cid.Cid, opts ...AddOption) (PinSt } func (c *Client) GetStatusByID(ctx context.Context, pinID string) (PinStatusGetter, error) { - getter := c.client.PinsApi.PinsIdGet(ctx, pinID) + getter := c.client.PinsApi.PinsRequestidGet(ctx, pinID) result, httpresp, err := getter.Execute() if err != nil { err := httperr(httpresp, err) @@ -322,7 +323,7 @@ func (c *Client) GetStatusByID(ctx context.Context, pinID string) (PinStatusGett } func (c *Client) DeleteByID(ctx context.Context, pinID string) error { - deleter := c.client.PinsApi.PinsIdDelete(ctx, pinID) + deleter := c.client.PinsApi.PinsRequestidDelete(ctx, pinID) httpresp, err := deleter.Execute() if err != nil { err := httperr(httpresp, err) @@ -339,7 +340,7 @@ func (c *Client) Modify(ctx context.Context, pinID string, cid cid.Cid, opts ... } } - adder := c.client.PinsApi.PinsIdPost(ctx, pinID) + adder := c.client.PinsApi.PinsRequestidPost(ctx, pinID) p := openapi.Pin{ Cid: cid.Encode(getCIDEncoder()), } @@ -402,7 +403,7 @@ func httperr(resp *http.Response, e error) error { relevantErr := fmt.Sprintf("{ httpcode: %d, httpresp: %s, httpbody: %s, reqstr: %s }", resp.StatusCode, resp.Status, bodystr, reqStr) relevantErrBytes, err := json.MarshalIndent(relevantErr, "", "\t") if err != nil { - return fmt.Errorf("RelevantInfo : %s, MarshalErr: %w, Err: %w", relevantErr, err, e) + return fmt.Errorf("RelevantInfo : %s, MarshalErr: %s, Err: %w", relevantErr, err, e) } return fmt.Errorf("relevantErr: %s, err: %w", relevantErrBytes, e) diff --git a/pinning/remote/client/cmd/main.go b/pinning/remote/client/cmd/main.go index d5b74bb2f..c095ed08c 100644 --- a/pinning/remote/client/cmd/main.go +++ b/pinning/remote/client/cmd/main.go @@ -55,7 +55,7 @@ func main() { var pinned bool for !pinned { - status, err := c.GetStatusByID(ctx, ps.GetId()) + status, err := c.GetStatusByID(ctx, ps.GetRequestId()) if err == nil { fmt.Println(status.GetStatus()) pinned = status.GetStatus() == pinclient.StatusPinned @@ -68,7 +68,7 @@ func main() { listPins(ctx, c) fmt.Println("Delete pin") - err = c.DeleteByID(ctx, ps.GetId()) + err = c.DeleteByID(ctx, ps.GetRequestId()) if err == nil { fmt.Println("Successfully deleted pin") } else { diff --git a/pinning/remote/client/model.go b/pinning/remote/client/model.go index 8fb51efff..506c43ae3 100644 --- a/pinning/remote/client/model.go +++ b/pinning/remote/client/model.go @@ -92,7 +92,7 @@ type PinStatusGetter interface { fmt.Stringer json.Marshaler // Globally unique ID of the pin request; can be used to check the status of ongoing pinning, modification of pin object, or pin removal - GetId() string + GetRequestId() string GetStatus() Status // Immutable timestamp indicating when a pin request entered a pinning service; can be used for filtering results and pagination GetCreated() time.Time @@ -129,6 +129,10 @@ func (p *pinStatusObject) GetStatus() Status { return Status(p.PinStatus.GetStatus()) } +func (p *pinStatusObject) GetRequestId() string { + return p.GetRequestid() +} + func (p *pinStatusObject) MarshalJSON() ([]byte, error) { var delegatesStr string if d := p.GetDelegates(); d != nil { @@ -146,8 +150,8 @@ func (p *pinStatusObject) MarshalJSON() ([]byte, error) { } } - str := fmt.Sprintf("{\"Pin\" : %v, \"ID\" : \"%s\", \"Status\" : \"%s\", \"Created\" : \"%v\", \"Delegates\" : %v, \"Info\" : %v }", - p.GetPin(), p.GetId(), p.GetStatus(), p.GetCreated(), delegatesStr, infoStr) + str := fmt.Sprintf("{\"Pin\" : %v, \"RequestID\" : \"%s\", \"Status\" : \"%s\", \"Created\" : \"%v\", \"Delegates\" : %v, \"Info\" : %v }", + p.GetPin(), p.GetRequestId(), p.GetStatus(), p.GetCreated(), delegatesStr, infoStr) return []byte(str), nil } diff --git a/pinning/remote/client/openapi/README.md b/pinning/remote/client/openapi/README.md index 64e843f88..277bbc39a 100644 --- a/pinning/remote/client/openapi/README.md +++ b/pinning/remote/client/openapi/README.md @@ -13,10 +13,21 @@ The IPFS Pinning Service API is intended to be an implementation-agnostic API&#x This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). + +## Identifiers +### cid +[Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. +### requestid +Unique identifier of a pin request. + +When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. + +Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. + ## Objects ### Pin object -![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) +![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. @@ -24,30 +35,30 @@ It includes the `cid` of data to be pinned, as well as optional metadata in `nam ### Pin status response -![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) +![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. -It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. +It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle -![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) +![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: -- `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future +- `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. -In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. +In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. -### Modifying an existing pin object -The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. +### Replacing an existing pin object +The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object -A pin object can be removed via `DELETE /pins/{id}`. +A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints @@ -161,15 +172,16 @@ All URIs are relative to *https://pinning-service.example.com* Class | Method | HTTP request | Description ------------ | ------------- | ------------- | ------------- *PinsApi* | [**PinsGet**](docs/PinsApi.md#pinsget) | **Get** /pins | List pin objects -*PinsApi* | [**PinsIdDelete**](docs/PinsApi.md#pinsiddelete) | **Delete** /pins/{id} | Remove pin object -*PinsApi* | [**PinsIdGet**](docs/PinsApi.md#pinsidget) | **Get** /pins/{id} | Get pin object -*PinsApi* | [**PinsIdPost**](docs/PinsApi.md#pinsidpost) | **Post** /pins/{id} | Modify pin object *PinsApi* | [**PinsPost**](docs/PinsApi.md#pinspost) | **Post** /pins | Add pin object +*PinsApi* | [**PinsRequestidDelete**](docs/PinsApi.md#pinsrequestiddelete) | **Delete** /pins/{requestid} | Remove pin object +*PinsApi* | [**PinsRequestidGet**](docs/PinsApi.md#pinsrequestidget) | **Get** /pins/{requestid} | Get pin object +*PinsApi* | [**PinsRequestidPost**](docs/PinsApi.md#pinsrequestidpost) | **Post** /pins/{requestid} | Replace pin object ## Documentation For Models - [Error](docs/Error.md) + - [ErrorError](docs/ErrorError.md) - [Pin](docs/Pin.md) - [PinResults](docs/PinResults.md) - [PinStatus](docs/PinStatus.md) diff --git a/pinning/remote/client/openapi/api_pins.go b/pinning/remote/client/openapi/api_pins.go index 623862df7..ff03d0c99 100644 --- a/pinning/remote/client/openapi/api_pins.go +++ b/pinning/remote/client/openapi/api_pins.go @@ -1,7 +1,7 @@ /* * IPFS Pinning Service API * - * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * * API version: 0.0.5 * Generated by: OpenAPI Generator (https://openapi-generator.tech) @@ -180,7 +180,47 @@ func (r apiPinsGetRequest) Execute() (PinResults, *_nethttp.Response, error) { newErr.model = v return localVarReturnValue, localVarHTTPResponse, newErr } - if localVarHTTPResponse.StatusCode == 500 { + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 409 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode > 400 && localVarHTTPResponse.StatusCode < 500 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode >= 500 { var v Error err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { @@ -204,54 +244,61 @@ func (r apiPinsGetRequest) Execute() (PinResults, *_nethttp.Response, error) { return localVarReturnValue, localVarHTTPResponse, nil } -type apiPinsIdDeleteRequest struct { +type apiPinsPostRequest struct { ctx _context.Context apiService *PinsApiService - id string + pin *Pin +} + +func (r apiPinsPostRequest) Pin(pin Pin) apiPinsPostRequest { + r.pin = &pin + return r } /* -PinsIdDelete Remove pin object -Remove a pin object +PinsPost Add pin object +Add a new pin object for the current access token * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - * @param id -@return apiPinsIdDeleteRequest +@return apiPinsPostRequest */ -func (a *PinsApiService) PinsIdDelete(ctx _context.Context, id string) apiPinsIdDeleteRequest { - return apiPinsIdDeleteRequest{ +func (a *PinsApiService) PinsPost(ctx _context.Context) apiPinsPostRequest { + return apiPinsPostRequest{ apiService: a, ctx: ctx, - id: id, } } /* Execute executes the request - + @return PinStatus */ -func (r apiPinsIdDeleteRequest) Execute() (*_nethttp.Response, error) { +func (r apiPinsPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { var ( - localVarHTTPMethod = _nethttp.MethodDelete + localVarHTTPMethod = _nethttp.MethodPost localVarPostBody interface{} localVarFormFileName string localVarFileName string localVarFileBytes []byte + localVarReturnValue PinStatus ) - localBasePath, err := r.apiService.client.cfg.ServerURLWithContext(r.ctx, "PinsApiService.PinsIdDelete") + localBasePath, err := r.apiService.client.cfg.ServerURLWithContext(r.ctx, "PinsApiService.PinsPost") if err != nil { - return nil, GenericOpenAPIError{error: err.Error()} + return localVarReturnValue, nil, GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/pins/{id}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", _neturl.PathEscape(parameterToString(r.id, "")), -1) + localVarPath := localBasePath + "/pins" localVarHeaderParams := make(map[string]string) localVarQueryParams := _neturl.Values{} localVarFormParams := _neturl.Values{} + if r.pin == nil { + return localVarReturnValue, nil, reportError("pin is required and must be specified") + } + // to determine the Content-Type header - localVarHTTPContentTypes := []string{} + localVarHTTPContentTypes := []string{"application/json"} // set Content-Type header localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) @@ -267,20 +314,22 @@ func (r apiPinsIdDeleteRequest) Execute() (*_nethttp.Response, error) { if localVarHTTPHeaderAccept != "" { localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept } + // body params + localVarPostBody = r.pin req, err := r.apiService.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) if err != nil { - return nil, err + return localVarReturnValue, nil, err } localVarHTTPResponse, err := r.apiService.client.callAPI(req) if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err + return localVarReturnValue, localVarHTTPResponse, err } localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) localVarHTTPResponse.Body.Close() if err != nil { - return localVarHTTPResponse, err + return localVarReturnValue, localVarHTTPResponse, err } if localVarHTTPResponse.StatusCode >= 300 { @@ -293,88 +342,116 @@ func (r apiPinsIdDeleteRequest) Execute() (*_nethttp.Response, error) { err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { newErr.error = err.Error() - return localVarHTTPResponse, newErr + return localVarReturnValue, localVarHTTPResponse, newErr } newErr.model = v - return localVarHTTPResponse, newErr + return localVarReturnValue, localVarHTTPResponse, newErr } if localVarHTTPResponse.StatusCode == 401 { var v Error err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { newErr.error = err.Error() - return localVarHTTPResponse, newErr + return localVarReturnValue, localVarHTTPResponse, newErr } newErr.model = v - return localVarHTTPResponse, newErr + return localVarReturnValue, localVarHTTPResponse, newErr } if localVarHTTPResponse.StatusCode == 404 { var v Error err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { newErr.error = err.Error() - return localVarHTTPResponse, newErr + return localVarReturnValue, localVarHTTPResponse, newErr } newErr.model = v - return localVarHTTPResponse, newErr + return localVarReturnValue, localVarHTTPResponse, newErr } - if localVarHTTPResponse.StatusCode == 500 { + if localVarHTTPResponse.StatusCode == 409 { var v Error err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { newErr.error = err.Error() - return localVarHTTPResponse, newErr + return localVarReturnValue, localVarHTTPResponse, newErr } newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr } - return localVarHTTPResponse, newErr + if localVarHTTPResponse.StatusCode > 400 && localVarHTTPResponse.StatusCode < 500 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode >= 500 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr } - return localVarHTTPResponse, nil + err = r.apiService.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil } -type apiPinsIdGetRequest struct { +type apiPinsRequestidDeleteRequest struct { ctx _context.Context apiService *PinsApiService - id string + requestid string } /* -PinsIdGet Get pin object -Get a pin object and its status +PinsRequestidDelete Remove pin object +Remove a pin object * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - * @param id -@return apiPinsIdGetRequest + * @param requestid +@return apiPinsRequestidDeleteRequest */ -func (a *PinsApiService) PinsIdGet(ctx _context.Context, id string) apiPinsIdGetRequest { - return apiPinsIdGetRequest{ +func (a *PinsApiService) PinsRequestidDelete(ctx _context.Context, requestid string) apiPinsRequestidDeleteRequest { + return apiPinsRequestidDeleteRequest{ apiService: a, ctx: ctx, - id: id, + requestid: requestid, } } /* Execute executes the request - @return PinStatus + */ -func (r apiPinsIdGetRequest) Execute() (PinStatus, *_nethttp.Response, error) { +func (r apiPinsRequestidDeleteRequest) Execute() (*_nethttp.Response, error) { var ( - localVarHTTPMethod = _nethttp.MethodGet + localVarHTTPMethod = _nethttp.MethodDelete localVarPostBody interface{} localVarFormFileName string localVarFileName string localVarFileBytes []byte - localVarReturnValue PinStatus ) - localBasePath, err := r.apiService.client.cfg.ServerURLWithContext(r.ctx, "PinsApiService.PinsIdGet") + localBasePath, err := r.apiService.client.cfg.ServerURLWithContext(r.ctx, "PinsApiService.PinsRequestidDelete") if err != nil { - return localVarReturnValue, nil, GenericOpenAPIError{error: err.Error()} + return nil, GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/pins/{id}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", _neturl.PathEscape(parameterToString(r.id, "")), -1) + localVarPath := localBasePath + "/pins/{requestid}" + localVarPath = strings.Replace(localVarPath, "{"+"requestid"+"}", _neturl.PathEscape(parameterToString(r.requestid, "")), -1) localVarHeaderParams := make(map[string]string) localVarQueryParams := _neturl.Values{} @@ -399,18 +476,18 @@ func (r apiPinsIdGetRequest) Execute() (PinStatus, *_nethttp.Response, error) { } req, err := r.apiService.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) if err != nil { - return localVarReturnValue, nil, err + return nil, err } localVarHTTPResponse, err := r.apiService.client.callAPI(req) if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err + return localVarHTTPResponse, err } localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) localVarHTTPResponse.Body.Close() if err != nil { - return localVarReturnValue, localVarHTTPResponse, err + return localVarHTTPResponse, err } if localVarHTTPResponse.StatusCode >= 300 { @@ -423,69 +500,84 @@ func (r apiPinsIdGetRequest) Execute() (PinStatus, *_nethttp.Response, error) { err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr + return localVarHTTPResponse, newErr } newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr + return localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + return localVarHTTPResponse, newErr } if localVarHTTPResponse.StatusCode == 404 { var v Error err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr + return localVarHTTPResponse, newErr } newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr + return localVarHTTPResponse, newErr } - if localVarHTTPResponse.StatusCode == 500 { + if localVarHTTPResponse.StatusCode == 409 { var v Error err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr + return localVarHTTPResponse, newErr } newErr.model = v + return localVarHTTPResponse, newErr } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = r.apiService.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), + if localVarHTTPResponse.StatusCode > 400 && localVarHTTPResponse.StatusCode < 500 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + return localVarHTTPResponse, newErr } - return localVarReturnValue, localVarHTTPResponse, newErr + if localVarHTTPResponse.StatusCode >= 500 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarHTTPResponse, newErr } - return localVarReturnValue, localVarHTTPResponse, nil + return localVarHTTPResponse, nil } -type apiPinsIdPostRequest struct { +type apiPinsRequestidGetRequest struct { ctx _context.Context apiService *PinsApiService - id string - pin *Pin -} - -func (r apiPinsIdPostRequest) Pin(pin Pin) apiPinsIdPostRequest { - r.pin = &pin - return r + requestid string } /* -PinsIdPost Modify pin object -Modify an existing pin object +PinsRequestidGet Get pin object +Get a pin object and its status * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - * @param id -@return apiPinsIdPostRequest + * @param requestid +@return apiPinsRequestidGetRequest */ -func (a *PinsApiService) PinsIdPost(ctx _context.Context, id string) apiPinsIdPostRequest { - return apiPinsIdPostRequest{ +func (a *PinsApiService) PinsRequestidGet(ctx _context.Context, requestid string) apiPinsRequestidGetRequest { + return apiPinsRequestidGetRequest{ apiService: a, ctx: ctx, - id: id, + requestid: requestid, } } @@ -493,9 +585,9 @@ func (a *PinsApiService) PinsIdPost(ctx _context.Context, id string) apiPinsIdPo Execute executes the request @return PinStatus */ -func (r apiPinsIdPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { +func (r apiPinsRequestidGetRequest) Execute() (PinStatus, *_nethttp.Response, error) { var ( - localVarHTTPMethod = _nethttp.MethodPost + localVarHTTPMethod = _nethttp.MethodGet localVarPostBody interface{} localVarFormFileName string localVarFileName string @@ -503,24 +595,20 @@ func (r apiPinsIdPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { localVarReturnValue PinStatus ) - localBasePath, err := r.apiService.client.cfg.ServerURLWithContext(r.ctx, "PinsApiService.PinsIdPost") + localBasePath, err := r.apiService.client.cfg.ServerURLWithContext(r.ctx, "PinsApiService.PinsRequestidGet") if err != nil { return localVarReturnValue, nil, GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/pins/{id}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", _neturl.PathEscape(parameterToString(r.id, "")), -1) + localVarPath := localBasePath + "/pins/{requestid}" + localVarPath = strings.Replace(localVarPath, "{"+"requestid"+"}", _neturl.PathEscape(parameterToString(r.requestid, "")), -1) localVarHeaderParams := make(map[string]string) localVarQueryParams := _neturl.Values{} localVarFormParams := _neturl.Values{} - if r.pin == nil { - return localVarReturnValue, nil, reportError("pin is required and must be specified") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} + localVarHTTPContentTypes := []string{} // set Content-Type header localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) @@ -536,8 +624,6 @@ func (r apiPinsIdPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { if localVarHTTPHeaderAccept != "" { localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept } - // body params - localVarPostBody = r.pin req, err := r.apiService.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) if err != nil { return localVarReturnValue, nil, err @@ -599,7 +685,17 @@ func (r apiPinsIdPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { newErr.model = v return localVarReturnValue, localVarHTTPResponse, newErr } - if localVarHTTPResponse.StatusCode == 500 { + if localVarHTTPResponse.StatusCode > 400 && localVarHTTPResponse.StatusCode < 500 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode >= 500 { var v Error err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { @@ -623,27 +719,30 @@ func (r apiPinsIdPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { return localVarReturnValue, localVarHTTPResponse, nil } -type apiPinsPostRequest struct { +type apiPinsRequestidPostRequest struct { ctx _context.Context apiService *PinsApiService + requestid string pin *Pin } -func (r apiPinsPostRequest) Pin(pin Pin) apiPinsPostRequest { +func (r apiPinsRequestidPostRequest) Pin(pin Pin) apiPinsRequestidPostRequest { r.pin = &pin return r } /* -PinsPost Add pin object -Add a new pin object for the current access token +PinsRequestidPost Replace pin object +Replace an existing pin object (shortcut for executing remove and add operations in one step to avoid unnecessary garbage collection of blocks present in both recursive pins) * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). -@return apiPinsPostRequest + * @param requestid +@return apiPinsRequestidPostRequest */ -func (a *PinsApiService) PinsPost(ctx _context.Context) apiPinsPostRequest { - return apiPinsPostRequest{ +func (a *PinsApiService) PinsRequestidPost(ctx _context.Context, requestid string) apiPinsRequestidPostRequest { + return apiPinsRequestidPostRequest{ apiService: a, ctx: ctx, + requestid: requestid, } } @@ -651,7 +750,7 @@ func (a *PinsApiService) PinsPost(ctx _context.Context) apiPinsPostRequest { Execute executes the request @return PinStatus */ -func (r apiPinsPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { +func (r apiPinsRequestidPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { var ( localVarHTTPMethod = _nethttp.MethodPost localVarPostBody interface{} @@ -661,12 +760,13 @@ func (r apiPinsPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { localVarReturnValue PinStatus ) - localBasePath, err := r.apiService.client.cfg.ServerURLWithContext(r.ctx, "PinsApiService.PinsPost") + localBasePath, err := r.apiService.client.cfg.ServerURLWithContext(r.ctx, "PinsApiService.PinsRequestidPost") if err != nil { return localVarReturnValue, nil, GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/pins" + localVarPath := localBasePath + "/pins/{requestid}" + localVarPath = strings.Replace(localVarPath, "{"+"requestid"+"}", _neturl.PathEscape(parameterToString(r.requestid, "")), -1) localVarHeaderParams := make(map[string]string) localVarQueryParams := _neturl.Values{} @@ -756,7 +856,17 @@ func (r apiPinsPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { newErr.model = v return localVarReturnValue, localVarHTTPResponse, newErr } - if localVarHTTPResponse.StatusCode == 500 { + if localVarHTTPResponse.StatusCode > 400 && localVarHTTPResponse.StatusCode < 500 { + var v Error + err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode >= 500 { var v Error err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { diff --git a/pinning/remote/client/openapi/client.go b/pinning/remote/client/openapi/client.go index 2fa0b79ab..14a9d5f0c 100644 --- a/pinning/remote/client/openapi/client.go +++ b/pinning/remote/client/openapi/client.go @@ -1,7 +1,7 @@ /* * IPFS Pinning Service API * - * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * * API version: 0.0.5 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/pinning/remote/client/openapi/configuration.go b/pinning/remote/client/openapi/configuration.go index 2dd763a9d..618454bca 100644 --- a/pinning/remote/client/openapi/configuration.go +++ b/pinning/remote/client/openapi/configuration.go @@ -1,7 +1,7 @@ /* * IPFS Pinning Service API * - * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * * API version: 0.0.5 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/pinning/remote/client/openapi/docs/Error.md b/pinning/remote/client/openapi/docs/Error.md index e88553965..762903943 100644 --- a/pinning/remote/client/openapi/docs/Error.md +++ b/pinning/remote/client/openapi/docs/Error.md @@ -4,14 +4,13 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**Code** | **int32** | | -**Message** | **string** | | +**Error** | [**ErrorError**](Error_error.md) | | ## Methods ### NewError -`func NewError(code int32, message string, ) *Error` +`func NewError(error_ ErrorError, ) *Error` NewError instantiates a new Error object This constructor will assign default values to properties that have it defined, @@ -26,44 +25,24 @@ NewErrorWithDefaults instantiates a new Error object This constructor will only assign default values to properties that have it defined, but it doesn't guarantee that properties required by API are set -### GetCode +### GetError -`func (o *Error) GetCode() int32` +`func (o *Error) GetError() ErrorError` -GetCode returns the Code field if non-nil, zero value otherwise. +GetError returns the Error field if non-nil, zero value otherwise. -### GetCodeOk +### GetErrorOk -`func (o *Error) GetCodeOk() (*int32, bool)` +`func (o *Error) GetErrorOk() (*ErrorError, bool)` -GetCodeOk returns a tuple with the Code field if it's non-nil, zero value otherwise +GetErrorOk returns a tuple with the Error field if it's non-nil, zero value otherwise and a boolean to check if the value has been set. -### SetCode +### SetError -`func (o *Error) SetCode(v int32)` +`func (o *Error) SetError(v ErrorError)` -SetCode sets Code field to given value. - - -### GetMessage - -`func (o *Error) GetMessage() string` - -GetMessage returns the Message field if non-nil, zero value otherwise. - -### GetMessageOk - -`func (o *Error) GetMessageOk() (*string, bool)` - -GetMessageOk returns a tuple with the Message field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetMessage - -`func (o *Error) SetMessage(v string)` - -SetMessage sets Message field to given value. +SetError sets Error field to given value. diff --git a/pinning/remote/client/openapi/docs/ErrorError.md b/pinning/remote/client/openapi/docs/ErrorError.md new file mode 100644 index 000000000..e44d63829 --- /dev/null +++ b/pinning/remote/client/openapi/docs/ErrorError.md @@ -0,0 +1,77 @@ +# ErrorError + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Reason** | **string** | Mandatory string identifying the type of error | +**Details** | Pointer to **string** | Optional, longer description of the error; may include UUID of transaction for support, links to documentation etc | [optional] + +## Methods + +### NewErrorError + +`func NewErrorError(reason string, ) *ErrorError` + +NewErrorError instantiates a new ErrorError object +This constructor will assign default values to properties that have it defined, +and makes sure properties required by API are set, but the set of arguments +will change when the set of required properties is changed + +### NewErrorErrorWithDefaults + +`func NewErrorErrorWithDefaults() *ErrorError` + +NewErrorErrorWithDefaults instantiates a new ErrorError object +This constructor will only assign default values to properties that have it defined, +but it doesn't guarantee that properties required by API are set + +### GetReason + +`func (o *ErrorError) GetReason() string` + +GetReason returns the Reason field if non-nil, zero value otherwise. + +### GetReasonOk + +`func (o *ErrorError) GetReasonOk() (*string, bool)` + +GetReasonOk returns a tuple with the Reason field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetReason + +`func (o *ErrorError) SetReason(v string)` + +SetReason sets Reason field to given value. + + +### GetDetails + +`func (o *ErrorError) GetDetails() string` + +GetDetails returns the Details field if non-nil, zero value otherwise. + +### GetDetailsOk + +`func (o *ErrorError) GetDetailsOk() (*string, bool)` + +GetDetailsOk returns a tuple with the Details field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetDetails + +`func (o *ErrorError) SetDetails(v string)` + +SetDetails sets Details field to given value. + +### HasDetails + +`func (o *ErrorError) HasDetails() bool` + +HasDetails returns a boolean if a field has been set. + + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/pinning/remote/client/openapi/docs/Pin.md b/pinning/remote/client/openapi/docs/Pin.md index cbdf1a0b7..e5d3e0f18 100644 --- a/pinning/remote/client/openapi/docs/Pin.md +++ b/pinning/remote/client/openapi/docs/Pin.md @@ -4,7 +4,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**Cid** | **string** | CID to be pinned recursively | +**Cid** | **string** | Content Identifier (CID) to be pinned recursively | **Name** | Pointer to **string** | Optional name for pinned data; can be used for lookups later | [optional] **Origins** | Pointer to **[]string** | Optional list of multiaddrs known to provide the data | [optional] **Meta** | Pointer to **map[string]string** | Optional metadata for pin object | [optional] diff --git a/pinning/remote/client/openapi/docs/PinStatus.md b/pinning/remote/client/openapi/docs/PinStatus.md index 2408abfda..40ae992ab 100644 --- a/pinning/remote/client/openapi/docs/PinStatus.md +++ b/pinning/remote/client/openapi/docs/PinStatus.md @@ -4,7 +4,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**Id** | **string** | Globally unique ID of the pin request; can be used to check the status of ongoing pinning, modification of pin object, or pin removal | +**Requestid** | **string** | Globally unique identifier of the pin request; can be used to check the status of ongoing pinning, or pin removal | **Status** | [**Status**](Status.md) | | **Created** | [**time.Time**](time.Time.md) | Immutable timestamp indicating when a pin request entered a pinning service; can be used for filtering results and pagination | **Pin** | [**Pin**](Pin.md) | | @@ -15,7 +15,7 @@ Name | Type | Description | Notes ### NewPinStatus -`func NewPinStatus(id string, status Status, created time.Time, pin Pin, delegates []string, ) *PinStatus` +`func NewPinStatus(requestid string, status Status, created time.Time, pin Pin, delegates []string, ) *PinStatus` NewPinStatus instantiates a new PinStatus object This constructor will assign default values to properties that have it defined, @@ -30,24 +30,24 @@ NewPinStatusWithDefaults instantiates a new PinStatus object This constructor will only assign default values to properties that have it defined, but it doesn't guarantee that properties required by API are set -### GetId +### GetRequestid -`func (o *PinStatus) GetId() string` +`func (o *PinStatus) GetRequestid() string` -GetId returns the Id field if non-nil, zero value otherwise. +GetRequestid returns the Requestid field if non-nil, zero value otherwise. -### GetIdOk +### GetRequestidOk -`func (o *PinStatus) GetIdOk() (*string, bool)` +`func (o *PinStatus) GetRequestidOk() (*string, bool)` -GetIdOk returns a tuple with the Id field if it's non-nil, zero value otherwise +GetRequestidOk returns a tuple with the Requestid field if it's non-nil, zero value otherwise and a boolean to check if the value has been set. -### SetId +### SetRequestid -`func (o *PinStatus) SetId(v string)` +`func (o *PinStatus) SetRequestid(v string)` -SetId sets Id field to given value. +SetRequestid sets Requestid field to given value. ### GetStatus diff --git a/pinning/remote/client/openapi/docs/PinsApi.md b/pinning/remote/client/openapi/docs/PinsApi.md index 0aea62d99..00cff667b 100644 --- a/pinning/remote/client/openapi/docs/PinsApi.md +++ b/pinning/remote/client/openapi/docs/PinsApi.md @@ -5,10 +5,10 @@ All URIs are relative to *https://pinning-service.example.com* Method | HTTP request | Description ------------- | ------------- | ------------- [**PinsGet**](PinsApi.md#PinsGet) | **Get** /pins | List pin objects -[**PinsIdDelete**](PinsApi.md#PinsIdDelete) | **Delete** /pins/{id} | Remove pin object -[**PinsIdGet**](PinsApi.md#PinsIdGet) | **Get** /pins/{id} | Get pin object -[**PinsIdPost**](PinsApi.md#PinsIdPost) | **Post** /pins/{id} | Modify pin object [**PinsPost**](PinsApi.md#PinsPost) | **Post** /pins | Add pin object +[**PinsRequestidDelete**](PinsApi.md#PinsRequestidDelete) | **Delete** /pins/{requestid} | Remove pin object +[**PinsRequestidGet**](PinsApi.md#PinsRequestidGet) | **Get** /pins/{requestid} | Get pin object +[**PinsRequestidPost**](PinsApi.md#PinsRequestidPost) | **Post** /pins/{requestid} | Replace pin object @@ -33,8 +33,8 @@ import ( ) func main() { - cid := []string{"Inner_example"} // []string | Return pin objects responsible for pinning the specified CID(s) (optional) - name := "name_example" // string | Return pin objects with names that contain provided value (partial or full match) (optional) + cid := []string{"Inner_example"} // []string | Return pin objects responsible for pinning the specified CID(s); be aware that using longer hash functions introduces further constraints on the number of CIDs that will fit under the limit of 2000 characters per URL in browser contexts (optional) + name := "name_example" // string | Return pin objects with names that contain provided value (case-insensitive, partial or full match) (optional) status := []Status{openapiclient.Status{}} // []Status | Return pin objects for pins with the specified status (optional) before := Get-Date // time.Time | Return results created (queued) before provided timestamp (optional) after := Get-Date // time.Time | Return results created (queued) after provided timestamp (optional) @@ -64,8 +64,8 @@ Other parameters are passed through a pointer to a apiPinsGetRequest struct via Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- - **cid** | [**[]string**](string.md) | Return pin objects responsible for pinning the specified CID(s) | - **name** | **string** | Return pin objects with names that contain provided value (partial or full match) | + **cid** | [**[]string**](string.md) | Return pin objects responsible for pinning the specified CID(s); be aware that using longer hash functions introduces further constraints on the number of CIDs that will fit under the limit of 2000 characters per URL in browser contexts | + **name** | **string** | Return pin objects with names that contain provided value (case-insensitive, partial or full match) | **status** | [**[]Status**](Status.md) | Return pin objects for pins with the specified status | **before** | **time.Time** | Return results created (queued) before provided timestamp | **after** | **time.Time** | Return results created (queued) after provided timestamp | @@ -90,11 +90,11 @@ Name | Type | Description | Notes [[Back to README]](../README.md) -## PinsIdDelete +## PinsPost -> PinsIdDelete(ctx, id).Execute() +> PinStatus PinsPost(ctx).Pin(pin).Execute() -Remove pin object +Add pin object @@ -111,38 +111,36 @@ import ( ) func main() { - id := "id_example" // string | + pin := openapiclient.Pin{Cid: "Cid_example", Name: "Name_example", Origins: []string{"Origins_example"), Meta: map[string]string{ "Key" = "Value" }} // Pin | configuration := openapiclient.NewConfiguration() api_client := openapiclient.NewAPIClient(configuration) - resp, r, err := api_client.PinsApi.PinsIdDelete(context.Background(), id).Execute() + resp, r, err := api_client.PinsApi.PinsPost(context.Background(), pin).Execute() if err != nil { - fmt.Fprintf(os.Stderr, "Error when calling `PinsApi.PinsIdDelete``: %v\n", err) + fmt.Fprintf(os.Stderr, "Error when calling `PinsApi.PinsPost``: %v\n", err) fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) } + // response from `PinsPost`: PinStatus + fmt.Fprintf(os.Stdout, "Response from `PinsApi.PinsPost`: %v\n", resp) } ``` ### Path Parameters -Name | Type | Description | Notes -------------- | ------------- | ------------- | ------------- -**ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. -**id** | **string** | | ### Other Parameters -Other parameters are passed through a pointer to a apiPinsIdDeleteRequest struct via the builder pattern +Other parameters are passed through a pointer to a apiPinsPostRequest struct via the builder pattern Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- - + **pin** | [**Pin**](Pin.md) | | ### Return type - (empty response body) +[**PinStatus**](PinStatus.md) ### Authorization @@ -150,7 +148,7 @@ Name | Type | Description | Notes ### HTTP request headers -- **Content-Type**: Not defined +- **Content-Type**: application/json - **Accept**: application/json [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) @@ -158,11 +156,11 @@ Name | Type | Description | Notes [[Back to README]](../README.md) -## PinsIdGet +## PinsRequestidDelete -> PinStatus PinsIdGet(ctx, id).Execute() +> PinsRequestidDelete(ctx, requestid).Execute() -Get pin object +Remove pin object @@ -179,17 +177,15 @@ import ( ) func main() { - id := "id_example" // string | + requestid := "requestid_example" // string | configuration := openapiclient.NewConfiguration() api_client := openapiclient.NewAPIClient(configuration) - resp, r, err := api_client.PinsApi.PinsIdGet(context.Background(), id).Execute() + resp, r, err := api_client.PinsApi.PinsRequestidDelete(context.Background(), requestid).Execute() if err != nil { - fmt.Fprintf(os.Stderr, "Error when calling `PinsApi.PinsIdGet``: %v\n", err) + fmt.Fprintf(os.Stderr, "Error when calling `PinsApi.PinsRequestidDelete``: %v\n", err) fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) } - // response from `PinsIdGet`: PinStatus - fmt.Fprintf(os.Stdout, "Response from `PinsApi.PinsIdGet`: %v\n", resp) } ``` @@ -199,11 +195,11 @@ func main() { Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. -**id** | **string** | | +**requestid** | **string** | | ### Other Parameters -Other parameters are passed through a pointer to a apiPinsIdGetRequest struct via the builder pattern +Other parameters are passed through a pointer to a apiPinsRequestidDeleteRequest struct via the builder pattern Name | Type | Description | Notes @@ -212,7 +208,7 @@ Name | Type | Description | Notes ### Return type -[**PinStatus**](PinStatus.md) + (empty response body) ### Authorization @@ -228,11 +224,11 @@ Name | Type | Description | Notes [[Back to README]](../README.md) -## PinsIdPost +## PinsRequestidGet -> PinStatus PinsIdPost(ctx, id).Pin(pin).Execute() +> PinStatus PinsRequestidGet(ctx, requestid).Execute() -Modify pin object +Get pin object @@ -249,18 +245,17 @@ import ( ) func main() { - id := "id_example" // string | - pin := openapiclient.Pin{Cid: "Cid_example", Name: "Name_example", Origins: []string{"Origins_example"), Meta: map[string]string{ "Key" = "Value" }} // Pin | + requestid := "requestid_example" // string | configuration := openapiclient.NewConfiguration() api_client := openapiclient.NewAPIClient(configuration) - resp, r, err := api_client.PinsApi.PinsIdPost(context.Background(), id, pin).Execute() + resp, r, err := api_client.PinsApi.PinsRequestidGet(context.Background(), requestid).Execute() if err != nil { - fmt.Fprintf(os.Stderr, "Error when calling `PinsApi.PinsIdPost``: %v\n", err) + fmt.Fprintf(os.Stderr, "Error when calling `PinsApi.PinsRequestidGet``: %v\n", err) fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) } - // response from `PinsIdPost`: PinStatus - fmt.Fprintf(os.Stdout, "Response from `PinsApi.PinsIdPost`: %v\n", resp) + // response from `PinsRequestidGet`: PinStatus + fmt.Fprintf(os.Stdout, "Response from `PinsApi.PinsRequestidGet`: %v\n", resp) } ``` @@ -270,17 +265,16 @@ func main() { Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. -**id** | **string** | | +**requestid** | **string** | | ### Other Parameters -Other parameters are passed through a pointer to a apiPinsIdPostRequest struct via the builder pattern +Other parameters are passed through a pointer to a apiPinsRequestidGetRequest struct via the builder pattern Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- - **pin** | [**Pin**](Pin.md) | | ### Return type @@ -292,7 +286,7 @@ Name | Type | Description | Notes ### HTTP request headers -- **Content-Type**: application/json +- **Content-Type**: Not defined - **Accept**: application/json [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) @@ -300,11 +294,11 @@ Name | Type | Description | Notes [[Back to README]](../README.md) -## PinsPost +## PinsRequestidPost -> PinStatus PinsPost(ctx).Pin(pin).Execute() +> PinStatus PinsRequestidPost(ctx, requestid).Pin(pin).Execute() -Add pin object +Replace pin object @@ -321,31 +315,37 @@ import ( ) func main() { + requestid := "requestid_example" // string | pin := openapiclient.Pin{Cid: "Cid_example", Name: "Name_example", Origins: []string{"Origins_example"), Meta: map[string]string{ "Key" = "Value" }} // Pin | configuration := openapiclient.NewConfiguration() api_client := openapiclient.NewAPIClient(configuration) - resp, r, err := api_client.PinsApi.PinsPost(context.Background(), pin).Execute() + resp, r, err := api_client.PinsApi.PinsRequestidPost(context.Background(), requestid, pin).Execute() if err != nil { - fmt.Fprintf(os.Stderr, "Error when calling `PinsApi.PinsPost``: %v\n", err) + fmt.Fprintf(os.Stderr, "Error when calling `PinsApi.PinsRequestidPost``: %v\n", err) fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) } - // response from `PinsPost`: PinStatus - fmt.Fprintf(os.Stdout, "Response from `PinsApi.PinsPost`: %v\n", resp) + // response from `PinsRequestidPost`: PinStatus + fmt.Fprintf(os.Stdout, "Response from `PinsApi.PinsRequestidPost`: %v\n", resp) } ``` ### Path Parameters +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- +**ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. +**requestid** | **string** | | ### Other Parameters -Other parameters are passed through a pointer to a apiPinsPostRequest struct via the builder pattern +Other parameters are passed through a pointer to a apiPinsRequestidPostRequest struct via the builder pattern Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- + **pin** | [**Pin**](Pin.md) | | ### Return type diff --git a/pinning/remote/client/openapi/model_error.go b/pinning/remote/client/openapi/model_error.go index d97ecaa2c..ab308de3d 100644 --- a/pinning/remote/client/openapi/model_error.go +++ b/pinning/remote/client/openapi/model_error.go @@ -1,7 +1,7 @@ /* * IPFS Pinning Service API * - * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * * API version: 0.0.5 * Generated by: OpenAPI Generator (https://openapi-generator.tech) @@ -13,20 +13,18 @@ import ( "encoding/json" ) -// Error Base error object +// Error Error object type Error struct { - Code int32 `json:"code"` - Message string `json:"message"` + Error ErrorError `json:"error"` } // NewError instantiates a new Error object // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewError(code int32, message string) *Error { +func NewError(error_ ErrorError) *Error { this := Error{} - this.Code = code - this.Message = message + this.Error = error_ return &this } @@ -38,61 +36,34 @@ func NewErrorWithDefaults() *Error { return &this } -// GetCode returns the Code field value -func (o *Error) GetCode() int32 { +// GetError returns the Error field value +func (o *Error) GetError() ErrorError { if o == nil { - var ret int32 + var ret ErrorError return ret } - return o.Code + return o.Error } -// GetCodeOk returns a tuple with the Code field value +// GetErrorOk returns a tuple with the Error field value // and a boolean to check if the value has been set. -func (o *Error) GetCodeOk() (*int32, bool) { +func (o *Error) GetErrorOk() (*ErrorError, bool) { if o == nil { return nil, false } - return &o.Code, true + return &o.Error, true } -// SetCode sets field value -func (o *Error) SetCode(v int32) { - o.Code = v -} - -// GetMessage returns the Message field value -func (o *Error) GetMessage() string { - if o == nil { - var ret string - return ret - } - - return o.Message -} - -// GetMessageOk returns a tuple with the Message field value -// and a boolean to check if the value has been set. -func (o *Error) GetMessageOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Message, true -} - -// SetMessage sets field value -func (o *Error) SetMessage(v string) { - o.Message = v +// SetError sets field value +func (o *Error) SetError(v ErrorError) { + o.Error = v } func (o Error) MarshalJSON() ([]byte, error) { toSerialize := map[string]interface{}{} if true { - toSerialize["code"] = o.Code - } - if true { - toSerialize["message"] = o.Message + toSerialize["error"] = o.Error } return json.Marshal(toSerialize) } diff --git a/pinning/remote/client/openapi/model_error_error.go b/pinning/remote/client/openapi/model_error_error.go new file mode 100644 index 000000000..0a177dbd2 --- /dev/null +++ b/pinning/remote/client/openapi/model_error_error.go @@ -0,0 +1,143 @@ +/* + * IPFS Pinning Service API + * + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * + * API version: 0.0.5 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package openapi + +import ( + "encoding/json" +) + +// ErrorError struct for ErrorError +type ErrorError struct { + // Mandatory string identifying the type of error + Reason string `json:"reason"` + // Optional, longer description of the error; may include UUID of transaction for support, links to documentation etc + Details *string `json:"details,omitempty"` +} + +// NewErrorError instantiates a new ErrorError object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewErrorError(reason string) *ErrorError { + this := ErrorError{} + this.Reason = reason + return &this +} + +// NewErrorErrorWithDefaults instantiates a new ErrorError object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewErrorErrorWithDefaults() *ErrorError { + this := ErrorError{} + return &this +} + +// GetReason returns the Reason field value +func (o *ErrorError) GetReason() string { + if o == nil { + var ret string + return ret + } + + return o.Reason +} + +// GetReasonOk returns a tuple with the Reason field value +// and a boolean to check if the value has been set. +func (o *ErrorError) GetReasonOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Reason, true +} + +// SetReason sets field value +func (o *ErrorError) SetReason(v string) { + o.Reason = v +} + +// GetDetails returns the Details field value if set, zero value otherwise. +func (o *ErrorError) GetDetails() string { + if o == nil || o.Details == nil { + var ret string + return ret + } + return *o.Details +} + +// GetDetailsOk returns a tuple with the Details field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ErrorError) GetDetailsOk() (*string, bool) { + if o == nil || o.Details == nil { + return nil, false + } + return o.Details, true +} + +// HasDetails returns a boolean if a field has been set. +func (o *ErrorError) HasDetails() bool { + if o != nil && o.Details != nil { + return true + } + + return false +} + +// SetDetails gets a reference to the given string and assigns it to the Details field. +func (o *ErrorError) SetDetails(v string) { + o.Details = &v +} + +func (o ErrorError) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["reason"] = o.Reason + } + if o.Details != nil { + toSerialize["details"] = o.Details + } + return json.Marshal(toSerialize) +} + +type NullableErrorError struct { + value *ErrorError + isSet bool +} + +func (v NullableErrorError) Get() *ErrorError { + return v.value +} + +func (v *NullableErrorError) Set(val *ErrorError) { + v.value = val + v.isSet = true +} + +func (v NullableErrorError) IsSet() bool { + return v.isSet +} + +func (v *NullableErrorError) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableErrorError(val *ErrorError) *NullableErrorError { + return &NullableErrorError{value: val, isSet: true} +} + +func (v NullableErrorError) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableErrorError) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/pinning/remote/client/openapi/model_pin.go b/pinning/remote/client/openapi/model_pin.go index c4a5e8015..31ae0ca86 100644 --- a/pinning/remote/client/openapi/model_pin.go +++ b/pinning/remote/client/openapi/model_pin.go @@ -1,7 +1,7 @@ /* * IPFS Pinning Service API * - * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * * API version: 0.0.5 * Generated by: OpenAPI Generator (https://openapi-generator.tech) @@ -15,7 +15,7 @@ import ( // Pin Pin object type Pin struct { - // CID to be pinned recursively + // Content Identifier (CID) to be pinned recursively Cid string `json:"cid"` // Optional name for pinned data; can be used for lookups later Name *string `json:"name,omitempty"` diff --git a/pinning/remote/client/openapi/model_pin_results.go b/pinning/remote/client/openapi/model_pin_results.go index ac8443976..b569fe5df 100644 --- a/pinning/remote/client/openapi/model_pin_results.go +++ b/pinning/remote/client/openapi/model_pin_results.go @@ -1,7 +1,7 @@ /* * IPFS Pinning Service API * - * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * * API version: 0.0.5 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/pinning/remote/client/openapi/model_pin_status.go b/pinning/remote/client/openapi/model_pin_status.go index 78f37dcd2..21d7992b2 100644 --- a/pinning/remote/client/openapi/model_pin_status.go +++ b/pinning/remote/client/openapi/model_pin_status.go @@ -1,7 +1,7 @@ /* * IPFS Pinning Service API * - * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * * API version: 0.0.5 * Generated by: OpenAPI Generator (https://openapi-generator.tech) @@ -16,9 +16,9 @@ import ( // PinStatus Pin object with status type PinStatus struct { - // Globally unique ID of the pin request; can be used to check the status of ongoing pinning, modification of pin object, or pin removal - Id string `json:"id"` - Status Status `json:"status"` + // Globally unique identifier of the pin request; can be used to check the status of ongoing pinning, or pin removal + Requestid string `json:"requestid"` + Status Status `json:"status"` // Immutable timestamp indicating when a pin request entered a pinning service; can be used for filtering results and pagination Created time.Time `json:"created"` Pin Pin `json:"pin"` @@ -32,9 +32,9 @@ type PinStatus struct { // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewPinStatus(id string, status Status, created time.Time, pin Pin, delegates []string) *PinStatus { +func NewPinStatus(requestid string, status Status, created time.Time, pin Pin, delegates []string) *PinStatus { this := PinStatus{} - this.Id = id + this.Requestid = requestid this.Status = status this.Created = created this.Pin = pin @@ -50,28 +50,28 @@ func NewPinStatusWithDefaults() *PinStatus { return &this } -// GetId returns the Id field value -func (o *PinStatus) GetId() string { +// GetRequestid returns the Requestid field value +func (o *PinStatus) GetRequestid() string { if o == nil { var ret string return ret } - return o.Id + return o.Requestid } -// GetIdOk returns a tuple with the Id field value +// GetRequestidOk returns a tuple with the Requestid field value // and a boolean to check if the value has been set. -func (o *PinStatus) GetIdOk() (*string, bool) { +func (o *PinStatus) GetRequestidOk() (*string, bool) { if o == nil { return nil, false } - return &o.Id, true + return &o.Requestid, true } -// SetId sets field value -func (o *PinStatus) SetId(v string) { - o.Id = v +// SetRequestid sets field value +func (o *PinStatus) SetRequestid(v string) { + o.Requestid = v } // GetStatus returns the Status field value @@ -205,7 +205,7 @@ func (o *PinStatus) SetInfo(v map[string]string) { func (o PinStatus) MarshalJSON() ([]byte, error) { toSerialize := map[string]interface{}{} if true { - toSerialize["id"] = o.Id + toSerialize["requestid"] = o.Requestid } if true { toSerialize["status"] = o.Status diff --git a/pinning/remote/client/openapi/model_status.go b/pinning/remote/client/openapi/model_status.go index fe727407e..7b30371ba 100644 --- a/pinning/remote/client/openapi/model_status.go +++ b/pinning/remote/client/openapi/model_status.go @@ -1,7 +1,7 @@ /* * IPFS Pinning Service API * - * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * * API version: 0.0.5 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/pinning/remote/client/openapi/response.go b/pinning/remote/client/openapi/response.go index d053bb91f..5f1b37456 100644 --- a/pinning/remote/client/openapi/response.go +++ b/pinning/remote/client/openapi/response.go @@ -1,7 +1,7 @@ /* * IPFS Pinning Service API * - * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * * API version: 0.0.5 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/pinning/remote/client/openapi/utils.go b/pinning/remote/client/openapi/utils.go index a5daa9247..976239127 100644 --- a/pinning/remote/client/openapi/utils.go +++ b/pinning/remote/client/openapi/utils.go @@ -1,7 +1,7 @@ /* * IPFS Pinning Service API * - * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Objects ### Pin object ![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin. ### Modifying an existing pin object The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{id}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. + * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * * API version: 0.0.5 * Generated by: OpenAPI Generator (https://openapi-generator.tech) From 941227de7a1b2be5605d1c54d6686c780cda686e Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Thu, 15 Oct 2020 15:23:19 -0400 Subject: [PATCH 3268/3817] docs: Update README with codegen instructions This commit was moved from ipfs/go-pinning-service-http-client@df65dc336ff0a790d84cb61487edad99886913c8 --- pinning/remote/client/README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/pinning/remote/client/README.md b/pinning/remote/client/README.md index 213f0df83..0326b556c 100644 --- a/pinning/remote/client/README.md +++ b/pinning/remote/client/README.md @@ -13,10 +13,27 @@ An IPFS Pinning Service HTTP Client [Adin Schmahmann](https://github.com/aschmahmann) +## Updating Pinning Service Spec + +Download the openapi-generator from https://github.com/OpenAPITools/openapi-generator and generate the code using: + +Current code generated with: openapi-generator 5.0.0-beta + +``` +openapi-generator generate -g go-experimental -i https://raw.githubusercontent.com/ipfs/pinning-services-api-spec/master/ipfs-pinning-service.yaml -o openapi +rm openapi/go.mod openapi/go.sum +``` + +Notes: +Due to https://github.com/OpenAPITools/openapi-generator/issues/7473 the code generator the http error codes processing +may need some manual editing. + +`go-experimental` is becoming mainstream and so in later versions will be replaced with `go` + ## Contributing Contributions are welcome! This repository is part of the IPFS project and therefore governed by our [contributing guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md). ## License -[SPDX-License-Identifier: Apache-2.0 OR MIT](LICENSE.md) \ No newline at end of file +[SPDX-License-Identifier: Apache-2.0 OR MIT](LICENSE.md) From a663575e700bef8b585d2fac0e43977e9c367375 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 5 Nov 2020 17:07:39 +1100 Subject: [PATCH 3269/3817] fix: main NewReader call This commit was moved from ipld/go-car@b8a3c261e6ea3844fb7256de666baf0f7eba6a6c --- ipld/car/car/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/car/car/main.go b/ipld/car/car/main.go index c88459ade..8ccd4e5d8 100644 --- a/ipld/car/car/main.go +++ b/ipld/car/car/main.go @@ -26,7 +26,7 @@ var headerCmd = cli.Command{ } defer fi.Close() - ch, err := car.ReadHeader(bufio.NewReader(fi)) + ch, _, err := car.ReadHeader(bufio.NewReader(fi)) if err != nil { return err } From d76751b039b3ac87e945d952bd43f6c47e655e06 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 5 Nov 2020 17:08:20 +1100 Subject: [PATCH 3270/3817] feat: handle mid-varint EOF case as UnexpectedEOF This commit was moved from ipld/go-car@c2f1ff261ecf16720deb8e5ee9f772bd51c0e477 --- ipld/car/car_test.go | 65 +++++++++++++++++++++++++++++++++++++++++++ ipld/car/util/util.go | 7 +++++ 2 files changed, 72 insertions(+) diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 53d59b365..e1e842402 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -3,6 +3,8 @@ package car import ( "bytes" "context" + "encoding/hex" + "io" "testing" cid "github.com/ipfs/go-cid" @@ -160,3 +162,66 @@ func TestRoundtripSelective(t *testing.T) { require.False(t, has) } } + +func TestEOFHandling(t *testing.T) { + // fixture is a clean single-block, single-root CAR + fixture, err := hex.DecodeString("3aa265726f6f747381d82a58250001711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80b6776657273696f6e012c01711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80ba165646f646779f5") + if err != nil { + t.Fatal(err) + } + + load := func(t *testing.T, byts []byte) *CarReader { + cr, err := NewCarReader(bytes.NewReader(byts)) + if err != nil { + t.Fatal(err) + } + + blk, err := cr.Next() + if err != nil { + t.Fatal(err) + } + if blk.Cid().String() != "bafyreiavd7u6opdcm6tqmddpnrgmvfb4enxuwglhenejmchnwqvixd5ibm" { + t.Fatal("unexpected CID") + } + + return cr + } + + t.Run("CleanEOF", func(t *testing.T) { + cr := load(t, fixture) + + blk, err := cr.Next() + if err != io.EOF { + t.Fatal("Didn't get expected EOF") + } + if blk != nil { + t.Fatal("EOF returned expected block") + } + }) + + t.Run("BadVarint", func(t *testing.T) { + fixtureBadVarint := append(fixture, 160) + cr := load(t, fixtureBadVarint) + + blk, err := cr.Next() + if err != io.ErrUnexpectedEOF { + t.Fatal("Didn't get unexpected EOF") + } + if blk != nil { + t.Fatal("EOF returned unexpected block") + } + }) + + t.Run("TruncatedBlock", func(t *testing.T) { + fixtureTruncatedBlock := append(fixture, 100, 0, 0) + cr := load(t, fixtureTruncatedBlock) + + blk, err := cr.Next() + if err != io.ErrUnexpectedEOF { + t.Fatal("Didn't get unexpected EOF") + } + if blk != nil { + t.Fatal("EOF returned unexpected block") + } + }) +} diff --git a/ipld/car/util/util.go b/ipld/car/util/util.go index 47c7e78d6..08048f333 100644 --- a/ipld/car/util/util.go +++ b/ipld/car/util/util.go @@ -100,8 +100,15 @@ func LdSize(d ...[]byte) uint64 { } func LdRead(r *bufio.Reader) ([]byte, error) { + if _, err := r.Peek(1); err != nil { // no more blocks, likely clean io.EOF + return nil, err + } + l, err := binary.ReadUvarint(r) if err != nil { + if err == io.EOF { + return nil, io.ErrUnexpectedEOF // don't silently pretend this is a clean EOF + } return nil, err } From 43fa6b3918353959b0e2574961797fff729a16e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Tue, 10 Nov 2020 16:28:56 +0000 Subject: [PATCH 3271/3817] add View() to all the various blockstores. (#59) This commit was moved from ipfs/go-ipfs-blockstore@3e8fd89307c11636913e21e3643ce930f39bb6c6 --- blockstore/arc_cache.go | 121 +++++++++++++++++++++++++------------- blockstore/blockstore.go | 15 +++++ blockstore/bloom_cache.go | 22 +++++++ blockstore/idstore.go | 27 ++++++++- 4 files changed, 143 insertions(+), 42 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index e2b930dca..1e497abf9 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -12,35 +12,42 @@ import ( type cacheHave bool type cacheSize int -// arccache wraps a BlockStore with an Adaptive Replacement Cache (ARC) for -// block Cids. This provides block access-time improvements, allowing -// to short-cut many searches without query-ing the underlying datastore. +// arccache wraps a BlockStore with an Adaptive Replacement Cache (ARC) that +// does not store the actual blocks, just metadata about them: existence and +// size. This provides block access-time improvements, allowing +// to short-cut many searches without querying the underlying datastore. type arccache struct { - arc *lru.TwoQueueCache + cache *lru.TwoQueueCache blockstore Blockstore + viewer Viewer hits metrics.Counter total metrics.Counter } +var _ Blockstore = (*arccache)(nil) +var _ Viewer = (*arccache)(nil) + func newARCCachedBS(ctx context.Context, bs Blockstore, lruSize int) (*arccache, error) { - arc, err := lru.New2Q(lruSize) + cache, err := lru.New2Q(lruSize) if err != nil { return nil, err } - c := &arccache{arc: arc, blockstore: bs} + c := &arccache{cache: cache, blockstore: bs} c.hits = metrics.NewCtx(ctx, "arc.hits_total", "Number of ARC cache hits").Counter() c.total = metrics.NewCtx(ctx, "arc_total", "Total number of ARC cache requests").Counter() - + if v, ok := bs.(Viewer); ok { + c.viewer = v + } return c, nil } func (b *arccache) DeleteBlock(k cid.Cid) error { - if has, _, ok := b.hasCached(k); ok && !has { + if has, _, ok := b.queryCache(k); ok && !has { return nil } - b.arc.Remove(k) // Invalidate cache before deleting. + b.cache.Remove(k) // Invalidate cache before deleting. err := b.blockstore.DeleteBlock(k) if err == nil { b.cacheHave(k, false) @@ -48,32 +55,8 @@ func (b *arccache) DeleteBlock(k cid.Cid) error { return err } -// if ok == false has is inconclusive -// if ok == true then has respons to question: is it contained -func (b *arccache) hasCached(k cid.Cid) (has bool, size int, ok bool) { - b.total.Inc() - if !k.Defined() { - log.Error("undefined cid in arccache") - // Return cache invalid so the call to blockstore happens - // in case of invalid key and correct error is created. - return false, -1, false - } - - h, ok := b.arc.Get(string(k.Hash())) - if ok { - b.hits.Inc() - switch h := h.(type) { - case cacheHave: - return bool(h), -1, true - case cacheSize: - return true, int(h), true - } - } - return false, -1, false -} - func (b *arccache) Has(k cid.Cid) (bool, error) { - if has, _, ok := b.hasCached(k); ok { + if has, _, ok := b.queryCache(k); ok { return has, nil } has, err := b.blockstore.Has(k) @@ -85,7 +68,7 @@ func (b *arccache) Has(k cid.Cid) (bool, error) { } func (b *arccache) GetSize(k cid.Cid) (int, error) { - if has, blockSize, ok := b.hasCached(k); ok { + if has, blockSize, ok := b.queryCache(k); ok { if !has { // don't have it, return return -1, ErrNotFound @@ -105,13 +88,38 @@ func (b *arccache) GetSize(k cid.Cid) (int, error) { return blockSize, err } +func (b *arccache) View(k cid.Cid, callback func([]byte) error) error { + // shortcircuit and fall back to Get if the underlying store + // doesn't support Viewer. + if b.viewer == nil { + blk, err := b.Get(k) + if err != nil { + return err + } + return callback(blk.RawData()) + } + + if !k.Defined() { + log.Error("undefined cid in arc cache") + return ErrNotFound + } + + if has, _, ok := b.queryCache(k); ok && !has { + // short circuit if the cache deterministically tells us the item + // doesn't exist. + return ErrNotFound + } + + return b.viewer.View(k, callback) +} + func (b *arccache) Get(k cid.Cid) (blocks.Block, error) { if !k.Defined() { log.Error("undefined cid in arc cache") return nil, ErrNotFound } - if has, _, ok := b.hasCached(k); ok && !has { + if has, _, ok := b.queryCache(k); ok && !has { return nil, ErrNotFound } @@ -125,7 +133,7 @@ func (b *arccache) Get(k cid.Cid) (blocks.Block, error) { } func (b *arccache) Put(bl blocks.Block) error { - if has, _, ok := b.hasCached(bl.Cid()); ok && has { + if has, _, ok := b.queryCache(bl.Cid()); ok && has { return nil } @@ -141,7 +149,7 @@ func (b *arccache) PutMany(bs []blocks.Block) error { for _, block := range bs { // call put on block if result is inconclusive or we are sure that // the block isn't in storage - if has, _, ok := b.hasCached(block.Cid()); !ok || (ok && !has) { + if has, _, ok := b.queryCache(block.Cid()); !ok || (ok && !has) { good = append(good, block) } } @@ -160,11 +168,44 @@ func (b *arccache) HashOnRead(enabled bool) { } func (b *arccache) cacheHave(c cid.Cid, have bool) { - b.arc.Add(string(c.Hash()), cacheHave(have)) + b.cache.Add(string(c.Hash()), cacheHave(have)) } func (b *arccache) cacheSize(c cid.Cid, blockSize int) { - b.arc.Add(string(c.Hash()), cacheSize(blockSize)) + b.cache.Add(string(c.Hash()), cacheSize(blockSize)) +} + +// queryCache checks if the CID is in the cache. If so, it returns: +// +// * exists (bool): whether the CID is known to exist or not. +// * size (int): the size if cached, or -1 if not cached. +// * ok (bool): whether present in the cache. +// +// When ok is false, the answer in inconclusive and the caller must ignore the +// other two return values. Querying the underying store is necessary. +// +// When ok is true, exists carries the correct answer, and size carries the +// size, if known, or -1 if not. +func (b *arccache) queryCache(k cid.Cid) (exists bool, size int, ok bool) { + b.total.Inc() + if !k.Defined() { + log.Error("undefined cid in arccache") + // Return cache invalid so the call to blockstore happens + // in case of invalid key and correct error is created. + return false, -1, false + } + + h, ok := b.cache.Get(string(k.Hash())) + if ok { + b.hits.Inc() + switch h := h.(type) { + case cacheHave: + return bool(h), -1, true + case cacheSize: + return true, int(h), true + } + } + return false, -1, false } func (b *arccache) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index f8eb07a7d..6625a3411 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -57,6 +57,21 @@ type Blockstore interface { HashOnRead(enabled bool) } +// Viewer can be implemented by blockstores that offer zero-copy access to +// values. +// +// Callers of View must not mutate or retain the byte slice, as it could be +// an mmapped memory region, or a pooled byte buffer. +// +// View is especially suitable for deserialising in place. +// +// The callback will only be called iff the query operation is successful (and +// the block is found); otherwise, the error will be propagated. Errors returned +// by the callback will be propagated as well. +type Viewer interface { + View(cid cid.Cid, callback func([]byte) error) error +} + // GCLocker abstract functionality to lock a blockstore when performing // garbage-collection operations. type GCLocker interface { diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index b4fadc2ef..da302c97d 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -29,6 +29,9 @@ func bloomCached(ctx context.Context, bs Blockstore, bloomSize, hashCount int) ( "Total number of requests to bloom cache").Counter(), buildChan: make(chan struct{}), } + if v, ok := bs.(Viewer); ok { + bc.viewer = v + } go func() { err := bc.build(ctx) if err != nil { @@ -67,12 +70,16 @@ type bloomcache struct { buildChan chan struct{} blockstore Blockstore + viewer Viewer // Statistics hits metrics.Counter total metrics.Counter } +var _ Blockstore = (*bloomcache)(nil) +var _ Viewer = (*bloomcache)(nil) + func (b *bloomcache) BloomActive() bool { return atomic.LoadInt32(&b.active) != 0 } @@ -151,6 +158,21 @@ func (b *bloomcache) GetSize(k cid.Cid) (int, error) { return b.blockstore.GetSize(k) } +func (b *bloomcache) View(k cid.Cid, callback func([]byte) error) error { + if b.viewer == nil { + blk, err := b.Get(k) + if err != nil { + return err + } + return callback(blk.RawData()) + } + + if has, ok := b.hasCached(k); ok && !has { + return ErrNotFound + } + return b.viewer.View(k, callback) +} + func (b *bloomcache) Get(k cid.Cid) (blocks.Block, error) { if has, ok := b.hasCached(k); ok && !has { return nil, ErrNotFound diff --git a/blockstore/idstore.go b/blockstore/idstore.go index 477da70b2..274c1a3b3 100644 --- a/blockstore/idstore.go +++ b/blockstore/idstore.go @@ -10,11 +10,19 @@ import ( // idstore wraps a BlockStore to add support for identity hashes type idstore struct { - bs Blockstore + bs Blockstore + viewer Viewer } +var _ Blockstore = (*idstore)(nil) +var _ Viewer = (*idstore)(nil) + func NewIdStore(bs Blockstore) Blockstore { - return &idstore{bs} + ids := &idstore{bs: bs} + if v, ok := bs.(Viewer); ok { + ids.viewer = v + } + return ids } func extractContents(k cid.Cid) (bool, []byte) { @@ -46,6 +54,21 @@ func (b *idstore) Has(k cid.Cid) (bool, error) { return b.bs.Has(k) } +func (b *idstore) View(k cid.Cid, callback func([]byte) error) error { + if b.viewer == nil { + blk, err := b.Get(k) + if err != nil { + return err + } + return callback(blk.RawData()) + } + isId, bdata := extractContents(k) + if isId { + return callback(bdata) + } + return b.viewer.View(k, callback) +} + func (b *idstore) GetSize(k cid.Cid) (int, error) { isId, bdata := extractContents(k) if isId { From c473863f1386f9aab5797e46c45ab964817f9f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Mon, 16 Nov 2020 15:17:40 +0000 Subject: [PATCH 3272/3817] make idstore implement io.Closer. (#60) This commit was moved from ipfs/go-ipfs-blockstore@7adc396ab4b40c660c72d215b3a58999b6f63413 --- blockstore/idstore.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/blockstore/idstore.go b/blockstore/idstore.go index 274c1a3b3..1166e5bda 100644 --- a/blockstore/idstore.go +++ b/blockstore/idstore.go @@ -2,6 +2,7 @@ package blockstore import ( "context" + "io" blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" @@ -16,6 +17,7 @@ type idstore struct { var _ Blockstore = (*idstore)(nil) var _ Viewer = (*idstore)(nil) +var _ io.Closer = (*idstore)(nil) func NewIdStore(bs Blockstore) Blockstore { ids := &idstore{bs: bs} @@ -112,3 +114,10 @@ func (b *idstore) HashOnRead(enabled bool) { func (b *idstore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return b.bs.AllKeysChan(ctx) } + +func (b *idstore) Close() error { + if c, ok := b.bs.(io.Closer); ok { + return c.Close() + } + return nil +} From 76030f7606088a461fd69768b3a7e9c86d1445e3 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Tue, 17 Nov 2020 12:20:17 -0800 Subject: [PATCH 3273/3817] feat(car): allow block hooks when using two step write This commit was moved from ipld/go-car@7f9fecd191336b673dda3a7445af9fd5576f4459 --- ipld/car/car_test.go | 11 ++++++++++- ipld/car/selectivecar.go | 28 +++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index e1e842402..1fb87f5f9 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -117,7 +117,9 @@ func TestRoundtripSelective(t *testing.T) { // write car in one step buf := new(bytes.Buffer) blockCount := 0 + var oneStepBlocks []Block err := sc.Write(buf, func(block Block) error { + oneStepBlocks = append(oneStepBlocks, block) blockCount++ return nil }) @@ -128,7 +130,11 @@ func TestRoundtripSelective(t *testing.T) { sc2 := NewSelectiveCar(context.Background(), sourceBs, []Dag{Dag{Root: nd3.Cid(), Selector: selector}}) // write car in two steps - scp, err := sc2.Prepare() + var twoStepBlocks []Block + scp, err := sc2.Prepare(func(block Block) error { + twoStepBlocks = append(twoStepBlocks, block) + return nil + }) require.NoError(t, err) buf2 := new(bytes.Buffer) err = scp.Dump(buf2) @@ -141,6 +147,9 @@ func TestRoundtripSelective(t *testing.T) { // verify equal data written by both methods require.Equal(t, buf.Bytes(), buf2.Bytes()) + // verify equal blocks were passed to user block hook funcs + require.Equal(t, oneStepBlocks, twoStepBlocks) + // readout car and verify contents bserv := dstest.Bserv() ch, err := LoadCar(bserv.Blockstore(), buf) diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index a41623731..007da104c 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -50,9 +50,10 @@ type OnNewCarBlockFunc func(Block) error // the Car file like size and number of blocks that go into it type SelectiveCarPrepared struct { SelectiveCar - size uint64 - header CarHeader - cids []cid.Cid + size uint64 + header CarHeader + cids []cid.Cid + userOnNewCarBlocks []OnNewCarBlockFunc } // NewSelectiveCar creates a new SelectiveCar for the given car file based @@ -72,7 +73,7 @@ func (sc SelectiveCar) traverse(onCarHeader OnCarHeaderFunc, onNewCarBlock OnNew // Prepare traverse a car file and collects data on what is about to be written, but // does not actually write the file -func (sc SelectiveCar) Prepare() (SelectiveCarPrepared, error) { +func (sc SelectiveCar) Prepare(userOnNewCarBlocks ...OnNewCarBlockFunc) (SelectiveCarPrepared, error) { var header CarHeader var cids []cid.Cid @@ -88,7 +89,7 @@ func (sc SelectiveCar) Prepare() (SelectiveCarPrepared, error) { if err != nil { return SelectiveCarPrepared{}, err } - return SelectiveCarPrepared{sc, size, header, cids}, nil + return SelectiveCarPrepared{sc, size, header, cids, userOnNewCarBlocks}, nil } func (sc SelectiveCar) Write(w io.Writer, userOnNewCarBlocks ...OnNewCarBlockFunc) error { @@ -133,6 +134,10 @@ func (sc SelectiveCarPrepared) Cids() []cid.Cid { // Dump writes the car file as quickly as possible based on information already // collected func (sc SelectiveCarPrepared) Dump(w io.Writer) error { + offset, err := HeaderSize(&sc.header) + if err != nil { + return fmt.Errorf("failed to size car header: %s", err) + } if err := WriteHeader(&sc.header, w); err != nil { return fmt.Errorf("failed to write car header: %s", err) } @@ -142,10 +147,23 @@ func (sc SelectiveCarPrepared) Dump(w io.Writer) error { return err } raw := blk.RawData() + size := util.LdSize(c.Bytes(), raw) err = util.LdWrite(w, c.Bytes(), raw) if err != nil { return err } + for _, userOnNewCarBlock := range sc.userOnNewCarBlocks { + err := userOnNewCarBlock(Block{ + BlockCID: c, + Data: raw, + Offset: offset, + Size: size, + }) + if err != nil { + return err + } + } + offset += size } return nil } From 4e02944ed6940d10f7b9e50e8c328e359ef8f191 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Thu, 15 Oct 2020 15:28:18 -0400 Subject: [PATCH 3274/3817] cleaned up client error processing This commit was moved from ipfs/go-pinning-service-http-client@b37b2d435ea52173793aeaf94f40f49e63322d44 --- pinning/remote/client/client.go | 43 ++++++--------------------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/pinning/remote/client/client.go b/pinning/remote/client/client.go index f021abb08..e3c04176e 100644 --- a/pinning/remote/client/client.go +++ b/pinning/remote/client/client.go @@ -1,10 +1,9 @@ package go_pinning_service_http_client import ( - "bytes" "context" - "encoding/json" "fmt" + "github.com/pkg/errors" "net/http" "time" @@ -38,11 +37,6 @@ func NewClient(url, bearerToken string) *Client { return &Client{client: openapi.NewAPIClient(config)} } -func getError(e *openapi.Error) error { - err := e.GetError() - return fmt.Errorf("request error: %s - %s", err.GetReason(), err.GetDetails()) -} - // TODO: We should probably make sure there are no duplicates sent type lsSettings struct { cids []string @@ -374,37 +368,16 @@ func getCIDEncoder() multibase.Encoder { func httperr(resp *http.Response, e error) error { oerr, ok := e.(openapi.GenericOpenAPIError) - if !ok { - panic("wrong error type") - } - var buf bytes.Buffer - var err error - - var reqStr string - if resp.Request.GetBody != nil { - resp.Request.Body, err = resp.Request.GetBody() - if err != nil { - reqStr = err.Error() - } else if err := resp.Request.Write(&buf); err != nil { - reqStr = err.Error() - } else { - reqStr = buf.String() + if ok { + ferr, ok := oerr.Model().(openapi.Failure) + if ok { + return errors.Wrapf(e,"statusCode: %d, reason : %q, details : %q", resp.StatusCode, ferr.Error.GetReason(), ferr.Error.GetDetails()) } - } else { - reqStr = resp.Request.URL.String() } - bodystr := string(oerr.Body()) - //body, err := ioutil.ReadAll(resp.Body) - //var bodystr string - //if err == nil { - // bodystr = string(body) - //} - relevantErr := fmt.Sprintf("{ httpcode: %d, httpresp: %s, httpbody: %s, reqstr: %s }", resp.StatusCode, resp.Status, bodystr, reqStr) - relevantErrBytes, err := json.MarshalIndent(relevantErr, "", "\t") - if err != nil { - return fmt.Errorf("RelevantInfo : %s, MarshalErr: %s, Err: %w", relevantErr, err, e) + if resp == nil { + return errors.Wrapf(e,"empty response from remote pinning service") } - return fmt.Errorf("relevantErr: %s, err: %w", relevantErrBytes, e) + return errors.Wrapf(e, "remote pinning service error. statusCode: %d", resp.StatusCode) } From 380ca9d435e00494b4e54580ab0c98af3f6258f0 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Thu, 15 Oct 2020 15:27:44 -0400 Subject: [PATCH 3275/3817] feat: bumped spec to v0.1.1 This commit was moved from ipfs/go-pinning-service-http-client@3159542e49a2f3caaf401a5556640cc2e625d877 --- pinning/remote/client/openapi/README.md | 6 +- pinning/remote/client/openapi/api_pins.go | 267 +----------------- pinning/remote/client/openapi/client.go | 4 +- .../remote/client/openapi/configuration.go | 2 +- .../openapi/docs/{Error.md => Failure.md} | 22 +- .../docs/{ErrorError.md => FailureError.md} | 28 +- .../{model_error.go => model_failure.go} | 50 ++-- ..._error_error.go => model_failure_error.go} | 54 ++-- pinning/remote/client/openapi/model_pin.go | 2 +- .../client/openapi/model_pin_results.go | 2 +- .../remote/client/openapi/model_pin_status.go | 2 +- pinning/remote/client/openapi/model_status.go | 2 +- pinning/remote/client/openapi/response.go | 2 +- pinning/remote/client/openapi/utils.go | 2 +- 14 files changed, 100 insertions(+), 345 deletions(-) rename pinning/remote/client/openapi/docs/{Error.md => Failure.md} (68%) rename pinning/remote/client/openapi/docs/{ErrorError.md => FailureError.md} (71%) rename pinning/remote/client/openapi/{model_error.go => model_failure.go} (89%) rename pinning/remote/client/openapi/{model_error_error.go => model_failure_error.go} (88%) diff --git a/pinning/remote/client/openapi/README.md b/pinning/remote/client/openapi/README.md index 277bbc39a..fe21b9a00 100644 --- a/pinning/remote/client/openapi/README.md +++ b/pinning/remote/client/openapi/README.md @@ -104,7 +104,7 @@ Pin objects can be listed by executing `GET /pins` with optional parameters: ## Overview This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [OpenAPI-spec](https://www.openapis.org/) from a remote server, you can easily generate an API client. -- API version: 0.0.5 +- API version: 0.1.1 - Package version: 1.0.0 - Build package: org.openapitools.codegen.languages.GoClientExperimentalCodegen @@ -180,8 +180,8 @@ Class | Method | HTTP request | Description ## Documentation For Models - - [Error](docs/Error.md) - - [ErrorError](docs/ErrorError.md) + - [Failure](docs/Failure.md) + - [FailureError](docs/FailureError.md) - [Pin](docs/Pin.md) - [PinResults](docs/PinResults.md) - [PinStatus](docs/PinStatus.md) diff --git a/pinning/remote/client/openapi/api_pins.go b/pinning/remote/client/openapi/api_pins.go index ff03d0c99..8c45df5a3 100644 --- a/pinning/remote/client/openapi/api_pins.go +++ b/pinning/remote/client/openapi/api_pins.go @@ -3,7 +3,7 @@ * * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * - * API version: 0.0.5 + * API version: 0.1.1 * Generated by: OpenAPI Generator (https://openapi-generator.tech) */ @@ -170,8 +170,8 @@ func (r apiPinsGetRequest) Execute() (PinResults, *_nethttp.Response, error) { body: localVarBody, error: localVarHTTPResponse.Status, } - if localVarHTTPResponse.StatusCode == 400 { - var v Error + if localVarHTTPResponse.StatusCode >= 400 && localVarHTTPResponse.StatusCode <= 600 { + var v Failure err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { newErr.error = err.Error() @@ -180,55 +180,6 @@ func (r apiPinsGetRequest) Execute() (PinResults, *_nethttp.Response, error) { newErr.model = v return localVarReturnValue, localVarHTTPResponse, newErr } - if localVarHTTPResponse.StatusCode == 401 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 404 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 409 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode > 400 && localVarHTTPResponse.StatusCode < 500 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode >= 500 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - } return localVarReturnValue, localVarHTTPResponse, newErr } @@ -337,28 +288,8 @@ func (r apiPinsPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { body: localVarBody, error: localVarHTTPResponse.Status, } - if localVarHTTPResponse.StatusCode == 400 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 401 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 404 { - var v Error + if localVarHTTPResponse.StatusCode >= 400 && localVarHTTPResponse.StatusCode <= 600 { + var v Failure err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { newErr.error = err.Error() @@ -367,35 +298,6 @@ func (r apiPinsPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { newErr.model = v return localVarReturnValue, localVarHTTPResponse, newErr } - if localVarHTTPResponse.StatusCode == 409 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode > 400 && localVarHTTPResponse.StatusCode < 500 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode >= 500 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - } return localVarReturnValue, localVarHTTPResponse, newErr } @@ -495,18 +397,8 @@ func (r apiPinsRequestidDeleteRequest) Execute() (*_nethttp.Response, error) { body: localVarBody, error: localVarHTTPResponse.Status, } - if localVarHTTPResponse.StatusCode == 400 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarHTTPResponse, newErr - } - newErr.model = v - return localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 401 { - var v Error + if localVarHTTPResponse.StatusCode >= 400 && localVarHTTPResponse.StatusCode <= 600 { + var v Failure err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { newErr.error = err.Error() @@ -515,45 +407,6 @@ func (r apiPinsRequestidDeleteRequest) Execute() (*_nethttp.Response, error) { newErr.model = v return localVarHTTPResponse, newErr } - if localVarHTTPResponse.StatusCode == 404 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarHTTPResponse, newErr - } - newErr.model = v - return localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 409 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarHTTPResponse, newErr - } - newErr.model = v - return localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode > 400 && localVarHTTPResponse.StatusCode < 500 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarHTTPResponse, newErr - } - newErr.model = v - return localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode >= 500 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarHTTPResponse, newErr - } - newErr.model = v - } return localVarHTTPResponse, newErr } @@ -645,8 +498,8 @@ func (r apiPinsRequestidGetRequest) Execute() (PinStatus, *_nethttp.Response, er body: localVarBody, error: localVarHTTPResponse.Status, } - if localVarHTTPResponse.StatusCode == 400 { - var v Error + if localVarHTTPResponse.StatusCode >= 400 && localVarHTTPResponse.StatusCode <= 600 { + var v Failure err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { newErr.error = err.Error() @@ -655,55 +508,6 @@ func (r apiPinsRequestidGetRequest) Execute() (PinStatus, *_nethttp.Response, er newErr.model = v return localVarReturnValue, localVarHTTPResponse, newErr } - if localVarHTTPResponse.StatusCode == 401 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 404 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 409 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode > 400 && localVarHTTPResponse.StatusCode < 500 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode >= 500 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - } return localVarReturnValue, localVarHTTPResponse, newErr } @@ -816,48 +620,8 @@ func (r apiPinsRequestidPostRequest) Execute() (PinStatus, *_nethttp.Response, e body: localVarBody, error: localVarHTTPResponse.Status, } - if localVarHTTPResponse.StatusCode == 400 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 401 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 404 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 409 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode > 400 && localVarHTTPResponse.StatusCode < 500 { - var v Error + if localVarHTTPResponse.StatusCode >= 400 && localVarHTTPResponse.StatusCode <= 600 { + var v Failure err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { newErr.error = err.Error() @@ -866,15 +630,6 @@ func (r apiPinsRequestidPostRequest) Execute() (PinStatus, *_nethttp.Response, e newErr.model = v return localVarReturnValue, localVarHTTPResponse, newErr } - if localVarHTTPResponse.StatusCode >= 500 { - var v Error - err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - } return localVarReturnValue, localVarHTTPResponse, newErr } diff --git a/pinning/remote/client/openapi/client.go b/pinning/remote/client/openapi/client.go index 14a9d5f0c..985408987 100644 --- a/pinning/remote/client/openapi/client.go +++ b/pinning/remote/client/openapi/client.go @@ -3,7 +3,7 @@ * * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * - * API version: 0.0.5 + * API version: 0.1.1 * Generated by: OpenAPI Generator (https://openapi-generator.tech) */ @@ -39,7 +39,7 @@ var ( xmlCheck = regexp.MustCompile(`(?i:(?:application|text)/xml)`) ) -// APIClient manages communication with the IPFS Pinning Service API API v0.0.5 +// APIClient manages communication with the IPFS Pinning Service API API v0.1.1 // In most cases there should be only one, shared, APIClient. type APIClient struct { cfg *Configuration diff --git a/pinning/remote/client/openapi/configuration.go b/pinning/remote/client/openapi/configuration.go index 618454bca..2f31e1352 100644 --- a/pinning/remote/client/openapi/configuration.go +++ b/pinning/remote/client/openapi/configuration.go @@ -3,7 +3,7 @@ * * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * - * API version: 0.0.5 + * API version: 0.1.1 * Generated by: OpenAPI Generator (https://openapi-generator.tech) */ diff --git a/pinning/remote/client/openapi/docs/Error.md b/pinning/remote/client/openapi/docs/Failure.md similarity index 68% rename from pinning/remote/client/openapi/docs/Error.md rename to pinning/remote/client/openapi/docs/Failure.md index 762903943..c899f7138 100644 --- a/pinning/remote/client/openapi/docs/Error.md +++ b/pinning/remote/client/openapi/docs/Failure.md @@ -1,46 +1,46 @@ -# Error +# Failure ## Properties Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**Error** | [**ErrorError**](Error_error.md) | | +**Error** | [**FailureError**](Failure_error.md) | | ## Methods -### NewError +### NewFailure -`func NewError(error_ ErrorError, ) *Error` +`func NewFailure(error_ FailureError, ) *Failure` -NewError instantiates a new Error object +NewFailure instantiates a new Failure object This constructor will assign default values to properties that have it defined, and makes sure properties required by API are set, but the set of arguments will change when the set of required properties is changed -### NewErrorWithDefaults +### NewFailureWithDefaults -`func NewErrorWithDefaults() *Error` +`func NewFailureWithDefaults() *Failure` -NewErrorWithDefaults instantiates a new Error object +NewFailureWithDefaults instantiates a new Failure object This constructor will only assign default values to properties that have it defined, but it doesn't guarantee that properties required by API are set ### GetError -`func (o *Error) GetError() ErrorError` +`func (o *Failure) GetError() FailureError` GetError returns the Error field if non-nil, zero value otherwise. ### GetErrorOk -`func (o *Error) GetErrorOk() (*ErrorError, bool)` +`func (o *Failure) GetErrorOk() (*FailureError, bool)` GetErrorOk returns a tuple with the Error field if it's non-nil, zero value otherwise and a boolean to check if the value has been set. ### SetError -`func (o *Error) SetError(v ErrorError)` +`func (o *Failure) SetError(v FailureError)` SetError sets Error field to given value. diff --git a/pinning/remote/client/openapi/docs/ErrorError.md b/pinning/remote/client/openapi/docs/FailureError.md similarity index 71% rename from pinning/remote/client/openapi/docs/ErrorError.md rename to pinning/remote/client/openapi/docs/FailureError.md index e44d63829..478f1b942 100644 --- a/pinning/remote/client/openapi/docs/ErrorError.md +++ b/pinning/remote/client/openapi/docs/FailureError.md @@ -1,4 +1,4 @@ -# ErrorError +# FailureError ## Properties @@ -9,65 +9,65 @@ Name | Type | Description | Notes ## Methods -### NewErrorError +### NewFailureError -`func NewErrorError(reason string, ) *ErrorError` +`func NewFailureError(reason string, ) *FailureError` -NewErrorError instantiates a new ErrorError object +NewFailureError instantiates a new FailureError object This constructor will assign default values to properties that have it defined, and makes sure properties required by API are set, but the set of arguments will change when the set of required properties is changed -### NewErrorErrorWithDefaults +### NewFailureErrorWithDefaults -`func NewErrorErrorWithDefaults() *ErrorError` +`func NewFailureErrorWithDefaults() *FailureError` -NewErrorErrorWithDefaults instantiates a new ErrorError object +NewFailureErrorWithDefaults instantiates a new FailureError object This constructor will only assign default values to properties that have it defined, but it doesn't guarantee that properties required by API are set ### GetReason -`func (o *ErrorError) GetReason() string` +`func (o *FailureError) GetReason() string` GetReason returns the Reason field if non-nil, zero value otherwise. ### GetReasonOk -`func (o *ErrorError) GetReasonOk() (*string, bool)` +`func (o *FailureError) GetReasonOk() (*string, bool)` GetReasonOk returns a tuple with the Reason field if it's non-nil, zero value otherwise and a boolean to check if the value has been set. ### SetReason -`func (o *ErrorError) SetReason(v string)` +`func (o *FailureError) SetReason(v string)` SetReason sets Reason field to given value. ### GetDetails -`func (o *ErrorError) GetDetails() string` +`func (o *FailureError) GetDetails() string` GetDetails returns the Details field if non-nil, zero value otherwise. ### GetDetailsOk -`func (o *ErrorError) GetDetailsOk() (*string, bool)` +`func (o *FailureError) GetDetailsOk() (*string, bool)` GetDetailsOk returns a tuple with the Details field if it's non-nil, zero value otherwise and a boolean to check if the value has been set. ### SetDetails -`func (o *ErrorError) SetDetails(v string)` +`func (o *FailureError) SetDetails(v string)` SetDetails sets Details field to given value. ### HasDetails -`func (o *ErrorError) HasDetails() bool` +`func (o *FailureError) HasDetails() bool` HasDetails returns a boolean if a field has been set. diff --git a/pinning/remote/client/openapi/model_error.go b/pinning/remote/client/openapi/model_failure.go similarity index 89% rename from pinning/remote/client/openapi/model_error.go rename to pinning/remote/client/openapi/model_failure.go index ab308de3d..ec1fc8268 100644 --- a/pinning/remote/client/openapi/model_error.go +++ b/pinning/remote/client/openapi/model_failure.go @@ -3,7 +3,7 @@ * * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * - * API version: 0.0.5 + * API version: 0.1.1 * Generated by: OpenAPI Generator (https://openapi-generator.tech) */ @@ -13,33 +13,33 @@ import ( "encoding/json" ) -// Error Error object -type Error struct { - Error ErrorError `json:"error"` +// Failure Response for a failed request +type Failure struct { + Error FailureError `json:"error"` } -// NewError instantiates a new Error object +// NewFailure instantiates a new Failure object // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewError(error_ ErrorError) *Error { - this := Error{} +func NewFailure(error_ FailureError) *Failure { + this := Failure{} this.Error = error_ return &this } -// NewErrorWithDefaults instantiates a new Error object +// NewFailureWithDefaults instantiates a new Failure object // This constructor will only assign default values to properties that have it defined, // but it doesn't guarantee that properties required by API are set -func NewErrorWithDefaults() *Error { - this := Error{} +func NewFailureWithDefaults() *Failure { + this := Failure{} return &this } // GetError returns the Error field value -func (o *Error) GetError() ErrorError { +func (o *Failure) GetError() FailureError { if o == nil { - var ret ErrorError + var ret FailureError return ret } @@ -48,7 +48,7 @@ func (o *Error) GetError() ErrorError { // GetErrorOk returns a tuple with the Error field value // and a boolean to check if the value has been set. -func (o *Error) GetErrorOk() (*ErrorError, bool) { +func (o *Failure) GetErrorOk() (*FailureError, bool) { if o == nil { return nil, false } @@ -56,11 +56,11 @@ func (o *Error) GetErrorOk() (*ErrorError, bool) { } // SetError sets field value -func (o *Error) SetError(v ErrorError) { +func (o *Failure) SetError(v FailureError) { o.Error = v } -func (o Error) MarshalJSON() ([]byte, error) { +func (o Failure) MarshalJSON() ([]byte, error) { toSerialize := map[string]interface{}{} if true { toSerialize["error"] = o.Error @@ -68,38 +68,38 @@ func (o Error) MarshalJSON() ([]byte, error) { return json.Marshal(toSerialize) } -type NullableError struct { - value *Error +type NullableFailure struct { + value *Failure isSet bool } -func (v NullableError) Get() *Error { +func (v NullableFailure) Get() *Failure { return v.value } -func (v *NullableError) Set(val *Error) { +func (v *NullableFailure) Set(val *Failure) { v.value = val v.isSet = true } -func (v NullableError) IsSet() bool { +func (v NullableFailure) IsSet() bool { return v.isSet } -func (v *NullableError) Unset() { +func (v *NullableFailure) Unset() { v.value = nil v.isSet = false } -func NewNullableError(val *Error) *NullableError { - return &NullableError{value: val, isSet: true} +func NewNullableFailure(val *Failure) *NullableFailure { + return &NullableFailure{value: val, isSet: true} } -func (v NullableError) MarshalJSON() ([]byte, error) { +func (v NullableFailure) MarshalJSON() ([]byte, error) { return json.Marshal(v.value) } -func (v *NullableError) UnmarshalJSON(src []byte) error { +func (v *NullableFailure) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } diff --git a/pinning/remote/client/openapi/model_error_error.go b/pinning/remote/client/openapi/model_failure_error.go similarity index 88% rename from pinning/remote/client/openapi/model_error_error.go rename to pinning/remote/client/openapi/model_failure_error.go index 0a177dbd2..4f38acdf9 100644 --- a/pinning/remote/client/openapi/model_error_error.go +++ b/pinning/remote/client/openapi/model_failure_error.go @@ -3,7 +3,7 @@ * * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * - * API version: 0.0.5 + * API version: 0.1.1 * Generated by: OpenAPI Generator (https://openapi-generator.tech) */ @@ -13,34 +13,34 @@ import ( "encoding/json" ) -// ErrorError struct for ErrorError -type ErrorError struct { +// FailureError struct for FailureError +type FailureError struct { // Mandatory string identifying the type of error Reason string `json:"reason"` // Optional, longer description of the error; may include UUID of transaction for support, links to documentation etc Details *string `json:"details,omitempty"` } -// NewErrorError instantiates a new ErrorError object +// NewFailureError instantiates a new FailureError object // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewErrorError(reason string) *ErrorError { - this := ErrorError{} +func NewFailureError(reason string) *FailureError { + this := FailureError{} this.Reason = reason return &this } -// NewErrorErrorWithDefaults instantiates a new ErrorError object +// NewFailureErrorWithDefaults instantiates a new FailureError object // This constructor will only assign default values to properties that have it defined, // but it doesn't guarantee that properties required by API are set -func NewErrorErrorWithDefaults() *ErrorError { - this := ErrorError{} +func NewFailureErrorWithDefaults() *FailureError { + this := FailureError{} return &this } // GetReason returns the Reason field value -func (o *ErrorError) GetReason() string { +func (o *FailureError) GetReason() string { if o == nil { var ret string return ret @@ -51,7 +51,7 @@ func (o *ErrorError) GetReason() string { // GetReasonOk returns a tuple with the Reason field value // and a boolean to check if the value has been set. -func (o *ErrorError) GetReasonOk() (*string, bool) { +func (o *FailureError) GetReasonOk() (*string, bool) { if o == nil { return nil, false } @@ -59,12 +59,12 @@ func (o *ErrorError) GetReasonOk() (*string, bool) { } // SetReason sets field value -func (o *ErrorError) SetReason(v string) { +func (o *FailureError) SetReason(v string) { o.Reason = v } // GetDetails returns the Details field value if set, zero value otherwise. -func (o *ErrorError) GetDetails() string { +func (o *FailureError) GetDetails() string { if o == nil || o.Details == nil { var ret string return ret @@ -74,7 +74,7 @@ func (o *ErrorError) GetDetails() string { // GetDetailsOk returns a tuple with the Details field value if set, nil otherwise // and a boolean to check if the value has been set. -func (o *ErrorError) GetDetailsOk() (*string, bool) { +func (o *FailureError) GetDetailsOk() (*string, bool) { if o == nil || o.Details == nil { return nil, false } @@ -82,7 +82,7 @@ func (o *ErrorError) GetDetailsOk() (*string, bool) { } // HasDetails returns a boolean if a field has been set. -func (o *ErrorError) HasDetails() bool { +func (o *FailureError) HasDetails() bool { if o != nil && o.Details != nil { return true } @@ -91,11 +91,11 @@ func (o *ErrorError) HasDetails() bool { } // SetDetails gets a reference to the given string and assigns it to the Details field. -func (o *ErrorError) SetDetails(v string) { +func (o *FailureError) SetDetails(v string) { o.Details = &v } -func (o ErrorError) MarshalJSON() ([]byte, error) { +func (o FailureError) MarshalJSON() ([]byte, error) { toSerialize := map[string]interface{}{} if true { toSerialize["reason"] = o.Reason @@ -106,38 +106,38 @@ func (o ErrorError) MarshalJSON() ([]byte, error) { return json.Marshal(toSerialize) } -type NullableErrorError struct { - value *ErrorError +type NullableFailureError struct { + value *FailureError isSet bool } -func (v NullableErrorError) Get() *ErrorError { +func (v NullableFailureError) Get() *FailureError { return v.value } -func (v *NullableErrorError) Set(val *ErrorError) { +func (v *NullableFailureError) Set(val *FailureError) { v.value = val v.isSet = true } -func (v NullableErrorError) IsSet() bool { +func (v NullableFailureError) IsSet() bool { return v.isSet } -func (v *NullableErrorError) Unset() { +func (v *NullableFailureError) Unset() { v.value = nil v.isSet = false } -func NewNullableErrorError(val *ErrorError) *NullableErrorError { - return &NullableErrorError{value: val, isSet: true} +func NewNullableFailureError(val *FailureError) *NullableFailureError { + return &NullableFailureError{value: val, isSet: true} } -func (v NullableErrorError) MarshalJSON() ([]byte, error) { +func (v NullableFailureError) MarshalJSON() ([]byte, error) { return json.Marshal(v.value) } -func (v *NullableErrorError) UnmarshalJSON(src []byte) error { +func (v *NullableFailureError) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } diff --git a/pinning/remote/client/openapi/model_pin.go b/pinning/remote/client/openapi/model_pin.go index 31ae0ca86..0152d43bb 100644 --- a/pinning/remote/client/openapi/model_pin.go +++ b/pinning/remote/client/openapi/model_pin.go @@ -3,7 +3,7 @@ * * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * - * API version: 0.0.5 + * API version: 0.1.1 * Generated by: OpenAPI Generator (https://openapi-generator.tech) */ diff --git a/pinning/remote/client/openapi/model_pin_results.go b/pinning/remote/client/openapi/model_pin_results.go index b569fe5df..eacb5e021 100644 --- a/pinning/remote/client/openapi/model_pin_results.go +++ b/pinning/remote/client/openapi/model_pin_results.go @@ -3,7 +3,7 @@ * * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * - * API version: 0.0.5 + * API version: 0.1.1 * Generated by: OpenAPI Generator (https://openapi-generator.tech) */ diff --git a/pinning/remote/client/openapi/model_pin_status.go b/pinning/remote/client/openapi/model_pin_status.go index 21d7992b2..0f44e62c2 100644 --- a/pinning/remote/client/openapi/model_pin_status.go +++ b/pinning/remote/client/openapi/model_pin_status.go @@ -3,7 +3,7 @@ * * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * - * API version: 0.0.5 + * API version: 0.1.1 * Generated by: OpenAPI Generator (https://openapi-generator.tech) */ diff --git a/pinning/remote/client/openapi/model_status.go b/pinning/remote/client/openapi/model_status.go index 7b30371ba..56944819f 100644 --- a/pinning/remote/client/openapi/model_status.go +++ b/pinning/remote/client/openapi/model_status.go @@ -3,7 +3,7 @@ * * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * - * API version: 0.0.5 + * API version: 0.1.1 * Generated by: OpenAPI Generator (https://openapi-generator.tech) */ diff --git a/pinning/remote/client/openapi/response.go b/pinning/remote/client/openapi/response.go index 5f1b37456..8f9fb0b08 100644 --- a/pinning/remote/client/openapi/response.go +++ b/pinning/remote/client/openapi/response.go @@ -3,7 +3,7 @@ * * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * - * API version: 0.0.5 + * API version: 0.1.1 * Generated by: OpenAPI Generator (https://openapi-generator.tech) */ diff --git a/pinning/remote/client/openapi/utils.go b/pinning/remote/client/openapi/utils.go index 976239127..25d36f11b 100644 --- a/pinning/remote/client/openapi/utils.go +++ b/pinning/remote/client/openapi/utils.go @@ -3,7 +3,7 @@ * * ## About this spec The IPFS Pinning Service API is intended to be an implementation-agnostic API: - For use and implementation by pinning service providers - For use in client mode by IPFS nodes and GUI-based applications > **Note**: while ready for implementation, this spec is still a work in progress! ðŸ—ï¸ **Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec).** # Schemas This section describes the most important object types and conventions. A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). ## Identifiers ### cid [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. ### requestid Unique identifier of a pin request. When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. ## Objects ### Pin object ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) The `Pin` object is a representation of a pin request. It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. ### Pin status response ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) The `PinStatus` object is a representation of the current state of a pinning operation. It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`. ## The pin lifecycle ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) ### Creating a new pin object The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future - `status` in `PinStatus` indicates the current state of a pin ### Checking status of in-progress pinning `status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. ### Replacing an existing pin object The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. ### Removing a pin object A pin object can be removed via `DELETE /pins/{requestid}`. ## Provider hints Pinning of new data can be accelerated by providing a list of known data sources in `Pin.origins`, and connecting at least one of them to pinning service nodes at `PinStatus.delegates`. The most common scenario is a client putting its own IPFS node's multiaddrs in `Pin.origins`, and then directly connecting to every multiaddr returned by a pinning service in `PinStatus.delegates` to initiate transfer. This ensures data transfer starts immediately (without waiting for provider discovery over DHT), and direct dial from a client works around peer routing issues in restrictive network topologies such as NATs. ## Custom metadata Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. ### Pin metadata String keys and values passed in `Pin.meta` are persisted with the pin object. Potential uses: - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables filtering pins per app via `?meta={\"app_id\":}` - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) Note that it is OK for a client to omit or ignore these optional attributes; doing so should not impact the basic pinning functionality. ### Pin status info Additional `PinStatus.info` can be returned by pinning service. Potential uses: - `PinStatus.info[status_details]`: more info about the current status (queue position, percentage of transferred data, summary of where data is stored, etc); when `PinStatus.status=failed`, it could provide a reason why a pin operation failed (e.g. lack of funds, DAG too big, etc.) - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire # Pagination and filtering Pin objects can be listed by executing `GET /pins` with optional parameters: - When no filters are provided, the endpoint will return a small batch of the 10 most recently created items, from the latest to the oldest. - The number of returned items can be adjusted with the `limit` parameter (implicit default is 10). - If the value in `PinResults.count` is bigger than the length of `PinResults.results`, the client can infer there are more results that can be queried. - To read more items, pass the `before` filter with the timestamp from `PinStatus.created` found in the oldest item in the current batch of results. Repeat to read all results. - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, `status`, or `meta` filters. > **Note**: pagination by the `created` timestamp requires each value to be globally unique. Any future considerations to add support for bulk creation must account for this. * - * API version: 0.0.5 + * API version: 0.1.1 * Generated by: OpenAPI Generator (https://openapi-generator.tech) */ From 9783d2953f0a8c1fc2c409fcff113ddeb81bdad6 Mon Sep 17 00:00:00 2001 From: Andrew Gillis Date: Mon, 30 Nov 2020 14:34:37 -0800 Subject: [PATCH 3276/3817] Datastore based pinner (#4) feat: store pins in datastore instead of a DAG Adds a new `/pins` namespace to the given datastore and uses that to store pins as cbor binary, keyed by unique pin ID. The new datastore pinner stores pins in the datastore as individual key-value items. This is faster than the dag pinner, which stored all pins in a single dag that had to be rewritten every time a pin was added or removed. The new pinner provides a secondary indexing mechanism that can be used to index any data that a pin has. Secondary indexing logic is provided by the `dsindex` package. The new pinner currently includes indexing by CID. Both the new datastore pinner (`dspinner` package) and the old dag pinner (`ipldpinner` package) implementations are included to support migration between the two. Migration logic is provided by the `pinconv` package. Other features in new pinner: - Benchmarks are provided to compare performance of between the old and new pinners - New pinner does not keep in-memory set of pinned CIDs, instead it relies on the datastore - Separate recursive and direct CID indexes allow searching for pins without having to load pin data to check the mode - New pinner can rebuild indexes on load, if saved pins appear out of sync with the indexes This commit was moved from ipfs/go-ipfs-pinner@4c920717b015dd9555472fbaf11d36ff70cbd26d --- pinning/pinner/.gitignore | 8 + pinning/pinner/dsindex/error.go | 8 + pinning/pinner/dsindex/indexer.go | 285 +++++ pinning/pinner/dsindex/indexer_test.go | 286 +++++ pinning/pinner/dspinner/pin.go | 961 ++++++++++++++++ pinning/pinner/dspinner/pin_test.go | 1137 +++++++++++++++++++ pinning/pinner/ipldpinner/pin.go | 528 +++++++++ pinning/pinner/{ => ipldpinner}/pin_test.go | 50 +- pinning/pinner/{ => ipldpinner}/set.go | 16 +- pinning/pinner/{ => ipldpinner}/set_test.go | 2 +- pinning/pinner/pin.go | 498 -------- pinning/pinner/pinconv/pinconv.go | 128 +++ pinning/pinner/pinconv/pinconv_test.go | 153 +++ 13 files changed, 3541 insertions(+), 519 deletions(-) create mode 100644 pinning/pinner/.gitignore create mode 100644 pinning/pinner/dsindex/error.go create mode 100644 pinning/pinner/dsindex/indexer.go create mode 100644 pinning/pinner/dsindex/indexer_test.go create mode 100644 pinning/pinner/dspinner/pin.go create mode 100644 pinning/pinner/dspinner/pin_test.go create mode 100644 pinning/pinner/ipldpinner/pin.go rename pinning/pinner/{ => ipldpinner}/pin_test.go (90%) rename pinning/pinner/{ => ipldpinner}/set.go (94%) rename pinning/pinner/{ => ipldpinner}/set_test.go (99%) create mode 100644 pinning/pinner/pinconv/pinconv.go create mode 100644 pinning/pinner/pinconv/pinconv_test.go diff --git a/pinning/pinner/.gitignore b/pinning/pinner/.gitignore new file mode 100644 index 000000000..3c342889d --- /dev/null +++ b/pinning/pinner/.gitignore @@ -0,0 +1,8 @@ +*~ +*.log + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool +*.out diff --git a/pinning/pinner/dsindex/error.go b/pinning/pinner/dsindex/error.go new file mode 100644 index 000000000..f3b685bb9 --- /dev/null +++ b/pinning/pinner/dsindex/error.go @@ -0,0 +1,8 @@ +package dsindex + +import "errors" + +var ( + ErrEmptyKey = errors.New("key is empty") + ErrEmptyValue = errors.New("value is empty") +) diff --git a/pinning/pinner/dsindex/indexer.go b/pinning/pinner/dsindex/indexer.go new file mode 100644 index 000000000..e48af2e17 --- /dev/null +++ b/pinning/pinner/dsindex/indexer.go @@ -0,0 +1,285 @@ +// Package dsindex provides secondary indexing functionality for a datastore. +package dsindex + +import ( + "context" + "fmt" + "path" + + ds "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/namespace" + "github.com/ipfs/go-datastore/query" + "github.com/multiformats/go-multibase" +) + +// Indexer maintains a secondary index. An index is a collection of key-value +// mappings where the key is the secondary index that maps to one or more +// values, where each value is a unique key being indexed. +type Indexer interface { + // Add adds the specified value to the key + Add(ctx context.Context, key, value string) error + + // Delete deletes the specified value from the key. If the value is not in + // the datastore, this method returns no error. + Delete(ctx context.Context, key, value string) error + + // DeleteKey deletes all values in the given key. If a key is not in the + // datastore, this method returns no error. Returns a count of values that + // were deleted. + DeleteKey(ctx context.Context, key string) (count int, err error) + + // DeleteAll deletes all keys managed by this Indexer. Returns a count of + // the values that were deleted. + DeleteAll(ctx context.Context) (count int, err error) + + // ForEach calls the function for each value in the specified key, until + // there are no more values, or until the function returns false. If key + // is empty string, then all keys are iterated. + ForEach(ctx context.Context, key string, fn func(key, value string) bool) error + + // HasValue determines if the key contains the specified value + HasValue(ctx context.Context, key, value string) (bool, error) + + // HasAny determines if any value is in the specified key. If key is + // empty string, then all values are searched. + HasAny(ctx context.Context, key string) (bool, error) + + // Search returns all values for the given key + Search(ctx context.Context, key string) (values []string, err error) +} + +// indexer is a simple implementation of Indexer. This implementation relies +// on the underlying data store to support efficient querying by prefix. +// +// TODO: Consider adding caching +type indexer struct { + dstore ds.Datastore +} + +// New creates a new datastore index. All indexes are stored under the +// specified index name. +// +// To persist the actions of calling Indexer functions, it is necessary to call +// dstore.Sync. +func New(dstore ds.Datastore, name ds.Key) Indexer { + return &indexer{ + dstore: namespace.Wrap(dstore, name), + } +} + +func (x *indexer) Add(ctx context.Context, key, value string) error { + if key == "" { + return ErrEmptyKey + } + if value == "" { + return ErrEmptyValue + } + dsKey := ds.NewKey(encode(key)).ChildString(encode(value)) + return x.dstore.Put(dsKey, []byte{}) +} + +func (x *indexer) Delete(ctx context.Context, key, value string) error { + if key == "" { + return ErrEmptyKey + } + if value == "" { + return ErrEmptyValue + } + return x.dstore.Delete(ds.NewKey(encode(key)).ChildString(encode(value))) +} + +func (x *indexer) DeleteKey(ctx context.Context, key string) (int, error) { + if key == "" { + return 0, ErrEmptyKey + } + return x.deletePrefix(ctx, encode(key)) +} + +func (x *indexer) DeleteAll(ctx context.Context) (int, error) { + return x.deletePrefix(ctx, "") +} + +func (x *indexer) ForEach(ctx context.Context, key string, fn func(key, value string) bool) error { + if key != "" { + key = encode(key) + } + + q := query.Query{ + Prefix: key, + KeysOnly: true, + } + results, err := x.dstore.Query(q) + if err != nil { + return err + } + + for { + r, ok := results.NextSync() + if !ok { + break + } + if r.Error != nil { + err = r.Error + break + } + if ctx.Err() != nil { + err = ctx.Err() + break + } + ent := r.Entry + decIdx, err := decode(path.Base(path.Dir(ent.Key))) + if err != nil { + err = fmt.Errorf("cannot decode index: %v", err) + break + } + decKey, err := decode(path.Base(ent.Key)) + if err != nil { + err = fmt.Errorf("cannot decode key: %v", err) + break + } + if !fn(decIdx, decKey) { + break + } + } + results.Close() + + return err +} + +func (x *indexer) HasValue(ctx context.Context, key, value string) (bool, error) { + if key == "" { + return false, ErrEmptyKey + } + if value == "" { + return false, ErrEmptyValue + } + return x.dstore.Has(ds.NewKey(encode(key)).ChildString(encode(value))) +} + +func (x *indexer) HasAny(ctx context.Context, key string) (bool, error) { + var any bool + err := x.ForEach(ctx, key, func(key, value string) bool { + any = true + return false + }) + return any, err +} + +func (x *indexer) Search(ctx context.Context, key string) ([]string, error) { + if key == "" { + return nil, ErrEmptyKey + } + ents, err := x.queryPrefix(ctx, encode(key)) + if err != nil { + return nil, err + } + if len(ents) == 0 { + return nil, nil + } + + values := make([]string, len(ents)) + for i := range ents { + values[i], err = decode(path.Base(ents[i].Key)) + if err != nil { + return nil, fmt.Errorf("cannot decode value: %v", err) + } + } + return values, nil +} + +// SyncIndex synchronizes the keys in the target Indexer to match those of the +// ref Indexer. This function does not change this indexer's key root (name +// passed into New). +func SyncIndex(ctx context.Context, ref, target Indexer) (bool, error) { + // Build reference index map + refs := map[string]string{} + err := ref.ForEach(ctx, "", func(key, value string) bool { + refs[value] = key + return true + }) + if err != nil { + return false, err + } + if len(refs) == 0 { + return false, nil + } + + // Compare current indexes + dels := map[string]string{} + err = target.ForEach(ctx, "", func(key, value string) bool { + refKey, ok := refs[value] + if ok && refKey == key { + // same in both; delete from refs, do not add to dels + delete(refs, value) + } else { + dels[value] = key + } + return true + }) + if err != nil { + return false, err + } + + // Items in dels are keys that no longer exist + for value, key := range dels { + err = target.Delete(ctx, key, value) + if err != nil { + return false, err + } + } + + // What remains in refs are keys that need to be added + for value, key := range refs { + err = target.Add(ctx, key, value) + if err != nil { + return false, err + } + } + + return len(refs) != 0 || len(dels) != 0, nil +} + +func (x *indexer) deletePrefix(ctx context.Context, prefix string) (int, error) { + ents, err := x.queryPrefix(ctx, prefix) + if err != nil { + return 0, err + } + + for i := range ents { + err = x.dstore.Delete(ds.NewKey(ents[i].Key)) + if err != nil { + return 0, err + } + } + + return len(ents), nil +} + +func (x *indexer) queryPrefix(ctx context.Context, prefix string) ([]query.Entry, error) { + q := query.Query{ + Prefix: prefix, + KeysOnly: true, + } + results, err := x.dstore.Query(q) + if err != nil { + return nil, err + } + return results.Rest() +} + +func encode(data string) string { + encData, err := multibase.Encode(multibase.Base64url, []byte(data)) + if err != nil { + // programming error; using unsupported encoding + panic(err.Error()) + } + return encData +} + +func decode(data string) (string, error) { + _, b, err := multibase.Decode(data) + if err != nil { + return "", err + } + return string(b), nil +} diff --git a/pinning/pinner/dsindex/indexer_test.go b/pinning/pinner/dsindex/indexer_test.go new file mode 100644 index 000000000..45372c605 --- /dev/null +++ b/pinning/pinner/dsindex/indexer_test.go @@ -0,0 +1,286 @@ +package dsindex + +import ( + "context" + "testing" + + ds "github.com/ipfs/go-datastore" +) + +func createIndexer() Indexer { + dstore := ds.NewMapDatastore() + nameIndex := New(dstore, ds.NewKey("/data/nameindex")) + + ctx := context.Background() + nameIndex.Add(ctx, "alice", "a1") + nameIndex.Add(ctx, "bob", "b1") + nameIndex.Add(ctx, "bob", "b2") + nameIndex.Add(ctx, "cathy", "c1") + + return nameIndex +} + +func TestAdd(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nameIndex := createIndexer() + err := nameIndex.Add(ctx, "someone", "s1") + if err != nil { + t.Fatal(err) + } + err = nameIndex.Add(ctx, "someone", "s1") + if err != nil { + t.Fatal(err) + } + + err = nameIndex.Add(ctx, "", "noindex") + if err != ErrEmptyKey { + t.Fatal("unexpected error:", err) + } + + err = nameIndex.Add(ctx, "nokey", "") + if err != ErrEmptyValue { + t.Fatal("unexpected error:", err) + } +} + +func TestHasValue(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nameIndex := createIndexer() + + ok, err := nameIndex.HasValue(ctx, "bob", "b1") + if err != nil { + t.Fatal(err) + } + if !ok { + t.Fatal("missing index") + } + + ok, err = nameIndex.HasValue(ctx, "bob", "b3") + if err != nil { + t.Fatal(err) + } + if ok { + t.Fatal("should not have index") + } + + _, err = nameIndex.HasValue(ctx, "", "b1") + if err != ErrEmptyKey { + t.Fatal("unexpected error:", err) + } + + _, err = nameIndex.HasValue(ctx, "bob", "") + if err != ErrEmptyValue { + t.Fatal("unexpected error:", err) + } +} + +func TestHasAny(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nameIndex := createIndexer() + + ok, err := nameIndex.HasAny(ctx, "nothere") + if err != nil { + t.Fatal(err) + } + if ok { + t.Fatal("should return false") + } + + for _, idx := range []string{"alice", "bob", ""} { + ok, err = nameIndex.HasAny(ctx, idx) + if err != nil { + t.Fatal(err) + } + if !ok { + t.Fatal("missing index", idx) + } + } + + count, err := nameIndex.DeleteAll(ctx) + if err != nil { + t.Fatal(err) + } + if count != 4 { + t.Fatal("expected 4 deletions") + } + + ok, err = nameIndex.HasAny(ctx, "") + if err != nil { + t.Fatal(err) + } + if ok { + t.Fatal("should return false") + } +} + +func TestForEach(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nameIndex := createIndexer() + + found := make(map[string]struct{}) + err := nameIndex.ForEach(ctx, "bob", func(key, value string) bool { + found[value] = struct{}{} + return true + }) + if err != nil { + t.Fatal(err) + } + + for _, value := range []string{"b1", "b2"} { + _, ok := found[value] + if !ok { + t.Fatal("missing key for value", value) + } + } + + values := map[string]string{} + err = nameIndex.ForEach(ctx, "", func(key, value string) bool { + values[value] = key + return true + }) + if err != nil { + t.Fatal(err) + } + if len(values) != 4 { + t.Fatal("expected 4 keys") + } + + if values["a1"] != "alice" { + t.Error("expected a1: alice") + } + if values["b1"] != "bob" { + t.Error("expected b1: bob") + } + if values["b2"] != "bob" { + t.Error("expected b2: bob") + } + if values["c1"] != "cathy" { + t.Error("expected c1: cathy") + } +} + +func TestSearch(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nameIndex := createIndexer() + + ids, err := nameIndex.Search(ctx, "bob") + if err != nil { + t.Fatal(err) + } + if len(ids) != 2 { + t.Fatal("wrong number of ids - expected 2 got", ids) + } + for _, id := range ids { + if id != "b1" && id != "b2" { + t.Fatal("wrong value in id set") + } + } + if ids[0] == ids[1] { + t.Fatal("duplicate id") + } + + ids, err = nameIndex.Search(ctx, "cathy") + if err != nil { + t.Fatal(err) + } + if len(ids) != 1 || ids[0] != "c1" { + t.Fatal("wrong ids") + } + + ids, err = nameIndex.Search(ctx, "amit") + if err != nil { + t.Fatal(err) + } + if len(ids) != 0 { + t.Fatal("unexpected ids returned") + } +} + +func TestDelete(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nameIndex := createIndexer() + + err := nameIndex.Delete(ctx, "bob", "b3") + if err != nil { + t.Fatal(err) + } + + err = nameIndex.Delete(ctx, "alice", "a1") + if err != nil { + t.Fatal(err) + } + + ok, err := nameIndex.HasValue(ctx, "alice", "a1") + if err != nil { + t.Fatal(err) + } + if ok { + t.Fatal("index key should have been deleted") + } + + count, err := nameIndex.DeleteKey(ctx, "bob") + if err != nil { + t.Fatal(err) + } + if count != 2 { + t.Fatal("wrong deleted count") + } + ok, _ = nameIndex.HasValue(ctx, "bob", "b1") + if ok { + t.Fatal("index not deleted") + } +} + +func TestSyncIndex(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nameIndex := createIndexer() + + dstore := ds.NewMapDatastore() + refIndex := New(dstore, ds.NewKey("/ref")) + refIndex.Add(ctx, "alice", "a1") + refIndex.Add(ctx, "cathy", "zz") + refIndex.Add(ctx, "dennis", "d1") + + changed, err := SyncIndex(ctx, refIndex, nameIndex) + if err != nil { + t.Fatal(err) + } + if !changed { + t.Error("change was not indicated") + } + + // Create map of id->index in sync target + syncs := map[string]string{} + err = nameIndex.ForEach(ctx, "", func(key, value string) bool { + syncs[value] = key + return true + }) + if err != nil { + t.Fatal(err) + } + + // Iterate items in sync source and make sure they appear in target + var itemCount int + err = refIndex.ForEach(ctx, "", func(key, value string) bool { + itemCount++ + syncKey, ok := syncs[value] + if !ok || key != syncKey { + t.Fatal("key", key, "-->", value, "was not synced") + } + return true + }) + if err != nil { + t.Fatal(err) + } + + if itemCount != len(syncs) { + t.Fatal("different number of items in sync source and target") + } +} diff --git a/pinning/pinner/dspinner/pin.go b/pinning/pinner/dspinner/pin.go new file mode 100644 index 000000000..5fd65e7bf --- /dev/null +++ b/pinning/pinner/dspinner/pin.go @@ -0,0 +1,961 @@ +// Package dspinner implements structures and methods to keep track of +// which objects a user wants to keep stored locally. This implementation +// stores pin data in a datastore. +package dspinner + +import ( + "context" + "errors" + "fmt" + "path" + "sync" + + "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/query" + ipfspinner "github.com/ipfs/go-ipfs-pinner" + "github.com/ipfs/go-ipfs-pinner/dsindex" + ipld "github.com/ipfs/go-ipld-format" + logging "github.com/ipfs/go-log" + mdag "github.com/ipfs/go-merkledag" + "github.com/ipfs/go-merkledag/dagutils" + "github.com/polydawn/refmt/cbor" + "github.com/polydawn/refmt/obj/atlas" +) + +const ( + basePath = "/pins" + pinKeyPath = "/pins/pin" + indexKeyPath = "/pins/index" + dirtyKeyPath = "/pins/state/dirty" +) + +var ( + // ErrNotPinned is returned when trying to unpin items that are not pinned. + ErrNotPinned = errors.New("not pinned or pinned indirectly") + + log logging.StandardLogger = logging.Logger("pin") + + linkDirect, linkRecursive string + + pinCidDIndexPath string + pinCidRIndexPath string + pinNameIndexPath string + + dirtyKey = ds.NewKey(dirtyKeyPath) + + pinAtl atlas.Atlas +) + +func init() { + directStr, ok := ipfspinner.ModeToString(ipfspinner.Direct) + if !ok { + panic("could not find Direct pin enum") + } + linkDirect = directStr + + recursiveStr, ok := ipfspinner.ModeToString(ipfspinner.Recursive) + if !ok { + panic("could not find Recursive pin enum") + } + linkRecursive = recursiveStr + + pinCidRIndexPath = path.Join(indexKeyPath, "cidRindex") + pinCidDIndexPath = path.Join(indexKeyPath, "cidDindex") + pinNameIndexPath = path.Join(indexKeyPath, "nameIndex") + + pinAtl = atlas.MustBuild( + atlas.BuildEntry(pin{}).StructMap(). + AddField("Cid", atlas.StructMapEntry{SerialName: "cid"}). + AddField("Metadata", atlas.StructMapEntry{SerialName: "metadata", OmitEmpty: true}). + AddField("Mode", atlas.StructMapEntry{SerialName: "mode"}). + AddField("Name", atlas.StructMapEntry{SerialName: "name", OmitEmpty: true}). + Complete(), + atlas.BuildEntry(cid.Cid{}).Transform(). + TransformMarshal(atlas.MakeMarshalTransformFunc(func(live cid.Cid) ([]byte, error) { return live.MarshalBinary() })). + TransformUnmarshal(atlas.MakeUnmarshalTransformFunc(func(serializable []byte) (cid.Cid, error) { + c := cid.Cid{} + err := c.UnmarshalBinary(serializable) + if err != nil { + return cid.Cid{}, err + } + return c, nil + })).Complete(), + ) + pinAtl = pinAtl.WithMapMorphism(atlas.MapMorphism{KeySortMode: atlas.KeySortMode_Strings}) +} + +// pinner implements the Pinner interface +type pinner struct { + lock sync.RWMutex + + dserv ipld.DAGService + dstore ds.Datastore + + cidDIndex dsindex.Indexer + cidRIndex dsindex.Indexer + nameIndex dsindex.Indexer + + clean int64 + dirty int64 +} + +var _ ipfspinner.Pinner = (*pinner)(nil) + +type pin struct { + Id string + Cid cid.Cid + Metadata map[string]interface{} + Mode ipfspinner.Mode + Name string +} + +func (p *pin) dsKey() ds.Key { + return ds.NewKey(path.Join(pinKeyPath, p.Id)) +} + +func newPin(c cid.Cid, mode ipfspinner.Mode, name string) *pin { + return &pin{ + Id: ds.RandomKey().String(), + Cid: c, + Name: name, + Mode: mode, + } +} + +type syncDAGService interface { + ipld.DAGService + Sync() error +} + +// New creates a new pinner and loads its keysets from the given datastore. If +// there is no data present in the datastore, then an empty pinner is returned. +func New(ctx context.Context, dstore ds.Datastore, dserv ipld.DAGService) (ipfspinner.Pinner, error) { + p := &pinner{ + cidDIndex: dsindex.New(dstore, ds.NewKey(pinCidDIndexPath)), + cidRIndex: dsindex.New(dstore, ds.NewKey(pinCidRIndexPath)), + nameIndex: dsindex.New(dstore, ds.NewKey(pinNameIndexPath)), + dserv: dserv, + dstore: dstore, + } + + data, err := dstore.Get(dirtyKey) + if err != nil { + if err == ds.ErrNotFound { + return p, nil + } + return nil, fmt.Errorf("cannot load dirty flag: %v", err) + } + if data[0] == 1 { + p.dirty = 1 + + pins, err := p.loadAllPins(ctx) + if err != nil { + return nil, fmt.Errorf("cannot load pins: %v", err) + } + + err = p.rebuildIndexes(ctx, pins) + if err != nil { + return nil, fmt.Errorf("cannot rebuild indexes: %v", err) + } + } + + return p, nil +} + +// Pin the given node, optionally recursive +func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { + err := p.dserv.Add(ctx, node) + if err != nil { + return err + } + + c := node.Cid() + cidKey := c.KeyString() + + p.lock.Lock() + defer p.lock.Unlock() + + if recurse { + found, err := p.cidRIndex.HasAny(ctx, cidKey) + if err != nil { + return err + } + if found { + return nil + } + + dirtyBefore := p.dirty + + // temporary unlock to fetch the entire graph + p.lock.Unlock() + // Fetch graph starting at node identified by cid + err = mdag.FetchGraph(ctx, c, p.dserv) + p.lock.Lock() + if err != nil { + return err + } + + // Only look again if something has changed. + if p.dirty != dirtyBefore { + found, err = p.cidRIndex.HasAny(ctx, cidKey) + if err != nil { + return err + } + if found { + return nil + } + } + + // TODO: remove this to support multiple pins per CID + found, err = p.cidDIndex.HasAny(ctx, cidKey) + if err != nil { + return err + } + if found { + p.removePinsForCid(ctx, c, ipfspinner.Direct) + } + + _, err = p.addPin(ctx, c, ipfspinner.Recursive, "") + if err != nil { + return err + } + } else { + found, err := p.cidRIndex.HasAny(ctx, cidKey) + if err != nil { + return err + } + if found { + return fmt.Errorf("%s already pinned recursively", c.String()) + } + + _, err = p.addPin(ctx, c, ipfspinner.Direct, "") + if err != nil { + return err + } + } + return nil +} + +func (p *pinner) addPin(ctx context.Context, c cid.Cid, mode ipfspinner.Mode, name string) (string, error) { + // Create new pin and store in datastore + pp := newPin(c, mode, name) + + // Serialize pin + pinData, err := encodePin(pp) + if err != nil { + return "", fmt.Errorf("could not encode pin: %v", err) + } + + p.setDirty(ctx, true) + + // Store CID index + switch mode { + case ipfspinner.Recursive: + err = p.cidRIndex.Add(ctx, c.KeyString(), pp.Id) + case ipfspinner.Direct: + err = p.cidDIndex.Add(ctx, c.KeyString(), pp.Id) + default: + panic("pin mode must be recursive or direct") + } + if err != nil { + return "", fmt.Errorf("could not add pin cid index: %v", err) + } + + if name != "" { + // Store name index + err = p.nameIndex.Add(ctx, name, pp.Id) + if err != nil { + return "", fmt.Errorf("could not add pin name index: %v", err) + } + } + + // Store the pin. Pin must be stored after index for recovery to work. + err = p.dstore.Put(pp.dsKey(), pinData) + if err != nil { + if mode == ipfspinner.Recursive { + p.cidRIndex.Delete(ctx, c.KeyString(), pp.Id) + } else { + p.cidDIndex.Delete(ctx, c.KeyString(), pp.Id) + } + if name != "" { + p.nameIndex.Delete(ctx, name, pp.Id) + } + return "", err + } + + return pp.Id, nil +} + +func (p *pinner) removePin(ctx context.Context, pp *pin) error { + p.setDirty(ctx, true) + + // Remove pin from datastore. Pin must be removed before index for + // recovery to work. + err := p.dstore.Delete(pp.dsKey()) + if err != nil { + return err + } + // Remove cid index from datastore + if pp.Mode == ipfspinner.Recursive { + err = p.cidRIndex.Delete(ctx, pp.Cid.KeyString(), pp.Id) + } else { + err = p.cidDIndex.Delete(ctx, pp.Cid.KeyString(), pp.Id) + } + if err != nil { + return err + } + + if pp.Name != "" { + // Remove name index from datastore + err = p.nameIndex.Delete(ctx, pp.Name, pp.Id) + if err != nil { + return err + } + } + + return nil +} + +// Unpin a given key +func (p *pinner) Unpin(ctx context.Context, c cid.Cid, recursive bool) error { + cidKey := c.KeyString() + + p.lock.Lock() + defer p.lock.Unlock() + + // TODO: use Ls() to lookup pins when new pinning API available + /* + matchSpec := map[string][]string { + "cid": []string{c.String} + } + matches := p.Ls(matchSpec) + */ + has, err := p.cidRIndex.HasAny(ctx, cidKey) + if err != nil { + return err + } + + if has { + if !recursive { + return fmt.Errorf("%s is pinned recursively", c.String()) + } + } else { + has, err = p.cidDIndex.HasAny(ctx, cidKey) + if err != nil { + return err + } + if !has { + return ErrNotPinned + } + } + + _, err = p.removePinsForCid(ctx, c, ipfspinner.Any) + if err != nil { + return err + } + + return nil +} + +// IsPinned returns whether or not the given key is pinned +// and an explanation of why its pinned +func (p *pinner) IsPinned(ctx context.Context, c cid.Cid) (string, bool, error) { + p.lock.RLock() + defer p.lock.RUnlock() + return p.isPinnedWithType(ctx, c, ipfspinner.Any) +} + +// IsPinnedWithType returns whether or not the given cid is pinned with the +// given pin type, as well as returning the type of pin its pinned with. +func (p *pinner) IsPinnedWithType(ctx context.Context, c cid.Cid, mode ipfspinner.Mode) (string, bool, error) { + p.lock.RLock() + defer p.lock.RUnlock() + return p.isPinnedWithType(ctx, c, mode) +} + +func (p *pinner) isPinnedWithType(ctx context.Context, c cid.Cid, mode ipfspinner.Mode) (string, bool, error) { + cidKey := c.KeyString() + switch mode { + case ipfspinner.Recursive: + has, err := p.cidRIndex.HasAny(ctx, cidKey) + if err != nil { + return "", false, err + } + if has { + return linkRecursive, true, nil + } + return "", false, nil + case ipfspinner.Direct: + has, err := p.cidDIndex.HasAny(ctx, cidKey) + if err != nil { + return "", false, err + } + if has { + return linkDirect, true, nil + } + return "", false, nil + case ipfspinner.Internal: + return "", false, nil + case ipfspinner.Indirect: + case ipfspinner.Any: + has, err := p.cidRIndex.HasAny(ctx, cidKey) + if err != nil { + return "", false, err + } + if has { + return linkRecursive, true, nil + } + has, err = p.cidDIndex.HasAny(ctx, cidKey) + if err != nil { + return "", false, err + } + if has { + return linkDirect, true, nil + } + default: + err := fmt.Errorf( + "invalid Pin Mode '%d', must be one of {%d, %d, %d, %d, %d}", + mode, ipfspinner.Direct, ipfspinner.Indirect, ipfspinner.Recursive, + ipfspinner.Internal, ipfspinner.Any) + return "", false, err + } + + // Default is Indirect + visitedSet := cid.NewSet() + + // No index for given CID, so search children of all recursive pinned CIDs + var has bool + var rc cid.Cid + var e error + err := p.cidRIndex.ForEach(ctx, "", func(key, value string) bool { + rc, e = cid.Cast([]byte(key)) + if e != nil { + return false + } + has, e = hasChild(ctx, p.dserv, rc, c, visitedSet.Visit) + if e != nil { + return false + } + if has { + return false + } + return true + }) + if err != nil { + return "", false, err + } + if e != nil { + return "", false, e + } + + if has { + return rc.String(), true, nil + } + + return "", false, nil +} + +// CheckIfPinned checks if a set of keys are pinned, more efficient than +// calling IsPinned for each key, returns the pinned status of cid(s) +// +// TODO: If a CID is pinned by multiple pins, should they all be reported? +func (p *pinner) CheckIfPinned(ctx context.Context, cids ...cid.Cid) ([]ipfspinner.Pinned, error) { + pinned := make([]ipfspinner.Pinned, 0, len(cids)) + toCheck := cid.NewSet() + + p.lock.RLock() + defer p.lock.RUnlock() + + // First check for non-Indirect pins directly + for _, c := range cids { + cidKey := c.KeyString() + has, err := p.cidRIndex.HasAny(ctx, cidKey) + if err != nil { + return nil, err + } + if has { + pinned = append(pinned, ipfspinner.Pinned{Key: c, Mode: ipfspinner.Recursive}) + } else { + has, err = p.cidDIndex.HasAny(ctx, cidKey) + if err != nil { + return nil, err + } + if has { + pinned = append(pinned, ipfspinner.Pinned{Key: c, Mode: ipfspinner.Direct}) + } else { + toCheck.Add(c) + } + } + } + + // Now walk all recursive pins to check for indirect pins + var checkChildren func(cid.Cid, cid.Cid) error + checkChildren = func(rk, parentKey cid.Cid) error { + links, err := ipld.GetLinks(ctx, p.dserv, parentKey) + if err != nil { + return err + } + for _, lnk := range links { + c := lnk.Cid + + if toCheck.Has(c) { + pinned = append(pinned, + ipfspinner.Pinned{Key: c, Mode: ipfspinner.Indirect, Via: rk}) + toCheck.Remove(c) + } + + err = checkChildren(rk, c) + if err != nil { + return err + } + + if toCheck.Len() == 0 { + return nil + } + } + return nil + } + + var e error + err := p.cidRIndex.ForEach(ctx, "", func(key, value string) bool { + var rk cid.Cid + rk, e = cid.Cast([]byte(key)) + if e != nil { + return false + } + e = checkChildren(rk, rk) + if e != nil { + return false + } + if toCheck.Len() == 0 { + return false + } + return true + }) + if err != nil { + return nil, err + } + if e != nil { + return nil, e + } + + // Anything left in toCheck is not pinned + for _, k := range toCheck.Keys() { + pinned = append(pinned, ipfspinner.Pinned{Key: k, Mode: ipfspinner.NotPinned}) + } + + return pinned, nil +} + +// RemovePinWithMode is for manually editing the pin structure. +// Use with care! If used improperly, garbage collection may not +// be successful. +func (p *pinner) RemovePinWithMode(c cid.Cid, mode ipfspinner.Mode) { + ctx := context.TODO() + // Check cache to see if CID is pinned + switch mode { + case ipfspinner.Direct, ipfspinner.Recursive: + default: + // programmer error, panic OK + panic("unrecognized pin type") + } + + p.lock.Lock() + defer p.lock.Unlock() + + p.removePinsForCid(ctx, c, mode) +} + +// removePinsForCid removes all pins for a cid that has the specified mode. +// Returns true if any pins, and all corresponding CID index entries, were +// removed. Otherwise, returns false. +func (p *pinner) removePinsForCid(ctx context.Context, c cid.Cid, mode ipfspinner.Mode) (bool, error) { + // Search for pins by CID + var ids []string + var err error + cidKey := c.KeyString() + switch mode { + case ipfspinner.Recursive: + ids, err = p.cidRIndex.Search(ctx, cidKey) + case ipfspinner.Direct: + ids, err = p.cidDIndex.Search(ctx, cidKey) + case ipfspinner.Any: + ids, err = p.cidRIndex.Search(ctx, cidKey) + if err != nil { + return false, err + } + dIds, err := p.cidDIndex.Search(ctx, cidKey) + if err != nil { + return false, err + } + if len(dIds) != 0 { + ids = append(ids, dIds...) + } + } + if err != nil { + return false, err + } + + var removed bool + + // Remove the pin with the requested mode + for _, pid := range ids { + var pp *pin + pp, err = p.loadPin(ctx, pid) + if err != nil { + if err == ds.ErrNotFound { + p.setDirty(ctx, true) + // Fix index; remove index for pin that does not exist + switch mode { + case ipfspinner.Recursive: + p.cidRIndex.DeleteKey(ctx, cidKey) + case ipfspinner.Direct: + p.cidDIndex.DeleteKey(ctx, cidKey) + case ipfspinner.Any: + p.cidRIndex.DeleteKey(ctx, cidKey) + p.cidDIndex.DeleteKey(ctx, cidKey) + } + log.Error("found CID index with missing pin") + continue + } + return false, err + } + if mode == ipfspinner.Any || pp.Mode == mode { + err = p.removePin(ctx, pp) + if err != nil { + return false, err + } + removed = true + } + } + return removed, nil +} + +// loadPin loads a single pin from the datastore. +func (p *pinner) loadPin(ctx context.Context, pid string) (*pin, error) { + pinData, err := p.dstore.Get(ds.NewKey(path.Join(pinKeyPath, pid))) + if err != nil { + return nil, err + } + return decodePin(pid, pinData) +} + +// loadAllPins loads all pins from the datastore. +func (p *pinner) loadAllPins(ctx context.Context) ([]*pin, error) { + q := query.Query{ + Prefix: pinKeyPath, + } + results, err := p.dstore.Query(q) + if err != nil { + return nil, err + } + ents, err := results.Rest() + if err != nil { + return nil, err + } + if len(ents) == 0 { + return nil, nil + } + + pins := make([]*pin, len(ents)) + for i := range ents { + if ctx.Err() != nil { + return nil, ctx.Err() + } + var p *pin + p, err = decodePin(path.Base(ents[i].Key), ents[i].Value) + if err != nil { + return nil, err + } + pins[i] = p + } + return pins, nil +} + +// rebuildIndexes uses the stored pins to rebuild secondary indexes. This +// resolves any discrepancy between secondary indexes and pins that could +// result from a program termination between saving the two. +func (p *pinner) rebuildIndexes(ctx context.Context, pins []*pin) error { + // Build temporary in-memory CID index from pins + dstoreMem := ds.NewMapDatastore() + tmpCidDIndex := dsindex.New(dstoreMem, ds.NewKey(pinCidDIndexPath)) + tmpCidRIndex := dsindex.New(dstoreMem, ds.NewKey(pinCidRIndexPath)) + tmpNameIndex := dsindex.New(dstoreMem, ds.NewKey(pinNameIndexPath)) + var hasNames bool + for _, pp := range pins { + if ctx.Err() != nil { + return ctx.Err() + } + if pp.Mode == ipfspinner.Recursive { + tmpCidRIndex.Add(ctx, pp.Cid.KeyString(), pp.Id) + } else if pp.Mode == ipfspinner.Direct { + tmpCidDIndex.Add(ctx, pp.Cid.KeyString(), pp.Id) + } + if pp.Name != "" { + tmpNameIndex.Add(ctx, pp.Name, pp.Id) + hasNames = true + } + } + + // Sync the CID index to what was build from pins. This fixes any invalid + // indexes, which could happen if ipfs was terminated between writing pin + // and writing secondary index. + changed, err := dsindex.SyncIndex(ctx, tmpCidRIndex, p.cidRIndex) + if err != nil { + return fmt.Errorf("cannot sync indexes: %v", err) + } + if changed { + log.Info("invalid recursive indexes detected - rebuilt") + } + + changed, err = dsindex.SyncIndex(ctx, tmpCidDIndex, p.cidDIndex) + if err != nil { + return fmt.Errorf("cannot sync indexes: %v", err) + } + if changed { + log.Info("invalid direct indexes detected - rebuilt") + } + + if hasNames { + changed, err = dsindex.SyncIndex(ctx, tmpNameIndex, p.nameIndex) + if err != nil { + return fmt.Errorf("cannot sync name indexes: %v", err) + } + if changed { + log.Info("invalid name indexes detected - rebuilt") + } + } + + return p.Flush(ctx) +} + +// DirectKeys returns a slice containing the directly pinned keys +func (p *pinner) DirectKeys(ctx context.Context) ([]cid.Cid, error) { + p.lock.RLock() + defer p.lock.RUnlock() + + cidSet := cid.NewSet() + var e error + err := p.cidDIndex.ForEach(ctx, "", func(key, value string) bool { + var c cid.Cid + c, e = cid.Cast([]byte(key)) + if e != nil { + return false + } + cidSet.Add(c) + return true + }) + if err != nil { + return nil, err + } + if e != nil { + return nil, e + } + + return cidSet.Keys(), nil +} + +// RecursiveKeys returns a slice containing the recursively pinned keys +func (p *pinner) RecursiveKeys(ctx context.Context) ([]cid.Cid, error) { + p.lock.RLock() + defer p.lock.RUnlock() + + cidSet := cid.NewSet() + var e error + err := p.cidRIndex.ForEach(ctx, "", func(key, value string) bool { + var c cid.Cid + c, e = cid.Cast([]byte(key)) + if e != nil { + return false + } + cidSet.Add(c) + return true + }) + if err != nil { + return nil, err + } + if e != nil { + return nil, e + } + + return cidSet.Keys(), nil +} + +// InternalPins returns all cids kept pinned for the internal state of the +// pinner +func (p *pinner) InternalPins(ctx context.Context) ([]cid.Cid, error) { + return nil, nil +} + +// Update updates a recursive pin from one cid to another. This is equivalent +// to pinning the new one and unpinning the old one. +// +// TODO: This will not work when multiple pins are supported +func (p *pinner) Update(ctx context.Context, from, to cid.Cid, unpin bool) error { + p.lock.Lock() + defer p.lock.Unlock() + + found, err := p.cidRIndex.HasAny(ctx, from.KeyString()) + if err != nil { + return err + } + if !found { + return errors.New("'from' cid was not recursively pinned already") + } + + // If `from` already recursively pinned and `to` is the same, then all done + if from == to { + return nil + } + + // Check if the `to` cid is already recursively pinned + found, err = p.cidRIndex.HasAny(ctx, to.KeyString()) + if err != nil { + return err + } + if found { + return errors.New("'to' cid was already recursively pinned") + } + + // Temporarily unlock while we fetch the differences. + p.lock.Unlock() + err = dagutils.DiffEnumerate(ctx, p.dserv, from, to) + p.lock.Lock() + + if err != nil { + return err + } + + _, err = p.addPin(ctx, to, ipfspinner.Recursive, "") + if err != nil { + return err + } + + if !unpin { + return nil + } + + _, err = p.removePinsForCid(ctx, from, ipfspinner.Recursive) + if err != nil { + return err + } + + return nil +} + +// Flush encodes and writes pinner keysets to the datastore +func (p *pinner) Flush(ctx context.Context) error { + p.lock.Lock() + defer p.lock.Unlock() + + if syncDServ, ok := p.dserv.(syncDAGService); ok { + if err := syncDServ.Sync(); err != nil { + return fmt.Errorf("cannot sync pinned data: %v", err) + } + } + + // Sync pins and indexes + if err := p.dstore.Sync(ds.NewKey(basePath)); err != nil { + return fmt.Errorf("cannot sync pin state: %v", err) + } + + p.setDirty(ctx, false) + + return nil +} + +// PinWithMode allows the user to have fine grained control over pin +// counts +func (p *pinner) PinWithMode(c cid.Cid, mode ipfspinner.Mode) { + ctx := context.TODO() + + p.lock.Lock() + defer p.lock.Unlock() + + // TODO: remove his to support multiple pins per CID + switch mode { + case ipfspinner.Recursive: + if has, _ := p.cidRIndex.HasAny(ctx, c.KeyString()); has { + return // already a recursive pin for this CID + } + case ipfspinner.Direct: + if has, _ := p.cidDIndex.HasAny(ctx, c.KeyString()); has { + return // already a direct pin for this CID + } + default: + panic("unrecognized pin mode") + } + + _, err := p.addPin(ctx, c, mode, "") + if err != nil { + return + } +} + +// hasChild recursively looks for a Cid among the children of a root Cid. +// The visit function can be used to shortcut already-visited branches. +func hasChild(ctx context.Context, ng ipld.NodeGetter, root cid.Cid, child cid.Cid, visit func(cid.Cid) bool) (bool, error) { + links, err := ipld.GetLinks(ctx, ng, root) + if err != nil { + return false, err + } + for _, lnk := range links { + c := lnk.Cid + if lnk.Cid.Equals(child) { + return true, nil + } + if visit(c) { + has, err := hasChild(ctx, ng, c, child, visit) + if err != nil { + return false, err + } + + if has { + return has, nil + } + } + } + return false, nil +} + +func encodePin(p *pin) ([]byte, error) { + b, err := cbor.MarshalAtlased(p, pinAtl) + if err != nil { + return nil, err + } + return b, nil +} + +func decodePin(pid string, data []byte) (*pin, error) { + p := &pin{Id: pid} + err := cbor.UnmarshalAtlased(cbor.DecodeOptions{}, data, p, pinAtl) + if err != nil { + return nil, err + } + return p, nil +} + +// setDirty saves a boolean dirty flag in the datastore whenever there is a +// transition between a dirty (counter > 0) and non-dirty (counter == 0) state. +func (p *pinner) setDirty(ctx context.Context, dirty bool) { + isClean := p.dirty == p.clean + if dirty { + p.dirty++ + if !isClean { + return // do not save; was already dirty + } + } else if isClean { + return // already clean + } else { + p.clean = p.dirty // set clean + } + + // Do edge-triggered write to datastore + data := []byte{0} + if dirty { + data[0] = 1 + } + p.dstore.Put(dirtyKey, data) + p.dstore.Sync(dirtyKey) +} diff --git a/pinning/pinner/dspinner/pin_test.go b/pinning/pinner/dspinner/pin_test.go new file mode 100644 index 000000000..40e2c70ca --- /dev/null +++ b/pinning/pinner/dspinner/pin_test.go @@ -0,0 +1,1137 @@ +package dspinner + +import ( + "context" + "errors" + "fmt" + "io" + "testing" + "time" + + bs "github.com/ipfs/go-blockservice" + mdag "github.com/ipfs/go-merkledag" + + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + lds "github.com/ipfs/go-ds-leveldb" + blockstore "github.com/ipfs/go-ipfs-blockstore" + offline "github.com/ipfs/go-ipfs-exchange-offline" + ipfspin "github.com/ipfs/go-ipfs-pinner" + "github.com/ipfs/go-ipfs-pinner/ipldpinner" + util "github.com/ipfs/go-ipfs-util" + ipld "github.com/ipfs/go-ipld-format" + logging "github.com/ipfs/go-log" +) + +var rand = util.NewTimeSeededRand() + +type fakeLogger struct { + logging.StandardLogger + lastError error +} + +func (f *fakeLogger) Error(args ...interface{}) { + f.lastError = errors.New(fmt.Sprint(args...)) +} + +func (f *fakeLogger) Errorf(format string, args ...interface{}) { + f.lastError = fmt.Errorf(format, args...) +} + +func randNode() (*mdag.ProtoNode, cid.Cid) { + nd := new(mdag.ProtoNode) + nd.SetData(make([]byte, 32)) + _, err := io.ReadFull(rand, nd.Data()) + if err != nil { + panic(err) + } + k := nd.Cid() + return nd, k +} + +func assertPinned(t *testing.T, p ipfspin.Pinner, c cid.Cid, failmsg string) { + _, pinned, err := p.IsPinned(context.Background(), c) + if err != nil { + t.Fatal(err) + } + + if !pinned { + t.Fatal(failmsg) + } +} + +func assertPinnedWithType(t *testing.T, p ipfspin.Pinner, c cid.Cid, mode ipfspin.Mode, failmsg string) { + modeText, pinned, err := p.IsPinnedWithType(context.Background(), c, mode) + if err != nil { + t.Fatal(err) + } + + expect, ok := ipfspin.ModeToString(mode) + if !ok { + t.Fatal("unrecognized pin mode") + } + + if !pinned { + t.Fatal(failmsg) + } + + if mode == ipfspin.Any { + return + } + + if expect != modeText { + t.Fatal("expected", expect, "pin, got", modeText) + } +} + +func assertUnpinned(t *testing.T, p ipfspin.Pinner, c cid.Cid, failmsg string) { + _, pinned, err := p.IsPinned(context.Background(), c) + if err != nil { + t.Fatal(err) + } + + if pinned { + t.Fatal(failmsg) + } +} + +func TestPinnerBasic(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv := bs.New(bstore, offline.Exchange(bstore)) + + dserv := mdag.NewDAGService(bserv) + + p, err := New(ctx, dstore, dserv) + if err != nil { + t.Fatal(err) + } + + a, ak := randNode() + err = dserv.Add(ctx, a) + if err != nil { + t.Fatal(err) + } + + // Pin A{} + err = p.Pin(ctx, a, false) + if err != nil { + t.Fatal(err) + } + + assertPinned(t, p, ak, "Failed to find key") + assertPinnedWithType(t, p, ak, ipfspin.Direct, "Expected direct pin") + + // create new node c, to be indirectly pinned through b + c, _ := randNode() + err = dserv.Add(ctx, c) + if err != nil { + t.Fatal(err) + } + ck := c.Cid() + + // Create new node b, to be parent to a and c + b, _ := randNode() + err = b.AddNodeLink("child", a) + if err != nil { + t.Fatal(err) + } + err = b.AddNodeLink("otherchild", c) + if err != nil { + t.Fatal(err) + } + + err = dserv.Add(ctx, b) + if err != nil { + t.Fatal(err) + } + bk := b.Cid() + + // recursively pin B{A,C} + err = p.Pin(ctx, b, true) + if err != nil { + t.Fatal(err) + } + + assertPinned(t, p, ck, "child of recursively pinned node not found") + + assertPinned(t, p, bk, "Pinned node not found") + assertPinnedWithType(t, p, bk, ipfspin.Recursive, "Recursively pinned node not found") + + d, _ := randNode() + d.AddNodeLink("a", a) + d.AddNodeLink("c", c) + + e, _ := randNode() + d.AddNodeLink("e", e) + + // Must be in dagserv for unpin to work + err = dserv.Add(ctx, e) + if err != nil { + t.Fatal(err) + } + err = dserv.Add(ctx, d) + if err != nil { + t.Fatal(err) + } + + // Add D{A,C,E} + err = p.Pin(ctx, d, true) + if err != nil { + t.Fatal(err) + } + + dk := d.Cid() + assertPinned(t, p, dk, "pinned node not found.") + + cids, err := p.RecursiveKeys(ctx) + if err != nil { + t.Fatal(err) + } + if len(cids) != 2 { + t.Error("expected 2 recursive pins") + } + if !(bk == cids[0] || bk == cids[1]) { + t.Error("expected recursive pin of B") + } + if !(dk == cids[0] || dk == cids[1]) { + t.Error("expected recursive pin of D") + } + + pinned, err := p.CheckIfPinned(ctx, ak, bk, ck, dk) + if err != nil { + t.Fatal(err) + } + if len(pinned) != 4 { + t.Error("incorrect number of results") + } + for _, pn := range pinned { + switch pn.Key { + case ak: + if pn.Mode != ipfspin.Direct { + t.Error("A pinned with wrong mode") + } + case bk: + if pn.Mode != ipfspin.Recursive { + t.Error("B pinned with wrong mode") + } + case ck: + if pn.Mode != ipfspin.Indirect { + t.Error("C should be pinned indirectly") + } + if pn.Via != dk && pn.Via != bk { + t.Error("C should be pinned via D or B") + } + case dk: + if pn.Mode != ipfspin.Recursive { + t.Error("D pinned with wrong mode") + } + } + } + + cids, err = p.DirectKeys(ctx) + if err != nil { + t.Fatal(err) + } + if len(cids) != 1 { + t.Error("expected 1 direct pin") + } + if cids[0] != ak { + t.Error("wrong direct pin") + } + + cids, _ = p.InternalPins(ctx) + if len(cids) != 0 { + t.Error("shound not have internal keys") + } + + err = p.Unpin(ctx, dk, false) + if err == nil { + t.Fatal("expected error unpinning recursive pin without specifying recursive") + } + + // Test recursive unpin + err = p.Unpin(ctx, dk, true) + if err != nil { + t.Fatal(err) + } + + err = p.Unpin(ctx, dk, true) + if err != ErrNotPinned { + t.Fatal("expected error:", ErrNotPinned) + } + + err = p.Flush(ctx) + if err != nil { + t.Fatal(err) + } + + p, err = New(ctx, dstore, dserv) + if err != nil { + t.Fatal(err) + } + + // Test directly pinned + assertPinned(t, p, ak, "Could not find pinned node!") + + // Test recursively pinned + assertPinned(t, p, bk, "could not find recursively pinned node") + + // Remove the pin but not the index to simulate corruption + dsp := p.(*pinner) + ids, err := dsp.cidDIndex.Search(ctx, ak.KeyString()) + if err != nil { + t.Fatal(err) + } + if len(ids) == 0 { + t.Fatal("did not find pin for cid", ak.String()) + } + pp, err := dsp.loadPin(ctx, ids[0]) + if err != nil { + t.Fatal(err) + } + if pp.Mode != ipfspin.Direct { + t.Error("loaded pin has wrong mode") + } + if pp.Cid != ak { + t.Error("loaded pin has wrong cid") + } + err = dsp.dstore.Delete(pp.dsKey()) + if err != nil { + t.Fatal(err) + } + + realLog := log + fakeLog := &fakeLogger{} + fakeLog.StandardLogger = log + log = fakeLog + err = p.Pin(ctx, a, true) + if err != nil { + t.Fatal(err) + } + if fakeLog.lastError == nil { + t.Error("expected error to be logged") + } else if fakeLog.lastError.Error() != "found CID index with missing pin" { + t.Error("did not get expected log message") + } + + log = realLog +} + +func TestAddLoadPin(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv := bs.New(bstore, offline.Exchange(bstore)) + + dserv := mdag.NewDAGService(bserv) + + ipfsPin, err := New(ctx, dstore, dserv) + if err != nil { + t.Fatal(err) + } + + p := ipfsPin.(*pinner) + + a, ak := randNode() + dserv.Add(ctx, a) + + mode := ipfspin.Recursive + name := "my-pin" + pid, err := p.addPin(ctx, ak, mode, name) + if err != nil { + t.Fatal(err) + } + + // Load pin and check that data decoded correctly + pinData, err := p.loadPin(ctx, pid) + if err != nil { + t.Fatal(err) + } + if pinData.Mode != mode { + t.Error("worng pin mode") + } + if pinData.Cid != ak { + t.Error("wrong pin cid") + } + if pinData.Name != name { + t.Error("wrong pin name; expected", name, "got", pinData.Name) + } +} + +func TestRemovePinWithMode(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv := bs.New(bstore, offline.Exchange(bstore)) + + dserv := mdag.NewDAGService(bserv) + + p, err := New(ctx, dstore, dserv) + if err != nil { + t.Fatal(err) + } + + a, ak := randNode() + dserv.Add(ctx, a) + + p.Pin(ctx, a, false) + + ok, err := p.(*pinner).removePinsForCid(ctx, ak, ipfspin.Recursive) + if err != nil { + t.Fatal(err) + } + if ok { + t.Error("pin should not have been removed") + } + + p.RemovePinWithMode(ak, ipfspin.Direct) + + assertUnpinned(t, p, ak, "pin was not removed") +} + +func TestIsPinnedLookup(t *testing.T) { + // Test that lookups work in pins which share + // the same branches. For that construct this tree: + // + // A5->A4->A3->A2->A1->A0 + // / / + // B------- / + // \ / + // C--------------- + // + // This ensures that IsPinned works for all objects both when they + // are pinned and once they have been unpinned. + aBranchLen := 6 + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv := bs.New(bstore, offline.Exchange(bstore)) + + dserv := mdag.NewDAGService(bserv) + + // Create new pinner. New will not load anything since there are + // no pins saved in the datastore yet. + p, err := New(ctx, dstore, dserv) + if err != nil { + t.Fatal(err) + } + + aKeys, bk, ck, err := makeTree(ctx, aBranchLen, dserv, p) + if err != nil { + t.Fatal(err) + } + + assertPinned(t, p, aKeys[0], "A0 should be pinned") + assertPinned(t, p, aKeys[1], "A1 should be pinned") + assertPinned(t, p, ck, "C should be pinned") + assertPinned(t, p, bk, "B should be pinned") + + // Unpin A5 recursively + if err = p.Unpin(ctx, aKeys[5], true); err != nil { + t.Fatal(err) + } + + assertPinned(t, p, aKeys[0], "A0 should still be pinned through B") + assertUnpinned(t, p, aKeys[4], "A4 should be unpinned") + + // Unpin B recursively + if err = p.Unpin(ctx, bk, true); err != nil { + t.Fatal(err) + } + assertUnpinned(t, p, bk, "B should be unpinned") + assertUnpinned(t, p, aKeys[1], "A1 should be unpinned") + assertPinned(t, p, aKeys[0], "A0 should still be pinned through C") +} + +func TestDuplicateSemantics(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv := bs.New(bstore, offline.Exchange(bstore)) + + dserv := mdag.NewDAGService(bserv) + + p, err := New(ctx, dstore, dserv) + if err != nil { + t.Fatal(err) + } + + a, _ := randNode() + err = dserv.Add(ctx, a) + if err != nil { + t.Fatal(err) + } + + // pin is recursively + err = p.Pin(ctx, a, true) + if err != nil { + t.Fatal(err) + } + + // pinning directly should fail + err = p.Pin(ctx, a, false) + if err == nil { + t.Fatal("expected direct pin to fail") + } + + // pinning recursively again should succeed + err = p.Pin(ctx, a, true) + if err != nil { + t.Fatal(err) + } +} + +func TestFlush(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv := bs.New(bstore, offline.Exchange(bstore)) + + dserv := mdag.NewDAGService(bserv) + p, err := New(ctx, dstore, dserv) + if err != nil { + t.Fatal(err) + } + _, k := randNode() + + p.PinWithMode(k, ipfspin.Recursive) + if err = p.Flush(ctx); err != nil { + t.Fatal(err) + } + assertPinned(t, p, k, "expected key to still be pinned") +} + +func TestPinRecursiveFail(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv := bs.New(bstore, offline.Exchange(bstore)) + dserv := mdag.NewDAGService(bserv) + + p, err := New(ctx, dstore, dserv) + if err != nil { + t.Fatal(err) + } + + a, _ := randNode() + b, _ := randNode() + err = a.AddNodeLink("child", b) + if err != nil { + t.Fatal(err) + } + + // NOTE: This isnt a time based test, we expect the pin to fail + mctx, cancel := context.WithTimeout(ctx, time.Millisecond) + defer cancel() + + err = p.Pin(mctx, a, true) + if err == nil { + t.Fatal("should have failed to pin here") + } + + err = dserv.Add(ctx, b) + if err != nil { + t.Fatal(err) + } + + err = dserv.Add(ctx, a) + if err != nil { + t.Fatal(err) + } + + // this one is time based... but shouldnt cause any issues + mctx, cancel = context.WithTimeout(ctx, time.Second) + defer cancel() + err = p.Pin(mctx, a, true) + if err != nil { + t.Fatal(err) + } +} + +func TestPinUpdate(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv := bs.New(bstore, offline.Exchange(bstore)) + + dserv := mdag.NewDAGService(bserv) + p, err := New(ctx, dstore, dserv) + if err != nil { + t.Fatal(err) + } + n1, c1 := randNode() + n2, c2 := randNode() + _, c3 := randNode() + + if err = dserv.Add(ctx, n1); err != nil { + t.Fatal(err) + } + if err = dserv.Add(ctx, n2); err != nil { + t.Fatal(err) + } + + if err = p.Pin(ctx, n1, true); err != nil { + t.Fatal(err) + } + + if err = p.Update(ctx, c1, c2, true); err != nil { + t.Fatal(err) + } + + assertPinned(t, p, c2, "c2 should be pinned now") + assertUnpinned(t, p, c1, "c1 should no longer be pinned") + + if err = p.Update(ctx, c2, c1, false); err != nil { + t.Fatal(err) + } + + // Test updating same pin that is already pinned. + if err = p.Update(ctx, c2, c2, true); err != nil { + t.Fatal(err) + } + // Check that pin is still pinned. + _, ok, err := p.IsPinned(ctx, c2) + if err != nil { + t.Fatal(err) + } + if !ok { + t.Fatal("c2 should still be pinned") + } + + // Test updating same pin that is not pinned. + if err = p.Update(ctx, c3, c3, false); err == nil { + t.Fatal("expected error updating unpinned cid") + } + _, ok, err = p.IsPinned(ctx, c3) + if err != nil { + t.Fatal(err) + } + if ok { + t.Fatal("c3 should not be pinned") + } + + assertPinned(t, p, c2, "c2 should be pinned still") + assertPinned(t, p, c1, "c1 should be pinned now") +} + +func TestLoadDirty(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + bstore := blockstore.NewBlockstore(dstore) + bserv := bs.New(bstore, offline.Exchange(bstore)) + dserv := mdag.NewDAGService(bserv) + + p, err := New(ctx, dstore, dserv) + if err != nil { + t.Fatal(err) + } + + a, ak := randNode() + err = dserv.Add(ctx, a) + if err != nil { + t.Fatal(err) + } + + _, bk := randNode() + + err = p.Pin(ctx, a, true) + if err != nil { + t.Fatal(err) + } + + cidAKey := ak.KeyString() + cidBKey := bk.KeyString() + + // Corrupt index + cidRIndex := p.(*pinner).cidRIndex + cidRIndex.DeleteKey(ctx, cidAKey) + cidRIndex.Add(ctx, cidBKey, "not-a-pin-id") + + // Verify dirty + data, err := dstore.Get(dirtyKey) + if err != nil { + t.Fatalf("could not read dirty flag: %v", err) + } + if data[0] != 1 { + t.Fatal("dirty flag not set") + } + + has, err := cidRIndex.HasAny(ctx, cidAKey) + if err != nil { + t.Fatal(err) + } + if has { + t.Fatal("index should be deleted") + } + + // Create new pinner on same datastore that was never flushed. + p, err = New(ctx, dstore, dserv) + if err != nil { + t.Fatal(err) + } + + // Verify not dirty + data, err = dstore.Get(dirtyKey) + if err != nil { + t.Fatalf("could not read dirty flag: %v", err) + } + if data[0] != 0 { + t.Fatal("dirty flag is set") + } + + // Verify index rebuilt + cidRIndex = p.(*pinner).cidRIndex + has, err = cidRIndex.HasAny(ctx, cidAKey) + if err != nil { + t.Fatal(err) + } + if !has { + t.Fatal("index should have been rebuilt") + } + + has, err = cidRIndex.HasAny(ctx, cidBKey) + if err != nil { + t.Fatal(err) + } + if has { + t.Fatal("index should have been removed by rebuild") + } +} + +func TestEncodeDecodePin(t *testing.T) { + _, c := randNode() + + pin := newPin(c, ipfspin.Recursive, "testpin") + pin.Metadata = make(map[string]interface{}, 2) + pin.Metadata["hello"] = "world" + pin.Metadata["foo"] = "bar" + + encBytes, err := encodePin(pin) + if err != nil { + t.Fatal(err) + } + + decPin, err := decodePin(pin.Id, encBytes) + if err != nil { + t.Fatal(err) + } + + if decPin.Id != pin.Id { + t.Errorf("wrong pin id: expect %q got %q", pin.Id, decPin.Id) + } + if decPin.Cid != pin.Cid { + t.Errorf("wrong pin cid: expect %q got %q", pin.Cid.String(), decPin.Cid.String()) + } + if decPin.Mode != pin.Mode { + expect, _ := ipfspin.ModeToString(pin.Mode) + got, _ := ipfspin.ModeToString(decPin.Mode) + t.Errorf("wrong pin mode: expect %s got %s", expect, got) + } + if decPin.Name != pin.Name { + t.Errorf("wrong pin name: expect %q got %q", pin.Name, decPin.Name) + } + for key, val := range pin.Metadata { + dval, ok := decPin.Metadata[key] + if !ok { + t.Errorf("decoded pin missing metadata key %q", key) + } + if dval != val { + t.Errorf("wrong metadata value: expected %q got %q", val, dval) + } + } +} + +func makeTree(ctx context.Context, aBranchLen int, dserv ipld.DAGService, p ipfspin.Pinner) (aKeys []cid.Cid, bk cid.Cid, ck cid.Cid, err error) { + if aBranchLen < 3 { + err = errors.New("set aBranchLen to at least 3") + return + } + + aNodes := make([]*mdag.ProtoNode, aBranchLen) + aKeys = make([]cid.Cid, aBranchLen) + for i := 0; i < aBranchLen; i++ { + a, _ := randNode() + if i >= 1 { + if err = a.AddNodeLink("child", aNodes[i-1]); err != nil { + return + } + } + + if err = dserv.Add(ctx, a); err != nil { + return + } + aNodes[i] = a + aKeys[i] = a.Cid() + } + + // Pin last A recursively + if err = p.Pin(ctx, aNodes[aBranchLen-1], true); err != nil { + return + } + + // Create node B and add A3 as child + b, _ := randNode() + if err = b.AddNodeLink("mychild", aNodes[3]); err != nil { + return + } + + // Create C node + c, _ := randNode() + // Add A0 as child of C + if err = c.AddNodeLink("child", aNodes[0]); err != nil { + return + } + + // Add C + if err = dserv.Add(ctx, c); err != nil { + return + } + ck = c.Cid() + + // Add C to B and Add B + if err = b.AddNodeLink("myotherchild", c); err != nil { + return + } + if err = dserv.Add(ctx, b); err != nil { + return + } + bk = b.Cid() + + // Pin C recursively + if err = p.Pin(ctx, c, true); err != nil { + return + } + + // Pin B recursively + if err = p.Pin(ctx, b, true); err != nil { + return + } + + if err = p.Flush(ctx); err != nil { + return + } + + return +} + +func makeNodes(count int, dserv ipld.DAGService) []ipld.Node { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nodes := make([]ipld.Node, count) + for i := 0; i < count; i++ { + n, _ := randNode() + err := dserv.Add(ctx, n) + if err != nil { + panic(err) + } + nodes[i] = n + } + return nodes +} + +func pinNodes(nodes []ipld.Node, p ipfspin.Pinner, recursive bool) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + var err error + + for i := range nodes { + err = p.Pin(ctx, nodes[i], recursive) + if err != nil { + panic(err) + } + } + err = p.Flush(ctx) + if err != nil { + panic(err) + } +} + +func unpinNodes(nodes []ipld.Node, p ipfspin.Pinner) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + var err error + + for i := range nodes { + err = p.Unpin(ctx, nodes[i].Cid(), true) + if err != nil { + panic(err) + } + } + err = p.Flush(ctx) + if err != nil { + panic(err) + } +} + +type batchWrap struct { + ds.Datastore +} + +func (d *batchWrap) Batch() (ds.Batch, error) { + return ds.NewBasicBatch(d), nil +} + +func makeStore() (ds.Datastore, ipld.DAGService) { + ldstore, err := lds.NewDatastore("", nil) + if err != nil { + panic(err) + } + var dstore ds.Batching + dstore = &batchWrap{ldstore} + + bstore := blockstore.NewBlockstore(dstore) + bserv := bs.New(bstore, offline.Exchange(bstore)) + dserv := mdag.NewDAGService(bserv) + return dstore, dserv +} + +// BenchmarkLoadRebuild loads a pinner that has some number of saved pins, and +// compares the load time when rebuilding indexes to loading without rebuilding +// indexes. +func BenchmarkLoadRebuild(b *testing.B) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dstore, dserv := makeStore() + pinner, err := New(ctx, dstore, dserv) + if err != nil { + panic(err.Error()) + } + + nodes := makeNodes(4096, dserv) + pinNodes(nodes, pinner, true) + + b.Run("RebuildTrue", func(b *testing.B) { + for i := 0; i < b.N; i++ { + dstore.Put(dirtyKey, []byte{1}) + + _, err = New(ctx, dstore, dserv) + if err != nil { + panic(err.Error()) + } + } + }) + + b.Run("RebuildFalse", func(b *testing.B) { + for i := 0; i < b.N; i++ { + dstore.Put(dirtyKey, []byte{0}) + + _, err = New(ctx, dstore, dserv) + if err != nil { + panic(err.Error()) + } + } + }) +} + +// BenchmarkNthPins shows the time it takes to create/save 1 pin when a number +// of other pins already exist. Each run in the series shows performance for +// creating a pin in a larger number of existing pins. +func BenchmarkNthPin(b *testing.B) { + dstore, dserv := makeStore() + pinner, err := New(context.Background(), dstore, dserv) + if err != nil { + panic(err.Error()) + } + pinnerIPLD, err := ipldpinner.New(dstore, dserv, dserv) + if err != nil { + panic(err.Error()) + } + + for count := 1000; count <= 10000; count += 1000 { + b.Run(fmt.Sprint("PinDS-", count), func(b *testing.B) { + benchmarkNthPin(b, count, pinner, dserv) + }) + + b.Run(fmt.Sprint("PinIPLD-", count), func(b *testing.B) { + benchmarkNthPin(b, count, pinnerIPLD, dserv) + }) + } +} + +func benchmarkNthPin(b *testing.B, count int, pinner ipfspin.Pinner, dserv ipld.DAGService) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nodes := makeNodes(count, dserv) + pinNodes(nodes[:count-1], pinner, true) + b.ResetTimer() + + which := count - 1 + for i := 0; i < b.N; i++ { + // Pin the Nth node and Flush + err := pinner.Pin(ctx, nodes[which], true) + if err != nil { + panic(err) + } + err = pinner.Flush(ctx) + if err != nil { + panic(err) + } + // Unpin the nodes so that it can pinned next iter. + b.StopTimer() + err = pinner.Unpin(ctx, nodes[which].Cid(), true) + if err != nil { + panic(err) + } + err = pinner.Flush(ctx) + if err != nil { + panic(err) + } + b.StartTimer() + } +} + +// BenchmarkNPins demonstrates creating individual pins. Each run in the +// series shows performance for a larger number of individual pins. +func BenchmarkNPins(b *testing.B) { + for count := 128; count < 16386; count <<= 1 { + b.Run(fmt.Sprint("PinDS-", count), func(b *testing.B) { + dstore, dserv := makeStore() + pinner, err := New(context.Background(), dstore, dserv) + if err != nil { + panic(err.Error()) + } + benchmarkNPins(b, count, pinner, dserv) + }) + + b.Run(fmt.Sprint("PinIPLD-", count), func(b *testing.B) { + dstore, dserv := makeStore() + pinner, err := ipldpinner.New(dstore, dserv, dserv) + if err != nil { + panic(err.Error()) + } + benchmarkNPins(b, count, pinner, dserv) + }) + } +} + +func benchmarkNPins(b *testing.B, count int, pinner ipfspin.Pinner, dserv ipld.DAGService) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nodes := makeNodes(count, dserv) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + // Pin all the nodes one at a time. + for j := range nodes { + err := pinner.Pin(ctx, nodes[j], true) + if err != nil { + panic(err) + } + err = pinner.Flush(ctx) + if err != nil { + panic(err) + } + } + + // Unpin all nodes so that they can be pinned next iter. + b.StopTimer() + unpinNodes(nodes, pinner) + b.StartTimer() + } +} + +// BenchmarkNUnpins demonstrates unpinning individual pins. Each run in the +// series shows performance for a larger number of individual unpins. +func BenchmarkNUnpins(b *testing.B) { + for count := 128; count < 16386; count <<= 1 { + b.Run(fmt.Sprint("UnpinDS-", count), func(b *testing.B) { + dstore, dserv := makeStore() + pinner, err := New(context.Background(), dstore, dserv) + if err != nil { + panic(err.Error()) + } + benchmarkNUnpins(b, count, pinner, dserv) + }) + + b.Run(fmt.Sprint("UninIPLD-", count), func(b *testing.B) { + dstore, dserv := makeStore() + pinner, err := ipldpinner.New(dstore, dserv, dserv) + if err != nil { + panic(err.Error()) + } + benchmarkNUnpins(b, count, pinner, dserv) + }) + } +} + +func benchmarkNUnpins(b *testing.B, count int, pinner ipfspin.Pinner, dserv ipld.DAGService) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + nodes := makeNodes(count, dserv) + pinNodes(nodes, pinner, true) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + for j := range nodes { + // Unpin nodes one at a time. + err := pinner.Unpin(ctx, nodes[j].Cid(), true) + if err != nil { + panic(err) + } + err = pinner.Flush(ctx) + if err != nil { + panic(err) + } + } + // Pin all nodes so that they can be unpinned next iter. + b.StopTimer() + pinNodes(nodes, pinner, true) + b.StartTimer() + } +} + +// BenchmarkPinAllSeries shows times to pin all nodes with only one Flush at +// the end. +func BenchmarkPinAll(b *testing.B) { + for count := 128; count < 16386; count <<= 1 { + b.Run(fmt.Sprint("PinAllDS-", count), func(b *testing.B) { + dstore, dserv := makeStore() + pinner, err := New(context.Background(), dstore, dserv) + if err != nil { + panic(err) + } + benchmarkPinAll(b, count, pinner, dserv) + }) + + b.Run(fmt.Sprint("PinAllIPLD-", count), func(b *testing.B) { + dstore, dserv := makeStore() + pinner, err := ipldpinner.New(dstore, dserv, dserv) + if err != nil { + panic(err.Error()) + } + benchmarkPinAll(b, count, pinner, dserv) + }) + } +} + +func benchmarkPinAll(b *testing.B, count int, pinner ipfspin.Pinner, dserv ipld.DAGService) { + nodes := makeNodes(count, dserv) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + pinNodes(nodes, pinner, true) + + b.StopTimer() + unpinNodes(nodes, pinner) + b.StartTimer() + } +} diff --git a/pinning/pinner/ipldpinner/pin.go b/pinning/pinner/ipldpinner/pin.go new file mode 100644 index 000000000..d0824b349 --- /dev/null +++ b/pinning/pinner/ipldpinner/pin.go @@ -0,0 +1,528 @@ +// Package ipldpinner implements structures and methods to keep track of +// which objects a user wants to keep stored locally. This implementation +// stores pin information in a mdag structure. +package ipldpinner + +import ( + "context" + "fmt" + "os" + "sync" + "time" + + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + ipld "github.com/ipfs/go-ipld-format" + logging "github.com/ipfs/go-log" + mdag "github.com/ipfs/go-merkledag" + "github.com/ipfs/go-merkledag/dagutils" + + ipfspinner "github.com/ipfs/go-ipfs-pinner" +) + +const loadTimeout = 5 * time.Second + +var log = logging.Logger("pin") + +var pinDatastoreKey = ds.NewKey("/local/pins") + +var emptyKey cid.Cid + +var linkDirect, linkRecursive, linkInternal string + +func init() { + e, err := cid.Decode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n") + if err != nil { + log.Error("failed to decode empty key constant") + os.Exit(1) + } + emptyKey = e + + directStr, ok := ipfspinner.ModeToString(ipfspinner.Direct) + if !ok { + panic("could not find Direct pin enum") + } + linkDirect = directStr + + recursiveStr, ok := ipfspinner.ModeToString(ipfspinner.Recursive) + if !ok { + panic("could not find Recursive pin enum") + } + linkRecursive = recursiveStr + + internalStr, ok := ipfspinner.ModeToString(ipfspinner.Internal) + if !ok { + panic("could not find Internal pin enum") + } + linkInternal = internalStr +} + +// pinner implements the Pinner interface +type pinner struct { + lock sync.RWMutex + recursePin *cid.Set + directPin *cid.Set + + // Track the keys used for storing the pinning state, so gc does + // not delete them. + internalPin *cid.Set + dserv ipld.DAGService + internal ipld.DAGService // dagservice used to store internal objects + dstore ds.Datastore +} + +var _ ipfspinner.Pinner = (*pinner)(nil) + +type syncDAGService interface { + ipld.DAGService + Sync() error +} + +// New creates a new pinner using the given datastore as a backend, and loads +// the pinner's keysets from the datastore +func New(dstore ds.Datastore, dserv, internal ipld.DAGService) (*pinner, error) { + rootKey, err := dstore.Get(pinDatastoreKey) + if err != nil { + if err == ds.ErrNotFound { + return &pinner{ + recursePin: cid.NewSet(), + directPin: cid.NewSet(), + internalPin: cid.NewSet(), + dserv: dserv, + internal: internal, + dstore: dstore, + }, nil + } + return nil, err + } + rootCid, err := cid.Cast(rootKey) + if err != nil { + return nil, err + } + + ctx, cancel := context.WithTimeout(context.TODO(), loadTimeout) + defer cancel() + + root, err := internal.Get(ctx, rootCid) + if err != nil { + return nil, fmt.Errorf("cannot find pinning root object: %v", err) + } + + rootpb, ok := root.(*mdag.ProtoNode) + if !ok { + return nil, mdag.ErrNotProtobuf + } + + internalset := cid.NewSet() + internalset.Add(rootCid) + recordInternal := internalset.Add + + // load recursive set + recurseKeys, err := loadSet(ctx, internal, rootpb, linkRecursive, recordInternal) + if err != nil { + return nil, fmt.Errorf("cannot load recursive pins: %v", err) + } + + // load direct set + directKeys, err := loadSet(ctx, internal, rootpb, linkDirect, recordInternal) + if err != nil { + return nil, fmt.Errorf("cannot load direct pins: %v", err) + } + + return &pinner{ + // assign pinsets + recursePin: cidSetWithValues(recurseKeys), + directPin: cidSetWithValues(directKeys), + internalPin: internalset, + // assign services + dserv: dserv, + dstore: dstore, + internal: internal, + }, nil +} + +// Pin the given node, optionally recursive +func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { + err := p.dserv.Add(ctx, node) + if err != nil { + return err + } + + c := node.Cid() + + p.lock.Lock() + defer p.lock.Unlock() + + if recurse { + if p.recursePin.Has(c) { + return nil + } + + p.lock.Unlock() + // temporary unlock to fetch the entire graph + err := mdag.FetchGraph(ctx, c, p.dserv) + p.lock.Lock() + if err != nil { + return err + } + + if p.recursePin.Has(c) { + return nil + } + + if p.directPin.Has(c) { + p.directPin.Remove(c) + } + + p.recursePin.Add(c) + } else { + if p.recursePin.Has(c) { + return fmt.Errorf("%s already pinned recursively", c.String()) + } + + p.directPin.Add(c) + } + return nil +} + +// ErrNotPinned is returned when trying to unpin items which are not pinned. +var ErrNotPinned = fmt.Errorf("not pinned or pinned indirectly") + +// Unpin a given key +func (p *pinner) Unpin(ctx context.Context, c cid.Cid, recursive bool) error { + p.lock.Lock() + defer p.lock.Unlock() + if p.recursePin.Has(c) { + if !recursive { + return fmt.Errorf("%s is pinned recursively", c) + } + p.recursePin.Remove(c) + return nil + } + if p.directPin.Has(c) { + p.directPin.Remove(c) + return nil + } + return ErrNotPinned +} + +func (p *pinner) isInternalPin(c cid.Cid) bool { + return p.internalPin.Has(c) +} + +// IsPinned returns whether or not the given key is pinned +// and an explanation of why its pinned +func (p *pinner) IsPinned(ctx context.Context, c cid.Cid) (string, bool, error) { + p.lock.RLock() + defer p.lock.RUnlock() + return p.isPinnedWithType(ctx, c, ipfspinner.Any) +} + +// IsPinnedWithType returns whether or not the given cid is pinned with the +// given pin type, as well as returning the type of pin its pinned with. +func (p *pinner) IsPinnedWithType(ctx context.Context, c cid.Cid, mode ipfspinner.Mode) (string, bool, error) { + p.lock.RLock() + defer p.lock.RUnlock() + return p.isPinnedWithType(ctx, c, mode) +} + +// isPinnedWithType is the implementation of IsPinnedWithType that does not lock. +// intended for use by other pinned methods that already take locks +func (p *pinner) isPinnedWithType(ctx context.Context, c cid.Cid, mode ipfspinner.Mode) (string, bool, error) { + switch mode { + case ipfspinner.Any, ipfspinner.Direct, ipfspinner.Indirect, ipfspinner.Recursive, ipfspinner.Internal: + default: + err := fmt.Errorf("invalid Pin Mode '%d', must be one of {%d, %d, %d, %d, %d}", + mode, ipfspinner.Direct, ipfspinner.Indirect, ipfspinner.Recursive, ipfspinner.Internal, ipfspinner.Any) + return "", false, err + } + if (mode == ipfspinner.Recursive || mode == ipfspinner.Any) && p.recursePin.Has(c) { + return linkRecursive, true, nil + } + if mode == ipfspinner.Recursive { + return "", false, nil + } + + if (mode == ipfspinner.Direct || mode == ipfspinner.Any) && p.directPin.Has(c) { + return linkDirect, true, nil + } + if mode == ipfspinner.Direct { + return "", false, nil + } + + if (mode == ipfspinner.Internal || mode == ipfspinner.Any) && p.isInternalPin(c) { + return linkInternal, true, nil + } + if mode == ipfspinner.Internal { + return "", false, nil + } + + // Default is Indirect + visitedSet := cid.NewSet() + for _, rc := range p.recursePin.Keys() { + has, err := hasChild(ctx, p.dserv, rc, c, visitedSet.Visit) + if err != nil { + return "", false, err + } + if has { + return rc.String(), true, nil + } + } + return "", false, nil +} + +// CheckIfPinned Checks if a set of keys are pinned, more efficient than +// calling IsPinned for each key, returns the pinned status of cid(s) +func (p *pinner) CheckIfPinned(ctx context.Context, cids ...cid.Cid) ([]ipfspinner.Pinned, error) { + p.lock.RLock() + defer p.lock.RUnlock() + pinned := make([]ipfspinner.Pinned, 0, len(cids)) + toCheck := cid.NewSet() + + // First check for non-Indirect pins directly + for _, c := range cids { + if p.recursePin.Has(c) { + pinned = append(pinned, ipfspinner.Pinned{Key: c, Mode: ipfspinner.Recursive}) + } else if p.directPin.Has(c) { + pinned = append(pinned, ipfspinner.Pinned{Key: c, Mode: ipfspinner.Direct}) + } else if p.isInternalPin(c) { + pinned = append(pinned, ipfspinner.Pinned{Key: c, Mode: ipfspinner.Internal}) + } else { + toCheck.Add(c) + } + } + + // Now walk all recursive pins to check for indirect pins + var checkChildren func(cid.Cid, cid.Cid) error + checkChildren = func(rk, parentKey cid.Cid) error { + links, err := ipld.GetLinks(ctx, p.dserv, parentKey) + if err != nil { + return err + } + for _, lnk := range links { + c := lnk.Cid + + if toCheck.Has(c) { + pinned = append(pinned, + ipfspinner.Pinned{Key: c, Mode: ipfspinner.Indirect, Via: rk}) + toCheck.Remove(c) + } + + err := checkChildren(rk, c) + if err != nil { + return err + } + + if toCheck.Len() == 0 { + return nil + } + } + return nil + } + + for _, rk := range p.recursePin.Keys() { + err := checkChildren(rk, rk) + if err != nil { + return nil, err + } + if toCheck.Len() == 0 { + break + } + } + + // Anything left in toCheck is not pinned + for _, k := range toCheck.Keys() { + pinned = append(pinned, ipfspinner.Pinned{Key: k, Mode: ipfspinner.NotPinned}) + } + + return pinned, nil +} + +// RemovePinWithMode is for manually editing the pin structure. +// Use with care! If used improperly, garbage collection may not +// be successful. +func (p *pinner) RemovePinWithMode(c cid.Cid, mode ipfspinner.Mode) { + p.lock.Lock() + defer p.lock.Unlock() + switch mode { + case ipfspinner.Direct: + p.directPin.Remove(c) + case ipfspinner.Recursive: + p.recursePin.Remove(c) + default: + // programmer error, panic OK + panic("unrecognized pin type") + } +} + +func cidSetWithValues(cids []cid.Cid) *cid.Set { + out := cid.NewSet() + for _, c := range cids { + out.Add(c) + } + return out +} + +// DirectKeys returns a slice containing the directly pinned keys +func (p *pinner) DirectKeys(ctx context.Context) ([]cid.Cid, error) { + p.lock.RLock() + defer p.lock.RUnlock() + + return p.directPin.Keys(), nil +} + +// RecursiveKeys returns a slice containing the recursively pinned keys +func (p *pinner) RecursiveKeys(ctx context.Context) ([]cid.Cid, error) { + p.lock.RLock() + defer p.lock.RUnlock() + + return p.recursePin.Keys(), nil +} + +// Update updates a recursive pin from one cid to another +// this is more efficient than simply pinning the new one and unpinning the +// old one +func (p *pinner) Update(ctx context.Context, from, to cid.Cid, unpin bool) error { + if from == to { + // Nothing to do. Don't remove this check or we'll end up + // _removing_ the pin. + // + // See #6648 + return nil + } + + p.lock.Lock() + defer p.lock.Unlock() + + if !p.recursePin.Has(from) { + return fmt.Errorf("'from' cid was not recursively pinned already") + } + + // Temporarily unlock while we fetch the differences. + p.lock.Unlock() + err := dagutils.DiffEnumerate(ctx, p.dserv, from, to) + p.lock.Lock() + + if err != nil { + return err + } + + p.recursePin.Add(to) + if unpin { + p.recursePin.Remove(from) + } + return nil +} + +// Flush encodes and writes pinner keysets to the datastore +func (p *pinner) Flush(ctx context.Context) error { + p.lock.Lock() + defer p.lock.Unlock() + + internalset := cid.NewSet() + recordInternal := internalset.Add + + root := &mdag.ProtoNode{} + { + n, err := storeSet(ctx, p.internal, p.directPin.Keys(), recordInternal) + if err != nil { + return err + } + if err := root.AddNodeLink(linkDirect, n); err != nil { + return err + } + } + + { + n, err := storeSet(ctx, p.internal, p.recursePin.Keys(), recordInternal) + if err != nil { + return err + } + if err := root.AddNodeLink(linkRecursive, n); err != nil { + return err + } + } + + // add the empty node, its referenced by the pin sets but never created + err := p.internal.Add(ctx, new(mdag.ProtoNode)) + if err != nil { + return err + } + + err = p.internal.Add(ctx, root) + if err != nil { + return err + } + + k := root.Cid() + + internalset.Add(k) + + if syncDServ, ok := p.dserv.(syncDAGService); ok { + if err := syncDServ.Sync(); err != nil { + return fmt.Errorf("cannot sync pinned data: %v", err) + } + } + + if syncInternal, ok := p.internal.(syncDAGService); ok { + if err := syncInternal.Sync(); err != nil { + return fmt.Errorf("cannot sync pinning data: %v", err) + } + } + + if err := p.dstore.Put(pinDatastoreKey, k.Bytes()); err != nil { + return fmt.Errorf("cannot store pin state: %v", err) + } + if err := p.dstore.Sync(pinDatastoreKey); err != nil { + return fmt.Errorf("cannot sync pin state: %v", err) + } + p.internalPin = internalset + return nil +} + +// InternalPins returns all cids kept pinned for the internal state of the +// pinner +func (p *pinner) InternalPins(ctx context.Context) ([]cid.Cid, error) { + p.lock.Lock() + defer p.lock.Unlock() + return p.internalPin.Keys(), nil +} + +// PinWithMode allows the user to have fine grained control over pin +// counts +func (p *pinner) PinWithMode(c cid.Cid, mode ipfspinner.Mode) { + p.lock.Lock() + defer p.lock.Unlock() + switch mode { + case ipfspinner.Recursive: + p.recursePin.Add(c) + case ipfspinner.Direct: + p.directPin.Add(c) + } +} + +// hasChild recursively looks for a Cid among the children of a root Cid. +// The visit function can be used to shortcut already-visited branches. +func hasChild(ctx context.Context, ng ipld.NodeGetter, root cid.Cid, child cid.Cid, visit func(cid.Cid) bool) (bool, error) { + links, err := ipld.GetLinks(ctx, ng, root) + if err != nil { + return false, err + } + for _, lnk := range links { + c := lnk.Cid + if lnk.Cid.Equals(child) { + return true, nil + } + if visit(c) { + has, err := hasChild(ctx, ng, c, child, visit) + if err != nil { + return false, err + } + + if has { + return has, nil + } + } + } + return false, nil +} diff --git a/pinning/pinner/pin_test.go b/pinning/pinner/ipldpinner/pin_test.go similarity index 90% rename from pinning/pinner/pin_test.go rename to pinning/pinner/ipldpinner/pin_test.go index e477ac07f..e193aa96c 100644 --- a/pinning/pinner/pin_test.go +++ b/pinning/pinner/ipldpinner/pin_test.go @@ -1,4 +1,4 @@ -package pin +package ipldpinner import ( "context" @@ -14,6 +14,7 @@ import ( dssync "github.com/ipfs/go-datastore/sync" blockstore "github.com/ipfs/go-ipfs-blockstore" offline "github.com/ipfs/go-ipfs-exchange-offline" + pin "github.com/ipfs/go-ipfs-pinner" util "github.com/ipfs/go-ipfs-util" ) @@ -30,7 +31,7 @@ func randNode() (*mdag.ProtoNode, cid.Cid) { return nd, k } -func assertPinned(t *testing.T, p Pinner, c cid.Cid, failmsg string) { +func assertPinned(t *testing.T, p pin.Pinner, c cid.Cid, failmsg string) { _, pinned, err := p.IsPinned(context.Background(), c) if err != nil { t.Fatal(err) @@ -41,7 +42,7 @@ func assertPinned(t *testing.T, p Pinner, c cid.Cid, failmsg string) { } } -func assertUnpinned(t *testing.T, p Pinner, c cid.Cid, failmsg string) { +func assertUnpinned(t *testing.T, p pin.Pinner, c cid.Cid, failmsg string) { _, pinned, err := p.IsPinned(context.Background(), c) if err != nil { t.Fatal(err) @@ -62,10 +63,13 @@ func TestPinnerBasic(t *testing.T) { dserv := mdag.NewDAGService(bserv) // TODO does pinner need to share datastore with blockservice? - p := NewPinner(dstore, dserv, dserv) + p, err := New(dstore, dserv, dserv) + if err != nil { + t.Fatal(err) + } a, ak := randNode() - err := dserv.Add(ctx, a) + err = dserv.Add(ctx, a) if err != nil { t.Fatal(err) } @@ -151,7 +155,7 @@ func TestPinnerBasic(t *testing.T) { t.Fatal(err) } - np, err := LoadPinner(dstore, dserv, dserv) + np, err := New(dstore, dserv, dserv) if err != nil { t.Fatal(err) } @@ -188,7 +192,10 @@ func TestIsPinnedLookup(t *testing.T) { dserv := mdag.NewDAGService(bserv) // TODO does pinner need to share datastore with blockservice? - p := NewPinner(dstore, dserv, dserv) + p, err := New(dstore, dserv, dserv) + if err != nil { + t.Fatal(err) + } aNodes := make([]*mdag.ProtoNode, aBranchLen) aKeys := make([]cid.Cid, aBranchLen) @@ -229,7 +236,7 @@ func TestIsPinnedLookup(t *testing.T) { } // Add C - err := dserv.Add(ctx, c) + err = dserv.Add(ctx, c) if err != nil { t.Fatal(err) } @@ -289,11 +296,13 @@ func TestDuplicateSemantics(t *testing.T) { dserv := mdag.NewDAGService(bserv) - // TODO does pinner need to share datastore with blockservice? - p := NewPinner(dstore, dserv, dserv) + p, err := New(dstore, dserv, dserv) + if err != nil { + t.Fatal(err) + } a, _ := randNode() - err := dserv.Add(ctx, a) + err = dserv.Add(ctx, a) if err != nil { t.Fatal(err) } @@ -323,10 +332,13 @@ func TestFlush(t *testing.T) { bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) - p := NewPinner(dstore, dserv, dserv) + p, err := New(dstore, dserv, dserv) + if err != nil { + t.Fatal(err) + } _, k := randNode() - p.PinWithMode(k, Recursive) + p.PinWithMode(k, pin.Recursive) if err := p.Flush(context.Background()); err != nil { t.Fatal(err) } @@ -340,11 +352,14 @@ func TestPinRecursiveFail(t *testing.T) { bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) - p := NewPinner(dstore, dserv, dserv) + p, err := New(dstore, dserv, dserv) + if err != nil { + t.Fatal(err) + } a, _ := randNode() b, _ := randNode() - err := a.AddNodeLink("child", b) + err = a.AddNodeLink("child", b) if err != nil { t.Fatal(err) } @@ -385,7 +400,10 @@ func TestPinUpdate(t *testing.T) { bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) - p := NewPinner(dstore, dserv, dserv) + p, err := New(dstore, dserv, dserv) + if err != nil { + t.Fatal(err) + } n1, c1 := randNode() n2, c2 := randNode() diff --git a/pinning/pinner/set.go b/pinning/pinner/ipldpinner/set.go similarity index 94% rename from pinning/pinner/set.go rename to pinning/pinner/ipldpinner/set.go index ca437974f..2fb931f93 100644 --- a/pinning/pinner/set.go +++ b/pinning/pinner/ipldpinner/set.go @@ -1,4 +1,4 @@ -package pin +package ipldpinner import ( "bytes" @@ -55,9 +55,14 @@ func (s sortByHash) Swap(a, b int) { } func storeItems(ctx context.Context, dag ipld.DAGService, estimatedLen uint64, depth uint32, iter itemIterator, internalKeys keyObserver) (*merkledag.ProtoNode, error) { - links := make([]*ipld.Link, 0, defaultFanout+maxItems) + // Each node wastes up to defaultFanout in empty links. + var leafLinks uint64 + if estimatedLen < maxItems { + leafLinks = estimatedLen + } + links := make([]*ipld.Link, defaultFanout, defaultFanout+leafLinks) for i := 0; i < defaultFanout; i++ { - links = append(links, &ipld.Link{Cid: emptyKey}) + links[i] = &ipld.Link{Cid: emptyKey} } // add emptyKey to our set of internal pinset objects @@ -97,7 +102,7 @@ func storeItems(ctx context.Context, dag ipld.DAGService, estimatedLen uint64, d sort.Stable(s) } - hashed := make([][]cid.Cid, defaultFanout) + var hashed [][]cid.Cid for { // This loop essentially enumerates every single item in the set // and maps them all into a set of buckets. Each bucket will be recursively @@ -116,6 +121,9 @@ func storeItems(ctx context.Context, dag ipld.DAGService, estimatedLen uint64, d if !ok { break } + if hashed == nil { + hashed = make([][]cid.Cid, defaultFanout) + } h := hash(depth, k) % defaultFanout hashed[h] = append(hashed[h], k) } diff --git a/pinning/pinner/set_test.go b/pinning/pinner/ipldpinner/set_test.go similarity index 99% rename from pinning/pinner/set_test.go rename to pinning/pinner/ipldpinner/set_test.go index 61a3118b2..0f32e6b5e 100644 --- a/pinning/pinner/set_test.go +++ b/pinning/pinner/ipldpinner/set_test.go @@ -1,4 +1,4 @@ -package pin +package ipldpinner import ( "context" diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index aa74c5185..7e1d88602 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -5,33 +5,14 @@ package pin import ( "context" "fmt" - "os" - "sync" - "time" cid "github.com/ipfs/go-cid" - ds "github.com/ipfs/go-datastore" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" - mdag "github.com/ipfs/go-merkledag" - "github.com/ipfs/go-merkledag/dagutils" ) var log = logging.Logger("pin") -var pinDatastoreKey = ds.NewKey("/local/pins") - -var emptyKey cid.Cid - -func init() { - e, err := cid.Decode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n") - if err != nil { - log.Error("failed to decode empty key constant") - os.Exit(1) - } - emptyKey = e -} - const ( linkRecursive = "recursive" linkDirect = "direct" @@ -177,482 +158,3 @@ func (p Pinned) String() string { return fmt.Sprintf("pinned: %s", modeStr) } } - -// pinner implements the Pinner interface -type pinner struct { - lock sync.RWMutex - recursePin *cid.Set - directPin *cid.Set - - // Track the keys used for storing the pinning state, so gc does - // not delete them. - internalPin *cid.Set - dserv ipld.DAGService - internal ipld.DAGService // dagservice used to store internal objects - dstore ds.Datastore -} - -type syncDAGService interface { - ipld.DAGService - Sync() error -} - -// NewPinner creates a new pinner using the given datastore as a backend -func NewPinner(dstore ds.Datastore, serv, internal ipld.DAGService) Pinner { - - rcset := cid.NewSet() - dirset := cid.NewSet() - - return &pinner{ - recursePin: rcset, - directPin: dirset, - dserv: serv, - dstore: dstore, - internal: internal, - internalPin: cid.NewSet(), - } -} - -// Pin the given node, optionally recursive -func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { - err := p.dserv.Add(ctx, node) - if err != nil { - return err - } - - c := node.Cid() - - p.lock.Lock() - defer p.lock.Unlock() - - if recurse { - if p.recursePin.Has(c) { - return nil - } - - p.lock.Unlock() - // temporary unlock to fetch the entire graph - err := mdag.FetchGraph(ctx, c, p.dserv) - p.lock.Lock() - if err != nil { - return err - } - - if p.recursePin.Has(c) { - return nil - } - - if p.directPin.Has(c) { - p.directPin.Remove(c) - } - - p.recursePin.Add(c) - } else { - if p.recursePin.Has(c) { - return fmt.Errorf("%s already pinned recursively", c.String()) - } - - p.directPin.Add(c) - } - return nil -} - -// ErrNotPinned is returned when trying to unpin items which are not pinned. -var ErrNotPinned = fmt.Errorf("not pinned or pinned indirectly") - -// Unpin a given key -func (p *pinner) Unpin(ctx context.Context, c cid.Cid, recursive bool) error { - p.lock.Lock() - defer p.lock.Unlock() - if p.recursePin.Has(c) { - if !recursive { - return fmt.Errorf("%s is pinned recursively", c) - } - p.recursePin.Remove(c) - return nil - } - if p.directPin.Has(c) { - p.directPin.Remove(c) - return nil - } - return ErrNotPinned -} - -func (p *pinner) isInternalPin(c cid.Cid) bool { - return p.internalPin.Has(c) -} - -// IsPinned returns whether or not the given key is pinned -// and an explanation of why its pinned -func (p *pinner) IsPinned(ctx context.Context, c cid.Cid) (string, bool, error) { - p.lock.RLock() - defer p.lock.RUnlock() - return p.isPinnedWithType(ctx, c, Any) -} - -// IsPinnedWithType returns whether or not the given cid is pinned with the -// given pin type, as well as returning the type of pin its pinned with. -func (p *pinner) IsPinnedWithType(ctx context.Context, c cid.Cid, mode Mode) (string, bool, error) { - p.lock.RLock() - defer p.lock.RUnlock() - return p.isPinnedWithType(ctx, c, mode) -} - -// isPinnedWithType is the implementation of IsPinnedWithType that does not lock. -// intended for use by other pinned methods that already take locks -func (p *pinner) isPinnedWithType(ctx context.Context, c cid.Cid, mode Mode) (string, bool, error) { - switch mode { - case Any, Direct, Indirect, Recursive, Internal: - default: - err := fmt.Errorf("invalid Pin Mode '%d', must be one of {%d, %d, %d, %d, %d}", - mode, Direct, Indirect, Recursive, Internal, Any) - return "", false, err - } - if (mode == Recursive || mode == Any) && p.recursePin.Has(c) { - return linkRecursive, true, nil - } - if mode == Recursive { - return "", false, nil - } - - if (mode == Direct || mode == Any) && p.directPin.Has(c) { - return linkDirect, true, nil - } - if mode == Direct { - return "", false, nil - } - - if (mode == Internal || mode == Any) && p.isInternalPin(c) { - return linkInternal, true, nil - } - if mode == Internal { - return "", false, nil - } - - // Default is Indirect - visitedSet := cid.NewSet() - for _, rc := range p.recursePin.Keys() { - has, err := hasChild(ctx, p.dserv, rc, c, visitedSet.Visit) - if err != nil { - return "", false, err - } - if has { - return rc.String(), true, nil - } - } - return "", false, nil -} - -// CheckIfPinned Checks if a set of keys are pinned, more efficient than -// calling IsPinned for each key, returns the pinned status of cid(s) -func (p *pinner) CheckIfPinned(ctx context.Context, cids ...cid.Cid) ([]Pinned, error) { - p.lock.RLock() - defer p.lock.RUnlock() - pinned := make([]Pinned, 0, len(cids)) - toCheck := cid.NewSet() - - // First check for non-Indirect pins directly - for _, c := range cids { - if p.recursePin.Has(c) { - pinned = append(pinned, Pinned{Key: c, Mode: Recursive}) - } else if p.directPin.Has(c) { - pinned = append(pinned, Pinned{Key: c, Mode: Direct}) - } else if p.isInternalPin(c) { - pinned = append(pinned, Pinned{Key: c, Mode: Internal}) - } else { - toCheck.Add(c) - } - } - - // Now walk all recursive pins to check for indirect pins - var checkChildren func(cid.Cid, cid.Cid) error - checkChildren = func(rk, parentKey cid.Cid) error { - links, err := ipld.GetLinks(ctx, p.dserv, parentKey) - if err != nil { - return err - } - for _, lnk := range links { - c := lnk.Cid - - if toCheck.Has(c) { - pinned = append(pinned, - Pinned{Key: c, Mode: Indirect, Via: rk}) - toCheck.Remove(c) - } - - err := checkChildren(rk, c) - if err != nil { - return err - } - - if toCheck.Len() == 0 { - return nil - } - } - return nil - } - - for _, rk := range p.recursePin.Keys() { - err := checkChildren(rk, rk) - if err != nil { - return nil, err - } - if toCheck.Len() == 0 { - break - } - } - - // Anything left in toCheck is not pinned - for _, k := range toCheck.Keys() { - pinned = append(pinned, Pinned{Key: k, Mode: NotPinned}) - } - - return pinned, nil -} - -// RemovePinWithMode is for manually editing the pin structure. -// Use with care! If used improperly, garbage collection may not -// be successful. -func (p *pinner) RemovePinWithMode(c cid.Cid, mode Mode) { - p.lock.Lock() - defer p.lock.Unlock() - switch mode { - case Direct: - p.directPin.Remove(c) - case Recursive: - p.recursePin.Remove(c) - default: - // programmer error, panic OK - panic("unrecognized pin type") - } -} - -func cidSetWithValues(cids []cid.Cid) *cid.Set { - out := cid.NewSet() - for _, c := range cids { - out.Add(c) - } - return out -} - -// LoadPinner loads a pinner and its keysets from the given datastore -func LoadPinner(d ds.Datastore, dserv, internal ipld.DAGService) (Pinner, error) { - p := new(pinner) - - rootKey, err := d.Get(pinDatastoreKey) - if err != nil { - return nil, fmt.Errorf("cannot load pin state: %v", err) - } - rootCid, err := cid.Cast(rootKey) - if err != nil { - return nil, err - } - - ctx, cancel := context.WithTimeout(context.TODO(), time.Second*5) - defer cancel() - - root, err := internal.Get(ctx, rootCid) - if err != nil { - return nil, fmt.Errorf("cannot find pinning root object: %v", err) - } - - rootpb, ok := root.(*mdag.ProtoNode) - if !ok { - return nil, mdag.ErrNotProtobuf - } - - internalset := cid.NewSet() - internalset.Add(rootCid) - recordInternal := internalset.Add - - { // load recursive set - recurseKeys, err := loadSet(ctx, internal, rootpb, linkRecursive, recordInternal) - if err != nil { - return nil, fmt.Errorf("cannot load recursive pins: %v", err) - } - p.recursePin = cidSetWithValues(recurseKeys) - } - - { // load direct set - directKeys, err := loadSet(ctx, internal, rootpb, linkDirect, recordInternal) - if err != nil { - return nil, fmt.Errorf("cannot load direct pins: %v", err) - } - p.directPin = cidSetWithValues(directKeys) - } - - p.internalPin = internalset - - // assign services - p.dserv = dserv - p.dstore = d - p.internal = internal - - return p, nil -} - -// DirectKeys returns a slice containing the directly pinned keys -func (p *pinner) DirectKeys(ctx context.Context) ([]cid.Cid, error) { - p.lock.RLock() - defer p.lock.RUnlock() - - return p.directPin.Keys(), nil -} - -// RecursiveKeys returns a slice containing the recursively pinned keys -func (p *pinner) RecursiveKeys(ctx context.Context) ([]cid.Cid, error) { - p.lock.RLock() - defer p.lock.RUnlock() - - return p.recursePin.Keys(), nil -} - -// Update updates a recursive pin from one cid to another -// this is more efficient than simply pinning the new one and unpinning the -// old one -func (p *pinner) Update(ctx context.Context, from, to cid.Cid, unpin bool) error { - if from == to { - // Nothing to do. Don't remove this check or we'll end up - // _removing_ the pin. - // - // See #6648 - return nil - } - - p.lock.Lock() - defer p.lock.Unlock() - - if !p.recursePin.Has(from) { - return fmt.Errorf("'from' cid was not recursively pinned already") - } - - // Temporarily unlock while we fetch the differences. - p.lock.Unlock() - err := dagutils.DiffEnumerate(ctx, p.dserv, from, to) - p.lock.Lock() - - if err != nil { - return err - } - - p.recursePin.Add(to) - if unpin { - p.recursePin.Remove(from) - } - return nil -} - -// Flush encodes and writes pinner keysets to the datastore -func (p *pinner) Flush(ctx context.Context) error { - p.lock.Lock() - defer p.lock.Unlock() - - internalset := cid.NewSet() - recordInternal := internalset.Add - - root := &mdag.ProtoNode{} - { - n, err := storeSet(ctx, p.internal, p.directPin.Keys(), recordInternal) - if err != nil { - return err - } - if err := root.AddNodeLink(linkDirect, n); err != nil { - return err - } - } - - { - n, err := storeSet(ctx, p.internal, p.recursePin.Keys(), recordInternal) - if err != nil { - return err - } - if err := root.AddNodeLink(linkRecursive, n); err != nil { - return err - } - } - - // add the empty node, its referenced by the pin sets but never created - err := p.internal.Add(ctx, new(mdag.ProtoNode)) - if err != nil { - return err - } - - err = p.internal.Add(ctx, root) - if err != nil { - return err - } - - k := root.Cid() - - internalset.Add(k) - - if syncDServ, ok := p.dserv.(syncDAGService); ok { - if err := syncDServ.Sync(); err != nil { - return fmt.Errorf("cannot sync pinned data: %v", err) - } - } - - if syncInternal, ok := p.internal.(syncDAGService); ok { - if err := syncInternal.Sync(); err != nil { - return fmt.Errorf("cannot sync pinning data: %v", err) - } - } - - if err := p.dstore.Put(pinDatastoreKey, k.Bytes()); err != nil { - return fmt.Errorf("cannot store pin state: %v", err) - } - if err := p.dstore.Sync(pinDatastoreKey); err != nil { - return fmt.Errorf("cannot sync pin state: %v", err) - } - p.internalPin = internalset - return nil -} - -// InternalPins returns all cids kept pinned for the internal state of the -// pinner -func (p *pinner) InternalPins(ctx context.Context) ([]cid.Cid, error) { - p.lock.Lock() - defer p.lock.Unlock() - var out []cid.Cid - out = append(out, p.internalPin.Keys()...) - return out, nil -} - -// PinWithMode allows the user to have fine grained control over pin -// counts -func (p *pinner) PinWithMode(c cid.Cid, mode Mode) { - p.lock.Lock() - defer p.lock.Unlock() - switch mode { - case Recursive: - p.recursePin.Add(c) - case Direct: - p.directPin.Add(c) - } -} - -// hasChild recursively looks for a Cid among the children of a root Cid. -// The visit function can be used to shortcut already-visited branches. -func hasChild(ctx context.Context, ng ipld.NodeGetter, root cid.Cid, child cid.Cid, visit func(cid.Cid) bool) (bool, error) { - links, err := ipld.GetLinks(ctx, ng, root) - if err != nil { - return false, err - } - for _, lnk := range links { - c := lnk.Cid - if lnk.Cid.Equals(child) { - return true, nil - } - if visit(c) { - has, err := hasChild(ctx, ng, c, child, visit) - if err != nil { - return false, err - } - - if has { - return has, nil - } - } - } - return false, nil -} diff --git a/pinning/pinner/pinconv/pinconv.go b/pinning/pinner/pinconv/pinconv.go new file mode 100644 index 000000000..9aee703a7 --- /dev/null +++ b/pinning/pinner/pinconv/pinconv.go @@ -0,0 +1,128 @@ +// Package pinconv converts pins between the dag-based ipldpinner and the +// datastore-based dspinner. Once conversion is complete, the pins from the +// source pinner are removed. +package pinconv + +import ( + "context" + "fmt" + + "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + ipfspinner "github.com/ipfs/go-ipfs-pinner" + "github.com/ipfs/go-ipfs-pinner/dspinner" + "github.com/ipfs/go-ipfs-pinner/ipldpinner" + ipld "github.com/ipfs/go-ipld-format" +) + +// ConvertPinsFromIPLDToDS converts pins stored in mdag based storage to pins +// stores in the datastore. Returns a dspinner loaded with the converted pins, +// and a count of the recursive and direct pins converted. +// +// After pins are stored in datastore, the root pin key is deleted to unlink +// the pin data in the DAGService. +func ConvertPinsFromIPLDToDS(ctx context.Context, dstore ds.Datastore, dserv ipld.DAGService, internal ipld.DAGService) (ipfspinner.Pinner, int, error) { + const ipldPinPath = "/local/pins" + + ipldPinner, err := ipldpinner.New(dstore, dserv, internal) + if err != nil { + return nil, 0, err + } + + dsPinner, err := dspinner.New(ctx, dstore, dserv) + if err != nil { + return nil, 0, err + } + + seen := cid.NewSet() + cids, err := ipldPinner.RecursiveKeys(ctx) + if err != nil { + return nil, 0, err + } + for i := range cids { + seen.Add(cids[i]) + dsPinner.PinWithMode(cids[i], ipfspinner.Recursive) + } + convCount := len(cids) + + cids, err = ipldPinner.DirectKeys(ctx) + if err != nil { + return nil, 0, err + } + for i := range cids { + if seen.Has(cids[i]) { + // Pin was already pinned recursively + continue + } + dsPinner.PinWithMode(cids[i], ipfspinner.Direct) + } + convCount += len(cids) + + err = dsPinner.Flush(ctx) + if err != nil { + return nil, 0, err + } + + // Delete root mdag key from datastore to remove old pin storage. + ipldPinDatastoreKey := ds.NewKey(ipldPinPath) + if err = dstore.Delete(ipldPinDatastoreKey); err != nil { + return nil, 0, fmt.Errorf("cannot delete old pin state: %v", err) + } + if err = dstore.Sync(ipldPinDatastoreKey); err != nil { + return nil, 0, fmt.Errorf("cannot sync old pin state: %v", err) + } + + return dsPinner, convCount, nil +} + +// ConvertPinsFromDSToIPLD converts the pins stored in the datastore by +// dspinner, into pins stored in the given internal DAGService by ipldpinner. +// Returns an ipldpinner loaded with the converted pins, and a count of the +// recursive and direct pins converted. +// +// After the pins are stored in the DAGService, the pins and their indexes are +// removed from the dspinner. +func ConvertPinsFromDSToIPLD(ctx context.Context, dstore ds.Datastore, dserv ipld.DAGService, internal ipld.DAGService) (ipfspinner.Pinner, int, error) { + dsPinner, err := dspinner.New(ctx, dstore, dserv) + if err != nil { + return nil, 0, err + } + + ipldPinner, err := ipldpinner.New(dstore, dserv, internal) + if err != nil { + return nil, 0, err + } + + cids, err := dsPinner.RecursiveKeys(ctx) + if err != nil { + return nil, 0, err + } + for i := range cids { + ipldPinner.PinWithMode(cids[i], ipfspinner.Recursive) + dsPinner.RemovePinWithMode(cids[i], ipfspinner.Recursive) + } + convCount := len(cids) + + cids, err = dsPinner.DirectKeys(ctx) + if err != nil { + return nil, 0, err + } + for i := range cids { + ipldPinner.PinWithMode(cids[i], ipfspinner.Direct) + dsPinner.RemovePinWithMode(cids[i], ipfspinner.Direct) + } + convCount += len(cids) + + // Save the ipldpinner pins + err = ipldPinner.Flush(ctx) + if err != nil { + return nil, 0, err + } + + err = dsPinner.Flush(ctx) + if err != nil { + return nil, 0, err + } + + return ipldPinner, convCount, nil +} diff --git a/pinning/pinner/pinconv/pinconv_test.go b/pinning/pinner/pinconv/pinconv_test.go new file mode 100644 index 000000000..ac7f8ffc5 --- /dev/null +++ b/pinning/pinner/pinconv/pinconv_test.go @@ -0,0 +1,153 @@ +package pinconv + +import ( + "context" + "errors" + "io" + "testing" + + bs "github.com/ipfs/go-blockservice" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + lds "github.com/ipfs/go-ds-leveldb" + blockstore "github.com/ipfs/go-ipfs-blockstore" + offline "github.com/ipfs/go-ipfs-exchange-offline" + ipfspin "github.com/ipfs/go-ipfs-pinner" + "github.com/ipfs/go-ipfs-pinner/dspinner" + util "github.com/ipfs/go-ipfs-util" + ipld "github.com/ipfs/go-ipld-format" + mdag "github.com/ipfs/go-merkledag" +) + +var rand = util.NewTimeSeededRand() + +type batchWrap struct { + ds.Datastore +} + +func randNode() (*mdag.ProtoNode, cid.Cid) { + nd := new(mdag.ProtoNode) + nd.SetData(make([]byte, 32)) + _, err := io.ReadFull(rand, nd.Data()) + if err != nil { + panic(err) + } + k := nd.Cid() + return nd, k +} + +func (d *batchWrap) Batch() (ds.Batch, error) { + return ds.NewBasicBatch(d), nil +} + +func makeStore() (ds.Datastore, ipld.DAGService) { + ldstore, err := lds.NewDatastore("", nil) + if err != nil { + panic(err) + } + var dstore ds.Batching + dstore = &batchWrap{ldstore} + + bstore := blockstore.NewBlockstore(dstore) + bserv := bs.New(bstore, offline.Exchange(bstore)) + dserv := mdag.NewDAGService(bserv) + return dstore, dserv +} + +func TestConversions(t *testing.T) { + ctx := context.Background() + dstore, dserv := makeStore() + + dsPinner, err := dspinner.New(ctx, dstore, dserv) + if err != nil { + t.Fatal(err) + } + + a, ak := randNode() + err = dsPinner.Pin(ctx, a, false) + if err != nil { + t.Fatal(err) + } + + // create new node c, to be indirectly pinned through b + c, ck := randNode() + dserv.Add(ctx, c) + + // Create new node b, to be parent to a and c + b, _ := randNode() + b.AddNodeLink("child", a) + b.AddNodeLink("otherchild", c) + bk := b.Cid() // CID changed after adding links + + // recursively pin B{A,C} + err = dsPinner.Pin(ctx, b, true) + if err != nil { + t.Fatal(err) + } + + err = dsPinner.Flush(ctx) + if err != nil { + t.Fatal(err) + } + + verifyPins := func(pinner ipfspin.Pinner) error { + pinned, err := pinner.CheckIfPinned(ctx, ak, bk, ck) + if err != nil { + return err + } + if len(pinned) != 3 { + return errors.New("incorrect number of results") + } + for _, pn := range pinned { + switch pn.Key { + case ak: + if pn.Mode != ipfspin.Direct { + return errors.New("A pinned with wrong mode") + } + case bk: + if pn.Mode != ipfspin.Recursive { + return errors.New("B pinned with wrong mode") + } + case ck: + if pn.Mode != ipfspin.Indirect { + return errors.New("C should be pinned indirectly") + } + if pn.Via != bk { + return errors.New("C should be pinned via B") + } + } + } + return nil + } + + err = verifyPins(dsPinner) + if err != nil { + t.Fatal(err) + } + + ipldPinner, toIPLDCount, err := ConvertPinsFromDSToIPLD(ctx, dstore, dserv, dserv) + if err != nil { + t.Fatal(err) + } + if toIPLDCount != 2 { + t.Fatal("expected 2 ds-to-ipld pins, got", toIPLDCount) + } + + err = verifyPins(ipldPinner) + if err != nil { + t.Fatal(err) + } + + toDSPinner, toDSCount, err := ConvertPinsFromIPLDToDS(ctx, dstore, dserv, dserv) + if err != nil { + t.Fatal(err) + } + if toDSCount != toIPLDCount { + t.Fatal("ds-to-ipld pins", toIPLDCount, "not equal to ipld-to-ds-pins", toDSCount) + } + + err = verifyPins(toDSPinner) + if err != nil { + t.Fatal(err) + } +} From d097b1d3ee904c7407d2baea3860f1182a9eca5a Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Tue, 17 Nov 2020 09:26:13 -0500 Subject: [PATCH 3277/3817] refactor: properly return non-GenericOpenAPIErrors This commit was moved from ipfs/go-pinning-service-http-client@450a21fea0ed8e3aa96cbc9274ef9c722f494c72 --- pinning/remote/client/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/remote/client/client.go b/pinning/remote/client/client.go index e3c04176e..206fd7ce6 100644 --- a/pinning/remote/client/client.go +++ b/pinning/remote/client/client.go @@ -371,7 +371,7 @@ func httperr(resp *http.Response, e error) error { if ok { ferr, ok := oerr.Model().(openapi.Failure) if ok { - return errors.Wrapf(e,"statusCode: %d, reason : %q, details : %q", resp.StatusCode, ferr.Error.GetReason(), ferr.Error.GetDetails()) + return errors.Wrapf(e,"statusCode: %d, reason: %q, details: %q", resp.StatusCode, ferr.Error.GetReason(), ferr.Error.GetDetails()) } } From b0590c5faf496518469b1ab062e0a15281ab5dc6 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Thu, 19 Nov 2020 20:39:34 +0100 Subject: [PATCH 3278/3817] feat: LsBatchSync This adds function that returns a single batch and an int with total count. This enables consumer of this lib to implement manual pagination or get total pin count in efficient manner. This commit was moved from ipfs/go-pinning-service-http-client@0b51d34f4aa84df43b636677b1ece164921f86c8 --- pinning/remote/client/client.go | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/pinning/remote/client/client.go b/pinning/remote/client/client.go index 206fd7ce6..ca67634e2 100644 --- a/pinning/remote/client/client.go +++ b/pinning/remote/client/client.go @@ -195,6 +195,30 @@ func (c *Client) LsSync(ctx context.Context, opts ...LsOption) ([]PinStatusGette return res, <-errCh } +// Manual version of Ls that returns a single batch of results and int with total count +func (c *Client) LsBatchSync(ctx context.Context, opts ...LsOption) ([]PinStatusGetter, int, error) { + var res []PinStatusGetter + + settings := new(lsSettings) + for _, o := range opts { + if err := o(settings); err != nil { + return nil, 0, err + } + } + + pinRes, err := c.lsInternal(ctx, settings) + if err != nil { + return nil, 0, err + } + + results := pinRes.GetResults() + for _, r := range results { + res = append(res, &pinStatusObject{r}) + } + + return res, int(pinRes.Count), nil +} + func (c *Client) lsInternal(ctx context.Context, settings *lsSettings) (pinResults, error) { getter := c.client.PinsApi.PinsGet(ctx) if len(settings.cids) > 0 { @@ -257,7 +281,7 @@ func (pinAddOpts) WithName(name string) AddOption { } } -func (pinLsOpts) WithOrigins(origins ...multiaddr.Multiaddr) AddOption { +func (pinAddOpts) WithOrigins(origins ...multiaddr.Multiaddr) AddOption { return func(options *addSettings) error { for _, o := range origins { options.origins = append(options.origins, o.String()) @@ -326,7 +350,7 @@ func (c *Client) DeleteByID(ctx context.Context, pinID string) error { return nil } -func (c *Client) Modify(ctx context.Context, pinID string, cid cid.Cid, opts ...AddOption) (PinStatusGetter, error) { +func (c *Client) Replace(ctx context.Context, pinID string, cid cid.Cid, opts ...AddOption) (PinStatusGetter, error) { settings := new(addSettings) for _, o := range opts { if err := o(settings); err != nil { From d62225f78f0f79a571782d47f76fdfadadc51941 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 1 Dec 2020 00:07:37 +0100 Subject: [PATCH 3279/3817] style: clean up error messages this cleans up errors This commit was moved from ipfs/go-pinning-service-http-client@7ffc18b3a85f5c4c2f26e364127da6f15d6f5c59 --- pinning/remote/client/client.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pinning/remote/client/client.go b/pinning/remote/client/client.go index ca67634e2..40b4b09f4 100644 --- a/pinning/remote/client/client.go +++ b/pinning/remote/client/client.go @@ -395,13 +395,13 @@ func httperr(resp *http.Response, e error) error { if ok { ferr, ok := oerr.Model().(openapi.Failure) if ok { - return errors.Wrapf(e,"statusCode: %d, reason: %q, details: %q", resp.StatusCode, ferr.Error.GetReason(), ferr.Error.GetDetails()) + return errors.Wrapf(e, "reason: %q, details: %q", ferr.Error.GetReason(), ferr.Error.GetDetails()) } } if resp == nil { - return errors.Wrapf(e,"empty response from remote pinning service") + return errors.Wrapf(e, "empty response from remote pinning service") } - return errors.Wrapf(e, "remote pinning service error. statusCode: %d", resp.StatusCode) + return errors.Wrapf(e, "remote pinning service returned http error %d", resp.StatusCode) } From c0b437ba6d7e2edc59d5e39bd8be650ec5f9cfb4 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Wed, 26 Aug 2020 13:17:48 -0400 Subject: [PATCH 3280/3817] ResolveToLastNode no longer fetches nodes it does not need This commit was moved from ipfs/go-path@9ba33de2c4e628f0942e4846366978910d627d77 --- path/resolver/resolver.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 67bb9f6fb..9f153840c 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -89,6 +89,10 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (cid. return cid.Cid{}, nil, err } + if len(rest) == 0 { + return lnk.Cid, nil, nil + } + next, err := lnk.GetNode(ctx, r.DAG) if err != nil { return cid.Cid{}, nil, err From 7902558debb9947429127a4d0ed84b53578f4d08 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Wed, 26 Aug 2020 13:59:39 -0400 Subject: [PATCH 3281/3817] test: add test that ResolveToLastNode does not perform unncessary fetches This commit was moved from ipfs/go-path@692649d53149f2ccd5db0e5de350f305b65805b9 --- path/resolver/resolver_test.go | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/path/resolver/resolver_test.go b/path/resolver/resolver_test.go index 480ccdf1d..d3c6913e7 100644 --- a/path/resolver/resolver_test.go +++ b/path/resolver/resolver_test.go @@ -105,3 +105,43 @@ func TestRecurivePathResolution(t *testing.T) { p.String(), rCid.String(), cKey.String())) } } + +func TestResolveToLastNode_NoUnnecessaryFetching(t *testing.T) { + ctx := context.Background() + dagService := dagmock.Mock() + + a := randNode() + b := randNode() + + err := a.AddNodeLink("child", b) + if err != nil { + t.Fatal(err) + } + + err = dagService.Add(ctx, a) + if err != nil { + t.Fatal(err) + } + + aKey := a.Cid() + + segments := []string{aKey.String(), "child"} + p, err := path.FromSegments("/ipfs/", segments...) + if err != nil { + t.Fatal(err) + } + + resolver := resolver.NewBasicResolver(dagService) + resolvedCID, remainingPath, err := resolver.ResolveToLastNode(ctx, p) + if err != nil { + t.Fatal(err) + } + + if len(remainingPath) > 0 { + t.Fatal("cannot have remaining path") + } + + if !resolvedCID.Equals(b.Cid()) { + t.Fatal("resolved to the wrong CID") + } +} From 20494dd96cd3cc170960891dbe8a377862968f74 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Wed, 2 Dec 2020 11:55:42 +1100 Subject: [PATCH 3282/3817] fix: improved error message on broken CIDv0 move lidel's fix from https://github.com/ipfs/go-cid/pull/116 This commit was moved from ipfs/go-path@5e8ad22f98b2095a11e1d735856ba25fad40c294 --- path/path.go | 16 ++++++++++++---- path/path_test.go | 11 +++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/path/path.go b/path/path.go index 18a85a902..df050008f 100644 --- a/path/path.go +++ b/path/path.go @@ -96,7 +96,7 @@ func ParsePath(txt string) (Path, error) { // if the path doesnt begin with a '/' // we expect this to start with a hash, and be an 'ipfs' path if parts[0] != "" { - if _, err := cid.Decode(parts[0]); err != nil { + if _, err := decodeCid(parts[0]); err != nil { return "", &pathError{error: err, path: txt} } // The case when the path starts with hash without a protocol prefix @@ -114,7 +114,7 @@ func ParsePath(txt string) (Path, error) { return "", &pathError{error: fmt.Errorf("not enough path components"), path: txt} } // Validate Cid. - _, err := cid.Decode(parts[2]) + _, err := decodeCid(parts[2]) if err != nil { return "", &pathError{error: fmt.Errorf("invalid CID: %s", err), path: txt} } @@ -135,7 +135,7 @@ func ParseCidToPath(txt string) (Path, error) { return "", &pathError{error: fmt.Errorf("empty"), path: txt} } - c, err := cid.Decode(txt) + c, err := decodeCid(txt) if err != nil { return "", &pathError{error: err, path: txt} } @@ -172,7 +172,7 @@ func SplitAbsPath(fpath Path) (cid.Cid, []string, error) { return cid.Cid{}, nil, &pathError{error: fmt.Errorf("empty"), path: string(fpath)} } - c, err := cid.Decode(parts[0]) + c, err := decodeCid(parts[0]) // first element in the path is a cid if err != nil { return cid.Cid{}, nil, &pathError{error: fmt.Errorf("invalid CID: %s", err), path: string(fpath)} @@ -180,3 +180,11 @@ func SplitAbsPath(fpath Path) (cid.Cid, []string, error) { return c, parts[1:], nil } + +func decodeCid(cstr string) (cid.Cid, error) { + c, err := cid.Decode(cstr) + if err != nil && len(cstr) == 46 && cstr[:2] == "qm" { // https://github.com/ipfs/go-ipfs/issues/7792 + return cid.Cid{}, fmt.Errorf("%v (possible lowercased CIDv0; consider converting to a case-agnostic CIDv1, such as base32)", err) + } + return c, err +} diff --git a/path/path_test.go b/path/path_test.go index fdd71fc0c..4552fc5f9 100644 --- a/path/path_test.go +++ b/path/path_test.go @@ -102,3 +102,14 @@ func TestPopLastSegment(t *testing.T) { } } } + +func TestV0ErrorDueToLowercase(t *testing.T) { + badb58 := "/ipfs/qmbwqxbekc3p8tqskc98xmwnzrzdtrlmimpl8wbutgsmnr" + _, err := ParsePath(badb58) + if err == nil { + t.Fatal("should have failed to decode") + } + if !strings.HasSuffix(err.Error(), "(possible lowercased CIDv0; consider converting to a case-agnostic CIDv1, such as base32)") { + t.Fatal("should have meaningful info about case-insensitive fix") + } +} From dc0e57b1f3455ec0efd4a4f4c9e374c07bc2e25f Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Thu, 3 Dec 2020 17:47:26 +0700 Subject: [PATCH 3283/3817] run gofmt -s This commit was moved from ipld/go-car@4aefd37300f7a1137654051f108f04fbaa693d23 --- ipld/car/car_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 1fb87f5f9..b50111b27 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -112,7 +112,7 @@ func TestRoundtripSelective(t *testing.T) { ssb.ExploreIndex(1, ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreAll(ssb.ExploreRecursiveEdge())))) }).Node() - sc := NewSelectiveCar(context.Background(), sourceBs, []Dag{Dag{Root: nd3.Cid(), Selector: selector}}) + sc := NewSelectiveCar(context.Background(), sourceBs, []Dag{{Root: nd3.Cid(), Selector: selector}}) // write car in one step buf := new(bytes.Buffer) @@ -127,7 +127,7 @@ func TestRoundtripSelective(t *testing.T) { require.NoError(t, err) // create a new builder for two-step write - sc2 := NewSelectiveCar(context.Background(), sourceBs, []Dag{Dag{Root: nd3.Cid(), Selector: selector}}) + sc2 := NewSelectiveCar(context.Background(), sourceBs, []Dag{{Root: nd3.Cid(), Selector: selector}}) // write car in two steps var twoStepBlocks []Block From 049be24650b49535cf41b85485640cb0fcfdeaf4 Mon Sep 17 00:00:00 2001 From: Keenan Nemetz Date: Tue, 5 Jan 2021 20:15:09 -0800 Subject: [PATCH 3284/3817] Fix bug in dagutils MergeDiffs. (#59) This commit was moved from ipfs/go-merkledag@bf51443272bb98cff071eb44ed9ce6c940e82f1f --- ipld/merkledag/dagutils/diff.go | 128 ++++++++++++--------------- ipld/merkledag/dagutils/diff_test.go | 127 ++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 71 deletions(-) create mode 100644 ipld/merkledag/dagutils/diff_test.go diff --git a/ipld/merkledag/dagutils/diff.go b/ipld/merkledag/dagutils/diff.go index 501523876..9fef3f964 100644 --- a/ipld/merkledag/dagutils/diff.go +++ b/ipld/merkledag/dagutils/diff.go @@ -102,64 +102,62 @@ func ApplyChange(ctx context.Context, ds ipld.DAGService, nd *dag.ProtoNode, cs // 2. both of two nodes are ProtoNode. // Otherwise, it compares the cid and emits a Mod change object. func Diff(ctx context.Context, ds ipld.DAGService, a, b ipld.Node) ([]*Change, error) { - // Base case where both nodes are leaves, just compare - // their CIDs. - if len(a.Links()) == 0 && len(b.Links()) == 0 { - return getChange(a, b) + if a.Cid() == b.Cid() { + return []*Change{}, nil } - var out []*Change cleanA, okA := a.Copy().(*dag.ProtoNode) cleanB, okB := b.Copy().(*dag.ProtoNode) - if !okA || !okB { - return getChange(a, b) + + linksA := a.Links() + linksB := b.Links() + + if !okA || !okB || (len(linksA) == 0 && len(linksB) == 0) { + return []*Change{{Type: Mod, Before: a.Cid(), After: b.Cid()}}, nil } - // strip out unchanged stuff - for _, lnk := range a.Links() { - l, _, err := b.ResolveLink([]string{lnk.Name}) - if err == nil { - if l.Cid.Equals(lnk.Cid) { - // no change... ignore it - } else { - anode, err := lnk.GetNode(ctx, ds) - if err != nil { - return nil, err - } - - bnode, err := l.GetNode(ctx, ds) - if err != nil { - return nil, err - } - - sub, err := Diff(ctx, ds, anode, bnode) - if err != nil { - return nil, err - } - - for _, subc := range sub { - subc.Path = path.Join(lnk.Name, subc.Path) - out = append(out, subc) - } - } - _ = cleanA.RemoveNodeLink(l.Name) - _ = cleanB.RemoveNodeLink(l.Name) + var out []*Change + for _, linkA := range linksA { + linkB, _, err := b.ResolveLink([]string{linkA.Name}) + if err != nil { + continue + } + + cleanA.RemoveNodeLink(linkA.Name) + cleanB.RemoveNodeLink(linkA.Name) + + if linkA.Cid == linkB.Cid { + continue + } + + nodeA, err := linkA.GetNode(ctx, ds) + if err != nil { + return nil, err } + + nodeB, err := linkB.GetNode(ctx, ds) + if err != nil { + return nil, err + } + + sub, err := Diff(ctx, ds, nodeA, nodeB) + if err != nil { + return nil, err + } + + for _, c := range sub { + c.Path = path.Join(linkA.Name, c.Path) + } + + out = append(out, sub...) } - for _, lnk := range cleanA.Links() { - out = append(out, &Change{ - Type: Remove, - Path: lnk.Name, - Before: lnk.Cid, - }) + for _, l := range cleanA.Links() { + out = append(out, &Change{Type: Remove, Path: l.Name, Before: l.Cid}) } - for _, lnk := range cleanB.Links() { - out = append(out, &Change{ - Type: Add, - Path: lnk.Name, - After: lnk.Cid, - }) + + for _, l := range cleanB.Links() { + out = append(out, &Change{Type: Add, Path: l.Name, After: l.Cid}) } return out, nil @@ -177,38 +175,26 @@ type Conflict struct { // A slice of Conflicts is returned and contains pointers to the // Changes involved (which share the same path). func MergeDiffs(a, b []*Change) ([]*Change, []Conflict) { - var out []*Change - var conflicts []Conflict paths := make(map[string]*Change) for _, c := range a { paths[c.Path] = c } - for _, c := range b { - if ca, ok := paths[c.Path]; ok { - conflicts = append(conflicts, Conflict{ - A: ca, - B: c, - }) + var changes []*Change + var conflicts []Conflict + + for _, changeB := range b { + if changeA, ok := paths[changeB.Path]; ok { + conflicts = append(conflicts, Conflict{changeA, changeB}) } else { - out = append(out, c) + changes = append(changes, changeB) } + delete(paths, changeB.Path) } + for _, c := range paths { - out = append(out, c) + changes = append(changes, c) } - return out, conflicts -} -func getChange(a, b ipld.Node) ([]*Change, error) { - if a.Cid().Equals(b.Cid()) { - return []*Change{}, nil - } - return []*Change{ - { - Type: Mod, - Before: a.Cid(), - After: b.Cid(), - }, - }, nil + return changes, conflicts } diff --git a/ipld/merkledag/dagutils/diff_test.go b/ipld/merkledag/dagutils/diff_test.go new file mode 100644 index 000000000..9cafe13bc --- /dev/null +++ b/ipld/merkledag/dagutils/diff_test.go @@ -0,0 +1,127 @@ +package dagutils + +import ( + "context" + "testing" + + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" + dag "github.com/ipfs/go-merkledag" + mdtest "github.com/ipfs/go-merkledag/test" +) + +func TestMergeDiffs(t *testing.T) { + node1 := dag.NodeWithData([]byte("one")) + node2 := dag.NodeWithData([]byte("two")) + node3 := dag.NodeWithData([]byte("three")) + node4 := dag.NodeWithData([]byte("four")) + + changesA := []*Change{ + {Add, "one", cid.Cid{}, node1.Cid()}, + {Remove, "two", node2.Cid(), cid.Cid{}}, + {Mod, "three", node3.Cid(), node4.Cid()}, + } + + changesB := []*Change{ + {Mod, "two", node2.Cid(), node3.Cid()}, + {Add, "four", cid.Cid{}, node4.Cid()}, + } + + changes, conflicts := MergeDiffs(changesA, changesB) + if len(changes) != 3 { + t.Fatal("unexpected merge changes") + } + + expect := []*Change{ + changesB[1], + changesA[0], + changesA[2], + } + + for i, change := range changes { + if change.Type != expect[i].Type { + t.Error("unexpected diff change type") + } + + if change.Path != expect[i].Path { + t.Error("unexpected diff change path") + } + + if change.Before != expect[i].Before { + t.Error("unexpected diff change before") + } + + if change.After != expect[i].After { + t.Error("unexpected diff change before") + } + } + + if len(conflicts) != 1 { + t.Fatal("unexpected merge conflicts") + } + + if conflicts[0].A != changesA[1] { + t.Error("unexpected merge conflict a") + } + + if conflicts[0].B != changesB[0] { + t.Error("unexpected merge conflict b") + } +} + +func TestDiff(t *testing.T) { + ctx := context.Background() + ds := mdtest.Mock() + + rootA := &dag.ProtoNode{} + rootB := &dag.ProtoNode{} + + child1 := dag.NodeWithData([]byte("one")) + child2 := dag.NodeWithData([]byte("two")) + child3 := dag.NodeWithData([]byte("three")) + child4 := dag.NodeWithData([]byte("four")) + + rootA.AddNodeLink("one", child1) + rootA.AddNodeLink("two", child2) + + rootB.AddNodeLink("one", child3) + rootB.AddNodeLink("four", child4) + + nodes := []ipld.Node{child1, child2, child3, child4, rootA, rootB} + if err := ds.AddMany(ctx, nodes); err != nil { + t.Fatal("failed to add nodes") + } + + changes, err := Diff(ctx, ds, rootA, rootB) + if err != nil { + t.Fatal("unexpected diff error") + } + + if len(changes) != 3 { + t.Fatal("unexpected diff changes") + } + + expect := []Change{ + {Mod, "one", child1.Cid(), child3.Cid()}, + {Remove, "two", child2.Cid(), cid.Cid{}}, + {Add, "four", cid.Cid{}, child4.Cid()}, + } + + for i, change := range changes { + if change.Type != expect[i].Type { + t.Error("unexpected diff change type") + } + + if change.Path != expect[i].Path { + t.Error("unexpected diff change path") + } + + if change.Before != expect[i].Before { + t.Error("unexpected diff change before") + } + + if change.After != expect[i].After { + t.Error("unexpected diff change before") + } + } +} From 4ce8cc3af8771a7673c5c6a195f484cbf856e04f Mon Sep 17 00:00:00 2001 From: Andrew Gillis Date: Wed, 27 Jan 2021 11:40:12 -0800 Subject: [PATCH 3285/3817] Avoid loading all pins into memory during migration (#5) * Converting from IPLD to datastore-based pins no longer requires loading all dag-storage pins (including indirect pins) into memory * increase test coverage This commit was moved from ipfs/go-ipfs-pinner@9ed588ac9b21e9fcc4dfa27d28bc1d5a266f5995 --- pinning/pinner/ipldpinner/pin.go | 35 ++++++++++ pinning/pinner/ipldpinner/pin_test.go | 96 +++++++++++++++++++++++++- pinning/pinner/ipldpinner/set.go | 35 +++++++++- pinning/pinner/pinconv/pinconv.go | 47 ++++++------- pinning/pinner/pinconv/pinconv_test.go | 28 +++++++- 5 files changed, 211 insertions(+), 30 deletions(-) diff --git a/pinning/pinner/ipldpinner/pin.go b/pinning/pinner/ipldpinner/pin.go index d0824b349..dc90dd495 100644 --- a/pinning/pinner/ipldpinner/pin.go +++ b/pinning/pinner/ipldpinner/pin.go @@ -141,6 +141,41 @@ func New(dstore ds.Datastore, dserv, internal ipld.DAGService) (*pinner, error) }, nil } +// LoadKeys reads the pinned CIDs and sends them on the given channel. This is +// used to read pins without loading them all into memory. +func LoadKeys(ctx context.Context, dstore ds.Datastore, dserv, internal ipld.DAGService, recursive bool, keyChan chan<- cid.Cid) error { + rootKey, err := dstore.Get(pinDatastoreKey) + if err != nil { + if err == ds.ErrNotFound { + return nil + } + return err + } + rootCid, err := cid.Cast(rootKey) + if err != nil { + return err + } + + root, err := internal.Get(ctx, rootCid) + if err != nil { + return fmt.Errorf("cannot find pinning root object: %v", err) + } + + rootpb, ok := root.(*mdag.ProtoNode) + if !ok { + return mdag.ErrNotProtobuf + } + + var linkName string + if recursive { + linkName = linkRecursive + } else { + linkName = linkDirect + } + + return loadSetChan(ctx, internal, rootpb, linkName, keyChan) +} + // Pin the given node, optionally recursive func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { err := p.dserv.Add(ctx, node) diff --git a/pinning/pinner/ipldpinner/pin_test.go b/pinning/pinner/ipldpinner/pin_test.go index e193aa96c..3c61d41fd 100644 --- a/pinning/pinner/ipldpinner/pin_test.go +++ b/pinning/pinner/ipldpinner/pin_test.go @@ -54,7 +54,8 @@ func assertUnpinned(t *testing.T, p pin.Pinner, c cid.Cid, failmsg string) { } func TestPinnerBasic(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() dstore := dssync.MutexWrap(ds.NewMapDatastore()) bstore := blockstore.NewBlockstore(dstore) @@ -62,7 +63,6 @@ func TestPinnerBasic(t *testing.T) { dserv := mdag.NewDAGService(bserv) - // TODO does pinner need to share datastore with blockservice? p, err := New(dstore, dserv, dserv) if err != nil { t.Fatal(err) @@ -165,6 +165,98 @@ func TestPinnerBasic(t *testing.T) { // Test recursively pinned assertPinned(t, np, bk, "could not find recursively pinned node") + + // Test that LoadKeys returns the expected CIDs. + keyChan := make(chan cid.Cid) + go func() { + err = LoadKeys(ctx, dstore, dserv, dserv, true, keyChan) + close(keyChan) + }() + keys := map[cid.Cid]struct{}{} + for c := range keyChan { + keys[c] = struct{}{} + } + if err != nil { + t.Fatal(err) + } + recKeys, _ := np.RecursiveKeys(ctx) + if len(keys) != len(recKeys) { + t.Fatal("wrong number of recursive keys from LoadKeys") + } + for _, k := range recKeys { + if _, ok := keys[k]; !ok { + t.Fatal("LoadKeys did not return correct recursive keys") + } + } + + keyChan = make(chan cid.Cid) + go func() { + err = LoadKeys(ctx, dstore, dserv, dserv, false, keyChan) + close(keyChan) + }() + keys = map[cid.Cid]struct{}{} + for c := range keyChan { + keys[c] = struct{}{} + } + if err != nil { + t.Fatal(err) + } + dirKeys, _ := np.DirectKeys(ctx) + if len(keys) != len(dirKeys) { + t.Fatal("wrong number of direct keys from LoadKeys") + } + for _, k := range dirKeys { + if _, ok := keys[k]; !ok { + t.Fatal("LoadKeys did not return correct direct keys") + } + } + + cancel() + emptyDS := dssync.MutexWrap(ds.NewMapDatastore()) + + // Check key not in datastore + err = LoadKeys(ctx, emptyDS, dserv, dserv, true, nil) + if err != nil { + t.Fatal(err) + } + + // Check error on bad key + if err = emptyDS.Put(pinDatastoreKey, []byte("bad-cid")); err != nil { + panic(err) + } + if err = emptyDS.Sync(pinDatastoreKey); err != nil { + panic(err) + } + if err = LoadKeys(ctx, emptyDS, dserv, dserv, true, nil); err == nil { + t.Fatal("expected error") + } + + // Lookup dag that does not exist + noKey, err := cid.Decode("QmYff9iHR1Hz6wufVeJodzXqQm4pkK4QNS9ms8tyPKVWm1") + if err != nil { + panic(err) + } + if err = emptyDS.Put(pinDatastoreKey, noKey.Bytes()); err != nil { + panic(err) + } + if err = emptyDS.Sync(pinDatastoreKey); err != nil { + panic(err) + } + err = LoadKeys(ctx, emptyDS, dserv, dserv, true, nil) + if err == nil || err.Error() != "cannot find pinning root object: merkledag: not found" { + t.Fatal("did not get expected error") + } + + // Check error when node has no links + if err = emptyDS.Put(pinDatastoreKey, emptyKey.Bytes()); err != nil { + panic(err) + } + if err = emptyDS.Sync(pinDatastoreKey); err != nil { + panic(err) + } + if err = LoadKeys(ctx, emptyDS, dserv, dserv, true, nil); err == nil { + t.Fatal("expected error") + } } func TestIsPinnedLookup(t *testing.T) { diff --git a/pinning/pinner/ipldpinner/set.go b/pinning/pinner/ipldpinner/set.go index 2fb931f93..51951a2c0 100644 --- a/pinning/pinner/ipldpinner/set.go +++ b/pinning/pinner/ipldpinner/set.go @@ -219,13 +219,15 @@ func walkItems(ctx context.Context, dag ipld.DAGService, n *merkledag.ProtoNode, // readHdr guarantees fanout is a safe value fanout := hdr.GetFanout() for i, l := range n.Links()[fanout:] { - if err := fn(i, l); err != nil { + if err = fn(i, l); err != nil { return err } } for _, l := range n.Links()[:fanout] { c := l.Cid - children(c) + if children != nil { + children(c) + } if c.Equals(emptyKey) { continue } @@ -239,7 +241,7 @@ func walkItems(ctx context.Context, dag ipld.DAGService, n *merkledag.ProtoNode, return merkledag.ErrNotProtobuf } - if err := walkItems(ctx, dag, stpb, fn, children); err != nil { + if err = walkItems(ctx, dag, stpb, fn, children); err != nil { return err } } @@ -277,6 +279,33 @@ func loadSet(ctx context.Context, dag ipld.DAGService, root *merkledag.ProtoNode return res, nil } +func loadSetChan(ctx context.Context, dag ipld.DAGService, root *merkledag.ProtoNode, name string, keyChan chan<- cid.Cid) error { + l, err := root.GetNodeLink(name) + if err != nil { + return err + } + + n, err := l.GetNode(ctx, dag) + if err != nil { + return err + } + + pbn, ok := n.(*merkledag.ProtoNode) + if !ok { + return merkledag.ErrNotProtobuf + } + + walk := func(idx int, link *ipld.Link) error { + keyChan <- link.Cid + return nil + } + + if err = walkItems(ctx, dag, pbn, walk, nil); err != nil { + return err + } + return nil +} + func getCidListIterator(cids []cid.Cid) itemIterator { return func() (c cid.Cid, ok bool) { if len(cids) == 0 { diff --git a/pinning/pinner/pinconv/pinconv.go b/pinning/pinner/pinconv/pinconv.go index 9aee703a7..df21f85b0 100644 --- a/pinning/pinner/pinconv/pinconv.go +++ b/pinning/pinner/pinconv/pinconv.go @@ -7,7 +7,7 @@ import ( "context" "fmt" - "github.com/ipfs/go-cid" + cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" ipfspinner "github.com/ipfs/go-ipfs-pinner" "github.com/ipfs/go-ipfs-pinner/dspinner" @@ -24,39 +24,38 @@ import ( func ConvertPinsFromIPLDToDS(ctx context.Context, dstore ds.Datastore, dserv ipld.DAGService, internal ipld.DAGService) (ipfspinner.Pinner, int, error) { const ipldPinPath = "/local/pins" - ipldPinner, err := ipldpinner.New(dstore, dserv, internal) - if err != nil { - return nil, 0, err - } - dsPinner, err := dspinner.New(ctx, dstore, dserv) if err != nil { return nil, 0, err } - seen := cid.NewSet() - cids, err := ipldPinner.RecursiveKeys(ctx) - if err != nil { - return nil, 0, err + var convCount int + keyChan := make(chan cid.Cid) + + go func() { + err = ipldpinner.LoadKeys(ctx, dstore, dserv, internal, true, keyChan) + close(keyChan) + }() + for key := range keyChan { + dsPinner.PinWithMode(key, ipfspinner.Recursive) + convCount++ } - for i := range cids { - seen.Add(cids[i]) - dsPinner.PinWithMode(cids[i], ipfspinner.Recursive) + if err != nil { + return nil, 0, fmt.Errorf("cannot load recursive keys: %s", err) } - convCount := len(cids) - cids, err = ipldPinner.DirectKeys(ctx) - if err != nil { - return nil, 0, err + keyChan = make(chan cid.Cid) + go func() { + err = ipldpinner.LoadKeys(ctx, dstore, dserv, internal, false, keyChan) + close(keyChan) + }() + for key := range keyChan { + dsPinner.PinWithMode(key, ipfspinner.Direct) + convCount++ } - for i := range cids { - if seen.Has(cids[i]) { - // Pin was already pinned recursively - continue - } - dsPinner.PinWithMode(cids[i], ipfspinner.Direct) + if err != nil { + return nil, 0, fmt.Errorf("cannot load direct keys: %s", err) } - convCount += len(cids) err = dsPinner.Flush(ctx) if err != nil { diff --git a/pinning/pinner/pinconv/pinconv_test.go b/pinning/pinner/pinconv/pinconv_test.go index ac7f8ffc5..02abca61c 100644 --- a/pinning/pinner/pinconv/pinconv_test.go +++ b/pinning/pinner/pinconv/pinconv_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "io" + "strings" "testing" bs "github.com/ipfs/go-blockservice" @@ -55,7 +56,8 @@ func makeStore() (ds.Datastore, ipld.DAGService) { } func TestConversions(t *testing.T) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() dstore, dserv := makeStore() dsPinner, err := dspinner.New(ctx, dstore, dserv) @@ -151,3 +153,27 @@ func TestConversions(t *testing.T) { t.Fatal(err) } } + +func TestConvertLoadError(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dstore, dserv := makeStore() + // Point /local/pins to empty node to cause failure loading pins. + pinDatastoreKey := ds.NewKey("/local/pins") + emptyKey, err := cid.Decode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n") + if err != nil { + panic(err) + } + if err = dstore.Put(pinDatastoreKey, emptyKey.Bytes()); err != nil { + panic(err) + } + if err = dstore.Sync(pinDatastoreKey); err != nil { + panic(err) + } + + _, _, err = ConvertPinsFromIPLDToDS(ctx, dstore, dserv, dserv) + if err == nil || !strings.HasPrefix(err.Error(), "cannot load recursive keys") { + t.Fatal("did not get expected error") + } +} From 158d298a69d288a393d5f422c57137ebd87005af Mon Sep 17 00:00:00 2001 From: "@RubenKelevra" Date: Thu, 28 Jan 2021 21:22:36 +0100 Subject: [PATCH 3286/3817] show the domain name with the error (#7886) * show the domain name if DNSLink failed to resolve a domain because it wasn't a valid domain name Original author: @AluisioASG This commit was moved from ipfs/go-namesys@f5566bc8463a10e03a55ae97ff6371fbc7d7a60a --- namesys/dns.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/namesys/dns.go b/namesys/dns.go index 984a27aa8..0b48ad34b 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -5,6 +5,7 @@ import ( "errors" "net" "strings" + "fmt" path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" @@ -53,7 +54,7 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options domain := segments[0] if !isd.IsDomain(domain) { - out <- onceResult{err: errors.New("not a valid domain name")} + out <- onceResult{err: fmt.Errorf("not a valid domain name: %s", domain)} close(out) return out } From d7142764d10265880993dd804c99504d50007b37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Fri, 5 Feb 2021 14:26:08 +0000 Subject: [PATCH 3287/3817] all: gofmt -s This "simplify" flag mainly removes redundant types in expressions. Not particularly important, but a nice change that also makes gopls not show warnings. This commit was moved from ipfs/go-namesys@996d2cba2fdd6877f2c4465d54fae606d6775cc1 --- namesys/dns.go | 2 +- namesys/dns_test.go | 44 ++++++++++++++++++++++---------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 0b48ad34b..738612f46 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -3,9 +3,9 @@ package namesys import ( "context" "errors" + "fmt" "net" "strings" - "fmt" path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 653c3c788..5a0e2a7d2 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -61,72 +61,72 @@ func TestDnsEntryParsing(t *testing.T) { func newMockDNS() *mockDNS { return &mockDNS{ entries: map[string][]string{ - "multihash.example.com.": []string{ + "multihash.example.com.": { "dnslink=QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", }, - "ipfs.example.com.": []string{ + "ipfs.example.com.": { "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", }, - "_dnslink.dipfs.example.com.": []string{ + "_dnslink.dipfs.example.com.": { "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", }, - "dns1.example.com.": []string{ + "dns1.example.com.": { "dnslink=/ipns/ipfs.example.com", }, - "dns2.example.com.": []string{ + "dns2.example.com.": { "dnslink=/ipns/dns1.example.com", }, - "multi.example.com.": []string{ + "multi.example.com.": { "some stuff", "dnslink=/ipns/dns1.example.com", "masked dnslink=/ipns/example.invalid", }, - "equals.example.com.": []string{ + "equals.example.com.": { "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/=equals", }, - "loop1.example.com.": []string{ + "loop1.example.com.": { "dnslink=/ipns/loop2.example.com", }, - "loop2.example.com.": []string{ + "loop2.example.com.": { "dnslink=/ipns/loop1.example.com", }, - "_dnslink.dloop1.example.com.": []string{ + "_dnslink.dloop1.example.com.": { "dnslink=/ipns/loop2.example.com", }, - "_dnslink.dloop2.example.com.": []string{ + "_dnslink.dloop2.example.com.": { "dnslink=/ipns/loop1.example.com", }, - "bad.example.com.": []string{ + "bad.example.com.": { "dnslink=", }, - "withsegment.example.com.": []string{ + "withsegment.example.com.": { "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment", }, - "withrecsegment.example.com.": []string{ + "withrecsegment.example.com.": { "dnslink=/ipns/withsegment.example.com/subsub", }, - "withtrailing.example.com.": []string{ + "withtrailing.example.com.": { "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/", }, - "withtrailingrec.example.com.": []string{ + "withtrailingrec.example.com.": { "dnslink=/ipns/withtrailing.example.com/segment/", }, - "double.example.com.": []string{ + "double.example.com.": { "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", }, - "_dnslink.double.example.com.": []string{ + "_dnslink.double.example.com.": { "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", }, - "double.conflict.com.": []string{ + "double.conflict.com.": { "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", }, - "_dnslink.conflict.example.com.": []string{ + "_dnslink.conflict.example.com.": { "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjE", }, - "fqdn.example.com.": []string{ + "fqdn.example.com.": { "dnslink=/ipfs/QmYvMB9yrsSf7RKBghkfwmHJkzJhW2ZgVwq3LxBXXPasFr", }, - "www.wealdtech.eth.link.": []string{ + "www.wealdtech.eth.link.": { "dnslink=/ipns/ipfs.example.com", }, }, From c4684fac44453c7a756ef45e9237d90224ab9e38 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 18 Feb 2021 12:15:06 -0800 Subject: [PATCH 3288/3817] optimize CheckIfPinned 1. Parallelize fetching from disk. 2. Avoid re-visiting blocks we've already checked. Adding the same data over and over with small changes is pretty common. This commit was moved from ipfs/go-ipfs-pinner@49ac7c3aec6b138956ab5db061b9463a39b50834 --- pinning/pinner/dspinner/pin.go | 48 ++++++++++---------------------- pinning/pinner/ipldpinner/pin.go | 34 +++++++--------------- 2 files changed, 25 insertions(+), 57 deletions(-) diff --git a/pinning/pinner/dspinner/pin.go b/pinning/pinner/dspinner/pin.go index 5fd65e7bf..f9f5ff9bf 100644 --- a/pinning/pinner/dspinner/pin.go +++ b/pinning/pinner/dspinner/pin.go @@ -17,6 +17,7 @@ import ( "github.com/ipfs/go-ipfs-pinner/dsindex" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" + "github.com/ipfs/go-merkledag" mdag "github.com/ipfs/go-merkledag" "github.com/ipfs/go-merkledag/dagutils" "github.com/polydawn/refmt/cbor" @@ -489,49 +490,30 @@ func (p *pinner) CheckIfPinned(ctx context.Context, cids ...cid.Cid) ([]ipfspinn } } - // Now walk all recursive pins to check for indirect pins - var checkChildren func(cid.Cid, cid.Cid) error - checkChildren = func(rk, parentKey cid.Cid) error { - links, err := ipld.GetLinks(ctx, p.dserv, parentKey) - if err != nil { - return err - } - for _, lnk := range links { - c := lnk.Cid - - if toCheck.Has(c) { - pinned = append(pinned, - ipfspinner.Pinned{Key: c, Mode: ipfspinner.Indirect, Via: rk}) - toCheck.Remove(c) - } - - err = checkChildren(rk, c) - if err != nil { - return err - } - - if toCheck.Len() == 0 { - return nil - } - } - return nil - } - var e error + visited := cid.NewSet() err := p.cidRIndex.ForEach(ctx, "", func(key, value string) bool { var rk cid.Cid rk, e = cid.Cast([]byte(key)) if e != nil { return false } - e = checkChildren(rk, rk) + e = merkledag.Walk(ctx, merkledag.GetLinksWithDAG(p.dserv), rk, func(c cid.Cid) bool { + if toCheck.Len() == 0 || !visited.Visit(c) { + return false + } + + if toCheck.Has(c) { + pinned = append(pinned, ipfspinner.Pinned{Key: c, Mode: ipfspinner.Indirect, Via: rk}) + toCheck.Remove(c) + } + + return true + }, merkledag.Concurrent()) if e != nil { return false } - if toCheck.Len() == 0 { - return false - } - return true + return toCheck.Len() > 0 }) if err != nil { return nil, err diff --git a/pinning/pinner/ipldpinner/pin.go b/pinning/pinner/ipldpinner/pin.go index dc90dd495..562083698 100644 --- a/pinning/pinner/ipldpinner/pin.go +++ b/pinning/pinner/ipldpinner/pin.go @@ -14,6 +14,7 @@ import ( ds "github.com/ipfs/go-datastore" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" + "github.com/ipfs/go-merkledag" mdag "github.com/ipfs/go-merkledag" "github.com/ipfs/go-merkledag/dagutils" @@ -328,35 +329,20 @@ func (p *pinner) CheckIfPinned(ctx context.Context, cids ...cid.Cid) ([]ipfspinn } // Now walk all recursive pins to check for indirect pins - var checkChildren func(cid.Cid, cid.Cid) error - checkChildren = func(rk, parentKey cid.Cid) error { - links, err := ipld.GetLinks(ctx, p.dserv, parentKey) - if err != nil { - return err - } - for _, lnk := range links { - c := lnk.Cid + visited := cid.NewSet() + for _, rk := range p.recursePin.Keys() { + err := merkledag.Walk(ctx, merkledag.GetLinksWithDAG(p.dserv), rk, func(c cid.Cid) bool { + if toCheck.Len() == 0 || !visited.Visit(c) { + return false + } if toCheck.Has(c) { - pinned = append(pinned, - ipfspinner.Pinned{Key: c, Mode: ipfspinner.Indirect, Via: rk}) + pinned = append(pinned, ipfspinner.Pinned{Key: c, Mode: ipfspinner.Indirect, Via: rk}) toCheck.Remove(c) } - err := checkChildren(rk, c) - if err != nil { - return err - } - - if toCheck.Len() == 0 { - return nil - } - } - return nil - } - - for _, rk := range p.recursePin.Keys() { - err := checkChildren(rk, rk) + return true + }, merkledag.Concurrent()) if err != nil { return nil, err } From cc03039011eb9c1f6db2a5e519b982504e4cd61a Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 18 Feb 2021 22:54:58 +0100 Subject: [PATCH 3289/3817] Update imports This commit was moved from ipfs/go-namesys@9fb2fb3cd08ec4fd66a595e6b060b97453a271ea --- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 4 ++-- namesys/resolve/resolve.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 84dcc911c..52b8eb8a4 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -6,7 +6,7 @@ import ( "time" keystore "github.com/ipfs/go-ipfs/keystore" - namesys "github.com/ipfs/go-ipfs/namesys" + namesys "github.com/ipfs/go-namesys" path "github.com/ipfs/go-path" proto "github.com/gogo/protobuf/proto" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index c75d7faa9..1f0dc6fab 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -20,8 +20,8 @@ import ( "github.com/ipfs/go-ipfs/core" "github.com/ipfs/go-ipfs/core/bootstrap" mock "github.com/ipfs/go-ipfs/core/mock" - namesys "github.com/ipfs/go-ipfs/namesys" - . "github.com/ipfs/go-ipfs/namesys/republisher" + namesys "github.com/ipfs/go-namesys" + . "github.com/ipfs/go-namesys/republisher" ) func TestRepublish(t *testing.T) { diff --git a/namesys/resolve/resolve.go b/namesys/resolve/resolve.go index f838a6611..5f1b4eed9 100644 --- a/namesys/resolve/resolve.go +++ b/namesys/resolve/resolve.go @@ -8,7 +8,7 @@ import ( "github.com/ipfs/go-path" - "github.com/ipfs/go-ipfs/namesys" + "github.com/ipfs/go-namesys" ) // ErrNoNamesys is an explicit error for when an IPFS node doesn't From 09e736ba65857561531b02cdc84b495125376396 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 18 Feb 2021 23:55:06 +0100 Subject: [PATCH 3290/3817] Remove dependencies to go-ipfs/core This commit was moved from ipfs/go-namesys@cfd50975208753b1da7fa82a34d534682b53155a --- namesys/republisher/repub_test.go | 149 ++++++++++++++++-------------- 1 file changed, 82 insertions(+), 67 deletions(-) diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 1f0dc6fab..3e79eb666 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -9,63 +9,91 @@ import ( "github.com/gogo/protobuf/proto" goprocess "github.com/jbenet/goprocess" + "github.com/libp2p/go-libp2p" + ic "github.com/libp2p/go-libp2p-core/crypto" + host "github.com/libp2p/go-libp2p-core/host" peer "github.com/libp2p/go-libp2p-core/peer" - mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" + routing "github.com/libp2p/go-libp2p-core/routing" + dht "github.com/libp2p/go-libp2p-kad-dht" ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipns" - "github.com/ipfs/go-ipns/pb" + ipns_pb "github.com/ipfs/go-ipns/pb" path "github.com/ipfs/go-path" - "github.com/ipfs/go-ipfs/core" - "github.com/ipfs/go-ipfs/core/bootstrap" - mock "github.com/ipfs/go-ipfs/core/mock" + keystore "github.com/ipfs/go-ipfs/keystore" namesys "github.com/ipfs/go-namesys" . "github.com/ipfs/go-namesys/republisher" ) +type mockNode struct { + h host.Host + id string + privKey ic.PrivKey + store ds.Batching + dht *dht.IpfsDHT + keystore keystore.Keystore +} + +func getMockNode(t *testing.T, ctx context.Context) *mockNode { + t.Helper() + + dstore := dssync.MutexWrap(ds.NewMapDatastore()) + var idht *dht.IpfsDHT + h, err := libp2p.New( + ctx, + libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0"), + libp2p.Routing(func(h host.Host) (routing.PeerRouting, error) { + rt, err := dht.New(ctx, h, dht.Mode(dht.ModeServer)) + idht = rt + return rt, err + }), + ) + if err != nil { + t.Fatal(err) + } + + return &mockNode{ + h: h, + id: h.ID().Pretty(), + privKey: h.Peerstore().PrivKey(h.ID()), + store: dstore, + dht: idht, + keystore: keystore.NewMemKeystore(), + } +} + func TestRepublish(t *testing.T) { // set cache life to zero for testing low-period repubs ctx, cancel := context.WithCancel(context.Background()) defer cancel() - // create network - mn := mocknet.New(ctx) - - var nodes []*core.IpfsNode + var nsystems []namesys.NameSystem + var nodes []*mockNode for i := 0; i < 10; i++ { - nd, err := mock.MockPublicNode(ctx, mn) - if err != nil { - t.Fatal(err) - } - - nd.Namesys = namesys.NewNameSystem(nd.Routing, nd.Repo.Datastore(), 0) + n := getMockNode(t, ctx) + ns := namesys.NewNameSystem(n.dht, n.store, 0) - nodes = append(nodes, nd) + nsystems = append(nsystems, ns) + nodes = append(nodes, n) } - if err := mn.LinkAll(); err != nil { - t.Fatal(err) - } - - bsinf := bootstrap.BootstrapConfigWithPeers( - []peer.AddrInfo{ - nodes[0].Peerstore.PeerInfo(nodes[0].Identity), - }, - ) + pinfo := host.InfoFromHost(nodes[0].h) for _, n := range nodes[1:] { - if err := n.Bootstrap(bsinf); err != nil { + if err := n.h.Connect(ctx, *pinfo); err != nil { t.Fatal(err) } } // have one node publish a record that is valid for 1 second publisher := nodes[3] + p := path.FromString("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") // does not need to be valid - rp := namesys.NewIpnsPublisher(publisher.Routing, publisher.Repo.Datastore()) - name := "/ipns/" + publisher.Identity.Pretty() + rp := namesys.NewIpnsPublisher(publisher.dht, publisher.store) + name := "/ipns/" + publisher.id // Retry in case the record expires before we can fetch it. This can // happen when running the test on a slow machine. @@ -73,12 +101,12 @@ func TestRepublish(t *testing.T) { timeout := time.Second for { expiration = time.Now().Add(time.Second) - err := rp.PublishWithEOL(ctx, publisher.PrivateKey, p, expiration) + err := rp.PublishWithEOL(ctx, publisher.privKey, p, expiration) if err != nil { t.Fatal(err) } - err = verifyResolution(nodes, name, p) + err = verifyResolution(nsystems, name, p) if err == nil { break } @@ -92,14 +120,14 @@ func TestRepublish(t *testing.T) { // Now wait a second, the records will be invalid and we should fail to resolve time.Sleep(timeout) - if err := verifyResolutionFails(nodes, name); err != nil { + if err := verifyResolutionFails(nsystems, name); err != nil { t.Fatal(err) } // The republishers that are contained within the nodes have their timeout set // to 12 hours. Instead of trying to tweak those, we're just going to pretend // they don't exist and make our own. - repub := NewRepublisher(rp, publisher.Repo.Datastore(), publisher.PrivateKey, publisher.Repo.Keystore()) + repub := NewRepublisher(rp, publisher.store, publisher.privKey, publisher.keystore) repub.Interval = time.Second repub.RecordLifetime = time.Second * 5 @@ -110,7 +138,7 @@ func TestRepublish(t *testing.T) { time.Sleep(time.Second * 2) // we should be able to resolve them now - if err := verifyResolution(nodes, name, p); err != nil { + if err := verifyResolution(nsystems, name, p); err != nil { t.Fatal(err) } } @@ -121,33 +149,20 @@ func TestLongEOLRepublish(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - // create network - mn := mocknet.New(ctx) - - var nodes []*core.IpfsNode + var nsystems []namesys.NameSystem + var nodes []*mockNode for i := 0; i < 10; i++ { - nd, err := mock.MockPublicNode(ctx, mn) - if err != nil { - t.Fatal(err) - } + n := getMockNode(t, ctx) + ns := namesys.NewNameSystem(n.dht, n.store, 0) - nd.Namesys = namesys.NewNameSystem(nd.Routing, nd.Repo.Datastore(), 0) - - nodes = append(nodes, nd) + nsystems = append(nsystems, ns) + nodes = append(nodes, n) } - if err := mn.LinkAll(); err != nil { - t.Fatal(err) - } - - bsinf := bootstrap.BootstrapConfigWithPeers( - []peer.AddrInfo{ - nodes[0].Peerstore.PeerInfo(nodes[0].Identity), - }, - ) + pinfo := host.InfoFromHost(nodes[0].h) for _, n := range nodes[1:] { - if err := n.Bootstrap(bsinf); err != nil { + if err := n.h.Connect(ctx, *pinfo); err != nil { t.Fatal(err) } } @@ -155,16 +170,16 @@ func TestLongEOLRepublish(t *testing.T) { // have one node publish a record that is valid for 1 second publisher := nodes[3] p := path.FromString("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") // does not need to be valid - rp := namesys.NewIpnsPublisher(publisher.Routing, publisher.Repo.Datastore()) - name := "/ipns/" + publisher.Identity.Pretty() + rp := namesys.NewIpnsPublisher(publisher.dht, publisher.store) + name := "/ipns/" + publisher.id expiration := time.Now().Add(time.Hour) - err := rp.PublishWithEOL(ctx, publisher.PrivateKey, p, expiration) + err := rp.PublishWithEOL(ctx, publisher.privKey, p, expiration) if err != nil { t.Fatal(err) } - err = verifyResolution(nodes, name, p) + err = verifyResolution(nsystems, name, p) if err != nil { t.Fatal(err) } @@ -172,7 +187,7 @@ func TestLongEOLRepublish(t *testing.T) { // The republishers that are contained within the nodes have their timeout set // to 12 hours. Instead of trying to tweak those, we're just going to pretend // they don't exist and make our own. - repub := NewRepublisher(rp, publisher.Repo.Datastore(), publisher.PrivateKey, publisher.Repo.Keystore()) + repub := NewRepublisher(rp, publisher.store, publisher.privKey, publisher.keystore) repub.Interval = time.Millisecond * 500 repub.RecordLifetime = time.Second @@ -182,12 +197,12 @@ func TestLongEOLRepublish(t *testing.T) { // now wait a couple seconds for it to fire a few times time.Sleep(time.Second * 2) - err = verifyResolution(nodes, name, p) + err = verifyResolution(nsystems, name, p) if err != nil { t.Fatal(err) } - entry, err := getLastIPNSEntry(publisher.Repo.Datastore(), publisher.Identity) + entry, err := getLastIPNSEntry(publisher.store, publisher.h.ID()) if err != nil { t.Fatal(err) } @@ -216,11 +231,11 @@ func getLastIPNSEntry(dstore ds.Datastore, id peer.ID) (*ipns_pb.IpnsEntry, erro return e, nil } -func verifyResolution(nodes []*core.IpfsNode, key string, exp path.Path) error { +func verifyResolution(nsystems []namesys.NameSystem, key string, exp path.Path) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - for _, n := range nodes { - val, err := n.Namesys.Resolve(ctx, key) + for _, n := range nsystems { + val, err := n.Resolve(ctx, key) if err != nil { return err } @@ -232,11 +247,11 @@ func verifyResolution(nodes []*core.IpfsNode, key string, exp path.Path) error { return nil } -func verifyResolutionFails(nodes []*core.IpfsNode, key string) error { +func verifyResolutionFails(nsystems []namesys.NameSystem, key string) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - for _, n := range nodes { - _, err := n.Namesys.Resolve(ctx, key) + for _, n := range nsystems { + _, err := n.Resolve(ctx, key) if err == nil { return errors.New("expected resolution to fail") } From d1e3dee42d961606fcb2dccf016dadf4c10cedb6 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 19 Feb 2021 00:09:13 +0100 Subject: [PATCH 3291/3817] Add .travis, licenses, readme, gomod This commit was moved from ipfs/go-ipfs-keystore@3482535a9cc24d44a85e131e0da66ee1df21e688 --- keystore/LICENSE | 8 ++++++++ keystore/LICENSE-APACHE | 5 +++++ keystore/LICENSE-MIT | 19 ++++++++++++++++++ keystore/README.md | 43 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 keystore/LICENSE create mode 100644 keystore/LICENSE-APACHE create mode 100644 keystore/LICENSE-MIT create mode 100644 keystore/README.md diff --git a/keystore/LICENSE b/keystore/LICENSE new file mode 100644 index 000000000..7b5f88c78 --- /dev/null +++ b/keystore/LICENSE @@ -0,0 +1,8 @@ +This project is transitioning from an MIT-only license to a dual MIT/Apache-2.0 license. +Unless otherwise noted, all code contributed prior to 2019-05-06 and not contributed by +a user listed in [this signoff issue](https://github.com/ipfs/go-ipfs/issues/6302) is +licensed under MIT-only. All new contributions (and past contributions since 2019-05-06) +are licensed under a dual MIT/Apache-2.0 license. + +MIT: https://www.opensource.org/licenses/mit +Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/keystore/LICENSE-APACHE b/keystore/LICENSE-APACHE new file mode 100644 index 000000000..14478a3b6 --- /dev/null +++ b/keystore/LICENSE-APACHE @@ -0,0 +1,5 @@ +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/keystore/LICENSE-MIT b/keystore/LICENSE-MIT new file mode 100644 index 000000000..72dc60d84 --- /dev/null +++ b/keystore/LICENSE-MIT @@ -0,0 +1,19 @@ +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/keystore/README.md b/keystore/README.md new file mode 100644 index 000000000..7a74a139c --- /dev/null +++ b/keystore/README.md @@ -0,0 +1,43 @@ +# go-ipfs-keystore + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) + +> go-ipfs-keystore implements keystores for ipfs + +go-ipfs-keystore provides the Keystore interface for key management. Keystores support adding, retrieving, and deleting keys as well as listing all keys and checking for membership. + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Contribute](#contribute) +- [License](#license) + +## Install + +`go-ipfs-keystore` works like a regular Go module: +``` +> go get github.com/ipfs/go-ipfs-keystore +``` + +It uses [Gx](https://github.com/whyrusleeping/gx) to manage dependencies. + +## Usage +``` +import "github.com/ipfs/go-ipfs-keystore" +``` + +## Contribute + +PRs accepted. + +Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +This project is dual-licensed under Apache 2.0 and MIT terms: + +- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) From 344b8821f36dba2eac3a484b96168b6c8cf5d44c Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 19 Feb 2021 00:12:35 +0100 Subject: [PATCH 3292/3817] Add travis CI flag This commit was moved from ipfs/go-ipfs-keystore@1ca81d457701907df63297ad3b2bf4043604b81c --- keystore/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/keystore/README.md b/keystore/README.md index 7a74a139c..4dd7ef31b 100644 --- a/keystore/README.md +++ b/keystore/README.md @@ -3,6 +3,8 @@ [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) [![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![Travis CI](https://travis-ci.org/ipfs/go-ipfs-keystore.svg?branch=master)](https://travis-ci.org/ipfs/go-ipfs-keystore) + > go-ipfs-keystore implements keystores for ipfs From 61b85dd51e6417599fcc0ef24745277cbb4cf845 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 19 Feb 2021 00:14:06 +0100 Subject: [PATCH 3293/3817] Switch badge to travis.com This commit was moved from ipfs/go-ipfs-keystore@2d5cf6f274d7e51ed0e69586756233d23750d1f4 --- keystore/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keystore/README.md b/keystore/README.md index 4dd7ef31b..fc63d66f1 100644 --- a/keystore/README.md +++ b/keystore/README.md @@ -3,7 +3,7 @@ [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) [![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) -[![Travis CI](https://travis-ci.org/ipfs/go-ipfs-keystore.svg?branch=master)](https://travis-ci.org/ipfs/go-ipfs-keystore) +[![Travis CI](https://travis-ci.com/ipfs/go-ipfs-keystore.svg?branch=master)](https://travis-ci.com/ipfs/go-ipfs-keystore) > go-ipfs-keystore implements keystores for ipfs From f80f7a377db717ba4fc73eb7c23ee0a9bbdb88a0 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 19 Feb 2021 00:26:19 +0100 Subject: [PATCH 3294/3817] Use extracted go-ipfs-keystore This commit was moved from ipfs/go-namesys@e77e070152877d9e588831b883a1a5bbdc7dd4f5 --- namesys/republisher/repub.go | 2 +- namesys/republisher/repub_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 52b8eb8a4..6e2f86226 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -5,7 +5,7 @@ import ( "errors" "time" - keystore "github.com/ipfs/go-ipfs/keystore" + keystore "github.com/ipfs/go-ipfs-keystore" namesys "github.com/ipfs/go-namesys" path "github.com/ipfs/go-path" diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 3e79eb666..0d1635aad 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -22,7 +22,7 @@ import ( ipns_pb "github.com/ipfs/go-ipns/pb" path "github.com/ipfs/go-path" - keystore "github.com/ipfs/go-ipfs/keystore" + keystore "github.com/ipfs/go-ipfs-keystore" namesys "github.com/ipfs/go-namesys" . "github.com/ipfs/go-namesys/republisher" ) From f3b8f7a7f2ba819d4a34d6abc5744b752203dbc7 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 19 Feb 2021 00:28:41 +0100 Subject: [PATCH 3295/3817] README: this module does not use Gx This commit was moved from ipfs/go-ipfs-keystore@26d3af14055cdf698799e290144477cf7e641a3b --- keystore/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/keystore/README.md b/keystore/README.md index fc63d66f1..36951daa9 100644 --- a/keystore/README.md +++ b/keystore/README.md @@ -24,8 +24,6 @@ go-ipfs-keystore provides the Keystore interface for key management. Keystores > go get github.com/ipfs/go-ipfs-keystore ``` -It uses [Gx](https://github.com/whyrusleeping/gx) to manage dependencies. - ## Usage ``` import "github.com/ipfs/go-ipfs-keystore" From 63ebbc5188858cc31958882aa39fdca93bcb4d5d Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 19 Feb 2021 00:30:44 +0100 Subject: [PATCH 3296/3817] Add .travis, LICENSE, README This commit was moved from ipfs/go-namesys@5bf90d860320beefeb773f78291236c68b646c93 --- namesys/LICENSE | 8 ++++++++ namesys/LICENSE-APACHE | 5 +++++ namesys/LICENSE-MIT | 19 +++++++++++++++++++ namesys/README.md | 43 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 namesys/LICENSE create mode 100644 namesys/LICENSE-APACHE create mode 100644 namesys/LICENSE-MIT create mode 100644 namesys/README.md diff --git a/namesys/LICENSE b/namesys/LICENSE new file mode 100644 index 000000000..7b5f88c78 --- /dev/null +++ b/namesys/LICENSE @@ -0,0 +1,8 @@ +This project is transitioning from an MIT-only license to a dual MIT/Apache-2.0 license. +Unless otherwise noted, all code contributed prior to 2019-05-06 and not contributed by +a user listed in [this signoff issue](https://github.com/ipfs/go-ipfs/issues/6302) is +licensed under MIT-only. All new contributions (and past contributions since 2019-05-06) +are licensed under a dual MIT/Apache-2.0 license. + +MIT: https://www.opensource.org/licenses/mit +Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/namesys/LICENSE-APACHE b/namesys/LICENSE-APACHE new file mode 100644 index 000000000..14478a3b6 --- /dev/null +++ b/namesys/LICENSE-APACHE @@ -0,0 +1,5 @@ +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/namesys/LICENSE-MIT b/namesys/LICENSE-MIT new file mode 100644 index 000000000..72dc60d84 --- /dev/null +++ b/namesys/LICENSE-MIT @@ -0,0 +1,19 @@ +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/namesys/README.md b/namesys/README.md new file mode 100644 index 000000000..ab950f444 --- /dev/null +++ b/namesys/README.md @@ -0,0 +1,43 @@ +# go-namesys + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) +[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![Travis CI](https://travis-ci.com/ipfs/go-namesys.svg?branch=master)](https://travis-ci.com/ipfs/go-namesys) + + +> go-namesys provides publish and resolution support for the /ipns/ namespace + +go-namesys allows to publish and resolve IPNS records or dnslink domain names. + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Contribute](#contribute) +- [License](#license) + +## Install + +`go-namesys` works like a regular Go module: +``` +> go get github.com/ipfs/go-namesys +``` + +## Usage +``` +import "github.com/ipfs/go-namesys" +``` + +## Contribute + +PRs accepted. + +Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + +## License + +This project is dual-licensed under Apache 2.0 and MIT terms: + +- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) From de8b73ddd55e2907d1195e2482346321896c3f2f Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 19 Feb 2021 16:48:56 +0100 Subject: [PATCH 3297/3817] Fix staticcheck issues This commit was moved from ipfs/go-namesys@b5861cdf3280faf6f27d38bff5de4bf39ebf0c41 --- namesys/namesys_test.go | 4 +++- namesys/resolve_test.go | 7 +++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index cc0ca6959..68ff46744 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -120,6 +120,8 @@ func TestPublishWithCache0(t *testing.T) { } } +type ctxKey string + func TestPublishWithTTL(t *testing.T) { dst := dssync.MutexWrap(ds.NewMapDatastore()) priv, _, err := ci.GenerateKeyPair(ci.RSA, 2048) @@ -150,7 +152,7 @@ func TestPublishWithTTL(t *testing.T) { ttl := 1 * time.Second eol := time.Now().Add(2 * time.Second) - ctx := context.WithValue(context.Background(), "ipns-publish-ttl", ttl) + ctx := context.WithValue(context.Background(), ctxKey("ipns-publish-ttl"), ttl) err = nsys.Publish(ctx, priv, p) if err != nil { t.Fatal(err) diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index 4f92a2d0d..f8b243669 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -11,14 +11,13 @@ import ( mockrouting "github.com/ipfs/go-ipfs-routing/mock" ipns "github.com/ipfs/go-ipns" path "github.com/ipfs/go-path" - testutil "github.com/libp2p/go-libp2p-testing/net" tnet "github.com/libp2p/go-libp2p-testing/net" ) func TestRoutingResolve(t *testing.T) { dstore := dssync.MutexWrap(ds.NewMapDatastore()) serv := mockrouting.NewServer() - id := testutil.RandIdentityOrFatal(t) + id := tnet.RandIdentityOrFatal(t) d := serv.ClientWithDatastore(context.Background(), id, dstore) resolver := NewIpnsResolver(d) @@ -44,7 +43,7 @@ func TestRoutingResolve(t *testing.T) { func TestPrexistingExpiredRecord(t *testing.T) { dstore := dssync.MutexWrap(ds.NewMapDatastore()) - d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore) + d := mockrouting.NewServer().ClientWithDatastore(context.Background(), tnet.RandIdentityOrFatal(t), dstore) resolver := NewIpnsResolver(d) publisher := NewIpnsPublisher(d, dstore) @@ -78,7 +77,7 @@ func TestPrexistingExpiredRecord(t *testing.T) { func TestPrexistingRecord(t *testing.T) { dstore := dssync.MutexWrap(ds.NewMapDatastore()) - d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore) + d := mockrouting.NewServer().ClientWithDatastore(context.Background(), tnet.RandIdentityOrFatal(t), dstore) resolver := NewIpnsResolver(d) publisher := NewIpnsPublisher(d, dstore) From 92dcd0cab6744cbc201251a2be674734ea590c0c Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 19 Feb 2021 17:56:39 +0100 Subject: [PATCH 3298/3817] golint: Improve package documentation and readme. This commit was moved from ipfs/go-namesys@0d5c0a8201e49123b5a6e9d9f743a89406b89533 --- namesys/README.md | 14 +++++++++++--- namesys/dns.go | 5 +++-- namesys/interface.go | 2 +- namesys/namesys.go | 13 +++++++++++++ namesys/proquint.go | 2 ++ namesys/publisher.go | 15 ++++++++++++++- namesys/republisher/repub.go | 6 ++++++ 7 files changed, 50 insertions(+), 7 deletions(-) diff --git a/namesys/README.md b/namesys/README.md index ab950f444..5c17728da 100644 --- a/namesys/README.md +++ b/namesys/README.md @@ -1,14 +1,20 @@ # go-namesys -[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai) [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) -[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![Go Reference](https://pkg.go.dev/badge/github.com/ipfs/go-namesys.svg)](https://pkg.go.dev/github.com/ipfs/go-namesys) [![Travis CI](https://travis-ci.com/ipfs/go-namesys.svg?branch=master)](https://travis-ci.com/ipfs/go-namesys) > go-namesys provides publish and resolution support for the /ipns/ namespace -go-namesys allows to publish and resolve IPNS records or dnslink domain names. +Package namesys defines `Resolver` and `Publisher` interfaces for IPNS paths, that is, paths in the form of `/ipns/`. A "resolved" IPNS path becomes an `/ipfs/` path. + +Traditionally, these paths would be in the form of `/ipns/peer_id`, which references an IPNS record in a distributed `ValueStore` (usually the IPFS DHT). + +Additionally, the /ipns/ namespace can also be used with domain names that use DNSLink (/ipns/my.domain.example, see https://dnslink.io) and proquint strings. + +The package provides implementations for all three resolvers. ## Table of Contents @@ -29,6 +35,8 @@ go-namesys allows to publish and resolve IPNS records or dnslink domain names. import "github.com/ipfs/go-namesys" ``` +See the [Pkg.go.dev documentation](https://pkg.go.dev/github.com/ipfs/go-namesys) + ## Contribute PRs accepted. diff --git a/namesys/dns.go b/namesys/dns.go index 738612f46..d8a42f210 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -15,6 +15,7 @@ import ( const ethTLD = "eth" const linkTLD = "link" +// LookupTXTFunc is a generic type for a function that lookups TXT record values. type LookupTXTFunc func(name string) (txt []string, err error) // DNSResolver implements a Resolver on DNS domains @@ -146,10 +147,10 @@ func parseEntry(txt string) (path.Path, error) { return p, nil } - return tryParseDnsLink(txt) + return tryParseDNSLink(txt) } -func tryParseDnsLink(txt string) (path.Path, error) { +func tryParseDNSLink(txt string) (path.Path, error) { parts := strings.SplitN(txt, "=", 2) if len(parts) == 2 && parts[0] == "dnslink" { return path.ParsePath(parts[1]) diff --git a/namesys/interface.go b/namesys/interface.go index ecd80943b..b4136dfcc 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -50,7 +50,7 @@ var ErrResolveRecursion = errors.New( // ErrPublishFailed signals an error when attempting to publish. var ErrPublishFailed = errors.New("could not publish name") -// Namesys represents a cohesive name publishing and resolving system. +// NameSystem represents a cohesive name publishing and resolving system. // // Publishing a name is the process of establishing a mapping, a key-value // pair, according to naming rules and databases. diff --git a/namesys/namesys.go b/namesys/namesys.go index 760d04c17..ae77771d7 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -1,3 +1,16 @@ +// Package namesys defines Resolver and Publisher interfaces for IPNS paths, +// that is, IPFS paths in the form of /ipns/. A "resolved" +// IPNS path becomes an /ipfs/ path. +// +// Traditionally, these paths would be in the form of /ipns/peer_id, which +// references an IPNS record in a distributed ValueStore (usually the IPFS +// DHT). +// +// Additionally, the /ipns/ namespace can also be used with domain names that +// use DNSLink (/ipns/my.domain.example, see https://dnslink.io) and proquint +// strings. +// +// The package provides implementations for all three resolvers. package namesys import ( diff --git a/namesys/proquint.go b/namesys/proquint.go index 63cb62a04..b918ec986 100644 --- a/namesys/proquint.go +++ b/namesys/proquint.go @@ -9,6 +9,8 @@ import ( opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" ) +// ProquintResolver implements the Resolver interface for proquint identifiers +// (see http://arxiv.org/html/0901.4016). type ProquintResolver struct{} // Resolve implements Resolver. diff --git a/namesys/publisher.go b/namesys/publisher.go index f558eaf28..11cc6dcd7 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -22,6 +22,9 @@ import ( const ipnsPrefix = "/ipns/" +// DefaultRecordEOL specifies the time that the network will cache IPNS +// records after being publihsed. Records should be re-published before this +// interval expires. const DefaultRecordEOL = 24 * time.Hour // IpnsPublisher is capable of publishing and resolving names to the IPFS @@ -49,11 +52,13 @@ func (p *IpnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Pa return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordEOL)) } +// IpnsDsKey returns a datastore key given an IPNS identifier (peer +// ID). Defines the storage key for IPNS records in the local datastore. func IpnsDsKey(id peer.ID) ds.Key { return ds.NewKey("/ipns/" + base32.RawStdEncoding.EncodeToString([]byte(id))) } -// PublishedNames returns the latest IPNS records published by this node and +// ListPublished returns the latest IPNS records published by this node and // their expiration times. // // This method will not search the routing system for records published by other @@ -212,6 +217,10 @@ func checkCtxTTL(ctx context.Context) (time.Duration, bool) { return d, ok } +// PutRecordToRouting publishes the given entry using the provided ValueStore, +// using the ID associated to the provided public key and embedding the public +// key in the IPNS entry when it cannot be extracted from the ID. In that +// case, it calls PublishPublicKey in addition to PublishEntry. func PutRecordToRouting(ctx context.Context, r routing.ValueStore, k ci.PubKey, entry *pb.IpnsEntry) error { ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -260,6 +269,8 @@ func waitOnErrChan(ctx context.Context, errs chan error) error { } } +// PublishPublicKey stores the given public key in the ValueStore with the +// given key. func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk ci.PubKey) error { log.Debugf("Storing pubkey at: %s", k) pkbytes, err := pubk.Bytes() @@ -271,6 +282,8 @@ func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk return r.PutValue(ctx, k, pkbytes) } +// PublishEntry stores the given IpnsEntry in the ValueStore with the given +// ipnskey. func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec *pb.IpnsEntry) error { data, err := proto.Marshal(rec) if err != nil { diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 6e2f86226..4ba5d483c 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -1,3 +1,5 @@ +// Package republisher provides a utility to automatically re-publish IPNS +// records related to the keys in a Keystore. package republisher import ( @@ -36,6 +38,8 @@ var FailureRetryInterval = time.Minute * 5 // DefaultRecordLifetime is the default lifetime for IPNS records const DefaultRecordLifetime = time.Hour * 24 +// Republisher facilitates the regular publishing of all the IPNS records +// associated to keys in a Keystore. type Republisher struct { ns namesys.Publisher ds ds.Datastore @@ -60,6 +64,8 @@ func NewRepublisher(ns namesys.Publisher, ds ds.Datastore, self ic.PrivKey, ks k } } +// Run starts the republisher facility. It can be stopped by stopping the +// provided proc. func (rp *Republisher) Run(proc goprocess.Process) { timer := time.NewTimer(InitialRebroadcastDelay) defer timer.Stop() From 9bca99eea424dd50194f9843dcdd506616fe5df4 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 19 Feb 2021 18:02:09 +0100 Subject: [PATCH 3299/3817] Add link to pkg.go.dev This commit was moved from ipfs/go-ipfs-keystore@57e438e43d6628e4077ace2f7387440feaa88dce --- keystore/README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/keystore/README.md b/keystore/README.md index 36951daa9..5fb84c6a1 100644 --- a/keystore/README.md +++ b/keystore/README.md @@ -1,9 +1,9 @@ # go-ipfs-keystore -[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai) [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) -[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) [![Travis CI](https://travis-ci.com/ipfs/go-ipfs-keystore.svg?branch=master)](https://travis-ci.com/ipfs/go-ipfs-keystore) +[![Go Reference](https://pkg.go.dev/badge/github.com/ipfs/go-ipfs-keystore.svg)](https://pkg.go.dev/github.com/ipfs/go-ipfs-keystore) > go-ipfs-keystore implements keystores for ipfs @@ -33,8 +33,6 @@ import "github.com/ipfs/go-ipfs-keystore" PRs accepted. -Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. - ## License This project is dual-licensed under Apache 2.0 and MIT terms: From c779e53df1b0ef1cbbf11e1a799b376dde91665e Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 19 Feb 2021 18:08:36 +0100 Subject: [PATCH 3300/3817] Revert staticcheck fix as it breaks things. This commit was moved from ipfs/go-namesys@71c6e11b7f804a4053db4eaf0dd6c7cb3369ae6b --- namesys/namesys_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 68ff46744..cc0ca6959 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -120,8 +120,6 @@ func TestPublishWithCache0(t *testing.T) { } } -type ctxKey string - func TestPublishWithTTL(t *testing.T) { dst := dssync.MutexWrap(ds.NewMapDatastore()) priv, _, err := ci.GenerateKeyPair(ci.RSA, 2048) @@ -152,7 +150,7 @@ func TestPublishWithTTL(t *testing.T) { ttl := 1 * time.Second eol := time.Now().Add(2 * time.Second) - ctx := context.WithValue(context.Background(), ctxKey("ipns-publish-ttl"), ttl) + ctx := context.WithValue(context.Background(), "ipns-publish-ttl", ttl) err = nsys.Publish(ctx, priv, p) if err != nil { t.Fatal(err) From be01b8f31225f7a27ef4cf8e1233994d68bd6724 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 19 Feb 2021 21:53:22 +0100 Subject: [PATCH 3301/3817] Update publisher.go Co-authored-by: Adin Schmahmann This commit was moved from ipfs/go-namesys@e738bc0769b2eba77d0ba33e147d97293ba72c94 --- namesys/publisher.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 11cc6dcd7..e2f9e67d8 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -218,9 +218,8 @@ func checkCtxTTL(ctx context.Context) (time.Duration, bool) { } // PutRecordToRouting publishes the given entry using the provided ValueStore, -// using the ID associated to the provided public key and embedding the public -// key in the IPNS entry when it cannot be extracted from the ID. In that -// case, it calls PublishPublicKey in addition to PublishEntry. +// keyed on the ID associated with the provided public key. The public key is +// also made available to the routing system so that entries can be verified. func PutRecordToRouting(ctx context.Context, r routing.ValueStore, k ci.PubKey, entry *pb.IpnsEntry) error { ctx, cancel := context.WithCancel(ctx) defer cancel() From ce6ed1b2303d3149a4eb8c143898040ebdeff1ea Mon Sep 17 00:00:00 2001 From: acruikshank Date: Mon, 22 Feb 2021 16:07:48 -0500 Subject: [PATCH 3302/3817] first commit This commit was moved from ipfs/go-fetcher@fae8c9ed946c4fcd02552f75c0205f9298b5087a --- fetcher/.gitignore | 0 fetcher/LICENSE | 21 ++++++ fetcher/README.md.go | 1 + fetcher/fetcher.go | 105 ++++++++++++++++++++++++++ fetcher/fetcher_test.go | 158 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 285 insertions(+) create mode 100644 fetcher/.gitignore create mode 100644 fetcher/LICENSE create mode 100644 fetcher/README.md.go create mode 100644 fetcher/fetcher.go create mode 100644 fetcher/fetcher_test.go diff --git a/fetcher/.gitignore b/fetcher/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/fetcher/LICENSE b/fetcher/LICENSE new file mode 100644 index 000000000..713896e4e --- /dev/null +++ b/fetcher/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Eric Myhre + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/fetcher/README.md.go b/fetcher/README.md.go new file mode 100644 index 000000000..b306ea498 --- /dev/null +++ b/fetcher/README.md.go @@ -0,0 +1 @@ +package fetcher diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go new file mode 100644 index 000000000..9e9062771 --- /dev/null +++ b/fetcher/fetcher.go @@ -0,0 +1,105 @@ +package fetcher + +import ( + "bytes" + "context" + "fmt" + "io" + + "github.com/ipld/go-ipld-prime/traversal/selector/builder" + + "github.com/ipld/go-ipld-prime/traversal" + + "github.com/ipld/go-ipld-prime/traversal/selector" + + "github.com/ipfs/go-bitswap" + + "github.com/ipfs/go-cid" + "github.com/ipld/go-ipld-prime" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + basicnode "github.com/ipld/go-ipld-prime/node/basic" +) + +// TODO: need to support sessions + +type Fetcher struct { + Exchange *bitswap.Bitswap +} + +func NewFetcher(exchange *bitswap.Bitswap) *Fetcher { + return &Fetcher{Exchange: exchange} +} + +func (f *Fetcher) FetchNode(ctx context.Context, c cid.Cid) (ipld.Node, error) { + nb := basicnode.Prototype.Any.NewBuilder() + + err := cidlink.Link{Cid: c}.Load(ctx, ipld.LinkContext{}, nb, f.loader(ctx)) + if err != nil { + return nil, err + } + + return nb.Build(), nil +} + +type NodeResult struct { + Node ipld.Node + Err error +} + +func (f *Fetcher) FetchMatching(ctx context.Context, root cid.Cid, match selector.Selector) (chan NodeResult, error) { + node, err := f.FetchNode(ctx, root) + if err != nil { + return nil, err + } + + results := make(chan NodeResult) + + go func() { + defer close(results) + err = traversal.Progress{ + Cfg: &traversal.Config{ + LinkLoader: f.loader(ctx), + LinkTargetNodePrototypeChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodePrototype, error) { + return basicnode.Prototype__Any{}, nil + }, + }, + }.WalkMatching(node, match, func(prog traversal.Progress, n ipld.Node) error { + results <- NodeResult{Node: n} + return nil + }) + if err != nil { + results <- NodeResult{Err: err} + } + }() + + return results, nil +} + +func (f *Fetcher) FetchAll(ctx context.Context, root cid.Cid) (chan NodeResult, error) { + ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype__Any{}) + allSelector, err := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( + ssb.Matcher(), + ssb.ExploreAll(ssb.ExploreRecursiveEdge()), + )).Selector() + if err != nil { + return nil, err + } + return f.FetchMatching(ctx, root, allSelector) +} + +// TODO: take optional Cid channel for links traversed +func (f *Fetcher) loader(ctx context.Context) ipld.Loader { + return func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) { + cidLink, ok := lnk.(cidlink.Link) + if !ok { + return nil, fmt.Errorf("invalid link type for loading: %v", lnk) + } + + blk, err := f.Exchange.GetBlock(ctx, cidLink.Cid) + if err != nil { + return nil, err + } + + return bytes.NewReader(blk.RawData()), nil + } +} diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go new file mode 100644 index 000000000..adb9d2b49 --- /dev/null +++ b/fetcher/fetcher_test.go @@ -0,0 +1,158 @@ +package fetcher_test + +import ( + "bytes" + "context" + "fmt" + "io" + "testing" + "time" + + testinstance "github.com/ipfs/go-bitswap/testinstance" + tn "github.com/ipfs/go-bitswap/testnet" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + delay "github.com/ipfs/go-ipfs-delay" + mockrouting "github.com/ipfs/go-ipfs-routing/mock" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/codec/dagcbor" + "github.com/ipld/go-ipld-prime/fluent" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/magiconair/properties/assert" + "github.com/stretchr/testify/require" + + "github.com/ipfs/fetcher" +) + +var _ cidlink.MulticodecDecoder = dagcbor.Decoder + +func TestFetchIPLDPrimeNode(t *testing.T) { + block, node, _ := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { + na.AssembleEntry("foo").AssignBool(true) + na.AssembleEntry("bar").AssignBool(false) + na.AssembleEntry("nested").CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry("nonlink").AssignString("zoo") + }) + })) + + net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(0*time.Millisecond)) + ig := testinstance.NewTestInstanceGenerator(net, nil, nil) + defer ig.Close() + + peers := ig.Instances(2) + hasBlock := peers[0] + defer hasBlock.Exchange.Close() + + err := hasBlock.Exchange.HasBlock(block) + require.NoError(t, err) + + wantsBlock := peers[1] + defer wantsBlock.Exchange.Close() + + fetch := fetcher.NewFetcher(wantsBlock.Exchange) + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + retrievedNode, err := fetch.FetchNode(ctx, block.Cid()) + require.NoError(t, err) + assert.Equal(t, node, retrievedNode) +} + +func TestFetchIPLDGraph(t *testing.T) { + block3, node3, link3 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleEntry("three").AssignBool(true) + })) + block4, node4, link4 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleEntry("four").AssignBool(true) + })) + block2, node2, link2 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { + na.AssembleEntry("link3").AssignLink(link3) + na.AssembleEntry("link4").AssignLink(link4) + })) + block1, node1, _ := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { + na.AssembleEntry("foo").AssignBool(true) + na.AssembleEntry("bar").AssignBool(false) + na.AssembleEntry("nested").CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry("link2").AssignLink(link2) + na.AssembleEntry("nonlink").AssignString("zoo") + }) + })) + + net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(0*time.Millisecond)) + ig := testinstance.NewTestInstanceGenerator(net, nil, nil) + defer ig.Close() + + peers := ig.Instances(2) + hasBlock := peers[0] + defer hasBlock.Exchange.Close() + + err := hasBlock.Exchange.HasBlock(block1) + require.NoError(t, err) + err = hasBlock.Exchange.HasBlock(block2) + require.NoError(t, err) + err = hasBlock.Exchange.HasBlock(block3) + require.NoError(t, err) + err = hasBlock.Exchange.HasBlock(block4) + require.NoError(t, err) + + wantsBlock := peers[1] + defer wantsBlock.Exchange.Close() + + fetch := fetcher.NewFetcher(wantsBlock.Exchange) + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + nodeChan, err := fetch.FetchAll(ctx, block1.Cid()) + require.NoError(t, err) + + order := 0 + for res := range nodeChan { + require.NoError(t, res.Err) + + switch order { + case 0: + assert.Equal(t, node1, res.Node) + case 4: + assert.Equal(t, node2, res.Node) + case 5: + assert.Equal(t, node3, res.Node) + case 7: + assert.Equal(t, node4, res.Node) + } + order++ + } + + // expect 10 nodes altogether including sub nodes + assert.Equal(t, 10, order) +} + +func encodeBlock(n ipld.Node) (blocks.Block, ipld.Node, ipld.Link) { + lb := cidlink.LinkBuilder{cid.Prefix{ + Version: 1, + Codec: 0x71, + MhType: 0x17, + MhLength: 4, + }} + var b blocks.Block + lnk, err := lb.Build(context.Background(), ipld.LinkContext{}, n, + func(ipld.LinkContext) (io.Writer, ipld.StoreCommitter, error) { + buf := bytes.Buffer{} + return &buf, func(lnk ipld.Link) error { + clnk, ok := lnk.(cidlink.Link) + if !ok { + return fmt.Errorf("incorrect link type %v", lnk) + } + var err error + b, err = blocks.NewBlockWithCid(buf.Bytes(), clnk.Cid) + return err + }, nil + }, + ) + if err != nil { + panic(err) + } + return b, n, lnk +} From 61eeca5c2f60ecd8b9d1ecc944b9ebf1fd3f8bd7 Mon Sep 17 00:00:00 2001 From: acruikshank Date: Mon, 22 Feb 2021 17:36:00 -0500 Subject: [PATCH 3303/3817] error channel, renaming This commit was moved from ipfs/go-fetcher@5aa0f059687a5791b248b880c25906a94a21bb51 --- fetcher/fetcher.go | 71 +++++++++++++++++++++++------------------ fetcher/fetcher_test.go | 37 +++++++++++++-------- 2 files changed, 63 insertions(+), 45 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 9e9062771..cf99c6063 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -6,31 +6,34 @@ import ( "fmt" "io" - "github.com/ipld/go-ipld-prime/traversal/selector/builder" - - "github.com/ipld/go-ipld-prime/traversal" - - "github.com/ipld/go-ipld-prime/traversal/selector" - "github.com/ipfs/go-bitswap" - "github.com/ipfs/go-cid" "github.com/ipld/go-ipld-prime" cidlink "github.com/ipld/go-ipld-prime/linking/cid" basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/ipld/go-ipld-prime/traversal" + "github.com/ipld/go-ipld-prime/traversal/selector" + "github.com/ipld/go-ipld-prime/traversal/selector/builder" ) // TODO: need to support sessions type Fetcher struct { - Exchange *bitswap.Bitswap + exchange *bitswap.Bitswap +} + +type FetchResult struct { + Node ipld.Node + Path ipld.Path + LastBlockPath ipld.Path + LastBlockLink ipld.Link } -func NewFetcher(exchange *bitswap.Bitswap) *Fetcher { - return &Fetcher{Exchange: exchange} +func NewFetcher(exchange *bitswap.Bitswap) Fetcher { + return Fetcher{exchange: exchange} } -func (f *Fetcher) FetchNode(ctx context.Context, c cid.Cid) (ipld.Node, error) { +func (f Fetcher) FetchNode(ctx context.Context, c cid.Cid) (ipld.Node, error) { nb := basicnode.Prototype.Any.NewBuilder() err := cidlink.Link{Cid: c}.Load(ctx, ipld.LinkContext{}, nb, f.loader(ctx)) @@ -41,21 +44,20 @@ func (f *Fetcher) FetchNode(ctx context.Context, c cid.Cid) (ipld.Node, error) { return nb.Build(), nil } -type NodeResult struct { - Node ipld.Node - Err error -} - -func (f *Fetcher) FetchMatching(ctx context.Context, root cid.Cid, match selector.Selector) (chan NodeResult, error) { - node, err := f.FetchNode(ctx, root) - if err != nil { - return nil, err - } - - results := make(chan NodeResult) +func (f Fetcher) FetchMatching(ctx context.Context, root cid.Cid, match selector.Selector) (chan FetchResult, chan error) { + results := make(chan FetchResult) + errors := make(chan error) go func() { defer close(results) + + // retrieve first node + node, err := f.FetchNode(ctx, root) + if err != nil { + errors <- err + return + } + err = traversal.Progress{ Cfg: &traversal.Config{ LinkLoader: f.loader(ctx), @@ -64,38 +66,45 @@ func (f *Fetcher) FetchMatching(ctx context.Context, root cid.Cid, match selecto }, }, }.WalkMatching(node, match, func(prog traversal.Progress, n ipld.Node) error { - results <- NodeResult{Node: n} + results <- FetchResult{ + Node: n, + Path: prog.Path, + LastBlockPath: prog.LastBlock.Path, + LastBlockLink: prog.LastBlock.Link, + } return nil }) if err != nil { - results <- NodeResult{Err: err} + errors <- err + return } }() - return results, nil + return results, errors } -func (f *Fetcher) FetchAll(ctx context.Context, root cid.Cid) (chan NodeResult, error) { +func (f Fetcher) FetchAll(ctx context.Context, root cid.Cid) (chan FetchResult, chan error) { ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype__Any{}) allSelector, err := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( ssb.Matcher(), ssb.ExploreAll(ssb.ExploreRecursiveEdge()), )).Selector() if err != nil { - return nil, err + errors := make(chan error, 1) + errors <- err + return nil, errors } return f.FetchMatching(ctx, root, allSelector) } -// TODO: take optional Cid channel for links traversed -func (f *Fetcher) loader(ctx context.Context) ipld.Loader { +func (f Fetcher) loader(ctx context.Context) ipld.Loader { return func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) { cidLink, ok := lnk.(cidlink.Link) if !ok { return nil, fmt.Errorf("invalid link type for loading: %v", lnk) } - blk, err := f.Exchange.GetBlock(ctx, cidLink.Cid) + blk, err := f.exchange.GetBlock(ctx, cidLink.Cid) if err != nil { return nil, err } diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index adb9d2b49..33fe69c97 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -105,24 +105,33 @@ func TestFetchIPLDGraph(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - nodeChan, err := fetch.FetchAll(ctx, block1.Cid()) + nodeCh, errCh := fetch.FetchAll(ctx, block1.Cid()) require.NoError(t, err) order := 0 - for res := range nodeChan { - require.NoError(t, res.Err) - - switch order { - case 0: - assert.Equal(t, node1, res.Node) - case 4: - assert.Equal(t, node2, res.Node) - case 5: - assert.Equal(t, node3, res.Node) - case 7: - assert.Equal(t, node4, res.Node) + +Loop: + for { + select { + case res, ok := <-nodeCh: + if !ok { + break Loop + } + + switch order { + case 0: + assert.Equal(t, node1, res.Node) + case 4: + assert.Equal(t, node2, res.Node) + case 5: + assert.Equal(t, node3, res.Node) + case 7: + assert.Equal(t, node4, res.Node) + } + order++ + case err := <-errCh: + require.FailNow(t, err.Error()) } - order++ } // expect 10 nodes altogether including sub nodes From 66524ab04201444fe4e127546dbc45f16d9e481a Mon Sep 17 00:00:00 2001 From: acruikshank Date: Mon, 22 Feb 2021 17:45:35 -0500 Subject: [PATCH 3304/3817] simplify naming, add NodeAll This commit was moved from ipfs/go-fetcher@6173aee41ab6741105f08778e7662b213b3a7290 --- fetcher/fetcher.go | 63 +++++++++++++++++++++++++++-------------- fetcher/fetcher_test.go | 4 +-- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index cf99c6063..991e84a26 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -33,7 +33,7 @@ func NewFetcher(exchange *bitswap.Bitswap) Fetcher { return Fetcher{exchange: exchange} } -func (f Fetcher) FetchNode(ctx context.Context, c cid.Cid) (ipld.Node, error) { +func (f Fetcher) Block(ctx context.Context, c cid.Cid) (ipld.Node, error) { nb := basicnode.Prototype.Any.NewBuilder() err := cidlink.Link{Cid: c}.Load(ctx, ipld.LinkContext{}, nb, f.loader(ctx)) @@ -44,7 +44,24 @@ func (f Fetcher) FetchNode(ctx context.Context, c cid.Cid) (ipld.Node, error) { return nb.Build(), nil } -func (f Fetcher) FetchMatching(ctx context.Context, root cid.Cid, match selector.Selector) (chan FetchResult, chan error) { +func (f Fetcher) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector) (chan FetchResult, chan error) { + results := make(chan FetchResult) + errors := make(chan error) + + go func() { + defer close(results) + + err := f.fetch(ctx, node, match, results) + if err != nil { + errors <- err + return + } + }() + + return results, errors +} + +func (f Fetcher) BlockMatching(ctx context.Context, root cid.Cid, match selector.Selector) (chan FetchResult, chan error) { results := make(chan FetchResult) errors := make(chan error) @@ -52,28 +69,13 @@ func (f Fetcher) FetchMatching(ctx context.Context, root cid.Cid, match selector defer close(results) // retrieve first node - node, err := f.FetchNode(ctx, root) + node, err := f.Block(ctx, root) if err != nil { errors <- err return } - err = traversal.Progress{ - Cfg: &traversal.Config{ - LinkLoader: f.loader(ctx), - LinkTargetNodePrototypeChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodePrototype, error) { - return basicnode.Prototype__Any{}, nil - }, - }, - }.WalkMatching(node, match, func(prog traversal.Progress, n ipld.Node) error { - results <- FetchResult{ - Node: n, - Path: prog.Path, - LastBlockPath: prog.LastBlock.Path, - LastBlockLink: prog.LastBlock.Link, - } - return nil - }) + err = f.fetch(ctx, node, match, results) if err != nil { errors <- err return @@ -83,7 +85,7 @@ func (f Fetcher) FetchMatching(ctx context.Context, root cid.Cid, match selector return results, errors } -func (f Fetcher) FetchAll(ctx context.Context, root cid.Cid) (chan FetchResult, chan error) { +func (f Fetcher) BlockAll(ctx context.Context, root cid.Cid) (chan FetchResult, chan error) { ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype__Any{}) allSelector, err := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( ssb.Matcher(), @@ -94,7 +96,26 @@ func (f Fetcher) FetchAll(ctx context.Context, root cid.Cid) (chan FetchResult, errors <- err return nil, errors } - return f.FetchMatching(ctx, root, allSelector) + return f.BlockMatching(ctx, root, allSelector) +} + +func (f Fetcher) fetch(ctx context.Context, node ipld.Node, match selector.Selector, results chan FetchResult) error { + return traversal.Progress{ + Cfg: &traversal.Config{ + LinkLoader: f.loader(ctx), + LinkTargetNodePrototypeChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodePrototype, error) { + return basicnode.Prototype__Any{}, nil + }, + }, + }.WalkMatching(node, match, func(prog traversal.Progress, n ipld.Node) error { + results <- FetchResult{ + Node: n, + Path: prog.Path, + LastBlockPath: prog.LastBlock.Path, + LastBlockLink: prog.LastBlock.Link, + } + return nil + }) } func (f Fetcher) loader(ctx context.Context) ipld.Loader { diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index 33fe69c97..456531f10 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -55,7 +55,7 @@ func TestFetchIPLDPrimeNode(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - retrievedNode, err := fetch.FetchNode(ctx, block.Cid()) + retrievedNode, err := fetch.Block(ctx, block.Cid()) require.NoError(t, err) assert.Equal(t, node, retrievedNode) } @@ -105,7 +105,7 @@ func TestFetchIPLDGraph(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - nodeCh, errCh := fetch.FetchAll(ctx, block1.Cid()) + nodeCh, errCh := fetch.BlockAll(ctx, block1.Cid()) require.NoError(t, err) order := 0 From 2079cd087a3c7e352f5f9ec64a04d9cc1287c414 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Mon, 22 Feb 2021 17:07:19 -0800 Subject: [PATCH 3305/3817] feat(fetcher): use block getter interface This commit was moved from ipfs/go-fetcher@a634b13c7b7144cadf8c434033959b1fb6292791 --- fetcher/fetcher.go | 12 +++++------- fetcher/fetcher_test.go | 11 +++++++---- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 991e84a26..230b30b42 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -6,7 +6,7 @@ import ( "fmt" "io" - "github.com/ipfs/go-bitswap" + "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" "github.com/ipld/go-ipld-prime" cidlink "github.com/ipld/go-ipld-prime/linking/cid" @@ -16,10 +16,8 @@ import ( "github.com/ipld/go-ipld-prime/traversal/selector/builder" ) -// TODO: need to support sessions - type Fetcher struct { - exchange *bitswap.Bitswap + blockGetter blockservice.BlockGetter } type FetchResult struct { @@ -29,8 +27,8 @@ type FetchResult struct { LastBlockLink ipld.Link } -func NewFetcher(exchange *bitswap.Bitswap) Fetcher { - return Fetcher{exchange: exchange} +func NewFetcher(blockGetter blockservice.BlockGetter) Fetcher { + return Fetcher{blockGetter: blockGetter} } func (f Fetcher) Block(ctx context.Context, c cid.Cid) (ipld.Node, error) { @@ -125,7 +123,7 @@ func (f Fetcher) loader(ctx context.Context) ipld.Loader { return nil, fmt.Errorf("invalid link type for loading: %v", lnk) } - blk, err := f.exchange.GetBlock(ctx, cidLink.Cid) + blk, err := f.blockGetter.GetBlock(ctx, cidLink.Cid) if err != nil { return nil, err } diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index 456531f10..6a83670c7 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -11,6 +11,7 @@ import ( testinstance "github.com/ipfs/go-bitswap/testinstance" tn "github.com/ipfs/go-bitswap/testnet" blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" delay "github.com/ipfs/go-ipfs-delay" mockrouting "github.com/ipfs/go-ipfs-routing/mock" @@ -50,7 +51,8 @@ func TestFetchIPLDPrimeNode(t *testing.T) { wantsBlock := peers[1] defer wantsBlock.Exchange.Close() - fetch := fetcher.NewFetcher(wantsBlock.Exchange) + wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) + fetch := fetcher.NewFetcher(wantsGetter) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() @@ -100,7 +102,8 @@ func TestFetchIPLDGraph(t *testing.T) { wantsBlock := peers[1] defer wantsBlock.Exchange.Close() - fetch := fetcher.NewFetcher(wantsBlock.Exchange) + wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) + fetch := fetcher.NewFetcher(wantsGetter) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() @@ -141,9 +144,9 @@ Loop: func encodeBlock(n ipld.Node) (blocks.Block, ipld.Node, ipld.Link) { lb := cidlink.LinkBuilder{cid.Prefix{ Version: 1, - Codec: 0x71, + Codec: cid.DagCBOR, MhType: 0x17, - MhLength: 4, + MhLength: 20, }} var b blocks.Block lnk, err := lb.Build(context.Background(), ipld.LinkContext{}, n, From 2181e98e7fa53ce88cdc29de2be63344e45b072f Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Mon, 22 Feb 2021 17:20:08 -0800 Subject: [PATCH 3306/3817] feat(fetcher): define sessions make a fetcher by default a session, with the singleton instance just being a config. only allow fetching methods on a session This commit was moved from ipfs/go-fetcher@62cdc719c38ebcd7240b3940444573032610e41c --- fetcher/fetcher.go | 31 +++++++++++++++++++++++-------- fetcher/fetcher_test.go | 7 ++++--- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 230b30b42..9147771a3 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -16,7 +16,15 @@ import ( "github.com/ipld/go-ipld-prime/traversal/selector/builder" ) +type FetcherConfig struct { + blockService blockservice.BlockService +} + type Fetcher struct { + // TODO: for now, passing this to instantiation of block session is enough to + // cancel on session context cancel, but we may want to use this direct reference + // more tightly in this code + ctx context.Context blockGetter blockservice.BlockGetter } @@ -27,11 +35,18 @@ type FetchResult struct { LastBlockLink ipld.Link } -func NewFetcher(blockGetter blockservice.BlockGetter) Fetcher { - return Fetcher{blockGetter: blockGetter} +func NewFetcherConfig(blockService blockservice.BlockService) FetcherConfig { + return FetcherConfig{blockService: blockService} +} + +func (fc FetcherConfig) NewSession(ctx context.Context) *Fetcher { + return &Fetcher{ + ctx: ctx, + blockGetter: blockservice.NewSession(ctx, fc.blockService), + } } -func (f Fetcher) Block(ctx context.Context, c cid.Cid) (ipld.Node, error) { +func (f *Fetcher) Block(ctx context.Context, c cid.Cid) (ipld.Node, error) { nb := basicnode.Prototype.Any.NewBuilder() err := cidlink.Link{Cid: c}.Load(ctx, ipld.LinkContext{}, nb, f.loader(ctx)) @@ -42,7 +57,7 @@ func (f Fetcher) Block(ctx context.Context, c cid.Cid) (ipld.Node, error) { return nb.Build(), nil } -func (f Fetcher) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector) (chan FetchResult, chan error) { +func (f *Fetcher) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector) (chan FetchResult, chan error) { results := make(chan FetchResult) errors := make(chan error) @@ -59,7 +74,7 @@ func (f Fetcher) NodeMatching(ctx context.Context, node ipld.Node, match selecto return results, errors } -func (f Fetcher) BlockMatching(ctx context.Context, root cid.Cid, match selector.Selector) (chan FetchResult, chan error) { +func (f *Fetcher) BlockMatching(ctx context.Context, root cid.Cid, match selector.Selector) (chan FetchResult, chan error) { results := make(chan FetchResult) errors := make(chan error) @@ -83,7 +98,7 @@ func (f Fetcher) BlockMatching(ctx context.Context, root cid.Cid, match selector return results, errors } -func (f Fetcher) BlockAll(ctx context.Context, root cid.Cid) (chan FetchResult, chan error) { +func (f *Fetcher) BlockAll(ctx context.Context, root cid.Cid) (chan FetchResult, chan error) { ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype__Any{}) allSelector, err := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( ssb.Matcher(), @@ -97,7 +112,7 @@ func (f Fetcher) BlockAll(ctx context.Context, root cid.Cid) (chan FetchResult, return f.BlockMatching(ctx, root, allSelector) } -func (f Fetcher) fetch(ctx context.Context, node ipld.Node, match selector.Selector, results chan FetchResult) error { +func (f *Fetcher) fetch(ctx context.Context, node ipld.Node, match selector.Selector, results chan FetchResult) error { return traversal.Progress{ Cfg: &traversal.Config{ LinkLoader: f.loader(ctx), @@ -116,7 +131,7 @@ func (f Fetcher) fetch(ctx context.Context, node ipld.Node, match selector.Selec }) } -func (f Fetcher) loader(ctx context.Context) ipld.Loader { +func (f *Fetcher) loader(ctx context.Context) ipld.Loader { return func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) { cidLink, ok := lnk.(cidlink.Link) if !ok { diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index 6a83670c7..52965af2d 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -52,7 +52,8 @@ func TestFetchIPLDPrimeNode(t *testing.T) { defer wantsBlock.Exchange.Close() wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) - fetch := fetcher.NewFetcher(wantsGetter) + fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + fetch := fetcherConfig.NewSession(context.Background()) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() @@ -103,8 +104,8 @@ func TestFetchIPLDGraph(t *testing.T) { defer wantsBlock.Exchange.Close() wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) - fetch := fetcher.NewFetcher(wantsGetter) - + fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + fetch := fetcherConfig.NewSession(context.Background()) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() From f19805da54b34924f85f528765d7af0943470e0c Mon Sep 17 00:00:00 2001 From: acruikshank Date: Thu, 25 Feb 2021 17:35:05 -0500 Subject: [PATCH 3307/3817] rework signatures to address types and document This commit was moved from ipfs/go-fetcher@20a76f27feaa685613a4103731b4a1d2075a0fb8 --- fetcher/.gitignore | 1 + fetcher/fetcher.go | 48 ++++++++++++++++++++++++++++++----------- fetcher/fetcher_test.go | 4 ++-- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/fetcher/.gitignore b/fetcher/.gitignore index e69de29bb..485dee64b 100644 --- a/fetcher/.gitignore +++ b/fetcher/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 9147771a3..a8afc83a2 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -7,7 +7,6 @@ import ( "io" "github.com/ipfs/go-blockservice" - "github.com/ipfs/go-cid" "github.com/ipld/go-ipld-prime" cidlink "github.com/ipld/go-ipld-prime/linking/cid" basicnode "github.com/ipld/go-ipld-prime/node/basic" @@ -21,10 +20,6 @@ type FetcherConfig struct { } type Fetcher struct { - // TODO: for now, passing this to instantiation of block session is enough to - // cancel on session context cancel, but we may want to use this direct reference - // more tightly in this code - ctx context.Context blockGetter blockservice.BlockGetter } @@ -35,21 +30,29 @@ type FetchResult struct { LastBlockLink ipld.Link } +// NewFetcherConfig creates a FetchConfig from which session may be created and nodes retrieved. func NewFetcherConfig(blockService blockservice.BlockService) FetcherConfig { return FetcherConfig{blockService: blockService} } +// NewSession creates a session from which nodes may be retrieved. +// The session ends when the provided context is canceled. func (fc FetcherConfig) NewSession(ctx context.Context) *Fetcher { return &Fetcher{ - ctx: ctx, blockGetter: blockservice.NewSession(ctx, fc.blockService), } } -func (f *Fetcher) Block(ctx context.Context, c cid.Cid) (ipld.Node, error) { - nb := basicnode.Prototype.Any.NewBuilder() +// Block fetches a schemaless node graph corresponding to single block by link. +func (f *Fetcher) Block(ctx context.Context, link ipld.Link) (ipld.Node, error) { + return f.BlockOfType(ctx, link, basicnode.Prototype.Any) +} + +// BlockOfType fetches a node graph of the provided type corresponding to single block by link. +func (f *Fetcher) BlockOfType(ctx context.Context, link ipld.Link, ptype ipld.NodePrototype) (ipld.Node, error) { + nb := ptype.NewBuilder() - err := cidlink.Link{Cid: c}.Load(ctx, ipld.LinkContext{}, nb, f.loader(ctx)) + err := link.Load(ctx, ipld.LinkContext{}, nb, f.loader(ctx)) if err != nil { return nil, err } @@ -57,6 +60,8 @@ func (f *Fetcher) Block(ctx context.Context, c cid.Cid) (ipld.Node, error) { return nb.Build(), nil } +// NodeMatching traverses a node graph starting with the provided node using the given selector and possibly crossing +// block boundaries. Each matched node is sent to the FetchResult channel. func (f *Fetcher) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector) (chan FetchResult, chan error) { results := make(chan FetchResult) errors := make(chan error) @@ -74,7 +79,16 @@ func (f *Fetcher) NodeMatching(ctx context.Context, node ipld.Node, match select return results, errors } -func (f *Fetcher) BlockMatching(ctx context.Context, root cid.Cid, match selector.Selector) (chan FetchResult, chan error) { +// BlockMatching traverses a schemaless node graph starting with the given link using the given selector and possibly crossing +// block boundaries. Each matched node is sent to the FetchResult channel. +func (f *Fetcher) BlockMatching(ctx context.Context, root ipld.Link, match selector.Selector) (chan FetchResult, chan error) { + return f.BlockMatchingOfType(ctx, root, match, basicnode.Prototype.Any) +} + +// BlockMatchingOfType traverses a node graph starting with the given link using the given selector and possibly +// crossing block boundaries. The nodes will be typed using the provided prototype. Each matched node is sent to +// the FetchResult channel. +func (f *Fetcher) BlockMatchingOfType(ctx context.Context, root ipld.Link, match selector.Selector, ptype ipld.NodePrototype) (chan FetchResult, chan error) { results := make(chan FetchResult) errors := make(chan error) @@ -82,7 +96,7 @@ func (f *Fetcher) BlockMatching(ctx context.Context, root cid.Cid, match selecto defer close(results) // retrieve first node - node, err := f.Block(ctx, root) + node, err := f.BlockOfType(ctx, root, ptype) if err != nil { errors <- err return @@ -98,7 +112,15 @@ func (f *Fetcher) BlockMatching(ctx context.Context, root cid.Cid, match selecto return results, errors } -func (f *Fetcher) BlockAll(ctx context.Context, root cid.Cid) (chan FetchResult, chan error) { +// BlockAll traverses all nodes in the graph linked by root. The nodes will be untyped and send over the results +// channel. +func (f *Fetcher) BlockAll(ctx context.Context, root ipld.Link) (chan FetchResult, chan error) { + return f.BlockAllOfType(ctx, root, basicnode.Prototype.Any) +} + +// BlockAllOfType traverses all nodes in the graph linked by root. The nodes will typed according to ptype +// and send over the results channel. +func (f *Fetcher) BlockAllOfType(ctx context.Context, root ipld.Link, ptype ipld.NodePrototype) (chan FetchResult, chan error) { ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype__Any{}) allSelector, err := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( ssb.Matcher(), @@ -109,7 +131,7 @@ func (f *Fetcher) BlockAll(ctx context.Context, root cid.Cid) (chan FetchResult, errors <- err return nil, errors } - return f.BlockMatching(ctx, root, allSelector) + return f.BlockMatchingOfType(ctx, root, allSelector, ptype) } func (f *Fetcher) fetch(ctx context.Context, node ipld.Node, match selector.Selector, results chan FetchResult) error { diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index 52965af2d..e1f75f966 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -58,7 +58,7 @@ func TestFetchIPLDPrimeNode(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - retrievedNode, err := fetch.Block(ctx, block.Cid()) + retrievedNode, err := fetch.Block(ctx, cidlink.Link{Cid: block.Cid()}) require.NoError(t, err) assert.Equal(t, node, retrievedNode) } @@ -109,7 +109,7 @@ func TestFetchIPLDGraph(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - nodeCh, errCh := fetch.BlockAll(ctx, block1.Cid()) + nodeCh, errCh := fetch.BlockAll(ctx, cidlink.Link{Cid: block1.Cid()}) require.NoError(t, err) order := 0 From ea1834b2c605037858662f0d4de58fb112217744 Mon Sep 17 00:00:00 2001 From: acruikshank Date: Thu, 25 Feb 2021 17:44:54 -0500 Subject: [PATCH 3308/3817] de-emptify the README This commit was moved from ipfs/go-fetcher@d18f1639fc1a60d9a4334362e7dfe2c0243467da --- fetcher/README.md | 15 +++++++++++++++ fetcher/README.md.go | 1 - 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 fetcher/README.md delete mode 100644 fetcher/README.md.go diff --git a/fetcher/README.md b/fetcher/README.md new file mode 100644 index 000000000..bc1410afc --- /dev/null +++ b/fetcher/README.md @@ -0,0 +1,15 @@ +go-fetcher +================== + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) + +Go-fetcher is a library to retrieve IPLD prime nodes from IPFS using Bitswap. + +## Contribute + +PRs are welcome! + +## License + +MIT \ No newline at end of file diff --git a/fetcher/README.md.go b/fetcher/README.md.go deleted file mode 100644 index b306ea498..000000000 --- a/fetcher/README.md.go +++ /dev/null @@ -1 +0,0 @@ -package fetcher From 955784e155713db0d83b2cf0de5f4bf6ce355711 Mon Sep 17 00:00:00 2001 From: acruikshank Date: Thu, 25 Feb 2021 17:55:24 -0500 Subject: [PATCH 3309/3817] support protobuf and raw nodes from fetcher This commit was moved from ipfs/go-fetcher@b89fa245d72bda38156ce6a600fc0c3f32dc72c2 --- fetcher/fetcher.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index a8afc83a2..5145754cb 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -6,6 +6,8 @@ import ( "fmt" "io" + dagpb "github.com/ipld/go-ipld-prime-proto" + "github.com/ipfs/go-blockservice" "github.com/ipld/go-ipld-prime" cidlink "github.com/ipld/go-ipld-prime/linking/cid" @@ -138,9 +140,9 @@ func (f *Fetcher) fetch(ctx context.Context, node ipld.Node, match selector.Sele return traversal.Progress{ Cfg: &traversal.Config{ LinkLoader: f.loader(ctx), - LinkTargetNodePrototypeChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodePrototype, error) { + LinkTargetNodePrototypeChooser: dagpb.AddDagPBSupportToChooser(func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodePrototype, error) { return basicnode.Prototype__Any{}, nil - }, + }), }, }.WalkMatching(node, match, func(prog traversal.Progress, n ipld.Node) error { results <- FetchResult{ From b63141f65929a27d5bf90d83c5b6cd28efb02db5 Mon Sep 17 00:00:00 2001 From: acruikshank Date: Thu, 25 Feb 2021 18:02:17 -0500 Subject: [PATCH 3310/3817] better chooser This commit was moved from ipfs/go-fetcher@a5568f5b87da6e8e4bf28f8175e2e7c1dfb2c4b8 --- fetcher/fetcher.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 5145754cb..5715297b6 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -6,6 +6,8 @@ import ( "fmt" "io" + "github.com/ipld/go-ipld-prime/schema" + dagpb "github.com/ipld/go-ipld-prime-proto" "github.com/ipfs/go-blockservice" @@ -140,8 +142,11 @@ func (f *Fetcher) fetch(ctx context.Context, node ipld.Node, match selector.Sele return traversal.Progress{ Cfg: &traversal.Config{ LinkLoader: f.loader(ctx), - LinkTargetNodePrototypeChooser: dagpb.AddDagPBSupportToChooser(func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodePrototype, error) { - return basicnode.Prototype__Any{}, nil + LinkTargetNodePrototypeChooser: dagpb.AddDagPBSupportToChooser(func(_ ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { + if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { + return tlnkNd.LinkTargetNodePrototype(), nil + } + return basicnode.Prototype.Any, nil }), }, }.WalkMatching(node, match, func(prog traversal.Progress, n ipld.Node) error { From d0a4c9d42d9b8e3e824fdcdbd680bcb8dfb7b6c0 Mon Sep 17 00:00:00 2001 From: acruikshank Date: Fri, 26 Feb 2021 11:23:20 -0500 Subject: [PATCH 3311/3817] assign prototypes based on Cid prefix whenn otherwise untyped This commit was moved from ipfs/go-fetcher@5bbfe2a464938759ba9435f1f740c10c3326450c --- fetcher/fetcher.go | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 5715297b6..36f253db7 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -49,7 +49,11 @@ func (fc FetcherConfig) NewSession(ctx context.Context) *Fetcher { // Block fetches a schemaless node graph corresponding to single block by link. func (f *Fetcher) Block(ctx context.Context, link ipld.Link) (ipld.Node, error) { - return f.BlockOfType(ctx, link, basicnode.Prototype.Any) + prototype, err := prototypeFromLink(link) + if err != nil { + return nil, err + } + return f.BlockOfType(ctx, link, prototype) } // BlockOfType fetches a node graph of the provided type corresponding to single block by link. @@ -86,7 +90,13 @@ func (f *Fetcher) NodeMatching(ctx context.Context, node ipld.Node, match select // BlockMatching traverses a schemaless node graph starting with the given link using the given selector and possibly crossing // block boundaries. Each matched node is sent to the FetchResult channel. func (f *Fetcher) BlockMatching(ctx context.Context, root ipld.Link, match selector.Selector) (chan FetchResult, chan error) { - return f.BlockMatchingOfType(ctx, root, match, basicnode.Prototype.Any) + prototype, err := prototypeFromLink(root) + if err != nil { + errors := make(chan error, 1) + errors <- err + return nil, errors + } + return f.BlockMatchingOfType(ctx, root, match, prototype) } // BlockMatchingOfType traverses a node graph starting with the given link using the given selector and possibly @@ -119,7 +129,13 @@ func (f *Fetcher) BlockMatchingOfType(ctx context.Context, root ipld.Link, match // BlockAll traverses all nodes in the graph linked by root. The nodes will be untyped and send over the results // channel. func (f *Fetcher) BlockAll(ctx context.Context, root ipld.Link) (chan FetchResult, chan error) { - return f.BlockAllOfType(ctx, root, basicnode.Prototype.Any) + prototype, err := prototypeFromLink(root) + if err != nil { + errors := make(chan error, 1) + errors <- err + return nil, errors + } + return f.BlockAllOfType(ctx, root, prototype) } // BlockAllOfType traverses all nodes in the graph linked by root. The nodes will typed according to ptype @@ -175,3 +191,9 @@ func (f *Fetcher) loader(ctx context.Context) ipld.Loader { return bytes.NewReader(blk.RawData()), nil } } + +func prototypeFromLink(lnk ipld.Link) (ipld.NodePrototype, error) { + return dagpb.AddDagPBSupportToChooser(func(_ ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { + return basicnode.Prototype__Any{}, nil + })(lnk, ipld.LinkContext{}) +} From 0f0dc9b1716926166b3252cff269beffd49e4092 Mon Sep 17 00:00:00 2001 From: acruikshank Date: Fri, 26 Feb 2021 11:27:13 -0500 Subject: [PATCH 3312/3817] declare returned chans readonly This commit was moved from ipfs/go-fetcher@ad44b88fc50918fd4d308fa8db9a05968a479910 --- fetcher/fetcher.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 36f253db7..00ab2291e 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -70,7 +70,7 @@ func (f *Fetcher) BlockOfType(ctx context.Context, link ipld.Link, ptype ipld.No // NodeMatching traverses a node graph starting with the provided node using the given selector and possibly crossing // block boundaries. Each matched node is sent to the FetchResult channel. -func (f *Fetcher) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector) (chan FetchResult, chan error) { +func (f *Fetcher) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector) (<-chan FetchResult, <-chan error) { results := make(chan FetchResult) errors := make(chan error) @@ -89,7 +89,7 @@ func (f *Fetcher) NodeMatching(ctx context.Context, node ipld.Node, match select // BlockMatching traverses a schemaless node graph starting with the given link using the given selector and possibly crossing // block boundaries. Each matched node is sent to the FetchResult channel. -func (f *Fetcher) BlockMatching(ctx context.Context, root ipld.Link, match selector.Selector) (chan FetchResult, chan error) { +func (f *Fetcher) BlockMatching(ctx context.Context, root ipld.Link, match selector.Selector) (<-chan FetchResult, <-chan error) { prototype, err := prototypeFromLink(root) if err != nil { errors := make(chan error, 1) @@ -102,7 +102,7 @@ func (f *Fetcher) BlockMatching(ctx context.Context, root ipld.Link, match selec // BlockMatchingOfType traverses a node graph starting with the given link using the given selector and possibly // crossing block boundaries. The nodes will be typed using the provided prototype. Each matched node is sent to // the FetchResult channel. -func (f *Fetcher) BlockMatchingOfType(ctx context.Context, root ipld.Link, match selector.Selector, ptype ipld.NodePrototype) (chan FetchResult, chan error) { +func (f *Fetcher) BlockMatchingOfType(ctx context.Context, root ipld.Link, match selector.Selector, ptype ipld.NodePrototype) (<-chan FetchResult, <-chan error) { results := make(chan FetchResult) errors := make(chan error) @@ -128,7 +128,7 @@ func (f *Fetcher) BlockMatchingOfType(ctx context.Context, root ipld.Link, match // BlockAll traverses all nodes in the graph linked by root. The nodes will be untyped and send over the results // channel. -func (f *Fetcher) BlockAll(ctx context.Context, root ipld.Link) (chan FetchResult, chan error) { +func (f *Fetcher) BlockAll(ctx context.Context, root ipld.Link) (<-chan FetchResult, <-chan error) { prototype, err := prototypeFromLink(root) if err != nil { errors := make(chan error, 1) @@ -140,7 +140,7 @@ func (f *Fetcher) BlockAll(ctx context.Context, root ipld.Link) (chan FetchResul // BlockAllOfType traverses all nodes in the graph linked by root. The nodes will typed according to ptype // and send over the results channel. -func (f *Fetcher) BlockAllOfType(ctx context.Context, root ipld.Link, ptype ipld.NodePrototype) (chan FetchResult, chan error) { +func (f *Fetcher) BlockAllOfType(ctx context.Context, root ipld.Link, ptype ipld.NodePrototype) (<-chan FetchResult, <-chan error) { ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype__Any{}) allSelector, err := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( ssb.Matcher(), From d80f2f337777d9311b7091be8c46b1e2030fe9c4 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Mon, 1 Mar 2021 15:22:05 -0800 Subject: [PATCH 3313/3817] feat(fetcher): correct module name to match URL This commit was moved from ipfs/go-fetcher@eec0fbe50e4460cd31068d3b39ee6844cfd006a3 --- fetcher/fetcher_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index 52965af2d..d755fff37 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -23,7 +23,7 @@ import ( "github.com/magiconair/properties/assert" "github.com/stretchr/testify/require" - "github.com/ipfs/fetcher" + "github.com/ipfs/go-fetcher" ) var _ cidlink.MulticodecDecoder = dagcbor.Decoder From 1ed85bc6dcd3a131c112b96e92abb5bececa67e3 Mon Sep 17 00:00:00 2001 From: acruikshank Date: Tue, 2 Mar 2021 12:56:38 -0500 Subject: [PATCH 3314/3817] replace go-merkledag with go-fetcher This commit was moved from ipfs/go-ipfs-provider@fb15f2eef31d8dabde1c36bc885180a1b722a1b7 --- provider/simple/reprovide.go | 28 ++++++++++++++++++---------- provider/simple/reprovide_test.go | 10 +++++++--- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index bfe6173e1..739ccd95b 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -4,15 +4,15 @@ import ( "context" "errors" "fmt" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" "time" "github.com/cenkalti/backoff" "github.com/ipfs/go-cid" "github.com/ipfs/go-cidutil" + "github.com/ipfs/go-fetcher" blocks "github.com/ipfs/go-ipfs-blockstore" - ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" - "github.com/ipfs/go-merkledag" "github.com/ipfs/go-verifcid" "github.com/libp2p/go-libp2p-core/routing" ) @@ -184,9 +184,9 @@ type Pinner interface { } // NewPinnedProvider returns provider supplying pinned keys -func NewPinnedProvider(onlyRoots bool, pinning Pinner, dag ipld.DAGService) KeyChanFunc { +func NewPinnedProvider(onlyRoots bool, pinning Pinner, fetchConfig fetcher.FetcherConfig) KeyChanFunc { return func(ctx context.Context) (<-chan cid.Cid, error) { - set, err := pinSet(ctx, pinning, dag, onlyRoots) + set, err := pinSet(ctx, pinning, fetchConfig, onlyRoots) if err != nil { return nil, err } @@ -208,7 +208,7 @@ func NewPinnedProvider(onlyRoots bool, pinning Pinner, dag ipld.DAGService) KeyC } } -func pinSet(ctx context.Context, pinning Pinner, dag ipld.DAGService, onlyRoots bool) (*cidutil.StreamingSet, error) { +func pinSet(ctx context.Context, pinning Pinner, fetchConfig fetcher.FetcherConfig, onlyRoots bool) (*cidutil.StreamingSet, error) { set := cidutil.NewStreamingSet() go func() { @@ -230,12 +230,20 @@ func pinSet(ctx context.Context, pinning Pinner, dag ipld.DAGService, onlyRoots logR.Errorf("reprovide indirect pins: %s", err) return } + + session := fetchConfig.NewSession(ctx) for _, key := range rkeys { - if onlyRoots { - set.Visitor(ctx)(key) - } else { - err := merkledag.Walk(ctx, merkledag.GetLinksWithDAG(dag), key, set.Visitor(ctx)) - if err != nil { + set.Visitor(ctx)(key) + if !onlyRoots { + nodeCh, errCh := fetcher.BlockAll(ctx, session, cidlink.Link{key}) + for res := range nodeCh { + clink, ok := res.LastBlockLink.(cidlink.Link) + if ok { + set.Visitor(ctx)(clink.Cid) + } + } + + if err := <-errCh; err != nil { logR.Errorf("reprovide indirect pins: %s", err) return } diff --git a/provider/simple/reprovide_test.go b/provider/simple/reprovide_test.go index 3858baf5e..913ca072c 100644 --- a/provider/simple/reprovide_test.go +++ b/provider/simple/reprovide_test.go @@ -9,11 +9,13 @@ import ( "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" + "github.com/ipfs/go-fetcher" "github.com/ipfs/go-ipfs-blockstore" offline "github.com/ipfs/go-ipfs-exchange-offline" mock "github.com/ipfs/go-ipfs-routing/mock" cbor "github.com/ipfs/go-ipld-cbor" - merkledag "github.com/ipfs/go-merkledag" + "github.com/ipld/go-ipld-prime/codec/dagcbor" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" peer "github.com/libp2p/go-libp2p-core/peer" testutil "github.com/libp2p/go-libp2p-testing/net" mh "github.com/multiformats/go-multihash" @@ -21,6 +23,8 @@ import ( . "github.com/ipfs/go-ipfs-provider/simple" ) +var _ cidlink.MulticodecDecoder = dagcbor.Decoder + func setupRouting(t *testing.T) (clA, clB mock.Client, idA, idB peer.ID) { mrserv := mock.NewServer() @@ -195,7 +199,7 @@ func TestReprovidePinned(t *testing.T) { nodes, bstore := setupDag(t) - dag := merkledag.NewDAGService(bsrv.New(bstore, offline.Exchange(bstore))) + fetchConfig := fetcher.NewFetcherConfig(bsrv.New(bstore, offline.Exchange(bstore))) for i := 0; i < 2; i++ { clA, clB, idA, _ := setupRouting(t) @@ -215,7 +219,7 @@ func TestReprovidePinned(t *testing.T) { keyProvider := NewPinnedProvider(onlyRoots, &mockPinner{ recursive: []cid.Cid{nodes[1]}, direct: []cid.Cid{nodes[3]}, - }, dag) + }, fetchConfig) reprov := NewReprovider(ctx, time.Hour, clA, keyProvider) err := reprov.Reprovide() From 15561c1ec81ad05d15d67a0696686ccdad1b9fa8 Mon Sep 17 00:00:00 2001 From: acruikshank Date: Mon, 1 Mar 2021 11:47:14 -0500 Subject: [PATCH 3315/3817] split into common interface and helper methods This commit was moved from ipfs/go-fetcher@3ccc95e8d72c70af7c3cc808377ec5417d47ae70 --- fetcher/fetcher.go | 85 +++++++++++++++------------- fetcher/fetcher_test.go | 121 ++++++++++++++++++++++++++++++++++------ 2 files changed, 151 insertions(+), 55 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 00ab2291e..e1fd9e87b 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -23,7 +23,21 @@ type FetcherConfig struct { blockService blockservice.BlockService } -type Fetcher struct { +type Fetcher interface { + // NodeMatching traverses a node graph starting with the provided node using the given selector and possibly crossing + // block boundaries. Each matched node is sent to the FetchResult channel. + NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector) (<-chan FetchResult, <-chan error) + + // BlockOfType fetches a node graph of the provided type corresponding to single block by link. + BlockOfType(ctx context.Context, link ipld.Link, ptype ipld.NodePrototype) (ipld.Node, error) + + // BlockMatchingOfType traverses a node graph starting with the given link using the given selector and possibly + // crossing block boundaries. The nodes will be typed using the provided prototype. Each matched node is sent to + // the FetchResult channel. + BlockMatchingOfType(ctx context.Context, root ipld.Link, match selector.Selector, ptype ipld.NodePrototype) (<-chan FetchResult, <-chan error) +} + +type fetcherSession struct { blockGetter blockservice.BlockGetter } @@ -41,23 +55,14 @@ func NewFetcherConfig(blockService blockservice.BlockService) FetcherConfig { // NewSession creates a session from which nodes may be retrieved. // The session ends when the provided context is canceled. -func (fc FetcherConfig) NewSession(ctx context.Context) *Fetcher { - return &Fetcher{ +func (fc FetcherConfig) NewSession(ctx context.Context) Fetcher { + return &fetcherSession{ blockGetter: blockservice.NewSession(ctx, fc.blockService), } } -// Block fetches a schemaless node graph corresponding to single block by link. -func (f *Fetcher) Block(ctx context.Context, link ipld.Link) (ipld.Node, error) { - prototype, err := prototypeFromLink(link) - if err != nil { - return nil, err - } - return f.BlockOfType(ctx, link, prototype) -} - // BlockOfType fetches a node graph of the provided type corresponding to single block by link. -func (f *Fetcher) BlockOfType(ctx context.Context, link ipld.Link, ptype ipld.NodePrototype) (ipld.Node, error) { +func (f *fetcherSession) BlockOfType(ctx context.Context, link ipld.Link, ptype ipld.NodePrototype) (ipld.Node, error) { nb := ptype.NewBuilder() err := link.Load(ctx, ipld.LinkContext{}, nb, f.loader(ctx)) @@ -68,9 +73,7 @@ func (f *Fetcher) BlockOfType(ctx context.Context, link ipld.Link, ptype ipld.No return nb.Build(), nil } -// NodeMatching traverses a node graph starting with the provided node using the given selector and possibly crossing -// block boundaries. Each matched node is sent to the FetchResult channel. -func (f *Fetcher) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector) (<-chan FetchResult, <-chan error) { +func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector) (<-chan FetchResult, <-chan error) { results := make(chan FetchResult) errors := make(chan error) @@ -87,22 +90,7 @@ func (f *Fetcher) NodeMatching(ctx context.Context, node ipld.Node, match select return results, errors } -// BlockMatching traverses a schemaless node graph starting with the given link using the given selector and possibly crossing -// block boundaries. Each matched node is sent to the FetchResult channel. -func (f *Fetcher) BlockMatching(ctx context.Context, root ipld.Link, match selector.Selector) (<-chan FetchResult, <-chan error) { - prototype, err := prototypeFromLink(root) - if err != nil { - errors := make(chan error, 1) - errors <- err - return nil, errors - } - return f.BlockMatchingOfType(ctx, root, match, prototype) -} - -// BlockMatchingOfType traverses a node graph starting with the given link using the given selector and possibly -// crossing block boundaries. The nodes will be typed using the provided prototype. Each matched node is sent to -// the FetchResult channel. -func (f *Fetcher) BlockMatchingOfType(ctx context.Context, root ipld.Link, match selector.Selector, ptype ipld.NodePrototype) (<-chan FetchResult, <-chan error) { +func (f *fetcherSession) BlockMatchingOfType(ctx context.Context, root ipld.Link, match selector.Selector, ptype ipld.NodePrototype) (<-chan FetchResult, <-chan error) { results := make(chan FetchResult) errors := make(chan error) @@ -126,22 +114,43 @@ func (f *Fetcher) BlockMatchingOfType(ctx context.Context, root ipld.Link, match return results, errors } +// Block fetches a schemaless node graph corresponding to single block by link. +func Block(ctx context.Context, f Fetcher, link ipld.Link) (ipld.Node, error) { + prototype, err := prototypeFromLink(link) + if err != nil { + return nil, err + } + return f.BlockOfType(ctx, link, prototype) +} + +// BlockMatching traverses a schemaless node graph starting with the given link using the given selector and possibly crossing +// block boundaries. Each matched node is sent to the FetchResult channel. +func BlockMatching(ctx context.Context, f Fetcher, root ipld.Link, match selector.Selector) (<-chan FetchResult, <-chan error) { + prototype, err := prototypeFromLink(root) + if err != nil { + errors := make(chan error, 1) + errors <- err + return nil, errors + } + return f.BlockMatchingOfType(ctx, root, match, prototype) +} + // BlockAll traverses all nodes in the graph linked by root. The nodes will be untyped and send over the results // channel. -func (f *Fetcher) BlockAll(ctx context.Context, root ipld.Link) (<-chan FetchResult, <-chan error) { +func BlockAll(ctx context.Context, f Fetcher, root ipld.Link) (<-chan FetchResult, <-chan error) { prototype, err := prototypeFromLink(root) if err != nil { errors := make(chan error, 1) errors <- err return nil, errors } - return f.BlockAllOfType(ctx, root, prototype) + return BlockAllOfType(ctx, f, root, prototype) } // BlockAllOfType traverses all nodes in the graph linked by root. The nodes will typed according to ptype // and send over the results channel. -func (f *Fetcher) BlockAllOfType(ctx context.Context, root ipld.Link, ptype ipld.NodePrototype) (<-chan FetchResult, <-chan error) { - ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype__Any{}) +func BlockAllOfType(ctx context.Context, f Fetcher, root ipld.Link, ptype ipld.NodePrototype) (<-chan FetchResult, <-chan error) { + ssb := builder.NewSelectorSpecBuilder(ptype) allSelector, err := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( ssb.Matcher(), ssb.ExploreAll(ssb.ExploreRecursiveEdge()), @@ -154,7 +163,7 @@ func (f *Fetcher) BlockAllOfType(ctx context.Context, root ipld.Link, ptype ipld return f.BlockMatchingOfType(ctx, root, allSelector, ptype) } -func (f *Fetcher) fetch(ctx context.Context, node ipld.Node, match selector.Selector, results chan FetchResult) error { +func (f *fetcherSession) fetch(ctx context.Context, node ipld.Node, match selector.Selector, results chan FetchResult) error { return traversal.Progress{ Cfg: &traversal.Config{ LinkLoader: f.loader(ctx), @@ -176,7 +185,7 @@ func (f *Fetcher) fetch(ctx context.Context, node ipld.Node, match selector.Sele }) } -func (f *Fetcher) loader(ctx context.Context) ipld.Loader { +func (f *fetcherSession) loader(ctx context.Context) ipld.Loader { return func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) { cidLink, ok := lnk.(cidlink.Link) if !ok { diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index e10aab8d2..aa40fe1bb 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -8,6 +8,9 @@ import ( "testing" "time" + "github.com/ipld/go-ipld-prime/traversal/selector" + "github.com/ipld/go-ipld-prime/traversal/selector/builder" + testinstance "github.com/ipfs/go-bitswap/testinstance" tn "github.com/ipfs/go-bitswap/testnet" blocks "github.com/ipfs/go-block-format" @@ -53,12 +56,12 @@ func TestFetchIPLDPrimeNode(t *testing.T) { wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) - fetch := fetcherConfig.NewSession(context.Background()) + session := fetcherConfig.NewSession(context.Background()) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - retrievedNode, err := fetch.Block(ctx, cidlink.Link{Cid: block.Cid()}) + retrievedNode, err := fetcher.Block(ctx, session, cidlink.Link{Cid: block.Cid()}) require.NoError(t, err) assert.Equal(t, node, retrievedNode) } @@ -105,15 +108,106 @@ func TestFetchIPLDGraph(t *testing.T) { wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) - fetch := fetcherConfig.NewSession(context.Background()) + session := fetcherConfig.NewSession(context.Background()) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - nodeCh, errCh := fetch.BlockAll(ctx, cidlink.Link{Cid: block1.Cid()}) + nodeCh, errCh := fetcher.BlockAll(ctx, session, cidlink.Link{Cid: block1.Cid()}) require.NoError(t, err) - order := 0 + assertNodesInOrder(t, nodeCh, errCh, 10, map[int]ipld.Node{0: node1, 4: node2, 5: node3, 7: node4}) +} + +func TestHelpers(t *testing.T) { + block3, node3, link3 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleEntry("three").AssignBool(true) + })) + block4, node4, link4 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleEntry("four").AssignBool(true) + })) + block2, node2, link2 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { + na.AssembleEntry("link3").AssignLink(link3) + na.AssembleEntry("link4").AssignLink(link4) + })) + block1, node1, _ := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { + na.AssembleEntry("foo").AssignBool(true) + na.AssembleEntry("bar").AssignBool(false) + na.AssembleEntry("nested").CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry("link2").AssignLink(link2) + na.AssembleEntry("nonlink").AssignString("zoo") + }) + })) + + net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(0*time.Millisecond)) + ig := testinstance.NewTestInstanceGenerator(net, nil, nil) + defer ig.Close() + + peers := ig.Instances(2) + hasBlock := peers[0] + defer hasBlock.Exchange.Close() + + err := hasBlock.Exchange.HasBlock(block1) + require.NoError(t, err) + err = hasBlock.Exchange.HasBlock(block2) + require.NoError(t, err) + err = hasBlock.Exchange.HasBlock(block3) + require.NoError(t, err) + err = hasBlock.Exchange.HasBlock(block4) + require.NoError(t, err) + + wantsBlock := peers[1] + defer wantsBlock.Exchange.Close() + + wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) + t.Run("Block retrieves node", func(t *testing.T) { + fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + session := fetcherConfig.NewSession(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + node, err := fetcher.Block(ctx, session, cidlink.Link{Cid: block1.Cid()}) + require.NoError(t, err) + + assert.Equal(t, node, node1) + }) + + t.Run("BlockMatching retrieves nodes matching selector", func(t *testing.T) { + // limit recursion depth to 2 nodes and expect to get only 2 blocks (4 nodes) + ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype__Any{}) + sel, err := ssb.ExploreRecursive(selector.RecursionLimitDepth(2), ssb.ExploreUnion( + ssb.Matcher(), + ssb.ExploreAll(ssb.ExploreRecursiveEdge()), + )).Selector() + require.NoError(t, err) + + fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + session := fetcherConfig.NewSession(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + nodeCh, errCh := fetcher.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel) + require.NoError(t, err) + + assertNodesInOrder(t, nodeCh, errCh, 4, map[int]ipld.Node{0: node1, 4: node2}) + }) + + t.Run("BlockAllOfType retrieves all nodes with a schema", func(t *testing.T) { + // limit recursion depth to 2 nodes and expect to get only 2 blocks (4 nodes) + fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + session := fetcherConfig.NewSession(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + nodeCh, errCh := fetcher.BlockAllOfType(ctx, session, cidlink.Link{Cid: block1.Cid()}, basicnode.Prototype__Any{}) + require.NoError(t, err) + + assertNodesInOrder(t, nodeCh, errCh, 10, map[int]ipld.Node{0: node1, 4: node2, 5: node3, 7: node4}) + }) +} + +func assertNodesInOrder(t *testing.T, nodeCh <-chan fetcher.FetchResult, errCh <-chan error, nodeCount int, nodes map[int]ipld.Node) { + order := 0 Loop: for { select { @@ -122,24 +216,17 @@ Loop: break Loop } - switch order { - case 0: - assert.Equal(t, node1, res.Node) - case 4: - assert.Equal(t, node2, res.Node) - case 5: - assert.Equal(t, node3, res.Node) - case 7: - assert.Equal(t, node4, res.Node) + expectedNode, ok := nodes[order] + if ok { + assert.Equal(t, expectedNode, res.Node) } + order++ case err := <-errCh: require.FailNow(t, err.Error()) } } - - // expect 10 nodes altogether including sub nodes - assert.Equal(t, 10, order) + assert.Equal(t, nodeCount, order) } func encodeBlock(n ipld.Node) (blocks.Block, ipld.Node, ipld.Link) { From d9b012ac641221bab6c9484c32ca13709a0451fe Mon Sep 17 00:00:00 2001 From: acruikshank Date: Tue, 2 Mar 2021 11:16:24 -0500 Subject: [PATCH 3316/3817] align license with go-ipfs This commit was moved from ipfs/go-fetcher@4ff0dac033d5bd193ae3c71e646d22fb14c7eab1 --- fetcher/LICENSE-APACHE | 5 +++++ fetcher/{LICENSE => LICENSE-MIT} | 0 fetcher/README.md | 5 ++++- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 fetcher/LICENSE-APACHE rename fetcher/{LICENSE => LICENSE-MIT} (100%) diff --git a/fetcher/LICENSE-APACHE b/fetcher/LICENSE-APACHE new file mode 100644 index 000000000..4c83a2841 --- /dev/null +++ b/fetcher/LICENSE-APACHE @@ -0,0 +1,5 @@ +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. \ No newline at end of file diff --git a/fetcher/LICENSE b/fetcher/LICENSE-MIT similarity index 100% rename from fetcher/LICENSE rename to fetcher/LICENSE-MIT diff --git a/fetcher/README.md b/fetcher/README.md index bc1410afc..7039f39b3 100644 --- a/fetcher/README.md +++ b/fetcher/README.md @@ -12,4 +12,7 @@ PRs are welcome! ## License -MIT \ No newline at end of file +The go-fetcher project is dual-licensed under Apache 2.0 and MIT terms: + +- Apache License, Version 2.0, ([LICENSE-APACHE](https://github.com/ipfs/go-fetcher/blob/master/LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](https://github.com/ipfs/go-fetcher/blob/master/LICENSE-MIT) or http://opensource.org/licenses/MIT) \ No newline at end of file From 79f9484e327c84582f107169213f4cac741977c2 Mon Sep 17 00:00:00 2001 From: acruikshank Date: Tue, 2 Mar 2021 12:29:00 -0500 Subject: [PATCH 3317/3817] better channel behavior and documentation This commit was moved from ipfs/go-fetcher@7ec6d7375e7c624396d59e1836faafd095fa7c3b --- fetcher/fetcher.go | 10 ++++++++-- fetcher/fetcher_test.go | 24 ++++++++---------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index e1fd9e87b..99b06e615 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -26,6 +26,8 @@ type FetcherConfig struct { type Fetcher interface { // NodeMatching traverses a node graph starting with the provided node using the given selector and possibly crossing // block boundaries. Each matched node is sent to the FetchResult channel. + // The results and error channels will be closed on query completion or error. The error channel is buffered, + // will emit at most one error and must be checked after processing the results. NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector) (<-chan FetchResult, <-chan error) // BlockOfType fetches a node graph of the provided type corresponding to single block by link. @@ -34,6 +36,8 @@ type Fetcher interface { // BlockMatchingOfType traverses a node graph starting with the given link using the given selector and possibly // crossing block boundaries. The nodes will be typed using the provided prototype. Each matched node is sent to // the FetchResult channel. + // The results and error channels will be closed on query completion or error. The error channel is buffered, + // will emit at most one error and must be checked after processing the results. BlockMatchingOfType(ctx context.Context, root ipld.Link, match selector.Selector, ptype ipld.NodePrototype) (<-chan FetchResult, <-chan error) } @@ -75,10 +79,11 @@ func (f *fetcherSession) BlockOfType(ctx context.Context, link ipld.Link, ptype func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector) (<-chan FetchResult, <-chan error) { results := make(chan FetchResult) - errors := make(chan error) + errors := make(chan error, 1) go func() { defer close(results) + defer close(errors) err := f.fetch(ctx, node, match, results) if err != nil { @@ -92,10 +97,11 @@ func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match func (f *fetcherSession) BlockMatchingOfType(ctx context.Context, root ipld.Link, match selector.Selector, ptype ipld.NodePrototype) (<-chan FetchResult, <-chan error) { results := make(chan FetchResult) - errors := make(chan error) + errors := make(chan error, 1) go func() { defer close(results) + defer close(errors) // retrieve first node node, err := f.BlockOfType(ctx, root, ptype) diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index aa40fe1bb..e7905fd64 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -208,24 +208,16 @@ func TestHelpers(t *testing.T) { func assertNodesInOrder(t *testing.T, nodeCh <-chan fetcher.FetchResult, errCh <-chan error, nodeCount int, nodes map[int]ipld.Node) { order := 0 -Loop: - for { - select { - case res, ok := <-nodeCh: - if !ok { - break Loop - } - - expectedNode, ok := nodes[order] - if ok { - assert.Equal(t, expectedNode, res.Node) - } - - order++ - case err := <-errCh: - require.FailNow(t, err.Error()) + for res := range nodeCh { + expectedNode, ok := nodes[order] + if ok { + assert.Equal(t, expectedNode, res.Node) } + order++ } + + err := <-errCh + require.NoError(t, err) assert.Equal(t, nodeCount, order) } From 425f0a13b05e253053664d3fb76c418479ab1a11 Mon Sep 17 00:00:00 2001 From: acruikshank Date: Tue, 2 Mar 2021 16:07:45 -0500 Subject: [PATCH 3318/3817] update go-fetcher version and fix import This commit was moved from ipfs/go-ipfs-provider@702a3da7867dc3bbc6c5f2ddaec376d8008eb956 --- provider/simple/reprovide.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index 739ccd95b..c52b6ff7e 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - cidlink "github.com/ipld/go-ipld-prime/linking/cid" "time" "github.com/cenkalti/backoff" @@ -14,6 +13,7 @@ import ( blocks "github.com/ipfs/go-ipfs-blockstore" logging "github.com/ipfs/go-log" "github.com/ipfs/go-verifcid" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" "github.com/libp2p/go-libp2p-core/routing" ) From f3094b9a3401eb8cd4bab9517e5498d320bf960a Mon Sep 17 00:00:00 2001 From: acruikshank Date: Thu, 4 Mar 2021 09:58:02 -0500 Subject: [PATCH 3319/3817] add test with more complicated selector This commit was moved from ipfs/go-fetcher@bc04b63c1e03cece0e3191db2ffae5dc2f83ea00 --- fetcher/fetcher_test.go | 66 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index e7905fd64..a40c9106a 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "io" + "strings" "testing" "time" @@ -23,7 +24,7 @@ import ( "github.com/ipld/go-ipld-prime/fluent" cidlink "github.com/ipld/go-ipld-prime/linking/cid" basicnode "github.com/ipld/go-ipld-prime/node/basic" - "github.com/magiconair/properties/assert" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/ipfs/go-fetcher" @@ -118,6 +119,69 @@ func TestFetchIPLDGraph(t *testing.T) { assertNodesInOrder(t, nodeCh, errCh, 10, map[int]ipld.Node{0: node1, 4: node2, 5: node3, 7: node4}) } +func TestFetchIPLDPath(t *testing.T) { + block5, node5, link5 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleEntry("five").AssignBool(true) + })) + block3, _, link3 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleEntry("three").AssignLink(link5) + })) + block4, _, link4 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleEntry("four").AssignBool(true) + })) + block2, _, link2 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { + na.AssembleEntry("link3").AssignLink(link3) + na.AssembleEntry("link4").AssignLink(link4) + })) + block1, _, _ := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { + na.AssembleEntry("foo").AssignBool(true) + na.AssembleEntry("bar").AssignBool(false) + na.AssembleEntry("nested").CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry("link2").AssignLink(link2) + na.AssembleEntry("nonlink").AssignString("zoo") + }) + })) + + net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(0*time.Millisecond)) + ig := testinstance.NewTestInstanceGenerator(net, nil, nil) + defer ig.Close() + + peers := ig.Instances(2) + hasBlock := peers[0] + defer hasBlock.Exchange.Close() + + for _, blk := range []blocks.Block{block1, block2, block3, block4, block5} { + err := hasBlock.Exchange.HasBlock(blk) + require.NoError(t, err) + } + + wantsBlock := peers[1] + defer wantsBlock.Exchange.Close() + + wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) + fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + session := fetcherConfig.NewSession(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + path := strings.Split("nested/link2/link3/three", "/") + ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype.Any) + spec := ssb.Matcher() + explorePath := func(p string, s builder.SelectorSpec) builder.SelectorSpec { + return ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) { efsb.Insert(p, s) }) + } + for i := len(path) - 1; i >= 0; i-- { + spec = explorePath(path[i], spec) + } + sel, err := spec.Selector() + require.NoError(t, err) + + nodeCh, errCh := fetcher.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel) + require.NoError(t, err) + + assertNodesInOrder(t, nodeCh, errCh, 1, map[int]ipld.Node{0: node5}) +} + func TestHelpers(t *testing.T) { block3, node3, link3 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { na.AssembleEntry("three").AssignBool(true) From 5d202f30e0edd0418a7bae01fecfcf26a655817e Mon Sep 17 00:00:00 2001 From: acruikshank Date: Thu, 4 Mar 2021 14:40:36 -0500 Subject: [PATCH 3320/3817] switch from channels to callbacks This commit was moved from ipfs/go-fetcher@51577c0fe3a03978bde51667c1ce8118c10fa3e7 --- fetcher/fetcher.go | 111 +++++++++++++++------------------------- fetcher/fetcher_test.go | 46 ++++++++++------- 2 files changed, 69 insertions(+), 88 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 99b06e615..d2cf87e59 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -6,14 +6,12 @@ import ( "fmt" "io" - "github.com/ipld/go-ipld-prime/schema" - - dagpb "github.com/ipld/go-ipld-prime-proto" - "github.com/ipfs/go-blockservice" "github.com/ipld/go-ipld-prime" + dagpb "github.com/ipld/go-ipld-prime-proto" cidlink "github.com/ipld/go-ipld-prime/linking/cid" basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/ipld/go-ipld-prime/schema" "github.com/ipld/go-ipld-prime/traversal" "github.com/ipld/go-ipld-prime/traversal/selector" "github.com/ipld/go-ipld-prime/traversal/selector/builder" @@ -25,20 +23,19 @@ type FetcherConfig struct { type Fetcher interface { // NodeMatching traverses a node graph starting with the provided node using the given selector and possibly crossing - // block boundaries. Each matched node is sent to the FetchResult channel. - // The results and error channels will be closed on query completion or error. The error channel is buffered, - // will emit at most one error and must be checked after processing the results. - NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector) (<-chan FetchResult, <-chan error) + // block boundaries. Each matched node is sent to the FetchResult channel. This method blocks until all results + // are read or an error occurs. The provided function will be called once for every result and possibly once with an + // error after which not be called. + NodeMatching(context.Context, ipld.Node, selector.Selector, FetchCallback) // BlockOfType fetches a node graph of the provided type corresponding to single block by link. - BlockOfType(ctx context.Context, link ipld.Link, ptype ipld.NodePrototype) (ipld.Node, error) + BlockOfType(context.Context, ipld.Link, ipld.NodePrototype) (ipld.Node, error) // BlockMatchingOfType traverses a node graph starting with the given link using the given selector and possibly // crossing block boundaries. The nodes will be typed using the provided prototype. Each matched node is sent to - // the FetchResult channel. - // The results and error channels will be closed on query completion or error. The error channel is buffered, - // will emit at most one error and must be checked after processing the results. - BlockMatchingOfType(ctx context.Context, root ipld.Link, match selector.Selector, ptype ipld.NodePrototype) (<-chan FetchResult, <-chan error) + // the FetchResult channel. This method blocks until all results are read or an error occurs. + // The provided function will be called once for every result and possibly once with an error after which not be called. + BlockMatchingOfType(context.Context, ipld.Link, selector.Selector, ipld.NodePrototype, FetchCallback) } type fetcherSession struct { @@ -52,6 +49,8 @@ type FetchResult struct { LastBlockLink ipld.Link } +type FetchCallback func(result FetchResult, err error) + // NewFetcherConfig creates a FetchConfig from which session may be created and nodes retrieved. func NewFetcherConfig(blockService blockservice.BlockService) FetcherConfig { return FetcherConfig{blockService: blockService} @@ -77,47 +76,21 @@ func (f *fetcherSession) BlockOfType(ctx context.Context, link ipld.Link, ptype return nb.Build(), nil } -func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector) (<-chan FetchResult, <-chan error) { - results := make(chan FetchResult) - errors := make(chan error, 1) - - go func() { - defer close(results) - defer close(errors) - - err := f.fetch(ctx, node, match, results) - if err != nil { - errors <- err - return - } - }() - - return results, errors +func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector, cb FetchCallback) { + f.fetch(ctx, node, match, cb) } -func (f *fetcherSession) BlockMatchingOfType(ctx context.Context, root ipld.Link, match selector.Selector, ptype ipld.NodePrototype) (<-chan FetchResult, <-chan error) { - results := make(chan FetchResult) - errors := make(chan error, 1) +func (f *fetcherSession) BlockMatchingOfType(ctx context.Context, root ipld.Link, match selector.Selector, + ptype ipld.NodePrototype, cb FetchCallback) { - go func() { - defer close(results) - defer close(errors) - - // retrieve first node - node, err := f.BlockOfType(ctx, root, ptype) - if err != nil { - errors <- err - return - } - - err = f.fetch(ctx, node, match, results) - if err != nil { - errors <- err - return - } - }() + // retrieve first node + node, err := f.BlockOfType(ctx, root, ptype) + if err != nil { + cb(FetchResult{}, err) + return + } - return results, errors + f.fetch(ctx, node, match, cb) } // Block fetches a schemaless node graph corresponding to single block by link. @@ -131,46 +104,43 @@ func Block(ctx context.Context, f Fetcher, link ipld.Link) (ipld.Node, error) { // BlockMatching traverses a schemaless node graph starting with the given link using the given selector and possibly crossing // block boundaries. Each matched node is sent to the FetchResult channel. -func BlockMatching(ctx context.Context, f Fetcher, root ipld.Link, match selector.Selector) (<-chan FetchResult, <-chan error) { +func BlockMatching(ctx context.Context, f Fetcher, root ipld.Link, match selector.Selector, cb FetchCallback) { prototype, err := prototypeFromLink(root) if err != nil { - errors := make(chan error, 1) - errors <- err - return nil, errors + cb(FetchResult{}, err) + return } - return f.BlockMatchingOfType(ctx, root, match, prototype) + f.BlockMatchingOfType(ctx, root, match, prototype, cb) } // BlockAll traverses all nodes in the graph linked by root. The nodes will be untyped and send over the results // channel. -func BlockAll(ctx context.Context, f Fetcher, root ipld.Link) (<-chan FetchResult, <-chan error) { +func BlockAll(ctx context.Context, f Fetcher, root ipld.Link, cb FetchCallback) { prototype, err := prototypeFromLink(root) if err != nil { - errors := make(chan error, 1) - errors <- err - return nil, errors + cb(FetchResult{}, err) + return } - return BlockAllOfType(ctx, f, root, prototype) + BlockAllOfType(ctx, f, root, prototype, cb) } // BlockAllOfType traverses all nodes in the graph linked by root. The nodes will typed according to ptype // and send over the results channel. -func BlockAllOfType(ctx context.Context, f Fetcher, root ipld.Link, ptype ipld.NodePrototype) (<-chan FetchResult, <-chan error) { +func BlockAllOfType(ctx context.Context, f Fetcher, root ipld.Link, ptype ipld.NodePrototype, cb FetchCallback) { ssb := builder.NewSelectorSpecBuilder(ptype) allSelector, err := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( ssb.Matcher(), ssb.ExploreAll(ssb.ExploreRecursiveEdge()), )).Selector() if err != nil { - errors := make(chan error, 1) - errors <- err - return nil, errors + cb(FetchResult{}, err) + return } - return f.BlockMatchingOfType(ctx, root, allSelector, ptype) + f.BlockMatchingOfType(ctx, root, allSelector, ptype, cb) } -func (f *fetcherSession) fetch(ctx context.Context, node ipld.Node, match selector.Selector, results chan FetchResult) error { - return traversal.Progress{ +func (f *fetcherSession) fetch(ctx context.Context, node ipld.Node, match selector.Selector, cb FetchCallback) { + err := traversal.Progress{ Cfg: &traversal.Config{ LinkLoader: f.loader(ctx), LinkTargetNodePrototypeChooser: dagpb.AddDagPBSupportToChooser(func(_ ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { @@ -181,14 +151,17 @@ func (f *fetcherSession) fetch(ctx context.Context, node ipld.Node, match select }), }, }.WalkMatching(node, match, func(prog traversal.Progress, n ipld.Node) error { - results <- FetchResult{ + cb(FetchResult{ Node: n, Path: prog.Path, LastBlockPath: prog.LastBlock.Path, LastBlockLink: prog.LastBlock.Link, - } + }, nil) return nil }) + if err != nil { + cb(FetchResult{}, err) + } } func (f *fetcherSession) loader(ctx context.Context) ipld.Loader { diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index a40c9106a..39c6cf0d4 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -113,10 +113,13 @@ func TestFetchIPLDGraph(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - nodeCh, errCh := fetcher.BlockAll(ctx, session, cidlink.Link{Cid: block1.Cid()}) - require.NoError(t, err) + results := []fetcher.FetchResult{} + fetcher.BlockAll(ctx, session, cidlink.Link{Cid: block1.Cid()}, func(res fetcher.FetchResult, err error) { + require.NoError(t, err) + results = append(results, res) + }) - assertNodesInOrder(t, nodeCh, errCh, 10, map[int]ipld.Node{0: node1, 4: node2, 5: node3, 7: node4}) + assertNodesInOrder(t, results, 10, map[int]ipld.Node{0: node1, 4: node2, 5: node3, 7: node4}) } func TestFetchIPLDPath(t *testing.T) { @@ -176,10 +179,13 @@ func TestFetchIPLDPath(t *testing.T) { sel, err := spec.Selector() require.NoError(t, err) - nodeCh, errCh := fetcher.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel) - require.NoError(t, err) + results := []fetcher.FetchResult{} + fetcher.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel, func(res fetcher.FetchResult, err error) { + require.NoError(t, err) + results = append(results, res) + }) - assertNodesInOrder(t, nodeCh, errCh, 1, map[int]ipld.Node{0: node5}) + assertNodesInOrder(t, results, 1, map[int]ipld.Node{0: node5}) } func TestHelpers(t *testing.T) { @@ -250,10 +256,13 @@ func TestHelpers(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - nodeCh, errCh := fetcher.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel) - require.NoError(t, err) + results := []fetcher.FetchResult{} + fetcher.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel, func(res fetcher.FetchResult, err error) { + require.NoError(t, err) + results = append(results, res) + }) - assertNodesInOrder(t, nodeCh, errCh, 4, map[int]ipld.Node{0: node1, 4: node2}) + assertNodesInOrder(t, results, 4, map[int]ipld.Node{0: node1, 4: node2}) }) t.Run("BlockAllOfType retrieves all nodes with a schema", func(t *testing.T) { @@ -263,26 +272,25 @@ func TestHelpers(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - nodeCh, errCh := fetcher.BlockAllOfType(ctx, session, cidlink.Link{Cid: block1.Cid()}, basicnode.Prototype__Any{}) - require.NoError(t, err) + results := []fetcher.FetchResult{} + fetcher.BlockAllOfType(ctx, session, cidlink.Link{Cid: block1.Cid()}, basicnode.Prototype__Any{}, func(res fetcher.FetchResult, err error) { + require.NoError(t, err) + results = append(results, res) + }) - assertNodesInOrder(t, nodeCh, errCh, 10, map[int]ipld.Node{0: node1, 4: node2, 5: node3, 7: node4}) + assertNodesInOrder(t, results, 10, map[int]ipld.Node{0: node1, 4: node2, 5: node3, 7: node4}) }) } -func assertNodesInOrder(t *testing.T, nodeCh <-chan fetcher.FetchResult, errCh <-chan error, nodeCount int, nodes map[int]ipld.Node) { - order := 0 - for res := range nodeCh { +func assertNodesInOrder(t *testing.T, results []fetcher.FetchResult, nodeCount int, nodes map[int]ipld.Node) { + for order, res := range results { expectedNode, ok := nodes[order] if ok { assert.Equal(t, expectedNode, res.Node) } - order++ } - err := <-errCh - require.NoError(t, err) - assert.Equal(t, nodeCount, order) + assert.Equal(t, nodeCount, len(results)) } func encodeBlock(n ipld.Node) (blocks.Block, ipld.Node, ipld.Link) { From 60412437761bb66599cfd66b3d4d274e5b17f543 Mon Sep 17 00:00:00 2001 From: acruikshank Date: Thu, 4 Mar 2021 15:36:58 -0500 Subject: [PATCH 3321/3817] correct and clarify documentation This commit was moved from ipfs/go-fetcher@dd838b25112cf1cfa25c75b75449472f152d4ac6 --- fetcher/fetcher.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index d2cf87e59..ba416c849 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -23,18 +23,19 @@ type FetcherConfig struct { type Fetcher interface { // NodeMatching traverses a node graph starting with the provided node using the given selector and possibly crossing - // block boundaries. Each matched node is sent to the FetchResult channel. This method blocks until all results - // are read or an error occurs. The provided function will be called once for every result and possibly once with an - // error after which not be called. + // block boundaries. Each matched node is passed as FetchResult to the callback. + // The sequence of events is: NodeMatching begins, the callback is called zero or more times with a FetchResult, the + // callback is called zero or one time with an error, then NodeMatching returns. NodeMatching(context.Context, ipld.Node, selector.Selector, FetchCallback) // BlockOfType fetches a node graph of the provided type corresponding to single block by link. BlockOfType(context.Context, ipld.Link, ipld.NodePrototype) (ipld.Node, error) // BlockMatchingOfType traverses a node graph starting with the given link using the given selector and possibly - // crossing block boundaries. The nodes will be typed using the provided prototype. Each matched node is sent to - // the FetchResult channel. This method blocks until all results are read or an error occurs. - // The provided function will be called once for every result and possibly once with an error after which not be called. + // crossing block boundaries. The nodes will be typed using the provided prototype. Each matched node is passed as + // a FetchResult to the callback. + // The sequence of events is: BlockMatchingOfType begins, the callback is called zero or more times with a + // FetchResult, the callback is called zero or one time with an error, then BlockMatchingOfType returns. BlockMatchingOfType(context.Context, ipld.Link, selector.Selector, ipld.NodePrototype, FetchCallback) } From a7eb407ad863245a2932316bf4ce7b6a7ac48301 Mon Sep 17 00:00:00 2001 From: acruikshank Date: Thu, 4 Mar 2021 15:56:51 -0500 Subject: [PATCH 3322/3817] return errors from function instead of passing to cb This commit was moved from ipfs/go-fetcher@0062e438af322806d0acaa783c9cb77516039304 --- fetcher/fetcher.go | 90 ++++++++++++++++++----------------------- fetcher/fetcher_test.go | 20 +++++---- 2 files changed, 51 insertions(+), 59 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index ba416c849..1619a92a3 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -23,20 +23,20 @@ type FetcherConfig struct { type Fetcher interface { // NodeMatching traverses a node graph starting with the provided node using the given selector and possibly crossing - // block boundaries. Each matched node is passed as FetchResult to the callback. - // The sequence of events is: NodeMatching begins, the callback is called zero or more times with a FetchResult, the - // callback is called zero or one time with an error, then NodeMatching returns. - NodeMatching(context.Context, ipld.Node, selector.Selector, FetchCallback) + // block boundaries. Each matched node is passed as FetchResult to the callback. Errors returned from callback will + // halt the traversal. The sequence of events is: NodeMatching begins, the callback is called zero or more times + // with a FetchResult, then NodeMatching returns. + NodeMatching(context.Context, ipld.Node, selector.Selector, FetchCallback) error // BlockOfType fetches a node graph of the provided type corresponding to single block by link. BlockOfType(context.Context, ipld.Link, ipld.NodePrototype) (ipld.Node, error) // BlockMatchingOfType traverses a node graph starting with the given link using the given selector and possibly // crossing block boundaries. The nodes will be typed using the provided prototype. Each matched node is passed as - // a FetchResult to the callback. + // a FetchResult to the callback. Errors returned from callback will halt the traversal. // The sequence of events is: BlockMatchingOfType begins, the callback is called zero or more times with a - // FetchResult, the callback is called zero or one time with an error, then BlockMatchingOfType returns. - BlockMatchingOfType(context.Context, ipld.Link, selector.Selector, ipld.NodePrototype, FetchCallback) + // FetchResult, then BlockMatchingOfType returns. + BlockMatchingOfType(context.Context, ipld.Link, selector.Selector, ipld.NodePrototype, FetchCallback) error } type fetcherSession struct { @@ -50,7 +50,7 @@ type FetchResult struct { LastBlockLink ipld.Link } -type FetchCallback func(result FetchResult, err error) +type FetchCallback func(result FetchResult) error // NewFetcherConfig creates a FetchConfig from which session may be created and nodes retrieved. func NewFetcherConfig(blockService blockservice.BlockService) FetcherConfig { @@ -77,21 +77,37 @@ func (f *fetcherSession) BlockOfType(ctx context.Context, link ipld.Link, ptype return nb.Build(), nil } -func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector, cb FetchCallback) { - f.fetch(ctx, node, match, cb) +func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector, cb FetchCallback) error { + return traversal.Progress{ + Cfg: &traversal.Config{ + LinkLoader: f.loader(ctx), + LinkTargetNodePrototypeChooser: dagpb.AddDagPBSupportToChooser(func(_ ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { + if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { + return tlnkNd.LinkTargetNodePrototype(), nil + } + return basicnode.Prototype.Any, nil + }), + }, + }.WalkMatching(node, match, func(prog traversal.Progress, n ipld.Node) error { + return cb(FetchResult{ + Node: n, + Path: prog.Path, + LastBlockPath: prog.LastBlock.Path, + LastBlockLink: prog.LastBlock.Link, + }) + }) } func (f *fetcherSession) BlockMatchingOfType(ctx context.Context, root ipld.Link, match selector.Selector, - ptype ipld.NodePrototype, cb FetchCallback) { + ptype ipld.NodePrototype, cb FetchCallback) error { // retrieve first node node, err := f.BlockOfType(ctx, root, ptype) if err != nil { - cb(FetchResult{}, err) - return + return err } - f.fetch(ctx, node, match, cb) + return f.NodeMatching(ctx, node, match, cb) } // Block fetches a schemaless node graph corresponding to single block by link. @@ -105,64 +121,36 @@ func Block(ctx context.Context, f Fetcher, link ipld.Link) (ipld.Node, error) { // BlockMatching traverses a schemaless node graph starting with the given link using the given selector and possibly crossing // block boundaries. Each matched node is sent to the FetchResult channel. -func BlockMatching(ctx context.Context, f Fetcher, root ipld.Link, match selector.Selector, cb FetchCallback) { +func BlockMatching(ctx context.Context, f Fetcher, root ipld.Link, match selector.Selector, cb FetchCallback) error { prototype, err := prototypeFromLink(root) if err != nil { - cb(FetchResult{}, err) - return + return err } - f.BlockMatchingOfType(ctx, root, match, prototype, cb) + return f.BlockMatchingOfType(ctx, root, match, prototype, cb) } // BlockAll traverses all nodes in the graph linked by root. The nodes will be untyped and send over the results // channel. -func BlockAll(ctx context.Context, f Fetcher, root ipld.Link, cb FetchCallback) { +func BlockAll(ctx context.Context, f Fetcher, root ipld.Link, cb FetchCallback) error { prototype, err := prototypeFromLink(root) if err != nil { - cb(FetchResult{}, err) - return + return err } - BlockAllOfType(ctx, f, root, prototype, cb) + return BlockAllOfType(ctx, f, root, prototype, cb) } // BlockAllOfType traverses all nodes in the graph linked by root. The nodes will typed according to ptype // and send over the results channel. -func BlockAllOfType(ctx context.Context, f Fetcher, root ipld.Link, ptype ipld.NodePrototype, cb FetchCallback) { +func BlockAllOfType(ctx context.Context, f Fetcher, root ipld.Link, ptype ipld.NodePrototype, cb FetchCallback) error { ssb := builder.NewSelectorSpecBuilder(ptype) allSelector, err := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( ssb.Matcher(), ssb.ExploreAll(ssb.ExploreRecursiveEdge()), )).Selector() if err != nil { - cb(FetchResult{}, err) - return - } - f.BlockMatchingOfType(ctx, root, allSelector, ptype, cb) -} - -func (f *fetcherSession) fetch(ctx context.Context, node ipld.Node, match selector.Selector, cb FetchCallback) { - err := traversal.Progress{ - Cfg: &traversal.Config{ - LinkLoader: f.loader(ctx), - LinkTargetNodePrototypeChooser: dagpb.AddDagPBSupportToChooser(func(_ ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { - if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { - return tlnkNd.LinkTargetNodePrototype(), nil - } - return basicnode.Prototype.Any, nil - }), - }, - }.WalkMatching(node, match, func(prog traversal.Progress, n ipld.Node) error { - cb(FetchResult{ - Node: n, - Path: prog.Path, - LastBlockPath: prog.LastBlock.Path, - LastBlockLink: prog.LastBlock.Link, - }, nil) - return nil - }) - if err != nil { - cb(FetchResult{}, err) + return err } + return f.BlockMatchingOfType(ctx, root, allSelector, ptype, cb) } func (f *fetcherSession) loader(ctx context.Context) ipld.Loader { diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index 39c6cf0d4..715251bad 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -114,10 +114,11 @@ func TestFetchIPLDGraph(t *testing.T) { defer cancel() results := []fetcher.FetchResult{} - fetcher.BlockAll(ctx, session, cidlink.Link{Cid: block1.Cid()}, func(res fetcher.FetchResult, err error) { - require.NoError(t, err) + err = fetcher.BlockAll(ctx, session, cidlink.Link{Cid: block1.Cid()}, func(res fetcher.FetchResult) error { results = append(results, res) + return nil }) + require.NoError(t, err) assertNodesInOrder(t, results, 10, map[int]ipld.Node{0: node1, 4: node2, 5: node3, 7: node4}) } @@ -180,10 +181,11 @@ func TestFetchIPLDPath(t *testing.T) { require.NoError(t, err) results := []fetcher.FetchResult{} - fetcher.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel, func(res fetcher.FetchResult, err error) { - require.NoError(t, err) + err = fetcher.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel, func(res fetcher.FetchResult) error { results = append(results, res) + return nil }) + require.NoError(t, err) assertNodesInOrder(t, results, 1, map[int]ipld.Node{0: node5}) } @@ -257,10 +259,11 @@ func TestHelpers(t *testing.T) { defer cancel() results := []fetcher.FetchResult{} - fetcher.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel, func(res fetcher.FetchResult, err error) { - require.NoError(t, err) + err = fetcher.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel, func(res fetcher.FetchResult) error { results = append(results, res) + return nil }) + require.NoError(t, err) assertNodesInOrder(t, results, 4, map[int]ipld.Node{0: node1, 4: node2}) }) @@ -273,10 +276,11 @@ func TestHelpers(t *testing.T) { defer cancel() results := []fetcher.FetchResult{} - fetcher.BlockAllOfType(ctx, session, cidlink.Link{Cid: block1.Cid()}, basicnode.Prototype__Any{}, func(res fetcher.FetchResult, err error) { - require.NoError(t, err) + err = fetcher.BlockAllOfType(ctx, session, cidlink.Link{Cid: block1.Cid()}, basicnode.Prototype__Any{}, func(res fetcher.FetchResult) error { results = append(results, res) + return nil }) + require.NoError(t, err) assertNodesInOrder(t, results, 10, map[int]ipld.Node{0: node1, 4: node2, 5: node3, 7: node4}) }) From 2c0d955f282a0dfa158b00fe28e60462a4118f28 Mon Sep 17 00:00:00 2001 From: acruikshank Date: Thu, 4 Mar 2021 16:57:03 -0500 Subject: [PATCH 3323/3817] upgrade to latest go-fetcher This commit was moved from ipfs/go-ipfs-provider@ebfb3d13ba62029f7197ba1886e51632947aec27 --- provider/simple/reprovide.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index c52b6ff7e..6161eaa70 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -235,15 +235,14 @@ func pinSet(ctx context.Context, pinning Pinner, fetchConfig fetcher.FetcherConf for _, key := range rkeys { set.Visitor(ctx)(key) if !onlyRoots { - nodeCh, errCh := fetcher.BlockAll(ctx, session, cidlink.Link{key}) - for res := range nodeCh { + err := fetcher.BlockAll(ctx, session, cidlink.Link{key}, func(res fetcher.FetchResult) error { clink, ok := res.LastBlockLink.(cidlink.Link) if ok { set.Visitor(ctx)(clink.Cid) } - } - - if err := <-errCh; err != nil { + return nil + }) + if err != nil { logR.Errorf("reprovide indirect pins: %s", err) return } From 5745a8396acccfb33047c71073437a4fde7919fc Mon Sep 17 00:00:00 2001 From: acruikshank Date: Mon, 8 Mar 2021 13:33:04 -0500 Subject: [PATCH 3324/3817] make compatible with linksystem and dagpb, add prototype chooser config This commit was moved from ipfs/go-fetcher@a372586bf0e761f2178036228f253a8c2eb8e26b --- fetcher/fetcher.go | 80 ++++++++++++++++++++++++----------------- fetcher/fetcher_test.go | 39 ++++++++++---------- 2 files changed, 67 insertions(+), 52 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 1619a92a3..85361a765 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -7,8 +7,8 @@ import ( "io" "github.com/ipfs/go-blockservice" + dagpb "github.com/ipld/go-codec-dagpb" "github.com/ipld/go-ipld-prime" - dagpb "github.com/ipld/go-ipld-prime-proto" cidlink "github.com/ipld/go-ipld-prime/linking/cid" basicnode "github.com/ipld/go-ipld-prime/node/basic" "github.com/ipld/go-ipld-prime/schema" @@ -18,7 +18,8 @@ import ( ) type FetcherConfig struct { - blockService blockservice.BlockService + blockService blockservice.BlockService + PrototypeChooser traversal.LinkTargetNodePrototypeChooser } type Fetcher interface { @@ -37,10 +38,14 @@ type Fetcher interface { // The sequence of events is: BlockMatchingOfType begins, the callback is called zero or more times with a // FetchResult, then BlockMatchingOfType returns. BlockMatchingOfType(context.Context, ipld.Link, selector.Selector, ipld.NodePrototype, FetchCallback) error + + // Uses the given link to pick a prototype to build the linked node. + PrototypeFromLink(link ipld.Link) (ipld.NodePrototype, error) } type fetcherSession struct { - blockGetter blockservice.BlockGetter + linkSystem ipld.LinkSystem + protoChooser traversal.LinkTargetNodePrototypeChooser } type FetchResult struct { @@ -54,39 +59,30 @@ type FetchCallback func(result FetchResult) error // NewFetcherConfig creates a FetchConfig from which session may be created and nodes retrieved. func NewFetcherConfig(blockService blockservice.BlockService) FetcherConfig { - return FetcherConfig{blockService: blockService} + return FetcherConfig{ + blockService: blockService, + PrototypeChooser: DefaultPrototypeChooser, + } } // NewSession creates a session from which nodes may be retrieved. // The session ends when the provided context is canceled. func (fc FetcherConfig) NewSession(ctx context.Context) Fetcher { - return &fetcherSession{ - blockGetter: blockservice.NewSession(ctx, fc.blockService), - } + ls := cidlink.DefaultLinkSystem() + ls.StorageReadOpener = blockOpener(ctx, blockservice.NewSession(ctx, fc.blockService)) + return &fetcherSession{linkSystem: ls, protoChooser: fc.PrototypeChooser} } // BlockOfType fetches a node graph of the provided type corresponding to single block by link. func (f *fetcherSession) BlockOfType(ctx context.Context, link ipld.Link, ptype ipld.NodePrototype) (ipld.Node, error) { - nb := ptype.NewBuilder() - - err := link.Load(ctx, ipld.LinkContext{}, nb, f.loader(ctx)) - if err != nil { - return nil, err - } - - return nb.Build(), nil + return f.linkSystem.Load(ipld.LinkContext{}, link, ptype) } func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector, cb FetchCallback) error { return traversal.Progress{ Cfg: &traversal.Config{ - LinkLoader: f.loader(ctx), - LinkTargetNodePrototypeChooser: dagpb.AddDagPBSupportToChooser(func(_ ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { - if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { - return tlnkNd.LinkTargetNodePrototype(), nil - } - return basicnode.Prototype.Any, nil - }), + LinkSystem: f.linkSystem, + LinkTargetNodePrototypeChooser: f.protoChooser, }, }.WalkMatching(node, match, func(prog traversal.Progress, n ipld.Node) error { return cb(FetchResult{ @@ -110,9 +106,13 @@ func (f *fetcherSession) BlockMatchingOfType(ctx context.Context, root ipld.Link return f.NodeMatching(ctx, node, match, cb) } +func (f *fetcherSession) PrototypeFromLink(lnk ipld.Link) (ipld.NodePrototype, error) { + return f.protoChooser(lnk, ipld.LinkContext{}) +} + // Block fetches a schemaless node graph corresponding to single block by link. func Block(ctx context.Context, f Fetcher, link ipld.Link) (ipld.Node, error) { - prototype, err := prototypeFromLink(link) + prototype, err := f.PrototypeFromLink(link) if err != nil { return nil, err } @@ -122,7 +122,7 @@ func Block(ctx context.Context, f Fetcher, link ipld.Link) (ipld.Node, error) { // BlockMatching traverses a schemaless node graph starting with the given link using the given selector and possibly crossing // block boundaries. Each matched node is sent to the FetchResult channel. func BlockMatching(ctx context.Context, f Fetcher, root ipld.Link, match selector.Selector, cb FetchCallback) error { - prototype, err := prototypeFromLink(root) + prototype, err := f.PrototypeFromLink(root) if err != nil { return err } @@ -132,7 +132,7 @@ func BlockMatching(ctx context.Context, f Fetcher, root ipld.Link, match selecto // BlockAll traverses all nodes in the graph linked by root. The nodes will be untyped and send over the results // channel. func BlockAll(ctx context.Context, f Fetcher, root ipld.Link, cb FetchCallback) error { - prototype, err := prototypeFromLink(root) + prototype, err := f.PrototypeFromLink(root) if err != nil { return err } @@ -153,14 +153,14 @@ func BlockAllOfType(ctx context.Context, f Fetcher, root ipld.Link, ptype ipld.N return f.BlockMatchingOfType(ctx, root, allSelector, ptype, cb) } -func (f *fetcherSession) loader(ctx context.Context) ipld.Loader { - return func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) { +func blockOpener(ctx context.Context, bs *blockservice.Session) ipld.BlockReadOpener { + return func(_ ipld.LinkContext, lnk ipld.Link) (io.Reader, error) { cidLink, ok := lnk.(cidlink.Link) if !ok { return nil, fmt.Errorf("invalid link type for loading: %v", lnk) } - blk, err := f.blockGetter.GetBlock(ctx, cidLink.Cid) + blk, err := bs.GetBlock(ctx, cidLink.Cid) if err != nil { return nil, err } @@ -169,8 +169,24 @@ func (f *fetcherSession) loader(ctx context.Context) ipld.Loader { } } -func prototypeFromLink(lnk ipld.Link) (ipld.NodePrototype, error) { - return dagpb.AddDagPBSupportToChooser(func(_ ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { - return basicnode.Prototype__Any{}, nil - })(lnk, ipld.LinkContext{}) +func DefaultPrototypeChooser(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { + c, ok := lnk.(cidlink.Link) + if ok { + switch c.Cid.Prefix().Codec { + case 0x70: + return dagpb.Type.PBNode, nil + case 0x55: + return basicnode.Prototype.Bytes, nil + default: + if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { + return tlnkNd.LinkTargetNodePrototype(), nil + } + return basicnode.Prototype.Any, nil + } + } + + if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { + return tlnkNd.LinkTargetNodePrototype(), nil + } + return basicnode.Prototype.Any, nil } diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index 715251bad..904afed10 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -20,9 +20,10 @@ import ( delay "github.com/ipfs/go-ipfs-delay" mockrouting "github.com/ipfs/go-ipfs-routing/mock" "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/codec/dagcbor" + _ "github.com/ipld/go-ipld-prime/codec/dagcbor" "github.com/ipld/go-ipld-prime/fluent" cidlink "github.com/ipld/go-ipld-prime/linking/cid" + _ "github.com/ipld/go-ipld-prime/multihash/register/all" basicnode "github.com/ipld/go-ipld-prime/node/basic" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -30,8 +31,6 @@ import ( "github.com/ipfs/go-fetcher" ) -var _ cidlink.MulticodecDecoder = dagcbor.Decoder - func TestFetchIPLDPrimeNode(t *testing.T) { block, node, _ := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { na.AssembleEntry("foo").AssignBool(true) @@ -298,27 +297,27 @@ func assertNodesInOrder(t *testing.T, results []fetcher.FetchResult, nodeCount i } func encodeBlock(n ipld.Node) (blocks.Block, ipld.Node, ipld.Link) { - lb := cidlink.LinkBuilder{cid.Prefix{ + ls := cidlink.DefaultLinkSystem() + var b blocks.Block + lb := cidlink.LinkPrototype{cid.Prefix{ Version: 1, - Codec: cid.DagCBOR, + Codec: 0x71, MhType: 0x17, MhLength: 20, }} - var b blocks.Block - lnk, err := lb.Build(context.Background(), ipld.LinkContext{}, n, - func(ipld.LinkContext) (io.Writer, ipld.StoreCommitter, error) { - buf := bytes.Buffer{} - return &buf, func(lnk ipld.Link) error { - clnk, ok := lnk.(cidlink.Link) - if !ok { - return fmt.Errorf("incorrect link type %v", lnk) - } - var err error - b, err = blocks.NewBlockWithCid(buf.Bytes(), clnk.Cid) - return err - }, nil - }, - ) + ls.StorageWriteOpener = func(ipld.LinkContext) (io.Writer, ipld.BlockWriteCommitter, error) { + buf := bytes.Buffer{} + return &buf, func(lnk ipld.Link) error { + clnk, ok := lnk.(cidlink.Link) + if !ok { + return fmt.Errorf("incorrect link type %v", lnk) + } + var err error + b, err = blocks.NewBlockWithCid(buf.Bytes(), clnk.Cid) + return err + }, nil + } + lnk, err := ls.Store(ipld.LinkContext{}, lb, n) if err != nil { panic(err) } From bcd1cabaf12474893ba17b2ee3efbc0c0f395928 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 8 Mar 2021 11:18:12 -0800 Subject: [PATCH 3325/3817] ci: remove travis support we use github actions now This commit was moved from ipld/go-car@bf7d93e8fb3d66d686a0812ab81a716bc587258f --- ipld/car/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/ipld/car/README.md b/ipld/car/README.md index 1142a2293..65124038a 100644 --- a/ipld/car/README.md +++ b/ipld/car/README.md @@ -5,7 +5,6 @@ go-car (go!) [![](https://img.shields.io/badge/project-ipld-orange.svg?style=flat-square)](https://github.com/ipld/ipld) [![](https://img.shields.io/badge/freenode-%23ipld-orange.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipld) [![Coverage Status](https://codecov.io/gh/ipld/go-car/branch/master/graph/badge.svg)](https://codecov.io/gh/ipld/go-car/branch/master) -[![Travis CI](https://travis-ci.org/ipld/go-car.svg?branch=master)](https://travis-ci.org/ipld/go-car) > go-car is a simple way of packing a merkledag into a single file From 63068475b3994422310f2c1caa58244047563261 Mon Sep 17 00:00:00 2001 From: acruikshank Date: Mon, 15 Mar 2021 14:17:49 -0400 Subject: [PATCH 3326/3817] use linksystem tag This commit was moved from ipfs/go-fetcher@efee55b83ad98ffda383576cfffa549673828ca3 --- fetcher/fetcher_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index 904afed10..0dd572020 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -23,7 +23,6 @@ import ( _ "github.com/ipld/go-ipld-prime/codec/dagcbor" "github.com/ipld/go-ipld-prime/fluent" cidlink "github.com/ipld/go-ipld-prime/linking/cid" - _ "github.com/ipld/go-ipld-prime/multihash/register/all" basicnode "github.com/ipld/go-ipld-prime/node/basic" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" From f4ded30259f80f76a96a4a170f5c11ee77b83fbd Mon Sep 17 00:00:00 2001 From: acruikshank Date: Mon, 15 Mar 2021 17:09:53 -0400 Subject: [PATCH 3327/3817] update to tagged ipld-prime and latest fetcher This commit was moved from ipfs/go-ipfs-provider@3ff3362e205a0fadf32540ffe3383882b81d6815 --- provider/simple/reprovide_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/provider/simple/reprovide_test.go b/provider/simple/reprovide_test.go index 913ca072c..e43e791b3 100644 --- a/provider/simple/reprovide_test.go +++ b/provider/simple/reprovide_test.go @@ -14,8 +14,7 @@ import ( offline "github.com/ipfs/go-ipfs-exchange-offline" mock "github.com/ipfs/go-ipfs-routing/mock" cbor "github.com/ipfs/go-ipld-cbor" - "github.com/ipld/go-ipld-prime/codec/dagcbor" - cidlink "github.com/ipld/go-ipld-prime/linking/cid" + _ "github.com/ipld/go-ipld-prime/codec/dagcbor" peer "github.com/libp2p/go-libp2p-core/peer" testutil "github.com/libp2p/go-libp2p-testing/net" mh "github.com/multiformats/go-multihash" @@ -23,7 +22,6 @@ import ( . "github.com/ipfs/go-ipfs-provider/simple" ) -var _ cidlink.MulticodecDecoder = dagcbor.Decoder func setupRouting(t *testing.T) (clA, clB mock.Client, idA, idB peer.ID) { mrserv := mock.NewServer() From eb3d7ddb5959c6f38f37f33891e777cb464b0005 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 16 Mar 2021 16:38:32 +0100 Subject: [PATCH 3328/3817] Remove InitializeKeyspace function from publisher This is mostly go-ipfs specific and has been moved there. See https://github.com/ipfs/go-ipfs/pull/7984 This commit was moved from ipfs/go-namesys@67510bf0749928fd3ca618418c76d67f5ff8e6dd --- namesys/publisher.go | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index e2f9e67d8..37dab0ed2 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -9,11 +9,9 @@ import ( proto "github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-datastore" dsquery "github.com/ipfs/go-datastore/query" - pin "github.com/ipfs/go-ipfs-pinner" ipns "github.com/ipfs/go-ipns" pb "github.com/ipfs/go-ipns/pb" path "github.com/ipfs/go-path" - ft "github.com/ipfs/go-unixfs" ci "github.com/libp2p/go-libp2p-core/crypto" peer "github.com/libp2p/go-libp2p-core/peer" routing "github.com/libp2p/go-libp2p-core/routing" @@ -294,27 +292,6 @@ func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec return r.PutValue(ctx, ipnskey, data) } -// InitializeKeyspace sets the ipns record for the given key to -// point to an empty directory. -// TODO: this doesnt feel like it belongs here -func InitializeKeyspace(ctx context.Context, pub Publisher, pins pin.Pinner, key ci.PrivKey) error { - emptyDir := ft.EmptyDirNode() - - // pin recursively because this might already be pinned - // and doing a direct pin would throw an error in that case - err := pins.Pin(ctx, emptyDir, true) - if err != nil { - return err - } - - err = pins.Flush(ctx) - if err != nil { - return err - } - - return pub.Publish(ctx, key, path.FromCid(emptyDir.Cid())) -} - // PkKeyForID returns the public key routing key for the given peer ID. func PkKeyForID(id peer.ID) string { return "/pk/" + string(id) From faba013c6c313cb9004bc22e8c1c6cc51e8cdcc9 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 16 Mar 2021 16:47:17 +0100 Subject: [PATCH 3329/3817] Remove dependencies to go-unixfs The remaining dependency was in tests. We can hardcode a CID instead of requiring unixfs just to obtain an arbitrary CID that correspond to the empty directory. This commit was moved from ipfs/go-namesys@d416a143bd64757d5965302e4def561cc9271fb9 --- namesys/namesys_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index cc0ca6959..0ae858f4e 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -11,7 +11,6 @@ import ( offroute "github.com/ipfs/go-ipfs-routing/offline" ipns "github.com/ipfs/go-ipns" path "github.com/ipfs/go-path" - unixfs "github.com/ipfs/go-unixfs" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" ci "github.com/libp2p/go-libp2p-core/crypto" peer "github.com/libp2p/go-libp2p-core/peer" @@ -110,7 +109,8 @@ func TestPublishWithCache0(t *testing.T) { }) nsys := NewNameSystem(routing, dst, 0) - p, err := path.ParsePath(unixfs.EmptyDirNode().Cid().String()) + // CID is arbitrary. + p, err := path.ParsePath("QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") if err != nil { t.Fatal(err) } @@ -142,7 +142,8 @@ func TestPublishWithTTL(t *testing.T) { }) nsys := NewNameSystem(routing, dst, 128) - p, err := path.ParsePath(unixfs.EmptyDirNode().Cid().String()) + // CID is arbitrary. + p, err := path.ParsePath("QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") if err != nil { t.Fatal(err) } From eddfec331ea8620baa639578f022840e44ee0cf4 Mon Sep 17 00:00:00 2001 From: acruikshank Date: Wed, 17 Mar 2021 11:14:12 -0400 Subject: [PATCH 3330/3817] upgrade dagpb, use its default chooser This commit was moved from ipfs/go-fetcher@7f06e527bfc240d654c9cff0879402ddab97519c --- fetcher/fetcher.go | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 85361a765..752eafbff 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -169,24 +169,10 @@ func blockOpener(ctx context.Context, bs *blockservice.Session) ipld.BlockReadOp } } -func DefaultPrototypeChooser(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { - c, ok := lnk.(cidlink.Link) - if ok { - switch c.Cid.Prefix().Codec { - case 0x70: - return dagpb.Type.PBNode, nil - case 0x55: - return basicnode.Prototype.Bytes, nil - default: - if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { - return tlnkNd.LinkTargetNodePrototype(), nil - } - return basicnode.Prototype.Any, nil - } - } - +// Chooser that supports DagPB nodes and choosing the prototype from the link. +var DefaultPrototypeChooser = dagpb.AddSupportToChooser(func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { return tlnkNd.LinkTargetNodePrototype(), nil } return basicnode.Prototype.Any, nil -} +}) From bb6b14f33b6a95d716406c93b62a4ed2733a089a Mon Sep 17 00:00:00 2001 From: acruikshank Date: Wed, 17 Mar 2021 11:38:16 -0400 Subject: [PATCH 3331/3817] gofmt This commit was moved from ipfs/go-ipfs-provider@28506e1a9e9e7acee4301052d1c0423801e1a978 --- provider/simple/reprovide_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/provider/simple/reprovide_test.go b/provider/simple/reprovide_test.go index e43e791b3..7c796cbfe 100644 --- a/provider/simple/reprovide_test.go +++ b/provider/simple/reprovide_test.go @@ -22,7 +22,6 @@ import ( . "github.com/ipfs/go-ipfs-provider/simple" ) - func setupRouting(t *testing.T) (clA, clB mock.Client, idA, idB peer.ID) { mrserv := mock.NewServer() From 0f4e8f07dd21fffbe4f901e42af4e0c64e809180 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 22 Mar 2021 10:52:06 -0700 Subject: [PATCH 3332/3817] Revert "Merge pull request ipfs/go-ipfs-provider#30 from ipfs/feat/use_ipld_prime" This reverts commit bbafe76e26330b48b0f3ded318d51cc3a3ff65d4, reversing changes made to b8fd93c8e02bf176c5649b5684e535f51ae8686c. This commit was moved from ipfs/go-ipfs-provider@46797b12263096b6acbc422169311eef194e530b --- provider/simple/reprovide.go | 25 +++++++++---------------- provider/simple/reprovide_test.go | 7 +++---- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index 6161eaa70..bfe6173e1 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -9,11 +9,11 @@ import ( "github.com/cenkalti/backoff" "github.com/ipfs/go-cid" "github.com/ipfs/go-cidutil" - "github.com/ipfs/go-fetcher" blocks "github.com/ipfs/go-ipfs-blockstore" + ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" + "github.com/ipfs/go-merkledag" "github.com/ipfs/go-verifcid" - cidlink "github.com/ipld/go-ipld-prime/linking/cid" "github.com/libp2p/go-libp2p-core/routing" ) @@ -184,9 +184,9 @@ type Pinner interface { } // NewPinnedProvider returns provider supplying pinned keys -func NewPinnedProvider(onlyRoots bool, pinning Pinner, fetchConfig fetcher.FetcherConfig) KeyChanFunc { +func NewPinnedProvider(onlyRoots bool, pinning Pinner, dag ipld.DAGService) KeyChanFunc { return func(ctx context.Context) (<-chan cid.Cid, error) { - set, err := pinSet(ctx, pinning, fetchConfig, onlyRoots) + set, err := pinSet(ctx, pinning, dag, onlyRoots) if err != nil { return nil, err } @@ -208,7 +208,7 @@ func NewPinnedProvider(onlyRoots bool, pinning Pinner, fetchConfig fetcher.Fetch } } -func pinSet(ctx context.Context, pinning Pinner, fetchConfig fetcher.FetcherConfig, onlyRoots bool) (*cidutil.StreamingSet, error) { +func pinSet(ctx context.Context, pinning Pinner, dag ipld.DAGService, onlyRoots bool) (*cidutil.StreamingSet, error) { set := cidutil.NewStreamingSet() go func() { @@ -230,18 +230,11 @@ func pinSet(ctx context.Context, pinning Pinner, fetchConfig fetcher.FetcherConf logR.Errorf("reprovide indirect pins: %s", err) return } - - session := fetchConfig.NewSession(ctx) for _, key := range rkeys { - set.Visitor(ctx)(key) - if !onlyRoots { - err := fetcher.BlockAll(ctx, session, cidlink.Link{key}, func(res fetcher.FetchResult) error { - clink, ok := res.LastBlockLink.(cidlink.Link) - if ok { - set.Visitor(ctx)(clink.Cid) - } - return nil - }) + if onlyRoots { + set.Visitor(ctx)(key) + } else { + err := merkledag.Walk(ctx, merkledag.GetLinksWithDAG(dag), key, set.Visitor(ctx)) if err != nil { logR.Errorf("reprovide indirect pins: %s", err) return diff --git a/provider/simple/reprovide_test.go b/provider/simple/reprovide_test.go index 7c796cbfe..3858baf5e 100644 --- a/provider/simple/reprovide_test.go +++ b/provider/simple/reprovide_test.go @@ -9,12 +9,11 @@ import ( "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" - "github.com/ipfs/go-fetcher" "github.com/ipfs/go-ipfs-blockstore" offline "github.com/ipfs/go-ipfs-exchange-offline" mock "github.com/ipfs/go-ipfs-routing/mock" cbor "github.com/ipfs/go-ipld-cbor" - _ "github.com/ipld/go-ipld-prime/codec/dagcbor" + merkledag "github.com/ipfs/go-merkledag" peer "github.com/libp2p/go-libp2p-core/peer" testutil "github.com/libp2p/go-libp2p-testing/net" mh "github.com/multiformats/go-multihash" @@ -196,7 +195,7 @@ func TestReprovidePinned(t *testing.T) { nodes, bstore := setupDag(t) - fetchConfig := fetcher.NewFetcherConfig(bsrv.New(bstore, offline.Exchange(bstore))) + dag := merkledag.NewDAGService(bsrv.New(bstore, offline.Exchange(bstore))) for i := 0; i < 2; i++ { clA, clB, idA, _ := setupRouting(t) @@ -216,7 +215,7 @@ func TestReprovidePinned(t *testing.T) { keyProvider := NewPinnedProvider(onlyRoots, &mockPinner{ recursive: []cid.Cid{nodes[1]}, direct: []cid.Cid{nodes[3]}, - }, fetchConfig) + }, dag) reprov := NewReprovider(ctx, time.Hour, clA, keyProvider) err := reprov.Reprovide() From 33c53eec1ec0b2e98bc147758b3dda55c2b31212 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Fri, 5 Mar 2021 13:46:11 -0800 Subject: [PATCH 3333/3817] feat(helpers): add helpers for block visiting This commit was moved from ipfs/go-fetcher@be81ab796f5dfe4e47200ffffa0fdefb746c913e --- fetcher/fetcher.go | 26 +++-- fetcher/fetcher_test.go | 62 +++--------- fetcher/helpers/block_visitor.go | 43 ++++++++ fetcher/helpers/block_visitor_test.go | 140 ++++++++++++++++++++++++++ fetcher/testutil/testutil.go | 42 ++++++++ 5 files changed, 258 insertions(+), 55 deletions(-) create mode 100644 fetcher/helpers/block_visitor.go create mode 100644 fetcher/helpers/block_visitor_test.go create mode 100644 fetcher/testutil/testutil.go diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 752eafbff..a4ba0e7d5 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -78,13 +78,8 @@ func (f *fetcherSession) BlockOfType(ctx context.Context, link ipld.Link, ptype return f.linkSystem.Load(ipld.LinkContext{}, link, ptype) } -func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector, cb FetchCallback) error { - return traversal.Progress{ - Cfg: &traversal.Config{ - LinkSystem: f.linkSystem, - LinkTargetNodePrototypeChooser: f.protoChooser, - }, - }.WalkMatching(node, match, func(prog traversal.Progress, n ipld.Node) error { +func (f *fetcherSession) nodeMatching(ctx context.Context, initialProgress traversal.Progress, node ipld.Node, match selector.Selector, cb FetchCallback) error { + return initialProgress.WalkMatching(node, match, func(prog traversal.Progress, n ipld.Node) error { return cb(FetchResult{ Node: n, Path: prog.Path, @@ -94,6 +89,19 @@ func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match }) } +func (f *fetcherSession) blankProgress(ctx context.Context) traversal.Progress { + return traversal.Progress{ + Cfg: &traversal.Config{ + LinkSystem: f.linkSystem, + LinkTargetNodePrototypeChooser: f.protoChooser, + }, + } +} + +func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector, cb FetchCallback) error { + return f.nodeMatching(ctx, f.blankProgress(ctx), node, match, cb) +} + func (f *fetcherSession) BlockMatchingOfType(ctx context.Context, root ipld.Link, match selector.Selector, ptype ipld.NodePrototype, cb FetchCallback) error { @@ -103,7 +111,9 @@ func (f *fetcherSession) BlockMatchingOfType(ctx context.Context, root ipld.Link return err } - return f.NodeMatching(ctx, node, match, cb) + progress := f.blankProgress(ctx) + progress.LastBlock.Link = root + return f.nodeMatching(ctx, progress, node, match, cb) } func (f *fetcherSession) PrototypeFromLink(lnk ipld.Link) (ipld.NodePrototype, error) { diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index 0dd572020..5287ef6b7 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -1,10 +1,7 @@ package fetcher_test import ( - "bytes" "context" - "fmt" - "io" "strings" "testing" "time" @@ -16,11 +13,10 @@ import ( tn "github.com/ipfs/go-bitswap/testnet" blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-blockservice" - "github.com/ipfs/go-cid" + "github.com/ipfs/go-fetcher/testutil" delay "github.com/ipfs/go-ipfs-delay" mockrouting "github.com/ipfs/go-ipfs-routing/mock" "github.com/ipld/go-ipld-prime" - _ "github.com/ipld/go-ipld-prime/codec/dagcbor" "github.com/ipld/go-ipld-prime/fluent" cidlink "github.com/ipld/go-ipld-prime/linking/cid" basicnode "github.com/ipld/go-ipld-prime/node/basic" @@ -31,7 +27,7 @@ import ( ) func TestFetchIPLDPrimeNode(t *testing.T) { - block, node, _ := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { + block, node, _ := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { na.AssembleEntry("foo").AssignBool(true) na.AssembleEntry("bar").AssignBool(false) na.AssembleEntry("nested").CreateMap(2, func(na fluent.MapAssembler) { @@ -66,17 +62,17 @@ func TestFetchIPLDPrimeNode(t *testing.T) { } func TestFetchIPLDGraph(t *testing.T) { - block3, node3, link3 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + block3, node3, link3 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { na.AssembleEntry("three").AssignBool(true) })) - block4, node4, link4 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + block4, node4, link4 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { na.AssembleEntry("four").AssignBool(true) })) - block2, node2, link2 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { + block2, node2, link2 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { na.AssembleEntry("link3").AssignLink(link3) na.AssembleEntry("link4").AssignLink(link4) })) - block1, node1, _ := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { + block1, node1, _ := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { na.AssembleEntry("foo").AssignBool(true) na.AssembleEntry("bar").AssignBool(false) na.AssembleEntry("nested").CreateMap(2, func(na fluent.MapAssembler) { @@ -122,20 +118,20 @@ func TestFetchIPLDGraph(t *testing.T) { } func TestFetchIPLDPath(t *testing.T) { - block5, node5, link5 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + block5, node5, link5 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { na.AssembleEntry("five").AssignBool(true) })) - block3, _, link3 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + block3, _, link3 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { na.AssembleEntry("three").AssignLink(link5) })) - block4, _, link4 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + block4, _, link4 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { na.AssembleEntry("four").AssignBool(true) })) - block2, _, link2 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { + block2, _, link2 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { na.AssembleEntry("link3").AssignLink(link3) na.AssembleEntry("link4").AssignLink(link4) })) - block1, _, _ := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { + block1, _, _ := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { na.AssembleEntry("foo").AssignBool(true) na.AssembleEntry("bar").AssignBool(false) na.AssembleEntry("nested").CreateMap(2, func(na fluent.MapAssembler) { @@ -189,17 +185,17 @@ func TestFetchIPLDPath(t *testing.T) { } func TestHelpers(t *testing.T) { - block3, node3, link3 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + block3, node3, link3 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { na.AssembleEntry("three").AssignBool(true) })) - block4, node4, link4 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + block4, node4, link4 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { na.AssembleEntry("four").AssignBool(true) })) - block2, node2, link2 := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { + block2, node2, link2 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { na.AssembleEntry("link3").AssignLink(link3) na.AssembleEntry("link4").AssignLink(link4) })) - block1, node1, _ := encodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { + block1, node1, _ := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { na.AssembleEntry("foo").AssignBool(true) na.AssembleEntry("bar").AssignBool(false) na.AssembleEntry("nested").CreateMap(2, func(na fluent.MapAssembler) { @@ -294,31 +290,3 @@ func assertNodesInOrder(t *testing.T, results []fetcher.FetchResult, nodeCount i assert.Equal(t, nodeCount, len(results)) } - -func encodeBlock(n ipld.Node) (blocks.Block, ipld.Node, ipld.Link) { - ls := cidlink.DefaultLinkSystem() - var b blocks.Block - lb := cidlink.LinkPrototype{cid.Prefix{ - Version: 1, - Codec: 0x71, - MhType: 0x17, - MhLength: 20, - }} - ls.StorageWriteOpener = func(ipld.LinkContext) (io.Writer, ipld.BlockWriteCommitter, error) { - buf := bytes.Buffer{} - return &buf, func(lnk ipld.Link) error { - clnk, ok := lnk.(cidlink.Link) - if !ok { - return fmt.Errorf("incorrect link type %v", lnk) - } - var err error - b, err = blocks.NewBlockWithCid(buf.Bytes(), clnk.Cid) - return err - }, nil - } - lnk, err := ls.Store(ipld.LinkContext{}, lb, n) - if err != nil { - panic(err) - } - return b, n, lnk -} diff --git a/fetcher/helpers/block_visitor.go b/fetcher/helpers/block_visitor.go new file mode 100644 index 000000000..25fc5420c --- /dev/null +++ b/fetcher/helpers/block_visitor.go @@ -0,0 +1,43 @@ +package helpers + +import ( + "github.com/ipfs/go-cid" + "github.com/ipfs/go-fetcher" + "github.com/ipld/go-ipld-prime" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" +) + +// BlockResult specifies a node at the top of a block boundary +type BlockResult struct { + Node ipld.Node + Link ipld.Link +} + +// BlockCallback is a callback for visiting blocks +type BlockCallback func(BlockResult) error + +// OnBlocks produces a fetch call back that only gets called when visiting blocks during a fetch +func OnBlocks(bv BlockCallback) fetcher.FetchCallback { + return func(fr fetcher.FetchResult) error { + if fr.LastBlockPath.String() == fr.Path.String() { + return bv(BlockResult{ + Node: fr.Node, + Link: fr.LastBlockLink, + }) + } + return nil + } +} + +// OnUniqueBlocks is a callback that only gets called visiting each block once +func OnUniqueBlocks(bv BlockCallback) fetcher.FetchCallback { + set := cid.NewSet() + return OnBlocks(func(br BlockResult) error { + c := br.Link.(cidlink.Link).Cid + if set.Has(c) { + return nil + } + set.Add(c) + return bv(br) + }) +} diff --git a/fetcher/helpers/block_visitor_test.go b/fetcher/helpers/block_visitor_test.go new file mode 100644 index 000000000..097946af4 --- /dev/null +++ b/fetcher/helpers/block_visitor_test.go @@ -0,0 +1,140 @@ +package helpers_test + +import ( + "context" + "testing" + "time" + + testinstance "github.com/ipfs/go-bitswap/testinstance" + tn "github.com/ipfs/go-bitswap/testnet" + "github.com/ipfs/go-blockservice" + "github.com/ipfs/go-fetcher" + "github.com/ipfs/go-fetcher/helpers" + "github.com/ipfs/go-fetcher/testutil" + delay "github.com/ipfs/go-ipfs-delay" + mockrouting "github.com/ipfs/go-ipfs-routing/mock" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/fluent" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestFetchGraphToBlocks(t *testing.T) { + block3, node3, link3 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleEntry("three").AssignBool(true) + })) + block4, node4, link4 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleEntry("four").AssignBool(true) + })) + block2, node2, link2 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { + na.AssembleEntry("link3").AssignLink(link3) + na.AssembleEntry("link4").AssignLink(link4) + })) + block1, node1, _ := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { + na.AssembleEntry("foo").AssignBool(true) + na.AssembleEntry("bar").AssignBool(false) + na.AssembleEntry("nested").CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry("link2").AssignLink(link2) + na.AssembleEntry("nonlink").AssignString("zoo") + }) + })) + + net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(0*time.Millisecond)) + ig := testinstance.NewTestInstanceGenerator(net, nil, nil) + defer ig.Close() + + peers := ig.Instances(2) + hasBlock := peers[0] + defer hasBlock.Exchange.Close() + + err := hasBlock.Exchange.HasBlock(block1) + require.NoError(t, err) + err = hasBlock.Exchange.HasBlock(block2) + require.NoError(t, err) + err = hasBlock.Exchange.HasBlock(block3) + require.NoError(t, err) + err = hasBlock.Exchange.HasBlock(block4) + require.NoError(t, err) + + wantsBlock := peers[1] + defer wantsBlock.Exchange.Close() + + wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) + fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + session := fetcherConfig.NewSession(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + results := []helpers.BlockResult{} + err = fetcher.BlockAll(ctx, session, cidlink.Link{Cid: block1.Cid()}, helpers.OnBlocks(func(res helpers.BlockResult) error { + results = append(results, res) + return nil + })) + require.NoError(t, err) + + assertBlocksInOrder(t, results, 4, map[int]ipld.Node{0: node1, 1: node2, 2: node3, 3: node4}) +} + +func TestFetchGraphToUniqueBlocks(t *testing.T) { + block3, node3, link3 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleEntry("three").AssignBool(true) + })) + block2, node2, link2 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { + na.AssembleEntry("link3").AssignLink(link3) + })) + block1, node1, _ := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { + na.AssembleEntry("foo").AssignBool(true) + na.AssembleEntry("bar").AssignBool(false) + na.AssembleEntry("nested").CreateMap(2, func(na fluent.MapAssembler) { + na.AssembleEntry("link3").AssignLink(link3) + na.AssembleEntry("link2").AssignLink(link2) + na.AssembleEntry("nonlink").AssignString("zoo") + }) + })) + + net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(0*time.Millisecond)) + ig := testinstance.NewTestInstanceGenerator(net, nil, nil) + defer ig.Close() + + peers := ig.Instances(2) + hasBlock := peers[0] + defer hasBlock.Exchange.Close() + + err := hasBlock.Exchange.HasBlock(block1) + require.NoError(t, err) + err = hasBlock.Exchange.HasBlock(block2) + require.NoError(t, err) + err = hasBlock.Exchange.HasBlock(block3) + require.NoError(t, err) + + wantsBlock := peers[1] + defer wantsBlock.Exchange.Close() + + wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) + fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + session := fetcherConfig.NewSession(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + results := []helpers.BlockResult{} + err = fetcher.BlockAll(ctx, session, cidlink.Link{Cid: block1.Cid()}, helpers.OnUniqueBlocks(func(res helpers.BlockResult) error { + results = append(results, res) + return nil + })) + require.NoError(t, err) + + assertBlocksInOrder(t, results, 3, map[int]ipld.Node{0: node1, 1: node3, 2: node2}) +} + +func assertBlocksInOrder(t *testing.T, results []helpers.BlockResult, nodeCount int, nodes map[int]ipld.Node) { + for order, res := range results { + expectedNode, ok := nodes[order] + if ok { + assert.Equal(t, expectedNode, res.Node) + } + } + + assert.Equal(t, nodeCount, len(results)) +} diff --git a/fetcher/testutil/testutil.go b/fetcher/testutil/testutil.go new file mode 100644 index 000000000..87badbdfb --- /dev/null +++ b/fetcher/testutil/testutil.go @@ -0,0 +1,42 @@ +package testutil + +import ( + "bytes" + "fmt" + "io" + + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + "github.com/ipld/go-ipld-prime" + _ "github.com/ipld/go-ipld-prime/codec/dagcbor" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" +) + +// EncodeBlock produces an encoded block from a node +func EncodeBlock(n ipld.Node) (blocks.Block, ipld.Node, ipld.Link) { + ls := cidlink.DefaultLinkSystem() + var b blocks.Block + lb := cidlink.LinkPrototype{cid.Prefix{ + Version: 1, + Codec: 0x71, + MhType: 0x17, + MhLength: 20, + }} + ls.StorageWriteOpener = func(ipld.LinkContext) (io.Writer, ipld.BlockWriteCommitter, error) { + buf := bytes.Buffer{} + return &buf, func(lnk ipld.Link) error { + clnk, ok := lnk.(cidlink.Link) + if !ok { + return fmt.Errorf("incorrect link type %v", lnk) + } + var err error + b, err = blocks.NewBlockWithCid(buf.Bytes(), clnk.Cid) + return err + }, nil + } + lnk, err := ls.Store(ipld.LinkContext{}, lb, n) + if err != nil { + panic(err) + } + return b, n, lnk +} From f5906a25fd14c10d1b7d5050959d309b5d9af756 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Mar 2021 16:08:47 -0700 Subject: [PATCH 3334/3817] chore: relicense All copyrightable contributions were made by PL employees anyways. This commit was moved from ipld/go-car@da44d7096cb24954d5b8f1d1fdf8f4572123c0d9 --- ipld/car/LICENSE | 4 ++++ ipld/car/LICENSE-APACHE | 5 +++++ ipld/car/LICENSE-MIT | 19 +++++++++++++++++++ ipld/car/README.md | 2 +- 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 ipld/car/LICENSE create mode 100644 ipld/car/LICENSE-APACHE create mode 100644 ipld/car/LICENSE-MIT diff --git a/ipld/car/LICENSE b/ipld/car/LICENSE new file mode 100644 index 000000000..2c2c6eb27 --- /dev/null +++ b/ipld/car/LICENSE @@ -0,0 +1,4 @@ +This project is dual-licensed under Apache 2.0 and MIT terms. + +MIT: https://www.opensource.org/licenses/mit +Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/ipld/car/LICENSE-APACHE b/ipld/car/LICENSE-APACHE new file mode 100644 index 000000000..14478a3b6 --- /dev/null +++ b/ipld/car/LICENSE-APACHE @@ -0,0 +1,5 @@ +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/ipld/car/LICENSE-MIT b/ipld/car/LICENSE-MIT new file mode 100644 index 000000000..72dc60d84 --- /dev/null +++ b/ipld/car/LICENSE-MIT @@ -0,0 +1,19 @@ +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ipld/car/README.md b/ipld/car/README.md index 65124038a..033318ee1 100644 --- a/ipld/car/README.md +++ b/ipld/car/README.md @@ -24,4 +24,4 @@ Small note: If editing the Readme, please conform to the [standard-readme](https ## License -MIT © Whyrusleeping +Apache-2.0/MIT © Protocol Labs From dcbdec2fbdf167765c86395e37cb2cd26c9d0c70 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Mar 2021 20:20:35 -0700 Subject: [PATCH 3335/3817] chore: remove LICENSE It looks like this may be confusing pkg.go.dev's license detection. This commit was moved from ipld/go-car@317e565c9ce5e797b52e1601212ecaefdb194b8d --- ipld/car/LICENSE | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 ipld/car/LICENSE diff --git a/ipld/car/LICENSE b/ipld/car/LICENSE deleted file mode 100644 index 2c2c6eb27..000000000 --- a/ipld/car/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual-licensed under Apache 2.0 and MIT terms. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 From 22ba694bcd784e959a7ca9d73f595b77c2e1cbce Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 24 Mar 2021 20:46:01 -0700 Subject: [PATCH 3336/3817] chore: switch to a single license file This one should work with both github and godoc. This commit was moved from ipld/go-car@eee4102e3bcaa4c3178517733620194ee5bf3e42 --- ipld/car/LICENSE-APACHE | 5 - ipld/car/LICENSE-MIT | 19 ---- ipld/car/LICENSE.md | 229 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+), 24 deletions(-) delete mode 100644 ipld/car/LICENSE-APACHE delete mode 100644 ipld/car/LICENSE-MIT create mode 100644 ipld/car/LICENSE.md diff --git a/ipld/car/LICENSE-APACHE b/ipld/car/LICENSE-APACHE deleted file mode 100644 index 14478a3b6..000000000 --- a/ipld/car/LICENSE-APACHE +++ /dev/null @@ -1,5 +0,0 @@ -Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/ipld/car/LICENSE-MIT b/ipld/car/LICENSE-MIT deleted file mode 100644 index 72dc60d84..000000000 --- a/ipld/car/LICENSE-MIT +++ /dev/null @@ -1,19 +0,0 @@ -The MIT License (MIT) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/ipld/car/LICENSE.md b/ipld/car/LICENSE.md new file mode 100644 index 000000000..15601cba6 --- /dev/null +++ b/ipld/car/LICENSE.md @@ -0,0 +1,229 @@ +The contents of this repository are Copyright (c) corresponding authors and +contributors, licensed under the `Permissive License Stack` meaning either of: + +- Apache-2.0 Software License: https://www.apache.org/licenses/LICENSE-2.0 + ([...4tr2kfsq](https://gateway.ipfs.io/ipfs/bafkreiankqxazcae4onkp436wag2lj3ccso4nawxqkkfckd6cg4tr2kfsq)) + +- MIT Software License: https://opensource.org/licenses/MIT + ([...vljevcba](https://gateway.ipfs.io/ipfs/bafkreiepofszg4gfe2gzuhojmksgemsub2h4uy2gewdnr35kswvljevcba)) + +You may not use the contents of this repository except in compliance +with one of the listed Licenses. For an extended clarification of the +intent behind the choice of Licensing please refer to +https://protocol.ai/blog/announcing-the-permissive-license-stack/ + +Unless required by applicable law or agreed to in writing, software +distributed under the terms listed in this notice is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +either express or implied. See each License for the specific language +governing permissions and limitations under that License. + + +`SPDX-License-Identifier: Apache-2.0 OR MIT` + +Verbatim copies of both licenses are included below: + +
Apache-2.0 Software License + +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS +``` +
+ +
MIT Software License + +``` +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` +
From 4d0f8e8085cd78ecf6e6cfc66c98925f49903e66 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Tue, 30 Mar 2021 11:40:29 +1300 Subject: [PATCH 3337/3817] Use eth.domains instead of eth.link This commit was moved from ipfs/go-namesys@64c4398224e68fc9e2ed373702def2db4b9dfe59 --- namesys/dns.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/dns.go b/namesys/dns.go index d8a42f210..9938aa8dd 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -13,7 +13,7 @@ import ( ) const ethTLD = "eth" -const linkTLD = "link" +const linkTLD = "domains" // LookupTXTFunc is a generic type for a function that lookups TXT record values. type LookupTXTFunc func(name string) (txt []string, err error) From 8bb80221b346d4fe2b7bedaf7db21dd706c23f58 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Tue, 30 Mar 2021 11:43:19 +1300 Subject: [PATCH 3338/3817] Update tests to use eth.domains This commit was moved from ipfs/go-namesys@cbcc19cb1368b4a21c2c939d7eadd6f77f17c45d --- namesys/dns_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 5a0e2a7d2..877b81464 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -126,7 +126,7 @@ func newMockDNS() *mockDNS { "fqdn.example.com.": { "dnslink=/ipfs/QmYvMB9yrsSf7RKBghkfwmHJkzJhW2ZgVwq3LxBXXPasFr", }, - "www.wealdtech.eth.link.": { + "www.wealdtech.eth.domains.": { "dnslink=/ipns/ipfs.example.com", }, }, @@ -168,5 +168,5 @@ func TestDNSResolution(t *testing.T) { testResolution(t, r, "fqdn.example.com.", opts.DefaultDepthLimit, "/ipfs/QmYvMB9yrsSf7RKBghkfwmHJkzJhW2ZgVwq3LxBXXPasFr", nil) testResolution(t, r, "www.wealdtech.eth", 1, "/ipns/ipfs.example.com", ErrResolveRecursion) testResolution(t, r, "www.wealdtech.eth", 2, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) - testResolution(t, r, "www.wealdtech.eth.link", 2, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "www.wealdtech.eth.domains", 2, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) } From 6cc2e74de007dee0da375acdd0c34b2826c4210b Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Thu, 1 Apr 2021 16:17:27 +0700 Subject: [PATCH 3339/3817] fix staticcheck errors This commit was moved from ipld/go-car@4f09635583701cee465e56eb52a68064dbefee38 --- ipld/car/car.go | 25 +++++++++++-------------- ipld/car/selectivecar.go | 2 +- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index 9f7e6d10d..30fdd3449 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -183,17 +183,16 @@ func loadCarFast(s batchStore, cr *CarReader) (*CarHeader, error) { var buf []blocks.Block for { blk, err := cr.Next() - switch err { - case io.EOF: - if len(buf) > 0 { - if err := s.PutMany(buf); err != nil { - return nil, err + if err != nil { + if err == io.EOF { + if len(buf) > 0 { + if err := s.PutMany(buf); err != nil { + return nil, err + } } + return cr.Header, nil } - return cr.Header, nil - default: return nil, err - case nil: } buf = append(buf, blk) @@ -208,15 +207,13 @@ func loadCarFast(s batchStore, cr *CarReader) (*CarHeader, error) { } func loadCarSlow(s Store, cr *CarReader) (*CarHeader, error) { - for { blk, err := cr.Next() - switch err { - case io.EOF: - return cr.Header, nil - default: + if err != nil { + if err == io.EOF { + return cr.Header, nil + } return nil, err - case nil: } if err := s.Put(blk); err != nil { diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index 007da104c..15de2d63d 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -212,7 +212,7 @@ func (sct *selectiveCarTraverser) traverseHeader() error { func (sct *selectiveCarTraverser) loader(lnk ipld.Link, ctx ipld.LinkContext) (io.Reader, error) { cl, ok := lnk.(cidlink.Link) if !ok { - return nil, errors.New("Incorrect Link Type") + return nil, errors.New("incorrect link type") } c := cl.Cid blk, err := sct.sc.store.Get(c) From 19d6550eac23a682fb638d0b26d1ebe48659c746 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 1 Apr 2021 12:46:06 -0700 Subject: [PATCH 3340/3817] feat(fetcher): add on demand prototype chooser augmentation This commit was moved from ipfs/go-fetcher@7fc88c4425ce0d23a91e0fcc587f3e89226044c4 --- fetcher/README.md | 2 +- fetcher/fetcher.go | 19 +++++++- fetcher/fetcher_test.go | 102 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 2 deletions(-) diff --git a/fetcher/README.md b/fetcher/README.md index 7039f39b3..71def50d4 100644 --- a/fetcher/README.md +++ b/fetcher/README.md @@ -4,7 +4,7 @@ go-fetcher [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) -Go-fetcher is a library to retrieve IPLD prime nodes from IPFS using Bitswap. +Go-fetcher is a library to retrieve IPLD prime nodes from IPFS using data exchange protocols ## Contribute diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index a4ba0e7d5..409e48b6c 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -17,11 +17,19 @@ import ( "github.com/ipld/go-ipld-prime/traversal/selector/builder" ) +// AugmentChooserFunc is a function that can augment a prototype chooser at the time the Fetcher is initialized, +// which is given the linksystem the fetcher itself will use +type AugmentChooserFunc func(*ipld.LinkSystem, traversal.LinkTargetNodePrototypeChooser) traversal.LinkTargetNodePrototypeChooser + +// FetcherConfig defines a configuration object from which Fetcher instances are constructed type FetcherConfig struct { blockService blockservice.BlockService + AugmentChooser AugmentChooserFunc PrototypeChooser traversal.LinkTargetNodePrototypeChooser } +// Fetcher is an interface for reading from a dag. Reads may be local or remote, and may employ data exchange +// protocols like graphsync and bitswap type Fetcher interface { // NodeMatching traverses a node graph starting with the provided node using the given selector and possibly crossing // block boundaries. Each matched node is passed as FetchResult to the callback. Errors returned from callback will @@ -48,6 +56,7 @@ type fetcherSession struct { protoChooser traversal.LinkTargetNodePrototypeChooser } +// FetchResult is a single node read as part of a dag operation called on a fetcher type FetchResult struct { Node ipld.Node Path ipld.Path @@ -55,6 +64,7 @@ type FetchResult struct { LastBlockLink ipld.Link } +// FetchCallback is called for each node traversed during a fetch type FetchCallback func(result FetchResult) error // NewFetcherConfig creates a FetchConfig from which session may be created and nodes retrieved. @@ -69,8 +79,15 @@ func NewFetcherConfig(blockService blockservice.BlockService) FetcherConfig { // The session ends when the provided context is canceled. func (fc FetcherConfig) NewSession(ctx context.Context) Fetcher { ls := cidlink.DefaultLinkSystem() + // while we may be loading blocks remotely, they are already hash verified by the time they load + // into ipld-prime + ls.TrustedStorage = true ls.StorageReadOpener = blockOpener(ctx, blockservice.NewSession(ctx, fc.blockService)) - return &fetcherSession{linkSystem: ls, protoChooser: fc.PrototypeChooser} + protoChooser := fc.PrototypeChooser + if fc.AugmentChooser != nil { + protoChooser = fc.AugmentChooser(&ls, protoChooser) + } + return &fetcherSession{linkSystem: ls, protoChooser: protoChooser} } // BlockOfType fetches a node graph of the provided type corresponding to single block by link. diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index 5287ef6b7..83205ce6b 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/ipld/go-ipld-prime/traversal" "github.com/ipld/go-ipld-prime/traversal/selector" "github.com/ipld/go-ipld-prime/traversal/selector/builder" @@ -290,3 +291,104 @@ func assertNodesInOrder(t *testing.T, results []fetcher.FetchResult, nodeCount i assert.Equal(t, nodeCount, len(results)) } + +type selfLoader struct { + ipld.Node + ctx context.Context + ls *ipld.LinkSystem +} + +func (sl *selfLoader) LookupByString(key string) (ipld.Node, error) { + nd, err := sl.Node.LookupByString(key) + if err != nil { + return nd, err + } + if nd.Kind() == ipld.Kind_Link { + lnk, _ := nd.AsLink() + nd, err = sl.ls.Load(ipld.LinkContext{Ctx: sl.ctx}, lnk, basicnode.Prototype.Any) + } + return nd, err +} + +type selfLoadPrototype struct { + ctx context.Context + ls *ipld.LinkSystem + basePrototype ipld.NodePrototype +} + +func (slp *selfLoadPrototype) NewBuilder() ipld.NodeBuilder { + return &selfLoadBuilder{ctx: slp.ctx, NodeBuilder: slp.basePrototype.NewBuilder(), ls: slp.ls} +} + +type selfLoadBuilder struct { + ctx context.Context + ipld.NodeBuilder + ls *ipld.LinkSystem +} + +func (slb *selfLoadBuilder) Build() ipld.Node { + nd := slb.NodeBuilder.Build() + return &selfLoader{nd, slb.ctx, slb.ls} +} + +func TestChooserAugmentation(t *testing.T) { + // demonstrates how to use the augment chooser to build an ADL that self loads its own nodes + block3, node3, link3 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleEntry("three").AssignBool(true) + })) + block4, node4, link4 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { + na.AssembleEntry("four").AssignBool(true) + })) + block2, _, _ := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { + na.AssembleEntry("link3").AssignLink(link3) + na.AssembleEntry("link4").AssignLink(link4) + })) + + net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(0*time.Millisecond)) + ig := testinstance.NewTestInstanceGenerator(net, nil, nil) + defer ig.Close() + + peers := ig.Instances(2) + hasBlock := peers[0] + defer hasBlock.Exchange.Close() + + err := hasBlock.Exchange.HasBlock(block2) + require.NoError(t, err) + err = hasBlock.Exchange.HasBlock(block3) + require.NoError(t, err) + err = hasBlock.Exchange.HasBlock(block4) + require.NoError(t, err) + + wantsBlock := peers[1] + defer wantsBlock.Exchange.Close() + + wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) + fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + augmentChooser := func(ls *ipld.LinkSystem, base traversal.LinkTargetNodePrototypeChooser) traversal.LinkTargetNodePrototypeChooser { + return func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { + np, err := base(lnk, lnkCtx) + if err != nil { + return np, err + } + return &selfLoadPrototype{ctx: lnkCtx.Ctx, ls: ls, basePrototype: np}, nil + } + } + fetcherConfig.AugmentChooser = augmentChooser + session := fetcherConfig.NewSession(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + retrievedNode, err := fetcher.Block(ctx, session, cidlink.Link{Cid: block2.Cid()}) + require.NoError(t, err) + + // instead of getting links back, we automatically load the nodes + + retrievedNode3, err := retrievedNode.LookupByString("link3") + require.NoError(t, err) + assert.Equal(t, node3, retrievedNode3) + + retrievedNode4, err := retrievedNode.LookupByString("link4") + require.NoError(t, err) + assert.Equal(t, node4, retrievedNode4) + +} From c06e355f962279ecf328a57272f4906df24800d1 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 1 Apr 2021 20:09:10 -0700 Subject: [PATCH 3341/3817] fix: handle missing session exchange in Session Otherwise, we'll panic. This commit was moved from ipfs/go-blockservice@39f3c34e410ee1a00af4255279f69adb31229ba9 --- blockservice/blockservice.go | 12 ++++++++++-- blockservice/blockservice_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 33f69141c..2f320b139 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -366,12 +366,20 @@ func (s *Session) getSession() exchange.Fetcher { // GetBlock gets a block in the context of a request session func (s *Session) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) { - return getBlock(ctx, c, s.bs, s.getSession) // hash security + var f func() exchange.Fetcher + if s.sessEx != nil { + f = s.getSession + } + return getBlock(ctx, c, s.bs, f) // hash security } // GetBlocks gets blocks in the context of a request session func (s *Session) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan blocks.Block { - return getBlocks(ctx, ks, s.bs, s.getSession) // hash security + var f func() exchange.Fetcher + if s.sessEx != nil { + f = s.getSession + } + return getBlocks(ctx, ks, s.bs, f) // hash security } var _ BlockGetter = (*Session)(nil) diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index dfd12fc43..36cdf0330 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -119,3 +119,31 @@ func (fe *fakeSessionExchange) NewSession(ctx context.Context) exchange.Fetcher } return fe.session } + +func TestNilExchange(t *testing.T) { + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + bgen := butil.NewBlockGenerator() + block := bgen.Next() + + bs := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) + bserv := NewWriteThrough(bs, nil) + sess := NewSession(ctx, bserv) + _, err := sess.GetBlock(ctx, block.Cid()) + if err != ErrNotFound { + t.Fatal("expected block to not be found") + } + err = bs.Put(block) + if err != nil { + t.Fatal(err) + } + b, err := sess.GetBlock(ctx, block.Cid()) + if err != nil { + t.Fatal(err) + } + if b.Cid() != block.Cid() { + t.Fatal("got the wrong block") + } +} From 0abaf0457867c3650e63f744a37207a794d692ae Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Fri, 2 Apr 2021 14:49:28 -0700 Subject: [PATCH 3342/3817] fix(fetcher): switch to node reifier This commit was moved from ipfs/go-fetcher@d4187fbb1ffc1cf945ab4d51a3d089f25b663080 --- fetcher/fetcher.go | 15 +++++++-------- fetcher/fetcher_test.go | 42 ++++++++--------------------------------- 2 files changed, 15 insertions(+), 42 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 409e48b6c..a8cfc4dcc 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -7,6 +7,7 @@ import ( "io" "github.com/ipfs/go-blockservice" + "github.com/ipfs/go-unixfsnode" dagpb "github.com/ipld/go-codec-dagpb" "github.com/ipld/go-ipld-prime" cidlink "github.com/ipld/go-ipld-prime/linking/cid" @@ -17,14 +18,10 @@ import ( "github.com/ipld/go-ipld-prime/traversal/selector/builder" ) -// AugmentChooserFunc is a function that can augment a prototype chooser at the time the Fetcher is initialized, -// which is given the linksystem the fetcher itself will use -type AugmentChooserFunc func(*ipld.LinkSystem, traversal.LinkTargetNodePrototypeChooser) traversal.LinkTargetNodePrototypeChooser - // FetcherConfig defines a configuration object from which Fetcher instances are constructed type FetcherConfig struct { blockService blockservice.BlockService - AugmentChooser AugmentChooserFunc + NodeReifier ipld.NodeReifier PrototypeChooser traversal.LinkTargetNodePrototypeChooser } @@ -71,6 +68,7 @@ type FetchCallback func(result FetchResult) error func NewFetcherConfig(blockService blockservice.BlockService) FetcherConfig { return FetcherConfig{ blockService: blockService, + NodeReifier: DefaultReifier, PrototypeChooser: DefaultPrototypeChooser, } } @@ -83,10 +81,9 @@ func (fc FetcherConfig) NewSession(ctx context.Context) Fetcher { // into ipld-prime ls.TrustedStorage = true ls.StorageReadOpener = blockOpener(ctx, blockservice.NewSession(ctx, fc.blockService)) + ls.NodeReifier = fc.NodeReifier + protoChooser := fc.PrototypeChooser - if fc.AugmentChooser != nil { - protoChooser = fc.AugmentChooser(&ls, protoChooser) - } return &fetcherSession{linkSystem: ls, protoChooser: protoChooser} } @@ -203,3 +200,5 @@ var DefaultPrototypeChooser = dagpb.AddSupportToChooser(func(lnk ipld.Link, lnkC } return basicnode.Prototype.Any, nil }) + +var DefaultReifier = unixfsnode.Reify diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index 83205ce6b..b20d27e53 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -6,7 +6,6 @@ import ( "testing" "time" - "github.com/ipld/go-ipld-prime/traversal" "github.com/ipld/go-ipld-prime/traversal/selector" "github.com/ipld/go-ipld-prime/traversal/selector/builder" @@ -310,28 +309,7 @@ func (sl *selfLoader) LookupByString(key string) (ipld.Node, error) { return nd, err } -type selfLoadPrototype struct { - ctx context.Context - ls *ipld.LinkSystem - basePrototype ipld.NodePrototype -} - -func (slp *selfLoadPrototype) NewBuilder() ipld.NodeBuilder { - return &selfLoadBuilder{ctx: slp.ctx, NodeBuilder: slp.basePrototype.NewBuilder(), ls: slp.ls} -} - -type selfLoadBuilder struct { - ctx context.Context - ipld.NodeBuilder - ls *ipld.LinkSystem -} - -func (slb *selfLoadBuilder) Build() ipld.Node { - nd := slb.NodeBuilder.Build() - return &selfLoader{nd, slb.ctx, slb.ls} -} - -func TestChooserAugmentation(t *testing.T) { +func TestNodeReification(t *testing.T) { // demonstrates how to use the augment chooser to build an ADL that self loads its own nodes block3, node3, link3 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { na.AssembleEntry("three").AssignBool(true) @@ -364,16 +342,10 @@ func TestChooserAugmentation(t *testing.T) { wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) - augmentChooser := func(ls *ipld.LinkSystem, base traversal.LinkTargetNodePrototypeChooser) traversal.LinkTargetNodePrototypeChooser { - return func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { - np, err := base(lnk, lnkCtx) - if err != nil { - return np, err - } - return &selfLoadPrototype{ctx: lnkCtx.Ctx, ls: ls, basePrototype: np}, nil - } + nodeReifier := func(lnkCtx ipld.LinkContext, nd ipld.Node, ls *ipld.LinkSystem) (ipld.Node, error) { + return &selfLoader{Node: nd, ctx: lnkCtx.Ctx, ls: ls}, nil } - fetcherConfig.AugmentChooser = augmentChooser + fetcherConfig.NodeReifier = nodeReifier session := fetcherConfig.NewSession(context.Background()) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() @@ -385,10 +357,12 @@ func TestChooserAugmentation(t *testing.T) { retrievedNode3, err := retrievedNode.LookupByString("link3") require.NoError(t, err) - assert.Equal(t, node3, retrievedNode3) + underlying3 := retrievedNode3.(*selfLoader).Node + assert.Equal(t, node3, underlying3) retrievedNode4, err := retrievedNode.LookupByString("link4") require.NoError(t, err) - assert.Equal(t, node4, retrievedNode4) + underlying4 := retrievedNode4.(*selfLoader).Node + assert.Equal(t, node4, underlying4) } From aae6fd0a78564a263b924819d7d156effde6ad33 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Fri, 2 Apr 2021 17:07:14 -0700 Subject: [PATCH 3343/3817] feat(fetcher): remove default reifier This commit was moved from ipfs/go-fetcher@9362e80d1a76af371182eeda7bba87593f79f707 --- fetcher/fetcher.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index a8cfc4dcc..2402e64ff 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -7,7 +7,6 @@ import ( "io" "github.com/ipfs/go-blockservice" - "github.com/ipfs/go-unixfsnode" dagpb "github.com/ipld/go-codec-dagpb" "github.com/ipld/go-ipld-prime" cidlink "github.com/ipld/go-ipld-prime/linking/cid" @@ -68,7 +67,6 @@ type FetchCallback func(result FetchResult) error func NewFetcherConfig(blockService blockservice.BlockService) FetcherConfig { return FetcherConfig{ blockService: blockService, - NodeReifier: DefaultReifier, PrototypeChooser: DefaultPrototypeChooser, } } @@ -200,5 +198,3 @@ var DefaultPrototypeChooser = dagpb.AddSupportToChooser(func(lnk ipld.Link, lnkC } return basicnode.Prototype.Any, nil }) - -var DefaultReifier = unixfsnode.Reify From 1e4dfb56f44d498e3cae7fb3a308e74a2b109999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Mon, 8 Mar 2021 12:45:29 +0000 Subject: [PATCH 3344/3817] replace go-ipld-prime-proto with go-codec-dagpb We're transitioning to the latter codec for dag-pb on ipld-prime, so go-car should use it too to avoid registering two codecs to handle the same multicodec code. Besides swapping the library, we also update go-ipld-prime to grab its raw codec, to be used for raw nodes. We also roll our own little version of go-ipld-prime-proto's AddDagPBSupportToChooser. With go-codec-dagpb, raw nodes don't need a special prototype, so we need just one special case for PBNode. It also doesn't seem worthwhile to add API to go-codec-dagpb for what is essentially three lines of fairly simple code. We can always revisit that if more downstream users require it too. This commit was moved from ipld/go-car@97bd4ecd4dfcf4e3d20d4cbd5a14aaebcabf4e37 --- ipld/car/selectivecar.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index 15de2d63d..f612fd4bd 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -10,11 +10,14 @@ import ( cid "github.com/ipfs/go-cid" util "github.com/ipld/go-car/util" "github.com/ipld/go-ipld-prime" - dagpb "github.com/ipld/go-ipld-prime-proto" cidlink "github.com/ipld/go-ipld-prime/linking/cid" basicnode "github.com/ipld/go-ipld-prime/node/basic" "github.com/ipld/go-ipld-prime/traversal" "github.com/ipld/go-ipld-prime/traversal/selector" + + // The dag-pb and raw codecs are necessary for unixfs. + dagpb "github.com/ipld/go-codec-dagpb" + _ "github.com/ipld/go-ipld-prime/codec/raw" ) // Dag is a root/selector combo to put into a car @@ -238,10 +241,14 @@ func (sct *selectiveCarTraverser) loader(lnk ipld.Link, ctx ipld.LinkContext) (i } func (sct *selectiveCarTraverser) traverseBlocks() error { - - nsc := dagpb.AddDagPBSupportToChooser(func(ipld.Link, ipld.LinkContext) (ipld.NodePrototype, error) { + nsc := func(lnk ipld.Link, lctx ipld.LinkContext) (ipld.NodePrototype, error) { + // We can decode all nodes into basicnode's Any, except for + // dagpb nodes, which must explicitly use the PBNode prototype. + if lnk, ok := lnk.(cidlink.Link); ok && lnk.Cid.Prefix().Codec == 0x70 { + return dagpb.Type.PBNode, nil + } return basicnode.Prototype.Any, nil - }) + } for _, carDag := range sct.sc.dags { parsed, err := selector.ParseSelector(carDag.Selector) @@ -249,10 +256,7 @@ func (sct *selectiveCarTraverser) traverseBlocks() error { return err } lnk := cidlink.Link{Cid: carDag.Root} - ns, err := nsc(lnk, ipld.LinkContext{}) - if err != nil { - return err - } + ns, _ := nsc(lnk, ipld.LinkContext{}) // nsc won't error nb := ns.NewBuilder() err = lnk.Load(sct.sc.ctx, ipld.LinkContext{}, nb, sct.loader) if err != nil { From 60c3e5f049bfe17bcc7b1266cfe4c99bc8bdadff Mon Sep 17 00:00:00 2001 From: zhoujiajie Date: Wed, 7 Apr 2021 10:08:01 +0800 Subject: [PATCH 3345/3817] chore: update the Usage part of readme This commit was moved from ipfs/go-ipfs-provider@f8b63a164afc1e0fbf12df07b1bc89b2dabd31de --- provider/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/README.md b/provider/README.md index d5f5aadc5..98ee8c08a 100644 --- a/provider/README.md +++ b/provider/README.md @@ -40,7 +40,7 @@ cid := (your cid to provide here) q := queue.NewQueue(context.Background(), "example", dstore) -reprov := simple.NewReprovider(context.Background(), time.Hour * 12, rsys, simple.NewBlockstoreProvider) +reprov := simple.NewReprovider(context.Background(), time.Hour * 12, rsys, simple.NewBlockstoreProvider(dstore)) prov := simple.NewProvider(context.Background(), q, rsys) sys := provider.NewSystem(prov, reprov) From 33050b4e6c480ea73b66dc234e7e309a835710af Mon Sep 17 00:00:00 2001 From: gammazero Date: Tue, 6 Apr 2021 02:08:34 -0700 Subject: [PATCH 3346/3817] Output a more useful resolve error This outputs a missage that blames a specific sife for not having DNSLink record. For example, the error message looks like: `could not resolve name: bad.example.net is missing DNSLink record (https://docs.ipfs.io/concepts/dnslink/)` The "could not resolve name" portion is still present because the returned error wraps the original ErrResolveFailed, allowing code to test if the error is an ErrorResolveFailed error. This commit was moved from ipfs/go-namesys@4e753ad875b58aebc217375b97f051db202336b4 --- namesys/base.go | 9 +++++++++ namesys/namesys_test.go | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/namesys/base.go b/namesys/base.go index 27cc38f88..096bdf91b 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -2,6 +2,7 @@ package namesys import ( "context" + "fmt" "strings" "time" @@ -36,6 +37,14 @@ func resolve(ctx context.Context, r resolver, name string, options opts.ResolveO } } + if err == ErrResolveFailed { + i := len(name) - 1 + for i >= 0 && name[i] != '/' { + i-- + } + // Wrap error so that it can be tested if it is a ErrResolveFailed + err = fmt.Errorf("%w: %s is missing DNSLink record (https://docs.ipfs.io/concepts/dnslink/)", ErrResolveFailed, name[i+1:]) + } return p, err } diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 0ae858f4e..30674106b 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -2,6 +2,7 @@ package namesys import ( "context" + "errors" "fmt" "testing" "time" @@ -25,7 +26,7 @@ type mockResolver struct { func testResolution(t *testing.T, resolver Resolver, name string, depth uint, expected string, expError error) { t.Helper() p, err := resolver.Resolve(context.Background(), name, opts.Depth(depth)) - if err != expError { + if !errors.Is(err, expError) { t.Fatal(fmt.Errorf( "expected %s with a depth of %d to have a '%s' error, but got '%s'", name, depth, expError, err)) From 80ecd1a7c5bf53d0783f17d77fd0768fb30de6dc Mon Sep 17 00:00:00 2001 From: Andrew Gillis Date: Tue, 6 Apr 2021 11:36:17 -0700 Subject: [PATCH 3347/3817] Update base.go with suggestion Co-authored-by: Marcin Rataj This commit was moved from ipfs/go-namesys@3d9078203aa6d1eb557f5c4d590060791d62f6dc --- namesys/base.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/base.go b/namesys/base.go index 096bdf91b..a463e48f1 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -43,7 +43,7 @@ func resolve(ctx context.Context, r resolver, name string, options opts.ResolveO i-- } // Wrap error so that it can be tested if it is a ErrResolveFailed - err = fmt.Errorf("%w: %s is missing DNSLink record (https://docs.ipfs.io/concepts/dnslink/)", ErrResolveFailed, name[i+1:]) + err = fmt.Errorf("%w: %q is missing a DNSLink record (https://docs.ipfs.io/concepts/dnslink/)", ErrResolveFailed, name[i+1:]) } return p, err } From 62a14187bba8bad0cd1f055ca1d3411a36926676 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Fri, 9 Apr 2021 17:39:41 -0700 Subject: [PATCH 3348/3817] feat(fetcher): switch selector to ipld.Node This commit was moved from ipfs/go-fetcher@69e4d9ba3745c36f090b435ea259daf8f1a9e8e6 --- fetcher/fetcher.go | 25 +++++++++++++------------ fetcher/fetcher_test.go | 10 ++++------ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 2402e64ff..d6702adfb 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -31,7 +31,7 @@ type Fetcher interface { // block boundaries. Each matched node is passed as FetchResult to the callback. Errors returned from callback will // halt the traversal. The sequence of events is: NodeMatching begins, the callback is called zero or more times // with a FetchResult, then NodeMatching returns. - NodeMatching(context.Context, ipld.Node, selector.Selector, FetchCallback) error + NodeMatching(context.Context, ipld.Node, ipld.Node, FetchCallback) error // BlockOfType fetches a node graph of the provided type corresponding to single block by link. BlockOfType(context.Context, ipld.Link, ipld.NodePrototype) (ipld.Node, error) @@ -41,7 +41,7 @@ type Fetcher interface { // a FetchResult to the callback. Errors returned from callback will halt the traversal. // The sequence of events is: BlockMatchingOfType begins, the callback is called zero or more times with a // FetchResult, then BlockMatchingOfType returns. - BlockMatchingOfType(context.Context, ipld.Link, selector.Selector, ipld.NodePrototype, FetchCallback) error + BlockMatchingOfType(context.Context, ipld.Link, ipld.Node, ipld.NodePrototype, FetchCallback) error // Uses the given link to pick a prototype to build the linked node. PrototypeFromLink(link ipld.Link) (ipld.NodePrototype, error) @@ -90,8 +90,12 @@ func (f *fetcherSession) BlockOfType(ctx context.Context, link ipld.Link, ptype return f.linkSystem.Load(ipld.LinkContext{}, link, ptype) } -func (f *fetcherSession) nodeMatching(ctx context.Context, initialProgress traversal.Progress, node ipld.Node, match selector.Selector, cb FetchCallback) error { - return initialProgress.WalkMatching(node, match, func(prog traversal.Progress, n ipld.Node) error { +func (f *fetcherSession) nodeMatching(ctx context.Context, initialProgress traversal.Progress, node ipld.Node, match ipld.Node, cb FetchCallback) error { + matchSelector, err := selector.ParseSelector(match) + if err != nil { + return err + } + return initialProgress.WalkMatching(node, matchSelector, func(prog traversal.Progress, n ipld.Node) error { return cb(FetchResult{ Node: n, Path: prog.Path, @@ -110,11 +114,11 @@ func (f *fetcherSession) blankProgress(ctx context.Context) traversal.Progress { } } -func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match selector.Selector, cb FetchCallback) error { +func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match ipld.Node, cb FetchCallback) error { return f.nodeMatching(ctx, f.blankProgress(ctx), node, match, cb) } -func (f *fetcherSession) BlockMatchingOfType(ctx context.Context, root ipld.Link, match selector.Selector, +func (f *fetcherSession) BlockMatchingOfType(ctx context.Context, root ipld.Link, match ipld.Node, ptype ipld.NodePrototype, cb FetchCallback) error { // retrieve first node @@ -143,7 +147,7 @@ func Block(ctx context.Context, f Fetcher, link ipld.Link) (ipld.Node, error) { // BlockMatching traverses a schemaless node graph starting with the given link using the given selector and possibly crossing // block boundaries. Each matched node is sent to the FetchResult channel. -func BlockMatching(ctx context.Context, f Fetcher, root ipld.Link, match selector.Selector, cb FetchCallback) error { +func BlockMatching(ctx context.Context, f Fetcher, root ipld.Link, match ipld.Node, cb FetchCallback) error { prototype, err := f.PrototypeFromLink(root) if err != nil { return err @@ -165,13 +169,10 @@ func BlockAll(ctx context.Context, f Fetcher, root ipld.Link, cb FetchCallback) // and send over the results channel. func BlockAllOfType(ctx context.Context, f Fetcher, root ipld.Link, ptype ipld.NodePrototype, cb FetchCallback) error { ssb := builder.NewSelectorSpecBuilder(ptype) - allSelector, err := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( + allSelector := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( ssb.Matcher(), ssb.ExploreAll(ssb.ExploreRecursiveEdge()), - )).Selector() - if err != nil { - return err - } + )).Node() return f.BlockMatchingOfType(ctx, root, allSelector, ptype, cb) } diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go index b20d27e53..434c60d7f 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/fetcher_test.go @@ -171,11 +171,10 @@ func TestFetchIPLDPath(t *testing.T) { for i := len(path) - 1; i >= 0; i-- { spec = explorePath(path[i], spec) } - sel, err := spec.Selector() - require.NoError(t, err) + sel := spec.Node() results := []fetcher.FetchResult{} - err = fetcher.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel, func(res fetcher.FetchResult) error { + err := fetcher.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel, func(res fetcher.FetchResult) error { results = append(results, res) return nil }) @@ -241,11 +240,10 @@ func TestHelpers(t *testing.T) { t.Run("BlockMatching retrieves nodes matching selector", func(t *testing.T) { // limit recursion depth to 2 nodes and expect to get only 2 blocks (4 nodes) ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype__Any{}) - sel, err := ssb.ExploreRecursive(selector.RecursionLimitDepth(2), ssb.ExploreUnion( + sel := ssb.ExploreRecursive(selector.RecursionLimitDepth(2), ssb.ExploreUnion( ssb.Matcher(), ssb.ExploreAll(ssb.ExploreRecursiveEdge()), - )).Selector() - require.NoError(t, err) + )).Node() fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) session := fetcherConfig.NewSession(context.Background()) From 0782fe6e1054ea1c250332c7312183d0d125d681 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Fri, 9 Apr 2021 17:57:03 -0700 Subject: [PATCH 3349/3817] feat(fetcher): seperate out implementation move block service implementation to its own package. move traversal helpers to traversal directory This commit was moved from ipfs/go-fetcher@3978480e91fe79e1354f40046446d9d99a7c115b --- fetcher/fetcher.go | 160 ------------------ fetcher/helpers/block_visitor_test.go | 10 +- fetcher/helpers/traversal.go | 50 ++++++ fetcher/impl/blockservice/fetcher.go | 127 ++++++++++++++ .../{ => impl/blockservice}/fetcher_test.go | 35 ++-- 5 files changed, 200 insertions(+), 182 deletions(-) create mode 100644 fetcher/helpers/traversal.go create mode 100644 fetcher/impl/blockservice/fetcher.go rename fetcher/{ => impl/blockservice}/fetcher_test.go (92%) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index d6702adfb..6fa0db0a9 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -1,29 +1,11 @@ package fetcher import ( - "bytes" "context" - "fmt" - "io" - "github.com/ipfs/go-blockservice" - dagpb "github.com/ipld/go-codec-dagpb" "github.com/ipld/go-ipld-prime" - cidlink "github.com/ipld/go-ipld-prime/linking/cid" - basicnode "github.com/ipld/go-ipld-prime/node/basic" - "github.com/ipld/go-ipld-prime/schema" - "github.com/ipld/go-ipld-prime/traversal" - "github.com/ipld/go-ipld-prime/traversal/selector" - "github.com/ipld/go-ipld-prime/traversal/selector/builder" ) -// FetcherConfig defines a configuration object from which Fetcher instances are constructed -type FetcherConfig struct { - blockService blockservice.BlockService - NodeReifier ipld.NodeReifier - PrototypeChooser traversal.LinkTargetNodePrototypeChooser -} - // Fetcher is an interface for reading from a dag. Reads may be local or remote, and may employ data exchange // protocols like graphsync and bitswap type Fetcher interface { @@ -47,11 +29,6 @@ type Fetcher interface { PrototypeFromLink(link ipld.Link) (ipld.NodePrototype, error) } -type fetcherSession struct { - linkSystem ipld.LinkSystem - protoChooser traversal.LinkTargetNodePrototypeChooser -} - // FetchResult is a single node read as part of a dag operation called on a fetcher type FetchResult struct { Node ipld.Node @@ -62,140 +39,3 @@ type FetchResult struct { // FetchCallback is called for each node traversed during a fetch type FetchCallback func(result FetchResult) error - -// NewFetcherConfig creates a FetchConfig from which session may be created and nodes retrieved. -func NewFetcherConfig(blockService blockservice.BlockService) FetcherConfig { - return FetcherConfig{ - blockService: blockService, - PrototypeChooser: DefaultPrototypeChooser, - } -} - -// NewSession creates a session from which nodes may be retrieved. -// The session ends when the provided context is canceled. -func (fc FetcherConfig) NewSession(ctx context.Context) Fetcher { - ls := cidlink.DefaultLinkSystem() - // while we may be loading blocks remotely, they are already hash verified by the time they load - // into ipld-prime - ls.TrustedStorage = true - ls.StorageReadOpener = blockOpener(ctx, blockservice.NewSession(ctx, fc.blockService)) - ls.NodeReifier = fc.NodeReifier - - protoChooser := fc.PrototypeChooser - return &fetcherSession{linkSystem: ls, protoChooser: protoChooser} -} - -// BlockOfType fetches a node graph of the provided type corresponding to single block by link. -func (f *fetcherSession) BlockOfType(ctx context.Context, link ipld.Link, ptype ipld.NodePrototype) (ipld.Node, error) { - return f.linkSystem.Load(ipld.LinkContext{}, link, ptype) -} - -func (f *fetcherSession) nodeMatching(ctx context.Context, initialProgress traversal.Progress, node ipld.Node, match ipld.Node, cb FetchCallback) error { - matchSelector, err := selector.ParseSelector(match) - if err != nil { - return err - } - return initialProgress.WalkMatching(node, matchSelector, func(prog traversal.Progress, n ipld.Node) error { - return cb(FetchResult{ - Node: n, - Path: prog.Path, - LastBlockPath: prog.LastBlock.Path, - LastBlockLink: prog.LastBlock.Link, - }) - }) -} - -func (f *fetcherSession) blankProgress(ctx context.Context) traversal.Progress { - return traversal.Progress{ - Cfg: &traversal.Config{ - LinkSystem: f.linkSystem, - LinkTargetNodePrototypeChooser: f.protoChooser, - }, - } -} - -func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match ipld.Node, cb FetchCallback) error { - return f.nodeMatching(ctx, f.blankProgress(ctx), node, match, cb) -} - -func (f *fetcherSession) BlockMatchingOfType(ctx context.Context, root ipld.Link, match ipld.Node, - ptype ipld.NodePrototype, cb FetchCallback) error { - - // retrieve first node - node, err := f.BlockOfType(ctx, root, ptype) - if err != nil { - return err - } - - progress := f.blankProgress(ctx) - progress.LastBlock.Link = root - return f.nodeMatching(ctx, progress, node, match, cb) -} - -func (f *fetcherSession) PrototypeFromLink(lnk ipld.Link) (ipld.NodePrototype, error) { - return f.protoChooser(lnk, ipld.LinkContext{}) -} - -// Block fetches a schemaless node graph corresponding to single block by link. -func Block(ctx context.Context, f Fetcher, link ipld.Link) (ipld.Node, error) { - prototype, err := f.PrototypeFromLink(link) - if err != nil { - return nil, err - } - return f.BlockOfType(ctx, link, prototype) -} - -// BlockMatching traverses a schemaless node graph starting with the given link using the given selector and possibly crossing -// block boundaries. Each matched node is sent to the FetchResult channel. -func BlockMatching(ctx context.Context, f Fetcher, root ipld.Link, match ipld.Node, cb FetchCallback) error { - prototype, err := f.PrototypeFromLink(root) - if err != nil { - return err - } - return f.BlockMatchingOfType(ctx, root, match, prototype, cb) -} - -// BlockAll traverses all nodes in the graph linked by root. The nodes will be untyped and send over the results -// channel. -func BlockAll(ctx context.Context, f Fetcher, root ipld.Link, cb FetchCallback) error { - prototype, err := f.PrototypeFromLink(root) - if err != nil { - return err - } - return BlockAllOfType(ctx, f, root, prototype, cb) -} - -// BlockAllOfType traverses all nodes in the graph linked by root. The nodes will typed according to ptype -// and send over the results channel. -func BlockAllOfType(ctx context.Context, f Fetcher, root ipld.Link, ptype ipld.NodePrototype, cb FetchCallback) error { - ssb := builder.NewSelectorSpecBuilder(ptype) - allSelector := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( - ssb.Matcher(), - ssb.ExploreAll(ssb.ExploreRecursiveEdge()), - )).Node() - return f.BlockMatchingOfType(ctx, root, allSelector, ptype, cb) -} - -func blockOpener(ctx context.Context, bs *blockservice.Session) ipld.BlockReadOpener { - return func(_ ipld.LinkContext, lnk ipld.Link) (io.Reader, error) { - cidLink, ok := lnk.(cidlink.Link) - if !ok { - return nil, fmt.Errorf("invalid link type for loading: %v", lnk) - } - - blk, err := bs.GetBlock(ctx, cidLink.Cid) - if err != nil { - return nil, err - } - - return bytes.NewReader(blk.RawData()), nil - } -} - -// Chooser that supports DagPB nodes and choosing the prototype from the link. -var DefaultPrototypeChooser = dagpb.AddSupportToChooser(func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { - if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { - return tlnkNd.LinkTargetNodePrototype(), nil - } - return basicnode.Prototype.Any, nil -}) diff --git a/fetcher/helpers/block_visitor_test.go b/fetcher/helpers/block_visitor_test.go index 097946af4..f7837043f 100644 --- a/fetcher/helpers/block_visitor_test.go +++ b/fetcher/helpers/block_visitor_test.go @@ -8,8 +8,8 @@ import ( testinstance "github.com/ipfs/go-bitswap/testinstance" tn "github.com/ipfs/go-bitswap/testnet" "github.com/ipfs/go-blockservice" - "github.com/ipfs/go-fetcher" "github.com/ipfs/go-fetcher/helpers" + bsfetcher "github.com/ipfs/go-fetcher/impl/blockservice" "github.com/ipfs/go-fetcher/testutil" delay "github.com/ipfs/go-ipfs-delay" mockrouting "github.com/ipfs/go-ipfs-routing/mock" @@ -62,13 +62,13 @@ func TestFetchGraphToBlocks(t *testing.T) { defer wantsBlock.Exchange.Close() wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) - fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + fetcherConfig := bsfetcher.NewFetcherConfig(wantsGetter) session := fetcherConfig.NewSession(context.Background()) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() results := []helpers.BlockResult{} - err = fetcher.BlockAll(ctx, session, cidlink.Link{Cid: block1.Cid()}, helpers.OnBlocks(func(res helpers.BlockResult) error { + err = helpers.BlockAll(ctx, session, cidlink.Link{Cid: block1.Cid()}, helpers.OnBlocks(func(res helpers.BlockResult) error { results = append(results, res) return nil })) @@ -113,13 +113,13 @@ func TestFetchGraphToUniqueBlocks(t *testing.T) { defer wantsBlock.Exchange.Close() wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) - fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + fetcherConfig := bsfetcher.NewFetcherConfig(wantsGetter) session := fetcherConfig.NewSession(context.Background()) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() results := []helpers.BlockResult{} - err = fetcher.BlockAll(ctx, session, cidlink.Link{Cid: block1.Cid()}, helpers.OnUniqueBlocks(func(res helpers.BlockResult) error { + err = helpers.BlockAll(ctx, session, cidlink.Link{Cid: block1.Cid()}, helpers.OnUniqueBlocks(func(res helpers.BlockResult) error { results = append(results, res) return nil })) diff --git a/fetcher/helpers/traversal.go b/fetcher/helpers/traversal.go new file mode 100644 index 000000000..37feeeb50 --- /dev/null +++ b/fetcher/helpers/traversal.go @@ -0,0 +1,50 @@ +package helpers + +import ( + "context" + + "github.com/ipfs/go-fetcher" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/traversal/selector" + "github.com/ipld/go-ipld-prime/traversal/selector/builder" +) + +// Block fetches a schemaless node graph corresponding to single block by link. +func Block(ctx context.Context, f fetcher.Fetcher, link ipld.Link) (ipld.Node, error) { + prototype, err := f.PrototypeFromLink(link) + if err != nil { + return nil, err + } + return f.BlockOfType(ctx, link, prototype) +} + +// BlockMatching traverses a schemaless node graph starting with the given link using the given selector and possibly crossing +// block boundaries. Each matched node is sent to the FetchResult channel. +func BlockMatching(ctx context.Context, f fetcher.Fetcher, root ipld.Link, match ipld.Node, cb fetcher.FetchCallback) error { + prototype, err := f.PrototypeFromLink(root) + if err != nil { + return err + } + return f.BlockMatchingOfType(ctx, root, match, prototype, cb) +} + +// BlockAll traverses all nodes in the graph linked by root. The nodes will be untyped and send over the results +// channel. +func BlockAll(ctx context.Context, f fetcher.Fetcher, root ipld.Link, cb fetcher.FetchCallback) error { + prototype, err := f.PrototypeFromLink(root) + if err != nil { + return err + } + return BlockAllOfType(ctx, f, root, prototype, cb) +} + +// BlockAllOfType traverses all nodes in the graph linked by root. The nodes will typed according to ptype +// and send over the results channel. +func BlockAllOfType(ctx context.Context, f fetcher.Fetcher, root ipld.Link, ptype ipld.NodePrototype, cb fetcher.FetchCallback) error { + ssb := builder.NewSelectorSpecBuilder(ptype) + allSelector := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( + ssb.Matcher(), + ssb.ExploreAll(ssb.ExploreRecursiveEdge()), + )).Node() + return f.BlockMatchingOfType(ctx, root, allSelector, ptype, cb) +} diff --git a/fetcher/impl/blockservice/fetcher.go b/fetcher/impl/blockservice/fetcher.go new file mode 100644 index 000000000..92f5e5972 --- /dev/null +++ b/fetcher/impl/blockservice/fetcher.go @@ -0,0 +1,127 @@ +package bsfetcher + +import ( + "bytes" + "context" + "fmt" + "io" + + "github.com/ipfs/go-blockservice" + "github.com/ipfs/go-fetcher" + dagpb "github.com/ipld/go-codec-dagpb" + "github.com/ipld/go-ipld-prime" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/ipld/go-ipld-prime/schema" + "github.com/ipld/go-ipld-prime/traversal" + "github.com/ipld/go-ipld-prime/traversal/selector" +) + +type fetcherSession struct { + linkSystem ipld.LinkSystem + protoChooser traversal.LinkTargetNodePrototypeChooser +} + +// FetcherConfig defines a configuration object from which Fetcher instances are constructed +type FetcherConfig struct { + blockService blockservice.BlockService + NodeReifier ipld.NodeReifier + PrototypeChooser traversal.LinkTargetNodePrototypeChooser +} + +// NewFetcherConfig creates a FetchConfig from which session may be created and nodes retrieved. +func NewFetcherConfig(blockService blockservice.BlockService) FetcherConfig { + return FetcherConfig{ + blockService: blockService, + PrototypeChooser: DefaultPrototypeChooser, + } +} + +// NewSession creates a session from which nodes may be retrieved. +// The session ends when the provided context is canceled. +func (fc FetcherConfig) NewSession(ctx context.Context) fetcher.Fetcher { + ls := cidlink.DefaultLinkSystem() + // while we may be loading blocks remotely, they are already hash verified by the time they load + // into ipld-prime + ls.TrustedStorage = true + ls.StorageReadOpener = blockOpener(ctx, blockservice.NewSession(ctx, fc.blockService)) + ls.NodeReifier = fc.NodeReifier + + protoChooser := fc.PrototypeChooser + return &fetcherSession{linkSystem: ls, protoChooser: protoChooser} +} + +// BlockOfType fetches a node graph of the provided type corresponding to single block by link. +func (f *fetcherSession) BlockOfType(ctx context.Context, link ipld.Link, ptype ipld.NodePrototype) (ipld.Node, error) { + return f.linkSystem.Load(ipld.LinkContext{}, link, ptype) +} + +func (f *fetcherSession) nodeMatching(ctx context.Context, initialProgress traversal.Progress, node ipld.Node, match ipld.Node, cb fetcher.FetchCallback) error { + matchSelector, err := selector.ParseSelector(match) + if err != nil { + return err + } + return initialProgress.WalkMatching(node, matchSelector, func(prog traversal.Progress, n ipld.Node) error { + return cb(fetcher.FetchResult{ + Node: n, + Path: prog.Path, + LastBlockPath: prog.LastBlock.Path, + LastBlockLink: prog.LastBlock.Link, + }) + }) +} + +func (f *fetcherSession) blankProgress(ctx context.Context) traversal.Progress { + return traversal.Progress{ + Cfg: &traversal.Config{ + LinkSystem: f.linkSystem, + LinkTargetNodePrototypeChooser: f.protoChooser, + }, + } +} + +func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match ipld.Node, cb fetcher.FetchCallback) error { + return f.nodeMatching(ctx, f.blankProgress(ctx), node, match, cb) +} + +func (f *fetcherSession) BlockMatchingOfType(ctx context.Context, root ipld.Link, match ipld.Node, + ptype ipld.NodePrototype, cb fetcher.FetchCallback) error { + + // retrieve first node + node, err := f.BlockOfType(ctx, root, ptype) + if err != nil { + return err + } + + progress := f.blankProgress(ctx) + progress.LastBlock.Link = root + return f.nodeMatching(ctx, progress, node, match, cb) +} + +func (f *fetcherSession) PrototypeFromLink(lnk ipld.Link) (ipld.NodePrototype, error) { + return f.protoChooser(lnk, ipld.LinkContext{}) +} + +// DefaultPrototypeChooser supports DagPB nodes and choosing the prototype from the link. +var DefaultPrototypeChooser = dagpb.AddSupportToChooser(func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { + if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { + return tlnkNd.LinkTargetNodePrototype(), nil + } + return basicnode.Prototype.Any, nil +}) + +func blockOpener(ctx context.Context, bs *blockservice.Session) ipld.BlockReadOpener { + return func(_ ipld.LinkContext, lnk ipld.Link) (io.Reader, error) { + cidLink, ok := lnk.(cidlink.Link) + if !ok { + return nil, fmt.Errorf("invalid link type for loading: %v", lnk) + } + + blk, err := bs.GetBlock(ctx, cidLink.Cid) + if err != nil { + return nil, err + } + + return bytes.NewReader(blk.RawData()), nil + } +} diff --git a/fetcher/fetcher_test.go b/fetcher/impl/blockservice/fetcher_test.go similarity index 92% rename from fetcher/fetcher_test.go rename to fetcher/impl/blockservice/fetcher_test.go index 434c60d7f..f8c2d0082 100644 --- a/fetcher/fetcher_test.go +++ b/fetcher/impl/blockservice/fetcher_test.go @@ -1,4 +1,4 @@ -package fetcher_test +package bsfetcher_test import ( "context" @@ -13,6 +13,9 @@ import ( tn "github.com/ipfs/go-bitswap/testnet" blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-blockservice" + "github.com/ipfs/go-fetcher" + "github.com/ipfs/go-fetcher/helpers" + bsfetcher "github.com/ipfs/go-fetcher/impl/blockservice" "github.com/ipfs/go-fetcher/testutil" delay "github.com/ipfs/go-ipfs-delay" mockrouting "github.com/ipfs/go-ipfs-routing/mock" @@ -22,8 +25,6 @@ import ( basicnode "github.com/ipld/go-ipld-prime/node/basic" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "github.com/ipfs/go-fetcher" ) func TestFetchIPLDPrimeNode(t *testing.T) { @@ -50,13 +51,13 @@ func TestFetchIPLDPrimeNode(t *testing.T) { defer wantsBlock.Exchange.Close() wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) - fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + fetcherConfig := bsfetcher.NewFetcherConfig(wantsGetter) session := fetcherConfig.NewSession(context.Background()) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - retrievedNode, err := fetcher.Block(ctx, session, cidlink.Link{Cid: block.Cid()}) + retrievedNode, err := helpers.Block(ctx, session, cidlink.Link{Cid: block.Cid()}) require.NoError(t, err) assert.Equal(t, node, retrievedNode) } @@ -102,13 +103,13 @@ func TestFetchIPLDGraph(t *testing.T) { defer wantsBlock.Exchange.Close() wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) - fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + fetcherConfig := bsfetcher.NewFetcherConfig(wantsGetter) session := fetcherConfig.NewSession(context.Background()) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() results := []fetcher.FetchResult{} - err = fetcher.BlockAll(ctx, session, cidlink.Link{Cid: block1.Cid()}, func(res fetcher.FetchResult) error { + err = helpers.BlockAll(ctx, session, cidlink.Link{Cid: block1.Cid()}, func(res fetcher.FetchResult) error { results = append(results, res) return nil }) @@ -157,7 +158,7 @@ func TestFetchIPLDPath(t *testing.T) { defer wantsBlock.Exchange.Close() wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) - fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + fetcherConfig := bsfetcher.NewFetcherConfig(wantsGetter) session := fetcherConfig.NewSession(context.Background()) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() @@ -174,7 +175,7 @@ func TestFetchIPLDPath(t *testing.T) { sel := spec.Node() results := []fetcher.FetchResult{} - err := fetcher.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel, func(res fetcher.FetchResult) error { + err := helpers.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel, func(res fetcher.FetchResult) error { results = append(results, res) return nil }) @@ -226,12 +227,12 @@ func TestHelpers(t *testing.T) { wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) t.Run("Block retrieves node", func(t *testing.T) { - fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + fetcherConfig := bsfetcher.NewFetcherConfig(wantsGetter) session := fetcherConfig.NewSession(context.Background()) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - node, err := fetcher.Block(ctx, session, cidlink.Link{Cid: block1.Cid()}) + node, err := helpers.Block(ctx, session, cidlink.Link{Cid: block1.Cid()}) require.NoError(t, err) assert.Equal(t, node, node1) @@ -245,13 +246,13 @@ func TestHelpers(t *testing.T) { ssb.ExploreAll(ssb.ExploreRecursiveEdge()), )).Node() - fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + fetcherConfig := bsfetcher.NewFetcherConfig(wantsGetter) session := fetcherConfig.NewSession(context.Background()) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() results := []fetcher.FetchResult{} - err = fetcher.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel, func(res fetcher.FetchResult) error { + err = helpers.BlockMatching(ctx, session, cidlink.Link{Cid: block1.Cid()}, sel, func(res fetcher.FetchResult) error { results = append(results, res) return nil }) @@ -262,13 +263,13 @@ func TestHelpers(t *testing.T) { t.Run("BlockAllOfType retrieves all nodes with a schema", func(t *testing.T) { // limit recursion depth to 2 nodes and expect to get only 2 blocks (4 nodes) - fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + fetcherConfig := bsfetcher.NewFetcherConfig(wantsGetter) session := fetcherConfig.NewSession(context.Background()) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() results := []fetcher.FetchResult{} - err = fetcher.BlockAllOfType(ctx, session, cidlink.Link{Cid: block1.Cid()}, basicnode.Prototype__Any{}, func(res fetcher.FetchResult) error { + err = helpers.BlockAllOfType(ctx, session, cidlink.Link{Cid: block1.Cid()}, basicnode.Prototype__Any{}, func(res fetcher.FetchResult) error { results = append(results, res) return nil }) @@ -339,7 +340,7 @@ func TestNodeReification(t *testing.T) { defer wantsBlock.Exchange.Close() wantsGetter := blockservice.New(wantsBlock.Blockstore(), wantsBlock.Exchange) - fetcherConfig := fetcher.NewFetcherConfig(wantsGetter) + fetcherConfig := bsfetcher.NewFetcherConfig(wantsGetter) nodeReifier := func(lnkCtx ipld.LinkContext, nd ipld.Node, ls *ipld.LinkSystem) (ipld.Node, error) { return &selfLoader{Node: nd, ctx: lnkCtx.Ctx, ls: ls}, nil } @@ -348,7 +349,7 @@ func TestNodeReification(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - retrievedNode, err := fetcher.Block(ctx, session, cidlink.Link{Cid: block2.Cid()}) + retrievedNode, err := helpers.Block(ctx, session, cidlink.Link{Cid: block2.Cid()}) require.NoError(t, err) // instead of getting links back, we automatically load the nodes From 352b91e07d3c507ee43176cf19f243714deeb4da Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Fri, 9 Apr 2021 18:06:54 -0700 Subject: [PATCH 3350/3817] style(lint): fix lint errors This commit was moved from ipfs/go-fetcher@febb8de4105ad6876d8872f00466b0ba365910f3 --- fetcher/testutil/testutil.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fetcher/testutil/testutil.go b/fetcher/testutil/testutil.go index 87badbdfb..f67e6ca76 100644 --- a/fetcher/testutil/testutil.go +++ b/fetcher/testutil/testutil.go @@ -8,6 +8,8 @@ import ( blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" "github.com/ipld/go-ipld-prime" + + // used to make sure we have dagcbor encoding _ "github.com/ipld/go-ipld-prime/codec/dagcbor" cidlink "github.com/ipld/go-ipld-prime/linking/cid" ) @@ -16,7 +18,7 @@ import ( func EncodeBlock(n ipld.Node) (blocks.Block, ipld.Node, ipld.Link) { ls := cidlink.DefaultLinkSystem() var b blocks.Block - lb := cidlink.LinkPrototype{cid.Prefix{ + lb := cidlink.LinkPrototype{Prefix: cid.Prefix{ Version: 1, Codec: 0x71, MhType: 0x17, From b3aaf9c5b6fa42571a1dcc79c4be6481ac5f2ffd Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Fri, 9 Apr 2021 18:14:54 -0700 Subject: [PATCH 3351/3817] feat(fetcher): define factory interface define an interface for making new instances of the fetcher This commit was moved from ipfs/go-fetcher@571518e2eca7d61008d3e9c7707ef75aee20a432 --- fetcher/fetcher.go | 5 +++++ fetcher/impl/blockservice/fetcher.go | 3 +++ 2 files changed, 8 insertions(+) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 6fa0db0a9..87059396c 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -39,3 +39,8 @@ type FetchResult struct { // FetchCallback is called for each node traversed during a fetch type FetchCallback func(result FetchResult) error + +// Factory is anything that can create new sessions of the fetcher +type Factory interface { + NewSession(ctx context.Context) Fetcher +} diff --git a/fetcher/impl/blockservice/fetcher.go b/fetcher/impl/blockservice/fetcher.go index 92f5e5972..ffefcf69d 100644 --- a/fetcher/impl/blockservice/fetcher.go +++ b/fetcher/impl/blockservice/fetcher.go @@ -51,6 +51,9 @@ func (fc FetcherConfig) NewSession(ctx context.Context) fetcher.Fetcher { return &fetcherSession{linkSystem: ls, protoChooser: protoChooser} } +// interface check +var _ fetcher.Factory = FetcherConfig{} + // BlockOfType fetches a node graph of the provided type corresponding to single block by link. func (f *fetcherSession) BlockOfType(ctx context.Context, link ipld.Link, ptype ipld.NodePrototype) (ipld.Node, error) { return f.linkSystem.Load(ipld.LinkContext{}, link, ptype) From d921b23d26bf41872c7b7682592989d6df4f74d6 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Fri, 9 Apr 2021 19:54:15 -0700 Subject: [PATCH 3352/3817] style(fetcher): add param names update parameter names on interface to be more clear This commit was moved from ipfs/go-fetcher@5325cff258c02c11f7ce7107070a0bc89bdbb35a --- fetcher/fetcher.go | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 87059396c..f332d9f12 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -9,21 +9,26 @@ import ( // Fetcher is an interface for reading from a dag. Reads may be local or remote, and may employ data exchange // protocols like graphsync and bitswap type Fetcher interface { - // NodeMatching traverses a node graph starting with the provided node using the given selector and possibly crossing - // block boundaries. Each matched node is passed as FetchResult to the callback. Errors returned from callback will - // halt the traversal. The sequence of events is: NodeMatching begins, the callback is called zero or more times - // with a FetchResult, then NodeMatching returns. - NodeMatching(context.Context, ipld.Node, ipld.Node, FetchCallback) error + // NodeMatching traverses a node graph starting with the provided root node using the given selector node and + // possibly crossing block boundaries. Each matched node is passed as FetchResult to the callback. Errors returned + // from callback will halt the traversal. The sequence of events is: NodeMatching begins, the callback is called zero + // or more times with a FetchResult, then NodeMatching returns. + NodeMatching(ctx context.Context, root ipld.Node, selector ipld.Node, cb FetchCallback) error // BlockOfType fetches a node graph of the provided type corresponding to single block by link. - BlockOfType(context.Context, ipld.Link, ipld.NodePrototype) (ipld.Node, error) + BlockOfType(ctx context.Context, link ipld.Link, nodePrototype ipld.NodePrototype) (ipld.Node, error) - // BlockMatchingOfType traverses a node graph starting with the given link using the given selector and possibly - // crossing block boundaries. The nodes will be typed using the provided prototype. Each matched node is passed as - // a FetchResult to the callback. Errors returned from callback will halt the traversal. + // BlockMatchingOfType traverses a node graph starting with the given root link using the given selector node and + // possibly crossing block boundaries. The nodes will be typed using the provided prototype. Each matched node is + // passed as a FetchResult to the callback. Errors returned from callback will halt the traversal. // The sequence of events is: BlockMatchingOfType begins, the callback is called zero or more times with a // FetchResult, then BlockMatchingOfType returns. - BlockMatchingOfType(context.Context, ipld.Link, ipld.Node, ipld.NodePrototype, FetchCallback) error + BlockMatchingOfType( + ctx context.Context, + root ipld.Link, + selector ipld.Node, + nodePrototype ipld.NodePrototype, + cb FetchCallback) error // Uses the given link to pick a prototype to build the linked node. PrototypeFromLink(link ipld.Link) (ipld.NodePrototype, error) From 44a211ab798906ca7dd0752ab30c789f986caeab Mon Sep 17 00:00:00 2001 From: Cory Schwartz Date: Wed, 14 Apr 2021 22:33:40 -0700 Subject: [PATCH 3353/3817] staticcheck This commit was moved from ipfs/go-merkledag@eb044326f1f3257103fc9b6a82f229ec58cc1a1f --- ipld/merkledag/merkledag.go | 4 ++-- ipld/merkledag/merkledag_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index a1bbf9711..76f402bea 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -137,10 +137,10 @@ func (sg *sesGetter) Get(ctx context.Context, c cid.Cid) (ipld.Node, error) { switch err { case bserv.ErrNotFound: return nil, ipld.ErrNotFound - default: - return nil, err case nil: // noop + default: + return nil, err } return ipld.Decode(blk) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 83af3f02c..3ff6c3f09 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -156,7 +156,7 @@ func SubtestNodeStat(t *testing.T, n *ProtoNode) { type devZero struct{} -func (_ devZero) Read(b []byte) (int, error) { +func (devZero) Read(b []byte) (int, error) { for i := range b { b[i] = 0 } From 50e049b7c01363ba6e9c6e0196ae186b238ac58d Mon Sep 17 00:00:00 2001 From: Cory Schwartz Date: Thu, 15 Apr 2021 23:47:52 -0700 Subject: [PATCH 3354/3817] io/dagreader.go This commit was moved from ipfs/go-unixfs@77ff92dc786d343a8c1d2a614ac734c40df3102b --- unixfs/io/dagreader.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 374b50916..9fb37afff 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -56,9 +56,27 @@ func NewDagReader(ctx context.Context, n ipld.Node, serv ipld.NodeGetter) (DagRe } switch fsNode.Type() { - case unixfs.TFile, unixfs.TRaw: + case unixfs.TFile: size = fsNode.FileSize() + case unixfs.TRaw: + stat, err := n.Stat() + if err != nil { + return nil, err + } + size = uint64(stat.DataSize) + for _, link := range n.Links() { + ln, err := link.GetNode(ctx, serv) + if err != nil { + return nil, err + } + stat, err := ln.Stat() + if err != nil { + return nil, err + } + size += uint64(stat.DataSize) + } + case unixfs.TDirectory, unixfs.THAMTShard: // Dont allow reading directories return nil, ErrIsDir From 7e3eb2d3d0276c11c961f16d7210fb708ec390c6 Mon Sep 17 00:00:00 2001 From: Cory Schwartz Date: Fri, 16 Apr 2021 15:29:59 -0700 Subject: [PATCH 3355/3817] Fix in size func This commit was moved from ipfs/go-unixfs@7e96bad956c88140fe130b2ad2129c486e546408 --- unixfs/io/dagreader.go | 20 +------------------- unixfs/unixfs.go | 4 ++-- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/unixfs/io/dagreader.go b/unixfs/io/dagreader.go index 9fb37afff..374b50916 100644 --- a/unixfs/io/dagreader.go +++ b/unixfs/io/dagreader.go @@ -56,27 +56,9 @@ func NewDagReader(ctx context.Context, n ipld.Node, serv ipld.NodeGetter) (DagRe } switch fsNode.Type() { - case unixfs.TFile: + case unixfs.TFile, unixfs.TRaw: size = fsNode.FileSize() - case unixfs.TRaw: - stat, err := n.Stat() - if err != nil { - return nil, err - } - size = uint64(stat.DataSize) - for _, link := range n.Links() { - ln, err := link.GetNode(ctx, serv) - if err != nil { - return nil, err - } - stat, err := ln.Stat() - if err != nil { - return nil, err - } - size += uint64(stat.DataSize) - } - case unixfs.TDirectory, unixfs.THAMTShard: // Dont allow reading directories return nil, ErrIsDir diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index 05abf6576..555d24efc 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -159,9 +159,9 @@ func size(pbdata *pb.Data) (uint64, error) { switch pbdata.GetType() { case pb.Data_Directory, pb.Data_HAMTShard: return 0, errors.New("can't get data size of directory") - case pb.Data_File: + case pb.Data_File, pb.Data_Raw: return pbdata.GetFilesize(), nil - case pb.Data_Symlink, pb.Data_Raw: + case pb.Data_Symlink: return uint64(len(pbdata.GetData())), nil default: return 0, errors.New("unrecognized node data type") From c16575e2e2f5462d765104b23411a96555e5d213 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Mon, 19 Apr 2021 13:00:02 -0700 Subject: [PATCH 3356/3817] fix(blockservice): remove ref to dag pb This commit was moved from ipfs/go-fetcher@6e0ef2aeedf1526048c988bafd982ab87a18d4f6 --- fetcher/impl/blockservice/fetcher.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fetcher/impl/blockservice/fetcher.go b/fetcher/impl/blockservice/fetcher.go index ffefcf69d..0a0244d8b 100644 --- a/fetcher/impl/blockservice/fetcher.go +++ b/fetcher/impl/blockservice/fetcher.go @@ -8,7 +8,6 @@ import ( "github.com/ipfs/go-blockservice" "github.com/ipfs/go-fetcher" - dagpb "github.com/ipld/go-codec-dagpb" "github.com/ipld/go-ipld-prime" cidlink "github.com/ipld/go-ipld-prime/linking/cid" basicnode "github.com/ipld/go-ipld-prime/node/basic" @@ -106,12 +105,12 @@ func (f *fetcherSession) PrototypeFromLink(lnk ipld.Link) (ipld.NodePrototype, e } // DefaultPrototypeChooser supports DagPB nodes and choosing the prototype from the link. -var DefaultPrototypeChooser = dagpb.AddSupportToChooser(func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { +var DefaultPrototypeChooser = func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { return tlnkNd.LinkTargetNodePrototype(), nil } return basicnode.Prototype.Any, nil -}) +} func blockOpener(ctx context.Context, bs *blockservice.Session) ipld.BlockReadOpener { return func(_ ipld.LinkContext, lnk ipld.Link) (io.Reader, error) { From 5cfe6154a526a28419239eb983b749ca26195728 Mon Sep 17 00:00:00 2001 From: Andrew Gillis Date: Wed, 21 Apr 2021 12:58:44 -0700 Subject: [PATCH 3357/3817] Properly report DNSLink errors (#12) * Properly report DNSLink errors Only report that there is no DNSLink for a name when there are no DNSLink TXT records available for that name. For all other errors, such as being offline, report the more general "cannot resolve name" error. * Document that we give precedence to good results from looking up DNSLinks in TXT records from `"_dnslink."+fqdn` over results from `fqdn`. This commit was moved from ipfs/go-namesys@22432d192e27fd822f08595a0b121da092218fd0 --- namesys/base.go | 9 --------- namesys/dns.go | 29 ++++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/namesys/base.go b/namesys/base.go index a463e48f1..27cc38f88 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -2,7 +2,6 @@ package namesys import ( "context" - "fmt" "strings" "time" @@ -37,14 +36,6 @@ func resolve(ctx context.Context, r resolver, name string, options opts.ResolveO } } - if err == ErrResolveFailed { - i := len(name) - 1 - for i >= 0 && name[i] != '/' { - i-- - } - // Wrap error so that it can be tested if it is a ErrResolveFailed - err = fmt.Errorf("%w: %q is missing a DNSLink record (https://docs.ipfs.io/concepts/dnslink/)", ErrResolveFailed, name[i+1:]) - } return p, err } diff --git a/namesys/dns.go b/namesys/dns.go index 9938aa8dd..74fc1093e 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "net" + gpath "path" "strings" path "github.com/ipfs/go-path" @@ -88,6 +89,7 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options go func() { defer close(out) + var rootResErr, subResErr error for { select { case subRes, ok := <-subChan: @@ -98,8 +100,11 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options if subRes.error == nil { p, err := appendPath(subRes.path) emitOnceResult(ctx, out, onceResult{value: p, err: err}) + // Return without waiting for rootRes, since this result + // (for "_dnslink."+fqdn) takes precedence return } + subResErr = subRes.error case rootRes, ok := <-rootChan: if !ok { rootChan = nil @@ -108,11 +113,24 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options if rootRes.error == nil { p, err := appendPath(rootRes.path) emitOnceResult(ctx, out, onceResult{value: p, err: err}) + // Do not return here. Wait for subRes so that it is + // output last if good, thereby giving subRes precedence. + } else { + rootResErr = rootRes.error } case <-ctx.Done(): return } if subChan == nil && rootChan == nil { + // If here, then both lookups are done + // + // If both lookups failed due to no TXT records with a + // dnslink, then output a more specific error message + if rootResErr == ErrResolveFailed && subResErr == ErrResolveFailed { + // Wrap error so that it can be tested if it is a ErrResolveFailed + err := fmt.Errorf("%w: %q is missing a DNSLink record (https://docs.ipfs.io/concepts/dnslink/)", ErrResolveFailed, gpath.Base(name)) + emitOnceResult(ctx, out, onceResult{err: err}) + } return } } @@ -126,7 +144,14 @@ func workDomain(r *DNSResolver, name string, res chan lookupRes) { txt, err := r.lookupTXT(name) if err != nil { - // Error is != nil + if dnsErr, ok := err.(*net.DNSError); ok { + // If no TXT records found, return same error as when no text + // records contain dnslink. Otherwise, return the actual error. + if dnsErr.IsNotFound { + err = ErrResolveFailed + } + } + // Could not look up any text records for name res <- lookupRes{"", err} return } @@ -138,6 +163,8 @@ func workDomain(r *DNSResolver, name string, res chan lookupRes) { return } } + + // There were no TXT records with a dnslink res <- lookupRes{"", ErrResolveFailed} } From 56487887b4839f02317d03228b03f851b127f7be Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 11 Mar 2021 18:15:57 -0800 Subject: [PATCH 3358/3817] feat(car): update for ipld linksystem updates to be compatible with ipld linksystem This commit was moved from ipld/go-car@adf2cca1ad46d409e5d6bc3312d380c752287547 --- ipld/car/selectivecar.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index f612fd4bd..50a955206 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -70,7 +70,9 @@ func NewSelectiveCar(ctx context.Context, store ReadStore, dags []Dag) Selective } func (sc SelectiveCar) traverse(onCarHeader OnCarHeaderFunc, onNewCarBlock OnNewCarBlockFunc) (uint64, error) { - traverser := &selectiveCarTraverser{onCarHeader, onNewCarBlock, 0, cid.NewSet(), sc} + + traverser := &selectiveCarTraverser{onCarHeader, onNewCarBlock, 0, cid.NewSet(), sc, cidlink.DefaultLinkSystem()} + traverser.lsys.StorageReadOpener = traverser.loader return traverser.traverse() } @@ -177,6 +179,7 @@ type selectiveCarTraverser struct { offset uint64 cidSet *cid.Set sc SelectiveCar + lsys ipld.LinkSystem } func (sct *selectiveCarTraverser) traverse() (uint64, error) { @@ -212,7 +215,7 @@ func (sct *selectiveCarTraverser) traverseHeader() error { return sct.onCarHeader(header) } -func (sct *selectiveCarTraverser) loader(lnk ipld.Link, ctx ipld.LinkContext) (io.Reader, error) { +func (sct *selectiveCarTraverser) loader(ctx ipld.LinkContext, lnk ipld.Link) (io.Reader, error) { cl, ok := lnk.(cidlink.Link) if !ok { return nil, errors.New("incorrect link type") @@ -257,16 +260,14 @@ func (sct *selectiveCarTraverser) traverseBlocks() error { } lnk := cidlink.Link{Cid: carDag.Root} ns, _ := nsc(lnk, ipld.LinkContext{}) // nsc won't error - nb := ns.NewBuilder() - err = lnk.Load(sct.sc.ctx, ipld.LinkContext{}, nb, sct.loader) + nd, err := sct.lsys.Load(ipld.LinkContext{Ctx: sct.sc.ctx}, lnk, ns) if err != nil { return err } - nd := nb.Build() err = traversal.Progress{ Cfg: &traversal.Config{ Ctx: sct.sc.ctx, - LinkLoader: sct.loader, + LinkSystem: sct.lsys, LinkTargetNodePrototypeChooser: nsc, }, }.WalkAdv(nd, parsed, func(traversal.Progress, ipld.Node, traversal.VisitReason) error { return nil }) From f7083da82fa24cd297134ab6499701111ed38b28 Mon Sep 17 00:00:00 2001 From: web3-bot Date: Fri, 23 Apr 2021 04:06:26 +0000 Subject: [PATCH 3359/3817] run gofmt -s This commit was moved from ipfs/go-path@32d3a4f5fe76ab132554b62327cc657b332335e4 --- path/path_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/path/path_test.go b/path/path_test.go index 4552fc5f9..42cacddf1 100644 --- a/path/path_test.go +++ b/path/path_test.go @@ -76,12 +76,12 @@ func TestIsJustAKey(t *testing.T) { func TestPopLastSegment(t *testing.T) { cases := map[string][]string{ - "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": []string{"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n", ""}, - "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": []string{"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n", ""}, - "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": []string{"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n", "a"}, - "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b": []string{"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a", "b"}, - "/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y/z": []string{"/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y", "z"}, - "/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y/z": []string{"/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y", "z"}, + "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": {"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n", ""}, + "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": {"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n", ""}, + "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a": {"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n", "a"}, + "/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a/b": {"/ipfs/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/a", "b"}, + "/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y/z": {"/ipns/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y", "z"}, + "/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y/z": {"/ipld/QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n/x/y", "z"}, } for p, expected := range cases { From 4f151169e67a07d3574dbe0e7bd185a2a170733b Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 12 Apr 2021 12:11:39 +0300 Subject: [PATCH 3360/3817] make DNS resolver pluggable, use new madns.BasicResolver interface This commit was moved from ipfs/go-namesys@a718e16e7538e9a132152dd88084bbe979108983 --- namesys/dns.go | 15 ++++++++------- namesys/dns_test.go | 3 ++- namesys/namesys.go | 5 +++-- namesys/namesys_test.go | 5 +++-- namesys/republisher/repub_test.go | 6 ++++-- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 74fc1093e..511b373b0 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -11,13 +11,14 @@ import ( path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" isd "github.com/jbenet/go-is-domain" + madns "github.com/multiformats/go-multiaddr-dns" ) const ethTLD = "eth" const linkTLD = "domains" // LookupTXTFunc is a generic type for a function that lookups TXT record values. -type LookupTXTFunc func(name string) (txt []string, err error) +type LookupTXTFunc func(ctx context.Context, name string) (txt []string, err error) // DNSResolver implements a Resolver on DNS domains type DNSResolver struct { @@ -27,8 +28,8 @@ type DNSResolver struct { } // NewDNSResolver constructs a name resolver using DNS TXT records. -func NewDNSResolver() *DNSResolver { - return &DNSResolver{lookupTXT: net.LookupTXT} +func NewDNSResolver(rslv madns.BasicResolver) *DNSResolver { + return &DNSResolver{lookupTXT: rslv.LookupTXT} } // Resolve implements Resolver. @@ -75,10 +76,10 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options } rootChan := make(chan lookupRes, 1) - go workDomain(r, fqdn, rootChan) + go workDomain(ctx, r, fqdn, rootChan) subChan := make(chan lookupRes, 1) - go workDomain(r, "_dnslink."+fqdn, subChan) + go workDomain(ctx, r, "_dnslink."+fqdn, subChan) appendPath := func(p path.Path) (path.Path, error) { if len(segments) > 1 { @@ -139,10 +140,10 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options return out } -func workDomain(r *DNSResolver, name string, res chan lookupRes) { +func workDomain(ctx context.Context, r *DNSResolver, name string, res chan lookupRes) { defer close(res) - txt, err := r.lookupTXT(name) + txt, err := r.lookupTXT(ctx, name) if err != nil { if dnsErr, ok := err.(*net.DNSError); ok { // If no TXT records found, return same error as when no text diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 877b81464..66f10e763 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -1,6 +1,7 @@ package namesys import ( + "context" "fmt" "testing" @@ -11,7 +12,7 @@ type mockDNS struct { entries map[string][]string } -func (m *mockDNS) lookupTXT(name string) (txt []string, err error) { +func (m *mockDNS) lookupTXT(ctx context.Context, name string) (txt []string, err error) { txt, ok := m.entries[name] if !ok { return nil, fmt.Errorf("no TXT entry for %s", name) diff --git a/namesys/namesys.go b/namesys/namesys.go index ae77771d7..b1649f684 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -29,6 +29,7 @@ import ( ci "github.com/libp2p/go-libp2p-core/crypto" peer "github.com/libp2p/go-libp2p-core/peer" routing "github.com/libp2p/go-libp2p-core/routing" + madns "github.com/multiformats/go-multiaddr-dns" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. @@ -49,7 +50,7 @@ type mpns struct { } // NewNameSystem will construct the IPFS naming system based on Routing -func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSystem { +func NewNameSystem(r routing.ValueStore, ds ds.Datastore, rslv madns.BasicResolver, cachesize int) NameSystem { var ( cache *lru.Cache staticMap map[string]path.Path @@ -73,7 +74,7 @@ func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSys } return &mpns{ - dnsResolver: NewDNSResolver(), + dnsResolver: NewDNSResolver(rslv), proquintResolver: new(ProquintResolver), ipnsResolver: NewIpnsResolver(r), ipnsPublisher: NewIpnsPublisher(r, ds), diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 30674106b..02068be32 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -17,6 +17,7 @@ import ( peer "github.com/libp2p/go-libp2p-core/peer" pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem" record "github.com/libp2p/go-libp2p-record" + madns "github.com/multiformats/go-multiaddr-dns" ) type mockResolver struct { @@ -109,7 +110,7 @@ func TestPublishWithCache0(t *testing.T) { "pk": record.PublicKeyValidator{}, }) - nsys := NewNameSystem(routing, dst, 0) + nsys := NewNameSystem(routing, dst, madns.DefaultResolver, 0) // CID is arbitrary. p, err := path.ParsePath("QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") if err != nil { @@ -142,7 +143,7 @@ func TestPublishWithTTL(t *testing.T) { "pk": record.PublicKeyValidator{}, }) - nsys := NewNameSystem(routing, dst, 128) + nsys := NewNameSystem(routing, dst, madns.DefaultResolver, 128) // CID is arbitrary. p, err := path.ParsePath("QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") if err != nil { diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 0d1635aad..985c7169f 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -22,6 +22,8 @@ import ( ipns_pb "github.com/ipfs/go-ipns/pb" path "github.com/ipfs/go-path" + madns "github.com/multiformats/go-multiaddr-dns" + keystore "github.com/ipfs/go-ipfs-keystore" namesys "github.com/ipfs/go-namesys" . "github.com/ipfs/go-namesys/republisher" @@ -74,7 +76,7 @@ func TestRepublish(t *testing.T) { var nodes []*mockNode for i := 0; i < 10; i++ { n := getMockNode(t, ctx) - ns := namesys.NewNameSystem(n.dht, n.store, 0) + ns := namesys.NewNameSystem(n.dht, n.store, madns.DefaultResolver, 0) nsystems = append(nsystems, ns) nodes = append(nodes, n) @@ -153,7 +155,7 @@ func TestLongEOLRepublish(t *testing.T) { var nodes []*mockNode for i := 0; i < 10; i++ { n := getMockNode(t, ctx) - ns := namesys.NewNameSystem(n.dht, n.store, 0) + ns := namesys.NewNameSystem(n.dht, n.store, madns.DefaultResolver, 0) nsystems = append(nsystems, ns) nodes = append(nodes, n) From cdae7adaa5792117c626aa8bdaa1b7fc4bdb13ba Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 12 Apr 2021 13:28:32 +0300 Subject: [PATCH 3361/3817] parameterize DNSResolver on the lookup TXT function This commit was moved from ipfs/go-namesys@1077b5af95a778de07171d59075b593a1af7f71f --- namesys/dns.go | 5 ++--- namesys/namesys.go | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 511b373b0..9b7f45bf8 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -11,7 +11,6 @@ import ( path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" isd "github.com/jbenet/go-is-domain" - madns "github.com/multiformats/go-multiaddr-dns" ) const ethTLD = "eth" @@ -28,8 +27,8 @@ type DNSResolver struct { } // NewDNSResolver constructs a name resolver using DNS TXT records. -func NewDNSResolver(rslv madns.BasicResolver) *DNSResolver { - return &DNSResolver{lookupTXT: rslv.LookupTXT} +func NewDNSResolver(lookup LookupTXTFunc) *DNSResolver { + return &DNSResolver{lookupTXT: lookup} } // Resolve implements Resolver. diff --git a/namesys/namesys.go b/namesys/namesys.go index b1649f684..a5f930467 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -74,7 +74,7 @@ func NewNameSystem(r routing.ValueStore, ds ds.Datastore, rslv madns.BasicResolv } return &mpns{ - dnsResolver: NewDNSResolver(rslv), + dnsResolver: NewDNSResolver(rslv.LookupTXT), proquintResolver: new(ProquintResolver), ipnsResolver: NewIpnsResolver(r), ipnsPublisher: NewIpnsPublisher(r, ds), From 594c293e56fba08a8e375d9a238cbca02c771835 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 16 Apr 2021 00:28:32 +0300 Subject: [PATCH 3362/3817] introduce functional options for NewNamesys constructor This commit was moved from ipfs/go-namesys@ea4eec1d06a8962f58c00eb0e4704a4256276936 --- namesys/namesys.go | 79 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 15 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index a5f930467..85075d228 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -23,6 +23,7 @@ import ( lru "github.com/hashicorp/golang-lru" cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" isd "github.com/jbenet/go-is-domain" @@ -42,6 +43,8 @@ import ( // It can only publish to: (a) IPFS routing naming. // type mpns struct { + ds ds.Datastore + dnsResolver, proquintResolver, ipnsResolver resolver ipnsPublisher Publisher @@ -49,15 +52,45 @@ type mpns struct { cache *lru.Cache } -// NewNameSystem will construct the IPFS naming system based on Routing -func NewNameSystem(r routing.ValueStore, ds ds.Datastore, rslv madns.BasicResolver, cachesize int) NameSystem { - var ( - cache *lru.Cache - staticMap map[string]path.Path - ) - if cachesize > 0 { - cache, _ = lru.New(cachesize) +type Option func(*mpns) error + +// WithCache is an option that instructs the name system to use a (LRU) cache of the given size. +func WithCache(size int) Option { + return func(ns *mpns) error { + if size <= 0 { + return fmt.Errorf("invalid cache size %d; must be > 0", size) + } + + cache, err := lru.New(size) + if err != nil { + return err + } + + ns.cache = cache + return nil } +} + +// WithDNSResolver is an option that supplies a custom DNS resolver to use instead of the system +// default. +func WithDNSResolver(rslv madns.BasicResolver) Option { + return func(ns *mpns) error { + ns.dnsResolver = NewDNSResolver(rslv.LookupTXT) + return nil + } +} + +// WithDatastore is an option that supplies a datastore to use instead of an in-memory map datastore. +func WithDatastore(ds ds.Datastore) Option { + return func(ns *mpns) error { + ns.ds = ds + return nil + } +} + +// NewNameSystem will construct the IPFS naming system based on Routing +func NewNameSystem(r routing.ValueStore, opts ...Option) (NameSystem, error) { + var staticMap map[string]path.Path // Prewarm namesys cache with static records for deterministic tests and debugging. // Useful for testing things like DNSLink without real DNS lookup. @@ -73,14 +106,30 @@ func NewNameSystem(r routing.ValueStore, ds ds.Datastore, rslv madns.BasicResolv } } - return &mpns{ - dnsResolver: NewDNSResolver(rslv.LookupTXT), - proquintResolver: new(ProquintResolver), - ipnsResolver: NewIpnsResolver(r), - ipnsPublisher: NewIpnsPublisher(r, ds), - staticMap: staticMap, - cache: cache, + ns := &mpns{ + staticMap: staticMap, + } + + for _, opt := range opts { + err := opt(ns) + if err != nil { + return nil, err + } + } + + if ns.ds == nil { + ns.ds = dssync.MutexWrap(ds.NewMapDatastore()) + } + + if ns.dnsResolver == nil { + ns.dnsResolver = NewDNSResolver(madns.DefaultResolver.LookupTXT) } + + ns.proquintResolver = new(ProquintResolver) + ns.ipnsResolver = NewIpnsResolver(r) + ns.ipnsPublisher = NewIpnsPublisher(r, ns.ds) + + return ns, nil } // DefaultResolverCacheTTL defines max ttl of a record placed in namesys cache. From a30368da785e805a07459c481e1016ca922050ae Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 16 Apr 2021 00:28:41 +0300 Subject: [PATCH 3363/3817] fix tests This commit was moved from ipfs/go-namesys@a360c661079483df7356fdeee04d0128b52da0fa --- namesys/namesys_test.go | 13 ++++++++++--- namesys/republisher/repub_test.go | 12 ++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 02068be32..6ae94a6cf 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -17,7 +17,6 @@ import ( peer "github.com/libp2p/go-libp2p-core/peer" pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem" record "github.com/libp2p/go-libp2p-record" - madns "github.com/multiformats/go-multiaddr-dns" ) type mockResolver struct { @@ -110,7 +109,11 @@ func TestPublishWithCache0(t *testing.T) { "pk": record.PublicKeyValidator{}, }) - nsys := NewNameSystem(routing, dst, madns.DefaultResolver, 0) + nsys, err := NewNameSystem(routing, WithDatastore(dst)) + if err != nil { + t.Fatal(err) + } + // CID is arbitrary. p, err := path.ParsePath("QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") if err != nil { @@ -143,7 +146,11 @@ func TestPublishWithTTL(t *testing.T) { "pk": record.PublicKeyValidator{}, }) - nsys := NewNameSystem(routing, dst, madns.DefaultResolver, 128) + nsys, err := NewNameSystem(routing, WithDatastore(dst), WithCache(128)) + if err != nil { + t.Fatal(err) + } + // CID is arbitrary. p, err := path.ParsePath("QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") if err != nil { diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 985c7169f..3775b188a 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -22,8 +22,6 @@ import ( ipns_pb "github.com/ipfs/go-ipns/pb" path "github.com/ipfs/go-path" - madns "github.com/multiformats/go-multiaddr-dns" - keystore "github.com/ipfs/go-ipfs-keystore" namesys "github.com/ipfs/go-namesys" . "github.com/ipfs/go-namesys/republisher" @@ -76,7 +74,10 @@ func TestRepublish(t *testing.T) { var nodes []*mockNode for i := 0; i < 10; i++ { n := getMockNode(t, ctx) - ns := namesys.NewNameSystem(n.dht, n.store, madns.DefaultResolver, 0) + ns, err := namesys.NewNameSystem(n.dht, namesys.WithDatastore(n.store)) + if err != nil { + t.Fatal(err) + } nsystems = append(nsystems, ns) nodes = append(nodes, n) @@ -155,7 +156,10 @@ func TestLongEOLRepublish(t *testing.T) { var nodes []*mockNode for i := 0; i < 10; i++ { n := getMockNode(t, ctx) - ns := namesys.NewNameSystem(n.dht, n.store, madns.DefaultResolver, 0) + ns, err := namesys.NewNameSystem(n.dht, namesys.WithDatastore(n.store)) + if err != nil { + t.Fatal(err) + } nsystems = append(nsystems, ns) nodes = append(nodes, n) From 69a6288b81ac0ef5c9d487615abb2af748451976 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 16 Apr 2021 00:52:49 +0300 Subject: [PATCH 3364/3817] remove special casing of .eth domains This commit was moved from ipfs/go-namesys@c91aa69e9d10af88be194de773ee8cef334ac062 --- namesys/dns.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 9b7f45bf8..96b9a6b25 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -13,9 +13,6 @@ import ( isd "github.com/jbenet/go-is-domain" ) -const ethTLD = "eth" -const linkTLD = "domains" - // LookupTXTFunc is a generic type for a function that lookups TXT record values. type LookupTXTFunc func(ctx context.Context, name string) (txt []string, err error) @@ -68,12 +65,6 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options fqdn = domain + "." } - if strings.HasSuffix(fqdn, "."+ethTLD+".") { - // This is an ENS name. As we're resolving via an arbitrary DNS server - // that may not know about .eth we need to add our link domain suffix. - fqdn += linkTLD + "." - } - rootChan := make(chan lookupRes, 1) go workDomain(ctx, r, fqdn, rootChan) From 246dfc7f244e1bee489db846f165240861e01f09 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 16 Apr 2021 00:52:55 +0300 Subject: [PATCH 3365/3817] fix test This commit was moved from ipfs/go-namesys@16c89a5712d67ad02a811f58319f7db50c02a801 --- namesys/dns_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 66f10e763..1cb75b62d 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -127,7 +127,7 @@ func newMockDNS() *mockDNS { "fqdn.example.com.": { "dnslink=/ipfs/QmYvMB9yrsSf7RKBghkfwmHJkzJhW2ZgVwq3LxBXXPasFr", }, - "www.wealdtech.eth.domains.": { + "www.wealdtech.eth.": { "dnslink=/ipns/ipfs.example.com", }, }, @@ -169,5 +169,5 @@ func TestDNSResolution(t *testing.T) { testResolution(t, r, "fqdn.example.com.", opts.DefaultDepthLimit, "/ipfs/QmYvMB9yrsSf7RKBghkfwmHJkzJhW2ZgVwq3LxBXXPasFr", nil) testResolution(t, r, "www.wealdtech.eth", 1, "/ipns/ipfs.example.com", ErrResolveRecursion) testResolution(t, r, "www.wealdtech.eth", 2, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) - testResolution(t, r, "www.wealdtech.eth.domains", 2, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "www.wealdtech.eth", 2, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) } From a5c21d5c133f73400c412dbb6c08fb8f039ec291 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 21 Apr 2021 19:42:03 +0200 Subject: [PATCH 3366/3817] feat: support non-ICANN DNSLink names https://github.com/ipfs/go-ipfs/issues/8060 This commit was moved from ipfs/go-namesys@1f2af4e5527ae625c797bad31f2528c4df44d092 --- namesys/dns.go | 4 ++-- namesys/namesys.go | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/namesys/dns.go b/namesys/dns.go index 96b9a6b25..43768804f 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -10,7 +10,7 @@ import ( path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" - isd "github.com/jbenet/go-is-domain" + dns "github.com/miekg/dns" ) // LookupTXTFunc is a generic type for a function that lookups TXT record values. @@ -52,7 +52,7 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options segments := strings.SplitN(name, "/", 2) domain := segments[0] - if !isd.IsDomain(domain) { + if _, ok := dns.IsDomainName(domain); !ok { out <- onceResult{err: fmt.Errorf("not a valid domain name: %s", domain)} close(out) return out diff --git a/namesys/namesys.go b/namesys/namesys.go index 85075d228..b28c13309 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -7,8 +7,8 @@ // DHT). // // Additionally, the /ipns/ namespace can also be used with domain names that -// use DNSLink (/ipns/my.domain.example, see https://dnslink.io) and proquint -// strings. +// use DNSLink (/ipns/, https://docs.ipfs.io/concepts/dnslink/) +// and proquint strings. // // The package provides implementations for all three resolvers. package namesys @@ -26,10 +26,10 @@ import ( dssync "github.com/ipfs/go-datastore/sync" path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" - isd "github.com/jbenet/go-is-domain" ci "github.com/libp2p/go-libp2p-core/crypto" peer "github.com/libp2p/go-libp2p-core/peer" routing "github.com/libp2p/go-libp2p-core/routing" + dns "github.com/miekg/dns" madns "github.com/multiformats/go-multiaddr-dns" ) @@ -225,9 +225,13 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. if err == nil { res = ns.ipnsResolver - } else if isd.IsDomain(key) { + } else if _, ok := dns.IsDomainName(key); ok { res = ns.dnsResolver } else { + // TODO: remove proquint? + // dns.IsDomainName(key) will return true for proquint strings, + // so this block is a dead code. + // (alternative is to move this before DNS check) res = ns.proquintResolver } From 6f288d9cbd6e1834526f85461db979977ad00759 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 21 Apr 2021 21:13:51 +0200 Subject: [PATCH 3367/3817] test: non-ICANN DNS names This commit was moved from ipfs/go-namesys@2ae3baed70b7704521469e83157911555765cb35 --- namesys/dns_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 1cb75b62d..cde077e47 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -127,6 +127,15 @@ func newMockDNS() *mockDNS { "fqdn.example.com.": { "dnslink=/ipfs/QmYvMB9yrsSf7RKBghkfwmHJkzJhW2ZgVwq3LxBXXPasFr", }, + "en.wikipedia-on-ipfs.org.": { + "dnslink=/ipfs/bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze", + }, + "custom.non-icann.tldextravaganza.": { + "dnslink=/ipfs/bafybeieto6mcuvqlechv4iadoqvnffondeiwxc2bcfcewhvpsd2odvbmvm", + }, + "singlednslabelshouldbeok.": { + "dnslink=/ipfs/bafybeih4a6ylafdki6ailjrdvmr7o4fbbeceeeuty4v3qyyouiz5koqlpi", + }, "www.wealdtech.eth.": { "dnslink=/ipns/ipfs.example.com", }, @@ -167,6 +176,9 @@ func TestDNSResolution(t *testing.T) { testResolution(t, r, "double.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) testResolution(t, r, "conflict.example.com", opts.DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjE", nil) testResolution(t, r, "fqdn.example.com.", opts.DefaultDepthLimit, "/ipfs/QmYvMB9yrsSf7RKBghkfwmHJkzJhW2ZgVwq3LxBXXPasFr", nil) + testResolution(t, r, "en.wikipedia-on-ipfs.org", 2, "/ipfs/bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze", nil) + testResolution(t, r, "custom.non-icann.tldextravaganza.", 2, "/ipfs/bafybeieto6mcuvqlechv4iadoqvnffondeiwxc2bcfcewhvpsd2odvbmvm", nil) + testResolution(t, r, "singlednslabelshouldbeok", 2, "/ipfs/bafybeih4a6ylafdki6ailjrdvmr7o4fbbeceeeuty4v3qyyouiz5koqlpi", nil) testResolution(t, r, "www.wealdtech.eth", 1, "/ipns/ipfs.example.com", ErrResolveRecursion) testResolution(t, r, "www.wealdtech.eth", 2, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) testResolution(t, r, "www.wealdtech.eth", 2, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) From b5662a5cb42965efdf7181db027c2876773b6cd5 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 21 Apr 2021 22:30:32 +0200 Subject: [PATCH 3368/3817] refactor: remove proquint.go See discussion in: https://github.com/ipfs/go-namesys/pull/13#pullrequestreview-641398404 This commit was moved from ipfs/go-namesys@aa54bc9e2df08848ace68fcb1914eb850ee4fb8a --- namesys/README.md | 4 ++-- namesys/namesys.go | 16 +++++----------- namesys/proquint.go | 34 ---------------------------------- 3 files changed, 7 insertions(+), 47 deletions(-) delete mode 100644 namesys/proquint.go diff --git a/namesys/README.md b/namesys/README.md index 5c17728da..78060ca03 100644 --- a/namesys/README.md +++ b/namesys/README.md @@ -10,9 +10,9 @@ Package namesys defines `Resolver` and `Publisher` interfaces for IPNS paths, that is, paths in the form of `/ipns/`. A "resolved" IPNS path becomes an `/ipfs/` path. -Traditionally, these paths would be in the form of `/ipns/peer_id`, which references an IPNS record in a distributed `ValueStore` (usually the IPFS DHT). +Traditionally, these paths would be in the form of `/ipns/{libp2p-key}`, which references an IPNS record in a distributed `ValueStore` (usually the IPFS DHT). -Additionally, the /ipns/ namespace can also be used with domain names that use DNSLink (/ipns/my.domain.example, see https://dnslink.io) and proquint strings. +Additionally, the `/ipns/` namespace can also be used with domain names that use DNSLink (`/ipns/en.wikipedia-on-ipfs.org`, see https://docs.ipfs.io/concepts/dnslink/). The package provides implementations for all three resolvers. diff --git a/namesys/namesys.go b/namesys/namesys.go index b28c13309..f1c1d22c2 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -8,7 +8,6 @@ // // Additionally, the /ipns/ namespace can also be used with domain names that // use DNSLink (/ipns/, https://docs.ipfs.io/concepts/dnslink/) -// and proquint strings. // // The package provides implementations for all three resolvers. package namesys @@ -38,15 +37,14 @@ import ( // Uses several Resolvers: // (a) IPFS routing naming: SFS-like PKI names. // (b) dns domains: resolves using links in DNS TXT records -// (c) proquints: interprets string as the raw byte data. // // It can only publish to: (a) IPFS routing naming. // type mpns struct { ds ds.Datastore - dnsResolver, proquintResolver, ipnsResolver resolver - ipnsPublisher Publisher + dnsResolver, ipnsResolver resolver + ipnsPublisher Publisher staticMap map[string]path.Path cache *lru.Cache @@ -125,7 +123,6 @@ func NewNameSystem(r routing.ValueStore, opts ...Option) (NameSystem, error) { ns.dnsResolver = NewDNSResolver(madns.DefaultResolver.LookupTXT) } - ns.proquintResolver = new(ProquintResolver) ns.ipnsResolver = NewIpnsResolver(r) ns.ipnsPublisher = NewIpnsPublisher(r, ns.ds) @@ -188,7 +185,6 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. // Resolver selection: // 1. if it is a PeerID/CID/multihash resolve through "ipns". // 2. if it is a domain name, resolve through "dns" - // 3. otherwise resolve through the "proquint" resolver var res resolver ipnsKey, err := peer.Decode(key) @@ -228,11 +224,9 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. } else if _, ok := dns.IsDomainName(key); ok { res = ns.dnsResolver } else { - // TODO: remove proquint? - // dns.IsDomainName(key) will return true for proquint strings, - // so this block is a dead code. - // (alternative is to move this before DNS check) - res = ns.proquintResolver + out <- onceResult{err: fmt.Errorf("invalid IPNS root: %q", key)} + close(out) + return out } resCh := res.resolveOnceAsync(ctx, key, options) diff --git a/namesys/proquint.go b/namesys/proquint.go deleted file mode 100644 index b918ec986..000000000 --- a/namesys/proquint.go +++ /dev/null @@ -1,34 +0,0 @@ -package namesys - -import ( - "context" - "errors" - - proquint "github.com/bren2010/proquint" - path "github.com/ipfs/go-path" - opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" -) - -// ProquintResolver implements the Resolver interface for proquint identifiers -// (see http://arxiv.org/html/0901.4016). -type ProquintResolver struct{} - -// Resolve implements Resolver. -func (r *ProquintResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) { - return resolve(ctx, r, name, opts.ProcessOpts(options)) -} - -// resolveOnce implements resolver. Decodes the proquint string. -func (r *ProquintResolver) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { - out := make(chan onceResult, 1) - defer close(out) - - ok, err := proquint.IsProquint(name) - if err != nil || !ok { - out <- onceResult{err: errors.New("not a valid proquint string")} - return out - } - // Return a 0 TTL as caching this result is pointless. - out <- onceResult{value: path.FromString(string(proquint.Decode(name)))} - return out -} From b1a0055a331cf77cf65adc8c3432aafa658fb81b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 22 Apr 2021 15:28:18 -0700 Subject: [PATCH 3369/3817] doc: document WithDatastore option Co-authored-by: Adin Schmahmann This commit was moved from ipfs/go-namesys@415b3531024d4505e2e8ade7314568ce5e085d47 --- namesys/namesys.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index f1c1d22c2..537f0d1b0 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -78,7 +78,7 @@ func WithDNSResolver(rslv madns.BasicResolver) Option { } } -// WithDatastore is an option that supplies a datastore to use instead of an in-memory map datastore. +// WithDatastore is an option that supplies a datastore to use instead of an in-memory map datastore. The datastore is used to store published IPNS records and make them available for querying. func WithDatastore(ds ds.Datastore) Option { return func(ns *mpns) error { ns.ds = ds From be784924bbd7cd1492c03b79b82d0e851dc9a248 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 23 Apr 2021 20:26:04 +0300 Subject: [PATCH 3370/3817] comment cosmetics This commit was moved from ipfs/go-namesys@df97fc2540cfbe4f21a76695885784a7fcee6eaf --- namesys/dns.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/dns.go b/namesys/dns.go index 43768804f..139835617 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -13,7 +13,7 @@ import ( dns "github.com/miekg/dns" ) -// LookupTXTFunc is a generic type for a function that lookups TXT record values. +// LookupTXTFunc is a function that lookups TXT record values. type LookupTXTFunc func(ctx context.Context, name string) (txt []string, err error) // DNSResolver implements a Resolver on DNS domains From 09f0a056535a4e78c0b99e387b60146510ec3881 Mon Sep 17 00:00:00 2001 From: frrist Date: Mon, 3 May 2021 11:51:03 -0700 Subject: [PATCH 3371/3817] fix(arc): striped locking on last byte of CID - fixes #64 This commit was moved from ipfs/go-ipfs-blockstore@8d1f7bfec762ad72f6e34ab4966dfac0c7cf36c8 --- blockstore/arc_cache.go | 70 ++++++++++++++++++++++++++++++++++-- blockstore/arc_cache_test.go | 38 ++++++++++++++------ 2 files changed, 95 insertions(+), 13 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 1e497abf9..7f859f342 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -2,6 +2,7 @@ package blockstore import ( "context" + "sync" lru "github.com/hashicorp/golang-lru" blocks "github.com/ipfs/go-block-format" @@ -17,7 +18,9 @@ type cacheSize int // size. This provides block access-time improvements, allowing // to short-cut many searches without querying the underlying datastore. type arccache struct { - cache *lru.TwoQueueCache + cache *lru.TwoQueueCache + lks [256]sync.RWMutex + blockstore Blockstore viewer Viewer @@ -42,11 +45,27 @@ func newARCCachedBS(ctx context.Context, bs Blockstore, lruSize int) (*arccache, return c, nil } +func (b *arccache) getLock(k cid.Cid) *sync.RWMutex { + return &b.lks[mutexKey(k)] +} + +func mutexKey(k cid.Cid) uint8 { + return k.KeyString()[len(k.KeyString())-1] +} + func (b *arccache) DeleteBlock(k cid.Cid) error { + if !k.Defined() { + return nil + } + if has, _, ok := b.queryCache(k); ok && !has { return nil } + lk := b.getLock(k) + lk.Lock() + defer lk.Unlock() + b.cache.Remove(k) // Invalidate cache before deleting. err := b.blockstore.DeleteBlock(k) if err == nil { @@ -56,9 +75,18 @@ func (b *arccache) DeleteBlock(k cid.Cid) error { } func (b *arccache) Has(k cid.Cid) (bool, error) { + if !k.Defined() { + return false, nil + } + if has, _, ok := b.queryCache(k); ok { return has, nil } + + lk := b.getLock(k) + lk.RLock() + defer lk.RUnlock() + has, err := b.blockstore.Has(k) if err != nil { return false, err @@ -68,6 +96,10 @@ func (b *arccache) Has(k cid.Cid) (bool, error) { } func (b *arccache) GetSize(k cid.Cid) (int, error) { + if !k.Defined() { + return -1, ErrNotFound + } + if has, blockSize, ok := b.queryCache(k); ok { if !has { // don't have it, return @@ -79,6 +111,11 @@ func (b *arccache) GetSize(k cid.Cid) (int, error) { } // we have it but don't know the size, ask the datastore. } + + lk := b.getLock(k) + lk.RLock() + defer lk.RUnlock() + blockSize, err := b.blockstore.GetSize(k) if err == ErrNotFound { b.cacheHave(k, false) @@ -100,7 +137,6 @@ func (b *arccache) View(k cid.Cid, callback func([]byte) error) error { } if !k.Defined() { - log.Error("undefined cid in arc cache") return ErrNotFound } @@ -110,12 +146,15 @@ func (b *arccache) View(k cid.Cid, callback func([]byte) error) error { return ErrNotFound } + lk := b.getLock(k) + lk.RLock() + defer lk.RUnlock() + return b.viewer.View(k, callback) } func (b *arccache) Get(k cid.Cid) (blocks.Block, error) { if !k.Defined() { - log.Error("undefined cid in arc cache") return nil, ErrNotFound } @@ -123,6 +162,10 @@ func (b *arccache) Get(k cid.Cid) (blocks.Block, error) { return nil, ErrNotFound } + lk := b.getLock(k) + lk.RLock() + defer lk.RUnlock() + bl, err := b.blockstore.Get(k) if bl == nil && err == ErrNotFound { b.cacheHave(k, false) @@ -137,6 +180,10 @@ func (b *arccache) Put(bl blocks.Block) error { return nil } + lk := b.getLock(bl.Cid()) + lk.Lock() + defer lk.Unlock() + err := b.blockstore.Put(bl) if err == nil { b.cacheSize(bl.Cid(), len(bl.RawData())) @@ -145,14 +192,31 @@ func (b *arccache) Put(bl blocks.Block) error { } func (b *arccache) PutMany(bs []blocks.Block) error { + mxs := [256]*sync.RWMutex{} var good []blocks.Block for _, block := range bs { // call put on block if result is inconclusive or we are sure that // the block isn't in storage if has, _, ok := b.queryCache(block.Cid()); !ok || (ok && !has) { good = append(good, block) + mxs[mutexKey(block.Cid())] = &b.lks[mutexKey(block.Cid())] + } + } + + for _, mx := range mxs { + if mx != nil { + mx.Lock() } } + + defer func() { + for _, mx := range mxs { + if mx != nil { + mx.Unlock() + } + } + }() + err := b.blockstore.PutMany(good) if err != nil { return err diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index dcd9c6e30..a15ff2d3a 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -246,16 +246,34 @@ func TestDifferentKeyObjectsWork(t *testing.T) { } func TestPutManyCaches(t *testing.T) { - arc, _, cd := createStores(t) - arc.PutMany([]blocks.Block{exampleBlock}) + t.Run("happy path PutMany", func(t *testing.T) { + arc, _, cd := createStores(t) + arc.PutMany([]blocks.Block{exampleBlock}) + + trap("has hit datastore", cd, t) + arc.Has(exampleBlock.Cid()) + arc.GetSize(exampleBlock.Cid()) + untrap(cd) + arc.DeleteBlock(exampleBlock.Cid()) + + arc.Put(exampleBlock) + trap("PunMany has hit datastore", cd, t) + arc.PutMany([]blocks.Block{exampleBlock}) + }) - trap("has hit datastore", cd, t) - arc.Has(exampleBlock.Cid()) - arc.GetSize(exampleBlock.Cid()) - untrap(cd) - arc.DeleteBlock(exampleBlock.Cid()) + t.Run("PutMany with duplicates", func(t *testing.T) { + arc, _, cd := createStores(t) + arc.PutMany([]blocks.Block{exampleBlock, exampleBlock}) + + trap("has hit datastore", cd, t) + arc.Has(exampleBlock.Cid()) + arc.GetSize(exampleBlock.Cid()) + untrap(cd) + arc.DeleteBlock(exampleBlock.Cid()) + + arc.Put(exampleBlock) + trap("PunMany has hit datastore", cd, t) + arc.PutMany([]blocks.Block{exampleBlock}) + }) - arc.Put(exampleBlock) - trap("PunMany has hit datastore", cd, t) - arc.PutMany([]blocks.Block{exampleBlock}) } From 401c064bce856dc0bcc93eda338972f4ff705104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Wed, 28 Apr 2021 13:51:41 +0100 Subject: [PATCH 3372/3817] WIP: add BenchmarkARCCacheConcurrentOps This commit was moved from ipfs/go-ipfs-blockstore@b3408fff0fa09a9491280404e50821c294bc84c2 --- blockstore/arc_cache_test.go | 121 ++++++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 1 deletion(-) diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index a15ff2d3a..64f45df6c 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -2,7 +2,11 @@ package blockstore import ( "context" + "io" + "math/rand" + "sync/atomic" "testing" + "time" blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" @@ -26,7 +30,7 @@ func testArcCached(ctx context.Context, bs Blockstore) (*arccache, error) { return nil, err } -func createStores(t *testing.T) (*arccache, Blockstore, *callbackDatastore) { +func createStores(t testing.TB) (*arccache, Blockstore, *callbackDatastore) { cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} bs := NewBlockstore(syncds.MutexWrap(cd)) arc, err := testArcCached(context.TODO(), bs) @@ -275,5 +279,120 @@ func TestPutManyCaches(t *testing.T) { trap("PunMany has hit datastore", cd, t) arc.PutMany([]blocks.Block{exampleBlock}) }) +} + +func BenchmarkARCCacheConcurrentOps(b *testing.B) { + // ~4k blocks seems high enough to be realistic, + // but low enough to cause collisions. + // Keep it as a power of 2, to simplify code below. + const numBlocks = 4 << 10 + + dummyBlocks := make([]blocks.Block, numBlocks) + + { + // scope dummyRand to prevent its unsafe concurrent use below + dummyRand := rand.New(rand.NewSource(time.Now().UnixNano())) + for i := range dummyBlocks { + dummy := make([]byte, 32) + if _, err := io.ReadFull(dummyRand, dummy); err != nil { + b.Fatal(err) + } + dummyBlocks[i] = blocks.NewBlock(dummy) + } + } + + // Each test begins with half the blocks present in the cache. + // This allows test cases to have both hits and misses, + // regardless of whether or not they do Puts. + putHalfBlocks := func(arc *arccache) { + for i, block := range dummyBlocks { + if i%2 == 0 { + if err := arc.Put(block); err != nil { + b.Fatal(err) + } + } + } + } + + // We always mix just two operations at a time. + const numOps = 2 + var testOps = []struct { + name string + ops [numOps]func(*arccache, blocks.Block) + }{ + {"PutDelete", [...]func(*arccache, blocks.Block){ + func(arc *arccache, block blocks.Block) { + arc.Put(block) + }, + func(arc *arccache, block blocks.Block) { + arc.DeleteBlock(block.Cid()) + }, + }}, + {"GetDelete", [...]func(*arccache, blocks.Block){ + func(arc *arccache, block blocks.Block) { + arc.Get(block.Cid()) + }, + func(arc *arccache, block blocks.Block) { + arc.DeleteBlock(block.Cid()) + }, + }}, + {"GetPut", [...]func(*arccache, blocks.Block){ + func(arc *arccache, block blocks.Block) { + arc.Get(block.Cid()) + }, + func(arc *arccache, block blocks.Block) { + arc.Put(block) + }, + }}, + } + for _, test := range testOps { + test := test // prevent reuse of the range var + b.Run(test.name, func(b *testing.B) { + arc, _, _ := createStores(b) + putHalfBlocks(arc) + var opCounts [numOps]uint64 + + b.ResetTimer() + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + rnd := rand.New(rand.NewSource(time.Now().UnixNano())) + for pb.Next() { + n := rnd.Int63() + blockIdx := n % numBlocks // lower bits decide the block + opIdx := (n / numBlocks) % numOps // higher bits decide what operation + + block := dummyBlocks[blockIdx] + op := test.ops[opIdx] + op(arc, block) + + atomic.AddUint64(&opCounts[opIdx], 1) + } + }) + + // We expect each op to fire roughly an equal amount of times. + // Error otherwise, as that likely means the logic is wrong. + var minIdx, maxIdx int + var minCount, maxCount uint64 + for opIdx, count := range opCounts { + if minCount == 0 || count < minCount { + minIdx = opIdx + minCount = count + } + if maxCount == 0 || count > maxCount { + maxIdx = opIdx + maxCount = count + } + } + // Skip this check if we ran few times, to avoid false positives. + if maxCount > 100 { + ratio := float64(maxCount) / float64(minCount) + if maxRatio := 2.0; ratio > maxRatio { + b.Fatalf("op %d ran %fx as many times as %d", maxIdx, ratio, minIdx) + } + } + + }) + } } From 4e36c997a8a820d2d153104296d0b2ed96ad316e Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 5 May 2021 20:35:43 -0300 Subject: [PATCH 3373/3817] feat: add UpgradeableDirectory This commit was moved from ipfs/go-unixfs@8c3d5ec04263f16e58962dfa0cf2e26c53fdc59b --- unixfs/io/directory.go | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index f773704a2..37d496b58 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -81,7 +81,10 @@ type HAMTDirectory struct { dserv ipld.DAGService } -// NewDirectory returns a Directory. It needs a `DAGService` to add the children. +// NewDirectory returns a Directory that can either be a HAMTDirectory if the +// UseHAMTSharding is set, or otherwise an UpgradeableDirectory containing a +// BasicDirectory that can be converted to a HAMTDirectory if the option is +// set in the future. func NewDirectory(dserv ipld.DAGService) Directory { if UseHAMTSharding { dir := new(HAMTDirectory) @@ -94,10 +97,10 @@ func NewDirectory(dserv ipld.DAGService) Directory { return dir } - dir := new(BasicDirectory) - dir.node = format.EmptyDirNode() - dir.dserv = dserv - return dir + basicDir := new(BasicDirectory) + basicDir.node = format.EmptyDirNode() + basicDir.dserv = dserv + return UpgradeableDirectory{basicDir} } // ErrNotADir implies that the given node was not a unixfs directory @@ -294,3 +297,27 @@ func (d *HAMTDirectory) GetNode() (ipld.Node, error) { func (d *HAMTDirectory) GetCidBuilder() cid.Builder { return d.shard.CidBuilder() } + +// UpgradeableDirectory wraps a Directory interface and provides extra logic +// to upgrade from its BasicDirectory implementation to HAMTDirectory. +type UpgradeableDirectory struct { + Directory +} + +var _ Directory = (*UpgradeableDirectory)(nil) + +// AddChild implements the `Directory` interface. We check when adding new entries +// if we should switch to HAMTDirectory according to global option(s). +func (d UpgradeableDirectory) AddChild(ctx context.Context, name string, nd ipld.Node) error { + if UseHAMTSharding { + if basicDir, ok := d.Directory.(*BasicDirectory); ok { + hamtDir, err := basicDir.SwitchToSharding(ctx) + if err != nil { + return err + } + d.Directory = hamtDir + } + } + + return d.Directory.AddChild(ctx, name, nd) +} From caf348ae2e09cd09c099f1db5dd80541ed91cfa4 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 5 May 2021 21:01:46 -0300 Subject: [PATCH 3374/3817] add test This commit was moved from ipfs/go-unixfs@930e8c98a84d167b57b3394d2b0e6565b6d42bd2 --- unixfs/io/directory_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/unixfs/io/directory_test.go b/unixfs/io/directory_test.go index 12c481753..09286458b 100644 --- a/unixfs/io/directory_test.go +++ b/unixfs/io/directory_test.go @@ -98,6 +98,30 @@ func TestDuplicateAddDir(t *testing.T) { } } +func TestUpgradeableDirectory(t *testing.T) { + oldHamtOption := UseHAMTSharding + defer func() {UseHAMTSharding = oldHamtOption}() + + ds := mdtest.Mock() + UseHAMTSharding = false // Create a BasicDirectory. + dir := NewDirectory(ds) + if _, ok := dir.(UpgradeableDirectory).Directory.(*BasicDirectory); !ok { + t.Fatal("UpgradeableDirectory doesn't contain BasicDirectory") + } + + // Any new directory entry will trigger the upgrade to HAMTDirectory + UseHAMTSharding = true + + err := dir.AddChild(context.Background(), "test", ft.EmptyDirNode()) + if err != nil { + t.Fatal(err) + } + + if _, ok := dir.(UpgradeableDirectory).Directory.(*HAMTDirectory); !ok { + t.Fatal("UpgradeableDirectory wasn't upgraded to HAMTDirectory") + } +} + func TestDirBuilder(t *testing.T) { ds := mdtest.Mock() dir := NewDirectory(ds) From 2021a81bb23240623834810e0e047bc6c1ee55c9 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 5 May 2021 21:05:06 -0300 Subject: [PATCH 3375/3817] fix: add pointer receiver This commit was moved from ipfs/go-unixfs@cd9b8c9ff657f500824efbd174728803972503e6 --- unixfs/io/directory.go | 4 ++-- unixfs/io/directory_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index 37d496b58..03b84b98f 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -100,7 +100,7 @@ func NewDirectory(dserv ipld.DAGService) Directory { basicDir := new(BasicDirectory) basicDir.node = format.EmptyDirNode() basicDir.dserv = dserv - return UpgradeableDirectory{basicDir} + return &UpgradeableDirectory{basicDir} } // ErrNotADir implies that the given node was not a unixfs directory @@ -308,7 +308,7 @@ var _ Directory = (*UpgradeableDirectory)(nil) // AddChild implements the `Directory` interface. We check when adding new entries // if we should switch to HAMTDirectory according to global option(s). -func (d UpgradeableDirectory) AddChild(ctx context.Context, name string, nd ipld.Node) error { +func (d *UpgradeableDirectory) AddChild(ctx context.Context, name string, nd ipld.Node) error { if UseHAMTSharding { if basicDir, ok := d.Directory.(*BasicDirectory); ok { hamtDir, err := basicDir.SwitchToSharding(ctx) diff --git a/unixfs/io/directory_test.go b/unixfs/io/directory_test.go index 09286458b..f7240b0f8 100644 --- a/unixfs/io/directory_test.go +++ b/unixfs/io/directory_test.go @@ -105,7 +105,7 @@ func TestUpgradeableDirectory(t *testing.T) { ds := mdtest.Mock() UseHAMTSharding = false // Create a BasicDirectory. dir := NewDirectory(ds) - if _, ok := dir.(UpgradeableDirectory).Directory.(*BasicDirectory); !ok { + if _, ok := dir.(*UpgradeableDirectory).Directory.(*BasicDirectory); !ok { t.Fatal("UpgradeableDirectory doesn't contain BasicDirectory") } @@ -117,7 +117,7 @@ func TestUpgradeableDirectory(t *testing.T) { t.Fatal(err) } - if _, ok := dir.(UpgradeableDirectory).Directory.(*HAMTDirectory); !ok { + if _, ok := dir.(*UpgradeableDirectory).Directory.(*HAMTDirectory); !ok { t.Fatal("UpgradeableDirectory wasn't upgraded to HAMTDirectory") } } From abf28084bfbc090a00dc679c21702a146895e2fc Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 5 May 2021 21:10:02 -0300 Subject: [PATCH 3376/3817] go fmt This commit was moved from ipfs/go-unixfs@28e86c5e803d504df2f20f740a918116d07f4f18 --- unixfs/io/directory_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/io/directory_test.go b/unixfs/io/directory_test.go index f7240b0f8..2b6e6afa2 100644 --- a/unixfs/io/directory_test.go +++ b/unixfs/io/directory_test.go @@ -100,7 +100,7 @@ func TestDuplicateAddDir(t *testing.T) { func TestUpgradeableDirectory(t *testing.T) { oldHamtOption := UseHAMTSharding - defer func() {UseHAMTSharding = oldHamtOption}() + defer func() { UseHAMTSharding = oldHamtOption }() ds := mdtest.Mock() UseHAMTSharding = false // Create a BasicDirectory. From 5f07e6f1126428d70822bb973cbd43704a1f48dd Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Fri, 7 May 2021 17:03:37 +0100 Subject: [PATCH 3377/3817] chore: fixup tests and ensure go vet and staticcheck pass This commit was moved from ipfs/go-namesys@f16eb589a6907639ecc4dbdad7f47695243e5e31 --- namesys/dns_test.go | 1 - namesys/namesys_test.go | 2 +- namesys/publisher.go | 12 +++++++++++- namesys/publisher_test.go | 3 ++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/namesys/dns_test.go b/namesys/dns_test.go index cde077e47..adab3e7d2 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -21,7 +21,6 @@ func (m *mockDNS) lookupTXT(ctx context.Context, name string) (txt []string, err } func TestDnsEntryParsing(t *testing.T) { - goodEntries := []string{ "QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 6ae94a6cf..c3e553429 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -160,7 +160,7 @@ func TestPublishWithTTL(t *testing.T) { ttl := 1 * time.Second eol := time.Now().Add(2 * time.Second) - ctx := context.WithValue(context.Background(), "ipns-publish-ttl", ttl) + ctx := ContextWithTTL(context.Background(), ttl) err = nsys.Publish(ctx, priv, p) if err != nil { t.Fatal(err) diff --git a/namesys/publisher.go b/namesys/publisher.go index 37dab0ed2..edf57375a 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -206,7 +206,7 @@ func (p *IpnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value // as such, i'm using the context to wire it through to avoid changing too // much code along the way. func checkCtxTTL(ctx context.Context) (time.Duration, bool) { - v := ctx.Value("ipns-publish-ttl") + v := ctx.Value(ttlContextKey) if v == nil { return 0, false } @@ -296,3 +296,13 @@ func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec func PkKeyForID(id peer.ID) string { return "/pk/" + string(id) } + +// contextKey is a private comparable type used to hold value keys in contexts +type contextKey string + +var ttlContextKey contextKey = "ipns-publish-ttl" + +// ContextWithTTL returns a copy of the parent context with an added value representing the TTL +func ContextWithTTL(ctx context.Context, ttl time.Duration) context.Context { + return context.WithValue(context.Background(), ttlContextKey, ttl) +} diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 625103383..afc9efcc2 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -3,10 +3,11 @@ package namesys import ( "context" "crypto/rand" - "github.com/ipfs/go-path" "testing" "time" + "github.com/ipfs/go-path" + ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" dshelp "github.com/ipfs/go-ipfs-ds-help" From a45e97d9902000e9d7aa86b5c8d4ee8db37b0d46 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Fri, 7 May 2021 16:43:55 -0300 Subject: [PATCH 3378/3817] feat: switch to HAMT based on size (#91) This commit was moved from ipfs/go-unixfs@4a10174b3e417406de313481042a510768b47d3c --- unixfs/io/directory.go | 148 +++++++++++++++++++++++++++--------- unixfs/io/directory_test.go | 104 +++++++++++++++++++++++-- unixfs/unixfs.go | 5 ++ 3 files changed, 212 insertions(+), 45 deletions(-) diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index 03b84b98f..b0c4549aa 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -12,11 +12,18 @@ import ( cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" + logging "github.com/ipfs/go-log" ) -// UseHAMTSharding is a global flag that signifies whether or not to use the -// HAMT sharding scheme for directory creation -var UseHAMTSharding = false +var log = logging.Logger("unixfs") + +// HAMTShardingSize is a global option that allows switching to a HAMTDirectory +// when the BasicDirectory grows above the size (in bytes) signalled by this +// flag. The default size of 0 disables the option. +// The size is not the *exact* block size of the encoded BasicDirectory but just +// the estimated size based byte length of links name and CID (BasicDirectory's +// ProtoNode doesn't use the Data field so this estimate is pretty accurate). +var HAMTShardingSize = 0 // DefaultShardWidth is the default value used for hamt sharding width. var DefaultShardWidth = 256 @@ -72,6 +79,13 @@ type Directory interface { type BasicDirectory struct { node *mdag.ProtoNode dserv ipld.DAGService + + // Internal variable used to cache the estimated size of the basic directory: + // for each link, aggregate link name + link CID. DO NOT CHANGE THIS + // as it will affect the HAMT transition behavior in HAMTShardingSize. + // (We maintain this value up to date even if the HAMTShardingSize is off + // since potentially the option could be activated on the fly.) + estimatedSize int } // HAMTDirectory is the HAMT implementation of `Directory`. @@ -81,26 +95,25 @@ type HAMTDirectory struct { dserv ipld.DAGService } -// NewDirectory returns a Directory that can either be a HAMTDirectory if the -// UseHAMTSharding is set, or otherwise an UpgradeableDirectory containing a -// BasicDirectory that can be converted to a HAMTDirectory if the option is -// set in the future. -func NewDirectory(dserv ipld.DAGService) Directory { - if UseHAMTSharding { - dir := new(HAMTDirectory) - s, err := hamt.NewShard(dserv, DefaultShardWidth) - if err != nil { - panic(err) // will only panic if DefaultShardWidth is a bad value - } - dir.shard = s - dir.dserv = dserv - return dir - } +func newEmptyBasicDirectory(dserv ipld.DAGService) *BasicDirectory { + return newBasicDirectoryFromNode(dserv, format.EmptyDirNode()) +} +func newBasicDirectoryFromNode(dserv ipld.DAGService, node *mdag.ProtoNode) *BasicDirectory { basicDir := new(BasicDirectory) - basicDir.node = format.EmptyDirNode() + basicDir.node = node basicDir.dserv = dserv - return &UpgradeableDirectory{basicDir} + + // Scan node links (if any) to restore estimated size. + basicDir.computeEstimatedSize() + + return basicDir +} + +// NewDirectory returns a Directory implemented by UpgradeableDirectory +// containing a BasicDirectory that can be converted to a HAMTDirectory. +func NewDirectory(dserv ipld.DAGService) Directory { + return &UpgradeableDirectory{newEmptyBasicDirectory(dserv)} } // ErrNotADir implies that the given node was not a unixfs directory @@ -121,10 +134,7 @@ func NewDirectoryFromNode(dserv ipld.DAGService, node ipld.Node) (Directory, err switch fsNode.Type() { case format.TDirectory: - return &BasicDirectory{ - dserv: dserv, - node: protoBufNode.Copy().(*mdag.ProtoNode), - }, nil + return newBasicDirectoryFromNode(dserv, protoBufNode.Copy().(*mdag.ProtoNode)), nil case format.THAMTShard: shard, err := hamt.NewHamtFromDag(dserv, node) if err != nil { @@ -139,6 +149,31 @@ func NewDirectoryFromNode(dserv ipld.DAGService, node ipld.Node) (Directory, err return nil, ErrNotADir } +func (d *BasicDirectory) computeEstimatedSize() { + d.ForEachLink(nil, func(l *ipld.Link) error { + d.addToEstimatedSize(l.Name, l.Cid) + return nil + }) +} + +func estimatedLinkSize(linkName string, linkCid cid.Cid) int { + return len(linkName) + linkCid.ByteLen() +} + +func (d *BasicDirectory) addToEstimatedSize(name string, linkCid cid.Cid) { + d.estimatedSize += estimatedLinkSize(name, linkCid) +} + +func (d *BasicDirectory) removeFromEstimatedSize(name string, linkCid cid.Cid) { + d.estimatedSize -= estimatedLinkSize(name, linkCid) + if d.estimatedSize < 0 { + // Something has gone very wrong. Log an error and recompute the + // size from scratch. + log.Error("BasicDirectory's estimatedSize went below 0") + d.computeEstimatedSize() + } +} + // SetCidBuilder implements the `Directory` interface. func (d *BasicDirectory) SetCidBuilder(builder cid.Builder) { d.node.SetCidBuilder(builder) @@ -147,10 +182,18 @@ func (d *BasicDirectory) SetCidBuilder(builder cid.Builder) { // AddChild implements the `Directory` interface. It adds (or replaces) // a link to the given `node` under `name`. func (d *BasicDirectory) AddChild(ctx context.Context, name string, node ipld.Node) error { - d.node.RemoveNodeLink(name) - // Remove old link (if it existed), don't check a potential `ErrNotFound`. + // Remove old link (if it existed; ignore `ErrNotExist` otherwise). + err := d.RemoveChild(ctx, name) + if err != nil && err != os.ErrNotExist { + return err + } - return d.node.AddNodeLink(name, node) + err = d.node.AddNodeLink(name, node) + if err != nil { + return err + } + d.addToEstimatedSize(name, node.Cid()) + return nil } // EnumLinksAsync returns a channel which will receive Links in the directory @@ -203,11 +246,24 @@ func (d *BasicDirectory) Find(ctx context.Context, name string) (ipld.Node, erro // RemoveChild implements the `Directory` interface. func (d *BasicDirectory) RemoveChild(ctx context.Context, name string) error { - err := d.node.RemoveNodeLink(name) + // We need to *retrieve* the link before removing it to update the estimated + // size. This means we may iterate the links slice twice: if traversing this + // becomes a problem, a factor of 2 isn't going to make much of a difference. + // We'd likely need to cache a link resolution map in that case. + link, err := d.node.GetNodeLink(name) if err == mdag.ErrLinkNotFound { - err = os.ErrNotExist + return os.ErrNotExist + } + if err != nil { + return err // at the moment there is no other error besides ErrLinkNotFound } - return err + + // The name actually existed so we should update the estimated size. + d.removeFromEstimatedSize(link.Name, link.Cid) + + return d.node.RemoveNodeLink(name) + // GetNodeLink didn't return ErrLinkNotFound so this won't fail with that + // and we don't need to convert the error again. } // GetNode implements the `Directory` interface. @@ -309,15 +365,31 @@ var _ Directory = (*UpgradeableDirectory)(nil) // AddChild implements the `Directory` interface. We check when adding new entries // if we should switch to HAMTDirectory according to global option(s). func (d *UpgradeableDirectory) AddChild(ctx context.Context, name string, nd ipld.Node) error { - if UseHAMTSharding { - if basicDir, ok := d.Directory.(*BasicDirectory); ok { - hamtDir, err := basicDir.SwitchToSharding(ctx) - if err != nil { - return err - } - d.Directory = hamtDir + err := d.Directory.AddChild(ctx, name, nd) + if err != nil { + return err + } + + // Evaluate possible HAMT upgrade. + if HAMTShardingSize == 0 { + return nil + } + basicDir, ok := d.Directory.(*BasicDirectory) + if !ok { + return nil + } + if basicDir.estimatedSize >= HAMTShardingSize { + // Ideally to minimize performance we should check if this last + // `AddChild` call would bring the directory size over the threshold + // *before* executing it since we would end up switching anyway and + // that call would be "wasted". This is a minimal performance impact + // and we prioritize a simple code base. + hamtDir, err := basicDir.SwitchToSharding(ctx) + if err != nil { + return err } + d.Directory = hamtDir } - return d.Directory.AddChild(ctx, name, nd) + return nil } diff --git a/unixfs/io/directory_test.go b/unixfs/io/directory_test.go index 2b6e6afa2..8c5d8e109 100644 --- a/unixfs/io/directory_test.go +++ b/unixfs/io/directory_test.go @@ -3,9 +3,11 @@ package io import ( "context" "fmt" + "math" "testing" ipld "github.com/ipfs/go-ipld-format" + mdag "github.com/ipfs/go-merkledag" mdtest "github.com/ipfs/go-merkledag/test" ft "github.com/ipfs/go-unixfs" @@ -98,27 +100,115 @@ func TestDuplicateAddDir(t *testing.T) { } } +// FIXME: Nothing blocking but nice to have: +// * Check estimated size against link enumeration (indirectly done in the +// restored node check from NewDirectoryFromNode). +// * Check estimated size against encoded node (the difference should only be +// a small percentage for a directory with 10s of entries). +func TestBasicDirectory_estimatedSize(t *testing.T) { + ds := mdtest.Mock() + ctx := context.Background() + child := ft.EmptyFileNode() + err := ds.Add(ctx, child) + if err != nil { + t.Fatal(err) + } + + basicDir := newEmptyBasicDirectory(ds) + + // Several overwrites should not corrupt the size estimation. + basicDir.AddChild(ctx, "child", child) + basicDir.AddChild(ctx, "child", child) + basicDir.AddChild(ctx, "child", child) + basicDir.RemoveChild(ctx, "child") + basicDir.AddChild(ctx, "child", child) + basicDir.RemoveChild(ctx, "child") + // FIXME: Check errors above (abstract adds/removals in iteration). + if basicDir.estimatedSize != 0 { + t.Fatal("estimated size is not zero after removing all entries") + } + + for i := 0; i < 100; i++ { + basicDir.AddChild(ctx, fmt.Sprintf("child-%03d", i), child) // e.g., "child-045" + } + // Estimated entry size: name (9) + CID (32 from hash and 2 extra for header) + entrySize := 9 + 32 + 2 + expectedSize := 100 * entrySize + if basicDir.estimatedSize != expectedSize { + t.Fatalf("estimated size (%d) inaccurate after adding many entries (expected %d)", + basicDir.estimatedSize, expectedSize) + } + + basicDir.RemoveChild(ctx, "child-045") // just random values + basicDir.RemoveChild(ctx, "child-063") + basicDir.RemoveChild(ctx, "child-011") + basicDir.RemoveChild(ctx, "child-000") + basicDir.RemoveChild(ctx, "child-099") + + basicDir.RemoveChild(ctx, "child-045") // already removed, won't impact size + basicDir.RemoveChild(ctx, "nonexistent-name") // also doesn't count + basicDir.RemoveChild(ctx, "child-100") // same + expectedSize -= 5 * entrySize + if basicDir.estimatedSize != expectedSize { + t.Fatalf("estimated size (%d) inaccurate after removing some entries (expected %d)", + basicDir.estimatedSize, expectedSize) + } + + // Restore a directory from original's node and check estimated size consistency. + basicDirSingleNode, _ := basicDir.GetNode() // no possible error + restoredBasicDir := newBasicDirectoryFromNode(ds, basicDirSingleNode.(*mdag.ProtoNode)) + if basicDir.estimatedSize != restoredBasicDir.estimatedSize { + t.Fatalf("restored basic directory size (%d) doesn't match original estimate (%d)", + basicDir.estimatedSize, restoredBasicDir.estimatedSize) + } +} + +// Basic test on extreme threshold to trigger switch. More fine-grained sizes +// are checked in TestBasicDirectory_estimatedSize (without the swtich itself +// but focusing on the size computation). +// FIXME: Ideally, instead of checking size computation on one test and directory +// upgrade on another a better structured test should test both dimensions +// simultaneously. func TestUpgradeableDirectory(t *testing.T) { - oldHamtOption := UseHAMTSharding - defer func() { UseHAMTSharding = oldHamtOption }() + oldHamtOption := HAMTShardingSize + defer func() { HAMTShardingSize = oldHamtOption }() ds := mdtest.Mock() - UseHAMTSharding = false // Create a BasicDirectory. dir := NewDirectory(ds) + ctx := context.Background() + child := ft.EmptyDirNode() + err := ds.Add(ctx, child) + if err != nil { + t.Fatal(err) + } + + HAMTShardingSize = 0 // Create a BasicDirectory. if _, ok := dir.(*UpgradeableDirectory).Directory.(*BasicDirectory); !ok { t.Fatal("UpgradeableDirectory doesn't contain BasicDirectory") } - // Any new directory entry will trigger the upgrade to HAMTDirectory - UseHAMTSharding = true + // Set a threshold so big a new entry won't trigger the change. + HAMTShardingSize = math.MaxInt32 + + err = dir.AddChild(ctx, "test", child) + if err != nil { + t.Fatal(err) + } + + if _, ok := dir.(*UpgradeableDirectory).Directory.(*HAMTDirectory); ok { + t.Fatal("UpgradeableDirectory was upgraded to HAMTDirectory for a large threshold") + } + + // Now set it so low to make sure any new entry will trigger the upgrade. + HAMTShardingSize = 1 - err := dir.AddChild(context.Background(), "test", ft.EmptyDirNode()) + err = dir.AddChild(ctx, "test", child) // overwriting an entry should also trigger the switch if err != nil { t.Fatal(err) } if _, ok := dir.(*UpgradeableDirectory).Directory.(*HAMTDirectory); !ok { - t.Fatal("UpgradeableDirectory wasn't upgraded to HAMTDirectory") + t.Fatal("UpgradeableDirectory wasn't upgraded to HAMTDirectory for a low threshold") } } diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index 555d24efc..026b8bb3f 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -361,6 +361,11 @@ func EmptyDirNode() *dag.ProtoNode { return dag.NodeWithData(FolderPBData()) } +// EmptyFileNode creates an empty file Protonode. +func EmptyFileNode() *dag.ProtoNode { + return dag.NodeWithData(FilePBData(nil, 0)) +} + // ReadUnixFSNodeData extracts the UnixFS data from an IPLD node. // Raw nodes are (also) processed because they are used as leaf // nodes containing (only) UnixFS data. From 20e9dac666c7ae39eafb902306aab5a716ca9d8f Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Thu, 1 Apr 2021 15:10:15 -0400 Subject: [PATCH 3379/3817] Add support for extensible records (and v2 signature) This commit was moved from ipfs/go-ipns@3deb032d28934baa818e4624507dd0b1fefbbdc6 --- ipns/examples/embed.go | 2 +- ipns/ipns.go | 236 ++++++++++++++++++++++- ipns/ipns_test.go | 4 +- ipns/pb/ipns.pb.go | 422 ++++++++++++++++++++++++++++++++++++----- ipns/pb/ipns.proto | 15 +- ipns/select_test.go | 14 +- ipns/validate_test.go | 220 ++++++++++++++++++++- 7 files changed, 844 insertions(+), 69 deletions(-) diff --git a/ipns/examples/embed.go b/ipns/examples/embed.go index 78ca4595a..cfd6ea754 100644 --- a/ipns/examples/embed.go +++ b/ipns/examples/embed.go @@ -15,7 +15,7 @@ import ( func CreateEntryWithEmbed(ipfsPath string, publicKey crypto.PubKey, privateKey crypto.PrivKey) (*pb.IpnsEntry, error) { ipfsPathByte := []byte(ipfsPath) eol := time.Now().Add(time.Hour * 48) - entry, err := ipns.Create(privateKey, ipfsPathByte, 1, eol) + entry, err := ipns.Create(privateKey, ipfsPathByte, 1, eol, 0) if err != nil { return nil, err } diff --git a/ipns/ipns.go b/ipns/ipns.go index 32a6104a7..f94863c35 100644 --- a/ipns/ipns.go +++ b/ipns/ipns.go @@ -3,8 +3,20 @@ package ipns import ( "bytes" "fmt" + "sort" "time" + "github.com/pkg/errors" + + "github.com/ipld/go-ipld-prime" + _ "github.com/ipld/go-ipld-prime/codec/dagcbor" // used to import the DagCbor encoder/decoder + ipldcodec "github.com/ipld/go-ipld-prime/multicodec" + "github.com/ipld/go-ipld-prime/node/basic" + + "github.com/multiformats/go-multicodec" + + "github.com/gogo/protobuf/proto" + pb "github.com/ipfs/go-ipns/pb" u "github.com/ipfs/go-ipfs-util" @@ -12,11 +24,19 @@ import ( peer "github.com/libp2p/go-libp2p-core/peer" ) +const ( + validity = "Validity" + validityType = "ValidityType" + value = "Value" + sequence = "Sequence" + ttl = "TTL" +) + // Create creates a new IPNS entry and signs it with the given private key. // // This function does not embed the public key. If you want to do that, use // `EmbedPublicKey`. -func Create(sk ic.PrivKey, val []byte, seq uint64, eol time.Time) (*pb.IpnsEntry, error) { +func Create(sk ic.PrivKey, val []byte, seq uint64, eol time.Time, ttl time.Duration) (*pb.IpnsEntry, error) { entry := new(pb.IpnsEntry) entry.Value = val @@ -25,20 +45,112 @@ func Create(sk ic.PrivKey, val []byte, seq uint64, eol time.Time) (*pb.IpnsEntry entry.Sequence = &seq entry.Validity = []byte(u.FormatRFC3339(eol)) - sig, err := sk.Sign(ipnsEntryDataForSig(entry)) + ttlNs := uint64(ttl.Nanoseconds()) + entry.Ttl = proto.Uint64(ttlNs) + + cborData, err := createCborDataForIpnsEntry(entry) + if err != nil { + return nil, err + } + entry.Data = cborData + + sig1, err := sk.Sign(ipnsEntryDataForSigV1(entry)) + if err != nil { + return nil, errors.Wrap(err, "could not compute signature data") + } + entry.SignatureV1 = sig1 + + sig2Data, err := ipnsEntryDataForSigV2(entry) + if err != nil { + return nil, err + } + sig2, err := sk.Sign(sig2Data) if err != nil { return nil, err } - entry.Signature = sig + entry.SignatureV2 = sig2 return entry, nil } +func createCborDataForIpnsEntry(e *pb.IpnsEntry) ([]byte, error) { + m := make(map[string]ipld.Node) + var keys []string + m[value] = basicnode.NewBytes(e.GetValue()) + keys = append(keys, value) + + m[validity] = basicnode.NewBytes(e.GetValidity()) + keys = append(keys, validity) + + m[validityType] = basicnode.NewInt(int64(e.GetValidityType())) + keys = append(keys, validityType) + + m[sequence] = basicnode.NewInt(int64(e.GetSequence())) + keys = append(keys, sequence) + + m[ttl] = basicnode.NewInt(int64(e.GetTtl())) + keys = append(keys, ttl) + + sort.Sort(cborMapKeyString_RFC7049(keys)) + + newNd := basicnode.Prototype__Map{}.NewBuilder() + ma, err := newNd.BeginMap(int64(len(keys))) + if err != nil { + return nil, err + } + + for _, k := range keys { + if err := ma.AssembleKey().AssignString(k); err != nil { + return nil, err + } + if err := ma.AssembleValue().AssignNode(m[k]); err != nil { + return nil, err + } + } + + if err := ma.Finish(); err != nil { + return nil, err + } + + nd := newNd.Build() + + enc, err := ipldcodec.LookupEncoder(uint64(multicodec.DagCbor)) + if err != nil { + return nil, err + } + + buf := new(bytes.Buffer) + if err := enc(nd, buf); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + // Validates validates the given IPNS entry against the given public key. func Validate(pk ic.PubKey, entry *pb.IpnsEntry) error { // Check the ipns record signature with the public key - if ok, err := pk.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok { - return ErrSignature + + // Check v2 signature if it's available, otherwise use the v1 signature + if entry.GetSignatureV2() != nil { + sig2Data, err := ipnsEntryDataForSigV2(entry) + if err != nil { + return fmt.Errorf("could not compute signature data: %w", err) + } + if ok, err := pk.Verify(sig2Data, entry.GetSignatureV2()); err != nil || !ok { + return ErrSignature + } + + // TODO: If we switch from pb.IpnsEntry to a more generic IpnsRecord type then perhaps we should only check + // this if there is no v1 signature. In the meanwhile this helps avoid some potential rough edges around people + // checking the entry fields instead of doing CBOR decoding everywhere. + if err := validateCborDataMatchesPbData(entry); err != nil { + return err + } + } else { + if ok, err := pk.Verify(ipnsEntryDataForSigV1(entry), entry.GetSignatureV1()); err != nil || !ok { + return ErrSignature + } } eol, err := GetEOL(entry) @@ -51,6 +163,87 @@ func Validate(pk ic.PubKey, entry *pb.IpnsEntry) error { return nil } +// TODO: Most of this function could probably be replaced with codegen +func validateCborDataMatchesPbData(entry *pb.IpnsEntry) error { + if len(entry.GetData()) == 0 { + return fmt.Errorf("record data is missing") + } + + dec, err := ipldcodec.LookupDecoder(uint64(multicodec.DagCbor)) + if err != nil { + return err + } + + ndbuilder := basicnode.Prototype__Map{}.NewBuilder() + if err := dec(ndbuilder, bytes.NewReader(entry.GetData())); err != nil { + return err + } + + fullNd := ndbuilder.Build() + nd, err := fullNd.LookupByString(value) + if err != nil { + return err + } + ndBytes, err := nd.AsBytes() + if err != nil { + return err + } + if !bytes.Equal(entry.GetValue(), ndBytes) { + return fmt.Errorf("field \"%v\" did not match between protobuf and CBOR", value) + } + + nd, err = fullNd.LookupByString(validity) + if err != nil { + return err + } + ndBytes, err = nd.AsBytes() + if err != nil { + return err + } + if !bytes.Equal(entry.GetValidity(), ndBytes) { + return fmt.Errorf("field \"%v\" did not match between protobuf and CBOR", validity) + } + + nd, err = fullNd.LookupByString(validityType) + if err != nil { + return err + } + ndInt, err := nd.AsInt() + if err != nil { + return err + } + if int64(entry.GetValidityType()) != ndInt { + return fmt.Errorf("field \"%v\" did not match between protobuf and CBOR", validityType) + } + + nd, err = fullNd.LookupByString(sequence) + if err != nil { + return err + } + ndInt, err = nd.AsInt() + if err != nil { + return err + } + + if entry.GetSequence() != uint64(ndInt) { + return fmt.Errorf("field \"%v\" did not match between protobuf and CBOR", sequence) + } + + nd, err = fullNd.LookupByString("TTL") + if err != nil { + return err + } + ndInt, err = nd.AsInt() + if err != nil { + return err + } + if entry.GetTtl() != uint64(ndInt) { + return fmt.Errorf("field \"%v\" did not match between protobuf and CBOR", ttl) + } + + return nil +} + // GetEOL returns the EOL of this IPNS entry // // This function returns ErrUnrecognizedValidity if the validity type of the @@ -130,6 +323,16 @@ func ExtractPublicKey(pid peer.ID, entry *pb.IpnsEntry) (ic.PubKey, error) { // `bytes.Compare`). You must do this if you are implementing a libp2p record // validator (or you can just use the one provided for you by this package). func Compare(a, b *pb.IpnsEntry) (int, error) { + aHasV2Sig := a.GetSignatureV2() != nil + bHasV2Sig := b.GetSignatureV2() != nil + + // Having a newer signature version is better than an older signature version + if aHasV2Sig && !bHasV2Sig { + return 1, nil + } else if !aHasV2Sig && bHasV2Sig { + return -1, nil + } + as := a.GetSequence() bs := b.GetSequence() @@ -158,7 +361,7 @@ func Compare(a, b *pb.IpnsEntry) (int, error) { return 0, nil } -func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte { +func ipnsEntryDataForSigV1(e *pb.IpnsEntry) []byte { return bytes.Join([][]byte{ e.Value, e.Validity, @@ -166,3 +369,24 @@ func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte { }, []byte{}) } + +func ipnsEntryDataForSigV2(e *pb.IpnsEntry) ([]byte, error) { + dataForSig := []byte("ipns-signature:") + dataForSig = append(dataForSig, e.Data...) + + return dataForSig, nil +} + +type cborMapKeyString_RFC7049 []string + +func (x cborMapKeyString_RFC7049) Len() int { return len(x) } +func (x cborMapKeyString_RFC7049) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x cborMapKeyString_RFC7049) Less(i, j int) bool { + li, lj := len(x[i]), len(x[j]) + if li == lj { + return x[i] < x[j] + } + return li < lj +} + +var _ sort.Interface = (cborMapKeyString_RFC7049)(nil) diff --git a/ipns/ipns_test.go b/ipns/ipns_test.go index 84a4b1d80..f56d1e7e2 100644 --- a/ipns/ipns_test.go +++ b/ipns/ipns_test.go @@ -23,7 +23,7 @@ func TestEmbedPublicKey(t *testing.T) { t.Fatal(err) } - e, err := Create(priv, []byte("/a/b"), 0, time.Now().Add(1*time.Hour)) + e, err := Create(priv, []byte("/a/b"), 0, time.Now().Add(1*time.Hour), 0) if err != nil { t.Fatal(err) } @@ -54,7 +54,7 @@ func ExampleCreate() { // Create an IPNS record that expires in one hour and points to the IPFS address // /ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5 - ipnsRecord, err := Create(privateKey, []byte("/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5"), 0, time.Now().Add(1*time.Hour)) + ipnsRecord, err := Create(privateKey, []byte("/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5"), 0, time.Now().Add(1*time.Hour), 0) if err != nil { panic(err) } diff --git a/ipns/pb/ipns.pb.go b/ipns/pb/ipns.pb.go index 6354831d0..8bcace7fc 100644 --- a/ipns/pb/ipns.pb.go +++ b/ipns/pb/ipns.pb.go @@ -5,7 +5,6 @@ package ipns_pb import ( fmt "fmt" - github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" proto "github.com/gogo/protobuf/proto" io "io" math "math" @@ -62,8 +61,8 @@ func (IpnsEntry_ValidityType) EnumDescriptor() ([]byte, []int) { } type IpnsEntry struct { - Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"` - Signature []byte `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"` + Value []byte `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"` + SignatureV1 []byte `protobuf:"bytes,2,opt,name=signatureV1" json:"signatureV1,omitempty"` ValidityType *IpnsEntry_ValidityType `protobuf:"varint,3,opt,name=validityType,enum=ipns.pb.IpnsEntry_ValidityType" json:"validityType,omitempty"` Validity []byte `protobuf:"bytes,4,opt,name=validity" json:"validity,omitempty"` Sequence *uint64 `protobuf:"varint,5,opt,name=sequence" json:"sequence,omitempty"` @@ -73,6 +72,8 @@ type IpnsEntry struct { // the record itself. For newer ed25519 keys, the public key can be embedded in the // peerID, making this field unnecessary. PubKey []byte `protobuf:"bytes,7,opt,name=pubKey" json:"pubKey,omitempty"` + SignatureV2 []byte `protobuf:"bytes,8,opt,name=signatureV2" json:"signatureV2,omitempty"` + Data []byte `protobuf:"bytes,9,opt,name=data" json:"data,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -118,9 +119,9 @@ func (m *IpnsEntry) GetValue() []byte { return nil } -func (m *IpnsEntry) GetSignature() []byte { +func (m *IpnsEntry) GetSignatureV1() []byte { if m != nil { - return m.Signature + return m.SignatureV1 } return nil } @@ -160,29 +161,102 @@ func (m *IpnsEntry) GetPubKey() []byte { return nil } +func (m *IpnsEntry) GetSignatureV2() []byte { + if m != nil { + return m.SignatureV2 + } + return nil +} + +func (m *IpnsEntry) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +type IpnsSignatureV2Checker struct { + PubKey []byte `protobuf:"bytes,7,opt,name=pubKey" json:"pubKey,omitempty"` + SignatureV2 []byte `protobuf:"bytes,8,opt,name=signatureV2" json:"signatureV2,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *IpnsSignatureV2Checker) Reset() { *m = IpnsSignatureV2Checker{} } +func (m *IpnsSignatureV2Checker) String() string { return proto.CompactTextString(m) } +func (*IpnsSignatureV2Checker) ProtoMessage() {} +func (*IpnsSignatureV2Checker) Descriptor() ([]byte, []int) { + return fileDescriptor_4d5b16fb32bfe8ea, []int{1} +} +func (m *IpnsSignatureV2Checker) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IpnsSignatureV2Checker) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IpnsSignatureV2Checker.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *IpnsSignatureV2Checker) XXX_Merge(src proto.Message) { + xxx_messageInfo_IpnsSignatureV2Checker.Merge(m, src) +} +func (m *IpnsSignatureV2Checker) XXX_Size() int { + return m.Size() +} +func (m *IpnsSignatureV2Checker) XXX_DiscardUnknown() { + xxx_messageInfo_IpnsSignatureV2Checker.DiscardUnknown(m) +} + +var xxx_messageInfo_IpnsSignatureV2Checker proto.InternalMessageInfo + +func (m *IpnsSignatureV2Checker) GetPubKey() []byte { + if m != nil { + return m.PubKey + } + return nil +} + +func (m *IpnsSignatureV2Checker) GetSignatureV2() []byte { + if m != nil { + return m.SignatureV2 + } + return nil +} + func init() { proto.RegisterEnum("ipns.pb.IpnsEntry_ValidityType", IpnsEntry_ValidityType_name, IpnsEntry_ValidityType_value) proto.RegisterType((*IpnsEntry)(nil), "ipns.pb.IpnsEntry") + proto.RegisterType((*IpnsSignatureV2Checker)(nil), "ipns.pb.IpnsSignatureV2Checker") } func init() { proto.RegisterFile("ipns.proto", fileDescriptor_4d5b16fb32bfe8ea) } var fileDescriptor_4d5b16fb32bfe8ea = []byte{ - // 221 bytes of a gzipped FileDescriptorProto + // 272 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xca, 0x2c, 0xc8, 0x2b, - 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0xb0, 0x93, 0x94, 0xfe, 0x33, 0x72, 0x71, + 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0xb0, 0x93, 0x94, 0x76, 0x32, 0x71, 0x71, 0x7a, 0x16, 0xe4, 0x15, 0xbb, 0xe6, 0x95, 0x14, 0x55, 0x0a, 0x89, 0x70, 0xb1, 0x96, 0x25, 0xe6, - 0x94, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x69, 0xf0, 0x04, 0x41, 0x38, 0x42, 0x32, 0x5c, 0x9c, 0xc5, - 0x99, 0xe9, 0x79, 0x89, 0x25, 0xa5, 0x45, 0xa9, 0x12, 0x4c, 0x60, 0x19, 0x84, 0x80, 0x90, 0x33, - 0x17, 0x4f, 0x59, 0x62, 0x4e, 0x66, 0x4a, 0x66, 0x49, 0x65, 0x48, 0x65, 0x41, 0xaa, 0x04, 0xb3, - 0x02, 0xa3, 0x06, 0x9f, 0x91, 0xbc, 0x1e, 0xd4, 0x06, 0x3d, 0xb8, 0xe9, 0x7a, 0x61, 0x48, 0xca, - 0x82, 0x50, 0x34, 0x09, 0x49, 0x71, 0x71, 0xc0, 0xf8, 0x12, 0x2c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, - 0x70, 0x3e, 0x48, 0xae, 0x38, 0xb5, 0xb0, 0x34, 0x35, 0x2f, 0x39, 0x55, 0x82, 0x55, 0x81, 0x51, - 0x83, 0x25, 0x08, 0xce, 0x17, 0x12, 0xe0, 0x62, 0x2e, 0x29, 0xc9, 0x91, 0x60, 0x03, 0x0b, 0x83, - 0x98, 0x42, 0x62, 0x5c, 0x6c, 0x05, 0xa5, 0x49, 0xde, 0xa9, 0x95, 0x12, 0xec, 0x60, 0x73, 0xa0, - 0x3c, 0x25, 0x71, 0x2e, 0x1e, 0x64, 0xfb, 0x85, 0xd8, 0xb9, 0x98, 0x5d, 0xfd, 0x7d, 0x04, 0x18, - 0x9c, 0x78, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0x46, 0x40, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x32, 0x35, 0xc7, 0xf2, 0x25, 0x01, 0x00, 0x00, + 0x94, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0xf0, 0x04, 0x41, 0x38, 0x42, 0x0a, 0x5c, 0xdc, 0xc5, + 0x99, 0xe9, 0x79, 0x89, 0x25, 0xa5, 0x45, 0xa9, 0x61, 0x86, 0x12, 0x4c, 0x60, 0x39, 0x64, 0x21, + 0x21, 0x67, 0x2e, 0x9e, 0xb2, 0xc4, 0x9c, 0xcc, 0x94, 0xcc, 0x92, 0xca, 0x90, 0xca, 0x82, 0x54, + 0x09, 0x66, 0x05, 0x46, 0x0d, 0x3e, 0x23, 0x79, 0x3d, 0xa8, 0x2d, 0x7a, 0x70, 0x1b, 0xf4, 0xc2, + 0x90, 0x94, 0x05, 0xa1, 0x68, 0x12, 0x92, 0xe2, 0xe2, 0x80, 0xf1, 0x25, 0x58, 0xc0, 0x76, 0xc0, + 0xf9, 0x20, 0xb9, 0xe2, 0xd4, 0xc2, 0xd2, 0xd4, 0xbc, 0xe4, 0x54, 0x09, 0x56, 0x05, 0x46, 0x0d, + 0x96, 0x20, 0x38, 0x5f, 0x48, 0x80, 0x8b, 0xb9, 0xa4, 0x24, 0x47, 0x82, 0x0d, 0x2c, 0x0c, 0x62, + 0x0a, 0x89, 0x71, 0xb1, 0x15, 0x94, 0x26, 0x79, 0xa7, 0x56, 0x4a, 0xb0, 0x83, 0xcd, 0x81, 0xf2, + 0x50, 0x3d, 0x62, 0x24, 0xc1, 0x81, 0xee, 0x11, 0x23, 0x21, 0x21, 0x2e, 0x96, 0x94, 0xc4, 0x92, + 0x44, 0x09, 0x4e, 0xb0, 0x14, 0x98, 0xad, 0x24, 0xce, 0xc5, 0x83, 0xec, 0x6a, 0x21, 0x76, 0x2e, + 0x66, 0x57, 0x7f, 0x1f, 0x01, 0x06, 0xa5, 0x20, 0x2e, 0x31, 0x90, 0xc7, 0x82, 0x11, 0xfa, 0x9d, + 0x33, 0x52, 0x93, 0xb3, 0x53, 0x8b, 0xc8, 0x77, 0x80, 0x93, 0xc4, 0x89, 0x47, 0x72, 0x8c, 0x17, + 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x18, 0xc5, 0xa5, 0xa7, 0x6f, 0x0d, 0x0a, 0xc3, 0xf8, + 0x82, 0x24, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x32, 0x85, 0x5b, 0xed, 0xbf, 0x01, 0x00, 0x00, } func (m *IpnsEntry) Marshal() (dAtA []byte, err error) { @@ -209,6 +283,20 @@ func (m *IpnsEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Data != nil { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintIpns(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x4a + } + if m.SignatureV2 != nil { + i -= len(m.SignatureV2) + copy(dAtA[i:], m.SignatureV2) + i = encodeVarintIpns(dAtA, i, uint64(len(m.SignatureV2))) + i-- + dAtA[i] = 0x42 + } if m.PubKey != nil { i -= len(m.PubKey) copy(dAtA[i:], m.PubKey) @@ -238,18 +326,14 @@ func (m *IpnsEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x18 } - if m.Signature == nil { - return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("signature") - } else { - i -= len(m.Signature) - copy(dAtA[i:], m.Signature) - i = encodeVarintIpns(dAtA, i, uint64(len(m.Signature))) + if m.SignatureV1 != nil { + i -= len(m.SignatureV1) + copy(dAtA[i:], m.SignatureV1) + i = encodeVarintIpns(dAtA, i, uint64(len(m.SignatureV1))) i-- dAtA[i] = 0x12 } - if m.Value == nil { - return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("value") - } else { + if m.Value != nil { i -= len(m.Value) copy(dAtA[i:], m.Value) i = encodeVarintIpns(dAtA, i, uint64(len(m.Value))) @@ -259,6 +343,47 @@ func (m *IpnsEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *IpnsSignatureV2Checker) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *IpnsSignatureV2Checker) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IpnsSignatureV2Checker) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.SignatureV2 != nil { + i -= len(m.SignatureV2) + copy(dAtA[i:], m.SignatureV2) + i = encodeVarintIpns(dAtA, i, uint64(len(m.SignatureV2))) + i-- + dAtA[i] = 0x42 + } + if m.PubKey != nil { + i -= len(m.PubKey) + copy(dAtA[i:], m.PubKey) + i = encodeVarintIpns(dAtA, i, uint64(len(m.PubKey))) + i-- + dAtA[i] = 0x3a + } + return len(dAtA) - i, nil +} + func encodeVarintIpns(dAtA []byte, offset int, v uint64) int { offset -= sovIpns(v) base := offset @@ -280,8 +405,8 @@ func (m *IpnsEntry) Size() (n int) { l = len(m.Value) n += 1 + l + sovIpns(uint64(l)) } - if m.Signature != nil { - l = len(m.Signature) + if m.SignatureV1 != nil { + l = len(m.SignatureV1) n += 1 + l + sovIpns(uint64(l)) } if m.ValidityType != nil { @@ -301,6 +426,34 @@ func (m *IpnsEntry) Size() (n int) { l = len(m.PubKey) n += 1 + l + sovIpns(uint64(l)) } + if m.SignatureV2 != nil { + l = len(m.SignatureV2) + n += 1 + l + sovIpns(uint64(l)) + } + if m.Data != nil { + l = len(m.Data) + n += 1 + l + sovIpns(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *IpnsSignatureV2Checker) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PubKey != nil { + l = len(m.PubKey) + n += 1 + l + sovIpns(uint64(l)) + } + if m.SignatureV2 != nil { + l = len(m.SignatureV2) + n += 1 + l + sovIpns(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -314,7 +467,6 @@ func sozIpns(x uint64) (n int) { return sovIpns(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } func (m *IpnsEntry) Unmarshal(dAtA []byte) error { - var hasFields [1]uint64 l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -377,10 +529,9 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { m.Value = []byte{} } iNdEx = postIndex - hasFields[0] |= uint64(0x00000001) case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SignatureV1", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -407,12 +558,11 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) - if m.Signature == nil { - m.Signature = []byte{} + m.SignatureV1 = append(m.SignatureV1[:0], dAtA[iNdEx:postIndex]...) + if m.SignatureV1 == nil { + m.SignatureV1 = []byte{} } iNdEx = postIndex - hasFields[0] |= uint64(0x00000002) case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ValidityType", wireType) @@ -541,16 +691,81 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { m.PubKey = []byte{} } iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignatureV2", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIpns + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthIpns + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthIpns + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SignatureV2 = append(m.SignatureV2[:0], dAtA[iNdEx:postIndex]...) + if m.SignatureV2 == nil { + m.SignatureV2 = []byte{} + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIpns + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthIpns + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthIpns + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipIpns(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthIpns - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthIpns } if (iNdEx + skippy) > l { @@ -560,11 +775,124 @@ func (m *IpnsEntry) Unmarshal(dAtA []byte) error { iNdEx += skippy } } - if hasFields[0]&uint64(0x00000001) == 0 { - return github_com_gogo_protobuf_proto.NewRequiredNotSetError("value") + + if iNdEx > l { + return io.ErrUnexpectedEOF } - if hasFields[0]&uint64(0x00000002) == 0 { - return github_com_gogo_protobuf_proto.NewRequiredNotSetError("signature") + return nil +} +func (m *IpnsSignatureV2Checker) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIpns + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: IpnsSignatureV2Checker: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IpnsSignatureV2Checker: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PubKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIpns + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthIpns + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthIpns + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PubKey = append(m.PubKey[:0], dAtA[iNdEx:postIndex]...) + if m.PubKey == nil { + m.PubKey = []byte{} + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignatureV2", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIpns + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthIpns + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthIpns + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SignatureV2 = append(m.SignatureV2[:0], dAtA[iNdEx:postIndex]...) + if m.SignatureV2 == nil { + m.SignatureV2 = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipIpns(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthIpns + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } } if iNdEx > l { @@ -629,9 +957,6 @@ func skipIpns(dAtA []byte) (n int, err error) { return 0, ErrInvalidLengthIpns } iNdEx += length - if iNdEx < 0 { - return 0, ErrInvalidLengthIpns - } case 3: depth++ case 4: @@ -644,6 +969,9 @@ func skipIpns(dAtA []byte) (n int, err error) { default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthIpns + } if depth == 0 { return iNdEx, nil } diff --git a/ipns/pb/ipns.proto b/ipns/pb/ipns.proto index f2e79feff..9fd8bda83 100644 --- a/ipns/pb/ipns.proto +++ b/ipns/pb/ipns.proto @@ -2,13 +2,15 @@ syntax = "proto2"; package ipns.pb; +option go_package = "./;ipns_pb"; + message IpnsEntry { enum ValidityType { // setting an EOL says "this record is valid until..." EOL = 0; } - required bytes value = 1; - required bytes signature = 2; + optional bytes value = 1; + optional bytes signatureV1 = 2; optional ValidityType validityType = 3; optional bytes validity = 4; @@ -22,4 +24,13 @@ message IpnsEntry { // the record itself. For newer ed25519 keys, the public key can be embedded in the // peerID, making this field unnecessary. optional bytes pubKey = 7; + + optional bytes signatureV2 = 8; + + optional bytes data = 9; +} + +message IpnsSignatureV2Checker { + optional bytes pubKey = 7; + optional bytes signatureV2 = 8; } diff --git a/ipns/select_test.go b/ipns/select_test.go index 35fc3f618..905afb1da 100644 --- a/ipns/select_test.go +++ b/ipns/select_test.go @@ -15,7 +15,7 @@ import ( func shuffle(a []*pb.IpnsEntry) { for n := 0; n < 5; n++ { - for i, _ := range a { + for i := range a { j := rand.Intn(len(a)) a[i], a[j] = a[j], a[i] } @@ -56,32 +56,32 @@ func TestOrdering(t *testing.T) { t.Fatal(err) } - e1, err := Create(priv, []byte("foo"), 1, ts.Add(time.Hour)) + e1, err := Create(priv, []byte("foo"), 1, ts.Add(time.Hour), 0) if err != nil { t.Fatal(err) } - e2, err := Create(priv, []byte("bar"), 2, ts.Add(time.Hour)) + e2, err := Create(priv, []byte("bar"), 2, ts.Add(time.Hour), 0) if err != nil { t.Fatal(err) } - e3, err := Create(priv, []byte("baz"), 3, ts.Add(time.Hour)) + e3, err := Create(priv, []byte("baz"), 3, ts.Add(time.Hour), 0) if err != nil { t.Fatal(err) } - e4, err := Create(priv, []byte("cat"), 3, ts.Add(time.Hour*2)) + e4, err := Create(priv, []byte("cat"), 3, ts.Add(time.Hour*2), 0) if err != nil { t.Fatal(err) } - e5, err := Create(priv, []byte("dog"), 4, ts.Add(time.Hour*3)) + e5, err := Create(priv, []byte("dog"), 4, ts.Add(time.Hour*3), 0) if err != nil { t.Fatal(err) } - e6, err := Create(priv, []byte("fish"), 4, ts.Add(time.Hour*3)) + e6, err := Create(priv, []byte("fish"), 4, ts.Add(time.Hour*3), 0) if err != nil { t.Fatal(err) } diff --git a/ipns/validate_test.go b/ipns/validate_test.go index 741d20bc1..276e6d4da 100644 --- a/ipns/validate_test.go +++ b/ipns/validate_test.go @@ -1,6 +1,8 @@ package ipns import ( + "bytes" + "errors" "fmt" "math/rand" "strings" @@ -9,6 +11,10 @@ import ( pb "github.com/ipfs/go-ipns/pb" + ipldcodec "github.com/ipld/go-ipld-prime/multicodec" + basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/multiformats/go-multicodec" + proto "github.com/gogo/protobuf/proto" u "github.com/ipfs/go-ipfs-util" ci "github.com/libp2p/go-libp2p-core/crypto" @@ -44,7 +50,7 @@ func testValidatorCaseMatchFunc(t *testing.T, priv ci.PrivKey, kbook pstore.KeyB data := val if data == nil { p := []byte("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") - entry, err := Create(priv, p, 1, eol) + entry, err := Create(priv, p, 1, eol, 0) if err != nil { t.Fatal(err) } @@ -64,7 +70,9 @@ func TestValidator(t *testing.T) { priv, id, _ := genKeys(t) priv2, id2, _ := genKeys(t) kbook := pstoremem.NewPeerstore() - kbook.AddPubKey(id, priv.GetPublic()) + if err := kbook.AddPubKey(id, priv.GetPublic()); err != nil { + t.Fatal(err) + } emptyKbook := pstoremem.NewPeerstore() testValidatorCase(t, priv, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour), nil) @@ -95,7 +103,7 @@ func TestEmbeddedPubKeyValidate(t *testing.T) { priv, _, ipnsk := genKeys(t) - entry, err := Create(priv, pth, 1, goodeol) + entry, err := Create(priv, pth, 1, goodeol, 0) if err != nil { t.Fatal(err) } @@ -147,7 +155,7 @@ func TestPeerIDPubKeyValidate(t *testing.T) { ipnsk := "/ipns/" + string(pid) - entry, err := Create(sk, pth, 1, goodeol) + entry, err := Create(sk, pth, 1, goodeol, 0) if err != nil { t.Fatal(err) } @@ -160,6 +168,210 @@ func TestPeerIDPubKeyValidate(t *testing.T) { testValidatorCase(t, sk, kbook, ipnsk, dataNoKey, goodeol, nil) } +func TestBothSignatureVersionsValidate(t *testing.T) { + goodeol := time.Now().Add(time.Hour) + + sk, pk, err := ci.GenerateEd25519Key(rand.New(rand.NewSource(42))) + if err != nil { + t.Fatal(err) + } + + path1 := []byte("/path/1") + entry, err := Create(sk, path1, 1, goodeol, 0) + if err != nil { + t.Fatal(err) + } + + if err := Validate(pk, entry); err != nil { + t.Fatal(err) + } + + entry.SignatureV2 = nil + if err := Validate(pk, entry); err != nil { + t.Fatal(err) + } + + entry.SignatureV1 = nil + if err := Validate(pk, entry); !errors.Is(err, ErrSignature) { + t.Fatal(err) + } +} + +func TestNewSignatureVersionPreferred(t *testing.T) { + goodeol := time.Now().Add(time.Hour) + + sk, pk, err := ci.GenerateEd25519Key(rand.New(rand.NewSource(42))) + if err != nil { + t.Fatal(err) + } + + pid, err := peer.IDFromPublicKey(pk) + if err != nil { + t.Fatal(err) + } + + ipnsk := "/ipns/" + string(pid) + + path1 := []byte("/path/1") + entry1, err := Create(sk, path1, 1, goodeol, 0) + if err != nil { + t.Fatal(err) + } + + path2 := []byte("/path/2") + entry2, err := Create(sk, path2, 2, goodeol, 0) + if err != nil { + t.Fatal(err) + } + + if err := Validate(pk, entry1); err != nil { + t.Fatal(err) + } + + if err := Validate(pk, entry2); err != nil { + t.Fatal(err) + } + + v := Validator{} + best, err := v.Select(ipnsk, [][]byte{mustMarshal(t, entry1), mustMarshal(t, entry2)}) + if err != nil { + t.Fatal(err) + } + if best != 1 { + t.Fatal("entry2 should be better than entry1") + } + + // Having only the v1 signature should be valid + entry2.SignatureV2 = nil + if err := Validate(pk, entry2); err != nil { + t.Fatal(err) + } + + // However the v2 signature should be preferred + best, err = v.Select(ipnsk, [][]byte{mustMarshal(t, entry1), mustMarshal(t, entry2)}) + if err != nil { + t.Fatal(err) + } + if best != 0 { + t.Fatal("entry1 should be better than entry2") + } + + // Having a missing v1 signature is acceptable as long as there is a valid v2 signature + entry1.SignatureV1 = nil + if err := Validate(pk, entry1); err != nil { + t.Fatal(err) + } + + // Having an invalid v1 signature is acceptable as long as there is a valid v2 signature + entry1.SignatureV1 = []byte("garbage") + if err := Validate(pk, entry1); err != nil { + t.Fatal(err) + } +} + +func TestCborDataCanonicalization(t *testing.T) { + goodeol := time.Now().Add(time.Hour) + + sk, pk, err := ci.GenerateEd25519Key(rand.New(rand.NewSource(42))) + if err != nil { + t.Fatal(err) + } + + path := append([]byte("/path/1"), 0x00) + seqnum := uint64(1) + entry, err := Create(sk, path, seqnum, goodeol, time.Hour) + if err != nil { + t.Fatal(err) + } + + if err := Validate(pk, entry); err != nil { + t.Fatal(err) + } + + dec, err := ipldcodec.LookupDecoder(uint64(multicodec.DagCbor)) + if err != nil { + t.Fatal(err) + } + + ndbuilder := basicnode.Prototype__Map{}.NewBuilder() + if err := dec(ndbuilder, bytes.NewReader(entry.GetData())); err != nil { + t.Fatal(err) + } + + nd := ndbuilder.Build() + iter := nd.MapIterator() + var fields []string + for !iter.Done() { + k, v, err := iter.Next() + if err != nil { + t.Fatal(err) + } + kstr, err := k.AsString() + if err != nil { + t.Fatal(err) + } + + switch kstr { + case value: + b, err := v.AsBytes() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(path, b) { + t.Fatal("value did not match") + } + case sequence: + s, err := v.AsInt() + if err != nil { + t.Fatal(err) + } + if uint64(s) != seqnum { + t.Fatal("sequence numbers did not match") + } + case validity: + val, err := v.AsBytes() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(val, []byte(u.FormatRFC3339(goodeol))) { + t.Fatal("validity did not match") + } + case validityType: + vt, err := v.AsInt() + if err != nil { + t.Fatal(err) + } + if uint64(vt) != 0 { + t.Fatal("validity types did not match") + } + case ttl: + ttlVal, err := v.AsInt() + if err != nil { + t.Fatal(err) + } + // TODO: test non-zero TTL + if uint64(ttlVal) != uint64(time.Hour.Nanoseconds()) { + t.Fatal("TTLs did not match") + } + } + + fields = append(fields, kstr) + } + + // Check for map sort order (i.e. by length then by value) + expectedOrder := []string{"TTL", "Value", "Sequence", "Validity", "ValidityType"} + if len(fields) != len(expectedOrder) { + t.Fatal("wrong number of fields") + } + + for i, f := range fields { + expected := expectedOrder[i] + if f != expected { + t.Fatalf("expected %s, got %s", expected, f) + } + } +} + func genKeys(t *testing.T) (ci.PrivKey, peer.ID, string) { sr := u.NewTimeSeededRand() priv, _, err := ci.GenerateKeyPairWithReader(ci.RSA, 2048, sr) From 735d02dc733b022b175bf53f150789b9ea1e08a0 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Tue, 11 May 2021 15:27:54 -0400 Subject: [PATCH 3380/3817] chore: update dep This commit was moved from ipfs/go-namesys@8af847d76a2e4ab2a1283889ee02d5f898ae3981 --- namesys/ipns_resolver_validation_test.go | 8 ++++---- namesys/publisher.go | 13 +++++-------- namesys/publisher_test.go | 2 +- namesys/resolve_test.go | 4 ++-- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 5dbfabf9c..d896b9e0d 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -57,7 +57,7 @@ func testResolverValidation(t *testing.T, keyType int) { priv, id, _, ipnsDHTPath := genKeys(t, keyType) ts := time.Now() p := []byte("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") - entry, err := createIPNSRecordWithEmbeddedPublicKey(priv, p, 1, ts.Add(time.Hour)) + entry, err := createIPNSRecordWithEmbeddedPublicKey(priv, p, 1, ts.Add(time.Hour), 0) if err != nil { t.Fatal(err) } @@ -77,7 +77,7 @@ func testResolverValidation(t *testing.T, keyType int) { t.Fatalf("Mismatch between published path %s and resolved path %s", p, resp) } // Create expired entry - expiredEntry, err := createIPNSRecordWithEmbeddedPublicKey(priv, p, 1, ts.Add(-1*time.Hour)) + expiredEntry, err := createIPNSRecordWithEmbeddedPublicKey(priv, p, 1, ts.Add(-1*time.Hour), 0) if err != nil { t.Fatal(err) } @@ -146,8 +146,8 @@ func genKeys(t *testing.T, keyType int) (ci.PrivKey, peer.ID, string, string) { return sk, id, PkKeyForID(id), ipns.RecordKey(id) } -func createIPNSRecordWithEmbeddedPublicKey(sk ci.PrivKey, val []byte, seq uint64, eol time.Time) (*ipns_pb.IpnsEntry, error) { - entry, err := ipns.Create(sk, val, seq, eol) +func createIPNSRecordWithEmbeddedPublicKey(sk ci.PrivKey, val []byte, seq uint64, eol time.Time, ttl time.Duration) (*ipns_pb.IpnsEntry, error) { + entry, err := ipns.Create(sk, val, seq, eol, ttl) if err != nil { return nil, err } diff --git a/namesys/publisher.go b/namesys/publisher.go index edf57375a..f67a8bf52 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -162,19 +162,16 @@ func (p *IpnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value pa seqno++ } + // Set the TTL + // TODO: Make this less hacky. + ttl, _ := checkCtxTTL(ctx) + // Create record - entry, err := ipns.Create(k, []byte(value), seqno, eol) + entry, err := ipns.Create(k, []byte(value), seqno, eol, ttl) if err != nil { return nil, err } - // Set the TTL - // TODO: Make this less hacky. - ttl, ok := checkCtxTTL(ctx) - if ok { - entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds())) - } - data, err := proto.Marshal(entry) if err != nil { return nil, err diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index afc9efcc2..844ed86ed 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -76,7 +76,7 @@ func testNamekeyPublisher(t *testing.T, keyType int, expectedErr error, expected serv := mockrouting.NewServer() r := serv.ClientWithDatastore(context.Background(), &identity{p}, dstore) - entry, err := ipns.Create(privKey, value, seqnum, eol) + entry, err := ipns.Create(privKey, value, seqnum, eol, 0) if err != nil { t.Fatal(err) } diff --git a/namesys/resolve_test.go b/namesys/resolve_test.go index f8b243669..b654936c4 100644 --- a/namesys/resolve_test.go +++ b/namesys/resolve_test.go @@ -54,7 +54,7 @@ func TestPrexistingExpiredRecord(t *testing.T) { h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN") eol := time.Now().Add(time.Hour * -1) - entry, err := ipns.Create(identity.PrivateKey(), []byte(h), 0, eol) + entry, err := ipns.Create(identity.PrivateKey(), []byte(h), 0, eol, 0) if err != nil { t.Fatal(err) } @@ -87,7 +87,7 @@ func TestPrexistingRecord(t *testing.T) { // Make a good record and put it in the datastore h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN") eol := time.Now().Add(time.Hour) - entry, err := ipns.Create(identity.PrivateKey(), []byte(h), 0, eol) + entry, err := ipns.Create(identity.PrivateKey(), []byte(h), 0, eol, 0) if err != nil { t.Fatal(err) } From 259f6afee25e8018564d1eb3ef897f5450f26226 Mon Sep 17 00:00:00 2001 From: gammazero Date: Wed, 12 May 2021 15:33:46 -0700 Subject: [PATCH 3381/3817] Remove old ipldpinner that has been replaced by dspinner - Remove old ipld pinner code - Remove pin conversion package - Remove ipldpinner portion of benchmarks This commit was moved from ipfs/go-ipfs-pinner@14ba63732f5e6a71c5deca423faef51fc5ecac82 --- pinning/pinner/dspinner/pin_test.go | 36 -- pinning/pinner/ipldpinner/pin.go | 549 ------------------------- pinning/pinner/ipldpinner/pin_test.go | 526 ----------------------- pinning/pinner/ipldpinner/set.go | 334 --------------- pinning/pinner/ipldpinner/set_test.go | 100 ----- pinning/pinner/pinconv/pinconv.go | 127 ------ pinning/pinner/pinconv/pinconv_test.go | 179 -------- 7 files changed, 1851 deletions(-) delete mode 100644 pinning/pinner/ipldpinner/pin.go delete mode 100644 pinning/pinner/ipldpinner/pin_test.go delete mode 100644 pinning/pinner/ipldpinner/set.go delete mode 100644 pinning/pinner/ipldpinner/set_test.go delete mode 100644 pinning/pinner/pinconv/pinconv.go delete mode 100644 pinning/pinner/pinconv/pinconv_test.go diff --git a/pinning/pinner/dspinner/pin_test.go b/pinning/pinner/dspinner/pin_test.go index 40e2c70ca..46a2f94a5 100644 --- a/pinning/pinner/dspinner/pin_test.go +++ b/pinning/pinner/dspinner/pin_test.go @@ -18,7 +18,6 @@ import ( blockstore "github.com/ipfs/go-ipfs-blockstore" offline "github.com/ipfs/go-ipfs-exchange-offline" ipfspin "github.com/ipfs/go-ipfs-pinner" - "github.com/ipfs/go-ipfs-pinner/ipldpinner" util "github.com/ipfs/go-ipfs-util" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" @@ -951,19 +950,11 @@ func BenchmarkNthPin(b *testing.B) { if err != nil { panic(err.Error()) } - pinnerIPLD, err := ipldpinner.New(dstore, dserv, dserv) - if err != nil { - panic(err.Error()) - } for count := 1000; count <= 10000; count += 1000 { b.Run(fmt.Sprint("PinDS-", count), func(b *testing.B) { benchmarkNthPin(b, count, pinner, dserv) }) - - b.Run(fmt.Sprint("PinIPLD-", count), func(b *testing.B) { - benchmarkNthPin(b, count, pinnerIPLD, dserv) - }) } } @@ -1011,15 +1002,6 @@ func BenchmarkNPins(b *testing.B) { } benchmarkNPins(b, count, pinner, dserv) }) - - b.Run(fmt.Sprint("PinIPLD-", count), func(b *testing.B) { - dstore, dserv := makeStore() - pinner, err := ipldpinner.New(dstore, dserv, dserv) - if err != nil { - panic(err.Error()) - } - benchmarkNPins(b, count, pinner, dserv) - }) } } @@ -1061,15 +1043,6 @@ func BenchmarkNUnpins(b *testing.B) { } benchmarkNUnpins(b, count, pinner, dserv) }) - - b.Run(fmt.Sprint("UninIPLD-", count), func(b *testing.B) { - dstore, dserv := makeStore() - pinner, err := ipldpinner.New(dstore, dserv, dserv) - if err != nil { - panic(err.Error()) - } - benchmarkNUnpins(b, count, pinner, dserv) - }) } } @@ -1111,15 +1084,6 @@ func BenchmarkPinAll(b *testing.B) { } benchmarkPinAll(b, count, pinner, dserv) }) - - b.Run(fmt.Sprint("PinAllIPLD-", count), func(b *testing.B) { - dstore, dserv := makeStore() - pinner, err := ipldpinner.New(dstore, dserv, dserv) - if err != nil { - panic(err.Error()) - } - benchmarkPinAll(b, count, pinner, dserv) - }) } } diff --git a/pinning/pinner/ipldpinner/pin.go b/pinning/pinner/ipldpinner/pin.go deleted file mode 100644 index 562083698..000000000 --- a/pinning/pinner/ipldpinner/pin.go +++ /dev/null @@ -1,549 +0,0 @@ -// Package ipldpinner implements structures and methods to keep track of -// which objects a user wants to keep stored locally. This implementation -// stores pin information in a mdag structure. -package ipldpinner - -import ( - "context" - "fmt" - "os" - "sync" - "time" - - cid "github.com/ipfs/go-cid" - ds "github.com/ipfs/go-datastore" - ipld "github.com/ipfs/go-ipld-format" - logging "github.com/ipfs/go-log" - "github.com/ipfs/go-merkledag" - mdag "github.com/ipfs/go-merkledag" - "github.com/ipfs/go-merkledag/dagutils" - - ipfspinner "github.com/ipfs/go-ipfs-pinner" -) - -const loadTimeout = 5 * time.Second - -var log = logging.Logger("pin") - -var pinDatastoreKey = ds.NewKey("/local/pins") - -var emptyKey cid.Cid - -var linkDirect, linkRecursive, linkInternal string - -func init() { - e, err := cid.Decode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n") - if err != nil { - log.Error("failed to decode empty key constant") - os.Exit(1) - } - emptyKey = e - - directStr, ok := ipfspinner.ModeToString(ipfspinner.Direct) - if !ok { - panic("could not find Direct pin enum") - } - linkDirect = directStr - - recursiveStr, ok := ipfspinner.ModeToString(ipfspinner.Recursive) - if !ok { - panic("could not find Recursive pin enum") - } - linkRecursive = recursiveStr - - internalStr, ok := ipfspinner.ModeToString(ipfspinner.Internal) - if !ok { - panic("could not find Internal pin enum") - } - linkInternal = internalStr -} - -// pinner implements the Pinner interface -type pinner struct { - lock sync.RWMutex - recursePin *cid.Set - directPin *cid.Set - - // Track the keys used for storing the pinning state, so gc does - // not delete them. - internalPin *cid.Set - dserv ipld.DAGService - internal ipld.DAGService // dagservice used to store internal objects - dstore ds.Datastore -} - -var _ ipfspinner.Pinner = (*pinner)(nil) - -type syncDAGService interface { - ipld.DAGService - Sync() error -} - -// New creates a new pinner using the given datastore as a backend, and loads -// the pinner's keysets from the datastore -func New(dstore ds.Datastore, dserv, internal ipld.DAGService) (*pinner, error) { - rootKey, err := dstore.Get(pinDatastoreKey) - if err != nil { - if err == ds.ErrNotFound { - return &pinner{ - recursePin: cid.NewSet(), - directPin: cid.NewSet(), - internalPin: cid.NewSet(), - dserv: dserv, - internal: internal, - dstore: dstore, - }, nil - } - return nil, err - } - rootCid, err := cid.Cast(rootKey) - if err != nil { - return nil, err - } - - ctx, cancel := context.WithTimeout(context.TODO(), loadTimeout) - defer cancel() - - root, err := internal.Get(ctx, rootCid) - if err != nil { - return nil, fmt.Errorf("cannot find pinning root object: %v", err) - } - - rootpb, ok := root.(*mdag.ProtoNode) - if !ok { - return nil, mdag.ErrNotProtobuf - } - - internalset := cid.NewSet() - internalset.Add(rootCid) - recordInternal := internalset.Add - - // load recursive set - recurseKeys, err := loadSet(ctx, internal, rootpb, linkRecursive, recordInternal) - if err != nil { - return nil, fmt.Errorf("cannot load recursive pins: %v", err) - } - - // load direct set - directKeys, err := loadSet(ctx, internal, rootpb, linkDirect, recordInternal) - if err != nil { - return nil, fmt.Errorf("cannot load direct pins: %v", err) - } - - return &pinner{ - // assign pinsets - recursePin: cidSetWithValues(recurseKeys), - directPin: cidSetWithValues(directKeys), - internalPin: internalset, - // assign services - dserv: dserv, - dstore: dstore, - internal: internal, - }, nil -} - -// LoadKeys reads the pinned CIDs and sends them on the given channel. This is -// used to read pins without loading them all into memory. -func LoadKeys(ctx context.Context, dstore ds.Datastore, dserv, internal ipld.DAGService, recursive bool, keyChan chan<- cid.Cid) error { - rootKey, err := dstore.Get(pinDatastoreKey) - if err != nil { - if err == ds.ErrNotFound { - return nil - } - return err - } - rootCid, err := cid.Cast(rootKey) - if err != nil { - return err - } - - root, err := internal.Get(ctx, rootCid) - if err != nil { - return fmt.Errorf("cannot find pinning root object: %v", err) - } - - rootpb, ok := root.(*mdag.ProtoNode) - if !ok { - return mdag.ErrNotProtobuf - } - - var linkName string - if recursive { - linkName = linkRecursive - } else { - linkName = linkDirect - } - - return loadSetChan(ctx, internal, rootpb, linkName, keyChan) -} - -// Pin the given node, optionally recursive -func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { - err := p.dserv.Add(ctx, node) - if err != nil { - return err - } - - c := node.Cid() - - p.lock.Lock() - defer p.lock.Unlock() - - if recurse { - if p.recursePin.Has(c) { - return nil - } - - p.lock.Unlock() - // temporary unlock to fetch the entire graph - err := mdag.FetchGraph(ctx, c, p.dserv) - p.lock.Lock() - if err != nil { - return err - } - - if p.recursePin.Has(c) { - return nil - } - - if p.directPin.Has(c) { - p.directPin.Remove(c) - } - - p.recursePin.Add(c) - } else { - if p.recursePin.Has(c) { - return fmt.Errorf("%s already pinned recursively", c.String()) - } - - p.directPin.Add(c) - } - return nil -} - -// ErrNotPinned is returned when trying to unpin items which are not pinned. -var ErrNotPinned = fmt.Errorf("not pinned or pinned indirectly") - -// Unpin a given key -func (p *pinner) Unpin(ctx context.Context, c cid.Cid, recursive bool) error { - p.lock.Lock() - defer p.lock.Unlock() - if p.recursePin.Has(c) { - if !recursive { - return fmt.Errorf("%s is pinned recursively", c) - } - p.recursePin.Remove(c) - return nil - } - if p.directPin.Has(c) { - p.directPin.Remove(c) - return nil - } - return ErrNotPinned -} - -func (p *pinner) isInternalPin(c cid.Cid) bool { - return p.internalPin.Has(c) -} - -// IsPinned returns whether or not the given key is pinned -// and an explanation of why its pinned -func (p *pinner) IsPinned(ctx context.Context, c cid.Cid) (string, bool, error) { - p.lock.RLock() - defer p.lock.RUnlock() - return p.isPinnedWithType(ctx, c, ipfspinner.Any) -} - -// IsPinnedWithType returns whether or not the given cid is pinned with the -// given pin type, as well as returning the type of pin its pinned with. -func (p *pinner) IsPinnedWithType(ctx context.Context, c cid.Cid, mode ipfspinner.Mode) (string, bool, error) { - p.lock.RLock() - defer p.lock.RUnlock() - return p.isPinnedWithType(ctx, c, mode) -} - -// isPinnedWithType is the implementation of IsPinnedWithType that does not lock. -// intended for use by other pinned methods that already take locks -func (p *pinner) isPinnedWithType(ctx context.Context, c cid.Cid, mode ipfspinner.Mode) (string, bool, error) { - switch mode { - case ipfspinner.Any, ipfspinner.Direct, ipfspinner.Indirect, ipfspinner.Recursive, ipfspinner.Internal: - default: - err := fmt.Errorf("invalid Pin Mode '%d', must be one of {%d, %d, %d, %d, %d}", - mode, ipfspinner.Direct, ipfspinner.Indirect, ipfspinner.Recursive, ipfspinner.Internal, ipfspinner.Any) - return "", false, err - } - if (mode == ipfspinner.Recursive || mode == ipfspinner.Any) && p.recursePin.Has(c) { - return linkRecursive, true, nil - } - if mode == ipfspinner.Recursive { - return "", false, nil - } - - if (mode == ipfspinner.Direct || mode == ipfspinner.Any) && p.directPin.Has(c) { - return linkDirect, true, nil - } - if mode == ipfspinner.Direct { - return "", false, nil - } - - if (mode == ipfspinner.Internal || mode == ipfspinner.Any) && p.isInternalPin(c) { - return linkInternal, true, nil - } - if mode == ipfspinner.Internal { - return "", false, nil - } - - // Default is Indirect - visitedSet := cid.NewSet() - for _, rc := range p.recursePin.Keys() { - has, err := hasChild(ctx, p.dserv, rc, c, visitedSet.Visit) - if err != nil { - return "", false, err - } - if has { - return rc.String(), true, nil - } - } - return "", false, nil -} - -// CheckIfPinned Checks if a set of keys are pinned, more efficient than -// calling IsPinned for each key, returns the pinned status of cid(s) -func (p *pinner) CheckIfPinned(ctx context.Context, cids ...cid.Cid) ([]ipfspinner.Pinned, error) { - p.lock.RLock() - defer p.lock.RUnlock() - pinned := make([]ipfspinner.Pinned, 0, len(cids)) - toCheck := cid.NewSet() - - // First check for non-Indirect pins directly - for _, c := range cids { - if p.recursePin.Has(c) { - pinned = append(pinned, ipfspinner.Pinned{Key: c, Mode: ipfspinner.Recursive}) - } else if p.directPin.Has(c) { - pinned = append(pinned, ipfspinner.Pinned{Key: c, Mode: ipfspinner.Direct}) - } else if p.isInternalPin(c) { - pinned = append(pinned, ipfspinner.Pinned{Key: c, Mode: ipfspinner.Internal}) - } else { - toCheck.Add(c) - } - } - - // Now walk all recursive pins to check for indirect pins - visited := cid.NewSet() - for _, rk := range p.recursePin.Keys() { - err := merkledag.Walk(ctx, merkledag.GetLinksWithDAG(p.dserv), rk, func(c cid.Cid) bool { - if toCheck.Len() == 0 || !visited.Visit(c) { - return false - } - - if toCheck.Has(c) { - pinned = append(pinned, ipfspinner.Pinned{Key: c, Mode: ipfspinner.Indirect, Via: rk}) - toCheck.Remove(c) - } - - return true - }, merkledag.Concurrent()) - if err != nil { - return nil, err - } - if toCheck.Len() == 0 { - break - } - } - - // Anything left in toCheck is not pinned - for _, k := range toCheck.Keys() { - pinned = append(pinned, ipfspinner.Pinned{Key: k, Mode: ipfspinner.NotPinned}) - } - - return pinned, nil -} - -// RemovePinWithMode is for manually editing the pin structure. -// Use with care! If used improperly, garbage collection may not -// be successful. -func (p *pinner) RemovePinWithMode(c cid.Cid, mode ipfspinner.Mode) { - p.lock.Lock() - defer p.lock.Unlock() - switch mode { - case ipfspinner.Direct: - p.directPin.Remove(c) - case ipfspinner.Recursive: - p.recursePin.Remove(c) - default: - // programmer error, panic OK - panic("unrecognized pin type") - } -} - -func cidSetWithValues(cids []cid.Cid) *cid.Set { - out := cid.NewSet() - for _, c := range cids { - out.Add(c) - } - return out -} - -// DirectKeys returns a slice containing the directly pinned keys -func (p *pinner) DirectKeys(ctx context.Context) ([]cid.Cid, error) { - p.lock.RLock() - defer p.lock.RUnlock() - - return p.directPin.Keys(), nil -} - -// RecursiveKeys returns a slice containing the recursively pinned keys -func (p *pinner) RecursiveKeys(ctx context.Context) ([]cid.Cid, error) { - p.lock.RLock() - defer p.lock.RUnlock() - - return p.recursePin.Keys(), nil -} - -// Update updates a recursive pin from one cid to another -// this is more efficient than simply pinning the new one and unpinning the -// old one -func (p *pinner) Update(ctx context.Context, from, to cid.Cid, unpin bool) error { - if from == to { - // Nothing to do. Don't remove this check or we'll end up - // _removing_ the pin. - // - // See #6648 - return nil - } - - p.lock.Lock() - defer p.lock.Unlock() - - if !p.recursePin.Has(from) { - return fmt.Errorf("'from' cid was not recursively pinned already") - } - - // Temporarily unlock while we fetch the differences. - p.lock.Unlock() - err := dagutils.DiffEnumerate(ctx, p.dserv, from, to) - p.lock.Lock() - - if err != nil { - return err - } - - p.recursePin.Add(to) - if unpin { - p.recursePin.Remove(from) - } - return nil -} - -// Flush encodes and writes pinner keysets to the datastore -func (p *pinner) Flush(ctx context.Context) error { - p.lock.Lock() - defer p.lock.Unlock() - - internalset := cid.NewSet() - recordInternal := internalset.Add - - root := &mdag.ProtoNode{} - { - n, err := storeSet(ctx, p.internal, p.directPin.Keys(), recordInternal) - if err != nil { - return err - } - if err := root.AddNodeLink(linkDirect, n); err != nil { - return err - } - } - - { - n, err := storeSet(ctx, p.internal, p.recursePin.Keys(), recordInternal) - if err != nil { - return err - } - if err := root.AddNodeLink(linkRecursive, n); err != nil { - return err - } - } - - // add the empty node, its referenced by the pin sets but never created - err := p.internal.Add(ctx, new(mdag.ProtoNode)) - if err != nil { - return err - } - - err = p.internal.Add(ctx, root) - if err != nil { - return err - } - - k := root.Cid() - - internalset.Add(k) - - if syncDServ, ok := p.dserv.(syncDAGService); ok { - if err := syncDServ.Sync(); err != nil { - return fmt.Errorf("cannot sync pinned data: %v", err) - } - } - - if syncInternal, ok := p.internal.(syncDAGService); ok { - if err := syncInternal.Sync(); err != nil { - return fmt.Errorf("cannot sync pinning data: %v", err) - } - } - - if err := p.dstore.Put(pinDatastoreKey, k.Bytes()); err != nil { - return fmt.Errorf("cannot store pin state: %v", err) - } - if err := p.dstore.Sync(pinDatastoreKey); err != nil { - return fmt.Errorf("cannot sync pin state: %v", err) - } - p.internalPin = internalset - return nil -} - -// InternalPins returns all cids kept pinned for the internal state of the -// pinner -func (p *pinner) InternalPins(ctx context.Context) ([]cid.Cid, error) { - p.lock.Lock() - defer p.lock.Unlock() - return p.internalPin.Keys(), nil -} - -// PinWithMode allows the user to have fine grained control over pin -// counts -func (p *pinner) PinWithMode(c cid.Cid, mode ipfspinner.Mode) { - p.lock.Lock() - defer p.lock.Unlock() - switch mode { - case ipfspinner.Recursive: - p.recursePin.Add(c) - case ipfspinner.Direct: - p.directPin.Add(c) - } -} - -// hasChild recursively looks for a Cid among the children of a root Cid. -// The visit function can be used to shortcut already-visited branches. -func hasChild(ctx context.Context, ng ipld.NodeGetter, root cid.Cid, child cid.Cid, visit func(cid.Cid) bool) (bool, error) { - links, err := ipld.GetLinks(ctx, ng, root) - if err != nil { - return false, err - } - for _, lnk := range links { - c := lnk.Cid - if lnk.Cid.Equals(child) { - return true, nil - } - if visit(c) { - has, err := hasChild(ctx, ng, c, child, visit) - if err != nil { - return false, err - } - - if has { - return has, nil - } - } - } - return false, nil -} diff --git a/pinning/pinner/ipldpinner/pin_test.go b/pinning/pinner/ipldpinner/pin_test.go deleted file mode 100644 index 3c61d41fd..000000000 --- a/pinning/pinner/ipldpinner/pin_test.go +++ /dev/null @@ -1,526 +0,0 @@ -package ipldpinner - -import ( - "context" - "io" - "testing" - "time" - - bs "github.com/ipfs/go-blockservice" - mdag "github.com/ipfs/go-merkledag" - - cid "github.com/ipfs/go-cid" - ds "github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-datastore/sync" - blockstore "github.com/ipfs/go-ipfs-blockstore" - offline "github.com/ipfs/go-ipfs-exchange-offline" - pin "github.com/ipfs/go-ipfs-pinner" - util "github.com/ipfs/go-ipfs-util" -) - -var rand = util.NewTimeSeededRand() - -func randNode() (*mdag.ProtoNode, cid.Cid) { - nd := new(mdag.ProtoNode) - nd.SetData(make([]byte, 32)) - _, err := io.ReadFull(rand, nd.Data()) - if err != nil { - panic(err) - } - k := nd.Cid() - return nd, k -} - -func assertPinned(t *testing.T, p pin.Pinner, c cid.Cid, failmsg string) { - _, pinned, err := p.IsPinned(context.Background(), c) - if err != nil { - t.Fatal(err) - } - - if !pinned { - t.Fatal(failmsg) - } -} - -func assertUnpinned(t *testing.T, p pin.Pinner, c cid.Cid, failmsg string) { - _, pinned, err := p.IsPinned(context.Background(), c) - if err != nil { - t.Fatal(err) - } - - if pinned { - t.Fatal(failmsg) - } -} - -func TestPinnerBasic(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - dstore := dssync.MutexWrap(ds.NewMapDatastore()) - bstore := blockstore.NewBlockstore(dstore) - bserv := bs.New(bstore, offline.Exchange(bstore)) - - dserv := mdag.NewDAGService(bserv) - - p, err := New(dstore, dserv, dserv) - if err != nil { - t.Fatal(err) - } - - a, ak := randNode() - err = dserv.Add(ctx, a) - if err != nil { - t.Fatal(err) - } - - // Pin A{} - err = p.Pin(ctx, a, false) - if err != nil { - t.Fatal(err) - } - - assertPinned(t, p, ak, "Failed to find key") - - // create new node c, to be indirectly pinned through b - c, _ := randNode() - err = dserv.Add(ctx, c) - if err != nil { - t.Fatal(err) - } - ck := c.Cid() - - // Create new node b, to be parent to a and c - b, _ := randNode() - err = b.AddNodeLink("child", a) - if err != nil { - t.Fatal(err) - } - - err = b.AddNodeLink("otherchild", c) - if err != nil { - t.Fatal(err) - } - - err = dserv.Add(ctx, b) - if err != nil { - t.Fatal(err) - } - bk := b.Cid() - - // recursively pin B{A,C} - err = p.Pin(ctx, b, true) - if err != nil { - t.Fatal(err) - } - - assertPinned(t, p, ck, "child of recursively pinned node not found") - - assertPinned(t, p, bk, "Recursively pinned node not found..") - - d, _ := randNode() - _ = d.AddNodeLink("a", a) - _ = d.AddNodeLink("c", c) - - e, _ := randNode() - _ = d.AddNodeLink("e", e) - - // Must be in dagserv for unpin to work - err = dserv.Add(ctx, e) - if err != nil { - t.Fatal(err) - } - err = dserv.Add(ctx, d) - if err != nil { - t.Fatal(err) - } - - // Add D{A,C,E} - err = p.Pin(ctx, d, true) - if err != nil { - t.Fatal(err) - } - - dk := d.Cid() - assertPinned(t, p, dk, "pinned node not found.") - - // Test recursive unpin - err = p.Unpin(ctx, dk, true) - if err != nil { - t.Fatal(err) - } - - err = p.Flush(ctx) - if err != nil { - t.Fatal(err) - } - - np, err := New(dstore, dserv, dserv) - if err != nil { - t.Fatal(err) - } - - // Test directly pinned - assertPinned(t, np, ak, "Could not find pinned node!") - - // Test recursively pinned - assertPinned(t, np, bk, "could not find recursively pinned node") - - // Test that LoadKeys returns the expected CIDs. - keyChan := make(chan cid.Cid) - go func() { - err = LoadKeys(ctx, dstore, dserv, dserv, true, keyChan) - close(keyChan) - }() - keys := map[cid.Cid]struct{}{} - for c := range keyChan { - keys[c] = struct{}{} - } - if err != nil { - t.Fatal(err) - } - recKeys, _ := np.RecursiveKeys(ctx) - if len(keys) != len(recKeys) { - t.Fatal("wrong number of recursive keys from LoadKeys") - } - for _, k := range recKeys { - if _, ok := keys[k]; !ok { - t.Fatal("LoadKeys did not return correct recursive keys") - } - } - - keyChan = make(chan cid.Cid) - go func() { - err = LoadKeys(ctx, dstore, dserv, dserv, false, keyChan) - close(keyChan) - }() - keys = map[cid.Cid]struct{}{} - for c := range keyChan { - keys[c] = struct{}{} - } - if err != nil { - t.Fatal(err) - } - dirKeys, _ := np.DirectKeys(ctx) - if len(keys) != len(dirKeys) { - t.Fatal("wrong number of direct keys from LoadKeys") - } - for _, k := range dirKeys { - if _, ok := keys[k]; !ok { - t.Fatal("LoadKeys did not return correct direct keys") - } - } - - cancel() - emptyDS := dssync.MutexWrap(ds.NewMapDatastore()) - - // Check key not in datastore - err = LoadKeys(ctx, emptyDS, dserv, dserv, true, nil) - if err != nil { - t.Fatal(err) - } - - // Check error on bad key - if err = emptyDS.Put(pinDatastoreKey, []byte("bad-cid")); err != nil { - panic(err) - } - if err = emptyDS.Sync(pinDatastoreKey); err != nil { - panic(err) - } - if err = LoadKeys(ctx, emptyDS, dserv, dserv, true, nil); err == nil { - t.Fatal("expected error") - } - - // Lookup dag that does not exist - noKey, err := cid.Decode("QmYff9iHR1Hz6wufVeJodzXqQm4pkK4QNS9ms8tyPKVWm1") - if err != nil { - panic(err) - } - if err = emptyDS.Put(pinDatastoreKey, noKey.Bytes()); err != nil { - panic(err) - } - if err = emptyDS.Sync(pinDatastoreKey); err != nil { - panic(err) - } - err = LoadKeys(ctx, emptyDS, dserv, dserv, true, nil) - if err == nil || err.Error() != "cannot find pinning root object: merkledag: not found" { - t.Fatal("did not get expected error") - } - - // Check error when node has no links - if err = emptyDS.Put(pinDatastoreKey, emptyKey.Bytes()); err != nil { - panic(err) - } - if err = emptyDS.Sync(pinDatastoreKey); err != nil { - panic(err) - } - if err = LoadKeys(ctx, emptyDS, dserv, dserv, true, nil); err == nil { - t.Fatal("expected error") - } -} - -func TestIsPinnedLookup(t *testing.T) { - // We are going to test that lookups work in pins which share - // the same branches. For that we will construct this tree: - // - // A5->A4->A3->A2->A1->A0 - // / / - // B------- / - // \ / - // C--------------- - // - // We will ensure that IsPinned works for all objects both when they - // are pinned and once they have been unpinned. - aBranchLen := 6 - if aBranchLen < 3 { - t.Fatal("set aBranchLen to at least 3") - } - - ctx := context.Background() - dstore := dssync.MutexWrap(ds.NewMapDatastore()) - bstore := blockstore.NewBlockstore(dstore) - bserv := bs.New(bstore, offline.Exchange(bstore)) - - dserv := mdag.NewDAGService(bserv) - - // TODO does pinner need to share datastore with blockservice? - p, err := New(dstore, dserv, dserv) - if err != nil { - t.Fatal(err) - } - - aNodes := make([]*mdag.ProtoNode, aBranchLen) - aKeys := make([]cid.Cid, aBranchLen) - for i := 0; i < aBranchLen; i++ { - a, _ := randNode() - if i >= 1 { - err := a.AddNodeLink("child", aNodes[i-1]) - if err != nil { - t.Fatal(err) - } - } - - err := dserv.Add(ctx, a) - if err != nil { - t.Fatal(err) - } - //t.Logf("a[%d] is %s", i, ak) - aNodes[i] = a - aKeys[i] = a.Cid() - } - - // Pin A5 recursively - if err := p.Pin(ctx, aNodes[aBranchLen-1], true); err != nil { - t.Fatal(err) - } - - // Create node B and add A3 as child - b, _ := randNode() - if err := b.AddNodeLink("mychild", aNodes[3]); err != nil { - t.Fatal(err) - } - - // Create C node - c, _ := randNode() - // Add A0 as child of C - if err := c.AddNodeLink("child", aNodes[0]); err != nil { - t.Fatal(err) - } - - // Add C - err = dserv.Add(ctx, c) - if err != nil { - t.Fatal(err) - } - ck := c.Cid() - //t.Logf("C is %s", ck) - - // Add C to B and Add B - if err := b.AddNodeLink("myotherchild", c); err != nil { - t.Fatal(err) - } - err = dserv.Add(ctx, b) - if err != nil { - t.Fatal(err) - } - bk := b.Cid() - //t.Logf("B is %s", bk) - - // Pin C recursively - - if err := p.Pin(ctx, c, true); err != nil { - t.Fatal(err) - } - - // Pin B recursively - - if err := p.Pin(ctx, b, true); err != nil { - t.Fatal(err) - } - - assertPinned(t, p, aKeys[0], "A0 should be pinned") - assertPinned(t, p, aKeys[1], "A1 should be pinned") - assertPinned(t, p, ck, "C should be pinned") - assertPinned(t, p, bk, "B should be pinned") - - // Unpin A5 recursively - if err := p.Unpin(ctx, aKeys[5], true); err != nil { - t.Fatal(err) - } - - assertPinned(t, p, aKeys[0], "A0 should still be pinned through B") - assertUnpinned(t, p, aKeys[4], "A4 should be unpinned") - - // Unpin B recursively - if err := p.Unpin(ctx, bk, true); err != nil { - t.Fatal(err) - } - assertUnpinned(t, p, bk, "B should be unpinned") - assertUnpinned(t, p, aKeys[1], "A1 should be unpinned") - assertPinned(t, p, aKeys[0], "A0 should still be pinned through C") -} - -func TestDuplicateSemantics(t *testing.T) { - ctx := context.Background() - dstore := dssync.MutexWrap(ds.NewMapDatastore()) - bstore := blockstore.NewBlockstore(dstore) - bserv := bs.New(bstore, offline.Exchange(bstore)) - - dserv := mdag.NewDAGService(bserv) - - p, err := New(dstore, dserv, dserv) - if err != nil { - t.Fatal(err) - } - - a, _ := randNode() - err = dserv.Add(ctx, a) - if err != nil { - t.Fatal(err) - } - - // pin is recursively - err = p.Pin(ctx, a, true) - if err != nil { - t.Fatal(err) - } - - // pinning directly should fail - err = p.Pin(ctx, a, false) - if err == nil { - t.Fatal("expected direct pin to fail") - } - - // pinning recursively again should succeed - err = p.Pin(ctx, a, true) - if err != nil { - t.Fatal(err) - } -} - -func TestFlush(t *testing.T) { - dstore := dssync.MutexWrap(ds.NewMapDatastore()) - bstore := blockstore.NewBlockstore(dstore) - bserv := bs.New(bstore, offline.Exchange(bstore)) - - dserv := mdag.NewDAGService(bserv) - p, err := New(dstore, dserv, dserv) - if err != nil { - t.Fatal(err) - } - _, k := randNode() - - p.PinWithMode(k, pin.Recursive) - if err := p.Flush(context.Background()); err != nil { - t.Fatal(err) - } - assertPinned(t, p, k, "expected key to still be pinned") -} - -func TestPinRecursiveFail(t *testing.T) { - ctx := context.Background() - dstore := dssync.MutexWrap(ds.NewMapDatastore()) - bstore := blockstore.NewBlockstore(dstore) - bserv := bs.New(bstore, offline.Exchange(bstore)) - dserv := mdag.NewDAGService(bserv) - - p, err := New(dstore, dserv, dserv) - if err != nil { - t.Fatal(err) - } - - a, _ := randNode() - b, _ := randNode() - err = a.AddNodeLink("child", b) - if err != nil { - t.Fatal(err) - } - - // NOTE: This isnt a time based test, we expect the pin to fail - mctx, cancel := context.WithTimeout(ctx, time.Millisecond) - defer cancel() - - err = p.Pin(mctx, a, true) - if err == nil { - t.Fatal("should have failed to pin here") - } - - err = dserv.Add(ctx, b) - if err != nil { - t.Fatal(err) - } - - err = dserv.Add(ctx, a) - if err != nil { - t.Fatal(err) - } - - // this one is time based... but shouldnt cause any issues - mctx, cancel = context.WithTimeout(ctx, time.Second) - defer cancel() - err = p.Pin(mctx, a, true) - if err != nil { - t.Fatal(err) - } -} - -func TestPinUpdate(t *testing.T) { - ctx := context.Background() - - dstore := dssync.MutexWrap(ds.NewMapDatastore()) - bstore := blockstore.NewBlockstore(dstore) - bserv := bs.New(bstore, offline.Exchange(bstore)) - - dserv := mdag.NewDAGService(bserv) - p, err := New(dstore, dserv, dserv) - if err != nil { - t.Fatal(err) - } - n1, c1 := randNode() - n2, c2 := randNode() - - if err := dserv.Add(ctx, n1); err != nil { - t.Fatal(err) - } - if err := dserv.Add(ctx, n2); err != nil { - t.Fatal(err) - } - - if err := p.Pin(ctx, n1, true); err != nil { - t.Fatal(err) - } - - if err := p.Update(ctx, c1, c2, true); err != nil { - t.Fatal(err) - } - - assertPinned(t, p, c2, "c2 should be pinned now") - assertUnpinned(t, p, c1, "c1 should no longer be pinned") - - if err := p.Update(ctx, c2, c1, false); err != nil { - t.Fatal(err) - } - - assertPinned(t, p, c2, "c2 should be pinned still") - assertPinned(t, p, c1, "c1 should be pinned now") -} diff --git a/pinning/pinner/ipldpinner/set.go b/pinning/pinner/ipldpinner/set.go deleted file mode 100644 index 51951a2c0..000000000 --- a/pinning/pinner/ipldpinner/set.go +++ /dev/null @@ -1,334 +0,0 @@ -package ipldpinner - -import ( - "bytes" - "context" - "encoding/binary" - "errors" - "fmt" - "hash/fnv" - "sort" - - "github.com/gogo/protobuf/proto" - cid "github.com/ipfs/go-cid" - ipld "github.com/ipfs/go-ipld-format" - "github.com/ipfs/go-merkledag" - - "github.com/ipfs/go-ipfs-pinner/internal/pb" -) - -const ( - // defaultFanout specifies the default number of fan-out links per layer - defaultFanout = 256 - - // maxItems is the maximum number of items that will fit in a single bucket - maxItems = 8192 -) - -func hash(seed uint32, c cid.Cid) uint32 { - var buf [4]byte - binary.LittleEndian.PutUint32(buf[:], seed) - h := fnv.New32a() - _, _ = h.Write(buf[:]) - _, _ = h.Write(c.Bytes()) - return h.Sum32() -} - -type itemIterator func() (c cid.Cid, ok bool) - -type keyObserver func(cid.Cid) - -type sortByHash struct { - links []*ipld.Link -} - -func (s sortByHash) Len() int { - return len(s.links) -} - -func (s sortByHash) Less(a, b int) bool { - return bytes.Compare(s.links[a].Cid.Bytes(), s.links[b].Cid.Bytes()) == -1 -} - -func (s sortByHash) Swap(a, b int) { - s.links[a], s.links[b] = s.links[b], s.links[a] -} - -func storeItems(ctx context.Context, dag ipld.DAGService, estimatedLen uint64, depth uint32, iter itemIterator, internalKeys keyObserver) (*merkledag.ProtoNode, error) { - // Each node wastes up to defaultFanout in empty links. - var leafLinks uint64 - if estimatedLen < maxItems { - leafLinks = estimatedLen - } - links := make([]*ipld.Link, defaultFanout, defaultFanout+leafLinks) - for i := 0; i < defaultFanout; i++ { - links[i] = &ipld.Link{Cid: emptyKey} - } - - // add emptyKey to our set of internal pinset objects - n := &merkledag.ProtoNode{} - n.SetLinks(links) - - internalKeys(emptyKey) - - hdr := &pb.Set{ - Version: 1, - Fanout: defaultFanout, - Seed: depth, - } - if err := writeHdr(n, hdr); err != nil { - return nil, err - } - - if estimatedLen < maxItems { - // it'll probably fit - links := n.Links() - for i := 0; i < maxItems; i++ { - k, ok := iter() - if !ok { - // all done - break - } - - links = append(links, &ipld.Link{Cid: k}) - } - - n.SetLinks(links) - - // sort by hash, also swap item Data - s := sortByHash{ - links: n.Links()[defaultFanout:], - } - sort.Stable(s) - } - - var hashed [][]cid.Cid - for { - // This loop essentially enumerates every single item in the set - // and maps them all into a set of buckets. Each bucket will be recursively - // turned into its own sub-set, and so on down the chain. Each sub-set - // gets added to the dagservice, and put into its place in a set nodes - // links array. - // - // Previously, the bucket was selected by taking an int32 from the hash of - // the input key + seed. This was erroneous as we would later be assigning - // the created sub-sets into an array of length 256 by the modulus of the - // int32 hash value with 256. This resulted in overwriting existing sub-sets - // and losing pins. The fix (a few lines down from this comment), is to - // map the hash value down to the 8 bit keyspace here while creating the - // buckets. This way, we avoid any overlapping later on. - k, ok := iter() - if !ok { - break - } - if hashed == nil { - hashed = make([][]cid.Cid, defaultFanout) - } - h := hash(depth, k) % defaultFanout - hashed[h] = append(hashed[h], k) - } - - for h, items := range hashed { - if len(items) == 0 { - // recursion base case - continue - } - - childIter := getCidListIterator(items) - - // recursively create a pinset from the items for this bucket index - child, err := storeItems(ctx, dag, uint64(len(items)), depth+1, childIter, internalKeys) - if err != nil { - return nil, err - } - - size, err := child.Size() - if err != nil { - return nil, err - } - - err = dag.Add(ctx, child) - if err != nil { - return nil, err - } - childKey := child.Cid() - - internalKeys(childKey) - - // overwrite the 'empty key' in the existing links array - n.Links()[h] = &ipld.Link{ - Cid: childKey, - Size: size, - } - } - return n, nil -} - -func readHdr(n *merkledag.ProtoNode) (*pb.Set, error) { - hdrLenRaw, consumed := binary.Uvarint(n.Data()) - if consumed <= 0 { - return nil, errors.New("invalid Set header length") - } - - pbdata := n.Data()[consumed:] - if hdrLenRaw > uint64(len(pbdata)) { - return nil, errors.New("impossibly large Set header length") - } - // as hdrLenRaw was <= an int, we now know it fits in an int - hdrLen := int(hdrLenRaw) - var hdr pb.Set - if err := proto.Unmarshal(pbdata[:hdrLen], &hdr); err != nil { - return nil, err - } - - if v := hdr.GetVersion(); v != 1 { - return nil, fmt.Errorf("unsupported Set version: %d", v) - } - if uint64(hdr.GetFanout()) > uint64(len(n.Links())) { - return nil, errors.New("impossibly large Fanout") - } - return &hdr, nil -} - -func writeHdr(n *merkledag.ProtoNode, hdr *pb.Set) error { - hdrData, err := proto.Marshal(hdr) - if err != nil { - return err - } - - // make enough space for the length prefix and the marshaled header data - data := make([]byte, binary.MaxVarintLen64, binary.MaxVarintLen64+len(hdrData)) - - // write the uvarint length of the header data - uvarlen := binary.PutUvarint(data, uint64(len(hdrData))) - - // append the actual protobuf data *after* the length value we wrote - data = append(data[:uvarlen], hdrData...) - - n.SetData(data) - return nil -} - -type walkerFunc func(idx int, link *ipld.Link) error - -func walkItems(ctx context.Context, dag ipld.DAGService, n *merkledag.ProtoNode, fn walkerFunc, children keyObserver) error { - hdr, err := readHdr(n) - if err != nil { - return err - } - // readHdr guarantees fanout is a safe value - fanout := hdr.GetFanout() - for i, l := range n.Links()[fanout:] { - if err = fn(i, l); err != nil { - return err - } - } - for _, l := range n.Links()[:fanout] { - c := l.Cid - if children != nil { - children(c) - } - if c.Equals(emptyKey) { - continue - } - subtree, err := l.GetNode(ctx, dag) - if err != nil { - return err - } - - stpb, ok := subtree.(*merkledag.ProtoNode) - if !ok { - return merkledag.ErrNotProtobuf - } - - if err = walkItems(ctx, dag, stpb, fn, children); err != nil { - return err - } - } - return nil -} - -func loadSet(ctx context.Context, dag ipld.DAGService, root *merkledag.ProtoNode, name string, internalKeys keyObserver) ([]cid.Cid, error) { - l, err := root.GetNodeLink(name) - if err != nil { - return nil, err - } - - lnkc := l.Cid - internalKeys(lnkc) - - n, err := l.GetNode(ctx, dag) - if err != nil { - return nil, err - } - - pbn, ok := n.(*merkledag.ProtoNode) - if !ok { - return nil, merkledag.ErrNotProtobuf - } - - var res []cid.Cid - walk := func(idx int, link *ipld.Link) error { - res = append(res, link.Cid) - return nil - } - - if err := walkItems(ctx, dag, pbn, walk, internalKeys); err != nil { - return nil, err - } - return res, nil -} - -func loadSetChan(ctx context.Context, dag ipld.DAGService, root *merkledag.ProtoNode, name string, keyChan chan<- cid.Cid) error { - l, err := root.GetNodeLink(name) - if err != nil { - return err - } - - n, err := l.GetNode(ctx, dag) - if err != nil { - return err - } - - pbn, ok := n.(*merkledag.ProtoNode) - if !ok { - return merkledag.ErrNotProtobuf - } - - walk := func(idx int, link *ipld.Link) error { - keyChan <- link.Cid - return nil - } - - if err = walkItems(ctx, dag, pbn, walk, nil); err != nil { - return err - } - return nil -} - -func getCidListIterator(cids []cid.Cid) itemIterator { - return func() (c cid.Cid, ok bool) { - if len(cids) == 0 { - return cid.Cid{}, false - } - - first := cids[0] - cids = cids[1:] - return first, true - } -} - -func storeSet(ctx context.Context, dag ipld.DAGService, cids []cid.Cid, internalKeys keyObserver) (*merkledag.ProtoNode, error) { - iter := getCidListIterator(cids) - - n, err := storeItems(ctx, dag, uint64(len(cids)), 0, iter, internalKeys) - if err != nil { - return nil, err - } - err = dag.Add(ctx, n) - if err != nil { - return nil, err - } - internalKeys(n.Cid()) - return n, nil -} diff --git a/pinning/pinner/ipldpinner/set_test.go b/pinning/pinner/ipldpinner/set_test.go deleted file mode 100644 index 0f32e6b5e..000000000 --- a/pinning/pinner/ipldpinner/set_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package ipldpinner - -import ( - "context" - "encoding/binary" - "testing" - - bserv "github.com/ipfs/go-blockservice" - cid "github.com/ipfs/go-cid" - ds "github.com/ipfs/go-datastore" - dsq "github.com/ipfs/go-datastore/query" - blockstore "github.com/ipfs/go-ipfs-blockstore" - offline "github.com/ipfs/go-ipfs-exchange-offline" - dag "github.com/ipfs/go-merkledag" -) - -func ignoreCids(_ cid.Cid) {} - -func objCount(d ds.Datastore) int { - q := dsq.Query{KeysOnly: true} - res, err := d.Query(q) - if err != nil { - panic(err) - } - - var count int - for { - _, ok := res.NextSync() - if !ok { - break - } - - count++ - } - return count -} - -func TestSet(t *testing.T) { - dst := ds.NewMapDatastore() - bstore := blockstore.NewBlockstore(dst) - ds := dag.NewDAGService(bserv.New(bstore, offline.Exchange(bstore))) - - // this value triggers the creation of a recursive shard. - // If the recursive sharding is done improperly, this will result in - // an infinite recursion and crash (OOM) - limit := uint32((defaultFanout * maxItems) + 1) - - var inputs []cid.Cid - buf := make([]byte, 4) - for i := uint32(0); i < limit; i++ { - binary.BigEndian.PutUint32(buf, i) - c := dag.NewRawNode(buf).Cid() - inputs = append(inputs, c) - } - - _, err := storeSet(context.Background(), ds, inputs[:len(inputs)-1], ignoreCids) - if err != nil { - t.Fatal(err) - } - - objs1 := objCount(dst) - - out, err := storeSet(context.Background(), ds, inputs, ignoreCids) - if err != nil { - t.Fatal(err) - } - - objs2 := objCount(dst) - if objs2-objs1 > 2 { - t.Fatal("set sharding does not appear to be deterministic") - } - - // weird wrapper node because loadSet expects us to pass an - // object pointing to multiple named sets - setroot := &dag.ProtoNode{} - err = setroot.AddNodeLink("foo", out) - if err != nil { - t.Fatal(err) - } - - outset, err := loadSet(context.Background(), ds, setroot, "foo", ignoreCids) - if err != nil { - t.Fatal(err) - } - - if uint32(len(outset)) != limit { - t.Fatal("got wrong number", len(outset), limit) - } - - seen := cid.NewSet() - for _, c := range outset { - seen.Add(c) - } - - for _, c := range inputs { - if !seen.Has(c) { - t.Fatalf("expected to have '%s', didnt find it", c) - } - } -} diff --git a/pinning/pinner/pinconv/pinconv.go b/pinning/pinner/pinconv/pinconv.go deleted file mode 100644 index df21f85b0..000000000 --- a/pinning/pinner/pinconv/pinconv.go +++ /dev/null @@ -1,127 +0,0 @@ -// Package pinconv converts pins between the dag-based ipldpinner and the -// datastore-based dspinner. Once conversion is complete, the pins from the -// source pinner are removed. -package pinconv - -import ( - "context" - "fmt" - - cid "github.com/ipfs/go-cid" - ds "github.com/ipfs/go-datastore" - ipfspinner "github.com/ipfs/go-ipfs-pinner" - "github.com/ipfs/go-ipfs-pinner/dspinner" - "github.com/ipfs/go-ipfs-pinner/ipldpinner" - ipld "github.com/ipfs/go-ipld-format" -) - -// ConvertPinsFromIPLDToDS converts pins stored in mdag based storage to pins -// stores in the datastore. Returns a dspinner loaded with the converted pins, -// and a count of the recursive and direct pins converted. -// -// After pins are stored in datastore, the root pin key is deleted to unlink -// the pin data in the DAGService. -func ConvertPinsFromIPLDToDS(ctx context.Context, dstore ds.Datastore, dserv ipld.DAGService, internal ipld.DAGService) (ipfspinner.Pinner, int, error) { - const ipldPinPath = "/local/pins" - - dsPinner, err := dspinner.New(ctx, dstore, dserv) - if err != nil { - return nil, 0, err - } - - var convCount int - keyChan := make(chan cid.Cid) - - go func() { - err = ipldpinner.LoadKeys(ctx, dstore, dserv, internal, true, keyChan) - close(keyChan) - }() - for key := range keyChan { - dsPinner.PinWithMode(key, ipfspinner.Recursive) - convCount++ - } - if err != nil { - return nil, 0, fmt.Errorf("cannot load recursive keys: %s", err) - } - - keyChan = make(chan cid.Cid) - go func() { - err = ipldpinner.LoadKeys(ctx, dstore, dserv, internal, false, keyChan) - close(keyChan) - }() - for key := range keyChan { - dsPinner.PinWithMode(key, ipfspinner.Direct) - convCount++ - } - if err != nil { - return nil, 0, fmt.Errorf("cannot load direct keys: %s", err) - } - - err = dsPinner.Flush(ctx) - if err != nil { - return nil, 0, err - } - - // Delete root mdag key from datastore to remove old pin storage. - ipldPinDatastoreKey := ds.NewKey(ipldPinPath) - if err = dstore.Delete(ipldPinDatastoreKey); err != nil { - return nil, 0, fmt.Errorf("cannot delete old pin state: %v", err) - } - if err = dstore.Sync(ipldPinDatastoreKey); err != nil { - return nil, 0, fmt.Errorf("cannot sync old pin state: %v", err) - } - - return dsPinner, convCount, nil -} - -// ConvertPinsFromDSToIPLD converts the pins stored in the datastore by -// dspinner, into pins stored in the given internal DAGService by ipldpinner. -// Returns an ipldpinner loaded with the converted pins, and a count of the -// recursive and direct pins converted. -// -// After the pins are stored in the DAGService, the pins and their indexes are -// removed from the dspinner. -func ConvertPinsFromDSToIPLD(ctx context.Context, dstore ds.Datastore, dserv ipld.DAGService, internal ipld.DAGService) (ipfspinner.Pinner, int, error) { - dsPinner, err := dspinner.New(ctx, dstore, dserv) - if err != nil { - return nil, 0, err - } - - ipldPinner, err := ipldpinner.New(dstore, dserv, internal) - if err != nil { - return nil, 0, err - } - - cids, err := dsPinner.RecursiveKeys(ctx) - if err != nil { - return nil, 0, err - } - for i := range cids { - ipldPinner.PinWithMode(cids[i], ipfspinner.Recursive) - dsPinner.RemovePinWithMode(cids[i], ipfspinner.Recursive) - } - convCount := len(cids) - - cids, err = dsPinner.DirectKeys(ctx) - if err != nil { - return nil, 0, err - } - for i := range cids { - ipldPinner.PinWithMode(cids[i], ipfspinner.Direct) - dsPinner.RemovePinWithMode(cids[i], ipfspinner.Direct) - } - convCount += len(cids) - - // Save the ipldpinner pins - err = ipldPinner.Flush(ctx) - if err != nil { - return nil, 0, err - } - - err = dsPinner.Flush(ctx) - if err != nil { - return nil, 0, err - } - - return ipldPinner, convCount, nil -} diff --git a/pinning/pinner/pinconv/pinconv_test.go b/pinning/pinner/pinconv/pinconv_test.go deleted file mode 100644 index 02abca61c..000000000 --- a/pinning/pinner/pinconv/pinconv_test.go +++ /dev/null @@ -1,179 +0,0 @@ -package pinconv - -import ( - "context" - "errors" - "io" - "strings" - "testing" - - bs "github.com/ipfs/go-blockservice" - cid "github.com/ipfs/go-cid" - ds "github.com/ipfs/go-datastore" - lds "github.com/ipfs/go-ds-leveldb" - blockstore "github.com/ipfs/go-ipfs-blockstore" - offline "github.com/ipfs/go-ipfs-exchange-offline" - ipfspin "github.com/ipfs/go-ipfs-pinner" - "github.com/ipfs/go-ipfs-pinner/dspinner" - util "github.com/ipfs/go-ipfs-util" - ipld "github.com/ipfs/go-ipld-format" - mdag "github.com/ipfs/go-merkledag" -) - -var rand = util.NewTimeSeededRand() - -type batchWrap struct { - ds.Datastore -} - -func randNode() (*mdag.ProtoNode, cid.Cid) { - nd := new(mdag.ProtoNode) - nd.SetData(make([]byte, 32)) - _, err := io.ReadFull(rand, nd.Data()) - if err != nil { - panic(err) - } - k := nd.Cid() - return nd, k -} - -func (d *batchWrap) Batch() (ds.Batch, error) { - return ds.NewBasicBatch(d), nil -} - -func makeStore() (ds.Datastore, ipld.DAGService) { - ldstore, err := lds.NewDatastore("", nil) - if err != nil { - panic(err) - } - var dstore ds.Batching - dstore = &batchWrap{ldstore} - - bstore := blockstore.NewBlockstore(dstore) - bserv := bs.New(bstore, offline.Exchange(bstore)) - dserv := mdag.NewDAGService(bserv) - return dstore, dserv -} - -func TestConversions(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - dstore, dserv := makeStore() - - dsPinner, err := dspinner.New(ctx, dstore, dserv) - if err != nil { - t.Fatal(err) - } - - a, ak := randNode() - err = dsPinner.Pin(ctx, a, false) - if err != nil { - t.Fatal(err) - } - - // create new node c, to be indirectly pinned through b - c, ck := randNode() - dserv.Add(ctx, c) - - // Create new node b, to be parent to a and c - b, _ := randNode() - b.AddNodeLink("child", a) - b.AddNodeLink("otherchild", c) - bk := b.Cid() // CID changed after adding links - - // recursively pin B{A,C} - err = dsPinner.Pin(ctx, b, true) - if err != nil { - t.Fatal(err) - } - - err = dsPinner.Flush(ctx) - if err != nil { - t.Fatal(err) - } - - verifyPins := func(pinner ipfspin.Pinner) error { - pinned, err := pinner.CheckIfPinned(ctx, ak, bk, ck) - if err != nil { - return err - } - if len(pinned) != 3 { - return errors.New("incorrect number of results") - } - for _, pn := range pinned { - switch pn.Key { - case ak: - if pn.Mode != ipfspin.Direct { - return errors.New("A pinned with wrong mode") - } - case bk: - if pn.Mode != ipfspin.Recursive { - return errors.New("B pinned with wrong mode") - } - case ck: - if pn.Mode != ipfspin.Indirect { - return errors.New("C should be pinned indirectly") - } - if pn.Via != bk { - return errors.New("C should be pinned via B") - } - } - } - return nil - } - - err = verifyPins(dsPinner) - if err != nil { - t.Fatal(err) - } - - ipldPinner, toIPLDCount, err := ConvertPinsFromDSToIPLD(ctx, dstore, dserv, dserv) - if err != nil { - t.Fatal(err) - } - if toIPLDCount != 2 { - t.Fatal("expected 2 ds-to-ipld pins, got", toIPLDCount) - } - - err = verifyPins(ipldPinner) - if err != nil { - t.Fatal(err) - } - - toDSPinner, toDSCount, err := ConvertPinsFromIPLDToDS(ctx, dstore, dserv, dserv) - if err != nil { - t.Fatal(err) - } - if toDSCount != toIPLDCount { - t.Fatal("ds-to-ipld pins", toIPLDCount, "not equal to ipld-to-ds-pins", toDSCount) - } - - err = verifyPins(toDSPinner) - if err != nil { - t.Fatal(err) - } -} - -func TestConvertLoadError(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - dstore, dserv := makeStore() - // Point /local/pins to empty node to cause failure loading pins. - pinDatastoreKey := ds.NewKey("/local/pins") - emptyKey, err := cid.Decode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n") - if err != nil { - panic(err) - } - if err = dstore.Put(pinDatastoreKey, emptyKey.Bytes()); err != nil { - panic(err) - } - if err = dstore.Sync(pinDatastoreKey); err != nil { - panic(err) - } - - _, _, err = ConvertPinsFromIPLDToDS(ctx, dstore, dserv, dserv) - if err == nil || !strings.HasPrefix(err.Error(), "cannot load recursive keys") { - t.Fatal("did not get expected error") - } -} From 2d5bca0320b59b5ab1b8f2d86b237955db37b374 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Thu, 13 May 2021 08:15:10 -0700 Subject: [PATCH 3382/3817] remove Makefile This commit was moved from ipfs/go-ipns@bce60af9cfd607d79747871908c1a330812642f0 --- ipns/Makefile | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 ipns/Makefile diff --git a/ipns/Makefile b/ipns/Makefile deleted file mode 100644 index 54152565e..000000000 --- a/ipns/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -export IPFS_API ?= v04x.ipfs.io - -gx: - go get -u github.com/whyrusleeping/gx - go get -u github.com/whyrusleeping/gx-go - -deps: gx - gx --verbose install --global - gx-go rewrite From 5968bb2130feadbf586df2b3c91b7ac0c13dea42 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Thu, 13 May 2021 08:24:30 -0700 Subject: [PATCH 3383/3817] remove Makefile This commit was moved from ipfs/interface-go-ipfs-core@9b4e1a554b3d82c37b68785e25e7c0381b11470a --- coreiface/Makefile | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 coreiface/Makefile diff --git a/coreiface/Makefile b/coreiface/Makefile deleted file mode 100644 index 89fc88d7f..000000000 --- a/coreiface/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -all: deps -gx: - go get github.com/whyrusleeping/gx - go get github.com/whyrusleeping/gx-go -deps: gx - gx --verbose install --global - gx-go rewrite -test: deps - gx test -v -race -coverprofile=coverage.txt -covermode=atomic . -rw: - gx-go rewrite -rwundo: - gx-go rewrite --undo -publish: rwundo - gx publish -.PHONY: all gx deps test rw rwundo publish From 6dfedd99957439607056235bf2caaa971a718ead Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Thu, 13 May 2021 08:27:04 -0700 Subject: [PATCH 3384/3817] remove Makefile This commit was moved from ipfs/go-path@575c743c48744adaf2582c8b7aeb487275fceb61 --- path/Makefile | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 path/Makefile diff --git a/path/Makefile b/path/Makefile deleted file mode 100644 index 20619413c..000000000 --- a/path/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -gx: - go get github.com/whyrusleeping/gx - go get github.com/whyrusleeping/gx-go - -deps: gx - gx --verbose install --global - gx-go rewrite - -publish: - gx-go rewrite --undo - From 9287cef08e600a783066e43c59bd4f2dd7f17e8b Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Thu, 13 May 2021 08:31:44 -0700 Subject: [PATCH 3385/3817] remove Makefile This commit was moved from ipfs/go-mfs@dbec3d53dbd0a3b8eeb44616619a1ee3b3febdd6 --- mfs/Makefile | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 mfs/Makefile diff --git a/mfs/Makefile b/mfs/Makefile deleted file mode 100644 index 73f2841f6..000000000 --- a/mfs/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -all: deps -gx: - go get github.com/whyrusleeping/gx - go get github.com/whyrusleeping/gx-go -deps: gx - gx --verbose install --global - gx-go rewrite -test: deps - gx test -v -race -coverprofile=coverage.txt -covermode=atomic . -rw: - gx-go rewrite -rwundo: - gx-go rewrite --undo -publish: rwundo - gx publish -.PHONY: all gx deps test rw rwundo publish - - From dae4407c6ba038a36d07ba7c77067c0afbad9204 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 13 May 2021 09:40:44 -0700 Subject: [PATCH 3386/3817] fix: remove the rest of the pb backed pinner This commit was moved from ipfs/go-ipfs-pinner@500826fdfdfa1656b1446216dd97a028ffd65a1e --- pinning/pinner/internal/pb/Makefile | 11 - pinning/pinner/internal/pb/doc.go | 3 - pinning/pinner/internal/pb/header.pb.go | 355 ------------------------ pinning/pinner/internal/pb/header.proto | 14 - 4 files changed, 383 deletions(-) delete mode 100644 pinning/pinner/internal/pb/Makefile delete mode 100644 pinning/pinner/internal/pb/doc.go delete mode 100644 pinning/pinner/internal/pb/header.pb.go delete mode 100644 pinning/pinner/internal/pb/header.proto diff --git a/pinning/pinner/internal/pb/Makefile b/pinning/pinner/internal/pb/Makefile deleted file mode 100644 index df34e54b0..000000000 --- a/pinning/pinner/internal/pb/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -PB = $(wildcard *.proto) -GO = $(PB:.proto=.pb.go) - -all: $(GO) - -%.pb.go: %.proto - protoc --proto_path=$(GOPATH)/src:. --gogofaster_out=. $< - -clean: - rm -f *.pb.go - rm -f *.go diff --git a/pinning/pinner/internal/pb/doc.go b/pinning/pinner/internal/pb/doc.go deleted file mode 100644 index 95d4afe67..000000000 --- a/pinning/pinner/internal/pb/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -package pb - -//go:generate protoc --gogo_out=. header.proto diff --git a/pinning/pinner/internal/pb/header.pb.go b/pinning/pinner/internal/pb/header.pb.go deleted file mode 100644 index b8b3b0e7d..000000000 --- a/pinning/pinner/internal/pb/header.pb.go +++ /dev/null @@ -1,355 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: header.proto - -package pb - -import ( - encoding_binary "encoding/binary" - fmt "fmt" - proto "github.com/gogo/protobuf/proto" - io "io" - math "math" - math_bits "math/bits" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package - -type Set struct { - // 1 for now, library will refuse to handle entries with an unrecognized version. - Version uint32 `protobuf:"varint,1,opt,name=version" json:"version"` - // how many of the links are subtrees - Fanout uint32 `protobuf:"varint,2,opt,name=fanout" json:"fanout"` - // hash seed for subtree selection, a random number - Seed uint32 `protobuf:"fixed32,3,opt,name=seed" json:"seed"` -} - -func (m *Set) Reset() { *m = Set{} } -func (m *Set) String() string { return proto.CompactTextString(m) } -func (*Set) ProtoMessage() {} -func (*Set) Descriptor() ([]byte, []int) { - return fileDescriptor_6398613e36d6c2ce, []int{0} -} -func (m *Set) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *Set) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Set.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *Set) XXX_Merge(src proto.Message) { - xxx_messageInfo_Set.Merge(m, src) -} -func (m *Set) XXX_Size() int { - return m.Size() -} -func (m *Set) XXX_DiscardUnknown() { - xxx_messageInfo_Set.DiscardUnknown(m) -} - -var xxx_messageInfo_Set proto.InternalMessageInfo - -func (m *Set) GetVersion() uint32 { - if m != nil { - return m.Version - } - return 0 -} - -func (m *Set) GetFanout() uint32 { - if m != nil { - return m.Fanout - } - return 0 -} - -func (m *Set) GetSeed() uint32 { - if m != nil { - return m.Seed - } - return 0 -} - -func init() { - proto.RegisterType((*Set)(nil), "ipfs.pin.Set") -} - -func init() { proto.RegisterFile("header.proto", fileDescriptor_6398613e36d6c2ce) } - -var fileDescriptor_6398613e36d6c2ce = []byte{ - // 146 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xc9, 0x48, 0x4d, 0x4c, - 0x49, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0xc8, 0x2c, 0x48, 0x2b, 0xd6, 0x2b, - 0xc8, 0xcc, 0x53, 0x8a, 0xe5, 0x62, 0x0e, 0x4e, 0x2d, 0x11, 0x92, 0xe3, 0x62, 0x2f, 0x4b, 0x2d, - 0x2a, 0xce, 0xcc, 0xcf, 0x93, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x75, 0x62, 0x39, 0x71, 0x4f, 0x9e, - 0x21, 0x08, 0x26, 0x28, 0x24, 0xc3, 0xc5, 0x96, 0x96, 0x98, 0x97, 0x5f, 0x5a, 0x22, 0xc1, 0x84, - 0x24, 0x0d, 0x15, 0x13, 0x92, 0xe0, 0x62, 0x29, 0x4e, 0x4d, 0x4d, 0x91, 0x60, 0x56, 0x60, 0xd4, - 0x60, 0x87, 0xca, 0x81, 0x45, 0x9c, 0x64, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, - 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, - 0x21, 0x8a, 0xa9, 0x20, 0x09, 0x10, 0x00, 0x00, 0xff, 0xff, 0x3c, 0x49, 0x19, 0x51, 0x95, 0x00, - 0x00, 0x00, -} - -func (m *Set) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *Set) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *Set) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - i -= 4 - encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(m.Seed)) - i-- - dAtA[i] = 0x1d - i = encodeVarintHeader(dAtA, i, uint64(m.Fanout)) - i-- - dAtA[i] = 0x10 - i = encodeVarintHeader(dAtA, i, uint64(m.Version)) - i-- - dAtA[i] = 0x8 - return len(dAtA) - i, nil -} - -func encodeVarintHeader(dAtA []byte, offset int, v uint64) int { - offset -= sovHeader(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *Set) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - n += 1 + sovHeader(uint64(m.Version)) - n += 1 + sovHeader(uint64(m.Fanout)) - n += 5 - return n -} - -func sovHeader(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozHeader(x uint64) (n int) { - return sovHeader(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *Set) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowHeader - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Set: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Set: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) - } - m.Version = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowHeader - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Version |= uint32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Fanout", wireType) - } - m.Fanout = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowHeader - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Fanout |= uint32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 5 { - return fmt.Errorf("proto: wrong wireType = %d for field Seed", wireType) - } - m.Seed = 0 - if (iNdEx + 4) > l { - return io.ErrUnexpectedEOF - } - m.Seed = uint32(encoding_binary.LittleEndian.Uint32(dAtA[iNdEx:])) - iNdEx += 4 - default: - iNdEx = preIndex - skippy, err := skipHeader(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthHeader - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthHeader - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipHeader(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowHeader - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowHeader - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowHeader - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthHeader - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupHeader - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthHeader - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthHeader = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowHeader = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupHeader = fmt.Errorf("proto: unexpected end of group") -) diff --git a/pinning/pinner/internal/pb/header.proto b/pinning/pinner/internal/pb/header.proto deleted file mode 100644 index 36b32b36d..000000000 --- a/pinning/pinner/internal/pb/header.proto +++ /dev/null @@ -1,14 +0,0 @@ -syntax = "proto2"; - -package ipfs.pin; - -option go_package = "pb"; - -message Set { - // 1 for now, library will refuse to handle entries with an unrecognized version. - optional uint32 version = 1; - // how many of the links are subtrees - optional uint32 fanout = 2; - // hash seed for subtree selection, a random number - optional fixed32 seed = 3; -} From f0e1f0827951702bf21a139b9e64d004b334912f Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Fri, 14 May 2021 01:04:19 -0700 Subject: [PATCH 3387/3817] Bulk Provide/Reproviding System (#34) * batched: added a batching providing and reproviding system that takes advantage of an underlying provide emitting system that can operate on many items at a time. The implementation is experimental and likely to change. * queue: modified documentation to indicate that its durability is best effort and determined by the underlying datastore This commit was moved from ipfs/go-ipfs-provider@d391dae4a595473f6797eb5d5b803a529a7bbdbc --- provider/batched/system.go | 415 ++++++++++++++++++++++++++++++++ provider/batched/system_test.go | 117 +++++++++ provider/queue/queue.go | 8 +- 3 files changed, 536 insertions(+), 4 deletions(-) create mode 100644 provider/batched/system.go create mode 100644 provider/batched/system_test.go diff --git a/provider/batched/system.go b/provider/batched/system.go new file mode 100644 index 000000000..5637e55b1 --- /dev/null +++ b/provider/batched/system.go @@ -0,0 +1,415 @@ +package batched + +import ( + "context" + "errors" + "fmt" + "strconv" + "sync" + "time" + + "github.com/ipfs/go-cid" + "github.com/ipfs/go-datastore" + provider "github.com/ipfs/go-ipfs-provider" + "github.com/ipfs/go-ipfs-provider/queue" + "github.com/ipfs/go-ipfs-provider/simple" + logging "github.com/ipfs/go-log" + "github.com/ipfs/go-verifcid" + "github.com/multiformats/go-multihash" +) + +var log = logging.Logger("provider.batched") + +type BatchProvidingSystem struct { + ctx context.Context + close context.CancelFunc + closewg sync.WaitGroup + + reprovideInterval time.Duration + initalReprovideDelay time.Duration + initialReprovideDelaySet bool + + rsys provideMany + keyProvider simple.KeyChanFunc + + q *queue.Queue + ds datastore.Batching + + reprovideCh chan cid.Cid + + totalProvides, lastReprovideBatchSize int + avgProvideDuration, lastReprovideDuration time.Duration +} + +var _ provider.System = (*BatchProvidingSystem)(nil) + +type provideMany interface { + ProvideMany(ctx context.Context, keys []multihash.Multihash) error + Ready() bool +} + +// Option defines the functional option type that can be used to configure +// BatchProvidingSystem instances +type Option func(system *BatchProvidingSystem) error + +var lastReprovideKey = datastore.NewKey("/provider/reprovide/lastreprovide") + +func New(provider provideMany, q *queue.Queue, opts ...Option) (*BatchProvidingSystem, error) { + s := &BatchProvidingSystem{ + reprovideInterval: time.Hour * 24, + rsys: provider, + keyProvider: nil, + q: q, + ds: datastore.NewMapDatastore(), + reprovideCh: make(chan cid.Cid), + } + + for _, o := range opts { + if err := o(s); err != nil { + return nil, err + } + } + + // Setup default behavior for the initial reprovide delay + // + // If the reprovide ticker is larger than a minute (likely), + // provide once after we've been up a minute. + // + // Don't provide _immediately_ as we might be just about to stop. + if !s.initialReprovideDelaySet && s.reprovideInterval > time.Minute { + s.initalReprovideDelay = time.Minute + s.initialReprovideDelaySet = true + } + + if s.keyProvider == nil { + s.keyProvider = func(ctx context.Context) (<-chan cid.Cid, error) { + ch := make(chan cid.Cid) + close(ch) + return ch, nil + } + } + + // This is after the options processing so we do not have to worry about leaking a context if there is an + // initialization error processing the options + ctx, cancel := context.WithCancel(context.Background()) + s.ctx = ctx + s.close = cancel + + return s, nil +} + +func Datastore(batching datastore.Batching) Option { + return func(system *BatchProvidingSystem) error { + system.ds = batching + return nil + } +} + +func ReproviderInterval(duration time.Duration) Option { + return func(system *BatchProvidingSystem) error { + system.reprovideInterval = duration + return nil + } +} + +func KeyProvider(fn simple.KeyChanFunc) Option { + return func(system *BatchProvidingSystem) error { + system.keyProvider = fn + return nil + } +} + +func initialReprovideDelay(duration time.Duration) Option { + return func(system *BatchProvidingSystem) error { + system.initialReprovideDelaySet = true + system.initalReprovideDelay = duration + return nil + } +} + +func (s *BatchProvidingSystem) Run() { + // how long we wait between the first provider we hear about and batching up the provides to send out + const pauseDetectionThreshold = time.Millisecond * 500 + // how long we are willing to collect providers for the batch after we receive the first one + const maxCollectionDuration = time.Minute * 10 + + provCh := s.q.Dequeue() + + s.closewg.Add(1) + go func() { + defer s.closewg.Done() + + m := make(map[cid.Cid]struct{}) + + // setup stopped timers + maxCollectionDurationTimer := time.NewTimer(time.Hour) + pauseDetectTimer := time.NewTimer(time.Hour) + stopAndEmptyTimer(maxCollectionDurationTimer) + stopAndEmptyTimer(pauseDetectTimer) + + // make sure timers are cleaned up + defer maxCollectionDurationTimer.Stop() + defer pauseDetectTimer.Stop() + + resetTimersAfterReceivingProvide := func() { + firstProvide := len(m) == 0 + if firstProvide { + // after receiving the first provider start up the timers + maxCollectionDurationTimer.Reset(maxCollectionDuration) + pauseDetectTimer.Reset(pauseDetectionThreshold) + } else { + // otherwise just do a full restart of the pause timer + stopAndEmptyTimer(pauseDetectTimer) + pauseDetectTimer.Reset(pauseDetectionThreshold) + } + } + + for { + performedReprovide := false + + // at the start of every loop the maxCollectionDurationTimer and pauseDetectTimer should be already be + // stopped and have empty channels + loop: + for { + select { + case <-maxCollectionDurationTimer.C: + // if this timer has fired then the pause timer has started so let's stop and empty it + stopAndEmptyTimer(pauseDetectTimer) + break loop + default: + } + + select { + case c := <-provCh: + resetTimersAfterReceivingProvide() + m[c] = struct{}{} + continue + default: + } + + select { + case c := <-provCh: + resetTimersAfterReceivingProvide() + m[c] = struct{}{} + case c := <-s.reprovideCh: + resetTimersAfterReceivingProvide() + m[c] = struct{}{} + performedReprovide = true + case <-pauseDetectTimer.C: + // if this timer has fired then the max collection timer has started so let's stop and empty it + stopAndEmptyTimer(maxCollectionDurationTimer) + break loop + case <-maxCollectionDurationTimer.C: + // if this timer has fired then the pause timer has started so let's stop and empty it + stopAndEmptyTimer(pauseDetectTimer) + break loop + case <-s.ctx.Done(): + return + } + } + + if len(m) == 0 { + continue + } + + keys := make([]multihash.Multihash, 0, len(m)) + for c := range m { + delete(m, c) + + // hash security + if err := verifcid.ValidateCid(c); err != nil { + log.Errorf("insecure hash in reprovider, %s (%s)", c, err) + continue + } + + keys = append(keys, c.Hash()) + } + + for !s.rsys.Ready() { + log.Debugf("reprovider system not ready") + select { + case <-time.After(time.Minute): + case <-s.ctx.Done(): + return + } + } + + log.Debugf("starting provide of %d keys", len(keys)) + start := time.Now() + err := s.rsys.ProvideMany(s.ctx, keys) + if err != nil { + log.Debugf("providing failed %v", err) + continue + } + dur := time.Since(start) + + totalProvideTime := int64(s.totalProvides) * int64(s.avgProvideDuration) + recentAvgProvideDuration := time.Duration(int64(dur) / int64(len(keys))) + s.avgProvideDuration = time.Duration((totalProvideTime + int64(dur)) / int64(s.totalProvides+len(keys))) + s.totalProvides += len(keys) + + log.Debugf("finished providing of %d keys. It took %v with an average of %v per provide", len(keys), dur, recentAvgProvideDuration) + + if performedReprovide { + s.lastReprovideBatchSize = len(keys) + s.lastReprovideDuration = dur + + if err := s.ds.Put(lastReprovideKey, storeTime(time.Now())); err != nil { + log.Errorf("could not store last reprovide time: %v", err) + } + if err := s.ds.Sync(lastReprovideKey); err != nil { + log.Errorf("could not perform sync of last reprovide time: %v", err) + } + } + } + }() + + s.closewg.Add(1) + go func() { + defer s.closewg.Done() + + var initialReprovideCh, reprovideCh <-chan time.Time + + // If reproviding is enabled (non-zero) + if s.reprovideInterval > 0 { + reprovideTicker := time.NewTicker(s.reprovideInterval) + defer reprovideTicker.Stop() + reprovideCh = reprovideTicker.C + + // if there is a non-zero initial reprovide time that was set in the initializer or if the fallback has been + if s.initialReprovideDelaySet { + initialReprovideTimer := time.NewTimer(s.initalReprovideDelay) + defer initialReprovideTimer.Stop() + + initialReprovideCh = initialReprovideTimer.C + } + } + + for s.ctx.Err() == nil { + select { + case <-initialReprovideCh: + case <-reprovideCh: + case <-s.ctx.Done(): + return + } + + err := s.reprovide(s.ctx, false) + + // only log if we've hit an actual error, otherwise just tell the client we're shutting down + if s.ctx.Err() == nil && err != nil { + log.Errorf("failed to reprovide: %s", err) + } + } + }() +} + +func stopAndEmptyTimer(t *time.Timer) { + if !t.Stop() { + <-t.C + } +} + +func storeTime(t time.Time) []byte { + val := []byte(fmt.Sprintf("%d", t.UnixNano())) + return val +} + +func parseTime(b []byte) (time.Time, error) { + tns, err := strconv.ParseInt(string(b), 10, 64) + if err != nil { + return time.Time{}, err + } + return time.Unix(0, tns), nil +} + +func (s *BatchProvidingSystem) Close() error { + s.close() + err := s.q.Close() + s.closewg.Wait() + return err +} + +func (s *BatchProvidingSystem) Provide(cid cid.Cid) error { + return s.q.Enqueue(cid) +} + +func (s *BatchProvidingSystem) Reprovide(ctx context.Context) error { + return s.reprovide(ctx, true) +} + +func (s *BatchProvidingSystem) reprovide(ctx context.Context, force bool) error { + if !s.shouldReprovide() && !force { + return nil + } + + kch, err := s.keyProvider(ctx) + if err != nil { + return err + } + +reprovideCidLoop: + for { + select { + case c, ok := <-kch: + if !ok { + break reprovideCidLoop + } + + select { + case s.reprovideCh <- c: + case <-ctx.Done(): + return ctx.Err() + } + case <-ctx.Done(): + return ctx.Err() + } + } + + return nil +} + +func (s *BatchProvidingSystem) getLastReprovideTime() (time.Time, error) { + val, err := s.ds.Get(lastReprovideKey) + if errors.Is(err, datastore.ErrNotFound) { + return time.Time{}, nil + } + if err != nil { + return time.Time{}, fmt.Errorf("could not get last reprovide time") + } + + t, err := parseTime(val) + if err != nil { + return time.Time{}, fmt.Errorf("could not decode last reprovide time, got %q", string(val)) + } + + return t, nil +} + +func (s *BatchProvidingSystem) shouldReprovide() bool { + t, err := s.getLastReprovideTime() + if err != nil { + log.Debugf("getting last reprovide time failed: %s", err) + return false + } + + if time.Since(t) < time.Duration(float64(s.reprovideInterval)*0.5) { + return false + } + return true +} + +type BatchedProviderStats struct { + TotalProvides, LastReprovideBatchSize int + AvgProvideDuration, LastReprovideDuration time.Duration +} + +// Stat returns various stats about this provider system +func (s *BatchProvidingSystem) Stat(ctx context.Context) (BatchedProviderStats, error) { + // TODO: Does it matter that there is no locking around the total+average values? + return BatchedProviderStats{ + TotalProvides: s.totalProvides, + LastReprovideBatchSize: s.lastReprovideBatchSize, + AvgProvideDuration: s.avgProvideDuration, + LastReprovideDuration: s.lastReprovideDuration, + }, nil +} diff --git a/provider/batched/system_test.go b/provider/batched/system_test.go new file mode 100644 index 000000000..b2b312020 --- /dev/null +++ b/provider/batched/system_test.go @@ -0,0 +1,117 @@ +package batched + +import ( + "context" + "strconv" + "sync" + "testing" + "time" + + "github.com/ipfs/go-cid" + "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + mh "github.com/multiformats/go-multihash" + + q "github.com/ipfs/go-ipfs-provider/queue" +) + +type mockProvideMany struct { + lk sync.Mutex + keys []mh.Multihash +} + +func (m *mockProvideMany) ProvideMany(ctx context.Context, keys []mh.Multihash) error { + m.lk.Lock() + defer m.lk.Unlock() + m.keys = keys + return nil +} + +func (m *mockProvideMany) Ready() bool { + return true +} + +func (m *mockProvideMany) GetKeys() []mh.Multihash { + m.lk.Lock() + defer m.lk.Unlock() + return m.keys[:] +} + +var _ provideMany = (*mockProvideMany)(nil) + +func TestBatched(t *testing.T) { + ctx := context.Background() + defer ctx.Done() + + ds := dssync.MutexWrap(datastore.NewMapDatastore()) + queue, err := q.NewQueue(ctx, "test", ds) + if err != nil { + t.Fatal(err) + } + + provider := &mockProvideMany{} + + ctx, cancel := context.WithTimeout(ctx, time.Second*10) + defer cancel() + + const numProvides = 100 + keysToProvide := make(map[cid.Cid]int) + for i := 0; i < numProvides; i++ { + h, err := mh.Sum([]byte(strconv.Itoa(i)), mh.SHA2_256, -1) + if err != nil { + panic(err) + } + c := cid.NewCidV1(cid.Raw, h) + keysToProvide[c] = i + } + + batchSystem, err := New(provider, queue, KeyProvider(func(ctx context.Context) (<-chan cid.Cid, error) { + ch := make(chan cid.Cid) + go func() { + for k := range keysToProvide { + select { + case ch <- k: + case <-ctx.Done(): + return + } + } + }() + return ch, nil + }), initialReprovideDelay(0)) + if err != nil { + t.Fatal(err) + } + + batchSystem.Run() + + var keys []mh.Multihash + for { + if ctx.Err() != nil { + t.Fatal("test hung") + } + keys = provider.GetKeys() + if len(keys) != 0 { + break + } + time.Sleep(time.Millisecond * 100) + } + + if len(keys) != numProvides { + t.Fatalf("expected %d provider keys, got %d", numProvides, len(keys)) + } + + provMap := make(map[string]struct{}) + for _, k := range keys { + provMap[string(k)] = struct{}{} + } + + for i := 0; i < numProvides; i++ { + h, err := mh.Sum([]byte(strconv.Itoa(i)), mh.SHA2_256, -1) + if err != nil { + panic(err) + } + if _, found := provMap[string(h)]; !found { + t.Fatalf("could not find provider with value %d", i) + } + } +} diff --git a/provider/queue/queue.go b/provider/queue/queue.go index 2c3350256..e81e341f6 100644 --- a/provider/queue/queue.go +++ b/provider/queue/queue.go @@ -14,11 +14,11 @@ import ( var log = logging.Logger("provider.queue") -// Queue provides a durable, FIFO interface to the datastore for storing cids +// Queue provides a best-effort durability, FIFO interface to the datastore for storing cids // -// Durability just means that cids in the process of being provided when a -// crash or shutdown occurs will still be in the queue when the node is -// brought back online. +// Best-effort durability just means that cids in the process of being provided when a +// crash or shutdown occurs may be in the queue when the node is brought back online +// depending on whether the underlying datastore has synchronous or asynchronous writes. type Queue struct { // used to differentiate queues in datastore // e.g. provider vs reprovider From 925510281b5466638f4daf426ddc0230a3c0a3b0 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sat, 15 May 2021 17:53:19 -0700 Subject: [PATCH 3388/3817] fix staticcheck This commit was moved from ipfs/go-ipfs-blockstore@c8a6ece032042c2c35fcb67d3294004b848734e1 --- blockstore/idstore.go | 2 +- blockstore/idstore_test.go | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/blockstore/idstore.go b/blockstore/idstore.go index 1166e5bda..b1a85b6b9 100644 --- a/blockstore/idstore.go +++ b/blockstore/idstore.go @@ -34,7 +34,7 @@ func extractContents(k cid.Cid) (bool, []byte) { } dmh, err := mh.Decode(k.Hash()) - if err != nil || dmh.Code != mh.ID { + if err != nil || dmh.Code != mh.IDENTITY { return false, nil } return true, dmh.Digest diff --git a/blockstore/idstore_test.go b/blockstore/idstore_test.go index 321d5ec77..65b902ef1 100644 --- a/blockstore/idstore_test.go +++ b/blockstore/idstore_test.go @@ -17,7 +17,7 @@ func createTestStores() (Blockstore, *callbackDatastore) { } func TestIdStore(t *testing.T) { - idhash1, _ := cid.NewPrefixV1(cid.Raw, mh.ID).Sum([]byte("idhash1")) + idhash1, _ := cid.NewPrefixV1(cid.Raw, mh.IDENTITY).Sum([]byte("idhash1")) idblock1, _ := blk.NewBlockWithCid([]byte("idhash1"), idhash1) hash1, _ := cid.NewPrefixV1(cid.Raw, mh.SHA2_256).Sum([]byte("hash1")) block1, _ := blk.NewBlockWithCid([]byte("hash1"), hash1) @@ -110,7 +110,7 @@ func TestIdStore(t *testing.T) { t.Fatal(err) } - idhash2, _ := cid.NewPrefixV1(cid.Raw, mh.ID).Sum([]byte("idhash2")) + idhash2, _ := cid.NewPrefixV1(cid.Raw, mh.IDENTITY).Sum([]byte("idhash2")) idblock2, _ := blk.NewBlockWithCid([]byte("idhash2"), idhash2) hash2, _ := cid.NewPrefixV1(cid.Raw, mh.SHA2_256).Sum([]byte("hash2")) block2, _ := blk.NewBlockWithCid([]byte("hash2"), hash2) @@ -146,10 +146,13 @@ func TestIdStore(t *testing.T) { } ch, err := ids.AllKeysChan(context.TODO()) + if err != nil { + t.Fatal(err) + } cnt := 0 for c := range ch { cnt++ - if c.Prefix().MhType == mh.ID { + if c.Prefix().MhType == mh.IDENTITY { t.Fatalf("block with identity hash found in blockstore") } } From 68f1ef886dd0f240b860d7b7559c34763e363f58 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sat, 15 May 2021 18:01:37 -0700 Subject: [PATCH 3389/3817] fix staticcheck This commit was moved from ipfs/go-filestore@c2dbc1416d7aa7b77f4fcbee503ebce40c49bd74 --- filestore/fsrefstore.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index bc183fc38..a29c2264e 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -149,10 +149,10 @@ func (f *FileManager) getDataObj(m mh.Multihash) (*pb.DataObj, error) { switch err { case ds.ErrNotFound: return nil, blockstore.ErrNotFound - default: - return nil, err case nil: // + default: + return nil, err } return unmarshalDataObj(o) @@ -290,7 +290,8 @@ func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { if !f.AllowFiles { return ErrFilestoreNotEnabled } - if !filepath.HasPrefix(b.PosInfo.FullPath, f.root) { //nolint:staticcheck + //lint:ignore SA1019 // ignore staticcheck + if !filepath.HasPrefix(b.PosInfo.FullPath, f.root) { return fmt.Errorf("cannot add filestore references outside ipfs root (%s)", f.root) } From 2a4eb0c9335686e2d43f8c6b6c1166f40c246450 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sat, 15 May 2021 18:07:02 -0700 Subject: [PATCH 3390/3817] fix staticcheck This commit was moved from ipfs/go-ipfs-pinner@40ae33dcdc59ec69db2f4ec8188146cea89e906d --- pinning/pinner/dsindex/indexer.go | 6 ++++-- pinning/pinner/dspinner/pin.go | 3 +-- pinning/pinner/dspinner/pin_test.go | 4 +--- pinning/pinner/pin.go | 3 --- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/pinning/pinner/dsindex/indexer.go b/pinning/pinner/dsindex/indexer.go index e48af2e17..e1119acac 100644 --- a/pinning/pinner/dsindex/indexer.go +++ b/pinning/pinner/dsindex/indexer.go @@ -127,12 +127,14 @@ func (x *indexer) ForEach(ctx context.Context, key string, fn func(key, value st break } ent := r.Entry - decIdx, err := decode(path.Base(path.Dir(ent.Key))) + var decIdx string + decIdx, err = decode(path.Base(path.Dir(ent.Key))) if err != nil { err = fmt.Errorf("cannot decode index: %v", err) break } - decKey, err := decode(path.Base(ent.Key)) + var decKey string + decKey, err = decode(path.Base(ent.Key)) if err != nil { err = fmt.Errorf("cannot decode key: %v", err) break diff --git a/pinning/pinner/dspinner/pin.go b/pinning/pinner/dspinner/pin.go index f9f5ff9bf..4adf95a6e 100644 --- a/pinning/pinner/dspinner/pin.go +++ b/pinning/pinner/dspinner/pin.go @@ -18,7 +18,6 @@ import ( ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" "github.com/ipfs/go-merkledag" - mdag "github.com/ipfs/go-merkledag" "github.com/ipfs/go-merkledag/dagutils" "github.com/polydawn/refmt/cbor" "github.com/polydawn/refmt/obj/atlas" @@ -191,7 +190,7 @@ func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { // temporary unlock to fetch the entire graph p.lock.Unlock() // Fetch graph starting at node identified by cid - err = mdag.FetchGraph(ctx, c, p.dserv) + err = merkledag.FetchGraph(ctx, c, p.dserv) p.lock.Lock() if err != nil { return err diff --git a/pinning/pinner/dspinner/pin_test.go b/pinning/pinner/dspinner/pin_test.go index 46a2f94a5..500d3e3e7 100644 --- a/pinning/pinner/dspinner/pin_test.go +++ b/pinning/pinner/dspinner/pin_test.go @@ -893,9 +893,7 @@ func makeStore() (ds.Datastore, ipld.DAGService) { if err != nil { panic(err) } - var dstore ds.Batching - dstore = &batchWrap{ldstore} - + dstore := &batchWrap{ldstore} bstore := blockstore.NewBlockstore(dstore) bserv := bs.New(bstore, offline.Exchange(bstore)) dserv := mdag.NewDAGService(bserv) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 7e1d88602..2bae75841 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -8,11 +8,8 @@ import ( cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - logging "github.com/ipfs/go-log" ) -var log = logging.Logger("pin") - const ( linkRecursive = "recursive" linkDirect = "direct" From f2a8fd4329e65cf6c5f41a11c515e1188ef6ee63 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sat, 15 May 2021 18:11:40 -0700 Subject: [PATCH 3391/3817] fix staticcheck This commit was moved from ipfs/go-blockservice@9659d2af50549ac49c9e2b849d197b873b081c2f --- blockservice/test/mock.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index a6eba6911..d4a611d43 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -1,24 +1,23 @@ package bstest import ( - . "github.com/ipfs/go-blockservice" - testinstance "github.com/ipfs/go-bitswap/testinstance" tn "github.com/ipfs/go-bitswap/testnet" + "github.com/ipfs/go-blockservice" delay "github.com/ipfs/go-ipfs-delay" mockrouting "github.com/ipfs/go-ipfs-routing/mock" ) // Mocks returns |n| connected mock Blockservices -func Mocks(n int) []BlockService { +func Mocks(n int) []blockservice.BlockService { net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(0)) sg := testinstance.NewTestInstanceGenerator(net) instances := sg.Instances(n) - var servs []BlockService + var servs []blockservice.BlockService for _, i := range instances { - servs = append(servs, New(i.Blockstore(), i.Exchange)) + servs = append(servs, blockservice.New(i.Blockstore(), i.Exchange)) } return servs } From 43462bd16cb81d7b51ca4e449d046425da18fe6e Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Mon, 17 May 2021 10:50:06 -0700 Subject: [PATCH 3392/3817] update go-bitswap to v0.3.4 This fixes a panic of the tests on i386, see https://github.com/ipfs/go-bitswap/pull/478 for details. This commit was moved from ipfs/go-blockservice@418971a9884fd4d97bec0e61de2bb9ca65ddd683 --- blockservice/blockservice.go | 10 +++++----- blockservice/test/mock.go | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 2f320b139..2c860aefd 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -147,7 +147,7 @@ func (s *blockService) AddBlock(o blocks.Block) error { return err } - log.Event(context.TODO(), "BlockService.BlockAdded", c) + log.Debugf("BlockService.BlockAdded %s", c) if s.exchange != nil { if err := s.exchange.HasBlock(o); err != nil { @@ -193,7 +193,7 @@ func (s *blockService) AddBlocks(bs []blocks.Block) error { if s.exchange != nil { for _, o := range toput { - log.Event(context.TODO(), "BlockService.BlockAdded", o.Cid()) + log.Debugf("BlockService.BlockAdded %s", o.Cid()) if err := s.exchange.HasBlock(o); err != nil { log.Errorf("HasBlock: %s", err.Error()) } @@ -243,7 +243,7 @@ func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget fun } return nil, err } - log.Event(ctx, "BlockService.BlockFetched", c) + log.Debugf("BlockService.BlockFetched %s", c) return blk, nil } @@ -320,7 +320,7 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget } for b := range rblocks { - log.Event(ctx, "BlockService.BlockFetched", b.Cid()) + log.Debugf("BlockService.BlockFetched %s", b.Cid()) select { case out <- b: case <-ctx.Done(): @@ -335,7 +335,7 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget func (s *blockService) DeleteBlock(c cid.Cid) error { err := s.blockstore.DeleteBlock(c) if err == nil { - log.Event(context.TODO(), "BlockService.BlockDeleted", c) + log.Debugf("BlockService.BlockDeleted %s", c) } return err } diff --git a/blockservice/test/mock.go b/blockservice/test/mock.go index d4a611d43..d55be8a3f 100644 --- a/blockservice/test/mock.go +++ b/blockservice/test/mock.go @@ -11,7 +11,7 @@ import ( // Mocks returns |n| connected mock Blockservices func Mocks(n int) []blockservice.BlockService { net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(0)) - sg := testinstance.NewTestInstanceGenerator(net) + sg := testinstance.NewTestInstanceGenerator(net, nil, nil) instances := sg.Instances(n) From aebf6e183c75f4d2f1e54716c836d8e7996815b3 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Wed, 19 May 2021 13:16:44 -0300 Subject: [PATCH 3393/3817] fix: always return upgradeable instead of basic dir (#92) This commit was moved from ipfs/go-unixfs@9dd1330c931383ed297ba39c191dd5f8b8f9ab55 --- unixfs/io/directory.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index b0c4549aa..0812670df 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -134,7 +134,7 @@ func NewDirectoryFromNode(dserv ipld.DAGService, node ipld.Node) (Directory, err switch fsNode.Type() { case format.TDirectory: - return newBasicDirectoryFromNode(dserv, protoBufNode.Copy().(*mdag.ProtoNode)), nil + return &UpgradeableDirectory{newBasicDirectoryFromNode(dserv, protoBufNode.Copy().(*mdag.ProtoNode))}, nil case format.THAMTShard: shard, err := hamt.NewHamtFromDag(dserv, node) if err != nil { From b85e50b956d3766b2f00201e79aba74d2e7e047d Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Wed, 19 May 2021 12:32:04 -0700 Subject: [PATCH 3394/3817] fix staticcheck This commit was moved from ipfs/go-ipfs-exchange-offline@57aa7ef2f88ce79285781157fc6454d544fe91f6 --- exchange/offline/offline.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index cb82b8a0a..88d04469b 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -34,7 +34,7 @@ func (e *offlineExchange) HasBlock(b blocks.Block) error { } // Close always returns nil. -func (_ *offlineExchange) Close() error { +func (e *offlineExchange) Close() error { // NB: exchange doesn't own the blockstore's underlying datastore, so it is // not responsible for closing it. return nil @@ -44,11 +44,9 @@ func (e *offlineExchange) GetBlocks(ctx context.Context, ks []cid.Cid) (<-chan b out := make(chan blocks.Block) go func() { defer close(out) - var misses []cid.Cid for _, k := range ks { hit, err := e.bs.Get(k) if err != nil { - misses = append(misses, k) // a long line of misses should abort when context is cancelled. select { // TODO case send misses down channel From f60162cb66c693adb5d89049f0ab4ec927822171 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Fri, 21 May 2021 16:50:49 -0300 Subject: [PATCH 3395/3817] fix(directory): initialize size when computing it This commit was moved from ipfs/go-unixfs@c3f568f65f9883981076ba81a8e11cfa3b0f55ff --- unixfs/io/directory.go | 1 + 1 file changed, 1 insertion(+) diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index b0c4549aa..62e57c874 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -150,6 +150,7 @@ func NewDirectoryFromNode(dserv ipld.DAGService, node ipld.Node) (Directory, err } func (d *BasicDirectory) computeEstimatedSize() { + d.estimatedSize = 0 d.ForEachLink(nil, func(l *ipld.Link) error { d.addToEstimatedSize(l.Name, l.Cid) return nil From 6e2490351853d5935bf49e488fe8c1991c5410f5 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Tue, 25 May 2021 19:23:37 -0400 Subject: [PATCH 3396/3817] fix: skip providing if the key set is empty after removing invalid CIDs This commit was moved from ipfs/go-ipfs-provider@28e02d77939162d75120b6ce3dac99317221c5a7 --- provider/batched/system.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/provider/batched/system.go b/provider/batched/system.go index 5637e55b1..111ee115b 100644 --- a/provider/batched/system.go +++ b/provider/batched/system.go @@ -225,6 +225,11 @@ func (s *BatchProvidingSystem) Run() { keys = append(keys, c.Hash()) } + // in case after removing all the invalid CIDs there are no valid ones left + if len(keys) == 0 { + continue + } + for !s.rsys.Ready() { log.Debugf("reprovider system not ready") select { From 5df09f84256c8b490f356c161c96b49d3a46bfd0 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Wed, 2 Jun 2021 09:11:14 -0700 Subject: [PATCH 3397/3817] fix staticcheck This commit was moved from ipfs/interface-go-ipfs-core@08bd316e61238880f341d7061c518c2a62830bd9 --- coreiface/tests/api.go | 2 +- coreiface/tests/block.go | 2 +- coreiface/tests/dag.go | 2 +- coreiface/tests/dht.go | 2 +- coreiface/tests/key.go | 12 ++++++------ coreiface/tests/name.go | 2 +- coreiface/tests/object.go | 2 +- coreiface/tests/pin.go | 2 +- coreiface/tests/pubsub.go | 2 +- coreiface/tests/unixfs.go | 5 ++--- 10 files changed, 16 insertions(+), 17 deletions(-) diff --git a/coreiface/tests/api.go b/coreiface/tests/api.go index 1af3a83b3..0801b3ca7 100644 --- a/coreiface/tests/api.go +++ b/coreiface/tests/api.go @@ -9,7 +9,7 @@ import ( coreiface "github.com/ipfs/interface-go-ipfs-core" ) -var apiNotImplemented = errors.New("api not implemented") +var errAPINotImplemented = errors.New("api not implemented") func (tp *TestSuite) makeAPI(ctx context.Context) (coreiface.CoreAPI, error) { api, err := tp.MakeAPISwarm(ctx, false, 1) diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 1f7252547..7dbfa4df0 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -32,7 +32,7 @@ func cborBlock() io.Reader { func (tp *TestSuite) TestBlock(t *testing.T) { tp.hasApi(t, func(api coreiface.CoreAPI) error { if api.Block() == nil { - return apiNotImplemented + return errAPINotImplemented } return nil }) diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index 2f68bbf05..6f9d9659e 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -18,7 +18,7 @@ import ( func (tp *TestSuite) TestDag(t *testing.T) { tp.hasApi(t, func(api coreiface.CoreAPI) error { if api.Dag() == nil { - return apiNotImplemented + return errAPINotImplemented } return nil }) diff --git a/coreiface/tests/dht.go b/coreiface/tests/dht.go index a957d66d7..c2e6d690f 100644 --- a/coreiface/tests/dht.go +++ b/coreiface/tests/dht.go @@ -13,7 +13,7 @@ import ( func (tp *TestSuite) TestDht(t *testing.T) { tp.hasApi(t, func(api iface.CoreAPI) error { if api.Dht() == nil { - return apiNotImplemented + return errAPINotImplemented } return nil }) diff --git a/coreiface/tests/key.go b/coreiface/tests/key.go index c3cd8626f..47f278f97 100644 --- a/coreiface/tests/key.go +++ b/coreiface/tests/key.go @@ -5,8 +5,7 @@ import ( "strings" "testing" - cid "github.com/ipfs/go-cid" - coreiface "github.com/ipfs/interface-go-ipfs-core" + "github.com/ipfs/go-cid" iface "github.com/ipfs/interface-go-ipfs-core" opt "github.com/ipfs/interface-go-ipfs-core/options" mbase "github.com/multiformats/go-multibase" @@ -15,7 +14,7 @@ import ( func (tp *TestSuite) TestKey(t *testing.T) { tp.hasApi(t, func(api iface.CoreAPI) error { if api.Key() == nil { - return apiNotImplemented + return errAPINotImplemented } return nil }) @@ -67,8 +66,8 @@ func (tp *TestSuite) TestListSelf(t *testing.T) { t.Errorf("expected the key to be called 'self', got '%s'", keys[0].Name()) } - if keys[0].Path().String() != "/ipns/"+coreiface.FormatKeyID(self.ID()) { - t.Errorf("expected the key to have path '/ipns/%s', got '%s'", coreiface.FormatKeyID(self.ID()), keys[0].Path().String()) + if keys[0].Path().String() != "/ipns/"+iface.FormatKeyID(self.ID()) { + t.Errorf("expected the key to have path '/ipns/%s', got '%s'", iface.FormatKeyID(self.ID()), keys[0].Path().String()) } } @@ -185,9 +184,10 @@ func (tp *TestSuite) TestGenerateSize(t *testing.T) { } func (tp *TestSuite) TestGenerateType(t *testing.T) { + t.Skip("disabled until libp2p/specs#111 is fixed") + ctx, cancel := context.WithCancel(context.Background()) defer cancel() - t.Skip("disabled until libp2p/specs#111 is fixed") api, err := tp.makeAPI(ctx) if err != nil { diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index 021c1bb97..2a8b4d76a 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -19,7 +19,7 @@ import ( func (tp *TestSuite) TestName(t *testing.T) { tp.hasApi(t, func(api coreiface.CoreAPI) error { if api.Name() == nil { - return apiNotImplemented + return errAPINotImplemented } return nil }) diff --git a/coreiface/tests/object.go b/coreiface/tests/object.go index 2e066ca71..e8ab1a7f4 100644 --- a/coreiface/tests/object.go +++ b/coreiface/tests/object.go @@ -15,7 +15,7 @@ import ( func (tp *TestSuite) TestObject(t *testing.T) { tp.hasApi(t, func(api iface.CoreAPI) error { if api.Object() == nil { - return apiNotImplemented + return errAPINotImplemented } return nil }) diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index 476bbea6b..d378d1015 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -18,7 +18,7 @@ import ( func (tp *TestSuite) TestPin(t *testing.T) { tp.hasApi(t, func(api iface.CoreAPI) error { if api.Pin() == nil { - return apiNotImplemented + return errAPINotImplemented } return nil }) diff --git a/coreiface/tests/pubsub.go b/coreiface/tests/pubsub.go index 36353f836..f8339f228 100644 --- a/coreiface/tests/pubsub.go +++ b/coreiface/tests/pubsub.go @@ -12,7 +12,7 @@ import ( func (tp *TestSuite) TestPubSub(t *testing.T) { tp.hasApi(t, func(api iface.CoreAPI) error { if api.PubSub() == nil { - return apiNotImplemented + return errAPINotImplemented } return nil }) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 1ed80e873..4273386aa 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -31,7 +31,7 @@ import ( func (tp *TestSuite) TestUnixfs(t *testing.T) { tp.hasApi(t, func(api coreiface.CoreAPI) error { if api.Unixfs() == nil { - return apiNotImplemented + return errAPINotImplemented } return nil }) @@ -1035,8 +1035,7 @@ func (tp *TestSuite) TestGetReadAt(t *testing.T) { origR := bytes.NewReader(orig) - r, err = api.Unixfs().Get(ctx, p) - if err != nil { + if _, err := api.Unixfs().Get(ctx, p); err != nil { t.Fatal(err) } From 74605a9fef1b920af6a4ad53b253fd36df681f2d Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Wed, 2 Jun 2021 09:17:07 -0700 Subject: [PATCH 3398/3817] fix staticcheck This commit was moved from ipfs/go-ipfs-routing@be9d9edc34835283c29a722be14e2898c447ffc0 --- routing/mock/centralized_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index 2767ff1a2..fc832cf7a 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" delay "github.com/ipfs/go-ipfs-delay" u "github.com/ipfs/go-ipfs-util" @@ -44,8 +44,8 @@ func TestClientFindProviders(t *testing.T) { providersFromClient := client.FindProvidersAsync(context.Background(), k, max) isInClient := false - for pi := range providersFromClient { - if pi.ID == pi.ID { // <-- typo? + for p := range providersFromClient { + if p.ID == pi.ID() { isInClient = true } } From 973264d33e0d1747f62b63f2c38fdd58fcdd5dc1 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Wed, 2 Jun 2021 09:20:01 -0700 Subject: [PATCH 3399/3817] fix staticcheck This commit was moved from ipfs/go-ipfs-util@508ba17d06c4133acccb88c2aeab4c50aed04b8c --- util/util.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util/util.go b/util/util.go index 8ebe3c706..ffcab2f33 100644 --- a/util/util.go +++ b/util/util.go @@ -23,14 +23,14 @@ const DefaultIpfsHash = mh.SHA2_256 var Debug bool // ErrNotImplemented signifies a function has not been implemented yet. -var ErrNotImplemented = errors.New("Error: not implemented yet.") +var ErrNotImplemented = errors.New("error: not implemented yet") // ErrTimeout implies that a timeout has been triggered -var ErrTimeout = errors.New("Error: Call timed out.") +var ErrTimeout = errors.New("error: call timed out") -// ErrSearchIncomplete implies that a search type operation didnt +// ErrSearchIncomplete implies that a search type operation didn't // find the expected node, but did find 'a' node. -var ErrSearchIncomplete = errors.New("Error: Search Incomplete.") +var ErrSearchIncomplete = errors.New("error: search incomplete") // ErrCast is returned when a cast fails AND the program should not panic. func ErrCast() error { From e320b1c850abcf2ddb1b71b2ec7ab877e7505568 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Wed, 2 Jun 2021 09:26:35 -0700 Subject: [PATCH 3400/3817] fix staticcheck This commit was moved from ipfs/go-unixfs@c8d1d63a7e0c0eca4b3064fa174e18ed5e2f5d84 --- unixfs/hamt/hamt_test.go | 21 +++++++++++++++++++++ unixfs/io/directory.go | 6 +++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 4d2f64b22..8d0b93889 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -280,6 +280,9 @@ func TestRemoveAfterMarshal(t *testing.T) { } s, err = NewHamtFromDag(ds, nd) + if err != nil { + t.Fatal(err) + } ctx := context.Background() @@ -334,7 +337,13 @@ func TestSetAfterMarshal(t *testing.T) { } nd, err = nds.Node() + if err != nil { + t.Fatal(err) + } nds, err = NewHamtFromDag(ds, nd) + if err != nil { + t.Fatal(err) + } links, err := nds.EnumLinks(ctx) if err != nil { @@ -408,7 +417,13 @@ func TestDuplicateAddShard(t *testing.T) { } node, err := dir.Node() + if err != nil { + t.Fatal(err) + } dir, err = NewHamtFromDag(ds, node) + if err != nil { + t.Fatal(err) + } lnks, err := dir.EnumLinks(ctx) if err != nil { @@ -503,7 +518,13 @@ func TestRemoveElemsAfterMarshal(t *testing.T) { } nd, err = nds.Node() + if err != nil { + t.Fatal(err) + } nds, err = NewHamtFromDag(ds, nd) + if err != nil { + t.Fatal(err) + } links, err := nds.EnumLinks(ctx) if err != nil { diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index 4163b80d6..15c7e862a 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -8,9 +8,9 @@ import ( mdag "github.com/ipfs/go-merkledag" format "github.com/ipfs/go-unixfs" - hamt "github.com/ipfs/go-unixfs/hamt" + "github.com/ipfs/go-unixfs/hamt" - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" ) @@ -151,7 +151,7 @@ func NewDirectoryFromNode(dserv ipld.DAGService, node ipld.Node) (Directory, err func (d *BasicDirectory) computeEstimatedSize() { d.estimatedSize = 0 - d.ForEachLink(nil, func(l *ipld.Link) error { + d.ForEachLink(context.TODO(), func(l *ipld.Link) error { d.addToEstimatedSize(l.Name, l.Cid) return nil }) From 6edffc7791305aa9e70e214d327eaa7ca9f57ce5 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 3 Jun 2021 13:37:15 +1000 Subject: [PATCH 3401/3817] fix: ReadHeader return value mismatch This commit was moved from ipld/go-car@97fb3e695cac0071c0935196cd99e5bef828501b --- ipld/car/car/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/car/car/main.go b/ipld/car/car/main.go index 8ccd4e5d8..c88459ade 100644 --- a/ipld/car/car/main.go +++ b/ipld/car/car/main.go @@ -26,7 +26,7 @@ var headerCmd = cli.Command{ } defer fi.Close() - ch, _, err := car.ReadHeader(bufio.NewReader(fi)) + ch, err := car.ReadHeader(bufio.NewReader(fi)) if err != nil { return err } From 873f8f2e2f10b27697efa657caaa9d4f7b197c77 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 3 Jun 2021 13:57:27 +1000 Subject: [PATCH 3402/3817] fix: lint errors This commit was moved from ipld/go-car@5bff03e9b7458ee5bdefb43671eeeb787ec59aa9 --- ipld/car/car/main.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/ipld/car/car/main.go b/ipld/car/car/main.go index c88459ade..e1be8830e 100644 --- a/ipld/car/car/main.go +++ b/ipld/car/car/main.go @@ -61,12 +61,11 @@ var verifyCmd = cli.Command{ for { _, err := cr.Next() - switch err { - case io.EOF: + if err == io.EOF { return nil - default: + } + if err != nil { return err - case nil: } } }, @@ -93,12 +92,11 @@ var lsCmd = cli.Command{ for { blk, err := cr.Next() - switch err { - case io.EOF: + if err == io.EOF { return nil - default: + } + if err != nil { return err - case nil: } fmt.Println(blk.Cid()) } @@ -112,5 +110,5 @@ func main() { lsCmd, verifyCmd, } - app.RunAndExitOnError() + app.Run(os.Args) } From b81b803836215c86c92bd15f2d5cf01c3a3c2289 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 3 Jun 2021 18:58:25 +1000 Subject: [PATCH 3403/3817] chore: add header error tests Ref: https://github.com/ipld/js-car/pull/28 the primary difference with js-car is that go-car requires a non-empty roots array whereas js-car is fine with empty roots array, hence the test fixture differences This commit was moved from ipld/go-car@2876c180ffecb8139ff380cefa519842e38f456c --- ipld/car/.gitignore | 3 ++ ipld/car/car.go | 10 +++--- ipld/car/car_test.go | 78 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 ipld/car/.gitignore diff --git a/ipld/car/.gitignore b/ipld/car/.gitignore new file mode 100644 index 000000000..3b831d277 --- /dev/null +++ b/ipld/car/.gitignore @@ -0,0 +1,3 @@ +car/car +main +coverage.txt diff --git a/ipld/car/car.go b/ipld/car/car.go index 30fdd3449..267237f28 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -77,7 +77,7 @@ func ReadHeader(br *bufio.Reader) (*CarHeader, error) { var ch CarHeader if err := cbor.DecodeInto(hb, &ch); err != nil { - return nil, err + return nil, fmt.Errorf("invalid header: %v", err) } return &ch, nil @@ -130,14 +130,14 @@ func NewCarReader(r io.Reader) (*CarReader, error) { return nil, err } - if len(ch.Roots) == 0 { - return nil, fmt.Errorf("empty car") - } - if ch.Version != 1 { return nil, fmt.Errorf("invalid car version: %d", ch.Version) } + if len(ch.Roots) == 0 { + return nil, fmt.Errorf("empty car, no roots") + } + return &CarReader{ br: br, Header: ch, diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index b50111b27..e9ae105dc 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -5,6 +5,7 @@ import ( "context" "encoding/hex" "io" + "strings" "testing" cid "github.com/ipfs/go-cid" @@ -234,3 +235,80 @@ func TestEOFHandling(t *testing.T) { } }) } + +func TestBadHeaders(t *testing.T) { + makeCar := func(t *testing.T, byts string) error { + fixture, err := hex.DecodeString(byts) + if err != nil { + t.Fatal(err) + } + _, err = NewCarReader(bytes.NewReader(fixture)) + return err + } + + t.Run("Sanity check {version:1,roots:[baeaaaa3bmjrq]}", func(t *testing.T) { + err := makeCar(t, "1ca265726f6f747381d82a4800010000036162636776657273696f6e01") + if err != nil { + t.Fatal(err) + } + }) + + t.Run("{version:2}", func(t *testing.T) { + err := makeCar(t, "0aa16776657273696f6e02") + if err.Error() != "invalid car version: 2" { + t.Fatalf("bad error: %v", err) + } + }) + + // an unfortunate error because we don't use a pointer + t.Run("{roots:[baeaaaa3bmjrq]}", func(t *testing.T) { + err := makeCar(t, "13a165726f6f747381d82a480001000003616263") + if err.Error() != "invalid car version: 0" { + t.Fatalf("bad error: %v", err) + } + }) + + t.Run("{version:\"1\",roots:[baeaaaa3bmjrq]}", func(t *testing.T) { + err := makeCar(t, "1da265726f6f747381d82a4800010000036162636776657273696f6e6131") + if !strings.HasPrefix(err.Error(), "invalid header: ") { + t.Fatalf("bad error: %v", err) + } + }) + + t.Run("{version:1}", func(t *testing.T) { + err := makeCar(t, "0aa16776657273696f6e01") + if err.Error() != "empty car, no roots" { + t.Fatalf("bad error: %v", err) + } + }) + + t.Run("{version:1,roots:{cid:baeaaaa3bmjrq}}", func(t *testing.T) { + err := makeCar(t, "20a265726f6f7473a163636964d82a4800010000036162636776657273696f6e01") + if !strings.HasPrefix(err.Error(), "invalid header: ") { + t.Fatalf("bad error: %v", err) + } + }) + + t.Run("{version:1,roots:[baeaaaa3bmjrq],blip:true}", func(t *testing.T) { + err := makeCar(t, "22a364626c6970f565726f6f747381d82a4800010000036162636776657273696f6e01") + if !strings.HasPrefix(err.Error(), "invalid header: ") { + t.Fatalf("bad error: %v", err) + } + }) + + t.Run("[1,[]]", func(t *testing.T) { + err := makeCar(t, "03820180") + if !strings.HasPrefix(err.Error(), "invalid header: ") { + t.Fatalf("bad error: %v", err) + } + }) + + // this is an unfortunate error, it'd be nice to catch it better but it's + // very unlikely we'd ever see this in practice + t.Run("null", func(t *testing.T) { + err := makeCar(t, "01f6") + if !strings.HasPrefix(err.Error(), "invalid car version: 0") { + t.Fatalf("bad error: %v", err) + } + }) +} From 24de071cad2b9bbb7240bb246d6759ceb05111e8 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 3 Jun 2021 21:28:32 +1000 Subject: [PATCH 3404/3817] chore: refactor header tests to iterate over a struct This commit was moved from ipld/go-car@dde2a73215367e9d274512a1aa7b2d48a363d962 --- ipld/car/car_test.go | 124 +++++++++++++++++++++++-------------------- 1 file changed, 66 insertions(+), 58 deletions(-) diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index e9ae105dc..0c3515636 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -237,6 +237,58 @@ func TestEOFHandling(t *testing.T) { } func TestBadHeaders(t *testing.T) { + testCases := []struct { + name string + hex string + errStr string // either the whole error string + errPfx string // or just the prefix + }{ + { + "{version:2}", + "0aa16776657273696f6e02", + "invalid car version: 2", + "", + }, + { + // an unfortunate error because we don't use a pointer + "{roots:[baeaaaa3bmjrq]}", + "13a165726f6f747381d82a480001000003616263", + "invalid car version: 0", + "", + }, { + "{version:\"1\",roots:[baeaaaa3bmjrq]}", + "1da265726f6f747381d82a4800010000036162636776657273696f6e6131", + "", "invalid header: ", + }, { + "{version:1}", + "0aa16776657273696f6e01", + "empty car, no roots", + "", + }, { + "{version:1,roots:{cid:baeaaaa3bmjrq}}", + "20a265726f6f7473a163636964d82a4800010000036162636776657273696f6e01", + "", + "invalid header: ", + }, { + "{version:1,roots:[baeaaaa3bmjrq],blip:true}", + "22a364626c6970f565726f6f747381d82a4800010000036162636776657273696f6e01", + "", + "invalid header: ", + }, { + "[1,[]]", + "03820180", + "", + "invalid header: ", + }, { + // this is an unfortunate error, it'd be nice to catch it better but it's + // very unlikely we'd ever see this in practice + "null", + "01f6", + "", + "invalid car version: 0", + }, + } + makeCar := func(t *testing.T, byts string) error { fixture, err := hex.DecodeString(byts) if err != nil { @@ -253,62 +305,18 @@ func TestBadHeaders(t *testing.T) { } }) - t.Run("{version:2}", func(t *testing.T) { - err := makeCar(t, "0aa16776657273696f6e02") - if err.Error() != "invalid car version: 2" { - t.Fatalf("bad error: %v", err) - } - }) - - // an unfortunate error because we don't use a pointer - t.Run("{roots:[baeaaaa3bmjrq]}", func(t *testing.T) { - err := makeCar(t, "13a165726f6f747381d82a480001000003616263") - if err.Error() != "invalid car version: 0" { - t.Fatalf("bad error: %v", err) - } - }) - - t.Run("{version:\"1\",roots:[baeaaaa3bmjrq]}", func(t *testing.T) { - err := makeCar(t, "1da265726f6f747381d82a4800010000036162636776657273696f6e6131") - if !strings.HasPrefix(err.Error(), "invalid header: ") { - t.Fatalf("bad error: %v", err) - } - }) - - t.Run("{version:1}", func(t *testing.T) { - err := makeCar(t, "0aa16776657273696f6e01") - if err.Error() != "empty car, no roots" { - t.Fatalf("bad error: %v", err) - } - }) - - t.Run("{version:1,roots:{cid:baeaaaa3bmjrq}}", func(t *testing.T) { - err := makeCar(t, "20a265726f6f7473a163636964d82a4800010000036162636776657273696f6e01") - if !strings.HasPrefix(err.Error(), "invalid header: ") { - t.Fatalf("bad error: %v", err) - } - }) - - t.Run("{version:1,roots:[baeaaaa3bmjrq],blip:true}", func(t *testing.T) { - err := makeCar(t, "22a364626c6970f565726f6f747381d82a4800010000036162636776657273696f6e01") - if !strings.HasPrefix(err.Error(), "invalid header: ") { - t.Fatalf("bad error: %v", err) - } - }) - - t.Run("[1,[]]", func(t *testing.T) { - err := makeCar(t, "03820180") - if !strings.HasPrefix(err.Error(), "invalid header: ") { - t.Fatalf("bad error: %v", err) - } - }) - - // this is an unfortunate error, it'd be nice to catch it better but it's - // very unlikely we'd ever see this in practice - t.Run("null", func(t *testing.T) { - err := makeCar(t, "01f6") - if !strings.HasPrefix(err.Error(), "invalid car version: 0") { - t.Fatalf("bad error: %v", err) - } - }) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := makeCar(t, tc.hex) + if tc.errStr != "" { + if err.Error() != tc.errStr { + t.Fatalf("bad error: %v", err) + } + } else { + if !strings.HasPrefix(err.Error(), tc.errPfx) { + t.Fatalf("bad error: %v", err) + } + } + }) + } } From c3b78ad4698e92945dfa0fc4d54eb248ca461742 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 3 Jun 2021 21:43:30 +1000 Subject: [PATCH 3405/3817] chore: make sure we get an error where we expect one This commit was moved from ipld/go-car@dce539042aceacdd96e96ca6ea6eed8989b696b1 --- ipld/car/car_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 0c3515636..13f96887b 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -308,6 +308,9 @@ func TestBadHeaders(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { err := makeCar(t, tc.hex) + if err == nil { + t.Fatal("expected error from bad header, didn't get one") + } if tc.errStr != "" { if err.Error() != tc.errStr { t.Fatalf("bad error: %v", err) From 9a1b1c060a4a778d169e05737d527415db47cffb Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 10 Jun 2021 11:12:05 +0100 Subject: [PATCH 3406/3817] Add `doc.go` to avoid CI issues for empty modules We might want to get this fixed at source; it is valid for a module to have no go files i think, and CI should tolerate that. This commit was moved from ipld/go-car@0c1de8338d512c16f7e505f71916e196c081b23d --- ipld/car/v2/doc.go | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 ipld/car/v2/doc.go diff --git a/ipld/car/v2/doc.go b/ipld/car/v2/doc.go new file mode 100644 index 000000000..5b210211b --- /dev/null +++ b/ipld/car/v2/doc.go @@ -0,0 +1,3 @@ +// Package car represents the CAR v2 implementation. +// TODO add CAR v2 byte structure here. +package car From f390a51295aec6089a4f289ba7c1707a7107c599 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 9 Jun 2021 13:08:17 +0100 Subject: [PATCH 3407/3817] Define basic CAR v2 constants along with tests Define the magic CAR v2 prefix proposed in ipld/specs#248 and assert that it remains a valid CAR v1 header, along with other tests that verify the expected length, content and version number. Define basic structs to represent CAR v2 header, along with placeholder for the future "characteristics" bitfield, and the optional padding between CAR v1 dump and index added to the end of a CAR v2 archive. Relates to: - https://github.com/ipld/specs/pull/248#issuecomment-833141588 This commit was moved from ipld/go-car@b76ee51478058763a5c7503cfd1226f5680f501c --- ipld/car/v2/car.go | 46 ++++++++++++++++++++++++ ipld/car/v2/car_test.go | 78 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 ipld/car/v2/car.go create mode 100644 ipld/car/v2/car_test.go diff --git a/ipld/car/v2/car.go b/ipld/car/v2/car.go new file mode 100644 index 000000000..c71d502f1 --- /dev/null +++ b/ipld/car/v2/car.go @@ -0,0 +1,46 @@ +package car + +const prefixBytesSize = 16 +const headerBytesSize = 32 + +var ( + // The fixed prefix of a CAR v2, signalling the version number to previous versions for graceful fail over. + PrefixBytes = []byte{ + 0x0a, // unit(10) + 0xa1, // map(1) + 0x67, // string(7) + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, // "version" + 0x02, // uint(2) + } + // The size of the CAR v2 prefix in 11 bytes, (i.e. 11). + PrefixBytesSize = uint64(len(PrefixBytes)) + // Reserved 128 bits space to capture future characteristics of CAR v2 such as order, duplication, etc. + EmptyCharacteristics = new(Characteristics) +) + +type ( + // Header represents the CAR v2 header/pragma. + Header struct { + // 128-bit characteristics of this CAR v2 file, such as order, deduplication, etc. Reserved for future use. + Characteristics Characteristics + // The size of CAR v1 encapsulated in this CAR v2 as bytes. + CarV1Size uint64 + // The offset from the beginning of the file at which the CAR v2 index begins. + IndexOffset uint64 + } + // Characteristics is a bitfield placeholder for capturing the characteristics of a CAR v2 such as order and determinism. + Characteristics struct { + Hi uint64 + Lo uint64 + } +) + +// Size gets the size of Header in number of bytes. +func (h *Header) Size() int { + return headerBytesSize +} + +// Size gets the size of Characteristics in number of bytes. +func (c *Characteristics) Size() int { + return prefixBytesSize +} diff --git a/ipld/car/v2/car_test.go b/ipld/car/v2/car_test.go new file mode 100644 index 000000000..21a726ff6 --- /dev/null +++ b/ipld/car/v2/car_test.go @@ -0,0 +1,78 @@ +package car_test + +import ( + cbor "github.com/ipfs/go-ipld-cbor" + car_v1 "github.com/ipld/go-car" + car_v2 "github.com/ipld/go-car/v2" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestCarV2PrefixLength(t *testing.T) { + tests := []struct { + name string + want interface{} + got interface{} + }{ + { + "cached size should be 11 bytes", + 11, + car_v2.PrefixBytesSize, + }, + { + "actual size should be 11 bytes", + 11, + len(car_v2.PrefixBytes), + }, + { + "should start with varint(10)", + car_v2.PrefixBytes[0], + 10, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert.EqualValues(t, tt.want, tt.got, "CarV2Prefix got = %v, want %v", tt.got, tt.want) + }) + } +} + +func TestCarV2PrefixIsValidCarV1Header(t *testing.T) { + var v1h car_v1.CarHeader + err := cbor.DecodeInto(car_v2.PrefixBytes[1:], &v1h) + assert.NoError(t, err, "cannot decode prefix as CBOR with CAR v1 header structure") + assert.Equal(t, car_v1.CarHeader{ + Roots: nil, + Version: 2, + }, v1h, "CAR v2 prefix must be a valid CAR v1 header") +} + +func TestEmptyCharacteristics(t *testing.T) { + tests := []struct { + name string + want interface{} + got interface{} + }{ + { + "is of size 16 bytes", + 16, + car_v2.EmptyCharacteristics.Size(), + }, + { + "is a whole lot of nothin'", + &car_v2.Characteristics{}, + car_v2.EmptyCharacteristics, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert.EqualValues(t, tt.want, tt.got, "EmptyCharacteristics got = %v, want %v", tt.got, tt.want) + }) + } +} + +func TestHeader_SizeIs32Bytes(t *testing.T) { + assert.Equal(t, 32, new(car_v2.Header).Size()) +} From 588083c1860e6a9e233c8de14d02bc17f9baa4a4 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 10 Jun 2021 17:12:06 +0100 Subject: [PATCH 3408/3817] Export constant header size Reflecting on PR review comments, no harm in exposing this and it may be useful to the users of the library. This commit was moved from ipld/go-car@29e325ac9c5fe71798611f34f3bea08bcfc11766 --- ipld/car/v2/car.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ipld/car/v2/car.go b/ipld/car/v2/car.go index c71d502f1..68a78ffa1 100644 --- a/ipld/car/v2/car.go +++ b/ipld/car/v2/car.go @@ -1,7 +1,7 @@ package car -const prefixBytesSize = 16 -const headerBytesSize = 32 +// HeaderBytesSize is the fixed size of CAR v2 header in number of bytes. +const HeaderBytesSize = 32 var ( // The fixed prefix of a CAR v2, signalling the version number to previous versions for graceful fail over. @@ -13,7 +13,7 @@ var ( 0x02, // uint(2) } // The size of the CAR v2 prefix in 11 bytes, (i.e. 11). - PrefixBytesSize = uint64(len(PrefixBytes)) + PrefixBytesSize = len(PrefixBytes) // Reserved 128 bits space to capture future characteristics of CAR v2 such as order, duplication, etc. EmptyCharacteristics = new(Characteristics) ) @@ -37,10 +37,10 @@ type ( // Size gets the size of Header in number of bytes. func (h *Header) Size() int { - return headerBytesSize + return HeaderBytesSize } // Size gets the size of Characteristics in number of bytes. func (c *Characteristics) Size() int { - return prefixBytesSize + return PrefixBytesSize } From 733fc62cde55619c503d87fd852c8936636ef201 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 10 Jun 2021 17:22:47 +0100 Subject: [PATCH 3409/3817] Add `CarV1Offset` field to CAR v2 header Reflecting on the review comment, adding this field could provide optimisation opportunities in the future in the context of block alignment. See: - https://github.com/ipld/go-car/pull/80/files#r649241583 This commit was moved from ipld/go-car@17e0aa9ef03c77f0ab0bf019b92c5313fb809a5a --- ipld/car/v2/car.go | 12 +++++++++--- ipld/car/v2/car_test.go | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ipld/car/v2/car.go b/ipld/car/v2/car.go index 68a78ffa1..c274d33e0 100644 --- a/ipld/car/v2/car.go +++ b/ipld/car/v2/car.go @@ -1,7 +1,11 @@ package car -// HeaderBytesSize is the fixed size of CAR v2 header in number of bytes. -const HeaderBytesSize = 32 +const ( + // HeaderBytesSize is the fixed size of CAR v2 header in number of bytes. + HeaderBytesSize = 40 + // CharacteristicsBytesSize is the fixed size of Characteristics bitfield within CAR v2 header in number of bytes. + CharacteristicsBytesSize = 16 +) var ( // The fixed prefix of a CAR v2, signalling the version number to previous versions for graceful fail over. @@ -23,6 +27,8 @@ type ( Header struct { // 128-bit characteristics of this CAR v2 file, such as order, deduplication, etc. Reserved for future use. Characteristics Characteristics + // The offset from the beginning of the file at which the dump of CAR v1 starts. + CarV1Offset uint64 // The size of CAR v1 encapsulated in this CAR v2 as bytes. CarV1Size uint64 // The offset from the beginning of the file at which the CAR v2 index begins. @@ -42,5 +48,5 @@ func (h *Header) Size() int { // Size gets the size of Characteristics in number of bytes. func (c *Characteristics) Size() int { - return PrefixBytesSize + return CharacteristicsBytesSize } diff --git a/ipld/car/v2/car_test.go b/ipld/car/v2/car_test.go index 21a726ff6..aecec024d 100644 --- a/ipld/car/v2/car_test.go +++ b/ipld/car/v2/car_test.go @@ -74,5 +74,5 @@ func TestEmptyCharacteristics(t *testing.T) { } func TestHeader_SizeIs32Bytes(t *testing.T) { - assert.Equal(t, 32, new(car_v2.Header).Size()) + assert.Equal(t, 40, new(car_v2.Header).Size()) } From 430bfd6572eb36c78adf599ab783432735369ddf Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 10 Jun 2021 17:54:37 +0100 Subject: [PATCH 3410/3817] Fix test case name to reflect what the test is Now that the `CarV1Offset` is added to the header, the size is increased from `32` to `40` bytes. Make it so in the test case name. This commit was moved from ipld/go-car@968459cc8d5824473ea939d3c3ece2ebe9edc510 --- ipld/car/v2/car_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/car/v2/car_test.go b/ipld/car/v2/car_test.go index aecec024d..ab5326850 100644 --- a/ipld/car/v2/car_test.go +++ b/ipld/car/v2/car_test.go @@ -73,6 +73,6 @@ func TestEmptyCharacteristics(t *testing.T) { } } -func TestHeader_SizeIs32Bytes(t *testing.T) { +func TestHeader_SizeIs40Bytes(t *testing.T) { assert.Equal(t, 40, new(car_v2.Header).Size()) } From b5d59127b697a0c60df116cec59142ab6c1ee7fd Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 11 Jun 2021 14:03:42 -0700 Subject: [PATCH 3411/3817] chore(deps): move bitfield to ipfs org This commit was moved from ipfs/go-unixfs@a5cc10dc5f2517199908135a5421de68fd8d4495 --- unixfs/hamt/hamt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 374f47df2..55b798ce4 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -25,7 +25,7 @@ import ( "fmt" "os" - bitfield "github.com/Stebalien/go-bitfield" + bitfield "github.com/ipfs/go-bitfield" cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" From 2d24be205ddb4154ec70781035ee6eb6cfce8d19 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 9 Jun 2021 13:54:08 +0100 Subject: [PATCH 3412/3817] Implement CAR v2 header construction and marshalling Define a constructor with sensible defaults, and the ability to customize the defaults conveniently. Implement `io.WriterTo` interface for both header and characteristics to provide a consistent standard API for writing data into a given `io.Writer`. This commit was moved from ipld/go-car@e571973176cfe57e86a6c5f92e84477c62376757 --- ipld/car/v2/car.go | 105 +++++++++++++++++++++++++++++++--- ipld/car/v2/car_test.go | 123 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 218 insertions(+), 10 deletions(-) diff --git a/ipld/car/v2/car.go b/ipld/car/v2/car.go index c274d33e0..428cb591b 100644 --- a/ipld/car/v2/car.go +++ b/ipld/car/v2/car.go @@ -1,10 +1,15 @@ package car +import ( + "encoding/binary" + "io" +) + const ( // HeaderBytesSize is the fixed size of CAR v2 header in number of bytes. - HeaderBytesSize = 40 + HeaderBytesSize uint64 = 40 // CharacteristicsBytesSize is the fixed size of Characteristics bitfield within CAR v2 header in number of bytes. - CharacteristicsBytesSize = 16 + CharacteristicsBytesSize uint64 = 16 ) var ( @@ -17,7 +22,7 @@ var ( 0x02, // uint(2) } // The size of the CAR v2 prefix in 11 bytes, (i.e. 11). - PrefixBytesSize = len(PrefixBytes) + PrefixBytesSize = uint64(len(PrefixBytes)) // Reserved 128 bits space to capture future characteristics of CAR v2 such as order, duplication, etc. EmptyCharacteristics = new(Characteristics) ) @@ -25,8 +30,9 @@ var ( type ( // Header represents the CAR v2 header/pragma. Header struct { + io.WriterTo // 128-bit characteristics of this CAR v2 file, such as order, deduplication, etc. Reserved for future use. - Characteristics Characteristics + Characteristics *Characteristics // The offset from the beginning of the file at which the dump of CAR v1 starts. CarV1Offset uint64 // The size of CAR v1 encapsulated in this CAR v2 as bytes. @@ -36,17 +42,100 @@ type ( } // Characteristics is a bitfield placeholder for capturing the characteristics of a CAR v2 such as order and determinism. Characteristics struct { + io.WriterTo Hi uint64 Lo uint64 } ) -// Size gets the size of Header in number of bytes. -func (h *Header) Size() int { - return HeaderBytesSize +// WriteTo writes this characteristics to the given writer. +func (c *Characteristics) WriteTo(w io.Writer) (n int64, err error) { + wn, err := writeUint64To(w, c.Hi) + if err != nil { + return + } + n += wn + wn, err = writeUint64To(w, c.Lo) + if err != nil { + return + } + n += wn + return } // Size gets the size of Characteristics in number of bytes. -func (c *Characteristics) Size() int { +func (c *Characteristics) Size() uint64 { return CharacteristicsBytesSize } + +// NewHeader instantiates a new CAR v2 header, given the byte length of a CAR v1. +func NewHeader(carV1Size uint64) *Header { + header := &Header{ + Characteristics: EmptyCharacteristics, + CarV1Size: carV1Size, + } + header.CarV1Offset = PrefixBytesSize + HeaderBytesSize + header.IndexOffset = header.CarV1Offset + carV1Size + return header +} + +// Size gets the size of Header in number of bytes. +func (h *Header) Size() uint64 { + return HeaderBytesSize +} + +// WithIndexPadding sets the index offset from the beginning of the file for this header and returns the +// header for convenient chained calls. +// The index offset is calculated as the sum of PrefixBytesLen, HeaderBytesLen, +// Header#CarV1Len, and the given padding. +func (h *Header) WithIndexPadding(padding uint64) *Header { + h.IndexOffset = h.IndexOffset + padding + return h +} + +// WithCarV1Padding sets the CAR v1 dump offset from the beginning of the file for this header and returns the +// header for convenient chained calls. +// The CAR v1 offset is calculated as the sum of PrefixBytesLen, HeaderBytesLen and the given padding. +// The call to this function also shifts the Header#IndexOffset forward by the given padding. +func (h *Header) WithCarV1Padding(padding uint64) *Header { + h.CarV1Offset = h.CarV1Offset + padding + h.IndexOffset = h.IndexOffset + padding + return h +} + +// WriteTo serializes this header as bytes and writes them using the given io.Writer. +func (h *Header) WriteTo(w io.Writer) (n int64, err error) { + chars := h.Characteristics + if chars == nil { + chars = EmptyCharacteristics + } + wn, err := chars.WriteTo(w) + if err != nil { + return + } + n += wn + wn, err = writeUint64To(w, h.CarV1Offset) + if err != nil { + return + } + n += wn + wn, err = writeUint64To(w, h.CarV1Size) + if err != nil { + return + } + n += wn + wn, err = writeUint64To(w, h.IndexOffset) + if err != nil { + return + } + n += wn + return +} + +func writeUint64To(w io.Writer, v uint64) (n int64, err error) { + err = binary.Write(w, binary.LittleEndian, v) + if err == nil { + n = 8 + } + return +} diff --git a/ipld/car/v2/car_test.go b/ipld/car/v2/car_test.go index ab5326850..b40d1fc89 100644 --- a/ipld/car/v2/car_test.go +++ b/ipld/car/v2/car_test.go @@ -1,6 +1,7 @@ package car_test import ( + "bytes" cbor "github.com/ipfs/go-ipld-cbor" car_v1 "github.com/ipld/go-car" car_v2 "github.com/ipld/go-car/v2" @@ -56,7 +57,7 @@ func TestEmptyCharacteristics(t *testing.T) { }{ { "is of size 16 bytes", - 16, + car_v2.CharacteristicsBytesSize, car_v2.EmptyCharacteristics.Size(), }, { @@ -74,5 +75,123 @@ func TestEmptyCharacteristics(t *testing.T) { } func TestHeader_SizeIs40Bytes(t *testing.T) { - assert.Equal(t, 40, new(car_v2.Header).Size()) + assert.Equal(t, uint64(40), new(car_v2.Header).Size()) +} + +func TestHeader_Marshal(t *testing.T) { + tests := []struct { + name string + target car_v2.Header + wantMarshal []byte + wantErr bool + }{ + { + "header with nil characteristics is marshalled as empty characteristics", + car_v2.Header{ + Characteristics: nil, + }, + []byte{ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + false, + }, + { + "header with empty characteristics is marshalled as expected", + car_v2.Header{ + Characteristics: car_v2.EmptyCharacteristics, + }, + []byte{ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + false, + }, + { + "non-empty header is marshalled as expected", + car_v2.Header{ + Characteristics: &car_v2.Characteristics{ + Hi: 1001, Lo: 1002, + }, + CarV1Offset: 99, + CarV1Size: 100, + IndexOffset: 101, + }, + []byte{ + 0xe9, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xea, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x63, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x64, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x65, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := &bytes.Buffer{} + written, err := tt.target.WriteTo(buf) + if (err != nil) != tt.wantErr { + t.Errorf("Marshal() error = %v, wantErr %v", err, tt.wantErr) + return + } + gotMarshal := buf.Bytes() + assert.Equal(t, tt.wantMarshal, gotMarshal, "Header.WriteTo() gotMarshal = %v, wantMarshal %v", gotMarshal, tt.wantMarshal) + assert.EqualValues(t, car_v2.HeaderBytesSize, uint64(len(gotMarshal)), "WriteTo() CAR v2 header length must always be %v bytes long", car_v2.HeaderBytesSize) + assert.EqualValues(t, car_v2.HeaderBytesSize, uint64(written), "WriteTo() CAR v2 header byte count must always be %v bytes long", car_v2.HeaderBytesSize) + }) + } +} + +func TestHeader_WithPadding(t *testing.T) { + tests := []struct { + name string + subject *car_v2.Header + wantCarV1Offset uint64 + wantIndexOffset uint64 + }{ + { + "when no padding, offsets are sum of sizes", + car_v2.NewHeader(123), + car_v2.PrefixBytesSize + car_v2.HeaderBytesSize, + car_v2.PrefixBytesSize + car_v2.HeaderBytesSize + 123, + }, + { + "when only padding car v1, both offsets shift", + car_v2.NewHeader(123).WithCarV1Padding(3), + car_v2.PrefixBytesSize + car_v2.HeaderBytesSize + 3, + car_v2.PrefixBytesSize + car_v2.HeaderBytesSize + 3 + 123, + }, + { + "when padding both car v1 and index, both offsets shift with additional index shift", + car_v2.NewHeader(123).WithCarV1Padding(3).WithIndexPadding(7), + car_v2.PrefixBytesSize + car_v2.HeaderBytesSize + 3, + car_v2.PrefixBytesSize + car_v2.HeaderBytesSize + 3 + 123 + 7, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.EqualValues(t, tt.wantCarV1Offset, tt.subject.CarV1Offset) + assert.EqualValues(t, tt.wantIndexOffset, tt.subject.IndexOffset) + }) + } +} + +func TestNewHeaderHasExpectedValues(t *testing.T) { + wantCarV1Len := uint64(1413) + want := &car_v2.Header{ + Characteristics: car_v2.EmptyCharacteristics, + CarV1Offset: car_v2.PrefixBytesSize + car_v2.HeaderBytesSize, + CarV1Size: wantCarV1Len, + IndexOffset: car_v2.PrefixBytesSize + car_v2.HeaderBytesSize + wantCarV1Len, + } + got := car_v2.NewHeader(wantCarV1Len) + assert.Equal(t, want, got, "NewHeader got = %v, want = %v", got, want) } From 10c9d33c5c4a99c0b75009d1e2199049d70b26cb Mon Sep 17 00:00:00 2001 From: Dr Ian Preston Date: Thu, 17 Jun 2021 14:52:14 +0100 Subject: [PATCH 3413/3817] Use bloom filter in GetSize This commit was moved from ipfs/go-ipfs-blockstore@2e485bebeebac8bdc0d000c5115265368e25d710 --- blockstore/bloom_cache.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index da302c97d..70fe5106b 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -155,6 +155,10 @@ func (b *bloomcache) Has(k cid.Cid) (bool, error) { } func (b *bloomcache) GetSize(k cid.Cid) (int, error) { + if has, ok := b.hasCached(k); ok && !has { + return -1, ErrNotFound + } + return b.blockstore.GetSize(k) } From daa11ac0a8b4241e83b3bdd90e38226339694a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Thu, 1 Jul 2021 18:18:20 +0200 Subject: [PATCH 3414/3817] Define ErrNotPinned alongside the Pinner interface Allows for alternative implementation with the same error being part of the interface contract. This commit was moved from ipfs/go-ipfs-pinner@3565d71fb90b70426cd6c001fb20f5402661d945 --- pinning/pinner/dspinner/pin.go | 5 +---- pinning/pinner/dspinner/pin_test.go | 4 ++-- pinning/pinner/pin.go | 4 ++++ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pinning/pinner/dspinner/pin.go b/pinning/pinner/dspinner/pin.go index 4adf95a6e..b793cf2ea 100644 --- a/pinning/pinner/dspinner/pin.go +++ b/pinning/pinner/dspinner/pin.go @@ -31,9 +31,6 @@ const ( ) var ( - // ErrNotPinned is returned when trying to unpin items that are not pinned. - ErrNotPinned = errors.New("not pinned or pinned indirectly") - log logging.StandardLogger = logging.Logger("pin") linkDirect, linkRecursive string @@ -346,7 +343,7 @@ func (p *pinner) Unpin(ctx context.Context, c cid.Cid, recursive bool) error { return err } if !has { - return ErrNotPinned + return ipfspinner.ErrNotPinned } } diff --git a/pinning/pinner/dspinner/pin_test.go b/pinning/pinner/dspinner/pin_test.go index 500d3e3e7..d8c4e9549 100644 --- a/pinning/pinner/dspinner/pin_test.go +++ b/pinning/pinner/dspinner/pin_test.go @@ -260,8 +260,8 @@ func TestPinnerBasic(t *testing.T) { } err = p.Unpin(ctx, dk, true) - if err != ErrNotPinned { - t.Fatal("expected error:", ErrNotPinned) + if err != ipfspin.ErrNotPinned { + t.Fatal("expected error:", ipfspin.ErrNotPinned) } err = p.Flush(ctx) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 2bae75841..bbabac5a0 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -75,6 +75,9 @@ func StringToMode(s string) (Mode, bool) { return mode, ok } +// ErrNotPinned is returned when trying to unpin items that are not pinned. +var ErrNotPinned = fmt.Errorf("not pinned or pinned indirectly") + // A Pinner provides the necessary methods to keep track of Nodes which are // to be kept locally, according to a pin mode. In practice, a Pinner is in // in charge of keeping the list of items from the local storage that should @@ -93,6 +96,7 @@ type Pinner interface { // Unpin the given cid. If recursive is true, removes either a recursive or // a direct pin. If recursive is false, only removes a direct pin. + // If the pin doesn't exist, return ErrNotPinned Unpin(ctx context.Context, cid cid.Cid, recursive bool) error // Update updates a recursive pin from one cid to another From 4c474f60d8b161a0e1b598c07a1ce50b8eee6b51 Mon Sep 17 00:00:00 2001 From: anorth <445306+anorth@users.noreply.github.com> Date: Fri, 16 Jul 2021 11:20:26 +1000 Subject: [PATCH 3415/3817] Fix lint errors This commit was moved from ipfs/go-mfs@352cb78f15d9792154f6823e1728cb8e28935631 --- mfs/file.go | 2 +- mfs/mfs_test.go | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/mfs/file.go b/mfs/file.go index bbe508ac3..2a2789a3e 100644 --- a/mfs/file.go +++ b/mfs/file.go @@ -169,7 +169,7 @@ func (fi *File) Sync() error { // just being able to take the writelock means the descriptor is synced // TODO: Why? fi.desclock.Lock() - fi.desclock.Unlock() + defer fi.desclock.Unlock() // Defer works around "empty critical section (SA2001)" return nil } diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 3c08fd9a6..1ea90ef33 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -1162,6 +1162,7 @@ func TestConcurrentWrites(t *testing.T) { var wg sync.WaitGroup nloops := 100 + errs := make(chan error, 1000) for i := 0; i < 10; i++ { wg.Add(1) go func() { @@ -1171,16 +1172,19 @@ func TestConcurrentWrites(t *testing.T) { err := writeFile(rt, "a/b/c/afile", func(buf []byte) []byte { if len(buf) == 0 { if lastSeen > 0 { - t.Fatalf("file corrupted, last seen: %d", lastSeen) + errs <- fmt.Errorf("file corrupted, last seen: %d", lastSeen) + return buf } buf = make([]byte, 8) } else if len(buf) != 8 { - t.Fatal("buf not the right size") + errs <- fmt.Errorf("buf not the right size") + return buf } num := binary.LittleEndian.Uint64(buf) if num < lastSeen { - t.Fatalf("count decreased: was %d, is %d", lastSeen, num) + errs <- fmt.Errorf("count decreased: was %d, is %d", lastSeen, num) + return buf } else { t.Logf("count correct: was %d, is %d", lastSeen, num) } @@ -1190,13 +1194,17 @@ func TestConcurrentWrites(t *testing.T) { return buf }) if err != nil { - t.Error("writefile failed: ", err) + errs <- fmt.Errorf("writefile failed: %v", err) return } } }() } wg.Wait() + close(errs) + for e := range errs { + t.Fatal(e) + } buf := make([]byte, 8) if err := readFile(rt, "a/b/c/afile", 0, buf); err != nil { t.Fatal(err) @@ -1353,10 +1361,10 @@ func TestTruncateAndWrite(t *testing.T) { } fd, err := fi.Open(Flags{Read: true, Write: true, Sync: true}) - defer fd.Close() if err != nil { t.Fatal(err) } + defer fd.Close() for i := 0; i < 200; i++ { err = fd.Truncate(0) if err != nil { From af87a902703011734e1928581f93951a7357fece Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 10 Jun 2021 16:59:26 +0100 Subject: [PATCH 3416/3817] Implement CAR v2 writer that uses `format.NodeGetter` Implement a CAR v2 writer, that produces binary structure corresponding to the specification of car V2, consisting of: 1. Version prefix 2. CAR v2 header 3. Dump of Car v1 4. Carbs index The implementation also facilities optional padding before dump of CAR v1 and Carbs index to provide scope for future optimisations. The padding is defined as a dedicated type that writes zero-valued bytes for a given padding size. The CAR v2 header, then captures the offsets from the beginning of the file for both the CAR v1 dump and Carbs index. This allows the user to quickly skip to the part of the CAR v2 they need as well as offset alignment, if necessary, by altering the value of padding. Note, this is an intermediate implementation, and does not correctly count the number of bytes written by the writer, pending the transfer/refactor of Carbs implementation in this repo. In the meantime, This implementation depends on Carbs as a go module. The implementation of extensive writer tests is postponed until Carbs is transferred and the written byte-count can be returned from the index marshaller. Address review comments - Avoid representing `Characteristics` as a pointer for easier casting of memory regions. - Remove anonymous fields in structs as a human readable way to document what interfaces a struct implements. - Simplify Header writing and written byte count calculation. The change reduces the number of lines by assuming that `writeUint64To` will always write 8 bytes. - Avoid `_` in package names. Use `carv1` and `carv2` to name corresponding packages. - Rename `Marshal` in tests to reflect `WriteTo` related tests. The method was renamed in the implementation but tests were not renamed. - Write padding in bulk to reduce redundant memory allocation. Write padding in bulks of `1024` bytes to reduce large memory allocation when padding itself is large. - Avoid using # in docs. Use go syntax instead, i.e. `.`. - Remove redundant type in constants, since it is implicitly converted. - Simplify over-refactored prefix write Since it is called only once. - Avoid `Header` pointer, since its size is small and this would reduce unnecessary allocations. - Use buffer in writer to store encoded Car V1. This is to avoid reallocation of bytes buffer. - Add TODO re optimisation of index generation. The current implementation reads the entire CAR v1 into memory in order to index it, because `carbs` API requires `io.Reader`. Once `a`carbs` is incorporated into this repo consider refactoring the API to make this a streaming operation that avoids copying all the bytes since CAR v1 can be large. - Remove a dedicated var for empty characteristics, since, we assume the default to be all zero, and it is easy enough to construct a zero valued Characteristics. - Unexport PrefixBytesSize, because we can, and if it is public it might have to stay public forever. So, unexport until we know we need it exported. - Remove `.Size()` on CAR v2 structs. Because we have the constant for them and don't want to expose them twice. - Use CamelCase for sub-test naming. This is to establish CamelCase for sub-test naming as the convention for this repo since it keeps the test names consistent in code and in the output ot `go test`. - Unexport padding as a type since it is only used internally This commit was moved from ipld/go-car@875a6548b3047e46f493f6edd29b2f37d4fb5b4e --- ipld/car/v2/car.go | 83 ++++++++-------------- ipld/car/v2/car_test.go | 136 ++++++++++++------------------------ ipld/car/v2/writer.go | 138 +++++++++++++++++++++++++++++++++++++ ipld/car/v2/writer_test.go | 96 ++++++++++++++++++++++++++ 4 files changed, 307 insertions(+), 146 deletions(-) create mode 100644 ipld/car/v2/writer.go create mode 100644 ipld/car/v2/writer_test.go diff --git a/ipld/car/v2/car.go b/ipld/car/v2/car.go index 428cb591b..e3f7c7b79 100644 --- a/ipld/car/v2/car.go +++ b/ipld/car/v2/car.go @@ -7,9 +7,10 @@ import ( const ( // HeaderBytesSize is the fixed size of CAR v2 header in number of bytes. - HeaderBytesSize uint64 = 40 + HeaderBytesSize = 40 // CharacteristicsBytesSize is the fixed size of Characteristics bitfield within CAR v2 header in number of bytes. - CharacteristicsBytesSize uint64 = 16 + CharacteristicsBytesSize = 16 + uint64BytesSize = 8 ) var ( @@ -22,17 +23,14 @@ var ( 0x02, // uint(2) } // The size of the CAR v2 prefix in 11 bytes, (i.e. 11). - PrefixBytesSize = uint64(len(PrefixBytes)) - // Reserved 128 bits space to capture future characteristics of CAR v2 such as order, duplication, etc. - EmptyCharacteristics = new(Characteristics) + prefixBytesSize = uint64(len(PrefixBytes)) ) type ( // Header represents the CAR v2 header/pragma. Header struct { - io.WriterTo // 128-bit characteristics of this CAR v2 file, such as order, deduplication, etc. Reserved for future use. - Characteristics *Characteristics + Characteristics Characteristics // The offset from the beginning of the file at which the dump of CAR v1 starts. CarV1Offset uint64 // The size of CAR v1 encapsulated in this CAR v2 as bytes. @@ -42,53 +40,39 @@ type ( } // Characteristics is a bitfield placeholder for capturing the characteristics of a CAR v2 such as order and determinism. Characteristics struct { - io.WriterTo Hi uint64 Lo uint64 } ) // WriteTo writes this characteristics to the given writer. -func (c *Characteristics) WriteTo(w io.Writer) (n int64, err error) { - wn, err := writeUint64To(w, c.Hi) - if err != nil { +func (c Characteristics) WriteTo(w io.Writer) (n int64, err error) { + if err = writeUint64To(w, c.Hi); err != nil { return } - n += wn - wn, err = writeUint64To(w, c.Lo) - if err != nil { + n += uint64BytesSize + if err = writeUint64To(w, c.Lo); err != nil { return } - n += wn + n += uint64BytesSize return } -// Size gets the size of Characteristics in number of bytes. -func (c *Characteristics) Size() uint64 { - return CharacteristicsBytesSize -} - // NewHeader instantiates a new CAR v2 header, given the byte length of a CAR v1. -func NewHeader(carV1Size uint64) *Header { - header := &Header{ - Characteristics: EmptyCharacteristics, - CarV1Size: carV1Size, +func NewHeader(carV1Size uint64) Header { + header := Header{ + CarV1Size: carV1Size, } - header.CarV1Offset = PrefixBytesSize + HeaderBytesSize + header.CarV1Offset = prefixBytesSize + HeaderBytesSize header.IndexOffset = header.CarV1Offset + carV1Size return header } -// Size gets the size of Header in number of bytes. -func (h *Header) Size() uint64 { - return HeaderBytesSize -} - // WithIndexPadding sets the index offset from the beginning of the file for this header and returns the // header for convenient chained calls. // The index offset is calculated as the sum of PrefixBytesLen, HeaderBytesLen, -// Header#CarV1Len, and the given padding. -func (h *Header) WithIndexPadding(padding uint64) *Header { +// Header.CarV1Len, and the given padding. +func (h Header) WithIndexPadding(padding uint64) Header { h.IndexOffset = h.IndexOffset + padding return h } @@ -96,46 +80,35 @@ func (h *Header) WithIndexPadding(padding uint64) *Header { // WithCarV1Padding sets the CAR v1 dump offset from the beginning of the file for this header and returns the // header for convenient chained calls. // The CAR v1 offset is calculated as the sum of PrefixBytesLen, HeaderBytesLen and the given padding. -// The call to this function also shifts the Header#IndexOffset forward by the given padding. -func (h *Header) WithCarV1Padding(padding uint64) *Header { +// The call to this function also shifts the Header.IndexOffset forward by the given padding. +func (h Header) WithCarV1Padding(padding uint64) Header { h.CarV1Offset = h.CarV1Offset + padding h.IndexOffset = h.IndexOffset + padding return h } // WriteTo serializes this header as bytes and writes them using the given io.Writer. -func (h *Header) WriteTo(w io.Writer) (n int64, err error) { - chars := h.Characteristics - if chars == nil { - chars = EmptyCharacteristics - } - wn, err := chars.WriteTo(w) +func (h Header) WriteTo(w io.Writer) (n int64, err error) { + wn, err := h.Characteristics.WriteTo(w) if err != nil { return } n += wn - wn, err = writeUint64To(w, h.CarV1Offset) - if err != nil { + if err = writeUint64To(w, h.CarV1Offset); err != nil { return } - n += wn - wn, err = writeUint64To(w, h.CarV1Size) - if err != nil { + n += uint64BytesSize + if err = writeUint64To(w, h.CarV1Size); err != nil { return } - n += wn - wn, err = writeUint64To(w, h.IndexOffset) - if err != nil { + n += uint64BytesSize + if err = writeUint64To(w, h.IndexOffset); err != nil { return } - n += wn + n += uint64BytesSize return } -func writeUint64To(w io.Writer, v uint64) (n int64, err error) { - err = binary.Write(w, binary.LittleEndian, v) - if err == nil { - n = 8 - } - return +func writeUint64To(w io.Writer, v uint64) error { + return binary.Write(w, binary.LittleEndian, v) } diff --git a/ipld/car/v2/car_test.go b/ipld/car/v2/car_test.go index b40d1fc89..6e1c87771 100644 --- a/ipld/car/v2/car_test.go +++ b/ipld/car/v2/car_test.go @@ -3,12 +3,14 @@ package car_test import ( "bytes" cbor "github.com/ipfs/go-ipld-cbor" - car_v1 "github.com/ipld/go-car" - car_v2 "github.com/ipld/go-car/v2" + carv1 "github.com/ipld/go-car" + carv2 "github.com/ipld/go-car/v2" "github.com/stretchr/testify/assert" "testing" ) +var prefixBytesSize = uint64(11) + func TestCarV2PrefixLength(t *testing.T) { tests := []struct { name string @@ -16,18 +18,13 @@ func TestCarV2PrefixLength(t *testing.T) { got interface{} }{ { - "cached size should be 11 bytes", + "ActualSizeShouldBe11", 11, - car_v2.PrefixBytesSize, + len(carv2.PrefixBytes), }, { - "actual size should be 11 bytes", - 11, - len(car_v2.PrefixBytes), - }, - { - "should start with varint(10)", - car_v2.PrefixBytes[0], + "ShouldStartWithVarint(10)", + carv2.PrefixBytes[0], 10, }, } @@ -40,69 +37,26 @@ func TestCarV2PrefixLength(t *testing.T) { } func TestCarV2PrefixIsValidCarV1Header(t *testing.T) { - var v1h car_v1.CarHeader - err := cbor.DecodeInto(car_v2.PrefixBytes[1:], &v1h) + var v1h carv1.CarHeader + err := cbor.DecodeInto(carv2.PrefixBytes[1:], &v1h) assert.NoError(t, err, "cannot decode prefix as CBOR with CAR v1 header structure") - assert.Equal(t, car_v1.CarHeader{ + assert.Equal(t, carv1.CarHeader{ Roots: nil, Version: 2, }, v1h, "CAR v2 prefix must be a valid CAR v1 header") } -func TestEmptyCharacteristics(t *testing.T) { - tests := []struct { - name string - want interface{} - got interface{} - }{ - { - "is of size 16 bytes", - car_v2.CharacteristicsBytesSize, - car_v2.EmptyCharacteristics.Size(), - }, - { - "is a whole lot of nothin'", - &car_v2.Characteristics{}, - car_v2.EmptyCharacteristics, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - assert.EqualValues(t, tt.want, tt.got, "EmptyCharacteristics got = %v, want %v", tt.got, tt.want) - }) - } -} - -func TestHeader_SizeIs40Bytes(t *testing.T) { - assert.Equal(t, uint64(40), new(car_v2.Header).Size()) -} - -func TestHeader_Marshal(t *testing.T) { +func TestHeader_WriteTo(t *testing.T) { tests := []struct { - name string - target car_v2.Header - wantMarshal []byte - wantErr bool + name string + target carv2.Header + wantWrite []byte + wantErr bool }{ { - "header with nil characteristics is marshalled as empty characteristics", - car_v2.Header{ - Characteristics: nil, - }, - []byte{ - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }, - false, - }, - { - "header with empty characteristics is marshalled as expected", - car_v2.Header{ - Characteristics: car_v2.EmptyCharacteristics, + "HeaderWithEmptyCharacteristicsIsWrittenAsExpected", + carv2.Header{ + Characteristics: carv2.Characteristics{}, }, []byte{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @@ -114,9 +68,9 @@ func TestHeader_Marshal(t *testing.T) { false, }, { - "non-empty header is marshalled as expected", - car_v2.Header{ - Characteristics: &car_v2.Characteristics{ + "NonEmptyHeaderIsWrittenAsExpected", + carv2.Header{ + Characteristics: carv2.Characteristics{ Hi: 1001, Lo: 1002, }, CarV1Offset: 99, @@ -138,13 +92,13 @@ func TestHeader_Marshal(t *testing.T) { buf := &bytes.Buffer{} written, err := tt.target.WriteTo(buf) if (err != nil) != tt.wantErr { - t.Errorf("Marshal() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("WriteTo() error = %v, wantErr %v", err, tt.wantErr) return } - gotMarshal := buf.Bytes() - assert.Equal(t, tt.wantMarshal, gotMarshal, "Header.WriteTo() gotMarshal = %v, wantMarshal %v", gotMarshal, tt.wantMarshal) - assert.EqualValues(t, car_v2.HeaderBytesSize, uint64(len(gotMarshal)), "WriteTo() CAR v2 header length must always be %v bytes long", car_v2.HeaderBytesSize) - assert.EqualValues(t, car_v2.HeaderBytesSize, uint64(written), "WriteTo() CAR v2 header byte count must always be %v bytes long", car_v2.HeaderBytesSize) + gotWrite := buf.Bytes() + assert.Equal(t, tt.wantWrite, gotWrite, "Header.WriteTo() gotWrite = %v, wantWrite %v", gotWrite, tt.wantWrite) + assert.EqualValues(t, carv2.HeaderBytesSize, uint64(len(gotWrite)), "WriteTo() CAR v2 header length must always be %v bytes long", carv2.HeaderBytesSize) + assert.EqualValues(t, carv2.HeaderBytesSize, uint64(written), "WriteTo() CAR v2 header byte count must always be %v bytes long", carv2.HeaderBytesSize) }) } } @@ -152,27 +106,27 @@ func TestHeader_Marshal(t *testing.T) { func TestHeader_WithPadding(t *testing.T) { tests := []struct { name string - subject *car_v2.Header + subject carv2.Header wantCarV1Offset uint64 wantIndexOffset uint64 }{ { - "when no padding, offsets are sum of sizes", - car_v2.NewHeader(123), - car_v2.PrefixBytesSize + car_v2.HeaderBytesSize, - car_v2.PrefixBytesSize + car_v2.HeaderBytesSize + 123, + "WhenNoPaddingOffsetsAreSumOfSizes", + carv2.NewHeader(123), + prefixBytesSize + carv2.HeaderBytesSize, + prefixBytesSize + carv2.HeaderBytesSize + 123, }, { - "when only padding car v1, both offsets shift", - car_v2.NewHeader(123).WithCarV1Padding(3), - car_v2.PrefixBytesSize + car_v2.HeaderBytesSize + 3, - car_v2.PrefixBytesSize + car_v2.HeaderBytesSize + 3 + 123, + "WhenOnlyPaddingCarV1BothOffsetsShift", + carv2.NewHeader(123).WithCarV1Padding(3), + prefixBytesSize + carv2.HeaderBytesSize + 3, + prefixBytesSize + carv2.HeaderBytesSize + 3 + 123, }, { - "when padding both car v1 and index, both offsets shift with additional index shift", - car_v2.NewHeader(123).WithCarV1Padding(3).WithIndexPadding(7), - car_v2.PrefixBytesSize + car_v2.HeaderBytesSize + 3, - car_v2.PrefixBytesSize + car_v2.HeaderBytesSize + 3 + 123 + 7, + "WhenPaddingBothCarV1AndIndexBothOffsetsShiftWithAdditionalIndexShift", + carv2.NewHeader(123).WithCarV1Padding(3).WithIndexPadding(7), + prefixBytesSize + carv2.HeaderBytesSize + 3, + prefixBytesSize + carv2.HeaderBytesSize + 3 + 123 + 7, }, } @@ -186,12 +140,12 @@ func TestHeader_WithPadding(t *testing.T) { func TestNewHeaderHasExpectedValues(t *testing.T) { wantCarV1Len := uint64(1413) - want := &car_v2.Header{ - Characteristics: car_v2.EmptyCharacteristics, - CarV1Offset: car_v2.PrefixBytesSize + car_v2.HeaderBytesSize, + want := carv2.Header{ + Characteristics: carv2.Characteristics{}, + CarV1Offset: prefixBytesSize + carv2.HeaderBytesSize, CarV1Size: wantCarV1Len, - IndexOffset: car_v2.PrefixBytesSize + car_v2.HeaderBytesSize + wantCarV1Len, + IndexOffset: prefixBytesSize + carv2.HeaderBytesSize + wantCarV1Len, } - got := car_v2.NewHeader(wantCarV1Len) + got := carv2.NewHeader(wantCarV1Len) assert.Equal(t, want, got, "NewHeader got = %v, want = %v", got, want) } diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go new file mode 100644 index 000000000..3e90817bc --- /dev/null +++ b/ipld/car/v2/writer.go @@ -0,0 +1,138 @@ +package car + +import ( + "bytes" + "context" + "github.com/ipfs/go-cid" + format "github.com/ipfs/go-ipld-format" + carv1 "github.com/ipld/go-car" + "github.com/willscott/carbs" + "io" +) + +const bulkPaddingBytesSize = 1024 + +var bulkPadding = make([]byte, bulkPaddingBytesSize) + +type ( + // padding represents the number of padding bytes. + padding uint64 + // Writer writes CAR v2 into a give io.Writer. + Writer struct { + Walk carv1.WalkFunc + IndexCodec carbs.IndexCodec + NodeGetter format.NodeGetter + CarV1Padding uint64 + IndexPadding uint64 + + ctx context.Context + roots []cid.Cid + encodedCarV1 *bytes.Buffer + } +) + +// WriteTo writes this padding to the given writer as default value bytes. +func (p padding) WriteTo(w io.Writer) (n int64, err error) { + var reminder int64 + if p > bulkPaddingBytesSize { + reminder = int64(p % bulkPaddingBytesSize) + iter := int(p / bulkPaddingBytesSize) + for i := 0; i < iter; i++ { + if _, err = w.Write(bulkPadding); err != nil { + return + } + n += bulkPaddingBytesSize + } + } else { + reminder = int64(p) + } + + paddingBytes := make([]byte, reminder) + _, err = w.Write(paddingBytes) + n += reminder + return +} + +// NewWriter instantiates a new CAR v2 writer. +// The writer instantiated uses `carbs.IndexSorted` as the index codec, +// and `carv1.DefaultWalkFunc` as the default walk function. +func NewWriter(ctx context.Context, ng format.NodeGetter, roots []cid.Cid) *Writer { + return &Writer{ + Walk: carv1.DefaultWalkFunc, + IndexCodec: carbs.IndexSorted, + NodeGetter: ng, + ctx: ctx, + roots: roots, + encodedCarV1: new(bytes.Buffer), + } +} + +// WriteTo writes the given root CIDs according to CAR v2 specification, traversing the DAG using the +// Writer.Walk function. +func (w *Writer) WriteTo(writer io.Writer) (n int64, err error) { + _, err = writer.Write(PrefixBytes) + if err != nil { + return + } + n += int64(prefixBytesSize) + // We read the entire car into memory because carbs.GenerateIndex takes a reader. + // Future PRs will make this more efficient by exposing necessary interfaces in carbs so that + // this can be done in an streaming manner. + if err = carv1.WriteCarWithWalker(w.ctx, w.NodeGetter, w.roots, w.encodedCarV1, w.Walk); err != nil { + return + } + carV1Len := w.encodedCarV1.Len() + + wn, err := w.writeHeader(writer, carV1Len) + if err != nil { + return + } + n += wn + + wn, err = padding(w.CarV1Padding).WriteTo(writer) + if err != nil { + return + } + n += wn + + carV1Bytes := w.encodedCarV1.Bytes() + wwn, err := writer.Write(carV1Bytes) + if err != nil { + return + } + n += int64(wwn) + + wn, err = padding(w.IndexPadding).WriteTo(writer) + if err != nil { + return + } + n += wn + + wn, err = w.writeIndex(writer, carV1Bytes) + if err == nil { + n += wn + } + return +} + +func (w *Writer) writeHeader(writer io.Writer, carV1Len int) (int64, error) { + header := NewHeader(uint64(carV1Len)). + WithCarV1Padding(w.CarV1Padding). + WithIndexPadding(w.IndexPadding) + return header.WriteTo(writer) +} + +func (w *Writer) writeIndex(writer io.Writer, carV1 []byte) (n int64, err error) { + // TODO avoid recopying the bytes by refacting carbs once it is integrated here. + // Right now we copy the bytes since carbs takes a writer. + // Consider refactoring carbs to make this process more efficient. + // We should avoid reading the entire car into memory since it can be large. + reader := bytes.NewReader(carV1) + index, err := carbs.GenerateIndex(reader, int64(len(carV1)), carbs.IndexSorted, true) + if err != nil { + return + } + err = index.Marshal(writer) + // FIXME refactor carbs to expose the number of bytes written. + return +} diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go new file mode 100644 index 000000000..caf7e6668 --- /dev/null +++ b/ipld/car/v2/writer_test.go @@ -0,0 +1,96 @@ +package car + +import ( + "bytes" + "context" + "github.com/ipfs/go-cid" + format "github.com/ipfs/go-ipld-format" + dag "github.com/ipfs/go-merkledag" + dstest "github.com/ipfs/go-merkledag/test" + "github.com/stretchr/testify/assert" + "github.com/willscott/carbs" + "testing" +) + +func TestPadding_WriteTo(t *testing.T) { + tests := []struct { + name string + padding padding + wantBytes []byte + wantN int64 + wantErr bool + }{ + { + "ZeroPaddingIsNoBytes", + padding(0), + nil, + 0, + false, + }, + { + "NonZeroPaddingIsCorrespondingZeroValueBytes", + padding(3), + []byte{0x00, 0x00, 0x00}, + 3, + false, + }, + { + "PaddingLargerThanTheBulkPaddingSizeIsCorrespondingZeroValueBytes", + padding(1025), + make([]byte, 1025), + 1025, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &bytes.Buffer{} + gotN, gotErr := tt.padding.WriteTo(w) + if tt.wantErr { + assert.Error(t, gotErr) + return + } + gotBytes := w.Bytes() + assert.Equal(t, tt.wantN, gotN) + assert.Equal(t, tt.wantBytes, gotBytes) + }) + } +} + +func TestNewWriter(t *testing.T) { + dagService := dstest.Mock() + wantRoots := generateRootCid(t, dagService) + writer := NewWriter(context.Background(), dagService, wantRoots) + assert.Equal(t, carbs.IndexSorted, writer.IndexCodec) + assert.Equal(t, wantRoots, writer.roots) +} + +func generateRootCid(t *testing.T, adder format.NodeAdder) []cid.Cid { + // TODO convert this into a utility testing lib that takes an rng and generates a random DAG with some threshold for depth/breadth. + this := dag.NewRawNode([]byte("fish")) + that := dag.NewRawNode([]byte("lobster")) + other := dag.NewRawNode([]byte("🌊")) + + one := &dag.ProtoNode{} + assertAddNodeLink(t, one, this, "fishmonger") + + another := &dag.ProtoNode{} + assertAddNodeLink(t, another, one, "barreleye") + assertAddNodeLink(t, another, that, "ðŸ¡") + + andAnother := &dag.ProtoNode{} + assertAddNodeLink(t, andAnother, another, "ðŸ¤") + + assertAddNodes(t, adder, this, that, other, one, another, andAnother) + return []cid.Cid{andAnother.Cid()} +} + +func assertAddNodeLink(t *testing.T, pn *dag.ProtoNode, fn format.Node, name string) { + assert.NoError(t, pn.AddNodeLink(name, fn)) +} + +func assertAddNodes(t *testing.T, adder format.NodeAdder, nds ...format.Node) { + for _, nd := range nds { + assert.NoError(t, adder.Add(context.Background(), nd)) + } +} From 8b06ad1fab876d64460d10ea6117d100ac3f5ef3 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 15 Jun 2021 09:55:31 +0100 Subject: [PATCH 3417/3817] Copy `carbs` indexing mechanism int `go-car` repo The implementation comes from here: - https://github.com/willscott/carbs The rationale for copying the code here is to be able to taylor the indexing API to the needs of CAR v2 in one place. This commit was moved from ipld/go-car@d9c8b766b77fa8cfde55c99c738dcdc02cda0b93 --- ipld/car/v2/carbs/carbs.go | 301 ++++++++++++++++++++++++++++++ ipld/car/v2/carbs/carbs_test.go | 108 +++++++++++ ipld/car/v2/carbs/index.go | 89 +++++++++ ipld/car/v2/carbs/indexgobhash.go | 44 +++++ ipld/car/v2/carbs/indexhashed.go | 43 +++++ ipld/car/v2/carbs/indexsorted.go | 197 +++++++++++++++++++ ipld/car/v2/carbs/reader.go | 20 ++ ipld/car/v2/carbs/test.car | Bin 0 -> 479907 bytes ipld/car/v2/carbs/util/hydrate.go | 51 +++++ ipld/car/v2/writer.go | 4 +- ipld/car/v2/writer_test.go | 2 +- 11 files changed, 856 insertions(+), 3 deletions(-) create mode 100644 ipld/car/v2/carbs/carbs.go create mode 100644 ipld/car/v2/carbs/carbs_test.go create mode 100644 ipld/car/v2/carbs/index.go create mode 100644 ipld/car/v2/carbs/indexgobhash.go create mode 100644 ipld/car/v2/carbs/indexhashed.go create mode 100644 ipld/car/v2/carbs/indexsorted.go create mode 100644 ipld/car/v2/carbs/reader.go create mode 100644 ipld/car/v2/carbs/test.car create mode 100644 ipld/car/v2/carbs/util/hydrate.go diff --git a/ipld/car/v2/carbs/carbs.go b/ipld/car/v2/carbs/carbs.go new file mode 100644 index 000000000..ca7256f81 --- /dev/null +++ b/ipld/car/v2/carbs/carbs.go @@ -0,0 +1,301 @@ +package carbs + +import ( + "bufio" + "bytes" + "context" + "encoding/binary" + "fmt" + "io" + + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + bs "github.com/ipfs/go-ipfs-blockstore" + "github.com/multiformats/go-multihash" + + pb "github.com/cheggaaa/pb/v3" + car "github.com/ipld/go-car" + "github.com/ipld/go-car/util" + "golang.org/x/exp/mmap" +) + +var errNotFound = bs.ErrNotFound + +// Carbs provides a read-only Car Block Store. +type Carbs struct { + backing io.ReaderAt + idx Index +} + +var _ bs.Blockstore = (*Carbs)(nil) + +func (c *Carbs) Read(idx int64) (cid.Cid, []byte, error) { + bcid, data, err := util.ReadNode(bufio.NewReader(&unatreader{c.backing, idx})) + return bcid, data, err +} + +// DeleteBlock doesn't delete a block on RO blockstore +func (c *Carbs) DeleteBlock(_ cid.Cid) error { + return fmt.Errorf("read only") +} + +// Has indicates if the store has a cid +func (c *Carbs) Has(key cid.Cid) (bool, error) { + offset, err := c.idx.Get(key) + if err != nil { + return false, err + } + uar := unatreader{c.backing, int64(offset)} + _, err = binary.ReadUvarint(&uar) + if err != nil { + return false, err + } + cid, _, err := readCid(c.backing, uar.at) + if err != nil { + return false, err + } + return cid.Equals(key), nil +} + +var cidv0Pref = []byte{0x12, 0x20} + +func readCid(store io.ReaderAt, at int64) (cid.Cid, int, error) { + var tag [2]byte + if _, err := store.ReadAt(tag[:], at); err != nil { + return cid.Undef, 0, err + } + if bytes.Equal(tag[:], cidv0Pref) { + cid0 := make([]byte, 34) + if _, err := store.ReadAt(cid0, at); err != nil { + return cid.Undef, 0, err + } + c, err := cid.Cast(cid0) + return c, 34, err + } + + // assume cidv1 + br := &unatreader{store, at} + vers, err := binary.ReadUvarint(br) + if err != nil { + return cid.Cid{}, 0, err + } + + // TODO: the go-cid package allows version 0 here as well + if vers != 1 { + return cid.Cid{}, 0, fmt.Errorf("invalid cid version number: %d", vers) + } + + codec, err := binary.ReadUvarint(br) + if err != nil { + return cid.Cid{}, 0, err + } + + mhr := multihash.NewReader(br) + h, err := mhr.ReadMultihash() + if err != nil { + return cid.Cid{}, 0, err + } + + return cid.NewCidV1(codec, h), int(br.at - at), nil +} + +// Get gets a block from the store +func (c *Carbs) Get(key cid.Cid) (blocks.Block, error) { + offset, err := c.idx.Get(key) + if err != nil { + return nil, err + } + entry, bytes, err := c.Read(int64(offset)) + if err != nil { + fmt.Printf("failed get %d:%v\n", offset, err) + return nil, bs.ErrNotFound + } + if !entry.Equals(key) { + return nil, bs.ErrNotFound + } + return blocks.NewBlockWithCid(bytes, key) +} + +// GetSize gets how big a item is +func (c *Carbs) GetSize(key cid.Cid) (int, error) { + idx, err := c.idx.Get(key) + if err != nil { + return -1, err + } + len, err := binary.ReadUvarint(&unatreader{c.backing, int64(idx)}) + if err != nil { + return -1, bs.ErrNotFound + } + cid, _, err := readCid(c.backing, int64(idx+len)) + if err != nil { + return 0, err + } + if !cid.Equals(key) { + return -1, bs.ErrNotFound + } + // get cid. validate. + return int(len), err +} + +// Put does nothing on a ro store +func (c *Carbs) Put(blocks.Block) error { + return fmt.Errorf("read only") +} + +// PutMany does nothing on a ro store +func (c *Carbs) PutMany([]blocks.Block) error { + return fmt.Errorf("read only") +} + +// AllKeysChan returns the list of keys in the store +func (c *Carbs) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { + header, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) + if err != nil { + return nil, fmt.Errorf("Error reading car header: %w", err) + } + offset, err := car.HeaderSize(header) + if err != nil { + return nil, err + } + + ch := make(chan cid.Cid, 5) + go func() { + done := ctx.Done() + + rdr := unatreader{c.backing, int64(offset)} + for true { + l, err := binary.ReadUvarint(&rdr) + thisItemForNxt := rdr.at + if err != nil { + return + } + c, _, err := readCid(c.backing, thisItemForNxt) + if err != nil { + return + } + rdr.at = thisItemForNxt + int64(l) + + select { + case ch <- c: + continue + case <-done: + return + } + } + }() + return ch, nil +} + +// HashOnRead does nothing +func (c *Carbs) HashOnRead(enabled bool) { + return +} + +// Roots returns the root CIDs of the backing car +func (c *Carbs) Roots() ([]cid.Cid, error) { + header, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) + if err != nil { + return nil, fmt.Errorf("Error reading car header: %w", err) + } + return header.Roots, nil +} + +// Load opens a carbs data store, generating an index if it does not exist +func Load(path string, noPersist bool) (*Carbs, error) { + reader, err := mmap.Open(path) + if err != nil { + return nil, err + } + idx, err := Restore(path) + if err != nil { + idx, err = GenerateIndex(reader, 0, IndexSorted, false) + if err != nil { + return nil, err + } + if !noPersist { + if err = Save(idx, path); err != nil { + return nil, err + } + } + } + obj := Carbs{ + backing: reader, + idx: idx, + } + return &obj, nil +} + +// Of opens a carbs data store from an existing reader of the base data and index +func Of(backing io.ReaderAt, index Index) *Carbs { + return &Carbs{backing, index} +} + +// GenerateIndex provides a low-level interface to create an index over a +// reader to a car stream. +func GenerateIndex(store io.ReaderAt, size int64, codec IndexCodec, verbose bool) (Index, error) { + indexcls, ok := IndexAtlas[codec] + if !ok { + return nil, fmt.Errorf("unknown codec: %#v", codec) + } + + bar := pb.New64(size) + bar.Set(pb.Bytes, true) + bar.Set(pb.Terminal, true) + + bar.Start() + + header, err := car.ReadHeader(bufio.NewReader(&unatreader{store, 0})) + if err != nil { + return nil, fmt.Errorf("Error reading car header: %w", err) + } + offset, err := car.HeaderSize(header) + if err != nil { + return nil, err + } + bar.Add64(int64(offset)) + + index := indexcls() + + records := make([]Record, 0) + rdr := unatreader{store, int64(offset)} + for true { + thisItemIdx := rdr.at + l, err := binary.ReadUvarint(&rdr) + bar.Add64(int64(l)) + thisItemForNxt := rdr.at + if err != nil { + if err == io.EOF { + break + } + return nil, err + } + c, _, err := readCid(store, thisItemForNxt) + if err != nil { + return nil, err + } + records = append(records, Record{c, uint64(thisItemIdx)}) + rdr.at = thisItemForNxt + int64(l) + } + + if err := index.Load(records); err != nil { + return nil, err + } + + bar.Finish() + + return index, nil +} + +// Generate walks a car file and generates an index of cid->byte offset in it. +func Generate(path string, codec IndexCodec) error { + store, err := mmap.Open(path) + if err != nil { + return err + } + idx, err := GenerateIndex(store, 0, codec, false) + if err != nil { + return err + } + + return Save(idx, path) +} diff --git a/ipld/car/v2/carbs/carbs_test.go b/ipld/car/v2/carbs/carbs_test.go new file mode 100644 index 000000000..5ec52e972 --- /dev/null +++ b/ipld/car/v2/carbs/carbs_test.go @@ -0,0 +1,108 @@ +package carbs + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/ipfs/go-cid" + format "github.com/ipfs/go-ipld-format" +) + +/* +func mkCar() (string, error) { + f, err := ioutil.TempFile(os.TempDir(), "car") + if err != nil { + return "", err + } + defer f.Close() + + ds := mockNodeGetter{ + Nodes: make(map[cid.Cid]format.Node), + } + type linker struct { + Name string + Links []*format.Link + } + cbornode.RegisterCborType(linker{}) + + children := make([]format.Node, 0, 10) + childLinks := make([]*format.Link, 0, 10) + for i := 0; i < 10; i++ { + child, _ := cbornode.WrapObject([]byte{byte(i)}, multihash.SHA2_256, -1) + children = append(children, child) + childLinks = append(childLinks, &format.Link{Name: fmt.Sprintf("child%d", i), Cid: child.Cid()}) + } + b, err := cbornode.WrapObject(linker{Name: "root", Links: childLinks}, multihash.SHA2_256, -1) + if err != nil { + return "", fmt.Errorf("couldn't make cbor node: %v", err) + } + ds.Nodes[b.Cid()] = b + + if err := car.WriteCar(context.Background(), &ds, []cid.Cid{b.Cid()}, f); err != nil { + return "", err + } + + return f.Name(), nil +} +*/ + +func TestIndexRT(t *testing.T) { + /* + carFile, err := mkCar() + if err != nil { + t.Fatal(err) + } + defer os.Remove(carFile) + */ + carFile := "test.car" + + cf, err := Load(carFile, false) + if err != nil { + t.Fatal(err) + } + defer os.Remove(carFile + ".idx") + + r, err := cf.Roots() + if err != nil { + t.Fatal(err) + } + if len(r) != 1 { + t.Fatalf("unexpected number of roots: %d", len(r)) + } + if _, err := cf.Get(r[0]); err != nil { + t.Fatalf("failed get: %v", err) + } + + idx, err := Restore(carFile) + if err != nil { + t.Fatalf("failed restore: %v", err) + } + if idx, err := idx.Get(r[0]); idx == 0 || err != nil { + t.Fatalf("bad index: %d %v", idx, err) + } +} + +type mockNodeGetter struct { + Nodes map[cid.Cid]format.Node +} + +func (m *mockNodeGetter) Get(_ context.Context, c cid.Cid) (format.Node, error) { + n, ok := m.Nodes[c] + if !ok { + return nil, fmt.Errorf("unknown node") + } + return n, nil +} + +func (m *mockNodeGetter) GetMany(_ context.Context, cs []cid.Cid) <-chan *format.NodeOption { + ch := make(chan *format.NodeOption, 5) + go func() { + for _, c := range cs { + n, e := m.Get(nil, c) + ch <- &format.NodeOption{Node: n, Err: e} + } + }() + return ch +} diff --git a/ipld/car/v2/carbs/index.go b/ipld/car/v2/carbs/index.go new file mode 100644 index 000000000..b139a24a4 --- /dev/null +++ b/ipld/car/v2/carbs/index.go @@ -0,0 +1,89 @@ +package carbs + +import ( + "encoding/binary" + "fmt" + "io" + "os" + + "github.com/ipfs/go-cid" + "golang.org/x/exp/mmap" +) + +// IndexCodec is used as a multicodec identifier for carbs index files +type IndexCodec int + +// IndexCodec table is a first var-int in carbs indexes +const ( + IndexHashed IndexCodec = iota + 0x300000 + IndexSorted + IndexSingleSorted + IndexGobHashed +) + +// IndexCls is a constructor for an index type +type IndexCls func() Index + +// IndexAtlas holds known index formats +var IndexAtlas = map[IndexCodec]IndexCls{ + IndexHashed: mkHashed, + IndexSorted: mkSorted, + IndexSingleSorted: mkSingleSorted, + IndexGobHashed: mkGobHashed, +} + +// Record is a pre-processed record of a car item and location. +type Record struct { + cid.Cid + Idx uint64 +} + +// Index provides an interface for figuring out where in the car a given cid begins +type Index interface { + Codec() IndexCodec + Marshal(w io.Writer) error + Unmarshal(r io.Reader) error + Get(cid.Cid) (uint64, error) + Load([]Record) error +} + +// Save writes a generated index for a car at `path` +func Save(i Index, path string) error { + stream, err := os.OpenFile(path+".idx", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0640) + if err != nil { + return err + } + defer stream.Close() + + buf := make([]byte, binary.MaxVarintLen64) + b := binary.PutUvarint(buf, uint64(i.Codec())) + if _, err := stream.Write(buf[:b]); err != nil { + return err + } + return i.Marshal(stream) +} + +// Restore loads an index from an on-disk representation. +func Restore(path string) (Index, error) { + reader, err := mmap.Open(path + ".idx") + if err != nil { + return nil, err + } + + defer reader.Close() + uar := unatreader{reader, 0} + codec, err := binary.ReadUvarint(&uar) + if err != nil { + return nil, err + } + idx, ok := IndexAtlas[IndexCodec(codec)] + if !ok { + return nil, fmt.Errorf("Unknown codec: %d", codec) + } + idxInst := idx() + if err := idxInst.Unmarshal(&uar); err != nil { + return nil, err + } + + return idxInst, nil +} diff --git a/ipld/car/v2/carbs/indexgobhash.go b/ipld/car/v2/carbs/indexgobhash.go new file mode 100644 index 000000000..b7dedba0a --- /dev/null +++ b/ipld/car/v2/carbs/indexgobhash.go @@ -0,0 +1,44 @@ +package carbs + +import ( + "encoding/gob" + "io" + + "github.com/ipfs/go-cid" +) + +type mapGobIndex map[cid.Cid]uint64 + +func (m *mapGobIndex) Get(c cid.Cid) (uint64, error) { + el, ok := (*m)[c] + if !ok { + return 0, errNotFound + } + return el, nil +} + +func (m *mapGobIndex) Marshal(w io.Writer) error { + e := gob.NewEncoder(w) + return e.Encode(m) +} + +func (m *mapGobIndex) Unmarshal(r io.Reader) error { + d := gob.NewDecoder(r) + return d.Decode(m) +} + +func (m *mapGobIndex) Codec() IndexCodec { + return IndexHashed +} + +func (m *mapGobIndex) Load(rs []Record) error { + for _, r := range rs { + (*m)[r.Cid] = r.Idx + } + return nil +} + +func mkGobHashed() Index { + mi := make(mapGobIndex) + return &mi +} diff --git a/ipld/car/v2/carbs/indexhashed.go b/ipld/car/v2/carbs/indexhashed.go new file mode 100644 index 000000000..f4c04aed0 --- /dev/null +++ b/ipld/car/v2/carbs/indexhashed.go @@ -0,0 +1,43 @@ +package carbs + +import ( + "io" + + "github.com/ipfs/go-cid" + cbor "github.com/whyrusleeping/cbor/go" +) + +type mapIndex map[cid.Cid]uint64 + +func (m *mapIndex) Get(c cid.Cid) (uint64, error) { + el, ok := (*m)[c] + if !ok { + return 0, errNotFound + } + return el, nil +} + +func (m *mapIndex) Marshal(w io.Writer) error { + return cbor.Encode(w, m) +} + +func (m *mapIndex) Unmarshal(r io.Reader) error { + d := cbor.NewDecoder(r) + return d.Decode(m) +} + +func (m *mapIndex) Codec() IndexCodec { + return IndexHashed +} + +func (m *mapIndex) Load(rs []Record) error { + for _, r := range rs { + (*m)[r.Cid] = r.Idx + } + return nil +} + +func mkHashed() Index { + mi := make(mapIndex) + return &mi +} diff --git a/ipld/car/v2/carbs/indexsorted.go b/ipld/car/v2/carbs/indexsorted.go new file mode 100644 index 000000000..d16d10d7d --- /dev/null +++ b/ipld/car/v2/carbs/indexsorted.go @@ -0,0 +1,197 @@ +package carbs + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "sort" + + "github.com/ipfs/go-cid" + "github.com/multiformats/go-multihash" +) + +type digestRecord struct { + digest []byte + index uint64 +} + +func (d digestRecord) write(buf []byte) { + n := copy(buf[:], d.digest) + binary.LittleEndian.PutUint64(buf[n:], d.index) +} + +type recordSet []digestRecord + +func (r recordSet) Len() int { + return len(r) +} + +func (r recordSet) Less(i, j int) bool { + return bytes.Compare(r[i].digest, r[j].digest) < 0 +} + +func (r recordSet) Swap(i, j int) { + r[i], r[j] = r[j], r[i] +} + +type singleWidthIndex struct { + width int32 + len int64 // in struct, len is #items. when marshaled, it's saved as #bytes. + index []byte +} + +func (s *singleWidthIndex) Codec() IndexCodec { + return IndexSingleSorted +} + +func (s *singleWidthIndex) Marshal(w io.Writer) error { + if err := binary.Write(w, binary.LittleEndian, s.width); err != nil { + return err + } + if err := binary.Write(w, binary.LittleEndian, int64(len(s.index))); err != nil { + return err + } + _, err := io.Copy(w, bytes.NewBuffer(s.index)) + return err +} + +func (s *singleWidthIndex) Unmarshal(r io.Reader) error { + if err := binary.Read(r, binary.LittleEndian, &s.width); err != nil { + return err + } + if err := binary.Read(r, binary.LittleEndian, &s.len); err != nil { + return err + } + s.index = make([]byte, s.len) + s.len /= int64(s.width) + _, err := io.ReadFull(r, s.index) + return err +} + +func (s *singleWidthIndex) Less(i int, digest []byte) bool { + return bytes.Compare(digest[:], s.index[i*int(s.width):((i+1)*int(s.width)-8)]) <= 0 +} + +func (s *singleWidthIndex) Get(c cid.Cid) (uint64, error) { + d, err := multihash.Decode(c.Hash()) + if err != nil { + return 0, err + } + return s.get(d.Digest), nil +} + +func (s *singleWidthIndex) get(d []byte) uint64 { + idx := sort.Search(int(s.len), func(i int) bool { + return s.Less(i, d) + }) + if int64(idx) == s.len { + return 0 + } + if bytes.Compare(d[:], s.index[idx*int(s.width):(idx+1)*int(s.width)-8]) != 0 { + return 0 + } + return binary.LittleEndian.Uint64(s.index[(idx+1)*int(s.width)-8 : (idx+1)*int(s.width)]) +} + +func (s *singleWidthIndex) Load(items []Record) error { + m := make(multiWidthIndex) + if err := m.Load(items); err != nil { + return err + } + if len(m) != 1 { + return fmt.Errorf("unexpected number of cid widths: %d", len(m)) + } + for _, i := range m { + s.index = i.index + s.len = i.len + s.width = i.width + return nil + } + return nil +} + +type multiWidthIndex map[int32]singleWidthIndex + +func (m *multiWidthIndex) Get(c cid.Cid) (uint64, error) { + d, err := multihash.Decode(c.Hash()) + if err != nil { + return 0, err + } + if s, ok := (*m)[int32(len(d.Digest)+8)]; ok { + return s.get(d.Digest), nil + } + return 0, errNotFound +} + +func (m *multiWidthIndex) Codec() IndexCodec { + return IndexSorted +} + +func (m *multiWidthIndex) Marshal(w io.Writer) error { + binary.Write(w, binary.LittleEndian, int32(len(*m))) + for _, s := range *m { + if err := s.Marshal(w); err != nil { + return err + } + } + return nil +} + +func (m *multiWidthIndex) Unmarshal(r io.Reader) error { + var l int32 + binary.Read(r, binary.LittleEndian, &l) + for i := 0; i < int(l); i++ { + s := singleWidthIndex{} + if err := s.Unmarshal(r); err != nil { + return err + } + (*m)[s.width] = s + } + return nil +} + +func (m *multiWidthIndex) Load(items []Record) error { + // Split cids on their digest length + idxs := make(map[int][]digestRecord) + for _, item := range items { + decHash, err := multihash.Decode(item.Hash()) + if err != nil { + return err + } + digest := decHash.Digest + idx, ok := idxs[len(digest)] + if !ok { + idxs[len(digest)] = make([]digestRecord, 0) + idx = idxs[len(digest)] + } + idxs[len(digest)] = append(idx, digestRecord{digest, item.Idx}) + } + + // Sort each list. then write to compact form. + for width, lst := range idxs { + sort.Sort(recordSet(lst)) + rcrdWdth := width + 8 + compact := make([]byte, rcrdWdth*len(lst)) + for off, itm := range lst { + itm.write(compact[off*rcrdWdth : (off+1)*rcrdWdth]) + } + s := singleWidthIndex{ + width: int32(rcrdWdth), + len: int64(len(lst)), + index: compact, + } + (*m)[int32(width)+8] = s + } + return nil +} + +func mkSorted() Index { + m := make(multiWidthIndex) + return &m +} + +func mkSingleSorted() Index { + s := singleWidthIndex{} + return &s +} diff --git a/ipld/car/v2/carbs/reader.go b/ipld/car/v2/carbs/reader.go new file mode 100644 index 000000000..8accf0a84 --- /dev/null +++ b/ipld/car/v2/carbs/reader.go @@ -0,0 +1,20 @@ +package carbs + +import "io" + +type unatreader struct { + io.ReaderAt + at int64 +} + +func (u *unatreader) Read(p []byte) (n int, err error) { + n, err = u.ReadAt(p, u.at) + u.at = u.at + int64(n) + return +} + +func (u *unatreader) ReadByte() (byte, error) { + b := []byte{0} + _, err := u.Read(b) + return b[0], err +} diff --git a/ipld/car/v2/carbs/test.car b/ipld/car/v2/carbs/test.car new file mode 100644 index 0000000000000000000000000000000000000000..47a61c8c2a7def9bafcc252d3a1a4d12529615f5 GIT binary patch literal 479907 zcmdSBRZtz;(yooW>%`q5XmHoy?ry=|-QC@TySuwXfZ!6`JxGAyhwQW0D%N+C|GW6B z&f>a9O{&Ly-nU1O9+NxI*xts*(ZO$tT8#n-*e+)mgz#PvS&t~k82RzAB9>MjOd+o_4OvAu(tjWzIhi2rX3qJ)6ff&J9kP7mV^XFOuvNS(Jd z{qMJ#FJ(8=_wi8BHQ}u=;<6T|Q=!iO zNagh)J~5HNCL!IMHV1)65=PfX6{g=_2mH!-7ERt++QP$L zF9`?O)_d_m#~buJ~qfQNd<{?*_Wvv1wjvydgi3JwF;2fgnvgXnOaj(8EsnL_~UKh{jg9!E! zc+84I=z?-6Rn5l;4CEwW$VA)=+TrX@(`9fOd&f&m8=xi7@U5BpuZN>~jE^d+nv==6Y~5oF#z9g3{DpJZ9+ zqM&7n$6;!v9Wh$eR%gyd5|mWF1?0UjUad|(!F~szc|(H7>(*YkZsVb5g_^aIQqtT3 z>cGB*FMJcYB@ls{NOg*`EY6~`1oxFO^caW zb9d>NuU_afcpyN=P7H`+JOPVCWllW)JZpV3eKbT#AW<(7sMAE zK>YBc_O2;aFRm35{t<7uWR;p;fkY;ifZ*EjZ&eiFk?u^mJ-f8NrxRqaYZ{?0uZ*FWo zn^7jViqYO&QbyBAS{LLQZ1W|iI(m1Vnx2`cgiq(Av18j}s$TjnE_qiMQ+b#~?rXCm zgP#zVZF#(W2tUTKZ#Jz1%C|t()+?gr1}T~=%8^=MXQNdPD%VV5#9Vk3vjC~To4a8w ztT=1gvu1Vq(a-@|4NgW5ZU7-et0g(BrkCc!`p`=dQ$|00*B$Vj_HC~%SR5x>;%sh2 z>S?S?!1XewedO68u!GBRHu3ESDGG}+NV;{%p?J3V^� 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 Tx_{@>Ah$cBb;X>)D9fct)9`U;$XV@y4$E$ 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

hRd> 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

vHSrHL`4Y!))mrU|+My=J*?NCLvdFH?W^2AuF;5 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

ecf&5+ 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 [codec]\n") + return + } + db := os.Args[1] + codec := carbs.IndexSorted + if len(os.Args) == 3 { + if os.Args[2] == "Hash" { + codec = carbs.IndexHashed + } else if os.Args[2] == "GobHash" { + codec = carbs.IndexGobHashed + } + } + + dbBacking, err := mmap.Open(db) + if err != nil { + fmt.Printf("Error Opening car for hydration: %v\n", err) + return + } + + dbstat, err := os.Stat(db) + if err != nil { + fmt.Printf("Error statting car for hydration: %v\n", err) + return + } + + idx, err := carbs.GenerateIndex(dbBacking, dbstat.Size(), codec, true) + if err != nil { + fmt.Printf("Error generating index: %v\n", err) + return + } + + fmt.Printf("Saving...\n") + + if err := carbs.Save(idx, db); err != nil { + fmt.Printf("Error saving : %v\n", err) + return + } + return +} diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index 3e90817bc..c8708bbd8 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -6,7 +6,7 @@ import ( "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" carv1 "github.com/ipld/go-car" - "github.com/willscott/carbs" + "github.com/ipld/go-car/v2/carbs" "io" ) @@ -123,7 +123,7 @@ func (w *Writer) writeHeader(writer io.Writer, carV1Len int) (int64, error) { } func (w *Writer) writeIndex(writer io.Writer, carV1 []byte) (n int64, err error) { - // TODO avoid recopying the bytes by refacting carbs once it is integrated here. + // TODO avoid recopying the bytes by refactoring carbs once it is integrated here. // Right now we copy the bytes since carbs takes a writer. // Consider refactoring carbs to make this process more efficient. // We should avoid reading the entire car into memory since it can be large. diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index caf7e6668..7b9595d77 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -7,8 +7,8 @@ import ( format "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" dstest "github.com/ipfs/go-merkledag/test" + "github.com/ipld/go-car/v2/carbs" "github.com/stretchr/testify/assert" - "github.com/willscott/carbs" "testing" ) From cc45766eebabef2295aee6960df8c5e65d317c90 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 15 Jun 2021 11:11:06 +0100 Subject: [PATCH 3418/3817] Address `staticcheck` errors Remove unused structs, convert error messages to lower case and remove redundant `return` statements. Address review comments - Reoder imports using `gofumpt`. - Use consistent import alias for `carv1`. - Rename structs for better readability. - Add TODO to fix logging, tests, etc. - Move test related files to `testdata`. This commit was moved from ipld/go-car@d5e22dd9711992ad168866df48d621c3400b3d59 --- ipld/car/v2/carbs/carbs.go | 92 +++++++++++----------- ipld/car/v2/carbs/carbs_test.go | 31 +------- ipld/car/v2/carbs/index.go | 2 +- ipld/car/v2/carbs/indexsorted.go | 2 +- ipld/car/v2/carbs/{ => testdata}/test.car | Bin ipld/car/v2/carbs/util/hydrate.go | 4 +- 6 files changed, 51 insertions(+), 80 deletions(-) rename ipld/car/v2/carbs/{ => testdata}/test.car (100%) diff --git a/ipld/car/v2/carbs/carbs.go b/ipld/car/v2/carbs/carbs.go index ca7256f81..6891d925d 100644 --- a/ipld/car/v2/carbs/carbs.go +++ b/ipld/car/v2/carbs/carbs.go @@ -10,47 +10,47 @@ import ( blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - bs "github.com/ipfs/go-ipfs-blockstore" + blockstore "github.com/ipfs/go-ipfs-blockstore" "github.com/multiformats/go-multihash" pb "github.com/cheggaaa/pb/v3" - car "github.com/ipld/go-car" + carv1 "github.com/ipld/go-car" "github.com/ipld/go-car/util" "golang.org/x/exp/mmap" ) -var errNotFound = bs.ErrNotFound +var errNotFound = blockstore.ErrNotFound -// Carbs provides a read-only Car Block Store. -type Carbs struct { +// BlockStore provides a read-only Car Block Store. +type BlockStore struct { backing io.ReaderAt idx Index } -var _ bs.Blockstore = (*Carbs)(nil) +var _ blockstore.Blockstore = (*BlockStore)(nil) -func (c *Carbs) Read(idx int64) (cid.Cid, []byte, error) { - bcid, data, err := util.ReadNode(bufio.NewReader(&unatreader{c.backing, idx})) +func (b *BlockStore) Read(idx int64) (cid.Cid, []byte, error) { + bcid, data, err := util.ReadNode(bufio.NewReader(&unatreader{b.backing, idx})) return bcid, data, err } // DeleteBlock doesn't delete a block on RO blockstore -func (c *Carbs) DeleteBlock(_ cid.Cid) error { +func (b *BlockStore) DeleteBlock(_ cid.Cid) error { return fmt.Errorf("read only") } // Has indicates if the store has a cid -func (c *Carbs) Has(key cid.Cid) (bool, error) { - offset, err := c.idx.Get(key) +func (b *BlockStore) Has(key cid.Cid) (bool, error) { + offset, err := b.idx.Get(key) if err != nil { return false, err } - uar := unatreader{c.backing, int64(offset)} + uar := unatreader{b.backing, int64(offset)} _, err = binary.ReadUvarint(&uar) if err != nil { return false, err } - cid, _, err := readCid(c.backing, uar.at) + cid, _, err := readCid(b.backing, uar.at) if err != nil { return false, err } @@ -100,60 +100,61 @@ func readCid(store io.ReaderAt, at int64) (cid.Cid, int, error) { } // Get gets a block from the store -func (c *Carbs) Get(key cid.Cid) (blocks.Block, error) { - offset, err := c.idx.Get(key) +func (b *BlockStore) Get(key cid.Cid) (blocks.Block, error) { + offset, err := b.idx.Get(key) if err != nil { return nil, err } - entry, bytes, err := c.Read(int64(offset)) + entry, bytes, err := b.Read(int64(offset)) if err != nil { + // TODO replace with logging fmt.Printf("failed get %d:%v\n", offset, err) - return nil, bs.ErrNotFound + return nil, blockstore.ErrNotFound } if !entry.Equals(key) { - return nil, bs.ErrNotFound + return nil, blockstore.ErrNotFound } return blocks.NewBlockWithCid(bytes, key) } // GetSize gets how big a item is -func (c *Carbs) GetSize(key cid.Cid) (int, error) { - idx, err := c.idx.Get(key) +func (b *BlockStore) GetSize(key cid.Cid) (int, error) { + idx, err := b.idx.Get(key) if err != nil { return -1, err } - len, err := binary.ReadUvarint(&unatreader{c.backing, int64(idx)}) + len, err := binary.ReadUvarint(&unatreader{b.backing, int64(idx)}) if err != nil { - return -1, bs.ErrNotFound + return -1, blockstore.ErrNotFound } - cid, _, err := readCid(c.backing, int64(idx+len)) + cid, _, err := readCid(b.backing, int64(idx+len)) if err != nil { return 0, err } if !cid.Equals(key) { - return -1, bs.ErrNotFound + return -1, blockstore.ErrNotFound } // get cid. validate. return int(len), err } // Put does nothing on a ro store -func (c *Carbs) Put(blocks.Block) error { +func (b *BlockStore) Put(blocks.Block) error { return fmt.Errorf("read only") } // PutMany does nothing on a ro store -func (c *Carbs) PutMany([]blocks.Block) error { +func (b *BlockStore) PutMany([]blocks.Block) error { return fmt.Errorf("read only") } // AllKeysChan returns the list of keys in the store -func (c *Carbs) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { - header, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) +func (b *BlockStore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { + header, err := carv1.ReadHeader(bufio.NewReader(&unatreader{b.backing, 0})) if err != nil { - return nil, fmt.Errorf("Error reading car header: %w", err) + return nil, fmt.Errorf("error reading car header: %w", err) } - offset, err := car.HeaderSize(header) + offset, err := carv1.HeaderSize(header) if err != nil { return nil, err } @@ -162,14 +163,14 @@ func (c *Carbs) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { go func() { done := ctx.Done() - rdr := unatreader{c.backing, int64(offset)} - for true { + rdr := unatreader{b.backing, int64(offset)} + for { l, err := binary.ReadUvarint(&rdr) thisItemForNxt := rdr.at if err != nil { return } - c, _, err := readCid(c.backing, thisItemForNxt) + c, _, err := readCid(b.backing, thisItemForNxt) if err != nil { return } @@ -187,21 +188,20 @@ func (c *Carbs) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { } // HashOnRead does nothing -func (c *Carbs) HashOnRead(enabled bool) { - return +func (b *BlockStore) HashOnRead(bool) { } // Roots returns the root CIDs of the backing car -func (c *Carbs) Roots() ([]cid.Cid, error) { - header, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) +func (b *BlockStore) Roots() ([]cid.Cid, error) { + header, err := carv1.ReadHeader(bufio.NewReader(&unatreader{b.backing, 0})) if err != nil { - return nil, fmt.Errorf("Error reading car header: %w", err) + return nil, fmt.Errorf("error reading car header: %w", err) } return header.Roots, nil } // Load opens a carbs data store, generating an index if it does not exist -func Load(path string, noPersist bool) (*Carbs, error) { +func Load(path string, noPersist bool) (*BlockStore, error) { reader, err := mmap.Open(path) if err != nil { return nil, err @@ -218,7 +218,7 @@ func Load(path string, noPersist bool) (*Carbs, error) { } } } - obj := Carbs{ + obj := BlockStore{ backing: reader, idx: idx, } @@ -226,8 +226,8 @@ func Load(path string, noPersist bool) (*Carbs, error) { } // Of opens a carbs data store from an existing reader of the base data and index -func Of(backing io.ReaderAt, index Index) *Carbs { - return &Carbs{backing, index} +func Of(backing io.ReaderAt, index Index) *BlockStore { + return &BlockStore{backing, index} } // GenerateIndex provides a low-level interface to create an index over a @@ -244,11 +244,11 @@ func GenerateIndex(store io.ReaderAt, size int64, codec IndexCodec, verbose bool bar.Start() - header, err := car.ReadHeader(bufio.NewReader(&unatreader{store, 0})) + header, err := carv1.ReadHeader(bufio.NewReader(&unatreader{store, 0})) if err != nil { - return nil, fmt.Errorf("Error reading car header: %w", err) + return nil, fmt.Errorf("error reading car header: %w", err) } - offset, err := car.HeaderSize(header) + offset, err := carv1.HeaderSize(header) if err != nil { return nil, err } @@ -258,7 +258,7 @@ func GenerateIndex(store io.ReaderAt, size int64, codec IndexCodec, verbose bool records := make([]Record, 0) rdr := unatreader{store, int64(offset)} - for true { + for { thisItemIdx := rdr.at l, err := binary.ReadUvarint(&rdr) bar.Add64(int64(l)) diff --git a/ipld/car/v2/carbs/carbs_test.go b/ipld/car/v2/carbs/carbs_test.go index 5ec52e972..7af483e9b 100644 --- a/ipld/car/v2/carbs/carbs_test.go +++ b/ipld/car/v2/carbs/carbs_test.go @@ -1,13 +1,8 @@ package carbs import ( - "context" - "fmt" "os" "testing" - - "github.com/ipfs/go-cid" - format "github.com/ipfs/go-ipld-format" ) /* @@ -56,7 +51,8 @@ func TestIndexRT(t *testing.T) { } defer os.Remove(carFile) */ - carFile := "test.car" + // TODO use temporari directory to run tests taht work with OS file system to avoid accidental source code modification + carFile := "testdata/test.car" cf, err := Load(carFile, false) if err != nil { @@ -83,26 +79,3 @@ func TestIndexRT(t *testing.T) { t.Fatalf("bad index: %d %v", idx, err) } } - -type mockNodeGetter struct { - Nodes map[cid.Cid]format.Node -} - -func (m *mockNodeGetter) Get(_ context.Context, c cid.Cid) (format.Node, error) { - n, ok := m.Nodes[c] - if !ok { - return nil, fmt.Errorf("unknown node") - } - return n, nil -} - -func (m *mockNodeGetter) GetMany(_ context.Context, cs []cid.Cid) <-chan *format.NodeOption { - ch := make(chan *format.NodeOption, 5) - go func() { - for _, c := range cs { - n, e := m.Get(nil, c) - ch <- &format.NodeOption{Node: n, Err: e} - } - }() - return ch -} diff --git a/ipld/car/v2/carbs/index.go b/ipld/car/v2/carbs/index.go index b139a24a4..1f381308c 100644 --- a/ipld/car/v2/carbs/index.go +++ b/ipld/car/v2/carbs/index.go @@ -78,7 +78,7 @@ func Restore(path string) (Index, error) { } idx, ok := IndexAtlas[IndexCodec(codec)] if !ok { - return nil, fmt.Errorf("Unknown codec: %d", codec) + return nil, fmt.Errorf("unknown codec: %d", codec) } idxInst := idx() if err := idxInst.Unmarshal(&uar); err != nil { diff --git a/ipld/car/v2/carbs/indexsorted.go b/ipld/car/v2/carbs/indexsorted.go index d16d10d7d..eca593c9f 100644 --- a/ipld/car/v2/carbs/indexsorted.go +++ b/ipld/car/v2/carbs/indexsorted.go @@ -88,7 +88,7 @@ func (s *singleWidthIndex) get(d []byte) uint64 { if int64(idx) == s.len { return 0 } - if bytes.Compare(d[:], s.index[idx*int(s.width):(idx+1)*int(s.width)-8]) != 0 { + if !bytes.Equal(d[:], s.index[idx*int(s.width):(idx+1)*int(s.width)-8]) { return 0 } return binary.LittleEndian.Uint64(s.index[(idx+1)*int(s.width)-8 : (idx+1)*int(s.width)]) diff --git a/ipld/car/v2/carbs/test.car b/ipld/car/v2/carbs/testdata/test.car similarity index 100% rename from ipld/car/v2/carbs/test.car rename to ipld/car/v2/carbs/testdata/test.car diff --git a/ipld/car/v2/carbs/util/hydrate.go b/ipld/car/v2/carbs/util/hydrate.go index 746145811..d0fb50ae1 100644 --- a/ipld/car/v2/carbs/util/hydrate.go +++ b/ipld/car/v2/carbs/util/hydrate.go @@ -2,9 +2,9 @@ package main import ( "fmt" - "github.com/ipld/go-car/v2/carbs" "os" + "github.com/ipld/go-car/v2/carbs" "golang.org/x/exp/mmap" ) @@ -45,7 +45,5 @@ func main() { if err := carbs.Save(idx, db); err != nil { fmt.Printf("Error saving : %v\n", err) - return } - return } From 7d429419da0183786175d436a11c563f767ac23b Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 16 Jun 2021 10:44:12 +0100 Subject: [PATCH 3419/3817] Implement `OffsetReader` Because `SectionReader` requires the max number of bytes to read, since it implements `Seek`. We need something like the `SectionReader` to read the index at the end of a CAR v2 that does not require the user to know the number of readable bytes. This is because, we do not store the size of index in CAR v2 header, since it is always added as the last section. The `OffsetReader` works just like `SectionReader`, except if `n`, the number of bytes to read, is set to zero it simply carries on reading until the underlying `io.ReaderAt` returns EOF. Consequently, `OffsetReader` does not implement `Seek`. This commit was moved from ipld/go-car@7cbf448577b17fa4a1712c5d8f33c99a7cfefea2 --- ipld/car/v2/offset_reader.go | 59 ++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 ipld/car/v2/offset_reader.go diff --git a/ipld/car/v2/offset_reader.go b/ipld/car/v2/offset_reader.go new file mode 100644 index 000000000..80cd38e81 --- /dev/null +++ b/ipld/car/v2/offset_reader.go @@ -0,0 +1,59 @@ +package car + +import "io" + +// OffsetReader implements Read, and ReadAt on a section +// of an underlying io.ReaderAt. +// The main difference between io.SectionReader and OffsetReader is that +// NewOffsetReader accepts zero as n, the number of bytes to read, with +// the trade-off that it does not implement Seek. +// When n is set to zero, it will delegate io.EOF errors to the underlying +// io.ReaderAt. +// This is useful when reading a section at the end of a io.ReaderAt without +// having to know the total number readable of bytes. +type OffsetReader struct { + r io.ReaderAt + base int64 + off int64 + limit int64 + limited bool +} + +// NewOffsetReader returns an OffsetReader that reads from r +// starting at offset off and stops with io.EOF after n bytes. +// If n is set to 0 then it will carry on reading until r reaches io.EOF. +func NewOffsetReader(r io.ReaderAt, off int64, n int64) *OffsetReader { + return &OffsetReader{r, off, off, off + n, n == 0} +} + +func (o *OffsetReader) Read(p []byte) (n int, err error) { + if o.limited { + if o.off >= o.limit { + return 0, io.EOF + } + if max := o.limit - o.off; int64(len(p)) > max { + p = p[0:max] + } + } + n, err = o.r.ReadAt(p, o.off) + o.off += int64(n) + return +} + +func (o *OffsetReader) ReadAt(p []byte, off int64) (n int, err error) { + if o.limited { + if off < 0 || off >= o.limit-o.base { + return 0, io.EOF + } + off += o.base + if max := o.limit - off; int64(len(p)) > max { + p = p[0:max] + n, err = o.r.ReadAt(p, off) + if err == nil { + err = io.EOF + } + return n, err + } + } + return o.r.ReadAt(p, off) +} From 068af1712fff21a954262800a0b191ef0c58150a Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 16 Jun 2021 11:24:15 +0100 Subject: [PATCH 3420/3817] Implement CAR v2 reader with API that provides `BlockStore` Implement a CAR v2 reader that allows access to each section of the CAR, i.e. CAR v1 dump and Index, as well as a higher level API that provides a `blockstore.BlockStore` from a CAR v2 file. Address Review comments - Simplify naming of section size constants - Simplify OffsetReader by removing the dual SectionReader functionality - Improve read efficiency by reading header in one chunk - Add TODOs to improve write efficiency in a similar manner This commit was moved from ipld/go-car@1d3cbb33f41f68e5bd5403c4b1f303280f3b8545 --- ipld/car/v2/car.go | 69 ++++++++++++++++++++-------- ipld/car/v2/car_test.go | 81 ++++++++++++++++++++++++++------- ipld/car/v2/carbs/carbs_test.go | 2 +- ipld/car/v2/offset_reader.go | 48 +++++-------------- ipld/car/v2/reader.go | 61 +++++++++++++++++++++++++ ipld/car/v2/writer.go | 5 +- 6 files changed, 191 insertions(+), 75 deletions(-) create mode 100644 ipld/car/v2/reader.go diff --git a/ipld/car/v2/car.go b/ipld/car/v2/car.go index e3f7c7b79..99c5fa7d9 100644 --- a/ipld/car/v2/car.go +++ b/ipld/car/v2/car.go @@ -6,11 +6,13 @@ import ( ) const ( - // HeaderBytesSize is the fixed size of CAR v2 header in number of bytes. - HeaderBytesSize = 40 - // CharacteristicsBytesSize is the fixed size of Characteristics bitfield within CAR v2 header in number of bytes. - CharacteristicsBytesSize = 16 - uint64BytesSize = 8 + // PrefixSize is the size of the CAR v2 prefix in 11 bytes, (i.e. 11). + PrefixSize = 11 + // HeaderSize is the fixed size of CAR v2 header in number of bytes. + HeaderSize = 40 + // CharacteristicsSize is the fixed size of Characteristics bitfield within CAR v2 header in number of bytes. + CharacteristicsSize = 16 + uint64Size = 8 ) var ( @@ -22,8 +24,6 @@ var ( 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, // "version" 0x02, // uint(2) } - // The size of the CAR v2 prefix in 11 bytes, (i.e. 11). - prefixBytesSize = uint64(len(PrefixBytes)) ) type ( @@ -47,23 +47,35 @@ type ( // WriteTo writes this characteristics to the given writer. func (c Characteristics) WriteTo(w io.Writer) (n int64, err error) { - if err = writeUint64To(w, c.Hi); err != nil { + if err = binary.Write(w, binary.LittleEndian, c.Hi); err != nil { return } - n += uint64BytesSize - if err = writeUint64To(w, c.Lo); err != nil { + n += uint64Size + if err = binary.Write(w, binary.LittleEndian, c.Lo); err != nil { return } - n += uint64BytesSize + n += uint64Size return } +func (c *Characteristics) ReadFrom(r io.Reader) (int64, error) { + buf := make([]byte, CharacteristicsSize) + read, err := io.ReadFull(r, buf) + n := int64(read) + if err != nil { + return n, err + } + c.Hi = binary.LittleEndian.Uint64(buf[:uint64Size]) + c.Lo = binary.LittleEndian.Uint64(buf[uint64Size:]) + return n, nil +} + // NewHeader instantiates a new CAR v2 header, given the byte length of a CAR v1. func NewHeader(carV1Size uint64) Header { header := Header{ CarV1Size: carV1Size, } - header.CarV1Offset = prefixBytesSize + HeaderBytesSize + header.CarV1Offset = PrefixSize + HeaderSize header.IndexOffset = header.CarV1Offset + carV1Size return header } @@ -89,26 +101,43 @@ func (h Header) WithCarV1Padding(padding uint64) Header { // WriteTo serializes this header as bytes and writes them using the given io.Writer. func (h Header) WriteTo(w io.Writer) (n int64, err error) { + // TODO optimize write by encoding all bytes in a slice and writing once. wn, err := h.Characteristics.WriteTo(w) if err != nil { return } n += wn - if err = writeUint64To(w, h.CarV1Offset); err != nil { + if err = binary.Write(w, binary.LittleEndian, h.CarV1Offset); err != nil { return } - n += uint64BytesSize - if err = writeUint64To(w, h.CarV1Size); err != nil { + n += uint64Size + if err = binary.Write(w, binary.LittleEndian, h.CarV1Size); err != nil { return } - n += uint64BytesSize - if err = writeUint64To(w, h.IndexOffset); err != nil { + n += uint64Size + if err = binary.Write(w, binary.LittleEndian, h.IndexOffset); err != nil { return } - n += uint64BytesSize + n += uint64Size return } -func writeUint64To(w io.Writer, v uint64) error { - return binary.Write(w, binary.LittleEndian, v) +// ReadFrom populates fields of this header from the given r. +func (h *Header) ReadFrom(r io.Reader) (int64, error) { + n, err := h.Characteristics.ReadFrom(r) + if err != nil { + return n, err + } + remainingSize := HeaderSize - CharacteristicsSize + buf := make([]byte, remainingSize) + read, err := io.ReadFull(r, buf) + n += int64(read) + if err != nil { + return n, err + } + carV1RelOffset := uint64Size * 2 + h.CarV1Offset = binary.LittleEndian.Uint64(buf[:uint64Size]) + h.CarV1Size = binary.LittleEndian.Uint64(buf[uint64Size:carV1RelOffset]) + h.IndexOffset = binary.LittleEndian.Uint64(buf[carV1RelOffset:]) + return n, nil } diff --git a/ipld/car/v2/car_test.go b/ipld/car/v2/car_test.go index 6e1c87771..9a6a2b423 100644 --- a/ipld/car/v2/car_test.go +++ b/ipld/car/v2/car_test.go @@ -1,16 +1,14 @@ package car_test import ( + "bufio" "bytes" - cbor "github.com/ipfs/go-ipld-cbor" carv1 "github.com/ipld/go-car" carv2 "github.com/ipld/go-car/v2" "github.com/stretchr/testify/assert" "testing" ) -var prefixBytesSize = uint64(11) - func TestCarV2PrefixLength(t *testing.T) { tests := []struct { name string @@ -37,10 +35,9 @@ func TestCarV2PrefixLength(t *testing.T) { } func TestCarV2PrefixIsValidCarV1Header(t *testing.T) { - var v1h carv1.CarHeader - err := cbor.DecodeInto(carv2.PrefixBytes[1:], &v1h) + v1h, err := carv1.ReadHeader(bufio.NewReader(bytes.NewReader(carv2.PrefixBytes))) assert.NoError(t, err, "cannot decode prefix as CBOR with CAR v1 header structure") - assert.Equal(t, carv1.CarHeader{ + assert.Equal(t, &carv1.CarHeader{ Roots: nil, Version: 2, }, v1h, "CAR v2 prefix must be a valid CAR v1 header") @@ -97,8 +94,60 @@ func TestHeader_WriteTo(t *testing.T) { } gotWrite := buf.Bytes() assert.Equal(t, tt.wantWrite, gotWrite, "Header.WriteTo() gotWrite = %v, wantWrite %v", gotWrite, tt.wantWrite) - assert.EqualValues(t, carv2.HeaderBytesSize, uint64(len(gotWrite)), "WriteTo() CAR v2 header length must always be %v bytes long", carv2.HeaderBytesSize) - assert.EqualValues(t, carv2.HeaderBytesSize, uint64(written), "WriteTo() CAR v2 header byte count must always be %v bytes long", carv2.HeaderBytesSize) + assert.EqualValues(t, carv2.HeaderSize, uint64(len(gotWrite)), "WriteTo() CAR v2 header length must always be %v bytes long", carv2.HeaderSize) + assert.EqualValues(t, carv2.HeaderSize, uint64(written), "WriteTo() CAR v2 header byte count must always be %v bytes long", carv2.HeaderSize) + }) + } +} +func TestHeader_ReadFrom(t *testing.T) { + tests := []struct { + name string + target []byte + wantHeader carv2.Header + wantErr bool + }{ + { + "HeaderWithEmptyCharacteristicsIsWrittenAsExpected", + []byte{ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + carv2.Header{ + Characteristics: carv2.Characteristics{}, + }, + false, + }, + { + "NonEmptyHeaderIsWrittenAsExpected", + + []byte{ + 0xe9, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xea, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x63, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x64, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x65, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + carv2.Header{ + Characteristics: carv2.Characteristics{ + Hi: 1001, Lo: 1002, + }, + CarV1Offset: 99, + CarV1Size: 100, + IndexOffset: 101, + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotHeader := carv2.Header{} + gotRead, err := gotHeader.ReadFrom(bytes.NewReader(tt.target)) + assert.NoError(t, err) + assert.Equal(t, int64(carv2.HeaderSize), gotRead) + assert.Equal(t, tt.wantHeader, gotHeader) }) } } @@ -113,20 +162,20 @@ func TestHeader_WithPadding(t *testing.T) { { "WhenNoPaddingOffsetsAreSumOfSizes", carv2.NewHeader(123), - prefixBytesSize + carv2.HeaderBytesSize, - prefixBytesSize + carv2.HeaderBytesSize + 123, + carv2.PrefixSize + carv2.HeaderSize, + carv2.PrefixSize + carv2.HeaderSize + 123, }, { "WhenOnlyPaddingCarV1BothOffsetsShift", carv2.NewHeader(123).WithCarV1Padding(3), - prefixBytesSize + carv2.HeaderBytesSize + 3, - prefixBytesSize + carv2.HeaderBytesSize + 3 + 123, + carv2.PrefixSize + carv2.HeaderSize + 3, + carv2.PrefixSize + carv2.HeaderSize + 3 + 123, }, { "WhenPaddingBothCarV1AndIndexBothOffsetsShiftWithAdditionalIndexShift", carv2.NewHeader(123).WithCarV1Padding(3).WithIndexPadding(7), - prefixBytesSize + carv2.HeaderBytesSize + 3, - prefixBytesSize + carv2.HeaderBytesSize + 3 + 123 + 7, + carv2.PrefixSize + carv2.HeaderSize + 3, + carv2.PrefixSize + carv2.HeaderSize + 3 + 123 + 7, }, } @@ -142,9 +191,9 @@ func TestNewHeaderHasExpectedValues(t *testing.T) { wantCarV1Len := uint64(1413) want := carv2.Header{ Characteristics: carv2.Characteristics{}, - CarV1Offset: prefixBytesSize + carv2.HeaderBytesSize, + CarV1Offset: carv2.PrefixSize + carv2.HeaderSize, CarV1Size: wantCarV1Len, - IndexOffset: prefixBytesSize + carv2.HeaderBytesSize + wantCarV1Len, + IndexOffset: carv2.PrefixSize + carv2.HeaderSize + wantCarV1Len, } got := carv2.NewHeader(wantCarV1Len) assert.Equal(t, want, got, "NewHeader got = %v, want = %v", got, want) diff --git a/ipld/car/v2/carbs/carbs_test.go b/ipld/car/v2/carbs/carbs_test.go index 7af483e9b..2aa48fe70 100644 --- a/ipld/car/v2/carbs/carbs_test.go +++ b/ipld/car/v2/carbs/carbs_test.go @@ -51,7 +51,7 @@ func TestIndexRT(t *testing.T) { } defer os.Remove(carFile) */ - // TODO use temporari directory to run tests taht work with OS file system to avoid accidental source code modification + // TODO use temporary directory to run tests taht work with OS file system to avoid accidental source code modification carFile := "testdata/test.car" cf, err := Load(carFile, false) diff --git a/ipld/car/v2/offset_reader.go b/ipld/car/v2/offset_reader.go index 80cd38e81..c0f09230f 100644 --- a/ipld/car/v2/offset_reader.go +++ b/ipld/car/v2/offset_reader.go @@ -2,58 +2,34 @@ package car import "io" +var _ io.ReaderAt = (*OffsetReader)(nil) + // OffsetReader implements Read, and ReadAt on a section // of an underlying io.ReaderAt. // The main difference between io.SectionReader and OffsetReader is that -// NewOffsetReader accepts zero as n, the number of bytes to read, with -// the trade-off that it does not implement Seek. -// When n is set to zero, it will delegate io.EOF errors to the underlying -// io.ReaderAt. -// This is useful when reading a section at the end of a io.ReaderAt without -// having to know the total number readable of bytes. +// NewOffsetReader does not require the user to know the number of readable bytes. type OffsetReader struct { - r io.ReaderAt - base int64 - off int64 - limit int64 - limited bool + r io.ReaderAt + base int64 + off int64 } // NewOffsetReader returns an OffsetReader that reads from r -// starting at offset off and stops with io.EOF after n bytes. -// If n is set to 0 then it will carry on reading until r reaches io.EOF. -func NewOffsetReader(r io.ReaderAt, off int64, n int64) *OffsetReader { - return &OffsetReader{r, off, off, off + n, n == 0} +// starting at offset off and stops with io.EOF when r reaches its end. +func NewOffsetReader(r io.ReaderAt, off int64) *OffsetReader { + return &OffsetReader{r, off, off} } func (o *OffsetReader) Read(p []byte) (n int, err error) { - if o.limited { - if o.off >= o.limit { - return 0, io.EOF - } - if max := o.limit - o.off; int64(len(p)) > max { - p = p[0:max] - } - } n, err = o.r.ReadAt(p, o.off) o.off += int64(n) return } func (o *OffsetReader) ReadAt(p []byte, off int64) (n int, err error) { - if o.limited { - if off < 0 || off >= o.limit-o.base { - return 0, io.EOF - } - off += o.base - if max := o.limit - off; int64(len(p)) > max { - p = p[0:max] - n, err = o.r.ReadAt(p, off) - if err == nil { - err = io.EOF - } - return n, err - } + if off < 0 { + return 0, io.EOF } + off += o.base return o.r.ReadAt(p, off) } diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go new file mode 100644 index 000000000..b3c729e85 --- /dev/null +++ b/ipld/car/v2/reader.go @@ -0,0 +1,61 @@ +package car + +import ( + "bufio" + "fmt" + "io" + + carv1 "github.com/ipld/go-car" +) + +const version2 = 2 + +// Reader represents a reader of CAR v2. +type Reader struct { + Header Header + r io.ReaderAt +} + +// NewReader constructs a new reader that reads CAR v2 from the given r. +// Upon instantiation, the reader inspects the payload by reading the first 11 bytes and will return +// an error if the payload does not represent a CAR v2. +func NewReader(r io.ReaderAt) (*Reader, error) { + cr := &Reader{ + r: r, + } + if err := cr.readPrefix(); err != nil { + return nil, err + } + if err := cr.readHeader(); err != nil { + return nil, err + } + return cr, nil +} + +func (r *Reader) readPrefix() (err error) { + pr := io.NewSectionReader(r.r, 0, PrefixSize) + header, err := carv1.ReadHeader(bufio.NewReader(pr)) + if err != nil { + return + } + if header.Version != version2 { + err = fmt.Errorf("invalid car version: %d", header.Version) + } + return +} + +func (r *Reader) readHeader() (err error) { + headerSection := io.NewSectionReader(r.r, PrefixSize, HeaderSize) + _, err = r.Header.ReadFrom(headerSection) + return +} + +// CarV1ReaderAt provides an io.ReaderAt containing the CAR v1 dump encapsulated in this CAR v2. +func (r *Reader) CarV1ReaderAt() io.ReaderAt { + return io.NewSectionReader(r.r, int64(r.Header.CarV1Offset), int64(r.Header.CarV1Size)) +} + +// IndexReaderAt provides an io.ReaderAt containing the carbs.Index of this CAR v2. +func (r *Reader) IndexReaderAt() io.ReaderAt { + return NewOffsetReader(r.r, int64(r.Header.IndexOffset)) +} diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index c8708bbd8..cd68c548f 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -3,11 +3,12 @@ package car import ( "bytes" "context" + "io" + "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" carv1 "github.com/ipld/go-car" "github.com/ipld/go-car/v2/carbs" - "io" ) const bulkPaddingBytesSize = 1024 @@ -74,7 +75,7 @@ func (w *Writer) WriteTo(writer io.Writer) (n int64, err error) { if err != nil { return } - n += int64(prefixBytesSize) + n += int64(PrefixSize) // We read the entire car into memory because carbs.GenerateIndex takes a reader. // Future PRs will make this more efficient by exposing necessary interfaces in carbs so that // this can be done in an streaming manner. From a5b8325e9e3eb09627fa36302ddb3ba94b9e76e7 Mon Sep 17 00:00:00 2001 From: Will Scott Date: Sat, 10 Apr 2021 11:25:04 -0700 Subject: [PATCH 3421/3817] initial implementation of the 'fd' based carbon This commit was moved from ipld/go-car@0f1e5aaca365ba9806fa42182aefd3803afb0826 --- ipld/car/v2/carbon/LICENSE | 202 +++++++++++++++++++++++++++ ipld/car/v2/carbon/LICENSE-MIT | 21 +++ ipld/car/v2/carbon/README.md | 14 ++ ipld/car/v2/carbon/carbon.go | 58 ++++++++ ipld/car/v2/carbon/carbon_fds.go | 55 ++++++++ ipld/car/v2/carbon/insertionindex.go | 148 ++++++++++++++++++++ ipld/car/v2/carbon/poswriter.go | 14 ++ ipld/car/v2/carbon/reader.go | 20 +++ 8 files changed, 532 insertions(+) create mode 100644 ipld/car/v2/carbon/LICENSE create mode 100644 ipld/car/v2/carbon/LICENSE-MIT create mode 100644 ipld/car/v2/carbon/README.md create mode 100644 ipld/car/v2/carbon/carbon.go create mode 100644 ipld/car/v2/carbon/carbon_fds.go create mode 100644 ipld/car/v2/carbon/insertionindex.go create mode 100644 ipld/car/v2/carbon/poswriter.go create mode 100644 ipld/car/v2/carbon/reader.go diff --git a/ipld/car/v2/carbon/LICENSE b/ipld/car/v2/carbon/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/ipld/car/v2/carbon/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ipld/car/v2/carbon/LICENSE-MIT b/ipld/car/v2/carbon/LICENSE-MIT new file mode 100644 index 000000000..c69ae66e4 --- /dev/null +++ b/ipld/car/v2/carbon/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Will Scott + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ipld/car/v2/carbon/README.md b/ipld/car/v2/carbon/README.md new file mode 100644 index 000000000..dafbb49ee --- /dev/null +++ b/ipld/car/v2/carbon/README.md @@ -0,0 +1,14 @@ +💎 Carbon +=== + +Carbon provides a [blockstore](https://github.com/ipfs/go-ipfs-blockstore) interface with saved blocks stored to a car-compatible log. A [Carbs](github.com/willscott/carbs/) index for the resulting file is tracked and can be saved as needed. + +Note: Carbon does not support deletion. + +License +--- + +Carbs is dual-licensed under Apache 2.0 and MIT terms: + + Apache License, Version 2.0, (LICENSE or http://www.apache.org/licenses/LICENSE-2.0) + MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) diff --git a/ipld/car/v2/carbon/carbon.go b/ipld/car/v2/carbon/carbon.go new file mode 100644 index 000000000..de60dd3c5 --- /dev/null +++ b/ipld/car/v2/carbon/carbon.go @@ -0,0 +1,58 @@ +package carbon + +import ( + "errors" + "os" + + "github.com/ipfs/go-cid" + bs "github.com/ipfs/go-ipfs-blockstore" + "github.com/ipld/go-car" + "github.com/willscott/carbs" +) + +// Carbon is a carbs-index-compatible blockstore supporting appending additional blocks +type Carbon interface { + bs.Blockstore + Checkpoint() error + Finish() error +} + +// errUnsupported is returned for unsupported blockstore operations (like delete) +var errUnsupported = errors.New("unsupported by carbon") + +// errNotFound is returned for lookups to entries that don't exist +var errNotFound = errors.New("not found") + +// New creates a new Carbon blockstore +func New(path string) (Carbon, error) { + return NewWithRoots(path, []cid.Cid{}) +} + +// NewWithRoots creates a new Carbon blockstore with a provided set of root cids as the car roots +func NewWithRoots(path string, roots []cid.Cid) (Carbon, error) { + wfd, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + return nil, err + } + rfd, err := os.OpenFile(path, os.O_RDONLY, 0666) + if err != nil { + return nil, err + } + + hdr := car.CarHeader{ + Roots: roots, + Version: 1, + } + if err := car.WriteHeader(&hdr, wfd); err != nil { + return nil, err + } + + idx := insertionIndex{} + f := carbonFD{ + path, + &poswriter{wfd, 0}, + *carbs.Of(rfd, &idx), + &idx, + } + return &f, nil +} diff --git a/ipld/car/v2/carbon/carbon_fds.go b/ipld/car/v2/carbon/carbon_fds.go new file mode 100644 index 000000000..fb25cfd7b --- /dev/null +++ b/ipld/car/v2/carbon/carbon_fds.go @@ -0,0 +1,55 @@ +package carbon + +import ( + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + "github.com/ipld/go-car/util" + carbs "github.com/willscott/carbs" +) + +// carbonFD is a carbon implementation based on having two file handles opened, one appending to the file, and the other +// seeking to read items as needed. This implementation is preferable for a write-heavy workload. +type carbonFD struct { + path string + writeHandle *poswriter + carbs.Carbs + idx *insertionIndex +} + +var _ (Carbon) = (*carbonFD)(nil) + +func (c *carbonFD) DeleteBlock(cid.Cid) error { + return errUnsupported +} + +// Put puts a given block to the underlying datastore +func (c *carbonFD) Put(b blocks.Block) error { + return c.PutMany([]blocks.Block{b}) +} + +// PutMany puts a slice of blocks at the same time using batching +// capabilities of the underlying datastore whenever possible. +func (c *carbonFD) PutMany(b []blocks.Block) error { + for _, bl := range b { + n := c.writeHandle.at + if err := util.LdWrite(c.writeHandle, bl.Cid().Bytes(), bl.RawData()); err != nil { + return err + } + c.idx.items.InsertNoReplace(mkRecordFromCid(bl.Cid(), n)) + } + return nil +} + +// Finish serializes the carbon index so that it can be later used as a carbs read-only blockstore +func (c *carbonFD) Finish() error { + fi, err := c.idx.Flatten() + if err != nil { + return err + } + return carbs.Save(fi, c.path) +} + +// Checkpoint serializes the carbon index so that the partially written blockstore can be resumed. +func (c *carbonFD) Checkpoint() error { + return carbs.Save(c.idx, c.path) +} diff --git a/ipld/car/v2/carbon/insertionindex.go b/ipld/car/v2/carbon/insertionindex.go new file mode 100644 index 000000000..0c87953b5 --- /dev/null +++ b/ipld/car/v2/carbon/insertionindex.go @@ -0,0 +1,148 @@ +package carbon + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + + "github.com/ipfs/go-cid" + "github.com/multiformats/go-multihash" + "github.com/petar/GoLLRB/llrb" + cbor "github.com/whyrusleeping/cbor/go" + carbs "github.com/willscott/carbs" +) + +// IndexInsertion carbs IndexCodec identifier +const IndexInsertion carbs.IndexCodec = 0x300010 + +// init hook to register the index format +func init() { + carbs.IndexAtlas[IndexInsertion] = mkInsertion +} + +type insertionIndex struct { + items llrb.LLRB +} + +type record struct { + digest []byte + carbs.Record +} + +func (r record) Less(than llrb.Item) bool { + other, ok := than.(record) + if !ok { + return false + } + return bytes.Compare(r.digest, other.digest) <= 0 +} + +func mkRecord(r carbs.Record) record { + d, err := multihash.Decode(r.Hash()) + if err != nil { + return record{} + } + + return record{d.Digest, r} +} + +func mkRecordFromCid(c cid.Cid, at uint64) record { + d, err := multihash.Decode(c.Hash()) + if err != nil { + return record{} + } + + return record{d.Digest, carbs.Record{Cid: c, Idx: at}} +} + +func (ii *insertionIndex) Get(c cid.Cid) (uint64, error) { + d, err := multihash.Decode(c.Hash()) + if err != nil { + return 0, err + } + entry := record{digest: d.Digest} + e := ii.items.Get(entry) + if e == nil { + return 0, errNotFound + } + r, ok := e.(record) + if !ok { + return 0, errUnsupported + } + + return r.Record.Idx, nil +} + +func (ii *insertionIndex) Marshal(w io.Writer) error { + if err := binary.Write(w, binary.LittleEndian, int64(ii.items.Len())); err != nil { + return err + } + + var err error + + iter := func(i llrb.Item) bool { + if err = cbor.Encode(w, i.(record).Record); err != nil { + return false + } + return true + } + ii.items.AscendGreaterOrEqual(ii.items.Min(), iter) + return err +} + +func (ii *insertionIndex) Unmarshal(r io.Reader) error { + var len int64 + if err := binary.Read(r, binary.LittleEndian, &len); err != nil { + return err + } + d := cbor.NewDecoder(r) + for i := int64(0); i < len; i++ { + var rec carbs.Record + if err := d.Decode(&rec); err != nil { + return err + } + ii.items.InsertNoReplace(mkRecord(rec)) + } + return nil +} + +// Codec identifies this index format +func (ii *insertionIndex) Codec() carbs.IndexCodec { + return IndexInsertion +} + +func (ii *insertionIndex) Load(rs []carbs.Record) error { + for _, r := range rs { + rec := mkRecord(r) + if rec.digest == nil { + return fmt.Errorf("invalid entry: %v", r) + } + ii.items.InsertNoReplace(rec) + } + return nil +} + +func mkInsertion() carbs.Index { + ii := insertionIndex{} + return &ii +} + +// Flatten returns a 'indexsorted' formatted index for more efficient subsequent loading +func (ii *insertionIndex) Flatten() (carbs.Index, error) { + si := carbs.IndexAtlas[carbs.IndexSorted]() + rcrds := make([]carbs.Record, ii.items.Len()) + + idx := 0 + iter := func(i llrb.Item) bool { + rcrds[idx] = i.(record).Record + idx++ + return true + } + ii.items.AscendGreaterOrEqual(ii.items.Min(), iter) + + if err := si.Load(rcrds); err != nil { + return nil, err + } + return si, nil +} diff --git a/ipld/car/v2/carbon/poswriter.go b/ipld/car/v2/carbon/poswriter.go new file mode 100644 index 000000000..5b9444e5a --- /dev/null +++ b/ipld/car/v2/carbon/poswriter.go @@ -0,0 +1,14 @@ +package carbon + +import "io" + +type poswriter struct { + io.Writer + at uint64 +} + +func (p *poswriter) Write(b []byte) (n int, err error) { + n, err = p.Writer.Write(b) + p.at += uint64(n) + return +} diff --git a/ipld/car/v2/carbon/reader.go b/ipld/car/v2/carbon/reader.go new file mode 100644 index 000000000..bc346557a --- /dev/null +++ b/ipld/car/v2/carbon/reader.go @@ -0,0 +1,20 @@ +package carbon + +import "io" + +type unatreader struct { + io.ReaderAt + at int64 +} + +func (u *unatreader) Read(p []byte) (n int, err error) { + n, err = u.ReadAt(p, u.at) + u.at = u.at + int64(n) + return +} + +func (u *unatreader) ReadByte() (byte, error) { + b := []byte{0} + _, err := u.Read(b) + return b[0], err +} From 086d199c7fcd5629054783e7021a9863261bdb55 Mon Sep 17 00:00:00 2001 From: Will Scott Date: Sat, 10 Apr 2021 11:36:49 -0700 Subject: [PATCH 3422/3817] close write handle on 'finish' This commit was moved from ipld/go-car@7593b75c95b3301c6f890665c2a110e3c909189e --- ipld/car/v2/carbon/carbon_fds.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ipld/car/v2/carbon/carbon_fds.go b/ipld/car/v2/carbon/carbon_fds.go index fb25cfd7b..c4cfdd609 100644 --- a/ipld/car/v2/carbon/carbon_fds.go +++ b/ipld/car/v2/carbon/carbon_fds.go @@ -1,6 +1,8 @@ package carbon import ( + "os" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" "github.com/ipld/go-car/util" @@ -46,6 +48,12 @@ func (c *carbonFD) Finish() error { if err != nil { return err } + fd, ok := c.writeHandle.Writer.(*os.File) + if ok { + if err := fd.Close(); err != nil { + return err + } + } return carbs.Save(fi, c.path) } From 89ff975cc9e1449d38d1e4a83bf32216a190ee8d Mon Sep 17 00:00:00 2001 From: Will Scott Date: Sun, 11 Apr 2021 09:49:54 -0700 Subject: [PATCH 3423/3817] add test for expected round-trip behavior This commit was moved from ipld/go-car@3e05a31e1b8732a2a63fbdd1951237c870651061 --- ipld/car/v2/carbon/carbon.go | 14 ++-- ipld/car/v2/carbon/carbon_test.go | 92 +++++++++++++++++++++++++++ ipld/car/v2/carbon/insertionindex.go | 2 +- ipld/car/v2/carbon/test.car | Bin 0 -> 479907 bytes 4 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 ipld/car/v2/carbon/carbon_test.go create mode 100644 ipld/car/v2/carbon/test.car diff --git a/ipld/car/v2/carbon/carbon.go b/ipld/car/v2/carbon/carbon.go index de60dd3c5..3122504f0 100644 --- a/ipld/car/v2/carbon/carbon.go +++ b/ipld/car/v2/carbon/carbon.go @@ -2,6 +2,7 @@ package carbon import ( "errors" + "fmt" "os" "github.com/ipfs/go-cid" @@ -30,27 +31,28 @@ func New(path string) (Carbon, error) { // NewWithRoots creates a new Carbon blockstore with a provided set of root cids as the car roots func NewWithRoots(path string, roots []cid.Cid) (Carbon, error) { - wfd, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND, 0666) + wfd, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666) if err != nil { - return nil, err + return nil, fmt.Errorf("couldn't create backing car: %w", err) } rfd, err := os.OpenFile(path, os.O_RDONLY, 0666) if err != nil { - return nil, err + return nil, fmt.Errorf("could not re-open read handle: %w", err) } hdr := car.CarHeader{ Roots: roots, Version: 1, } - if err := car.WriteHeader(&hdr, wfd); err != nil { - return nil, err + writer := poswriter{wfd, 0} + if err := car.WriteHeader(&hdr, &writer); err != nil { + return nil, fmt.Errorf("couldn't write car header: %w", err) } idx := insertionIndex{} f := carbonFD{ path, - &poswriter{wfd, 0}, + &writer, *carbs.Of(rfd, &idx), &idx, } diff --git a/ipld/car/v2/carbon/carbon_test.go b/ipld/car/v2/carbon/carbon_test.go new file mode 100644 index 000000000..1d90eae24 --- /dev/null +++ b/ipld/car/v2/carbon/carbon_test.go @@ -0,0 +1,92 @@ +package carbon_test + +import ( + "io" + "math/rand" + "os" + "testing" + + "github.com/ipfs/go-cid" + "github.com/ipld/go-car" + "github.com/willscott/carbon" + "github.com/willscott/carbs" +) + +func TestCarbon(t *testing.T) { + f, err := os.Open("test.car") + if err != nil { + t.Skipf("fixture not found: %q", err) + return + } + defer f.Close() + + ingester, err := carbon.New("testcarbon.car") + if err != nil { + t.Fatal(err) + } + defer func() { + os.Remove("testcarbon.car") + }() + + r, err := car.NewCarReader(f) + if err != nil { + t.Fatal(err) + } + cids := make([]cid.Cid, 0) + for true { + b, err := r.Next() + if err == io.EOF { + break + } + if err := ingester.Put(b); err != nil { + t.Fatal(err) + } + cids = append(cids, b.Cid()) + + // try reading a random one: + candidate := cids[rand.Intn(len(cids))] + if has, err := ingester.Has(candidate); !has || err != nil { + t.Fatalf("expected to find %s but didn't: %s", candidate, err) + } + } + + for _, c := range cids { + b, err := ingester.Get(c) + if err != nil { + t.Fatal(err) + } + if !b.Cid().Equals(c) { + t.Fatal("wrong item returned") + } + } + + if err := ingester.Finish(); err != nil { + t.Fatal(err) + } + defer func() { + os.Remove("testcarbon.car.idx") + }() + + stat, err := os.Stat("testcarbon.car.idx") + if err != nil { + t.Fatal(err) + } + if stat.Size() <= 0 { + t.Fatalf("index not written: %v", stat) + } + + carb, err := carbs.Load("testcarbon.car", true) + if err != nil { + t.Fatal(err) + } + + for _, c := range cids { + b, err := carb.Get(c) + if err != nil { + t.Fatal(err) + } + if !b.Cid().Equals(c) { + t.Fatal("wrong item returned") + } + } +} diff --git a/ipld/car/v2/carbon/insertionindex.go b/ipld/car/v2/carbon/insertionindex.go index 0c87953b5..c74857678 100644 --- a/ipld/car/v2/carbon/insertionindex.go +++ b/ipld/car/v2/carbon/insertionindex.go @@ -35,7 +35,7 @@ func (r record) Less(than llrb.Item) bool { if !ok { return false } - return bytes.Compare(r.digest, other.digest) <= 0 + return bytes.Compare(r.digest, other.digest) < 0 } func mkRecord(r carbs.Record) record { diff --git a/ipld/car/v2/carbon/test.car b/ipld/car/v2/carbon/test.car new file mode 100644 index 0000000000000000000000000000000000000000..47a61c8c2a7def9bafcc252d3a1a4d12529615f5 GIT binary patch literal 479907 zcmdSBRZtz;(yooW>%`q5XmHoy?ry=|-QC@TySuwXfZ!6`JxGAyhwQW0D%N+C|GW6B z&f>a9O{&Ly-nU1O9+NxI*xts*(ZO$tT8#n-*e+)mgz#PvS&t~k82RzAB9>MjOd+o_4OvAu(tjWzIhi2rX3qJ)6ff&J9kP7mV^XFOuvNS(Jd z{qMJ#FJ(8=_wi8BHQ}u=;<6T|Q=!iO zNagh)J~5HNCL!IMHV1)65=PfX6{g=_2mH!-7ERt++QP$L zF9`?O)_d_m#~buJ~qfQNd<{?*_Wvv1wjvydgi3JwF;2fgnvgXnOaj(8EsnL_~UKh{jg9!E! zc+84I=z?-6Rn5l;4CEwW$VA)=+TrX@(`9fOd&f&m8=xi7@U5BpuZN>~jE^d+nv==6Y~5oF#z9g3{DpJZ9+ zqM&7n$6;!v9Wh$eR%gyd5|mWF1?0UjUad|(!F~szc|(H7>(*YkZsVb5g_^aIQqtT3 z>cGB*FMJcYB@ls{NOg*`EY6~`1oxFO^caW zb9d>NuU_afcpyN=P7H`+JOPVCWllW)JZpV3eKbT#AW<(7sMAE zK>YBc_O2;aFRm35{t<7uWR;p;fkY;ifZ*EjZ&eiFk?u^mJ-f8NrxRqaYZ{?0uZ*FWo zn^7jViqYO&QbyBAS{LLQZ1W|iI(m1Vnx2`cgiq(Av18j}s$TjnE_qiMQ+b#~?rXCm zgP#zVZF#(W2tUTKZ#Jz1%C|t()+?gr1}T~=%8^=MXQNdPD%VV5#9Vk3vjC~To4a8w ztT=1gvu1Vq(a-@|4NgW5ZU7-et0g(BrkCc!`p`=dQ$|00*B$Vj_HC~%SR5x>;%sh2 z>S?S?!1XewedO68u!GBRHu3ESDGG}+NV;{%p?J3V^� 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 Tx_{@>Ah$cBb;X>)D9fct)9`U;$XV@y4$E$ z*eQTV_~wpPza$N;iF~qTt9(-U8?Ha){td4GT%5MRg*Z2duf6>MQGARCBcHJWbpUH^+{uK~fG)irF2I@#40d52k zF^uCx;189qqdV^>>5k8vtkAV&`I)V_C6xvbbYMeQLIdQ1HIAao)|W;J*~VL;vj}Yr z3BDbI4T6}j8IS+QUyKNMtbNPy}!6Jf6!jWK!#e`B3md?ulTSI1pvQVLqJ zy1?>!$rs6hNmkR#C+yOQ;1nMs2+!Li(WpM#gT~R^t&*URHe2G&zzARMh`pu4yJ)C$ zr8#R8Nu^i>m}3YEQa11np*0(Fw=uR-hn8#{x)GZV8-;By6^@d?Ud|y-@VBFCHC)oc zI%j46hO3RlpX+wR`jG34ntx=Ab>4#8VR_u+ioKt*NQ=;>l?DsypKu+)e15!5+_o#r z{kSC+FSs_&&ZI{3#AvpWpT;JaI&b|37g-Fz?j12l>%a+`3kUIO`la*}Hor1ijrqU1 z^C4_8(UHHH+&jL1i1_b=%jylTW=Gc-)3n{~A!yCbxyPNUYg1!>C&IfOC84V}2#4nI zKp#c_^&lRD1^+EEm^6S72we;?v$90->Nb9vUdRMWndUO#L!TEHCsrFnN-Hk9ASS^; zS7N>JbJ_T5MRCUK-xcJaa{m`x@y6(J0@rDR1r%Rb)U=zaTa%I4B&#S@+d#m|$2Afn z<&FKv6%0Lv-D2EIqY#Y;!C?GBUAFer44j{0@;S)}Wtr^8Y`(yl=En~f^S=z2jq@6# zV3p^{ZqsjRpX9JyzQGmd*JCEI>FCqqun^J`5n0#ySooQ<5WN-MwM3z?=xHGdJ(_Ib z=s3`VexA(*6h5rhd`9y;XEhkLWHTr`{G~Jdh}>Zg5&wyUf)GA zoQr;0SF>M<=&nzz#IA5h0J&ARs9-q=&c}Q=mC{q;o`c>yH`fv~6yI-9#K8OCN~1v3 z^y+9LM5aq%>m&S4CfJU69rZPS;wsKSrUw7~Dx^fma;)yCf(?T7iBC=PJDmi61TYj) zJat9T@GAo+t_sKxp2y<2)&o-JGnqe->rZ!xT)zv8TH$sla7^==&z$r1ftVQlAb2q_-PlLeoF8z^xwzf?>EZ9ZM*eh z$j;qF#Bo}A7kR-y!B)CUt0@Kcnpjq*^Y%<#wD-Y&My(r67;Kf`vwc=UofJ2R_q%!3 z&Rv*w1o{vy_<#t+sCf-URUYBZBQTrS^0+mh$Hkt0X)JHz%uy=vcdzwN+w>QQafqeC z>zA0q2&o&^KY`t4P}JuJmi821H8+;(kc3W^1IjS+o<`~D(?8E zVxssb@Dnq`_jI-l#S!As?}w|7OMrQ|cE+8_O!JJbkVO%0GlCS@IQX(hLF_a8392%D zm-C71YN&kZ@kbs+o0u-=VDwR`IsNKZFxL2A7BZC6wN+2Mt)+{P8G{I(%1-HqoDz9Sv z_$1LGq5T35wZ43XPPoe67>d&&2>&sK+rX)WTTPr?a{po@sGE~4@o^uT!Hn`{9!W_T zNl=YwJ5dTs`I&ReSX2?F!ghY`gF&lCR?s=>jxiQ(Z?$duS=3w=x7iwzk~-r}gG8)t zNULHRGQE6$dK-{onL5i6*DeZ@6oyX_{lOI$@WuP$fV>2v93SD8gs}Di?aAq|zqnB}uLrFLjrFw$azwR?q_;|@S z2&%1xDFa4RGKZ^SJkUzz)7?wC!cK(;bN7bE@v?3Q&p95!6TD7i)<=g;B?c%;!nW(? zEY*dM)ETLN_gY8qRPlWsMsWRhn0&{TM6(h%L`o(6gtz6WXeefKE~T*u|1*uauAtbA z_N)z18U9?V$2lj5IY7pJ%iGOSnAR!}Sce7t&t5CuqUJ$CfPhY}dX{%;O~YJ;<=H?G zGZAXrG;c$FiK+gZhztt>Y~CqDwP~Oq_!fd7Lenplo)%U#(wo;!5F&gyQsoJH?4U{B zccK425kY+u5#Mg}F+4*=2d)5RWT7749W-_R%V;nlPHbt%VcCK$zXMD=qwk@bkG7)& ztBX6;ToAgPP*MD;H{J9iiJ2hMS$GnIH-20|Cpf-6^p#AzUBkiB>gIJ+Tp3S_KTJJ~ z4}b#y+Yb6u?%zb@KdZS8&?+OpO<% z7X6OV32@SSm1g-U`^BBB+BVuVm@^%g6tq!VQg1JyA&g@3g&gKxmsU|#!!hDuh*h{{ z3B#Dzgs;`#%)FtdN)3-+*tU7|!$&;g$32=T<)@R$na-S zTqpt9DUV6ow=__opF_oc5N@na%v?Z;;J=kbwyBM5wlCRU$VV3V(G*z`k&EnXKb}+` zhib)X@PL@Lv+}In!}#Kg_k8l_z5{O8we3d|V12iC)Sa_p%ju1vQf9PzD{)%v-~3eN6t}U}i*}-&G2!gj ze9o>uAPw=+(4IH&!-;CpMXohk{|eESzJ!hNoGb-k?8~|Jcl*Vb?cbF$_8BBv)w0zr z=}%5LCPZ1~%gvBrYBN0dq+U$*%1Aibe-8SVoL2XJ%TS&(z^uHIO*Qc5_%dsFXUqB2 zLbCZX>rOB94wMwK)V$Nk9&OaC*p#8 zE8QAUASI))Qmjy;Fe;oOpQ&pINzUHC687iyfk}QB`mdvqZ2yhIC_>1V+b0q)jSBY3 z^Gi9!N71v|j2HvDmFkwe1(U{3P`jBpM@2@2548#sf%+L;!EYJ`_|F-f!ZPQ;??jMtL3+_@>LBD+d zY9<1%Sr4g8hbHsNqWm2fLy@FvS5B$q1cO%%7KD^PKihE40g>OclXFVya}LL6?$uu{ zPa~UoR*sn8z;R_@*pw9b+gHb>kbEIkq0Gq63;mdTKr`iet35>q-o6(*dLdIWv0S5O9@&P557hX$AFgQrQio!ROW z?`?^#Q5!v!>g7M=d(TfugS4?9Tij1;r(qw+I@<)f!L?jDwTlNiv$b+r{@7}lZYE!x;3oTynF7lh5D zri@5DUaa5h(n7k{hk%Lo#h?pLa$7c4+99|?`yxj;De6LTv3c6I~%u#@(wUk4~ovzJoyBE28qBn zFZ$SlTCX4-5VnKgG$DZ+2w3Y0%l#_Z21CkM=`1$lXQ*sgz8@s?k;6>H&M7~@_ih9H zx0S2oE|{eP5SI@(gG^`Omy<-g&!!85BA-DyH!Z@J8dR}x|8s3xWSV4LX(jY4R$ehe zJV|K$(3B&_|G;5rgx$LJ`rhs z6On*bo#vYvS#9dWNOTY6rQX2Q?m0Py0msx_q*|Hq(ni&Dw!;^?4hPe%eM0e*3n>M#iAy!rOcA>RUm5H9TXrk{tnY}b=&dEUq5r1q+ zVYyLjV;0(r1bM=RW40@Z-@u+DoVuNAB&6N%(6j7-Y)xLUBQ!JaAOs>Uthj3TFVpWN z|Kb4+%uZrJtJtm(e&6||AuRs(z+P{*oB#o+QK+O{5rydL04p_hjoQZwnp(S~ZLEJ& zh#e)G^wq|8e!2}-wafffmzI%@?8iQ}t#0eS6XQu1X!G0X0nqqm73w-|LVoR53a~JxB@YJ=-_$c z?Kd7Xs4=a44gx@Dz~mFEwL@pLE;I7f;m;Ln&)1v8@l#0~p&8VYJ_9l!VZQO9eh|2D z9RQ0abL7`H&i9}CNV(fty@Oifr@RS9V0%^x62b1TMg~VnruMf(|0(x>!BzG=CgWJ4 zmOft!^@(L@Mh;Q2h`7W%hlb`zOm`+F^)+RnoWHW_nB8dixjHrvNDu3m^dNHOqha=V zzNDToM{nDbJsKJSENkQ>Ku*`2hsWK-Fu*&VfPg@mFOba;gr%@Q;QV9W$9c3Us!t6o4+0jqRNMxSx`; zd=1K^x}L+iML7CZHoAdo%Je2@|Hevw4ICe-?O50@ z!>P61f|RU1S0CXM)@I{l?Asn$9>y@@Tavb7;>!A2ZV&@}AvD>eSJk;f-M;V%OkS7m+mv&>41O&7j#vY?L{}E4$lXTP%0<_ z<8=oQo2|8c;DaVt?y)r@_0K7r0y!mj(R{L|b2rkL{`<>qhGi3bi#C1BqBpI9;4;C4<-CCCu-)MX%ovUn0$fd7mhV=S!4TO6pBN`{X(u`%{GY#j@wryPZ~l>buBgdub(t8PrA1Nx+L)dv z75NUYt4H62_f13)1_2iDL}U>tF#8h}LY?c&@~vX-+RleOTy0yXm2wQeo3%`$&b@b` z|2`4XeG`#lQ9fa}AQ-rnDrkmOKRsltZE5xl=WNz*P$cg1u-in2uCODBc1~7?4wX~lS7IY~URD-kQTn#&fhuxB+H_hLI9T^A1?>X8M6#1DX0JYxA>?4u zn?w7y2f9BuIs*F#-B(+}Tx^o@Kd2vJ(GddE&{EITyI~?0yGC3o(((ELPllvI1Suuh z>lA$QG*+%#d$!m5wPOwpWOYgJvN{i6StA2cc)~ZlOUxhz`Cap<-}?GNx-=Trce;|S z0^M|m7G#mKPy+xi=NDz~IsIs8x1mG@jUbC|l#7Ray+h6ZSejm9YgbqNX^5O_YzDh$ znehB4sRhf`VX${EPZn=y7u}81x>qrdjH3NSk%oL!%3)2!5=^<8UbBTbf#u$2?``xZ zYCb4j^r8N>^No$;akQsYC?7tn_F#`k|-M+c_P%D-U-9CA{ zwsxU!#O*Y<>U6@M@)N20+xY)KMvo`(3i~lMj(umXh1$6tyob5ZWU1MCu0Pe!E6xZF zLemKYp=?zE%-<377_}krvxF^oi^0fP?deIzH$33hTug6p z`F)85XbV}Lp0UF90<6xF3#YQ&EbIr>U7tWQs8F7oIXuhiGv#81WWN6&>fR|l)9h*c zj;)SuTOHfBt&Wq9la9@fZQHh!j%}-B+nq1@AMebUd3I)d_Uq0@UHP3=YgMhP%2+IC zLPI27jEN{Q4K>Y3d`v*PWi1jEzI^k_?b`L&U)`2N4) z`ZwSI1y`Q5_E8c@N~}?f)gN$&Ibu_yU!k$fxNG5v%OX1>6K63iylQ#aW-U`jzRE=L zH}y!BGAbaNykFYQySD826DOw^Rm&RP`r&0eIrt+U>>ap@aAY3b_ds_g>Jj~_Xg!;-jK4y%v-r~rd9Lu`HV(+7!e-OKEU=ehm<@Z9Rpl zGYfF2a;YJeJo(OzLm1{&mT@#&k3Z9qt=SeLXR15!0T+3+FK?G#3VI5n`Np_$u1zfv zE%~t^y80EGzP?iS5C#SW$Uo-PVj4ggtQD!9(g_%#i2JXRJ;?vDQG0QRKi3F2Bt(xV%OB0H=qPk%&(0>{((08~(o*vRqMx|&FSndKbOo_5oMPRkKsFK!zex$!)Y9jI273L6B z!SNJ+F!@7Y8)Kvl6bk@fZijcU@;rse_SS3;*_=HnMc|+VpT^vh%vJ8bK_(2na?LP= zu>g>AnKpb;t#PHoSoECPuC@&smK?3&ul-S%cOtPbdY9}B9nD5j0(5eRwXc52l_834 z`dF*7ww}I`{YY;mDPfLZB@!dkk6iGbQ0 zrA@KV*D9lBulHbs(DnNJ&{vlRih#~U#H|@PA8XU1|2M6II%z5gdA|6KzeMXS@|B0R za$CEcCjR*yw&(ywsXU+VKZiR*yGur6rI-vf6vw9O6AFufuXE)<^bbG)o($|Z zY5nN51)cs#f5UhLS3)DfiuZvyXHEyFQBE5j`&(l=N?Fqy7g8_I-$cJqHvZGADfM}r;FT>N31&N1E8{phTMYh|)-5ppVO;VdvxZAla z6Z|bA|K|I@M5LKwiP7=KnTX=cW|vB#kWvUYip(Jij||F;vu|sD=z_xnAG>A4)GlY% zCM-V&?kf`XZ^@{tz>7zdTHW!NO-EqWCG@#;jalkmrrVpJDV{VwuC8qa!YE@;Hg}xx zV*(!{;_pd)0fCnPkZR-WaLaM%ztJh8$UR6^dDG(fn0pek=pG`3FKm-q)s$|ST)Wgs z+ZfaRd+y2BE^X>z&FOwF%oSo5^&Bp#W(EITo@-9QGiD6)wQ ziW+0Fu!dG`lSH_!XmnS;E#s=0xlHxrqnIG#OL@!FLjUS4bky-jv8>F%GF}y@=OMio zlu{Mh?y9q{Vo*%%N?On^oS=!~9~3PGKQKmMGOp?L2xuQC>EgIhC|j(!y~==Wy@-Zi zIil`hjC6=*x3Q+froT3JeoreDs>SUSQrqp62M(SfeL!p54A8_uyR)tpA~J`B59=c% z7c!WP#L*7HdGa)*Yet&Bx<-5x!d%^k0dk$&MDgoElO~Nw8ec&D?o^qXRheaJzqOpM zo5KqsdA|rOQO{RjmKBBG(@3C3_#q;u2G4vUI4GfA5`)r@J6hkhOy#QT5}qXpl!fw*)d{pCNFMLMC-XuMInmKuqgXtJl}jJ6bz7mJ`SRe0g0VJ z2Hk5;QcL&m{9)>E%}X46f8Y&H8O{N^=PUazA{zSWCZCjn0n5>OSr1J#J)mD-1>=jl zg@#gBB-*i93LQ;5+iFG9C)59)h?KsINWk|*WK@CsE*we5`~t*NnEfrIGZvVbVtiZS zHK^v$RX#sWnYZp9EUEzjrstM{H*{x#Y@NkNol|jn0=LQStVhtfNld`h|)O zdBRpUOl^Bz^AN$!3Ate!_%XY^NCI5XV*9X4ScsG+`;Q|O2o|sB?3`vyuD{Y%^|n|d z1w5rI=#TkuirOuLxS<5j#=7@3KX^yODV<22^M@cBxjFYJ+y-oN2?2OGR>ZT@pE2+B zdx89fMZ3k)W%A{389=dJU9=6! zt0d7mY5D3Z@M_Gn)#xiY=c{Aukmv1R6WK7Te>Pt;w(?ZvQvaa@#_d|0Ah(%SGLDt> z)bQlRzC69m8EaVZ5uhb9m~@;=EHD<%Qxi&EQv3DT7_>@Am)*TeRf10XJ^)h%kNf8h zh&%vd?x32Uca6+i>1w8bf6qP@By!6n&JQ+m4>JOPpDn`tig=4`t_ z9%FSNDZVTWN`9D@gLbF@@dLNq;yYY4V(7-7h=IEr5fIn=iDm0lfxz>0q6WiVgVuh@ z;>)XW7(?eR^Z3d1zXzA+J6wT-7&W1#$Fbn|QP|k{iN^b@H>8b77r+2$OGRfyPTIF> zmcL5=yq+~xT0&Qir02u?A#LzVr|WE}vM`obQbs5Ax}=_-g@D-_#=e3K*^3P0`TBy8 z1Mb-<8r-%r5a0hDq5sYIf5Fw{2;b-}HS)F|SSp$^cX#;0$Ipkw4?r=FZh)gVJxt+r z4xF3zu!Zkag!bj>O+R;;Oq5g?(`x_P`LO-w{)1OP?mDWhARbC(XUIfq%kj4cG#)(6<@X%_}iL6{N?W&)-`Sc#q{713Jx( zXgs0Y6dL2KIEfmKTT`g~$HlH8mU)QPO3aN67_!JzFDnn(9a#~Wde9OS2nF{F;-bVa zNA7CSlw!6sWXVX_p2(?@D{gU`kjX=o98BlE>~fSR$36V>!f{f1A98%ZLE6CSq|rL_ zYcXqSFmg)5it+@3+Z=+!u+>co^fh7ob(`1IboI`JwConHb`Xuu;iHbZDeuUa7fA_=Qhs!Xe_ z-0T#^R^XP7I{Qdosfw2Enmee7OU*`6;is9P00Fll`B_jkRQ3S0?Ao&R)p`H@pk$&# z&mcsZQ%N-3+~n$R#;c(MCBwq2J$J%=+4S*PojyQBhj2B$`kykB%Mh)+EZ-}J!R6l0 z^ld|sCc9i!%t2AFhM1o9a8>FmVH{^K&XFlc2n$%{zvvr9d}kRl;tr2!o5XP3# z^Dk6;^XsI&?tzQ*-`YqsUY0d3Rgp^gD+`oKY8QZ&yo(3`9s0LV%0TkEg2$rWXarTz zF~L{Pq%Ab)BlAL!Z;4lJ#>mOuQvJ#Fzb7K{?;?`tEUw}!UJ`y*H2Cs^>*uxb9j$lb z1Bt5Ck#Mu-GY1Mb)^$}>F49UvQ+z#Y_&f!$ZzdEU^+=Upxs;DDEH|~%$$~X{8?wn- zV)5H#e(iQ)gvs}MR)em~l|nG2N|E8eMdaUn|CfkFbfq%O)dDuUT*M}lM<*RDoOATU zjdN=Bj_vGc83IThn{Uq%)-OW@oD#cse@0GJJmV0<~-^^TyUq}!8)`C+R8e+ zTa#sJjjb3$rR)SygYfcnoV$&u(LVC|5Rr;pYTF4xxe^BXR~u{0jSl(J)(CjORsYgs z%Pikklzk%|+d$tSP%LD-Ev-UU%Evh7NRc0CGb)=y+O|_B_^;FyArR82Tem3h!0_ECsR}z2dAg zdDr^glPWlsY9I2Q{vJu>6?$M%a&=$?{u1jZ&Lzw<1_z*7^^G^(KZSI?Hw0z@o*&`X zOm>V51csCb2v#vc7YaGpO=E#To*I1?DCNt`$jbGV0U4Mylkj zDoq8A`qQNZd^2p^+ZPue!L-tkbut!&@Z207n^Z6~Y9>Xzv(C-ja2lPdrrVjh{HKWfn7&7`TAy%?T~N44AW7c%88o<`t?Oxbie4mFO@a# zjG7{ML(9za5fMuvs5)H)Yc~1RdjQ;fILW<8eg;rtUTa zxC3_TV$GmN*;JA2t&90j#rL~%&1sYTzh;)M(LSZh$Y6a?g7|b{<`D5w9@)75?&aQA z+GRIuVQIGiJEcd#N$I{=Bd#NjVaLl%8iE*v&~{Fs$56`EVK-UR5vE88;L8juy3r?P z=-FUpa3R|XXS3Lie48tR^kn!hiWv7To*%#Qqg5E`lj(m?M2O!-q^yyLKU?2`={0rU z16mq|#|dZw|EAykvdXoddC+tGS^Q1L#J6s4sSUk5xC!T+83>!+(C>w6u%w|@l zA?+?wF-A33y%?7U{?vjPo_x|w)CI!C$0?Up3%9ceQmOVe?SeJTrx-<8;gkc3%xsQ! z54@E~LSOgTHfFk=ZquZ3I3-oDkHrf}b1f9|Qs&FSpZE}wcu7$dN zBO?|V6$^j)$?~~4h%F1V);aI|h=*_aEO}%Mz)of$fZbzKOCb~`t z5?LqIU#A)bj?#mK4VM|GveG4)lE0*l$6MXo;DK^pb;{f77Hd7SjNVq5?W4VQfp!?3 z8Wj^e8{fxONp~VbIW^g)c4`uo9kFEF`q`)PR?o-!0z5)`CjhECmj-P;r1zmNcq*Hy zr9U^u6UkSqD9s_{$xAS3+xB9S_|VEGM>pb{gR#%rElP=)R~e_Xe~L@`WNJK6)pdCJmJgBw#_P7 zswO6#J2k!1$^`2fUxgfO&lbNW$eFr6Pmfs(v9lRbfZyRgR=nOC5O)=i6hJ|c6Zkc~ z^eO#cpm{);IFLtg{br>KzoHv{Qig}bC$pJxY*&aEG^F2AI3f?(rs<9|lg%xw#+csEh6|re|8pWj zbpI|Q)n*yBIV88T7&on$E?G8s?yN8o5cq>JjWvIm(ALX&-hx}}xyY^9Ja+a5h@w#! z!H)e$gI@^&>gc7?7^67ZnPSRaQO3zB>~4Ej?&Yaqy)QgXUKPVtrOkiJ7Mo}W|NTOeTis!&Hai z6GH`tZTNC$$xHye0GW06yf1tlAOwE_{gnoKo?xBIe}MY+{FR`QQq}m341nBwT;Tn# zAcp#o71~SC`<0>WA9I z_)YE9m2YTKYs)&2%&ZIsEKLgU&>JzmsfMcYD+FR#Aa%H8!V1q1ucebCXTX6$&2R(U z^!dTC`Mvcl@zx#U=7|x;Z3*YueEtV4%k5zMSZN02w@s;;HN zH0AaI)z_N5ILY=7U~x9f_|_Jf+ihVlu5b}BsIOr-YC}`*58C8H;=<+ ztCF={znC3Z(5xI|wkh%MsI2{`qov0f*r30PE5!-b^G_7-M@XNcMkve$fU4MDdbcWOGPAV*;%Cb z;s@QB`BVS?`O$it*!E7W1cp#*F5N`?&bmemT`)LFi0b5?T^xY6-gcrxRNANvH!em+&O;N&UZtl zHOtV={c7Vy^z+N6P2#~XtN?OdLPu+VZrB-ov-anfMe<^VUlX92M98FzO|>=Xkh!1 z7r4Sg7K@QmMXFF2JYf!EfGYthWz!)*!KBk3Uc^`q@0lBQribN9Al&BlwLRCLP|0X( z;|rajm?17PQx=%r!GN2asOiNTNnyVb>fLx%j>q&5hkmpp3e?Ks?3ARR2GGo?Jk%A$Mt^su&3Wyt8Z5 zB=uJ1O~Q&;tBMz=Yfi}gz3BVftdIZnam-)yOcq zT-!jr`(2vsE4yQC^J>`)^8Opt27WUb#y|~n$-r-4`&19-|i<>U#PZu zeH0!?#A=qTh7h&Sn)~=AipbHlEo|jEQmh}2ku$a@k&65owcUHq zYCnag@+d&>?Z#%lDt<6zfC+aZ%1-(nuJ{YBx0?O23aS{Gq47G3B*6}Ir6el)n;d+Vbt3Scy2vd(J`uZ~jigYE4Ah~TplMNlQ$bSmfk7g3%DgXAf?^a_x}`X)L~fdVi(C}gRg6nDgc-c1Gc7o|0?dbm_QaIVmHxIw z>1r~U9XetF;eHGfg$7Ai&>}JA4g^^;P|bXfhyl;|)mTQW0-fuS4Mc3*MoT$+rmVM^ z`X-#_1FjZt?z780>uK>V20F9z{qXP8vi@-)-f^3F#L#{~ z4pp}=W2Gw3@053SKq=)uw`{Sx5QZ)0{0wYWK~E6G(BcMJ8lay)(9;7hFH{#TAJnxdCqM`TcZD%KMZ-`_>qf;~$|;&9r^< z&41VfpE?=bW8uKxsU|k*5sQPnpFJV=I{M)PBzGIGEB31-*LBX`~d1 z5;X53rR8oFa@eixOj`5~SAZGcK_n>p{UtOEgmy2%D`q*+!XyC2zJG@sxzzf+5^$W| zu7#8)W`u^$+^AvSls{VUA;a^FBKo90ognO-i8?5MX}a<&WGygriKC2 zw#=Q0Ih9>T^3tFf)Kmxn;nWLV_Y*NJxqDy1%wp)()6j(FGJfhQ{JF`5ctQ~UCB)vG z{i{yQyb(#KH2e&>P{nFsOFfB;x# z-H_UO%Y;`Yhw?*AYGR&34CPnkE-L}nFnAwtrlbpnIR{@@O|1QvE!AQoX5<$&vV;v-_z$^Qh>e^4U-V|WFftRa@+ljYX zYxc)$@xsDi6ML4!Z^T|Y@CGZ=d?84Plp+fz5rx4ii0yoqhfW`G4Vy!ytQU<2|9JdtWp3kI1Du`2&vtFCI-ULV2n?vmyEFXJtUJGaY;o z4S;&WU~ShsTwTWKI-iJvj=VQI5hgIWbS`(_oC&buD{<3h454z~g&RQ_#qs{!m-XL+ z3-ukYfK)r;#0VPn!qK101yU^#l-l`2>3Kyb?2P42q z18V-B*zrdqMH!sCQH$n-cv@V+;;r>BSKXmmQ9xX0iriRIa0o}nXbFs^y1Fx0`5Uf( z^Zj3N`TAkdGzzcy5nYAIns1ZB>+_(HnI zq8aPLZJZ}Wk3{mP*&CuK%!;`fVn@B&ZdYm26DehG-VrSN@EO|v*DEq}kU@q*Q_vDM zZO)>B)|L*N_0z{YnshV*f~ss?ATd)AynSC2h73Oo2_pr&BS)LDY#%($1g?SUhYhw= z{c|Rt=S}sSjQja+2-|rt~96g?zZ|KwMnQ1*3 zdmc;Kh9!1Y@x9uX1|xP1x3{?Q(OF&TR^uHQE`4be1yR9!%eZdfY!JRWO&61-ic*#^ zi+C6Dcaj(m@dmMzUpRazS|Ccj3LJfzj<=MbJtlt5D1&or{Ix(;_yEQ7vbSYS~wp* zN$;aRkPs+|aw{{ehRf=h9Bo@-NKQMnOF*p`IDh+2vA=+|EBZAV?<+9~K}J2NSZbe^CJ>c6WK|K|I@;EL+B z*#@mkz5wisaNZT;5Ni$E6hw2Z(VowfRv{$EUKEODpL&Zwvyv4M;6@rsnM2faB|>w@ z>(lk}u>~mpvx)Fsg`5fqZ1h_4+La_IUuyzgw51d}Nrk0_d0l?T4gI4!Q8^MYc0cD4 zq~7LdHdUvuK>icP4L>enRop}-SD^m*w)@9U1vhadB#@O}2X#ahay+D|)`&DqIlQFr zkBsHAX;UT#&wYEp%!C??bPEXZ7Z1PhQw@q8VlLxsN>{hJ#-kL+W`(DC_H z&+snMXf**bJH94!%Y-(dRPCRur<(2|mMW(UM!v}oNEG8`p7A0Tbp3`odvMG0Z5!)x_Ym#t7GiAY{|L8d;Xqc z(D}Mx<0Fwb?r=NIt#H-Bt! zr-jb#NB4Yr$pliS4##t>x3PYy)MtQRpkUTv{XLJZ$2aPA%brvCg7BjNZ*<^A$wQw7 z`9KD1&{Y+uGY-t1%KY18ekEzWDt=zsnx9o{6`Xczr)f`OG|w0ZZNL4kprtDeb0~(&0`SC4gVPdF zR6D1N5IEykgC0R#fZ%p^EQ}eh8uxB;gSVT$^!OU*z#BiMUkkmFLZ!pe?42}Ty$Ogy z=+WW;A2>2Jpnp^+h)J|NDMJH=;b_E`29?Kz?q`}B>kmGD?zl(@5GrHC%W!V3@b&`B+4XX_20H(M;VSH=Ykz;?&!2OxK9C6NO^C zF8*3A2em9&OGu!JHn{jDOhXP6qfr7{J%L14n(dCF!o(Wi_}EK&sN7CqCGz>OpWyjC zY^pz1931|R(EsN9zeFUO80_j~^-2ZMR14FfJSVFh6X4-h=tRa3RrbMc>^?uyI4~QN zpIApNIIjpM^g$DK;yWZ!S<-LfGl~+LMF*-h{inQT(Wz0NDDuvy9ko}a>PHnv3_x#D zruV-gr``7+p#xi-1WRap4~Q#+*9L}N>UL^5H(A9yszL4_J6zqI5Q~k&p==*^pMvyd z6EDk`+oNK`EH3BBARkq5ld);*#oVL=&&s~&Hu{^;BQK7?rNYuixH+lC1Ln!uT6;qP z62jLsO%;VsRhGcX=y`K7LA^1Fvur@vQqc3!cNSSU*@#wG(OiDR1Q7owF{xg&!Pw-w zzwCAq=t#PQ8Q~bfJw#7+VorGH`G5lV(%mzvB$+i*?D|JGh!dWv8tt{wG{Vtd;r`m zjeW{9YV_F2s|f0;l7$eeTpGFfu&H~E7NHhc64Hu$h-rw?y-^N6BDA-3caPeVhP?2w zWan%1`?~eYo_XKi0!SOF;#XWKGF=Q};D4mQp5YR9KtGQ_Fyi6naet6=AP(W0XJ=h| z>>h8LhNa_+yu-zgg|7JtE;l28Ls9-vFN{>)sjLfN!UFhx-LTMT4M5Q}Sj4Ezl~1Ps zJ-8y@;R8!a4?Xpv)GE1D)up!f2?2V5wp%#Mge z5e+0n5y?L~3kj$@WCwW1s#K$NME{JeTGRpXzSZ8>y;i!M9q;vbM?<=T+$zP_fPlZ@ z`ZwSI1y^xd4&t}83!45|Q?djzNQqS87aEC^BOAGJ3X2;8>-G}6?PO4x1CP9_)(blctc#5~Sn3$kpYgzM5- zKU6=Hll26&RGG>QJ$haI<&#J4o6>W*zTP7eJNdg_@vewNOf|iN)3KI4nvQ8q3Dz00 zcmUN=i#COeThquzU}bUM_C}zVl;*?A)kwTaXT9RyE0cQgp^c+`c{TiYptR#knX9+1 zYz&gY`5{QvfW#fX+~wbzJFmm#@lt2Gi-MTm5*NQd;F6bzYaDsKp}m!9N=G+qm8L~1 z{qjKX*jTOG0Cd^)gn&W(&-Ax+O&<3y5?9~EXAHo9Fh2VzGfUg43=hK&8n$y+uW9}r zE*KzmjZefZRAH9$2r9Wn%d(cr5VXkRhHq(nKSxg8te2?2chDgGWcuHO%itZZDm1@j zkBzL{ZZ3A(CSn$a$>0H0rzQL&AO6QMt6h*uBnvVHM+3xLO$o7Hqk)=xhbtkNY+69C z_(ge&+w;Y1gv46LS+7%72mlR#}sdR{O_|@!e`!{p?pH+*T zP_yN>(w>AqD!NPH351X+JmvrjTj1orV@3xFrsp)m90SQEG|qwq4~M>$IJ+JC^ijHj z0L>}+1G4=btt?SU>CCGUbS%9@(6R(ZgAYZsj3a7#(UAeu@_mPffBaiJcfyH2`Ef$kh%?ox+s#m6HQ%N z@0#425O4b`)DJap1BWaE1Wm%z0F$y6e!wN5Lw?gmTO+dv>Vq z->03og{5$%nYT(L?#BY-!~Fal1pW>mY;zAQ`^ogb z2kh5(VDs*8f5YE4pB%2TyfVA94S=W8->{R5$=saQ%oyP`6d8e~1s#*^Pj4Igvjt-s zcp)zzW6m?(Av6$=g6}fp#IYOxg5A(I*#IglrZo0<&9;y^)@ZS-o-@_lk-Z-j=KcG; z`fp$KFJSYJ&=!)AcjZWL0-CYp#8185>YKp|fz*2wd#FsUY>nu6esaKCLrhfrjW1g2 zuHGY}Mq<{1a(XaebQ!hTdtBh}iy{@cei^)jNwRCOvAaP)#wu<}GBcHgbB&#Xy#L?g z`ljv-Tb<}cSZY8uPm5Q(tB$uZj+iA;`uN@H#Dlzt$mSE;kz&4==?BjV6v!ij?Fv@_ zRih;mB*!m4>594G@4a+<;ARKsoQfd)RYBm>T71ZrPLi#j$0JS>xCE1Rn8-;zyw^ca zZCVobLCf*qGUsk3rD!%!C8=4_)|t9C*oC~Ra~@w^E-F33kr?q|l6{UNMv@erwotth zAZn^$sMoQlf^Wsh~875rEO3mK_qEGDkN5czo);IeV{6v0X#W^*jX$s+! zYhj1mnG~UmM!M=oYIV}Mnf!&Ter;PK*C>7=%JTEdh^fTkwNzo1POk5SH?`QH9GQe) zuao^s!Z-T>e50hfjud0~5qr2)ZIYZqe2|u4iw0ioVhrIf2T3RO>!mw{+^QR9gb}72 zYFS(&JIXy{?eV-wgFMx#&=GI@)$-~*H+HsJXSo;c?Tcxz=QDlwHyD$kwB{{yXC+i| zX^v+>2~~o)#Q`Dqy?MAfW`A3w9ac3xA45FEH8F6)iL)Oezh6jGHSAY3>**NQgL069 zeh-qt1chfCmTb_K>^nT=$rQ~*1NY~JNx5bs`!M#D?EgGRrzDt|`9=1U5{Wj3%V@F` z&{&z2{q(Kv$L17hY^Bz(r;qch(7~AJVGc}0(=|R*)>oUcOE9W?eZ7h z|F@P)Sr;gHNNqV9wxc=EFV%6D)`BiR;CLN_8yrY(tUjOjW2f)u^D6587z8fA6{Cm7 z_8<&5a+Jw_uu~f^P>|bf!*47PwXJs%DNaXM`=kud^7FTxW#kFW_=opg4+uvH zp)A-eZIyyaEsp9uMVH%T2#^X4PGaFHmwiNjfI_cNYCGa}VSHN;{yhYjG{dQYwJsZz zipF7#LX=|CJ>(n;m{4VkTLez@*M#Qo3ncCF;u4?-$|IpkdR?!*mJaL^g`EeeQ$LjH z-+0@#b`ZU%702TZsqW68Dl?F1j3LZHKu^VWaSli0(=lr=C9f-I(vgYP_`liu(L{8{ z0t-d6-n)48g)HPGcws>MJsU(QQ+IP(eAmT+1b#8+(Rs*SH?_DbM`&HQeD6`3@2qDs z!2*P+%)ROP{k)UO+f|d7W?{n&%}F&%=VSAr1wMprL@T9rOro9Wbs->Iz2iv~a_{gF z^^-#zSFM~d+dao)B9WAj+rx`rSjwY@6>En>#mKkpBtNFteBK9_zf)D}3SIoi0-ei&{*VmMy^VtcylFqu0{us7 z^HrGCD`f0NgB!2JJx5{V-4s4)5Ok-lofy3(cAid#zb#Mx&*RQ-0LX9qXn!AV|Lrrq zbNIjICUu1IWi7|W6<7X3id%=QQ~b4aF}$|j+bxedLxxJYvC#yr5(n+s`GS=p#19K% zWk7u3QMHz8X2fU)Eq`uhu<*QK`*sTb*y`R`&q}yK^ZLVHeadJ9W(b(}RZF8QVd{>e@h#O&IL&C&cIrR z{8e)5={3Yu1;BcKKtGRc`?jAP%uNmSm1>oVh4Q#6X%Nnj5Tr~yk`Ch>U|ucR!2r8W zL_M8+HKcSTi8a|W{tErE@i70$S{TAlXI%*Z5yzI)K z4jv5VX2YkkIki-b-#g3y5#-u^L+xN$bK_nigxW!gU6g9io=h#JV;KOSfeOEp$@#v+ zB`J@t@(C`ZYrZ<_)&lI^Mue{l&Nf~H&f@^9fhtRLP6~j%Jc*wp=zkBcig&p3`zxdc z+WC({xtKJis3TS~fh&fm^DCEuo|V)@5f$_VEhsB+H?TLb7nNs|>tytx=v{%8^>ZOa zMSl|+mg|T~BbMnX;AZ}5qWdv;#!%)PfHPL0N^~k{NZ!Kn1h!om>jsI}+nw9ACRlCI{s?lfD3yx-rESON zNZmn9`=We8osE(s*HTtvM+c{>_R}s=I?&)P8sMJG-&S$Gyf!Oplp24I_E23uaJoyl zql?6s!)hp)L9o=yjQ$3{^XnRD%W=L%QEwDtk^I>Lxo!qgyz?g<#5(2H_JxoTyG$S= zftZXb-o03EX0=%5RYu>G)5wfd4{Iz_U#=IJE|4X(%}6iw8?3hJiCJBoMg7CN5x@W{&jUku_cZSlqcltoZ$9A4IggmVQI5~zF5ebdOei`5{^Q}Mj+7v;EE{t= zv&;^Nf&5Rn2J{lMke7062oC4&s)bZDVeecN3UkgxXCiz$D!)Zzzr%&ghOYbxE^0h$ zdn{_jn=+6d9c1c1t&-SWCz{bY;?3+8o#Uee;?(CTVbq0Br2pi0+}_L zdqHzeE`G4K4A52RX3YFUc6jA|&qj&Z0DMw>FBZTF1fXr!Zy@}C^Zj3NmDAl5Mu@_C zrQ}Uq>XY$0iT-%nY0g)nS!o*&#O(?;9f~+S_Lp3-+o58Be#J(a0Iee{Kg6|SvZ;EB zi%B5|h^IOVDv!WbN?DV^)ONa6kpO7n0BcA`i%!?6>GB|b_$Zqet+i|~tD7KwLjtr9 z5R#DQ*)N{e^)_@#Ph)x9;p>F3^Z&CHm}#ejDGdUy_j~MFECb1L0&0M? zb|cYu3SNqza#gPdN-H9&Z#Pog2BTjJ*sY)q;5s3V>U%yzQ9;~F)uEPO__LrUeSX9> zc89pSPmt_R!jHR-#V6jn{1RawGoshFGG1XJ+kOJ~N4J;_)PNtVC46%a!Y+SaUyBZ% zog?Jp6$gN>c$ky8Ep+I*~G^nxIraj7s72?>v;epMDc- zG;JAyP>0o&Ro5JDKo3d%3-}{W4V!Xwg;wp>dV$~-*jRSo^IvddQK|HqEhAT91xbWE zK)K0XJ;D}EnUSpP-k+2^LBvnm%~Qq^;|E)X+TIpUn4rZr*y5}58X|s|`kKXz3}W>Z z&PDSNH~o+AFFfj%M+BkS z7fe_rog@EA5YAjqvk4mWMPI?#|2|ehnrO4TycIE z$ExK{uubSC_q6WDXnRH^%c=OI0*DaDCKTg_u_~YBzc0Q1?KAzQHi;A&_#pVW)iG|R zMNmqu62BQn3OY#6+bK4)10SW+fiXO2Pz0fPd{EP6J z?(92SCq(?cWmqXOvTnJ~*Z#TB`&Sblhyc7jV$Mu7Z4=4W(M=s9WHM$f(@%u- zt~Xup9d(%W2kf%6ON8ah5M}`1?=`oRx`a!+ho+P+&tV3d^- z;UuO}tM7}}CMH6D_~FAu!s^7t>R8~PbO~?a=^D>uW$xz~c|m3CGb<$+_03YCW$P7{ z^rC(R4pZ?c;j3qmx0j`CTET_Se)a9+1MY1PYGAh$#CwDa3Eyq4amA+|=b0x$2vvgp z-=$Yk7SkV1eG2Y|xEMt+`7A3`r-bO#^%bFkVbVU#B0PKH%fm1_K0NYtbEU(z^$xh(EO(7 zJ;<5^^)4cvC+LcwL`1amvR?#p29yj_fo5heAjXst82nk@HgC!{6R51H?(@>o|DK4D zy^BadC(vDfeTg65W2=A~PmTGgfv-yY%Ui;}^uo6?+tta`l;NP`Zunnk>#%gU3$y57 z>V$vp!JOZdPzW07pS;4VvG^+ZcWF=HEY{6c3AmmiA-`6VsY9q|;K)i*pN?Yxed+aY zzJC{y|Gln>^`kY5?G-Iajbin-28wfI(k3#f^I!Aa#itpQ8wsddg~!~M{6oKt(L75i z5sO!cX3}w&gn_kt`ppcX-yH8@+DNw~H&XJbQ~DRvUI(r6J{h(m^@*MjS+2KZgG+bk z`+1-$nJq`{Si4ZNVD33_Imq^xN|%9MQFQj>M#pQ4bzHz>*sTyjoBi32jF^or6)RS_^(hR$__o!C`mi`=Mj8^+v3!jxe@04hxI^_J;V=RhfRDM<4eT5vz{EqfE)BrWX;*cot)Vfw6rI zMw2$%c>jWxva<1~qLAEdJ1_f~V3qj{yq-K)X#mG7SEkNf*%tIW`Th>vsz6S6z|-#R zeeXBxLemOHKWAeT-R$U?<1Xqh2>c?Dt-4%q)?~(nT&s`sfU3;mROTe+QaIm23Om1q zxf2vBq>o&zD{?W(40U18dypf3fio`rT17p z7l>nhq6&+&a{3(D&C*N9?2vV$Izp(h*gY5p#s-pIDFmfY0`Z;0m>G11Prs;nd#Yk{ zGXvuI|EN2s@I1F@ZO69Vn2pmUjcwaTW2dnj+qP}nZfvV@8aw^J?tia!W$lw5_k6r3 z+wK8e;4}KaY*>|n?nx}mMR0$dmUK* zwHbtNsfSev9p`#Ze$Y%MT^+)4X@Q$rz%l-iQ^f$cBi0Vv9K@=jKhlDL)>Yv zP^5~IDPaD=c{KW?tM;YP+(OWymmA*Cxx`8p$58}c-hWN6KmDeEig{21Q$p_&9>?65 z29FmpNPVd5{Gh({&HIt~dZ|P%=McYhdM__$4)yS#!N3OMY(GFxHju}HPK)Pn`|9Y^ zGaiZHeyph+d#ZS=ehFr5L)baK-0uY&y0ycG!DY-(azVi`pjW+9j5xWXQTW z5VJ#Ai781S4kS(r)0*DbG^Y%VY@&X2xCd!79sBzJ=x!`8`o;xJ`!_S21KF536fy?# z=Pp}U#Q}o$+h8$rQ?J?I9IEi8Q~YA6YhSzdSIxXzDB47VJQ}#SFkp=X)*EQ7WBh$# z@x3qQQQcq_1TQKgw(gZx>J|?}C}Cq%X4RMy=85F5gn8ZzfJ;9c>;2l=#zSRHgD@hw z{K(}jU>|0?5w^6brIrq@Dr2%xU!!~NuZC*^09QcinD%x{YJ2a)bL7)x{u`~+dnlFgn~o?T*o$RU9bVTsPe88lWB zKyAD2=O14xGGmhYU!V9A(bi>J5>0{GYaJfmxNnYARiAA1>7{yZt9KSOmIj-L`L;DR zs`0N{LT%jY*az?|aH{E*TOqgAK0U)NlKwK5=FWcEy|!G$Z02<9+%>%5_2Ndswl}}) zxWIUj?iFUnV+oB$!6i!9P|XGArV^(;stYLX#@ebQK*}*sr&&h0*f6{q+G0iYcBA;w z>78={`5c(?8!pFl0<`KRK>@h$0fnV79w8W}_`}$9cBhYb_Ojciq2m7?mfejJs{d79 zE}ydjF66!fREulyJJ4DSavNF6Sd|$W+dTj+mLM#-cf53Q$9lT(@hcZ=7;G%VcE{z7 ziafOI`Xc0UN}!jfE$zJv{rl=xHUO<+oQ;0bDIE(hcSXNvp~**1!#1;P)Gyi({+QVj zx<)dLenBD0b8xFKI5LnXD>KnZiKQrBFV51O{8mwmki7fR)kKtJ8p+@l(^p2PUFaMi zHPX3~&}8;f6pc^TcOixTty_P}{Xb|`j*Z=i8yVOe`6Ah&ZsqkM9!6t%i3}_#XWpC| zWq`-GJiK6_;-QLKz>lic@!{JeGG^FfUOVQD@U2cVN7vRy*1#Vr1)|?OyCgbnC=zlF zaQoDpdrU(ynJg!6@WRvp(8~P<6yB}BMV?}mR55>usqJHHT|Tb z=-D52rW+5LOZ;$}BjAqwg%Pt=n*9+5a=on8t$*i%`ol+SUTq*c?m=K{%!GdPqn}OR z?hrTtb<2dkXGb5cDxN@OK*dl?NeHcIFv);7sQ^B;Bms+TTZl3r;*{mwLS{$&RoG9e z0;)~dEEcG@p;NP9*~$Hu2$l$>cejrZat$()6z45Dzlk9c&cUxIC9}r*O$%`|{x@3R zo)i)fqM!V>*J2lEBoh=PHBQrHg*4q;AUuUlm|ul|Hza&tw}g3G@A3j`k%6Y>6E5f_ zhBUp_O;_|3c|+}*2J60lk_W&AX^r*%u2NVU*Jo^jI>tVFqef(=nLQq~H^I z3N`#A3Wv!NRnrTpg#61SBxGu=SxbF&eC$Vs5?}veP`Zy0e?;p2opl6sw#g_ltGT`+}+skY&}Vx!#?n>q)H^{vTDiVGFyI zB>3BE(NY9pw9EWqyQD)rA1pED!W*gsIN8<@qfgZAP8AlY{VMORcUnh#@;s?T$Ld&g zjILq7u5zvXpvtX_(5=kl;TJFEoJ9|$!?Dq*aZbd>o7A$4@gL}as$hC)l+uM53L`D| ztb%RjfZM1^$(I%H5s-u8Fqb8S1hqH-qP6z=RrDzxjN^P!1$}&NK>-00!m{jHYcOo+ zhLWKb_))erf+9`+B@?=B5~3;2HJAKg$Sfq!Rv`TsV`O^FBaN+f_ChHmz=Rmzz}U=MnogHtvdI4E!EH~@cH}Xkv?;y4 zrbq=T>K+^HsA3EnLWy_$T8t8zvrH&NCebex$!zL_`205_~<&p+T}Fe_fZ0T0$1!P8;@3cs3J8yzfv?LZb)HN_07+0*J>j}*)iExBW zEOqW5Y(ZmE0MOguXvk4jegP9uY#X)T^Uks;3Q1GSP$3x*uSiD*3t z&rFk#e{}$!0SYlIgKKi^1o`s$^^AmMmChHH*|H@+WjRIhE}(|nA9w7oh=~I^4)83I zCEdb%rd6tBQbOf2E_pXyODDN-X!kGh9~tR84%-!RH&B*%?xJ)QR!sEZCD~oDi5F!>2I639bEv>U*n6G8aaFZ)l8o`bk}C2oZa`W$xus+ z@l6F3Y^47#^9>w~2}Q0YrShu@-OZ1O0%m)u%McnV=Tbhk%k|+^rSn;OIdV>1qUg~Ubl^5PFRXSRCp@)uFtt?L)@B4s3F5Rt;VP8G1@EJShOjrH4@0Gi_Ycz>&6p z@T?K>SQdBmdBgqOPknHP<{RE=`~yx^0Zb|sWBa-1XLZ2_R}&tbE4~*0ioyqzoP_9d z+p*1dP6#E~&d6_>R~c6}>fCSsst}+t1Cl`|r?-AD#Smssz{btRgz9F;YLch?v8@zg z*dC>CU13TIeEtQp zTgaws?9S1T?%AEXUAvbOgjCWiKZVdwfx{zcJTpM|(UN!9rVRK3c85vA(+6y}MVL6L zxFE6$!iGGGkw|MxPV3878V6m)aeU{v;`#pMnvS?yG}f(~f3#se<)X51L?+WxDIT`i4kLSADQ@+ zUe~o?L~+-UoS95M;i(`Pz@ZNbmejj10-e?$y*#7YB(Te-WypAJz5+uSePq%OKwV^Y zZMKHGe;4}KarogBz+vbjP24pZsaK+(tMW$;59AIZh5EFNB#^C7A$H?Mj=NLVH)g*O zqr_Eow&M$5CvPx2mp^KtNb*{*h7^#s1n{( zq?roL0Hcg`+4ApV9#BFg@@N$lmKd>{xj2lICZ2qbI0ALWzn7rEc)%Ij)7?*MfMokd z(yr8Wj_jZ=7QueK@4QwzX(kkn4s*=NzIRt5aDgKgfPoJ;^tcdwe}!c_?#H?)%z1Fv z{B#jA3+56mZovJ_<{|%)KF-UHktL?F5z&J{-(;J@J&|tH6L$(IfatEXJ2GY3p>5}F z)&J~mq;|&OrQ5yRk_!pAVW!swb9idM?yIv6KKK^YRT{BOqiGfg(2XH&Kf%WoU7vYA zUKA)U8}qqJ`A6ms9A@P0{)!ok-q_Khv*e_6^&yWdK3G>9w}3poQ-eFC_NeJwp7c?` z_B37i-gm#f*+p$GvHS?n9rk47lf2Q~#cjpP)1X zvn2(4RS0S&VPkowi5%B6}Q(OP;$kAJL@8v$%*~p zEKelUMZ-uDh!;#R%icvRH)3xXhLq-C$L>$L2S~{OT(7R6WTlTiPrUoiT4K0aKrP#P zF$@@xsYtVj#!pv%Dy4dV-ww3{Kl~=)8iRV9EsG5Pnb0Ee;aDPm{#Fn-Hww5nF3|TC zd}Pg)1%6~(UM(vh!qrSJd^NO+l>=-n{RL2@y2DwRd5rZy32_#d#i*X6B= zeqr5Pz}s0|8}{n0tJkZ>=hr|i5#fFcAu1ha0Dtpx6X1I(7HRNn=vFVM2ZaF2q#8wJ z_SED;dnmz)SXh)iEWpbKOgGW_S_+GADHki?52LE53YO&yonaWxJn~WpvdZixrRt#h z;%lNw3WUV`G3@{-G~o6goi7ACM8fkfE_D_bG=i|wR2qn(-RW&y{yLoo$OBOs3{B^XsKM$XWQQ4mCUI0q zA%~Q74G|SgewT6@JJUJTn6Fz`c2evtpg;$BgwLB@(B`yOOp7GFjpc^_ce9z^@tmM&vxsv6%PC;xqL#RA}}d8)72GCu7(gBOYN!1SlV_NEg~IU$e|rM0o( z8<5?j2?r6OJl(`RHR;;~YeBHXoa5Aj+QOKqto(9>Y6=}V=tzEDIEH+}d5`|&NGzz} z;8Xp0aXLj!5%Wj09GBLU#~@HVzl1BRvqdrH$BkFn&#A=$Y#`Q3dnMRtxLucn+z$q0|& z7O!%Br(#eNN;`;XRWNx>4Y;Is6DO$Bnsi8gFJ}Zi!qR>2m?^(JY~VWlC(bU+d@Y4q zNp=$zsGibP&`-O7)qU0?txp(Qy0*7f+xK70*5y?b)sL2a&K|Adl?0Y~5ju9jFCucj zSSMb~?w{#Dj=11jP8M2vX65#xGkK(PdV|?xN(Yv59X*cSziEBSy zW4E;5x4J=sV|iz41VD%_lL+cDm&IHQS>Bk1GUz?eUE*LKqhb2RUVY@l9e-M{`3=`< z0G_lYWLYWA{S)0|LqWmfG$&mu*f(V$#gfXJQcG6A_3HoF;SpU_yHw%aQ0|6ty{Y_y52Z@-Y?9E$X!!Gq*xl3ykBl5d*8GIje8T=b1?b*>Ls?k53?< zqWOp1J!iEfWr?q>F{aj!>W)17)4?*3iB9}F3_dFq@Bn$)Z(^~J^0XBgg;gvBv93NtN|Wo(C@GANv~=k`FbsaBaU|g1;B0K~`xzf{ z5wQf8<%P!4(voM>$IWvQu?7O+V_DCmPqeXw&WO&erchsf*1g)FVcdi2qDK)Q!*(_{ z)S;6K`ojZJ?e48+vMtB3){JZDcsPoXS#)JFuhMC&^u=)J7E|JF=m*87e=08>sB#`_ z?Mm7x3awJJ?IJ|V3@W8~k!{+@_ESvbaf_&ck=u+zW&|mj?zEa-H2IEfgI+FF)DyAb zM7*TZmvu>0k_saKN-43$(Iw;nCOUe#tB&c^C^uXtIvU;NjKy#n32yq}`qNsp)cjO| zgV((V&O~sy%7K0Zo$|T`V)-%sht#<5G@9Piz>y0rKR1D{@y$nh!M!?!5(+U^VQh6n zMSf8bEGaWN2iysNBWSqz=&tIP3vPMvmDX#M>y%idvqGUvJz`??+u+dnqt4QB*vD$E zLf!VBEXgKm4{OXWf`Wh$v~K(K_QV~VJxBISt;w9e41gVOgTm(HcnxSGnUHnBNxT&% zo5{k3_Qh<_tS=tc=M;niS#^EI7bV;p`%+|zu&o=ul@gik#mF4U4SW9v(Y29q{4sk| z|LsHE%V)Q*f~{vj+mmx2WOGD}?U)q;l4c}+FMe@EtnE>}Sp?i`#FJUw;MZ&R@<-j= zy`Hb1gWL$6AL0LZ@JyKX$lH z!_TZvgxb2`g->K8JUVR~=!2QIcd=}QwPM%S98}f-5g97L5__i%?oKtkC{vo>wH$)P z4N)2Qr65CEXz{Cx9|H&_j#@7p--Z5tBEk+3k(|b*%L_a{**=S&vPVhugZx{e|CIZGL?k|oHP#;bn1*)7!se4`umrw7DHi?t z17B9X(&rlLuH2cNKnS6?p>67)+onYQmcV1;Vti=jbO* zf#ik_6K)hE1JKBdN%u2VO%_Q_w1x7P(SS41ao zgP%jF$TJ93qINRLRbYrX_I7FH2h|AD&$hRXnRMTnvbHs-Vd(S)W7gf^E>7`vJ55Qx z4==YEVsaGk_fzeD`^8BigN!I4v;R1)Z~E$$cfXDlAfxjWR{rATLs3Z&$V7=!@pxD_ z7t@DsoA}tNxiGWu=_w6g`h&_%KVDOOAiWS!pPdQv3^?1XJ=GM7#2wYKt<`>^q!H#h z**wJGEg1$At$k6`%d^UglhI&3v)tu31M@$p=mc^ER<8Gtg&hC17ZzYC9$V!1K>xay zhru^|Eab3N0Yv@fQXpF-&%r?PxS8Y=j!qmKzXJNRO3%hx-JD7^1PXyO4HyXrv-EJ6 z$=o`J)b9$t!9qxqJg9Xd)20=#RyKgw@wMirwh;;6a{g!E616V#|9wKQ3S&=3NzsPN zE)Bhb%pfPVrTE$Q;j1QE*0izSfcNUxwtbR@(FiG1EmyxYQ3rJfU%L6 zQ6eupg4;n5unH|{c^CTk!37I|s{*NlV!?rJZ5WZGy4cMJBXDF%l&^RQSRLMT==;DT z1(3LY8bOr3J#ss6yh43Y6AY?|Q)L#bp*#H5JR9iUn@QF&jX{o&YASSDlt86Km?cL7 z`lsDPGz)oYSd7qcmVa&HKjr=(xSEo3ZnM|IOIKV_5{`RfWTo z1leX;ECa}-$+x|7bzXN8Q?N|EIF+Vtn0l8ztbE3|S7@(j3gW-T11(OFl-*30tM9ji zLPBwSm!z5Af^2YJ*!gK4%dgi_HrJrh7NOX4Nb)n#g19*D zRaLu%Q%Vc1?#SJ?rpL)lIEO5{fz5@o+oQPBscYhcL5u!vq`{*TnxQBe`uxw<{Rk9nmDlar$h zurqyl(Z4#n8V}`sY9+)*E5z5Uq+96kMN*D$s(o)4909^otne8p4br9IwVd?(#!vwT z;B{g1Gw1oIWdtQvVmIn};EoKL%u+l@VY}km_IgnW9}p&u+5{aTJ*x4G7!zEC>M{^Q zISrZbFcN8|xr&Pf!mY!D3pHJU~{&gG@YydcnRP)3f zCVbT5KfH?9hHHF@Or8s!#tSJzmlq5vsWlfZQOfH+YekS0a&V`+EPeikYVAmk2rQl! zoamC5t{I@R6dbc$XJ$bRt*HbNq0=bpa@ns9Tw62?}f)d{U!j1|I^CC z|MO37C98Nx2FC1k3(T>q1w^!V>EaxQY`(^?Xf?U5$E7yt>rPN0_Q9DMa=B3=*vV%& z1pIkLbRq}%IRV<_Y-*aUW0*`_)0o{59;tIqJz1o8-t~P2M$swu#Me2By*cK@Z#NSkD%0>Td4f8COzw`9=?gwd7Q*K^F8Fdff9R zy{1ZsloRjC*f^;%f|Llp6$Y$0^-Rn+E8_gkFZ>kXvU)#36nbzD`@Tjz1bwptl3Q+Y zBv(8ho!aC{DZR_yuQ(-GmP`Ys)F#o$=7%R4Lja~uvuaIh)&*4^^tq%UW|D#TxkXIy zfcvPepEIDR+NV3+Jlh+zK=}M!VyvIN>oo>mrI-N6h zUyZ+&V-rI8?;BrF7JY`FWVgL1!|KE;B}TCL%0Z_Q*j%_=m9|_?B>RB7#Bf}ESiyisL3E7BY40`Hj^2(V9NGLS~E zH^f_5liIb%iczZEJ}}Zz#>n#L5n91DFA&EFB-x3|@qgEkf6DzoB`yr2L*08A2v{b$ z860aN-u)+^hu~Zs<;y8S7-WHiTR-Y{%Gq-?7?32eib1fz+BvFB)q_DAzCl@SftB|i z{1tcV-3NkWjB%$E;2CP<8yRSCE$Jo3qE^e#{nu_^#su@eI~B8#+0%Pt8K8}(nB z(g;U{XlN*^Dv)7on(`K6E9uS`%Ck;#k|jQuRGuu@MIwn&q8wLl%@$u5bBuwG7b zw8J=L6w(}jH=s&~=7I)`ag%~U#Z;A`zAO}XFzie@SF z5Me%p*wyBGx(`W4r4y_QuxOGnV0dahE!zc7&&mN2w%XEd=8dvE+fL$U&dLc@E;O(1|$3T*M>Rirrr;0b>VE ziEyk~ziAxml6AAemW#OlMoTdi*cjJSJnADgzUlICdwF^Xp)`F4x`$<+vdiLTEyn-7 zs9(FP``LWz(|PZkKZabq3WY#>rA<-YqXes?eA_Xy>ni{*L<%gScf^$SxI4zjc0*X| zosSN>ZsvUb&`P-7>xp7ud1jwN1IP0&^zVZ!0037@bOVU~k~^w4Fk$2((hqy>5I9MvfYw z0whLQa{5yjIfiI1NNURZUvF`bec+e=b$I@i`+wl_Yd+)vanweH{6RP9)H%MPMLIrN zd#7v>kvNb(dh7x&%k9;h@EyK=%qxl{c34jisa+%r>iko}Gyjl=?2!~Hr5riL9 zha)b}GUOIftO^cob% z+u2)@YS}nz=^p5`H@v*~L4rU;^dMk1*v*w&I5NUf*`!pp0%( zR(eQRH#yJm+r^DV=6Z>n*s;2Q-fvFtpX8$Ou>KZerV@+XNDiLvZa(!$*3!DySio(z z7rCAplGS5Ml`lZ|OBNL*f4jCWL#Y)C(2vnysi}n_ zo}j=B=Hk^lH$OsvNjGB(_q>v_Hu*CjlE3<|7N1oT^O-4?-gRVTa}Q^e!fe4Kzyo^! zn!DL7qio(sp&JAfBe5<$d|z&@IO+K&;Aya0GfJ;lOj9`7h2FLCut2yrM>EW4Kb*S^ zBIi+~tF3JkC9)4s$)H9)PRw70zp1GJO?L1YSh1yM)N#a~{_0Ld@EZ8-@Bo8Xi)Lzo ziMmqm1;nbANO2n))Q4-ns>!6S98kGXK>Y9V=2q-W28VJ{Vfs8)sygx_s!-Vqgxa7@ zdAEX&XK}PlYTYqKYuDxw`(t&bop7PKcqK4wBdusaA? z@0*F+aZ)Si1T>Hv9}nzlhM{Xpu*Ut=0V8-J7og)C!l)?zt3^Z=AR>k1)b>$)GR>Ln zFrFza-|_Ok%wsfjN4isufaC%bUCFGy1aiuLhb)ujpbwbXRuO`CVg%-bHa7YC!VUxu z3sQoDXBwzMZK`O=V%Fn9Quy^jFsenNHK5uy;Rko`3JcS}o6tYy9v~wB^Q5($D!-}Y z#a~jSkLtF{M>KyToZsRi1+}iKYw84Rt2BkF!;Blf-}KBIvu6oc2I35LyxAP*(FZc1p@+l{NVX?7jQY>?sY z0ckjP4oLS>BrGlWEyun!)hha19c*^~@J=(mK^}+ZIR~LD#CY1^8EPmYFv8PaH!61i zd|JDjAFr3HgcSj;%X&Ya*~h3z=7{`~ay7ktu3$pYOIP*Rjmc|yU@R{m{S8dOx!ZFEO$gqk*`b+aR9TJvVQoXqWZExdmQrB>jMuN zJ7|D!OsUwTjZ5)V(<~jPHTlNrFu) zsT9i1bE2iR%)^4i^uH_RKmDeEig}|%KPM2U&%(q{jtOoeedp}_+ex_qjvZ9_+&a$^ z?`0PCA;7}7M0V7u(QCpneHSKs_LJv@gZ2 z9h?&N36}iE6ZQ~*Uck?6?H=lmwzu7|2tw3!;G|_wf2-_D2*K$!&rq=|hHn;A>G>WJE z{?eTbo_~wnpK||Ckt>X%YpOd2hl*E`;hXk5;em8({gms^M|q|p;qZYUj5eQl`1MZ6 zVxZ1i<&fU5zZr`73x^CxfDSg3k)uxi*a@jzaXjh34oezIE5@$Q)e5Wn@_>tC8NZI| z*B|;;0zLq^f^+w{!BO0XgX~=S>~Lv>L19g_OoPf z3;UuUQVH|sVo;P>{K7J5O%oar+z!{vHEpQ}UPl>koYel^o5 znDtCRP;+-1+o?E*sIPNNY&*j&C>&ZhX)T)oBfJYgcx_pzZ}h+}&KGkQz*N-OLd6cz zd$%N8u&pd7QVSG>$#dYlJaR)c-E8fuK9|MyrMqvE`xZx}mX~Rw9htQ7eP|hD(ZyYTd->gvzsT zR#(npKI6rrvr-MtLcGV_8roWfgZejI?=RZm!ym`xKlSS=OWxc~UYO(T4BwCn7{sI} zb|*|rJYBzU0nGfJ+t&+-L~+fKPNrs3;wvgH^{?uIIRuGWH*OoY-p>FIRZ6h<-obT< zcIP8pzPGK!D2H|BzUeFGTZPn4pf7&T(%NbV)b+l_!{5i@6oA8IG;Pn&Pr$Erf$^4{ z;Sj%`;CR_m5NC`mzKF$0I7dyM4)4Hi_`~}32$sULI4P5Qk*8o5c85;4KHPpQy7)fUnKjII>8erB?bmsN z#1*;EyW!d~l&pCEh*!hLedDgY4x?*?yg3@=+#3OV(7t40iMnL8T#^ir4jLOyX~AK! z+AZtE;<bF=*h6JS&;!#?~)O zn@>kc<{~4U2b*w<2T+(bu@;9h0j^-sHKz9cbVAvSL)+hy{ z2W?Ky6sz@eoLmIT%C$TORF;C1)Hhkc#`^5M{djfexnqwJp4>>kXEpZ^r z8Wu%l;OAnKMlaQQZ$sFS8T^R^7CX|hYk|00jR!gwXbq9G;jKJ5y<=^;H{y$*Jf`MB z=#Sn~eJ8H{zHg%in;J+)4zS02jhyBC`q_I3JXWv?m3Rvq2t|`b@nojujd61-5tbYs zA{`~qI;Y*L=@rWbw>qGUs^*n)$`0m+m3;hK5!;!!T96o08g7c%Ye-rx)Q-xyqem=( zo=4ue5DZph$8Er-C2J(o?^`mBsJ4wgCEUhsY5PR_&B+PIYaOb1#no`vy*b~p@R=Cm za_RSuH8;@=lFw`B<%}_&5@cJVzoHwQC|XUsdJps^7jvf?=6~MSIJRRx($>xxnlo`W4F%I;aC{EeAbR zAQUpwDr3CazuD#-sJP*$wKa*{oD{}!`&0Enq_VOiD4k1orjo0?MwS0oY2)8j@}F}5 zkAOtWDzRLQAu1h<%E(~k0ufWV^L$|t!SJyH8nWD*T9h*Z-50{(OkWCaj^PvI2Li14z$s`{dVVf0bolA5o zyco5aO{xN`)NQ z>2UwX`=I}8dI*Zszf#viyu4*u#BSu?7twisIT8jBas?Wf&AKAkLR9BaxT#2O;8&;k z6)E|Y(dH2BH{a6u8fs$6AbQ$B@jQ&?XYO?~HVwUJ4m!Y{AW5_~g$1H*vzbsk)`>G; z*BR8;s31WH8X*<=e@{Nz`jPObu>K|om=CjWvi z&Q&Gr1)gNfzSbUKTFV&Vu4m_`b$`n+qzcA2kjxnw5Dz`yhE+?lK*d7tWRysQ&^yU- zUXkA(?)&v-k__KqWmBpk#$_$*3L|S6#e&v@>`d6Xib70O6I=YdIu|h@7;vuOIk@%0c?47>`Hv zq=D-__ag>IJ5Oiew6c92?A{MGm(}Lq?CSz$A#f3$%UC{OB3hWNiVz96Q{>zTZOPD| zuq5)Fyv4W;)r4G&D-dm5r=3OoWcroVbwHDaf0%`8L#KqV0%|@C=<+u-WbT(P0iV}RG1UhF8b|>ODXK8D3>hK!dDrRqy~De;up!R zG#@OCn-f;n?2SS-y((aBa(boDH=wu97nvKFIG-uqy-@WYk;bg3e(g?9^QXYf+&xNG zBj%agf#R2Zuf_z|M5PA0`>rIOxFz`(1vhJq@$9|kewu5`Mn&1zvv;ZF!B*%}$32S! zE9AM<+4Z|RhXev`MA&}f*KG=?d%mCAYz<=gn&#nev5)$zSJT^H9~p}5pE1B-0J}>T3bM>YM?U{JQgaEcA0hg`Ez{lSs!>WDWY>>su?b~uFss400FVdN#@7xQ-ZB;GN6 z7y9>!h&Vt*;-*HbH2e6bWv#{eApHjSHC7b1E6bZaZP^STb|wSoL>dY{<|xvNBJ>FN zriQ<5z+U)(0ShQTXxOBoZ!4PZQyO04-UW>PX@BYM_FcS-cW`FkB4ol>oJJ@?ez z|0^PY%Kbkg5+S2HBIXhK-1F7ws~>w4*F)I;EBol)Wlwkz2J4j5IZQZmQNg(WJxDJp zRFwKw0y1yE`mEe9WqW__OC}8I-3H!$f_$yzX$Bqi5haJ>rn?(5(}y2zHDk8!9|^-^ zp?-@WPw;iuJc3Z+>s?fuwE*%E=aYknovB22EEG%Kuyc~RzB(;G#rE<%LlSXM)?5kcn zJ;edbZ!y(N5Fhxi4mV!$6dTx}5dB^EJ?R$(j$MpUQ8{b$)Sz2UK2Syz<)CPrRx#-& zw3Ed%ePcC}ESKzQz@p{<$js0|DgAlm<_?2|Us9?sdO||4HBLh4Yguhj`(QscT!5a4 z}ZxA+9$;i8Zq4VSuUK*osch3_aLaY z4JBk$^qkCcAG#-l!05Bd&TI6%*RI{vWI`8lOd5i{u)*>1gYLxfR9UwmJQDgEl+kPA zw-Q_=sG#e~l2iSDoiP^S;6J0m2X3V`iU>I$$c5Lq&Z6=j)za*L`GI`6QA@#W2ZrFI zz_zxmF`lWls1CuAk8N!rF(`A{Zc|h2f(>}QYsP!b?z;Is$v9tZ&sc`1w+$?xC%Y;d zq-r?q68vF3JLf^#V@Z1SMWsdGPO7g0ZcXuO{=}G5! z+71smZTkJ@Zt2Gu)1Z_uUh$WRbhK)G(QMe*C+#o$0qDqIu(mQ#2>Zo zeVWrlg9B_Km021WJ*~vVjYxsi)YOJ>T8GV~Cwn8KWw1cp5wMdFRn_GZNe)Fq9E9@Y z3}U*&%!ricnDG)k(#$?0`V-Chjswv7|M~=f%Kbkg5)7DYmZNk=TTdrz)_{+;nu-~I zsES3)+sGJEeMlD0PA~Cf>z_3i4}~fY0#?lE4@VE%eg=C_##jN0#zum?8-&zq|0Qp= zETnJlt;1uEDyfWY2Pw{v@5f6G1g9_*$!`%!Wne2A)^G!uE*%xM`ZZU5T<`!sdb!zw9FJVdB@p1>5dHX1ms)ASO}oIJ!bP z@YbO|{YEJx1cH)Qe^j`s2JKfHVBrXdun79x@(;x`E88;ila`&yhd6CYAB4zN;=S%yZThGn_V^BnoMMp};f zX-=&MUenuqHHNgX$}4(Hw)2WPkj3_R*f_K_In_q;AZan}#EMfVOXmoU`@gC3G2 zh_3gv(EEjaF4dJEYNPQJIii>`z`|2Jd>GpFJ8q`sN7;T(f7us$yHV568BU$YfPG4- z1KB!0)-sr=+U_Eq@v5DX_V}klHiK={hsW$I-q9JcMV{B^N;%BUU6kBV7@a2Uj%I_i zYY0OZND`t%7H(()v^TvF?1h!EVxG4yVcGBao~-A)`o@}jhDtG0czv4S6RC zMM0pJqyFCZX+{O<-E)}ecHb0V#5dp88V2gag)P&Y0eWMHg2nmn3#Pc#^}e)D^h8bN zO&Z~e^!oOEUE`pfB^OAy204`snu^m(ki}6sEJM_E3=)MJV z^e~LBxa=+N{iFL&fs!qhFv8RwAD@-}wH5yKn*e(AKZoGKG%qDgt?aPb>=d#c{#P2` zyu%vo9?h?~uX}m@XWTOJ!Ny9JoRF0)U=RG{U)}kFULlSu-Fg8GeS?Chv z0fV>j161ZVRg!ON)HUGDK~bqDbqb1=94#1EL*aV@NbzSO{_M#bvxQszoo#Dp^U~V6 z!fNqErY4BtR!5Gu$df04tpKJlvl5FLwPmY8_5y0Sml2MC%#1(_-sBG>jgGyM5xg2_ zvfAdOw>sdec--?S1o9!EHB<9>B^nzYvCemMrqJ%luj46!R|fB_d888usGydvL5izh zY_tq+$anjv+HYII?nfH?VxqRrIcu&Q0)E4W$#k9voz}9zxB+?s?m-PNHrRhXJ5F=Q z#lhW6+%bD~Gw5);6rI$A+lt-Lng2)KImPF-Z4W!PZJUi6v$3tlb{gBZokopq+cp}z zaT*)n+w=eS{^gvTeZB9;do$PjJY%e}<{We0i>LCF8QL8HF8)m{t`EdO)io@@a8K~U zqCJbrDgFNZ6740BM@`rBS-vVIQ)7zcL+HN`uHHXzwZOwRSoVA5j9+^ezWi!=gT>q~ z-KB{|iB$0NT|+DVwXS82!r^4O+h_uCL>Ep_Ccqkrl}+IZbdo{@g9 z+9g%^{v0Ve+{XjdcZbVC`C_>E^~uEIZ@B(x?f**Vl}3xGqEw$nflq6s^mE&)>Si8| zPbY&b63?v4SQL=3xABP0^R^}(>_#U|OmaFoq3MvSZJPvtTE6#&yjch#w;y738{S;i zdS3WYeiiX>!Ehhzhv^5*JC_h5O%6QgO3XpJHmuD4R>X6A zOu^S97@tZ4xX*Q14y^wLw&3%3eH7its*D&bR%F%bEPjm!)`f0KUzpwAy$7o^De|wPjdsp_ zXYF~%N`K%AbuC!r+O5|l3tYtYnT+hOQVtG4uQ$(C!mBFr&QRt3-%5EL#JPNQvvD4g zh3v-jHvzFQhHu6F&<2FZbb3XVUq~GQE|ftm&JV;a*Mx|)KT|=*?U_K??P||Fl>h!| zAfI4$BW<{L;{EN{htPi?TuA`90x&eoCo~n&u0lrJXUMhQL+FpCCh<<`PSLO2xY+gY zDzH#XeVxK~e!Vp{O0tMBLhMP5m!R>Zc61gvugHTtuOCO~a*VCshh#aFfDfPgh9IC|yociq(8}dzu~t7LibD zneeQanaI2*JDNyn+iZpoCu{%$`h9TS`&wP1HU{$~fE3J~c+XY}oVIs3XV*6+0U3(M zzR5IEb#(r8!3!&lQcqz;C4wAOX|1$9+{L2$_D;WR?{Gq(*`2tbmfWxGz%hq!S{tpF zA}p1U#*DZe8=9(@H9z^ZXWIPY+0$^Jh@DVlN9&ZM*2`-cm@1#A<=O74@uq@0z(TEr z6-sUu&Ck6x2t6?wk{Ot-OYDRcE4=TxpE6%mS4egj{~(C2WLKwyv1O2rq>DWHvw5YV&*BKAnY zt=KYrcgdiuY*TUt96a=nE~{=KLR$4oxo5E79lIt~;JKNtUOpil&&Tb^i!S%XiblDGcH$J*DZq&6r!VuhP;m(lufsnpkQeTL@4C?Xy*Bz?Jm?roPYP!uJ6>zQMk}E=FOi<`6yFs&?fm^5eh@ksxBpE zEH&uuL1k5ajRK9)1$d-a3vdYPPozd^@9R5V86NYayI z?Joz|rUo4Ffpf1{YJ_9R(^&L2E*%2fa<~I43SssVEXw0slUs}QW1kP92HaxbnHI5A zTRr;TX+#_*zTyc4i#BZ=tifAvaI)q_eh59q#VSxb^9{A`D52AxT{-8n8;S?|#E%zz zjWPQUn}{V01ifE@#qr@6Cz4|sJrh59VoM#|j)@Q~k#}+s=X-+-_}c{Y5lNa(K7{`J z)MN%wQ}bA5;Z?(F5P`oU(TFOz9UH%2GsLo>f!9SO($1s8WoHMHya`h}8a3YF&kg%` zMv!$_Vj~y=tKDlnUN8j)+F$zolxy9(tdJITU%SBF1Or0bgJr7&k!*Ph1rUI|cK_}K z{?ilvOHHYb`I)gQxR?E- zrn*{H+x)jO8-7F?qdJB+i&~Fu!o4rHc5KhcVF?{QmKD|}U`?B=%SY7R1K{4smN`4% z`kE%j6&X}esyiaY5N@XO(H*n?>OZqzx%c<&$%62<>v8 zVGdQl&dOa*Q7RB_VbVEv&L|D??Mr`q-X5f@@}WGezBu(h5H|i?{i$d#I1j<^djK!Q zAd4YjDycPr9EDU6*L;z?zT8*-#wiqkg~PI&o>V-f>;cX?+0=yb3w->hwXU%XZ<*u0 zA1%I0wne=CEe+(3#vQFmg|DA_anCk=dd%eyZ9$36ux|>>y24(w3@b%5(Os582z@oDig zbWcl+oBh?VmNe^_P!8If7e1UMvQEm8Y_%I<$A=#yu=+v{U61bRXfN~Na)xghkh;k^ zl_Fyyhd)17b6m~xl+3P4*az-H>kIgtP`gN@>Xogng00&w=vQOD;-gdy0*21@^vF`9 zB*}vEl7DL3GT6!2r^iTgg1WX-fY~gKF&plrVBjsqkUrL`hOw=vr0y)LS`(|Jdfqv< z2iH%{{yi2^ioZtROY1jc4QbCGRay+&%bD@mALmg}-H1l8PhDTXEN*fC z|IZ29u8qQKer|?2+#1-R?aw`ZN}qe5h7xp}V}>{1&rEp#+=TpbsCPo7LNtoYJy2pA zEzSW%KJ#^OCjMdvBq^>eT&iws+!R0})%sZMAIzZ8D|gi&M^@9cnb(lZzPlfgbx_Be z`U+A_xHN<4fCT;_^xr2DZh%B`T;OS$m-XFrWpcv@qOFX&yYUEy!RMD zr0_hD6ra{fBMIEJV)pC>x~?i#L|z;W#b7zS1I<^9}Vf&Yn$H|tWLn_ZIJS! zwlxeTbP7dh1$BdyoP|Iqlr2F;0=zsVXo7x{Wm{#K+sd<(lTllD)~6bK`J(V#_`Y*g z?6Op5&OmAE^x~CXhb*kHpT(?P;!Pd$U8U`IRK4f zWGA$vd4w&(t3S)kjlqT85r6Lq8gl}#^whY}ksvM9*m-&EbdPoK1bLizR+DFyd2?ET zjsIu}`$Bx=yeson%%A(W?TPib1@=upZ--Z20{t7Ol29u#55+7fQohRpf0{mQkmS~L zY%1}(iLZT+R_c0*CE*L8j?DAmVDAk}gTg`_E1;oxIkm=h-%Wm)Ok+r4vt^S<5$X;a zkRmWDWiDyM=bOC+Eu=~Y%3W+#NeOYX;F_|ApFqi_lBw3xHYAty&--4N-)d*N@Jhr3 zb4C8=Z01GN{$-`S2ExK(5%F%()^bkAA>ISbD|96msj|SJ4>dLtppo|B|G%++k&l4J zz|$^}MaAPuC9_XA&@g&zp2kgycLJsm(DNa*DgimEv0*j)Yqwk520FK;fHq;`qR77vV@U0uMDC<1P59-m-ibw&V0C}tqPjvBpg46{`-(c03ciS-aO9+(E!?0`$Tl( zkAjF5!URX*|N045Q|BAlBk_P{Jz5d9VE+2sHj|xYNQnT*vr>-_%sUrbo#?gt{&^0J zS#Fr~62x4%8-a}NSCpaj+~e+2vrr&!U0zL-2)*#X^Zq|QQ2?_4X{PF`uA3bmmfuisw~?&^=f>-x-jX76bzlK9x|(g*0Q+b9^cI6aogk$#Za`UL5AU{2_;MXp$w1DubbJ?v+BcRKEd4U3y&SBf{5yFPA@wT zhKzOV+_glkcXY_xjuUwBoaF|PoKXd`ug7W2v*Qh5!?`&a}lIfeR2BR6avKgPz#9bLx48g<(;&@%$7j>+W7<)@Bb66rk z{^b3zuLw@mz10Q05uQC%p|`X$=1{M$(C{oc(tZM#8a@)(SpV5Zkno%o^>&r*X+9<;rN7@+rOhO!rhad2Y$xlb#0DWpt&x0|8eV_0l^j{|t z+--nFVvnI9IX|zUgA(660!G@HtcVS%5Vh@dbHtZ?w}xHuFSgYmITKY$7RJZJile_@ z1CwME!Id0wW{x(W0^=R@L0i_})-jD^H90xa8h279o`w6WuO<96;zRoRYOfu4|L>;Z zKdt>=5{c3g4CoB2Wfknp>lKe-ygI#7#fq7M@!=}8dvKO=+Q+7VI*jM7c!I*;I^P2^ zIYlq|Hlh2vqRT2XCla=$sA~M%lIT{fPYvqTgcKWL`@4`pNcH8g#tKh9ltTpeh$F`GS`ReQXc<}9l1kS-1OQSAR z%zPoJwZrzcinBtf(>@hCMYw&nm;xXq15m%N!0Iu>#d2&|n&~SiBvbwoO5` zjs-fxc6vYvEG;HORyx>t|Gm+Vp_Rz2ah1Rq4oH#1<=4$n$oKNFO(o#3?vRh@gNIF9 z0Jw;Du~MPMJemWRZECFa+;jJL%k)JE@gHyMw`KBBWdR{ui~ zkd^=QT_-plFU)dLu1rD++r3OOwoBk|35t%NW#F}(yd65{b_!R~fq!S{e_H#$;7S^- z;w5(KNXecu#hG3&g5=o8#Xp;ZLAAZCuI0YBaLW**u)4k7&Na(b6z2a4;ea6SwtePh z*I|lL!D+)x>#F?4ZCObxGC%wC>K+@FT4m98?1Orq0_O=EM6$kM#L}M(U7c+UM01^> z-RWWXFn?7X*{IRBDV#^;-EZNyn{m7ASwpwzTf88JvoFn@>9WIoy+L z|3Y&Y*i3pn(ZbD@Z9#i>2cCn$sAZ%C2P>{(k=Dc;)1Y*EABeo~w*7q7y5}7a?5DW0 z2?$c3q3p1b61jzO#V?ja44OHArbhW`*)FXF=5#ZbOatqW7e%SNEfg^Q5!Qygk&_{6<#BVd zQ~BN5xk*O{&yPW6XTC!UA{e95h-)^)juaFYQQOD7m^dgtOXYK%#*NTYc-A{66+TrA z!JRptlEDgpTIRJcO|<)67mx6^xrVd>BkB%ER{*OtgCl#xBxs>Pq=SA3qwNRVnY~cR zf@?QUrVVO&UF0#oKz{GKRoc)_;4JLpBC9je0*lf$B;wcRnx1@hFtwL)-d^kV4#Lvs zPc0uwZ&TAmY4>E(oT0AQJbJ{gzXt3-487lz@%MRq? znu%qO`u?kd@|$&fsEzSi!nPMyIKE=J?>sDwAU58A9~@_7qi_4>Vqdzv_h$5p{9?B(0hOzwC6uPz3LJy&g^yQtL9sf|cg z8#v5oc$NoP=p-?0as4X^ve3CA^44W&v4Ne{GmmwD9IS<76AjC=;bV%iGX*Y}{A4yO zB%RSo!=?(>HM$>a)=yT)Z51xJQm+~nDg8-*v)W4F|>mw^7sCpfXU0dc@=8gc_5zhQ0B4QU%WshjpShhwGj zI=R`cA0}#76C_Eb!wH4JBk^8YeoA>mB1cX?uxSMP$c(L7_oz9nYSK0&{63$B(s$8f zPqr z(*0>={`8<`2SX`cfT2O(%Ie$Eeuh~q*%1`X?_N-VL326{BQoFk1$S(1 zv_1a3IN3}7WHO~Q0&+p!)Xp%%htPkYL^J^sX+h{~mm5euup6}HQ;%G{vTLIF-8vqY zk=gtc(Ji5Z=T3W&{}>GiN~D3yOfC+Q4%vJ@09-K&W|qG;FS2ms6G>dkyl3?e>ojPs zQxdFqT}D(>@`^X`8szkRcwy19``=~$Kdt>=5-Fl}^aXn_tDO~zrKxfaX=RK9N?tWURzs|b%H^MiSaqdYAH;G`|9@F&$L0rru0U)UanrT zt}`da?px+g77^V-vGi?dqs1Hsp)H`ykGra8)gvtL3}K22Q0*l5lVfDJEQ7z`)&JCCa)&H)Khl9*~P6A1EZ1 zMSOMsQ|2=Rjx?C>wQT;lOX3o~l}&dZ=vFuHJq2^%C%WC^TXgp25@SS5aHQ#FUEhf` z09#CQ9+1KWs`uG>pnSo&??e?@x4{ztYOxo4-P{jfA1YBu2I`iFxGvG#A+AU&O~tn2 zR4eWek%8!_9K7c92KxK@n}1sSzu*d*-0cGnv|B36Xb;SGNh=6hb{lNE@0}BteMDH` zqHYj^J=>|I-D&))V{s1oDaaIP0e_iM7Y5V8#BW7N-mW#~=o7c`cV?G<1P7K8RT&uI z(IP6im&v1Nn1#yKejWcm89J$eMfDB^kAp30bRz!|SKZU5m>5x_I}%bo7Io<-6=`_b z&CisG&Wrt|zN{3aW{6bn+RG*91k}uxnugu2`^0hao^-g$i`Y3#+F(N(s=}3LY*~Ni zVkJ)reD@Ve;zD#8LKdWb7+2{2#!v?KNBYZq{_-AN-*xRUUwC5^LXiDDo^gV-89K?Y zFBM6wHMiGM_ZVa9^Zjw3}dXuc4*^FKMHUf(T_|;Kunl zAv-bzvnT5_zp4=(F(}FDtUlj$wgbIO&3h&_Q6}{ZS=>@jUcGJLrn_a^9+V>^j4Od% zz`coM>LacnSK7H`QosDZLkUPZU zWQ&bfl)q}jHhOfI4XYb_8N($qNk00_htPkYLVy4YDKifxBc0HPf=)ggM=rFEMR#jH zJLd9o<)>0nvi5|=k46fKw<1*t@p0g|kWkDB1-@{}VPyUNY$X%})vJdb2t*NN%`Q`8 zIE#SWdu6rKQhfG2`qjv6atAFw(PCq@>A2Q8N4PUK>SfiUGP9g>Z>b(D(1ptSgyJiqqS(p9R=`r zP8xi0&XRf<<6qyUHE8wu{U&)4HJTqHN{QdKwW7I<%w(^CHE}s1ocdjf;F5`|rf#u& zTPDg$g4e%nBxEAFpA5^kjvQQKz%uF-=$#mK1%OR(ts9#!kqA|8GJWG`*V)ZQ^!N5r zE>ED0(vtQ_rL~?5L-vP8&jCTz%J}uA8tHp_{v=m44t>oTNE&q%eLd)$B81PvuDC9H zQQTB5q@&jl#b*upk;c6HY4D43%ah=gl-$9!33H7j2i!Ue&P=O9+#|d|t)cix-B%Iy z-*AuPi$TipFmXu~Hfs*wOuVC;vj#>LohPtkYh-aHS@-dY=-j!RrAWmd12!Y)*e^bwp zicY1CpQpOMlbHh+A$Nl*t$;+_>iF{HR`|%G&WOw=q8<4kg=pvEyFgP4 zZfC({r)R0gUS+D9{bps8W)mUrlEnf!Rl|n+k3^)9KdBr68K}v{$=RT;o?BCtWnxIP zLy2gRUl?-gKVV=({dcDz)A+&wY`;Z`6ahI{*G8a~5WXnk`1iHl(a}KUMA7;vKqCAm zSd1Ucz;jHMOv2{qxhO~oJw+2~gU-kVy9)%J05n(mb0P{M<3s4bP9ivS0EyHtH$*eE zPHSQ+Dl=c0%wsI8lzgAMb*AmL8u7o6w)q`sy5f0Fwm5>LV0pnhp7jeYph1z_7nPEe zseBV5OS@acR4sNvi~YK0>%P@;o-NYvTJ7SDodI#YT0_YZ61(K@BLAP({x69Hz?w7& zuYSHj_R!JZi8{h$<^aCxZ&>Vr4xA~>ghZicJuhLEvMeC~K1R^5a2L=HQI&97)(LYe zb04sypS9MU5&)uV%KRn;J(laU9D=7l)Th#bpgsE;-@Q}kK1J>`pvW(lPF7J05XG=l z%?;63*%$!799p6)W>(E?34^70-a4GOLMRchDAgsMla9bhn3iek^+d_{vD>BvcV4RV zyYsTZnh?U^hP{da48|SXU>KYFy%4kG35$7(8*}`D-&sd>jcM81G%797I7*~q){AuPFoNPkY+WEK9n5>4`D=VXMVNEu{J;9bYMep8}71TlY!#d|U zeJkvI<{(^aKMDPI_FOg}$LGsVr<@3!9V!dmoc|te^-pULfa^aSWRe`Se@n)hi;^71 z!3iDSi*F&i!-S41a|cU+3M;$oT;2C-uXz7rKXIm0)E3Uo}7UsOG#kcZwJ8D#P5qjfO6lM(&?6nL5z3K z%|iE3^V5>jt%)lghPh>M6AluZ0!8cK$`~69neHdCTe}ri>8eVlc#&Z znZQxkYH&w{#~Vra2_&2xYlx^E#cr-TF&(`rFgK`R*o$q15oODUjZ|S-9oY?s-1z3W zY_Dmsgg_m-@LpdkzxT?z8PEakDM#qU#+lN{LLTVRig9OeH*6eRwzPoHx3E0KP_({W zY|#aV#aaUc+;&FY^(#cVoQsGhfccGtPfEREcZ6++H-u_utlwO>FA#hn5@Tc7-c9$68h zWR!z=A$UvUNJL6gBPjmBmGgzB5U&ER%PS{7hb=pX+Mc936J!Xt|Moa^On?N1?$7S} zk4MNpCuh+?hBYE8z-YbIR>3=I7lDUCg+c3LM^^*%mi)2qH8(nk{{(i79$|Ub^vEy2 zV^=82?rz!|u;9NS@$HgzPZ$VtpB0P#1BZHptdD+sL^oSIx86BQKPP1&j@Pr4_(HQ| z8zi?9YvDeG{`-=g8-T-lK1VfA8{~*aiwK>wBM;+Vq~tL@)=-)2PTAHW1tKSc*+XYP z;r&iB+EC#F5vNR)tfwb4R-0UuDAmaq=Af{#@ybCYV!ln5{&k3(!>mubs85aVIy5uf zOtH4%z)Ii#u9W}jG5wX~qF%qmekYI_r|StjPzBy%#P%;;c3Lh-p=n}IEu-$j+v5c* z49x*1NlKdco*Vv&162n0#X^bBZ1u=Uw<2RLOYC!d6ngF^{S#U}Sc>gb^(*Y-mA`;u z`k9cI&nRV){+~)Y6|do|09maNyEN1Qp=nLl;I>Yiz80|NXD`^Bg-IB&+@H?Cnpd(N zIHIGIf_#hC@xJ576YPpjfGe+^{Dl`{bT}9mMtW?`Z3@U$mflpHASl+t!i}q0u-`P+ zdP>5O;Q1(Z)woDJ&sC>j;G+xG5iO0dEb|q2H5$~iOE|3r^E z80c$F%JF3^TA&HX;2r5m-j~06!^E%Z9BAu=8qa-o`faDE7==GWtqhmJ8~aA(a}^W# zKua0YaGp5L>SJGcx1K08RlOL^6Aqm)#85J@U%iu5a#dswuwRYD@QZ6Hc1wv8C8vAz zx77Dr<iOd6)Ko!R=`hg0Ie*ZD&SjRyP!wwKmxgA z{;V~7mm#LS5Z|0*)0cwILez0GinhHB`mW`gst6eT^RQw>_WCd6?we%v`EgEb&Uujg zSz#ct0X#9Py=!f+BHaFD%D%FKQ4~KZxr+Db9k9?PmDKerq_N^d2`p^DQOmA%v9)4U z#1iL9#oI++1|58X_GZq^i)1jxF-fvdC%Q~!D!ze3Er;787Hr^UpQ}`=Ar?2dgt~Og zP#=F?4G~f~Z&5mz1p(7vThfH*Vz#f6bUfPoQz;vKhZ79PTc;%IME7ZPP2-O{+>Z1+ z@rw`Y2`L)I%uy< z!z-&2>1`NRo3zvszaKwo-*X46w)|Zw|I^z4C6Q!2yHE#;(s&->gAJ*jj&idy5Y@1k zHdgCJtkrY)9W(GRzs7AlDnMX<;Rs438La{ZC)4?-KzLRZ{W7!>R|=j9r-DK1RveK_ z-?{+5O7Sp>P^|06M@==+jW_g~^!l^gwRQ;9$y!87s{J|mtx7lkZU+UjQuYBTN7hur z+%@SdMe$<@7`L)tM(PYwTkOfMRMTpg<_LxUig!%+^sPmwn^u-py}qt@zhujuhi;o) z_7vT`)?&Zmw9Rfy!`9UvA~G$i*ERa9k>_Ts^Wh$<9r>dVPls*J}5j zcneNLkZRL??=%CJ*!ZpKlTT!OhjiEVTfqFyBPR$q?cSj#MG+8O8uU7A1{XTX=*dVJtv-qkc<=3JDj4zQ^4+ZU3U&ddH z@HepBzYBYmA>`GQUV@?(;-dQgaTfXf6XwtbQbb7j1j!E*VRQ?I3>5! zQ_VB2$ZvD{M^3?Oo64o-WX7}9jG?up{l>kFNHKZ#&*Hp?J%m#O;EHo(oG*13&L}*A zfbYGfn++Eu$i9AYlM{#fAR(Jtmh*CPQYh8mwG(N?%Y_1P5XKmK^QGC`#G#bwYBxYx zTl(rTx6AC&gBd?Bq9Z*`<@?@rwg*t|wbcnfJcpVX2`|KNFjOrbf+dNSxt9EaYtO#l zJcVjRH@Ey zn8Kp@@QcU|{83Cj5A>sF_8;%ogc`CNu{dVB!@!pv<%2M^;UDM4{_8lzz5;NVN0SQX z7e2Fp{gsV?NqJH0o;o6?g6%4#OlVQG8z+ta>L%hbh~$^Xw#nB9X1!lq2rQO|%KJB( zSic6>p45DWVwIek$I;xaf-n>(4#TN$VP-uUtE66e8-=b#mF(wy{;rh&=`sDqVek;A zS0b$;7!1=Knux;Ep|3EONqc*)uNcd#x@%ac$;>nZC_0@0`15nSB^Lv+7f8lZTER2C z&EZti@o(=>as*jnNIYfWf_&(*r~$@55cMz1(EW{|A6)bB?!AV6_x@DM^&;vkoFY?v z#&39>o+-?;L{X1+A*P>gWAf{_;`761v{`8BCz!8dh-#67+Mb?|8f}Z$JQI|s@!#Lg z_z=Tk5DSGhE9}yhNm2TyF-I>XbiUNcr(8PxTn+Y0iW%6uUU`ZtGg0VJeERYI4F1}Q*=xUc#&9A$zrgS8P6YY*=9RRezBtZUPLk~e z6uNO50h|}8mNj7n63om`=y;P8W|jdeNnvu z-Ec*W%9YZ6c#;3C?bs8Xg?igCvB=W^^RCJ6q85{z3~Ys)zp$D|Cr zj_C7~8u5rsk-Xt?suqFDXe1mgMns@*5psGZ)lUTDCb69fpQX%Zo^#m0`k~xG&Brbv z(a&f1cW+8X`mIE{A1R6sBE%p0a7nQxjG zT%vori&&y~e=23TWf3`rmC%)=LbK^czGf*fUCEBgrK=}Aa2L`RWfrafKOxGT9bQvH zHiu{~{UTJicjt82_`W#YS~VN=Z0pkAN}J$+he~Ws?6H*^evi@NEJ7Ju5;jkEm7pzO z4{8h60TOr@HDdrsgent@`h!G*DV44#Ph~aO5PWB#2rIEsUlTJxl?>+d_o&h;g%(vuUCXOx{5Qy-~5j%4i?XGz5^t?Nbax>x*n zrTkB850J=z+FngB3&QF4(=r9Gjw4W8sPnp z3a&{6KtZ5iV`)29C4yE~rW-HKmf4bTqatAH_~P>wnt|otuXkA=dTT9f#h0Tc8O2#D z3I6nyQ?XBB1Qep@iQF2+-RDS`vA0)$DJoDi*IIo>M|b;#M-3ldmjXwZ&)jG5voz0vjP@O zIGOD%4l6wM6U$Qp84E#WP&?HGYqf5FT-{VSP-uk19>UZ(|N2|Yj=Ek;kKx|FEH>rX z*DZEsQp9wbaK0}qu15-wOE`fZ;5$f+aXt@ljouPfX3bfF`1*D5?53Hguk~KXq$g0u zoMNb3^y^RC{!DiN@^(nn%EYlDY1!LWcW|YRj4)yG6Vc zQ94IE>l-k6S(&*k@53BoIR!S+e_tKHJQSIc%Ma($60NdPI(n~5V-buGerM1ms8$PI zhD&N73;y$j&bHgF*oi%DCbC-(^1^ADl_z6_lT zfU7`-i=WcI)hyQFt~3EL{?`T~-{AsT!8<%C$bz%ZtEg)b@F@~}5!+7GowR|jR4YWR`tjcx`k&VR zFSuIlb#h!O3Si#l$KUfybeV!4%~s1ePJfh%lbP!(F8sPHr@^I&(mfTE@23waJzB{ z$9X#b@yCMr)va)6z>gR(h4hJPy+>Q4K$)g;-^Ojy}G$l?@6(&P4>mr8}hw=B}uRL`Ceoe&!aE+)7NXu(+0TU3lOO` zgkDbB@F%50Xsz3X5yA`*e@S({ym|j8UyK;}4aMMNVH7Wa>PYc(cZUqbk*+1U0ixVQ~Xaa z3t7><2|4Ip3_3nWO}fz^eqDxsrtS6KW4~_&QKVLy{eMqz*|evmM~Fk`i=}wACfy^% z!wrAaz3|ae=HwWq$yd_upMkb-OH!*Xy0VIb9YKwFaNm;&hz4wd@tan_% zXg=Wsr-IG%-wz^#2s?&iO_}GZhpdWKeN#=4Y8q?^Gu0s7!bo0fpHMbgYp zg^>()HH_F@qOE@Q(p{*93_7cbG7}8ZOB~z7ptc;;4;Jk?2k+i5h93P|SU}u+(jGH8A|+_D&_LqR&!EXSpOH zO~REUU!!Y`7MB3N)#EYtluj4(p%~F@LTm`6Ch_NIr_s=NwWTH*c#Qx*()l{ohko{G zu;&%_3EyFE+(irHFSk6FoZHIY7P+$8@=s=Sid}+2>#Is6FQw~P>Plb4H2rcsZm>lR zd$-s4$UTy+a@|e%QJ#EzBeW4U*}Fx%{8~8nMh}ykw%;UzF@J{EJ=fY{w8#uaP#5YV zg4z<6`i*SJT9W*FzHd+7!H1|F10^Jgen+@3_jDam*K24Cg^s^wdhh~W*S68eCk+AZ z{3H@*fEw?>Cn)}CG?LAT0-s)l#`t`2D-4N2{26usvIA9{aVxhy*x6}OoZ(VL=VG}? zqUH4z@+Ac|M~_K{uN2%I)ELD~KthmbHsOy%WJgv=ue9`Bp{40PlOFi-mC z1p@EHFvq1Ku)+Q_nTDWi4~8qGDRYSL6}@gK3kT%K4XLEp3W$PPNZ?h8C%Cd&4$zOA zX?2j=h-Ix!U4nG@ARmXv2n%SBy?kHeaGRxxG(Cr{#0H`Tf z0gK{;Z_+(DlUs-{&RF5YKp&tv?h;R^E`sXzp59cf9)a%DK2C=G_o;~wpr%j|xt+)| z1?^azu$o^nxP%#CI5>|4TUs!$T8ZBC22~s9CS*DsL|{C*7PHXE5_RzIo1a7A^>K}j zou>0U^btZ1xjFSJbRq`+L2r)UQij00+=NJeL+ibX)ZtkR0`z9)$RczH;6gxR3$X(%BR ze-`j1Q*@==%CP(q$InX5$~8*WfY)xD-`Hc?%i97UAYWRTp=MoRFl4{*V~8}vA#)17 zQ)#z6m&+8YfhNN!C_(m|2=6S~XUhqm0P;|4gfqfd2iXmdV<6aPU9ZoEBgRU2jSCA} z-7;mLEmNDTY-zMMv>j@63mZun@b}zOJTT~U=Fh0I`4JQsI8=P7o2ddpUum{TN1M*L-+Xpz{UDXPtJ{gg8E|`0W;c_83aZ$$+$iBYL zx&7mT)MGLoGu)TlVt7nVgwPwi7fSGn5=Qq3d{`Wkk^ERjJ@$0>u0bLCZ5j9-BF3-B z`zJLah1QRQv3~wvXBifBoncP&Dt}%c+~l$h%uYIpj)F{GNWL90vKIe}4-y+f#=IUy z$Rh|ebh1#1rgR}f$8owGcjRajjb$O{He z(4%&R+#a~q!)V_HDzMTR0j2k7dPg!Fs3^t{QCT2T7fdi>kAq)D#oXq*X49V|7{hi+ z8Z>0Y1H_%m6ex=!*lolFQ(t@aq9P9~=X1FA6#jr9HL9A9R1-M9My7Tz|z?@+f2;tSyF|M}k3AGG4rO)66i-ES-<3 z{Di9n{v^xCP4U`u9%6te?`2Bh_ZgCa0{hFlCe#m)jk|UMU;@>MmO;7_au4v?sX5#W zpLsj;0Y=LC@+AAv?y^g}UzLv@z}56LS&g4ww3om_2VC01H&7acW@4MWpMhPp?=4QFe?E4Ya>ESBUP9ofLkQ@za``D1Y4 zHMAlY()=>432inn!wm`dLm zx)D8-zZ$2ze0n#MRne09N!S>6I(Z=GI@{`mNvkn4r8@0(Awc|h9Se;mAwKlG;v}5X z7=GYBZ*Ap`rvQ*Uh(O1iRCESd+Vj^p`pHn4BwPqJ2~Cl4yptlC*^KG3&@R5=FUg0- zkQ1(0wa*hOMFX=)tucE^qEWGAhqZSJntz?E1j>lSye13>#!vv^+DrWgWFQFK&8V65lsr^*KQgbLy z`SpGIWx#WtH3tldS72sUox^Snu?*&nai>v{GmWq`ldVDSLNuOA**u?PCD_z};THJ> z$U5f*S^f`Pp%5uoivmU$Nrv4<+BQv=nIjQV#NR;-5-}h1taJM?v5EeBpK_=x?!FY{ z@@b_{_*;ZfZ6!S2Jjl{3jw>c=tW-z*<~9H>yb?wr$&PoW{0on~l++v2CldZU4LP_n$NJzBlLkTxN{5b20aLp0(#(Yt0GF6nQj# zH%7e*Asp)wz3RPL1QLGbJ_H-Jr~R*nD+~Zvlh#_&WNb~TN;K2skfRY|Iny2tP#x)g z4b6F$Jx&iwm)$7rIXWd>$&Ej`F-6ucIu!rCbc~YD!sxpO?&gjKWb6xtx-aeijv<3= zVKSkYF^_tTg$!mwnEhY~c^i_;-(|0Viv3@3m1(ntV+1-a&Wdvm+C_A!=X6#s7nYTd zl)H{%XH>J;x*v>z>Q!BViU47!nv-wJ!pH0J@b;F_$^}K?Ng>E<)+nkI(Dsr*$3Sd| zG`SdPoRjVC6EoT-9QH2K)~N7}{>fNl!acm*`Xmqo$?2C*^Wr-m4ZE75$Wp5zVT;`Y z-Uxrk80O!pW;n^E6U7k4`@y`|C?}QQhQxo$6p5oWFd4UXRoQdBPLhs_e7LIvzkEQb z?;sPex-6A70Zy~*iAjdWj^Pbjam6jk?7)xwUJQZR)|I;Qy>ta%M%a6WufPpMM^x}c zSakZk5@V#Ogm{YI zwe+Y!KI?qRWUOfZt_l{GE@VT?<1I^~UZPnTIS?E=L(O1cl~gcPh^NN0eZa7!Pw$JO z*PCuJwO8ZD9MW!y;!>CmRw!kWCw2U((5?MZH1|v)-(_J4Q>r`rpWmjY;=%1CdE>Y0Fa^yS2Q2eT=HN^}b1!!D!n5#Tc zLW&rM>!WQb+hxEtP@@7KGDE+~#HkYb>IM+Q16sKhrDGil5Md64Zont`o)oWUS&NWya~Q$BK&u zpy9QOQwpF$>w>%-#QgU?g735M1eE{3EdpE*f>7Korv8O=DM@V2p%O(pPWDzMb8j88 z&5LYKhQa?nx{UEqrfCVaHjq@WTbPj8zrfuJP=_Hr5~pI@E>i!f=8pqW1pQQl5kl0p4e9}g7(b2* zC;j8Rqg;zsys{cvetV>xb;1c3NzwoFdMA?+QboY#S)Uh z*B+2$g~j9u7xfk>p*x;uAPHM=_sjgv>BF0q#PTo0<({m5#W0rp<0-Y$tgwNA-*&w5 z!Bsgaq48Bb1|x1!#%qO>WzBHQ+a19TBHNr9f|D^7eM)wu)TpxX zQ?)9p#p|lCZ(Dj-p9`_iUuqx7enfRC=%KR&!a2*nQ4m%CCNcz`fwjfB-9cMMp6Q~^ zf{C6bTT?raOvA!af!T#{h>HW)98(kLL>~hP9lDTvrt8+7|1-uX7V~_4ebsFYo76d0 zva>cB%1(t+%g=?nj}nZ@C{<)D$Ofi4@wIf@MIAw~StdYG;+&)JSf$uRU7Bg{Jutc3 z_~Mm97P$k)g$pD8J7!(1Dx7Lp<;QRhcw?Wt)qJQf{lS>bq2?xz?%cQH)}o( z%!be(apJ3D8$CLSpsZ`MewRPgEvtfH)b`EJ^gY)Amcez*z1s<;?$#Ua(6{PXs-r4! zfW-D1=O+m^4~Z6__qv`kb;lNs!E@M;+PV>juj3%Q8h}z%!O}gFalWgqi?b1);pBxW zd{&`e1|&IbaSL@IGVhB#17ZrS_w8Km&LR0vpNph!{7x_fgTxoA{Y((il#s_q0C{Zd zq#CgBJV|nzy>cfdKXS$oWMinujjq8d0$lp?OPyapcQu%dKoqjcB`}Z=GIiq<60mr)c$8#mI%W+`F0wQ>+<&)42>J?SJod8t2I+-N!x&4TjHwcBi#ahGM0c%1 z9j0LbINb*SnTtjE!3?-@yGN+dy%`5EF;ejr#doL`Tjnr^7L7e1F-r&_8vGxY{`(|C z50FSQSV{0ki-!J=ueh%$k|LGDLW@T?u&av^u|zO06|sVd-xwxgwx=D(S0uRjTSs8{gr&T1p2PvmmF_n2C69ULjc3T9=X#DWJP8;%9_qvSvJ54E&p-O7 z*#9Mw24*mHU$Jw};=8)O{tCV+`eEhT(aXh18%*h%-;W;MY8arS*6fEhv=;&wHWLH^ z*mxFu>`Zpb{t00ZTqhS&iYiizZfU9bs~jD|$p$wXJh;E;=c=<>7iH$L$7v`~)=Q4vE$Mo1?*t5h98l?aQ(fjEXwKHtbL|<-w*g zZx*jpw<(#JnscI5TF0%<{TMLAajSVV366S^@=B@99Hi;kvE!wl=22EKyvybE@upa4wOsLBI;JV^Jo=I z=uZwdlWBQLr;}-vz6#VKzVBGhWd4>MwFN^Pu^vAlt2!wrI*7QaEr8a%?*Z$YQC5;d zdndZ1?jR?pAZN*#veoMF&I}i;rk5lYYgN#1lE$7n7s_d4J#GB^CrE(4?eToXv!!Nz z`hy}>C&8n9otet2iv{Ln@E)evQaIsK3ZZzn;O`L{HU4^*l05UAURNiPiRv7&I#^yj zA`f^?&~EsV<|>wWdhWPR>&0`=_uLzx7g=3z9_4A+F`Nu1H%Is8FcRozOgNa?z z4~Y<4!6-{b;3FQ2NlM8BvhS#2YC8&zQ!9k`~G){NGWMk3h zyQl$t6BdF+@IfMP>PMXXq<$PPD6=6~qbb1sUC}$+49qC<_#vw|$^jpT#Qyty0}Aj> z#OGJcUH0}#nwScge8fT2)ac=>V-m5~xXhvxxr6$8Iwr~3tp&K{^gYxns2OWjXX>Ix>e*v32wck0zdJ?z)7SKu zZ~V|9b=3#?&G$#8*5Vqpx$!lM+5O|2pTtk} zB8@TLLAUbD!VkIvZb)nERhz=K8I5JDS?aUu_)a(QSQNkI!#2W3&66Ed{ZNQ|>2)L1 zotuj&86KlGPN79EY|qMAf`~H_V!&5@$>*Pz%TQJB*UZgG1Jxmt+g~>s)LcKN}5f1hP!wQ~Y+C;E|f>e*Ur3nRk>gN~DmUG|wx6=*=9o20v z-K_eW+f^^O#KS^Wb%3Voi z%XOTHE{4_*sXz7ZBF{Nd_to_L^Y&zvVZ|<(fJP240auluJ%zzaYF$UN1}tS+k6E=5 zTRrz6^KQ*y<)yuOJlq_>5x9K~;qAr}I);Ec;^N~P7a^I}@n^~c9l>Fj_%@8nqWYRx zNgH0T+l#!3A`PPc_$KutDe2;!QmuIfA0l3wpU2mj`WlmEG*W>%w_5Htj079*Kfals zA`#2+-=ey3mU5ippBh{Iv}%5!RRLebg!ciznXid8@Q~-`J;3Pxn%bgD3`l zf0o)QBEHbOe9i2K)Q4L+{y7n5Eon(p}-P zXEhW)EdAFh1alssklGqcgg%Wp10dDWKG@1YM`D!}O<)(Y-7CkE%2d{|CAtlC0po6N zwY<8q?+l&aEwGm}XsDmVM&3>x`}uK>9Bq!TJ`r{HNFh6!M?0&z0zh(vVEnIl+#(?04Y<5B5sv2*TFqHymVV_u&v_8D4Hd zt}c?|pEgKKcqQ4;l@L2+XCgh;>E@lo2U?0c;qw`<yaZGxD^_#SIWuV$ubCP5IxCoibCxZ#N;en{?uqH^=h!p^?qEQ+)Yv~2 zJS455-)9^rm6!6fuDUXxOI%e;+tM#46BiDB%Z)BBKdodp4xhYSqZP3jMafQ@I_;?S zCNpri#z08KDo~BSJJA^_CJ*4Wk>vN^mEEK#>Y2}ee9^6NEDDEd3~+Y$ygrjl-tZLN zC=ELcNN6`ONGoi!Li%E!g&iLDfyxHzGO8H%5<~jt~3((2Q z`Xokw)ffqE=oglxRVkF_DyQrEGeS9F63*c1C)Zi0=WjEId;F-!7i9RhRjJ8fCOGY@ z+6u5GTy$gnJw$s5j`O63p5H2pf)OQ;m1ZF;@|XC&y}AM8n^Tu6^%G^>gf7P^_X!0b*C~+F# zjg8?TiyYX7@Z$k=asvOtg@yNl7%V(jYYdQh0WXNEk zN~9$;q*+JA@+AZMxnN?3pU}@7n@`q(PcdUPRCYGC@RQF1Zgi7H4%WcQf*!s(G&eaL zf5Y`pvHuIMoCJ1dSy@BDPiWs#t_5q@mU&0pJ_B2qGTSU=xsVN^e%^JQZ+y{r84n^) zeA?;g$K-q~j6GF{f0|mbq>pJJ+d@bjZn=^-QlaQ@!{61yeWOGae4|2?uIu1@NO8n6 z0KgU9T5aW_*aI}Mza8L*6L7I3!*%xhRW7eA0*Q_gv6Cj=R*>n5BKTRpq!2ZAxF|nf z$A?kndZ{=TB}2^>V%BaRUMKKd>A@d^s0b zKAGfZfN0jg*fivAX}1uqOjl;M23g-}?1xv++C>pule!v8ais>0HK{C$d1(Q2AzO*D zJ%b}sl4?4t3%<9r;<#@cdhk+th@pzZC~u@vJA8ore9F*JHEuTvxa}IcPD?(WE!=

hRd> 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

vHSrHL`4Y!))mrU|+My=J*?NCLvdFH?W^2AuF;5 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

ecf&5+ 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 Date: Thu, 17 Jun 2021 11:54:02 +0100 Subject: [PATCH 3424/3817] Address `staticcheck` issues and remove dedicated carbon go module Make the copied carbon code part of the go-car/v2 module, and address `staticcheck` issues. Move README content from the original implementation into `doc.go`. This commit was moved from ipld/go-car@c7430a143c30e8d2c276f4d7d56394ad652c6c62 --- ipld/car/v2/carbon/LICENSE | 202 --------------------------- ipld/car/v2/carbon/LICENSE-MIT | 21 --- ipld/car/v2/carbon/README.md | 14 -- ipld/car/v2/carbon/carbon.go | 6 +- ipld/car/v2/carbon/carbon_fds.go | 4 +- ipld/car/v2/carbon/carbon_test.go | 8 +- ipld/car/v2/carbon/doc.go | 5 + ipld/car/v2/carbon/insertionindex.go | 2 +- ipld/car/v2/carbon/reader.go | 3 + ipld/car/v2/carbon/test.car | Bin 479907 -> 0 bytes 10 files changed, 18 insertions(+), 247 deletions(-) delete mode 100644 ipld/car/v2/carbon/LICENSE delete mode 100644 ipld/car/v2/carbon/LICENSE-MIT delete mode 100644 ipld/car/v2/carbon/README.md create mode 100644 ipld/car/v2/carbon/doc.go delete mode 100644 ipld/car/v2/carbon/test.car diff --git a/ipld/car/v2/carbon/LICENSE b/ipld/car/v2/carbon/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/ipld/car/v2/carbon/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/ipld/car/v2/carbon/LICENSE-MIT b/ipld/car/v2/carbon/LICENSE-MIT deleted file mode 100644 index c69ae66e4..000000000 --- a/ipld/car/v2/carbon/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 Will Scott - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/ipld/car/v2/carbon/README.md b/ipld/car/v2/carbon/README.md deleted file mode 100644 index dafbb49ee..000000000 --- a/ipld/car/v2/carbon/README.md +++ /dev/null @@ -1,14 +0,0 @@ -💎 Carbon -=== - -Carbon provides a [blockstore](https://github.com/ipfs/go-ipfs-blockstore) interface with saved blocks stored to a car-compatible log. A [Carbs](github.com/willscott/carbs/) index for the resulting file is tracked and can be saved as needed. - -Note: Carbon does not support deletion. - -License ---- - -Carbs is dual-licensed under Apache 2.0 and MIT terms: - - Apache License, Version 2.0, (LICENSE or http://www.apache.org/licenses/LICENSE-2.0) - MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) diff --git a/ipld/car/v2/carbon/carbon.go b/ipld/car/v2/carbon/carbon.go index 3122504f0..3ba853208 100644 --- a/ipld/car/v2/carbon/carbon.go +++ b/ipld/car/v2/carbon/carbon.go @@ -3,17 +3,17 @@ package carbon import ( "errors" "fmt" + "github.com/ipld/go-car/v2/carbs" "os" "github.com/ipfs/go-cid" - bs "github.com/ipfs/go-ipfs-blockstore" + blockstore "github.com/ipfs/go-ipfs-blockstore" "github.com/ipld/go-car" - "github.com/willscott/carbs" ) // Carbon is a carbs-index-compatible blockstore supporting appending additional blocks type Carbon interface { - bs.Blockstore + blockstore.Blockstore Checkpoint() error Finish() error } diff --git a/ipld/car/v2/carbon/carbon_fds.go b/ipld/car/v2/carbon/carbon_fds.go index c4cfdd609..856eb9bb3 100644 --- a/ipld/car/v2/carbon/carbon_fds.go +++ b/ipld/car/v2/carbon/carbon_fds.go @@ -1,12 +1,12 @@ package carbon import ( + "github.com/ipld/go-car/v2/carbs" "os" blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" "github.com/ipld/go-car/util" - carbs "github.com/willscott/carbs" ) // carbonFD is a carbon implementation based on having two file handles opened, one appending to the file, and the other @@ -14,7 +14,7 @@ import ( type carbonFD struct { path string writeHandle *poswriter - carbs.Carbs + carbs.BlockStore idx *insertionIndex } diff --git a/ipld/car/v2/carbon/carbon_test.go b/ipld/car/v2/carbon/carbon_test.go index 1d90eae24..a3c7d6d98 100644 --- a/ipld/car/v2/carbon/carbon_test.go +++ b/ipld/car/v2/carbon/carbon_test.go @@ -1,6 +1,8 @@ package carbon_test import ( + "github.com/ipld/go-car/v2/carbon" + "github.com/ipld/go-car/v2/carbs" "io" "math/rand" "os" @@ -8,12 +10,10 @@ import ( "github.com/ipfs/go-cid" "github.com/ipld/go-car" - "github.com/willscott/carbon" - "github.com/willscott/carbs" ) func TestCarbon(t *testing.T) { - f, err := os.Open("test.car") + f, err := os.Open("../carbs/testdata/test.car") if err != nil { t.Skipf("fixture not found: %q", err) return @@ -33,7 +33,7 @@ func TestCarbon(t *testing.T) { t.Fatal(err) } cids := make([]cid.Cid, 0) - for true { + for { b, err := r.Next() if err == io.EOF { break diff --git a/ipld/car/v2/carbon/doc.go b/ipld/car/v2/carbon/doc.go new file mode 100644 index 000000000..a1ce3cc6a --- /dev/null +++ b/ipld/car/v2/carbon/doc.go @@ -0,0 +1,5 @@ +// Package carbon provides a blockstore interface with saved blocks stored to a car-compatible log. +// A carbs index for the resulting file is tracked and can be saved as needed. +// Note, carbon does not support deletion. +// See: https://github.com/ipfs/go-ipfs-blockstore +package carbon diff --git a/ipld/car/v2/carbon/insertionindex.go b/ipld/car/v2/carbon/insertionindex.go index c74857678..2e5d943a3 100644 --- a/ipld/car/v2/carbon/insertionindex.go +++ b/ipld/car/v2/carbon/insertionindex.go @@ -4,13 +4,13 @@ import ( "bytes" "encoding/binary" "fmt" + "github.com/ipld/go-car/v2/carbs" "io" "github.com/ipfs/go-cid" "github.com/multiformats/go-multihash" "github.com/petar/GoLLRB/llrb" cbor "github.com/whyrusleeping/cbor/go" - carbs "github.com/willscott/carbs" ) // IndexInsertion carbs IndexCodec identifier diff --git a/ipld/car/v2/carbon/reader.go b/ipld/car/v2/carbon/reader.go index bc346557a..4e3755491 100644 --- a/ipld/car/v2/carbon/reader.go +++ b/ipld/car/v2/carbon/reader.go @@ -2,17 +2,20 @@ package carbon import "io" +//lint:ignore U1000 The entire carbon package will be reviewed; this is temporary type unatreader struct { io.ReaderAt at int64 } +//lint:ignore U1000 The entire carbon package will be reviewed; this is temporary func (u *unatreader) Read(p []byte) (n int, err error) { n, err = u.ReadAt(p, u.at) u.at = u.at + int64(n) return } +//lint:ignore U1000 The entire carbon package will be reviewed; this is temporary func (u *unatreader) ReadByte() (byte, error) { b := []byte{0} _, err := u.Read(b) diff --git a/ipld/car/v2/carbon/test.car b/ipld/car/v2/carbon/test.car deleted file mode 100644 index 47a61c8c2a7def9bafcc252d3a1a4d12529615f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 479907 zcmdSBRZtz;(yooW>%`q5XmHoy?ry=|-QC@TySuwXfZ!6`JxGAyhwQW0D%N+C|GW6B z&f>a9O{&Ly-nU1O9+NxI*xts*(ZO$tT8#n-*e+)mgz#PvS&t~k82RzAB9>MjOd+o_4OvAu(tjWzIhi2rX3qJ)6ff&J9kP7mV^XFOuvNS(Jd z{qMJ#FJ(8=_wi8BHQ}u=;<6T|Q=!iO zNagh)J~5HNCL!IMHV1)65=PfX6{g=_2mH!-7ERt++QP$L zF9`?O)_d_m#~buJ~qfQNd<{?*_Wvv1wjvydgi3JwF;2fgnvgXnOaj(8EsnL_~UKh{jg9!E! zc+84I=z?-6Rn5l;4CEwW$VA)=+TrX@(`9fOd&f&m8=xi7@U5BpuZN>~jE^d+nv==6Y~5oF#z9g3{DpJZ9+ zqM&7n$6;!v9Wh$eR%gyd5|mWF1?0UjUad|(!F~szc|(H7>(*YkZsVb5g_^aIQqtT3 z>cGB*FMJcYB@ls{NOg*`EY6~`1oxFO^caW zb9d>NuU_afcpyN=P7H`+JOPVCWllW)JZpV3eKbT#AW<(7sMAE zK>YBc_O2;aFRm35{t<7uWR;p;fkY;ifZ*EjZ&eiFk?u^mJ-f8NrxRqaYZ{?0uZ*FWo zn^7jViqYO&QbyBAS{LLQZ1W|iI(m1Vnx2`cgiq(Av18j}s$TjnE_qiMQ+b#~?rXCm zgP#zVZF#(W2tUTKZ#Jz1%C|t()+?gr1}T~=%8^=MXQNdPD%VV5#9Vk3vjC~To4a8w ztT=1gvu1Vq(a-@|4NgW5ZU7-et0g(BrkCc!`p`=dQ$|00*B$Vj_HC~%SR5x>;%sh2 z>S?S?!1XewedO68u!GBRHu3ESDGG}+NV;{%p?J3V^� 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 Tx_{@>Ah$cBb;X>)D9fct)9`U;$XV@y4$E$ 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

hRd> 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

vHSrHL`4Y!))mrU|+My=J*?NCLvdFH?W^2AuF;5 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

ecf&5+ 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 Date: Thu, 17 Jun 2021 12:16:12 +0100 Subject: [PATCH 3425/3817] Remove the copied `carbs` without history Remove the carbs copied without history This commit was moved from ipld/go-car@93bd6235b295cebabf9757c1f2d66b47eb38da2c --- ipld/car/v2/carbs/carbs.go | 301 ---------------------------- ipld/car/v2/carbs/carbs_test.go | 81 -------- ipld/car/v2/carbs/index.go | 89 -------- ipld/car/v2/carbs/indexgobhash.go | 44 ---- ipld/car/v2/carbs/indexhashed.go | 43 ---- ipld/car/v2/carbs/indexsorted.go | 197 ------------------ ipld/car/v2/carbs/reader.go | 20 -- ipld/car/v2/carbs/testdata/test.car | Bin 479907 -> 0 bytes ipld/car/v2/carbs/util/hydrate.go | 49 ----- 9 files changed, 824 deletions(-) delete mode 100644 ipld/car/v2/carbs/carbs.go delete mode 100644 ipld/car/v2/carbs/carbs_test.go delete mode 100644 ipld/car/v2/carbs/index.go delete mode 100644 ipld/car/v2/carbs/indexgobhash.go delete mode 100644 ipld/car/v2/carbs/indexhashed.go delete mode 100644 ipld/car/v2/carbs/indexsorted.go delete mode 100644 ipld/car/v2/carbs/reader.go delete mode 100644 ipld/car/v2/carbs/testdata/test.car delete mode 100644 ipld/car/v2/carbs/util/hydrate.go diff --git a/ipld/car/v2/carbs/carbs.go b/ipld/car/v2/carbs/carbs.go deleted file mode 100644 index 6891d925d..000000000 --- a/ipld/car/v2/carbs/carbs.go +++ /dev/null @@ -1,301 +0,0 @@ -package carbs - -import ( - "bufio" - "bytes" - "context" - "encoding/binary" - "fmt" - "io" - - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" - blockstore "github.com/ipfs/go-ipfs-blockstore" - "github.com/multiformats/go-multihash" - - pb "github.com/cheggaaa/pb/v3" - carv1 "github.com/ipld/go-car" - "github.com/ipld/go-car/util" - "golang.org/x/exp/mmap" -) - -var errNotFound = blockstore.ErrNotFound - -// BlockStore provides a read-only Car Block Store. -type BlockStore struct { - backing io.ReaderAt - idx Index -} - -var _ blockstore.Blockstore = (*BlockStore)(nil) - -func (b *BlockStore) Read(idx int64) (cid.Cid, []byte, error) { - bcid, data, err := util.ReadNode(bufio.NewReader(&unatreader{b.backing, idx})) - return bcid, data, err -} - -// DeleteBlock doesn't delete a block on RO blockstore -func (b *BlockStore) DeleteBlock(_ cid.Cid) error { - return fmt.Errorf("read only") -} - -// Has indicates if the store has a cid -func (b *BlockStore) Has(key cid.Cid) (bool, error) { - offset, err := b.idx.Get(key) - if err != nil { - return false, err - } - uar := unatreader{b.backing, int64(offset)} - _, err = binary.ReadUvarint(&uar) - if err != nil { - return false, err - } - cid, _, err := readCid(b.backing, uar.at) - if err != nil { - return false, err - } - return cid.Equals(key), nil -} - -var cidv0Pref = []byte{0x12, 0x20} - -func readCid(store io.ReaderAt, at int64) (cid.Cid, int, error) { - var tag [2]byte - if _, err := store.ReadAt(tag[:], at); err != nil { - return cid.Undef, 0, err - } - if bytes.Equal(tag[:], cidv0Pref) { - cid0 := make([]byte, 34) - if _, err := store.ReadAt(cid0, at); err != nil { - return cid.Undef, 0, err - } - c, err := cid.Cast(cid0) - return c, 34, err - } - - // assume cidv1 - br := &unatreader{store, at} - vers, err := binary.ReadUvarint(br) - if err != nil { - return cid.Cid{}, 0, err - } - - // TODO: the go-cid package allows version 0 here as well - if vers != 1 { - return cid.Cid{}, 0, fmt.Errorf("invalid cid version number: %d", vers) - } - - codec, err := binary.ReadUvarint(br) - if err != nil { - return cid.Cid{}, 0, err - } - - mhr := multihash.NewReader(br) - h, err := mhr.ReadMultihash() - if err != nil { - return cid.Cid{}, 0, err - } - - return cid.NewCidV1(codec, h), int(br.at - at), nil -} - -// Get gets a block from the store -func (b *BlockStore) Get(key cid.Cid) (blocks.Block, error) { - offset, err := b.idx.Get(key) - if err != nil { - return nil, err - } - entry, bytes, err := b.Read(int64(offset)) - if err != nil { - // TODO replace with logging - fmt.Printf("failed get %d:%v\n", offset, err) - return nil, blockstore.ErrNotFound - } - if !entry.Equals(key) { - return nil, blockstore.ErrNotFound - } - return blocks.NewBlockWithCid(bytes, key) -} - -// GetSize gets how big a item is -func (b *BlockStore) GetSize(key cid.Cid) (int, error) { - idx, err := b.idx.Get(key) - if err != nil { - return -1, err - } - len, err := binary.ReadUvarint(&unatreader{b.backing, int64(idx)}) - if err != nil { - return -1, blockstore.ErrNotFound - } - cid, _, err := readCid(b.backing, int64(idx+len)) - if err != nil { - return 0, err - } - if !cid.Equals(key) { - return -1, blockstore.ErrNotFound - } - // get cid. validate. - return int(len), err -} - -// Put does nothing on a ro store -func (b *BlockStore) Put(blocks.Block) error { - return fmt.Errorf("read only") -} - -// PutMany does nothing on a ro store -func (b *BlockStore) PutMany([]blocks.Block) error { - return fmt.Errorf("read only") -} - -// AllKeysChan returns the list of keys in the store -func (b *BlockStore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { - header, err := carv1.ReadHeader(bufio.NewReader(&unatreader{b.backing, 0})) - if err != nil { - return nil, fmt.Errorf("error reading car header: %w", err) - } - offset, err := carv1.HeaderSize(header) - if err != nil { - return nil, err - } - - ch := make(chan cid.Cid, 5) - go func() { - done := ctx.Done() - - rdr := unatreader{b.backing, int64(offset)} - for { - l, err := binary.ReadUvarint(&rdr) - thisItemForNxt := rdr.at - if err != nil { - return - } - c, _, err := readCid(b.backing, thisItemForNxt) - if err != nil { - return - } - rdr.at = thisItemForNxt + int64(l) - - select { - case ch <- c: - continue - case <-done: - return - } - } - }() - return ch, nil -} - -// HashOnRead does nothing -func (b *BlockStore) HashOnRead(bool) { -} - -// Roots returns the root CIDs of the backing car -func (b *BlockStore) Roots() ([]cid.Cid, error) { - header, err := carv1.ReadHeader(bufio.NewReader(&unatreader{b.backing, 0})) - if err != nil { - return nil, fmt.Errorf("error reading car header: %w", err) - } - return header.Roots, nil -} - -// Load opens a carbs data store, generating an index if it does not exist -func Load(path string, noPersist bool) (*BlockStore, error) { - reader, err := mmap.Open(path) - if err != nil { - return nil, err - } - idx, err := Restore(path) - if err != nil { - idx, err = GenerateIndex(reader, 0, IndexSorted, false) - if err != nil { - return nil, err - } - if !noPersist { - if err = Save(idx, path); err != nil { - return nil, err - } - } - } - obj := BlockStore{ - backing: reader, - idx: idx, - } - return &obj, nil -} - -// Of opens a carbs data store from an existing reader of the base data and index -func Of(backing io.ReaderAt, index Index) *BlockStore { - return &BlockStore{backing, index} -} - -// GenerateIndex provides a low-level interface to create an index over a -// reader to a car stream. -func GenerateIndex(store io.ReaderAt, size int64, codec IndexCodec, verbose bool) (Index, error) { - indexcls, ok := IndexAtlas[codec] - if !ok { - return nil, fmt.Errorf("unknown codec: %#v", codec) - } - - bar := pb.New64(size) - bar.Set(pb.Bytes, true) - bar.Set(pb.Terminal, true) - - bar.Start() - - header, err := carv1.ReadHeader(bufio.NewReader(&unatreader{store, 0})) - if err != nil { - return nil, fmt.Errorf("error reading car header: %w", err) - } - offset, err := carv1.HeaderSize(header) - if err != nil { - return nil, err - } - bar.Add64(int64(offset)) - - index := indexcls() - - records := make([]Record, 0) - rdr := unatreader{store, int64(offset)} - for { - thisItemIdx := rdr.at - l, err := binary.ReadUvarint(&rdr) - bar.Add64(int64(l)) - thisItemForNxt := rdr.at - if err != nil { - if err == io.EOF { - break - } - return nil, err - } - c, _, err := readCid(store, thisItemForNxt) - if err != nil { - return nil, err - } - records = append(records, Record{c, uint64(thisItemIdx)}) - rdr.at = thisItemForNxt + int64(l) - } - - if err := index.Load(records); err != nil { - return nil, err - } - - bar.Finish() - - return index, nil -} - -// Generate walks a car file and generates an index of cid->byte offset in it. -func Generate(path string, codec IndexCodec) error { - store, err := mmap.Open(path) - if err != nil { - return err - } - idx, err := GenerateIndex(store, 0, codec, false) - if err != nil { - return err - } - - return Save(idx, path) -} diff --git a/ipld/car/v2/carbs/carbs_test.go b/ipld/car/v2/carbs/carbs_test.go deleted file mode 100644 index 2aa48fe70..000000000 --- a/ipld/car/v2/carbs/carbs_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package carbs - -import ( - "os" - "testing" -) - -/* -func mkCar() (string, error) { - f, err := ioutil.TempFile(os.TempDir(), "car") - if err != nil { - return "", err - } - defer f.Close() - - ds := mockNodeGetter{ - Nodes: make(map[cid.Cid]format.Node), - } - type linker struct { - Name string - Links []*format.Link - } - cbornode.RegisterCborType(linker{}) - - children := make([]format.Node, 0, 10) - childLinks := make([]*format.Link, 0, 10) - for i := 0; i < 10; i++ { - child, _ := cbornode.WrapObject([]byte{byte(i)}, multihash.SHA2_256, -1) - children = append(children, child) - childLinks = append(childLinks, &format.Link{Name: fmt.Sprintf("child%d", i), Cid: child.Cid()}) - } - b, err := cbornode.WrapObject(linker{Name: "root", Links: childLinks}, multihash.SHA2_256, -1) - if err != nil { - return "", fmt.Errorf("couldn't make cbor node: %v", err) - } - ds.Nodes[b.Cid()] = b - - if err := car.WriteCar(context.Background(), &ds, []cid.Cid{b.Cid()}, f); err != nil { - return "", err - } - - return f.Name(), nil -} -*/ - -func TestIndexRT(t *testing.T) { - /* - carFile, err := mkCar() - if err != nil { - t.Fatal(err) - } - defer os.Remove(carFile) - */ - // TODO use temporary directory to run tests taht work with OS file system to avoid accidental source code modification - carFile := "testdata/test.car" - - cf, err := Load(carFile, false) - if err != nil { - t.Fatal(err) - } - defer os.Remove(carFile + ".idx") - - r, err := cf.Roots() - if err != nil { - t.Fatal(err) - } - if len(r) != 1 { - t.Fatalf("unexpected number of roots: %d", len(r)) - } - if _, err := cf.Get(r[0]); err != nil { - t.Fatalf("failed get: %v", err) - } - - idx, err := Restore(carFile) - if err != nil { - t.Fatalf("failed restore: %v", err) - } - if idx, err := idx.Get(r[0]); idx == 0 || err != nil { - t.Fatalf("bad index: %d %v", idx, err) - } -} diff --git a/ipld/car/v2/carbs/index.go b/ipld/car/v2/carbs/index.go deleted file mode 100644 index 1f381308c..000000000 --- a/ipld/car/v2/carbs/index.go +++ /dev/null @@ -1,89 +0,0 @@ -package carbs - -import ( - "encoding/binary" - "fmt" - "io" - "os" - - "github.com/ipfs/go-cid" - "golang.org/x/exp/mmap" -) - -// IndexCodec is used as a multicodec identifier for carbs index files -type IndexCodec int - -// IndexCodec table is a first var-int in carbs indexes -const ( - IndexHashed IndexCodec = iota + 0x300000 - IndexSorted - IndexSingleSorted - IndexGobHashed -) - -// IndexCls is a constructor for an index type -type IndexCls func() Index - -// IndexAtlas holds known index formats -var IndexAtlas = map[IndexCodec]IndexCls{ - IndexHashed: mkHashed, - IndexSorted: mkSorted, - IndexSingleSorted: mkSingleSorted, - IndexGobHashed: mkGobHashed, -} - -// Record is a pre-processed record of a car item and location. -type Record struct { - cid.Cid - Idx uint64 -} - -// Index provides an interface for figuring out where in the car a given cid begins -type Index interface { - Codec() IndexCodec - Marshal(w io.Writer) error - Unmarshal(r io.Reader) error - Get(cid.Cid) (uint64, error) - Load([]Record) error -} - -// Save writes a generated index for a car at `path` -func Save(i Index, path string) error { - stream, err := os.OpenFile(path+".idx", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0640) - if err != nil { - return err - } - defer stream.Close() - - buf := make([]byte, binary.MaxVarintLen64) - b := binary.PutUvarint(buf, uint64(i.Codec())) - if _, err := stream.Write(buf[:b]); err != nil { - return err - } - return i.Marshal(stream) -} - -// Restore loads an index from an on-disk representation. -func Restore(path string) (Index, error) { - reader, err := mmap.Open(path + ".idx") - if err != nil { - return nil, err - } - - defer reader.Close() - uar := unatreader{reader, 0} - codec, err := binary.ReadUvarint(&uar) - if err != nil { - return nil, err - } - idx, ok := IndexAtlas[IndexCodec(codec)] - if !ok { - return nil, fmt.Errorf("unknown codec: %d", codec) - } - idxInst := idx() - if err := idxInst.Unmarshal(&uar); err != nil { - return nil, err - } - - return idxInst, nil -} diff --git a/ipld/car/v2/carbs/indexgobhash.go b/ipld/car/v2/carbs/indexgobhash.go deleted file mode 100644 index b7dedba0a..000000000 --- a/ipld/car/v2/carbs/indexgobhash.go +++ /dev/null @@ -1,44 +0,0 @@ -package carbs - -import ( - "encoding/gob" - "io" - - "github.com/ipfs/go-cid" -) - -type mapGobIndex map[cid.Cid]uint64 - -func (m *mapGobIndex) Get(c cid.Cid) (uint64, error) { - el, ok := (*m)[c] - if !ok { - return 0, errNotFound - } - return el, nil -} - -func (m *mapGobIndex) Marshal(w io.Writer) error { - e := gob.NewEncoder(w) - return e.Encode(m) -} - -func (m *mapGobIndex) Unmarshal(r io.Reader) error { - d := gob.NewDecoder(r) - return d.Decode(m) -} - -func (m *mapGobIndex) Codec() IndexCodec { - return IndexHashed -} - -func (m *mapGobIndex) Load(rs []Record) error { - for _, r := range rs { - (*m)[r.Cid] = r.Idx - } - return nil -} - -func mkGobHashed() Index { - mi := make(mapGobIndex) - return &mi -} diff --git a/ipld/car/v2/carbs/indexhashed.go b/ipld/car/v2/carbs/indexhashed.go deleted file mode 100644 index f4c04aed0..000000000 --- a/ipld/car/v2/carbs/indexhashed.go +++ /dev/null @@ -1,43 +0,0 @@ -package carbs - -import ( - "io" - - "github.com/ipfs/go-cid" - cbor "github.com/whyrusleeping/cbor/go" -) - -type mapIndex map[cid.Cid]uint64 - -func (m *mapIndex) Get(c cid.Cid) (uint64, error) { - el, ok := (*m)[c] - if !ok { - return 0, errNotFound - } - return el, nil -} - -func (m *mapIndex) Marshal(w io.Writer) error { - return cbor.Encode(w, m) -} - -func (m *mapIndex) Unmarshal(r io.Reader) error { - d := cbor.NewDecoder(r) - return d.Decode(m) -} - -func (m *mapIndex) Codec() IndexCodec { - return IndexHashed -} - -func (m *mapIndex) Load(rs []Record) error { - for _, r := range rs { - (*m)[r.Cid] = r.Idx - } - return nil -} - -func mkHashed() Index { - mi := make(mapIndex) - return &mi -} diff --git a/ipld/car/v2/carbs/indexsorted.go b/ipld/car/v2/carbs/indexsorted.go deleted file mode 100644 index eca593c9f..000000000 --- a/ipld/car/v2/carbs/indexsorted.go +++ /dev/null @@ -1,197 +0,0 @@ -package carbs - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" - "sort" - - "github.com/ipfs/go-cid" - "github.com/multiformats/go-multihash" -) - -type digestRecord struct { - digest []byte - index uint64 -} - -func (d digestRecord) write(buf []byte) { - n := copy(buf[:], d.digest) - binary.LittleEndian.PutUint64(buf[n:], d.index) -} - -type recordSet []digestRecord - -func (r recordSet) Len() int { - return len(r) -} - -func (r recordSet) Less(i, j int) bool { - return bytes.Compare(r[i].digest, r[j].digest) < 0 -} - -func (r recordSet) Swap(i, j int) { - r[i], r[j] = r[j], r[i] -} - -type singleWidthIndex struct { - width int32 - len int64 // in struct, len is #items. when marshaled, it's saved as #bytes. - index []byte -} - -func (s *singleWidthIndex) Codec() IndexCodec { - return IndexSingleSorted -} - -func (s *singleWidthIndex) Marshal(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, s.width); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, int64(len(s.index))); err != nil { - return err - } - _, err := io.Copy(w, bytes.NewBuffer(s.index)) - return err -} - -func (s *singleWidthIndex) Unmarshal(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &s.width); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &s.len); err != nil { - return err - } - s.index = make([]byte, s.len) - s.len /= int64(s.width) - _, err := io.ReadFull(r, s.index) - return err -} - -func (s *singleWidthIndex) Less(i int, digest []byte) bool { - return bytes.Compare(digest[:], s.index[i*int(s.width):((i+1)*int(s.width)-8)]) <= 0 -} - -func (s *singleWidthIndex) Get(c cid.Cid) (uint64, error) { - d, err := multihash.Decode(c.Hash()) - if err != nil { - return 0, err - } - return s.get(d.Digest), nil -} - -func (s *singleWidthIndex) get(d []byte) uint64 { - idx := sort.Search(int(s.len), func(i int) bool { - return s.Less(i, d) - }) - if int64(idx) == s.len { - return 0 - } - if !bytes.Equal(d[:], s.index[idx*int(s.width):(idx+1)*int(s.width)-8]) { - return 0 - } - return binary.LittleEndian.Uint64(s.index[(idx+1)*int(s.width)-8 : (idx+1)*int(s.width)]) -} - -func (s *singleWidthIndex) Load(items []Record) error { - m := make(multiWidthIndex) - if err := m.Load(items); err != nil { - return err - } - if len(m) != 1 { - return fmt.Errorf("unexpected number of cid widths: %d", len(m)) - } - for _, i := range m { - s.index = i.index - s.len = i.len - s.width = i.width - return nil - } - return nil -} - -type multiWidthIndex map[int32]singleWidthIndex - -func (m *multiWidthIndex) Get(c cid.Cid) (uint64, error) { - d, err := multihash.Decode(c.Hash()) - if err != nil { - return 0, err - } - if s, ok := (*m)[int32(len(d.Digest)+8)]; ok { - return s.get(d.Digest), nil - } - return 0, errNotFound -} - -func (m *multiWidthIndex) Codec() IndexCodec { - return IndexSorted -} - -func (m *multiWidthIndex) Marshal(w io.Writer) error { - binary.Write(w, binary.LittleEndian, int32(len(*m))) - for _, s := range *m { - if err := s.Marshal(w); err != nil { - return err - } - } - return nil -} - -func (m *multiWidthIndex) Unmarshal(r io.Reader) error { - var l int32 - binary.Read(r, binary.LittleEndian, &l) - for i := 0; i < int(l); i++ { - s := singleWidthIndex{} - if err := s.Unmarshal(r); err != nil { - return err - } - (*m)[s.width] = s - } - return nil -} - -func (m *multiWidthIndex) Load(items []Record) error { - // Split cids on their digest length - idxs := make(map[int][]digestRecord) - for _, item := range items { - decHash, err := multihash.Decode(item.Hash()) - if err != nil { - return err - } - digest := decHash.Digest - idx, ok := idxs[len(digest)] - if !ok { - idxs[len(digest)] = make([]digestRecord, 0) - idx = idxs[len(digest)] - } - idxs[len(digest)] = append(idx, digestRecord{digest, item.Idx}) - } - - // Sort each list. then write to compact form. - for width, lst := range idxs { - sort.Sort(recordSet(lst)) - rcrdWdth := width + 8 - compact := make([]byte, rcrdWdth*len(lst)) - for off, itm := range lst { - itm.write(compact[off*rcrdWdth : (off+1)*rcrdWdth]) - } - s := singleWidthIndex{ - width: int32(rcrdWdth), - len: int64(len(lst)), - index: compact, - } - (*m)[int32(width)+8] = s - } - return nil -} - -func mkSorted() Index { - m := make(multiWidthIndex) - return &m -} - -func mkSingleSorted() Index { - s := singleWidthIndex{} - return &s -} diff --git a/ipld/car/v2/carbs/reader.go b/ipld/car/v2/carbs/reader.go deleted file mode 100644 index 8accf0a84..000000000 --- a/ipld/car/v2/carbs/reader.go +++ /dev/null @@ -1,20 +0,0 @@ -package carbs - -import "io" - -type unatreader struct { - io.ReaderAt - at int64 -} - -func (u *unatreader) Read(p []byte) (n int, err error) { - n, err = u.ReadAt(p, u.at) - u.at = u.at + int64(n) - return -} - -func (u *unatreader) ReadByte() (byte, error) { - b := []byte{0} - _, err := u.Read(b) - return b[0], err -} diff --git a/ipld/car/v2/carbs/testdata/test.car b/ipld/car/v2/carbs/testdata/test.car deleted file mode 100644 index 47a61c8c2a7def9bafcc252d3a1a4d12529615f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 479907 zcmdSBRZtz;(yooW>%`q5XmHoy?ry=|-QC@TySuwXfZ!6`JxGAyhwQW0D%N+C|GW6B z&f>a9O{&Ly-nU1O9+NxI*xts*(ZO$tT8#n-*e+)mgz#PvS&t~k82RzAB9>MjOd+o_4OvAu(tjWzIhi2rX3qJ)6ff&J9kP7mV^XFOuvNS(Jd z{qMJ#FJ(8=_wi8BHQ}u=;<6T|Q=!iO zNagh)J~5HNCL!IMHV1)65=PfX6{g=_2mH!-7ERt++QP$L zF9`?O)_d_m#~buJ~qfQNd<{?*_Wvv1wjvydgi3JwF;2fgnvgXnOaj(8EsnL_~UKh{jg9!E! zc+84I=z?-6Rn5l;4CEwW$VA)=+TrX@(`9fOd&f&m8=xi7@U5BpuZN>~jE^d+nv==6Y~5oF#z9g3{DpJZ9+ zqM&7n$6;!v9Wh$eR%gyd5|mWF1?0UjUad|(!F~szc|(H7>(*YkZsVb5g_^aIQqtT3 z>cGB*FMJcYB@ls{NOg*`EY6~`1oxFO^caW zb9d>NuU_afcpyN=P7H`+JOPVCWllW)JZpV3eKbT#AW<(7sMAE zK>YBc_O2;aFRm35{t<7uWR;p;fkY;ifZ*EjZ&eiFk?u^mJ-f8NrxRqaYZ{?0uZ*FWo zn^7jViqYO&QbyBAS{LLQZ1W|iI(m1Vnx2`cgiq(Av18j}s$TjnE_qiMQ+b#~?rXCm zgP#zVZF#(W2tUTKZ#Jz1%C|t()+?gr1}T~=%8^=MXQNdPD%VV5#9Vk3vjC~To4a8w ztT=1gvu1Vq(a-@|4NgW5ZU7-et0g(BrkCc!`p`=dQ$|00*B$Vj_HC~%SR5x>;%sh2 z>S?S?!1XewedO68u!GBRHu3ESDGG}+NV;{%p?J3V^� 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 Tx_{@>Ah$cBb;X>)D9fct)9`U;$XV@y4$E$ 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

hRd> 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

vHSrHL`4Y!))mrU|+My=J*?NCLvdFH?W^2AuF;5 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

ecf&5+ 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 [codec]\n") - return - } - db := os.Args[1] - codec := carbs.IndexSorted - if len(os.Args) == 3 { - if os.Args[2] == "Hash" { - codec = carbs.IndexHashed - } else if os.Args[2] == "GobHash" { - codec = carbs.IndexGobHashed - } - } - - dbBacking, err := mmap.Open(db) - if err != nil { - fmt.Printf("Error Opening car for hydration: %v\n", err) - return - } - - dbstat, err := os.Stat(db) - if err != nil { - fmt.Printf("Error statting car for hydration: %v\n", err) - return - } - - idx, err := carbs.GenerateIndex(dbBacking, dbstat.Size(), codec, true) - if err != nil { - fmt.Printf("Error generating index: %v\n", err) - return - } - - fmt.Printf("Saving...\n") - - if err := carbs.Save(idx, db); err != nil { - fmt.Printf("Error saving : %v\n", err) - } -} From 22af2e3857cf44373cb8f834068665e402eca7cc Mon Sep 17 00:00:00 2001 From: Will Scott Date: Wed, 7 Oct 2020 20:09:38 -0700 Subject: [PATCH 3426/3817] original willscott/carbs history in squashed form partial finish interface license / readme add cli for hydration indirection extensible indexes fixed int sizes additional typed int expose car Roots fix issues with index restoration add gob-based hash index typo in util/hydrate Create go.yml Create codeql-analysis.yml Create dependabot.yml work on testing round trip test passes mmap idx fix issue in sorted index gets add progress bar This commit was moved from ipld/go-car@efbdc872e95deb81238b263f3689587d4a06a200 --- ipld/car/v2/carbs/LICENSE | 202 ++++++++++++++++++++ ipld/car/v2/carbs/LICENSE-MIT | 21 +++ ipld/car/v2/carbs/README.md | 13 ++ ipld/car/v2/carbs/carbs.go | 295 ++++++++++++++++++++++++++++++ ipld/car/v2/carbs/carbs_test.go | 108 +++++++++++ ipld/car/v2/carbs/index.go | 89 +++++++++ ipld/car/v2/carbs/indexgobhash.go | 44 +++++ ipld/car/v2/carbs/indexhashed.go | 43 +++++ ipld/car/v2/carbs/indexsorted.go | 197 ++++++++++++++++++++ ipld/car/v2/carbs/reader.go | 20 ++ ipld/car/v2/carbs/test.car | Bin 0 -> 479907 bytes ipld/car/v2/carbs/util/hydrate.go | 51 ++++++ 12 files changed, 1083 insertions(+) create mode 100644 ipld/car/v2/carbs/LICENSE create mode 100644 ipld/car/v2/carbs/LICENSE-MIT create mode 100644 ipld/car/v2/carbs/README.md create mode 100644 ipld/car/v2/carbs/carbs.go create mode 100644 ipld/car/v2/carbs/carbs_test.go create mode 100644 ipld/car/v2/carbs/index.go create mode 100644 ipld/car/v2/carbs/indexgobhash.go create mode 100644 ipld/car/v2/carbs/indexhashed.go create mode 100644 ipld/car/v2/carbs/indexsorted.go create mode 100644 ipld/car/v2/carbs/reader.go create mode 100644 ipld/car/v2/carbs/test.car create mode 100644 ipld/car/v2/carbs/util/hydrate.go diff --git a/ipld/car/v2/carbs/LICENSE b/ipld/car/v2/carbs/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/ipld/car/v2/carbs/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ipld/car/v2/carbs/LICENSE-MIT b/ipld/car/v2/carbs/LICENSE-MIT new file mode 100644 index 000000000..8c6ca2a25 --- /dev/null +++ b/ipld/car/v2/carbs/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Will Scott + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ipld/car/v2/carbs/README.md b/ipld/car/v2/carbs/README.md new file mode 100644 index 000000000..9aec21a2c --- /dev/null +++ b/ipld/car/v2/carbs/README.md @@ -0,0 +1,13 @@ +🔠Carbs +=== + +Car Blockstore provides a read-only blockstore interface directly reading out of a car file. + + +License +--- + +Carbs is dual-licensed under Apache 2.0 and MIT terms: + + Apache License, Version 2.0, (LICENSE or http://www.apache.org/licenses/LICENSE-2.0) + MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) diff --git a/ipld/car/v2/carbs/carbs.go b/ipld/car/v2/carbs/carbs.go new file mode 100644 index 000000000..c59e5d892 --- /dev/null +++ b/ipld/car/v2/carbs/carbs.go @@ -0,0 +1,295 @@ +package carbs + +import ( + "bufio" + "bytes" + "context" + "encoding/binary" + "fmt" + "io" + + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + bs "github.com/ipfs/go-ipfs-blockstore" + "github.com/multiformats/go-multihash" + + pb "github.com/cheggaaa/pb/v3" + car "github.com/ipld/go-car" + "github.com/ipld/go-car/util" + "golang.org/x/exp/mmap" +) + +var errNotFound = bs.ErrNotFound + +// Carbs provides a read-only Car Block Store. +type Carbs struct { + backing io.ReaderAt + idx Index +} + +var _ bs.Blockstore = (*Carbs)(nil) + +func (c *Carbs) Read(idx int64) (cid.Cid, []byte, error) { + return util.ReadNode(bufio.NewReader(&unatreader{c.backing, idx})) +} + +// DeleteBlock doesn't delete a block on RO blockstore +func (c *Carbs) DeleteBlock(_ cid.Cid) error { + return fmt.Errorf("read only") +} + +// Has indicates if the store has a cid +func (c *Carbs) Has(key cid.Cid) (bool, error) { + offset, err := c.idx.Get(key) + if err != nil { + return false, err + } + uar := unatreader{c.backing, int64(offset)} + _, err = binary.ReadUvarint(&uar) + if err != nil { + return false, err + } + cid, _, err := readCid(c.backing, uar.at) + if err != nil { + return false, err + } + return cid.Equals(key), nil +} + +var cidv0Pref = []byte{0x12, 0x20} + +func readCid(store io.ReaderAt, at int64) (cid.Cid, int, error) { + var tag [2]byte + if _, err := store.ReadAt(tag[:], at); err != nil { + return cid.Undef, 0, err + } + if bytes.Equal(tag[:], cidv0Pref) { + cid0 := make([]byte, 34) + if _, err := store.ReadAt(cid0, at); err != nil { + return cid.Undef, 0, err + } + c, err := cid.Cast(cid0) + return c, 34, err + } + + // assume cidv1 + br := &unatreader{store, at} + vers, err := binary.ReadUvarint(br) + if err != nil { + return cid.Cid{}, 0, err + } + + // TODO: the go-cid package allows version 0 here as well + if vers != 1 { + return cid.Cid{}, 0, fmt.Errorf("invalid cid version number: %d", vers) + } + + codec, err := binary.ReadUvarint(br) + if err != nil { + return cid.Cid{}, 0, err + } + + mhr := multihash.NewReader(br) + h, err := mhr.ReadMultihash() + if err != nil { + return cid.Cid{}, 0, err + } + + return cid.NewCidV1(codec, h), int(br.at - at), nil +} + +// Get gets a block from the store +func (c *Carbs) Get(key cid.Cid) (blocks.Block, error) { + offset, err := c.idx.Get(key) + if err != nil { + return nil, err + } + entry, bytes, err := c.Read(int64(offset)) + if err != nil { + fmt.Printf("failed get %d:%v\n", offset, err) + return nil, bs.ErrNotFound + } + if !entry.Equals(key) { + return nil, bs.ErrNotFound + } + return blocks.NewBlockWithCid(bytes, key) +} + +// GetSize gets how big a item is +func (c *Carbs) GetSize(key cid.Cid) (int, error) { + idx, err := c.idx.Get(key) + if err != nil { + return -1, err + } + len, err := binary.ReadUvarint(&unatreader{c.backing, int64(idx)}) + if err != nil { + return -1, bs.ErrNotFound + } + cid, _, err := readCid(c.backing, int64(idx+len)) + if err != nil { + return 0, err + } + if !cid.Equals(key) { + return -1, bs.ErrNotFound + } + // get cid. validate. + return int(len), err +} + +// Put does nothing on a ro store +func (c *Carbs) Put(blocks.Block) error { + return fmt.Errorf("read only") +} + +// PutMany does nothing on a ro store +func (c *Carbs) PutMany([]blocks.Block) error { + return fmt.Errorf("read only") +} + +// AllKeysChan returns the list of keys in the store +func (c *Carbs) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { + header, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) + if err != nil { + return nil, fmt.Errorf("Error reading car header: %w", err) + } + offset, err := car.HeaderSize(header) + if err != nil { + return nil, err + } + + ch := make(chan cid.Cid, 5) + go func() { + done := ctx.Done() + + rdr := unatreader{c.backing, int64(offset)} + for true { + l, err := binary.ReadUvarint(&rdr) + thisItemForNxt := rdr.at + if err != nil { + return + } + c, _, err := readCid(c.backing, thisItemForNxt) + if err != nil { + return + } + rdr.at = thisItemForNxt + int64(l) + + select { + case ch <- c: + continue + case <-done: + return + } + } + }() + return ch, nil +} + +// HashOnRead does nothing +func (c *Carbs) HashOnRead(enabled bool) { + return +} + +// Roots returns the root CIDs of the backing car +func (c *Carbs) Roots() ([]cid.Cid, error) { + header, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) + if err != nil { + return nil, fmt.Errorf("Error reading car header: %w", err) + } + return header.Roots, nil +} + +// Load opens a carbs data store, generating an index if it does not exist +func Load(path string, noPersist bool) (*Carbs, error) { + reader, err := mmap.Open(path) + if err != nil { + return nil, err + } + idx, err := Restore(path) + if err != nil { + idx, err = GenerateIndex(reader, 0, IndexSorted, false) + if err != nil { + return nil, err + } + if !noPersist { + if err = Save(idx, path); err != nil { + return nil, err + } + } + } + obj := Carbs{ + backing: reader, + idx: idx, + } + return &obj, nil +} + +// GenerateIndex provides a low-level interface to create an index over a +// reader to a car stream. +func GenerateIndex(store io.ReaderAt, size int64, codec IndexCodec, verbose bool) (Index, error) { + indexcls, ok := IndexAtlas[codec] + if !ok { + return nil, fmt.Errorf("unknown codec: %#v", codec) + } + + bar := pb.New64(size) + bar.Set(pb.Bytes, true) + bar.Set(pb.Terminal, true) + + bar.Start() + + header, err := car.ReadHeader(bufio.NewReader(&unatreader{store, 0})) + if err != nil { + return nil, fmt.Errorf("Error reading car header: %w", err) + } + offset, err := car.HeaderSize(header) + if err != nil { + return nil, err + } + bar.Add64(int64(offset)) + + index := indexcls() + + records := make([]Record, 0) + rdr := unatreader{store, int64(offset)} + for true { + thisItemIdx := rdr.at + l, err := binary.ReadUvarint(&rdr) + bar.Add64(int64(l)) + thisItemForNxt := rdr.at + if err != nil { + if err == io.EOF { + break + } + return nil, err + } + c, _, err := readCid(store, thisItemForNxt) + if err != nil { + return nil, err + } + records = append(records, Record{c, uint64(thisItemIdx)}) + rdr.at = thisItemForNxt + int64(l) + } + + if err := index.Load(records); err != nil { + return nil, err + } + + bar.Finish() + + return index, nil +} + +// Generate walks a car file and generates an index of cid->byte offset in it. +func Generate(path string, codec IndexCodec) error { + store, err := mmap.Open(path) + if err != nil { + return err + } + idx, err := GenerateIndex(store, 0, codec, false) + if err != nil { + return err + } + + return Save(idx, path) +} diff --git a/ipld/car/v2/carbs/carbs_test.go b/ipld/car/v2/carbs/carbs_test.go new file mode 100644 index 000000000..5ec52e972 --- /dev/null +++ b/ipld/car/v2/carbs/carbs_test.go @@ -0,0 +1,108 @@ +package carbs + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/ipfs/go-cid" + format "github.com/ipfs/go-ipld-format" +) + +/* +func mkCar() (string, error) { + f, err := ioutil.TempFile(os.TempDir(), "car") + if err != nil { + return "", err + } + defer f.Close() + + ds := mockNodeGetter{ + Nodes: make(map[cid.Cid]format.Node), + } + type linker struct { + Name string + Links []*format.Link + } + cbornode.RegisterCborType(linker{}) + + children := make([]format.Node, 0, 10) + childLinks := make([]*format.Link, 0, 10) + for i := 0; i < 10; i++ { + child, _ := cbornode.WrapObject([]byte{byte(i)}, multihash.SHA2_256, -1) + children = append(children, child) + childLinks = append(childLinks, &format.Link{Name: fmt.Sprintf("child%d", i), Cid: child.Cid()}) + } + b, err := cbornode.WrapObject(linker{Name: "root", Links: childLinks}, multihash.SHA2_256, -1) + if err != nil { + return "", fmt.Errorf("couldn't make cbor node: %v", err) + } + ds.Nodes[b.Cid()] = b + + if err := car.WriteCar(context.Background(), &ds, []cid.Cid{b.Cid()}, f); err != nil { + return "", err + } + + return f.Name(), nil +} +*/ + +func TestIndexRT(t *testing.T) { + /* + carFile, err := mkCar() + if err != nil { + t.Fatal(err) + } + defer os.Remove(carFile) + */ + carFile := "test.car" + + cf, err := Load(carFile, false) + if err != nil { + t.Fatal(err) + } + defer os.Remove(carFile + ".idx") + + r, err := cf.Roots() + if err != nil { + t.Fatal(err) + } + if len(r) != 1 { + t.Fatalf("unexpected number of roots: %d", len(r)) + } + if _, err := cf.Get(r[0]); err != nil { + t.Fatalf("failed get: %v", err) + } + + idx, err := Restore(carFile) + if err != nil { + t.Fatalf("failed restore: %v", err) + } + if idx, err := idx.Get(r[0]); idx == 0 || err != nil { + t.Fatalf("bad index: %d %v", idx, err) + } +} + +type mockNodeGetter struct { + Nodes map[cid.Cid]format.Node +} + +func (m *mockNodeGetter) Get(_ context.Context, c cid.Cid) (format.Node, error) { + n, ok := m.Nodes[c] + if !ok { + return nil, fmt.Errorf("unknown node") + } + return n, nil +} + +func (m *mockNodeGetter) GetMany(_ context.Context, cs []cid.Cid) <-chan *format.NodeOption { + ch := make(chan *format.NodeOption, 5) + go func() { + for _, c := range cs { + n, e := m.Get(nil, c) + ch <- &format.NodeOption{Node: n, Err: e} + } + }() + return ch +} diff --git a/ipld/car/v2/carbs/index.go b/ipld/car/v2/carbs/index.go new file mode 100644 index 000000000..b13baa9bb --- /dev/null +++ b/ipld/car/v2/carbs/index.go @@ -0,0 +1,89 @@ +package carbs + +import ( + "encoding/binary" + "fmt" + "io" + "os" + + "github.com/ipfs/go-cid" + "golang.org/x/exp/mmap" +) + +// IndexCodec is used as a multicodec identifier for carbs index files +type IndexCodec int + +// IndexCodec table is a first var-int in carbs indexes +const ( + IndexHashed IndexCodec = iota + 0x300000 + IndexSorted + IndexSingleSorted + IndexGobHashed +) + +// IndexCls is a constructor for an index type +type IndexCls func() Index + +// IndexAtlas holds known index formats +var IndexAtlas = map[IndexCodec]IndexCls{ + IndexHashed: mkHashed, + IndexSorted: mkSorted, + IndexSingleSorted: mkSingleSorted, + IndexGobHashed: mkGobHashed, +} + +// Record is a pre-processed record of a car item and location. +type Record struct { + cid.Cid + idx uint64 +} + +// Index provides an interface for figuring out where in the car a given cid begins +type Index interface { + Codec() IndexCodec + Marshal(w io.Writer) error + Unmarshal(r io.Reader) error + Get(cid.Cid) (uint64, error) + Load([]Record) error +} + +// Save writes a generated index for a car at `path` +func Save(i Index, path string) error { + stream, err := os.OpenFile(path+".idx", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0640) + if err != nil { + return err + } + defer stream.Close() + + buf := make([]byte, binary.MaxVarintLen64) + b := binary.PutUvarint(buf, uint64(i.Codec())) + if _, err := stream.Write(buf[:b]); err != nil { + return err + } + return i.Marshal(stream) +} + +// Restore loads an index from an on-disk representation. +func Restore(path string) (Index, error) { + reader, err := mmap.Open(path + ".idx") + if err != nil { + return nil, err + } + + defer reader.Close() + uar := unatreader{reader, 0} + codec, err := binary.ReadUvarint(&uar) + if err != nil { + return nil, err + } + idx, ok := IndexAtlas[IndexCodec(codec)] + if !ok { + return nil, fmt.Errorf("Unknown codec: %d", codec) + } + idxInst := idx() + if err := idxInst.Unmarshal(&uar); err != nil { + return nil, err + } + + return idxInst, nil +} diff --git a/ipld/car/v2/carbs/indexgobhash.go b/ipld/car/v2/carbs/indexgobhash.go new file mode 100644 index 000000000..51ba4c699 --- /dev/null +++ b/ipld/car/v2/carbs/indexgobhash.go @@ -0,0 +1,44 @@ +package carbs + +import ( + "encoding/gob" + "io" + + "github.com/ipfs/go-cid" +) + +type mapGobIndex map[cid.Cid]uint64 + +func (m *mapGobIndex) Get(c cid.Cid) (uint64, error) { + el, ok := (*m)[c] + if !ok { + return 0, errNotFound + } + return el, nil +} + +func (m *mapGobIndex) Marshal(w io.Writer) error { + e := gob.NewEncoder(w) + return e.Encode(m) +} + +func (m *mapGobIndex) Unmarshal(r io.Reader) error { + d := gob.NewDecoder(r) + return d.Decode(m) +} + +func (m *mapGobIndex) Codec() IndexCodec { + return IndexHashed +} + +func (m *mapGobIndex) Load(rs []Record) error { + for _, r := range rs { + (*m)[r.Cid] = r.idx + } + return nil +} + +func mkGobHashed() Index { + mi := make(mapGobIndex) + return &mi +} diff --git a/ipld/car/v2/carbs/indexhashed.go b/ipld/car/v2/carbs/indexhashed.go new file mode 100644 index 000000000..62bfce400 --- /dev/null +++ b/ipld/car/v2/carbs/indexhashed.go @@ -0,0 +1,43 @@ +package carbs + +import ( + "io" + + "github.com/ipfs/go-cid" + cbor "github.com/whyrusleeping/cbor/go" +) + +type mapIndex map[cid.Cid]uint64 + +func (m *mapIndex) Get(c cid.Cid) (uint64, error) { + el, ok := (*m)[c] + if !ok { + return 0, errNotFound + } + return el, nil +} + +func (m *mapIndex) Marshal(w io.Writer) error { + return cbor.Encode(w, m) +} + +func (m *mapIndex) Unmarshal(r io.Reader) error { + d := cbor.NewDecoder(r) + return d.Decode(m) +} + +func (m *mapIndex) Codec() IndexCodec { + return IndexHashed +} + +func (m *mapIndex) Load(rs []Record) error { + for _, r := range rs { + (*m)[r.Cid] = r.idx + } + return nil +} + +func mkHashed() Index { + mi := make(mapIndex) + return &mi +} diff --git a/ipld/car/v2/carbs/indexsorted.go b/ipld/car/v2/carbs/indexsorted.go new file mode 100644 index 000000000..6378df34c --- /dev/null +++ b/ipld/car/v2/carbs/indexsorted.go @@ -0,0 +1,197 @@ +package carbs + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "sort" + + "github.com/ipfs/go-cid" + "github.com/multiformats/go-multihash" +) + +type digestRecord struct { + digest []byte + index uint64 +} + +func (d digestRecord) write(buf []byte) { + n := copy(buf[:], d.digest) + binary.LittleEndian.PutUint64(buf[n:], d.index) +} + +type recordSet []digestRecord + +func (r recordSet) Len() int { + return len(r) +} + +func (r recordSet) Less(i, j int) bool { + return bytes.Compare(r[i].digest, r[j].digest) < 0 +} + +func (r recordSet) Swap(i, j int) { + r[i], r[j] = r[j], r[i] +} + +type singleWidthIndex struct { + width int32 + len int64 // in struct, len is #items. when marshaled, it's saved as #bytes. + index []byte +} + +func (s *singleWidthIndex) Codec() IndexCodec { + return IndexSingleSorted +} + +func (s *singleWidthIndex) Marshal(w io.Writer) error { + if err := binary.Write(w, binary.LittleEndian, s.width); err != nil { + return err + } + if err := binary.Write(w, binary.LittleEndian, int64(len(s.index))); err != nil { + return err + } + _, err := io.Copy(w, bytes.NewBuffer(s.index)) + return err +} + +func (s *singleWidthIndex) Unmarshal(r io.Reader) error { + if err := binary.Read(r, binary.LittleEndian, &s.width); err != nil { + return err + } + if err := binary.Read(r, binary.LittleEndian, &s.len); err != nil { + return err + } + s.index = make([]byte, s.len) + s.len /= int64(s.width) + _, err := io.ReadFull(r, s.index) + return err +} + +func (s *singleWidthIndex) Less(i int, digest []byte) bool { + return bytes.Compare(digest[:], s.index[i*int(s.width):((i+1)*int(s.width)-8)]) <= 0 +} + +func (s *singleWidthIndex) Get(c cid.Cid) (uint64, error) { + d, err := multihash.Decode(c.Hash()) + if err != nil { + return 0, err + } + return s.get(d.Digest), nil +} + +func (s *singleWidthIndex) get(d []byte) uint64 { + idx := sort.Search(int(s.len), func(i int) bool { + return s.Less(i, d) + }) + if int64(idx) == s.len { + return 0 + } + if bytes.Compare(d[:], s.index[idx*int(s.width):(idx+1)*int(s.width)-8]) != 0 { + return 0 + } + return binary.LittleEndian.Uint64(s.index[(idx+1)*int(s.width)-8 : (idx+1)*int(s.width)]) +} + +func (s *singleWidthIndex) Load(items []Record) error { + m := make(multiWidthIndex) + if err := m.Load(items); err != nil { + return err + } + if len(m) != 1 { + return fmt.Errorf("unexpected number of cid widths: %d", len(m)) + } + for _, i := range m { + s.index = i.index + s.len = i.len + s.width = i.width + return nil + } + return nil +} + +type multiWidthIndex map[int32]singleWidthIndex + +func (m *multiWidthIndex) Get(c cid.Cid) (uint64, error) { + d, err := multihash.Decode(c.Hash()) + if err != nil { + return 0, err + } + if s, ok := (*m)[int32(len(d.Digest)+8)]; ok { + return s.get(d.Digest), nil + } + return 0, errNotFound +} + +func (m *multiWidthIndex) Codec() IndexCodec { + return IndexSorted +} + +func (m *multiWidthIndex) Marshal(w io.Writer) error { + binary.Write(w, binary.LittleEndian, int32(len(*m))) + for _, s := range *m { + if err := s.Marshal(w); err != nil { + return err + } + } + return nil +} + +func (m *multiWidthIndex) Unmarshal(r io.Reader) error { + var l int32 + binary.Read(r, binary.LittleEndian, &l) + for i := 0; i < int(l); i++ { + s := singleWidthIndex{} + if err := s.Unmarshal(r); err != nil { + return err + } + (*m)[s.width] = s + } + return nil +} + +func (m *multiWidthIndex) Load(items []Record) error { + // Split cids on their digest length + idxs := make(map[int][]digestRecord) + for _, item := range items { + decHash, err := multihash.Decode(item.Hash()) + if err != nil { + return err + } + digest := decHash.Digest + idx, ok := idxs[len(digest)] + if !ok { + idxs[len(digest)] = make([]digestRecord, 0) + idx = idxs[len(digest)] + } + idxs[len(digest)] = append(idx, digestRecord{digest, item.idx}) + } + + // Sort each list. then write to compact form. + for width, lst := range idxs { + sort.Sort(recordSet(lst)) + rcrdWdth := width + 8 + compact := make([]byte, rcrdWdth*len(lst)) + for off, itm := range lst { + itm.write(compact[off*rcrdWdth : (off+1)*rcrdWdth]) + } + s := singleWidthIndex{ + width: int32(rcrdWdth), + len: int64(len(lst)), + index: compact, + } + (*m)[int32(width)+8] = s + } + return nil +} + +func mkSorted() Index { + m := make(multiWidthIndex) + return &m +} + +func mkSingleSorted() Index { + s := singleWidthIndex{} + return &s +} diff --git a/ipld/car/v2/carbs/reader.go b/ipld/car/v2/carbs/reader.go new file mode 100644 index 000000000..8accf0a84 --- /dev/null +++ b/ipld/car/v2/carbs/reader.go @@ -0,0 +1,20 @@ +package carbs + +import "io" + +type unatreader struct { + io.ReaderAt + at int64 +} + +func (u *unatreader) Read(p []byte) (n int, err error) { + n, err = u.ReadAt(p, u.at) + u.at = u.at + int64(n) + return +} + +func (u *unatreader) ReadByte() (byte, error) { + b := []byte{0} + _, err := u.Read(b) + return b[0], err +} diff --git a/ipld/car/v2/carbs/test.car b/ipld/car/v2/carbs/test.car new file mode 100644 index 0000000000000000000000000000000000000000..47a61c8c2a7def9bafcc252d3a1a4d12529615f5 GIT binary patch literal 479907 zcmdSBRZtz;(yooW>%`q5XmHoy?ry=|-QC@TySuwXfZ!6`JxGAyhwQW0D%N+C|GW6B z&f>a9O{&Ly-nU1O9+NxI*xts*(ZO$tT8#n-*e+)mgz#PvS&t~k82RzAB9>MjOd+o_4OvAu(tjWzIhi2rX3qJ)6ff&J9kP7mV^XFOuvNS(Jd z{qMJ#FJ(8=_wi8BHQ}u=;<6T|Q=!iO zNagh)J~5HNCL!IMHV1)65=PfX6{g=_2mH!-7ERt++QP$L zF9`?O)_d_m#~buJ~qfQNd<{?*_Wvv1wjvydgi3JwF;2fgnvgXnOaj(8EsnL_~UKh{jg9!E! zc+84I=z?-6Rn5l;4CEwW$VA)=+TrX@(`9fOd&f&m8=xi7@U5BpuZN>~jE^d+nv==6Y~5oF#z9g3{DpJZ9+ zqM&7n$6;!v9Wh$eR%gyd5|mWF1?0UjUad|(!F~szc|(H7>(*YkZsVb5g_^aIQqtT3 z>cGB*FMJcYB@ls{NOg*`EY6~`1oxFO^caW zb9d>NuU_afcpyN=P7H`+JOPVCWllW)JZpV3eKbT#AW<(7sMAE zK>YBc_O2;aFRm35{t<7uWR;p;fkY;ifZ*EjZ&eiFk?u^mJ-f8NrxRqaYZ{?0uZ*FWo zn^7jViqYO&QbyBAS{LLQZ1W|iI(m1Vnx2`cgiq(Av18j}s$TjnE_qiMQ+b#~?rXCm zgP#zVZF#(W2tUTKZ#Jz1%C|t()+?gr1}T~=%8^=MXQNdPD%VV5#9Vk3vjC~To4a8w ztT=1gvu1Vq(a-@|4NgW5ZU7-et0g(BrkCc!`p`=dQ$|00*B$Vj_HC~%SR5x>;%sh2 z>S?S?!1XewedO68u!GBRHu3ESDGG}+NV;{%p?J3V^� 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 Tx_{@>Ah$cBb;X>)D9fct)9`U;$XV@y4$E$ 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

hRd> zxIQPp`+1js7cr;A2G9Sz#Cb*_ZChlr4a28WU<}i_e zuBDVaJs))P(_~?NiyW>e0sKw|^8J9ysCp2xuN>~eXfYP}L34T0&Vl@^Y>%3Ak2Gc2 z=2WYe5D?fq2^Q{$PoR`I^N>b4&(CSKn(E<7H*fFteEu3mG(ELNVuKBMD)3?Hzi;RA z0KgT9_q}C4#xxE125~RlKd(fbZcWBo(;Ny0G{(BW#;AQLH}OsrS0;jtccn82Pi`8m zEVJKD9UY>B$9?dWF8`W1|0ti&FbF%@J+DSS*eufMZM#DeoKiQ_{s0Lx8W~@KF;K$S7sZKXveAYNqj>VEwyjh zygOO+_X9ij@pQ*bD0J)s$?U!zguw1HfBgabp*jpTv(UqwVa5Q9f*2)ETS{+U@ z(91|c9d%iy-5|-dTY~SupRGgCbbb;L)@nP3bQs(wvRJUogv;<93B(NK;WogNHfyx?;$PWbpeo}586beLoy^){uPPtQkWyUmd z$P0x9GS9s>RIS~?8t659XIsut5g!zI3esTO)QVSY0pzL?RS^tp7o2;#rMVYqy|@*e zfHvkiYLHiGQIL?JY%H@7m`u!Q--ErdL68Fk2>QifGy2$u5FWF68d%;$_o}+ro(L8;f zRQlq(ABEhnECtxP{tK?lY5d0~_5Gl(SFi}^g3m4pz$y^*@%E83Zv?g%A(46jxTayS zK7AnO*@ME}P-uVA)V0<3)Pf;5(p!REzqxxf=6nUn;5|-`IX@a^sSo17!$JpG^R2QK}`xx z^{s*JMhXeM>udPHaGON`n6^Yyph50n-(ocNRFGySSY%w`;&C;}3tWx2z4UA> zC(;!N_9`ZAJ!xG)-RtIwUsxGxlt~$sI}V{$Z)#vkHHs`M{0r0GG%N%OGIu`?lZj29 zf3`$-Ji{ss+T|~KnNQvVrueu&JuXcTi7nrF^<|ulh6{qI9=-z z=)o<%q>sL2=1SOT|H`s2yq;#oJF5EC=d>Q>Q=IhGv9{GttrrT`0mr7#fXOm136)>5IZ;*)F#M_@$iNDNk95?mDo+;AV^ku7jA=+UawDfSY|N3OeBf2{0HWD@7 zx3fO~OVTQ_JSBv7Y=$#McEod(Sq{+Fi!S84MOeTVJrKF0b4wp)B z{3ef03I_DC!c%0MoVLgdr%0HkhW~pQ97Kw^l7LqT-FCvR&uuOVBfzL zlh;5$s7o}Y={-9pt^R`XHlyxO?YkP- zjT(^9R%uOhZrSs2Y6nWx)5V{vKQ06{5`~= z>bfv67RUT}avLGuIriNh+!5YA@m_CKAL-fD+$SBP0(=YCO&Ato_1$P4JBRbg&$Wt%a9Uo};AOy= zkLW9zov-(9_7(RL5yCr@Y1^;RjYOw@$bupO8!DVS^ z3wjK42M;AL^jkYb9}ck;Yh*Oi{z_MDq;nqDnjZ2A)6ez$i5wlXs!uksGA*j5dvN$Z z%QuBH>z1KT%w#BTYMgZ;(YQnQ!^YmIp+{PQnBU@T(ZXB00sgGDt)l(UoMO?Okh;DE z(IK|4er36?Xv{E5oYjsDpHvxFF`bEkFwbs)n$ncKBi0=Cup0ljBJI z*_ckYK#r_*PAVxXgcu7wJ7?_BI1bZ|E?^Xl2XkH2Y4^E%f{eJp&K;@Jxq<{C3*yn+ zAcL$Y_0h-Gr#<=+yYsMKkIFDC$`#ut41*0mf|^sFi3G)HIhl-QgwV07U5RaS+jCVg zjT`dIvM`tI4>UUi|!zZX`8mL$1=8 z2uRgaaBac_7`k--bu!_0(IFe+vimP9f=QYZTpQbmQW@5VqYzT3>RW08A#2Q}K3sSd zpX}2bSrzfzHd;=1`pA(*O!yr|wFOPJ#J|6Wshfh){bv|ouzXYNxH4oaQkq~NU4heg zF54)FraK(TOA%@}3out?z@hb@itG<^zXRp_HU{Kc+6kGW-*=WAXW&P&@C z&LU_h0gv3VMDAtxVd=llHyF17-}pz_WkVc=;>EEvy%9F@cU>^if?}dV&D?(XI3MdBYt7nYA+ly-rrcsZoXiR zzUk9bD(*mPJ2$r~Q6#B-eIhlAd z&zG+{_)-(o(;5?ExiqpmDL_q};XxfytFoL2+!BU}bGH)g%LGGV_Fj<@stcD6DVCE|)zLgX zlIf~j0z^sQMndT!Vqz03irvU|_a6rruVO^&IuhM2dO72~RU06TNSfji#(294L4aX3 zh({ns1A^BV93lF59`!Qn1&v06ahLmmI&vh`Zt#FDGGbi0bQ@o7cEsrWJnLb!(+Cn4 zPLnluPN^I!h|o>fMer!+cWf;U5R)t)^TNKK6O$mC5rgpP%jWuh>y#F)O**V=js|-; zEluRNBBd#2K>Ae0U!gz=1ZW0Ww*ULWFkevc8MD3&xUhr-xTLjc(16VW@}^N5-Rss` zF)!=LGhk?)Nj!!QYH zhSf2gYdD`mH0rbfRj$ZPc{QZ}KbN?jVcT>nrlAoeregry6r&CD3fvsav2bUA?UbFu z8k+Hc+aWOwv@pcFXK2!)KgQB@A0fqS`W{|L- z#EjbD^c8EGE0@OTGTV87WUyAK)56#Mkn7VM_of#DN|7}nc}S5`F-x(yo_zD(4b(LG zguj9xkzQBoVq*xr*&2LmRgV%&Z3%y6GZtn1>z39*bhc!Li_LSUpr z4MmmU=D}U7G*0SAlv)}yF&C62@mS4{UlIRyurln@uWVWTB2w|N2y{2=vdA`dX{~e) zhPP8mnXf-4#hZH5Y$@=Rr(l|7#VJl3)@-AoRP6OA1y^P8n#BoqR4TJygu6GW$7_dM za0WC@34UP+0As1AA~d8I313LZP6DO_1iZ~I4jdFwoFDVUM~efowTJ`zXBkjz8XSn; zRda$rh4y=H{s_OtDB#gUb8d>{L4!y^zH{NSaTHX{4eq<3<|z_B(umKEsiu-C?KcW# ztp?$oCq81*ROKQ~-4j$jrZwvqzx;^FxV5(t7ez&P8 zcOcsisWJhBr6v1BaN8h8F>AJRXH@`&pMO~T?_(<+z}C+h2_JVl!?9=~;vf)^T9%sa z(EIQj3E6Ln#Y1t06Whk%U4;-)1R|rp$V7?ty33%P z2#;xYUJe$7^-HVA*S^-rPyi>-iX)2LJpeVQ$fxm}c>y>}1A z2&>OmCW&>9r1XZPb4jm}!Qxn6-#;b)B0e2W*y#km3JxQi(2XN^JK;jlI=Ee*IN1kn z=r3JcGxnN#*)2OYIfCk-ixQXZ1+`@C)R6+Pm7E|F?1YxtL*U4^ZlK$fX(noL5F$te zL}qvLQ)-2SXT!7GbLczyuSw0R$*PuWnqZz`sf$hEqOaF*kx%&cRJHikq`3CC-$aCv zW>GaLnI+X?aT!PV9C2_vaqHyrq);(R;42hx#vDUohsTh(d&8E@cA2t`FLr(}=Y-fB zSv+Yoi6YqOb`Y_UtsB}kOcfPm(x1I$WDpGtVgQE5R|xyg0da~~Uiv>GWIm+o9V8`5 zu+5J{3DhRE?o#i~<*DhF`yMIQ9JJX>UND+BfrZA8rfrwj9z0WqN2-aWj%zcyCbHsn zbm?%h)VRKMeNy9r)FZ%QA5Sa&DpPei+e0W{);d|5a3@tP%)CxxfvT4_R?r$TF3;E3 zBmq1BTJD?lhbE*p1QSWKdALjwjy+e(Ta7*1|`31W%-o{T?zKYtTs6(u_2?2o~sAGHpX6Q=EJyZ{{SG${N1M!s+ zJry{Pf0kmBL`t9a6eYm_SHCaBum-S|>bgVd**FmHL)M8dSQ-%5nXJj8r4TQn06!yt zzp=$v+1B6)y$G#y)33aUMItZ(YCI+<9hiW!%Dq;f{tJ9#!}fd#-!vwX_$-{@l!zCP z#m*ee`~^*A-~8(Rg>bg@Z?^s^^nWGW4G(+Ac3ekb%@|ehn-SSBhS-`J6g_Ufxp$H< zhreB}zebXrqizWajoOHS=i7vLKu;%e$d#E~n`u|~ny^jiohr%>F$^$EC(&6w7*|;Gv*sxa131!ZQkQk%_#oPde z3Cba563H-89FEX9uyq(P$%6k}3!AM01+n7IrdNUR6OU376sntGlM#!uGHP^`v|prl=vg>mE;;^U*AmUEud3oY}>kq6wT)#t2vs?BRyFeIsdSw zWo4jS)byoQGU9~wjXK@WDi~5IvoDAe=yx{w!3s0Q{|>KAfR%wVSLi!q(o4#UJlL^` z3uFxzHG^28Z*i0S^kn@8V5^l6{R3MpiPm3HJ9O7;ySXBUD3Ag{DHYt=;T=TgG%AZ) z>lL#R;DfEA_)tJn@b;E~c`J#C@KqM&eEf>AR^tuS0G8T7_BUPVHEesj{ zi^%;1sH!mNF0u+HoewK7b zvHk|cL=n(I^Pj}BieR(gBI3yTZSAO8)7%n)sZUXTc60WOIUY5JkJSnOfjdv8DIPNu zmPfHSl1L%6)VV*Rb#*zR^m#9y>BZsUu*_qsJQYKhU7I?avbEKo_wGgk={2Ko)bjxg z_5QvdB&&SzQ*Tq9S4p3>rJtlx1#<8KrP4tR9i4wyHUYb{5x;aCP_G+QB%Oi6X^OK? zVctD<+ybrsF8GCQ_bwwQnuXIH5I5bC*JZX$z7<1rQRN0+b&}#T%ddr4fA@01yb@s_ zu&)?gmp(T#%n+kN)MRKuT{NX8E7Q4JSBRJElB$IkcZ*sMqWt$X1WlRup<^2^4GZJG z{O5KurEncsW5+qbmoVg#6md6MU)hdjt%$H6E~%o1)n_jUh0hZ-pfV^cxB4ri8lM!u zE-y=uJukTWup{@;P59bT0h7i`>3_$?sT>CVOliCL#1c!uo;SoiQf%ny=zCS|duHK% zi@-U2D*_AUv?ztWE1cM-1%mOlF?FZW;J$AFZZK5_a8V+JD7YbNNk|ve*;-@vMTLwMDLX>Jz8>5dn93HN{xosZg~ge!1aXeZ#N#kAG~ z@qHL>w%vC89dgmr?hS;hW_K4N$d4%dlfC1g#4_`Xm-)|Jh5^x@fv`5~6CkM7mkWZx z*2t3k{=z1S z@AkbdtYSkXGxkix&Pe&-j?mY7;Fo@hk=9TIKb7A4`hbau^gtq%u=(@#!(8NoP=J@B z$Un9Wg@6D{R|TP=mE0pzn>1|P%h~+)opjxscs+3A`i`a7>cjv3_jw5o;H6Tit)r+4 zWt$Xr!-^J$J!kUBXJhQEsgSGw{K2)$^rmd|w~$X@Vp8GD=n4tLwyU0)*+KY;D~BNFuhvK-4y6kJEZ*P{T+CYPP&9(KVmz2(vA5y=C)DRiROS z5)FTE`wiih=J6d`%AH>$!$(FSAlhoP9KGB-iI|2rusI_nvMw1RN4+Izt5Ml)xh zh^mVu6$xin0&qFCUNVj!;1IPH)DL1nBU@Oa8ceroAt7vdV7V{cF4c3r6-GVsIUfnT zXt$O6u=HQ&5cGY3Ln0OEoAeE-2cX-Pl!i>HDq{8Q_ejIZ1_x@mTnW4|r?YuMq*1RF zgo`>R${wyXjZh%SV-g;ggr%o&s${R4uzF5JboXu0@VQ}g**#KxA1f6)!j1XKM?;Uf#`j)U#_h-lOb# zv9RUys2p;U=v}GI)U$1zxbNGY-uNT-aaky}h8~YX7=1UwUV$a^5bU<`U)1!13F_o& zA--C_HY=KP$5CAM?Vei21yh1w!YzVtx$xxb+Ns$2PQyD$*hA7Gr{Q_ROHzXg(FxF~ zznWpgnZF5_cPMd>Bv=L?Z{EBt#7(P=D5*>5ULy&=l~U)oNPYQF-VjU4mxO21&Iy&( zr_}2t26hHYtWWdFuu!utVCr2S&lJ!x^uKcQK|5ot>pSRR>em>AHlz5z0aci4^zf!)CgW%)|-r6RMrXI$wKMdJtO# zc5r<37sU-FS^ajh>PHm9_2m@=%4Xp4ZZ@wW``pt{4+l+%K$%+lB;S0Fv%#qTX6v6q z{})?{8!&nE!pc|ejFfp8hJ~uIiig)W;lzz%O_vMf-+MD(J+(erAe+^vRXF`U@NTg zNj;EP;ieu<@>>?vC@DPrBXLmWICkil>>{&*Jt#Tn^vn#T4X)Hi08kO3n8x$(g7Uev$@WpaWF1C@+2r6cIe&Ze1p>9{F& z@0^+_iyH$ND}1mrpgFP$a$Yjj+jG2+s|n?4>h}4R>syuP!}sAwIf*-1+ZoRTZ9fJ` zk^No3*bOl5-D;3Og9PgRP`gJw-(X|?bt>jPdmaZ&AJxHiE$JBGnto(rB z;vt8<6iRa+UeR2%)#EL}PKL99Y^v6gs>3AGTk z3M_(g*(qX=%}-;X2R#*61d#5i6s=!2H3w!Q{^aCy^CXGNEA+_{`e~4bpr4&_3$jFR z`86@(pi(=yeHsb=v;_QEOJ04J);|@z4e-2y^iP!g9FSTl(;%Y90mK?csz3EwD=Gv8 z@>Yuc0hoBD-{m%$ck3a#Z=NB~ zt4u1VAyrC$o%p6;rkIqdzaEO(?#PVCLIX>1^KEPvOgIHD#`7%w&DKAK{x7z2T#Hg` z0=&T5@Dr!PjfMt(N#q%&I^4WVarTSOuGGS}4@p(-?;FjPJxLKkmp?H8OB}0Xac71l zgo8_GNUVEp5N|YtwL&d`lalr_BsGAv-M#maR!{Mg_cb;Y4N{2%*z#3W2(coWz=T;O zZRKQKk7(BKp_)Zv{rw-df-^#?X8RVpsBBewm!ZN#VTGwS|z{R-lHxv6IE3x z^STA5-e5<)HK-UrcfV{=Mxo_YqG8dcrLZY1jYU^Gm4c7|)~&^{VtnAuLO(^R2Al11z%ox)ZKi$Wue*C}sxr&HrJIy7-*QwVCznW;Bsf;2%m*Kk;>8ymYtnXyYA>Al zWt8u$2nQI3V&uLqaNPIZN-A!41no2_!00f&#O*sMV=*yK-hjmRN(9y~(z3D^d!d9& zcsz_{QC6`qS)z4j?7^ewDi1r28)OCgV5I&fc-dGPeTMG%@BO+(w(qlM4Ce4RH98Ts z1yHkv!)n%Fn1A)a`{!sC)wlTL{b7re8BB0&m=%F7;UoyPDY44GA<`0gFwHO|yfBj3 zft2Nc-vaxksxW7!mH3cE>aG3Qtfp1)V{@6NRg4>Fc_M2rS=a-xH5KzQXSE&@*%-}n zpq&FSwvtg5#qs4Z=+z*0U z@*7>WOGPk1p9htdH8~1QTn;kii}05_2*-&%+Z}=4*ujqvvcLWk-}KV#%jYGub+dOs zfkI|Btv1bME?_Brb66%ZJgN53iLox0#_;@`t$zyrUu=~>h6#u%AE*>7Gh-LV>ka;J zCvIP4)Tb4*rplWba|_x>C7a19@5o`;wExnv>K=*QLh@tXJ|#@&<{-6zUJZBbQR?i6D!V-Kx{kr6N zaePdVDo*Bf*$%fB>9`fpB2%LV0_J|$M@(6*jk_vZt@vKA9~E&tWvkAysgXTi6WGg5 z82D2;ND@Vb!0iz^DX8hiIDo&~&e=B`jY%u}YN)r7(LoNgT{Od_>F04t0zFUz7Qt0k0g+C-+xQmW3Rc4S3 z=U0jZJ8o;`ju5X4@jfVUvA@~+r_le!Rw4|XMA(HuQF~sBz6elg6VG;aD>&?YqO(_w zus9goOsi9_qe1KiH&4a>FK+EQ9N=vCm|s?SkD~O15@4pJpfsmS+>Yy@;H~`p6m`BM58krS5?ic=c!`l;McZc=_i$f06dO@uCd$IDd zf)F3Rz?7%41F<0!iKqCi`Jj)Sm^B*}SN(Gm20BiYbYMJ@sCe7W@p^>|wm*8Xm_wkCNrcrp2{n zp#F0l{9aE{i4N(xR-WX{c@iz0`gcbfT`gYH7N0Nl;QJlpr&b$mj^0B+p^zd+GCF@> zk;WSUI=sehCKu_@&z}v`Oe!y3mxcI zx}dk3B=d1J9vI64QTsQ*n{p6m9fS|SP`lp{<~h`sgCm1=U^*m6O#DnqVRh&UrpN1K zBB)77{;>4l#})*Dtwj3qPF}WJgfpjDs9{*zpUX$Q_7~2!E^LT4xL+H{oENxgYCmvG8A8j1IXpB9OaINAL+Hta3)3`WF}j(yVLmhbFc+6YCD_^3%k(>vGVlNZggr zD?y-HJXC#y~amX z)89=mNmy$TLr-X^bR@!`vNAAD(uz&(u->EN>XUto!+m!7_#Z--BnS!FDY>>LvX0$> zposj$;eDj79=>PZsb_4~pj$HP=q+F^@YW;DVdI(NJB{NZ`djj#=^aF^NY<|*x7o-h zM9M|K+>DzqI>l%ot7S?ie&^G~ZNbNg%F~D0vMQK(N97d+6^>`_PcD_K22syPU!2s>ftWeNLrt zFcLO1WHz+%A~Rk9TN1(_FS`NV8KBJ8NyH}yNz)z_nTINx1mmTox}pCze(3KUw0NJ+~HaM^fRU7B08jJK(u36tmdL^Q!3BZqees4u($89G3n z|NpGvpF;l^TS<;3?JZ9`vg(e8y`~o529dt0q{M$&hRLo`2@9T$JPowFht?6<@!MdI zs-kMgnFj7k{CU&ft3y&{uTP{rWLziA1f<5efvF>l*X5-hRZl%lc6h%6CNDYU6lf8~ zNQ{h0sKn-$HGlK*t%$i!OggQZQ6(n+fHXaN#u(%H>4@Q29vQqHDIq;r`%rjPui*q%OF^p zeTq#pQ|M7XZw(ZA{i6ED5aXkFrZEJ+Ap)2;&hm9K8@szz3C~AQ3mvWr^}tYh|6T%Buz{yQ#gHb*Jm`=_itcF z0Cg_yW-^!khWp#7&>wZcEb&FYhNVa!ml?3}is+)m%3cd*xdBoxuYDMVemj-j(pR)#NTERo;8z7WkrsNEts(e=Qy5(K(ynxm-N#buM zHIt10zsJ8DH&7@{dZZ=PPoti4$TptTzMy%!o;+>8kBz6&{_? zObwvMfSAUZsRg2BCurA7?qSq^mUIi>;`aE#d6rmjpeJK3(+8PhY}q8kS<6FUSF-qc z&_Yo|D6ny~@V`$h|0(o;86@FM0wkc1aKjw~>NQfcCrfY=c5q47pM|JCV&+&yi7bBb z!X_)s`0|t*o9C7Z!w4wE>=S}&XTtk8HS5Cm+7ivMx2dvYU8kzR42x2JJhI{|GTB^7 z9hf`a&-z7LM=^g4l7eddYGl46uWCD2iz|=N6kZ?#5~vyqr$jM{;H-Dj{L&iu%Ux{S z*7i}NsASNSJ1`IP2-QGx>0KI;T}tDs0VKBPCqzxSU6wdZ-EW(9HVpY;48n&1VGGOS zurn3mb2L(oG|nit?lf&|7EMQkVOw-05_?7G%w`&VXhJUf-WnyNT@HGI z<5i5i_>YWlY5|BM(JjBpW`M)+LDT4tY}4yCR>(2ISyx3Xw|94gw%*#d+t>*iDntAp z*S@AGNdWhzv3k<)h!>R6zuM84=8%~H$#>~ENq!nycIpeYB-$Eo+)sF(Ff7+0`1Bxt z{?rqKNecu{~%#PLh3cNZOZm-vHb^PX%P*mvfq(sQ((ZZCkms?NH%yoxfWdvB{uxzm+UC814`DC-qo$Gp zhPY(q%ei6!QzFgW@;5W^M(0cJ28e4al?wmr_df=~LKNn>^Gu}g&muu)Sh}dNsn!u{ ze-^?vMMR*07V&Ye&w&^QiQyvSSKs2f7n&;QwuP zX^MoxEwwcxiOamOLt$O%v*h-Lo7|pgusI4LjnL(7O$Z410u}ay7>d-M1tLh_-K$Or z@gVlBTdSQw-+W#4v~aQoI1=z@Bt9(t*Zsmr^9DF1VjGryXS-x|c7k60ad84)-vKF8 zX>$+=v9VAdB5I@)2HoT*+6bS612k#M#I80FRwUb6vR}%I6#NSYQDje^f|Fwm&^peK z1xHt3aMhp6kym)vYO;1uGkY1>!#LjRXT@~XqLSQ|AavcokLk(kdU{n<=^ zml(Z-)A;3~rjhRU5by2CII^8`r$#H`8$GV5U=OmAfY4kQuJx*$P_ODn78{nwE& zVkzi&c~1G>r6RZPuOf6>U)6gxJ#x>B{c%YB!Ds3U2EK}Kf(d8mHKbHOU~n}ge?>|< z%f+@<;9z3t%J!$g0Kxl%EOSar;fzb}v0NZ&|W!Is)mj2QSKk zcO;J@Ec7sp=-Rg+F$D0SyDqAOL-8V$G*D7lXTb08fmR9=$Po@^F)-L}xLA(Qxv=1* z{6Fg6I;yH~{~A7YclV*C8v*GM>4t+K(%mW2-JpPgbax5TNJ@8yfPkcQNXK)ySA6;U z-Z6OZ^NeSV_jiVW0M0Y_+G~E+Tx)+md#_Cc!(E9C~bP15UHNkura# zYr8M)8}g?yRfSkvv@GR#OM}?zv7Eyjd|0sT?e(mLXnnOYZOkZw{>NO6IAd_d2dwY0 zp&3?drcGf#Z08LmSzD*#HmLwBAnb;X(yvl$;h(^>Olv@l&*Z(SzQrRN))#J}@>WDG zfHrL8LcrD2NOx!+*jRC)GF5YDUYS5i!`ln}?rS%W-uvWJl)=ES9W@&qMYf%B1LA~i z1aaR8w^Q!fQDW>9;Cc+!PyDX6xz%=%?x(yHKk*>D{&>-7^JU(6NaaLssqF*6{uD$< zpqQ1SyAM$VrfLk1K6f@tsU|P}7~ae;6$^n^vL$LHq2%9X>AT9PsZ3lkM$6Z~e)}rL zGJv*`_G2XFG%mJciFcwQkn_$%GUK@goZhk}qaSwIh`X-cgoWU=?KpN{XZ$j85cU0t zMnL?pM+(c)OP?={Q}&wE&@I>a67%h|Vm?QrKU*x+TlfhNvmt+XFV@2h+=<&^c5_6! zy#2R?+EF~}mBwU9NyBrMR%v*~8_%>Syg ztzI3I=b_KR?>?-tSa<;apXVXUU=Mi*ktoI`NG=*6CWI72?z{IvNl>a_7w2OPYh$aM z+ZDSq&miTC{QCC=ArcF*bc3Sd02zK3rNDEmet5Cn%KzH zJA2hSBO^26`8hF7G^jkwvmXZEsWW|j-~c{@>x$DMS(Vy6RBa-dFw2J^D-63Tk^ zNq<3Ph9GJ&;r@2Wb2KD_*TfZFihP{4QWye}GMP9`RC?4#7`XhRnFHvOVxOe=G4_TA ztkRfkRLl>hTzGitZL%O7faI5vBYAyPfVY;A0;!xxb~y*SMr~5wM&NhBMnt=L<4F^@ z4Ley^EML^WH#7HSLr%_>wc2t zRJwwGSh5m+HEL(e&Bm${g$DimU9er$K349Y%yb<^xEQ+fq0f~lrX71^bbl$K{;>E& zTt^T64+q&-wrZgSJW*Yi3z(%uw|XXvz#`ok+*(@aMaW7((Bvs?YCb`{@Tifd0>O5Nfc4_+^j)bBru!MKjmv@ae10 zI(DAmyze5z;1kltkPS(4rhwz#iL`#d*=6GRPRS^h0?H7gOgilwO3%eXQ$I*;x`CAj zu^_M7jk_$J0e!o9cpDAfj678Mt9kR$`kA1x+Mhh*V_kpmAl_PRiCFya0^vjkgqLIX z-nFMdzC?w)3@p&;Wjv?ifapKf(>>_J6#r77O%{ap?HO+PUVIA_h0VSxU|j1>Y<_;< z%U<|%#T!HkiS!J?$)R(;jSK{hNLJF}-0uyZ6ocSAW8U$e3jXZodljdb8^)yam7S?_ zO_T_x@~It0JUyuMK_R2$14xqH$6ChmRl?A3=8A*BZ-;XSQIbE+d~I5I;8t{#ndNzy z{OWn%>GP4%schA4@hrS|cwQ*w+R0zhUbbN$(EhS=M*%|nY}WDLoM`Ny%O8hssb;}C zy`xkt-|x`hNOCu`i8Y z3;~3qc#s0u^hlBh>?B=~v1Qr~|6st3e{=MuHXe?+ zCeV(1j#PI0n%!rXkfXf1c}Bk9#P94O^ncvg!a4(wtzfyG%vlYqB*bJ-iM0H+(1<)J zCgdiW&uLC6h|a^&e93g=09ZGRp(Ktee}6IE9z259-Kwmc4#}^Cx8dnXSOcxzVsnxR zk#;YXvDo{RmirFT%ef| z(NoS#bfR528P9V6l}&BO{Gblq*>zlRB2o~AMG^LrYWO0Y`gh`MnO|PS))c3JE)yb~ zJUEx`u3~*xxA_iU9me6_0FfKvnXZkT^nX2T5pk`}F`{?SSS|4};rKO@+CuKBoP#E6 z@&G4+5U)M5h65eC>W`hXeQ`ubXlc3`_&8-7Sy-`)oNTq%*Cp%7SWycs#}xT*k&d~7 zLyx0ES@k2T0T9Y|3B+aqlw_uC%VG`~p)v+~kD z3Is)Y8#!zU`b|t3V-~Bksa7&(Bdnh3%$VhaUr-K0q+cvB`lyOmU9g^2tMz?@JG_dwtJ8VP+QBKa=AR)rHQ2KOzhfpxXDRdzT=C<$Dc@4vj$O$6qj4@P= z@hLwpTo6v=)L=&Eax+7p3Co}Ns&~JPRWPe^P{E3O(n*s&wOUhy&diyN=W#bXzj#d) z=jFqnr8o7YSTFZRYzhviqd{7TF1G#0`|p?=kOcVu+RI+{GiArI@qUvtj2is`^vhu9 z2u*j=oCb=hD@2QixqLM6do%tqKZ~nobnp_h`q^SDO=rs|Opz0@Xvu5L^(JJQCrIgW zBY{2SSsB~|4@uBBGpd;`zmljGP$Cq>8|D)#*YyFVP`rsbV?Q6Q}PqgU>3h*FlFBtfzmufgf2Glyw)XKVMoPxcu&z5>O zM1qXgEeWL=eDY00%Cjn93an>xMnYgZGpTboawkNj>R8j%+iW}RZp{_4gj) z9qC1TEy#zAE80YrdRju<8??Y?ZI^)#KIJ=J{EKFF`{;_Hr;W z5#vt9ddNN*W86UzQJQ;A)UJOVHmCA4C?8`L7F{m*6R&KA(60mJfeS|acPo4!sCn3= zUK5x=>L<^a;S4@eC;~OotzR<6m(xyf!J{wr&Ia_oeXcvNYxW}ch5Res*pUOh;MR|6 zku;|6jZ z&V_tVMAr#gkbB$6lvegFFNO8>%f$wJ_FwWd(Pvq!IY{YR-0xNARpVW@w!}3*b&)4g ztlpmBXX@T2*U+#|D$TY^8&ML=g^nAHErkiV~dx(VA5jC_Uk|2inf+^f4d{W$I6ed&S+ieN+iTqNLXc zK7{xGRQK7h*JYbm&SoPC+J{u?!EExOTvD^8?wv_8Mkkz+XV@S`-^*iEv#1Fp2>P;O z_d(a_%df8zNcIkc(WUK8mM455-H3s)p~F|wIo;eC^Dp5XpxbzQlPb1-?eWM?Fg=!4 zw>0$L_?`B&zF6&tf0rIg0RoUqno3wS?piinRS_v%>9CU}MPj$XhngPgFO>v^=*GU^ zbkAqt#MO|)RWVZ^NVxM5ce7z-A?w{Q1FxXNWnD7QNBBH-pJ{NNpF%p}tLmM}{eCWD zT$j$sG_M^|?(<;r`KeIjQO$PVxcX$r!Y~TeaXbskKOH3cD|ZMUFWC=Cv*ltM#E)8J z)hpVc6q--dRJ!Orc8p}e4uTp7`@j~kVRqp@Vz(?)s;4gHoMsK;iO?R^Q_FkQUFmT3 zR+m297wvzZgQS8TBo8iAHy9i0#dJ4G8e|={>rzNKH(bp~RM#orO7FBPKpyMH>TC4N-X*Xq4}xxm_0=g>%ub5I`zPe$$9YIuJ3ULv?KnhQlkIad$2cuH^mi3I zaN%HqB)qP@h|Y5=3{QnW;P~!=B(CZxpxD6!w9f(0sdTu}<2OBK?-JQ3>khThWt>Pp z^-OMP79*p)tMR8UMLaL34gU@wN8!Kp{3ou}CLLSzsvqD`C)bsI>McXVa6_qOtp0WP zDkMT^;r@P1>uE-8QBVo^dGlq@>!&#QDLxd)zTP>iheR+cJuQA+w(d=g*{O~OgIAZ& z!w>-Ss&O*GlcGGoFllT&r8d%X(N?ucC-Bdj*@X_47=~Un{$xZPL4fz~tVS@1&&;uN zSk_YRiUwJz8!;%r>DVFHqAJ=PFf8E%%yT~XrKz1KMr!~H{KMe4NWC;a4tv-(4n`JG z_z6sX5_v)wDa#nv=Fy^iMpUQbw~#jGd)4iOmv9@b1g=#)Z9hu!r`;3u3FZxp!kGja z(Kz=F0Uh{C5mnz$lRvMltozxDDG+u34G@<@gZ_8UTz>){d0|Pl{38P#4uo(X}D8R*f zywv~2lSVJob*}Soi{(DFpKS6TKvgx>dlM>^Sbr5oDR?#JMz;<{=Qn2uLKxZLz7j`= z{QLkKVS|dWD8U_2>tp(1Ks|4lWl$GDvrAbl{_4#>u(ekhJg{2;|93xxIwV+j;Nyf5 z^-W+S397#aM)T9I_w41B<^E`@f~mAzPC`~Tt8y{kqI*rY=N1*+Ra^JozJd+)10k}L z9!BgW>R7&aFSSN*#+o8Zb1DW%Pyc4IZVIh*1NzH&4N2gZCxIUf-W9{{!T%MK? zB`52g61UTSF${|DP$JfT>eD4u<@muWMAK|KZO2cHyJoZ9a_xK>LJq-Ap`}V zR8|6_1lZ7egjuxUas$l_YcEEWTv_n|vPuam&0$^($^!dN4}lSrALlUKb$X05s&HT?otFC^SS!s`)_5&H~s2(FVNHlnq}nB zyxmwEki59?ULzv^y^WCli2Hm9+6$SYP>{3Xt3l>L3OfRVa9RJtCJQGZb&RkW;Ikq0 zgFzsyl4C@G@BiAs&3=EPQ0fQ;EBNA7tFZN_Sx;r0hMTt!p#R0({AL`)W8g{5N94a5 zE9P!nl&JA!|Ai<#?oBms%g0+XCUD4CJPZCDBuVkix>nI~=@j@3@+ua(F00WAp^SfU z0sE~X#I3NTR6?t~!XyOMUSMoD!VlW^ZZi^ezRz8CC7^?GHC|GdS4@jn$^-K zv=GdllysX^aFEBRRCot1?uM2)_&CLHBfd?OYLG}cD3_wbg1BE#VkntM4(vM9RCOOA zQC5~9YMk8ua_xqB(#df#%t?-f8lO491>91>_cq6x# zOGy`IU1l4uoEm#jh^etrG=x8V&~Ir`q>>#!srKkokEj7*Kd4}D=?^+g}nHg1*im7Z%Y`$&N6xs+{B(nZv%GqQC+4?}9U%OqpS zK&=FSV*dNjX??bhJkz?E-%cY~aO9(kqrJFR-*Yx^FuyVVbdw<$-*RYGlyKNDDUV8A z*IX>O#RM&641FTSST!$E>4WQ7cHtl~cj$g-H}DDXtNf}tF_D}P@2!Mm+K7MIxgO-O4eMajpHbgZf;l3+2JeF z9S)ztwpEF3B%izw6r$L_GKsi0t&_|(e0i{tg z4zcO|n$TaE<@070m@BwmT8$$!ao;@_28M;;;VoaCbiO;-|86t7p8O+{NQ1IW%#QhM zUX=mTK?`{I(SPnlmPH6$IR=hoyd)`&QaQOs`|#$R*H4q2&!dtHHJy;ILJSZj1NFcc z$`p`|CN%~)ZrjSB8DmhE%42mQZj=t@GOvq=ak6**XD{#Zz!s9J29f+Ki~WY3=xVg5 z=S$T^Cq-Q9rUT7+t4&N4Tt1hV{e~6$F(4y0S4{_|)=zT-u zBe^$G>)$V60&)-`}t`Xf2siug0rT7KH9xia4Sg8O9D&dCw}5_b7vtr_3c>G3FAh}X;-fAcw@>jNG zh&^MXSaLoYr|s02gDgspm{w7trHh#iq+OCiS@owi?&EBQhc+#|qVqiLVuz6vP5D3t z%k?rG%3^>my?YE?UEf3Z6* zArdTkBkH^xHkdcqzTW!~h^jgE!LP=CH;B^k07=QFOM;El-V-u`=aV%L#h1^<-&oNG z!xMYMRdhx8ZBAV~NM%5=MT^V_rKQ+?)XEnfVE?ungPe5x*0Brc=+$j;1hN zXe=Y%Gr8F6%oZ*3JaGJ%RV|u}>Pkg7Z4U4Bg@vI=fG`I0%hn+q$@IJ~RqwZ^$xCa` zxiQ3jt6qV0OV6)@qoC8h1O)47(699Nwxw186yoR;CBZ2t!u0KC zTd^%tVM~h>cR#ZO2pG%F&Q&mnZc`T39TS_L@oNO?-~}7b9YSveGFu&sd2e4Ee%y*^ zxU&$itD1I*pPP?l4c?xP-+aCYo^=G2hjh2EbuXZf-R)bX%^xeWZCT{uvRt| zaDb~u&ThF6e*%kv`HF8@BbWzISpMeIcT|(c!FFP9(II|p-?UqWIs&!NaTsr|EAaDN zElJsm(-Dt!-!B6y$C6ZPdr;f8X+CBgnucIeBT=#cv5?1l{@y~0bSuJUmARzW%~OCx zo7OfR3~u&n_{(fa;t-Kvx_(sG@@Hbt$*yjiSWaD@QSRyDG}@$cT#p1K3f(GHyQJLM z!Bcob{W#D~DATF#juy4EA}PZ%=|uC9l(oR}`e=rDXCb9V0ooS{v`G~wN)ods%1&b& zLA*J$s{k`WbUD5%qtw&x`9WWI%!;(oFXJD=?2y{ zxYg5gPhp+235eo}rMdRk*p(SLcD*$vE}G#fffWT^bg)D(2>Q_a4?-g8mOa|F*4{(g zuq`2X&sbbQL6~>6iZMI^o*fKRo;}wQyztn(x zk2_na%GFt{_87yHb53OM!fmmjniWKBTHfB6B?bkyIf z59C9zEngpJWC>*hpH6;UXO;R|yG$;J%zmTL(eDHK?5bOwueby?z~{<9jlGs`HK|c+ z1^SEgPrEduLX(gVZNk0jFz?0?;`G-B(qq8wos;yoJ-%v2w^+1ja$doP{(7U-@yauM zeNl1nJ9oodsfC1Z_&*=&7i2SEIfT`4SL6W%I8o6czRRn$KXa9}KpdTE?m6{KsFJ3b zSnGwA6nVXhkmbz;zvc$sE9%xMrf+#twKcOfoYYy$S#W#WG}pChMOE<&pOUr4md3%I zg;bO&C?>O}Z}dJFw_#D^_^AEVQ}?KxM&#qhyBd%)!z=Iuw09N~_U)wu*(83Z2NNEC z{VQM55>J^>(?X#E(2}Mup)WTlI2HMiItBB9Wt41!*C|eSFr7HCa602v65rg^#r_A-|KdVM zx_su%o+%$gT?*~U7oA-}q_u=Yi9@FIYGZjOu?JOO`$OAe;qlFxXcaly^t-UE}pczp08^f0O-+*uz4U&vs70UxJqMJAiM7&21{>ILmk_7L@( z#=LbTQ{ks(wd%!3M+zoIjJkLvG9l@uMg7*HYY{^P5JZgR5IL_$=obc|k&sK2&xNQY zcPLHf{LGG4Uw4gV%SzOU^t+*DBsqk$qs=w}3L*c!ebZw-A0Y|=_<(@U_QpV4dm9^L z)_;Hg{@qW}+Qt#62edcRv-~#@L~I5$aWZmn06LnQIRL@`EX=G8!3aHTLmMk^Z(+a# zk^b-K`Ph^o2kBsDVy)-sWN!ri+;#ms6-a$c8v_gA!~Xz;BmTRu9&4p9hzz|20CAs1 zg247h7ykDz(BMBpcmtrLGW6JdeDwj){?qgd00=bzqOqAJ3mc1py^ZyQ?vN-SY6A{# zYq=Bae;p?f;91$qjts`JPhNkWj99+X+5!;%7mopW7ich%;vEsvzlp5Otc~m+Fg5kN zE^WsVF7Y$jjuhdfnXeLLe?3&$z$%^6HB$TXwhKZA0HOOTbu*9_9RT>lgZMwEk^{gx z6CXa6(oWd+{@r~B;AaLOId43f386_y)00s?cXr=*>rx>vLAzK;7%8!9@M~3^5a<6% zX|qQ-pR0LSmy5kjx(%m9Wp*|W&6yI!wck0b+npwK*^4+m{7-#p^ncs||0#Oz;#}!2 zCXs)~xt_g+(Zl%B>f3~Kg*&QfcM{!;=TySY09!hgmr2{29>$e>43(qH|@E z?N7F5wkD;=knJmpuew`#Jlq3e2q-dYP@UBUO8=A2rhm2WZ%%Kn?Hh+A3^@I(+0QcJ zo+_*=!{1)vpL*ak`TLT5NWu7r&iMbdt^dcsX#ee!kC~S@;a_(3z`1H5{>|~2ZH1;k za3h3dCEvml=)5(36C7+2S|w8nq*B!UW%e(ZS%Zrx45iK- zmRJODH&Txdc$aSC2T0J0wut}$k^qQ*`TH>dQ2x8&kF~`Q8vlu(2%O)8Ho0i*{gU$s zM3VRf*yyV4rva?=)**2LJh3gB7$1CtiXT8}DPZ={Z2Jjek}oCtHep()JzIF5y-%%+ z!3IG)S{yyRd-A)DRk9w-M>M?aaCP{zBK1%qzPZw>y0bKyc5>Hu#6I3rzgzs+;r&F^ zz|jMu1^io?A#!x}Z-(`=>NXUwW(Q?D_A5g|qy?c~6XC68R5rUhJ%FNWuqJX8aBK6R znK`EUGS+R=|Ga7!-$kIU6l1&8kb1aH@NPX*&G(L3KQ(3~MilXo!0$iUzOAonu>UNz ztpAE@9`%5APjyZnvb>Tdy&}FlUJd)Ok?GZy6!_+g?FRqn2{!NM-|Mi)fPNyR-u}KIpn)>ooR-gk@DW>%Zd2F|r?x_D zE3fLy&blI$QzWHYHV{xQrXb($*Ds8VqVLNqhfWjae)vwcX3nS+QU}c5kd|m*5(l8d zH|ro3TK^yrc6>7KGVEtD+sTaRWWf+Cu6GerufREgxvw!kUQDcVqRiV?n{AdkjWHYS z=>gN0@wPL=b**Gz5^j2hUGf%uJiA{KbI7>?&0M{6!BJtyzE^Iu9PvTnPe8zCDKQsf z;;qYD_tk4h5^wk9^kU=YPSN^eQC}3j)r-f_{c_>3y$$GBJU8xrjOZ%w-1vyE%mkJeRzV@n!lg_hZ5Kmo8ef z&QKg#DZ6SV!B;_63Z+^ypotRF%4tngzN!t26R0ndL!SP1V7aW1Veb-t^1el^op3zm6?x-U=}1^%KYp zD2;5v4^WPKRaWxPb?(2|(L5EF0|8~B$67G=<3i9usqEqy>dA%KmG9c$|Db6xp)aif z?z4kQ{6cGp-y|CuqM`-}Y{U`F0Ri=mTlHvs_2U+wk#X%< zd(UB79;K(D$C2aHrB%sD?8!Wqxh=vt%s!xfUy zK|mVv8EuTj3wi6P5%)F)76aQxYN~p3$|IhE-ZFD`dc5CqKV}$z>Ea?@jf5g;wfu^A z)c(y%5DXo`86a@x{~8R>dL0QDl0?pTv8v}m5lOAJV zuvu)&MPCDPmo_6}ko^-~=s-mGIX8P_I5G}5ENXBkV=MN^9~Rngm?~O)7g#`}{^{u^ zVdE#9ztodf{@&mX*dlaPBB#(ShKm;33E4&taTY2&pPxLb`8lNc#lUlOyX9_R!igMt zmlmT%zAI!)_v1-*(-3y4gUmBx`ayA16J}jP3ckj_3g1T>iRv{6Scgi>m;Peh;k~ji zpQAY^EZ%{$mnH&}K=pTA0Fx`BHYv}G-gQrJu$6=udZO$$8^rDYd~50k2?EY**Wy)% zOvn-E`AcQWdYuLSve0tfqGSD5@sl%{c?{*Z+>e?5U%Jd0?PY1i7us#+Ew{PpX^g)N z!csL$ii~L|hLip#QlEA&_hY!fXlL^qZ&VXl)rHNk0o^ z?tVF*aU2?+_!R@o2Jfz~GPy1g6mh_vJZ}=~-Rf6LGF0w&m zD4Sq2+WoO0i1&Q}9bWY{A<*k;)+++o$qQt+p3`p89BR<8#VbeUTwAKxa+? zqUB@*lL)>@=++^#B^Kn=RPq=<7}ZdU-LYvNe9~35UUNnJiwOi=CD+dleu2y(xIa(S z2tYkf3RuEo(!UIc_`zR6SqO`GzbJWZq4U?GM9Vb8Fi+aL&ux&-$9Jfr)UhOBwAnqC zi_fRmMD;`V%7f*DQ(MSK)w&xskAt^ zZatcArW6ATg4#I-*`k~Gi(5#%-pQT=1L24Hs80V(9Y@2XDFBPEF-J%vH38o_8Z(Ek z{nK%nFXU>TvG)P@SVR8`xOlv}6b#DoybkTq96c=&F2`J){Hj>~p$;brvsZ*4J??2A z!~I3O*(5kNha(&)Zv84w`j|$#Yw{@Y67j<1Ej9(51}i_@pE!ODcpt|K#hZ@vDPQE$ z7b7!{pMYN}79kATnvN$a)&t|}0zU%)PeBDf3~@Tu!D)GKqIuXqYh1#qa}*QdbPa)? zI2%8kh6hJa<~P1Rfa<=0$xGDBgXaLWZo;nTiq<>66FsCQ@?0$3ARq}EsUfzt{8K3b z;k9LG2XBYpWLu^F-(491F<(`)mO4e7d#Uvuf zR~MC>AmBxE)cZLCIIs8dX*bV)#80O&byBVAw8ZCyIiFH~V0#88kzP73T3HPZbekzc zeU>GzxhQ&^lJz3XjdZ2#@DeHw7X*~5+zG3?+3`o#wWxq#sB7459PHl7A)+yF9kDdU z9D4fOwjK+%zjP^t$sb~|8x6Z?#FO{(XM({ zhJ4^kO;x-uFS5l7>UJya8&?z7P6GuU2Ga{HA^nm2G2p%2p~(ds-&WcyM$4Whbn{fB zEbLpTJDxz9Nv=(Lw3h-TK){>28;0H>$kOQ}^5edKK(Joiy!q_()wElv;9-qTKR&pV zWjx=HB3LLL6vOSfWAfrZEh+X)p4SBLD^^LYiz1K(2LjR-wv2d9AR`w*xq>V8=F&kyzU$)pjdqqBFb@Ac(EXS7tb`xkNWzo?1jOm+ ziw@K=x;`iips8<`dbL4859FrEW0eqVwQNmtV*-=ROKuYv5m#8f(4emCEHV?>C;uW6 ztc=i-THwdCkY#2A0y-7FM5LYNP_*`~NB@Qnf2z76h>Ghj;oN91sQX?_i|)7Fj~T{a zy7-E%@BNy5{pO5fAyvXWJ)D4SNfOdVLtl)GI2={d$mm|~$8dkqS^z(Au8~4=%;!m+ zRK){XuGPXo;LD=M3@0JN1E)!}KXN|?yqDY9w`f;~8S@~mejrFhm05hsAd4vDId$3$ zw0J~R*}NtQNP3INnKqp-Sl?N4LPB*_=xIJyz#M>#yc&9kv@PQb~6;{{mVs(vah}#>z`hNfJ*C_?~*-(TJ(Ka z<~1xJ!e?=|)Xxp}L6;o?s&Cq8)D$Mv# zY8}9UfM@N3#>>`?J$qf8>4)crW+Oej+dCnxQGdNQioSM5gyx57*H-ykdm$D~We({nN-G zU^Hcz5yJY{|%gQ{H21gfJn8Vl5e4>lPAEO{Apzh=@PzAd8Qscx{V|ystN@2vr z$IV-6GoHm#jU+WO;qZZgJbfA@(ju>ya%on$y~iivKhU8uQn#@pqTKo^elfSB2a`DK zWOO*4nl~O$EAm7|GGYCskM@nIJ-x28g_SW@P6J;YemYoaCw6cYZ&w46%4=P{Ot^l^ z!b*YiJTa)>USY(A2TT(48W%!s$Q{VG-to%(sewYGn0`dhOo_gla~Lr_N~jiU*DQpBi{II68QXl4@5Z*$u(d^D_xYVEc`T*kCj#U9VEMq&;33Q z2wp`~|3yikVhFJ^R;kFpBsqvYafczyY&PRT=doqXuAU;?lAGd1)B6F* zzNsjg7}CYI{3iv+%m-4>j)Z81+wuuOKrsZK=J(p(>EmWMeWk_tI$Hyc(ix7{ zs3fkXqE&ppcO)>+@&uaoTK8hRWF&P~N4~{@M!ZxOphQaEgg7TD!7+gE$lWiLUv@Qa z3E{r0JJ1)v>OLl=$z zNofdb33xui8CzR_6QUv~&Tzs#PZTlt!I>uXEk!3%5{X##hX2 zjg0IaSvfg5iNMuM_`3=w>Hn)5_>a=y@67*WWx@0ZrGvZj-@iHTep35)&i1h);6r=u zzP>d-!V7Ga5P91Z8FKxruKF~6+qT@1AvFj|kf1q-0BR%hcW(7Ds4s}X8}P48{@uC$ z>w^yc`v?Dj6-D2$LFMk8_I@FOJ#C(*2au;t>3>x#U+uMuWZv zCh9z5fP4#Yj|INr{Wt$x$?%Q;^-U*8da0jLxbb+OB-Btc059Kc9KTsO#5cRAO%Ga$ za$yFI8g-j6{wNEb#*!~<4)~M-b!aNzp)nrqAA!>lRrIrwc->uwc%a3S1>fktNby*S z^dHrg|Kk3M{{jjM?aa5q6P-mJ5S+3AC9=Op+pjX;-Y=bP)GQ#T!-)o0SA@Z36{dg3 z#bd;N0DzycxA*-xdW`&T9R0U){%?Hc_c&r_Xa6&f?(18R6>I;YfL-gsQ;Q+2uvvH) z%fJXD#)dH}y|s|p9Pi+85)P-O4-Xa+{{{51PWsRFt;Y`VAL?5s7{A1C;*X(5Z}%}t z+hv7WT*q|ky}#N(uNytr8abMM(EKEm(`ZBBE?rtms~$8~Ha3~4oZkT%G?evZOBdql z{KJ>F5^y9b4$7eQE}mUlCGEA1xCk;KC?WdvoqmX-B6fIp@u$A^*x~<5C4Dxrrme&A z8`@el&-<0Q9adZKBTE{PSBKlE0`>CJz~Vt`FFzTF3Dk8ACK`0_HF}=WJDQ8qm7gQg zyTYu?E4wzne*kR`n0ada!6Q+(Kmq|f7WS$Yfb|07Frm2QXNOGr8sOo*9|efV3R$H> z@_xz*zE*B3%+9FtUKjkY4`LGa_eK@m z)J^EAGqyHLQOk9X_57apG2CCYTFvDt*d0i^(RDc=kEO)}leF)C7)*QZvA+WGb4yGku3|;Q zl|13@V)G}OPHvLmuaK`xI#RBe$xw-GLKEpCu{jML&Ae!O+nl|I-5iPk^3>= zz1(;OzZ74?=HeTw@+ssaRNpw&AH?`os#-E$ETCr|vFU-INnC94K!&4Uq|G>pB#sGm zyDokHx+|O1e))_R|10eNol>t$pN$nqNWhjiMSXV_D`kBnc$B}_xAdI`!Ar%qDpv&r zR6HFS_^!2cCVJ)&s(B`oj(mz)z_HPf&x1=>U-F@l4otF2w!7PON*2rwv6v=oyz)~n zncF+!OwZ7^jG9z`o)28`n8((V86X#cr#t2(aD`eVTi-GF_p!vTncyP${N70o8%&~l zEX79MC3=klVerer7ac7Lpts6MGzN1!_11@~XP*oNM3GW-uJA@QbQ82dQHZx6F~bz=ihQInt7=!8b+y zUhc4Xu%v}I^LplG*{TT3G?z|s1x*3=v zdfM}mztU?<6+DQPVk*z4sIjscLZ%%p$U#6+HS5XJwE1_Rg^8(>hn8UtCR9q~Q)mR* z=)|ZJT}C*;o%|$}@QR0eK0ZHRng~RLgBdhavg?FmEEP}sxd*2bhXMqAE=&jA{U+#g zKrAp^2bC0u;DcNZol$iEzH>Yg^wIWRC31-1mM?j*S@LI7Fr5-24+JGq0AcYPh1h1& z8`PyXLvUdg*Ym1F!@gS{J7~M5DDnG%0Q%*pFSe(;JT-^8h0-c_Rg)ZptlgTFBZkd}7pza)%BIA5#;6)x~teuza?zL)zk++Vav zd~^}CbP>}NFx4gp86~hp+u9|}#FP5->LRE&)&_2WCRM|S?4rAYboXL#$+&pNjSqpd@1%v4_0Haqh>x2@iKPLf8JRx(99vtS{)7+1Q%9! za3nlAP4`@g+R!HBh+it)=yu9@TZDzP|O? zLg%kV$>Q7GR0&+n)Oh!k9)|Ax4#QJTk@IuR&4~;}nJF3Ti1Rd3LJrF(d-nnNSVR8`xSzM9)8cwX zg;?f${9bdJIJ<4l4vjF%R8MrgHcn&{!tQAw!~I2@AoZdo815`zXO)pT=HdsNbmd!Z zlOVrYf0Nm5`0K{!f8zKt;C&pMTZS#^%|Y0tL%8}=MX7J1*!3Iw$ubas0dRKK6Tb#m z)HlTnF@JSA$6UL1{rriCn7UJzZ0`jQ$jzdKC#It&S00eK%@l35j|`M+A(HuCu3|$je8e&B6bO9RIs|9o@Xc%>VrC= ziECg)$!a17z;b84!yLImUxd^f=<7hv43ar#M$I^NVTfa>g#%?&blnxn3PxdDqQv$R zV=BEr!lhsgagOB#yr&@f6mKA90c6!9eBTmS?_E6 z3D4bd$VJ{8-kFx`unl>p|7DyhvLohixgRr(zjVQt+3TrGR6hM8IHSLIvQ`2EKWmB+ z-A3>^J;KDn4?6i??#FO{(NgXUq$Md?$O&WRZBSoWHF#*zSlS&8pbNm0Y`&cQl<-IH z$AI^8a~v*pt@3k(0W3y0V0eYPG3FzQ7hm8Sx@n{*CT`8rf`HbCXXo1_tleLxOHYz? z)7^R!s$w0=IIl7Qyy+vE-zmVI%na;}`R(2PGO-yfsW4)Klb@%bzxUUc z-!|i@knvI%sx%krk;MEgPXhv$X<$sl39`p97mo$asx7?4J{$INIVRFPtI0py$P-b(k7hCYL-<pR8-jA!1`|Wb-@--otdnYcqFa<<&fXlsR4elET@4ra!1#kICT=L1{S2gxU zyPI#+6Gf_ZiAmgzDLGA33$$djGV(xno}h2j-%jUru4OqSH=in%D`wEb*+Vhd)?8 zV8AQxI62*tH!#n?ESp2HW{;>Ma&jU)YB_fD@?A&K?`a>y{Y7i4uu`0TL{k2(uvNb*#imKw7Lcf25LU#Y zh1HSEsUP_#;vNIuM;rzJ@d$R`Sp+~YTq)}-))l*5LQJnU!Uu&QWWHYm@4(AoD~2B& z+tWuLspyn5m?p{Q5=WK}7^8OB@;j$kBMaAe!`e;3>3FTlih=EPWlUV7aW)Rl7-Ra~ zN@HY^CNXxo4>S1I$++{hv6mdw$fGwCn<PyQXsFOgGn5u>hwb=GTwe~waF5In*Ywr{|guOB05B@z~Pp>YZL63=(Q#Eeh!R~ zNo7(TA|j`Ua^DY9hu&j{)typsD7VN9{x+D$!s0Jo4n9C?CG$nsdMCNBGnTAr$s9u$ zgt>B*bfh{Iuze#sx|jPg++VaNZ-`O6!;_l$HZ+u(k!0vEN`B~D2tQ>e{zSGl1?>W}TlK3kgqL{%9+`Yr+J%K3I+W%i+l z@vfXn+__>Ho)jla67#>lsw$L`Dyq|*(a#Kuh&47I-4nBI)wqAwJd+*a#V(j3q{Mvb`9Sx+6jlqFKt*V1sOVtB5>oyR1Ex5Mh{?2G)>2|!;J;sj?eZ{RTd=RGat1;`&iu`~Ah z3Pe&mCdj`cB5)8}K+*Wav;`EjKTKOdvHioe1r#TTwG~uR5y7nB8IC#h&VHe(Wa5Zo zo>;dIoJ1>`V8dhq^3i|}9fwORx0TwQmt*D%TUGb^3n-j^n6`kT?uTg$C^&y;wxEL% zXx@SjMxdxTY{QD+z(MBlaVH(SlwZ8@_m^~JR^ae2vnKI%Kl%b=@ns^SS;29abSBf0 zJN$PbXlJqu z(B)aoQ3ykx$&tU%gf@@i_i4OTy4I=BU<6xFGtN~^7(?ev|jyT$#J;8MH~<2!Tp=sx*7M_3c`<}Rj`5!Jr!A4gKN_pVG- zaWw1}-0NJRknmyJ0*V|TrY)cV^I_To3f98f%DLi9N3Ff=K+ntgFe7C6$^7HdBZjo< zC>SiAGx5v|m9#2D6BsWU2cK&hat9SzlJ4~vP)zqQZ2^UT57QPz_H-6bb?GBX`<<`!|bvO5voG0QP37BT(&l35%?<_F}-VDyw z-0NJRXy;+t0t$*ArY)e@>7m*Jh(p5K^6zq~%Ifr_InrEQI>=cq=7G!DiivhofsEP_ z{*%(F>Bb^-X8dHddvCg#qZ>)k(f0ZaC|r4%wt%9VhiMBa_<3lypo0-8egqwik2KE8 z=J+o@q}4aZR1v*bm#$G6Ytg>BeDbIHX)&G4`RoK6cn0=sS$byJ5C z%Igy?z2$Ob0g;t&OB-8Nj&@teA3IE2Xo$!+28b6kHgQv_PnM5&;ZCYc%-Q$vh$nq~ zb{t>f^kLdMH=+FI65_MW4ddi8b)*?FQ?BuOexL58tGVr+?Ih-ShiMCK>PnVLL(&aT zeDo_J+KR5lTDnDoIPx1gsp+$C>xB7MSdoEisD94vL6O8$EgS0^ zyc=27-}&pG+KTngk3BBbbX(nd*^J15z@(wAK6{9>r-}BA!F)2Y$9MhkpW4d0J3{=- zHV?h7@Jy9al#=%4q-d!jzObm5om)=Sex5jo_6?zf5hy|n9gMT?Si$*$-X;v#F|KWw zwd>{S=8hVdW~!#@;@je@yPbkS5Z_u8hwoRvRNG>iKwFK$$Gj&flB;%zpO$JV9e?R#o zl*g}|=}R{l;xS(ndl;{U3Zrc%n(DriN317*sc8KAe7^?%fCy4yrv9BB%e~I6<~{q) z&x_H7SA6C@G5SLdDS|pbA3lmyy|JUt)o^6wupi-{+L{cMP1oIOpA(TbMku9YL)$|i$Uhul~0TXUN^`WTrPyPn!>*2A;Y*xA)ySZJo_DW2c!ulHs|&abPN?aA{PPIw9nX$pjZ zNZuy4E>*t{k#Lf;#)o~ce`<^F{mFzZi@vC*We;AowN;(Sio`y;i8KA0$Y2>4mBAJF zFm0)u1&AY#vy;)R`6d`i1izofK#UO}-EqD{?Hk4ak{q37>P332;GsfyfOEE|?1W4Y3T*+d0U@PSY zyUfo?JInITks#o9>fJHqt#A-oN2NG%b)FD!Gcs8oMr(tDZDHEDd#L1_ilC<>_O_yx z#Bs9<awc4>MH$u;KK8Z zo8(uC(1h@hnJSDmNWQXMKX*k41=i)6D?-eyD;;fVZ5}^3aVyX= z#qK!LVcH6%2#>zQaYURHUZcjlJ7?sA(1f z_K?PVrPP_OtItqsl3Y<#ipF1!#~nRPTY78yrnY`sx}^U3FV8QNe+YT?Zjo{&0;Tno z#IbIlG!U$<+yEhd;n5dGY4L&=6zR?m+1+q$yqTj&UX4k(6MMDUZi?Swjd5B)o!od#)w58!jNXQghz^mUPE=u=G!DnFP~0$%9Qm zlUrdHjIkB6lunJQOY#=(0)A1BoG+`to4)_4tp`tK@1qDm$Fri5 zr;%Zu8}>oRVN`;eAp-s`YhGaTy&S>yRbJvXTSOYsro8cCql75brD;P)g*i7ikf89! zyS>gmgL&+>5>H~muizw5OB^c&tQr!FI$k}^y^JLoTE5lLpGby@87*UPxk*w^jk zzGpP}NhGc;qE?IZM!FR>Yf<#3ZIGQa|8A1`r?#$A(JCK5%e7)m&~9B@vz1(D$8Ucn zj!+A?_0rK^d^_61d@oVDIvJM!j08gl31$H<6x#Tyu=H_{M>EnD+&gSksNb#p|IvFv z2O|*NLkA;JiCox^aM0=v;*g^Ju%R)aO1g(>3#j1kVcG(!%X^r%fNI>r+DeZkLVQbR zM5A=}f-8>uET`3*ZKq7b=1d%8@8jF#8>S%<4N9G7eZ5>|yW6hQ5cKTz7f>nO!?Xod zG50WS0TtFgG+WTY2t=r%gAu5rEo{TW>3o-tj4d);P#-AlR2dU4!}zFLRK;}deyt__ zRC2vX6RrqznG1baQwXWNHA$$$UgrW8zCBD^K=pAC(-u$}-NUp6RFW3fmN%0dLatdx zlhmBgu)&b=;&q1Hktg?61aGIXnvJm=t_lU@I@yz=TME^@Zv=Q_!M}n9`yeb)!61!q%9r z7Ny5oGsfs_*_IsGbKc;c5}wgs=K_^;Jxp6bRbLO&7Ep26!?XodXcg90qE!97aDUCX za+Rdv=-=J83Kr7y&h`s=;z4Sq%$&vx#U9SnW z0ez+T@M10w%AVXxqF(PnQ0ZRh0##o5>g{Uv$5k5cRkC;S@${@s8Z=++5#$QdYHC=>Yg5^Eub2ru(pCuxt^-pqO=oh z{$PBci8!8;#%9W?y}P_DVLqs;b!e6j&6ol|#UU)Zu)D|Uox9dve*u*mJxp6b6-f`% z7Es~RL$d{QCD@mn)4m?J{POee?iog)8`L~tc3nyIg1A)dZP2GfE*RBr9hqq{adS#&n3?GOC%{^j>TJXiDR3bt^z}D`2v?i zj=6!1zMh{0((S5F<@{M3`j0d8$Cv*?{^s_jfTsZ@Oa!<4YX}bds~H|n-uo+P4>g z{%VF_gVwkNx~T5E)4u8?Z&77qAj8cZDF*${i$}U7=pVMwhV^M4#%U z>TlmjeH`){)I45eU4=u&hSY@A9gXwE^-XG=9IICDk84~48Tdhs%ZK=s;inzNPTxoo z@0e{Dw-UVG|K6p+#_eiH?V29a%TSo+*X)Q}yQB`GGnKC&^=)nx{)9cLjg3v}be?wh z?9j9sY}o{4f3<1J!DsRMeePzAlU=wjQ>A(i76yb7KDIo`(h>IGYFq-D-&0O%;^TEr z)sn!07L7+s0X-ywD59N}m<~A3MBH;_8D$PKFs;3DB4&{}DJ+o(-V@E9k1VytI1Let z+Nmpr3LO(I)Po#GRL;Ce<|=!qb4*?Q`8$=by0c3HlaAy}7=9Q&xkLtU0b6-0;^ap@ zxAl_x5lSsBL{JAUo%#KTdK>!B6;*lNdy^h)LQnDoHFVEO{>zVBqLFNZz+%`N<|fZx zs;e=$t(To$*EC<%6X#FW(u1l6lG~%UJN4Mg$+6C&<~D*3$?C~%GS-~x2rkj6Ctk(5 z_@9jbr@j!7AhbY0Sly1Q9Qr8VlqWv@n|ck`_`67n8DoQd{M5BCX*9#&)Ml;Yoj^X@rY<>%$2MW-0Czy*_IF`ORn`9-RH55D7w8~ujJ;m-J@APlZmrq zW3n{EH|y0+1}VP?kVd&%4+5yO?eU97FcwX*0q>dm)tWV`8A=+?>ynNo7C7}XzLCl8 zqPS0>?go2xusm(A|9Uw8!dOJqRE{sJk6J zv5wzhdFS?Y=S+Cx`PL|$Zlc<4bi!=@yK6^gb#@@tNP$v6As_iod!wq}s*&xmzoKb` z!Km&jW$!D9ZYbwT_n<%C=Xqf(jvG?P>k&1M!vZlND%67_PT7V4kw-KNbc%w+B;;M zOz+7Z3Lci-c23<1_>?dRDgO_Y3<~+k6}@LCaV*bLw&)JjT>Ge0t2p~8b&4_`8*-WY zJL;BqJAUPsm=}$vBfy8JTx|3|*^SRhK&V3 z$i??#rUDiY0V%wi%+aYIY#rEH14xb?Yet6=UB5qiTm)5-Bvo{Uq+;+?NkXL3WQWc1 z(tFM>lp#X>7oi;vsG)m0T>M;D3_QhzXAQh&h8QDG(d}2DR~p(foVOp!DR=}U!9&#o z$?Z`)sM_FPpO~*d`uYPNzMaWH5fAbzy;FDL>T-XP>~X@)Z~YiZ5ZaH6>0F5Uj0bGv zc6_+q#V@MY&c0zAOr`I=*KjG%q^9~h~v{^{+ITj&UY9pLIxI=v~bNWRK7T7v~?D?kS8?z9Ee+4lHF zmg5eyolwQ?q4q8tN$}up@10TK^Ld{26PsJnsW_NcA*K^2M;wD*L!>mXZ1 zJVQ`N(I=4>OcilFO}G+o^r6!?cLNDR-QBg3^IkzYKChZpuqjK0{;}CriH{wIC7Nlr zg&!j8^AsT{1(0?7*;C8X#*>IF?IZ7*6W0uB=(Rd>D+riOE7CP*33`a_kE_!FrPD%` z@HK%DFh|2FFi^^`(lnw3Dln);<6W*BpgN`SHvNO4kU9-eo-9O383|h+FWmmEF#g| z4}vjOZ)cy(@h{Hbqky^_NN$f>QYNW2B4Hw;^zAYv*TI{idJG6YNo%ts&5Es5B6!0m zzquPo5bAC?K~$|RbA2;mf`|RJd|gtXh-Go{$QS~&IRkYV92{jJ>j{8tQOHNWdbyUN zThVWYZp{>5`a|pls{a%sQSjmh;&k6;Y8$>F1RDU-Kp{%veXeoLtDHV3(=dOdSxu<0 zChRKGxfqlIReGY2=vs7;JKTWsJ0VJxN&>+#@flr57Vn(SVSQW&ny77Sc3fz9ckdp8T$5YxTq&!ZUeR`ERqnxf@6j>TX5o_iXZz9;p_&RUCuryGo0ZKN7d}Pd=vOrJMI%jFjQ0bB(S~~DGcDVa7 zt&~Rzy_q$yFhP_6WsyRZR4Szisn@R_Nj;aPk8$KO9LBw?H?6XBD|7F3^Qlho%$KC{3MDruKo9{a@6%;BiU<@^*;C`P z5XU`4m+mCS(w$?vCU>{)n={g|&-sP)*3k)tqgx`4!q5i+I$Sxzf(pmnY)(3y`+6&_ zgc-Eih$N22ii4=Xv1Ff~91EX2>Ji%(!%tU0^KvrYHG z-zej7Hm~mGEL*i*-4LKBm&(GYXJD&T^-9Um+ci*i)rp=Wd_XHu%@C8>-i_fcv z6>Xz)e3jz$%PB+df{=?YfD&jSANgus7x#>&Ded;if}G=Sn`In`* z6JT{3pv+i^5+mXiq<48ex=dQ55*K0yAmtUJga*X# z&Sg2iio139NxcwNNX8lQCOvV(`5+;o?4XwCkfjq)E-UozDWH(^=ews4dfNIY9@)Kn ziktgOcKw$-+@MuBolnl}+<1hGtiRQlC+~MhnM3(izc$GSM-opb)5O@vQORTLwiy4SvYD5 zOS?B*h7dp8H@L{U$Fv6~xhAUwpBTGndF zW+|>WF3z}%zjWSJof@Yc1P9$ygClUp5&8NT1jzq{a|R*!f7z4L2O+rkuqc8cbzITD zZYCO7S-@ViC7IP)Nz*cO(Sbt0x`R~c)Vufb_q_%- z`d39}SIsZO9vpkoWIHkwLN+4EYT1@b7=3MwG`@OCc@jUPTlca`sa-9cJP1x#J8H_4 z5eEd?ZJZHAr_yJgm|u1385meto8N@#A3%30LbT#HJ!Se(-D>$wD`?Lvf4*4}MkUbg zilDErtPi>Z@Ww#*2ZDa^#s>fRjlVSy?jNgyQve~|qL}Yg%0QdYe`@n>7Zc#US$l4? z(3jw_`aF@Mz<5&v9(#j^ha{A=>s102Ms`<{-m=+&*|n`&yBy6O=mcX?pFB8TdV4@=JCAO!L|~k8udL zx}RR9kl*0z(eS5z`0-a`JBh{g(E?eO@R8+PbQmi$RfIfj8625OASEIC38)t z2gnT7;^E(*` zOT=?m=QByt&)F&}WnaE-?Yq+T>8Qk4PzDlnJ@5P#ga7AGmv3|G+R3j7YcwP%1- z+5ZJVJx&)_ebC{oh}#=6K}|$vs!jU@z~rst&9zROsIvyBjspBP@hZ+H5 z!36;hFc=DeeY*e%^n@D-!A%s~8Nz(k89`r{ZvPGd9wJumhU#rP)K3}Qq2MQXpL!_n zV3``B#Rq$bjFacykD^P{zsa0u{=uS$X5>TLT%B+ z*C(@-1WoN@Ad_3t9Q&g`LWWzRnei+v0=JUFH z9EY<=FQ+VD@ga?@?Lbd@Qj_X6H z%QnPRr?eSvznWZRqMp1|G819per99A_&AhVZLs??j|aaAt-kAV+q@&gU52T|$#~-A zM*o{E7shOh#9zj&5cbWiZZkjY!BSzWG-9pr!Er>Td1s-P;5-^Z8>3-3QxT^7FYPsa zUVQ!CYq)C4{xd6AgfGl$GSfUU!MIE>c)!f*^bMp9?-knA2O{c~baZrfUtG-;% zNr9pomGP_dt=jkY&#V@$Cwf#xH#9IF_7EB#2N5c2QJh{Z)I1f>C@TNz6yna0{QR%% zH2_U5-!UuH$n&l#=?IDA`d0`EL>bQ;OQL6BCB5Y;-Ha=HqU{2E-QfX5%fwLODM&{e zsdEEaTBPKH(Rmq8lbH(XuWx>gdrkAC31+Wh1q6cbH2^*Jd9UH1xbhQ^K=&Ge(C6D; z19X!IXrzDl;SaELa{y7G?HZU5zY0QtQd*!v@RO9*l&j~`kOtUenI+HsNnofs*Mqtw z8QavGrwy(mgc<~pf)SvU)?>HSEYK5fAVfD&YG>H?{I65mLu9|ujs!H7eAkhH4!OcG z&jW~}Ea6~dc&?BM-)lvebW^`nNqenIC+o&L$|FY0OH@=7Xg@ukQ+}?^LHE+$| zFAFWEfp3=%Z+hDqS8>XlNa4}DnsS7yrF%`XF|W^!#%G-W5j*)kH@N-_EVSqTN41Jr z7Y@07vd~BL0=DwqeCY*JZXhH#QDJ9K7$g5BGIWT%2^t3q)4zJTpeJifvQX$e*=gce{K8pU?owW0 zYT+aD47vRFzbp=f^cR8uTm(k?g21EiU6iZ}0-ikGF9N^KVwxeMQ$e6-QEg5|$EA5r ze1|ey=Bax^?%AO3l&QaQpk1syoE^5lg+;jc2s%0!_ldyIjGhyW5EHJw%{+bH_xKcP z2Y-Y#R;P7s$nzu5j|d?&{09*jsKfnk2#f?pU?5-oBm$e9Q&(iy?O+{|e>hGQ>CIha zo}>{8kkv+~P&&o*6gTSxh2V2%72tp8lFM*afUBi=sAEaLPo*Y^w7!?@2r zZMqFxAVw3qJ(+1a1{NepmfBiHF^AWTq2Jol{kvQ~%ygJRrHNE-o5nlP6JL9eb?iML zLNANyiKlN@O~m<-Sq@PP6ANkaxumE%PBK`Rddw%_g6iOWyqmt)FT-_|zqtPA z;yTI~Tpwn92&yb$p=R1IuG>$$>s8!Vqn>^$ZS2k7I*ZaqcGt2z3_Oqv0o|4op6_^bDGRA`X zm$Cf0j0N=zV;QJCsT=OT-)XaQ?PH7=EAyFK z4{$OSDCg@(e*Ra|M}a!r@5WeAp^OE{7eC2Zbdk!uEt!W;Z0qDacd9vKqmTFrSB!rL z{X!jSJHPau3^3|m#sc(&8ywotGnU7pRb7$F!}quEcwxl1%hojA)))v-WPbiZQgYQ! z6Kw13vU@xTNN{hp2_O)Lf%g3ZYl8Zwm>>No`^*^{N~Ce~;;dWX9Tyv2EwX4a{)bfU za=#2%(f$JKp9`#LUx2mGAR#B@mbYZkeu0(u3KBkhpdrVk2Z@i?r)M_%7cyFEf(S-XqBkpGo&V0_;duP)aQ!8myWVTOWJ)fXjFUF`?@XVDMUtjRS)17vc zfMI$8d~=nx1d*=R>;J(}08oef-GCJh3amiB_(@>py}tcC)om_@1^giN#wEEMnC!Kw z(`gTfh`Q2!s4w1}0;BB(R-h-`;Lv{_STn88QLij8PO0FAzuCBKacuN5ww<>VJG?+g z!QF?}A;QjSyTA%0xVP%W0f6-YqWi^QFK^Zs32H3#(AIMs2(QxIv9_%jteKbu7iOh2 z>?jp$e;Ed&|Ha@x7lYBiU~msy7DijaTPDN(VsK4Kc^m1NNxk0Y$IVX-CubwNXb4D$ zh?|TCH*Rw2Ej9hc;KN+pj`vkc`ypUP>1{7Fc=+*!9c$FNes@owIaQh{V4Kbi)wxSxLuY6mF}=>gZ{&` z;AyZ(vP>5keJ=(BJ>dq2@$(pLLG4J6*C`cnnhawrz~2LFnkcaMlCJkm+$3>;&L>H> zZ}%hu3GOX?aR6ZbcSQFK*Pu0hypCMCpioZ*w#kI6-bxzlIL`T{!QL3brc~Mz<-ZKB zG5*5!p9|L*U*Nhk`uUaF@l*%R{lfLr2@{-zjJ)~_3Hga*)E#(USHk>XHw{SRKgOVI z$o5V83)hDU*HeOpGZvi^ZVCH@Ypf-?!MVY;%1;5KWc4>ez9$_5${T!+W)2QA>v9=Q z+x!RN8mPnlZg7nOg=-*R{3KjA-W69*_A1*LvdOFuH;8ScKKn^2HlDBR-j0>Lc~DFm z7-KJ713lpehxzkxZDMoT@K1}ZtCoZp}!2-G5;d_ zpNs66Uy!|BsIuxk{JS~#{USRi(={984~>fFHt7+2rAf|t9k=GFyql22YA!J%<8!>~ zFR~vdvcHeY46u5Rj_a^bWWQETBR-H(UGIvdXDNx25175A6J?l5ygpfxY)Bd1vSJU1SFm+;s@Ycf9$u9f2K^sMZGw;!Ey8a)3K6< zjvtY|Lrpb;+cNzeMr7($TaPdCGno}YopYmAI&*(~S1P)<=YioB{b;~|tgmxb(QTlo`5J=2b zo$CK}@6rFj5M_><7c<%tBK-KnOYV@swu%PGj%-{FpPAP@L<%ne9z?=iQ3hXMSrYdru3IY3*1Xb6;A01b}cjapzssYTF* zH>nnn*M_`w7BF;a8oZmO!zr7J-=10#OTSVtueMd7 zDHs(i;QE~B2<~?9JIwu4i%%Gqs>?c6*{6KCQ({~OTaR@GI+|Whvmxm9$$V_g8~h_b z|0~fApjY}yYQYd~5+>iQa%9YCOD!STXu>%V3_3HCgV=?J+_OBu;|a!rQVZx!i9k=d z!Qn!3)?qJ71Ul^wvji8bbD$7a+eyQ*@`(@}h!R&z#(8ONV+MXG&i=~gnEf=2=#h1k zGhXWDop=cakK~k*=oCz+l4;vkoJTZP+HM?eI{S@<0O|b8LcUz;4D)FZz(RmTcMsbG zIcx7>`~8v|pyBzuksBN+xl#6u~~=ELm)xyO34VK(6kGVI8LnH}=cO z4enoZ^XHNq+%M#&{@J8pQW3*7wf~!O6UZe_PRJze?yP?ZxtZxCr?Qr(pLX!QEPNKU z!2KpbkZJ7XD=Zq8fpfH#ipu*ZHv;C)YWMlfgJhyRiUMT9XAPA+kl&w8GPJ@=W2LC! zxb@4(4bUt7B)P$Ak~WkzuOLet+#c)NhL;!iuzH~K8Rusrk2Fc|MI z2LHJjjQ0hDYn2xtquKg|>ipltV8|6VR8Q3#)ENE{44yEF%HAAV??bymho?cn9>4WY z+C`bc)`ewSD$X=!V|M=-JRQ!+R^w$EDw9Nj+!}SFokmn=OlvMVKM&b0^F->+A713} zuLKx?Ug;+>Sg?ZAX!CVSSjPkQE!Clr3i))EQ$Z`eGQ|-*c8x}MAHjHgF&O9xH#q!X zg27@gvPg4-3@2vfhvpDW!(W{ zjn{|tHSgjb)#Z`rR;vP=4-~eHv+74SIVxRNpJtxIX1MGaOd1(>@AGBRF6z4Jz9AFu zK0L19wys4-^MBldK+$V-mT&lmALZQ<25cmjc+)xyQ_5W4zD)Np4t$~vOy8pZhq@T! zGMQUo*9Z5V=l!r@$)6ct{SC=qAI#m)~mfxigZ9+4-sR3;U&wZV8%8Zx{UlX$Qs#+;@bL6%pwu6 zsc&4O$GO)jAw&zq8vXCfweBv)D4hUvG4aYy9J|-wtF*ua#`UKnbRF16wcZBRnL};> zwED#5Un=P~nQ}P^yp$4!ba9B@xpSQW(W(T5TV(SwA$!7xrg2{A)#tHjFjX9>cJKTckf1r^t}q4 z=3S;pCKu<`L?fjE!@0l^PXDM)Ec$eso-C(YyOoPiCXTRF(1qiWb;#W_!^)En6qNvh z)%)>b9snY6Frq-0wZn+sv%o@qBgx}Y=wck)teQB--l7XSI!Bi#p>4B3Q_&!zt_NTK zT|Up@|Ap)lCE*0bj;E<9i&wc8c~IxLMEh~!0@Sw_(elr5&Ldq*Iuq{}rHpAW8;3cM zW!qk;Vlo6*setG?Dd%2El1Q41o!nCGXP1oh(bPO$OOT7~SV%%LDp9Xc!gv1}gLyGt3|aP|KQZx}>3rOS)l?VyrwP?j23sf?VNktW0~i z`+a=9Z{>NYc+mx&$P;KEYnl3_%q67Jz8`LUqLlbL-{s}bnew+RM7;Ao=588r&dJ@_ z?;3@BZX;?7t|&6sD>-=zsW~Gymdp$%P@OPZTMyK5Cq#Vu&Mm%Qozkb!N8L@({TvBb z;t7L|Cgb6BD#p5}M`n6%iVWRAvGsdILwEKm_SK6=j$TQ>!E6o}qn>mV9Xx99o`Qoi zm(^&>jwL*C>GV$TSwsP)bmh~`@2og4tDn8b*7t&*xGk8l<@E}#bd2(oU@my94_

vHSrHL`4Y!))mrU|+My=J*?NCLvdFH?W^2AuF;5 z;H^HAd%^lZMQrJb*-5nbPf&?pd+=T^*X121D!XTq%{*6JEFWU=;F+tYu?UAjkMY#2 z_9sGRCvFW^MH99shL0Yf>5~Qh{TcKETfSaucsJA?dq_JPhI zPm<(TUpm*yla4 z=0y*|Q`IdP?pb3TiYFo-X$Ac#{AC2$7ghqnsemwzu9~v{Z2dAwHhG9EZDazpei;a& zRq?Y{w69vPnqR#MgGeSEyxr25sUJM29N6)7FT8xxZ*ysx=x+GD!*!Ywij>XW)rLOk z3c#+lB^6oOWtQR)m5q_BOj_65h3>*V2%R`fu8woBdnX7z6(kRW)76fe@?^vTfp(W_ z2LJetzcugkruskoc!XaT)8%ZK<*%I);z{X1R_P>}O9eP*pICS4EmsZa0;^eaac&EZ;X>`UEO_7o5)N%Leic*5Ih^iR;MoSu9bOhPL1GBY<@NAIXn6z|P1aigxdofdul_ zZ&!i}=xR#ngaR}re>b5V;JC^GL=SYb1s_Uid4kPP;FswPJcBC{Sw*}XyW<)jFPM&Ws&D)LwIgU z>jz0*!?enm=Obo=PoKs45z+ZeXn%IOwxA!`C>KiB=DlA+yV{JA9bmiSMtyQ+FeiIj zI~B8q&x)(PQtvj& zr`$^BNaRt%|6$!OnOJ0T_*|R%$7I2Pglrt1fkhI#J3@h9u}N7ZQTvsX_hHn zAt$eM@E(UD$3RDNw06Vd02=@QkNP@;K(4zJdI@-K_ZQ^QFO*@_FI}+r*3}IWvNzHO ziKHC)7=N?n=oxN#oJSpw($rJ}zl>oJ{$&_{F2f-H!Z3=j(RklcOFjR1zYL@OV}DZi(?o;uE=|1lzh|n4!QQ_eb98cx4k-yACGY%P@eRaDyZIB@Dwi%j#u=1OXNj zwcjm{<=FLk%&`WmVs62k#8q>7w_d#aR_+T(@V6`XbpVEOfEW=F8M%k{m5pSfOT8&o zRVl-@tXJcPLx>1<_7k$ezJKy!u~1Yx$ansi@dcuN^93|K5(jqA)0DULsm)rV!rl8D zGF7nB)`cHE-0Iy7pcwcOiX6@s8>CtSN8js&^EY?bY=5`dKwo=N`?aH_30AUF2qWZq z(v7W3oKs6h43$I=ZqL?>mYz#DB_a7j7@ngr&od;(B!v7W3@)y}U%jt`5(c#yDrFGI zH149}eZE*K+-R=UEzIdaA2Cq7R#tjUndN_wFaXUh-w}obY@8iH^gw%T5YGH{1OoZu zCkaEKfg7JtN*?WpAc7AL>f~da50D9oc1~2|y=6ZtlX_+nOthCU06pObNBm0&gL8E- zGNJ3TB~g}8$#q{A+mnt$;kqSpx)|t7!(5lnWD*I7Gq#qcuQ@ zMe*sZj?4w2zE2z`MAp~o=&?j*19|iA+8s$KO7`dZ+9iR+T*V>53~1dlNH7CRNg@cE zBUF4^5KotTu3J!25(JY`waX;f+rF;5;TRYw+z^0{r^2m0;#glpcE`#5&oBwSewJ=&Cr}c0+KT$Yk~?QMaA>@u+_eyv2zrvwZX58B31?h;6%>2+KS)V{=9b@$k`O~F z36L*-l9C|QkrOb_Ny=l4Eaomx;$)aonX65gI6uaqt`_d4q`3+vhEkHOU2X*Qgc}?Q z$uHp~nGd43{B@CJ#TmAXD@w4|A79NH6zj-}m`{73zj+1m>bLY6Ai>`*edd3ilN^BQ z0P&Kl-SZ1Mu>lvW)WSG>BV9QSvRrSi4Y^8&uHAoR!bgbHWr^hpJsXs!?n&Cm9XD|| z`#Mc!VE%iofWr0cSQ~LaZ5AxeV_^w(^tF=aehi-~@7Y`3WG_MnEp}ely|8T27p=~} z7#O)tv_r!~c2a`AgjDe)TUzkAuey=LtI#0EH;Nb^>sFr}|9pNSKtm|XFO)XvIu@b3 z3?<@y@(Z^D3i(yUH4A##6ncq5sFe|-*z#6c&py`%4|P~LCDOM&U|MzXoULgWcX;Mc zKd!HhM}>HyU)1iUVLFkC2FVNO6Sr6DI_!2(ZyOUYu94u}o6n!Gf#1aAKm6JX_$_(ZG8SbC#*-=HWNj8Z#e#BpSH3 zy`(CckV#pq_3jQYIemxYw%uK8eMrrqFvn_d>Pz@4L&5xt@@?lk&Ws#mO{J~~_KhQ( z&)CQ+rBvjXH-TeM-iMwHqq!f!iKnP;KBqT5lABcj(i2mmeK@rtK4ngIJ_CL=BW}sy)NRn z#<4C}Gkk^FN_~06ughWLmi02;x&F%*GbAMYUd_-TH2#zjdhv&M-X2bX6;f73f9jgGZ1dmM%mG(39!jo%v&qqW<4p%?v6{ zsBE|}dA!{1KEGQv0^Gq82ew% zP`{7x`$Q0Pom2Q0yihx>Q~pDf0GvAAiAd39(8D5w4?hY}9O@y4!d)&7G`IY2s~Hj! zuicgdIpZgnGsc8ZR@?Ke76lQ?EX8IB1WmWNd|YLwhiUxo5myOs?0`u~pzE3b-If47 z0$Ivspp13!T1fl|gvNal8zA^lHAucLo5 zO=QeQUDuF%7NNU&Gi0Cy&COU*YLKr#{|ICm(hLtdM*SqjTo`=CAx`8h4-eJ^Tukr< z2AVBg;TybSa}LW^!kuPn$0t}c(`kImL*$ETOqkG~T*0Lm${=`j@`5)u+bxV)_`pdq zJwL*e2(LV<k^D@NUqE(Nl(YS5XL#BdLO!uNzQT~IF z3iS?@&4^j@ZRb~7G%Z~Q;m-%$sGJSk5^eA2B(bZDymTn>w&xs59Ve0DX_rgV{LuB3 zhxrw@!!$To|9*F^jdt{?;0KXe3D3(QWR!G_=1V=?(}NhXG>y)JowL}u%(0YDZKy9y zu0I#tZcp@;2-`*uFXEg$FO_edyRDA#y9K6SD(4OM9pj8+rKR$47Od{AQ{|=QlXNuj@XddzYL;tb$MBvKy7eW?jj$N<6{gd$6RVTcDhZ{XV{u4y~y>NCG+7M zW5={ELyl3Wjgc(boG|qINbEH8Vz3JV3Efh3r{_u9` zUWNn+iMg*xgVxtDyarSTjHb@DTQ27_WCY}m8k3Z&Vpx&lAbehi5I*qWCcDH_AE_8h z#_uJ4&ENCwN%D0QOUuoNX-hlIvZOQ!aIj^c|e{KpX3FVg+OFEVMT9@R^)qUrX3bZ_KUY4<}detZr zCf~s}>AF;Ca%>=g_4;zdJP!T3#gCZm?|VvRIB#&K#ol#v>o;AotRD5=)?j;Y9I@Lz zpd8Y;o8H;?NpmRe2D9%@CRA29Y51}d(h}&EZ?Io^aKu_~|0|XX!t{GWi2gdsl+RSF z5pd*aCs!d^+S+S7)k`>DV7B@9M z-S8Y&^xZEPzplAWC#x;-0)^KDojjHcBkr|Nrn|c(l#p&jx*G&(Bm_hWX(5}@P z_rWhZe4fqrKF5!q8~$UAx%RmBUh7)#*PLt4xuzo@_x6!75?0&!2x@01?_}WTJZWIr z{;Q+}nA!IwB`0|o-~OGJl#GI(DJeO1oq)ZG-@E`I_(LF=g62Bmo(5m{-DvI@ip&j| zx)eJkysq}Q)z&`hWtw|h2m$pBaxc37*P#sF1ZCen>uV@bziG zf|w!Fz#(G&dbu<@HNLsE;(CowSvts!!B=Zp^ii3rr7-oxjtcm4;TLG$LzQo}lh%&$y+&4Qp2z&y9DQGBzcZxE= zCgUej2I5uJHJ-?xCj1x9sgN|*-rFAKH}hdcU5z*78;|Hv@F4+s-=hq$Atxw<|GS|K zv^C%Mn1-KcljSb;teuz-?2-a{Qk!XsW2SNTEKUE+*RHd`3{SFTJkQ?30|Lr`kRbxB zcI8yw(Vse9r3i8A>(#0<)gzFtn3`>*;s_z_dN)aKjmr`7`q#-j{)N2%i{+j8i@ZP8 zuxJn6lr0`Uue?9sSX!Zd-|N>NV~XuEvc^%`RZ9Rd!876OaJ> z@8uoXkP~?)_}%1vQ2>HL(+bc9=>hQT$*v*nkPYnh#|)7aBxu+Agl^&CdD;*RnBmD6 z^E`WM{Ljcc2&tOqB>@1Tixq?_uIekBM!&+wQ5n1%bS=J&%Tn*8?{~z@ps@Tp5+JxB zfqyXxkbEJ5^j$|@fr11(z4MYl865t|R(Rmjv$2|+O*yl-LNFXIWyJC8#uwnHh_iVvo4Rx(JY zLjnlCCjnqXPDp_8cOwBR@+T7x6D;qwZ3x1>8oboK#YcP}M})fs2vg8)V0RRJ?ZXAk z@cTYo|K}v|4?u%b0u1R3#+3&lmy=KyT3$Be`tR^8amw5&igUSy+oN&)*Fgi}1vLDN zp@H-ZG^9p(WOk^j^zxk-8qkDp%`$q)bWgUutNSEmnsD<)YxTxk8n!p&Ef1T_{h}_Q z;qMF|9OWCy=n;4xoF-xbOLX`kz7IhNgR1O*BN<1Lp#O1JszW=c&;FIwR?V$&hFS4H z5E_6jxZe&M2v4B_*kt@9G>{VwjwD(#=?t^=Jn!)W)4b18&{YJ}D+NiD2H0OtON0av zeh&@6hMYhH(eDNgG&#X<@SZy;$&Bx{SY)H!Ag;|9#I728G_8$ae7WK_)z{Wqzzn}{ zt@S5C!{5jkr}d*u@f8M3JMz(+hk?b*9+9dNNJy}c^GdsF5t0B*sm^Mr_h0$T@`-Dj zfR%Sq;dSscIZh|VCg<9t~@4slrnLmmFmFa>k=>3j|k(!gP z$*>Toy$b0A%EGh3Tic7868TV^@-kHfl&zih2c4z0s?Im`bGSr4CvImbBQv2KaG*|v zDR@P}Yf8*kybE1jcA}!qD(Q(nIc{#uiqJCg^sX4jhmjMs;irj(q6{vzXcG@#Z8Y(P zdaAO+zXS#Cfi%??tCsB2Jy;jv==c6mA5Rvdi$Sm~Y4J6d>6+-f#?YVxr1j4CDe#Iu z;enfh)Ncf?2|3(QV7M{B9ot)pdV zx~R%g>Qe3z==9q~ZdgxiRI_&c;#!`9g(!!;L5Q>%<&DHG&vpX&CE_l?{vw{%uD_%J3Q=_#cEi?dLj6$Ni z!k$!&Vh>~TUul2xPTXSNVNh%S1HFwNcZ$JZBbO?@igl1ZH zhNo+*apvV@C-OD($6BeidR0b(c%2Y@EBFz31WJs%hB|-$ryibS}$*zWiQGSMPA+-Fu(iJ{5|g{wA|FmMULKPPUJ z=^#u-wfAwW^snwh45-pO(*&SDp%V0K^28go!|)}*h5ELB^zL!WFq57oPnYTPY;2}4 zP0PKqEL$a%1C~2fl=o4H0!6@})s+E6XY!VSq4gQlKQ0>^r|%>i+t0q*dHw+v;F!>d z?|M!ji5h>R^9Q((TGNr+;RSNxVMEH#)oyl2v~OMKpX21mEkwJCuNO-JfQQH=r9!2+ zc@VE!t=mnkgxw=&$fQ!E{ZOuQX1A-FECN2)!@UD+7wKy@_L2GmpAf};-UIX=zxB2l z@R#)ur>9ss(G`jOhBuYHul_nGh3MQ)%29)}g+4j^158^+E|DzR2V*34CiASXyAx+_ zJBEXU(f^2C|DO8MfsnmT=br0P!GDE*E*kWmw?QBI7bhjLE6lslJD#uVyiN*~GkGfp zp+x))WZ4xdy>Z0}t@VSjE3|&t-teoJstB-uG}RZ8&7X{HVuGV;jI8+TNLHUSvWY_} zcO05KnGlRfpl=P`sLV9$?8&&T0xO&U7ESRoYV9BBqyXDnzHw4O=(-1jDM$_a07R#0 zw7_1KZ=M{4j4BXJK})_OI(4srEu)`wulx-cQ=vrZFQb;<6II~!ZVd3%X7$BR zhI)?jq8AcC^u2opY{-dwMf|(DSMdUEYA!Pw1A1zou2vzal^NiG_pDo=T%=W3g7=sa z$M0(+LSTmfMI%BGlCMB;g#OP>iOAae;xzIuA+)UP5b&Nrmh?YrA-3?`XS@2MX&A-<8%NW`Wiv1aj`bnV@Lq;_hbWX$O+ky{BC5!f|%LDkjNsoU@d^V zi7UbpMK2B;pRtgqhW~mXZmu==Ydb?=hW|x7!+$B+084y36F>tNWk02s)ygO)o3L^k zt6`0dS8rk`o=Y^R&d<>5M%>`-W}LYD@Yhib$+@XTO5}z~hjB;Zm`IbbRC1HeqFN#> z|MeLCO+Wh({wOB%e~wyCT1jEBLxW8gTu=*$)I#}%S{@^(8E-2sul%PkwISH(=X=@} zK3_+V_@C?OZ<$*e+nrg)z}({>P4z`2=~qRPNEEa5-c8 zoN2@MJS-zd56f~GEi544UV79`F;Q7>J6Q+PN;?SB$e#!1N}3d$Uvy;f1S&0iZ+Rg#HM_?syWqm zcQy+}#9dSE!W990#D0~W@K@~@Yars!PVB#fqBF_1kYRhA~ zuYd#WGcN$A2TnNv>C8_H0^k4(GyT;$4%lRU-#PBQ+!T~jjP3`{9=y=C-A+FYBO2!R zaQN;-21MZco!5vVv)aFon@G>kO*^Ca=%TtlLfj$TaBJimd|~rq5)uuvAX%!7GvaX^ z^N+;z?}_%DG_-kgwSsRz{eqi7w+!URg*T0Zdyti@@?fExEdv=F!Rojm<1o{03gfK;Rz~b`LMT zrsL;?1dx8uO~8hna1+@d%uPZ3BC68c&ma})AKzUff|#4Xl|?jNxO&uAGBAtfbX)ps z>m*=?-?vTzh5~11<-GE)xU=Q|TKsy-#*mkC)0h{O1!k?sSb4A$ZEym5=vaKmuakGO zbIW@}kXupuY%PW}hWyN4bdwJ<}TxJqS-k`#Tn)8p(4UYicM(H#(gRkv`fEj+@ zP6z~f2f_5bBrrbT4Q=@rA!OP&IrLQ`cCxl3E3HW@!j0*@b|X3Q3e9U;_;?~dI}ggI8WZ<`s0%G`V#V^*rXMFR;S|DFVZ z4LKnJia(eHKB>**W)FXwZq1dlO{2iew?(_(>&jyxo)h8{P!1sH{n~j5nBhrk%Qu~e zK#=!;#uOMdoEG_!Zz!S&C89zXL@wgXY1zZ?jm18L)kevwEUOh_AcR;vy(IFPZ0!=; zpgm?^j!u8I6q=~-@J+Lkc^YYUI(8MRta3*Cc;ycgeiJpc4=B@KfDIAic3Rk&ILyfF zIA+F=@yB$RG7h&B$%$y?^&& ziHdQe&I~CCBeiNmQ$8=NFx}d3Zsp)^sj9KyrPdBa!%AUqxDQ+7>ow0A z@h~gZ9o(8q%(db1q13LFqj3`J8NMj^PUx|lZNg1aFBszjw8IS=uDF? zV2yoZ_R#Bt{_9Wz#&b9P2(E4I!iGF_ZS*hXF)uSCFLETI7uXGQ?yFg+sB`u`@x5YBgjXpm zFPE!Z!kZ`Hy7KKN=8wH*xdj5h(*kF5AbBXM0>C4OUhf2awh6ZFlpuIea30t|-fKxM~RPbj0%h z0NW?S;}h$oql~a|Mv_mqM}3HwT3sx4l3K7LYFbEYno#8f-@?xPS;4nC`*#K$lDu?D zBo?SY)V;imktm|2j#ojMSL<7jAS)N(@WRL8pmUq>ouW&eXWol&cs}#&5Wn}=6{yf5 z<(R+2txrmPFx_Q(xcJt&!!%q<}DO zH4`+DMWz%`3avmdBXB#Sn`4z)f{vZbYe#P9I#^27$WzdJKhYeZN|V^@p)v^n(oOAI zwI2puA&&KtnqEvwSln%FPfQi=$*gVXZU|8?yK4URqnFp*YpV4ZGv?rgP$FS)_VKNf z?h-#!634+plh+Q+uSvwHbFi0^qgNZm)*ShKNhvT5vH&~51}Ku+dA3FJ9zPOeS5{g>3?`S zgkN5+2NF-)jQV7Z7t!dU-DU6dwzw5HUmDr6>#p#O{-l%TVOC~ozh1o`%T9+#{o+znowi3g*4PfX79-{d5(Gr*;=R!lRHUvx*9oo?)PAj? zeSC)}s)BTNnd6c_?Z`!%Ri9_mpN^C*_rP{y`F@>SPjPOyUi)fQ4>xD2Vb!c*6bL?BUM@+fm?>IeKbq>_H}CP}e8`#G^g)g7 zU(4q=yMYxG(oWG_&CZaV^{?jx*5*gkfERAsYWioGZ~iyy7?KT<))py%K3owxkaJpUtIf`RQVznxo8 zaautO>{a>Z$w4TK1;G@w3R;TOfCR8*^pgPzDD4Kw&c={eg4@dwTauSfB{M`Z)AS8DdUwTQP=t7fkrcs~t^yr3lV^D5wn*3$_ zNdb{7t=E@JzBWJvW_a=&c%Ge=eF2VRnFEk^1825wKpvLM6@6xv+RYTF{ zI%Z<~-LW>O?-Q^s>6`Zngxi`xFa_;mCyLWrmS-&zc~}SQ`CX8B)TGlT9+qp9!#k{Z zIF{di^aB%2NB?z9M|r_?|36DQY>4n`jp?eQl=mjgX|!y+uRH(`i=LCiS3?c%KZ-!i z0h0y;*V9h?;7g4P2KJXA8=jPjT>o4qa_K*1BKn4g)}VpdUy^J9_POHZzxyZv!xyIW z*PD%S#EE=#^M8}+rkz{C4g=LJPj6QE)0s{W!D9H5>APT|FtYA91+A7-r8^EqnnQEX z6^}%iD>xU=&vd4pVa}v-D>oqo|9zV=E4X6PlP>*;$YLi*2;z(4b; z!1`&N`C7orb5BtO*qij*p$OF}itypl4?YZEG-BK}YEsLz*INbmtAZ9*zWLVr?kCqk zxsSgNMW`-NzzOBa>X&pW2*EtURx^)Rk3LK4(rEyfb*lsd$GikMs&-BwjT7f`}gLn2K|!^Frcx8 zxY614r{dUwseM&99)Jhv~m*mtrkv-qR5$EG#97e`L zDjD>oP1D5Y3->+!b<{$AK`sAcYGL|9E#C2a-hnX`6z9%$1s3W5k(&L!hhiW2%TeYK zT1L?SigEqv)MDF~urQvQ93$)pQGo!f5+l!L6erx<(pgBB$8zsQasK(Kg`BZ$AS+N3 z+z|Wtp%TYyfF)Oy++;A*c55^)U(L2D@{i{EZ#@({4WIz~N6C_#DStxZEln9@FdadgZTNb6n1F2|#JBQM5z zuv4j~WycNeJ5Ck7)(K0j!dpp4k4MF*51Hb>^9+LZ;PR+EGXQP*JpxIbiEuL68vj_Z z=t9TVrIL@g_n7*dwMSxJESkRh*z`Fsw`^JBZ38#aPPGy*-st=GmFff--26xwMAurX z8O6}NJE_7_OTuZwviFbga|T&!!ljz-CGw<*q?@}yZ=lFNh9mE+BGEX)^-|W(yrMuh z$q|pQSw8Y<0Xp(s1X`KZ8U^IgttDY3{&%j=xhfpyjz%j!ZZ*s6v>#mMoPunEm?h}B z@i1njfA0m2DK{77Zkk)fYk3yys%!jihHE^_b?>8gs96*mtS>Q?(~exDcI{F$AcZYO zfAVnOPx<9#zP#&A#nie-;HqxTZZ}*_C}VTedg;UsU^Zu+fn1^AOj zhBb8i*u3?I@-Dcd^)4ZoZ95dg+;Wgmtqsw;g|ayGI^68_f&!@ZgZJ0&1&_?fbMf7m ztZHGDFo#SkI&5uhAL7{zi(+2?gAGlvSJVBZ($?{GJW|`+t&XS#TsWS2 z6+WS^oDZlVi`RUmUu@IUw79Gis;<)FJeT1c-IRV)LY@&~g1-k)QENcqm(D1WyLw$s zFYjt`9&)rLAtNg$wSq@RgXT(=I4P@9TgrCXB~nxfO>O=fISH;EQG&gMeWH=Fd*H(n zF=T>6BuqL=#w=BbuZ-7g!hF;%m1|_L%vav2V$rnIwOSTRQ@JHb-B!8zKI3y<;hsWq=XWw`vx79&#Q zrR1bo68TOA;N8Y6{YQwnNZ+Ooj<-*9W*a6v)ZxiQZ|xT`k8+lz&mHlp^3?$oJQg!! z5&?syi3iZ0**Af){Tb6gE-x3i`6Ms5h=ZZ-^|Oh(_`O(EEmv5q<_JYcM6Qe{LpRAX zM@e60lrYo^PZ3Yvqa0eOHI#44!Lh@3H{!=slCY+E>xYOOjr3IhX7+6_wG=y5F{6t2 zyPRxz*wb@#?3mR3avN4JhQFo`28|NF!O^>e^0WPD0l>AbOBo!l4Ih?c#~rape2V?HS~&Iju7oW^*G9Zb z@YwDZn?9n@cM!tJe&lJtLPR3*@_OI-y(EOC``mQc$WhrUX;g`F_eP{r53`9}JqC1l zRTas?E+hVEp8r-a=@bWnEx2!R5QMBc5KKW!z@t6&?|@ClPx^Q89qWCbZOPZ3x|2pF zz2bXlE6o|p(<_)Y&&=Y=^!5P?B!KpN{|?xY6aS9(5BBd`IR-HWn+Ne2^mT_C_$ofy zGhe2~dYPpgFV~Z6HGI|b419$7(^ecS2#@i$*Pt{HeW1L9xGaEMYJEjZRZh8^Gt=a& ze+TR{|78CTSe@aS-wT-227>85@c$GZ0h@yVqW@BBN_XmH)9$9(bmZgSJ~BqaYC9i6 z?d;^84E&rY4J_NIdkV14?VCLXgv>$^OhHRRq&B8NtUPqKf>88ZPMYXB%q6ab2|^k$Z2%a!xv&eM~sj7<6?TKBp49 zCu=|U&Jn+;#01L$VX`R#Dg|EYA4rG57Th;GIS9xHf+=WpNOwwyz$W7-=}Em-^R8+swo*H!?@75+!ky^5Wt0E;h4JX%t(0xybz=oXAA>IE59TGyOXvYjTE?!P> zg14J4A8>}gp21>toE=;JT5u)eK;dhXDPV^GMUyFDc>rgK3s^tvQ)p0-7}ZVJLl@fc zakjNm8X?_kq$oxkhK<`zuAWNGf@k@4&_H(q4gX?jVEY0Mcd&?U4BBFwAkGU7B8ziJ zmsjtN)$j*@j9GCp!%!3`L#ciRzx>cFt?J>v(gif=vwm(@W%OUymOw~@VIstE>}XHQ z60n^U8X%0}qc0bQ3t4YLxOwlj%yePkbXthkBsy0{33$Pc3jKl50BphicF;h73Jt&} z<0qk^DHnsRoN;+y1sV|GT-BsDG(3kqCswqlH?pyV*6UUT384QT8h{Nsfd=~j4QTij z?Ei$)0pSR1t!cZq!uvQyeY$t7cz+A0_kkqMVsYR9>qiB|@W1Fs1p+jH5YYj~7xi%e ziRhRceZ0g1wSX09v0Sz%pfG8hA;s-#TItn$zgN;`jbr+>A5|9WC64xWc06rsF6caR z$aimqV+EY}MTh9QkjqLLbmCaX--3se6Q@&mJbWZ*@J1&XbR`Ji7%d zFR`ga6CPDgm?{4Cu#24~Bg*SHIZqp@hh{YM%Ev7}pL<79Ux89b&XQ)Sc0{>P!7HX8 zu4WJ8Z^2I%y|oX>2)q?ivN%<6UzB5dupG)MAT>%a4YGWef!GQqsFX=XjF9MobOf~} z(vta2xEJzyto`O1_c$V+>xmd17a{4;2_AH*ciDP46{&Bu8@7q1&%E?r)k)ILn{9YA ztl9B?sEiL-G~k*)u64fVQzI7qASV4B z8SOp~ITYLm5+KV1xsvUrgtpvbp2n;2UZvg}Y_$nO6>D99I%yEoyt~M0<$fz!6rw&%P^M$%-uTLR zV-11#IU#QNNlHJ2E2-b9I7+>uhW}M=Ms?A+&(%BOlt_%+oGW?9ODb!@dOCrQ;s`rvnv#Dq)o?Wy;IDoQ?~C>Eq=nBp+a$OHV$n&B*e9fJmD zr!h)~r#4WiOOCyJ(#bW(c9oB+gyjXu z2JNqJU_XdmakC|vAjsbT3*ijFOiw=N|KELie|~W_#}^mnwj!Gax#ykL((}3~-E$Tp z!)#R2)A#YEj*dGV!Bk@;gR-?m=EAPE!;NEb{b;H$;+j7j*F^e6?{es;1wC+17llJX zis#vlH}Zhqa%cfKwVUha0hU*fE6EKCj|!fNOeFq+E()-{#E`IM6&8lFX@-2K=3pCfaHo3Io$6_HAq1k` z*?jD9PI!8mmX;+i4;}(aySUNf9&K+tiX)r5DWSv+DjNb{&+CDG=AV3C4}#0~H=5r6 zt2hAI6#N%mi~bq66@=daSSjCW1l@?4xy%SPQNWw_rZMN;{u@mRwg|^5=7-@;RQ|W) zdw$_H4Gb4}`Y(>4bAG{7`xU+g(dX=kGyj`-`VlN0d!Dt3JK`_=``t>{K*iIL8~7i+ zXxB2A$E3VqxdYb_Anun=cjPl(H!)O&?PW*SKV8fWDO+3MhYjmx@?A zst{-0t(TNFvCjQRZph!7lzECuz`oK?qEdoI!5VEaOYG)=4GRj)wR@dQyU6`k z&K(%Gf%%XC#_v%H*pL%cV*KAgrBFLZ+t$KM^t9}<^o%mlRyRb3J8RwsTcI`?4n2f{ z^Zwc<2$NedEx8kO2sSDQL-pjHlGXzd#d3#_8bJCqxY2K^J6& z#mtI01qL4h)}24l9*i9K>w;>G7u51ErWUR*)M6VfdQ+lbZan_H)S|SjO+mjy%LASB zIJKA&KoJngF&Vptw7YeSrk@)&xdgWyonn!9(HjCMDBal@Wi#aB=T61?+6omMBW^Em^$YrRqlaGtRxe7 z!9ss03t^HV?)3$)U3wbROmw!p?k;Wg$ zLckW>Z^uF`rz`|)GJcYUQsKP1oH^LN9i)}bwm9f9qS!1m#*%u4_jNKmu4! zS!npL)DK`oPFRTLBzkeSk@KI_7ZO;`&>3XuoOTRWwSx3B((V{+E2ucc(tkjQRffNN zpQ{dimv>v>jHwN+9gOj%eSDb-^v1$V`DnLt*<@_g++BZD z;{u!hPHe)y5ABd+olEWUZw?0!?gt;}aCyic@y9(O?k zSii?6U_(x@iS+`TzQd-3=*GTyrLmAh4?m=|Zpr30C-vb_dD@B{QPC||4M-QyQ^mF; zo`9$?a%=EIb#E%Wo3z?01lXs)F<}~*MpK78b-C-;DK_hcV*iU3oA--in?YOH6QzfW z&z@JY594ar=Wt*YQF~Z@l)YU7c%>dZOtvX+M4;}g!Y&`$y-@7GQ?UV4`)d@@$5s93 zRP4az0|l-%!-tRLV;)Y=2WAlg;5^2gOtTevUqQZ_o*4N96&u)s`|T8)?NqUWO~y|u zwmTv_UY}41@msCcIh@U0e!sw#sd;wZ^4FdF4e>@#aUlV0-zzq-At#E>cA?ncDRzOK z0`cZ5^#^6N*zrAMYx*f;6j%Sd%n*D%We*}9qXfPx_U@{4))%lhsFW7VJMzv4JhP-%hdFPZb;3Wc;LJZ;(S4huB`ZlE`DX$bIjF zpMAyV5`5h(WltVq?mlm_G$erid&LGePJB6b%-OtK=~Yg$&Qg9`yK91#II9q z_6x=S7b`aZ7sWOd@^I@Q*b8MpuVRZy5(X8c6kWORMYPL;P|uwv`^uvBQCY%TlXUvR zQZMy|V*j0rO_(`9=Rgu=A8<~^Hu<=bL7nnu9^YoB@$H+5op)3)S%o_^Vq5}qXwl}y zxk$qL2T(5~*;Wp(~-f0`<0HjoeedMql&0J?{>%WsRx^gh z4=Yj`G_T~O<4fE7Dh&EnBt2fX?HA#Z+`N+eK0SkaStII2q>1Pun!=}hVBBy=A%gVF zA~MhD78dgRP*hrVa2@Qgp=>kpzYaRe?r27yN?^Ma*=y^5f0=gi61X4IXzf_k$7!Q} zsMRJ8P7^}Iz~(AM`DiX`O-#OS>y44v8gqbG0PoU)a~zTuw)X&>KXLE_)l|tT8-xOG zio>_5hRx-9FU`m&^4_l-N!Lc_Zp2|xxTnrQV2`MV8ct_6W$@|a&4vs(1Xt5%yAPN3 zXSwszWd}OF6=)&2k9Lq(-nZdI)QaBx0?`kq6eGU96-9JT{>IUWz6LuCI;W^dn^=iy zy6=^8)A_)$L%*dC$j(@^ns_<~#i-JjUW!+Zcmp>cOIT6fbz-rKY&U8yp6UzLe%b+> z(wMD0pfWEpTF@pdCQ6mR7aQ;fHepP-!xVGk<1U|lN}g5M>r!)6Df8obqKYo2_(Uc{ zj#pj9GHQAOD!D6prOIBfF0-u78NlRNl(fx@8%%C67W(9m;bO|Xi_*RAY@B6`h)Aqy z?D}p9BL5D*om%1b;u%PHBa=<}l-;e%podnbHPxJLS}zj(L&E%b;isew5cEZ-`)9 zkQ1-W7|?QG7XgEpJpgc?$qE4mt7lCAxGs$N6DM65Gbqt{n9D~P=*zSU1*-8}xYqg+ zlRiehj@A(9f~Mc%7NQjuC42c0+gU_TED2UH3{FiU<*{KaE^05kZwWjutWai7>CuqF z^ILk@ay#VD`?9HIi#o&jJ0kU}>RhaQ9KlXE5iR$C`E5!4&X616T#s`z@5_%$kE+?( z& zTZ5y>6na zSPB;?y!yO}l=7mve!H#_BX>JkiXGyo@d@zgeT@-Hv zNt8(^9d6Ry0xQF=Cy8bvtwaeP$8ay71X}+6V-2U|{p|xWgbB8U)`oav`_-79KCT{r zC745$60Jw}rtMh`_QoSdw67B`;|p@W)#{shii$DK_E0|*4$7-ZV$KfPwC`TqKC3rP zT%)#$u+Qw(2TlSbtSlo}1D-ddTA-{w(LGLoFtQk9;8w$*o~wx9oi^>6Ia~LHGvm5$ z;`=fHyJdf{2~Ap0#8cEpF_&wR%DG9Ns^}v5v%SSbynvXJV?8Ij4>2G7S!CZ0T}9Nd z9v-}bDdS(2<3=dN%t)yEVzZOo%?kfY6;6iT%d1cIj~~c!zZxBMgK3F^k1y|eIclPu zbdUM!vlYn_>x4x*(&b>?>-Gd`w}k@&RZD6YF_Or&QtsE3m=_Yxv^Fru$@wcS-S(h4 z=9VJLJFuJ@zwYNBQZ!)k=E;&zBF{4z<*I=?kt<0N`UoeDTZTU!g&2^dK%#Uea& zpB@U$Rco^G6qyliR$NTT&#hcnmGb2MpN=I%ag6AcGK!mfrje{mX)_&7H4tjK&e-X5NfU*8=QGwvwW9V#xlLEThf8+*ToJv z&K*18y=TEP_>`dk5Iw)G*{uE%T+-dd=v|xCp%(u>;%7-sKXNGlo>s0WH&62x)2U6K?EG;^%o}5!~ejoJ!HsmDg!TG-t^$21#?8BOlywWCahSAgP zL;u>h{r;Zj4E^CND}h@B(L!hN&3!)R(|#+nT{hNXQ@t(myxj^b^rk(VZh0({D0Xl} z+SgWQzzqN7R%XB|7|#4&`nv?qXb}37fnW++D>Kg1q)){L`SqqSilVZyl;=;lr)Ino=bmxI96}Tl_rF82M}_`% zP|0}#mH*-cDZwvLsa}y512AHlJ@g!dfbJ}jk9&ZrE&Gdw?3PU%%bJ!{NYqX@MPxw`k^$+RWo_L`SP znW}96Z-vTJzybD^eiCq+bkUJ0RPqV7-am&Qf|aG;7O}~uD1oHhe<~bW z+iI!^3E=u3aDWXt0UWLi!1)evwB_z;WIA*?xT{8Xk)cG~j4`$6vkf$hYJMbN$6AU1 z|9Xc3Gd%fa|JdGPAOH@qKJ8~%2w0o3Qx*dDD*tvY#B~~_H{cUbVC}<=sl3y8OebpJ zu1O&1D^@mPFnsCOC=W+U&`Fej6#Sl=UJ5qPwG~yZiF8Z{e?;j82=bMYFbTsqB}I;$ zWjTXklUSnOG8$g`?_YgVG4w|eEduy6`M)GU@~Om#GcIAXH!s{ZDxXat=&0Zb7Roj( zc6rA#cR4+7@xA)x#KR{j{ntN7=?ih99Y1B~y)g+UGPGRsB^hdHd_n-V#;UARtwlw; zArMK^>b@~P;^Nx@tzZ<38~s$#v(lDy)l~pZZdG}zN@H{_o&{&s-W%*TMtTl)Dhh8% zEvvc9xGTz%wELug(p7M$z8Bi!=prAlNKbZ`9>MX(ay^12MBqUYRSKPDKR%qhFO*_M zuMz?Mp$v*W^h!#3&y$>^j2ae9#1tM_-0jQSCKC35rtoV@EHZ^$aC*J(Ahz~W_7JDC zyJ$R$!5p2+ZNqB3H+QI0RbpfI7AGv4@wZbR8^DiA=h%|HO3^fUJKC;8M%;wCS&b57 ztY%bHy6Nk|O0;q%71lCNCcfVqew=R&v1Vx8xuv!^cBW2z1#5_^WJaE`uU5 zICZ&EB*w`BsvQ!Q&8Rvevi^Y*ke@K^w6$Et8|JbfH8GYV?a>Zah#-&Aa@JaO#>~K7 zdTOI|mBfz|Kq$u=S_a!j&7Ddsu}qy-uVf`62aBm`(|7YK^BuN5A{SPCGBv%JC-WVq zifMDM=ASSeN-=LcnrUlJ)Vmq8V892EsqWtDUrXk~C}VZ%8ISf$pG5tDViV(qO>~X_ zPC1O%ZG?%%wfk}eZ52#7b&*d5iscEx(|mG$ogOb(?paJ)uOLu;{2Zl+6B4{C)eya( zE>TL>eCwu)lT&Ay>2b`yL-25+lms;^F4UI`*9J2ZirnBhnikiif_f!6$qwcn83iH> zUL%#}!r?)Z8{krWd(;;Zq!@zkv;!6yOOH1Plg75;8&B9*151uChZLh*8P;)f)K{nv z;6C$m19Jn;nEr7`eFS_bM}27Act9KCUEWuKU zV>)^rWBQn5N!HL|1CiYw%N`;dKRD*t(ocKfghDB3u|kaRfD}G)bJi8_ZTRO+BwS2_ zaTfO3ZRvbx{_FN9J5j{eA7cy`<~m-Wpm0CrCqk~?SnM-tCJmJe#BzV6 zN>dm+nLS|K4XvCW##Nd;Y+DN-mov9&c4*hWPGV$DjE|_ixH~10&4;VHkwz0r6jgPj zezQXa);`|7FVIHFT0P;z*i8D;n@pD4z?5N#2HmvQNtdA|;>7XsmP$Tu#BA^( z9oq<(UGW^Q=pkQtcOa_zsU~x7?i#Uw4LuzF{jcM~icMur|pb$JjTQIr0Y+S3*A$_^8Jyq+&6PH&#^iO0w zgXqEsZJs$UY#;|w`!dOyJMCmxOj}qUMh5VNY>c$_ZY_5Xzqs09KxcRFr5~}zu4zt= zJariPBKf!=8GtoHd3_U&+|B$xvzlH`OJGm*Iw~cX$&TKW1bALwfpMb;ok*`WI-g7V zizBo}>*UZCKQ(J^@i6DBPZS-%*=;gX68vN&{P6WA5s|JcuOdRaZi$Y2!V+s49rVb8 zxspoXo=t~fW6I5XM5tVt&zpz>UEA);GfK}Hf z4*F0LIN_ALomkCP2kh3tvpq;YwHZG3xt6WRi4X1UfW`dnG2g`oa(c=Kc5=-3WdjLw zoN)~Z9&tf~i-U%moy+u4TF0|3;$UsNtWzDssipF%&L5E$F6#xMYS(ZN=|^M0gHBtj zmUU^@EfFO)_cZ=we5Y0bBe=_L5BaCeT%Hd}UNJ?= z-K>D`XZ19LV2;g*;rij3z~6Js_Yo|ue0tl<>ET~4^~EvYd5`(7eTm3ikErJ)F#;b5 z{_^`7o&YY8t11I;nxs;7Ssd`a*Oq4#mmg^x&cxqAd_eH7t|WD$GK%9zp8D?_k=dg1 z^APE&^}q9*Mr6n=dixXT1F6jadOnEnX2-_b$=L1;oT&#{WYu%$s1}M9wNUksU?tt@ zNx6N+2Fi-SkKEKYp7s0@8CE%NDGpWBem#o(Y;Z%SWtVrU3fm5F zzcnIr>Q4gOTfXrpK{&eu!4$M(KJL>e2llFb^W-3$-GN{V+A$yZX%r0DGWy9V7}~g| z!7wuNLrwMe$!qTF6I2B6(6=~xp=6uEWoo_QDf-$%iK4LON|abHBizKep5M~+)W z;$LCzUd;_?Ac3LLiBr^uQ@k8*CoLtx8bC7owNn}}!#}xG8VFIaf5!B@hljv++HbZA z5KaU^Fa_=Kko)xTaIolsX!Sk~Y}m93ob8nMyWQNC0Rx2y%S{=>;YAOd*7u_XysZzl^-zy7331DzLrfo1Gj4{sh4kG*rEO3VgsO<0padsUBviBt-0v5EWxu z+jVyFh46;RT5?ou5*})9Q*(i6NWkS&;Ct{_&wF4)PJr+736`8~d;c$OjSE6v*QSVc zC76Nm-tc7I4Ho3l@C~diW7iwVYbmnouMS?KLZ9&vyCZIgZme9r7KKm2GpOQ-FfJcU z6->OuPQ*DMQ*C)n_mvHSedYxl_P{9zAf5SXLBPn?lBz^uePoP;)pkCD z+S$oF8TdI*8d$bZ2@CGo^IinL_A7{(`{MFSD_jR5?t&?_4E099b~_bur8mQ zu$1m4-R)kh5MFZ{n@K|X_*`H+cHojMn-28`j*FtR>(zgbuud}Bk__MKd0*uGTZ2eg zqF)G0wxdVxNf1S=^#x)58wg7vHos1eb=a`5_4akl-}rH{tq^y0IYv zJl_)*upuXe#dASe--XnG(a;$T2;vt}mEL{^sYw6$?h+Bi-2AO9qUplbqrQ@XSuCg9 z(qCH(WIlkjeB*W3F3u=DzODxc%hpHfKyvJ8_<3_lkf>#R`Ds;fxSN($${H5`17XG) zON}-JA^ACjNwxsfGIe~le3Gc*cfq@``LrS)BrO#>FkX`7P$`4xajf$Yj_UoohA#sO zX3MP%VoZA!hV-iUat*AVg4v@tmm48^xZ!Tm-@eV)@;Oz&)Bdf&3e?hoCKbHPOI9Q# ziCdng&urzQda%$+HCRswtHFxOWQ+(k&PU3g`s9>hB0E+QtGT%oJDMB2zwCPekjLitHQPcib2GdV@t8!Z)Gbypv-b)hLyK|jmBY2|IBY5PG-t}F;_R&^aep0y z;}kivj-dw05ZWL^$6rjfb*Z_#jR`-X;Qm~3;~`Ia{U+9Bjb?{C>6iGg^&)OwZ|LqP zs7ukc1)G7adv9XgkAn1uHXJ@Lf6`X}buqV9^n9RJ)h9K&8CPFQRX>3Zg&>SanJ()^ zvpX*^vLretGu2lH;b1=a!pz)C6wp*C_l@2p&qo#~t)9JZ(<;oOvqoZrbl`wn=)y~{ zQlYe%%UapyzgD+qc){-$o4%W)x0xCTHbfsBic z`?Yr@l60L9zM5WSgiz@niULuF;x$>lNB8}a-Ba1&lbLPhWObpZawX&_u{xATXP?jAWmTHHi&5!IJipsj5Ia_}1a)KPR0L+9j zXAIH&=TrfPhpxw%MBdZ)bDI zc0ZD1O_sm1WS@j382JkOi%=;i4Kx^^GR%|W;cu#f`EEM&znogbZ?#i5hYzxi)pjR`9|eAf~cvKud6}iCZszrfT|8*I+AF>T9|HTg2m$#<&+)QJTtQ^rG$YCDm!V z#t)thIK#R=S5TQV`gI-x&$&H>dgc$;LmzKt(PxeyNp{^rw|y1qWk7U&X^`Rwywk0Z z=%4ctPR@JL6#InD@GoMpAbJSmUpxd!#JQWzZu(FW=k*Z!k>%|$qZTpLQOjA`UBw^j z$`s42@CD;=Nmbu;&k2J1(Ntfg3$QZl8ybS>!$C-cVN$fWNd~gtf* zUT0<(ga9@Orod{9PGj8xPb%tR1;}sN`S;HGxxWno%X#^!O^&eFv~70%p?X5&uOnIB zbCax?YXo!k9Uato{yjIh5LEkc4Qx2VCMksC{zoF!$G*rv!kfP*n_$P!so&~Sg$v@nRta?*m@>^R^&(g zK_%H_TJArPWP$B1-|!#^DFPsvf|gCdcglmnCgUf0aQb5p>-AT&f?STzmy!1607>!v#pf^ zK%Rn&{PiMZTgSl7zGJF04-k5tPCk8ESK`K%_s1+2*p9a36v%?h;oKz;TuC!3vmTyO ziyK><3Emx;0Fo4~X3?6GNbjPogV(GAb(0%6 z7dmM}K9}+!=lAoumD;bo0LOn__n)Jdy# zWznsV!>1&qOKt>_MnI5|k`R#Y?(XiA?nY@)1nExcMi7)#LP9_(2}P8U7GCrSKV&`c z8sI$oRS*1q?ODUUXJ&t{ooxGR0ji8sszpFJO07b}6p3YZVWU2hMM%X@rNhUVe3h@+nHKUuNKysMb7B_qKw+=d?nt6OGc1uuaZ`MF3}-yuK}oRhdj`wZ30KQI2?r)qmjU z0cTspSe-s9S%3dbm$UWA$6b#s^bI&%(@?mRwczK7&w%qc0LP)AW(K=Z)PO`JV?wYx zrZF}cZ5_^|x+ki9E>EpBaq15Q4$!ydw*wCQK5&34<8Vt>+t?wu13Sv~VPqDbxDk4ahauY6JaHD#)A%w>UFaTR?u?vaIo z1hF3l4p5Oj;IN+o=k&mlZ85t-Lj8EwpLJ2Jxjl_B-OP#`g%-Yi)ZK5IaH(PXV4q*0 zz+bb^@6Q3wPq2`cKD~He!s_k*S9(jPU6Dq-!Hjm-a!V=e*I9`D*cN)6 zv5tZ6Z&K^@>U_YH66a~noLFr*i<(a_U2B9!mmV(sJPYmQYj)IZmPBlwb@rUFvq$)w zg>HHy7AVOt3>KeR=x?x4#KP*{!LO0FcMHd%eItxgtQRM=-Vx&6E?N+TSXBG);t#YC z(6{Bcvk=F=g@7vKlooO{5s6qok7O-XC|C`J!oXbsmsr?#u`R-q9MPQJ|6(a5h~ub* zfQsx{h~vycr?*fuF?EEA0%2<<44SF#GhRyTGAhHG3lSPd$@uTNsnk#ixqVV*`wo7>=rinAqn+5vYRtCvg~iHTeQfz zb=%d(;NFr_NSzSTADi<^1=v zZ)XW^gVF2M>H55(U%{GGA|G`y3t8z@YZvP^EDx{FYOz|VKud0iLz=cjr@v!F#Y|Ll zi7xYXPQ$tRbYuQ6bczm6F-a|J9HUJ|DoAdLZG7D2Y70XTA3uC3s3TLNx~i{WZQ<}_ zTK`>aQ5mJlO-9RUG}mDhVIzpl?Sep4zTp0>Pw>pjsY;8`#XSU$lMGfLkX$tJdD%5X zgkOQz-UTP0E}CSsT_aaT;3QDc@Vdk@_gYa-^Q8dJN;mEm=Vg>)U#^;qqU4DZdfMl4 zi!BsGhCVi}U%v}p2Zzxb$_5=Oz0k$JID1*|zH9DeW%yQtY%fRE+j7$hXl}b9GkM_} z4mvc*(3`k=NzOiyNY}nDbopczH(BFi(Yk@|(Se5TZY(Twi5dMF=w^sk9IHL_78}&p zWiGi7K=~2n74Ejz)_4nEIbWwob5Z7Qd_mp-m3R)lXImc|^y4xYQC#xQQEbJft|GUE z58ST#Yl9L-BV~jruW%VZyc!!_=K+H!u>;*alo|u&3x`7gxMePvQuda)_=wCdJsFBD zT(qHUZU~-)rXX(y3#d{J>+iP$Vp$|!}{^*!&(xCrGhacd(THoE~;<2MHOWbCLb(wnZ;Oj=gplOm!Xva zEjhjM5b!8Ksln3}T)19UWeHX<_3JV(j$>zDD~qJn)FSmf!Svk_P_U^2uZz!p9KKYY z3jv2PUo~mu_R}&i3~&?$7F{c2__VXk>x7w?=(o)4Mgx97oPo!j`f)R_Pv~*l6xHsd z?w3{35^|zvXI7L{g35a*B8b%xiR!jq{Zmz+Eps{TG8eLIjb-#XL>?aBA8(*tF`XOS zpN}f~$6G8CZ}J5_;~-MEaDV#f!qgypmSmaJqRAhac>#S}e!I-;=KeAlpjYLGJ3qnN zl@o-XY?;f={hSf#F*;?=n27sPOTRdVi0TT&OHZ`V(;q6{y+X7X)ov(pMe)3LNd^*h z^JvZpRAet_ym^*0o<3)cXi9D0!R9XVufi;U`v}_Jf~aCWa+boYiJd>Y4YlOl!7>-1 zz`aUvKfG1d36iU`(Bm!~K@QSU-5S61tVWO|jbl=xZ)4TgT;yi5gavaXWR>29cfZbt zH_vSN7oTA#_RWUOY81wV?Vb#p$F*S-%r@#=mB2zNYLfq(ja@1QjN4FT7qyCDv3$O9J+B-J z5$>vPP41UCP0lMbxi~`N51Z#^kCAS=7%De zKS_=wFO9TBPvBZ@x`1idV~P9Z!1ioP8{;Y7)FG;PZE-~wl~!%0_%l-k-@a$&8rE@mOxbJJ_TTprFx-UcVcHpnG4#H-eD^w{$hOjap%%6UU^BvNw+h zf~+cJc)48k#`q?yR6Z2xI9bqZ(HNx{UU4pd{jyh{BwHs%G!2wzOw4&sW3gvK)gjT= z3E}71#o-&hn-I7n7(;?cgyvYCk^P8_-W#3C^}06893|N9j3>0=ITtF!^dONimRmQ} zue^EU(yVYxl8=cz^lhW`t98%B6+3YkL*hn9rD~*i5uQUrney=C@oGNfVim+|;EMvn z7Yqux(YD>X**>LbJ+g;ZH+lXFYYeF~&%_wn^x-om^;$~f-CV(U&RB?AujPDaj16eV zdOYDx6_Pu7$ezYWOvc%~xTm5?-YawG;hbhFnWY7T!sUw!B=yWD>un_2B%D|r7X6jN zuFOGi;3THt6*RmT0~)smAO=*w;CcjMwKu%c`+^aVGl+f#56Mlzm#sD+Go2vY`PSAP ztT5$#GtKllPJ3a z^y%_%49#ZLcJ~G+yc$7=4}%9vn+~;Cf8OB4m#>2pD8y@yfhhU5)TARbgGF^+X}9LE zr_l7sskV3)%SSRl*L}`J!WvM*=)UtlHRK&UifxtlQ-Q9r_#PX~_d23Rb>YbYIkPqK zN_XCKlgYrDjbF=v;`3dMRv9?gp5c+Je=s-!dpC4suH*h7)YZk9s)9PDIKD>xsoIr|&t6YE^kS3J$Pg%*o zvU33jq?K5TF(C?M;dh~*O_4wD6nTkn2`K%2_82S^W?tdrCZMa3`5Kd2csI!NNVnD+YiA{IBfz-xm*x*dF>h1{t6JF!HKkV#$7(z2}$B0 zb`cdW=q)ItT0u!qBACdt}}nLjxsIgHYF-|hz8eAiyT|w58KA(u$IJb2*USmVQjR<5 zV981$o8(lNuD>HYqnX=hF*$nZM3_TxaI{iw7MfznPDgnV-5h zM&L5fam|M%8-LgKwmWH*clAfRX){#0%sJYcdt)XlSIa<-7bp16%=d2&I#A1|Eo*mF zNQWJB&_O!_Q3)T5r2?!jkToOciw{f9U1ss~l3m5xq{cF8%-8=w^8r0@znz@8_vHjs z8K;yJGweL)YL~)Dr+l-64s#`&d``gw-?@+7zE4{VX=QZW$dnei@FK!?H~bCJXDyD8 z)|m|c&p+wBULg0o>~+X)u^1c@4w?x zzmvA_$*H!KQn4T3d_WKEpfP{JG5Xnvly=E?WnQ!fv3s=?@(*m^fxaz2wC^YAs5wFC z$@+9|?YHkhm2t}UT@LPgtabz0V6rkzx}=M8)PRIyH6bJu!!_@b9e>jC8<3z|N85Lx zB75!ot<$&f)*Ch^OrUXGv<5NRWjCQ9+(4u!gN&T zv)^I@RmLe>Oku`LnPWvuFJU>Wp?Jn`zBA`B+pWc%sHU@dORWCLV;T~~bF{?-DzevN z@|?cKd>SQ~g|=MECro4CDkQOJ_!e2TSWdJnOzjC@f}?u?&wh&u6nCV>1ll$5VDeF* z3a=m(`AQP_{kDKWv>tIGmF0l&K?K{En?w^YC5c|{9{?CS*c|a`CQ!UTPir)Bh0C_O zD!4@|&uuzIw%;O!mmH6o9vo*YRo#t)1zoIXS33zM2pqmhg8pL;qzcL14DVz{TW`z2 z=dmtXtz-ydY*3hLo4b}G5!h-@lpRGz8&;RUPCiQ8QeBK)S|wvLuzt%-^9eqKxSqf= z4*JXF`g$iyy3HihxuPMH#!{n8*f-7eyqb|q-Rm0`&0ypNLIWq>v%p&1HGnEK?ig5l zPo*O8yn2299^?eJ?kBYfe2eWq->sk*u%lS30tg8b4vHX;b+XIw4Vl+h7RunL%}H-j zKqL!O>OyeJwvM33EWxx|$ih59&n%YDG3kzbF4jl&jEHr*KL~QVBQqOesp1LxbK7`P zTp3|jp?CeZR19x$__6Za-ulAksZ41iepa)cti>Ti=Tzr}UPKV;x$~K2t~J5ZAI9}@ zAWi8*F)opsR}r9)eupBvwmcp+V`dLAayd7>t~to{_%8itLf5)3WhlIr6g8cmnz}VY z`GL!TCry!O#ro;F7BZ7rS9`<)w*+I;8YTh#JbR93aMeQH+B#sg={T}u8KW-Z>~R?q zya8x1;$T;zJ0$E;E(tH+?!+f+)Asf;TP7PN;xsHBtbJeD)_HknZO<@u~q z(I(xXf<_9)`7b{6bQQ0u9zK6()l^Nk`3bIbw>7+$yp#P)t?Uq$tp}FCIT60>$xcQ> z7ONO+)N|F*?5ev2-fu1bbI6_d1CkJpj<&C3@S)1BSrH3F1A}4FiP5L!p1%eo z+7Dy|cIhIvi7iu!XXEIJB0m*rNR(Z~Y^v%!A7Vu?CS(`1fKsuXzhRBazngu(r?@VQ zyJmUU*7i9y)S@H}I_pZmZ&+v2)oU;64<;WWK)6e6Y z?O#NBO5JnD?h~?G_8Ys&3QePOL7U(?PZFQyw zSK<$-Sd=g>m4%#O>HjRK+rMO(SkRaZp>;@{b$8O#u>@GBE$9u&ryslTq z+kVSSb%<3O?Rwr^#}?5S#4$(7@WaQ9O@QUSHgc6R_)_Lu=bBs?XO-#{yOb)M!NM(a z^-eGo%E1&KplhB@;W;ve2WVc-p?3=?bU*h2s;ncqkB`J%dDbKkPNCb=h+#0D0@FxP zHhA>`<2~<$+1`M4k{#?k3KaN1vh!#FL|}jP@_kIxkaH8!?LOwT4&4K71GyI2{m~4T zG)!0bziL3A*B`!W3ZMx%!KCz$5oXXy@UC@n8X)+!?elN6@Z(Bus`$GmA9@T<5DL45 zf{ft1a`9%!b9i1a1+_*(qbBx8$j`JpK?}T}5c;^l!1%$yIl=b+K7)aH_q&BZu4)mu z2RmUvUg6!WS}CqQZ!Re&Lz**(FoDEv8kkl2>wL<4Y@cSj3RMeXuju6RI2Og_VTekO zO)@55%bm|=UgMjZl867N#_Zo2NJZGZa^nT}hI!4IPfzGmxovLUEPM4Lw%__-i6zP8=7-74A+8+%Ccjc->6$2l^D~+w#Mg`~;(;CkQ>+ zKq}w9Pk}1qls+BEl5M(Ir|BP1pVn4s*4>JlN0=BpK!#Eim?4zzauFF4#CO!EKt=X^ z%6EF7uD*MwB0tgC7e|U5Y0Us~Mb*s>T;WYGU?PYYx93q6-KP;y+z}dCKE6sw6Eht} zFx$S35v}^nFlZ=Z9^V@cOBBwBWy(SHU?WnX!2gkrNP$i{KBO3+`Ct1Mak4F%M0=t9 zT*cZ?P#c~0tq4p&CDo;Z%nfgRp}DHXvR`KrzGGWNS)zzZO|gD?^4)+{LOCNV8LABS z275ruRD8-9^CH7f9HK^6!He(m>kYnnW|0$GME;va26K$USyi!fYrfyrHa zOC%F0-^oo@#I}1~lpLj#aqew?h*>jU`zJ^c|51wo71^^0|LHB_HfezYT51)s#1DS2 z$YMOgCXjmWDRElPyoEqQKeTx9a9kIv`@la$(6t)-pr5F9dD&Dew^-nVbG^zTw zsE9ax(|QLEy5N8Uf6d+}K$Er)QQ!niy-pB%0*jpBs>fx^49P@F5=-o}TLtXfa$uVT zJz^UYkJxRngPV8yoHa)5ejQu*kIfco;YaSkYuvPIz=Tkrz|O^Q14?8wj9@;9)Bi?Pq1S3Uv1p?RD7 zp=~ZRwUt8Y8dfRTpcNG9=BrYcKaedz-U3aJva1i4NUB*LS%&m2^?h$P?0^h2%MfRC6|=OdC+&uWfUu6?`$pUtoP>$ zpahDpE_e9Q)UbZRK3K^O6nNwox-Yi0ES7P>K0=r?fFD@pMG#z$G#d_KbfDOZGb6vP zatH#w5q6==A31JC;}Ek9$=RMC6Te3qa(SK}k6|{p#$=b`fH*+cJR=T5%m6{np`Z2y z!~qK3@0rmID#Me{B1?0a(jLH`m%Q(7o9!At%5vfH;9?kML7C^TBaXnaiQ@$)tIN~Z z8UuaTj)QMj;5=Pe7^qDJLOSTYfv@ z2<{UHs4`AToK_lwan>^5?1}c(r5!cbdt=j=AG&@llW@B;iA1D5vIYqfJW3p(B74LU zJmL$W8Apd2!BWpoB$7oY$kojlqs?u0LOm=j%OMDLYG(K$ZSqH-3(1Bp~@JlWt^90?p zKS>jd+5@Vl+df;xf#IP1CvXU1j|t)bz}6M$+w$ABu0s2*D^O*evUSB1i>Zz!4^u|U z+6irTdUw6+nZ`=;OClK2`zc|mTU7TTK|)7cSD+$$t*g+HrtJi+YyWL1!4Fr|X}KQa zrH7SpHdJIUt;I+sF6Y0^WA)bNAUkMXfdY@*!U zuUl*|Gk0u0R^>attPIc1#(s||@M#Wqn94Ae-yKFHWl-bZl5w#DdWT@mmB6pd_@e0K zp5M#pGKaPHh;!bQZb596&wpIxZ;NgkjfudATtx1;(x1Uk%0E)G?G!guT2{7q5@V!a zLK>R6c5rNw?GhESwvyFG?3H&BY1H;E)7X6jRA?Q97b<6$5fN_Jk5`@B5i6i%^@cO_ zZAx6&++k5+dRfly2)byh_QX?#FxRf9rzt#dvMY=-w3?0obL(=_5L{%`xaqtTuXcvV zoLB-Fk!-3eQT6=u?Qw@(pA@>@qF69^ z{JwHoJx)I}C^*}Vn~d{X7`9xmODdQg<#di)uB|(l(>qNk#dLOJ>Y-v^ZjB_D5qzp% z0Z*Z&hqhQN1zqg2J9nds$vwFPq+Tg#WQcNW5h%g_)H5GJ;fnyeoc ztf2GZN4sKs;lxGoO4KdPD1KeWhopUZRTt{{t%hZ0T-HYgNilHEW9E32uB9ywTiYf# zQNAtWyOmm}#&Lm}r+R%M!H(5;D7-?kI@#!&iO5KHF6rtGiM@@+f7n6XyR)L!4Ki4i z0)kE+na)mp8EmIJA1<<1Wc#Ap9MViZ4;Z4@Cy?+Ve*&@cQ0O1Gi0^X!-XgyI*n-!i z7eKR}Wl-=LqV9atNjvDH*boCqW#+0i_bicNk!y9fEUxw#5us!HqmzN3&x*!I#U!Zb zkh{Ac|r=$+h#|8SQyIC7V+l~U) zn+J_MoFQ+g&OgJHxtJ^_0jqF>vlE*1pQ`%z-9h|!@BX4Ge<1Z*g#l;k`{NC?gLuuV zXLSGFoB16Axdp|p!m+JIp?BU(nHODHS4+LFH;eiQ0v+hv^4kGjcz>Y|(5v#pou6P^ z@DqfdYzJ}S{nP^JF*;>xp+2q-_9SJbr-PF%G*9B?^>j1qvRa%3u)2juYB$wULf%xwEzk{ zatn9vkGKS5fdw;Z&yHKrfK}Ao@aTTpZcO{&!@GQsuIjQw8p315owbU_g7k~4l};5r zAIuOHrzC7SIRK%qI+8>sVsS7w33Sb0Gd2k{h z3bxJr2dWe3f&1<3CbDlgpvpL<-HJ$srd%WX-E{o8g*HkdyFIn-Chj~(b3|lty;-f5 zQwj+ZIchhcB71fdIRd8>*bN=oGl5Jc!-Q)+N7dzd_=cKfFe!>jlabWN*7IT48DAaP z4Jh!)EdV*|P?O!;9!7bw&=$v1q|S9Rpn5Ba9v0qpy(agrkA$-D`Sl8SPl*2JaJ~GWvjJ#2`k@~IXhQz}w60f9)XCK_Q^D#XWMIj$)&iSi!3??|%;`9ASU;%I zaH0GU}!^kW=$<2Ud;>y{SFuXhsLwVLt z&X*t14mBhk>*YMg7vo+rYy>pR@j9yC>g=NJ+z9 zbWIRk^Q9c}!;}j8%+h_6)q3M`p^r;9SO(L<9!fxgzh)1m6S(CB!Z^WIkBgC@I5rk+ z?qb>mNu&#RBJb;u-CB&LuzW|wtx_J=9vPAJ>o6jEhLOJ*BkJEUf;`gsGTx#hB<;8u zNt~?nQzX|sRghy+f6k@<5eU!c{C z4OlB43c)&GdnG0UgTGkHemk7dpv~^`o9Y9M00kbog%e=pq20YJsgYTmLvK1TpftG_ zACc2x(qicB4k_h7Q2lZ@_+Zx}puk_VYtaeV@N+P7f*UxlTk6@vT^u;MWHXhtUJt=H zM5MIwt=Fh~Af>PhVP?k-PW?K!h@H9RFLsN@H@6@<4mkRG&DrxE*DbE~1u$8ZPnzgK z7VK2_Q^Z+9&0(APpTy(ipe&RpB?g?i<==G6jhnVloE}=?rXJHRQkO05A?TXeS#`tA zCO)p;&A6`AwkG0q*@v{&mTSApa<@^bBp-CTYxI#lx|78gGA5QlXL->Y@4Qf zWdltC4V6hc>O%wfVoWLy@??=%;V&W9X2>YZPN`)ij zeB#FEn01Unk&LP+(VP5q;1;03Be!q@x8zf#MH$aJGH@cZdL2*%UXq0;C z+G^0dl^sm60Sf%>rr7+SyX7Yk$J|-2T?s!VHh0e9gZJC><7M#K8I_NyF$&_l(lwPD zMt&V}#LtNH7ZXSG8*$)4kE=KN#FC61GplX;c}1wJDaWubddX3QeY3~Dn3@g0V# zFOg_#Nuy`P`8SEfmSlogm@qGL{+Ps3{t#rAd*4JPA!~8k0fmt;5ISz7Guzalz(B%U zoj_Oh49et*YlQ3h$a1ZQPWH>6ri#psT#cZVDmTeXjS__|8+rW82bD4q}}jm9{pYo z!y9uJOUZ@B8ihPB*vBwXI7jEAF!6Hu9_qD;Ml$r8=6i@(N15R|JezL722Uu}Ld@l< zCux0FYTolqA+z=izD4`nGHZJlLeMK1%;(fx(zQdJXxMa4+5Isz_u{1Ove;GWbi1E< zjWdPnImFu<4i>Uq`(WO1m)PF1fQwXL?PdmjnS5li$%FDf72zwy{ZdtNoGA6i4exZL z1TBKbQ9{NP;~ou?#u}q0HrX^TaFYeR&3GE&ZxG)?dC%r448<^Rfa9k}&8L`qd_2%j}t#PaSP&w<1Ri$uKWmOOc;!s1N)20^cCS z;t3T=npr5fK5AP8>mrlwFwlcY!$((K{qt&L#LVcV**D@sv{&jVg4NaBh;`$(B&bq%jL z9O`DGuHcNB#9c};Kf*$E= zb|mh?mb+OyX%IT=%q2*iHH1*RT;uEOb(%@#m@*0OO>S?xAlx8w63hi1{oK+VpaV1( zxAiw2*Hg47@tdn-Cbq+GXjiClH?GkunhKUwg}%|7S&}_iQu=6jvyj=z5aI@niTdzG zM2tR+aBNXIg4#?xa*W5$mMN_^auYCz;Xy4ZLS z9jx5)dgA7KFnoPkSh}hMqJg($NaSOme>~RU_gzxD@y?c=W%=2r!uOQrcRLsICZv`F zR1xZo!7)>+{FhhJDfnmw@<`tgFI;jTv9`f#mRDptM=B!{er-hX?eSA8`@(B6QR9A< z<%IG2AFy8Yr<)CW^AS6kqOo(K1xXB}{!{AwQ_h71dfIixmym6J zaHfU4tsCrX-_RH?m3#F0eViMNvuwH#>g&{k1W6uEsep>?rBsrqPpR%1@sOtH1({IE z?#i~I6uijPW)^>Kt8LSV(p+E=T-|$+QUL`XNvVL2Iy__wp!DZZXjlMw<*o$xn;V_A z$j!rZBu?-jZeo|2v#ZF9$@Vg2=(+E&2nKoset7Usa30?YLQi&dpXC1Reg1;QWT7fWpI4VLczd>fGB*!Z*;kHA;jfX&y8FE)U=!tg7%uGwzm1n_6>m$2E*Xe zRd^mfdl&5fF$n?DBUQ;UEh@E4Up(C|mDMGVh#C2+rY1hcl()XLAcOD^Bm~d{_rsU` z1RFM-AoOH=J4o#l0;n=hNr=9?DFQTh$cm;mG3mACBXRhsEQ~jkxbrB5@+nj2L=_-G zQb!2^RAi43Ql}>b9Cu-Fr@`mza+(Z<63M7H3DEoPK|z!bMgv30R4rvQ2ZR6${3V3A z1evTFJJj@^Io=-5YN}$;;ha=Djm~cNy_#0ej}hAv?)x2r3yu+DU1l%mH%h`FEv1#{ zZt_6eDmP~&2C>BJ4BG~V?so_#-_v8y9+-L{FTRe8wdE{HdP!)8fFh3|PAnWV8@!x(ay0J=L&D=7Eu<~XRHI#kpft5e@V%6s{=)5vxL9+CDx|HX zrL~h2uAU)z!+~5IFWi+Deq;JAK}-F|wN9ckErjb0i5E@LmmCIe@UJg?mMv%C`JzB$ z2Ya8~oJeiHNpyT_; zUcah(fs5TA&~rB0s72zaV%OMHxpHkDlXte7G)xwV*P*G# z8M&5V4)|U4ke%^y=HzdDue$QQA)FXyhNg{SGMi#Ec!sY*ObxnlQ4M|Rc7!*i5KYk`W%TYB=Ubw7=(ZxyxL!EF@QrD@y*0|C6V)MRLum=_3=0X#Ak4x{)1n^a zRhdZIn9tSUE&`jK#ssEoMJq1V69fap-f(G|=yd~v(2Skt1@f;Qf|FAGjEzZtP<>sZ zX~~Tme!*~;?qzT}MUY2;U>?g7kzpf%f9(*2RIrI^5RksX+3agjh4sSiEqxKhjdbw5 z8VHUYR$2^IY}g+=1P!g1+g>XKJThkbBU z-XWNh+{e)_HHsZI2P4&V;mgpKIYO{!Fd;WX?{M_ifiunWY7~mVo*$LHKHcYh!}R^N z(O3)-Vd8%AWcGs&L2U44&8ud{=&s4qcJQc*fim3T;dp4(kQB0yNHl9EHGZAurOq_} z7i(VctLAZbub~>0MoMq39#`{|H?oHNIz9)QN-d^D;i=B{m{@^9LLg`Jz9)RdB7`LM zXK8+0#5m$T`;vp)cP;;J9fG%>ijxY^Rk+3;Q}Y5To1PwTHewmTqM-+OMKSy)U@UbW zvP<{#j6O9yBW;uVmObujHXQ`NQi4#aUF_Hng^xpZDbf~1dX z9;nEk=A}=s`G+O(u5C1RpWVqgG7N;!?b+>_TXP3rAt_ae zlg5zK>NN(s$%l%oTRd|mJLMY}5GXfL1|KOxuDI{-Ck7Pw+wCXTp4^)<@nn6)U@G$d z%YH(tOEp$LoT}N0Yw0*SNsTnC`#qgNul5gb)CsyTPY`;to=)lgp3bY6&8%^*O0VQX zHL77;-KiGFWt&$HOBSqT$*=Env=RGtHj+NG(O+yM{ckqf?!-ZFA?`+U`rowCPQK>s zt2|rX?!UxFg@JUVo+acvkJr*C85FTqRpqs?nwh<+Az}<&W(nUA9N$Jv>0>H5RZVc< z=bGZ260f1@Y)~gGMGm5N`ows<=o8ERbQ|pp1L$8mr7-GU(=p0=iCd&!tMSnIw`g4L zqEHaB^bc&OdEXX!v7HkVBy&_4Kt=Y1A#-|R%*&xVeo4T=8y&g==U?AD&Kp^5*y|O9 z%sd1OiFTEv{y-Q&fxko;{P(*kuL@SyZPhi@NNs_c!jm~rBH%su$Fug&U1CJuKG=E; zDDb!2dJJeO?BQOX)IcnyIZn>q5-NBR4c?9L*GQ00pYBj;)Ng(}r*a=7K=0CThY^{5 zjOZ+LmRzk)$9-Fl!XRaD;r7(N3v9$!Zs?_*6nGOyjK{CTh|C#A{$h+6e8b45F>!r) za$=ME|0G5*z)=*~=B}j`e)vl;QdT5Trj?+H|8S`hJPtDg@s92JbZ>KqVPYar{zR;a zrsHD-g_<+7?V*AKYFe)EPMCglsc!(-E3KXf&#~-<6m5%$ei=r9{-sl5L>|kKZsx%Z zo-ct2n9a-A2#+5OJE1QJDVmX(a@O?WLqmdOk75L<$R0*yPmhtZUGA$6WOs-L9zRg3 z_Rv~&w!xz}NE{GPk8AGs9_3m+zz9&_FTu#5$RJW#iuUXqs&xtcoQ+YV7uhNsQ%~C@ z&{134_}lGLb^?q5t>`#~TYz4>-_9+v!_fa(wnFKF)70{%mR1G>{+kGfb=Km7-wdN0{5USIjdN-e@=Duda2~hIU0fbh^{=z^UE6CQm(n zAd5-MA5Br5G&!f4%)9NiU&8ty;zCWnq|T6hc3oiA z6MH4XuPR`Gqsm^F>OIR1UbRLIw&VdJZk0>Pv1Xj~9 zyjZ{7n#Jm|H+rjg;qmK8YY`R8=7p`gG#4n4xP zo`F_~Oev9HV0miK5j)5fFKil^h(^$YZ$V@*kV$y*fFTP@g5(aB0HW-n&_8eVHh*vQ zw%9u|Dtvx0zhY6z_<8XInIuDF3(3I>VlvYGjm4$gvF?- zK2g;J|IizJg3x+yI0#(q@%GEb;S^nbLQ(KcBpeq_*tOQTHdA`=6Fa!T!1%$yIl=b+ zK7)be_WM?WD&v&t%*%?5IBwiteQ<;27GGS?!10`AcV7)#p}~vlN#+sq?T{e3qv;G# zk-c>QS z4t#!wq)(z*XszGUju62-{0^bi_*8P4MguAIl#l4p5Oj;>e$#I5)}8Nh{sO&59As@L1~{ zO~i(^%z_sRE>Pv2;!z#b^*kUBP~a~i4u*mE`v^*$7om1JX>Y+smZic3)1Pz?LwUuQ zsYwZ=P#kOm1Qht&Z36UvPMj0$$q00o>Ar@5UgqCUL-PCmz?aC}mR;-QXVxBktO>f$ z_Q0rGC}t)C5+5O0x4%uX{pwyna1G>MKX7Jdn$ont@7R_*f1S-^MZd8wa3B22&GYoe zH!&N0yE3R^#X3TIyb{TXg1h4BW6+Se1p}hTBI(rC#i%cDw?#qI_g@ZZ zj2mHs9xwY3kAUob5<{TK=jk73x_nml!8C>8;dj!yvA4HT{Q$EjVY`mq4FvYj)(dl} ziq}h~ZZNM5UP|YGA?LeXfcT-C9ojHI^j#(VsG0bssWDfZ>Vb-xxaz0y_u(i;FFZ=> zy3>IL{z4v+pNcANR=fDs9GE!i)O zL@QxS<{y6T2Yz;|>oRNdJ-6+hLNKL>UOAU{@*(9bJl5r+SZ4Zq_)^%g5I^(-Uzq8H z#p5!ib5WXjhQN*ysz2jzS924>&#A9$3dcoZe6JrkD;}h9D60kn@}bZ_t{<2X_G>?| zG#3QumeF&J;ewqCPB^h(QhjaAH077i2V^#(+?LHcJG~3X^-3FLUxJ)2JNRs$V~g6{ z(A^!M+zny9DV-F7xtT+jb8S~`FhY3RdCa$zn_x`FqHy42?-bXn{s4GB*#1&LV@_Jf z;obqASt}fYx{LyD_Dlo>=1W*vi+s@FTP4JA_Xk;WDeAr2&1)B2eUv-=&

ecf&5+ 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 [codec]\n") + return + } + db := os.Args[1] + codec := carbs.IndexSorted + if len(os.Args) == 3 { + if os.Args[2] == "Hash" { + codec = carbs.IndexHashed + } else if os.Args[2] == "GobHash" { + codec = carbs.IndexGobHashed + } + } + + dbBacking, err := mmap.Open(db) + if err != nil { + fmt.Printf("Error Opening car for hydration: %v\n", err) + return + } + + dbstat, err := os.Stat(db) + if err != nil { + fmt.Printf("Error statting car for hydration: %v\n", err) + return + } + + idx, err := carbs.GenerateIndex(dbBacking, dbstat.Size(), codec, true) + if err != nil { + fmt.Printf("Error generating index: %v\n", err) + return + } + + fmt.Printf("Saving...\n") + + if err := carbs.Save(idx, db); err != nil { + fmt.Printf("Error saving : %v\n", err) + return + } + return +} From f016c35c868a5e73db3d10f1340ff1b5d971e03d Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 14 Jan 2021 12:08:54 +0000 Subject: [PATCH 3427/3817] Update version of github.com/ipld/go-car This commit was moved from ipld/go-car@260b871d171ed80c48800eed6b2adb60dcaba579 --- ipld/car/v2/carbs/carbs.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ipld/car/v2/carbs/carbs.go b/ipld/car/v2/carbs/carbs.go index c59e5d892..303a9779d 100644 --- a/ipld/car/v2/carbs/carbs.go +++ b/ipld/car/v2/carbs/carbs.go @@ -30,7 +30,8 @@ type Carbs struct { var _ bs.Blockstore = (*Carbs)(nil) func (c *Carbs) Read(idx int64) (cid.Cid, []byte, error) { - return util.ReadNode(bufio.NewReader(&unatreader{c.backing, idx})) + bcid, _, data, err := util.ReadNode(bufio.NewReader(&unatreader{c.backing, idx})) + return bcid, data, err } // DeleteBlock doesn't delete a block on RO blockstore @@ -148,7 +149,7 @@ func (c *Carbs) PutMany([]blocks.Block) error { // AllKeysChan returns the list of keys in the store func (c *Carbs) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { - header, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) + header, _, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) if err != nil { return nil, fmt.Errorf("Error reading car header: %w", err) } @@ -192,7 +193,7 @@ func (c *Carbs) HashOnRead(enabled bool) { // Roots returns the root CIDs of the backing car func (c *Carbs) Roots() ([]cid.Cid, error) { - header, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) + header, _, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) if err != nil { return nil, fmt.Errorf("Error reading car header: %w", err) } @@ -238,7 +239,7 @@ func GenerateIndex(store io.ReaderAt, size int64, codec IndexCodec, verbose bool bar.Start() - header, err := car.ReadHeader(bufio.NewReader(&unatreader{store, 0})) + header, _, err := car.ReadHeader(bufio.NewReader(&unatreader{store, 0})) if err != nil { return nil, fmt.Errorf("Error reading car header: %w", err) } From a20f777ebbab4f1738fb537b524a445d35d50af6 Mon Sep 17 00:00:00 2001 From: Will Scott Date: Fri, 9 Apr 2021 19:33:49 -0700 Subject: [PATCH 3428/3817] add 'of' for creation of new Carbs This commit was moved from ipld/go-car@4c5f1ae563fa7eb6e99a3c43dffc7bc05163d2b0 --- ipld/car/v2/carbs/carbs.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ipld/car/v2/carbs/carbs.go b/ipld/car/v2/carbs/carbs.go index 303a9779d..e29d75552 100644 --- a/ipld/car/v2/carbs/carbs.go +++ b/ipld/car/v2/carbs/carbs.go @@ -225,6 +225,11 @@ func Load(path string, noPersist bool) (*Carbs, error) { return &obj, nil } +// Of opens a carbs data store from an existing reader of the base data and index +func Of(backing io.ReaderAt, index Index) *Carbs { + return &Carbs{backing, index} +} + // GenerateIndex provides a low-level interface to create an index over a // reader to a car stream. func GenerateIndex(store io.ReaderAt, size int64, codec IndexCodec, verbose bool) (Index, error) { From 17ed81bf1d88959016a047caab6c20e4ba73d674 Mon Sep 17 00:00:00 2001 From: Will Scott Date: Fri, 9 Apr 2021 20:46:46 -0700 Subject: [PATCH 3429/3817] allow 'Record' Index to be public to allow for other implementations of Index This commit was moved from ipld/go-car@4e343d75979e9dd7f59711ebc67299f2cc1086a5 --- ipld/car/v2/carbs/index.go | 2 +- ipld/car/v2/carbs/indexgobhash.go | 2 +- ipld/car/v2/carbs/indexhashed.go | 2 +- ipld/car/v2/carbs/indexsorted.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ipld/car/v2/carbs/index.go b/ipld/car/v2/carbs/index.go index b13baa9bb..b139a24a4 100644 --- a/ipld/car/v2/carbs/index.go +++ b/ipld/car/v2/carbs/index.go @@ -35,7 +35,7 @@ var IndexAtlas = map[IndexCodec]IndexCls{ // Record is a pre-processed record of a car item and location. type Record struct { cid.Cid - idx uint64 + Idx uint64 } // Index provides an interface for figuring out where in the car a given cid begins diff --git a/ipld/car/v2/carbs/indexgobhash.go b/ipld/car/v2/carbs/indexgobhash.go index 51ba4c699..b7dedba0a 100644 --- a/ipld/car/v2/carbs/indexgobhash.go +++ b/ipld/car/v2/carbs/indexgobhash.go @@ -33,7 +33,7 @@ func (m *mapGobIndex) Codec() IndexCodec { func (m *mapGobIndex) Load(rs []Record) error { for _, r := range rs { - (*m)[r.Cid] = r.idx + (*m)[r.Cid] = r.Idx } return nil } diff --git a/ipld/car/v2/carbs/indexhashed.go b/ipld/car/v2/carbs/indexhashed.go index 62bfce400..f4c04aed0 100644 --- a/ipld/car/v2/carbs/indexhashed.go +++ b/ipld/car/v2/carbs/indexhashed.go @@ -32,7 +32,7 @@ func (m *mapIndex) Codec() IndexCodec { func (m *mapIndex) Load(rs []Record) error { for _, r := range rs { - (*m)[r.Cid] = r.idx + (*m)[r.Cid] = r.Idx } return nil } diff --git a/ipld/car/v2/carbs/indexsorted.go b/ipld/car/v2/carbs/indexsorted.go index 6378df34c..d16d10d7d 100644 --- a/ipld/car/v2/carbs/indexsorted.go +++ b/ipld/car/v2/carbs/indexsorted.go @@ -165,7 +165,7 @@ func (m *multiWidthIndex) Load(items []Record) error { idxs[len(digest)] = make([]digestRecord, 0) idx = idxs[len(digest)] } - idxs[len(digest)] = append(idx, digestRecord{digest, item.idx}) + idxs[len(digest)] = append(idx, digestRecord{digest, item.Idx}) } // Sort each list. then write to compact form. From 19d771b9d68cd0583b744fd288dc30540a3a54cd Mon Sep 17 00:00:00 2001 From: Will Scott Date: Sat, 10 Apr 2021 17:14:52 -0700 Subject: [PATCH 3430/3817] update to go-car v0.2.2 This commit was moved from ipld/go-car@0509247d9d32d72ba07752ea1ebe45bee3c3c914 --- ipld/car/v2/carbs/carbs.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ipld/car/v2/carbs/carbs.go b/ipld/car/v2/carbs/carbs.go index e29d75552..ca7256f81 100644 --- a/ipld/car/v2/carbs/carbs.go +++ b/ipld/car/v2/carbs/carbs.go @@ -30,7 +30,7 @@ type Carbs struct { var _ bs.Blockstore = (*Carbs)(nil) func (c *Carbs) Read(idx int64) (cid.Cid, []byte, error) { - bcid, _, data, err := util.ReadNode(bufio.NewReader(&unatreader{c.backing, idx})) + bcid, data, err := util.ReadNode(bufio.NewReader(&unatreader{c.backing, idx})) return bcid, data, err } @@ -149,7 +149,7 @@ func (c *Carbs) PutMany([]blocks.Block) error { // AllKeysChan returns the list of keys in the store func (c *Carbs) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { - header, _, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) + header, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) if err != nil { return nil, fmt.Errorf("Error reading car header: %w", err) } @@ -193,7 +193,7 @@ func (c *Carbs) HashOnRead(enabled bool) { // Roots returns the root CIDs of the backing car func (c *Carbs) Roots() ([]cid.Cid, error) { - header, _, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) + header, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) if err != nil { return nil, fmt.Errorf("Error reading car header: %w", err) } @@ -244,7 +244,7 @@ func GenerateIndex(store io.ReaderAt, size int64, codec IndexCodec, verbose bool bar.Start() - header, _, err := car.ReadHeader(bufio.NewReader(&unatreader{store, 0})) + header, err := car.ReadHeader(bufio.NewReader(&unatreader{store, 0})) if err != nil { return nil, fmt.Errorf("Error reading car header: %w", err) } From 6977b265b02b3a12cae336d6d321d7305e043238 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 15 Jun 2021 11:11:06 +0100 Subject: [PATCH 3431/3817] Address `staticcheck` errors Remove unused structs, convert error messages to lower case and remove redundant `return` statements. Address review comments - Reoder imports using `gofumpt`. - Use consistent import alias for `carv1`. - Rename structs for better readability. - Add TODO to fix logging, tests, etc. - Move test related files to `testdata`. This commit was moved from ipld/go-car@94190bc83ca291d393111d21519920632ca00ed8 --- ipld/car/v2/carbs/carbs.go | 92 +++++++++++----------- ipld/car/v2/carbs/carbs_test.go | 31 +------- ipld/car/v2/carbs/index.go | 2 +- ipld/car/v2/carbs/indexsorted.go | 2 +- ipld/car/v2/carbs/{ => testdata}/test.car | Bin ipld/car/v2/carbs/util/hydrate.go | 4 +- 6 files changed, 51 insertions(+), 80 deletions(-) rename ipld/car/v2/carbs/{ => testdata}/test.car (100%) diff --git a/ipld/car/v2/carbs/carbs.go b/ipld/car/v2/carbs/carbs.go index ca7256f81..6891d925d 100644 --- a/ipld/car/v2/carbs/carbs.go +++ b/ipld/car/v2/carbs/carbs.go @@ -10,47 +10,47 @@ import ( blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - bs "github.com/ipfs/go-ipfs-blockstore" + blockstore "github.com/ipfs/go-ipfs-blockstore" "github.com/multiformats/go-multihash" pb "github.com/cheggaaa/pb/v3" - car "github.com/ipld/go-car" + carv1 "github.com/ipld/go-car" "github.com/ipld/go-car/util" "golang.org/x/exp/mmap" ) -var errNotFound = bs.ErrNotFound +var errNotFound = blockstore.ErrNotFound -// Carbs provides a read-only Car Block Store. -type Carbs struct { +// BlockStore provides a read-only Car Block Store. +type BlockStore struct { backing io.ReaderAt idx Index } -var _ bs.Blockstore = (*Carbs)(nil) +var _ blockstore.Blockstore = (*BlockStore)(nil) -func (c *Carbs) Read(idx int64) (cid.Cid, []byte, error) { - bcid, data, err := util.ReadNode(bufio.NewReader(&unatreader{c.backing, idx})) +func (b *BlockStore) Read(idx int64) (cid.Cid, []byte, error) { + bcid, data, err := util.ReadNode(bufio.NewReader(&unatreader{b.backing, idx})) return bcid, data, err } // DeleteBlock doesn't delete a block on RO blockstore -func (c *Carbs) DeleteBlock(_ cid.Cid) error { +func (b *BlockStore) DeleteBlock(_ cid.Cid) error { return fmt.Errorf("read only") } // Has indicates if the store has a cid -func (c *Carbs) Has(key cid.Cid) (bool, error) { - offset, err := c.idx.Get(key) +func (b *BlockStore) Has(key cid.Cid) (bool, error) { + offset, err := b.idx.Get(key) if err != nil { return false, err } - uar := unatreader{c.backing, int64(offset)} + uar := unatreader{b.backing, int64(offset)} _, err = binary.ReadUvarint(&uar) if err != nil { return false, err } - cid, _, err := readCid(c.backing, uar.at) + cid, _, err := readCid(b.backing, uar.at) if err != nil { return false, err } @@ -100,60 +100,61 @@ func readCid(store io.ReaderAt, at int64) (cid.Cid, int, error) { } // Get gets a block from the store -func (c *Carbs) Get(key cid.Cid) (blocks.Block, error) { - offset, err := c.idx.Get(key) +func (b *BlockStore) Get(key cid.Cid) (blocks.Block, error) { + offset, err := b.idx.Get(key) if err != nil { return nil, err } - entry, bytes, err := c.Read(int64(offset)) + entry, bytes, err := b.Read(int64(offset)) if err != nil { + // TODO replace with logging fmt.Printf("failed get %d:%v\n", offset, err) - return nil, bs.ErrNotFound + return nil, blockstore.ErrNotFound } if !entry.Equals(key) { - return nil, bs.ErrNotFound + return nil, blockstore.ErrNotFound } return blocks.NewBlockWithCid(bytes, key) } // GetSize gets how big a item is -func (c *Carbs) GetSize(key cid.Cid) (int, error) { - idx, err := c.idx.Get(key) +func (b *BlockStore) GetSize(key cid.Cid) (int, error) { + idx, err := b.idx.Get(key) if err != nil { return -1, err } - len, err := binary.ReadUvarint(&unatreader{c.backing, int64(idx)}) + len, err := binary.ReadUvarint(&unatreader{b.backing, int64(idx)}) if err != nil { - return -1, bs.ErrNotFound + return -1, blockstore.ErrNotFound } - cid, _, err := readCid(c.backing, int64(idx+len)) + cid, _, err := readCid(b.backing, int64(idx+len)) if err != nil { return 0, err } if !cid.Equals(key) { - return -1, bs.ErrNotFound + return -1, blockstore.ErrNotFound } // get cid. validate. return int(len), err } // Put does nothing on a ro store -func (c *Carbs) Put(blocks.Block) error { +func (b *BlockStore) Put(blocks.Block) error { return fmt.Errorf("read only") } // PutMany does nothing on a ro store -func (c *Carbs) PutMany([]blocks.Block) error { +func (b *BlockStore) PutMany([]blocks.Block) error { return fmt.Errorf("read only") } // AllKeysChan returns the list of keys in the store -func (c *Carbs) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { - header, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) +func (b *BlockStore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { + header, err := carv1.ReadHeader(bufio.NewReader(&unatreader{b.backing, 0})) if err != nil { - return nil, fmt.Errorf("Error reading car header: %w", err) + return nil, fmt.Errorf("error reading car header: %w", err) } - offset, err := car.HeaderSize(header) + offset, err := carv1.HeaderSize(header) if err != nil { return nil, err } @@ -162,14 +163,14 @@ func (c *Carbs) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { go func() { done := ctx.Done() - rdr := unatreader{c.backing, int64(offset)} - for true { + rdr := unatreader{b.backing, int64(offset)} + for { l, err := binary.ReadUvarint(&rdr) thisItemForNxt := rdr.at if err != nil { return } - c, _, err := readCid(c.backing, thisItemForNxt) + c, _, err := readCid(b.backing, thisItemForNxt) if err != nil { return } @@ -187,21 +188,20 @@ func (c *Carbs) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { } // HashOnRead does nothing -func (c *Carbs) HashOnRead(enabled bool) { - return +func (b *BlockStore) HashOnRead(bool) { } // Roots returns the root CIDs of the backing car -func (c *Carbs) Roots() ([]cid.Cid, error) { - header, err := car.ReadHeader(bufio.NewReader(&unatreader{c.backing, 0})) +func (b *BlockStore) Roots() ([]cid.Cid, error) { + header, err := carv1.ReadHeader(bufio.NewReader(&unatreader{b.backing, 0})) if err != nil { - return nil, fmt.Errorf("Error reading car header: %w", err) + return nil, fmt.Errorf("error reading car header: %w", err) } return header.Roots, nil } // Load opens a carbs data store, generating an index if it does not exist -func Load(path string, noPersist bool) (*Carbs, error) { +func Load(path string, noPersist bool) (*BlockStore, error) { reader, err := mmap.Open(path) if err != nil { return nil, err @@ -218,7 +218,7 @@ func Load(path string, noPersist bool) (*Carbs, error) { } } } - obj := Carbs{ + obj := BlockStore{ backing: reader, idx: idx, } @@ -226,8 +226,8 @@ func Load(path string, noPersist bool) (*Carbs, error) { } // Of opens a carbs data store from an existing reader of the base data and index -func Of(backing io.ReaderAt, index Index) *Carbs { - return &Carbs{backing, index} +func Of(backing io.ReaderAt, index Index) *BlockStore { + return &BlockStore{backing, index} } // GenerateIndex provides a low-level interface to create an index over a @@ -244,11 +244,11 @@ func GenerateIndex(store io.ReaderAt, size int64, codec IndexCodec, verbose bool bar.Start() - header, err := car.ReadHeader(bufio.NewReader(&unatreader{store, 0})) + header, err := carv1.ReadHeader(bufio.NewReader(&unatreader{store, 0})) if err != nil { - return nil, fmt.Errorf("Error reading car header: %w", err) + return nil, fmt.Errorf("error reading car header: %w", err) } - offset, err := car.HeaderSize(header) + offset, err := carv1.HeaderSize(header) if err != nil { return nil, err } @@ -258,7 +258,7 @@ func GenerateIndex(store io.ReaderAt, size int64, codec IndexCodec, verbose bool records := make([]Record, 0) rdr := unatreader{store, int64(offset)} - for true { + for { thisItemIdx := rdr.at l, err := binary.ReadUvarint(&rdr) bar.Add64(int64(l)) diff --git a/ipld/car/v2/carbs/carbs_test.go b/ipld/car/v2/carbs/carbs_test.go index 5ec52e972..2aa48fe70 100644 --- a/ipld/car/v2/carbs/carbs_test.go +++ b/ipld/car/v2/carbs/carbs_test.go @@ -1,13 +1,8 @@ package carbs import ( - "context" - "fmt" "os" "testing" - - "github.com/ipfs/go-cid" - format "github.com/ipfs/go-ipld-format" ) /* @@ -56,7 +51,8 @@ func TestIndexRT(t *testing.T) { } defer os.Remove(carFile) */ - carFile := "test.car" + // TODO use temporary directory to run tests taht work with OS file system to avoid accidental source code modification + carFile := "testdata/test.car" cf, err := Load(carFile, false) if err != nil { @@ -83,26 +79,3 @@ func TestIndexRT(t *testing.T) { t.Fatalf("bad index: %d %v", idx, err) } } - -type mockNodeGetter struct { - Nodes map[cid.Cid]format.Node -} - -func (m *mockNodeGetter) Get(_ context.Context, c cid.Cid) (format.Node, error) { - n, ok := m.Nodes[c] - if !ok { - return nil, fmt.Errorf("unknown node") - } - return n, nil -} - -func (m *mockNodeGetter) GetMany(_ context.Context, cs []cid.Cid) <-chan *format.NodeOption { - ch := make(chan *format.NodeOption, 5) - go func() { - for _, c := range cs { - n, e := m.Get(nil, c) - ch <- &format.NodeOption{Node: n, Err: e} - } - }() - return ch -} diff --git a/ipld/car/v2/carbs/index.go b/ipld/car/v2/carbs/index.go index b139a24a4..1f381308c 100644 --- a/ipld/car/v2/carbs/index.go +++ b/ipld/car/v2/carbs/index.go @@ -78,7 +78,7 @@ func Restore(path string) (Index, error) { } idx, ok := IndexAtlas[IndexCodec(codec)] if !ok { - return nil, fmt.Errorf("Unknown codec: %d", codec) + return nil, fmt.Errorf("unknown codec: %d", codec) } idxInst := idx() if err := idxInst.Unmarshal(&uar); err != nil { diff --git a/ipld/car/v2/carbs/indexsorted.go b/ipld/car/v2/carbs/indexsorted.go index d16d10d7d..eca593c9f 100644 --- a/ipld/car/v2/carbs/indexsorted.go +++ b/ipld/car/v2/carbs/indexsorted.go @@ -88,7 +88,7 @@ func (s *singleWidthIndex) get(d []byte) uint64 { if int64(idx) == s.len { return 0 } - if bytes.Compare(d[:], s.index[idx*int(s.width):(idx+1)*int(s.width)-8]) != 0 { + if !bytes.Equal(d[:], s.index[idx*int(s.width):(idx+1)*int(s.width)-8]) { return 0 } return binary.LittleEndian.Uint64(s.index[(idx+1)*int(s.width)-8 : (idx+1)*int(s.width)]) diff --git a/ipld/car/v2/carbs/test.car b/ipld/car/v2/carbs/testdata/test.car similarity index 100% rename from ipld/car/v2/carbs/test.car rename to ipld/car/v2/carbs/testdata/test.car diff --git a/ipld/car/v2/carbs/util/hydrate.go b/ipld/car/v2/carbs/util/hydrate.go index db800da7d..d0fb50ae1 100644 --- a/ipld/car/v2/carbs/util/hydrate.go +++ b/ipld/car/v2/carbs/util/hydrate.go @@ -4,7 +4,7 @@ import ( "fmt" "os" - "github.com/willscott/carbs" + "github.com/ipld/go-car/v2/carbs" "golang.org/x/exp/mmap" ) @@ -45,7 +45,5 @@ func main() { if err := carbs.Save(idx, db); err != nil { fmt.Printf("Error saving : %v\n", err) - return } - return } From 2d6cf75d688d8e660f223198c3a25ae38d14c615 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 17 Jun 2021 12:21:31 +0100 Subject: [PATCH 3432/3817] Remove carbs dedicated go module Remove the carbs dedicated go module, along with CI configuration, licenses and move README into `doc.go`. This commit was moved from ipld/go-car@9e407d6bed12e2e1b2c42fd7d8b6e665b1937e53 --- ipld/car/v2/carbs/LICENSE | 202 ---------------------------------- ipld/car/v2/carbs/LICENSE-MIT | 21 ---- ipld/car/v2/carbs/README.md | 13 --- ipld/car/v2/carbs/doc.go | 2 + 4 files changed, 2 insertions(+), 236 deletions(-) delete mode 100644 ipld/car/v2/carbs/LICENSE delete mode 100644 ipld/car/v2/carbs/LICENSE-MIT delete mode 100644 ipld/car/v2/carbs/README.md create mode 100644 ipld/car/v2/carbs/doc.go diff --git a/ipld/car/v2/carbs/LICENSE b/ipld/car/v2/carbs/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/ipld/car/v2/carbs/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/ipld/car/v2/carbs/LICENSE-MIT b/ipld/car/v2/carbs/LICENSE-MIT deleted file mode 100644 index 8c6ca2a25..000000000 --- a/ipld/car/v2/carbs/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 Will Scott - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/ipld/car/v2/carbs/README.md b/ipld/car/v2/carbs/README.md deleted file mode 100644 index 9aec21a2c..000000000 --- a/ipld/car/v2/carbs/README.md +++ /dev/null @@ -1,13 +0,0 @@ -🔠Carbs -=== - -Car Blockstore provides a read-only blockstore interface directly reading out of a car file. - - -License ---- - -Carbs is dual-licensed under Apache 2.0 and MIT terms: - - Apache License, Version 2.0, (LICENSE or http://www.apache.org/licenses/LICENSE-2.0) - MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) diff --git a/ipld/car/v2/carbs/doc.go b/ipld/car/v2/carbs/doc.go new file mode 100644 index 000000000..bcff12ac3 --- /dev/null +++ b/ipld/car/v2/carbs/doc.go @@ -0,0 +1,2 @@ +// Package carbs provides a read-only blockstore interface directly reading out of a car file. +package carbs From e5b0008807a888ce24ecd48501e9b2d0d921f538 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Fri, 18 Jun 2021 14:03:41 +0100 Subject: [PATCH 3433/3817] Implement read-only random access blockstore Refactor the carbon and carbs packages into a read-only blockstore. Move the packages to `internal` as needed and remove duplicate reader implementations. Improve serialization efficiency for car v2 primitives. Note the readonly blockstore will be altered in the coming PRs to understand car v2 format and work transparently. For now we want to push the refactoring and changes to unblock other parallel workstreams. Address PR reviews - Sort imports - Rename types for clarity - Add TODO for future PRs This commit was moved from ipld/go-car@d336f84d1a2121486619c9aafff9a0ffdc6dd2a6 --- ipld/car/v2/blockstore/readonly.go | 189 +++++++++++ .../readonly_test.go} | 7 +- .../{carbs => blockstore}/testdata/test.car | Bin ipld/car/v2/car.go | 54 ++-- ipld/car/v2/carbon/reader.go | 23 -- ipld/car/v2/carbs/carbs.go | 301 ------------------ ipld/car/v2/carbs/reader.go | 20 -- ipld/car/v2/{ => internal}/carbon/carbon.go | 23 +- .../v2/{ => internal}/carbon/carbon_fds.go | 13 +- .../v2/{ => internal}/carbon/carbon_test.go | 6 +- ipld/car/v2/{ => internal}/carbon/doc.go | 1 + .../car/v2/{ => internal}/carbon/poswriter.go | 0 ipld/car/v2/{ => internal}/carbs/doc.go | 1 + .../v2/{ => internal}/carbs/util/hydrate.go | 15 +- ipld/car/v2/internal/index/errors.go | 9 + ipld/car/v2/internal/index/generator.go | 81 +++++ .../car/v2/{carbs => internal/index}/index.go | 66 ++-- .../{carbs => internal/index}/indexgobhash.go | 4 +- .../{carbs => internal/index}/indexhashed.go | 4 +- .../{carbs => internal/index}/indexsorted.go | 33 +- .../index}/insertionindex.go | 65 ++-- ipld/car/v2/internal/io/cid.go | 52 +++ .../car/v2/{ => internal/io}/offset_reader.go | 18 +- ipld/car/v2/internal/io/offset_writer.go | 16 + ipld/car/v2/reader.go | 3 +- ipld/car/v2/writer.go | 9 +- ipld/car/v2/writer_test.go | 4 +- 27 files changed, 513 insertions(+), 504 deletions(-) create mode 100644 ipld/car/v2/blockstore/readonly.go rename ipld/car/v2/{carbs/carbs_test.go => blockstore/readonly_test.go} (92%) rename ipld/car/v2/{carbs => blockstore}/testdata/test.car (100%) delete mode 100644 ipld/car/v2/carbon/reader.go delete mode 100644 ipld/car/v2/carbs/carbs.go delete mode 100644 ipld/car/v2/carbs/reader.go rename ipld/car/v2/{ => internal}/carbon/carbon.go (73%) rename ipld/car/v2/{ => internal}/carbon/carbon_fds.go (86%) rename ipld/car/v2/{ => internal}/carbon/carbon_test.go (91%) rename ipld/car/v2/{ => internal}/carbon/doc.go (92%) rename ipld/car/v2/{ => internal}/carbon/poswriter.go (100%) rename ipld/car/v2/{ => internal}/carbs/doc.go (81%) rename ipld/car/v2/{ => internal}/carbs/util/hydrate.go (73%) create mode 100644 ipld/car/v2/internal/index/errors.go create mode 100644 ipld/car/v2/internal/index/generator.go rename ipld/car/v2/{carbs => internal/index}/index.go (55%) rename ipld/car/v2/{carbs => internal/index}/indexgobhash.go (91%) rename ipld/car/v2/{carbs => internal/index}/indexhashed.go (91%) rename ipld/car/v2/{carbs => internal/index}/indexsorted.go (90%) rename ipld/car/v2/{carbon => internal/index}/insertionindex.go (57%) create mode 100644 ipld/car/v2/internal/io/cid.go rename ipld/car/v2/{ => internal/io}/offset_reader.go (71%) create mode 100644 ipld/car/v2/internal/io/offset_writer.go diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go new file mode 100644 index 000000000..57a1e0f76 --- /dev/null +++ b/ipld/car/v2/blockstore/readonly.go @@ -0,0 +1,189 @@ +package blockstore + +import ( + "bufio" + "context" + "encoding/binary" + "errors" + "fmt" + "io" + + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + blockstore "github.com/ipfs/go-ipfs-blockstore" + carv1 "github.com/ipld/go-car" + "github.com/ipld/go-car/util" + "github.com/ipld/go-car/v2/internal/index" + internalio "github.com/ipld/go-car/v2/internal/io" + "golang.org/x/exp/mmap" +) + +var _ blockstore.Blockstore = (*ReadOnly)(nil) + +// errUnsupported is returned for unsupported operations +var errUnsupported = errors.New("unsupported operation") + +// ReadOnly provides a read-only Car Block Store. +type ReadOnly struct { + backing io.ReaderAt + idx index.Index +} + +// ReadOnlyOf opens a carbs data store from an existing reader of the base data and index +func ReadOnlyOf(backing io.ReaderAt, index index.Index) *ReadOnly { + return &ReadOnly{backing, index} +} + +// LoadReadOnly opens a read-only blockstore, generating an index if it does not exist +func LoadReadOnly(path string, noPersist bool) (*ReadOnly, error) { + reader, err := mmap.Open(path) + if err != nil { + return nil, err + } + idx, err := index.Restore(path) + if err != nil { + idx, err = index.GenerateIndex(reader, 0, index.IndexSorted) + if err != nil { + return nil, err + } + if !noPersist { + if err = index.Save(idx, path); err != nil { + return nil, err + } + } + } + obj := ReadOnly{ + backing: reader, + idx: idx, + } + return &obj, nil +} + +func (b *ReadOnly) read(idx int64) (cid.Cid, []byte, error) { + bcid, data, err := util.ReadNode(bufio.NewReader(internalio.NewOffsetReader(b.backing, idx))) + return bcid, data, err +} + +// DeleteBlock is unsupported and always returns an error +func (b *ReadOnly) DeleteBlock(_ cid.Cid) error { + return errUnsupported +} + +// Has indicates if the store has a cid +func (b *ReadOnly) Has(key cid.Cid) (bool, error) { + offset, err := b.idx.Get(key) + if err != nil { + return false, err + } + uar := internalio.NewOffsetReader(b.backing, int64(offset)) + _, err = binary.ReadUvarint(uar) + if err != nil { + return false, err + } + c, _, err := internalio.ReadCid(b.backing, uar.Offset()) + if err != nil { + return false, err + } + return c.Equals(key), nil +} + +// Get gets a block from the store +func (b *ReadOnly) Get(key cid.Cid) (blocks.Block, error) { + offset, err := b.idx.Get(key) + if err != nil { + return nil, err + } + entry, bytes, err := b.read(int64(offset)) + if err != nil { + // TODO Improve error handling; not all errors mean NotFound. + return nil, blockstore.ErrNotFound + } + if !entry.Equals(key) { + return nil, blockstore.ErrNotFound + } + return blocks.NewBlockWithCid(bytes, key) +} + +// GetSize gets how big a item is +func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { + idx, err := b.idx.Get(key) + if err != nil { + return -1, err + } + l, err := binary.ReadUvarint(internalio.NewOffsetReader(b.backing, int64(idx))) + if err != nil { + return -1, blockstore.ErrNotFound + } + c, _, err := internalio.ReadCid(b.backing, int64(idx+l)) + if err != nil { + return 0, err + } + if !c.Equals(key) { + return -1, blockstore.ErrNotFound + } + // get cid. validate. + return int(l), err +} + +// Put is not supported and always returns an error +func (b *ReadOnly) Put(blocks.Block) error { + return errUnsupported +} + +// PutMany is not supported and always returns an error +func (b *ReadOnly) PutMany([]blocks.Block) error { + return errUnsupported +} + +// AllKeysChan returns the list of keys in the store +func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { + // TODO we may use this walk for populating the index, and we need to be able to iterate keys in this way somewhere for index generation. In general though, when it's asked for all keys from a blockstore with an index, we should iterate through the index when possible rather than linear reads through the full car. + header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReader(b.backing, 0))) + if err != nil { + return nil, fmt.Errorf("error reading car header: %w", err) + } + offset, err := carv1.HeaderSize(header) + if err != nil { + return nil, err + } + + ch := make(chan cid.Cid, 5) + go func() { + done := ctx.Done() + + rdr := internalio.NewOffsetReader(b.backing, int64(offset)) + for { + l, err := binary.ReadUvarint(rdr) + thisItemForNxt := rdr.Offset() + if err != nil { + return + } + c, _, err := internalio.ReadCid(b.backing, thisItemForNxt) + if err != nil { + return + } + rdr.SeekOffset(thisItemForNxt + int64(l)) + + select { + case ch <- c: + continue + case <-done: + return + } + } + }() + return ch, nil +} + +// HashOnRead does nothing +func (b *ReadOnly) HashOnRead(bool) { +} + +// Roots returns the root CIDs of the backing car +func (b *ReadOnly) Roots() ([]cid.Cid, error) { + header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReader(b.backing, 0))) + if err != nil { + return nil, fmt.Errorf("error reading car header: %w", err) + } + return header.Roots, nil +} diff --git a/ipld/car/v2/carbs/carbs_test.go b/ipld/car/v2/blockstore/readonly_test.go similarity index 92% rename from ipld/car/v2/carbs/carbs_test.go rename to ipld/car/v2/blockstore/readonly_test.go index 2aa48fe70..a2d38a282 100644 --- a/ipld/car/v2/carbs/carbs_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -1,6 +1,7 @@ -package carbs +package blockstore import ( + "github.com/ipld/go-car/v2/internal/index" "os" "testing" ) @@ -54,7 +55,7 @@ func TestIndexRT(t *testing.T) { // TODO use temporary directory to run tests taht work with OS file system to avoid accidental source code modification carFile := "testdata/test.car" - cf, err := Load(carFile, false) + cf, err := LoadReadOnly(carFile, false) if err != nil { t.Fatal(err) } @@ -71,7 +72,7 @@ func TestIndexRT(t *testing.T) { t.Fatalf("failed get: %v", err) } - idx, err := Restore(carFile) + idx, err := index.Restore(carFile) if err != nil { t.Fatalf("failed restore: %v", err) } diff --git a/ipld/car/v2/carbs/testdata/test.car b/ipld/car/v2/blockstore/testdata/test.car similarity index 100% rename from ipld/car/v2/carbs/testdata/test.car rename to ipld/car/v2/blockstore/testdata/test.car diff --git a/ipld/car/v2/car.go b/ipld/car/v2/car.go index 99c5fa7d9..8dc30a8a4 100644 --- a/ipld/car/v2/car.go +++ b/ipld/car/v2/car.go @@ -12,7 +12,6 @@ const ( HeaderSize = 40 // CharacteristicsSize is the fixed size of Characteristics bitfield within CAR v2 header in number of bytes. CharacteristicsSize = 16 - uint64Size = 8 ) var ( @@ -45,17 +44,13 @@ type ( } ) -// WriteTo writes this characteristics to the given writer. +// WriteTo writes this characteristics to the given w. func (c Characteristics) WriteTo(w io.Writer) (n int64, err error) { - if err = binary.Write(w, binary.LittleEndian, c.Hi); err != nil { - return - } - n += uint64Size - if err = binary.Write(w, binary.LittleEndian, c.Lo); err != nil { - return - } - n += uint64Size - return + buf := make([]byte, 16) + binary.LittleEndian.PutUint64(buf[:8], c.Hi) + binary.LittleEndian.PutUint64(buf[8:], c.Lo) + written, err := w.Write(buf) + return int64(written), err } func (c *Characteristics) ReadFrom(r io.Reader) (int64, error) { @@ -65,8 +60,8 @@ func (c *Characteristics) ReadFrom(r io.Reader) (int64, error) { if err != nil { return n, err } - c.Hi = binary.LittleEndian.Uint64(buf[:uint64Size]) - c.Lo = binary.LittleEndian.Uint64(buf[uint64Size:]) + c.Hi = binary.LittleEndian.Uint64(buf[:8]) + c.Lo = binary.LittleEndian.Uint64(buf[8:]) return n, nil } @@ -101,25 +96,18 @@ func (h Header) WithCarV1Padding(padding uint64) Header { // WriteTo serializes this header as bytes and writes them using the given io.Writer. func (h Header) WriteTo(w io.Writer) (n int64, err error) { - // TODO optimize write by encoding all bytes in a slice and writing once. wn, err := h.Characteristics.WriteTo(w) - if err != nil { - return - } n += wn - if err = binary.Write(w, binary.LittleEndian, h.CarV1Offset); err != nil { - return - } - n += uint64Size - if err = binary.Write(w, binary.LittleEndian, h.CarV1Size); err != nil { - return - } - n += uint64Size - if err = binary.Write(w, binary.LittleEndian, h.IndexOffset); err != nil { + if err != nil { return } - n += uint64Size - return + buf := make([]byte, 24) + binary.LittleEndian.PutUint64(buf[:8], h.CarV1Offset) + binary.LittleEndian.PutUint64(buf[8:16], h.CarV1Size) + binary.LittleEndian.PutUint64(buf[16:], h.IndexOffset) + written, err := w.Write(buf) + n += int64(written) + return n, err } // ReadFrom populates fields of this header from the given r. @@ -128,16 +116,14 @@ func (h *Header) ReadFrom(r io.Reader) (int64, error) { if err != nil { return n, err } - remainingSize := HeaderSize - CharacteristicsSize - buf := make([]byte, remainingSize) + buf := make([]byte, 24) read, err := io.ReadFull(r, buf) n += int64(read) if err != nil { return n, err } - carV1RelOffset := uint64Size * 2 - h.CarV1Offset = binary.LittleEndian.Uint64(buf[:uint64Size]) - h.CarV1Size = binary.LittleEndian.Uint64(buf[uint64Size:carV1RelOffset]) - h.IndexOffset = binary.LittleEndian.Uint64(buf[carV1RelOffset:]) + h.CarV1Offset = binary.LittleEndian.Uint64(buf[:8]) + h.CarV1Size = binary.LittleEndian.Uint64(buf[8:16]) + h.IndexOffset = binary.LittleEndian.Uint64(buf[16:]) return n, nil } diff --git a/ipld/car/v2/carbon/reader.go b/ipld/car/v2/carbon/reader.go deleted file mode 100644 index 4e3755491..000000000 --- a/ipld/car/v2/carbon/reader.go +++ /dev/null @@ -1,23 +0,0 @@ -package carbon - -import "io" - -//lint:ignore U1000 The entire carbon package will be reviewed; this is temporary -type unatreader struct { - io.ReaderAt - at int64 -} - -//lint:ignore U1000 The entire carbon package will be reviewed; this is temporary -func (u *unatreader) Read(p []byte) (n int, err error) { - n, err = u.ReadAt(p, u.at) - u.at = u.at + int64(n) - return -} - -//lint:ignore U1000 The entire carbon package will be reviewed; this is temporary -func (u *unatreader) ReadByte() (byte, error) { - b := []byte{0} - _, err := u.Read(b) - return b[0], err -} diff --git a/ipld/car/v2/carbs/carbs.go b/ipld/car/v2/carbs/carbs.go deleted file mode 100644 index 6891d925d..000000000 --- a/ipld/car/v2/carbs/carbs.go +++ /dev/null @@ -1,301 +0,0 @@ -package carbs - -import ( - "bufio" - "bytes" - "context" - "encoding/binary" - "fmt" - "io" - - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" - blockstore "github.com/ipfs/go-ipfs-blockstore" - "github.com/multiformats/go-multihash" - - pb "github.com/cheggaaa/pb/v3" - carv1 "github.com/ipld/go-car" - "github.com/ipld/go-car/util" - "golang.org/x/exp/mmap" -) - -var errNotFound = blockstore.ErrNotFound - -// BlockStore provides a read-only Car Block Store. -type BlockStore struct { - backing io.ReaderAt - idx Index -} - -var _ blockstore.Blockstore = (*BlockStore)(nil) - -func (b *BlockStore) Read(idx int64) (cid.Cid, []byte, error) { - bcid, data, err := util.ReadNode(bufio.NewReader(&unatreader{b.backing, idx})) - return bcid, data, err -} - -// DeleteBlock doesn't delete a block on RO blockstore -func (b *BlockStore) DeleteBlock(_ cid.Cid) error { - return fmt.Errorf("read only") -} - -// Has indicates if the store has a cid -func (b *BlockStore) Has(key cid.Cid) (bool, error) { - offset, err := b.idx.Get(key) - if err != nil { - return false, err - } - uar := unatreader{b.backing, int64(offset)} - _, err = binary.ReadUvarint(&uar) - if err != nil { - return false, err - } - cid, _, err := readCid(b.backing, uar.at) - if err != nil { - return false, err - } - return cid.Equals(key), nil -} - -var cidv0Pref = []byte{0x12, 0x20} - -func readCid(store io.ReaderAt, at int64) (cid.Cid, int, error) { - var tag [2]byte - if _, err := store.ReadAt(tag[:], at); err != nil { - return cid.Undef, 0, err - } - if bytes.Equal(tag[:], cidv0Pref) { - cid0 := make([]byte, 34) - if _, err := store.ReadAt(cid0, at); err != nil { - return cid.Undef, 0, err - } - c, err := cid.Cast(cid0) - return c, 34, err - } - - // assume cidv1 - br := &unatreader{store, at} - vers, err := binary.ReadUvarint(br) - if err != nil { - return cid.Cid{}, 0, err - } - - // TODO: the go-cid package allows version 0 here as well - if vers != 1 { - return cid.Cid{}, 0, fmt.Errorf("invalid cid version number: %d", vers) - } - - codec, err := binary.ReadUvarint(br) - if err != nil { - return cid.Cid{}, 0, err - } - - mhr := multihash.NewReader(br) - h, err := mhr.ReadMultihash() - if err != nil { - return cid.Cid{}, 0, err - } - - return cid.NewCidV1(codec, h), int(br.at - at), nil -} - -// Get gets a block from the store -func (b *BlockStore) Get(key cid.Cid) (blocks.Block, error) { - offset, err := b.idx.Get(key) - if err != nil { - return nil, err - } - entry, bytes, err := b.Read(int64(offset)) - if err != nil { - // TODO replace with logging - fmt.Printf("failed get %d:%v\n", offset, err) - return nil, blockstore.ErrNotFound - } - if !entry.Equals(key) { - return nil, blockstore.ErrNotFound - } - return blocks.NewBlockWithCid(bytes, key) -} - -// GetSize gets how big a item is -func (b *BlockStore) GetSize(key cid.Cid) (int, error) { - idx, err := b.idx.Get(key) - if err != nil { - return -1, err - } - len, err := binary.ReadUvarint(&unatreader{b.backing, int64(idx)}) - if err != nil { - return -1, blockstore.ErrNotFound - } - cid, _, err := readCid(b.backing, int64(idx+len)) - if err != nil { - return 0, err - } - if !cid.Equals(key) { - return -1, blockstore.ErrNotFound - } - // get cid. validate. - return int(len), err -} - -// Put does nothing on a ro store -func (b *BlockStore) Put(blocks.Block) error { - return fmt.Errorf("read only") -} - -// PutMany does nothing on a ro store -func (b *BlockStore) PutMany([]blocks.Block) error { - return fmt.Errorf("read only") -} - -// AllKeysChan returns the list of keys in the store -func (b *BlockStore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { - header, err := carv1.ReadHeader(bufio.NewReader(&unatreader{b.backing, 0})) - if err != nil { - return nil, fmt.Errorf("error reading car header: %w", err) - } - offset, err := carv1.HeaderSize(header) - if err != nil { - return nil, err - } - - ch := make(chan cid.Cid, 5) - go func() { - done := ctx.Done() - - rdr := unatreader{b.backing, int64(offset)} - for { - l, err := binary.ReadUvarint(&rdr) - thisItemForNxt := rdr.at - if err != nil { - return - } - c, _, err := readCid(b.backing, thisItemForNxt) - if err != nil { - return - } - rdr.at = thisItemForNxt + int64(l) - - select { - case ch <- c: - continue - case <-done: - return - } - } - }() - return ch, nil -} - -// HashOnRead does nothing -func (b *BlockStore) HashOnRead(bool) { -} - -// Roots returns the root CIDs of the backing car -func (b *BlockStore) Roots() ([]cid.Cid, error) { - header, err := carv1.ReadHeader(bufio.NewReader(&unatreader{b.backing, 0})) - if err != nil { - return nil, fmt.Errorf("error reading car header: %w", err) - } - return header.Roots, nil -} - -// Load opens a carbs data store, generating an index if it does not exist -func Load(path string, noPersist bool) (*BlockStore, error) { - reader, err := mmap.Open(path) - if err != nil { - return nil, err - } - idx, err := Restore(path) - if err != nil { - idx, err = GenerateIndex(reader, 0, IndexSorted, false) - if err != nil { - return nil, err - } - if !noPersist { - if err = Save(idx, path); err != nil { - return nil, err - } - } - } - obj := BlockStore{ - backing: reader, - idx: idx, - } - return &obj, nil -} - -// Of opens a carbs data store from an existing reader of the base data and index -func Of(backing io.ReaderAt, index Index) *BlockStore { - return &BlockStore{backing, index} -} - -// GenerateIndex provides a low-level interface to create an index over a -// reader to a car stream. -func GenerateIndex(store io.ReaderAt, size int64, codec IndexCodec, verbose bool) (Index, error) { - indexcls, ok := IndexAtlas[codec] - if !ok { - return nil, fmt.Errorf("unknown codec: %#v", codec) - } - - bar := pb.New64(size) - bar.Set(pb.Bytes, true) - bar.Set(pb.Terminal, true) - - bar.Start() - - header, err := carv1.ReadHeader(bufio.NewReader(&unatreader{store, 0})) - if err != nil { - return nil, fmt.Errorf("error reading car header: %w", err) - } - offset, err := carv1.HeaderSize(header) - if err != nil { - return nil, err - } - bar.Add64(int64(offset)) - - index := indexcls() - - records := make([]Record, 0) - rdr := unatreader{store, int64(offset)} - for { - thisItemIdx := rdr.at - l, err := binary.ReadUvarint(&rdr) - bar.Add64(int64(l)) - thisItemForNxt := rdr.at - if err != nil { - if err == io.EOF { - break - } - return nil, err - } - c, _, err := readCid(store, thisItemForNxt) - if err != nil { - return nil, err - } - records = append(records, Record{c, uint64(thisItemIdx)}) - rdr.at = thisItemForNxt + int64(l) - } - - if err := index.Load(records); err != nil { - return nil, err - } - - bar.Finish() - - return index, nil -} - -// Generate walks a car file and generates an index of cid->byte offset in it. -func Generate(path string, codec IndexCodec) error { - store, err := mmap.Open(path) - if err != nil { - return err - } - idx, err := GenerateIndex(store, 0, codec, false) - if err != nil { - return err - } - - return Save(idx, path) -} diff --git a/ipld/car/v2/carbs/reader.go b/ipld/car/v2/carbs/reader.go deleted file mode 100644 index 8accf0a84..000000000 --- a/ipld/car/v2/carbs/reader.go +++ /dev/null @@ -1,20 +0,0 @@ -package carbs - -import "io" - -type unatreader struct { - io.ReaderAt - at int64 -} - -func (u *unatreader) Read(p []byte) (n int, err error) { - n, err = u.ReadAt(p, u.at) - u.at = u.at + int64(n) - return -} - -func (u *unatreader) ReadByte() (byte, error) { - b := []byte{0} - _, err := u.Read(b) - return b[0], err -} diff --git a/ipld/car/v2/carbon/carbon.go b/ipld/car/v2/internal/carbon/carbon.go similarity index 73% rename from ipld/car/v2/carbon/carbon.go rename to ipld/car/v2/internal/carbon/carbon.go index 3ba853208..4394f7887 100644 --- a/ipld/car/v2/carbon/carbon.go +++ b/ipld/car/v2/internal/carbon/carbon.go @@ -3,12 +3,13 @@ package carbon import ( "errors" "fmt" - "github.com/ipld/go-car/v2/carbs" + carblockstore "github.com/ipld/go-car/v2/blockstore" + "github.com/ipld/go-car/v2/internal/index" "os" "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" - "github.com/ipld/go-car" + carv1 "github.com/ipld/go-car" ) // Carbon is a carbs-index-compatible blockstore supporting appending additional blocks @@ -21,9 +22,6 @@ type Carbon interface { // errUnsupported is returned for unsupported blockstore operations (like delete) var errUnsupported = errors.New("unsupported by carbon") -// errNotFound is returned for lookups to entries that don't exist -var errNotFound = errors.New("not found") - // New creates a new Carbon blockstore func New(path string) (Carbon, error) { return NewWithRoots(path, []cid.Cid{}) @@ -40,21 +38,26 @@ func NewWithRoots(path string, roots []cid.Cid) (Carbon, error) { return nil, fmt.Errorf("could not re-open read handle: %w", err) } - hdr := car.CarHeader{ + hdr := carv1.CarHeader{ Roots: roots, Version: 1, } writer := poswriter{wfd, 0} - if err := car.WriteHeader(&hdr, &writer); err != nil { + if err := carv1.WriteHeader(&hdr, &writer); err != nil { return nil, fmt.Errorf("couldn't write car header: %w", err) } - idx := insertionIndex{} + indexcls, ok := index.IndexAtlas[index.IndexInsertion] + if !ok { + return nil, fmt.Errorf("unknownindex codec: %#v", index.IndexInsertion) + } + + idx := (indexcls()).(*index.InsertionIndex) f := carbonFD{ path, &writer, - *carbs.Of(rfd, &idx), - &idx, + *carblockstore.ReadOnlyOf(rfd, idx), + idx, } return &f, nil } diff --git a/ipld/car/v2/carbon/carbon_fds.go b/ipld/car/v2/internal/carbon/carbon_fds.go similarity index 86% rename from ipld/car/v2/carbon/carbon_fds.go rename to ipld/car/v2/internal/carbon/carbon_fds.go index 856eb9bb3..000379a83 100644 --- a/ipld/car/v2/carbon/carbon_fds.go +++ b/ipld/car/v2/internal/carbon/carbon_fds.go @@ -1,7 +1,8 @@ package carbon import ( - "github.com/ipld/go-car/v2/carbs" + "github.com/ipld/go-car/v2/blockstore" + "github.com/ipld/go-car/v2/internal/index" "os" blocks "github.com/ipfs/go-block-format" @@ -14,8 +15,8 @@ import ( type carbonFD struct { path string writeHandle *poswriter - carbs.BlockStore - idx *insertionIndex + blockstore.ReadOnly + idx *index.InsertionIndex } var _ (Carbon) = (*carbonFD)(nil) @@ -37,7 +38,7 @@ func (c *carbonFD) PutMany(b []blocks.Block) error { if err := util.LdWrite(c.writeHandle, bl.Cid().Bytes(), bl.RawData()); err != nil { return err } - c.idx.items.InsertNoReplace(mkRecordFromCid(bl.Cid(), n)) + c.idx.InsertNoReplace(bl.Cid(), n) } return nil } @@ -54,10 +55,10 @@ func (c *carbonFD) Finish() error { return err } } - return carbs.Save(fi, c.path) + return index.Save(fi, c.path) } // Checkpoint serializes the carbon index so that the partially written blockstore can be resumed. func (c *carbonFD) Checkpoint() error { - return carbs.Save(c.idx, c.path) + return index.Save(c.idx, c.path) } diff --git a/ipld/car/v2/carbon/carbon_test.go b/ipld/car/v2/internal/carbon/carbon_test.go similarity index 91% rename from ipld/car/v2/carbon/carbon_test.go rename to ipld/car/v2/internal/carbon/carbon_test.go index a3c7d6d98..a7cc3de40 100644 --- a/ipld/car/v2/carbon/carbon_test.go +++ b/ipld/car/v2/internal/carbon/carbon_test.go @@ -1,8 +1,8 @@ package carbon_test import ( - "github.com/ipld/go-car/v2/carbon" - "github.com/ipld/go-car/v2/carbs" + "github.com/ipld/go-car/v2/blockstore" + "github.com/ipld/go-car/v2/internal/carbon" "io" "math/rand" "os" @@ -75,7 +75,7 @@ func TestCarbon(t *testing.T) { t.Fatalf("index not written: %v", stat) } - carb, err := carbs.Load("testcarbon.car", true) + carb, err := blockstore.LoadReadOnly("testcarbon.car", true) if err != nil { t.Fatal(err) } diff --git a/ipld/car/v2/carbon/doc.go b/ipld/car/v2/internal/carbon/doc.go similarity index 92% rename from ipld/car/v2/carbon/doc.go rename to ipld/car/v2/internal/carbon/doc.go index a1ce3cc6a..e620f4d00 100644 --- a/ipld/car/v2/carbon/doc.go +++ b/ipld/car/v2/internal/carbon/doc.go @@ -2,4 +2,5 @@ // A carbs index for the resulting file is tracked and can be saved as needed. // Note, carbon does not support deletion. // See: https://github.com/ipfs/go-ipfs-blockstore +// TODO to be refactored package carbon diff --git a/ipld/car/v2/carbon/poswriter.go b/ipld/car/v2/internal/carbon/poswriter.go similarity index 100% rename from ipld/car/v2/carbon/poswriter.go rename to ipld/car/v2/internal/carbon/poswriter.go diff --git a/ipld/car/v2/carbs/doc.go b/ipld/car/v2/internal/carbs/doc.go similarity index 81% rename from ipld/car/v2/carbs/doc.go rename to ipld/car/v2/internal/carbs/doc.go index bcff12ac3..099875379 100644 --- a/ipld/car/v2/carbs/doc.go +++ b/ipld/car/v2/internal/carbs/doc.go @@ -1,2 +1,3 @@ // Package carbs provides a read-only blockstore interface directly reading out of a car file. +// TODO to be refactored package carbs diff --git a/ipld/car/v2/carbs/util/hydrate.go b/ipld/car/v2/internal/carbs/util/hydrate.go similarity index 73% rename from ipld/car/v2/carbs/util/hydrate.go rename to ipld/car/v2/internal/carbs/util/hydrate.go index d0fb50ae1..e0a39f930 100644 --- a/ipld/car/v2/carbs/util/hydrate.go +++ b/ipld/car/v2/internal/carbs/util/hydrate.go @@ -2,10 +2,9 @@ package main import ( "fmt" - "os" - - "github.com/ipld/go-car/v2/carbs" + "github.com/ipld/go-car/v2/internal/index" "golang.org/x/exp/mmap" + "os" ) func main() { @@ -14,12 +13,12 @@ func main() { return } db := os.Args[1] - codec := carbs.IndexSorted + codec := index.IndexSorted if len(os.Args) == 3 { if os.Args[2] == "Hash" { - codec = carbs.IndexHashed + codec = index.IndexHashed } else if os.Args[2] == "GobHash" { - codec = carbs.IndexGobHashed + codec = index.IndexGobHashed } } @@ -35,7 +34,7 @@ func main() { return } - idx, err := carbs.GenerateIndex(dbBacking, dbstat.Size(), codec, true) + idx, err := index.GenerateIndex(dbBacking, dbstat.Size(), codec) if err != nil { fmt.Printf("Error generating index: %v\n", err) return @@ -43,7 +42,7 @@ func main() { fmt.Printf("Saving...\n") - if err := carbs.Save(idx, db); err != nil { + if err := index.Save(idx, db); err != nil { fmt.Printf("Error saving : %v\n", err) } } diff --git a/ipld/car/v2/internal/index/errors.go b/ipld/car/v2/internal/index/errors.go new file mode 100644 index 000000000..c45c24020 --- /dev/null +++ b/ipld/car/v2/internal/index/errors.go @@ -0,0 +1,9 @@ +package index + +import "errors" + +var ( + // errNotFound is returned for lookups to entries that don't exist + errNotFound = errors.New("not found") + errUnsupported = errors.New("not supported") +) diff --git a/ipld/car/v2/internal/index/generator.go b/ipld/car/v2/internal/index/generator.go new file mode 100644 index 000000000..18e25edfb --- /dev/null +++ b/ipld/car/v2/internal/index/generator.go @@ -0,0 +1,81 @@ +package index + +import ( + "bufio" + "encoding/binary" + "fmt" + "github.com/cheggaaa/pb/v3" + carv1 "github.com/ipld/go-car" + internalio "github.com/ipld/go-car/v2/internal/io" + "golang.org/x/exp/mmap" + "io" +) + +// GenerateIndex provides a low-level interface to create an index over a +// reader to a car stream. +func GenerateIndex(store io.ReaderAt, size int64, codec Codec) (Index, error) { + indexcls, ok := IndexAtlas[codec] + if !ok { + return nil, fmt.Errorf("unknown codec: %#v", codec) + } + + bar := pb.New64(size) + bar.Set(pb.Bytes, true) + bar.Set(pb.Terminal, true) + + bar.Start() + + header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReader(store, 0))) + if err != nil { + return nil, fmt.Errorf("error reading car header: %w", err) + } + offset, err := carv1.HeaderSize(header) + if err != nil { + return nil, err + } + bar.Add64(int64(offset)) + + idx := indexcls() + + records := make([]Record, 0) + rdr := internalio.NewOffsetReader(store, int64(offset)) + for { + thisItemIdx := rdr.Offset() + l, err := binary.ReadUvarint(rdr) + bar.Add64(int64(l)) + thisItemForNxt := rdr.Offset() + if err != nil { + if err == io.EOF { + break + } + return nil, err + } + c, _, err := internalio.ReadCid(store, thisItemForNxt) + if err != nil { + return nil, err + } + records = append(records, Record{c, uint64(thisItemIdx)}) + rdr.SeekOffset(thisItemForNxt + int64(l)) + } + + if err := idx.Load(records); err != nil { + return nil, err + } + + bar.Finish() + + return idx, nil +} + +// Generate walks a car file and generates an index of cid->byte offset in it. +func Generate(path string, codec Codec) error { + store, err := mmap.Open(path) + if err != nil { + return err + } + idx, err := GenerateIndex(store, 0, codec) + if err != nil { + return err + } + return Save(idx, path) +} diff --git a/ipld/car/v2/carbs/index.go b/ipld/car/v2/internal/index/index.go similarity index 55% rename from ipld/car/v2/carbs/index.go rename to ipld/car/v2/internal/index/index.go index 1f381308c..c0afdf309 100644 --- a/ipld/car/v2/carbs/index.go +++ b/ipld/car/v2/internal/index/index.go @@ -1,50 +1,54 @@ -package carbs +package index import ( "encoding/binary" "fmt" - "io" - "os" - "github.com/ipfs/go-cid" + internalio "github.com/ipld/go-car/v2/internal/io" "golang.org/x/exp/mmap" + "io" + "os" ) -// IndexCodec is used as a multicodec identifier for carbs index files -type IndexCodec int - -// IndexCodec table is a first var-int in carbs indexes +// Codec table is a first var-int in carbs indexes const ( - IndexHashed IndexCodec = iota + 0x300000 + IndexHashed Codec = iota + 0x300000 IndexSorted IndexSingleSorted IndexGobHashed + IndexInsertion ) -// IndexCls is a constructor for an index type -type IndexCls func() Index +type ( + // Codec is used as a multicodec identifier for carbs index files + Codec int + + // IndexCls is a constructor for an index type + IndexCls func() Index + + // Record is a pre-processed record of a car item and location. + Record struct { + cid.Cid + Idx uint64 + } + + // Index provides an interface for figuring out where in the car a given cid begins + Index interface { + Codec() Codec + Marshal(w io.Writer) error + Unmarshal(r io.Reader) error + Get(cid.Cid) (uint64, error) + Load([]Record) error + } +) // IndexAtlas holds known index formats -var IndexAtlas = map[IndexCodec]IndexCls{ +var IndexAtlas = map[Codec]IndexCls{ IndexHashed: mkHashed, IndexSorted: mkSorted, IndexSingleSorted: mkSingleSorted, IndexGobHashed: mkGobHashed, -} - -// Record is a pre-processed record of a car item and location. -type Record struct { - cid.Cid - Idx uint64 -} - -// Index provides an interface for figuring out where in the car a given cid begins -type Index interface { - Codec() IndexCodec - Marshal(w io.Writer) error - Unmarshal(r io.Reader) error - Get(cid.Cid) (uint64, error) - Load([]Record) error + IndexInsertion: mkInsertion, } // Save writes a generated index for a car at `path` @@ -71,17 +75,17 @@ func Restore(path string) (Index, error) { } defer reader.Close() - uar := unatreader{reader, 0} - codec, err := binary.ReadUvarint(&uar) + uar := internalio.NewOffsetReader(reader, 0) + codec, err := binary.ReadUvarint(uar) if err != nil { return nil, err } - idx, ok := IndexAtlas[IndexCodec(codec)] + idx, ok := IndexAtlas[Codec(codec)] if !ok { return nil, fmt.Errorf("unknown codec: %d", codec) } idxInst := idx() - if err := idxInst.Unmarshal(&uar); err != nil { + if err := idxInst.Unmarshal(uar); err != nil { return nil, err } diff --git a/ipld/car/v2/carbs/indexgobhash.go b/ipld/car/v2/internal/index/indexgobhash.go similarity index 91% rename from ipld/car/v2/carbs/indexgobhash.go rename to ipld/car/v2/internal/index/indexgobhash.go index b7dedba0a..ce2768a9f 100644 --- a/ipld/car/v2/carbs/indexgobhash.go +++ b/ipld/car/v2/internal/index/indexgobhash.go @@ -1,4 +1,4 @@ -package carbs +package index import ( "encoding/gob" @@ -27,7 +27,7 @@ func (m *mapGobIndex) Unmarshal(r io.Reader) error { return d.Decode(m) } -func (m *mapGobIndex) Codec() IndexCodec { +func (m *mapGobIndex) Codec() Codec { return IndexHashed } diff --git a/ipld/car/v2/carbs/indexhashed.go b/ipld/car/v2/internal/index/indexhashed.go similarity index 91% rename from ipld/car/v2/carbs/indexhashed.go rename to ipld/car/v2/internal/index/indexhashed.go index f4c04aed0..c64e5cd84 100644 --- a/ipld/car/v2/carbs/indexhashed.go +++ b/ipld/car/v2/internal/index/indexhashed.go @@ -1,4 +1,4 @@ -package carbs +package index import ( "io" @@ -26,7 +26,7 @@ func (m *mapIndex) Unmarshal(r io.Reader) error { return d.Decode(m) } -func (m *mapIndex) Codec() IndexCodec { +func (m *mapIndex) Codec() Codec { return IndexHashed } diff --git a/ipld/car/v2/carbs/indexsorted.go b/ipld/car/v2/internal/index/indexsorted.go similarity index 90% rename from ipld/car/v2/carbs/indexsorted.go rename to ipld/car/v2/internal/index/indexsorted.go index eca593c9f..b14334d2c 100644 --- a/ipld/car/v2/carbs/indexsorted.go +++ b/ipld/car/v2/internal/index/indexsorted.go @@ -1,4 +1,4 @@ -package carbs +package index import ( "bytes" @@ -11,18 +11,25 @@ import ( "github.com/multiformats/go-multihash" ) -type digestRecord struct { - digest []byte - index uint64 -} +type ( + digestRecord struct { + digest []byte + index uint64 + } + recordSet []digestRecord + singleWidthIndex struct { + width int32 + len int64 // in struct, len is #items. when marshaled, it's saved as #bytes. + index []byte + } + multiWidthIndex map[int32]singleWidthIndex +) func (d digestRecord) write(buf []byte) { n := copy(buf[:], d.digest) binary.LittleEndian.PutUint64(buf[n:], d.index) } -type recordSet []digestRecord - func (r recordSet) Len() int { return len(r) } @@ -35,13 +42,7 @@ func (r recordSet) Swap(i, j int) { r[i], r[j] = r[j], r[i] } -type singleWidthIndex struct { - width int32 - len int64 // in struct, len is #items. when marshaled, it's saved as #bytes. - index []byte -} - -func (s *singleWidthIndex) Codec() IndexCodec { +func (s *singleWidthIndex) Codec() Codec { return IndexSingleSorted } @@ -111,8 +112,6 @@ func (s *singleWidthIndex) Load(items []Record) error { return nil } -type multiWidthIndex map[int32]singleWidthIndex - func (m *multiWidthIndex) Get(c cid.Cid) (uint64, error) { d, err := multihash.Decode(c.Hash()) if err != nil { @@ -124,7 +123,7 @@ func (m *multiWidthIndex) Get(c cid.Cid) (uint64, error) { return 0, errNotFound } -func (m *multiWidthIndex) Codec() IndexCodec { +func (m *multiWidthIndex) Codec() Codec { return IndexSorted } diff --git a/ipld/car/v2/carbon/insertionindex.go b/ipld/car/v2/internal/index/insertionindex.go similarity index 57% rename from ipld/car/v2/carbon/insertionindex.go rename to ipld/car/v2/internal/index/insertionindex.go index 2e5d943a3..d64be077f 100644 --- a/ipld/car/v2/carbon/insertionindex.go +++ b/ipld/car/v2/internal/index/insertionindex.go @@ -1,10 +1,9 @@ -package carbon +package index import ( "bytes" "encoding/binary" "fmt" - "github.com/ipld/go-car/v2/carbs" "io" "github.com/ipfs/go-cid" @@ -13,60 +12,56 @@ import ( cbor "github.com/whyrusleeping/cbor/go" ) -// IndexInsertion carbs IndexCodec identifier -const IndexInsertion carbs.IndexCodec = 0x300010 - -// init hook to register the index format -func init() { - carbs.IndexAtlas[IndexInsertion] = mkInsertion +type InsertionIndex struct { + items llrb.LLRB } -type insertionIndex struct { - items llrb.LLRB +func (ii *InsertionIndex) InsertNoReplace(key cid.Cid, n uint64) { + ii.items.InsertNoReplace(mkRecordFromCid(key, n)) } -type record struct { +type recordDigest struct { digest []byte - carbs.Record + Record } -func (r record) Less(than llrb.Item) bool { - other, ok := than.(record) +func (r recordDigest) Less(than llrb.Item) bool { + other, ok := than.(recordDigest) if !ok { return false } return bytes.Compare(r.digest, other.digest) < 0 } -func mkRecord(r carbs.Record) record { +func mkRecord(r Record) recordDigest { d, err := multihash.Decode(r.Hash()) if err != nil { - return record{} + return recordDigest{} } - return record{d.Digest, r} + return recordDigest{d.Digest, r} } -func mkRecordFromCid(c cid.Cid, at uint64) record { +func mkRecordFromCid(c cid.Cid, at uint64) recordDigest { d, err := multihash.Decode(c.Hash()) if err != nil { - return record{} + return recordDigest{} } - return record{d.Digest, carbs.Record{Cid: c, Idx: at}} + return recordDigest{d.Digest, Record{Cid: c, Idx: at}} } -func (ii *insertionIndex) Get(c cid.Cid) (uint64, error) { +func (ii *InsertionIndex) Get(c cid.Cid) (uint64, error) { d, err := multihash.Decode(c.Hash()) if err != nil { return 0, err } - entry := record{digest: d.Digest} + entry := recordDigest{digest: d.Digest} e := ii.items.Get(entry) if e == nil { return 0, errNotFound } - r, ok := e.(record) + r, ok := e.(recordDigest) if !ok { return 0, errUnsupported } @@ -74,7 +69,7 @@ func (ii *insertionIndex) Get(c cid.Cid) (uint64, error) { return r.Record.Idx, nil } -func (ii *insertionIndex) Marshal(w io.Writer) error { +func (ii *InsertionIndex) Marshal(w io.Writer) error { if err := binary.Write(w, binary.LittleEndian, int64(ii.items.Len())); err != nil { return err } @@ -82,7 +77,7 @@ func (ii *insertionIndex) Marshal(w io.Writer) error { var err error iter := func(i llrb.Item) bool { - if err = cbor.Encode(w, i.(record).Record); err != nil { + if err = cbor.Encode(w, i.(recordDigest).Record); err != nil { return false } return true @@ -91,14 +86,14 @@ func (ii *insertionIndex) Marshal(w io.Writer) error { return err } -func (ii *insertionIndex) Unmarshal(r io.Reader) error { +func (ii *InsertionIndex) Unmarshal(r io.Reader) error { var len int64 if err := binary.Read(r, binary.LittleEndian, &len); err != nil { return err } d := cbor.NewDecoder(r) for i := int64(0); i < len; i++ { - var rec carbs.Record + var rec Record if err := d.Decode(&rec); err != nil { return err } @@ -108,11 +103,11 @@ func (ii *insertionIndex) Unmarshal(r io.Reader) error { } // Codec identifies this index format -func (ii *insertionIndex) Codec() carbs.IndexCodec { +func (ii *InsertionIndex) Codec() Codec { return IndexInsertion } -func (ii *insertionIndex) Load(rs []carbs.Record) error { +func (ii *InsertionIndex) Load(rs []Record) error { for _, r := range rs { rec := mkRecord(r) if rec.digest == nil { @@ -123,19 +118,19 @@ func (ii *insertionIndex) Load(rs []carbs.Record) error { return nil } -func mkInsertion() carbs.Index { - ii := insertionIndex{} +func mkInsertion() Index { + ii := InsertionIndex{} return &ii } // Flatten returns a 'indexsorted' formatted index for more efficient subsequent loading -func (ii *insertionIndex) Flatten() (carbs.Index, error) { - si := carbs.IndexAtlas[carbs.IndexSorted]() - rcrds := make([]carbs.Record, ii.items.Len()) +func (ii *InsertionIndex) Flatten() (Index, error) { + si := IndexAtlas[IndexSorted]() + rcrds := make([]Record, ii.items.Len()) idx := 0 iter := func(i llrb.Item) bool { - rcrds[idx] = i.(record).Record + rcrds[idx] = i.(recordDigest).Record idx++ return true } diff --git a/ipld/car/v2/internal/io/cid.go b/ipld/car/v2/internal/io/cid.go new file mode 100644 index 000000000..932b63bec --- /dev/null +++ b/ipld/car/v2/internal/io/cid.go @@ -0,0 +1,52 @@ +package io + +import ( + "bytes" + "encoding/binary" + "fmt" + "github.com/ipfs/go-cid" + "github.com/multiformats/go-multihash" + "io" +) + +var cidv0Pref = []byte{0x12, 0x20} + +func ReadCid(store io.ReaderAt, at int64) (cid.Cid, int, error) { + var tag [2]byte + if _, err := store.ReadAt(tag[:], at); err != nil { + return cid.Undef, 0, err + } + if bytes.Equal(tag[:], cidv0Pref) { + cid0 := make([]byte, 34) + if _, err := store.ReadAt(cid0, at); err != nil { + return cid.Undef, 0, err + } + c, err := cid.Cast(cid0) + return c, 34, err + } + + // assume cidv1 + br := NewOffsetReader(store, at) + vers, err := binary.ReadUvarint(br) + if err != nil { + return cid.Cid{}, 0, err + } + + // TODO: the go-cid package allows version 0 here as well + if vers != 1 { + return cid.Cid{}, 0, fmt.Errorf("invalid cid version number: %d", vers) + } + + codec, err := binary.ReadUvarint(br) + if err != nil { + return cid.Cid{}, 0, err + } + + mhr := multihash.NewReader(br) + h, err := mhr.ReadMultihash() + if err != nil { + return cid.Cid{}, 0, err + } + + return cid.NewCidV1(codec, h), int(br.Offset() - at), nil +} diff --git a/ipld/car/v2/offset_reader.go b/ipld/car/v2/internal/io/offset_reader.go similarity index 71% rename from ipld/car/v2/offset_reader.go rename to ipld/car/v2/internal/io/offset_reader.go index c0f09230f..03f7e547f 100644 --- a/ipld/car/v2/offset_reader.go +++ b/ipld/car/v2/internal/io/offset_reader.go @@ -1,4 +1,4 @@ -package car +package io import "io" @@ -15,7 +15,7 @@ type OffsetReader struct { } // NewOffsetReader returns an OffsetReader that reads from r -// starting at offset off and stops with io.EOF when r reaches its end. +// starting offset offset off and stops with io.EOF when r reaches its end. func NewOffsetReader(r io.ReaderAt, off int64) *OffsetReader { return &OffsetReader{r, off, off} } @@ -33,3 +33,17 @@ func (o *OffsetReader) ReadAt(p []byte, off int64) (n int, err error) { off += o.base return o.r.ReadAt(p, off) } + +func (o *OffsetReader) ReadByte() (byte, error) { + b := []byte{0} + _, err := o.Read(b) + return b[0], err +} + +func (o *OffsetReader) Offset() int64 { + return o.off +} + +func (o *OffsetReader) SeekOffset(off int64) { + o.off = off +} diff --git a/ipld/car/v2/internal/io/offset_writer.go b/ipld/car/v2/internal/io/offset_writer.go new file mode 100644 index 000000000..495190a17 --- /dev/null +++ b/ipld/car/v2/internal/io/offset_writer.go @@ -0,0 +1,16 @@ +package io + +import "io" + +var _ io.Writer = (*offsetWriter)(nil) + +type offsetWriter struct { + wa io.WriterAt + offset int64 +} + +func (ow *offsetWriter) Write(b []byte) (n int, err error) { + n, err = ow.wa.WriteAt(b, ow.offset) + ow.offset += int64(n) + return +} diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index b3c729e85..da8be152a 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -3,6 +3,7 @@ package car import ( "bufio" "fmt" + internalio "github.com/ipld/go-car/v2/internal/io" "io" carv1 "github.com/ipld/go-car" @@ -57,5 +58,5 @@ func (r *Reader) CarV1ReaderAt() io.ReaderAt { // IndexReaderAt provides an io.ReaderAt containing the carbs.Index of this CAR v2. func (r *Reader) IndexReaderAt() io.ReaderAt { - return NewOffsetReader(r.r, int64(r.Header.IndexOffset)) + return internalio.NewOffsetReader(r.r, int64(r.Header.IndexOffset)) } diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index cd68c548f..d49674c9e 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -8,7 +8,7 @@ import ( "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" carv1 "github.com/ipld/go-car" - "github.com/ipld/go-car/v2/carbs" + "github.com/ipld/go-car/v2/internal/index" ) const bulkPaddingBytesSize = 1024 @@ -21,7 +21,7 @@ type ( // Writer writes CAR v2 into a give io.Writer. Writer struct { Walk carv1.WalkFunc - IndexCodec carbs.IndexCodec + IndexCodec index.Codec NodeGetter format.NodeGetter CarV1Padding uint64 IndexPadding uint64 @@ -30,6 +30,7 @@ type ( roots []cid.Cid encodedCarV1 *bytes.Buffer } + WriteOption func(*Writer) ) // WriteTo writes this padding to the given writer as default value bytes. @@ -60,7 +61,7 @@ func (p padding) WriteTo(w io.Writer) (n int64, err error) { func NewWriter(ctx context.Context, ng format.NodeGetter, roots []cid.Cid) *Writer { return &Writer{ Walk: carv1.DefaultWalkFunc, - IndexCodec: carbs.IndexSorted, + IndexCodec: index.IndexSorted, NodeGetter: ng, ctx: ctx, roots: roots, @@ -129,7 +130,7 @@ func (w *Writer) writeIndex(writer io.Writer, carV1 []byte) (n int64, err error) // Consider refactoring carbs to make this process more efficient. // We should avoid reading the entire car into memory since it can be large. reader := bytes.NewReader(carV1) - index, err := carbs.GenerateIndex(reader, int64(len(carV1)), carbs.IndexSorted, true) + index, err := index.GenerateIndex(reader, int64(len(carV1)), index.IndexSorted) if err != nil { return } diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index 7b9595d77..120f7efa7 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -7,7 +7,7 @@ import ( format "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" dstest "github.com/ipfs/go-merkledag/test" - "github.com/ipld/go-car/v2/carbs" + "github.com/ipld/go-car/v2/internal/index" "github.com/stretchr/testify/assert" "testing" ) @@ -61,7 +61,7 @@ func TestNewWriter(t *testing.T) { dagService := dstest.Mock() wantRoots := generateRootCid(t, dagService) writer := NewWriter(context.Background(), dagService, wantRoots) - assert.Equal(t, carbs.IndexSorted, writer.IndexCodec) + assert.Equal(t, index.IndexSorted, writer.IndexCodec) assert.Equal(t, wantRoots, writer.roots) } From 0e7f7c51dbfac61241ebe77d757d1b06d8ed6640 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Mon, 21 Jun 2021 14:13:06 +0100 Subject: [PATCH 3434/3817] Run `gofumpt -l -w .` Format code in v2 consistently using `gofumpt -l -w .` This commit was moved from ipld/go-car@8b5f18d17d91ef92cfbcc797c96d09cf993a3e73 --- ipld/car/v2/blockstore/readonly_test.go | 3 ++- ipld/car/v2/car.go | 18 ++++++++---------- ipld/car/v2/car_test.go | 4 +++- ipld/car/v2/internal/carbon/carbon.go | 7 ++++--- ipld/car/v2/internal/carbon/carbon_fds.go | 3 ++- ipld/car/v2/internal/carbon/carbon_test.go | 5 +++-- ipld/car/v2/internal/carbs/util/hydrate.go | 3 ++- ipld/car/v2/internal/index/generator.go | 3 ++- ipld/car/v2/internal/index/index.go | 7 ++++--- ipld/car/v2/internal/io/cid.go | 3 ++- ipld/car/v2/reader.go | 3 ++- ipld/car/v2/writer_test.go | 3 ++- 12 files changed, 36 insertions(+), 26 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index a2d38a282..2ceb11a26 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -1,9 +1,10 @@ package blockstore import ( - "github.com/ipld/go-car/v2/internal/index" "os" "testing" + + "github.com/ipld/go-car/v2/internal/index" ) /* diff --git a/ipld/car/v2/car.go b/ipld/car/v2/car.go index 8dc30a8a4..92c01c739 100644 --- a/ipld/car/v2/car.go +++ b/ipld/car/v2/car.go @@ -14,16 +14,14 @@ const ( CharacteristicsSize = 16 ) -var ( - // The fixed prefix of a CAR v2, signalling the version number to previous versions for graceful fail over. - PrefixBytes = []byte{ - 0x0a, // unit(10) - 0xa1, // map(1) - 0x67, // string(7) - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, // "version" - 0x02, // uint(2) - } -) +// The fixed prefix of a CAR v2, signalling the version number to previous versions for graceful fail over. +var PrefixBytes = []byte{ + 0x0a, // unit(10) + 0xa1, // map(1) + 0x67, // string(7) + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, // "version" + 0x02, // uint(2) +} type ( // Header represents the CAR v2 header/pragma. diff --git a/ipld/car/v2/car_test.go b/ipld/car/v2/car_test.go index 9a6a2b423..e1795ad9d 100644 --- a/ipld/car/v2/car_test.go +++ b/ipld/car/v2/car_test.go @@ -3,10 +3,11 @@ package car_test import ( "bufio" "bytes" + "testing" + carv1 "github.com/ipld/go-car" carv2 "github.com/ipld/go-car/v2" "github.com/stretchr/testify/assert" - "testing" ) func TestCarV2PrefixLength(t *testing.T) { @@ -99,6 +100,7 @@ func TestHeader_WriteTo(t *testing.T) { }) } } + func TestHeader_ReadFrom(t *testing.T) { tests := []struct { name string diff --git a/ipld/car/v2/internal/carbon/carbon.go b/ipld/car/v2/internal/carbon/carbon.go index 4394f7887..1de4d6362 100644 --- a/ipld/car/v2/internal/carbon/carbon.go +++ b/ipld/car/v2/internal/carbon/carbon.go @@ -3,9 +3,10 @@ package carbon import ( "errors" "fmt" + "os" + carblockstore "github.com/ipld/go-car/v2/blockstore" "github.com/ipld/go-car/v2/internal/index" - "os" "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" @@ -29,11 +30,11 @@ func New(path string) (Carbon, error) { // NewWithRoots creates a new Carbon blockstore with a provided set of root cids as the car roots func NewWithRoots(path string, roots []cid.Cid) (Carbon, error) { - wfd, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666) + wfd, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o666) if err != nil { return nil, fmt.Errorf("couldn't create backing car: %w", err) } - rfd, err := os.OpenFile(path, os.O_RDONLY, 0666) + rfd, err := os.OpenFile(path, os.O_RDONLY, 0o666) if err != nil { return nil, fmt.Errorf("could not re-open read handle: %w", err) } diff --git a/ipld/car/v2/internal/carbon/carbon_fds.go b/ipld/car/v2/internal/carbon/carbon_fds.go index 000379a83..c0be2254d 100644 --- a/ipld/car/v2/internal/carbon/carbon_fds.go +++ b/ipld/car/v2/internal/carbon/carbon_fds.go @@ -1,9 +1,10 @@ package carbon import ( + "os" + "github.com/ipld/go-car/v2/blockstore" "github.com/ipld/go-car/v2/internal/index" - "os" blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" diff --git a/ipld/car/v2/internal/carbon/carbon_test.go b/ipld/car/v2/internal/carbon/carbon_test.go index a7cc3de40..d2307abb9 100644 --- a/ipld/car/v2/internal/carbon/carbon_test.go +++ b/ipld/car/v2/internal/carbon/carbon_test.go @@ -1,13 +1,14 @@ package carbon_test import ( - "github.com/ipld/go-car/v2/blockstore" - "github.com/ipld/go-car/v2/internal/carbon" "io" "math/rand" "os" "testing" + "github.com/ipld/go-car/v2/blockstore" + "github.com/ipld/go-car/v2/internal/carbon" + "github.com/ipfs/go-cid" "github.com/ipld/go-car" ) diff --git a/ipld/car/v2/internal/carbs/util/hydrate.go b/ipld/car/v2/internal/carbs/util/hydrate.go index e0a39f930..7329bb084 100644 --- a/ipld/car/v2/internal/carbs/util/hydrate.go +++ b/ipld/car/v2/internal/carbs/util/hydrate.go @@ -2,9 +2,10 @@ package main import ( "fmt" + "os" + "github.com/ipld/go-car/v2/internal/index" "golang.org/x/exp/mmap" - "os" ) func main() { diff --git a/ipld/car/v2/internal/index/generator.go b/ipld/car/v2/internal/index/generator.go index 18e25edfb..1d55708ea 100644 --- a/ipld/car/v2/internal/index/generator.go +++ b/ipld/car/v2/internal/index/generator.go @@ -4,11 +4,12 @@ import ( "bufio" "encoding/binary" "fmt" + "io" + "github.com/cheggaaa/pb/v3" carv1 "github.com/ipld/go-car" internalio "github.com/ipld/go-car/v2/internal/io" "golang.org/x/exp/mmap" - "io" ) // GenerateIndex provides a low-level interface to create an index over a diff --git a/ipld/car/v2/internal/index/index.go b/ipld/car/v2/internal/index/index.go index c0afdf309..f4fd1b6fc 100644 --- a/ipld/car/v2/internal/index/index.go +++ b/ipld/car/v2/internal/index/index.go @@ -3,11 +3,12 @@ package index import ( "encoding/binary" "fmt" + "io" + "os" + "github.com/ipfs/go-cid" internalio "github.com/ipld/go-car/v2/internal/io" "golang.org/x/exp/mmap" - "io" - "os" ) // Codec table is a first var-int in carbs indexes @@ -53,7 +54,7 @@ var IndexAtlas = map[Codec]IndexCls{ // Save writes a generated index for a car at `path` func Save(i Index, path string) error { - stream, err := os.OpenFile(path+".idx", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0640) + stream, err := os.OpenFile(path+".idx", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o640) if err != nil { return err } diff --git a/ipld/car/v2/internal/io/cid.go b/ipld/car/v2/internal/io/cid.go index 932b63bec..ee348e257 100644 --- a/ipld/car/v2/internal/io/cid.go +++ b/ipld/car/v2/internal/io/cid.go @@ -4,9 +4,10 @@ import ( "bytes" "encoding/binary" "fmt" + "io" + "github.com/ipfs/go-cid" "github.com/multiformats/go-multihash" - "io" ) var cidv0Pref = []byte{0x12, 0x20} diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index da8be152a..975a47bf1 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -3,9 +3,10 @@ package car import ( "bufio" "fmt" - internalio "github.com/ipld/go-car/v2/internal/io" "io" + internalio "github.com/ipld/go-car/v2/internal/io" + carv1 "github.com/ipld/go-car" ) diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index 120f7efa7..5d25a2f20 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -3,13 +3,14 @@ package car import ( "bytes" "context" + "testing" + "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" dstest "github.com/ipfs/go-merkledag/test" "github.com/ipld/go-car/v2/internal/index" "github.com/stretchr/testify/assert" - "testing" ) func TestPadding_WriteTo(t *testing.T) { From 36ce87f5ac6fb2717cbdd8f47c40875d4d942a7f Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Mon, 21 Jun 2021 17:28:32 +0100 Subject: [PATCH 3435/3817] Rename Prefix to Pragma for consistency Pragma seems like a better name for the prefix bytes of a car v2. Rename it along with references to it in tests etc. This commit was moved from ipld/go-car@e6a626c458704cda43acc187762a02d29463b25a --- ipld/car/v2/car.go | 17 +++++++++-------- ipld/car/v2/car_test.go | 32 ++++++++++++++++---------------- ipld/car/v2/reader.go | 8 ++++---- ipld/car/v2/writer.go | 4 ++-- 4 files changed, 31 insertions(+), 30 deletions(-) diff --git a/ipld/car/v2/car.go b/ipld/car/v2/car.go index 92c01c739..6032daabf 100644 --- a/ipld/car/v2/car.go +++ b/ipld/car/v2/car.go @@ -6,16 +6,17 @@ import ( ) const ( - // PrefixSize is the size of the CAR v2 prefix in 11 bytes, (i.e. 11). - PrefixSize = 11 + // PragmaSize is the size of the CAR v2 pragma in bytes. + PragmaSize = 11 // HeaderSize is the fixed size of CAR v2 header in number of bytes. HeaderSize = 40 // CharacteristicsSize is the fixed size of Characteristics bitfield within CAR v2 header in number of bytes. CharacteristicsSize = 16 ) -// The fixed prefix of a CAR v2, signalling the version number to previous versions for graceful fail over. -var PrefixBytes = []byte{ +// The pragma of a CAR v2, containing the version number.. +// This is a valid CAR v1 header, with version number set to 2. +var Pragma = []byte{ 0x0a, // unit(10) 0xa1, // map(1) 0x67, // string(7) @@ -68,15 +69,15 @@ func NewHeader(carV1Size uint64) Header { header := Header{ CarV1Size: carV1Size, } - header.CarV1Offset = PrefixSize + HeaderSize + header.CarV1Offset = PragmaSize + HeaderSize header.IndexOffset = header.CarV1Offset + carV1Size return header } // WithIndexPadding sets the index offset from the beginning of the file for this header and returns the // header for convenient chained calls. -// The index offset is calculated as the sum of PrefixBytesLen, HeaderBytesLen, -// Header.CarV1Len, and the given padding. +// The index offset is calculated as the sum of PragmaSize, HeaderSize, +// Header.CarV1Size, and the given padding. func (h Header) WithIndexPadding(padding uint64) Header { h.IndexOffset = h.IndexOffset + padding return h @@ -84,7 +85,7 @@ func (h Header) WithIndexPadding(padding uint64) Header { // WithCarV1Padding sets the CAR v1 dump offset from the beginning of the file for this header and returns the // header for convenient chained calls. -// The CAR v1 offset is calculated as the sum of PrefixBytesLen, HeaderBytesLen and the given padding. +// The CAR v1 offset is calculated as the sum of PragmaSize, HeaderSize and the given padding. // The call to this function also shifts the Header.IndexOffset forward by the given padding. func (h Header) WithCarV1Padding(padding uint64) Header { h.CarV1Offset = h.CarV1Offset + padding diff --git a/ipld/car/v2/car_test.go b/ipld/car/v2/car_test.go index e1795ad9d..3a3606ff4 100644 --- a/ipld/car/v2/car_test.go +++ b/ipld/car/v2/car_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestCarV2PrefixLength(t *testing.T) { +func TestCarV2PragmaLength(t *testing.T) { tests := []struct { name string want interface{} @@ -19,29 +19,29 @@ func TestCarV2PrefixLength(t *testing.T) { { "ActualSizeShouldBe11", 11, - len(carv2.PrefixBytes), + len(carv2.Pragma), }, { "ShouldStartWithVarint(10)", - carv2.PrefixBytes[0], + carv2.Pragma[0], 10, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - assert.EqualValues(t, tt.want, tt.got, "CarV2Prefix got = %v, want %v", tt.got, tt.want) + assert.EqualValues(t, tt.want, tt.got, "CarV2Pragma got = %v, want %v", tt.got, tt.want) }) } } -func TestCarV2PrefixIsValidCarV1Header(t *testing.T) { - v1h, err := carv1.ReadHeader(bufio.NewReader(bytes.NewReader(carv2.PrefixBytes))) - assert.NoError(t, err, "cannot decode prefix as CBOR with CAR v1 header structure") +func TestCarV2PragmaIsValidCarV1Header(t *testing.T) { + v1h, err := carv1.ReadHeader(bufio.NewReader(bytes.NewReader(carv2.Pragma))) + assert.NoError(t, err, "cannot decode pragma as CBOR with CAR v1 header structure") assert.Equal(t, &carv1.CarHeader{ Roots: nil, Version: 2, - }, v1h, "CAR v2 prefix must be a valid CAR v1 header") + }, v1h, "CAR v2 pragma must be a valid CAR v1 header") } func TestHeader_WriteTo(t *testing.T) { @@ -164,20 +164,20 @@ func TestHeader_WithPadding(t *testing.T) { { "WhenNoPaddingOffsetsAreSumOfSizes", carv2.NewHeader(123), - carv2.PrefixSize + carv2.HeaderSize, - carv2.PrefixSize + carv2.HeaderSize + 123, + carv2.PragmaSize + carv2.HeaderSize, + carv2.PragmaSize + carv2.HeaderSize + 123, }, { "WhenOnlyPaddingCarV1BothOffsetsShift", carv2.NewHeader(123).WithCarV1Padding(3), - carv2.PrefixSize + carv2.HeaderSize + 3, - carv2.PrefixSize + carv2.HeaderSize + 3 + 123, + carv2.PragmaSize + carv2.HeaderSize + 3, + carv2.PragmaSize + carv2.HeaderSize + 3 + 123, }, { "WhenPaddingBothCarV1AndIndexBothOffsetsShiftWithAdditionalIndexShift", carv2.NewHeader(123).WithCarV1Padding(3).WithIndexPadding(7), - carv2.PrefixSize + carv2.HeaderSize + 3, - carv2.PrefixSize + carv2.HeaderSize + 3 + 123 + 7, + carv2.PragmaSize + carv2.HeaderSize + 3, + carv2.PragmaSize + carv2.HeaderSize + 3 + 123 + 7, }, } @@ -193,9 +193,9 @@ func TestNewHeaderHasExpectedValues(t *testing.T) { wantCarV1Len := uint64(1413) want := carv2.Header{ Characteristics: carv2.Characteristics{}, - CarV1Offset: carv2.PrefixSize + carv2.HeaderSize, + CarV1Offset: carv2.PragmaSize + carv2.HeaderSize, CarV1Size: wantCarV1Len, - IndexOffset: carv2.PrefixSize + carv2.HeaderSize + wantCarV1Len, + IndexOffset: carv2.PragmaSize + carv2.HeaderSize + wantCarV1Len, } got := carv2.NewHeader(wantCarV1Len) assert.Equal(t, want, got, "NewHeader got = %v, want = %v", got, want) diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 975a47bf1..49d028dd9 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -25,7 +25,7 @@ func NewReader(r io.ReaderAt) (*Reader, error) { cr := &Reader{ r: r, } - if err := cr.readPrefix(); err != nil { + if err := cr.readPragma(); err != nil { return nil, err } if err := cr.readHeader(); err != nil { @@ -34,8 +34,8 @@ func NewReader(r io.ReaderAt) (*Reader, error) { return cr, nil } -func (r *Reader) readPrefix() (err error) { - pr := io.NewSectionReader(r.r, 0, PrefixSize) +func (r *Reader) readPragma() (err error) { + pr := io.NewSectionReader(r.r, 0, PragmaSize) header, err := carv1.ReadHeader(bufio.NewReader(pr)) if err != nil { return @@ -47,7 +47,7 @@ func (r *Reader) readPrefix() (err error) { } func (r *Reader) readHeader() (err error) { - headerSection := io.NewSectionReader(r.r, PrefixSize, HeaderSize) + headerSection := io.NewSectionReader(r.r, PragmaSize, HeaderSize) _, err = r.Header.ReadFrom(headerSection) return } diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index d49674c9e..7ec081f32 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -72,11 +72,11 @@ func NewWriter(ctx context.Context, ng format.NodeGetter, roots []cid.Cid) *Writ // WriteTo writes the given root CIDs according to CAR v2 specification, traversing the DAG using the // Writer.Walk function. func (w *Writer) WriteTo(writer io.Writer) (n int64, err error) { - _, err = writer.Write(PrefixBytes) + _, err = writer.Write(Pragma) if err != nil { return } - n += int64(PrefixSize) + n += int64(PragmaSize) // We read the entire car into memory because carbs.GenerateIndex takes a reader. // Future PRs will make this more efficient by exposing necessary interfaces in carbs so that // this can be done in an streaming manner. From 6295a648be81ee5ce60d172afbca608140597b2e Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Sat, 19 Jun 2021 21:50:44 +0100 Subject: [PATCH 3436/3817] Implement CAR introspector API Implement an API that allows a car file to be introspected, which would worth for both CAR v1 and v2. When the given reader is a v1, the `HasIndex` will always be false, and the car v1 size is set to the total readable bytes in the given reader. When the given reader is a v2, then the bytes are parsed and corresponding values are fetched from v2 header. Regardless of car version, `Roots` filed is populated, either from the v1 header, or the v1 dump in car v2. Add `HasIndex` placeholder in v2 header characteristics. Note the placeholder always returns true, since the current writer always writes the index. When the writer is made configurable we then change the placeholder to do its thing. Add lazy loading of roots in CAR v2 reader, used by the introspector. Add utility io function to size the number of readable bytes in a reader. Address review comments - Remove redundant reader sizer and use `Copy` `Discard` instead. - Embed two header types into Introspect to represent values One outstanding comment remains about the naming of `Introspect` This commit was moved from ipld/go-car@806cff8e373ec3c96bd3239e09e3b0b88ee6cf57 --- ipld/car/v2/car.go | 5 ++++ ipld/car/v2/introspector.go | 52 +++++++++++++++++++++++++++++++++++++ ipld/car/v2/reader.go | 19 ++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 ipld/car/v2/introspector.go diff --git a/ipld/car/v2/car.go b/ipld/car/v2/car.go index 6032daabf..24edbcdfe 100644 --- a/ipld/car/v2/car.go +++ b/ipld/car/v2/car.go @@ -93,6 +93,11 @@ func (h Header) WithCarV1Padding(padding uint64) Header { return h } +// HasIndex indicates whether the index is present. +func (h Header) HasIndex() bool { + return h.IndexOffset != 0 +} + // WriteTo serializes this header as bytes and writes them using the given io.Writer. func (h Header) WriteTo(w io.Writer) (n int64, err error) { wn, err := h.Characteristics.WriteTo(w) diff --git a/ipld/car/v2/introspector.go b/ipld/car/v2/introspector.go new file mode 100644 index 000000000..917f4d853 --- /dev/null +++ b/ipld/car/v2/introspector.go @@ -0,0 +1,52 @@ +package car + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + + carv1 "github.com/ipld/go-car" + internalio "github.com/ipld/go-car/v2/internal/io" +) + +// Introspection captures the result of an Introspect call. +type Introspection struct { + carv1.CarHeader + Header +} + +// Introspect introspects the given readable bytes and provides metadata about the characteristics +// and the version of CAR that r represents regardless of its version. This function is backward +// compatible; it supports both CAR v1 and v2. +// Returns error if r does not contain a valid CAR payload. +func Introspect(r io.ReaderAt) (*Introspection, error) { + i := &Introspection{} + or := internalio.NewOffsetReader(r, 0) + header, err := carv1.ReadHeader(bufio.NewReader(or)) + if err != nil { + return nil, err + } + i.CarHeader = *header + or.SeekOffset(0) + switch i.Version { + case 1: + written, err := io.Copy(ioutil.Discard, or) + if err != nil { + return i, err + } + i.CarV1Size = uint64(written) + case 2: + v2r, err := NewReader(or) + if err != nil { + return i, err + } + i.Header = v2r.Header + if i.Roots, err = v2r.Roots(); err != nil { + return i, err + } + default: + return i, fmt.Errorf("unknown version: %v", i.Version) + } + return i, nil +} diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 49d028dd9..64cc8a396 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -7,6 +7,7 @@ import ( internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/ipfs/go-cid" carv1 "github.com/ipld/go-car" ) @@ -16,6 +17,7 @@ const version2 = 2 type Reader struct { Header Header r io.ReaderAt + roots []cid.Cid } // NewReader constructs a new reader that reads CAR v2 from the given r. @@ -46,6 +48,19 @@ func (r *Reader) readPragma() (err error) { return } +// Roots returns the root CIDs of this CAR +func (r *Reader) Roots() ([]cid.Cid, error) { + if r.roots != nil { + return r.roots, nil + } + header, err := carv1.ReadHeader(bufio.NewReader(r.carv1SectionReader())) + if err != nil { + return nil, err + } + r.roots = header.Roots + return r.roots, nil +} + func (r *Reader) readHeader() (err error) { headerSection := io.NewSectionReader(r.r, PragmaSize, HeaderSize) _, err = r.Header.ReadFrom(headerSection) @@ -54,6 +69,10 @@ func (r *Reader) readHeader() (err error) { // CarV1ReaderAt provides an io.ReaderAt containing the CAR v1 dump encapsulated in this CAR v2. func (r *Reader) CarV1ReaderAt() io.ReaderAt { + return r.carv1SectionReader() +} + +func (r *Reader) carv1SectionReader() *io.SectionReader { return io.NewSectionReader(r.r, int64(r.Header.CarV1Offset), int64(r.Header.CarV1Size)) } From 6e3290ef6a5b0cc871a4eb22aebcdf9a345e27d2 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 22 Jun 2021 11:40:10 +0100 Subject: [PATCH 3437/3817] Replace introspector.go with simple `ReadPragma` Provide a `ReadPragma` function that accepts both v1 and v2 payload. This is the smallest functionality I can think of without inventing new concepts like introspection. The carv2.Reader is refactored to make use of this and strictly requres an 11 bytes long car V2 pragma. This commit was moved from ipld/go-car@885b0d0264dde4f48b5f420b808dc4f2a361d357 --- ipld/car/v2/introspector.go | 52 ------------------------------------- ipld/car/v2/reader.go | 36 ++++++++++++++++++------- 2 files changed, 26 insertions(+), 62 deletions(-) delete mode 100644 ipld/car/v2/introspector.go diff --git a/ipld/car/v2/introspector.go b/ipld/car/v2/introspector.go deleted file mode 100644 index 917f4d853..000000000 --- a/ipld/car/v2/introspector.go +++ /dev/null @@ -1,52 +0,0 @@ -package car - -import ( - "bufio" - "fmt" - "io" - "io/ioutil" - - carv1 "github.com/ipld/go-car" - internalio "github.com/ipld/go-car/v2/internal/io" -) - -// Introspection captures the result of an Introspect call. -type Introspection struct { - carv1.CarHeader - Header -} - -// Introspect introspects the given readable bytes and provides metadata about the characteristics -// and the version of CAR that r represents regardless of its version. This function is backward -// compatible; it supports both CAR v1 and v2. -// Returns error if r does not contain a valid CAR payload. -func Introspect(r io.ReaderAt) (*Introspection, error) { - i := &Introspection{} - or := internalio.NewOffsetReader(r, 0) - header, err := carv1.ReadHeader(bufio.NewReader(or)) - if err != nil { - return nil, err - } - i.CarHeader = *header - or.SeekOffset(0) - switch i.Version { - case 1: - written, err := io.Copy(ioutil.Discard, or) - if err != nil { - return i, err - } - i.CarV1Size = uint64(written) - case 2: - v2r, err := NewReader(or) - if err != nil { - return i, err - } - i.Header = v2r.Header - if i.Roots, err = v2r.Roots(); err != nil { - return i, err - } - default: - return i, fmt.Errorf("unknown version: %v", i.Version) - } - return i, nil -} diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 64cc8a396..47bd17cda 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -11,8 +11,6 @@ import ( carv1 "github.com/ipld/go-car" ) -const version2 = 2 - // Reader represents a reader of CAR v2. type Reader struct { Header Header @@ -21,13 +19,13 @@ type Reader struct { } // NewReader constructs a new reader that reads CAR v2 from the given r. -// Upon instantiation, the reader inspects the payload by reading the first 11 bytes and will return -// an error if the payload does not represent a CAR v2. +// Upon instantiation, the reader inspects the payload by reading the pragma and will return +// an error if the pragma does not represent a CAR v2. func NewReader(r io.ReaderAt) (*Reader, error) { cr := &Reader{ r: r, } - if err := cr.readPragma(); err != nil { + if err := cr.requireV2Pragma(); err != nil { return nil, err } if err := cr.readHeader(); err != nil { @@ -36,14 +34,17 @@ func NewReader(r io.ReaderAt) (*Reader, error) { return cr, nil } -func (r *Reader) readPragma() (err error) { - pr := io.NewSectionReader(r.r, 0, PragmaSize) - header, err := carv1.ReadHeader(bufio.NewReader(pr)) +func (r *Reader) requireV2Pragma() (err error) { + or := internalio.NewOffsetReader(r.r, 0) + version, _, err := ReadPragma(or) if err != nil { return } - if header.Version != version2 { - err = fmt.Errorf("invalid car version: %d", header.Version) + if version != 2 { + return fmt.Errorf("invalid car version: %d", version) + } + if or.Offset() != PragmaSize { + err = fmt.Errorf("invalid car v2 pragma; size %d is larger than expected %d", or.Offset(), PragmaSize) } return } @@ -80,3 +81,18 @@ func (r *Reader) carv1SectionReader() *io.SectionReader { func (r *Reader) IndexReaderAt() io.ReaderAt { return internalio.NewOffsetReader(r.r, int64(r.Header.IndexOffset)) } + +// ReadPragma reads the pragma from r. +// This function accepts both CAR v1 and v2 payloads. +// The roots are returned only if the version of pragma equals 1, otherwise returns nil as roots. +func ReadPragma(r io.Reader) (version uint64, roots []cid.Cid, err error) { + header, err := carv1.ReadHeader(bufio.NewReader(r)) + if err != nil { + return + } + version = header.Version + if version == 1 { + roots = header.Roots + } + return +} From 16c207a6bd332bffbd321308cecb69924d8db702 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 23 Jun 2021 10:13:55 +0100 Subject: [PATCH 3438/3817] Add TODO to reduce reader wrapping when working with CAR v1 reader Car V1 reader demands bufio.Reader. We want to be smart about wrapping readers unencessarily when calling the reader API from car v2. Add TODO to improve this this. This commit was moved from ipld/go-car@588d68e7d923c085b59a70aaa434f1e8b0a4b428 --- ipld/car/v2/reader.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 47bd17cda..c832570b2 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -86,6 +86,7 @@ func (r *Reader) IndexReaderAt() io.ReaderAt { // This function accepts both CAR v1 and v2 payloads. // The roots are returned only if the version of pragma equals 1, otherwise returns nil as roots. func ReadPragma(r io.Reader) (version uint64, roots []cid.Cid, err error) { + // TODO if the user provides a reader that sufficiently satisfies what carv1.ReadHeader is asking then use that instead of wrapping every time. header, err := carv1.ReadHeader(bufio.NewReader(r)) if err != nil { return From 67386bd80fe5a89d207a5e404356a96a6bd33c0f Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 23 Jun 2021 11:55:40 +0100 Subject: [PATCH 3439/3817] Use unsigned integer to represent width and count in sorted index Note specs need to be update to reflect this here: - https://github.com/ipld/ipld/pull/107 For context, see: - https://github.com/ipld/ipld/pull/107/files#r656749263 This commit was moved from ipld/go-car@aef90dfe9260b2110e925998347f418c618774f8 --- ipld/car/v2/internal/index/indexsorted.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ipld/car/v2/internal/index/indexsorted.go b/ipld/car/v2/internal/index/indexsorted.go index b14334d2c..2646d08b7 100644 --- a/ipld/car/v2/internal/index/indexsorted.go +++ b/ipld/car/v2/internal/index/indexsorted.go @@ -18,11 +18,11 @@ type ( } recordSet []digestRecord singleWidthIndex struct { - width int32 - len int64 // in struct, len is #items. when marshaled, it's saved as #bytes. + width uint32 + len uint64 // in struct, len is #items. when marshaled, it's saved as #bytes. index []byte } - multiWidthIndex map[int32]singleWidthIndex + multiWidthIndex map[uint32]singleWidthIndex ) func (d digestRecord) write(buf []byte) { @@ -65,7 +65,7 @@ func (s *singleWidthIndex) Unmarshal(r io.Reader) error { return err } s.index = make([]byte, s.len) - s.len /= int64(s.width) + s.len /= uint64(s.width) _, err := io.ReadFull(r, s.index) return err } @@ -86,7 +86,7 @@ func (s *singleWidthIndex) get(d []byte) uint64 { idx := sort.Search(int(s.len), func(i int) bool { return s.Less(i, d) }) - if int64(idx) == s.len { + if uint64(idx) == s.len { return 0 } if !bytes.Equal(d[:], s.index[idx*int(s.width):(idx+1)*int(s.width)-8]) { @@ -117,7 +117,7 @@ func (m *multiWidthIndex) Get(c cid.Cid) (uint64, error) { if err != nil { return 0, err } - if s, ok := (*m)[int32(len(d.Digest)+8)]; ok { + if s, ok := (*m)[uint32(len(d.Digest)+8)]; ok { return s.get(d.Digest), nil } return 0, errNotFound @@ -176,11 +176,11 @@ func (m *multiWidthIndex) Load(items []Record) error { itm.write(compact[off*rcrdWdth : (off+1)*rcrdWdth]) } s := singleWidthIndex{ - width: int32(rcrdWdth), - len: int64(len(lst)), + width: uint32(rcrdWdth), + len: uint64(len(lst)), index: compact, } - (*m)[int32(width)+8] = s + (*m)[uint32(width)+8] = s } return nil } From d1402203184a6025092220cb5bb709efa1dc5eca Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 23 Jun 2021 12:06:09 +0100 Subject: [PATCH 3440/3817] Move index out of internal package to unblock Ignite team As requested, move `index` package out of internal to unblock ignite team subject to the fact that the API might change. This commit was moved from ipld/go-car@40097aa111f06840b23fa8b4d8708b24b9c4fdcf --- ipld/car/v2/blockstore/readonly.go | 2 +- ipld/car/v2/blockstore/readonly_test.go | 2 +- ipld/car/v2/{internal => }/index/errors.go | 0 ipld/car/v2/{internal => }/index/generator.go | 0 ipld/car/v2/{internal => }/index/index.go | 0 ipld/car/v2/{internal => }/index/indexgobhash.go | 0 ipld/car/v2/{internal => }/index/indexhashed.go | 0 ipld/car/v2/{internal => }/index/indexsorted.go | 0 ipld/car/v2/{internal => }/index/insertionindex.go | 0 ipld/car/v2/internal/carbon/carbon.go | 2 +- ipld/car/v2/internal/carbon/carbon_fds.go | 2 +- ipld/car/v2/internal/carbs/util/hydrate.go | 2 +- ipld/car/v2/writer.go | 2 +- ipld/car/v2/writer_test.go | 2 +- 14 files changed, 7 insertions(+), 7 deletions(-) rename ipld/car/v2/{internal => }/index/errors.go (100%) rename ipld/car/v2/{internal => }/index/generator.go (100%) rename ipld/car/v2/{internal => }/index/index.go (100%) rename ipld/car/v2/{internal => }/index/indexgobhash.go (100%) rename ipld/car/v2/{internal => }/index/indexhashed.go (100%) rename ipld/car/v2/{internal => }/index/indexsorted.go (100%) rename ipld/car/v2/{internal => }/index/insertionindex.go (100%) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 57a1e0f76..14cf74d70 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -13,7 +13,7 @@ import ( blockstore "github.com/ipfs/go-ipfs-blockstore" carv1 "github.com/ipld/go-car" "github.com/ipld/go-car/util" - "github.com/ipld/go-car/v2/internal/index" + "github.com/ipld/go-car/v2/index" internalio "github.com/ipld/go-car/v2/internal/io" "golang.org/x/exp/mmap" ) diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 2ceb11a26..73ed19dcb 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -4,7 +4,7 @@ import ( "os" "testing" - "github.com/ipld/go-car/v2/internal/index" + "github.com/ipld/go-car/v2/index" ) /* diff --git a/ipld/car/v2/internal/index/errors.go b/ipld/car/v2/index/errors.go similarity index 100% rename from ipld/car/v2/internal/index/errors.go rename to ipld/car/v2/index/errors.go diff --git a/ipld/car/v2/internal/index/generator.go b/ipld/car/v2/index/generator.go similarity index 100% rename from ipld/car/v2/internal/index/generator.go rename to ipld/car/v2/index/generator.go diff --git a/ipld/car/v2/internal/index/index.go b/ipld/car/v2/index/index.go similarity index 100% rename from ipld/car/v2/internal/index/index.go rename to ipld/car/v2/index/index.go diff --git a/ipld/car/v2/internal/index/indexgobhash.go b/ipld/car/v2/index/indexgobhash.go similarity index 100% rename from ipld/car/v2/internal/index/indexgobhash.go rename to ipld/car/v2/index/indexgobhash.go diff --git a/ipld/car/v2/internal/index/indexhashed.go b/ipld/car/v2/index/indexhashed.go similarity index 100% rename from ipld/car/v2/internal/index/indexhashed.go rename to ipld/car/v2/index/indexhashed.go diff --git a/ipld/car/v2/internal/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go similarity index 100% rename from ipld/car/v2/internal/index/indexsorted.go rename to ipld/car/v2/index/indexsorted.go diff --git a/ipld/car/v2/internal/index/insertionindex.go b/ipld/car/v2/index/insertionindex.go similarity index 100% rename from ipld/car/v2/internal/index/insertionindex.go rename to ipld/car/v2/index/insertionindex.go diff --git a/ipld/car/v2/internal/carbon/carbon.go b/ipld/car/v2/internal/carbon/carbon.go index 1de4d6362..a55320066 100644 --- a/ipld/car/v2/internal/carbon/carbon.go +++ b/ipld/car/v2/internal/carbon/carbon.go @@ -6,7 +6,7 @@ import ( "os" carblockstore "github.com/ipld/go-car/v2/blockstore" - "github.com/ipld/go-car/v2/internal/index" + "github.com/ipld/go-car/v2/index" "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" diff --git a/ipld/car/v2/internal/carbon/carbon_fds.go b/ipld/car/v2/internal/carbon/carbon_fds.go index c0be2254d..eecabf3a0 100644 --- a/ipld/car/v2/internal/carbon/carbon_fds.go +++ b/ipld/car/v2/internal/carbon/carbon_fds.go @@ -4,7 +4,7 @@ import ( "os" "github.com/ipld/go-car/v2/blockstore" - "github.com/ipld/go-car/v2/internal/index" + "github.com/ipld/go-car/v2/index" blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" diff --git a/ipld/car/v2/internal/carbs/util/hydrate.go b/ipld/car/v2/internal/carbs/util/hydrate.go index 7329bb084..7a16fcea5 100644 --- a/ipld/car/v2/internal/carbs/util/hydrate.go +++ b/ipld/car/v2/internal/carbs/util/hydrate.go @@ -4,7 +4,7 @@ import ( "fmt" "os" - "github.com/ipld/go-car/v2/internal/index" + "github.com/ipld/go-car/v2/index" "golang.org/x/exp/mmap" ) diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index 7ec081f32..51d404798 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -8,7 +8,7 @@ import ( "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" carv1 "github.com/ipld/go-car" - "github.com/ipld/go-car/v2/internal/index" + "github.com/ipld/go-car/v2/index" ) const bulkPaddingBytesSize = 1024 diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index 5d25a2f20..1db27f9ad 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -9,7 +9,7 @@ import ( format "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" dstest "github.com/ipfs/go-merkledag/test" - "github.com/ipld/go-car/v2/internal/index" + "github.com/ipld/go-car/v2/index" "github.com/stretchr/testify/assert" ) From 6534b95f1b79532fd2343784f61359af1bf7dd1a Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 23 Jun 2021 16:52:35 +0100 Subject: [PATCH 3441/3817] Implement read-while-writing blockstore Implement a blockstore that can write blocks formatted in a CAR v2 form, while at the same time it can be used to read blocks written to it. This implementation refactors existing work from carbon to achieve this. Refactor index read/write to accept reader and writer for more flexibility. Remove progress bar from carbon implementation and hydra CLI. Remove poswriter and instead use existing offset writer by adding the abiliy to get position of the writer. Refactor index generator methods for better human readability. This commit was moved from ipld/go-car@01e55a90f14e394862789381d76e9894ca527b70 --- ipld/car/v2/blockstore/blockstore.go | 181 ++++++++++++++++++ .../blockstore_test.go} | 31 ++- ipld/car/v2/blockstore/readonly.go | 27 ++- ipld/car/v2/blockstore/readonly_test.go | 83 -------- ipld/car/v2/index/generator.go | 29 +-- ipld/car/v2/index/index.go | 28 ++- ipld/car/v2/internal/carbon/carbon.go | 64 ------- ipld/car/v2/internal/carbon/carbon_fds.go | 65 ------- ipld/car/v2/internal/carbon/doc.go | 6 - ipld/car/v2/internal/carbon/poswriter.go | 14 -- ipld/car/v2/internal/carbs/util/hydrate.go | 8 +- ipld/car/v2/internal/io/offset_writer.go | 20 +- ipld/car/v2/reader.go | 14 +- ipld/car/v2/writer.go | 4 +- 14 files changed, 257 insertions(+), 317 deletions(-) create mode 100644 ipld/car/v2/blockstore/blockstore.go rename ipld/car/v2/{internal/carbon/carbon_test.go => blockstore/blockstore_test.go} (76%) delete mode 100644 ipld/car/v2/blockstore/readonly_test.go delete mode 100644 ipld/car/v2/internal/carbon/carbon.go delete mode 100644 ipld/car/v2/internal/carbon/carbon_fds.go delete mode 100644 ipld/car/v2/internal/carbon/doc.go delete mode 100644 ipld/car/v2/internal/carbon/poswriter.go diff --git a/ipld/car/v2/blockstore/blockstore.go b/ipld/car/v2/blockstore/blockstore.go new file mode 100644 index 000000000..00f5bde85 --- /dev/null +++ b/ipld/car/v2/blockstore/blockstore.go @@ -0,0 +1,181 @@ +package blockstore + +import ( + "context" + "errors" + "fmt" + "io" + "os" + + blockstore "github.com/ipfs/go-ipfs-blockstore" + carv1 "github.com/ipld/go-car" + carv2 "github.com/ipld/go-car/v2" + internalio "github.com/ipld/go-car/v2/internal/io" + + "github.com/ipld/go-car/v2/index" + + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + "github.com/ipld/go-car/util" +) + +var _ (blockstore.Blockstore) = (*Blockstore)(nil) +var errFinalized = errors.New("finalized blockstore") + +// Blockstore is a carbon implementation based on having two file handles opened, +// one appending to the file, and the other +// seeking to read items as needed. +// This implementation is preferable for a write-heavy workload. +// The Finalize function must be called once the putting blocks are finished. +// Upon calling Finalize all read and write calls to this blockstore will result in error. +type ( + Blockstore struct { + w io.WriterAt + carV1Wrtier *internalio.OffsetWriter + ReadOnly + idx *index.InsertionIndex + header carv2.Header + } + Option func(*Blockstore) +) + +func WithCarV1Padding(p uint64) Option { + return func(b *Blockstore) { + b.header = b.header.WithCarV1Padding(p) + } +} + +func WithIndexPadding(p uint64) Option { + return func(b *Blockstore) { + b.header = b.header.WithIndexPadding(p) + } +} + +// New creates a new Blockstore at the given path with a provided set of root cids as the car roots. +func New(path string, roots []cid.Cid, opts ...Option) (*Blockstore, error) { + // TODO support resumption if the path provided contains partially written blocks in v2 format. + wfd, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o666) + if err != nil { + return nil, fmt.Errorf("couldn't create backing car: %w", err) + } + rfd, err := os.OpenFile(path, os.O_RDONLY, 0o666) + if err != nil { + return nil, fmt.Errorf("could not re-open read handle: %w", err) + } + + indexcls, ok := index.IndexAtlas[index.IndexInsertion] + if !ok { + return nil, fmt.Errorf("unknownindex codec: %#v", index.IndexInsertion) + } + idx := (indexcls()).(*index.InsertionIndex) + + b := &Blockstore{ + w: wfd, + ReadOnly: *ReadOnlyOf(rfd, idx), + idx: idx, + header: carv2.Header{}, + } + + applyOptions(b, opts) + b.carV1Wrtier = internalio.NewOffsetWriter(wfd, int64(b.header.CarV1Offset)) + + if _, err := wfd.Write(carv2.Pragma); err != nil { + return nil, err + } + + v1Header := &carv1.CarHeader{ + Roots: roots, + Version: 1, + } + if err := carv1.WriteHeader(v1Header, b.carV1Wrtier); err != nil { + return nil, fmt.Errorf("couldn't write car header: %w", err) + } + return b, nil +} + +func applyOptions(b *Blockstore, opts []Option) { + for _, opt := range opts { + opt(b) + } +} + +func (b *Blockstore) DeleteBlock(cid.Cid) error { + return errUnsupported +} + +// Put puts a given block to the underlying datastore +func (b *Blockstore) Put(blk blocks.Block) error { + if b.isFinalized() { + return errFinalized + } + return b.PutMany([]blocks.Block{blk}) +} + +// PutMany puts a slice of blocks at the same time using batching +// capabilities of the underlying datastore whenever possible. +func (b *Blockstore) PutMany(blks []blocks.Block) error { + if b.isFinalized() { + return errFinalized + } + for _, bl := range blks { + n := uint64(b.carV1Wrtier.Position()) + if err := util.LdWrite(b.carV1Wrtier, bl.Cid().Bytes(), bl.RawData()); err != nil { + return err + } + b.idx.InsertNoReplace(bl.Cid(), n) + } + return nil +} + +func (b *Blockstore) isFinalized() bool { + return b.header.CarV1Size != 0 +} + +// Finalize finalizes this blockstore by writing the CAR v2 header, along with flattened index +// for more efficient subsequent read. +// After this call, this blockstore can no longer be used for read or write. +func (b *Blockstore) Finalize() error { + if b.isFinalized() { + return errFinalized + } + // TODO check if add index option is set and don't write the index then set index offset to zero. + // TODO see if folks need to continue reading from a finalized blockstore, if so return ReadOnly blockstore here. + b.header.CarV1Size = uint64(b.carV1Wrtier.Position()) + if _, err := b.header.WriteTo(internalio.NewOffsetWriter(b.w, carv2.PragmaSize)); err != nil { + return err + } + // TODO if index not needed don't bother flattening it. + fi, err := b.idx.Flatten() + if err != nil { + return err + } + return index.WriteTo(fi, internalio.NewOffsetWriter(b.w, int64(b.header.IndexOffset))) +} + +func (b *Blockstore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { + if b.isFinalized() { + return nil, errFinalized + } + return b.ReadOnly.AllKeysChan(ctx) +} + +func (b *Blockstore) Has(key cid.Cid) (bool, error) { + if b.isFinalized() { + return false, errFinalized + } + return b.ReadOnly.Has(key) +} + +func (b *Blockstore) Get(key cid.Cid) (blocks.Block, error) { + if b.isFinalized() { + return nil, errFinalized + } + return b.ReadOnly.Get(key) +} + +func (b *Blockstore) GetSize(key cid.Cid) (int, error) { + if b.isFinalized() { + return 0, errFinalized + } + return b.ReadOnly.GetSize(key) +} diff --git a/ipld/car/v2/internal/carbon/carbon_test.go b/ipld/car/v2/blockstore/blockstore_test.go similarity index 76% rename from ipld/car/v2/internal/carbon/carbon_test.go rename to ipld/car/v2/blockstore/blockstore_test.go index d2307abb9..76073b86b 100644 --- a/ipld/car/v2/internal/carbon/carbon_test.go +++ b/ipld/car/v2/blockstore/blockstore_test.go @@ -1,19 +1,17 @@ -package carbon_test +package blockstore_test import ( + "github.com/ipld/go-car/v2/blockstore" "io" "math/rand" "os" "testing" - "github.com/ipld/go-car/v2/blockstore" - "github.com/ipld/go-car/v2/internal/carbon" - "github.com/ipfs/go-cid" - "github.com/ipld/go-car" + carv1 "github.com/ipld/go-car" ) -func TestCarbon(t *testing.T) { +func TestBlockstore(t *testing.T) { f, err := os.Open("../carbs/testdata/test.car") if err != nil { t.Skipf("fixture not found: %q", err) @@ -21,18 +19,20 @@ func TestCarbon(t *testing.T) { } defer f.Close() - ingester, err := carbon.New("testcarbon.car") + r, err := carv1.NewCarReader(f) + if err != nil { t.Fatal(err) } - defer func() { - os.Remove("testcarbon.car") - }() - - r, err := car.NewCarReader(f) + path := "testv2blockstore.car" + ingester, err := blockstore.New(path, r.Header.Roots) if err != nil { t.Fatal(err) } + defer func() { + os.Remove(path) + }() + cids := make([]cid.Cid, 0) for { b, err := r.Next() @@ -61,12 +61,9 @@ func TestCarbon(t *testing.T) { } } - if err := ingester.Finish(); err != nil { + if err := ingester.Finalize(); err != nil { t.Fatal(err) } - defer func() { - os.Remove("testcarbon.car.idx") - }() stat, err := os.Stat("testcarbon.car.idx") if err != nil { @@ -76,7 +73,7 @@ func TestCarbon(t *testing.T) { t.Fatalf("index not written: %v", stat) } - carb, err := blockstore.LoadReadOnly("testcarbon.car", true) + carb, err := blockstore.OpenReadOnly(path, true) if err != nil { t.Fatal(err) } diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 14cf74d70..849df2e53 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -6,16 +6,16 @@ import ( "encoding/binary" "errors" "fmt" - "io" - blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" carv1 "github.com/ipld/go-car" "github.com/ipld/go-car/util" + carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" internalio "github.com/ipld/go-car/v2/internal/io" "golang.org/x/exp/mmap" + "io" ) var _ blockstore.Blockstore = (*ReadOnly)(nil) @@ -29,20 +29,26 @@ type ReadOnly struct { idx index.Index } -// ReadOnlyOf opens a carbs data store from an existing reader of the base data and index +// ReadOnlyOf opens a carbs data store from an existing backing of the base data (i.e. CAR v1 payload) and index. func ReadOnlyOf(backing io.ReaderAt, index index.Index) *ReadOnly { return &ReadOnly{backing, index} } -// LoadReadOnly opens a read-only blockstore, generating an index if it does not exist -func LoadReadOnly(path string, noPersist bool) (*ReadOnly, error) { +// OpenReadOnly opens a read-only blockstore from a CAR v2 file, generating an index if it does not exist. +// If noPersist is set to false then the generated index is written into the CAR v2 file at path. +func OpenReadOnly(path string, noPersist bool) (*ReadOnly, error) { reader, err := mmap.Open(path) if err != nil { return nil, err } - idx, err := index.Restore(path) + + v2r, err := carv2.NewReader(reader) if err != nil { - idx, err = index.GenerateIndex(reader, 0, index.IndexSorted) + return nil, err + } + var idx index.Index + if !v2r.Header.HasIndex() { + idx, err := index.Generate(v2r.CarV1Reader(), index.IndexSorted) if err != nil { return nil, err } @@ -51,9 +57,14 @@ func LoadReadOnly(path string, noPersist bool) (*ReadOnly, error) { return nil, err } } + } else { + idx, err = index.ReadFrom(v2r.IndexReader()) + if err != nil { + return nil, err + } } obj := ReadOnly{ - backing: reader, + backing: v2r.CarV1Reader(), idx: idx, } return &obj, nil diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go deleted file mode 100644 index 73ed19dcb..000000000 --- a/ipld/car/v2/blockstore/readonly_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package blockstore - -import ( - "os" - "testing" - - "github.com/ipld/go-car/v2/index" -) - -/* -func mkCar() (string, error) { - f, err := ioutil.TempFile(os.TempDir(), "car") - if err != nil { - return "", err - } - defer f.Close() - - ds := mockNodeGetter{ - Nodes: make(map[cid.Cid]format.Node), - } - type linker struct { - Name string - Links []*format.Link - } - cbornode.RegisterCborType(linker{}) - - children := make([]format.Node, 0, 10) - childLinks := make([]*format.Link, 0, 10) - for i := 0; i < 10; i++ { - child, _ := cbornode.WrapObject([]byte{byte(i)}, multihash.SHA2_256, -1) - children = append(children, child) - childLinks = append(childLinks, &format.Link{Name: fmt.Sprintf("child%d", i), Cid: child.Cid()}) - } - b, err := cbornode.WrapObject(linker{Name: "root", Links: childLinks}, multihash.SHA2_256, -1) - if err != nil { - return "", fmt.Errorf("couldn't make cbor node: %v", err) - } - ds.Nodes[b.Cid()] = b - - if err := car.WriteCar(context.Background(), &ds, []cid.Cid{b.Cid()}, f); err != nil { - return "", err - } - - return f.Name(), nil -} -*/ - -func TestIndexRT(t *testing.T) { - /* - carFile, err := mkCar() - if err != nil { - t.Fatal(err) - } - defer os.Remove(carFile) - */ - // TODO use temporary directory to run tests taht work with OS file system to avoid accidental source code modification - carFile := "testdata/test.car" - - cf, err := LoadReadOnly(carFile, false) - if err != nil { - t.Fatal(err) - } - defer os.Remove(carFile + ".idx") - - r, err := cf.Roots() - if err != nil { - t.Fatal(err) - } - if len(r) != 1 { - t.Fatalf("unexpected number of roots: %d", len(r)) - } - if _, err := cf.Get(r[0]); err != nil { - t.Fatalf("failed get: %v", err) - } - - idx, err := index.Restore(carFile) - if err != nil { - t.Fatalf("failed restore: %v", err) - } - if idx, err := idx.Get(r[0]); idx == 0 || err != nil { - t.Fatalf("bad index: %d %v", idx, err) - } -} diff --git a/ipld/car/v2/index/generator.go b/ipld/car/v2/index/generator.go index 1d55708ea..5c1ef7f87 100644 --- a/ipld/car/v2/index/generator.go +++ b/ipld/car/v2/index/generator.go @@ -6,27 +6,19 @@ import ( "fmt" "io" - "github.com/cheggaaa/pb/v3" carv1 "github.com/ipld/go-car" internalio "github.com/ipld/go-car/v2/internal/io" "golang.org/x/exp/mmap" ) -// GenerateIndex provides a low-level interface to create an index over a -// reader to a car stream. -func GenerateIndex(store io.ReaderAt, size int64, codec Codec) (Index, error) { +// Generate generates index given car v1 payload using the given codec. +func Generate(car io.ReaderAt, codec Codec) (Index, error) { indexcls, ok := IndexAtlas[codec] if !ok { return nil, fmt.Errorf("unknown codec: %#v", codec) } - bar := pb.New64(size) - bar.Set(pb.Bytes, true) - bar.Set(pb.Terminal, true) - - bar.Start() - - header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReader(store, 0))) + header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReader(car, 0))) if err != nil { return nil, fmt.Errorf("error reading car header: %w", err) } @@ -34,16 +26,14 @@ func GenerateIndex(store io.ReaderAt, size int64, codec Codec) (Index, error) { if err != nil { return nil, err } - bar.Add64(int64(offset)) idx := indexcls() records := make([]Record, 0) - rdr := internalio.NewOffsetReader(store, int64(offset)) + rdr := internalio.NewOffsetReader(car, int64(offset)) for { thisItemIdx := rdr.Offset() l, err := binary.ReadUvarint(rdr) - bar.Add64(int64(l)) thisItemForNxt := rdr.Offset() if err != nil { if err == io.EOF { @@ -51,7 +41,7 @@ func GenerateIndex(store io.ReaderAt, size int64, codec Codec) (Index, error) { } return nil, err } - c, _, err := internalio.ReadCid(store, thisItemForNxt) + c, _, err := internalio.ReadCid(car, thisItemForNxt) if err != nil { return nil, err } @@ -63,18 +53,17 @@ func GenerateIndex(store io.ReaderAt, size int64, codec Codec) (Index, error) { return nil, err } - bar.Finish() - return idx, nil } -// Generate walks a car file and generates an index of cid->byte offset in it. -func Generate(path string, codec Codec) error { +// GenerateFromFile walks a car v1 file and generates an index of cid->byte offset, then +// stors it in a separate file at the given path with extension `.idx`. +func GenerateFromFile(path string, codec Codec) error { store, err := mmap.Open(path) if err != nil { return err } - idx, err := GenerateIndex(store, 0, codec) + idx, err := Generate(store, codec) if err != nil { return err } diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index f4fd1b6fc..5ecdfbf56 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -1,14 +1,13 @@ package index import ( + "bufio" "encoding/binary" "fmt" "io" "os" "github.com/ipfs/go-cid" - internalio "github.com/ipld/go-car/v2/internal/io" - "golang.org/x/exp/mmap" ) // Codec table is a first var-int in carbs indexes @@ -52,32 +51,28 @@ var IndexAtlas = map[Codec]IndexCls{ IndexInsertion: mkInsertion, } -// Save writes a generated index for a car at `path` +// Save writes a generated index into the given `path` as a file with a `.idx` extension. func Save(i Index, path string) error { stream, err := os.OpenFile(path+".idx", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o640) if err != nil { return err } defer stream.Close() + return WriteTo(i, stream) +} +func WriteTo(i Index, w io.Writer) error { buf := make([]byte, binary.MaxVarintLen64) b := binary.PutUvarint(buf, uint64(i.Codec())) - if _, err := stream.Write(buf[:b]); err != nil { + if _, err := w.Write(buf[:b]); err != nil { return err } - return i.Marshal(stream) + return i.Marshal(w) } -// Restore loads an index from an on-disk representation. -func Restore(path string) (Index, error) { - reader, err := mmap.Open(path + ".idx") - if err != nil { - return nil, err - } - - defer reader.Close() - uar := internalio.NewOffsetReader(reader, 0) - codec, err := binary.ReadUvarint(uar) +func ReadFrom(uar io.Reader) (Index, error) { + reader := bufio.NewReader(uar) + codec, err := binary.ReadUvarint(reader) if err != nil { return nil, err } @@ -86,9 +81,8 @@ func Restore(path string) (Index, error) { return nil, fmt.Errorf("unknown codec: %d", codec) } idxInst := idx() - if err := idxInst.Unmarshal(uar); err != nil { + if err := idxInst.Unmarshal(reader); err != nil { return nil, err } - return idxInst, nil } diff --git a/ipld/car/v2/internal/carbon/carbon.go b/ipld/car/v2/internal/carbon/carbon.go deleted file mode 100644 index a55320066..000000000 --- a/ipld/car/v2/internal/carbon/carbon.go +++ /dev/null @@ -1,64 +0,0 @@ -package carbon - -import ( - "errors" - "fmt" - "os" - - carblockstore "github.com/ipld/go-car/v2/blockstore" - "github.com/ipld/go-car/v2/index" - - "github.com/ipfs/go-cid" - blockstore "github.com/ipfs/go-ipfs-blockstore" - carv1 "github.com/ipld/go-car" -) - -// Carbon is a carbs-index-compatible blockstore supporting appending additional blocks -type Carbon interface { - blockstore.Blockstore - Checkpoint() error - Finish() error -} - -// errUnsupported is returned for unsupported blockstore operations (like delete) -var errUnsupported = errors.New("unsupported by carbon") - -// New creates a new Carbon blockstore -func New(path string) (Carbon, error) { - return NewWithRoots(path, []cid.Cid{}) -} - -// NewWithRoots creates a new Carbon blockstore with a provided set of root cids as the car roots -func NewWithRoots(path string, roots []cid.Cid) (Carbon, error) { - wfd, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o666) - if err != nil { - return nil, fmt.Errorf("couldn't create backing car: %w", err) - } - rfd, err := os.OpenFile(path, os.O_RDONLY, 0o666) - if err != nil { - return nil, fmt.Errorf("could not re-open read handle: %w", err) - } - - hdr := carv1.CarHeader{ - Roots: roots, - Version: 1, - } - writer := poswriter{wfd, 0} - if err := carv1.WriteHeader(&hdr, &writer); err != nil { - return nil, fmt.Errorf("couldn't write car header: %w", err) - } - - indexcls, ok := index.IndexAtlas[index.IndexInsertion] - if !ok { - return nil, fmt.Errorf("unknownindex codec: %#v", index.IndexInsertion) - } - - idx := (indexcls()).(*index.InsertionIndex) - f := carbonFD{ - path, - &writer, - *carblockstore.ReadOnlyOf(rfd, idx), - idx, - } - return &f, nil -} diff --git a/ipld/car/v2/internal/carbon/carbon_fds.go b/ipld/car/v2/internal/carbon/carbon_fds.go deleted file mode 100644 index eecabf3a0..000000000 --- a/ipld/car/v2/internal/carbon/carbon_fds.go +++ /dev/null @@ -1,65 +0,0 @@ -package carbon - -import ( - "os" - - "github.com/ipld/go-car/v2/blockstore" - "github.com/ipld/go-car/v2/index" - - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" - "github.com/ipld/go-car/util" -) - -// carbonFD is a carbon implementation based on having two file handles opened, one appending to the file, and the other -// seeking to read items as needed. This implementation is preferable for a write-heavy workload. -type carbonFD struct { - path string - writeHandle *poswriter - blockstore.ReadOnly - idx *index.InsertionIndex -} - -var _ (Carbon) = (*carbonFD)(nil) - -func (c *carbonFD) DeleteBlock(cid.Cid) error { - return errUnsupported -} - -// Put puts a given block to the underlying datastore -func (c *carbonFD) Put(b blocks.Block) error { - return c.PutMany([]blocks.Block{b}) -} - -// PutMany puts a slice of blocks at the same time using batching -// capabilities of the underlying datastore whenever possible. -func (c *carbonFD) PutMany(b []blocks.Block) error { - for _, bl := range b { - n := c.writeHandle.at - if err := util.LdWrite(c.writeHandle, bl.Cid().Bytes(), bl.RawData()); err != nil { - return err - } - c.idx.InsertNoReplace(bl.Cid(), n) - } - return nil -} - -// Finish serializes the carbon index so that it can be later used as a carbs read-only blockstore -func (c *carbonFD) Finish() error { - fi, err := c.idx.Flatten() - if err != nil { - return err - } - fd, ok := c.writeHandle.Writer.(*os.File) - if ok { - if err := fd.Close(); err != nil { - return err - } - } - return index.Save(fi, c.path) -} - -// Checkpoint serializes the carbon index so that the partially written blockstore can be resumed. -func (c *carbonFD) Checkpoint() error { - return index.Save(c.idx, c.path) -} diff --git a/ipld/car/v2/internal/carbon/doc.go b/ipld/car/v2/internal/carbon/doc.go deleted file mode 100644 index e620f4d00..000000000 --- a/ipld/car/v2/internal/carbon/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// Package carbon provides a blockstore interface with saved blocks stored to a car-compatible log. -// A carbs index for the resulting file is tracked and can be saved as needed. -// Note, carbon does not support deletion. -// See: https://github.com/ipfs/go-ipfs-blockstore -// TODO to be refactored -package carbon diff --git a/ipld/car/v2/internal/carbon/poswriter.go b/ipld/car/v2/internal/carbon/poswriter.go deleted file mode 100644 index 5b9444e5a..000000000 --- a/ipld/car/v2/internal/carbon/poswriter.go +++ /dev/null @@ -1,14 +0,0 @@ -package carbon - -import "io" - -type poswriter struct { - io.Writer - at uint64 -} - -func (p *poswriter) Write(b []byte) (n int, err error) { - n, err = p.Writer.Write(b) - p.at += uint64(n) - return -} diff --git a/ipld/car/v2/internal/carbs/util/hydrate.go b/ipld/car/v2/internal/carbs/util/hydrate.go index 7a16fcea5..c1e8533a6 100644 --- a/ipld/car/v2/internal/carbs/util/hydrate.go +++ b/ipld/car/v2/internal/carbs/util/hydrate.go @@ -29,13 +29,7 @@ func main() { return } - dbstat, err := os.Stat(db) - if err != nil { - fmt.Printf("Error statting car for hydration: %v\n", err) - return - } - - idx, err := index.GenerateIndex(dbBacking, dbstat.Size(), codec) + idx, err := index.Generate(dbBacking, codec) if err != nil { fmt.Printf("Error generating index: %v\n", err) return diff --git a/ipld/car/v2/internal/io/offset_writer.go b/ipld/car/v2/internal/io/offset_writer.go index 495190a17..1dd810165 100644 --- a/ipld/car/v2/internal/io/offset_writer.go +++ b/ipld/car/v2/internal/io/offset_writer.go @@ -2,15 +2,25 @@ package io import "io" -var _ io.Writer = (*offsetWriter)(nil) +var _ io.Writer = (*OffsetWriter)(nil) -type offsetWriter struct { - wa io.WriterAt +type OffsetWriter struct { + w io.WriterAt + base int64 offset int64 } -func (ow *offsetWriter) Write(b []byte) (n int, err error) { - n, err = ow.wa.WriteAt(b, ow.offset) +func NewOffsetWriter(w io.WriterAt, off int64) *OffsetWriter { + return &OffsetWriter{w, off, off} +} + +func (ow *OffsetWriter) Write(b []byte) (n int, err error) { + n, err = ow.w.WriteAt(b, ow.offset) ow.offset += int64(n) return } + +// Position returns the current position of this writer relative to the initial offset, i.e. the number of bytes written. +func (ow *OffsetWriter) Position() int64 { + return ow.offset - ow.base +} diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index c832570b2..2f801bb9b 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -54,7 +54,7 @@ func (r *Reader) Roots() ([]cid.Cid, error) { if r.roots != nil { return r.roots, nil } - header, err := carv1.ReadHeader(bufio.NewReader(r.carv1SectionReader())) + header, err := carv1.ReadHeader(bufio.NewReader(r.CarV1Reader())) if err != nil { return nil, err } @@ -68,17 +68,13 @@ func (r *Reader) readHeader() (err error) { return } -// CarV1ReaderAt provides an io.ReaderAt containing the CAR v1 dump encapsulated in this CAR v2. -func (r *Reader) CarV1ReaderAt() io.ReaderAt { - return r.carv1SectionReader() -} - -func (r *Reader) carv1SectionReader() *io.SectionReader { +// CarV1Reader provides a reader containing the CAR v1 section encapsulated in this CAR v2. +func (r *Reader) CarV1Reader() *io.SectionReader { return io.NewSectionReader(r.r, int64(r.Header.CarV1Offset), int64(r.Header.CarV1Size)) } -// IndexReaderAt provides an io.ReaderAt containing the carbs.Index of this CAR v2. -func (r *Reader) IndexReaderAt() io.ReaderAt { +// IndexReader provides an io.Reader containing the carbs.Index of this CAR v2. +func (r *Reader) IndexReader() io.Reader { return internalio.NewOffsetReader(r.r, int64(r.Header.IndexOffset)) } diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index 51d404798..fba4edcd8 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -77,7 +77,7 @@ func (w *Writer) WriteTo(writer io.Writer) (n int64, err error) { return } n += int64(PragmaSize) - // We read the entire car into memory because carbs.GenerateIndex takes a reader. + // We read the entire car into memory because carbs.Generate takes a reader. // Future PRs will make this more efficient by exposing necessary interfaces in carbs so that // this can be done in an streaming manner. if err = carv1.WriteCarWithWalker(w.ctx, w.NodeGetter, w.roots, w.encodedCarV1, w.Walk); err != nil { @@ -130,7 +130,7 @@ func (w *Writer) writeIndex(writer io.Writer, carV1 []byte) (n int64, err error) // Consider refactoring carbs to make this process more efficient. // We should avoid reading the entire car into memory since it can be large. reader := bytes.NewReader(carV1) - index, err := index.GenerateIndex(reader, int64(len(carV1)), index.IndexSorted) + index, err := index.Generate(reader, index.IndexSorted) if err != nil { return } From b4491513c900276f1a672874f900dea96852c70a Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 23 Jun 2021 17:03:12 +0100 Subject: [PATCH 3442/3817] Format code using gofumpt Use consistent code formatting by running `gofumpt -l -w .` This commit was moved from ipld/go-car@1e464d9e7ea76b68142d25e422fd9df07810a06a --- ipld/car/v2/blockstore/blockstore.go | 6 ++++-- ipld/car/v2/blockstore/blockstore_test.go | 4 ++-- ipld/car/v2/blockstore/readonly.go | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ipld/car/v2/blockstore/blockstore.go b/ipld/car/v2/blockstore/blockstore.go index 00f5bde85..fdc537b39 100644 --- a/ipld/car/v2/blockstore/blockstore.go +++ b/ipld/car/v2/blockstore/blockstore.go @@ -19,8 +19,10 @@ import ( "github.com/ipld/go-car/util" ) -var _ (blockstore.Blockstore) = (*Blockstore)(nil) -var errFinalized = errors.New("finalized blockstore") +var ( + _ (blockstore.Blockstore) = (*Blockstore)(nil) + errFinalized = errors.New("finalized blockstore") +) // Blockstore is a carbon implementation based on having two file handles opened, // one appending to the file, and the other diff --git a/ipld/car/v2/blockstore/blockstore_test.go b/ipld/car/v2/blockstore/blockstore_test.go index 76073b86b..b08ab29c1 100644 --- a/ipld/car/v2/blockstore/blockstore_test.go +++ b/ipld/car/v2/blockstore/blockstore_test.go @@ -1,12 +1,13 @@ package blockstore_test import ( - "github.com/ipld/go-car/v2/blockstore" "io" "math/rand" "os" "testing" + "github.com/ipld/go-car/v2/blockstore" + "github.com/ipfs/go-cid" carv1 "github.com/ipld/go-car" ) @@ -20,7 +21,6 @@ func TestBlockstore(t *testing.T) { defer f.Close() r, err := carv1.NewCarReader(f) - if err != nil { t.Fatal(err) } diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 849df2e53..20d46f1cf 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -6,6 +6,8 @@ import ( "encoding/binary" "errors" "fmt" + "io" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" @@ -15,7 +17,6 @@ import ( "github.com/ipld/go-car/v2/index" internalio "github.com/ipld/go-car/v2/internal/io" "golang.org/x/exp/mmap" - "io" ) var _ blockstore.Blockstore = (*ReadOnly)(nil) From a7bd9e283c84da2ad91371ec4cb26d30762bad42 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 23 Jun 2021 17:16:28 +0100 Subject: [PATCH 3443/3817] Fix attach index in ReadOnly blockstore Implement ability to attach index to an existing car v2 file. Fix bug in ReadOnly blockstore where the generated index was being saved as a separate file rather than being added to the CAR v2 file. Address PR comments - Add TODOs for future iterations to unify options and add interfaces - Fix failing skipped test for readwrite blockstore.go - Rename readwrite blockstore for consistency This commit was moved from ipld/go-car@3ab265837e67932e76781a6028a52f52254a9a0b --- ipld/car/v2/blockstore/readonly.go | 2 +- .../{blockstore.go => readwrite.go} | 82 +++++++++---------- .../{blockstore_test.go => readwrite_test.go} | 34 +++----- ipld/car/v2/car.go | 8 +- ipld/car/v2/index/index.go | 17 +++- ipld/car/v2/reader.go | 7 +- 6 files changed, 73 insertions(+), 77 deletions(-) rename ipld/car/v2/blockstore/{blockstore.go => readwrite.go} (63%) rename ipld/car/v2/blockstore/{blockstore_test.go => readwrite_test.go} (73%) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 20d46f1cf..e294f3f63 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -54,7 +54,7 @@ func OpenReadOnly(path string, noPersist bool) (*ReadOnly, error) { return nil, err } if !noPersist { - if err = index.Save(idx, path); err != nil { + if err := index.Attach(path, idx, v2r.Header.IndexOffset); err != nil { return nil, err } } diff --git a/ipld/car/v2/blockstore/blockstore.go b/ipld/car/v2/blockstore/readwrite.go similarity index 63% rename from ipld/car/v2/blockstore/blockstore.go rename to ipld/car/v2/blockstore/readwrite.go index fdc537b39..7bee5b734 100644 --- a/ipld/car/v2/blockstore/blockstore.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "io" "os" blockstore "github.com/ipfs/go-ipfs-blockstore" @@ -20,49 +19,46 @@ import ( ) var ( - _ (blockstore.Blockstore) = (*Blockstore)(nil) - errFinalized = errors.New("finalized blockstore") + _ blockstore.Blockstore = (*ReadWrite)(nil) + errFinalized = errors.New("finalized blockstore") ) -// Blockstore is a carbon implementation based on having two file handles opened, +// ReadWrite is a carbon implementation based on having two file handles opened, // one appending to the file, and the other // seeking to read items as needed. // This implementation is preferable for a write-heavy workload. // The Finalize function must be called once the putting blocks are finished. // Upon calling Finalize all read and write calls to this blockstore will result in error. type ( - Blockstore struct { - w io.WriterAt + // TODO consider exposing interfaces + ReadWrite struct { + f *os.File carV1Wrtier *internalio.OffsetWriter ReadOnly idx *index.InsertionIndex header carv2.Header } - Option func(*Blockstore) + Option func(*ReadWrite) // TODO consider unifying with writer options ) func WithCarV1Padding(p uint64) Option { - return func(b *Blockstore) { + return func(b *ReadWrite) { b.header = b.header.WithCarV1Padding(p) } } func WithIndexPadding(p uint64) Option { - return func(b *Blockstore) { + return func(b *ReadWrite) { b.header = b.header.WithIndexPadding(p) } } -// New creates a new Blockstore at the given path with a provided set of root cids as the car roots. -func New(path string, roots []cid.Cid, opts ...Option) (*Blockstore, error) { +// NewReadWrite creates a new ReadWrite at the given path with a provided set of root cids as the car roots. +func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, error) { // TODO support resumption if the path provided contains partially written blocks in v2 format. - wfd, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o666) + f, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0o666) if err != nil { - return nil, fmt.Errorf("couldn't create backing car: %w", err) - } - rfd, err := os.OpenFile(path, os.O_RDONLY, 0o666) - if err != nil { - return nil, fmt.Errorf("could not re-open read handle: %w", err) + return nil, fmt.Errorf("could not open read/write file: %w", err) } indexcls, ok := index.IndexAtlas[index.IndexInsertion] @@ -71,17 +67,18 @@ func New(path string, roots []cid.Cid, opts ...Option) (*Blockstore, error) { } idx := (indexcls()).(*index.InsertionIndex) - b := &Blockstore{ - w: wfd, - ReadOnly: *ReadOnlyOf(rfd, idx), - idx: idx, - header: carv2.Header{}, + b := &ReadWrite{ + f: f, + idx: idx, + header: carv2.NewHeader(0), } - - applyOptions(b, opts) - b.carV1Wrtier = internalio.NewOffsetWriter(wfd, int64(b.header.CarV1Offset)) - - if _, err := wfd.Write(carv2.Pragma); err != nil { + for _, opt := range opts { + opt(b) + } + b.carV1Wrtier = internalio.NewOffsetWriter(f, int64(b.header.CarV1Offset)) + carV1Reader := internalio.NewOffsetReader(f, int64(b.header.CarV1Offset)) + b.ReadOnly = *ReadOnlyOf(carV1Reader, idx) + if _, err := f.WriteAt(carv2.Pragma, 0); err != nil { return nil, err } @@ -95,18 +92,12 @@ func New(path string, roots []cid.Cid, opts ...Option) (*Blockstore, error) { return b, nil } -func applyOptions(b *Blockstore, opts []Option) { - for _, opt := range opts { - opt(b) - } -} - -func (b *Blockstore) DeleteBlock(cid.Cid) error { +func (b *ReadWrite) DeleteBlock(cid.Cid) error { return errUnsupported } // Put puts a given block to the underlying datastore -func (b *Blockstore) Put(blk blocks.Block) error { +func (b *ReadWrite) Put(blk blocks.Block) error { if b.isFinalized() { return errFinalized } @@ -115,7 +106,7 @@ func (b *Blockstore) Put(blk blocks.Block) error { // PutMany puts a slice of blocks at the same time using batching // capabilities of the underlying datastore whenever possible. -func (b *Blockstore) PutMany(blks []blocks.Block) error { +func (b *ReadWrite) PutMany(blks []blocks.Block) error { if b.isFinalized() { return errFinalized } @@ -129,21 +120,22 @@ func (b *Blockstore) PutMany(blks []blocks.Block) error { return nil } -func (b *Blockstore) isFinalized() bool { +func (b *ReadWrite) isFinalized() bool { return b.header.CarV1Size != 0 } // Finalize finalizes this blockstore by writing the CAR v2 header, along with flattened index // for more efficient subsequent read. // After this call, this blockstore can no longer be used for read or write. -func (b *Blockstore) Finalize() error { +func (b *ReadWrite) Finalize() error { if b.isFinalized() { return errFinalized } // TODO check if add index option is set and don't write the index then set index offset to zero. // TODO see if folks need to continue reading from a finalized blockstore, if so return ReadOnly blockstore here. - b.header.CarV1Size = uint64(b.carV1Wrtier.Position()) - if _, err := b.header.WriteTo(internalio.NewOffsetWriter(b.w, carv2.PragmaSize)); err != nil { + b.header = b.header.WithCarV1Size(uint64(b.carV1Wrtier.Position())) + defer b.f.Close() + if _, err := b.header.WriteTo(internalio.NewOffsetWriter(b.f, carv2.PragmaSize)); err != nil { return err } // TODO if index not needed don't bother flattening it. @@ -151,31 +143,31 @@ func (b *Blockstore) Finalize() error { if err != nil { return err } - return index.WriteTo(fi, internalio.NewOffsetWriter(b.w, int64(b.header.IndexOffset))) + return index.WriteTo(fi, internalio.NewOffsetWriter(b.f, int64(b.header.IndexOffset))) } -func (b *Blockstore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { +func (b *ReadWrite) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { if b.isFinalized() { return nil, errFinalized } return b.ReadOnly.AllKeysChan(ctx) } -func (b *Blockstore) Has(key cid.Cid) (bool, error) { +func (b *ReadWrite) Has(key cid.Cid) (bool, error) { if b.isFinalized() { return false, errFinalized } return b.ReadOnly.Has(key) } -func (b *Blockstore) Get(key cid.Cid) (blocks.Block, error) { +func (b *ReadWrite) Get(key cid.Cid) (blocks.Block, error) { if b.isFinalized() { return nil, errFinalized } return b.ReadOnly.Get(key) } -func (b *Blockstore) GetSize(key cid.Cid) (int, error) { +func (b *ReadWrite) GetSize(key cid.Cid) (int, error) { if b.isFinalized() { return 0, errFinalized } diff --git a/ipld/car/v2/blockstore/blockstore_test.go b/ipld/car/v2/blockstore/readwrite_test.go similarity index 73% rename from ipld/car/v2/blockstore/blockstore_test.go rename to ipld/car/v2/blockstore/readwrite_test.go index b08ab29c1..81d287335 100644 --- a/ipld/car/v2/blockstore/blockstore_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -4,8 +4,11 @@ import ( "io" "math/rand" "os" + "path/filepath" "testing" + "github.com/stretchr/testify/assert" + "github.com/ipld/go-car/v2/blockstore" "github.com/ipfs/go-cid" @@ -13,25 +16,17 @@ import ( ) func TestBlockstore(t *testing.T) { - f, err := os.Open("../carbs/testdata/test.car") - if err != nil { - t.Skipf("fixture not found: %q", err) - return - } + tempDir := t.TempDir() + f, err := os.Open("testdata/test.car") + assert.NoError(t, err) defer f.Close() - r, err := carv1.NewCarReader(f) + assert.NoError(t, err) + path := filepath.Join(tempDir, "/testv2blockstore.car") + ingester, err := blockstore.NewReadWrite(path, r.Header.Roots) if err != nil { t.Fatal(err) } - path := "testv2blockstore.car" - ingester, err := blockstore.New(path, r.Header.Roots) - if err != nil { - t.Fatal(err) - } - defer func() { - os.Remove(path) - }() cids := make([]cid.Cid, 0) for { @@ -39,6 +34,8 @@ func TestBlockstore(t *testing.T) { if err == io.EOF { break } + assert.NoError(t, err) + if err := ingester.Put(b); err != nil { t.Fatal(err) } @@ -64,15 +61,6 @@ func TestBlockstore(t *testing.T) { if err := ingester.Finalize(); err != nil { t.Fatal(err) } - - stat, err := os.Stat("testcarbon.car.idx") - if err != nil { - t.Fatal(err) - } - if stat.Size() <= 0 { - t.Fatalf("index not written: %v", stat) - } - carb, err := blockstore.OpenReadOnly(path, true) if err != nil { t.Fatal(err) diff --git a/ipld/car/v2/car.go b/ipld/car/v2/car.go index 24edbcdfe..4d8c3f50a 100644 --- a/ipld/car/v2/car.go +++ b/ipld/car/v2/car.go @@ -88,11 +88,17 @@ func (h Header) WithIndexPadding(padding uint64) Header { // The CAR v1 offset is calculated as the sum of PragmaSize, HeaderSize and the given padding. // The call to this function also shifts the Header.IndexOffset forward by the given padding. func (h Header) WithCarV1Padding(padding uint64) Header { - h.CarV1Offset = h.CarV1Offset + padding + h.CarV1Offset = PragmaSize + HeaderSize + padding h.IndexOffset = h.IndexOffset + padding return h } +func (h Header) WithCarV1Size(size uint64) Header { + h.CarV1Size = size + h.IndexOffset = size + h.IndexOffset + return h +} + // HasIndex indicates whether the index is present. func (h Header) HasIndex() bool { return h.IndexOffset != 0 diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 5ecdfbf56..edaf271e3 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -7,6 +7,8 @@ import ( "io" "os" + internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/ipfs/go-cid" ) @@ -52,13 +54,24 @@ var IndexAtlas = map[Codec]IndexCls{ } // Save writes a generated index into the given `path` as a file with a `.idx` extension. -func Save(i Index, path string) error { +func Save(idx Index, path string) error { stream, err := os.OpenFile(path+".idx", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o640) if err != nil { return err } defer stream.Close() - return WriteTo(i, stream) + return WriteTo(idx, stream) +} + +// Attach attaches a given index to an existing car v2 file at given path and offset. +func Attach(path string, idx Index, offset uint64) error { + out, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o640) + if err != nil { + return err + } + defer out.Close() + indexWriter := internalio.NewOffsetWriter(out, int64(offset)) + return WriteTo(idx, indexWriter) } func WriteTo(i Index, w io.Writer) error { diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 2f801bb9b..06b89936d 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -43,9 +43,6 @@ func (r *Reader) requireV2Pragma() (err error) { if version != 2 { return fmt.Errorf("invalid car version: %d", version) } - if or.Offset() != PragmaSize { - err = fmt.Errorf("invalid car v2 pragma; size %d is larger than expected %d", or.Offset(), PragmaSize) - } return } @@ -69,12 +66,12 @@ func (r *Reader) readHeader() (err error) { } // CarV1Reader provides a reader containing the CAR v1 section encapsulated in this CAR v2. -func (r *Reader) CarV1Reader() *io.SectionReader { +func (r *Reader) CarV1Reader() *io.SectionReader { // TODO consider returning io.Reader+ReaderAt in a custom interface return io.NewSectionReader(r.r, int64(r.Header.CarV1Offset), int64(r.Header.CarV1Size)) } // IndexReader provides an io.Reader containing the carbs.Index of this CAR v2. -func (r *Reader) IndexReader() io.Reader { +func (r *Reader) IndexReader() io.Reader { // TODO consider returning io.Reader+ReaderAt in a custom interface return internalio.NewOffsetReader(r.r, int64(r.Header.IndexOffset)) } From cd8a143d96bba9514f88abe6fbede74a881dace8 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 23 Jun 2021 23:56:47 +0100 Subject: [PATCH 3444/3817] Revert use of t.TempDir due to access issue in windows In windows clean up of test temp directories fail due to: - Access is denied. Revert the changes to push the PR forward until we investigate why. This commit was moved from ipld/go-car@8a3fdb1ab190c8c3be3952cca03e566d60401d8c --- ipld/car/v2/blockstore/readwrite_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 81d287335..5b6de5251 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -4,7 +4,6 @@ import ( "io" "math/rand" "os" - "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -16,17 +15,19 @@ import ( ) func TestBlockstore(t *testing.T) { - tempDir := t.TempDir() f, err := os.Open("testdata/test.car") assert.NoError(t, err) defer f.Close() r, err := carv1.NewCarReader(f) assert.NoError(t, err) - path := filepath.Join(tempDir, "/testv2blockstore.car") + path := "testv2blockstore.car" ingester, err := blockstore.NewReadWrite(path, r.Header.Roots) if err != nil { t.Fatal(err) } + defer func() { + os.Remove(path) + }() cids := make([]cid.Cid, 0) for { From 645e7dda2827121a6807e7a2bd4a1c8b1174c39d Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 24 Jun 2021 10:52:02 +0100 Subject: [PATCH 3445/3817] Rename `ReadPragma` to `ReadVersion` and drop returning roots Change the `ReadPragma` signature to only return the version and drop the roots that _may_ exist if the version is 1. Because, this results in a less error-prone API, and right now it is unclear if we want functionality that should return roots. So, we lean towards providing less functionality unless requested. This commit was moved from ipld/go-car@6bd284c98d5cf558da326e8d68c4277ee6bc4d06 --- ipld/car/v2/reader.go | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 06b89936d..04b471ae8 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -25,7 +25,7 @@ func NewReader(r io.ReaderAt) (*Reader, error) { cr := &Reader{ r: r, } - if err := cr.requireV2Pragma(); err != nil { + if err := cr.requireVersion2(); err != nil { return nil, err } if err := cr.readHeader(); err != nil { @@ -34,9 +34,9 @@ func NewReader(r io.ReaderAt) (*Reader, error) { return cr, nil } -func (r *Reader) requireV2Pragma() (err error) { +func (r *Reader) requireVersion2() (err error) { or := internalio.NewOffsetReader(r.r, 0) - version, _, err := ReadPragma(or) + version, err := ReadVersion(or) if err != nil { return } @@ -75,18 +75,13 @@ func (r *Reader) IndexReader() io.Reader { // TODO consider returning io.Reader+ return internalio.NewOffsetReader(r.r, int64(r.Header.IndexOffset)) } -// ReadPragma reads the pragma from r. +// ReadVersion reads the version from the pragma. // This function accepts both CAR v1 and v2 payloads. -// The roots are returned only if the version of pragma equals 1, otherwise returns nil as roots. -func ReadPragma(r io.Reader) (version uint64, roots []cid.Cid, err error) { +func ReadVersion(r io.Reader) (version uint64, err error) { // TODO if the user provides a reader that sufficiently satisfies what carv1.ReadHeader is asking then use that instead of wrapping every time. header, err := carv1.ReadHeader(bufio.NewReader(r)) if err != nil { return } - version = header.Version - if version == 1 { - roots = header.Roots - } - return + return header.Version, nil } From 4857fa30a282b663536d9955d842cb18e9893669 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 24 Jun 2021 12:53:48 +0100 Subject: [PATCH 3446/3817] Remove dependency to CAR v1 go module Remove dependency to CAR v1 go module, because that module depends on later version of ipld-prime which have not been reflected in filecoin project. In the interest of time, we fork the code that v2 needs in v1. This allows a more efficient implementation of some APIs that accep types like `bufio.Reader` while helping the filecoin team push ahead with deliverables without undergoing a big upgrade process. On the CAR v2 side, this however means that we postpone the implementation of Selective Car for v2 until filecoin is upgraded its ipld-prime dependency. This is to avoid implementing an obsolete selective car API that uses the old ipld-prime API which force user yet another upgrade. This commit was moved from ipld/go-car@ff99aafeec59dc062b7cb8af6619ae36df1af60e --- ipld/car/v2/blockstore/readonly.go | 4 +- ipld/car/v2/blockstore/readwrite.go | 4 +- ipld/car/v2/blockstore/readwrite_test.go | 2 +- ipld/car/v2/car_test.go | 2 +- ipld/car/v2/index/generator.go | 2 +- ipld/car/v2/internal/carv1/car.go | 222 ++++++++++++++++++ ipld/car/v2/internal/carv1/car_test.go | 231 +++++++++++++++++++ ipld/car/v2/internal/carv1/doc.go | 2 + ipld/car/v2/internal/carv1/util/util.go | 121 ++++++++++ ipld/car/v2/internal/carv1/util/util_test.go | 27 +++ ipld/car/v2/reader.go | 2 +- ipld/car/v2/writer.go | 2 +- 12 files changed, 612 insertions(+), 9 deletions(-) create mode 100644 ipld/car/v2/internal/carv1/car.go create mode 100644 ipld/car/v2/internal/carv1/car_test.go create mode 100644 ipld/car/v2/internal/carv1/doc.go create mode 100644 ipld/car/v2/internal/carv1/util/util.go create mode 100644 ipld/car/v2/internal/carv1/util/util_test.go diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index e294f3f63..354f6dfb0 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -11,10 +11,10 @@ import ( blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" - carv1 "github.com/ipld/go-car" - "github.com/ipld/go-car/util" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-car/v2/internal/carv1" + "github.com/ipld/go-car/v2/internal/carv1/util" internalio "github.com/ipld/go-car/v2/internal/io" "golang.org/x/exp/mmap" ) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 7bee5b734..6b03ec51b 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -7,15 +7,15 @@ import ( "os" blockstore "github.com/ipfs/go-ipfs-blockstore" - carv1 "github.com/ipld/go-car" carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/internal/carv1" internalio "github.com/ipld/go-car/v2/internal/io" "github.com/ipld/go-car/v2/index" blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - "github.com/ipld/go-car/util" + "github.com/ipld/go-car/v2/internal/carv1/util" ) var ( diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 5b6de5251..63432431f 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -11,7 +11,7 @@ import ( "github.com/ipld/go-car/v2/blockstore" "github.com/ipfs/go-cid" - carv1 "github.com/ipld/go-car" + "github.com/ipld/go-car/v2/internal/carv1" ) func TestBlockstore(t *testing.T) { diff --git a/ipld/car/v2/car_test.go b/ipld/car/v2/car_test.go index 3a3606ff4..5fea95bbc 100644 --- a/ipld/car/v2/car_test.go +++ b/ipld/car/v2/car_test.go @@ -5,8 +5,8 @@ import ( "bytes" "testing" - carv1 "github.com/ipld/go-car" carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/internal/carv1" "github.com/stretchr/testify/assert" ) diff --git a/ipld/car/v2/index/generator.go b/ipld/car/v2/index/generator.go index 5c1ef7f87..5fad938cc 100644 --- a/ipld/car/v2/index/generator.go +++ b/ipld/car/v2/index/generator.go @@ -6,7 +6,7 @@ import ( "fmt" "io" - carv1 "github.com/ipld/go-car" + "github.com/ipld/go-car/v2/internal/carv1" internalio "github.com/ipld/go-car/v2/internal/io" "golang.org/x/exp/mmap" ) diff --git a/ipld/car/v2/internal/carv1/car.go b/ipld/car/v2/internal/carv1/car.go new file mode 100644 index 000000000..3886527ff --- /dev/null +++ b/ipld/car/v2/internal/carv1/car.go @@ -0,0 +1,222 @@ +package carv1 + +import ( + "bufio" + "context" + "fmt" + "io" + + "github.com/ipld/go-car/v2/internal/carv1/util" + + blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + cbor "github.com/ipfs/go-ipld-cbor" + format "github.com/ipfs/go-ipld-format" + dag "github.com/ipfs/go-merkledag" +) + +func init() { + cbor.RegisterCborType(CarHeader{}) +} + +type Store interface { + Put(blocks.Block) error +} + +type ReadStore interface { + Get(cid.Cid) (blocks.Block, error) +} + +type CarHeader struct { + Roots []cid.Cid + Version uint64 +} + +type carWriter struct { + ds format.NodeGetter + w io.Writer + walk WalkFunc +} + +type WalkFunc func(format.Node) ([]*format.Link, error) + +func WriteCar(ctx context.Context, ds format.NodeGetter, roots []cid.Cid, w io.Writer) error { + return WriteCarWithWalker(ctx, ds, roots, w, DefaultWalkFunc) +} + +func WriteCarWithWalker(ctx context.Context, ds format.NodeGetter, roots []cid.Cid, w io.Writer, walk WalkFunc) error { + h := &CarHeader{ + Roots: roots, + Version: 1, + } + + if err := WriteHeader(h, w); err != nil { + return fmt.Errorf("failed to write car header: %s", err) + } + + cw := &carWriter{ds: ds, w: w, walk: walk} + seen := cid.NewSet() + for _, r := range roots { + if err := dag.Walk(ctx, cw.enumGetLinks, r, seen.Visit); err != nil { + return err + } + } + return nil +} + +func DefaultWalkFunc(nd format.Node) ([]*format.Link, error) { + return nd.Links(), nil +} + +func ReadHeader(br *bufio.Reader) (*CarHeader, error) { + hb, err := util.LdRead(br) + if err != nil { + return nil, err + } + + var ch CarHeader + if err := cbor.DecodeInto(hb, &ch); err != nil { + return nil, fmt.Errorf("invalid header: %v", err) + } + + return &ch, nil +} + +func WriteHeader(h *CarHeader, w io.Writer) error { + hb, err := cbor.DumpObject(h) + if err != nil { + return err + } + + return util.LdWrite(w, hb) +} + +func HeaderSize(h *CarHeader) (uint64, error) { + hb, err := cbor.DumpObject(h) + if err != nil { + return 0, err + } + + return util.LdSize(hb), nil +} + +func (cw *carWriter) enumGetLinks(ctx context.Context, c cid.Cid) ([]*format.Link, error) { + nd, err := cw.ds.Get(ctx, c) + if err != nil { + return nil, err + } + + if err := cw.writeNode(ctx, nd); err != nil { + return nil, err + } + + return cw.walk(nd) +} + +func (cw *carWriter) writeNode(ctx context.Context, nd format.Node) error { + return util.LdWrite(cw.w, nd.Cid().Bytes(), nd.RawData()) +} + +type CarReader struct { + br *bufio.Reader + Header *CarHeader +} + +func NewCarReader(r io.Reader) (*CarReader, error) { + br := bufio.NewReader(r) + ch, err := ReadHeader(br) + if err != nil { + return nil, err + } + + if ch.Version != 1 { + return nil, fmt.Errorf("invalid car version: %d", ch.Version) + } + + if len(ch.Roots) == 0 { + return nil, fmt.Errorf("empty car, no roots") + } + + return &CarReader{ + br: br, + Header: ch, + }, nil +} + +func (cr *CarReader) Next() (blocks.Block, error) { + c, data, err := util.ReadNode(cr.br) + if err != nil { + return nil, err + } + + hashed, err := c.Prefix().Sum(data) + if err != nil { + return nil, err + } + + if !hashed.Equals(c) { + return nil, fmt.Errorf("mismatch in content integrity, name: %s, data: %s", c, hashed) + } + + return blocks.NewBlockWithCid(data, c) +} + +type batchStore interface { + PutMany([]blocks.Block) error +} + +func LoadCar(s Store, r io.Reader) (*CarHeader, error) { + cr, err := NewCarReader(r) + if err != nil { + return nil, err + } + + if bs, ok := s.(batchStore); ok { + return loadCarFast(bs, cr) + } + + return loadCarSlow(s, cr) +} + +func loadCarFast(s batchStore, cr *CarReader) (*CarHeader, error) { + var buf []blocks.Block + for { + blk, err := cr.Next() + if err != nil { + if err == io.EOF { + if len(buf) > 0 { + if err := s.PutMany(buf); err != nil { + return nil, err + } + } + return cr.Header, nil + } + return nil, err + } + + buf = append(buf, blk) + + if len(buf) > 1000 { + if err := s.PutMany(buf); err != nil { + return nil, err + } + buf = buf[:0] + } + } +} + +func loadCarSlow(s Store, cr *CarReader) (*CarHeader, error) { + for { + blk, err := cr.Next() + if err != nil { + if err == io.EOF { + return cr.Header, nil + } + return nil, err + } + + if err := s.Put(blk); err != nil { + return nil, err + } + } +} diff --git a/ipld/car/v2/internal/carv1/car_test.go b/ipld/car/v2/internal/carv1/car_test.go new file mode 100644 index 000000000..b637fcf43 --- /dev/null +++ b/ipld/car/v2/internal/carv1/car_test.go @@ -0,0 +1,231 @@ +package carv1 + +import ( + "bytes" + "context" + "encoding/hex" + "io" + "strings" + "testing" + + cid "github.com/ipfs/go-cid" + format "github.com/ipfs/go-ipld-format" + dag "github.com/ipfs/go-merkledag" + dstest "github.com/ipfs/go-merkledag/test" +) + +func assertAddNodes(t *testing.T, ds format.DAGService, nds ...format.Node) { + for _, nd := range nds { + if err := ds.Add(context.Background(), nd); err != nil { + t.Fatal(err) + } + } +} + +func TestRoundtrip(t *testing.T) { + dserv := dstest.Mock() + a := dag.NewRawNode([]byte("aaaa")) + b := dag.NewRawNode([]byte("bbbb")) + c := dag.NewRawNode([]byte("cccc")) + + nd1 := &dag.ProtoNode{} + nd1.AddNodeLink("cat", a) + + nd2 := &dag.ProtoNode{} + nd2.AddNodeLink("first", nd1) + nd2.AddNodeLink("dog", b) + + nd3 := &dag.ProtoNode{} + nd3.AddNodeLink("second", nd2) + nd3.AddNodeLink("bear", c) + + assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) + + buf := new(bytes.Buffer) + if err := WriteCar(context.Background(), dserv, []cid.Cid{nd3.Cid()}, buf); err != nil { + t.Fatal(err) + } + + bserv := dstest.Bserv() + ch, err := LoadCar(bserv.Blockstore(), buf) + if err != nil { + t.Fatal(err) + } + + if len(ch.Roots) != 1 { + t.Fatal("should have one root") + } + + if !ch.Roots[0].Equals(nd3.Cid()) { + t.Fatal("got wrong cid") + } + + bs := bserv.Blockstore() + for _, nd := range []format.Node{a, b, c, nd1, nd2, nd3} { + has, err := bs.Has(nd.Cid()) + if err != nil { + t.Fatal(err) + } + + if !has { + t.Fatal("should have cid in blockstore") + } + } +} + +func TestEOFHandling(t *testing.T) { + // fixture is a clean single-block, single-root CAR + fixture, err := hex.DecodeString("3aa265726f6f747381d82a58250001711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80b6776657273696f6e012c01711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80ba165646f646779f5") + if err != nil { + t.Fatal(err) + } + + load := func(t *testing.T, byts []byte) *CarReader { + cr, err := NewCarReader(bytes.NewReader(byts)) + if err != nil { + t.Fatal(err) + } + + blk, err := cr.Next() + if err != nil { + t.Fatal(err) + } + if blk.Cid().String() != "bafyreiavd7u6opdcm6tqmddpnrgmvfb4enxuwglhenejmchnwqvixd5ibm" { + t.Fatal("unexpected CID") + } + + return cr + } + + t.Run("CleanEOF", func(t *testing.T) { + cr := load(t, fixture) + + blk, err := cr.Next() + if err != io.EOF { + t.Fatal("Didn't get expected EOF") + } + if blk != nil { + t.Fatal("EOF returned expected block") + } + }) + + t.Run("BadVarint", func(t *testing.T) { + fixtureBadVarint := append(fixture, 160) + cr := load(t, fixtureBadVarint) + + blk, err := cr.Next() + if err != io.ErrUnexpectedEOF { + t.Fatal("Didn't get unexpected EOF") + } + if blk != nil { + t.Fatal("EOF returned unexpected block") + } + }) + + t.Run("TruncatedBlock", func(t *testing.T) { + fixtureTruncatedBlock := append(fixture, 100, 0, 0) + cr := load(t, fixtureTruncatedBlock) + + blk, err := cr.Next() + if err != io.ErrUnexpectedEOF { + t.Fatal("Didn't get unexpected EOF") + } + if blk != nil { + t.Fatal("EOF returned unexpected block") + } + }) +} + +func TestBadHeaders(t *testing.T) { + testCases := []struct { + name string + hex string + errStr string // either the whole error string + errPfx string // or just the prefix + }{ + { + "{version:2}", + "0aa16776657273696f6e02", + "invalid car version: 2", + "", + }, + { + // an unfortunate error because we don't use a pointer + "{roots:[baeaaaa3bmjrq]}", + "13a165726f6f747381d82a480001000003616263", + "invalid car version: 0", + "", + }, + { + "{version:\"1\",roots:[baeaaaa3bmjrq]}", + "1da265726f6f747381d82a4800010000036162636776657273696f6e6131", + "", "invalid header: ", + }, + { + "{version:1}", + "0aa16776657273696f6e01", + "empty car, no roots", + "", + }, + { + "{version:1,roots:{cid:baeaaaa3bmjrq}}", + "20a265726f6f7473a163636964d82a4800010000036162636776657273696f6e01", + "", + "invalid header: ", + }, + { + "{version:1,roots:[baeaaaa3bmjrq],blip:true}", + "22a364626c6970f565726f6f747381d82a4800010000036162636776657273696f6e01", + "", + "invalid header: ", + }, + { + "[1,[]]", + "03820180", + "", + "invalid header: ", + }, + { + // this is an unfortunate error, it'd be nice to catch it better but it's + // very unlikely we'd ever see this in practice + "null", + "01f6", + "", + "invalid car version: 0", + }, + } + + makeCar := func(t *testing.T, byts string) error { + fixture, err := hex.DecodeString(byts) + if err != nil { + t.Fatal(err) + } + _, err = NewCarReader(bytes.NewReader(fixture)) + return err + } + + t.Run("Sanity check {version:1,roots:[baeaaaa3bmjrq]}", func(t *testing.T) { + err := makeCar(t, "1ca265726f6f747381d82a4800010000036162636776657273696f6e01") + if err != nil { + t.Fatal(err) + } + }) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := makeCar(t, tc.hex) + if err == nil { + t.Fatal("expected error from bad header, didn't get one") + } + if tc.errStr != "" { + if err.Error() != tc.errStr { + t.Fatalf("bad error: %v", err) + } + } else { + if !strings.HasPrefix(err.Error(), tc.errPfx) { + t.Fatalf("bad error: %v", err) + } + } + }) + } +} diff --git a/ipld/car/v2/internal/carv1/doc.go b/ipld/car/v2/internal/carv1/doc.go new file mode 100644 index 000000000..a13ffdfc2 --- /dev/null +++ b/ipld/car/v2/internal/carv1/doc.go @@ -0,0 +1,2 @@ +// Forked from CAR v1 to avoid dependency to ipld-prime 0.9.0 due to outstanding upgrades in filecoin. +package carv1 diff --git a/ipld/car/v2/internal/carv1/util/util.go b/ipld/car/v2/internal/carv1/util/util.go new file mode 100644 index 000000000..08048f333 --- /dev/null +++ b/ipld/car/v2/internal/carv1/util/util.go @@ -0,0 +1,121 @@ +package util + +import ( + "bufio" + "bytes" + "encoding/binary" + "fmt" + "io" + + cid "github.com/ipfs/go-cid" + mh "github.com/multiformats/go-multihash" +) + +var cidv0Pref = []byte{0x12, 0x20} + +type BytesReader interface { + io.Reader + io.ByteReader +} + +// TODO: this belongs in the go-cid package +func ReadCid(buf []byte) (cid.Cid, int, error) { + if bytes.Equal(buf[:2], cidv0Pref) { + c, err := cid.Cast(buf[:34]) + return c, 34, err + } + + br := bytes.NewReader(buf) + + // assume cidv1 + vers, err := binary.ReadUvarint(br) + if err != nil { + return cid.Cid{}, 0, err + } + + // TODO: the go-cid package allows version 0 here as well + if vers != 1 { + return cid.Cid{}, 0, fmt.Errorf("invalid cid version number") + } + + codec, err := binary.ReadUvarint(br) + if err != nil { + return cid.Cid{}, 0, err + } + + mhr := mh.NewReader(br) + h, err := mhr.ReadMultihash() + if err != nil { + return cid.Cid{}, 0, err + } + + return cid.NewCidV1(codec, h), len(buf) - br.Len(), nil +} + +func ReadNode(br *bufio.Reader) (cid.Cid, []byte, error) { + data, err := LdRead(br) + if err != nil { + return cid.Cid{}, nil, err + } + + c, n, err := ReadCid(data) + if err != nil { + return cid.Cid{}, nil, err + } + + return c, data[n:], nil +} + +func LdWrite(w io.Writer, d ...[]byte) error { + var sum uint64 + for _, s := range d { + sum += uint64(len(s)) + } + + buf := make([]byte, 8) + n := binary.PutUvarint(buf, sum) + _, err := w.Write(buf[:n]) + if err != nil { + return err + } + + for _, s := range d { + _, err = w.Write(s) + if err != nil { + return err + } + } + + return nil +} + +func LdSize(d ...[]byte) uint64 { + var sum uint64 + for _, s := range d { + sum += uint64(len(s)) + } + buf := make([]byte, 8) + n := binary.PutUvarint(buf, sum) + return sum + uint64(n) +} + +func LdRead(r *bufio.Reader) ([]byte, error) { + if _, err := r.Peek(1); err != nil { // no more blocks, likely clean io.EOF + return nil, err + } + + l, err := binary.ReadUvarint(r) + if err != nil { + if err == io.EOF { + return nil, io.ErrUnexpectedEOF // don't silently pretend this is a clean EOF + } + return nil, err + } + + buf := make([]byte, l) + if _, err := io.ReadFull(r, buf); err != nil { + return nil, err + } + + return buf, nil +} diff --git a/ipld/car/v2/internal/carv1/util/util_test.go b/ipld/car/v2/internal/carv1/util/util_test.go new file mode 100644 index 000000000..76828be9a --- /dev/null +++ b/ipld/car/v2/internal/carv1/util/util_test.go @@ -0,0 +1,27 @@ +package util_test + +import ( + "bytes" + "math/rand" + "testing" + + "github.com/ipld/go-car/v2/internal/carv1/util" + + "github.com/stretchr/testify/require" +) + +func TestLdSize(t *testing.T) { + for i := 0; i < 5; i++ { + var buf bytes.Buffer + data := make([][]byte, 5) + for j := 0; j < 5; j++ { + data[j] = make([]byte, rand.Intn(30)) + _, err := rand.Read(data[j]) + require.NoError(t, err) + } + size := util.LdSize(data...) + err := util.LdWrite(&buf, data...) + require.NoError(t, err) + require.Equal(t, uint64(len(buf.Bytes())), size) + } +} diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 04b471ae8..f21d3fa04 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -8,7 +8,7 @@ import ( internalio "github.com/ipld/go-car/v2/internal/io" "github.com/ipfs/go-cid" - carv1 "github.com/ipld/go-car" + "github.com/ipld/go-car/v2/internal/carv1" ) // Reader represents a reader of CAR v2. diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index fba4edcd8..68749e724 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -7,8 +7,8 @@ import ( "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" - carv1 "github.com/ipld/go-car" "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-car/v2/internal/carv1" ) const bulkPaddingBytesSize = 1024 From 8d4d86a2dad46e39f44f960364036be9fad8193c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Thu, 24 Jun 2021 12:44:31 +0100 Subject: [PATCH 3447/3817] copy v1 license into the v2 module This will allow pkgsite to render the wip-v2 module. This commit was moved from ipld/go-car@567a4c6b505d811bab9e5bcf18b9d88044bf67d4 --- ipld/car/v2/LICENSE.md | 229 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 ipld/car/v2/LICENSE.md diff --git a/ipld/car/v2/LICENSE.md b/ipld/car/v2/LICENSE.md new file mode 100644 index 000000000..15601cba6 --- /dev/null +++ b/ipld/car/v2/LICENSE.md @@ -0,0 +1,229 @@ +The contents of this repository are Copyright (c) corresponding authors and +contributors, licensed under the `Permissive License Stack` meaning either of: + +- Apache-2.0 Software License: https://www.apache.org/licenses/LICENSE-2.0 + ([...4tr2kfsq](https://gateway.ipfs.io/ipfs/bafkreiankqxazcae4onkp436wag2lj3ccso4nawxqkkfckd6cg4tr2kfsq)) + +- MIT Software License: https://opensource.org/licenses/MIT + ([...vljevcba](https://gateway.ipfs.io/ipfs/bafkreiepofszg4gfe2gzuhojmksgemsub2h4uy2gewdnr35kswvljevcba)) + +You may not use the contents of this repository except in compliance +with one of the listed Licenses. For an extended clarification of the +intent behind the choice of Licensing please refer to +https://protocol.ai/blog/announcing-the-permissive-license-stack/ + +Unless required by applicable law or agreed to in writing, software +distributed under the terms listed in this notice is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +either express or implied. See each License for the specific language +governing permissions and limitations under that License. + + +`SPDX-License-Identifier: Apache-2.0 OR MIT` + +Verbatim copies of both licenses are included below: + +

Apache-2.0 Software License + +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS +``` +
+ +
MIT Software License + +``` +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` +
From f99c6aa884e01ace9eeaf3b08b5f39fc7ff303fb Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 24 Jun 2021 17:37:13 +0100 Subject: [PATCH 3448/3817] Remove CAR v1 walk function until it is explicitly asked for We want to simplify the API and not carry over any CAR v1 functionality we don't have to. We also want to encourage users to use the selective car functionality to achieve what walk function did once it is added. This commit was moved from ipld/go-car@2f36f2e4ada95242ac985fe87e604af0f36f4c04 --- ipld/car/v2/blockstore/readwrite.go | 1 + ipld/car/v2/internal/carv1/car.go | 19 ++++--------------- ipld/car/v2/writer.go | 8 ++------ 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 6b03ec51b..83bdcf0de 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -56,6 +56,7 @@ func WithIndexPadding(p uint64) Option { // NewReadWrite creates a new ReadWrite at the given path with a provided set of root cids as the car roots. func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, error) { // TODO support resumption if the path provided contains partially written blocks in v2 format. + // TODO either lock the file or open exclusively; can we do somethign to reduce edge cases. f, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0o666) if err != nil { return nil, fmt.Errorf("could not open read/write file: %w", err) diff --git a/ipld/car/v2/internal/carv1/car.go b/ipld/car/v2/internal/carv1/car.go index 3886527ff..d69f3a7b7 100644 --- a/ipld/car/v2/internal/carv1/car.go +++ b/ipld/car/v2/internal/carv1/car.go @@ -33,18 +33,11 @@ type CarHeader struct { } type carWriter struct { - ds format.NodeGetter - w io.Writer - walk WalkFunc + ds format.NodeGetter + w io.Writer } -type WalkFunc func(format.Node) ([]*format.Link, error) - func WriteCar(ctx context.Context, ds format.NodeGetter, roots []cid.Cid, w io.Writer) error { - return WriteCarWithWalker(ctx, ds, roots, w, DefaultWalkFunc) -} - -func WriteCarWithWalker(ctx context.Context, ds format.NodeGetter, roots []cid.Cid, w io.Writer, walk WalkFunc) error { h := &CarHeader{ Roots: roots, Version: 1, @@ -54,7 +47,7 @@ func WriteCarWithWalker(ctx context.Context, ds format.NodeGetter, roots []cid.C return fmt.Errorf("failed to write car header: %s", err) } - cw := &carWriter{ds: ds, w: w, walk: walk} + cw := &carWriter{ds: ds, w: w} seen := cid.NewSet() for _, r := range roots { if err := dag.Walk(ctx, cw.enumGetLinks, r, seen.Visit); err != nil { @@ -64,10 +57,6 @@ func WriteCarWithWalker(ctx context.Context, ds format.NodeGetter, roots []cid.C return nil } -func DefaultWalkFunc(nd format.Node) ([]*format.Link, error) { - return nd.Links(), nil -} - func ReadHeader(br *bufio.Reader) (*CarHeader, error) { hb, err := util.LdRead(br) if err != nil { @@ -110,7 +99,7 @@ func (cw *carWriter) enumGetLinks(ctx context.Context, c cid.Cid) ([]*format.Lin return nil, err } - return cw.walk(nd) + return nd.Links(), nil } func (cw *carWriter) writeNode(ctx context.Context, nd format.Node) error { diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index 68749e724..950acb121 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -20,7 +20,6 @@ type ( padding uint64 // Writer writes CAR v2 into a give io.Writer. Writer struct { - Walk carv1.WalkFunc IndexCodec index.Codec NodeGetter format.NodeGetter CarV1Padding uint64 @@ -57,10 +56,8 @@ func (p padding) WriteTo(w io.Writer) (n int64, err error) { // NewWriter instantiates a new CAR v2 writer. // The writer instantiated uses `carbs.IndexSorted` as the index codec, -// and `carv1.DefaultWalkFunc` as the default walk function. func NewWriter(ctx context.Context, ng format.NodeGetter, roots []cid.Cid) *Writer { return &Writer{ - Walk: carv1.DefaultWalkFunc, IndexCodec: index.IndexSorted, NodeGetter: ng, ctx: ctx, @@ -69,8 +66,7 @@ func NewWriter(ctx context.Context, ng format.NodeGetter, roots []cid.Cid) *Writ } } -// WriteTo writes the given root CIDs according to CAR v2 specification, traversing the DAG using the -// Writer.Walk function. +// WriteTo writes the given root CIDs according to CAR v2 specification. func (w *Writer) WriteTo(writer io.Writer) (n int64, err error) { _, err = writer.Write(Pragma) if err != nil { @@ -80,7 +76,7 @@ func (w *Writer) WriteTo(writer io.Writer) (n int64, err error) { // We read the entire car into memory because carbs.Generate takes a reader. // Future PRs will make this more efficient by exposing necessary interfaces in carbs so that // this can be done in an streaming manner. - if err = carv1.WriteCarWithWalker(w.ctx, w.NodeGetter, w.roots, w.encodedCarV1, w.Walk); err != nil { + if err = carv1.WriteCar(w.ctx, w.NodeGetter, w.roots, w.encodedCarV1); err != nil { return } carV1Len := w.encodedCarV1.Len() From 5ccf9fc6bd304741daeee78e4d75a564959369b4 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 24 Jun 2021 19:05:13 +0100 Subject: [PATCH 3449/3817] Fix the writer index codec to sorted Remove the ability to configure index codec in the CAR v2 writer to avoid unintended complexity, since in the first pass the only supported codec will be sorted. This commit was moved from ipld/go-car@a6127a0def08e0b0115c6f000d80db27c59ea01b --- ipld/car/v2/writer.go | 11 ++++++----- ipld/car/v2/writer_test.go | 2 -- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index 950acb121..1f3365e82 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -11,7 +11,10 @@ import ( "github.com/ipld/go-car/v2/internal/carv1" ) -const bulkPaddingBytesSize = 1024 +const ( + bulkPaddingBytesSize = 1024 + defaultIndexCodex = index.IndexSorted +) var bulkPadding = make([]byte, bulkPaddingBytesSize) @@ -20,7 +23,6 @@ type ( padding uint64 // Writer writes CAR v2 into a give io.Writer. Writer struct { - IndexCodec index.Codec NodeGetter format.NodeGetter CarV1Padding uint64 IndexPadding uint64 @@ -55,10 +57,9 @@ func (p padding) WriteTo(w io.Writer) (n int64, err error) { } // NewWriter instantiates a new CAR v2 writer. -// The writer instantiated uses `carbs.IndexSorted` as the index codec, +// The writer instantiated uses `carbs.IndexSorted` as the index codec. func NewWriter(ctx context.Context, ng format.NodeGetter, roots []cid.Cid) *Writer { return &Writer{ - IndexCodec: index.IndexSorted, NodeGetter: ng, ctx: ctx, roots: roots, @@ -126,7 +127,7 @@ func (w *Writer) writeIndex(writer io.Writer, carV1 []byte) (n int64, err error) // Consider refactoring carbs to make this process more efficient. // We should avoid reading the entire car into memory since it can be large. reader := bytes.NewReader(carV1) - index, err := index.Generate(reader, index.IndexSorted) + index, err := index.Generate(reader, defaultIndexCodex) if err != nil { return } diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index 1db27f9ad..2e48fc3ca 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -9,7 +9,6 @@ import ( format "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" dstest "github.com/ipfs/go-merkledag/test" - "github.com/ipld/go-car/v2/index" "github.com/stretchr/testify/assert" ) @@ -62,7 +61,6 @@ func TestNewWriter(t *testing.T) { dagService := dstest.Mock() wantRoots := generateRootCid(t, dagService) writer := NewWriter(context.Background(), dagService, wantRoots) - assert.Equal(t, index.IndexSorted, writer.IndexCodec) assert.Equal(t, wantRoots, writer.roots) } From 13bf5d7861ccf5ec97953c7acac46c3125bc9ef0 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 24 Jun 2021 21:41:06 +0100 Subject: [PATCH 3450/3817] Improve documentation and fix bug in writer Add documentation where it is sparse to help folks learn how to use the library easier. Unexport the ability to select codec, until we decide what codec we want to expose. Fix all encodings of index to `IndexSorted`. Fix a bug in writer where index is written by direct marshalling instead of index.WriteTo which includes index codec. Refactor in places for better readability. This commit was moved from ipld/go-car@c954c990bac2430cc8c04909d74e0a77bb77b069 --- ipld/car/v2/blockstore/doc.go | 14 ++++++++ ipld/car/v2/blockstore/readonly.go | 39 ++++++++++---------- ipld/car/v2/blockstore/readwrite.go | 20 +++++------ ipld/car/v2/blockstore/readwrite_test.go | 2 +- ipld/car/v2/index/doc.go | 12 +++++++ ipld/car/v2/index/errors.go | 5 +-- ipld/car/v2/index/generator.go | 27 ++++++-------- ipld/car/v2/index/index.go | 42 ++++++++++++---------- ipld/car/v2/index/insertionindex.go | 2 +- ipld/car/v2/internal/carbs/util/hydrate.go | 12 ++----- ipld/car/v2/reader.go | 2 +- ipld/car/v2/writer.go | 27 ++++++-------- 12 files changed, 110 insertions(+), 94 deletions(-) create mode 100644 ipld/car/v2/blockstore/doc.go create mode 100644 ipld/car/v2/index/doc.go diff --git a/ipld/car/v2/blockstore/doc.go b/ipld/car/v2/blockstore/doc.go new file mode 100644 index 000000000..c2cf62a80 --- /dev/null +++ b/ipld/car/v2/blockstore/doc.go @@ -0,0 +1,14 @@ +// package blockstore implements IPFS blockstore interface backed by a CAR file. +// This package provides two flavours of blockstore: ReadOnly and ReadWrite. +// +// The ReadOnly blockstore provides a read-only random access from a given data payload either in +// unindexed v1 format or indexed/unindexed v2 format: +// - ReadOnly.ReadOnlyOf can be used to instantiate a new read-only blockstore for a given CAR v1 +// data payload and an existing index. See index.Generate for index generation from CAR v1 +// payload. +// - ReadOnly.OpenReadOnly can be used to instantiate a new read-only blockstore for a given CAR v2 +// file with automatic index generation if the index is not present in the given file. This +// function can optionally attach the index to the given CAR v2 file. +// + +package blockstore diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 354f6dfb0..fa94531fd 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -26,34 +26,37 @@ var errUnsupported = errors.New("unsupported operation") // ReadOnly provides a read-only Car Block Store. type ReadOnly struct { + // The backing containing the CAR in v1 format backing io.ReaderAt - idx index.Index + // The CAR v1 content index + idx index.Index } -// ReadOnlyOf opens a carbs data store from an existing backing of the base data (i.e. CAR v1 payload) and index. +// ReadOnlyOf opens ReadOnly blockstore from an existing backing containing a CAR v1 payload and an existing index. +// The index for a CAR v1 payload can be separately generated using index.Generate. func ReadOnlyOf(backing io.ReaderAt, index index.Index) *ReadOnly { return &ReadOnly{backing, index} } // OpenReadOnly opens a read-only blockstore from a CAR v2 file, generating an index if it does not exist. -// If noPersist is set to false then the generated index is written into the CAR v2 file at path. -func OpenReadOnly(path string, noPersist bool) (*ReadOnly, error) { +// If attachIndex is set to true and the index is not present in the given CAR v2 file, +// then the generated index is written into the given path. +func OpenReadOnly(path string, attachIndex bool) (*ReadOnly, error) { reader, err := mmap.Open(path) if err != nil { return nil, err } - v2r, err := carv2.NewReader(reader) if err != nil { return nil, err } var idx index.Index if !v2r.Header.HasIndex() { - idx, err := index.Generate(v2r.CarV1Reader(), index.IndexSorted) + idx, err := index.Generate(v2r.CarV1Reader()) if err != nil { return nil, err } - if !noPersist { + if attachIndex { if err := index.Attach(path, idx, v2r.Header.IndexOffset); err != nil { return nil, err } @@ -71,17 +74,17 @@ func OpenReadOnly(path string, noPersist bool) (*ReadOnly, error) { return &obj, nil } -func (b *ReadOnly) read(idx int64) (cid.Cid, []byte, error) { +func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) { bcid, data, err := util.ReadNode(bufio.NewReader(internalio.NewOffsetReader(b.backing, idx))) return bcid, data, err } -// DeleteBlock is unsupported and always returns an error +// DeleteBlock is unsupported and always returns an error. func (b *ReadOnly) DeleteBlock(_ cid.Cid) error { return errUnsupported } -// Has indicates if the store has a cid +// Has indicates if the store contains a block that corresponds to the given key. func (b *ReadOnly) Has(key cid.Cid) (bool, error) { offset, err := b.idx.Get(key) if err != nil { @@ -99,13 +102,13 @@ func (b *ReadOnly) Has(key cid.Cid) (bool, error) { return c.Equals(key), nil } -// Get gets a block from the store +// Get gets a block corresponding to the given key. func (b *ReadOnly) Get(key cid.Cid) (blocks.Block, error) { offset, err := b.idx.Get(key) if err != nil { return nil, err } - entry, bytes, err := b.read(int64(offset)) + entry, bytes, err := b.readBlock(int64(offset)) if err != nil { // TODO Improve error handling; not all errors mean NotFound. return nil, blockstore.ErrNotFound @@ -116,7 +119,7 @@ func (b *ReadOnly) Get(key cid.Cid) (blocks.Block, error) { return blocks.NewBlockWithCid(bytes, key) } -// GetSize gets how big a item is +// GetSize gets the size of an item corresponding to the given key. func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { idx, err := b.idx.Get(key) if err != nil { @@ -137,17 +140,17 @@ func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { return int(l), err } -// Put is not supported and always returns an error +// Put is not supported and always returns an error. func (b *ReadOnly) Put(blocks.Block) error { return errUnsupported } -// PutMany is not supported and always returns an error +// PutMany is not supported and always returns an error. func (b *ReadOnly) PutMany([]blocks.Block) error { return errUnsupported } -// AllKeysChan returns the list of keys in the store +// AllKeysChan returns the list of keys in the CAR. func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // TODO we may use this walk for populating the index, and we need to be able to iterate keys in this way somewhere for index generation. In general though, when it's asked for all keys from a blockstore with an index, we should iterate through the index when possible rather than linear reads through the full car. header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReader(b.backing, 0))) @@ -187,11 +190,11 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return ch, nil } -// HashOnRead does nothing +// HashOnRead does nothing. func (b *ReadOnly) HashOnRead(bool) { } -// Roots returns the root CIDs of the backing car +// Roots returns the root CIDs of the backing CAR. func (b *ReadOnly) Roots() ([]cid.Cid, error) { header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReader(b.backing, 0))) if err != nil { diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 83bdcf0de..909993d30 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -23,12 +23,14 @@ var ( errFinalized = errors.New("finalized blockstore") ) -// ReadWrite is a carbon implementation based on having two file handles opened, -// one appending to the file, and the other -// seeking to read items as needed. +// ReadWrite implements a blockstore that stores blocks in CAR v2 format. +// Blocks put into the blockstore can be read back once they are successfully written. // This implementation is preferable for a write-heavy workload. +// The blocks are written immediately on Put and PutAll calls, while the index is stored in memory +// and updated incrementally. // The Finalize function must be called once the putting blocks are finished. -// Upon calling Finalize all read and write calls to this blockstore will result in error. +// Upon calling Finalize header is finalized and index is written out. +// Once finalized, all read and write calls to this blockstore will result in error. type ( // TODO consider exposing interfaces ReadWrite struct { @@ -41,19 +43,21 @@ type ( Option func(*ReadWrite) // TODO consider unifying with writer options ) +// WithCarV1Padding sets the padding to be added between CAR v2 header and its data payload on Finalize. func WithCarV1Padding(p uint64) Option { return func(b *ReadWrite) { b.header = b.header.WithCarV1Padding(p) } } +// WithIndexPadding sets the padding between data payload and its index on Finalize. func WithIndexPadding(p uint64) Option { return func(b *ReadWrite) { b.header = b.header.WithIndexPadding(p) } } -// NewReadWrite creates a new ReadWrite at the given path with a provided set of root cids as the car roots. +// NewReadWrite creates a new ReadWrite at the given path with a provided set of root CIDs as the car roots. func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, error) { // TODO support resumption if the path provided contains partially written blocks in v2 format. // TODO either lock the file or open exclusively; can we do somethign to reduce edge cases. @@ -62,7 +66,7 @@ func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, err return nil, fmt.Errorf("could not open read/write file: %w", err) } - indexcls, ok := index.IndexAtlas[index.IndexInsertion] + indexcls, ok := index.BuildersByCodec[index.IndexInsertion] if !ok { return nil, fmt.Errorf("unknownindex codec: %#v", index.IndexInsertion) } @@ -93,10 +97,6 @@ func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, err return b, nil } -func (b *ReadWrite) DeleteBlock(cid.Cid) error { - return errUnsupported -} - // Put puts a given block to the underlying datastore func (b *ReadWrite) Put(blk blocks.Block) error { if b.isFinalized() { diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 63432431f..f49bdf53f 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -62,7 +62,7 @@ func TestBlockstore(t *testing.T) { if err := ingester.Finalize(); err != nil { t.Fatal(err) } - carb, err := blockstore.OpenReadOnly(path, true) + carb, err := blockstore.OpenReadOnly(path, false) if err != nil { t.Fatal(err) } diff --git a/ipld/car/v2/index/doc.go b/ipld/car/v2/index/doc.go new file mode 100644 index 000000000..25a38c34c --- /dev/null +++ b/ipld/car/v2/index/doc.go @@ -0,0 +1,12 @@ +// package index provides indexing functionality for CAR v1 data payload represented as a mapping of +// CID to offset. This can then be used to implement random access over a CAR v1. +// +// Index can be written or read using the following static functions: index.WriteTo and +// index.ReadFrom. +// +// This package also provides functionality to generate an index from a given CAR v1 file using: +// index.Generate and index.GenerateFromFile +// +// In addition to the above, it provides functionality to attach an index to an index-less CAR v2 +// using index.Attach +package index diff --git a/ipld/car/v2/index/errors.go b/ipld/car/v2/index/errors.go index c45c24020..6d9c89c19 100644 --- a/ipld/car/v2/index/errors.go +++ b/ipld/car/v2/index/errors.go @@ -3,7 +3,8 @@ package index import "errors" var ( - // errNotFound is returned for lookups to entries that don't exist - errNotFound = errors.New("not found") + // errNotFound signals a record is not found in the index. + errNotFound = errors.New("not found") + // errUnsupported signals unsupported operation by an index. errUnsupported = errors.New("not supported") ) diff --git a/ipld/car/v2/index/generator.go b/ipld/car/v2/index/generator.go index 5fad938cc..b7621af85 100644 --- a/ipld/car/v2/index/generator.go +++ b/ipld/car/v2/index/generator.go @@ -11,13 +11,9 @@ import ( "golang.org/x/exp/mmap" ) -// Generate generates index given car v1 payload using the given codec. -func Generate(car io.ReaderAt, codec Codec) (Index, error) { - indexcls, ok := IndexAtlas[codec] - if !ok { - return nil, fmt.Errorf("unknown codec: %#v", codec) - } - +// Generate generates index for a given car in v1 format. +// The index can be stored using index.Save into a file or serialized using index.WriteTo. +func Generate(car io.ReaderAt) (Index, error) { header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReader(car, 0))) if err != nil { return nil, fmt.Errorf("error reading car header: %w", err) @@ -27,7 +23,7 @@ func Generate(car io.ReaderAt, codec Codec) (Index, error) { return nil, err } - idx := indexcls() + idx := mkSorted() records := make([]Record, 0) rdr := internalio.NewOffsetReader(car, int64(offset)) @@ -56,16 +52,13 @@ func Generate(car io.ReaderAt, codec Codec) (Index, error) { return idx, nil } -// GenerateFromFile walks a car v1 file and generates an index of cid->byte offset, then -// stors it in a separate file at the given path with extension `.idx`. -func GenerateFromFile(path string, codec Codec) error { +// GenerateFromFile walks a car v1 file at the give path and generates an index of cid->byte offset. +// The index can be stored using index.Save into a file or serialized using index.WriteTo. +func GenerateFromFile(path string) (Index, error) { store, err := mmap.Open(path) if err != nil { - return err - } - idx, err := Generate(store, codec) - if err != nil { - return err + return nil, err } - return Save(idx, path) + defer store.Close() + return Generate(store) } diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index edaf271e3..971d581de 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -12,7 +12,7 @@ import ( "github.com/ipfs/go-cid" ) -// Codec table is a first var-int in carbs indexes +// Codec table is a first var-int in CAR indexes const ( IndexHashed Codec = iota + 0x300000 IndexSorted @@ -22,11 +22,11 @@ const ( ) type ( - // Codec is used as a multicodec identifier for carbs index files + // Codec is used as a multicodec identifier for CAR index files Codec int - // IndexCls is a constructor for an index type - IndexCls func() Index + // Builder is a constructor for an index type + Builder func() Index // Record is a pre-processed record of a car item and location. Record struct { @@ -34,7 +34,7 @@ type ( Idx uint64 } - // Index provides an interface for figuring out where in the car a given cid begins + // Index provides an interface for looking up byte offset of a given CID. Index interface { Codec() Codec Marshal(w io.Writer) error @@ -44,8 +44,8 @@ type ( } ) -// IndexAtlas holds known index formats -var IndexAtlas = map[Codec]IndexCls{ +// BuildersByCodec holds known index formats +var BuildersByCodec = map[Codec]Builder{ IndexHashed: mkHashed, IndexSorted: mkSorted, IndexSingleSorted: mkSingleSorted, @@ -53,9 +53,9 @@ var IndexAtlas = map[Codec]IndexCls{ IndexInsertion: mkInsertion, } -// Save writes a generated index into the given `path` as a file with a `.idx` extension. +// Save writes a generated index into the given `path`. func Save(idx Index, path string) error { - stream, err := os.OpenFile(path+".idx", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o640) + stream, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o640) if err != nil { return err } @@ -74,28 +74,34 @@ func Attach(path string, idx Index, offset uint64) error { return WriteTo(idx, indexWriter) } -func WriteTo(i Index, w io.Writer) error { +// WriteTo writes the given idx into w. +// The written bytes include the index encoding. +// This can then be read back using index.ReadFrom +func WriteTo(idx Index, w io.Writer) error { buf := make([]byte, binary.MaxVarintLen64) - b := binary.PutUvarint(buf, uint64(i.Codec())) + b := binary.PutUvarint(buf, uint64(idx.Codec())) if _, err := w.Write(buf[:b]); err != nil { return err } - return i.Marshal(w) + return idx.Marshal(w) } -func ReadFrom(uar io.Reader) (Index, error) { - reader := bufio.NewReader(uar) +// ReadFrom reads index from r. +// The reader decodes the index by reading the first byte to interpret the encoding. +// Returns error if the encoding is not known. +func ReadFrom(r io.Reader) (Index, error) { + reader := bufio.NewReader(r) codec, err := binary.ReadUvarint(reader) if err != nil { return nil, err } - idx, ok := IndexAtlas[Codec(codec)] + builder, ok := BuildersByCodec[Codec(codec)] if !ok { return nil, fmt.Errorf("unknown codec: %d", codec) } - idxInst := idx() - if err := idxInst.Unmarshal(reader); err != nil { + idx := builder() + if err := idx.Unmarshal(reader); err != nil { return nil, err } - return idxInst, nil + return idx, nil } diff --git a/ipld/car/v2/index/insertionindex.go b/ipld/car/v2/index/insertionindex.go index d64be077f..518210578 100644 --- a/ipld/car/v2/index/insertionindex.go +++ b/ipld/car/v2/index/insertionindex.go @@ -125,7 +125,7 @@ func mkInsertion() Index { // Flatten returns a 'indexsorted' formatted index for more efficient subsequent loading func (ii *InsertionIndex) Flatten() (Index, error) { - si := IndexAtlas[IndexSorted]() + si := BuildersByCodec[IndexSorted]() rcrds := make([]Record, ii.items.Len()) idx := 0 diff --git a/ipld/car/v2/internal/carbs/util/hydrate.go b/ipld/car/v2/internal/carbs/util/hydrate.go index c1e8533a6..eead86cf1 100644 --- a/ipld/car/v2/internal/carbs/util/hydrate.go +++ b/ipld/car/v2/internal/carbs/util/hydrate.go @@ -10,18 +10,10 @@ import ( func main() { if len(os.Args) < 2 { - fmt.Printf("Usage: hydrate [codec]\n") + fmt.Printf("Usage: hydrate \n") return } db := os.Args[1] - codec := index.IndexSorted - if len(os.Args) == 3 { - if os.Args[2] == "Hash" { - codec = index.IndexHashed - } else if os.Args[2] == "GobHash" { - codec = index.IndexGobHashed - } - } dbBacking, err := mmap.Open(db) if err != nil { @@ -29,7 +21,7 @@ func main() { return } - idx, err := index.Generate(dbBacking, codec) + idx, err := index.Generate(dbBacking) if err != nil { fmt.Printf("Error generating index: %v\n", err) return diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index f21d3fa04..c300f4873 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -70,7 +70,7 @@ func (r *Reader) CarV1Reader() *io.SectionReader { // TODO consider returning io return io.NewSectionReader(r.r, int64(r.Header.CarV1Offset), int64(r.Header.CarV1Size)) } -// IndexReader provides an io.Reader containing the carbs.Index of this CAR v2. +// IndexReader provides an io.Reader containing the index of this CAR v2. func (r *Reader) IndexReader() io.Reader { // TODO consider returning io.Reader+ReaderAt in a custom interface return internalio.NewOffsetReader(r.r, int64(r.Header.IndexOffset)) } diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index 1f3365e82..c425b2385 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -11,10 +11,7 @@ import ( "github.com/ipld/go-car/v2/internal/carv1" ) -const ( - bulkPaddingBytesSize = 1024 - defaultIndexCodex = index.IndexSorted -) +const bulkPaddingBytesSize = 1024 var bulkPadding = make([]byte, bulkPaddingBytesSize) @@ -57,7 +54,6 @@ func (p padding) WriteTo(w io.Writer) (n int64, err error) { } // NewWriter instantiates a new CAR v2 writer. -// The writer instantiated uses `carbs.IndexSorted` as the index codec. func NewWriter(ctx context.Context, ng format.NodeGetter, roots []cid.Cid) *Writer { return &Writer{ NodeGetter: ng, @@ -74,8 +70,8 @@ func (w *Writer) WriteTo(writer io.Writer) (n int64, err error) { return } n += int64(PragmaSize) - // We read the entire car into memory because carbs.Generate takes a reader. - // Future PRs will make this more efficient by exposing necessary interfaces in carbs so that + // We read the entire car into memory because index.Generate takes a reader. + // TODO Future PRs will make this more efficient by exposing necessary interfaces in index pacakge so that // this can be done in an streaming manner. if err = carv1.WriteCar(w.ctx, w.NodeGetter, w.roots, w.encodedCarV1); err != nil { return @@ -121,17 +117,16 @@ func (w *Writer) writeHeader(writer io.Writer, carV1Len int) (int64, error) { return header.WriteTo(writer) } -func (w *Writer) writeIndex(writer io.Writer, carV1 []byte) (n int64, err error) { - // TODO avoid recopying the bytes by refactoring carbs once it is integrated here. - // Right now we copy the bytes since carbs takes a writer. - // Consider refactoring carbs to make this process more efficient. +func (w *Writer) writeIndex(writer io.Writer, carV1 []byte) (int64, error) { + // TODO avoid recopying the bytes by refactoring index once it is integrated here. + // Right now we copy the bytes since index takes a writer. + // Consider refactoring index to make this process more efficient. // We should avoid reading the entire car into memory since it can be large. reader := bytes.NewReader(carV1) - index, err := index.Generate(reader, defaultIndexCodex) + idx, err := index.Generate(reader) if err != nil { - return + return 0, err } - err = index.Marshal(writer) - // FIXME refactor carbs to expose the number of bytes written. - return + // FIXME refactor index to expose the number of bytes written. + return 0, index.WriteTo(idx, writer) } From 3aeda868aaeb1e8eca512db9be54d9e03030adeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Fri, 25 Jun 2021 10:11:28 +0100 Subject: [PATCH 3451/3817] index: use the IndexSorted multicodec Also add TODOs about cleaning up the API. We can't do that right now, as it would require a refactor in the other packages. This commit was moved from ipld/go-car@48d5a7f2741fffc757e1e6be5ff829301a158a05 --- ipld/car/v2/index/index.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 971d581de..0b96c5f3d 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -14,8 +14,10 @@ import ( // Codec table is a first var-int in CAR indexes const ( - IndexHashed Codec = iota + 0x300000 - IndexSorted + IndexSorted Codec = 0x0400 // as per https://github.com/multiformats/multicodec/pull/220 + + // TODO: unexport these before the final release, probably + IndexHashed Codec = 0x300000 + iota IndexSingleSorted IndexGobHashed IndexInsertion @@ -23,6 +25,7 @@ const ( type ( // Codec is used as a multicodec identifier for CAR index files + // TODO: use go-multicodec before the final release Codec int // Builder is a constructor for an index type @@ -45,6 +48,7 @@ type ( ) // BuildersByCodec holds known index formats +// TODO: turn this into a func before the final release? var BuildersByCodec = map[Codec]Builder{ IndexHashed: mkHashed, IndexSorted: mkSorted, From fbce43c3c404984821d4e52d0cb1479f70c1eb17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Fri, 25 Jun 2021 13:56:11 +0100 Subject: [PATCH 3452/3817] add Close methods and NewReaderMmap Without close methods, it's currently impossible to close the underlying mmap file. NewReaderMmap is technically unnecessary, but also helpful. This commit was moved from ipld/go-car@cacae149167772d730b41040731f594d896171d9 --- ipld/car/v2/blockstore/readonly.go | 34 +++++++++++++++++--------- ipld/car/v2/reader.go | 39 ++++++++++++++++++++++++++---- 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index fa94531fd..14ff75d65 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -16,7 +16,6 @@ import ( "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/carv1/util" internalio "github.com/ipld/go-car/v2/internal/io" - "golang.org/x/exp/mmap" ) var _ blockstore.Blockstore = (*ReadOnly)(nil) @@ -26,30 +25,31 @@ var errUnsupported = errors.New("unsupported operation") // ReadOnly provides a read-only Car Block Store. type ReadOnly struct { - // The backing containing the CAR in v1 format + // The backing containing the CAR in v1 format. backing io.ReaderAt - // The CAR v1 content index + // The CAR v1 content index. idx index.Index + + // If we called carv2.NewReaderMmap, remember to close it too. + carv2Closer io.Closer } // ReadOnlyOf opens ReadOnly blockstore from an existing backing containing a CAR v1 payload and an existing index. // The index for a CAR v1 payload can be separately generated using index.Generate. func ReadOnlyOf(backing io.ReaderAt, index index.Index) *ReadOnly { - return &ReadOnly{backing, index} + return &ReadOnly{backing: backing, idx: index} } // OpenReadOnly opens a read-only blockstore from a CAR v2 file, generating an index if it does not exist. // If attachIndex is set to true and the index is not present in the given CAR v2 file, // then the generated index is written into the given path. func OpenReadOnly(path string, attachIndex bool) (*ReadOnly, error) { - reader, err := mmap.Open(path) - if err != nil { - return nil, err - } - v2r, err := carv2.NewReader(reader) + + v2r, err := carv2.NewReaderMmap(path) if err != nil { return nil, err } + var idx index.Index if !v2r.Header.HasIndex() { idx, err := index.Generate(v2r.CarV1Reader()) @@ -68,8 +68,9 @@ func OpenReadOnly(path string, attachIndex bool) (*ReadOnly, error) { } } obj := ReadOnly{ - backing: v2r.CarV1Reader(), - idx: idx, + backing: v2r.CarV1Reader(), + idx: idx, + carv2Closer: v2r, } return &obj, nil } @@ -190,8 +191,9 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return ch, nil } -// HashOnRead does nothing. +// HashOnRead is currently unimplemented; hashing on reads never happens. func (b *ReadOnly) HashOnRead(bool) { + // TODO: implement before the final release? } // Roots returns the root CIDs of the backing CAR. @@ -202,3 +204,11 @@ func (b *ReadOnly) Roots() ([]cid.Cid, error) { } return header.Roots, nil } + +// Close closes the underlying reader if it was opened by OpenReadOnly. +func (b *ReadOnly) Close() error { + if b.carv2Closer != nil { + return b.carv2Closer.Close() + } + return nil +} diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index c300f4873..d410ef879 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -9,13 +9,32 @@ import ( "github.com/ipfs/go-cid" "github.com/ipld/go-car/v2/internal/carv1" + "golang.org/x/exp/mmap" ) // Reader represents a reader of CAR v2. type Reader struct { - Header Header - r io.ReaderAt - roots []cid.Cid + Header Header + r io.ReaderAt + roots []cid.Cid + carv2Closer io.Closer +} + +// NewReaderMmap is a wrapper for NewReader which opens the file at path with +// x/exp/mmap. +func NewReaderMmap(path string) (*Reader, error) { + f, err := mmap.Open(path) + if err != nil { + return nil, err + } + + r, err := NewReader(f) + if err != nil { + return nil, err + } + + r.carv2Closer = f + return r, nil } // NewReader constructs a new reader that reads CAR v2 from the given r. @@ -66,15 +85,25 @@ func (r *Reader) readHeader() (err error) { } // CarV1Reader provides a reader containing the CAR v1 section encapsulated in this CAR v2. -func (r *Reader) CarV1Reader() *io.SectionReader { // TODO consider returning io.Reader+ReaderAt in a custom interface +func (r *Reader) CarV1Reader() *io.SectionReader { + // TODO consider returning io.Reader+ReaderAt in a custom interface return io.NewSectionReader(r.r, int64(r.Header.CarV1Offset), int64(r.Header.CarV1Size)) } // IndexReader provides an io.Reader containing the index of this CAR v2. -func (r *Reader) IndexReader() io.Reader { // TODO consider returning io.Reader+ReaderAt in a custom interface +func (r *Reader) IndexReader() io.Reader { + // TODO consider returning io.Reader+ReaderAt in a custom interface return internalio.NewOffsetReader(r.r, int64(r.Header.IndexOffset)) } +// Close closes the underlying reader if it was opened by NewReaderMmap. +func (r *Reader) Close() error { + if r.carv2Closer != nil { + return r.carv2Closer.Close() + } + return nil +} + // ReadVersion reads the version from the pragma. // This function accepts both CAR v1 and v2 payloads. func ReadVersion(r io.Reader) (version uint64, err error) { From 97a018a472f629b8561ac1f80d6e837181fb2117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Mon, 28 Jun 2021 09:03:38 +0100 Subject: [PATCH 3453/3817] blockstore: close AllKeysChan channel when done The added test case hangs without the fix. Updates #110. This commit was moved from ipld/go-car@73d15aedda1b0c82c30df18cff9fccc595fd7abf --- ipld/car/v2/blockstore/readonly.go | 11 ++++++----- ipld/car/v2/blockstore/readwrite_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 14ff75d65..a1bc8d1fd 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -163,27 +163,28 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return nil, err } + // TODO: document this choice of 5, or use simpler buffering like 0 or 1. ch := make(chan cid.Cid, 5) + go func() { - done := ctx.Done() + defer close(ch) rdr := internalio.NewOffsetReader(b.backing, int64(offset)) for { l, err := binary.ReadUvarint(rdr) thisItemForNxt := rdr.Offset() if err != nil { - return + return // TODO: log this error } c, _, err := internalio.ReadCid(b.backing, thisItemForNxt) if err != nil { - return + return // TODO: log this error } rdr.SeekOffset(thisItemForNxt + int64(l)) select { case ch <- c: - continue - case <-done: + case <-ctx.Done(): return } } diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index f49bdf53f..e35dae953 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -1,10 +1,12 @@ package blockstore_test import ( + "context" "io" "math/rand" "os" "testing" + "time" "github.com/stretchr/testify/assert" @@ -15,6 +17,9 @@ import ( ) func TestBlockstore(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + f, err := os.Open("testdata/test.car") assert.NoError(t, err) defer f.Close() @@ -67,6 +72,25 @@ func TestBlockstore(t *testing.T) { t.Fatal(err) } + allKeysCh, err := carb.AllKeysChan(ctx) + if err != nil { + t.Fatal(err) + } + numKeysCh := 0 + for c := range allKeysCh { + b, err := carb.Get(c) + if err != nil { + t.Fatal(err) + } + if !b.Cid().Equals(c) { + t.Fatal("wrong item returned") + } + numKeysCh++ + } + if numKeysCh != len(cids) { + t.Fatal("AllKeysChan returned an unexpected amount of keys") + } + for _, c := range cids { b, err := carb.Get(c) if err != nil { From 14bc11d6dd42096b631cf8cdcedef1490551d01c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Mon, 28 Jun 2021 10:45:42 +0100 Subject: [PATCH 3454/3817] blockstore: make unsupported methods panic That is, calling write methods on a read-only blockstore, or any methods on a finalized blockstore. The user's code should never be calling these, so there's no reason to avoid panics. There is one good reason to use panics, though: if anywhere in a program those rules aren't followed, a panic is much more obvious and quick to spot than an error which could just get logged or ignored. If a user wants a read-only blockstore that returns errors on write funcs, they could always wrap our read-only blockstore to do that. Arguably that's their best option anyway, so they are in control of the exact behavior they need. Also fix a typo in carV1Wrtier. This commit was moved from ipld/go-car@c25c2351cfe777e24f8dbbf3432bdbbfcacf83e1 --- ipld/car/v2/blockstore/readonly.go | 10 ++-- ipld/car/v2/blockstore/readwrite.go | 84 +++++++++++++---------------- 2 files changed, 40 insertions(+), 54 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index a1bc8d1fd..eb0b6b378 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -4,7 +4,6 @@ import ( "bufio" "context" "encoding/binary" - "errors" "fmt" "io" @@ -20,9 +19,6 @@ import ( var _ blockstore.Blockstore = (*ReadOnly)(nil) -// errUnsupported is returned for unsupported operations -var errUnsupported = errors.New("unsupported operation") - // ReadOnly provides a read-only Car Block Store. type ReadOnly struct { // The backing containing the CAR in v1 format. @@ -82,7 +78,7 @@ func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) { // DeleteBlock is unsupported and always returns an error. func (b *ReadOnly) DeleteBlock(_ cid.Cid) error { - return errUnsupported + panic("called write method on a read-only blockstore") } // Has indicates if the store contains a block that corresponds to the given key. @@ -143,12 +139,12 @@ func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { // Put is not supported and always returns an error. func (b *ReadOnly) Put(blocks.Block) error { - return errUnsupported + panic("called write method on a read-only blockstore") } // PutMany is not supported and always returns an error. func (b *ReadOnly) PutMany([]blocks.Block) error { - return errUnsupported + panic("called write method on a read-only blockstore") } // AllKeysChan returns the list of keys in the CAR. diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 909993d30..f72d01e6c 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -2,7 +2,6 @@ package blockstore import ( "context" - "errors" "fmt" "os" @@ -18,10 +17,7 @@ import ( "github.com/ipld/go-car/v2/internal/carv1/util" ) -var ( - _ blockstore.Blockstore = (*ReadWrite)(nil) - errFinalized = errors.New("finalized blockstore") -) +var _ blockstore.Blockstore = (*ReadWrite)(nil) // ReadWrite implements a blockstore that stores blocks in CAR v2 format. // Blocks put into the blockstore can be read back once they are successfully written. @@ -30,18 +26,17 @@ var ( // and updated incrementally. // The Finalize function must be called once the putting blocks are finished. // Upon calling Finalize header is finalized and index is written out. -// Once finalized, all read and write calls to this blockstore will result in error. -type ( - // TODO consider exposing interfaces - ReadWrite struct { - f *os.File - carV1Wrtier *internalio.OffsetWriter - ReadOnly - idx *index.InsertionIndex - header carv2.Header - } - Option func(*ReadWrite) // TODO consider unifying with writer options -) +// Once finalized, all read and write calls to this blockstore will result in panics. +type ReadWrite struct { + f *os.File + carV1Writer *internalio.OffsetWriter + ReadOnly + idx *index.InsertionIndex + header carv2.Header +} + +// TODO consider exposing interfaces +type Option func(*ReadWrite) // TODO consider unifying with writer options // WithCarV1Padding sets the padding to be added between CAR v2 header and its data payload on Finalize. func WithCarV1Padding(p uint64) Option { @@ -80,7 +75,7 @@ func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, err for _, opt := range opts { opt(b) } - b.carV1Wrtier = internalio.NewOffsetWriter(f, int64(b.header.CarV1Offset)) + b.carV1Writer = internalio.NewOffsetWriter(f, int64(b.header.CarV1Offset)) carV1Reader := internalio.NewOffsetReader(f, int64(b.header.CarV1Offset)) b.ReadOnly = *ReadOnlyOf(carV1Reader, idx) if _, err := f.WriteAt(carv2.Pragma, 0); err != nil { @@ -91,29 +86,33 @@ func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, err Roots: roots, Version: 1, } - if err := carv1.WriteHeader(v1Header, b.carV1Wrtier); err != nil { + if err := carv1.WriteHeader(v1Header, b.carV1Writer); err != nil { return nil, fmt.Errorf("couldn't write car header: %w", err) } return b, nil } +func (b *ReadWrite) panicIfFinalized() { + if b.header.CarV1Size != 0 { + panic("must not use a read-write blockstore after finalizing") + } +} + // Put puts a given block to the underlying datastore func (b *ReadWrite) Put(blk blocks.Block) error { - if b.isFinalized() { - return errFinalized - } + b.panicIfFinalized() + return b.PutMany([]blocks.Block{blk}) } // PutMany puts a slice of blocks at the same time using batching // capabilities of the underlying datastore whenever possible. func (b *ReadWrite) PutMany(blks []blocks.Block) error { - if b.isFinalized() { - return errFinalized - } + b.panicIfFinalized() + for _, bl := range blks { - n := uint64(b.carV1Wrtier.Position()) - if err := util.LdWrite(b.carV1Wrtier, bl.Cid().Bytes(), bl.RawData()); err != nil { + n := uint64(b.carV1Writer.Position()) + if err := util.LdWrite(b.carV1Writer, bl.Cid().Bytes(), bl.RawData()); err != nil { return err } b.idx.InsertNoReplace(bl.Cid(), n) @@ -121,20 +120,15 @@ func (b *ReadWrite) PutMany(blks []blocks.Block) error { return nil } -func (b *ReadWrite) isFinalized() bool { - return b.header.CarV1Size != 0 -} - // Finalize finalizes this blockstore by writing the CAR v2 header, along with flattened index // for more efficient subsequent read. // After this call, this blockstore can no longer be used for read or write. func (b *ReadWrite) Finalize() error { - if b.isFinalized() { - return errFinalized - } + b.panicIfFinalized() + // TODO check if add index option is set and don't write the index then set index offset to zero. // TODO see if folks need to continue reading from a finalized blockstore, if so return ReadOnly blockstore here. - b.header = b.header.WithCarV1Size(uint64(b.carV1Wrtier.Position())) + b.header = b.header.WithCarV1Size(uint64(b.carV1Writer.Position())) defer b.f.Close() if _, err := b.header.WriteTo(internalio.NewOffsetWriter(b.f, carv2.PragmaSize)); err != nil { return err @@ -148,29 +142,25 @@ func (b *ReadWrite) Finalize() error { } func (b *ReadWrite) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { - if b.isFinalized() { - return nil, errFinalized - } + b.panicIfFinalized() + return b.ReadOnly.AllKeysChan(ctx) } func (b *ReadWrite) Has(key cid.Cid) (bool, error) { - if b.isFinalized() { - return false, errFinalized - } + b.panicIfFinalized() + return b.ReadOnly.Has(key) } func (b *ReadWrite) Get(key cid.Cid) (blocks.Block, error) { - if b.isFinalized() { - return nil, errFinalized - } + b.panicIfFinalized() + return b.ReadOnly.Get(key) } func (b *ReadWrite) GetSize(key cid.Cid) (int, error) { - if b.isFinalized() { - return 0, errFinalized - } + b.panicIfFinalized() + return b.ReadOnly.GetSize(key) } From 0d1a833586b3e3586bacd643a35165cb567d3f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Mon, 28 Jun 2021 18:10:08 +0100 Subject: [PATCH 3455/3817] consistently use CID hashes for uniqueness The blockstore used comparisons on the entire CID for Has/Get to succeed, and the indexes generally used the inner multihash's digest to uniquely identify indexed CID-block pairs. The blockstore interface is generally understood to understand CID uniqueness by hash, and it's what go-ipfs's datastore does, so let's do the same. Add a test case for what broke with that inconsistency. Updates #110. This commit was moved from ipld/go-car@bd5908e3899a993c3a2865ad7c0b6d8070f0e506 --- ipld/car/v2/blockstore/readonly.go | 10 ++-- ipld/car/v2/blockstore/readwrite_test.go | 65 ++++++++++++++++++++++-- 2 files changed, 66 insertions(+), 9 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index eb0b6b378..486e9781e 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -2,6 +2,7 @@ package blockstore import ( "bufio" + "bytes" "context" "encoding/binary" "fmt" @@ -40,7 +41,6 @@ func ReadOnlyOf(backing io.ReaderAt, index index.Index) *ReadOnly { // If attachIndex is set to true and the index is not present in the given CAR v2 file, // then the generated index is written into the given path. func OpenReadOnly(path string, attachIndex bool) (*ReadOnly, error) { - v2r, err := carv2.NewReaderMmap(path) if err != nil { return nil, err @@ -96,7 +96,7 @@ func (b *ReadOnly) Has(key cid.Cid) (bool, error) { if err != nil { return false, err } - return c.Equals(key), nil + return bytes.Equal(key.Hash(), c.Hash()), nil } // Get gets a block corresponding to the given key. @@ -105,15 +105,15 @@ func (b *ReadOnly) Get(key cid.Cid) (blocks.Block, error) { if err != nil { return nil, err } - entry, bytes, err := b.readBlock(int64(offset)) + entry, data, err := b.readBlock(int64(offset)) if err != nil { // TODO Improve error handling; not all errors mean NotFound. return nil, blockstore.ErrNotFound } - if !entry.Equals(key) { + if !bytes.Equal(key.Hash(), entry.Hash()) { return nil, blockstore.ErrNotFound } - return blocks.NewBlockWithCid(bytes, key) + return blocks.NewBlockWithCid(data, key) } // GetSize gets the size of an item corresponding to the given key. diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index e35dae953..d362a8b68 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -8,10 +8,12 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" + "github.com/multiformats/go-multihash" + "github.com/stretchr/testify/require" "github.com/ipld/go-car/v2/blockstore" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" "github.com/ipld/go-car/v2/internal/carv1" ) @@ -21,10 +23,10 @@ func TestBlockstore(t *testing.T) { defer cancel() f, err := os.Open("testdata/test.car") - assert.NoError(t, err) + require.NoError(t, err) defer f.Close() r, err := carv1.NewCarReader(f) - assert.NoError(t, err) + require.NoError(t, err) path := "testv2blockstore.car" ingester, err := blockstore.NewReadWrite(path, r.Header.Roots) if err != nil { @@ -40,7 +42,7 @@ func TestBlockstore(t *testing.T) { if err == io.EOF { break } - assert.NoError(t, err) + require.NoError(t, err) if err := ingester.Put(b); err != nil { t.Fatal(err) @@ -101,3 +103,58 @@ func TestBlockstore(t *testing.T) { } } } + +func TestBlockstorePutSameHashes(t *testing.T) { + path := "testv2blockstore.car" + wbs, err := blockstore.NewReadWrite(path, nil) + if err != nil { + t.Fatal(err) + } + defer func() { os.Remove(path) }() + + var blockList []blocks.Block + + addBlock := func(data []byte, version, codec uint64) { + c, err := cid.Prefix{ + Version: version, + Codec: codec, + MhType: multihash.SHA2_256, + MhLength: -1, + }.Sum(data) + require.NoError(t, err) + + block, err := blocks.NewBlockWithCid(data, c) + require.NoError(t, err) + + blockList = append(blockList, block) + } + + data1 := []byte("foo bar") + addBlock(data1, 0, cid.Raw) + addBlock(data1, 1, cid.Raw) + addBlock(data1, 1, cid.DagCBOR) + + data2 := []byte("foo bar baz") + addBlock(data2, 0, cid.Raw) + addBlock(data2, 1, cid.Raw) + addBlock(data2, 1, cid.DagCBOR) + + for _, block := range blockList { + err = wbs.Put(block) + require.NoError(t, err) + } + + for _, block := range blockList { + has, err := wbs.Has(block.Cid()) + require.NoError(t, err) + require.True(t, has) + + got, err := wbs.Get(block.Cid()) + require.NoError(t, err) + require.Equal(t, block.Cid(), got.Cid()) + require.Equal(t, block.RawData(), got.RawData()) + } + + err = wbs.Finalize() + require.NoError(t, err) +} From ff6bfc1875cbb33ea2e1d3083ca92783b98fcfbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Tue, 29 Jun 2021 10:24:04 +0100 Subject: [PATCH 3456/3817] blockstore: don't error on Has not finding a CID Fixes #116. This commit was moved from ipld/go-car@acc013c1e000d43b4da747d1582a788e08b3c5d7 --- ipld/car/v2/blockstore/readonly.go | 5 ++++- ipld/car/v2/blockstore/readwrite_test.go | 25 ++++++++++++++++-------- ipld/car/v2/index/errors.go | 4 ++-- ipld/car/v2/index/indexgobhash.go | 2 +- ipld/car/v2/index/indexhashed.go | 2 +- ipld/car/v2/index/indexsorted.go | 2 +- ipld/car/v2/index/insertionindex.go | 2 +- 7 files changed, 27 insertions(+), 15 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 486e9781e..3bdc57a76 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -5,6 +5,7 @@ import ( "bytes" "context" "encoding/binary" + "errors" "fmt" "io" @@ -84,7 +85,9 @@ func (b *ReadOnly) DeleteBlock(_ cid.Cid) error { // Has indicates if the store contains a block that corresponds to the given key. func (b *ReadOnly) Has(key cid.Cid) (bool, error) { offset, err := b.idx.Get(key) - if err != nil { + if errors.Is(err, index.ErrNotFound) { + return false, nil + } else if err != nil { return false, err } uar := internalio.NewOffsetReader(b.backing, int64(offset)) diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index d362a8b68..ba08c9c5e 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -114,7 +114,7 @@ func TestBlockstorePutSameHashes(t *testing.T) { var blockList []blocks.Block - addBlock := func(data []byte, version, codec uint64) { + appendBlock := func(data []byte, version, codec uint64) { c, err := cid.Prefix{ Version: version, Codec: codec, @@ -130,16 +130,25 @@ func TestBlockstorePutSameHashes(t *testing.T) { } data1 := []byte("foo bar") - addBlock(data1, 0, cid.Raw) - addBlock(data1, 1, cid.Raw) - addBlock(data1, 1, cid.DagCBOR) + appendBlock(data1, 0, cid.Raw) + appendBlock(data1, 1, cid.Raw) + appendBlock(data1, 1, cid.DagCBOR) data2 := []byte("foo bar baz") - addBlock(data2, 0, cid.Raw) - addBlock(data2, 1, cid.Raw) - addBlock(data2, 1, cid.DagCBOR) + appendBlock(data2, 0, cid.Raw) + appendBlock(data2, 1, cid.Raw) + appendBlock(data2, 1, cid.DagCBOR) + + for i, block := range blockList { + // Has should never error here. + // The first block should be missing. + // Others might not, given the duplicate hashes. + has, err := wbs.Has(block.Cid()) + require.NoError(t, err) + if i == 0 { + require.False(t, has) + } - for _, block := range blockList { err = wbs.Put(block) require.NoError(t, err) } diff --git a/ipld/car/v2/index/errors.go b/ipld/car/v2/index/errors.go index 6d9c89c19..fba7afba9 100644 --- a/ipld/car/v2/index/errors.go +++ b/ipld/car/v2/index/errors.go @@ -3,8 +3,8 @@ package index import "errors" var ( - // errNotFound signals a record is not found in the index. - errNotFound = errors.New("not found") + // ErrNotFound signals a record is not found in the index. + ErrNotFound = errors.New("not found") // errUnsupported signals unsupported operation by an index. errUnsupported = errors.New("not supported") ) diff --git a/ipld/car/v2/index/indexgobhash.go b/ipld/car/v2/index/indexgobhash.go index ce2768a9f..9e61001fa 100644 --- a/ipld/car/v2/index/indexgobhash.go +++ b/ipld/car/v2/index/indexgobhash.go @@ -12,7 +12,7 @@ type mapGobIndex map[cid.Cid]uint64 func (m *mapGobIndex) Get(c cid.Cid) (uint64, error) { el, ok := (*m)[c] if !ok { - return 0, errNotFound + return 0, ErrNotFound } return el, nil } diff --git a/ipld/car/v2/index/indexhashed.go b/ipld/car/v2/index/indexhashed.go index c64e5cd84..b24a9014a 100644 --- a/ipld/car/v2/index/indexhashed.go +++ b/ipld/car/v2/index/indexhashed.go @@ -12,7 +12,7 @@ type mapIndex map[cid.Cid]uint64 func (m *mapIndex) Get(c cid.Cid) (uint64, error) { el, ok := (*m)[c] if !ok { - return 0, errNotFound + return 0, ErrNotFound } return el, nil } diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index 2646d08b7..3f7a2617a 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -120,7 +120,7 @@ func (m *multiWidthIndex) Get(c cid.Cid) (uint64, error) { if s, ok := (*m)[uint32(len(d.Digest)+8)]; ok { return s.get(d.Digest), nil } - return 0, errNotFound + return 0, ErrNotFound } func (m *multiWidthIndex) Codec() Codec { diff --git a/ipld/car/v2/index/insertionindex.go b/ipld/car/v2/index/insertionindex.go index 518210578..10b83ebaa 100644 --- a/ipld/car/v2/index/insertionindex.go +++ b/ipld/car/v2/index/insertionindex.go @@ -59,7 +59,7 @@ func (ii *InsertionIndex) Get(c cid.Cid) (uint64, error) { entry := recordDigest{digest: d.Digest} e := ii.items.Get(entry) if e == nil { - return 0, errNotFound + return 0, ErrNotFound } r, ok := e.(recordDigest) if !ok { From 81641ca92cb9672f0d7778854819e833ff162542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Tue, 29 Jun 2021 12:47:54 +0100 Subject: [PATCH 3457/3817] blockstore: make it safe for concurrent use IPLD traversals would sporadically fail due to data races, since they do concurrent blockstore Puts. The added test reproduced that kind of error very reliably. Fixes #121. This commit was moved from ipld/go-car@307cc4cc87c5672eff4058259d12a3de33b36822 --- ipld/car/v2/blockstore/readonly.go | 23 +++++++++++++ ipld/car/v2/blockstore/readwrite.go | 9 +++-- ipld/car/v2/blockstore/readwrite_test.go | 44 ++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 3bdc57a76..2db1c9e08 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "io" + "sync" blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" @@ -23,6 +24,14 @@ var _ blockstore.Blockstore = (*ReadOnly)(nil) // ReadOnly provides a read-only Car Block Store. type ReadOnly struct { + // mu allows ReadWrite to be safe for concurrent use. + // It's in ReadOnly so that read operations also grab read locks, + // given that ReadWrite embeds ReadOnly for methods like Get and Has. + // + // The main fields guarded by the mutex are the index and the underlying writers. + // For simplicity, the entirety of the blockstore methods grab the mutex. + mu sync.RWMutex + // The backing containing the CAR in v1 format. backing io.ReaderAt // The CAR v1 content index. @@ -84,6 +93,9 @@ func (b *ReadOnly) DeleteBlock(_ cid.Cid) error { // Has indicates if the store contains a block that corresponds to the given key. func (b *ReadOnly) Has(key cid.Cid) (bool, error) { + b.mu.RLock() + defer b.mu.RUnlock() + offset, err := b.idx.Get(key) if errors.Is(err, index.ErrNotFound) { return false, nil @@ -104,6 +116,9 @@ func (b *ReadOnly) Has(key cid.Cid) (bool, error) { // Get gets a block corresponding to the given key. func (b *ReadOnly) Get(key cid.Cid) (blocks.Block, error) { + b.mu.RLock() + defer b.mu.RUnlock() + offset, err := b.idx.Get(key) if err != nil { return nil, err @@ -121,6 +136,9 @@ func (b *ReadOnly) Get(key cid.Cid) (blocks.Block, error) { // GetSize gets the size of an item corresponding to the given key. func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { + b.mu.RLock() + defer b.mu.RUnlock() + idx, err := b.idx.Get(key) if err != nil { return -1, err @@ -152,6 +170,9 @@ func (b *ReadOnly) PutMany([]blocks.Block) error { // AllKeysChan returns the list of keys in the CAR. func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { + // We release the lock when the channel-sending goroutine stops. + b.mu.RLock() + // TODO we may use this walk for populating the index, and we need to be able to iterate keys in this way somewhere for index generation. In general though, when it's asked for all keys from a blockstore with an index, we should iterate through the index when possible rather than linear reads through the full car. header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReader(b.backing, 0))) if err != nil { @@ -166,6 +187,8 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { ch := make(chan cid.Cid, 5) go func() { + defer b.mu.RUnlock() + defer close(ch) rdr := internalio.NewOffsetReader(b.backing, int64(offset)) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index f72d01e6c..fef65a6c8 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -100,8 +100,7 @@ func (b *ReadWrite) panicIfFinalized() { // Put puts a given block to the underlying datastore func (b *ReadWrite) Put(blk blocks.Block) error { - b.panicIfFinalized() - + // PutMany already calls panicIfFinalized. return b.PutMany([]blocks.Block{blk}) } @@ -110,6 +109,9 @@ func (b *ReadWrite) Put(blk blocks.Block) error { func (b *ReadWrite) PutMany(blks []blocks.Block) error { b.panicIfFinalized() + b.mu.Lock() + defer b.mu.Unlock() + for _, bl := range blks { n := uint64(b.carV1Writer.Position()) if err := util.LdWrite(b.carV1Writer, bl.Cid().Bytes(), bl.RawData()); err != nil { @@ -126,6 +128,9 @@ func (b *ReadWrite) PutMany(blks []blocks.Block) error { func (b *ReadWrite) Finalize() error { b.panicIfFinalized() + b.mu.Lock() + defer b.mu.Unlock() + // TODO check if add index option is set and don't write the index then set index offset to zero. // TODO see if folks need to continue reading from a finalized blockstore, if so return ReadOnly blockstore here. b.header = b.header.WithCarV1Size(uint64(b.carV1Writer.Position())) diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index ba08c9c5e..b4a138c93 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -2,9 +2,11 @@ package blockstore_test import ( "context" + "fmt" "io" "math/rand" "os" + "sync" "testing" "time" @@ -167,3 +169,45 @@ func TestBlockstorePutSameHashes(t *testing.T) { err = wbs.Finalize() require.NoError(t, err) } + +func TestBlockstoreConcurrentUse(t *testing.T) { + path := "testv2blockstore.car" + wbs, err := blockstore.NewReadWrite(path, nil) + if err != nil { + t.Fatal(err) + } + defer func() { os.Remove(path) }() + + var wg sync.WaitGroup + for i := 0; i < 100; i++ { + data := []byte(fmt.Sprintf("data-%d", i)) + + wg.Add(1) + go func() { + defer wg.Done() + + c, err := cid.Prefix{ + Version: 1, + Codec: cid.Raw, + MhType: multihash.SHA2_256, + MhLength: -1, + }.Sum(data) + require.NoError(t, err) + + block, err := blocks.NewBlockWithCid(data, c) + require.NoError(t, err) + + has, err := wbs.Has(block.Cid()) + require.NoError(t, err) + require.False(t, has) + + err = wbs.Put(block) + require.NoError(t, err) + + got, err := wbs.Get(block.Cid()) + require.NoError(t, err) + require.Equal(t, data, got.RawData()) + }() + } + wg.Wait() +} From 7b4ca3a1a3cdb26d80e5ed4f66b66111a9f399ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Wed, 30 Jun 2021 14:44:34 +0100 Subject: [PATCH 3458/3817] add WrapV1File See the added doc and example. Also needed to make index marshaling deterministic, as per the spec. This commit was moved from ipld/go-car@26113397838d1d5a5b39f98ed7434b0186137bc5 --- ipld/car/v2/example_test.go | 50 ++++++++++++++++ ipld/car/v2/index/generator.go | 4 ++ ipld/car/v2/index/indexsorted.go | 18 +++++- ipld/car/v2/testdata/sample-v1.car | Bin 0 -> 479907 bytes ipld/car/v2/testdata/sample-wrapped-v2.car | Bin 0 -> 521859 bytes ipld/car/v2/writer.go | 66 +++++++++++++++++++++ 6 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 ipld/car/v2/example_test.go create mode 100644 ipld/car/v2/testdata/sample-v1.car create mode 100644 ipld/car/v2/testdata/sample-wrapped-v2.car diff --git a/ipld/car/v2/example_test.go b/ipld/car/v2/example_test.go new file mode 100644 index 000000000..70d8ed632 --- /dev/null +++ b/ipld/car/v2/example_test.go @@ -0,0 +1,50 @@ +package car_test + +import ( + "bytes" + "fmt" + "io/ioutil" + + carv2 "github.com/ipld/go-car/v2" +) + +func ExampleWrapV1File() { + // We have a sample CARv1 file. + // Wrap it as-is in a CARv2, with an index. + // Writing the result to testdata allows reusing that file in other tests, + // and also helps ensure that the result is deterministic. + src := "testdata/sample-v1.car" + dst := "testdata/sample-wrapped-v2.car" + if err := carv2.WrapV1File(src, dst); err != nil { + panic(err) + } + + // Open our new CARv2 file and show some info about it. + cr, err := carv2.NewReaderMmap(dst) + if err != nil { + panic(err) + } + defer cr.Close() + roots, err := cr.Roots() + if err != nil { + panic(err) + } + fmt.Println("Roots:", roots) + fmt.Println("Has index:", cr.Header.HasIndex()) + + // Verify that the CARv1 remains exactly the same. + orig, err := ioutil.ReadFile(src) + if err != nil { + panic(err) + } + inner, err := ioutil.ReadAll(cr.CarV1Reader()) + if err != nil { + panic(err) + } + fmt.Println("Inner CARv1 is exactly the same:", bytes.Equal(orig, inner)) + + // Output: + // Roots: [bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy] + // Has index: true + // Inner CARv1 is exactly the same: true +} diff --git a/ipld/car/v2/index/generator.go b/ipld/car/v2/index/generator.go index b7621af85..f65de3ecc 100644 --- a/ipld/car/v2/index/generator.go +++ b/ipld/car/v2/index/generator.go @@ -18,6 +18,10 @@ func Generate(car io.ReaderAt) (Index, error) { if err != nil { return nil, fmt.Errorf("error reading car header: %w", err) } + + // TODO: Generate should likely just take an io.ReadSeeker. + // TODO: ensure the input's header version is 1. + offset, err := carv1.HeaderSize(header) if err != nil { return nil, err diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index 3f7a2617a..b7d275103 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -53,6 +53,7 @@ func (s *singleWidthIndex) Marshal(w io.Writer) error { if err := binary.Write(w, binary.LittleEndian, int64(len(s.index))); err != nil { return err } + // TODO: we could just w.Write(s.index) here and avoid overhead _, err := io.Copy(w, bytes.NewBuffer(s.index)) return err } @@ -129,8 +130,21 @@ func (m *multiWidthIndex) Codec() Codec { func (m *multiWidthIndex) Marshal(w io.Writer) error { binary.Write(w, binary.LittleEndian, int32(len(*m))) - for _, s := range *m { - if err := s.Marshal(w); err != nil { + + // The widths are unique, but ranging over a map isn't deterministic. + // As per the CARv2 spec, we must order buckets by digest length. + + widths := make([]uint32, 0, len(*m)) + for width := range *m { + widths = append(widths, width) + } + sort.Slice(widths, func(i, j int) bool { + return widths[i] < widths[j] + }) + + for _, width := range widths { + bucket := (*m)[width] + if err := bucket.Marshal(w); err != nil { return err } } diff --git a/ipld/car/v2/testdata/sample-v1.car b/ipld/car/v2/testdata/sample-v1.car new file mode 100644 index 0000000000000000000000000000000000000000..47a61c8c2a7def9bafcc252d3a1a4d12529615f5 GIT binary patch literal 479907 zcmdSBRZtz;(yooW>%`q5XmHoy?ry=|-QC@TySuwXfZ!6`JxGAyhwQW0D%N+C|GW6B z&f>a9O{&Ly-nU1O9+NxI*xts*(ZO$tT8#n-*e+)mgz#PvS&t~k82RzAB9>MjOd+o_4OvAu(tjWzIhi2rX3qJ)6ff&J9kP7mV^XFOuvNS(Jd z{qMJ#FJ(8=_wi8BHQ}u=;<6T|Q=!iO zNagh)J~5HNCL!IMHV1)65=PfX6{g=_2mH!-7ERt++QP$L zF9`?O)_d_m#~buJ~qfQNd<{?*_Wvv1wjvydgi3JwF;2fgnvgXnOaj(8EsnL_~UKh{jg9!E! zc+84I=z?-6Rn5l;4CEwW$VA)=+TrX@(`9fOd&f&m8=xi7@U5BpuZN>~jE^d+nv==6Y~5oF#z9g3{DpJZ9+ zqM&7n$6;!v9Wh$eR%gyd5|mWF1?0UjUad|(!F~szc|(H7>(*YkZsVb5g_^aIQqtT3 z>cGB*FMJcYB@ls{NOg*`EY6~`1oxFO^caW zb9d>NuU_afcpyN=P7H`+JOPVCWllW)JZpV3eKbT#AW<(7sMAE zK>YBc_O2;aFRm35{t<7uWR;p;fkY;ifZ*EjZ&eiFk?u^mJ-f8NrxRqaYZ{?0uZ*FWo zn^7jViqYO&QbyBAS{LLQZ1W|iI(m1Vnx2`cgiq(Av18j}s$TjnE_qiMQ+b#~?rXCm zgP#zVZF#(W2tUTKZ#Jz1%C|t()+?gr1}T~=%8^=MXQNdPD%VV5#9Vk3vjC~To4a8w ztT=1gvu1Vq(a-@|4NgW5ZU7-et0g(BrkCc!`p`=dQ$|00*B$Vj_HC~%SR5x>;%sh2 z>S?S?!1XewedO68u!GBRHu3ESDGG}+NV;{%p?J3V^� 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 Tx_{@>Ah$cBb;X>)D9fct)9`U;$XV@y4$E$ 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

hRd> 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

vHSrHL`4Y!))mrU|+My=J*?NCLvdFH?W^2AuF;5 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

ecf&5+ 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 z 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í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 G@Bag@W-Mg@ literal 0 HcmV?d00001 diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index c425b2385..8c9f504e0 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "io" + "os" "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" @@ -130,3 +131,68 @@ func (w *Writer) writeIndex(writer io.Writer, carV1 []byte) (int64, error) { // FIXME refactor index to expose the number of bytes written. return 0, index.WriteTo(idx, writer) } + +// WrapV1File takes a source path to a CARv1 file and wraps it as a CARv2 file +// with an index, writing the result to the destination path. +// The resulting CARv2 file's inner CARv1 payload is left unmodified, +// and does not use any padding before the innner CARv1 or index. +func WrapV1File(srcPath, dstPath string) error { + // TODO: verify src is indeed a CARv1 to prevent misuse. + // index.Generate should probably be in charge of that. + + // TODO: also expose WrapV1(io.ReadSeeker, io.Writer), + // once index.Generate takes a ReadSeeker. + + // We don't use mmap.Open, so we can later use io.Copy. + f1, err := os.Open(srcPath) + if err != nil { + return err + } + defer f1.Close() + + idx, err := index.Generate(f1) + if err != nil { + return err + } + + // Use Seek to learn the size of the CARv1 before reading it. + v1Size, err := f1.Seek(0, io.SeekEnd) + if err != nil { + return err + } + if _, err := f1.Seek(0, io.SeekStart); err != nil { + return err + } + + // Only create the destination CARv2 when we've gathered all the + // information we need, such as the index and the CARv1 size. + f2, err := os.Create(dstPath) + if err != nil { + return err + } + defer f2.Close() + + // Similar to the Writer API, write all components of a CARv2 to the + // destination file: Pragma, Header, CARv1, Index. + v2Header := NewHeader(uint64(v1Size)) + if _, err := f2.Write(Pragma); err != nil { + return err + } + if _, err := v2Header.WriteTo(f2); err != nil { + return err + } + if _, err := io.Copy(f2, f1); err != nil { + return err + } + if err := index.WriteTo(idx, f2); err != nil { + return err + } + + // Check the close error, since we're writing to f2. + // Note that we also do a "defer f2.Close()" above, + // to make sure that the earlier error returns don't leak the file. + if err := f2.Close(); err != nil { + return err + } + return nil +} From 3b981370b8eacc920696a1cc65cf7960d447c7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Thu, 1 Jul 2021 13:11:49 +0100 Subject: [PATCH 3459/3817] blockstore: add option to deduplicate by CID And a test that uses duplicate hashes as well as duplicate CIDs. We reuse the same insertion index, since it's enough for this purpose. There's no need to keep a separate map or set of CIDs. While at it, make the index package not silently swallow errors, and improve the tests to handle errors more consistently. Fixes #123. Fixes #125. This commit was moved from ipld/go-car@08f751c83f924b3b52ef5dac1b32edf06553772c --- ipld/car/v2/blockstore/readwrite.go | 26 +++++- ipld/car/v2/blockstore/readwrite_test.go | 103 ++++++++++++++--------- ipld/car/v2/index/insertionindex.go | 30 ++++++- 3 files changed, 113 insertions(+), 46 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index fef65a6c8..99fd11d2a 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -33,6 +33,8 @@ type ReadWrite struct { ReadOnly idx *index.InsertionIndex header carv2.Header + + dedupCids bool } // TODO consider exposing interfaces @@ -52,6 +54,15 @@ func WithIndexPadding(p uint64) Option { } } +// WithCidDeduplication makes Put calls ignore blocks if the blockstore already +// has the exact same CID. +// This can help avoid redundancy in a CARv1's list of CID-Block pairs. +// +// Note that this compares whole CIDs, not just multihashes. +func WithCidDeduplication(b *ReadWrite) { + b.dedupCids = true +} + // NewReadWrite creates a new ReadWrite at the given path with a provided set of root CIDs as the car roots. func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, error) { // TODO support resumption if the path provided contains partially written blocks in v2 format. @@ -113,11 +124,16 @@ func (b *ReadWrite) PutMany(blks []blocks.Block) error { defer b.mu.Unlock() for _, bl := range blks { + c := bl.Cid() + if b.dedupCids && b.idx.HasExactCID(c) { + continue + } + n := uint64(b.carV1Writer.Position()) - if err := util.LdWrite(b.carV1Writer, bl.Cid().Bytes(), bl.RawData()); err != nil { + if err := util.LdWrite(b.carV1Writer, c.Bytes(), bl.RawData()); err != nil { return err } - b.idx.InsertNoReplace(bl.Cid(), n) + b.idx.InsertNoReplace(c, n) } return nil } @@ -126,7 +142,11 @@ func (b *ReadWrite) PutMany(blks []blocks.Block) error { // for more efficient subsequent read. // After this call, this blockstore can no longer be used for read or write. func (b *ReadWrite) Finalize() error { - b.panicIfFinalized() + if b.header.CarV1Size != 0 { + // Allow duplicate Finalize calls, just like Close. + // Still error, just like ReadOnly.Close; it should be discarded. + return fmt.Errorf("called Finalize twice") + } b.mu.Lock() defer b.mu.Unlock() diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index b4a138c93..3b66b3c4f 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -6,6 +6,7 @@ import ( "io" "math/rand" "os" + "path/filepath" "sync" "testing" "time" @@ -26,17 +27,14 @@ func TestBlockstore(t *testing.T) { f, err := os.Open("testdata/test.car") require.NoError(t, err) - defer f.Close() + t.Cleanup(func() { f.Close() }) r, err := carv1.NewCarReader(f) require.NoError(t, err) - path := "testv2blockstore.car" + + path := filepath.Join(t.TempDir(), "readwrite.car") ingester, err := blockstore.NewReadWrite(path, r.Header.Roots) - if err != nil { - t.Fatal(err) - } - defer func() { - os.Remove(path) - }() + require.NoError(t, err) + t.Cleanup(func() { ingester.Finalize() }) cids := make([]cid.Cid, 0) for { @@ -46,9 +44,8 @@ func TestBlockstore(t *testing.T) { } require.NoError(t, err) - if err := ingester.Put(b); err != nil { - t.Fatal(err) - } + err = ingester.Put(b) + require.NoError(t, err) cids = append(cids, b.Cid()) // try reading a random one: @@ -60,32 +57,24 @@ func TestBlockstore(t *testing.T) { for _, c := range cids { b, err := ingester.Get(c) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) if !b.Cid().Equals(c) { t.Fatal("wrong item returned") } } - if err := ingester.Finalize(); err != nil { - t.Fatal(err) - } + err = ingester.Finalize() + require.NoError(t, err) carb, err := blockstore.OpenReadOnly(path, false) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) + t.Cleanup(func() { carb.Close() }) allKeysCh, err := carb.AllKeysChan(ctx) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) numKeysCh := 0 for c := range allKeysCh { b, err := carb.Get(c) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) if !b.Cid().Equals(c) { t.Fatal("wrong item returned") } @@ -97,9 +86,7 @@ func TestBlockstore(t *testing.T) { for _, c := range cids { b, err := carb.Get(c) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) if !b.Cid().Equals(c) { t.Fatal("wrong item returned") } @@ -107,12 +94,19 @@ func TestBlockstore(t *testing.T) { } func TestBlockstorePutSameHashes(t *testing.T) { - path := "testv2blockstore.car" - wbs, err := blockstore.NewReadWrite(path, nil) - if err != nil { - t.Fatal(err) - } - defer func() { os.Remove(path) }() + tdir := t.TempDir() + wbs, err := blockstore.NewReadWrite( + filepath.Join(tdir, "readwrite.car"), nil, + ) + require.NoError(t, err) + t.Cleanup(func() { wbs.Finalize() }) + + wbsd, err := blockstore.NewReadWrite( + filepath.Join(tdir, "readwrite-dedup.car"), nil, + blockstore.WithCidDeduplication, + ) + require.NoError(t, err) + t.Cleanup(func() { wbsd.Finalize() }) var blockList []blocks.Block @@ -131,16 +125,32 @@ func TestBlockstorePutSameHashes(t *testing.T) { blockList = append(blockList, block) } + // Two raw blocks, meaning we have two unique multihashes. + // However, we have multiple CIDs for each multihash. + // We also have two duplicate CIDs. data1 := []byte("foo bar") appendBlock(data1, 0, cid.Raw) appendBlock(data1, 1, cid.Raw) appendBlock(data1, 1, cid.DagCBOR) + appendBlock(data1, 1, cid.DagCBOR) // duplicate CID data2 := []byte("foo bar baz") appendBlock(data2, 0, cid.Raw) appendBlock(data2, 1, cid.Raw) + appendBlock(data2, 1, cid.Raw) // duplicate CID appendBlock(data2, 1, cid.DagCBOR) + countBlocks := func(bs *blockstore.ReadWrite) int { + ch, err := bs.AllKeysChan(context.Background()) + require.NoError(t, err) + + n := 0 + for range ch { + n++ + } + return n + } + for i, block := range blockList { // Has should never error here. // The first block should be missing. @@ -166,17 +176,28 @@ func TestBlockstorePutSameHashes(t *testing.T) { require.Equal(t, block.RawData(), got.RawData()) } + require.Equal(t, len(blockList), countBlocks(wbs)) + err = wbs.Finalize() require.NoError(t, err) + + // Put the same list of blocks to the blockstore that + // deduplicates by CID. + // We should end up with two fewer blocks. + for _, block := range blockList { + err = wbsd.Put(block) + require.NoError(t, err) + } + require.Equal(t, len(blockList)-2, countBlocks(wbsd)) + + err = wbsd.Finalize() + require.NoError(t, err) } func TestBlockstoreConcurrentUse(t *testing.T) { - path := "testv2blockstore.car" - wbs, err := blockstore.NewReadWrite(path, nil) - if err != nil { - t.Fatal(err) - } - defer func() { os.Remove(path) }() + wbs, err := blockstore.NewReadWrite(filepath.Join(t.TempDir(), "readwrite.car"), nil) + require.NoError(t, err) + t.Cleanup(func() { wbs.Finalize() }) var wg sync.WaitGroup for i := 0; i < 100; i++ { diff --git a/ipld/car/v2/index/insertionindex.go b/ipld/car/v2/index/insertionindex.go index 10b83ebaa..78dace1df 100644 --- a/ipld/car/v2/index/insertionindex.go +++ b/ipld/car/v2/index/insertionindex.go @@ -36,7 +36,7 @@ func (r recordDigest) Less(than llrb.Item) bool { func mkRecord(r Record) recordDigest { d, err := multihash.Decode(r.Hash()) if err != nil { - return recordDigest{} + panic(err) } return recordDigest{d.Digest, r} @@ -45,7 +45,7 @@ func mkRecord(r Record) recordDigest { func mkRecordFromCid(c cid.Cid, at uint64) recordDigest { d, err := multihash.Decode(c.Hash()) if err != nil { - return recordDigest{} + panic(err) } return recordDigest{d.Digest, Record{Cid: c, Idx: at}} @@ -141,3 +141,29 @@ func (ii *InsertionIndex) Flatten() (Index, error) { } return si, nil } + +func (ii *InsertionIndex) HasExactCID(c cid.Cid) bool { + d, err := multihash.Decode(c.Hash()) + if err != nil { + panic(err) + } + entry := recordDigest{digest: d.Digest} + + found := false + iter := func(i llrb.Item) bool { + existing := i.(recordDigest) + if !bytes.Equal(existing.digest, entry.digest) { + // We've already looked at all entries with matching digests. + return false + } + if existing.Record.Cid == c { + // We found an exact match. + found = true + return false + } + // Continue looking in ascending order. + return true + } + ii.items.AscendGreaterOrEqual(entry, iter) + return found +} From 70b38cf10eca3db621a7696ecad54074a788db63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Thu, 1 Jul 2021 16:45:41 +0100 Subject: [PATCH 3460/3817] replace util.ReadCid with go-cid go-cid's CidFromBytes does exactly what we want already. This commit was moved from ipld/go-car@695f454f334c63296ddb5f2293b3f3a09ec322e9 --- ipld/car/v2/internal/carv1/util/util.go | 41 +------------------------ 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/ipld/car/v2/internal/carv1/util/util.go b/ipld/car/v2/internal/carv1/util/util.go index 08048f333..9db5f8c15 100644 --- a/ipld/car/v2/internal/carv1/util/util.go +++ b/ipld/car/v2/internal/carv1/util/util.go @@ -2,63 +2,24 @@ package util import ( "bufio" - "bytes" "encoding/binary" - "fmt" "io" cid "github.com/ipfs/go-cid" - mh "github.com/multiformats/go-multihash" ) -var cidv0Pref = []byte{0x12, 0x20} - type BytesReader interface { io.Reader io.ByteReader } -// TODO: this belongs in the go-cid package -func ReadCid(buf []byte) (cid.Cid, int, error) { - if bytes.Equal(buf[:2], cidv0Pref) { - c, err := cid.Cast(buf[:34]) - return c, 34, err - } - - br := bytes.NewReader(buf) - - // assume cidv1 - vers, err := binary.ReadUvarint(br) - if err != nil { - return cid.Cid{}, 0, err - } - - // TODO: the go-cid package allows version 0 here as well - if vers != 1 { - return cid.Cid{}, 0, fmt.Errorf("invalid cid version number") - } - - codec, err := binary.ReadUvarint(br) - if err != nil { - return cid.Cid{}, 0, err - } - - mhr := mh.NewReader(br) - h, err := mhr.ReadMultihash() - if err != nil { - return cid.Cid{}, 0, err - } - - return cid.NewCidV1(codec, h), len(buf) - br.Len(), nil -} - func ReadNode(br *bufio.Reader) (cid.Cid, []byte, error) { data, err := LdRead(br) if err != nil { return cid.Cid{}, nil, err } - c, n, err := ReadCid(data) + n, c, err := cid.CidFromBytes(data) if err != nil { return cid.Cid{}, nil, err } From aec932caf62036ffd2bc0b19d1647fc4e0a21263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Fri, 2 Jul 2021 18:40:58 +0100 Subject: [PATCH 3461/3817] replace internal/io.ReadCid with cid.CidFromReader The go-cid change hasn't been merged yet, but it's already had two reviews, and it looks like it will get merged early next week. We can move to go-cid master before the final release. This commit was moved from ipld/go-car@5f5aa1c026457fe8a0f3ab8468e6574cd7b05633 --- ipld/car/v2/blockstore/readonly.go | 8 ++--- ipld/car/v2/index/generator.go | 5 +-- ipld/car/v2/internal/io/cid.go | 53 ------------------------------ 3 files changed, 7 insertions(+), 59 deletions(-) delete mode 100644 ipld/car/v2/internal/io/cid.go diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 2db1c9e08..ac603001e 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -107,7 +107,7 @@ func (b *ReadOnly) Has(key cid.Cid) (bool, error) { if err != nil { return false, err } - c, _, err := internalio.ReadCid(b.backing, uar.Offset()) + _, c, err := cid.CidFromReader(uar) if err != nil { return false, err } @@ -147,7 +147,7 @@ func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { if err != nil { return -1, blockstore.ErrNotFound } - c, _, err := internalio.ReadCid(b.backing, int64(idx+l)) + _, c, err := cid.CidFromReader(internalio.NewOffsetReader(b.backing, int64(idx+l))) if err != nil { return 0, err } @@ -194,11 +194,11 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { rdr := internalio.NewOffsetReader(b.backing, int64(offset)) for { l, err := binary.ReadUvarint(rdr) - thisItemForNxt := rdr.Offset() if err != nil { return // TODO: log this error } - c, _, err := internalio.ReadCid(b.backing, thisItemForNxt) + thisItemForNxt := rdr.Offset() + _, c, err := cid.CidFromReader(rdr) if err != nil { return // TODO: log this error } diff --git a/ipld/car/v2/index/generator.go b/ipld/car/v2/index/generator.go index f65de3ecc..08b32fda9 100644 --- a/ipld/car/v2/index/generator.go +++ b/ipld/car/v2/index/generator.go @@ -6,6 +6,7 @@ import ( "fmt" "io" + "github.com/ipfs/go-cid" "github.com/ipld/go-car/v2/internal/carv1" internalio "github.com/ipld/go-car/v2/internal/io" "golang.org/x/exp/mmap" @@ -34,14 +35,14 @@ func Generate(car io.ReaderAt) (Index, error) { for { thisItemIdx := rdr.Offset() l, err := binary.ReadUvarint(rdr) - thisItemForNxt := rdr.Offset() if err != nil { if err == io.EOF { break } return nil, err } - c, _, err := internalio.ReadCid(car, thisItemForNxt) + thisItemForNxt := rdr.Offset() + _, c, err := cid.CidFromReader(rdr) if err != nil { return nil, err } diff --git a/ipld/car/v2/internal/io/cid.go b/ipld/car/v2/internal/io/cid.go deleted file mode 100644 index ee348e257..000000000 --- a/ipld/car/v2/internal/io/cid.go +++ /dev/null @@ -1,53 +0,0 @@ -package io - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" - - "github.com/ipfs/go-cid" - "github.com/multiformats/go-multihash" -) - -var cidv0Pref = []byte{0x12, 0x20} - -func ReadCid(store io.ReaderAt, at int64) (cid.Cid, int, error) { - var tag [2]byte - if _, err := store.ReadAt(tag[:], at); err != nil { - return cid.Undef, 0, err - } - if bytes.Equal(tag[:], cidv0Pref) { - cid0 := make([]byte, 34) - if _, err := store.ReadAt(cid0, at); err != nil { - return cid.Undef, 0, err - } - c, err := cid.Cast(cid0) - return c, 34, err - } - - // assume cidv1 - br := NewOffsetReader(store, at) - vers, err := binary.ReadUvarint(br) - if err != nil { - return cid.Cid{}, 0, err - } - - // TODO: the go-cid package allows version 0 here as well - if vers != 1 { - return cid.Cid{}, 0, fmt.Errorf("invalid cid version number: %d", vers) - } - - codec, err := binary.ReadUvarint(br) - if err != nil { - return cid.Cid{}, 0, err - } - - mhr := multihash.NewReader(br) - h, err := mhr.ReadMultihash() - if err != nil { - return cid.Cid{}, 0, err - } - - return cid.NewCidV1(codec, h), int(br.Offset() - at), nil -} From 473e128146139972ce5790f1384e220a0d3dbaf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Mon, 5 Jul 2021 13:06:19 +0100 Subject: [PATCH 3462/3817] avoid exposing io.SectionReader in the API As per the TODO. Also delete the other TODO. An index doesn't have any paddings or large chunks to be skipped, so ReaderAt is not necessary nor useful. The index package's ReadFrom works on a Reader, too. This commit was moved from ipld/go-car@eaeb24be1aef199755ac567d87adf05ff9af4bc3 --- ipld/car/v2/reader.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index d410ef879..d8b822635 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -84,15 +84,22 @@ func (r *Reader) readHeader() (err error) { return } +// SectionReader implements both io.ReadSeeker and io.ReaderAt. +// It is the interface version of io.SectionReader, but note that the +// implementation is not guaranteed to be an io.SectionReader. +type SectionReader interface { + io.Reader + io.Seeker + io.ReaderAt +} + // CarV1Reader provides a reader containing the CAR v1 section encapsulated in this CAR v2. -func (r *Reader) CarV1Reader() *io.SectionReader { - // TODO consider returning io.Reader+ReaderAt in a custom interface +func (r *Reader) CarV1Reader() SectionReader { return io.NewSectionReader(r.r, int64(r.Header.CarV1Offset), int64(r.Header.CarV1Size)) } // IndexReader provides an io.Reader containing the index of this CAR v2. func (r *Reader) IndexReader() io.Reader { - // TODO consider returning io.Reader+ReaderAt in a custom interface return internalio.NewOffsetReader(r.r, int64(r.Header.IndexOffset)) } From 3942e43faa351dfb7d27a05a399b44e23fdedc10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Mon, 5 Jul 2021 16:38:11 +0100 Subject: [PATCH 3463/3817] add carv2.WrapV1 based on io interfaces This requires index.Generate to work on io.ReadSeeker, which is a good idea anyway, as it just needs to read and seek. The main advantage of ReaderAt is that it allows concurrent reads, but index generation is sequential by nature. Plus, it only needs seeking to skip blocks; it doesn't need random access to the carv1. Also make the WrapV1 example test that the resulting carv2 works, including the index. Fixes #129. This commit was moved from ipld/go-car@e54e2356d000fd860984b41ee584bc943dcf6a27 --- ipld/car/v2/example_test.go | 9 +++ ipld/car/v2/index/generator.go | 51 +++++++++++----- ipld/car/v2/internal/carbs/util/hydrate.go | 9 +-- ipld/car/v2/writer.go | 69 ++++++++++++---------- 4 files changed, 84 insertions(+), 54 deletions(-) diff --git a/ipld/car/v2/example_test.go b/ipld/car/v2/example_test.go index 70d8ed632..93dedd772 100644 --- a/ipld/car/v2/example_test.go +++ b/ipld/car/v2/example_test.go @@ -6,6 +6,7 @@ import ( "io/ioutil" carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/blockstore" ) func ExampleWrapV1File() { @@ -43,8 +44,16 @@ func ExampleWrapV1File() { } fmt.Println("Inner CARv1 is exactly the same:", bytes.Equal(orig, inner)) + // Verify that the CARv2 works well with its index. + bs, err := blockstore.OpenReadOnly(dst, false) + if err != nil { + panic(err) + } + fmt.Println(bs.Get(roots[0])) + // Output: // Roots: [bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy] // Has index: true // Inner CARv1 is exactly the same: true + // [Block bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy] } diff --git a/ipld/car/v2/index/generator.go b/ipld/car/v2/index/generator.go index 08b32fda9..201b221cf 100644 --- a/ipld/car/v2/index/generator.go +++ b/ipld/car/v2/index/generator.go @@ -5,17 +5,26 @@ import ( "encoding/binary" "fmt" "io" + "os" "github.com/ipfs/go-cid" "github.com/ipld/go-car/v2/internal/carv1" - internalio "github.com/ipld/go-car/v2/internal/io" - "golang.org/x/exp/mmap" ) +type readSeekerPlusByte struct { + io.ReadSeeker +} + +func (r readSeekerPlusByte) ReadByte() (byte, error) { + var p [1]byte + _, err := io.ReadFull(r, p[:]) + return p[0], err +} + // Generate generates index for a given car in v1 format. // The index can be stored using index.Save into a file or serialized using index.WriteTo. -func Generate(car io.ReaderAt) (Index, error) { - header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReader(car, 0))) +func Generate(v1 io.ReadSeeker) (Index, error) { + header, err := carv1.ReadHeader(bufio.NewReader(v1)) if err != nil { return nil, fmt.Errorf("error reading car header: %w", err) } @@ -31,23 +40,37 @@ func Generate(car io.ReaderAt) (Index, error) { idx := mkSorted() records := make([]Record, 0) - rdr := internalio.NewOffsetReader(car, int64(offset)) + + // Seek to the first frame. + // Record the start of each frame, which we need for the index records. + frameOffset := int64(0) + if frameOffset, err = v1.Seek(int64(offset), io.SeekStart); err != nil { + return nil, err + } + for { - thisItemIdx := rdr.Offset() - l, err := binary.ReadUvarint(rdr) + // Grab the length of the frame. + // Note that ReadUvarint wants a ByteReader. + length, err := binary.ReadUvarint(readSeekerPlusByte{v1}) if err != nil { if err == io.EOF { break } return nil, err } - thisItemForNxt := rdr.Offset() - _, c, err := cid.CidFromReader(rdr) + + // Grab the CID. + n, c, err := cid.CidFromReader(v1) if err != nil { return nil, err } - records = append(records, Record{c, uint64(thisItemIdx)}) - rdr.SeekOffset(thisItemForNxt + int64(l)) + records = append(records, Record{c, uint64(frameOffset)}) + + // Seek to the next frame by skipping the block. + // The frame length includes the CID, so subtract it. + if frameOffset, err = v1.Seek(int64(length)-int64(n), io.SeekCurrent); err != nil { + return nil, err + } } if err := idx.Load(records); err != nil { @@ -60,10 +83,10 @@ func Generate(car io.ReaderAt) (Index, error) { // GenerateFromFile walks a car v1 file at the give path and generates an index of cid->byte offset. // The index can be stored using index.Save into a file or serialized using index.WriteTo. func GenerateFromFile(path string) (Index, error) { - store, err := mmap.Open(path) + f, err := os.Open(path) if err != nil { return nil, err } - defer store.Close() - return Generate(store) + defer f.Close() + return Generate(f) } diff --git a/ipld/car/v2/internal/carbs/util/hydrate.go b/ipld/car/v2/internal/carbs/util/hydrate.go index eead86cf1..8e43556cb 100644 --- a/ipld/car/v2/internal/carbs/util/hydrate.go +++ b/ipld/car/v2/internal/carbs/util/hydrate.go @@ -5,7 +5,6 @@ import ( "os" "github.com/ipld/go-car/v2/index" - "golang.org/x/exp/mmap" ) func main() { @@ -15,13 +14,7 @@ func main() { } db := os.Args[1] - dbBacking, err := mmap.Open(db) - if err != nil { - fmt.Printf("Error Opening car for hydration: %v\n", err) - return - } - - idx, err := index.Generate(dbBacking) + idx, err := index.GenerateFromFile(db) if err != nil { fmt.Printf("Error generating index: %v\n", err) return diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index 8c9f504e0..59777c87f 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -132,67 +132,72 @@ func (w *Writer) writeIndex(writer io.Writer, carV1 []byte) (int64, error) { return 0, index.WriteTo(idx, writer) } -// WrapV1File takes a source path to a CARv1 file and wraps it as a CARv2 file -// with an index, writing the result to the destination path. -// The resulting CARv2 file's inner CARv1 payload is left unmodified, -// and does not use any padding before the innner CARv1 or index. +// WrapV1File is a wrapper around WrapV1 that takes filesystem paths. +// The source path is assumed to exist, and the destination path is overwritten. +// Note that the destination path might still be created even if an error +// occurred. func WrapV1File(srcPath, dstPath string) error { - // TODO: verify src is indeed a CARv1 to prevent misuse. - // index.Generate should probably be in charge of that. - - // TODO: also expose WrapV1(io.ReadSeeker, io.Writer), - // once index.Generate takes a ReadSeeker. - - // We don't use mmap.Open, so we can later use io.Copy. - f1, err := os.Open(srcPath) + src, err := os.Open(srcPath) if err != nil { return err } - defer f1.Close() + defer src.Close() - idx, err := index.Generate(f1) + dst, err := os.Create(dstPath) if err != nil { return err } + defer dst.Close() - // Use Seek to learn the size of the CARv1 before reading it. - v1Size, err := f1.Seek(0, io.SeekEnd) - if err != nil { + if err := WrapV1(src, dst); err != nil { return err } - if _, err := f1.Seek(0, io.SeekStart); err != nil { + + // Check the close error, since we're writing to dst. + // Note that we also do a "defer dst.Close()" above, + // to make sure that the earlier error returns don't leak the file. + if err := dst.Close(); err != nil { return err } + return nil +} - // Only create the destination CARv2 when we've gathered all the - // information we need, such as the index and the CARv1 size. - f2, err := os.Create(dstPath) +// WrapV1 takes a CARv1 file and wraps it as a CARv2 file with an index. +// The resulting CARv2 file's inner CARv1 payload is left unmodified, +// and does not use any padding before the innner CARv1 or index. +func WrapV1(src io.ReadSeeker, dst io.Writer) error { + // TODO: verify src is indeed a CARv1 to prevent misuse. + // index.Generate should probably be in charge of that. + + idx, err := index.Generate(src) if err != nil { return err } - defer f2.Close() + + // Use Seek to learn the size of the CARv1 before reading it. + v1Size, err := src.Seek(0, io.SeekEnd) + if err != nil { + return err + } + if _, err := src.Seek(0, io.SeekStart); err != nil { + return err + } // Similar to the Writer API, write all components of a CARv2 to the // destination file: Pragma, Header, CARv1, Index. v2Header := NewHeader(uint64(v1Size)) - if _, err := f2.Write(Pragma); err != nil { + if _, err := dst.Write(Pragma); err != nil { return err } - if _, err := v2Header.WriteTo(f2); err != nil { + if _, err := v2Header.WriteTo(dst); err != nil { return err } - if _, err := io.Copy(f2, f1); err != nil { + if _, err := io.Copy(dst, src); err != nil { return err } - if err := index.WriteTo(idx, f2); err != nil { + if err := index.WriteTo(idx, dst); err != nil { return err } - // Check the close error, since we're writing to f2. - // Note that we also do a "defer f2.Close()" above, - // to make sure that the earlier error returns don't leak the file. - if err := f2.Close(); err != nil { - return err - } return nil } From 8ce2fa7495c3ff0d6d54d6f3b77abe23a076a901 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Mon, 5 Jul 2021 16:52:40 +0100 Subject: [PATCH 3464/3817] Replace `binary.Put/ReadUvarint` with `go-varint` To guarantee minimal encoding Fixes #135 Reflect on review comments Use `varint.PutUvarint` when dealing with writing bytes. This commit was moved from ipld/go-car@f598e5961ec244734e3cbf84c128f5b62b274960 --- ipld/car/v2/blockstore/readonly.go | 9 +++++---- ipld/car/v2/index/generator.go | 5 +++-- ipld/car/v2/index/index.go | 6 ++++-- ipld/car/v2/internal/carv1/util/util.go | 12 ++++++------ 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index ac603001e..521f30280 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -4,12 +4,13 @@ import ( "bufio" "bytes" "context" - "encoding/binary" "errors" "fmt" "io" "sync" + "github.com/multiformats/go-varint" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" @@ -103,7 +104,7 @@ func (b *ReadOnly) Has(key cid.Cid) (bool, error) { return false, err } uar := internalio.NewOffsetReader(b.backing, int64(offset)) - _, err = binary.ReadUvarint(uar) + _, err = varint.ReadUvarint(uar) if err != nil { return false, err } @@ -143,7 +144,7 @@ func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { if err != nil { return -1, err } - l, err := binary.ReadUvarint(internalio.NewOffsetReader(b.backing, int64(idx))) + l, err := varint.ReadUvarint(internalio.NewOffsetReader(b.backing, int64(idx))) if err != nil { return -1, blockstore.ErrNotFound } @@ -193,7 +194,7 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { rdr := internalio.NewOffsetReader(b.backing, int64(offset)) for { - l, err := binary.ReadUvarint(rdr) + l, err := varint.ReadUvarint(rdr) if err != nil { return // TODO: log this error } diff --git a/ipld/car/v2/index/generator.go b/ipld/car/v2/index/generator.go index 201b221cf..94841efc2 100644 --- a/ipld/car/v2/index/generator.go +++ b/ipld/car/v2/index/generator.go @@ -2,11 +2,12 @@ package index import ( "bufio" - "encoding/binary" "fmt" "io" "os" + "github.com/multiformats/go-varint" + "github.com/ipfs/go-cid" "github.com/ipld/go-car/v2/internal/carv1" ) @@ -51,7 +52,7 @@ func Generate(v1 io.ReadSeeker) (Index, error) { for { // Grab the length of the frame. // Note that ReadUvarint wants a ByteReader. - length, err := binary.ReadUvarint(readSeekerPlusByte{v1}) + length, err := varint.ReadUvarint(readSeekerPlusByte{v1}) if err != nil { if err == io.EOF { break diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 0b96c5f3d..53df02fbc 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -7,6 +7,8 @@ import ( "io" "os" + "github.com/multiformats/go-varint" + internalio "github.com/ipld/go-car/v2/internal/io" "github.com/ipfs/go-cid" @@ -83,7 +85,7 @@ func Attach(path string, idx Index, offset uint64) error { // This can then be read back using index.ReadFrom func WriteTo(idx Index, w io.Writer) error { buf := make([]byte, binary.MaxVarintLen64) - b := binary.PutUvarint(buf, uint64(idx.Codec())) + b := varint.PutUvarint(buf, uint64(idx.Codec())) if _, err := w.Write(buf[:b]); err != nil { return err } @@ -95,7 +97,7 @@ func WriteTo(idx Index, w io.Writer) error { // Returns error if the encoding is not known. func ReadFrom(r io.Reader) (Index, error) { reader := bufio.NewReader(r) - codec, err := binary.ReadUvarint(reader) + codec, err := varint.ReadUvarint(reader) if err != nil { return nil, err } diff --git a/ipld/car/v2/internal/carv1/util/util.go b/ipld/car/v2/internal/carv1/util/util.go index 9db5f8c15..297d74c8a 100644 --- a/ipld/car/v2/internal/carv1/util/util.go +++ b/ipld/car/v2/internal/carv1/util/util.go @@ -2,9 +2,10 @@ package util import ( "bufio" - "encoding/binary" "io" + "github.com/multiformats/go-varint" + cid "github.com/ipfs/go-cid" ) @@ -34,7 +35,7 @@ func LdWrite(w io.Writer, d ...[]byte) error { } buf := make([]byte, 8) - n := binary.PutUvarint(buf, sum) + n := varint.PutUvarint(buf, sum) _, err := w.Write(buf[:n]) if err != nil { return err @@ -55,9 +56,8 @@ func LdSize(d ...[]byte) uint64 { for _, s := range d { sum += uint64(len(s)) } - buf := make([]byte, 8) - n := binary.PutUvarint(buf, sum) - return sum + uint64(n) + s := varint.UvarintSize(sum) + return sum + uint64(s) } func LdRead(r *bufio.Reader) ([]byte, error) { @@ -65,7 +65,7 @@ func LdRead(r *bufio.Reader) ([]byte, error) { return nil, err } - l, err := binary.ReadUvarint(r) + l, err := varint.ReadUvarint(r) if err != nil { if err == io.EOF { return nil, io.ErrUnexpectedEOF // don't silently pretend this is a clean EOF From 10bd2b56dec1635320a5141f22d266ac2f64aee0 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Mon, 5 Jul 2021 14:10:15 +0100 Subject: [PATCH 3465/3817] Rename `ReadOnlyOf` to `NewReadOnly` and accept both v1 and v2 payload Rename the `blockstore.ReadOnlyOf` to `blockstore.NewReadOnly` for better readability and consistent naming. Allow the function to accept both v1 and v2 payloads and "do the right thing" depending on the input. Optionally, allow the caller to supply an index that, if present, will overrider any existing index. Otherwise an in-memory index is generated. Note, `blockstore.OpenReadOnly` only accepts v2 payload. Future PRs will change this function to also accept both versions for consistency, and drop modification of given path, delegating any writing to the write-specific APIs like `index.Attach` and `index.Generate`. Fixes #128 This commit was moved from ipld/go-car@4e863b281d91c5942c4439e25ce08182be629417 --- ipld/car/v2/blockstore/doc.go | 7 ++--- ipld/car/v2/blockstore/readonly.go | 45 ++++++++++++++++++++++++++--- ipld/car/v2/blockstore/readwrite.go | 2 +- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/ipld/car/v2/blockstore/doc.go b/ipld/car/v2/blockstore/doc.go index c2cf62a80..ddbcc98e6 100644 --- a/ipld/car/v2/blockstore/doc.go +++ b/ipld/car/v2/blockstore/doc.go @@ -3,10 +3,9 @@ // // The ReadOnly blockstore provides a read-only random access from a given data payload either in // unindexed v1 format or indexed/unindexed v2 format: -// - ReadOnly.ReadOnlyOf can be used to instantiate a new read-only blockstore for a given CAR v1 -// data payload and an existing index. See index.Generate for index generation from CAR v1 -// payload. -// - ReadOnly.OpenReadOnly can be used to instantiate a new read-only blockstore for a given CAR v2 +// * ReadOnly.NewReadOnly can be used to instantiate a new read-only blockstore for a given CAR v1 +// or CAR v2 data payload with an optional index override. +// * ReadOnly.OpenReadOnly can be used to instantiate a new read-only blockstore for a given CAR v2 // file with automatic index generation if the index is not present in the given file. This // function can optionally attach the index to the given CAR v2 file. // diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 521f30280..4125cc1aa 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -42,10 +42,47 @@ type ReadOnly struct { carv2Closer io.Closer } -// ReadOnlyOf opens ReadOnly blockstore from an existing backing containing a CAR v1 payload and an existing index. -// The index for a CAR v1 payload can be separately generated using index.Generate. -func ReadOnlyOf(backing io.ReaderAt, index index.Index) *ReadOnly { - return &ReadOnly{backing: backing, idx: index} +// NewReadOnly creates a new ReadOnly blockstore from the backing with a optional index as idx. +// This function accepts both CAR v1 and v2 backing. +// The blockstore is instantiated with the given index if it is not nil. +// +// Otherwise: +// * For a CAR v1 backing an index is generated. +// * For a CAR v2 backing an index is only generated if Header.HasIndex returns false. +// +// There is no need to call ReadOnly.Close on instances returned by this function. +func NewReadOnly(backing io.ReaderAt, idx index.Index) (*ReadOnly, error) { + version, err := carv2.ReadVersion(internalio.NewOffsetReader(backing, 0)) + if err != nil { + return nil, err + } + switch version { + case 1: + if idx == nil { + if idx, err = index.Generate(backing); err != nil { + return nil, err + } + } + return &ReadOnly{backing: backing, idx: idx}, nil + case 2: + v2r, err := carv2.NewReader(backing) + if err != nil { + return nil, err + } + if idx == nil { + if v2r.Header.HasIndex() { + idx, err = index.ReadFrom(v2r.IndexReader()) + if err != nil { + return nil, err + } + } else if idx, err = index.Generate(backing); err != nil { + return nil, err + } + } + return &ReadOnly{backing: v2r.CarV1Reader(), idx: idx}, nil + default: + return nil, fmt.Errorf("unsupported car version: %v", version) + } } // OpenReadOnly opens a read-only blockstore from a CAR v2 file, generating an index if it does not exist. diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 99fd11d2a..cf10a7750 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -88,7 +88,7 @@ func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, err } b.carV1Writer = internalio.NewOffsetWriter(f, int64(b.header.CarV1Offset)) carV1Reader := internalio.NewOffsetReader(f, int64(b.header.CarV1Offset)) - b.ReadOnly = *ReadOnlyOf(carV1Reader, idx) + b.ReadOnly = ReadOnly{backing: carV1Reader, idx: idx} if _, err := f.WriteAt(carv2.Pragma, 0); err != nil { return nil, err } From f93cb1b39bc8e9623149cbef966c8f5ee844328f Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Mon, 5 Jul 2021 16:04:44 +0100 Subject: [PATCH 3466/3817] Accommodate `index.Generate` taking `io.ReadSeeker` Accommodate to the `index.Generate` signature change, done in #136. Insead of implementng a new reader that converts `io.ReaderAt` to `io.ReadSeaker`, make `internalio.OffsetReader` partially implement `Seek`. The "partial" refers to inability to satisfy `Seek` calls with `io.SeekEnd` whence since the point of `internalio.OffsetReader` is that it needs not to know the total size of a file, i.e. cannot know its end. Instead, it panics if `Seek` is called with whence `io.SeekEnd`. None of this matterns much since it is all placed under internal APIs. This commit was moved from ipld/go-car@c0023edfedbc7fa45bd180009e47e9291f364d83 --- ipld/car/v2/blockstore/readonly.go | 46 +++++++++---- ipld/car/v2/blockstore/readwrite.go | 2 +- ipld/car/v2/internal/io/offset_read_seeker.go | 65 +++++++++++++++++++ ipld/car/v2/internal/io/offset_reader.go | 49 -------------- ipld/car/v2/reader.go | 4 +- 5 files changed, 103 insertions(+), 63 deletions(-) create mode 100644 ipld/car/v2/internal/io/offset_read_seeker.go delete mode 100644 ipld/car/v2/internal/io/offset_reader.go diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 4125cc1aa..6ffb6b3bb 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -52,14 +52,14 @@ type ReadOnly struct { // // There is no need to call ReadOnly.Close on instances returned by this function. func NewReadOnly(backing io.ReaderAt, idx index.Index) (*ReadOnly, error) { - version, err := carv2.ReadVersion(internalio.NewOffsetReader(backing, 0)) + version, err := readVersion(backing) if err != nil { return nil, err } switch version { case 1: if idx == nil { - if idx, err = index.Generate(backing); err != nil { + if idx, err = generateIndex(backing); err != nil { return nil, err } } @@ -75,7 +75,7 @@ func NewReadOnly(backing io.ReaderAt, idx index.Index) (*ReadOnly, error) { if err != nil { return nil, err } - } else if idx, err = index.Generate(backing); err != nil { + } else if idx, err = generateIndex(v2r.CarV1Reader()); err != nil { return nil, err } } @@ -85,6 +85,28 @@ func NewReadOnly(backing io.ReaderAt, idx index.Index) (*ReadOnly, error) { } } +func readVersion(at io.ReaderAt) (uint64, error) { + var rr io.Reader + switch r := at.(type) { + case io.Reader: + rr = r + default: + rr = internalio.NewOffsetReadSeeker(r, 0) + } + return carv2.ReadVersion(rr) +} + +func generateIndex(at io.ReaderAt) (index.Index, error) { + var rs io.ReadSeeker + switch r := at.(type) { + case io.ReadSeeker: + rs = r + default: + rs = internalio.NewOffsetReadSeeker(r, 0) + } + return index.Generate(rs) +} + // OpenReadOnly opens a read-only blockstore from a CAR v2 file, generating an index if it does not exist. // If attachIndex is set to true and the index is not present in the given CAR v2 file, // then the generated index is written into the given path. @@ -120,7 +142,7 @@ func OpenReadOnly(path string, attachIndex bool) (*ReadOnly, error) { } func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) { - bcid, data, err := util.ReadNode(bufio.NewReader(internalio.NewOffsetReader(b.backing, idx))) + bcid, data, err := util.ReadNode(bufio.NewReader(internalio.NewOffsetReadSeeker(b.backing, idx))) return bcid, data, err } @@ -140,7 +162,7 @@ func (b *ReadOnly) Has(key cid.Cid) (bool, error) { } else if err != nil { return false, err } - uar := internalio.NewOffsetReader(b.backing, int64(offset)) + uar := internalio.NewOffsetReadSeeker(b.backing, int64(offset)) _, err = varint.ReadUvarint(uar) if err != nil { return false, err @@ -181,11 +203,11 @@ func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { if err != nil { return -1, err } - l, err := varint.ReadUvarint(internalio.NewOffsetReader(b.backing, int64(idx))) + l, err := varint.ReadUvarint(internalio.NewOffsetReadSeeker(b.backing, int64(idx))) if err != nil { return -1, blockstore.ErrNotFound } - _, c, err := cid.CidFromReader(internalio.NewOffsetReader(b.backing, int64(idx+l))) + _, c, err := cid.CidFromReader(internalio.NewOffsetReadSeeker(b.backing, int64(idx+l))) if err != nil { return 0, err } @@ -212,7 +234,7 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { b.mu.RLock() // TODO we may use this walk for populating the index, and we need to be able to iterate keys in this way somewhere for index generation. In general though, when it's asked for all keys from a blockstore with an index, we should iterate through the index when possible rather than linear reads through the full car. - header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReader(b.backing, 0))) + header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReadSeeker(b.backing, 0))) if err != nil { return nil, fmt.Errorf("error reading car header: %w", err) } @@ -229,7 +251,7 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { defer close(ch) - rdr := internalio.NewOffsetReader(b.backing, int64(offset)) + rdr := internalio.NewOffsetReadSeeker(b.backing, int64(offset)) for { l, err := varint.ReadUvarint(rdr) if err != nil { @@ -240,7 +262,9 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { if err != nil { return // TODO: log this error } - rdr.SeekOffset(thisItemForNxt + int64(l)) + if _, err := rdr.Seek(thisItemForNxt+int64(l), io.SeekStart); err != nil { + return // TODO: log this error + } select { case ch <- c: @@ -259,7 +283,7 @@ func (b *ReadOnly) HashOnRead(bool) { // Roots returns the root CIDs of the backing CAR. func (b *ReadOnly) Roots() ([]cid.Cid, error) { - header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReader(b.backing, 0))) + header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReadSeeker(b.backing, 0))) if err != nil { return nil, fmt.Errorf("error reading car header: %w", err) } diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index cf10a7750..68f8cfab1 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -87,7 +87,7 @@ func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, err opt(b) } b.carV1Writer = internalio.NewOffsetWriter(f, int64(b.header.CarV1Offset)) - carV1Reader := internalio.NewOffsetReader(f, int64(b.header.CarV1Offset)) + carV1Reader := internalio.NewOffsetReadSeeker(f, int64(b.header.CarV1Offset)) b.ReadOnly = ReadOnly{backing: carV1Reader, idx: idx} if _, err := f.WriteAt(carv2.Pragma, 0); err != nil { return nil, err diff --git a/ipld/car/v2/internal/io/offset_read_seeker.go b/ipld/car/v2/internal/io/offset_read_seeker.go new file mode 100644 index 000000000..c92c3154c --- /dev/null +++ b/ipld/car/v2/internal/io/offset_read_seeker.go @@ -0,0 +1,65 @@ +package io + +import "io" + +var ( + _ io.ReaderAt = (*OffsetReadSeeker)(nil) + _ io.ReadSeeker = (*OffsetReadSeeker)(nil) +) + +// OffsetReadSeeker implements Read, and ReadAt on a section +// of an underlying io.ReaderAt. +// The main difference between io.SectionReader and OffsetReadSeeker is that +// NewOffsetReadSeeker does not require the user to know the number of readable bytes. +// +// It also partially implements Seek, where the implementation panics if io.SeekEnd is passed. +// This is because, OffsetReadSeeker does not know the end of the file therefore cannot seek relative +// to it. +type OffsetReadSeeker struct { + r io.ReaderAt + base int64 + off int64 +} + +// NewOffsetReadSeeker returns an OffsetReadSeeker that reads from r +// starting offset offset off and stops with io.EOF when r reaches its end. +// The Seek function will panic if whence io.SeekEnd is passed. +func NewOffsetReadSeeker(r io.ReaderAt, off int64) *OffsetReadSeeker { + return &OffsetReadSeeker{r, off, off} +} + +func (o *OffsetReadSeeker) Read(p []byte) (n int, err error) { + n, err = o.r.ReadAt(p, o.off) + o.off += int64(n) + return +} + +func (o *OffsetReadSeeker) ReadAt(p []byte, off int64) (n int, err error) { + if off < 0 { + return 0, io.EOF + } + off += o.base + return o.r.ReadAt(p, off) +} + +func (o *OffsetReadSeeker) ReadByte() (byte, error) { + b := []byte{0} + _, err := o.Read(b) + return b[0], err +} + +func (o *OffsetReadSeeker) Offset() int64 { + return o.off +} + +func (o *OffsetReadSeeker) Seek(offset int64, whence int) (int64, error) { + switch whence { + case io.SeekStart: + o.off = offset + case io.SeekCurrent: + o.off += offset + case io.SeekEnd: + panic("unsupported whence: SeekEnd") + } + return o.off, nil +} diff --git a/ipld/car/v2/internal/io/offset_reader.go b/ipld/car/v2/internal/io/offset_reader.go deleted file mode 100644 index 03f7e547f..000000000 --- a/ipld/car/v2/internal/io/offset_reader.go +++ /dev/null @@ -1,49 +0,0 @@ -package io - -import "io" - -var _ io.ReaderAt = (*OffsetReader)(nil) - -// OffsetReader implements Read, and ReadAt on a section -// of an underlying io.ReaderAt. -// The main difference between io.SectionReader and OffsetReader is that -// NewOffsetReader does not require the user to know the number of readable bytes. -type OffsetReader struct { - r io.ReaderAt - base int64 - off int64 -} - -// NewOffsetReader returns an OffsetReader that reads from r -// starting offset offset off and stops with io.EOF when r reaches its end. -func NewOffsetReader(r io.ReaderAt, off int64) *OffsetReader { - return &OffsetReader{r, off, off} -} - -func (o *OffsetReader) Read(p []byte) (n int, err error) { - n, err = o.r.ReadAt(p, o.off) - o.off += int64(n) - return -} - -func (o *OffsetReader) ReadAt(p []byte, off int64) (n int, err error) { - if off < 0 { - return 0, io.EOF - } - off += o.base - return o.r.ReadAt(p, off) -} - -func (o *OffsetReader) ReadByte() (byte, error) { - b := []byte{0} - _, err := o.Read(b) - return b[0], err -} - -func (o *OffsetReader) Offset() int64 { - return o.off -} - -func (o *OffsetReader) SeekOffset(off int64) { - o.off = off -} diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index d8b822635..6cf684c74 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -54,7 +54,7 @@ func NewReader(r io.ReaderAt) (*Reader, error) { } func (r *Reader) requireVersion2() (err error) { - or := internalio.NewOffsetReader(r.r, 0) + or := internalio.NewOffsetReadSeeker(r.r, 0) version, err := ReadVersion(or) if err != nil { return @@ -100,7 +100,7 @@ func (r *Reader) CarV1Reader() SectionReader { // IndexReader provides an io.Reader containing the index of this CAR v2. func (r *Reader) IndexReader() io.Reader { - return internalio.NewOffsetReader(r.r, int64(r.Header.IndexOffset)) + return internalio.NewOffsetReadSeeker(r.r, int64(r.Header.IndexOffset)) } // Close closes the underlying reader if it was opened by NewReaderMmap. From 750b3b50deba82ee44c6f006ccc7c8a61df30981 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Mon, 5 Jul 2021 17:55:55 +0100 Subject: [PATCH 3467/3817] Consistently accept CAR v1 or v2 on `blockstore.OpenReadOnly` Since `blockstore.NewReadOnly` accepts both versions, and the `attach` flag is never used in `OpenReadOnly` make things consistent and arguably more useful by accepting both v1 and v2 CAR files. This also avoids modifying files passed to a read-only API which is always sweet as sugar. This commit was moved from ipld/go-car@2776842e99101cc3d850916b6392076745ccaca2 --- ipld/car/v2/blockstore/doc.go | 4 +-- ipld/car/v2/blockstore/readonly.go | 40 ++++++++---------------- ipld/car/v2/blockstore/readwrite_test.go | 2 +- ipld/car/v2/example_test.go | 2 +- 4 files changed, 16 insertions(+), 32 deletions(-) diff --git a/ipld/car/v2/blockstore/doc.go b/ipld/car/v2/blockstore/doc.go index ddbcc98e6..4d6a1a448 100644 --- a/ipld/car/v2/blockstore/doc.go +++ b/ipld/car/v2/blockstore/doc.go @@ -6,8 +6,6 @@ // * ReadOnly.NewReadOnly can be used to instantiate a new read-only blockstore for a given CAR v1 // or CAR v2 data payload with an optional index override. // * ReadOnly.OpenReadOnly can be used to instantiate a new read-only blockstore for a given CAR v2 -// file with automatic index generation if the index is not present in the given file. This -// function can optionally attach the index to the given CAR v2 file. -// +// or car v2 file with automatic index generation if the index is not present. package blockstore diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 6ffb6b3bb..362345d3e 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -9,6 +9,8 @@ import ( "io" "sync" + "golang.org/x/exp/mmap" + "github.com/multiformats/go-varint" blocks "github.com/ipfs/go-block-format" @@ -107,38 +109,22 @@ func generateIndex(at io.ReaderAt) (index.Index, error) { return index.Generate(rs) } -// OpenReadOnly opens a read-only blockstore from a CAR v2 file, generating an index if it does not exist. -// If attachIndex is set to true and the index is not present in the given CAR v2 file, -// then the generated index is written into the given path. -func OpenReadOnly(path string, attachIndex bool) (*ReadOnly, error) { - v2r, err := carv2.NewReaderMmap(path) +// OpenReadOnly opens a read-only blockstore from a CAR file (either v1 or v2), generating an index if it does not exist. +// Note, the generated index if the index does not exist is ephemeral and only stored in memory. +// See index.Generate and Index.Attach for persisting index onto a CAR file. +func OpenReadOnly(path string) (*ReadOnly, error) { + f, err := mmap.Open(path) if err != nil { return nil, err } - var idx index.Index - if !v2r.Header.HasIndex() { - idx, err := index.Generate(v2r.CarV1Reader()) - if err != nil { - return nil, err - } - if attachIndex { - if err := index.Attach(path, idx, v2r.Header.IndexOffset); err != nil { - return nil, err - } - } - } else { - idx, err = index.ReadFrom(v2r.IndexReader()) - if err != nil { - return nil, err - } - } - obj := ReadOnly{ - backing: v2r.CarV1Reader(), - idx: idx, - carv2Closer: v2r, + robs, err := NewReadOnly(f, nil) + if err != nil { + return nil, err } - return &obj, nil + robs.carv2Closer = f + + return robs, nil } func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) { diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 3b66b3c4f..808e40d72 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -65,7 +65,7 @@ func TestBlockstore(t *testing.T) { err = ingester.Finalize() require.NoError(t, err) - carb, err := blockstore.OpenReadOnly(path, false) + carb, err := blockstore.OpenReadOnly(path) require.NoError(t, err) t.Cleanup(func() { carb.Close() }) diff --git a/ipld/car/v2/example_test.go b/ipld/car/v2/example_test.go index 93dedd772..d590ab89f 100644 --- a/ipld/car/v2/example_test.go +++ b/ipld/car/v2/example_test.go @@ -45,7 +45,7 @@ func ExampleWrapV1File() { fmt.Println("Inner CARv1 is exactly the same:", bytes.Equal(orig, inner)) // Verify that the CARv2 works well with its index. - bs, err := blockstore.OpenReadOnly(dst, false) + bs, err := blockstore.OpenReadOnly(dst) if err != nil { panic(err) } From b8de35aae0ef08dcfb2de3a7b61d8d3c4a8f77eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Mon, 5 Jul 2021 18:37:33 +0100 Subject: [PATCH 3468/3817] index: add support for null paddings in Generate Fixes #140. This commit was moved from ipld/go-car@6765c84a9741c56866e0891f3ace0a3112fc43af --- ipld/car/v2/blockstore/readonly.go | 10 ++++-- ipld/car/v2/blockstore/readwrite_test.go | 41 ++++++++++++++++++++++++ ipld/car/v2/index/generator.go | 7 ++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 362345d3e..4f91a29c4 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -239,16 +239,22 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { rdr := internalio.NewOffsetReadSeeker(b.backing, int64(offset)) for { - l, err := varint.ReadUvarint(rdr) + length, err := varint.ReadUvarint(rdr) if err != nil { return // TODO: log this error } + + // Null padding; treat it as EOF. + if length == 0 { + break + } + thisItemForNxt := rdr.Offset() _, c, err := cid.CidFromReader(rdr) if err != nil { return // TODO: log this error } - if _, err := rdr.Seek(thisItemForNxt+int64(l), io.SeekStart); err != nil { + if _, err := rdr.Seek(thisItemForNxt+int64(length), io.SeekStart); err != nil { return // TODO: log this error } diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 808e40d72..a693f384b 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "io/ioutil" "math/rand" "os" "path/filepath" @@ -232,3 +233,43 @@ func TestBlockstoreConcurrentUse(t *testing.T) { } wg.Wait() } + +type bufferReaderAt []byte + +func (b bufferReaderAt) ReadAt(p []byte, off int64) (int, error) { + if off >= int64(len(b)) { + return 0, io.EOF + } + return copy(p, b[off:]), nil +} + +func TestBlockstoreNullPadding(t *testing.T) { + paddedV1, err := ioutil.ReadFile("testdata/test.car") + require.NoError(t, err) + + // A sample null-padded CARv1 file. + paddedV1 = append(paddedV1, make([]byte, 2048)...) + + rbs, err := blockstore.NewReadOnly(bufferReaderAt(paddedV1), nil) + require.NoError(t, err) + + roots, err := rbs.Roots() + require.NoError(t, err) + + has, err := rbs.Has(roots[0]) + require.NoError(t, err) + require.True(t, has) + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + allKeysCh, err := rbs.AllKeysChan(ctx) + require.NoError(t, err) + for c := range allKeysCh { + b, err := rbs.Get(c) + require.NoError(t, err) + if !b.Cid().Equals(c) { + t.Fatal("wrong item returned") + } + } +} diff --git a/ipld/car/v2/index/generator.go b/ipld/car/v2/index/generator.go index 94841efc2..1781f8dcf 100644 --- a/ipld/car/v2/index/generator.go +++ b/ipld/car/v2/index/generator.go @@ -60,6 +60,13 @@ func Generate(v1 io.ReadSeeker) (Index, error) { return nil, err } + // Null padding; treat zero-length frames as an EOF. + // They don't contain a CID nor block, so they're not useful. + // TODO: Amend the CARv1 spec to explicitly allow this. + if length == 0 { + break + } + // Grab the CID. n, c, err := cid.CidFromReader(v1) if err != nil { From 5e55b0400d207045c4a1a94cf18bc38c24cc5d0f Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 8 Jul 2021 11:34:14 +0100 Subject: [PATCH 3469/3817] Implement read or generate index from CAR accepting v1 or v2 A user has to undergo a lot of boilerplate code to generate or get an index where there is a CAR file, the version for which is unknown. Provide a function that accepts either v1 or v2 payload and reads the index if present, otherwise generates it for the user. The generated index lives entirely in memory and will not depend on the given `io.ReadSeeker` to lookup indices. Implement tests that check index is returned for v1 or v2 files. Add a sample file with an imaginary version of 42 for testing purposes to assert that the indexing function will error if the given file is not v1 or v2. See: https://github.com/ipld/go-car/issues/143 This commit was moved from ipld/go-car@fc494d46623af156f057ef363c1ee2a7ffe0cb13 --- ipld/car/v2/reader.go | 62 ++++++++++++++++++ ipld/car/v2/reader_test.go | 67 ++++++++++++++++++++ ipld/car/v2/testdata/sample-rootless-v42.car | 1 + 3 files changed, 130 insertions(+) create mode 100644 ipld/car/v2/reader_test.go create mode 100644 ipld/car/v2/testdata/sample-rootless-v42.car diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 6cf684c74..ae11792ae 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -4,6 +4,9 @@ import ( "bufio" "fmt" "io" + "sync" + + "github.com/ipld/go-car/v2/index" internalio "github.com/ipld/go-car/v2/internal/io" @@ -121,3 +124,62 @@ func ReadVersion(r io.Reader) (version uint64, err error) { } return header.Version, nil } + +var _ io.ReaderAt = (*readSeekerAt)(nil) + +type readSeekerAt struct { + rs io.ReadSeeker + mu sync.RWMutex +} + +func (rsa *readSeekerAt) ReadAt(p []byte, off int64) (n int, err error) { + rsa.mu.RLock() + defer rsa.mu.RUnlock() + if _, err := rsa.rs.Seek(off, io.SeekStart); err != nil { + return 0, err + } + return rsa.rs.Read(p) +} + +// ReadOrGenerateIndex accepts both CAR v1 and v2 format, and reads or generates an index for it. +// When the given reader is in CAR v1 format an index is always generated. +// For a payload in CAR v2 format, an index is only generated if Header.HasIndex returns false. +// An error is returned for all other formats, i.e. versions other than 1 or 2. +// +// Note, the returned index lives entirely in memory and will not depend on the +// given reader to fulfill index lookup. +func ReadOrGenerateIndex(rs io.ReadSeeker) (index.Index, error) { + // Seek to the beginning of the reader in order to read version. + if _, err := rs.Seek(0, io.SeekStart); err != nil { + return nil, err + } + // Read version. + version, err := ReadVersion(rs) + if err != nil { + return nil, err + } + // Seek to the begining, since reading the version changes the reader's offset. + if _, err := rs.Seek(0, io.SeekStart); err != nil { + return nil, err + } + + switch version { + case 1: + // Simply generate the index, since there can't be a pre-existing one. + return index.Generate(rs) + case 2: + // Read CAR v2 format + v2r, err := NewReader(&readSeekerAt{rs: rs}) + if err != nil { + return nil, err + } + // If index is present, then no need to generate; decode and return it. + if v2r.Header.HasIndex() { + return index.ReadFrom(v2r.IndexReader()) + } + // Otherwise, generate index from CAR v1 payload wrapped within CAR v2 format. + return index.Generate(v2r.CarV1Reader()) + default: + return nil, fmt.Errorf("unknown version %v", version) + } +} diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go new file mode 100644 index 000000000..c172315cd --- /dev/null +++ b/ipld/car/v2/reader_test.go @@ -0,0 +1,67 @@ +package car + +import ( + "github.com/ipld/go-car/v2/index" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "os" + "testing" +) + +func TestReadOrGenerateIndex(t *testing.T) { + tests := []struct { + name string + carPath string + wantIndexer func(t *testing.T) index.Index + wantErr bool + }{ + { + "CarV1IsIndexedAsExpected", + "testdata/sample-v1.car", + func(t *testing.T) index.Index { + v1, err := os.Open("testdata/sample-v1.car") + require.NoError(t, err) + defer v1.Close() + want, err := index.Generate(v1) + require.NoError(t, err) + return want + }, + false, + }, + { + "CarV2WithIndexIsReturnedAsExpected", + "testdata/sample-v1.car", + func(t *testing.T) index.Index { + v2, err := os.Open("testdata/sample-wrapped-v2.car") + require.NoError(t, err) + defer v2.Close() + reader, err := NewReader(v2) + require.NoError(t, err) + want, err := index.ReadFrom(reader.IndexReader()) + require.NoError(t, err) + return want + }, + false, + }, + { + "CarOtherThanV1OrV2IsError", + "testdata/sample-rootless-v42.car", + func(t *testing.T) index.Index { return nil }, + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + carFile, err := os.Open(tt.carPath) + require.NoError(t, err) + t.Cleanup(func() { assert.NoError(t, carFile.Close()) }) + got, err := ReadOrGenerateIndex(carFile) + if tt.wantErr { + require.Error(t, err) + } + want := tt.wantIndexer(t) + require.Equal(t, want, got) + }) + } +} diff --git a/ipld/car/v2/testdata/sample-rootless-v42.car b/ipld/car/v2/testdata/sample-rootless-v42.car new file mode 100644 index 000000000..846b71a5a --- /dev/null +++ b/ipld/car/v2/testdata/sample-rootless-v42.car @@ -0,0 +1 @@ +¢erootsögversion* \ No newline at end of file From 793dca74963ef120afa118ffc9c0eb33d841c0ab Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 8 Jul 2021 11:45:43 +0100 Subject: [PATCH 3470/3817] Replace redundant use of RW mutex where a simple mutex would do Simply use mutex where reader will only do reading. This commit was moved from ipld/go-car@a6e8bc1bcce71a0f254d05d1429cfc1c1864c62b --- ipld/car/v2/reader.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index ae11792ae..0a8aefac4 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -129,12 +129,12 @@ var _ io.ReaderAt = (*readSeekerAt)(nil) type readSeekerAt struct { rs io.ReadSeeker - mu sync.RWMutex + mu sync.Mutex } func (rsa *readSeekerAt) ReadAt(p []byte, off int64) (n int, err error) { - rsa.mu.RLock() - defer rsa.mu.RUnlock() + rsa.mu.Lock() + defer rsa.mu.Unlock() if _, err := rsa.rs.Seek(off, io.SeekStart); err != nil { return 0, err } From c3e3a0bfe75956235de12038192fae6bb321d077 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 8 Jul 2021 10:11:46 +0100 Subject: [PATCH 3471/3817] Implement ReadWrite blockstore resumption * Implement an option for read-write blockstore, that if enabled the write can resume from where the writer left off. For resumption to work the `WithResumption` option needs to be set explicitly. Otherwise, if path to an existing file is passed, the blockstore construction will return an error. The resumption requires the roots passed to constructor as well as padding options to be identical with roots in file. Resumption only works on paths where at least V2 pragma and CAR v1 header was successfully written onto the file. Otherwise an error is returned. * Implement resumption test that verifies files resumed from match expected header, data and index. * Implement a CAR v1 equals function to check if two given headers are identical. This implementation requires exact ordering of root elements. A TODO is left to relax the exact ordering requirement. * Implement Seeker in internal offset writer in order to forward offset of CAR v1 writer within a resumed read-write blockstore after resumption. The offset of the writer needs to be set to the latest written frame in order for consecutive writes to be at the right offset. * Fix bug in offset read seeker in internal IO, where seek and returned position was not normalized by the base. Reflect the fix in read-only blockstore AllKeysChan where reader was twisted to work. We now read the header to get its size, then seek past it to then iterate over blocks to populate the channel. * Add TODOs in places to make treating zero-length frames as EOF optional; See #140 for context. * Run `gofumpt -l -w .` on everything to maintain consistent formatting. Address review comments * Implement equality check for CAR v1 headers where roots in different order are considered to be equal. * Improve resumption docs to clarify what matching roots mean. This commit was moved from ipld/go-car@2a5f8942ddae910f4fa9d2a822b103e87aaac1c4 --- ipld/car/v2/blockstore/doc.go | 14 +- ipld/car/v2/blockstore/readonly.go | 15 +- ipld/car/v2/blockstore/readwrite.go | 174 +++++++++++++++--- ipld/car/v2/blockstore/readwrite_test.go | 98 +++++++++- ipld/car/v2/internal/carv1/car.go | 36 ++++ ipld/car/v2/internal/carv1/car_test.go | 68 +++++++ ipld/car/v2/internal/io/offset_read_seeker.go | 9 +- .../car/v2/internal/io/offset_write_seeker.go | 41 +++++ ipld/car/v2/internal/io/offset_writer.go | 26 --- ipld/car/v2/reader_test.go | 6 +- 10 files changed, 415 insertions(+), 72 deletions(-) create mode 100644 ipld/car/v2/internal/io/offset_write_seeker.go delete mode 100644 ipld/car/v2/internal/io/offset_writer.go diff --git a/ipld/car/v2/blockstore/doc.go b/ipld/car/v2/blockstore/doc.go index 4d6a1a448..e31c04329 100644 --- a/ipld/car/v2/blockstore/doc.go +++ b/ipld/car/v2/blockstore/doc.go @@ -5,7 +5,15 @@ // unindexed v1 format or indexed/unindexed v2 format: // * ReadOnly.NewReadOnly can be used to instantiate a new read-only blockstore for a given CAR v1 // or CAR v2 data payload with an optional index override. -// * ReadOnly.OpenReadOnly can be used to instantiate a new read-only blockstore for a given CAR v2 -// or car v2 file with automatic index generation if the index is not present. - +// * ReadOnly.OpenReadOnly can be used to instantiate a new read-only blockstore for a given CAR v1 +// or CAR v2 file with automatic index generation if the index is not present. +// +// The ReadWrite blockstore allows writing and reading of the blocks concurrently. The user of this +// blockstore is responsible for calling ReadWrite.Finalize when finished writing blocks. +// Upon finalization, the instance can no longer be used for reading or writing blocks and will +// panic if used. To continue reading the blocks users are encouraged to use ReadOnly blockstore +// instantiated from the same file path using OpenReadOnly. +// A user may resume reading/writing from files produced by an instance of ReadWrite blockstore that +// on which ReadWrite.Finalize was never called. To resume, WithResumption option must be set to +// true. See docs on WithResumption for usage criteria. package blockstore diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 4f91a29c4..8ef74833e 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -220,11 +220,12 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { b.mu.RLock() // TODO we may use this walk for populating the index, and we need to be able to iterate keys in this way somewhere for index generation. In general though, when it's asked for all keys from a blockstore with an index, we should iterate through the index when possible rather than linear reads through the full car. - header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReadSeeker(b.backing, 0))) + rdr := internalio.NewOffsetReadSeeker(b.backing, 0) + header, err := carv1.ReadHeader(bufio.NewReader(rdr)) if err != nil { return nil, fmt.Errorf("error reading car header: %w", err) } - offset, err := carv1.HeaderSize(header) + headerSize, err := carv1.HeaderSize(header) if err != nil { return nil, err } @@ -232,12 +233,15 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // TODO: document this choice of 5, or use simpler buffering like 0 or 1. ch := make(chan cid.Cid, 5) + // Seek to the end of header. + if _, err = rdr.Seek(int64(headerSize), io.SeekStart); err != nil { + return nil, err + } + go func() { defer b.mu.RUnlock() - defer close(ch) - rdr := internalio.NewOffsetReadSeeker(b.backing, int64(offset)) for { length, err := varint.ReadUvarint(rdr) if err != nil { @@ -246,7 +250,7 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // Null padding; treat it as EOF. if length == 0 { - break + break // TODO make this an optional behaviour; by default we should error } thisItemForNxt := rdr.Offset() @@ -261,6 +265,7 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { select { case ch <- c: case <-ctx.Done(): + // TODO: log ctx error return } } diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 68f8cfab1..286b55e88 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -1,13 +1,18 @@ package blockstore import ( + "bufio" "context" + "errors" "fmt" + "io" "os" + "github.com/ipld/go-car/v2/internal/carv1" + "github.com/multiformats/go-varint" + blockstore "github.com/ipfs/go-ipfs-blockstore" carv2 "github.com/ipld/go-car/v2" - "github.com/ipld/go-car/v2/internal/carv1" internalio "github.com/ipld/go-car/v2/internal/io" "github.com/ipld/go-car/v2/index" @@ -24,17 +29,19 @@ var _ blockstore.Blockstore = (*ReadWrite)(nil) // This implementation is preferable for a write-heavy workload. // The blocks are written immediately on Put and PutAll calls, while the index is stored in memory // and updated incrementally. +// // The Finalize function must be called once the putting blocks are finished. // Upon calling Finalize header is finalized and index is written out. // Once finalized, all read and write calls to this blockstore will result in panics. type ReadWrite struct { f *os.File - carV1Writer *internalio.OffsetWriter + carV1Writer *internalio.OffsetWriteSeeker ReadOnly idx *index.InsertionIndex header carv2.Header dedupCids bool + resume bool } // TODO consider exposing interfaces @@ -59,50 +66,165 @@ func WithIndexPadding(p uint64) Option { // This can help avoid redundancy in a CARv1's list of CID-Block pairs. // // Note that this compares whole CIDs, not just multihashes. -func WithCidDeduplication(b *ReadWrite) { +func WithCidDeduplication(b *ReadWrite) { // TODO should this take a bool and return an option to allow disabling dedupliation? b.dedupCids = true } -// NewReadWrite creates a new ReadWrite at the given path with a provided set of root CIDs as the car roots. +// WithResumption sets whether the blockstore should resume on a file produced by a ReadWrite +// blockstore on which ReadWrite.Finalize was not called. +// +// This option is set to false by default, i.e. disabled. When disabled, the file at path must not +// exist, otherwise an error is returned upon ReadWrite blockstore construction. +// +// When this option is set to true the existing data frames in file are re-indexed, allowing the +// caller to continue putting any remaining blocks without having to re-ingest blocks for which +// previous ReadWrite.Put returned successfully. +// +// Resumption is only allowed on files that satisfy the following criteria: +// 1. start with a complete CAR v2 car.Pragma. +// 2. contain a complete CAR v1 data header with root CIDs matching the CIDs passed to the +// constructor, starting at offset specified by WithCarV1Padding, followed by zero or more +// complete data frames. If any corrupt data frames are present the resumption will fail. Note, +// it is important that new instantiations of ReadWrite blockstore with resumption enabled use +// the same WithCarV1Padding option, since this option is used to locate the offset at which +// the data payload starts. +// 3. have not been produced by a ReadWrite blockstore that was finalized, i.e. call to +// ReadWrite.Finalize returned successfully. +func WithResumption(enabled bool) Option { + return func(b *ReadWrite) { + b.resume = enabled + } +} + +// NewReadWrite creates a new ReadWrite at the given path with a provided set of root CIDs and options. func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, error) { - // TODO support resumption if the path provided contains partially written blocks in v2 format. // TODO either lock the file or open exclusively; can we do somethign to reduce edge cases. - f, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0o666) - if err != nil { - return nil, fmt.Errorf("could not open read/write file: %w", err) + + b := &ReadWrite{ + header: carv2.NewHeader(0), } indexcls, ok := index.BuildersByCodec[index.IndexInsertion] if !ok { return nil, fmt.Errorf("unknownindex codec: %#v", index.IndexInsertion) } - idx := (indexcls()).(*index.InsertionIndex) + b.idx = (indexcls()).(*index.InsertionIndex) - b := &ReadWrite{ - f: f, - idx: idx, - header: carv2.NewHeader(0), - } for _, opt := range opts { opt(b) } - b.carV1Writer = internalio.NewOffsetWriter(f, int64(b.header.CarV1Offset)) - carV1Reader := internalio.NewOffsetReadSeeker(f, int64(b.header.CarV1Offset)) - b.ReadOnly = ReadOnly{backing: carV1Reader, idx: idx} - if _, err := f.WriteAt(carv2.Pragma, 0); err != nil { - return nil, err - } - v1Header := &carv1.CarHeader{ - Roots: roots, - Version: 1, + fFlag := os.O_RDWR | os.O_CREATE + if !b.resume { + fFlag = fFlag | os.O_EXCL } - if err := carv1.WriteHeader(v1Header, b.carV1Writer); err != nil { - return nil, fmt.Errorf("couldn't write car header: %w", err) + var err error + b.f, err = os.OpenFile(path, fFlag, 0o666) // TODO: Should the user be able to configure FileMode permissions? + if err != nil { + return nil, fmt.Errorf("could not open read/write file: %w", err) } + b.carV1Writer = internalio.NewOffsetWriter(b.f, int64(b.header.CarV1Offset)) + v1r := internalio.NewOffsetReadSeeker(b.f, int64(b.header.CarV1Offset)) + b.ReadOnly = ReadOnly{backing: v1r, idx: b.idx, carv2Closer: b.f} + + if b.resume { + if err = b.resumeWithRoots(roots); err != nil { + return nil, err + } + } else { + if err = b.initWithRoots(roots); err != nil { + return nil, err + } + } + return b, nil } +func (b *ReadWrite) initWithRoots(roots []cid.Cid) error { + if _, err := b.f.WriteAt(carv2.Pragma, 0); err != nil { + return err + } + return carv1.WriteHeader(&carv1.CarHeader{Roots: roots, Version: 1}, b.carV1Writer) +} + +func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { + // On resumption it is expected that the CAR v2 Pragma, and the CAR v1 header is successfully written. + // Otherwise we cannot resume from the file. + // Read pragma to assert if b.f is indeed a CAR v2. + version, err := carv2.ReadVersion(b.f) + if err != nil { + // The file is not a valid CAR file and cannot resume from it. + // Or the write must have failed before pragma was written. + return err + } + if version != 2 { + // The file is not a CAR v2 and we cannot resume from it. + return fmt.Errorf("cannot resume on CAR file with version %v", version) + } + + // Use the given CAR v1 padding to instantiate the CAR v1 reader on file. + v1r := internalio.NewOffsetReadSeeker(b.ReadOnly.backing, 0) + header, err := carv1.ReadHeader(bufio.NewReader(v1r)) + if err != nil { + // Cannot read the CAR v1 header; the file is most likely corrupt. + return fmt.Errorf("error reading car header: %w", err) + } + if !header.Equals(carv1.CarHeader{Roots: roots, Version: 1}) { + // Cannot resume if version and root does not match. + return errors.New("cannot resume on file with mismatching data header") + } + + // TODO See how we can reduce duplicate code here. + // The code here comes from index.Generate. + // Copied because we need to populate an insertindex, not a sorted index. + // Producing a sorted index via generate, then converting it to insertindex is not possible. + // Because Index interface does not expose internal records. + // This may be done as part of https://github.com/ipld/go-car/issues/95 + + offset, err := carv1.HeaderSize(header) + if err != nil { + return err + } + frameOffset := int64(0) + if frameOffset, err = v1r.Seek(int64(offset), io.SeekStart); err != nil { + return err + } + + for { + // Grab the length of the frame. + // Note that ReadUvarint wants a ByteReader. + length, err := varint.ReadUvarint(v1r) + if err != nil { + if err == io.EOF { + break + } + return err + } + + // Null padding; treat zero-length frames as an EOF. + // They don't contain a CID nor block, so they're not useful. + if length == 0 { + break // TODO This behaviour should be an option, not default. By default we should error. Hook this up to a write option + } + + // Grab the CID. + n, c, err := cid.CidFromReader(v1r) + if err != nil { + return err + } + b.idx.InsertNoReplace(c, uint64(frameOffset)) + + // Seek to the next frame by skipping the block. + // The frame length includes the CID, so subtract it. + if frameOffset, err = v1r.Seek(int64(length)-int64(n), io.SeekCurrent); err != nil { + return err + } + } + // Seek to the end of last skipped block where the writer should resume writing. + _, err = b.carV1Writer.Seek(frameOffset, io.SeekStart) + return err +} + func (b *ReadWrite) panicIfFinalized() { if b.header.CarV1Size != 0 { panic("must not use a read-write blockstore after finalizing") @@ -154,7 +276,7 @@ func (b *ReadWrite) Finalize() error { // TODO check if add index option is set and don't write the index then set index offset to zero. // TODO see if folks need to continue reading from a finalized blockstore, if so return ReadOnly blockstore here. b.header = b.header.WithCarV1Size(uint64(b.carV1Writer.Position())) - defer b.f.Close() + defer b.Close() if _, err := b.header.WriteTo(internalio.NewOffsetWriter(b.f, carv2.PragmaSize)); err != nil { return err } diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index a693f384b..914d20be1 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -12,6 +12,10 @@ import ( "testing" "time" + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/index" + "github.com/stretchr/testify/assert" + "github.com/multiformats/go-multihash" "github.com/stretchr/testify/require" @@ -28,7 +32,7 @@ func TestBlockstore(t *testing.T) { f, err := os.Open("testdata/test.car") require.NoError(t, err) - t.Cleanup(func() { f.Close() }) + t.Cleanup(func() { assert.NoError(t, f.Close()) }) r, err := carv1.NewCarReader(f) require.NoError(t, err) @@ -66,15 +70,15 @@ func TestBlockstore(t *testing.T) { err = ingester.Finalize() require.NoError(t, err) - carb, err := blockstore.OpenReadOnly(path) + robs, err := blockstore.OpenReadOnly(path) require.NoError(t, err) - t.Cleanup(func() { carb.Close() }) + t.Cleanup(func() { assert.NoError(t, robs.Close()) }) - allKeysCh, err := carb.AllKeysChan(ctx) + allKeysCh, err := robs.AllKeysChan(ctx) require.NoError(t, err) numKeysCh := 0 for c := range allKeysCh { - b, err := carb.Get(c) + b, err := robs.Get(c) require.NoError(t, err) if !b.Cid().Equals(c) { t.Fatal("wrong item returned") @@ -82,11 +86,11 @@ func TestBlockstore(t *testing.T) { numKeysCh++ } if numKeysCh != len(cids) { - t.Fatal("AllKeysChan returned an unexpected amount of keys") + t.Fatalf("AllKeysChan returned an unexpected amount of keys; expected %v but got %v", len(cids), numKeysCh) } for _, c := range cids { - b, err := carb.Get(c) + b, err := robs.Get(c) require.NoError(t, err) if !b.Cid().Equals(c) { t.Fatal("wrong item returned") @@ -273,3 +277,83 @@ func TestBlockstoreNullPadding(t *testing.T) { } } } + +func TestBlockstoreResumption(t *testing.T) { + v1f, err := os.Open("testdata/test.car") + require.NoError(t, err) + t.Cleanup(func() { assert.NoError(t, v1f.Close()) }) + r, err := carv1.NewCarReader(v1f) + require.NoError(t, err) + + path := filepath.Join(t.TempDir(), "readwrite-resume.car") + // Create an incomplete CAR v2 file with no blocks put. + subject, err := blockstore.NewReadWrite(path, r.Header.Roots) + require.NoError(t, err) + + // For each block resume on the same file, putting blocks one at a time. + for { + b, err := r.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + + // 30% chance of subject failing (or closing file without calling Finalize). + // The higher this percentage the slower the test runs considering the number of blocks in the original CAR v1 test payload. + shouldFailAbruptly := rand.Float32() <= 0.3 + if shouldFailAbruptly { + // Close off the open file and re-instantiate a new subject with resumption enabled. + // Note, we don't have to close the file for resumption to work. + // We do this to avoid resource leak during testing. + require.NoError(t, subject.Close()) + subject, err = blockstore.NewReadWrite(path, r.Header.Roots, blockstore.WithResumption(true)) + require.NoError(t, err) + } + require.NoError(t, subject.Put(b)) + } + require.NoError(t, subject.Close()) + + // Finalize the blockstore to complete partially written CAR v2 file. + subject, err = blockstore.NewReadWrite(path, r.Header.Roots, blockstore.WithResumption(true)) + require.NoError(t, err) + require.NoError(t, subject.Finalize()) + + // Assert resumed from file is a valid CAR v2 with index. + v2f, err := os.Open(path) + require.NoError(t, err) + t.Cleanup(func() { assert.NoError(t, v2f.Close()) }) + v2r, err := carv2.NewReader(v2f) + require.NoError(t, err) + require.True(t, v2r.Header.HasIndex()) + + // Assert CAR v1 payload in file matches the original CAR v1 payload. + _, err = v1f.Seek(0, io.SeekStart) + require.NoError(t, err) + wantPayloadReader, err := carv1.NewCarReader(v1f) + require.NoError(t, err) + + gotPayloadReader, err := carv1.NewCarReader(v2r.CarV1Reader()) + require.NoError(t, err) + + require.Equal(t, wantPayloadReader.Header, gotPayloadReader.Header) + for { + wantNextBlock, wantErr := wantPayloadReader.Next() + gotNextBlock, gotErr := gotPayloadReader.Next() + if wantErr == io.EOF { + require.Equal(t, wantErr, gotErr) + break + } + require.NoError(t, wantErr) + require.NoError(t, gotErr) + require.Equal(t, wantNextBlock, gotNextBlock) + } + + // Assert index in resumed from file is identical to index generated directly from original CAR v1 payload. + _, err = v1f.Seek(0, io.SeekStart) + require.NoError(t, err) + gotIdx, err := index.ReadFrom(v2r.IndexReader()) + require.NoError(t, err) + wantIdx, err := index.Generate(v1f) + require.NoError(t, err) + require.Equal(t, wantIdx, gotIdx) +} diff --git a/ipld/car/v2/internal/carv1/car.go b/ipld/car/v2/internal/carv1/car.go index d69f3a7b7..7332ebadb 100644 --- a/ipld/car/v2/internal/carv1/car.go +++ b/ipld/car/v2/internal/carv1/car.go @@ -209,3 +209,39 @@ func loadCarSlow(s Store, cr *CarReader) (*CarHeader, error) { } } } + +// Equals checks whether two headers are equal. +// Two headers are considered equal if: +// 1. They have the same version number, and +// 2. They contain the same root CIDs in any order. +func (h CarHeader) Equals(other CarHeader) bool { + if h.Version != other.Version { + return false + } + thisLen := len(h.Roots) + if thisLen != len(other.Roots) { + return false + } + // Headers with a single root are popular. + // Implement a fast execution path for popular cases. + if thisLen == 1 { + return h.Roots[0].Equals(other.Roots[0]) + } + + // Check other contains all roots. + for _, r := range h.Roots { + if !other.containsRoot(r) { + return false + } + } + return true +} + +func (h *CarHeader) containsRoot(root cid.Cid) bool { + for _, r := range h.Roots { + if r.Equals(root) { + return true + } + } + return false +} diff --git a/ipld/car/v2/internal/carv1/car_test.go b/ipld/car/v2/internal/carv1/car_test.go index b637fcf43..fb0b0db37 100644 --- a/ipld/car/v2/internal/carv1/car_test.go +++ b/ipld/car/v2/internal/carv1/car_test.go @@ -8,6 +8,8 @@ import ( "strings" "testing" + "github.com/stretchr/testify/require" + cid "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" @@ -229,3 +231,69 @@ func TestBadHeaders(t *testing.T) { }) } } + +func TestCarHeaderEquals(t *testing.T) { + oneCid := dag.NewRawNode([]byte("fish")).Cid() + anotherCid := dag.NewRawNode([]byte("lobster")).Cid() + tests := []struct { + name string + one CarHeader + other CarHeader + want bool + }{ + { + "SameVersionNilRootsIsEqual", + CarHeader{nil, 1}, + CarHeader{nil, 1}, + true, + }, + { + "SameVersionEmptyRootsIsEqual", + CarHeader{[]cid.Cid{}, 1}, + CarHeader{[]cid.Cid{}, 1}, + true, + }, + { + "SameVersionNonEmptySameRootsIsEqual", + CarHeader{[]cid.Cid{oneCid}, 1}, + CarHeader{[]cid.Cid{oneCid}, 1}, + true, + }, + { + "SameVersionNonEmptySameRootsInDifferentOrderIsEqual", + CarHeader{[]cid.Cid{oneCid, anotherCid}, 1}, + CarHeader{[]cid.Cid{anotherCid, oneCid}, 1}, + true, + }, + { + "SameVersionDifferentRootsIsNotEqual", + CarHeader{[]cid.Cid{oneCid}, 1}, + CarHeader{[]cid.Cid{anotherCid}, 1}, + false, + }, + { + "DifferentVersionDifferentRootsIsNotEqual", + CarHeader{[]cid.Cid{oneCid}, 0}, + CarHeader{[]cid.Cid{anotherCid}, 1}, + false, + }, + { + "MismatchingVersionIsNotEqual", + CarHeader{nil, 0}, + CarHeader{nil, 1}, + false, + }, + { + "ZeroValueHeadersAreEqual", + CarHeader{}, + CarHeader{}, + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.one.Equals(tt.other) + require.Equal(t, tt.want, got, "Equals() = %v, want %v", got, tt.want) + }) + } +} diff --git a/ipld/car/v2/internal/io/offset_read_seeker.go b/ipld/car/v2/internal/io/offset_read_seeker.go index c92c3154c..4b701351f 100644 --- a/ipld/car/v2/internal/io/offset_read_seeker.go +++ b/ipld/car/v2/internal/io/offset_read_seeker.go @@ -55,11 +55,16 @@ func (o *OffsetReadSeeker) Offset() int64 { func (o *OffsetReadSeeker) Seek(offset int64, whence int) (int64, error) { switch whence { case io.SeekStart: - o.off = offset + o.off = offset + o.base case io.SeekCurrent: o.off += offset case io.SeekEnd: panic("unsupported whence: SeekEnd") } - return o.off, nil + return o.Position(), nil +} + +// Position returns the current position of this reader relative to the initial offset. +func (o *OffsetReadSeeker) Position() int64 { + return o.off - o.base } diff --git a/ipld/car/v2/internal/io/offset_write_seeker.go b/ipld/car/v2/internal/io/offset_write_seeker.go new file mode 100644 index 000000000..7e0f6ba58 --- /dev/null +++ b/ipld/car/v2/internal/io/offset_write_seeker.go @@ -0,0 +1,41 @@ +package io + +import "io" + +var ( + _ io.Writer = (*OffsetWriteSeeker)(nil) + _ io.WriteSeeker = (*OffsetWriteSeeker)(nil) +) + +type OffsetWriteSeeker struct { + w io.WriterAt + base int64 + offset int64 +} + +func NewOffsetWriter(w io.WriterAt, off int64) *OffsetWriteSeeker { + return &OffsetWriteSeeker{w, off, off} +} + +func (ow *OffsetWriteSeeker) Write(b []byte) (n int, err error) { + n, err = ow.w.WriteAt(b, ow.offset) + ow.offset += int64(n) + return +} + +func (ow *OffsetWriteSeeker) Seek(offset int64, whence int) (int64, error) { + switch whence { + case io.SeekStart: + ow.offset = offset + ow.base + case io.SeekCurrent: + ow.offset += offset + case io.SeekEnd: + panic("unsupported whence: SeekEnd") + } + return ow.Position(), nil +} + +// Position returns the current position of this writer relative to the initial offset, i.e. the number of bytes written. +func (ow *OffsetWriteSeeker) Position() int64 { + return ow.offset - ow.base +} diff --git a/ipld/car/v2/internal/io/offset_writer.go b/ipld/car/v2/internal/io/offset_writer.go deleted file mode 100644 index 1dd810165..000000000 --- a/ipld/car/v2/internal/io/offset_writer.go +++ /dev/null @@ -1,26 +0,0 @@ -package io - -import "io" - -var _ io.Writer = (*OffsetWriter)(nil) - -type OffsetWriter struct { - w io.WriterAt - base int64 - offset int64 -} - -func NewOffsetWriter(w io.WriterAt, off int64) *OffsetWriter { - return &OffsetWriter{w, off, off} -} - -func (ow *OffsetWriter) Write(b []byte) (n int, err error) { - n, err = ow.w.WriteAt(b, ow.offset) - ow.offset += int64(n) - return -} - -// Position returns the current position of this writer relative to the initial offset, i.e. the number of bytes written. -func (ow *OffsetWriter) Position() int64 { - return ow.offset - ow.base -} diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go index c172315cd..a7dd6c0e7 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/reader_test.go @@ -1,11 +1,12 @@ package car import ( + "os" + "testing" + "github.com/ipld/go-car/v2/index" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "os" - "testing" ) func TestReadOrGenerateIndex(t *testing.T) { @@ -52,7 +53,6 @@ func TestReadOrGenerateIndex(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - carFile, err := os.Open(tt.carPath) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, carFile.Close()) }) From e71a1c90584d0521e5d65b3551ab01cd80d8b695 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Fri, 9 Jul 2021 12:18:45 +0100 Subject: [PATCH 3472/3817] Avoid creating files on resumption in`ReadWrite` blockstore When resumption is enabled, we do not want to create a file if it does not exist. Similarly, when resumption is disabled, fail if the given file at path already exists to avoid unintended file overrides and creations. This commit was moved from ipld/go-car@8c08fbfef55c8f1463bbd4eb3004011bc076a23a --- ipld/car/v2/blockstore/readwrite.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 286b55e88..844d04f89 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -114,9 +114,9 @@ func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, err opt(b) } - fFlag := os.O_RDWR | os.O_CREATE + fFlag := os.O_RDWR if !b.resume { - fFlag = fFlag | os.O_EXCL + fFlag = fFlag | os.O_CREATE | os.O_EXCL } var err error b.f, err = os.OpenFile(path, fFlag, 0o666) // TODO: Should the user be able to configure FileMode permissions? From ccb72111954bf90444083030f7fefe71b9ae6529 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Fri, 9 Jul 2021 17:58:30 +0100 Subject: [PATCH 3473/3817] Remove resumption option for automatic resumption when file exists When file exists, attempt resumption by default. This seems like a more user-friendly API and less requirements to explicitly tune knobs. Explicitly fail of the file is finalized. We technically can append to a finalized file but that involves changes in multiple places in reader abstractions. Left a TODO as a feature for future when asked for. Close off file if construction of ReadWrite blockstore fails after file is opened. If left open, it causes test failures when t cleans up temp files. This commit was moved from ipld/go-car@eca650ea663fe729f435295e23043828e4a4bb6e --- ipld/car/v2/blockstore/doc.go | 5 +- ipld/car/v2/blockstore/readwrite.go | 125 ++++++++++++++--------- ipld/car/v2/blockstore/readwrite_test.go | 14 ++- 3 files changed, 89 insertions(+), 55 deletions(-) diff --git a/ipld/car/v2/blockstore/doc.go b/ipld/car/v2/blockstore/doc.go index e31c04329..c41500d78 100644 --- a/ipld/car/v2/blockstore/doc.go +++ b/ipld/car/v2/blockstore/doc.go @@ -13,7 +13,6 @@ // Upon finalization, the instance can no longer be used for reading or writing blocks and will // panic if used. To continue reading the blocks users are encouraged to use ReadOnly blockstore // instantiated from the same file path using OpenReadOnly. -// A user may resume reading/writing from files produced by an instance of ReadWrite blockstore that -// on which ReadWrite.Finalize was never called. To resume, WithResumption option must be set to -// true. See docs on WithResumption for usage criteria. +// A user may resume reading/writing from files produced by an instance of ReadWrite blockstore. The +// resumption is attempted automatically, if the path passed to NewReadWrite exists. package blockstore diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 844d04f89..fda3223bf 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -41,7 +41,6 @@ type ReadWrite struct { header carv2.Header dedupCids bool - resume bool } // TODO consider exposing interfaces @@ -70,74 +69,87 @@ func WithCidDeduplication(b *ReadWrite) { // TODO should this take a bool and re b.dedupCids = true } -// WithResumption sets whether the blockstore should resume on a file produced by a ReadWrite -// blockstore on which ReadWrite.Finalize was not called. +// NewReadWrite creates a new ReadWrite at the given path with a provided set of root CIDs and options. +// +// ReadWrite.Finalize must be called once putting and reading blocks are no longer needed. +// Upon calling ReadWrite.Finalize the CAR v2 header and index are written out onto the file and the +// backing file is closed. Once finalized, all read and write calls to this blockstore will result +// in panics. Note, a finalized file cannot be resumed from. // -// This option is set to false by default, i.e. disabled. When disabled, the file at path must not -// exist, otherwise an error is returned upon ReadWrite blockstore construction. +// If a file at given path does not exist, the instantiation will write car.Pragma and data payload +// header (i.e. the inner CAR v1 header) onto the file before returning. // -// When this option is set to true the existing data frames in file are re-indexed, allowing the -// caller to continue putting any remaining blocks without having to re-ingest blocks for which -// previous ReadWrite.Put returned successfully. +// When the given path already exists, the blockstore will attempt to resume from it. +// On resumption the existing data frames in file are re-indexed, allowing the caller to continue +// putting any remaining blocks without having to re-ingest blocks for which previous ReadWrite.Put +// returned successfully. // -// Resumption is only allowed on files that satisfy the following criteria: +// Resumption only works on files that were created by a previous instance of a ReadWrite +// blockstore. This means a file created as a result of a successful call to NewReadWrite can be +// resumed from as long as write operations such as ReadWrite.Put, and ReadWrite.PutMany returned +// successfully and ReadWrite. On resumption the roots argument and WithCarV1Padding option must match the +// previous instantiation of ReadWrite blockstore that created the file. +// More explicitly, the file resuming from must: // 1. start with a complete CAR v2 car.Pragma. // 2. contain a complete CAR v1 data header with root CIDs matching the CIDs passed to the -// constructor, starting at offset specified by WithCarV1Padding, followed by zero or more -// complete data frames. If any corrupt data frames are present the resumption will fail. Note, -// it is important that new instantiations of ReadWrite blockstore with resumption enabled use -// the same WithCarV1Padding option, since this option is used to locate the offset at which -// the data payload starts. -// 3. have not been produced by a ReadWrite blockstore that was finalized, i.e. call to -// ReadWrite.Finalize returned successfully. -func WithResumption(enabled bool) Option { - return func(b *ReadWrite) { - b.resume = enabled - } -} - -// NewReadWrite creates a new ReadWrite at the given path with a provided set of root CIDs and options. +// constructor, starting at offset optionally padded by WithCarV1Padding, followed by zero or +// more complete data frames. If any corrupt data frames are present the resumption will fail. +// Note, if set previously, the blockstore must use the same WithCarV1Padding option as before, +// since this option is used to locate the CAR v1 data payload. +// 3. ReadWrite.Finalize must not have been called on the file. func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, error) { - // TODO either lock the file or open exclusively; can we do somethign to reduce edge cases. - - b := &ReadWrite{ - header: carv2.NewHeader(0), + // Try and resume by default if the file exists. + resume := true + if _, err := os.Stat(path); err != nil { // TODO should we use stats to avoid resuming from files with zero size? + if os.IsNotExist(err) { + resume = false + } else { + return nil, err + } + } + f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0o666) // TODO: Should the user be able to configure FileMode permissions? + if err != nil { + return nil, fmt.Errorf("could not open read/write file: %w", err) } - indexcls, ok := index.BuildersByCodec[index.IndexInsertion] + // If construction of blockstore fails, make sure to close off the open file. + defer func() { + if err != nil { + f.Close() + } + }() + + idxBuilder, ok := index.BuildersByCodec[index.IndexInsertion] if !ok { return nil, fmt.Errorf("unknownindex codec: %#v", index.IndexInsertion) } - b.idx = (indexcls()).(*index.InsertionIndex) + // Instantiate block store. + // Set the header fileld before applying options since padding options may modify header. + rwbs := &ReadWrite{ + f: f, + idx: (idxBuilder()).(*index.InsertionIndex), + header: carv2.NewHeader(0), + } for _, opt := range opts { - opt(b) + opt(rwbs) } - fFlag := os.O_RDWR - if !b.resume { - fFlag = fFlag | os.O_CREATE | os.O_EXCL - } - var err error - b.f, err = os.OpenFile(path, fFlag, 0o666) // TODO: Should the user be able to configure FileMode permissions? - if err != nil { - return nil, fmt.Errorf("could not open read/write file: %w", err) - } - b.carV1Writer = internalio.NewOffsetWriter(b.f, int64(b.header.CarV1Offset)) - v1r := internalio.NewOffsetReadSeeker(b.f, int64(b.header.CarV1Offset)) - b.ReadOnly = ReadOnly{backing: v1r, idx: b.idx, carv2Closer: b.f} + rwbs.carV1Writer = internalio.NewOffsetWriter(rwbs.f, int64(rwbs.header.CarV1Offset)) + v1r := internalio.NewOffsetReadSeeker(rwbs.f, int64(rwbs.header.CarV1Offset)) + rwbs.ReadOnly = ReadOnly{backing: v1r, idx: rwbs.idx, carv2Closer: rwbs.f} - if b.resume { - if err = b.resumeWithRoots(roots); err != nil { + if resume { + if err = rwbs.resumeWithRoots(roots); err != nil { return nil, err } } else { - if err = b.initWithRoots(roots); err != nil { + if err = rwbs.initWithRoots(roots); err != nil { return nil, err } } - return b, nil + return rwbs, nil } func (b *ReadWrite) initWithRoots(roots []cid.Cid) error { @@ -162,6 +174,17 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { return fmt.Errorf("cannot resume on CAR file with version %v", version) } + // Check if file is finalized. + // A file is finalized when it contains a valid CAR v2 header with non-zero v1 offset. + // If finalized, cannot resume from. + var headerInFile carv2.Header + if _, err := headerInFile.ReadFrom(internalio.NewOffsetReadSeeker(b.f, carv2.PragmaSize)); err == nil { + // TODO: we technically can; this could be a feature to do if asked for. + if headerInFile.CarV1Offset != 0 { + return fmt.Errorf("cannot resume from a finalized file") + } + } + // Use the given CAR v1 padding to instantiate the CAR v1 reader on file. v1r := internalio.NewOffsetReadSeeker(b.ReadOnly.backing, 0) header, err := carv1.ReadHeader(bufio.NewReader(v1r)) @@ -277,15 +300,17 @@ func (b *ReadWrite) Finalize() error { // TODO see if folks need to continue reading from a finalized blockstore, if so return ReadOnly blockstore here. b.header = b.header.WithCarV1Size(uint64(b.carV1Writer.Position())) defer b.Close() - if _, err := b.header.WriteTo(internalio.NewOffsetWriter(b.f, carv2.PragmaSize)); err != nil { - return err - } + // TODO if index not needed don't bother flattening it. fi, err := b.idx.Flatten() if err != nil { return err } - return index.WriteTo(fi, internalio.NewOffsetWriter(b.f, int64(b.header.IndexOffset))) + if err := index.WriteTo(fi, internalio.NewOffsetWriter(b.f, int64(b.header.IndexOffset))); err != nil { + return err + } + _, err = b.header.WriteTo(internalio.NewOffsetWriter(b.f, carv2.PragmaSize)) + return err } func (b *ReadWrite) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 914d20be1..379c48ee3 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -306,7 +306,7 @@ func TestBlockstoreResumption(t *testing.T) { // Note, we don't have to close the file for resumption to work. // We do this to avoid resource leak during testing. require.NoError(t, subject.Close()) - subject, err = blockstore.NewReadWrite(path, r.Header.Roots, blockstore.WithResumption(true)) + subject, err = blockstore.NewReadWrite(path, r.Header.Roots) require.NoError(t, err) } require.NoError(t, subject.Put(b)) @@ -314,7 +314,7 @@ func TestBlockstoreResumption(t *testing.T) { require.NoError(t, subject.Close()) // Finalize the blockstore to complete partially written CAR v2 file. - subject, err = blockstore.NewReadWrite(path, r.Header.Roots, blockstore.WithResumption(true)) + subject, err = blockstore.NewReadWrite(path, r.Header.Roots) require.NoError(t, err) require.NoError(t, subject.Finalize()) @@ -357,3 +357,13 @@ func TestBlockstoreResumption(t *testing.T) { require.NoError(t, err) require.Equal(t, wantIdx, gotIdx) } + +func TestBlockstoreResumptionFailsOnFinalizedFile(t *testing.T) { + path := filepath.Join(t.TempDir(), "readwrite-resume-finalized.car") + // Create an incomplete CAR v2 file with no blocks put. + subject, err := blockstore.NewReadWrite(path, []cid.Cid{}) + require.NoError(t, err) + require.NoError(t, subject.Finalize()) + _, err = blockstore.NewReadWrite(path, []cid.Cid{}) + require.Errorf(t, err, "cannot resume from a finalized file") +} From 7633fa073ceb6feb62b0a8f99c9d8d061d5d91ef Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Fri, 9 Jul 2021 20:21:42 +0100 Subject: [PATCH 3474/3817] Fix bug in ReadOnly blockstore GetSize Fix a bug where the returned size in `ReadOnly.GetSize` blockstore was incorrect and the CID was being read from the wrong offset. Implement tests to assert returned size matches the block raw data size. Fixes #133 This commit was moved from ipld/go-car@7373fe29be3dbd8bf17300976a8ee72a31cb0e4c --- ipld/car/v2/blockstore/readonly.go | 10 +++--- ipld/car/v2/blockstore/readonly_test.go | 43 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 ipld/car/v2/blockstore/readonly_test.go diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 8ef74833e..ae3259ae3 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -189,19 +189,19 @@ func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { if err != nil { return -1, err } - l, err := varint.ReadUvarint(internalio.NewOffsetReadSeeker(b.backing, int64(idx))) + rdr := internalio.NewOffsetReadSeeker(b.backing, int64(idx)) + frameLen, err := varint.ReadUvarint(rdr) if err != nil { return -1, blockstore.ErrNotFound } - _, c, err := cid.CidFromReader(internalio.NewOffsetReadSeeker(b.backing, int64(idx+l))) + cidLen, readCid, err := cid.CidFromReader(rdr) if err != nil { return 0, err } - if !c.Equals(key) { + if !readCid.Equals(key) { return -1, blockstore.ErrNotFound } - // get cid. validate. - return int(l), err + return int(frameLen) - cidLen, err } // Put is not supported and always returns an error. diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go new file mode 100644 index 000000000..71803dd93 --- /dev/null +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -0,0 +1,43 @@ +package blockstore + +import ( + "io" + "os" + "testing" + + "github.com/ipld/go-car/v2/internal/carv1" + "github.com/stretchr/testify/require" +) + +func TestReadOnlyGetSize(t *testing.T) { + carv1Path := "testdata/test.car" + subject, err := OpenReadOnly(carv1Path) + t.Cleanup(func() { subject.Close() }) + require.NoError(t, err) + + f, err := os.Open(carv1Path) + require.NoError(t, err) + t.Cleanup(func() { f.Close() }) + v1r, err := carv1.NewCarReader(f) + require.NoError(t, err) + for { + wantBlock, err := v1r.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + + key := wantBlock.Cid() + + // Assert returned size matches the block.RawData length. + getSize, err := subject.GetSize(key) + wantSize := len(wantBlock.RawData()) + require.NoError(t, err) + require.Equal(t, wantSize, getSize) + + // While at it test blocks are as expected. + gotBlock, err := subject.Get(key) + require.NoError(t, err) + require.Equal(t, wantBlock, gotBlock) + } +} From aec7ae84171372ca05cd3c74d2e01807d6cc9194 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Fri, 9 Jul 2021 21:18:04 +0100 Subject: [PATCH 3475/3817] Implement more tests for `ReadOnly` blockstore Implement tests that check `ReadOnly` blockstore functions work based on data payload originated from a V1 or V2 formatted CAR file. Implement tests that assert instantiation of `ReadOnly` blockstore fails on unexpected pragma version. This commit was moved from ipld/go-car@ba7ed7c9668c23591b54fd9672fe74f0200d6811 --- ipld/car/v2/blockstore/readonly_test.go | 132 +++++++++++++++++++----- 1 file changed, 108 insertions(+), 24 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 71803dd93..cd70844e9 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -1,43 +1,127 @@ package blockstore import ( + "context" "io" "os" "testing" + "time" + + "github.com/ipfs/go-cid" + carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/internal/carv1" "github.com/stretchr/testify/require" ) -func TestReadOnlyGetSize(t *testing.T) { - carv1Path := "testdata/test.car" - subject, err := OpenReadOnly(carv1Path) - t.Cleanup(func() { subject.Close() }) +func TestReadOnly(t *testing.T) { + tests := []struct { + name string + v1OrV2path string + v1r *carv1.CarReader + }{ + { + "OpenedWithCarV1", + "testdata/test.car", + newReaderFromV1File(t, "testdata/test.car"), + }, + { + "OpenedWithAnotherCarV1", + "../testdata/sample-v1.car", + newReaderFromV1File(t, "../testdata/sample-v1.car"), + }, + { + "OpenedWithCarV2", + "../testdata/sample-wrapped-v2.car", + newReaderFromV2File(t, "../testdata/sample-wrapped-v2.car"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + subject, err := OpenReadOnly(tt.v1OrV2path) + t.Cleanup(func() { subject.Close() }) + require.NoError(t, err) + + // Assert roots match v1 payload. + wantRoots := tt.v1r.Header.Roots + gotRoots, err := subject.Roots() + require.NoError(t, err) + require.Equal(t, wantRoots, gotRoots) + + var wantCids []cid.Cid + for { + wantBlock, err := tt.v1r.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + + key := wantBlock.Cid() + wantCids = append(wantCids, key) + + // Assert blockstore contains key. + has, err := subject.Has(key) + require.NoError(t, err) + require.True(t, has) + + // Assert size matches block raw data length. + gotSize, err := subject.GetSize(key) + wantSize := len(wantBlock.RawData()) + require.NoError(t, err) + require.Equal(t, wantSize, gotSize) + + // Assert block itself matches v1 payload block. + gotBlock, err := subject.Get(key) + require.NoError(t, err) + require.Equal(t, wantBlock, gotBlock) + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) + defer cancel() + + // Assert all cids in blockstore match v1 payload CIDs. + allKeysChan, err := subject.AllKeysChan(ctx) + require.NoError(t, err) + var gotCids []cid.Cid + for gotKey := range allKeysChan { + gotCids = append(gotCids, gotKey) + } + require.Equal(t, wantCids, gotCids) + }) + } +} + +func TestOpenReadOnlyFailsOnUnknownVersion(t *testing.T) { + subject, err := OpenReadOnly("../testdata/sample-rootless-v42.car") + require.Errorf(t, err, "unsupported car version: 42") + require.Nil(t, subject) +} + +func TestNewReadOnlyFailsOnUnknownVersion(t *testing.T) { + f, err := os.Open("../testdata/sample-rootless-v42.car") require.NoError(t, err) + t.Cleanup(func() { f.Close() }) + subject, err := NewReadOnly(f, nil) + require.Errorf(t, err, "unsupported car version: 42") + require.Nil(t, subject) +} +func newReaderFromV1File(t *testing.T, carv1Path string) *carv1.CarReader { f, err := os.Open(carv1Path) require.NoError(t, err) t.Cleanup(func() { f.Close() }) v1r, err := carv1.NewCarReader(f) require.NoError(t, err) - for { - wantBlock, err := v1r.Next() - if err == io.EOF { - break - } - require.NoError(t, err) - - key := wantBlock.Cid() - - // Assert returned size matches the block.RawData length. - getSize, err := subject.GetSize(key) - wantSize := len(wantBlock.RawData()) - require.NoError(t, err) - require.Equal(t, wantSize, getSize) - - // While at it test blocks are as expected. - gotBlock, err := subject.Get(key) - require.NoError(t, err) - require.Equal(t, wantBlock, gotBlock) - } + return v1r +} + +func newReaderFromV2File(t *testing.T, carv2Path string) *carv1.CarReader { + f, err := os.Open(carv2Path) + require.NoError(t, err) + t.Cleanup(func() { f.Close() }) + v2r, err := carv2.NewReader(f) + require.NoError(t, err) + v1r, err := carv1.NewCarReader(v2r.CarV1Reader()) + require.NoError(t, err) + return v1r } From 054d96d43470e68ba718995fe3e80afed1c15186 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Mon, 12 Jul 2021 12:45:13 +0100 Subject: [PATCH 3476/3817] Assert write operations on ReadOnly blockstore panic Update typo in docs while at it. This commit was moved from ipld/go-car@224cd8fd875cc5ecc8941cf4af37b3eae0661dd3 --- ipld/car/v2/blockstore/readonly_test.go | 13 +++++++------ ipld/car/v2/blockstore/readwrite.go | 8 ++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index cd70844e9..499748172 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -7,6 +7,8 @@ import ( "testing" "time" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" carv2 "github.com/ipld/go-car/v2" @@ -74,6 +76,11 @@ func TestReadOnly(t *testing.T) { gotBlock, err := subject.Get(key) require.NoError(t, err) require.Equal(t, wantBlock, gotBlock) + + // Assert write operations panic + require.Panics(t, func() { subject.Put(wantBlock) }) + require.Panics(t, func() { subject.PutMany([]blocks.Block{wantBlock}) }) + require.Panics(t, func() { subject.DeleteBlock(key) }) } ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) @@ -91,12 +98,6 @@ func TestReadOnly(t *testing.T) { } } -func TestOpenReadOnlyFailsOnUnknownVersion(t *testing.T) { - subject, err := OpenReadOnly("../testdata/sample-rootless-v42.car") - require.Errorf(t, err, "unsupported car version: 42") - require.Nil(t, subject) -} - func TestNewReadOnlyFailsOnUnknownVersion(t *testing.T) { f, err := os.Open("../testdata/sample-rootless-v42.car") require.NoError(t, err) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index fda3223bf..9cfd63811 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -86,10 +86,10 @@ func WithCidDeduplication(b *ReadWrite) { // TODO should this take a bool and re // // Resumption only works on files that were created by a previous instance of a ReadWrite // blockstore. This means a file created as a result of a successful call to NewReadWrite can be -// resumed from as long as write operations such as ReadWrite.Put, and ReadWrite.PutMany returned -// successfully and ReadWrite. On resumption the roots argument and WithCarV1Padding option must match the -// previous instantiation of ReadWrite blockstore that created the file. -// More explicitly, the file resuming from must: +// resumed from as long as write operations such as ReadWrite.Put, ReadWrite.PutMany returned +// successfully and ReadWrite.Finalize was never called. On resumption the roots argument and +// WithCarV1Padding option must match the previous instantiation of ReadWrite blockstore that +// created the file. More explicitly, the file resuming from must: // 1. start with a complete CAR v2 car.Pragma. // 2. contain a complete CAR v1 data header with root CIDs matching the CIDs passed to the // constructor, starting at offset optionally padded by WithCarV1Padding, followed by zero or From 8bec630c1ddbbd522060c74f890433572f0ef3d4 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 13 Jul 2021 12:22:33 +0100 Subject: [PATCH 3477/3817] Unexport unecesserry index APIs and integrate with mulitcodec Use the codec dedicated to CAR index sorted when marshalling and unmarshalling `indexSorted`. Note, the code depends on a specific commit of `go-multicodec` `master` branch. This needs to be replaced with a tag once a release is made on the go-multicodec side later. Unexport index APIs that should not be exposed publicly. Remove `Builder` now that it is not needed anywhere. Move `insertionIndex` into `blockstore` package since that's the only place it is used. Introduce an index constructor that takes multicodec code and instantiates an index. Fix ignored errors in `indexsorted.go` during marshalling/unmarshlling. Rename index constructor functions to use consistent terminology; i.e. `new` instead if `mk`. Remove redundant TODOs in code. Relates to: - https://github.com/multiformats/go-multicodec/pull/46 Address review comments * Rename constructor of index by codec to a simpler name and update docs. * Use multicodec.Code as constant instead of wrappint unit64 every time. This commit was moved from ipld/go-car@2b593c11b4c706d52fc920b92052bcbdd411f687 --- .../{index => blockstore}/insertionindex.go | 86 +++++++++++-------- ipld/car/v2/blockstore/readwrite.go | 17 ++-- ipld/car/v2/index/errors.go | 8 +- ipld/car/v2/index/generator.go | 3 +- ipld/car/v2/index/index.go | 48 +++++------ ipld/car/v2/index/indexgobhash.go | 10 ++- ipld/car/v2/index/indexhashed.go | 10 ++- ipld/car/v2/index/indexsorted.go | 23 +++-- 8 files changed, 107 insertions(+), 98 deletions(-) rename ipld/car/v2/{index => blockstore}/insertionindex.go (58%) diff --git a/ipld/car/v2/index/insertionindex.go b/ipld/car/v2/blockstore/insertionindex.go similarity index 58% rename from ipld/car/v2/index/insertionindex.go rename to ipld/car/v2/blockstore/insertionindex.go index 78dace1df..8e664eac9 100644 --- a/ipld/car/v2/index/insertionindex.go +++ b/ipld/car/v2/blockstore/insertionindex.go @@ -1,29 +1,36 @@ -package index +package blockstore import ( "bytes" "encoding/binary" + "errors" "fmt" "io" + "github.com/ipld/go-car/v2/index" + "github.com/multiformats/go-multicodec" + "github.com/ipfs/go-cid" "github.com/multiformats/go-multihash" "github.com/petar/GoLLRB/llrb" cbor "github.com/whyrusleeping/cbor/go" ) -type InsertionIndex struct { - items llrb.LLRB -} +var ( + errUnsupported = errors.New("not supported") + insertionIndexCodec = multicodec.Code(0x300003) +) -func (ii *InsertionIndex) InsertNoReplace(key cid.Cid, n uint64) { - ii.items.InsertNoReplace(mkRecordFromCid(key, n)) -} +type ( + insertionIndex struct { + items llrb.LLRB + } -type recordDigest struct { - digest []byte - Record -} + recordDigest struct { + digest []byte + index.Record + } +) func (r recordDigest) Less(than llrb.Item) bool { other, ok := than.(recordDigest) @@ -33,7 +40,7 @@ func (r recordDigest) Less(than llrb.Item) bool { return bytes.Compare(r.digest, other.digest) < 0 } -func mkRecord(r Record) recordDigest { +func newRecordDigest(r index.Record) recordDigest { d, err := multihash.Decode(r.Hash()) if err != nil { panic(err) @@ -42,16 +49,20 @@ func mkRecord(r Record) recordDigest { return recordDigest{d.Digest, r} } -func mkRecordFromCid(c cid.Cid, at uint64) recordDigest { +func newRecordFromCid(c cid.Cid, at uint64) recordDigest { d, err := multihash.Decode(c.Hash()) if err != nil { panic(err) } - return recordDigest{d.Digest, Record{Cid: c, Idx: at}} + return recordDigest{d.Digest, index.Record{Cid: c, Idx: at}} } -func (ii *InsertionIndex) Get(c cid.Cid) (uint64, error) { +func (ii *insertionIndex) insertNoReplace(key cid.Cid, n uint64) { + ii.items.InsertNoReplace(newRecordFromCid(key, n)) +} + +func (ii *insertionIndex) Get(c cid.Cid) (uint64, error) { d, err := multihash.Decode(c.Hash()) if err != nil { return 0, err @@ -59,7 +70,7 @@ func (ii *InsertionIndex) Get(c cid.Cid) (uint64, error) { entry := recordDigest{digest: d.Digest} e := ii.items.Get(entry) if e == nil { - return 0, ErrNotFound + return 0, index.ErrNotFound } r, ok := e.(recordDigest) if !ok { @@ -69,13 +80,11 @@ func (ii *InsertionIndex) Get(c cid.Cid) (uint64, error) { return r.Record.Idx, nil } -func (ii *InsertionIndex) Marshal(w io.Writer) error { +func (ii *insertionIndex) Marshal(w io.Writer) error { if err := binary.Write(w, binary.LittleEndian, int64(ii.items.Len())); err != nil { return err } - var err error - iter := func(i llrb.Item) bool { if err = cbor.Encode(w, i.(recordDigest).Record); err != nil { return false @@ -86,30 +95,29 @@ func (ii *InsertionIndex) Marshal(w io.Writer) error { return err } -func (ii *InsertionIndex) Unmarshal(r io.Reader) error { - var len int64 - if err := binary.Read(r, binary.LittleEndian, &len); err != nil { +func (ii *insertionIndex) Unmarshal(r io.Reader) error { + var length int64 + if err := binary.Read(r, binary.LittleEndian, &length); err != nil { return err } d := cbor.NewDecoder(r) - for i := int64(0); i < len; i++ { - var rec Record + for i := int64(0); i < length; i++ { + var rec index.Record if err := d.Decode(&rec); err != nil { return err } - ii.items.InsertNoReplace(mkRecord(rec)) + ii.items.InsertNoReplace(newRecordDigest(rec)) } return nil } -// Codec identifies this index format -func (ii *InsertionIndex) Codec() Codec { - return IndexInsertion +func (ii *insertionIndex) Codec() multicodec.Code { + return insertionIndexCodec } -func (ii *InsertionIndex) Load(rs []Record) error { +func (ii *insertionIndex) Load(rs []index.Record) error { for _, r := range rs { - rec := mkRecord(r) + rec := newRecordDigest(r) if rec.digest == nil { return fmt.Errorf("invalid entry: %v", r) } @@ -118,15 +126,17 @@ func (ii *InsertionIndex) Load(rs []Record) error { return nil } -func mkInsertion() Index { - ii := InsertionIndex{} - return &ii +func newInsertionIndex() *insertionIndex { + return &insertionIndex{} } -// Flatten returns a 'indexsorted' formatted index for more efficient subsequent loading -func (ii *InsertionIndex) Flatten() (Index, error) { - si := BuildersByCodec[IndexSorted]() - rcrds := make([]Record, ii.items.Len()) +// flatten returns a 'indexsorted' formatted index for more efficient subsequent loading +func (ii *insertionIndex) flatten() (index.Index, error) { + si, err := index.New(multicodec.CarIndexSorted) + if err != nil { + return nil, err + } + rcrds := make([]index.Record, ii.items.Len()) idx := 0 iter := func(i llrb.Item) bool { @@ -142,7 +152,7 @@ func (ii *InsertionIndex) Flatten() (Index, error) { return si, nil } -func (ii *InsertionIndex) HasExactCID(c cid.Cid) bool { +func (ii *insertionIndex) hasExactCID(c cid.Cid) bool { d, err := multihash.Decode(c.Hash()) if err != nil { panic(err) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 9cfd63811..c4bb3e421 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -37,7 +37,7 @@ type ReadWrite struct { f *os.File carV1Writer *internalio.OffsetWriteSeeker ReadOnly - idx *index.InsertionIndex + idx *insertionIndex header carv2.Header dedupCids bool @@ -119,16 +119,11 @@ func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, err } }() - idxBuilder, ok := index.BuildersByCodec[index.IndexInsertion] - if !ok { - return nil, fmt.Errorf("unknownindex codec: %#v", index.IndexInsertion) - } - // Instantiate block store. // Set the header fileld before applying options since padding options may modify header. rwbs := &ReadWrite{ f: f, - idx: (idxBuilder()).(*index.InsertionIndex), + idx: newInsertionIndex(), header: carv2.NewHeader(0), } for _, opt := range opts { @@ -235,7 +230,7 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { if err != nil { return err } - b.idx.InsertNoReplace(c, uint64(frameOffset)) + b.idx.insertNoReplace(c, uint64(frameOffset)) // Seek to the next frame by skipping the block. // The frame length includes the CID, so subtract it. @@ -270,7 +265,7 @@ func (b *ReadWrite) PutMany(blks []blocks.Block) error { for _, bl := range blks { c := bl.Cid() - if b.dedupCids && b.idx.HasExactCID(c) { + if b.dedupCids && b.idx.hasExactCID(c) { continue } @@ -278,7 +273,7 @@ func (b *ReadWrite) PutMany(blks []blocks.Block) error { if err := util.LdWrite(b.carV1Writer, c.Bytes(), bl.RawData()); err != nil { return err } - b.idx.InsertNoReplace(c, n) + b.idx.insertNoReplace(c, n) } return nil } @@ -302,7 +297,7 @@ func (b *ReadWrite) Finalize() error { defer b.Close() // TODO if index not needed don't bother flattening it. - fi, err := b.idx.Flatten() + fi, err := b.idx.flatten() if err != nil { return err } diff --git a/ipld/car/v2/index/errors.go b/ipld/car/v2/index/errors.go index fba7afba9..1a10a9846 100644 --- a/ipld/car/v2/index/errors.go +++ b/ipld/car/v2/index/errors.go @@ -2,9 +2,5 @@ package index import "errors" -var ( - // ErrNotFound signals a record is not found in the index. - ErrNotFound = errors.New("not found") - // errUnsupported signals unsupported operation by an index. - errUnsupported = errors.New("not supported") -) +// ErrNotFound signals a record is not found in the index. +var ErrNotFound = errors.New("not found") diff --git a/ipld/car/v2/index/generator.go b/ipld/car/v2/index/generator.go index 1781f8dcf..f7c5075a3 100644 --- a/ipld/car/v2/index/generator.go +++ b/ipld/car/v2/index/generator.go @@ -30,7 +30,6 @@ func Generate(v1 io.ReadSeeker) (Index, error) { return nil, fmt.Errorf("error reading car header: %w", err) } - // TODO: Generate should likely just take an io.ReadSeeker. // TODO: ensure the input's header version is 1. offset, err := carv1.HeaderSize(header) @@ -38,7 +37,7 @@ func Generate(v1 io.ReadSeeker) (Index, error) { return nil, err } - idx := mkSorted() + idx := newSorted() records := make([]Record, 0) diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 53df02fbc..4c66ac137 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -7,6 +7,8 @@ import ( "io" "os" + "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-varint" internalio "github.com/ipld/go-car/v2/internal/io" @@ -16,22 +18,14 @@ import ( // Codec table is a first var-int in CAR indexes const ( - IndexSorted Codec = 0x0400 // as per https://github.com/multiformats/multicodec/pull/220 - - // TODO: unexport these before the final release, probably - IndexHashed Codec = 0x300000 + iota - IndexSingleSorted - IndexGobHashed - IndexInsertion + indexHashed codec = 0x300000 + iota + indexSingleSorted + indexGobHashed ) type ( - // Codec is used as a multicodec identifier for CAR index files - // TODO: use go-multicodec before the final release - Codec int - - // Builder is a constructor for an index type - Builder func() Index + // codec is used as a multicodec identifier for CAR index files + codec int // Record is a pre-processed record of a car item and location. Record struct { @@ -41,7 +35,7 @@ type ( // Index provides an interface for looking up byte offset of a given CID. Index interface { - Codec() Codec + Codec() multicodec.Code Marshal(w io.Writer) error Unmarshal(r io.Reader) error Get(cid.Cid) (uint64, error) @@ -49,14 +43,14 @@ type ( } ) -// BuildersByCodec holds known index formats -// TODO: turn this into a func before the final release? -var BuildersByCodec = map[Codec]Builder{ - IndexHashed: mkHashed, - IndexSorted: mkSorted, - IndexSingleSorted: mkSingleSorted, - IndexGobHashed: mkGobHashed, - IndexInsertion: mkInsertion, +// New constructs a new index corresponding to the given CAR index codec. +func New(codec multicodec.Code) (Index, error) { + switch codec { + case multicodec.CarIndexSorted: + return newSorted(), nil + default: + return nil, fmt.Errorf("unknwon index codec: %v", codec) + } } // Save writes a generated index into the given `path`. @@ -97,15 +91,15 @@ func WriteTo(idx Index, w io.Writer) error { // Returns error if the encoding is not known. func ReadFrom(r io.Reader) (Index, error) { reader := bufio.NewReader(r) - codec, err := varint.ReadUvarint(reader) + code, err := varint.ReadUvarint(reader) if err != nil { return nil, err } - builder, ok := BuildersByCodec[Codec(codec)] - if !ok { - return nil, fmt.Errorf("unknown codec: %d", codec) + codec := multicodec.Code(code) + idx, err := New(codec) + if err != nil { + return nil, err } - idx := builder() if err := idx.Unmarshal(reader); err != nil { return nil, err } diff --git a/ipld/car/v2/index/indexgobhash.go b/ipld/car/v2/index/indexgobhash.go index 9e61001fa..a74e8b92b 100644 --- a/ipld/car/v2/index/indexgobhash.go +++ b/ipld/car/v2/index/indexgobhash.go @@ -4,9 +4,12 @@ import ( "encoding/gob" "io" + "github.com/multiformats/go-multicodec" + "github.com/ipfs/go-cid" ) +//lint:ignore U1000 kept for potential future use. type mapGobIndex map[cid.Cid]uint64 func (m *mapGobIndex) Get(c cid.Cid) (uint64, error) { @@ -27,8 +30,8 @@ func (m *mapGobIndex) Unmarshal(r io.Reader) error { return d.Decode(m) } -func (m *mapGobIndex) Codec() Codec { - return IndexHashed +func (m *mapGobIndex) Codec() multicodec.Code { + return multicodec.Code(indexHashed) } func (m *mapGobIndex) Load(rs []Record) error { @@ -38,7 +41,8 @@ func (m *mapGobIndex) Load(rs []Record) error { return nil } -func mkGobHashed() Index { +//lint:ignore U1000 kept for potential future use. +func newGobHashed() Index { mi := make(mapGobIndex) return &mi } diff --git a/ipld/car/v2/index/indexhashed.go b/ipld/car/v2/index/indexhashed.go index b24a9014a..84b0ad157 100644 --- a/ipld/car/v2/index/indexhashed.go +++ b/ipld/car/v2/index/indexhashed.go @@ -3,10 +3,13 @@ package index import ( "io" + "github.com/multiformats/go-multicodec" + "github.com/ipfs/go-cid" cbor "github.com/whyrusleeping/cbor/go" ) +//lint:ignore U1000 kept for potential future use. type mapIndex map[cid.Cid]uint64 func (m *mapIndex) Get(c cid.Cid) (uint64, error) { @@ -26,8 +29,8 @@ func (m *mapIndex) Unmarshal(r io.Reader) error { return d.Decode(m) } -func (m *mapIndex) Codec() Codec { - return IndexHashed +func (m *mapIndex) Codec() multicodec.Code { + return multicodec.Code(indexHashed) } func (m *mapIndex) Load(rs []Record) error { @@ -37,7 +40,8 @@ func (m *mapIndex) Load(rs []Record) error { return nil } -func mkHashed() Index { +//lint:ignore U1000 kept for potential future use. +func newHashed() Index { mi := make(mapIndex) return &mi } diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index b7d275103..a8f401aef 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -7,6 +7,8 @@ import ( "io" "sort" + "github.com/multiformats/go-multicodec" + "github.com/ipfs/go-cid" "github.com/multiformats/go-multihash" ) @@ -42,8 +44,8 @@ func (r recordSet) Swap(i, j int) { r[i], r[j] = r[j], r[i] } -func (s *singleWidthIndex) Codec() Codec { - return IndexSingleSorted +func (s *singleWidthIndex) Codec() multicodec.Code { + return multicodec.Code(indexSingleSorted) } func (s *singleWidthIndex) Marshal(w io.Writer) error { @@ -124,12 +126,14 @@ func (m *multiWidthIndex) Get(c cid.Cid) (uint64, error) { return 0, ErrNotFound } -func (m *multiWidthIndex) Codec() Codec { - return IndexSorted +func (m *multiWidthIndex) Codec() multicodec.Code { + return multicodec.CarIndexSorted } func (m *multiWidthIndex) Marshal(w io.Writer) error { - binary.Write(w, binary.LittleEndian, int32(len(*m))) + if err := binary.Write(w, binary.LittleEndian, int32(len(*m))); err != nil { + return err + } // The widths are unique, but ranging over a map isn't deterministic. // As per the CARv2 spec, we must order buckets by digest length. @@ -153,7 +157,9 @@ func (m *multiWidthIndex) Marshal(w io.Writer) error { func (m *multiWidthIndex) Unmarshal(r io.Reader) error { var l int32 - binary.Read(r, binary.LittleEndian, &l) + if err := binary.Read(r, binary.LittleEndian, &l); err != nil { + return err + } for i := 0; i < int(l); i++ { s := singleWidthIndex{} if err := s.Unmarshal(r); err != nil { @@ -199,12 +205,13 @@ func (m *multiWidthIndex) Load(items []Record) error { return nil } -func mkSorted() Index { +func newSorted() Index { m := make(multiWidthIndex) return &m } -func mkSingleSorted() Index { +//lint:ignore U1000 kept for potential future use. +func newSingleSorted() Index { s := singleWidthIndex{} return &s } From 3945e7e4fba67b26ab22d53de9d2db54a353a628 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 13 Jul 2021 12:34:35 +0100 Subject: [PATCH 3478/3817] Remove redundant seek before read or generate index Assume the read seeker passed in is seeked to the beginning of CAR payload. This is both for consistency of API and avoid unnecessary seeks when the reader may already be at the right place. Address review comments * Use the file already open to get the stats for resumption purposes. On getting the stats, because the open flag contains `os.O_CREATE` check the size of the file as a way to determine wheter we should attempt resumption or not. In practice this is the same as the code that explicitly differentiates from non-existing files, since file existence doesn't matter here; the key thing is the file content. Plus this also avoids unnecessary errors where the files exists but is empty. * Add TODOs in places to consider enabling deduplication by default and optimise computational complexity of roots check. Note, explicitly enable deduplication by default in a separate PR so that the change is clearly communicated to dependant clients in case it causes any unintended side-effects. * Rename `Equals` to `Matches` to avoid confusion about what it does. Relates to: - https://github.com/ipld/go-car/pull/147#discussion_r668587909 - https://github.com/ipld/go-car/pull/147#discussion_r668603050 - https://github.com/ipld/go-car/pull/147#discussion_r668609927 This commit was moved from ipld/go-car@cb2b58de6580d25adb22a43c41d301e1e6912a95 --- ipld/car/v2/blockstore/readwrite.go | 23 ++++++++++++----------- ipld/car/v2/internal/carv1/car.go | 9 ++++++--- ipld/car/v2/internal/carv1/car_test.go | 22 +++++++++++----------- ipld/car/v2/reader.go | 4 ---- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index c4bb3e421..3f77af18b 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -97,21 +97,22 @@ func WithCidDeduplication(b *ReadWrite) { // TODO should this take a bool and re // Note, if set previously, the blockstore must use the same WithCarV1Padding option as before, // since this option is used to locate the CAR v1 data payload. // 3. ReadWrite.Finalize must not have been called on the file. +// +// Note, resumption should be used with WithCidDeduplication, so that blocks that are successfully +// written into the file are not re-written. Unless, the user explicitly wants duplicate blocks. func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, error) { - // Try and resume by default if the file exists. - resume := true - if _, err := os.Stat(path); err != nil { // TODO should we use stats to avoid resuming from files with zero size? - if os.IsNotExist(err) { - resume = false - } else { - return nil, err - } - } + // TODO: enable deduplication by default now that resumption is automatically attempted. f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0o666) // TODO: Should the user be able to configure FileMode permissions? if err != nil { return nil, fmt.Errorf("could not open read/write file: %w", err) } - + stat, err := f.Stat() + if err != nil { + // Note, we should not get a an os.ErrNotExist here because the flags used to open file includes os.O_CREATE + return nil, err + } + // Try and resume by default if the file size is non-zero. + resume := stat.Size() != 0 // If construction of blockstore fails, make sure to close off the open file. defer func() { if err != nil { @@ -187,7 +188,7 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { // Cannot read the CAR v1 header; the file is most likely corrupt. return fmt.Errorf("error reading car header: %w", err) } - if !header.Equals(carv1.CarHeader{Roots: roots, Version: 1}) { + if !header.Matches(carv1.CarHeader{Roots: roots, Version: 1}) { // Cannot resume if version and root does not match. return errors.New("cannot resume on file with mismatching data header") } diff --git a/ipld/car/v2/internal/carv1/car.go b/ipld/car/v2/internal/carv1/car.go index 7332ebadb..d940ce25a 100644 --- a/ipld/car/v2/internal/carv1/car.go +++ b/ipld/car/v2/internal/carv1/car.go @@ -210,11 +210,13 @@ func loadCarSlow(s Store, cr *CarReader) (*CarHeader, error) { } } -// Equals checks whether two headers are equal. -// Two headers are considered equal if: +// Matches checks whether two headers match. +// Two headers are considered matching if: // 1. They have the same version number, and // 2. They contain the same root CIDs in any order. -func (h CarHeader) Equals(other CarHeader) bool { +// Note, this function explicitly ignores the order of roots. +// If order of roots matter use reflect.DeepEqual instead. +func (h CarHeader) Matches(other CarHeader) bool { if h.Version != other.Version { return false } @@ -229,6 +231,7 @@ func (h CarHeader) Equals(other CarHeader) bool { } // Check other contains all roots. + // TODO: should this be optimised for cases where the number of roots are large since it has O(N^2) complexity? for _, r := range h.Roots { if !other.containsRoot(r) { return false diff --git a/ipld/car/v2/internal/carv1/car_test.go b/ipld/car/v2/internal/carv1/car_test.go index fb0b0db37..e5dd680c8 100644 --- a/ipld/car/v2/internal/carv1/car_test.go +++ b/ipld/car/v2/internal/carv1/car_test.go @@ -232,7 +232,7 @@ func TestBadHeaders(t *testing.T) { } } -func TestCarHeaderEquals(t *testing.T) { +func TestCarHeaderMatchess(t *testing.T) { oneCid := dag.NewRawNode([]byte("fish")).Cid() anotherCid := dag.NewRawNode([]byte("lobster")).Cid() tests := []struct { @@ -242,49 +242,49 @@ func TestCarHeaderEquals(t *testing.T) { want bool }{ { - "SameVersionNilRootsIsEqual", + "SameVersionNilRootsIsMatching", CarHeader{nil, 1}, CarHeader{nil, 1}, true, }, { - "SameVersionEmptyRootsIsEqual", + "SameVersionEmptyRootsIsMatching", CarHeader{[]cid.Cid{}, 1}, CarHeader{[]cid.Cid{}, 1}, true, }, { - "SameVersionNonEmptySameRootsIsEqual", + "SameVersionNonEmptySameRootsIsMatching", CarHeader{[]cid.Cid{oneCid}, 1}, CarHeader{[]cid.Cid{oneCid}, 1}, true, }, { - "SameVersionNonEmptySameRootsInDifferentOrderIsEqual", + "SameVersionNonEmptySameRootsInDifferentOrderIsMatching", CarHeader{[]cid.Cid{oneCid, anotherCid}, 1}, CarHeader{[]cid.Cid{anotherCid, oneCid}, 1}, true, }, { - "SameVersionDifferentRootsIsNotEqual", + "SameVersionDifferentRootsIsNotMatching", CarHeader{[]cid.Cid{oneCid}, 1}, CarHeader{[]cid.Cid{anotherCid}, 1}, false, }, { - "DifferentVersionDifferentRootsIsNotEqual", + "DifferentVersionDifferentRootsIsNotMatching", CarHeader{[]cid.Cid{oneCid}, 0}, CarHeader{[]cid.Cid{anotherCid}, 1}, false, }, { - "MismatchingVersionIsNotEqual", + "MismatchingVersionIsNotMatching", CarHeader{nil, 0}, CarHeader{nil, 1}, false, }, { - "ZeroValueHeadersAreEqual", + "ZeroValueHeadersAreMatching", CarHeader{}, CarHeader{}, true, @@ -292,8 +292,8 @@ func TestCarHeaderEquals(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := tt.one.Equals(tt.other) - require.Equal(t, tt.want, got, "Equals() = %v, want %v", got, tt.want) + got := tt.one.Matches(tt.other) + require.Equal(t, tt.want, got, "Matches() = %v, want %v", got, tt.want) }) } } diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 0a8aefac4..803e61aaf 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -149,10 +149,6 @@ func (rsa *readSeekerAt) ReadAt(p []byte, off int64) (n int, err error) { // Note, the returned index lives entirely in memory and will not depend on the // given reader to fulfill index lookup. func ReadOrGenerateIndex(rs io.ReadSeeker) (index.Index, error) { - // Seek to the beginning of the reader in order to read version. - if _, err := rs.Seek(0, io.SeekStart); err != nil { - return nil, err - } // Read version. version, err := ReadVersion(rs) if err != nil { From 742f13dcac2e13b4202f30983e4a50f9f71b7f99 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 13 Jul 2021 16:04:08 +0100 Subject: [PATCH 3479/3817] Move index generation functionality to root v2 package Move index generation functionality to root, because they read/write CAR files. This leaves the `index` package to only contain the index implementation itself. Move `index.Attach` to root while at it following the same logic. Add TODOs to the implementation of Attach to make it more robust. As is it _could_ result in corrupt v2 files if offset includes padding since v2 header is not updated in this function. Add tests to generate index and move existing tests to correspond to the go file containing generation functionality. This commit was moved from ipld/go-car@24b825365c40b2b44ad16d64a1d1b146f40f2b99 --- ipld/car/v2/blockstore/readonly.go | 4 +- ipld/car/v2/blockstore/readwrite.go | 2 +- ipld/car/v2/blockstore/readwrite_test.go | 2 +- ipld/car/v2/index/doc.go | 5 - ipld/car/v2/index/generator.go | 99 ----------- ipld/car/v2/index/index.go | 13 -- ipld/car/v2/index_gen.go | 162 ++++++++++++++++++ .../v2/{reader_test.go => index_gen_test.go} | 49 +++++- ipld/car/v2/internal/carbs/util/hydrate.go | 4 +- ipld/car/v2/reader.go | 58 ------- ipld/car/v2/writer.go | 24 ++- 11 files changed, 236 insertions(+), 186 deletions(-) delete mode 100644 ipld/car/v2/index/generator.go create mode 100644 ipld/car/v2/index_gen.go rename ipld/car/v2/{reader_test.go => index_gen_test.go} (58%) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index ae3259ae3..f7e1c646b 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -106,12 +106,12 @@ func generateIndex(at io.ReaderAt) (index.Index, error) { default: rs = internalio.NewOffsetReadSeeker(r, 0) } - return index.Generate(rs) + return carv2.GenerateIndex(rs) } // OpenReadOnly opens a read-only blockstore from a CAR file (either v1 or v2), generating an index if it does not exist. // Note, the generated index if the index does not exist is ephemeral and only stored in memory. -// See index.Generate and Index.Attach for persisting index onto a CAR file. +// See car.GenerateIndex and Index.Attach for persisting index onto a CAR file. func OpenReadOnly(path string) (*ReadOnly, error) { f, err := mmap.Open(path) if err != nil { diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 3f77af18b..52878ebbc 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -194,7 +194,7 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { } // TODO See how we can reduce duplicate code here. - // The code here comes from index.Generate. + // The code here comes from car.GenerateIndex. // Copied because we need to populate an insertindex, not a sorted index. // Producing a sorted index via generate, then converting it to insertindex is not possible. // Because Index interface does not expose internal records. diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 379c48ee3..97d1bf58b 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -353,7 +353,7 @@ func TestBlockstoreResumption(t *testing.T) { require.NoError(t, err) gotIdx, err := index.ReadFrom(v2r.IndexReader()) require.NoError(t, err) - wantIdx, err := index.Generate(v1f) + wantIdx, err := carv2.GenerateIndex(v1f) require.NoError(t, err) require.Equal(t, wantIdx, gotIdx) } diff --git a/ipld/car/v2/index/doc.go b/ipld/car/v2/index/doc.go index 25a38c34c..4c7beb1f8 100644 --- a/ipld/car/v2/index/doc.go +++ b/ipld/car/v2/index/doc.go @@ -4,9 +4,4 @@ // Index can be written or read using the following static functions: index.WriteTo and // index.ReadFrom. // -// This package also provides functionality to generate an index from a given CAR v1 file using: -// index.Generate and index.GenerateFromFile -// -// In addition to the above, it provides functionality to attach an index to an index-less CAR v2 -// using index.Attach package index diff --git a/ipld/car/v2/index/generator.go b/ipld/car/v2/index/generator.go deleted file mode 100644 index f7c5075a3..000000000 --- a/ipld/car/v2/index/generator.go +++ /dev/null @@ -1,99 +0,0 @@ -package index - -import ( - "bufio" - "fmt" - "io" - "os" - - "github.com/multiformats/go-varint" - - "github.com/ipfs/go-cid" - "github.com/ipld/go-car/v2/internal/carv1" -) - -type readSeekerPlusByte struct { - io.ReadSeeker -} - -func (r readSeekerPlusByte) ReadByte() (byte, error) { - var p [1]byte - _, err := io.ReadFull(r, p[:]) - return p[0], err -} - -// Generate generates index for a given car in v1 format. -// The index can be stored using index.Save into a file or serialized using index.WriteTo. -func Generate(v1 io.ReadSeeker) (Index, error) { - header, err := carv1.ReadHeader(bufio.NewReader(v1)) - if err != nil { - return nil, fmt.Errorf("error reading car header: %w", err) - } - - // TODO: ensure the input's header version is 1. - - offset, err := carv1.HeaderSize(header) - if err != nil { - return nil, err - } - - idx := newSorted() - - records := make([]Record, 0) - - // Seek to the first frame. - // Record the start of each frame, which we need for the index records. - frameOffset := int64(0) - if frameOffset, err = v1.Seek(int64(offset), io.SeekStart); err != nil { - return nil, err - } - - for { - // Grab the length of the frame. - // Note that ReadUvarint wants a ByteReader. - length, err := varint.ReadUvarint(readSeekerPlusByte{v1}) - if err != nil { - if err == io.EOF { - break - } - return nil, err - } - - // Null padding; treat zero-length frames as an EOF. - // They don't contain a CID nor block, so they're not useful. - // TODO: Amend the CARv1 spec to explicitly allow this. - if length == 0 { - break - } - - // Grab the CID. - n, c, err := cid.CidFromReader(v1) - if err != nil { - return nil, err - } - records = append(records, Record{c, uint64(frameOffset)}) - - // Seek to the next frame by skipping the block. - // The frame length includes the CID, so subtract it. - if frameOffset, err = v1.Seek(int64(length)-int64(n), io.SeekCurrent); err != nil { - return nil, err - } - } - - if err := idx.Load(records); err != nil { - return nil, err - } - - return idx, nil -} - -// GenerateFromFile walks a car v1 file at the give path and generates an index of cid->byte offset. -// The index can be stored using index.Save into a file or serialized using index.WriteTo. -func GenerateFromFile(path string) (Index, error) { - f, err := os.Open(path) - if err != nil { - return nil, err - } - defer f.Close() - return Generate(f) -} diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 4c66ac137..906be639f 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -11,8 +11,6 @@ import ( "github.com/multiformats/go-varint" - internalio "github.com/ipld/go-car/v2/internal/io" - "github.com/ipfs/go-cid" ) @@ -63,17 +61,6 @@ func Save(idx Index, path string) error { return WriteTo(idx, stream) } -// Attach attaches a given index to an existing car v2 file at given path and offset. -func Attach(path string, idx Index, offset uint64) error { - out, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o640) - if err != nil { - return err - } - defer out.Close() - indexWriter := internalio.NewOffsetWriter(out, int64(offset)) - return WriteTo(idx, indexWriter) -} - // WriteTo writes the given idx into w. // The written bytes include the index encoding. // This can then be read back using index.ReadFrom diff --git a/ipld/car/v2/index_gen.go b/ipld/car/v2/index_gen.go new file mode 100644 index 000000000..59110897d --- /dev/null +++ b/ipld/car/v2/index_gen.go @@ -0,0 +1,162 @@ +package car + +import ( + "bufio" + "fmt" + "io" + "os" + "sync" + + "github.com/ipld/go-car/v2/index" + "github.com/multiformats/go-multicodec" + + "github.com/multiformats/go-varint" + + "github.com/ipfs/go-cid" + "github.com/ipld/go-car/v2/internal/carv1" +) + +type readSeekerPlusByte struct { + io.ReadSeeker +} + +func (r readSeekerPlusByte) ReadByte() (byte, error) { + var p [1]byte + _, err := io.ReadFull(r, p[:]) + return p[0], err +} + +// GenerateIndex generates index for a given car in v1 format. +// The index can be stored using index.Save into a file or serialized using index.WriteTo. +func GenerateIndex(v1 io.ReadSeeker) (index.Index, error) { + header, err := carv1.ReadHeader(bufio.NewReader(v1)) + if err != nil { + return nil, fmt.Errorf("error reading car header: %w", err) + } + + if header.Version != 1 { + return nil, fmt.Errorf("expected version to be 1, got %v", header.Version) + } + + offset, err := carv1.HeaderSize(header) + if err != nil { + return nil, err + } + + idx, err := index.New(multicodec.CarIndexSorted) + if err != nil { + return nil, err + } + records := make([]index.Record, 0) + + // Seek to the first frame. + // Record the start of each frame, which we need for the index records. + frameOffset := int64(0) + if frameOffset, err = v1.Seek(int64(offset), io.SeekStart); err != nil { + return nil, err + } + + for { + // Grab the length of the frame. + // Note that ReadUvarint wants a ByteReader. + length, err := varint.ReadUvarint(readSeekerPlusByte{v1}) + if err != nil { + if err == io.EOF { + break + } + return nil, err + } + + // Null padding; treat zero-length frames as an EOF. + // They don't contain a CID nor block, so they're not useful. + // TODO: Amend the CARv1 spec to explicitly allow this. + if length == 0 { + break + } + + // Grab the CID. + n, c, err := cid.CidFromReader(v1) + if err != nil { + return nil, err + } + records = append(records, index.Record{Cid: c, Idx: uint64(frameOffset)}) + + // Seek to the next frame by skipping the block. + // The frame length includes the CID, so subtract it. + if frameOffset, err = v1.Seek(int64(length)-int64(n), io.SeekCurrent); err != nil { + return nil, err + } + } + + if err := idx.Load(records); err != nil { + return nil, err + } + + return idx, nil +} + +// GenerateIndexFromFile walks a car v1 file at the give path and generates an index of cid->byte offset. +// The index can be stored using index.Save into a file or serialized using index.WriteTo. +func GenerateIndexFromFile(path string) (index.Index, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + return GenerateIndex(f) +} + +var _ io.ReaderAt = (*readSeekerAt)(nil) + +type readSeekerAt struct { + rs io.ReadSeeker + mu sync.Mutex +} + +func (rsa *readSeekerAt) ReadAt(p []byte, off int64) (n int, err error) { + rsa.mu.Lock() + defer rsa.mu.Unlock() + if _, err := rsa.rs.Seek(off, io.SeekStart); err != nil { + return 0, err + } + return rsa.rs.Read(p) +} + +// ReadOrGenerateIndex accepts both CAR v1 and v2 format, and reads or generates an index for it. +// When the given reader is in CAR v1 format an index is always generated. +// For a payload in CAR v2 format, an index is only generated if Header.HasIndex returns false. +// An error is returned for all other formats, i.e. versions other than 1 or 2. +// +// Note, the returned index lives entirely in memory and will not depend on the +// given reader to fulfill index lookup. +func ReadOrGenerateIndex(rs io.ReadSeeker) (index.Index, error) { + // Read version. + version, err := ReadVersion(rs) + if err != nil { + return nil, err + } + // Seek to the begining, since reading the version changes the reader's offset. + if _, err := rs.Seek(0, io.SeekStart); err != nil { + return nil, err + } + + switch version { + case 1: + // Simply generate the index, since there can't be a pre-existing one. + return GenerateIndex(rs) + case 2: + // Read CAR v2 format + v2r, err := NewReader(&readSeekerAt{rs: rs}) + if err != nil { + return nil, err + } + // If index is present, then no need to generate; decode and return it. + if v2r.Header.HasIndex() { + return index.ReadFrom(v2r.IndexReader()) + } + // Otherwise, generate index from CAR v1 payload wrapped within CAR v2 format. + return GenerateIndex(v2r.CarV1Reader()) + default: + return nil, fmt.Errorf("unknown version %v", version) + } +} diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/index_gen_test.go similarity index 58% rename from ipld/car/v2/reader_test.go rename to ipld/car/v2/index_gen_test.go index a7dd6c0e7..133aaf925 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/index_gen_test.go @@ -23,7 +23,7 @@ func TestReadOrGenerateIndex(t *testing.T) { v1, err := os.Open("testdata/sample-v1.car") require.NoError(t, err) defer v1.Close() - want, err := index.Generate(v1) + want, err := GenerateIndex(v1) require.NoError(t, err) return want }, @@ -31,7 +31,7 @@ func TestReadOrGenerateIndex(t *testing.T) { }, { "CarV2WithIndexIsReturnedAsExpected", - "testdata/sample-v1.car", + "testdata/sample-wrapped-v2.car", func(t *testing.T) index.Index { v2, err := os.Open("testdata/sample-wrapped-v2.car") require.NoError(t, err) @@ -65,3 +65,48 @@ func TestReadOrGenerateIndex(t *testing.T) { }) } } + +func TestGenerateIndexFromFile(t *testing.T) { + tests := []struct { + name string + carPath string + wantIndexer func(t *testing.T) index.Index + wantErr bool + }{ + { + "CarV1IsIndexedAsExpected", + "testdata/sample-v1.car", + func(t *testing.T) index.Index { + v1, err := os.Open("testdata/sample-v1.car") + require.NoError(t, err) + defer v1.Close() + want, err := GenerateIndex(v1) + require.NoError(t, err) + return want + }, + false, + }, + { + "CarV2IsErrorSinceOnlyV1PayloadIsExpected", + "testdata/sample-wrapped-v2.car", + func(t *testing.T) index.Index { return nil }, + true, + }, + { + "CarOtherThanV1OrV2IsError", + "testdata/sample-rootless-v42.car", + func(t *testing.T) index.Index { return nil }, + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GenerateIndexFromFile(tt.carPath) + if tt.wantErr { + require.Error(t, err) + } + want := tt.wantIndexer(t) + require.Equal(t, want, got) + }) + } +} diff --git a/ipld/car/v2/internal/carbs/util/hydrate.go b/ipld/car/v2/internal/carbs/util/hydrate.go index 8e43556cb..96bb4c953 100644 --- a/ipld/car/v2/internal/carbs/util/hydrate.go +++ b/ipld/car/v2/internal/carbs/util/hydrate.go @@ -4,6 +4,8 @@ import ( "fmt" "os" + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/index" ) @@ -14,7 +16,7 @@ func main() { } db := os.Args[1] - idx, err := index.GenerateFromFile(db) + idx, err := carv2.GenerateIndexFromFile(db) if err != nil { fmt.Printf("Error generating index: %v\n", err) return diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 803e61aaf..6cf684c74 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -4,9 +4,6 @@ import ( "bufio" "fmt" "io" - "sync" - - "github.com/ipld/go-car/v2/index" internalio "github.com/ipld/go-car/v2/internal/io" @@ -124,58 +121,3 @@ func ReadVersion(r io.Reader) (version uint64, err error) { } return header.Version, nil } - -var _ io.ReaderAt = (*readSeekerAt)(nil) - -type readSeekerAt struct { - rs io.ReadSeeker - mu sync.Mutex -} - -func (rsa *readSeekerAt) ReadAt(p []byte, off int64) (n int, err error) { - rsa.mu.Lock() - defer rsa.mu.Unlock() - if _, err := rsa.rs.Seek(off, io.SeekStart); err != nil { - return 0, err - } - return rsa.rs.Read(p) -} - -// ReadOrGenerateIndex accepts both CAR v1 and v2 format, and reads or generates an index for it. -// When the given reader is in CAR v1 format an index is always generated. -// For a payload in CAR v2 format, an index is only generated if Header.HasIndex returns false. -// An error is returned for all other formats, i.e. versions other than 1 or 2. -// -// Note, the returned index lives entirely in memory and will not depend on the -// given reader to fulfill index lookup. -func ReadOrGenerateIndex(rs io.ReadSeeker) (index.Index, error) { - // Read version. - version, err := ReadVersion(rs) - if err != nil { - return nil, err - } - // Seek to the begining, since reading the version changes the reader's offset. - if _, err := rs.Seek(0, io.SeekStart); err != nil { - return nil, err - } - - switch version { - case 1: - // Simply generate the index, since there can't be a pre-existing one. - return index.Generate(rs) - case 2: - // Read CAR v2 format - v2r, err := NewReader(&readSeekerAt{rs: rs}) - if err != nil { - return nil, err - } - // If index is present, then no need to generate; decode and return it. - if v2r.Header.HasIndex() { - return index.ReadFrom(v2r.IndexReader()) - } - // Otherwise, generate index from CAR v1 payload wrapped within CAR v2 format. - return index.Generate(v2r.CarV1Reader()) - default: - return nil, fmt.Errorf("unknown version %v", version) - } -} diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index 59777c87f..29b04f30a 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -6,6 +6,8 @@ import ( "io" "os" + internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" "github.com/ipld/go-car/v2/index" @@ -71,7 +73,7 @@ func (w *Writer) WriteTo(writer io.Writer) (n int64, err error) { return } n += int64(PragmaSize) - // We read the entire car into memory because index.Generate takes a reader. + // We read the entire car into memory because GenerateIndex takes a reader. // TODO Future PRs will make this more efficient by exposing necessary interfaces in index pacakge so that // this can be done in an streaming manner. if err = carv1.WriteCar(w.ctx, w.NodeGetter, w.roots, w.encodedCarV1); err != nil { @@ -124,7 +126,7 @@ func (w *Writer) writeIndex(writer io.Writer, carV1 []byte) (int64, error) { // Consider refactoring index to make this process more efficient. // We should avoid reading the entire car into memory since it can be large. reader := bytes.NewReader(carV1) - idx, err := index.Generate(reader) + idx, err := GenerateIndex(reader) if err != nil { return 0, err } @@ -167,9 +169,9 @@ func WrapV1File(srcPath, dstPath string) error { // and does not use any padding before the innner CARv1 or index. func WrapV1(src io.ReadSeeker, dst io.Writer) error { // TODO: verify src is indeed a CARv1 to prevent misuse. - // index.Generate should probably be in charge of that. + // GenerateIndex should probably be in charge of that. - idx, err := index.Generate(src) + idx, err := GenerateIndex(src) if err != nil { return err } @@ -201,3 +203,17 @@ func WrapV1(src io.ReadSeeker, dst io.Writer) error { return nil } + +// AttachIndex attaches a given index to an existing car v2 file at given path and offset. +func AttachIndex(path string, idx index.Index, offset uint64) error { + // TODO: instead of offset, maybe take padding? + // TODO: check that the given path is indeed a CAR v2. + // TODO: update CAR v2 header according to the offset at which index is written out. + out, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o640) + if err != nil { + return err + } + defer out.Close() + indexWriter := internalio.NewOffsetWriter(out, int64(offset)) + return index.WriteTo(idx, indexWriter) +} From 921e3489ec295f9078492a8b8f448b8e05fe9503 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 14 Jul 2021 16:10:36 +0100 Subject: [PATCH 3480/3817] Support resumption in `ReadWrite` blockstore for finalized files Support resumption from a complete CARv2 file, effectively implementing append block to a CARv2 file. Note, on resumption the entire file is re-indexed. So it is undesirable to do this for complete cars. Fix a bug where `AllKeysChan` failed on files that contained the index and resumed from. Fix is done by truncating the CARv2 file, removing the index if it is present when blockstore is constructed when resuming. This is because if `AllKeysChan` is called on a file that contains the index, the ReadOnly backing will continue to read until EOF which will result in error. Since file is re-indexed on resumption and we require finalize to be called we truncate the file to remove index. Write header on every put so that resuming from finalized files that put more blocks without then finalizing works as expected. Otherwise, because the last put did not finalize and header is not updated any blocks put will effectively be lost. Add tests that assert resume with and without finalization. Add tests that assert read operations work on resumed read-write blockstore. Fixes: https://github.com/ipld/go-car/issues/155 Address review comments Avoid having to write car v2 header on every put by unfinalizing the blockstore first if resumed from a finalized file. Fix random seed for tests. This commit was moved from ipld/go-car@96e8614d94d704c49c6a5fbde3587b1c577db47d --- ipld/car/v2/blockstore/readwrite.go | 64 ++++++++++++++++++----- ipld/car/v2/blockstore/readwrite_test.go | 66 +++++++++++++++++++----- 2 files changed, 104 insertions(+), 26 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 52878ebbc..1ea7352ca 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -74,7 +74,8 @@ func WithCidDeduplication(b *ReadWrite) { // TODO should this take a bool and re // ReadWrite.Finalize must be called once putting and reading blocks are no longer needed. // Upon calling ReadWrite.Finalize the CAR v2 header and index are written out onto the file and the // backing file is closed. Once finalized, all read and write calls to this blockstore will result -// in panics. Note, a finalized file cannot be resumed from. +// in panics. Note, ReadWrite.Finalize must be called on an open instance regardless of whether any +// blocks were put or not. // // If a file at given path does not exist, the instantiation will write car.Pragma and data payload // header (i.e. the inner CAR v1 header) onto the file before returning. @@ -87,19 +88,21 @@ func WithCidDeduplication(b *ReadWrite) { // TODO should this take a bool and re // Resumption only works on files that were created by a previous instance of a ReadWrite // blockstore. This means a file created as a result of a successful call to NewReadWrite can be // resumed from as long as write operations such as ReadWrite.Put, ReadWrite.PutMany returned -// successfully and ReadWrite.Finalize was never called. On resumption the roots argument and -// WithCarV1Padding option must match the previous instantiation of ReadWrite blockstore that -// created the file. More explicitly, the file resuming from must: +// successfully. On resumption the roots argument and WithCarV1Padding option must match the +// previous instantiation of ReadWrite blockstore that created the file. More explicitly, the file +// resuming from must: // 1. start with a complete CAR v2 car.Pragma. // 2. contain a complete CAR v1 data header with root CIDs matching the CIDs passed to the // constructor, starting at offset optionally padded by WithCarV1Padding, followed by zero or // more complete data frames. If any corrupt data frames are present the resumption will fail. // Note, if set previously, the blockstore must use the same WithCarV1Padding option as before, // since this option is used to locate the CAR v1 data payload. -// 3. ReadWrite.Finalize must not have been called on the file. // // Note, resumption should be used with WithCidDeduplication, so that blocks that are successfully // written into the file are not re-written. Unless, the user explicitly wants duplicate blocks. +// +// Resuming from finalized files is allowed. However, resumption will regenerate the index +// regardless by scanning every existing block in file. func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, error) { // TODO: enable deduplication by default now that resumption is automatically attempted. f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0o666) // TODO: Should the user be able to configure FileMode permissions? @@ -170,14 +173,45 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { return fmt.Errorf("cannot resume on CAR file with version %v", version) } - // Check if file is finalized. - // A file is finalized when it contains a valid CAR v2 header with non-zero v1 offset. - // If finalized, cannot resume from. + // Check if file was finalized by trying to read the CAR v2 header. + // We check because if finalized the CARv1 reader behaviour needs to be adjusted since + // EOF will not signify end of CAR v1 payload. i.e. index is most likely present. var headerInFile carv2.Header - if _, err := headerInFile.ReadFrom(internalio.NewOffsetReadSeeker(b.f, carv2.PragmaSize)); err == nil { - // TODO: we technically can; this could be a feature to do if asked for. - if headerInFile.CarV1Offset != 0 { - return fmt.Errorf("cannot resume from a finalized file") + _, err = headerInFile.ReadFrom(internalio.NewOffsetReadSeeker(b.f, carv2.PragmaSize)) + + // If reading CARv2 header succeeded, and CARv1 offset in header is not zero then the file is + // most-likely finalized. Check padding and truncate the file to remove index. + // Otherwise, carry on reading the v1 payload at offset determined from b.header. + if err == nil && headerInFile.CarV1Offset != 0 { + if headerInFile.CarV1Offset != b.header.CarV1Offset { + // Assert that the padding on file matches the given WithCarV1Padding option. + gotPadding := headerInFile.CarV1Offset - carv2.PragmaSize - carv2.HeaderSize + wantPadding := b.header.CarV1Offset - carv2.PragmaSize - carv2.HeaderSize + return fmt.Errorf( + "cannot resume from file with mismatched CARv1 offset; "+ + "`WithCarV1Padding` option must match the padding on file."+ + "Expected padding value of %v but got %v", wantPadding, gotPadding, + ) + } else if headerInFile.CarV1Size != 0 { + // If header in file contains the size of car v1, then the index is most likely present. + // Since we will need to re-generate the index, as the one in file is flattened, truncate + // the file so that the Readonly.backing has the right set of bytes to deal with. + // This effectively means resuming from a finalized file will wipe its index even if there + // are no blocks put unless the user calls finalize. + if err := b.f.Truncate(int64(headerInFile.CarV1Offset + headerInFile.CarV1Size)); err != nil { + return err + } + } else { + // If CARv1 size is zero, since CARv1 offset wasn't, then the CARv2 header was + // most-likely partially written. Since we write the header last in Finalize then the + // file most-likely contains the index and we cannot know where it starts, therefore + // can't resume. + return errors.New("corrupt CARv2 header; cannot resume from file") + } + // Now that CARv2 header is present on file, clear it to avoid incorrect size and offset in + // header in case blocksotre is closed without finalization and is resumed from. + if err := b.unfinalize(); err != nil { + return err } } @@ -244,6 +278,11 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { return err } +func (b *ReadWrite) unfinalize() error { + _, err := new(carv2.Header).WriteTo(internalio.NewOffsetWriter(b.f, carv2.PragmaSize)) + return err +} + func (b *ReadWrite) panicIfFinalized() { if b.header.CarV1Size != 0 { panic("must not use a read-write blockstore after finalizing") @@ -291,7 +330,6 @@ func (b *ReadWrite) Finalize() error { b.mu.Lock() defer b.mu.Unlock() - // TODO check if add index option is set and don't write the index then set index offset to zero. // TODO see if folks need to continue reading from a finalized blockstore, if so return ReadOnly blockstore here. b.header = b.header.WithCarV1Size(uint64(b.carV1Writer.Position())) diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 97d1bf58b..22ac788c5 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -26,6 +26,8 @@ import ( "github.com/ipld/go-car/v2/internal/carv1" ) +var rng = rand.New(rand.NewSource(1413)) + func TestBlockstore(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() @@ -54,7 +56,7 @@ func TestBlockstore(t *testing.T) { cids = append(cids, b.Cid()) // try reading a random one: - candidate := cids[rand.Intn(len(cids))] + candidate := cids[rng.Intn(len(cids))] if has, err := ingester.Has(candidate); !has || err != nil { t.Fatalf("expected to find %s but didn't: %s", candidate, err) } @@ -291,25 +293,62 @@ func TestBlockstoreResumption(t *testing.T) { require.NoError(t, err) // For each block resume on the same file, putting blocks one at a time. + var wantBlockCountSoFar int + wantBlocks := make(map[cid.Cid]blocks.Block) for { b, err := r.Next() if err == io.EOF { break } require.NoError(t, err) - - // 30% chance of subject failing (or closing file without calling Finalize). - // The higher this percentage the slower the test runs considering the number of blocks in the original CAR v1 test payload. - shouldFailAbruptly := rand.Float32() <= 0.3 - if shouldFailAbruptly { - // Close off the open file and re-instantiate a new subject with resumption enabled. - // Note, we don't have to close the file for resumption to work. - // We do this to avoid resource leak during testing. - require.NoError(t, subject.Close()) + wantBlockCountSoFar++ + wantBlocks[b.Cid()] = b + + // 30% chance of subject failing; more concretely: re-instantiating blockstore with the same + // file without calling Finalize. The higher this percentage the slower the test runs + // considering the number of blocks in the original CAR v1 test payload. + resume := rng.Float32() <= 0.3 + // If testing resume case, then flip a coin to decide whether to finalize before blockstore + // re-instantiation or not. Note, both cases should work for resumption since we do not + // limit resumption to unfinalized files. + finalizeBeforeResumption := rng.Float32() <= 0.5 + if resume { + if finalizeBeforeResumption { + require.NoError(t, subject.Finalize()) + } else { + // Close off the open file and re-instantiate a new subject with resumption enabled. + // Note, we don't have to close the file for resumption to work. + // We do this to avoid resource leak during testing. + require.NoError(t, subject.Close()) + } subject, err = blockstore.NewReadWrite(path, r.Header.Roots) require.NoError(t, err) } require.NoError(t, subject.Put(b)) + + // With 10% chance test read operations on an resumed read-write blockstore. + // We don't test on every put to reduce test runtime. + testRead := rng.Float32() <= 0.1 + if testRead { + // Assert read operations on the read-write blockstore are as expected when resumed from an + // existing file + var gotBlockCountSoFar int + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + t.Cleanup(cancel) + keysChan, err := subject.AllKeysChan(ctx) + require.NoError(t, err) + for k := range keysChan { + has, err := subject.Has(k) + require.NoError(t, err) + require.True(t, has) + gotBlock, err := subject.Get(k) + require.NoError(t, err) + require.Equal(t, wantBlocks[k], gotBlock) + gotBlockCountSoFar++ + } + // Assert the number of blocks in file are as expected calculated via AllKeysChan + require.Equal(t, wantBlockCountSoFar, gotBlockCountSoFar) + } } require.NoError(t, subject.Close()) @@ -358,12 +397,13 @@ func TestBlockstoreResumption(t *testing.T) { require.Equal(t, wantIdx, gotIdx) } -func TestBlockstoreResumptionFailsOnFinalizedFile(t *testing.T) { +func TestBlockstoreResumptionIsSupportedOnFinalizedFile(t *testing.T) { path := filepath.Join(t.TempDir(), "readwrite-resume-finalized.car") // Create an incomplete CAR v2 file with no blocks put. subject, err := blockstore.NewReadWrite(path, []cid.Cid{}) require.NoError(t, err) require.NoError(t, subject.Finalize()) - _, err = blockstore.NewReadWrite(path, []cid.Cid{}) - require.Errorf(t, err, "cannot resume from a finalized file") + subject, err = blockstore.NewReadWrite(path, []cid.Cid{}) + t.Cleanup(func() { subject.Close() }) + require.NoError(t, err) } From c473e9dfee091bb70a0f6af01e725b84ed16ee41 Mon Sep 17 00:00:00 2001 From: aarshkshah1992 Date: Thu, 15 Jul 2021 13:18:55 +0530 Subject: [PATCH 3481/3817] index error should return not found This commit was moved from ipld/go-car@62be0f341082206c4c9b3f39f484762428493c91 --- ipld/car/v2/blockstore/readonly.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index f7e1c646b..6ceba0c35 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -166,8 +166,9 @@ func (b *ReadOnly) Get(key cid.Cid) (blocks.Block, error) { defer b.mu.RUnlock() offset, err := b.idx.Get(key) + // TODO Improve error handling; not all errors mean NotFound. if err != nil { - return nil, err + return nil, blockstore.ErrNotFound } entry, data, err := b.readBlock(int64(offset)) if err != nil { From 2e12923651c0d901b78d5f9d595bddadeea8d714 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 15 Jul 2021 10:28:09 +0100 Subject: [PATCH 3482/3817] Remove dependency to `bufio.Reader` in internal `carv1` package Remove dependency to `bufio.Reader` in internal `carv1` package that seems to be mainly used for peeking a byte to return appropriate error when stream abruptly ends, relating to #36. This allows simplification of code across the repo and remove all unnecessary wrappings of `io.Reader` with `bufio.Reader`. This will also aid simplify the internal IO utilities which will be done in future PRs. For now we simply remove dependency to `bufio.Reader` See: - https://github.com/ipld/go-car/pull/36 This commit was moved from ipld/go-car@cc1e449c9bc81b8d8ce5731406be5ebda14cd4c7 --- ipld/car/v2/blockstore/readonly.go | 7 +++---- ipld/car/v2/blockstore/readwrite.go | 3 +-- ipld/car/v2/car_test.go | 3 +-- ipld/car/v2/index/index.go | 8 ++++---- ipld/car/v2/index_gen.go | 3 +-- ipld/car/v2/internal/carv1/car.go | 14 ++++++-------- ipld/car/v2/internal/carv1/util/util.go | 20 +++++++++----------- ipld/car/v2/internal/io/converter.go | 20 ++++++++++++++++++++ ipld/car/v2/reader.go | 5 ++--- 9 files changed, 47 insertions(+), 36 deletions(-) create mode 100644 ipld/car/v2/internal/io/converter.go diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 6ceba0c35..658587046 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -1,7 +1,6 @@ package blockstore import ( - "bufio" "bytes" "context" "errors" @@ -128,7 +127,7 @@ func OpenReadOnly(path string) (*ReadOnly, error) { } func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) { - bcid, data, err := util.ReadNode(bufio.NewReader(internalio.NewOffsetReadSeeker(b.backing, idx))) + bcid, data, err := util.ReadNode(internalio.NewOffsetReadSeeker(b.backing, idx)) return bcid, data, err } @@ -222,7 +221,7 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // TODO we may use this walk for populating the index, and we need to be able to iterate keys in this way somewhere for index generation. In general though, when it's asked for all keys from a blockstore with an index, we should iterate through the index when possible rather than linear reads through the full car. rdr := internalio.NewOffsetReadSeeker(b.backing, 0) - header, err := carv1.ReadHeader(bufio.NewReader(rdr)) + header, err := carv1.ReadHeader(rdr) if err != nil { return nil, fmt.Errorf("error reading car header: %w", err) } @@ -281,7 +280,7 @@ func (b *ReadOnly) HashOnRead(bool) { // Roots returns the root CIDs of the backing CAR. func (b *ReadOnly) Roots() ([]cid.Cid, error) { - header, err := carv1.ReadHeader(bufio.NewReader(internalio.NewOffsetReadSeeker(b.backing, 0))) + header, err := carv1.ReadHeader(internalio.NewOffsetReadSeeker(b.backing, 0)) if err != nil { return nil, fmt.Errorf("error reading car header: %w", err) } diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 1ea7352ca..a0f3b46a0 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -1,7 +1,6 @@ package blockstore import ( - "bufio" "context" "errors" "fmt" @@ -217,7 +216,7 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { // Use the given CAR v1 padding to instantiate the CAR v1 reader on file. v1r := internalio.NewOffsetReadSeeker(b.ReadOnly.backing, 0) - header, err := carv1.ReadHeader(bufio.NewReader(v1r)) + header, err := carv1.ReadHeader(v1r) if err != nil { // Cannot read the CAR v1 header; the file is most likely corrupt. return fmt.Errorf("error reading car header: %w", err) diff --git a/ipld/car/v2/car_test.go b/ipld/car/v2/car_test.go index 5fea95bbc..83a552ba6 100644 --- a/ipld/car/v2/car_test.go +++ b/ipld/car/v2/car_test.go @@ -1,7 +1,6 @@ package car_test import ( - "bufio" "bytes" "testing" @@ -36,7 +35,7 @@ func TestCarV2PragmaLength(t *testing.T) { } func TestCarV2PragmaIsValidCarV1Header(t *testing.T) { - v1h, err := carv1.ReadHeader(bufio.NewReader(bytes.NewReader(carv2.Pragma))) + v1h, err := carv1.ReadHeader(bytes.NewReader(carv2.Pragma)) assert.NoError(t, err, "cannot decode pragma as CBOR with CAR v1 header structure") assert.Equal(t, &carv1.CarHeader{ Roots: nil, diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 906be639f..96b730f16 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -1,12 +1,13 @@ package index import ( - "bufio" "encoding/binary" "fmt" "io" "os" + internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/multiformats/go-multicodec" "github.com/multiformats/go-varint" @@ -77,8 +78,7 @@ func WriteTo(idx Index, w io.Writer) error { // The reader decodes the index by reading the first byte to interpret the encoding. // Returns error if the encoding is not known. func ReadFrom(r io.Reader) (Index, error) { - reader := bufio.NewReader(r) - code, err := varint.ReadUvarint(reader) + code, err := varint.ReadUvarint(internalio.ToByteReader(r)) if err != nil { return nil, err } @@ -87,7 +87,7 @@ func ReadFrom(r io.Reader) (Index, error) { if err != nil { return nil, err } - if err := idx.Unmarshal(reader); err != nil { + if err := idx.Unmarshal(r); err != nil { return nil, err } return idx, nil diff --git a/ipld/car/v2/index_gen.go b/ipld/car/v2/index_gen.go index 59110897d..cfbc18437 100644 --- a/ipld/car/v2/index_gen.go +++ b/ipld/car/v2/index_gen.go @@ -1,7 +1,6 @@ package car import ( - "bufio" "fmt" "io" "os" @@ -29,7 +28,7 @@ func (r readSeekerPlusByte) ReadByte() (byte, error) { // GenerateIndex generates index for a given car in v1 format. // The index can be stored using index.Save into a file or serialized using index.WriteTo. func GenerateIndex(v1 io.ReadSeeker) (index.Index, error) { - header, err := carv1.ReadHeader(bufio.NewReader(v1)) + header, err := carv1.ReadHeader(v1) if err != nil { return nil, fmt.Errorf("error reading car header: %w", err) } diff --git a/ipld/car/v2/internal/carv1/car.go b/ipld/car/v2/internal/carv1/car.go index d940ce25a..ab5753bec 100644 --- a/ipld/car/v2/internal/carv1/car.go +++ b/ipld/car/v2/internal/carv1/car.go @@ -1,7 +1,6 @@ package carv1 import ( - "bufio" "context" "fmt" "io" @@ -57,8 +56,8 @@ func WriteCar(ctx context.Context, ds format.NodeGetter, roots []cid.Cid, w io.W return nil } -func ReadHeader(br *bufio.Reader) (*CarHeader, error) { - hb, err := util.LdRead(br) +func ReadHeader(r io.Reader) (*CarHeader, error) { + hb, err := util.LdRead(r) if err != nil { return nil, err } @@ -107,13 +106,12 @@ func (cw *carWriter) writeNode(ctx context.Context, nd format.Node) error { } type CarReader struct { - br *bufio.Reader + r io.Reader Header *CarHeader } func NewCarReader(r io.Reader) (*CarReader, error) { - br := bufio.NewReader(r) - ch, err := ReadHeader(br) + ch, err := ReadHeader(r) if err != nil { return nil, err } @@ -127,13 +125,13 @@ func NewCarReader(r io.Reader) (*CarReader, error) { } return &CarReader{ - br: br, + r: r, Header: ch, }, nil } func (cr *CarReader) Next() (blocks.Block, error) { - c, data, err := util.ReadNode(cr.br) + c, data, err := util.ReadNode(cr.r) if err != nil { return nil, err } diff --git a/ipld/car/v2/internal/carv1/util/util.go b/ipld/car/v2/internal/carv1/util/util.go index 297d74c8a..dce53003b 100644 --- a/ipld/car/v2/internal/carv1/util/util.go +++ b/ipld/car/v2/internal/carv1/util/util.go @@ -1,9 +1,10 @@ package util import ( - "bufio" "io" + internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/multiformats/go-varint" cid "github.com/ipfs/go-cid" @@ -14,8 +15,8 @@ type BytesReader interface { io.ByteReader } -func ReadNode(br *bufio.Reader) (cid.Cid, []byte, error) { - data, err := LdRead(br) +func ReadNode(r io.Reader) (cid.Cid, []byte, error) { + data, err := LdRead(r) if err != nil { return cid.Cid{}, nil, err } @@ -60,15 +61,12 @@ func LdSize(d ...[]byte) uint64 { return sum + uint64(s) } -func LdRead(r *bufio.Reader) ([]byte, error) { - if _, err := r.Peek(1); err != nil { // no more blocks, likely clean io.EOF - return nil, err - } - - l, err := varint.ReadUvarint(r) +func LdRead(r io.Reader) ([]byte, error) { + l, err := varint.ReadUvarint(internalio.ToByteReader(r)) if err != nil { - if err == io.EOF { - return nil, io.ErrUnexpectedEOF // don't silently pretend this is a clean EOF + // If the length of bytes read is non-zero when the error is EOF then signal an unclean EOF. + if l > 0 && err == io.EOF { + return nil, io.ErrUnexpectedEOF } return nil, err } diff --git a/ipld/car/v2/internal/io/converter.go b/ipld/car/v2/internal/io/converter.go new file mode 100644 index 000000000..37973bbe3 --- /dev/null +++ b/ipld/car/v2/internal/io/converter.go @@ -0,0 +1,20 @@ +package io + +import "io" + +func ToByteReader(r io.Reader) io.ByteReader { + if br, ok := r.(io.ByteReader); ok { + return br + } + return readerPlusByte{r} +} + +type readerPlusByte struct { + io.Reader +} + +func (r readerPlusByte) ReadByte() (byte, error) { + var p [1]byte + _, err := io.ReadFull(r, p[:]) + return p[0], err +} diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 6cf684c74..709048b5c 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -1,7 +1,6 @@ package car import ( - "bufio" "fmt" "io" @@ -70,7 +69,7 @@ func (r *Reader) Roots() ([]cid.Cid, error) { if r.roots != nil { return r.roots, nil } - header, err := carv1.ReadHeader(bufio.NewReader(r.CarV1Reader())) + header, err := carv1.ReadHeader(r.CarV1Reader()) if err != nil { return nil, err } @@ -115,7 +114,7 @@ func (r *Reader) Close() error { // This function accepts both CAR v1 and v2 payloads. func ReadVersion(r io.Reader) (version uint64, err error) { // TODO if the user provides a reader that sufficiently satisfies what carv1.ReadHeader is asking then use that instead of wrapping every time. - header, err := carv1.ReadHeader(bufio.NewReader(r)) + header, err := carv1.ReadHeader(r) if err != nil { return } From 767480e58cb1ab1ed32687da49e1877d3df3b09d Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 15 Jul 2021 13:17:18 +0100 Subject: [PATCH 3483/3817] Implement index generation using only `io.Reader` Implement the ability to generate index from a CARv1 payload given only an `io.Reader`, where the previous implementation required `io.ReadSeeker`. The rationale is to be minimal in what we expect in the API, since index generation from a CARv1 payload never need to rewind the reader and only moves forward in the stream. Refactor utility IO functions that convert between types in one place. Implement constructor functions that only instantiate wrappers when the passed argument does not satisfy a required interface. Fixes: - https://github.com/ipld/go-car/issues/146 Relates to: - https://github.com/ipld/go-car/issues/145 This commit was moved from ipld/go-car@6b085bcb2b89b012668f811f86d29715605a87d1 --- ipld/car/v2/index_gen.go | 50 ++++++++--------- ipld/car/v2/internal/io/converter.go | 81 ++++++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 34 deletions(-) diff --git a/ipld/car/v2/index_gen.go b/ipld/car/v2/index_gen.go index cfbc18437..223939d20 100644 --- a/ipld/car/v2/index_gen.go +++ b/ipld/car/v2/index_gen.go @@ -6,6 +6,8 @@ import ( "os" "sync" + internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/ipld/go-car/v2/index" "github.com/multiformats/go-multicodec" @@ -15,20 +17,11 @@ import ( "github.com/ipld/go-car/v2/internal/carv1" ) -type readSeekerPlusByte struct { - io.ReadSeeker -} - -func (r readSeekerPlusByte) ReadByte() (byte, error) { - var p [1]byte - _, err := io.ReadFull(r, p[:]) - return p[0], err -} - // GenerateIndex generates index for a given car in v1 format. // The index can be stored using index.Save into a file or serialized using index.WriteTo. -func GenerateIndex(v1 io.ReadSeeker) (index.Index, error) { - header, err := carv1.ReadHeader(v1) +func GenerateIndex(v1r io.Reader) (index.Index, error) { + reader := internalio.ToByteReadSeeker(v1r) + header, err := carv1.ReadHeader(reader) if err != nil { return nil, fmt.Errorf("error reading car header: %w", err) } @@ -37,28 +30,26 @@ func GenerateIndex(v1 io.ReadSeeker) (index.Index, error) { return nil, fmt.Errorf("expected version to be 1, got %v", header.Version) } - offset, err := carv1.HeaderSize(header) - if err != nil { - return nil, err - } - idx, err := index.New(multicodec.CarIndexSorted) if err != nil { return nil, err } records := make([]index.Record, 0) - // Seek to the first frame. - // Record the start of each frame, which we need for the index records. - frameOffset := int64(0) - if frameOffset, err = v1.Seek(int64(offset), io.SeekStart); err != nil { + // Record the start of each frame, with first frame starring from current position in the + // reader, i.e. right after the header, since we have only read the header so far. + var frameOffset int64 + + // The Seek call below is equivalent to getting the reader.offset directly. + // We get it through Seek to only depend on APIs of a typical io.Seeker. + // This would also reduce refactoring in case the utility reader is moved. + if frameOffset, err = reader.Seek(0, io.SeekCurrent); err != nil { return nil, err } for { - // Grab the length of the frame. - // Note that ReadUvarint wants a ByteReader. - length, err := varint.ReadUvarint(readSeekerPlusByte{v1}) + // Read the frame's length. + frameLen, err := varint.ReadUvarint(reader) if err != nil { if err == io.EOF { break @@ -69,12 +60,12 @@ func GenerateIndex(v1 io.ReadSeeker) (index.Index, error) { // Null padding; treat zero-length frames as an EOF. // They don't contain a CID nor block, so they're not useful. // TODO: Amend the CARv1 spec to explicitly allow this. - if length == 0 { + if frameLen == 0 { break } - // Grab the CID. - n, c, err := cid.CidFromReader(v1) + // Read the CID. + cidLen, c, err := cid.CidFromReader(reader) if err != nil { return nil, err } @@ -82,7 +73,8 @@ func GenerateIndex(v1 io.ReadSeeker) (index.Index, error) { // Seek to the next frame by skipping the block. // The frame length includes the CID, so subtract it. - if frameOffset, err = v1.Seek(int64(length)-int64(n), io.SeekCurrent); err != nil { + remainingFrameLen := int64(frameLen) - int64(cidLen) + if frameOffset, err = reader.Seek(remainingFrameLen, io.SeekCurrent); err != nil { return nil, err } } @@ -134,7 +126,7 @@ func ReadOrGenerateIndex(rs io.ReadSeeker) (index.Index, error) { if err != nil { return nil, err } - // Seek to the begining, since reading the version changes the reader's offset. + // Seek to the beginning, since reading the version changes the reader's offset. if _, err := rs.Seek(0, io.SeekStart); err != nil { return nil, err } diff --git a/ipld/car/v2/internal/io/converter.go b/ipld/car/v2/internal/io/converter.go index 37973bbe3..4c723ec46 100644 --- a/ipld/car/v2/internal/io/converter.go +++ b/ipld/car/v2/internal/io/converter.go @@ -1,19 +1,90 @@ package io -import "io" +import ( + "io" + "io/ioutil" +) + +var ( + _ io.ByteReader = (*readerPlusByte)(nil) + _ io.ByteReader = (*readSeekerPlusByte)(nil) + _ io.ByteReader = (*discardingReadSeekerPlusByte)(nil) + _ io.ReadSeeker = (*discardingReadSeekerPlusByte)(nil) +) + +type ( + readerPlusByte struct { + io.Reader + } + + readSeekerPlusByte struct { + io.ReadSeeker + } + + discardingReadSeekerPlusByte struct { + io.Reader + offset int64 + } + + ByteReadSeeker interface { + io.ReadSeeker + io.ByteReader + } +) func ToByteReader(r io.Reader) io.ByteReader { if br, ok := r.(io.ByteReader); ok { return br } - return readerPlusByte{r} + return &readerPlusByte{r} +} + +func ToByteReadSeeker(r io.Reader) ByteReadSeeker { + if brs, ok := r.(ByteReadSeeker); ok { + return brs + } + if rs, ok := r.(io.ReadSeeker); ok { + return &readSeekerPlusByte{rs} + } + return &discardingReadSeekerPlusByte{Reader: r} +} + +func (rb readerPlusByte) ReadByte() (byte, error) { + return readByte(rb) +} + +func (rsb readSeekerPlusByte) ReadByte() (byte, error) { + return readByte(rsb) } -type readerPlusByte struct { - io.Reader +func (drsb *discardingReadSeekerPlusByte) ReadByte() (byte, error) { + return readByte(drsb) +} + +func (drsb *discardingReadSeekerPlusByte) Read(p []byte) (read int, err error) { + read, err = drsb.Reader.Read(p) + drsb.offset += int64(read) + return +} + +func (drsb *discardingReadSeekerPlusByte) Seek(offset int64, whence int) (int64, error) { + switch whence { + case io.SeekStart: + n := offset - drsb.offset + if n < 0 { + panic("unsupported rewind via whence: io.SeekStart") + } + _, err := io.CopyN(ioutil.Discard, drsb, n) + return drsb.offset, err + case io.SeekCurrent: + _, err := io.CopyN(ioutil.Discard, drsb, offset) + return drsb.offset, err + default: + panic("unsupported whence: io.SeekEnd") + } } -func (r readerPlusByte) ReadByte() (byte, error) { +func readByte(r io.Reader) (byte, error) { var p [1]byte _, err := io.ReadFull(r, p[:]) return p[0], err From 30b51b4345e7cdcfe94056b0329588f36fe1bab2 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 15 Jul 2021 09:53:41 +0100 Subject: [PATCH 3484/3817] Unexport unused `Writer` API until it is re-implemented Unexport the CARv2 writer API until it is reimplemented using WriterAt or WriteSeeker to be more efficient. This API is not used anyway and we can postpone releasing it until SelectiveCar writer API is figured out. For now we unexport it to carry on with interface finalization and alpha release. This commit was moved from ipld/go-car@1a14cefa8e25824be305428cfc8bb51346c1e5bc --- ipld/car/v2/writer.go | 19 +++++++++---------- ipld/car/v2/writer_test.go | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index 29b04f30a..367597228 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -21,8 +21,8 @@ var bulkPadding = make([]byte, bulkPaddingBytesSize) type ( // padding represents the number of padding bytes. padding uint64 - // Writer writes CAR v2 into a give io.Writer. - Writer struct { + // writer writes CAR v2 into a given io.Writer. + writer struct { NodeGetter format.NodeGetter CarV1Padding uint64 IndexPadding uint64 @@ -31,7 +31,6 @@ type ( roots []cid.Cid encodedCarV1 *bytes.Buffer } - WriteOption func(*Writer) ) // WriteTo writes this padding to the given writer as default value bytes. @@ -56,9 +55,9 @@ func (p padding) WriteTo(w io.Writer) (n int64, err error) { return } -// NewWriter instantiates a new CAR v2 writer. -func NewWriter(ctx context.Context, ng format.NodeGetter, roots []cid.Cid) *Writer { - return &Writer{ +// newWriter instantiates a new CAR v2 writer. +func newWriter(ctx context.Context, ng format.NodeGetter, roots []cid.Cid) *writer { + return &writer{ NodeGetter: ng, ctx: ctx, roots: roots, @@ -67,7 +66,7 @@ func NewWriter(ctx context.Context, ng format.NodeGetter, roots []cid.Cid) *Writ } // WriteTo writes the given root CIDs according to CAR v2 specification. -func (w *Writer) WriteTo(writer io.Writer) (n int64, err error) { +func (w *writer) WriteTo(writer io.Writer) (n int64, err error) { _, err = writer.Write(Pragma) if err != nil { return @@ -113,14 +112,14 @@ func (w *Writer) WriteTo(writer io.Writer) (n int64, err error) { return } -func (w *Writer) writeHeader(writer io.Writer, carV1Len int) (int64, error) { +func (w *writer) writeHeader(writer io.Writer, carV1Len int) (int64, error) { header := NewHeader(uint64(carV1Len)). WithCarV1Padding(w.CarV1Padding). WithIndexPadding(w.IndexPadding) return header.WriteTo(writer) } -func (w *Writer) writeIndex(writer io.Writer, carV1 []byte) (int64, error) { +func (w *writer) writeIndex(writer io.Writer, carV1 []byte) (int64, error) { // TODO avoid recopying the bytes by refactoring index once it is integrated here. // Right now we copy the bytes since index takes a writer. // Consider refactoring index to make this process more efficient. @@ -185,7 +184,7 @@ func WrapV1(src io.ReadSeeker, dst io.Writer) error { return err } - // Similar to the Writer API, write all components of a CARv2 to the + // Similar to the writer API, write all components of a CARv2 to the // destination file: Pragma, Header, CARv1, Index. v2Header := NewHeader(uint64(v1Size)) if _, err := dst.Write(Pragma); err != nil { diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index 2e48fc3ca..06d0ff42d 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -60,7 +60,7 @@ func TestPadding_WriteTo(t *testing.T) { func TestNewWriter(t *testing.T) { dagService := dstest.Mock() wantRoots := generateRootCid(t, dagService) - writer := NewWriter(context.Background(), dagService, wantRoots) + writer := newWriter(context.Background(), dagService, wantRoots) assert.Equal(t, wantRoots, writer.roots) } From 9bac8b3ca6e7ca5b08a2b5716c0e22c88fbb56c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Thu, 15 Jul 2021 15:29:26 +0100 Subject: [PATCH 3485/3817] make all "Open" APIs use consistent names All the "New" APIs take IO interfaces, so they don't open any file by themselves. However, the APIs that take a path to disk and return a blockstore or a reader need closing, so the prefix "Open" helps clarify that. Plus, it makes names more consistent. This commit was moved from ipld/go-car@4827ee39c808dc1c5c5c0008362d9603b3760c9e --- ipld/car/v2/blockstore/doc.go | 2 +- ipld/car/v2/blockstore/readwrite.go | 6 +++--- ipld/car/v2/blockstore/readwrite_test.go | 18 +++++++++--------- ipld/car/v2/example_test.go | 2 +- ipld/car/v2/reader.go | 7 +++---- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/ipld/car/v2/blockstore/doc.go b/ipld/car/v2/blockstore/doc.go index c41500d78..7210d742b 100644 --- a/ipld/car/v2/blockstore/doc.go +++ b/ipld/car/v2/blockstore/doc.go @@ -14,5 +14,5 @@ // panic if used. To continue reading the blocks users are encouraged to use ReadOnly blockstore // instantiated from the same file path using OpenReadOnly. // A user may resume reading/writing from files produced by an instance of ReadWrite blockstore. The -// resumption is attempted automatically, if the path passed to NewReadWrite exists. +// resumption is attempted automatically, if the path passed to OpenReadWrite exists. package blockstore diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index a0f3b46a0..c65f4deb0 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -68,7 +68,7 @@ func WithCidDeduplication(b *ReadWrite) { // TODO should this take a bool and re b.dedupCids = true } -// NewReadWrite creates a new ReadWrite at the given path with a provided set of root CIDs and options. +// OpenReadWrite creates a new ReadWrite at the given path with a provided set of root CIDs and options. // // ReadWrite.Finalize must be called once putting and reading blocks are no longer needed. // Upon calling ReadWrite.Finalize the CAR v2 header and index are written out onto the file and the @@ -85,7 +85,7 @@ func WithCidDeduplication(b *ReadWrite) { // TODO should this take a bool and re // returned successfully. // // Resumption only works on files that were created by a previous instance of a ReadWrite -// blockstore. This means a file created as a result of a successful call to NewReadWrite can be +// blockstore. This means a file created as a result of a successful call to OpenReadWrite can be // resumed from as long as write operations such as ReadWrite.Put, ReadWrite.PutMany returned // successfully. On resumption the roots argument and WithCarV1Padding option must match the // previous instantiation of ReadWrite blockstore that created the file. More explicitly, the file @@ -102,7 +102,7 @@ func WithCidDeduplication(b *ReadWrite) { // TODO should this take a bool and re // // Resuming from finalized files is allowed. However, resumption will regenerate the index // regardless by scanning every existing block in file. -func NewReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, error) { +func OpenReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, error) { // TODO: enable deduplication by default now that resumption is automatically attempted. f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0o666) // TODO: Should the user be able to configure FileMode permissions? if err != nil { diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 22ac788c5..191865bf3 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -39,7 +39,7 @@ func TestBlockstore(t *testing.T) { require.NoError(t, err) path := filepath.Join(t.TempDir(), "readwrite.car") - ingester, err := blockstore.NewReadWrite(path, r.Header.Roots) + ingester, err := blockstore.OpenReadWrite(path, r.Header.Roots) require.NoError(t, err) t.Cleanup(func() { ingester.Finalize() }) @@ -102,13 +102,13 @@ func TestBlockstore(t *testing.T) { func TestBlockstorePutSameHashes(t *testing.T) { tdir := t.TempDir() - wbs, err := blockstore.NewReadWrite( + wbs, err := blockstore.OpenReadWrite( filepath.Join(tdir, "readwrite.car"), nil, ) require.NoError(t, err) t.Cleanup(func() { wbs.Finalize() }) - wbsd, err := blockstore.NewReadWrite( + wbsd, err := blockstore.OpenReadWrite( filepath.Join(tdir, "readwrite-dedup.car"), nil, blockstore.WithCidDeduplication, ) @@ -202,7 +202,7 @@ func TestBlockstorePutSameHashes(t *testing.T) { } func TestBlockstoreConcurrentUse(t *testing.T) { - wbs, err := blockstore.NewReadWrite(filepath.Join(t.TempDir(), "readwrite.car"), nil) + wbs, err := blockstore.OpenReadWrite(filepath.Join(t.TempDir(), "readwrite.car"), nil) require.NoError(t, err) t.Cleanup(func() { wbs.Finalize() }) @@ -289,7 +289,7 @@ func TestBlockstoreResumption(t *testing.T) { path := filepath.Join(t.TempDir(), "readwrite-resume.car") // Create an incomplete CAR v2 file with no blocks put. - subject, err := blockstore.NewReadWrite(path, r.Header.Roots) + subject, err := blockstore.OpenReadWrite(path, r.Header.Roots) require.NoError(t, err) // For each block resume on the same file, putting blocks one at a time. @@ -321,7 +321,7 @@ func TestBlockstoreResumption(t *testing.T) { // We do this to avoid resource leak during testing. require.NoError(t, subject.Close()) } - subject, err = blockstore.NewReadWrite(path, r.Header.Roots) + subject, err = blockstore.OpenReadWrite(path, r.Header.Roots) require.NoError(t, err) } require.NoError(t, subject.Put(b)) @@ -353,7 +353,7 @@ func TestBlockstoreResumption(t *testing.T) { require.NoError(t, subject.Close()) // Finalize the blockstore to complete partially written CAR v2 file. - subject, err = blockstore.NewReadWrite(path, r.Header.Roots) + subject, err = blockstore.OpenReadWrite(path, r.Header.Roots) require.NoError(t, err) require.NoError(t, subject.Finalize()) @@ -400,10 +400,10 @@ func TestBlockstoreResumption(t *testing.T) { func TestBlockstoreResumptionIsSupportedOnFinalizedFile(t *testing.T) { path := filepath.Join(t.TempDir(), "readwrite-resume-finalized.car") // Create an incomplete CAR v2 file with no blocks put. - subject, err := blockstore.NewReadWrite(path, []cid.Cid{}) + subject, err := blockstore.OpenReadWrite(path, []cid.Cid{}) require.NoError(t, err) require.NoError(t, subject.Finalize()) - subject, err = blockstore.NewReadWrite(path, []cid.Cid{}) + subject, err = blockstore.OpenReadWrite(path, []cid.Cid{}) t.Cleanup(func() { subject.Close() }) require.NoError(t, err) } diff --git a/ipld/car/v2/example_test.go b/ipld/car/v2/example_test.go index d590ab89f..4e7446628 100644 --- a/ipld/car/v2/example_test.go +++ b/ipld/car/v2/example_test.go @@ -21,7 +21,7 @@ func ExampleWrapV1File() { } // Open our new CARv2 file and show some info about it. - cr, err := carv2.NewReaderMmap(dst) + cr, err := carv2.OpenReader(dst) if err != nil { panic(err) } diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 709048b5c..ad231c0f1 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -19,9 +19,8 @@ type Reader struct { carv2Closer io.Closer } -// NewReaderMmap is a wrapper for NewReader which opens the file at path with -// x/exp/mmap. -func NewReaderMmap(path string) (*Reader, error) { +// OpenReader is a wrapper for NewReader which opens the file at path. +func OpenReader(path string) (*Reader, error) { f, err := mmap.Open(path) if err != nil { return nil, err @@ -102,7 +101,7 @@ func (r *Reader) IndexReader() io.Reader { return internalio.NewOffsetReadSeeker(r.r, int64(r.Header.IndexOffset)) } -// Close closes the underlying reader if it was opened by NewReaderMmap. +// Close closes the underlying reader if it was opened by OpenReader. func (r *Reader) Close() error { if r.carv2Closer != nil { return r.carv2Closer.Close() From ef8903fa3c08502a0335d7660c2ea2a185887006 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 15 Jul 2021 17:00:39 +0100 Subject: [PATCH 3486/3817] Improve test coverage for `ReadWrite` blockstore Add tests that asserts: - when padding options are set the payload is as expected - when finalized, blockstore calls panic - when resumed from mismatching padding error is as expected - when resumed from non-v2 file error is as expected Remove redundant TODOs in code This commit was moved from ipld/go-car@dbdb7428304d334d74495dc83b4fa674bed61050 --- ipld/car/v2/blockstore/readwrite.go | 13 ++- ipld/car/v2/blockstore/readwrite_test.go | 140 ++++++++++++++++++++++- 2 files changed, 148 insertions(+), 5 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index c65f4deb0..0829b2c7d 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -184,11 +184,11 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { if err == nil && headerInFile.CarV1Offset != 0 { if headerInFile.CarV1Offset != b.header.CarV1Offset { // Assert that the padding on file matches the given WithCarV1Padding option. - gotPadding := headerInFile.CarV1Offset - carv2.PragmaSize - carv2.HeaderSize - wantPadding := b.header.CarV1Offset - carv2.PragmaSize - carv2.HeaderSize + wantPadding := headerInFile.CarV1Offset - carv2.PragmaSize - carv2.HeaderSize + gotPadding := b.header.CarV1Offset - carv2.PragmaSize - carv2.HeaderSize return fmt.Errorf( "cannot resume from file with mismatched CARv1 offset; "+ - "`WithCarV1Padding` option must match the padding on file."+ + "`WithCarV1Padding` option must match the padding on file. "+ "Expected padding value of %v but got %v", wantPadding, gotPadding, ) } else if headerInFile.CarV1Size != 0 { @@ -330,7 +330,6 @@ func (b *ReadWrite) Finalize() error { b.mu.Lock() defer b.mu.Unlock() // TODO check if add index option is set and don't write the index then set index offset to zero. - // TODO see if folks need to continue reading from a finalized blockstore, if so return ReadOnly blockstore here. b.header = b.header.WithCarV1Size(uint64(b.carV1Writer.Position())) defer b.Close() @@ -369,3 +368,9 @@ func (b *ReadWrite) GetSize(key cid.Cid) (int, error) { return b.ReadOnly.GetSize(key) } + +func (b *ReadWrite) HashOnRead(h bool) { + b.panicIfFinalized() + + b.ReadOnly.HashOnRead(h) +} diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 191865bf3..9d254c014 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -12,6 +12,8 @@ import ( "testing" "time" + dag "github.com/ipfs/go-merkledag" + carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" "github.com/stretchr/testify/assert" @@ -26,7 +28,11 @@ import ( "github.com/ipld/go-car/v2/internal/carv1" ) -var rng = rand.New(rand.NewSource(1413)) +var ( + rng = rand.New(rand.NewSource(1413)) + oneTestBlock = dag.NewRawNode([]byte("fish")).Block + anotherTestBlock = dag.NewRawNode([]byte("barreleye")).Block +) func TestBlockstore(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) @@ -407,3 +413,135 @@ func TestBlockstoreResumptionIsSupportedOnFinalizedFile(t *testing.T) { t.Cleanup(func() { subject.Close() }) require.NoError(t, err) } + +func TestReadWritePanicsOnlyWhenFinalized(t *testing.T) { + oneTestBlockCid := oneTestBlock.Cid() + anotherTestBlockCid := anotherTestBlock.Cid() + wantRoots := []cid.Cid{oneTestBlockCid, anotherTestBlockCid} + path := filepath.Join(t.TempDir(), "readwrite-finalized-panic.car") + + subject, err := blockstore.OpenReadWrite(path, wantRoots) + require.NoError(t, err) + t.Cleanup(func() { subject.Close() }) + + require.NoError(t, subject.Put(oneTestBlock)) + require.NoError(t, subject.Put(anotherTestBlock)) + + gotBlock, err := subject.Get(oneTestBlockCid) + require.NoError(t, err) + require.Equal(t, oneTestBlock, gotBlock) + + gotSize, err := subject.GetSize(oneTestBlockCid) + require.NoError(t, err) + require.Equal(t, len(oneTestBlock.RawData()), gotSize) + + gotRoots, err := subject.Roots() + require.NoError(t, err) + require.Equal(t, wantRoots, gotRoots) + + has, err := subject.Has(oneTestBlockCid) + require.NoError(t, err) + require.True(t, has) + + subject.HashOnRead(true) + // Delete should always panic regardless of finalize + require.Panics(t, func() { subject.DeleteBlock(oneTestBlockCid) }) + + require.NoError(t, subject.Finalize()) + require.Panics(t, func() { subject.Get(oneTestBlockCid) }) + require.Panics(t, func() { subject.GetSize(anotherTestBlockCid) }) + require.Panics(t, func() { subject.Has(anotherTestBlockCid) }) + require.Panics(t, func() { subject.HashOnRead(true) }) + require.Panics(t, func() { subject.Put(oneTestBlock) }) + require.Panics(t, func() { subject.PutMany([]blocks.Block{anotherTestBlock}) }) + require.Panics(t, func() { subject.AllKeysChan(context.Background()) }) + require.Panics(t, func() { subject.DeleteBlock(oneTestBlockCid) }) +} + +func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) { + oneTestBlockCid := oneTestBlock.Cid() + anotherTestBlockCid := anotherTestBlock.Cid() + WantRoots := []cid.Cid{oneTestBlockCid, anotherTestBlockCid} + path := filepath.Join(t.TempDir(), "readwrite-with-padding.car") + + wantCarV1Padding := uint64(1413) + wantIndexPadding := uint64(1314) + subject, err := blockstore.OpenReadWrite( + path, + WantRoots, + blockstore.WithCarV1Padding(wantCarV1Padding), + blockstore.WithIndexPadding(wantIndexPadding)) + require.NoError(t, err) + t.Cleanup(func() { subject.Close() }) + require.NoError(t, subject.Put(oneTestBlock)) + require.NoError(t, subject.Put(anotherTestBlock)) + require.NoError(t, subject.Finalize()) + + // Assert CARv2 header contains right offsets. + gotCarV2, err := carv2.OpenReader(path) + t.Cleanup(func() { gotCarV2.Close() }) + require.NoError(t, err) + wantCarV1Offset := carv2.PragmaSize + carv2.HeaderSize + wantCarV1Padding + wantIndexOffset := wantCarV1Offset + gotCarV2.Header.CarV1Size + wantIndexPadding + require.Equal(t, wantCarV1Offset, gotCarV2.Header.CarV1Offset) + require.Equal(t, wantIndexOffset, gotCarV2.Header.IndexOffset) + require.NoError(t, gotCarV2.Close()) + + f, err := os.Open(path) + require.NoError(t, err) + t.Cleanup(func() { f.Close() }) + + // Assert reading CARv1 directly at offset and size is as expected. + gotCarV1, err := carv1.NewCarReader(io.NewSectionReader(f, int64(wantCarV1Offset), int64(gotCarV2.Header.CarV1Size))) + require.NoError(t, err) + require.Equal(t, WantRoots, gotCarV1.Header.Roots) + gotOneBlock, err := gotCarV1.Next() + require.NoError(t, err) + require.Equal(t, oneTestBlock, gotOneBlock) + gotAnotherBlock, err := gotCarV1.Next() + require.NoError(t, err) + require.Equal(t, anotherTestBlock, gotAnotherBlock) + _, err = gotCarV1.Next() + require.Equal(t, io.EOF, err) + + // Assert reading index directly from file is parsable and has expected CIDs. + stat, err := f.Stat() + require.NoError(t, err) + indexSize := stat.Size() - int64(wantIndexOffset) + gotIdx, err := index.ReadFrom(io.NewSectionReader(f, int64(wantIndexOffset), indexSize)) + require.NoError(t, err) + _, err = gotIdx.Get(oneTestBlockCid) + require.NoError(t, err) + _, err = gotIdx.Get(anotherTestBlockCid) + require.NoError(t, err) +} + +func TestReadWriteResumptionFromNonV2FileIsError(t *testing.T) { + subject, err := blockstore.OpenReadWrite("../testdata/sample-rootless-v42.car", []cid.Cid{}) + require.EqualError(t, err, "cannot resume on CAR file with version 42") + require.Nil(t, subject) +} + +func TestReadWriteResumptionFromFileWithDifferentCarV1PaddingIsError(t *testing.T) { + oneTestBlockCid := oneTestBlock.Cid() + WantRoots := []cid.Cid{oneTestBlockCid} + path := filepath.Join(t.TempDir(), "readwrite-resume-with-padding.car") + + subject, err := blockstore.OpenReadWrite( + path, + WantRoots, + blockstore.WithCarV1Padding(1413)) + require.NoError(t, err) + t.Cleanup(func() { subject.Close() }) + require.NoError(t, subject.Put(oneTestBlock)) + require.NoError(t, subject.Finalize()) + + resumingSubject, err := blockstore.OpenReadWrite( + path, + WantRoots, + blockstore.WithCarV1Padding(1314)) + require.EqualError(t, err, "cannot resume from file with mismatched CARv1 offset; "+ + "`WithCarV1Padding` option must match the padding on file. "+ + "Expected padding value of 1413 but got 1314") + require.Nil(t, resumingSubject) +} From 2a0eb4f6ab30219e5dfda50f3f1c78fb960021a6 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 15 Jul 2021 17:20:08 +0100 Subject: [PATCH 3487/3817] Use consistent import and bot CID versions in testing Use both CID v0 and v1 in testing `ReadWrite` blockstore. Use consistent import package name `merkledag` across tests. This commit was moved from ipld/go-car@25dc0003fa3ab4240f6e67ba5260ed5d269c67b6 --- ipld/car/car.go | 4 +-- ipld/car/car_test.go | 32 +++++++++---------- ipld/car/v2/blockstore/readwrite_test.go | 40 ++++++++++++------------ ipld/car/v2/internal/carv1/car.go | 4 +-- ipld/car/v2/internal/carv1/car_test.go | 18 +++++------ ipld/car/v2/writer_test.go | 16 +++++----- 6 files changed, 57 insertions(+), 57 deletions(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index 267237f28..51826706d 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -10,7 +10,7 @@ import ( cid "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" format "github.com/ipfs/go-ipld-format" - dag "github.com/ipfs/go-merkledag" + "github.com/ipfs/go-merkledag" util "github.com/ipld/go-car/util" ) @@ -58,7 +58,7 @@ func WriteCarWithWalker(ctx context.Context, ds format.NodeGetter, roots []cid.C cw := &carWriter{ds: ds, w: w, walk: walk} seen := cid.NewSet() for _, r := range roots { - if err := dag.Walk(ctx, cw.enumGetLinks, r, seen.Visit); err != nil { + if err := merkledag.Walk(ctx, cw.enumGetLinks, r, seen.Visit); err != nil { return err } } diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 13f96887b..137edd629 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -8,9 +8,9 @@ import ( "strings" "testing" - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" - dag "github.com/ipfs/go-merkledag" + "github.com/ipfs/go-merkledag" dstest "github.com/ipfs/go-merkledag/test" basicnode "github.com/ipld/go-ipld-prime/node/basic" "github.com/ipld/go-ipld-prime/traversal/selector" @@ -28,18 +28,18 @@ func assertAddNodes(t *testing.T, ds format.DAGService, nds ...format.Node) { func TestRoundtrip(t *testing.T) { dserv := dstest.Mock() - a := dag.NewRawNode([]byte("aaaa")) - b := dag.NewRawNode([]byte("bbbb")) - c := dag.NewRawNode([]byte("cccc")) + a := merkledag.NewRawNode([]byte("aaaa")) + b := merkledag.NewRawNode([]byte("bbbb")) + c := merkledag.NewRawNode([]byte("cccc")) - nd1 := &dag.ProtoNode{} + nd1 := &merkledag.ProtoNode{} nd1.AddNodeLink("cat", a) - nd2 := &dag.ProtoNode{} + nd2 := &merkledag.ProtoNode{} nd2.AddNodeLink("first", nd1) nd2.AddNodeLink("dog", b) - nd3 := &dag.ProtoNode{} + nd3 := &merkledag.ProtoNode{} nd3.AddNodeLink("second", nd2) nd3.AddNodeLink("bear", c) @@ -80,20 +80,20 @@ func TestRoundtrip(t *testing.T) { func TestRoundtripSelective(t *testing.T) { sourceBserv := dstest.Bserv() sourceBs := sourceBserv.Blockstore() - dserv := dag.NewDAGService(sourceBserv) - a := dag.NewRawNode([]byte("aaaa")) - b := dag.NewRawNode([]byte("bbbb")) - c := dag.NewRawNode([]byte("cccc")) + dserv := merkledag.NewDAGService(sourceBserv) + a := merkledag.NewRawNode([]byte("aaaa")) + b := merkledag.NewRawNode([]byte("bbbb")) + c := merkledag.NewRawNode([]byte("cccc")) - nd1 := &dag.ProtoNode{} + nd1 := &merkledag.ProtoNode{} nd1.AddNodeLink("cat", a) - nd2 := &dag.ProtoNode{} + nd2 := &merkledag.ProtoNode{} nd2.AddNodeLink("first", nd1) nd2.AddNodeLink("dog", b) nd2.AddNodeLink("repeat", nd1) - nd3 := &dag.ProtoNode{} + nd3 := &merkledag.ProtoNode{} nd3.AddNodeLink("second", nd2) nd3.AddNodeLink("bear", c) @@ -106,7 +106,7 @@ func TestRoundtripSelective(t *testing.T) { // this selector starts at n3, and traverses a link at index 1 (nd2, the second link, zero indexed) // it then recursively traverses all of its children // the only node skipped is 'c' -- link at index 0 immediately below nd3 - // the purpose is simply to show we are not writing the entire dag underneath + // the purpose is simply to show we are not writing the entire merkledag underneath // nd3 selector := ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) { efsb.Insert("Links", diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 9d254c014..acc46a8d0 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - dag "github.com/ipfs/go-merkledag" + "github.com/ipfs/go-merkledag" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" @@ -29,9 +29,9 @@ import ( ) var ( - rng = rand.New(rand.NewSource(1413)) - oneTestBlock = dag.NewRawNode([]byte("fish")).Block - anotherTestBlock = dag.NewRawNode([]byte("barreleye")).Block + rng = rand.New(rand.NewSource(1413)) + oneTestBlockWithCidV1 = merkledag.NewRawNode([]byte("fish")).Block + anotherTestBlockWithCidV0 = blocks.NewBlock([]byte("barreleye")) ) func TestBlockstore(t *testing.T) { @@ -415,8 +415,8 @@ func TestBlockstoreResumptionIsSupportedOnFinalizedFile(t *testing.T) { } func TestReadWritePanicsOnlyWhenFinalized(t *testing.T) { - oneTestBlockCid := oneTestBlock.Cid() - anotherTestBlockCid := anotherTestBlock.Cid() + oneTestBlockCid := oneTestBlockWithCidV1.Cid() + anotherTestBlockCid := anotherTestBlockWithCidV0.Cid() wantRoots := []cid.Cid{oneTestBlockCid, anotherTestBlockCid} path := filepath.Join(t.TempDir(), "readwrite-finalized-panic.car") @@ -424,16 +424,16 @@ func TestReadWritePanicsOnlyWhenFinalized(t *testing.T) { require.NoError(t, err) t.Cleanup(func() { subject.Close() }) - require.NoError(t, subject.Put(oneTestBlock)) - require.NoError(t, subject.Put(anotherTestBlock)) + require.NoError(t, subject.Put(oneTestBlockWithCidV1)) + require.NoError(t, subject.Put(anotherTestBlockWithCidV0)) gotBlock, err := subject.Get(oneTestBlockCid) require.NoError(t, err) - require.Equal(t, oneTestBlock, gotBlock) + require.Equal(t, oneTestBlockWithCidV1, gotBlock) gotSize, err := subject.GetSize(oneTestBlockCid) require.NoError(t, err) - require.Equal(t, len(oneTestBlock.RawData()), gotSize) + require.Equal(t, len(oneTestBlockWithCidV1.RawData()), gotSize) gotRoots, err := subject.Roots() require.NoError(t, err) @@ -452,15 +452,15 @@ func TestReadWritePanicsOnlyWhenFinalized(t *testing.T) { require.Panics(t, func() { subject.GetSize(anotherTestBlockCid) }) require.Panics(t, func() { subject.Has(anotherTestBlockCid) }) require.Panics(t, func() { subject.HashOnRead(true) }) - require.Panics(t, func() { subject.Put(oneTestBlock) }) - require.Panics(t, func() { subject.PutMany([]blocks.Block{anotherTestBlock}) }) + require.Panics(t, func() { subject.Put(oneTestBlockWithCidV1) }) + require.Panics(t, func() { subject.PutMany([]blocks.Block{anotherTestBlockWithCidV0}) }) require.Panics(t, func() { subject.AllKeysChan(context.Background()) }) require.Panics(t, func() { subject.DeleteBlock(oneTestBlockCid) }) } func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) { - oneTestBlockCid := oneTestBlock.Cid() - anotherTestBlockCid := anotherTestBlock.Cid() + oneTestBlockCid := oneTestBlockWithCidV1.Cid() + anotherTestBlockCid := anotherTestBlockWithCidV0.Cid() WantRoots := []cid.Cid{oneTestBlockCid, anotherTestBlockCid} path := filepath.Join(t.TempDir(), "readwrite-with-padding.car") @@ -473,8 +473,8 @@ func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) { blockstore.WithIndexPadding(wantIndexPadding)) require.NoError(t, err) t.Cleanup(func() { subject.Close() }) - require.NoError(t, subject.Put(oneTestBlock)) - require.NoError(t, subject.Put(anotherTestBlock)) + require.NoError(t, subject.Put(oneTestBlockWithCidV1)) + require.NoError(t, subject.Put(anotherTestBlockWithCidV0)) require.NoError(t, subject.Finalize()) // Assert CARv2 header contains right offsets. @@ -497,10 +497,10 @@ func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) { require.Equal(t, WantRoots, gotCarV1.Header.Roots) gotOneBlock, err := gotCarV1.Next() require.NoError(t, err) - require.Equal(t, oneTestBlock, gotOneBlock) + require.Equal(t, oneTestBlockWithCidV1, gotOneBlock) gotAnotherBlock, err := gotCarV1.Next() require.NoError(t, err) - require.Equal(t, anotherTestBlock, gotAnotherBlock) + require.Equal(t, anotherTestBlockWithCidV0, gotAnotherBlock) _, err = gotCarV1.Next() require.Equal(t, io.EOF, err) @@ -523,7 +523,7 @@ func TestReadWriteResumptionFromNonV2FileIsError(t *testing.T) { } func TestReadWriteResumptionFromFileWithDifferentCarV1PaddingIsError(t *testing.T) { - oneTestBlockCid := oneTestBlock.Cid() + oneTestBlockCid := oneTestBlockWithCidV1.Cid() WantRoots := []cid.Cid{oneTestBlockCid} path := filepath.Join(t.TempDir(), "readwrite-resume-with-padding.car") @@ -533,7 +533,7 @@ func TestReadWriteResumptionFromFileWithDifferentCarV1PaddingIsError(t *testing. blockstore.WithCarV1Padding(1413)) require.NoError(t, err) t.Cleanup(func() { subject.Close() }) - require.NoError(t, subject.Put(oneTestBlock)) + require.NoError(t, subject.Put(oneTestBlockWithCidV1)) require.NoError(t, subject.Finalize()) resumingSubject, err := blockstore.OpenReadWrite( diff --git a/ipld/car/v2/internal/carv1/car.go b/ipld/car/v2/internal/carv1/car.go index ab5753bec..48b4f3eeb 100644 --- a/ipld/car/v2/internal/carv1/car.go +++ b/ipld/car/v2/internal/carv1/car.go @@ -11,7 +11,7 @@ import ( cid "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" format "github.com/ipfs/go-ipld-format" - dag "github.com/ipfs/go-merkledag" + "github.com/ipfs/go-merkledag" ) func init() { @@ -49,7 +49,7 @@ func WriteCar(ctx context.Context, ds format.NodeGetter, roots []cid.Cid, w io.W cw := &carWriter{ds: ds, w: w} seen := cid.NewSet() for _, r := range roots { - if err := dag.Walk(ctx, cw.enumGetLinks, r, seen.Visit); err != nil { + if err := merkledag.Walk(ctx, cw.enumGetLinks, r, seen.Visit); err != nil { return err } } diff --git a/ipld/car/v2/internal/carv1/car_test.go b/ipld/car/v2/internal/carv1/car_test.go index e5dd680c8..70ce7d859 100644 --- a/ipld/car/v2/internal/carv1/car_test.go +++ b/ipld/car/v2/internal/carv1/car_test.go @@ -12,7 +12,7 @@ import ( cid "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" - dag "github.com/ipfs/go-merkledag" + "github.com/ipfs/go-merkledag" dstest "github.com/ipfs/go-merkledag/test" ) @@ -26,18 +26,18 @@ func assertAddNodes(t *testing.T, ds format.DAGService, nds ...format.Node) { func TestRoundtrip(t *testing.T) { dserv := dstest.Mock() - a := dag.NewRawNode([]byte("aaaa")) - b := dag.NewRawNode([]byte("bbbb")) - c := dag.NewRawNode([]byte("cccc")) + a := merkledag.NewRawNode([]byte("aaaa")) + b := merkledag.NewRawNode([]byte("bbbb")) + c := merkledag.NewRawNode([]byte("cccc")) - nd1 := &dag.ProtoNode{} + nd1 := &merkledag.ProtoNode{} nd1.AddNodeLink("cat", a) - nd2 := &dag.ProtoNode{} + nd2 := &merkledag.ProtoNode{} nd2.AddNodeLink("first", nd1) nd2.AddNodeLink("dog", b) - nd3 := &dag.ProtoNode{} + nd3 := &merkledag.ProtoNode{} nd3.AddNodeLink("second", nd2) nd3.AddNodeLink("bear", c) @@ -233,8 +233,8 @@ func TestBadHeaders(t *testing.T) { } func TestCarHeaderMatchess(t *testing.T) { - oneCid := dag.NewRawNode([]byte("fish")).Cid() - anotherCid := dag.NewRawNode([]byte("lobster")).Cid() + oneCid := merkledag.NewRawNode([]byte("fish")).Cid() + anotherCid := merkledag.NewRawNode([]byte("lobster")).Cid() tests := []struct { name string one CarHeader diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index 06d0ff42d..633fc4626 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -7,7 +7,7 @@ import ( "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" - dag "github.com/ipfs/go-merkledag" + "github.com/ipfs/go-merkledag" dstest "github.com/ipfs/go-merkledag/test" "github.com/stretchr/testify/assert" ) @@ -66,25 +66,25 @@ func TestNewWriter(t *testing.T) { func generateRootCid(t *testing.T, adder format.NodeAdder) []cid.Cid { // TODO convert this into a utility testing lib that takes an rng and generates a random DAG with some threshold for depth/breadth. - this := dag.NewRawNode([]byte("fish")) - that := dag.NewRawNode([]byte("lobster")) - other := dag.NewRawNode([]byte("🌊")) + this := merkledag.NewRawNode([]byte("fish")) + that := merkledag.NewRawNode([]byte("lobster")) + other := merkledag.NewRawNode([]byte("🌊")) - one := &dag.ProtoNode{} + one := &merkledag.ProtoNode{} assertAddNodeLink(t, one, this, "fishmonger") - another := &dag.ProtoNode{} + another := &merkledag.ProtoNode{} assertAddNodeLink(t, another, one, "barreleye") assertAddNodeLink(t, another, that, "ðŸ¡") - andAnother := &dag.ProtoNode{} + andAnother := &merkledag.ProtoNode{} assertAddNodeLink(t, andAnother, another, "ðŸ¤") assertAddNodes(t, adder, this, that, other, one, another, andAnother) return []cid.Cid{andAnother.Cid()} } -func assertAddNodeLink(t *testing.T, pn *dag.ProtoNode, fn format.Node, name string) { +func assertAddNodeLink(t *testing.T, pn *merkledag.ProtoNode, fn format.Node, name string) { assert.NoError(t, pn.AddNodeLink(name, fn)) } From 7f6a1c85a640e5e52c6819f3b718fa2fcc84898b Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 15 Jul 2021 22:02:22 +0100 Subject: [PATCH 3488/3817] Fix index not found error not being propagated Fix an issue where single width index not finding a given key returns `0` offset instead of `index.ErrNotFound`. Reflect the changes in blockstore to return appropriate IPFS blockstore error. Add tests that asserts error types both in index and blockstore packages. Remove redundant TODOs. Relates to: - https://github.com/ipld/go-car/pull/158 This commit was moved from ipld/go-car@7751d8b965a57d9be40990c140130b799bf2a01c --- ipld/car/v2/blockstore/readonly.go | 9 +++--- ipld/car/v2/blockstore/readonly_test.go | 13 +++++++++ ipld/car/v2/blockstore/readwrite_test.go | 14 +++++++++ ipld/car/v2/index/indexsorted.go | 12 ++++---- ipld/car/v2/index/indexsorted_test.go | 36 ++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 ipld/car/v2/index/indexsorted_test.go diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 658587046..cf799fe7a 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -165,14 +165,15 @@ func (b *ReadOnly) Get(key cid.Cid) (blocks.Block, error) { defer b.mu.RUnlock() offset, err := b.idx.Get(key) - // TODO Improve error handling; not all errors mean NotFound. if err != nil { - return nil, blockstore.ErrNotFound + if err == index.ErrNotFound { + err = blockstore.ErrNotFound + } + return nil, err } entry, data, err := b.readBlock(int64(offset)) if err != nil { - // TODO Improve error handling; not all errors mean NotFound. - return nil, blockstore.ErrNotFound + return nil, err } if !bytes.Equal(key.Hash(), entry.Hash()) { return nil, blockstore.ErrNotFound diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 499748172..58a58d6a9 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -2,6 +2,8 @@ package blockstore import ( "context" + blockstore "github.com/ipfs/go-ipfs-blockstore" + "github.com/ipfs/go-merkledag" "io" "os" "testing" @@ -16,6 +18,17 @@ import ( "github.com/stretchr/testify/require" ) +func TestReadOnlyGetReturnsBlockstoreNotFoundWhenCidDoesNotExist(t *testing.T) { + subject, err := OpenReadOnly("../testdata/sample-v1.car") + require.NoError(t, err) + nonExistingKey := merkledag.NewRawNode([]byte("lobstermuncher")).Block.Cid() + + // Assert blockstore API returns blockstore.ErrNotFound + gotBlock, err := subject.Get(nonExistingKey) + require.Equal(t, blockstore.ErrNotFound, err) + require.Nil(t, gotBlock) +} + func TestReadOnly(t *testing.T) { tests := []struct { name string diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index acc46a8d0..892b4e052 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -21,6 +21,7 @@ import ( "github.com/multiformats/go-multihash" "github.com/stretchr/testify/require" + ipfsblockstore "github.com/ipfs/go-ipfs-blockstore" "github.com/ipld/go-car/v2/blockstore" blocks "github.com/ipfs/go-block-format" @@ -34,6 +35,19 @@ var ( anotherTestBlockWithCidV0 = blocks.NewBlock([]byte("barreleye")) ) +func TestReadWriteGetReturnsBlockstoreNotFoundWhenCidDoesNotExist(t *testing.T) { + path := filepath.Join(t.TempDir(), "readwrite-err-not-found.car") + subject, err := blockstore.OpenReadWrite(path, []cid.Cid{}) + t.Cleanup(func() { subject.Close() }) + require.NoError(t, err) + nonExistingKey := merkledag.NewRawNode([]byte("undadasea")).Block.Cid() + + // Assert blockstore API returns blockstore.ErrNotFound + gotBlock, err := subject.Get(nonExistingKey) + require.Equal(t, ipfsblockstore.ErrNotFound, err) + require.Nil(t, gotBlock) +} + func TestBlockstore(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index a8f401aef..65446f665 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -82,20 +82,20 @@ func (s *singleWidthIndex) Get(c cid.Cid) (uint64, error) { if err != nil { return 0, err } - return s.get(d.Digest), nil + return s.get(d.Digest) } -func (s *singleWidthIndex) get(d []byte) uint64 { +func (s *singleWidthIndex) get(d []byte) (uint64, error) { idx := sort.Search(int(s.len), func(i int) bool { return s.Less(i, d) }) if uint64(idx) == s.len { - return 0 + return 0, ErrNotFound } if !bytes.Equal(d[:], s.index[idx*int(s.width):(idx+1)*int(s.width)-8]) { - return 0 + return 0, ErrNotFound } - return binary.LittleEndian.Uint64(s.index[(idx+1)*int(s.width)-8 : (idx+1)*int(s.width)]) + return binary.LittleEndian.Uint64(s.index[(idx+1)*int(s.width)-8 : (idx+1)*int(s.width)]), nil } func (s *singleWidthIndex) Load(items []Record) error { @@ -121,7 +121,7 @@ func (m *multiWidthIndex) Get(c cid.Cid) (uint64, error) { return 0, err } if s, ok := (*m)[uint32(len(d.Digest)+8)]; ok { - return s.get(d.Digest), nil + return s.get(d.Digest) } return 0, ErrNotFound } diff --git a/ipld/car/v2/index/indexsorted_test.go b/ipld/car/v2/index/indexsorted_test.go new file mode 100644 index 000000000..c491bedf5 --- /dev/null +++ b/ipld/car/v2/index/indexsorted_test.go @@ -0,0 +1,36 @@ +package index + +import ( + "github.com/ipfs/go-merkledag" + "github.com/multiformats/go-multicodec" + "github.com/stretchr/testify/require" + "testing" +) + +func TestSortedIndexCodec(t *testing.T) { + require.Equal(t, multicodec.CarIndexSorted, newSorted().Codec()) +} + +func TestSortedIndex_GetReturnsNotFoundWhenCidDoesNotExist(t *testing.T) { + nonExistingKey := merkledag.NewRawNode([]byte("lobstermuncher")).Block.Cid() + tests := []struct { + name string + subject Index + }{ + { + "SingleSorted", + newSingleSorted(), + }, + { + "Sorted", + newSorted(), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotOffset, err := tt.subject.Get(nonExistingKey) + require.Equal(t, ErrNotFound, err) + require.Equal(t, uint64(0), gotOffset) + }) + } +} From bf399a86fa1333e1040d8fb92c44cd4c3e34277d Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 15 Jul 2021 22:21:35 +0100 Subject: [PATCH 3489/3817] Remove duplicate test CARv1 file Remove the duplicate test CARv1 file that existed both in `testdata` and `blockstore/testdata` in favour of the one in root. Reflect change in tests. Duplicity checked using `md5` digest: ```sh $ md5 testdata/sample-v1.car MD5 (testdata/sample-v1.car) = 14fcffc271e50bc56cfce744d462a9bd $ md5 blockstore/testdata/test.car MD5 (blockstore/testdata/test.car) = 14fcffc271e50bc56cfce744d462a9bd ``` This commit was moved from ipld/go-car@fd226df91b98c441bd98f703814d060317af519e --- ipld/car/v2/blockstore/readonly_test.go | 5 ----- ipld/car/v2/blockstore/readwrite_test.go | 6 +++--- ipld/car/v2/blockstore/testdata/test.car | Bin 479907 -> 0 bytes 3 files changed, 3 insertions(+), 8 deletions(-) delete mode 100644 ipld/car/v2/blockstore/testdata/test.car diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 58a58d6a9..97ed73091 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -37,11 +37,6 @@ func TestReadOnly(t *testing.T) { }{ { "OpenedWithCarV1", - "testdata/test.car", - newReaderFromV1File(t, "testdata/test.car"), - }, - { - "OpenedWithAnotherCarV1", "../testdata/sample-v1.car", newReaderFromV1File(t, "../testdata/sample-v1.car"), }, diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 892b4e052..884edca75 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -52,7 +52,7 @@ func TestBlockstore(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - f, err := os.Open("testdata/test.car") + f, err := os.Open("../testdata/sample-v1.car") require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, f.Close()) }) r, err := carv1.NewCarReader(f) @@ -270,7 +270,7 @@ func (b bufferReaderAt) ReadAt(p []byte, off int64) (int, error) { } func TestBlockstoreNullPadding(t *testing.T) { - paddedV1, err := ioutil.ReadFile("testdata/test.car") + paddedV1, err := ioutil.ReadFile("../testdata/sample-v1.car") require.NoError(t, err) // A sample null-padded CARv1 file. @@ -301,7 +301,7 @@ func TestBlockstoreNullPadding(t *testing.T) { } func TestBlockstoreResumption(t *testing.T) { - v1f, err := os.Open("testdata/test.car") + v1f, err := os.Open("../testdata/sample-v1.car") require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, v1f.Close()) }) r, err := carv1.NewCarReader(v1f) diff --git a/ipld/car/v2/blockstore/testdata/test.car b/ipld/car/v2/blockstore/testdata/test.car deleted file mode 100644 index 47a61c8c2a7def9bafcc252d3a1a4d12529615f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 479907 zcmdSBRZtz;(yooW>%`q5XmHoy?ry=|-QC@TySuwXfZ!6`JxGAyhwQW0D%N+C|GW6B z&f>a9O{&Ly-nU1O9+NxI*xts*(ZO$tT8#n-*e+)mgz#PvS&t~k82RzAB9>MjOd+o_4OvAu(tjWzIhi2rX3qJ)6ff&J9kP7mV^XFOuvNS(Jd z{qMJ#FJ(8=_wi8BHQ}u=;<6T|Q=!iO zNagh)J~5HNCL!IMHV1)65=PfX6{g=_2mH!-7ERt++QP$L zF9`?O)_d_m#~buJ~qfQNd<{?*_Wvv1wjvydgi3JwF;2fgnvgXnOaj(8EsnL_~UKh{jg9!E! zc+84I=z?-6Rn5l;4CEwW$VA)=+TrX@(`9fOd&f&m8=xi7@U5BpuZN>~jE^d+nv==6Y~5oF#z9g3{DpJZ9+ zqM&7n$6;!v9Wh$eR%gyd5|mWF1?0UjUad|(!F~szc|(H7>(*YkZsVb5g_^aIQqtT3 z>cGB*FMJcYB@ls{NOg*`EY6~`1oxFO^caW zb9d>NuU_afcpyN=P7H`+JOPVCWllW)JZpV3eKbT#AW<(7sMAE zK>YBc_O2;aFRm35{t<7uWR;p;fkY;ifZ*EjZ&eiFk?u^mJ-f8NrxRqaYZ{?0uZ*FWo zn^7jViqYO&QbyBAS{LLQZ1W|iI(m1Vnx2`cgiq(Av18j}s$TjnE_qiMQ+b#~?rXCm zgP#zVZF#(W2tUTKZ#Jz1%C|t()+?gr1}T~=%8^=MXQNdPD%VV5#9Vk3vjC~To4a8w ztT=1gvu1Vq(a-@|4NgW5ZU7-et0g(BrkCc!`p`=dQ$|00*B$Vj_HC~%SR5x>;%sh2 z>S?S?!1XewedO68u!GBRHu3ESDGG}+NV;{%p?J3V^� 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 Tx_{@>Ah$cBb;X>)D9fct)9`U;$XV@y4$E$ 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

hRd> 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

vHSrHL`4Y!))mrU|+My=J*?NCLvdFH?W^2AuF;5 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

hRd> zxIQPp`+1js7cr;A2G9Sz#Cb*_ZChlr4a28WU<}i_e zuBDVaJs))P(_~?NiyW>e0sKw|^8J9ysCp2xuN>~eXfYP}L34T0&Vl@^Y>%3Ak2Gc2 z=2WYe5D?fq2^Q{$PoR`I^N>b4&(CSKn(E<7H*fFteEu3mG(ELNVuKBMD)3?Hzi;RA z0KgT9_q}C4#xxE125~RlKd(fbZcWBo(;Ny0G{(BW#;AQLH}OsrS0;jtccn82Pi`8m zEVJKD9UY>B$9?dWF8`W1|0ti&FbF%@J+DSS*eufMZM#DeoKiQ_{s0Lx8W~@KF;K$S7sZKXveAYNqj>VEwyjh zygOO+_X9ij@pQ*bD0J)s$?U!zguw1HfBgabp*jpTv(UqwVa5Q9f*2)ETS{+U@ z(91|c9d%iy-5|-dTY~SupRGgCbbb;L)@nP3bQs(wvRJUogv;<93B(NK;WogNHfyx?;$PWbpeo}586beLoy^){uPPtQkWyUmd z$P0x9GS9s>RIS~?8t659XIsut5g!zI3esTO)QVSY0pzL?RS^tp7o2;#rMVYqy|@*e zfHvkiYLHiGQIL?JY%H@7m`u!Q--ErdL68Fk2>QifGy2$u5FWF68d%;$_o}+ro(L8;f zRQlq(ABEhnECtxP{tK?lY5d0~_5Gl(SFi}^g3m4pz$y^*@%E83Zv?g%A(46jxTayS zK7AnO*@ME}P-uVA)V0<3)Pf;5(p!REzqxxf=6nUn;5|-`IX@a^sSo17!$JpG^R2QK}`xx z^{s*JMhXeM>udPHaGON`n6^Yyph50n-(ocNRFGySSY%w`;&C;}3tWx2z4UA> zC(;!N_9`ZAJ!xG)-RtIwUsxGxlt~$sI}V{$Z)#vkHHs`M{0r0GG%N%OGIu`?lZj29 zf3`$-Ji{ss+T|~KnNQvVrueu&JuXcTi7nrF^<|ulh6{qI9=-z z=)o<%q>sL2=1SOT|H`s2yq;#oJF5EC=d>Q>Q=IhGv9{GttrrT`0mr7#fXOm136)>5IZ;*)F#M_@$iNDNk95?mDo+;AV^ku7jA=+UawDfSY|N3OeBf2{0HWD@7 zx3fO~OVTQ_JSBv7Y=$#McEod(Sq{+Fi!S84MOeTVJrKF0b4wp)B z{3ef03I_DC!c%0MoVLgdr%0HkhW~pQ97Kw^l7LqT-FCvR&uuOVBfzL zlh;5$s7o}Y={-9pt^R`XHlyxO?YkP- zjT(^9R%uOhZrSs2Y6nWx)5V{vKQ06{5`~= z>bfv67RUT}avLGuIriNh+!5YA@m_CKAL-fD+$SBP0(=YCO&Ato_1$P4JBRbg&$Wt%a9Uo};AOy= zkLW9zov-(9_7(RL5yCr@Y1^;RjYOw@$bupO8!DVS^ z3wjK42M;AL^jkYb9}ck;Yh*Oi{z_MDq;nqDnjZ2A)6ez$i5wlXs!uksGA*j5dvN$Z z%QuBH>z1KT%w#BTYMgZ;(YQnQ!^YmIp+{PQnBU@T(ZXB00sgGDt)l(UoMO?Okh;DE z(IK|4er36?Xv{E5oYjsDpHvxFF`bEkFwbs)n$ncKBi0=Cup0ljBJI z*_ckYK#r_*PAVxXgcu7wJ7?_BI1bZ|E?^Xl2XkH2Y4^E%f{eJp&K;@Jxq<{C3*yn+ zAcL$Y_0h-Gr#<=+yYsMKkIFDC$`#ut41*0mf|^sFi3G)HIhl-QgwV07U5RaS+jCVg zjT`dIvM`tI4>UUi|!zZX`8mL$1=8 z2uRgaaBac_7`k--bu!_0(IFe+vimP9f=QYZTpQbmQW@5VqYzT3>RW08A#2Q}K3sSd zpX}2bSrzfzHd;=1`pA(*O!yr|wFOPJ#J|6Wshfh){bv|ouzXYNxH4oaQkq~NU4heg zF54)FraK(TOA%@}3out?z@hb@itG<^zXRp_HU{Kc+6kGW-*=WAXW&P&@C z&LU_h0gv3VMDAtxVd=llHyF17-}pz_WkVc=;>EEvy%9F@cU>^if?}dV&D?(XI3MdBYt7nYA+ly-rrcsZoXiR zzUk9bD(*mPJ2$r~Q6#B-eIhlAd z&zG+{_)-(o(;5?ExiqpmDL_q};XxfytFoL2+!BU}bGH)g%LGGV_Fj<@stcD6DVCE|)zLgX zlIf~j0z^sQMndT!Vqz03irvU|_a6rruVO^&IuhM2dO72~RU06TNSfji#(294L4aX3 zh({ns1A^BV93lF59`!Qn1&v06ahLmmI&vh`Zt#FDGGbi0bQ@o7cEsrWJnLb!(+Cn4 zPLnluPN^I!h|o>fMer!+cWf;U5R)t)^TNKK6O$mC5rgpP%jWuh>y#F)O**V=js|-; zEluRNBBd#2K>Ae0U!gz=1ZW0Ww*ULWFkevc8MD3&xUhr-xTLjc(16VW@}^N5-Rss` zF)!=LGhk?)Nj!!QYH zhSf2gYdD`mH0rbfRj$ZPc{QZ}KbN?jVcT>nrlAoeregry6r&CD3fvsav2bUA?UbFu z8k+Hc+aWOwv@pcFXK2!)KgQB@A0fqS`W{|L- z#EjbD^c8EGE0@OTGTV87WUyAK)56#Mkn7VM_of#DN|7}nc}S5`F-x(yo_zD(4b(LG zguj9xkzQBoVq*xr*&2LmRgV%&Z3%y6GZtn1>z39*bhc!Li_LSUpr z4MmmU=D}U7G*0SAlv)}yF&C62@mS4{UlIRyurln@uWVWTB2w|N2y{2=vdA`dX{~e) zhPP8mnXf-4#hZH5Y$@=Rr(l|7#VJl3)@-AoRP6OA1y^P8n#BoqR4TJygu6GW$7_dM za0WC@34UP+0As1AA~d8I313LZP6DO_1iZ~I4jdFwoFDVUM~efowTJ`zXBkjz8XSn; zRda$rh4y=H{s_OtDB#gUb8d>{L4!y^zH{NSaTHX{4eq<3<|z_B(umKEsiu-C?KcW# ztp?$oCq81*ROKQ~-4j$jrZwvqzx;^FxV5(t7ez&P8 zcOcsisWJhBr6v1BaN8h8F>AJRXH@`&pMO~T?_(<+z}C+h2_JVl!?9=~;vf)^T9%sa z(EIQj3E6Ln#Y1t06Whk%U4;-)1R|rp$V7?ty33%P z2#;xYUJe$7^-HVA*S^-rPyi>-iX)2LJpeVQ$fxm}c>y>}1A z2&>OmCW&>9r1XZPb4jm}!Qxn6-#;b)B0e2W*y#km3JxQi(2XN^JK;jlI=Ee*IN1kn z=r3JcGxnN#*)2OYIfCk-ixQXZ1+`@C)R6+Pm7E|F?1YxtL*U4^ZlK$fX(noL5F$te zL}qvLQ)-2SXT!7GbLczyuSw0R$*PuWnqZz`sf$hEqOaF*kx%&cRJHikq`3CC-$aCv zW>GaLnI+X?aT!PV9C2_vaqHyrq);(R;42hx#vDUohsTh(d&8E@cA2t`FLr(}=Y-fB zSv+Yoi6YqOb`Y_UtsB}kOcfPm(x1I$WDpGtVgQE5R|xyg0da~~Uiv>GWIm+o9V8`5 zu+5J{3DhRE?o#i~<*DhF`yMIQ9JJX>UND+BfrZA8rfrwj9z0WqN2-aWj%zcyCbHsn zbm?%h)VRKMeNy9r)FZ%QA5Sa&DpPei+e0W{);d|5a3@tP%)CxxfvT4_R?r$TF3;E3 zBmq1BTJD?lhbE*p1QSWKdALjwjy+e(Ta7*1|`31W%-o{T?zKYtTs6(u_2?2o~sAGHpX6Q=EJyZ{{SG${N1M!s+ zJry{Pf0kmBL`t9a6eYm_SHCaBum-S|>bgVd**FmHL)M8dSQ-%5nXJj8r4TQn06!yt zzp=$v+1B6)y$G#y)33aUMItZ(YCI+<9hiW!%Dq;f{tJ9#!}fd#-!vwX_$-{@l!zCP z#m*ee`~^*A-~8(Rg>bg@Z?^s^^nWGW4G(+Ac3ekb%@|ehn-SSBhS-`J6g_Ufxp$H< zhreB}zebXrqizWajoOHS=i7vLKu;%e$d#E~n`u|~ny^jiohr%>F$^$EC(&6w7*|;Gv*sxa131!ZQkQk%_#oPde z3Cba563H-89FEX9uyq(P$%6k}3!AM01+n7IrdNUR6OU376sntGlM#!uGHP^`v|prl=vg>mE;;^U*AmUEud3oY}>kq6wT)#t2vs?BRyFeIsdSw zWo4jS)byoQGU9~wjXK@WDi~5IvoDAe=yx{w!3s0Q{|>KAfR%wVSLi!q(o4#UJlL^` z3uFxzHG^28Z*i0S^kn@8V5^l6{R3MpiPm3HJ9O7;ySXBUD3Ag{DHYt=;T=TgG%AZ) z>lL#R;DfEA_)tJn@b;E~c`J#C@KqM&eEf>AR^tuS0G8T7_BUPVHEesj{ zi^%;1sH!mNF0u+HoewK7b zvHk|cL=n(I^Pj}BieR(gBI3yTZSAO8)7%n)sZUXTc60WOIUY5JkJSnOfjdv8DIPNu zmPfHSl1L%6)VV*Rb#*zR^m#9y>BZsUu*_qsJQYKhU7I?avbEKo_wGgk={2Ko)bjxg z_5QvdB&&SzQ*Tq9S4p3>rJtlx1#<8KrP4tR9i4wyHUYb{5x;aCP_G+QB%Oi6X^OK? zVctD<+ybrsF8GCQ_bwwQnuXIH5I5bC*JZX$z7<1rQRN0+b&}#T%ddr4fA@01yb@s_ zu&)?gmp(T#%n+kN)MRKuT{NX8E7Q4JSBRJElB$IkcZ*sMqWt$X1WlRup<^2^4GZJG z{O5KurEncsW5+qbmoVg#6md6MU)hdjt%$H6E~%o1)n_jUh0hZ-pfV^cxB4ri8lM!u zE-y=uJukTWup{@;P59bT0h7i`>3_$?sT>CVOliCL#1c!uo;SoiQf%ny=zCS|duHK% zi@-U2D*_AUv?ztWE1cM-1%mOlF?FZW;J$AFZZK5_a8V+JD7YbNNk|ve*;-@vMTLwMDLX>Jz8>5dn93HN{xosZg~ge!1aXeZ#N#kAG~ z@qHL>w%vC89dgmr?hS;hW_K4N$d4%dlfC1g#4_`Xm-)|Jh5^x@fv`5~6CkM7mkWZx z*2t3k{=z1S z@AkbdtYSkXGxkix&Pe&-j?mY7;Fo@hk=9TIKb7A4`hbau^gtq%u=(@#!(8NoP=J@B z$Un9Wg@6D{R|TP=mE0pzn>1|P%h~+)opjxscs+3A`i`a7>cjv3_jw5o;H6Tit)r+4 zWt$Xr!-^J$J!kUBXJhQEsgSGw{K2)$^rmd|w~$X@Vp8GD=n4tLwyU0)*+KY;D~BNFuhvK-4y6kJEZ*P{T+CYPP&9(KVmz2(vA5y=C)DRiROS z5)FTE`wiih=J6d`%AH>$!$(FSAlhoP9KGB-iI|2rusI_nvMw1RN4+Izt5Ml)xh zh^mVu6$xin0&qFCUNVj!;1IPH)DL1nBU@Oa8ceroAt7vdV7V{cF4c3r6-GVsIUfnT zXt$O6u=HQ&5cGY3Ln0OEoAeE-2cX-Pl!i>HDq{8Q_ejIZ1_x@mTnW4|r?YuMq*1RF zgo`>R${wyXjZh%SV-g;ggr%o&s${R4uzF5JboXu0@VQ}g**#KxA1f6)!j1XKM?;Uf#`j)U#_h-lOb# zv9RUys2p;U=v}GI)U$1zxbNGY-uNT-aaky}h8~YX7=1UwUV$a^5bU<`U)1!13F_o& zA--C_HY=KP$5CAM?Vei21yh1w!YzVtx$xxb+Ns$2PQyD$*hA7Gr{Q_ROHzXg(FxF~ zznWpgnZF5_cPMd>Bv=L?Z{EBt#7(P=D5*>5ULy&=l~U)oNPYQF-VjU4mxO21&Iy&( zr_}2t26hHYtWWdFuu!utVCr2S&lJ!x^uKcQK|5ot>pSRR>em>AHlz5z0aci4^zf!)CgW%)|-r6RMrXI$wKMdJtO# zc5r<37sU-FS^ajh>PHm9_2m@=%4Xp4ZZ@wW``pt{4+l+%K$%+lB;S0Fv%#qTX6v6q z{})?{8!&nE!pc|ejFfp8hJ~uIiig)W;lzz%O_vMf-+MD(J+(erAe+^vRXF`U@NTg zNj;EP;ieu<@>>?vC@DPrBXLmWICkil>>{&*Jt#Tn^vn#T4X)Hi08kO3n8x$(g7Uev$@WpaWF1C@+2r6cIe&Ze1p>9{F& z@0^+_iyH$ND}1mrpgFP$a$Yjj+jG2+s|n?4>h}4R>syuP!}sAwIf*-1+ZoRTZ9fJ` zk^No3*bOl5-D;3Og9PgRP`gJw-(X|?bt>jPdmaZ&AJxHiE$JBGnto(rB z;vt8<6iRa+UeR2%)#EL}PKL99Y^v6gs>3AGTk z3M_(g*(qX=%}-;X2R#*61d#5i6s=!2H3w!Q{^aCy^CXGNEA+_{`e~4bpr4&_3$jFR z`86@(pi(=yeHsb=v;_QEOJ04J);|@z4e-2y^iP!g9FSTl(;%Y90mK?csz3EwD=Gv8 z@>Yuc0hoBD-{m%$ck3a#Z=NB~ zt4u1VAyrC$o%p6;rkIqdzaEO(?#PVCLIX>1^KEPvOgIHD#`7%w&DKAK{x7z2T#Hg` z0=&T5@Dr!PjfMt(N#q%&I^4WVarTSOuGGS}4@p(-?;FjPJxLKkmp?H8OB}0Xac71l zgo8_GNUVEp5N|YtwL&d`lalr_BsGAv-M#maR!{Mg_cb;Y4N{2%*z#3W2(coWz=T;O zZRKQKk7(BKp_)Zv{rw-df-^#?X8RVpsBBewm!ZN#VTGwS|z{R-lHxv6IE3x z^STA5-e5<)HK-UrcfV{=Mxo_YqG8dcrLZY1jYU^Gm4c7|)~&^{VtnAuLO(^R2Al11z%ox)ZKi$Wue*C}sxr&HrJIy7-*QwVCznW;Bsf;2%m*Kk;>8ymYtnXyYA>Al zWt8u$2nQI3V&uLqaNPIZN-A!41no2_!00f&#O*sMV=*yK-hjmRN(9y~(z3D^d!d9& zcsz_{QC6`qS)z4j?7^ewDi1r28)OCgV5I&fc-dGPeTMG%@BO+(w(qlM4Ce4RH98Ts z1yHkv!)n%Fn1A)a`{!sC)wlTL{b7re8BB0&m=%F7;UoyPDY44GA<`0gFwHO|yfBj3 zft2Nc-vaxksxW7!mH3cE>aG3Qtfp1)V{@6NRg4>Fc_M2rS=a-xH5KzQXSE&@*%-}n zpq&FSwvtg5#qs4Z=+z*0U z@*7>WOGPk1p9htdH8~1QTn;kii}05_2*-&%+Z}=4*ujqvvcLWk-}KV#%jYGub+dOs zfkI|Btv1bME?_Brb66%ZJgN53iLox0#_;@`t$zyrUu=~>h6#u%AE*>7Gh-LV>ka;J zCvIP4)Tb4*rplWba|_x>C7a19@5o`;wExnv>K=*QLh@tXJ|#@&<{-6zUJZBbQR?i6D!V-Kx{kr6N zaePdVDo*Bf*$%fB>9`fpB2%LV0_J|$M@(6*jk_vZt@vKA9~E&tWvkAysgXTi6WGg5 z82D2;ND@Vb!0iz^DX8hiIDo&~&e=B`jY%u}YN)r7(LoNgT{Od_>F04t0zFUz7Qt0k0g+C-+xQmW3Rc4S3 z=U0jZJ8o;`ju5X4@jfVUvA@~+r_le!Rw4|XMA(HuQF~sBz6elg6VG;aD>&?YqO(_w zus9goOsi9_qe1KiH&4a>FK+EQ9N=vCm|s?SkD~O15@4pJpfsmS+>Yy@;H~`p6m`BM58krS5?ic=c!`l;McZc=_i$f06dO@uCd$IDd zf)F3Rz?7%41F<0!iKqCi`Jj)Sm^B*}SN(Gm20BiYbYMJ@sCe7W@p^>|wm*8Xm_wkCNrcrp2{n zp#F0l{9aE{i4N(xR-WX{c@iz0`gcbfT`gYH7N0Nl;QJlpr&b$mj^0B+p^zd+GCF@> zk;WSUI=sehCKu_@&z}v`Oe!y3mxcI zx}dk3B=d1J9vI64QTsQ*n{p6m9fS|SP`lp{<~h`sgCm1=U^*m6O#DnqVRh&UrpN1K zBB)77{;>4l#})*Dtwj3qPF}WJgfpjDs9{*zpUX$Q_7~2!E^LT4xL+H{oENxgYCmvG8A8j1IXpB9OaINAL+Hta3)3`WF}j(yVLmhbFc+6YCD_^3%k(>vGVlNZggr zD?y-HJXC#y~amX z)89=mNmy$TLr-X^bR@!`vNAAD(uz&(u->EN>XUto!+m!7_#Z--BnS!FDY>>LvX0$> zposj$;eDj79=>PZsb_4~pj$HP=q+F^@YW;DVdI(NJB{NZ`djj#=^aF^NY<|*x7o-h zM9M|K+>DzqI>l%ot7S?ie&^G~ZNbNg%F~D0vMQK(N97d+6^>`_PcD_K22syPU!2s>ftWeNLrt zFcLO1WHz+%A~Rk9TN1(_FS`NV8KBJ8NyH}yNz)z_nTINx1mmTox}pCze(3KUw0NJ+~HaM^fRU7B08jJK(u36tmdL^Q!3BZqees4u($89G3n z|NpGvpF;l^TS<;3?JZ9`vg(e8y`~o529dt0q{M$&hRLo`2@9T$JPowFht?6<@!MdI zs-kMgnFj7k{CU&ft3y&{uTP{rWLziA1f<5efvF>l*X5-hRZl%lc6h%6CNDYU6lf8~ zNQ{h0sKn-$HGlK*t%$i!OggQZQ6(n+fHXaN#u(%H>4@Q29vQqHDIq;r`%rjPui*q%OF^p zeTq#pQ|M7XZw(ZA{i6ED5aXkFrZEJ+Ap)2;&hm9K8@szz3C~AQ3mvWr^}tYh|6T%Buz{yQ#gHb*Jm`=_itcF z0Cg_yW-^!khWp#7&>wZcEb&FYhNVa!ml?3}is+)m%3cd*xdBoxuYDMVemj-j(pR)#NTERo;8z7WkrsNEts(e=Qy5(K(ynxm-N#buM zHIt10zsJ8DH&7@{dZZ=PPoti4$TptTzMy%!o;+>8kBz6&{_? zObwvMfSAUZsRg2BCurA7?qSq^mUIi>;`aE#d6rmjpeJK3(+8PhY}q8kS<6FUSF-qc z&_Yo|D6ny~@V`$h|0(o;86@FM0wkc1aKjw~>NQfcCrfY=c5q47pM|JCV&+&yi7bBb z!X_)s`0|t*o9C7Z!w4wE>=S}&XTtk8HS5Cm+7ivMx2dvYU8kzR42x2JJhI{|GTB^7 z9hf`a&-z7LM=^g4l7eddYGl46uWCD2iz|=N6kZ?#5~vyqr$jM{;H-Dj{L&iu%Ux{S z*7i}NsASNSJ1`IP2-QGx>0KI;T}tDs0VKBPCqzxSU6wdZ-EW(9HVpY;48n&1VGGOS zurn3mb2L(oG|nit?lf&|7EMQkVOw-05_?7G%w`&VXhJUf-WnyNT@HGI z<5i5i_>YWlY5|BM(JjBpW`M)+LDT4tY}4yCR>(2ISyx3Xw|94gw%*#d+t>*iDntAp z*S@AGNdWhzv3k<)h!>R6zuM84=8%~H$#>~ENq!nycIpeYB-$Eo+)sF(Ff7+0`1Bxt z{?rqKNecu{~%#PLh3cNZOZm-vHb^PX%P*mvfq(sQ((ZZCkms?NH%yoxfWdvB{uxzm+UC814`DC-qo$Gp zhPY(q%ei6!QzFgW@;5W^M(0cJ28e4al?wmr_df=~LKNn>^Gu}g&muu)Sh}dNsn!u{ ze-^?vMMR*07V&Ye&w&^QiQyvSSKs2f7n&;QwuP zX^MoxEwwcxiOamOLt$O%v*h-Lo7|pgusI4LjnL(7O$Z410u}ay7>d-M1tLh_-K$Or z@gVlBTdSQw-+W#4v~aQoI1=z@Bt9(t*Zsmr^9DF1VjGryXS-x|c7k60ad84)-vKF8 zX>$+=v9VAdB5I@)2HoT*+6bS612k#M#I80FRwUb6vR}%I6#NSYQDje^f|Fwm&^peK z1xHt3aMhp6kym)vYO;1uGkY1>!#LjRXT@~XqLSQ|AavcokLk(kdU{n<=^ zml(Z-)A;3~rjhRU5by2CII^8`r$#H`8$GV5U=OmAfY4kQuJx*$P_ODn78{nwE& zVkzi&c~1G>r6RZPuOf6>U)6gxJ#x>B{c%YB!Ds3U2EK}Kf(d8mHKbHOU~n}ge?>|< z%f+@<;9z3t%J!$g0Kxl%EOSar;fzb}v0NZ&|W!Is)mj2QSKk zcO;J@Ec7sp=-Rg+F$D0SyDqAOL-8V$G*D7lXTb08fmR9=$Po@^F)-L}xLA(Qxv=1* z{6Fg6I;yH~{~A7YclV*C8v*GM>4t+K(%mW2-JpPgbax5TNJ@8yfPkcQNXK)ySA6;U z-Z6OZ^NeSV_jiVW0M0Y_+G~E+Tx)+md#_Cc!(E9C~bP15UHNkura# zYr8M)8}g?yRfSkvv@GR#OM}?zv7Eyjd|0sT?e(mLXnnOYZOkZw{>NO6IAd_d2dwY0 zp&3?drcGf#Z08LmSzD*#HmLwBAnb;X(yvl$;h(^>Olv@l&*Z(SzQrRN))#J}@>WDG zfHrL8LcrD2NOx!+*jRC)GF5YDUYS5i!`ln}?rS%W-uvWJl)=ES9W@&qMYf%B1LA~i z1aaR8w^Q!fQDW>9;Cc+!PyDX6xz%=%?x(yHKk*>D{&>-7^JU(6NaaLssqF*6{uD$< zpqQ1SyAM$VrfLk1K6f@tsU|P}7~ae;6$^n^vL$LHq2%9X>AT9PsZ3lkM$6Z~e)}rL zGJv*`_G2XFG%mJciFcwQkn_$%GUK@goZhk}qaSwIh`X-cgoWU=?KpN{XZ$j85cU0t zMnL?pM+(c)OP?={Q}&wE&@I>a67%h|Vm?QrKU*x+TlfhNvmt+XFV@2h+=<&^c5_6! zy#2R?+EF~}mBwU9NyBrMR%v*~8_%>Syg ztzI3I=b_KR?>?-tSa<;apXVXUU=Mi*ktoI`NG=*6CWI72?z{IvNl>a_7w2OPYh$aM z+ZDSq&miTC{QCC=ArcF*bc3Sd02zK3rNDEmet5Cn%KzH zJA2hSBO^26`8hF7G^jkwvmXZEsWW|j-~c{@>x$DMS(Vy6RBa-dFw2J^D-63Tk^ zNq<3Ph9GJ&;r@2Wb2KD_*TfZFihP{4QWye}GMP9`RC?4#7`XhRnFHvOVxOe=G4_TA ztkRfkRLl>hTzGitZL%O7faI5vBYAyPfVY;A0;!xxb~y*SMr~5wM&NhBMnt=L<4F^@ z4Ley^EML^WH#7HSLr%_>wc2t zRJwwGSh5m+HEL(e&Bm${g$DimU9er$K349Y%yb<^xEQ+fq0f~lrX71^bbl$K{;>E& zTt^T64+q&-wrZgSJW*Yi3z(%uw|XXvz#`ok+*(@aMaW7((Bvs?YCb`{@Tifd0>O5Nfc4_+^j)bBru!MKjmv@ae10 zI(DAmyze5z;1kltkPS(4rhwz#iL`#d*=6GRPRS^h0?H7gOgilwO3%eXQ$I*;x`CAj zu^_M7jk_$J0e!o9cpDAfj678Mt9kR$`kA1x+Mhh*V_kpmAl_PRiCFya0^vjkgqLIX z-nFMdzC?w)3@p&;Wjv?ifapKf(>>_J6#r77O%{ap?HO+PUVIA_h0VSxU|j1>Y<_;< z%U<|%#T!HkiS!J?$)R(;jSK{hNLJF}-0uyZ6ocSAW8U$e3jXZodljdb8^)yam7S?_ zO_T_x@~It0JUyuMK_R2$14xqH$6ChmRl?A3=8A*BZ-;XSQIbE+d~I5I;8t{#ndNzy z{OWn%>GP4%schA4@hrS|cwQ*w+R0zhUbbN$(EhS=M*%|nY}WDLoM`Ny%O8hssb;}C zy`xkt-|x`hNOCu`i8Y z3;~3qc#s0u^hlBh>?B=~v1Qr~|6st3e{=MuHXe?+ zCeV(1j#PI0n%!rXkfXf1c}Bk9#P94O^ncvg!a4(wtzfyG%vlYqB*bJ-iM0H+(1<)J zCgdiW&uLC6h|a^&e93g=09ZGRp(Ktee}6IE9z259-Kwmc4#}^Cx8dnXSOcxzVsnxR zk#;YXvDo{RmirFT%ef| z(NoS#bfR528P9V6l}&BO{Gblq*>zlRB2o~AMG^LrYWO0Y`gh`MnO|PS))c3JE)yb~ zJUEx`u3~*xxA_iU9me6_0FfKvnXZkT^nX2T5pk`}F`{?SSS|4};rKO@+CuKBoP#E6 z@&G4+5U)M5h65eC>W`hXeQ`ubXlc3`_&8-7Sy-`)oNTq%*Cp%7SWycs#}xT*k&d~7 zLyx0ES@k2T0T9Y|3B+aqlw_uC%VG`~p)v+~kD z3Is)Y8#!zU`b|t3V-~Bksa7&(Bdnh3%$VhaUr-K0q+cvB`lyOmU9g^2tMz?@JG_dwtJ8VP+QBKa=AR)rHQ2KOzhfpxXDRdzT=C<$Dc@4vj$O$6qj4@P= z@hLwpTo6v=)L=&Eax+7p3Co}Ns&~JPRWPe^P{E3O(n*s&wOUhy&diyN=W#bXzj#d) z=jFqnr8o7YSTFZRYzhviqd{7TF1G#0`|p?=kOcVu+RI+{GiArI@qUvtj2is`^vhu9 z2u*j=oCb=hD@2QixqLM6do%tqKZ~nobnp_h`q^SDO=rs|Opz0@Xvu5L^(JJQCrIgW zBY{2SSsB~|4@uBBGpd;`zmljGP$Cq>8|D)#*YyFVP`rsbV?Q6Q}PqgU>3h*FlFBtfzmufgf2Glyw)XKVMoPxcu&z5>O zM1qXgEeWL=eDY00%Cjn93an>xMnYgZGpTboawkNj>R8j%+iW}RZp{_4gj) z9qC1TEy#zAE80YrdRju<8??Y?ZI^)#KIJ=J{EKFF`{;_Hr;W z5#vt9ddNN*W86UzQJQ;A)UJOVHmCA4C?8`L7F{m*6R&KA(60mJfeS|acPo4!sCn3= zUK5x=>L<^a;S4@eC;~OotzR<6m(xyf!J{wr&Ia_oeXcvNYxW}ch5Res*pUOh;MR|6 zku;|6jZ z&V_tVMAr#gkbB$6lvegFFNO8>%f$wJ_FwWd(Pvq!IY{YR-0xNARpVW@w!}3*b&)4g ztlpmBXX@T2*U+#|D$TY^8&ML=g^nAHErkiV~dx(VA5jC_Uk|2inf+^f4d{W$I6ed&S+ieN+iTqNLXc zK7{xGRQK7h*JYbm&SoPC+J{u?!EExOTvD^8?wv_8Mkkz+XV@S`-^*iEv#1Fp2>P;O z_d(a_%df8zNcIkc(WUK8mM455-H3s)p~F|wIo;eC^Dp5XpxbzQlPb1-?eWM?Fg=!4 zw>0$L_?`B&zF6&tf0rIg0RoUqno3wS?piinRS_v%>9CU}MPj$XhngPgFO>v^=*GU^ zbkAqt#MO|)RWVZ^NVxM5ce7z-A?w{Q1FxXNWnD7QNBBH-pJ{NNpF%p}tLmM}{eCWD zT$j$sG_M^|?(<;r`KeIjQO$PVxcX$r!Y~TeaXbskKOH3cD|ZMUFWC=Cv*ltM#E)8J z)hpVc6q--dRJ!Orc8p}e4uTp7`@j~kVRqp@Vz(?)s;4gHoMsK;iO?R^Q_FkQUFmT3 zR+m297wvzZgQS8TBo8iAHy9i0#dJ4G8e|={>rzNKH(bp~RM#orO7FBPKpyMH>TC4N-X*Xq4}xxm_0=g>%ub5I`zPe$$9YIuJ3ULv?KnhQlkIad$2cuH^mi3I zaN%HqB)qP@h|Y5=3{QnW;P~!=B(CZxpxD6!w9f(0sdTu}<2OBK?-JQ3>khThWt>Pp z^-OMP79*p)tMR8UMLaL34gU@wN8!Kp{3ou}CLLSzsvqD`C)bsI>McXVa6_qOtp0WP zDkMT^;r@P1>uE-8QBVo^dGlq@>!&#QDLxd)zTP>iheR+cJuQA+w(d=g*{O~OgIAZ& z!w>-Ss&O*GlcGGoFllT&r8d%X(N?ucC-Bdj*@X_47=~Un{$xZPL4fz~tVS@1&&;uN zSk_YRiUwJz8!;%r>DVFHqAJ=PFf8E%%yT~XrKz1KMr!~H{KMe4NWC;a4tv-(4n`JG z_z6sX5_v)wDa#nv=Fy^iMpUQbw~#jGd)4iOmv9@b1g=#)Z9hu!r`;3u3FZxp!kGja z(Kz=F0Uh{C5mnz$lRvMltozxDDG+u34G@<@gZ_8UTz>){d0|Pl{38P#4uo(X}D8R*f zywv~2lSVJob*}Soi{(DFpKS6TKvgx>dlM>^Sbr5oDR?#JMz;<{=Qn2uLKxZLz7j`= z{QLkKVS|dWD8U_2>tp(1Ks|4lWl$GDvrAbl{_4#>u(ekhJg{2;|93xxIwV+j;Nyf5 z^-W+S397#aM)T9I_w41B<^E`@f~mAzPC`~Tt8y{kqI*rY=N1*+Ra^JozJd+)10k}L z9!BgW>R7&aFSSN*#+o8Zb1DW%Pyc4IZVIh*1NzH&4N2gZCxIUf-W9{{!T%MK? zB`52g61UTSF${|DP$JfT>eD4u<@muWMAK|KZO2cHyJoZ9a_xK>LJq-Ap`}V zR8|6_1lZ7egjuxUas$l_YcEEWTv_n|vPuam&0$^($^!dN4}lSrALlUKb$X05s&HT?otFC^SS!s`)_5&H~s2(FVNHlnq}nB zyxmwEki59?ULzv^y^WCli2Hm9+6$SYP>{3Xt3l>L3OfRVa9RJtCJQGZb&RkW;Ikq0 zgFzsyl4C@G@BiAs&3=EPQ0fQ;EBNA7tFZN_Sx;r0hMTt!p#R0({AL`)W8g{5N94a5 zE9P!nl&JA!|Ai<#?oBms%g0+XCUD4CJPZCDBuVkix>nI~=@j@3@+ua(F00WAp^SfU z0sE~X#I3NTR6?t~!XyOMUSMoD!VlW^ZZi^ezRz8CC7^?GHC|GdS4@jn$^-K zv=GdllysX^aFEBRRCot1?uM2)_&CLHBfd?OYLG}cD3_wbg1BE#VkntM4(vM9RCOOA zQC5~9YMk8ua_xqB(#df#%t?-f8lO491>91>_cq6x# zOGy`IU1l4uoEm#jh^etrG=x8V&~Ir`q>>#!srKkokEj7*Kd4}D=?^+g}nHg1*im7Z%Y`$&N6xs+{B(nZv%GqQC+4?}9U%OqpS zK&=FSV*dNjX??bhJkz?E-%cY~aO9(kqrJFR-*Yx^FuyVVbdw<$-*RYGlyKNDDUV8A z*IX>O#RM&641FTSST!$E>4WQ7cHtl~cj$g-H}DDXtNf}tF_D}P@2!Mm+K7MIxgO-O4eMajpHbgZf;l3+2JeF z9S)ztwpEF3B%izw6r$L_GKsi0t&_|(e0i{tg z4zcO|n$TaE<@070m@BwmT8$$!ao;@_28M;;;VoaCbiO;-|86t7p8O+{NQ1IW%#QhM zUX=mTK?`{I(SPnlmPH6$IR=hoyd)`&QaQOs`|#$R*H4q2&!dtHHJy;ILJSZj1NFcc z$`p`|CN%~)ZrjSB8DmhE%42mQZj=t@GOvq=ak6**XD{#Zz!s9J29f+Ki~WY3=xVg5 z=S$T^Cq-Q9rUT7+t4&N4Tt1hV{e~6$F(4y0S4{_|)=zT-u zBe^$G>)$V60&)-`}t`Xf2siug0rT7KH9xia4Sg8O9D&dCw}5_b7vtr_3c>G3FAh}X;-fAcw@>jNG zh&^MXSaLoYr|s02gDgspm{w7trHh#iq+OCiS@owi?&EBQhc+#|qVqiLVuz6vP5D3t z%k?rG%3^>my?YE?UEf3Z6* zArdTkBkH^xHkdcqzTW!~h^jgE!LP=CH;B^k07=QFOM;El-V-u`=aV%L#h1^<-&oNG z!xMYMRdhx8ZBAV~NM%5=MT^V_rKQ+?)XEnfVE?ungPe5x*0Brc=+$j;1hN zXe=Y%Gr8F6%oZ*3JaGJ%RV|u}>Pkg7Z4U4Bg@vI=fG`I0%hn+q$@IJ~RqwZ^$xCa` zxiQ3jt6qV0OV6)@qoC8h1O)47(699Nwxw186yoR;CBZ2t!u0KC zTd^%tVM~h>cR#ZO2pG%F&Q&mnZc`T39TS_L@oNO?-~}7b9YSveGFu&sd2e4Ee%y*^ zxU&$itD1I*pPP?l4c?xP-+aCYo^=G2hjh2EbuXZf-R)bX%^xeWZCT{uvRt| zaDb~u&ThF6e*%kv`HF8@BbWzISpMeIcT|(c!FFP9(II|p-?UqWIs&!NaTsr|EAaDN zElJsm(-Dt!-!B6y$C6ZPdr;f8X+CBgnucIeBT=#cv5?1l{@y~0bSuJUmARzW%~OCx zo7OfR3~u&n_{(fa;t-Kvx_(sG@@Hbt$*yjiSWaD@QSRyDG}@$cT#p1K3f(GHyQJLM z!Bcob{W#D~DATF#juy4EA}PZ%=|uC9l(oR}`e=rDXCb9V0ooS{v`G~wN)ods%1&b& zLA*J$s{k`WbUD5%qtw&x`9WWI%!;(oFXJD=?2y{ zxYg5gPhp+235eo}rMdRk*p(SLcD*$vE}G#fffWT^bg)D(2>Q_a4?-g8mOa|F*4{(g zuq`2X&sbbQL6~>6iZMI^o*fKRo;}wQyztn(x zk2_na%GFt{_87yHb53OM!fmmjniWKBTHfB6B?bkyIf z59C9zEngpJWC>*hpH6;UXO;R|yG$;J%zmTL(eDHK?5bOwueby?z~{<9jlGs`HK|c+ z1^SEgPrEduLX(gVZNk0jFz?0?;`G-B(qq8wos;yoJ-%v2w^+1ja$doP{(7U-@yauM zeNl1nJ9oodsfC1Z_&*=&7i2SEIfT`4SL6W%I8o6czRRn$KXa9}KpdTE?m6{KsFJ3b zSnGwA6nVXhkmbz;zvc$sE9%xMrf+#twKcOfoYYy$S#W#WG}pChMOE<&pOUr4md3%I zg;bO&C?>O}Z}dJFw_#D^_^AEVQ}?KxM&#qhyBd%)!z=Iuw09N~_U)wu*(83Z2NNEC z{VQM55>J^>(?X#E(2}Mup)WTlI2HMiItBB9Wt41!*C|eSFr7HCa602v65rg^#r_A-|KdVM zx_su%o+%$gT?*~U7oA-}q_u=Yi9@FIYGZjOu?JOO`$OAe;qlFxXcaly^t-UE}pczp08^f0O-+*uz4U&vs70UxJqMJAiM7&21{>ILmk_7L@( z#=LbTQ{ks(wd%!3M+zoIjJkLvG9l@uMg7*HYY{^P5JZgR5IL_$=obc|k&sK2&xNQY zcPLHf{LGG4Uw4gV%SzOU^t+*DBsqk$qs=w}3L*c!ebZw-A0Y|=_<(@U_QpV4dm9^L z)_;Hg{@qW}+Qt#62edcRv-~#@L~I5$aWZmn06LnQIRL@`EX=G8!3aHTLmMk^Z(+a# zk^b-K`Ph^o2kBsDVy)-sWN!ri+;#ms6-a$c8v_gA!~Xz;BmTRu9&4p9hzz|20CAs1 zg247h7ykDz(BMBpcmtrLGW6JdeDwj){?qgd00=bzqOqAJ3mc1py^ZyQ?vN-SY6A{# zYq=Bae;p?f;91$qjts`JPhNkWj99+X+5!;%7mopW7ich%;vEsvzlp5Otc~m+Fg5kN zE^WsVF7Y$jjuhdfnXeLLe?3&$z$%^6HB$TXwhKZA0HOOTbu*9_9RT>lgZMwEk^{gx z6CXa6(oWd+{@r~B;AaLOId43f386_y)00s?cXr=*>rx>vLAzK;7%8!9@M~3^5a<6% zX|qQ-pR0LSmy5kjx(%m9Wp*|W&6yI!wck0b+npwK*^4+m{7-#p^ncs||0#Oz;#}!2 zCXs)~xt_g+(Zl%B>f3~Kg*&QfcM{!;=TySY09!hgmr2{29>$e>43(qH|@E z?N7F5wkD;=knJmpuew`#Jlq3e2q-dYP@UBUO8=A2rhm2WZ%%Kn?Hh+A3^@I(+0QcJ zo+_*=!{1)vpL*ak`TLT5NWu7r&iMbdt^dcsX#ee!kC~S@;a_(3z`1H5{>|~2ZH1;k za3h3dCEvml=)5(36C7+2S|w8nq*B!UW%e(ZS%Zrx45iK- zmRJODH&Txdc$aSC2T0J0wut}$k^qQ*`TH>dQ2x8&kF~`Q8vlu(2%O)8Ho0i*{gU$s zM3VRf*yyV4rva?=)**2LJh3gB7$1CtiXT8}DPZ={Z2Jjek}oCtHep()JzIF5y-%%+ z!3IG)S{yyRd-A)DRk9w-M>M?aaCP{zBK1%qzPZw>y0bKyc5>Hu#6I3rzgzs+;r&F^ zz|jMu1^io?A#!x}Z-(`=>NXUwW(Q?D_A5g|qy?c~6XC68R5rUhJ%FNWuqJX8aBK6R znK`EUGS+R=|Ga7!-$kIU6l1&8kb1aH@NPX*&G(L3KQ(3~MilXo!0$iUzOAonu>UNz ztpAE@9`%5APjyZnvb>Tdy&}FlUJd)Ok?GZy6!_+g?FRqn2{!NM-|Mi)fPNyR-u}KIpn)>ooR-gk@DW>%Zd2F|r?x_D zE3fLy&blI$QzWHYHV{xQrXb($*Ds8VqVLNqhfWjae)vwcX3nS+QU}c5kd|m*5(l8d zH|ro3TK^yrc6>7KGVEtD+sTaRWWf+Cu6GerufREgxvw!kUQDcVqRiV?n{AdkjWHYS z=>gN0@wPL=b**Gz5^j2hUGf%uJiA{KbI7>?&0M{6!BJtyzE^Iu9PvTnPe8zCDKQsf z;;qYD_tk4h5^wk9^kU=YPSN^eQC}3j)r-f_{c_>3y$$GBJU8xrjOZ%w-1vyE%mkJeRzV@n!lg_hZ5Kmo8ef z&QKg#DZ6SV!B;_63Z+^ypotRF%4tngzN!t26R0ndL!SP1V7aW1Veb-t^1el^op3zm6?x-U=}1^%KYp zD2;5v4^WPKRaWxPb?(2|(L5EF0|8~B$67G=<3i9usqEqy>dA%KmG9c$|Db6xp)aif z?z4kQ{6cGp-y|CuqM`-}Y{U`F0Ri=mTlHvs_2U+wk#X%< zd(UB79;K(D$C2aHrB%sD?8!Wqxh=vt%s!xfUy zK|mVv8EuTj3wi6P5%)F)76aQxYN~p3$|IhE-ZFD`dc5CqKV}$z>Ea?@jf5g;wfu^A z)c(y%5DXo`86a@x{~8R>dL0QDl0?pTv8v}m5lOAJV zuvu)&MPCDPmo_6}ko^-~=s-mGIX8P_I5G}5ENXBkV=MN^9~Rngm?~O)7g#`}{^{u^ zVdE#9ztodf{@&mX*dlaPBB#(ShKm;33E4&taTY2&pPxLb`8lNc#lUlOyX9_R!igMt zmlmT%zAI!)_v1-*(-3y4gUmBx`ayA16J}jP3ckj_3g1T>iRv{6Scgi>m;Peh;k~ji zpQAY^EZ%{$mnH&}K=pTA0Fx`BHYv}G-gQrJu$6=udZO$$8^rDYd~50k2?EY**Wy)% zOvn-E`AcQWdYuLSve0tfqGSD5@sl%{c?{*Z+>e?5U%Jd0?PY1i7us#+Ew{PpX^g)N z!csL$ii~L|hLip#QlEA&_hY!fXlL^qZ&VXl)rHNk0o^ z?tVF*aU2?+_!R@o2Jfz~GPy1g6mh_vJZ}=~-Rf6LGF0w&m zD4Sq2+WoO0i1&Q}9bWY{A<*k;)+++o$qQt+p3`p89BR<8#VbeUTwAKxa+? zqUB@*lL)>@=++^#B^Kn=RPq=<7}ZdU-LYvNe9~35UUNnJiwOi=CD+dleu2y(xIa(S z2tYkf3RuEo(!UIc_`zR6SqO`GzbJWZq4U?GM9Vb8Fi+aL&ux&-$9Jfr)UhOBwAnqC zi_fRmMD;`V%7f*DQ(MSK)w&xskAt^ zZatcArW6ATg4#I-*`k~Gi(5#%-pQT=1L24Hs80V(9Y@2XDFBPEF-J%vH38o_8Z(Ek z{nK%nFXU>TvG)P@SVR8`xOlv}6b#DoybkTq96c=&F2`J){Hj>~p$;brvsZ*4J??2A z!~I3O*(5kNha(&)Zv84w`j|$#Yw{@Y67j<1Ej9(51}i_@pE!ODcpt|K#hZ@vDPQE$ z7b7!{pMYN}79kATnvN$a)&t|}0zU%)PeBDf3~@Tu!D)GKqIuXqYh1#qa}*QdbPa)? zI2%8kh6hJa<~P1Rfa<=0$xGDBgXaLWZo;nTiq<>66FsCQ@?0$3ARq}EsUfzt{8K3b z;k9LG2XBYpWLu^F-(491F<(`)mO4e7d#Uvuf zR~MC>AmBxE)cZLCIIs8dX*bV)#80O&byBVAw8ZCyIiFH~V0#88kzP73T3HPZbekzc zeU>GzxhQ&^lJz3XjdZ2#@DeHw7X*~5+zG3?+3`o#wWxq#sB7459PHl7A)+yF9kDdU z9D4fOwjK+%zjP^t$sb~|8x6Z?#FO{(XM({ zhJ4^kO;x-uFS5l7>UJya8&?z7P6GuU2Ga{HA^nm2G2p%2p~(ds-&WcyM$4Whbn{fB zEbLpTJDxz9Nv=(Lw3h-TK){>28;0H>$kOQ}^5edKK(Joiy!q_()wElv;9-qTKR&pV zWjx=HB3LLL6vOSfWAfrZEh+X)p4SBLD^^LYiz1K(2LjR-wv2d9AR`w*xq>V8=F&kyzU$)pjdqqBFb@Ac(EXS7tb`xkNWzo?1jOm+ ziw@K=x;`iips8<`dbL4859FrEW0eqVwQNmtV*-=ROKuYv5m#8f(4emCEHV?>C;uW6 ztc=i-THwdCkY#2A0y-7FM5LYNP_*`~NB@Qnf2z76h>Ghj;oN91sQX?_i|)7Fj~T{a zy7-E%@BNy5{pO5fAyvXWJ)D4SNfOdVLtl)GI2={d$mm|~$8dkqS^z(Au8~4=%;!m+ zRK){XuGPXo;LD=M3@0JN1E)!}KXN|?yqDY9w`f;~8S@~mejrFhm05hsAd4vDId$3$ zw0J~R*}NtQNP3INnKqp-Sl?N4LPB*_=xIJyz#M>#yc&9kv@PQb~6;{{mVs(vah}#>z`hNfJ*C_?~*-(TJ(Ka z<~1xJ!e?=|)Xxp}L6;o?s&Cq8)D$Mv# zY8}9UfM@N3#>>`?J$qf8>4)crW+Oej+dCnxQGdNQioSM5gyx57*H-ykdm$D~We({nN-G zU^Hcz5yJY{|%gQ{H21gfJn8Vl5e4>lPAEO{Apzh=@PzAd8Qscx{V|ystN@2vr z$IV-6GoHm#jU+WO;qZZgJbfA@(ju>ya%on$y~iivKhU8uQn#@pqTKo^elfSB2a`DK zWOO*4nl~O$EAm7|GGYCskM@nIJ-x28g_SW@P6J;YemYoaCw6cYZ&w46%4=P{Ot^l^ z!b*YiJTa)>USY(A2TT(48W%!s$Q{VG-to%(sewYGn0`dhOo_gla~Lr_N~jiU*DQpBi{II68QXl4@5Z*$u(d^D_xYVEc`T*kCj#U9VEMq&;33Q z2wp`~|3yikVhFJ^R;kFpBsqvYafczyY&PRT=doqXuAU;?lAGd1)B6F* zzNsjg7}CYI{3iv+%m-4>j)Z81+wuuOKrsZK=J(p(>EmWMeWk_tI$Hyc(ix7{ zs3fkXqE&ppcO)>+@&uaoTK8hRWF&P~N4~{@M!ZxOphQaEgg7TD!7+gE$lWiLUv@Qa z3E{r0JJ1)v>OLl=$z zNofdb33xui8CzR_6QUv~&Tzs#PZTlt!I>uXEk!3%5{X##hX2 zjg0IaSvfg5iNMuM_`3=w>Hn)5_>a=y@67*WWx@0ZrGvZj-@iHTep35)&i1h);6r=u zzP>d-!V7Ga5P91Z8FKxruKF~6+qT@1AvFj|kf1q-0BR%hcW(7Ds4s}X8}P48{@uC$ z>w^yc`v?Dj6-D2$LFMk8_I@FOJ#C(*2au;t>3>x#U+uMuWZv zCh9z5fP4#Yj|INr{Wt$x$?%Q;^-U*8da0jLxbb+OB-Btc059Kc9KTsO#5cRAO%Ga$ za$yFI8g-j6{wNEb#*!~<4)~M-b!aNzp)nrqAA!>lRrIrwc->uwc%a3S1>fktNby*S z^dHrg|Kk3M{{jjM?aa5q6P-mJ5S+3AC9=Op+pjX;-Y=bP)GQ#T!-)o0SA@Z36{dg3 z#bd;N0DzycxA*-xdW`&T9R0U){%?Hc_c&r_Xa6&f?(18R6>I;YfL-gsQ;Q+2uvvH) z%fJXD#)dH}y|s|p9Pi+85)P-O4-Xa+{{{51PWsRFt;Y`VAL?5s7{A1C;*X(5Z}%}t z+hv7WT*q|ky}#N(uNytr8abMM(EKEm(`ZBBE?rtms~$8~Ha3~4oZkT%G?evZOBdql z{KJ>F5^y9b4$7eQE}mUlCGEA1xCk;KC?WdvoqmX-B6fIp@u$A^*x~<5C4Dxrrme&A z8`@el&-<0Q9adZKBTE{PSBKlE0`>CJz~Vt`FFzTF3Dk8ACK`0_HF}=WJDQ8qm7gQg zyTYu?E4wzne*kR`n0ada!6Q+(Kmq|f7WS$Yfb|07Frm2QXNOGr8sOo*9|efV3R$H> z@_xz*zE*B3%+9FtUKjkY4`LGa_eK@m z)J^EAGqyHLQOk9X_57apG2CCYTFvDt*d0i^(RDc=kEO)}leF)C7)*QZvA+WGb4yGku3|;Q zl|13@V)G}OPHvLmuaK`xI#RBe$xw-GLKEpCu{jML&Ae!O+nl|I-5iPk^3>= zz1(;OzZ74?=HeTw@+ssaRNpw&AH?`os#-E$ETCr|vFU-INnC94K!&4Uq|G>pB#sGm zyDokHx+|O1e))_R|10eNol>t$pN$nqNWhjiMSXV_D`kBnc$B}_xAdI`!Ar%qDpv&r zR6HFS_^!2cCVJ)&s(B`oj(mz)z_HPf&x1=>U-F@l4otF2w!7PON*2rwv6v=oyz)~n zncF+!OwZ7^jG9z`o)28`n8((V86X#cr#t2(aD`eVTi-GF_p!vTncyP${N70o8%&~l zEX79MC3=klVerer7ac7Lpts6MGzN1!_11@~XP*oNM3GW-uJA@QbQ82dQHZx6F~bz=ihQInt7=!8b+y zUhc4Xu%v}I^LplG*{TT3G?z|s1x*3=v zdfM}mztU?<6+DQPVk*z4sIjscLZ%%p$U#6+HS5XJwE1_Rg^8(>hn8UtCR9q~Q)mR* z=)|ZJT}C*;o%|$}@QR0eK0ZHRng~RLgBdhavg?FmEEP}sxd*2bhXMqAE=&jA{U+#g zKrAp^2bC0u;DcNZol$iEzH>Yg^wIWRC31-1mM?j*S@LI7Fr5-24+JGq0AcYPh1h1& z8`PyXLvUdg*Ym1F!@gS{J7~M5DDnG%0Q%*pFSe(;JT-^8h0-c_Rg)ZptlgTFBZkd}7pza)%BIA5#;6)x~teuza?zL)zk++Vav zd~^}CbP>}NFx4gp86~hp+u9|}#FP5->LRE&)&_2WCRM|S?4rAYboXL#$+&pNjSqpd@1%v4_0Haqh>x2@iKPLf8JRx(99vtS{)7+1Q%9! za3nlAP4`@g+R!HBh+it)=yu9@TZDzP|O? zLg%kV$>Q7GR0&+n)Oh!k9)|Ax4#QJTk@IuR&4~;}nJF3Ti1Rd3LJrF(d-nnNSVR8`xSzM9)8cwX zg;?f${9bdJIJ<4l4vjF%R8MrgHcn&{!tQAw!~I2@AoZdo815`zXO)pT=HdsNbmd!Z zlOVrYf0Nm5`0K{!f8zKt;C&pMTZS#^%|Y0tL%8}=MX7J1*!3Iw$ubas0dRKK6Tb#m z)HlTnF@JSA$6UL1{rriCn7UJzZ0`jQ$jzdKC#It&S00eK%@l35j|`M+A(HuCu3|$je8e&B6bO9RIs|9o@Xc%>VrC= ziECg)$!a17z;b84!yLImUxd^f=<7hv43ar#M$I^NVTfa>g#%?&blnxn3PxdDqQv$R zV=BEr!lhsgagOB#yr&@f6mKA90c6!9eBTmS?_E6 z3D4bd$VJ{8-kFx`unl>p|7DyhvLohixgRr(zjVQt+3TrGR6hM8IHSLIvQ`2EKWmB+ z-A3>^J;KDn4?6i??#FO{(NgXUq$Md?$O&WRZBSoWHF#*zSlS&8pbNm0Y`&cQl<-IH z$AI^8a~v*pt@3k(0W3y0V0eYPG3FzQ7hm8Sx@n{*CT`8rf`HbCXXo1_tleLxOHYz? z)7^R!s$w0=IIl7Qyy+vE-zmVI%na;}`R(2PGO-yfsW4)Klb@%bzxUUc z-!|i@knvI%sx%krk;MEgPXhv$X<$sl39`p97mo$asx7?4J{$INIVRFPtI0py$P-b(k7hCYL-<pR8-jA!1`|Wb-@--otdnYcqFa<<&fXlsR4elET@4ra!1#kICT=L1{S2gxU zyPI#+6Gf_ZiAmgzDLGA33$$djGV(xno}h2j-%jUru4OqSH=in%D`wEb*+Vhd)?8 zV8AQxI62*tH!#n?ESp2HW{;>Ma&jU)YB_fD@?A&K?`a>y{Y7i4uu`0TL{k2(uvNb*#imKw7Lcf25LU#Y zh1HSEsUP_#;vNIuM;rzJ@d$R`Sp+~YTq)}-))l*5LQJnU!Uu&QWWHYm@4(AoD~2B& z+tWuLspyn5m?p{Q5=WK}7^8OB@;j$kBMaAe!`e;3>3FTlih=EPWlUV7aW)Rl7-Ra~ zN@HY^CNXxo4>S1I$++{hv6mdw$fGwCn<PyQXsFOgGn5u>hwb=GTwe~waF5In*Ywr{|guOB05B@z~Pp>YZL63=(Q#Eeh!R~ zNo7(TA|j`Ua^DY9hu&j{)typsD7VN9{x+D$!s0Jo4n9C?CG$nsdMCNBGnTAr$s9u$ zgt>B*bfh{Iuze#sx|jPg++VaNZ-`O6!;_l$HZ+u(k!0vEN`B~D2tQ>e{zSGl1?>W}TlK3kgqL{%9+`Yr+J%K3I+W%i+l z@vfXn+__>Ho)jla67#>lsw$L`Dyq|*(a#Kuh&47I-4nBI)wqAwJd+*a#V(j3q{Mvb`9Sx+6jlqFKt*V1sOVtB5>oyR1Ex5Mh{?2G)>2|!;J;sj?eZ{RTd=RGat1;`&iu`~Ah z3Pe&mCdj`cB5)8}K+*Wav;`EjKTKOdvHioe1r#TTwG~uR5y7nB8IC#h&VHe(Wa5Zo zo>;dIoJ1>`V8dhq^3i|}9fwORx0TwQmt*D%TUGb^3n-j^n6`kT?uTg$C^&y;wxEL% zXx@SjMxdxTY{QD+z(MBlaVH(SlwZ8@_m^~JR^ae2vnKI%Kl%b=@ns^SS;29abSBf0 zJN$PbXlJqu z(B)aoQ3ykx$&tU%gf@@i_i4OTy4I=BU<6xFGtN~^7(?ev|jyT$#J;8MH~<2!Tp=sx*7M_3c`<}Rj`5!Jr!A4gKN_pVG- zaWw1}-0NJRknmyJ0*V|TrY)cV^I_To3f98f%DLi9N3Ff=K+ntgFe7C6$^7HdBZjo< zC>SiAGx5v|m9#2D6BsWU2cK&hat9SzlJ4~vP)zqQZ2^UT57QPz_H-6bb?GBX`<<`!|bvO5voG0QP37BT(&l35%?<_F}-VDyw z-0NJRXy;+t0t$*ArY)e@>7m*Jh(p5K^6zq~%Ifr_InrEQI>=cq=7G!DiivhofsEP_ z{*%(F>Bb^-X8dHddvCg#qZ>)k(f0ZaC|r4%wt%9VhiMBa_<3lypo0-8egqwik2KE8 z=J+o@q}4aZR1v*bm#$G6Ytg>BeDbIHX)&G4`RoK6cn0=sS$byJ5C z%Igy?z2$Ob0g;t&OB-8Nj&@teA3IE2Xo$!+28b6kHgQv_PnM5&;ZCYc%-Q$vh$nq~ zb{t>f^kLdMH=+FI65_MW4ddi8b)*?FQ?BuOexL58tGVr+?Ih-ShiMCK>PnVLL(&aT zeDo_J+KR5lTDnDoIPx1gsp+$C>xB7MSdoEisD94vL6O8$EgS0^ zyc=27-}&pG+KTngk3BBbbX(nd*^J15z@(wAK6{9>r-}BA!F)2Y$9MhkpW4d0J3{=- zHV?h7@Jy9al#=%4q-d!jzObm5om)=Sex5jo_6?zf5hy|n9gMT?Si$*$-X;v#F|KWw zwd>{S=8hVdW~!#@;@je@yPbkS5Z_u8hwoRvRNG>iKwFK$$Gj&flB;%zpO$JV9e?R#o zl*g}|=}R{l;xS(ndl;{U3Zrc%n(DriN317*sc8KAe7^?%fCy4yrv9BB%e~I6<~{q) z&x_H7SA6C@G5SLdDS|pbA3lmyy|JUt)o^6wupi-{+L{cMP1oIOpA(TbMku9YL)$|i$Uhul~0TXUN^`WTrPyPn!>*2A;Y*xA)ySZJo_DW2c!ulHs|&abPN?aA{PPIw9nX$pjZ zNZuy4E>*t{k#Lf;#)o~ce`<^F{mFzZi@vC*We;AowN;(Sio`y;i8KA0$Y2>4mBAJF zFm0)u1&AY#vy;)R`6d`i1izofK#UO}-EqD{?Hk4ak{q37>P332;GsfyfOEE|?1W4Y3T*+d0U@PSY zyUfo?JInITks#o9>fJHqt#A-oN2NG%b)FD!Gcs8oMr(tDZDHEDd#L1_ilC<>_O_yx z#Bs9<awc4>MH$u;KK8Z zo8(uC(1h@hnJSDmNWQXMKX*k41=i)6D?-eyD;;fVZ5}^3aVyX= z#qK!LVcH6%2#>zQaYURHUZcjlJ7?sA(1f z_K?PVrPP_OtItqsl3Y<#ipF1!#~nRPTY78yrnY`sx}^U3FV8QNe+YT?Zjo{&0;Tno z#IbIlG!U$<+yEhd;n5dGY4L&=6zR?m+1+q$yqTj&UX4k(6MMDUZi?Swjd5B)o!od#)w58!jNXQghz^mUPE=u=G!DnFP~0$%9Qm zlUrdHjIkB6lunJQOY#=(0)A1BoG+`to4)_4tp`tK@1qDm$Fri5 zr;%Zu8}>oRVN`;eAp-s`YhGaTy&S>yRbJvXTSOYsro8cCql75brD;P)g*i7ikf89! zyS>gmgL&+>5>H~muizw5OB^c&tQr!FI$k}^y^JLoTE5lLpGby@87*UPxk*w^jk zzGpP}NhGc;qE?IZM!FR>Yf<#3ZIGQa|8A1`r?#$A(JCK5%e7)m&~9B@vz1(D$8Ucn zj!+A?_0rK^d^_61d@oVDIvJM!j08gl31$H<6x#Tyu=H_{M>EnD+&gSksNb#p|IvFv z2O|*NLkA;JiCox^aM0=v;*g^Ju%R)aO1g(>3#j1kVcG(!%X^r%fNI>r+DeZkLVQbR zM5A=}f-8>uET`3*ZKq7b=1d%8@8jF#8>S%<4N9G7eZ5>|yW6hQ5cKTz7f>nO!?Xod zG50WS0TtFgG+WTY2t=r%gAu5rEo{TW>3o-tj4d);P#-AlR2dU4!}zFLRK;}deyt__ zRC2vX6RrqznG1baQwXWNHA$$$UgrW8zCBD^K=pAC(-u$}-NUp6RFW3fmN%0dLatdx zlhmBgu)&b=;&q1Hktg?61aGIXnvJm=t_lU@I@yz=TME^@Zv=Q_!M}n9`yeb)!61!q%9r z7Ny5oGsfs_*_IsGbKc;c5}wgs=K_^;Jxp6bRbLO&7Ep26!?XodXcg90qE!97aDUCX za+Rdv=-=J83Kr7y&h`s=;z4Sq%$&vx#U9SnW z0ez+T@M10w%AVXxqF(PnQ0ZRh0##o5>g{Uv$5k5cRkC;S@${@s8Z=++5#$QdYHC=>Yg5^Eub2ru(pCuxt^-pqO=oh z{$PBci8!8;#%9W?y}P_DVLqs;b!e6j&6ol|#UU)Zu)D|Uox9dve*u*mJxp6b6-f`% z7Es~RL$d{QCD@mn)4m?J{POee?iog)8`L~tc3nyIg1A)dZP2GfE*RBr9hqq{adS#&n3?GOC%{^j>TJXiDR3bt^z}D`2v?i zj=6!1zMh{0((S5F<@{M3`j0d8$Cv*?{^s_jfTsZ@Oa!<4YX}bds~H|n-uo+P4>g z{%VF_gVwkNx~T5E)4u8?Z&77qAj8cZDF*${i$}U7=pVMwhV^M4#%U z>TlmjeH`){)I45eU4=u&hSY@A9gXwE^-XG=9IICDk84~48Tdhs%ZK=s;inzNPTxoo z@0e{Dw-UVG|K6p+#_eiH?V29a%TSo+*X)Q}yQB`GGnKC&^=)nx{)9cLjg3v}be?wh z?9j9sY}o{4f3<1J!DsRMeePzAlU=wjQ>A(i76yb7KDIo`(h>IGYFq-D-&0O%;^TEr z)sn!07L7+s0X-ywD59N}m<~A3MBH;_8D$PKFs;3DB4&{}DJ+o(-V@E9k1VytI1Let z+Nmpr3LO(I)Po#GRL;Ce<|=!qb4*?Q`8$=by0c3HlaAy}7=9Q&xkLtU0b6-0;^ap@ zxAl_x5lSsBL{JAUo%#KTdK>!B6;*lNdy^h)LQnDoHFVEO{>zVBqLFNZz+%`N<|fZx zs;e=$t(To$*EC<%6X#FW(u1l6lG~%UJN4Mg$+6C&<~D*3$?C~%GS-~x2rkj6Ctk(5 z_@9jbr@j!7AhbY0Sly1Q9Qr8VlqWv@n|ck`_`67n8DoQd{M5BCX*9#&)Ml;Yoj^X@rY<>%$2MW-0Czy*_IF`ORn`9-RH55D7w8~ujJ;m-J@APlZmrq zW3n{EH|y0+1}VP?kVd&%4+5yO?eU97FcwX*0q>dm)tWV`8A=+?>ynNo7C7}XzLCl8 zqPS0>?go2xusm(A|9Uw8!dOJqRE{sJk6J zv5wzhdFS?Y=S+Cx`PL|$Zlc<4bi!=@yK6^gb#@@tNP$v6As_iod!wq}s*&xmzoKb` z!Km&jW$!D9ZYbwT_n<%C=Xqf(jvG?P>k&1M!vZlND%67_PT7V4kw-KNbc%w+B;;M zOz+7Z3Lci-c23<1_>?dRDgO_Y3<~+k6}@LCaV*bLw&)JjT>Ge0t2p~8b&4_`8*-WY zJL;BqJAUPsm=}$vBfy8JTx|3|*^SRhK&V3 z$i??#rUDiY0V%wi%+aYIY#rEH14xb?Yet6=UB5qiTm)5-Bvo{Uq+;+?NkXL3WQWc1 z(tFM>lp#X>7oi;vsG)m0T>M;D3_QhzXAQh&h8QDG(d}2DR~p(foVOp!DR=}U!9&#o z$?Z`)sM_FPpO~*d`uYPNzMaWH5fAbzy;FDL>T-XP>~X@)Z~YiZ5ZaH6>0F5Uj0bGv zc6_+q#V@MY&c0zAOr`I=*KjG%q^9~h~v{^{+ITj&UY9pLIxI=v~bNWRK7T7v~?D?kS8?z9Ee+4lHF zmg5eyolwQ?q4q8tN$}up@10TK^Ld{26PsJnsW_NcA*K^2M;wD*L!>mXZ1 zJVQ`N(I=4>OcilFO}G+o^r6!?cLNDR-QBg3^IkzYKChZpuqjK0{;}CriH{wIC7Nlr zg&!j8^AsT{1(0?7*;C8X#*>IF?IZ7*6W0uB=(Rd>D+riOE7CP*33`a_kE_!FrPD%` z@HK%DFh|2FFi^^`(lnw3Dln);<6W*BpgN`SHvNO4kU9-eo-9O383|h+FWmmEF#g| z4}vjOZ)cy(@h{Hbqky^_NN$f>QYNW2B4Hw;^zAYv*TI{idJG6YNo%ts&5Es5B6!0m zzquPo5bAC?K~$|RbA2;mf`|RJd|gtXh-Go{$QS~&IRkYV92{jJ>j{8tQOHNWdbyUN zThVWYZp{>5`a|pls{a%sQSjmh;&k6;Y8$>F1RDU-Kp{%veXeoLtDHV3(=dOdSxu<0 zChRKGxfqlIReGY2=vs7;JKTWsJ0VJxN&>+#@flr57Vn(SVSQW&ny77Sc3fz9ckdp8T$5YxTq&!ZUeR`ERqnxf@6j>TX5o_iXZz9;p_&RUCuryGo0ZKN7d}Pd=vOrJMI%jFjQ0bB(S~~DGcDVa7 zt&~Rzy_q$yFhP_6WsyRZR4Szisn@R_Nj;aPk8$KO9LBw?H?6XBD|7F3^Qlho%$KC{3MDruKo9{a@6%;BiU<@^*;C`P z5XU`4m+mCS(w$?vCU>{)n={g|&-sP)*3k)tqgx`4!q5i+I$Sxzf(pmnY)(3y`+6&_ zgc-Eih$N22ii4=Xv1Ff~91EX2>Ji%(!%tU0^KvrYHG z-zej7Hm~mGEL*i*-4LKBm&(GYXJD&T^-9Um+ci*i)rp=Wd_XHu%@C8>-i_fcv z6>Xz)e3jz$%PB+df{=?YfD&jSANgus7x#>&Ded;if}G=Sn`In`* z6JT{3pv+i^5+mXiq<48ex=dQ55*K0yAmtUJga*X# z&Sg2iio139NxcwNNX8lQCOvV(`5+;o?4XwCkfjq)E-UozDWH(^=ews4dfNIY9@)Kn ziktgOcKw$-+@MuBolnl}+<1hGtiRQlC+~MhnM3(izc$GSM-opb)5O@vQORTLwiy4SvYD5 zOS?B*h7dp8H@L{U$Fv6~xhAUwpBTGndF zW+|>WF3z}%zjWSJof@Yc1P9$ygClUp5&8NT1jzq{a|R*!f7z4L2O+rkuqc8cbzITD zZYCO7S-@ViC7IP)Nz*cO(Sbt0x`R~c)Vufb_q_%- z`d39}SIsZO9vpkoWIHkwLN+4EYT1@b7=3MwG`@OCc@jUPTlca`sa-9cJP1x#J8H_4 z5eEd?ZJZHAr_yJgm|u1385meto8N@#A3%30LbT#HJ!Se(-D>$wD`?Lvf4*4}MkUbg zilDErtPi>Z@Ww#*2ZDa^#s>fRjlVSy?jNgyQve~|qL}Yg%0QdYe`@n>7Zc#US$l4? z(3jw_`aF@Mz<5&v9(#j^ha{A=>s102Ms`<{-m=+&*|n`&yBy6O=mcX?pFB8TdV4@=JCAO!L|~k8udL zx}RR9kl*0z(eS5z`0-a`JBh{g(E?eO@R8+PbQmi$RfIfj8625OASEIC38)t z2gnT7;^E(*` zOT=?m=QByt&)F&}WnaE-?Yq+T>8Qk4PzDlnJ@5P#ga7AGmv3|G+R3j7YcwP%1- z+5ZJVJx&)_ebC{oh}#=6K}|$vs!jU@z~rst&9zROsIvyBjspBP@hZ+H5 z!36;hFc=DeeY*e%^n@D-!A%s~8Nz(k89`r{ZvPGd9wJumhU#rP)K3}Qq2MQXpL!_n zV3``B#Rq$bjFacykD^P{zsa0u{=uS$X5>TLT%B+ z*C(@-1WoN@Ad_3t9Q&g`LWWzRnei+v0=JUFH z9EY<=FQ+VD@ga?@?Lbd@Qj_X6H z%QnPRr?eSvznWZRqMp1|G819per99A_&AhVZLs??j|aaAt-kAV+q@&gU52T|$#~-A zM*o{E7shOh#9zj&5cbWiZZkjY!BSzWG-9pr!Er>Td1s-P;5-^Z8>3-3QxT^7FYPsa zUVQ!CYq)C4{xd6AgfGl$GSfUU!MIE>c)!f*^bMp9?-knA2O{c~baZrfUtG-;% zNr9pomGP_dt=jkY&#V@$Cwf#xH#9IF_7EB#2N5c2QJh{Z)I1f>C@TNz6yna0{QR%% zH2_U5-!UuH$n&l#=?IDA`d0`EL>bQ;OQL6BCB5Y;-Ha=HqU{2E-QfX5%fwLODM&{e zsdEEaTBPKH(Rmq8lbH(XuWx>gdrkAC31+Wh1q6cbH2^*Jd9UH1xbhQ^K=&Ge(C6D; z19X!IXrzDl;SaELa{y7G?HZU5zY0QtQd*!v@RO9*l&j~`kOtUenI+HsNnofs*Mqtw z8QavGrwy(mgc<~pf)SvU)?>HSEYK5fAVfD&YG>H?{I65mLu9|ujs!H7eAkhH4!OcG z&jW~}Ea6~dc&?BM-)lvebW^`nNqenIC+o&L$|FY0OH@=7Xg@ukQ+}?^LHE+$| zFAFWEfp3=%Z+hDqS8>XlNa4}DnsS7yrF%`XF|W^!#%G-W5j*)kH@N-_EVSqTN41Jr z7Y@07vd~BL0=DwqeCY*JZXhH#QDJ9K7$g5BGIWT%2^t3q)4zJTpeJifvQX$e*=gce{K8pU?owW0 zYT+aD47vRFzbp=f^cR8uTm(k?g21EiU6iZ}0-ikGF9N^KVwxeMQ$e6-QEg5|$EA5r ze1|ey=Bax^?%AO3l&QaQpk1syoE^5lg+;jc2s%0!_ldyIjGhyW5EHJw%{+bH_xKcP z2Y-Y#R;P7s$nzu5j|d?&{09*jsKfnk2#f?pU?5-oBm$e9Q&(iy?O+{|e>hGQ>CIha zo}>{8kkv+~P&&o*6gTSxh2V2%72tp8lFM*afUBi=sAEaLPo*Y^w7!?@2r zZMqFxAVw3qJ(+1a1{NepmfBiHF^AWTq2Jol{kvQ~%ygJRrHNE-o5nlP6JL9eb?iML zLNANyiKlN@O~m<-Sq@PP6ANkaxumE%PBK`Rddw%_g6iOWyqmt)FT-_|zqtPA z;yTI~Tpwn92&yb$p=R1IuG>$$>s8!Vqn>^$ZS2k7I*ZaqcGt2z3_Oqv0o|4op6_^bDGRA`X zm$Cf0j0N=zV;QJCsT=OT-)XaQ?PH7=EAyFK z4{$OSDCg@(e*Ra|M}a!r@5WeAp^OE{7eC2Zbdk!uEt!W;Z0qDacd9vKqmTFrSB!rL z{X!jSJHPau3^3|m#sc(&8ywotGnU7pRb7$F!}quEcwxl1%hojA)))v-WPbiZQgYQ! z6Kw13vU@xTNN{hp2_O)Lf%g3ZYl8Zwm>>No`^*^{N~Ce~;;dWX9Tyv2EwX4a{)bfU za=#2%(f$JKp9`#LUx2mGAR#B@mbYZkeu0(u3KBkhpdrVk2Z@i?r)M_%7cyFEf(S-XqBkpGo&V0_;duP)aQ!8myWVTOWJ)fXjFUF`?@XVDMUtjRS)17vc zfMI$8d~=nx1d*=R>;J(}08oef-GCJh3amiB_(@>py}tcC)om_@1^giN#wEEMnC!Kw z(`gTfh`Q2!s4w1}0;BB(R-h-`;Lv{_STn88QLij8PO0FAzuCBKacuN5ww<>VJG?+g z!QF?}A;QjSyTA%0xVP%W0f6-YqWi^QFK^Zs32H3#(AIMs2(QxIv9_%jteKbu7iOh2 z>?jp$e;Ed&|Ha@x7lYBiU~msy7DijaTPDN(VsK4Kc^m1NNxk0Y$IVX-CubwNXb4D$ zh?|TCH*Rw2Ej9hc;KN+pj`vkc`ypUP>1{7Fc=+*!9c$FNes@owIaQh{V4Kbi)wxSxLuY6mF}=>gZ{&` z;AyZ(vP>5keJ=(BJ>dq2@$(pLLG4J6*C`cnnhawrz~2LFnkcaMlCJkm+$3>;&L>H> zZ}%hu3GOX?aR6ZbcSQFK*Pu0hypCMCpioZ*w#kI6-bxzlIL`T{!QL3brc~Mz<-ZKB zG5*5!p9|L*U*Nhk`uUaF@l*%R{lfLr2@{-zjJ)~_3Hga*)E#(USHk>XHw{SRKgOVI z$o5V83)hDU*HeOpGZvi^ZVCH@Ypf-?!MVY;%1;5KWc4>ez9$_5${T!+W)2QA>v9=Q z+x!RN8mPnlZg7nOg=-*R{3KjA-W69*_A1*LvdOFuH;8ScKKn^2HlDBR-j0>Lc~DFm z7-KJ713lpehxzkxZDMoT@K1}ZtCoZp}!2-G5;d_ zpNs66Uy!|BsIuxk{JS~#{USRi(={984~>fFHt7+2rAf|t9k=GFyql22YA!J%<8!>~ zFR~vdvcHeY46u5Rj_a^bWWQETBR-H(UGIvdXDNx25175A6J?l5ygpfxY)Bd1vSJU1SFm+;s@Ycf9$u9f2K^sMZGw;!Ey8a)3K6< zjvtY|Lrpb;+cNzeMr7($TaPdCGno}YopYmAI&*(~S1P)<=YioB{b;~|tgmxb(QTlo`5J=2b zo$CK}@6rFj5M_><7c<%tBK-KnOYV@swu%PGj%-{FpPAP@L<%ne9z?=iQ3hXMSrYdru3IY3*1Xb6;A01b}cjapzssYTF* zH>nnn*M_`w7BF;a8oZmO!zr7J-=10#OTSVtueMd7 zDHs(i;QE~B2<~?9JIwu4i%%Gqs>?c6*{6KCQ({~OTaR@GI+|Whvmxm9$$V_g8~h_b z|0~fApjY}yYQYd~5+>iQa%9YCOD!STXu>%V3_3HCgV=?J+_OBu;|a!rQVZx!i9k=d z!Qn!3)?qJ71Ul^wvji8bbD$7a+eyQ*@`(@}h!R&z#(8ONV+MXG&i=~gnEf=2=#h1k zGhXWDop=cakK~k*=oCz+l4;vkoJTZP+HM?eI{S@<0O|b8LcUz;4D)FZz(RmTcMsbG zIcx7>`~8v|pyBzuksBN+xl#6u~~=ELm)xyO34VK(6kGVI8LnH}=cO z4enoZ^XHNq+%M#&{@J8pQW3*7wf~!O6UZe_PRJze?yP?ZxtZxCr?Qr(pLX!QEPNKU z!2KpbkZJ7XD=Zq8fpfH#ipu*ZHv;C)YWMlfgJhyRiUMT9XAPA+kl&w8GPJ@=W2LC! zxb@4(4bUt7B)P$Ak~WkzuOLet+#c)NhL;!iuzH~K8Rusrk2Fc|MI z2LHJjjQ0hDYn2xtquKg|>ipltV8|6VR8Q3#)ENE{44yEF%HAAV??bymho?cn9>4WY z+C`bc)`ewSD$X=!V|M=-JRQ!+R^w$EDw9Nj+!}SFokmn=OlvMVKM&b0^F->+A713} zuLKx?Ug;+>Sg?ZAX!CVSSjPkQE!Clr3i))EQ$Z`eGQ|-*c8x}MAHjHgF&O9xH#q!X zg27@gvPg4-3@2vfhvpDW!(W{ zjn{|tHSgjb)#Z`rR;vP=4-~eHv+74SIVxRNpJtxIX1MGaOd1(>@AGBRF6z4Jz9AFu zK0L19wys4-^MBldK+$V-mT&lmALZQ<25cmjc+)xyQ_5W4zD)Np4t$~vOy8pZhq@T! zGMQUo*9Z5V=l!r@$)6ct{SC=qAI#m)~mfxigZ9+4-sR3;U&wZV8%8Zx{UlX$Qs#+;@bL6%pwu6 zsc&4O$GO)jAw&zq8vXCfweBv)D4hUvG4aYy9J|-wtF*ua#`UKnbRF16wcZBRnL};> zwED#5Un=P~nQ}P^yp$4!ba9B@xpSQW(W(T5TV(SwA$!7xrg2{A)#tHjFjX9>cJKTckf1r^t}q4 z=3S;pCKu<`L?fjE!@0l^PXDM)Ec$eso-C(YyOoPiCXTRF(1qiWb;#W_!^)En6qNvh z)%)>b9snY6Frq-0wZn+sv%o@qBgx}Y=wck)teQB--l7XSI!Bi#p>4B3Q_&!zt_NTK zT|Up@|Ap)lCE*0bj;E<9i&wc8c~IxLMEh~!0@Sw_(elr5&Ldq*Iuq{}rHpAW8;3cM zW!qk;Vlo6*setG?Dd%2El1Q41o!nCGXP1oh(bPO$OOT7~SV%%LDp9Xc!gv1}gLyGt3|aP|KQZx}>3rOS)l?VyrwP?j23sf?VNktW0~i z`+a=9Z{>NYc+mx&$P;KEYnl3_%q67Jz8`LUqLlbL-{s}bnew+RM7;Ao=588r&dJ@_ z?;3@BZX;?7t|&6sD>-=zsW~Gymdp$%P@OPZTMyK5Cq#Vu&Mm%Qozkb!N8L@({TvBb z;t7L|Cgb6BD#p5}M`n6%iVWRAvGsdILwEKm_SK6=j$TQ>!E6o}qn>mV9Xx99o`Qoi zm(^&>jwL*C>GV$TSwsP)bmh~`@2og4tDn8b*7t&*xGk8l<@E}#bd2(oU@my94_

vHSrHL`4Y!))mrU|+My=J*?NCLvdFH?W^2AuF;5 z;H^HAd%^lZMQrJb*-5nbPf&?pd+=T^*X121D!XTq%{*6JEFWU=;F+tYu?UAjkMY#2 z_9sGRCvFW^MH99shL0Yf>5~Qh{TcKETfSaucsJA?dq_JPhI zPm<(TUpm*yla4 z=0y*|Q`IdP?pb3TiYFo-X$Ac#{AC2$7ghqnsemwzu9~v{Z2dAwHhG9EZDazpei;a& zRq?Y{w69vPnqR#MgGeSEyxr25sUJM29N6)7FT8xxZ*ysx=x+GD!*!Ywij>XW)rLOk z3c#+lB^6oOWtQR)m5q_BOj_65h3>*V2%R`fu8woBdnX7z6(kRW)76fe@?^vTfp(W_ z2LJetzcugkruskoc!XaT)8%ZK<*%I);z{X1R_P>}O9eP*pICS4EmsZa0;^eaac&EZ;X>`UEO_7o5)N%Leic*5Ih^iR;MoSu9bOhPL1GBY<@NAIXn6z|P1aigxdofdul_ zZ&!i}=xR#ngaR}re>b5V;JC^GL=SYb1s_Uid4kPP;FswPJcBC{Sw*}XyW<)jFPM&Ws&D)LwIgU z>jz0*!?enm=Obo=PoKs45z+ZeXn%IOwxA!`C>KiB=DlA+yV{JA9bmiSMtyQ+FeiIj zI~B8q&x)(PQtvj& zr`$^BNaRt%|6$!OnOJ0T_*|R%$7I2Pglrt1fkhI#J3@h9u}N7ZQTvsX_hHn zAt$eM@E(UD$3RDNw06Vd02=@QkNP@;K(4zJdI@-K_ZQ^QFO*@_FI}+r*3}IWvNzHO ziKHC)7=N?n=oxN#oJSpw($rJ}zl>oJ{$&_{F2f-H!Z3=j(RklcOFjR1zYL@OV}DZi(?o;uE=|1lzh|n4!QQ_eb98cx4k-yACGY%P@eRaDyZIB@Dwi%j#u=1OXNj zwcjm{<=FLk%&`WmVs62k#8q>7w_d#aR_+T(@V6`XbpVEOfEW=F8M%k{m5pSfOT8&o zRVl-@tXJcPLx>1<_7k$ezJKy!u~1Yx$ansi@dcuN^93|K5(jqA)0DULsm)rV!rl8D zGF7nB)`cHE-0Iy7pcwcOiX6@s8>CtSN8js&^EY?bY=5`dKwo=N`?aH_30AUF2qWZq z(v7W3oKs6h43$I=ZqL?>mYz#DB_a7j7@ngr&od;(B!v7W3@)y}U%jt`5(c#yDrFGI zH149}eZE*K+-R=UEzIdaA2Cq7R#tjUndN_wFaXUh-w}obY@8iH^gw%T5YGH{1OoZu zCkaEKfg7JtN*?WpAc7AL>f~da50D9oc1~2|y=6ZtlX_+nOthCU06pObNBm0&gL8E- zGNJ3TB~g}8$#q{A+mnt$;kqSpx)|t7!(5lnWD*I7Gq#qcuQ@ zMe*sZj?4w2zE2z`MAp~o=&?j*19|iA+8s$KO7`dZ+9iR+T*V>53~1dlNH7CRNg@cE zBUF4^5KotTu3J!25(JY`waX;f+rF;5;TRYw+z^0{r^2m0;#glpcE`#5&oBwSewJ=&Cr}c0+KT$Yk~?QMaA>@u+_eyv2zrvwZX58B31?h;6%>2+KS)V{=9b@$k`O~F z36L*-l9C|QkrOb_Ny=l4Eaomx;$)aonX65gI6uaqt`_d4q`3+vhEkHOU2X*Qgc}?Q z$uHp~nGd43{B@CJ#TmAXD@w4|A79NH6zj-}m`{73zj+1m>bLY6Ai>`*edd3ilN^BQ z0P&Kl-SZ1Mu>lvW)WSG>BV9QSvRrSi4Y^8&uHAoR!bgbHWr^hpJsXs!?n&Cm9XD|| z`#Mc!VE%iofWr0cSQ~LaZ5AxeV_^w(^tF=aehi-~@7Y`3WG_MnEp}ely|8T27p=~} z7#O)tv_r!~c2a`AgjDe)TUzkAuey=LtI#0EH;Nb^>sFr}|9pNSKtm|XFO)XvIu@b3 z3?<@y@(Z^D3i(yUH4A##6ncq5sFe|-*z#6c&py`%4|P~LCDOM&U|MzXoULgWcX;Mc zKd!HhM}>HyU)1iUVLFkC2FVNO6Sr6DI_!2(ZyOUYu94u}o6n!Gf#1aAKm6JX_$_(ZG8SbC#*-=HWNj8Z#e#BpSH3 zy`(CckV#pq_3jQYIemxYw%uK8eMrrqFvn_d>Pz@4L&5xt@@?lk&Ws#mO{J~~_KhQ( z&)CQ+rBvjXH-TeM-iMwHqq!f!iKnP;KBqT5lABcj(i2mmeK@rtK4ngIJ_CL=BW}sy)NRn z#<4C}Gkk^FN_~06ughWLmi02;x&F%*GbAMYUd_-TH2#zjdhv&M-X2bX6;f73f9jgGZ1dmM%mG(39!jo%v&qqW<4p%?v6{ zsBE|}dA!{1KEGQv0^Gq82ew% zP`{7x`$Q0Pom2Q0yihx>Q~pDf0GvAAiAd39(8D5w4?hY}9O@y4!d)&7G`IY2s~Hj! zuicgdIpZgnGsc8ZR@?Ke76lQ?EX8IB1WmWNd|YLwhiUxo5myOs?0`u~pzE3b-If47 z0$Ivspp13!T1fl|gvNal8zA^lHAucLo5 zO=QeQUDuF%7NNU&Gi0Cy&COU*YLKr#{|ICm(hLtdM*SqjTo`=CAx`8h4-eJ^Tukr< z2AVBg;TybSa}LW^!kuPn$0t}c(`kImL*$ETOqkG~T*0Lm${=`j@`5)u+bxV)_`pdq zJwL*e2(LV<k^D@NUqE(Nl(YS5XL#BdLO!uNzQT~IF z3iS?@&4^j@ZRb~7G%Z~Q;m-%$sGJSk5^eA2B(bZDymTn>w&xs59Ve0DX_rgV{LuB3 zhxrw@!!$To|9*F^jdt{?;0KXe3D3(QWR!G_=1V=?(}NhXG>y)JowL}u%(0YDZKy9y zu0I#tZcp@;2-`*uFXEg$FO_edyRDA#y9K6SD(4OM9pj8+rKR$47Od{AQ{|=QlXNuj@XddzYL;tb$MBvKy7eW?jj$N<6{gd$6RVTcDhZ{XV{u4y~y>NCG+7M zW5={ELyl3Wjgc(boG|qINbEH8Vz3JV3Efh3r{_u9` zUWNn+iMg*xgVxtDyarSTjHb@DTQ27_WCY}m8k3Z&Vpx&lAbehi5I*qWCcDH_AE_8h z#_uJ4&ENCwN%D0QOUuoNX-hlIvZOQ!aIj^c|e{KpX3FVg+OFEVMT9@R^)qUrX3bZ_KUY4<}detZr zCf~s}>AF;Ca%>=g_4;zdJP!T3#gCZm?|VvRIB#&K#ol#v>o;AotRD5=)?j;Y9I@Lz zpd8Y;o8H;?NpmRe2D9%@CRA29Y51}d(h}&EZ?Io^aKu_~|0|XX!t{GWi2gdsl+RSF z5pd*aCs!d^+S+S7)k`>DV7B@9M z-S8Y&^xZEPzplAWC#x;-0)^KDojjHcBkr|Nrn|c(l#p&jx*G&(Bm_hWX(5}@P z_rWhZe4fqrKF5!q8~$UAx%RmBUh7)#*PLt4xuzo@_x6!75?0&!2x@01?_}WTJZWIr z{;Q+}nA!IwB`0|o-~OGJl#GI(DJeO1oq)ZG-@E`I_(LF=g62Bmo(5m{-DvI@ip&j| zx)eJkysq}Q)z&`hWtw|h2m$pBaxc37*P#sF1ZCen>uV@bziG zf|w!Fz#(G&dbu<@HNLsE;(CowSvts!!B=Zp^ii3rr7-oxjtcm4;TLG$LzQo}lh%&$y+&4Qp2z&y9DQGBzcZxE= zCgUej2I5uJHJ-?xCj1x9sgN|*-rFAKH}hdcU5z*78;|Hv@F4+s-=hq$Atxw<|GS|K zv^C%Mn1-KcljSb;teuz-?2-a{Qk!XsW2SNTEKUE+*RHd`3{SFTJkQ?30|Lr`kRbxB zcI8yw(Vse9r3i8A>(#0<)gzFtn3`>*;s_z_dN)aKjmr`7`q#-j{)N2%i{+j8i@ZP8 zuxJn6lr0`Uue?9sSX!Zd-|N>NV~XuEvc^%`RZ9Rd!876OaJ> z@8uoXkP~?)_}%1vQ2>HL(+bc9=>hQT$*v*nkPYnh#|)7aBxu+Agl^&CdD;*RnBmD6 z^E`WM{Ljcc2&tOqB>@1Tixq?_uIekBM!&+wQ5n1%bS=J&%Tn*8?{~z@ps@Tp5+JxB zfqyXxkbEJ5^j$|@fr11(z4MYl865t|R(Rmjv$2|+O*yl-LNFXIWyJC8#uwnHh_iVvo4Rx(JY zLjnlCCjnqXPDp_8cOwBR@+T7x6D;qwZ3x1>8oboK#YcP}M})fs2vg8)V0RRJ?ZXAk z@cTYo|K}v|4?u%b0u1R3#+3&lmy=KyT3$Be`tR^8amw5&igUSy+oN&)*Fgi}1vLDN zp@H-ZG^9p(WOk^j^zxk-8qkDp%`$q)bWgUutNSEmnsD<)YxTxk8n!p&Ef1T_{h}_Q z;qMF|9OWCy=n;4xoF-xbOLX`kz7IhNgR1O*BN<1Lp#O1JszW=c&;FIwR?V$&hFS4H z5E_6jxZe&M2v4B_*kt@9G>{VwjwD(#=?t^=Jn!)W)4b18&{YJ}D+NiD2H0OtON0av zeh&@6hMYhH(eDNgG&#X<@SZy;$&Bx{SY)H!Ag;|9#I728G_8$ae7WK_)z{Wqzzn}{ zt@S5C!{5jkr}d*u@f8M3JMz(+hk?b*9+9dNNJy}c^GdsF5t0B*sm^Mr_h0$T@`-Dj zfR%Sq;dSscIZh|VCg<9t~@4slrnLmmFmFa>k=>3j|k(!gP z$*>Toy$b0A%EGh3Tic7868TV^@-kHfl&zih2c4z0s?Im`bGSr4CvImbBQv2KaG*|v zDR@P}Yf8*kybE1jcA}!qD(Q(nIc{#uiqJCg^sX4jhmjMs;irj(q6{vzXcG@#Z8Y(P zdaAO+zXS#Cfi%??tCsB2Jy;jv==c6mA5Rvdi$Sm~Y4J6d>6+-f#?YVxr1j4CDe#Iu z;enfh)Ncf?2|3(QV7M{B9ot)pdV zx~R%g>Qe3z==9q~ZdgxiRI_&c;#!`9g(!!;L5Q>%<&DHG&vpX&CE_l?{vw{%uD_%J3Q=_#cEi?dLj6$Ni z!k$!&Vh>~TUul2xPTXSNVNh%S1HFwNcZ$JZBbO?@igl1ZH zhNo+*apvV@C-OD($6BeidR0b(c%2Y@EBFz31WJs%hB|-$ryibS}$*zWiQGSMPA+-Fu(iJ{5|g{wA|FmMULKPPUJ z=^#u-wfAwW^snwh45-pO(*&SDp%V0K^28go!|)}*h5ELB^zL!WFq57oPnYTPY;2}4 zP0PKqEL$a%1C~2fl=o4H0!6@})s+E6XY!VSq4gQlKQ0>^r|%>i+t0q*dHw+v;F!>d z?|M!ji5h>R^9Q((TGNr+;RSNxVMEH#)oyl2v~OMKpX21mEkwJCuNO-JfQQH=r9!2+ zc@VE!t=mnkgxw=&$fQ!E{ZOuQX1A-FECN2)!@UD+7wKy@_L2GmpAf};-UIX=zxB2l z@R#)ur>9ss(G`jOhBuYHul_nGh3MQ)%29)}g+4j^158^+E|DzR2V*34CiASXyAx+_ zJBEXU(f^2C|DO8MfsnmT=br0P!GDE*E*kWmw?QBI7bhjLE6lslJD#uVyiN*~GkGfp zp+x))WZ4xdy>Z0}t@VSjE3|&t-teoJstB-uG}RZ8&7X{HVuGV;jI8+TNLHUSvWY_} zcO05KnGlRfpl=P`sLV9$?8&&T0xO&U7ESRoYV9BBqyXDnzHw4O=(-1jDM$_a07R#0 zw7_1KZ=M{4j4BXJK})_OI(4srEu)`wulx-cQ=vrZFQb;<6II~!ZVd3%X7$BR zhI)?jq8AcC^u2opY{-dwMf|(DSMdUEYA!Pw1A1zou2vzal^NiG_pDo=T%=W3g7=sa z$M0(+LSTmfMI%BGlCMB;g#OP>iOAae;xzIuA+)UP5b&Nrmh?YrA-3?`XS@2MX&A-<8%NW`Wiv1aj`bnV@Lq;_hbWX$O+ky{BC5!f|%LDkjNsoU@d^V zi7UbpMK2B;pRtgqhW~mXZmu==Ydb?=hW|x7!+$B+084y36F>tNWk02s)ygO)o3L^k zt6`0dS8rk`o=Y^R&d<>5M%>`-W}LYD@Yhib$+@XTO5}z~hjB;Zm`IbbRC1HeqFN#> z|MeLCO+Wh({wOB%e~wyCT1jEBLxW8gTu=*$)I#}%S{@^(8E-2sul%PkwISH(=X=@} zK3_+V_@C?OZ<$*e+nrg)z}({>P4z`2=~qRPNEEa5-c8 zoN2@MJS-zd56f~GEi544UV79`F;Q7>J6Q+PN;?SB$e#!1N}3d$Uvy;f1S&0iZ+Rg#HM_?syWqm zcQy+}#9dSE!W990#D0~W@K@~@Yars!PVB#fqBF_1kYRhA~ zuYd#WGcN$A2TnNv>C8_H0^k4(GyT;$4%lRU-#PBQ+!T~jjP3`{9=y=C-A+FYBO2!R zaQN;-21MZco!5vVv)aFon@G>kO*^Ca=%TtlLfj$TaBJimd|~rq5)uuvAX%!7GvaX^ z^N+;z?}_%DG_-kgwSsRz{eqi7w+!URg*T0Zdyti@@?fExEdv=F!Rojm<1o{03gfK;Rz~b`LMT zrsL;?1dx8uO~8hna1+@d%uPZ3BC68c&ma})AKzUff|#4Xl|?jNxO&uAGBAtfbX)ps z>m*=?-?vTzh5~11<-GE)xU=Q|TKsy-#*mkC)0h{O1!k?sSb4A$ZEym5=vaKmuakGO zbIW@}kXupuY%PW}hWyN4bdwJ<}TxJqS-k`#Tn)8p(4UYicM(H#(gRkv`fEj+@ zP6z~f2f_5bBrrbT4Q=@rA!OP&IrLQ`cCxl3E3HW@!j0*@b|X3Q3e9U;_;?~dI}ggI8WZ<`s0%G`V#V^*rXMFR;S|DFVZ z4LKnJia(eHKB>**W)FXwZq1dlO{2iew?(_(>&jyxo)h8{P!1sH{n~j5nBhrk%Qu~e zK#=!;#uOMdoEG_!Zz!S&C89zXL@wgXY1zZ?jm18L)kevwEUOh_AcR;vy(IFPZ0!=; zpgm?^j!u8I6q=~-@J+Lkc^YYUI(8MRta3*Cc;ycgeiJpc4=B@KfDIAic3Rk&ILyfF zIA+F=@yB$RG7h&B$%$y?^&& ziHdQe&I~CCBeiNmQ$8=NFx}d3Zsp)^sj9KyrPdBa!%AUqxDQ+7>ow0A z@h~gZ9o(8q%(db1q13LFqj3`J8NMj^PUx|lZNg1aFBszjw8IS=uDF? zV2yoZ_R#Bt{_9Wz#&b9P2(E4I!iGF_ZS*hXF)uSCFLETI7uXGQ?yFg+sB`u`@x5YBgjXpm zFPE!Z!kZ`Hy7KKN=8wH*xdj5h(*kF5AbBXM0>C4OUhf2awh6ZFlpuIea30t|-fKxM~RPbj0%h z0NW?S;}h$oql~a|Mv_mqM}3HwT3sx4l3K7LYFbEYno#8f-@?xPS;4nC`*#K$lDu?D zBo?SY)V;imktm|2j#ojMSL<7jAS)N(@WRL8pmUq>ouW&eXWol&cs}#&5Wn}=6{yf5 z<(R+2txrmPFx_Q(xcJt&!!%q<}DO zH4`+DMWz%`3avmdBXB#Sn`4z)f{vZbYe#P9I#^27$WzdJKhYeZN|V^@p)v^n(oOAI zwI2puA&&KtnqEvwSln%FPfQi=$*gVXZU|8?yK4URqnFp*YpV4ZGv?rgP$FS)_VKNf z?h-#!634+plh+Q+uSvwHbFi0^qgNZm)*ShKNhvT5vH&~51}Ku+dA3FJ9zPOeS5{g>3?`S zgkN5+2NF-)jQV7Z7t!dU-DU6dwzw5HUmDr6>#p#O{-l%TVOC~ozh1o`%T9+#{o+znowi3g*4PfX79-{d5(Gr*;=R!lRHUvx*9oo?)PAj? zeSC)}s)BTNnd6c_?Z`!%Ri9_mpN^C*_rP{y`F@>SPjPOyUi)fQ4>xD2Vb!c*6bL?BUM@+fm?>IeKbq>_H}CP}e8`#G^g)g7 zU(4q=yMYxG(oWG_&CZaV^{?jx*5*gkfERAsYWioGZ~iyy7?KT<))py%K3owxkaJpUtIf`RQVznxo8 zaautO>{a>Z$w4TK1;G@w3R;TOfCR8*^pgPzDD4Kw&c={eg4@dwTauSfB{M`Z)AS8DdUwTQP=t7fkrcs~t^yr3lV^D5wn*3$_ zNdb{7t=E@JzBWJvW_a=&c%Ge=eF2VRnFEk^1825wKpvLM6@6xv+RYTF{ zI%Z<~-LW>O?-Q^s>6`Zngxi`xFa_;mCyLWrmS-&zc~}SQ`CX8B)TGlT9+qp9!#k{Z zIF{di^aB%2NB?z9M|r_?|36DQY>4n`jp?eQl=mjgX|!y+uRH(`i=LCiS3?c%KZ-!i z0h0y;*V9h?;7g4P2KJXA8=jPjT>o4qa_K*1BKn4g)}VpdUy^J9_POHZzxyZv!xyIW z*PD%S#EE=#^M8}+rkz{C4g=LJPj6QE)0s{W!D9H5>APT|FtYA91+A7-r8^EqnnQEX z6^}%iD>xU=&vd4pVa}v-D>oqo|9zV=E4X6PlP>*;$YLi*2;z(4b; z!1`&N`C7orb5BtO*qij*p$OF}itypl4?YZEG-BK}YEsLz*INbmtAZ9*zWLVr?kCqk zxsSgNMW`-NzzOBa>X&pW2*EtURx^)Rk3LK4(rEyfb*lsd$GikMs&-BwjT7f`}gLn2K|!^Frcx8 zxY614r{dUwseM&99)Jhv~m*mtrkv-qR5$EG#97e`L zDjD>oP1D5Y3->+!b<{$AK`sAcYGL|9E#C2a-hnX`6z9%$1s3W5k(&L!hhiW2%TeYK zT1L?SigEqv)MDF~urQvQ93$)pQGo!f5+l!L6erx<(pgBB$8zsQasK(Kg`BZ$AS+N3 z+z|Wtp%TYyfF)Oy++;A*c55^)U(L2D@{i{EZ#@({4WIz~N6C_#DStxZEln9@FdadgZTNb6n1F2|#JBQM5z zuv4j~WycNeJ5Ck7)(K0j!dpp4k4MF*51Hb>^9+LZ;PR+EGXQP*JpxIbiEuL68vj_Z z=t9TVrIL@g_n7*dwMSxJESkRh*z`Fsw`^JBZ38#aPPGy*-st=GmFff--26xwMAurX z8O6}NJE_7_OTuZwviFbga|T&!!ljz-CGw<*q?@}yZ=lFNh9mE+BGEX)^-|W(yrMuh z$q|pQSw8Y<0Xp(s1X`KZ8U^IgttDY3{&%j=xhfpyjz%j!ZZ*s6v>#mMoPunEm?h}B z@i1njfA0m2DK{77Zkk)fYk3yys%!jihHE^_b?>8gs96*mtS>Q?(~exDcI{F$AcZYO zfAVnOPx<9#zP#&A#nie-;HqxTZZ}*_C}VTedg;UsU^Zu+fn1^AOj zhBb8i*u3?I@-Dcd^)4ZoZ95dg+;Wgmtqsw;g|ayGI^68_f&!@ZgZJ0&1&_?fbMf7m ztZHGDFo#SkI&5uhAL7{zi(+2?gAGlvSJVBZ($?{GJW|`+t&XS#TsWS2 z6+WS^oDZlVi`RUmUu@IUw79Gis;<)FJeT1c-IRV)LY@&~g1-k)QENcqm(D1WyLw$s zFYjt`9&)rLAtNg$wSq@RgXT(=I4P@9TgrCXB~nxfO>O=fISH;EQG&gMeWH=Fd*H(n zF=T>6BuqL=#w=BbuZ-7g!hF;%m1|_L%vav2V$rnIwOSTRQ@JHb-B!8zKI3y<;hsWq=XWw`vx79&#Q zrR1bo68TOA;N8Y6{YQwnNZ+Ooj<-*9W*a6v)ZxiQZ|xT`k8+lz&mHlp^3?$oJQg!! z5&?syi3iZ0**Af){Tb6gE-x3i`6Ms5h=ZZ-^|Oh(_`O(EEmv5q<_JYcM6Qe{LpRAX zM@e60lrYo^PZ3Yvqa0eOHI#44!Lh@3H{!=slCY+E>xYOOjr3IhX7+6_wG=y5F{6t2 zyPRxz*wb@#?3mR3avN4JhQFo`28|NF!O^>e^0WPD0l>AbOBo!l4Ih?c#~rape2V?HS~&Iju7oW^*G9Zb z@YwDZn?9n@cM!tJe&lJtLPR3*@_OI-y(EOC``mQc$WhrUX;g`F_eP{r53`9}JqC1l zRTas?E+hVEp8r-a=@bWnEx2!R5QMBc5KKW!z@t6&?|@ClPx^Q89qWCbZOPZ3x|2pF zz2bXlE6o|p(<_)Y&&=Y=^!5P?B!KpN{|?xY6aS9(5BBd`IR-HWn+Ne2^mT_C_$ofy zGhe2~dYPpgFV~Z6HGI|b419$7(^ecS2#@i$*Pt{HeW1L9xGaEMYJEjZRZh8^Gt=a& ze+TR{|78CTSe@aS-wT-227>85@c$GZ0h@yVqW@BBN_XmH)9$9(bmZgSJ~BqaYC9i6 z?d;^84E&rY4J_NIdkV14?VCLXgv>$^OhHRRq&B8NtUPqKf>88ZPMYXB%q6ab2|^k$Z2%a!xv&eM~sj7<6?TKBp49 zCu=|U&Jn+;#01L$VX`R#Dg|EYA4rG57Th;GIS9xHf+=WpNOwwyz$W7-=}Em-^R8+swo*H!?@75+!ky^5Wt0E;h4JX%t(0xybz=oXAA>IE59TGyOXvYjTE?!P> zg14J4A8>}gp21>toE=;JT5u)eK;dhXDPV^GMUyFDc>rgK3s^tvQ)p0-7}ZVJLl@fc zakjNm8X?_kq$oxkhK<`zuAWNGf@k@4&_H(q4gX?jVEY0Mcd&?U4BBFwAkGU7B8ziJ zmsjtN)$j*@j9GCp!%!3`L#ciRzx>cFt?J>v(gif=vwm(@W%OUymOw~@VIstE>}XHQ z60n^U8X%0}qc0bQ3t4YLxOwlj%yePkbXthkBsy0{33$Pc3jKl50BphicF;h73Jt&} z<0qk^DHnsRoN;+y1sV|GT-BsDG(3kqCswqlH?pyV*6UUT384QT8h{Nsfd=~j4QTij z?Ei$)0pSR1t!cZq!uvQyeY$t7cz+A0_kkqMVsYR9>qiB|@W1Fs1p+jH5YYj~7xi%e ziRhRceZ0g1wSX09v0Sz%pfG8hA;s-#TItn$zgN;`jbr+>A5|9WC64xWc06rsF6caR z$aimqV+EY}MTh9QkjqLLbmCaX--3se6Q@&mJbWZ*@J1&XbR`Ji7%d zFR`ga6CPDgm?{4Cu#24~Bg*SHIZqp@hh{YM%Ev7}pL<79Ux89b&XQ)Sc0{>P!7HX8 zu4WJ8Z^2I%y|oX>2)q?ivN%<6UzB5dupG)MAT>%a4YGWef!GQqsFX=XjF9MobOf~} z(vta2xEJzyto`O1_c$V+>xmd17a{4;2_AH*ciDP46{&Bu8@7q1&%E?r)k)ILn{9YA ztl9B?sEiL-G~k*)u64fVQzI7qASV4B z8SOp~ITYLm5+KV1xsvUrgtpvbp2n;2UZvg}Y_$nO6>D99I%yEoyt~M0<$fz!6rw&%P^M$%-uTLR zV-11#IU#QNNlHJ2E2-b9I7+>uhW}M=Ms?A+&(%BOlt_%+oGW?9ODb!@dOCrQ;s`rvnv#Dq)o?Wy;IDoQ?~C>Eq=nBp+a$OHV$n&B*e9fJmD zr!h)~r#4WiOOCyJ(#bW(c9oB+gyjXu z2JNqJU_XdmakC|vAjsbT3*ijFOiw=N|KELie|~W_#}^mnwj!Gax#ykL((}3~-E$Tp z!)#R2)A#YEj*dGV!Bk@;gR-?m=EAPE!;NEb{b;H$;+j7j*F^e6?{es;1wC+17llJX zis#vlH}Zhqa%cfKwVUha0hU*fE6EKCj|!fNOeFq+E()-{#E`IM6&8lFX@-2K=3pCfaHo3Io$6_HAq1k` z*?jD9PI!8mmX;+i4;}(aySUNf9&K+tiX)r5DWSv+DjNb{&+CDG=AV3C4}#0~H=5r6 zt2hAI6#N%mi~bq66@=daSSjCW1l@?4xy%SPQNWw_rZMN;{u@mRwg|^5=7-@;RQ|W) zdw$_H4Gb4}`Y(>4bAG{7`xU+g(dX=kGyj`-`VlN0d!Dt3JK`_=``t>{K*iIL8~7i+ zXxB2A$E3VqxdYb_Anun=cjPl(H!)O&?PW*SKV8fWDO+3MhYjmx@?A zst{-0t(TNFvCjQRZph!7lzECuz`oK?qEdoI!5VEaOYG)=4GRj)wR@dQyU6`k z&K(%Gf%%XC#_v%H*pL%cV*KAgrBFLZ+t$KM^t9}<^o%mlRyRb3J8RwsTcI`?4n2f{ z^Zwc<2$NedEx8kO2sSDQL-pjHlGXzd#d3#_8bJCqxY2K^J6& z#mtI01qL4h)}24l9*i9K>w;>G7u51ErWUR*)M6VfdQ+lbZan_H)S|SjO+mjy%LASB zIJKA&KoJngF&Vptw7YeSrk@)&xdgWyonn!9(HjCMDBal@Wi#aB=T61?+6omMBW^Em^$YrRqlaGtRxe7 z!9ss03t^HV?)3$)U3wbROmw!p?k;Wg$ zLckW>Z^uF`rz`|)GJcYUQsKP1oH^LN9i)}bwm9f9qS!1m#*%u4_jNKmu4! zS!npL)DK`oPFRTLBzkeSk@KI_7ZO;`&>3XuoOTRWwSx3B((V{+E2ucc(tkjQRffNN zpQ{dimv>v>jHwN+9gOj%eSDb-^v1$V`DnLt*<@_g++BZD z;{u!hPHe)y5ABd+olEWUZw?0!?gt;}aCyic@y9(O?k zSii?6U_(x@iS+`TzQd-3=*GTyrLmAh4?m=|Zpr30C-vb_dD@B{QPC||4M-QyQ^mF; zo`9$?a%=EIb#E%Wo3z?01lXs)F<}~*MpK78b-C-;DK_hcV*iU3oA--in?YOH6QzfW z&z@JY594ar=Wt*YQF~Z@l)YU7c%>dZOtvX+M4;}g!Y&`$y-@7GQ?UV4`)d@@$5s93 zRP4az0|l-%!-tRLV;)Y=2WAlg;5^2gOtTevUqQZ_o*4N96&u)s`|T8)?NqUWO~y|u zwmTv_UY}41@msCcIh@U0e!sw#sd;wZ^4FdF4e>@#aUlV0-zzq-At#E>cA?ncDRzOK z0`cZ5^#^6N*zrAMYx*f;6j%Sd%n*D%We*}9qXfPx_U@{4))%lhsFW7VJMzv4JhP-%hdFPZb;3Wc;LJZ;(S4huB`ZlE`DX$bIjF zpMAyV5`5h(WltVq?mlm_G$erid&LGePJB6b%-OtK=~Yg$&Qg9`yK91#II9q z_6x=S7b`aZ7sWOd@^I@Q*b8MpuVRZy5(X8c6kWORMYPL;P|uwv`^uvBQCY%TlXUvR zQZMy|V*j0rO_(`9=Rgu=A8<~^Hu<=bL7nnu9^YoB@$H+5op)3)S%o_^Vq5}qXwl}y zxk$qL2T(5~*;Wp(~-f0`<0HjoeedMql&0J?{>%WsRx^gh z4=Yj`G_T~O<4fE7Dh&EnBt2fX?HA#Z+`N+eK0SkaStII2q>1Pun!=}hVBBy=A%gVF zA~MhD78dgRP*hrVa2@Qgp=>kpzYaRe?r27yN?^Ma*=y^5f0=gi61X4IXzf_k$7!Q} zsMRJ8P7^}Iz~(AM`DiX`O-#OS>y44v8gqbG0PoU)a~zTuw)X&>KXLE_)l|tT8-xOG zio>_5hRx-9FU`m&^4_l-N!Lc_Zp2|xxTnrQV2`MV8ct_6W$@|a&4vs(1Xt5%yAPN3 zXSwszWd}OF6=)&2k9Lq(-nZdI)QaBx0?`kq6eGU96-9JT{>IUWz6LuCI;W^dn^=iy zy6=^8)A_)$L%*dC$j(@^ns_<~#i-JjUW!+Zcmp>cOIT6fbz-rKY&U8yp6UzLe%b+> z(wMD0pfWEpTF@pdCQ6mR7aQ;fHepP-!xVGk<1U|lN}g5M>r!)6Df8obqKYo2_(Uc{ zj#pj9GHQAOD!D6prOIBfF0-u78NlRNl(fx@8%%C67W(9m;bO|Xi_*RAY@B6`h)Aqy z?D}p9BL5D*om%1b;u%PHBa=<}l-;e%podnbHPxJLS}zj(L&E%b;isew5cEZ-`)9 zkQ1-W7|?QG7XgEpJpgc?$qE4mt7lCAxGs$N6DM65Gbqt{n9D~P=*zSU1*-8}xYqg+ zlRiehj@A(9f~Mc%7NQjuC42c0+gU_TED2UH3{FiU<*{KaE^05kZwWjutWai7>CuqF z^ILk@ay#VD`?9HIi#o&jJ0kU}>RhaQ9KlXE5iR$C`E5!4&X616T#s`z@5_%$kE+?( z& zTZ5y>6na zSPB;?y!yO}l=7mve!H#_BX>JkiXGyo@d@zgeT@-Hv zNt8(^9d6Ry0xQF=Cy8bvtwaeP$8ay71X}+6V-2U|{p|xWgbB8U)`oav`_-79KCT{r zC745$60Jw}rtMh`_QoSdw67B`;|p@W)#{shii$DK_E0|*4$7-ZV$KfPwC`TqKC3rP zT%)#$u+Qw(2TlSbtSlo}1D-ddTA-{w(LGLoFtQk9;8w$*o~wx9oi^>6Ia~LHGvm5$ z;`=fHyJdf{2~Ap0#8cEpF_&wR%DG9Ns^}v5v%SSbynvXJV?8Ij4>2G7S!CZ0T}9Nd z9v-}bDdS(2<3=dN%t)yEVzZOo%?kfY6;6iT%d1cIj~~c!zZxBMgK3F^k1y|eIclPu zbdUM!vlYn_>x4x*(&b>?>-Gd`w}k@&RZD6YF_Or&QtsE3m=_Yxv^Fru$@wcS-S(h4 z=9VJLJFuJ@zwYNBQZ!)k=E;&zBF{4z<*I=?kt<0N`UoeDTZTU!g&2^dK%#Uea& zpB@U$Rco^G6qyliR$NTT&#hcnmGb2MpN=I%ag6AcGK!mfrje{mX)_&7H4tjK&e-X5NfU*8=QGwvwW9V#xlLEThf8+*ToJv z&K*18y=TEP_>`dk5Iw)G*{uE%T+-dd=v|xCp%(u>;%7-sKXNGlo>s0WH&62x)2U6K?EG;^%o}5!~ejoJ!HsmDg!TG-t^$21#?8BOlywWCahSAgP zL;u>h{r;Zj4E^CND}h@B(L!hN&3!)R(|#+nT{hNXQ@t(myxj^b^rk(VZh0({D0Xl} z+SgWQzzqN7R%XB|7|#4&`nv?qXb}37fnW++D>Kg1q)){L`SqqSilVZyl;=;lr)Ino=bmxI96}Tl_rF82M}_`% zP|0}#mH*-cDZwvLsa}y512AHlJ@g!dfbJ}jk9&ZrE&Gdw?3PU%%bJ!{NYqX@MPxw`k^$+RWo_L`SP znW}96Z-vTJzybD^eiCq+bkUJ0RPqV7-am&Qf|aG;7O}~uD1oHhe<~bW z+iI!^3E=u3aDWXt0UWLi!1)evwB_z;WIA*?xT{8Xk)cG~j4`$6vkf$hYJMbN$6AU1 z|9Xc3Gd%fa|JdGPAOH@qKJ8~%2w0o3Qx*dDD*tvY#B~~_H{cUbVC}<=sl3y8OebpJ zu1O&1D^@mPFnsCOC=W+U&`Fej6#Sl=UJ5qPwG~yZiF8Z{e?;j82=bMYFbTsqB}I;$ zWjTXklUSnOG8$g`?_YgVG4w|eEduy6`M)GU@~Om#GcIAXH!s{ZDxXat=&0Zb7Roj( zc6rA#cR4+7@xA)x#KR{j{ntN7=?ih99Y1B~y)g+UGPGRsB^hdHd_n-V#;UARtwlw; zArMK^>b@~P;^Nx@tzZ<38~s$#v(lDy)l~pZZdG}zN@H{_o&{&s-W%*TMtTl)Dhh8% zEvvc9xGTz%wELug(p7M$z8Bi!=prAlNKbZ`9>MX(ay^12MBqUYRSKPDKR%qhFO*_M zuMz?Mp$v*W^h!#3&y$>^j2ae9#1tM_-0jQSCKC35rtoV@EHZ^$aC*J(Ahz~W_7JDC zyJ$R$!5p2+ZNqB3H+QI0RbpfI7AGv4@wZbR8^DiA=h%|HO3^fUJKC;8M%;wCS&b57 ztY%bHy6Nk|O0;q%71lCNCcfVqew=R&v1Vx8xuv!^cBW2z1#5_^WJaE`uU5 zICZ&EB*w`BsvQ!Q&8Rvevi^Y*ke@K^w6$Et8|JbfH8GYV?a>Zah#-&Aa@JaO#>~K7 zdTOI|mBfz|Kq$u=S_a!j&7Ddsu}qy-uVf`62aBm`(|7YK^BuN5A{SPCGBv%JC-WVq zifMDM=ASSeN-=LcnrUlJ)Vmq8V892EsqWtDUrXk~C}VZ%8ISf$pG5tDViV(qO>~X_ zPC1O%ZG?%%wfk}eZ52#7b&*d5iscEx(|mG$ogOb(?paJ)uOLu;{2Zl+6B4{C)eya( zE>TL>eCwu)lT&Ay>2b`yL-25+lms;^F4UI`*9J2ZirnBhnikiif_f!6$qwcn83iH> zUL%#}!r?)Z8{krWd(;;Zq!@zkv;!6yOOH1Plg75;8&B9*151uChZLh*8P;)f)K{nv z;6C$m19Jn;nEr7`eFS_bM}27Act9KCUEWuKU zV>)^rWBQn5N!HL|1CiYw%N`;dKRD*t(ocKfghDB3u|kaRfD}G)bJi8_ZTRO+BwS2_ zaTfO3ZRvbx{_FN9J5j{eA7cy`<~m-Wpm0CrCqk~?SnM-tCJmJe#BzV6 zN>dm+nLS|K4XvCW##Nd;Y+DN-mov9&c4*hWPGV$DjE|_ixH~10&4;VHkwz0r6jgPj zezQXa);`|7FVIHFT0P;z*i8D;n@pD4z?5N#2HmvQNtdA|;>7XsmP$Tu#BA^( z9oq<(UGW^Q=pkQtcOa_zsU~x7?i#Uw4LuzF{jcM~icMur|pb$JjTQIr0Y+S3*A$_^8Jyq+&6PH&#^iO0w zgXqEsZJs$UY#;|w`!dOyJMCmxOj}qUMh5VNY>c$_ZY_5Xzqs09KxcRFr5~}zu4zt= zJariPBKf!=8GtoHd3_U&+|B$xvzlH`OJGm*Iw~cX$&TKW1bALwfpMb;ok*`WI-g7V zizBo}>*UZCKQ(J^@i6DBPZS-%*=;gX68vN&{P6WA5s|JcuOdRaZi$Y2!V+s49rVb8 zxspoXo=t~fW6I5XM5tVt&zpz>UEA);GfK}Hf z4*F0LIN_ALomkCP2kh3tvpq;YwHZG3xt6WRi4X1UfW`dnG2g`oa(c=Kc5=-3WdjLw zoN)~Z9&tf~i-U%moy+u4TF0|3;$UsNtWzDssipF%&L5E$F6#xMYS(ZN=|^M0gHBtj zmUU^@EfFO)_cZ=we5Y0bBe=_L5BaCeT%Hd}UNJ?= z-K>D`XZ19LV2;g*;rij3z~6Js_Yo|ue0tl<>ET~4^~EvYd5`(7eTm3ikErJ)F#;b5 z{_^`7o&YY8t11I;nxs;7Ssd`a*Oq4#mmg^x&cxqAd_eH7t|WD$GK%9zp8D?_k=dg1 z^APE&^}q9*Mr6n=dixXT1F6jadOnEnX2-_b$=L1;oT&#{WYu%$s1}M9wNUksU?tt@ zNx6N+2Fi-SkKEKYp7s0@8CE%NDGpWBem#o(Y;Z%SWtVrU3fm5F zzcnIr>Q4gOTfXrpK{&eu!4$M(KJL>e2llFb^W-3$-GN{V+A$yZX%r0DGWy9V7}~g| z!7wuNLrwMe$!qTF6I2B6(6=~xp=6uEWoo_QDf-$%iK4LON|abHBizKep5M~+)W z;$LCzUd;_?Ac3LLiBr^uQ@k8*CoLtx8bC7owNn}}!#}xG8VFIaf5!B@hljv++HbZA z5KaU^Fa_=Kko)xTaIolsX!Sk~Y}m93ob8nMyWQNC0Rx2y%S{=>;YAOd*7u_XysZzl^-zy7331DzLrfo1Gj4{sh4kG*rEO3VgsO<0padsUBviBt-0v5EWxu z+jVyFh46;RT5?ou5*})9Q*(i6NWkS&;Ct{_&wF4)PJr+736`8~d;c$OjSE6v*QSVc zC76Nm-tc7I4Ho3l@C~diW7iwVYbmnouMS?KLZ9&vyCZIgZme9r7KKm2GpOQ-FfJcU z6->OuPQ*DMQ*C)n_mvHSedYxl_P{9zAf5SXLBPn?lBz^uePoP;)pkCD z+S$oF8TdI*8d$bZ2@CGo^IinL_A7{(`{MFSD_jR5?t&?_4E099b~_bur8mQ zu$1m4-R)kh5MFZ{n@K|X_*`H+cHojMn-28`j*FtR>(zgbuud}Bk__MKd0*uGTZ2eg zqF)G0wxdVxNf1S=^#x)58wg7vHos1eb=a`5_4akl-}rH{tq^y0IYv zJl_)*upuXe#dASe--XnG(a;$T2;vt}mEL{^sYw6$?h+Bi-2AO9qUplbqrQ@XSuCg9 z(qCH(WIlkjeB*W3F3u=DzODxc%hpHfKyvJ8_<3_lkf>#R`Ds;fxSN($${H5`17XG) zON}-JA^ACjNwxsfGIe~le3Gc*cfq@``LrS)BrO#>FkX`7P$`4xajf$Yj_UoohA#sO zX3MP%VoZA!hV-iUat*AVg4v@tmm48^xZ!Tm-@eV)@;Oz&)Bdf&3e?hoCKbHPOI9Q# ziCdng&urzQda%$+HCRswtHFxOWQ+(k&PU3g`s9>hB0E+QtGT%oJDMB2zwCPekjLitHQPcib2GdV@t8!Z)Gbypv-b)hLyK|jmBY2|IBY5PG-t}F;_R&^aep0y z;}kivj-dw05ZWL^$6rjfb*Z_#jR`-X;Qm~3;~`Ia{U+9Bjb?{C>6iGg^&)OwZ|LqP zs7ukc1)G7adv9XgkAn1uHXJ@Lf6`X}buqV9^n9RJ)h9K&8CPFQRX>3Zg&>SanJ()^ zvpX*^vLretGu2lH;b1=a!pz)C6wp*C_l@2p&qo#~t)9JZ(<;oOvqoZrbl`wn=)y~{ zQlYe%%UapyzgD+qc){-$o4%W)x0xCTHbfsBic z`?Yr@l60L9zM5WSgiz@niULuF;x$>lNB8}a-Ba1&lbLPhWObpZawX&_u{xATXP?jAWmTHHi&5!IJipsj5Ia_}1a)KPR0L+9j zXAIH&=TrfPhpxw%MBdZ)bDI zc0ZD1O_sm1WS@j382JkOi%=;i4Kx^^GR%|W;cu#f`EEM&znogbZ?#i5hYzxi)pjR`9|eAf~cvKud6}iCZszrfT|8*I+AF>T9|HTg2m$#<&+)QJTtQ^rG$YCDm!V z#t)thIK#R=S5TQV`gI-x&$&H>dgc$;LmzKt(PxeyNp{^rw|y1qWk7U&X^`Rwywk0Z z=%4ctPR@JL6#InD@GoMpAbJSmUpxd!#JQWzZu(FW=k*Z!k>%|$qZTpLQOjA`UBw^j z$`s42@CD;=Nmbu;&k2J1(Ntfg3$QZl8ybS>!$C-cVN$fWNd~gtf* zUT0<(ga9@Orod{9PGj8xPb%tR1;}sN`S;HGxxWno%X#^!O^&eFv~70%p?X5&uOnIB zbCax?YXo!k9Uato{yjIh5LEkc4Qx2VCMksC{zoF!$G*rv!kfP*n_$P!so&~Sg$v@nRta?*m@>^R^&(g zK_%H_TJArPWP$B1-|!#^DFPsvf|gCdcglmnCgUf0aQb5p>-AT&f?STzmy!1607>!v#pf^ zK%Rn&{PiMZTgSl7zGJF04-k5tPCk8ESK`K%_s1+2*p9a36v%?h;oKz;TuC!3vmTyO ziyK><3Emx;0Fo4~X3?6GNbjPogV(GAb(0%6 z7dmM}K9}+!=lAoumD;bo0LOn__n)Jdy# zWznsV!>1&qOKt>_MnI5|k`R#Y?(XiA?nY@)1nExcMi7)#LP9_(2}P8U7GCrSKV&`c z8sI$oRS*1q?ODUUXJ&t{ooxGR0ji8sszpFJO07b}6p3YZVWU2hMM%X@rNhUVe3h@+nHKUuNKysMb7B_qKw+=d?nt6OGc1uuaZ`MF3}-yuK}oRhdj`wZ30KQI2?r)qmjU z0cTspSe-s9S%3dbm$UWA$6b#s^bI&%(@?mRwczK7&w%qc0LP)AW(K=Z)PO`JV?wYx zrZF}cZ5_^|x+ki9E>EpBaq15Q4$!ydw*wCQK5&34<8Vt>+t?wu13Sv~VPqDbxDk4ahauY6JaHD#)A%w>UFaTR?u?vaIo z1hF3l4p5Oj;IN+o=k&mlZ85t-Lj8EwpLJ2Jxjl_B-OP#`g%-Yi)ZK5IaH(PXV4q*0 zz+bb^@6Q3wPq2`cKD~He!s_k*S9(jPU6Dq-!Hjm-a!V=e*I9`D*cN)6 zv5tZ6Z&K^@>U_YH66a~noLFr*i<(a_U2B9!mmV(sJPYmQYj)IZmPBlwb@rUFvq$)w zg>HHy7AVOt3>KeR=x?x4#KP*{!LO0FcMHd%eItxgtQRM=-Vx&6E?N+TSXBG);t#YC z(6{Bcvk=F=g@7vKlooO{5s6qok7O-XC|C`J!oXbsmsr?#u`R-q9MPQJ|6(a5h~ub* zfQsx{h~vycr?*fuF?EEA0%2<<44SF#GhRyTGAhHG3lSPd$@uTNsnk#ixqVV*`wo7>=rinAqn+5vYRtCvg~iHTeQfz zb=%d(;NFr_NSzSTADi<^1=v zZ)XW^gVF2M>H55(U%{GGA|G`y3t8z@YZvP^EDx{FYOz|VKud0iLz=cjr@v!F#Y|Ll zi7xYXPQ$tRbYuQ6bczm6F-a|J9HUJ|DoAdLZG7D2Y70XTA3uC3s3TLNx~i{WZQ<}_ zTK`>aQ5mJlO-9RUG}mDhVIzpl?Sep4zTp0>Pw>pjsY;8`#XSU$lMGfLkX$tJdD%5X zgkOQz-UTP0E}CSsT_aaT;3QDc@Vdk@_gYa-^Q8dJN;mEm=Vg>)U#^;qqU4DZdfMl4 zi!BsGhCVi}U%v}p2Zzxb$_5=Oz0k$JID1*|zH9DeW%yQtY%fRE+j7$hXl}b9GkM_} z4mvc*(3`k=NzOiyNY}nDbopczH(BFi(Yk@|(Se5TZY(Twi5dMF=w^sk9IHL_78}&p zWiGi7K=~2n74Ejz)_4nEIbWwob5Z7Qd_mp-m3R)lXImc|^y4xYQC#xQQEbJft|GUE z58ST#Yl9L-BV~jruW%VZyc!!_=K+H!u>;*alo|u&3x`7gxMePvQuda)_=wCdJsFBD zT(qHUZU~-)rXX(y3#d{J>+iP$Vp$|!}{^*!&(xCrGhacd(THoE~;<2MHOWbCLb(wnZ;Oj=gplOm!Xva zEjhjM5b!8Ksln3}T)19UWeHX<_3JV(j$>zDD~qJn)FSmf!Svk_P_U^2uZz!p9KKYY z3jv2PUo~mu_R}&i3~&?$7F{c2__VXk>x7w?=(o)4Mgx97oPo!j`f)R_Pv~*l6xHsd z?w3{35^|zvXI7L{g35a*B8b%xiR!jq{Zmz+Eps{TG8eLIjb-#XL>?aBA8(*tF`XOS zpN}f~$6G8CZ}J5_;~-MEaDV#f!qgypmSmaJqRAhac>#S}e!I-;=KeAlpjYLGJ3qnN zl@o-XY?;f={hSf#F*;?=n27sPOTRdVi0TT&OHZ`V(;q6{y+X7X)ov(pMe)3LNd^*h z^JvZpRAet_ym^*0o<3)cXi9D0!R9XVufi;U`v}_Jf~aCWa+boYiJd>Y4YlOl!7>-1 zz`aUvKfG1d36iU`(Bm!~K@QSU-5S61tVWO|jbl=xZ)4TgT;yi5gavaXWR>29cfZbt zH_vSN7oTA#_RWUOY81wV?Vb#p$F*S-%r@#=mB2zNYLfq(ja@1QjN4FT7qyCDv3$O9J+B-J z5$>vPP41UCP0lMbxi~`N51Z#^kCAS=7%De zKS_=wFO9TBPvBZ@x`1idV~P9Z!1ioP8{;Y7)FG;PZE-~wl~!%0_%l-k-@a$&8rE@mOxbJJ_TTprFx-UcVcHpnG4#H-eD^w{$hOjap%%6UU^BvNw+h zf~+cJc)48k#`q?yR6Z2xI9bqZ(HNx{UU4pd{jyh{BwHs%G!2wzOw4&sW3gvK)gjT= z3E}71#o-&hn-I7n7(;?cgyvYCk^P8_-W#3C^}06893|N9j3>0=ITtF!^dONimRmQ} zue^EU(yVYxl8=cz^lhW`t98%B6+3YkL*hn9rD~*i5uQUrney=C@oGNfVim+|;EMvn z7Yqux(YD>X**>LbJ+g;ZH+lXFYYeF~&%_wn^x-om^;$~f-CV(U&RB?AujPDaj16eV zdOYDx6_Pu7$ezYWOvc%~xTm5?-YawG;hbhFnWY7T!sUw!B=yWD>un_2B%D|r7X6jN zuFOGi;3THt6*RmT0~)smAO=*w;CcjMwKu%c`+^aVGl+f#56Mlzm#sD+Go2vY`PSAP ztT5$#GtKllPJ3a z^y%_%49#ZLcJ~G+yc$7=4}%9vn+~;Cf8OB4m#>2pD8y@yfhhU5)TARbgGF^+X}9LE zr_l7sskV3)%SSRl*L}`J!WvM*=)UtlHRK&UifxtlQ-Q9r_#PX~_d23Rb>YbYIkPqK zN_XCKlgYrDjbF=v;`3dMRv9?gp5c+Je=s-!dpC4suH*h7)YZk9s)9PDIKD>xsoIr|&t6YE^kS3J$Pg%*o zvU33jq?K5TF(C?M;dh~*O_4wD6nTkn2`K%2_82S^W?tdrCZMa3`5Kd2csI!NNVnD+YiA{IBfz-xm*x*dF>h1{t6JF!HKkV#$7(z2}$B0 zb`cdW=q)ItT0u!qBACdt}}nLjxsIgHYF-|hz8eAiyT|w58KA(u$IJb2*USmVQjR<5 zV981$o8(lNuD>HYqnX=hF*$nZM3_TxaI{iw7MfznPDgnV-5h zM&L5fam|M%8-LgKwmWH*clAfRX){#0%sJYcdt)XlSIa<-7bp16%=d2&I#A1|Eo*mF zNQWJB&_O!_Q3)T5r2?!jkToOciw{f9U1ss~l3m5xq{cF8%-8=w^8r0@znz@8_vHjs z8K;yJGweL)YL~)Dr+l-64s#`&d``gw-?@+7zE4{VX=QZW$dnei@FK!?H~bCJXDyD8 z)|m|c&p+wBULg0o>~+X)u^1c@4w?x zzmvA_$*H!KQn4T3d_WKEpfP{JG5Xnvly=E?WnQ!fv3s=?@(*m^fxaz2wC^YAs5wFC z$@+9|?YHkhm2t}UT@LPgtabz0V6rkzx}=M8)PRIyH6bJu!!_@b9e>jC8<3z|N85Lx zB75!ot<$&f)*Ch^OrUXGv<5NRWjCQ9+(4u!gN&T zv)^I@RmLe>Oku`LnPWvuFJU>Wp?Jn`zBA`B+pWc%sHU@dORWCLV;T~~bF{?-DzevN z@|?cKd>SQ~g|=MECro4CDkQOJ_!e2TSWdJnOzjC@f}?u?&wh&u6nCV>1ll$5VDeF* z3a=m(`AQP_{kDKWv>tIGmF0l&K?K{En?w^YC5c|{9{?CS*c|a`CQ!UTPir)Bh0C_O zD!4@|&uuzIw%;O!mmH6o9vo*YRo#t)1zoIXS33zM2pqmhg8pL;qzcL14DVz{TW`z2 z=dmtXtz-ydY*3hLo4b}G5!h-@lpRGz8&;RUPCiQ8QeBK)S|wvLuzt%-^9eqKxSqf= z4*JXF`g$iyy3HihxuPMH#!{n8*f-7eyqb|q-Rm0`&0ypNLIWq>v%p&1HGnEK?ig5l zPo*O8yn2299^?eJ?kBYfe2eWq->sk*u%lS30tg8b4vHX;b+XIw4Vl+h7RunL%}H-j zKqL!O>OyeJwvM33EWxx|$ih59&n%YDG3kzbF4jl&jEHr*KL~QVBQqOesp1LxbK7`P zTp3|jp?CeZR19x$__6Za-ulAksZ41iepa)cti>Ti=Tzr}UPKV;x$~K2t~J5ZAI9}@ zAWi8*F)opsR}r9)eupBvwmcp+V`dLAayd7>t~to{_%8itLf5)3WhlIr6g8cmnz}VY z`GL!TCry!O#ro;F7BZ7rS9`<)w*+I;8YTh#JbR93aMeQH+B#sg={T}u8KW-Z>~R?q zya8x1;$T;zJ0$E;E(tH+?!+f+)Asf;TP7PN;xsHBtbJeD)_HknZO<@u~q z(I(xXf<_9)`7b{6bQQ0u9zK6()l^Nk`3bIbw>7+$yp#P)t?Uq$tp}FCIT60>$xcQ> z7ONO+)N|F*?5ev2-fu1bbI6_d1CkJpj<&C3@S)1BSrH3F1A}4FiP5L!p1%eo z+7Dy|cIhIvi7iu!XXEIJB0m*rNR(Z~Y^v%!A7Vu?CS(`1fKsuXzhRBazngu(r?@VQ zyJmUU*7i9y)S@H}I_pZmZ&+v2)oU;64<;WWK)6e6Y z?O#NBO5JnD?h~?G_8Ys&3QePOL7U(?PZFQyw zSK<$-Sd=g>m4%#O>HjRK+rMO(SkRaZp>;@{b$8O#u>@GBE$9u&ryslTq z+kVSSb%<3O?Rwr^#}?5S#4$(7@WaQ9O@QUSHgc6R_)_Lu=bBs?XO-#{yOb)M!NM(a z^-eGo%E1&KplhB@;W;ve2WVc-p?3=?bU*h2s;ncqkB`J%dDbKkPNCb=h+#0D0@FxP zHhA>`<2~<$+1`M4k{#?k3KaN1vh!#FL|}jP@_kIxkaH8!?LOwT4&4K71GyI2{m~4T zG)!0bziL3A*B`!W3ZMx%!KCz$5oXXy@UC@n8X)+!?elN6@Z(Bus`$GmA9@T<5DL45 zf{ft1a`9%!b9i1a1+_*(qbBx8$j`JpK?}T}5c;^l!1%$yIl=b+K7)aH_q&BZu4)mu z2RmUvUg6!WS}CqQZ!Re&Lz**(FoDEv8kkl2>wL<4Y@cSj3RMeXuju6RI2Og_VTekO zO)@55%bm|=UgMjZl867N#_Zo2NJZGZa^nT}hI!4IPfzGmxovLUEPM4Lw%__-i6zP8=7-74A+8+%Ccjc->6$2l^D~+w#Mg`~;(;CkQ>+ zKq}w9Pk}1qls+BEl5M(Ir|BP1pVn4s*4>JlN0=BpK!#Eim?4zzauFF4#CO!EKt=X^ z%6EF7uD*MwB0tgC7e|U5Y0Us~Mb*s>T;WYGU?PYYx93q6-KP;y+z}dCKE6sw6Eht} zFx$S35v}^nFlZ=Z9^V@cOBBwBWy(SHU?WnX!2gkrNP$i{KBO3+`Ct1Mak4F%M0=t9 zT*cZ?P#c~0tq4p&CDo;Z%nfgRp}DHXvR`KrzGGWNS)zzZO|gD?^4)+{LOCNV8LABS z275ruRD8-9^CH7f9HK^6!He(m>kYnnW|0$GME;va26K$USyi!fYrfyrHa zOC%F0-^oo@#I}1~lpLj#aqew?h*>jU`zJ^c|51wo71^^0|LHB_HfezYT51)s#1DS2 z$YMOgCXjmWDRElPyoEqQKeTx9a9kIv`@la$(6t)-pr5F9dD&Dew^-nVbG^zTw zsE9ax(|QLEy5N8Uf6d+}K$Er)QQ!niy-pB%0*jpBs>fx^49P@F5=-o}TLtXfa$uVT zJz^UYkJxRngPV8yoHa)5ejQu*kIfco;YaSkYuvPIz=Tkrz|O^Q14?8wj9@;9)Bi?Pq1S3Uv1p?RD7 zp=~ZRwUt8Y8dfRTpcNG9=BrYcKaedz-U3aJva1i4NUB*LS%&m2^?h$P?0^h2%MfRC6|=OdC+&uWfUu6?`$pUtoP>$ zpahDpE_e9Q)UbZRK3K^O6nNwox-Yi0ES7P>K0=r?fFD@pMG#z$G#d_KbfDOZGb6vP zatH#w5q6==A31JC;}Ek9$=RMC6Te3qa(SK}k6|{p#$=b`fH*+cJR=T5%m6{np`Z2y z!~qK3@0rmID#Me{B1?0a(jLH`m%Q(7o9!At%5vfH;9?kML7C^TBaXnaiQ@$)tIN~Z z8UuaTj)QMj;5=Pe7^qDJLOSTYfv@ z2<{UHs4`AToK_lwan>^5?1}c(r5!cbdt=j=AG&@llW@B;iA1D5vIYqfJW3p(B74LU zJmL$W8Apd2!BWpoB$7oY$kojlqs?u0LOm=j%OMDLYG(K$ZSqH-3(1Bp~@JlWt^90?p zKS>jd+5@Vl+df;xf#IP1CvXU1j|t)bz}6M$+w$ABu0s2*D^O*evUSB1i>Zz!4^u|U z+6irTdUw6+nZ`=;OClK2`zc|mTU7TTK|)7cSD+$$t*g+HrtJi+YyWL1!4Fr|X}KQa zrH7SpHdJIUt;I+sF6Y0^WA)bNAUkMXfdY@*!U zuUl*|Gk0u0R^>attPIc1#(s||@M#Wqn94Ae-yKFHWl-bZl5w#DdWT@mmB6pd_@e0K zp5M#pGKaPHh;!bQZb596&wpIxZ;NgkjfudATtx1;(x1Uk%0E)G?G!guT2{7q5@V!a zLK>R6c5rNw?GhESwvyFG?3H&BY1H;E)7X6jRA?Q97b<6$5fN_Jk5`@B5i6i%^@cO_ zZAx6&++k5+dRfly2)byh_QX?#FxRf9rzt#dvMY=-w3?0obL(=_5L{%`xaqtTuXcvV zoLB-Fk!-3eQT6=u?Qw@(pA@>@qF69^ z{JwHoJx)I}C^*}Vn~d{X7`9xmODdQg<#di)uB|(l(>qNk#dLOJ>Y-v^ZjB_D5qzp% z0Z*Z&hqhQN1zqg2J9nds$vwFPq+Tg#WQcNW5h%g_)H5GJ;fnyeoc ztf2GZN4sKs;lxGoO4KdPD1KeWhopUZRTt{{t%hZ0T-HYgNilHEW9E32uB9ywTiYf# zQNAtWyOmm}#&Lm}r+R%M!H(5;D7-?kI@#!&iO5KHF6rtGiM@@+f7n6XyR)L!4Ki4i z0)kE+na)mp8EmIJA1<<1Wc#Ap9MViZ4;Z4@Cy?+Ve*&@cQ0O1Gi0^X!-XgyI*n-!i z7eKR}Wl-=LqV9atNjvDH*boCqW#+0i_bicNk!y9fEUxw#5us!HqmzN3&x*!I#U!Zb zkh{Ac|r=$+h#|8SQyIC7V+l~U) zn+J_MoFQ+g&OgJHxtJ^_0jqF>vlE*1pQ`%z-9h|!@BX4Ge<1Z*g#l;k`{NC?gLuuV zXLSGFoB16Axdp|p!m+JIp?BU(nHODHS4+LFH;eiQ0v+hv^4kGjcz>Y|(5v#pou6P^ z@DqfdYzJ}S{nP^JF*;>xp+2q-_9SJbr-PF%G*9B?^>j1qvRa%3u)2juYB$wULf%xwEzk{ zatn9vkGKS5fdw;Z&yHKrfK}Ao@aTTpZcO{&!@GQsuIjQw8p315owbU_g7k~4l};5r zAIuOHrzC7SIRK%qI+8>sVsS7w33Sb0Gd2k{h z3bxJr2dWe3f&1<3CbDlgpvpL<-HJ$srd%WX-E{o8g*HkdyFIn-Chj~(b3|lty;-f5 zQwj+ZIchhcB71fdIRd8>*bN=oGl5Jc!-Q)+N7dzd_=cKfFe!>jlabWN*7IT48DAaP z4Jh!)EdV*|P?O!;9!7bw&=$v1q|S9Rpn5Ba9v0qpy(agrkA$-D`Sl8SPl*2JaJ~GWvjJ#2`k@~IXhQz}w60f9)XCK_Q^D#XWMIj$)&iSi!3??|%;`9ASU;%I zaH0GU}!^kW=$<2Ud;>y{SFuXhsLwVLt z&X*t14mBhk>*YMg7vo+rYy>pR@j9yC>g=NJ+z9 zbWIRk^Q9c}!;}j8%+h_6)q3M`p^r;9SO(L<9!fxgzh)1m6S(CB!Z^WIkBgC@I5rk+ z?qb>mNu&#RBJb;u-CB&LuzW|wtx_J=9vPAJ>o6jEhLOJ*BkJEUf;`gsGTx#hB<;8u zNt~?nQzX|sRghy+f6k@<5eU!c{C z4OlB43c)&GdnG0UgTGkHemk7dpv~^`o9Y9M00kbog%e=pq20YJsgYTmLvK1TpftG_ zACc2x(qicB4k_h7Q2lZ@_+Zx}puk_VYtaeV@N+P7f*UxlTk6@vT^u;MWHXhtUJt=H zM5MIwt=Fh~Af>PhVP?k-PW?K!h@H9RFLsN@H@6@<4mkRG&DrxE*DbE~1u$8ZPnzgK z7VK2_Q^Z+9&0(APpTy(ipe&RpB?g?i<==G6jhnVloE}=?rXJHRQkO05A?TXeS#`tA zCO)p;&A6`AwkG0q*@v{&mTSApa<@^bBp-CTYxI#lx|78gGA5QlXL->Y@4Qf zWdltC4V6hc>O%wfVoWLy@??=%;V&W9X2>YZPN`)ij zeB#FEn01Unk&LP+(VP5q;1;03Be!q@x8zf#MH$aJGH@cZdL2*%UXq0;C z+G^0dl^sm60Sf%>rr7+SyX7Yk$J|-2T?s!VHh0e9gZJC><7M#K8I_NyF$&_l(lwPD zMt&V}#LtNH7ZXSG8*$)4kE=KN#FC61GplX;c}1wJDaWubddX3QeY3~Dn3@g0V# zFOg_#Nuy`P`8SEfmSlogm@qGL{+Ps3{t#rAd*4JPA!~8k0fmt;5ISz7Guzalz(B%U zoj_Oh49et*YlQ3h$a1ZQPWH>6ri#psT#cZVDmTeXjS__|8+rW82bD4q}}jm9{pYo z!y9uJOUZ@B8ihPB*vBwXI7jEAF!6Hu9_qD;Ml$r8=6i@(N15R|JezL722Uu}Ld@l< zCux0FYTolqA+z=izD4`nGHZJlLeMK1%;(fx(zQdJXxMa4+5Isz_u{1Ove;GWbi1E< zjWdPnImFu<4i>Uq`(WO1m)PF1fQwXL?PdmjnS5li$%FDf72zwy{ZdtNoGA6i4exZL z1TBKbQ9{NP;~ou?#u}q0HrX^TaFYeR&3GE&ZxG)?dC%r448<^Rfa9k}&8L`qd_2%j}t#PaSP&w<1Ri$uKWmOOc;!s1N)20^cCS z;t3T=npr5fK5AP8>mrlwFwlcY!$((K{qt&L#LVcV**D@sv{&jVg4NaBh;`$(B&bq%jL z9O`DGuHcNB#9c};Kf*$E= zb|mh?mb+OyX%IT=%q2*iHH1*RT;uEOb(%@#m@*0OO>S?xAlx8w63hi1{oK+VpaV1( zxAiw2*Hg47@tdn-Cbq+GXjiClH?GkunhKUwg}%|7S&}_iQu=6jvyj=z5aI@niTdzG zM2tR+aBNXIg4#?xa*W5$mMN_^auYCz;Xy4ZLS z9jx5)dgA7KFnoPkSh}hMqJg($NaSOme>~RU_gzxD@y?c=W%=2r!uOQrcRLsICZv`F zR1xZo!7)>+{FhhJDfnmw@<`tgFI;jTv9`f#mRDptM=B!{er-hX?eSA8`@(B6QR9A< z<%IG2AFy8Yr<)CW^AS6kqOo(K1xXB}{!{AwQ_h71dfIixmym6J zaHfU4tsCrX-_RH?m3#F0eViMNvuwH#>g&{k1W6uEsep>?rBsrqPpR%1@sOtH1({IE z?#i~I6uijPW)^>Kt8LSV(p+E=T-|$+QUL`XNvVL2Iy__wp!DZZXjlMw<*o$xn;V_A z$j!rZBu?-jZeo|2v#ZF9$@Vg2=(+E&2nKoset7Usa30?YLQi&dpXC1Reg1;QWT7fWpI4VLczd>fGB*!Z*;kHA;jfX&y8FE)U=!tg7%uGwzm1n_6>m$2E*Xe zRd^mfdl&5fF$n?DBUQ;UEh@E4Up(C|mDMGVh#C2+rY1hcl()XLAcOD^Bm~d{_rsU` z1RFM-AoOH=J4o#l0;n=hNr=9?DFQTh$cm;mG3mACBXRhsEQ~jkxbrB5@+nj2L=_-G zQb!2^RAi43Ql}>b9Cu-Fr@`mza+(Z<63M7H3DEoPK|z!bMgv30R4rvQ2ZR6${3V3A z1evTFJJj@^Io=-5YN}$;;ha=Djm~cNy_#0ej}hAv?)x2r3yu+DU1l%mH%h`FEv1#{ zZt_6eDmP~&2C>BJ4BG~V?so_#-_v8y9+-L{FTRe8wdE{HdP!)8fFh3|PAnWV8@!x(ay0J=L&D=7Eu<~XRHI#kpft5e@V%6s{=)5vxL9+CDx|HX zrL~h2uAU)z!+~5IFWi+Deq;JAK}-F|wN9ckErjb0i5E@LmmCIe@UJg?mMv%C`JzB$ z2Ya8~oJeiHNpyT_; zUcah(fs5TA&~rB0s72zaV%OMHxpHkDlXte7G)xwV*P*G# z8M&5V4)|U4ke%^y=HzdDue$QQA)FXyhNg{SGMi#Ec!sY*ObxnlQ4M|Rc7!*i5KYk`W%TYB=Ubw7=(ZxyxL!EF@QrD@y*0|C6V)MRLum=_3=0X#Ak4x{)1n^a zRhdZIn9tSUE&`jK#ssEoMJq1V69fap-f(G|=yd~v(2Skt1@f;Qf|FAGjEzZtP<>sZ zX~~Tme!*~;?qzT}MUY2;U>?g7kzpf%f9(*2RIrI^5RksX+3agjh4sSiEqxKhjdbw5 z8VHUYR$2^IY}g+=1P!g1+g>XKJThkbBU z-XWNh+{e)_HHsZI2P4&V;mgpKIYO{!Fd;WX?{M_ifiunWY7~mVo*$LHKHcYh!}R^N z(O3)-Vd8%AWcGs&L2U44&8ud{=&s4qcJQc*fim3T;dp4(kQB0yNHl9EHGZAurOq_} z7i(VctLAZbub~>0MoMq39#`{|H?oHNIz9)QN-d^D;i=B{m{@^9LLg`Jz9)RdB7`LM zXK8+0#5m$T`;vp)cP;;J9fG%>ijxY^Rk+3;Q}Y5To1PwTHewmTqM-+OMKSy)U@UbW zvP<{#j6O9yBW;uVmObujHXQ`NQi4#aUF_Hng^xpZDbf~1dX z9;nEk=A}=s`G+O(u5C1RpWVqgG7N;!?b+>_TXP3rAt_ae zlg5zK>NN(s$%l%oTRd|mJLMY}5GXfL1|KOxuDI{-Ck7Pw+wCXTp4^)<@nn6)U@G$d z%YH(tOEp$LoT}N0Yw0*SNsTnC`#qgNul5gb)CsyTPY`;to=)lgp3bY6&8%^*O0VQX zHL77;-KiGFWt&$HOBSqT$*=Env=RGtHj+NG(O+yM{ckqf?!-ZFA?`+U`rowCPQK>s zt2|rX?!UxFg@JUVo+acvkJr*C85FTqRpqs?nwh<+Az}<&W(nUA9N$Jv>0>H5RZVc< z=bGZ260f1@Y)~gGMGm5N`ows<=o8ERbQ|pp1L$8mr7-GU(=p0=iCd&!tMSnIw`g4L zqEHaB^bc&OdEXX!v7HkVBy&_4Kt=Y1A#-|R%*&xVeo4T=8y&g==U?AD&Kp^5*y|O9 z%sd1OiFTEv{y-Q&fxko;{P(*kuL@SyZPhi@NNs_c!jm~rBH%su$Fug&U1CJuKG=E; zDDb!2dJJeO?BQOX)IcnyIZn>q5-NBR4c?9L*GQ00pYBj;)Ng(}r*a=7K=0CThY^{5 zjOZ+LmRzk)$9-Fl!XRaD;r7(N3v9$!Zs?_*6nGOyjK{CTh|C#A{$h+6e8b45F>!r) za$=ME|0G5*z)=*~=B}j`e)vl;QdT5Trj?+H|8S`hJPtDg@s92JbZ>KqVPYar{zR;a zrsHD-g_<+7?V*AKYFe)EPMCglsc!(-E3KXf&#~-<6m5%$ei=r9{-sl5L>|kKZsx%Z zo-ct2n9a-A2#+5OJE1QJDVmX(a@O?WLqmdOk75L<$R0*yPmhtZUGA$6WOs-L9zRg3 z_Rv~&w!xz}NE{GPk8AGs9_3m+zz9&_FTu#5$RJW#iuUXqs&xtcoQ+YV7uhNsQ%~C@ z&{134_}lGLb^?q5t>`#~TYz4>-_9+v!_fa(wnFKF)70{%mR1G>{+kGfb=Km7-wdN0{5USIjdN-e@=Duda2~hIU0fbh^{=z^UE6CQm(n zAd5-MA5Br5G&!f4%)9NiU&8ty;zCWnq|T6hc3oiA z6MH4XuPR`Gqsm^F>OIR1UbRLIw&VdJZk0>Pv1Xj~9 zyjZ{7n#Jm|H+rjg;qmK8YY`R8=7p`gG#4n4xP zo`F_~Oev9HV0miK5j)5fFKil^h(^$YZ$V@*kV$y*fFTP@g5(aB0HW-n&_8eVHh*vQ zw%9u|Dtvx0zhY6z_<8XInIuDF3(3I>VlvYGjm4$gvF?- zK2g;J|IizJg3x+yI0#(q@%GEb;S^nbLQ(KcBpeq_*tOQTHdA`=6Fa!T!1%$yIl=b+ zK7)be_WM?WD&v&t%*%?5IBwiteQ<;27GGS?!10`AcV7)#p}~vlN#+sq?T{e3qv;G# zk-c>QS z4t#!wq)(z*XszGUju62-{0^bi_*8P4MguAIl#l4p5Oj;>e$#I5)}8Nh{sO&59As@L1~{ zO~i(^%z_sRE>Pv2;!z#b^*kUBP~a~i4u*mE`v^*$7om1JX>Y+smZic3)1Pz?LwUuQ zsYwZ=P#kOm1Qht&Z36UvPMj0$$q00o>Ar@5UgqCUL-PCmz?aC}mR;-QXVxBktO>f$ z_Q0rGC}t)C5+5O0x4%uX{pwyna1G>MKX7Jdn$ont@7R_*f1S-^MZd8wa3B22&GYoe zH!&N0yE3R^#X3TIyb{TXg1h4BW6+Se1p}hTBI(rC#i%cDw?#qI_g@ZZ zj2mHs9xwY3kAUob5<{TK=jk73x_nml!8C>8;dj!yvA4HT{Q$EjVY`mq4FvYj)(dl} ziq}h~ZZNM5UP|YGA?LeXfcT-C9ojHI^j#(VsG0bssWDfZ>Vb-xxaz0y_u(i;FFZ=> zy3>IL{z4v+pNcANR=fDs9GE!i)O zL@QxS<{y6T2Yz;|>oRNdJ-6+hLNKL>UOAU{@*(9bJl5r+SZ4Zq_)^%g5I^(-Uzq8H z#p5!ib5WXjhQN*ysz2jzS924>&#A9$3dcoZe6JrkD;}h9D60kn@}bZ_t{<2X_G>?| zG#3QumeF&J;ewqCPB^h(QhjaAH077i2V^#(+?LHcJG~3X^-3FLUxJ)2JNRs$V~g6{ z(A^!M+zny9DV-F7xtT+jb8S~`FhY3RdCa$zn_x`FqHy42?-bXn{s4GB*#1&LV@_Jf z;obqASt}fYx{LyD_Dlo>=1W*vi+s@FTP4JA_Xk;WDeAr2&1)B2eUv-=&

ecf&5+ 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 Date: Wed, 21 Jul 2021 10:21:40 +0100 Subject: [PATCH 3506/3817] Improve error handing in tests When error is not expected in tests, assert that there is indeed no error. This commit was moved from ipld/go-car@ffcc4b77dd181ed7b2179e7e4013b70461473b22 --- ipld/car/v2/index/index_test.go | 1 + ipld/car/v2/index_gen_test.go | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ipld/car/v2/index/index_test.go b/ipld/car/v2/index/index_test.go index 03efbc94d..acafffe13 100644 --- a/ipld/car/v2/index/index_test.go +++ b/ipld/car/v2/index/index_test.go @@ -39,6 +39,7 @@ func TestNew(t *testing.T) { if tt.wantErr { require.Error(t, err) } else { + require.NoError(t, err) require.Equal(t, tt.want, got) } }) diff --git a/ipld/car/v2/index_gen_test.go b/ipld/car/v2/index_gen_test.go index 97eb244b4..058d07e6e 100644 --- a/ipld/car/v2/index_gen_test.go +++ b/ipld/car/v2/index_gen_test.go @@ -98,9 +98,11 @@ func TestReadOrGenerateIndex(t *testing.T) { got, err := ReadOrGenerateIndex(carFile, tt.readOpts...) if tt.wantErr { require.Error(t, err) + } else { + require.NoError(t, err) + want := tt.wantIndexer(t) + require.Equal(t, want, got) } - want := tt.wantIndexer(t) - require.Equal(t, want, got) }) } } @@ -143,9 +145,11 @@ func TestGenerateIndexFromFile(t *testing.T) { got, err := GenerateIndexFromFile(tt.carPath) if tt.wantErr { require.Error(t, err) + } else { + require.NoError(t, err) + want := tt.wantIndexer(t) + require.Equal(t, want, got) } - want := tt.wantIndexer(t) - require.Equal(t, want, got) }) } } From f99c88be186a5b0402d3ffe58943a8d57f5891b2 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 22 Jul 2021 15:47:31 -0700 Subject: [PATCH 3507/3817] fix: remove deprecated calls This commit was moved from ipfs/go-ipns@a2d4e93f7e8ffc9f996471eb1a24ff12c8484120 --- ipns/ipns.go | 2 +- ipns/validate_test.go | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ipns/ipns.go b/ipns/ipns.go index f94863c35..f85b1ad8e 100644 --- a/ipns/ipns.go +++ b/ipns/ipns.go @@ -273,7 +273,7 @@ func EmbedPublicKey(pk ic.PubKey, entry *pb.IpnsEntry) error { // We failed to extract the public key from the peer ID, embed it in the // record. - pkBytes, err := pk.Bytes() + pkBytes, err := ic.MarshalPublicKey(pk) if err != nil { return err } diff --git a/ipns/validate_test.go b/ipns/validate_test.go index 276e6d4da..40b41b6e3 100644 --- a/ipns/validate_test.go +++ b/ipns/validate_test.go @@ -17,13 +17,13 @@ import ( proto "github.com/gogo/protobuf/proto" u "github.com/ipfs/go-ipfs-util" - ci "github.com/libp2p/go-libp2p-core/crypto" + "github.com/libp2p/go-libp2p-core/crypto" peer "github.com/libp2p/go-libp2p-core/peer" pstore "github.com/libp2p/go-libp2p-core/peerstore" pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem" ) -func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, key string, val []byte, eol time.Time, exp error) { +func testValidatorCase(t *testing.T, priv crypto.PrivKey, kbook pstore.KeyBook, key string, val []byte, eol time.Time, exp error) { t.Helper() match := func(t *testing.T, err error) { @@ -43,7 +43,7 @@ func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, key testValidatorCaseMatchFunc(t, priv, kbook, key, val, eol, match) } -func testValidatorCaseMatchFunc(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, key string, val []byte, eol time.Time, matchf func(*testing.T, error)) { +func testValidatorCaseMatchFunc(t *testing.T, priv crypto.PrivKey, kbook pstore.KeyBook, key string, val []byte, eol time.Time, matchf func(*testing.T, error)) { t.Helper() validator := Validator{kbook} @@ -110,7 +110,7 @@ func TestEmbeddedPubKeyValidate(t *testing.T) { testValidatorCase(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, ErrPublicKeyNotFound) - pubkb, err := priv.GetPublic().Bytes() + pubkb, err := crypto.MarshalPublicKey(priv.GetPublic()) if err != nil { t.Fatal(err) } @@ -126,7 +126,7 @@ func TestEmbeddedPubKeyValidate(t *testing.T) { }) opriv, _, _ := genKeys(t) - wrongkeydata, err := opriv.GetPublic().Bytes() + wrongkeydata, err := crypto.MarshalPublicKey(opriv.GetPublic()) if err != nil { t.Fatal(err) } @@ -143,7 +143,7 @@ func TestPeerIDPubKeyValidate(t *testing.T) { pth := []byte("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") - sk, pk, err := ci.GenerateEd25519Key(rand.New(rand.NewSource(42))) + sk, pk, err := crypto.GenerateEd25519Key(rand.New(rand.NewSource(42))) if err != nil { t.Fatal(err) } @@ -171,7 +171,7 @@ func TestPeerIDPubKeyValidate(t *testing.T) { func TestBothSignatureVersionsValidate(t *testing.T) { goodeol := time.Now().Add(time.Hour) - sk, pk, err := ci.GenerateEd25519Key(rand.New(rand.NewSource(42))) + sk, pk, err := crypto.GenerateEd25519Key(rand.New(rand.NewSource(42))) if err != nil { t.Fatal(err) } @@ -200,7 +200,7 @@ func TestBothSignatureVersionsValidate(t *testing.T) { func TestNewSignatureVersionPreferred(t *testing.T) { goodeol := time.Now().Add(time.Hour) - sk, pk, err := ci.GenerateEd25519Key(rand.New(rand.NewSource(42))) + sk, pk, err := crypto.GenerateEd25519Key(rand.New(rand.NewSource(42))) if err != nil { t.Fatal(err) } @@ -272,7 +272,7 @@ func TestNewSignatureVersionPreferred(t *testing.T) { func TestCborDataCanonicalization(t *testing.T) { goodeol := time.Now().Add(time.Hour) - sk, pk, err := ci.GenerateEd25519Key(rand.New(rand.NewSource(42))) + sk, pk, err := crypto.GenerateEd25519Key(rand.New(rand.NewSource(42))) if err != nil { t.Fatal(err) } @@ -372,9 +372,9 @@ func TestCborDataCanonicalization(t *testing.T) { } } -func genKeys(t *testing.T) (ci.PrivKey, peer.ID, string) { +func genKeys(t *testing.T) (crypto.PrivKey, peer.ID, string) { sr := u.NewTimeSeededRand() - priv, _, err := ci.GenerateKeyPairWithReader(ci.RSA, 2048, sr) + priv, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, sr) if err != nil { t.Fatal(err) } From 3d1dd48587f136d349d9105fe10033e583545578 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 22 Jul 2021 15:51:34 -0700 Subject: [PATCH 3508/3817] fix: remove deprecated call to pk.Bytes And rename import. This commit was moved from ipfs/go-namesys@863ceca683c01626aa03c70921e9c602416fa677 --- namesys/publisher.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index f67a8bf52..307b3920c 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -12,7 +12,7 @@ import ( ipns "github.com/ipfs/go-ipns" pb "github.com/ipfs/go-ipns/pb" path "github.com/ipfs/go-path" - ci "github.com/libp2p/go-libp2p-core/crypto" + "github.com/libp2p/go-libp2p-core/crypto" peer "github.com/libp2p/go-libp2p-core/peer" routing "github.com/libp2p/go-libp2p-core/routing" base32 "github.com/whyrusleeping/base32" @@ -45,7 +45,7 @@ func NewIpnsPublisher(route routing.ValueStore, ds ds.Datastore) *IpnsPublisher // Publish implements Publisher. Accepts a keypair and a value, // and publishes it out to the routing system -func (p *IpnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error { +func (p *IpnsPublisher) Publish(ctx context.Context, k crypto.PrivKey, value path.Path) error { log.Debugf("Publish %s", value) return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordEOL)) } @@ -140,7 +140,7 @@ func (p *IpnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouti return e, nil } -func (p *IpnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) (*pb.IpnsEntry, error) { +func (p *IpnsPublisher) updateRecord(ctx context.Context, k crypto.PrivKey, value path.Path, eol time.Time) (*pb.IpnsEntry, error) { id, err := peer.IDFromPrivateKey(k) if err != nil { return nil, err @@ -190,7 +190,7 @@ func (p *IpnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value pa // PublishWithEOL is a temporary stand in for the ipns records implementation // see here for more details: https://github.com/ipfs/specs/tree/master/records -func (p *IpnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error { +func (p *IpnsPublisher) PublishWithEOL(ctx context.Context, k crypto.PrivKey, value path.Path, eol time.Time) error { record, err := p.updateRecord(ctx, k, value, eol) if err != nil { return err @@ -215,7 +215,7 @@ func checkCtxTTL(ctx context.Context) (time.Duration, bool) { // PutRecordToRouting publishes the given entry using the provided ValueStore, // keyed on the ID associated with the provided public key. The public key is // also made available to the routing system so that entries can be verified. -func PutRecordToRouting(ctx context.Context, r routing.ValueStore, k ci.PubKey, entry *pb.IpnsEntry) error { +func PutRecordToRouting(ctx context.Context, r routing.ValueStore, k crypto.PubKey, entry *pb.IpnsEntry) error { ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -265,9 +265,9 @@ func waitOnErrChan(ctx context.Context, errs chan error) error { // PublishPublicKey stores the given public key in the ValueStore with the // given key. -func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk ci.PubKey) error { +func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk crypto.PubKey) error { log.Debugf("Storing pubkey at: %s", k) - pkbytes, err := pubk.Bytes() + pkbytes, err := crypto.MarshalPublicKey(pubk) if err != nil { return err } From 985af925de0d5f2085b3cd6d75bdb8d5efdd2bad Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Fri, 23 Jul 2021 12:02:00 +0100 Subject: [PATCH 3509/3817] Add zero-length sections as EOF option to internal CARv1 reader The CARv2 implementation uses an internal fork of CARv1 due to upstream dependency issues captured in #104. Propagate the options set in CARv2 APIs for treatment of zero-lenth sections onto internal packages so that APIs using the internal CARv1 reader behave consistently. Use a `bool` for setting the option, since it is the only option needed in CARv1. Update tests to reflect changes. Add additional tests to internal CARv1 package and ReadOnly blockstore that assert option is propagated. Fixes #190 This commit was moved from ipld/go-car@703b88c25262f019bd1a4be7aa213fb04785e53a --- ipld/car/v2/blockstore/readonly.go | 4 +-- ipld/car/v2/blockstore/readonly_test.go | 40 ++++++++++++++++++------- ipld/car/v2/index/index_test.go | 2 +- ipld/car/v2/internal/carv1/car.go | 22 ++++++++++---- ipld/car/v2/internal/carv1/car_test.go | 34 +++++++++++++++++++++ ipld/car/v2/internal/carv1/util/util.go | 8 +++-- 6 files changed, 88 insertions(+), 22 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 6c2397f99..fcd973456 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -162,7 +162,7 @@ func OpenReadOnly(path string, opts ...carv2.ReadOption) (*ReadOnly, error) { } func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) { - bcid, data, err := util.ReadNode(internalio.NewOffsetReadSeeker(b.backing, idx)) + bcid, data, err := util.ReadNode(internalio.NewOffsetReadSeeker(b.backing, idx), b.ropts.ZeroLengthSectionAsEOF) return bcid, data, err } @@ -252,7 +252,7 @@ func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { b.mu.RLock() defer b.mu.RUnlock() - var fnSize int = -1 + fnSize := -1 var fnErr error err := b.idx.GetAll(key, func(offset uint64) bool { rdr := internalio.NewOffsetReadSeeker(b.backing, int64(offset)) diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 4d443c3cf..470ace2cc 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -34,25 +34,38 @@ func TestReadOnly(t *testing.T) { tests := []struct { name string v1OrV2path string + opts []carv2.ReadOption v1r *carv1.CarReader }{ { "OpenedWithCarV1", "../testdata/sample-v1.car", - newReaderFromV1File(t, "../testdata/sample-v1.car"), + []carv2.ReadOption{UseWholeCIDs(true)}, + newV1ReaderFromV1File(t, "../testdata/sample-v1.car", false), }, { "OpenedWithCarV2", "../testdata/sample-wrapped-v2.car", - newReaderFromV2File(t, "../testdata/sample-wrapped-v2.car"), + []carv2.ReadOption{UseWholeCIDs(true)}, + newV1ReaderFromV2File(t, "../testdata/sample-wrapped-v2.car", false), + }, + { + "OpenedWithCarV1ZeroLenSection", + "../testdata/sample-v1-with-zero-len-section.car", + []carv2.ReadOption{UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, + newV1ReaderFromV1File(t, "../testdata/sample-v1-with-zero-len-section.car", true), + }, + { + "OpenedWithAnotherCarV1ZeroLenSection", + "../testdata/sample-v1-with-zero-len-section2.car", + []carv2.ReadOption{UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, + newV1ReaderFromV1File(t, "../testdata/sample-v1-with-zero-len-section2.car", true), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - subject, err := OpenReadOnly(tt.v1OrV2path, - UseWholeCIDs(true), - ) - t.Cleanup(func() { subject.Close() }) + subject, err := OpenReadOnly(tt.v1OrV2path, tt.opts...) + t.Cleanup(func() { require.NoError(t, subject.Close()) }) require.NoError(t, err) // Assert roots match v1 payload. @@ -118,22 +131,29 @@ func TestNewReadOnlyFailsOnUnknownVersion(t *testing.T) { require.Nil(t, subject) } -func newReaderFromV1File(t *testing.T, carv1Path string) *carv1.CarReader { +func newV1ReaderFromV1File(t *testing.T, carv1Path string, zeroLenSectionAsEOF bool) *carv1.CarReader { f, err := os.Open(carv1Path) require.NoError(t, err) t.Cleanup(func() { f.Close() }) - v1r, err := carv1.NewCarReader(f) + v1r, err := newV1Reader(f, zeroLenSectionAsEOF) require.NoError(t, err) return v1r } -func newReaderFromV2File(t *testing.T, carv2Path string) *carv1.CarReader { +func newV1ReaderFromV2File(t *testing.T, carv2Path string, zeroLenSectionAsEOF bool) *carv1.CarReader { f, err := os.Open(carv2Path) require.NoError(t, err) t.Cleanup(func() { f.Close() }) v2r, err := carv2.NewReader(f) require.NoError(t, err) - v1r, err := carv1.NewCarReader(v2r.DataReader()) + v1r, err := newV1Reader(v2r.DataReader(), zeroLenSectionAsEOF) require.NoError(t, err) return v1r } + +func newV1Reader(r io.Reader, zeroLenSectionAsEOF bool) (*carv1.CarReader, error) { + if zeroLenSectionAsEOF { + return carv1.NewCarReaderWithZeroLengthSectionAsEOF(r) + } + return carv1.NewCarReader(r) +} diff --git a/ipld/car/v2/index/index_test.go b/ipld/car/v2/index/index_test.go index acafffe13..bb369cfe0 100644 --- a/ipld/car/v2/index/index_test.go +++ b/ipld/car/v2/index/index_test.go @@ -77,7 +77,7 @@ func TestReadFrom(t *testing.T) { require.NoError(t, err) // Read the fame at offset and assert the frame corresponds to the expected block. - gotCid, gotData, err := util.ReadNode(crf) + gotCid, gotData, err := util.ReadNode(crf, false) require.NoError(t, err) gotBlock, err := blocks.NewBlockWithCid(gotData, gotCid) require.NoError(t, err) diff --git a/ipld/car/v2/internal/carv1/car.go b/ipld/car/v2/internal/carv1/car.go index 48b4f3eeb..6a25e667f 100644 --- a/ipld/car/v2/internal/carv1/car.go +++ b/ipld/car/v2/internal/carv1/car.go @@ -57,7 +57,7 @@ func WriteCar(ctx context.Context, ds format.NodeGetter, roots []cid.Cid, w io.W } func ReadHeader(r io.Reader) (*CarHeader, error) { - hb, err := util.LdRead(r) + hb, err := util.LdRead(r, false) if err != nil { return nil, err } @@ -106,11 +106,20 @@ func (cw *carWriter) writeNode(ctx context.Context, nd format.Node) error { } type CarReader struct { - r io.Reader - Header *CarHeader + r io.Reader + Header *CarHeader + zeroLenAsEOF bool +} + +func NewCarReaderWithZeroLengthSectionAsEOF(r io.Reader) (*CarReader, error) { + return newCarReader(r, true) } func NewCarReader(r io.Reader) (*CarReader, error) { + return newCarReader(r, false) +} + +func newCarReader(r io.Reader, zeroLenAsEOF bool) (*CarReader, error) { ch, err := ReadHeader(r) if err != nil { return nil, err @@ -125,13 +134,14 @@ func NewCarReader(r io.Reader) (*CarReader, error) { } return &CarReader{ - r: r, - Header: ch, + r: r, + Header: ch, + zeroLenAsEOF: zeroLenAsEOF, }, nil } func (cr *CarReader) Next() (blocks.Block, error) { - c, data, err := util.ReadNode(cr.r) + c, data, err := util.ReadNode(cr.r, cr.zeroLenAsEOF) if err != nil { return nil, err } diff --git a/ipld/car/v2/internal/carv1/car_test.go b/ipld/car/v2/internal/carv1/car_test.go index 70ce7d859..71e06ee93 100644 --- a/ipld/car/v2/internal/carv1/car_test.go +++ b/ipld/car/v2/internal/carv1/car_test.go @@ -5,6 +5,7 @@ import ( "context" "encoding/hex" "io" + "os" "strings" "testing" @@ -297,3 +298,36 @@ func TestCarHeaderMatchess(t *testing.T) { }) } } + +func TestReadingZeroLengthSectionWithoutOptionSetIsError(t *testing.T) { + f, err := os.Open("../../testdata/sample-v1-with-zero-len-section.car") + require.NoError(t, err) + subject, err := NewCarReader(f) + require.NoError(t, err) + + for { + _, err := subject.Next() + if err == io.EOF { + break + } else if err != nil { + require.EqualError(t, err, "varints malformed, could not reach the end") + return + } + } + require.Fail(t, "expected error when reading file with zero section without option set") +} + +func TestReadingZeroLengthSectionWithOptionSetIsSuccess(t *testing.T) { + f, err := os.Open("../../testdata/sample-v1-with-zero-len-section.car") + require.NoError(t, err) + subject, err := NewCarReaderWithZeroLengthSectionAsEOF(f) + require.NoError(t, err) + + for { + _, err := subject.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + } +} diff --git a/ipld/car/v2/internal/carv1/util/util.go b/ipld/car/v2/internal/carv1/util/util.go index dce53003b..6b9495613 100644 --- a/ipld/car/v2/internal/carv1/util/util.go +++ b/ipld/car/v2/internal/carv1/util/util.go @@ -15,8 +15,8 @@ type BytesReader interface { io.ByteReader } -func ReadNode(r io.Reader) (cid.Cid, []byte, error) { - data, err := LdRead(r) +func ReadNode(r io.Reader, zeroLenAsEOF bool) (cid.Cid, []byte, error) { + data, err := LdRead(r, zeroLenAsEOF) if err != nil { return cid.Cid{}, nil, err } @@ -61,7 +61,7 @@ func LdSize(d ...[]byte) uint64 { return sum + uint64(s) } -func LdRead(r io.Reader) ([]byte, error) { +func LdRead(r io.Reader, zeroLenAsEOF bool) ([]byte, error) { l, err := varint.ReadUvarint(internalio.ToByteReader(r)) if err != nil { // If the length of bytes read is non-zero when the error is EOF then signal an unclean EOF. @@ -69,6 +69,8 @@ func LdRead(r io.Reader) ([]byte, error) { return nil, io.ErrUnexpectedEOF } return nil, err + } else if l == 0 && zeroLenAsEOF { + return nil, io.EOF } buf := make([]byte, l) From 9fccb1714375cf6a6982dc4903a9937b6e4b945d Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Fri, 23 Jul 2021 10:24:42 +0100 Subject: [PATCH 3510/3817] Propagate async `blockstore.AllKeysChan` errors via context Implement a mechanism that allows the user to hook an error handling function to the context passed to `AllKeysChan`. The function is then notified when an error occurs during asynchronous traversal of data payload. Add tests that assert the success and failure cases when error handler is set. Fixes #177 This commit was moved from ipld/go-car@de2711c09ab3267aba84f7b8bd8ecaaeb4885f21 --- ipld/car/v2/blockstore/readonly.go | 88 +++++++++++++++++-------- ipld/car/v2/blockstore/readonly_test.go | 78 ++++++++++++++++++++++ 2 files changed, 139 insertions(+), 27 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index fcd973456..dbf7bf61b 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -24,26 +24,34 @@ import ( var _ blockstore.Blockstore = (*ReadOnly)(nil) -// ReadOnly provides a read-only CAR Block Store. -type ReadOnly struct { - // mu allows ReadWrite to be safe for concurrent use. - // It's in ReadOnly so that read operations also grab read locks, - // given that ReadWrite embeds ReadOnly for methods like Get and Has. - // - // The main fields guarded by the mutex are the index and the underlying writers. - // For simplicity, the entirety of the blockstore methods grab the mutex. - mu sync.RWMutex - - // The backing containing the data payload in CARv1 format. - backing io.ReaderAt - // The CARv1 content index. - idx index.Index - - // If we called carv2.NewReaderMmap, remember to close it too. - carv2Closer io.Closer - - ropts carv2.ReadOptions -} +var errZeroLengthSection = fmt.Errorf("zero-length section not allowed by default; see WithZeroLengthSectionAsEOF option") + +type ( + // ReadOnly provides a read-only CAR Block Store. + ReadOnly struct { + // mu allows ReadWrite to be safe for concurrent use. + // It's in ReadOnly so that read operations also grab read locks, + // given that ReadWrite embeds ReadOnly for methods like Get and Has. + // + // The main fields guarded by the mutex are the index and the underlying writers. + // For simplicity, the entirety of the blockstore methods grab the mutex. + mu sync.RWMutex + + // The backing containing the data payload in CARv1 format. + backing io.ReaderAt + // The CARv1 content index. + idx index.Index + + // If we called carv2.NewReaderMmap, remember to close it too. + carv2Closer io.Closer + + ropts carv2.ReadOptions + } + + contextKey string +) + +const asyncErrHandlerKey contextKey = "asyncErrorHandlerKey" // UseWholeCIDs is a read option which makes a CAR blockstore identify blocks by // whole CIDs, and not just their multihashes. The default is to use @@ -303,7 +311,19 @@ func (b *ReadOnly) PutMany([]blocks.Block) error { panic("called write method on a read-only blockstore") } -// AllKeysChan returns the list of keys in the CAR. +// WithAsyncErrorHandler returns a context with async error handling set to the given errHandler. +// Any errors that occur during asynchronous operations of AllKeysChan will be passed to the given +// handler. +func WithAsyncErrorHandler(ctx context.Context, errHandler func(error)) context.Context { + return context.WithValue(ctx, asyncErrHandlerKey, errHandler) +} + +// AllKeysChan returns the list of keys in the CAR data payload. +// If the ctx is constructed using WithAsyncErrorHandler any errors that occur during asynchronous +// retrieval of CIDs will be passed to the error handler function set in context. +// Otherwise, errors will terminate the asynchronous operation silently. +// +// See WithAsyncErrorHandler func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // We release the lock when the channel-sending goroutine stops. b.mu.RLock() @@ -334,7 +354,10 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { for { length, err := varint.ReadUvarint(rdr) if err != nil { - return // TODO: log this error + if err != io.EOF { + maybeReportError(ctx, err) + } + return } // Null padding; by default it's an error. @@ -342,18 +365,20 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { if b.ropts.ZeroLengthSectionAsEOF { break } else { - return // TODO: log this error - // return fmt.Errorf("carv1 null padding not allowed by default; see WithZeroLegthSectionAsEOF") + maybeReportError(ctx, errZeroLengthSection) + return } } thisItemForNxt := rdr.Offset() _, c, err := cid.CidFromReader(rdr) if err != nil { - return // TODO: log this error + maybeReportError(ctx, err) + return } if _, err := rdr.Seek(thisItemForNxt+int64(length), io.SeekStart); err != nil { - return // TODO: log this error + maybeReportError(ctx, err) + return } // If we're just using multihashes, flatten to the "raw" codec. @@ -364,7 +389,7 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { select { case ch <- c: case <-ctx.Done(): - // TODO: log ctx error + maybeReportError(ctx, ctx.Err()) return } } @@ -372,6 +397,15 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return ch, nil } +// maybeReportError checks if an error handler is present in context associated to the key +// asyncErrHandlerKey, and if preset it will pass the error to it. +func maybeReportError(ctx context.Context, err error) { + value := ctx.Value(asyncErrHandlerKey) + if eh, _ := value.(func(error)); eh != nil { + eh(err) + } +} + // HashOnRead is currently unimplemented; hashing on reads never happens. func (b *ReadOnly) HashOnRead(bool) { // TODO: implement before the final release? diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 470ace2cc..ecc30e1d1 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -131,6 +131,84 @@ func TestNewReadOnlyFailsOnUnknownVersion(t *testing.T) { require.Nil(t, subject) } +func TestReadOnlyAllKeysChanErrHandlerCalledOnTimeout(t *testing.T) { + expiredCtx, cancel := context.WithTimeout(context.Background(), -time.Millisecond) + t.Cleanup(cancel) + + subject, err := OpenReadOnly("../testdata/sample-v1.car") + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, subject.Close()) }) + + // Make a channel to be able to select and block on until error handler is called. + errHandlerCalled := make(chan interface{}) + expiredErrHandlingCtx := WithAsyncErrorHandler(expiredCtx, func(err error) { + defer close(errHandlerCalled) + require.EqualError(t, err, "context deadline exceeded") + }) + _, err = subject.AllKeysChan(expiredErrHandlingCtx) + require.NoError(t, err) + + // Assert error handler was called with required condition, waiting at most 3 seconds. + select { + case <-errHandlerCalled: + break + case <-time.After(time.Second * 3): + require.Fail(t, "error handler was not called within expected time window") + } +} + +func TestReadOnlyAllKeysChanErrHandlerNeverCalled(t *testing.T) { + tests := []struct { + name string + path string + errHandler func(err error) + wantCIDs []cid.Cid + }{ + { + "ReadingValidCarV1ReturnsNoErrors", + "../testdata/sample-v1.car", + func(err error) { + require.Fail(t, "unexpected call", "error handler called unexpectedly with err: %v", err) + }, + listCids(t, newV1ReaderFromV1File(t, "../testdata/sample-v1.car", false)), + }, + { + "ReadingValidCarV2ReturnsNoErrors", + "../testdata/sample-wrapped-v2.car", + func(err error) { + require.Fail(t, "unexpected call", "error handler called unexpectedly with err: %v", err) + }, + listCids(t, newV1ReaderFromV2File(t, "../testdata/sample-wrapped-v2.car", false)), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + subject, err := OpenReadOnly(tt.path, UseWholeCIDs(true)) + require.NoError(t, err) + ctx := WithAsyncErrorHandler(context.Background(), tt.errHandler) + keysChan, err := subject.AllKeysChan(ctx) + require.NoError(t, err) + var gotCids []cid.Cid + for k := range keysChan { + gotCids = append(gotCids, k) + } + require.Equal(t, tt.wantCIDs, gotCids) + }) + } +} + +func listCids(t *testing.T, v1r *carv1.CarReader) (cids []cid.Cid) { + for { + block, err := v1r.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + cids = append(cids, block.Cid()) + } + return +} + func newV1ReaderFromV1File(t *testing.T, carv1Path string, zeroLenSectionAsEOF bool) *carv1.CarReader { f, err := os.Open(carv1Path) require.NoError(t, err) From 7d6ac7be50e69cfcc7fdcc492413e8e0da913a51 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Fri, 23 Jul 2021 18:30:38 +0100 Subject: [PATCH 3511/3817] Implement reader block iterator over CARv1 or CARv2 Implement the mechanism to iterate over blocks in CARv1 or CARv2 via the `carv2.Reader` API. Implement tests both for the iterator and the rest of the reader API. Add test files with corrupt sections etc. Fixes #189 This commit was moved from ipld/go-car@d393a8377dcd52ed356c3f8da53499414e475622 --- ipld/car/v2/reader.go | 96 ++++++-- ipld/car/v2/reader_test.go | 231 ++++++++++++++++++ .../car/v2/testdata/sample-corrupt-pragma.car | 1 + .../sample-v1-tailing-corrupt-section.car | Bin 0 -> 479894 bytes .../sample-v2-corrupt-data-and-index.car | Bin 0 -> 521859 bytes 5 files changed, 301 insertions(+), 27 deletions(-) create mode 100644 ipld/car/v2/reader_test.go create mode 100644 ipld/car/v2/testdata/sample-corrupt-pragma.car create mode 100644 ipld/car/v2/testdata/sample-v1-tailing-corrupt-section.car create mode 100644 ipld/car/v2/testdata/sample-v2-corrupt-data-and-index.car diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 98d3715d3..b4b4d0d45 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -4,6 +4,8 @@ import ( "fmt" "io" + blocks "github.com/ipfs/go-block-format" + internalio "github.com/ipld/go-car/v2/internal/io" "github.com/ipfs/go-cid" @@ -13,10 +15,16 @@ import ( // Reader represents a reader of CARv2. type Reader struct { - Header Header - r io.ReaderAt - roots []cid.Cid - closer io.Closer + Header Header + Version uint64 + r io.ReaderAt + roots []cid.Cid + ropts ReadOptions + // carV1Reader is lazily created, is not reusable, and exclusively used by Reader.Next. + // Note, this reader is forward-only and cannot be rewound. Once it reaches the end of the data + // payload, it will always return io.EOF. + carV1Reader *carv1.CarReader + closer io.Closer } // OpenReader is a wrapper for NewReader which opens the file at path. @@ -35,32 +43,39 @@ func OpenReader(path string, opts ...ReadOption) (*Reader, error) { return r, nil } -// NewReader constructs a new reader that reads CARv2 from the given r. -// Upon instantiation, the reader inspects the payload by reading the pragma and will return -// an error if the pragma does not represent a CARv2. +// NewReader constructs a new reader that reads either CARv1 or CARv2 from the given r. +// Upon instantiation, the reader inspects the payload and provides appropriate read operations +// for both CARv1 and CARv2. +// +// Note that any other version other than 1 or 2 will result in an error. The caller may use +// Reader.Version to get the actual version r represents. In the case where r represents a CARv1 +// Reader.Header will not be populated and is left as zero-valued. func NewReader(r io.ReaderAt, opts ...ReadOption) (*Reader, error) { cr := &Reader{ r: r, } - if err := cr.requireVersion2(); err != nil { - return nil, err + for _, o := range opts { + o(&cr.ropts) } - if err := cr.readHeader(); err != nil { + + or := internalio.NewOffsetReadSeeker(r, 0) + var err error + cr.Version, err = ReadVersion(or) + if err != nil { return nil, err } - return cr, nil -} -func (r *Reader) requireVersion2() (err error) { - or := internalio.NewOffsetReadSeeker(r.r, 0) - version, err := ReadVersion(or) - if err != nil { - return + if cr.Version != 1 && cr.Version != 2 { + return nil, fmt.Errorf("invalid car version: %d", cr.Version) } - if version != 2 { - return fmt.Errorf("invalid car version: %d", version) + + if cr.Version == 2 { + if err := cr.readV2Header(); err != nil { + return nil, err + } } - return + + return cr, nil } // Roots returns the root CIDs. @@ -77,7 +92,7 @@ func (r *Reader) Roots() ([]cid.Cid, error) { return r.roots, nil } -func (r *Reader) readHeader() (err error) { +func (r *Reader) readV2Header() (err error) { headerSection := io.NewSectionReader(r.r, PragmaSize, HeaderSize) _, err = r.Header.ReadFrom(headerSection) return @@ -94,12 +109,20 @@ type SectionReader interface { // DataReader provides a reader containing the data payload in CARv1 format. func (r *Reader) DataReader() SectionReader { - return io.NewSectionReader(r.r, int64(r.Header.DataOffset), int64(r.Header.DataSize)) + if r.Version == 2 { + return io.NewSectionReader(r.r, int64(r.Header.DataOffset), int64(r.Header.DataSize)) + } + return internalio.NewOffsetReadSeeker(r.r, 0) } -// IndexReader provides an io.Reader containing the index for the data payload. +// IndexReader provides an io.Reader containing the index for the data payload if the index is +// present. Otherwise, returns nil. +// Note, this function will always return nil if the backing payload represents a CARv1. func (r *Reader) IndexReader() io.Reader { - return internalio.NewOffsetReadSeeker(r.r, int64(r.Header.IndexOffset)) + if r.Version == 2 { + return internalio.NewOffsetReadSeeker(r.r, int64(r.Header.IndexOffset)) + } + return nil } // Close closes the underlying reader if it was opened by OpenReader. @@ -110,13 +133,32 @@ func (r *Reader) Close() error { return nil } +// Next reads the next block in the data payload with an io.EOF error indicating the end is reached. +// Note, this function is forward-only; once the end has been reached it will always return io.EOF. +func (r *Reader) Next() (blocks.Block, error) { + if r.carV1Reader == nil { + var err error + if r.carV1Reader, err = r.newCarV1Reader(); err != nil { + return nil, err + } + } + return r.carV1Reader.Next() +} + +func (r *Reader) newCarV1Reader() (*carv1.CarReader, error) { + dr := r.DataReader() + if r.ropts.ZeroLengthSectionAsEOF { + return carv1.NewCarReaderWithZeroLengthSectionAsEOF(dr) + } + return carv1.NewCarReader(dr) +} + // ReadVersion reads the version from the pragma. // This function accepts both CARv1 and CARv2 payloads. -func ReadVersion(r io.Reader) (version uint64, err error) { - // TODO if the user provides a reader that sufficiently satisfies what carv1.ReadHeader is asking then use that instead of wrapping every time. +func ReadVersion(r io.Reader) (uint64, error) { header, err := carv1.ReadHeader(r) if err != nil { - return + return 0, err } return header.Version, nil } diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go new file mode 100644 index 000000000..8b5ec228f --- /dev/null +++ b/ipld/car/v2/reader_test.go @@ -0,0 +1,231 @@ +package car_test + +import ( + "io" + "os" + "testing" + + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-car/v2/internal/carv1" + "github.com/stretchr/testify/require" +) + +func TestReadVersion(t *testing.T) { + tests := []struct { + name string + path string + want uint64 + wantErr bool + }{ + { + name: "CarV1VersionIsOne", + path: "testdata/sample-v1.car", + want: 1, + }, + { + name: "CarV2VersionIsTwo", + path: "testdata/sample-rw-bs-v2.car", + want: 2, + }, + { + name: "CarV1VersionWithZeroLenSectionIsOne", + path: "testdata/sample-v1-with-zero-len-section.car", + want: 1, + }, + { + name: "AnotherCarV1VersionWithZeroLenSectionIsOne", + path: "testdata/sample-v1-with-zero-len-section2.car", + want: 1, + }, + { + name: "WrappedCarV1InCarV2VersionIsTwo", + path: "testdata/sample-wrapped-v2.car", + want: 2, + }, + { + name: "FutureVersionWithCorrectPragmaIsAsExpected", + path: "testdata/sample-rootless-v42.car", + want: 42, + }, + { + name: "CarV1WithValidHeaderButCorruptSectionIsOne", + path: "testdata/sample-v1-tailing-corrupt-section.car", + want: 1, + }, + { + name: "CarV2WithValidHeaderButCorruptSectionAndIndexIsTwo", + path: "testdata/sample-v2-corrupt-data-and-index.car", + want: 2, + }, + { + name: "CarFileWithCorruptPragmaIsError", + path: "testdata/sample-corrupt-pragma.car", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f, err := os.Open(tt.path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, f.Close()) }) + + got, err := carv2.ReadVersion(f) + if tt.wantErr { + require.Error(t, err, "ReadVersion() error = %v, wantErr %v", err, tt.wantErr) + } else { + require.NoError(t, err) + require.Equal(t, tt.want, got, "ReadVersion() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestReaderFailsOnUnknownVersion(t *testing.T) { + _, err := carv2.OpenReader("testdata/sample-rootless-v42.car") + require.EqualError(t, err, "invalid car version: 42") +} + +func TestReaderFailsOnCorruptPragma(t *testing.T) { + _, err := carv2.OpenReader("testdata/sample-corrupt-pragma.car") + require.EqualError(t, err, "unexpected EOF") +} + +func TestReader_WithCarV1Consistency(t *testing.T) { + tests := []struct { + name string + path string + zerLenAsEOF bool + }{ + { + name: "CarV1WithoutZeroLengthSection", + path: "testdata/sample-v1.car", + }, + { + name: "CarV1WithZeroLenSection", + path: "testdata/sample-v1-with-zero-len-section.car", + zerLenAsEOF: true, + }, + { + name: "AnotherCarV1WithZeroLenSection", + path: "testdata/sample-v1-with-zero-len-section2.car", + zerLenAsEOF: true, + }, + { + name: "CarV1WithZeroLenSectionWithoutOption", + path: "testdata/sample-v1-with-zero-len-section.car", + }, + { + name: "AnotherCarV1WithZeroLenSectionWithoutOption", + path: "testdata/sample-v1-with-zero-len-section2.car", + }, + { + name: "CorruptCarV1", + path: "testdata/sample-v1-tailing-corrupt-section.car", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + subject, err := carv2.OpenReader(tt.path, carv2.ZeroLengthSectionAsEOF(tt.zerLenAsEOF)) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, subject.Close()) }) + wantReader := requireNewCarV1ReaderFromV1File(t, tt.path, tt.zerLenAsEOF) + + require.Equal(t, uint64(1), subject.Version) + gotRoots, err := subject.Roots() + require.NoError(t, err) + require.Equal(t, wantReader.Header.Roots, gotRoots) + require.Nil(t, subject.IndexReader()) + + for { + gotBlock, gotErr := subject.Next() + wantBlock, wantErr := wantReader.Next() + require.Equal(t, wantBlock, gotBlock) + require.Equal(t, wantErr, gotErr) + if gotErr == io.EOF { + break + } + } + }) + } +} + +func TestReader_WithCarV2Consistency(t *testing.T) { + tests := []struct { + name string + path string + zerLenAsEOF bool + }{ + { + name: "CarV2WrappingV1", + path: "testdata/sample-wrapped-v2.car", + }, + { + name: "CarV2ProducedByBlockstore", + path: "testdata/sample-rw-bs-v2.car", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + subject, err := carv2.OpenReader(tt.path, carv2.ZeroLengthSectionAsEOF(tt.zerLenAsEOF)) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, subject.Close()) }) + wantReader := requireNewCarV1ReaderFromV2File(t, tt.path, tt.zerLenAsEOF) + + require.Equal(t, uint64(2), subject.Version) + gotRoots, err := subject.Roots() + require.NoError(t, err) + require.Equal(t, wantReader.Header.Roots, gotRoots) + + gotIndexReader := subject.IndexReader() + require.NotNil(t, gotIndexReader) + gotIndex, err := index.ReadFrom(gotIndexReader) + require.NoError(t, err) + wantIndex, err := carv2.GenerateIndex(subject.DataReader()) + require.NoError(t, err) + require.Equal(t, wantIndex, gotIndex) + + for { + gotBlock, gotErr := subject.Next() + wantBlock, wantErr := wantReader.Next() + require.Equal(t, wantBlock, gotBlock) + require.Equal(t, wantErr, gotErr) + if gotErr == io.EOF { + break + } + } + }) + } +} + +func requireNewCarV1ReaderFromV2File(t *testing.T, carV12Path string, zerLenAsEOF bool) *carv1.CarReader { + f, err := os.Open(carV12Path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, f.Close()) }) + + _, err = f.Seek(carv2.PragmaSize, io.SeekStart) + require.NoError(t, err) + header := carv2.Header{} + _, err = header.ReadFrom(f) + require.NoError(t, err) + return requireNewCarV1Reader(t, io.NewSectionReader(f, int64(header.DataOffset), int64(header.DataSize)), zerLenAsEOF) +} + +func requireNewCarV1ReaderFromV1File(t *testing.T, carV1Path string, zerLenAsEOF bool) *carv1.CarReader { + f, err := os.Open(carV1Path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, f.Close()) }) + return requireNewCarV1Reader(t, f, zerLenAsEOF) +} + +func requireNewCarV1Reader(t *testing.T, r io.Reader, zerLenAsEOF bool) *carv1.CarReader { + var cr *carv1.CarReader + var err error + if zerLenAsEOF { + cr, err = carv1.NewCarReaderWithZeroLengthSectionAsEOF(r) + } else { + cr, err = carv1.NewCarReader(r) + } + require.NoError(t, err) + return cr +} diff --git a/ipld/car/v2/testdata/sample-corrupt-pragma.car b/ipld/car/v2/testdata/sample-corrupt-pragma.car new file mode 100644 index 000000000..1ac73be1e --- /dev/null +++ b/ipld/car/v2/testdata/sample-corrupt-pragma.car @@ -0,0 +1 @@ +¢erootsögversio \ No newline at end of file diff --git a/ipld/car/v2/testdata/sample-v1-tailing-corrupt-section.car b/ipld/car/v2/testdata/sample-v1-tailing-corrupt-section.car new file mode 100644 index 0000000000000000000000000000000000000000..23f8308a67bb87c1f53184349a16ca07ce45f48a GIT binary patch literal 479894 zcmdSBRZtz;(yooW>%`q5XmHoy?ry=|-QC@TySuwXfZ!6`JxGAyhwQW0D%N+C|GW6B z&f>a9O{&Ly-nU1O9+NxI*xts*(ZO$tT8#n-*e+)mgz#PvS&t~k82RzAB9>MjOd+o_4OvAu(tjWzIhi2rX3qJ)6ff&J9kP7mV^XFOuvNS(Jd z{qMJ#FJ(8=_wi8BHQ}u=;<6T|Q=!iO zNagh)J~5HNCL!IMHV1)65=PfX6{g=_2mH!-7ERt++QP$L zF9`?O)_d_m#~buJ~qfQNd<{?*_Wvv1wjvydgi3JwF;2fgnvgXnOaj(8EsnL_~UKh{jg9!E! zc+84I=z?-6Rn5l;4CEwW$VA)=+TrX@(`9fOd&f&m8=xi7@U5BpuZN>~jE^d+nv==6Y~5oF#z9g3{DpJZ9+ zqM&7n$6;!v9Wh$eR%gyd5|mWF1?0UjUad|(!F~szc|(H7>(*YkZsVb5g_^aIQqtT3 z>cGB*FMJcYB@ls{NOg*`EY6~`1oxFO^caW zb9d>NuU_afcpyN=P7H`+JOPVCWllW)JZpV3eKbT#AW<(7sMAE zK>YBc_O2;aFRm35{t<7uWR;p;fkY;ifZ*EjZ&eiFk?u^mJ-f8NrxRqaYZ{?0uZ*FWo zn^7jViqYO&QbyBAS{LLQZ1W|iI(m1Vnx2`cgiq(Av18j}s$TjnE_qiMQ+b#~?rXCm zgP#zVZF#(W2tUTKZ#Jz1%C|t()+?gr1}T~=%8^=MXQNdPD%VV5#9Vk3vjC~To4a8w ztT=1gvu1Vq(a-@|4NgW5ZU7-et0g(BrkCc!`p`=dQ$|00*B$Vj_HC~%SR5x>;%sh2 z>S?S?!1XewedO68u!GBRHu3ESDGG}+NV;{%p?J3V^� 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 Tx_{@>Ah$cBb;X>)D9fct)9`U;$XV@y4$E$ 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

hRd> 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

vHSrHL`4Y!))mrU|+My=J*?NCLvdFH?W^2AuF;5 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

ecf&5+ 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

  • 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

    g`WHLp*^ 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ώtL~geALh4$?$m5~zXBOJGVB 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

    o_luVG zHJ%0A;We5Ku|LgUTmLrsJuBfhVx;2ulgk_1 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 Ie$V^=0Iu6JN&o-= literal 0 HcmV?d00001 From e1880a0df237d26a90434f6835c49af69f8f7d6d Mon Sep 17 00:00:00 2001 From: Andrew Gillis Date: Thu, 29 Jul 2021 08:39:25 -0700 Subject: [PATCH 3512/3817] Fix/minimize rebuild (#15) * Sync pinner on every pin operation by default * Optimize reindexing by writing only corrupt pins * Sync periodically while reindexing * Log pinning and reindexing operations Co-authored-by: Petar Maymounkov This commit was moved from ipfs/go-ipfs-pinner@f708928dd30b209c6fc0d9aa2c3996fe0f8af22a --- pinning/pinner/dsindex/indexer.go | 32 +- pinning/pinner/dspinner/pin.go | 447 +++++++++++++++++---------- pinning/pinner/dspinner/pin_test.go | 336 ++++++++++++++++++-- pinning/pinner/dspinner/sync_test.go | 88 ++++++ 4 files changed, 694 insertions(+), 209 deletions(-) create mode 100644 pinning/pinner/dspinner/sync_test.go diff --git a/pinning/pinner/dsindex/indexer.go b/pinning/pinner/dsindex/indexer.go index e1119acac..884cd8025 100644 --- a/pinning/pinner/dsindex/indexer.go +++ b/pinning/pinner/dsindex/indexer.go @@ -112,40 +112,30 @@ func (x *indexer) ForEach(ctx context.Context, key string, fn func(key, value st if err != nil { return err } + defer results.Close() - for { - r, ok := results.NextSync() - if !ok { - break + for r := range results.Next() { + if ctx.Err() != nil { + return ctx.Err() } if r.Error != nil { - err = r.Error - break - } - if ctx.Err() != nil { - err = ctx.Err() - break + return fmt.Errorf("cannot read index: %v", r.Error) } ent := r.Entry - var decIdx string - decIdx, err = decode(path.Base(path.Dir(ent.Key))) + decIdx, err := decode(path.Base(path.Dir(ent.Key))) if err != nil { - err = fmt.Errorf("cannot decode index: %v", err) - break + return fmt.Errorf("cannot decode index: %v", err) } - var decKey string - decKey, err = decode(path.Base(ent.Key)) + decKey, err := decode(path.Base(ent.Key)) if err != nil { - err = fmt.Errorf("cannot decode key: %v", err) - break + return fmt.Errorf("cannot decode key: %v", err) } if !fn(decIdx, decKey) { - break + return nil } } - results.Close() - return err + return nil } func (x *indexer) HasValue(ctx context.Context, key, value string) (bool, error) { diff --git a/pinning/pinner/dspinner/pin.go b/pinning/pinner/dspinner/pin.go index b793cf2ea..a02f01547 100644 --- a/pinning/pinner/dspinner/pin.go +++ b/pinning/pinner/dspinner/pin.go @@ -84,7 +84,8 @@ func init() { // pinner implements the Pinner interface type pinner struct { - lock sync.RWMutex + autoSync bool + lock sync.RWMutex dserv ipld.DAGService dstore ds.Datastore @@ -113,7 +114,7 @@ func (p *pin) dsKey() ds.Key { func newPin(c cid.Cid, mode ipfspinner.Mode, name string) *pin { return &pin{ - Id: ds.RandomKey().String(), + Id: path.Base(ds.RandomKey().String()), Cid: c, Name: name, Mode: mode, @@ -127,8 +128,13 @@ type syncDAGService interface { // New creates a new pinner and loads its keysets from the given datastore. If // there is no data present in the datastore, then an empty pinner is returned. -func New(ctx context.Context, dstore ds.Datastore, dserv ipld.DAGService) (ipfspinner.Pinner, error) { +// +// By default, changes are automatically flushed to the datastore. This can be +// disabled by calling SetAutosync(false), which will require that Flush be +// called explicitly. +func New(ctx context.Context, dstore ds.Datastore, dserv ipld.DAGService) (*pinner, error) { p := &pinner{ + autoSync: true, cidDIndex: dsindex.New(dstore, ds.NewKey(pinCidDIndexPath)), cidRIndex: dsindex.New(dstore, ds.NewKey(pinCidRIndexPath)), nameIndex: dsindex.New(dstore, ds.NewKey(pinNameIndexPath)), @@ -146,12 +152,7 @@ func New(ctx context.Context, dstore ds.Datastore, dserv ipld.DAGService) (ipfsp if data[0] == 1 { p.dirty = 1 - pins, err := p.loadAllPins(ctx) - if err != nil { - return nil, fmt.Errorf("cannot load pins: %v", err) - } - - err = p.rebuildIndexes(ctx, pins) + err = p.rebuildIndexes(ctx) if err != nil { return nil, fmt.Errorf("cannot rebuild indexes: %v", err) } @@ -160,6 +161,17 @@ func New(ctx context.Context, dstore ds.Datastore, dserv ipld.DAGService) (ipfsp return p, nil } +// SetAutosync allows auto-syncing to be enabled or disabled during runtime. +// This may be used to turn off autosync before doing many repeated pinning +// operations, and then turn it on after. Returns the previous value. +func (p *pinner) SetAutosync(auto bool) bool { + p.lock.Lock() + defer p.lock.Unlock() + + p.autoSync, auto = auto, p.autoSync + return auto +} + // Pin the given node, optionally recursive func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { err := p.dserv.Add(ctx, node) @@ -193,6 +205,12 @@ func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { return err } + // If autosyncing, sync dag service before making any change to pins + err = p.flushDagService(ctx, false) + if err != nil { + return err + } + // Only look again if something has changed. if p.dirty != dirtyBefore { found, err = p.cidRIndex.HasAny(ctx, cidKey) @@ -210,7 +228,10 @@ func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { return err } if found { - p.removePinsForCid(ctx, c, ipfspinner.Direct) + _, err = p.removePinsForCid(ctx, c, ipfspinner.Direct) + if err != nil { + return err + } } _, err = p.addPin(ctx, c, ipfspinner.Recursive, "") @@ -231,7 +252,7 @@ func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { return err } } - return nil + return p.flushPins(ctx, false) } func (p *pinner) addPin(ctx context.Context, c cid.Cid, mode ipfspinner.Mode, name string) (string, error) { @@ -244,7 +265,13 @@ func (p *pinner) addPin(ctx context.Context, c cid.Cid, mode ipfspinner.Mode, na return "", fmt.Errorf("could not encode pin: %v", err) } - p.setDirty(ctx, true) + p.setDirty(ctx) + + // Store the pin + err = p.dstore.Put(pp.dsKey(), pinData) + if err != nil { + return "", err + } // Store CID index switch mode { @@ -263,36 +290,28 @@ func (p *pinner) addPin(ctx context.Context, c cid.Cid, mode ipfspinner.Mode, na // Store name index err = p.nameIndex.Add(ctx, name, pp.Id) if err != nil { + if mode == ipfspinner.Recursive { + e := p.cidRIndex.Delete(ctx, c.KeyString(), pp.Id) + if e != nil { + log.Errorf("error deleting index: %s", e) + } + } else { + e := p.cidDIndex.Delete(ctx, c.KeyString(), pp.Id) + if e != nil { + log.Errorf("error deleting index: %s", e) + } + } return "", fmt.Errorf("could not add pin name index: %v", err) } } - // Store the pin. Pin must be stored after index for recovery to work. - err = p.dstore.Put(pp.dsKey(), pinData) - if err != nil { - if mode == ipfspinner.Recursive { - p.cidRIndex.Delete(ctx, c.KeyString(), pp.Id) - } else { - p.cidDIndex.Delete(ctx, c.KeyString(), pp.Id) - } - if name != "" { - p.nameIndex.Delete(ctx, name, pp.Id) - } - return "", err - } - return pp.Id, nil } func (p *pinner) removePin(ctx context.Context, pp *pin) error { - p.setDirty(ctx, true) + p.setDirty(ctx) + var err error - // Remove pin from datastore. Pin must be removed before index for - // recovery to work. - err := p.dstore.Delete(pp.dsKey()) - if err != nil { - return err - } // Remove cid index from datastore if pp.Mode == ipfspinner.Recursive { err = p.cidRIndex.Delete(ctx, pp.Cid.KeyString(), pp.Id) @@ -311,6 +330,13 @@ func (p *pinner) removePin(ctx context.Context, pp *pin) error { } } + // The pin is removed last so that an incomplete remove is detected by a + // pin that has a missing index. + err = p.dstore.Delete(pp.dsKey()) + if err != nil { + return err + } + return nil } @@ -347,12 +373,15 @@ func (p *pinner) Unpin(ctx context.Context, c cid.Cid, recursive bool) error { } } - _, err = p.removePinsForCid(ctx, c, ipfspinner.Any) + removed, err := p.removePinsForCid(ctx, c, ipfspinner.Any) if err != nil { return err } + if !removed { + return nil + } - return nil + return p.flushPins(ctx, false) } // IsPinned returns whether or not the given key is pinned @@ -542,7 +571,17 @@ func (p *pinner) RemovePinWithMode(c cid.Cid, mode ipfspinner.Mode) { p.lock.Lock() defer p.lock.Unlock() - p.removePinsForCid(ctx, c, mode) + removed, err := p.removePinsForCid(ctx, c, mode) + if err != nil { + log.Error("cound not remove pins: %s", err) + return + } + if !removed { + return + } + if err = p.flushPins(ctx, false); err != nil { + log.Error("cound not remove pins: %s", err) + } } // removePinsForCid removes all pins for a cid that has the specified mode. @@ -583,17 +622,35 @@ func (p *pinner) removePinsForCid(ctx context.Context, c cid.Cid, mode ipfspinne pp, err = p.loadPin(ctx, pid) if err != nil { if err == ds.ErrNotFound { - p.setDirty(ctx, true) + p.setDirty(ctx) // Fix index; remove index for pin that does not exist switch mode { case ipfspinner.Recursive: - p.cidRIndex.DeleteKey(ctx, cidKey) + _, err = p.cidRIndex.DeleteKey(ctx, cidKey) + if err != nil { + return false, fmt.Errorf("error deleting index: %s", err) + } case ipfspinner.Direct: - p.cidDIndex.DeleteKey(ctx, cidKey) + _, err = p.cidDIndex.DeleteKey(ctx, cidKey) + if err != nil { + return false, fmt.Errorf("error deleting index: %s", err) + } case ipfspinner.Any: - p.cidRIndex.DeleteKey(ctx, cidKey) - p.cidDIndex.DeleteKey(ctx, cidKey) + _, err = p.cidRIndex.DeleteKey(ctx, cidKey) + if err != nil { + return false, fmt.Errorf("error deleting index: %s", err) + } + _, err = p.cidDIndex.DeleteKey(ctx, cidKey) + if err != nil { + return false, fmt.Errorf("error deleting index: %s", err) + } + } + if err = p.flushPins(ctx, true); err != nil { + return false, err } + // Mark this as removed since it removed an index, which is + // what prevents determines if an item is pinned. + removed = true log.Error("found CID index with missing pin") continue } @@ -619,95 +676,6 @@ func (p *pinner) loadPin(ctx context.Context, pid string) (*pin, error) { return decodePin(pid, pinData) } -// loadAllPins loads all pins from the datastore. -func (p *pinner) loadAllPins(ctx context.Context) ([]*pin, error) { - q := query.Query{ - Prefix: pinKeyPath, - } - results, err := p.dstore.Query(q) - if err != nil { - return nil, err - } - ents, err := results.Rest() - if err != nil { - return nil, err - } - if len(ents) == 0 { - return nil, nil - } - - pins := make([]*pin, len(ents)) - for i := range ents { - if ctx.Err() != nil { - return nil, ctx.Err() - } - var p *pin - p, err = decodePin(path.Base(ents[i].Key), ents[i].Value) - if err != nil { - return nil, err - } - pins[i] = p - } - return pins, nil -} - -// rebuildIndexes uses the stored pins to rebuild secondary indexes. This -// resolves any discrepancy between secondary indexes and pins that could -// result from a program termination between saving the two. -func (p *pinner) rebuildIndexes(ctx context.Context, pins []*pin) error { - // Build temporary in-memory CID index from pins - dstoreMem := ds.NewMapDatastore() - tmpCidDIndex := dsindex.New(dstoreMem, ds.NewKey(pinCidDIndexPath)) - tmpCidRIndex := dsindex.New(dstoreMem, ds.NewKey(pinCidRIndexPath)) - tmpNameIndex := dsindex.New(dstoreMem, ds.NewKey(pinNameIndexPath)) - var hasNames bool - for _, pp := range pins { - if ctx.Err() != nil { - return ctx.Err() - } - if pp.Mode == ipfspinner.Recursive { - tmpCidRIndex.Add(ctx, pp.Cid.KeyString(), pp.Id) - } else if pp.Mode == ipfspinner.Direct { - tmpCidDIndex.Add(ctx, pp.Cid.KeyString(), pp.Id) - } - if pp.Name != "" { - tmpNameIndex.Add(ctx, pp.Name, pp.Id) - hasNames = true - } - } - - // Sync the CID index to what was build from pins. This fixes any invalid - // indexes, which could happen if ipfs was terminated between writing pin - // and writing secondary index. - changed, err := dsindex.SyncIndex(ctx, tmpCidRIndex, p.cidRIndex) - if err != nil { - return fmt.Errorf("cannot sync indexes: %v", err) - } - if changed { - log.Info("invalid recursive indexes detected - rebuilt") - } - - changed, err = dsindex.SyncIndex(ctx, tmpCidDIndex, p.cidDIndex) - if err != nil { - return fmt.Errorf("cannot sync indexes: %v", err) - } - if changed { - log.Info("invalid direct indexes detected - rebuilt") - } - - if hasNames { - changed, err = dsindex.SyncIndex(ctx, tmpNameIndex, p.nameIndex) - if err != nil { - return fmt.Errorf("cannot sync name indexes: %v", err) - } - if changed { - log.Info("invalid name indexes detected - rebuilt") - } - } - - return p.Flush(ctx) -} - // DirectKeys returns a slice containing the directly pinned keys func (p *pinner) DirectKeys(ctx context.Context) ([]cid.Cid, error) { p.lock.RLock() @@ -810,37 +778,50 @@ func (p *pinner) Update(ctx context.Context, from, to cid.Cid, unpin bool) error return err } - if !unpin { - return nil - } - - _, err = p.removePinsForCid(ctx, from, ipfspinner.Recursive) - if err != nil { - return err + if unpin { + _, err = p.removePinsForCid(ctx, from, ipfspinner.Recursive) + if err != nil { + return err + } } - return nil + return p.flushPins(ctx, false) } -// Flush encodes and writes pinner keysets to the datastore -func (p *pinner) Flush(ctx context.Context) error { - p.lock.Lock() - defer p.lock.Unlock() - +func (p *pinner) flushDagService(ctx context.Context, force bool) error { + if !p.autoSync && !force { + return nil + } if syncDServ, ok := p.dserv.(syncDAGService); ok { if err := syncDServ.Sync(); err != nil { return fmt.Errorf("cannot sync pinned data: %v", err) } } + return nil +} - // Sync pins and indexes +func (p *pinner) flushPins(ctx context.Context, force bool) error { + if !p.autoSync && !force { + return nil + } if err := p.dstore.Sync(ds.NewKey(basePath)); err != nil { return fmt.Errorf("cannot sync pin state: %v", err) } + p.setClean(ctx) + return nil +} - p.setDirty(ctx, false) +// Flush encodes and writes pinner keysets to the datastore +func (p *pinner) Flush(ctx context.Context) error { + p.lock.Lock() + defer p.lock.Unlock() - return nil + err := p.flushDagService(ctx, true) + if err != nil { + return err + } + + return p.flushPins(ctx, true) } // PinWithMode allows the user to have fine grained control over pin @@ -869,6 +850,9 @@ func (p *pinner) PinWithMode(c cid.Cid, mode ipfspinner.Mode) { if err != nil { return } + if err = p.flushPins(ctx, false); err != nil { + log.Errorf("failed to create %s pin: %s", mode, err) + } } // hasChild recursively looks for a Cid among the children of a root Cid. @@ -914,26 +898,163 @@ func decodePin(pid string, data []byte) (*pin, error) { return p, nil } -// setDirty saves a boolean dirty flag in the datastore whenever there is a -// transition between a dirty (counter > 0) and non-dirty (counter == 0) state. -func (p *pinner) setDirty(ctx context.Context, dirty bool) { - isClean := p.dirty == p.clean - if dirty { - p.dirty++ - if !isClean { - return // do not save; was already dirty - } - } else if isClean { +// setDirty updates the dirty counter and saves a dirty state in the datastore +// if the state was previously clean +func (p *pinner) setDirty(ctx context.Context) { + wasClean := p.dirty == p.clean + p.dirty++ + + if !wasClean { + return // do not save; was already dirty + } + + data := []byte{1} + err := p.dstore.Put(dirtyKey, data) + if err != nil { + log.Errorf("failed to set pin dirty flag: %s", err) + return + } + err = p.dstore.Sync(dirtyKey) + if err != nil { + log.Errorf("failed to sync pin dirty flag: %s", err) + } +} + +// setClean saves a clean state value in the datastore if the state was +// previously dirty +func (p *pinner) setClean(ctx context.Context) { + if p.dirty == p.clean { return // already clean - } else { - p.clean = p.dirty // set clean } - // Do edge-triggered write to datastore data := []byte{0} - if dirty { - data[0] = 1 + err := p.dstore.Put(dirtyKey, data) + if err != nil { + log.Errorf("failed to set clear dirty flag: %s", err) + return } - p.dstore.Put(dirtyKey, data) - p.dstore.Sync(dirtyKey) + if err = p.dstore.Sync(dirtyKey); err != nil { + log.Errorf("failed to sync cleared pin dirty flag: %s", err) + return + } + p.clean = p.dirty // set clean +} + +// sync datastore after every 50 cid repairs +const syncRepairFrequency = 50 + +// rebuildIndexes uses the stored pins to rebuild secondary indexes. This +// resolves any discrepancy between secondary indexes and pins that could +// result from a program termination between saving the two. +func (p *pinner) rebuildIndexes(ctx context.Context) error { + // Load all pins from the datastore. + q := query.Query{ + Prefix: pinKeyPath, + } + results, err := p.dstore.Query(q) + if err != nil { + return err + } + defer results.Close() + + var checkedCount, repairedCount int + + // Iterate all pins and check if the corresponding recursive or direct + // index is missing. If the index is missing then create the index. + for r := range results.Next() { + if ctx.Err() != nil { + return ctx.Err() + } + if r.Error != nil { + return fmt.Errorf("cannot read index: %v", r.Error) + } + ent := r.Entry + pp, err := decodePin(path.Base(ent.Key), ent.Value) + if err != nil { + return err + } + + indexKey := pp.Cid.KeyString() + + var indexer, staleIndexer dsindex.Indexer + var idxrName, staleIdxrName string + if pp.Mode == ipfspinner.Recursive { + indexer = p.cidRIndex + staleIndexer = p.cidDIndex + idxrName = linkRecursive + staleIdxrName = linkDirect + } else if pp.Mode == ipfspinner.Direct { + indexer = p.cidDIndex + staleIndexer = p.cidRIndex + idxrName = linkDirect + staleIdxrName = linkRecursive + } else { + log.Error("unrecognized pin mode:", pp.Mode) + continue + } + + // Remove any stale index from unused indexer + ok, err := staleIndexer.HasValue(ctx, indexKey, pp.Id) + if err != nil { + return err + } + if ok { + // Delete any stale index + log.Errorf("deleting stale %s pin index for cid %v", staleIdxrName, pp.Cid.String()) + if err = staleIndexer.Delete(ctx, indexKey, pp.Id); err != nil { + return err + } + } + + // Check that the indexer indexes this pin + ok, err = indexer.HasValue(ctx, indexKey, pp.Id) + if err != nil { + return err + } + + var repaired bool + if !ok { + // Do not rebuild if index has an old value with leading slash + ok, err = indexer.HasValue(ctx, indexKey, "/"+pp.Id) + if err != nil { + return err + } + if !ok { + log.Errorf("repairing %s pin index for cid: %s", idxrName, pp.Cid.String()) + // There was no index found for this pin. This was either an + // incomplete add or and incomplete delete of a pin. Either + // way, restore the index to complete the add or to undo the + // incomplete delete. + if err = indexer.Add(ctx, indexKey, pp.Id); err != nil { + return err + } + repaired = true + } + } + // Check for missing name index + if pp.Name != "" { + ok, err = p.nameIndex.HasValue(ctx, pp.Name, pp.Id) + if err != nil { + return err + } + if !ok { + log.Errorf("repairing name pin index for cid: %s", pp.Cid.String()) + if err = p.nameIndex.Add(ctx, pp.Name, pp.Id); err != nil { + return err + } + } + repaired = true + } + + if repaired { + repairedCount++ + } + checkedCount++ + if checkedCount%syncRepairFrequency == 0 { + p.flushPins(ctx, true) + } + } + + log.Errorf("checked %d pins for invalid indexes, repaired %d pins", checkedCount, repairedCount) + return p.flushPins(ctx, true) } diff --git a/pinning/pinner/dspinner/pin_test.go b/pinning/pinner/dspinner/pin_test.go index d8c4e9549..ed2828658 100644 --- a/pinning/pinner/dspinner/pin_test.go +++ b/pinning/pinner/dspinner/pin_test.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "path" "testing" "time" @@ -13,6 +14,7 @@ import ( cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/query" dssync "github.com/ipfs/go-datastore/sync" lds "github.com/ipfs/go-ds-leveldb" blockstore "github.com/ipfs/go-ipfs-blockstore" @@ -162,11 +164,20 @@ func TestPinnerBasic(t *testing.T) { assertPinnedWithType(t, p, bk, ipfspin.Recursive, "Recursively pinned node not found") d, _ := randNode() - d.AddNodeLink("a", a) - d.AddNodeLink("c", c) + err = d.AddNodeLink("a", a) + if err != nil { + panic(err) + } + err = d.AddNodeLink("c", c) + if err != nil { + panic(err) + } e, _ := randNode() - d.AddNodeLink("e", e) + err = d.AddNodeLink("e", e) + if err != nil { + panic(err) + } // Must be in dagserv for unpin to work err = dserv.Add(ctx, e) @@ -281,15 +292,14 @@ func TestPinnerBasic(t *testing.T) { assertPinned(t, p, bk, "could not find recursively pinned node") // Remove the pin but not the index to simulate corruption - dsp := p.(*pinner) - ids, err := dsp.cidDIndex.Search(ctx, ak.KeyString()) + ids, err := p.cidDIndex.Search(ctx, ak.KeyString()) if err != nil { t.Fatal(err) } if len(ids) == 0 { t.Fatal("did not find pin for cid", ak.String()) } - pp, err := dsp.loadPin(ctx, ids[0]) + pp, err := p.loadPin(ctx, ids[0]) if err != nil { t.Fatal(err) } @@ -299,7 +309,7 @@ func TestPinnerBasic(t *testing.T) { if pp.Cid != ak { t.Error("loaded pin has wrong cid") } - err = dsp.dstore.Delete(pp.dsKey()) + err = p.dstore.Delete(pp.dsKey()) if err != nil { t.Fatal(err) } @@ -331,15 +341,16 @@ func TestAddLoadPin(t *testing.T) { dserv := mdag.NewDAGService(bserv) - ipfsPin, err := New(ctx, dstore, dserv) + p, err := New(ctx, dstore, dserv) if err != nil { t.Fatal(err) } - p := ipfsPin.(*pinner) - a, ak := randNode() - dserv.Add(ctx, a) + err = dserv.Add(ctx, a) + if err != nil { + panic(err) + } mode := ipfspin.Recursive name := "my-pin" @@ -380,11 +391,17 @@ func TestRemovePinWithMode(t *testing.T) { } a, ak := randNode() - dserv.Add(ctx, a) + err = dserv.Add(ctx, a) + if err != nil { + panic(err) + } - p.Pin(ctx, a, false) + err = p.Pin(ctx, a, false) + if err != nil { + t.Fatal(err) + } - ok, err := p.(*pinner).removePinsForCid(ctx, ak, ipfspin.Recursive) + ok, err := p.removePinsForCid(ctx, ak, ipfspin.Recursive) if err != nil { t.Fatal(err) } @@ -642,6 +659,18 @@ func TestLoadDirty(t *testing.T) { if err != nil { t.Fatal(err) } + prev := p.SetAutosync(false) + if !prev { + t.Fatal("expected previous autosync to be true") + } + prev = p.SetAutosync(false) + if prev { + t.Fatal("expected previous autosync to be false") + } + prev = p.SetAutosync(true) + if prev { + t.Fatal("expected previous autosync to be false") + } a, ak := randNode() err = dserv.Add(ctx, a) @@ -660,9 +689,18 @@ func TestLoadDirty(t *testing.T) { cidBKey := bk.KeyString() // Corrupt index - cidRIndex := p.(*pinner).cidRIndex - cidRIndex.DeleteKey(ctx, cidAKey) - cidRIndex.Add(ctx, cidBKey, "not-a-pin-id") + cidRIndex := p.cidRIndex + _, err = cidRIndex.DeleteKey(ctx, cidAKey) + if err != nil { + t.Fatal(err) + } + err = cidRIndex.Add(ctx, cidBKey, "not-a-pin-id") + if err != nil { + t.Fatal(err) + } + + // Force dirty, since Pin syncs automatically + p.setDirty(ctx) // Verify dirty data, err := dstore.Get(dirtyKey) @@ -681,7 +719,8 @@ func TestLoadDirty(t *testing.T) { t.Fatal("index should be deleted") } - // Create new pinner on same datastore that was never flushed. + // Create new pinner on same datastore that was never flushed. This should + // detect the dirty flag and repair the indexes. p, err = New(ctx, dstore, dserv) if err != nil { t.Fatal(err) @@ -697,7 +736,7 @@ func TestLoadDirty(t *testing.T) { } // Verify index rebuilt - cidRIndex = p.(*pinner).cidRIndex + cidRIndex = p.cidRIndex has, err = cidRIndex.HasAny(ctx, cidAKey) if err != nil { t.Fatal(err) @@ -706,12 +745,12 @@ func TestLoadDirty(t *testing.T) { t.Fatal("index should have been rebuilt") } - has, err = cidRIndex.HasAny(ctx, cidBKey) + has, err = p.removePinsForCid(ctx, bk, ipfspin.Any) if err != nil { t.Fatal(err) } - if has { - t.Fatal("index should have been removed by rebuild") + if !has { + t.Fatal("expected Unpin to return true since index removed") } } @@ -903,7 +942,7 @@ func makeStore() (ds.Datastore, ipld.DAGService) { // BenchmarkLoadRebuild loads a pinner that has some number of saved pins, and // compares the load time when rebuilding indexes to loading without rebuilding // indexes. -func BenchmarkLoadRebuild(b *testing.B) { +func BenchmarkLoad(b *testing.B) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -918,7 +957,10 @@ func BenchmarkLoadRebuild(b *testing.B) { b.Run("RebuildTrue", func(b *testing.B) { for i := 0; i < b.N; i++ { - dstore.Put(dirtyKey, []byte{1}) + err = dstore.Put(dirtyKey, []byte{1}) + if err != nil { + panic(err.Error()) + } _, err = New(ctx, dstore, dserv) if err != nil { @@ -929,7 +971,10 @@ func BenchmarkLoadRebuild(b *testing.B) { b.Run("RebuildFalse", func(b *testing.B) { for i := 0; i < b.N; i++ { - dstore.Put(dirtyKey, []byte{0}) + err = dstore.Put(dirtyKey, []byte{0}) + if err != nil { + panic(err.Error()) + } _, err = New(ctx, dstore, dserv) if err != nil { @@ -1097,3 +1142,244 @@ func benchmarkPinAll(b *testing.B, count int, pinner ipfspin.Pinner, dserv ipld. b.StartTimer() } } + +func BenchmarkRebuild(b *testing.B) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dstore, dserv := makeStore() + pinIncr := 32768 + + for pins := pinIncr; pins <= pinIncr*5; pins += pinIncr { + pinner, err := New(ctx, dstore, dserv) + if err != nil { + panic(err.Error()) + } + nodes := makeNodes(pinIncr, dserv) + pinNodes(nodes, pinner, true) + + b.Run(fmt.Sprintf("Rebuild %d", pins), func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + err = dstore.Put(dirtyKey, []byte{1}) + if err != nil { + panic(err.Error()) + } + + _, err = New(ctx, dstore, dserv) + if err != nil { + panic(err.Error()) + } + } + }) + } +} + +func TestCidIndex(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dstore, dserv := makeStore() + pinner, err := New(ctx, dstore, dserv) + if err != nil { + t.Fatal(err) + } + nodes := makeNodes(1, dserv) + node := nodes[0] + + c := node.Cid() + cidKey := c.KeyString() + + // Pin the cid + pid, err := pinner.addPin(ctx, c, ipfspin.Recursive, "") + if err != nil { + t.Fatal(err) + } + + t.Log("Added pin:", pid) + t.Log("CID index:", c.String(), "-->", pid) + + // Check that the index exists + ok, err := pinner.cidRIndex.HasAny(ctx, cidKey) + if err != nil { + t.Fatal(err) + } + if !ok { + t.Fatal("R-index has no value for", cidKey) + } + + // Check that searching for the cid returns a value + values, err := pinner.cidRIndex.Search(ctx, cidKey) + if err != nil { + t.Fatal(err) + } + if len(values) != 1 { + t.Fatal("expect index to return one value") + } + if values[0] != pid { + t.Fatal("indexer should have has value", cidKey, "-->", pid) + } + + // Check that index has specific value + ok, err = pinner.cidRIndex.HasValue(ctx, cidKey, pid) + if err != nil { + t.Fatal(err) + } + if !ok { + t.Fatal("indexer should have has value", cidKey, "-->", pid) + } + + // Iterate values of index + var seen bool + err = pinner.cidRIndex.ForEach(ctx, "", func(key, value string) bool { + if seen { + t.Fatal("expected one key-value pair") + } + if key != cidKey { + t.Fatal("unexpected key:", key) + } + if value != pid { + t.Fatal("unexpected value:", value) + } + seen = true + return true + }) + if err != nil { + t.Fatal(err) + } + + // Load all pins from the datastore. + q := query.Query{ + Prefix: pinKeyPath, + } + results, err := pinner.dstore.Query(q) + if err != nil { + t.Fatal(err) + } + defer results.Close() + + // Iterate all pins and check if the corresponding recursive or direct + // index is missing. If the index is missing then create the index. + seen = false + for r := range results.Next() { + if seen { + t.Fatal("has more than one pin") + } + if r.Error != nil { + t.Fatal(fmt.Errorf("cannot read index: %v", r.Error)) + } + ent := r.Entry + pp, err := decodePin(path.Base(ent.Key), ent.Value) + if err != nil { + t.Fatal(err) + } + t.Log("Found pin:", pp.Id) + if pp.Id != pid { + t.Fatal("ID of loaded pin is not the same known to indexer") + } + seen = true + } +} + +func TestRebuild(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dstore, dserv := makeStore() + pinner, err := New(ctx, dstore, dserv) + if err != nil { + t.Fatal(err) + } + nodes := makeNodes(3, dserv) + pinNodes(nodes, pinner, true) + + c1 := nodes[0].Cid() + cid1Key := c1.KeyString() + c2 := nodes[1].Cid() + cid2Key := c2.KeyString() + c3 := nodes[2].Cid() + cid3Key := c3.KeyString() + + // Get pin IDs + values, err := pinner.cidRIndex.Search(ctx, cid1Key) + if err != nil { + t.Fatal(err) + } + pid1 := values[0] + values, err = pinner.cidRIndex.Search(ctx, cid2Key) + if err != nil { + t.Fatal(err) + } + pid2 := values[0] + values, err = pinner.cidRIndex.Search(ctx, cid3Key) + if err != nil { + t.Fatal(err) + } + pid3 := values[0] + + // Corrupt by adding direct index when there is already a recursive index + err = pinner.cidDIndex.Add(ctx, cid1Key, pid1) + if err != nil { + t.Fatal(err) + } + + // Corrupt index by deleting cid index 2 to simulate an incomplete add or delete + _, err = pinner.cidRIndex.DeleteKey(ctx, cid2Key) + if err != nil { + t.Fatal(err) + } + + // Corrupt index by deleting pin to simulate corruption + var pp *pin + pp, err = pinner.loadPin(ctx, pid3) + if err != nil { + t.Fatal(err) + } + err = pinner.dstore.Delete(pp.dsKey()) + if err != nil { + t.Fatal(err) + } + + pinner.setDirty(ctx) + + // Rebuild indexes + pinner, err = New(ctx, dstore, dserv) + if err != nil { + t.Fatal(err) + } + + // Verify that indexes have same values as before + err = verifyIndexValue(ctx, pinner, cid1Key, pid1) + if err != nil { + t.Fatal(err) + } + err = verifyIndexValue(ctx, pinner, cid2Key, pid2) + if err != nil { + t.Fatal(err) + } + err = verifyIndexValue(ctx, pinner, cid3Key, pid3) + if err != nil { + t.Fatal(err) + } +} + +func verifyIndexValue(ctx context.Context, pinner *pinner, cidKey, expectedPid string) error { + values, err := pinner.cidRIndex.Search(ctx, cidKey) + if err != nil { + return err + } + if len(values) != 1 { + return errors.New("expected 1 value") + } + if expectedPid != values[0] { + return errors.New("index has wrong value") + } + ok, err := pinner.cidDIndex.HasAny(ctx, cidKey) + if err != nil { + return err + } + if ok { + return errors.New("should not have a direct index") + } + return nil +} diff --git a/pinning/pinner/dspinner/sync_test.go b/pinning/pinner/dspinner/sync_test.go new file mode 100644 index 000000000..0f3c81ad4 --- /dev/null +++ b/pinning/pinner/dspinner/sync_test.go @@ -0,0 +1,88 @@ +package dspinner + +import ( + "context" + "os" + "testing" + + bs "github.com/ipfs/go-blockservice" + ds "github.com/ipfs/go-datastore" + bds "github.com/ipfs/go-ds-badger" + lds "github.com/ipfs/go-ds-leveldb" + blockstore "github.com/ipfs/go-ipfs-blockstore" + offline "github.com/ipfs/go-ipfs-exchange-offline" + ipld "github.com/ipfs/go-ipld-format" + mdag "github.com/ipfs/go-merkledag" +) + +func makeStoreLevelDB(dir string) (ds.Datastore, ipld.DAGService) { + ldstore, err := lds.NewDatastore(dir, nil) + if err != nil { + panic(err) + } + // dstore := &batchWrap{ldstore} + dstore := ldstore + bstore := blockstore.NewBlockstore(dstore) + bserv := bs.New(bstore, offline.Exchange(bstore)) + dserv := mdag.NewDAGService(bserv) + return dstore, dserv +} + +func makeStoreBadger(dir string) (ds.Datastore, ipld.DAGService) { + bdstore, err := bds.NewDatastore(dir, nil) + if err != nil { + panic(err) + } + dstore := &batchWrap{bdstore} + bstore := blockstore.NewBlockstore(dstore) + bserv := bs.New(bstore, offline.Exchange(bstore)) + dserv := mdag.NewDAGService(bserv) + return dstore, dserv +} + +func benchAutoSync(b *testing.B, N int, auto bool, dstore ds.Datastore, dserv ipld.DAGService) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + pinner, err := New(ctx, dstore, dserv) + if err != nil { + panic(err.Error()) + } + + nodes := makeNodes(N, dserv) + + pinner.SetAutosync(auto) + pinNodes(nodes, pinner, true) +} + +func BenchmarkSyncOnceBadger(b *testing.B) { + const dsDir = "b-once" + dstoreB1, dservB1 := makeStoreBadger(dsDir) + defer os.RemoveAll(dsDir) + benchAutoSync(b, b.N, false, dstoreB1, dservB1) + dstoreB1.Close() +} + +func BenchmarkSyncEveryBadger(b *testing.B) { + const dsDir = "b-every" + dstoreB2, dservB2 := makeStoreBadger(dsDir) + defer os.RemoveAll(dsDir) + benchAutoSync(b, b.N, true, dstoreB2, dservB2) + dstoreB2.Close() +} + +func BenchmarkSyncOnceLevelDB(b *testing.B) { + const dsDir = "l-once" + dstoreL1, dservL1 := makeStoreLevelDB(dsDir) + defer os.RemoveAll(dsDir) + benchAutoSync(b, b.N, false, dstoreL1, dservL1) + dstoreL1.Close() +} + +func BenchmarkSyncEveryLevelDB(b *testing.B) { + const dsDir = "l-every" + dstoreL2, dservL2 := makeStoreLevelDB(dsDir) + defer os.RemoveAll(dsDir) + benchAutoSync(b, b.N, true, dstoreL2, dservL2) + dstoreL2.Close() +} From e544633fbf2adf93d91f3b184f5ffee89c3c5f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Tue, 27 Jul 2021 13:36:43 +0100 Subject: [PATCH 3513/3817] add the first read-only benchmarks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BenchmarkReadBlocks uses carv2.OpenReader with its Roots and Next method. From six runs on a laptop with a i5-8350U and benchstat: name time/op ReadBlocks-8 633µs ± 2% name speed ReadBlocks-8 824MB/s ± 2% name alloc/op ReadBlocks-8 1.32MB ± 0% name allocs/op ReadBlocks-8 13.5k ± 0% OpenReadOnlyV1 uses blockstore.OpenReadOnly with its Get method. The method is used on all blocks in a shuffled order, to ensure that the index is working as intended. The input file lacks an index, so we also generate that. name time/op OpenReadOnlyV1-8 899µs ± 1% name speed OpenReadOnlyV1-8 534MB/s ± 1% name alloc/op OpenReadOnlyV1-8 1.52MB ± 0% name allocs/op OpenReadOnlyV1-8 27.2k ± 0% Both benchmarks use the "sample" v1/v2 CAR files, which weigh about half a megabyte. It seems like a good size; a significantly larger CAR file would make each benchmark iteration take tens or hundreds of milliseconds, making it much slower to obtain many benchmark results, since we want at least thousands of iterations to avoid noise. This commit was moved from ipld/go-car@311809b079f4e839bfa815fb6986f713841644e0 --- ipld/car/v2/bench_test.go | 48 +++++++++++++++++++ ipld/car/v2/blockstore/bench_test.go | 71 ++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 ipld/car/v2/bench_test.go create mode 100644 ipld/car/v2/blockstore/bench_test.go diff --git a/ipld/car/v2/bench_test.go b/ipld/car/v2/bench_test.go new file mode 100644 index 000000000..c1fe5b624 --- /dev/null +++ b/ipld/car/v2/bench_test.go @@ -0,0 +1,48 @@ +package car_test + +import ( + "io" + "os" + "testing" + + carv2 "github.com/ipld/go-car/v2" +) + +// Open a reader, get the roots, and iterate over all blocks. +// Essentially looking at the contents of any CARv1 or CARv2 file. +// Note that this also uses ReadVersion underneath. + +func BenchmarkReadBlocks(b *testing.B) { + path := "testdata/sample-wrapped-v2.car" + + info, err := os.Stat(path) + if err != nil { + b.Fatal(err) + } + b.SetBytes(info.Size()) + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + cr, err := carv2.OpenReader(path) + if err != nil { + b.Fatal(err) + } + _, err = cr.Roots() + if err != nil { + b.Fatal(err) + } + for { + _, err := cr.Next() + if err == io.EOF { + break + } + if err != nil { + b.Fatal(err) + } + } + + cr.Close() + } + }) +} diff --git a/ipld/car/v2/blockstore/bench_test.go b/ipld/car/v2/blockstore/bench_test.go new file mode 100644 index 000000000..c97dc3526 --- /dev/null +++ b/ipld/car/v2/blockstore/bench_test.go @@ -0,0 +1,71 @@ +package blockstore_test + +import ( + "io" + mathrand "math/rand" + "os" + "testing" + + "github.com/ipfs/go-cid" + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/blockstore" +) + +// Open a read-only blockstore, +// and retrieve all blocks in a shuffled order. +// Note that this benchmark includes generating an index, +// since the input file is a CARv1. + +func BenchmarkOpenReadOnlyV1(b *testing.B) { + path := "../testdata/sample-v1.car" + + info, err := os.Stat(path) + if err != nil { + b.Fatal(err) + } + b.SetBytes(info.Size()) + b.ReportAllocs() + + var shuffledCIDs []cid.Cid + cr, err := carv2.OpenReader(path) + if err != nil { + b.Fatal(err) + } + for { + block, err := cr.Next() + if err == io.EOF { + break + } + if err != nil { + b.Fatal(err) + } + shuffledCIDs = append(shuffledCIDs, block.Cid()) + } + cr.Close() + + // The shuffling needs to be deterministic, + // for the sake of stable benchmark results. + // Any source number works as long as it's fixed. + rnd := mathrand.New(mathrand.NewSource(123456)) + rnd.Shuffle(len(shuffledCIDs), func(i, j int) { + shuffledCIDs[i], shuffledCIDs[j] = shuffledCIDs[j], shuffledCIDs[i] + }) + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + bs, err := blockstore.OpenReadOnly(path) + if err != nil { + b.Fatal(err) + } + + for _, c := range shuffledCIDs { + _, err := bs.Get(c) + if err != nil { + b.Fatal(err) + } + } + + bs.Close() + } + }) +} From b1c325bd7233d50504a6300b92e27103f63ef3aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Mon, 2 Aug 2021 12:19:18 +0100 Subject: [PATCH 3514/3817] blockstore: use errors when API contracts are broken It's technically correct to use panics in these situations, as the downstream user is clearly breaking the contract documented in the API's godoc. However, all these methods return errors already, so panicking is not our only option here. There's another reason that makes a panic unfortunate. ReadWrite.Finalize enables the panic behavior on other methods, which makes it much easier to run into in production even when the code and its tests work normally. Finally, there's some precedent for IO interfaces using errors rather than panics when they are used after closing. For example, os.File.Read returns an error if the file is closed. Note that this change doesn't make panics entirely impossible. The blockstore could still run into exceptional and unexpected panics, such as those caused by memory corruption or internal bugs. This commit was moved from ipld/go-car@a6dc547f3808b4acf5ae2345c1f08c21d38cbc86 --- ipld/car/v2/blockstore/readonly.go | 11 +++++--- ipld/car/v2/blockstore/readonly_test.go | 6 ++-- ipld/car/v2/blockstore/readwrite.go | 36 +++++++++++++----------- ipld/car/v2/blockstore/readwrite_test.go | 26 ++++++++++------- 4 files changed, 46 insertions(+), 33 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index dbf7bf61b..2df9a7946 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -24,7 +24,10 @@ import ( var _ blockstore.Blockstore = (*ReadOnly)(nil) -var errZeroLengthSection = fmt.Errorf("zero-length section not allowed by default; see WithZeroLengthSectionAsEOF option") +var ( + errZeroLengthSection = fmt.Errorf("zero-length carv2 section not allowed by default; see WithZeroLengthSectionAsEOF option") + errReadOnly = fmt.Errorf("called write method on a read-only carv2 blockstore") +) type ( // ReadOnly provides a read-only CAR Block Store. @@ -176,7 +179,7 @@ func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) { // DeleteBlock is unsupported and always panics. func (b *ReadOnly) DeleteBlock(_ cid.Cid) error { - panic("called write method on a read-only blockstore") + return errReadOnly } // Has indicates if the store contains a block that corresponds to the given key. @@ -303,12 +306,12 @@ func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { // Put is not supported and always returns an error. func (b *ReadOnly) Put(blocks.Block) error { - panic("called write method on a read-only blockstore") + return errReadOnly } // PutMany is not supported and always returns an error. func (b *ReadOnly) PutMany([]blocks.Block) error { - panic("called write method on a read-only blockstore") + return errReadOnly } // WithAsyncErrorHandler returns a context with async error handling set to the given errHandler. diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index ecc30e1d1..3fd16c8c6 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -102,9 +102,9 @@ func TestReadOnly(t *testing.T) { require.Equal(t, wantBlock, gotBlock) // Assert write operations panic - require.Panics(t, func() { subject.Put(wantBlock) }) - require.Panics(t, func() { subject.PutMany([]blocks.Block{wantBlock}) }) - require.Panics(t, func() { subject.DeleteBlock(key) }) + require.Error(t, subject.Put(wantBlock)) + require.Error(t, subject.PutMany([]blocks.Block{wantBlock})) + require.Error(t, subject.DeleteBlock(key)) } ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index ba87ee601..1192e749c 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -23,6 +23,8 @@ import ( var _ blockstore.Blockstore = (*ReadWrite)(nil) +var errFinalized = fmt.Errorf("cannot use a read-write carv2 blockstore after finalizing") + // ReadWrite implements a blockstore that stores blocks in CARv2 format. // Blocks put into the blockstore can be read back once they are successfully written. // This implementation is preferable for a write-heavy workload. @@ -284,22 +286,22 @@ func (b *ReadWrite) unfinalize() error { return err } -func (b *ReadWrite) panicIfFinalized() { - if b.header.DataSize != 0 { - panic("must not use a read-write blockstore after finalizing") - } +func (b *ReadWrite) finalized() bool { + return b.header.DataSize != 0 } // Put puts a given block to the underlying datastore func (b *ReadWrite) Put(blk blocks.Block) error { - // PutMany already calls panicIfFinalized. + // PutMany already checks b.finalized. return b.PutMany([]blocks.Block{blk}) } // PutMany puts a slice of blocks at the same time using batching // capabilities of the underlying datastore whenever possible. func (b *ReadWrite) PutMany(blks []blocks.Block) error { - b.panicIfFinalized() + if b.finalized() { + return errFinalized + } b.mu.Lock() defer b.mu.Unlock() @@ -362,31 +364,33 @@ func (b *ReadWrite) Finalize() error { } func (b *ReadWrite) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { - b.panicIfFinalized() + if b.finalized() { + return nil, errFinalized + } return b.ReadOnly.AllKeysChan(ctx) } func (b *ReadWrite) Has(key cid.Cid) (bool, error) { - b.panicIfFinalized() + if b.finalized() { + return false, errFinalized + } return b.ReadOnly.Has(key) } func (b *ReadWrite) Get(key cid.Cid) (blocks.Block, error) { - b.panicIfFinalized() + if b.finalized() { + return nil, errFinalized + } return b.ReadOnly.Get(key) } func (b *ReadWrite) GetSize(key cid.Cid) (int, error) { - b.panicIfFinalized() + if b.finalized() { + return 0, errFinalized + } return b.ReadOnly.GetSize(key) } - -func (b *ReadWrite) HashOnRead(h bool) { - b.panicIfFinalized() - - b.ReadOnly.HashOnRead(h) -} diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index f3016e25d..77442de0d 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -494,18 +494,24 @@ func TestReadWritePanicsOnlyWhenFinalized(t *testing.T) { require.True(t, has) subject.HashOnRead(true) - // Delete should always panic regardless of finalize - require.Panics(t, func() { subject.DeleteBlock(oneTestBlockCid) }) + // Delete should always error regardless of finalize + require.Error(t, subject.DeleteBlock(oneTestBlockCid)) require.NoError(t, subject.Finalize()) - require.Panics(t, func() { subject.Get(oneTestBlockCid) }) - require.Panics(t, func() { subject.GetSize(anotherTestBlockCid) }) - require.Panics(t, func() { subject.Has(anotherTestBlockCid) }) - require.Panics(t, func() { subject.HashOnRead(true) }) - require.Panics(t, func() { subject.Put(oneTestBlockWithCidV1) }) - require.Panics(t, func() { subject.PutMany([]blocks.Block{anotherTestBlockWithCidV0}) }) - require.Panics(t, func() { subject.AllKeysChan(context.Background()) }) - require.Panics(t, func() { subject.DeleteBlock(oneTestBlockCid) }) + require.Error(t, subject.Finalize()) + + _, err = subject.Get(oneTestBlockCid) + require.Error(t, err) + _, err = subject.GetSize(anotherTestBlockCid) + require.Error(t, err) + _, err = subject.Has(anotherTestBlockCid) + require.Error(t, err) + + require.Error(t, subject.Put(oneTestBlockWithCidV1)) + require.Error(t, subject.PutMany([]blocks.Block{anotherTestBlockWithCidV0})) + _, err = subject.AllKeysChan(context.Background()) + require.Error(t, err) + require.Error(t, subject.DeleteBlock(oneTestBlockCid)) } func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) { From 999b9e081d04a3af1c3427c97b3b7ef08d0bb56a Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Mon, 2 Aug 2021 14:42:09 -0700 Subject: [PATCH 3515/3817] add constructor that doesnt mess with datastore keys This commit was moved from ipfs/go-ipfs-blockstore@c56038684c45ce32184d5c1441d7e8b1d6bfa512 --- blockstore/blockstore.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 6625a3411..0f9686683 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -121,6 +121,16 @@ func NewBlockstore(d ds.Batching) Blockstore { } } +// NewBlockstoreNoPrefix returns a default Blockstore implementation +// using the provided datastore.Batching backend. +// This constructor does not modify input keys in any way +func NewBlockstoreNoPrefix(d ds.Batching) Blockstore { + return &blockstore{ + datastore: d, + rehash: uatomic.NewBool(false), + } +} + type blockstore struct { datastore ds.Batching From dc7d0f816f72c3f07ad4e76f19f63dcdb6d214fc Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 3 Aug 2021 12:48:08 +0100 Subject: [PATCH 3516/3817] Implement version agnostic streaming CAR block iterator Implement an iterator over CAR blocks that accepts both v1 and v2. The reader only requires io.Reader interface to operate and exposes the version and roots of the underlaying CAR file as fields. Remove `Next` from CARv2 reader now that the iteration functionality is provided using `BlockReader`. Update the benchmarks to reflect the changes and add example usage for `NewBlockReader`. Add tests that assert `BlockReader` behaviour in erroneous and successful cases for both underlying CARv1 and CARv2 payload. This commit was moved from ipld/go-car@d0e44a62f0a3b31f0c6280653381fcdff19f65e5 --- ipld/car/v2/bench_test.go | 17 ++-- ipld/car/v2/block_reader.go | 142 +++++++++++++++++++++++++++ ipld/car/v2/block_reader_test.go | 112 +++++++++++++++++++++ ipld/car/v2/blockstore/bench_test.go | 24 +++-- ipld/car/v2/example_test.go | 59 +++++++++++ ipld/car/v2/reader.go | 28 +----- ipld/car/v2/reader_test.go | 20 ---- 7 files changed, 339 insertions(+), 63 deletions(-) create mode 100644 ipld/car/v2/block_reader.go create mode 100644 ipld/car/v2/block_reader_test.go diff --git a/ipld/car/v2/bench_test.go b/ipld/car/v2/bench_test.go index c1fe5b624..83adc346a 100644 --- a/ipld/car/v2/bench_test.go +++ b/ipld/car/v2/bench_test.go @@ -8,10 +8,9 @@ import ( carv2 "github.com/ipld/go-car/v2" ) -// Open a reader, get the roots, and iterate over all blocks. -// Essentially looking at the contents of any CARv1 or CARv2 file. -// Note that this also uses ReadVersion underneath. - +// BenchmarkReadBlocks instantiates a BlockReader, and iterates over all blocks. +// It essentially looks at the contents of any CARv1 or CARv2 file. +// Note that this also uses internal carv1.ReadHeader underneath. func BenchmarkReadBlocks(b *testing.B) { path := "testdata/sample-wrapped-v2.car" @@ -24,16 +23,16 @@ func BenchmarkReadBlocks(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { - cr, err := carv2.OpenReader(path) + r, err := os.Open("testdata/sample-wrapped-v2.car") if err != nil { b.Fatal(err) } - _, err = cr.Roots() + br, err := carv2.NewBlockReader(r) if err != nil { b.Fatal(err) } for { - _, err := cr.Next() + _, err := br.Next() if err == io.EOF { break } @@ -42,7 +41,9 @@ func BenchmarkReadBlocks(b *testing.B) { } } - cr.Close() + if err := r.Close(); err != nil { + b.Fatal(err) + } } }) } diff --git a/ipld/car/v2/block_reader.go b/ipld/car/v2/block_reader.go new file mode 100644 index 000000000..5c3350294 --- /dev/null +++ b/ipld/car/v2/block_reader.go @@ -0,0 +1,142 @@ +package car + +import ( + "fmt" + "io" + + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + "github.com/ipld/go-car/v2/internal/carv1" + "github.com/ipld/go-car/v2/internal/carv1/util" + internalio "github.com/ipld/go-car/v2/internal/io" +) + +// BlockReader facilitates iteration over CAR blocks for both CARv1 and CARv2. +// See NewBlockReader +type BlockReader struct { + // The detected version of the CAR payload. + Version uint64 + // The roots of the CAR payload. May be empty. + Roots []cid.Cid + + // Used internally only, by BlockReader.Next during iteration over blocks. + r io.Reader + ropts ReadOptions +} + +// NewBlockReader instantiates a new BlockReader facilitating iteration over blocks in CARv1 or +// CARv2 payload. Upon instantiation, the version is automatically detected and exposed via +// BlockReader.Version. The root CIDs of the CAR payload are exposed via BlockReader.Roots +// +// See BlockReader.Next +func NewBlockReader(r io.Reader, opts ...ReadOption) (*BlockReader, error) { + // Read CARv1 header or CARv2 pragma. + // Both are a valid CARv1 header, therefore are read as such. + pragmaOrV1Header, err := carv1.ReadHeader(r) + if err != nil { + return nil, err + } + + // Populate the block reader version. + br := &BlockReader{ + Version: pragmaOrV1Header.Version, + } + + // Populate read options + for _, o := range opts { + o(&br.ropts) + } + + // Expect either version 1 or 2. + switch br.Version { + case 1: + // If version is 1, r represents a CARv1. + // Simply populate br.Roots and br.r without modifying r. + br.Roots = pragmaOrV1Header.Roots + br.r = r + case 2: + // If the version is 2: + // 1. Read CARv2 specific header to locate the inner CARv1 data payload offset and size. + // 2. Skip to the beginning of the inner CARv1 data payload. + // 3. Re-initialize r as a LimitReader, limited to the size of the inner CARv1 payload. + // 4. Read the header of inner CARv1 data payload via r to populate br.Roots. + + // Read CARv2-specific header. + v2h := Header{} + if _, err := v2h.ReadFrom(r); err != nil { + return nil, err + } + // Assert the data payload offset validity. + // It must be at least 51 ( + ). + dataOffset := int64(v2h.DataOffset) + if dataOffset < PragmaSize+HeaderSize { + return nil, fmt.Errorf("invalid data payload offset: %v", dataOffset) + } + // Assert the data size validity. + // It must be larger than zero. + // Technically, it should be at least 11 bytes (i.e. a valid CARv1 header with no roots) but + // we let further parsing of the header to signal invalid data payload header. + dataSize := int64(v2h.DataSize) + if dataSize <= 0 { + return nil, fmt.Errorf("invalid data payload size: %v", dataSize) + } + + // Skip to the beginning of inner CARv1 data payload. + // Note, at this point the pragma and CARv1 header have been read. + // An io.ReadSeeker is opportunistically constructed from the given io.Reader r. + // The constructor does not take an initial offset, so we use Seek in io.SeekCurrent to + // fast forward to the beginning of data payload by subtracting pragma and header size from + // dataOffset. + rs := internalio.ToByteReadSeeker(r) + if _, err := rs.Seek(dataOffset-PragmaSize-HeaderSize, io.SeekCurrent); err != nil { + return nil, err + } + + // Set br.r to a LimitReader reading from r limited to dataSize. + br.r = io.LimitReader(r, dataSize) + + // Populate br.Roots by reading the inner CARv1 data payload header. + header, err := carv1.ReadHeader(br.r) + if err != nil { + return nil, err + } + // Assert that the data payload header is exactly 1, i.e. the header represents a CARv1. + if header.Version != 1 { + return nil, fmt.Errorf("invalid data payload header version; expected 1, got %v", header.Version) + } + br.Roots = header.Roots + default: + // Otherwise, error out with invalid version since only versions 1 or 2 are expected. + return nil, fmt.Errorf("invalid car version: %d", br.Version) + } + return br, nil +} + +// Next iterates over blocks in the underlying CAR payload with an io.EOF error indicating the end +// is reached. Note, this function is forward-only; once the end has been reached it will always +// return io.EOF. +// +// When the payload represents a CARv1 the BlockReader.Next simply iterates over blocks until it +// reaches the end of the underlying io.Reader stream. +// +// As for CARv2 payload, the underlying io.Reader is read only up to the end of the last block. +// Note, in a case where ReadOption.ZeroLengthSectionAsEOF is enabled, io.EOF is returned +// immediately upon encountering a zero-length section without reading any further bytes from the +// underlying io.Reader. +func (br *BlockReader) Next() (blocks.Block, error) { + c, data, err := util.ReadNode(br.r, br.ropts.ZeroLengthSectionAsEOF) + if err != nil { + return nil, err + } + + hashed, err := c.Prefix().Sum(data) + if err != nil { + return nil, err + } + + if !hashed.Equals(c) { + return nil, fmt.Errorf("mismatch in content integrity, name: %s, data: %s", c, hashed) + } + + return blocks.NewBlockWithCid(data, c) +} diff --git a/ipld/car/v2/block_reader_test.go b/ipld/car/v2/block_reader_test.go new file mode 100644 index 000000000..afffc806c --- /dev/null +++ b/ipld/car/v2/block_reader_test.go @@ -0,0 +1,112 @@ +package car_test + +import ( + "io" + "os" + "testing" + + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/internal/carv1" + "github.com/stretchr/testify/require" +) + +func TestBlockReaderFailsOnUnknownVersion(t *testing.T) { + r := requireReaderFromPath(t, "testdata/sample-rootless-v42.car") + _, err := carv2.NewBlockReader(r) + require.EqualError(t, err, "invalid car version: 42") +} + +func TestBlockReaderFailsOnCorruptPragma(t *testing.T) { + r := requireReaderFromPath(t, "testdata/sample-corrupt-pragma.car") + _, err := carv2.NewBlockReader(r) + require.EqualError(t, err, "unexpected EOF") +} + +func TestBlockReader_WithCarV1Consistency(t *testing.T) { + tests := []struct { + name string + path string + zerLenAsEOF bool + wantVersion uint64 + }{ + { + name: "CarV1WithoutZeroLengthSection", + path: "testdata/sample-v1.car", + wantVersion: 1, + }, + { + name: "CarV1WithZeroLenSection", + path: "testdata/sample-v1-with-zero-len-section.car", + zerLenAsEOF: true, + wantVersion: 1, + }, + { + name: "AnotherCarV1WithZeroLenSection", + path: "testdata/sample-v1-with-zero-len-section2.car", + zerLenAsEOF: true, + wantVersion: 1, + }, + { + name: "CarV1WithZeroLenSectionWithoutOption", + path: "testdata/sample-v1-with-zero-len-section.car", + wantVersion: 1, + }, + { + name: "AnotherCarV1WithZeroLenSectionWithoutOption", + path: "testdata/sample-v1-with-zero-len-section2.car", + wantVersion: 1, + }, + { + name: "CorruptCarV1", + path: "testdata/sample-v1-tailing-corrupt-section.car", + wantVersion: 1, + }, + { + name: "CarV2WrappingV1", + path: "testdata/sample-wrapped-v2.car", + wantVersion: 2, + }, + { + name: "CarV2ProducedByBlockstore", + path: "testdata/sample-rw-bs-v2.car", + wantVersion: 2, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := requireReaderFromPath(t, tt.path) + subject, err := carv2.NewBlockReader(r, carv2.ZeroLengthSectionAsEOF(tt.zerLenAsEOF)) + require.NoError(t, err) + + require.Equal(t, tt.wantVersion, subject.Version) + + var wantReader *carv1.CarReader + switch tt.wantVersion { + case 1: + wantReader = requireNewCarV1ReaderFromV1File(t, tt.path, tt.zerLenAsEOF) + case 2: + wantReader = requireNewCarV1ReaderFromV2File(t, tt.path, tt.zerLenAsEOF) + default: + require.Failf(t, "invalid test-case", "unknown wantVersion %v", tt.wantVersion) + } + require.Equal(t, wantReader.Header.Roots, subject.Roots) + + for { + gotBlock, gotErr := subject.Next() + wantBlock, wantErr := wantReader.Next() + require.Equal(t, wantBlock, gotBlock) + require.Equal(t, wantErr, gotErr) + if gotErr == io.EOF { + break + } + } + }) + } +} + +func requireReaderFromPath(t *testing.T, path string) io.Reader { + f, err := os.Open(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, f.Close()) }) + return f +} diff --git a/ipld/car/v2/blockstore/bench_test.go b/ipld/car/v2/blockstore/bench_test.go index c97dc3526..644acbe95 100644 --- a/ipld/car/v2/blockstore/bench_test.go +++ b/ipld/car/v2/blockstore/bench_test.go @@ -11,14 +11,21 @@ import ( "github.com/ipld/go-car/v2/blockstore" ) -// Open a read-only blockstore, -// and retrieve all blocks in a shuffled order. +// BenchmarkOpenReadOnlyV1 opens a read-only blockstore, +// and retrieves all blocks in a shuffled order. // Note that this benchmark includes generating an index, // since the input file is a CARv1. - func BenchmarkOpenReadOnlyV1(b *testing.B) { path := "../testdata/sample-v1.car" - + f, err := os.Open("../testdata/sample-v1.car") + if err != nil { + b.Fatal(err) + } + defer func() { + if err := f.Close(); err != nil { + b.Fatal(err) + } + }() info, err := os.Stat(path) if err != nil { b.Fatal(err) @@ -27,12 +34,12 @@ func BenchmarkOpenReadOnlyV1(b *testing.B) { b.ReportAllocs() var shuffledCIDs []cid.Cid - cr, err := carv2.OpenReader(path) + br, err := carv2.NewBlockReader(f) if err != nil { b.Fatal(err) } for { - block, err := cr.Next() + block, err := br.Next() if err == io.EOF { break } @@ -41,7 +48,6 @@ func BenchmarkOpenReadOnlyV1(b *testing.B) { } shuffledCIDs = append(shuffledCIDs, block.Cid()) } - cr.Close() // The shuffling needs to be deterministic, // for the sake of stable benchmark results. @@ -65,7 +71,9 @@ func BenchmarkOpenReadOnlyV1(b *testing.B) { } } - bs.Close() + if err := bs.Close(); err != nil { + b.Fatal(err) + } } }) } diff --git a/ipld/car/v2/example_test.go b/ipld/car/v2/example_test.go index 37bcc22b5..24223c634 100644 --- a/ipld/car/v2/example_test.go +++ b/ipld/car/v2/example_test.go @@ -3,6 +3,7 @@ package car_test import ( "bytes" "fmt" + "io" "io/ioutil" "os" "path/filepath" @@ -68,3 +69,61 @@ func ExampleWrapV1File() { // Inner CARv1 is exactly the same: true // [Block bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy] } + +// ExampleNewBlockReader instantiates a new BlockReader for a CARv1 file and its wrapped CARv2 +// version. For each file, it prints the version, the root CIDs and the first five block CIDs. +// Note, the roots and first five block CIDs are identical in both files since both represent the +// same root CIDs and data blocks. +func ExampleNewBlockReader() { + for _, path := range []string{ + "testdata/sample-v1.car", + "testdata/sample-wrapped-v2.car", + } { + fmt.Println("File:", path) + f, err := os.Open(path) + if err != nil { + panic(err) + } + br, err := carv2.NewBlockReader(f) + if err != nil { + panic(err) + } + defer func() { + if err := f.Close(); err != nil { + panic(err) + } + }() + fmt.Println("Version:", br.Version) + fmt.Println("Roots:", br.Roots) + fmt.Println("First 5 block CIDs:") + for i := 0; i < 5; i++ { + bl, err := br.Next() + if err == io.EOF { + break + } + if err != nil { + panic(err) + } + fmt.Printf("\t%v\n", bl.Cid()) + } + } + // Output: + // File: testdata/sample-v1.car + // Version: 1 + // Roots: [bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy] + // First 5 block CIDs: + // bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy + // bafy2bzaceaycv7jhaegckatnncu5yugzkrnzeqsppzegufr35lroxxnsnpspu + // bafy2bzaceb62wdepofqu34afqhbcn4a7jziwblt2ih5hhqqm6zitd3qpzhdp4 + // bafy2bzaceb3utcspm5jqcdqpih3ztbaztv7yunzkiyfq7up7xmokpxemwgu5u + // bafy2bzacedjwekyjresrwjqj4n2r5bnuuu3klncgjo2r3slsp6wgqb37sz4ck + // File: testdata/sample-wrapped-v2.car + // Version: 2 + // Roots: [bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy] + // First 5 block CIDs: + // bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy + // bafy2bzaceaycv7jhaegckatnncu5yugzkrnzeqsppzegufr35lroxxnsnpspu + // bafy2bzaceb62wdepofqu34afqhbcn4a7jziwblt2ih5hhqqm6zitd3qpzhdp4 + // bafy2bzaceb3utcspm5jqcdqpih3ztbaztv7yunzkiyfq7up7xmokpxemwgu5u + // bafy2bzacedjwekyjresrwjqj4n2r5bnuuu3klncgjo2r3slsp6wgqb37sz4ck +} diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index b4b4d0d45..db8f82105 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -4,8 +4,6 @@ import ( "fmt" "io" - blocks "github.com/ipfs/go-block-format" - internalio "github.com/ipld/go-car/v2/internal/io" "github.com/ipfs/go-cid" @@ -20,11 +18,7 @@ type Reader struct { r io.ReaderAt roots []cid.Cid ropts ReadOptions - // carV1Reader is lazily created, is not reusable, and exclusively used by Reader.Next. - // Note, this reader is forward-only and cannot be rewound. Once it reaches the end of the data - // payload, it will always return io.EOF. - carV1Reader *carv1.CarReader - closer io.Closer + closer io.Closer } // OpenReader is a wrapper for NewReader which opens the file at path. @@ -133,26 +127,6 @@ func (r *Reader) Close() error { return nil } -// Next reads the next block in the data payload with an io.EOF error indicating the end is reached. -// Note, this function is forward-only; once the end has been reached it will always return io.EOF. -func (r *Reader) Next() (blocks.Block, error) { - if r.carV1Reader == nil { - var err error - if r.carV1Reader, err = r.newCarV1Reader(); err != nil { - return nil, err - } - } - return r.carV1Reader.Next() -} - -func (r *Reader) newCarV1Reader() (*carv1.CarReader, error) { - dr := r.DataReader() - if r.ropts.ZeroLengthSectionAsEOF { - return carv1.NewCarReaderWithZeroLengthSectionAsEOF(dr) - } - return carv1.NewCarReader(dr) -} - // ReadVersion reads the version from the pragma. // This function accepts both CARv1 and CARv2 payloads. func ReadVersion(r io.Reader) (uint64, error) { diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go index 8b5ec228f..79d6f855d 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/reader_test.go @@ -136,16 +136,6 @@ func TestReader_WithCarV1Consistency(t *testing.T) { require.NoError(t, err) require.Equal(t, wantReader.Header.Roots, gotRoots) require.Nil(t, subject.IndexReader()) - - for { - gotBlock, gotErr := subject.Next() - wantBlock, wantErr := wantReader.Next() - require.Equal(t, wantBlock, gotBlock) - require.Equal(t, wantErr, gotErr) - if gotErr == io.EOF { - break - } - } }) } } @@ -184,16 +174,6 @@ func TestReader_WithCarV2Consistency(t *testing.T) { wantIndex, err := carv2.GenerateIndex(subject.DataReader()) require.NoError(t, err) require.Equal(t, wantIndex, gotIndex) - - for { - gotBlock, gotErr := subject.Next() - wantBlock, wantErr := wantReader.Next() - require.Equal(t, wantBlock, gotBlock) - require.Equal(t, wantErr, gotErr) - if gotErr == io.EOF { - break - } - } }) } } From 5045cdd7462ba9999425826407b9367682c3b7e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Tue, 3 Aug 2021 18:59:24 +0100 Subject: [PATCH 3517/3817] blockstore: stop embedding ReadOnly in ReadWrite It has unintended side effects, as we leak the inner ReadOnly value as well as its methods to the parent ReadWrite API. One unintended consequence is that it was possible to "close" a ReadWrite, when we only wanted to support finalizing it. The resumption tests do want to close without finalization, for the sake of emulating partially-written CARv2 files. Those can hook into the unexported API via export_test.go. If a downstream really wants to support closing a ReadWrite blockstore without finalizing it, two alternatives are outlined in export_test.go. This commit was moved from ipld/go-car@3752fdb3f2d6be8cf6da7c37591c0ac5822c0822 --- ipld/car/v2/blockstore/example_test.go | 2 - ipld/car/v2/blockstore/export_test.go | 11 +++++ ipld/car/v2/blockstore/readwrite.go | 53 +++++++++++++++--------- ipld/car/v2/blockstore/readwrite_test.go | 14 +++---- 4 files changed, 51 insertions(+), 29 deletions(-) create mode 100644 ipld/car/v2/blockstore/export_test.go diff --git a/ipld/car/v2/blockstore/example_test.go b/ipld/car/v2/blockstore/example_test.go index 3091e471e..7e826addb 100644 --- a/ipld/car/v2/blockstore/example_test.go +++ b/ipld/car/v2/blockstore/example_test.go @@ -93,7 +93,6 @@ func ExampleOpenReadWrite() { if err != nil { panic(err) } - defer rwbs.Close() // Put all blocks onto the blockstore. blocks := []blocks.Block{thisBlock, thatBlock} @@ -121,7 +120,6 @@ func ExampleOpenReadWrite() { if err != nil { panic(err) } - defer resumedRwbos.Close() // Put another block, appending it to the set of blocks that are written previously. if err := resumedRwbos.Put(andTheOtherBlock); err != nil { diff --git a/ipld/car/v2/blockstore/export_test.go b/ipld/car/v2/blockstore/export_test.go new file mode 100644 index 000000000..d4998eec8 --- /dev/null +++ b/ipld/car/v2/blockstore/export_test.go @@ -0,0 +1,11 @@ +package blockstore + +// CloseReadWrite allows our external tests to close a read-write blockstore +// without finalizing it. +// The public API doesn't expose such a method. +// In the future, we might consider adding NewReadWrite taking io interfaces, +// meaning that the caller could be fully in control of opening and closing files. +// Another option would be to expose a "Discard" method alongside "Finalize". +func CloseReadWrite(b *ReadWrite) error { + return b.ronly.Close() +} diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 1192e749c..8986ff305 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -35,11 +35,12 @@ var errFinalized = fmt.Errorf("cannot use a read-write carv2 blockstore after fi // Upon calling Finalize header is finalized and index is written out. // Once finalized, all read and write calls to this blockstore will result in panics. type ReadWrite struct { + ronly ReadOnly + f *os.File dataWriter *internalio.OffsetWriteSeeker - ReadOnly - idx *insertionIndex - header carv2.Header + idx *insertionIndex + header carv2.Header wopts carv2.WriteOptions } @@ -120,7 +121,7 @@ func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.ReadWriteOption) for _, opt := range opts { switch opt := opt.(type) { case carv2.ReadOption: - opt(&rwbs.ropts) + opt(&rwbs.ronly.ropts) case carv2.WriteOption: opt(&rwbs.wopts) } @@ -134,9 +135,9 @@ func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.ReadWriteOption) rwbs.dataWriter = internalio.NewOffsetWriter(rwbs.f, int64(rwbs.header.DataOffset)) v1r := internalio.NewOffsetReadSeeker(rwbs.f, int64(rwbs.header.DataOffset)) - rwbs.ReadOnly.backing = v1r - rwbs.ReadOnly.idx = rwbs.idx - rwbs.ReadOnly.carv2Closer = rwbs.f + rwbs.ronly.backing = v1r + rwbs.ronly.idx = rwbs.idx + rwbs.ronly.carv2Closer = rwbs.f if resume { if err = rwbs.resumeWithRoots(roots); err != nil { @@ -216,7 +217,7 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { } // Use the given CARv1 padding to instantiate the CARv1 reader on file. - v1r := internalio.NewOffsetReadSeeker(b.ReadOnly.backing, 0) + v1r := internalio.NewOffsetReadSeeker(b.ronly.backing, 0) header, err := carv1.ReadHeader(v1r) if err != nil { // Cannot read the CARv1 header; the file is most likely corrupt. @@ -256,7 +257,7 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { // Null padding; by default it's an error. if length == 0 { - if b.ropts.ZeroLengthSectionAsEOF { + if b.ronly.ropts.ZeroLengthSectionAsEOF { break } else { return fmt.Errorf("carv1 null padding not allowed by default; see WithZeroLegthSectionAsEOF") @@ -303,17 +304,17 @@ func (b *ReadWrite) PutMany(blks []blocks.Block) error { return errFinalized } - b.mu.Lock() - defer b.mu.Unlock() + b.ronly.mu.Lock() + defer b.ronly.mu.Unlock() for _, bl := range blks { c := bl.Cid() if !b.wopts.BlockstoreAllowDuplicatePuts { - if b.ropts.BlockstoreUseWholeCIDs && b.idx.hasExactCID(c) { + if b.ronly.ropts.BlockstoreUseWholeCIDs && b.idx.hasExactCID(c) { continue // deduplicated by CID } - if !b.ropts.BlockstoreUseWholeCIDs { + if !b.ronly.ropts.BlockstoreUseWholeCIDs { _, err := b.idx.Get(c) if err == nil { continue // deduplicated by hash @@ -340,8 +341,8 @@ func (b *ReadWrite) Finalize() error { return fmt.Errorf("called Finalize twice") } - b.mu.Lock() - defer b.mu.Unlock() + b.ronly.mu.Lock() + defer b.ronly.mu.Unlock() // TODO check if add index option is set and don't write the index then set index offset to zero. b.header = b.header.WithDataSize(uint64(b.dataWriter.Position())) @@ -349,7 +350,7 @@ func (b *ReadWrite) Finalize() error { // mutex we're holding here. // TODO: should we check the error here? especially with OpenReadWrite, // we should care about close errors. - defer b.closeWithoutMutex() + defer b.ronly.closeWithoutMutex() // TODO if index not needed don't bother flattening it. fi, err := b.idx.flatten() @@ -368,7 +369,7 @@ func (b *ReadWrite) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return nil, errFinalized } - return b.ReadOnly.AllKeysChan(ctx) + return b.ronly.AllKeysChan(ctx) } func (b *ReadWrite) Has(key cid.Cid) (bool, error) { @@ -376,7 +377,7 @@ func (b *ReadWrite) Has(key cid.Cid) (bool, error) { return false, errFinalized } - return b.ReadOnly.Has(key) + return b.ronly.Has(key) } func (b *ReadWrite) Get(key cid.Cid) (blocks.Block, error) { @@ -384,7 +385,7 @@ func (b *ReadWrite) Get(key cid.Cid) (blocks.Block, error) { return nil, errFinalized } - return b.ReadOnly.Get(key) + return b.ronly.Get(key) } func (b *ReadWrite) GetSize(key cid.Cid) (int, error) { @@ -392,5 +393,17 @@ func (b *ReadWrite) GetSize(key cid.Cid) (int, error) { return 0, errFinalized } - return b.ReadOnly.GetSize(key) + return b.ronly.GetSize(key) +} + +func (b *ReadWrite) DeleteBlock(_ cid.Cid) error { + return fmt.Errorf("ReadWrite blockstore does not support deleting blocks") +} + +func (b *ReadWrite) HashOnRead(enable bool) { + b.ronly.HashOnRead(enable) +} + +func (b *ReadWrite) Roots() ([]cid.Cid, error) { + return b.ronly.Roots() } diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 77442de0d..5316f5bf7 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -38,7 +38,7 @@ var ( func TestReadWriteGetReturnsBlockstoreNotFoundWhenCidDoesNotExist(t *testing.T) { path := filepath.Join(t.TempDir(), "readwrite-err-not-found.car") subject, err := blockstore.OpenReadWrite(path, []cid.Cid{}) - t.Cleanup(func() { subject.Close() }) + t.Cleanup(func() { subject.Finalize() }) require.NoError(t, err) nonExistingKey := merkledag.NewRawNode([]byte("undadasea")).Block.Cid() @@ -373,7 +373,7 @@ func TestBlockstoreResumption(t *testing.T) { // Close off the open file and re-instantiate a new subject with resumption enabled. // Note, we don't have to close the file for resumption to work. // We do this to avoid resource leak during testing. - require.NoError(t, subject.Close()) + require.NoError(t, blockstore.CloseReadWrite(subject)) } subject, err = blockstore.OpenReadWrite(path, r.Header.Roots, blockstore.UseWholeCIDs(true)) @@ -405,7 +405,7 @@ func TestBlockstoreResumption(t *testing.T) { require.Equal(t, wantBlockCountSoFar, gotBlockCountSoFar) } } - require.NoError(t, subject.Close()) + require.NoError(t, blockstore.CloseReadWrite(subject)) // Finalize the blockstore to complete partially written CARv2 file. subject, err = blockstore.OpenReadWrite(path, r.Header.Roots, @@ -460,8 +460,8 @@ func TestBlockstoreResumptionIsSupportedOnFinalizedFile(t *testing.T) { require.NoError(t, err) require.NoError(t, subject.Finalize()) subject, err = blockstore.OpenReadWrite(path, []cid.Cid{}) - t.Cleanup(func() { subject.Close() }) require.NoError(t, err) + t.Cleanup(func() { subject.Finalize() }) } func TestReadWritePanicsOnlyWhenFinalized(t *testing.T) { @@ -472,7 +472,6 @@ func TestReadWritePanicsOnlyWhenFinalized(t *testing.T) { subject, err := blockstore.OpenReadWrite(path, wantRoots) require.NoError(t, err) - t.Cleanup(func() { subject.Close() }) require.NoError(t, subject.Put(oneTestBlockWithCidV1)) require.NoError(t, subject.Put(anotherTestBlockWithCidV0)) @@ -500,6 +499,9 @@ func TestReadWritePanicsOnlyWhenFinalized(t *testing.T) { require.NoError(t, subject.Finalize()) require.Error(t, subject.Finalize()) + _, ok := (interface{})(subject).(io.Closer) + require.False(t, ok) + _, err = subject.Get(oneTestBlockCid) require.Error(t, err) _, err = subject.GetSize(anotherTestBlockCid) @@ -528,7 +530,6 @@ func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) { carv2.UseDataPadding(wantCarV1Padding), carv2.UseIndexPadding(wantIndexPadding)) require.NoError(t, err) - t.Cleanup(func() { subject.Close() }) require.NoError(t, subject.Put(oneTestBlockWithCidV1)) require.NoError(t, subject.Put(anotherTestBlockWithCidV0)) require.NoError(t, subject.Finalize()) @@ -606,7 +607,6 @@ func TestReadWriteResumptionFromFileWithDifferentCarV1PaddingIsError(t *testing. WantRoots, carv2.UseDataPadding(1413)) require.NoError(t, err) - t.Cleanup(func() { subject.Close() }) require.NoError(t, subject.Put(oneTestBlockWithCidV1)) require.NoError(t, subject.Finalize()) From cffae0eb1a8f20ff0069546cdbaf34e5ed371292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Fri, 30 Jul 2021 15:07:36 +0100 Subject: [PATCH 3518/3817] update package godocs and root level README for v2 While at it, make v2's license a symlink, so the two stay in sync. The reason we want the v2 module to have its own license is so that its module zip includes the file as well. Besides mentioning v0 and v2, the root README now also links to pkgsite and lists Masih and myself as default maintainers, given that we're the ones that did major work on this library last. Fixes #124. Fixes #179. This commit was moved from ipld/go-car@61cfb4dbcfc4bf48c54425385a1aae279b2b6ca1 --- ipld/car/README.md | 17 ++- ipld/car/v2/LICENSE.md | 230 +--------------------------------- ipld/car/v2/blockstore/doc.go | 2 +- ipld/car/v2/doc.go | 9 +- 4 files changed, 21 insertions(+), 237 deletions(-) mode change 100644 => 120000 ipld/car/v2/LICENSE.md diff --git a/ipld/car/README.md b/ipld/car/README.md index 033318ee1..faec4b69a 100644 --- a/ipld/car/README.md +++ b/ipld/car/README.md @@ -4,17 +4,24 @@ go-car (go!) [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](https://protocol.ai) [![](https://img.shields.io/badge/project-ipld-orange.svg?style=flat-square)](https://github.com/ipld/ipld) [![](https://img.shields.io/badge/freenode-%23ipld-orange.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipld) +[![Go Reference](https://pkg.go.dev/badge/github.com/ipld/go-car.svg)](https://pkg.go.dev/github.com/ipld/go-car) [![Coverage Status](https://codecov.io/gh/ipld/go-car/branch/master/graph/badge.svg)](https://codecov.io/gh/ipld/go-car/branch/master) -> go-car is a simple way of packing a merkledag into a single file +> A library to interact with merkledags stored as a single file +This is an implementation in Go of the [CAR spec](https://ipld.io/specs/transport/car/). -## Table of Contents +Note that there are two major module versions: -- [Install](#install) -- [Contribute](#contribute) -- [License](#license) +* [go-car/v2](v2/) is geared towards reading and writing CARv2 files, and also + supports consuming CARv1 files and using CAR files as an IPFS blockstore. +* go-car v0, in the root directory, just supports reading and writing CARv1 files. +Most users should attempt to use v2, especially for new software. + +## Maintainers + +[Daniel Martí](https://github.com/mvdan) and [Masih Derkani](https://github.com/masih). ## Contribute diff --git a/ipld/car/v2/LICENSE.md b/ipld/car/v2/LICENSE.md deleted file mode 100644 index 15601cba6..000000000 --- a/ipld/car/v2/LICENSE.md +++ /dev/null @@ -1,229 +0,0 @@ -The contents of this repository are Copyright (c) corresponding authors and -contributors, licensed under the `Permissive License Stack` meaning either of: - -- Apache-2.0 Software License: https://www.apache.org/licenses/LICENSE-2.0 - ([...4tr2kfsq](https://gateway.ipfs.io/ipfs/bafkreiankqxazcae4onkp436wag2lj3ccso4nawxqkkfckd6cg4tr2kfsq)) - -- MIT Software License: https://opensource.org/licenses/MIT - ([...vljevcba](https://gateway.ipfs.io/ipfs/bafkreiepofszg4gfe2gzuhojmksgemsub2h4uy2gewdnr35kswvljevcba)) - -You may not use the contents of this repository except in compliance -with one of the listed Licenses. For an extended clarification of the -intent behind the choice of Licensing please refer to -https://protocol.ai/blog/announcing-the-permissive-license-stack/ - -Unless required by applicable law or agreed to in writing, software -distributed under the terms listed in this notice is distributed on -an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -either express or implied. See each License for the specific language -governing permissions and limitations under that License. - - -`SPDX-License-Identifier: Apache-2.0 OR MIT` - -Verbatim copies of both licenses are included below: - -

    Apache-2.0 Software License - -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS -``` -
    - -
    MIT Software License - -``` -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -``` -
    diff --git a/ipld/car/v2/LICENSE.md b/ipld/car/v2/LICENSE.md new file mode 120000 index 000000000..7eabdb1c2 --- /dev/null +++ b/ipld/car/v2/LICENSE.md @@ -0,0 +1 @@ +../LICENSE.md \ No newline at end of file diff --git a/ipld/car/v2/blockstore/doc.go b/ipld/car/v2/blockstore/doc.go index 180dc7292..de95d63bf 100644 --- a/ipld/car/v2/blockstore/doc.go +++ b/ipld/car/v2/blockstore/doc.go @@ -1,4 +1,4 @@ -// package blockstore implements IPFS blockstore interface backed by a CAR file. +// package blockstore implements the IPFS blockstore interface backed by a CAR file. // This package provides two flavours of blockstore: ReadOnly and ReadWrite. // // The ReadOnly blockstore provides a read-only random access from a given data payload either in diff --git a/ipld/car/v2/doc.go b/ipld/car/v2/doc.go index 2029d7cf3..b12266649 100644 --- a/ipld/car/v2/doc.go +++ b/ipld/car/v2/doc.go @@ -1,3 +1,8 @@ -// Package car represents the CARv2 implementation. -// TODO add CARv2 byte structure here. +// Package car allows inspecting and reading CAR files, +// described at https://ipld.io/specs/transport/car/. +// The entire library is geared towards the CARv2 spec, +// but many of the APIs consuming CAR files also accept CARv1. +// +// The blockstore sub-package contains an implementation of the +// go-ipfs-blockstore interface. package car From 1296deb10c924807dfaa924a95cf8b7d6f9a4bed Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 4 Aug 2021 16:36:26 +0100 Subject: [PATCH 3519/3817] Update the readme with link to examples Add examples link to the README and refine its structure. List features of CARv2. Use stronger language to encourage users to use `v2` over `v0`. This commit was moved from ipld/go-car@92dec83621a0da02046f8e29313d8e6e02b2e54e --- ipld/car/README.md | 48 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/ipld/car/README.md b/ipld/car/README.md index faec4b69a..7ccf7cca3 100644 --- a/ipld/car/README.md +++ b/ipld/car/README.md @@ -9,25 +9,61 @@ go-car (go!) > A library to interact with merkledags stored as a single file -This is an implementation in Go of the [CAR spec](https://ipld.io/specs/transport/car/). +This is a Go implementation of the [CAR specifications](https://ipld.io/specs/transport/car/), both [CARv1](https://ipld.io/specs/transport/car/carv1/) and [CARv2](https://ipld.io/specs/transport/car/carv2/). Note that there are two major module versions: -* [go-car/v2](v2/) is geared towards reading and writing CARv2 files, and also +* [`go-car/v2`](v2/) is geared towards reading and writing CARv2 files, and also supports consuming CARv1 files and using CAR files as an IPFS blockstore. -* go-car v0, in the root directory, just supports reading and writing CARv1 files. +* `go-car` v0, in the root directory, just supports reading and writing CARv1 files. -Most users should attempt to use v2, especially for new software. +Most users should use v2, especially for new software, since the v2 API transparently supports both CAR formats. + +## Features + +[CARv2](v2) features: +* [Generate index](https://pkg.go.dev/github.com/ipld/go-car/v2#GenerateIndex) from an existing CARv1 file +* [Wrap](https://pkg.go.dev/github.com/ipld/go-car/v2#WrapV1) CARv1 files into a CARv2 with automatic index generation. +* Random-access to blocks in a CAR file given their CID via [Read-Only blockstore](https://pkg.go.dev/github.com/ipld/go-car/v2/blockstore#NewReadOnly) API, with transparent support for both CARv1 and CARv2 +* Write CARv2 files via [Read-Write blockstore](https://pkg.go.dev/github.com/ipld/go-car/v2/blockstore#OpenReadWrite) API, with support for appending blocks to an existing CARv2 file, and resumption from a partially written CARv2 files. +* Individual access to [inner CARv1 data payload]((https://pkg.go.dev/github.com/ipld/go-car/v2#Reader.DataReader)) and [index]((https://pkg.go.dev/github.com/ipld/go-car/v2#Reader.IndexReader)) of a CARv2 file via the `Reader` API. + +## Install + +To install the latest version of `go-car/v2` module, run: +```shell script +go get github.com/ipld/go-car/v2 +``` + +Alternatively, to install the v0 module, run: +```shell script +go get github.com/ipld/go-car +``` + +## API Documentation + +See docs on [pkg.go.dev](https://pkg.go.dev/github.com/ipld/go-car). + +## Examples + +Here is a shortlist of other examples from the documentation + +* [Wrap an existing CARv1 file into an indexed CARv2 file](https://pkg.go.dev/github.com/ipld/go-car/v2#example-WrapV1File) +* [Open read-only blockstore from a CAR file](https://pkg.go.dev/github.com/ipld/go-car/v2/blockstore#example-OpenReadOnly) +* [Open read-write blockstore from a CAR file](https://pkg.go.dev/github.com/ipld/go-car/v2/blockstore#example-OpenReadWrite) +* [Read the index from an existing CARv2 file](https://pkg.go.dev/github.com/ipld/go-car/v2/index#example-ReadFrom) +* [Extract the index from a CARv2 file and store it as a separate file](https://pkg.go.dev/github.com/ipld/go-car/v2/index#example-WriteTo) ## Maintainers -[Daniel Martí](https://github.com/mvdan) and [Masih Derkani](https://github.com/masih). +* [Daniel Martí](https://github.com/mvdan) +* [Masih Derkani](https://github.com/masih) ## Contribute PRs are welcome! -Small note: If editing the Readme, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. +When editing the Readme, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. ## License From 7ab33240b1154a548b3fb25ed7243a7c58a82bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Thu, 5 Aug 2021 17:44:38 +0100 Subject: [PATCH 3520/3817] v2: stop using a symlink for LICENSE.md Go modules has to support many OSs and filesystems, so it completely ignores symlinks when creating module zips. And parts of the Go ecosystem, like pkg.go.dev, consume those zips. So this symlink actually makes the module technically non-OSS, since it completely lacks a LICENSE file when downloaded. pkg.go.dev thus refuses to show its documentation. One option would be to rename the root LICENSE.md file to just LICENSE, since cmd/go treats that file in a special way, including it in subdirectory-module zips. Unfortunately, we do want the ".md" extension, since the file is formatted to outline the double license. As such, simply copy the file. It should be fine as it's mostly static. This commit was moved from ipld/go-car@00a30a95ae6c7a6a8596f5166f93156678b631e2 --- ipld/car/LICENSE.md | 229 ---------------------------------------- ipld/car/v2/LICENSE.md | 230 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 229 insertions(+), 230 deletions(-) delete mode 100644 ipld/car/LICENSE.md mode change 120000 => 100644 ipld/car/v2/LICENSE.md diff --git a/ipld/car/LICENSE.md b/ipld/car/LICENSE.md deleted file mode 100644 index 15601cba6..000000000 --- a/ipld/car/LICENSE.md +++ /dev/null @@ -1,229 +0,0 @@ -The contents of this repository are Copyright (c) corresponding authors and -contributors, licensed under the `Permissive License Stack` meaning either of: - -- Apache-2.0 Software License: https://www.apache.org/licenses/LICENSE-2.0 - ([...4tr2kfsq](https://gateway.ipfs.io/ipfs/bafkreiankqxazcae4onkp436wag2lj3ccso4nawxqkkfckd6cg4tr2kfsq)) - -- MIT Software License: https://opensource.org/licenses/MIT - ([...vljevcba](https://gateway.ipfs.io/ipfs/bafkreiepofszg4gfe2gzuhojmksgemsub2h4uy2gewdnr35kswvljevcba)) - -You may not use the contents of this repository except in compliance -with one of the listed Licenses. For an extended clarification of the -intent behind the choice of Licensing please refer to -https://protocol.ai/blog/announcing-the-permissive-license-stack/ - -Unless required by applicable law or agreed to in writing, software -distributed under the terms listed in this notice is distributed on -an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -either express or implied. See each License for the specific language -governing permissions and limitations under that License. - - -`SPDX-License-Identifier: Apache-2.0 OR MIT` - -Verbatim copies of both licenses are included below: - -
    Apache-2.0 Software License - -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS -``` -
    - -
    MIT Software License - -``` -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -``` -
    diff --git a/ipld/car/v2/LICENSE.md b/ipld/car/v2/LICENSE.md deleted file mode 120000 index 7eabdb1c2..000000000 --- a/ipld/car/v2/LICENSE.md +++ /dev/null @@ -1 +0,0 @@ -../LICENSE.md \ No newline at end of file diff --git a/ipld/car/v2/LICENSE.md b/ipld/car/v2/LICENSE.md new file mode 100644 index 000000000..15601cba6 --- /dev/null +++ b/ipld/car/v2/LICENSE.md @@ -0,0 +1,229 @@ +The contents of this repository are Copyright (c) corresponding authors and +contributors, licensed under the `Permissive License Stack` meaning either of: + +- Apache-2.0 Software License: https://www.apache.org/licenses/LICENSE-2.0 + ([...4tr2kfsq](https://gateway.ipfs.io/ipfs/bafkreiankqxazcae4onkp436wag2lj3ccso4nawxqkkfckd6cg4tr2kfsq)) + +- MIT Software License: https://opensource.org/licenses/MIT + ([...vljevcba](https://gateway.ipfs.io/ipfs/bafkreiepofszg4gfe2gzuhojmksgemsub2h4uy2gewdnr35kswvljevcba)) + +You may not use the contents of this repository except in compliance +with one of the listed Licenses. For an extended clarification of the +intent behind the choice of Licensing please refer to +https://protocol.ai/blog/announcing-the-permissive-license-stack/ + +Unless required by applicable law or agreed to in writing, software +distributed under the terms listed in this notice is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +either express or implied. See each License for the specific language +governing permissions and limitations under that License. + + +`SPDX-License-Identifier: Apache-2.0 OR MIT` + +Verbatim copies of both licenses are included below: + +
    Apache-2.0 Software License + +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS +``` +
    + +
    MIT Software License + +``` +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` +
    From 6e7b62dd830fa13e8b305e6df23712492e4aad13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Thu, 5 Aug 2021 17:58:52 +0100 Subject: [PATCH 3521/3817] re-add root LICENSE file The last commit did a mv. I intended to do a cp. This commit was moved from ipld/go-car@2c0f1eee363d6ccdaff9849b763035913fcd895d --- ipld/car/LICENSE.md | 229 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 ipld/car/LICENSE.md diff --git a/ipld/car/LICENSE.md b/ipld/car/LICENSE.md new file mode 100644 index 000000000..15601cba6 --- /dev/null +++ b/ipld/car/LICENSE.md @@ -0,0 +1,229 @@ +The contents of this repository are Copyright (c) corresponding authors and +contributors, licensed under the `Permissive License Stack` meaning either of: + +- Apache-2.0 Software License: https://www.apache.org/licenses/LICENSE-2.0 + ([...4tr2kfsq](https://gateway.ipfs.io/ipfs/bafkreiankqxazcae4onkp436wag2lj3ccso4nawxqkkfckd6cg4tr2kfsq)) + +- MIT Software License: https://opensource.org/licenses/MIT + ([...vljevcba](https://gateway.ipfs.io/ipfs/bafkreiepofszg4gfe2gzuhojmksgemsub2h4uy2gewdnr35kswvljevcba)) + +You may not use the contents of this repository except in compliance +with one of the listed Licenses. For an extended clarification of the +intent behind the choice of Licensing please refer to +https://protocol.ai/blog/announcing-the-permissive-license-stack/ + +Unless required by applicable law or agreed to in writing, software +distributed under the terms listed in this notice is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +either express or implied. See each License for the specific language +governing permissions and limitations under that License. + + +`SPDX-License-Identifier: Apache-2.0 OR MIT` + +Verbatim copies of both licenses are included below: + +
    Apache-2.0 Software License + +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS +``` +
    + +
    MIT Software License + +``` +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` +
    From 80905ed91867a39a4cb36aebbd30e40b78b3adba Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Wed, 17 Feb 2021 14:39:26 -0800 Subject: [PATCH 3522/3817] feat(merkledag): use ipld-prime for encode & decode in DAG Service This commit was moved from ipfs/go-merkledag@4a93571fee3e2184e5569a085b4cffbf15c0c1d1 --- ipld/merkledag/coding.go | 103 +++++++++++++------ ipld/merkledag/merkledag.go | 84 +++++++-------- ipld/merkledag/node.go | 73 ++++++++----- ipld/merkledag/prime.go | 198 ++++++++++++++++++++++++++++++++++++ ipld/merkledag/raw.go | 70 +++++++++---- 5 files changed, 412 insertions(+), 116 deletions(-) create mode 100644 ipld/merkledag/prime.go diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index f0d6d69f0..fc155df80 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -1,15 +1,18 @@ package merkledag import ( + "bytes" "fmt" "sort" "strings" blocks "github.com/ipfs/go-block-format" - pb "github.com/ipfs/go-merkledag/pb" - cid "github.com/ipfs/go-cid" - ipld "github.com/ipfs/go-ipld-format" + format "github.com/ipfs/go-ipld-format" + pb "github.com/ipfs/go-merkledag/pb" + dagpb "github.com/ipld/go-ipld-prime-proto" + "github.com/ipld/go-ipld-prime/fluent" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" ) // Make sure the user doesn't upgrade this file. @@ -22,38 +25,77 @@ const _ = pb.DoNotUpgradeFileEverItWillChangeYourHashes // unmarshal decodes raw data into a *Node instance. // The conversion uses an intermediate PBNode. -func (n *ProtoNode) unmarshal(encoded []byte) error { - var pbn pb.PBNode - if err := pbn.Unmarshal(encoded); err != nil { - return fmt.Errorf("unmarshal failed. %v", err) +func unmarshal(encodedBytes []byte) (*ProtoNode, error) { + nb := dagpb.Type.PBNode.NewBuilder() + err := dagpb.RawDecoder(nb, bytes.NewBuffer(encodedBytes)) + if err != nil { + return nil, err } + nd := nb.Build() + return fromImmutableNode(&immutableProtoNode{encodedBytes, nd.(dagpb.PBNode)}), nil +} - pbnl := pbn.GetLinks() - n.links = make([]*ipld.Link, len(pbnl)) - for i, l := range pbnl { - n.links[i] = &ipld.Link{Name: l.GetName(), Size: l.GetTsize()} - c, err := cid.Cast(l.GetHash()) - if err != nil { - return fmt.Errorf("link hash #%d is not valid multihash. %v", i, err) +func fromImmutableNode(encoded *immutableProtoNode) *ProtoNode { + n := new(ProtoNode) + n.encoded = encoded + n.data = n.encoded.PBNode.Data.Bytes() + links := make([]*format.Link, 0, n.encoded.PBNode.Links.Length()) + iter := n.encoded.PBNode.Links.Iterator() + for !iter.Done() { + _, next := iter.Next() + link := &format.Link{ + Name: next.FieldName().Must().String(), + Size: uint64(next.FieldTsize().Must().Int()), + Cid: next.FieldHash().Must().Link().(cidlink.Link).Cid, } - n.links[i].Cid = c + links = append(links, link) } - sort.Stable(LinkSlice(n.links)) // keep links sorted - - n.data = pbn.GetData() - n.encoded = encoded - return nil + n.links = links + return n +} +func (n *ProtoNode) marshalImmutable() (*immutableProtoNode, error) { + nb := dagpb.Type.PBNode.NewBuilder() + err := fluent.Recover(func() { + fb := fluent.WrapAssembler(nb) + fb.CreateMap(-1, func(fmb fluent.MapAssembler) { + fmb.AssembleEntry("Links").CreateList(int64(len(n.links)), func(flb fluent.ListAssembler) { + for _, link := range n.links { + flb.AssembleValue().CreateMap(-1, func(fmb fluent.MapAssembler) { + if link.Cid.Defined() { + hash, err := cid.Cast(link.Cid.Bytes()) + if err != nil { + panic(fluent.Error{Err: fmt.Errorf("unmarshal failed. %v", err)}) + } + fmb.AssembleEntry("Hash").AssignLink(cidlink.Link{Cid: hash}) + } + fmb.AssembleEntry("Name").AssignString(link.Name) + fmb.AssembleEntry("Tsize").AssignInt(int64(link.Size)) + }) + } + }) + fmb.AssembleEntry("Data").AssignBytes(n.data) + }) + }) + if err != nil { + return nil, err + } + nd := nb.Build() + newData := new(bytes.Buffer) + err = dagpb.PBEncoder(nd, newData) + if err != nil { + return nil, err + } + return &immutableProtoNode{newData.Bytes(), nd.(dagpb.PBNode)}, nil } // Marshal encodes a *Node instance into a new byte slice. // The conversion uses an intermediate PBNode. func (n *ProtoNode) Marshal() ([]byte, error) { - pbn := n.GetPBNode() - data, err := pbn.Marshal() + enc, err := n.marshalImmutable() if err != nil { - return data, fmt.Errorf("marshal failed. %v", err) + return nil, err } - return data, nil + return enc.encoded, nil } // GetPBNode converts *ProtoNode into it's protocol buffer variant. @@ -88,14 +130,14 @@ func (n *ProtoNode) EncodeProtobuf(force bool) ([]byte, error) { if n.encoded == nil || force { n.cached = cid.Undef var err error - n.encoded, err = n.Marshal() + n.encoded, err = n.marshalImmutable() if err != nil { return nil, err } } if !n.cached.Defined() { - c, err := n.CidBuilder().Sum(n.encoded) + c, err := n.CidBuilder().Sum(n.encoded.encoded) if err != nil { return nil, err } @@ -103,13 +145,12 @@ func (n *ProtoNode) EncodeProtobuf(force bool) ([]byte, error) { n.cached = c } - return n.encoded, nil + return n.encoded.encoded, nil } // DecodeProtobuf decodes raw data and returns a new Node instance. func DecodeProtobuf(encoded []byte) (*ProtoNode, error) { - n := new(ProtoNode) - err := n.unmarshal(encoded) + n, err := unmarshal(encoded) if err != nil { return nil, fmt.Errorf("incorrectly formatted merkledag node: %s", err) } @@ -118,7 +159,7 @@ func DecodeProtobuf(encoded []byte) (*ProtoNode, error) { // DecodeProtobufBlock is a block decoder for protobuf IPLD nodes conforming to // node.DecodeBlockFunc -func DecodeProtobufBlock(b blocks.Block) (ipld.Node, error) { +func DecodeProtobufBlock(b blocks.Block) (format.Node, error) { c := b.Cid() if c.Type() != cid.DagProtobuf { return nil, fmt.Errorf("this function can only decode protobuf nodes") @@ -138,4 +179,4 @@ func DecodeProtobufBlock(b blocks.Block) (ipld.Node, error) { } // Type assertion -var _ ipld.DecodeBlockFunc = DecodeProtobufBlock +var _ format.DecodeBlockFunc = DecodeProtobufBlock diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 76f402bea..4a1e12397 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -10,16 +10,20 @@ import ( bserv "github.com/ipfs/go-blockservice" cid "github.com/ipfs/go-cid" ipldcbor "github.com/ipfs/go-ipld-cbor" - ipld "github.com/ipfs/go-ipld-format" + format "github.com/ipfs/go-ipld-format" + legacy "github.com/ipfs/go-ipld-legacy" + dagpb "github.com/ipld/go-ipld-prime-proto" ) // TODO: We should move these registrations elsewhere. Really, most of the IPLD // functionality should go in a `go-ipld` repo but that will take a lot of work // and design. func init() { - ipld.Register(cid.DagProtobuf, DecodeProtobufBlock) - ipld.Register(cid.Raw, DecodeRawBlock) - ipld.Register(cid.DagCBOR, ipldcbor.DecodeBlock) + format.Register(cid.DagProtobuf, DecodeProtobufBlock) + format.Register(cid.Raw, DecodeRawBlock) + format.Register(cid.DagCBOR, ipldcbor.DecodeBlock) + legacy.RegisterCodec(cid.DagProtobuf, dagpb.Type.PBNode, ProtoNodeConverter) + legacy.RegisterCodec(cid.Raw, dagpb.Type.RawNode, RawNodeConverter) } // contextKey is a type to use as value for the ProgressTracker contexts. @@ -43,7 +47,7 @@ type dagService struct { } // Add adds a node to the dagService, storing the block in the BlockService -func (n *dagService) Add(ctx context.Context, nd ipld.Node) error { +func (n *dagService) Add(ctx context.Context, nd format.Node) error { if n == nil { // FIXME remove this assertion. protect with constructor invariant return fmt.Errorf("dagService is nil") } @@ -51,7 +55,7 @@ func (n *dagService) Add(ctx context.Context, nd ipld.Node) error { return n.Blocks.AddBlock(nd) } -func (n *dagService) AddMany(ctx context.Context, nds []ipld.Node) error { +func (n *dagService) AddMany(ctx context.Context, nds []format.Node) error { blks := make([]blocks.Block, len(nds)) for i, nd := range nds { blks[i] = nd @@ -60,7 +64,7 @@ func (n *dagService) AddMany(ctx context.Context, nds []ipld.Node) error { } // Get retrieves a node from the dagService, fetching the block in the BlockService -func (n *dagService) Get(ctx context.Context, c cid.Cid) (ipld.Node, error) { +func (n *dagService) Get(ctx context.Context, c cid.Cid) (format.Node, error) { if n == nil { return nil, fmt.Errorf("dagService is nil") } @@ -71,17 +75,17 @@ func (n *dagService) Get(ctx context.Context, c cid.Cid) (ipld.Node, error) { b, err := n.Blocks.GetBlock(ctx, c) if err != nil { if err == bserv.ErrNotFound { - return nil, ipld.ErrNotFound + return nil, format.ErrNotFound } return nil, fmt.Errorf("failed to get block for %s: %v", c, err) } - return ipld.Decode(b) + return legacy.DecodeNode(ctx, b) } // GetLinks return the links for the node, the node doesn't necessarily have // to exist locally. -func (n *dagService) GetLinks(ctx context.Context, c cid.Cid) ([]*ipld.Link, error) { +func (n *dagService) GetLinks(ctx context.Context, c cid.Cid) ([]*format.Link, error) { if c.Type() == cid.Raw { return nil, nil } @@ -114,12 +118,12 @@ func (n *dagService) RemoveMany(ctx context.Context, cids []cid.Cid) error { // GetLinksDirect creates a function to get the links for a node, from // the node, bypassing the LinkService. If the node does not exist // locally (and can not be retrieved) an error will be returned. -func GetLinksDirect(serv ipld.NodeGetter) GetLinks { - return func(ctx context.Context, c cid.Cid) ([]*ipld.Link, error) { +func GetLinksDirect(serv format.NodeGetter) GetLinks { + return func(ctx context.Context, c cid.Cid) ([]*format.Link, error) { nd, err := serv.Get(ctx, c) if err != nil { if err == bserv.ErrNotFound { - err = ipld.ErrNotFound + err = format.ErrNotFound } return nil, err } @@ -132,32 +136,32 @@ type sesGetter struct { } // Get gets a single node from the DAG. -func (sg *sesGetter) Get(ctx context.Context, c cid.Cid) (ipld.Node, error) { +func (sg *sesGetter) Get(ctx context.Context, c cid.Cid) (format.Node, error) { blk, err := sg.bs.GetBlock(ctx, c) switch err { case bserv.ErrNotFound: - return nil, ipld.ErrNotFound + return nil, format.ErrNotFound case nil: // noop default: return nil, err } - return ipld.Decode(blk) + return legacy.DecodeNode(ctx, blk) } // GetMany gets many nodes at once, batching the request if possible. -func (sg *sesGetter) GetMany(ctx context.Context, keys []cid.Cid) <-chan *ipld.NodeOption { +func (sg *sesGetter) GetMany(ctx context.Context, keys []cid.Cid) <-chan *format.NodeOption { return getNodesFromBG(ctx, sg.bs, keys) } // Session returns a NodeGetter using a new session for block fetches. -func (n *dagService) Session(ctx context.Context) ipld.NodeGetter { +func (n *dagService) Session(ctx context.Context) format.NodeGetter { return &sesGetter{bserv.NewSession(ctx, n.Blocks)} } // FetchGraph fetches all nodes that are children of the given node -func FetchGraph(ctx context.Context, root cid.Cid, serv ipld.DAGService) error { +func FetchGraph(ctx context.Context, root cid.Cid, serv format.DAGService) error { return FetchGraphWithDepthLimit(ctx, root, -1, serv) } @@ -165,8 +169,8 @@ func FetchGraph(ctx context.Context, root cid.Cid, serv ipld.DAGService) error { // node down to the given depth. maxDepth=0 means "only fetch root", // maxDepth=1 means "fetch root and its direct children" and so on... // maxDepth=-1 means unlimited. -func FetchGraphWithDepthLimit(ctx context.Context, root cid.Cid, depthLim int, serv ipld.DAGService) error { - var ng ipld.NodeGetter = NewSession(ctx, serv) +func FetchGraphWithDepthLimit(ctx context.Context, root cid.Cid, depthLim int, serv format.DAGService) error { + var ng format.NodeGetter = NewSession(ctx, serv) set := make(map[cid.Cid]int) @@ -212,7 +216,7 @@ func FetchGraphWithDepthLimit(ctx context.Context, root cid.Cid, depthLim int, s // This method may not return all requested nodes (and may or may not return an // error indicating that it failed to do so. It is up to the caller to verify // that it received all nodes. -func (n *dagService) GetMany(ctx context.Context, keys []cid.Cid) <-chan *ipld.NodeOption { +func (n *dagService) GetMany(ctx context.Context, keys []cid.Cid) <-chan *format.NodeOption { return getNodesFromBG(ctx, n.Blocks, keys) } @@ -227,10 +231,10 @@ func dedupKeys(keys []cid.Cid) []cid.Cid { return set.Keys() } -func getNodesFromBG(ctx context.Context, bs bserv.BlockGetter, keys []cid.Cid) <-chan *ipld.NodeOption { +func getNodesFromBG(ctx context.Context, bs bserv.BlockGetter, keys []cid.Cid) <-chan *format.NodeOption { keys = dedupKeys(keys) - out := make(chan *ipld.NodeOption, len(keys)) + out := make(chan *format.NodeOption, len(keys)) blocks := bs.GetBlocks(ctx, keys) var count int @@ -241,22 +245,22 @@ func getNodesFromBG(ctx context.Context, bs bserv.BlockGetter, keys []cid.Cid) < case b, ok := <-blocks: if !ok { if count != len(keys) { - out <- &ipld.NodeOption{Err: fmt.Errorf("failed to fetch all nodes")} + out <- &format.NodeOption{Err: fmt.Errorf("failed to fetch all nodes")} } return } - nd, err := ipld.Decode(b) + nd, err := legacy.DecodeNode(ctx, b) if err != nil { - out <- &ipld.NodeOption{Err: err} + out <- &format.NodeOption{Err: err} return } - out <- &ipld.NodeOption{Node: nd} + out <- &format.NodeOption{Node: nd} count++ case <-ctx.Done(): - out <- &ipld.NodeOption{Err: ctx.Err()} + out <- &format.NodeOption{Err: ctx.Err()} return } } @@ -266,15 +270,15 @@ func getNodesFromBG(ctx context.Context, bs bserv.BlockGetter, keys []cid.Cid) < // GetLinks is the type of function passed to the EnumerateChildren function(s) // for getting the children of an IPLD node. -type GetLinks func(context.Context, cid.Cid) ([]*ipld.Link, error) +type GetLinks func(context.Context, cid.Cid) ([]*format.Link, error) // GetLinksWithDAG returns a GetLinks function that tries to use the given // NodeGetter as a LinkGetter to get the children of a given IPLD node. This may // allow us to traverse the DAG without actually loading and parsing the node in // question (if we already have the links cached). -func GetLinksWithDAG(ng ipld.NodeGetter) GetLinks { - return func(ctx context.Context, c cid.Cid) ([]*ipld.Link, error) { - return ipld.GetLinks(ctx, ng, c) +func GetLinksWithDAG(ng format.NodeGetter) GetLinks { + return func(ctx context.Context, c cid.Cid) ([]*format.Link, error) { + return format.GetLinks(ctx, ng, c) } } @@ -344,7 +348,7 @@ func IgnoreErrors() WalkOption { func IgnoreMissing() WalkOption { return func(walkOptions *walkOptions) { walkOptions.addHandler(func(c cid.Cid, err error) error { - if err == ipld.ErrNotFound { + if err == format.ErrNotFound { return nil } return err @@ -357,7 +361,7 @@ func IgnoreMissing() WalkOption { func OnMissing(callback func(c cid.Cid)) WalkOption { return func(walkOptions *walkOptions) { walkOptions.addHandler(func(c cid.Cid, err error) error { - if err == ipld.ErrNotFound { + if err == format.ErrNotFound { callback(c) } return err @@ -454,7 +458,7 @@ func parallelWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, vis } type linksDepth struct { - links []*ipld.Link + links []*format.Link depth int } @@ -569,7 +573,7 @@ func parallelWalkDepth(ctx context.Context, getLinks GetLinks, root cid.Cid, vis } } -var _ ipld.LinkGetter = &dagService{} -var _ ipld.NodeGetter = &dagService{} -var _ ipld.NodeGetter = &sesGetter{} -var _ ipld.DAGService = &dagService{} +var _ format.LinkGetter = &dagService{} +var _ format.NodeGetter = &dagService{} +var _ format.NodeGetter = &sesGetter{} +var _ format.DAGService = &dagService{} diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 3478a02e0..c45f8b9a1 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -5,8 +5,12 @@ import ( "encoding/json" "fmt" + blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" - ipld "github.com/ipfs/go-ipld-format" + format "github.com/ipfs/go-ipld-format" + legacy "github.com/ipfs/go-ipld-legacy" + ipld "github.com/ipld/go-ipld-prime" + dagpb "github.com/ipld/go-ipld-prime-proto" mh "github.com/multiformats/go-multihash" ) @@ -16,16 +20,20 @@ var ( ErrLinkNotFound = fmt.Errorf("no link by that name") ) +type immutableProtoNode struct { + encoded []byte + dagpb.PBNode +} + // ProtoNode represents a node in the IPFS Merkle DAG. // nodes have opaque data and a set of navigable links. type ProtoNode struct { - links []*ipld.Link + links []*format.Link data []byte // cache encoded/marshaled value - encoded []byte - - cached cid.Cid + encoded *immutableProtoNode + cached cid.Cid // builder specifies cid version and hashing function builder cid.Builder @@ -82,8 +90,8 @@ func (n *ProtoNode) SetCidBuilder(builder cid.Builder) { } } -// LinkSlice is a slice of ipld.Links -type LinkSlice []*ipld.Link +// LinkSlice is a slice of format.Links +type LinkSlice []*format.Link func (ls LinkSlice) Len() int { return len(ls) } func (ls LinkSlice) Swap(a, b int) { ls[a], ls[b] = ls[b], ls[a] } @@ -95,10 +103,8 @@ func NodeWithData(d []byte) *ProtoNode { } // AddNodeLink adds a link to another node. -func (n *ProtoNode) AddNodeLink(name string, that ipld.Node) error { - n.encoded = nil - - lnk, err := ipld.MakeLink(that) +func (n *ProtoNode) AddNodeLink(name string, that format.Node) error { + lnk, err := format.MakeLink(that) if err != nil { return err } @@ -111,9 +117,9 @@ func (n *ProtoNode) AddNodeLink(name string, that ipld.Node) error { } // AddRawLink adds a copy of a link to this node -func (n *ProtoNode) AddRawLink(name string, l *ipld.Link) error { +func (n *ProtoNode) AddRawLink(name string, l *format.Link) error { n.encoded = nil - n.links = append(n.links, &ipld.Link{ + n.links = append(n.links, &format.Link{ Name: name, Size: l.Size, Cid: l.Cid, @@ -147,10 +153,10 @@ func (n *ProtoNode) RemoveNodeLink(name string) error { } // GetNodeLink returns a copy of the link with the given name. -func (n *ProtoNode) GetNodeLink(name string) (*ipld.Link, error) { +func (n *ProtoNode) GetNodeLink(name string) (*format.Link, error) { for _, l := range n.links { if l.Name == name { - return &ipld.Link{ + return &format.Link{ Name: l.Name, Size: l.Size, Cid: l.Cid, @@ -161,7 +167,7 @@ func (n *ProtoNode) GetNodeLink(name string) (*ipld.Link, error) { } // GetLinkedProtoNode returns a copy of the ProtoNode with the given name. -func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds ipld.DAGService, name string) (*ProtoNode, error) { +func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds format.DAGService, name string) (*ProtoNode, error) { nd, err := n.GetLinkedNode(ctx, ds, name) if err != nil { return nil, err @@ -176,7 +182,7 @@ func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds ipld.DAGService, } // GetLinkedNode returns a copy of the IPLD Node with the given name. -func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds ipld.DAGService, name string) (ipld.Node, error) { +func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds format.DAGService, name string) (format.Node, error) { lnk, err := n.GetNodeLink(name) if err != nil { return nil, err @@ -187,7 +193,7 @@ func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds ipld.DAGService, name // Copy returns a copy of the node. // NOTE: Does not make copies of Node objects in the links. -func (n *ProtoNode) Copy() ipld.Node { +func (n *ProtoNode) Copy() format.Node { nnode := new(ProtoNode) if len(n.data) > 0 { nnode.data = make([]byte, len(n.data)) @@ -195,7 +201,7 @@ func (n *ProtoNode) Copy() ipld.Node { } if len(n.links) > 0 { - nnode.links = make([]*ipld.Link, len(n.links)) + nnode.links = make([]*format.Link, len(n.links)) copy(nnode.links, n.links) } @@ -204,7 +210,6 @@ func (n *ProtoNode) Copy() ipld.Node { return nnode } -// RawData returns the protobuf-encoded version of the node. func (n *ProtoNode) RawData() []byte { out, _ := n.EncodeProtobuf(false) return out @@ -247,7 +252,7 @@ func (n *ProtoNode) Size() (uint64, error) { } // Stat returns statistics on the node. -func (n *ProtoNode) Stat() (*ipld.NodeStat, error) { +func (n *ProtoNode) Stat() (*format.NodeStat, error) { enc, err := n.EncodeProtobuf(false) if err != nil { return nil, err @@ -258,7 +263,7 @@ func (n *ProtoNode) Stat() (*ipld.NodeStat, error) { return nil, err } - return &ipld.NodeStat{ + return &format.NodeStat{ Hash: n.Cid().String(), NumLinks: len(n.links), BlockSize: len(enc), @@ -278,8 +283,8 @@ func (n *ProtoNode) Loggable() map[string]interface{} { // UnmarshalJSON reads the node fields from a JSON-encoded byte slice. func (n *ProtoNode) UnmarshalJSON(b []byte) error { s := struct { - Data []byte `json:"data"` - Links []*ipld.Link `json:"links"` + Data []byte `json:"data"` + Links []*format.Link `json:"links"` }{} err := json.Unmarshal(b, &s) @@ -338,12 +343,12 @@ func (n *ProtoNode) Multihash() mh.Multihash { } // Links returns the node links. -func (n *ProtoNode) Links() []*ipld.Link { +func (n *ProtoNode) Links() []*format.Link { return n.links } // SetLinks replaces the node links with the given ones. -func (n *ProtoNode) SetLinks(links []*ipld.Link) { +func (n *ProtoNode) SetLinks(links []*format.Link) { n.links = links } @@ -355,7 +360,7 @@ func (n *ProtoNode) Resolve(path []string) (interface{}, []string, error) { // ResolveLink consumes the first element of the path and obtains the link // corresponding to it from the node. It returns the link // and the path without the consumed element. -func (n *ProtoNode) ResolveLink(path []string) (*ipld.Link, []string, error) { +func (n *ProtoNode) ResolveLink(path []string) (*format.Link, []string, error) { if len(path) == 0 { return nil, nil, fmt.Errorf("end of path, no more links to resolve") } @@ -382,3 +387,17 @@ func (n *ProtoNode) Tree(p string, depth int) []string { } return out } + +func ProtoNodeConverter(b blocks.Block, nd ipld.Node) (legacy.UniversalNode, error) { + pbNode, ok := nd.(dagpb.PBNode) + if !ok { + return nil, ErrNotProtobuf + } + encoded := &immutableProtoNode{b.RawData(), pbNode} + pn := fromImmutableNode(encoded) + pn.cached = b.Cid() + pn.builder = b.Cid().Prefix() + return pn, nil +} + +var _ legacy.UniversalNode = &ProtoNode{} diff --git a/ipld/merkledag/prime.go b/ipld/merkledag/prime.go new file mode 100644 index 000000000..6269bed58 --- /dev/null +++ b/ipld/merkledag/prime.go @@ -0,0 +1,198 @@ +package merkledag + +import ( + "github.com/ipld/go-ipld-prime" + dagpb "github.com/ipld/go-ipld-prime-proto" +) + +// Kind returns a value from the Kind enum describing what the +// essential serializable kind of this node is (map, list, integer, etc). +// Most other handling of a node requires first switching upon the kind. +func (n *ProtoNode) Kind() ipld.Kind { + _, _ = n.EncodeProtobuf(false) + return n.encoded.Kind() +} + +// LookupByString looks up a child object in this node and returns it. +// The returned Node may be any of the Kind: +// a primitive (string, int64, etc), a map, a list, or a link. +// +// If the Kind of this Node is not Kind_Map, a nil node and an error +// will be returned. +// +// If the key does not exist, a nil node and an error will be returned. +func (n *ProtoNode) LookupByString(key string) (ipld.Node, error) { + _, err := n.EncodeProtobuf(false) + if err != nil { + return nil, err + } + return n.encoded.LookupByString(key) +} + +// LookupByNode is the equivalent of LookupByString, but takes a reified Node +// as a parameter instead of a plain string. +// This mechanism is useful if working with typed maps (if the key types +// have constraints, and you already have a reified `schema.TypedNode` value, +// using that value can save parsing and validation costs); +// and may simply be convenient if you already have a Node value in hand. +// +// (When writing generic functions over Node, a good rule of thumb is: +// when handling a map, check for `schema.TypedNode`, and in this case prefer +// the LookupByNode(Node) method; otherwise, favor LookupByString; typically +// implementations will have their fastest paths thusly.) +func (n *ProtoNode) LookupByNode(key ipld.Node) (ipld.Node, error) { + _, err := n.EncodeProtobuf(false) + if err != nil { + return nil, err + } + return n.encoded.LookupByNode(key) +} + +// LookupByIndex is the equivalent of LookupByString but for indexing into a list. +// As with LookupByString, the returned Node may be any of the Kind: +// a primitive (string, int64, etc), a map, a list, or a link. +// +// If the Kind of this Node is not Kind_List, a nil node and an error +// will be returned. +// +// If idx is out of range, a nil node and an error will be returned. +func (n *ProtoNode) LookupByIndex(idx int64) (ipld.Node, error) { + _, err := n.EncodeProtobuf(false) + if err != nil { + return nil, err + } + return n.encoded.LookupByIndex(idx) +} + +// LookupBySegment is will act as either LookupByString or LookupByIndex, +// whichever is contextually appropriate. +// +// Using LookupBySegment may imply an "atoi" conversion if used on a list node, +// or an "itoa" conversion if used on a map node. If an "itoa" conversion +// takes place, it may error, and this method may return that error. +func (n *ProtoNode) LookupBySegment(seg ipld.PathSegment) (ipld.Node, error) { + _, err := n.EncodeProtobuf(false) + if err != nil { + return nil, err + } + return n.encoded.LookupBySegment(seg) +} + +// Note that when using codegenerated types, there may be a fifth variant +// of lookup method on maps: `Get($GeneratedTypeKey) $GeneratedTypeValue`! +// MapIterator returns an iterator which yields key-value pairs +// traversing the node. +// If the node kind is anything other than a map, nil will be returned. +// +// The iterator will yield every entry in the map; that is, it +// can be expected that itr.Next will be called node.Length times +// before itr.Done becomes true. +func (n *ProtoNode) MapIterator() ipld.MapIterator { + _, _ = n.EncodeProtobuf(false) + return n.encoded.MapIterator() +} + +// ListIterator returns an iterator which yields key-value pairs +// traversing the node. +// If the node kind is anything other than a list, nil will be returned. +// +// The iterator will yield every entry in the list; that is, it +// can be expected that itr.Next will be called node.Length times +// before itr.Done becomes true. +func (n *ProtoNode) ListIterator() ipld.ListIterator { + _, _ = n.EncodeProtobuf(false) + return n.encoded.ListIterator() +} + +// Length returns the length of a list, or the number of entries in a map, +// or -1 if the node is not of list nor map kind. +func (n *ProtoNode) Length() int64 { + _, _ = n.EncodeProtobuf(false) + return n.encoded.Length() +} + +// Absent nodes are returned when traversing a struct field that is +// defined by a schema but unset in the data. (Absent nodes are not +// possible otherwise; you'll only see them from `schema.TypedNode`.) +// The absent flag is necessary so iterating over structs can +// unambiguously make the distinction between values that are +// present-and-null versus values that are absent. +// +// Absent nodes respond to `Kind()` as `ipld.Kind_Null`, +// for lack of any better descriptive value; you should therefore +// always check IsAbsent rather than just a switch on kind +// when it may be important to handle absent values distinctly. +func (n *ProtoNode) IsAbsent() bool { + _, _ = n.EncodeProtobuf(false) + return n.encoded.IsAbsent() +} + +func (n *ProtoNode) IsNull() bool { + _, _ = n.EncodeProtobuf(false) + return n.encoded.IsNull() +} + +func (n *ProtoNode) AsBool() (bool, error) { + _, err := n.EncodeProtobuf(false) + if err != nil { + return false, err + } + return n.encoded.AsBool() +} + +func (n *ProtoNode) AsInt() (int64, error) { + _, err := n.EncodeProtobuf(false) + if err != nil { + return 0, err + } + return n.encoded.AsInt() +} + +func (n *ProtoNode) AsFloat() (float64, error) { + _, err := n.EncodeProtobuf(false) + if err != nil { + return 0, err + } + return n.encoded.AsFloat() +} + +func (n *ProtoNode) AsString() (string, error) { + _, err := n.EncodeProtobuf(false) + if err != nil { + return "", err + } + return n.encoded.AsString() +} + +func (n *ProtoNode) AsBytes() ([]byte, error) { + _, err := n.EncodeProtobuf(false) + if err != nil { + return nil, err + } + return n.encoded.AsBytes() +} + +func (n *ProtoNode) AsLink() (ipld.Link, error) { + _, err := n.EncodeProtobuf(false) + if err != nil { + return nil, err + } + return n.encoded.AsLink() +} + +// Prototype returns a NodePrototype which can describe some properties of this node's implementation, +// and also be used to get a NodeBuilder, +// which can be use to create new nodes with the same implementation as this one. +// +// For typed nodes, the NodePrototype will also implement schema.Type. +// +// For Advanced Data Layouts, the NodePrototype will encapsulate any additional +// parameters and configuration of the ADL, and will also (usually) +// implement NodePrototypeSupportingAmend. +// +// Calling this method should not cause an allocation. +func (n *ProtoNode) Prototype() ipld.NodePrototype { + return dagpb.Type.PBNode +} + +var _ ipld.Node = &ProtoNode{} diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index a0adb4a1d..719061c75 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -1,39 +1,55 @@ package merkledag import ( + "bytes" "encoding/json" "fmt" - "github.com/ipfs/go-block-format" - cid "github.com/ipfs/go-cid" + blocks "github.com/ipfs/go-block-format" u "github.com/ipfs/go-ipfs-util" - ipld "github.com/ipfs/go-ipld-format" + legacy "github.com/ipfs/go-ipld-legacy" + ipld "github.com/ipld/go-ipld-prime" + dagpb "github.com/ipld/go-ipld-prime-proto" + + cid "github.com/ipfs/go-cid" + format "github.com/ipfs/go-ipld-format" ) // RawNode represents a node which only contains data. type RawNode struct { blocks.Block + dagpb.RawNode } +var _ legacy.UniversalNode = &RawNode{} + // NewRawNode creates a RawNode using the default sha2-256 hash function. func NewRawNode(data []byte) *RawNode { h := u.Hash(data) c := cid.NewCidV1(cid.Raw, h) blk, _ := blocks.NewBlockWithCid(data, c) - - return &RawNode{blk} + nb := dagpb.Type.RawNode.NewBuilder() + _ = dagpb.RawDecoder(nb, bytes.NewBuffer(blk.RawData())) + nd := nb.Build() + return &RawNode{blk, nd.(dagpb.RawNode)} } // DecodeRawBlock is a block decoder for raw IPLD nodes conforming to `node.DecodeBlockFunc`. -func DecodeRawBlock(block blocks.Block) (ipld.Node, error) { +func DecodeRawBlock(block blocks.Block) (format.Node, error) { if block.Cid().Type() != cid.Raw { return nil, fmt.Errorf("raw nodes cannot be decoded from non-raw blocks: %d", block.Cid().Type()) } + nb := dagpb.Type.RawNode.NewBuilder() + err := dagpb.RawDecoder(nb, bytes.NewBuffer(block.RawData())) + if err != nil { + return nil, err + } + nd := nb.Build() // Once you "share" a block, it should be immutable. Therefore, we can just use this block as-is. - return &RawNode{block}, nil + return &RawNode{block, nd.(dagpb.RawNode)}, nil } -var _ ipld.DecodeBlockFunc = DecodeRawBlock +var _ format.DecodeBlockFunc = DecodeRawBlock // NewRawNodeWPrefix creates a RawNode using the provided cid builder func NewRawNodeWPrefix(data []byte, builder cid.Builder) (*RawNode, error) { @@ -46,16 +62,23 @@ func NewRawNodeWPrefix(data []byte, builder cid.Builder) (*RawNode, error) { if err != nil { return nil, err } - return &RawNode{blk}, nil + nb := dagpb.Type.RawNode.NewBuilder() + err = dagpb.RawDecoder(nb, bytes.NewBuffer(data)) + if err != nil { + return nil, err + } + nd := nb.Build() + // Once you "share" a block, it should be immutable. Therefore, we can just use this block as-is. + return &RawNode{blk, nd.(dagpb.RawNode)}, nil } // Links returns nil. -func (rn *RawNode) Links() []*ipld.Link { +func (rn *RawNode) Links() []*format.Link { return nil } // ResolveLink returns an error. -func (rn *RawNode) ResolveLink(path []string) (*ipld.Link, []string, error) { +func (rn *RawNode) ResolveLink(path []string) (*format.Link, []string, error) { return nil, nil, ErrLinkNotFound } @@ -69,8 +92,8 @@ func (rn *RawNode) Tree(p string, depth int) []string { return nil } -// Copy performs a deep copy of this node and returns it as an ipld.Node -func (rn *RawNode) Copy() ipld.Node { +// Copy performs a deep copy of this node and returns it as an format.Node +func (rn *RawNode) Copy() format.Node { copybuf := make([]byte, len(rn.RawData())) copy(copybuf, rn.RawData()) nblk, err := blocks.NewBlockWithCid(rn.RawData(), rn.Cid()) @@ -78,8 +101,11 @@ func (rn *RawNode) Copy() ipld.Node { // programmer error panic("failure attempting to clone raw block: " + err.Error()) } - - return &RawNode{nblk} + nb := dagpb.Type.RawNode.NewBuilder() + _ = dagpb.RawDecoder(nb, bytes.NewBuffer(nblk.RawData())) + nd := nb.Build() + // Once you "share" a block, it should be immutable. Therefore, we can just use this block as-is. + return &RawNode{nblk, nd.(dagpb.RawNode)} } // Size returns the size of this node @@ -88,8 +114,8 @@ func (rn *RawNode) Size() (uint64, error) { } // Stat returns some Stats about this node. -func (rn *RawNode) Stat() (*ipld.NodeStat, error) { - return &ipld.NodeStat{ +func (rn *RawNode) Stat() (*format.NodeStat, error) { + return &format.NodeStat{ CumulativeSize: len(rn.RawData()), DataSize: len(rn.RawData()), }, nil @@ -100,4 +126,12 @@ func (rn *RawNode) MarshalJSON() ([]byte, error) { return json.Marshal(string(rn.RawData())) } -var _ ipld.Node = (*RawNode)(nil) +func RawNodeConverter(b blocks.Block, nd ipld.Node) (legacy.UniversalNode, error) { + rn, ok := nd.(dagpb.RawNode) + if !ok { + return nil, ErrNotProtobuf + } + return &RawNode{b, rn}, nil +} + +var _ legacy.UniversalNode = (*RawNode)(nil) From 2a7b0a4ad7690d2d4477200f8166e5e008f32834 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 18 Feb 2021 13:14:36 -0800 Subject: [PATCH 3523/3817] feat(coding): switch to qp for node building This commit was moved from ipfs/go-merkledag@a289dd40cdeaa0eebc8673efae1a0cdc74969699 --- ipld/merkledag/coding.go | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index fc155df80..6fea9a8a2 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -10,8 +10,9 @@ import ( cid "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" pb "github.com/ipfs/go-merkledag/pb" + ipld "github.com/ipld/go-ipld-prime" dagpb "github.com/ipld/go-ipld-prime-proto" - "github.com/ipld/go-ipld-prime/fluent" + "github.com/ipld/go-ipld-prime/fluent/qp" cidlink "github.com/ipld/go-ipld-prime/linking/cid" ) @@ -54,32 +55,27 @@ func fromImmutableNode(encoded *immutableProtoNode) *ProtoNode { return n } func (n *ProtoNode) marshalImmutable() (*immutableProtoNode, error) { - nb := dagpb.Type.PBNode.NewBuilder() - err := fluent.Recover(func() { - fb := fluent.WrapAssembler(nb) - fb.CreateMap(-1, func(fmb fluent.MapAssembler) { - fmb.AssembleEntry("Links").CreateList(int64(len(n.links)), func(flb fluent.ListAssembler) { - for _, link := range n.links { - flb.AssembleValue().CreateMap(-1, func(fmb fluent.MapAssembler) { - if link.Cid.Defined() { - hash, err := cid.Cast(link.Cid.Bytes()) - if err != nil { - panic(fluent.Error{Err: fmt.Errorf("unmarshal failed. %v", err)}) - } - fmb.AssembleEntry("Hash").AssignLink(cidlink.Link{Cid: hash}) + nd, err := qp.BuildMap(dagpb.Type.PBNode, 2, func(ma ipld.MapAssembler) { + qp.MapEntry(ma, "Links", qp.List(int64(len(n.links)), func(la ipld.ListAssembler) { + for _, link := range n.links { + qp.ListEntry(la, qp.Map(-1, func(ma ipld.MapAssembler) { + if link.Cid.Defined() { + hash, err := cid.Cast(link.Cid.Bytes()) + if err != nil { + panic(fmt.Errorf("unmarshal failed. %v", err)) } - fmb.AssembleEntry("Name").AssignString(link.Name) - fmb.AssembleEntry("Tsize").AssignInt(int64(link.Size)) - }) - } - }) - fmb.AssembleEntry("Data").AssignBytes(n.data) - }) + qp.MapEntry(ma, "Hash", qp.Link(cidlink.Link{Cid: hash})) + } + qp.MapEntry(ma, "Name", qp.String(link.Name)) + qp.MapEntry(ma, "Tsize", qp.Int(int64(link.Size))) + })) + } + })) + qp.MapEntry(ma, "Data", qp.Bytes(n.data)) }) if err != nil { return nil, err } - nd := nb.Build() newData := new(bytes.Buffer) err = dagpb.PBEncoder(nd, newData) if err != nil { From 21783e318e019ec81bd6dc001c6a6b71f58d79fc Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 18 Feb 2021 14:18:18 -0800 Subject: [PATCH 3524/3817] fix(coding): fix decode funcs Fix functionality for decode function -- was using incorrect decoder and not handling maybe cases This commit was moved from ipfs/go-merkledag@485193b229d85a710136df90f88195c47c6da260 --- ipld/merkledag/coding.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 6fea9a8a2..b9cc69829 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -28,7 +28,7 @@ const _ = pb.DoNotUpgradeFileEverItWillChangeYourHashes // The conversion uses an intermediate PBNode. func unmarshal(encodedBytes []byte) (*ProtoNode, error) { nb := dagpb.Type.PBNode.NewBuilder() - err := dagpb.RawDecoder(nb, bytes.NewBuffer(encodedBytes)) + err := dagpb.PBDecoder(nb, bytes.NewBuffer(encodedBytes)) if err != nil { return nil, err } @@ -44,10 +44,22 @@ func fromImmutableNode(encoded *immutableProtoNode) *ProtoNode { iter := n.encoded.PBNode.Links.Iterator() for !iter.Done() { _, next := iter.Next() + name := "" + if next.FieldName().Exists() { + name = next.FieldName().Must().String() + } + c := cid.Undef + if next.FieldHash().Exists() { + c = next.FieldHash().Must().Link().(cidlink.Link).Cid + } + size := uint64(0) + if next.FieldTsize().Exists() { + size = uint64(next.FieldTsize().Must().Int()) + } link := &format.Link{ - Name: next.FieldName().Must().String(), - Size: uint64(next.FieldTsize().Must().Int()), - Cid: next.FieldHash().Must().Link().(cidlink.Link).Cid, + Name: name, + Size: size, + Cid: c, } links = append(links, link) } From 132d0f7838634ea5b65b303a8f85141d4db17866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Thu, 4 Mar 2021 15:37:05 +0000 Subject: [PATCH 3525/3817] switch from go-ipld-prime-proto to go-codec-dagpb Points worth noting: 1) go-ipld-prime-proto contains a raw codec, but go-codec-dagpb does not. We're adding one such codec to go-ipld-prime, so for now, just add one here to keep the tests passing. 2) dagpb uses a slightly different schema. In particular, the Data field is optional, but a link's Hash is not. 3) As per the dag-pb spec, and following the above, links must contain hashes. This required updating a number of tests. Note that TestStableCID gets its CID changed because of this. go-ipld-prime-proto arrives at the same new CID with the new node. dagutils.TestInsertNode still fails, because it arrives at a different CID hash, but it's not clear why just yet. This commit was moved from ipfs/go-merkledag@dcba11bf4ba8018c801006280b7acdacd5790f13 --- ipld/merkledag/coding.go | 14 ++++---- ipld/merkledag/merkledag.go | 10 ++++-- ipld/merkledag/node.go | 9 +++-- ipld/merkledag/node_test.go | 42 +++++++++++++--------- ipld/merkledag/prime.go | 2 +- ipld/merkledag/raw.go | 72 ++++++++++++++++++++++--------------- 6 files changed, 92 insertions(+), 57 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index b9cc69829..04c435676 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -10,8 +10,8 @@ import ( cid "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" pb "github.com/ipfs/go-merkledag/pb" + dagpb "github.com/ipld/go-codec-dagpb" ipld "github.com/ipld/go-ipld-prime" - dagpb "github.com/ipld/go-ipld-prime-proto" "github.com/ipld/go-ipld-prime/fluent/qp" cidlink "github.com/ipld/go-ipld-prime/linking/cid" ) @@ -28,7 +28,7 @@ const _ = pb.DoNotUpgradeFileEverItWillChangeYourHashes // The conversion uses an intermediate PBNode. func unmarshal(encodedBytes []byte) (*ProtoNode, error) { nb := dagpb.Type.PBNode.NewBuilder() - err := dagpb.PBDecoder(nb, bytes.NewBuffer(encodedBytes)) + err := dagpb.Decoder(nb, bytes.NewBuffer(encodedBytes)) if err != nil { return nil, err } @@ -39,7 +39,9 @@ func unmarshal(encodedBytes []byte) (*ProtoNode, error) { func fromImmutableNode(encoded *immutableProtoNode) *ProtoNode { n := new(ProtoNode) n.encoded = encoded - n.data = n.encoded.PBNode.Data.Bytes() + if n.encoded.PBNode.Data.Exists() { + n.data = n.encoded.PBNode.Data.Must().Bytes() + } links := make([]*format.Link, 0, n.encoded.PBNode.Links.Length()) iter := n.encoded.PBNode.Links.Iterator() for !iter.Done() { @@ -49,9 +51,7 @@ func fromImmutableNode(encoded *immutableProtoNode) *ProtoNode { name = next.FieldName().Must().String() } c := cid.Undef - if next.FieldHash().Exists() { - c = next.FieldHash().Must().Link().(cidlink.Link).Cid - } + c = next.FieldHash().Link().(cidlink.Link).Cid size := uint64(0) if next.FieldTsize().Exists() { size = uint64(next.FieldTsize().Must().Int()) @@ -89,7 +89,7 @@ func (n *ProtoNode) marshalImmutable() (*immutableProtoNode, error) { return nil, err } newData := new(bytes.Buffer) - err = dagpb.PBEncoder(nd, newData) + err = dagpb.Encoder(nd, newData) if err != nil { return nil, err } diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 4a1e12397..84cd594e3 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -12,7 +12,9 @@ import ( ipldcbor "github.com/ipfs/go-ipld-cbor" format "github.com/ipfs/go-ipld-format" legacy "github.com/ipfs/go-ipld-legacy" - dagpb "github.com/ipld/go-ipld-prime-proto" + dagpb "github.com/ipld/go-codec-dagpb" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + basicnode "github.com/ipld/go-ipld-prime/node/basic" ) // TODO: We should move these registrations elsewhere. Really, most of the IPLD @@ -22,8 +24,12 @@ func init() { format.Register(cid.DagProtobuf, DecodeProtobufBlock) format.Register(cid.Raw, DecodeRawBlock) format.Register(cid.DagCBOR, ipldcbor.DecodeBlock) + legacy.RegisterCodec(cid.DagProtobuf, dagpb.Type.PBNode, ProtoNodeConverter) - legacy.RegisterCodec(cid.Raw, dagpb.Type.RawNode, RawNodeConverter) + legacy.RegisterCodec(cid.Raw, basicnode.Prototype.Bytes, RawNodeConverter) + + cidlink.RegisterMulticodecDecoder(cid.Raw, RawDecoder) + cidlink.RegisterMulticodecEncoder(cid.Raw, RawEncoder) } // contextKey is a type to use as value for the ProgressTracker contexts. diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index c45f8b9a1..aecc40e95 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -9,8 +9,8 @@ import ( cid "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" legacy "github.com/ipfs/go-ipld-legacy" + dagpb "github.com/ipld/go-codec-dagpb" ipld "github.com/ipld/go-ipld-prime" - dagpb "github.com/ipld/go-ipld-prime-proto" mh "github.com/multiformats/go-multihash" ) @@ -211,7 +211,10 @@ func (n *ProtoNode) Copy() format.Node { } func (n *ProtoNode) RawData() []byte { - out, _ := n.EncodeProtobuf(false) + out, err := n.EncodeProtobuf(false) + if err != nil { + panic(err) + } return out } @@ -314,7 +317,7 @@ func (n *ProtoNode) Cid() cid.Cid { return n.cached } - c, err := n.builder.Sum(n.RawData()) + c, err := n.CidBuilder().Sum(n.RawData()) if err != nil { // programmer error err = fmt.Errorf("invalid CID of length %d: %x: %v", len(n.RawData()), n.RawData(), err) diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index 2ae75e774..e52fa0d1b 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -12,15 +12,25 @@ import ( ipld "github.com/ipfs/go-ipld-format" ) +var sampleCid cid.Cid + +func init() { + var err error + sampleCid, err = cid.Cast([]byte{1, 85, 0, 5, 0, 1, 2, 3, 4}) + if err != nil { + panic(err) + } +} + func TestStableCID(t *testing.T) { nd := &ProtoNode{} nd.SetData([]byte("foobar")) nd.SetLinks([]*ipld.Link{ - {Name: "a"}, - {Name: "b"}, - {Name: "c"}, + {Name: "a", Cid: sampleCid}, + {Name: "b", Cid: sampleCid}, + {Name: "c", Cid: sampleCid}, }) - expected, err := cid.Decode("QmSN3WED2xPLbYvBbfvew2ZLtui8EbFYYcbfkpKH5jwG9C") + expected, err := cid.Decode("QmciCHWD9Q47VPX6naY3XsPZGnqVqbedAniGCcaHjBaCri") if err != nil { t.Fatal(err) } @@ -32,12 +42,12 @@ func TestStableCID(t *testing.T) { func TestRemoveLink(t *testing.T) { nd := &ProtoNode{} nd.SetLinks([]*ipld.Link{ - {Name: "a"}, - {Name: "b"}, - {Name: "a"}, - {Name: "a"}, - {Name: "c"}, - {Name: "a"}, + {Name: "a", Cid: sampleCid}, + {Name: "b", Cid: sampleCid}, + {Name: "a", Cid: sampleCid}, + {Name: "a", Cid: sampleCid}, + {Name: "c", Cid: sampleCid}, + {Name: "a", Cid: sampleCid}, }) err := nd.RemoveNodeLink("a") @@ -138,9 +148,9 @@ func TestFindLink(t *testing.T) { func TestNodeCopy(t *testing.T) { nd := &ProtoNode{} nd.SetLinks([]*ipld.Link{ - {Name: "a"}, - {Name: "c"}, - {Name: "b"}, + {Name: "a", Cid: sampleCid}, + {Name: "c", Cid: sampleCid}, + {Name: "b", Cid: sampleCid}, }) nd.SetData([]byte("testing")) @@ -156,9 +166,9 @@ func TestNodeCopy(t *testing.T) { func TestJsonRoundtrip(t *testing.T) { nd := new(ProtoNode) nd.SetLinks([]*ipld.Link{ - {Name: "a"}, - {Name: "c"}, - {Name: "b"}, + {Name: "a", Cid: sampleCid}, + {Name: "c", Cid: sampleCid}, + {Name: "b", Cid: sampleCid}, }) nd.SetData([]byte("testing")) diff --git a/ipld/merkledag/prime.go b/ipld/merkledag/prime.go index 6269bed58..fa9e00dd7 100644 --- a/ipld/merkledag/prime.go +++ b/ipld/merkledag/prime.go @@ -2,7 +2,7 @@ package merkledag import ( "github.com/ipld/go-ipld-prime" - dagpb "github.com/ipld/go-ipld-prime-proto" + dagpb "github.com/ipld/go-codec-dagpb" ) // Kind returns a value from the Kind enum describing what the diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 719061c75..7e99174f3 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -1,15 +1,16 @@ package merkledag import ( - "bytes" "encoding/json" "fmt" + "io" + "io/ioutil" blocks "github.com/ipfs/go-block-format" u "github.com/ipfs/go-ipfs-util" legacy "github.com/ipfs/go-ipld-legacy" ipld "github.com/ipld/go-ipld-prime" - dagpb "github.com/ipld/go-ipld-prime-proto" + basicnode "github.com/ipld/go-ipld-prime/node/basic" cid "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" @@ -18,7 +19,41 @@ import ( // RawNode represents a node which only contains data. type RawNode struct { blocks.Block - dagpb.RawNode + + // Always a node/basic Bytes. + // We can't reference a specific type, as it's not exposed there. + // If we find that the interface indirection really matters, + // then we could possibly use dagpb.Bytes. + ipld.Node +} + +type byteAccesor interface { + Bytes() []byte +} + +// TODO(mvdan): replace with go-ipld-prime's raw codec + +func RawDecoder(am ipld.NodeAssembler, r io.Reader) error { + var data []byte + if buf, ok := r.(byteAccesor); ok { + data = buf.Bytes() + } else { + var err error + data, err = ioutil.ReadAll(r) + if err != nil { + return fmt.Errorf("could not decode raw node: %v", err) + } + } + return am.AssignBytes(data) +} + +func RawEncoder(node ipld.Node, w io.Writer) error { + data, err := node.AsBytes() + if err != nil { + return err + } + _, err = w.Write(data) + return err } var _ legacy.UniversalNode = &RawNode{} @@ -28,10 +63,7 @@ func NewRawNode(data []byte) *RawNode { h := u.Hash(data) c := cid.NewCidV1(cid.Raw, h) blk, _ := blocks.NewBlockWithCid(data, c) - nb := dagpb.Type.RawNode.NewBuilder() - _ = dagpb.RawDecoder(nb, bytes.NewBuffer(blk.RawData())) - nd := nb.Build() - return &RawNode{blk, nd.(dagpb.RawNode)} + return &RawNode{blk, basicnode.NewBytes(data)} } // DecodeRawBlock is a block decoder for raw IPLD nodes conforming to `node.DecodeBlockFunc`. @@ -39,14 +71,8 @@ func DecodeRawBlock(block blocks.Block) (format.Node, error) { if block.Cid().Type() != cid.Raw { return nil, fmt.Errorf("raw nodes cannot be decoded from non-raw blocks: %d", block.Cid().Type()) } - nb := dagpb.Type.RawNode.NewBuilder() - err := dagpb.RawDecoder(nb, bytes.NewBuffer(block.RawData())) - if err != nil { - return nil, err - } - nd := nb.Build() // Once you "share" a block, it should be immutable. Therefore, we can just use this block as-is. - return &RawNode{block, nd.(dagpb.RawNode)}, nil + return &RawNode{block, basicnode.NewBytes(block.RawData())}, nil } var _ format.DecodeBlockFunc = DecodeRawBlock @@ -62,14 +88,8 @@ func NewRawNodeWPrefix(data []byte, builder cid.Builder) (*RawNode, error) { if err != nil { return nil, err } - nb := dagpb.Type.RawNode.NewBuilder() - err = dagpb.RawDecoder(nb, bytes.NewBuffer(data)) - if err != nil { - return nil, err - } - nd := nb.Build() // Once you "share" a block, it should be immutable. Therefore, we can just use this block as-is. - return &RawNode{blk, nd.(dagpb.RawNode)}, nil + return &RawNode{blk, basicnode.NewBytes(data)}, nil } // Links returns nil. @@ -101,11 +121,8 @@ func (rn *RawNode) Copy() format.Node { // programmer error panic("failure attempting to clone raw block: " + err.Error()) } - nb := dagpb.Type.RawNode.NewBuilder() - _ = dagpb.RawDecoder(nb, bytes.NewBuffer(nblk.RawData())) - nd := nb.Build() // Once you "share" a block, it should be immutable. Therefore, we can just use this block as-is. - return &RawNode{nblk, nd.(dagpb.RawNode)} + return &RawNode{nblk, basicnode.NewBytes(nblk.RawData())} } // Size returns the size of this node @@ -127,11 +144,10 @@ func (rn *RawNode) MarshalJSON() ([]byte, error) { } func RawNodeConverter(b blocks.Block, nd ipld.Node) (legacy.UniversalNode, error) { - rn, ok := nd.(dagpb.RawNode) - if !ok { + if nd.Kind() != ipld.Kind_Bytes { return nil, ErrNotProtobuf } - return &RawNode{b, rn}, nil + return &RawNode{b, nd}, nil } var _ legacy.UniversalNode = (*RawNode)(nil) From 3daaf9faa72e00876ba271a204c21a72b09d22ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Mon, 8 Mar 2021 17:57:41 +0000 Subject: [PATCH 3526/3817] use codec/raw This commit was moved from ipfs/go-merkledag@6eb498e941187619314d7308e0d9c917e14b4cca --- ipld/merkledag/merkledag.go | 5 +---- ipld/merkledag/raw.go | 27 --------------------------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 84cd594e3..258d87519 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -13,7 +13,7 @@ import ( format "github.com/ipfs/go-ipld-format" legacy "github.com/ipfs/go-ipld-legacy" dagpb "github.com/ipld/go-codec-dagpb" - cidlink "github.com/ipld/go-ipld-prime/linking/cid" + _ "github.com/ipld/go-ipld-prime/codec/raw" basicnode "github.com/ipld/go-ipld-prime/node/basic" ) @@ -27,9 +27,6 @@ func init() { legacy.RegisterCodec(cid.DagProtobuf, dagpb.Type.PBNode, ProtoNodeConverter) legacy.RegisterCodec(cid.Raw, basicnode.Prototype.Bytes, RawNodeConverter) - - cidlink.RegisterMulticodecDecoder(cid.Raw, RawDecoder) - cidlink.RegisterMulticodecEncoder(cid.Raw, RawEncoder) } // contextKey is a type to use as value for the ProgressTracker contexts. diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 7e99174f3..54cdf8cd3 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -3,8 +3,6 @@ package merkledag import ( "encoding/json" "fmt" - "io" - "io/ioutil" blocks "github.com/ipfs/go-block-format" u "github.com/ipfs/go-ipfs-util" @@ -31,31 +29,6 @@ type byteAccesor interface { Bytes() []byte } -// TODO(mvdan): replace with go-ipld-prime's raw codec - -func RawDecoder(am ipld.NodeAssembler, r io.Reader) error { - var data []byte - if buf, ok := r.(byteAccesor); ok { - data = buf.Bytes() - } else { - var err error - data, err = ioutil.ReadAll(r) - if err != nil { - return fmt.Errorf("could not decode raw node: %v", err) - } - } - return am.AssignBytes(data) -} - -func RawEncoder(node ipld.Node, w io.Writer) error { - data, err := node.AsBytes() - if err != nil { - return err - } - _, err = w.Write(data) - return err -} - var _ legacy.UniversalNode = &RawNode{} // NewRawNode creates a RawNode using the default sha2-256 hash function. From 4c09489102a78dfe714ecea6ec830a830b1e335d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Tue, 9 Mar 2021 17:54:19 +0000 Subject: [PATCH 3527/3817] nil data shouldn't be encoded This commit was moved from ipfs/go-merkledag@d6ea3cabf5e5b44b9811f1eba8fa559977b80cbf --- ipld/merkledag/coding.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 04c435676..a18e925c4 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -83,7 +83,9 @@ func (n *ProtoNode) marshalImmutable() (*immutableProtoNode, error) { })) } })) - qp.MapEntry(ma, "Data", qp.Bytes(n.data)) + if n.data != nil { + qp.MapEntry(ma, "Data", qp.Bytes(n.data)) + } }) if err != nil { return nil, err From cd570b7fe7c0b32fa90298d24dde2e5127d2e677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Sat, 27 Mar 2021 16:04:29 +0000 Subject: [PATCH 3528/3817] add a PBNode roundtrip benchmark This commit was moved from ipfs/go-merkledag@53cb8e86d73a7ca7357b03cf4e549f306dae4fa0 --- ipld/merkledag/coding_test.go | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 ipld/merkledag/coding_test.go diff --git a/ipld/merkledag/coding_test.go b/ipld/merkledag/coding_test.go new file mode 100644 index 000000000..6c1171e9a --- /dev/null +++ b/ipld/merkledag/coding_test.go @@ -0,0 +1,53 @@ +package merkledag_test + +import ( + "bytes" + "fmt" + "testing" + + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" + "github.com/ipfs/go-merkledag" +) + +var benchInput []byte + +func init() { + someData := bytes.Repeat([]byte("some plaintext data\n"), 10) + someCid, _ := cid.Cast([]byte{1, 85, 0, 5, 0, 1, 2, 3, 4}) + + node := &merkledag.ProtoNode{} + node.SetData(someData) + for i := 0; i < 10; i++ { + node.AddRawLink(fmt.Sprintf("%d", i), &ipld.Link{ + Size: 10, + Cid: someCid, + }) + } + + enc, err := node.EncodeProtobuf(true) + if err != nil { + panic(err) + } + benchInput = enc + // println(len(benchInput)) +} + +func BenchmarkRoundtrip(b *testing.B) { + b.ReportAllocs() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + node, err := merkledag.DecodeProtobuf(benchInput) + if err != nil { + b.Fatal(err) + } + + enc, err := node.EncodeProtobuf(true) + if err != nil { + b.Fatal(err) + } + // println(len(benchInput), len(enc)) + _ = enc + } + }) +} From 7a3cea34a3f0590a7f507cd3a5197c1ca76450d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Sat, 27 Mar 2021 17:43:09 +0000 Subject: [PATCH 3529/3817] reduce allocs in fromImmutableNode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allocate all the underlying links at once. This means one alloc per call, instead of one alloc per link. name old time/op new time/op delta Roundtrip-8 5.34µs ± 1% 5.20µs ± 1% -2.61% (p=0.002 n=6+6) name old alloc/op new alloc/op delta Roundtrip-8 7.50kB ± 0% 7.44kB ± 0% -0.85% (p=0.002 n=6+6) name old allocs/op new allocs/op delta Roundtrip-8 148 ± 0% 139 ± 0% -6.08% (p=0.002 n=6+6) This commit was moved from ipfs/go-merkledag@d930d2d69533073a3380c0083e6b07ebc54d6a93 --- ipld/merkledag/coding.go | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index a18e925c4..3b24cf6c4 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -42,10 +42,11 @@ func fromImmutableNode(encoded *immutableProtoNode) *ProtoNode { if n.encoded.PBNode.Data.Exists() { n.data = n.encoded.PBNode.Data.Must().Bytes() } - links := make([]*format.Link, 0, n.encoded.PBNode.Links.Length()) - iter := n.encoded.PBNode.Links.Iterator() - for !iter.Done() { - _, next := iter.Next() + numLinks := n.encoded.PBNode.Links.Length() + n.links = make([]*format.Link, numLinks) + linkAllocs := make([]format.Link, numLinks) + for i := int64(0); i < numLinks; i++ { + next := n.encoded.PBNode.Links.Lookup(i) name := "" if next.FieldName().Exists() { name = next.FieldName().Must().String() @@ -56,21 +57,19 @@ func fromImmutableNode(encoded *immutableProtoNode) *ProtoNode { if next.FieldTsize().Exists() { size = uint64(next.FieldTsize().Must().Int()) } - link := &format.Link{ - Name: name, - Size: size, - Cid: c, - } - links = append(links, link) + link := &linkAllocs[i] + link.Name = name + link.Size = size + link.Cid = c + n.links[i] = link } - n.links = links return n } func (n *ProtoNode) marshalImmutable() (*immutableProtoNode, error) { nd, err := qp.BuildMap(dagpb.Type.PBNode, 2, func(ma ipld.MapAssembler) { qp.MapEntry(ma, "Links", qp.List(int64(len(n.links)), func(la ipld.ListAssembler) { for _, link := range n.links { - qp.ListEntry(la, qp.Map(-1, func(ma ipld.MapAssembler) { + qp.ListEntry(la, qp.Map(3, func(ma ipld.MapAssembler) { if link.Cid.Defined() { hash, err := cid.Cast(link.Cid.Bytes()) if err != nil { @@ -91,8 +90,7 @@ func (n *ProtoNode) marshalImmutable() (*immutableProtoNode, error) { return nil, err } newData := new(bytes.Buffer) - err = dagpb.Encoder(nd, newData) - if err != nil { + if err := dagpb.Encoder(nd, newData); err != nil { return nil, err } return &immutableProtoNode{newData.Bytes(), nd.(dagpb.PBNode)}, nil From cccb7e8566b561e0ab521a11190466bbda8e8ad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Sat, 27 Mar 2021 17:53:33 +0000 Subject: [PATCH 3530/3817] reduce overhead in marshalImmutable's node translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's unclear to me why we did the Bytes+Cast dance for each cid.Cid in the links list. Perhaps to do a copy, but that wouldn't make sense as dagpb only reads the CIDs when marshaling. We can use the same Cid value directly, which shaves off a significant amount of allocation overhead. name old time/op new time/op delta Roundtrip-8 5.20µs ± 1% 4.81µs ± 0% -7.53% (p=0.004 n=6+5) name old alloc/op new alloc/op delta Roundtrip-8 7.44kB ± 0% 7.14kB ± 0% -4.09% (p=0.002 n=6+6) name old allocs/op new allocs/op delta Roundtrip-8 139 ± 0% 119 ± 0% -14.39% (p=0.002 n=6+6) This commit was moved from ipfs/go-merkledag@46af2f22b9b2458a7226c69b9e101e58b55c1869 --- ipld/merkledag/coding.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 3b24cf6c4..5e690bac9 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -71,11 +71,7 @@ func (n *ProtoNode) marshalImmutable() (*immutableProtoNode, error) { for _, link := range n.links { qp.ListEntry(la, qp.Map(3, func(ma ipld.MapAssembler) { if link.Cid.Defined() { - hash, err := cid.Cast(link.Cid.Bytes()) - if err != nil { - panic(fmt.Errorf("unmarshal failed. %v", err)) - } - qp.MapEntry(ma, "Hash", qp.Link(cidlink.Link{Cid: hash})) + qp.MapEntry(ma, "Hash", qp.Link(cidlink.Link{Cid: link.Cid})) } qp.MapEntry(ma, "Name", qp.String(link.Name)) qp.MapEntry(ma, "Tsize", qp.Int(int64(link.Size))) From 7022a3dc6dad8561bf315540e172035e3f7351f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Sun, 28 Mar 2021 17:11:24 +0100 Subject: [PATCH 3531/3817] make use of dagpb's []byte APIs directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cutting out the bytes.Buffer middleman saves a non-trivial amount of work, it turns out. name old time/op new time/op delta Roundtrip-8 3.24µs ± 3% 4.07µs ± 0% +25.72% (p=0.002 n=6+6) name old alloc/op new alloc/op delta Roundtrip-8 4.94kB ± 0% 6.38kB ± 0% +29.13% (p=0.002 n=6+6) name old allocs/op new allocs/op delta Roundtrip-8 109 ± 0% 103 ± 0% -5.50% (p=0.002 n=6+6) This commit was moved from ipfs/go-merkledag@25506f2faa7523be60d7db274ed6292c5eeac62f --- ipld/merkledag/coding.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 5e690bac9..a9a1189eb 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -1,7 +1,6 @@ package merkledag import ( - "bytes" "fmt" "sort" "strings" @@ -28,8 +27,7 @@ const _ = pb.DoNotUpgradeFileEverItWillChangeYourHashes // The conversion uses an intermediate PBNode. func unmarshal(encodedBytes []byte) (*ProtoNode, error) { nb := dagpb.Type.PBNode.NewBuilder() - err := dagpb.Decoder(nb, bytes.NewBuffer(encodedBytes)) - if err != nil { + if err := dagpb.DecodeBytes(nb, encodedBytes); err != nil { return nil, err } nd := nb.Build() @@ -85,11 +83,16 @@ func (n *ProtoNode) marshalImmutable() (*immutableProtoNode, error) { if err != nil { return nil, err } - newData := new(bytes.Buffer) - if err := dagpb.Encoder(nd, newData); err != nil { + + // 1KiB can be allocated on the stack, and covers most small nodes + // without having to grow the buffer and cause allocations. + enc := make([]byte, 0, 1024) + + enc, err = dagpb.AppendEncode(enc, nd) + if err != nil { return nil, err } - return &immutableProtoNode{newData.Bytes(), nd.(dagpb.PBNode)}, nil + return &immutableProtoNode{enc, nd.(dagpb.PBNode)}, nil } // Marshal encodes a *Node instance into a new byte slice. From 6e613f9e5519941cef035b7159232d4d96ae92ce Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 5 Aug 2021 22:42:57 -0700 Subject: [PATCH 3532/3817] style(lint): fix lint errors This commit was moved from ipfs/go-merkledag@d863a07d3ef3849bb22a171af4c08d19025d6b45 --- ipld/merkledag/raw.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 54cdf8cd3..00fc2928c 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -25,10 +25,6 @@ type RawNode struct { ipld.Node } -type byteAccesor interface { - Bytes() []byte -} - var _ legacy.UniversalNode = &RawNode{} // NewRawNode creates a RawNode using the default sha2-256 hash function. From 6632e6bab049b72c7aa4104045f6a2c8cb56a472 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 5 Aug 2021 23:28:58 -0700 Subject: [PATCH 3533/3817] fix(merkledag): address PR comments This commit was moved from ipfs/go-merkledag@1c8d1181b4afc4ceff3bf100a647b19627af0aa7 --- ipld/merkledag/coding_test.go | 2 -- ipld/merkledag/node.go | 13 +++++++++++++ ipld/merkledag/prime.go | 10 +++++++++- ipld/merkledag/raw.go | 2 +- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/ipld/merkledag/coding_test.go b/ipld/merkledag/coding_test.go index 6c1171e9a..efc36661b 100644 --- a/ipld/merkledag/coding_test.go +++ b/ipld/merkledag/coding_test.go @@ -30,7 +30,6 @@ func init() { panic(err) } benchInput = enc - // println(len(benchInput)) } func BenchmarkRoundtrip(b *testing.B) { @@ -46,7 +45,6 @@ func BenchmarkRoundtrip(b *testing.B) { if err != nil { b.Fatal(err) } - // println(len(benchInput), len(enc)) _ = enc } }) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index aecc40e95..cafd9c39c 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -17,6 +17,7 @@ import ( // Common errors var ( ErrNotProtobuf = fmt.Errorf("expected protobuf dag node") + ErrNotRawNode = fmt.Errorf("expected raw bytes node") ErrLinkNotFound = fmt.Errorf("no link by that name") ) @@ -27,6 +28,18 @@ type immutableProtoNode struct { // ProtoNode represents a node in the IPFS Merkle DAG. // nodes have opaque data and a set of navigable links. +// ProtoNode is a go-ipld-legacy.UniversalNode, meaning it is both +// a go-ipld-prime node and a go-ipld-format node. +// ProtoNode maintains compatibility with it's original implementation +// as a go-ipld-format only node, which included some mutability, namely the +// the ability to add/remove links in place +// +// TODO: We should be able to eventually replace this implementation with +// * go-codec-dagpb for basic DagPB encode/decode to go-ipld-prime +// * go-unixfsnode ADLs for higher level DAGPB functionality +// For the time being however, go-unixfsnode is read only and +// this mutable protonode implementation is needed to support go-unixfs, +// the only library that implements both read and write for UnixFS v1. type ProtoNode struct { links []*format.Link data []byte diff --git a/ipld/merkledag/prime.go b/ipld/merkledag/prime.go index fa9e00dd7..5f9bbc691 100644 --- a/ipld/merkledag/prime.go +++ b/ipld/merkledag/prime.go @@ -1,10 +1,18 @@ package merkledag import ( - "github.com/ipld/go-ipld-prime" dagpb "github.com/ipld/go-codec-dagpb" + "github.com/ipld/go-ipld-prime" ) +// Protonode was originally implemented as a go-ipld-format node, and included +// functionality that does not fit well into the model for go-ipld-prime, namely +// the ability ot modify the node in place. + +// In order to support the go-ipld-prime interface, all of these prime methods +// serialize and rebuild the go-ipld-prime node as needed, so that it remains up +// to date with mutations made via the add/remove link methods + // Kind returns a value from the Kind enum describing what the // essential serializable kind of this node is (map, list, integer, etc). // Most other handling of a node requires first switching upon the kind. diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index 00fc2928c..f4e9e53a4 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -114,7 +114,7 @@ func (rn *RawNode) MarshalJSON() ([]byte, error) { func RawNodeConverter(b blocks.Block, nd ipld.Node) (legacy.UniversalNode, error) { if nd.Kind() != ipld.Kind_Bytes { - return nil, ErrNotProtobuf + return nil, ErrNotRawNode } return &RawNode{b, nd}, nil } From 7310da4b188ccb6f10f772e688a5aecbd44a4fde Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 5 Aug 2021 23:33:41 -0700 Subject: [PATCH 3534/3817] style(comments): make comment more accurate This commit was moved from ipfs/go-fetcher@e5100db9d7788d8a7a3d0243f0ef891b5e849d56 --- fetcher/impl/blockservice/fetcher.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fetcher/impl/blockservice/fetcher.go b/fetcher/impl/blockservice/fetcher.go index 0a0244d8b..b0ee9f417 100644 --- a/fetcher/impl/blockservice/fetcher.go +++ b/fetcher/impl/blockservice/fetcher.go @@ -104,7 +104,8 @@ func (f *fetcherSession) PrototypeFromLink(lnk ipld.Link) (ipld.NodePrototype, e return f.protoChooser(lnk, ipld.LinkContext{}) } -// DefaultPrototypeChooser supports DagPB nodes and choosing the prototype from the link. +// DefaultPrototypeChooser supports choosing the prototype from the link and falling +// back to a basicnode.Any builder var DefaultPrototypeChooser = func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { return tlnkNd.LinkTargetNodePrototype(), nil From 2585bdf4ab09067b5d98ceb7b537a00202d7efcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Fri, 6 Aug 2021 16:03:58 +0100 Subject: [PATCH 3535/3817] update LICENSE files to point to the new gateway This commit was moved from ipld/go-car@6c87996fda3e65ebab397c4278cfc64e815285fb --- ipld/car/LICENSE.md | 4 ++-- ipld/car/v2/LICENSE.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ipld/car/LICENSE.md b/ipld/car/LICENSE.md index 15601cba6..2fa16a153 100644 --- a/ipld/car/LICENSE.md +++ b/ipld/car/LICENSE.md @@ -2,10 +2,10 @@ The contents of this repository are Copyright (c) corresponding authors and contributors, licensed under the `Permissive License Stack` meaning either of: - Apache-2.0 Software License: https://www.apache.org/licenses/LICENSE-2.0 - ([...4tr2kfsq](https://gateway.ipfs.io/ipfs/bafkreiankqxazcae4onkp436wag2lj3ccso4nawxqkkfckd6cg4tr2kfsq)) + ([...4tr2kfsq](https://dweb.link/ipfs/bafkreiankqxazcae4onkp436wag2lj3ccso4nawxqkkfckd6cg4tr2kfsq)) - MIT Software License: https://opensource.org/licenses/MIT - ([...vljevcba](https://gateway.ipfs.io/ipfs/bafkreiepofszg4gfe2gzuhojmksgemsub2h4uy2gewdnr35kswvljevcba)) + ([...vljevcba](https://dweb.link/ipfs/bafkreiepofszg4gfe2gzuhojmksgemsub2h4uy2gewdnr35kswvljevcba)) You may not use the contents of this repository except in compliance with one of the listed Licenses. For an extended clarification of the diff --git a/ipld/car/v2/LICENSE.md b/ipld/car/v2/LICENSE.md index 15601cba6..2fa16a153 100644 --- a/ipld/car/v2/LICENSE.md +++ b/ipld/car/v2/LICENSE.md @@ -2,10 +2,10 @@ The contents of this repository are Copyright (c) corresponding authors and contributors, licensed under the `Permissive License Stack` meaning either of: - Apache-2.0 Software License: https://www.apache.org/licenses/LICENSE-2.0 - ([...4tr2kfsq](https://gateway.ipfs.io/ipfs/bafkreiankqxazcae4onkp436wag2lj3ccso4nawxqkkfckd6cg4tr2kfsq)) + ([...4tr2kfsq](https://dweb.link/ipfs/bafkreiankqxazcae4onkp436wag2lj3ccso4nawxqkkfckd6cg4tr2kfsq)) - MIT Software License: https://opensource.org/licenses/MIT - ([...vljevcba](https://gateway.ipfs.io/ipfs/bafkreiepofszg4gfe2gzuhojmksgemsub2h4uy2gewdnr35kswvljevcba)) + ([...vljevcba](https://dweb.link/ipfs/bafkreiepofszg4gfe2gzuhojmksgemsub2h4uy2gewdnr35kswvljevcba)) You may not use the contents of this repository except in compliance with one of the listed Licenses. For an extended clarification of the From dff188c46985a4db71239bab0fb49194115527f7 Mon Sep 17 00:00:00 2001 From: Will Scott Date: Mon, 9 Aug 2021 22:55:11 -0700 Subject: [PATCH 3536/3817] Add a WithReifier method to allow easier derivation of different pathing semantics This commit was moved from ipfs/go-fetcher@b24de2cb27b4008c031e886138a6f348ecaffd16 --- fetcher/impl/blockservice/fetcher.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fetcher/impl/blockservice/fetcher.go b/fetcher/impl/blockservice/fetcher.go index b0ee9f417..237f3f955 100644 --- a/fetcher/impl/blockservice/fetcher.go +++ b/fetcher/impl/blockservice/fetcher.go @@ -50,6 +50,16 @@ func (fc FetcherConfig) NewSession(ctx context.Context) fetcher.Fetcher { return &fetcherSession{linkSystem: ls, protoChooser: protoChooser} } +// WithReifier derives a different fetcher factory from the same source but +// with a chosen NodeReifier for pathing semantics. +func (fc FetcherConfig) WithReifier(nr ipld.NodeReifier) fetcher.Factory { + return FetcherConfig{ + blockService: fc.blockService, + NodeReifier: nr, + PrototypeChooser: fc.PrototypeChooser, + } +} + // interface check var _ fetcher.Factory = FetcherConfig{} From facff846be34777c82d6401a10b2a7ebed42e9a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Tue, 10 Aug 2021 10:57:48 +0100 Subject: [PATCH 3537/3817] v2/blockstore: add ReadWrite.Discard This allows closing a read-write blockstore without doing the extra work to finalize its header and index. Can be useful if an entire piece of work is cancelled, and also simplifies the tests. Also make ReadOnly error in a straightforward way if it is used after being closed. Before, this could lead to panics, as we'd attempt to read the CARv1 file when it's closed. Both mechanisms now use a "closed" boolean, which is consistent and simpler than checking a header field. Finally, add tests that ensure both ReadOnly and ReadWrite behave as intended once they have been closed. The tests also uncovered that AllKeysChan would not release the mutex if it encountered an error early on. Fix that, too. While at it, fix some now-obsolete references to panics on unsupported or after-close method calls. Fixes #205. This commit was moved from ipld/go-car@039ddc7c8d11415c11b3f84a202c076fae25f748 --- ipld/car/v2/blockstore/doc.go | 2 +- ipld/car/v2/blockstore/export_test.go | 11 ---- ipld/car/v2/blockstore/readonly.go | 80 ++++++++++++++++-------- ipld/car/v2/blockstore/readonly_test.go | 37 ++++++++++- ipld/car/v2/blockstore/readwrite.go | 72 ++++++++++----------- ipld/car/v2/blockstore/readwrite_test.go | 52 ++++++++++++++- 6 files changed, 175 insertions(+), 79 deletions(-) delete mode 100644 ipld/car/v2/blockstore/export_test.go diff --git a/ipld/car/v2/blockstore/doc.go b/ipld/car/v2/blockstore/doc.go index de95d63bf..a82723419 100644 --- a/ipld/car/v2/blockstore/doc.go +++ b/ipld/car/v2/blockstore/doc.go @@ -11,7 +11,7 @@ // The ReadWrite blockstore allows writing and reading of the blocks concurrently. The user of this // blockstore is responsible for calling ReadWrite.Finalize when finished writing blocks. // Upon finalization, the instance can no longer be used for reading or writing blocks and will -// panic if used. To continue reading the blocks users are encouraged to use ReadOnly blockstore +// error if used. To continue reading the blocks users are encouraged to use ReadOnly blockstore // instantiated from the same file path using OpenReadOnly. // A user may resume reading/writing from files produced by an instance of ReadWrite blockstore. The // resumption is attempted automatically, if the path passed to OpenReadWrite exists. diff --git a/ipld/car/v2/blockstore/export_test.go b/ipld/car/v2/blockstore/export_test.go deleted file mode 100644 index d4998eec8..000000000 --- a/ipld/car/v2/blockstore/export_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package blockstore - -// CloseReadWrite allows our external tests to close a read-write blockstore -// without finalizing it. -// The public API doesn't expose such a method. -// In the future, we might consider adding NewReadWrite taking io interfaces, -// meaning that the caller could be fully in control of opening and closing files. -// Another option would be to expose a "Discard" method alongside "Finalize". -func CloseReadWrite(b *ReadWrite) error { - return b.ronly.Close() -} diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 2df9a7946..7e165bd24 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -27,32 +27,36 @@ var _ blockstore.Blockstore = (*ReadOnly)(nil) var ( errZeroLengthSection = fmt.Errorf("zero-length carv2 section not allowed by default; see WithZeroLengthSectionAsEOF option") errReadOnly = fmt.Errorf("called write method on a read-only carv2 blockstore") + errClosed = fmt.Errorf("cannot use a carv2 blockstore after closing") ) -type ( - // ReadOnly provides a read-only CAR Block Store. - ReadOnly struct { - // mu allows ReadWrite to be safe for concurrent use. - // It's in ReadOnly so that read operations also grab read locks, - // given that ReadWrite embeds ReadOnly for methods like Get and Has. - // - // The main fields guarded by the mutex are the index and the underlying writers. - // For simplicity, the entirety of the blockstore methods grab the mutex. - mu sync.RWMutex - - // The backing containing the data payload in CARv1 format. - backing io.ReaderAt - // The CARv1 content index. - idx index.Index - - // If we called carv2.NewReaderMmap, remember to close it too. - carv2Closer io.Closer - - ropts carv2.ReadOptions - } +// ReadOnly provides a read-only CAR Block Store. +type ReadOnly struct { + // mu allows ReadWrite to be safe for concurrent use. + // It's in ReadOnly so that read operations also grab read locks, + // given that ReadWrite embeds ReadOnly for methods like Get and Has. + // + // The main fields guarded by the mutex are the index and the underlying writers. + // For simplicity, the entirety of the blockstore methods grab the mutex. + mu sync.RWMutex + + // When true, the blockstore has been closed via Close, Discard, or + // Finalize, and must not be used. Any further blockstore method calls + // will return errClosed to avoid panics or broken behavior. + closed bool + + // The backing containing the data payload in CARv1 format. + backing io.ReaderAt + // The CARv1 content index. + idx index.Index + + // If we called carv2.NewReaderMmap, remember to close it too. + carv2Closer io.Closer + + ropts carv2.ReadOptions +} - contextKey string -) +type contextKey string const asyncErrHandlerKey contextKey = "asyncErrorHandlerKey" @@ -177,7 +181,7 @@ func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) { return bcid, data, err } -// DeleteBlock is unsupported and always panics. +// DeleteBlock is unsupported and always errors. func (b *ReadOnly) DeleteBlock(_ cid.Cid) error { return errReadOnly } @@ -187,6 +191,10 @@ func (b *ReadOnly) Has(key cid.Cid) (bool, error) { b.mu.RLock() defer b.mu.RUnlock() + if b.closed { + return false, errClosed + } + var fnFound bool var fnErr error err := b.idx.GetAll(key, func(offset uint64) bool { @@ -223,6 +231,10 @@ func (b *ReadOnly) Get(key cid.Cid) (blocks.Block, error) { b.mu.RLock() defer b.mu.RUnlock() + if b.closed { + return nil, errClosed + } + var fnData []byte var fnErr error err := b.idx.GetAll(key, func(offset uint64) bool { @@ -263,6 +275,10 @@ func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { b.mu.RLock() defer b.mu.RUnlock() + if b.closed { + return 0, errClosed + } + fnSize := -1 var fnErr error err := b.idx.GetAll(key, func(offset uint64) bool { @@ -329,16 +345,26 @@ func WithAsyncErrorHandler(ctx context.Context, errHandler func(error)) context. // See WithAsyncErrorHandler func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // We release the lock when the channel-sending goroutine stops. + // Note that we can't use a deferred unlock here, + // because if we return a nil error, + // we only want to unlock once the async goroutine has stopped. b.mu.RLock() + if b.closed { + b.mu.RUnlock() // don't hold the mutex forever + return nil, errClosed + } + // TODO we may use this walk for populating the index, and we need to be able to iterate keys in this way somewhere for index generation. In general though, when it's asked for all keys from a blockstore with an index, we should iterate through the index when possible rather than linear reads through the full car. rdr := internalio.NewOffsetReadSeeker(b.backing, 0) header, err := carv1.ReadHeader(rdr) if err != nil { + b.mu.RUnlock() // don't hold the mutex forever return nil, fmt.Errorf("error reading car header: %w", err) } headerSize, err := carv1.HeaderSize(header) if err != nil { + b.mu.RUnlock() // don't hold the mutex forever return nil, err } @@ -347,6 +373,7 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // Seek to the end of header. if _, err = rdr.Seek(int64(headerSize), io.SeekStart); err != nil { + b.mu.RUnlock() // don't hold the mutex forever return nil, err } @@ -424,10 +451,10 @@ func (b *ReadOnly) Roots() ([]cid.Cid, error) { } // Close closes the underlying reader if it was opened by OpenReadOnly. +// After this call, the blockstore can no longer be used. // // Note that this call may block if any blockstore operations are currently in -// progress, including an AllKeysChan that hasn't been fully consumed or -// cancelled. +// progress, including an AllKeysChan that hasn't been fully consumed or cancelled. func (b *ReadOnly) Close() error { b.mu.Lock() defer b.mu.Unlock() @@ -436,6 +463,7 @@ func (b *ReadOnly) Close() error { } func (b *ReadOnly) closeWithoutMutex() error { + b.closed = true if b.carv2Closer != nil { return b.carv2Closer.Close() } diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 3fd16c8c6..7b87aee2a 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -101,7 +101,7 @@ func TestReadOnly(t *testing.T) { require.NoError(t, err) require.Equal(t, wantBlock, gotBlock) - // Assert write operations panic + // Assert write operations error require.Error(t, subject.Put(wantBlock)) require.Error(t, subject.PutMany([]blocks.Block{wantBlock})) require.Error(t, subject.DeleteBlock(key)) @@ -235,3 +235,38 @@ func newV1Reader(r io.Reader, zeroLenSectionAsEOF bool) (*carv1.CarReader, error } return carv1.NewCarReader(r) } + +func TestReadOnlyErrorAfterClose(t *testing.T) { + bs, err := OpenReadOnly("../testdata/sample-v1.car") + require.NoError(t, err) + + roots, err := bs.Roots() + require.NoError(t, err) + _, err = bs.Has(roots[0]) + require.NoError(t, err) + _, err = bs.Get(roots[0]) + require.NoError(t, err) + _, err = bs.GetSize(roots[0]) + require.NoError(t, err) + + ctx, cancel := context.WithCancel(context.Background()) + _, err = bs.AllKeysChan(ctx) + require.NoError(t, err) + cancel() // to stop the AllKeysChan goroutine + + bs.Close() + + _, err = bs.Roots() + require.Error(t, err) + _, err = bs.Has(roots[0]) + require.Error(t, err) + _, err = bs.Get(roots[0]) + require.Error(t, err) + _, err = bs.GetSize(roots[0]) + require.Error(t, err) + _, err = bs.AllKeysChan(ctx) + require.Error(t, err) + + // TODO: test that closing blocks if an AllKeysChan operation is + // in progress. +} diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 8986ff305..ae482a7f9 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -23,8 +23,6 @@ import ( var _ blockstore.Blockstore = (*ReadWrite)(nil) -var errFinalized = fmt.Errorf("cannot use a read-write carv2 blockstore after finalizing") - // ReadWrite implements a blockstore that stores blocks in CARv2 format. // Blocks put into the blockstore can be read back once they are successfully written. // This implementation is preferable for a write-heavy workload. @@ -33,7 +31,7 @@ var errFinalized = fmt.Errorf("cannot use a read-write carv2 blockstore after fi // // The Finalize function must be called once the putting blocks are finished. // Upon calling Finalize header is finalized and index is written out. -// Once finalized, all read and write calls to this blockstore will result in panics. +// Once finalized, all read and write calls to this blockstore will result in errors. type ReadWrite struct { ronly ReadOnly @@ -62,7 +60,7 @@ func AllowDuplicatePuts(allow bool) carv2.WriteOption { // ReadWrite.Finalize must be called once putting and reading blocks are no longer needed. // Upon calling ReadWrite.Finalize the CARv2 header and index are written out onto the file and the // backing file is closed. Once finalized, all read and write calls to this blockstore will result -// in panics. Note, ReadWrite.Finalize must be called on an open instance regardless of whether any +// in errors. Note, ReadWrite.Finalize must be called on an open instance regardless of whether any // blocks were put or not. // // If a file at given path does not exist, the instantiation will write car.Pragma and data payload @@ -287,26 +285,22 @@ func (b *ReadWrite) unfinalize() error { return err } -func (b *ReadWrite) finalized() bool { - return b.header.DataSize != 0 -} - // Put puts a given block to the underlying datastore func (b *ReadWrite) Put(blk blocks.Block) error { - // PutMany already checks b.finalized. + // PutMany already checks b.ronly.closed. return b.PutMany([]blocks.Block{blk}) } // PutMany puts a slice of blocks at the same time using batching // capabilities of the underlying datastore whenever possible. func (b *ReadWrite) PutMany(blks []blocks.Block) error { - if b.finalized() { - return errFinalized - } - b.ronly.mu.Lock() defer b.ronly.mu.Unlock() + if b.ronly.closed { + return errClosed + } + for _, bl := range blks { c := bl.Cid() @@ -331,25 +325,37 @@ func (b *ReadWrite) PutMany(blks []blocks.Block) error { return nil } +// Discard closes this blockstore without finalizing its header and index. +// After this call, the blockstore can no longer be used. +// +// Note that this call may block if any blockstore operations are currently in +// progress, including an AllKeysChan that hasn't been fully consumed or cancelled. +func (b *ReadWrite) Discard() { + // Same semantics as ReadOnly.Close, including allowing duplicate calls. + // The only difference is that our method is called Discard, + // to further clarify that we're not properly finalizing and writing a + // CARv2 file. + b.ronly.Close() +} + // Finalize finalizes this blockstore by writing the CARv2 header, along with flattened index // for more efficient subsequent read. -// After this call, this blockstore can no longer be used for read or write. +// After this call, the blockstore can no longer be used. func (b *ReadWrite) Finalize() error { - if b.header.DataSize != 0 { + b.ronly.mu.Lock() + defer b.ronly.mu.Unlock() + + if b.ronly.closed { // Allow duplicate Finalize calls, just like Close. // Still error, just like ReadOnly.Close; it should be discarded. - return fmt.Errorf("called Finalize twice") + return fmt.Errorf("called Finalize on a closed blockstore") } - b.ronly.mu.Lock() - defer b.ronly.mu.Unlock() // TODO check if add index option is set and don't write the index then set index offset to zero. b.header = b.header.WithDataSize(uint64(b.dataWriter.Position())) // Note that we can't use b.Close here, as that tries to grab the same // mutex we're holding here. - // TODO: should we check the error here? especially with OpenReadWrite, - // we should care about close errors. defer b.ronly.closeWithoutMutex() // TODO if index not needed don't bother flattening it. @@ -360,39 +366,29 @@ func (b *ReadWrite) Finalize() error { if err := index.WriteTo(fi, internalio.NewOffsetWriter(b.f, int64(b.header.IndexOffset))); err != nil { return err } - _, err = b.header.WriteTo(internalio.NewOffsetWriter(b.f, carv2.PragmaSize)) - return err -} + if _, err := b.header.WriteTo(internalio.NewOffsetWriter(b.f, carv2.PragmaSize)); err != nil { + return err + } -func (b *ReadWrite) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { - if b.finalized() { - return nil, errFinalized + if err := b.ronly.closeWithoutMutex(); err != nil { + return err } + return nil +} +func (b *ReadWrite) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return b.ronly.AllKeysChan(ctx) } func (b *ReadWrite) Has(key cid.Cid) (bool, error) { - if b.finalized() { - return false, errFinalized - } - return b.ronly.Has(key) } func (b *ReadWrite) Get(key cid.Cid) (blocks.Block, error) { - if b.finalized() { - return nil, errFinalized - } - return b.ronly.Get(key) } func (b *ReadWrite) GetSize(key cid.Cid) (int, error) { - if b.finalized() { - return 0, errFinalized - } - return b.ronly.GetSize(key) } diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 5316f5bf7..a30bbb235 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -373,7 +373,7 @@ func TestBlockstoreResumption(t *testing.T) { // Close off the open file and re-instantiate a new subject with resumption enabled. // Note, we don't have to close the file for resumption to work. // We do this to avoid resource leak during testing. - require.NoError(t, blockstore.CloseReadWrite(subject)) + subject.Discard() } subject, err = blockstore.OpenReadWrite(path, r.Header.Roots, blockstore.UseWholeCIDs(true)) @@ -405,7 +405,7 @@ func TestBlockstoreResumption(t *testing.T) { require.Equal(t, wantBlockCountSoFar, gotBlockCountSoFar) } } - require.NoError(t, blockstore.CloseReadWrite(subject)) + subject.Discard() // Finalize the blockstore to complete partially written CARv2 file. subject, err = blockstore.OpenReadWrite(path, r.Header.Roots, @@ -619,3 +619,51 @@ func TestReadWriteResumptionFromFileWithDifferentCarV1PaddingIsError(t *testing. "Expected padding value of 1413 but got 1314") require.Nil(t, resumingSubject) } + +func TestReadWriteErrorAfterClose(t *testing.T) { + root := blocks.NewBlock([]byte("foo")) + for _, closeMethod := range []func(*blockstore.ReadWrite){ + (*blockstore.ReadWrite).Discard, + func(bs *blockstore.ReadWrite) { bs.Finalize() }, + } { + path := filepath.Join(t.TempDir(), "readwrite.car") + bs, err := blockstore.OpenReadWrite(path, []cid.Cid{root.Cid()}) + require.NoError(t, err) + + err = bs.Put(root) + require.NoError(t, err) + + roots, err := bs.Roots() + require.NoError(t, err) + _, err = bs.Has(roots[0]) + require.NoError(t, err) + _, err = bs.Get(roots[0]) + require.NoError(t, err) + _, err = bs.GetSize(roots[0]) + require.NoError(t, err) + + ctx, cancel := context.WithCancel(context.Background()) + _, err = bs.AllKeysChan(ctx) + require.NoError(t, err) + cancel() // to stop the AllKeysChan goroutine + + closeMethod(bs) + + _, err = bs.Roots() + require.Error(t, err) + _, err = bs.Has(roots[0]) + require.Error(t, err) + _, err = bs.Get(roots[0]) + require.Error(t, err) + _, err = bs.GetSize(roots[0]) + require.Error(t, err) + _, err = bs.AllKeysChan(ctx) + require.Error(t, err) + + err = bs.Put(root) + require.Error(t, err) + + // TODO: test that closing blocks if an AllKeysChan operation is + // in progress. + } +} From 240282a4a651c6c331225bf582cd38f4abd287c2 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 10 Aug 2021 16:12:12 +0100 Subject: [PATCH 3538/3817] Implement utility to extract CARv1 from a CARv2 Implement `ExtractV1File` where the function takes path to a CARv2 file and efficiently extracts its inner CARv1 payload. Note, the implementation only supports CARv2 as input and returns a dedicated error if the supplied input is already in CARv1 format. Implement benchmarks comparing extraction using `Reader` vs `ExtractV1File`. Implement tests that assert in-place extraction as well as invalid input and both v1/v2 input Fixes #207 This commit was moved from ipld/go-car@81137942c924463c87a8cd310283ac651da5d7c7 --- ipld/car/v2/bench_test.go | 98 +++++++++++++++++++++++++++++++++ ipld/car/v2/writer.go | 108 +++++++++++++++++++++++++++++++++++++ ipld/car/v2/writer_test.go | 43 +++++++++++++++ 3 files changed, 249 insertions(+) diff --git a/ipld/car/v2/bench_test.go b/ipld/car/v2/bench_test.go index 83adc346a..16ae93378 100644 --- a/ipld/car/v2/bench_test.go +++ b/ipld/car/v2/bench_test.go @@ -2,12 +2,20 @@ package car_test import ( "io" + "math/rand" "os" + "path/filepath" "testing" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-merkledag" + "github.com/ipld/go-car/v2/blockstore" + carv2 "github.com/ipld/go-car/v2" ) +var rng = rand.New(rand.NewSource(1413)) + // BenchmarkReadBlocks instantiates a BlockReader, and iterates over all blocks. // It essentially looks at the contents of any CARv1 or CARv2 file. // Note that this also uses internal carv1.ReadHeader underneath. @@ -47,3 +55,93 @@ func BenchmarkReadBlocks(b *testing.B) { } }) } + +// BenchmarkExtractV1File extracts inner CARv1 payload from a sample CARv2 file using ExtractV1File. +func BenchmarkExtractV1File(b *testing.B) { + path := filepath.Join(b.TempDir(), "bench-large-v2.car") + generateRandomCarV2File(b, path, 10*1024*1024) // 10 MiB + defer os.Remove(path) + + info, err := os.Stat(path) + if err != nil { + b.Fatal(err) + } + b.SetBytes(info.Size()) + b.ReportAllocs() + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + dstPath := filepath.Join(b.TempDir(), "destination.car") + for pb.Next() { + err = carv2.ExtractV1File(path, dstPath) + if err != nil { + b.Fatal(err) + } + _ = os.Remove(dstPath) + } + }) +} + +// BenchmarkExtractV1UsingReader extracts inner CARv1 payload from a sample CARv2 file using Reader +// API. This benchmark is implemented to be used as a comparison in conjunction with +// BenchmarkExtractV1File. +func BenchmarkExtractV1UsingReader(b *testing.B) { + path := filepath.Join(b.TempDir(), "bench-large-v2.car") + generateRandomCarV2File(b, path, 10*1024*1024) // 10 MiB + defer os.Remove(path) + + info, err := os.Stat(path) + if err != nil { + b.Fatal(err) + } + b.SetBytes(info.Size()) + b.ReportAllocs() + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + dstPath := filepath.Join(b.TempDir(), "destination.car") + for pb.Next() { + dst, err := os.Create(dstPath) + if err != nil { + b.Fatal(err) + } + reader, err := carv2.OpenReader(path) + if err != nil { + b.Fatal(err) + } + _, err = io.Copy(dst, reader.DataReader()) + if err != nil { + b.Fatal(err) + } + if err := dst.Close(); err != nil { + b.Fatal(err) + } + } + }) +} + +func generateRandomCarV2File(b *testing.B, path string, minTotalBlockSize int) { + bs, err := blockstore.OpenReadWrite(path, []cid.Cid{}) + defer func() { + if err := bs.Finalize(); err != nil { + b.Fatal(err) + } + }() + if err != nil { + b.Fatal(err) + } + buf := make([]byte, 1024) + var totalBlockSize int + for totalBlockSize < minTotalBlockSize { + size, err := rng.Read(buf) + if err != nil { + b.Fatal(err) + } + + blk := merkledag.NewRawNode(buf) + if err := bs.Put(blk); err != nil { + b.Fatal(err) + } + totalBlockSize += size + } +} diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index 40004648e..91b534016 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -1,6 +1,8 @@ package car import ( + "errors" + "fmt" "io" "os" @@ -9,6 +11,9 @@ import ( "github.com/ipld/go-car/v2/index" ) +// ErrAlreadyV1 signals that the given payload is already in CARv1 format. +var ErrAlreadyV1 = errors.New("already a CARv1") + // WrapV1File is a wrapper around WrapV1 that takes filesystem paths. // The source path is assumed to exist, and the destination path is overwritten. // Note that the destination path might still be created even if an error @@ -79,6 +84,109 @@ func WrapV1(src io.ReadSeeker, dst io.Writer) error { return nil } +// ExtractV1File takes a CARv2 file and extracts its CARv1 data payload, unmodified. +// The resulting CARv1 file will not include any data payload padding that may be present in the +// CARv2 srcPath. +// If srcPath represents a CARv1 ErrAlreadyV1 error is returned. +// The srcPath is assumed to exist, and the destination path is created if not exist. +// Note that the destination path might still be created even if an error +// occurred. +// If srcPath and dstPath are the same, then the dstPath is converted, in-place, to CARv1. +func ExtractV1File(srcPath, dstPath string) (err error) { + src, err := os.Open(srcPath) + if err != nil { + return err + } + + // Ignore close error since only reading from src. + defer src.Close() + + // Detect CAR version. + version, err := ReadVersion(src) + if err != nil { + return err + } + if version == 1 { + return ErrAlreadyV1 + } + if version != 2 { + return fmt.Errorf("invalid source version: %v", version) + } + + // Read CARv2 header to locate data payload. + var v2h Header + if _, err := v2h.ReadFrom(src); err != nil { + return err + } + + // TODO consider extracting this into Header.Validate since it is also implemented in BlockReader. + // Validate header + dataOffset := int64(v2h.DataOffset) + if dataOffset < PragmaSize+HeaderSize { + return fmt.Errorf("invalid data payload offset: %v", dataOffset) + } + dataSize := int64(v2h.DataSize) + if dataSize <= 0 { + return fmt.Errorf("invalid data payload size: %v", dataSize) + } + + // Seek to the point where the data payload starts + if _, err := src.Seek(dataOffset, io.SeekStart); err != nil { + return err + } + + // Open destination as late as possible to minimise unintended file creation in case an error + // occurs earlier. + // Note, we explicitly do not use os.O_TRUNC here so that we can support in-place extraction. + // Otherwise, truncation of an existing file will wipe the data we would be reading from if + // source and destination paths are the same. + // Later, we do truncate the file to the right size to assert there are no tailing extra bytes. + dst, err := os.OpenFile(dstPath, os.O_CREATE|os.O_WRONLY, 0o666) + if err != nil { + return err + } + + defer func() { + // Close destination and override return error type if it is nil. + cerr := dst.Close() + if err == nil { + err = cerr + } + }() + + // Copy data payload over, expecting to write exactly the right number of bytes. + // Note that we explicitly use io.CopyN using file descriptors to leverage the SDK's efficient + // byte copy which should stay out of userland. + // There are two benchmarks to measure this: BenchmarkExtractV1File vs. BenchmarkExtractV1UsingReader + written, err := io.CopyN(dst, src, dataSize) + if err != nil { + return err + } + if written != dataSize { + return fmt.Errorf("expected to write exactly %v but wrote %v", dataSize, written) + } + + // Check that the size destination file matches expected size. + // If bigger truncate. + // Note, we need to truncate: + // - if file is changed in-place, i.e. src and dst paths are the same then index or padding + // could be present after the data payload. + // - if an existing file is passed as destination which is different from source and is larger + // than the data payload size. + // In general, we want to guarantee that this function produces correct CARv2 payload in + // destination. + stat, err := dst.Stat() + if err != nil { + return err + } + if stat.Size() > dataSize { + // Truncate to the expected size to assure the resulting file is a correctly sized CARv1. + err = dst.Truncate(written) + } + + return err +} + // AttachIndex attaches a given index to an existing CARv2 file at given path and offset. func AttachIndex(path string, idx index.Index, offset uint64) error { // TODO: instead of offset, maybe take padding? diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index 3cf119cee..c35beb4a4 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -59,6 +59,49 @@ func TestWrapV1(t *testing.T) { require.Equal(t, wantIdx, gotIdx) } +func TestExtractV1(t *testing.T) { + // Produce a CARv1 file to test. + dagSvc := dstest.Mock() + v1Src := filepath.Join(t.TempDir(), "original-test-v1.car") + v1f, err := os.Create(v1Src) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, v1f.Close()) }) + require.NoError(t, carv1.WriteCar(context.Background(), dagSvc, generateRootCid(t, dagSvc), v1f)) + _, err = v1f.Seek(0, io.SeekStart) + require.NoError(t, err) + wantV1, err := ioutil.ReadAll(v1f) + require.NoError(t, err) + + // Wrap the produced CARv1 into a CARv2 to use for testing. + v2path := filepath.Join(t.TempDir(), "wrapped-for-extract-test-v2.car") + require.NoError(t, WrapV1File(v1Src, v2path)) + + // Assert extract from CARv2 file is as expected. + dstPath := filepath.Join(t.TempDir(), "extract-file-test-v1.car") + require.NoError(t, ExtractV1File(v2path, dstPath)) + gotFromFile, err := ioutil.ReadFile(dstPath) + require.NoError(t, err) + require.Equal(t, wantV1, gotFromFile) + + // Assert extract from CARv2 file in-place is as expected + require.NoError(t, ExtractV1File(v2path, v2path)) + gotFromInPlaceFile, err := ioutil.ReadFile(v2path) + require.NoError(t, err) + require.Equal(t, wantV1, gotFromInPlaceFile) +} + +func TestExtractV1WithUnknownVersionIsError(t *testing.T) { + dstPath := filepath.Join(t.TempDir(), "extract-dst-file-test-v42.car") + err := ExtractV1File("testdata/sample-rootless-v42.car", dstPath) + require.EqualError(t, err, "invalid source version: 42") +} + +func TestExtractV1FromACarV1IsError(t *testing.T) { + dstPath := filepath.Join(t.TempDir(), "extract-dst-file-test-v1.car") + err := ExtractV1File("testdata/sample-v1.car", dstPath) + require.Equal(t, ErrAlreadyV1, err) +} + func generateRootCid(t *testing.T, adder format.NodeAdder) []cid.Cid { // TODO convert this into a utility testing lib that takes an rng and generates a random DAG with some threshold for depth/breadth. this := merkledag.NewRawNode([]byte("fish")) From 7fd2bd25c643303dd20d33308ef5e1f4390eb81d Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 11 Aug 2021 12:36:42 +0100 Subject: [PATCH 3539/3817] Document performance caveats of `ExtractV1File` and address comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since `CopyFileRange` performance is OS-dependant, we cannot guarantee that `ExtractV1File` will keep copies out of user-space. For example, on Linux with sufficiently old Kernel the current behaviour will fall back to user-space copy. Document this on the function so that it is made clear. Improve benchmarking determinism and error messages. The benchmark numbers that Daniel obtained on his laptop, running Linux 5.13 on an i5-8350U with /tmp being tmpfs, are as follows. The "old" results are BenchmarkExtractV1UsingReader, and the "new" are BenchmarkExtractV1File. name old time/op new time/op delta ExtractV1-8 1.33ms ± 1% 1.11ms ± 2% -16.48% (p=0.000 n=8+8) name old speed new speed delta ExtractV1-8 7.88GB/s ± 1% 9.43GB/s ± 2% +19.74% (p=0.000 n=8+8) name old alloc/op new alloc/op delta ExtractV1-8 34.0kB ± 0% 1.0kB ± 0% -97.09% (p=0.000 n=8+8) name old allocs/op new allocs/op delta ExtractV1-8 26.0 ± 0% 23.0 ± 0% -11.54% (p=0.000 n=8+8) So, at least in the case where the filesystem is very fast, we can see that the benefit is around 10-20%, as well as fewer allocs thanks to not needing a user-space buffer. The performance benefit will likely be smaller on slower disks. For the Linux syscall logic, see: - https://cs.opensource.google/go/go/+/refs/tags/go1.16.7:src/internal/poll/copy_file_range_linux.go;drc=refs%2Ftags%2Fgo1.16.7;l=54 This commit was moved from ipld/go-car@c514a30114d7725035293f7718de4b4e1cff9fdf --- ipld/car/v2/bench_test.go | 10 +++++----- ipld/car/v2/writer.go | 14 ++++++++++---- ipld/car/v2/writer_test.go | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/ipld/car/v2/bench_test.go b/ipld/car/v2/bench_test.go index 16ae93378..15ae0c24f 100644 --- a/ipld/car/v2/bench_test.go +++ b/ipld/car/v2/bench_test.go @@ -14,8 +14,6 @@ import ( carv2 "github.com/ipld/go-car/v2" ) -var rng = rand.New(rand.NewSource(1413)) - // BenchmarkReadBlocks instantiates a BlockReader, and iterates over all blocks. // It essentially looks at the contents of any CARv1 or CARv2 file. // Note that this also uses internal carv1.ReadHeader underneath. @@ -59,7 +57,7 @@ func BenchmarkReadBlocks(b *testing.B) { // BenchmarkExtractV1File extracts inner CARv1 payload from a sample CARv2 file using ExtractV1File. func BenchmarkExtractV1File(b *testing.B) { path := filepath.Join(b.TempDir(), "bench-large-v2.car") - generateRandomCarV2File(b, path, 10*1024*1024) // 10 MiB + generateRandomCarV2File(b, path, 10<<20) // 10 MiB defer os.Remove(path) info, err := os.Stat(path) @@ -87,7 +85,7 @@ func BenchmarkExtractV1File(b *testing.B) { // BenchmarkExtractV1File. func BenchmarkExtractV1UsingReader(b *testing.B) { path := filepath.Join(b.TempDir(), "bench-large-v2.car") - generateRandomCarV2File(b, path, 10*1024*1024) // 10 MiB + generateRandomCarV2File(b, path, 10<<20) // 10 MiB defer os.Remove(path) info, err := os.Stat(path) @@ -121,6 +119,8 @@ func BenchmarkExtractV1UsingReader(b *testing.B) { } func generateRandomCarV2File(b *testing.B, path string, minTotalBlockSize int) { + // Use fixed RNG for determinism across benchmarks. + rng := rand.New(rand.NewSource(1413)) bs, err := blockstore.OpenReadWrite(path, []cid.Cid{}) defer func() { if err := bs.Finalize(); err != nil { @@ -130,7 +130,7 @@ func generateRandomCarV2File(b *testing.B, path string, minTotalBlockSize int) { if err != nil { b.Fatal(err) } - buf := make([]byte, 1024) + buf := make([]byte, 32<<10) // 32 KiB var totalBlockSize int for totalBlockSize < minTotalBlockSize { size, err := rng.Read(buf) diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index 91b534016..c34482815 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -92,6 +92,12 @@ func WrapV1(src io.ReadSeeker, dst io.Writer) error { // Note that the destination path might still be created even if an error // occurred. // If srcPath and dstPath are the same, then the dstPath is converted, in-place, to CARv1. +// +// This function aims to extract the CARv1 payload as efficiently as possible. +// The method is best-effort and depends on your operating system; +// for example, it should use copy_file_range on recent Linux versions. +// This API should be preferred over copying directly via Reader.DataReader, +// as it should allow for better performance while always being at least as efficient. func ExtractV1File(srcPath, dstPath string) (err error) { src, err := os.Open(srcPath) if err != nil { @@ -110,7 +116,7 @@ func ExtractV1File(srcPath, dstPath string) (err error) { return ErrAlreadyV1 } if version != 2 { - return fmt.Errorf("invalid source version: %v", version) + return fmt.Errorf("source version must be 2; got: %d", version) } // Read CARv2 header to locate data payload. @@ -123,11 +129,11 @@ func ExtractV1File(srcPath, dstPath string) (err error) { // Validate header dataOffset := int64(v2h.DataOffset) if dataOffset < PragmaSize+HeaderSize { - return fmt.Errorf("invalid data payload offset: %v", dataOffset) + return fmt.Errorf("invalid data payload offset: %d", dataOffset) } dataSize := int64(v2h.DataSize) if dataSize <= 0 { - return fmt.Errorf("invalid data payload size: %v", dataSize) + return fmt.Errorf("invalid data payload size: %d", dataSize) } // Seek to the point where the data payload starts @@ -163,7 +169,7 @@ func ExtractV1File(srcPath, dstPath string) (err error) { return err } if written != dataSize { - return fmt.Errorf("expected to write exactly %v but wrote %v", dataSize, written) + return fmt.Errorf("expected to write exactly %d but wrote %d", dataSize, written) } // Check that the size destination file matches expected size. diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index c35beb4a4..c29b43397 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -93,7 +93,7 @@ func TestExtractV1(t *testing.T) { func TestExtractV1WithUnknownVersionIsError(t *testing.T) { dstPath := filepath.Join(t.TempDir(), "extract-dst-file-test-v42.car") err := ExtractV1File("testdata/sample-rootless-v42.car", dstPath) - require.EqualError(t, err, "invalid source version: 42") + require.EqualError(t, err, "source version must be 2; got: 42") } func TestExtractV1FromACarV1IsError(t *testing.T) { From e0bba0ad7db9de4be588e186b7ab4b0b84e46ca0 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Wed, 11 Aug 2021 09:34:41 -0700 Subject: [PATCH 3540/3817] style(comment): add explanatory comment on test cid This commit was moved from ipfs/go-merkledag@98630859551b72269a8734cf85becc75a3bb7bf8 --- ipld/merkledag/coding_test.go | 1 + ipld/merkledag/node_test.go | 1 + 2 files changed, 2 insertions(+) diff --git a/ipld/merkledag/coding_test.go b/ipld/merkledag/coding_test.go index efc36661b..2ebe74384 100644 --- a/ipld/merkledag/coding_test.go +++ b/ipld/merkledag/coding_test.go @@ -14,6 +14,7 @@ var benchInput []byte func init() { someData := bytes.Repeat([]byte("some plaintext data\n"), 10) + // make a test CID -- doesn't matter just to add as a link someCid, _ := cid.Cast([]byte{1, 85, 0, 5, 0, 1, 2, 3, 4}) node := &merkledag.ProtoNode{} diff --git a/ipld/merkledag/node_test.go b/ipld/merkledag/node_test.go index e52fa0d1b..d12e08844 100644 --- a/ipld/merkledag/node_test.go +++ b/ipld/merkledag/node_test.go @@ -16,6 +16,7 @@ var sampleCid cid.Cid func init() { var err error + // make a test CID -- doesn't matter just to add as a link sampleCid, err = cid.Cast([]byte{1, 85, 0, 5, 0, 1, 2, 3, 4}) if err != nil { panic(err) From 98297d49ea00b6c8acc9a005dcb52d89f4f56852 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Wed, 11 Aug 2021 09:46:46 -0700 Subject: [PATCH 3541/3817] style(comments): add blank import comment This commit was moved from ipfs/go-merkledag@f0d2ffbc46e7fa32dc5fde5df35ef95929bb9184 --- ipld/merkledag/merkledag.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 258d87519..5d318e3b7 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -13,6 +13,8 @@ import ( format "github.com/ipfs/go-ipld-format" legacy "github.com/ipfs/go-ipld-legacy" dagpb "github.com/ipld/go-codec-dagpb" + + // blank import is used to register the IPLD raw codec _ "github.com/ipld/go-ipld-prime/codec/raw" basicnode "github.com/ipld/go-ipld-prime/node/basic" ) From 49cba9701f51ad69bfcd89b8b45dbecf44fe5f7e Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Thu, 12 Aug 2021 08:41:35 -0700 Subject: [PATCH 3542/3817] fix(tests): fix tests for ipld update This commit was moved from ipfs/go-fetcher@ef6ef50eaf5c3968d412d87980e3ff45a7e3e6c2 --- fetcher/helpers/block_visitor_test.go | 4 ++-- fetcher/testutil/testutil.go | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/fetcher/helpers/block_visitor_test.go b/fetcher/helpers/block_visitor_test.go index f7837043f..2fcf58d03 100644 --- a/fetcher/helpers/block_visitor_test.go +++ b/fetcher/helpers/block_visitor_test.go @@ -88,8 +88,8 @@ func TestFetchGraphToUniqueBlocks(t *testing.T) { na.AssembleEntry("foo").AssignBool(true) na.AssembleEntry("bar").AssignBool(false) na.AssembleEntry("nested").CreateMap(2, func(na fluent.MapAssembler) { - na.AssembleEntry("link3").AssignLink(link3) na.AssembleEntry("link2").AssignLink(link2) + na.AssembleEntry("link3").AssignLink(link3) na.AssembleEntry("nonlink").AssignString("zoo") }) })) @@ -125,7 +125,7 @@ func TestFetchGraphToUniqueBlocks(t *testing.T) { })) require.NoError(t, err) - assertBlocksInOrder(t, results, 3, map[int]ipld.Node{0: node1, 1: node3, 2: node2}) + assertBlocksInOrder(t, results, 3, map[int]ipld.Node{0: node1, 1: node2, 2: node3}) } func assertBlocksInOrder(t *testing.T, results []helpers.BlockResult, nodeCount int, nodes map[int]ipld.Node) { diff --git a/fetcher/testutil/testutil.go b/fetcher/testutil/testutil.go index f67e6ca76..ecb7ac102 100644 --- a/fetcher/testutil/testutil.go +++ b/fetcher/testutil/testutil.go @@ -24,6 +24,9 @@ func EncodeBlock(n ipld.Node) (blocks.Block, ipld.Node, ipld.Link) { MhType: 0x17, MhLength: 20, }} + ls.StorageReadOpener = func(ipld.LinkContext, ipld.Link) (io.Reader, error) { + return bytes.NewReader(b.RawData()), nil + } ls.StorageWriteOpener = func(ipld.LinkContext) (io.Writer, ipld.BlockWriteCommitter, error) { buf := bytes.Buffer{} return &buf, func(lnk ipld.Link) error { @@ -40,5 +43,9 @@ func EncodeBlock(n ipld.Node) (blocks.Block, ipld.Node, ipld.Link) { if err != nil { panic(err) } - return b, n, lnk + ln, err := ls.Load(ipld.LinkContext{}, lnk, n.Prototype()) + if err != nil { + panic(err) + } + return b, ln, lnk } From 1df7385e41f9e784e014612e3c960878051a19e0 Mon Sep 17 00:00:00 2001 From: Hannah Howard Date: Thu, 12 Aug 2021 08:52:05 -0700 Subject: [PATCH 3543/3817] IPLD Prime In IPFS: Target Merge Branch (#36) * Use go-fetcher, unixfsnode, and ipld-prime to resolve paths. (#34) * first pass * Update resolver/resolver.go Co-authored-by: Eric Myhre * update dependencies to tagged versions * correctly handles nested nodes within blocks * return link from resolve path so we can fetch container block * return expected NoSuchLink error * more accurate errors * feat(resolver): remove resolve once remove ResolveOnce as it's no longer used and is just confusing Co-authored-by: acruikshank Co-authored-by: Eric Myhre Co-authored-by: hannahhoward * fix(update to tagged branches): update to tagged branches and use node reifier * fix(deps): update go-unixfsnode * fix(deps): update to latest go fetcher (#37) * feat(resolver): take fetcher config as parameter (#38) * fix(deps): switch to tagged go-fetcher * fix(resolver): removed ipldcbor dependency * fix(mod): remove unneeded deps * fix(resolver): correct comments * test(resolver): add test verifying ErrNoLink functionality * fix(lint): fix lint errors resolve go vet and staticcheck issues. note we had to ignore two lines that use deprecated behavior, but which replacing could have unintended effects * fix(resolver): LookupBySegment to handle list indexes as well as map fields (#42) * fix(resolver): LookupBySegment to handle list indexes as well as map fields * Add test for /mixed/path/segment/types/1/2/3 * feat(resolver): address more PR comments * style(tests): add clarification * style(lint): fix lint errors, redo test fix * fix(deps): update deps to tagged version Co-authored-by: Alex Cruikshank <169613+acruikshank@users.noreply.github.com> Co-authored-by: acruikshank Co-authored-by: Eric Myhre Co-authored-by: Rod Vagg This commit was moved from ipfs/go-path@ea3a11629c2ec53a75b322a6deeb7d4bc1641339 --- path/resolver/resolver.go | 260 +++++++++++++++++++++------------ path/resolver/resolver_test.go | 204 ++++++++++++++++++++++---- 2 files changed, 339 insertions(+), 125 deletions(-) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 9f153840c..e42855e0c 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -7,12 +7,19 @@ import ( "fmt" "time" + "github.com/ipld/go-ipld-prime/schema" + path "github.com/ipfs/go-path" cid "github.com/ipfs/go-cid" - ipld "github.com/ipfs/go-ipld-format" + "github.com/ipfs/go-fetcher" + fetcherhelpers "github.com/ipfs/go-fetcher/helpers" + format "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" - dag "github.com/ipfs/go-merkledag" + "github.com/ipld/go-ipld-prime" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/ipld/go-ipld-prime/traversal/selector/builder" ) var log = logging.Logger("pathresolv") @@ -34,29 +41,24 @@ func (e ErrNoLink) Error() string { return fmt.Sprintf("no link named %q under %s", e.Name, e.Node.String()) } -// ResolveOnce resolves path through a single node -type ResolveOnce func(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, names []string) (*ipld.Link, []string, error) - // Resolver provides path resolution to IPFS -// It has a pointer to a DAGService, which is uses to resolve nodes. +// It references a FetcherFactory, which is uses to resolve nodes. // TODO: now that this is more modular, try to unify this code with the // the resolvers in namesys type Resolver struct { - DAG ipld.NodeGetter - - ResolveOnce ResolveOnce + FetcherFactory fetcher.Factory } // NewBasicResolver constructs a new basic resolver. -func NewBasicResolver(ds ipld.DAGService) *Resolver { +func NewBasicResolver(fetcherFactory fetcher.Factory) *Resolver { return &Resolver{ - DAG: ds, - ResolveOnce: ResolveSingle, + FetcherFactory: fetcherFactory, } } -// ResolveToLastNode walks the given path and returns the cid of the last node -// referenced by the path +// ResolveToLastNode walks the given path and returns the cid of the last block +// referenced by the path, and the path segments to traverse from the final block boundary to the final node +// within the block. func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (cid.Cid, []string, error) { c, p, err := path.SplitAbsPath(fpath) if err != nil { @@ -67,147 +69,213 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (cid. return c, nil, nil } - nd, err := r.DAG.Get(ctx, c) + // create a selector to traverse and match all path segments + pathSelector := pathAllSelector(p[:len(p)-1]) + + // resolve node before last path segment + nodes, lastCid, depth, err := r.resolveNodes(ctx, c, pathSelector) if err != nil { return cid.Cid{}, nil, err } - for len(p) > 0 { - lnk, rest, err := r.ResolveOnce(ctx, r.DAG, nd, p) - - // Note: have to drop the error here as `ResolveOnce` doesn't handle 'leaf' - // paths (so e.g. for `echo '{"foo":123}' | ipfs dag put` we wouldn't be - // able to resolve `zdpu[...]/foo`) - if lnk == nil { - break - } - - if err != nil { - if err == dag.ErrLinkNotFound { - err = ErrNoLink{Name: p[0], Node: nd.Cid()} - } - return cid.Cid{}, nil, err - } + if len(nodes) < 1 { + return cid.Cid{}, nil, fmt.Errorf("path %v did not resolve to a node", fpath) + } else if len(nodes) < len(p) { + return cid.Undef, nil, ErrNoLink{Name: p[len(nodes)-1], Node: lastCid} + } - if len(rest) == 0 { - return lnk.Cid, nil, nil - } + parent := nodes[len(nodes)-1] + lastSegment := p[len(p)-1] - next, err := lnk.GetNode(ctx, r.DAG) - if err != nil { - return cid.Cid{}, nil, err - } - nd = next - p = rest + // find final path segment within node + nd, err := parent.LookupBySegment(ipld.ParsePathSegment(lastSegment)) + switch err.(type) { + case nil: + case schema.ErrNoSuchField: + return cid.Undef, nil, ErrNoLink{Name: lastSegment, Node: lastCid} + default: + return cid.Cid{}, nil, err } - if len(p) == 0 { - return nd.Cid(), nil, nil + // if last node is not a link, just return it's cid, add path to remainder and return + if nd.Kind() != ipld.Kind_Link { + // return the cid and the remainder of the path + return lastCid, p[len(p)-depth-1:], nil } - // Confirm the path exists within the object - val, rest, err := nd.Resolve(p) + lnk, err := nd.AsLink() if err != nil { - if err == dag.ErrLinkNotFound { - err = ErrNoLink{Name: p[0], Node: nd.Cid()} - } return cid.Cid{}, nil, err } - if len(rest) > 0 { - return cid.Cid{}, nil, errors.New("path failed to resolve fully") - } - switch val.(type) { - case *ipld.Link: - return cid.Cid{}, nil, errors.New("inconsistent ResolveOnce / nd.Resolve") - default: - return nd.Cid(), p, nil + clnk, ok := lnk.(cidlink.Link) + if !ok { + return cid.Cid{}, nil, fmt.Errorf("path %v resolves to a link that is not a cid link: %v", fpath, lnk) } + + return clnk.Cid, []string{}, nil } // ResolvePath fetches the node for given path. It returns the last item -// returned by ResolvePathComponents. -func (r *Resolver) ResolvePath(ctx context.Context, fpath path.Path) (ipld.Node, error) { +// returned by ResolvePathComponents and the last link traversed which can be used to recover the block. +func (r *Resolver) ResolvePath(ctx context.Context, fpath path.Path) (ipld.Node, ipld.Link, error) { // validate path if err := fpath.IsValid(); err != nil { - return nil, err + return nil, nil, err } - nodes, err := r.ResolvePathComponents(ctx, fpath) - if err != nil || nodes == nil { - return nil, err + c, p, err := path.SplitAbsPath(fpath) + if err != nil { + return nil, nil, err } - return nodes[len(nodes)-1], err + + // create a selector to traverse all path segments but only match the last + pathSelector := pathLeafSelector(p) + + nodes, c, _, err := r.resolveNodes(ctx, c, pathSelector) + if err != nil { + return nil, nil, err + } + if len(nodes) < 1 { + return nil, nil, fmt.Errorf("path %v did not resolve to a node", fpath) + } + return nodes[len(nodes)-1], cidlink.Link{Cid: c}, nil } // ResolveSingle simply resolves one hop of a path through a graph with no // extra context (does not opaquely resolve through sharded nodes) -func ResolveSingle(ctx context.Context, ds ipld.NodeGetter, nd ipld.Node, names []string) (*ipld.Link, []string, error) { +// Deprecated: fetch node as ipld-prime or convert it and then use a selector to traverse through it. +func ResolveSingle(ctx context.Context, ds format.NodeGetter, nd format.Node, names []string) (*format.Link, []string, error) { return nd.ResolveLink(names) } // ResolvePathComponents fetches the nodes for each segment of the given path. // It uses the first path component as a hash (key) of the first node, then -// resolves all other components walking the links, with ResolveLinks. +// resolves all other components walking the links via a selector traversal func (r *Resolver) ResolvePathComponents(ctx context.Context, fpath path.Path) ([]ipld.Node, error) { + //lint:ignore SA1019 TODO: replace EventBegin evt := log.EventBegin(ctx, "resolvePathComponents", logging.LoggableMap{"fpath": fpath}) defer evt.Done() - h, parts, err := path.SplitAbsPath(fpath) - if err != nil { + // validate path + if err := fpath.IsValid(); err != nil { evt.Append(logging.LoggableMap{"error": err.Error()}) return nil, err } - log.Debug("resolve dag get") - nd, err := r.DAG.Get(ctx, h) + c, p, err := path.SplitAbsPath(fpath) if err != nil { evt.Append(logging.LoggableMap{"error": err.Error()}) return nil, err } - return r.ResolveLinks(ctx, nd, parts) + // create a selector to traverse and match all path segments + pathSelector := pathAllSelector(p) + + nodes, _, _, err := r.resolveNodes(ctx, c, pathSelector) + if err != nil { + evt.Append(logging.LoggableMap{"error": err.Error()}) + } + + return nodes, err } // ResolveLinks iteratively resolves names by walking the link hierarchy. -// Every node is fetched from the DAGService, resolving the next name. +// Every node is fetched from the Fetcher, resolving the next name. // Returns the list of nodes forming the path, starting with ndd. This list is // guaranteed never to be empty. // // ResolveLinks(nd, []string{"foo", "bar", "baz"}) // would retrieve "baz" in ("bar" in ("foo" in nd.Links).Links).Links func (r *Resolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names []string) ([]ipld.Node, error) { - + //lint:ignore SA1019 TODO: replace EventBegin evt := log.EventBegin(ctx, "resolveLinks", logging.LoggableMap{"names": names}) defer evt.Done() - result := make([]ipld.Node, 0, len(names)+1) - result = append(result, ndd) - nd := ndd // dup arg workaround - - // for each of the path components - for len(names) > 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, time.Minute) - defer cancel() - - lnk, rest, err := r.ResolveOnce(ctx, r.DAG, nd, names) - if err == dag.ErrLinkNotFound { - evt.Append(logging.LoggableMap{"error": err.Error()}) - return result, ErrNoLink{Name: names[0], Node: nd.Cid()} - } else if err != nil { - evt.Append(logging.LoggableMap{"error": err.Error()}) - return result, err + + // create a selector to traverse and match all path segments + pathSelector := pathAllSelector(names) + + // create a new cancellable session + ctx, cancel := context.WithTimeout(ctx, time.Minute) + defer cancel() + + session := r.FetcherFactory.NewSession(ctx) + + // traverse selector + nodes := []ipld.Node{ndd} + err := session.NodeMatching(ctx, ndd, pathSelector, func(res fetcher.FetchResult) error { + nodes = append(nodes, res.Node) + return nil + }) + if err != nil { + evt.Append(logging.LoggableMap{"error": err.Error()}) + return nil, err + } + + return nodes, err +} + +// Finds nodes matching the selector starting with a cid. Returns the matched nodes, the cid of the block containing +// the last node, and the depth of the last node within its block (root is depth 0). +func (r *Resolver) resolveNodes(ctx context.Context, c cid.Cid, sel ipld.Node) ([]ipld.Node, cid.Cid, int, error) { + // create a new cancellable session + ctx, cancel := context.WithTimeout(ctx, time.Minute) + defer cancel() + + session := r.FetcherFactory.NewSession(ctx) + + // traverse selector + lastLink := cid.Undef + depth := 0 + nodes := []ipld.Node{} + err := fetcherhelpers.BlockMatching(ctx, session, cidlink.Link{Cid: c}, sel, func(res fetcher.FetchResult) error { + if res.LastBlockLink == nil { + res.LastBlockLink = cidlink.Link{Cid: c} + } + cidLnk, ok := res.LastBlockLink.(cidlink.Link) + if !ok { + return fmt.Errorf("link is not a cidlink: %v", cidLnk) } - nextnode, err := lnk.GetNode(ctx, r.DAG) - if err != nil { - evt.Append(logging.LoggableMap{"error": err.Error()}) - return result, err + // if we hit a block boundary + if !lastLink.Equals(cidLnk.Cid) { + depth = 0 + lastLink = cidLnk.Cid + } else { + depth++ } - nd = nextnode - result = append(result, nextnode) - names = rest + nodes = append(nodes, res.Node) + return nil + }) + if err != nil { + return nil, cid.Undef, 0, err + } + + return nodes, lastLink, depth, nil +} + +func pathLeafSelector(path []string) ipld.Node { + ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype.Any) + return pathSelector(path, ssb, func(p string, s builder.SelectorSpec) builder.SelectorSpec { + return ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) { efsb.Insert(p, s) }) + }) +} + +func pathAllSelector(path []string) ipld.Node { + ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype.Any) + return pathSelector(path, ssb, func(p string, s builder.SelectorSpec) builder.SelectorSpec { + return ssb.ExploreUnion( + ssb.Matcher(), + ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) { efsb.Insert(p, s) }), + ) + }) +} + +func pathSelector(path []string, ssb builder.SelectorSpecBuilder, reduce func(string, builder.SelectorSpec) builder.SelectorSpec) ipld.Node { + spec := ssb.Matcher() + for i := len(path) - 1; i >= 0; i-- { + spec = reduce(path[i], spec) } - return result, nil + return spec.Node() } diff --git a/path/resolver/resolver_test.go b/path/resolver/resolver_test.go index d3c6913e7..b1d8dec9e 100644 --- a/path/resolver/resolver_test.go +++ b/path/resolver/resolver_test.go @@ -1,18 +1,33 @@ package resolver_test import ( + "bytes" "context" "fmt" "math/rand" + "strings" "testing" "time" - path "github.com/ipfs/go-path" - "github.com/ipfs/go-path/resolver" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + bsfetcher "github.com/ipfs/go-fetcher/impl/blockservice" + dagpb "github.com/ipld/go-codec-dagpb" + "github.com/ipld/go-ipld-prime" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/ipld/go-ipld-prime/schema" + "github.com/multiformats/go-multihash" - ipld "github.com/ipfs/go-ipld-format" merkledag "github.com/ipfs/go-merkledag" dagmock "github.com/ipfs/go-merkledag/test" + path "github.com/ipfs/go-path" + "github.com/ipfs/go-path/resolver" + "github.com/ipfs/go-unixfsnode" + dagcbor "github.com/ipld/go-ipld-prime/codec/dagcbor" + dagjson "github.com/ipld/go-ipld-prime/codec/dagjson" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func randNode() *merkledag.ProtoNode { @@ -25,7 +40,7 @@ func randNode() *merkledag.ProtoNode { func TestRecurivePathResolution(t *testing.T) { ctx := context.Background() - dagService := dagmock.Mock() + bsrv := dagmock.Bserv() a := randNode() b := randNode() @@ -41,8 +56,8 @@ func TestRecurivePathResolution(t *testing.T) { t.Fatal(err) } - for _, n := range []ipld.Node{a, b, c} { - err = dagService.Add(ctx, n) + for _, n := range []*merkledag.ProtoNode{a, b, c} { + err = bsrv.AddBlock(n) if err != nil { t.Fatal(err) } @@ -56,19 +71,31 @@ func TestRecurivePathResolution(t *testing.T) { t.Fatal(err) } - resolver := resolver.NewBasicResolver(dagService) - node, err := resolver.ResolvePath(ctx, p) + fetcherFactory := bsfetcher.NewFetcherConfig(bsrv) + fetcherFactory.NodeReifier = unixfsnode.Reify + fetcherFactory.PrototypeChooser = dagpb.AddSupportToChooser(func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { + if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { + return tlnkNd.LinkTargetNodePrototype(), nil + } + return basicnode.Prototype.Any, nil + }) + resolver := resolver.NewBasicResolver(fetcherFactory) + + node, lnk, err := resolver.ResolvePath(ctx, p) if err != nil { t.Fatal(err) } + uNode, ok := node.(unixfsnode.PathedPBNode) + require.True(t, ok) + fd := uNode.FieldData() + byts, err := fd.Must().AsBytes() + require.NoError(t, err) + + assert.Equal(t, cidlink.Link{Cid: c.Cid()}, lnk) + + assert.Equal(t, c.Data(), byts) cKey := c.Cid() - key := node.Cid() - if key.String() != cKey.String() { - t.Fatal(fmt.Errorf( - "recursive path resolution failed for %s: %s != %s", - p.String(), key.String(), cKey.String())) - } rCid, rest, err := resolver.ResolveToLastNode(ctx, p) if err != nil { @@ -105,43 +132,162 @@ func TestRecurivePathResolution(t *testing.T) { p.String(), rCid.String(), cKey.String())) } } - -func TestResolveToLastNode_NoUnnecessaryFetching(t *testing.T) { +func TestResolveToLastNode_ErrNoLink(t *testing.T) { ctx := context.Background() - dagService := dagmock.Mock() + bsrv := dagmock.Bserv() a := randNode() b := randNode() + c := randNode() - err := a.AddNodeLink("child", b) + err := b.AddNodeLink("grandchild", c) if err != nil { t.Fatal(err) } - err = dagService.Add(ctx, a) + err = a.AddNodeLink("child", b) if err != nil { t.Fatal(err) } + for _, n := range []*merkledag.ProtoNode{a, b, c} { + err = bsrv.AddBlock(n) + if err != nil { + t.Fatal(err) + } + } + + aKey := a.Cid() + + fetcherFactory := bsfetcher.NewFetcherConfig(bsrv) + fetcherFactory.PrototypeChooser = dagpb.AddSupportToChooser(func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { + if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { + return tlnkNd.LinkTargetNodePrototype(), nil + } + return basicnode.Prototype.Any, nil + }) + fetcherFactory.NodeReifier = unixfsnode.Reify + r := resolver.NewBasicResolver(fetcherFactory) + + // test missing link intermediate segment + segments := []string{aKey.String(), "cheese", "time"} + p, err := path.FromSegments("/ipfs/", segments...) + require.NoError(t, err) + + _, _, err = r.ResolveToLastNode(ctx, p) + require.EqualError(t, err, resolver.ErrNoLink{Name: "cheese", Node: aKey}.Error()) + + // test missing link at end + bKey := b.Cid() + segments = []string{aKey.String(), "child", "apples"} + p, err = path.FromSegments("/ipfs/", segments...) + require.NoError(t, err) + + _, _, err = r.ResolveToLastNode(ctx, p) + require.EqualError(t, err, resolver.ErrNoLink{Name: "apples", Node: bKey}.Error()) +} + +func TestResolveToLastNode_NoUnnecessaryFetching(t *testing.T) { + ctx := context.Background() + bsrv := dagmock.Bserv() + + a := randNode() + b := randNode() + + err := a.AddNodeLink("child", b) + require.NoError(t, err) + + err = bsrv.AddBlock(a) + require.NoError(t, err) + aKey := a.Cid() segments := []string{aKey.String(), "child"} p, err := path.FromSegments("/ipfs/", segments...) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) + + fetcherFactory := bsfetcher.NewFetcherConfig(bsrv) + fetcherFactory.PrototypeChooser = dagpb.AddSupportToChooser(func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { + if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { + return tlnkNd.LinkTargetNodePrototype(), nil + } + return basicnode.Prototype.Any, nil + }) + fetcherFactory.NodeReifier = unixfsnode.Reify + resolver := resolver.NewBasicResolver(fetcherFactory) - resolver := resolver.NewBasicResolver(dagService) resolvedCID, remainingPath, err := resolver.ResolveToLastNode(ctx, p) + require.NoError(t, err) + + require.Equal(t, len(remainingPath), 0, "cannot have remaining path") + require.Equal(t, b.Cid(), resolvedCID) +} + +func TestPathRemainder(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + bsrv := dagmock.Bserv() + + nb := basicnode.Prototype.Any.NewBuilder() + err := dagjson.Decode(nb, strings.NewReader(`{"foo": {"bar": "baz"}}`)) + require.NoError(t, err) + out := new(bytes.Buffer) + err = dagcbor.Encode(nb.Build(), out) + require.NoError(t, err) + lnk, err := cid.Prefix{ + Version: 1, + Codec: cid.DagCBOR, + MhType: multihash.SHA2_256, + MhLength: 32, + }.Sum(out.Bytes()) + require.NoError(t, err) + blk, err := blocks.NewBlockWithCid(out.Bytes(), lnk) + require.NoError(t, err) + bsrv.AddBlock(blk) + fetcherFactory := bsfetcher.NewFetcherConfig(bsrv) + resolver := resolver.NewBasicResolver(fetcherFactory) + + rp1, remainder, err := resolver.ResolveToLastNode(ctx, path.FromString(lnk.String()+"/foo/bar")) + require.NoError(t, err) + + assert.Equal(t, lnk, rp1) + require.Equal(t, "foo/bar", path.Join(remainder)) +} + +func TestResolveToLastNode_MixedSegmentTypes(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + bsrv := dagmock.Bserv() + a := randNode() + err := bsrv.AddBlock(a) if err != nil { t.Fatal(err) } - if len(remainingPath) > 0 { - t.Fatal("cannot have remaining path") - } + nb := basicnode.Prototype.Any.NewBuilder() + json := `{"foo":{"bar":[0,{"boom":["baz",1,2,{"/":"CID"},"blop"]}]}}` + json = strings.ReplaceAll(json, "CID", a.Cid().String()) + err = dagjson.Decode(nb, strings.NewReader(json)) + require.NoError(t, err) + out := new(bytes.Buffer) + err = dagcbor.Encode(nb.Build(), out) + require.NoError(t, err) + lnk, err := cid.Prefix{ + Version: 1, + Codec: cid.DagCBOR, + MhType: multihash.SHA2_256, + MhLength: 32, + }.Sum(out.Bytes()) + require.NoError(t, err) + blk, err := blocks.NewBlockWithCid(out.Bytes(), lnk) + require.NoError(t, err) + bsrv.AddBlock(blk) + fetcherFactory := bsfetcher.NewFetcherConfig(bsrv) + resolver := resolver.NewBasicResolver(fetcherFactory) - if !resolvedCID.Equals(b.Cid()) { - t.Fatal("resolved to the wrong CID") - } + cid, remainder, err := resolver.ResolveToLastNode(ctx, path.FromString(lnk.String()+"/foo/bar/1/boom/3")) + require.NoError(t, err) + + assert.Equal(t, 0, len(remainder)) + assert.True(t, cid.Equals(a.Cid())) } From 991fe0d4052e04bba6be6793d977af279aa1a254 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 12 Aug 2021 09:01:43 -0700 Subject: [PATCH 3544/3817] Update to IPLD Prime (#32) Update for use with IPLD prime Co-authored-by: hannahhoward This commit was moved from ipfs/go-ipfs-provider@681975ae2fde4168f9b2fefb32bf66fe736b481c --- provider/simple/reprovide.go | 26 ++++++++++------ provider/simple/reprovide_test.go | 50 ++++++++++++++++++++++++------- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index bfe6173e1..c46148c72 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -9,11 +9,12 @@ import ( "github.com/cenkalti/backoff" "github.com/ipfs/go-cid" "github.com/ipfs/go-cidutil" + "github.com/ipfs/go-fetcher" + fetcherhelpers "github.com/ipfs/go-fetcher/helpers" blocks "github.com/ipfs/go-ipfs-blockstore" - ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" - "github.com/ipfs/go-merkledag" "github.com/ipfs/go-verifcid" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" "github.com/libp2p/go-libp2p-core/routing" ) @@ -184,9 +185,9 @@ type Pinner interface { } // NewPinnedProvider returns provider supplying pinned keys -func NewPinnedProvider(onlyRoots bool, pinning Pinner, dag ipld.DAGService) KeyChanFunc { +func NewPinnedProvider(onlyRoots bool, pinning Pinner, fetchConfig fetcher.Factory) KeyChanFunc { return func(ctx context.Context) (<-chan cid.Cid, error) { - set, err := pinSet(ctx, pinning, dag, onlyRoots) + set, err := pinSet(ctx, pinning, fetchConfig, onlyRoots) if err != nil { return nil, err } @@ -208,7 +209,7 @@ func NewPinnedProvider(onlyRoots bool, pinning Pinner, dag ipld.DAGService) KeyC } } -func pinSet(ctx context.Context, pinning Pinner, dag ipld.DAGService, onlyRoots bool) (*cidutil.StreamingSet, error) { +func pinSet(ctx context.Context, pinning Pinner, fetchConfig fetcher.Factory, onlyRoots bool) (*cidutil.StreamingSet, error) { set := cidutil.NewStreamingSet() go func() { @@ -230,11 +231,18 @@ func pinSet(ctx context.Context, pinning Pinner, dag ipld.DAGService, onlyRoots logR.Errorf("reprovide indirect pins: %s", err) return } + + session := fetchConfig.NewSession(ctx) for _, key := range rkeys { - if onlyRoots { - set.Visitor(ctx)(key) - } else { - err := merkledag.Walk(ctx, merkledag.GetLinksWithDAG(dag), key, set.Visitor(ctx)) + set.Visitor(ctx)(key) + if !onlyRoots { + err := fetcherhelpers.BlockAll(ctx, session, cidlink.Link{key}, func(res fetcher.FetchResult) error { + clink, ok := res.LastBlockLink.(cidlink.Link) + if ok { + set.Visitor(ctx)(clink.Cid) + } + return nil + }) if err != nil { logR.Errorf("reprovide indirect pins: %s", err) return diff --git a/provider/simple/reprovide_test.go b/provider/simple/reprovide_test.go index 3858baf5e..e29524ae2 100644 --- a/provider/simple/reprovide_test.go +++ b/provider/simple/reprovide_test.go @@ -1,19 +1,25 @@ package simple_test import ( + "bytes" "context" "testing" "time" + blocks "github.com/ipfs/go-block-format" bsrv "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" - "github.com/ipfs/go-ipfs-blockstore" + bsfetcher "github.com/ipfs/go-fetcher/impl/blockservice" + blockstore "github.com/ipfs/go-ipfs-blockstore" offline "github.com/ipfs/go-ipfs-exchange-offline" mock "github.com/ipfs/go-ipfs-routing/mock" - cbor "github.com/ipfs/go-ipld-cbor" - merkledag "github.com/ipfs/go-merkledag" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/codec/dagcbor" + "github.com/ipld/go-ipld-prime/fluent/qp" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + basicnode "github.com/ipld/go-ipld-prime/node/basic" peer "github.com/libp2p/go-libp2p-core/peer" testutil "github.com/libp2p/go-libp2p-testing/net" mh "github.com/multiformats/go-multihash" @@ -36,22 +42,24 @@ func setupRouting(t *testing.T) (clA, clB mock.Client, idA, idB peer.ID) { func setupDag(t *testing.T) (nodes []cid.Cid, bstore blockstore.Blockstore) { bstore = blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) for _, data := range []string{"foo", "bar"} { - blk, err := cbor.WrapObject(data, mh.SHA2_256, -1) + nb := basicnode.Prototype.Any.NewBuilder() + err := nb.AssignString(data) if err != nil { t.Fatal(err) } + blk := toBlock(t, nb.Build()) err = bstore.Put(blk) if err != nil { t.Fatal(err) } nodes = append(nodes, blk.Cid()) - - blk, err = cbor.WrapObject(map[string]interface{}{ - "child": blk.Cid(), - }, mh.SHA2_256, -1) + nd, err := qp.BuildMap(basicnode.Prototype.Map, 1, func(ma ipld.MapAssembler) { + qp.MapEntry(ma, "child", qp.Link(cidlink.Link{Cid: blk.Cid()})) + }) if err != nil { t.Fatal(err) } + blk = toBlock(t, nd) err = bstore.Put(blk) if err != nil { t.Fatal(err) @@ -62,6 +70,28 @@ func setupDag(t *testing.T) (nodes []cid.Cid, bstore blockstore.Blockstore) { return nodes, bstore } +func toBlock(t *testing.T, nd ipld.Node) blocks.Block { + buf := new(bytes.Buffer) + err := dagcbor.Encode(nd, buf) + if err != nil { + t.Fatal(err) + } + c, err := cid.Prefix{ + Version: 1, + Codec: cid.DagCBOR, + MhType: mh.SHA2_256, + MhLength: -1, + }.Sum(buf.Bytes()) + if err != nil { + t.Fatal(err) + } + blk, err := blocks.NewBlockWithCid(buf.Bytes(), c) + if err != nil { + t.Fatal(err) + } + return blk +} + func TestReprovide(t *testing.T) { testReprovide(t, func(r *Reprovider, ctx context.Context) error { return r.Reprovide() @@ -195,7 +225,7 @@ func TestReprovidePinned(t *testing.T) { nodes, bstore := setupDag(t) - dag := merkledag.NewDAGService(bsrv.New(bstore, offline.Exchange(bstore))) + fetchConfig := bsfetcher.NewFetcherConfig(bsrv.New(bstore, offline.Exchange(bstore))) for i := 0; i < 2; i++ { clA, clB, idA, _ := setupRouting(t) @@ -215,7 +245,7 @@ func TestReprovidePinned(t *testing.T) { keyProvider := NewPinnedProvider(onlyRoots, &mockPinner{ recursive: []cid.Cid{nodes[1]}, direct: []cid.Cid{nodes[3]}, - }, dag) + }, fetchConfig) reprov := NewReprovider(ctx, time.Hour, clA, keyProvider) err := reprov.Reprovide() From 8f0db8f18428127afb5e6c114ada8c751d5116de Mon Sep 17 00:00:00 2001 From: Hannah Howard Date: Thu, 12 Aug 2021 09:35:49 -0700 Subject: [PATCH 3545/3817] IPLD In IPFS: Target Merge Branch (#67) * update go-path and error message * add node api for prime interactions * use go-unixfsnode * update against fetcher Co-authored-by: acruikshank This commit was moved from ipfs/interface-go-ipfs-core@49cdff8024e607072e57a7f7556e9875d2aa0412 --- coreiface/coreapi.go | 1 + coreiface/tests/path.go | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index 12cb166a8..aacda0459 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -4,6 +4,7 @@ package iface import ( "context" + path "github.com/ipfs/interface-go-ipfs-core/path" "github.com/ipfs/interface-go-ipfs-core/options" diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index 2d9497244..5a249fabf 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -2,11 +2,14 @@ package tests import ( "context" - "github.com/ipfs/interface-go-ipfs-core/path" + "errors" "math" "strings" "testing" + "github.com/ipfs/go-path/resolver" + "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/interface-go-ipfs-core/options" ipldcbor "github.com/ipfs/go-ipld-cbor" @@ -138,7 +141,7 @@ func (tp *TestSuite) TestInvalidPathRemainder(t *testing.T) { } _, err = api.ResolvePath(ctx, path.New("/ipld/"+nd.Cid().String()+"/bar/baz")) - if err == nil || !strings.Contains(err.Error(), "no such link found") { + if err == nil || !errors.As(err, &resolver.ErrNoLink{}) { t.Fatalf("unexpected error: %s", err) } } From 21945952f68690addf556dfe69ce1c722e353a61 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Fri, 13 Aug 2021 01:24:24 -0400 Subject: [PATCH 3546/3817] feat: do not special case downloading the first block in the blockservice fetcher This commit was moved from ipfs/go-fetcher@101960bbcd62556c4dabe5621e8de3a6873c1b55 --- fetcher/impl/blockservice/fetcher.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fetcher/impl/blockservice/fetcher.go b/fetcher/impl/blockservice/fetcher.go index 237f3f955..3327a4ecd 100644 --- a/fetcher/impl/blockservice/fetcher.go +++ b/fetcher/impl/blockservice/fetcher.go @@ -97,10 +97,14 @@ func (f *fetcherSession) NodeMatching(ctx context.Context, node ipld.Node, match } func (f *fetcherSession) BlockMatchingOfType(ctx context.Context, root ipld.Link, match ipld.Node, - ptype ipld.NodePrototype, cb fetcher.FetchCallback) error { + _ ipld.NodePrototype, cb fetcher.FetchCallback) error { // retrieve first node - node, err := f.BlockOfType(ctx, root, ptype) + prototype, err := f.PrototypeFromLink(root) + if err != nil { + return err + } + node, err := f.BlockOfType(ctx, root, prototype) if err != nil { return err } From 0ab30632be7adcfd1884213ce00793ed600c3b78 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Fri, 13 Aug 2021 01:28:50 -0400 Subject: [PATCH 3547/3817] fix: delete BlockAllOfType helper and fix BlockAll helper so that the selector now always compiles This commit was moved from ipfs/go-fetcher@e91ab79237f6545ce880e2ae7a69aa07161051fb --- fetcher/helpers/traversal.go | 22 +++++----------------- fetcher/impl/blockservice/fetcher_test.go | 2 +- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/fetcher/helpers/traversal.go b/fetcher/helpers/traversal.go index 37feeeb50..87b631d1e 100644 --- a/fetcher/helpers/traversal.go +++ b/fetcher/helpers/traversal.go @@ -5,6 +5,7 @@ import ( "github.com/ipfs/go-fetcher" "github.com/ipld/go-ipld-prime" + basicnode "github.com/ipld/go-ipld-prime/node/basic" "github.com/ipld/go-ipld-prime/traversal/selector" "github.com/ipld/go-ipld-prime/traversal/selector/builder" ) @@ -21,30 +22,17 @@ func Block(ctx context.Context, f fetcher.Fetcher, link ipld.Link) (ipld.Node, e // BlockMatching traverses a schemaless node graph starting with the given link using the given selector and possibly crossing // block boundaries. Each matched node is sent to the FetchResult channel. func BlockMatching(ctx context.Context, f fetcher.Fetcher, root ipld.Link, match ipld.Node, cb fetcher.FetchCallback) error { - prototype, err := f.PrototypeFromLink(root) - if err != nil { - return err - } - return f.BlockMatchingOfType(ctx, root, match, prototype, cb) + return f.BlockMatchingOfType(ctx, root, match, nil, cb) } // BlockAll traverses all nodes in the graph linked by root. The nodes will be untyped and send over the results // channel. func BlockAll(ctx context.Context, f fetcher.Fetcher, root ipld.Link, cb fetcher.FetchCallback) error { - prototype, err := f.PrototypeFromLink(root) - if err != nil { - return err - } - return BlockAllOfType(ctx, f, root, prototype, cb) -} - -// BlockAllOfType traverses all nodes in the graph linked by root. The nodes will typed according to ptype -// and send over the results channel. -func BlockAllOfType(ctx context.Context, f fetcher.Fetcher, root ipld.Link, ptype ipld.NodePrototype, cb fetcher.FetchCallback) error { - ssb := builder.NewSelectorSpecBuilder(ptype) + ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype.Any) allSelector := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( ssb.Matcher(), ssb.ExploreAll(ssb.ExploreRecursiveEdge()), )).Node() - return f.BlockMatchingOfType(ctx, root, allSelector, ptype, cb) + + return f.BlockMatchingOfType(ctx, root, allSelector, nil, cb) } diff --git a/fetcher/impl/blockservice/fetcher_test.go b/fetcher/impl/blockservice/fetcher_test.go index f8c2d0082..42d31d436 100644 --- a/fetcher/impl/blockservice/fetcher_test.go +++ b/fetcher/impl/blockservice/fetcher_test.go @@ -269,7 +269,7 @@ func TestHelpers(t *testing.T) { defer cancel() results := []fetcher.FetchResult{} - err = helpers.BlockAllOfType(ctx, session, cidlink.Link{Cid: block1.Cid()}, basicnode.Prototype__Any{}, func(res fetcher.FetchResult) error { + err = helpers.BlockAll(ctx, session, cidlink.Link{Cid: block1.Cid()}, func(res fetcher.FetchResult) error { results = append(results, res) return nil }) From d38d37bac5204c68a28dc497675dab9789f68a50 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Fri, 13 Aug 2021 01:32:00 -0400 Subject: [PATCH 3548/3817] feat: precompile the matchAll selector This commit was moved from ipfs/go-fetcher@760b004af53a8be83ca201343becdd26a392cd6a --- fetcher/helpers/traversal.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/fetcher/helpers/traversal.go b/fetcher/helpers/traversal.go index 87b631d1e..0bc42acff 100644 --- a/fetcher/helpers/traversal.go +++ b/fetcher/helpers/traversal.go @@ -10,6 +10,16 @@ import ( "github.com/ipld/go-ipld-prime/traversal/selector/builder" ) +var matchAllSelector ipld.Node + +func init() { + ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype.Any) + matchAllSelector = ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( + ssb.Matcher(), + ssb.ExploreAll(ssb.ExploreRecursiveEdge()), + )).Node() +} + // Block fetches a schemaless node graph corresponding to single block by link. func Block(ctx context.Context, f fetcher.Fetcher, link ipld.Link) (ipld.Node, error) { prototype, err := f.PrototypeFromLink(link) @@ -28,11 +38,5 @@ func BlockMatching(ctx context.Context, f fetcher.Fetcher, root ipld.Link, match // BlockAll traverses all nodes in the graph linked by root. The nodes will be untyped and send over the results // channel. func BlockAll(ctx context.Context, f fetcher.Fetcher, root ipld.Link, cb fetcher.FetchCallback) error { - ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype.Any) - allSelector := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreUnion( - ssb.Matcher(), - ssb.ExploreAll(ssb.ExploreRecursiveEdge()), - )).Node() - - return f.BlockMatchingOfType(ctx, root, allSelector, nil, cb) + return f.BlockMatchingOfType(ctx, root, matchAllSelector, nil, cb) } From a1f3f890866135fa4f5815a67043ec76907a73f4 Mon Sep 17 00:00:00 2001 From: web3-bot <81333946+web3-bot@users.noreply.github.com> Date: Tue, 17 Aug 2021 10:54:30 -0400 Subject: [PATCH 3549/3817] sync: update CI config files (#34) This commit was moved from ipfs/go-ipfs-chunker@494c66c779132d72965f085ab9cb3bf7575a01ea --- chunker/buzhash_norace_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chunker/buzhash_norace_test.go b/chunker/buzhash_norace_test.go index 2565a4c53..7d1d03d6d 100644 --- a/chunker/buzhash_norace_test.go +++ b/chunker/buzhash_norace_test.go @@ -1,4 +1,5 @@ -//+build !race +//go:build !race +// +build !race package chunk From b722930c08cda76a887769564a94055f41268c38 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 17 Aug 2021 13:10:26 -0700 Subject: [PATCH 3550/3817] fix: check errors by string Unfortunately, we return errors over the HTTP API and lose the type. This commit was moved from ipfs/interface-go-ipfs-core@98e72571bc4514239cbe7bba4321ab5da4194366 --- coreiface/tests/path.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go index 5a249fabf..f6d05372e 100644 --- a/coreiface/tests/path.go +++ b/coreiface/tests/path.go @@ -2,12 +2,10 @@ package tests import ( "context" - "errors" "math" "strings" "testing" - "github.com/ipfs/go-path/resolver" "github.com/ipfs/interface-go-ipfs-core/path" "github.com/ipfs/interface-go-ipfs-core/options" @@ -141,7 +139,7 @@ func (tp *TestSuite) TestInvalidPathRemainder(t *testing.T) { } _, err = api.ResolvePath(ctx, path.New("/ipld/"+nd.Cid().String()+"/bar/baz")) - if err == nil || !errors.As(err, &resolver.ErrNoLink{}) { + if err == nil || !strings.Contains(err.Error(), `no link named "bar"`) { t.Fatalf("unexpected error: %s", err) } } From 5f33f8c06a2d9866fa83362934a6156f0b87e6bd Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 19 Aug 2021 14:21:50 +0100 Subject: [PATCH 3551/3817] Assert `OpenReader` from file does not panic after closure Write a test that asserts reader instantiated from `OpenReader` do not panic and instead error gracefully if underlying IO is closed. Relates to #211 This commit was moved from ipld/go-car@ed281f92b484d92584a32fdf9b0b5ccd1e4588cc --- ipld/car/v2/reader_test.go | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go index 79d6f855d..02906f1a8 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/reader_test.go @@ -178,6 +178,40 @@ func TestReader_WithCarV2Consistency(t *testing.T) { } } +func TestOpenReader_DoesNotPanicForReadersCreatedBeforeClosure(t *testing.T) { + subject, err := carv2.OpenReader("testdata/sample-wrapped-v2.car") + require.NoError(t, err) + dReaderBeforeClosure := subject.DataReader() + iReaderBeforeClosure := subject.IndexReader() + require.NoError(t, subject.Close()) + + buf := make([]byte, 1) + panicTest := func(r io.Reader) { + _, err := r.Read(buf) + require.EqualError(t, err, "mmap: closed") + } + + require.NotPanics(t, func() { panicTest(dReaderBeforeClosure) }) + require.NotPanics(t, func() { panicTest(iReaderBeforeClosure) }) +} + +func TestOpenReader_DoesNotPanicForReadersCreatedAfterClosure(t *testing.T) { + subject, err := carv2.OpenReader("testdata/sample-wrapped-v2.car") + require.NoError(t, err) + require.NoError(t, subject.Close()) + dReaderAfterClosure := subject.DataReader() + iReaderAfterClosure := subject.IndexReader() + + buf := make([]byte, 1) + panicTest := func(r io.Reader) { + _, err := r.Read(buf) + require.EqualError(t, err, "mmap: closed") + } + + require.NotPanics(t, func() { panicTest(dReaderAfterClosure) }) + require.NotPanics(t, func() { panicTest(iReaderAfterClosure) }) +} + func requireNewCarV1ReaderFromV2File(t *testing.T, carV12Path string, zerLenAsEOF bool) *carv1.CarReader { f, err := os.Open(carV12Path) require.NoError(t, err) From 13be8273ebe30744bf6c8a5fc908afba7d7c3dbe Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Fri, 27 Aug 2021 15:04:24 +0100 Subject: [PATCH 3552/3817] Return `nil` as Index reader when reading indexless CARv2 Fix an issue where if a `v2.Reader` is given an indexless CARv2, an invalid section reader is returned. Add tests to assert fix using CARv1 and indexless CARv2 sample files. This commit was moved from ipld/go-car@1bac13d05359f7998e694dd55741e6ed3cf8be74 --- ipld/car/v2/reader.go | 6 ++--- ipld/car/v2/reader_test.go | 24 +++++++++++++++++++ ipld/car/v2/testdata/sample-v2-indexless.car | Bin 0 -> 479958 bytes 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 ipld/car/v2/testdata/sample-v2-indexless.car diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index db8f82105..2f7cbb18e 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -113,10 +113,10 @@ func (r *Reader) DataReader() SectionReader { // present. Otherwise, returns nil. // Note, this function will always return nil if the backing payload represents a CARv1. func (r *Reader) IndexReader() io.Reader { - if r.Version == 2 { - return internalio.NewOffsetReadSeeker(r.r, int64(r.Header.IndexOffset)) + if r.Version == 1 || !r.Header.HasIndex() { + return nil } - return nil + return internalio.NewOffsetReadSeeker(r.r, int64(r.Header.IndexOffset)) } // Close closes the underlying reader if it was opened by OpenReader. diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go index 02906f1a8..a0c6e3cda 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/reader_test.go @@ -212,6 +212,30 @@ func TestOpenReader_DoesNotPanicForReadersCreatedAfterClosure(t *testing.T) { require.NotPanics(t, func() { panicTest(iReaderAfterClosure) }) } +func TestReader_ReturnsNilWhenThereIsNoIndex(t *testing.T) { + tests := []struct { + name string + path string + }{ + { + name: "IndexlessCarV2", + path: "testdata/sample-v2-indexless.car", + }, + { + name: "CarV1", + path: "testdata/sample-v1.car", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + subject, err := carv2.OpenReader(tt.path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, subject.Close()) }) + require.Nil(t, subject.IndexReader()) + }) + } +} + func requireNewCarV1ReaderFromV2File(t *testing.T, carV12Path string, zerLenAsEOF bool) *carv1.CarReader { f, err := os.Open(carV12Path) require.NoError(t, err) diff --git a/ipld/car/v2/testdata/sample-v2-indexless.car b/ipld/car/v2/testdata/sample-v2-indexless.car new file mode 100644 index 0000000000000000000000000000000000000000..3b160b6fa75fad21af1c21778aa13c8ae145d2e5 GIT binary patch literal 479958 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
    {1kxno)d5r;&4t;VAqjNzP=e` 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

    hRd> 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

    vHSrHL`4Y!))mrU|+My=J*?NCLvdFH?W^2AuF;5 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

    ecf&5+ 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 z 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í#z)A5=3wvHh0L=Ai;@$CD{9xI(x5s^)mUAa=`_xPGQWUQ@f)oc z5jOM|ZHPd~d*~y7!pg$*s0s8)T^8v%^)4+`yH)`u9O&;%J?0OxI-Z97G^8wYzSpw(<1UFy_*ORE zd7xX}y!SK&c|I|dv9%2yuuty4;CfWda+AzXTsI1L!`AIG z+6sUxEftID12IrZ)1%-M_Ft5IVGaNpI{`=sf2f$Sc z?yBmQ|GnE7Ql98`k8jc0mrINhEy0ndmvwz7(g18R$$3Bu6R6&2=YjGC~(WLe0``yAsMJ!9^$%0Z-=-dsWcVaic_t)KSTziqjKu>&P?f-%+ zXmYm?IM8mXETcUz+a;|aWZ7-7>ArVPSoRTNfs4992=;8Jl6I%@tB%DvLsL_f1M_hGJn_^-_iS9^9^;p!UpH!sbVK+ZhB04Yjllrn!keVSCNE;=FlmDgX{ZWUp0Q>9nTwS?CGg!>B#8^rWe8c2_F-J1`x`?U z*dOUH@A=DnaDCUc!+hb5O$b5u^LWMy(q`x+zrIu?vEEn)22lD1>s2?HJAX-~d;#xw zN4Z;L8oB@;$IQw#qP>4b+QfODOt{36x^6g3S7}dnMT2E3$=G72J;iGdsES<-37AIe>a zE4c%yl+lNV#g)_0aK@J57OHd<9f@r9`qG>Cd*r-#Bh-bhJ%Yq_+qryRH-oe7{UnWy zy2rQ|vj2|sK`tRPmo_0sT`$5!uYKhE_eaQ`Zr0yJM!gNujPay0FE1cG`9%Z{B5}f^ zUpS^Tu|qg-jDPw`QavCn9wb^ZkHa*EiA~)2_8e`zd7YHH&tadGfD#3_Zx|TSES%5S z2M}%m4n0_NRagAU&}8vbKwReJ@Y;~4*`A~d>?+g06P4?os}saHf1-(#GW{n%pNM&u zFzyVMi)IC2J&DGQNj=}Nck83=AckBwS~hnZ{O@U=eH5i%?rsV>mvYyA&Si&t$hY8d z$xCw{XyFdlW2hou0SdA5!TR*U3wJiIr9kcwi<2!jT2cP04cq9^T{f(4>}3p>$Rzpb zGao|#eF^~rD5T6hl#Fyj9|}78Y#h1JIu_ln`Rtg>%axx>MakL|8b2B-B;JZtA;ial z<3d6)BNX_;C5Msq_p_By3{G_jf(Kz%qXCP_RQS|kobBYi?3%laF>_u@?wUCZpKNO!e;71zs?x(>o$}LZV zQ&MsV*Cxz0jvR37C^$2%3UQC{0=0(XBXwUz)PKW0jxPo&!^6ZSQP`|Gd^7QmZq6DM zHTIqTT46UlQd!>k;$qzhjGfR@q`9^GoKoWL(rBE!p=}P9D9-vKb7sU^I2^{~knO`t zb{gus9wMG??#Rbf?s!*j>4G?sxgZ2~&-_h2Ln=CzHh!M!`c7sJScKdSrnCYQakC>V z^}SJ~hd5EeoQtv4v)ke3)!hiKgK@;p%om`LT)e48O&O)tsB5AisAm}&PQHWvA*9xP zLLf?>hSQCc=_@7){R!@^9A0mRZ}8=7Xj>%ZY5BV6I@3J7$e18^lH+~Ppr}5{C$8~+ zS0ILXQL5w1lUv~4^L4IM#ssDh14fWrhf=uHJ1F-!T zB~k?BU|k!5RzmoqgyY}Wc1K48krPGhqX3EUn_w}1FaysqRWb>iqvxU^A@meYqzyVF z6YMS!bOO*^<eiXI*J{N7 zKHBDYpy`U|HQC|_j)LU{>v+~Lw15UhZeLVNPNworge>iD4O6w)1ugdLnyvd*%Xzj) zziYLNGj;~V@oEhvM@a0Fzl;2TTKm5w5&&z`AiVnd0@*`Hdnf7$lbHkfs=r~e13GY~ zFcT7mn)SSdRm!q}{QDR|zrtNWJ498&X;~-Csmy)Aj(*l!b4mb+swwlE6!ciG&vFQ! z`cR)r1A_MKXMFcgo%bWS<~BVk&msn-)F-^Xs77TkHM&hO640&7ADgB$iL0x%eNY=dEJ>i32@ z)N1@HU#0#AW;>_=^1g_8`@-LFif{jC!m zLk;ADT3N)YG?7Me>^W_Lm=>noUBcz3ND9~G7xFAh_tNLi=ZXz---!CXOdcBMxl_q6vOCXpU9J&%?PxN4dwVr&559>N4~Si z=M!sj-0zkPZajjwC+hDM)5Vq(G*nOr*$?ZSo%IJDqYO zaCWFHbaVcDwADYYJpiu%Y>-KE%>FGIXD&)|7zZbGcrU(%=nfM)s>~fM0V=HQu5)$Y ztG(j=i~YozPElKMx62!Y|0tZSI2@$Zj43L0`wII4K7<0#!42-+^SY=ZajXp_wYD#Q z6MJ$7k}M^GZND7=R};T43IWP}V@ju6A_g(uIX4U4L(NZ1(!_4iG|SCjH#j!r6A=y> z@)NNtN3*f>KLc$VICnr3DVeDCMK78*Domd41!n?BU8}(z5gucn*Proh~wf?+SV5k{0P8#YpfWp!jX9CG8E=)!w_sr=q6>t;X)w5J@Q z6B}nrBMW(;M=Qpiz1^^JY}wKRKHtLf5JS=Wa{3~<{Sb=R*D<#H|}mH_5A z5%4@e7&W3o6e6n|BzybfwR#9}ZdwVDF-)y|#2 zmOOYRUHO9k(z6$(q1S!^DHL}K^lW|d4|-%ph>}qb;)UQXjUy2$O^u-V16R%$nnJt^ zxGt}p_#C$E7;1Zx>P(O!-2U6+&@llL6uLjV>pvbL`<$Fb2N~9gr~sq&R$B${q+J9a z1{DUaiyd7J&|C7yy4T$39R3s7F?xjMS<@rG{El6rB)hw5Yrum4g2cB=);(b$$bD8U z`VSoH4YEG^?GfE-ikjJZ+F8 z8Z9Dp&W=2cdy$gI^jJe>t~+I0hZKmM2xbqR{e<^B$!J4`3q+hUQL>(%%vf!5QKD2Q zUzmf!#>OiLk%;*=S^C!@ZVt0P>7qV0y6e!)a5Kf)h65{o`@2&9r^obHl8bu%68oJ% zW}L1k>_8QGixJzublGXSAcdxhJ++Lw3vZ7XtS~eOm?SA_-g|ENCk|8@*cS^WI5!+!jQ>}0LKS}EtuSpO3}>R_O+H7UoJv1oxN9D{eHBY9u`>J1aW zs&k;N6KXv7)#w&;oW+o&{XweG*38m z!Vp8rz<%{kQpr`3Ilz8362mX9rPwVcN|c=L(ces+J;s zVh^uU<5Z|%nppuqxdF7Yc&dPJeeHrCtpf?|KVK@MtB}Tu4<)d$0Y@#n*2UI}Q4vd=D-~}SeHnD{1=^cAGcS_C6vrgV zKAq??m8tj!4z(O^i&(IMmwm2MsfJkG;1cT6Ekk|$bu~mt<-A4dTowdOe{D$%_NxWVOg^kQom-?Yloppg$Vw z-zO1ifJBn9m{q*Q%$r)I43=^bSvOdgAwcu)@ONPi_>ny*U-oE|j#!-N$Q!@bh5Fj5 zjNG6jo43jney&w>Z^c;F@(qw*O{d%03G1M}G7Yb+N~E`8SZ&f$NBn;Lq_`4kx$V%AFV_zGF+?Ocj7HL4MD0+_r22$SYqS1rcXYR?H$ss zo60P$(N|Xh4_szA@hf#Z3A;x_kQ@>GrvJvjg&j*c_fLr;rgV>2SBG*``aj9s=V*%{`l?lGvmzXFKT)JbReF**c!9@swEA~J? zY$>PEM)aoltKPDY;@jmfgHL+JvA-Jy*3EAG3vSjZTL2!4{7-BD7hG|) zg5E2t)r=gZTbD(XKZh@nt#lGxi(rQf9~(do93H;ck6o1Eg8e+tCr9DCu;qs47mLu( zIK&yW(i&uA>A`@6nd_*qXTzLNy2dKC_4TP?o*nLO82yA{9!n7Y$b$obE6tX!7N%=} z#8w*axxvfdQpfUU-q?*fdpaT+@_Tc^u|dW5VO5kj;Q~2*!j8bRQQEnWT)+XlDAb@R zm_B5H+T$hT6!jb}?r&*A1O+Iu)gke;rSu4f(eo7^k>gp-8t73+WEYIau~aLuwo3_k z5Kf0QT@wm;MgBFzVt%AYFW<9 z#Yv%5f7ec=5ib`Cz(E*e=*^dAa}$SBrmNimWo_xJ$J{QnM-OKFyoiqUG?njr)7c(C zxz|=F{O}xVVkEo}zrj$ocnFpxR_0pr2d+K)e)ANn5#8MKcd3+oY8dKmuaxm4_(o2^ zi7*_21hqdOaz9qeTo;O^FEF_B%j8za`dZ*Oku^1o0~h@$f@slb=&6vTA5d{xT1|N6 zAM12S^8SiW=UwfVwa4gA#|=EqeXQwHw44XvP-6;<=EE-{H}FR>^*qpzp4orATN7%? zZp7l4=?(*5c9ajo(1w4U7yGZ{5c>+iVIEB?m|ytJ{`FTj0w(1}t$XT-m8L-Fay_cQow zCuXnx+8M)%@caV5vpW&wwB-6Hw^JX#{Xypjy_15tOs6*D3A%b&}!ruV&@H(Q;Pin*?GDY%+$EjKbDx;Bbuow}6 zzD3CCl~g|wjGM%ECVZAMn|aP*|LTWw2Q?qNfJ8r^-QT?_73sGU<$k0nI*1T|e+tD^2^{cSbjrZiMo%m)c$}IZ`$d^_%HfcaVD@fvcQdp#W zjxB5VRYnD`?`!@b$#1U{{zpQdoL^jDmfaCX%hg1{`yA8;bVFVix{&yVGfhD36+&W^ z@1O(JHDrHUks#WyT=1ToIsj2U(>-fqc4oe5UT}%-=`Lc4;{B(Mm7fL*cZ?G2 zHo;tyYPm@jEFLif5>$0w9!2()9md81$IeRymJOLKk{o!@tThlam5;{!G@CwmAC!Ki z%1BR6NS;x4T1BPha;ptw!65c4wrKpmx}q?P-AbKPtE;5dZ~&evPH=Sd|D`S($FUG+Sm% zzKx22spE^!S7-*7f4|;kedw*VtQB95mShxXsU-N*Q%=P`g%MDQo+ol^6n|SPVU|xY z3upkGaG%xTL=Pqy>jA)=1!LyrYp1#(59h06w8FPxEZqchNANy&vvoP?+ggC~(<_!&v z=TPm@%_SPQ7ib;<|4HV$w+b2VH>xdbdh8bQPDJS(?W}LWTN4=OsP$#o2o0471eG%&&LRyf;Xxk9fE*NUtF{bV4gZN(?hBtWvFgWvXXt-g`@i67vDe9Qr6_=Tmmh!6FVSTRdNf-t z<2e0MCQfFqtGMv%j)640kWP6XPxe8jLj=f3{hlr42o>RNeQ z5Htvt*8pOGW$t%NbJEDl^60>qVG} z#@~+(s9`nqI#WO6VcU2&ygyHyO}K<1h{Em4AspxF_{Sd$;#arAodG{$z!cIas`Va` ztYg;o4?3m^o$qmX?Xe=8=x>7yOV@b@LrWU?2h>F(BGE#yD7e-*C@v*(F4aHF#WySh zxFxKsnmtp=CyVN>r2uj=GWzPZgYhgJO5woQkw;rmF`0y>(jiRuq55 z?jIZ8l&1PwYHmn9NVXKXKh>I7VROKNY5mNzI~HV{RQ*|jg8Bo$7>ABI{6;4k=50`u zNE{JevBx{10=qxI{*;h*SpeQgs*+u8os$+wCrMPO1`EZ;&D4bn`t|DOO1&q=vNqWl zS8vGo`jsTT+UI+bSv-%v;7?z#El(TZf-gX%+7NmQ`u_44NZ zpL{W5B+?IMPXDB`kh~`jrLdum4Qb4WSURHz6 z?8-XgI?^SDo{z-6Zt&F<5Tf7e(UriYHbDe)KYZ4l@0f85)+N!r-~N-KMP3L<9n=On zD(78WvpAaBxv_KbEk%3Z+R;_JA9r!-0Zs8gy)0x!_a@|^cQNSr7&YlefB1D7`kA)Z zdyoCT6-1F*Y4-m;!DZ8)k{%%roiCQ+)tYpV5Dz!}P4~h_OPP~nlqOSW;1M8^Nee8> z4`!$v7Oy~mxvHE6#i1nLtM|6L+*!M4@?briIS&OAC_woT`tOs7B|sv9oyUz|-mc|p z32>g=>SE&^2Y{~;slHtl-Pk}Xl}Yc|La7uKHTPj0$n_#e#}qyzpuO{WXTo-{0d+5* z&ZB*k>5fx7^Qwt1nIkY-TumSmhmtRw!{?!yn$zd;35j$6dos;Gt^HpTiDbpDVAu-u z7#7qydSJk|AJZ*XBC9RZJtSrM()HdmF0kHl0i*eZ51a}%&woFN3?l3piZx}PryjB@ zR`pFaL8@u6AMN*>Lnxuc#@gIp4tt&J?FE~zQtl)H| z@e8bQy9}9Rd*|IeXGKZWb~gjX^7DlWPXhZ_vZ7*Vo7$E{R(yRZG473>WTzQ9t`}af z*Y|xHKDEZPArJhLv5WcrSJR%KwHULAR;cR|%@;;8*wrv%cZs(8)k}Aw7Bc9pvhv(< z1EX&A7@`PgG&Su2;7~pMm5zxp#I9ORv}4k=^1a$zX{g_NNolww3;9Z*vx%cNK1HHC zp(JXgfkH9&;lfh4$<@H{kJ~$ysER&IC7tDxgft0Pj(m--FwkebAwpPfcS-_@3yWZ*Rd{7C2PR3G};qrsk6*e864xp5aQjKAFSSaNPFdt2nn zYRf;F%_(*X3azgyk-U_yW2q~B5!3X`@wmYjG3?!5<0JP-w#s!k;YWG$?Tyez)MW1# z?ec5k*c&}eYTAC22*&&wTK8OQhtVQ46hU36iwJ5-oMtc?Tb&b_|q| zAo?BQzTDGwL|w0;EfhNbn(4s{bY0sVY6T=*rhQJ2<&tw|5>dWE*<{q0$ zGPCFv4>m9X>mYp|uBtC$zL%>6Vy^&+fd1EH8h5@QO6B~RMjID0D*8c!Ct)0piKWaT zzE||Rp)4GbA2+0uUMnC9W+8!BC7$5QYB@kZZl={iY9p4lHgyTo;e&h}9wRKEJ@)c_ zjl*r0CerjchO{SAjwOm2e)V8bT*L+>V*sG0Tm>wO557tF;7o2IzBprr4+DLG=D15d zp}GjF+k1Lbv3dl$Py09-^53T>K7g7+LF9HK%M`R@al&eT#o!WVfZ^ag5^QO~ylN$S z&l^;2oSTs8a1ep<ax25~M;t#ZH7nOBRRdnT zZGK~qX)kXJe1LpuVTPJ@fx(dd!jB=+42R4q^iHMS@?0)cs0Nx0qo4%Yb0WO6XrC=7 zcml{ntr5-$UmavOIF5l}pLM-H8;%$&;WaKSXm!h!eYQ+(uCk@k+R%2W(JgEwUBKUS zOYy*<&zV1?%H~HC!M1bwC1A{~WtX-M)YIE0#i;yT7HU1*rYvr_6ce5=E# zT5liVfOl0Zkosgq(z#&nA%@F^;KW58M|Q9r zCrTLIBk*ByNJjEw8THuH-Ma>b=(lCycZe9j9`B#jgcMpo4#xWVf1PDm&~=76(X0G< zd2o}CwD?UhU2pRKw6d{ikaKQV2ULII744wVSo*^E|CseTX zArI`0kbm2`nn=&G)nFI}unqy>vI55<`vBM0;T*WtBuw;Pa8B${7DkS?>D<@LwPe|% zLG>-r?y`UNh7_zl09-+Qt#{7yd{$v~X&^5cI6;rv6>@vvRu7|n7pTBWV+54mqv;*V zY@nhTKSX7LNL?_&h&>K|6%})v@0v}2j$jPiC27!*5f2b|DpQ~=hG4f56HI;W)r*Qe ztenr`)>HT!u76s409@^$et=_#x}&4qQcgn*p%73OdpoT1kZx^(?(f&0v_uQ^-&k}6 z-iPpV^5brf89vq8mbzA8+PEZcBjnl?usyTyCCUqip$XEXD^^Xnn>H;Hh9mEJN#7^q z5Z6vk-qU)O$dzaV;0oYq1u~5ot=$J3Fx_*PY$^Tb>*Su*2yT(lT$T3J7JtzFc@ru7 zka7JLSIMK0d9bz^avli=QOS7m)=e|REVFbzrt%Z668MuWA2-Eo&v}Rep1hYSf!}9H z0t)Oe>zYtMJT~sy1%L@uBU%ROO2|FHXQ$?HFMQ_h%m)}L=gX7qL%Yi^?S550dH`3` z(_}S%deL423mtH23*SI#5Sodx@(R#Ep6;{f?|!hJdDVdeM&>=!m=6BL2LcsNKl-Sy zR}gnB8+dQyTIUJj@79z-zHK)))zP9dNYs1I|K1-!-$Fo6RYnqY8!{It4+NhQ4&^L` zDEEV@JTJN!f0)R_^(!XLQwNAN-4 zLS|UoI)4Us(Z08MDLp)PgX_*!q?B_8rK(~4?`^#9Bt+uZ5+?4 zY@$KrBUOTSCP?-GxcE-7K6cv`?r+0J#1!+cR|8b~Hv_;d(Ym}ho1 z;P79Uxv-i5a76?n6jmltV{gYNq0BO-S>rao2$(2C$uJ8^;0FnNij44REQZe+rbog` z4xK;UN<$d7{}4dCW+4g@Xyh}lb&~VF?qm~OdR+~M$|>^Z9SYQp)f~3s9kHnDl(ptq z`~5du|Frgh!4(qcJZ(#dPiX))?z?z1Lu?deW$L~N%_sgET#_!|erdAA3&z{A=zE1x z55EZ$&x3F)WUk;Q&ahaLb97__FHiM0bL5Y~f!ENASV;5BuqL$G9Az47cVuX~L|!oI zWdHzIWEM$2n(Z45EfVCrnwk|^06y4sv12NIW9UZoO#W(|?(*r~NLEEl<|koe*y-ef znCon-6DF<3%#`Z1*M$J_-*qfBmW24w?~0RfN@Mte`@FT4H=Y7O?jQmkZ&J}2U}?`^ z-{>bpWs-0q)Fd=T#_>*yWM(s_%R;;OhQA~q8beOFV%0uRs1yy%BDKcsC5cAGk{#9> zT0Crc!$Q9zo+vwBUT%HKHQ7^vb>h%3U!J{0Wo1=&3Gpgg0^7==SJ%sFWweqRPB$2}{kPIOW&(<(C1^b=Dj(Bwm4;Rdo)#F~l;M zGsc}pMb0$B(oD7nxeL*FDrNJ0j+J0j1BP4V6CmrH7i9TAaD_soTrCP1T_hQH8)@4# zS!RwzL=k@nF-XLG%(Kqz!^9@~?|sUluDJVBkjtl)KH+Z>Lba9fbn_rfuQ;xlsIgN2 zkGgY;u0!h@c5FLoj7E(c+qP{tPLsyAZ98d<#tB9t=<|>3ucLd8R#14@#HaDC{{pC0+52AGt9_ z<}NxE|GjjylFq{Dy9Vy&js;}Q3x&E5?f#A-gKR+(p{FsAdbEWMW_+mqU@&W(mUxa9W%d=Nz;P?^4h1tXM86Eg30u8O2VoVzG5Q7z5R-yaE*g!cH+K z-;{-q)8paoEvA(VjKq^dkk_nMR41VAC4r8H*br&_VW4qNwzp5rXd8dnyGUED!Z-RS zV~q}T_j2u%Knx(KUp~!^>v%NmYJwt5se*(pa`k^B{2gtWcc+@}B$q}MO%&$~^Iol- zSaus6_bEdpmeRmv+}1^9&*eH%Ix^znt`_|A0imvgOuX{4MAig2)v_l#2^u?^H*m!T zw>YB%KjM241ZG=T%F6eW6?_?CuNA(0R}39d!4qN8>F-L65uy@ay+%2Y^&^<`x7gg7 zx>omTVeC`7SoP|Qdfz-G!VSa%QTjBCgyOB}6h_z5A_MrW^CXk7qWHVYSy;M|4K0ti zEQxxFW?|$&aOey*gM3s{z)&Hc8qW3s!;(I|FN$7ox<=PrjT>`FyC#TBVb)urlt!G? z@~1$z^heU%GX;N_g&|C?>g<1ho0^IPx0B?J71Kx9!7*c&?-gEb1+)CFmNbWUW2_|p z232a@&5$QKs%4UcD!(|ty5jJ1HXgl2QH>T_k#CBG)fKmjHmT^1Am8aUb(pnB6|H~x zP#39j1Knv9xk2Q}cZi|*RZnY*89WluxacrPd7zjSF%;KZ+fcaa8p?5tmKQ-1xNleb z;Pgf(RCb(@NatW&~?2qK4YQ z3T402D$t6a(1GIBgmA7C#Q$Wh;v=QTX)ec#i}|2oHHuRTphD|{yd1>*_dSB|v+o3y z|GzB)Tn~a!>@BALg>(r?O!c7>MH)`lRt0l!EwasvY<9ZA|312mc2}lp4zV_nRIgo_ zkl4S#-SSt5Aw1-vK4jY2hkl3_0s`M@#v=M)hKuGzTHBaL3hPyk!hzwG&3duEyn|gz z-8dzV8JbP6s1Hm3eLJ)^Kq5`@ErO;6v{rOs=&s)-_P!y5bqTj%^$+o7kL=q>VdpXd zgFq6eVB0QI|E%JV1yKb3RE-f#)U^%j4u%*vjtVFJ^Sq->i&ea$3R-@9q>OdK2^UGx z@AG;NbN|=Li&_1@CGtcroaurYxY5*qBoeI0(J6cwhd6yocBIsxvhYi_GP2q8s;_TbdRLzdvCmIxAIN@0 zbt&+nvlzlT)4o9vRsSX;7@mQ(*|^<7TSlJgqRoPdo+V3DJC{tu!cl?w2jLJG2d+7$ zCeDdI1`s-Q0ryPTtsDPmj881)dHVXQ+ZZ+}bF5@%Z8DUd3MH1G3v?eP7?V&c$ySgJ zOta%^=(YvfS|-VN8hnZu!*`fQ{Q`Fa<=isD}*d^28;_9M*Mcnx>!{>)vn5p z;p*|mK6k5mQ(gLjF_}s6R4Uy3vas*7d2l{)}$4(Q&j`7|&aOn=0QuZnH-=p=%&uF3jc{!q8H z5`t0NCo99}Tmx7J*D>dACxp6NZ?r?-s$;2^D&GMT+jE?sB*;8CN`T(;ddk!dTQnNa zVLx)~Mi{=9gY0SmN>K$%_ejS1uBJBDMtFvk7pCA@g?br~B}y4T7mAWF&TjPaqh{Zp(^GvUkagu3S>e&?NCl$&xh8YMLBmX>7Rv zZi^83707t(W%muz6PboFhCmrpCCV0aVlwHeP|LT>VGJ!AdO)I=5I{8eKP>(CNrWCCktDFxMfP7VKv&(`jQ#OPA~7r?W@V6EHShZq_oaC#-;|9nuE|io zwT~1KODcM26ebBfMd!7w>~phdVztx~CKbA%HwT#PEl*n8Iv3@jlY(0wu19d(ZqUa~ zKtt}3Slz!l8oU@GqR7#{EGxmNs3UB{4n);Mn1Ij&+cIjri3q}LB0B)m2X`;rlgCs_YC0sp*UN{}}G%R9H8b+%V> zVB$w3cIxkxu`>y4$q-9D-r^4}aX3RHuD8;fw}Cy}jqnky4LJGTE!6hk%P@-SYFcUWEiEb0(FS*JC-q- zza>R(!O(`U#|_A;PKt>RA}(qRpf&Bg!@8uG7AMo*iSDR7$jK?lSu!SXwK%*p!^NoS zB}&Cu<@cMUvS-YNaN1Z;8~^?V;;(OeJRkmSsacoyph(q8@F-tvrn36O0&_BG4^wO@ zjBqKLP`q34_Xv#|e;rG4u6cH^i<8JiRkm0yEH56BJG>@nH~dIbB}*K=`5%e2V1_yA zDww|zFO55#P`tZ0Er;9qF0U=E+nI5nlImLl@{~Ukk$`9y4$~cp|LV*7!{M8H-b}!Q30=|;2@qRBC`*OlBkqccN=bo(Bg!8y zy3(dM8Jyh9Cf&S-)tq|9tBS$FL8g{BOn1O!VbSLOPy_fTG#HEEgGAock2v{BeK}rG zW`nOrlY#rYqIR|!m{H{MgI8~q{XY(g{rC9>6yTfi&##!f?Cp~@(d9q#5C>6HqK2=I zNyJ`bGYU`S4(jUYm?UGi7T}iC_E0OKdMtBc>8kg0Xo?7%Sg6A~@{TBt=zYCz>fP=p^GvlCw zcg}FcWQZQU2eg7=eL&8Zo^vVz*R+NlU%m}i1d|3mvYgcV?54X{d`IGi3LMD6$5kx# zK%v<_QnwHm>sXzu=hO;LT28ET<6{)P`^PuGh@a?18lt@dZ{?STA9Mv=k=E8LH-&4` z8%kF*)o0c5oo?WqewGHx*JcJVt4pLW^A3o|Uo$5@#Sp zgRlCK&p$1fqN?1lnVXRYs6$ADeP<$=@ohvWBKHB8#SClfG{)!m`Yyh~jLMJU;B3@& z964WgGy*~}^Va@LSF)2R8eY>J@nm=Fbssh)+&uKe7AmIH*<3UdlY?f3dTly94D2U{ z6+FSTiC{SesUB~0BMS7?uP>U-=RS3BryUGBs@q(;nRV5-tDde2hXwpB^=@X35A?qK z^o4BgN9zlhMP!YXe&%D(btzy{5mpG-G~_^8d1@EwxdY9+Jyoa!oF0t=$5RGH&oDi3 zbnC1Gkgk30h+r-P8G|6DCe%f4f^hc9@##>{T6zt+6Ob}t$0LqbE89^-r)yR_wGiK? zBljC!GO}_GX#IOp(gsG{nHQ?7XNyv6}zBtt$)34FZ3dcFo^u)o0N;h#EW-I zwWbw(h&X9}9v@@sYfP5W2nFVxD!JQG5^T8t_-1;FL@e8Hi|WEz%5jQ+YHab-s`-Id zIeZ}#UMr@wmdbw@PrTl9S84UWu}_}z(2@#uN;djQdq~9=r+&=jJvtja%;!FGjx8pz+O(Lp?(e>c{_FN z=f^p6v^l?guDru@ddfJaZE|955bpJESC$bY&%SD+tC zLo!)s2RY`j--Qi4*ejtU2wR`uaFCtdhe4F4d%6a?{E!s?v_V?TE6IkggxD!N6XCv2 zH}4!a&|KIFpT~F|%YIc|n5l}k@~KDYvi=aeCc^=pk!j;A(w!6JC7?1{zM6y1nL(>~ z&CIyhS<|3X-AD0nSq-%20{W>zG~FniOxt7xj(0k zB){LT>?S=?&wSS7i*C7NVHiwe7k``YC)S7(iihYXSTdI z5>{V_4rFx`Z3l02#@|24o9tdAmESdHu7i<10iB$zPoni#jgi2HTCpUpN}x1XIbGJD z5y}9Qa0X95xy(8}f15en<3~NdAj7wE! zMg+dGY``P$|AuQ*hkM3D=9Yr_tESaNqX#KQvC{x=Of&~s#K1O$FAt!T6ZjV{EW8iI zVBxt~gQ#Mw4%qetSM{t*;k2P>aTc;kOiB7qq)Q?GtKWrTmi);FwuYgVXkUse@i(oj zP7i?xn|DY9-MDiSFRQY)*8+Dg2ZD4~Uluwbg9igtA}paH%{s!DFB#C!1rySJg??q* ze6kLBiXN+`va_jypL`Z@rJF2tum(;Nboa@oxyj!68?Jwf{aS^xBY!_{4aK7 zxXxa`%H@`ZBhe8ecGASz3Nk%W1U<_a7oesL7v{z3cr&V8FBQe0q^r4LjD&w`fIypG-YOM zkoBF0et7lFT@=AJsjH!67i!QLlZwLVmu9dZWGm6OXK+MHQjJHoLHBl69QSQQ51uLy z(NwV*WerqnhYygSPZ{bf$L%How_QWlY00Ovg!|7QnN&f~_-+|o2ZNybN0?IOy7kUY zXx^ThLv5aOo@3UqUeBG<+JG@?510;dG0$aR9Uglf*XQJSKkw4-B4!uc;Q5^wJI@HD zZmaDDB=cDAfMqU;ZU2Ew;aBr$s8t3G&SJ92947Lwwd69V=YvjunoO*35yN#PfZxeL zJ|9pSRS!h=k;7dWEy4mnXeuk*Igo#q?NM{?k){mYoNCb$0s?y{!NUFU36x@I9@0qX z`8ll?Q$1YirtQ6+&tD^nrl+<@Y_I`O1wJhO_w8Kn0JsA1zBkWDo2CNaAnv94H&bR)nv!#B^H1tOEZH*FVMnFSsJRS>4(nz6tg^E0xf^sCRtN zYc7@pKl%OkHvEOq+nIdy%4`D??Knj~k#ESNx#kU9m$zjgrnNmceg>pEKbHayaMLQ_sae;f z{nBkA){iROh_Mo1_GZTS8Kip0IN4 zyZ1_~mA?H4t~eh^I-RfNOLi02p0HIe=cpcXG*4eAmA?4wM;0QecT+=XEpFR-t>`vikD73$5>QdvFG1MAj zr>*@qN4sBK1p-zJ`k1uvVd=jQE?EFvVYNc}EEO*3Z3SB1HNAr^Ar0QoLe-_81VRa7 z^7U&X;gK5TCPwf{TGJhYzEyD;V?y?yMpvXFs7axzzSXncNFjlDeGMBJZjuFZJ zqsrDkr}aqhqQtL`H7#~(y-=_YI5vF-OxB4-;ot7|_QGPoZs6>LM?kB4MT) z{_kOU$dcn=p06+}Jsl3yry;g*nIBMD$=WMV_w1Os+6v=mCM&YW zzS2Mh;4uFZ3+KZpwi-G5GdH&uNWikz)BRH0cQvpZG$5g^QXA)7y|%;pJ}mwBaYzi{ zFb;KK_tH{~5^C+{det-+@VqW%A=>a{FS>jAd$2*}bwNN3j`{KAHbR_p%)1-7BfML} zz22xk(zB_#cN#=F_!h3KFgWTAu~0Da@tw7FniNdVDc2f582|k%aGH8;(BBp1fBKsK z;xNvunk?}Rg1{8(yU{v!Hs_JAOC=5Aw7jUn%YZQ-(N{9Zi-&8K<1_TV0b&Bgr%Gh( zv!yl4$o7I%GQKH_8*f^m>*zefcS{z5{2U$TnUq6Ob;mN^`hnbP1OK~cf&Wx^>H;rA z^=^`8^$WK!ymP;IOZ+qnYHbs0wgh$gVK9-6%TnJK_!#I09ztH=yLN~^9Be7pz-XlX zm9EH0=RC9}E%+0rugmunIXY%l?<`IO{^9v4`x3 z4ZV>=kF)~Ozs1?2gtv74{a9;SMEjpP#iBSNb$tk;f^A=YOLJV%m|>JSs~j0VsWPr& zIuik5p4|X7rYd=buQ}?;y;@{n^v2#9FWK_qA&qQ-99ij{R8mw3F$Q{e&e)-0 z9Htvxz$ge0=DM)c?sNA98F9Xy8&ZXHISE20#G{u%I$2N3qqmE9d(v z_g_{76E!8cHntC?(yb3iA*4>#x6}lJ*O*DYx$r1H*{3$JD&o0rG@tJDkt2(k@H>iX z3z}+)e}4^CHwB~n&oI6q`No!UWylnyRKY&Fe5dbRwvi5vcQ}%lBGj%HU@pjjL+d{k z<%_CI!EZvW76vp3S9(y2`Hw>=xM#VeiW1)hKz?FV)BkzW@zFQ)%WrQ+MZW}g;gWn% zYPOl)Zi!2pNYtb$c)ri!7hU9iP(n>^v}TWLGQ(23FN0x~uhZSSuW-^`WSS*AI?}u? zLLk654((XjA3kBK&_{XL%C0|;xn-8?V_p%)OWPL4B4{T8kKD0D?rHa7>A%i57`Fi5 z_(j@fK^%qP#j-TM5jOC5T`(`t+2FI*{7V&8>K_4YGQg?V?vA^4P`+-4g=mJ zw)>o%^az(5miF2u{GtwcFh<`ldsiWVF3NB5NB+-Dr)O2DM|)>3jm%C8P!nf(P)F3t zOy>dD_#xt)t$6!V!4R0eS7d~$f~7->QPQ`O5PFE{n1u2oSF+vx z$HB#`Xwlk^1UHLb&R8$idI%$u#yEsA-flt=U|0>}5r|R$p!Eeui2j{Nz4SUkqmdxo zLUq&k8yQ5_bX=5nh7e_RH0PG1Gp7K}3Y0(UbNxUglVnzrRl<}$S5j8DLGDEKbJ>KM*7oOb~lb*jHAS44)q8q)ut zOWe+|Z8{ZQ-vAQbF#v9g(FS=1ZjR+xurt7R%1&Vo&G^6VkRYjTm&$su@y36mspbJpmFC2*bkqV1`t*u~g;Iad4i_zy}*VJhTKH{28%AH`ly` zO-0qNeE1Jb|9uh}1V|*?TGeu0U#q;AYJ9}|hHx(s?I+f?YsgnD#`O3_XAK#2iNX6ln|r90uU2_d?{?z?%48*v ziDY*Ne?(ycEkpd@@XvpWJwPJ=xk_G7EKhaH6uv8FG%>Z)5Uy=}{aRwo1^9C?t+I$K z>^h~{q}>coQvuHX?GOxnr~pU?MRQdG3iC_!AYng=8MVRbE7mku4vo=emh=9|V2x0x zg^&3m*QYn`O-}@rLTfaq` zDNgIxY$Ksm?DZ%GS7q>;#0hm&DzaXLyEmxEYlfR~1~g3xS}_EGvD8x#>eC8^FQj88 z0n-8e-)0vF4hkvGkNM%F#DUnF#ew}Y4JbAZ4n*&&IKiJn`aL#(hTUT1^XQ>DH%9QF zK_nyJ{ot~36jaO!>bs!kDHJ}^h|7ttqLL};Hws~`0^ywH8#&SulXLV?RD`wvlRU(e zNn* z^qGaC1|FG9YSj@A9^b=v3;-9pJ>~~ss$fNax2Y(1AlnY9G6937CV7W*+aN|VYqoG_ zmIH;Ie^~nOV=E27)~^`}Z#O!_u_z(pKoF1`mg?=0`><*W*>4F&L$L)D+s5Es1rU)0 zBBMUYLor`&6TUrF4Sp3VuKn#d5h0{mR1HdINwpYU#?d`T9NbRaTDe>) zRE%Qyas`|*#}L@zF(mHZ&?U27rYz%&o!`sZ!S+TLPufhP2sXMML@Z?MhIaK+h4~ru zXK(50M8kp@fT8i_!aj3AoZ=Oievb$l4=H*FiHQZ5XzUfOqRsoNfik*uhUqd>ZOk5w}g+&^Yt}Kz|Oyx`6T{f%Z5Ir+UXL(MAB>? zE<=Q4&jmC;b9s~}b+HYxM0;iz9~%qgzp7sK`7JC>uA}yr)m1v&QukQCL9UFqanlvA zqV_225UXuMK%fWe7$1Ncx>9lr(F5$&ZYJ75e5FKB`HthCrI;j<(q=tG3Gn~b?+Y=k z0c@qX>=1f141{@;b)pNF_{Vl8X|iZ3#7QW?&&c0zZ1GjJ)jL8jLhIc0D=%V^2uy$) zkI6{~#G|Zouhpfsf^Tfto)6)hMkf%Tg%O+*@#3-AnS+_XpsDPeU%kH&&bIu`)<1>* zuVlOaVeijzu_mAtZ5hUlRTS7vkHX`78Hensm z(}^5%r6$*A+Eu+KY!iB?3bUopMyG9fGWgT^tjVw6z*j==Ht?9AU)K(U)T>beY?T%J zFqx_HzzUjlI6Ol(YzPe2uhnoune!ne1gbzW*F#}~a!8p(FiaGMAv6qZ9r{nQ;6K;E zW@$h{ta!2Ml_UJZqm%@N>L%D^#G)*TTGl?$ zPNRrcPkI3QYWjz;qc4^~FbKWu4P8R!-^erb^mKVf~NPV==2f)vW= z3#0`4odtfd!c6hM!z<%qWuVLz`p%g25;G$Xc5Gq;Sc62(AXey`T_ry~S-%0;YT-lw zz!pn_^;gsm-SwJouJ9oWqySJ#1vhqh2a!3Aio%vU#jFoY|9$%wKY*?1dh}WXkCmQ; znjjsBC^-TMi$f-1(^4SI8L^EZ4|~6@z>@c#Dn^$cqt5GC%t&A8Y~pa*Oa^N9uD7gk zO9S+9@$%4OogfSZEv3ED=OEfk#c|5pr2SQ#!p!e*H)=IeW$&hZ@bt>IDD5oh#EAhnWG(qu3ikq!3c#+#lYux*T8fycfsx z;_z@->ONJLf+5SUO`S#A(qhkhccXyxn%+0+@qmSTe_scZSvL5ox3SifJLb32(5xDKqb<80td7;;I9*qh9+ zY{#-zMA#3PRFT8#vzLRy=kXd)>68^){S}c7Pl{icm!-#^7hJsAk^AT-eC()zNn@n+ zzvJRm41<2Av|W5+i6LOm9bz6SGW2ltxvKIxv+%k_;2gdcfrWBfl)~N>PH58t!T8#c zveRI2-!}j^m?8tXC=pB)R3Et{qzme7Ei!{rI;~G7YL}bLn&W)!mQpjd$>kH=FElIiF*sU(Bk8HlOLceQpaX*$~N$Jrc0flRvm4PxVk5}o*L!CnHM@@mzBQcBpUjnxt95c z71*x@=hrud+TpAh?Z6J}TA*&I|MSv6eW3s^{iooS7mX0#fgDaZy9aq?h4Be)Q)0mu zKDd1Xcrxwx>|C$(M8n;a>C`rOwEM8h?q}^WlorJo?yDf`YwCQotk&USDCGhlxO_V(nd`wzM4X?Ogzxc* z+ZNuNmcTpyDWxbVq#HpSHjYc=FVm{;|3g#wI3b$_|FN_VA6lSnd?HAM`Qgd|Jms)B zEYBS#o)Xt#1Co(1$)7sJIPR9y2neTnD^b$Y6{dGa*Gvg@dzV5Fo-9TRDUM(1XVQ8| zhLrC8c^QvP;x_>VFbPRb#KcM?Yv2*T-64GA0N)*iTC$(S4lrp ztgJne`ke36X1}bzi@3dfna}}F%r3OX0K{c!@#EgSHM7%5-PmUktp`g1K|mL;y2Y+i{fG|Dfc;qPs~Av{yvze8*M@lw^(#sy{{ zgy6koEGfx&cmR6{s@+b1X@6%?2Jw)PConeFf6Vl%x$+_r=a=`zpN8su+yLiBRoE!X zU~K*{1oKqpCXn}kC*?$CJ|mEp6E0SH4Y=NDX73YGb&;eX;mk?^E~nN>#_|IkqPBwi zK@4bQ3rkdkX*SIyg!KxZs7K!CBcT`Vwo)IK{_7lqz7KFngaUn|z9IDh zbi0z$kSSGpjGp}-X&BkyKsA>OfhXp47B7f2>Xm|UVaG)2!{TOH&xxq+IXp7VIx=KHSrEsIv1|+Dbr7&`B3s2zLlMiQzw?HF3jJRWsda-7(|OpW z>jhb)w~~u>CwqjmnQbO{PwTo$>KQb>NPC1I>%K6N5RVLJY1@uo2l8|GyF$={Xu3td zvTW`llB;8^hvnaM_Z*%=H`h6`PTGobjh{iq zerc_IemQb+ZNF2XI-sPGXMyi;ZZHl@-t8D^hlS*TuQ;mRi-YTO(6w5&`~6(Y`YUP6 zlH)YWT_X^PEOVv&qA7PQ#Z}+#sa0$cCHN)W zBKX!1o*Z2}6+544cn1l4NIK+HJP&wDYA_)>0UGsJGi*5XH{r4lCGL@U%b?@Uo0o;y zX_XNrb?KaIB;mIb>bz#DFaOCKVkr3%@l4t|p)&iFdY#0;&OnLvX+9YiXtw!Jz02d7 z0y>8NS56)P-FQ{6OR_<@>?fg56>u|S+D#^q&SGM)gG#|N`~tX%0!rQevB#KV;DKK$ z&D}1;io~K9Q0tY=vz$^@>+mPFaJcVzdceE?YPOmIY(*8*riqJ4wwdqjEZwO(n5s_K z#`ld-7C=ZOyG0!~(FJEDOn9A8?JU;%*t61u*dnllx06*pq7bexuOLu1 z0grdHc@EjN11rX{ zL%w7cn&t0tW_?CL7^`32cX4P?7+HNFt-&jw7I0Q()$S5`0(~w@PlMAmws)t#F{^;| z7xBx91Ah0U1|}$#6I2+eV3a8tL0@(@&B#r|O}2aI)I?d_7{FNJgOvfzmW`M5l%d|9 z<9%F>FH2Ro&!b%5sxTkE4?D_E*umOPe;#Q2IY5f+_XCXG0OQ`Z8u>FwfZk8Fd&Kjt zsfS-yyYHXqR@Tvq|MYI@$~4r2gj#b} z#Ps>*qiQ+11hPcIu|j1&c>iQizL*%3wmVdN;e;=vd|yR4z%Ude_H}_{zwcI1akC?6 zr%C}vhv_A3-$5CRiE;A!C$v`}uzr!2m9^LlAzZ@aVJwZbih;=#tu7C;$26^C>^RF4SyS=C9)PW>=#M$8^^nNMXto3Gw7?UyTrU`h7@yE8jq!oLKDZ-R6QF4Eca6m34-)7Dr7 za-TBhB%IXie%M&2k6Yq7vjDStM(`4r;QQsft$-Gh5;@>M z_tQRn%4%)gMbT=-=X(99kmD&!b&gGq?D3kwUT(s`kIF%kC^8sskH|?uO)uI3{M~lW zzP?ClSYb9(q!XK>7^Q9W!5G<>a=%PcFa>zV$fSIbW-!Pl@YRS|EX|GaGbxNkkCN=TVU3zAD-(DKR6QNIZ{2=dUT?XFic zScFeY9F+pt%6CTmuo*oeL(?qHcB_#bY@kK776@PtAp~(~Hs`B2tjp?I%q{ z*H?G`vY^-&KwlVr#krS#^;a*F--D@Yaw?>XLEF;)`xpe0yyZ;x!>Fjg@*}c%QC(&f zpZXP(69#+gJS{Ew8M4?zdC4{0k98g;%aBcrYREwS<~aC0pCS_+(sHak$eD8`nmP6F zjx@TOJ*6!^U+BU2JH}0|*4rGt2ZKT(MU13({=OoOGXQjWjonNt)S;h08>XAIEKJ@W zuBU@Lc~s_e^1nE!ACdu<9GU{%Vfj6tW>+}43LG5gkCmTyxidJIfgSI9efCq&NU+&8 zq%Sa*GH$v|DhRPjC?1CAl=MKrr(3*zt$YB}=?`03r;2K2xK5$fMILA@q_!fN={~V=n107>trIRj!*iq^xwx81c0ps`teR)wi<*p zrx>VVSlVC9N4)kI&bB|;5N&Y3){{9ea1TRb*}MImD6p&FY68kb8)hZ7xCyc-g%V+m#YEmz^YJ9M0NU?%jP3(vN-)#L;=mBi~=RN38%vDq> z^A(PBr>n^QfGurMe!(aF_w_=VrT`Np%g~)u#IHN1n_n?nNQ%=tV$zgbghO8iVX!vyYc6g#AMn+(us47B$gA5~3zH@zfbtwszvp`p@|2z$y*$23VT zGPT2ckBY5J@+k`Q-sR(e2w9RKBxI-L+M38bb_0SU@)d{ombQBMo^hw1zFCcK$*7~Z zfVIF|hcJhYXNvDMj)&-H$%CeM5V;~*w}#wiBO4zf7xi*8ZocRgt$nPPA(`- z4VGA4vN*Oi?PwLXaoHe!@mq420e{#ss&E-1)t|ef`wrV9VP?>u3349#PI5IgOPNT? zYp?)<4ffwde`nf-(mk33(@ zH>bJ-klGtUE)Jg|_#i?#nVvR<%lK&}_tMfZaW`j>wMh26JR3RXff!a96^woAzEEl1h7hBIP0D zT45$2?dCcz0xTMW6yInmy`!Wqu~-je+2zI|i_i?=KW7bjIaT&6E46AsMU@nOMs>B~ zgcU$QM|LElt6fibj8O@>+rE-u3GJqA4-j9}8nTt5^*QkLd#1r+U@A@KFZeZN!KAWm zXEH0ZD|D%LwpK%Bx+fl+q8hft`V}yF$swme3o%BbWlTaOHn*%1q=sC-9dPR~HF_0c z5S+Wis1^GA-|m!+vA8kl7Mi0)=LRqZ%;~%1G<0^DA4zXTz}mppMkx$##^!0$hF{or zI_pUyUzEBcP5IWFjPPo=onP=eg6B#WbQ%>Y&xZKBtHnavcZ!*ug$Bzx>l zPjQUWzxO*`&fG<82J0qiLUO6X>5H2_qp`kUJv#!Zb4fRox$HOG-$n&~r~_sRFY?ta zh5ES6fQ?r~7bQmaS};9#3%=7_L7w8JrnQ7+^`PzS_;-(c&z2$|D9u^(o6Ht*%W0YQ zFEH#4$-U)zNV(khr97by4H)EZpOIjkap}BkX%)_HIknBUt4INkE*uxIiMdt{=GZ@H z4bZfLhtM}bD7{Qc>E4xjtm3uHyI#5eEoYL%--@dz8UKHesi)Sw*wYrT$ahqCwS|Z6 zY)X$LGx(JB17Xlndx&9QhyON6qVUA>@4jUh2~w9K%Kh=XYJIiw5crJVymxDlDikjc zfI;xSd>la>96~cLM1TOBG$Hp0Hu=*Z;r08)cM1xwi&GJGS3KE)f3-n=0t^yXf%_R> z?Mu?n3ajTUac^^nV#7{!9YI zzmIUk4Fl>mLbE4Ra1wTKN!E{rs4jfwSVf5}Zt%h;Gt~I1f@){{ z`!_Y~g7%tX&Cs{0(qmnx%7Ao>5`H|gqAN1l97!FRJKfLvg<40^e+-h0YW!+sz9X+{ zJ6D4%kI)#FF9H&v8Um+8F^S-;chdCI642@)dj<7(dIB$v;?XV#y@2sb#$EhJ#y2&8M3JcG-()kuq4=Pw zbVs&nbs8(=nBc6dq7~b_yMbG8ZQE_^gbWqIzK?5Pla(ZZdsA6G=y$~POX*+j=u5K6 zOn~IObetqV4J|wMg;)}84L9t^KTjBz=@5K+5I_Ib)|4JyN|Q*|8FdwsM;iC3t65RR z`hwUVv<@fHJ}Ef$%ev3ZIBh9h9-wfe=*P;NRRkhFeg?q<0x@47k z?iuD89^w`Ff-JQB{X2!nNTYjvbPp`I1yi(XZKGVmNi%I9PF)3ofH%%$h^W%Df@|Eg zxykAJIUfelxm@Q%HN6J*=5iH>5h|J{*e@#*w)^%J%q}e?D|8#A6z64^P|m=uKb^FD z9yH{6?BxyP=LcYLAE7eL9h^oIqp0X==(ECkQtUP%5AE2gxa4)u!&}cJ#u#u^jTV0wWp>RuW%}C-hFYHiQS9mYEe&Hs! zCmL*uL`Wt4@wO%e1bl%C`#}taYR>}Ur0;H(Cxmzqd)6&gPM~i-KlHS4vIIEd@n+gg%u@`@Dv3kFd{Pp*QKV>Hk@&d&u$7awrdU&;|zc-LyOc26^V>DDdH z!S8=NP8e9mc=YQ+fiaE=y-Ze`P`)-x9qPXbXs54c{VT(9Y@^6CiXXn+VR6k*GH6&XjB%S4ATFP-SF?417lVO11{XmvErLQ7* z@qBE+ESE>;i;VRtLT0;{9-6i+*)|=4^w@(IWx+d=MiCZz7)EsMn~@m&dC*-J)xn{7 zkxA+)DXi1s_xC_6gbCyb{~vX49aYu0e+?hHyVFBUHv-Zj(hY|Y>FyNiZcso#y1N8v zB&EAUKtNJDq~kfEZ( z;tdyJ0yT_JQaH}H8507MRo5n>qC<5=5}^F|bk(+VQwfzX>T{7=(!!S%_K=y}wA0ay z8e?OdiR=t1c<+V?ja5R~!%lPPu&oU^%<@Lc{28z9zO--1oyJrZVr|hdm*XuBVyne+ z3~%sa!Lqg2vk;>7)yA|jp$PaNb2j3P!4)5{yvK&7U#*!oh5fLdH;`m)or>F}46J~# z8#YS6O09)|3eP;P4lzEH_on(5k7!t5sD;v70kr_yu#po1S5H0Np?P3q#f8#T)tzZ& z0woP^FZ8>w-8g#h(@#+b1HX1uZEzG=cg78f6S5J+d?VaWxn@U+u}^^OF<3wGyVmAb z+d;aY@=pB3g6#U^MWW4@dEz0J61gO|4*>g95FLS{RtoMuL=BiKF*y2M+03OHJosaH zGryEA_+QDCsFH+|eV3u@Dx;z_am5%dU;p~;t0eOP+D6)sk(AT8*oq~diH1OqI}gc> z=i+yI%bJXS*kL2)x^@#5g44F+*nOSx%fvy%_ahns@xLA^EJrVWu`o{2YfeqOT;og3 zyU&980*UTyu~2W}Cp^rC+}*uc4>NElZim^;5$SUF-x7|qf7Whur>e+3(^Q9PMC7oo zCJ2-Tdq~>_;saxlK{9K3(g2gNKqJSho7FS_tID=|bxe+%E(gE+u*PEH0rY>KhbVzP zD+5@hk-dE8no4+u<@UEiACQZKam>76uMh-^|AE2vZeo2Fl$g<}$7Zk)N z%p-L}m#;UcMwl84-}l6}qz_LVNBx}W6&pt?ZI&+}3j?#~gA-n0PNvik+_t*&kh&Ti zwx9ViyQkS_lzjNwi@D}u4f-x1af@0OBeQ5?BUA6}RqKq5%!C)`#MIHC@+{AO7<{MB z^!0%Q_zcb~4u@nFD)Ug)iRfyzBor8e7d5)8h2M<{64dd;F|6ARV$kBI=4z{mzoDV^ z*&AGb?k?MnPMCBtah8+uJb_&hHrL=UA*vp$ieWzb>fsd5Toti`FE2B`uQWV;WC3Fm z-S8$-`ln-FE?OjiwWkC^6y$r}O!?N&Fj@f+X5S^0_3YFBg2)U3)MCQ@?T{B}NCvNoE4mbT zIcg;__#>q=aTqD}sEjah`9v}Y&?Q7aN%CRr4GmbOG1VxWA4up=MFrqNi{@91(bIlay@ckIn2+$fnC=9G|8!S1^uvOCH!jC&X$XnML7x$`uDqF zyQqAu+&!6SJBn~Ibmc-{D3VV*_DJjgQbhe>@rk&O4*DMsvae*-LIHTHvMd`gOM!0n zTn2$z(2qM#;#-NDJSozU7uZ2QEFnCw1=@E4n;PoK*3UzGUn4t_j@9AMcNfuzzEe?~ z-^=#X4}JjspXVS{UMtRh@P0JjHq6^#p@gP!~fcB*~c^j%z2< z`u%2?iQ_v(qf~MzLx?h|v~MUq7Y9xKAl2yx7HY(TylOYDvUGa%?dIWaG;}kvP@%8p z%}48J0z#^P@{Esl{k?;DYq2I`@x2R#6CMy+j@f(Ho&u?f3U?V;pwr86PRS0@f2yZ@ z(1$7Zr9k^h5Z1TnxZ!*8El}h(`=)?#tv9jx`F)zb@aGCQh~nbu83L0-=e!#k2r$K7LJ8olTP2u1K9`LF4aBn;R{x**HhYCyWh zU;7Zn%cNhzT)#-5l4=I=xw__CEgCn4WkE47IhYk!VM=uzv;+>(QFxw@eFuHeBQeexP-=6k+}EsC#Y3=*op%QG&ylz%Je ze9m#m&Q6^Mrzt!^Yi;_Hof|N_Y1;s3kS7P{8UIlqdsoo%W3bADrYvxl*o+Hb*&~F# zi4bPw+w}MNlCBqBCE&4T+716;z>I%$^rbQ$j<_b!j(dSrcKe#mXO@t?yt;WtuHVG( z>>>1j+}Of81COm>*`3T;b*m)AWKZ$5{I$@CJSaxwCh5;ySEy=GO=wvUXznE}yL)=OlfT__pPa{rZ0ZO8ne4(-`> zTyG*$5V=JW_L55YBAnWH;%n(&Uc}bqr+_XKBAYxom+r1&eOI^n4jvtb;obn@8=;x5 zjhys&RG93(Uvl`EQYqIfFxwqe5BqJ4c5Xra~c+(v^ytBSV`B zo^Wgc)21_4&oKzX;=g5`IA-TR3k{eY(|Mf{6On%HlxUc0Fh=MkREjWV<)j%G)I!05 z>DSZKp71*GXu%$ReUNFlY*CRiz|X!OFo5oFObXlOs2Z&7mm47h%_nLa=H9)SgtQ@^ zdb#M+lymYJMJY|ZQd#z(dGb4hV|w|YQwCP zA&2z!Uh(O0&_^9eQ2*N_Z6o*F9@jL#Ne8p?(mrwo1vwj8YzVqdOlf0gtFx)rCrn0I zJ=2*n%Ll)p90W|i|QQ^%7ptwzLnRKKxa3a+{86U)p3ZB zRilsjJX&K^c|T!nbhnU@Cz-wC+2*bc=NjcSF-SMxJzdv5@#$$`?YDN=hzg>doVP)O zgm0nrY5NYLV1`p@LlVqw->LB!h8vL)MAR8$s2Jl@d|bF7oXDxcjLzj^f<6ZA;`2X6=Ugk4J$FcE#lQWDe-2t>_uycflJ84b>dDIo6MZ;V^8u+~#|Cpb} zR5Chvh*|t>u@$GYVUv1IU;?S0zF3Ac_(ZM%R8P0oWQZ@P zncjj&U+SF==zIG@cV5@*W$a72SGuty2YSJ+AJfK9qdCr4-RsDCf+5O-yonZCO)g#5yuO{6HGyjc=ukHCPHEAFX@ezC^|WIvn>`J9NZ6SN@vwvjQd>|0(6%WKWW z279(&ax>9qS*tln=~`UxRpwRVUADHwG(L5aC6TY*p5SNd-X_;jvrH<^!uoXcu<}l< z5NwivE93Q1cVgqtzaG|~RR6lWG!wdwmv21c5=b+pKPkNObK)6v=}Y{?^`uq9xDvni z)JsFG&hb8ttWXHo%r}H%ZPacUU2&>3&kc04=Lfo5XCrK-f`CL34tO8F8H|b$odMCB zl@};H;k5_a&g%3rAPc4IPl$WP+_!yH2tuNy)(1X>_y1J$*{|1Soma|cB?;PxRP4cQ z@}XE#wWaEvNis$!oROp7AVuHHV^FoI2_p#lvSRl^*XYZyui{Ad4ujF9?M;>^ydd3( zfw7^(S5i6MTp073a1PLI+`UN^+rIXAPfjpBmQ}XY_1^fM_O!lS?T3Gt9!d@ZkV%+| zTQu%kHe6K^$zSQPJxPkhZi5dsJVPmYCWl5(f%age43`*Mdz_&Bn@^D)Hv7& zwtx+@3-=MbWtl=Pbt&gGYY*(M~ti%OBO_29=Hohrg_h!yzZtO$J6iLczf*1;UimZYQF{}Bk&|eODomN8* zEzhEdK$$LM`Ny*!iGlAQ>m7s$$RcCpU|sly6fgUh>lu?H0nNXwosV_>y@Pz1uZufm z9F$r#zCH2xj9o_0=-joRJ#?FV(cZI`MsT9Cg#2=T&?lXSn9@gIx40V}V{utTtvQ00 zo0M{`&fllqlzs{ZZsY2iC9n0q$dYTV0*YZVDt_nl(pta@UvkNvgOs4A3Du_l%q?Iv z6xu;DoU4t}_LLqC%x023*d_Q#BLtB4u+Gaz@DP^>)=Q5TJNIus8oWISx)st_BVRE) zDF*MKkc%JZA!+UOJSn&15NS=O&&eF)wB*p=RrJ7xof(qwy7nSEpx75;$zy9bh( ziX*>52RG0@2Rx_J;zp0(^q9R%WSguz)IyhbBKg!axuH>vjPkC=pQ;q`yqqTdJA53u z|I&+}xK^9Atj()_fJ2>JSGK9Q^bNxerIxY!*WIg-2%&}h`!TJj8L>q{CE(}Hmp!ka z;oztEkR$tg=cpVK!6^5%_;uO3H!)+ZS`Pa!tfNO7S6Zsn1o;2V5r_k%H>L5Khr7H~SdX;Q@I+`OcVMLO zYRjSt^{;wc-iiX2JYXIe$T@Qu?b?UM_^&|$F4p6v{x6?4dYP_so_|{`_o4Y@llK6s zqM_ECP^rlBt0+p|t1&mabtpQ&IXe)-$OiY77&_$V2ha!`RD?xwu7Fw}(+>k`dArPm zx&Z23iej->Z}x$$y+YuD-2(W(`ytdJ!LkD%Cyc0X0vky{?KLo(k7m7RFRv{3M^hC{ zrR8!GvXWVqi}4oi>nD3|QPEwsb?@yfSW!O^B0K3}#7?4)<$L#1srP2ADWEi`Vu1AY zZx-vO&`LL;zl_(Aq<&47f$(h7F9gi|-bU!Lp1(Jcy6#j!9Cqy6op|AyW zMX=!!4(Cq*hj3O;&p`g)s*T=!Y7KaCx5001C?B%TGdGzag$!Auh9JUEs!-f`@V{R4 z_OrWvnN>l<>kYzb8< zey|GBG@Dl2@e{+igbG~$Kpr*;0a{;=9*jlB>@Tv(uvX63$s9NeUMYcA8BWX3sbS9M z^o^SccDnV6_XGDZXZx@{Gj))fh!3X=KUWM>zNS(wqbjD%d>_DE$mwv3*ncLyF4{GS z%bFyIy-?OYK8R}%1AeRuG5HW-uyJ7rvtj#skToQgg@7mlHnbjL7A?5kKqJH2ivcB9 zMl9e-r8uR=FpmZD6n~^bnaGO8>CGj5pcd};qf(e6f-1d=tcd9K-6WeM-_GXJBgdv6 z&KISK$8>XdDFXKSTz#?qx65)U6!;wSDi*pftI-LejBjuO`>i3wt&oIdLaUtoBn0JNU~D(S z51RIFGZJ*(&s}vTpo4K$9#ZC4jEh&}Qlnq%yG$Uo5X_wvb(@rNkjJN#c?K=+hL#xk zIK^)xzD<*=lZZPgm7>CexL!|UD4ItO>^f6dbsr&7RF)vBpWOa(?S^^U$$l}+L5CYb zRVuO+uVXk$N#n6YF{xS`s@iLl9k!Y2uWO%pBfFMMK^taWW*e@Q8hcRWxcBG9F^~29 z{m`nG@@YX<>?v+YJZ*(XZsf$Kj7Aw7QPV0Jxz_r)XSg)3GU%BC9Z}{@#DVdX#k3e! zh*z&JHZ!=X!)U38Bjh$HhmJ^-M(0%|bO6h~0n*b~Or0}24d3*!s9H}NyU1|w=H^`E zB^d3rCq)qgL>kuQ=RSo$)UwL43dy#&5#mDuhSzxQe#k#5?d|3|Ub$$uLZa2mSA>kc z>7aoTaO|YJDSV+{OMog$4CqDg(?5FBEWg9Uh-Tbnc!6{jc%y>U?ePvv3r7vb&ZpV* zvpcTzn8Jzlugbc$p&!f+1EqFt1fMic_@JnT875lRVM#uI-2K53nHqr~`qI4Wi$1Jv z+$zH>J=a>ckpS0oN!y;Ji>Om)Wb1$)`qXTfNrsStT5-O_{P&;J`fMAyr*$#EoklR@ z$VC-LdvUJ5=V;zwdSm+OCPOy9<idG~%zXEm{a$NvbI($!mJ!&{SK_6eRE0js zPk=(Im|7oGLkqX+IC-inS#lXSj-x=jxm`JDhp$L?*nI}uR>ilGeDXe!i(voCB;xw% zOtiT88uMf6JF@-P__cxB*`J;U+h8B7Xcc|TzaS-Zo56bj%Hu?qBVyhy`J}NS9z?W0 z);SF7Z08er+0Nsly|_y2nRPce1HU*!ABDOFltxKA#HROaK!0J9%bS&Fs^HYL8b@a2 zx_c}P3=6@-TfRE!e0Q+_-DY$>`9~y?Iz^eN9n;smDg&g07Vz$)|J;czix9YS3>?YO zBq@zjKDkEw@aCMyPlJrtqmmOfoshOd6c8j0^}rX(d+^_HOwE7+aqF-_!?cOT5 z6(Hjwfu;5nJMp-=vyhzncC6_HHJl+ld}0!l#L6F;UIO?#D%FXQu}~+_%A)G-gQf@- z-=Qn3i)ZIgJ76;wkO;)3^t1txoT)f()fEo;DqAu{pEHs#IiHNvbn44O79~eaD=X8` z#!Lp%EJ>oQ`qLQqakRoin-*TtdLDMM!N`iFe4vEo)C`Am7rEa1WVqCI`4)J<_)&5_dW!oYK(pG ztFhk=qA)x_Qncw3XXUW>giPT6WX(?W z=tVSDfE(T(S(634vB*)`p#-;xG@{0GoET@z*D~j#XY^}MMhtIVI6%p5_}c4OVsW^H zkstGz)=mdECqQ~;enKnE!!~)u?*yLf)bp^R$qyGAON;ePF19+eMhm|P9RFoii{_%T zQqfJ5!!vzhVJI9Rgu$fQI%Fe}p4X+~{nj*jY3&6UhL~^FE0AvK`BiWfbh;P6Kpi#u zmEPXA+aeXVv^a71GaG<_q1^0T8FT11 zWl_yBvFSOVdY}$ou;JVx^hO|))v>7e_O;>1t%!y@3-Q|e>?6igKnhuNIeiidm7uLi zw9mD!_2;TK91 z0ROqbhe03`;R&3J58K`%$dwop1i&pv<JLJFN`A-+MD-ChD~Wl{kLxT<7qmizE0uo#%Hc$d|Kx$%VL zZa#fSHEA4dC*~3v;=}e$yOpmaQ2iW-@#eY$KhM>Yl(je=@ksamGN5uSNx8NMwOyO~ zW5%Ir2o@C*CEFhhd93H}Eu=`dB5YQPQ*zxr1xU1MZPP*TX0M9B%!(ui5&5O-M|CY< zCia}n>ZXb1)a4n)o-R(KO)C5KNI;_Ct$ejh%8ea7xhK?*1Kor&o$Bsr5j!iAGAxr$ zG#?2W3oNgXW{7tdQfd^SeUU(uRB@sxK5L@nG`11MlQX*tFcUzR<*hPGJ?)+!)D|_j zdm}Jl9PrFEF=&7ZzQ|M!nO(J$+J(_6LcEf8U|pR{EiLyH);XJiD4tk~bAOFZiJpDe zTSNS!8J+@IQP4#TOZ1YU53T1$`;3z4YXM(jjJVxqE4GR)Q*+c`VR-gS4!HNYvxX{PoyBU8(LX)sK=v-&mJJQJ z=QHvdHH}qf6wCi9SJcEP*_UA9#=syuWEMf!@#;|Eqo|Qz6ciGsL-$io4~*c21;VMt z50JXJ<&#%Hw$J|jcHF%l!W)d%1QdLi9}rDP{jK^yJ_Orx^>Id)P&V-CWXE+@sjs!m zWOJU_ZxlNEeIT1%b&K;A6Q=_BTp6gc)zYpeHEOLue{ue4mu6IG64IefxHlc<-55fg z{@Orl47j~>lHRt*Tg~7Wixy4BBhb)aZCOR zBL7jRU>dNDl1cD7#pw>F6$2JdXS}L{Ys5kJ!A}aaqz!KF6$LO7Ks*@i+6M7b319Zs z7c4j{ubzg#F&E51A2Z_%Bx1A68O$%>u)WcR{{0Iy_>U0Y0O+U;JysuI zeE_uoG@U#ELKT2$Y-Y*K%4}e7WBs5z#LI`;fP>pw?!@|E#|ikkSGKYvgK_MW*Iy?i zmanw70EGUl7|f}3$BFW9PCWwy8z*bW2drb})C9Tqk}sf_ zh(1g74Jom19@bN};l6j-=@I^-P+|#Y#p=&_dir(9_75xIuTwU&HhV~E!SZZM!syBS zI+D4YF#lR3&hX2_Bix{md+v6?CE~)%->0k(FnQ#RZAl56jp!QURL_^po@(2T@drV? zk6)J~0-;At-NYaj0sg}o8~h)i@t@{F{GU_F0$`nq51&bDCv1EF?mh$XGlP$uH=gu_ z;3TB!$tbTooA0}I$q-G@E*26-O6(f^S``Px`F~Q{>=Divs@~P*qMAv!;S{J$&c>lR zQv$g5J7;yf(}XU25vPa$sV|NGk2~N$MbBNFE8fK<^6xm;v$rsM7(ZHln{cjhM-|bH zr**RMzC}L@tv=8c(Dpi$+cfIBDhmA($ngJ4`yl`7Nn0D2e~d#aS{rHuEa1q>TbpnC zvcyaMd#Sv5a-bWlOC;hj3kRJ)BYNO(roTsYu5_~f$=1x)q|_L)eI@Z#cPqDtdmsz} zc}5MYvl@Ttf703Xuh#v|>CL%)p}zg_Y%^YSMA%dQ?cR}I9!IUcjE(DVmxgpjQ0TUY{}x2A7`gDp&>Xey3W zikiR7_T@6GIsKIn@P7|l&6WOC!t-bS_xzNh)S1H)i{R}>s?h=O(oOsTaT<{}VE{k^ z0P!z>KL!BGe>eQGw)jEgKlKxa^Lx-H7xlegvi^Wb5}yDYUFH2WfR)}lBrbqEwnYQu zgKtpr11JqS%pRI;KOs!=rFh>aOzX5~3-|N)sdX{fAZSO6qlb4-ez&no)?@yNhIbvV z27gwh7AnX)S6WqfmL}a!=K7A<$9w8`iyu3@pRg)8dO$RQe=9RYhOYL_uzps}hWyp+ zpmfK6Wk`sW0Mu(DytRzVW>=>NP*io6MD_wMZEiF($24Dtx^23jSM6fE2sD+Vte5JN z54Q>4t!J$H-ZAT^%7ny#A{G+({Riu}^;LDYpQV=dUvbT&9Tg~zeR<{3X`KHj8FfPPfXN%u5)Dk^0F?h`9i&X-9|XdVPsUw_{VZxbnGu~V z5MssoE@J8xI0rEIHO9w_kwsR7Y1?YE%`&GkW`iv~VA?X?c4oM)^$D1Si%x!*tOXy> z?w9x+a&AB~XYX8aRM@famD?+;rp_1cld+dVnG*toe5dyL4IPaWa zOdwt^Vh%rd*#W?APGJJiC9Y$98Gpt621aDUOt^wBd5_Gv1o7;Ct(DXsJhnvsfZ6l<-Q2A9}qcn~oBk^3>= zz1*g5!!PTJ%FOL(GGd>&htwp*;p&Mj3?S>Jl-iA0y@Le-bG&vB+~>&7-F6pczxPgN zdC*i-b};5xXIq#z{c?9D19$STV~d)%{7iZM1kwYFBU|tT6ysi%m3(uZ`!9DiPK9Ja zKpE(<7R>#)5Oh!~n;3>#a$$DmyY}}#s9Q|vN-Kc-Y+w?<&>G@5$wr2#r~&*Nv4nxj znUwuu(k-8cFN8ayi(R-uKz-v@J!)V5xW(sBICreQ=P)gg(v(v(*WxUIqn&D;s9=&< zG!(8Jj`o9fXzqyv2FekpoFDYz3}>petIB-fy`dGmO77O*vv>SG=D83R>JH;XZBr+``MkKB&|@8!lCs~no( z1|U+8bUh`2dKpQ3tic*uG*9FvCuuT}|4bVM{QM%=EVkvMuYtHrn*lM%{;4i>Afo%6 zo4qmI6LvQ&DsU%bEA+@67TRx^Dp-8yUqGY!>FFkJ<0q8A)RR{J-rx<`B6O4^r_d~h zix%1mSw{|W7AiYmoII`hIi&E#z;koENN*ghf2wn{$kkSxw0>xqdq4r-hs21A_9{@^>S=ody1~&~n|PW%*X| zlOvdE4CS}nkD2~oy385vWvRy(+HK}7x4G%5k81{DshA~2#c3p@Lq1B=vG~MYfClGT+V)# z+l1csOZuvyLJ@bv#5xZXts^B6&_4#D;>)*eH&S|czZ}mvc6Cquih*T=ch^^$oEHcR zIN(m6H;MIb^(!SAD)&3ggE|mgd{5bq>|fp1z-}y<&AlWF0tT%R0;KIOr9ar2muoPu zn36ts=zbTa5%`uYiSk)u3?!Ju)7~6ojs&Vgx*ToENhV0bI(7KaaylEOSh@Wf_@y1T z;O(7AWf70eKfc6Ic2E6$dKcwH75traIk_&1YRa|>pZa~7U0lO^cX)F zRacDNv1uNB+Euk)b4Bxu5d>T%)6WfliOeprKTp&MKs`N&=fMK|x4w~%XX7wm$W%RJ?*s0!hW-_B@pyG9 z7!>1q9onHedRoGqj=4DbRk3_S9ZuqAuLwVS+|xdW`-^t7NnmUaM<`It`c<6NF|}0J znqn{65->ki%Jd<@FF?t{Tu9y{36+Km z0!miygjL<__#^9DR6x+zHS9JHcJJg6QJc4pSejxEJ^O82j|JObx)j3X5hirmMF$6s z6y{`ah#bXveEh*>76}8;aDb2hxqUD9W4OO)S3N63K5(X{DqNQr*G+qB{>c3p@LulFGToVabG_mSTAnge0KV3+AUPzu*RkzAKb~(p6^EyEEErl;db0HdGMc=6niGm zYk>C^t0dM%;m?8t0ci?bM!Y7Fkqe+)K^7a<+Vfnvn71{C)zX)Z&8gg8ca9TKthNae zNybwkEkTf=T8jwuy^~E396sQ`L1AnF;TceGv{;LTE`X@Z(;{GP3~zor*LOX=d3K ztbOazzoEmQs%!|L;<}4FH`)v6z8BS^{Vn%nhVhp!zM|`Uzb0S5IU`?46*o^0CwQ_X z0coSIFUm{>lOW-N(eG#@Kq3P48QjiDUjAB`mecQOp;R#koNG9=qhTn`6OW#PzK0?`*uIW-Cv7LM}r zNEHOMYMMp8Nhz?xB82sQiZO+Mi!?&I0jV@Mwo3NAdS{3QOfr$xR#as;^y+0qm9OAP zAHWa084C1Xb5XqPtFOoUCruDgaUJtrvS(0>zVFJsx&=h|>_Qkfql(Hq9Qz{b5Gw)< zFiGIx6=?zztWcYMMd}4j+xD$OYE7&e`pif98UIPG0~iqStQ|S`rnM~}7+JsEp$f%| zWlPb*D8KGE_%h}xTRa#3Z@C{c{l9d1!w6#Ym0k?T(StwP`%Jy1eI}*zEZE>_J4%u( zE{*To>+?_hg>c6#fN+>Zh8<-XZZ zu1a?GNA%2;=&LG4n_xlh34x=R6z#|khg!Xaa-nw3LNK`aoxPT^%_q5T zY%6tJTNE~*-|JZ|so*Cx!2yEpoKL@>-p z+a5qs%ZbEoXS^rfm-jGjJFdCGkLYw5>sH?ie;U;ML9|J89~zHQ{uLTfdVCq5J_i$! z4>r3nFd3>Bh+Csu>@Cw5+UiPgF4%s%r+p0f7wxZ{xmjMLUfKC$ON$;j#Bk?%RJN?lItf#8pESpIp55ayx<3=QrhcJF?Msv%3`$NbOgaYwGYTA_sdz z-_JZeV%1_P3;i+RW#ClRkkJB-%-YI0 z+Rz~##nama$-Pz`Ln#ojK6&}BUl%Bl$gV|;pzD0ouB3@puVOll~hwnZQa{Df)ulDM*Q zfOpGR>3ifa_=P?cFv*k%F$Y`n#`Mdw&z!}vp->6w^4(>?a2PE6B7UhwN7qp@^9}YMjL8BG4JtCGe z)W}ZeQH$A*dzb`2=TdZT(G%vaA^W3D-PP z#M}pG>d?33ok&UWs6aM86}t4_2J=|T*&oCT6|vc~)rD^mOy+ERXb`4paeI(nZAHVu z9B>9VE-;w?M|Bsag(;t)$?#h4v=D7Pw_1`($$BKXt?X~;**8B)Buu)w2ZBJy#^{=|>)AViI za!2~qAS3~T<{Sd3jmY1*)yJT|AOdf|zcTrE=lZV?I`r=!{Qp%HeZvZsyK~z6g#`Ar zd72JDmNupTZP0BYIwas*ah99R)F-8qUkMov`s$dd^N0a*Ej&FI_=fl2{BI@0H~!Z* zognEXe@5ZP<9!lWMackYzS%f_vv7!Sc2Aogv=ZgQ1R6E!HevWt7CMb3SJoWxDFf=z zRIWpPJla13ry;87XCv{tyEO4YizPF@(S4EPu@dP&sw@A+{S*HM6cpN-Z-XZ?i#i}M zWdTZLdyTeVWxl;%I@_pGKun7h4X&;Tfy*k4|Bj2ti2VQnKOt}L`*HLb`Q14BZ{_^o z_{#5b#Ky+vi zEF}I5=wqGqpX*zX9pFFIw@fg8iQU8>Lyg|C}6FwSitYdZ9IPH2a|W zNhYV!hQM9gw3JpoXsm4PCnB~azddJH$ zB=D4sK@(!Xp1`B{DdUX8(=V&77p2FhfDemC9d=)v+?)WsIH1clRQfqzO(EI|C1B#B z`)u;mNdCGQUt34l<$OGr1`kZqzWZS?@lBkn!L_Wc9ZM4Pu4~)N8bB#fbwT$XZQC$0 z_!4I3J&?GVINHx-TB;dcZ~4dm3dGMXQT4cr6?Ip#gu9E)pJ+O{Ndmt@zAouVx?Uzj zC9(=mqzlL9c!=}5pTf0+ld~87=uHPlpUe>tO(6lMd=|bP%C1lwPvE(hJl!Rmwh4Z( z1s@BxzjP_Pfyjnz8ya)yS5h_|u3|$kxtNy1f17-Cyp}PMV7PHF_hY!fXjzcW@R+*@ zegxUR?#<#`aUAE498fxaJ1$p;U;5M89Qu#kj{)!H#w+-x@ESH3-%y2DJ|Cg_#;N`w z#;;PvlHp5JE0*`)T%XEgXBzu$t(`NGGlx)(GvRdPQ_KSP zjedM?T-y4Q4~4W~lGP`>yG^H0g1I0T(}avye#$0uc}JY-8QPXnk?PO$f(st=*jmy9 zWc={7#~cK%P>WC2cg+2LEU{}QI0-(#cT&X$lc*d^vQl-4T%$l3{BrO`M@s_etuhde z!Q4*0^`Y$9e*yxcNGdpTHyYNU>{2+cl;l$Y^N70L=xYR+`TKju%;xTM3y&GbU%F)E z%aDAO2^~iGMOmAJ)Uk(K=EzjwLUZmMY025(o1%6v_hY!fXa(U6#;&;9_BL0ZBvCDh zxwpe5+~SF%ZMwtET>yDPIsVA~81P>1ydO5Y8JNO)+VhdW(rZiQJ%|)zD$l2=u(Ihx zrX4NFKtK^y>&eoz`FEd%h$)kYmSGJhluP7NsQKGyMJW?qMmWHo{4|vCikoUaK0jZI z2tx5z~8BhAT2d5H;90YtJL<`;hCg^fNG%#ETl@y2IgKQ10QFQ;lb376B z(e_;>a){uTH+isG;%8GZts){f1O-t5VeuRJ*k;lj)TK5HHW!{Qp$H#lN^Jr-I~rzAH&E7*iTiB&FIIvI7q~4y#G)$Q%l#PcFIpsC+6Wrji0KKKYLkPE5?G>b?Gh&9 zN&R^>VbmLI1GhhNKL)&)Th%a0PdiZ={$Q7jZb>q4{>*F6!^}tc%X<_l-MtJ4aORLp z9VAVlUA8e@S_P5_Qj{4yRMV);kJ1s1j5d%ABDhP>Rth=B_#N?MGv{1$T$937UqfLb z3MLgu1M)gMKGVnQf`A(N5Y-7GY1?dRGejPXzFi`;XUfeia~hwu6!UjuG8n)l9N{6p z_?<^N+3#&Ug;+`C)doDz6Xp?con({ZgozmXc zB8E}OQzk%bO-wM!)DK8;i7P3Y=JSHX#fwr7{WUlH_ycZc9p22mgikimAmC{7Wh8fKv>Sbx0+jv6+}N8E#zD_S>W&MTaPVt{#ukQzRgV)$Hh$kN>5*oHOlrH z>wNdC!9{~;z-%pU3+k7P2P=)7yaErwX2IQ)OC9D$Dx&Ob#P{pY`GW_{68L3R@p#z} zpoT|!T%OKPXo=98l6n|fzHJp=Xc_aOkqBr<)jA;CaX)~QkUtP{I*REYZf8rsg?v&-* z<$h0)5!_?oABoH%07nlTJVL6&_xU;{Y)bu+`*< z#|c$_vq$(jUqj5Xw)h~?Yq;d+y{Swq0XpeCSYVQ5S*z*e3XzRGdllsq=^LQC2s;{^hd2jyu4UZi)n}8^-02FL@mz72HQ_DvrTYME=B` z*prrHYUh2{48Lvbv0(d4mnb;yoj_!C$v4yjQrq7!GUBY}hBSVK`sJs*J89vgm%Nwz zG2CCYFh)%Dg%D)2EHJZQEyF2>)h;M+e`FGjs0==#QvizQ{*n7J;Jw_QXkA04OC2P% z0bc?KYbauW;+=MmrkwQm`tzqfXF~zsr81L`6>EAD{LbSUp`TNq=wuW}uuEGYQUcp;#7l0Iib%{XH#m z`AFi$m$-&*>gkDzTeCDEp!MO|`8Ekl_m}C?lO)}Ax1NNmScfu>s|)~7`bg$?a&RZp zN4&?6b%DgSOfp1Oq*0(Jo%AE64lo!7&9k28zxoDVgMQzX2Qnpy%mD-4(fCFSH&l>=Jg)A@^4pCQYS>7=QG zMEq~)bax^V5t&-RmmEf8je-Pp8IUY34T}^??q3u%*b!g(UuBoayglkAO(FR$_hY92 zmo9#o>~Z4G!9hM0*&OS|7QAdXr#t&x8zFCt8Mg5}Du?goehl{)Z8l?kDZRS%VYndY6b<_U&VS^740ta$?+Yh|$QW0%0P?Q)a<5r~`-Z{$FXFtxTR!5KyfXMzjeXJX<{R}yk*Zyy;&)?8R>RZ+E!nJ$ zERc;m=-c$S(>a}M8Fq=yXNu(t88q;CAmFf>qm9pORia_x1?+m{+4o8cjVWi*t)Hd7 z-<5Y?!`)S{XM$&I>w z?!^GO(x#=&`&L#K&mbc97065soTk<`%kZ$h{AjL?Ewi~RLF4l~{rp%;<*)iyYSFEj zPZeyxrZsfn=!AcWdHj{vwH$O0&yj?#+|~E+2g?Txc!eD&r(3cHrg_b>IRtCAh$nbUqNSkZHuZ@o&wN z_A%UFw5IYa#o0$B<=+Zh^{Y~Bnv`q-iP{BWMeJHw9l0F(k$)oYG2nf~k@FpoVE3Ix z0QABYv%X?ovDqcW^jaf)kPkxU{Wb6oybQLY|G~aJedLjfPBDXNl58%1Wa)r0YKJYi zbBZ;xaD6wd-Q=B)*P5*8Sx;BS#MB#S*yLzjQhH0I8MC8(r(2QKP?jp*oJ?#FO{(VDy= zM)3|$YU15cS7Jhvrn@Nlp>HAdj15JFgMq${h3}8tj{)!H_8GKlGf$y1^_Jui_${r?SKuAkS>``pKSywBQe&E9Kf z-gnllSre3h^7D2k`Rh>w-zUMM6oDy%u!)Mc77j#Q2F$4%phsoT_#WDQ&e=5`xI&z6 z*O04$;zy!0E}`u}JxxjA?4&9iD}m|HMGB5-fSjW`ZpB!^zBi8%ZE|tOWWhrZ0EL1u zD#J}Nk2lGD-i22$g9&aPJ2~m7tW{+|(vY7a7U*#50m{dSok0sJ+7a-4NkUSrZbI|-G}FC`guNu&d{<3<%lo0q8i9H}t6vpqM|@^l}JMpe?q&eFyUW zCyn;bsC+qful1(+5<#^%g*)_7`u5Ev1^{!&8 z(@OMwL5s?Me*uNlkJ1)U)cq)J0R`ue%ob!Y0?k{H!3Y!;hi+IQ3|ycYZ0u==PNi4x z{QM*wnB>|0O09@}+>XCOUwWN@V48o@IgQa|_#WRqa9Y^Y{mumno*$(xpcwm6+5!s2 zAEhmzur{=<_>#UUMxi@?O?Nz2O~N^L|m^BwPvZ zhwwjhHMh_q}^*S%$ifZIVi8vC6K6zx1pTR=h4qqGGSJ3Ufc0C7lY zTYjC+m6;tL)W@2NN(MNpMBQ;1S};&g%ac+$z##)OxI`rus`Q&a;n8tQ(3 z0fj4%(iTuu^C)cr1wW6>7Gy92#g8C^@rn9*nQXtM$259I7|J3K3R2+U7vRfUd(%rx z-0lDSOseaVL(_ z7AgYLt$w1V^evnes?%lToj8+f;`4TWyJCqSU!25~KYNt6E=(xByMpi{W78<9R1I-f z)P!?Lk)}?e0Jhv_c1qgZJKF+S zXj^Ie%lQi!1aaHAM0gqnCP=D^qsQhEy9+b)ON?|7j-3|{QePeuVIG+i7V&I05GC60 zFPYIZIROi#I*u6cg%s#H2kYit9~Fu}*R-~(#=VtU^_{=|sjV2VyqJ@Mjd#_YR!j-? z@r~#_zpx*KWE=`SP^xqsIW|EaCa`@=*pY;w_R3(i#;AKpY746b`RjW>pcK*0gNrp;_4xSC3n(Gh@X<2tf|w4jZx~>(+ljV!tReJ`ISf$Z2i30 z&fRV2P>|d?x8d=W?kH^y^${n*G;fbIpH*mC%t^S2pTIJcfbcQN@~RxO=j~R?qqIfj zO6o%Wb~tRM^9*5kFMn5*JiE8>n?(-1Ev?cLQVa}eTcMMkwBguNMK!z3XR2I6;?NW* zZxmH6)tr~FHGOHFksVF}v+Y)cdd4n}C;8?^F~fd;#ni+2T0G^MUzEiDG)QQm=wg<& zbVF8yV*)STl-s?V^(bwxmUrz>O9*P>J+7m)b;3{=2CbPx zk6N<4g;g4{7OA()r}{2a)Cp2gHeJ=XEJtZ;4k2g6to+@>+w#?lxv{Mg+5+U2`eoDu zoTd0gj&#!Bz1RJdmye%GG?J{J1}QFOsoanEv3q8S9_5*(-D@P+Z%N4oLNhCZx3#Ug zwAe()U9_;@Uo)nJPH(GL?8tKIPk9IlYVZexh~Fo)ELXh^7I&1j!h?RVe`d?BHrV^T0MQyzR0s{T+?9 z{HlIfxzwF2P@+gO8U69wl8#%-+$^V(867!f-k{JjFo_I$yGw2cl*L1d8e^EJu=L)0 zTi^$-sr;g4)<}(TIT}$KAJI#IGfDIwbfw%N=Y@GGCmG&(VtAYm-FpT+<@UlGDCDQE zFA(5vMI^~VX|0#HDM%f63z2wN9{7CN&PJqyD0WW1EL>)l6^H%fc?T#_PTdrG@j%=ISQDcTtrHDwYZA?7Uv%4x)9GgEIzAZikvP#FWcWI5wm#%bC0J`$jr&ysr9g z`u?Z39zB&cd+!2Y(Y2&&}0M|jDhS} znjR^O9y)W%0+uh9Oz_KGD>6=4p4aah&x}HvMuK*3=m%~45pgQ|aM=4SxdBNJvIREQ zc!<_*5U52ObH|4a<0Da)rwttB=UrJr0z#Xk`<;6Z?u7gymzNvy%Y7F+opqbK zJiBOwP9nEzP4*xhrL8*T!fPty=i1`Ex&)cU&(`NXl8|8$Z(@(`B0!_Djr^Xx{=@g` z8>PR$(Aqc^wmL=6tuxMi^KPNWC#f#xj|E<7h}TWN>$Ly$8$KwbDkY)4y+zBGA20eu z5^mO6V|8>lJiG$^8lO+Iy&&{Ad?9?XP*f+=^hGC%FCATnn2t$$L^aNiUdv zH_7}{Th}RRlun-KTs6XPv#P1yPO7!#v%3~cpo!CR<#-RCEzMEBmk4dGG;?2iyaBy9 z6F(<1P25yy+PM3ZS*dcaT~n1>0?o!}8sZV}?Of2DXvc$sMvy zli=`r#g6kno-Q(7tv9LhyZ8GGs1)u|+5)PWdz7|-3hN%3Ey!R5BGiz<2vpG)x?y3o zKFi0(mgq03^%ry~j|r8ce^e=~WW4dP#)57tsm{F-N0_P9nXa=jm_*KsIK+OxbAbxq z9;Gdy`nX4F3#g3lQQ873NegYui_sN6$27fBa^8DLf6!>@CVkHEvxmw8cavF6$Jh+k z1pRXy?MTop1gl56cAkn?@Anr_<=UgP1ytbnC~X1N!96ltkiiJFWQ7bypgOJ44f9Cl z7{16U4w8ebdS|yKU|vc@VP1u@Sr#L1W5im6-0h?hZFs(PTNch^L4Q;c*Kogcfy%ia zr7fVUuSaPMs5tCV+5#%H3T-PvvTi}BuX^0!TKelwqAPyPXobh06?&<^pimGLOJ3X} zDpE8asF}xHEl#d8-%3*3?=PTwtVd}JsBG&|+5)QXdStdBgAphw1R0D#WmTaYRxn`Y z>e;EMU!>Qs8E0Ndmyg+2@+iT0S)bs(=LBlMo?={B5hpuEcTNRik5>SwWWRHPs;?fU zEubQ;M`;VFmg`a40;-D&Z7Z8%Ldphb>jHCs+^VvxGRhkJ1#d3i3^ChxLW%VxyrI^j zi6gEVi~UcHg$oF3@+$ZH3#feRQQ88kvU-%ZfQqpmnJvg*1PZ%C1|v`jQs{>H4u)QK z7Bo~2k*&O*o>^?FLRAsP5lKtb`%&K?sQ}C3oSqypiJ^e$*vatw9%bV!`<)9^sq`pq z0TnboN?Sm6Pmj_TPz_ONTY+a>&Qxww*oroNFnY*H6h}dAJ>}TeRaP3m5LnqVI7f?W zM2?qi9~xEA)$KUyrn%o=K&3{H(iTuf(xbEmRJioWY(ZTK_T}cZug5LF{Jg(+h7srn zHFv08my(J`m0jckk-7d!GUhpCONnE_pKDI{ z-tyy1@$-G+iSig@ah9B7SjJ~7;lQ_i!IgoJxq%G7o}UBK?V?8M^jRG8j}zp_m;ZwQ z=K7_8r#?7L1he;R5C-zA2^L2Es~Mnnxq@K8=a`+Gpva?c7Rv8vGX2G+u30iA~Ci*ofE_h5gLs zT}rGhi)PM`Yg_^u_(6@!$9NQBXB|Y(-bxm0pKBAd6u8+p<6Lj;dcD17T^I3n2vqZ{ zcg3unQwGr(%QlYtG&Kl)!kW~=!Xk0JNHceSaM~2QYyy&>>a@hbi#WYrH&cemP8{c{ z5?y<9eS&ar8}1~jaJz3cE`iMND2Dm-pI ziI27*C;5RIy6+_a)u+u-h}MB{qF9?|#xGv0snWY{l%C(vFk90V<4e)hg{TFR+o!fG z<;3dgv5vx~R{VB}s>vNvmh7r9=Q#2iB_2(PLI$g*IvhBLFo};(RP%ju@ zUGLl3Tlp<5)Q@Y*mcrA5lL+#IL{#N29n#SRz=e0Z8C|&5>4pB)4~r; z)KhN^l`fI*ENK9jI7W3S}(H{HeUwcNdu76 z309KY*fNdZBTk+w<4m4PQEu(l*sr`Lz(CG(4VF5t`J5iO;0!>rCRoXa0A8e0e2%=~ z8SYKv5()H=I~fyZF6kEI44HlYeSRy>dY! zVzK6w%;nwmbLkZkxi`nqG|N=fsDaWq!AjgiAI&pfG7(*((j8d5(f1JUj$pT&$QwO8 z?pt;kJxxa7mH^~s?ztOi(%5Y~&YeS{fQ%joIja702pxtybI&IIsE1n2SjgTIA10Q0Fh*XVHFHBMGhn3Y1F<{>b7ar-H4^tI_EcU^Z)-Bdz6p zTdRFjE{h3i9CLh(X$E#TP?9KENrt&3ZI+Pkr#VtVvAZWEPlWe75V;C4zDyp@-B}4Z z11|gpl>G@-5~8wXa`*ahd78Y0-f|te-|EBI(+FcLV%IW=Bu2A5P1UG@(mEkEE`f;7 zK2oBU=Be`%`zaqH;nar&OlF1rh`1n8vj7oi~(RxbVUl6KG{B zjN&|6GH2S9xO@{9s=GWApJqXcqP?ITW%UidJTqp(Z*K3O%%i~+mGZ&Ho{hzy_{51O zG$_%Hha)G2Q51+%L{^E*2hJ47M<`CVTc0d>;N(mZEZBD$(&2y_y062<&38t_l23S4 z!)m09GGG_peFJ)>zpIinBcYoWU%f{TR@#{pI>Cy?=jg5mfsz0>$H}D8@TJWJL0nugdrE} zW1O7lL8k|CH;~*uwRPSof{_At99^_ zgM&*-@2|xXpiOV@APrtm0A!1TKl1gfHS}EyzN@tBCU{aGVkS`hrVt2&mNpTld$&?r z@dUuw0FVX>RuboZgMC5y>;>uigsSkMH;~*uwLw;zlsGNNlCyA$a_^}=*PQg=b17Y~ zBVret&9%&XpZU$*K!Ol=EBKY>>1s`yRQ9}GcHiwwGgjHacFuhl&PvMLar>2pz8W=9 zvMKl@V`P^Gc$m~WNnwOY6%W$T!rfqlc^KV7ah$-5N#hzLSP4)TDOgE`VzQuG-P*B~ z3z>T8$F9PlKe&EdOCj@Yz-wtvG5l>dHEN*rP_UB9h$|}aSTEmHVwLp28YH_mKdO^Q zd5U|XIE6&|*K-U2P#qpP_-dcf$3#?FCOI?{6^;AeH;NhOib% ziJ83#>Xzf>;nFzc&3pI5n1UP#$D?uK&ZO8Uo`du_Ah~^NPqJm0tY5n*OT{1*w>bQf z; zJ&Qt;Xs(S_MOYi8dxdpq;Q{96=cN-+GV0z01Jo_{`K8xRf*<}kPm8$(NimHUE6!6? znoez}*SErT5vvx4vwI=#29n#SHX1v10U;4H`^p$ApW#fCl4CZphp`p-PyikuQ=`s! z&Np`h2}0a`zwU$$RO+ zvl=EeJHv|(uDC|&z4;*+=cuF>{0SFcm*7mmsZj%E#)6d?5+y4|aew^y^jMc(SC9Fx8lxt&fA~kYRAC3z{?VY+!utcOut#cEVh|e5X*N%Q~47clD=Z2#I!K zeNn&qnpj(rxeFy`1&))=%y1L_F^1%dW_=cOQJqmy`hC3Ri!N$Z*kvFX$etPuz7w|a z*T2An|0j$S2;T3@o|GO4-mRNi0R*n&iu!dkQQy)W`kF0?%$5r3=Gn{kdb?WNtn_HRPhUyJWh=uk{YozQ!3{cRT$;JsP8?=sUBW3zZalO#ufR}44y4iy(sFnc2bR+OVO z)@$cSp1uyC6R0NgkG|#AfBpe}(0ky(S}%U?ljwW>{7bQ8sQv>~@c5g94E}NZV8QKc zL86LRY=5BPxi@BD_kGDP*#S_^YvVY#Apy=~CkL3$T zf+U*&^`)OoHer`Ra03~h>JgLd2cJvkoJ{kVGjgvQHRoP08Xj*`#e#!@B%8SHeMq1u zTtTql!re~LBm^kG1F9c5%Q1I^vf$-)&=VAk<*Y4a5T{+RQBcgfdeh2hwe!<)@voo^ zB<6C_=_>~R&!5iU=G67mUzN-fJG0$Tea4eaDH~%b|8X(;(G>c%M&Vd*dYt1sET3W^ zOvV)?ECzy4h7f`b1@x<(s!d#U2P$cu0|rjuza*8@FuU|wr!b`~TDz$)Sx zI)zw5B?KI7ho`%r2|??1LZwf$JcxD&Bm}uB9Xa$b7h|}dS#vR?X?ADbnnA|q zjZY&_FQd6f^l;}tNC<#B-0wyRU?G4ChoHpcK3#P$MMgI70wvN7i?h63U;9OOD^xP%s)IkAoA%1S~ zO+vk`5(OjN&}6N$yn_ePENGSjfha?$b_v(+F97OtIJ@Y9j%G#N)_?(OBs5iNJRkrj zZJ%taaoj?g(?@aO=d+Geb}|;E#SRIDC?vQ`#KL~(89qxAnKsX2h0{P-60WkG) z6KFhS)cVQtfWa>1sWqex1!mT}HU4~C4|DV{@w3ChK>)CK4*-Fla0S7;ieNc`nXeiH z=+|V8U1_Yd}MA@kHzdQQo>ak7o|m!33u~&%fdcUu|Thq$71W7 z`~wot8Ss|%3Di9NNS!8{OJNl8#@ZC_;(3a6$hrcm6<6+JbrhYGX4KY@msGb@*XJZKJ*nBz2`3gbQV z2@Fnx2oyBQ&n^{coQY!)k$ZCnVfROV{#W)IfTou3m=#LIMVI6>_=Iu2YXtZr3>S?g z(9$sz-*c91#g;zPa)!R{@DQSN+==+nv!9ysm`(CM8peI~G z2(BVjPSEZ7U#GN3$bKOm31}+$t|I{*a)n}^hY&?x#>PVTSS1yD(1Ir6s&=J<=0>Gp zHdd{E6Y;#nB6>SiM}iu`Ku`UmoGHi%1|qfJMzFxw<x!aPzKe%X6kmB``eA7Yz{L>N{I>CZd7+`e+U zJ}>>(ZEzv{Wubp=d=KFZ3muogyB$Gd){=Kn7Ft9N+a?v(_`V~y{EQc&{FBk@GWg2n z2aPh(Z!e6*rCV6B}AaGH)}ICRJoLC zwd&lU`xvgg22mvXE}b<@W+KtJYQ+xd5*z}A3}x<-A)qH*L5Qv*LQWn~M*d4==m>ce zBn}j+cl~O9cjmT4f#5~bvqW$BgfcbVBt7A%gpSG4=kVG6vN#apUj+Vh5g73c0*}l% zD_Z9JKYM;q1b&~%I7>*Y3{S_b(v*URL;aHI9z~Y)bGP`M^MPH-Q-9+?dsuliJ8Zp+ zOE5F=+S->7h`=ulU*ZoF5v;$@ID66O0vlgYQ()6+XBn1zJWd$l#Z_pQs2)O-45M)j=eQ^v&s8`?2m<%*Au!Mr zt{^1vdUAb$j1!7ka}t4|Yg2gC)^2K5270_mXmD=9TuGZ@lKZf&9zB z8tE@s|G8j|^aZSky}Xl{#ps-G9u%yHa9(`cavd^9h$3)(HrspxP5?hia(fNg3|1qW zZhKqj?{fJl(_sb_CsMeq8}30)eCch zDxJd}@t&*t|3R<@>TtgsSR+Be8pszv3D(aeJZ9Eq-}U)}<|vp4naWvL8@;Et{Vi(k z%MCRRE0ExjAYknQ_ceG6^n@!2`AY@hKYzJ=2(UhcDA3sv$ReD{|Ll~yD!LiJcfB^c z6=mB?;(^+f6W;!pR0bB}T=hJE8LlJ$#q~cI*O9;A`VhloP(?8_72`p1-EP`VxBRXu z)%0^IBQLg=IkbA@4@*;gsS`WdXM}k0*Z$)AQR4bg?x0fmr|Ngz2V8{fQP8N6Gce(k zt9Z`Y=wEo$qj~zBPXe=YRqZWA&Qgqm{~)ddb-3RR*O4K(4&;lU#C6+j(+df^4enC1 zm%HdgrEaI(VQ1meIwi-q%wO~-^v**#WC*T9k}-jvaD_nuADo2F2sL*i@zT4q9PC;a zKf0lzok)(c)|aGME+6%1Rg_bbMGuMrSTI>4Ud7lVJ2#A5nTe5(W17kpdMgNXeV9lV zW$KYttCQ;t$3$2%&=v z=Q(s@>ju_dSmcehoj=3;oVa#5-+1tsF&31+jOEW|EGSh?A*6IbT2W^S_cl3e@3#H^zbjVJtwt z_({g1gIMZi!8CMgM?3qaWA!;}J%mp@NdW)W5*`=K?G07hvtxkIxRi<0TPz zP+;Y`hKR=&V8A}BVjT)tnd#Uo3VG-|?_io;#<0G~xcui2Ip?GoLf|My(s7 zYa|Q{O%DjH7vojxL>UzFU$_wA=?Oe~zT0N(KSalmXQsR!FWlL3^FJ600P1kR8?d56 zfECCWKMAZnH+Np9xXwp2!#xVQbw&0T23t+abn4?l!p<~rs>`>h;86DiE6@|JFlavy ztQnRUs8$ylrj&8Q-fdnrKQVF@%ht=04VJ$>|NdjEU?Hc}JzxbA++TI#5Wso}(Su^J zrx#1JI29&ZNXvyy_&2F;m^)UBR*Z}Si*u6dwiF6AzYK%X{$lW-i@|7LFt{5g6TLP6 zJ)^-vF}S+8td(TUxK4NLowNU?5-o zBnGG7S(Vz(dJ^%tS*5OS+%{93QfJ7eUhi>g&@`NIl5{5=+I|cMdcqY3{pT^*oXUX= zw?oqZEGhc7zn?qiG+{u`6&A7{6;`0*5sM>KouZ8-(ZS0rAdx}n5pXHPI7p{*IuBQYFX3aaqUE>c3*O<$) z1M>sx6`%Y^Nb7Ecd`{c@m(_b8&lnhB(&03mw*C*oHBg89-QXG>0@pyk_(`~KxG$!b zU`6E&?C8APL6IGU@rJe0 zhX#cUTXYCLQp6WLPg=28+>g&@F%ut__C8tp7uk;z*=Hg%{4HOi;n*J#*>6-)i}j}$ zI)yH93a49&ls7ottL5!Ck*hKx9HMz!IQ$<(cAyUTyCFLU1lfUn@sr5TSWAPiUCQ#F z2hCtW@n%K%y6){#+3J$TQ(kAM%M`Rk;V||iJJ1uZFql7&?4x(kIT(0{CY6E(EwgLT zY{rfvEH-hAGqvxd35^0^`g?FB+PeFrB2>U0Y^ z4puHcc>Z`+AN%a$UMNv>QZ3DIvfsY=e5|;>{YPZ)NK?%qSPJw?KZ&IdT{qGt3G80l zq&;(axj1}LowBF$oaRk3;@0FF-n2_^%W_6N7jn8hez7_p=`a%^k+6fuO$~!p45;<<(tPUe$ zWWS7pVEv^ae=Y^V`a(gPZ949G!o?3~{NJP?;Cqe)8Jg44JNDN3|8)wIP(>1_fNsS4 zgzK`HG1^_64-IiNfzO>I*7+%R?&OD@JU9jMH{q7)h9lQ&%H)??ep%#Ic-ldp7T_5z+X5cP>ZRXjUlgLw>9a=G4 zzH69W=pOLPs0H?4YVqe%3+yk{qLFBDfMJrg`0Eu)`rsSR0j_uiia7rWa(>@@|F#D7 z!TZz4IST#|YC#geE_zYy1~+%=9lZ+O+?p2t#vl|-|C{q3!#F!ZqZkLL7N5{9R93Vr zv(9*PB}Y3Cw4CS+a4@-^YK`CHo$=I&C+J6h{#T+KK(F+Z)Pg?BI8?4l`Pi7@wrYHk z;e=BF9O&F|HbN&VQuj(fw+9?HgjzsuN(6es6$S^KvkrYxBG74fs3o{)tv$Jj>TW8w zrFXbMf25dd6838=Yg4$#Vr*}$PuNXEi5}Z9KIf@c)`1(J|3p>^kyhShDv73b)oECL zwe{BV#`E7;2$0UdEac0j&QPEB5G({pbnmb|khAt5wm&Gj0UDma8@a)TkQ*hBN!MhS z^)Ze<*=|Qr@v`~Wg-e;m^<-+k=vJ{hFk`=r+~E8rH-9dRwC=Bo@-&Q2oCd zHvwPbH-t zr23H8EKoYCz0hAeY|cQ@9cku#qJbrDDhqiv`<-7#Zh&6tC&>+Fqm+S+SvhIKz|Ke? z)hV7W-4D^M=xN2p#5Y_^W7WyvaQ2fMpeI~maDNH8;qjX|(=4;L8aL`AS)!O0lwlV^ z-Bk~FdSvCfWQ)QL=Wplofds)(9w+E(MnHHC#R3kB!9cy}cf(*D2nOTRcl9vWJ~1z( zrGBx&{o(mYl8()q1m3jMLWB3>D~of#41;n1V(_1f!MI;AxJGH|DXNWki1zG^h!U8!2;zRhFfowL)#y*ZL16h zm&>Iop9x&;kuD17wrw!9{RoGcA$%^`P;Y$xG+H9pMKO{541T4+_^n9qxC7YusMMuXz`vlvjseTCVYLJ(Axx%B&mS zVy|#sd!BIyi~g!Z5J^PngU^>mJFDrW`2i4uAp1jBKJnzsgUyA#~ z^jL_@aVE9qCKNe5y%}zy?0AIf7(Ru4k9E+;r8BnSTpm4iTJXhkPA0+w72b(Sx2EQE z!-dN%bVi7yd~LXcq>>a^>Z(@eH<((wwDPRk&H=}Rm5(*){YoTUCzG!x!YwC9B3>S(bL!Z@ zN3bjg;S^ebjL({|=58V3iY`CV{Xucrga`3jp1~mDHOt8~_XqN5jIjlH$SoOo1JwOZ z?bcJ&N%m?wPEX=*5)cOp;~-du%Vt=kEH&@wP;@=jLw$Wm<4(}VYS3_*$Z6Hg&EQ>i zUgF%lNdy6$2oa^)xB7~>o6g<#vBtYA&yD2lCcQPV)M8Qgo|;rKWepbPH8YJ|ySDa_ zKb^Cy8ubznRd9%EHUkA&zgm!a{{8z=!M$$+rg>J#lgPw)G*C&X;b5Gh2&Z4<7A9R9 zb$6y?jqU2?XA{Sm%W1=~N!w)~m}2J21&D});MDr?pdJ7sa8RN^m$gHQJ}}2bc_+c` zUf^sL)TELy&(^F1IzCUEDz0U{NL^kptfmWF_FX>D(f@_?3I)Lg*p7#Z3A1OJCRt#| zxOm%1p?sA0=23DlurDIsNIVzk8mWX~CliaYfN9fKplmz{Qz4JwF)8a-L7YI6gO$`= zkHK}t#W9oR|@=}Z(1I|4SoctW29n1_n*N45lJ@4hXDS6NY9LeHoo@$zSC(p;H z(##AsJX1_~o9Fy`_gvX~Wv4b&8H2f~rmk4aKuV z@sy_w*Ea&x-3Sn#kGjV7sZn?rc&oVzxLqLTOgN>#*=RJBM#)h7{Mc;wZQ;RN$Tq%D zsA_qrr{Xxg}#G&u2E6uwe>KTsgbja~^?TF-_?#)2JoK zRkiasSbJa55w!*pG{0TNk&0G&7Q_jQ`N8uY8d`avjOuChca_Rbx(JczJRcQE5%Bo8 z-J*;HSOXp=w8TE6S!Q!f9abGff0I^o`SHDrS$EEZN-S5DT6uQjP|v2SShCGsTDT8; z*(uq_2)p-vJ$2KCK!fRvrY(aaWR+xsd@ONJeB3p#MxafTf*Wg6_>KJ8rC#lqY@F|0 zzd@P>Ymg;%3(m(hqABiHta0!)oK373iAV}8{3rx)AmfT^wgb!$|!=igs_p5^SnJ-QHx^} zXKgjXGw5m8_GAL*SLg;(*fyNe5%ieaC5`IFXn5Qug96mG^Q+{`jxY#}#Y&%MKj9Ui zkmA%>mR#jawXG3qtfsqouF|Zbpdqxywygy&f@};)tJ9V%`Yb;Swx|;|e02`#;w2aG zEC?+2mR}y#Yg|)&J3i%6 z`$f=g7a|-HSCmsb5(?`QA68rh{>~sjo_!!Q$g@P*wbxEH(zz-~H9G_R?xv5Hv~nCY z`z|t%&Yg6wg*koyq~8j^h-i}PL*ht9&rdw>`+7 zPcOQ8>4Ys6byJKRx5QsuFUBR$Y7qrN7So7`@K01>Sy<)9kEy-U&l`U>Y+^+>=twi@ zKD4sVU1j>q7SnhKUQE+Qmivjw;5W9$VZGPa!MN-$aHgsm-7RyBUEx&t6V1RMg})5J z`@)JK7-bNK;dK+XpRHd8&L$6bp^1ox)Gq^pwJLnpiuzUSb+hZYp%BTKou^CcD%GQx z6#ctCZUt9Q`))0-5Z(`4u)j$?OrE^8x7yGHT?5#)wxA>}y~FgjY1QyvW1Akf}YP5&Ri@weuE-crvX7t?kAIiVQCBf}ulS!6X0-_)ilbgIo@6XC6n@xmzPF_|BE`uE+f?_FFh(Mwt1 z3HVJH)AOKsHg9r=aS%9|ZI=ija0e#>N+6T_ctPbIJ$-SAZX z#K9NSBZH%54ucO(b7*8;FHf%Mjd#djl|r*Or(#Nxw4lZ>`H`QGbTJJ{x&oS7zFTCW zL|k-9PJ>Sv*Ski5FT!xqNCGV#Gx0rV$yRLXGc9LosF~mpqNSoJu;e7e4OBS+%*~Rr zL1;YmXGu*2^)|LX#=fO~*7#jMHy)(nKqKY5h9Bbg+(U>S>Y`md$mtKDPV|%E3-y=* zca!Z`*)~Wv=3>gnih2^!ZbXlGW?3M^v6v$EUxmZlAHD#3!W9Pp=ffAiEc%@UqmKm} zrHt^}tKCo3c{I6fULHHKQ)4D%+8rVS)m2DNq#t9-Pn$qt>x9uhdsSbK-U=>qXW}N( zLMC&hg@Mht{0JbO|3~s84zV+GgrZ%0WgvmP_1l%80=k+KGNAxX$=^*Vhd8cs2+>2G zY{7#NTJ9jTQ+TD?gV_}VzIBY)?r=an!Y_oj zq>+d&f9;sJ^+5^kDO9mMKL!bw;L?bTGQr$;r1SzMZeUpE$?+1g!lun(|A^@PCA2@g zTwBzOXpjvdZS^`Rp|8LUat*& z1Bbt#7Xm%u3PbQqcp=_62+7ZE+>ZrEYqidGl2@sP)Pc~wnD4`eZ4#02($KduTR?)p zU70N}Sntl@m2d*n2MF(j8{Ew~tXsS4yHYKZJA+T(Wal{vMUH`v|@;R=HutMpPcJ@5Oe~@7Sb-3S+VGuwV29Pg)l3_gSsBUr^j=dwwErCbqf*fSyf^Lcod(sbS zxBZQEXw3#3!G4AT^n@!6;V)qrKAD!U>&5Xg5vhFdu&=~yEMSb)TNZH%+$O4=&%N_% z^jo|R+}9x(#vx)vKxE_r>Q^?Bi6;53L`At2$D&S^3kE(s#K~9C9P8of%SD2b zX&|44U&a>*56l-(af$8OJkCC~=}VQ?@at1H45+ZJL>B z(WMsuLBaquw|qw!4zY1|2+>3Bv4J`B*AWQhi=QM60s5}Ig2}lw9|G||*sGC^Z9PIF zAlyAwh5MfExOB?7NjSp&gaPOYR~VvSLKvK?f{+MYRxAiJ1&eR`Fx#AV5De2Pj@7v| z+IDBdn$hlCO*0_D->#+^P*N^bNa6?y1C&;OMP`NPbK26E1baWR8xvaHq@}|Yo(tf~ zy>ELgzA(v;`)ij35_1s)2Qwgb%fP`52qg)}ZwgoTZbmp;=CNT;L4hAcO4%kIZ)Y1D zG_0gr9*6PEC<)PlDT)94Pkz-f!nz8U%N3VEGX>{(`FfX|`NXLU*6!%D#rXYulmuL5 zpmn(MSe%4Luih#TsO#gz7tQc!CUoB=#jT52q zwRANSX1?^FDjwKb-exOA0xfmi)VZ``-W#RHw-gYuL%2)LO?q0Ku9!sOG;3-A@g^pLn=}Q&L$XVE{0sP$#WeDJSmk>NgQ=9@ zBUy9TSkAxHf*Wi%cTAvbeZ;tC?=e^1CT9P_k8WH~3zrh%QlE(JYlAdGV|C(JPDwQ; z$;SuJTQ%XHI7R_0mpf96YwXmLl9}{g^lZwqZe^?aCz>(M*wvhLe@7Iur12dIr+&D~ zdE^ao>9c+r))%~6xx9@iQ)(U$=ncAU_UbK#=x&ajxkwZrpv=!o?QnTP+1eseo*NUU z_R^_|0eM0^HCjppb*9x}RihaKDdR(jx? zL7@&+UQ}1`lm~ixUKP zNmhJ0-ni+rYqZ{U=|Uy^$u)B2$LmbCAG_0@ih2-reC6`JGrBlPgP#cD9pYeD6Cf|IAPVel5qt3gBvLqVRZck}&5A0{{D#dBzItLbDhN1Xn+ z6Mc8G^vpy$vKf6CZzDYY?o`!~{B?EXDh#IGyJ%FWhKcm;BuQrZbp5N)u&+m=H$LW_ zw`#&6uU>VgbLH1_Y+{OI&ns|1Kpb#f_94PbuihVk?rM*9LJD5#VUG*_0!H4^Neu4jZ0PA+;TTbN^Zr5ul1VgbJaY`CU z7{?mO^0K#ZkE4>He}94j>4Zg33e5U3=^#w)7ji~2k2=ukWqM_^IKsRXV{Ve%69>VI z`VbRCF?=8j4kh}ZI|M~o3qAyu^Y%dulKSoSqPNEqai%qp#v-|?Hs0rf{GwQ!FK{kd zj?U!y>H1G#`*2jA)l?Rt!#OcKW(V?SlMaKg$PcFBuaFkW`U7g5G|@{Wi5$V$?Uc@FAJjB%+rIF>2N$&8MXAoJ8zFBzzQy_qBnKJ zN~Y=WYUYru8L}^{nXLDcbWVKH$5H-ou4V=lCzLmx8Qov+b)Vm@n(<)+tnAZL?4RnD zGN%bYV&fz|3Cu>hX?X1vJ4b={_Vfwg9`u8+W~gSueLvwx-{cUw11s1@p zd;ybfPHz|K=^<+02Sk-Zo4att#E|t&-(E|A9s#dsNWi(>;Db?W40{_ihbWf_l)4Y4 z6{rd#lxXQKmK*n0KiYFf!B$3lvE9T5g@|sy^O7qT#$1>`-D2gmSJV$kZhuifpxskQ zzXKWo-}Spg zmv;=j3~7P|AESO2Y$gPE%|2H6JU2Jy1Wa_$C3@;@9HCo0qVx7EmO>q-swXFyHPWbk z%7W#JsErv>pIyVD6HLc{a{7`N7V91KIoN z`kL1v_wzEu&AdgIyTPb+%U!yhLsaLoW?|l=;BvKgZduwWlkeM zPIj{Y!kBoFVctDTEskW{-K}C#3=NU!$axve8Ww$mpq|UzAIvK+ojOLmY<toDg<5=}HVKQ!UV=XnfO&1Qylh7(UPTyv; zVWnR3G1hk9(H?}67270am*VUK4Sp(@VSC>|_<0%PFDUA^Dh1kDNB8Vk?l+ve&}Olc zN1yJWJ7Pp!qJnNof(`$98A9;Los0AecU^=+2q~YZ)D1t6_ovA=j4dp-9;YtvGRcro z!^1$A3G3|L-8D$a7ac0%m#O^BeUtO7GCN~Zr6xvDCw_L zR}=8QhO~LHp$J~tmp8a3-8F_Ok_grN@w2d+NB-440>*PwTz$7(^;jumW21^$=`3#e z5VL5n)}ek|vAi$fdmTCazKa$-vHZCy zq{I|oRxD|hYU`a6v)2!tL(1R$v}r}!qVi3HaHw26>!izaf$@ocf0mmo^$XZ^8|FV^ zvcK;sm7&~$*=9SJk?r4f#jfNAo}EXIMHN@D{E?sk zl@$w6hx?8>9^&Z9Aw&;#N`;sNvSI;p#!s$Te07Iu&Pl_v@L(pFW*~`l0v1FFJgl&GtUWkDeR;V~n}>xb|M_TJP7KYtFg8wo3(O_EHVj-klhfT>HdL&EE7e_L(sqh6-Dr-cwu&mi}r z`+ptE;9a21zZhkRzMxEYwSj&kmFb7%^9Em^_A7`PA`KiO)~}aKqf_IXTPv>D_>`sN zTs@ku6`T?Kk;Gk~%+D6w?)Z6zTgq9_N1qdAL@sr3pk-xjO;0i(QKC3l7>Z%Gh{u{( z9=H{tI_66p{(&e1Y{7lAlY_t~Aee%NGI*yb18g#W5@jG>MP1{G>}kS(;hYLdW9_}| zQGPQYM%2}KL%#8d4h0_)fcHJh02^|GGWfq6%0OH5ZI5aAc{W+@QqS6n`M@qIpeMDN zmN;e_XV22~&wTAV3(W8&OUCo;Ej%Eg3Sk32!Bb_M1i{b-*5kw^uw1^3&@JN~J>1DlMWl=tK7gnPMf zx5&hK71Kxp^QT_pF5zn2xYXzjQixjz)}`r*AZP(@E+Nbo7BOxZjQh2u?`=*kt@93E<8! zKs~~w9o4~zn(uzktVNb9Fw4uXB%t_U>SiT_WI7~(;Cm7PHspi^2!A&cpdx=V;V{AS zUfYHs+^fM$-CKOb_i;qHOMoy1-3E3?!Ph=qzzo0d!}Who0{;LsC?&vod8h}m4PeKDZ(cnm; zC6mrDThH?zFEGvfECpRfFuhWcG--hS<+Mad0O9x00Bpz!G!Xr6&_I(D{08s2gOben zUW-LG+705`d_nB0p-0o&_{EniZc}}2tp&{R`_@{25;Xjcd~sSo$`oH=u(Tr|y?Gc| zyzCLFDuILq`#7((s}>;%z?AB&c6$Gnzbv1)rU_ViXZDCZ9PQD=g)1{@bT*XbBdkZ3 zndv5|vRSBge)Il|cAWX67*Lrm$b#PQco?ZU>6#1+aoVepKAs40;T#VIdS zML^lwS%1)3TC3`OLqCU0l!rZLw;}F5QE55srTE z5B2e6A-Wg@%aRsfW0|grzH1B(IzU?Qe4hfZ=o22e8A$y`;F^%b4K*gB9n`2EYgHMO zVf1xBHeof3>4F^+$4_4N z{Vl<0Q$_D&dS|z$VW+p`(fU!(%a9GO`ts^Dw}wY49?MRU3D*%xs|~IkJH#B{ph=U{ zvEIz=Wb{s#+|0b>ud>6#URkV$HLNG%;GC}cf+y@r#VGbrVUrECG+?geQ+9}g=TB}!OB#74u z!MB1Rfk&XkxNE5M_kZf)`Ph3jStAEvt9n?+z>3hN_nI;9dZZCs!Q=bHYI=T^=1>CE zti*w&77zQt3z3ag$at_Y3LOvQpEw@YPaqR5L&Z&uy~;e_EBPj+ zX*aHVj}4MeI^06Qv(g8ABEF0&ZP(aBV6m8t-V$b56yfjARJz0ZIdQu$l_7A7ok@2w z7cnc8K?css{>Xi1HI*2ud{DS*Qwal?0Q_^}Hkl5>WK??}w@Ux&F2sN;y)#V!`V%Ta zza~$-K|2gz0$iwX>qqY%rwlXcS@Lw5F3-kh`qH%AE6cJ~LOEc$Lq&NXg(y%23|d_o zKy)T=2^dveYb3NQUz;=MJW#kgcl6^2nQfD&H`no%D=C)%vI2iqp$o2239~}tU+jQ=^9u@po z=;xwA-+3GKk$-Vg61&2@3%%p{s?O`AKsl4QVh~Ei&p?)4kaBQua0E(IU}1mlyb+RxswUOhy?o9(2dGWv(BE3+bXcK z`ESt_FQeA}fldmrz2zGx1%$49Aee&GpbtQFnnnxkRr%)0LCB~A!4$OQE22~P3fMCG zN%zX%a4{81l>Rbm`8~1aKBxpWt>?x7Uu{-j>}06tC@*>;0Yu-sSHOmxxL3r#n|l>6 z(5B`xlQE#D_UURBf?AmY4tUSH^~ptAbtQO@8FBo+HX;OO_+K<41R?nf1V`xq+?0r{ ztuJmPg+8&98W93ByRZ0dLj1{prwQ@@?~a9& z)cDi9GlKA$1q}G6_<4j-nTWj1ZH&B0a|FYirHv4wgUApXLcfm5h|kSrtyRYubctx0 z#MIqTwwDDDW!U_C{qgd*2RL8~nqK5){Ya?)9ws}vS<+j~PMDu zZ%%H7ewe&qvfr>nA;?TtcI#d7#Ie+eCPhTGG3ueK47l>xcV29U*1Q`LL2b^-`2(2@ z*xvFD*?<6AAee&2WW=Xr18g#Wl5CC$SgMuUV3CTR@g(4nspq#74% zb3KLx5PwfLz=oWV4ax6DHY|vlEewe)Vhh#+xSO~l98vV*u<;oSd20Bt2jb>hbHBDT z1ZMbOv@`sdk`1uLw=)4WU{Ur{YFVv}VzLPn6*(|Ch!t!5_(ckp558;nuGXLkO<)oDq20Jv^ zWWfcsfJiNrU#R6Va+>kB((=lG`cfN$jefqTUE%X}^oaktj{cUpm9gEKWem(c{?Sxl zM3R10B#A^ZOYhy}ea5oX8^XfrxNJRPIC0Fpxxtw>Y|q0oVnqHxY5}&l{C3nra!M`0 zCgUfmMMOb^*^+3nF$QKUtO%Y@JvLzqxx>lWceDb2_*QrJJxBn_DYf`Q{KvBfHspj_ zNKaD9&OXpT(}@g(y8hR>%%*6Qs7P$em#dmnZFgtGa3H~KgLg6+@jN!<~* zLpN5gUW>w~;2BhLL>QNkr3xlqVkhF9kEymiruzywz&`T=aC+dB1CY-Av>*TuurSkK zo#TK_*7u#`&dW_fDaGi1;OxN*UEA&S!!V*@ZV!j=PGmp?uHSi$7&5E<>$r*Z{M@uN zdXFxu>m$S+!VR}ZuE7^JFD4<;APbVE+BhQ~$1(p%O#hx}&q+g@Cs!-@2GlRO2}Ew9 z`oc}oxq1ByNyU3B7tx-7L$qgi_*S+3I8zXfK*KXJ`s>XOZ1R~UnaD^lYJDz=miFEL z6S)b<-tyaV6X_{80h^4om{QZwrg8)~BD6d_k4P!tpF&YFpBhG#r)RFp*Dlz*jDAMS zF3opezK&Y-q}YKil6^LWp2Tn9bqoalL1Fjs(rY??PDlXh_uK?*$O$)*{lVN6#4n;M zz5NVQk^b@BB_fEq`CC~;(}k-?eI*04SWdU4zqU>SX83*UBw#3TW>(HC?}|HH{;$Qa zr)&&)DL0LIL0MqddW@9^JJALwpofmdcl%HX zh#{^b_6EUe9?LI{qSI20jP~TJGq%eCe;^3}+gpA+5+FY%0brBylO*u?5o*S4k=PMJ zt@!TP%|w`EW&5_7VW`Z_w=rg=%3Cy$0P^oi0N9Wd5}^2lN#K*(Om6n@r|H&QDcdv( zynI`<`@OC_7UDS}E&=5La^A0S;^U2-k|yoh)Wc zx|+*@SLW5T@5pvqDG27N1${2^dt%k(IUq;G7ON}`=Z8V#Sq;&B7%t#!WKRsG3OI~w z|3uT7m>>Rrt6U;$%^s6>+>iWG-_(rUX5RaEFP5knC+f_Qf-q96CN$;qvI^6!{pMB< z?v|<=3tnpNKs2lr_J;egHNIZ+oG~ss!ac;J&B+QcU3+HpxlCEmh7vLSdtxz5ON@hw z$8P0?epjUHqf<*4-q1OsxE9o8Iz%lf)LehzRNp*m=KVl-4n}1|AQyQr*F=sl;6Ceo;j@Lrj-}KVLAQ zE&_vDaJ{n@RxVJSmrpw+ltOK=>pc+CuR@5KIp#=<$z}*IxLfx4okmD z0u~EX&336d;>IZ17SY?(z2M5=C&+EW5|EsE0Y1lJDsdgrBt~}+3@LFH_cDiMU_r#H@8{hbPsuH$$Y;~*Yg{WI-ugA$Ji?5UE!dmh?q<9nYI>c628uo3`;_jwp zE&`S3heG88byJFZHr3TJ6tMIbpG#5U5+S1NhpfW*Nse1LLoP?Vq<=2*Tafe2qj42n z6H};g5D%cdJjqUY`0RbFGe20QF-`RzD=yrZB0rLNY{lBqRZ>fN7ESg?w^t0Nr0!xl zLrk%nvertN&P(EALV({p6E9}=0tdvvhs4dx+T1M0oDP|i0W5rUM6)j*xFn=AR3u?C>Bn)46>7olVU3f@dA0*En%ywyL?xlpN6+~R~ zX{B&EuU3au7i!H@@H>|9&Bpa=RGUxpxu0BvjTCo!9iXlwDR>h%UmlS%VC1rW?0B}v zCDLGJrEvgR-QIRLPn*Lh(&vh@9EYoh&`w7z?+>tjGCV%9PCCj68)qc>WP8+yc&XLJ zQYWbeE25@_q^1c~KJYE<%%2r}o3npsz#+*?mqcQL`a|8zyBLWgTIzTelzFwje_eqJ9a4@7JPzM6D6s>)BPXlB5+?`My$o8! z*DuLFc=QEtoS0p%vSdjVE;;q}UELa4ZcGXY(^fM<16gEB0j1Ci^fCgsBf2?OsU_&x zxx9AdcCLe^M2$QJz4sH%0je~Ky&fuq@Gsreo>lu{&=ulXAF1iZq=d!Y#`eTi;hxOe zcJ77{^|GtxUq5iG*$ZQlKEaYsm38ubgE7LD7HPvZ* zlw*zUfNL>gejq_Wq%PhYEkQ-<>T{joDnaen3fjkac%mvuSC=_1`9to)U*3*fq*?WO zHvQ>H*>VqTCzkKmx%Cw1cI&mTR`r0hFDD|D3#SKm>g&0|jp8UhzB{IUdopJ?g!$z^ z=hn}@uNNZ_y*q?(;nsub*3*A+>jN7`2>AGnMQ)tetv8XegH1VV%NKh}Ba6XU&l^_F z8b*QOv*qQIgo>G>751a4{(bWvPtJ#&xlJF`*#5PAezO}`F(K^~&DHD-$yxt;K45Kr zG!4j6h)7&^DimmIt~H0uqPw|mzoXCK7UyWtamg@CIKNx($+nN9+sXr9-bht8+ojlx zlC${HI{zaTWDI)#{oZ-YAIhBVakMu$_qK9!mSPg2dp71Vx8&WkCZLVV`w%;9VbNW63+mgO{pFp^+2?SHn zE_R|gtz~)EB9Vu6u%6!qiAPO3UE*Q6HaWb*dWU2A-A6w#!F2Rr$8?kzO!xn@q{D^? zuhy8ZDoS~8!kk9Sw)@Hh@UZAPIeazL;Qpfs#2hebFmOHX#1FpIs9<1!39{iynaK6e zWg?gUQzoKsXlM-@i2WtW24J5nPX4=(0x*1GI)A;{2uGaAM>qdBnQq#-73?rj&GPhS zg+HC?^bjnDFPXjz778QlepAqDIaRvjP^39D_gwKvgt>xq@%&6@+8O3dDz|bILO}il zg#B7;Ctue|IPn@D*=^%p{~W&Rf1K$~`2yHiI;&(LWH#RO=owL&UJBb$wEv(q!$M}m zhqpaSP@`B6&O+bzRE-f5K=nOe02^|`7gT>RU+li{!N7`Zu-9VF8W9XLdb4}42BAt` zLObwi)?OgGvFU5CL|}$qLb{&5XCkEk3<>-*p9-v>#+k1LtUUJ=MS#6Yza5HDouUXI z9{u3M@I@oWU85$oOnbdmaK9>OVda}|t?zzv4V3%%>rjO10!9ACD8l#!MSPD|5g$&_ zX88PXqR2^f(r>c9QS(15-2m)QM-eExead@2ce393!!B1GlRTzMPvf<c(_cp|)ECt9FQyiz zFVx~4zvmqoLqT!wTvuR`{vWB??|UfrfxjGO4xwcP{jV6;pH3~dZ3zqGsmU?Ieh?K1 zuqrX~Tt;!iy)B)EWO*$2UKHn_pIXQn%LcLnCBY4`j~^;=yarfuMafMDGi|p<cp8wWEvC{wwu&?x!)G~VM{c)kp3o>!b@rXY6jQmx8cIHq1S-569CYCt3sKt-~ znp0{i`m3b|upuYZLUVG_!r9ot|9MLd5DsW2mG9fzsC3*;KvE?vyN=IstcA<1!r?K! z4zE}K$~DL342O{JTFCgR)%Buhl*LIY!H~;Ze#jzwx9K^dzWL6cs!i@I-~ju~3&8ol z`vx!;It|MJdq00Wz@a(K%XRD@iZ~Q#ec_7gp^3%dQRtF5&Fsp+2j0oIpy8A`m~xU* z*A8({O)prJwW#{>9r^8qXSLyAuldbWO1&c0pY+Wk(czchjzgc@E=Eb7vtB*~e z^K#3UCEhl06YW$h@#2lXZ(pfSfWgg=gh6zzrJ7L;&AXE-EVU$@CM0TmFib%S-3-ktx>|;3c&MFd(BU~?K?aV6*WRo26_?qP-pBA7a-$kI6S*=k(4&7Q3 zM&f_x`kbr6VeV+O;^S7cyiWVURn95MCWu*ro*NHiM*8<&(3o;_LGGrxMZA`0v97wt z?`F8hvt0K+YKNLdp~3nRLpklpHEP!`MFUdUQuHSe_x+S#UgpcY-c(GjdjziP*6en} z)r2xOH?5aW+yG|NW+Ic+AdN_m3bJ_3SNg>^Jxzz~qedDNX1fE*++Ea$huWT_QHC{?iiY1ZnQ~=&>ywZP!h>P@X>fm_$ zG-tM9!b2UNO!U@%5%VZ#N&4IopDJG+Fu`LnGbRx*SekeM?U{WO7~7vQ{p0d-ahp%_ za*H?^>RvyasEgl=Mb&bJ#cGaFbVTIJcrtX8EOV6fRYnOzt?(4_R`|) z;Ts&iJ6Ip~xl@mC5v$9MhT@s&wt%nIVB)Xm1C}~?Xz5DWGIVXks|1hjUa{#T3VjD5jO<6A_A5jr5-+d! zo!?7BSh~+mhm9PSt&&ESD0go}D)lg%$kk&&cUM)BEbKDkkLLMr^^#6;5ZHqI1_wdN zssq6kv;;ibQ~wUwWc;Lm2j8*Y=h>Eg?WsFyRMIQHhqlt3u{^zkS@X;+u1s$qpg;m> zzxVHe4LR}eX#ZgUu9agDQ?Pjuk3nB|sDZEIqdoIwTCA5@y76*7xmLqhEziJ5m_KdB zv4ZdzZ+i_&^Uw#%JBZ5yxTV%tv{dDkyE!vWzWR5-KJ!oZ?|{`Ap836iIc*@A-UI(n z;SsPY_%He|wWf5ZPB!gsnoUPO?(HLEB&@dc5!B93-pRnvdD6hLeY&Rr+uXj{Q$WZp z1i=)vBt+WN7<&D5v2zhgl4R2R7kA%Uhx@gf&_YoX+U{qWC=so>Vq^X~I;6dz!+$Xy zvVNgMcPFfkDLo`NsPoccDf-y5>U99Z7=%$7t*wg5E3r0MfM@#_laLX793ra7kEr2- z4u7_xHXPSg`w_YKMl9!~!`a7#Q;k7ax8ZXtv3s)iWA7aCi%Lwe91td(BA`;>mHvTr z2yDT9vy+2>d?1*DMu&8#bO>xRev%F~GZ@@$Se-sTCq_l}8||r4=kacB5*4W>8@MV` zg41wv4G7)$bO>z72_4e?Z_ptjWQumoVB_ND1SfdA>GA<*=<69QM#tH))vpCtA`TS3 zHkkru_+K=c0+t7GhPZ(Bvp$6e1&LAJbUk#T4IgJ)E2R*rncXWC6-dGKP@W+@H7c&e+fijfpSMbXZ z&C;qK?kinDgFfr$c2!3Ib!`cRG#DmA49AZ4q$~m3IiUf<7(V)PQMi!x7KEGkUdv1u z22Q7iXicJXWt4yy+^EnW2o1m%+;0aB^rz4OY%+cl8k%x3$jTX)_f?<)0nSxTYD2?w z$a7*vdwL@qJ7~RbMUVjc@1X(MkP~R2|KEUyPr?3AC>;=vu-2NkYb(5uQ`Dz>$BOs2 zaC#p|(kvGD{l9)xKn(wjepDbp0|*fvV0=*z_n(N4xzWcUuVbDw&sG)BZsWV*#@_x;3HQZ%6>VtddODYy}*(G z<|_07{Sx;BZJoMDIQr~iV*6ItljuD8ILotJpz;!%N;Kh7<%F5yUk|(3Su&!$ev|XG zk$PxGGp~Hy;`6z86!jG-b>u8*mTE_o`xLxl`r&H!F#Z<&WYJsufQ-OfAtj4b1@}cc zrU%QR%++Wfmj^pHQ9XBmjCP=ZRCRKy609!N(}TOuu)--LT1pU2v7u5phe;<=uP;c*d? z4xQjZmwK12hf|UIM!R8~So+LM?^T^7-Mra`H^Zu30N&Z;N`wM=biam3E4ctT6>tTqZ6E=%Jdi8dUP@@oE#_&w z3h!0wy}?$SAXKr|1*j9dr>}bW(A3FB<-^8s4Z1>E$dgx-YI?D~h-$ zrED`nP!U#X9lVphi3OTRgeYkSbM0663tgPdF6CVMeC{2GHl+(bH2pwBh<`2tNzLt} zv%s{5U`@`%z}5LzZf5upU?)aV07gN0FjUQ1AG4A}#cGzNELg`@yK<6LU8E0w2T4r0 zB;THTFQ}sA6N+L%dWIJY z?$jGk=H%2nH(yGt?#(0{M~0@UO|9SKq-!Zsx26_L6a9BegkEBPc+&y~>-DITOIVDs z1l++d4ct_FtTP5$_)-*-m=XPl0oH*l3=&LS(Y^*#G{NS)l4+t7kyl*`Gq{f(`x*i~ zcE6r_>%HXIyEjd;IX=1Nk*qZERt{!iBvI+AUsC9Esqx5Sq+jQv(4X5yA_^=C_gr}MfxoP!YB8j#@Gph~J0U~)&jrjU zXPK7Ftjy+C<_>4po&^hczqm1RSOwwmQJ{CZF=x>J`Uduc*cCThk_m$B{l5^-0L=8{ zbN>I`m-puvS95%EQEn@;S&)0)SuH)Ui_$%35i-n1B|Uu~U+U<%!x2n1MlvW{OJpwW zT07i02G@_K`Xa9RvvEzNPxLN_ep=82=X6mx6r^~b-FPDp=q-m9fK$7#Fa<3l%W&#k0b52t z>0Hs50mK#BqFZ?M%VUVQ?E1?UL>=)zp~o1BJ$33A4ZZ;hVEEp-0ygBtxnlT(ovVVJ zJWdP=TUKFVD4S-;cWMr{5eIjwN8PEOMi)XL+MUhE4(EiYmuYEP^77yzptOq{E$-3w z#-lj0xtkJ7%%HL%@b$bN*k}I9=k*}CY=5Ka{lAI>fK9=F(Y5HGaa%$74S<#Mokq}& zn3>CrP!k2bX>S^H-tE89lwgZ+oML_$-bCepJHF=^UemyEfv5lC2s-B%JhflpTM&KD zemL{LiKidI(y`}Ri?}2H!oT0GbPZHI4Y`5;(TjF1b9qe43zj=@4FTeQ>2yav<8>25 zRrqeAp!4JDh#kaBetwNhh6dVztx1`us08dQ z{UjuYe>6Ww`lsgVM8AM zNX>rVpxWv=qc?lX@$_b$ffZ!c zGVvcoEvGvrz=1}3q6BDGES%>Hlhrq# zBzyF!Xjjjkm5Rr?%A^RkgZ~0>{?5ckv=;yuY?a2Eo^t|DZ+!SivMm1Lv0Jj}*MjmM zy1E(lgFRW|zAX>aS6jLC^bZ6aU<>ZI103d4zyUTHKM6RlgORySiKR;952abPDwkIi zaLS~SMDHGjMy)u$)1(ZA1TcROIKYOS01os22H-$dShkM1vBR6VA>&~eXF%k>R}D{G zi%W9O&d@^#uuOS+LH;NI5165s(6OiAnS2xw4ru<*P5&9d0pV+%mxbUSb-saax4U*D z$&YTZJC8T*l@Y}I68L@b$vnaG!Z`b1$3n~(Ec7pCq03)b$nv$C<=Rxv`=Rr)kf^tl z>md`zB+EWQ82yegp-1G+k%y^sUQ*>AsKiP#ffp?Fcd`&B3G$Aj`ZUetIa$c=^|e-i zwFWinHCgl&#VFG$>N0H$*t?L|5^?#Lv>s{vfh+`U!TokD#B$0)z$W7-Stu3GtIL^# z-P=K0*=&o09y2}|&;P!YpFvQrCFQzyv;ZW4<&=en|4RJ;HspkbSWcoBXB#>HNqr%K z3(06&a1g5n_UZ4~uwWVG~W9g{7{zYd#NF0koej7>aWu<53${z`QFvW(|>v1z)) z8yQAyX-(CQdhDiR>8NRCUDTCVK61lqqs!g(H#IJ>>F>lQ?EBCTIo2s+X5b{|90dum$(qVH4{qHUXQApTwr!flR-Q z5$Qr3hLud{mBiwkSIzD>1>DMfro#I~yXSEiB!KmMYyvjq1e;hdu<1K&N{DXki&q*8 zIrQ*DO6!(vZgWx}4wa{^*bx=oa@Bxz@jO**JK_n5`XaXmKUDXovb#yEtwMl(`Wq9b zfoU{#$WxcQew|{oUMTjzSh0D(D7G22g*{PvsQB!875gx*c6|;9MiI4#)koReC4g7z z!NX*m@Op*lZVy{heYL*eMWiu2O$cMvEQaGq$FmGDdOrzsn54 z*HiW&(lJWlt77l2I!BJXz&!FAJW`v)MTIgGIk;U(_G-Pe_pu>G+nxAdr`T*4iv2HE zY`!mwZJg?`<9jpGmGHcZ{q7UpnrKF1&(_UA-Z5%0`7jkk279w7X}v)UFb_o%;w}{X z?^JAj(RIt}u6_UHb1L@1?#q^Ybm8dxl%pQoxU<~vz^^k%{S0~H(C zg8S_hoBdR=flbCwD)t6BWO0b?l`Dxnc8lEiKKR*JY%amq%~JN{5$5jmCQCyC*uPh7 zU_(w6oBcwuzf){$Vt0J>Ua`=tct{7K4}Fjqu)_y+^!%4n=W#-Hk42fjD)#N*(Hy)s z)X`U5WTbusgkFa@0t}ST;g{?f`LW;O?nL}L#b&=y?0>Oh^M6rnLm>~h4uZW<_VX&X zm?U9PF-p;u`(8x5EC}`7X|k^@Y9Ey)tTjoeFD&&^Unut9sn~>>^K%X)QT74nRBV%v z8yVCoZ|3oBW*XnVnb>(p1(Q{{LnFo|FozaxUOfH>DmJhM_uDBp$Ejiin~a}S?6~n+ zMLVB0DRj1(;*Qv$=c)#h{uOS;h_W*kkpo0dCs$*0e6QHRhMXui$Aw~lr`Q%IrV!az z)yegms1pwn45*KP|e< zTjIb65g(7FH9OKz?;gzco>XuXG1^L>H`;Zcm703**2UppnPq5TQbZ%uWhWg$}UJT1z;BL1A? zP2zbqp2jBa5ezYj&FRR`C>{u;_Q6aVX= zqwJ1mW5lw;@~tPGz@I6LX?l@qSnOZ>$ct) ziLEgQcm?n-9XQ7!X<>U0!1)sgKTu7ToU%bE;HEfyn`+oxp7+v>d?N4tx{-8kbnZqR z7KMB23W^AKHhA|fJ1OKeYX2>S$~#0FI{$^(_4WSg8OI(dF6c@PDHKf z%`XuBU`jFK+gnjY=j3l3jp%Ez!=Q7DdbEj^n5O$)DL0)D96R(|>VWKwHLHoIb5M*b zZRw?W)rdE6GURyGRV<^X7od{6l2@wi_3ARq+MEGQ zjzvk^ytu*S24kU5{unN%%)2Pv+s?*W#)yc-n#Qj0h9L6q@Y|^sUN4@3^r}~z*~?X2 zd_1UwVX}sEbZOenY0q8$Mp8$cy4{f_d#XE&2cNzVmt+nz zF6y^k7@sL^Ft<}q&FPpInY;{&#^6VJJ@SSKwgoxy%8UUm_jM64c-aF0=b5Y!V6b|| z^pESph(B@Cg)xH?ork%6gn_aT2 zOb2DJX{Z7+H-a8)GH`l zv08tNDoch;^{~sB6RYKJf+(bb4tW>E2Hv!mZ;YjIfx@fLn@G7nY7YjtrRS0xS9>gl z+WWd(PiS5kr55bSho0sZ(SDY6OU?(g_})eFHjqS_bkgA_-7T;(?0S-DCelij;BgH1 z@=2iO-#^xHO5Wc-5JQ+?OK5F~H@07m>FML@@mGR5L@CjFWN+G@)nIQtVnq8o;WEA; z=Uc75nWv~2(`*m*L*byjnk44zkWKsUwe7Qd)5JAun+W^NUVY#sFv7|*ay8(2GpYs3 z+7sR5^amr0F$Qim{OP%h2;OPao|&_CPdGEK>n6T01F&262b<8O^+Y^HZ4`647O9+@ z+1;%0 zuT;{ciE_I!l>+y5f77Lo#g*8FXlRQL|=^NbH{1a0*^G zoxL-=JA3Bi;l z%uFb^;Zn_(v@1~q|S zBHr3@o%O?dC1nG4nA7rWp&EreOMt+a*nt*)9E$XTln?)TV+YaXD=%&vvp=RR6wRCsqyOLSKX}hKByNe15lHStRgd`cj^qZ4b^F zJE&tHaFth#w$>$6D$!tD)1^k-g*3QAc)Oq}ryYCYQ0x4$gSw~Hog{L1iqhqVp!wG~ z1Fb{(SaKNDnhsvCiqf{KZ~bVV|JGhvr(Gw2?JeKL4nXM73W6zUtz0=zpB&h$^39Wj z&;$bnQ_%WlaGpjzfGwk+jCxFM4tm5~<)Px!3LJhMZgwMBuW>;$|2<%|7(6ecSKvY0l6ezOoXyH4rUy2H)K0 zV?OP-GTUWi9X8e563^SMutIOzv+0({B8g%LN2GmiWd_XfPi|!ftb*ap@1?&>Fh0@? z&4?bx{a`hyv3kP0^leDhP_(&@nb>}Jtj*~Q4Qx;R=7k2KKN$$7ptUmNJWcvkY>;1X z3Zp108%ufqgnMenD{<}_N6aBaA#wjZ6nj+YUk8<(7f|^xK9Cap0+s3&X)ypJmf3S3 zNC8vIAHl}&3zh3l;jrs;(9E&_5~x%mZ786e5s`;B4q@~so4kt_UPO4$5$?lM>g0^d z;5)E;{%KfLpp|!21ijV-V?*RwckQ2b+x&WN-07qNy zo<^oamxH@%WEUAq#LXB}dp_Ghv#91r@^!40`2VkW7%;<=U-pmf9R>p60PE9!hJ}E& z89QYmV6XCT$3k4EQF;SD@dVaB+?dKcjmLDN_U)Pkg1%y969&VVZjJJAqy(Kr=|{ou zsp+L)^ITg|)tX4hbnr)%UVtE983~gxd{a{7$XS*%7&eI|>Mf(;rT_lbCly0~1koaZ zKa>AU0wkYGj5y;GHhc5JU8C~Z1cHtVj$ol|!(x|rEOVFB;}+kmUrs!Hg3^EebCkXi zC))8-cHSG4U?M}yC0~-EhQ=oZP;0EpD%Dz4q#FW}G_CF%<0CG<9ncC!vAEGs6+J6$ zNmpG3(BxK?r>ZnY*Wy`lR_(pPZeyh9P^Y5shSai}yNtV{EJ?dh`X^llcj|kgEsieo z@rv|hcj*xvZ!Fg%SV9CI6j7znS@z?@x%)yXR`eza6*h8R%roMS{ z))2b}(D|LkKY2?Iax#286oo*SO@P13hUPLT5`$Bh8%1KA9H81EQQ3^DBO>b`C;|Bi z(@tB@qzI01xmtf6JFZPeVUv=YnI zY4u80B66^pnl^nmuQK0Z+aq#e#V1qKi+M8NVXBxm=W6~5!=V)OwxgN0=0v@lF$)HK z0GaCUt^T!SE{rl(r=Ia>zw}Ae4=6S^lSx7fMM` zv*JR1xo~YTBcaF*j-zRDJu0YIl9TLU-jPutvfwpRX)YWdB)I`D#kWU&5kZO}=uSIe zk+Jl6b1-Ra8@};`eKoM;_;N@wx|LxaCr5pS`T*`TFE=nZ;Ed@XchpC~cXHI1wvi(c zTrWNw@J7$S1?#zn>f1-~DaBiKmU77s7&82oUX<|z_?I?b;(>n@VR*#ogJBt?JW(g{ zs;m*Q`fUU`aUPWcThX&r$kAa%127aaCU}wrd%HDxn#MM^PXp_zpW#%dlV;LTsX#3EN2)Z1v6I;Y#@*1$>0w-@$-}m_@NqeF zt7eCG?dv2)*2MUT%8R>G64`vXsvBuEp+r$tH|jS#L}2aX-TMMqzfuZ1y+CuT;&quk=h#JuV6^Qm4t&;u4{L$rv)I6I_P=F^xqA9M$i zaR3Uz1GEK`tINi<3LVmy``S~rK0I-G)kFV8#xsa6e9-2Zu3z7j?Bb3)S(a7D*?=!3E z<+KF$M6aV#a+&PtJxPG)^%WR5deDjVTBGy1l)pGaTeMCNUGY=1<`xfgzWPMb0i4|? zBPGF4M#2wYZxRvds`4r#r0bUGxF;;JmeE0vESM{)^zGSn2sWnNtVe{(h55XRDA2X- zt~{gkoFNFLN=}ktLDOY9AX_TA$0c7AveZ?L`{key6@e2@x!Z}=Ty?;19X#8Emo8-=Z?sT9X^7)-1d-v%FN~YkmMCpq}je&nhDz7d%%DnAdAo?8DqziC8<%%ZnHfj*GR{IBPO z_-=M=teuSQ&cK;^kVRHKcaCbISWydA{|Hvnot~82S8SlH2>i%RZR1(bACX~|8qZdlH z8C<5;8@>V(!2Nv`4A_v9C>Zxe6zsbw*m&f)MI`u$ldJsLFkAR84;8g08mb^j6%LyPy3o`c)U&%K8%F z%Sp{wW(On>4U~SQYkv=_o}?5pL=KXC>bO8vN2{~fr~}PN6ZwLw3aoGHZSEN<%U+=B zz551WzgtEcsHmDkh+xsw0~c+OQ?Fm!eb@bES;6C`fqakPo%zei>#ZAqAgTh}TfW)J zLEujiOhH4{%csBxY%+cl_@3%vhDt)j?g&varnOyX7hec(h^!??#U|mQ<~B7Kh=v4Q zJ_Wu9fAzcvHsl2OE}vk@*|zup($=^jBdDF7ypw^S^Q3`g`;@TYo;~kH;A_8v zh`BE=ue8E-5aKSFLd#IkrBDzZuiioS>j>-exd}_@Ueev}wF==ir?HtNl#kB^wqpk_ z$+GECZ{WBnI=f!|=LqX0lP$^ct)BNq&c8K?geCfguw*-W+wLsdN0?&+9{YldULrE zqK6yq7X9tpd@Y|-1w8HF8mvGq4QNuqyS!vYLXx=UY5L4oF3M-{k}0Q#4r-Dmq+D}{ z{yoJPzT+f*$BznT3JahZUncW?^pGONQA?ZN1yh?O+uUauet2WFO3l&t-jUL%GLeUR z0o%2q^q35&r^b$l!Mw+_uK9O0v9i(DZy5BHfU8;E@DF)xeqXaKGIadKR9lytyW5!X z0}AfX6*nI8q}Oj^UDjxJxRZW~|5`8N=Jkf|euBCbU0bjj$h!9?#{DQrZ)n5e^YSNc z^v8MP4awXankD9>o%>zEIMl>Hb@5!xP>mf^ePofi@B_oZT{P}1jex&dSs*&%F>)7 zSE0EZmd3iE?XZHN7vyj1rnwv!!h~x8WD>}@$hcp7Mg z)q8Z`AK5*X9X^@aR!&wIdMZ~!juNXwd6ed*o}8ytGX_F(CiJz3$n$zVEslWW_}GzE z^9g0y!C|R(INJQk?xCox`K?}f47<0xD&3{f6V0h?yj5+S_hBs-RU{^VW z#l}Xug-UJu)}Rm|iH-8|)=AHR&sD+3mGpKtcWn0~Io4$PD@*oCSb~wSu)heEa?(J9 z@hQVRDIWf&Dwyx4L;uUEHT+gPb#wS2+gP2)vs-yn0bM=r{P`?qnA;*?2m)gO-ZSqA zFs?mg`sd{vHJs!dWxfc?8A)usvDEI5L=m|l(Kv}W`hbnmLs!g^YCsy>gj9EW^VN;? zg0dUh&QJ168xTJ#y!!aO^0w;Ymbs|K_L$C|kH;%BO+ zA9W43Vx_*O3$R5z9%+nwffc2xJVr0t9$!+OrfdA*$$&Gg>vIK_Iip|aA@H2rL#Su| za6R<#Ru+Bc_>pASEp*#gkzNKw*Ovwpz7frEG*bM(71`DExApXTe zkVKrj+3cnd6>(k*;l7c0mYWgJ24*#^^NG9q^>09#(+- zmYsj^oS*yK5U`w=pW5UIdrjMB*B`1UH2yl0id$I|3{5C#y(TfMFW4b{0J5?aq=w5?R=2VK+#?eND=9+^9@O{sNz=oXg zAm0TK{yYzoB9X`N#1?Zj7`|SAH7m&F_cz&;Gwq1el?h5Q^tX zZo3-wU*!uR@C68__oV51V;C_?dnvK1uRYsZDFEatxX52GGPZRL-0VB1I`aUb*XiWb zmvtp>TzP-YVu9^wOHP3-xE#)1^1zieqcZE^DYdw<#hKvUfe9c<(P|d0DT(wh$~t(> zDnKqIard@6*`(pGqZYn%Q;RD-daoC2J5e{eadV-QHso_D4|0A#pIfQ@$_sG(*LD9n zYS~+GSbcqsgI(`}T0o>0$uHC*BK|2*4#k>B?t)tW4b)Onqc-TY8ZF6>qmbm}L)rdf zU5(x4|6}jI*!$OvU+l$4Pj zKlP5U++M%i!TVL8>doJe>)ej#bT{7Efg`__T;mgjyC=kdI#7TDy|^y9AAT%OK#>!K zouWVMYQayzi2Tpvbe;Yn`>B$HImri9_=X&B10c5=S_fOaTT=4#W` z*M!K8mj0NO75~Z?wO>=F`o>%qxD{8?N8=t@7)TKNQQ!a-*#i#y8E{Sy9N8AL%cL}q zXZ=|h#hTmGuBV$>aih?|myf#pO%p9OOdss?3l#Wk_WAue!1)OlveIV|&r4Xn-TzAO zo}Yi_Loyf3`lZX#h&Pzg?pkgsW&Jt}u^-z)k2BUW(EUwnonD;}cv9j#t(g<64QEmF z>7{Fp(CE^`g`a1koqWxXn$41kt+URa6L$6pf3wg{Z^Qy6`GvvaGYkC<7K&I{-8=X- z()MoQIJ9qsQHu5Agw{JE{M$tff)I;pA71={76SUV{B{=N*tZZ+Wt`GNjwT`z>*tZI zr3wYBp->pP3*Zt9+i=<pu9svj-^f*K7&=e{P|Fkxh@k?}^FqoZZ6}j97X^ zn?|gtOUND_mvXnTxerOG*OA?vsgY%WW8I=f&aK<7HU{^WltSu+h$r9o()d!VWj5Ep z($KCUH&IRUk{B)SCydnoeAmI&0}c^vX3NXAg0ky;a>vMWLAJldmq=(iYE7h;WOqd#FpR(`lMEaM3Fb)ieH-mB>L{Bf_?_<@^DTiENflph-1i?Hk5vOK4| z7-Pn9+Ur8%bgsh25YH--ZCPsuzeQFJcBX55!I92lc?m9Z@g0hzosLEeeQd06X?yS| zRS!GXP#QMndlZx|l@c(PkbNCtYOA`>uv6zP2Jj?f+Oo-YH6L0U=t9%Kdn%%AcBIdN42;^l7Q=(_(nemf*Ho z2f_PP{a%vK85x{@7SuhfEmilMv*GQ7vX%4S&%T`{ybZ>nQ>W|mhG7M3Qi)>J!7OB@ zQ>|UB*RVXiI;+KMp#m+r9S&*Q4xQnS5j8V$%|-gm*EtR6=F^S&ztAf>IK?EjtZ|Gs z6{#S(CART#m#ZxdJ$(G|p`eaTiR!Apg0+RilWF~Tu|;K6CO5BJPNTUFn+O|0WNsG( zn(_trXMKWaR!&t~gf8wObev?g`heu3Nx;jl86x}&y!I|Q#dOgmo9!BfDgq~=f`->c zmbur8a+)s%a96tVE;}!y6#H`3;D}NrO6X~y$1Ao_3>o^^w0`X_cpV%@YbYCZsPsY? z`{L{+z5A}Yla=9H39`K$Rd35pC!o3QhRo!JYdGl9AVY8B=_NV)Kq6iJy3pm5RorBa zi$&`Ox<>~Zw!5*g%tdDOXP}!QT5+uQ&|7R!UzfS$J^$$>p}fMo{^8Zw=sFJ= zJgFV%=AqOWC|@`f`o}GExtOxI%*97!cInAbWZ|L>eRD(bBs3*OGgv^CYFK|a28Iqn zs|fm+jG(JZExzoq^aKePndHG#8WP^fPrsL=B3ju*#@Da!s6|ft0w2~-z!27wFf0{} z5!rh_QgTs!%Pp!XgDCl6naeE3sylD))VK_t1Zc_Wjfa3o0ZI+Nrr^T0swzvcdZ}NR zd2t*&^IBOXtELgD=Lu%$hJb=i6?k2I?&I*q>Rbpog!!sTBe$QHd0~K~D6r^S8N;WY zWnL%DyhOicUN;&D`r!;b=G2dyd3{2U)26I;A9cT^ik6TQJv+0aq!Lu#I}t&mhDcnu z_3EFh`fQoYX_vW>Uu`U7$RYOd`2KhU?TYE#;QoA6(Ldf|kz|uE=otsGx`q4GM;E3B z*|Q|eoEA;~z|0Hi+w$9GUN`rbxd6Q?Kiv5V)~=i&^kmCiZtmxdK#$QWbH+rxk6QZ0 zF~roDAzpf-eV+bM@$MC(y{L9Wi7Sfd)r&Hapqoc?MxY{lIpfWyfjRUQO)$*=?vL=MI*+00r(Y zn_#w4=c)u2N>P*i-)!tsD`4D)BFE7xf{mv}AVI3jKeM4G-PitooxS?y5TQ?Yq$bgJ$h56wN(q-a-JPLb z&;$3|35#=ISU{C=N@2Zi=qj^Q_WC%Z(vRD0XNEZv4yhP7r+q7PkRsKnKZgtw#JMjl zpYQ7xfQooQfH=LlAUzND;KV67^$76v~{onc#E5$^%~jUGofir$^pvv!=1EyeV6R zr%tKC4SEs~%;Yr1)wmW;vA%v4y&Z}Nl>iiYhP?k`Ei=$GRJs4m#07d|ytsbYk;sC= z3R)L&3s+}Y+2yQOSw)De*EyehU#D>^ z;-ZFji*+n)QvWtI(bD;hsQ2>6Qit+&O+8Y=Kvbd?eNj4m{!C}W{AqTEL>7LP`Kjm8 zb6FO<`Dl0bGSfU3k|nK-urPxodFi2MwyJR7gN98jf?to=u1{vfOpeEL3*NyddjJKE zPW<}a5Cr}E8om*X9Jr+`v28R86P>swT~xh!JP_nnA;Zh%qBq7jS*7x!$i~ToUW>*k zz3_^2@#~kp{3O{rDWYkhJY!Wu71 z~v~c4s`H4bQny8>R<|gt6SZp?>+z6PIR%TatWC6rpb$rC+UkCa&0t zyBLx*Ix1Boy^HW163Uc^ACFh_85gS{Sp#1b5WZkgz>T);*3I@QJ?oJ@w7SXjS6E|6 zop~n4$fggUF{#&58Smx_zH`Px)Os!FJ7a7>H`e0`Z>o^o$wU4$K4LP?=EXe~O^RNb zI}hhHQ^_qY7!@wzD3I1Oo2<8yW|MMaaai+S+UJPj58h{v3{etHa zgw@{gM(+zoJnkU+Wqc$z1z)z>fXsBlZ0B2BbFjix^Ubu==af6x30t)n#nL&SczBuX zWk!@oV(mJAS(_JcGA8#4@K1ZyLz^ZB?J~*5DRUo1{_D_$3R;(bG;1t0PXkY!d?eBU zI^*sB$cs^U*ob&kY9d8^-_QI*N7EDXP`=7t&+y1cSfM#bQi@qWrI2p=nR1X{I8~)F zHXg(ePrMpmC~Z2_ zUj2E46JNd#PN0yiIR>KS+tQGY%nTOQb*0^!!=6IZqoCg6Su7vP{9N}r6A5cT38VYY z`_zzk@F=!b+D`?##^QTyFyHHl8r6j-2jtAw#4Fu-&rL1^XEuH{1B%agFr) z0~1i7Z_5t}=n1wXI6>&ib}r!BADjSsRereh6O1^VAoOH|6I}bLGtgsn%G5bvR}h9a zoxvmX?kI=3A7Vo)ZiN9ik3b1ZCoEjq6@3Co5ZBSv8K}r!>dbW{byoB8xTTZ84om0p z=JqGtIGd|QddcY|l?Gh)k?~TOeGYH3|9&F-4pdXjn<#wC9lkumXG??Q?dp+tLSk8C ztZzt#2_Ix_K-WA=oll=S107A_{yl30s;nbf8^nDxy?$O3buA@~C#ojI7$}`i*9O2U z+pR5!D9KhGO5Hua_s#$X?mb@ahr4D@FqU%MK?h4#O4%guQZn6l%~Q%jn94%eBR;7? z;ct_1({Mh$keB)E%*SqEqe43Dn1c@55r|3zSS%G_b%CrIIbVEO zYVI*zNnYwUAau$Bj&BfeSApYPna&I`t{fBNYEJAFFm`gqTG z!KLfPp7rURaKfq7xAvW;U*f4nK~as$S^NGwF7-QU`<|R?OC=Ti@y!SHzz$mT7aXIX zjmYShd{^d0YY@9vOCkTj_8sWk@JH(A^lra>2Z}q=z60&t=)T`#?$pB~UI>g<36~?ir8f~9+HM+7 zn8$CB|8UVd@|@wY@2_hyZymeE%-`}43~Ul6p%Q5-n$1 z$e-6@!d_>!lz^O6ls;=QPuOBge`_(X*eGc%l-jf=owb;MLyL)1!^O?r_-sa(%7UKf z^XJ7o8)bw7xoI8d3qE56@C4`nz!nqe+w$ABm^}L}CQxOZvc(j>elc^bXz3*^XEhYh z_|13bJZ8JKm=o3XHg8GPA9+kef_RR$m_SALT1=kPx0p|(1hdeVOZkLp?OTN;77gDb zix$g?c7>@u;Y)CI58&BvF@fTaw3tA<1|Cd43RK}0grZnU0>9rD5Qx?zE~K&?5I%@t z`*M?b;-w_<%l!iYLkF89KFtJ*_vdMiCa!SVR#yeLNaeXrr^xnOr0`PUGc$nWZl$Wb zaj>9^_3UaVp#*`$7fCRD%z;!Py_?~keBIXDGVpnCZFjZl5yGT=#5{!OAi#U5+h6HZ_8jLvD<>(Fxdz6d9%eOlT$lJ8N zeax20M~OKNO9yM;7xuXH&IJgdP_j)X)O~qAt5mc}Kd7LQf^q(f&pds_YwCy3-&r+P zlW%^4>)dS(uchc@|57VEL~ZMVC2&rJFMG1{IuVOi3^wXH^n6Yh;k+wq_sj=vTo^At zCiK39@cF9M?GYb(T7t-1pUBA8=ELV}&4^y)<(Yh)d^D#&NP&|&BtAj=u!VEAAVcN7 z5w0?4VTXVq-v;sw)YZLNMPFA&e~J%6JD-d&E-(jG_`JyJd}EA`Z+|Sk$~nQ_)l0oj zVn+`Ebh)NU`-${(a=@E2wM=0mXAaM|)L*lda4Hu8WWuyKNT4|T%;VewGt zA2<03zi4mrQ6;$$mCkI@APM}k$u)bcMP7&u(Yy8o8G&88h;3ra)Z*E=dZNfrMH&)i z7crZvI?sn#5snGj1udXdEaz`nqw?=&-|s1|%i^wC-nF%TP6M?lNsG?9((fDAnRMmq zi~57fM+lI*?!j^fxiTti$al+GeW8^Vdq~^oDg0lVxR^4q%>O!e^BkMqeYC`#^n`2* zm=$VTmEzhp?bC9>fn#)waYh1arp6R`Kabt#W9j-AQJ&KDoU!|a?3Vq;Zt_CYsNA?b z{FMJqcJH0KGx>HrMQ!u@;{&<3W<~PrniYiqtXVPDv@+JUIrQ;TZl8m>4YT|xyk@OZ z6xK6mGTStYJd2{uj>x=5t4l)Wq4KVFV3tMBcfWie#|%`KI? z>XbT25k+^2a^(2zuB+~tpdM<^3KW3U$uo_>c-{EEm)w@aKtr~qp~7fwzbX+9c9o`;ele^HT5|zWdwX7tibn+#1_Ren zO_=i~M*#!dpTYz5ZTaok&AZQTpvpKUyB}$=Wy>;MqLON61Pf4mB^IAVl)8iV$&O7U z;8iIemn|fS_b9u8itMqQ_XxWSP1^BaF$=HjmGQRU@=_gQl}5XkH`lR6`~`8$Q8N7S zF=G>8d9RIJr3}85`PR867sgqoI>j!f%4V=|i(I)A%!G0!qO9NMzK+{s{S* zb|+|o_Y*=N7Z?~n7&s@`-rr|15bu7s@W)jxLib=N49Ls8n^h~twdc(x#bn5G<`5>3 zxJ?7IDu10%d5`VWOjn_5A?y{MTpq`wxI7F|$+5}n$yamdvzgcUrl#cK|EV$icLq`s zHZR|J!M$N#bLP_%`c&?lPf5k>={gM^M=Q^K`Zsh7gQJ%k-@H&!Msbo2zFCmTrR+xIC@Wt`Hd16i_7_v$qL1M1V- zD$TlEQS*orV+Y7lY63Ha(p_+nAwhgceF{`$&!>E+_vz}pXDad&jeT)s$dT5JAXik~ z+`tvy^a3WrcyW6kRndJK0mU7mk>%qnM6@x}QG~PY+ZfTR&kTcxBIXIa(Xd3}d|0L& zL=QG11q%Eh*@zVAl;cB+0h<4{ZxJWkqDizD%Fk7-{RFkq>E4RK1XNOAEXdsOCJ>sd zS}gl@7U4U#MU*9qnA8;OmnYv1SS6HSXC+6K!QNmGXqk#n8Dm~#{E0)<$SU~pU4Fg6 zH_t3`LW{_Mvk1|=d)IT4#K+gqEb=#4#PkgnA>k9x1R8M;yly?@!E+VYa-kk+uR?im zXVAc6nE!zm0s6N5b{65^w+K*WoYEqU7hPa-*WMD#1j=`ElNYh=UK6E2>AXJoHb2Cy z8NdA#B#8g0MSzOzS%m-e7IB-jzyK|^idYf^zgJ{29$^zmJ@=F(EoXA;Tw_j+Z|S~8 zfZ~o=WQSxZvLg!Hi~Zt@{$LevS-<+H83CHqeOuJT9KLD20|#AjK!Lwz?-QU&+lMG{ zf~8(32t9#CPH@%ZvSo&JA|;6>_Svlh_H8+^O~M|r4T(qWHrT<^-d1Zl>wk3dz~45D$x1g@A|bf^qEX@;%_iw4C;4>!TlzEdsWyPUW9rP)b8Uy`&QifkO^wh+!Yh4A8 z-MZ3;{jxu46N}md>ZaR1Tg8Flp!_Fr2w{(j z;QzqZ73kaY+qJGj`>iWbWt_5g#TSdIj-?1wM#|a=ZFYKht?QY_O7cr$7}5JFVX0fx z_aH$+M_X5*B73c?(2=I?1g&fTZ79JHm(}UG9^$8mm2fsxWG}77NF^@kzs+Oy*5)8T zXkCEIQl5|ms6Z!`&~qH4MCA8*>$rr;m* z*`v{x?!ue5s6_Ku)o-V@N>6H&F_20|ZKnC}FXKxmQ!5;N;e}DWZa;t>WX+9w`wH_0 z^K~{^+>M3p34*8$%cjeASR#R}*hf;E=|zb@m8qL+JqFQdyG*4iV^c~iOtu~9z%ago0* zx@j~f0v~b_h2u(p20t19NX@oW+)!y*+1^Qvk$wqjXy)3%u|>8^)Fj$URvWRG-$kU+ z*t<+)_YF{^br4;soLxplxLrS9b#6zjfQr={&d|3haba_ZMTO~QIlChW$5ic!rwUQ7 zT~AL_c-~}J7*%LB8~^9l<)k6F$f$ACc_&`&439an1f&KFaRc>NOJX-_najhkU5K6| zyU6tV^Es9KFR!;faKSE~yi@!Dx8p|b$kM%A>cTEuc*Km6FrZab1v4$T{40*$qj_ni zHhcj>JGDX!HrkP~&E$3I5kZSPg3PF3UMd#)Jb5WgO4cJWp&~}{4_?!Nmk7I3iHrAo zMsVibFsj5WP^Ut@(8*+V8=z5EC!Qk=x%4PL9{mh`?Y&ULl&Z}YAa=k98 zU~*K`Ic~YO?p#jqG@TUF*-2=Iiha2?l3YdzsCxxGg_a)LVyzT(vCHn+!)ClIQl7ltR9H#j3IPQu z9|rkTAc#5KXE2GTZ~05wyLK)^Lp(s3*hXryepIl6&PNdKitU9P7r`r0w=kplbr~O$ z_N7%_sOPsDmYMNb9~C6Uz%`GV<5Rhowm58Uo7_bCwutXmYMmO#1!kV=^@RjGR^OrU z3dQPVqpKz&BiXrRt2ZR}HX8q72XXJtidr|wU{Oj4dU<4eJMm?(o$h?N$XbzY9JM*5 znR*^DM6pjG;Y0oeV&$RGKW-7TFqD=`kWk$Mi=h2S1+`jg5*)SkEJ+qo^z;O7Wae%%jq-bp|v3E?K>{ z37IhGO=z<|rZ1EEI2x^uyUPcQ_dZQ0qzg=Ml zyY}mVE_7_58_CU-A+5Eh#bmw=CxqWF=%4x|Q%6}Zw;Sh`ni7+2@$-NVnp`K?k?lsr zI|KR&fv)lm=vWYi^CVBn9H5U2^iOxQHi)+!1+Fy@8h1ED-cFr=hAD%SEG7Y~aD%fG zn(Uvd`uE*I{CDsEqAhc0_ZV1Won^5t`7Dj zWu&KrlPxq);^nn;GwZTi+ytP+Eol8LBdB<3!ox~(GilF`ThM}4)ZFms ze%fwK_u#|3e2=c`vO^lezm7L+6^#Yy7gsBtDtJDaF)B_;*m80JLS1zviCVZKS z#j=;(4?HYpMhsU0C1hYH0zT)5Rq)@*p#5K;rTHBVT43P+^`$_n-_(hbSart)O|BaK zxa!1b6{`#PkH>=9dG;b)Ffi3Hu_s4_&HUEJN*?I{(c*%o(u53u92B1_zF* z&fo`X!p;xzi4*+FXW(UBf+-UO<7!h)#Idg6Tx|v0=KTZJ3G~4Ic6Jllw;NDpoYHPZ zWI|J}5&dpDe%wMErI6j8+IACno})P;GP>TZ*2*b`1c@BA8&HuwyNMiu(+TW`j_jF0 zu99KGwVtEu@;rP)O){7a#iYqd>SOEquxr;}9oP*h@W?FyIqOi9-P;~Ug;QvYYbjFa zIvG&C6~q7w@48--d)G%oS@`^Vg}W!je~zpK3iRT-Ef)M&X-ew{Ex&V=z73q$RjFwi)j&_xQ3 zj4m8k7~*$rGLULDLSUeZd_ZYm_!`HQ$v8B?aPGh8&a^QSl{gc|zbOnxrvOB=LD$ml zV+!NE?Ir8m7vmb@6kgnNCn^qb4t<`L%4tsluD9?aJ9S9$4-^K_1NYkrLv&vlK$UU0 zC97@hklTSB<@zu(3r~78Aep3cHYE%{Ps322wUhJZ2ed;C3CDUlk1=s6r2f0Tg)T7EU0HR0t_)SRB^`!8Kp1AwNv1pwBGbH(9MW9vAw! zbc1Cu9qge56!>fQP&$EIP9TgET=lpZ35sK5!R9WeOOQmma3}J<{@AU>SW3%x)Z8lN zVeOF-Nxu#wqGuTSi!q}94I{`SoiF1pDninZi;=|1IzL5eb#kJ<0I8bFba^wzp1Hbc z{&g3Ft|0suYbj?K`8P4bH0oxk`zQsP_m~*ja<8EpKow5UfdAb0^nyi9K>dr5&q%0D z@)>ahap5qte;`JH9=P8QBVzj)0ji8sVx)M2=1LC1)npdWSLc)T(|78T@r0|QEvYw-~UJtiH7&hC&>{sYx7cY_ahEdmPs zHM12ClS?*JN$d3xd_zP^8{c}3x(8AUs}N>(+~Cx&bBoxS zTmE9VXnb=EqT_(0pVyo{-*MgIT3-N@MfIeK0c62WeLqEK0T5l8%tIDav5G`|rC9yCrMMb00S zILaS_%yRFWh$Lh!PCKAnCklj)+vvZ&=c|NCR~)q7N4wM8fLSgu!7b_+`<(ZM{6oX%ttlNWr($oLRuZi z5`voxv+X|65YRQxG}H$x8;Ee|r#*p&fI^#K|1)z4m#m=YBczjwo0{Pq)AOP`JQ-Qe z{+I|Ba4a$bm`{Yz_7;@B2KP|V2uOxTBv!=51#QJ&Ug=>iM7xpcssG%v7)$CAi_^R8 zIb?IQc4zUrwp%fik3@ZlT9J*dd&ppI2YE+ExL!5KT`=B$W6kxv*T3O*~rFJ}`V;P#T8*K0RqS{Fz6;SHtkeoW)XdVX;Ob&kOc33>41MxhPEh z9KMHoZK9Eky{7pdBGyr6cn;5|8?eC>O0^JkdFn}9pOu>TJX6T5{X$^T{p?VJSwuXa+Y*#;+H{2z$cP!u{ z(^tEh!B8e2S#0v4yiY~=GD*KwRU9Wuy>Y`k-6%nepmCItF~zt?gJiMBsEJKBjSJl5 z0dF&&M)(`Vw@}@)c?v^0j2Gbe=}~iQ(abI!9pd%yb6T`237>_NMAuqkA8uq#q-9kG zHxwgtimcnAK${GEfjgHVwquFV2}^g340$yWO?hNB`@ue@-{mV)#>2wRa4vNe#>aYd zWt+~p)qSX(^~I`ExT&%#2m^7bA<$`43w4x|_GR&eiX`nUlv^K-ErNBC$#xj%L8RfM z%dY-;wK3vo#s~wK=}j4~8{YxlkqWtBtw>1?&3&~^d+V`Yci;Ww%vf^;jeLKIr;^GX z5d|XXX4zresd(jG5(XWglc~=UazhKKUZm z9ZwR91v@iknB=~)?PpLp-K++>ITPG0RdYO3Lenoj+WFbD&y#*#Q0k$?)f#~)hcw?* zSfXi}DnN!os`P=B@WE;lt{q+X9RX}usITWjR;~%$O8H!ns<)}8c;WxA^)VLeh z7!*wfOR7TO=*=w29xN$+w7Xfz>|_XWgVscS7zYuf4te9qNkf4$rH>Da7Igc(j9z+i-x4fRXxgHE(Ulx|G>VRnAEg2H| z*ykUQHTZp(ly1DUWoKD_wyE$vW%=FCg}e!=bgWh~34yI`ATxdZO z!>Iq1I{%b&A%Pya-!7$++)t^1D&v$XmC!|GTOZtMA#dvj``S0OhD+rheSRP32IDN7 z?t}U|wID%~M^h@GB6}&73KmW)Uvy>Z72mVGPRk-U)yTi^r18t7z9`M z9;8%2fk#p*prZ~CnF1*NITRWeKvB6X!Tsh&XDxE`@EoZV{D+&^W#;TE@?x^Rj2U|F z`zwNh-hdw-yc3+qcY@H99o;9nKYO3QV6mAGuJ;@{judt8v`@GJJtWMQq-AQiz|sIGeXc^)zsEDKiR$^5W-*>T)GO+qi64e-9IKFAbO-KIi^LWmKlnt z+oiI)#1SzgU)9varP|NY<6&(%OOjm_njyXK%M$bEqcP;x2vOo@a-uuDX8M}dVt2!Q!UxO`3Jb{k zx^DcpUy5$5QdBdyj3KPNS;TlQuIK99Pva4;24fDkDB1Q_HJX$>1cBshhoFzb^FXo@ z`n(!Swpx0>&nzfSEfRb$Wv9Pzdm=8@+@TI>D`{!%3!Oo)n z`cnZCYpc1nvzq4GY8MVv0t9pd|JZ9+R4;I``(xan>Il7+qgZFw-kKld z=1o~`pX*Xb;l+US#u{Xb9rph7I>J;hmqfzL+ng_vj4C3--JcG^gbE@RWJ6uo6L*}J zUwqVZQHmm*``lILh3(jGB9nVZCC7b5e=G}yQ>RshRA){s=Jkx|MXLEx+1 z^$B{;MjN$A998Tpdn#A1&0~trR+EOw0`WRD)i@*963hWV91qzUA7@Vf#`mf#&l|!? zU}k9B7$>tSH-l&R8pPC~3m4VUmu^RRLkiKBz1}j%M#33Peh_&jjKy#B{*9#}w=%)2 zMyf8eR!nKoUSbzj@+4YT`M|qsh%Jh z81{yX%fzo65QJvzG%rwm?GT)l;=kUQ0W{mEd zENusmsu(E49UhL4Rt-rh`-oJtW>VwVX$Z*7$gL8Ht&0)M=U}}Qh%1_w?&L2-m@<`$bHxH@75uB>!~=I z0DXmP>@hVjkh1CN@n$2I5iA;da90$=Zvw_r=RuBC1Y5j^l;wxy<7@r_Znt!R1`&P% z#aIW^jx%*#d+$IT2SR@F3m6ewiHC##RL?)9LlEeJ`|UI@y{~zo$~dLwCFL7&V0OZ? zuY8WJRZRP#VZ_B1bjdd)v9iFa+It^4dw&XHjt zgl^An&)k|j_zFp>N`34tZ-&)@=79o#iRQWcsGBr~oK~+g(oa5AT;1ZCE7>XExPU;l zfin0=5pu;dJ9Q6lGFdDjdt=iXJ6&n>URGnHYyCHAN4Gu*m=B`KFO$v zt*R=oh1JaLO#=~Q=rT+6hVb|{VoD!V!L4e713%Xk=ahIAO=p89VJUJDwbLiY+eM#5 z?x)*mUl>6D(kX>e@0yNL)=SbN{aTHO*1tvLN*ASqkfncMJMH_nNSt;~NRZ4?VE`4` z6Nb#`g)uLO>i8uA7k_lKK{v0<-Q5Hj-+EF{_$j`{;(00sUMVesGYqPikj zS+`Z!P$RVkW(rU4K!t$++#lcCKX>Um^7g^jV?cqw-PU73OJNW9@}vf0Da~|;b{nX}|dbvoYLauh}>dkeRx_FZ5j zzH&n^?WDk)IAT109Y$o%F!C2;#NZo7HjPQ@%aaqE)c+?jf&q@Az&3X^t?BvI}DQ$d-5k@O*9=JBPcYSnQadh6j0N0eRsn2qf31Q zz+P$fJa~>}FQjN&MEuJz0`xDP5+m|hhV(NJX7GIpMZj!czD9WbVAu(LIY`lr)ReQP zj{q7HBzqJiKt=X2B71s_lH$W8 z0)Gic21N#u%2Kpv-%zhh5aeu(8okI?*_e9TCV^%sgM)5Ed9X_vP~dO3OW6r90<@yz z6m9`}?S4DA$PPpQXW0ss2X0f#ms&buFdLyePafqacvfq1G%tSY_tCr;L@pG+H%i?J zvA1lcg5bpi9vw`DXDMZgN_N!k_X4AHS`i-cN02Txu=UaJQfEb7x=7e-g@kY=eK%mH z6Q*^{oV3Qy-T`dT5t(Q2WNpW>cs?T+U$l5Jy!#(E`EcD-NYQ;Rrd(ruo!#d*ZRVxW zkVEE>HB3LMn1lG@^@AKTzlGJRrO(9+aY~Wyn%z5Aw``%2jL|@T(9bZfqF0K7jUQod z-(E4#RCuG^Ot8AvRU6tB@zCi`#{;K!_nJKQ_<<}YEq^pcakAu`dZ!4Gv*5W2iqAuQ z1vl1SXY?5t$z9Wc^Wdt#WDwgMwe9@Lfg)L@6fUY$*+;unrAXbw-wUpU;7L7hR$Mu5 z&BLUuw`Aydvoi0t*M14>e~1S)`I06>^4T?kRZr~k;O^enSTL2$3L4Q5dEOOVv8-CD z%GqwD(xl3ynV`&gaXSxXMwNe})e)EP+|bnZ$04`PUD&kR1*E1h1Z6*V4tQ+3A+icq zT5)oI=1g@)Q(MBiz@_sztaA=LuzN%)IUul_mhr{W!VwOEDTTFU>!vgAD}*=5go|uJsJGLS#yb`~u5Udyd3Grg&k~z(h2H z4txtDgOOaqlLri0SP~?6s00vY4~71Dqqq5cqqoK0kx}9EgZUMUO2*HNAIKyb8e2#X zR*;~{#K|%w8phR|u+Yu4r}~^N0Gk(s4+jQwFnSAie}TO)i$}BYOR2rSjc|-K7F3KmlVhzJ1GEd}=;!pN=rRUxEiGU0yZ6!Al3*M!d+R%zw7~TDt|RgFI`IFwj;W!UuGOIn{rP7#7=23$ zK4&mvt2j(`K>lwg1&~%^p%0@GWkxLrz!sjDpPSDqGrPL>?dJr6XgHI4z&kYBGhdthYsW_an zi%%#Do{5wL$An#LeQPtNhaj!na;ed$cW>{>(vK0 zSZ?vf^9&r%S$6l;uoW7_QBN|DkZ*?s$sJ8+fQsy;GjgX-XMEGMzsS!dbn`=gidW15DXn}}}rF{g9r9%viLwaD&| zX0)Vby0YJ63-oRI;gvqYnD7ZgPd0okx8GyCRVpCKe)l%+`s_Lbvg(8KPJtpCtF8gH zq#MuP3t41P{yLn=o#E^+#+mUqoGtTmJ^fUd-75OOiL-NkJma3OwFQ!Y3C?nr@dk|> zoLGD!Y7v@WjFkki&^P5T1~`&Mfjv=8%)^u)PIeok8HE?!oQV1~z9=V&4}tYsFwP;h}N?-Y;fn6BpmaexAU z32`tCyx&Jq;l2p9%Sn3+HnJ=gCYb)Ddl~0{if3{wjLsh(1GIfJ_W$yZ>vzo;DDz7ZtyEJ{-%0haJqRvr4YBh6X8hDg{rt_y+t95 zZdsS%aLW*>no*i%&6$jZ+x13G6iC-by>H2WVI*1!TQdLfYd`R_TV0n}lkd50?-YV5 zMfA$Kyps_~dQ~>rLsT2+YkK@|>%?YJ(BN z)6QeQrQC#LG8TmcAA6^`R`mzK^TGC)0vdDDIS%&@=*(K-3e;s3aIv_dnoiz)p&%V@*^s7+m zCUoC*>ISE;2`a!7!NaNUd`-HW0&#QD*Y)07)%i^Z`Yz#fF{rnn_@}Yl=6&C+#yD(b zpN;)>DPU~!%DThc6Q@vT@MZ7?nvZNR&6O)tFV<$X89-%{dfWAB1t?r^(aBJRM$3YF z0%n{79!2l@B_PPFG?m+3$b8FON2ij^)>EUaQl zVO{rD0~b_5ZiDxTpuD|)WrA$ioPiR86|2OPEtHC8 z3!As0yDYNYdF$z|z7{Ip@_x7GYY8$U@2+qwR(uICmX*J3a|iNmzgj_wo`r|56W*#o zak_?e%^=($l#h(Yd1_rh9_Db<<+Xbb6z-J$UVhzVcOqiO^dCdCE|(bekT|z(?Bw!~ zU@kv5znQo%IUF%Ksy_90<3Zh(*`$g^Q!wIEdmjWu|hw|)#D|g|}e&W3K$gll+lH40O zeviyAu}J2#{Zw0*+_ToOf!XfPS{9&eY6s>xt0DMq7>Z=mgy+K2~6R#;fd9*h&Vq9 zP3yl~zN6XWTWqZSfU0m1igysXZ~%cd7}{=97s3jrB$GOG7`EC2??I(}YSomF8v*7Q zULNA9n8)C~OEV?s4+fJi@)2jS;wMQjicc~ntw=?u*VZ__E6%osG)MNgSVI*5b^R6c z$L_Bnr*Gay`v|XKU|fZXvcLuIbe)}bY{l`FGza$Wu~%Zf|CHSPJA+9VGp3%DmB*CK zd`~#f29v%Ybl+=4hMjID$Oko!Tf*P|T#9aPsfx7mLYV!BR<2r!JQI-FxBiOxOKSZR z@}Hg@dnq7-bpV_?du|9)LYP}j%bf6?^m6%V_^f+XfsMD5yL}w~sj7e5jXf}6ud~u4 z3a2ZZ{igjDd=$KPJ-bPirQh!l;>Z}Mg}MC?W1O@PeP3%lhI}>2g!|s&DPf8~3v#jU zA~U6)UBJX$3@^>&_gB<3OK)lJ47-?uWe&uFH16ZSl6TC|92XAb9?rQbD8%?r_54!? zlYqW0Kjc~`7$G`A=*b3?6!x=3pjYLGJ3qlbBPR$w*_I><`+Y<}kI^anh=$S36J0(d zmT~%A-I-w$Tge}Zpb%2s-SspN*~GX&I|K<*INC=9RAjG@Na6H-L@yR0pCZ3WEAsX+ey<9mX!t)CNma~DMI9uc!y zEBIhIpF%vX(XZpd&Yf^iClsC$LD>;mce(#e3j>Pt;sSR+Jj+OP9dB&(NL$#PLJMmCQe4#Eq2-m9E(+5&b%TDV*`^FCH#3|HiMmS_6LrsV(9c-`aQWy{*HK zH1J0!F_m`(l*GV+caG&&E#K8@U`hNE@*xRdK6ETZ!5~{6G3cL~6fr`wdyNtBK}qaG9(rUcIa01++p;qb3xgW?$*{$e&*d}G6mvK6Z)t89qt zaoOMzl*~AV*6H}jYg%2SmMB)f7h2sFyJQT9AXq4bt840v4gcmLSwbJ^Q5*5tw5X5C z1{zrr?@rg}X{^;HYxbVrvthq+~>5ynrzNbr!BGl2p;)5Oza?PPJS_(D1yRcgao>g$BOMzr@Yk^MT1 zD4${EFUE+~H;hcg_@sR9-C(?XT#T%F%Sg=4Q%_2?oNpd}dG|8!xKW4O%UqCSMNb7D zdA7?LM*dBVh!^2!QG3N!;T;nrLN%*veoe##Ol|Jqw`a6A)1RzIPNb6&xZ*9xMJ`6e z{(%?)dfw?j-y*f6bX;^d_;qekIdjWj>=x^9ZW&{|s~G0Z)rEXq zw?HWMBwlYPne_JxrUvtRO&k;-s%|9O1$KpP$Exr|E61{&@q<7dL;-S9Mdgt z5#ROQM!6xE8pK)DinPwCz9w;Q{#iXa&lJsKR*Tm4KhP~e58Q9(7S(;X09D2*-SVUs zvp?c|(IU&;pdCl;%i`VDFTOma3X|zF1kXUjU!R2psUCF;P?0^isGi;}H*JTWmTn?< zdZjH4e?s%mu*e@8XWlz0ik7`gGw40y`2)8A1^#Ys5smDzzk!NDY7mkxS7d%Ea*gwL z_pa>o&g89+kGa;Ptq=C71PVO5N9F&=-dzS%wQUce-*ksG2olnbC?%~R(t>nIhjgcu zf{284cY}nqbR*p$DBZ0fNb}GmJdXFgw}E@~uU^iV^JUEC-mJCf`i&W5{!VWB0mNa7 zpS}U9>qjpafypNx<*6dE|0x=J8~INCZPwSZ^?6D^k2p#f#QB4XWB!#mL*1dsh=c?0-xgV1d6HaZ2%#3s?3o#nE1A zcWD_wLG=L)q?%ad&9Nhk2TzPB#2weI0~Yvw-MT-NI6pu`nQs*))}^{P2d>zp&O}2z z)k~&w81yE{&bfP=j^+KG;h(1=e@DKVVDkPC9$%?mvvq+?UW^Fs+fvrkV2b3(z>$5U{{sO+$=zQ;l|$mpQn}wG}ne=L90xJ`|21H-injy{2j8 zUBN#taR)5$`x18-8u|fnN~JDm6t8}->CN(iwn=S&2pQ6%RJ}->W>+6J*jPHnTrmo% z3*CxGfZ0v&0#5%Xoa9(Ox7u8brg(8mIHB%4&j?zHvtck8Msfip9>WZZ47xe@Lzmy> z*b(0|CD z`AXq)f4}(lqo-#-QZ@M?_n6DJzz3~vpc7$`qw1u%&uDGn)@a{{!&@@AtH?o_HbGb5G ztR^L@{#Fj8h_U->ySI{8!kEZ{pNs712)wx8!|w7=^|U=$5XOgOreahaN}0co{|0=xL>nc9YFue= zI?RRZDe|aKsg&{quVr?BLZWhYlS@XLFP7$G^|(;hL}Q>j3_zoc?)1Dwf)!K_Vi4#q z6rW2T+jFO*#vWYSsu`q!G<@Nrnou%t>nayLku#0$wO5^6%^Zm0V7q}wI}qP3rMk7s zLXCN|`)PPNUA_(K@@rX@d{jGyF>*j$N6Q|LAGjEVdF)NLIrZ!a)fQ{vC!cR?_}N_E zuc>$F9=R28Ii=nm4W=%c$(}BTI%=}^E+O4e_1wsP|9y3{7wC2}d0S!2FLnteI4b!r zp{U)#eWhPMAO59YsC`@yAiM!O-GrxT_Nl&#Z4#%tu*tQnF?Hg)8wfm`T-d~4>xJS> z7d{=C0$Wa$TM>!3E!YQi8SUn=`m`pcf6yAmz_^c%4Dz>np`{9>PBL5}m%WFw1Mkwk z4vL<9>oTdEOa{)ae2YYYBjq;W(w$dWswaL;z|xBop}$?x;j58{MTbLd3f&oPr;SsQ zqnQGd`m-?11FNu13Jms`QV(8*B6@a!n8w#PX*+rfle%k;`szZWzh&y?iVm>8&%(1^ z$xoIH@z=YF(?Qss?R^L9BR?t<5bw!Y6iIFZ5yj&{IhhQ?_Lc8J2ew zZ@i7_GKt)fvmL9X1|=v-S$BV4qNZ|@sQtl-n)TO2tw*Th#V-KS9?Pkn)qXe>};{9HU8f*xI_0lwPk;=D32<&k8*1a!h4#Iys zb5PY(PxIvIg$m7cP&QC&Iew*|UyDGyq)((71d^pvG+7cEm8#g>yI6bLL*SN7u$x#VBa$bg6AV!hf@3-dDn+q^bpU|`&l>=u4i$?uV>?ZuVkp`%G_+_?p;DT z@&0rLQ@Gn`Ar@|iR*XyLwZukPJ!WJa_}P)U1pj3}+OQuOmg@J38n7mZiJI#96ScyF zja-9(cq#*Vih$J@5WC=|ZjUis@2KU4yY7fGY~>s$YQO@2wL~q_q)>9Shk!*%bZ@J& zFLP?TN|7YbCprvOIj4i+dgaE6o8tS7;`M)^a>+=`*p z-A1|103cI+05#3j`A@Hsf2W!}0n9s?UMoAQyWd`)7uDoXTTR~Ps}4Ko8ecZiqXhO| z=+N0k4e-0`aE+qaM~`VgZ)6G;V!pEjpU~Dna$km*NRg$oYma~f{5RAgu)gJQJ^2|* z#GfJbY}Mq|j&um@W}H)p6#|Qp>h?Ne{_w&rhLf40NDN|6t#5cW_=uQ{+@#D|2g6eP zUWdS%9O_W*{5nh&sz`Ly2%5Y{*1}`ug)^}ViS_o;{XR3D^1E#6HptM&Is_K@tLg9z zBmOtx^0|;4#Cn8@-0!!nd5vva<8Mz<9Auls#f4UNgf||SFas9&eF?KOIIcOaJAS-& z^OMeGKv-`d5e;6csVlcqTFmF9%jvPz^d~V3%$!FUsUBfwuqK=EV$qxOIp|G+abPgI zgA$+_$U{(Am1P`;`tvZNc4~|;3f;sC0lVFXxqcb2lk{fRRGuOi@0ovet8~(2b!gw{ zkHZL&UZ*Ik;H93w789xgE--RhjM#p~Nc+=n9Zw~@#kUt2`4cc=PbrRrK&~aaw^Jg$ zz!Cbu6zpc2Qe!v|t)%5=c6)W63FuqKBXQ9nOM>Rb-G>Q(7AFw1C1C?)q_zs&M} zRt9*+ zY}>_Xa&l^dbU^)%fH!>D5JmuYrfkN*b10uTQNPA4CX zGYI1hgD7XX>KQ^$OB}@2X5PCho|jE5Gm$yB7iHl&axlw2GP!U`%=ytdiEjNo;%Jm69& zZ%mv?$oyzrYpAZBK*W6Z8W*YdN-|w*$}-UgSpfOn@^>4@HPnCweqTfF48r(7C(ao@ z>uEK_aM{9YPzCc&q-&k3H1;JqJ^A~UK9~yvVRSTQEblWzex8OjPpzT&^8R|10cjkB zHaM9+V+4v2f2Ml5oqe>a?3!^p#60jHry&a8JoKm6;Ms*QG;~G{J^HGlH>w|Eu`HaQ zbX{oZPtZ`H4N1Vm$U){ocmm3z3hfTtsP$^&v3cD*J#p&&brXxt<%EE@gd9m3dHE~EhTn9 zd|Om1pTfX)NK3caR7^)LjHSuW&x4cJso_KJ6X3)Z0ZG*mN_|__ zu|Ps)t3S2sp)+22RRjS&>U|Cg(h;=Z5Kh4QmR}A|+DC8#b~DZir`WJd1DElFW)>M= zNnDZ4)~W;Z$;=02RAv`QVOJpDtpmf-{vJ-injFGO`}}bFI2e^t)-sVq;1a`sz&XM= zh;Gf{0KH{4_ZT$pfQ-KD7*4oWPar;NHdH)L~(|-+xQj-B$~7LN8oJ?LO}sE-i-y#!Xvp zuD&qAkE3?iV$M_(`wfi+tZ(_{jHPpAEMPa|oW{bD1jO;e5yF%6K@D&7dk$PNkLaic z9T7#_uAL#N$ZH0}()r$4z?vKyOXvK?>fae{Gg{wFUZB3>I+7k|9jaFOngmhr?l_ta z6K`w#-tjaQu)xDi;}cJMC`i759ViC#@4A5T1nCs6)6yDN-HE3|jug z^;JsPC%#sc?@6zA&g4rTPJS(Z!J08Hc=_zH+RJ9bwFKiZ)&A zV#w=H;8=%(m+VYN_L{Pptho>~Io5p_-cTpaNOMI}d(CjsfdiemRcm9&rrV z%{V8=29<-f&E1mn4MM7wTOa4&r&4wHn$&_-O1l;L&Intp1`JF0dyWBXa>y~=^K+~d z!u4tJwxgffaCyatMlLRGLY<6mkUCj%evORhJB}d790L~kzrit2_S-T-8@XWjsKOi< zuYk_X8l+yC%>J<7m-luW-O5tP=|}?ofG5!c(11Q@ZsP$@B| z809V`a7GC@f0aP_AZEOUPl{LFg#`Wt3A`J$q^Or8O(?PdNMiOd(&^rms6M04<>A^< zcz=;b_cN5t2Ue`?pMKImq!J^rzU7ybz{4X60J|CIlmPu}1df(DeyvaFD9_;_V$g;6;)n*rdicErfHgUkz{B%P;G-?kVgSMoY}FCatKMlN6-twhHU6l;kgnl6UZ6s}V}Lq;@QU zvT$;%uGQgc1Q)E!;N0Zy#P7oi%3(3M9b#d1?iV6>|2zpiJhcR*6R3o=(z=jK=tyFs zTg9~8huNIB+~O2i%$8L*%q_eBsoVH>s_xyedn~TvM0lC-LIP)$fXi12lv)#NagpgK z?p;XWPmlnTs0Sl0iLgUjPS`t|cM3X0NmFlY1^VP6{klvx;zF2z!!ZG1eakN=0lgy$ z0J|CIlt72)!sQTj#jC!;k49{I-HXNS8toOYHfHgUkfZq8f zFg}xQ#qN0BlWdIfNzX8ij7~F)JEfnwn{oP`=S#I5`Nt9f7Wls*fo<8Tmuc^}C!1di znj{e8WSU%Z>9u9myOZv1<57flmF-vp0-d=NCfL-EF$$j2nhDGl32>!*UR-g#rDu~yTh`DdSe zOualA4I_Fr{~qExIiXobG@9+*;xz9pnN2)ZR@-Rvb~UQ$Dz-BGwLbQ{ZC?(&OZo^y zN&42YQgy1y5QlIvKP#2K#H_+SBzRCLItzj=7n?A@{)vG_i+sH}wd~9)zKb=5Zmw&* zy>(#m+xoIB5#I?4g@DyD+-ZdSo@jdIl?r8YHXiW8%$uKEbYxeHE#c^d2uYE-TM@?< zqSt(40w@U_J<%qOi^ z>`AjD_A-Z^1rAyrF?rIV&>#TU-b<*rXK=l#Q+dZHC5T-!I};DekvfLR9Iz+|b*bJ@ z$(T$>XN^0BHN7n@_nGO!y-=oW`iFPIkY{w-5Q*%tF|z8l-n957v8xzSW3;~il9O8L=!&}APF9+V0fO)m_wi4BpOB1vN^;N(TDWre&CLbSdwPp@m9_rG? zMgs*LhKcgR3td`@e7y7)+if1Nkz+u8zT3q{F&x5nhKlGF1ljio-aVV=Z#!_mrXu7N zV$E3gP?ytC2yd!yic;H>;HOO`= zsREDT04+36|Bgyj+kiRDV3-Uc2LU_S_J&~2OZoxgT^^;gQdsXW3!@o94C|;{w&3BD zjSH~aHA1yp&4+)G3uQd)j6$RysRGS}yybk-ym%@3Ey$#;fBH%PkZD$6eakPGQW+ejRKRY=Ia8|7yhho& z^O9K$-5j=k5w@^wwt6mx~)Ojw+aFs^ghrFQ2DHX85-#De(r1IVyTGFreTuWw4jUZX0wqhAU zh$^~fCq3*C2I^~l!as-mFY)|Bpo|3PuVpRF@!#{eLI>gT+)B4BDSQc2y~KUI{vFsg z|BLnS&O(&EAvCB3y(H+Rw{q84vamA5ZNwyAX_{C(|I~Ltdg9~$n3U?5ziI|3h)K*O zC;_m$b@ecZ4dd*0!_G`aX+?b_g3SB>RU*o|NK$XJW&2G|%x-t~sO8r=`zykj_ z^jc6LOP(DRj#|WXk466;x+rx1#Dcm$VCpi&*eGuQE=JmSYjor41ZoLNBR@$N6^<$4h*DOJeHn)r1a^=1y)tox?H1Ym6z7a<;e$tM zD#2woZ-q3h;vAENkz;O~Cz=}js?|kvzbDW}8+_)qT-C2|Q~gDcWnNG$+*<>gQDp)p zLg*%p@TO~wS??2-JXBgHmA#`i?yN%g0T|?$KR=Xt%9g*f=WF1w)^~W87!=;qR*EnO zY)3diYvogCpDzFTnWmBj?UL)@Om`k;MaesJW)a%GQ|cwei#KLsz~_~EQp*C(`*{iK zIu+6<+Xyzf6y+TQ?U zFOoF2yo(FC;@@t259)Sx?=7b6=LQu{&p^RHhi!mzCHojI4S)^>Qh`G?NqcjP+Q_OT zuxb-^=C_E0RH;PWq=t}rD0Fqpmly)|evDEl`$GL*!=tjY2!Q4uwA1*KMbc$11m3G} zjbI-43kI0AzuI(ETiC2|Oz<7@gm51jTk>8T*Jie|@b__~9ly%PHhh0h=*t8N&NgAm z8+^KEsyZEo?w+Yz+Rt1kSqopfsF9`B#8~t(VZ(!bnIO@td|R-M)MKa{OhnEB2Vj>|A)&ef>MUhr;`8HI#XyQ80ebvDJlD4!0n#rhr>q+LhkMo4)Q%i@)N z9Tl8ZGsdCj_w*o7T3ZsWL`J=jCrBXG*0ak|8l6XIL|zCB*5ptI0B95gMLG#aKAF4- zTg3l)iJIZ56Sdpi2>mzb1Ho+W4l>lmmGhV{O&|=2alYYUFossDZI=8e)%`oG&)H+O z0K1~@oADPDBxjr;asQgAWl}3brAb(S+Wy~6)Gn2ykxnmWKOg%;5;eJ74_vcKp2Q`O zWv8(PKYyHaga1W?zbo_YK2gR`G|R=OPt>l_*<9k$?Fe8?RBJM{p%CCQ;uW!;F%!I( zC)M=`vWM}Xe$qc=f&^IK@;45i;UM=jgr4opccY^e6WGl-XNp-OLp+$=Csd<9JiyJw zOxNOVHJcVizpNZIS`L*W{ zSwQ4-*TM2Fl3DW|0z!{dOkjck8!2XmEWTB^M_cdYW1&DC=XvmMFrWTHQLC!NQOcBX z0wv4i2@+s||HTB!@04Qxn9q9J5r1I);NQBRGYsROA@pn`{zgY5{&T}1{tA6Fexa(V zbgf345loug9kyJEW7lX%_qDgo1%DnSjZO{8^4C0JL8>Gljf{sl#!QXx##7=JUMXyt zH*1z7l;Wdp`=__gzXM4wFhv%4*1GQ93rL<3lFz-jWXgvS}$8mhtzH1lHsblE&wUWHau= zS_w{qW!KpJM#5UQsnnYL;xte%ul46l-z&bUW_LXJ4lM9+@crqD5vTw2@{wow0Kito zocLO3(>m3Ws{Z-epK?{$2kqgZ#cVDMKc|Rx!*$ao42ak{5el>@`R2`!*Jzf6yK^JKF6VL#j!7Lu% z=k_joIXxfI1`@wT6%N%K47Eu3)3ptG!nM`k5DmckmR}AHCP!!hb~Db2hQ|eNcj-U7 z*5x*)&14i!J$%Q34pk&HPs*b2uOLeD)(i~G(( z5(xd`K#7~dy8@l}p^=c$O}LmDj?n-t@B$6rp#f;=#CMQ?Eh3F`L9=hE)WVNJWi-$$dBm8zKcsQ)+%V?_COZxH0f`ea*08Wbk4pUvHtUYC``trnW)Qdb+#S z5VSDDuPwHkuhZHhN|26iA&OO1@w04#f@mW5K6~rEN*w1%XX6#b-+n(!`x;VE3NNejI6SO&|Bd~CURAu;j3rmvj0a>g5A=$I%7(yge%P8ES?O`L<=H}_ zloki48*NT{;536=yD9h70+8OA@9H6%=;va)^Q^)sLPZf2%nrQn#pFdPQ|hl<+8-); zC*L(XpYy{x%={7!ky& zz4!`$Mh45Wl!T^^00kRMVa{mbI=#`uI|HwqZUfLHQm{-5f{zLFL=yEQEaJx;%CAPcb1%+s(gEtyRind3b9x;reO>e<%MY*fTC#&kdb(|l>$Jy;k$_>T{`0h z#l$*6)RDOh-QLrWB?{W2?N6fPxaaF$*vIrww_5kfTS{>N$p7amXnrZDg6v!J;Rl{q z5os;8JQDjkF-(^>UoXGKv0Cx^yb2HkfT$=%&AE{w0|4J{wC3S8CB9q}K}%Pkj)6{9 zUHyzl=70(T(0=v#V5Qy3yU9b%*7V=LHRyMLNn;}`&6C~m{ne)sr2N5%;-2Z!$@F0@ ztVK<2dCW#a7)I~d$6hF0{|!YBtZ(_{L~eStuo2i#1Tt9PVSy~j22^oCtjdu2=$Ys5wW(W1;f+R(8D8_c(m{WUC*U&y+xLh&{{RF z<`WM9a0dXw4LJO79e`zeluH16jLw-$EXDMv??66(H+^%$l#;xPIUI>_GioocGpsiH z(RHE#RWK~m?{f)YO%8Jj)AQ#NAc*P6a1jibW$X=&)rH4(UR$T5%g0Rebx@@`_G7eH z9On|i0)OLN;tXSzXBdEN0%QeC5w7#yX2i?l@V=GBiaJgBh~y^Kxa8-qn`K}TPLKXd zC9;dH&`f%NjdTbb1m!$Gdv zd$O3%19t3V)IyzMpBCcXd(d(s6qqsP{`%K}UGXsh$OvH*d*rZq>mM?Y?tO6ZR&|EkGH6zSrx9Y zV!>8*MC)XF{GNEBm-wX5m*URY3*oj_>eGP=0wDAnlxfrt-<;gH0ARBK?n&6cK4AkL zf$Ur>LDeSRf_=68ZetxQ%3P!%{!XZ|Awh7zdBpe7#Bf6X+ED-i;2;11h!X((#kaQ- z_~Bbg?900t_OExbx~Y-jiJQTf-Cj-5wy0o_t5(~C&A~Fckl^i(ZW=N3AObdfvZ@lx z^v{FlaO*X`ibXkg6evd6mdbQ)!ZHV%0wz$2JNJkS6|CN|e0veA0kpq+qZ{ZN{_Tx! zdd8YHLiFT+yVP<0*)5@QmnXS!A#NeSf(0f3lJKe{G}Vj|cK!Ukc0XD>UPH zJ$G+iYT8skEi}C~@;%q&BqyT#reKRbM&6r);I1@)ce}_|5BQ zF~96`Miu43N7qpoY>wtjfbkX7+ukz??H=y~T1VxNSvTPlix&wVGMfeTGCJ$Y20QEi zMtqg}tCI!R51f3zRTU))8`(=#PUL|x!Win>#qK-?1mRddtM&G#U!nn3!v>pc`L>N4 zSg4CEE&%}G1o+lrAM)+*F1K`MKgPC4exxU+rC+-LT&? z*LN@D?S%VZNo1^t#FnVZpdb#xx{6k^HtJz%9JTbCYg}l+Qg^hz6CR7h+TMB-j3qMj+Ia_>nd(I0!Jau}kzs|t20HwS;?}HJH`HD{pAr>HSJ^RUe zcoj?c=(1;hzhP+}wLI&~6dP>z2dL!5W`kcv# z{X3#PYrHRM7x}htBQhAuXHcnVE8=^9eo%j~&wJ-6y?6}Z<^}8@x!faBYdukf#F{>A zgNsm)+hYN%n3c3H0lq1Bx^j%h^3Bh4x!Hxw|3Akpe}9*Ie|35EXi#JDn~#F%U*DtZ zfZ;FV|Nk4TeZqAus0us1d@kjqz8vgz^bCSWCH$_a7vLkCm zv5rjY>rGb!0CPEQ^Q7~dd+h2QQ<8tyL7koBCst>RiU8247u?hKvMKL>BbOhU57=Y; z%bCyo2xh=;#yMg3IkhF(hESvJVU39*qvkS3RO(KrOHbJVyYY-+OLSEL7?%0>Fay@) z5N77*hZ)?ZL6xRTW4FzTG=F5GC4YN(Ib&Hjg=h4PAoqnnI-niH3|Qa=%)W!!NKdVk z(D1VY98!E;(MFxmhBMNBi+U}w%P0e{A2vqA9#_Ez7Wi|jV4neIXOJN(CC6Bq zH1a~w-m#9Qv@V9zOuWTx8OA>?BQhQY8bzULUQCWjoeI6ud(b0Vk~-qGOc zP|S7=)C#ZKNiLC;0&Q%8em-%@ZiJNPm}9^KFF5uc$C{hiU~$L9wdc{i7pC0h(un=S zyW(c3cGFw?+u+__`F~5c0So*&#an+2$9{m^LRoukomKlDN5nMDdI_Mj#5L?zGm+Wt zuoIAjDGWQS{XDr@T*&PYmfMT3atp^z)0#Ly3Kc%B+)UbHb5K-zfn0g+^>Q#$mMoX2=>P)?7u zx;N+iVlVYccg*Asv3CB3as&1l|8jD(Jdzu*n{iILg<+3miAiPFU@cw~uuOW@A-GdU zQYAMRmeb%dMVm2_42EU-z1)B`Ih32_`Q^4j1@dX8W2DRoI>sqhey+Vo%wGYTaC*); z&Z0hr0!sK;ZomRBs9s=pbwFwR&NGp;ygE7D{i&88R(gZ%8;;S;fjJ}3r6KBXyTf$ru~$fRwM{%+m(Hm z-fHqo4c2U5Zm+2=+J$IvxEA|nSX#rPJ^;+HWUc0b-k1dA?!;(69+kqt8*g=)a5nit zLGUc~Pqs@d8arJ+vpPapoA9Qr+En?Oak+*oh>bB6#mH-q$!B?J*Bvof3Nj^WjGm!9 zth}1I4VUEi1Wsu0KsQ|v@JI!-P20);ig8Uyf;pl4Ze$4ELs@5W*D7k9tHeB6t0T3; zQ=;#6pMp5!H-f+CrzIuks>OwuQF<;V5vS$yxl68(BU-?jV<4m8c1=ylOLG88+OoT1 zT>RkDW`&r0xo*H(w%V@Z?!Ir93n@v@XJ4{$( z8Nzeh;){H}kqTU8qe1Cx4v zjmFj1fgN*)s(d`6thy1!z3;Vg2jRzB3pMqNiCIzi&aN;|u!+AOQdi@S8(18sGov(}6=bWFzv7odW7`1rs`*NpTNs~xq%t-E_}}*2 z%_Z>R0AMPP_RS+Xd_%;BYg$v8-#4-qA`OS$?VC$hz zgnAhv!i@4UxfVI=1hz^iFVnTD07#dwzPznu{ehsWiTiLbT;;cAWIyVEILdfrc8C8? z9C>jpZTExX0rYPWohjjl)&NJHNCx*S zPQ7N+RNMQhJawf7+ER&l+T|7xO6x#;Ry8Se$PYV_VUWWOt%YJ?FYplFVDdbOi*9iG za2-r=u6uxW@*%!=B*LaQ7s01pe&Q=P91S46B3|8QVc7sc(s`giHu&&JW5GB4{vhWYb_ffXm00 zje^Y93rTy2pd;D~)>zvx3{XAXms4r+sv5hsgH)TihQzbnKl;t!LnAk@{RncUI;Q4_ zCOVMdZ|oYVlSee9IL7ViKe~i+Pol9Tr#Pdlx_aila(0$=ga)62%|~{>Us8#}hm!V$ zf*ivqGg(=@@d{7H^kWqG`-!vO3b?!Zy+iYHYtS)JP(xec5cgS9uEwzu%*xuSGgyq) zOKr_L-y+`}1jqMZyc+c49^>5%;*WX3Vtp9VJt|0bEBXT)jY6+zT(ZeP9Ytqm4Ig<( zzI8)>Y79ams}hQdHeZJKj9OYrxlLr>IZ^7`2UQVq%{{5WI6ix1$cP8R5nMq@0&JHG zH@x4{xoIEsa8zN^oZ(r1$_{g)U49~yw(4E`za9f zcsHwIQ?GM=dnkXkb9{8Cb9uv)vz+X{eLi68wp?sLfcx zy`tyE_OV0P5cl-MP3`R^_Yg3qB@Q=!4__`!=SdOgl70S4Z?dp3Yr8`2?cjetJ!*g- zQXU#ukM+0VrZbf4J45K%s`^BSexM9klf!{BtMd<(X^f7`45LX& z3qZjn;FgR&@46(N#8E>jIOzSMo~WaT;&?a*Sm6K0K-n2~4vz+m>IGcppzHqd(xaLH zl3Y3bo)Yvu7C%!VK>-F2f?=nl9|^F2=5IfeGt^W*L+IJcFIye)kEB)zKe49>{UbsX znmnVNbwhm1J$l;rA_gt@o_NBS7W}-~Mym_{{lT-10blv&8_=1mQC$j8aKXPXl@$N` zYA|26(_X@0B_*pFevjx*zw7I9} zU%}$tOnvA~Flyh87KqKL&+Mkro}3s{Gwk=Sqs>^RFaB`;9Z>|>S2`y}a9>NPJq+uX z?~*ooZfdNkX33Gy18%`=D%Bp68SE@_0ET7#Jw<>uIi!g7`6;5(vW{P~uj@17U8Czu)Az!FG^BjxS%BDRT2HURHDvLPGfSPI?YI1 zM32=1?C0^zsl~bq`mZgDL+zmJ5Vn^%GhRb1Xmc~gz>Yk58{Nwdb#>vE%kn&8+0hY( z$(+8gGXf-U2jAe1Duao`^HwB%!&Y4kV!SKv!3zz+d{^hg8p8$yrcfoLwK2xd0#w~o zMbT{(SkL(OG82PJwfQk^PoGzlo_Lb>HBy~}W%BKbwL4mM6CTq3CB&>@uwO>LogKNV z+Jff8b3yE`Vt|u0(`;`O38p_X0Bb@tcSdY=}}2}gw3C+sX&JzT@) z#P)g!eF34N*{0uAuWLfL#yox*U~^M1er!-y525h+5_s4e?s93QxmpjcJ|*kZ)w{ct z{&g-bh*~V6fp=G)RDPgL4H?N+Kb$^W6#mppG`D(3pwwDdTr*xP39Aixf|TX){*zD< zjPU$``t@;*J7%`>JJFE0q=`Ww)0_3~R0YE8p_lSJ-Ami@682YC^5CP{gH_0g0m0RO zx?AkO-Z7*_q=@huSx}?+%gA>WKEw0*;f9!HgHKyy_Y{L$uYxf7yx-X^wwgA}81J9} z`*MI`TRM`91e~!GZ}hMSzq;`&%))`LB7-&9`}%#<1ta-)4$nFK+vJ&|{Z6S4HtMxT zku6Bk`eHi5y`{-M$Z?{90z0xjww23=lV@QGST-kq+rYSfBJ}s0JnKE2Jj;#h(o5aH z#lYp3NTCE>n=)uK8-E9MX2pCcg*o{8m@AukC>zI8O(A?T@@0IV0&Os1U%;TYNKlJh zwOJhUGUAA#J4A6hAv68GTD}5>=5oBI_Gb|Z`irySb78_|ID5zA-tBdztni70^+D2s z;JLC94lg2>3_5+ic;UBkXxp-`+Wx%6$NJQX4@Wgaux`ojqb-)ti0K;QqM3EZS<7m3fmm-wJs(@DJac`Iu3ha^4?E|R`) zq*E-hcsTo#9>YN$NXw>5s(ex#8WiPwM>}MA`o!l&ZIcEUJPCP>5I(<2XOceD2)ryC z{BnABi`eHX6p!M6>L`B5xHqsK>uj)WwSYd%62n*1~%Gzze9BJT67MK zaQw^_U-sDR#lBYuHei7lDVxFDV4f2IixJxGpDMH1hFg0U@>xeyx=v1d zRdjg@b*gwos%D2k;6!K#ECZ+=ZtN}7{)}oqv76i`LxpXGpc&(9mxH+Nh4meeZvwDu zUL3aA11;qNf8tO3FD~A6{#N)0*xfq(F*=>BkamWTaN6@8fpzeI>sQY(i*Sa}vrRbI z98Ea9MSBmC*f{@Y+$a6+P!E@3@uDmT``ye{CNwd(*VmwRf1bN-F5LYGPdEgBb+?D% z3P}j7+DgiWyZ=3RqYsiiGIHiuPW(gMji>4F?r7niU^+(1Y#{F%&dM1eF;MaRC0h3i zB4!N&_37Qs2V4IE$^}#kXO7q?Bz6pbxR1H~uKyLf_jC=sUC8z3Kiu6%dIk2C&Z*bI z!Is=Vabl_Dph|~wdif1bzycrhKsh}=pWV)|NeCpt151-XI!-{y z$01^B78yEd>)s4_Hy^2p(}J)p`>JrDkWtj>jfGkR@fxoSJ<7Ze9ILBx(p89unniAr z4x`ahzZRZ}NAaf_3)ojWr?FJr!X}}gPE-NB)KYbE^Te!uqzCIhV5HwERZ@D+ie(Ol zW%s?YfHgTZmfiV{)wIm4<2aDRGWPJAT_)!8+iFJyaN8x*%beY$D%d?fhR4PN7I41S5&3=j1`@QU?5a$B48){TD%2<=fRjGjm{ufoL&tR-G40)ZF zW59m*zZ}QxjyOicB!Rg3Hs7V0hdSOfM^ji-i!IZ==@RBVyt=oN5E=N-NA?FwMhj7f}^6kPxetttmn8R(z_$dr*k)w;@ z3peDK^=P;63OL+2J;$p3jO22ghR8iQWKd_>Zt<@OeL`jMco#w1-OruGkUIC%I0o!1 zos(mP*HL7tp%P-KP0TV_n{QlG9^`#>P?Q~F6D0p|MsfxX49os|jsa_O$T9o#bBrrc zzC(GBr67r`DPWATz+B4fq3Khf%)7C0hI&S=Lu|(!0~UC}vGa561^cJHt(@?vARkF@ zq$C5bV)ATr@>DiF*O2RYZ_#da9@nA<7WiM(qW&=)`vGzT)iQzNjU zdhRmHj96;C*g7$+32bHNw__fGUGeA~mJ?SFSohNyq`a?b2WRMI{)*R32-` zFpuJ-id`3&GwUd#FWYS#fZ4Cxa|oiTRwynX?=BDmgAlCV5f}<3e`(wCMA;7oQai>m zEHdR`hl~;evELi@R*dU$4u6_ykA?<6K{a@;#s1}e8#xA z9IX5uOwDYk3TyDL(B1?`qFb1wq_|g56#Z6x?c=mM*O`dYC=|zw9A4YLeE|}zJUZMC zF~kaC&`8rMQO^^5i#a@Ns6&O(cn*0b4i{ zDpe3{0?@y$@$Ocmf_X-AnG|Ni;)-itB2!`>zfeA2qp9Od9Vo{jYm(al3`ruaM<;&7 zz|zGNp}=~Q6QRKRvct94?k`?ezG@6pCaw`T&8=z+Fn|eGR^J`nxo&$Cal0{GBvH`3 z*fpi<6>Z$CuRbYJ9qNPj4kzfuRb+@9e6R%-w0d;B8MZ3|WdJb!wkUu;8cRVv)R-dRjlZo}38mUH2U1rZigP1PvY!Frtyq?iQMG%A( z?m=l_O4rsbSh-IJ$_VdX-dc(21PUUs3Xp7j{-D%%G|bQ}?}i8tLG;VcWX(=X!DnSS z1|0bx)mN==uM!`x@rG@R$fXE5JSOvvVRS0{93#c>i;DaAYE$DAMJ1L&;k|!e;%$GC zc>lqPclg)D+i3>F#k?7uT;#Ng_cO*JzDw=9`D2rQ^@xnSC_I-{9#%vKiVEyIZ*Z;U zB>rdW^l#NRxf$yVx5kqV0DMblf6cRE!G!>*(@mU$FJ~Soiv6OAx4uB?!;(%ZLF#Xx z4_Ivz-15j(eV{hVQa^BF?VwiNo&(Y(;BLWtZe zc^`d4dXNgdB@v1J}=jIPf5^LYB`f~S8lPb0qa)WMZN5yNggnEbRn zovYjCq|KAvdHbC8om2PS_eb9Oy{cFD-a4t;RoPV|bIvj5?EdEJ)x8>x z_0PPI!v6n;?djjj`2Y6%@L%XT{|nInT6^mMAMNSilJPILr}_%@t z3~SXG!8hI4?djiu|J|Pc>+R_~*3=Z-p!~5_ZE!CVl9eSSHPQl=-DlNN zi1(YL5qR+b`4z#x0slW-3h}>Fd-}I+OaH^t>)$Sg_>X>2eJS8VM4n11r2g*Xx{}1j5bN;;_^rN8T%D@Nrv-*GA50bUe-xD7b z)(UfH$2pfLK^k%TyP9C^oN$?Dk*jk4(EfieIQ@4&_`g~|=!}@soEEHk`ybyg|J!oq z0e+36%*$*oqk>1AVzqEY4_sRFf`OiKDS_h$iqn;g{|)=Wzm@U-a{XYyfAoWYOUA$0 z4-$cqb4a?T`-HYqzdc0D&tq8bnSRyleIdTKt&yC9Po*(JUk(V8n*Bi0owE^?s1b!)UlS-jcRx z#Ow{RQZb9mWtgIbA4Q}f;zOJQ@~8U$Ov2xQ|0E&sfBcr?e_;vm|ImK$zo5Y1TE`&R zcmG@+LfQb_AM$U1fB(;a_l3a!gJqrz)|HE!)V(x$TDi%kso}tVpXSu73z~5G#qC>R z6qXAG0COhw6E%ORAYkXz_=M1Vqn|AHa>h*BgbzpO&$J=&&l7@he>ud1Lka`fyER3h zYm`k&3s+z|EU}4F)wxFvZ0<~nLQJtIHW2S#9RdG$+&kn-RD^6+0xdrWCo}mS>sGHy zEZzJ)i7EQ>fJN-duGTha0Ef|JCyd?Lrp{;SZ;ik#pWrKmucZBDU)sAKCu(DW3Kju~ z?{AML$WlOj?~?QTGT5EHE%zE2%7VqzNj?=CdB$Ip)U#Kwa8YMurxbzg{pF-6>kf#= zcw)fXxHr#Z8=DZ@0iw7%L3GHUwPd3Ba0`nI-7DF42mlAJJCn~fO1T>u63Yk{vOy7( z6K`azFagFO@H@gwgH%oU2=EJT0hh*8pD6112i!08Vd&Qk2F{e7L}#v7TdHsf?1t#> z6~w>m0v_iVTY{+~X2cX=cvZ`f@U`uf)Rx;dd7eGUPCG5grfLAt4?NSsazVo0$W#}z z-@u*%6Ls8|V-c)%)YW;Jj{4TCDRC1LuvhBv2s3U^Q)<)wP&Nj2r^%rA11mSkS~jx& zXQGp)RqhHXzQNQFyvcL@1V6lrAl^HM-ui#Ht$~*CvA9<1d!<7mSkVH-H>27rw6CCg z&EB{_s#-pwgoKs6($SGq^iiU#skW57&j+vv0ntD1=9%GBj!V=#WUoFZg82~30$o zcE)!Q&WmCcgGf627etGotnrr0w*K$u1VP$J>`=H%q;^7V3_lRAnp?SOA$&FOys`<| z8=7Ry1OX!s0^_^<4co0Q+4XPzYJdl1tNrM@A3L0-A2L36LkW1*idJIoRaTx?MTs#w zF3YYrA&@u7Ap%U=a|t=b?W@M?L)033Vh63`j=&s5l78W3B2t^roPb{_I07-=@U^s< z(-XWBsYziln4K>rbPBQHq@lkvc+G=mHUM3q5DV0B2UO|~`4bdOEOy9K9_#OoU0JDl zz1l77VI~E@@d3Y3?0H-ojtX-M&CC#G1aDn*nTEE?_x9{w~_M>5B^s`u}#!cW&MZUOe7 zp&_%hz!{n3_Q|xT%$oP}x9#02Hv-5v!ZxpOx%xj!A^`TFP5;#WKpzg;WK@Wkm+fny zO9o@qPf?gA-Mn#*&pai;OaXYn&^s+xzPlxpPC+(mlCzdcm`9+JS&^-0&4^MhbjI_q zhz9h7k*QrPd6o=#(%@p`sCXa1Dl-K~K|?FL5gjJ^kl0qIE2>-c)S568e$mZ~-mosMEyH(F`jy+MBCf zD$^Iz7Qn%NiC;S!%UptFxJqlQ{h^yB*=yA{Qct#;sA*W+UjL?B#a(>y%?xF7=Nla!L$1=n-jCy_#0h8`~E z+O%w)NpfF*5bifB3S#&TzVPQs1c?xB!(MXRKEVc*e>OGZo=aRJ&SfM4@xh7J&5m3D z;m|bUtWz#T6DGr0@WhRw_p)7FMq&Kfd@$z=%m;t_x|{hipflX$?}fmz{10WH$WrqL z`M)^Jac1z&idw-h`VdIo808l~SzavSN&C+7e5#^&SqDlEh!1|q<8?iJ9B1hV z+EsMW*>iN_c2OWdthz@rm5|BWFI3G8AilrtKWk+NevjZ!gSqLjky9%)i>Y%JgVtq> zUMh%v{FjXGwN%Ixco67zf%p*6R*Xlwox<;TE0R#j&yxHQb)2-r^`cnV->J)8`D1EM z0dBkYMEdhIo5Eg;4n$}h7`gbT_TKL1E^>k*aMo(!YSL{E1WamB2eFW+S(enP~TVJ(n za?WHA^<%rAYq)pQsLaOY`=irCmP0V}r)R(~qH7eKkzmzlF*GEdo4{)bUflBe#+Bj>4h_YGhfot=g1i zh_qyaxdf5>edzLjTm<|g9nSKoqShBQxUnU(@5j-TMlKDfgA+X0&90pe(+O~rqyu?F zy5S%tD z#u-q5$aMq~PRoG{emfWFP6IDIidt&}md4F*qV*HHy97rx4X+^Fn3TDULl1uG`pfPr z2?zI+$z1^xVs%Fcu5CzHkq!akBcKcNZ`t+p_1oUJ_+9*AH?SB^zxeKvXzJSaby{OK z%VV#?ssF%@ipdzq5;%L*pb9;nY9k6Z;B;Pb1q-VlB{BypK1j0yJWv=5WWI)8q%FfW z)3FRO zz<1tA+-7rw>?uaGGf~O=S2SdX%;7g-)DtzAa4WHns=hV$2Pmw&R96 zWT?Ih?JCbb+`2o;3l}Oo@gzgESz;gO4+8aq(yCy>9`$Dw9arNS?Mf+SjV^*95E7rtm1R71NpQNgPjE9rt<)hfENTm)i%d zU{P=7pyZ@OVut1rg?hnt;wdX-+`z6zN7l~`RQG7G%4hEGZ)ELE>$kP&=RPI4uofCBttLY>28V0Q0NXy$Hzy(^l)5v93qqf-3T`fSKK(p;09nhfZS zNwv64HXxViCcE1`5 zcEuVku@~=K{%0%K5XkHd3=qyb^dyO%U@G3D^6M|KwQJP`nUenLRmhN>WP6)S^obdypsNUmK)8MD-W-#prn;L(V}TVv zL|7$n0{Ix)yl}#Bn}56endBaT|WUix8cWnJ1+8B^tymFgcq0lMHC zsnRXN6<$NIF*{g3KqezeUh9w4)3py* z1FfljFj8j%f4k&m>=n@EZ~I@Dd4YNNZ_B;t zN+29Wp+Wj`-w98SC!EXP_a!eV)e;u-w~CC8VUdfZXJxw+AU-0bTUv$J|3S`^1%cdP9L69QGS}cs{ zj54s)1G*4NQk3!&7pPq+?Q$2~sHT>5cK9AQR=7hrqC`5~At}v#2jTwmp}$6K5RXN@ zgW?cT!vdWg|Ma?Vb5-_d{bB567;V!+-x|xTP5!x<;otudlaDQAT^G{lPK6kt>5A!{ z*&o&C$l_`-Qzqsiy+@pn1LcF5i}27T6$c_Bt5dC520oTEB}-S%d3O=q$Y8C2QQiqm z0Emy6U-3s`WDX9@>(Er9OmwkL{!FFTemtQ~0^jsNI(z4lCsDt$IALPy%)YK$@^S1=A$n*w1(%r*AnVJ!8p9I>;Z(|vr+0!GTZ+`h z#J=LO%_ZPuG4oY@E_GsSN+?MeH;4mpWQY{$ycrD~y=KF|2lqv|+?&qwuA|yg44lnm ze;>+Ci{}92h>Sw&&ANziYpzqQn5{mXnp6o9>uZd@n?bY~<-MTt60Iem^WP3sv(N!~ zBhyQ~*uU$I8&$OOJC9_B9Qf>WMFaCt;w1bh0$gLk8-EF?`@g*cnot1Q`^!x_7T3VM zMy{(IE=t{cS=CvH$X@ql5Ba+b$@sOe5(QzGoXYC>T%I)Gmx6eCJ=dPeaE)!QX)Xjz zFPNovrXG4;7UF#|?$cRu?!6I&uZgw~itJwv=0 z)zLqmE^R68VoJi~N(uv7k?t*HFCg5dU|yDroq`BP=^@qQ$M#Qp)b`HT74Bg(qY=gcrLwK1OO$~K{N$({utzC_6pn7{(~r%X zOe+hekAw_;<*bl*^P;)1Jzo-eV-{Nu=tt>10*#mWJKeDkm|QE5Iwde@H_QcWuP*Kg<*LL6M6btx)ZM`|++(&;@P$5m4W!tJq(Ts|B{ z?bnY_2!t_qu7*D}fcXCQii21Ms28d{yL7p64YFUB&srrZmIr}}5Mc7#4Nwd4FiY2K zT;a@DAbWqgbaCw-;7fIF^ka)qxvI~3zEN@O)@_E?x3Q4_n;(h5Yr$xa4*gFw5Z^!J zi1qQ_Hz6dPH|q`c$5KVh;g`%14@Npty|zzOej5ZJ+{-EnmqCj;e94bVk!p4W@u48a zXBJX>l6o69e%`ivi}!$EYKnqww{4ac11G}7klqlTP^tCs%%nu`e}*O9HTGm}zBOQv zh8*ew#c2#5D*H)d^a!eF$-kS2KOwg@2~!)=&F(s~@)~%)q+uI)M!!JNwzg}-LOmCK z=gTF?zjfoYq9%mry|tn^yVD8GSMMu7kKi}CsmX6W4B1tnsgrcoh+wy`rtBFIH1M=~ zP|rI7e%}v~V;Xfa?r>8=jq7iy$_;Ke6&kp@ zTnIDCh@p|qd7lNuN8f@gr9754#B1_Bo#cLYJ}E(|0N zt^^EfH&u|mhL{Ym?Q(+zvydU60 zL+sl+8kTjd(N+^>;+%Fsd<=<@Nt6Ga&1_BA&I#@Ewhna=rcAm!41}Tk%m=j}tTbi{ z%+G&2u_{6dj1R_~n754QaKwT?@a|SMqYW%8nb~@b(Mtt`Uf7h&CRq=AfX<9do^+HQ zsEpD1l7xs`9T2~^;L)K48}3WN#aO$FqFx$2fcTgcpI&)=e_R~t?U9w)+IBm9Gt$Kv z)wh&H#ze{0ufv<62lz5+$cK*mPk!ZZ|8{2;+#2zn>RU!bM_rXuJ>=$G?|_-=6bP5y zYI=I7ly>tH({SdWtR;}%Hz3zGZ>Lm z6}74MPR|GC0qf^s%N95}Q1?uudqLNc1;3BDN|I{d27Ci;Iy z`jEddm&z4i%((D{Yiy^I3Kbzi>-@}|jO|XIE)~l=9^CN4)rqwKY^^*LakVzV@KV;nXccktU&zxc_ zZq2cWEu=jhkT({(7n*m7RNx=2%h!~b)%6m^l>DV_vH6d8PSzXj_8v0N_PgMvCw%OL1@vQ)feHn)YL6-qvitw1b2Pg6JpsyrY83J{n^Ox(hA@zkP2i8KwrX= z7rtTK1F^EpO>p8gNLCcdi_&Ou#Y1Q55w-ILGsHx*3#b>CRwTi(BT35}Ec6%z==_|Y z>hjTHaY4S~%!?kTT!ML9=AihFrC8oZ)as70A5qcDvyvL%oGq<5SXjSDKOfV_E+fu@ z`YV>-8GP0BD&!gu#;-!zjnfii!k#huxhOMOo}$v+;dVBVfIU_Mch41L9I2eJ0TWF; zG#xu#_3ke?C(@bBjGa7dHR+msAemGw=R||ztI35jFl(pz z#+QqhGf?xPQaCV{67ex|x!b%OXGdJ5qZ-HuyBt4mI)wNH&L0A>r;?#V7hT#$qNt?2 zam|vw-i(E*XvL7Ei@1DW$i&;!_$!Esmjs?9G4WmnBAX;{WDIx#RNKDz3OHUZ#NbtJsC@1B#tc zUyUYE2b^HNttM1ZM_o&*IvZuU=rA9d23H;K@QV~PnCM>B|J;iM`f(C{f~mUuMWG(8 zH7-U|FYv~pK#Mxgb@Z#|v%>)K=p`{GkPlAp*T><{2-*sxW(J)pq^p=M@FEgc$+_Ef zSSjQ$e<&T~fcQAOmMh*g7PebZG^|1+%boU|pv6?HZhecFXj528=+>;k0DD~VrCiU{ z0feO`&HQRg-;%~Rh>y_GS2R`FSZj6tqll#rfV^>~IB;Ne8ErK53d9b-M-79ZRrTdX za+RFdttn~$rk00;4NPoYsksa=QOzhS@cOR}wMgy;rBG3@|GetJi>uRULQZAT6a@Hk z7436E+a$8+WaCIE=f)bVzTV`)KB7wf6z6R%`1#EM{R|XewW#dO!2j0{+TgDitAJL=^dmDyzaU$P6(m0$C!x(gkdMC|4a^k;`Vw9)^OwzR$;msxnXDO6`YOH= zvs#!V)o4B~U3=SIFDJC@}#!o?T#ny~@vo)p-;fZViur=r!Ns0EV7y#6#b zz%SoI)+3oc8|;G4AXr+0uY>9wA~U!eQ+*0lZv*M)#jRz~^FH6n9s^$dzJJ1%%SeVf>l{NfE4Aeb8KK=5S#1nJI zd-HznQdi^4caiN-Vj8v+jscH8F^cEMeL$V^Q=rLWwe);ytY@V_jWfBK#Hu+Vf9Fph z^3_KlzrkvG`!W9g$qdtlg2ILLOh=^Y%8GQ@3~cwu;LNmWTTuVZZ|k=B zbN7m1)J4YzF4j52FmwM;^=|ac+$Vt=7HiB~CwLGKhd`}3UWX*5MaT)i9V7 z#WHgj^eRTe0TvGt(0V)nC)B3~k+|a8a;)hpIps%cuJ;W{Xae$UZiq<4pMuAs<3RTQ za>o@k5>N;H6FKA8(oTI-qax{Yb4-W9zrWkjuf;PuUyod%Qr$mKK$2{BU6;HqqD zO=w->_Z3Nak3fC}dRQ%=W-8L>5}2PgH&T1#sx^3$ZkDCnGr4?5Eqxy2-vNFFj^C$} zbGrF0h?2lf(#1MpF|#7ro%YO9<$Y+97(Dqd1Ad2 zjeV%}Xm6>4>pc?*4g>1oZ@07YBLKPxxk5ig-H=x%h-fQ$6|Jp{m>Fb$NN?ia%HG?~(%yYcmWJXq&)1libH z1BpFxgDs2T51{uX!tTq1O^+z!Az#l|FVQ1Qoobf1dHz{fXAzt*i8(=nhd%-4jlX>n zuLZP@B%D#xt;o7&RyXRQ%3LkfdPgx7(>>W)+$*tqSdYPHD-jClESwEZKbXNFt{u>< zYW%QR-eVg?^rI)6lm$awP?dTUQ%45SS-2xl^ZVWkC!;H+?mh8b46`3jk9B#g*MgB}rw`W1gwtoB!*=0HF{5d*wV2S0oGrvSa}u420f|A-9Qluy|E z1?Uhwm40RN8=!q7BHKIir>NJ89h31Y(btSo`R@(tZ$?QKf4wl4G0~JN___n}iE_<( zN@(Shb`Lx=<+)e!Fu7EW{gXp=c~Nj+e2B&1WQ6alxZ{6&k6>k~ zoSQm3iHIqP>Shv|K$vTiYx^!0WN*liP*m%+3e_b#Q(!IkgH}1}Pj|165Xm2yr@ZKW zkGDYUG-8vjhwq?tU)!@!~Wy=CTP z+s)IteG)(V_S*7*jY$81y40Wpaibgw=pweb?@?M$kga!>C)OIEvaPb$w48wm?RM#= zfuuaY98bRs;KY$8Inc^W{_O2Azfz&N*_laV4oPKL+CTf%qPo{ezbJ$D*NO9LT_-Ut z1z!byLZmBIsV$xy3c&zB&2>X|sW{AOZC8l^_=<}lE%G0yKzPfnunrXd5Oq7-MOs!8 z8WLr7f;H$LRin%V@+&T4F#4Rc#^XIh7i)F=g`y``iTyJh((1uaX$c4LtO2GPKxc7f zU1odYlfHE5KMD14{5lyqyU3HC7U3JMaBClDD}oL{>%ijb1p~Z5F6p z%zFRqK^RFdvQ3vU;X-g20p@3M`$%|6YYk`FaaJ?cEiUV9x(s??Xwuid?1`q+ig*J0~*-We98s0 z?4X|iy48xYnsBsp%g;tnPL-uLARiK;o<1*396KyegbDO_VnqJQw(DPt4nkzV`=gSm z$;Tj50OLcVZnBqj;#AL%(6oOJPmpKtq&O}T%O(53C>y?RuczDk2;d>fBNZ^;#~#(H zcnv1dYoxyt?t}&=ddt$O!BFejPTcYp^qyE!hA6fHrCu;Ng!rBXzK6S5VbHECr6uR8 zU+(Mi={$|z2f(kSMqqQ@2i>fb- zU)+O3hZd>P{cMso^S9BPHWs#g2VlHO-%nP1S*lfWTz19#=eyIHp=4|pw z+>Qro(Fo94hAi>(T(3s2^t(T9Q!;8+(_cE@>A>A%Ac?8rk{$Syz2*VqQijE!)<>|% zb7OYFO9EouGwO9W<~{{Ut|-Q?qB)$>G|n7kPp*OM%1|8s=%H^ccAlP6xbJcQJKmY3kRj5NyT(+hQx)JVON8PcMzAj%>e}tuxyfSlyCrBY z;^Js2%egNTcW&Gi-WAYUR$Se3(qb}qQ@meHHoxELL^~HoKsDXuCek%dAeG6c=riC~ z)}c+>hIu})V5nHdzjtb}z9X@Wtl_}?Zj*^b-0h&~EZ?vcQE-Up_SCH?owJ0;Yl{)E?A$ycec zqT8VO?lywxQfJP=eTlC9`nZ;VIQ`gxK!d2wmmT9w%FJS3O$FkU<02~Cs{cI?E7A@r zzAb$0DDLpqiM7+t=Jt!ys3BLFUNgl#A1u3w1^+H1UVoCf}H(GYF_>W$6v0ug2)2*I{1H4r=RUfBGmJ(AHqG z?(;i#i^}0x@J@j`P|@nehj~q?-~tQ8#Q!qbwb!ZjT7gF1JSG$$#w2RKK@b7>RVmRN z7Nb#WX$r={FcnDn`Z{YDze&lD;h=F_BC;^)># zmJ*WOd0wp7Oy0-sSB9QDVp*dDkT*5_ur@0Qw${DCw$oY%JxmnlKuK$ED!TpAhN%IG z_{%d45RMS?oqLIpjv-3hdQ}y-4t-RbGor_F$+2?B`w!415B1@I&T4WEfzL4t)27n? zR|V&Jt5uCE#D9<UA9>1lqBP?nTD$e!1TO<4r9Gn(PPTMMNA=9?J31&jGxsJKj@H zdEfdhZzQ}qUDaY6Kxeh>k^`RyIC{B2f~xH^B?o)&^3*>k9e#Gm(9wr+cT0(-p!iN4 zJ$fmfgwUijgZR9RrbM@a5P}GuX z$jY&mnx!n6W4zx-Ccbey%3(XM3T(&gag?XMaBlUnvfZe&s50a1ldgMQB+MjMyjt-MznB1L&d&$B}N}kQfP`#&V$KX+b+dyMcmfuq2b-MS|!^{h7NT=BMHjY{b>O~93hV3BxfH<5? z9?Wfi1FxG^TsGfb5+TtAVi`qg^S*@fH%J<*`*hBOND3i-FDR*HssEIXN=`b^o_dX}#A5 z_-aT0EOW#WTR|QdTC~&7WHUm!Y_dJQtXb*MDJP`GN<0Fcqtc8Z*csqX3AZuSPL$EE_jjVTq zg<$*$y=I|=4(OuO-Rsvo;j?2SyV)J5mbO$Z20P1IH*>^VAKVoCizS0$7|4gNP#P7i zz?Q;G6`oc=Dos7FFu$`FAI~YwbI|y#GODK~Xdkkkay_@9PS^GB@uEv^3l~2PnmXQt zVARjOoT4qBD^7?NbikgTDFq5KsrOy0GHvIFfw+BiKfl{)`Vwv_j$T9W66+ik(0L|3 z>jLvqS%*>I3EH6G$%Y!Y>adwEzg$kfpb;zel;XY68(>`i?TdAe0>FHrSHkegiPiJG zi3Alja$~Mjhc|SzUqZZ#d`U?B8%}Q~&oQ8jURPPc$Yu~yzD7Blq?OLR0x?WRLoOAu zY~u^Q<&5X$Cm29yy{=bzR^|=yG_hq)Dk%H56jlDTXX$BmSQJz&XJKyEk2S#f&>KC1 z@rJo>NbI<4Re%ky+^zVSLQ_sV;z91%VeG9Uq72%Pskh<*!CsVeu2CKN9Pv@9Mo&(A zzM8mkNynP6>Ci6wgcH=~=$*wlRAt)B=aygXd!cfXM?+^lj8}ZLuC-QnH6pVxUg z)8{O($R{{-b3S-vD+{2qiPW{ZLDsh$4}oB-EAo%vS(^sxO5ZF^Hof!WV8f2H@%eqM zm#y$u)L9#WaH2u$-H7?EG0{ChXMMk?4r@FNwqb<_XvLpx%w#WTdkfGz@M|E{KKG9OEEoQ} z*w>gT6JtNv4IH)1fK3CBoyz+-=AT*3Kt2ou8 zD0Q>85vb1@f$UXB8n>e6@IhlA-IXHKDYaye=y$D@TR_mq%2mhYT}hh)c{6?WYzP`Ip7Ib7HP_!^!WO_AoD7#88YWT@}qyM0$X zlQ<{YZK(Z}ou1&PwC5WO)RmElUOqsrt2lEj z=ZRxCz~gWC!WDqd>l**$cnI1fz1iCNEj6qy&_B)gT|fY@;lvN!gJ zr0?WB;Se!4y*6XM&TRw1phFcRtkfjkTk|TgQ0)!S#U!SgSgV}otJj{{BHpP=W~h6G zf8shXcF)pP0)1_BusI1Z51HH(ZmrKlF`_4MM_k7Cb!>+=PsFvarr8);OFqqJIKgTG zd`*eSH3?~GF$pyz&%Di)6PEQkw_-}^H2Q~Jq1n2SzMI#BaQsp^tP6PQZN7bPbP}%P zdW0h3k}cb&Ye*kAcsUAV4^4nNFm<&cmAsadFRml?wLKmTo$aNyxoeGXh1PDUxZ_wz zH0=ZO_O}}tyqJJ_%`6E?mlTKhJJ^qC{?f{qFy4+YJWB8?6OCrncIt*a9Gjs1=;qAK zE#|1X!lAUb7MuZ#>is#+@Nb1SFNyusQ6AZIsCp1UeCDPvu9Z2dGhpNU1MYpO_)O4a z(2IGf`N3P2MY4?gjTR07U-PXUtqn{KO=%doV#4;Vm>D$962U$?*_F7v_rQ* zKFqI0zA?0y5dLaG&=mcY!H<9jlQnd#`}O*u$65!J0q`AND82I}a7JP(MMV1*j`a&*k-+fYicq zFpI&)DDgUM(Z)N0g1()z{Bypmi_e6gK>OP*BklU_eGk5;CAVYy|jP)~g21Khe#VG~28#lS6TQS5vvRU=~bAyCeG1ZDJ)9sRrcN^8C7l zrf}qt!0A`t#K`vDG&OX2-Q9ahF5v^TG@FJkEO8(oR;cKL@;kF~gq(|e?;UCamwgxn zX$pX%fbzzWAO^h&8XAIW|XIUh)u4Nj3XE*^s7b`D9q`=s$AAE;es!RxS zbdS}$rgkFSY)I-H4D`J$0wVYzdlfPr#F1}Vq2s#>P_blq80&;`Wg1h4td>cXKYI-D zUep0ytfmsY$P_PZ!G)EMz+JCKr}$^a#H2sqlY3a)M^*B7uz=qCS!3n@nf>N+B>>sw zBzB4^VE@b(9V~skNgDeZr%(9d)j$l0&su4(s`#3yZndJvv?<5&hpH1;rha)$CA%KR zKnaIwoC+=wpN;uB5)0%pR*cObhZG~cp9ii^?;om{zry0>*X zLS*KYd{>e9&fV`rxU0Z~;wO9E=&>g`!b@#S1odjHW-R#fSzd7r_NKz{p4KdjZX1*TuHiPX4=_KXJl5Am+q%k|r1Z<5cnZTl?& zy4WH8vt4P$f9Cs_e{Mx+nmo1E#{OLZEsY5sCBRlL=I^re0fZw>;n*hzXJ0Rsxot`# zWq#3!(JrVD>?m*-#=ml3KtX)~>d%gadr``QB%zgP?KLL!y9HQzAXVPuL zil?G?AbVVcM+-jb@U`SP7YG@S*wdC56#0bd*)SCX7RK9%%B-RQ4?7W7pViq#^FtQ` z=^v%FVuEFeh&)6%@1DL<{`&k4(9cd;TZ!3tj&WNqc^^AFioMODOec-J;+-xT z8nqZRw}o{f$giapc!iJ-g>gTPvIo_lzKc)nwI}Y?VUs0ETHj%HMe3&koITr;;f4-G z*frfZhy27e2eyQvGHLHhHz6|{$TYKfyyXM{_wi%1=DEs;nqH`l)G(_TZJe0oC%=5t zj3D@k79tg+u>{cf06uQglaciY=Q1e?5l&m8EJ1zesDf-TyM+vWE@gtnbWFwocsP*h z@sOb4k^V-H7TZ}|d5ReP{5qoJVBS<>_%_G$hf4Ak(AmM`n?`@r{{Bit@uDbyK7blHXj4_4|Rzr&K7WBIO3Gh&aN%Kf+q z^fIb_9%vmBjiJ#@pLRbG2<`&9ILvk&e1_rT-6fx@d4`>wTf|iaAG1oLvJQAjh-_Kl zItM+6I+A=MK%bY47s5w;j{g#de2ksAY-k*HT6BmLZ}cX?1P=O6hokso(DrXxs=hUh z4meoO!9XJRCFIm=2cBm16hohh7s4nT}^r|DTv(R7g+sz0SrL7H2n6)_* zB__JvYi33uTp(VQsmJfsknWPbdtIY4LZm;d<*Gdjc?maSY?6$ot_=VW$EM4?AIJ@j zkqewu6hemW8(2Scoo*AkNgJfE9r$P+7#4u}z;Qumgx(OdUu7<1UmRDZIe4vSG07YA=Zh|qLy- zWkJ`Ts`0RG^IqS1RxsjSLfU~Mfi~*r#n&W+N;uGSs1vOVv<1==f&4alUma2n+Q%yP zbk`K*+ie_KxT6I3>H9{2hm%0XeIsrqdQ0T8O_3ojb7toWgutoQf^%r%^~~8DI0R@t z(Mgismli4cz3cmHS{#LQ=*4$&u|gla4d`j~dYfq{U$8;_pOf*QddU=(tUJgZEUu<> zyzVHN;iZcXE@D?6`tBZ#Y@ua9XD6G`IpM763p;M%(VS~3>RDy#lp$=T?_#=#Y}G=A zQER_Jxa@;=s9~^ZGN|6sdH9!M`skBWs0i18Ud7buS)idP8-v!Lo%!iB3PPKxEsh8F z^kT5|WVwuIJ%PQrGNN{ymm&g^7iRgc$zs<7zi zTHO_2L@W(3KAd|P$XU|%Q^*cSpD|MnhHPS%lTJxP(Qy>u!g%+*6r2!&`2O~(RdLYo zs`FUR@m5iOx>zK5D?E&5x)PUpme~BzR2_LWOVE#on!0JA4xA@OOEW5N3Vf2Egl$&% zUNEZWN0PU`he@CLisnEL^381n@wt#}gZI??^+uw8>Te0iyBrn8eWF+2wu&i!-;KP> zGjqTM@O81M`e%FAY!5`OIe1krHg(-#ZmSQo#js&){M!J^PCFA7z{ACgU8y?v)_Y;a zG9x0gERXt=bJy?pYDj!M=wF z6FAjR(H#Q4A9R`aTaQAKmaB}`_Aob?V45Iz#>0!153TrN=T1tMBqF~6%p0y81y{6X z>7`uY;qbn^_>~jZRZz$z0Sgtx2uSd0VFZPsbqm*04SQ%W39-(Z7;LBDHKNd12!?P= z5sO0ZGZm;({#iHBdnVThH+yc}Z9?>JMUCqK*%-oTEx^NlaW&~hc=U%4s=poP35h~paD*ACn&3`d7zMDW@$Qo3gaDmAX`7aiHQx7HGWJBH zeu5+2bJQvc+|48}&@n&lNjDxO{sP43Dc+2LB=vE}fOp2lYcB$d7VjRcbbg`PPjBP0wOS9d1_t!Q3wT# zBMJ(0G*-LJ1N&%l0FDjDliaO?mJ%FMg?$3DSI3-|vK`UdLY`xFhO;46;;I<4kM7g{gUn( z@uJTO!%0cYM0CF3_}6E|5_e`UAtEEXH*%@fpmWh)jM{9$a$B)jCa|{`->6UY?_~|S zBhB9TA!T`EW9deeJp%T;YC8__>J+T0hx7M_ssNq6 z=Cpat{0aF8LXVO^_&HT(YQ4Xd7K6opZRYLHcq3uli2!u=nk$sQuWVoAb#n31NFF9W zRD$eE*=iw%UR{m<9v4`B{1ed6hk`U>4Y%^E$9UX_Qw`YMgk9-sM|Qq53&(l&r(cT$T+T=UaZ7e5*9VXINQVuQy%B^g=u8WG^@V=td{50C7s zS|`&EiOMNf3zuIvfVCx*bIuV;iGct>u8vitllD3x|Ca`2MZ&+0f!vl zZ~peFbdR_*(DSd)a&lFFZnGN27dck_wzJC|oEAAmiGbJm)C>1%J*s#~0>GZHsjp+X z6CEiRH+@~;Oj+6$%mLZ}IAt8?-b*t2UBSAmIiR1fd8nC`XmkKxmhg1`B?Ng1%7IZ# zbb{_Xsca8q&w)`xT9v5EhC=tvYiR#0o3!b1t}0(-`tSg?V1at(A2!+pL6t<+v={kRU7M9Es2 z4^>vW?ajD3rki)@*u`z8LiZiO+5IHIpx0T0_@q$UG?aPj9AOmp;p&*L;_$7GN0=Mq zuDk}o*@NyY9ukeQ;@eBR`l(>5o*e{#nNH?Jaf#xc=74YuzB46I?>*Skjq%)}!$|V9 z2PHcj5&~*VUq@Jl$wg(nojl2Qj0m#;{5)zJErXGyrDWEZ5bu>)FFub9y>9~B7GR!B zW>r#{?yatsIexAGww1MrR{9mRZSiqt%FKGnO5~Nd0Q@|UQl4MNnC;3i-v)YZiI40q zS)p$2+4Ndc5hbxA;|p%oLH@nbBA>vV#bCDc=@MC2ARby!NpP3qP@mix-&OdgI|j@X&Wwo0MkdqNyl_g$0e2Ns^0o3X3N29poe&MwKklW^MlHrrDXretpejc{ z=bSdzqbk*u_1!CsuV20Z)et%tsP|rxa(3VmCy=;_$csd-G<;|yL}s};H=g^aTc+We z|84a|0l0W|SHqOj5A?I+*a$MFVSi|;94`h9Kbci3`D5idWqeL)KWBOX&dK&AS{$Trt5#wz@}D@2hv#ZjYOW@X8&|lG z8cPh4#~Xq`v^CW;Xg}L~*9HUUdPKpNB;r(y*L+PtrT}V~qOg4T1*)&SBdD@^iwq-%qlN@EVu$_vfY2Zh9!J-)6z%>KoQh&9UO^75IEc6ZHGPrK74Xm3 zYkpmQ?=1E~+5T8{pM}hZAMbvHpi{Z`6AS%#i6-L{Xg}6BJdbk^ZNjb2|3FW_lPpDJ z4qDF(w_yv9BtkAybaF3c3W&pZLD}~Vk|L%Yr;!Z&#$PcIzB{1ptBsF?XU1GR5{`W( zEnwFVY3Ne5+|bqaj#xIFVGDl1AKgcDo8i(tlJ9Ezw-rJ29gr`6;)j=ai+V1PWE)wM zW;qeK#M{!~)*6Q5oCFcb(!YMSJ_5Y?rK)pgrj_`RN9wOvY}YWU`0=>RGO5N*ay?VF zmMk4q6@%pd#|2kLpnf>N-N&Ca((&qvnYE~Y+hz>m^;3x~XcTRlqsg!`Vp|HJA_4#W z;naRf%#S2PM>`2g9m&A6#m3@{YJ;m*41^0LRy)-?Pyo32BY*OlY9o@WtmL^+Q~6~o zL(9k0T({MH$6{?Jz0x{*#RIH!e>C5D!k(99wQ%iGmZiA215Lh9dT4jx5sEBppHD}BkkL^wn=>O z=58mhSi;zdQ%48v2Ew&mH|mjZlC1j68PEmZ&YAR4ie+#a8}6agaE-p|-{%1Q1`2aS zB3H%LGvcaE3fVdOOs4!3+p zky6Oo`4jX{1~#1l^d&CW7Ax2XBQ_x3prW!jjl--7si#62l=F3**OwRp^SWmCRUAo+ zEG;<%{X#%4#07s}9%kjN$~){mT&%7NiD4dm=?%{X8$Cw8Gj4Mm*9h=0B$RPdf*2Vg zdFtyENg4~ZW_6ia8eOWe zDO*h9v}NIDARq#8`Cm8o5rgJ_gc_aZLrT)~ptSf(oi9RWYHBzB!U@u04Z_NSZ2a7{ zpy3J9cOo_U5yGY~?~~%f-f8xm+i_)Vn`ybKLei|`WITH0Kpx1`(6@$9uS0TU?V-qi z>H`f80aFp_fuv|*ibi z3??MZpznEM_!7;{Qc_mha4_pn#3ihJnOkS+r<~s}%$c5LWxP0xXo2Tyn6{)WB<*eP zr?k<~Ftk}EIqjR!xMf7gg=g}e1!eLDt$X18B`mkK7e<)e(k^u2FvN2*3aotSd0dfj z!DRg4I$A5U-y{(vS7>}!_=VDiCGB3A)LbO&6sL`sYOnxtwEm!Ho6+eH0-e5zUVSN>HUB}Qqb z_3^c0{0D?!f|;I%{Oz_R4jxe7LwLfE$q8Bb<-xCa*-kY=^5T68mp3-7yu5GcrcxJn z9krnTq40D8UR<$y)+}XYtf+)ElP&S96gt}zMltQ?*s+B8+&^D{ybrIj8yc0sK!zrc zaALX1d%Q&2AGK>|7jVGtzi4pk+H<1>@*w=+HGHipwx~bSLRe#`OZmy}RCbbh^0VKD zhuorIgAA4mfM59e>sPKTqv%AXl<2*q@KPciSg)-ClQLz#VTj?q?e$s?0GI!DTOSxY zU^hZQLPP$(9zvNu&HIc9{P4)KlxVN!-}_?2@&Tr^lRP!3Z!W@Mn@m9loRJ&3th*XE zS>;7tRwP5koxGGfmXBzMcKx#wNMDAoyhk<>c~)@qA%dD`1YMSFdS`)!5;g-K^9b0h zWWE@HOGI8BTF58%$dlRo3Hmi?$ykr*HSg^r@bGK*5rn7%ov~Puz7<0c5={i|o#A?; zJSD@-8df|V#@|PbT%?j_ENR44&VNDrHV8+t_1!U#U%2x>EQz%gH)8Ni_5OUi>Z}-o zrwX z8x1MQF!(EsK1^2p0!LxV3D3*#X=XV&oB`H(9~!xEOb($Em+G7LyNFUx6 zFNR3XC%nKKL^eP!>Kp!rKkZfV`KFvd40reyJh)%e-c%K%im{Of+#FGaIV`|i)X&C< z8!gq{?8SZfwy@1L2XNx4t`;@rWu$$h=FI-@$pb(>N2RTKMqq0nWB+OnrHeV;H!w zOpQ*rn8rUW7u`Go{EM-3v>mHKqNip5LeHoGW%W&BZMt(Z(h6n3=x;PMYyb^BfM1M< z8e?0im7%;={nC`&;Xi_%04;cuV74eYco&$(CHk7vYPGb@Ms_cogq8-gz;^4C*(+ z8sI>@|LeXjb7ug*I3s^v!LkOjhj@$!4^902msl7~tapw9v>j$DnR11rXHfrI+;38A zx}Z9bgI`e)bM30)Q1$>hGe|`3t}QSw z#Tw0Q|{oP4=#&vi6@hjB+9~K zue(0|VFQa8 z<5iRHC}}f*ubMNSO8ERnJ_7P38M`$Tm3+Q)Fx|@ySc20eDL>{QawWx#m@}MNk<}2z#1OxJElbDg=U9m{gv!p3fa81CUGQop7?w{I!>z_4%=s{+$_e~1^i3neCG9k>=@Z5)c&v2ex8inDk{O?$*@&PojYKqB=*Gz zbPg!Z-0gXF{_L=J2TL_ggsIWM!oucwKi#yA;useGvJWDH4#>YWciyB{(tqb1NTCpq za6I=3r!@Zp>-V1D)hwRC9#Z+kYk_*2mWWbxP%HHg-!1*PX_f{3p=Lk6zXn-+JDFnI zq;^LGTLQ?XQ}DaWFY#6yxl);?=*e_$ABHO6h{&`rWk~R$al_N-qyc=Sn>@t*B)Oo? z&g?aSL@&S~7W?9VMGOgHwb(NZ|FDh~jtr99I#9L?ypls>+pn8SjcC*`lnrh5s6+U& z-buFnpHU6fCnKt+-5Te9_h9TAn_Eg3LaeHvhEZ_*n!QX5nrDtijMyO@UO*ffzy29Q?zgIvBclfCS&6g}BNfM= zT;A9o9}h-M6t9okasl{dECyI)dzHy;M5+5*PNv@BZ%~L>53}J#CT7;5o4MVFO#=Cx ziN?H1nqkc)Bx;tVqaM*YMwRAzo5#N9ku>E~Px8zt!VK`6XS$*Ee^Dgo+JOd zwBPCYjBpd0@?iQ)eNapNTJd5ZeZOr}t^b8YCL9@fFZo~BS%QPkDdo(#qG>)kPf7=I zFR^>03Axg?^)`s{WDg1+EX%$bs?xdv_~k4NPWWu+JBe{gqGS2NCc63|TEc=eM8mw# zPrIZHKaNlU`IocX6|AMIM@=>#s?yO~q+!K5d)$>I{pe{r8BR3)kiV<~;F60{aS2s| zT&KIw#Akduq3T_S6J+lAg8v(xk9J)Dpq|1K;5Roof++Ia#t#)LNP`a(5zYO|zRA?| zs^FC1SB_MUs{)o6kiIn36@0>^!!!0;6(ZFd<3Wh%sz$KNo|+~#+-_vq!ClZf_1qE# z!c>K@X}CLzmO_NVORQ=Krc!6>C#NV2&UU{{bx6>;{5(Q<^MQHTWW(}agYG$R&!Oh( zw1MR+ugHfZ9^~t>n_Untd43ok<2}a2IVe2n*?|^|O)d!<`J44-+wduB(2N}p!_`1O z=M^~8tE60Ro}-72vsh0Ht>2kZv0ew=o_*;+nz?U;=spK<&WH7%b>UzSaFAE~?-l`i zjEqRUP(SAo!-xtC%4?luVHFCH93IKuEy+yocfzV@)%&;4i7rpP{#vAbp7or_Ie4^n z(W^i{=R1VfM&Z^Gx~ z&VUtjr-Jqq3mB8D=r!c8a|~^=y{7b>@x}j85oHc2p|hxwxBm-t+63)S7JR3cK|Jx3 zRoL}hBm$qGcl=@SPS_zeU{-;8oXcfK!p8{YLBYjhE;zwq&>;BtDlux5Hs@)uX)PTY zR_6q{FM)~B;Mt(Q>q7bNLhqkRd&<<~qRx?cyVuY-%bwpB?M=I6`HWmqU|$G72lUK~rIMt2G*afPf|8Iqd<|su~4I|%*nu_;{=g~q;Qle+a*QC)i5y?ku z!%L2*0J}vfmy7Nb)nD7wm*JMmfB4O{V(^KUoV?LyrJ1Z&Mqv9Ag5=~g?CwujP8a3o z5bf#-UjOv+czOgu;gaPxBRi=JR#}7km5VfA#3Cp@+^f$>2%!(G+H0I3-hEy4wehmW z_!5Zb&_R*(_-YM7sdG- zu?nU$$!7^me= zUWxHVd25#|kGbNkjORgWZ%+e35gw?2B}-qWj1ZUK5PGj$pE2S8E@n+2#J=pNQwvcbV&$%&+=Sl-{ltTONY~kwe z!jFEi{`W$D&KtF+iJIcugu!V@f2fPIS4jpuXG-C3mGY~E75!CG{4Cv7nVgUny`Kg2 zuHw@SFK3lJk}^Pj0Hq9F>|guVens*8V;VJlUq^v<>@aFEVSKfKPqtAn3Df!j;wasC zZXe(+OYAl!4`ZR|nI0KnI^UL7rz4^16M?2dAQJ+82P=JLm>Bkcfjc*X^W^7qDNyq< zmk47~{TDTu3V-j}%;6aX@Ky#@jF&QO8y6BhQ&w1TBzBYJnYzpMI|>R+%N{oQzmG~=eXXmn6AF_gtro~pWY+j_mp@O;j;H3`+fRA#ntRG7QwKPR1Wt8BI z-AzxwvXZp4iLV({c8Uy<1wO3!0lVeB?hXlYJaAXqlCYi)W)!le4h$bnmp1d;xx->6|iMdAsBNU6-C#|VI5 z)dB-LofNA~CDrNQ3qqeYdk5@64J~dcs&5K$%xkjyK6XI7Rm(qi@6_fVSBP9o53`|y z&5P;1R-;I2>SQIDdDf&iNbW&$FCk%}KBeKcrR}f7M?cNZrB9JukCZ98Z9ORrJy;;P zL37Wml@Na|YD}6bPA?yt{oq2A2k{PWGh z364p^UVT8k)v?*H(fZJ`;*@Xgy&cGpnZIk+#pY|$E-Ox(UQD2V3v&VMw>lm*v8i#^ zADP`o_NvUV!gz@mO<|HaJUIU1UH->Z;hQ9or`6?^oai#PtYTu2HiHl|nhv({ukIZi zo>V2tHQ)$#u5Vx6;C)kb_Twt5rqhjVD3#C4ayV`oH@6453n@R3FyR3IY7nHh zI@Q&>QEf&v*grw0CHq7OT7Qn>{LwC$RS6b;SxN=?S7R|ow8|s@Xxt<@Cho7Z!mo

    CJyD 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 J2lbx!{{TLP56A!j literal 0 HcmV?d00001 diff --git a/ipld/car/cmd/car/testdata/script/get-block.txt b/ipld/car/cmd/car/testdata/script/get-block.txt new file mode 100644 index 000000000..1e5fd7c27 --- /dev/null +++ b/ipld/car/cmd/car/testdata/script/get-block.txt @@ -0,0 +1,19 @@ +env SAMPLE_CID='bafy2bzacebohz654namrgmwjjx4qmtwgxixsd7pn4tlanyrc3g3hwj75hlxrw' +env MISSING_CID='bafy2bzacebohz654namrgmwjjx4qmtwgxixsd7pn4tlanyrc3g3hwj75xxxxx' + +# "get-block" on a CARv1 with an output file. +car get-block ${INPUTS}/sample-v1.car ${SAMPLE_CID} out.block +cmp out.block ${INPUTS}/${SAMPLE_CID}.block +rm out.block + +# "get-block" on a CARv1 with stdout. +car get-block ${INPUTS}/sample-v1.car ${SAMPLE_CID} +cmp stdout ${INPUTS}/${SAMPLE_CID}.block + +# Short "gb" alias. +car get-block ${INPUTS}/sample-v1.car ${SAMPLE_CID} +cmp stdout ${INPUTS}/${SAMPLE_CID}.block + +# "get-block" on a missing CID. +! car get-block ${INPUTS}/sample-v1.car ${MISSING_CID} +stderr 'block not found' diff --git a/ipld/car/cmd/car/testdata/script/list.txt b/ipld/car/cmd/car/testdata/script/list.txt new file mode 100644 index 000000000..29bb9021b --- /dev/null +++ b/ipld/car/cmd/car/testdata/script/list.txt @@ -0,0 +1,16 @@ +env SAMPLE_CID='bafy2bzacebohz654namrgmwjjx4qmtwgxixsd7pn4tlanyrc3g3hwj75hlxrw' +# "list" on a CARv1. +car list ${INPUTS}/sample-v1.car +stdout -count=1043 '^bafy' +stdout -count=6 '^bafk' +stdout -count=1 'bafy2bzacebohz654namrgmwjjx4qmtwgxixsd7pn4tlanyrc3g3hwj75hlxrw' + +# "list" on a CARv2. +car list ${INPUTS}/sample-wrapped-v2.car +stdout -count=1043 '^bafy' +stdout -count=6 '^bafk' +stdout -count=1 'bafy2bzacebohz654namrgmwjjx4qmtwgxixsd7pn4tlanyrc3g3hwj75hlxrw' + +# Short "l" alias. +car l ${INPUTS}/sample-v1.car +stdout -count=1043 '^bafy' From af75974067786dbe494642ba4118958b34f0ebab Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Tue, 14 Sep 2021 09:12:49 -0400 Subject: [PATCH 3570/3817] fix: give one minute timeouts to function calls instead of block retrievals to get around issues with shared IPLD ADL contexts This commit was moved from ipfs/go-path@d0512cea3d6455f4bb15837224d5d06dbab3e358 --- path/resolver/resolver.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index e42855e0c..06533f0e2 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -72,6 +72,10 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (cid. // create a selector to traverse and match all path segments pathSelector := pathAllSelector(p[:len(p)-1]) + // create a new cancellable session + ctx, cancel := context.WithTimeout(ctx, time.Minute) + defer cancel() + // resolve node before last path segment nodes, lastCid, depth, err := r.resolveNodes(ctx, c, pathSelector) if err != nil { @@ -132,6 +136,10 @@ func (r *Resolver) ResolvePath(ctx context.Context, fpath path.Path) (ipld.Node, // create a selector to traverse all path segments but only match the last pathSelector := pathLeafSelector(p) + // create a new cancellable session + ctx, cancel := context.WithTimeout(ctx, time.Minute) + defer cancel() + nodes, c, _, err := r.resolveNodes(ctx, c, pathSelector) if err != nil { return nil, nil, err @@ -172,6 +180,10 @@ func (r *Resolver) ResolvePathComponents(ctx context.Context, fpath path.Path) ( // create a selector to traverse and match all path segments pathSelector := pathAllSelector(p) + // create a new cancellable session + ctx, cancel := context.WithTimeout(ctx, time.Minute) + defer cancel() + nodes, _, _, err := r.resolveNodes(ctx, c, pathSelector) if err != nil { evt.Append(logging.LoggableMap{"error": err.Error()}) @@ -218,10 +230,6 @@ func (r *Resolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names []stri // Finds nodes matching the selector starting with a cid. Returns the matched nodes, the cid of the block containing // the last node, and the depth of the last node within its block (root is depth 0). func (r *Resolver) resolveNodes(ctx context.Context, c cid.Cid, sel ipld.Node) ([]ipld.Node, cid.Cid, int, error) { - // create a new cancellable session - ctx, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() - session := r.FetcherFactory.NewSession(ctx) // traverse selector From 3f77af4ef81785db3843c1cedb6e3f848e39dc84 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 15 Sep 2021 16:55:14 +0200 Subject: [PATCH 3571/3817] chore: update log This commit was moved from ipfs/go-blockservice@631cc327f2972b4eac7bdf265af1cfc77aa3a49f --- blockservice/blockservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 2c860aefd..6346bec5f 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -13,7 +13,7 @@ import ( cid "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" exchange "github.com/ipfs/go-ipfs-exchange-interface" - logging "github.com/ipfs/go-log" + logging "github.com/ipfs/go-log/v2" "github.com/ipfs/go-verifcid" ) From 03248629a3f8abfe8ba0e9c2058004b843656e55 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 15 Sep 2021 11:00:34 -0700 Subject: [PATCH 3572/3817] Add test script for car verify (#236) * Add test script for car verify * Add test script for filter This commit was moved from ipld/go-car@c8572be41c5bf2f90b78961cc4edcbccc25015a4 --- ipld/car/.gitattributes | 2 ++ ipld/car/cmd/car/car.go | 4 +++ ipld/car/cmd/car/filter.go | 28 ++++++++++++++++++- ipld/car/cmd/car/testdata/script/filter.txt | 30 +++++++++++++++++++++ ipld/car/cmd/car/testdata/script/verify.txt | 3 +++ ipld/car/cmd/car/verify.go | 15 +++++++++-- 6 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 ipld/car/.gitattributes create mode 100644 ipld/car/cmd/car/testdata/script/filter.txt create mode 100644 ipld/car/cmd/car/testdata/script/verify.txt diff --git a/ipld/car/.gitattributes b/ipld/car/.gitattributes new file mode 100644 index 000000000..6f9522992 --- /dev/null +++ b/ipld/car/.gitattributes @@ -0,0 +1,2 @@ +# To prevent CRLF breakages on Windows for fragile files, like testdata. +* -text diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index 12a1f1f1b..dca4c0f25 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -31,6 +31,10 @@ func main1() int { Usage: "A file to read CIDs from", TakesFile: true, }, + &cli.BoolFlag{ + Name: "append", + Usage: "Append cids to an existing output file", + }, }, }, { diff --git a/ipld/car/cmd/car/filter.go b/ipld/car/cmd/car/filter.go index a941a8b36..0a0f28937 100644 --- a/ipld/car/cmd/car/filter.go +++ b/ipld/car/cmd/car/filter.go @@ -50,10 +50,36 @@ func FilterCar(c *cli.Context) error { outRoots = append(outRoots, r) } } + + outPath := c.Args().Get(1) + if !c.Bool("append") { + if _, err := os.Stat(outPath); err == nil || !os.IsNotExist(err) { + // output to an existing file. + if err := os.Truncate(outPath, 0); err != nil { + return err + } + } + } else { + // roots will need to be whatever is in the output already. + cv2r, err := carv2.OpenReader(outPath) + if err != nil { + return err + } + if cv2r.Version != 2 { + return fmt.Errorf("can only append to version 2 car files") + } + outRoots, err = cv2r.Roots() + if err != nil { + return err + } + _ = cv2r.Close() + } + if len(outRoots) == 0 { fmt.Fprintf(os.Stderr, "warning: no roots defined after filtering\n") } - bs, err := blockstore.OpenReadWrite(c.Args().Get(1), outRoots) + + bs, err := blockstore.OpenReadWrite(outPath, outRoots) if err != nil { return err } diff --git a/ipld/car/cmd/car/testdata/script/filter.txt b/ipld/car/cmd/car/testdata/script/filter.txt new file mode 100644 index 000000000..0c0b12de7 --- /dev/null +++ b/ipld/car/cmd/car/testdata/script/filter.txt @@ -0,0 +1,30 @@ +# basic filter +stdin filteredcids.txt +car filter ${INPUTS}/sample-wrapped-v2.car out.car +stderr 'warning: no roots defined after filtering' +car list out.car +! stderr . +cmp stdout filteredcids.txt + +# filter with root CID +stdin filteredroot.txt +car filter ${INPUTS}/sample-wrapped-v2.car out.car +! stderr . +car list out.car +! stderr . +cmp stdout filteredroot.txt + +# append other cids +stdin filteredcids.txt +car filter -append ${INPUTS}/sample-wrapped-v2.car out.car +! stderr . +car list out.car +stdout -count=4 '^bafy' + + +-- filteredcids.txt -- +bafy2bzacebohz654namrgmwjjx4qmtwgxixsd7pn4tlanyrc3g3hwj75hlxrw +bafy2bzaceaqtiesyfqd2jibmofz22oolguzf5wscwh73rmeypglfu2xhkptri +bafy2bzacebct3dm7izgyauijzkaf3yd7ylni725k66rq7dfp3jr5ywhpprj3k +-- filteredroot.txt -- +bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy diff --git a/ipld/car/cmd/car/testdata/script/verify.txt b/ipld/car/cmd/car/testdata/script/verify.txt new file mode 100644 index 000000000..3ef3c49e4 --- /dev/null +++ b/ipld/car/cmd/car/testdata/script/verify.txt @@ -0,0 +1,3 @@ +# "verify" should exit with code 0 on reasonable cars. +car verify ${INPUTS}/sample-v1.car +car verify ${INPUTS}/sample-wrapped-v2.car diff --git a/ipld/car/cmd/car/verify.go b/ipld/car/cmd/car/verify.go index c9c753d78..3a43de938 100644 --- a/ipld/car/cmd/car/verify.go +++ b/ipld/car/cmd/car/verify.go @@ -8,6 +8,7 @@ import ( "github.com/ipfs/go-cid" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" + "github.com/multiformats/go-multihash" "github.com/urfave/cli/v2" ) @@ -44,12 +45,15 @@ func VerifyCar(c *cli.Context) error { if err != nil { return err } - lengthToIndex := carv2.PragmaSize + carv2.HeaderSize + rx.Header.DataOffset + rx.Header.DataSize + lengthToIndex := carv2.PragmaSize + carv2.HeaderSize + rx.Header.DataSize if uint64(flen.Size()) > lengthToIndex && rx.Header.IndexOffset == 0 { return fmt.Errorf("header claims no index, but extra bytes in file beyond data size") } + if rx.Header.DataOffset < carv2.PragmaSize+carv2.HeaderSize { + return fmt.Errorf("data offset places data within carv2 header") + } if rx.Header.IndexOffset < lengthToIndex { - return fmt.Errorf("index offset overlaps with data") + return fmt.Errorf("index offset overlaps with data. data ends at %d. index offset of %d", lengthToIndex, rx.Header.IndexOffset) } } @@ -87,6 +91,13 @@ func VerifyCar(c *cli.Context) error { return err } for _, c := range cidList { + cidHash, err := multihash.Decode(c.Hash()) + if err != nil { + return err + } + if cidHash.Code == multihash.IDENTITY { + continue + } if err := idx.GetAll(c, func(_ uint64) bool { return true }); err != nil { From 24b126977b926e0241fce8643e60086398cacaf5 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 15 Sep 2021 21:08:52 +0100 Subject: [PATCH 3573/3817] Combine API options for simplicity and logical coherence Introduce a simple `Option` type that includes both read and write options, as well as read-write options. The rationale for this change is to avoid re-declaring options that apply to both read and write, and reduce code verbosity when converting from one type to another as part of API call chains. Deprecate `ReadOption`, `WriteOption` and `ReadWriteOption` in favour of the simpler `Option`, and update APIs to use the new type. Remove now redundant internal utility functions for working with option types. Reformat code using `gofumt` utility for consistency. This commit was moved from ipld/go-car@23ca7db579f3d625be7957db11282ef5dbe64437 --- ipld/car/v2/block_reader.go | 12 +-- ipld/car/v2/blockstore/readonly.go | 30 ++++---- ipld/car/v2/blockstore/readonly_test.go | 17 ++--- ipld/car/v2/blockstore/readwrite.go | 50 +++++-------- ipld/car/v2/index/mhindexsorted.go | 6 +- ipld/car/v2/index_gen.go | 10 +-- ipld/car/v2/index_gen_test.go | 22 +++--- ipld/car/v2/internal/options/options.go | 19 ----- ipld/car/v2/options.go | 98 ++++++++++--------------- ipld/car/v2/reader.go | 11 ++- ipld/car/v2/writer.go | 18 +---- 11 files changed, 110 insertions(+), 183 deletions(-) delete mode 100644 ipld/car/v2/internal/options/options.go diff --git a/ipld/car/v2/block_reader.go b/ipld/car/v2/block_reader.go index 8cd23e72c..456d8d317 100644 --- a/ipld/car/v2/block_reader.go +++ b/ipld/car/v2/block_reader.go @@ -20,8 +20,8 @@ type BlockReader struct { Roots []cid.Cid // Used internally only, by BlockReader.Next during iteration over blocks. - r io.Reader - ropts ReadOptions + r io.Reader + opts Options } // NewBlockReader instantiates a new BlockReader facilitating iteration over blocks in CARv1 or @@ -29,7 +29,7 @@ type BlockReader struct { // BlockReader.Version. The root CIDs of the CAR payload are exposed via BlockReader.Roots // // See BlockReader.Next -func NewBlockReader(r io.Reader, opts ...ReadOption) (*BlockReader, error) { +func NewBlockReader(r io.Reader, opts ...Option) (*BlockReader, error) { // Read CARv1 header or CARv2 pragma. // Both are a valid CARv1 header, therefore are read as such. pragmaOrV1Header, err := carv1.ReadHeader(r) @@ -40,7 +40,7 @@ func NewBlockReader(r io.Reader, opts ...ReadOption) (*BlockReader, error) { // Populate the block reader version and options. br := &BlockReader{ Version: pragmaOrV1Header.Version, - ropts: ApplyReadOptions(opts...), + opts: ApplyOptions(opts...), } // Expect either version 1 or 2. @@ -116,11 +116,11 @@ func NewBlockReader(r io.Reader, opts ...ReadOption) (*BlockReader, error) { // reaches the end of the underlying io.Reader stream. // // As for CARv2 payload, the underlying io.Reader is read only up to the end of the last block. -// Note, in a case where ReadOption.ZeroLengthSectionAsEOF is enabled, io.EOF is returned +// Note, in a case where ZeroLengthSectionAsEOF Option is enabled, io.EOF is returned // immediately upon encountering a zero-length section without reading any further bytes from the // underlying io.Reader. func (br *BlockReader) Next() (blocks.Block, error) { - c, data, err := util.ReadNode(br.r, br.ropts.ZeroLengthSectionAsEOF) + c, data, err := util.ReadNode(br.r, br.opts.ZeroLengthSectionAsEOF) if err != nil { return nil, err } diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 4e7e82027..690e233b7 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -8,8 +8,6 @@ import ( "io" "sync" - "golang.org/x/exp/mmap" - blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" @@ -20,6 +18,7 @@ import ( internalio "github.com/ipld/go-car/v2/internal/io" "github.com/multiformats/go-multihash" "github.com/multiformats/go-varint" + "golang.org/x/exp/mmap" ) var _ blockstore.Blockstore = (*ReadOnly)(nil) @@ -53,7 +52,7 @@ type ReadOnly struct { // If we called carv2.NewReaderMmap, remember to close it too. carv2Closer io.Closer - ropts carv2.ReadOptions + opts carv2.Options } type contextKey string @@ -78,8 +77,8 @@ const asyncErrHandlerKey contextKey = "asyncErrorHandlerKey" // // Note that this option only affects the blockstore, and is ignored by the root // go-car/v2 package. -func UseWholeCIDs(enable bool) carv2.ReadOption { - return func(o *carv2.ReadOptions) { +func UseWholeCIDs(enable bool) carv2.Option { + return func(o *carv2.Options) { o.BlockstoreUseWholeCIDs = enable } } @@ -93,10 +92,9 @@ func UseWholeCIDs(enable bool) carv2.ReadOption { // * For a CARv2 backing an index is only generated if Header.HasIndex returns false. // // There is no need to call ReadOnly.Close on instances returned by this function. -func NewReadOnly(backing io.ReaderAt, idx index.Index, opts ...carv2.ReadOption) (*ReadOnly, error) { - ropts := carv2.ApplyReadOptions(opts...) +func NewReadOnly(backing io.ReaderAt, idx index.Index, opts ...carv2.Option) (*ReadOnly, error) { b := &ReadOnly{ - ropts: ropts, + opts: carv2.ApplyOptions(opts...), } version, err := readVersion(backing) @@ -147,7 +145,7 @@ func readVersion(at io.ReaderAt) (uint64, error) { return carv2.ReadVersion(rr) } -func generateIndex(at io.ReaderAt, opts ...carv2.ReadOption) (index.Index, error) { +func generateIndex(at io.ReaderAt, opts ...carv2.Option) (index.Index, error) { var rs io.ReadSeeker switch r := at.(type) { case io.ReadSeeker: @@ -163,7 +161,7 @@ func generateIndex(at io.ReaderAt, opts ...carv2.ReadOption) (index.Index, error // OpenReadOnly opens a read-only blockstore from a CAR file (either v1 or v2), generating an index if it does not exist. // Note, the generated index if the index does not exist is ephemeral and only stored in memory. // See car.GenerateIndex and Index.Attach for persisting index onto a CAR file. -func OpenReadOnly(path string, opts ...carv2.ReadOption) (*ReadOnly, error) { +func OpenReadOnly(path string, opts ...carv2.Option) (*ReadOnly, error) { f, err := mmap.Open(path) if err != nil { return nil, err @@ -179,7 +177,7 @@ func OpenReadOnly(path string, opts ...carv2.ReadOption) (*ReadOnly, error) { } func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) { - bcid, data, err := util.ReadNode(internalio.NewOffsetReadSeeker(b.backing, idx), b.ropts.ZeroLengthSectionAsEOF) + bcid, data, err := util.ReadNode(internalio.NewOffsetReadSeeker(b.backing, idx), b.opts.ZeroLengthSectionAsEOF) return bcid, data, err } @@ -221,7 +219,7 @@ func (b *ReadOnly) Has(key cid.Cid) (bool, error) { fnErr = err return false } - if b.ropts.BlockstoreUseWholeCIDs { + if b.opts.BlockstoreUseWholeCIDs { fnFound = readCid.Equals(key) return !fnFound // continue looking if we haven't found it } else { @@ -263,7 +261,7 @@ func (b *ReadOnly) Get(key cid.Cid) (blocks.Block, error) { fnErr = err return false } - if b.ropts.BlockstoreUseWholeCIDs { + if b.opts.BlockstoreUseWholeCIDs { if readCid.Equals(key) { fnData = data return false @@ -321,7 +319,7 @@ func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { fnErr = err return false } - if b.ropts.BlockstoreUseWholeCIDs { + if b.opts.BlockstoreUseWholeCIDs { if readCid.Equals(key) { fnSize = int(sectionLen) - cidLen return false @@ -430,7 +428,7 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // Null padding; by default it's an error. if length == 0 { - if b.ropts.ZeroLengthSectionAsEOF { + if b.opts.ZeroLengthSectionAsEOF { break } else { maybeReportError(ctx, errZeroLengthSection) @@ -450,7 +448,7 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { } // If we're just using multihashes, flatten to the "raw" codec. - if !b.ropts.BlockstoreUseWholeCIDs { + if !b.opts.BlockstoreUseWholeCIDs { c = cid.NewCidV1(cid.Raw, c.Hash()) } diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 7b87aee2a..129e6b0da 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -7,14 +7,11 @@ import ( "testing" "time" - blockstore "github.com/ipfs/go-ipfs-blockstore" - "github.com/ipfs/go-merkledag" - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" + blockstore "github.com/ipfs/go-ipfs-blockstore" + "github.com/ipfs/go-merkledag" carv2 "github.com/ipld/go-car/v2" - "github.com/ipld/go-car/v2/internal/carv1" "github.com/stretchr/testify/require" ) @@ -34,31 +31,31 @@ func TestReadOnly(t *testing.T) { tests := []struct { name string v1OrV2path string - opts []carv2.ReadOption + opts []carv2.Option v1r *carv1.CarReader }{ { "OpenedWithCarV1", "../testdata/sample-v1.car", - []carv2.ReadOption{UseWholeCIDs(true)}, + []carv2.Option{UseWholeCIDs(true)}, newV1ReaderFromV1File(t, "../testdata/sample-v1.car", false), }, { "OpenedWithCarV2", "../testdata/sample-wrapped-v2.car", - []carv2.ReadOption{UseWholeCIDs(true)}, + []carv2.Option{UseWholeCIDs(true)}, newV1ReaderFromV2File(t, "../testdata/sample-wrapped-v2.car", false), }, { "OpenedWithCarV1ZeroLenSection", "../testdata/sample-v1-with-zero-len-section.car", - []carv2.ReadOption{UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, + []carv2.Option{UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, newV1ReaderFromV1File(t, "../testdata/sample-v1-with-zero-len-section.car", true), }, { "OpenedWithAnotherCarV1ZeroLenSection", "../testdata/sample-v1-with-zero-len-section2.car", - []carv2.ReadOption{UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, + []carv2.Option{UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, newV1ReaderFromV1File(t, "../testdata/sample-v1-with-zero-len-section2.car", true), }, } diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 7b14b55f9..4b3119087 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -7,18 +7,15 @@ import ( "io" "os" - "github.com/ipld/go-car/v2/internal/carv1" - "github.com/multiformats/go-varint" - + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" carv2 "github.com/ipld/go-car/v2" - internalio "github.com/ipld/go-car/v2/internal/io" - "github.com/ipld/go-car/v2/index" - - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" + "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/carv1/util" + internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/multiformats/go-varint" ) var _ blockstore.Blockstore = (*ReadWrite)(nil) @@ -40,7 +37,7 @@ type ReadWrite struct { idx *insertionIndex header carv2.Header - wopts carv2.WriteOptions + opts carv2.Options } // AllowDuplicatePuts is a write option which makes a CAR blockstore not @@ -49,8 +46,8 @@ type ReadWrite struct { // // Note that this option only affects the blockstore, and is ignored by the root // go-car/v2 package. -func AllowDuplicatePuts(allow bool) carv2.WriteOption { - return func(o *carv2.WriteOptions) { +func AllowDuplicatePuts(allow bool) carv2.Option { + return func(o *carv2.Options) { o.BlockstoreAllowDuplicatePuts = allow } } @@ -89,7 +86,7 @@ func AllowDuplicatePuts(allow bool) carv2.WriteOption { // // Resuming from finalized files is allowed. However, resumption will regenerate the index // regardless by scanning every existing block in file. -func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.ReadWriteOption) (*ReadWrite, error) { +func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.Option) (*ReadWrite, error) { f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0o666) // TODO: Should the user be able to configure FileMode permissions? if err != nil { return nil, fmt.Errorf("could not open read/write file: %w", err) @@ -114,25 +111,14 @@ func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.ReadWriteOption) f: f, idx: newInsertionIndex(), header: carv2.NewHeader(0), + opts: carv2.ApplyOptions(opts...), } + rwbs.ronly.opts = rwbs.opts - var ro []carv2.ReadOption - var wo []carv2.WriteOption - for _, opt := range opts { - switch opt := opt.(type) { - case carv2.ReadOption: - ro = append(ro, opt) - case carv2.WriteOption: - wo = append(wo, opt) - } - } - rwbs.ronly.ropts = carv2.ApplyReadOptions(ro...) - rwbs.wopts = carv2.ApplyWriteOptions(wo...) - - if p := rwbs.wopts.DataPadding; p > 0 { + if p := rwbs.opts.DataPadding; p > 0 { rwbs.header = rwbs.header.WithDataPadding(p) } - if p := rwbs.wopts.IndexPadding; p > 0 { + if p := rwbs.opts.IndexPadding; p > 0 { rwbs.header = rwbs.header.WithIndexPadding(p) } @@ -260,7 +246,7 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { // Null padding; by default it's an error. if length == 0 { - if b.ronly.ropts.ZeroLengthSectionAsEOF { + if b.ronly.opts.ZeroLengthSectionAsEOF { break } else { return fmt.Errorf("carv1 null padding not allowed by default; see WithZeroLegthSectionAsEOF") @@ -316,11 +302,11 @@ func (b *ReadWrite) PutMany(blks []blocks.Block) error { continue } - if !b.wopts.BlockstoreAllowDuplicatePuts { - if b.ronly.ropts.BlockstoreUseWholeCIDs && b.idx.hasExactCID(c) { + if !b.opts.BlockstoreAllowDuplicatePuts { + if b.ronly.opts.BlockstoreUseWholeCIDs && b.idx.hasExactCID(c) { continue // deduplicated by CID } - if !b.ronly.ropts.BlockstoreUseWholeCIDs { + if !b.ronly.opts.BlockstoreUseWholeCIDs { _, err := b.idx.Get(c) if err == nil { continue // deduplicated by hash @@ -371,7 +357,7 @@ func (b *ReadWrite) Finalize() error { defer b.ronly.closeWithoutMutex() // TODO if index not needed don't bother flattening it. - fi, err := b.idx.flatten(b.wopts.IndexCodec) + fi, err := b.idx.flatten(b.opts.IndexCodec) if err != nil { return err } diff --git a/ipld/car/v2/index/mhindexsorted.go b/ipld/car/v2/index/mhindexsorted.go index 15a731c80..6ae2c5687 100644 --- a/ipld/car/v2/index/mhindexsorted.go +++ b/ipld/car/v2/index/mhindexsorted.go @@ -10,8 +10,10 @@ import ( "github.com/multiformats/go-multihash" ) -var _ Index = (*MultihashIndexSorted)(nil) -var _ IterableIndex = (*MultihashIndexSorted)(nil) +var ( + _ Index = (*MultihashIndexSorted)(nil) + _ IterableIndex = (*MultihashIndexSorted)(nil) +) type ( // MultihashIndexSorted maps multihash code (i.e. hashing algorithm) to multiWidthCodedIndex. diff --git a/ipld/car/v2/index_gen.go b/ipld/car/v2/index_gen.go index 91ebb5891..4602add56 100644 --- a/ipld/car/v2/index_gen.go +++ b/ipld/car/v2/index_gen.go @@ -16,7 +16,7 @@ import ( // The generated index will be in multicodec.CarMultihashIndexSorted, the default index codec. // The index can be stored in serialized format using index.WriteTo. // See LoadIndex. -func GenerateIndex(v1r io.Reader, opts ...ReadOption) (index.Index, error) { +func GenerateIndex(v1r io.Reader, opts ...Option) (index.Index, error) { idx := index.NewMultihashSorted() if err := LoadIndex(idx, v1r, opts...); err != nil { return nil, err @@ -26,7 +26,7 @@ func GenerateIndex(v1r io.Reader, opts ...ReadOption) (index.Index, error) { // LoadIndex populates idx with index records generated from v1r. // The v1r must be data payload in CARv1 format. -func LoadIndex(idx index.Index, v1r io.Reader, opts ...ReadOption) error { +func LoadIndex(idx index.Index, v1r io.Reader, opts ...Option) error { reader := internalio.ToByteReadSeeker(v1r) header, err := carv1.ReadHeader(reader) if err != nil { @@ -38,7 +38,7 @@ func LoadIndex(idx index.Index, v1r io.Reader, opts ...ReadOption) error { } // Parse Options. - ropts := ApplyReadOptions(opts...) + o := ApplyOptions(opts...) // Record the start of each section, with first section starring from current position in the // reader, i.e. right after the header, since we have only read the header so far. @@ -64,7 +64,7 @@ func LoadIndex(idx index.Index, v1r io.Reader, opts ...ReadOption) error { // Null padding; by default it's an error. if sectionLen == 0 { - if ropts.ZeroLengthSectionAsEOF { + if o.ZeroLengthSectionAsEOF { break } else { return fmt.Errorf("carv1 null padding not allowed by default; see ZeroLengthSectionAsEOF") @@ -112,7 +112,7 @@ func GenerateIndexFromFile(path string) (index.Index, error) { // // Note, the returned index lives entirely in memory and will not depend on the // given reader to fulfill index lookup. -func ReadOrGenerateIndex(rs io.ReadSeeker, opts ...ReadOption) (index.Index, error) { +func ReadOrGenerateIndex(rs io.ReadSeeker, opts ...Option) (index.Index, error) { // Read version. version, err := ReadVersion(rs) if err != nil { diff --git a/ipld/car/v2/index_gen_test.go b/ipld/car/v2/index_gen_test.go index 601fb8cb5..529fb9137 100644 --- a/ipld/car/v2/index_gen_test.go +++ b/ipld/car/v2/index_gen_test.go @@ -5,16 +5,14 @@ import ( "os" "testing" - "github.com/multiformats/go-multihash" - "github.com/ipfs/go-cid" carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1" internalio "github.com/ipld/go-car/v2/internal/io" "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-multihash" "github.com/multiformats/go-varint" - - "github.com/ipld/go-car/v2/index" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -23,14 +21,14 @@ func TestReadOrGenerateIndex(t *testing.T) { tests := []struct { name string carPath string - readOpts []carv2.ReadOption + opts []carv2.Option wantIndexer func(t *testing.T) index.Index wantErr bool }{ { "CarV1IsIndexedAsExpected", "testdata/sample-v1.car", - []carv2.ReadOption{}, + []carv2.Option{}, func(t *testing.T) index.Index { v1, err := os.Open("testdata/sample-v1.car") require.NoError(t, err) @@ -44,7 +42,7 @@ func TestReadOrGenerateIndex(t *testing.T) { { "CarV2WithIndexIsReturnedAsExpected", "testdata/sample-wrapped-v2.car", - []carv2.ReadOption{}, + []carv2.Option{}, func(t *testing.T) index.Index { v2, err := os.Open("testdata/sample-wrapped-v2.car") require.NoError(t, err) @@ -60,7 +58,7 @@ func TestReadOrGenerateIndex(t *testing.T) { { "CarV1WithZeroLenSectionIsGeneratedAsExpected", "testdata/sample-v1-with-zero-len-section.car", - []carv2.ReadOption{carv2.ZeroLengthSectionAsEOF(true)}, + []carv2.Option{carv2.ZeroLengthSectionAsEOF(true)}, func(t *testing.T) index.Index { v1, err := os.Open("testdata/sample-v1-with-zero-len-section.car") require.NoError(t, err) @@ -74,7 +72,7 @@ func TestReadOrGenerateIndex(t *testing.T) { { "AnotherCarV1WithZeroLenSectionIsGeneratedAsExpected", "testdata/sample-v1-with-zero-len-section2.car", - []carv2.ReadOption{carv2.ZeroLengthSectionAsEOF(true)}, + []carv2.Option{carv2.ZeroLengthSectionAsEOF(true)}, func(t *testing.T) index.Index { v1, err := os.Open("testdata/sample-v1-with-zero-len-section2.car") require.NoError(t, err) @@ -88,14 +86,14 @@ func TestReadOrGenerateIndex(t *testing.T) { { "CarV1WithZeroLenSectionWithoutOptionIsError", "testdata/sample-v1-with-zero-len-section.car", - []carv2.ReadOption{}, + []carv2.Option{}, func(t *testing.T) index.Index { return nil }, true, }, { "CarOtherThanV1OrV2IsError", "testdata/sample-rootless-v42.car", - []carv2.ReadOption{}, + []carv2.Option{}, func(t *testing.T) index.Index { return nil }, true, }, @@ -105,7 +103,7 @@ func TestReadOrGenerateIndex(t *testing.T) { carFile, err := os.Open(tt.carPath) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, carFile.Close()) }) - got, err := carv2.ReadOrGenerateIndex(carFile, tt.readOpts...) + got, err := carv2.ReadOrGenerateIndex(carFile, tt.opts...) if tt.wantErr { require.Error(t, err) } else { diff --git a/ipld/car/v2/internal/options/options.go b/ipld/car/v2/internal/options/options.go deleted file mode 100644 index 3add2b5a4..000000000 --- a/ipld/car/v2/internal/options/options.go +++ /dev/null @@ -1,19 +0,0 @@ -// Package options provides utilities to work with ReadOption and WriteOption used internally. -package options - -import carv2 "github.com/ipld/go-car/v2" - -// SplitReadWriteOptions splits the rwopts by type into ReadOption and WriteOption slices. -func SplitReadWriteOptions(rwopts ...carv2.ReadWriteOption) ([]carv2.ReadOption, []carv2.WriteOption) { - var ropts []carv2.ReadOption - var wopts []carv2.WriteOption - for _, opt := range rwopts { - switch opt := opt.(type) { - case carv2.ReadOption: - ropts = append(ropts, opt) - case carv2.WriteOption: - wopts = append(wopts, opt) - } - } - return ropts, wopts -} diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go index ed6502cc9..ad554a79c 100644 --- a/ipld/car/v2/options.go +++ b/ipld/car/v2/options.go @@ -2,51 +2,41 @@ package car import "github.com/multiformats/go-multicodec" -// ReadOptions holds the configured options after applying a number of -// ReadOption funcs. -// -// This type should not be used directly by end users; it's only exposed as a -// side effect of ReadOption. -type ReadOptions struct { - ZeroLengthSectionAsEOF bool - - BlockstoreUseWholeCIDs bool -} - -// ApplyReadOptions applies ropts and returns the resulting ReadOptions. -func ApplyReadOptions(ropts ...ReadOption) ReadOptions { - var opts ReadOptions - for _, opt := range ropts { - opt(&opts) - } - return opts -} +// Option describes an option which affects behavior when interacting with CAR files. +type Option func(*Options) -// ReadOption describes an option which affects behavior when parsing CAR files. -type ReadOption func(*ReadOptions) +// ReadOption hints that an API wants options related only to reading CAR files. +type ReadOption = Option -func (ReadOption) readWriteOption() {} +// WriteOption hints that an API wants options related only to reading CAR files. +type WriteOption = Option -var _ ReadWriteOption = ReadOption(nil) +// ReadWriteOption is either a ReadOption or a WriteOption. +// Deprecated: use Option instead. +type ReadWriteOption = Option -// WriteOptions holds the configured options after applying a number of -// WriteOption funcs. +// Options holds the configured options after applying a number of +// Option funcs. // // This type should not be used directly by end users; it's only exposed as a -// side effect of WriteOption. -type WriteOptions struct { - DataPadding uint64 - IndexPadding uint64 - IndexCodec multicodec.Code +// side effect of Option. +type Options struct { + DataPadding uint64 + IndexPadding uint64 + IndexCodec multicodec.Code + ZeroLengthSectionAsEOF bool BlockstoreAllowDuplicatePuts bool + BlockstoreUseWholeCIDs bool } -// ApplyWriteOptions applies the given ropts and returns the resulting WriteOptions. -func ApplyWriteOptions(ropts ...WriteOption) WriteOptions { - var opts WriteOptions - for _, opt := range ropts { - opt(&opts) +// ApplyOptions applies given opts and returns the resulting Options. +// This function should not be used directly by end users; it's only exposed as a +// side effect of Option. +func ApplyOptions(opt ...Option) Options { + var opts Options + for _, o := range opt { + o(&opts) } // Set defaults for zero valued fields. if opts.IndexCodec == 0 { @@ -55,47 +45,33 @@ func ApplyWriteOptions(ropts ...WriteOption) WriteOptions { return opts } -// WriteOption describes an option which affects behavior when encoding CAR files. -type WriteOption func(*WriteOptions) - -func (WriteOption) readWriteOption() {} - -var _ ReadWriteOption = WriteOption(nil) - -// ReadWriteOption is either a ReadOption or a WriteOption. -type ReadWriteOption interface { - readWriteOption() -} - -// ZeroLengthSectionAsEOF is a read option which allows a CARv1 decoder to treat +// ZeroLengthSectionAsEOF sets whether to allow the CARv1 decoder to treat // a zero-length section as the end of the input CAR file. For example, this can // be useful to allow "null padding" after a CARv1 without knowing where the // padding begins. -func ZeroLengthSectionAsEOF(enable bool) ReadOption { - return func(o *ReadOptions) { +func ZeroLengthSectionAsEOF(enable bool) Option { + return func(o *Options) { o.ZeroLengthSectionAsEOF = enable } } -// UseDataPadding is a write option which sets the padding to be added between -// CARv2 header and its data payload on Finalize. -func UseDataPadding(p uint64) WriteOption { - return func(o *WriteOptions) { +// UseDataPadding sets the padding to be added between CARv2 header and its data payload on Finalize. +func UseDataPadding(p uint64) Option { + return func(o *Options) { o.DataPadding = p } } -// UseIndexPadding is a write option which sets the padding between data payload -// and its index on Finalize. -func UseIndexPadding(p uint64) WriteOption { - return func(o *WriteOptions) { +// UseIndexPadding sets the padding between data payload and its index on Finalize. +func UseIndexPadding(p uint64) Option { + return func(o *Options) { o.IndexPadding = p } } -// UseIndexCodec is a write option which sets the codec used for index generation. -func UseIndexCodec(c multicodec.Code) WriteOption { - return func(o *WriteOptions) { +// UseIndexCodec sets the codec used for index generation. +func UseIndexCodec(c multicodec.Code) Option { + return func(o *Options) { o.IndexCodec = c } } diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index b9742ec72..9394a736b 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -4,10 +4,9 @@ import ( "fmt" "io" - internalio "github.com/ipld/go-car/v2/internal/io" - "github.com/ipfs/go-cid" "github.com/ipld/go-car/v2/internal/carv1" + internalio "github.com/ipld/go-car/v2/internal/io" "golang.org/x/exp/mmap" ) @@ -17,12 +16,12 @@ type Reader struct { Version uint64 r io.ReaderAt roots []cid.Cid - ropts ReadOptions + opts Options closer io.Closer } // OpenReader is a wrapper for NewReader which opens the file at path. -func OpenReader(path string, opts ...ReadOption) (*Reader, error) { +func OpenReader(path string, opts ...Option) (*Reader, error) { f, err := mmap.Open(path) if err != nil { return nil, err @@ -44,11 +43,11 @@ func OpenReader(path string, opts ...ReadOption) (*Reader, error) { // Note that any other version other than 1 or 2 will result in an error. The caller may use // Reader.Version to get the actual version r represents. In the case where r represents a CARv1 // Reader.Header will not be populated and is left as zero-valued. -func NewReader(r io.ReaderAt, opts ...ReadOption) (*Reader, error) { +func NewReader(r io.ReaderAt, opts ...Option) (*Reader, error) { cr := &Reader{ r: r, } - cr.ropts = ApplyReadOptions(opts...) + cr.opts = ApplyOptions(opts...) or := internalio.NewOffsetReadSeeker(r, 0) var err error diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index 2a22414db..8ed5756fe 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -46,27 +46,17 @@ func WrapV1File(srcPath, dstPath string) error { // WrapV1 takes a CARv1 file and wraps it as a CARv2 file with an index. // The resulting CARv2 file's inner CARv1 payload is left unmodified, // and does not use any padding before the innner CARv1 or index. -func WrapV1(src io.ReadSeeker, dst io.Writer, opts ...ReadWriteOption) error { +func WrapV1(src io.ReadSeeker, dst io.Writer, opts ...Option) error { // TODO: verify src is indeed a CARv1 to prevent misuse. // GenerateIndex should probably be in charge of that. - var ro []ReadOption - var wo []WriteOption - for _, opt := range opts { - switch opt := opt.(type) { - case ReadOption: - ro = append(ro, opt) - case WriteOption: - wo = append(wo, opt) - } - } - wopts := ApplyWriteOptions(wo...) - idx, err := index.New(wopts.IndexCodec) + o := ApplyOptions(opts...) + idx, err := index.New(o.IndexCodec) if err != nil { return err } - if err := LoadIndex(idx, src, ro...); err != nil { + if err := LoadIndex(idx, src, opts...); err != nil { return err } From fb4eba3870533c15c6aaab79218c45a0ee848620 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Fri, 17 Sep 2021 21:56:56 +0100 Subject: [PATCH 3574/3817] Implement options to handle `IDENTITY` CIDs gracefully Implement two additional options that allow a CARv2 file to 1) include IDENTNTIY CIDs, and 2) specify a maximum allowed CID length with default of 2KiB as a sufficiently large default. Configure ReadWrite blockstore to persist given blocks with IDENTITY CIDs. Introduce a new Characteristics filed that signalls whether an index in a CAR file contains a full catalog of CIDs for backward compatibility purposes. Note, this is a new addition and will need to be added to the spec in a separate PR. Relates to #215 This commit was moved from ipld/go-car@f35d88ce16ca85297c9d2ca6cf23022ed8b7aec6 --- ipld/car/v2/blockstore/doc.go | 2 +- ipld/car/v2/blockstore/readonly_test.go | 20 +-- ipld/car/v2/blockstore/readwrite.go | 23 ++- ipld/car/v2/blockstore/readwrite_test.go | 181 +++++++++++++++++++++-- ipld/car/v2/car.go | 34 +++++ ipld/car/v2/car_test.go | 43 ++++++ ipld/car/v2/errors.go | 18 +++ ipld/car/v2/errors_test.go | 12 ++ ipld/car/v2/index/index.go | 25 +++- ipld/car/v2/index/indexsorted.go | 6 - ipld/car/v2/index/indexsorted_test.go | 42 ------ ipld/car/v2/index/mhindexsorted.go | 5 - ipld/car/v2/index/mhindexsorted_test.go | 25 ---- ipld/car/v2/index_gen.go | 18 ++- ipld/car/v2/options.go | 25 ++++ ipld/car/v2/options_test.go | 41 +++++ 16 files changed, 408 insertions(+), 112 deletions(-) create mode 100644 ipld/car/v2/errors.go create mode 100644 ipld/car/v2/errors_test.go create mode 100644 ipld/car/v2/options_test.go diff --git a/ipld/car/v2/blockstore/doc.go b/ipld/car/v2/blockstore/doc.go index 6b96b7a6e..479442e12 100644 --- a/ipld/car/v2/blockstore/doc.go +++ b/ipld/car/v2/blockstore/doc.go @@ -22,7 +22,7 @@ // * blockstore.Has will always return true. // * blockstore.Get will always succeed, returning the multihash digest of the given CID. // * blockstore.GetSize will always succeed, returning the multihash digest length of the given CID. -// * blockstore.Put and blockstore.PutMany will always succeed without performing any operation. +// * blockstore.Put and blockstore.PutMany will always succeed without performing any operation unless car.StoreIdentityCIDs is enabled. // // See: https://pkg.go.dev/github.com/ipfs/go-ipfs-blockstore#NewIdStore package blockstore diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 129e6b0da..a242abd8c 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -32,48 +32,50 @@ func TestReadOnly(t *testing.T) { name string v1OrV2path string opts []carv2.Option - v1r *carv1.CarReader }{ { "OpenedWithCarV1", "../testdata/sample-v1.car", - []carv2.Option{UseWholeCIDs(true)}, - newV1ReaderFromV1File(t, "../testdata/sample-v1.car", false), + []carv2.Option{UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, }, { "OpenedWithCarV2", "../testdata/sample-wrapped-v2.car", - []carv2.Option{UseWholeCIDs(true)}, - newV1ReaderFromV2File(t, "../testdata/sample-wrapped-v2.car", false), + []carv2.Option{UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, }, { "OpenedWithCarV1ZeroLenSection", "../testdata/sample-v1-with-zero-len-section.car", []carv2.Option{UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, - newV1ReaderFromV1File(t, "../testdata/sample-v1-with-zero-len-section.car", true), }, { "OpenedWithAnotherCarV1ZeroLenSection", "../testdata/sample-v1-with-zero-len-section2.car", []carv2.Option{UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, - newV1ReaderFromV1File(t, "../testdata/sample-v1-with-zero-len-section2.car", true), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { subject, err := OpenReadOnly(tt.v1OrV2path, tt.opts...) + require.NoError(t, err) t.Cleanup(func() { require.NoError(t, subject.Close()) }) + + f, err := os.Open(tt.v1OrV2path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, f.Close()) }) + + reader, err := carv2.NewBlockReader(f, tt.opts...) require.NoError(t, err) // Assert roots match v1 payload. - wantRoots := tt.v1r.Header.Roots + wantRoots := reader.Roots gotRoots, err := subject.Roots() require.NoError(t, err) require.Equal(t, wantRoots, gotRoots) var wantCids []cid.Cid for { - wantBlock, err := tt.v1r.Next() + wantBlock, err := reader.Next() if err == io.EOF { break } diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 4b3119087..b98c58d4b 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -295,11 +295,23 @@ func (b *ReadWrite) PutMany(blks []blocks.Block) error { for _, bl := range blks { c := bl.Cid() - // Check for IDENTITY CID. If IDENTITY, ignore and move to the next block. - if _, ok, err := isIdentity(c); err != nil { - return err - } else if ok { - continue + // If StoreIdentityCIDs option is disabled then treat IDENTITY CIDs like IdStore. + if !b.opts.StoreIdentityCIDs { + // Check for IDENTITY CID. If IDENTITY, ignore and move to the next block. + if _, ok, err := isIdentity(c); err != nil { + return err + } else if ok { + continue + } + } + + // Check if its size is too big. + // If larger than maximum allowed size, return error. + // Note, we need to check this regardless of whether we have IDENTITY CID or not. + // Since multhihash codes other than IDENTITY can result in large digests. + cSize := uint64(len(c.Bytes())) + if cSize > b.opts.MaxIndexCidSize { + return &carv2.ErrCidTooLarge{MaxSize: b.opts.MaxIndexCidSize, CurrentSize: cSize} } if !b.opts.BlockstoreAllowDuplicatePuts { @@ -351,6 +363,7 @@ func (b *ReadWrite) Finalize() error { // TODO check if add index option is set and don't write the index then set index offset to zero. b.header = b.header.WithDataSize(uint64(b.dataWriter.Position())) + b.header.Characteristics.SetFullyIndexed(b.opts.StoreIdentityCIDs) // Note that we can't use b.Close here, as that tries to grab the same // mutex we're holding here. diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index cfdad4642..b7da6ec83 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -2,6 +2,7 @@ package blockstore_test import ( "context" + "crypto/sha512" "fmt" "io" "io/ioutil" @@ -12,21 +13,19 @@ import ( "testing" "time" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + ipfsblockstore "github.com/ipfs/go-ipfs-blockstore" + cbor "github.com/ipfs/go-ipld-cbor" "github.com/ipfs/go-merkledag" - carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/blockstore" "github.com/ipld/go-car/v2/index" - "github.com/stretchr/testify/assert" - + "github.com/ipld/go-car/v2/internal/carv1" + "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - ipfsblockstore "github.com/ipfs/go-ipfs-blockstore" - "github.com/ipld/go-car/v2/blockstore" - - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" - "github.com/ipld/go-car/v2/internal/carv1" ) var ( @@ -688,3 +687,165 @@ func TestReadWriteErrorAfterClose(t *testing.T) { // in progress. } } + +func TestOpenReadWrite_WritesIdentityCIDsWhenOptionIsEnabled(t *testing.T) { + path := filepath.Join(t.TempDir(), "readwrite-with-id-enabled.car") + subject, err := blockstore.OpenReadWrite(path, []cid.Cid{}, carv2.StoreIdentityCIDs(true)) + require.NoError(t, err) + + data := []byte("fish") + idmh, err := multihash.Sum(data, multihash.IDENTITY, -1) + require.NoError(t, err) + idCid := cid.NewCidV1(uint64(multicodec.Raw), idmh) + + idBlock, err := blocks.NewBlockWithCid(data, idCid) + require.NoError(t, err) + err = subject.Put(idBlock) + require.NoError(t, err) + + has, err := subject.Has(idCid) + require.NoError(t, err) + require.True(t, has) + + gotBlock, err := subject.Get(idCid) + require.NoError(t, err) + require.Equal(t, idBlock, gotBlock) + + keysChan, err := subject.AllKeysChan(context.Background()) + require.NoError(t, err) + var i int + for c := range keysChan { + i++ + require.Equal(t, idCid, c) + } + require.Equal(t, 1, i) + + err = subject.Finalize() + require.NoError(t, err) + + // Assert resulting CAR file indeed has the IDENTITY block. + f, err := os.Open(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, f.Close()) }) + + reader, err := carv2.NewBlockReader(f) + require.NoError(t, err) + + gotBlock, err = reader.Next() + require.NoError(t, err) + require.Equal(t, idBlock, gotBlock) + + next, err := reader.Next() + require.Equal(t, io.EOF, err) + require.Nil(t, next) + + // Assert the id is indexed. + r, err := carv2.OpenReader(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, r.Close()) }) + require.True(t, r.Header.HasIndex()) + + ir := r.IndexReader() + require.NotNil(t, ir) + + gotIdx, err := index.ReadFrom(ir) + require.NoError(t, err) + + // Determine expected offset as the length of header plus one + header, err := carv1.ReadHeader(r.DataReader()) + require.NoError(t, err) + object, err := cbor.DumpObject(header) + require.NoError(t, err) + expectedOffset := len(object) + 1 + + // Assert index is iterable and has exactly one record with expected multihash and offset. + switch idx := gotIdx.(type) { + case index.IterableIndex: + var i int + err := idx.ForEach(func(mh multihash.Multihash, offset uint64) error { + i++ + require.Equal(t, idmh, mh) + require.Equal(t, uint64(expectedOffset), offset) + return nil + }) + require.NoError(t, err) + require.Equal(t, 1, i) + default: + require.Failf(t, "unexpected index type", "wanted %v but got %v", multicodec.CarMultihashIndexSorted, idx.Codec()) + } +} + +func TestOpenReadWrite_ErrorsWhenWritingTooLargeOfACid(t *testing.T) { + maxAllowedCidSize := uint64(2) + path := filepath.Join(t.TempDir(), "readwrite-with-id-enabled-too-large.car") + subject, err := blockstore.OpenReadWrite(path, []cid.Cid{}, carv2.MaxIndexCidSize(maxAllowedCidSize)) + t.Cleanup(subject.Discard) + require.NoError(t, err) + + data := []byte("monsterlobster") + mh, err := multihash.Sum(data, multihash.SHA2_256, -1) + require.NoError(t, err) + bigCid := cid.NewCidV1(uint64(multicodec.Raw), mh) + bigCidLen := uint64(bigCid.ByteLen()) + require.True(t, bigCidLen > maxAllowedCidSize) + + bigBlock, err := blocks.NewBlockWithCid(data, bigCid) + require.NoError(t, err) + err = subject.Put(bigBlock) + require.Equal(t, &carv2.ErrCidTooLarge{MaxSize: maxAllowedCidSize, CurrentSize: bigCidLen}, err) +} + +func TestReadWrite_ReWritingCARv1WithIdentityCidIsIdenticalToOriginalWithOptionsEnabled(t *testing.T) { + originalCARv1Path := "../testdata/sample-v1.car" + originalCarV1, err := os.Open(originalCARv1Path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, originalCarV1.Close()) }) + + r, err := carv2.NewBlockReader(originalCarV1) + require.NoError(t, err) + + path := filepath.Join(t.TempDir(), "readwrite-from-carv1-with-id-enabled.car") + subject, err := blockstore.OpenReadWrite(path, r.Roots, carv2.StoreIdentityCIDs(true)) + require.NoError(t, err) + var idCidCount int + for { + next, err := r.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + if next.Cid().Prefix().MhType == multihash.IDENTITY { + idCidCount++ + } + err = subject.Put(next) + require.NoError(t, err) + } + require.NotZero(t, idCidCount) + err = subject.Finalize() + require.NoError(t, err) + + v2r, err := carv2.OpenReader(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, v2r.Close()) }) + + // Assert characteristics bit is set. + require.True(t, v2r.Header.Characteristics.IsFullyIndexed()) + + // Assert original CARv1 and generated innter CARv1 payload have the same SHA512 hash + // Note, we hash instead of comparing bytes to avoid excessive memory usage when sample CARv1 is large. + + hasher := sha512.New() + gotWritten, err := io.Copy(hasher, v2r.DataReader()) + require.NoError(t, err) + gotSum := hasher.Sum(nil) + + hasher.Reset() + _, err = originalCarV1.Seek(0, io.SeekStart) + require.NoError(t, err) + wantWritten, err := io.Copy(hasher, originalCarV1) + require.NoError(t, err) + wantSum := hasher.Sum(nil) + + require.Equal(t, wantWritten, gotWritten) + require.Equal(t, wantSum, gotSum) +} diff --git a/ipld/car/v2/car.go b/ipld/car/v2/car.go index d12683313..f2885d9d6 100644 --- a/ipld/car/v2/car.go +++ b/ipld/car/v2/car.go @@ -43,6 +43,9 @@ type ( } ) +// fullyIndexedCharPos is the position of Characteristics.Hi bit that specifies whether the index is a catalog af all CIDs or not. +const fullyIndexedCharPos = 7 // left-most bit + // WriteTo writes this characteristics to the given w. func (c Characteristics) WriteTo(w io.Writer) (n int64, err error) { buf := make([]byte, 16) @@ -64,6 +67,37 @@ func (c *Characteristics) ReadFrom(r io.Reader) (int64, error) { return n, nil } +// IsFullyIndexed specifies whether the index of CARv2 represents a catalog of all CID segments. +// See StoreIdentityCIDs +func (c *Characteristics) IsFullyIndexed() bool { + return isBitSet(c.Hi, fullyIndexedCharPos) +} + +// SetFullyIndexed sets whether of CARv2 represents a catalog of all CID segments. +func (c *Characteristics) SetFullyIndexed(b bool) { + if b { + c.Hi = setBit(c.Hi, fullyIndexedCharPos) + } else { + c.Hi = unsetBit(c.Hi, fullyIndexedCharPos) + } +} + +func setBit(n uint64, pos uint) uint64 { + n |= 1 << pos + return n +} + +func unsetBit(n uint64, pos uint) uint64 { + mask := uint64(^(1 << pos)) + n &= mask + return n +} + +func isBitSet(n uint64, pos uint) bool { + bit := n & (1 << pos) + return bit > 0 +} + // NewHeader instantiates a new CARv2 header, given the data size. func NewHeader(dataSize uint64) Header { header := Header{ diff --git a/ipld/car/v2/car_test.go b/ipld/car/v2/car_test.go index 4223a5600..64519b297 100644 --- a/ipld/car/v2/car_test.go +++ b/ipld/car/v2/car_test.go @@ -4,6 +4,8 @@ import ( "bytes" "testing" + "github.com/stretchr/testify/require" + carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/internal/carv1" "github.com/stretchr/testify/assert" @@ -199,3 +201,44 @@ func TestNewHeaderHasExpectedValues(t *testing.T) { got := carv2.NewHeader(wantCarV1Len) assert.Equal(t, want, got, "NewHeader got = %v, want = %v", got, want) } + +func TestCharacteristics_StoreIdentityCIDs(t *testing.T) { + subject := carv2.Characteristics{} + require.False(t, subject.IsFullyIndexed()) + + subject.SetFullyIndexed(true) + require.True(t, subject.IsFullyIndexed()) + + var buf bytes.Buffer + written, err := subject.WriteTo(&buf) + require.NoError(t, err) + require.Equal(t, int64(16), written) + require.Equal(t, []byte{ + 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, buf.Bytes()) + + var decodedSubject carv2.Characteristics + read, err := decodedSubject.ReadFrom(&buf) + require.NoError(t, err) + require.Equal(t, int64(16), read) + require.True(t, decodedSubject.IsFullyIndexed()) + + buf.Reset() + subject.SetFullyIndexed(false) + require.False(t, subject.IsFullyIndexed()) + + written, err = subject.WriteTo(&buf) + require.NoError(t, err) + require.Equal(t, int64(16), written) + require.Equal(t, []byte{ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, buf.Bytes()) + + var decodedSubjectAgain carv2.Characteristics + read, err = decodedSubjectAgain.ReadFrom(&buf) + require.NoError(t, err) + require.Equal(t, int64(16), read) + require.False(t, decodedSubjectAgain.IsFullyIndexed()) +} diff --git a/ipld/car/v2/errors.go b/ipld/car/v2/errors.go new file mode 100644 index 000000000..ee89e0b25 --- /dev/null +++ b/ipld/car/v2/errors.go @@ -0,0 +1,18 @@ +package car + +import ( + "fmt" +) + +var _ (error) = (*ErrCidTooLarge)(nil) + +// ErrCidTooLarge signals that a CID is too large to include in CARv2 index. +// See: MaxIndexCidSize. +type ErrCidTooLarge struct { + MaxSize uint64 + CurrentSize uint64 +} + +func (e *ErrCidTooLarge) Error() string { + return fmt.Sprintf("cid size is larger than max allowed (%d > %d)", e.CurrentSize, e.MaxSize) +} diff --git a/ipld/car/v2/errors_test.go b/ipld/car/v2/errors_test.go new file mode 100644 index 000000000..56e2c7a09 --- /dev/null +++ b/ipld/car/v2/errors_test.go @@ -0,0 +1,12 @@ +package car + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewErrCidTooLarge_ErrorContainsSizes(t *testing.T) { + subject := &ErrCidTooLarge{MaxSize: 1413, CurrentSize: 1414} + require.EqualError(t, subject, "cid size is larger than max allowed (1414 > 1413)") +} diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 8abd1d3a3..8e447d0fe 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -31,9 +31,7 @@ type ( // implementations might index the entire CID, the entire multihash, or // just part of a multihash's digest. // - // In accordance with the CARv2 specification, Index will never contain information about CIDs - // with multihash.IDENTITY code. - // See: https://ipld.io/specs/transport/car/carv2/#index-format + // See: multicodec.CarIndexSorted, multicodec.CarMultihashIndexSorted Index interface { // Codec provides the multicodec code that the index implements. // @@ -47,12 +45,19 @@ type ( Unmarshal(r io.Reader) error // Load inserts a number of records into the index. + // Note that Index will load all given records. Any filtering of the records such as + // exclusion of CIDs with multihash.IDENTITY code must occur prior to calling this function. + // + // Further, the actual information extracted and indexed from the given records entirely + // depends on the concrete index implementation. + // For example, some index implementations may only store partial multihashes. Load([]Record) error // GetAll looks up all blocks matching a given CID, // calling a function for each one of their offsets. // - // If the function returns false, GetAll stops. + // GetAll stops if the given function returns false, + // or there are no more offsets; whichever happens first. // // If no error occurred and the CID isn't indexed, // meaning that no callbacks happen, @@ -61,8 +66,13 @@ type ( } // IterableIndex extends Index in cases where the Index is able to - // provide an iterator for getting the list of all entries in the + // provide an iterator for getting the list of all multihashes in the // index. + // + // Note that it is possible for an index to contain multiple offsets for + // a given multihash. + // + // See: IterableIndex.ForEach, Index.GetAll. IterableIndex interface { Index @@ -73,6 +83,11 @@ type ( // // If the callback returns a non-nil error, the iteration is aborted, // and the ForEach function returns the error to the user. + // + // An index may contain multiple offsets corresponding to the same multihash, e.g. via duplicate blocks. + // In such cases, the given function may be called multiple times with the same multhihash but different offset. + // + // The order of calls to the given function is entirely index-specific. ForEach(func(multihash.Multihash, uint64) error) error } ) diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index c6165ffa7..6b6c5a680 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -207,12 +207,6 @@ func (m *multiWidthIndex) Load(items []Record) error { return err } - // Ignore records with IDENTITY as required by CARv2 spec. - // See: https://ipld.io/specs/transport/car/carv2/#index-format - if decHash.Code == multihash.IDENTITY { - continue - } - digest := decHash.Digest idx, ok := idxs[len(digest)] if !ok { diff --git a/ipld/car/v2/index/indexsorted_test.go b/ipld/car/v2/index/indexsorted_test.go index 46322e543..5c1ee4495 100644 --- a/ipld/car/v2/index/indexsorted_test.go +++ b/ipld/car/v2/index/indexsorted_test.go @@ -4,9 +4,6 @@ import ( "encoding/binary" "testing" - "github.com/ipfs/go-cid" - "github.com/multiformats/go-multihash" - "github.com/ipfs/go-merkledag" "github.com/multiformats/go-multicodec" "github.com/stretchr/testify/require" @@ -65,42 +62,3 @@ func TestSingleWidthIndex_GetAll(t *testing.T) { require.NoError(t, err) require.Equal(t, 3, foundCount) } - -func TestIndexSorted_IgnoresIdentityCids(t *testing.T) { - data := []byte("🟠in da 🌊d") - // Generate a record with IDENTITY multihash - idMh, err := multihash.Sum(data, multihash.IDENTITY, -1) - require.NoError(t, err) - idRec := Record{ - Cid: cid.NewCidV1(cid.Raw, idMh), - Offset: 1, - } - // Generate a record with non-IDENTITY multihash - nonIdMh, err := multihash.Sum(data, multihash.SHA2_256, -1) - require.NoError(t, err) - noIdRec := Record{ - Cid: cid.NewCidV1(cid.Raw, nonIdMh), - Offset: 2, - } - - subject := newSorted() - err = subject.Load([]Record{idRec, noIdRec}) - require.NoError(t, err) - - // Assert record with IDENTITY CID is not present. - err = subject.GetAll(idRec.Cid, func(u uint64) bool { - require.Fail(t, "no IDENTITY record shoul be found") - return false - }) - require.Equal(t, ErrNotFound, err) - - // Assert record with non-IDENTITY CID is indeed present. - var found bool - err = subject.GetAll(noIdRec.Cid, func(gotOffset uint64) bool { - found = true - require.Equal(t, noIdRec.Offset, gotOffset) - return false - }) - require.NoError(t, err) - require.True(t, found) -} diff --git a/ipld/car/v2/index/mhindexsorted.go b/ipld/car/v2/index/mhindexsorted.go index 6ae2c5687..f81e3a942 100644 --- a/ipld/car/v2/index/mhindexsorted.go +++ b/ipld/car/v2/index/mhindexsorted.go @@ -17,7 +17,6 @@ var ( type ( // MultihashIndexSorted maps multihash code (i.e. hashing algorithm) to multiWidthCodedIndex. - // This index ignores any Record with multihash.IDENTITY. MultihashIndexSorted map[uint64]*multiWidthCodedIndex // multiWidthCodedIndex stores multihash code for each multiWidthIndex. multiWidthCodedIndex struct { @@ -123,10 +122,6 @@ func (m *MultihashIndexSorted) Load(records []Record) error { return err } code := dmh.Code - // Ignore IDENTITY multihash in the index. - if code == multihash.IDENTITY { - continue - } recsByCode, ok := byCode[code] if !ok { recsByCode = make([]Record, 0) diff --git a/ipld/car/v2/index/mhindexsorted_test.go b/ipld/car/v2/index/mhindexsorted_test.go index ced8a921a..b5ef7b89b 100644 --- a/ipld/car/v2/index/mhindexsorted_test.go +++ b/ipld/car/v2/index/mhindexsorted_test.go @@ -20,31 +20,6 @@ func TestMutilhashSortedIndex_Codec(t *testing.T) { require.Equal(t, multicodec.CarMultihashIndexSorted, subject.Codec()) } -func TestMultiWidthCodedIndex_LoadDoesNotLoadIdentityMultihash(t *testing.T) { - rng := rand.New(rand.NewSource(1413)) - identityRecords := generateIndexRecords(t, multihash.IDENTITY, rng) - nonIdentityRecords := generateIndexRecords(t, multihash.SHA2_256, rng) - records := append(identityRecords, nonIdentityRecords...) - - subject, err := index.New(multicodec.CarMultihashIndexSorted) - require.NoError(t, err) - err = subject.Load(records) - require.NoError(t, err) - - // Assert index does not contain any records with IDENTITY multihash code. - for _, r := range identityRecords { - wantCid := r.Cid - err = subject.GetAll(wantCid, func(o uint64) bool { - require.Fail(t, "subject should not contain any records with IDENTITY multihash code") - return false - }) - require.Equal(t, index.ErrNotFound, err) - } - - // Assert however, index does contain the non IDENTITY records. - requireContainsAll(t, subject, nonIdentityRecords) -} - func TestMultiWidthCodedIndex_MarshalUnmarshal(t *testing.T) { rng := rand.New(rand.NewSource(1413)) records := generateIndexRecords(t, multihash.SHA2_256, rng) diff --git a/ipld/car/v2/index_gen.go b/ipld/car/v2/index_gen.go index 4602add56..81d23919d 100644 --- a/ipld/car/v2/index_gen.go +++ b/ipld/car/v2/index_gen.go @@ -9,15 +9,19 @@ import ( "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1" internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/multiformats/go-multihash" "github.com/multiformats/go-varint" ) // GenerateIndex generates index for a given car in v1 format. -// The generated index will be in multicodec.CarMultihashIndexSorted, the default index codec. // The index can be stored in serialized format using index.WriteTo. // See LoadIndex. func GenerateIndex(v1r io.Reader, opts ...Option) (index.Index, error) { - idx := index.NewMultihashSorted() + wopts := ApplyOptions(opts...) + idx, err := index.New(wopts.IndexCodec) + if err != nil { + return nil, err + } if err := LoadIndex(idx, v1r, opts...); err != nil { return nil, err } @@ -76,7 +80,13 @@ func LoadIndex(idx index.Index, v1r io.Reader, opts ...Option) error { if err != nil { return err } - records = append(records, index.Record{Cid: c, Offset: uint64(sectionOffset)}) + + if o.StoreIdentityCIDs || c.Prefix().MhType != multihash.IDENTITY { + if uint64(cidLen) > o.MaxIndexCidSize { + return &ErrCidTooLarge{MaxSize: o.MaxIndexCidSize, CurrentSize: uint64(cidLen)} + } + records = append(records, index.Record{Cid: c, Offset: uint64(sectionOffset)}) + } // Seek to the next section by skipping the block. // The section length includes the CID, so subtract it. @@ -94,7 +104,7 @@ func LoadIndex(idx index.Index, v1r io.Reader, opts ...Option) error { } // GenerateIndexFromFile walks a car v1 file at the give path and generates an index of cid->byte offset. -// The index can be stored using index.Save into a file or serialized using index.WriteTo. +// The index can be stored using index.WriteTo. // See GenerateIndex. func GenerateIndexFromFile(path string) (index.Index, error) { f, err := os.Open(path) diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go index ad554a79c..cc6018c40 100644 --- a/ipld/car/v2/options.go +++ b/ipld/car/v2/options.go @@ -2,6 +2,9 @@ package car import "github.com/multiformats/go-multicodec" +// DefaultMaxIndexCidSize specifies the maximum size in byptes accepted as a section CID by CARv2 index. +const DefaultMaxIndexCidSize = 2 << 10 // 2 KiB + // Option describes an option which affects behavior when interacting with CAR files. type Option func(*Options) @@ -25,6 +28,8 @@ type Options struct { IndexPadding uint64 IndexCodec multicodec.Code ZeroLengthSectionAsEOF bool + MaxIndexCidSize uint64 + StoreIdentityCIDs bool BlockstoreAllowDuplicatePuts bool BlockstoreUseWholeCIDs bool @@ -42,6 +47,9 @@ func ApplyOptions(opt ...Option) Options { if opts.IndexCodec == 0 { opts.IndexCodec = multicodec.CarMultihashIndexSorted } + if opts.MaxIndexCidSize == 0 { + opts.MaxIndexCidSize = DefaultMaxIndexCidSize + } return opts } @@ -75,3 +83,20 @@ func UseIndexCodec(c multicodec.Code) Option { o.IndexCodec = c } } + +// StoreIdentityCIDs sets whether to persist sections that are referenced by +// CIDs with multihash.IDENTITY digest. +// This option is disabled by default. +func StoreIdentityCIDs(b bool) Option { + return func(o *Options) { + o.StoreIdentityCIDs = b + } +} + +// MaxIndexCidSize specifies the maximum allowed size for indexed CIDs in bytes. +// Indexing a CID with larger than the allowed size results in ErrCidTooLarge error. +func MaxIndexCidSize(s uint64) Option { + return func(o *Options) { + o.MaxIndexCidSize = s + } +} diff --git a/ipld/car/v2/options_test.go b/ipld/car/v2/options_test.go new file mode 100644 index 000000000..307568a4a --- /dev/null +++ b/ipld/car/v2/options_test.go @@ -0,0 +1,41 @@ +package car_test + +import ( + "testing" + + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/blockstore" + "github.com/multiformats/go-multicodec" + "github.com/stretchr/testify/require" +) + +func TestApplyOptions_SetsExpectedDefaults(t *testing.T) { + require.Equal(t, carv2.Options{ + IndexCodec: multicodec.CarMultihashIndexSorted, + MaxIndexCidSize: carv2.DefaultMaxIndexCidSize, + }, carv2.ApplyOptions()) +} + +func TestApplyOptions_AppliesOptions(t *testing.T) { + require.Equal(t, + carv2.Options{ + DataPadding: 123, + IndexPadding: 456, + IndexCodec: multicodec.CarIndexSorted, + ZeroLengthSectionAsEOF: true, + MaxIndexCidSize: 789, + StoreIdentityCIDs: true, + BlockstoreAllowDuplicatePuts: true, + BlockstoreUseWholeCIDs: true, + }, + carv2.ApplyOptions( + carv2.UseDataPadding(123), + carv2.UseIndexPadding(456), + carv2.UseIndexCodec(multicodec.CarIndexSorted), + carv2.ZeroLengthSectionAsEOF(true), + carv2.MaxIndexCidSize(789), + carv2.StoreIdentityCIDs(true), + blockstore.AllowDuplicatePuts(true), + blockstore.UseWholeCIDs(true), + )) +} From 2104ab54f7873faef772db9f3666647e0ee5ff1c Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Mon, 20 Sep 2021 13:04:19 -0400 Subject: [PATCH 3575/3817] removed timeouts for all resolver functions except for ResolveToLastNode This commit was moved from ipfs/go-path@c2dfedef9ee084ae45ff1d6269e7255a5054f89d --- path/resolver/resolver.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 06533f0e2..f3957c6ef 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -136,10 +136,6 @@ func (r *Resolver) ResolvePath(ctx context.Context, fpath path.Path) (ipld.Node, // create a selector to traverse all path segments but only match the last pathSelector := pathLeafSelector(p) - // create a new cancellable session - ctx, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() - nodes, c, _, err := r.resolveNodes(ctx, c, pathSelector) if err != nil { return nil, nil, err @@ -180,10 +176,6 @@ func (r *Resolver) ResolvePathComponents(ctx context.Context, fpath path.Path) ( // create a selector to traverse and match all path segments pathSelector := pathAllSelector(p) - // create a new cancellable session - ctx, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() - nodes, _, _, err := r.resolveNodes(ctx, c, pathSelector) if err != nil { evt.Append(logging.LoggableMap{"error": err.Error()}) @@ -207,10 +199,6 @@ func (r *Resolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names []stri // create a selector to traverse and match all path segments pathSelector := pathAllSelector(names) - // create a new cancellable session - ctx, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() - session := r.FetcherFactory.NewSession(ctx) // traverse selector From c661a6d9222a9ca5739b2d36f71d6458faed818b Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Mon, 20 Sep 2021 13:13:18 -0400 Subject: [PATCH 3576/3817] add warning note about context cancellation and ADLs to functions that work with fetchers and return IPLD nodes This commit was moved from ipfs/go-path@461c266805c82ecf43f70bf1fd5073743907b70f --- path/resolver/resolver.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index f3957c6ef..47dd47075 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -122,6 +122,9 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (cid. // ResolvePath fetches the node for given path. It returns the last item // returned by ResolvePathComponents and the last link traversed which can be used to recover the block. +// +// Note: if/when the context is cancelled or expires then if a multi-block ADL node is returned then it may not be +// possible to load certain values. func (r *Resolver) ResolvePath(ctx context.Context, fpath path.Path) (ipld.Node, ipld.Link, error) { // validate path if err := fpath.IsValid(); err != nil { @@ -156,6 +159,9 @@ func ResolveSingle(ctx context.Context, ds format.NodeGetter, nd format.Node, na // ResolvePathComponents fetches the nodes for each segment of the given path. // It uses the first path component as a hash (key) of the first node, then // resolves all other components walking the links via a selector traversal +// +// Note: if/when the context is cancelled or expires then if a multi-block ADL node is returned then it may not be +// possible to load certain values. func (r *Resolver) ResolvePathComponents(ctx context.Context, fpath path.Path) ([]ipld.Node, error) { //lint:ignore SA1019 TODO: replace EventBegin evt := log.EventBegin(ctx, "resolvePathComponents", logging.LoggableMap{"fpath": fpath}) @@ -191,6 +197,9 @@ func (r *Resolver) ResolvePathComponents(ctx context.Context, fpath path.Path) ( // // ResolveLinks(nd, []string{"foo", "bar", "baz"}) // would retrieve "baz" in ("bar" in ("foo" in nd.Links).Links).Links +// +// Note: if/when the context is cancelled or expires then if a multi-block ADL node is returned then it may not be +// possible to load certain values. func (r *Resolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names []string) ([]ipld.Node, error) { //lint:ignore SA1019 TODO: replace EventBegin evt := log.EventBegin(ctx, "resolveLinks", logging.LoggableMap{"names": names}) From 92e1b6b4d4d223951a28eb81db2048f1415ece5c Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 22 Sep 2021 17:49:56 +0100 Subject: [PATCH 3577/3817] Resolve lint issues prior to CI integration Remove unused functions. Resolve staticcheck issues and run `go mod tidy` Upgrade module to `1.16` This commit was moved from ipfs/go-pinning-service-http-client@133fb5347ddc144063d2e7e88e8c37ed8f8fd60c --- pinning/remote/client/openapi/client.go | 46 +++---------------- .../remote/client/openapi/configuration.go | 4 +- 2 files changed, 8 insertions(+), 42 deletions(-) diff --git a/pinning/remote/client/openapi/client.go b/pinning/remote/client/openapi/client.go index 985408987..029d1cd72 100644 --- a/pinning/remote/client/openapi/client.go +++ b/pinning/remote/client/openapi/client.go @@ -16,6 +16,7 @@ import ( "encoding/xml" "errors" "fmt" + "golang.org/x/oauth2" "io" "log" "mime/multipart" @@ -26,12 +27,8 @@ import ( "path/filepath" "reflect" "regexp" - "strconv" "strings" "time" - "unicode/utf8" - - "golang.org/x/oauth2" ) var ( @@ -71,10 +68,6 @@ func NewAPIClient(cfg *Configuration) *APIClient { return c } -func atoi(in string) (int, error) { - return strconv.Atoi(in) -} - // selectHeaderContentType select a content type from the available list. func selectHeaderContentType(contentTypes []string) string { if len(contentTypes) == 0 { @@ -102,27 +95,13 @@ func selectHeaderAccept(accepts []string) string { // contains is a case insenstive match, finding needle in a haystack func contains(haystack []string, needle string) bool { for _, a := range haystack { - if strings.ToLower(a) == strings.ToLower(needle) { + if strings.EqualFold(a, needle) { return true } } return false } -// Verify optional parameters are of the correct type. -func typeCheckParameter(obj interface{}, expected string, name string) error { - // Make sure there is an object. - if obj == nil { - return nil - } - - // Check the type is as expected. - if reflect.TypeOf(obj).String() != expected { - return fmt.Errorf("Expected %s to be of type %s but received %s.", name, expected, reflect.TypeOf(obj).String()) - } - return nil -} - // parameterToString convert interface{} parameters to string, using a delimiter if format is provided. func parameterToString(obj interface{}, collectionFormat string) string { var delimiter string @@ -147,15 +126,6 @@ func parameterToString(obj interface{}, collectionFormat string) string { return fmt.Sprintf("%v", obj) } -// helper for converting interface{} parameters to json strings -func parameterToJson(obj interface{}) (string, error) { - jsonBuf, err := json.Marshal(obj) - if err != nil { - return "", err - } - return string(jsonBuf), err -} - // callAPI do the request. func (c *APIClient) callAPI(request *http.Request) (*http.Response, error) { if c.cfg.Debug { @@ -218,7 +188,7 @@ func (c *APIClient) prepareRequest( // add form parameters and file if available. if strings.HasPrefix(headerParams["Content-Type"], "multipart/form-data") && len(formParams) > 0 || (len(fileBytes) > 0 && fileName != "") { if body != nil { - return nil, errors.New("Cannot specify postBody and multipart form at the same time.") + return nil, errors.New("cannot specify postBody and multipart form at the same time") } body = &bytes.Buffer{} w := multipart.NewWriter(body) @@ -258,7 +228,7 @@ func (c *APIClient) prepareRequest( if strings.HasPrefix(headerParams["Content-Type"], "application/x-www-form-urlencoded") && len(formParams) > 0 { if body != nil { - return nil, errors.New("Cannot specify postBody and x-www-form-urlencoded form at the same time.") + return nil, errors.New("cannot specify postBody and x-www-form-urlencoded form at the same time") } body = &bytes.Buffer{} body.WriteString(formParams.Encode()) @@ -370,7 +340,7 @@ func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err err return err } } else { - errors.New("Unknown type with GetActualInstance but no unmarshalObj.UnmarshalJSON defined") + return errors.New("unknown type with GetActualInstance but no unmarshalObj.UnmarshalJSON defined") } } else if err = json.Unmarshal(b, v); err != nil { // simple model return err @@ -427,7 +397,7 @@ func setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err e } if bodyBuf.Len() == 0 { - err = fmt.Errorf("Invalid body type %s\n", contentType) + err = fmt.Errorf("invalid body type %s", contentType) return nil, err } return bodyBuf, nil @@ -504,10 +474,6 @@ func CacheExpires(r *http.Response) time.Time { return expires } -func strlen(s string) int { - return utf8.RuneCountInString(s) -} - // GenericOpenAPIError Provides access to the body, error and model on returned errors. type GenericOpenAPIError struct { body []byte diff --git a/pinning/remote/client/openapi/configuration.go b/pinning/remote/client/openapi/configuration.go index 2f31e1352..bb0a8e507 100644 --- a/pinning/remote/client/openapi/configuration.go +++ b/pinning/remote/client/openapi/configuration.go @@ -121,7 +121,7 @@ func (c *Configuration) AddDefaultHeader(key string, value string) { // URL formats template on a index using given variables func (sc ServerConfigurations) URL(index int, variables map[string]string) (string, error) { if index < 0 || len(sc) <= index { - return "", fmt.Errorf("Index %v out of range %v", index, len(sc)-1) + return "", fmt.Errorf("index %v out of range %v", index, len(sc)-1) } server := sc[index] url := server.URL @@ -136,7 +136,7 @@ func (sc ServerConfigurations) URL(index int, variables map[string]string) (stri } } if !found { - return "", fmt.Errorf("The variable %s in the server URL has invalid value %v. Must be %v", name, value, variable.EnumValues) + return "", fmt.Errorf("the variable %s in the server URL has invalid value %v. Must be %v", name, value, variable.EnumValues) } url = strings.Replace(url, "{"+name+"}", value, -1) } else { From f8e2617e95109c65443bd551f51d3092f82ee9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Wed, 22 Sep 2021 18:47:59 +0200 Subject: [PATCH 3578/3817] fix RawNode incomplete stats This commit was moved from ipfs/go-merkledag@9979f9a558d878e23ca660348831d62411ef1669 --- ipld/merkledag/raw.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ipld/merkledag/raw.go b/ipld/merkledag/raw.go index f4e9e53a4..e53ef09a7 100644 --- a/ipld/merkledag/raw.go +++ b/ipld/merkledag/raw.go @@ -102,6 +102,10 @@ func (rn *RawNode) Size() (uint64, error) { // Stat returns some Stats about this node. func (rn *RawNode) Stat() (*format.NodeStat, error) { return &format.NodeStat{ + Hash: rn.Cid().String(), + NumLinks: 0, + BlockSize: len(rn.RawData()), + LinksSize: 0, CumulativeSize: len(rn.RawData()), DataSize: len(rn.RawData()), }, nil From d666c463e521df9175c72978df8ae558d42dd984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Mon, 27 Sep 2021 17:45:01 +0100 Subject: [PATCH 3579/3817] clarify the relation between StoreIdentityCIDs and SetFullyIndexed I had to read the code to remind myself if this was the case. Document it, for the sake of other godoc readers. This commit was moved from ipld/go-car@999f74fc3e3f0b06ea155b5efb16e43f6d422bcd --- ipld/car/v2/options.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go index cc6018c40..5a3b1930a 100644 --- a/ipld/car/v2/options.go +++ b/ipld/car/v2/options.go @@ -86,6 +86,9 @@ func UseIndexCodec(c multicodec.Code) Option { // StoreIdentityCIDs sets whether to persist sections that are referenced by // CIDs with multihash.IDENTITY digest. +// When writing CAR files with this option, +// Characteristics.IsFullyIndexed will be set. +// // This option is disabled by default. func StoreIdentityCIDs(b bool) Option { return func(o *Options) { From 32b4e02fba2d8df64765f710840b25632d8cfab5 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 30 Sep 2021 17:48:19 +1000 Subject: [PATCH 3580/3817] Add MaxTraversalLinks option to NewSelectiveCar to limit link loading Using the new traversal budget feature of go-ipld-prime This commit was moved from ipld/go-car@2aa0e3852942a0c7b3640db6bff07daef71d0694 --- ipld/car/car_test.go | 39 ++++++++++++++++++++++++++++++++ ipld/car/selectivecar.go | 49 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 137edd629..7a15033c4 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -15,6 +15,7 @@ import ( basicnode "github.com/ipld/go-ipld-prime/node/basic" "github.com/ipld/go-ipld-prime/traversal/selector" "github.com/ipld/go-ipld-prime/traversal/selector/builder" + selectorparse "github.com/ipld/go-ipld-prime/traversal/selector/parse" "github.com/stretchr/testify/require" ) @@ -173,6 +174,44 @@ func TestRoundtripSelective(t *testing.T) { } } +func TestLinkLimitSelective(t *testing.T) { + sourceBserv := dstest.Bserv() + sourceBs := sourceBserv.Blockstore() + dserv := merkledag.NewDAGService(sourceBserv) + a := merkledag.NewRawNode([]byte("aaaa")) + b := merkledag.NewRawNode([]byte("bbbb")) + c := merkledag.NewRawNode([]byte("cccc")) + + nd1 := &merkledag.ProtoNode{} + nd1.AddNodeLink("cat", a) + + nd2 := &merkledag.ProtoNode{} + nd2.AddNodeLink("first", nd1) + nd2.AddNodeLink("dog", b) + nd2.AddNodeLink("repeat", nd1) + + nd3 := &merkledag.ProtoNode{} + nd3.AddNodeLink("second", nd2) + nd3.AddNodeLink("bear", c) + + assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) + + sc := NewSelectiveCar(context.Background(), + sourceBs, + []Dag{{Root: nd3.Cid(), Selector: selectorparse.CommonSelector_ExploreAllRecursively}}, + MaxTraversalLinks(2)) + + buf := new(bytes.Buffer) + blockCount := 0 + err := sc.Write(buf, func(block Block) error { + blockCount++ + return nil + }) + require.Equal(t, blockCount, 3) // root + 2 + require.Error(t, err) + require.Regexp(t, "^traversal budget exceeded: budget for links reached zero while on path .*", err) +} + func TestEOFHandling(t *testing.T) { // fixture is a clean single-block, single-root CAR fixture, err := hex.DecodeString("3aa265726f6f747381d82a58250001711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80b6776657273696f6e012c01711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80ba165646f646779f5") diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index 50a955206..657a4ed22 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "math" cid "github.com/ipfs/go-cid" util "github.com/ipld/go-car/util" @@ -40,6 +41,7 @@ type SelectiveCar struct { ctx context.Context dags []Dag store ReadStore + opts selectiveCarOptions } // OnCarHeaderFunc is called during traversal when the header is created @@ -61,16 +63,16 @@ type SelectiveCarPrepared struct { // NewSelectiveCar creates a new SelectiveCar for the given car file based // a block store and set of root+selector pairs -func NewSelectiveCar(ctx context.Context, store ReadStore, dags []Dag) SelectiveCar { +func NewSelectiveCar(ctx context.Context, store ReadStore, dags []Dag, opts ...SelectiveCarOption) SelectiveCar { return SelectiveCar{ ctx: ctx, store: store, dags: dags, + opts: applyOptions(opts...), } } func (sc SelectiveCar) traverse(onCarHeader OnCarHeaderFunc, onNewCarBlock OnNewCarBlockFunc) (uint64, error) { - traverser := &selectiveCarTraverser{onCarHeader, onNewCarBlock, 0, cid.NewSet(), sc, cidlink.DefaultLinkSystem()} traverser.lsys.StorageReadOpener = traverser.loader return traverser.traverse() @@ -264,16 +266,55 @@ func (sct *selectiveCarTraverser) traverseBlocks() error { if err != nil { return err } - err = traversal.Progress{ + prog := traversal.Progress{ Cfg: &traversal.Config{ Ctx: sct.sc.ctx, LinkSystem: sct.lsys, LinkTargetNodePrototypeChooser: nsc, }, - }.WalkAdv(nd, parsed, func(traversal.Progress, ipld.Node, traversal.VisitReason) error { return nil }) + } + if sct.sc.opts.maxTraversalLinks < math.MaxInt64 { + prog.Budget = &traversal.Budget{ + NodeBudget: math.MaxInt64, + LinkBudget: sct.sc.opts.maxTraversalLinks, + } + } + err = prog.WalkAdv(nd, parsed, func(traversal.Progress, ipld.Node, traversal.VisitReason) error { return nil }) if err != nil { return err } } return nil } + +// selectiveCarOptions holds the configured options after applying a number of +// SelectiveCarOption funcs. +// +// This type should not be used directly by end users; it's only exposed as a +// side effect of SelectiveCarOption. +type selectiveCarOptions struct { + maxTraversalLinks int64 +} + +// SelectiveCarOption describes an option which affects behavior when +// interacting with the SelectiveCar interface. +type SelectiveCarOption func(*selectiveCarOptions) + +// MaxTraversalLinks changes the allowed number of links a selector traversal +// can execute before failing +func MaxTraversalLinks(maxTraversalLinks uint64) SelectiveCarOption { + return func(sco *selectiveCarOptions) { + sco.maxTraversalLinks = int64(maxTraversalLinks) + } +} + +// applyOptions applies given opts and returns the resulting selectiveCarOptions +func applyOptions(opt ...SelectiveCarOption) selectiveCarOptions { + opts := selectiveCarOptions{ + maxTraversalLinks: math.MaxInt64, // default: traverse all + } + for _, o := range opt { + o(&opts) + } + return opts +} From 4bbb58ee890b9416f94d9dc9fbcdd3ddbfcfd2b0 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 30 Sep 2021 20:59:45 +1000 Subject: [PATCH 3581/3817] Defer uint64 cast of link limit until it's passed to go-ipld-prime This commit was moved from ipld/go-car@4fe92f5bc09bec6066495286019eed2ead4f0517 --- ipld/car/selectivecar.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index 657a4ed22..c15eb205d 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -276,7 +276,7 @@ func (sct *selectiveCarTraverser) traverseBlocks() error { if sct.sc.opts.maxTraversalLinks < math.MaxInt64 { prog.Budget = &traversal.Budget{ NodeBudget: math.MaxInt64, - LinkBudget: sct.sc.opts.maxTraversalLinks, + LinkBudget: int64(sct.sc.opts.maxTraversalLinks), } } err = prog.WalkAdv(nd, parsed, func(traversal.Progress, ipld.Node, traversal.VisitReason) error { return nil }) @@ -293,7 +293,7 @@ func (sct *selectiveCarTraverser) traverseBlocks() error { // This type should not be used directly by end users; it's only exposed as a // side effect of SelectiveCarOption. type selectiveCarOptions struct { - maxTraversalLinks int64 + maxTraversalLinks uint64 } // SelectiveCarOption describes an option which affects behavior when @@ -304,7 +304,7 @@ type SelectiveCarOption func(*selectiveCarOptions) // can execute before failing func MaxTraversalLinks(maxTraversalLinks uint64) SelectiveCarOption { return func(sco *selectiveCarOptions) { - sco.maxTraversalLinks = int64(maxTraversalLinks) + sco.maxTraversalLinks = maxTraversalLinks } } From 6fb151275454c61ee65de324d5fbf74ef35dfed0 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 1 Oct 2021 15:56:49 +1000 Subject: [PATCH 3582/3817] Move to generic `Options` like v2 Options This commit was moved from ipld/go-car@db3d3b2580563226955f2a22ef55cf57a6d287d4 --- ipld/car/selectivecar.go | 42 +++++----------------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index c15eb205d..04d4b5940 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -41,7 +41,7 @@ type SelectiveCar struct { ctx context.Context dags []Dag store ReadStore - opts selectiveCarOptions + opts Options } // OnCarHeaderFunc is called during traversal when the header is created @@ -63,12 +63,12 @@ type SelectiveCarPrepared struct { // NewSelectiveCar creates a new SelectiveCar for the given car file based // a block store and set of root+selector pairs -func NewSelectiveCar(ctx context.Context, store ReadStore, dags []Dag, opts ...SelectiveCarOption) SelectiveCar { +func NewSelectiveCar(ctx context.Context, store ReadStore, dags []Dag, opts ...Option) SelectiveCar { return SelectiveCar{ ctx: ctx, store: store, dags: dags, - opts: applyOptions(opts...), + opts: ApplyOptions(opts...), } } @@ -273,10 +273,10 @@ func (sct *selectiveCarTraverser) traverseBlocks() error { LinkTargetNodePrototypeChooser: nsc, }, } - if sct.sc.opts.maxTraversalLinks < math.MaxInt64 { + if sct.sc.opts.MaxTraversalLinks < math.MaxInt64 { prog.Budget = &traversal.Budget{ NodeBudget: math.MaxInt64, - LinkBudget: int64(sct.sc.opts.maxTraversalLinks), + LinkBudget: int64(sct.sc.opts.MaxTraversalLinks), } } err = prog.WalkAdv(nd, parsed, func(traversal.Progress, ipld.Node, traversal.VisitReason) error { return nil }) @@ -286,35 +286,3 @@ func (sct *selectiveCarTraverser) traverseBlocks() error { } return nil } - -// selectiveCarOptions holds the configured options after applying a number of -// SelectiveCarOption funcs. -// -// This type should not be used directly by end users; it's only exposed as a -// side effect of SelectiveCarOption. -type selectiveCarOptions struct { - maxTraversalLinks uint64 -} - -// SelectiveCarOption describes an option which affects behavior when -// interacting with the SelectiveCar interface. -type SelectiveCarOption func(*selectiveCarOptions) - -// MaxTraversalLinks changes the allowed number of links a selector traversal -// can execute before failing -func MaxTraversalLinks(maxTraversalLinks uint64) SelectiveCarOption { - return func(sco *selectiveCarOptions) { - sco.maxTraversalLinks = maxTraversalLinks - } -} - -// applyOptions applies given opts and returns the resulting selectiveCarOptions -func applyOptions(opt ...SelectiveCarOption) selectiveCarOptions { - opts := selectiveCarOptions{ - maxTraversalLinks: math.MaxInt64, // default: traverse all - } - for _, o := range opt { - o(&opts) - } - return opts -} From 9599a55e392f16901a8d0dcb5a73308f474412eb Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 1 Oct 2021 16:28:53 +1000 Subject: [PATCH 3583/3817] Expose TraverseLinksOnlyOnce -> traversal.LinkVisitOnlyOnce option This commit was moved from ipld/go-car@5b6a589c70b24016e6a135b69d4820bf1c255b52 --- ipld/car/car_test.go | 152 ++--------------------- ipld/car/options.go | 56 +++++++++ ipld/car/options_test.go | 28 +++++ ipld/car/selectivecar.go | 1 + ipld/car/selectivecar_test.go | 227 ++++++++++++++++++++++++++++++++++ 5 files changed, 319 insertions(+), 145 deletions(-) create mode 100644 ipld/car/options.go create mode 100644 ipld/car/options_test.go create mode 100644 ipld/car/selectivecar_test.go diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 7a15033c4..0a9c80fed 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -1,4 +1,4 @@ -package car +package car_test import ( "bytes" @@ -12,11 +12,7 @@ import ( format "github.com/ipfs/go-ipld-format" "github.com/ipfs/go-merkledag" dstest "github.com/ipfs/go-merkledag/test" - basicnode "github.com/ipld/go-ipld-prime/node/basic" - "github.com/ipld/go-ipld-prime/traversal/selector" - "github.com/ipld/go-ipld-prime/traversal/selector/builder" - selectorparse "github.com/ipld/go-ipld-prime/traversal/selector/parse" - "github.com/stretchr/testify/require" + car "github.com/ipld/go-car" ) func assertAddNodes(t *testing.T, ds format.DAGService, nds ...format.Node) { @@ -47,12 +43,12 @@ func TestRoundtrip(t *testing.T) { assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) buf := new(bytes.Buffer) - if err := WriteCar(context.Background(), dserv, []cid.Cid{nd3.Cid()}, buf); err != nil { + if err := car.WriteCar(context.Background(), dserv, []cid.Cid{nd3.Cid()}, buf); err != nil { t.Fatal(err) } bserv := dstest.Bserv() - ch, err := LoadCar(bserv.Blockstore(), buf) + ch, err := car.LoadCar(bserv.Blockstore(), buf) if err != nil { t.Fatal(err) } @@ -78,140 +74,6 @@ func TestRoundtrip(t *testing.T) { } } -func TestRoundtripSelective(t *testing.T) { - sourceBserv := dstest.Bserv() - sourceBs := sourceBserv.Blockstore() - dserv := merkledag.NewDAGService(sourceBserv) - a := merkledag.NewRawNode([]byte("aaaa")) - b := merkledag.NewRawNode([]byte("bbbb")) - c := merkledag.NewRawNode([]byte("cccc")) - - nd1 := &merkledag.ProtoNode{} - nd1.AddNodeLink("cat", a) - - nd2 := &merkledag.ProtoNode{} - nd2.AddNodeLink("first", nd1) - nd2.AddNodeLink("dog", b) - nd2.AddNodeLink("repeat", nd1) - - nd3 := &merkledag.ProtoNode{} - nd3.AddNodeLink("second", nd2) - nd3.AddNodeLink("bear", c) - - assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) - - ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype.Any) - - // the graph assembled above looks as follows, in order: - // nd3 -> [c, nd2 -> [nd1 -> a, b, nd1 -> a]] - // this selector starts at n3, and traverses a link at index 1 (nd2, the second link, zero indexed) - // it then recursively traverses all of its children - // the only node skipped is 'c' -- link at index 0 immediately below nd3 - // the purpose is simply to show we are not writing the entire merkledag underneath - // nd3 - selector := ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) { - efsb.Insert("Links", - ssb.ExploreIndex(1, ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreAll(ssb.ExploreRecursiveEdge())))) - }).Node() - - sc := NewSelectiveCar(context.Background(), sourceBs, []Dag{{Root: nd3.Cid(), Selector: selector}}) - - // write car in one step - buf := new(bytes.Buffer) - blockCount := 0 - var oneStepBlocks []Block - err := sc.Write(buf, func(block Block) error { - oneStepBlocks = append(oneStepBlocks, block) - blockCount++ - return nil - }) - require.Equal(t, blockCount, 5) - require.NoError(t, err) - - // create a new builder for two-step write - sc2 := NewSelectiveCar(context.Background(), sourceBs, []Dag{{Root: nd3.Cid(), Selector: selector}}) - - // write car in two steps - var twoStepBlocks []Block - scp, err := sc2.Prepare(func(block Block) error { - twoStepBlocks = append(twoStepBlocks, block) - return nil - }) - require.NoError(t, err) - buf2 := new(bytes.Buffer) - err = scp.Dump(buf2) - require.NoError(t, err) - - // verify preparation step correctly assesed length and blocks - require.Equal(t, scp.Size(), uint64(buf.Len())) - require.Equal(t, len(scp.Cids()), blockCount) - - // verify equal data written by both methods - require.Equal(t, buf.Bytes(), buf2.Bytes()) - - // verify equal blocks were passed to user block hook funcs - require.Equal(t, oneStepBlocks, twoStepBlocks) - - // readout car and verify contents - bserv := dstest.Bserv() - ch, err := LoadCar(bserv.Blockstore(), buf) - require.NoError(t, err) - require.Equal(t, len(ch.Roots), 1) - - require.True(t, ch.Roots[0].Equals(nd3.Cid())) - - bs := bserv.Blockstore() - for _, nd := range []format.Node{a, b, nd1, nd2, nd3} { - has, err := bs.Has(nd.Cid()) - require.NoError(t, err) - require.True(t, has) - } - - for _, nd := range []format.Node{c} { - has, err := bs.Has(nd.Cid()) - require.NoError(t, err) - require.False(t, has) - } -} - -func TestLinkLimitSelective(t *testing.T) { - sourceBserv := dstest.Bserv() - sourceBs := sourceBserv.Blockstore() - dserv := merkledag.NewDAGService(sourceBserv) - a := merkledag.NewRawNode([]byte("aaaa")) - b := merkledag.NewRawNode([]byte("bbbb")) - c := merkledag.NewRawNode([]byte("cccc")) - - nd1 := &merkledag.ProtoNode{} - nd1.AddNodeLink("cat", a) - - nd2 := &merkledag.ProtoNode{} - nd2.AddNodeLink("first", nd1) - nd2.AddNodeLink("dog", b) - nd2.AddNodeLink("repeat", nd1) - - nd3 := &merkledag.ProtoNode{} - nd3.AddNodeLink("second", nd2) - nd3.AddNodeLink("bear", c) - - assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) - - sc := NewSelectiveCar(context.Background(), - sourceBs, - []Dag{{Root: nd3.Cid(), Selector: selectorparse.CommonSelector_ExploreAllRecursively}}, - MaxTraversalLinks(2)) - - buf := new(bytes.Buffer) - blockCount := 0 - err := sc.Write(buf, func(block Block) error { - blockCount++ - return nil - }) - require.Equal(t, blockCount, 3) // root + 2 - require.Error(t, err) - require.Regexp(t, "^traversal budget exceeded: budget for links reached zero while on path .*", err) -} - func TestEOFHandling(t *testing.T) { // fixture is a clean single-block, single-root CAR fixture, err := hex.DecodeString("3aa265726f6f747381d82a58250001711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80b6776657273696f6e012c01711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80ba165646f646779f5") @@ -219,8 +81,8 @@ func TestEOFHandling(t *testing.T) { t.Fatal(err) } - load := func(t *testing.T, byts []byte) *CarReader { - cr, err := NewCarReader(bytes.NewReader(byts)) + load := func(t *testing.T, byts []byte) *car.CarReader { + cr, err := car.NewCarReader(bytes.NewReader(byts)) if err != nil { t.Fatal(err) } @@ -333,7 +195,7 @@ func TestBadHeaders(t *testing.T) { if err != nil { t.Fatal(err) } - _, err = NewCarReader(bytes.NewReader(fixture)) + _, err = car.NewCarReader(bytes.NewReader(fixture)) return err } diff --git a/ipld/car/options.go b/ipld/car/options.go new file mode 100644 index 000000000..d82bd44e0 --- /dev/null +++ b/ipld/car/options.go @@ -0,0 +1,56 @@ +package car + +import "math" + +// Options holds the configured options after applying a number of +// Option funcs. +// +// This type should not be used directly by end users; it's only exposed as a +// side effect of Option. +type Options struct { + TraverseLinksOnlyOnce bool + MaxTraversalLinks uint64 +} + +// Option describes an option which affects behavior when +// interacting with the interface. +type Option func(*Options) + +// TraverseLinksOnlyOnce prevents the traversal engine from repeatedly visiting +// the same links more than once. +// +// This can be an efficient strategy for an exhaustive selector where it's known +// that repeat visits won't impact the completeness of execution. However it +// should be used with caution with most other selectors as repeat visits of +// links for different reasons during selector execution can be valid and +// necessary to perform full traversal. +func TraverseLinksOnlyOnce() Option { + return func(sco *Options) { + sco.TraverseLinksOnlyOnce = true + } +} + +// MaxTraversalLinks changes the allowed number of links a selector traversal +// can execute before failing. +// +// Note that setting this option may cause an error to be returned from selector +// execution when building a SelectiveCar. +func MaxTraversalLinks(MaxTraversalLinks uint64) Option { + return func(sco *Options) { + sco.MaxTraversalLinks = MaxTraversalLinks + } +} + +// ApplyOptions applies given opts and returns the resulting Options. +// This function should not be used directly by end users; it's only exposed as a +// side effect of Option. +func ApplyOptions(opt ...Option) Options { + opts := Options{ + TraverseLinksOnlyOnce: false, // default: recurse until exhausted + MaxTraversalLinks: math.MaxInt64, // default: traverse all + } + for _, o := range opt { + o(&opts) + } + return opts +} diff --git a/ipld/car/options_test.go b/ipld/car/options_test.go new file mode 100644 index 000000000..34f623f52 --- /dev/null +++ b/ipld/car/options_test.go @@ -0,0 +1,28 @@ +package car_test + +import ( + "math" + "testing" + + car "github.com/ipld/go-car" + "github.com/stretchr/testify/require" +) + +func TestApplyOptions_SetsExpectedDefaults(t *testing.T) { + require.Equal(t, car.Options{ + MaxTraversalLinks: math.MaxInt64, + TraverseLinksOnlyOnce: false, + }, car.ApplyOptions()) +} + +func TestApplyOptions_AppliesOptions(t *testing.T) { + require.Equal(t, + car.Options{ + MaxTraversalLinks: 123, + TraverseLinksOnlyOnce: true, + }, + car.ApplyOptions( + car.MaxTraversalLinks(123), + car.TraverseLinksOnlyOnce(), + )) +} diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index 04d4b5940..6e5c5438f 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -271,6 +271,7 @@ func (sct *selectiveCarTraverser) traverseBlocks() error { Ctx: sct.sc.ctx, LinkSystem: sct.lsys, LinkTargetNodePrototypeChooser: nsc, + LinkVisitOnlyOnce: sct.sc.opts.TraverseLinksOnlyOnce, }, } if sct.sc.opts.MaxTraversalLinks < math.MaxInt64 { diff --git a/ipld/car/selectivecar_test.go b/ipld/car/selectivecar_test.go new file mode 100644 index 000000000..387203ff8 --- /dev/null +++ b/ipld/car/selectivecar_test.go @@ -0,0 +1,227 @@ +package car_test + +import ( + "bytes" + "context" + "testing" + + blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" + format "github.com/ipfs/go-ipld-format" + "github.com/ipfs/go-merkledag" + dstest "github.com/ipfs/go-merkledag/test" + car "github.com/ipld/go-car" + basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/ipld/go-ipld-prime/traversal/selector" + "github.com/ipld/go-ipld-prime/traversal/selector/builder" + selectorparse "github.com/ipld/go-ipld-prime/traversal/selector/parse" + "github.com/stretchr/testify/require" +) + +func TestRoundtripSelective(t *testing.T) { + sourceBserv := dstest.Bserv() + sourceBs := sourceBserv.Blockstore() + dserv := merkledag.NewDAGService(sourceBserv) + a := merkledag.NewRawNode([]byte("aaaa")) + b := merkledag.NewRawNode([]byte("bbbb")) + c := merkledag.NewRawNode([]byte("cccc")) + + nd1 := &merkledag.ProtoNode{} + nd1.AddNodeLink("cat", a) + + nd2 := &merkledag.ProtoNode{} + nd2.AddNodeLink("first", nd1) + nd2.AddNodeLink("dog", b) + nd2.AddNodeLink("repeat", nd1) + + nd3 := &merkledag.ProtoNode{} + nd3.AddNodeLink("second", nd2) + nd3.AddNodeLink("bear", c) + + assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) + + ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype.Any) + + // the graph assembled above looks as follows, in order: + // nd3 -> [c, nd2 -> [nd1 -> a, b, nd1 -> a]] + // this selector starts at n3, and traverses a link at index 1 (nd2, the second link, zero indexed) + // it then recursively traverses all of its children + // the only node skipped is 'c' -- link at index 0 immediately below nd3 + // the purpose is simply to show we are not writing the entire merkledag underneath + // nd3 + selector := ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) { + efsb.Insert("Links", + ssb.ExploreIndex(1, ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreAll(ssb.ExploreRecursiveEdge())))) + }).Node() + + sc := car.NewSelectiveCar(context.Background(), sourceBs, []car.Dag{{Root: nd3.Cid(), Selector: selector}}) + + // write car in one step + buf := new(bytes.Buffer) + blockCount := 0 + var oneStepBlocks []car.Block + err := sc.Write(buf, func(block car.Block) error { + oneStepBlocks = append(oneStepBlocks, block) + blockCount++ + return nil + }) + require.Equal(t, blockCount, 5) + require.NoError(t, err) + + // create a new builder for two-step write + sc2 := car.NewSelectiveCar(context.Background(), sourceBs, []car.Dag{{Root: nd3.Cid(), Selector: selector}}) + + // write car in two steps + var twoStepBlocks []car.Block + scp, err := sc2.Prepare(func(block car.Block) error { + twoStepBlocks = append(twoStepBlocks, block) + return nil + }) + require.NoError(t, err) + buf2 := new(bytes.Buffer) + err = scp.Dump(buf2) + require.NoError(t, err) + + // verify preparation step correctly assesed length and blocks + require.Equal(t, scp.Size(), uint64(buf.Len())) + require.Equal(t, len(scp.Cids()), blockCount) + + // verify equal data written by both methods + require.Equal(t, buf.Bytes(), buf2.Bytes()) + + // verify equal blocks were passed to user block hook funcs + require.Equal(t, oneStepBlocks, twoStepBlocks) + + // readout car and verify contents + bserv := dstest.Bserv() + ch, err := car.LoadCar(bserv.Blockstore(), buf) + require.NoError(t, err) + require.Equal(t, len(ch.Roots), 1) + + require.True(t, ch.Roots[0].Equals(nd3.Cid())) + + bs := bserv.Blockstore() + for _, nd := range []format.Node{a, b, nd1, nd2, nd3} { + has, err := bs.Has(nd.Cid()) + require.NoError(t, err) + require.True(t, has) + } + + for _, nd := range []format.Node{c} { + has, err := bs.Has(nd.Cid()) + require.NoError(t, err) + require.False(t, has) + } +} + +func TestNoLinkRepeatSelective(t *testing.T) { + sourceBserv := dstest.Bserv() + sourceBs := countingReadStore{bs: sourceBserv.Blockstore()} + dserv := merkledag.NewDAGService(sourceBserv) + a := merkledag.NewRawNode([]byte("aaaa")) + b := merkledag.NewRawNode([]byte("bbbb")) + c := merkledag.NewRawNode([]byte("cccc")) + + nd1 := &merkledag.ProtoNode{} + nd1.AddNodeLink("cat", a) + + nd2 := &merkledag.ProtoNode{} + nd2.AddNodeLink("first", nd1) + nd2.AddNodeLink("dog", b) + nd2.AddNodeLink("repeat", nd1) + + nd3 := &merkledag.ProtoNode{} + nd3.AddNodeLink("second", nd2) + nd3.AddNodeLink("bear", c) + nd3.AddNodeLink("bearagain1", c) + nd3.AddNodeLink("bearagain2", c) + nd3.AddNodeLink("bearagain3", c) + + assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) + + t.Run("TraverseLinksOnlyOnce off", func(t *testing.T) { + sourceBs.count = 0 + sc := car.NewSelectiveCar(context.Background(), + &sourceBs, + []car.Dag{{Root: nd3.Cid(), Selector: selectorparse.CommonSelector_ExploreAllRecursively}}, + ) + + buf := new(bytes.Buffer) + blockCount := 0 + err := sc.Write(buf, func(block car.Block) error { + blockCount++ + return nil + }) + require.Equal(t, blockCount, 6) + require.Equal(t, sourceBs.count, 11) // with TraverseLinksOnlyOnce off, we expect repeat block visits because our DAG has repeat links + require.NoError(t, err) + }) + + t.Run("TraverseLinksOnlyOnce on", func(t *testing.T) { + sourceBs.count = 0 + + sc := car.NewSelectiveCar(context.Background(), + &sourceBs, + []car.Dag{{Root: nd3.Cid(), Selector: selectorparse.CommonSelector_ExploreAllRecursively}}, + car.TraverseLinksOnlyOnce(), + ) + + buf := new(bytes.Buffer) + blockCount := 0 + err := sc.Write(buf, func(block car.Block) error { + blockCount++ + return nil + }) + require.Equal(t, blockCount, 6) + require.Equal(t, sourceBs.count, 6) // only 6 blocks to load, no duplicate loading expected + require.NoError(t, err) + }) +} + +func TestLinkLimitSelective(t *testing.T) { + sourceBserv := dstest.Bserv() + sourceBs := sourceBserv.Blockstore() + dserv := merkledag.NewDAGService(sourceBserv) + a := merkledag.NewRawNode([]byte("aaaa")) + b := merkledag.NewRawNode([]byte("bbbb")) + c := merkledag.NewRawNode([]byte("cccc")) + + nd1 := &merkledag.ProtoNode{} + nd1.AddNodeLink("cat", a) + + nd2 := &merkledag.ProtoNode{} + nd2.AddNodeLink("first", nd1) + nd2.AddNodeLink("dog", b) + nd2.AddNodeLink("repeat", nd1) + + nd3 := &merkledag.ProtoNode{} + nd3.AddNodeLink("second", nd2) + nd3.AddNodeLink("bear", c) + + assertAddNodes(t, dserv, a, b, c, nd1, nd2, nd3) + + sc := car.NewSelectiveCar(context.Background(), + sourceBs, + []car.Dag{{Root: nd3.Cid(), Selector: selectorparse.CommonSelector_ExploreAllRecursively}}, + car.MaxTraversalLinks(2)) + + buf := new(bytes.Buffer) + blockCount := 0 + err := sc.Write(buf, func(block car.Block) error { + blockCount++ + return nil + }) + require.Equal(t, blockCount, 3) // root + 2 + require.Error(t, err) + require.Regexp(t, "^traversal budget exceeded: budget for links reached zero while on path .*", err) +} + +type countingReadStore struct { + bs car.ReadStore + count int +} + +func (rs *countingReadStore) Get(c cid.Cid) (blocks.Block, error) { + rs.count++ + return rs.bs.Get(c) +} From 9c0ff822e5d04da9b3164f88c2ff42f0da135f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Thu, 30 Sep 2021 11:07:37 +0100 Subject: [PATCH 3584/3817] blockstore: OpenReadWrite should not modify if it refuses to resume If OpenReadWrite sees a good reason to refuse to resume, such as the provided roots not matching what's on disk, it should not truncate or un-finalize the file on disk. Add a test that exercises that edge case, and also verifies that the file isn't modified. To fix the problem, carefully move truncation and un-finalization past the point where we've checked the existing header. Note that we may still leave a broken file if we encounter an error later on, such as when writing to the file or building the index. If we want those to not break the input file we'll need bigger changes, such as writing to a temporary file and atomically replacing the final file at the very end. For now, at least we can avoid a common breakage case. Before the fix, the test would fail: Error Trace: readwrite_test.go:621 Error: Not equal: expected: 521708 actual : 479958 Test: TestReadWriteResumptionMismatchingRootsIsError Fixes #247. This commit was moved from ipld/go-car@4e0a1fa04c8f7e8fc69176007bc07c25ef7ce9c9 --- ipld/car/v2/blockstore/readwrite.go | 32 +++++++++++++----------- ipld/car/v2/blockstore/readwrite_test.go | 26 +++++++++++++++++-- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index b98c58d4b..0dda5a0bd 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -182,27 +182,13 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { "`WithDataPadding` option must match the padding on file. "+ "Expected padding value of %v but got %v", wantPadding, gotPadding, ) - } else if headerInFile.DataSize != 0 { - // If header in file contains the size of car v1, then the index is most likely present. - // Since we will need to re-generate the index, as the one in file is flattened, truncate - // the file so that the Readonly.backing has the right set of bytes to deal with. - // This effectively means resuming from a finalized file will wipe its index even if there - // are no blocks put unless the user calls finalize. - if err := b.f.Truncate(int64(headerInFile.DataOffset + headerInFile.DataSize)); err != nil { - return err - } - } else { + } else if headerInFile.DataSize == 0 { // If CARv1 size is zero, since CARv1 offset wasn't, then the CARv2 header was // most-likely partially written. Since we write the header last in Finalize then the // file most-likely contains the index and we cannot know where it starts, therefore // can't resume. return errors.New("corrupt CARv2 header; cannot resume from file") } - // Now that CARv2 header is present on file, clear it to avoid incorrect size and offset in - // header in case blocksotre is closed without finalization and is resumed from. - if err := b.unfinalize(); err != nil { - return err - } } // Use the given CARv1 padding to instantiate the CARv1 reader on file. @@ -217,6 +203,22 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { return errors.New("cannot resume on file with mismatching data header") } + if headerInFile.DataOffset != 0 { + // If header in file contains the size of car v1, then the index is most likely present. + // Since we will need to re-generate the index, as the one in file is flattened, truncate + // the file so that the Readonly.backing has the right set of bytes to deal with. + // This effectively means resuming from a finalized file will wipe its index even if there + // are no blocks put unless the user calls finalize. + if err := b.f.Truncate(int64(headerInFile.DataOffset + headerInFile.DataSize)); err != nil { + return err + } + } + // Now that CARv2 header is present on file, clear it to avoid incorrect size and offset in + // header in case blocksotre is closed without finalization and is resumed from. + if err := b.unfinalize(); err != nil { + return fmt.Errorf("could not un-finalize: %w", err) + } + // TODO See how we can reduce duplicate code here. // The code here comes from car.GenerateIndex. // Copied because we need to populate an insertindex, not a sorted index. diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index b7da6ec83..d58b6de3c 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -600,17 +600,39 @@ func TestReadWriteResumptionFromNonV2FileIsError(t *testing.T) { require.Nil(t, subject) } +func TestReadWriteResumptionMismatchingRootsIsError(t *testing.T) { + tmpPath := requireTmpCopy(t, "../testdata/sample-wrapped-v2.car") + + origContent, err := ioutil.ReadFile(tmpPath) + require.NoError(t, err) + + badRoot, err := cid.NewPrefixV1(cid.Raw, multihash.SHA2_256).Sum([]byte("bad root")) + require.NoError(t, err) + + subject, err := blockstore.OpenReadWrite(tmpPath, []cid.Cid{badRoot}) + require.EqualError(t, err, "cannot resume on file with mismatching data header") + require.Nil(t, subject) + + newContent, err := ioutil.ReadFile(tmpPath) + require.NoError(t, err) + + // Expect the bad file to be left untouched; check the size first. + // If the sizes mismatch, printing a huge diff would not help us. + require.Equal(t, len(origContent), len(newContent)) + require.Equal(t, origContent, newContent) +} + func requireTmpCopy(t *testing.T, src string) string { srcF, err := os.Open(src) require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, srcF.Close()) }) + defer func() { require.NoError(t, srcF.Close()) }() stats, err := srcF.Stat() require.NoError(t, err) dst := filepath.Join(t.TempDir(), stats.Name()) dstF, err := os.Create(dst) require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, dstF.Close()) }) + defer func() { require.NoError(t, dstF.Close()) }() _, err = io.Copy(dstF, srcF) require.NoError(t, err) From 501b8412572ef72018e43cc8fd1baf8fa340f7f8 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 30 Sep 2021 16:43:37 +0100 Subject: [PATCH 3585/3817] Implement API to allow replacing root CIDs in a CARv1 or CARv2 Implement an API that allows a caller to replace root CIDs in an existing CAR file, may it be v1 or v2, as long as the resulting serialized header is of identical size to the existing header. Assert that the new API works in a variety of CARv1 and CARv2 files along with failure scenarios. Fixes #245 This commit was moved from ipld/go-car@f4378127a67f1bf4d616ef83a1e5a4d841a9478b --- ipld/car/v2/writer.go | 100 +++++++++++++++++++++++++ ipld/car/v2/writer_test.go | 146 +++++++++++++++++++++++++++++++++++++ 2 files changed, 246 insertions(+) diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index 8ed5756fe..72675cb27 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -1,12 +1,15 @@ package car import ( + "bytes" "errors" "fmt" "io" "os" + "github.com/ipfs/go-cid" "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-car/v2/internal/carv1" internalio "github.com/ipld/go-car/v2/internal/io" ) @@ -210,3 +213,100 @@ func AttachIndex(path string, idx index.Index, offset uint64) error { indexWriter := internalio.NewOffsetWriter(out, int64(offset)) return index.WriteTo(idx, indexWriter) } + +// ReplaceRootsInFile replaces the root CIDs in CAR file at given path with the given roots. +// This function accepts both CARv1 and CARv2 files. +// +// Note that the roots are only replaced if their total serialized size exactly matches the total +// serialized size of existing roots in CAR file. +func ReplaceRootsInFile(path string, roots []cid.Cid) (err error) { + f, err := os.OpenFile(path, os.O_RDWR, 0o666) + if err != nil { + return err + } + defer func() { + // Close file and override return error type if it is nil. + if cerr := f.Close(); err == nil { + err = cerr + } + }() + + // Read header or pragma; note that both are a valid CARv1 header. + header, err := carv1.ReadHeader(f) + if err != nil { + return err + } + + var currentSize int64 + var newHeaderOffset int64 + switch header.Version { + case 1: + // When the given file is a CARv1 : + // 1. The offset at which the new header should be written is zero (newHeaderOffset = 0) + // 2. The current header size is equal to the number of bytes read, and + // + // Note that we explicitly avoid using carv1.HeaderSize to determine the current header size. + // This is based on the fact that carv1.ReadHeader does not read any extra bytes. + // Therefore, we can avoid extra allocations of carv1.HeaderSize to determine size by simply + // counting the bytes read so far. + currentSize, err = f.Seek(0, io.SeekCurrent) + if err != nil { + return err + } + case 2: + // When the given file is a CARv2 : + // 1. The offset at which the new header should be written is carv2.Header.DataOffset + // 2. The inner CARv1 header size is equal to the number of bytes read minus carv2.Header.DataOffset + var v2h Header + if _, err = v2h.ReadFrom(f); err != nil { + return err + } + newHeaderOffset = int64(v2h.DataOffset) + if _, err = f.Seek(newHeaderOffset, io.SeekStart); err != nil { + return err + } + var innerV1Header *carv1.CarHeader + innerV1Header, err = carv1.ReadHeader(f) + if err != nil { + return err + } + if innerV1Header.Version != 1 { + err = fmt.Errorf("invalid data payload header: expected version 1, got %d", innerV1Header.Version) + } + var readSoFar int64 + readSoFar, err = f.Seek(0, io.SeekCurrent) + if err != nil { + return err + } + currentSize = readSoFar - newHeaderOffset + default: + err = fmt.Errorf("invalid car version: %d", header.Version) + return err + } + + newHeader := &carv1.CarHeader{ + Roots: roots, + Version: 1, + } + // Serialize the new header straight up instead of using carv1.HeaderSize. + // Because, carv1.HeaderSize serialises it to calculate size anyway. + // By serializing straight up we get the replacement bytes and size. + // Otherwise, we end up serializing the new header twice: + // once through carv1.HeaderSize, and + // once to write it out. + var buf bytes.Buffer + if err = carv1.WriteHeader(newHeader, &buf); err != nil { + return err + } + // Assert the header sizes are consistent. + newSize := int64(buf.Len()) + if currentSize != newSize { + return fmt.Errorf("current header size (%d) must match replacement header size (%d)", currentSize, newSize) + } + // Seek to the offset at which the new header should be written. + if _, err = f.Seek(newHeaderOffset, io.SeekStart); err != nil { + return err + } + _, err = f.Write(buf.Bytes()) + return err +} diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index c29b43397..11b5ff129 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -131,3 +131,149 @@ func assertAddNodes(t *testing.T, adder format.NodeAdder, nds ...format.Node) { assert.NoError(t, adder.Add(context.Background(), nd)) } } + +func TestReplaceRootsInFile(t *testing.T) { + tests := []struct { + name string + path string + roots []cid.Cid + wantErrMsg string + }{ + { + name: "CorruptPragmaIsRejected", + path: "testdata/sample-corrupt-pragma.car", + wantErrMsg: "unexpected EOF", + }, + { + name: "CARv42IsRejected", + path: "testdata/sample-rootless-v42.car", + wantErrMsg: "invalid car version: 42", + }, + { + name: "CARv1RootsOfDifferentSizeAreNotReplaced", + path: "testdata/sample-v1.car", + wantErrMsg: "current header size (61) must match replacement header size (18)", + }, + { + name: "CARv2RootsOfDifferentSizeAreNotReplaced", + path: "testdata/sample-wrapped-v2.car", + wantErrMsg: "current header size (61) must match replacement header size (18)", + }, + { + name: "CARv1NonEmptyRootsOfDifferentSizeAreNotReplaced", + path: "testdata/sample-v1.car", + roots: []cid.Cid{requireDecodedCid(t, "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n")}, + wantErrMsg: "current header size (61) must match replacement header size (57)", + }, + { + name: "CARv1ZeroLenNonEmptyRootsOfDifferentSizeAreNotReplaced", + path: "testdata/sample-v1-with-zero-len-section.car", + roots: []cid.Cid{merkledag.NewRawNode([]byte("fish")).Cid()}, + wantErrMsg: "current header size (61) must match replacement header size (59)", + }, + { + name: "CARv2NonEmptyRootsOfDifferentSizeAreNotReplaced", + path: "testdata/sample-wrapped-v2.car", + roots: []cid.Cid{merkledag.NewRawNode([]byte("fish")).Cid()}, + wantErrMsg: "current header size (61) must match replacement header size (59)", + }, + { + name: "CARv2IndexlessNonEmptyRootsOfDifferentSizeAreNotReplaced", + path: "testdata/sample-v2-indexless.car", + roots: []cid.Cid{merkledag.NewRawNode([]byte("fish")).Cid()}, + wantErrMsg: "current header size (61) must match replacement header size (59)", + }, + { + name: "CARv1SameSizeRootsAreReplaced", + path: "testdata/sample-v1.car", + roots: []cid.Cid{requireDecodedCid(t, "bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5od")}, + }, + { + name: "CARv2SameSizeRootsAreReplaced", + path: "testdata/sample-wrapped-v2.car", + roots: []cid.Cid{requireDecodedCid(t, "bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oi")}, + }, + { + name: "CARv2IndexlessSameSizeRootsAreReplaced", + path: "testdata/sample-v2-indexless.car", + roots: []cid.Cid{requireDecodedCid(t, "bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oi")}, + }, + { + name: "CARv1ZeroLenSameSizeRootsAreReplaced", + path: "testdata/sample-v1-with-zero-len-section.car", + roots: []cid.Cid{requireDecodedCid(t, "bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5o5")}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Make a copy of input files to preserve original for comparison. + // This also avoids modification files in testdata. + tmpCopy := requireTmpCopy(t, tt.path) + err := ReplaceRootsInFile(tmpCopy, tt.roots) + if tt.wantErrMsg != "" { + require.EqualError(t, err, tt.wantErrMsg) + return + } + require.NoError(t, err) + + original, err := os.Open(tt.path) + require.NoError(t, err) + defer func() { require.NoError(t, original.Close()) }() + + target, err := os.Open(tmpCopy) + require.NoError(t, err) + defer func() { require.NoError(t, target.Close()) }() + + // Assert file size has not changed. + wantStat, err := original.Stat() + require.NoError(t, err) + gotStat, err := target.Stat() + require.NoError(t, err) + require.Equal(t, wantStat.Size(), gotStat.Size()) + + wantReader, err := NewBlockReader(original, ZeroLengthSectionAsEOF(true)) + require.NoError(t, err) + gotReader, err := NewBlockReader(target, ZeroLengthSectionAsEOF(true)) + require.NoError(t, err) + + // Assert roots are replaced. + require.Equal(t, tt.roots, gotReader.Roots) + + // Assert data blocks are identical. + for { + wantNext, wantErr := wantReader.Next() + gotNext, gotErr := gotReader.Next() + if wantErr == io.EOF { + require.Equal(t, io.EOF, gotErr) + break + } + require.NoError(t, wantErr) + require.NoError(t, gotErr) + require.Equal(t, wantNext, gotNext) + } + }) + } +} + +func requireDecodedCid(t *testing.T, s string) cid.Cid { + decoded, err := cid.Decode(s) + require.NoError(t, err) + return decoded +} + +func requireTmpCopy(t *testing.T, src string) string { + srcF, err := os.Open(src) + require.NoError(t, err) + defer func() { require.NoError(t, srcF.Close()) }() + stats, err := srcF.Stat() + require.NoError(t, err) + + dst := filepath.Join(t.TempDir(), stats.Name()) + dstF, err := os.Create(dst) + require.NoError(t, err) + defer func() { require.NoError(t, dstF.Close()) }() + + _, err = io.Copy(dstF, srcF) + require.NoError(t, err) + return dst +} From 901cdc0ae4a3722ad8b0ef3d89b738a376bc3005 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 1 Oct 2021 19:49:00 +1000 Subject: [PATCH 3586/3817] Make Options private This commit was moved from ipld/go-car@3ff3c49cdb50241006e9124a88aa87acdf59a072 --- ipld/car/options.go | 17 ++++++----------- ipld/car/options_test.go | 15 +++++++-------- ipld/car/selectivecar.go | 4 ++-- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/ipld/car/options.go b/ipld/car/options.go index d82bd44e0..4eef80696 100644 --- a/ipld/car/options.go +++ b/ipld/car/options.go @@ -4,17 +4,14 @@ import "math" // Options holds the configured options after applying a number of // Option funcs. -// -// This type should not be used directly by end users; it's only exposed as a -// side effect of Option. -type Options struct { +type options struct { TraverseLinksOnlyOnce bool MaxTraversalLinks uint64 } // Option describes an option which affects behavior when // interacting with the interface. -type Option func(*Options) +type Option func(*options) // TraverseLinksOnlyOnce prevents the traversal engine from repeatedly visiting // the same links more than once. @@ -25,7 +22,7 @@ type Option func(*Options) // links for different reasons during selector execution can be valid and // necessary to perform full traversal. func TraverseLinksOnlyOnce() Option { - return func(sco *Options) { + return func(sco *options) { sco.TraverseLinksOnlyOnce = true } } @@ -36,16 +33,14 @@ func TraverseLinksOnlyOnce() Option { // Note that setting this option may cause an error to be returned from selector // execution when building a SelectiveCar. func MaxTraversalLinks(MaxTraversalLinks uint64) Option { - return func(sco *Options) { + return func(sco *options) { sco.MaxTraversalLinks = MaxTraversalLinks } } // ApplyOptions applies given opts and returns the resulting Options. -// This function should not be used directly by end users; it's only exposed as a -// side effect of Option. -func ApplyOptions(opt ...Option) Options { - opts := Options{ +func applyOptions(opt ...Option) options { + opts := options{ TraverseLinksOnlyOnce: false, // default: recurse until exhausted MaxTraversalLinks: math.MaxInt64, // default: traverse all } diff --git a/ipld/car/options_test.go b/ipld/car/options_test.go index 34f623f52..250c67203 100644 --- a/ipld/car/options_test.go +++ b/ipld/car/options_test.go @@ -1,28 +1,27 @@ -package car_test +package car import ( "math" "testing" - car "github.com/ipld/go-car" "github.com/stretchr/testify/require" ) func TestApplyOptions_SetsExpectedDefaults(t *testing.T) { - require.Equal(t, car.Options{ + require.Equal(t, options{ MaxTraversalLinks: math.MaxInt64, TraverseLinksOnlyOnce: false, - }, car.ApplyOptions()) + }, applyOptions()) } func TestApplyOptions_AppliesOptions(t *testing.T) { require.Equal(t, - car.Options{ + options{ MaxTraversalLinks: 123, TraverseLinksOnlyOnce: true, }, - car.ApplyOptions( - car.MaxTraversalLinks(123), - car.TraverseLinksOnlyOnce(), + applyOptions( + MaxTraversalLinks(123), + TraverseLinksOnlyOnce(), )) } diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index 6e5c5438f..9b5bd8cef 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -41,7 +41,7 @@ type SelectiveCar struct { ctx context.Context dags []Dag store ReadStore - opts Options + opts options } // OnCarHeaderFunc is called during traversal when the header is created @@ -68,7 +68,7 @@ func NewSelectiveCar(ctx context.Context, store ReadStore, dags []Dag, opts ...O ctx: ctx, store: store, dags: dags, - opts: ApplyOptions(opts...), + opts: applyOptions(opts...), } } From c27f485f406a94af12d475dafc0b954ec4bc31f9 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 1 Oct 2021 20:05:30 +1000 Subject: [PATCH 3587/3817] fix: doc typos This commit was moved from ipld/go-car@11922368e3a4cc435b5a1bdee65b5e3c293b9d76 --- ipld/car/options.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/car/options.go b/ipld/car/options.go index 4eef80696..e317f9cc9 100644 --- a/ipld/car/options.go +++ b/ipld/car/options.go @@ -2,7 +2,7 @@ package car import "math" -// Options holds the configured options after applying a number of +// options holds the configured options after applying a number of // Option funcs. type options struct { TraverseLinksOnlyOnce bool @@ -38,7 +38,7 @@ func MaxTraversalLinks(MaxTraversalLinks uint64) Option { } } -// ApplyOptions applies given opts and returns the resulting Options. +// applyOptions applies given opts and returns the resulting options. func applyOptions(opt ...Option) options { opts := options{ TraverseLinksOnlyOnce: false, // default: recurse until exhausted From 31cd0742970e47a1148caa1b44e015d29556dbdc Mon Sep 17 00:00:00 2001 From: Will Date: Sat, 16 Oct 2021 16:26:42 -0700 Subject: [PATCH 3588/3817] forEach iterates over index in stable order (#258) * forEach iterates over index in stable order This commit was moved from ipld/go-car@f9c3b063844cc50478c41bea1e407c2d40a03696 --- ipld/car/v2/index/index.go | 2 +- ipld/car/v2/index/indexsorted.go | 8 ++++++- ipld/car/v2/index/mhindexsorted.go | 8 ++++++- ipld/car/v2/index/mhindexsorted_test.go | 29 +++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 8e447d0fe..998a17a0b 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -87,7 +87,7 @@ type ( // An index may contain multiple offsets corresponding to the same multihash, e.g. via duplicate blocks. // In such cases, the given function may be called multiple times with the same multhihash but different offset. // - // The order of calls to the given function is entirely index-specific. + // The order of calls to the given function is deterministic, but entirely index-specific. ForEach(func(multihash.Multihash, uint64) error) error } ) diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index 6b6c5a680..86994dd8b 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -235,7 +235,13 @@ func (m *multiWidthIndex) Load(items []Record) error { } func (m *multiWidthIndex) forEachDigest(f func(digest []byte, offset uint64) error) error { - for _, swi := range *m { + sizes := make([]uint32, 0, len(*m)) + for k := range *m { + sizes = append(sizes, k) + } + sort.Slice(sizes, func(i, j int) bool { return sizes[i] < sizes[j] }) + for _, s := range sizes { + swi := (*m)[s] if err := swi.forEachDigest(f); err != nil { return err } diff --git a/ipld/car/v2/index/mhindexsorted.go b/ipld/car/v2/index/mhindexsorted.go index f81e3a942..e3cae3d07 100644 --- a/ipld/car/v2/index/mhindexsorted.go +++ b/ipld/car/v2/index/mhindexsorted.go @@ -157,7 +157,13 @@ func (m *MultihashIndexSorted) GetAll(cid cid.Cid, f func(uint64) bool) error { // ForEach calls f for every multihash and its associated offset stored by this index. func (m *MultihashIndexSorted) ForEach(f func(mh multihash.Multihash, offset uint64) error) error { - for _, mwci := range *m { + sizes := make([]uint64, 0, len(*m)) + for k := range *m { + sizes = append(sizes, k) + } + sort.Slice(sizes, func(i, j int) bool { return sizes[i] < sizes[j] }) + for _, s := range sizes { + mwci := (*m)[s] if err := mwci.forEach(f); err != nil { return err } diff --git a/ipld/car/v2/index/mhindexsorted_test.go b/ipld/car/v2/index/mhindexsorted_test.go index b5ef7b89b..e02ba0599 100644 --- a/ipld/car/v2/index/mhindexsorted_test.go +++ b/ipld/car/v2/index/mhindexsorted_test.go @@ -46,6 +46,35 @@ func TestMultiWidthCodedIndex_MarshalUnmarshal(t *testing.T) { requireContainsAll(t, umSubject, records) } +func TestMultiWidthCodedIndex_StableIterate(t *testing.T) { + rng := rand.New(rand.NewSource(1414)) + records := generateIndexRecords(t, multihash.SHA2_256, rng) + records = append(records, generateIndexRecords(t, multihash.SHA2_512, rng)...) + records = append(records, generateIndexRecords(t, multihash.IDENTITY, rng)...) + + // Create a new mh sorted index and load randomly generated records into it. + subject, err := index.New(multicodec.CarMultihashIndexSorted) + require.NoError(t, err) + err = subject.Load(records) + require.NoError(t, err) + + iterable := subject.(index.IterableIndex) + mh := make([]multihash.Multihash, 0, len(records)) + require.NoError(t, iterable.ForEach(func(m multihash.Multihash, _ uint64) error { + mh = append(mh, m) + return nil + })) + + for i := 0; i < 10; i++ { + candidate := make([]multihash.Multihash, 0, len(records)) + require.NoError(t, iterable.ForEach(func(m multihash.Multihash, _ uint64) error { + candidate = append(candidate, m) + return nil + })) + require.Equal(t, mh, candidate) + } +} + func generateIndexRecords(t *testing.T, hasherCode uint64, rng *rand.Rand) []index.Record { var records []index.Record recordCount := rng.Intn(99) + 1 // Up to 100 records From f4068879ee84f1dee4f23ba46b110d96de984d8d Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Wed, 20 Oct 2021 14:35:16 -0700 Subject: [PATCH 3589/3817] expose session construction to other callers This commit was moved from ipfs/go-merkledag@7602f0e4a625f5fe25f293f5f2210552e1733be6 --- ipld/merkledag/merkledag.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 5d318e3b7..0790b3e85 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -160,9 +160,14 @@ func (sg *sesGetter) GetMany(ctx context.Context, keys []cid.Cid) <-chan *format return getNodesFromBG(ctx, sg.bs, keys) } +// WrapSession wraps a blockservice session to satisfy the format.NodeGetter interface +func WrapSession(s *bserv.Session) format.NodeGetter { + return &sesGetter{s} +} + // Session returns a NodeGetter using a new session for block fetches. func (n *dagService) Session(ctx context.Context) format.NodeGetter { - return &sesGetter{bserv.NewSession(ctx, n.Blocks)} + return WrapSession(bserv.NewSession(ctx, n.Blocks)) } // FetchGraph fetches all nodes that are children of the given node From 0e594882a8d72754142d334ab2476befbc90665e Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Thu, 21 Oct 2021 10:16:30 -0700 Subject: [PATCH 3590/3817] add a fetcher constructor for the case where we already have a session This commit was moved from ipfs/go-fetcher@99235cc120519afe2bddcb6b1f7a6844ecc3707f --- fetcher/impl/blockservice/fetcher.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fetcher/impl/blockservice/fetcher.go b/fetcher/impl/blockservice/fetcher.go index 3327a4ecd..d203a1597 100644 --- a/fetcher/impl/blockservice/fetcher.go +++ b/fetcher/impl/blockservice/fetcher.go @@ -39,11 +39,15 @@ func NewFetcherConfig(blockService blockservice.BlockService) FetcherConfig { // NewSession creates a session from which nodes may be retrieved. // The session ends when the provided context is canceled. func (fc FetcherConfig) NewSession(ctx context.Context) fetcher.Fetcher { + return fc.FetcherWithSession(ctx, blockservice.NewSession(ctx, fc.blockService)) +} + +func (fc FetcherConfig) FetcherWithSession(ctx context.Context, s *blockservice.Session) fetcher.Fetcher { ls := cidlink.DefaultLinkSystem() // while we may be loading blocks remotely, they are already hash verified by the time they load // into ipld-prime ls.TrustedStorage = true - ls.StorageReadOpener = blockOpener(ctx, blockservice.NewSession(ctx, fc.blockService)) + ls.StorageReadOpener = blockOpener(ctx, s) ls.NodeReifier = fc.NodeReifier protoChooser := fc.PrototypeChooser From b7dfe90b28ea139103e655e46648cfb6b14ffbc4 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Thu, 21 Oct 2021 14:08:19 -0400 Subject: [PATCH 3591/3817] feat: plumb through context changes This commit was moved from ipfs/go-path@6f599234c77d0f23f9eb62aa6c4e3d89906ba8e5 --- path/resolver/resolver_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/path/resolver/resolver_test.go b/path/resolver/resolver_test.go index b1d8dec9e..d2420af04 100644 --- a/path/resolver/resolver_test.go +++ b/path/resolver/resolver_test.go @@ -57,7 +57,7 @@ func TestRecurivePathResolution(t *testing.T) { } for _, n := range []*merkledag.ProtoNode{a, b, c} { - err = bsrv.AddBlock(n) + err = bsrv.AddBlock(ctx, n) if err != nil { t.Fatal(err) } @@ -151,7 +151,7 @@ func TestResolveToLastNode_ErrNoLink(t *testing.T) { } for _, n := range []*merkledag.ProtoNode{a, b, c} { - err = bsrv.AddBlock(n) + err = bsrv.AddBlock(ctx, n) if err != nil { t.Fatal(err) } @@ -197,7 +197,7 @@ func TestResolveToLastNode_NoUnnecessaryFetching(t *testing.T) { err := a.AddNodeLink("child", b) require.NoError(t, err) - err = bsrv.AddBlock(a) + err = bsrv.AddBlock(ctx, a) require.NoError(t, err) aKey := a.Cid() @@ -243,7 +243,7 @@ func TestPathRemainder(t *testing.T) { require.NoError(t, err) blk, err := blocks.NewBlockWithCid(out.Bytes(), lnk) require.NoError(t, err) - bsrv.AddBlock(blk) + bsrv.AddBlock(ctx, blk) fetcherFactory := bsfetcher.NewFetcherConfig(bsrv) resolver := resolver.NewBasicResolver(fetcherFactory) @@ -259,7 +259,7 @@ func TestResolveToLastNode_MixedSegmentTypes(t *testing.T) { defer cancel() bsrv := dagmock.Bserv() a := randNode() - err := bsrv.AddBlock(a) + err := bsrv.AddBlock(ctx, a) if err != nil { t.Fatal(err) } @@ -281,7 +281,7 @@ func TestResolveToLastNode_MixedSegmentTypes(t *testing.T) { require.NoError(t, err) blk, err := blocks.NewBlockWithCid(out.Bytes(), lnk) require.NoError(t, err) - bsrv.AddBlock(blk) + bsrv.AddBlock(ctx, blk) fetcherFactory := bsfetcher.NewFetcherConfig(bsrv) resolver := resolver.NewBasicResolver(fetcherFactory) From 793bb035fe1c3b7c981fbb6c36204526ca2f2cee Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Thu, 21 Oct 2021 14:11:36 -0400 Subject: [PATCH 3592/3817] Revert "feat: plumb through context changes" This reverts commit b7dfe90b28ea139103e655e46648cfb6b14ffbc4. This commit was moved from ipfs/go-path@bda72a40745b64afc01b19e619761d5470b8aa2f --- path/resolver/resolver_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/path/resolver/resolver_test.go b/path/resolver/resolver_test.go index d2420af04..b1d8dec9e 100644 --- a/path/resolver/resolver_test.go +++ b/path/resolver/resolver_test.go @@ -57,7 +57,7 @@ func TestRecurivePathResolution(t *testing.T) { } for _, n := range []*merkledag.ProtoNode{a, b, c} { - err = bsrv.AddBlock(ctx, n) + err = bsrv.AddBlock(n) if err != nil { t.Fatal(err) } @@ -151,7 +151,7 @@ func TestResolveToLastNode_ErrNoLink(t *testing.T) { } for _, n := range []*merkledag.ProtoNode{a, b, c} { - err = bsrv.AddBlock(ctx, n) + err = bsrv.AddBlock(n) if err != nil { t.Fatal(err) } @@ -197,7 +197,7 @@ func TestResolveToLastNode_NoUnnecessaryFetching(t *testing.T) { err := a.AddNodeLink("child", b) require.NoError(t, err) - err = bsrv.AddBlock(ctx, a) + err = bsrv.AddBlock(a) require.NoError(t, err) aKey := a.Cid() @@ -243,7 +243,7 @@ func TestPathRemainder(t *testing.T) { require.NoError(t, err) blk, err := blocks.NewBlockWithCid(out.Bytes(), lnk) require.NoError(t, err) - bsrv.AddBlock(ctx, blk) + bsrv.AddBlock(blk) fetcherFactory := bsfetcher.NewFetcherConfig(bsrv) resolver := resolver.NewBasicResolver(fetcherFactory) @@ -259,7 +259,7 @@ func TestResolveToLastNode_MixedSegmentTypes(t *testing.T) { defer cancel() bsrv := dagmock.Bserv() a := randNode() - err := bsrv.AddBlock(ctx, a) + err := bsrv.AddBlock(a) if err != nil { t.Fatal(err) } @@ -281,7 +281,7 @@ func TestResolveToLastNode_MixedSegmentTypes(t *testing.T) { require.NoError(t, err) blk, err := blocks.NewBlockWithCid(out.Bytes(), lnk) require.NoError(t, err) - bsrv.AddBlock(ctx, blk) + bsrv.AddBlock(blk) fetcherFactory := bsfetcher.NewFetcherConfig(bsrv) resolver := resolver.NewBasicResolver(fetcherFactory) From 27eb714bc83bb25aed64eea72ea47d78fd9b4e3a Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 21 Oct 2021 11:14:25 -0700 Subject: [PATCH 3593/3817] creation of car from file / directory (#246) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * creation of car from file / directory Co-authored-by: Daniel Martí Co-authored-by: Rod Vagg This commit was moved from ipld/go-car@9bd74165e21fd22be458fa05d8ce7dda5a507bbc --- ipld/car/cmd/car/car.go | 29 +++ ipld/car/cmd/car/create.go | 120 ++++++++++++ ipld/car/cmd/car/get.go | 9 +- ipld/car/cmd/car/index.go | 17 ++ ipld/car/cmd/car/list.go | 193 ++++++++++++++++++-- ipld/car/cmd/car/testdata/script/create.txt | 13 ++ 6 files changed, 365 insertions(+), 16 deletions(-) create mode 100644 ipld/car/cmd/car/create.go create mode 100644 ipld/car/cmd/car/testdata/script/create.txt diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index dca4c0f25..aea9f4c25 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -15,6 +15,20 @@ func main1() int { Name: "car", Usage: "Utility for working with car files", Commands: []*cli.Command{ + { + Name: "create", + Usage: "Create a car file", + Aliases: []string{"c"}, + Action: CreateCar, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "file", + Aliases: []string{"f", "output", "o"}, + Usage: "The car file to write to", + TakesFile: true, + }, + }, + }, { Name: "detach-index", Usage: "Detach an index to a detached file", @@ -72,6 +86,10 @@ func main1() int { Usage: "The type of index to write", Value: multicodec.CarMultihashIndexSorted.String(), }, + &cli.BoolFlag{ + Name: "v1", + Usage: "Write out only the carV1 file. Implies codec of 'none'", + }, }, }, { @@ -79,6 +97,17 @@ func main1() int { Aliases: []string{"l"}, Usage: "List the CIDs in a car", Action: ListCar, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "verbose", + Aliases: []string{"v"}, + Usage: "Include verbose information about contained blocks", + }, + &cli.BoolFlag{ + Name: "unixfs", + Usage: "List unixfs filesystem from the root of the car", + }, + }, }, { Name: "verify", diff --git a/ipld/car/cmd/car/create.go b/ipld/car/cmd/car/create.go new file mode 100644 index 000000000..497905381 --- /dev/null +++ b/ipld/car/cmd/car/create.go @@ -0,0 +1,120 @@ +package main + +import ( + "bytes" + "context" + "fmt" + "io" + "path" + + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-unixfsnode/data/builder" + "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/blockstore" + dagpb "github.com/ipld/go-codec-dagpb" + "github.com/ipld/go-ipld-prime" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-multihash" + "github.com/urfave/cli/v2" +) + +// CreateCar creates a car +func CreateCar(c *cli.Context) error { + var err error + if c.Args().Len() == 0 { + return fmt.Errorf("a source location to build the car from must be specified") + } + + if !c.IsSet("file") { + return fmt.Errorf("a file destination must be specified") + } + + // make a cid with the right length that we eventually will patch with the root. + hasher, err := multihash.GetHasher(multihash.SHA2_256) + if err != nil { + return err + } + digest := hasher.Sum([]byte{}) + hash, err := multihash.Encode(digest, multihash.SHA2_256) + if err != nil { + return err + } + proxyRoot := cid.NewCidV1(uint64(multicodec.DagPb), hash) + + cdest, err := blockstore.OpenReadWrite(c.String("file"), []cid.Cid{proxyRoot}) + if err != nil { + return err + } + + // Write the unixfs blocks into the store. + root, err := writeFiles(c.Context, cdest, c.Args().Slice()...) + if err != nil { + return err + } + + if err := cdest.Finalize(); err != nil { + return err + } + // re-open/finalize with the final root. + return car.ReplaceRootsInFile(c.String("file"), []cid.Cid{root}) +} + +func writeFiles(ctx context.Context, bs *blockstore.ReadWrite, paths ...string) (cid.Cid, error) { + ls := cidlink.DefaultLinkSystem() + ls.TrustedStorage = true + ls.StorageReadOpener = func(_ ipld.LinkContext, l ipld.Link) (io.Reader, error) { + cl, ok := l.(cidlink.Link) + if !ok { + return nil, fmt.Errorf("not a cidlink") + } + blk, err := bs.Get(cl.Cid) + if err != nil { + return nil, err + } + return bytes.NewBuffer(blk.RawData()), nil + } + ls.StorageWriteOpener = func(_ ipld.LinkContext) (io.Writer, ipld.BlockWriteCommitter, error) { + buf := bytes.NewBuffer(nil) + return buf, func(l ipld.Link) error { + cl, ok := l.(cidlink.Link) + if !ok { + return fmt.Errorf("not a cidlink") + } + blk, err := blocks.NewBlockWithCid(buf.Bytes(), cl.Cid) + if err != nil { + return err + } + bs.Put(blk) + return nil + }, nil + } + + topLevel := make([]dagpb.PBLink, 0, len(paths)) + for _, p := range paths { + l, size, err := builder.BuildUnixFSRecursive(p, &ls) + if err != nil { + return cid.Undef, err + } + name := path.Base(p) + entry, err := builder.BuildUnixFSDirectoryEntry(name, int64(size), l) + if err != nil { + return cid.Undef, err + } + topLevel = append(topLevel, entry) + } + + // make a directory for the file(s). + + root, err := builder.BuildUnixFSDirectory(topLevel, &ls) + if err != nil { + return cid.Undef, nil + } + rcl, ok := root.(cidlink.Link) + if !ok { + return cid.Undef, fmt.Errorf("could not interpret %s", root) + } + + return rcl.Cid, nil +} diff --git a/ipld/car/cmd/car/get.go b/ipld/car/cmd/car/get.go index 9cdc7a4cc..f13733136 100644 --- a/ipld/car/cmd/car/get.go +++ b/ipld/car/cmd/car/get.go @@ -85,8 +85,9 @@ func GetCarDag(c *cli.Context) error { } ls := cidlink.DefaultLinkSystem() + ls.TrustedStorage = true ls.StorageReadOpener = func(_ linking.LinkContext, l datamodel.Link) (io.Reader, error) { - if cl, ok := l.(*cidlink.Link); ok { + if cl, ok := l.(cidlink.Link); ok { blk, err := bs.Get(cl.Cid) if err != nil { if err == ipfsbs.ErrNotFound { @@ -104,7 +105,7 @@ func GetCarDag(c *cli.Context) error { ls.StorageWriteOpener = func(_ linking.LinkContext) (io.Writer, linking.BlockWriteCommitter, error) { buf := bytes.NewBuffer(nil) return buf, func(l datamodel.Link) error { - if cl, ok := l.(*cidlink.Link); ok { + if cl, ok := l.(cidlink.Link); ok { blk, err := blocks.NewBlockWithCid(buf.Bytes(), cl.Cid) if err != nil { return err @@ -124,7 +125,7 @@ func GetCarDag(c *cli.Context) error { } // selector traversal - s := selectorParser.CommonSelector_MatchAllRecursively + s, _ := selector.CompileSelector(selectorParser.CommonSelector_MatchAllRecursively) if c.IsSet("selector") { sn, err := selectorParser.ParseJSONSelector(c.String("selector")) if err != nil { @@ -141,7 +142,7 @@ func GetCarDag(c *cli.Context) error { } err = traversal.WalkMatching(node, s, func(p traversal.Progress, n datamodel.Node) error { if p.LastBlock.Link != nil { - if cl, ok := p.LastBlock.Link.(*cidlink.Link); ok { + if cl, ok := p.LastBlock.Link.(cidlink.Link); ok { lnkProto = cidlink.LinkPrototype{ Prefix: cl.Prefix(), } diff --git a/ipld/car/cmd/car/index.go b/ipld/car/cmd/car/index.go index a1fa6d864..ac3688861 100644 --- a/ipld/car/cmd/car/index.go +++ b/ipld/car/cmd/car/index.go @@ -23,6 +23,23 @@ func IndexCar(c *cli.Context) error { } defer r.Close() + if c.Bool("v1") { + if c.IsSet("codec") && c.String("codec") != "none" { + return fmt.Errorf("only supported codec for a v1 car is 'none'") + } + outStream := os.Stdout + if c.Args().Len() >= 2 { + outStream, err = os.Create(c.Args().Get(1)) + if err != nil { + return err + } + } + defer outStream.Close() + + _, err := io.Copy(outStream, r.DataReader()) + return err + } + var idx index.Index if c.String("codec") != "none" { var mc multicodec.Code diff --git a/ipld/car/cmd/car/list.go b/ipld/car/cmd/car/list.go index e9cf1f7e1..2912b1d33 100644 --- a/ipld/car/cmd/car/list.go +++ b/ipld/car/cmd/car/list.go @@ -1,39 +1,51 @@ package main import ( + "bytes" "fmt" "io" "os" + "path" + "github.com/dustin/go-humanize" + "github.com/ipfs/go-cid" + data "github.com/ipfs/go-unixfsnode/data" + "github.com/ipfs/go-unixfsnode/hamt" carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/blockstore" + dagpb "github.com/ipld/go-codec-dagpb" + "github.com/ipld/go-ipld-prime" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + "github.com/multiformats/go-multicodec" "github.com/urfave/cli/v2" ) // ListCar is a command to output the cids in a car. func ListCar(c *cli.Context) error { - inStream := os.Stdin var err error - if c.Args().Len() >= 1 { - inStream, err = os.Open(c.Args().First()) + outStream := os.Stdout + if c.Args().Len() >= 2 { + outStream, err = os.Create(c.Args().Get(1)) if err != nil { return err } - defer inStream.Close() } - rd, err := carv2.NewBlockReader(inStream) - if err != nil { - return err + defer outStream.Close() + + if c.Bool("unixfs") { + return listUnixfs(c, outStream) } - outStream := os.Stdout - if c.Args().Len() >= 2 { - outStream, err = os.Create(c.Args().Get(1)) + inStream := os.Stdin + if c.Args().Len() >= 1 { + inStream, err = os.Open(c.Args().First()) if err != nil { return err } + defer inStream.Close() } - defer outStream.Close() + rd, err := carv2.NewBlockReader(inStream) if err != nil { return err } @@ -46,8 +58,165 @@ func ListCar(c *cli.Context) error { } return err } - fmt.Fprintf(outStream, "%s\n", blk.Cid()) + if c.Bool("verbose") { + fmt.Fprintf(outStream, "%s: %s\n", + multicodec.Code(blk.Cid().Prefix().Codec).String(), + blk.Cid()) + if blk.Cid().Prefix().Codec == uint64(multicodec.DagPb) { + // parse as dag-pb + builder := dagpb.Type.PBNode.NewBuilder() + if err := dagpb.DecodeBytes(builder, blk.RawData()); err != nil { + fmt.Fprintf(outStream, "\tnot interpretable as dag-pb: %s\n", err) + continue + } + n := builder.Build() + pbn, ok := n.(dagpb.PBNode) + if !ok { + continue + } + dl := 0 + if pbn.Data.Exists() { + dl = len(pbn.Data.Must().Bytes()) + } + fmt.Fprintf(outStream, "\t%d links. %d bytes\n", pbn.Links.Length(), dl) + // example link: + li := pbn.Links.ListIterator() + max := 3 + for !li.Done() { + _, l, _ := li.Next() + max-- + pbl, ok := l.(dagpb.PBLink) + if ok && max >= 0 { + hsh := "" + lnk, ok := pbl.Hash.Link().(cidlink.Link) + if ok { + hsh = lnk.Cid.String() + } + name := "" + if pbl.Name.Exists() { + name = pbl.Name.Must().String() + } + size := 0 + if pbl.Tsize.Exists() { + size = int(pbl.Tsize.Must().Int()) + } + fmt.Fprintf(outStream, "\t\t%s[%s] %s\n", name, humanize.Bytes(uint64(size)), hsh) + } + } + if max < 0 { + fmt.Fprintf(outStream, "\t\t(%d total)\n", 3-max) + } + // see if it's unixfs. + ufd, err := data.DecodeUnixFSData(pbn.Data.Must().Bytes()) + if err != nil { + fmt.Fprintf(outStream, "\tnot interpretable as unixfs: %s\n", err) + continue + } + fmt.Fprintf(outStream, "\tUnixfs %s\n", data.DataTypeNames[ufd.FieldDataType().Int()]) + } + } else { + fmt.Fprintf(outStream, "%s\n", blk.Cid()) + } } return err } + +func listUnixfs(c *cli.Context, outStream io.Writer) error { + if c.Args().Len() == 0 { + return fmt.Errorf("must provide file to read from. unixfs reading requires random access") + } + + bs, err := blockstore.OpenReadOnly(c.Args().First()) + if err != nil { + return err + } + ls := cidlink.DefaultLinkSystem() + ls.TrustedStorage = true + ls.StorageReadOpener = func(_ ipld.LinkContext, l ipld.Link) (io.Reader, error) { + cl, ok := l.(cidlink.Link) + if !ok { + return nil, fmt.Errorf("not a cidlink") + } + blk, err := bs.Get(cl.Cid) + if err != nil { + return nil, err + } + return bytes.NewBuffer(blk.RawData()), nil + } + + roots, err := bs.Roots() + if err != nil { + return err + } + for _, r := range roots { + if err := printUnixFSNode(c, "", r, &ls, outStream); err != nil { + return err + } + } + return nil +} + +func printUnixFSNode(c *cli.Context, prefix string, node cid.Cid, ls *ipld.LinkSystem, outStream io.Writer) error { + // it might be a raw file (bytes) node. if so, not actually an error. + if node.Prefix().Codec == cid.Raw { + return nil + } + + pbn, err := ls.Load(ipld.LinkContext{}, cidlink.Link{Cid: node}, dagpb.Type.PBNode) + if err != nil { + return err + } + + pbnode := pbn.(dagpb.PBNode) + + ufd, err := data.DecodeUnixFSData(pbnode.Data.Must().Bytes()) + if err != nil { + return err + } + + if ufd.FieldDataType().Int() == data.Data_Directory { + i := pbnode.Links.Iterator() + for !i.Done() { + _, l := i.Next() + name := path.Join(prefix, l.Name.Must().String()) + fmt.Fprintf(outStream, "%s\n", name) + // recurse into the file/directory + cl, err := l.Hash.AsLink() + if err != nil { + return err + } + if cidl, ok := cl.(cidlink.Link); ok { + if err := printUnixFSNode(c, name, cidl.Cid, ls, outStream); err != nil { + return err + } + } + + } + } else if ufd.FieldDataType().Int() == data.Data_HAMTShard { + hn, err := hamt.AttemptHAMTShardFromNode(c.Context, pbn, ls) + if err != nil { + return err + } + i := hn.Iterator() + for !i.Done() { + n, l := i.Next() + fmt.Fprintf(outStream, "%s\n", path.Join(prefix, n.String())) + // recurse into the file/directory + cl, err := l.AsLink() + if err != nil { + return err + } + if cidl, ok := cl.(cidlink.Link); ok { + if err := printUnixFSNode(c, path.Join(prefix, n.String()), cidl.Cid, ls, outStream); err != nil { + return err + } + } + } + } else { + // file, file chunk, symlink, other un-named entities. + return nil + } + + return nil +} diff --git a/ipld/car/cmd/car/testdata/script/create.txt b/ipld/car/cmd/car/testdata/script/create.txt new file mode 100644 index 000000000..13849e31a --- /dev/null +++ b/ipld/car/cmd/car/testdata/script/create.txt @@ -0,0 +1,13 @@ +car create --file=out.car foo.txt bar.txt + +car verify out.car +car list --unixfs out.car +stdout -count=2 'txt$' +car list out.car +stdout -count=3 '^baf' +stdout -count=2 '^bafk' + +-- foo.txt -- +foo content +-- bar.txt -- +bar content From 66b393c6a8b9104446debfe61e79b4beec427b98 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 21 Oct 2021 21:36:04 +1100 Subject: [PATCH 3594/3817] feat: fix get-dag and add version=1 option This commit was moved from ipld/go-car@4a8d623fa17148133077b1b85c49dce14eb1de98 --- ipld/car/cmd/car/car.go | 5 ++ ipld/car/cmd/car/get.go | 121 +++++++++++++++++++++++----------------- 2 files changed, 76 insertions(+), 50 deletions(-) diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index aea9f4c25..76a1ed498 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -72,6 +72,11 @@ func main1() int { Name: "strict", Usage: "Fail if the selector finds links to blocks not in the original car", }, + &cli.IntFlag{ + Name: "version", + Value: 2, + Usage: "Write output as a v1 or v2 format car", + }, }, }, { diff --git a/ipld/car/cmd/car/get.go b/ipld/car/cmd/car/get.go index f13733136..324f050ad 100644 --- a/ipld/car/cmd/car/get.go +++ b/ipld/car/cmd/car/get.go @@ -2,20 +2,22 @@ package main import ( "bytes" + "context" "fmt" "io" "os" - _ "github.com/ipld/go-codec-dagpb" + dagpb "github.com/ipld/go-codec-dagpb" + "github.com/ipld/go-ipld-prime" _ "github.com/ipld/go-ipld-prime/codec/cbor" _ "github.com/ipld/go-ipld-prime/codec/dagcbor" _ "github.com/ipld/go-ipld-prime/codec/dagjson" _ "github.com/ipld/go-ipld-prime/codec/json" _ "github.com/ipld/go-ipld-prime/codec/raw" - blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ipfsbs "github.com/ipfs/go-ipfs-blockstore" + "github.com/ipld/go-car" "github.com/ipld/go-car/v2/blockstore" "github.com/ipld/go-ipld-prime/datamodel" "github.com/ipld/go-ipld-prime/linking" @@ -68,18 +70,45 @@ func GetCarDag(c *cli.Context) error { return fmt.Errorf("usage: car get-dag [-s selector] ") } - bs, err := blockstore.OpenReadOnly(c.Args().Get(0)) + // string to CID for the root of the DAG to extract + rootCid, err := cid.Parse(c.Args().Get(1)) if err != nil { return err } - // string to CID - blkCid, err := cid.Parse(c.Args().Get(1)) + bs, err := blockstore.OpenReadOnly(c.Args().Get(0)) if err != nil { return err } - outStore, err := blockstore.OpenReadWrite(c.Args().Get(2), []cid.Cid{blkCid}) + output := c.Args().Get(2) + strict := c.Bool("strict") + + // selector traversal, default to ExploreAllRecursively which only explores the DAG blocks + // because we only care about the blocks loaded during the walk, not the nodes matched + sel := selectorParser.CommonSelector_ExploreAllRecursively + if c.IsSet("selector") { + sel, err = selectorParser.ParseJSONSelector(c.String("selector")) + if err != nil { + return err + } + } + linkVisitOnlyOnce := !c.IsSet("selector") // if using a custom selector, this isn't as safe + + switch c.Int("version") { + case 2: + return writeCarV2(rootCid, output, bs, strict, sel, linkVisitOnlyOnce) + case 1: + return writeCarV1(rootCid, output, bs, strict, sel, linkVisitOnlyOnce) + default: + return fmt.Errorf("invalid CAR version %d", c.Int("version")) + } +} + +func writeCarV2(rootCid cid.Cid, output string, bs *blockstore.ReadOnly, strict bool, sel datamodel.Node, linkVisitOnlyOnce bool) error { + _ = os.Remove(output) + + outStore, err := blockstore.OpenReadWrite(output, []cid.Cid{rootCid}, blockstore.AllowDuplicatePuts(false)) if err != nil { return err } @@ -91,7 +120,7 @@ func GetCarDag(c *cli.Context) error { blk, err := bs.Get(cl.Cid) if err != nil { if err == ipfsbs.ErrNotFound { - if c.Bool("strict") { + if strict { return nil, err } return nil, traversal.SkipMe{} @@ -102,61 +131,53 @@ func GetCarDag(c *cli.Context) error { } return nil, fmt.Errorf("unknown link type: %T", l) } - ls.StorageWriteOpener = func(_ linking.LinkContext) (io.Writer, linking.BlockWriteCommitter, error) { - buf := bytes.NewBuffer(nil) - return buf, func(l datamodel.Link) error { - if cl, ok := l.(cidlink.Link); ok { - blk, err := blocks.NewBlockWithCid(buf.Bytes(), cl.Cid) - if err != nil { - return err - } - return outStore.Put(blk) - } - return fmt.Errorf("unknown link type: %T", l) - }, nil - } - rootlnk := cidlink.Link{ - Cid: blkCid, + nsc := func(lnk datamodel.Link, lctx ipld.LinkContext) (datamodel.NodePrototype, error) { + if lnk, ok := lnk.(cidlink.Link); ok && lnk.Cid.Prefix().Codec == 0x70 { + return dagpb.Type.PBNode, nil + } + return basicnode.Prototype.Any, nil } - node, err := ls.Load(linking.LinkContext{}, rootlnk, basicnode.Prototype.Any) + + rootLink := cidlink.Link{Cid: rootCid} + ns, _ := nsc(rootLink, ipld.LinkContext{}) + rootNode, err := ls.Load(ipld.LinkContext{}, rootLink, ns) if err != nil { return err } - // selector traversal - s, _ := selector.CompileSelector(selectorParser.CommonSelector_MatchAllRecursively) - if c.IsSet("selector") { - sn, err := selectorParser.ParseJSONSelector(c.String("selector")) - if err != nil { - return err - } - s, err = selector.CompileSelector(sn) - if err != nil { - return err - } + traversalProgress := traversal.Progress{ + Cfg: &traversal.Config{ + LinkSystem: ls, + LinkTargetNodePrototypeChooser: nsc, + LinkVisitOnlyOnce: linkVisitOnlyOnce, + }, } - lnkProto := cidlink.LinkPrototype{ - Prefix: blkCid.Prefix(), + s, err := selector.CompileSelector(sel) + if err != nil { + return err } - err = traversal.WalkMatching(node, s, func(p traversal.Progress, n datamodel.Node) error { - if p.LastBlock.Link != nil { - if cl, ok := p.LastBlock.Link.(cidlink.Link); ok { - lnkProto = cidlink.LinkPrototype{ - Prefix: cl.Prefix(), - } - } - } - _, err = ls.Store(linking.LinkContext{}, lnkProto, n) - if err != nil { - return err - } - return nil - }) + + err = traversalProgress.WalkMatching(rootNode, s, func(p traversal.Progress, n datamodel.Node) error { return nil }) if err != nil { return err } return outStore.Finalize() } + +func writeCarV1(rootCid cid.Cid, output string, bs *blockstore.ReadOnly, strict bool, sel datamodel.Node, linkVisitOnlyOnce bool) error { + opts := make([]car.Option, 0) + if linkVisitOnlyOnce { + opts = append(opts, car.TraverseLinksOnlyOnce()) + } + sc := car.NewSelectiveCar(context.Background(), bs, []car.Dag{{Root: rootCid, Selector: sel}}, opts...) + f, err := os.Create(output) + if err != nil { + return err + } + defer f.Close() + + return sc.Write(f) +} From 6ea6de457ac8a180f0b97e195fcb3df6e1c2deb9 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 22 Oct 2021 12:49:43 +1100 Subject: [PATCH 3595/3817] fix!: use -version=n instead of -v1 for index command This commit was moved from ipld/go-car@5a583de9641de1f2f6d20dc46b5a0313b67d7f81 --- ipld/car/cmd/car/car.go | 7 ++++--- ipld/car/cmd/car/index.go | 8 ++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index 76a1ed498..850673c6f 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -91,9 +91,10 @@ func main1() int { Usage: "The type of index to write", Value: multicodec.CarMultihashIndexSorted.String(), }, - &cli.BoolFlag{ - Name: "v1", - Usage: "Write out only the carV1 file. Implies codec of 'none'", + &cli.IntFlag{ + Name: "version", + Value: 2, + Usage: "Write output as a v1 or v2 format car", }, }, }, diff --git a/ipld/car/cmd/car/index.go b/ipld/car/cmd/car/index.go index ac3688861..153c0b7c9 100644 --- a/ipld/car/cmd/car/index.go +++ b/ipld/car/cmd/car/index.go @@ -23,9 +23,9 @@ func IndexCar(c *cli.Context) error { } defer r.Close() - if c.Bool("v1") { + if c.Int("version") == 1 { if c.IsSet("codec") && c.String("codec") != "none" { - return fmt.Errorf("only supported codec for a v1 car is 'none'") + return fmt.Errorf("'none' is the only supported codec for a v1 car") } outStream := os.Stdout if c.Args().Len() >= 2 { @@ -40,6 +40,10 @@ func IndexCar(c *cli.Context) error { return err } + if c.Int("version") != 2 { + return fmt.Errorf("invalid CAR version %d", c.Int("version")) + } + var idx index.Index if c.String("codec") != "none" { var mc multicodec.Code From b0665591e329a76f05bece9a16cf2b0728577054 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Thu, 28 Oct 2021 10:21:57 -0400 Subject: [PATCH 3596/3817] feat: add context to interface (#18) This commit was moved from ipfs/go-ipfs-exchange-interface@09692a6b268b868db071fc36a2afc64ef7b01a76 --- exchange/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index c3032b235..7640c5733 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -15,7 +15,7 @@ type Interface interface { // type Exchanger interface // TODO Should callers be concerned with whether the block was made // available on the network? - HasBlock(blocks.Block) error + HasBlock(context.Context, blocks.Block) error IsOnline() bool From d041c547b61567d4e7bcbfc22825d673e4b606b2 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Fri, 29 Oct 2021 16:30:27 -0400 Subject: [PATCH 3597/3817] feat: plumb through context changes (#28) This commit was moved from ipfs/go-ipfs-routing@be1ea983aa42904278cc0a8717c44c44dd858064 --- routing/offline/offline.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/offline/offline.go b/routing/offline/offline.go index c76f92098..0b3083c59 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -67,11 +67,11 @@ func (c *offlineRouting) PutValue(ctx context.Context, key string, val []byte, _ return err } - return c.datastore.Put(dshelp.NewKeyFromBinary([]byte(key)), data) + return c.datastore.Put(ctx, dshelp.NewKeyFromBinary([]byte(key)), data) } func (c *offlineRouting) GetValue(ctx context.Context, key string, _ ...routing.Option) ([]byte, error) { - buf, err := c.datastore.Get(dshelp.NewKeyFromBinary([]byte(key))) + buf, err := c.datastore.Get(ctx, dshelp.NewKeyFromBinary([]byte(key))) if err != nil { return nil, err } From 815dd33f8cefacb6203c459b2b673aea9eb1af0e Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 1 Nov 2021 02:22:43 -0700 Subject: [PATCH 3598/3817] Add a barebones readme to the car CLI (#262) This commit was moved from ipld/go-car@85f0751677fa4115a44455d16ae1d060bc078104 --- ipld/car/README.md | 2 +- ipld/car/cmd/car/README.md | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 ipld/car/cmd/car/README.md diff --git a/ipld/car/README.md b/ipld/car/README.md index 7ccf7cca3..0f2157991 100644 --- a/ipld/car/README.md +++ b/ipld/car/README.md @@ -3,7 +3,7 @@ go-car (go!) [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](https://protocol.ai) [![](https://img.shields.io/badge/project-ipld-orange.svg?style=flat-square)](https://github.com/ipld/ipld) -[![](https://img.shields.io/badge/freenode-%23ipld-orange.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipld) +[![](https://img.shields.io/badge/matrix-%23ipld-blue.svg?style=flat-square)](https://matrix.to/#/#ipld:ipfs.io) [![Go Reference](https://pkg.go.dev/badge/github.com/ipld/go-car.svg)](https://pkg.go.dev/github.com/ipld/go-car) [![Coverage Status](https://codecov.io/gh/ipld/go-car/branch/master/graph/badge.svg)](https://codecov.io/gh/ipld/go-car/branch/master) diff --git a/ipld/car/cmd/car/README.md b/ipld/car/cmd/car/README.md new file mode 100644 index 000000000..f3825f0d2 --- /dev/null +++ b/ipld/car/cmd/car/README.md @@ -0,0 +1,33 @@ +car - The CLI tool +================== + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](https://protocol.ai) +[![](https://img.shields.io/badge/project-ipld-orange.svg?style=flat-square)](https://github.com/ipld/ipld) +[![](https://img.shields.io/badge/matrix-%23ipld-blue.svg?style=flat-square)](https://matrix.to/#/#ipld:ipfs.io) + +> A CLI to interact with car files + +## Usage + +``` +USAGE: + car [global options] command [command options] [arguments...] + +COMMANDS: + create, c Create a car file + detach-index Detach an index to a detached file + filter, f Filter the CIDs in a car + get-block, gb Get a block out of a car + get-dag, gd Get a dag out of a car + index, i write out the car with an index + list, l List the CIDs in a car + verify, v Verify a CAR is wellformed + help, h Shows a list of commands or help for one command +``` + +## Install + +To install the latest version of `car` module, run: +```shell script +go install github.com/ipld/go-car/cmd/car +``` From a8d200c1b313f82188dbb335baf926d1bc704d0d Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 9 Nov 2021 02:29:01 -0800 Subject: [PATCH 3599/3817] support extraction of unixfs content stored in car files (#263) * support extraction of unixfs content stored in car files This commit was moved from ipld/go-car@6d94b7b90cf3d267d566223cd386a2dd36e6749f --- ipld/car/cmd/car/car.go | 20 ++ ipld/car/cmd/car/extract.go | 228 ++++++++++++++++++ .../car/testdata/script/create-extract.txt | 12 + 3 files changed, 260 insertions(+) create mode 100644 ipld/car/cmd/car/extract.go create mode 100644 ipld/car/cmd/car/testdata/script/create-extract.txt diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index 850673c6f..43b7307d7 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -34,6 +34,26 @@ func main1() int { Usage: "Detach an index to a detached file", Action: DetachCar, }, + { + Name: "extract", + Aliases: []string{"x"}, + Usage: "Extract the contents of a car when the car encodes UnixFS data", + Action: ExtractCar, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "file", + Aliases: []string{"f"}, + Usage: "The car file to extract from", + Required: true, + TakesFile: true, + }, + &cli.BoolFlag{ + Name: "verbose", + Aliases: []string{"v"}, + Usage: "Include verbose information about extracted contents", + }, + }, + }, { Name: "filter", Aliases: []string{"f"}, diff --git a/ipld/car/cmd/car/extract.go b/ipld/car/cmd/car/extract.go new file mode 100644 index 000000000..4a56e1775 --- /dev/null +++ b/ipld/car/cmd/car/extract.go @@ -0,0 +1,228 @@ +package main + +import ( + "bytes" + "fmt" + "io" + "os" + "path" + "path/filepath" + + "github.com/ipfs/go-cid" + "github.com/ipfs/go-unixfsnode" + "github.com/ipfs/go-unixfsnode/data" + "github.com/ipfs/go-unixfsnode/file" + "github.com/ipld/go-car/v2/blockstore" + dagpb "github.com/ipld/go-codec-dagpb" + "github.com/ipld/go-ipld-prime" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/urfave/cli/v2" +) + +// ExtractCar pulls files and directories out of a car +func ExtractCar(c *cli.Context) error { + outputDir, err := os.Getwd() + if err != nil { + return err + } + if c.Args().Present() { + outputDir = c.Args().First() + } + + bs, err := blockstore.OpenReadOnly(c.String("file")) + if err != nil { + return err + } + + ls := cidlink.DefaultLinkSystem() + ls.TrustedStorage = true + ls.StorageReadOpener = func(_ ipld.LinkContext, l ipld.Link) (io.Reader, error) { + cl, ok := l.(cidlink.Link) + if !ok { + return nil, fmt.Errorf("not a cidlink") + } + blk, err := bs.Get(cl.Cid) + if err != nil { + return nil, err + } + return bytes.NewBuffer(blk.RawData()), nil + } + + roots, err := bs.Roots() + if err != nil { + return err + } + + for _, root := range roots { + if err := extractRoot(c, &ls, root, outputDir); err != nil { + return err + } + } + + return nil +} + +func extractRoot(c *cli.Context, ls *ipld.LinkSystem, root cid.Cid, outputDir string) error { + if root.Prefix().Codec == cid.Raw { + if c.IsSet("verbose") { + fmt.Fprintf(c.App.ErrWriter, "skipping raw root %s\n", root) + } + return nil + } + + pbn, err := ls.Load(ipld.LinkContext{}, cidlink.Link{Cid: root}, dagpb.Type.PBNode) + if err != nil { + return err + } + pbnode := pbn.(dagpb.PBNode) + + ufn, err := unixfsnode.Reify(ipld.LinkContext{}, pbnode, ls) + if err != nil { + return err + } + + outputResolvedDir, err := filepath.EvalSymlinks(outputDir) + if err != nil { + return err + } + if _, err := os.Stat(outputResolvedDir); os.IsNotExist(err) { + if err := os.Mkdir(outputResolvedDir, 0755); err != nil { + return err + } + } + if err := extractDir(c, ls, ufn, outputResolvedDir, "/"); err != nil { + return fmt.Errorf("%s: %w", root, err) + } + + return nil +} + +func resolvePath(root, pth string) (string, error) { + rp, err := filepath.Rel("/", pth) + if err != nil { + return "", fmt.Errorf("couldn't check relative-ness of %s: %w", pth, err) + } + joined := path.Join(root, rp) + + basename := path.Dir(joined) + final, err := filepath.EvalSymlinks(basename) + if err != nil { + return "", fmt.Errorf("couldn't eval symlinks in %s: %w", basename, err) + } + if final != path.Clean(basename) { + return "", fmt.Errorf("path attempts to redirect through symlinks") + } + return joined, nil +} + +func extractDir(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputRoot, outputPath string) error { + dirPath, err := resolvePath(outputRoot, outputPath) + if err != nil { + return err + } + // make the directory. + if err := os.MkdirAll(dirPath, 0755); err != nil { + return err + } + + if n.Kind() == ipld.Kind_Map { + mi := n.MapIterator() + for !mi.Done() { + key, val, err := mi.Next() + if err != nil { + return err + } + ks, err := key.AsString() + if err != nil { + return err + } + nextRes, err := resolvePath(outputRoot, path.Join(outputPath, ks)) + if err != nil { + return err + } + if c.IsSet("verbose") { + fmt.Fprintf(c.App.Writer, "%s\n", nextRes) + } + + if val.Kind() != ipld.Kind_Link { + return fmt.Errorf("unexpected map value for %s at %s", ks, outputPath) + } + // a directory may be represented as a map of name: if unixADL is applied + vl, err := val.AsLink() + if err != nil { + return err + } + dest, err := ls.Load(ipld.LinkContext{}, vl, basicnode.Prototype.Any) + if err != nil { + return err + } + // degenerate files are handled here. + if dest.Kind() == ipld.Kind_Bytes { + if err := extractFile(c, ls, dest, nextRes); err != nil { + return err + } + continue + } else { + // dir / pbnode + pbb := dagpb.Type.PBNode.NewBuilder() + if err := pbb.AssignNode(dest); err != nil { + return err + } + dest = pbb.Build() + } + pbnode := dest.(dagpb.PBNode) + + // interpret dagpb 'data' as unixfs data and look at type. + ufsData, err := pbnode.LookupByString("Data") + if err != nil { + return err + } + ufsBytes, err := ufsData.AsBytes() + if err != nil { + return err + } + ufsNode, err := data.DecodeUnixFSData(ufsBytes) + if err != nil { + return err + } + if ufsNode.DataType.Int() == data.Data_Directory || ufsNode.DataType.Int() == data.Data_HAMTShard { + ufn, err := unixfsnode.Reify(ipld.LinkContext{}, pbnode, ls) + if err != nil { + return err + } + + if err := extractDir(c, ls, ufn, outputRoot, path.Join(outputPath, ks)); err != nil { + return err + } + } else if ufsNode.DataType.Int() == data.Data_File || ufsNode.DataType.Int() == data.Data_Raw { + if err := extractFile(c, ls, pbnode, nextRes); err != nil { + return err + } + } else if ufsNode.DataType.Int() == data.Data_Symlink { + data := ufsNode.Data.Must().Bytes() + if err := os.Symlink(string(data), nextRes); err != nil { + return err + } + } + } + return nil + } + return fmt.Errorf("not a directory") +} + +func extractFile(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputName string) error { + node, err := file.NewUnixFSFile(c.Context, n, ls) + if err != nil { + return err + } + + f, err := os.Create(outputName) + if err != nil { + return err + } + defer f.Close() + _, err = io.Copy(f, node) + + return err +} diff --git a/ipld/car/cmd/car/testdata/script/create-extract.txt b/ipld/car/cmd/car/testdata/script/create-extract.txt new file mode 100644 index 000000000..648bafc49 --- /dev/null +++ b/ipld/car/cmd/car/testdata/script/create-extract.txt @@ -0,0 +1,12 @@ +car create --file=out.car foo.txt bar.txt +mkdir out +car extract -v -f out.car out +! stderr . +stdout -count=2 'txt$' +car create --file=out2.car out/foo.txt out/bar.txt +cmp out.car out2.car + +-- foo.txt -- +foo content +-- bar.txt -- +bar content From d1cf54c89982b7175e722d3bc59afcccbb6b870d Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Wed, 10 Nov 2021 12:43:08 -0500 Subject: [PATCH 3600/3817] feat: plumb through contexts (#42) This commit was moved from ipfs/go-ipfs-exchange-offline@6ef3e0ac8dfd20522c918e1eb6f8ac07cec41bd1 --- exchange/offline/offline.go | 10 +++++----- exchange/offline/offline_test.go | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 88d04469b..73622659b 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -24,13 +24,13 @@ type offlineExchange struct { // GetBlock returns nil to signal that a block could not be retrieved for the // given key. // NB: This function may return before the timeout expires. -func (e *offlineExchange) GetBlock(_ context.Context, k cid.Cid) (blocks.Block, error) { - return e.bs.Get(k) +func (e *offlineExchange) GetBlock(ctx context.Context, k cid.Cid) (blocks.Block, error) { + return e.bs.Get(ctx, k) } // HasBlock always returns nil. -func (e *offlineExchange) HasBlock(b blocks.Block) error { - return e.bs.Put(b) +func (e *offlineExchange) HasBlock(ctx context.Context, b blocks.Block) error { + return e.bs.Put(ctx, b) } // Close always returns nil. @@ -45,7 +45,7 @@ func (e *offlineExchange) GetBlocks(ctx context.Context, ks []cid.Cid) (<-chan b go func() { defer close(out) for _, k := range ks { - hit, err := e.bs.Get(k) + hit, err := e.bs.Get(ctx, k) if err != nil { // a long line of misses should abort when context is cancelled. select { diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 3b84b8c1e..0ac95a6b6 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -28,12 +28,12 @@ func TestHasBlockReturnsNil(t *testing.T) { ex := Exchange(store) block := blocks.NewBlock([]byte("data")) - err := ex.HasBlock(block) + err := ex.HasBlock(context.Background(), block) if err != nil { t.Fail() } - if _, err := store.Get(block.Cid()); err != nil { + if _, err := store.Get(context.Background(), block.Cid()); err != nil { t.Fatal(err) } } @@ -46,7 +46,7 @@ func TestGetBlocks(t *testing.T) { expected := g.Blocks(2) for _, b := range expected { - if err := ex.HasBlock(b); err != nil { + if err := ex.HasBlock(context.Background(), b); err != nil { t.Fail() } } From 378fb2130fd761e11b00c9c7b9b756efe8839e22 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Wed, 10 Nov 2021 14:33:32 -0500 Subject: [PATCH 3601/3817] feat: add context to interfaces (#86) This commit was moved from ipfs/go-blockservice@285f9bc81cd0f4eeff330aa162b493583b113c16 --- blockservice/blockservice.go | 30 +++++++++++++++--------------- blockservice/blockservice_test.go | 14 +++++++------- blockservice/test/blocks_test.go | 4 ++-- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 6346bec5f..66905a677 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -51,14 +51,14 @@ type BlockService interface { Exchange() exchange.Interface // AddBlock puts a given block to the underlying datastore - AddBlock(o blocks.Block) error + AddBlock(ctx context.Context, o blocks.Block) error // AddBlocks adds a slice of blocks at the same time using batching // capabilities of the underlying datastore whenever possible. - AddBlocks(bs []blocks.Block) error + AddBlocks(ctx context.Context, bs []blocks.Block) error // DeleteBlock deletes the given block from the blockservice. - DeleteBlock(o cid.Cid) error + DeleteBlock(ctx context.Context, o cid.Cid) error } type blockService struct { @@ -130,7 +130,7 @@ func NewSession(ctx context.Context, bs BlockService) *Session { // AddBlock adds a particular block to the service, Putting it into the datastore. // TODO pass a context into this if the remote.HasBlock is going to remain here. -func (s *blockService) AddBlock(o blocks.Block) error { +func (s *blockService) AddBlock(ctx context.Context, o blocks.Block) error { c := o.Cid() // hash security err := verifcid.ValidateCid(c) @@ -138,19 +138,19 @@ func (s *blockService) AddBlock(o blocks.Block) error { return err } if s.checkFirst { - if has, err := s.blockstore.Has(c); has || err != nil { + if has, err := s.blockstore.Has(ctx, c); has || err != nil { return err } } - if err := s.blockstore.Put(o); err != nil { + if err := s.blockstore.Put(ctx, o); err != nil { return err } log.Debugf("BlockService.BlockAdded %s", c) if s.exchange != nil { - if err := s.exchange.HasBlock(o); err != nil { + if err := s.exchange.HasBlock(ctx, o); err != nil { log.Errorf("HasBlock: %s", err.Error()) } } @@ -158,7 +158,7 @@ func (s *blockService) AddBlock(o blocks.Block) error { return nil } -func (s *blockService) AddBlocks(bs []blocks.Block) error { +func (s *blockService) AddBlocks(ctx context.Context, bs []blocks.Block) error { // hash security for _, b := range bs { err := verifcid.ValidateCid(b.Cid()) @@ -170,7 +170,7 @@ func (s *blockService) AddBlocks(bs []blocks.Block) error { if s.checkFirst { toput = make([]blocks.Block, 0, len(bs)) for _, b := range bs { - has, err := s.blockstore.Has(b.Cid()) + has, err := s.blockstore.Has(ctx, b.Cid()) if err != nil { return err } @@ -186,7 +186,7 @@ func (s *blockService) AddBlocks(bs []blocks.Block) error { return nil } - err := s.blockstore.PutMany(toput) + err := s.blockstore.PutMany(ctx, toput) if err != nil { return err } @@ -194,7 +194,7 @@ func (s *blockService) AddBlocks(bs []blocks.Block) error { if s.exchange != nil { for _, o := range toput { log.Debugf("BlockService.BlockAdded %s", o.Cid()) - if err := s.exchange.HasBlock(o); err != nil { + if err := s.exchange.HasBlock(ctx, o); err != nil { log.Errorf("HasBlock: %s", err.Error()) } } @@ -225,7 +225,7 @@ func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget fun return nil, err } - block, err := bs.Get(c) + block, err := bs.Get(ctx, c) if err == nil { return block, nil } @@ -296,7 +296,7 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget var misses []cid.Cid for _, c := range ks { - hit, err := bs.Get(c) + hit, err := bs.Get(ctx, c) if err != nil { misses = append(misses, c) continue @@ -332,8 +332,8 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget } // DeleteBlock deletes a block in the blockservice from the datastore -func (s *blockService) DeleteBlock(c cid.Cid) error { - err := s.blockstore.DeleteBlock(c) +func (s *blockService) DeleteBlock(ctx context.Context, c cid.Cid) error { + err := s.blockstore.DeleteBlock(ctx, c) if err == nil { log.Debugf("BlockService.BlockDeleted %s", c) } diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index 36cdf0330..268a7a592 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -26,7 +26,7 @@ func TestWriteThroughWorks(t *testing.T) { block := bgen.Next() t.Logf("PutCounter: %d", bstore.PutCounter) - err := bserv.AddBlock(block) + err := bserv.AddBlock(context.Background(), block) if err != nil { t.Fatal(err) } @@ -34,7 +34,7 @@ func TestWriteThroughWorks(t *testing.T) { t.Fatalf("expected just one Put call, have: %d", bstore.PutCounter) } - err = bserv.AddBlock(block) + err = bserv.AddBlock(context.Background(), block) if err != nil { t.Fatal(err) } @@ -58,12 +58,12 @@ func TestLazySessionInitialization(t *testing.T) { bgen := butil.NewBlockGenerator() block := bgen.Next() - err := bstore.Put(block) + err := bstore.Put(ctx, block) if err != nil { t.Fatal(err) } block2 := bgen.Next() - err = session.HasBlock(block2) + err = session.HasBlock(ctx, block2) if err != nil { t.Fatal(err) } @@ -101,9 +101,9 @@ type PutCountingBlockstore struct { PutCounter int } -func (bs *PutCountingBlockstore) Put(block blocks.Block) error { +func (bs *PutCountingBlockstore) Put(ctx context.Context, block blocks.Block) error { bs.PutCounter++ - return bs.Blockstore.Put(block) + return bs.Blockstore.Put(ctx, block) } var _ exchange.SessionExchange = (*fakeSessionExchange)(nil) @@ -135,7 +135,7 @@ func TestNilExchange(t *testing.T) { if err != ErrNotFound { t.Fatal("expected block to not be found") } - err = bs.Put(block) + err = bs.Put(ctx, block) if err != nil { t.Fatal(err) } diff --git a/blockservice/test/blocks_test.go b/blockservice/test/blocks_test.go index ee808e66e..c9e2faee7 100644 --- a/blockservice/test/blocks_test.go +++ b/blockservice/test/blocks_test.go @@ -33,7 +33,7 @@ func TestBlocks(t *testing.T) { t.Error("Block key and data multihash key not equal") } - err := bs.AddBlock(o) + err := bs.AddBlock(context.Background(), o) if err != nil { t.Error("failed to add block to BlockService", err) return @@ -74,7 +74,7 @@ func TestGetBlocksSequential(t *testing.T) { var cids []cid.Cid for _, o := range objs { cids = append(cids, o.Cid()) - err := servs[0].AddBlock(o) + err := servs[0].AddBlock(context.Background(), o) if err != nil { t.Fatal(err) } From 1e8a6de0d46052eedf4c379b9c1c78dc985532fe Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Wed, 10 Nov 2021 16:02:23 -0500 Subject: [PATCH 3602/3817] feat: plumb through contexts (#78) This commit was moved from ipfs/go-merkledag@bfaa7663d177de206edb8a513b09ab4f94b95e57 --- ipld/merkledag/merkledag.go | 8 ++++---- ipld/merkledag/merkledag_test.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 0790b3e85..3ab9b1299 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -57,7 +57,7 @@ func (n *dagService) Add(ctx context.Context, nd format.Node) error { return fmt.Errorf("dagService is nil") } - return n.Blocks.AddBlock(nd) + return n.Blocks.AddBlock(ctx, nd) } func (n *dagService) AddMany(ctx context.Context, nds []format.Node) error { @@ -65,7 +65,7 @@ func (n *dagService) AddMany(ctx context.Context, nds []format.Node) error { for i, nd := range nds { blks[i] = nd } - return n.Blocks.AddBlocks(blks) + return n.Blocks.AddBlocks(ctx, blks) } // Get retrieves a node from the dagService, fetching the block in the BlockService @@ -102,7 +102,7 @@ func (n *dagService) GetLinks(ctx context.Context, c cid.Cid) ([]*format.Link, e } func (n *dagService) Remove(ctx context.Context, c cid.Cid) error { - return n.Blocks.DeleteBlock(c) + return n.Blocks.DeleteBlock(ctx, c) } // RemoveMany removes multiple nodes from the DAG. It will likely be faster than @@ -113,7 +113,7 @@ func (n *dagService) Remove(ctx context.Context, c cid.Cid) error { func (n *dagService) RemoveMany(ctx context.Context, cids []cid.Cid) error { // TODO(#4608): make this batch all the way down. for _, c := range cids { - if err := n.Blocks.DeleteBlock(c); err != nil { + if err := n.Blocks.DeleteBlock(ctx, c); err != nil { return err } } diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index ec4b1f163..17a05c6a4 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -610,7 +610,7 @@ func TestCidRetention(t *testing.T) { } bs := dstest.Bserv() - err = bs.AddBlock(blk) + err = bs.AddBlock(ctx, blk) if err != nil { t.Fatal(err) } From ae9364a8f22d661b7eeea4816c97856c983d7d83 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Wed, 10 Nov 2021 16:03:21 -0500 Subject: [PATCH 3603/3817] feat: plumb through context changes (#28) This commit was moved from ipfs/go-fetcher@d2ffddced6cb002bbb98237798c2e12096dad66c --- fetcher/helpers/block_visitor_test.go | 16 +++++++------ fetcher/impl/blockservice/fetcher_test.go | 28 ++++++++++++----------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/fetcher/helpers/block_visitor_test.go b/fetcher/helpers/block_visitor_test.go index 2fcf58d03..300e3d09c 100644 --- a/fetcher/helpers/block_visitor_test.go +++ b/fetcher/helpers/block_visitor_test.go @@ -21,6 +21,8 @@ import ( "github.com/stretchr/testify/require" ) +var bg = context.Background() + func TestFetchGraphToBlocks(t *testing.T) { block3, node3, link3 := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { na.AssembleEntry("three").AssignBool(true) @@ -49,13 +51,13 @@ func TestFetchGraphToBlocks(t *testing.T) { hasBlock := peers[0] defer hasBlock.Exchange.Close() - err := hasBlock.Exchange.HasBlock(block1) + err := hasBlock.Exchange.HasBlock(bg, block1) require.NoError(t, err) - err = hasBlock.Exchange.HasBlock(block2) + err = hasBlock.Exchange.HasBlock(bg, block2) require.NoError(t, err) - err = hasBlock.Exchange.HasBlock(block3) + err = hasBlock.Exchange.HasBlock(bg, block3) require.NoError(t, err) - err = hasBlock.Exchange.HasBlock(block4) + err = hasBlock.Exchange.HasBlock(bg, block4) require.NoError(t, err) wantsBlock := peers[1] @@ -102,11 +104,11 @@ func TestFetchGraphToUniqueBlocks(t *testing.T) { hasBlock := peers[0] defer hasBlock.Exchange.Close() - err := hasBlock.Exchange.HasBlock(block1) + err := hasBlock.Exchange.HasBlock(bg, block1) require.NoError(t, err) - err = hasBlock.Exchange.HasBlock(block2) + err = hasBlock.Exchange.HasBlock(bg, block2) require.NoError(t, err) - err = hasBlock.Exchange.HasBlock(block3) + err = hasBlock.Exchange.HasBlock(bg, block3) require.NoError(t, err) wantsBlock := peers[1] diff --git a/fetcher/impl/blockservice/fetcher_test.go b/fetcher/impl/blockservice/fetcher_test.go index 42d31d436..33cd2ee53 100644 --- a/fetcher/impl/blockservice/fetcher_test.go +++ b/fetcher/impl/blockservice/fetcher_test.go @@ -27,6 +27,8 @@ import ( "github.com/stretchr/testify/require" ) +var bg = context.Background() + func TestFetchIPLDPrimeNode(t *testing.T) { block, node, _ := testutil.EncodeBlock(fluent.MustBuildMap(basicnode.Prototype__Map{}, 3, func(na fluent.MapAssembler) { na.AssembleEntry("foo").AssignBool(true) @@ -44,7 +46,7 @@ func TestFetchIPLDPrimeNode(t *testing.T) { hasBlock := peers[0] defer hasBlock.Exchange.Close() - err := hasBlock.Exchange.HasBlock(block) + err := hasBlock.Exchange.HasBlock(bg, block) require.NoError(t, err) wantsBlock := peers[1] @@ -90,13 +92,13 @@ func TestFetchIPLDGraph(t *testing.T) { hasBlock := peers[0] defer hasBlock.Exchange.Close() - err := hasBlock.Exchange.HasBlock(block1) + err := hasBlock.Exchange.HasBlock(bg, block1) require.NoError(t, err) - err = hasBlock.Exchange.HasBlock(block2) + err = hasBlock.Exchange.HasBlock(bg, block2) require.NoError(t, err) - err = hasBlock.Exchange.HasBlock(block3) + err = hasBlock.Exchange.HasBlock(bg, block3) require.NoError(t, err) - err = hasBlock.Exchange.HasBlock(block4) + err = hasBlock.Exchange.HasBlock(bg, block4) require.NoError(t, err) wantsBlock := peers[1] @@ -150,7 +152,7 @@ func TestFetchIPLDPath(t *testing.T) { defer hasBlock.Exchange.Close() for _, blk := range []blocks.Block{block1, block2, block3, block4, block5} { - err := hasBlock.Exchange.HasBlock(blk) + err := hasBlock.Exchange.HasBlock(bg, blk) require.NoError(t, err) } @@ -212,13 +214,13 @@ func TestHelpers(t *testing.T) { hasBlock := peers[0] defer hasBlock.Exchange.Close() - err := hasBlock.Exchange.HasBlock(block1) + err := hasBlock.Exchange.HasBlock(bg, block1) require.NoError(t, err) - err = hasBlock.Exchange.HasBlock(block2) + err = hasBlock.Exchange.HasBlock(bg, block2) require.NoError(t, err) - err = hasBlock.Exchange.HasBlock(block3) + err = hasBlock.Exchange.HasBlock(bg, block3) require.NoError(t, err) - err = hasBlock.Exchange.HasBlock(block4) + err = hasBlock.Exchange.HasBlock(bg, block4) require.NoError(t, err) wantsBlock := peers[1] @@ -329,11 +331,11 @@ func TestNodeReification(t *testing.T) { hasBlock := peers[0] defer hasBlock.Exchange.Close() - err := hasBlock.Exchange.HasBlock(block2) + err := hasBlock.Exchange.HasBlock(bg, block2) require.NoError(t, err) - err = hasBlock.Exchange.HasBlock(block3) + err = hasBlock.Exchange.HasBlock(bg, block3) require.NoError(t, err) - err = hasBlock.Exchange.HasBlock(block4) + err = hasBlock.Exchange.HasBlock(bg, block4) require.NoError(t, err) wantsBlock := peers[1] From 3a39d7e054ac3ccff120ccb0a203349d6196e728 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Wed, 10 Nov 2021 17:26:29 -0500 Subject: [PATCH 3604/3817] feat: plumb through context changes (#47) This commit was moved from ipfs/go-path@19b77b2365c51c8fab1dc5b8a062ee96c21d91a8 --- path/resolver/resolver_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/path/resolver/resolver_test.go b/path/resolver/resolver_test.go index b1d8dec9e..d2420af04 100644 --- a/path/resolver/resolver_test.go +++ b/path/resolver/resolver_test.go @@ -57,7 +57,7 @@ func TestRecurivePathResolution(t *testing.T) { } for _, n := range []*merkledag.ProtoNode{a, b, c} { - err = bsrv.AddBlock(n) + err = bsrv.AddBlock(ctx, n) if err != nil { t.Fatal(err) } @@ -151,7 +151,7 @@ func TestResolveToLastNode_ErrNoLink(t *testing.T) { } for _, n := range []*merkledag.ProtoNode{a, b, c} { - err = bsrv.AddBlock(n) + err = bsrv.AddBlock(ctx, n) if err != nil { t.Fatal(err) } @@ -197,7 +197,7 @@ func TestResolveToLastNode_NoUnnecessaryFetching(t *testing.T) { err := a.AddNodeLink("child", b) require.NoError(t, err) - err = bsrv.AddBlock(a) + err = bsrv.AddBlock(ctx, a) require.NoError(t, err) aKey := a.Cid() @@ -243,7 +243,7 @@ func TestPathRemainder(t *testing.T) { require.NoError(t, err) blk, err := blocks.NewBlockWithCid(out.Bytes(), lnk) require.NoError(t, err) - bsrv.AddBlock(blk) + bsrv.AddBlock(ctx, blk) fetcherFactory := bsfetcher.NewFetcherConfig(bsrv) resolver := resolver.NewBasicResolver(fetcherFactory) @@ -259,7 +259,7 @@ func TestResolveToLastNode_MixedSegmentTypes(t *testing.T) { defer cancel() bsrv := dagmock.Bserv() a := randNode() - err := bsrv.AddBlock(a) + err := bsrv.AddBlock(ctx, a) if err != nil { t.Fatal(err) } @@ -281,7 +281,7 @@ func TestResolveToLastNode_MixedSegmentTypes(t *testing.T) { require.NoError(t, err) blk, err := blocks.NewBlockWithCid(out.Bytes(), lnk) require.NoError(t, err) - bsrv.AddBlock(blk) + bsrv.AddBlock(ctx, blk) fetcherFactory := bsfetcher.NewFetcherConfig(bsrv) resolver := resolver.NewBasicResolver(fetcherFactory) From 8604dc0c64c8975faf3856bd0ab3763c62dd35ae Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Thu, 11 Nov 2021 13:09:08 -0500 Subject: [PATCH 3605/3817] feat: plumb through context changes (#18) This commit was moved from ipfs/go-ipfs-pinner@72f5e02e73db5e05ef0a140a7938cbc89dfc38b0 --- pinning/pinner/dsindex/indexer.go | 12 ++++++------ pinning/pinner/dspinner/pin.go | 20 ++++++++++---------- pinning/pinner/dspinner/pin_test.go | 18 +++++++++--------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/pinning/pinner/dsindex/indexer.go b/pinning/pinner/dsindex/indexer.go index 884cd8025..8384ad5d5 100644 --- a/pinning/pinner/dsindex/indexer.go +++ b/pinning/pinner/dsindex/indexer.go @@ -75,7 +75,7 @@ func (x *indexer) Add(ctx context.Context, key, value string) error { return ErrEmptyValue } dsKey := ds.NewKey(encode(key)).ChildString(encode(value)) - return x.dstore.Put(dsKey, []byte{}) + return x.dstore.Put(ctx, dsKey, []byte{}) } func (x *indexer) Delete(ctx context.Context, key, value string) error { @@ -85,7 +85,7 @@ func (x *indexer) Delete(ctx context.Context, key, value string) error { if value == "" { return ErrEmptyValue } - return x.dstore.Delete(ds.NewKey(encode(key)).ChildString(encode(value))) + return x.dstore.Delete(ctx, ds.NewKey(encode(key)).ChildString(encode(value))) } func (x *indexer) DeleteKey(ctx context.Context, key string) (int, error) { @@ -108,7 +108,7 @@ func (x *indexer) ForEach(ctx context.Context, key string, fn func(key, value st Prefix: key, KeysOnly: true, } - results, err := x.dstore.Query(q) + results, err := x.dstore.Query(ctx, q) if err != nil { return err } @@ -145,7 +145,7 @@ func (x *indexer) HasValue(ctx context.Context, key, value string) (bool, error) if value == "" { return false, ErrEmptyValue } - return x.dstore.Has(ds.NewKey(encode(key)).ChildString(encode(value))) + return x.dstore.Has(ctx, ds.NewKey(encode(key)).ChildString(encode(value))) } func (x *indexer) HasAny(ctx context.Context, key string) (bool, error) { @@ -238,7 +238,7 @@ func (x *indexer) deletePrefix(ctx context.Context, prefix string) (int, error) } for i := range ents { - err = x.dstore.Delete(ds.NewKey(ents[i].Key)) + err = x.dstore.Delete(ctx, ds.NewKey(ents[i].Key)) if err != nil { return 0, err } @@ -252,7 +252,7 @@ func (x *indexer) queryPrefix(ctx context.Context, prefix string) ([]query.Entry Prefix: prefix, KeysOnly: true, } - results, err := x.dstore.Query(q) + results, err := x.dstore.Query(ctx, q) if err != nil { return nil, err } diff --git a/pinning/pinner/dspinner/pin.go b/pinning/pinner/dspinner/pin.go index a02f01547..fa3d9e754 100644 --- a/pinning/pinner/dspinner/pin.go +++ b/pinning/pinner/dspinner/pin.go @@ -142,7 +142,7 @@ func New(ctx context.Context, dstore ds.Datastore, dserv ipld.DAGService) (*pinn dstore: dstore, } - data, err := dstore.Get(dirtyKey) + data, err := dstore.Get(ctx, dirtyKey) if err != nil { if err == ds.ErrNotFound { return p, nil @@ -268,7 +268,7 @@ func (p *pinner) addPin(ctx context.Context, c cid.Cid, mode ipfspinner.Mode, na p.setDirty(ctx) // Store the pin - err = p.dstore.Put(pp.dsKey(), pinData) + err = p.dstore.Put(ctx, pp.dsKey(), pinData) if err != nil { return "", err } @@ -332,7 +332,7 @@ func (p *pinner) removePin(ctx context.Context, pp *pin) error { // The pin is removed last so that an incomplete remove is detected by a // pin that has a missing index. - err = p.dstore.Delete(pp.dsKey()) + err = p.dstore.Delete(ctx, pp.dsKey()) if err != nil { return err } @@ -669,7 +669,7 @@ func (p *pinner) removePinsForCid(ctx context.Context, c cid.Cid, mode ipfspinne // loadPin loads a single pin from the datastore. func (p *pinner) loadPin(ctx context.Context, pid string) (*pin, error) { - pinData, err := p.dstore.Get(ds.NewKey(path.Join(pinKeyPath, pid))) + pinData, err := p.dstore.Get(ctx, ds.NewKey(path.Join(pinKeyPath, pid))) if err != nil { return nil, err } @@ -804,7 +804,7 @@ func (p *pinner) flushPins(ctx context.Context, force bool) error { if !p.autoSync && !force { return nil } - if err := p.dstore.Sync(ds.NewKey(basePath)); err != nil { + if err := p.dstore.Sync(ctx, ds.NewKey(basePath)); err != nil { return fmt.Errorf("cannot sync pin state: %v", err) } p.setClean(ctx) @@ -909,12 +909,12 @@ func (p *pinner) setDirty(ctx context.Context) { } data := []byte{1} - err := p.dstore.Put(dirtyKey, data) + err := p.dstore.Put(ctx, dirtyKey, data) if err != nil { log.Errorf("failed to set pin dirty flag: %s", err) return } - err = p.dstore.Sync(dirtyKey) + err = p.dstore.Sync(ctx, dirtyKey) if err != nil { log.Errorf("failed to sync pin dirty flag: %s", err) } @@ -928,12 +928,12 @@ func (p *pinner) setClean(ctx context.Context) { } data := []byte{0} - err := p.dstore.Put(dirtyKey, data) + err := p.dstore.Put(ctx, dirtyKey, data) if err != nil { log.Errorf("failed to set clear dirty flag: %s", err) return } - if err = p.dstore.Sync(dirtyKey); err != nil { + if err = p.dstore.Sync(ctx, dirtyKey); err != nil { log.Errorf("failed to sync cleared pin dirty flag: %s", err) return } @@ -951,7 +951,7 @@ func (p *pinner) rebuildIndexes(ctx context.Context) error { q := query.Query{ Prefix: pinKeyPath, } - results, err := p.dstore.Query(q) + results, err := p.dstore.Query(ctx, q) if err != nil { return err } diff --git a/pinning/pinner/dspinner/pin_test.go b/pinning/pinner/dspinner/pin_test.go index ed2828658..4e12fefb7 100644 --- a/pinning/pinner/dspinner/pin_test.go +++ b/pinning/pinner/dspinner/pin_test.go @@ -309,7 +309,7 @@ func TestPinnerBasic(t *testing.T) { if pp.Cid != ak { t.Error("loaded pin has wrong cid") } - err = p.dstore.Delete(pp.dsKey()) + err = p.dstore.Delete(ctx, pp.dsKey()) if err != nil { t.Fatal(err) } @@ -703,7 +703,7 @@ func TestLoadDirty(t *testing.T) { p.setDirty(ctx) // Verify dirty - data, err := dstore.Get(dirtyKey) + data, err := dstore.Get(ctx, dirtyKey) if err != nil { t.Fatalf("could not read dirty flag: %v", err) } @@ -727,7 +727,7 @@ func TestLoadDirty(t *testing.T) { } // Verify not dirty - data, err = dstore.Get(dirtyKey) + data, err = dstore.Get(ctx, dirtyKey) if err != nil { t.Fatalf("could not read dirty flag: %v", err) } @@ -923,7 +923,7 @@ type batchWrap struct { ds.Datastore } -func (d *batchWrap) Batch() (ds.Batch, error) { +func (d *batchWrap) Batch(_ context.Context) (ds.Batch, error) { return ds.NewBasicBatch(d), nil } @@ -957,7 +957,7 @@ func BenchmarkLoad(b *testing.B) { b.Run("RebuildTrue", func(b *testing.B) { for i := 0; i < b.N; i++ { - err = dstore.Put(dirtyKey, []byte{1}) + err = dstore.Put(ctx, dirtyKey, []byte{1}) if err != nil { panic(err.Error()) } @@ -971,7 +971,7 @@ func BenchmarkLoad(b *testing.B) { b.Run("RebuildFalse", func(b *testing.B) { for i := 0; i < b.N; i++ { - err = dstore.Put(dirtyKey, []byte{0}) + err = dstore.Put(ctx, dirtyKey, []byte{0}) if err != nil { panic(err.Error()) } @@ -1161,7 +1161,7 @@ func BenchmarkRebuild(b *testing.B) { b.Run(fmt.Sprintf("Rebuild %d", pins), func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - err = dstore.Put(dirtyKey, []byte{1}) + err = dstore.Put(ctx, dirtyKey, []byte{1}) if err != nil { panic(err.Error()) } @@ -1252,7 +1252,7 @@ func TestCidIndex(t *testing.T) { q := query.Query{ Prefix: pinKeyPath, } - results, err := pinner.dstore.Query(q) + results, err := pinner.dstore.Query(ctx, q) if err != nil { t.Fatal(err) } @@ -1335,7 +1335,7 @@ func TestRebuild(t *testing.T) { if err != nil { t.Fatal(err) } - err = pinner.dstore.Delete(pp.dsKey()) + err = pinner.dstore.Delete(ctx, pp.dsKey()) if err != nil { t.Fatal(err) } From 01d2a5e4d1184e212da369ebc5471abf6928b75a Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Thu, 11 Nov 2021 13:45:22 -0500 Subject: [PATCH 3606/3817] feat: plumb through datastore contexts (#39) This commit was moved from ipfs/go-ipfs-provider@4aff05e6304c6e222c4ff7ebbb6c6f8df6d8aa17 --- provider/batched/system.go | 6 +++--- provider/queue/queue.go | 8 ++++---- provider/queue/queue_test.go | 4 ++-- provider/simple/reprovide.go | 4 ++-- provider/simple/reprovide_test.go | 5 +++-- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/provider/batched/system.go b/provider/batched/system.go index 111ee115b..de9177796 100644 --- a/provider/batched/system.go +++ b/provider/batched/system.go @@ -259,10 +259,10 @@ func (s *BatchProvidingSystem) Run() { s.lastReprovideBatchSize = len(keys) s.lastReprovideDuration = dur - if err := s.ds.Put(lastReprovideKey, storeTime(time.Now())); err != nil { + if err := s.ds.Put(s.ctx, lastReprovideKey, storeTime(time.Now())); err != nil { log.Errorf("could not store last reprovide time: %v", err) } - if err := s.ds.Sync(lastReprovideKey); err != nil { + if err := s.ds.Sync(s.ctx, lastReprovideKey); err != nil { log.Errorf("could not perform sync of last reprovide time: %v", err) } } @@ -374,7 +374,7 @@ reprovideCidLoop: } func (s *BatchProvidingSystem) getLastReprovideTime() (time.Time, error) { - val, err := s.ds.Get(lastReprovideKey) + val, err := s.ds.Get(s.ctx, lastReprovideKey) if errors.Is(err, datastore.ErrNotFound) { return time.Time{}, nil } diff --git a/provider/queue/queue.go b/provider/queue/queue.go index e81e341f6..18ed6a798 100644 --- a/provider/queue/queue.go +++ b/provider/queue/queue.go @@ -97,7 +97,7 @@ func (q *Queue) work() { c, err = cid.Parse(head.Value) if err != nil { log.Warningf("error parsing queue entry cid with key (%s), removing it from queue: %s", head.Key, err) - err = q.ds.Delete(k) + err = q.ds.Delete(q.ctx, k) if err != nil { log.Errorf("error deleting queue entry with key (%s), due to error (%s), stopping provider", head.Key, err) return @@ -120,12 +120,12 @@ func (q *Queue) work() { keyPath := fmt.Sprintf("%d/%s", time.Now().UnixNano(), c.String()) nextKey := datastore.NewKey(keyPath) - if err := q.ds.Put(nextKey, toQueue.Bytes()); err != nil { + if err := q.ds.Put(q.ctx, nextKey, toQueue.Bytes()); err != nil { log.Errorf("Failed to enqueue cid: %s", err) continue } case dequeue <- c: - err := q.ds.Delete(k) + err := q.ds.Delete(q.ctx, k) if err != nil { log.Errorf("Failed to delete queued cid %s with key %s: %s", c, k, err) @@ -141,7 +141,7 @@ func (q *Queue) work() { func (q *Queue) getQueueHead() (*query.Entry, error) { qry := query.Query{Orders: []query.Order{query.OrderByKey{}}, Limit: 1} - results, err := q.ds.Query(qry) + results, err := q.ds.Query(q.ctx, qry) if err != nil { return nil, err } diff --git a/provider/queue/queue_test.go b/provider/queue/queue_test.go index 819fa90f9..a0fa36c3a 100644 --- a/provider/queue/queue_test.go +++ b/provider/queue/queue_test.go @@ -8,7 +8,7 @@ import ( "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore/sync" - "github.com/ipfs/go-ipfs-blocksutil" + blocksutil "github.com/ipfs/go-ipfs-blocksutil" ) var blockGenerator = blocksutil.NewBlockGenerator() @@ -72,7 +72,7 @@ func TestMangledData(t *testing.T) { // put bad data in the queue queueKey := datastore.NewKey("/test/0") - err = queue.ds.Put(queueKey, []byte("borked")) + err = queue.ds.Put(ctx, queueKey, []byte("borked")) if err != nil { t.Fatal(err) } diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index c46148c72..b62369a07 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -45,7 +45,7 @@ type Reprovider struct { } // NewReprovider creates new Reprovider instance. -func NewReprovider(ctx context.Context, reprovideIniterval time.Duration, rsys routing.ContentRouting, keyProvider KeyChanFunc) *Reprovider { +func NewReprovider(ctx context.Context, reprovideInterval time.Duration, rsys routing.ContentRouting, keyProvider KeyChanFunc) *Reprovider { ctx, cancel := context.WithCancel(ctx) return &Reprovider{ ctx: ctx, @@ -55,7 +55,7 @@ func NewReprovider(ctx context.Context, reprovideIniterval time.Duration, rsys r rsys: rsys, keyProvider: keyProvider, - tick: reprovideIniterval, + tick: reprovideInterval, } } diff --git a/provider/simple/reprovide_test.go b/provider/simple/reprovide_test.go index e29524ae2..20b066e60 100644 --- a/provider/simple/reprovide_test.go +++ b/provider/simple/reprovide_test.go @@ -48,7 +48,7 @@ func setupDag(t *testing.T) (nodes []cid.Cid, bstore blockstore.Blockstore) { t.Fatal(err) } blk := toBlock(t, nb.Build()) - err = bstore.Put(blk) + err = bstore.Put(context.Background(), blk) if err != nil { t.Fatal(err) } @@ -60,7 +60,7 @@ func setupDag(t *testing.T) (nodes []cid.Cid, bstore blockstore.Blockstore) { t.Fatal(err) } blk = toBlock(t, nd) - err = bstore.Put(blk) + err = bstore.Put(context.Background(), blk) if err != nil { t.Fatal(err) } @@ -117,6 +117,7 @@ func testReprovide(t *testing.T, trigger func(r *Reprovider, ctx context.Context keyProvider := NewBlockstoreProvider(bstore) reprov := NewReprovider(ctx, time.Hour, clA, keyProvider) + reprov.Trigger(context.Background()) err := trigger(reprov, ctx) if err != nil { t.Fatal(err) From 8232413c90f8f9458ee81d4ce7e997c84456c7d7 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Thu, 11 Nov 2021 22:12:23 -0300 Subject: [PATCH 3607/3817] Size-based unsharding (#94) * Renamed UpgradeableDirectory to DynamicDirectory to indicate that we can switch between Basic and HAMT Directories * Transition between HAMT directory and Basic Directory based on the global sharding threshold * Unexported BasicDirectory.SwitchToSharding as an unnecessary exposure point until requested * NewDirectoryFromNode always returns a DynamicDirectory instead of an UpgradeableDirectory or HAMTDirectory * Added Swap and Take functions to HAMT Shards * Fix for the size estimation logic where we were not tracking that replacing an entry with a differently sized CID could trigger switching between being a Basic or HAMT directory * Use custom parallel DAG traversal to the EnumLinksAsync for HAMTs that is closer to DFS than BFS * Added lots of comments to the HAMT code * Exported LogTwo function to make it more accessible within the package Co-authored-by: Lucas Molas Co-authored-by: Adin Schmahmann This commit was moved from ipfs/go-unixfs@bd53b6a811b1e1a594f02e384cf6e9606d853657 --- unixfs/hamt/hamt.go | 356 +++++++++++++++----- unixfs/hamt/util.go | 18 +- unixfs/internal/config.go | 3 + unixfs/io/completehamt_test.go | 97 ++++++ unixfs/io/directory.go | 314 ++++++++++++++--- unixfs/io/directory_test.go | 500 +++++++++++++++++++++++----- unixfs/private/linksize/linksize.go | 5 + 7 files changed, 1081 insertions(+), 212 deletions(-) create mode 100644 unixfs/internal/config.go create mode 100644 unixfs/io/completehamt_test.go create mode 100644 unixfs/private/linksize/linksize.go diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 55b798ce4..ac1c5e458 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -24,12 +24,17 @@ import ( "context" "fmt" "os" + "sync" + + "golang.org/x/sync/errgroup" + + format "github.com/ipfs/go-unixfs" + "github.com/ipfs/go-unixfs/internal" bitfield "github.com/ipfs/go-bitfield" cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" dag "github.com/ipfs/go-merkledag" - format "github.com/ipfs/go-unixfs" ) const ( @@ -37,27 +42,41 @@ const ( HashMurmur3 uint64 = 0x22 ) +func init() { + internal.HAMTHashFunction = murmur3Hash +} + func (ds *Shard) isValueNode() bool { return ds.key != "" && ds.val != nil } // A Shard represents the HAMT. It should be initialized with NewShard(). type Shard struct { - cid cid.Cid - childer *childer - tableSize int + // Entries per node (number of possible childs indexed by the partial key). + tableSize int + // Bits needed to encode child indexes (log2 of number of entries). This is + // the number of bits taken from the hash key on each level of the tree. tableSizeLg2 int builder cid.Builder hashFunc uint64 + // String format with number of zeros that will be present in the hexadecimal + // encoding of the child index to always reach the fixed maxpadlen chars. + // Example: maxpadlen = 4 => prefixPadStr: "%04X" (print number in hexadecimal + // format padding with zeros to always reach 4 characters). prefixPadStr string - maxpadlen int + // Length in chars of string that encodes child indexes. We encode indexes + // as hexadecimal strings to this is log4 of number of entries. + maxpadlen int dserv ipld.DAGService + // FIXME: Remove. We don't actually store "value nodes". This confusing + // abstraction just removes the maxpadlen from the link names to extract + // the actual value link the trie is storing. // leaf node key string val *ipld.Link @@ -70,12 +89,13 @@ func NewShard(dserv ipld.DAGService, size int) (*Shard, error) { return nil, err } + // FIXME: Make this at least a static configuration for testing. ds.hashFunc = HashMurmur3 return ds, nil } func makeShard(ds ipld.DAGService, size int) (*Shard, error) { - lg2s, err := logtwo(size) + lg2s, err := Logtwo(size) if err != nil { return nil, err } @@ -123,7 +143,6 @@ func NewHamtFromDag(dserv ipld.DAGService, nd ipld.Node) (*Shard, error) { ds.childer.makeChilder(fsn.Data(), pbnd.Links()) - ds.cid = pbnd.Cid() ds.hashFunc = fsn.HashType() ds.builder = pbnd.CidBuilder() @@ -206,31 +225,49 @@ func (ds *Shard) makeShardValue(lnk *ipld.Link) (*Shard, error) { // Set sets 'name' = nd in the HAMT func (ds *Shard) Set(ctx context.Context, name string, nd ipld.Node) error { - hv := &hashBits{b: hash([]byte(name))} - err := ds.dserv.Add(ctx, nd) + _, err := ds.Swap(ctx, name, nd) + return err +} + +// Swap sets a link pointing to the passed node as the value under the +// name key in this Shard or its children. It also returns the previous link +// under that name key (if any). +func (ds *Shard) Swap(ctx context.Context, name string, node ipld.Node) (*ipld.Link, error) { + hv := newHashBits(name) + err := ds.dserv.Add(ctx, node) if err != nil { - return err + return nil, err } - lnk, err := ipld.MakeLink(nd) + lnk, err := ipld.MakeLink(node) if err != nil { - return err + return nil, err } + + // FIXME: We don't need to set the name here, it will get overwritten. + // This is confusing, confirm and remove this line. lnk.Name = ds.linkNamePrefix(0) + name - return ds.modifyValue(ctx, hv, name, lnk) + return ds.swapValue(ctx, hv, name, lnk) } // Remove deletes the named entry if it exists. Otherwise, it returns // os.ErrNotExist. func (ds *Shard) Remove(ctx context.Context, name string) error { - hv := &hashBits{b: hash([]byte(name))} - return ds.modifyValue(ctx, hv, name, nil) + _, err := ds.Take(ctx, name) + return err +} + +// Take is similar to the public Remove but also returns the +// old removed link (if it exists). +func (ds *Shard) Take(ctx context.Context, name string) (*ipld.Link, error) { + hv := newHashBits(name) + return ds.swapValue(ctx, hv, name, nil) } // Find searches for a child node by 'name' within this hamt func (ds *Shard) Find(ctx context.Context, name string) (*ipld.Link, error) { - hv := &hashBits{b: hash([]byte(name))} + hv := newHashBits(name) var out *ipld.Link err := ds.getValue(ctx, hv, name, func(sv *Shard) error { @@ -338,9 +375,11 @@ func (ds *Shard) EnumLinksAsync(ctx context.Context) <-chan format.LinkResult { go func() { defer close(linkResults) defer cancel() - getLinks := makeAsyncTrieGetLinks(ds.dserv, linkResults) - cset := cid.NewSet() - err := dag.Walk(ctx, getLinks, ds.cid, cset.Visit, dag.Concurrent()) + + err := parallelShardWalk(ctx, ds, ds.dserv, func(formattedLink *ipld.Link) error { + emitResult(ctx, linkResults, format.LinkResult{Link: formattedLink, Err: nil}) + return nil + }) if err != nil { emitResult(ctx, linkResults, format.LinkResult{Link: nil, Err: err}) } @@ -348,44 +387,178 @@ func (ds *Shard) EnumLinksAsync(ctx context.Context) <-chan format.LinkResult { return linkResults } -// makeAsyncTrieGetLinks builds a getLinks function that can be used with EnumerateChildrenAsync -// to iterate a HAMT shard. It takes an IPLD Dag Service to fetch nodes, and a call back that will get called -// on all links to leaf nodes in a HAMT tree, so they can be collected for an EnumLinks operation -func makeAsyncTrieGetLinks(dagService ipld.DAGService, linkResults chan<- format.LinkResult) dag.GetLinks { - - return func(ctx context.Context, currentCid cid.Cid) ([]*ipld.Link, error) { - node, err := dagService.Get(ctx, currentCid) - if err != nil { - return nil, err - } - directoryShard, err := NewHamtFromDag(dagService, node) - if err != nil { - return nil, err - } +type listCidsAndShards struct { + cids []cid.Cid + shards []*Shard +} - childShards := make([]*ipld.Link, 0, directoryShard.childer.length()) - links := directoryShard.childer.links - for idx := range directoryShard.childer.children { - lnk := links[idx] - lnkLinkType, err := directoryShard.childLinkType(lnk) +func (ds *Shard) walkChildren(processLinkValues func(formattedLink *ipld.Link) error) (*listCidsAndShards, error) { + res := &listCidsAndShards{} + for idx, lnk := range ds.childer.links { + if nextShard := ds.childer.children[idx]; nextShard == nil { + lnkLinkType, err := ds.childLinkType(lnk) if err != nil { return nil, err } - if lnkLinkType == shardLink { - childShards = append(childShards, lnk) - } else { - sv, err := directoryShard.makeShardValue(lnk) + + switch lnkLinkType { + case shardValueLink: + sv, err := ds.makeShardValue(lnk) if err != nil { return nil, err } formattedLink := sv.val formattedLink.Name = sv.key - emitResult(ctx, linkResults, format.LinkResult{Link: formattedLink, Err: nil}) + + if err := processLinkValues(formattedLink); err != nil { + return nil, err + } + case shardLink: + res.cids = append(res.cids, lnk.Cid) + default: + return nil, fmt.Errorf("unsupported shard link type") + } + + } else { + if nextShard.val != nil { + formattedLink := &ipld.Link{ + Name: nextShard.key, + Size: nextShard.val.Size, + Cid: nextShard.val.Cid, + } + if err := processLinkValues(formattedLink); err != nil { + return nil, err + } + } else { + res.shards = append(res.shards, nextShard) } } - return childShards, nil } + return res, nil +} + +// parallelShardWalk is quite similar to the DAG walking algorithm from https://github.com/ipfs/go-merkledag/blob/594e515f162e764183243b72c2ba84f743424c8c/merkledag.go#L464 +// However, there are a few notable differences: +// 1. Some children are actualized Shard structs and some are in the blockstore, this will leverage walking over the in memory Shards as well as the stored blocks +// 2. Instead of just passing each child into the worker pool by itself we group them so that we can leverage optimizations from GetMany. +// This optimization also makes the walk a little more biased towards depth (as opposed to BFS) in the earlier part of the DAG. +// This is particularly helpful for operations like estimating the directory size which should complete quickly when possible. +// 3. None of the extra options from that package are needed +func parallelShardWalk(ctx context.Context, root *Shard, dserv ipld.DAGService, processShardValues func(formattedLink *ipld.Link) error) error { + const concurrency = 32 + + var visitlk sync.Mutex + visitSet := cid.NewSet() + visit := visitSet.Visit + + // Setup synchronization + grp, errGrpCtx := errgroup.WithContext(ctx) + + // Input and output queues for workers. + feed := make(chan *listCidsAndShards) + out := make(chan *listCidsAndShards) + done := make(chan struct{}) + + for i := 0; i < concurrency; i++ { + grp.Go(func() error { + for feedChildren := range feed { + for _, nextShard := range feedChildren.shards { + nextChildren, err := nextShard.walkChildren(processShardValues) + if err != nil { + return err + } + + select { + case out <- nextChildren: + case <-errGrpCtx.Done(): + return nil + } + } + + var linksToVisit []cid.Cid + for _, nextCid := range feedChildren.cids { + var shouldVisit bool + + visitlk.Lock() + shouldVisit = visit(nextCid) + visitlk.Unlock() + + if shouldVisit { + linksToVisit = append(linksToVisit, nextCid) + } + } + + chNodes := dserv.GetMany(errGrpCtx, linksToVisit) + for optNode := range chNodes { + if optNode.Err != nil { + return optNode.Err + } + + nextShard, err := NewHamtFromDag(dserv, optNode.Node) + if err != nil { + return err + } + + nextChildren, err := nextShard.walkChildren(processShardValues) + if err != nil { + return err + } + + select { + case out <- nextChildren: + case <-errGrpCtx.Done(): + return nil + } + } + + select { + case done <- struct{}{}: + case <-errGrpCtx.Done(): + } + } + return nil + }) + } + + send := feed + var todoQueue []*listCidsAndShards + var inProgress int + + next := &listCidsAndShards{ + shards: []*Shard{root}, + } + +dispatcherLoop: + for { + select { + case send <- next: + inProgress++ + if len(todoQueue) > 0 { + next = todoQueue[0] + todoQueue = todoQueue[1:] + } else { + next = nil + send = nil + } + case <-done: + inProgress-- + if inProgress == 0 && next == nil { + break dispatcherLoop + } + case nextNodes := <-out: + if next == nil { + next = nextNodes + send = feed + } else { + todoQueue = append(todoQueue, nextNodes) + } + case <-errGrpCtx.Done(): + break dispatcherLoop + } + } + close(feed) + return grp.Wait() } func emitResult(ctx context.Context, linkResults chan<- format.LinkResult, r format.LinkResult) { @@ -419,75 +592,95 @@ func (ds *Shard) walkTrie(ctx context.Context, cb func(*Shard) error) error { }) } -func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val *ipld.Link) error { +// swapValue sets the link `value` in the given key, either creating the entry +// if it didn't exist or overwriting the old one. It returns the old entry (if any). +func (ds *Shard) swapValue(ctx context.Context, hv *hashBits, key string, value *ipld.Link) (*ipld.Link, error) { idx, err := hv.Next(ds.tableSizeLg2) if err != nil { - return err + return nil, err } if !ds.childer.has(idx) { - return ds.childer.insert(key, val, idx) + // Entry does not exist, create a new one. + return nil, ds.childer.insert(key, value, idx) } i := ds.childer.sliceIndex(idx) - child, err := ds.childer.get(ctx, i) if err != nil { - return err + return nil, err } if child.isValueNode() { + // Leaf node. This is the base case of this recursive function. if child.key == key { - // value modification - if val == nil { - return ds.childer.rm(idx) + // We are in the correct shard (tree level) so we modify this child + // and return. + oldValue := child.val + + if value == nil { // Remove old entry. + return oldValue, ds.childer.rm(idx) } - child.val = val - return nil + child.val = value // Overwrite entry. + return oldValue, nil } - if val == nil { - return os.ErrNotExist + if value == nil { + return nil, os.ErrNotExist } - // replace value with another shard, one level deeper - ns, err := NewShard(ds.dserv, ds.tableSize) + // We are in the same slot with another entry with a different key + // so we need to fork this leaf node into a shard with two childs: + // the old entry and the new one being inserted here. + // We don't overwrite anything here so we keep: + // `oldValue = nil` + + // The child of this shard will now be a new shard. The old child value + // will be a child of this new shard (along with the new value being + // inserted). + grandChild := child + child, err = NewShard(ds.dserv, ds.tableSize) if err != nil { - return err - } - ns.builder = ds.builder - chhv := &hashBits{ - b: hash([]byte(child.key)), - consumed: hv.consumed, + return nil, err } - - err = ns.modifyValue(ctx, hv, key, val) + child.builder = ds.builder + chhv := newConsumedHashBits(grandChild.key, hv.consumed) + + // We explicitly ignore the oldValue returned by the next two insertions + // (which will be nil) to highlight there is no overwrite here: they are + // done with different keys to a new (empty) shard. (At best this shard + // will create new ones until we find different slots for both.) + _, err = child.swapValue(ctx, hv, key, value) if err != nil { - return err + return nil, err } - - err = ns.modifyValue(ctx, chhv, child.key, child.val) + _, err = child.swapValue(ctx, chhv, grandChild.key, grandChild.val) if err != nil { - return err + return nil, err } - ds.childer.set(ns, i) - return nil + // Replace this leaf node with the new Shard node. + ds.childer.set(child, i) + return nil, nil } else { - err := child.modifyValue(ctx, hv, key, val) + // We are in a Shard (internal node). We will recursively call this + // function until finding the leaf (the logic of the `if` case above). + oldValue, err := child.swapValue(ctx, hv, key, value) if err != nil { - return err + return nil, err } - if val == nil { + if value == nil { + // We have removed an entry, check if we should remove shards + // as well. switch child.childer.length() { case 0: // empty sub-shard, prune it // Note: this shouldnt normally ever happen // in the event of another implementation creates flawed // structures, this will help to normalize them. - return ds.childer.rm(idx) + return oldValue, ds.childer.rm(idx) case 1: // The single child _should_ be a value by // induction. However, we allow for it to be a @@ -499,24 +692,25 @@ func (ds *Shard) modifyValue(ctx context.Context, hv *hashBits, key string, val if schild.isValueNode() { ds.childer.set(schild, i) } - return nil + return oldValue, nil } // Otherwise, work with the link. slnk := child.childer.link(0) - lnkType, err := child.childer.sd.childLinkType(slnk) + var lnkType linkType + lnkType, err = child.childer.sd.childLinkType(slnk) if err != nil { - return err + return nil, err } if lnkType == shardValueLink { // sub-shard with a single value element, collapse it ds.childer.setLink(slnk, i) } - return nil + return oldValue, nil } } - return nil + return oldValue, nil } } diff --git a/unixfs/hamt/util.go b/unixfs/hamt/util.go index 7ae02dfb3..29f59435e 100644 --- a/unixfs/hamt/util.go +++ b/unixfs/hamt/util.go @@ -2,9 +2,11 @@ package hamt import ( "fmt" + "math/bits" + + "github.com/ipfs/go-unixfs/internal" "github.com/spaolacci/murmur3" - "math/bits" ) // hashBits is a helper that allows the reading of the 'next n bits' as an integer. @@ -13,6 +15,16 @@ type hashBits struct { consumed int } +func newHashBits(val string) *hashBits { + return &hashBits{b: internal.HAMTHashFunction([]byte(val))} +} + +func newConsumedHashBits(val string, consumed int) *hashBits { + hv := &hashBits{b: internal.HAMTHashFunction([]byte(val))} + hv.consumed = consumed + return hv +} + func mkmask(n int) byte { return (1 << uint(n)) - 1 } @@ -50,7 +62,7 @@ func (hb *hashBits) next(i int) int { } } -func logtwo(v int) (int, error) { +func Logtwo(v int) (int, error) { if v <= 0 { return 0, fmt.Errorf("hamt size should be a power of two") } @@ -61,7 +73,7 @@ func logtwo(v int) (int, error) { return lg2, nil } -func hash(val []byte) []byte { +func murmur3Hash(val []byte) []byte { h := murmur3.New64() h.Write(val) return h.Sum(nil) diff --git a/unixfs/internal/config.go b/unixfs/internal/config.go new file mode 100644 index 000000000..9250ae2ae --- /dev/null +++ b/unixfs/internal/config.go @@ -0,0 +1,3 @@ +package internal + +var HAMTHashFunction func(val []byte) []byte diff --git a/unixfs/io/completehamt_test.go b/unixfs/io/completehamt_test.go new file mode 100644 index 000000000..2af652e32 --- /dev/null +++ b/unixfs/io/completehamt_test.go @@ -0,0 +1,97 @@ +package io + +import ( + "context" + "encoding/binary" + "fmt" + "github.com/ipfs/go-unixfs/internal" + "math" + "testing" + + mdtest "github.com/ipfs/go-merkledag/test" + "github.com/stretchr/testify/assert" + + "github.com/ipfs/go-unixfs" + "github.com/ipfs/go-unixfs/hamt" + + ipld "github.com/ipfs/go-ipld-format" +) + +// CreateCompleteHAMT creates a HAMT the following properties: +// * its height (distance/edges from root to deepest node) is specified by treeHeight. +// * all leaf Shard nodes have the same depth (and have only 'value' links). +// * all internal Shard nodes point only to other Shards (and hence have zero 'value' links). +// * the total number of 'value' links (directory entries) is: +// childsPerNode ^ (treeHeight). +// treeHeight: The number of layers of non-value HAMT nodes (e.g. height = 1 is a single shard pointing to some values) +// FIXME: HAMTHashFunction needs to be set to idHash by the caller. We depend on +// this simplification for the current logic to work. +func CreateCompleteHAMT(ds ipld.DAGService, treeHeight int, childsPerNode int) (ipld.Node, error) { + if treeHeight < 1 { + panic("treeHeight < 1") + } + if treeHeight > 8 { + panic("treeHeight > 8: we don't allow a key larger than what can be encoded in a 64-bit word") + } + + rootShard, err := hamt.NewShard(ds, childsPerNode) + if err != nil { + return nil, err + } + + // Assuming we are using the ID hash function we can just insert all + // the combinations of a byte slice that will reach the desired height. + totalChildren := int(math.Pow(float64(childsPerNode), float64(treeHeight))) + log2ofChilds, err := hamt.Logtwo(childsPerNode) + if err != nil { + return nil, err + } + if log2ofChilds*treeHeight%8 != 0 { + return nil, fmt.Errorf("childsPerNode * treeHeight should be multiple of 8") + } + bytesInKey := log2ofChilds * treeHeight / 8 + for i := 0; i < totalChildren; i++ { + var hashbuf [8]byte + binary.LittleEndian.PutUint64(hashbuf[:], uint64(i)) + var oldLink *ipld.Link + oldLink, err = rootShard.Swap(context.Background(), string(hashbuf[:bytesInKey]), unixfs.EmptyFileNode()) + if err != nil { + return nil, err + } + if oldLink != nil { + // We shouldn't be overwriting any value, otherwise the tree + // won't be complete. + return nil, fmt.Errorf("we have overwritten entry %s", + oldLink.Cid) + } + } + + return rootShard.Node() +} + +// Return the same value as the hash. +func idHash(val []byte) []byte { + return val +} + +// FIXME: This is not checking the exact height of the tree but just making +// sure there are as many children as we would have with a complete HAMT. +func TestCreateCompleteShard(t *testing.T) { + oldHashFunc := internal.HAMTHashFunction + defer func() { internal.HAMTHashFunction = oldHashFunc }() + internal.HAMTHashFunction = idHash + + ds := mdtest.Mock() + childsPerNode := 16 + treeHeight := 2 + node, err := CreateCompleteHAMT(ds, treeHeight, childsPerNode) + assert.NoError(t, err) + + shard, err := hamt.NewHamtFromDag(ds, node) + assert.NoError(t, err) + links, err := shard.EnumLinks(context.Background()) + assert.NoError(t, err) + + childNodes := int(math.Pow(float64(childsPerNode), float64(treeHeight))) + assert.Equal(t, childNodes, len(links)) +} diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index 15c7e862a..2ec862247 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -5,14 +5,15 @@ import ( "fmt" "os" - mdag "github.com/ipfs/go-merkledag" - - format "github.com/ipfs/go-unixfs" "github.com/ipfs/go-unixfs/hamt" + "github.com/ipfs/go-unixfs/private/linksize" + "github.com/alecthomas/units" "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" + mdag "github.com/ipfs/go-merkledag" + format "github.com/ipfs/go-unixfs" ) var log = logging.Logger("unixfs") @@ -23,9 +24,10 @@ var log = logging.Logger("unixfs") // The size is not the *exact* block size of the encoded BasicDirectory but just // the estimated size based byte length of links name and CID (BasicDirectory's // ProtoNode doesn't use the Data field so this estimate is pretty accurate). -var HAMTShardingSize = 0 +var HAMTShardingSize = int(256 * units.KiB) // DefaultShardWidth is the default value used for hamt sharding width. +// Needs to be a power of two (shard entry size) and multiple of 8 (bitfield size). var DefaultShardWidth = 256 // Directory defines a UnixFS directory. It is used for creating, reading and @@ -74,6 +76,16 @@ type Directory interface { // TODO: Evaluate removing `dserv` from this layer and providing it in MFS. // (The functions should in that case add a `DAGService` argument.) +// Link size estimation function. For production it's usually the one here +// but during test we may mock it to get fixed sizes. +func productionLinkSize(linkName string, linkCid cid.Cid) int { + return len(linkName) + linkCid.ByteLen() +} + +func init() { + linksize.LinkSizeFunction = productionLinkSize +} + // BasicDirectory is the basic implementation of `Directory`. All the entries // are stored in a single node. type BasicDirectory struct { @@ -93,6 +105,10 @@ type BasicDirectory struct { type HAMTDirectory struct { shard *hamt.Shard dserv ipld.DAGService + + // Track the changes in size by the AddChild and RemoveChild calls + // for the HAMTShardingSize option. + sizeChange int } func newEmptyBasicDirectory(dserv ipld.DAGService) *BasicDirectory { @@ -110,10 +126,10 @@ func newBasicDirectoryFromNode(dserv ipld.DAGService, node *mdag.ProtoNode) *Bas return basicDir } -// NewDirectory returns a Directory implemented by UpgradeableDirectory +// NewDirectory returns a Directory implemented by DynamicDirectory // containing a BasicDirectory that can be converted to a HAMTDirectory. func NewDirectory(dserv ipld.DAGService) Directory { - return &UpgradeableDirectory{newEmptyBasicDirectory(dserv)} + return &DynamicDirectory{newEmptyBasicDirectory(dserv)} } // ErrNotADir implies that the given node was not a unixfs directory @@ -134,16 +150,13 @@ func NewDirectoryFromNode(dserv ipld.DAGService, node ipld.Node) (Directory, err switch fsNode.Type() { case format.TDirectory: - return &UpgradeableDirectory{newBasicDirectoryFromNode(dserv, protoBufNode.Copy().(*mdag.ProtoNode))}, nil + return &DynamicDirectory{newBasicDirectoryFromNode(dserv, protoBufNode.Copy().(*mdag.ProtoNode))}, nil case format.THAMTShard: shard, err := hamt.NewHamtFromDag(dserv, node) if err != nil { return nil, err } - return &HAMTDirectory{ - dserv: dserv, - shard: shard, - }, nil + return &DynamicDirectory{&HAMTDirectory{shard, dserv, 0}}, nil } return nil, ErrNotADir @@ -155,18 +168,16 @@ func (d *BasicDirectory) computeEstimatedSize() { d.addToEstimatedSize(l.Name, l.Cid) return nil }) -} - -func estimatedLinkSize(linkName string, linkCid cid.Cid) int { - return len(linkName) + linkCid.ByteLen() + // ForEachLink will never fail traversing the BasicDirectory + // and neither the inner callback `addToEstimatedSize`. } func (d *BasicDirectory) addToEstimatedSize(name string, linkCid cid.Cid) { - d.estimatedSize += estimatedLinkSize(name, linkCid) + d.estimatedSize += linksize.LinkSizeFunction(name, linkCid) } func (d *BasicDirectory) removeFromEstimatedSize(name string, linkCid cid.Cid) { - d.estimatedSize -= estimatedLinkSize(name, linkCid) + d.estimatedSize -= linksize.LinkSizeFunction(name, linkCid) if d.estimatedSize < 0 { // Something has gone very wrong. Log an error and recompute the // size from scratch. @@ -183,17 +194,50 @@ func (d *BasicDirectory) SetCidBuilder(builder cid.Builder) { // AddChild implements the `Directory` interface. It adds (or replaces) // a link to the given `node` under `name`. func (d *BasicDirectory) AddChild(ctx context.Context, name string, node ipld.Node) error { - // Remove old link (if it existed; ignore `ErrNotExist` otherwise). + link, err := ipld.MakeLink(node) + if err != nil { + return err + } + + return d.addLinkChild(ctx, name, link) +} + +func (d *BasicDirectory) needsToSwitchToHAMTDir(name string, nodeToAdd ipld.Node) (bool, error) { + if HAMTShardingSize == 0 { // Option disabled. + return false, nil + } + + operationSizeChange := 0 + // Find if there is an old entry under that name that will be overwritten. + entryToRemove, err := d.node.GetNodeLink(name) + if err != mdag.ErrLinkNotFound { + if err != nil { + return false, err + } + operationSizeChange -= linksize.LinkSizeFunction(name, entryToRemove.Cid) + } + if nodeToAdd != nil { + operationSizeChange += linksize.LinkSizeFunction(name, nodeToAdd.Cid()) + } + + return d.estimatedSize+operationSizeChange >= HAMTShardingSize, nil +} + +// addLinkChild adds the link as an entry to this directory under the given +// name. Plumbing function for the AddChild API. +func (d *BasicDirectory) addLinkChild(ctx context.Context, name string, link *ipld.Link) error { + // Remove old link and account for size change (if it existed; ignore + // `ErrNotExist` otherwise). err := d.RemoveChild(ctx, name) if err != nil && err != os.ErrNotExist { return err } - err = d.node.AddNodeLink(name, node) + err = d.node.AddRawLink(name, link) if err != nil { return err } - d.addToEstimatedSize(name, node.Cid()) + d.addToEstimatedSize(name, link.Cid) return nil } @@ -218,7 +262,7 @@ func (d *BasicDirectory) EnumLinksAsync(ctx context.Context) <-chan format.LinkR } // ForEachLink implements the `Directory` interface. -func (d *BasicDirectory) ForEachLink(ctx context.Context, f func(*ipld.Link) error) error { +func (d *BasicDirectory) ForEachLink(_ context.Context, f func(*ipld.Link) error) error { for _, l := range d.node.Links() { if err := f(l); err != nil { return err @@ -277,8 +321,8 @@ func (d *BasicDirectory) GetCidBuilder() cid.Builder { return d.node.CidBuilder() } -// SwitchToSharding returns a HAMT implementation of this directory. -func (d *BasicDirectory) SwitchToSharding(ctx context.Context) (Directory, error) { +// switchToSharding returns a HAMT implementation of this directory. +func (d *BasicDirectory) switchToSharding(ctx context.Context) (*HAMTDirectory, error) { hamtDir := new(HAMTDirectory) hamtDir.dserv = d.dserv @@ -311,7 +355,16 @@ func (d *HAMTDirectory) SetCidBuilder(builder cid.Builder) { // AddChild implements the `Directory` interface. func (d *HAMTDirectory) AddChild(ctx context.Context, name string, nd ipld.Node) error { - return d.shard.Set(ctx, name, nd) + oldChild, err := d.shard.Swap(ctx, name, nd) + if err != nil { + return err + } + + if oldChild != nil { + d.removeFromSizeChange(oldChild.Name, oldChild.Cid) + } + d.addToSizeChange(name, nd.Cid()) + return nil } // ForEachLink implements the `Directory` interface. @@ -342,7 +395,16 @@ func (d *HAMTDirectory) Find(ctx context.Context, name string) (ipld.Node, error // RemoveChild implements the `Directory` interface. func (d *HAMTDirectory) RemoveChild(ctx context.Context, name string) error { - return d.shard.Remove(ctx, name) + oldChild, err := d.shard.Take(ctx, name) + if err != nil { + return err + } + + if oldChild != nil { + d.removeFromSizeChange(oldChild.Name, oldChild.Cid) + } + + return nil } // GetNode implements the `Directory` interface. @@ -355,42 +417,198 @@ func (d *HAMTDirectory) GetCidBuilder() cid.Builder { return d.shard.CidBuilder() } -// UpgradeableDirectory wraps a Directory interface and provides extra logic -// to upgrade from its BasicDirectory implementation to HAMTDirectory. -type UpgradeableDirectory struct { +// switchToBasic returns a BasicDirectory implementation of this directory. +func (d *HAMTDirectory) switchToBasic(ctx context.Context) (*BasicDirectory, error) { + basicDir := newEmptyBasicDirectory(d.dserv) + basicDir.SetCidBuilder(d.GetCidBuilder()) + + err := d.ForEachLink(ctx, func(lnk *ipld.Link) error { + err := basicDir.addLinkChild(ctx, lnk.Name, lnk) + if err != nil { + return err + } + + return nil + // This function enumerates all the links in the Directory requiring all + // shards to be accessible but it is only called *after* sizeBelowThreshold + // returns true, which means we have already enumerated and fetched *all* + // shards in the first place (that's the only way we can be really sure + // we are actually below the threshold). + }) + if err != nil { + return nil, err + } + + return basicDir, nil +} + +func (d *HAMTDirectory) addToSizeChange(name string, linkCid cid.Cid) { + d.sizeChange += linksize.LinkSizeFunction(name, linkCid) +} + +func (d *HAMTDirectory) removeFromSizeChange(name string, linkCid cid.Cid) { + d.sizeChange -= linksize.LinkSizeFunction(name, linkCid) +} + +// Evaluate a switch from HAMTDirectory to BasicDirectory in case the size will +// go above the threshold when we are adding or removing an entry. +// In both the add/remove operations any old name will be removed, and for the +// add operation in particular a new entry will be added under that name (otherwise +// nodeToAdd is nil). We compute both (potential) future subtraction and +// addition to the size change. +func (d *HAMTDirectory) needsToSwitchToBasicDir(ctx context.Context, name string, nodeToAdd ipld.Node) (switchToBasic bool, err error) { + if HAMTShardingSize == 0 { // Option disabled. + return false, nil + } + + operationSizeChange := 0 + + // Find if there is an old entry under that name that will be overwritten + // (AddEntry) or flat out removed (RemoveEntry). + entryToRemove, err := d.shard.Find(ctx, name) + if err != os.ErrNotExist { + if err != nil { + return false, err + } + operationSizeChange -= linksize.LinkSizeFunction(name, entryToRemove.Cid) + } + + // For the AddEntry case compute the size addition of the new entry. + if nodeToAdd != nil { + operationSizeChange += linksize.LinkSizeFunction(name, nodeToAdd.Cid()) + } + + if d.sizeChange+operationSizeChange >= 0 { + // We won't have reduced the HAMT net size. + return false, nil + } + + // We have reduced the directory size, check if went below the + // HAMTShardingSize threshold to trigger a switch. + return d.sizeBelowThreshold(ctx, operationSizeChange) +} + +// Evaluate directory size and a future sizeChange and check if it will be below +// HAMTShardingSize threshold (to trigger a transition to a BasicDirectory). +// Instead of enumerating the entire tree we eagerly call EnumLinksAsync +// until we either reach a value above the threshold (in that case no need +// to keep counting) or an error occurs (like the context being canceled +// if we take too much time fetching the necessary shards). +func (d *HAMTDirectory) sizeBelowThreshold(ctx context.Context, sizeChange int) (below bool, err error) { + if HAMTShardingSize == 0 { + panic("asked to compute HAMT size with HAMTShardingSize option off (0)") + } + + // We don't necessarily compute the full size of *all* shards as we might + // end early if we already know we're above the threshold or run out of time. + partialSize := 0 + + // We stop the enumeration once we have enough information and exit this function. + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + for linkResult := range d.EnumLinksAsync(ctx) { + if linkResult.Err != nil { + return false, linkResult.Err + } + + partialSize += linksize.LinkSizeFunction(linkResult.Link.Name, linkResult.Link.Cid) + if partialSize+sizeChange >= HAMTShardingSize { + // We have already fetched enough shards to assert we are + // above the threshold, so no need to keep fetching. + return false, nil + } + } + + // We enumerated *all* links in all shards and didn't reach the threshold. + return true, nil +} + +// DynamicDirectory wraps a Directory interface and provides extra logic +// to switch from BasicDirectory to HAMTDirectory and backwards based on +// size. +type DynamicDirectory struct { Directory } -var _ Directory = (*UpgradeableDirectory)(nil) +var _ Directory = (*DynamicDirectory)(nil) // AddChild implements the `Directory` interface. We check when adding new entries // if we should switch to HAMTDirectory according to global option(s). -func (d *UpgradeableDirectory) AddChild(ctx context.Context, name string, nd ipld.Node) error { - err := d.Directory.AddChild(ctx, name, nd) +func (d *DynamicDirectory) AddChild(ctx context.Context, name string, nd ipld.Node) error { + hamtDir, ok := d.Directory.(*HAMTDirectory) + if ok { + // We evaluate a switch in the HAMTDirectory case even for an AddChild + // as it may overwrite an existing entry and end up actually reducing + // the directory size. + switchToBasic, err := hamtDir.needsToSwitchToBasicDir(ctx, name, nd) + if err != nil { + return err + } + + if switchToBasic { + basicDir, err := hamtDir.switchToBasic(ctx) + if err != nil { + return err + } + err = basicDir.AddChild(ctx, name, nd) + if err != nil { + return err + } + d.Directory = basicDir + return nil + } + + return d.Directory.AddChild(ctx, name, nd) + } + + // BasicDirectory + basicDir := d.Directory.(*BasicDirectory) + switchToHAMT, err := basicDir.needsToSwitchToHAMTDir(name, nd) if err != nil { return err } - - // Evaluate possible HAMT upgrade. - if HAMTShardingSize == 0 { - return nil + if !switchToHAMT { + return basicDir.AddChild(ctx, name, nd) } - basicDir, ok := d.Directory.(*BasicDirectory) + hamtDir, err = basicDir.switchToSharding(ctx) + if err != nil { + return err + } + hamtDir.AddChild(ctx, name, nd) + if err != nil { + return err + } + d.Directory = hamtDir + return nil +} + +// RemoveChild implements the `Directory` interface. Used in the case where we wrap +// a HAMTDirectory that might need to be downgraded to a BasicDirectory. The +// upgrade path is in AddChild. +func (d *DynamicDirectory) RemoveChild(ctx context.Context, name string) error { + hamtDir, ok := d.Directory.(*HAMTDirectory) if !ok { - return nil + return d.Directory.RemoveChild(ctx, name) } - if basicDir.estimatedSize >= HAMTShardingSize { - // Ideally to minimize performance we should check if this last - // `AddChild` call would bring the directory size over the threshold - // *before* executing it since we would end up switching anyway and - // that call would be "wasted". This is a minimal performance impact - // and we prioritize a simple code base. - hamtDir, err := basicDir.SwitchToSharding(ctx) - if err != nil { - return err - } - d.Directory = hamtDir + + switchToBasic, err := hamtDir.needsToSwitchToBasicDir(ctx, name, nil) + if err != nil { + return err } + if !switchToBasic { + return hamtDir.RemoveChild(ctx, name) + } + + basicDir, err := hamtDir.switchToBasic(ctx) + if err != nil { + return err + } + basicDir.RemoveChild(ctx, name) + if err != nil { + return err + } + d.Directory = basicDir return nil } diff --git a/unixfs/io/directory_test.go b/unixfs/io/directory_test.go index 8c5d8e109..f5fa2e564 100644 --- a/unixfs/io/directory_test.go +++ b/unixfs/io/directory_test.go @@ -4,13 +4,30 @@ import ( "context" "fmt" "math" + "sort" + "strconv" + "strings" + "sync" "testing" - + "time" + + blocks "github.com/ipfs/go-block-format" + bsrv "github.com/ipfs/go-blockservice" + cid "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + blockstore "github.com/ipfs/go-ipfs-blockstore" + offline "github.com/ipfs/go-ipfs-exchange-offline" ipld "github.com/ipfs/go-ipld-format" mdag "github.com/ipfs/go-merkledag" mdtest "github.com/ipfs/go-merkledag/test" ft "github.com/ipfs/go-unixfs" + "github.com/ipfs/go-unixfs/hamt" + "github.com/ipfs/go-unixfs/internal" + "github.com/ipfs/go-unixfs/private/linksize" + + "github.com/stretchr/testify/assert" ) func TestEmptyNode(t *testing.T) { @@ -100,116 +117,331 @@ func TestDuplicateAddDir(t *testing.T) { } } -// FIXME: Nothing blocking but nice to have: -// * Check estimated size against link enumeration (indirectly done in the -// restored node check from NewDirectoryFromNode). -// * Check estimated size against encoded node (the difference should only be -// a small percentage for a directory with 10s of entries). func TestBasicDirectory_estimatedSize(t *testing.T) { ds := mdtest.Mock() + basicDir := newEmptyBasicDirectory(ds) + + testDirectorySizeEstimation(t, basicDir, ds, func(dir Directory) int { + return dir.(*BasicDirectory).estimatedSize + }) +} + +func TestHAMTDirectory_sizeChange(t *testing.T) { + ds := mdtest.Mock() + hamtDir, err := newEmptyHAMTDirectory(ds, DefaultShardWidth) + assert.NoError(t, err) + + testDirectorySizeEstimation(t, hamtDir, ds, func(dir Directory) int { + // Since we created a HAMTDirectory from scratch with size 0 its + // internal sizeChange delta will in fact track the directory size + // throughout this run. + return dir.(*HAMTDirectory).sizeChange + }) +} + +func fullSizeEnumeration(dir Directory) int { + size := 0 + dir.ForEachLink(context.Background(), func(l *ipld.Link) error { + size += linksize.LinkSizeFunction(l.Name, l.Cid) + return nil + }) + return size +} + +func testDirectorySizeEstimation(t *testing.T, dir Directory, ds ipld.DAGService, size func(Directory) int) { + linksize.LinkSizeFunction = mockLinkSizeFunc(1) + defer func() { linksize.LinkSizeFunction = productionLinkSize }() + ctx := context.Background() child := ft.EmptyFileNode() - err := ds.Add(ctx, child) - if err != nil { - t.Fatal(err) - } - - basicDir := newEmptyBasicDirectory(ds) + assert.NoError(t, ds.Add(ctx, child)) // Several overwrites should not corrupt the size estimation. - basicDir.AddChild(ctx, "child", child) - basicDir.AddChild(ctx, "child", child) - basicDir.AddChild(ctx, "child", child) - basicDir.RemoveChild(ctx, "child") - basicDir.AddChild(ctx, "child", child) - basicDir.RemoveChild(ctx, "child") - // FIXME: Check errors above (abstract adds/removals in iteration). - if basicDir.estimatedSize != 0 { - t.Fatal("estimated size is not zero after removing all entries") - } - - for i := 0; i < 100; i++ { - basicDir.AddChild(ctx, fmt.Sprintf("child-%03d", i), child) // e.g., "child-045" - } - // Estimated entry size: name (9) + CID (32 from hash and 2 extra for header) - entrySize := 9 + 32 + 2 - expectedSize := 100 * entrySize - if basicDir.estimatedSize != expectedSize { - t.Fatalf("estimated size (%d) inaccurate after adding many entries (expected %d)", - basicDir.estimatedSize, expectedSize) - } - - basicDir.RemoveChild(ctx, "child-045") // just random values - basicDir.RemoveChild(ctx, "child-063") - basicDir.RemoveChild(ctx, "child-011") - basicDir.RemoveChild(ctx, "child-000") - basicDir.RemoveChild(ctx, "child-099") - - basicDir.RemoveChild(ctx, "child-045") // already removed, won't impact size - basicDir.RemoveChild(ctx, "nonexistent-name") // also doesn't count - basicDir.RemoveChild(ctx, "child-100") // same - expectedSize -= 5 * entrySize - if basicDir.estimatedSize != expectedSize { - t.Fatalf("estimated size (%d) inaccurate after removing some entries (expected %d)", - basicDir.estimatedSize, expectedSize) - } + assert.NoError(t, dir.AddChild(ctx, "child", child)) + assert.NoError(t, dir.AddChild(ctx, "child", child)) + assert.NoError(t, dir.AddChild(ctx, "child", child)) + assert.NoError(t, dir.RemoveChild(ctx, "child")) + assert.NoError(t, dir.AddChild(ctx, "child", child)) + assert.NoError(t, dir.RemoveChild(ctx, "child")) + assert.Equal(t, 0, size(dir), "estimated size is not zero after removing all entries") + + dirEntries := 100 + for i := 0; i < dirEntries; i++ { + assert.NoError(t, dir.AddChild(ctx, fmt.Sprintf("child-%03d", i), child)) + } + assert.Equal(t, dirEntries, size(dir), "estimated size inaccurate after adding many entries") + + assert.NoError(t, dir.RemoveChild(ctx, "child-045")) // just random values + assert.NoError(t, dir.RemoveChild(ctx, "child-063")) + assert.NoError(t, dir.RemoveChild(ctx, "child-011")) + assert.NoError(t, dir.RemoveChild(ctx, "child-000")) + assert.NoError(t, dir.RemoveChild(ctx, "child-099")) + dirEntries -= 5 + assert.Equal(t, dirEntries, size(dir), "estimated size inaccurate after removing some entries") + + // All of the following remove operations will fail (won't impact dirEntries): + assert.Error(t, dir.RemoveChild(ctx, "nonexistent-name")) + assert.Error(t, dir.RemoveChild(ctx, "child-045")) // already removed + assert.Error(t, dir.RemoveChild(ctx, "child-100")) + assert.Equal(t, dirEntries, size(dir), "estimated size inaccurate after failed remove attempts") // Restore a directory from original's node and check estimated size consistency. - basicDirSingleNode, _ := basicDir.GetNode() // no possible error - restoredBasicDir := newBasicDirectoryFromNode(ds, basicDirSingleNode.(*mdag.ProtoNode)) - if basicDir.estimatedSize != restoredBasicDir.estimatedSize { - t.Fatalf("restored basic directory size (%d) doesn't match original estimate (%d)", - basicDir.estimatedSize, restoredBasicDir.estimatedSize) + dirNode, err := dir.GetNode() + assert.NoError(t, err) + restoredDir, err := NewDirectoryFromNode(ds, dirNode.(*mdag.ProtoNode)) + assert.NoError(t, err) + assert.Equal(t, size(dir), fullSizeEnumeration(restoredDir), "restored directory's size doesn't match original's") + // We don't use the estimation size function for the restored directory + // because in the HAMT case this function depends on the sizeChange variable + // that will be cleared when loading the directory from the node. + // This also covers the case of comparing the size estimation `size()` with + // the full enumeration function `fullSizeEnumeration()` to make sure it's + // correct. +} + +// Any entry link size will have the fixedSize passed. +func mockLinkSizeFunc(fixedSize int) func(linkName string, linkCid cid.Cid) int { + return func(_ string, _ cid.Cid) int { + return fixedSize } } -// Basic test on extreme threshold to trigger switch. More fine-grained sizes -// are checked in TestBasicDirectory_estimatedSize (without the swtich itself -// but focusing on the size computation). -// FIXME: Ideally, instead of checking size computation on one test and directory -// upgrade on another a better structured test should test both dimensions -// simultaneously. -func TestUpgradeableDirectory(t *testing.T) { +func checkBasicDirectory(t *testing.T, dir Directory, errorMessage string) { + if _, ok := dir.(*DynamicDirectory).Directory.(*BasicDirectory); !ok { + t.Fatal(errorMessage) + } +} + +func checkHAMTDirectory(t *testing.T, dir Directory, errorMessage string) { + if _, ok := dir.(*DynamicDirectory).Directory.(*HAMTDirectory); !ok { + t.Fatal(errorMessage) + } +} + +func TestProductionLinkSize(t *testing.T) { + link, err := ipld.MakeLink(ft.EmptyDirNode()) + assert.NoError(t, err) + link.Name = "directory_link_name" + assert.Equal(t, 53, productionLinkSize(link.Name, link.Cid)) + + link, err = ipld.MakeLink(ft.EmptyFileNode()) + assert.NoError(t, err) + link.Name = "file_link_name" + assert.Equal(t, 48, productionLinkSize(link.Name, link.Cid)) + + ds := mdtest.Mock() + basicDir := newEmptyBasicDirectory(ds) + assert.NoError(t, err) + for i := 0; i < 10; i++ { + basicDir.AddChild(context.Background(), strconv.FormatUint(uint64(i), 10), ft.EmptyFileNode()) + } + basicDirNode, err := basicDir.GetNode() + assert.NoError(t, err) + link, err = ipld.MakeLink(basicDirNode) + assert.NoError(t, err) + link.Name = "basic_dir" + assert.Equal(t, 43, productionLinkSize(link.Name, link.Cid)) +} + +// Test HAMTDirectory <-> BasicDirectory switch based on directory size. The +// switch is managed by the DynamicDirectory abstraction. +func TestDynamicDirectorySwitch(t *testing.T) { oldHamtOption := HAMTShardingSize defer func() { HAMTShardingSize = oldHamtOption }() + HAMTShardingSize = 0 // Disable automatic switch at the start. + linksize.LinkSizeFunction = mockLinkSizeFunc(1) + defer func() { linksize.LinkSizeFunction = productionLinkSize }() ds := mdtest.Mock() dir := NewDirectory(ds) + checkBasicDirectory(t, dir, "new dir is not BasicDirectory") + ctx := context.Background() child := ft.EmptyDirNode() err := ds.Add(ctx, child) - if err != nil { - t.Fatal(err) - } + assert.NoError(t, err) - HAMTShardingSize = 0 // Create a BasicDirectory. - if _, ok := dir.(*UpgradeableDirectory).Directory.(*BasicDirectory); !ok { - t.Fatal("UpgradeableDirectory doesn't contain BasicDirectory") - } + err = dir.AddChild(ctx, "1", child) + assert.NoError(t, err) + checkBasicDirectory(t, dir, "added child, option still disabled") // Set a threshold so big a new entry won't trigger the change. HAMTShardingSize = math.MaxInt32 - err = dir.AddChild(ctx, "test", child) - if err != nil { - t.Fatal(err) - } - - if _, ok := dir.(*UpgradeableDirectory).Directory.(*HAMTDirectory); ok { - t.Fatal("UpgradeableDirectory was upgraded to HAMTDirectory for a large threshold") - } + err = dir.AddChild(ctx, "2", child) + assert.NoError(t, err) + checkBasicDirectory(t, dir, "added child, option now enabled but at max") // Now set it so low to make sure any new entry will trigger the upgrade. HAMTShardingSize = 1 - err = dir.AddChild(ctx, "test", child) // overwriting an entry should also trigger the switch - if err != nil { - t.Fatal(err) + // We are already above the threshold, we trigger the switch with an overwrite + // (any AddChild() should reevaluate the size). + err = dir.AddChild(ctx, "2", child) + assert.NoError(t, err) + checkHAMTDirectory(t, dir, "added child, option at min, should switch up") + + // Set threshold at the number of current entries and delete the last one + // to trigger a switch and evaluate if the rest of the entries are conserved. + HAMTShardingSize = 2 + err = dir.RemoveChild(ctx, "2") + assert.NoError(t, err) + checkBasicDirectory(t, dir, "removed threshold entry, option at min, should switch down") +} + +func TestIntegrityOfDirectorySwitch(t *testing.T) { + ds := mdtest.Mock() + dir := NewDirectory(ds) + checkBasicDirectory(t, dir, "new dir is not BasicDirectory") + + ctx := context.Background() + child := ft.EmptyDirNode() + err := ds.Add(ctx, child) + assert.NoError(t, err) + + basicDir := newEmptyBasicDirectory(ds) + hamtDir, err := newEmptyHAMTDirectory(ds, DefaultShardWidth) + assert.NoError(t, err) + for i := 0; i < 1000; i++ { + basicDir.AddChild(ctx, strconv.FormatUint(uint64(i), 10), child) + hamtDir.AddChild(ctx, strconv.FormatUint(uint64(i), 10), child) + } + compareDirectoryEntries(t, basicDir, hamtDir) + + hamtDirFromSwitch, err := basicDir.switchToSharding(ctx) + assert.NoError(t, err) + basicDirFromSwitch, err := hamtDir.switchToBasic(ctx) + assert.NoError(t, err) + compareDirectoryEntries(t, basicDir, basicDirFromSwitch) + compareDirectoryEntries(t, hamtDir, hamtDirFromSwitch) +} + +// This is the value of concurrent fetches during dag.Walk. Used in +// test to better predict how many nodes will be fetched. +var defaultConcurrentFetch = 32 + +// FIXME: Taken from private github.com/ipfs/go-merkledag@v0.2.3/merkledag.go. +// (We can also pass an explicit concurrency value in `(*Shard).EnumLinksAsync()` +// and take ownership of this configuration, but departing from the more +// standard and reliable one in `go-merkledag`. + +// Test that we fetch as little nodes as needed to reach the HAMTShardingSize +// during the sizeBelowThreshold computation. +func TestHAMTEnumerationWhenComputingSize(t *testing.T) { + // Adjust HAMT global/static options for the test to simplify its logic. + // FIXME: These variables weren't designed to be modified and we should + // review in depth side effects. + + // Set all link sizes to a uniform 1 so the estimated directory size + // is just the count of its entry links (in HAMT/Shard terminology these + // are the "value" links pointing to anything that is *not* another Shard). + linksize.LinkSizeFunction = mockLinkSizeFunc(1) + defer func() { linksize.LinkSizeFunction = productionLinkSize }() + + // Use an identity hash function to ease the construction of "complete" HAMTs + // (see CreateCompleteHAMT below for more details). (Ideally this should be + // a parameter we pass and not a global option we modify in the caller.) + oldHashFunc := internal.HAMTHashFunction + defer func() { internal.HAMTHashFunction = oldHashFunc }() + internal.HAMTHashFunction = idHash + + oldHamtOption := HAMTShardingSize + defer func() { HAMTShardingSize = oldHamtOption }() + + // --- End of test static configuration adjustments. --- + + // Some arbitrary values below that make this test not that expensive. + treeHeight := 4 + // How many leaf shards nodes (with value links, + // i.e., directory entries) do we need to reach the threshold. + thresholdToWidthRatio := 4 + // Departing from DefaultShardWidth of 256 to reduce HAMT size in + // CreateCompleteHAMT. + shardWidth := 16 + HAMTShardingSize = shardWidth * thresholdToWidthRatio + + // We create a "complete" HAMT (see CreateCompleteHAMT for more details) + // with a regular structure to be able to predict how many Shard nodes we + // will need to fetch in order to reach the HAMTShardingSize threshold in + // sizeBelowThreshold (assuming a sequential DAG walk function). + + bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) + countGetsDS := newCountGetsDS(bstore) + dsrv := mdag.NewDAGService(bsrv.New(countGetsDS, offline.Exchange(countGetsDS))) + completeHAMTRoot, err := CreateCompleteHAMT(dsrv, treeHeight, shardWidth) + assert.NoError(t, err) + + // Calculate the optimal number of nodes to traverse + optimalNodesToFetch := 0 + nodesToProcess := HAMTShardingSize + for i := 0; i < treeHeight-1; i++ { + // divide by the shard width to get the parents and continue up the tree + parentNodes := int(math.Ceil(float64(nodesToProcess) / float64(shardWidth))) + optimalNodesToFetch += parentNodes + nodesToProcess = parentNodes + } + + // With this structure and a BFS traversal (from `parallelWalkDepth`) then + // we would roughly fetch the following nodes: + nodesToFetch := 0 + // * all layers up to (but not including) the last one with leaf nodes + // (because it's a BFS) + for i := 0; i < treeHeight-1; i++ { + nodesToFetch += int(math.Pow(float64(shardWidth), float64(i))) + } + // * `thresholdToWidthRatio` leaf Shards with enough value links to reach + // the HAMTShardingSize threshold. + nodesToFetch += thresholdToWidthRatio + + hamtDir, err := newHAMTDirectoryFromNode(dsrv, completeHAMTRoot) + assert.NoError(t, err) + + countGetsDS.resetCounter() + countGetsDS.setRequestDelay(10 * time.Millisecond) + // (Without the `setRequestDelay` above the number of nodes fetched + // drops dramatically and unpredictably as the BFS starts to behave + // more like a DFS because some search paths are fetched faster than + // others.) + below, err := hamtDir.sizeBelowThreshold(context.TODO(), 0) + assert.NoError(t, err) + assert.False(t, below) + t.Logf("fetched %d nodes (predicted range: %d-%d)", + countGetsDS.uniqueCidsFetched(), optimalNodesToFetch, nodesToFetch+defaultConcurrentFetch) + // Check that the actual number of nodes fetched is within the margin of the + // estimated `nodesToFetch` plus an extra of `defaultConcurrentFetch` since + // we are fetching in parallel. + assert.True(t, countGetsDS.uniqueCidsFetched() <= nodesToFetch+defaultConcurrentFetch) + assert.True(t, countGetsDS.uniqueCidsFetched() >= optimalNodesToFetch) +} + +// Compare entries in the leftDir against the rightDir and possibly +// missingEntries in the second. +func compareDirectoryEntries(t *testing.T, leftDir Directory, rightDir Directory) { + leftLinks, err := getAllLinksSortedByName(leftDir) + assert.NoError(t, err) + rightLinks, err := getAllLinksSortedByName(rightDir) + assert.NoError(t, err) + + assert.Equal(t, len(leftLinks), len(rightLinks)) + + for i, leftLink := range leftLinks { + assert.Equal(t, leftLink, rightLinks[i]) // FIXME: Can we just compare the entire struct? } +} - if _, ok := dir.(*UpgradeableDirectory).Directory.(*HAMTDirectory); !ok { - t.Fatal("UpgradeableDirectory wasn't upgraded to HAMTDirectory for a low threshold") +func getAllLinksSortedByName(d Directory) ([]*ipld.Link, error) { + entries, err := d.Links(context.Background()) + if err != nil { + return nil, err } + sortLinksByName(entries) + return entries, nil +} + +func sortLinksByName(l []*ipld.Link) { + sort.SliceStable(l, func(i, j int) bool { + return strings.Compare(l[i].Name, l[j].Name) == -1 // FIXME: Is this correct? + }) } func TestDirBuilder(t *testing.T) { @@ -296,3 +528,111 @@ func TestDirBuilder(t *testing.T) { t.Fatal("wrong number of links", len(asyncLinks), count) } } + +func newHAMTDirectoryFromNode(dserv ipld.DAGService, node ipld.Node) (*HAMTDirectory, error) { + shard, err := hamt.NewHamtFromDag(dserv, node) + if err != nil { + return nil, err + } + return &HAMTDirectory{ + dserv: dserv, + shard: shard, + }, nil +} + +func newEmptyHAMTDirectory(dserv ipld.DAGService, shardWidth int) (*HAMTDirectory, error) { + shard, err := hamt.NewShard(dserv, shardWidth) + if err != nil { + return nil, err + } + + return &HAMTDirectory{ + dserv: dserv, + shard: shard, + }, nil +} + +// countGetsDS is a DAG service that keeps track of the number of +// unique CIDs fetched. +type countGetsDS struct { + blockstore.Blockstore + + cidsFetched map[cid.Cid]struct{} + mapLock sync.Mutex + started bool + + getRequestDelay time.Duration +} + +var _ blockstore.Blockstore = (*countGetsDS)(nil) + +func newCountGetsDS(bs blockstore.Blockstore) *countGetsDS { + return &countGetsDS{ + bs, + make(map[cid.Cid]struct{}), + sync.Mutex{}, + false, + 0, + } +} + +func (d *countGetsDS) resetCounter() { + d.mapLock.Lock() + defer d.mapLock.Unlock() + d.cidsFetched = make(map[cid.Cid]struct{}) + d.started = true +} + +func (d *countGetsDS) uniqueCidsFetched() int { + d.mapLock.Lock() + defer d.mapLock.Unlock() + return len(d.cidsFetched) +} + +func (d *countGetsDS) setRequestDelay(timeout time.Duration) { + d.getRequestDelay = timeout +} + +func (d *countGetsDS) maybeSleep(c cid.Cid) { + d.mapLock.Lock() + _, cidRequestedBefore := d.cidsFetched[c] + d.cidsFetched[c] = struct{}{} + d.mapLock.Unlock() + + if d.getRequestDelay != 0 && !cidRequestedBefore { + // First request gets a timeout to simulate a network fetch. + // Subsequent requests get no timeout simulating an in-disk cache. + time.Sleep(d.getRequestDelay) + } +} + +func (d *countGetsDS) Has(c cid.Cid) (bool, error) { + if d.started { + panic("implement me") + } + return d.Blockstore.Has(c) +} + +func (d *countGetsDS) Get(c cid.Cid) (blocks.Block, error) { + blk, err := d.Blockstore.Get(c) + if err != nil { + return nil, err + } + + d.maybeSleep(c) + return blk, nil +} + +func (d *countGetsDS) GetSize(c cid.Cid) (int, error) { + if d.started { + panic("implement me") + } + return d.Blockstore.GetSize(c) +} + +func (d *countGetsDS) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { + if d.started { + panic("implement me") + } + return d.Blockstore.AllKeysChan(ctx) +} diff --git a/unixfs/private/linksize/linksize.go b/unixfs/private/linksize/linksize.go new file mode 100644 index 000000000..e7ae098b6 --- /dev/null +++ b/unixfs/private/linksize/linksize.go @@ -0,0 +1,5 @@ +package linksize + +import "github.com/ipfs/go-cid" + +var LinkSizeFunction func(linkName string, linkCid cid.Cid) int From 2614f3ecb4532255fbaf8ad544b58fd4e563b0de Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Fri, 12 Nov 2021 13:41:18 -0500 Subject: [PATCH 3608/3817] feat: add context to interfaces (#90) This commit was moved from ipfs/go-ipfs-blockstore@60f8b66fddb5bf2fa7c1b6b6ee04370e05c23035 --- blockstore/arc_cache.go | 46 +++++++------- blockstore/arc_cache_test.go | 112 ++++++++++++++++----------------- blockstore/blockstore.go | 68 ++++++++++---------- blockstore/blockstore_test.go | 72 ++++++++++----------- blockstore/bloom_cache.go | 46 +++++++------- blockstore/bloom_cache_test.go | 58 ++++++++--------- blockstore/idstore.go | 34 +++++----- blockstore/idstore_test.go | 36 +++++------ 8 files changed, 237 insertions(+), 235 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 7f859f342..09aa44138 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -53,7 +53,7 @@ func mutexKey(k cid.Cid) uint8 { return k.KeyString()[len(k.KeyString())-1] } -func (b *arccache) DeleteBlock(k cid.Cid) error { +func (b *arccache) DeleteBlock(ctx context.Context, k cid.Cid) error { if !k.Defined() { return nil } @@ -67,14 +67,14 @@ func (b *arccache) DeleteBlock(k cid.Cid) error { defer lk.Unlock() b.cache.Remove(k) // Invalidate cache before deleting. - err := b.blockstore.DeleteBlock(k) + err := b.blockstore.DeleteBlock(ctx, k) if err == nil { b.cacheHave(k, false) } return err } -func (b *arccache) Has(k cid.Cid) (bool, error) { +func (b *arccache) Has(ctx context.Context, k cid.Cid) (bool, error) { if !k.Defined() { return false, nil } @@ -87,7 +87,7 @@ func (b *arccache) Has(k cid.Cid) (bool, error) { lk.RLock() defer lk.RUnlock() - has, err := b.blockstore.Has(k) + has, err := b.blockstore.Has(ctx, k) if err != nil { return false, err } @@ -95,7 +95,7 @@ func (b *arccache) Has(k cid.Cid) (bool, error) { return has, nil } -func (b *arccache) GetSize(k cid.Cid) (int, error) { +func (b *arccache) GetSize(ctx context.Context, k cid.Cid) (int, error) { if !k.Defined() { return -1, ErrNotFound } @@ -116,7 +116,7 @@ func (b *arccache) GetSize(k cid.Cid) (int, error) { lk.RLock() defer lk.RUnlock() - blockSize, err := b.blockstore.GetSize(k) + blockSize, err := b.blockstore.GetSize(ctx, k) if err == ErrNotFound { b.cacheHave(k, false) } else if err == nil { @@ -125,11 +125,11 @@ func (b *arccache) GetSize(k cid.Cid) (int, error) { return blockSize, err } -func (b *arccache) View(k cid.Cid, callback func([]byte) error) error { +func (b *arccache) View(ctx context.Context, k cid.Cid, callback func([]byte) error) error { // shortcircuit and fall back to Get if the underlying store // doesn't support Viewer. if b.viewer == nil { - blk, err := b.Get(k) + blk, err := b.Get(ctx, k) if err != nil { return err } @@ -150,10 +150,10 @@ func (b *arccache) View(k cid.Cid, callback func([]byte) error) error { lk.RLock() defer lk.RUnlock() - return b.viewer.View(k, callback) + return b.viewer.View(ctx, k, callback) } -func (b *arccache) Get(k cid.Cid) (blocks.Block, error) { +func (b *arccache) Get(ctx context.Context, k cid.Cid) (blocks.Block, error) { if !k.Defined() { return nil, ErrNotFound } @@ -166,7 +166,7 @@ func (b *arccache) Get(k cid.Cid) (blocks.Block, error) { lk.RLock() defer lk.RUnlock() - bl, err := b.blockstore.Get(k) + bl, err := b.blockstore.Get(ctx, k) if bl == nil && err == ErrNotFound { b.cacheHave(k, false) } else if bl != nil { @@ -175,7 +175,7 @@ func (b *arccache) Get(k cid.Cid) (blocks.Block, error) { return bl, err } -func (b *arccache) Put(bl blocks.Block) error { +func (b *arccache) Put(ctx context.Context, bl blocks.Block) error { if has, _, ok := b.queryCache(bl.Cid()); ok && has { return nil } @@ -184,14 +184,14 @@ func (b *arccache) Put(bl blocks.Block) error { lk.Lock() defer lk.Unlock() - err := b.blockstore.Put(bl) + err := b.blockstore.Put(ctx, bl) if err == nil { b.cacheSize(bl.Cid(), len(bl.RawData())) } return err } -func (b *arccache) PutMany(bs []blocks.Block) error { +func (b *arccache) PutMany(ctx context.Context, bs []blocks.Block) error { mxs := [256]*sync.RWMutex{} var good []blocks.Block for _, block := range bs { @@ -217,7 +217,7 @@ func (b *arccache) PutMany(bs []blocks.Block) error { } }() - err := b.blockstore.PutMany(good) + err := b.blockstore.PutMany(ctx, good) if err != nil { return err } @@ -227,8 +227,8 @@ func (b *arccache) PutMany(bs []blocks.Block) error { return nil } -func (b *arccache) HashOnRead(enabled bool) { - b.blockstore.HashOnRead(enabled) +func (b *arccache) HashOnRead(ctx context.Context, enabled bool) { + b.blockstore.HashOnRead(ctx, enabled) } func (b *arccache) cacheHave(c cid.Cid, have bool) { @@ -276,14 +276,14 @@ func (b *arccache) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return b.blockstore.AllKeysChan(ctx) } -func (b *arccache) GCLock() Unlocker { - return b.blockstore.(GCBlockstore).GCLock() +func (b *arccache) GCLock(ctx context.Context) Unlocker { + return b.blockstore.(GCBlockstore).GCLock(ctx) } -func (b *arccache) PinLock() Unlocker { - return b.blockstore.(GCBlockstore).PinLock() +func (b *arccache) PinLock(ctx context.Context) Unlocker { + return b.blockstore.(GCBlockstore).PinLock(ctx) } -func (b *arccache) GCRequested() bool { - return b.blockstore.(GCBlockstore).GCRequested() +func (b *arccache) GCRequested(ctx context.Context) bool { + return b.blockstore.(GCBlockstore).GCRequested(ctx) } diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 64f45df6c..992cd2688 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -52,7 +52,7 @@ func untrap(cd *callbackDatastore) { func TestRemoveCacheEntryOnDelete(t *testing.T) { arc, _, cd := createStores(t) - arc.Put(exampleBlock) + arc.Put(bg, exampleBlock) cd.Lock() writeHitTheDatastore := false @@ -62,8 +62,8 @@ func TestRemoveCacheEntryOnDelete(t *testing.T) { writeHitTheDatastore = true }) - arc.DeleteBlock(exampleBlock.Cid()) - arc.Put(exampleBlock) + arc.DeleteBlock(bg, exampleBlock.Cid()) + arc.Put(bg, exampleBlock) if !writeHitTheDatastore { t.Fail() } @@ -72,29 +72,29 @@ func TestRemoveCacheEntryOnDelete(t *testing.T) { func TestElideDuplicateWrite(t *testing.T) { arc, _, cd := createStores(t) - arc.Put(exampleBlock) + arc.Put(bg, exampleBlock) trap("write hit datastore", cd, t) - arc.Put(exampleBlock) + arc.Put(bg, exampleBlock) } func TestHasRequestTriggersCache(t *testing.T) { arc, _, cd := createStores(t) - arc.Has(exampleBlock.Cid()) + arc.Has(bg, exampleBlock.Cid()) trap("has hit datastore", cd, t) - if has, err := arc.Has(exampleBlock.Cid()); has || err != nil { + if has, err := arc.Has(bg, exampleBlock.Cid()); has || err != nil { t.Fatal("has was true but there is no such block") } untrap(cd) - err := arc.Put(exampleBlock) + err := arc.Put(bg, exampleBlock) if err != nil { t.Fatal(err) } trap("has hit datastore", cd, t) - if has, err := arc.Has(exampleBlock.Cid()); !has || err != nil { + if has, err := arc.Has(bg, exampleBlock.Cid()); !has || err != nil { t.Fatal("has returned invalid result") } } @@ -102,31 +102,31 @@ func TestHasRequestTriggersCache(t *testing.T) { func TestGetFillsCache(t *testing.T) { arc, _, cd := createStores(t) - if bl, err := arc.Get(exampleBlock.Cid()); bl != nil || err == nil { + if bl, err := arc.Get(bg, exampleBlock.Cid()); bl != nil || err == nil { t.Fatal("block was found or there was no error") } trap("has hit datastore", cd, t) - if has, err := arc.Has(exampleBlock.Cid()); has || err != nil { + if has, err := arc.Has(bg, exampleBlock.Cid()); has || err != nil { t.Fatal("has was true but there is no such block") } - if _, err := arc.GetSize(exampleBlock.Cid()); err != ErrNotFound { + if _, err := arc.GetSize(bg, exampleBlock.Cid()); err != ErrNotFound { t.Fatal("getsize was true but there is no such block") } untrap(cd) - if err := arc.Put(exampleBlock); err != nil { + if err := arc.Put(bg, exampleBlock); err != nil { t.Fatal(err) } trap("has hit datastore", cd, t) - if has, err := arc.Has(exampleBlock.Cid()); !has || err != nil { + if has, err := arc.Has(bg, exampleBlock.Cid()); !has || err != nil { t.Fatal("has returned invalid result") } - if blockSize, err := arc.GetSize(exampleBlock.Cid()); blockSize == -1 || err != nil { + if blockSize, err := arc.GetSize(bg, exampleBlock.Cid()); blockSize == -1 || err != nil { t.Fatal("getsize returned invalid result", blockSize, err) } } @@ -134,16 +134,16 @@ func TestGetFillsCache(t *testing.T) { func TestGetAndDeleteFalseShortCircuit(t *testing.T) { arc, _, cd := createStores(t) - arc.Has(exampleBlock.Cid()) - arc.GetSize(exampleBlock.Cid()) + arc.Has(bg, exampleBlock.Cid()) + arc.GetSize(bg, exampleBlock.Cid()) trap("get hit datastore", cd, t) - if bl, err := arc.Get(exampleBlock.Cid()); bl != nil || err != ErrNotFound { + if bl, err := arc.Get(bg, exampleBlock.Cid()); bl != nil || err != ErrNotFound { t.Fatal("get returned invalid result") } - if arc.DeleteBlock(exampleBlock.Cid()) != nil { + if arc.DeleteBlock(bg, exampleBlock.Cid()) != nil { t.Fatal("expected deletes to be idempotent") } } @@ -157,7 +157,7 @@ func TestArcCreationFailure(t *testing.T) { func TestInvalidKey(t *testing.T) { arc, _, _ := createStores(t) - bl, err := arc.Get(cid.Cid{}) + bl, err := arc.Get(bg, cid.Cid{}) if bl != nil { t.Fatal("blocks should be nil") @@ -170,30 +170,30 @@ func TestInvalidKey(t *testing.T) { func TestHasAfterSucessfulGetIsCached(t *testing.T) { arc, bs, cd := createStores(t) - bs.Put(exampleBlock) + bs.Put(bg, exampleBlock) - arc.Get(exampleBlock.Cid()) + arc.Get(bg, exampleBlock.Cid()) trap("has hit datastore", cd, t) - arc.Has(exampleBlock.Cid()) + arc.Has(bg, exampleBlock.Cid()) } func TestGetSizeAfterSucessfulGetIsCached(t *testing.T) { arc, bs, cd := createStores(t) - bs.Put(exampleBlock) + bs.Put(bg, exampleBlock) - arc.Get(exampleBlock.Cid()) + arc.Get(bg, exampleBlock.Cid()) trap("has hit datastore", cd, t) - arc.GetSize(exampleBlock.Cid()) + arc.GetSize(bg, exampleBlock.Cid()) } func TestGetSizeAfterSucessfulHas(t *testing.T) { arc, bs, _ := createStores(t) - bs.Put(exampleBlock) - has, err := arc.Has(exampleBlock.Cid()) + bs.Put(bg, exampleBlock) + has, err := arc.Has(bg, exampleBlock.Cid()) if err != nil { t.Fatal(err) } @@ -201,7 +201,7 @@ func TestGetSizeAfterSucessfulHas(t *testing.T) { t.Fatal("expected to have block") } - if size, err := arc.GetSize(exampleBlock.Cid()); err != nil { + if size, err := arc.GetSize(bg, exampleBlock.Cid()); err != nil { t.Fatal(err) } else if size != len(exampleBlock.RawData()) { t.Fatalf("expected size %d, got %d", len(exampleBlock.RawData()), size) @@ -213,20 +213,20 @@ func TestGetSizeMissingZeroSizeBlock(t *testing.T) { emptyBlock := blocks.NewBlock([]byte{}) missingBlock := blocks.NewBlock([]byte("missingBlock")) - bs.Put(emptyBlock) + bs.Put(bg, emptyBlock) - arc.Get(emptyBlock.Cid()) + arc.Get(bg, emptyBlock.Cid()) trap("has hit datastore", cd, t) - if blockSize, err := arc.GetSize(emptyBlock.Cid()); blockSize != 0 || err != nil { + if blockSize, err := arc.GetSize(bg, emptyBlock.Cid()); blockSize != 0 || err != nil { t.Fatal("getsize returned invalid result") } untrap(cd) - arc.Get(missingBlock.Cid()) + arc.Get(bg, missingBlock.Cid()) trap("has hit datastore", cd, t) - if _, err := arc.GetSize(missingBlock.Cid()); err != ErrNotFound { + if _, err := arc.GetSize(bg, missingBlock.Cid()); err != ErrNotFound { t.Fatal("getsize returned invalid result") } } @@ -234,9 +234,9 @@ func TestGetSizeMissingZeroSizeBlock(t *testing.T) { func TestDifferentKeyObjectsWork(t *testing.T) { arc, bs, cd := createStores(t) - bs.Put(exampleBlock) + bs.Put(bg, exampleBlock) - arc.Get(exampleBlock.Cid()) + arc.Get(bg, exampleBlock.Cid()) trap("has hit datastore", cd, t) cidstr := exampleBlock.Cid().String() @@ -246,38 +246,38 @@ func TestDifferentKeyObjectsWork(t *testing.T) { t.Fatal(err) } - arc.Has(ncid) + arc.Has(bg, ncid) } func TestPutManyCaches(t *testing.T) { t.Run("happy path PutMany", func(t *testing.T) { arc, _, cd := createStores(t) - arc.PutMany([]blocks.Block{exampleBlock}) + arc.PutMany(bg, []blocks.Block{exampleBlock}) trap("has hit datastore", cd, t) - arc.Has(exampleBlock.Cid()) - arc.GetSize(exampleBlock.Cid()) + arc.Has(bg, exampleBlock.Cid()) + arc.GetSize(bg, exampleBlock.Cid()) untrap(cd) - arc.DeleteBlock(exampleBlock.Cid()) + arc.DeleteBlock(bg, exampleBlock.Cid()) - arc.Put(exampleBlock) + arc.Put(bg, exampleBlock) trap("PunMany has hit datastore", cd, t) - arc.PutMany([]blocks.Block{exampleBlock}) + arc.PutMany(bg, []blocks.Block{exampleBlock}) }) t.Run("PutMany with duplicates", func(t *testing.T) { arc, _, cd := createStores(t) - arc.PutMany([]blocks.Block{exampleBlock, exampleBlock}) + arc.PutMany(bg, []blocks.Block{exampleBlock, exampleBlock}) trap("has hit datastore", cd, t) - arc.Has(exampleBlock.Cid()) - arc.GetSize(exampleBlock.Cid()) + arc.Has(bg, exampleBlock.Cid()) + arc.GetSize(bg, exampleBlock.Cid()) untrap(cd) - arc.DeleteBlock(exampleBlock.Cid()) + arc.DeleteBlock(bg, exampleBlock.Cid()) - arc.Put(exampleBlock) + arc.Put(bg, exampleBlock) trap("PunMany has hit datastore", cd, t) - arc.PutMany([]blocks.Block{exampleBlock}) + arc.PutMany(bg, []blocks.Block{exampleBlock}) }) } @@ -307,7 +307,7 @@ func BenchmarkARCCacheConcurrentOps(b *testing.B) { putHalfBlocks := func(arc *arccache) { for i, block := range dummyBlocks { if i%2 == 0 { - if err := arc.Put(block); err != nil { + if err := arc.Put(bg, block); err != nil { b.Fatal(err) } } @@ -322,26 +322,26 @@ func BenchmarkARCCacheConcurrentOps(b *testing.B) { }{ {"PutDelete", [...]func(*arccache, blocks.Block){ func(arc *arccache, block blocks.Block) { - arc.Put(block) + arc.Put(bg, block) }, func(arc *arccache, block blocks.Block) { - arc.DeleteBlock(block.Cid()) + arc.DeleteBlock(bg, block.Cid()) }, }}, {"GetDelete", [...]func(*arccache, blocks.Block){ func(arc *arccache, block blocks.Block) { - arc.Get(block.Cid()) + arc.Get(bg, block.Cid()) }, func(arc *arccache, block blocks.Block) { - arc.DeleteBlock(block.Cid()) + arc.DeleteBlock(bg, block.Cid()) }, }}, {"GetPut", [...]func(*arccache, blocks.Block){ func(arc *arccache, block blocks.Block) { - arc.Get(block.Cid()) + arc.Get(bg, block.Cid()) }, func(arc *arccache, block blocks.Block) { - arc.Put(block) + arc.Put(bg, block) }, }}, } diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 0f9686683..dfac6ce42 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -33,19 +33,19 @@ var ErrNotFound = errors.New("blockstore: block not found") // Blockstore wraps a Datastore block-centered methods and provides a layer // of abstraction which allows to add different caching strategies. type Blockstore interface { - DeleteBlock(cid.Cid) error - Has(cid.Cid) (bool, error) - Get(cid.Cid) (blocks.Block, error) + DeleteBlock(context.Context, cid.Cid) error + Has(context.Context, cid.Cid) (bool, error) + Get(context.Context, cid.Cid) (blocks.Block, error) // GetSize returns the CIDs mapped BlockSize - GetSize(cid.Cid) (int, error) + GetSize(context.Context, cid.Cid) (int, error) // Put puts a given block to the underlying datastore - Put(blocks.Block) error + Put(context.Context, blocks.Block) error // PutMany puts a slice of blocks at the same time using batching // capabilities of the underlying datastore whenever possible. - PutMany([]blocks.Block) error + PutMany(context.Context, []blocks.Block) error // AllKeysChan returns a channel from which // the CIDs in the Blockstore can be read. It should respect @@ -54,7 +54,7 @@ type Blockstore interface { // HashOnRead specifies if every read block should be // rehashed to make sure it matches its CID. - HashOnRead(enabled bool) + HashOnRead(ctx context.Context, enabled bool) } // Viewer can be implemented by blockstores that offer zero-copy access to @@ -69,7 +69,7 @@ type Blockstore interface { // the block is found); otherwise, the error will be propagated. Errors returned // by the callback will be propagated as well. type Viewer interface { - View(cid cid.Cid, callback func([]byte) error) error + View(ctx context.Context, cid cid.Cid, callback func([]byte) error) error } // GCLocker abstract functionality to lock a blockstore when performing @@ -78,17 +78,17 @@ type GCLocker interface { // GCLock locks the blockstore for garbage collection. No operations // that expect to finish with a pin should ocurr simultaneously. // Reading during GC is safe, and requires no lock. - GCLock() Unlocker + GCLock(context.Context) Unlocker // PinLock locks the blockstore for sequences of puts expected to finish // with a pin (before GC). Multiple put->pin sequences can write through // at the same time, but no GC should happen simulatenously. // Reading during Pinning is safe, and requires no lock. - PinLock() Unlocker + PinLock(context.Context) Unlocker // GcRequested returns true if GCLock has been called and is waiting to // take the lock - GCRequested() bool + GCRequested(context.Context) bool } // GCBlockstore is a blockstore that can safely run garbage-collection @@ -137,16 +137,16 @@ type blockstore struct { rehash *uatomic.Bool } -func (bs *blockstore) HashOnRead(enabled bool) { +func (bs *blockstore) HashOnRead(_ context.Context, enabled bool) { bs.rehash.Store(enabled) } -func (bs *blockstore) Get(k cid.Cid) (blocks.Block, error) { +func (bs *blockstore) Get(ctx context.Context, k cid.Cid) (blocks.Block, error) { if !k.Defined() { log.Error("undefined cid in blockstore") return nil, ErrNotFound } - bdata, err := bs.datastore.Get(dshelp.MultihashToDsKey(k.Hash())) + bdata, err := bs.datastore.Get(ctx, dshelp.MultihashToDsKey(k.Hash())) if err == ds.ErrNotFound { return nil, ErrNotFound } @@ -168,51 +168,51 @@ func (bs *blockstore) Get(k cid.Cid) (blocks.Block, error) { return blocks.NewBlockWithCid(bdata, k) } -func (bs *blockstore) Put(block blocks.Block) error { +func (bs *blockstore) Put(ctx context.Context, block blocks.Block) error { k := dshelp.MultihashToDsKey(block.Cid().Hash()) // Has is cheaper than Put, so see if we already have it - exists, err := bs.datastore.Has(k) + exists, err := bs.datastore.Has(ctx, k) if err == nil && exists { return nil // already stored. } - return bs.datastore.Put(k, block.RawData()) + return bs.datastore.Put(ctx, k, block.RawData()) } -func (bs *blockstore) PutMany(blocks []blocks.Block) error { - t, err := bs.datastore.Batch() +func (bs *blockstore) PutMany(ctx context.Context, blocks []blocks.Block) error { + t, err := bs.datastore.Batch(ctx) if err != nil { return err } for _, b := range blocks { k := dshelp.MultihashToDsKey(b.Cid().Hash()) - exists, err := bs.datastore.Has(k) + exists, err := bs.datastore.Has(ctx, k) if err == nil && exists { continue } - err = t.Put(k, b.RawData()) + err = t.Put(ctx, k, b.RawData()) if err != nil { return err } } - return t.Commit() + return t.Commit(ctx) } -func (bs *blockstore) Has(k cid.Cid) (bool, error) { - return bs.datastore.Has(dshelp.MultihashToDsKey(k.Hash())) +func (bs *blockstore) Has(ctx context.Context, k cid.Cid) (bool, error) { + return bs.datastore.Has(ctx, dshelp.MultihashToDsKey(k.Hash())) } -func (bs *blockstore) GetSize(k cid.Cid) (int, error) { - size, err := bs.datastore.GetSize(dshelp.MultihashToDsKey(k.Hash())) +func (bs *blockstore) GetSize(ctx context.Context, k cid.Cid) (int, error) { + size, err := bs.datastore.GetSize(ctx, dshelp.MultihashToDsKey(k.Hash())) if err == ds.ErrNotFound { return -1, ErrNotFound } return size, err } -func (bs *blockstore) DeleteBlock(k cid.Cid) error { - return bs.datastore.Delete(dshelp.MultihashToDsKey(k.Hash())) +func (bs *blockstore) DeleteBlock(ctx context.Context, k cid.Cid) error { + return bs.datastore.Delete(ctx, dshelp.MultihashToDsKey(k.Hash())) } // AllKeysChan runs a query for keys from the blockstore. @@ -223,7 +223,7 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // KeysOnly, because that would be _a lot_ of data. q := dsq.Query{KeysOnly: true} - res, err := bs.datastore.Query(q) + res, err := bs.datastore.Query(ctx, q) if err != nil { return nil, err } @@ -277,30 +277,30 @@ type gclocker struct { // Unlocker represents an object which can Unlock // something. type Unlocker interface { - Unlock() + Unlock(context.Context) } type unlocker struct { unlock func() } -func (u *unlocker) Unlock() { +func (u *unlocker) Unlock(_ context.Context) { u.unlock() u.unlock = nil // ensure its not called twice } -func (bs *gclocker) GCLock() Unlocker { +func (bs *gclocker) GCLock(_ context.Context) Unlocker { atomic.AddInt32(&bs.gcreq, 1) bs.lk.Lock() atomic.AddInt32(&bs.gcreq, -1) return &unlocker{bs.lk.Unlock} } -func (bs *gclocker) PinLock() Unlocker { +func (bs *gclocker) PinLock(_ context.Context) Unlocker { bs.lk.RLock() return &unlocker{bs.lk.RUnlock} } -func (bs *gclocker) GCRequested() bool { +func (bs *gclocker) GCRequested(_ context.Context) bool { return atomic.LoadInt32(&bs.gcreq) > 0 } diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 28f98e14a..423be2b27 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -17,7 +17,7 @@ import ( func TestGetWhenKeyNotPresent(t *testing.T) { bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) c := cid.NewCidV0(u.Hash([]byte("stuff"))) - bl, err := bs.Get(c) + bl, err := bs.Get(bg, c) if bl != nil { t.Error("nil block expected") @@ -29,7 +29,7 @@ func TestGetWhenKeyNotPresent(t *testing.T) { func TestGetWhenKeyIsNil(t *testing.T) { bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) - _, err := bs.Get(cid.Cid{}) + _, err := bs.Get(bg, cid.Cid{}) if err != ErrNotFound { t.Fail() } @@ -39,12 +39,12 @@ func TestPutThenGetBlock(t *testing.T) { bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) block := blocks.NewBlock([]byte("some data")) - err := bs.Put(block) + err := bs.Put(bg, block) if err != nil { t.Fatal(err) } - blockFromBlockstore, err := bs.Get(block.Cid()) + blockFromBlockstore, err := bs.Get(bg, block.Cid()) if err != nil { t.Fatal(err) } @@ -57,12 +57,12 @@ func TestCidv0v1(t *testing.T) { bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) block := blocks.NewBlock([]byte("some data")) - err := bs.Put(block) + err := bs.Put(bg, block) if err != nil { t.Fatal(err) } - blockFromBlockstore, err := bs.Get(cid.NewCidV1(cid.DagProtobuf, block.Cid().Hash())) + blockFromBlockstore, err := bs.Get(bg, cid.NewCidV1(cid.DagProtobuf, block.Cid().Hash())) if err != nil { t.Fatal(err) } @@ -77,12 +77,12 @@ func TestPutThenGetSizeBlock(t *testing.T) { missingBlock := blocks.NewBlock([]byte("missingBlock")) emptyBlock := blocks.NewBlock([]byte{}) - err := bs.Put(block) + err := bs.Put(bg, block) if err != nil { t.Fatal(err) } - blockSize, err := bs.GetSize(block.Cid()) + blockSize, err := bs.GetSize(bg, block.Cid()) if err != nil { t.Fatal(err) } @@ -90,16 +90,16 @@ func TestPutThenGetSizeBlock(t *testing.T) { t.Fail() } - err = bs.Put(emptyBlock) + err = bs.Put(bg, emptyBlock) if err != nil { t.Fatal(err) } - if blockSize, err := bs.GetSize(emptyBlock.Cid()); blockSize != 0 || err != nil { + if blockSize, err := bs.GetSize(bg, emptyBlock.Cid()); blockSize != 0 || err != nil { t.Fatal(err) } - if blockSize, err := bs.GetSize(missingBlock.Cid()); blockSize != -1 || err == nil { + if blockSize, err := bs.GetSize(bg, missingBlock.Cid()); blockSize != -1 || err == nil { t.Fatal("getsize returned invalid result") } } @@ -109,9 +109,9 @@ type countHasDS struct { hasCount int } -func (ds *countHasDS) Has(key ds.Key) (exists bool, err error) { +func (ds *countHasDS) Has(ctx context.Context, key ds.Key) (exists bool, err error) { ds.hasCount += 1 - return ds.Datastore.Has(key) + return ds.Datastore.Has(ctx, key) } func TestPutUsesHas(t *testing.T) { @@ -125,10 +125,10 @@ func TestPutUsesHas(t *testing.T) { } bs := NewBlockstore(ds_sync.MutexWrap(ds)) bl := blocks.NewBlock([]byte("some data")) - if err := bs.Put(bl); err != nil { + if err := bs.Put(bg, bl); err != nil { t.Fatal(err) } - if err := bs.Put(bl); err != nil { + if err := bs.Put(bg, bl); err != nil { t.Fatal(err) } if ds.hasCount != 2 { @@ -150,15 +150,15 @@ func TestHashOnRead(t *testing.T) { t.Fatal("debug is off, still got an error") } bl2 := blocks.NewBlock([]byte("some other data")) - bs.Put(blBad) - bs.Put(bl2) - bs.HashOnRead(true) + bs.Put(bg, blBad) + bs.Put(bg, bl2) + bs.HashOnRead(bg, true) - if _, err := bs.Get(bl.Cid()); err != ErrHashMismatch { + if _, err := bs.Get(bg, bl.Cid()); err != ErrHashMismatch { t.Fatalf("expected '%v' got '%v'\n", ErrHashMismatch, err) } - if b, err := bs.Get(bl2.Cid()); err != nil || b.String() != bl2.String() { + if b, err := bs.Get(bg, bl2.Cid()); err != nil || b.String() != bl2.String() { t.Fatal("got wrong blocks") } } @@ -172,7 +172,7 @@ func newBlockStoreWithKeys(t *testing.T, d ds.Datastore, N int) (Blockstore, []c keys := make([]cid.Cid, N) for i := 0; i < N; i++ { block := blocks.NewBlock([]byte(fmt.Sprintf("some data %d", i))) - err := bs.Put(block) + err := bs.Put(bg, block) if err != nil { t.Fatal(err) } @@ -293,38 +293,38 @@ type queryTestDS struct { func (c *queryTestDS) SetFunc(f func(dsq.Query) (dsq.Results, error)) { c.cb = f } -func (c *queryTestDS) Put(key ds.Key, value []byte) (err error) { - return c.ds.Put(key, value) +func (c *queryTestDS) Put(ctx context.Context, key ds.Key, value []byte) (err error) { + return c.ds.Put(ctx, key, value) } -func (c *queryTestDS) Get(key ds.Key) (value []byte, err error) { - return c.ds.Get(key) +func (c *queryTestDS) Get(ctx context.Context, key ds.Key) (value []byte, err error) { + return c.ds.Get(ctx, key) } -func (c *queryTestDS) Has(key ds.Key) (exists bool, err error) { - return c.ds.Has(key) +func (c *queryTestDS) Has(ctx context.Context, key ds.Key) (exists bool, err error) { + return c.ds.Has(ctx, key) } -func (c *queryTestDS) GetSize(key ds.Key) (size int, err error) { - return c.ds.GetSize(key) +func (c *queryTestDS) GetSize(ctx context.Context, key ds.Key) (size int, err error) { + return c.ds.GetSize(ctx, key) } -func (c *queryTestDS) Delete(key ds.Key) (err error) { - return c.ds.Delete(key) +func (c *queryTestDS) Delete(ctx context.Context, key ds.Key) (err error) { + return c.ds.Delete(ctx, key) } -func (c *queryTestDS) Query(q dsq.Query) (dsq.Results, error) { +func (c *queryTestDS) Query(ctx context.Context, q dsq.Query) (dsq.Results, error) { if c.cb != nil { return c.cb(q) } - return c.ds.Query(q) + return c.ds.Query(ctx, q) } -func (c *queryTestDS) Sync(key ds.Key) error { - return c.ds.Sync(key) +func (c *queryTestDS) Sync(ctx context.Context, key ds.Key) error { + return c.ds.Sync(ctx, key) } -func (c *queryTestDS) Batch() (ds.Batch, error) { +func (c *queryTestDS) Batch(_ context.Context) (ds.Batch, error) { return ds.NewBasicBatch(c), nil } func (c *queryTestDS) Close() error { diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 70fe5106b..37990191d 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -118,12 +118,12 @@ func (b *bloomcache) build(ctx context.Context) error { } } -func (b *bloomcache) DeleteBlock(k cid.Cid) error { +func (b *bloomcache) DeleteBlock(ctx context.Context, k cid.Cid) error { if has, ok := b.hasCached(k); ok && !has { return nil } - return b.blockstore.DeleteBlock(k) + return b.blockstore.DeleteBlock(ctx, k) } // if ok == false has is inconclusive @@ -146,25 +146,25 @@ func (b *bloomcache) hasCached(k cid.Cid) (has bool, ok bool) { return false, false } -func (b *bloomcache) Has(k cid.Cid) (bool, error) { +func (b *bloomcache) Has(ctx context.Context, k cid.Cid) (bool, error) { if has, ok := b.hasCached(k); ok { return has, nil } - return b.blockstore.Has(k) + return b.blockstore.Has(ctx, k) } -func (b *bloomcache) GetSize(k cid.Cid) (int, error) { +func (b *bloomcache) GetSize(ctx context.Context, k cid.Cid) (int, error) { if has, ok := b.hasCached(k); ok && !has { return -1, ErrNotFound } - return b.blockstore.GetSize(k) + return b.blockstore.GetSize(ctx, k) } -func (b *bloomcache) View(k cid.Cid, callback func([]byte) error) error { +func (b *bloomcache) View(ctx context.Context, k cid.Cid, callback func([]byte) error) error { if b.viewer == nil { - blk, err := b.Get(k) + blk, err := b.Get(ctx, k) if err != nil { return err } @@ -174,32 +174,32 @@ func (b *bloomcache) View(k cid.Cid, callback func([]byte) error) error { if has, ok := b.hasCached(k); ok && !has { return ErrNotFound } - return b.viewer.View(k, callback) + return b.viewer.View(ctx, k, callback) } -func (b *bloomcache) Get(k cid.Cid) (blocks.Block, error) { +func (b *bloomcache) Get(ctx context.Context, k cid.Cid) (blocks.Block, error) { if has, ok := b.hasCached(k); ok && !has { return nil, ErrNotFound } - return b.blockstore.Get(k) + return b.blockstore.Get(ctx, k) } -func (b *bloomcache) Put(bl blocks.Block) error { +func (b *bloomcache) Put(ctx context.Context, bl blocks.Block) error { // See comment in PutMany - err := b.blockstore.Put(bl) + err := b.blockstore.Put(ctx, bl) if err == nil { b.bloom.AddTS(bl.Cid().Hash()) } return err } -func (b *bloomcache) PutMany(bs []blocks.Block) error { +func (b *bloomcache) PutMany(ctx context.Context, bs []blocks.Block) error { // bloom cache gives only conclusive resulty if key is not contained // to reduce number of puts we need conclusive information if block is contained // this means that PutMany can't be improved with bloom cache so we just // just do a passthrough. - err := b.blockstore.PutMany(bs) + err := b.blockstore.PutMany(ctx, bs) if err != nil { return err } @@ -209,22 +209,22 @@ func (b *bloomcache) PutMany(bs []blocks.Block) error { return nil } -func (b *bloomcache) HashOnRead(enabled bool) { - b.blockstore.HashOnRead(enabled) +func (b *bloomcache) HashOnRead(ctx context.Context, enabled bool) { + b.blockstore.HashOnRead(ctx, enabled) } func (b *bloomcache) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return b.blockstore.AllKeysChan(ctx) } -func (b *bloomcache) GCLock() Unlocker { - return b.blockstore.(GCBlockstore).GCLock() +func (b *bloomcache) GCLock(ctx context.Context) Unlocker { + return b.blockstore.(GCBlockstore).GCLock(ctx) } -func (b *bloomcache) PinLock() Unlocker { - return b.blockstore.(GCBlockstore).PinLock() +func (b *bloomcache) PinLock(ctx context.Context) Unlocker { + return b.blockstore.(GCBlockstore).PinLock(ctx) } -func (b *bloomcache) GCRequested() bool { - return b.blockstore.(GCBlockstore).GCRequested() +func (b *bloomcache) GCRequested(ctx context.Context) bool { + return b.blockstore.(GCBlockstore).GCRequested(ctx) } diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 3b290a0c2..43f747d5e 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -13,6 +13,8 @@ import ( syncds "github.com/ipfs/go-datastore/sync" ) +var bg = context.Background() + func testBloomCached(ctx context.Context, bs Blockstore) (*bloomcache, error) { if ctx == nil { ctx = context.Background() @@ -45,12 +47,12 @@ func TestPutManyAddsToBloom(t *testing.T) { block2 := blocks.NewBlock([]byte("bar")) emptyBlock := blocks.NewBlock([]byte{}) - cachedbs.PutMany([]blocks.Block{block1, emptyBlock}) - has, err := cachedbs.Has(block1.Cid()) + cachedbs.PutMany(bg, []blocks.Block{block1, emptyBlock}) + has, err := cachedbs.Has(bg, block1.Cid()) if err != nil { t.Fatal(err) } - blockSize, err := cachedbs.GetSize(block1.Cid()) + blockSize, err := cachedbs.GetSize(bg, block1.Cid()) if err != nil { t.Fatal(err) } @@ -58,11 +60,11 @@ func TestPutManyAddsToBloom(t *testing.T) { t.Fatal("added block is reported missing") } - has, err = cachedbs.Has(block2.Cid()) + has, err = cachedbs.Has(bg, block2.Cid()) if err != nil { t.Fatal(err) } - blockSize, err = cachedbs.GetSize(block2.Cid()) + blockSize, err = cachedbs.GetSize(bg, block2.Cid()) if err != nil && err != ErrNotFound { t.Fatal(err) } @@ -70,11 +72,11 @@ func TestPutManyAddsToBloom(t *testing.T) { t.Fatal("not added block is reported to be in blockstore") } - has, err = cachedbs.Has(emptyBlock.Cid()) + has, err = cachedbs.Has(bg, emptyBlock.Cid()) if err != nil { t.Fatal(err) } - blockSize, err = cachedbs.GetSize(emptyBlock.Cid()) + blockSize, err = cachedbs.GetSize(bg, emptyBlock.Cid()) if err != nil { t.Fatal(err) } @@ -95,7 +97,7 @@ func TestHasIsBloomCached(t *testing.T) { bs := NewBlockstore(syncds.MutexWrap(cd)) for i := 0; i < 1000; i++ { - bs.Put(blocks.NewBlock([]byte(fmt.Sprintf("data: %d", i)))) + bs.Put(bg, blocks.NewBlock([]byte(fmt.Sprintf("data: %d", i)))) } ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() @@ -115,7 +117,7 @@ func TestHasIsBloomCached(t *testing.T) { }) for i := 0; i < 1000; i++ { - cachedbs.Has(blocks.NewBlock([]byte(fmt.Sprintf("data: %d", i+2000))).Cid()) + cachedbs.Has(bg, blocks.NewBlock([]byte(fmt.Sprintf("data: %d", i+2000))).Cid()) } if float64(cacheFails)/float64(1000) > float64(0.05) { @@ -125,20 +127,20 @@ func TestHasIsBloomCached(t *testing.T) { cacheFails = 0 block := blocks.NewBlock([]byte("newBlock")) - cachedbs.PutMany([]blocks.Block{block}) + cachedbs.PutMany(bg, []blocks.Block{block}) if cacheFails != 2 { t.Fatalf("expected two datastore hits: %d", cacheFails) } - cachedbs.Put(block) + cachedbs.Put(bg, block) if cacheFails != 3 { t.Fatalf("expected datastore hit: %d", cacheFails) } - if has, err := cachedbs.Has(block.Cid()); !has || err != nil { + if has, err := cachedbs.Has(bg, block.Cid()); !has || err != nil { t.Fatal("has gave wrong response") } - bl, err := cachedbs.Get(block.Cid()) + bl, err := cachedbs.Get(bg, block.Cid()) if bl.String() != block.String() { t.Fatal("block data doesn't match") } @@ -168,45 +170,45 @@ func (c *callbackDatastore) CallF() { c.f() } -func (c *callbackDatastore) Put(key ds.Key, value []byte) (err error) { +func (c *callbackDatastore) Put(ctx context.Context, key ds.Key, value []byte) (err error) { c.CallF() - return c.ds.Put(key, value) + return c.ds.Put(ctx, key, value) } -func (c *callbackDatastore) Get(key ds.Key) (value []byte, err error) { +func (c *callbackDatastore) Get(ctx context.Context, key ds.Key) (value []byte, err error) { c.CallF() - return c.ds.Get(key) + return c.ds.Get(ctx, key) } -func (c *callbackDatastore) Has(key ds.Key) (exists bool, err error) { +func (c *callbackDatastore) Has(ctx context.Context, key ds.Key) (exists bool, err error) { c.CallF() - return c.ds.Has(key) + return c.ds.Has(ctx, key) } -func (c *callbackDatastore) GetSize(key ds.Key) (size int, err error) { +func (c *callbackDatastore) GetSize(ctx context.Context, key ds.Key) (size int, err error) { c.CallF() - return c.ds.GetSize(key) + return c.ds.GetSize(ctx, key) } func (c *callbackDatastore) Close() error { return nil } -func (c *callbackDatastore) Delete(key ds.Key) (err error) { +func (c *callbackDatastore) Delete(ctx context.Context, key ds.Key) (err error) { c.CallF() - return c.ds.Delete(key) + return c.ds.Delete(ctx, key) } -func (c *callbackDatastore) Query(q dsq.Query) (dsq.Results, error) { +func (c *callbackDatastore) Query(ctx context.Context, q dsq.Query) (dsq.Results, error) { c.CallF() - return c.ds.Query(q) + return c.ds.Query(ctx, q) } -func (c *callbackDatastore) Sync(key ds.Key) error { +func (c *callbackDatastore) Sync(ctx context.Context, key ds.Key) error { c.CallF() - return c.ds.Sync(key) + return c.ds.Sync(ctx, key) } -func (c *callbackDatastore) Batch() (ds.Batch, error) { +func (c *callbackDatastore) Batch(_ context.Context) (ds.Batch, error) { return ds.NewBasicBatch(c), nil } diff --git a/blockstore/idstore.go b/blockstore/idstore.go index b1a85b6b9..497d5c505 100644 --- a/blockstore/idstore.go +++ b/blockstore/idstore.go @@ -40,25 +40,25 @@ func extractContents(k cid.Cid) (bool, []byte) { return true, dmh.Digest } -func (b *idstore) DeleteBlock(k cid.Cid) error { +func (b *idstore) DeleteBlock(ctx context.Context, k cid.Cid) error { isId, _ := extractContents(k) if isId { return nil } - return b.bs.DeleteBlock(k) + return b.bs.DeleteBlock(ctx, k) } -func (b *idstore) Has(k cid.Cid) (bool, error) { +func (b *idstore) Has(ctx context.Context, k cid.Cid) (bool, error) { isId, _ := extractContents(k) if isId { return true, nil } - return b.bs.Has(k) + return b.bs.Has(ctx, k) } -func (b *idstore) View(k cid.Cid, callback func([]byte) error) error { +func (b *idstore) View(ctx context.Context, k cid.Cid, callback func([]byte) error) error { if b.viewer == nil { - blk, err := b.Get(k) + blk, err := b.Get(ctx, k) if err != nil { return err } @@ -68,34 +68,34 @@ func (b *idstore) View(k cid.Cid, callback func([]byte) error) error { if isId { return callback(bdata) } - return b.viewer.View(k, callback) + return b.viewer.View(ctx, k, callback) } -func (b *idstore) GetSize(k cid.Cid) (int, error) { +func (b *idstore) GetSize(ctx context.Context, k cid.Cid) (int, error) { isId, bdata := extractContents(k) if isId { return len(bdata), nil } - return b.bs.GetSize(k) + return b.bs.GetSize(ctx, k) } -func (b *idstore) Get(k cid.Cid) (blocks.Block, error) { +func (b *idstore) Get(ctx context.Context, k cid.Cid) (blocks.Block, error) { isId, bdata := extractContents(k) if isId { return blocks.NewBlockWithCid(bdata, k) } - return b.bs.Get(k) + return b.bs.Get(ctx, k) } -func (b *idstore) Put(bl blocks.Block) error { +func (b *idstore) Put(ctx context.Context, bl blocks.Block) error { isId, _ := extractContents(bl.Cid()) if isId { return nil } - return b.bs.Put(bl) + return b.bs.Put(ctx, bl) } -func (b *idstore) PutMany(bs []blocks.Block) error { +func (b *idstore) PutMany(ctx context.Context, bs []blocks.Block) error { toPut := make([]blocks.Block, 0, len(bs)) for _, bl := range bs { isId, _ := extractContents(bl.Cid()) @@ -104,11 +104,11 @@ func (b *idstore) PutMany(bs []blocks.Block) error { } toPut = append(toPut, bl) } - return b.bs.PutMany(toPut) + return b.bs.PutMany(ctx, toPut) } -func (b *idstore) HashOnRead(enabled bool) { - b.bs.HashOnRead(enabled) +func (b *idstore) HashOnRead(ctx context.Context, enabled bool) { + b.bs.HashOnRead(ctx, enabled) } func (b *idstore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { diff --git a/blockstore/idstore_test.go b/blockstore/idstore_test.go index 65b902ef1..5f96bc903 100644 --- a/blockstore/idstore_test.go +++ b/blockstore/idstore_test.go @@ -26,12 +26,12 @@ func TestIdStore(t *testing.T) { ids, cb := createTestStores() - have, _ := ids.Has(idhash1) + have, _ := ids.Has(bg, idhash1) if !have { t.Fatal("Has() failed on idhash") } - _, err := ids.Get(idhash1) + _, err := ids.Get(bg, idhash1) if err != nil { t.Fatalf("Get() failed on idhash: %v", err) } @@ -42,70 +42,70 @@ func TestIdStore(t *testing.T) { } cb.f = failIfPassThough - err = ids.Put(idblock1) + err = ids.Put(bg, idblock1) if err != nil { t.Fatal(err) } cb.f = noop - err = ids.Put(block1) + err = ids.Put(bg, block1) if err != nil { t.Fatalf("Put() failed on normal block: %v", err) } - have, _ = ids.Has(hash1) + have, _ = ids.Has(bg, hash1) if !have { t.Fatal("normal block not added to datastore") } - blockSize, _ := ids.GetSize(hash1) + blockSize, _ := ids.GetSize(bg, hash1) if blockSize == -1 { t.Fatal("normal block not added to datastore") } - _, err = ids.Get(hash1) + _, err = ids.Get(bg, hash1) if err != nil { t.Fatal(err) } - err = ids.Put(emptyBlock) + err = ids.Put(bg, emptyBlock) if err != nil { t.Fatalf("Put() failed on normal block: %v", err) } - have, _ = ids.Has(emptyHash) + have, _ = ids.Has(bg, emptyHash) if !have { t.Fatal("normal block not added to datastore") } - blockSize, _ = ids.GetSize(emptyHash) + blockSize, _ = ids.GetSize(bg, emptyHash) if blockSize != 0 { t.Fatal("normal block not added to datastore") } cb.f = failIfPassThough - err = ids.DeleteBlock(idhash1) + err = ids.DeleteBlock(bg, idhash1) if err != nil { t.Fatal(err) } cb.f = noop - err = ids.DeleteBlock(hash1) + err = ids.DeleteBlock(bg, hash1) if err != nil { t.Fatal(err) } - have, _ = ids.Has(hash1) + have, _ = ids.Has(bg, hash1) if have { t.Fatal("normal block not deleted from datastore") } - blockSize, _ = ids.GetSize(hash1) + blockSize, _ = ids.GetSize(bg, hash1) if blockSize > -1 { t.Fatal("normal block not deleted from datastore") } - err = ids.DeleteBlock(emptyHash) + err = ids.DeleteBlock(bg, emptyHash) if err != nil { t.Fatal(err) } @@ -116,7 +116,7 @@ func TestIdStore(t *testing.T) { block2, _ := blk.NewBlockWithCid([]byte("hash2"), hash2) cb.f = failIfPassThough - err = ids.PutMany([]blk.Block{idblock1, idblock2}) + err = ids.PutMany(bg, []blk.Block{idblock1, idblock2}) if err != nil { t.Fatal(err) } @@ -126,7 +126,7 @@ func TestIdStore(t *testing.T) { opCount++ } - err = ids.PutMany([]blk.Block{block1, block2}) + err = ids.PutMany(bg, []blk.Block{block1, block2}) if err != nil { t.Fatal(err) } @@ -136,7 +136,7 @@ func TestIdStore(t *testing.T) { } opCount = 0 - err = ids.PutMany([]blk.Block{idblock1, block1}) + err = ids.PutMany(bg, []blk.Block{idblock1, block1}) if err != nil { t.Fatal(err) } From bfa7e921bdfde67e88f8da8af3dbe3dafc6672ff Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Thu, 21 Oct 2021 13:40:28 -0400 Subject: [PATCH 3609/3817] feat: plumb through datastore context changes This commit was moved from ipfs/go-unixfs@abdf700b6b5b76a2f684eb730cd83353603206d4 --- unixfs/io/directory_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/unixfs/io/directory_test.go b/unixfs/io/directory_test.go index f5fa2e564..ca67f5d25 100644 --- a/unixfs/io/directory_test.go +++ b/unixfs/io/directory_test.go @@ -606,15 +606,15 @@ func (d *countGetsDS) maybeSleep(c cid.Cid) { } } -func (d *countGetsDS) Has(c cid.Cid) (bool, error) { +func (d *countGetsDS) Has(ctx context.Context, c cid.Cid) (bool, error) { if d.started { panic("implement me") } - return d.Blockstore.Has(c) + return d.Blockstore.Has(ctx, c) } -func (d *countGetsDS) Get(c cid.Cid) (blocks.Block, error) { - blk, err := d.Blockstore.Get(c) +func (d *countGetsDS) Get(ctx context.Context, c cid.Cid) (blocks.Block, error) { + blk, err := d.Blockstore.Get(ctx, c) if err != nil { return nil, err } @@ -623,11 +623,11 @@ func (d *countGetsDS) Get(c cid.Cid) (blocks.Block, error) { return blk, nil } -func (d *countGetsDS) GetSize(c cid.Cid) (int, error) { +func (d *countGetsDS) GetSize(ctx context.Context, c cid.Cid) (int, error) { if d.started { panic("implement me") } - return d.Blockstore.GetSize(c) + return d.Blockstore.GetSize(ctx, c) } func (d *countGetsDS) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { From 91853fbb7ab45174e198b1eaf7da9113f7409efa Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Mon, 15 Nov 2021 21:07:50 -0300 Subject: [PATCH 3610/3817] support threshold based automatic sharding and unsharding of directories (#88) * feat: update go-unixfs and use built in automatic sharding and unsharding * chore: update deps Co-authored-by: Adin Schmahmann Co-authored-by: Gus Eggert This commit was moved from ipfs/go-mfs@e61420fa2f77775867cedd654709b6671270f58a --- mfs/dir.go | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/mfs/dir.go b/mfs/dir.go index 61f85d064..52b1b046d 100644 --- a/mfs/dir.go +++ b/mfs/dir.go @@ -128,7 +128,7 @@ func (d *Directory) localUpdate(c child) (*dag.ProtoNode, error) { // Update child entry in the underlying UnixFS directory. func (d *Directory) updateChild(c child) error { - err := d.addUnixFSChild(c) + err := d.unixfsDir.AddChild(d.ctx, c.Name, c.Node) if err != nil { return err } @@ -313,7 +313,7 @@ func (d *Directory) Mkdir(name string) (*Directory, error) { return nil, err } - err = d.addUnixFSChild(child{name, ndir}) + err = d.unixfsDir.AddChild(d.ctx, name, ndir) if err != nil { return nil, err } @@ -360,7 +360,7 @@ func (d *Directory) AddChild(name string, nd ipld.Node) error { return err } - err = d.addUnixFSChild(child{name, nd}) + err = d.unixfsDir.AddChild(d.ctx, name, nd) if err != nil { return err } @@ -369,29 +369,6 @@ func (d *Directory) AddChild(name string, nd ipld.Node) error { return nil } -// addUnixFSChild adds a child to the inner UnixFS directory -// and transitions to a HAMT implementation if needed. -func (d *Directory) addUnixFSChild(c child) error { - if uio.UseHAMTSharding { - // If the directory HAMT implementation is being used and this - // directory is actually a basic implementation switch it to HAMT. - if basicDir, ok := d.unixfsDir.(*uio.BasicDirectory); ok { - hamtDir, err := basicDir.SwitchToSharding(d.ctx) - if err != nil { - return err - } - d.unixfsDir = hamtDir - } - } - - err := d.unixfsDir.AddChild(d.ctx, c.Name, c.Node) - if err != nil { - return err - } - - return nil -} - func (d *Directory) sync() error { for name, entry := range d.entriesCache { nd, err := entry.GetNode() From 8c04627a0f96ff55a87e2f0db3ce6796c1df0552 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 17 Nov 2021 13:44:34 +0100 Subject: [PATCH 3611/3817] chore: add keys to composite literal fields This commit was moved from ipfs/go-ipfs-provider@bb73e1b7d8674f625b756aa243370e2aa82e02af --- provider/simple/reprovide.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index b62369a07..38d6f86d7 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -236,7 +236,7 @@ func pinSet(ctx context.Context, pinning Pinner, fetchConfig fetcher.Factory, on for _, key := range rkeys { set.Visitor(ctx)(key) if !onlyRoots { - err := fetcherhelpers.BlockAll(ctx, session, cidlink.Link{key}, func(res fetcher.FetchResult) error { + err := fetcherhelpers.BlockAll(ctx, session, cidlink.Link{Cid: key}, func(res fetcher.FetchResult) error { clink, ok := res.LastBlockLink.(cidlink.Link) if ok { set.Visitor(ctx)(clink.Cid) From f47878a2b8043d1380ff5f0c0903c273ce72b5cf Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 17 Nov 2021 13:46:54 +0100 Subject: [PATCH 3612/3817] chore: use Warnf instead of Warningf This commit was moved from ipfs/go-ipfs-provider@56883e0765868ca990331aee5e690850d243f7cc --- provider/queue/queue.go | 2 +- provider/simple/provider.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/provider/queue/queue.go b/provider/queue/queue.go index 18ed6a798..753d66c63 100644 --- a/provider/queue/queue.go +++ b/provider/queue/queue.go @@ -96,7 +96,7 @@ func (q *Queue) work() { k = datastore.NewKey(head.Key) c, err = cid.Parse(head.Value) if err != nil { - log.Warningf("error parsing queue entry cid with key (%s), removing it from queue: %s", head.Key, err) + log.Warnf("error parsing queue entry cid with key (%s), removing it from queue: %s", head.Key, err) err = q.ds.Delete(q.ctx, k) if err != nil { log.Errorf("error deleting queue entry with key (%s), due to error (%s), stopping provider", head.Key, err) diff --git a/provider/simple/provider.go b/provider/simple/provider.go index 6c50ef925..d43cd6ac8 100644 --- a/provider/simple/provider.go +++ b/provider/simple/provider.go @@ -110,7 +110,7 @@ func (p *Provider) doProvide(c cid.Cid) { logP.Info("announce - start - ", c) if err := p.contentRouting.Provide(ctx, c, true); err != nil { - logP.Warningf("Unable to provide entry: %s, %s", c, err) + logP.Warnf("Unable to provide entry: %s, %s", c, err) } logP.Info("announce - end - ", c) } From dcf224435eb1d34a5784349a608b4e9665291613 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 16 Nov 2021 21:01:54 +0000 Subject: [PATCH 3613/3817] Seek to start before index generation in `ReadOnly` blockstore The blockstore reads the version of the given CAR payload to determine whether to generate an index for the given payload or not. When the payload represents a CARv1 and no index is specified, the backing reader is passed on for index generation. But the version is already read from the stream. Seek to the beginning of the backing reader before generating the index so that the index generation mechanism receives the complete CARv1. Fixes #265 This commit was moved from ipld/go-car@fa995b9d72f961b725eb8abd4dbe3de6765f8b27 --- ipld/car/v2/blockstore/readonly.go | 4 ++++ ipld/car/v2/blockstore/readonly_test.go | 28 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 690e233b7..e25f51251 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -150,6 +150,10 @@ func generateIndex(at io.ReaderAt, opts ...carv2.Option) (index.Index, error) { switch r := at.(type) { case io.ReadSeeker: rs = r + // The version may have been read from the given io.ReaderAt; therefore move back to the begining. + if _, err := rs.Seek(0, io.SeekStart); err != nil { + return nil, err + } default: rs = internalio.NewOffsetReadSeeker(r, 0) } diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index a242abd8c..4922acd1b 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -1,8 +1,10 @@ package blockstore import ( + "bytes" "context" "io" + "io/ioutil" "os" "testing" "time" @@ -269,3 +271,29 @@ func TestReadOnlyErrorAfterClose(t *testing.T) { // TODO: test that closing blocks if an AllKeysChan operation is // in progress. } + +func TestNewReadOnly_CarV1WithoutIndexWorksAsExpected(t *testing.T) { + carV1Bytes, err := ioutil.ReadFile("../testdata/sample-v1.car") + require.NoError(t, err) + + reader := bytes.NewReader(carV1Bytes) + v1r, err := carv1.NewCarReader(reader) + require.NoError(t, err) + require.Equal(t, uint64(1), v1r.Header.Version) + + // Pick the first block in CARv1 as candidate to check `Get` works. + wantBlock, err := v1r.Next() + require.NoError(t, err) + + // Seek back to the begining of the CARv1 payload. + _, err = reader.Seek(0, io.SeekStart) + require.NoError(t, err) + + subject, err := NewReadOnly(reader, nil, UseWholeCIDs(true)) + require.NoError(t, err) + + // Require that the block is found via ReadOnly API and contetns are as expected. + gotBlock, err := subject.Get(wantBlock.Cid()) + require.NoError(t, err) + require.Equal(t, wantBlock, gotBlock) +} From c57ae7942c0fc9e2074548372ff3d6a45f84c582 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Fri, 19 Nov 2021 15:35:11 -0500 Subject: [PATCH 3614/3817] fix: remove context from HashOnRead This is so that v0 and v1 have identical interfaces, which is required to avoid massive pain for consumers like estuary. Since we have already plumbed v0 all the way through, it's easiest to just remove the probably-unnecessary context from HashOnRead. This commit was moved from ipfs/go-ipfs-blockstore@fbe708e941904f4048d0ae4261f34b74a347a3dd --- blockstore/arc_cache.go | 4 ++-- blockstore/blockstore.go | 4 ++-- blockstore/blockstore_test.go | 2 +- blockstore/bloom_cache.go | 4 ++-- blockstore/idstore.go | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 09aa44138..bb78c2a2a 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -227,8 +227,8 @@ func (b *arccache) PutMany(ctx context.Context, bs []blocks.Block) error { return nil } -func (b *arccache) HashOnRead(ctx context.Context, enabled bool) { - b.blockstore.HashOnRead(ctx, enabled) +func (b *arccache) HashOnRead(enabled bool) { + b.blockstore.HashOnRead(enabled) } func (b *arccache) cacheHave(c cid.Cid, have bool) { diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index dfac6ce42..9572f76c0 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -54,7 +54,7 @@ type Blockstore interface { // HashOnRead specifies if every read block should be // rehashed to make sure it matches its CID. - HashOnRead(ctx context.Context, enabled bool) + HashOnRead(enabled bool) } // Viewer can be implemented by blockstores that offer zero-copy access to @@ -137,7 +137,7 @@ type blockstore struct { rehash *uatomic.Bool } -func (bs *blockstore) HashOnRead(_ context.Context, enabled bool) { +func (bs *blockstore) HashOnRead(enabled bool) { bs.rehash.Store(enabled) } diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 423be2b27..1ee3341c3 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -152,7 +152,7 @@ func TestHashOnRead(t *testing.T) { bl2 := blocks.NewBlock([]byte("some other data")) bs.Put(bg, blBad) bs.Put(bg, bl2) - bs.HashOnRead(bg, true) + bs.HashOnRead(true) if _, err := bs.Get(bg, bl.Cid()); err != ErrHashMismatch { t.Fatalf("expected '%v' got '%v'\n", ErrHashMismatch, err) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 37990191d..e3332ef38 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -209,8 +209,8 @@ func (b *bloomcache) PutMany(ctx context.Context, bs []blocks.Block) error { return nil } -func (b *bloomcache) HashOnRead(ctx context.Context, enabled bool) { - b.blockstore.HashOnRead(ctx, enabled) +func (b *bloomcache) HashOnRead(enabled bool) { + b.blockstore.HashOnRead(enabled) } func (b *bloomcache) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { diff --git a/blockstore/idstore.go b/blockstore/idstore.go index 497d5c505..25a6284c8 100644 --- a/blockstore/idstore.go +++ b/blockstore/idstore.go @@ -107,8 +107,8 @@ func (b *idstore) PutMany(ctx context.Context, bs []blocks.Block) error { return b.bs.PutMany(ctx, toPut) } -func (b *idstore) HashOnRead(ctx context.Context, enabled bool) { - b.bs.HashOnRead(ctx, enabled) +func (b *idstore) HashOnRead(enabled bool) { + b.bs.HashOnRead(enabled) } func (b *idstore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { From e81cb4c04decababe4b54ba7ecb711457b311315 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Fri, 19 Nov 2021 14:21:53 -0500 Subject: [PATCH 3615/3817] feat: plumb through context changes This commit was moved from ipfs/go-filestore@9d7d5b4ea6c3ace567f121731c4abcca220a45d2 --- filestore/filestore.go | 40 ++++++++++++++--------------- filestore/filestore_test.go | 12 +++++---- filestore/fsrefstore.go | 50 ++++++++++++++++++------------------- filestore/util.go | 43 +++++++++++++++---------------- 4 files changed, 74 insertions(+), 71 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index a9c36c5d3..6382a6db4 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -115,13 +115,13 @@ func (f *Filestore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // blockstore. As expected, in the case of FileManager blocks, only the // reference is deleted, not its contents. It may return // ErrNotFound when the block is not stored. -func (f *Filestore) DeleteBlock(c cid.Cid) error { - err1 := f.bs.DeleteBlock(c) +func (f *Filestore) DeleteBlock(ctx context.Context, c cid.Cid) error { + err1 := f.bs.DeleteBlock(ctx, c) if err1 != nil && err1 != blockstore.ErrNotFound { return err1 } - err2 := f.fm.DeleteBlock(c) + err2 := f.fm.DeleteBlock(ctx, c) // if we successfully removed something from the blockstore, but the // filestore didnt have it, return success @@ -140,13 +140,13 @@ func (f *Filestore) DeleteBlock(c cid.Cid) error { // Get retrieves the block with the given Cid. It may return // ErrNotFound when the block is not stored. -func (f *Filestore) Get(c cid.Cid) (blocks.Block, error) { - blk, err := f.bs.Get(c) +func (f *Filestore) Get(ctx context.Context, c cid.Cid) (blocks.Block, error) { + blk, err := f.bs.Get(ctx, c) switch err { case nil: return blk, nil case blockstore.ErrNotFound: - return f.fm.Get(c) + return f.fm.Get(ctx, c) default: return nil, err } @@ -154,13 +154,13 @@ func (f *Filestore) Get(c cid.Cid) (blocks.Block, error) { // GetSize returns the size of the requested block. It may return ErrNotFound // when the block is not stored. -func (f *Filestore) GetSize(c cid.Cid) (int, error) { - size, err := f.bs.GetSize(c) +func (f *Filestore) GetSize(ctx context.Context, c cid.Cid) (int, error) { + size, err := f.bs.GetSize(ctx, c) switch err { case nil: return size, nil case blockstore.ErrNotFound: - return f.fm.GetSize(c) + return f.fm.GetSize(ctx, c) default: return -1, err } @@ -168,8 +168,8 @@ func (f *Filestore) GetSize(c cid.Cid) (int, error) { // Has returns true if the block with the given Cid is // stored in the Filestore. -func (f *Filestore) Has(c cid.Cid) (bool, error) { - has, err := f.bs.Has(c) +func (f *Filestore) Has(ctx context.Context, c cid.Cid) (bool, error) { + has, err := f.bs.Has(ctx, c) if err != nil { return false, err } @@ -178,15 +178,15 @@ func (f *Filestore) Has(c cid.Cid) (bool, error) { return true, nil } - return f.fm.Has(c) + return f.fm.Has(ctx, c) } // Put stores a block in the Filestore. For blocks of // underlying type FilestoreNode, the operation is // delegated to the FileManager, while the rest of blocks // are handled by the regular blockstore. -func (f *Filestore) Put(b blocks.Block) error { - has, err := f.Has(b.Cid()) +func (f *Filestore) Put(ctx context.Context, b blocks.Block) error { + has, err := f.Has(ctx, b.Cid()) if err != nil { return err } @@ -197,20 +197,20 @@ func (f *Filestore) Put(b blocks.Block) error { switch b := b.(type) { case *posinfo.FilestoreNode: - return f.fm.Put(b) + return f.fm.Put(ctx, b) default: - return f.bs.Put(b) + return f.bs.Put(ctx, b) } } // PutMany is like Put(), but takes a slice of blocks, allowing // the underlying blockstore to perform batch transactions. -func (f *Filestore) PutMany(bs []blocks.Block) error { +func (f *Filestore) PutMany(ctx context.Context, bs []blocks.Block) error { var normals []blocks.Block var fstores []*posinfo.FilestoreNode for _, b := range bs { - has, err := f.Has(b.Cid()) + has, err := f.Has(ctx, b.Cid()) if err != nil { return err } @@ -228,14 +228,14 @@ func (f *Filestore) PutMany(bs []blocks.Block) error { } if len(normals) > 0 { - err := f.bs.PutMany(normals) + err := f.bs.PutMany(ctx, normals) if err != nil { return err } } if len(fstores) > 0 { - err := f.fm.PutMany(fstores) + err := f.fm.PutMany(ctx, fstores) if err != nil { return err } diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 783dc86f9..e3b822cf4 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -15,6 +15,8 @@ import ( posinfo "github.com/ipfs/go-ipfs-posinfo" ) +var bg = context.Background() + func newTestFilestore(t *testing.T) (string, *Filestore) { mds := ds.NewMapDatastore() @@ -65,7 +67,7 @@ func TestBasicFilestore(t *testing.T) { Node: dag.NewRawNode(buf[i*10 : (i+1)*10]), } - err := fs.Put(n) + err := fs.Put(bg, n) if err != nil { t.Fatal(err) } @@ -73,7 +75,7 @@ func TestBasicFilestore(t *testing.T) { } for i, c := range cids { - blk, err := fs.Get(c) + blk, err := fs.Get(bg, c) if err != nil { t.Fatal(err) } @@ -122,7 +124,7 @@ func randomFileAdd(t *testing.T, fs *Filestore, dir string, size int) (string, [ }, Node: dag.NewRawNode(buf[i*10 : (i+1)*10]), } - err := fs.Put(n) + err := fs.Put(bg, n) if err != nil { t.Fatal(err) } @@ -137,7 +139,7 @@ func TestDeletes(t *testing.T) { _, cids := randomFileAdd(t, fs, dir, 100) todelete := cids[:4] for _, c := range todelete { - err := fs.DeleteBlock(c) + err := fs.DeleteBlock(bg, c) if err != nil { t.Fatal(err) } @@ -145,7 +147,7 @@ func TestDeletes(t *testing.T) { deleted := make(map[string]bool) for _, c := range todelete { - _, err := fs.Get(c) + _, err := fs.Get(bg, c) if err != blockstore.ErrNotFound { t.Fatal("expected blockstore not found error") } diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index a29c2264e..9eb2b4316 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -66,7 +66,7 @@ func NewFileManager(ds ds.Batching, root string) *FileManager { func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { q := dsq.Query{KeysOnly: true} - res, err := f.ds.Query(q) + res, err := f.ds.Query(ctx, q) if err != nil { return nil, err } @@ -100,8 +100,8 @@ func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // DeleteBlock deletes the reference-block from the underlying // datastore. It does not touch the referenced data. -func (f *FileManager) DeleteBlock(c cid.Cid) error { - err := f.ds.Delete(dshelp.MultihashToDsKey(c.Hash())) +func (f *FileManager) DeleteBlock(ctx context.Context, c cid.Cid) error { + err := f.ds.Delete(ctx, dshelp.MultihashToDsKey(c.Hash())) if err == ds.ErrNotFound { return blockstore.ErrNotFound } @@ -112,12 +112,12 @@ func (f *FileManager) DeleteBlock(c cid.Cid) error { // is done in two steps: the first step retrieves the reference // block from the datastore. The second step uses the stored // path and offsets to read the raw block data directly from disk. -func (f *FileManager) Get(c cid.Cid) (blocks.Block, error) { - dobj, err := f.getDataObj(c.Hash()) +func (f *FileManager) Get(ctx context.Context, c cid.Cid) (blocks.Block, error) { + dobj, err := f.getDataObj(ctx, c.Hash()) if err != nil { return nil, err } - out, err := f.readDataObj(c.Hash(), dobj) + out, err := f.readDataObj(ctx, c.Hash(), dobj) if err != nil { return nil, err } @@ -129,23 +129,23 @@ func (f *FileManager) Get(c cid.Cid) (blocks.Block, error) { // // This method may successfully return the size even if returning the block // would fail because the associated file is no longer available. -func (f *FileManager) GetSize(c cid.Cid) (int, error) { - dobj, err := f.getDataObj(c.Hash()) +func (f *FileManager) GetSize(ctx context.Context, c cid.Cid) (int, error) { + dobj, err := f.getDataObj(ctx, c.Hash()) if err != nil { return -1, err } return int(dobj.GetSize_()), nil } -func (f *FileManager) readDataObj(m mh.Multihash, d *pb.DataObj) ([]byte, error) { +func (f *FileManager) readDataObj(ctx context.Context, m mh.Multihash, d *pb.DataObj) ([]byte, error) { if IsURL(d.GetFilePath()) { - return f.readURLDataObj(m, d) + return f.readURLDataObj(ctx, m, d) } return f.readFileDataObj(m, d) } -func (f *FileManager) getDataObj(m mh.Multihash) (*pb.DataObj, error) { - o, err := f.ds.Get(dshelp.MultihashToDsKey(m)) +func (f *FileManager) getDataObj(ctx context.Context, m mh.Multihash) (*pb.DataObj, error) { + o, err := f.ds.Get(ctx, dshelp.MultihashToDsKey(m)) switch err { case ds.ErrNotFound: return nil, blockstore.ErrNotFound @@ -213,12 +213,12 @@ func (f *FileManager) readFileDataObj(m mh.Multihash, d *pb.DataObj) ([]byte, er } // reads and verifies the block from URL -func (f *FileManager) readURLDataObj(m mh.Multihash, d *pb.DataObj) ([]byte, error) { +func (f *FileManager) readURLDataObj(ctx context.Context, m mh.Multihash, d *pb.DataObj) ([]byte, error) { if !f.AllowUrls { return nil, ErrUrlstoreNotEnabled } - req, err := http.NewRequest("GET", d.GetFilePath(), nil) + req, err := http.NewRequestWithContext(ctx, "GET", d.GetFilePath(), nil) if err != nil { return nil, err } @@ -261,24 +261,24 @@ func (f *FileManager) readURLDataObj(m mh.Multihash, d *pb.DataObj) ([]byte, err // Has returns if the FileManager is storing a block reference. It does not // validate the data, nor checks if the reference is valid. -func (f *FileManager) Has(c cid.Cid) (bool, error) { +func (f *FileManager) Has(ctx context.Context, c cid.Cid) (bool, error) { // NOTE: interesting thing to consider. Has doesnt validate the data. // So the data on disk could be invalid, and we could think we have it. dsk := dshelp.MultihashToDsKey(c.Hash()) - return f.ds.Has(dsk) + return f.ds.Has(ctx, dsk) } type putter interface { - Put(ds.Key, []byte) error + Put(context.Context, ds.Key, []byte) error } // Put adds a new reference block to the FileManager. It does not check // that the reference is valid. -func (f *FileManager) Put(b *posinfo.FilestoreNode) error { - return f.putTo(b, f.ds) +func (f *FileManager) Put(ctx context.Context, b *posinfo.FilestoreNode) error { + return f.putTo(ctx, b, f.ds) } -func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { +func (f *FileManager) putTo(ctx context.Context, b *posinfo.FilestoreNode, to putter) error { var dobj pb.DataObj if IsURL(b.PosInfo.FullPath) { @@ -310,24 +310,24 @@ func (f *FileManager) putTo(b *posinfo.FilestoreNode, to putter) error { return err } - return to.Put(dshelp.MultihashToDsKey(b.Cid().Hash()), data) + return to.Put(ctx, dshelp.MultihashToDsKey(b.Cid().Hash()), data) } // PutMany is like Put() but takes a slice of blocks instead, // allowing it to create a batch transaction. -func (f *FileManager) PutMany(bs []*posinfo.FilestoreNode) error { - batch, err := f.ds.Batch() +func (f *FileManager) PutMany(ctx context.Context, bs []*posinfo.FilestoreNode) error { + batch, err := f.ds.Batch(ctx) if err != nil { return err } for _, b := range bs { - if err := f.putTo(b, batch); err != nil { + if err := f.putTo(ctx, b, batch); err != nil { return err } } - return batch.Commit() + return batch.Commit(ctx) } // IsURL returns true if the string represents a valid URL that the diff --git a/filestore/util.go b/filestore/util.go index dc860f735..4bd1226d3 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -1,6 +1,7 @@ package filestore import ( + "context" "fmt" "sort" @@ -86,64 +87,64 @@ func (r *ListRes) FormatLong(enc func(cid.Cid) string) string { // of the given Filestore and returns a ListRes object with the information. // List does not verify that the reference is valid or whether the // raw data is accesible. See Verify(). -func List(fs *Filestore, key cid.Cid) *ListRes { - return list(fs, false, key.Hash()) +func List(ctx context.Context, fs *Filestore, key cid.Cid) *ListRes { + return list(ctx, fs, false, key.Hash()) } // ListAll returns a function as an iterator which, once invoked, returns // one by one each block in the Filestore's FileManager. // ListAll does not verify that the references are valid or whether // the raw data is accessible. See VerifyAll(). -func ListAll(fs *Filestore, fileOrder bool) (func() *ListRes, error) { +func ListAll(ctx context.Context, fs *Filestore, fileOrder bool) (func(context.Context) *ListRes, error) { if fileOrder { - return listAllFileOrder(fs, false) + return listAllFileOrder(ctx, fs, false) } - return listAll(fs, false) + return listAll(ctx, fs, false) } // Verify fetches the block with the given key from the Filemanager // of the given Filestore and returns a ListRes object with the information. // Verify makes sure that the reference is valid and the block data can be // read. -func Verify(fs *Filestore, key cid.Cid) *ListRes { - return list(fs, true, key.Hash()) +func Verify(ctx context.Context, fs *Filestore, key cid.Cid) *ListRes { + return list(ctx, fs, true, key.Hash()) } // VerifyAll returns a function as an iterator which, once invoked, // returns one by one each block in the Filestore's FileManager. // VerifyAll checks that the reference is valid and that the block data // can be read. -func VerifyAll(fs *Filestore, fileOrder bool) (func() *ListRes, error) { +func VerifyAll(ctx context.Context, fs *Filestore, fileOrder bool) (func(context.Context) *ListRes, error) { if fileOrder { - return listAllFileOrder(fs, true) + return listAllFileOrder(ctx, fs, true) } - return listAll(fs, true) + return listAll(ctx, fs, true) } -func list(fs *Filestore, verify bool, key mh.Multihash) *ListRes { - dobj, err := fs.fm.getDataObj(key) +func list(ctx context.Context, fs *Filestore, verify bool, key mh.Multihash) *ListRes { + dobj, err := fs.fm.getDataObj(ctx, key) if err != nil { return mkListRes(key, nil, err) } if verify { - _, err = fs.fm.readDataObj(key, dobj) + _, err = fs.fm.readDataObj(ctx, key, dobj) } return mkListRes(key, dobj, err) } -func listAll(fs *Filestore, verify bool) (func() *ListRes, error) { +func listAll(ctx context.Context, fs *Filestore, verify bool) (func(context.Context) *ListRes, error) { q := dsq.Query{} - qr, err := fs.fm.ds.Query(q) + qr, err := fs.fm.ds.Query(ctx, q) if err != nil { return nil, err } - return func() *ListRes { + return func(ctx context.Context) *ListRes { mhash, dobj, err := next(qr) if dobj == nil && err == nil { return nil } else if err == nil && verify { - _, err = fs.fm.readDataObj(mhash, dobj) + _, err = fs.fm.readDataObj(ctx, mhash, dobj) } return mkListRes(mhash, dobj, err) }, nil @@ -169,9 +170,9 @@ func next(qr dsq.Results) (mh.Multihash, *pb.DataObj, error) { return mhash, dobj, nil } -func listAllFileOrder(fs *Filestore, verify bool) (func() *ListRes, error) { +func listAllFileOrder(ctx context.Context, fs *Filestore, verify bool) (func(context.Context) *ListRes, error) { q := dsq.Query{} - qr, err := fs.fm.ds.Query(q) + qr, err := fs.fm.ds.Query(ctx, q) if err != nil { return nil, err } @@ -201,7 +202,7 @@ func listAllFileOrder(fs *Filestore, verify bool) (func() *ListRes, error) { sort.Sort(entries) i := 0 - return func() *ListRes { + return func(ctx context.Context) *ListRes { if i >= len(entries) { return nil } @@ -228,7 +229,7 @@ func listAllFileOrder(fs *Filestore, verify bool) (func() *ListRes, error) { // finally verify the dataobj if requested var err error if verify { - _, err = fs.fm.readDataObj(mhash, &dobj) + _, err = fs.fm.readDataObj(ctx, mhash, &dobj) } return mkListRes(mhash, &dobj, err) }, nil From c66b60c62c4654763488920374d89ea6af5cb8fe Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Mon, 22 Nov 2021 16:53:15 -0500 Subject: [PATCH 3616/3817] feat: plumb through datastore contexts This commit was moved from ipfs/go-namesys@aaf9aa85f1e2edd60aa514286588875004eb6e23 --- namesys/ipns_resolver_validation_test.go | 5 ++++- namesys/namesys_test.go | 10 ++++++++-- namesys/publisher.go | 8 ++++---- namesys/publisher_test.go | 6 +++--- namesys/republisher/repub.go | 6 +++--- namesys/republisher/repub_test.go | 7 +++---- 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index d896b9e0d..cc3b58f36 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -46,7 +46,10 @@ func testResolverValidation(t *testing.T, keyType int) { ctx := context.Background() rid := testutil.RandIdentityOrFatal(t) dstore := dssync.MutexWrap(ds.NewMapDatastore()) - peerstore := pstoremem.NewPeerstore() + peerstore, err := pstoremem.NewPeerstore() + if err != nil { + t.Fatal(err) + } vstore := newMockValueStore(rid, dstore, peerstore) resolver := NewIpnsResolver(vstore) diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index c3e553429..af115ac2b 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -94,7 +94,10 @@ func TestPublishWithCache0(t *testing.T) { if err != nil { t.Fatal(err) } - ps := pstoremem.NewPeerstore() + ps, err := pstoremem.NewPeerstore() + if err != nil { + t.Fatal(err) + } pid, err := peer.IDFromPrivateKey(priv) if err != nil { t.Fatal(err) @@ -131,7 +134,10 @@ func TestPublishWithTTL(t *testing.T) { if err != nil { t.Fatal(err) } - ps := pstoremem.NewPeerstore() + ps, err := pstoremem.NewPeerstore() + if err != nil { + t.Fatal(err) + } pid, err := peer.IDFromPrivateKey(priv) if err != nil { t.Fatal(err) diff --git a/namesys/publisher.go b/namesys/publisher.go index 307b3920c..3b69bce73 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -62,7 +62,7 @@ func IpnsDsKey(id peer.ID) ds.Key { // This method will not search the routing system for records published by other // nodes. func (p *IpnsPublisher) ListPublished(ctx context.Context) (map[peer.ID]*pb.IpnsEntry, error) { - query, err := p.ds.Query(dsquery.Query{ + query, err := p.ds.Query(ctx, dsquery.Query{ Prefix: ipnsPrefix, }) if err != nil { @@ -112,7 +112,7 @@ func (p *IpnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouti ctx, cancel := context.WithTimeout(ctx, time.Second*30) defer cancel() - value, err := p.ds.Get(IpnsDsKey(id)) + value, err := p.ds.Get(ctx, IpnsDsKey(id)) switch err { case nil: case ds.ErrNotFound: @@ -179,10 +179,10 @@ func (p *IpnsPublisher) updateRecord(ctx context.Context, k crypto.PrivKey, valu // Put the new record. key := IpnsDsKey(id) - if err := p.ds.Put(key, data); err != nil { + if err := p.ds.Put(ctx, key, data); err != nil { return nil, err } - if err := p.ds.Sync(key); err != nil { + if err := p.ds.Sync(ctx, key); err != nil { return nil, err } return entry, nil diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 844ed86ed..4be9ec846 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -95,7 +95,7 @@ func testNamekeyPublisher(t *testing.T, keyType int, expectedErr error, expected // Also check datastore for completeness key := dshelp.NewKeyFromBinary([]byte(namekey)) - exists, err := dstore.Has(key) + exists, err := dstore.Has(ctx, key) if err != nil { t.Fatal(err) } @@ -150,7 +150,7 @@ type checkSyncDS struct { syncKeys map[ds.Key]struct{} } -func (d *checkSyncDS) Sync(prefix ds.Key) error { +func (d *checkSyncDS) Sync(ctx context.Context, prefix ds.Key) error { d.syncKeys[prefix] = struct{}{} - return d.Datastore.Sync(prefix) + return d.Datastore.Sync(ctx, prefix) } diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 4ba5d483c..5fefac222 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -133,7 +133,7 @@ func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) erro log.Debugf("republishing ipns entry for %s", id) // Look for it locally only - e, err := rp.getLastIPNSEntry(id) + e, err := rp.getLastIPNSEntry(ctx, id) if err != nil { if err == errNoEntry { return nil @@ -155,9 +155,9 @@ func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) erro return rp.ns.PublishWithEOL(ctx, priv, p, eol) } -func (rp *Republisher) getLastIPNSEntry(id peer.ID) (*pb.IpnsEntry, error) { +func (rp *Republisher) getLastIPNSEntry(ctx context.Context, id peer.ID) (*pb.IpnsEntry, error) { // Look for it locally only - val, err := rp.ds.Get(namesys.IpnsDsKey(id)) + val, err := rp.ds.Get(ctx, namesys.IpnsDsKey(id)) switch err { case nil: case ds.ErrNotFound: diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index 3775b188a..c7c0f0185 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -42,7 +42,6 @@ func getMockNode(t *testing.T, ctx context.Context) *mockNode { dstore := dssync.MutexWrap(ds.NewMapDatastore()) var idht *dht.IpfsDHT h, err := libp2p.New( - ctx, libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0"), libp2p.Routing(func(h host.Host) (routing.PeerRouting, error) { rt, err := dht.New(ctx, h, dht.Mode(dht.ModeServer)) @@ -208,7 +207,7 @@ func TestLongEOLRepublish(t *testing.T) { t.Fatal(err) } - entry, err := getLastIPNSEntry(publisher.store, publisher.h.ID()) + entry, err := getLastIPNSEntry(ctx, publisher.store, publisher.h.ID()) if err != nil { t.Fatal(err) } @@ -223,9 +222,9 @@ func TestLongEOLRepublish(t *testing.T) { } } -func getLastIPNSEntry(dstore ds.Datastore, id peer.ID) (*ipns_pb.IpnsEntry, error) { +func getLastIPNSEntry(ctx context.Context, dstore ds.Datastore, id peer.ID) (*ipns_pb.IpnsEntry, error) { // Look for it locally only - val, err := dstore.Get(namesys.IpnsDsKey(id)) + val, err := dstore.Get(ctx, namesys.IpnsDsKey(id)) if err != nil { return nil, err } From ab62a53afcdf15e0324f1a7b3e4a824d0973cb87 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 30 Nov 2021 10:21:59 -0800 Subject: [PATCH 3617/3817] Traversal-based car creation (#269) Add a writing/creation API in the car v2 package. Allows creation of file and streaming car files in a selector order. This commit was moved from ipld/go-car@c35591a559b5fc60bdd107f164b5b25d56d06378 --- ipld/car/v2/blockstore/insertionindex.go | 9 +- ipld/car/v2/blockstore/readwrite.go | 2 +- ipld/car/v2/index/example_test.go | 2 +- ipld/car/v2/index/index.go | 16 +- ipld/car/v2/index/index_test.go | 6 +- ipld/car/v2/index/indexsorted.go | 28 +- ipld/car/v2/index/mhindexsorted.go | 20 +- ipld/car/v2/index/mhindexsorted_test.go | 2 +- .../car/v2/internal/loader/counting_loader.go | 61 ++++ ipld/car/v2/internal/loader/writing_loader.go | 114 ++++++++ ipld/car/v2/options.go | 16 +- ipld/car/v2/options_test.go | 7 +- ipld/car/v2/selective.go | 269 ++++++++++++++++++ ipld/car/v2/selective_test.go | 83 ++++++ ipld/car/v2/testdata/sample-unixfs-v2.car | Bin 0 -> 485 bytes ipld/car/v2/writer.go | 5 +- 16 files changed, 603 insertions(+), 37 deletions(-) create mode 100644 ipld/car/v2/internal/loader/counting_loader.go create mode 100644 ipld/car/v2/internal/loader/writing_loader.go create mode 100644 ipld/car/v2/selective.go create mode 100644 ipld/car/v2/selective_test.go create mode 100644 ipld/car/v2/testdata/sample-unixfs-v2.car diff --git a/ipld/car/v2/blockstore/insertionindex.go b/ipld/car/v2/blockstore/insertionindex.go index 7cca61b31..e8575ee1d 100644 --- a/ipld/car/v2/blockstore/insertionindex.go +++ b/ipld/car/v2/blockstore/insertionindex.go @@ -103,10 +103,13 @@ func (ii *insertionIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { return nil } -func (ii *insertionIndex) Marshal(w io.Writer) error { +func (ii *insertionIndex) Marshal(w io.Writer) (uint64, error) { + l := uint64(0) if err := binary.Write(w, binary.LittleEndian, int64(ii.items.Len())); err != nil { - return err + return l, err } + l += 8 + var err error iter := func(i llrb.Item) bool { if err = cbor.Encode(w, i.(recordDigest).Record); err != nil { @@ -115,7 +118,7 @@ func (ii *insertionIndex) Marshal(w io.Writer) error { return true } ii.items.AscendGreaterOrEqual(ii.items.Min(), iter) - return err + return l, err } func (ii *insertionIndex) Unmarshal(r io.Reader) error { diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 0dda5a0bd..4f288fa0e 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -376,7 +376,7 @@ func (b *ReadWrite) Finalize() error { if err != nil { return err } - if err := index.WriteTo(fi, internalio.NewOffsetWriter(b.f, int64(b.header.IndexOffset))); err != nil { + if _, err := index.WriteTo(fi, internalio.NewOffsetWriter(b.f, int64(b.header.IndexOffset))); err != nil { return err } if _, err := b.header.WriteTo(internalio.NewOffsetWriter(b.f, carv2.PragmaSize)); err != nil { diff --git a/ipld/car/v2/index/example_test.go b/ipld/car/v2/index/example_test.go index ca0ac73ab..3e484afb9 100644 --- a/ipld/car/v2/index/example_test.go +++ b/ipld/car/v2/index/example_test.go @@ -77,7 +77,7 @@ func ExampleWriteTo() { panic(err) } }() - err = index.WriteTo(idx, f) + _, err = index.WriteTo(idx, f) if err != nil { panic(err) } diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 998a17a0b..10195b43a 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -15,6 +15,9 @@ import ( "github.com/ipfs/go-cid" ) +// CarIndexNone is a sentinal value used as a multicodec code for the index indicating no index. +const CarIndexNone = 0x300000 + type ( // Record is a pre-processed record of a car item and location. Record struct { @@ -40,7 +43,7 @@ type ( Codec() multicodec.Code // Marshal encodes the index in serial form. - Marshal(w io.Writer) error + Marshal(w io.Writer) (uint64, error) // Unmarshal decodes the index from its serial form. Unmarshal(r io.Reader) error @@ -118,13 +121,16 @@ func New(codec multicodec.Code) (Index, error) { // WriteTo writes the given idx into w. // The written bytes include the index encoding. // This can then be read back using index.ReadFrom -func WriteTo(idx Index, w io.Writer) error { +func WriteTo(idx Index, w io.Writer) (uint64, error) { buf := make([]byte, binary.MaxVarintLen64) b := varint.PutUvarint(buf, uint64(idx.Codec())) - if _, err := w.Write(buf[:b]); err != nil { - return err + n, err := w.Write(buf[:b]) + if err != nil { + return uint64(n), err } - return idx.Marshal(w) + + l, err := idx.Marshal(w) + return uint64(n) + l, err } // ReadFrom reads index from r. diff --git a/ipld/car/v2/index/index_test.go b/ipld/car/v2/index/index_test.go index bb369cfe0..267beb034 100644 --- a/ipld/car/v2/index/index_test.go +++ b/ipld/car/v2/index/index_test.go @@ -100,7 +100,8 @@ func TestWriteTo(t *testing.T) { destF, err := os.Create(dest) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, destF.Close()) }) - require.NoError(t, WriteTo(wantIdx, destF)) + _, err = WriteTo(wantIdx, destF) + require.NoError(t, err) // Seek to the beginning of the written out file. _, err = destF.Seek(0, io.SeekStart) @@ -126,6 +127,7 @@ func TestMarshalledIndexStartsWithCodec(t *testing.T) { // Assert the first two bytes are the corresponding multicodec code. buf := new(bytes.Buffer) - require.NoError(t, WriteTo(wantIdx, buf)) + _, err = WriteTo(wantIdx, buf) + require.NoError(t, err) require.Equal(t, varint.ToUvarint(uint64(multicodec.CarIndexSorted)), buf.Bytes()[:2]) } diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index 86994dd8b..2c05a9227 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -46,16 +46,18 @@ func (r recordSet) Swap(i, j int) { r[i], r[j] = r[j], r[i] } -func (s *singleWidthIndex) Marshal(w io.Writer) error { +func (s *singleWidthIndex) Marshal(w io.Writer) (uint64, error) { + l := uint64(0) if err := binary.Write(w, binary.LittleEndian, s.width); err != nil { - return err + return 0, err } + l += 4 if err := binary.Write(w, binary.LittleEndian, int64(len(s.index))); err != nil { - return err + return l, err } - // TODO: we could just w.Write(s.index) here and avoid overhead - _, err := io.Copy(w, bytes.NewBuffer(s.index)) - return err + l += 8 + n, err := w.Write(s.index) + return l + uint64(n), err } func (s *singleWidthIndex) Unmarshal(r io.Reader) error { @@ -158,10 +160,12 @@ func (m *multiWidthIndex) Codec() multicodec.Code { return multicodec.CarIndexSorted } -func (m *multiWidthIndex) Marshal(w io.Writer) error { +func (m *multiWidthIndex) Marshal(w io.Writer) (uint64, error) { + l := uint64(0) if err := binary.Write(w, binary.LittleEndian, int32(len(*m))); err != nil { - return err + return l, err } + l += 4 // The widths are unique, but ranging over a map isn't deterministic. // As per the CARv2 spec, we must order buckets by digest length. @@ -176,11 +180,13 @@ func (m *multiWidthIndex) Marshal(w io.Writer) error { for _, width := range widths { bucket := (*m)[width] - if err := bucket.Marshal(w); err != nil { - return err + n, err := bucket.Marshal(w) + l += n + if err != nil { + return l, err } } - return nil + return l, nil } func (m *multiWidthIndex) Unmarshal(r io.Reader) error { diff --git a/ipld/car/v2/index/mhindexsorted.go b/ipld/car/v2/index/mhindexsorted.go index e3cae3d07..55975b8e5 100644 --- a/ipld/car/v2/index/mhindexsorted.go +++ b/ipld/car/v2/index/mhindexsorted.go @@ -31,11 +31,12 @@ func newMultiWidthCodedIndex() *multiWidthCodedIndex { } } -func (m *multiWidthCodedIndex) Marshal(w io.Writer) error { +func (m *multiWidthCodedIndex) Marshal(w io.Writer) (uint64, error) { if err := binary.Write(w, binary.LittleEndian, m.code); err != nil { - return err + return 8, err } - return m.multiWidthIndex.Marshal(w) + n, err := m.multiWidthIndex.Marshal(w) + return 8 + n, err } func (m *multiWidthCodedIndex) Unmarshal(r io.Reader) error { @@ -59,22 +60,25 @@ func (m *MultihashIndexSorted) Codec() multicodec.Code { return multicodec.CarMultihashIndexSorted } -func (m *MultihashIndexSorted) Marshal(w io.Writer) error { +func (m *MultihashIndexSorted) Marshal(w io.Writer) (uint64, error) { if err := binary.Write(w, binary.LittleEndian, int32(len(*m))); err != nil { - return err + return 4, err } // The codes are unique, but ranging over a map isn't deterministic. // As per the CARv2 spec, we must order buckets by digest length. // TODO update CARv2 spec to reflect this for the new index type. codes := m.sortedMultihashCodes() + l := uint64(4) for _, code := range codes { mwci := (*m)[code] - if err := mwci.Marshal(w); err != nil { - return err + n, err := mwci.Marshal(w) + l += n + if err != nil { + return l, err } } - return nil + return l, nil } func (m *MultihashIndexSorted) sortedMultihashCodes() []uint64 { diff --git a/ipld/car/v2/index/mhindexsorted_test.go b/ipld/car/v2/index/mhindexsorted_test.go index e02ba0599..79fc9c5f0 100644 --- a/ipld/car/v2/index/mhindexsorted_test.go +++ b/ipld/car/v2/index/mhindexsorted_test.go @@ -32,7 +32,7 @@ func TestMultiWidthCodedIndex_MarshalUnmarshal(t *testing.T) { // Marshal the index. buf := new(bytes.Buffer) - err = subject.Marshal(buf) + _, err = subject.Marshal(buf) require.NoError(t, err) // Unmarshal it back to another instance of mh sorted index. diff --git a/ipld/car/v2/internal/loader/counting_loader.go b/ipld/car/v2/internal/loader/counting_loader.go new file mode 100644 index 000000000..e428993ea --- /dev/null +++ b/ipld/car/v2/internal/loader/counting_loader.go @@ -0,0 +1,61 @@ +package loader + +import ( + "bytes" + "io" + + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/linking" + "github.com/multiformats/go-varint" +) + +// counter tracks how much data has been read. +type counter struct { + totalRead uint64 +} + +func (c *counter) Size() uint64 { + return c.totalRead +} + +// ReadCounter provides an externally consumable interface to the +// additional data tracked about the linksystem. +type ReadCounter interface { + Size() uint64 +} + +type countingReader struct { + r io.Reader + c *counter +} + +func (c *countingReader) Read(p []byte) (int, error) { + n, err := c.r.Read(p) + c.c.totalRead += uint64(n) + return n, err +} + +// CountingLinkSystem wraps an ipld linksystem with to track the size of +// data loaded in a `counter` object. Each time nodes are loaded from the +// link system which trigger block reads, the size of the block as it would +// appear in a CAR file is added to the counter (included the size of the +// CID and the varint length for the block data). +func CountingLinkSystem(ls ipld.LinkSystem) (ipld.LinkSystem, ReadCounter) { + c := counter{} + clc := ls + clc.StorageReadOpener = func(lc linking.LinkContext, l ipld.Link) (io.Reader, error) { + r, err := ls.StorageReadOpener(lc, l) + if err != nil { + return nil, err + } + buf := bytes.NewBuffer(nil) + n, err := buf.ReadFrom(r) + if err != nil { + return nil, err + } + size := varint.ToUvarint(uint64(n) + uint64(len(l.Binary()))) + c.totalRead += uint64(len(size)) + uint64(len(l.Binary())) + return &countingReader{buf, &c}, nil + } + return clc, &c +} diff --git a/ipld/car/v2/internal/loader/writing_loader.go b/ipld/car/v2/internal/loader/writing_loader.go new file mode 100644 index 000000000..13236f1c6 --- /dev/null +++ b/ipld/car/v2/internal/loader/writing_loader.go @@ -0,0 +1,114 @@ +package loader + +import ( + "bytes" + "io" + + "github.com/ipfs/go-cid" + "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/linking" + "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-varint" +) + +type writerOutput struct { + w io.Writer + size uint64 + code multicodec.Code + rcrds []index.Record +} + +func (w *writerOutput) Size() uint64 { + return w.size +} + +func (w *writerOutput) Index() (index.Index, error) { + idx, err := index.New(w.code) + if err != nil { + return nil, err + } + if err := idx.Load(w.rcrds); err != nil { + return nil, err + } + + return idx, nil +} + +// An IndexTracker tracks the records loaded/written, calculate an +// index based on them. +type IndexTracker interface { + ReadCounter + Index() (index.Index, error) +} + +type writingReader struct { + r io.Reader + len int64 + cid string + wo *writerOutput +} + +func (w *writingReader) Read(p []byte) (int, error) { + if w.wo != nil { + // write the cid + size := varint.ToUvarint(uint64(w.len) + uint64(len(w.cid))) + if _, err := w.wo.w.Write(size); err != nil { + return 0, err + } + if _, err := w.wo.w.Write([]byte(w.cid)); err != nil { + return 0, err + } + cpy := bytes.NewBuffer(w.r.(*bytes.Buffer).Bytes()) + if _, err := cpy.WriteTo(w.wo.w); err != nil { + return 0, err + } + + // maybe write the index. + if w.wo.code != index.CarIndexNone { + _, c, err := cid.CidFromBytes([]byte(w.cid)) + if err != nil { + return 0, err + } + w.wo.rcrds = append(w.wo.rcrds, index.Record{ + Cid: c, + Offset: w.wo.size, + }) + } + w.wo.size += uint64(w.len) + uint64(len(size)+len(w.cid)) + + w.wo = nil + } + + return w.r.Read(p) +} + +// TeeingLinkSystem wraps an IPLD.LinkSystem so that each time a block is loaded from it, +// that block is also written as a CAR block to the provided io.Writer. Metadata +// (the size of data written) is provided in the second return value. +// The `initialOffset` is used to calculate the offsets recorded for the index, and will be +// included in the `.Size()` of the IndexTracker. +// An indexCodec of `index.CarIndexNoIndex` can be used to not track these offsets. +func TeeingLinkSystem(ls ipld.LinkSystem, w io.Writer, initialOffset uint64, indexCodec multicodec.Code) (ipld.LinkSystem, IndexTracker) { + wo := writerOutput{ + w: w, + size: initialOffset, + code: indexCodec, + rcrds: make([]index.Record, 0), + } + + tls := ls + tls.StorageReadOpener = func(lc linking.LinkContext, l ipld.Link) (io.Reader, error) { + r, err := ls.StorageReadOpener(lc, l) + if err != nil { + return nil, err + } + buf := bytes.NewBuffer(nil) + n, err := buf.ReadFrom(r) + if err != nil { + return nil, err + } + return &writingReader{buf, n, l.Binary(), &wo}, nil + } + return tls, &wo +} diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go index 5a3b1930a..2fb1d1fc4 100644 --- a/ipld/car/v2/options.go +++ b/ipld/car/v2/options.go @@ -1,6 +1,11 @@ package car -import "github.com/multiformats/go-multicodec" +import ( + "math" + + "github.com/ipld/go-car/v2/index" + "github.com/multiformats/go-multicodec" +) // DefaultMaxIndexCidSize specifies the maximum size in byptes accepted as a section CID by CARv2 index. const DefaultMaxIndexCidSize = 2 << 10 // 2 KiB @@ -33,6 +38,7 @@ type Options struct { BlockstoreAllowDuplicatePuts bool BlockstoreUseWholeCIDs bool + MaxTraversalLinks uint64 } // ApplyOptions applies given opts and returns the resulting Options. @@ -40,6 +46,7 @@ type Options struct { // side effect of Option. func ApplyOptions(opt ...Option) Options { var opts Options + opts.MaxTraversalLinks = math.MaxInt64 //default: traverse all for _, o := range opt { o(&opts) } @@ -84,6 +91,13 @@ func UseIndexCodec(c multicodec.Code) Option { } } +// WithoutIndex flags that no index should be included in generation. +func WithoutIndex() Option { + return func(o *Options) { + o.IndexCodec = index.CarIndexNone + } +} + // StoreIdentityCIDs sets whether to persist sections that are referenced by // CIDs with multihash.IDENTITY digest. // When writing CAR files with this option, diff --git a/ipld/car/v2/options_test.go b/ipld/car/v2/options_test.go index 307568a4a..7e060acf0 100644 --- a/ipld/car/v2/options_test.go +++ b/ipld/car/v2/options_test.go @@ -1,6 +1,7 @@ package car_test import ( + "math" "testing" carv2 "github.com/ipld/go-car/v2" @@ -11,8 +12,9 @@ import ( func TestApplyOptions_SetsExpectedDefaults(t *testing.T) { require.Equal(t, carv2.Options{ - IndexCodec: multicodec.CarMultihashIndexSorted, - MaxIndexCidSize: carv2.DefaultMaxIndexCidSize, + IndexCodec: multicodec.CarMultihashIndexSorted, + MaxIndexCidSize: carv2.DefaultMaxIndexCidSize, + MaxTraversalLinks: math.MaxInt64, }, carv2.ApplyOptions()) } @@ -27,6 +29,7 @@ func TestApplyOptions_AppliesOptions(t *testing.T) { StoreIdentityCIDs: true, BlockstoreAllowDuplicatePuts: true, BlockstoreUseWholeCIDs: true, + MaxTraversalLinks: math.MaxInt64, }, carv2.ApplyOptions( carv2.UseDataPadding(123), diff --git a/ipld/car/v2/selective.go b/ipld/car/v2/selective.go new file mode 100644 index 000000000..22351e715 --- /dev/null +++ b/ipld/car/v2/selective.go @@ -0,0 +1,269 @@ +package car + +import ( + "context" + "fmt" + "io" + "math" + "os" + + "github.com/ipfs/go-cid" + "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-car/v2/internal/carv1" + "github.com/ipld/go-car/v2/internal/loader" + ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/linking" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + "github.com/ipld/go-ipld-prime/node/basicnode" + "github.com/ipld/go-ipld-prime/traversal" + "github.com/ipld/go-ipld-prime/traversal/selector" +) + +// ErrSizeMismatch is returned when a written traversal realizes the written header size does not +// match the actual number of car bytes written. +var ErrSizeMismatch = fmt.Errorf("car-error-sizemismatch") + +// ErrOffsetImpossible is returned when specified paddings or offsets of either a wrapped carv1 +// or index cannot be satisfied based on the data being written. +var ErrOffsetImpossible = fmt.Errorf("car-error-offsetimpossible") + +// MaxTraversalLinks changes the allowed number of links a selector traversal +// can execute before failing. +// +// Note that setting this option may cause an error to be returned from selector +// execution when building a SelectiveCar. +func MaxTraversalLinks(MaxTraversalLinks uint64) Option { + return func(sco *Options) { + sco.MaxTraversalLinks = MaxTraversalLinks + } +} + +// NewSelectiveWriter walks through the proposed dag traversal to learn its total size in order to be able to +// stream out a car to a writer in the expected traversal order in one go. +func NewSelectiveWriter(ctx context.Context, ls *ipld.LinkSystem, root cid.Cid, selector ipld.Node, opts ...Option) (Writer, error) { + cls, cntr := loader.CountingLinkSystem(*ls) + + c1h := carv1.CarHeader{Roots: []cid.Cid{root}, Version: 1} + headSize, err := carv1.HeaderSize(&c1h) + if err != nil { + return nil, err + } + if err := traverse(ctx, &cls, root, selector, ApplyOptions(opts...)); err != nil { + return nil, err + } + tc := traversalCar{ + size: headSize + cntr.Size(), + ctx: ctx, + root: root, + selector: selector, + ls: ls, + opts: ApplyOptions(opts...), + } + return &tc, nil +} + +// TraverseToFile writes a car file matching a given root and selector to the +// path at `destination` using one read of each block. +func TraverseToFile(ctx context.Context, ls *ipld.LinkSystem, root cid.Cid, selector ipld.Node, destination string, opts ...Option) error { + tc := traversalCar{ + size: 0, + ctx: ctx, + root: root, + selector: selector, + ls: ls, + opts: ApplyOptions(opts...), + } + + fp, err := os.Create(destination) + if err != nil { + return err + } + defer fp.Close() + + _, err = tc.WriteTo(fp) + if err != nil { + return err + } + + // fix header size. + if _, err = fp.Seek(0, 0); err != nil { + return err + } + + tc.size = uint64(tc.size) + if _, err = tc.WriteV2Header(fp); err != nil { + return err + } + + return nil +} + +// TraverseV1 walks through the proposed dag traversal and writes a carv1 to the provided io.Writer +func TraverseV1(ctx context.Context, ls *ipld.LinkSystem, root cid.Cid, selector ipld.Node, writer io.Writer, opts ...Option) (uint64, error) { + opts = append(opts, WithoutIndex()) + tc := traversalCar{ + size: 0, + ctx: ctx, + root: root, + selector: selector, + ls: ls, + opts: ApplyOptions(opts...), + } + + len, _, err := tc.WriteV1(writer) + return len, err +} + +// Writer is an interface allowing writing a car prepared by PrepareTraversal +type Writer interface { + io.WriterTo +} + +var _ Writer = (*traversalCar)(nil) + +type traversalCar struct { + size uint64 + ctx context.Context + root cid.Cid + selector ipld.Node + ls *ipld.LinkSystem + opts Options +} + +func (tc *traversalCar) WriteTo(w io.Writer) (int64, error) { + n, err := tc.WriteV2Header(w) + if err != nil { + return n, err + } + v1s, idx, err := tc.WriteV1(w) + n += int64(v1s) + + if err != nil { + return n, err + } + + // index padding, then index + if tc.opts.IndexCodec != index.CarIndexNone { + if tc.opts.IndexPadding > 0 { + buf := make([]byte, tc.opts.IndexPadding) + pn, err := w.Write(buf) + n += int64(pn) + if err != nil { + return n, err + } + } + in, err := index.WriteTo(idx, w) + n += int64(in) + if err != nil { + return n, err + } + } + + return n, err +} + +func (tc *traversalCar) WriteV2Header(w io.Writer) (int64, error) { + n, err := w.Write(Pragma) + if err != nil { + return int64(n), err + } + + h := NewHeader(tc.size) + if p := tc.opts.DataPadding; p > 0 { + h = h.WithDataPadding(p) + } + if p := tc.opts.IndexPadding; p > 0 { + h = h.WithIndexPadding(p) + } + if tc.opts.IndexCodec == index.CarIndexNone { + h.IndexOffset = 0 + } + hn, err := h.WriteTo(w) + if err != nil { + return int64(n) + hn, err + } + hn += int64(n) + + // We include the initial data padding after the carv2 header + if h.DataOffset > uint64(hn) { + // TODO: buffer writes if this needs to be big. + buf := make([]byte, h.DataOffset-uint64(hn)) + n, err = w.Write(buf) + hn += int64(n) + if err != nil { + return hn, err + } + } else if h.DataOffset < uint64(hn) { + return hn, ErrOffsetImpossible + } + + return hn, nil +} + +func (tc *traversalCar) WriteV1(w io.Writer) (uint64, index.Index, error) { + // write the v1 header + c1h := carv1.CarHeader{Roots: []cid.Cid{tc.root}, Version: 1} + if err := carv1.WriteHeader(&c1h, w); err != nil { + return 0, nil, err + } + v1Size, err := carv1.HeaderSize(&c1h) + if err != nil { + return v1Size, nil, err + } + + // write the block. + wls, writer := loader.TeeingLinkSystem(*tc.ls, w, v1Size, tc.opts.IndexCodec) + err = traverse(tc.ctx, &wls, tc.root, tc.selector, tc.opts) + v1Size = writer.Size() + if err != nil { + return v1Size, nil, err + } + if tc.size != 0 && tc.size != v1Size { + return v1Size, nil, ErrSizeMismatch + } + tc.size = v1Size + + if tc.opts.IndexCodec == index.CarIndexNone { + return v1Size, nil, nil + } + idx, err := writer.Index() + return v1Size, idx, err +} + +func traverse(ctx context.Context, ls *ipld.LinkSystem, root cid.Cid, s ipld.Node, opts Options) error { + sel, err := selector.CompileSelector(s) + if err != nil { + return err + } + + progress := traversal.Progress{ + Cfg: &traversal.Config{ + Ctx: ctx, + LinkSystem: *ls, + LinkTargetNodePrototypeChooser: func(_ ipld.Link, _ linking.LinkContext) (ipld.NodePrototype, error) { + return basicnode.Prototype.Any, nil + }, + LinkVisitOnlyOnce: !opts.BlockstoreAllowDuplicatePuts, + }, + } + if opts.MaxTraversalLinks < math.MaxInt64 { + progress.Budget = &traversal.Budget{ + NodeBudget: math.MaxInt64, + LinkBudget: int64(opts.MaxTraversalLinks), + } + } + + lnk := cidlink.Link{Cid: root} + ls.TrustedStorage = true + rootNode, err := ls.Load(ipld.LinkContext{}, lnk, basicnode.Prototype.Any) + if err != nil { + return fmt.Errorf("root blk load failed: %s", err) + } + err = progress.WalkMatching(rootNode, sel, func(_ traversal.Progress, _ ipld.Node) error { + return nil + }) + if err != nil { + return fmt.Errorf("walk failed: %s", err) + } + return nil +} diff --git a/ipld/car/v2/selective_test.go b/ipld/car/v2/selective_test.go new file mode 100644 index 000000000..f2bba6f8a --- /dev/null +++ b/ipld/car/v2/selective_test.go @@ -0,0 +1,83 @@ +package car_test + +import ( + "bytes" + "context" + "os" + "path" + "testing" + + "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/blockstore" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + "github.com/ipld/go-ipld-prime/storage/bsadapter" + selectorparse "github.com/ipld/go-ipld-prime/traversal/selector/parse" + "github.com/stretchr/testify/require" + + _ "github.com/ipld/go-codec-dagpb" + _ "github.com/ipld/go-ipld-prime/codec/dagcbor" + _ "github.com/ipld/go-ipld-prime/codec/raw" +) + +func TestPrepareTraversal(t *testing.T) { + from, err := blockstore.OpenReadOnly("testdata/sample-unixfs-v2.car") + require.NoError(t, err) + ls := cidlink.DefaultLinkSystem() + bsa := bsadapter.Adapter{Wrapped: from} + ls.SetReadStorage(&bsa) + + rts, _ := from.Roots() + writer, err := car.NewSelectiveWriter(context.Background(), &ls, rts[0], selectorparse.CommonSelector_ExploreAllRecursively) + require.NoError(t, err) + + buf := bytes.Buffer{} + n, err := writer.WriteTo(&buf) + require.NoError(t, err) + require.Equal(t, int64(len(buf.Bytes())), n) + + fi, _ := os.Stat("testdata/sample-unixfs-v2.car") + require.Equal(t, fi.Size(), n) + + // Headers should be equal + h1, _ := car.OpenReader("testdata/sample-unixfs-v2.car") + h1h := bytes.Buffer{} + h1h.Write(car.Pragma) + h1.Header.WriteTo(&h1h) + require.Equal(t, buf.Bytes()[:h1h.Len()], h1h.Bytes()) +} + +func TestFileTraversal(t *testing.T) { + from, err := blockstore.OpenReadOnly("testdata/sample-unixfs-v2.car") + require.NoError(t, err) + ls := cidlink.DefaultLinkSystem() + bsa := bsadapter.Adapter{Wrapped: from} + ls.SetReadStorage(&bsa) + + rts, _ := from.Roots() + outDir := t.TempDir() + err = car.TraverseToFile(context.Background(), &ls, rts[0], selectorparse.CommonSelector_ExploreAllRecursively, path.Join(outDir, "out.car")) + require.NoError(t, err) + + require.FileExists(t, path.Join(outDir, "out.car")) + + fa, _ := os.Stat("testdata/sample-unixfs-v2.car") + fb, _ := os.Stat(path.Join(outDir, "out.car")) + require.Equal(t, fa.Size(), fb.Size()) +} + +func TestV1Traversal(t *testing.T) { + from, err := blockstore.OpenReadOnly("testdata/sample-v1.car") + require.NoError(t, err) + ls := cidlink.DefaultLinkSystem() + bsa := bsadapter.Adapter{Wrapped: from} + ls.SetReadStorage(&bsa) + + rts, _ := from.Roots() + w := bytes.NewBuffer(nil) + n, err := car.TraverseV1(context.Background(), &ls, rts[0], selectorparse.CommonSelector_ExploreAllRecursively, w) + require.NoError(t, err) + require.Equal(t, int64(len(w.Bytes())), int64(n)) + + fa, _ := os.Stat("testdata/sample-v1.car") + require.Equal(t, fa.Size(), int64(n)) +} diff --git a/ipld/car/v2/testdata/sample-unixfs-v2.car b/ipld/car/v2/testdata/sample-unixfs-v2.car new file mode 100644 index 0000000000000000000000000000000000000000..0d8d8fd6451f778cc3ed8d936714ebe286e659eb GIT binary patch literal 485 zcmd;Dm|m7zRGgWg$HagJjG=rPMhL?nN?R>TEy~X?DQ>)>6`{(&SRkapW#UnI@7eJX zh5E>xKU3FfURkh6u0VNGRBWQELhhASTVPf&8Zd?mDXjFE;IGf`KD)nhdZxF5gg39= z#e-+H7jKM;zNqQv}B@#SbOdO1%#5i0?8)z~yE?`WQU;vrg$N>r@ gA!x{f*cw1w0p$}4r(;lcghI#~s*X^= Date: Thu, 23 Sep 2021 21:33:42 +0100 Subject: [PATCH 3618/3817] feat: per-cid locking Unfortunately, stripped locking breaks down when doing many concurrent operations. Luckily, per-cid locking isn't that expensive. In my benchmarks, it doesn't even make a noticeable difference. This commit was moved from ipfs/go-ipfs-blockstore@f5eac75f2f2368c73bff22c28c7a31ac52d7b137 --- blockstore/arc_cache.go | 251 +++++++++++++++++++++++++++++----------- 1 file changed, 183 insertions(+), 68 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index bb78c2a2a..5dc7c6ed0 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -2,6 +2,7 @@ package blockstore import ( "context" + "sort" "sync" lru "github.com/hashicorp/golang-lru" @@ -13,13 +14,20 @@ import ( type cacheHave bool type cacheSize int +type lock struct { + mu sync.RWMutex + refcnt int +} + // arccache wraps a BlockStore with an Adaptive Replacement Cache (ARC) that // does not store the actual blocks, just metadata about them: existence and // size. This provides block access-time improvements, allowing // to short-cut many searches without querying the underlying datastore. type arccache struct { + lklk sync.Mutex + lks map[string]*lock + cache *lru.TwoQueueCache - lks [256]sync.RWMutex blockstore Blockstore viewer Viewer @@ -36,7 +44,7 @@ func newARCCachedBS(ctx context.Context, bs Blockstore, lruSize int) (*arccache, if err != nil { return nil, err } - c := &arccache{cache: cache, blockstore: bs} + c := &arccache{cache: cache, blockstore: bs, lks: make(map[string]*lock)} c.hits = metrics.NewCtx(ctx, "arc.hits_total", "Number of ARC cache hits").Counter() c.total = metrics.NewCtx(ctx, "arc_total", "Total number of ARC cache requests").Counter() if v, ok := bs.(Viewer); ok { @@ -45,12 +53,39 @@ func newARCCachedBS(ctx context.Context, bs Blockstore, lruSize int) (*arccache, return c, nil } -func (b *arccache) getLock(k cid.Cid) *sync.RWMutex { - return &b.lks[mutexKey(k)] +func (b *arccache) lock(k string, write bool) { + b.lklk.Lock() + lk, ok := b.lks[k] + if !ok { + lk = new(lock) + b.lks[k] = lk + } + lk.refcnt++ + b.lklk.Unlock() + if write { + lk.mu.Lock() + } else { + lk.mu.RLock() + } +} + +func (b *arccache) unlock(key string, write bool) { + b.lklk.Lock() + lk := b.lks[key] + lk.refcnt-- + if lk.refcnt == 0 { + delete(b.lks, key) + } + b.lklk.Unlock() + if write { + lk.mu.Unlock() + } else { + lk.mu.RUnlock() + } } -func mutexKey(k cid.Cid) uint8 { - return k.KeyString()[len(k.KeyString())-1] +func cacheKey(k cid.Cid) string { + return string(k.Hash()) } func (b *arccache) DeleteBlock(ctx context.Context, k cid.Cid) error { @@ -58,18 +93,20 @@ func (b *arccache) DeleteBlock(ctx context.Context, k cid.Cid) error { return nil } - if has, _, ok := b.queryCache(k); ok && !has { + key := cacheKey(k) + + if has, _, ok := b.queryCache(key); ok && !has { return nil } - lk := b.getLock(k) - lk.Lock() - defer lk.Unlock() + b.lock(key, true) + defer b.unlock(key, true) - b.cache.Remove(k) // Invalidate cache before deleting. err := b.blockstore.DeleteBlock(ctx, k) if err == nil { - b.cacheHave(k, false) + b.cacheHave(key, false) + } else { + b.cacheInvalidate(key) } return err } @@ -79,19 +116,20 @@ func (b *arccache) Has(ctx context.Context, k cid.Cid) (bool, error) { return false, nil } - if has, _, ok := b.queryCache(k); ok { + key := cacheKey(k) + + if has, _, ok := b.queryCache(key); ok { return has, nil } - lk := b.getLock(k) - lk.RLock() - defer lk.RUnlock() + b.lock(key, false) + defer b.unlock(key, false) has, err := b.blockstore.Has(ctx, k) if err != nil { return false, err } - b.cacheHave(k, has) + b.cacheHave(key, has) return has, nil } @@ -100,7 +138,9 @@ func (b *arccache) GetSize(ctx context.Context, k cid.Cid) (int, error) { return -1, ErrNotFound } - if has, blockSize, ok := b.queryCache(k); ok { + key := cacheKey(k) + + if has, blockSize, ok := b.queryCache(key); ok { if !has { // don't have it, return return -1, ErrNotFound @@ -112,15 +152,14 @@ func (b *arccache) GetSize(ctx context.Context, k cid.Cid) (int, error) { // we have it but don't know the size, ask the datastore. } - lk := b.getLock(k) - lk.RLock() - defer lk.RUnlock() + b.lock(key, false) + defer b.unlock(key, false) blockSize, err := b.blockstore.GetSize(ctx, k) if err == ErrNotFound { - b.cacheHave(k, false) + b.cacheHave(key, false) } else if err == nil { - b.cacheSize(k, blockSize) + b.cacheSize(key, blockSize) } return blockSize, err } @@ -140,17 +179,33 @@ func (b *arccache) View(ctx context.Context, k cid.Cid, callback func([]byte) er return ErrNotFound } - if has, _, ok := b.queryCache(k); ok && !has { + key := cacheKey(k) + + if has, _, ok := b.queryCache(key); ok && !has { // short circuit if the cache deterministically tells us the item // doesn't exist. return ErrNotFound } - lk := b.getLock(k) - lk.RLock() - defer lk.RUnlock() + b.lock(key, false) + defer b.unlock(key, false) - return b.viewer.View(ctx, k, callback) + var cberr error + var size int + if err := b.viewer.View(ctx, k, func(buf []byte) error { + size = len(buf) + cberr = callback(buf) + return nil + }); err != nil { + if err == ErrNotFound { + b.cacheHave(key, false) + } + return err + } + + b.cacheSize(key, size) + + return cberr } func (b *arccache) Get(ctx context.Context, k cid.Cid) (blocks.Block, error) { @@ -158,72 +213,134 @@ func (b *arccache) Get(ctx context.Context, k cid.Cid) (blocks.Block, error) { return nil, ErrNotFound } - if has, _, ok := b.queryCache(k); ok && !has { + key := cacheKey(k) + + if has, _, ok := b.queryCache(key); ok && !has { return nil, ErrNotFound } - lk := b.getLock(k) - lk.RLock() - defer lk.RUnlock() + b.lock(key, false) + defer b.unlock(key, false) bl, err := b.blockstore.Get(ctx, k) if bl == nil && err == ErrNotFound { - b.cacheHave(k, false) + b.cacheHave(key, false) } else if bl != nil { - b.cacheSize(k, len(bl.RawData())) + b.cacheSize(key, len(bl.RawData())) } return bl, err } func (b *arccache) Put(ctx context.Context, bl blocks.Block) error { - if has, _, ok := b.queryCache(bl.Cid()); ok && has { + key := cacheKey(bl.Cid()) + + if has, _, ok := b.queryCache(key); ok && has { return nil } - lk := b.getLock(bl.Cid()) - lk.Lock() - defer lk.Unlock() + b.lock(key, true) + defer b.unlock(key, true) err := b.blockstore.Put(ctx, bl) if err == nil { - b.cacheSize(bl.Cid(), len(bl.RawData())) + b.cacheSize(key, len(bl.RawData())) + } else { + b.cacheInvalidate(key) } return err } +type keyedBlocks struct { + keys []string + blocks []blocks.Block +} + +func (b *keyedBlocks) Len() int { + return len(b.keys) +} + +func (b *keyedBlocks) Less(i, j int) bool { + return b.keys[i] < b.keys[j] +} + +func (b *keyedBlocks) Swap(i, j int) { + b.keys[i], b.keys[j] = b.keys[j], b.keys[i] + b.blocks[i], b.blocks[j] = b.blocks[j], b.blocks[i] +} + +func (b *keyedBlocks) append(key string, blk blocks.Block) { + b.keys = append(b.keys, key) + b.blocks = append(b.blocks, blk) +} + +func (b *keyedBlocks) isEmpty() bool { + return len(b.keys) == 0 +} + +func (b *keyedBlocks) sortAndDedup() { + if b.isEmpty() { + return + } + + sort.Sort(b) + + // https://github.com/golang/go/wiki/SliceTricks#in-place-deduplicate-comparable + j := 0 + for i := 1; i < len(b.keys); i++ { + if b.keys[j] == b.keys[i] { + continue + } + j++ + b.keys[j] = b.keys[i] + b.blocks[j] = b.blocks[i] + } + + b.keys = b.keys[:j+1] + b.blocks = b.blocks[:j+1] +} + +func newKeyedBlocks(cap int) *keyedBlocks { + return &keyedBlocks{ + keys: make([]string, 0, cap), + blocks: make([]blocks.Block, 0, cap), + } +} + func (b *arccache) PutMany(ctx context.Context, bs []blocks.Block) error { - mxs := [256]*sync.RWMutex{} - var good []blocks.Block - for _, block := range bs { + good := newKeyedBlocks(len(bs)) + for _, blk := range bs { // call put on block if result is inconclusive or we are sure that // the block isn't in storage - if has, _, ok := b.queryCache(block.Cid()); !ok || (ok && !has) { - good = append(good, block) - mxs[mutexKey(block.Cid())] = &b.lks[mutexKey(block.Cid())] + key := cacheKey(blk.Cid()) + if has, _, ok := b.queryCache(key); !ok || (ok && !has) { + good.append(key, blk) } } - for _, mx := range mxs { - if mx != nil { - mx.Lock() - } + if good.isEmpty() { + return nil + } + + good.sortAndDedup() + + for _, key := range good.keys { + b.lock(key, true) } defer func() { - for _, mx := range mxs { - if mx != nil { - mx.Unlock() - } + for _, key := range good.keys { + b.unlock(key, true) } }() - err := b.blockstore.PutMany(ctx, good) + err := b.blockstore.PutMany(ctx, good.blocks) if err != nil { return err } - for _, block := range good { - b.cacheSize(block.Cid(), len(block.RawData())) + for i, key := range good.keys { + b.cacheSize(key, len(good.blocks[i].RawData())) } + return nil } @@ -231,12 +348,16 @@ func (b *arccache) HashOnRead(enabled bool) { b.blockstore.HashOnRead(enabled) } -func (b *arccache) cacheHave(c cid.Cid, have bool) { - b.cache.Add(string(c.Hash()), cacheHave(have)) +func (b *arccache) cacheHave(key string, have bool) { + b.cache.Add(key, cacheHave(have)) +} + +func (b *arccache) cacheSize(key string, blockSize int) { + b.cache.Add(key, cacheSize(blockSize)) } -func (b *arccache) cacheSize(c cid.Cid, blockSize int) { - b.cache.Add(string(c.Hash()), cacheSize(blockSize)) +func (b *arccache) cacheInvalidate(key string) { + b.cache.Remove(key) } // queryCache checks if the CID is in the cache. If so, it returns: @@ -250,16 +371,10 @@ func (b *arccache) cacheSize(c cid.Cid, blockSize int) { // // When ok is true, exists carries the correct answer, and size carries the // size, if known, or -1 if not. -func (b *arccache) queryCache(k cid.Cid) (exists bool, size int, ok bool) { +func (b *arccache) queryCache(k string) (exists bool, size int, ok bool) { b.total.Inc() - if !k.Defined() { - log.Error("undefined cid in arccache") - // Return cache invalid so the call to blockstore happens - // in case of invalid key and correct error is created. - return false, -1, false - } - h, ok := b.cache.Get(string(k.Hash())) + h, ok := b.cache.Get(k) if ok { b.hits.Inc() switch h := h.(type) { From 641311a15baba9fa5c6390d3d9a2e9fbaec590b6 Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Thu, 9 Dec 2021 17:20:40 -0800 Subject: [PATCH 3619/3817] update context datastore This commit was moved from ipld/go-car@b04ac83786cb9ddd9b632a921004fede96c7ff73 --- ipld/car/car.go | 22 +++++++++++----------- ipld/car/car_test.go | 5 +++-- ipld/car/selectivecar.go | 6 +++--- ipld/car/selectivecar_test.go | 13 +++++++------ 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index 51826706d..bf773779a 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -20,11 +20,11 @@ func init() { } type Store interface { - Put(blocks.Block) error + Put(context.Context, blocks.Block) error } type ReadStore interface { - Get(cid.Cid) (blocks.Block, error) + Get(context.Context, cid.Cid) (blocks.Block, error) } type CarHeader struct { @@ -163,30 +163,30 @@ func (cr *CarReader) Next() (blocks.Block, error) { } type batchStore interface { - PutMany([]blocks.Block) error + PutMany(context.Context, []blocks.Block) error } -func LoadCar(s Store, r io.Reader) (*CarHeader, error) { +func LoadCar(ctx context.Context, s Store, r io.Reader) (*CarHeader, error) { cr, err := NewCarReader(r) if err != nil { return nil, err } if bs, ok := s.(batchStore); ok { - return loadCarFast(bs, cr) + return loadCarFast(ctx, bs, cr) } - return loadCarSlow(s, cr) + return loadCarSlow(ctx, s, cr) } -func loadCarFast(s batchStore, cr *CarReader) (*CarHeader, error) { +func loadCarFast(ctx context.Context, s batchStore, cr *CarReader) (*CarHeader, error) { var buf []blocks.Block for { blk, err := cr.Next() if err != nil { if err == io.EOF { if len(buf) > 0 { - if err := s.PutMany(buf); err != nil { + if err := s.PutMany(ctx, buf); err != nil { return nil, err } } @@ -198,7 +198,7 @@ func loadCarFast(s batchStore, cr *CarReader) (*CarHeader, error) { buf = append(buf, blk) if len(buf) > 1000 { - if err := s.PutMany(buf); err != nil { + if err := s.PutMany(ctx, buf); err != nil { return nil, err } buf = buf[:0] @@ -206,7 +206,7 @@ func loadCarFast(s batchStore, cr *CarReader) (*CarHeader, error) { } } -func loadCarSlow(s Store, cr *CarReader) (*CarHeader, error) { +func loadCarSlow(ctx context.Context, s Store, cr *CarReader) (*CarHeader, error) { for { blk, err := cr.Next() if err != nil { @@ -216,7 +216,7 @@ func loadCarSlow(s Store, cr *CarReader) (*CarHeader, error) { return nil, err } - if err := s.Put(blk); err != nil { + if err := s.Put(ctx, blk); err != nil { return nil, err } } diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 0a9c80fed..9ae309099 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -24,6 +24,7 @@ func assertAddNodes(t *testing.T, ds format.DAGService, nds ...format.Node) { } func TestRoundtrip(t *testing.T) { + ctx := context.Background() dserv := dstest.Mock() a := merkledag.NewRawNode([]byte("aaaa")) b := merkledag.NewRawNode([]byte("bbbb")) @@ -48,7 +49,7 @@ func TestRoundtrip(t *testing.T) { } bserv := dstest.Bserv() - ch, err := car.LoadCar(bserv.Blockstore(), buf) + ch, err := car.LoadCar(ctx, bserv.Blockstore(), buf) if err != nil { t.Fatal(err) } @@ -63,7 +64,7 @@ func TestRoundtrip(t *testing.T) { bs := bserv.Blockstore() for _, nd := range []format.Node{a, b, c, nd1, nd2, nd3} { - has, err := bs.Has(nd.Cid()) + has, err := bs.Has(ctx, nd.Cid()) if err != nil { t.Fatal(err) } diff --git a/ipld/car/selectivecar.go b/ipld/car/selectivecar.go index 9b5bd8cef..14c5b05ab 100644 --- a/ipld/car/selectivecar.go +++ b/ipld/car/selectivecar.go @@ -140,7 +140,7 @@ func (sc SelectiveCarPrepared) Cids() []cid.Cid { // Dump writes the car file as quickly as possible based on information already // collected -func (sc SelectiveCarPrepared) Dump(w io.Writer) error { +func (sc SelectiveCarPrepared) Dump(ctx context.Context, w io.Writer) error { offset, err := HeaderSize(&sc.header) if err != nil { return fmt.Errorf("failed to size car header: %s", err) @@ -149,7 +149,7 @@ func (sc SelectiveCarPrepared) Dump(w io.Writer) error { return fmt.Errorf("failed to write car header: %s", err) } for _, c := range sc.cids { - blk, err := sc.store.Get(c) + blk, err := sc.store.Get(ctx, c) if err != nil { return err } @@ -223,7 +223,7 @@ func (sct *selectiveCarTraverser) loader(ctx ipld.LinkContext, lnk ipld.Link) (i return nil, errors.New("incorrect link type") } c := cl.Cid - blk, err := sct.sc.store.Get(c) + blk, err := sct.sc.store.Get(ctx.Ctx, c) if err != nil { return nil, err } diff --git a/ipld/car/selectivecar_test.go b/ipld/car/selectivecar_test.go index 387203ff8..59a4c8e1f 100644 --- a/ipld/car/selectivecar_test.go +++ b/ipld/car/selectivecar_test.go @@ -19,6 +19,7 @@ import ( ) func TestRoundtripSelective(t *testing.T) { + ctx := context.Background() sourceBserv := dstest.Bserv() sourceBs := sourceBserv.Blockstore() dserv := merkledag.NewDAGService(sourceBserv) @@ -79,7 +80,7 @@ func TestRoundtripSelective(t *testing.T) { }) require.NoError(t, err) buf2 := new(bytes.Buffer) - err = scp.Dump(buf2) + err = scp.Dump(ctx, buf2) require.NoError(t, err) // verify preparation step correctly assesed length and blocks @@ -94,7 +95,7 @@ func TestRoundtripSelective(t *testing.T) { // readout car and verify contents bserv := dstest.Bserv() - ch, err := car.LoadCar(bserv.Blockstore(), buf) + ch, err := car.LoadCar(ctx, bserv.Blockstore(), buf) require.NoError(t, err) require.Equal(t, len(ch.Roots), 1) @@ -102,13 +103,13 @@ func TestRoundtripSelective(t *testing.T) { bs := bserv.Blockstore() for _, nd := range []format.Node{a, b, nd1, nd2, nd3} { - has, err := bs.Has(nd.Cid()) + has, err := bs.Has(ctx, nd.Cid()) require.NoError(t, err) require.True(t, has) } for _, nd := range []format.Node{c} { - has, err := bs.Has(nd.Cid()) + has, err := bs.Has(ctx, nd.Cid()) require.NoError(t, err) require.False(t, has) } @@ -221,7 +222,7 @@ type countingReadStore struct { count int } -func (rs *countingReadStore) Get(c cid.Cid) (blocks.Block, error) { +func (rs *countingReadStore) Get(ctx context.Context, c cid.Cid) (blocks.Block, error) { rs.count++ - return rs.bs.Get(c) + return rs.bs.Get(ctx, c) } From 6226033d34f29554e4a427e41d0a353faae66473 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Fri, 10 Dec 2021 19:09:42 -0500 Subject: [PATCH 3620/3817] Update v2 to context datastores (#275) This commit was moved from ipld/go-car@be2525f6bf2d9d9eda818b41cd275521b3ddca9b --- ipld/car/v2/bench_test.go | 3 +- ipld/car/v2/blockstore/bench_test.go | 3 +- ipld/car/v2/blockstore/example_test.go | 16 +-- ipld/car/v2/blockstore/readonly.go | 12 +-- ipld/car/v2/blockstore/readonly_test.go | 30 +++--- ipld/car/v2/blockstore/readwrite.go | 20 ++-- ipld/car/v2/blockstore/readwrite_test.go | 120 +++++++++++++---------- ipld/car/v2/example_test.go | 3 +- ipld/car/v2/internal/carv1/car.go | 21 ++-- ipld/car/v2/internal/carv1/car_test.go | 2 +- 10 files changed, 130 insertions(+), 100 deletions(-) diff --git a/ipld/car/v2/bench_test.go b/ipld/car/v2/bench_test.go index 15ae0c24f..6e7669359 100644 --- a/ipld/car/v2/bench_test.go +++ b/ipld/car/v2/bench_test.go @@ -1,6 +1,7 @@ package car_test import ( + "context" "io" "math/rand" "os" @@ -139,7 +140,7 @@ func generateRandomCarV2File(b *testing.B, path string, minTotalBlockSize int) { } blk := merkledag.NewRawNode(buf) - if err := bs.Put(blk); err != nil { + if err := bs.Put(context.TODO(), blk); err != nil { b.Fatal(err) } totalBlockSize += size diff --git a/ipld/car/v2/blockstore/bench_test.go b/ipld/car/v2/blockstore/bench_test.go index 644acbe95..57544b967 100644 --- a/ipld/car/v2/blockstore/bench_test.go +++ b/ipld/car/v2/blockstore/bench_test.go @@ -1,6 +1,7 @@ package blockstore_test import ( + "context" "io" mathrand "math/rand" "os" @@ -65,7 +66,7 @@ func BenchmarkOpenReadOnlyV1(b *testing.B) { } for _, c := range shuffledCIDs { - _, err := bs.Get(c) + _, err := bs.Get(context.TODO(), c) if err != nil { b.Fatal(err) } diff --git a/ipld/car/v2/blockstore/example_test.go b/ipld/car/v2/blockstore/example_test.go index 7e826addb..198443c9d 100644 --- a/ipld/car/v2/blockstore/example_test.go +++ b/ipld/car/v2/blockstore/example_test.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "os" "path/filepath" + "time" blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" @@ -57,7 +58,7 @@ func ExampleOpenReadOnly() { cancel() break } - size, err := robs.GetSize(k) + size, err := robs.GetSize(context.TODO(), k) if err != nil { panic(err) } @@ -78,6 +79,9 @@ func ExampleOpenReadOnly() { // ExampleOpenReadWrite creates a read-write blockstore and puts func ExampleOpenReadWrite() { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + thisBlock := merkledag.NewRawNode([]byte("fish")).Block thatBlock := merkledag.NewRawNode([]byte("lobster")).Block andTheOtherBlock := merkledag.NewRawNode([]byte("barreleye")).Block @@ -96,13 +100,13 @@ func ExampleOpenReadWrite() { // Put all blocks onto the blockstore. blocks := []blocks.Block{thisBlock, thatBlock} - if err := rwbs.PutMany(blocks); err != nil { + if err := rwbs.PutMany(ctx, blocks); err != nil { panic(err) } fmt.Printf("Successfully wrote %v blocks into the blockstore.\n", len(blocks)) // Any blocks put can be read back using the same blockstore instance. - block, err := rwbs.Get(thatBlock.Cid()) + block, err := rwbs.Get(ctx, thatBlock.Cid()) if err != nil { panic(err) } @@ -122,13 +126,13 @@ func ExampleOpenReadWrite() { } // Put another block, appending it to the set of blocks that are written previously. - if err := resumedRwbos.Put(andTheOtherBlock); err != nil { + if err := resumedRwbos.Put(ctx, andTheOtherBlock); err != nil { panic(err) } // Read back the the block put before resumption. // Blocks previously put are present. - block, err = resumedRwbos.Get(thatBlock.Cid()) + block, err = resumedRwbos.Get(ctx, thatBlock.Cid()) if err != nil { panic(err) } @@ -136,7 +140,7 @@ func ExampleOpenReadWrite() { // Put an additional block to the CAR. // Blocks put after resumption are also present. - block, err = resumedRwbos.Get(andTheOtherBlock.Cid()) + block, err = resumedRwbos.Get(ctx, andTheOtherBlock.Cid()) if err != nil { panic(err) } diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index e25f51251..31636d4e4 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -186,13 +186,13 @@ func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) { } // DeleteBlock is unsupported and always errors. -func (b *ReadOnly) DeleteBlock(_ cid.Cid) error { +func (b *ReadOnly) DeleteBlock(_ context.Context, _ cid.Cid) error { return errReadOnly } // Has indicates if the store contains a block that corresponds to the given key. // This function always returns true for any given key with multihash.IDENTITY code. -func (b *ReadOnly) Has(key cid.Cid) (bool, error) { +func (b *ReadOnly) Has(ctx context.Context, key cid.Cid) (bool, error) { // Check if the given CID has multihash.IDENTITY code // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. if _, ok, err := isIdentity(key); err != nil { @@ -241,7 +241,7 @@ func (b *ReadOnly) Has(key cid.Cid) (bool, error) { // Get gets a block corresponding to the given key. // This API will always return true if the given key has multihash.IDENTITY code. -func (b *ReadOnly) Get(key cid.Cid) (blocks.Block, error) { +func (b *ReadOnly) Get(ctx context.Context, key cid.Cid) (blocks.Block, error) { // Check if the given CID has multihash.IDENTITY code // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. if digest, ok, err := isIdentity(key); err != nil { @@ -293,7 +293,7 @@ func (b *ReadOnly) Get(key cid.Cid) (blocks.Block, error) { } // GetSize gets the size of an item corresponding to the given key. -func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { +func (b *ReadOnly) GetSize(ctx context.Context, key cid.Cid) (int, error) { // Check if the given CID has multihash.IDENTITY code // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. if digest, ok, err := isIdentity(key); err != nil { @@ -361,12 +361,12 @@ func isIdentity(key cid.Cid) (digest []byte, ok bool, err error) { } // Put is not supported and always returns an error. -func (b *ReadOnly) Put(blocks.Block) error { +func (b *ReadOnly) Put(context.Context, blocks.Block) error { return errReadOnly } // PutMany is not supported and always returns an error. -func (b *ReadOnly) PutMany([]blocks.Block) error { +func (b *ReadOnly) PutMany(context.Context, []blocks.Block) error { return errReadOnly } diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 4922acd1b..0ac2b7d16 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -24,7 +24,7 @@ func TestReadOnlyGetReturnsBlockstoreNotFoundWhenCidDoesNotExist(t *testing.T) { nonExistingKey := merkledag.NewRawNode([]byte("lobstermuncher")).Block.Cid() // Assert blockstore API returns blockstore.ErrNotFound - gotBlock, err := subject.Get(nonExistingKey) + gotBlock, err := subject.Get(context.TODO(), nonExistingKey) require.Equal(t, blockstore.ErrNotFound, err) require.Nil(t, gotBlock) } @@ -58,6 +58,7 @@ func TestReadOnly(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + ctx := context.TODO() subject, err := OpenReadOnly(tt.v1OrV2path, tt.opts...) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, subject.Close()) }) @@ -87,25 +88,25 @@ func TestReadOnly(t *testing.T) { wantCids = append(wantCids, key) // Assert blockstore contains key. - has, err := subject.Has(key) + has, err := subject.Has(ctx, key) require.NoError(t, err) require.True(t, has) // Assert size matches block raw data length. - gotSize, err := subject.GetSize(key) + gotSize, err := subject.GetSize(ctx, key) wantSize := len(wantBlock.RawData()) require.NoError(t, err) require.Equal(t, wantSize, gotSize) // Assert block itself matches v1 payload block. - gotBlock, err := subject.Get(key) + gotBlock, err := subject.Get(ctx, key) require.NoError(t, err) require.Equal(t, wantBlock, gotBlock) // Assert write operations error - require.Error(t, subject.Put(wantBlock)) - require.Error(t, subject.PutMany([]blocks.Block{wantBlock})) - require.Error(t, subject.DeleteBlock(key)) + require.Error(t, subject.Put(ctx, wantBlock)) + require.Error(t, subject.PutMany(ctx, []blocks.Block{wantBlock})) + require.Error(t, subject.DeleteBlock(ctx, key)) } ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) @@ -239,15 +240,16 @@ func newV1Reader(r io.Reader, zeroLenSectionAsEOF bool) (*carv1.CarReader, error func TestReadOnlyErrorAfterClose(t *testing.T) { bs, err := OpenReadOnly("../testdata/sample-v1.car") + ctx := context.TODO() require.NoError(t, err) roots, err := bs.Roots() require.NoError(t, err) - _, err = bs.Has(roots[0]) + _, err = bs.Has(ctx, roots[0]) require.NoError(t, err) - _, err = bs.Get(roots[0]) + _, err = bs.Get(ctx, roots[0]) require.NoError(t, err) - _, err = bs.GetSize(roots[0]) + _, err = bs.GetSize(ctx, roots[0]) require.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) @@ -259,11 +261,11 @@ func TestReadOnlyErrorAfterClose(t *testing.T) { _, err = bs.Roots() require.Error(t, err) - _, err = bs.Has(roots[0]) + _, err = bs.Has(ctx, roots[0]) require.Error(t, err) - _, err = bs.Get(roots[0]) + _, err = bs.Get(ctx, roots[0]) require.Error(t, err) - _, err = bs.GetSize(roots[0]) + _, err = bs.GetSize(ctx, roots[0]) require.Error(t, err) _, err = bs.AllKeysChan(ctx) require.Error(t, err) @@ -293,7 +295,7 @@ func TestNewReadOnly_CarV1WithoutIndexWorksAsExpected(t *testing.T) { require.NoError(t, err) // Require that the block is found via ReadOnly API and contetns are as expected. - gotBlock, err := subject.Get(wantBlock.Cid()) + gotBlock, err := subject.Get(context.TODO(), wantBlock.Cid()) require.NoError(t, err) require.Equal(t, wantBlock, gotBlock) } diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 4f288fa0e..8b2ca90d5 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -279,14 +279,14 @@ func (b *ReadWrite) unfinalize() error { } // Put puts a given block to the underlying datastore -func (b *ReadWrite) Put(blk blocks.Block) error { +func (b *ReadWrite) Put(ctx context.Context, blk blocks.Block) error { // PutMany already checks b.ronly.closed. - return b.PutMany([]blocks.Block{blk}) + return b.PutMany(ctx, []blocks.Block{blk}) } // PutMany puts a slice of blocks at the same time using batching // capabilities of the underlying datastore whenever possible. -func (b *ReadWrite) PutMany(blks []blocks.Block) error { +func (b *ReadWrite) PutMany(ctx context.Context, blks []blocks.Block) error { b.ronly.mu.Lock() defer b.ronly.mu.Unlock() @@ -393,19 +393,19 @@ func (b *ReadWrite) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return b.ronly.AllKeysChan(ctx) } -func (b *ReadWrite) Has(key cid.Cid) (bool, error) { - return b.ronly.Has(key) +func (b *ReadWrite) Has(ctx context.Context, key cid.Cid) (bool, error) { + return b.ronly.Has(ctx, key) } -func (b *ReadWrite) Get(key cid.Cid) (blocks.Block, error) { - return b.ronly.Get(key) +func (b *ReadWrite) Get(ctx context.Context, key cid.Cid) (blocks.Block, error) { + return b.ronly.Get(ctx, key) } -func (b *ReadWrite) GetSize(key cid.Cid) (int, error) { - return b.ronly.GetSize(key) +func (b *ReadWrite) GetSize(ctx context.Context, key cid.Cid) (int, error) { + return b.ronly.GetSize(ctx, key) } -func (b *ReadWrite) DeleteBlock(_ cid.Cid) error { +func (b *ReadWrite) DeleteBlock(_ context.Context, _ cid.Cid) error { return fmt.Errorf("ReadWrite blockstore does not support deleting blocks") } diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index d58b6de3c..90f5bb536 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -42,7 +42,7 @@ func TestReadWriteGetReturnsBlockstoreNotFoundWhenCidDoesNotExist(t *testing.T) nonExistingKey := merkledag.NewRawNode([]byte("undadasea")).Block.Cid() // Assert blockstore API returns blockstore.ErrNotFound - gotBlock, err := subject.Get(nonExistingKey) + gotBlock, err := subject.Get(context.TODO(), nonExistingKey) require.Equal(t, ipfsblockstore.ErrNotFound, err) require.Nil(t, gotBlock) } @@ -71,13 +71,13 @@ func TestBlockstore(t *testing.T) { } require.NoError(t, err) - err = ingester.Put(b) + err = ingester.Put(ctx, b) require.NoError(t, err) cids = append(cids, b.Cid()) // try reading a random one: candidate := cids[rng.Intn(len(cids))] - if has, err := ingester.Has(candidate); !has || err != nil { + if has, err := ingester.Has(ctx, candidate); !has || err != nil { t.Fatalf("expected to find %s but didn't: %s", candidate, err) } @@ -89,7 +89,7 @@ func TestBlockstore(t *testing.T) { } for _, c := range cids { - b, err := ingester.Get(c) + b, err := ingester.Get(ctx, c) require.NoError(t, err) if !b.Cid().Equals(c) { t.Fatal("wrong item returned") @@ -106,7 +106,7 @@ func TestBlockstore(t *testing.T) { require.NoError(t, err) numKeysCh := 0 for c := range allKeysCh { - b, err := robs.Get(c) + b, err := robs.Get(ctx, c) require.NoError(t, err) if !b.Cid().Equals(c) { t.Fatal("wrong item returned") @@ -117,7 +117,7 @@ func TestBlockstore(t *testing.T) { require.Equal(t, expectedCidCount, numKeysCh, "AllKeysChan returned an unexpected amount of keys; expected %v but got %v", expectedCidCount, numKeysCh) for _, c := range cids { - b, err := robs.Get(c) + b, err := robs.Get(ctx, c) require.NoError(t, err) if !b.Cid().Equals(c) { t.Fatal("wrong item returned") @@ -127,6 +127,8 @@ func TestBlockstore(t *testing.T) { func TestBlockstorePutSameHashes(t *testing.T) { tdir := t.TempDir() + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() // This blockstore allows duplicate puts, // and identifies by multihash as per the default. @@ -209,26 +211,26 @@ func TestBlockstorePutSameHashes(t *testing.T) { // Has should never error here. // The first block should be missing. // Others might not, given the duplicate hashes. - has, err := bs.Has(block.Cid()) + has, err := bs.Has(ctx, block.Cid()) require.NoError(t, err) if i == 0 { require.False(t, has) } - err = bs.Put(block) + err = bs.Put(ctx, block) require.NoError(t, err) // Has, Get, and GetSize need to work right after a Put. - has, err = bs.Has(block.Cid()) + has, err = bs.Has(ctx, block.Cid()) require.NoError(t, err) require.True(t, has) - got, err := bs.Get(block.Cid()) + got, err := bs.Get(ctx, block.Cid()) require.NoError(t, err) require.Equal(t, block.Cid(), got.Cid()) require.Equal(t, block.RawData(), got.RawData()) - size, err := bs.GetSize(block.Cid()) + size, err := bs.GetSize(ctx, block.Cid()) require.NoError(t, err) require.Equal(t, len(block.RawData()), size) } @@ -263,6 +265,9 @@ func TestBlockstorePutSameHashes(t *testing.T) { func TestBlockstoreConcurrentUse(t *testing.T) { wbs, err := blockstore.OpenReadWrite(filepath.Join(t.TempDir(), "readwrite.car"), nil) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + require.NoError(t, err) t.Cleanup(func() { wbs.Finalize() }) @@ -285,14 +290,14 @@ func TestBlockstoreConcurrentUse(t *testing.T) { block, err := blocks.NewBlockWithCid(data, c) require.NoError(t, err) - has, err := wbs.Has(block.Cid()) + has, err := wbs.Has(ctx, block.Cid()) require.NoError(t, err) require.False(t, has) - err = wbs.Put(block) + err = wbs.Put(ctx, block) require.NoError(t, err) - got, err := wbs.Get(block.Cid()) + got, err := wbs.Get(ctx, block.Cid()) require.NoError(t, err) require.Equal(t, data, got.RawData()) }() @@ -310,6 +315,9 @@ func (b bufferReaderAt) ReadAt(p []byte, off int64) (int, error) { } func TestBlockstoreNullPadding(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + paddedV1, err := ioutil.ReadFile("../testdata/sample-v1-with-zero-len-section.car") require.NoError(t, err) @@ -320,17 +328,14 @@ func TestBlockstoreNullPadding(t *testing.T) { roots, err := rbs.Roots() require.NoError(t, err) - has, err := rbs.Has(roots[0]) + has, err := rbs.Has(ctx, roots[0]) require.NoError(t, err) require.True(t, has) - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - allKeysCh, err := rbs.AllKeysChan(ctx) require.NoError(t, err) for c := range allKeysCh { - b, err := rbs.Get(c) + b, err := rbs.Get(ctx, c) require.NoError(t, err) if !b.Cid().Equals(c) { t.Fatal("wrong item returned") @@ -339,6 +344,9 @@ func TestBlockstoreNullPadding(t *testing.T) { } func TestBlockstoreResumption(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + v1f, err := os.Open("../testdata/sample-v1.car") require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, v1f.Close()) }) @@ -390,7 +398,7 @@ func TestBlockstoreResumption(t *testing.T) { blockstore.UseWholeCIDs(true)) require.NoError(t, err) } - require.NoError(t, subject.Put(b)) + require.NoError(t, subject.Put(ctx, b)) // With 10% chance test read operations on an resumed read-write blockstore. // We don't test on every put to reduce test runtime. @@ -404,10 +412,10 @@ func TestBlockstoreResumption(t *testing.T) { keysChan, err := subject.AllKeysChan(ctx) require.NoError(t, err) for k := range keysChan { - has, err := subject.Has(k) + has, err := subject.Has(ctx, k) require.NoError(t, err) require.True(t, has) - gotBlock, err := subject.Get(k) + gotBlock, err := subject.Get(ctx, k) require.NoError(t, err) require.Equal(t, wantBlocks[k], gotBlock) gotBlockCountSoFar++ @@ -485,6 +493,9 @@ func TestBlockstoreResumptionIsSupportedOnFinalizedFile(t *testing.T) { } func TestReadWritePanicsOnlyWhenFinalized(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + oneTestBlockCid := oneTestBlockWithCidV1.Cid() anotherTestBlockCid := anotherTestBlockWithCidV0.Cid() wantRoots := []cid.Cid{oneTestBlockCid, anotherTestBlockCid} @@ -493,14 +504,14 @@ func TestReadWritePanicsOnlyWhenFinalized(t *testing.T) { subject, err := blockstore.OpenReadWrite(path, wantRoots) require.NoError(t, err) - require.NoError(t, subject.Put(oneTestBlockWithCidV1)) - require.NoError(t, subject.Put(anotherTestBlockWithCidV0)) + require.NoError(t, subject.Put(ctx, oneTestBlockWithCidV1)) + require.NoError(t, subject.Put(ctx, anotherTestBlockWithCidV0)) - gotBlock, err := subject.Get(oneTestBlockCid) + gotBlock, err := subject.Get(ctx, oneTestBlockCid) require.NoError(t, err) require.Equal(t, oneTestBlockWithCidV1, gotBlock) - gotSize, err := subject.GetSize(oneTestBlockCid) + gotSize, err := subject.GetSize(ctx, oneTestBlockCid) require.NoError(t, err) require.Equal(t, len(oneTestBlockWithCidV1.RawData()), gotSize) @@ -508,13 +519,13 @@ func TestReadWritePanicsOnlyWhenFinalized(t *testing.T) { require.NoError(t, err) require.Equal(t, wantRoots, gotRoots) - has, err := subject.Has(oneTestBlockCid) + has, err := subject.Has(ctx, oneTestBlockCid) require.NoError(t, err) require.True(t, has) subject.HashOnRead(true) // Delete should always error regardless of finalize - require.Error(t, subject.DeleteBlock(oneTestBlockCid)) + require.Error(t, subject.DeleteBlock(ctx, oneTestBlockCid)) require.NoError(t, subject.Finalize()) require.Error(t, subject.Finalize()) @@ -522,21 +533,24 @@ func TestReadWritePanicsOnlyWhenFinalized(t *testing.T) { _, ok := (interface{})(subject).(io.Closer) require.False(t, ok) - _, err = subject.Get(oneTestBlockCid) + _, err = subject.Get(ctx, oneTestBlockCid) require.Error(t, err) - _, err = subject.GetSize(anotherTestBlockCid) + _, err = subject.GetSize(ctx, anotherTestBlockCid) require.Error(t, err) - _, err = subject.Has(anotherTestBlockCid) + _, err = subject.Has(ctx, anotherTestBlockCid) require.Error(t, err) - require.Error(t, subject.Put(oneTestBlockWithCidV1)) - require.Error(t, subject.PutMany([]blocks.Block{anotherTestBlockWithCidV0})) + require.Error(t, subject.Put(ctx, oneTestBlockWithCidV1)) + require.Error(t, subject.PutMany(ctx, []blocks.Block{anotherTestBlockWithCidV0})) _, err = subject.AllKeysChan(context.Background()) require.Error(t, err) - require.Error(t, subject.DeleteBlock(oneTestBlockCid)) + require.Error(t, subject.DeleteBlock(ctx, oneTestBlockCid)) } func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + oneTestBlockCid := oneTestBlockWithCidV1.Cid() anotherTestBlockCid := anotherTestBlockWithCidV0.Cid() WantRoots := []cid.Cid{oneTestBlockCid, anotherTestBlockCid} @@ -550,8 +564,8 @@ func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) { carv2.UseDataPadding(wantCarV1Padding), carv2.UseIndexPadding(wantIndexPadding)) require.NoError(t, err) - require.NoError(t, subject.Put(oneTestBlockWithCidV1)) - require.NoError(t, subject.Put(anotherTestBlockWithCidV0)) + require.NoError(t, subject.Put(ctx, oneTestBlockWithCidV1)) + require.NoError(t, subject.Put(ctx, anotherTestBlockWithCidV0)) require.NoError(t, subject.Finalize()) // Assert CARv2 header contains right offsets. @@ -649,7 +663,7 @@ func TestReadWriteResumptionFromFileWithDifferentCarV1PaddingIsError(t *testing. WantRoots, carv2.UseDataPadding(1413)) require.NoError(t, err) - require.NoError(t, subject.Put(oneTestBlockWithCidV1)) + require.NoError(t, subject.Put(context.TODO(), oneTestBlockWithCidV1)) require.NoError(t, subject.Finalize()) resumingSubject, err := blockstore.OpenReadWrite( @@ -663,6 +677,9 @@ func TestReadWriteResumptionFromFileWithDifferentCarV1PaddingIsError(t *testing. } func TestReadWriteErrorAfterClose(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + root := blocks.NewBlock([]byte("foo")) for _, closeMethod := range []func(*blockstore.ReadWrite){ (*blockstore.ReadWrite).Discard, @@ -672,16 +689,16 @@ func TestReadWriteErrorAfterClose(t *testing.T) { bs, err := blockstore.OpenReadWrite(path, []cid.Cid{root.Cid()}) require.NoError(t, err) - err = bs.Put(root) + err = bs.Put(ctx, root) require.NoError(t, err) roots, err := bs.Roots() require.NoError(t, err) - _, err = bs.Has(roots[0]) + _, err = bs.Has(ctx, roots[0]) require.NoError(t, err) - _, err = bs.Get(roots[0]) + _, err = bs.Get(ctx, roots[0]) require.NoError(t, err) - _, err = bs.GetSize(roots[0]) + _, err = bs.GetSize(ctx, roots[0]) require.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) @@ -693,16 +710,16 @@ func TestReadWriteErrorAfterClose(t *testing.T) { _, err = bs.Roots() require.Error(t, err) - _, err = bs.Has(roots[0]) + _, err = bs.Has(ctx, roots[0]) require.Error(t, err) - _, err = bs.Get(roots[0]) + _, err = bs.Get(ctx, roots[0]) require.Error(t, err) - _, err = bs.GetSize(roots[0]) + _, err = bs.GetSize(ctx, roots[0]) require.Error(t, err) _, err = bs.AllKeysChan(ctx) require.Error(t, err) - err = bs.Put(root) + err = bs.Put(ctx, root) require.Error(t, err) // TODO: test that closing blocks if an AllKeysChan operation is @@ -711,6 +728,9 @@ func TestReadWriteErrorAfterClose(t *testing.T) { } func TestOpenReadWrite_WritesIdentityCIDsWhenOptionIsEnabled(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + path := filepath.Join(t.TempDir(), "readwrite-with-id-enabled.car") subject, err := blockstore.OpenReadWrite(path, []cid.Cid{}, carv2.StoreIdentityCIDs(true)) require.NoError(t, err) @@ -722,14 +742,14 @@ func TestOpenReadWrite_WritesIdentityCIDsWhenOptionIsEnabled(t *testing.T) { idBlock, err := blocks.NewBlockWithCid(data, idCid) require.NoError(t, err) - err = subject.Put(idBlock) + err = subject.Put(ctx, idBlock) require.NoError(t, err) - has, err := subject.Has(idCid) + has, err := subject.Has(ctx, idCid) require.NoError(t, err) require.True(t, has) - gotBlock, err := subject.Get(idCid) + gotBlock, err := subject.Get(ctx, idCid) require.NoError(t, err) require.Equal(t, idBlock, gotBlock) @@ -813,7 +833,7 @@ func TestOpenReadWrite_ErrorsWhenWritingTooLargeOfACid(t *testing.T) { bigBlock, err := blocks.NewBlockWithCid(data, bigCid) require.NoError(t, err) - err = subject.Put(bigBlock) + err = subject.Put(context.TODO(), bigBlock) require.Equal(t, &carv2.ErrCidTooLarge{MaxSize: maxAllowedCidSize, CurrentSize: bigCidLen}, err) } @@ -839,7 +859,7 @@ func TestReadWrite_ReWritingCARv1WithIdentityCidIsIdenticalToOriginalWithOptions if next.Cid().Prefix().MhType == multihash.IDENTITY { idCidCount++ } - err = subject.Put(next) + err = subject.Put(context.TODO(), next) require.NoError(t, err) } require.NotZero(t, idCidCount) diff --git a/ipld/car/v2/example_test.go b/ipld/car/v2/example_test.go index 24223c634..53dfa3491 100644 --- a/ipld/car/v2/example_test.go +++ b/ipld/car/v2/example_test.go @@ -2,6 +2,7 @@ package car_test import ( "bytes" + "context" "fmt" "io" "io/ioutil" @@ -61,7 +62,7 @@ func ExampleWrapV1File() { if err != nil { panic(err) } - fmt.Println(bs.Get(roots[0])) + fmt.Println(bs.Get(context.TODO(), roots[0])) // Output: // Roots: [bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy] diff --git a/ipld/car/v2/internal/carv1/car.go b/ipld/car/v2/internal/carv1/car.go index 6a25e667f..48b7c86be 100644 --- a/ipld/car/v2/internal/carv1/car.go +++ b/ipld/car/v2/internal/carv1/car.go @@ -19,11 +19,11 @@ func init() { } type Store interface { - Put(blocks.Block) error + Put(context.Context, blocks.Block) error } type ReadStore interface { - Get(cid.Cid) (blocks.Block, error) + Get(context.Context, cid.Cid) (blocks.Block, error) } type CarHeader struct { @@ -159,30 +159,31 @@ func (cr *CarReader) Next() (blocks.Block, error) { } type batchStore interface { - PutMany([]blocks.Block) error + PutMany(context.Context, []blocks.Block) error } func LoadCar(s Store, r io.Reader) (*CarHeader, error) { + ctx := context.TODO() cr, err := NewCarReader(r) if err != nil { return nil, err } if bs, ok := s.(batchStore); ok { - return loadCarFast(bs, cr) + return loadCarFast(ctx, bs, cr) } - return loadCarSlow(s, cr) + return loadCarSlow(ctx, s, cr) } -func loadCarFast(s batchStore, cr *CarReader) (*CarHeader, error) { +func loadCarFast(ctx context.Context, s batchStore, cr *CarReader) (*CarHeader, error) { var buf []blocks.Block for { blk, err := cr.Next() if err != nil { if err == io.EOF { if len(buf) > 0 { - if err := s.PutMany(buf); err != nil { + if err := s.PutMany(ctx, buf); err != nil { return nil, err } } @@ -194,7 +195,7 @@ func loadCarFast(s batchStore, cr *CarReader) (*CarHeader, error) { buf = append(buf, blk) if len(buf) > 1000 { - if err := s.PutMany(buf); err != nil { + if err := s.PutMany(ctx, buf); err != nil { return nil, err } buf = buf[:0] @@ -202,7 +203,7 @@ func loadCarFast(s batchStore, cr *CarReader) (*CarHeader, error) { } } -func loadCarSlow(s Store, cr *CarReader) (*CarHeader, error) { +func loadCarSlow(ctx context.Context, s Store, cr *CarReader) (*CarHeader, error) { for { blk, err := cr.Next() if err != nil { @@ -212,7 +213,7 @@ func loadCarSlow(s Store, cr *CarReader) (*CarHeader, error) { return nil, err } - if err := s.Put(blk); err != nil { + if err := s.Put(ctx, blk); err != nil { return nil, err } } diff --git a/ipld/car/v2/internal/carv1/car_test.go b/ipld/car/v2/internal/carv1/car_test.go index 71e06ee93..bdd57573f 100644 --- a/ipld/car/v2/internal/carv1/car_test.go +++ b/ipld/car/v2/internal/carv1/car_test.go @@ -65,7 +65,7 @@ func TestRoundtrip(t *testing.T) { bs := bserv.Blockstore() for _, nd := range []format.Node{a, b, c, nd1, nd2, nd3} { - has, err := bs.Has(nd.Cid()) + has, err := bs.Has(context.TODO(), nd.Cid()) if err != nil { t.Fatal(err) } From a4712fa48461bd23295c8f74481606817b54862d Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 6 Jan 2022 03:06:21 -0800 Subject: [PATCH 3621/3817] make specification of root cid in get-dag command optional (#281) * make specification of root cid in get-dag cmd optional add a test script This commit was moved from ipld/go-car@c9eb0b764cac29b4c70c8290ac87033f3a06a7c5 --- ipld/car/cmd/car/get.go | 35 +++++++++++++++----- ipld/car/cmd/car/testdata/script/get-dag.txt | 6 ++++ 2 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 ipld/car/cmd/car/testdata/script/get-dag.txt diff --git a/ipld/car/cmd/car/get.go b/ipld/car/cmd/car/get.go index 324f050ad..de220c527 100644 --- a/ipld/car/cmd/car/get.go +++ b/ipld/car/cmd/car/get.go @@ -66,27 +66,41 @@ func GetCarBlock(c *cli.Context) error { // GetCarDag is a command to get a dag out of a car func GetCarDag(c *cli.Context) error { - if c.Args().Len() < 3 { - return fmt.Errorf("usage: car get-dag [-s selector] ") + if c.Args().Len() < 2 { + return fmt.Errorf("usage: car get-dag [-s selector] [root cid] ") } - // string to CID for the root of the DAG to extract - rootCid, err := cid.Parse(c.Args().Get(1)) - if err != nil { - return err - } + // if root cid is emitted we'll read it from the root of file.car. + output := c.Args().Get(1) + var rootCid cid.Cid bs, err := blockstore.OpenReadOnly(c.Args().Get(0)) if err != nil { return err } - output := c.Args().Get(2) + if c.Args().Len() == 2 { + roots, err := bs.Roots() + if err != nil { + return err + } + if len(roots) != 1 { + return fmt.Errorf("car file has does not have exactly one root, dag root must be specified explicitly") + } + rootCid = roots[0] + } else { + rootCid, err = cid.Parse(output) + if err != nil { + return err + } + output = c.Args().Get(2) + } + strict := c.Bool("strict") // selector traversal, default to ExploreAllRecursively which only explores the DAG blocks // because we only care about the blocks loaded during the walk, not the nodes matched - sel := selectorParser.CommonSelector_ExploreAllRecursively + sel := selectorParser.CommonSelector_MatchAllRecursively if c.IsSet("selector") { sel, err = selectorParser.ParseJSONSelector(c.String("selector")) if err != nil { @@ -127,6 +141,9 @@ func writeCarV2(rootCid cid.Cid, output string, bs *blockstore.ReadOnly, strict } return nil, err } + if err := outStore.Put(blk); err != nil { + return nil, err + } return bytes.NewBuffer(blk.RawData()), nil } return nil, fmt.Errorf("unknown link type: %T", l) diff --git a/ipld/car/cmd/car/testdata/script/get-dag.txt b/ipld/car/cmd/car/testdata/script/get-dag.txt new file mode 100644 index 000000000..85751a08f --- /dev/null +++ b/ipld/car/cmd/car/testdata/script/get-dag.txt @@ -0,0 +1,6 @@ +env SAMPLE_CID='bafy2bzaceaycv7jhaegckatnncu5yugzkrnzeqsppzegufr35lroxxnsnpspu' +car get-dag ${INPUTS}/sample-v1.car ${SAMPLE_CID} out.car +! stderr . +car list out.car +! stderr . +stdout -count=1 '^bafy2bzaceaycv7jhaegckatnncu5yugzkrnzeqsppzegufr35lroxxnsnpspu' \ No newline at end of file From 18d51e4c219f49e1bd1b73266e08b19a3b0b56cb Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 13 Jan 2022 05:08:49 -0800 Subject: [PATCH 3622/3817] add `car root` command (#283) * add root subcommand This commit was moved from ipld/go-car@3c99491a50b5a4adfbb187b4dba07bb6b37fbb7d --- ipld/car/cmd/car/car.go | 5 ++++ ipld/car/cmd/car/root.go | 30 +++++++++++++++++++++++ ipld/car/cmd/car/testdata/script/root.txt | 15 ++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 ipld/car/cmd/car/root.go create mode 100644 ipld/car/cmd/car/testdata/script/root.txt diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index 43b7307d7..1acb8e14e 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -135,6 +135,11 @@ func main1() int { }, }, }, + { + Name: "root", + Usage: "Get the root CID of a car", + Action: CarRoot, + }, { Name: "verify", Aliases: []string{"v"}, diff --git a/ipld/car/cmd/car/root.go b/ipld/car/cmd/car/root.go new file mode 100644 index 000000000..7e8d5b2c4 --- /dev/null +++ b/ipld/car/cmd/car/root.go @@ -0,0 +1,30 @@ +package main + +import ( + "fmt" + "os" + + carv2 "github.com/ipld/go-car/v2" + "github.com/urfave/cli/v2" +) + +// CarRoot prints the root CID in a car +func CarRoot(c *cli.Context) (err error) { + inStream := os.Stdin + if c.Args().Len() >= 1 { + inStream, err = os.Open(c.Args().First()) + if err != nil { + return err + } + } + + rd, err := carv2.NewBlockReader(inStream) + if err != nil { + return err + } + for _, r := range rd.Roots { + fmt.Printf("%s\n", r.String()) + } + + return nil +} diff --git a/ipld/car/cmd/car/testdata/script/root.txt b/ipld/car/cmd/car/testdata/script/root.txt new file mode 100644 index 000000000..834f3a69d --- /dev/null +++ b/ipld/car/cmd/car/testdata/script/root.txt @@ -0,0 +1,15 @@ +car root ${INPUTS}/sample-v1.car +cmp stdout v1root.txt + +car root ${INPUTS}/sample-wrapped-v2.car +cmp stdout v2root.txt + +stop stdin_test_needs_car_fix +stdin ${INPUTS}/sample-wrapped-v2.car +car root +cmp stdout v2root.txt + +-- v1root.txt -- +bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy +-- v2root.txt -- +bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy \ No newline at end of file From 5c3ecfa907f66837f1ed3bf496c0d310d7c10593 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 20 Jan 2022 14:35:36 -0800 Subject: [PATCH 3623/3817] add `car detach-index list` to list detached index contents (#287) * add `car detach-index list` to list detached index contents Co-authored-by: Masih H. Derkani This commit was moved from ipld/go-car@b2c65c2f4de28dcb1068a0dbf25ceea5e8624a3b --- ipld/car/cmd/car/car.go | 5 +++++ ipld/car/cmd/car/detach.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index 1acb8e14e..094708ba4 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -33,6 +33,11 @@ func main1() int { Name: "detach-index", Usage: "Detach an index to a detached file", Action: DetachCar, + Subcommands: []*cli.Command{{ + Name: "list", + Usage: "List a detached index", + Action: DetachCarList, + }}, }, { Name: "extract", diff --git a/ipld/car/cmd/car/detach.go b/ipld/car/cmd/car/detach.go index 276d73b47..da0c236b5 100644 --- a/ipld/car/cmd/car/detach.go +++ b/ipld/car/cmd/car/detach.go @@ -6,6 +6,8 @@ import ( "os" carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/index" + "github.com/multiformats/go-multihash" "github.com/urfave/cli/v2" ) @@ -33,3 +35,35 @@ func DetachCar(c *cli.Context) error { _, err = io.Copy(outStream, r.IndexReader()) return err } + +// DetachCarList prints a list of what's found in a detached index. +func DetachCarList(c *cli.Context) error { + var err error + + inStream := os.Stdin + if c.Args().Len() >= 1 { + inStream, err = os.Open(c.Args().First()) + if err != nil { + return err + } + defer inStream.Close() + } + + idx, err := index.ReadFrom(inStream) + if err != nil { + return err + } + + if iidx, ok := idx.(index.IterableIndex); ok { + err := iidx.ForEach(func(mh multihash.Multihash, offset uint64) error { + fmt.Printf("%s %d\n", mh, offset) + return nil + }) + if err != nil { + return err + } + return nil + } + + return fmt.Errorf("index of codec %s is not iterable", idx.Codec()) +} From b262c2bdddf6de02ad5b0ef63a20720530f97aa5 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 21 Jan 2022 20:14:36 +1100 Subject: [PATCH 3624/3817] feat: add option to create blockstore that writes a plain CARv1 (#288) Use-case: I only want a CARv1 output but I want to use the blockstore interface. I don't want to have to extract the CARv1 output from a CARv2 afterward. This commit was moved from ipld/go-car@7e10f104f4525a51cd72ba6bed89a60136e0d97d --- ipld/car/v2/blockstore/readwrite.go | 110 +++++++---- ipld/car/v2/blockstore/readwrite_test.go | 171 +++++++++++------- ipld/car/v2/options.go | 1 + ipld/car/v2/testdata/sample-v1-noidentity.car | Bin 0 -> 479743 bytes 4 files changed, 179 insertions(+), 103 deletions(-) create mode 100644 ipld/car/v2/testdata/sample-v1-noidentity.car diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 8b2ca90d5..090633c05 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -40,6 +40,18 @@ type ReadWrite struct { opts carv2.Options } +// WriteAsCarV1 is a write option which makes a CAR blockstore write the output +// as a CARv1 only, with no CARv2 header or index. Indexing is used internally +// during write but is discarded upon finalization. +// +// Note that this option only affects the blockstore, and is ignored by the root +// go-car/v2 package. +func WriteAsCarV1(asCarV1 bool) carv2.Option { + return func(o *carv2.Options) { + o.WriteAsCarV1 = asCarV1 + } +} + // AllowDuplicatePuts is a write option which makes a CAR blockstore not // deduplicate blocks in Put and PutMany. The default is to deduplicate, // which matches the current semantics of go-ipfs-blockstore v1. @@ -122,18 +134,22 @@ func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.Option) (*ReadWri rwbs.header = rwbs.header.WithIndexPadding(p) } - rwbs.dataWriter = internalio.NewOffsetWriter(rwbs.f, int64(rwbs.header.DataOffset)) - v1r := internalio.NewOffsetReadSeeker(rwbs.f, int64(rwbs.header.DataOffset)) + offset := int64(rwbs.header.DataOffset) + if rwbs.opts.WriteAsCarV1 { + offset = 0 + } + rwbs.dataWriter = internalio.NewOffsetWriter(rwbs.f, offset) + v1r := internalio.NewOffsetReadSeeker(rwbs.f, offset) rwbs.ronly.backing = v1r rwbs.ronly.idx = rwbs.idx rwbs.ronly.carv2Closer = rwbs.f if resume { - if err = rwbs.resumeWithRoots(roots); err != nil { + if err = rwbs.resumeWithRoots(!rwbs.opts.WriteAsCarV1, roots); err != nil { return nil, err } } else { - if err = rwbs.initWithRoots(roots); err != nil { + if err = rwbs.initWithRoots(!rwbs.opts.WriteAsCarV1, roots); err != nil { return nil, err } } @@ -141,14 +157,16 @@ func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.Option) (*ReadWri return rwbs, nil } -func (b *ReadWrite) initWithRoots(roots []cid.Cid) error { - if _, err := b.f.WriteAt(carv2.Pragma, 0); err != nil { - return err +func (b *ReadWrite) initWithRoots(v2 bool, roots []cid.Cid) error { + if v2 { + if _, err := b.f.WriteAt(carv2.Pragma, 0); err != nil { + return err + } } return carv1.WriteHeader(&carv1.CarHeader{Roots: roots, Version: 1}, b.dataWriter) } -func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { +func (b *ReadWrite) resumeWithRoots(v2 bool, roots []cid.Cid) error { // On resumption it is expected that the CARv2 Pragma, and the CARv1 header is successfully written. // Otherwise we cannot resume from the file. // Read pragma to assert if b.f is indeed a CARv2. @@ -158,36 +176,42 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { // Or the write must have failed before pragma was written. return err } - if version != 2 { - // The file is not a CARv2 and we cannot resume from it. + switch { + case version == 1 && !v2: + case version == 2 && v2: + default: + // The file is not the expected version and we cannot resume from it. return fmt.Errorf("cannot resume on CAR file with version %v", version) } - // Check if file was finalized by trying to read the CARv2 header. - // We check because if finalized the CARv1 reader behaviour needs to be adjusted since - // EOF will not signify end of CARv1 payload. i.e. index is most likely present. var headerInFile carv2.Header - _, err = headerInFile.ReadFrom(internalio.NewOffsetReadSeeker(b.f, carv2.PragmaSize)) - - // If reading CARv2 header succeeded, and CARv1 offset in header is not zero then the file is - // most-likely finalized. Check padding and truncate the file to remove index. - // Otherwise, carry on reading the v1 payload at offset determined from b.header. - if err == nil && headerInFile.DataOffset != 0 { - if headerInFile.DataOffset != b.header.DataOffset { - // Assert that the padding on file matches the given WithDataPadding option. - wantPadding := headerInFile.DataOffset - carv2.PragmaSize - carv2.HeaderSize - gotPadding := b.header.DataOffset - carv2.PragmaSize - carv2.HeaderSize - return fmt.Errorf( - "cannot resume from file with mismatched CARv1 offset; "+ - "`WithDataPadding` option must match the padding on file. "+ - "Expected padding value of %v but got %v", wantPadding, gotPadding, - ) - } else if headerInFile.DataSize == 0 { - // If CARv1 size is zero, since CARv1 offset wasn't, then the CARv2 header was - // most-likely partially written. Since we write the header last in Finalize then the - // file most-likely contains the index and we cannot know where it starts, therefore - // can't resume. - return errors.New("corrupt CARv2 header; cannot resume from file") + + if v2 { + // Check if file was finalized by trying to read the CARv2 header. + // We check because if finalized the CARv1 reader behaviour needs to be adjusted since + // EOF will not signify end of CARv1 payload. i.e. index is most likely present. + _, err = headerInFile.ReadFrom(internalio.NewOffsetReadSeeker(b.f, carv2.PragmaSize)) + + // If reading CARv2 header succeeded, and CARv1 offset in header is not zero then the file is + // most-likely finalized. Check padding and truncate the file to remove index. + // Otherwise, carry on reading the v1 payload at offset determined from b.header. + if err == nil && headerInFile.DataOffset != 0 { + if headerInFile.DataOffset != b.header.DataOffset { + // Assert that the padding on file matches the given WithDataPadding option. + wantPadding := headerInFile.DataOffset - carv2.PragmaSize - carv2.HeaderSize + gotPadding := b.header.DataOffset - carv2.PragmaSize - carv2.HeaderSize + return fmt.Errorf( + "cannot resume from file with mismatched CARv1 offset; "+ + "`WithDataPadding` option must match the padding on file. "+ + "Expected padding value of %v but got %v", wantPadding, gotPadding, + ) + } else if headerInFile.DataSize == 0 { + // If CARv1 size is zero, since CARv1 offset wasn't, then the CARv2 header was + // most-likely partially written. Since we write the header last in Finalize then the + // file most-likely contains the index and we cannot know where it starts, therefore + // can't resume. + return errors.New("corrupt CARv2 header; cannot resume from file") + } } } @@ -213,10 +237,13 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { return err } } - // Now that CARv2 header is present on file, clear it to avoid incorrect size and offset in - // header in case blocksotre is closed without finalization and is resumed from. - if err := b.unfinalize(); err != nil { - return fmt.Errorf("could not un-finalize: %w", err) + + if v2 { + // Now that CARv2 header is present on file, clear it to avoid incorrect size and offset in + // header in case blocksotre is closed without finalization and is resumed from. + if err := b.unfinalize(); err != nil { + return fmt.Errorf("could not un-finalize: %w", err) + } } // TODO See how we can reduce duplicate code here. @@ -354,6 +381,13 @@ func (b *ReadWrite) Discard() { // for more efficient subsequent read. // After this call, the blockstore can no longer be used. func (b *ReadWrite) Finalize() error { + if b.opts.WriteAsCarV1 { + // all blocks are already properly written to the CARv1 inner container and there's + // no additional finalization required at the end of the file for a complete v1 + b.ronly.Close() + return nil + } + b.ronly.mu.Lock() defer b.ronly.mu.Unlock() diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 90f5bb536..63f040d14 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -47,81 +47,122 @@ func TestReadWriteGetReturnsBlockstoreNotFoundWhenCidDoesNotExist(t *testing.T) require.Nil(t, gotBlock) } -func TestBlockstore(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() +func TestBlockstoreX(t *testing.T) { + originalCARv1Path := "../testdata/sample-v1.car" + originalCARv1ComparePath := "../testdata/sample-v1-noidentity.car" + originalCARv1ComparePathStat, err := os.Stat(originalCARv1ComparePath) + require.NoError(t, err) + + variants := []struct { + name string + options []carv2.Option + expectedV1StartOffset int64 + }{ + // no options, expect a standard CARv2 with the noidentity inner CARv1 + {"noopt_carv2", []carv2.Option{}, int64(carv2.PragmaSize + carv2.HeaderSize)}, + // option to only write as a CARv1, expect the noidentity inner CARv1 + {"carv1", []carv2.Option{blockstore.WriteAsCarV1(true)}, int64(0)}, + } - f, err := os.Open("../testdata/sample-v1.car") - require.NoError(t, err) - t.Cleanup(func() { assert.NoError(t, f.Close()) }) - r, err := carv1.NewCarReader(f) - require.NoError(t, err) + for _, variant := range variants { + t.Run(variant.name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() - path := filepath.Join(t.TempDir(), "readwrite.car") - ingester, err := blockstore.OpenReadWrite(path, r.Header.Roots) - require.NoError(t, err) - t.Cleanup(func() { ingester.Finalize() }) + f, err := os.Open(originalCARv1Path) + require.NoError(t, err) + t.Cleanup(func() { assert.NoError(t, f.Close()) }) + r, err := carv1.NewCarReader(f) + require.NoError(t, err) - cids := make([]cid.Cid, 0) - var idCidCount int - for { - b, err := r.Next() - if err == io.EOF { - break - } - require.NoError(t, err) + path := filepath.Join(t.TempDir(), fmt.Sprintf("readwrite_%s.car", variant.name)) + ingester, err := blockstore.OpenReadWrite(path, r.Header.Roots, variant.options...) + require.NoError(t, err) + t.Cleanup(func() { ingester.Finalize() }) + + cids := make([]cid.Cid, 0) + var idCidCount int + for { + b, err := r.Next() + if err == io.EOF { + break + } + require.NoError(t, err) - err = ingester.Put(ctx, b) - require.NoError(t, err) - cids = append(cids, b.Cid()) + err = ingester.Put(ctx, b) + require.NoError(t, err) + cids = append(cids, b.Cid()) - // try reading a random one: - candidate := cids[rng.Intn(len(cids))] - if has, err := ingester.Has(ctx, candidate); !has || err != nil { - t.Fatalf("expected to find %s but didn't: %s", candidate, err) - } + // try reading a random one: + candidate := cids[rng.Intn(len(cids))] + if has, err := ingester.Has(ctx, candidate); !has || err != nil { + t.Fatalf("expected to find %s but didn't: %s", candidate, err) + } - dmh, err := multihash.Decode(b.Cid().Hash()) - require.NoError(t, err) - if dmh.Code == multihash.IDENTITY { - idCidCount++ - } - } + dmh, err := multihash.Decode(b.Cid().Hash()) + require.NoError(t, err) + if dmh.Code == multihash.IDENTITY { + idCidCount++ + } + } - for _, c := range cids { - b, err := ingester.Get(ctx, c) - require.NoError(t, err) - if !b.Cid().Equals(c) { - t.Fatal("wrong item returned") - } - } + for _, c := range cids { + b, err := ingester.Get(ctx, c) + require.NoError(t, err) + if !b.Cid().Equals(c) { + t.Fatal("wrong item returned") + } + } - err = ingester.Finalize() - require.NoError(t, err) - robs, err := blockstore.OpenReadOnly(path) - require.NoError(t, err) - t.Cleanup(func() { assert.NoError(t, robs.Close()) }) + err = ingester.Finalize() + require.NoError(t, err) + robs, err := blockstore.OpenReadOnly(path) + require.NoError(t, err) + t.Cleanup(func() { assert.NoError(t, robs.Close()) }) - allKeysCh, err := robs.AllKeysChan(ctx) - require.NoError(t, err) - numKeysCh := 0 - for c := range allKeysCh { - b, err := robs.Get(ctx, c) - require.NoError(t, err) - if !b.Cid().Equals(c) { - t.Fatal("wrong item returned") - } - numKeysCh++ - } - expectedCidCount := len(cids) - idCidCount - require.Equal(t, expectedCidCount, numKeysCh, "AllKeysChan returned an unexpected amount of keys; expected %v but got %v", expectedCidCount, numKeysCh) + allKeysCh, err := robs.AllKeysChan(ctx) + require.NoError(t, err) + numKeysCh := 0 + for c := range allKeysCh { + b, err := robs.Get(ctx, c) + require.NoError(t, err) + if !b.Cid().Equals(c) { + t.Fatal("wrong item returned") + } + numKeysCh++ + } + expectedCidCount := len(cids) - idCidCount + require.Equal(t, expectedCidCount, numKeysCh, "AllKeysChan returned an unexpected amount of keys; expected %v but got %v", expectedCidCount, numKeysCh) - for _, c := range cids { - b, err := robs.Get(ctx, c) - require.NoError(t, err) - if !b.Cid().Equals(c) { - t.Fatal("wrong item returned") - } + for _, c := range cids { + b, err := robs.Get(ctx, c) + require.NoError(t, err) + if !b.Cid().Equals(c) { + t.Fatal("wrong item returned") + } + } + + wrote, err := os.Open(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, wrote.Close()) }) + _, err = wrote.Seek(variant.expectedV1StartOffset, io.SeekStart) + require.NoError(t, err) + hasher := sha512.New() + gotWritten, err := io.Copy(hasher, io.LimitReader(wrote, originalCARv1ComparePathStat.Size())) + require.NoError(t, err) + gotSum := hasher.Sum(nil) + + hasher.Reset() + originalCarV1, err := os.Open(originalCARv1ComparePath) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, originalCarV1.Close()) }) + wantWritten, err := io.Copy(hasher, originalCarV1) + require.NoError(t, err) + wantSum := hasher.Sum(nil) + + require.Equal(t, wantWritten, gotWritten) + require.Equal(t, wantSum, gotSum) + }) } } diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go index 2fb1d1fc4..228b359be 100644 --- a/ipld/car/v2/options.go +++ b/ipld/car/v2/options.go @@ -39,6 +39,7 @@ type Options struct { BlockstoreAllowDuplicatePuts bool BlockstoreUseWholeCIDs bool MaxTraversalLinks uint64 + WriteAsCarV1 bool } // ApplyOptions applies given opts and returns the resulting Options. diff --git a/ipld/car/v2/testdata/sample-v1-noidentity.car b/ipld/car/v2/testdata/sample-v1-noidentity.car new file mode 100644 index 0000000000000000000000000000000000000000..c83ee22146c36880abe6966b3a3c17bec6d2835d GIT binary patch literal 479743 zcmdSBRZtz;(yooW>%`q5XmHoy?ry=|-QC@TySuwXfZ!6`JxGAyhwQW0D%N+C|GW6B z&f>a9O{&Ly-nU1O9+NxI*xts*(ZO$tT8#n-*e+)mgz#PvS&t~k82RzAB9>MjOd+o_4OvAu(tjWzIhi2rX3qJ)6ff&J9kP7mV^XFOuvNS(Jd z{qMJ#FJ(8=_wi8BHQ}u=;<6T|Q=!iO zNagh)J~5HNCL!IMHV1)65=PfX6{g=_2mH!-7ERt++QP$L zF9`?O)_d_m#~buJ~qfQNd<{?*_Wvv1wjvydgi3JwF;2fgnvgXnOaj(8EsnL_~UKh{jg9!E! zc+84I=z?-6Rn5l;4CEwW$VA)=+TrX@(`9fOd&f&m8=xi7@U5BpuZN>~jE^d+nv==6Y~5oF#z9g3{DpJZ9+ zqM&7n$6;!v9Wh$eR%gyd5|mWF1?0UjUad|(!F~szc|(H7>(*YkZsVb5g_^aIQqtT3 z>cGB*FMJcYB@ls{NOg*`EY6~`1oxFO^caW zb9d>NuU_afcpyN=P7H`+JOPVCWllW)JZpV3eKbT#AW<(7sMAE zK>YBc_O2;aFRm35{t<7uWR;p;fkY;ifZ*EjZ&eiFk?u^mJ-f8NrxRqaYZ{?0uZ*FWo zn^7jViqYO&QbyBAS{LLQZ1W|iI(m1Vnx2`cgiq(Av18j}s$TjnE_qiMQ+b#~?rXCm zgP#zVZF#(W2tUTKZ#Jz1%C|t()+?gr1}T~=%8^=MXQNdPD%VV5#9Vk3vjC~To4a8w ztT=1gvu1Vq(a-@|4NgW5ZU7-et0g(BrkCc!`p`=dQ$|00*B$Vj_HC~%SR5x>;%sh2 z>S?S?!1XewedO68u!GBRHu3ESDGG}+NV;{%p?J3V^� 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 Tx_{@>Ah$cBb;X>)D9fct)9`U;$XV@y4$E$ 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

    hRd> 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 z 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ώtL~geALh4$?$m5~zXB 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 z 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 o%$na}VY_pmFJJ9Ocq9-~2#kaV Date: Fri, 21 Jan 2022 10:48:04 +1100 Subject: [PATCH 3625/3817] feat: --version selector for `car create` & update deps This commit was moved from ipld/go-car@497b9e19151bf058a0148a0b499817c998051b68 --- ipld/car/cmd/car/car.go | 5 +++++ ipld/car/cmd/car/create.go | 16 +++++++++++++--- ipld/car/cmd/car/extract.go | 2 +- ipld/car/cmd/car/filter.go | 2 +- ipld/car/cmd/car/get.go | 10 +++++----- ipld/car/cmd/car/index.go | 3 ++- ipld/car/cmd/car/list.go | 2 +- 7 files changed, 28 insertions(+), 12 deletions(-) diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index 094708ba4..d34ce4162 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -27,6 +27,11 @@ func main1() int { Usage: "The car file to write to", TakesFile: true, }, + &cli.IntFlag{ + Name: "version", + Value: 2, + Usage: "Write output as a v1 or v2 format car", + }, }, }, { diff --git a/ipld/car/cmd/car/create.go b/ipld/car/cmd/car/create.go index 497905381..de9f6abd2 100644 --- a/ipld/car/cmd/car/create.go +++ b/ipld/car/cmd/car/create.go @@ -43,7 +43,17 @@ func CreateCar(c *cli.Context) error { } proxyRoot := cid.NewCidV1(uint64(multicodec.DagPb), hash) - cdest, err := blockstore.OpenReadWrite(c.String("file"), []cid.Cid{proxyRoot}) + options := []car.Option{} + switch c.Int("version") { + case 1: + options = []car.Option{blockstore.WriteAsCarV1(true)} + case 2: + // already the default + default: + return fmt.Errorf("invalid CAR version %d", c.Int("version")) + } + + cdest, err := blockstore.OpenReadWrite(c.String("file"), []cid.Cid{proxyRoot}, options...) if err != nil { return err } @@ -69,7 +79,7 @@ func writeFiles(ctx context.Context, bs *blockstore.ReadWrite, paths ...string) if !ok { return nil, fmt.Errorf("not a cidlink") } - blk, err := bs.Get(cl.Cid) + blk, err := bs.Get(ctx, cl.Cid) if err != nil { return nil, err } @@ -86,7 +96,7 @@ func writeFiles(ctx context.Context, bs *blockstore.ReadWrite, paths ...string) if err != nil { return err } - bs.Put(blk) + bs.Put(ctx, blk) return nil }, nil } diff --git a/ipld/car/cmd/car/extract.go b/ipld/car/cmd/car/extract.go index 4a56e1775..76c1173d6 100644 --- a/ipld/car/cmd/car/extract.go +++ b/ipld/car/cmd/car/extract.go @@ -42,7 +42,7 @@ func ExtractCar(c *cli.Context) error { if !ok { return nil, fmt.Errorf("not a cidlink") } - blk, err := bs.Get(cl.Cid) + blk, err := bs.Get(c.Context, cl.Cid) if err != nil { return nil, err } diff --git a/ipld/car/cmd/car/filter.go b/ipld/car/cmd/car/filter.go index 0a0f28937..a76b6bd05 100644 --- a/ipld/car/cmd/car/filter.go +++ b/ipld/car/cmd/car/filter.go @@ -93,7 +93,7 @@ func FilterCar(c *cli.Context) error { return err } if _, ok := cidMap[blk.Cid()]; ok { - if err := bs.Put(blk); err != nil { + if err := bs.Put(c.Context, blk); err != nil { return err } } diff --git a/ipld/car/cmd/car/get.go b/ipld/car/cmd/car/get.go index de220c527..090f0fad3 100644 --- a/ipld/car/cmd/car/get.go +++ b/ipld/car/cmd/car/get.go @@ -46,7 +46,7 @@ func GetCarBlock(c *cli.Context) error { return err } - blk, err := bs.Get(blkCid) + blk, err := bs.Get(c.Context, blkCid) if err != nil { return err } @@ -111,7 +111,7 @@ func GetCarDag(c *cli.Context) error { switch c.Int("version") { case 2: - return writeCarV2(rootCid, output, bs, strict, sel, linkVisitOnlyOnce) + return writeCarV2(c.Context, rootCid, output, bs, strict, sel, linkVisitOnlyOnce) case 1: return writeCarV1(rootCid, output, bs, strict, sel, linkVisitOnlyOnce) default: @@ -119,7 +119,7 @@ func GetCarDag(c *cli.Context) error { } } -func writeCarV2(rootCid cid.Cid, output string, bs *blockstore.ReadOnly, strict bool, sel datamodel.Node, linkVisitOnlyOnce bool) error { +func writeCarV2(ctx context.Context, rootCid cid.Cid, output string, bs *blockstore.ReadOnly, strict bool, sel datamodel.Node, linkVisitOnlyOnce bool) error { _ = os.Remove(output) outStore, err := blockstore.OpenReadWrite(output, []cid.Cid{rootCid}, blockstore.AllowDuplicatePuts(false)) @@ -131,7 +131,7 @@ func writeCarV2(rootCid cid.Cid, output string, bs *blockstore.ReadOnly, strict ls.TrustedStorage = true ls.StorageReadOpener = func(_ linking.LinkContext, l datamodel.Link) (io.Reader, error) { if cl, ok := l.(cidlink.Link); ok { - blk, err := bs.Get(cl.Cid) + blk, err := bs.Get(ctx, cl.Cid) if err != nil { if err == ipfsbs.ErrNotFound { if strict { @@ -141,7 +141,7 @@ func writeCarV2(rootCid cid.Cid, output string, bs *blockstore.ReadOnly, strict } return nil, err } - if err := outStore.Put(blk); err != nil { + if err := outStore.Put(ctx, blk); err != nil { return nil, err } return bytes.NewBuffer(blk.RawData()), nil diff --git a/ipld/car/cmd/car/index.go b/ipld/car/cmd/car/index.go index 153c0b7c9..8fd2f7e87 100644 --- a/ipld/car/cmd/car/index.go +++ b/ipld/car/cmd/car/index.go @@ -156,5 +156,6 @@ func IndexCar(c *cli.Context) error { return err } - return index.WriteTo(idx, outStream) + _, err = index.WriteTo(idx, outStream) + return err } diff --git a/ipld/car/cmd/car/list.go b/ipld/car/cmd/car/list.go index 2912b1d33..62cdb265d 100644 --- a/ipld/car/cmd/car/list.go +++ b/ipld/car/cmd/car/list.go @@ -138,7 +138,7 @@ func listUnixfs(c *cli.Context, outStream io.Writer) error { if !ok { return nil, fmt.Errorf("not a cidlink") } - blk, err := bs.Get(cl.Cid) + blk, err := bs.Get(c.Context, cl.Cid) if err != nil { return nil, err } From bb04114b7fef3c8f004392100c1ce2f60e6d1a1b Mon Sep 17 00:00:00 2001 From: Jorropo Date: Wed, 9 Feb 2022 23:33:40 +0100 Subject: [PATCH 3626/3817] cmd/car: use a better install command in the README This commit was moved from ipld/go-car@d1093d99c93081f51295e1068e3f3fb92f1d804d --- ipld/car/cmd/car/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/car/cmd/car/README.md b/ipld/car/cmd/car/README.md index f3825f0d2..b9a4ca9a8 100644 --- a/ipld/car/cmd/car/README.md +++ b/ipld/car/cmd/car/README.md @@ -29,5 +29,5 @@ COMMANDS: To install the latest version of `car` module, run: ```shell script -go install github.com/ipld/go-car/cmd/car +go install github.com/ipld/go-car/cmd/car@latest ``` From de93c866c318470fb67fbb3e5687e991d38c08f9 Mon Sep 17 00:00:00 2001 From: Shu Shen Date: Wed, 9 Feb 2022 22:07:06 -0800 Subject: [PATCH 3627/3817] chore: improve error message for invalid ipfs paths This commit was moved from ipfs/go-path@5e4e8349c90f7dfcbb97fc550aa721545d4e5ba6 --- path/path.go | 2 +- path/path_test.go | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/path/path.go b/path/path.go index df050008f..bc9065a2a 100644 --- a/path/path.go +++ b/path/path.go @@ -104,7 +104,7 @@ func ParsePath(txt string) (Path, error) { } if len(parts) < 3 { - return "", &pathError{error: fmt.Errorf("path does not begin with '/'"), path: txt} + return "", &pathError{error: fmt.Errorf("invalid ipfs path"), path: txt} } //TODO: make this smarter diff --git a/path/path_test.go b/path/path_test.go index 42cacddf1..2b26a5678 100644 --- a/path/path_test.go +++ b/path/path_test.go @@ -51,6 +51,19 @@ func TestNoComponents(t *testing.T) { } } +func TestInvalidPaths(t *testing.T) { + for _, s := range []string{ + "/ipfs", + "/testfs", + "/", + } { + _, err := ParsePath(s) + if err == nil || !strings.Contains(err.Error(), "invalid ipfs path") || !strings.Contains(err.Error(), s) { + t.Error("wrong error") + } + } +} + func TestIsJustAKey(t *testing.T) { cases := map[string]bool{ "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n": true, From a3031a7b378978315cad9e91361c451b9a2a0d76 Mon Sep 17 00:00:00 2001 From: Shu Shen Date: Wed, 9 Feb 2022 22:11:54 -0800 Subject: [PATCH 3628/3817] chore: update doc to match implementation for CID only paths This commit was moved from ipfs/go-path@9500a344406b9c33831bce44770c78394e0c5824 --- path/path.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/path.go b/path/path.go index bc9065a2a..f49dde110 100644 --- a/path/path.go +++ b/path/path.go @@ -10,7 +10,7 @@ import ( ) // A Path represents an ipfs content path: -// * //path/to/file +// * /path/to/file // * /ipfs/ // * /ipns//path/to/folder // * etc From 2877898ef1b96eff6b0341ef2ba5e9594f8a65c3 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 15 Feb 2022 02:05:10 -0800 Subject: [PATCH 3629/3817] Allow extracton of a raw unixfs file (#284) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allow extracton of a raw unixfs file Co-authored-by: Daniel Martí This commit was moved from ipld/go-car@4cb3d4f649555f072e34d2d175c7173f52cc859c --- ipld/car/cmd/car/extract.go | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/ipld/car/cmd/car/extract.go b/ipld/car/cmd/car/extract.go index 76c1173d6..5ebc2b771 100644 --- a/ipld/car/cmd/car/extract.go +++ b/ipld/car/cmd/car/extract.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "errors" "fmt" "io" "os" @@ -20,6 +21,8 @@ import ( "github.com/urfave/cli/v2" ) +var ErrNotDir = fmt.Errorf("not a directory") + // ExtractCar pulls files and directories out of a car func ExtractCar(c *cli.Context) error { outputDir, err := os.Getwd() @@ -92,7 +95,27 @@ func extractRoot(c *cli.Context, ls *ipld.LinkSystem, root cid.Cid, outputDir st } } if err := extractDir(c, ls, ufn, outputResolvedDir, "/"); err != nil { - return fmt.Errorf("%s: %w", root, err) + if !errors.Is(err, ErrNotDir) { + return fmt.Errorf("%s: %w", root, err) + } + ufsData, err := pbnode.LookupByString("Data") + if err != nil { + return err + } + ufsBytes, err := ufsData.AsBytes() + if err != nil { + return err + } + ufsNode, err := data.DecodeUnixFSData(ufsBytes) + if err != nil { + return err + } + if ufsNode.DataType.Int() == data.Data_File || ufsNode.DataType.Int() == data.Data_Raw { + if err := extractFile(c, ls, pbnode, filepath.Join(outputResolvedDir, "unknown")); err != nil { + return err + } + } + return nil } return nil @@ -208,7 +231,7 @@ func extractDir(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputRoot, ou } return nil } - return fmt.Errorf("not a directory") + return ErrNotDir } func extractFile(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputName string) error { From 3ea7266a648938330afc555c5327fce0e03f6f73 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 25 Feb 2022 10:04:05 +0100 Subject: [PATCH 3630/3817] Resolver: convert to interface. This converts Resolver to an interface and leaves its implementation to the BasicResolver type. This should cause minimal distruption upstream (*Resolver -> Resolver) and opens the door to swap the BasicResolver for custom Resolvers. This commit was moved from ipfs/go-path@289629653f40e30327932a43cc5088071699b70d --- path/resolver/resolver.go | 43 +++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 47dd47075..55c322aaf 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -41,25 +41,42 @@ func (e ErrNoLink) Error() string { return fmt.Sprintf("no link named %q under %s", e.Name, e.Node.String()) } -// Resolver provides path resolution to IPFS +// Resolver provides path resolution to IPFS. +type Resolver interface { + // ResolveToLastNode walks the given path and returns the cid of the + // last block referenced by the path, and the path segments to + // traverse from the final block boundary to the final node within the + // block. + ResolveToLastNode(ctx context.Context, fpath path.Path) (cid.Cid, []string, error) + // ResolvePath fetches the node for given path. It returns the last + // item returned by ResolvePathComponents and the last link traversed + // which can be used to recover the block. + ResolvePath(ctx context.Context, fpath path.Path) (ipld.Node, ipld.Link, error) + // ResolvePathComponents fetches the nodes for each segment of the given path. + // It uses the first path component as a hash (key) of the first node, then + // resolves all other components walking the links via a selector traversal + ResolvePathComponents(ctx context.Context, fpath path.Path) ([]ipld.Node, error) +} + +// BasicResolver implements the Resolver interface. // It references a FetcherFactory, which is uses to resolve nodes. // TODO: now that this is more modular, try to unify this code with the -// the resolvers in namesys -type Resolver struct { +// the resolvers in namesys. +type BasicResolver struct { FetcherFactory fetcher.Factory } // NewBasicResolver constructs a new basic resolver. -func NewBasicResolver(fetcherFactory fetcher.Factory) *Resolver { - return &Resolver{ +func NewBasicResolver(fetcherFactory fetcher.Factory) Resolver { + return &BasicResolver{ FetcherFactory: fetcherFactory, } } -// ResolveToLastNode walks the given path and returns the cid of the last block -// referenced by the path, and the path segments to traverse from the final block boundary to the final node -// within the block. -func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (cid.Cid, []string, error) { +// ResolveToLastNode walks the given path and returns the cid of the last +// block referenced by the path, and the path segments to traverse from the +// final block boundary to the final node within the block. +func (r *BasicResolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (cid.Cid, []string, error) { c, p, err := path.SplitAbsPath(fpath) if err != nil { return cid.Cid{}, nil, err @@ -125,7 +142,7 @@ func (r *Resolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (cid. // // Note: if/when the context is cancelled or expires then if a multi-block ADL node is returned then it may not be // possible to load certain values. -func (r *Resolver) ResolvePath(ctx context.Context, fpath path.Path) (ipld.Node, ipld.Link, error) { +func (r *BasicResolver) ResolvePath(ctx context.Context, fpath path.Path) (ipld.Node, ipld.Link, error) { // validate path if err := fpath.IsValid(); err != nil { return nil, nil, err @@ -162,7 +179,7 @@ func ResolveSingle(ctx context.Context, ds format.NodeGetter, nd format.Node, na // // Note: if/when the context is cancelled or expires then if a multi-block ADL node is returned then it may not be // possible to load certain values. -func (r *Resolver) ResolvePathComponents(ctx context.Context, fpath path.Path) ([]ipld.Node, error) { +func (r *BasicResolver) ResolvePathComponents(ctx context.Context, fpath path.Path) ([]ipld.Node, error) { //lint:ignore SA1019 TODO: replace EventBegin evt := log.EventBegin(ctx, "resolvePathComponents", logging.LoggableMap{"fpath": fpath}) defer evt.Done() @@ -200,7 +217,7 @@ func (r *Resolver) ResolvePathComponents(ctx context.Context, fpath path.Path) ( // // Note: if/when the context is cancelled or expires then if a multi-block ADL node is returned then it may not be // possible to load certain values. -func (r *Resolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names []string) ([]ipld.Node, error) { +func (r *BasicResolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names []string) ([]ipld.Node, error) { //lint:ignore SA1019 TODO: replace EventBegin evt := log.EventBegin(ctx, "resolveLinks", logging.LoggableMap{"names": names}) defer evt.Done() @@ -226,7 +243,7 @@ func (r *Resolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names []stri // Finds nodes matching the selector starting with a cid. Returns the matched nodes, the cid of the block containing // the last node, and the depth of the last node within its block (root is depth 0). -func (r *Resolver) resolveNodes(ctx context.Context, c cid.Cid, sel ipld.Node) ([]ipld.Node, cid.Cid, int, error) { +func (r *BasicResolver) resolveNodes(ctx context.Context, c cid.Cid, sel ipld.Node) ([]ipld.Node, cid.Cid, int, error) { session := r.FetcherFactory.NewSession(ctx) // traverse selector From c0633c10d611c85a351d307fb47f743de9bfeb24 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 3 Mar 2022 16:42:51 +1100 Subject: [PATCH 3631/3817] fix(test): rootless fixture should have no roots, not null roots This commit was moved from ipld/go-car@d2cce98147ead6cd2987bfeb58376d64f4dfb7fe --- ipld/car/v2/testdata/sample-rootless-v42.car | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/car/v2/testdata/sample-rootless-v42.car b/ipld/car/v2/testdata/sample-rootless-v42.car index 846b71a5a..3fda90cf9 100644 --- a/ipld/car/v2/testdata/sample-rootless-v42.car +++ b/ipld/car/v2/testdata/sample-rootless-v42.car @@ -1 +1 @@ -¢erootsögversion* \ No newline at end of file + ¡gversion* \ No newline at end of file From 4547f9ea3f328f3b0ec3dc8262e751a16b60812e Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 7 Apr 2020 19:01:48 +0200 Subject: [PATCH 3632/3817] Use ipld.ErrNotFound for NotFound errors This commit was moved from ipfs/go-ipfs-blockstore@b598a29143df44364db9a1b80ddfc3549f7cdce5 --- blockstore/arc_cache.go | 19 ++++++++++--------- blockstore/arc_cache_test.go | 7 ++++--- blockstore/blockstore.go | 12 +++++------- blockstore/blockstore_test.go | 3 ++- blockstore/bloom_cache.go | 7 ++++--- blockstore/bloom_cache_test.go | 3 ++- 6 files changed, 27 insertions(+), 24 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 5dc7c6ed0..14d6bd4bf 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -8,6 +8,7 @@ import ( lru "github.com/hashicorp/golang-lru" blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" metrics "github.com/ipfs/go-metrics-interface" ) @@ -135,7 +136,7 @@ func (b *arccache) Has(ctx context.Context, k cid.Cid) (bool, error) { func (b *arccache) GetSize(ctx context.Context, k cid.Cid) (int, error) { if !k.Defined() { - return -1, ErrNotFound + return -1, ipld.ErrNotFound{Cid: k} } key := cacheKey(k) @@ -143,7 +144,7 @@ func (b *arccache) GetSize(ctx context.Context, k cid.Cid) (int, error) { if has, blockSize, ok := b.queryCache(key); ok { if !has { // don't have it, return - return -1, ErrNotFound + return -1, ipld.ErrNotFound{Cid: k} } if blockSize >= 0 { // have it and we know the size @@ -156,7 +157,7 @@ func (b *arccache) GetSize(ctx context.Context, k cid.Cid) (int, error) { defer b.unlock(key, false) blockSize, err := b.blockstore.GetSize(ctx, k) - if err == ErrNotFound { + if ipld.IsNotFound(err) { b.cacheHave(key, false) } else if err == nil { b.cacheSize(key, blockSize) @@ -176,7 +177,7 @@ func (b *arccache) View(ctx context.Context, k cid.Cid, callback func([]byte) er } if !k.Defined() { - return ErrNotFound + return ipld.ErrNotFound{Cid: k} } key := cacheKey(k) @@ -184,7 +185,7 @@ func (b *arccache) View(ctx context.Context, k cid.Cid, callback func([]byte) er if has, _, ok := b.queryCache(key); ok && !has { // short circuit if the cache deterministically tells us the item // doesn't exist. - return ErrNotFound + return ipld.ErrNotFound{Cid: k} } b.lock(key, false) @@ -197,7 +198,7 @@ func (b *arccache) View(ctx context.Context, k cid.Cid, callback func([]byte) er cberr = callback(buf) return nil }); err != nil { - if err == ErrNotFound { + if ipld.IsNotFound(err) { b.cacheHave(key, false) } return err @@ -210,20 +211,20 @@ func (b *arccache) View(ctx context.Context, k cid.Cid, callback func([]byte) er func (b *arccache) Get(ctx context.Context, k cid.Cid) (blocks.Block, error) { if !k.Defined() { - return nil, ErrNotFound + return nil, ipld.ErrNotFound{Cid: k} } key := cacheKey(k) if has, _, ok := b.queryCache(key); ok && !has { - return nil, ErrNotFound + return nil, ipld.ErrNotFound{Cid: k} } b.lock(key, false) defer b.unlock(key, false) bl, err := b.blockstore.Get(ctx, k) - if bl == nil && err == ErrNotFound { + if bl == nil && ipld.IsNotFound(err) { b.cacheHave(key, false) } else if bl != nil { b.cacheSize(key, len(bl.RawData())) diff --git a/blockstore/arc_cache_test.go b/blockstore/arc_cache_test.go index 992cd2688..164457d1b 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/arc_cache_test.go @@ -12,6 +12,7 @@ import ( cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" + ipld "github.com/ipfs/go-ipld-format" ) var exampleBlock = blocks.NewBlock([]byte("foo")) @@ -111,7 +112,7 @@ func TestGetFillsCache(t *testing.T) { if has, err := arc.Has(bg, exampleBlock.Cid()); has || err != nil { t.Fatal("has was true but there is no such block") } - if _, err := arc.GetSize(bg, exampleBlock.Cid()); err != ErrNotFound { + if _, err := arc.GetSize(bg, exampleBlock.Cid()); !ipld.IsNotFound(err) { t.Fatal("getsize was true but there is no such block") } @@ -139,7 +140,7 @@ func TestGetAndDeleteFalseShortCircuit(t *testing.T) { trap("get hit datastore", cd, t) - if bl, err := arc.Get(bg, exampleBlock.Cid()); bl != nil || err != ErrNotFound { + if bl, err := arc.Get(bg, exampleBlock.Cid()); bl != nil || !ipld.IsNotFound(err) { t.Fatal("get returned invalid result") } @@ -226,7 +227,7 @@ func TestGetSizeMissingZeroSizeBlock(t *testing.T) { arc.Get(bg, missingBlock.Cid()) trap("has hit datastore", cd, t) - if _, err := arc.GetSize(bg, missingBlock.Cid()); err != ErrNotFound { + if _, err := arc.GetSize(bg, missingBlock.Cid()); !ipld.IsNotFound(err) { t.Fatal("getsize returned invalid result") } } diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 9572f76c0..b0a50e2d7 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -14,6 +14,7 @@ import ( dsns "github.com/ipfs/go-datastore/namespace" dsq "github.com/ipfs/go-datastore/query" dshelp "github.com/ipfs/go-ipfs-ds-help" + ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" uatomic "go.uber.org/atomic" ) @@ -27,9 +28,6 @@ var BlockPrefix = ds.NewKey("blocks") // is different than expected. var ErrHashMismatch = errors.New("block in storage has different hash than requested") -// ErrNotFound is an error returned when a block is not found. -var ErrNotFound = errors.New("blockstore: block not found") - // Blockstore wraps a Datastore block-centered methods and provides a layer // of abstraction which allows to add different caching strategies. type Blockstore interface { @@ -143,12 +141,12 @@ func (bs *blockstore) HashOnRead(enabled bool) { func (bs *blockstore) Get(ctx context.Context, k cid.Cid) (blocks.Block, error) { if !k.Defined() { - log.Error("undefined cid in blockstore") - return nil, ErrNotFound + logger.Error("undefined cid in blockstore") + return nil, ipld.ErrNotFound{Cid: k} } bdata, err := bs.datastore.Get(ctx, dshelp.MultihashToDsKey(k.Hash())) if err == ds.ErrNotFound { - return nil, ErrNotFound + return nil, ipld.ErrNotFound{Cid: k} } if err != nil { return nil, err @@ -206,7 +204,7 @@ func (bs *blockstore) Has(ctx context.Context, k cid.Cid) (bool, error) { func (bs *blockstore) GetSize(ctx context.Context, k cid.Cid) (int, error) { size, err := bs.datastore.GetSize(ctx, dshelp.MultihashToDsKey(k.Hash())) if err == ds.ErrNotFound { - return -1, ErrNotFound + return -1, ipld.ErrNotFound{Cid: k} } return size, err } diff --git a/blockstore/blockstore_test.go b/blockstore/blockstore_test.go index 1ee3341c3..522ed95d3 100644 --- a/blockstore/blockstore_test.go +++ b/blockstore/blockstore_test.go @@ -12,6 +12,7 @@ import ( dsq "github.com/ipfs/go-datastore/query" ds_sync "github.com/ipfs/go-datastore/sync" u "github.com/ipfs/go-ipfs-util" + ipld "github.com/ipfs/go-ipld-format" ) func TestGetWhenKeyNotPresent(t *testing.T) { @@ -30,7 +31,7 @@ func TestGetWhenKeyNotPresent(t *testing.T) { func TestGetWhenKeyIsNil(t *testing.T) { bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) _, err := bs.Get(bg, cid.Cid{}) - if err != ErrNotFound { + if !ipld.IsNotFound(err) { t.Fail() } } diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index e3332ef38..64092f5d1 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -9,6 +9,7 @@ import ( bloom "github.com/ipfs/bbloom" blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" metrics "github.com/ipfs/go-metrics-interface" ) @@ -156,7 +157,7 @@ func (b *bloomcache) Has(ctx context.Context, k cid.Cid) (bool, error) { func (b *bloomcache) GetSize(ctx context.Context, k cid.Cid) (int, error) { if has, ok := b.hasCached(k); ok && !has { - return -1, ErrNotFound + return -1, ipld.ErrNotFound{Cid: k} } return b.blockstore.GetSize(ctx, k) @@ -172,14 +173,14 @@ func (b *bloomcache) View(ctx context.Context, k cid.Cid, callback func([]byte) } if has, ok := b.hasCached(k); ok && !has { - return ErrNotFound + return ipld.ErrNotFound{Cid: k} } return b.viewer.View(ctx, k, callback) } func (b *bloomcache) Get(ctx context.Context, k cid.Cid) (blocks.Block, error) { if has, ok := b.hasCached(k); ok && !has { - return nil, ErrNotFound + return nil, ipld.ErrNotFound{Cid: k} } return b.blockstore.Get(ctx, k) diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 43f747d5e..3c998c551 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -11,6 +11,7 @@ import ( ds "github.com/ipfs/go-datastore" dsq "github.com/ipfs/go-datastore/query" syncds "github.com/ipfs/go-datastore/sync" + ipld "github.com/ipfs/go-ipld-format" ) var bg = context.Background() @@ -65,7 +66,7 @@ func TestPutManyAddsToBloom(t *testing.T) { t.Fatal(err) } blockSize, err = cachedbs.GetSize(bg, block2.Cid()) - if err != nil && err != ErrNotFound { + if err != nil && !ipld.IsNotFound(err) { t.Fatal(err) } if blockSize > -1 || has { From 8dceb79e3cd2b78e85ab4ec8070b3409bd33e91f Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 7 Apr 2020 19:05:18 +0200 Subject: [PATCH 3633/3817] s/log/logger This commit was moved from ipfs/go-ipfs-blockstore@b3ee1d9409119e0ca8962dad77531760b055e873 --- blockstore/arc_cache.go | 3 +++ blockstore/blockstore.go | 6 +++--- blockstore/bloom_cache.go | 8 ++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 14d6bd4bf..2733dfc37 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -114,6 +114,9 @@ func (b *arccache) DeleteBlock(ctx context.Context, k cid.Cid) error { func (b *arccache) Has(ctx context.Context, k cid.Cid) (bool, error) { if !k.Defined() { + logger.Error("undefined cid in arccache") + // Return cache invalid so the call to blockstore happens + // in case of invalid key and correct error is created. return false, nil } diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index b0a50e2d7..61cb780f8 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -19,7 +19,7 @@ import ( uatomic "go.uber.org/atomic" ) -var log = logging.Logger("blockstore") +var logger = logging.Logger("blockstore") // BlockPrefix namespaces blockstore datastores var BlockPrefix = ds.NewKey("blocks") @@ -239,14 +239,14 @@ func (bs *blockstore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return } if e.Error != nil { - log.Errorf("blockstore.AllKeysChan got err: %s", e.Error) + logger.Errorf("blockstore.AllKeysChan got err: %s", e.Error) return } // need to convert to key.Key using key.KeyFromDsKey. bk, err := dshelp.BinaryFromDsKey(ds.RawKey(e.Key)) if err != nil { - log.Warningf("error parsing key from binary: %s", err) + logger.Warningf("error parsing key from binary: %s", err) continue } k := cid.NewCidV1(cid.Raw, bk) diff --git a/blockstore/bloom_cache.go b/blockstore/bloom_cache.go index 64092f5d1..23d14832c 100644 --- a/blockstore/bloom_cache.go +++ b/blockstore/bloom_cache.go @@ -38,9 +38,9 @@ func bloomCached(ctx context.Context, bs Blockstore, bloomSize, hashCount int) ( if err != nil { select { case <-ctx.Done(): - log.Warning("Cache rebuild closed by context finishing: ", err) + logger.Warning("Cache rebuild closed by context finishing: ", err) default: - log.Error(err) + logger.Error(err) } return } @@ -95,7 +95,7 @@ func (b *bloomcache) Wait(ctx context.Context) error { } func (b *bloomcache) build(ctx context.Context) error { - evt := log.EventBegin(ctx, "bloomcache.build") + evt := logger.EventBegin(ctx, "bloomcache.build") defer evt.Done() defer close(b.buildChan) @@ -132,7 +132,7 @@ func (b *bloomcache) DeleteBlock(ctx context.Context, k cid.Cid) error { func (b *bloomcache) hasCached(k cid.Cid) (has bool, ok bool) { b.total.Inc() if !k.Defined() { - log.Error("undefined in bloom cache") + logger.Error("undefined in bloom cache") // Return cache invalid so call to blockstore // in case of invalid key is forwarded deeper return false, false From 8001a94d5b444b7c3e0f0d8c9d77a5c9707fe845 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 7 Apr 2020 23:09:02 +0200 Subject: [PATCH 3634/3817] Use ipld.ErrNotFound instead of ErrNotFound This commit was moved from ipfs/go-blockservice@56521629cb9bfc87208b49abbf9e5b9ae3507d83 --- blockservice/blockservice.go | 15 +++------------ blockservice/blockservice_test.go | 3 ++- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 66905a677..0bb016547 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -5,7 +5,6 @@ package blockservice import ( "context" - "errors" "io" "sync" @@ -13,14 +12,13 @@ import ( cid "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" exchange "github.com/ipfs/go-ipfs-exchange-interface" + ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log/v2" "github.com/ipfs/go-verifcid" ) var log = logging.Logger("blockservice") -var ErrNotFound = errors.New("blockservice: key not found") - // BlockGetter is the common interface shared between blockservice sessions and // the blockservice. type BlockGetter interface { @@ -151,7 +149,7 @@ func (s *blockService) AddBlock(ctx context.Context, o blocks.Block) error { if s.exchange != nil { if err := s.exchange.HasBlock(ctx, o); err != nil { - log.Errorf("HasBlock: %s", err.Error()) + logger.Errorf("HasBlock: %s", err.Error()) } } @@ -230,7 +228,7 @@ func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget fun return block, nil } - if err == blockstore.ErrNotFound && fget != nil { + if ipld.IsNotFound(err) && fget != nil { f := fget() // Don't load the exchange until we have to // TODO be careful checking ErrNotFound. If the underlying @@ -238,9 +236,6 @@ func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget fun log.Debug("Blockservice: Searching bitswap") blk, err := f.GetBlock(ctx, c) if err != nil { - if err == blockstore.ErrNotFound { - return nil, ErrNotFound - } return nil, err } log.Debugf("BlockService.BlockFetched %s", c) @@ -248,10 +243,6 @@ func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget fun } log.Debug("Blockservice GetBlock: Not found") - if err == blockstore.ErrNotFound { - return nil, ErrNotFound - } - return nil, err } diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index 268a7a592..c29f0cfc5 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -11,6 +11,7 @@ import ( butil "github.com/ipfs/go-ipfs-blocksutil" exchange "github.com/ipfs/go-ipfs-exchange-interface" offline "github.com/ipfs/go-ipfs-exchange-offline" + ipld "github.com/ipfs/go-ipld-format" ) func TestWriteThroughWorks(t *testing.T) { @@ -132,7 +133,7 @@ func TestNilExchange(t *testing.T) { bserv := NewWriteThrough(bs, nil) sess := NewSession(ctx, bserv) _, err := sess.GetBlock(ctx, block.Cid()) - if err != ErrNotFound { + if !ipld.IsNotFound(err) { t.Fatal("expected block to not be found") } err = bs.Put(ctx, block) From 00a68a654ea781df6e8927bb86e1bea33938dc67 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 2 Mar 2022 15:34:09 +0100 Subject: [PATCH 3635/3817] s/log/logger This commit was moved from ipfs/go-blockservice@60962f59d0f2ad2b12ed4aa13c6fbf63ccb946c2 --- blockservice/blockservice.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 0bb016547..4249f54c2 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -17,7 +17,7 @@ import ( "github.com/ipfs/go-verifcid" ) -var log = logging.Logger("blockservice") +var logger = logging.Logger("blockservice") // BlockGetter is the common interface shared between blockservice sessions and // the blockservice. @@ -70,7 +70,7 @@ type blockService struct { // NewBlockService creates a BlockService with given datastore instance. func New(bs blockstore.Blockstore, rem exchange.Interface) BlockService { if rem == nil { - log.Debug("blockservice running in local (offline) mode.") + logger.Debug("blockservice running in local (offline) mode.") } return &blockService{ @@ -84,7 +84,7 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) BlockService { // through to the blockstore and are not skipped by cache checks. func NewWriteThrough(bs blockstore.Blockstore, rem exchange.Interface) BlockService { if rem == nil { - log.Debug("blockservice running in local (offline) mode.") + logger.Debug("blockservice running in local (offline) mode.") } return &blockService{ @@ -145,7 +145,7 @@ func (s *blockService) AddBlock(ctx context.Context, o blocks.Block) error { return err } - log.Debugf("BlockService.BlockAdded %s", c) + logger.Debugf("BlockService.BlockAdded %s", c) if s.exchange != nil { if err := s.exchange.HasBlock(ctx, o); err != nil { @@ -191,9 +191,9 @@ func (s *blockService) AddBlocks(ctx context.Context, bs []blocks.Block) error { if s.exchange != nil { for _, o := range toput { - log.Debugf("BlockService.BlockAdded %s", o.Cid()) + logger.Debugf("BlockService.BlockAdded %s", o.Cid()) if err := s.exchange.HasBlock(ctx, o); err != nil { - log.Errorf("HasBlock: %s", err.Error()) + logger.Errorf("HasBlock: %s", err.Error()) } } } @@ -203,7 +203,7 @@ func (s *blockService) AddBlocks(ctx context.Context, bs []blocks.Block) error { // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). func (s *blockService) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) { - log.Debugf("BlockService GetBlock: '%s'", c) + logger.Debugf("BlockService GetBlock: '%s'", c) var f func() exchange.Fetcher if s.exchange != nil { @@ -233,16 +233,16 @@ func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget fun // TODO be careful checking ErrNotFound. If the underlying // implementation changes, this will break. - log.Debug("Blockservice: Searching bitswap") + logger.Debug("Blockservice: Searching bitswap") blk, err := f.GetBlock(ctx, c) if err != nil { return nil, err } - log.Debugf("BlockService.BlockFetched %s", c) + logger.Debugf("BlockService.BlockFetched %s", c) return blk, nil } - log.Debug("Blockservice GetBlock: Not found") + logger.Debug("Blockservice GetBlock: Not found") return nil, err } @@ -279,7 +279,7 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget if err := verifcid.ValidateCid(c); err == nil { ks2 = append(ks2, c) } else { - log.Errorf("unsafe CID (%s) passed to blockService.GetBlocks: %s", c, err) + logger.Errorf("unsafe CID (%s) passed to blockService.GetBlocks: %s", c, err) } } ks = ks2 @@ -306,12 +306,12 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget f := fget() // don't load exchange unless we have to rblocks, err := f.GetBlocks(ctx, misses) if err != nil { - log.Debugf("Error with GetBlocks: %s", err) + logger.Debugf("Error with GetBlocks: %s", err) return } for b := range rblocks { - log.Debugf("BlockService.BlockFetched %s", b.Cid()) + logger.Debugf("BlockService.BlockFetched %s", b.Cid()) select { case out <- b: case <-ctx.Done(): @@ -326,13 +326,13 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget func (s *blockService) DeleteBlock(ctx context.Context, c cid.Cid) error { err := s.blockstore.DeleteBlock(ctx, c) if err == nil { - log.Debugf("BlockService.BlockDeleted %s", c) + logger.Debugf("BlockService.BlockDeleted %s", c) } return err } func (s *blockService) Close() error { - log.Debug("blockservice is shutting down...") + logger.Debug("blockservice is shutting down...") return s.exchange.Close() } From 4ec405c16891a8524e66da7e85396587467d5f53 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 8 Apr 2020 00:01:52 +0200 Subject: [PATCH 3636/3817] Improve NotFound error description This provide a better description when the offline exchange does not find a block. Rather than bubbling a blockstore error, it wraps it and specifies that the block was not found locally (offline). This commit was moved from ipfs/go-ipfs-exchange-offline@84971a95e6ed894af9cdeb239459dcbffc785ac8 --- exchange/offline/offline.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 73622659b..7c5d7c5ea 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -4,11 +4,13 @@ package offline import ( "context" + "fmt" blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" exchange "github.com/ipfs/go-ipfs-exchange-interface" + ipld "github.com/ipfs/go-ipld-format" ) func Exchange(bs blockstore.Blockstore) exchange.Interface { @@ -25,7 +27,11 @@ type offlineExchange struct { // given key. // NB: This function may return before the timeout expires. func (e *offlineExchange) GetBlock(ctx context.Context, k cid.Cid) (blocks.Block, error) { - return e.bs.Get(ctx, k) + blk, err := e.bs.Get(ctx, k) + if ipld.IsNotFound(err) { + return nil, fmt.Errorf("block was not found locally (offline): %w", err) + } + return blk, err } // HasBlock always returns nil. From a5ff8e4f5599068c9dae4f00ea1028c2764667fd Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 8 Apr 2020 00:05:14 +0200 Subject: [PATCH 3637/3817] Improve ErrNotFound Work with ipld.ErrNotFounds. This commit was moved from ipfs/go-merkledag@ca245bf8f977e2e55690fdb365ce593e28d5b2b6 --- ipld/merkledag/dagutils/diffenum_test.go | 2 +- ipld/merkledag/dagutils/utils.go | 6 +++--- ipld/merkledag/merkledag.go | 20 +++++--------------- ipld/merkledag/readonly_test.go | 2 +- 4 files changed, 10 insertions(+), 20 deletions(-) diff --git a/ipld/merkledag/dagutils/diffenum_test.go b/ipld/merkledag/dagutils/diffenum_test.go index 7400d2d31..c8a0a9d60 100644 --- a/ipld/merkledag/dagutils/diffenum_test.go +++ b/ipld/merkledag/dagutils/diffenum_test.go @@ -211,7 +211,7 @@ func TestDiffEnumFail(t *testing.T) { } err := DiffEnumerate(ctx, lgds, nds["a1"].Cid(), nds["a2"].Cid()) - if err != ipld.ErrNotFound { + if !ipld.IsNotFound(err) { t.Fatal("expected err not found") } diff --git a/ipld/merkledag/dagutils/utils.go b/ipld/merkledag/dagutils/utils.go index bc80741cd..3bae57801 100644 --- a/ipld/merkledag/dagutils/utils.go +++ b/ipld/merkledag/dagutils/utils.go @@ -107,7 +107,7 @@ func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.ProtoNode, path if err == dag.ErrLinkNotFound && create != nil { nd = create() err = nil // no longer an error case - } else if err == ipld.ErrNotFound { + } else if ipld.IsNotFound(err) { // try finding it in our source dagstore nd, err = root.GetLinkedProtoNode(ctx, e.src, path[0]) } @@ -170,7 +170,7 @@ func (e *Editor) rmLink(ctx context.Context, root *dag.ProtoNode, path []string) // search for node in both tmp dagstore and source dagstore nd, err := root.GetLinkedProtoNode(ctx, e.tmp, path[0]) - if err == ipld.ErrNotFound { + if ipld.IsNotFound(err) { nd, err = root.GetLinkedProtoNode(ctx, e.src, path[0]) } @@ -217,7 +217,7 @@ func copyDag(ctx context.Context, nd ipld.Node, from, to ipld.DAGService) error for _, lnk := range nd.Links() { child, err := lnk.GetNode(ctx, from) if err != nil { - if err == ipld.ErrNotFound { + if ipld.IsNotFound(err) { // not found means we didnt modify it, and it should // already be in the target datastore continue diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 3ab9b1299..73b06d925 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -79,10 +79,7 @@ func (n *dagService) Get(ctx context.Context, c cid.Cid) (format.Node, error) { b, err := n.Blocks.GetBlock(ctx, c) if err != nil { - if err == bserv.ErrNotFound { - return nil, format.ErrNotFound - } - return nil, fmt.Errorf("failed to get block for %s: %v", c, err) + return nil, err } return legacy.DecodeNode(ctx, b) @@ -127,9 +124,6 @@ func GetLinksDirect(serv format.NodeGetter) GetLinks { return func(ctx context.Context, c cid.Cid) ([]*format.Link, error) { nd, err := serv.Get(ctx, c) if err != nil { - if err == bserv.ErrNotFound { - err = format.ErrNotFound - } return nil, err } return nd.Links(), nil @@ -143,12 +137,8 @@ type sesGetter struct { // Get gets a single node from the DAG. func (sg *sesGetter) Get(ctx context.Context, c cid.Cid) (format.Node, error) { blk, err := sg.bs.GetBlock(ctx, c) - switch err { - case bserv.ErrNotFound: - return nil, format.ErrNotFound - case nil: - // noop - default: + + if err != nil { return nil, err } @@ -358,7 +348,7 @@ func IgnoreErrors() WalkOption { func IgnoreMissing() WalkOption { return func(walkOptions *walkOptions) { walkOptions.addHandler(func(c cid.Cid, err error) error { - if err == format.ErrNotFound { + if format.IsNotFound(err) { return nil } return err @@ -371,7 +361,7 @@ func IgnoreMissing() WalkOption { func OnMissing(callback func(c cid.Cid)) WalkOption { return func(walkOptions *walkOptions) { walkOptions.addHandler(func(c cid.Cid, err error) error { - if err == format.ErrNotFound { + if format.IsNotFound(err) { callback(c) } return err diff --git a/ipld/merkledag/readonly_test.go b/ipld/merkledag/readonly_test.go index 8beb8d50e..9f91c9b04 100644 --- a/ipld/merkledag/readonly_test.go +++ b/ipld/merkledag/readonly_test.go @@ -55,7 +55,7 @@ func TestReadonlyProperties(t *testing.T) { t.Fatal("expected ErrReadOnly") } - if _, err := ro.Get(ctx, cids[0]); err != ipld.ErrNotFound { + if _, err := ro.Get(ctx, cids[0]); !ipld.IsNotFound(err) { t.Fatal("expected ErrNotFound") } if _, err := ro.Get(ctx, cids[3]); err != nil { From 049d13b0e6c67cede58681469dfb54b86bdcbb07 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 8 Apr 2020 00:33:40 +0200 Subject: [PATCH 3638/3817] Use ipld.ErrNotFound This commit was moved from ipfs/go-filestore@54dbf5f297c1667e7ac1bbfbe0cf2537faa1510c --- filestore/filestore.go | 36 +++++++++++++++--------------------- filestore/filestore_test.go | 3 ++- filestore/fsrefstore.go | 6 +++--- filestore/util.go | 4 ++-- 4 files changed, 22 insertions(+), 27 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 6382a6db4..2372a6074 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -16,6 +16,7 @@ import ( dsq "github.com/ipfs/go-datastore/query" blockstore "github.com/ipfs/go-ipfs-blockstore" posinfo "github.com/ipfs/go-ipfs-posinfo" + ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" ) @@ -117,7 +118,7 @@ func (f *Filestore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // ErrNotFound when the block is not stored. func (f *Filestore) DeleteBlock(ctx context.Context, c cid.Cid) error { err1 := f.bs.DeleteBlock(ctx, c) - if err1 != nil && err1 != blockstore.ErrNotFound { + if err1 != nil && !ipld.IsNotFound(err1) { return err1 } @@ -125,45 +126,38 @@ func (f *Filestore) DeleteBlock(ctx context.Context, c cid.Cid) error { // if we successfully removed something from the blockstore, but the // filestore didnt have it, return success - switch err2 { - case nil: - return nil - case blockstore.ErrNotFound: - if err1 == blockstore.ErrNotFound { - return blockstore.ErrNotFound + if ipld.IsNotFound(err2) { + if ipld.IsNotFound(err1) { + return err1 } + // being here means err1 was nil and err2 NotFound. return nil - default: - return err2 } + + return err2 } // Get retrieves the block with the given Cid. It may return // ErrNotFound when the block is not stored. func (f *Filestore) Get(ctx context.Context, c cid.Cid) (blocks.Block, error) { blk, err := f.bs.Get(ctx, c) - switch err { - case nil: - return blk, nil - case blockstore.ErrNotFound: + if ipld.IsNotFound(err) { return f.fm.Get(ctx, c) - default: - return nil, err } + return blk, err } // GetSize returns the size of the requested block. It may return ErrNotFound // when the block is not stored. func (f *Filestore) GetSize(ctx context.Context, c cid.Cid) (int, error) { size, err := f.bs.GetSize(ctx, c) - switch err { - case nil: - return size, nil - case blockstore.ErrNotFound: - return f.fm.GetSize(ctx, c) - default: + if err != nil { + if ipld.IsNotFound(err) { + return f.fm.GetSize(ctx, c) + } return -1, err } + return size, nil } // Has returns true if the block with the given Cid is diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index e3b822cf4..8117a2392 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -13,6 +13,7 @@ import ( ds "github.com/ipfs/go-datastore" blockstore "github.com/ipfs/go-ipfs-blockstore" posinfo "github.com/ipfs/go-ipfs-posinfo" + ipld "github.com/ipfs/go-ipld-format" ) var bg = context.Background() @@ -148,7 +149,7 @@ func TestDeletes(t *testing.T) { deleted := make(map[string]bool) for _, c := range todelete { _, err := fs.Get(bg, c) - if err != blockstore.ErrNotFound { + if !ipld.IsNotFound(err) { t.Fatal("expected blockstore not found error") } deleted[c.KeyString()] = true diff --git a/filestore/fsrefstore.go b/filestore/fsrefstore.go index 9eb2b4316..844acd885 100644 --- a/filestore/fsrefstore.go +++ b/filestore/fsrefstore.go @@ -16,9 +16,9 @@ import ( ds "github.com/ipfs/go-datastore" dsns "github.com/ipfs/go-datastore/namespace" dsq "github.com/ipfs/go-datastore/query" - blockstore "github.com/ipfs/go-ipfs-blockstore" dshelp "github.com/ipfs/go-ipfs-ds-help" posinfo "github.com/ipfs/go-ipfs-posinfo" + ipld "github.com/ipfs/go-ipld-format" mh "github.com/multiformats/go-multihash" ) @@ -103,7 +103,7 @@ func (f *FileManager) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { func (f *FileManager) DeleteBlock(ctx context.Context, c cid.Cid) error { err := f.ds.Delete(ctx, dshelp.MultihashToDsKey(c.Hash())) if err == ds.ErrNotFound { - return blockstore.ErrNotFound + return ipld.ErrNotFound{Cid: c} } return err } @@ -148,7 +148,7 @@ func (f *FileManager) getDataObj(ctx context.Context, m mh.Multihash) (*pb.DataO o, err := f.ds.Get(ctx, dshelp.MultihashToDsKey(m)) switch err { case ds.ErrNotFound: - return nil, blockstore.ErrNotFound + return nil, ipld.ErrNotFound{Cid: cid.NewCidV1(cid.Raw, m)} case nil: // default: diff --git a/filestore/util.go b/filestore/util.go index 4bd1226d3..125b96b04 100644 --- a/filestore/util.go +++ b/filestore/util.go @@ -10,8 +10,8 @@ import ( cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dsq "github.com/ipfs/go-datastore/query" - blockstore "github.com/ipfs/go-ipfs-blockstore" dshelp "github.com/ipfs/go-ipfs-ds-help" + ipld "github.com/ipfs/go-ipld-format" mh "github.com/multiformats/go-multihash" ) @@ -261,7 +261,7 @@ func mkListRes(m mh.Multihash, d *pb.DataObj, err error) *ListRes { status := StatusOk errorMsg := "" if err != nil { - if err == ds.ErrNotFound || err == blockstore.ErrNotFound { + if err == ds.ErrNotFound || ipld.IsNotFound(err) { status = StatusKeyNotFound } else if err, ok := err.(*CorruptReferenceError); ok { status = err.Code From 3f1d60e6c62e8fc20432db83990a10b7af89e6c3 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 18 Mar 2022 00:17:03 +0100 Subject: [PATCH 3639/3817] refactor: follow the happy left practice in Filestore.DeleteBlock This commit was moved from ipfs/go-filestore@f280748ba419477beb2cf93a9fa3e7ebbefe73a3 --- filestore/filestore.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/filestore/filestore.go b/filestore/filestore.go index 2372a6074..8b966f3fa 100644 --- a/filestore/filestore.go +++ b/filestore/filestore.go @@ -123,18 +123,18 @@ func (f *Filestore) DeleteBlock(ctx context.Context, c cid.Cid) error { } err2 := f.fm.DeleteBlock(ctx, c) + // if we successfully removed something from the blockstore, but the // filestore didnt have it, return success + if !ipld.IsNotFound(err2) { + return err2 + } - if ipld.IsNotFound(err2) { - if ipld.IsNotFound(err1) { - return err1 - } - // being here means err1 was nil and err2 NotFound. - return nil + if ipld.IsNotFound(err1) { + return err1 } - return err2 + return nil } // Get retrieves the block with the given Cid. It may return From 2263eca4a44789edb3c0d244c961c12bcb1585ad Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 2 Mar 2022 16:44:11 +0100 Subject: [PATCH 3640/3817] Update tests to use ipld.IsNotFound to check for notfound errors This commit was moved from ipfs/interface-go-ipfs-core@01ee9419a28353cab04979f0791133df9869f30a --- coreiface/tests/block.go | 5 +++-- coreiface/tests/unixfs.go | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 7dbfa4df0..8d0243e7e 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + ipld "github.com/ipfs/go-ipld-format" coreiface "github.com/ipfs/interface-go-ipfs-core" opt "github.com/ipfs/interface-go-ipfs-core/options" "github.com/ipfs/interface-go-ipfs-core/path" @@ -179,7 +180,7 @@ func (tp *TestSuite) TestBlockRm(t *testing.T) { if err == nil { t.Fatal("expected err to exist") } - if !strings.Contains(err.Error(), "blockservice: key not found") { + if !ipld.IsNotFound(err) { t.Errorf("unexpected error; %s", err.Error()) } @@ -187,7 +188,7 @@ func (tp *TestSuite) TestBlockRm(t *testing.T) { if err == nil { t.Fatal("expected err to exist") } - if !strings.Contains(err.Error(), "blockstore: block not found") { + if !strings.Contains(err.Error(), "not found") { t.Errorf("unexpected error; %s", err.Error()) } diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 4273386aa..f47d34d0a 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -5,7 +5,6 @@ import ( "context" "encoding/hex" "fmt" - "github.com/ipfs/interface-go-ipfs-core/path" "io" "io/ioutil" "math" @@ -16,12 +15,15 @@ import ( "sync" "testing" + "github.com/ipfs/interface-go-ipfs-core/path" + coreiface "github.com/ipfs/interface-go-ipfs-core" "github.com/ipfs/interface-go-ipfs-core/options" "github.com/ipfs/go-cid" - "github.com/ipfs/go-ipfs-files" + files "github.com/ipfs/go-ipfs-files" cbor "github.com/ipfs/go-ipld-cbor" + ipld "github.com/ipfs/go-ipld-format" mdag "github.com/ipfs/go-merkledag" "github.com/ipfs/go-unixfs" "github.com/ipfs/go-unixfs/importer/helpers" @@ -576,7 +578,7 @@ func (tp *TestSuite) TestAddHashOnly(t *testing.T) { if err == nil { t.Fatal("expected an error") } - if !strings.Contains(err.Error(), "blockservice: key not found") { + if !ipld.IsNotFound(err) { t.Errorf("unxepected error: %s", err.Error()) } } From 44ec33010423e8a4ee04e19ec5a3059d2273ab53 Mon Sep 17 00:00:00 2001 From: web3-bot <81333946+web3-bot@users.noreply.github.com> Date: Fri, 18 Mar 2022 03:41:51 +0100 Subject: [PATCH 3641/3817] sync: update CI config files (#15) * disable Travis * add version.json file * add .github/workflows/automerge.yml * add .github/workflows/go-test.yml * add .github/workflows/go-check.yml * add .github/workflows/releaser.yml * add .github/workflows/release-check.yml * add .github/workflows/tagpush.yml * fix: field cid is unused (U1000) https://github.com/ipfs/go-pinning-service-http-client/runs/5594929290?check_suite_focus=true#step:10:31 Co-authored-by: web3-bot Co-authored-by: Marcin Rataj This commit was moved from ipfs/go-pinning-service-http-client@014aba06c4d68a3e367f352cdb82ea0eef73cda6 --- pinning/remote/client/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pinning/remote/client/client.go b/pinning/remote/client/client.go index 40b4b09f4..f4799a74d 100644 --- a/pinning/remote/client/client.go +++ b/pinning/remote/client/client.go @@ -3,10 +3,11 @@ package go_pinning_service_http_client import ( "context" "fmt" - "github.com/pkg/errors" "net/http" "time" + "github.com/pkg/errors" + "github.com/ipfs/go-cid" "github.com/ipfs/go-pinning-service-http-client/openapi" "github.com/multiformats/go-multiaddr" @@ -261,7 +262,6 @@ func (c *Client) lsInternal(ctx context.Context, settings *lsSettings) (pinResul // TODO: We should probably make sure there are no duplicates sent type addSettings struct { - cid string name string origins []string meta map[string]string From e9c49a2bc004dc7ef014605bf0882c1ab7ce779f Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 23 Mar 2022 19:21:31 +0100 Subject: [PATCH 3642/3817] Resolver: unexport BasicResover This commit was moved from ipfs/go-path@29ae0276833f231c99258840ec806f06964e082e --- path/resolver/resolver.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 55c322aaf..64778c1ca 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -58,17 +58,17 @@ type Resolver interface { ResolvePathComponents(ctx context.Context, fpath path.Path) ([]ipld.Node, error) } -// BasicResolver implements the Resolver interface. +// basicResolver implements the Resolver interface. // It references a FetcherFactory, which is uses to resolve nodes. // TODO: now that this is more modular, try to unify this code with the // the resolvers in namesys. -type BasicResolver struct { +type basicResolver struct { FetcherFactory fetcher.Factory } // NewBasicResolver constructs a new basic resolver. func NewBasicResolver(fetcherFactory fetcher.Factory) Resolver { - return &BasicResolver{ + return &basicResolver{ FetcherFactory: fetcherFactory, } } @@ -76,7 +76,7 @@ func NewBasicResolver(fetcherFactory fetcher.Factory) Resolver { // ResolveToLastNode walks the given path and returns the cid of the last // block referenced by the path, and the path segments to traverse from the // final block boundary to the final node within the block. -func (r *BasicResolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (cid.Cid, []string, error) { +func (r *basicResolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (cid.Cid, []string, error) { c, p, err := path.SplitAbsPath(fpath) if err != nil { return cid.Cid{}, nil, err @@ -142,7 +142,7 @@ func (r *BasicResolver) ResolveToLastNode(ctx context.Context, fpath path.Path) // // Note: if/when the context is cancelled or expires then if a multi-block ADL node is returned then it may not be // possible to load certain values. -func (r *BasicResolver) ResolvePath(ctx context.Context, fpath path.Path) (ipld.Node, ipld.Link, error) { +func (r *basicResolver) ResolvePath(ctx context.Context, fpath path.Path) (ipld.Node, ipld.Link, error) { // validate path if err := fpath.IsValid(); err != nil { return nil, nil, err @@ -179,7 +179,7 @@ func ResolveSingle(ctx context.Context, ds format.NodeGetter, nd format.Node, na // // Note: if/when the context is cancelled or expires then if a multi-block ADL node is returned then it may not be // possible to load certain values. -func (r *BasicResolver) ResolvePathComponents(ctx context.Context, fpath path.Path) ([]ipld.Node, error) { +func (r *basicResolver) ResolvePathComponents(ctx context.Context, fpath path.Path) ([]ipld.Node, error) { //lint:ignore SA1019 TODO: replace EventBegin evt := log.EventBegin(ctx, "resolvePathComponents", logging.LoggableMap{"fpath": fpath}) defer evt.Done() @@ -217,7 +217,7 @@ func (r *BasicResolver) ResolvePathComponents(ctx context.Context, fpath path.Pa // // Note: if/when the context is cancelled or expires then if a multi-block ADL node is returned then it may not be // possible to load certain values. -func (r *BasicResolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names []string) ([]ipld.Node, error) { +func (r *basicResolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names []string) ([]ipld.Node, error) { //lint:ignore SA1019 TODO: replace EventBegin evt := log.EventBegin(ctx, "resolveLinks", logging.LoggableMap{"names": names}) defer evt.Done() @@ -243,7 +243,7 @@ func (r *BasicResolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names [ // Finds nodes matching the selector starting with a cid. Returns the matched nodes, the cid of the block containing // the last node, and the depth of the last node within its block (root is depth 0). -func (r *BasicResolver) resolveNodes(ctx context.Context, c cid.Cid, sel ipld.Node) ([]ipld.Node, cid.Cid, int, error) { +func (r *basicResolver) resolveNodes(ctx context.Context, c cid.Cid, sel ipld.Node) ([]ipld.Node, cid.Cid, int, error) { session := r.FetcherFactory.NewSession(ctx) // traverse selector From 98f012c64ebf9fa12d0ff8e5a96604656a90e1bc Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 24 Mar 2022 15:34:02 +0000 Subject: [PATCH 3643/3817] Car command supports for `largebytes` nodes (#296) * Car command supports for `largebytes` nodes and partial unixfs file traversals * check copy error This commit was moved from ipld/go-car@867ce4a3ec215a515c419ee9a7e54de086cea978 --- ipld/car/cmd/car/car.go | 2 +- ipld/car/cmd/car/create.go | 2 +- ipld/car/cmd/car/extract.go | 6 +++++- ipld/car/cmd/car/get.go | 16 +++++++++++++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index d34ce4162..90cf9425d 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -130,7 +130,7 @@ func main1() int { }, { Name: "list", - Aliases: []string{"l"}, + Aliases: []string{"l", "ls"}, Usage: "List the CIDs in a car", Action: ListCar, Flags: []cli.Flag{ diff --git a/ipld/car/cmd/car/create.go b/ipld/car/cmd/car/create.go index de9f6abd2..3a76c9131 100644 --- a/ipld/car/cmd/car/create.go +++ b/ipld/car/cmd/car/create.go @@ -117,7 +117,7 @@ func writeFiles(ctx context.Context, bs *blockstore.ReadWrite, paths ...string) // make a directory for the file(s). - root, err := builder.BuildUnixFSDirectory(topLevel, &ls) + root, _, err := builder.BuildUnixFSDirectory(topLevel, &ls) if err != nil { return cid.Undef, nil } diff --git a/ipld/car/cmd/car/extract.go b/ipld/car/cmd/car/extract.go index 5ebc2b771..ae2da8751 100644 --- a/ipld/car/cmd/car/extract.go +++ b/ipld/car/cmd/car/extract.go @@ -239,13 +239,17 @@ func extractFile(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputName st if err != nil { return err } + nlr, err := node.AsLargeBytes() + if err != nil { + return err + } f, err := os.Create(outputName) if err != nil { return err } defer f.Close() - _, err = io.Copy(f, node) + _, err = io.Copy(f, nlr) return err } diff --git a/ipld/car/cmd/car/get.go b/ipld/car/cmd/car/get.go index 090f0fad3..84531b7a6 100644 --- a/ipld/car/cmd/car/get.go +++ b/ipld/car/cmd/car/get.go @@ -17,6 +17,7 @@ import ( "github.com/ipfs/go-cid" ipfsbs "github.com/ipfs/go-ipfs-blockstore" + "github.com/ipfs/go-unixfsnode" "github.com/ipld/go-car" "github.com/ipld/go-car/v2/blockstore" "github.com/ipld/go-ipld-prime/datamodel" @@ -128,6 +129,7 @@ func writeCarV2(ctx context.Context, rootCid cid.Cid, output string, bs *blockst } ls := cidlink.DefaultLinkSystem() + ls.KnownReifiers = map[string]linking.NodeReifier{"unixfs": unixfsnode.Reify} ls.TrustedStorage = true ls.StorageReadOpener = func(_ linking.LinkContext, l datamodel.Link) (io.Reader, error) { if cl, ok := l.(cidlink.Link); ok { @@ -176,7 +178,19 @@ func writeCarV2(ctx context.Context, rootCid cid.Cid, output string, bs *blockst return err } - err = traversalProgress.WalkMatching(rootNode, s, func(p traversal.Progress, n datamodel.Node) error { return nil }) + err = traversalProgress.WalkMatching(rootNode, s, func(p traversal.Progress, n datamodel.Node) error { + lb, ok := n.(datamodel.LargeBytesNode) + if ok { + rs, err := lb.AsLargeBytes() + if err == nil { + _, err := io.Copy(io.Discard, rs) + if err != nil { + return err + } + } + } + return nil + }) if err != nil { return err } From f9d0ad972ce79a1fca890370606d585cf49416fe Mon Sep 17 00:00:00 2001 From: godcong Date: Fri, 25 Mar 2022 22:32:10 +0800 Subject: [PATCH 3644/3817] fix: document error (#74) This commit was moved from ipfs/interface-go-ipfs-core@e9a299166898903a08f98e766aa23f452170496a --- coreiface/coreapi.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index aacda0459..894ffb318 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -32,7 +32,7 @@ type CoreAPI interface { // Pin returns an implementation of Pin API Pin() PinAPI - // ObjectAPI returns an implementation of Object API + // Object returns an implementation of Object API Object() ObjectAPI // Dht returns an implementation of Dht API From af325c0139aa9b6dfb222588b7a9b05bd226e4bd Mon Sep 17 00:00:00 2001 From: godcong Date: Fri, 25 Mar 2022 23:45:20 +0800 Subject: [PATCH 3645/3817] fix(publisher): fix garbled code output (#28) This commit was moved from ipfs/go-namesys@ff68a748348b2853e66be031629c826c967cb8b0 --- namesys/publisher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/publisher.go b/namesys/publisher.go index 3b69bce73..1ea9e2145 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -284,7 +284,7 @@ func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec return err } - log.Debugf("Storing ipns entry at: %s", ipnskey) + log.Debugf("Storing ipns entry at: %x", ipnskey) // Store ipns entry at "/ipns/"+h(pubkey) return r.PutValue(ctx, ipnskey, data) } From 548e3d4298370c022c6e79dbe5614d8c80ea90b9 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Thu, 31 Mar 2022 23:55:17 +0200 Subject: [PATCH 3646/3817] fix: use IPLD.ErrNotFound instead of string comparison in tests This commit was moved from ipfs/interface-go-ipfs-core@03f4e9cca18f0882ae13f718d4b3e18ef1f361ca --- coreiface/tests/block.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 8d0243e7e..87fa90b65 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -188,7 +188,7 @@ func (tp *TestSuite) TestBlockRm(t *testing.T) { if err == nil { t.Fatal("expected err to exist") } - if !strings.Contains(err.Error(), "not found") { + if !ipld.IsNotFound(err) { t.Errorf("unexpected error; %s", err.Error()) } From f249ce14d91553d0e1d23b77040a6162fead4760 Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Wed, 6 Apr 2022 15:00:19 +0200 Subject: [PATCH 3647/3817] fix: this value of c is never used This commit was moved from ipfs/go-merkledag@ad31a3a8dcab389d15f703012be91c75ced45a25 --- ipld/merkledag/coding.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index a9a1189eb..bdc1dcde2 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -49,8 +49,7 @@ func fromImmutableNode(encoded *immutableProtoNode) *ProtoNode { if next.FieldName().Exists() { name = next.FieldName().Must().String() } - c := cid.Undef - c = next.FieldHash().Link().(cidlink.Link).Cid + c := next.FieldHash().Link().(cidlink.Link).Cid size := uint64(0) if next.FieldTsize().Exists() { size = uint64(next.FieldTsize().Must().Int()) From 948dcb33278f7ee434fab22af624941444cd2466 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Fri, 8 Apr 2022 10:56:07 -0400 Subject: [PATCH 3648/3817] feat: add tracing (#30) * feat: add tracing * make span names and attributes more consistent This commit was moved from ipfs/go-namesys@bf4b3cffd9bdc0fb668bb6879f17070128145798 --- namesys/base.go | 6 ++++++ namesys/dns.go | 17 +++++++++++++++++ namesys/namesys.go | 21 +++++++++++++++++++++ namesys/publisher.go | 14 ++++++++++++++ namesys/republisher/repub.go | 13 ++++++++++++- namesys/resolve/resolve.go | 4 ++++ namesys/routing.go | 12 ++++++++++++ namesys/tracing.go | 13 +++++++++++++ 8 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 namesys/tracing.go diff --git a/namesys/base.go b/namesys/base.go index 27cc38f88..5bf64c0e3 100644 --- a/namesys/base.go +++ b/namesys/base.go @@ -40,12 +40,18 @@ func resolve(ctx context.Context, r resolver, name string, options opts.ResolveO } func resolveAsync(ctx context.Context, r resolver, name string, options opts.ResolveOpts) <-chan Result { + ctx, span := StartSpan(ctx, "ResolveAsync") + defer span.End() + resCh := r.resolveOnceAsync(ctx, name, options) depth := options.Depth outCh := make(chan Result, 1) go func() { defer close(outCh) + ctx, span := StartSpan(ctx, "ResolveAsync.Worker") + defer span.End() + var subCh <-chan Result var cancelSub context.CancelFunc defer func() { diff --git a/namesys/dns.go b/namesys/dns.go index 139835617..ba1906162 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -11,6 +11,8 @@ import ( path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" dns "github.com/miekg/dns" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" ) // LookupTXTFunc is a function that lookups TXT record values. @@ -30,11 +32,17 @@ func NewDNSResolver(lookup LookupTXTFunc) *DNSResolver { // Resolve implements Resolver. func (r *DNSResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) { + ctx, span := StartSpan(ctx, "DNSResolver.Resolve") + defer span.End() + return resolve(ctx, r, name, opts.ProcessOpts(options)) } // ResolveAsync implements Resolver. func (r *DNSResolver) ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result { + ctx, span := StartSpan(ctx, "DNSResolver.ResolveAsync") + defer span.End() + return resolveAsync(ctx, r, name, opts.ProcessOpts(options)) } @@ -47,6 +55,9 @@ type lookupRes struct { // TXT records for a given domain name should contain a b58 // encoded multihash. func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { + ctx, span := StartSpan(ctx, "DNSResolver.ResolveOnceAsync") + defer span.End() + var fqdn string out := make(chan onceResult, 1) segments := strings.SplitN(name, "/", 2) @@ -80,6 +91,9 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options go func() { defer close(out) + ctx, span := StartSpan(ctx, "DNSResolver.ResolveOnceAsync.Worker") + defer span.End() + var rootResErr, subResErr error for { select { @@ -131,6 +145,9 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options } func workDomain(ctx context.Context, r *DNSResolver, name string, res chan lookupRes) { + ctx, span := StartSpan(ctx, "DNSResolver.WorkDomain", trace.WithAttributes(attribute.String("Name", name))) + defer span.End() + defer close(res) txt, err := r.lookupTXT(ctx, name) diff --git a/namesys/namesys.go b/namesys/namesys.go index 537f0d1b0..51b5e7096 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -30,6 +30,8 @@ import ( routing "github.com/libp2p/go-libp2p-core/routing" dns "github.com/miekg/dns" madns "github.com/multiformats/go-multiaddr-dns" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" ) // mpns (a multi-protocol NameSystem) implements generic IPFS naming. @@ -134,6 +136,9 @@ const DefaultResolverCacheTTL = time.Minute // Resolve implements Resolver. func (ns *mpns) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) { + ctx, span := StartSpan(ctx, "MPNS.Resolve", trace.WithAttributes(attribute.String("Name", name))) + defer span.End() + if strings.HasPrefix(name, "/ipfs/") { return path.ParsePath(name) } @@ -146,6 +151,9 @@ func (ns *mpns) Resolve(ctx context.Context, name string, options ...opts.Resolv } func (ns *mpns) ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result { + ctx, span := StartSpan(ctx, "MPNS.ResolveAsync", trace.WithAttributes(attribute.String("Name", name))) + defer span.End() + if strings.HasPrefix(name, "/ipfs/") { p, err := path.ParsePath(name) res := make(chan Result, 1) @@ -167,6 +175,9 @@ func (ns *mpns) ResolveAsync(ctx context.Context, name string, options ...opts.R // resolveOnce implements resolver. func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { + ctx, span := StartSpan(ctx, "MPNS.ResolveOnceAsync") + defer span.End() + out := make(chan onceResult, 1) if !strings.HasPrefix(name, ipnsPrefix) { @@ -213,11 +224,14 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. if len(segments) > 3 { p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3]) } + span.SetAttributes(attribute.Bool("CacheHit", true)) + span.RecordError(err) out <- onceResult{value: p, err: err} close(out) return out } + span.SetAttributes(attribute.Bool("CacheHit", false)) if err == nil { res = ns.ipnsResolver @@ -273,18 +287,25 @@ func emitOnceResult(ctx context.Context, outCh chan<- onceResult, r onceResult) // Publish implements Publisher func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error { + ctx, span := StartSpan(ctx, "MPNS.Publish") + defer span.End() return ns.PublishWithEOL(ctx, name, value, time.Now().Add(DefaultRecordEOL)) } func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error { + ctx, span := StartSpan(ctx, "MPNS.PublishWithEOL", trace.WithAttributes(attribute.String("Value", value.String()))) + defer span.End() id, err := peer.IDFromPrivateKey(name) if err != nil { + span.RecordError(err) return err } + span.SetAttributes(attribute.String("ID", id.String())) if err := ns.ipnsPublisher.PublishWithEOL(ctx, name, value, eol); err != nil { // Invalidate the cache. Publishing may _partially_ succeed but // still return an error. ns.cacheInvalidate(string(id)) + span.RecordError(err) return err } ttl := DefaultResolverCacheTTL diff --git a/namesys/publisher.go b/namesys/publisher.go index 1ea9e2145..bf1c46d9d 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -16,6 +16,8 @@ import ( peer "github.com/libp2p/go-libp2p-core/peer" routing "github.com/libp2p/go-libp2p-core/routing" base32 "github.com/whyrusleeping/base32" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" ) const ipnsPrefix = "/ipns/" @@ -191,6 +193,9 @@ func (p *IpnsPublisher) updateRecord(ctx context.Context, k crypto.PrivKey, valu // PublishWithEOL is a temporary stand in for the ipns records implementation // see here for more details: https://github.com/ipfs/specs/tree/master/records func (p *IpnsPublisher) PublishWithEOL(ctx context.Context, k crypto.PrivKey, value path.Path, eol time.Time) error { + ctx, span := StartSpan(ctx, "IpnsPublisher.PublishWithEOL", trace.WithAttributes(attribute.String("Value", value.String()))) + defer span.End() + record, err := p.updateRecord(ctx, k, value, eol) if err != nil { return err @@ -216,6 +221,9 @@ func checkCtxTTL(ctx context.Context) (time.Duration, bool) { // keyed on the ID associated with the provided public key. The public key is // also made available to the routing system so that entries can be verified. func PutRecordToRouting(ctx context.Context, r routing.ValueStore, k crypto.PubKey, entry *pb.IpnsEntry) error { + ctx, span := StartSpan(ctx, "PutRecordToRouting") + defer span.End() + ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -266,6 +274,9 @@ func waitOnErrChan(ctx context.Context, errs chan error) error { // PublishPublicKey stores the given public key in the ValueStore with the // given key. func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk crypto.PubKey) error { + ctx, span := StartSpan(ctx, "PublishPublicKey", trace.WithAttributes(attribute.String("Key", k))) + defer span.End() + log.Debugf("Storing pubkey at: %s", k) pkbytes, err := crypto.MarshalPublicKey(pubk) if err != nil { @@ -279,6 +290,9 @@ func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk // PublishEntry stores the given IpnsEntry in the ValueStore with the given // ipnskey. func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec *pb.IpnsEntry) error { + ctx, span := StartSpan(ctx, "PublishEntry", trace.WithAttributes(attribute.String("IPNSKey", ipnskey))) + defer span.End() + data, err := proto.Marshal(rec) if err != nil { return err diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index 5fefac222..a24e59dff 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -10,6 +10,7 @@ import ( keystore "github.com/ipfs/go-ipfs-keystore" namesys "github.com/ipfs/go-namesys" path "github.com/ipfs/go-path" + "go.opentelemetry.io/otel/attribute" proto "github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-datastore" @@ -93,6 +94,8 @@ func (rp *Republisher) Run(proc goprocess.Process) { func (rp *Republisher) republishEntries(p goprocess.Process) error { ctx, cancel := context.WithCancel(gpctx.OnClosingContext(p)) defer cancel() + ctx, span := namesys.StartSpan(ctx, "Republisher.RepublishEntries") + defer span.End() // TODO: Use rp.ipns.ListPublished(). We can't currently *do* that // because: @@ -125,8 +128,11 @@ func (rp *Republisher) republishEntries(p goprocess.Process) error { } func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) error { + ctx, span := namesys.StartSpan(ctx, "Republisher.RepublishEntry") + defer span.End() id, err := peer.IDFromPrivateKey(priv) if err != nil { + span.RecordError(err) return err } @@ -136,14 +142,17 @@ func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) erro e, err := rp.getLastIPNSEntry(ctx, id) if err != nil { if err == errNoEntry { + span.SetAttributes(attribute.Bool("NoEntry", true)) return nil } + span.RecordError(err) return err } p := path.Path(e.GetValue()) prevEol, err := ipns.GetEOL(e) if err != nil { + span.RecordError(err) return err } @@ -152,7 +161,9 @@ func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) erro if prevEol.After(eol) { eol = prevEol } - return rp.ns.PublishWithEOL(ctx, priv, p, eol) + err = rp.ns.PublishWithEOL(ctx, priv, p, eol) + span.RecordError(err) + return err } func (rp *Republisher) getLastIPNSEntry(ctx context.Context, id peer.ID) (*pb.IpnsEntry, error) { diff --git a/namesys/resolve/resolve.go b/namesys/resolve/resolve.go index 5f1b4eed9..38096593e 100644 --- a/namesys/resolve/resolve.go +++ b/namesys/resolve/resolve.go @@ -7,6 +7,8 @@ import ( "strings" "github.com/ipfs/go-path" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" "github.com/ipfs/go-namesys" ) @@ -18,6 +20,8 @@ var ErrNoNamesys = errors.New( // ResolveIPNS resolves /ipns paths func ResolveIPNS(ctx context.Context, nsys namesys.NameSystem, p path.Path) (path.Path, error) { + ctx, span := namesys.StartSpan(ctx, "ResolveIPNS", trace.WithAttributes(attribute.String("Path", p.String()))) + defer span.End() if strings.HasPrefix(p.String(), "/ipns/") { // TODO(cryptix): we should be able to query the local cache for the path if nsys == nil { diff --git a/namesys/routing.go b/namesys/routing.go index 8bdfe21e6..c73e23ed7 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -16,6 +16,8 @@ import ( routing "github.com/libp2p/go-libp2p-core/routing" dht "github.com/libp2p/go-libp2p-kad-dht" mh "github.com/multiformats/go-multihash" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" ) var log = logging.Logger("namesys") @@ -38,17 +40,24 @@ func NewIpnsResolver(route routing.ValueStore) *IpnsResolver { // Resolve implements Resolver. func (r *IpnsResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) { + ctx, span := StartSpan(ctx, "IpnsResolver.Resolve", trace.WithAttributes(attribute.String("Name", name))) + defer span.End() return resolve(ctx, r, name, opts.ProcessOpts(options)) } // ResolveAsync implements Resolver. func (r *IpnsResolver) ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result { + ctx, span := StartSpan(ctx, "IpnsResolver.ResolveAsync", trace.WithAttributes(attribute.String("Name", name))) + defer span.End() return resolveAsync(ctx, r, name, opts.ProcessOpts(options)) } // resolveOnce implements resolver. Uses the IPFS routing system to // resolve SFS-like names. func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult { + ctx, span := StartSpan(ctx, "IpnsResolver.ResolveOnceAsync", trace.WithAttributes(attribute.String("Name", name))) + defer span.End() + out := make(chan onceResult, 1) log.Debugf("RoutingResolver resolving %s", name) cancel := func() {} @@ -86,6 +95,9 @@ func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, option go func() { defer cancel() defer close(out) + ctx, span := StartSpan(ctx, "IpnsResolver.ResolveOnceAsync.Worker") + defer span.End() + for { select { case val, ok := <-vals: diff --git a/namesys/tracing.go b/namesys/tracing.go new file mode 100644 index 000000000..4ef84294a --- /dev/null +++ b/namesys/tracing.go @@ -0,0 +1,13 @@ +package namesys + +import ( + "context" + "fmt" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" +) + +func StartSpan(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { + return otel.Tracer("go-namesys").Start(ctx, fmt.Sprintf("Namesys.%s", name)) +} From aca3a1839f42a590b5fa7ce30e743232c9336023 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Thu, 21 Apr 2022 12:41:58 -0300 Subject: [PATCH 3649/3817] refactor(block): CIDv1 and BlockPutSettings CidPrefix (#80) * feat(block options): add store codec * refactor: BlockPutSettings.CidPrefix Removes duplicated fields and replaces them with cid.Prefix Codec, MhType and MhLength were already in prefix, and we already return prefix. A lot of duplicated values and code responsible for syncing them did not really need to exist. * test: CIDv1 raw and dag-pb cases * chore: release 0.7.0 Co-authored-by: Marcin Rataj This commit was moved from ipfs/interface-go-ipfs-core@a3374d99028d96a1ef262b81acb385690eb36f97 --- coreiface/options/block.go | 134 ++++++++++++++++++++++++------------- coreiface/tests/block.go | 103 +++++++++++++++++++++++++--- 2 files changed, 181 insertions(+), 56 deletions(-) diff --git a/coreiface/options/block.go b/coreiface/options/block.go index 043dfdea4..130648682 100644 --- a/coreiface/options/block.go +++ b/coreiface/options/block.go @@ -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 } type BlockRmSettings struct { @@ -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) { @@ -87,13 +63,75 @@ 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" + } + + // 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 @@ -101,8 +139,8 @@ func (blockOpts) Format(codec string) BlockPutOption { // 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 } } diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 87fa90b65..916e52dd3 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -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}) } @@ -38,8 +42,12 @@ 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) t.Run("TestBlockPutHash", tp.TestBlockPutHash) t.Run("TestBlockGet", tp.TestBlockGet) t.Run("TestBlockRm", tp.TestBlockRm) @@ -47,6 +55,7 @@ func (tp *TestSuite) TestBlock(t *testing.T) { 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() @@ -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) @@ -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() @@ -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) From 373d18038c2ab5270433cd4f920962f1e76a98b9 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 25 Apr 2022 14:14:11 +0000 Subject: [PATCH 3650/3817] bump to newer blockstore err not found (#301) This commit was moved from ipld/go-car@c4b9dc88fc62d07a453efc8ae76a341084096ae7 --- ipld/car/v2/blockstore/readonly.go | 11 ++++++----- ipld/car/v2/blockstore/readonly_test.go | 4 ++-- ipld/car/v2/blockstore/readwrite_test.go | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 31636d4e4..f0a15e782 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -11,6 +11,7 @@ import ( blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" + format "github.com/ipfs/go-ipld-format" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1" @@ -280,14 +281,14 @@ func (b *ReadOnly) Get(ctx context.Context, key cid.Cid) (blocks.Block, error) { } }) if errors.Is(err, index.ErrNotFound) { - return nil, blockstore.ErrNotFound + return nil, format.ErrNotFound{Cid: key} } else if err != nil { - return nil, err + return nil, format.ErrNotFound{Cid: key} } else if fnErr != nil { return nil, fnErr } if fnData == nil { - return nil, blockstore.ErrNotFound + return nil, format.ErrNotFound{Cid: key} } return blocks.NewBlockWithCid(fnData, key) } @@ -338,14 +339,14 @@ func (b *ReadOnly) GetSize(ctx context.Context, key cid.Cid) (int, error) { } }) if errors.Is(err, index.ErrNotFound) { - return -1, blockstore.ErrNotFound + return -1, format.ErrNotFound{Cid: key} } else if err != nil { return -1, err } else if fnErr != nil { return -1, fnErr } if fnSize == -1 { - return -1, blockstore.ErrNotFound + return -1, format.ErrNotFound{Cid: key} } return fnSize, nil } diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 0ac2b7d16..f55a1e50c 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -11,7 +11,7 @@ import ( blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blockstore "github.com/ipfs/go-ipfs-blockstore" + format "github.com/ipfs/go-ipld-format" "github.com/ipfs/go-merkledag" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/internal/carv1" @@ -25,7 +25,7 @@ func TestReadOnlyGetReturnsBlockstoreNotFoundWhenCidDoesNotExist(t *testing.T) { // Assert blockstore API returns blockstore.ErrNotFound gotBlock, err := subject.Get(context.TODO(), nonExistingKey) - require.Equal(t, blockstore.ErrNotFound, err) + require.IsType(t, format.ErrNotFound{}, err) require.Nil(t, gotBlock) } diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 63f040d14..86ab06ea1 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -15,8 +15,8 @@ import ( blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - ipfsblockstore "github.com/ipfs/go-ipfs-blockstore" cbor "github.com/ipfs/go-ipld-cbor" + format "github.com/ipfs/go-ipld-format" "github.com/ipfs/go-merkledag" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/blockstore" @@ -43,7 +43,7 @@ func TestReadWriteGetReturnsBlockstoreNotFoundWhenCidDoesNotExist(t *testing.T) // Assert blockstore API returns blockstore.ErrNotFound gotBlock, err := subject.Get(context.TODO(), nonExistingKey) - require.Equal(t, ipfsblockstore.ErrNotFound, err) + require.IsType(t, format.ErrNotFound{}, err) require.Nil(t, gotBlock) } From a2af87efe6b346aa120c77ce66d27b72394759e4 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Sat, 16 Apr 2022 01:03:52 +0200 Subject: [PATCH 3651/3817] fix: error handling while enumerating pins This commit was moved from ipfs/go-pinning-service-http-client@e0e28d4c7d9538a7c869eeb559d4149844aacb76 --- pinning/remote/client/client.go | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/pinning/remote/client/client.go b/pinning/remote/client/client.go index f4799a74d..f1bed8af8 100644 --- a/pinning/remote/client/client.go +++ b/pinning/remote/client/client.go @@ -153,8 +153,22 @@ func (c *Client) Ls(ctx context.Context, opts ...LsOption) (chan PinStatusGetter } go func() { - defer close(errs) - defer close(res) + defer func() { + if r := recover(); r != nil { + var err error + switch x := r.(type) { + case string: + err = fmt.Errorf("unexpected error while listing remote pins: %s", x) + case error: + err = fmt.Errorf("unexpected error while listing remote pins: %w", x) + default: + err = errors.New("unknown panic while listing remote pins") + } + errs <- err + } + close(errs) + close(res) + }() for { pinRes, err := c.lsInternal(ctx, settings) @@ -173,11 +187,19 @@ func (c *Client) Ls(ctx context.Context, opts ...LsOption) (chan PinStatusGetter } } - if int(pinRes.Count) == len(results) { + batchSize := len(results) + if int(pinRes.Count) == batchSize { + // no more batches + return + } + + // Better DX/UX for cases like https://github.com/application-research/estuary/issues/124 + if batchSize == 0 && int(pinRes.Count) != 0 { + errs <- fmt.Errorf("invalid pinning service response: PinResults.count=%d but no PinResults.results", int(pinRes.Count)) return } - oldestResult := pinRes.Results[len(pinRes.Results)-1] + oldestResult := results[batchSize-1] settings.before = &oldestResult.Created } }() From 043eed1b7bfb2d4068fd5eb380de9b0961f29ea0 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 27 Apr 2022 17:11:38 +0200 Subject: [PATCH 3652/3817] fix: CIDv1 error with go-libp2p 0.19 (#32) The error message changed in libp2p and we no longer get this nice error message. String matching is a bad practice so just removing it, as we validate CID and codec already. This commit was moved from ipfs/go-namesys@605965e675aa7758ef32c4b5cd8ae856738fe77c --- namesys/namesys.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 51b5e7096..6dfad0b71 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -202,12 +202,12 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. // CIDs in IPNS are expected to have libp2p-key multicodec // We ease the transition by returning a more meaningful error with a valid CID - if err != nil && err.Error() == "can't convert CID of type protobuf to a peer ID" { + if err != nil { ipnsCid, cidErr := cid.Decode(key) if cidErr == nil && ipnsCid.Version() == 1 && ipnsCid.Type() != cid.Libp2pKey { fixedCid := cid.NewCidV1(cid.Libp2pKey, ipnsCid.Hash()).String() codecErr := fmt.Errorf("peer ID represented as CIDv1 require libp2p-key multicodec: retry with /ipns/%s", fixedCid) - log.Debugf("RoutingResolver: could not convert public key hash %s to peer ID: %s\n", key, codecErr) + log.Debugf("RoutingResolver: could not convert public key hash %q to peer ID: %s\n", key, codecErr) out <- onceResult{err: codecErr} close(out) return out From 435dbbc3d260f4a6db82063967d95788e7dfab45 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Wed, 4 May 2022 11:33:03 +0100 Subject: [PATCH 3653/3817] feat: add basic tracing This commit was moved from ipfs/go-blockservice@4c0ba0c53bbde606dad4e1b5e4156f9561658d01 --- blockservice/blockservice.go | 25 ++++++++++++++++++++++++- blockservice/internal/tracing.go | 13 +++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 blockservice/internal/tracing.go diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 4249f54c2..007d1131c 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -8,7 +8,11 @@ import ( "io" "sync" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-blockservice/internal" cid "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" exchange "github.com/ipfs/go-ipfs-exchange-interface" @@ -129,6 +133,9 @@ func NewSession(ctx context.Context, bs BlockService) *Session { // AddBlock adds a particular block to the service, Putting it into the datastore. // TODO pass a context into this if the remote.HasBlock is going to remain here. func (s *blockService) AddBlock(ctx context.Context, o blocks.Block) error { + ctx, span := internal.StartSpan(ctx, "blockService.AddBlock") + defer span.End() + c := o.Cid() // hash security err := verifcid.ValidateCid(c) @@ -157,6 +164,9 @@ func (s *blockService) AddBlock(ctx context.Context, o blocks.Block) error { } func (s *blockService) AddBlocks(ctx context.Context, bs []blocks.Block) error { + ctx, span := internal.StartSpan(ctx, "blockService.AddBlocks") + defer span.End() + // hash security for _, b := range bs { err := verifcid.ValidateCid(b.Cid()) @@ -203,7 +213,8 @@ func (s *blockService) AddBlocks(ctx context.Context, bs []blocks.Block) error { // GetBlock retrieves a particular block from the service, // Getting it from the datastore using the key (hash). func (s *blockService) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) { - logger.Debugf("BlockService GetBlock: '%s'", c) + ctx, span := internal.StartSpan(ctx, "blockService.GetBlock", trace.WithAttributes(attribute.Stringer("CID", c))) + defer span.End() var f func() exchange.Fetcher if s.exchange != nil { @@ -250,6 +261,9 @@ func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget fun // the returned channel. // NB: No guarantees are made about order. func (s *blockService) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan blocks.Block { + ctx, span := internal.StartSpan(ctx, "blockService.GetBlocks") + defer span.End() + var f func() exchange.Fetcher if s.exchange != nil { f = s.getExchange @@ -324,6 +338,9 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget // DeleteBlock deletes a block in the blockservice from the datastore func (s *blockService) DeleteBlock(ctx context.Context, c cid.Cid) error { + ctx, span := internal.StartSpan(ctx, "blockService.DeleteBlock", trace.WithAttributes(attribute.Stringer("CID", c))) + defer span.End() + err := s.blockstore.DeleteBlock(ctx, c) if err == nil { logger.Debugf("BlockService.BlockDeleted %s", c) @@ -357,6 +374,9 @@ func (s *Session) getSession() exchange.Fetcher { // GetBlock gets a block in the context of a request session func (s *Session) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) { + ctx, span := internal.StartSpan(ctx, "Session.GetBlock", trace.WithAttributes(attribute.Stringer("CID", c))) + defer span.End() + var f func() exchange.Fetcher if s.sessEx != nil { f = s.getSession @@ -366,6 +386,9 @@ func (s *Session) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) // GetBlocks gets blocks in the context of a request session func (s *Session) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan blocks.Block { + ctx, span := internal.StartSpan(ctx, "Session.GetBlocks") + defer span.End() + var f func() exchange.Fetcher if s.sessEx != nil { f = s.getSession diff --git a/blockservice/internal/tracing.go b/blockservice/internal/tracing.go new file mode 100644 index 000000000..96a61ff42 --- /dev/null +++ b/blockservice/internal/tracing.go @@ -0,0 +1,13 @@ +package internal + +import ( + "context" + "fmt" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" +) + +func StartSpan(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { + return otel.Tracer("go-blockservice").Start(ctx, fmt.Sprintf("Blockservice.%s", name), opts...) +} From 80f06e2c953103b30e8c09ccacf3a458de866c94 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Wed, 4 May 2022 11:47:06 +0100 Subject: [PATCH 3654/3817] feat: add basic tracing This commit was moved from ipfs/go-path@33dbd0ef83599fff9f2a4ccf65d4b3074f68d04a --- path/internal/tracing.go | 13 +++++++++++++ path/resolver/resolver.go | 24 +++++++++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 path/internal/tracing.go diff --git a/path/internal/tracing.go b/path/internal/tracing.go new file mode 100644 index 000000000..f9eda2f92 --- /dev/null +++ b/path/internal/tracing.go @@ -0,0 +1,13 @@ +package internal + +import ( + "context" + "fmt" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" +) + +func StartSpan(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { + return otel.Tracer("go-path").Start(ctx, fmt.Sprintf("Path.%s", name), opts...) +} diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 64778c1ca..5a1d3f870 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -7,18 +7,20 @@ import ( "fmt" "time" - "github.com/ipld/go-ipld-prime/schema" - - path "github.com/ipfs/go-path" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" cid "github.com/ipfs/go-cid" "github.com/ipfs/go-fetcher" fetcherhelpers "github.com/ipfs/go-fetcher/helpers" format "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" + path "github.com/ipfs/go-path" + "github.com/ipfs/go-path/internal" "github.com/ipld/go-ipld-prime" cidlink "github.com/ipld/go-ipld-prime/linking/cid" basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/ipld/go-ipld-prime/schema" "github.com/ipld/go-ipld-prime/traversal/selector/builder" ) @@ -77,6 +79,9 @@ func NewBasicResolver(fetcherFactory fetcher.Factory) Resolver { // block referenced by the path, and the path segments to traverse from the // final block boundary to the final node within the block. func (r *basicResolver) ResolveToLastNode(ctx context.Context, fpath path.Path) (cid.Cid, []string, error) { + ctx, span := internal.StartSpan(ctx, "basicResolver.ResolveToLastNode", trace.WithAttributes(attribute.Stringer("Path", fpath))) + defer span.End() + c, p, err := path.SplitAbsPath(fpath) if err != nil { return cid.Cid{}, nil, err @@ -143,6 +148,9 @@ func (r *basicResolver) ResolveToLastNode(ctx context.Context, fpath path.Path) // Note: if/when the context is cancelled or expires then if a multi-block ADL node is returned then it may not be // possible to load certain values. func (r *basicResolver) ResolvePath(ctx context.Context, fpath path.Path) (ipld.Node, ipld.Link, error) { + ctx, span := internal.StartSpan(ctx, "basicResolver.ResolvePath", trace.WithAttributes(attribute.Stringer("Path", fpath))) + defer span.End() + // validate path if err := fpath.IsValid(); err != nil { return nil, nil, err @@ -170,6 +178,8 @@ func (r *basicResolver) ResolvePath(ctx context.Context, fpath path.Path) (ipld. // extra context (does not opaquely resolve through sharded nodes) // Deprecated: fetch node as ipld-prime or convert it and then use a selector to traverse through it. func ResolveSingle(ctx context.Context, ds format.NodeGetter, nd format.Node, names []string) (*format.Link, []string, error) { + ctx, span := internal.StartSpan(ctx, "ResolveSingle", trace.WithAttributes(attribute.Stringer("CID", nd.Cid()))) + defer span.End() return nd.ResolveLink(names) } @@ -180,6 +190,9 @@ func ResolveSingle(ctx context.Context, ds format.NodeGetter, nd format.Node, na // Note: if/when the context is cancelled or expires then if a multi-block ADL node is returned then it may not be // possible to load certain values. func (r *basicResolver) ResolvePathComponents(ctx context.Context, fpath path.Path) ([]ipld.Node, error) { + ctx, span := internal.StartSpan(ctx, "basicResolver.ResolvePathComponents", trace.WithAttributes(attribute.Stringer("Path", fpath))) + defer span.End() + //lint:ignore SA1019 TODO: replace EventBegin evt := log.EventBegin(ctx, "resolvePathComponents", logging.LoggableMap{"fpath": fpath}) defer evt.Done() @@ -218,6 +231,9 @@ func (r *basicResolver) ResolvePathComponents(ctx context.Context, fpath path.Pa // Note: if/when the context is cancelled or expires then if a multi-block ADL node is returned then it may not be // possible to load certain values. func (r *basicResolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names []string) ([]ipld.Node, error) { + ctx, span := internal.StartSpan(ctx, "basicResolver.ResolveLinks") + defer span.End() + //lint:ignore SA1019 TODO: replace EventBegin evt := log.EventBegin(ctx, "resolveLinks", logging.LoggableMap{"names": names}) defer evt.Done() @@ -244,6 +260,8 @@ func (r *basicResolver) ResolveLinks(ctx context.Context, ndd ipld.Node, names [ // Finds nodes matching the selector starting with a cid. Returns the matched nodes, the cid of the block containing // the last node, and the depth of the last node within its block (root is depth 0). func (r *basicResolver) resolveNodes(ctx context.Context, c cid.Cid, sel ipld.Node) ([]ipld.Node, cid.Cid, int, error) { + ctx, span := internal.StartSpan(ctx, "basicResolver.resolveNodes", trace.WithAttributes(attribute.Stringer("CID", c))) + defer span.End() session := r.FetcherFactory.NewSession(ctx) // traverse selector From 0bf8c2a0b2c64871ba6213be635e963a959a0d64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Sat, 7 May 2022 00:04:33 +0200 Subject: [PATCH 3655/3817] feat: fast-path for PutMany, falling back to Put for single block call (#97) This commit was moved from ipfs/go-ipfs-blockstore@e4da93c569b737ab9f40ccf8c11a4e7dd2eebdba --- blockstore/blockstore.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 61cb780f8..509e678f5 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -178,6 +178,11 @@ func (bs *blockstore) Put(ctx context.Context, block blocks.Block) error { } func (bs *blockstore) PutMany(ctx context.Context, blocks []blocks.Block) error { + if len(blocks) == 1 { + // performance fast-path + return bs.Put(ctx, blocks[0]) + } + t, err := bs.datastore.Batch(ctx) if err != nil { return err From 9b58e529e5ce1e00314d983c2c7a6e6612bbe959 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 12 May 2022 14:08:55 +0100 Subject: [PATCH 3656/3817] Remove unused context This commit was moved from ipfs/go-path@6c040f7024577a6bc9e55066dc607ec9c607e34d --- path/resolver/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index 5a1d3f870..ba7e1a2c1 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -178,7 +178,7 @@ func (r *basicResolver) ResolvePath(ctx context.Context, fpath path.Path) (ipld. // extra context (does not opaquely resolve through sharded nodes) // Deprecated: fetch node as ipld-prime or convert it and then use a selector to traverse through it. func ResolveSingle(ctx context.Context, ds format.NodeGetter, nd format.Node, names []string) (*format.Link, []string, error) { - ctx, span := internal.StartSpan(ctx, "ResolveSingle", trace.WithAttributes(attribute.Stringer("CID", nd.Cid()))) + _, span := internal.StartSpan(ctx, "ResolveSingle", trace.WithAttributes(attribute.Stringer("CID", nd.Cid()))) defer span.End() return nd.ResolveLink(names) } From 5414376436cdf299a73a0e49ebd6b1fb1b8dec30 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 31 May 2022 07:28:28 +0000 Subject: [PATCH 3657/3817] PrototypeChooser support (#305) * complete reads of lazy nodes in walks This commit was moved from ipld/go-car@c65f0bfea9e791e877a0a9ccadca54e1d0c1715f --- ipld/car/v2/internal/loader/writing_loader.go | 38 ++++++++----- ipld/car/v2/options.go | 10 ++++ ipld/car/v2/selective.go | 37 +++++++++--- ipld/car/v2/selective_test.go | 56 ++++++++++++++++++- 4 files changed, 118 insertions(+), 23 deletions(-) diff --git a/ipld/car/v2/internal/loader/writing_loader.go b/ipld/car/v2/internal/loader/writing_loader.go index 13236f1c6..b4d0e6efe 100644 --- a/ipld/car/v2/internal/loader/writing_loader.go +++ b/ipld/car/v2/internal/loader/writing_loader.go @@ -16,7 +16,7 @@ type writerOutput struct { w io.Writer size uint64 code multicodec.Code - rcrds []index.Record + rcrds map[cid.Cid]index.Record } func (w *writerOutput) Size() uint64 { @@ -28,7 +28,11 @@ func (w *writerOutput) Index() (index.Index, error) { if err != nil { return nil, err } - if err := idx.Load(w.rcrds); err != nil { + rcrds := make([]index.Record, 0, len(w.rcrds)) + for _, r := range w.rcrds { + rcrds = append(rcrds, r) + } + if err := idx.Load(rcrds); err != nil { return nil, err } @@ -63,17 +67,13 @@ func (w *writingReader) Read(p []byte) (int, error) { if _, err := cpy.WriteTo(w.wo.w); err != nil { return 0, err } - - // maybe write the index. - if w.wo.code != index.CarIndexNone { - _, c, err := cid.CidFromBytes([]byte(w.cid)) - if err != nil { - return 0, err - } - w.wo.rcrds = append(w.wo.rcrds, index.Record{ - Cid: c, - Offset: w.wo.size, - }) + _, c, err := cid.CidFromBytes([]byte(w.cid)) + if err != nil { + return 0, err + } + w.wo.rcrds[c] = index.Record{ + Cid: c, + Offset: w.wo.size, } w.wo.size += uint64(w.len) + uint64(len(size)+len(w.cid)) @@ -94,11 +94,21 @@ func TeeingLinkSystem(ls ipld.LinkSystem, w io.Writer, initialOffset uint64, ind w: w, size: initialOffset, code: indexCodec, - rcrds: make([]index.Record, 0), + rcrds: make(map[cid.Cid]index.Record), } tls := ls tls.StorageReadOpener = func(lc linking.LinkContext, l ipld.Link) (io.Reader, error) { + _, c, err := cid.CidFromBytes([]byte(l.Binary())) + if err != nil { + return nil, err + } + + // if we've already read this cid in this session, don't re-write it. + if _, ok := wo.rcrds[c]; ok { + return ls.StorageReadOpener(lc, l) + } + r, err := ls.StorageReadOpener(lc, l) if err != nil { return nil, err diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go index 228b359be..d2923b214 100644 --- a/ipld/car/v2/options.go +++ b/ipld/car/v2/options.go @@ -4,6 +4,7 @@ import ( "math" "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-ipld-prime/traversal" "github.com/multiformats/go-multicodec" ) @@ -40,6 +41,7 @@ type Options struct { BlockstoreUseWholeCIDs bool MaxTraversalLinks uint64 WriteAsCarV1 bool + TraversalPrototypeChooser traversal.LinkTargetNodePrototypeChooser } // ApplyOptions applies given opts and returns the resulting Options. @@ -118,3 +120,11 @@ func MaxIndexCidSize(s uint64) Option { o.MaxIndexCidSize = s } } + +// WithTraversalPrototypeChooser specifies the prototype chooser that should be used +// when performing traversals in writes from a linksystem. +func WithTraversalPrototypeChooser(t traversal.LinkTargetNodePrototypeChooser) Option { + return func(o *Options) { + o.TraversalPrototypeChooser = t + } +} diff --git a/ipld/car/v2/selective.go b/ipld/car/v2/selective.go index 22351e715..39bb5f911 100644 --- a/ipld/car/v2/selective.go +++ b/ipld/car/v2/selective.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "io/ioutil" "math" "os" @@ -12,6 +13,7 @@ import ( "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/loader" ipld "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/datamodel" "github.com/ipld/go-ipld-prime/linking" cidlink "github.com/ipld/go-ipld-prime/linking/cid" "github.com/ipld/go-ipld-prime/node/basicnode" @@ -236,14 +238,19 @@ func traverse(ctx context.Context, ls *ipld.LinkSystem, root cid.Cid, s ipld.Nod return err } + chooser := func(_ ipld.Link, _ linking.LinkContext) (ipld.NodePrototype, error) { + return basicnode.Prototype.Any, nil + } + if opts.TraversalPrototypeChooser != nil { + chooser = opts.TraversalPrototypeChooser + } + progress := traversal.Progress{ Cfg: &traversal.Config{ - Ctx: ctx, - LinkSystem: *ls, - LinkTargetNodePrototypeChooser: func(_ ipld.Link, _ linking.LinkContext) (ipld.NodePrototype, error) { - return basicnode.Prototype.Any, nil - }, - LinkVisitOnlyOnce: !opts.BlockstoreAllowDuplicatePuts, + Ctx: ctx, + LinkSystem: *ls, + LinkTargetNodePrototypeChooser: chooser, + LinkVisitOnlyOnce: !opts.BlockstoreAllowDuplicatePuts, }, } if opts.MaxTraversalLinks < math.MaxInt64 { @@ -255,11 +262,25 @@ func traverse(ctx context.Context, ls *ipld.LinkSystem, root cid.Cid, s ipld.Nod lnk := cidlink.Link{Cid: root} ls.TrustedStorage = true - rootNode, err := ls.Load(ipld.LinkContext{}, lnk, basicnode.Prototype.Any) + rp, err := chooser(lnk, ipld.LinkContext{}) + if err != nil { + return err + } + rootNode, err := ls.Load(ipld.LinkContext{}, lnk, rp) if err != nil { return fmt.Errorf("root blk load failed: %s", err) } - err = progress.WalkMatching(rootNode, sel, func(_ traversal.Progress, _ ipld.Node) error { + err = progress.WalkMatching(rootNode, sel, func(_ traversal.Progress, node ipld.Node) error { + if lbn, ok := node.(datamodel.LargeBytesNode); ok { + s, err := lbn.AsLargeBytes() + if err != nil { + return err + } + _, err = io.Copy(ioutil.Discard, s) + if err != nil { + return err + } + } return nil }) if err != nil { diff --git a/ipld/car/v2/selective_test.go b/ipld/car/v2/selective_test.go index f2bba6f8a..924351d46 100644 --- a/ipld/car/v2/selective_test.go +++ b/ipld/car/v2/selective_test.go @@ -3,18 +3,27 @@ package car_test import ( "bytes" "context" + "io" "os" "path" "testing" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-unixfsnode" + "github.com/ipfs/go-unixfsnode/data/builder" "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/blockstore" + dagpb "github.com/ipld/go-codec-dagpb" + "github.com/ipld/go-ipld-prime/datamodel" + "github.com/ipld/go-ipld-prime/linking" cidlink "github.com/ipld/go-ipld-prime/linking/cid" + basicnode "github.com/ipld/go-ipld-prime/node/basic" "github.com/ipld/go-ipld-prime/storage/bsadapter" + sb "github.com/ipld/go-ipld-prime/traversal/selector/builder" selectorparse "github.com/ipld/go-ipld-prime/traversal/selector/parse" "github.com/stretchr/testify/require" - _ "github.com/ipld/go-codec-dagpb" _ "github.com/ipld/go-ipld-prime/codec/dagcbor" _ "github.com/ipld/go-ipld-prime/codec/raw" ) @@ -81,3 +90,48 @@ func TestV1Traversal(t *testing.T) { fa, _ := os.Stat("testdata/sample-v1.car") require.Equal(t, fa.Size(), int64(n)) } + +func TestPartialTraversal(t *testing.T) { + store := cidlink.Memory{Bag: make(map[string][]byte)} + ls := cidlink.DefaultLinkSystem() + ls.StorageReadOpener = store.OpenRead + ls.StorageWriteOpener = store.OpenWrite + unixfsnode.AddUnixFSReificationToLinkSystem(&ls) + + // write a unixfs file. + initBuf := bytes.Buffer{} + _, _ = initBuf.Write(make([]byte, 1000000)) + rt, _, err := builder.BuildUnixFSFile(&initBuf, "", &ls) + require.NoError(t, err) + + // read a subset of the file. + _, rts, err := cid.CidFromBytes([]byte(rt.Binary())) + require.NoError(t, err) + ssb := sb.NewSelectorSpecBuilder(basicnode.Prototype.Any) + sel := ssb.ExploreInterpretAs("unixfs", ssb.MatcherSubset(0, 256*1000)) + buf := bytes.Buffer{} + chooser := dagpb.AddSupportToChooser(func(l datamodel.Link, lc linking.LinkContext) (datamodel.NodePrototype, error) { + return basicnode.Prototype.Any, nil + }) + _, err = car.TraverseV1(context.Background(), &ls, rts, sel.Node(), &buf, car.WithTraversalPrototypeChooser(chooser)) + require.NoError(t, err) + + fb := len(buf.Bytes()) + require.Less(t, fb, 1000000) + + loaded, err := car.NewBlockReader(&buf) + require.NoError(t, err) + fnd := make(map[cid.Cid]struct{}) + var b blocks.Block + for err == nil { + b, err = loaded.Next() + if err == io.EOF { + break + } + if _, ok := fnd[b.Cid()]; ok { + require.Fail(t, "duplicate block present", b.Cid()) + } + fnd[b.Cid()] = struct{}{} + } + require.Equal(t, 2, len(fnd)) +} From 9194b97973b73f0fdc8064c02a5115234af7c6e8 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 15 Jun 2022 20:00:49 +0200 Subject: [PATCH 3658/3817] Make switchToSharding more efficient When automatically switching a BasicDirectory to a HAMTDirectory because it grew too big, the current code: * loops every link in the BasicDirectory * reads each node referenced by those links * adds the nodes to a new HAMTDirectory shard, which in turn: * writes the nodes to the DAG service (they were just read from there!) * makes a link out of them (identical to the link in the BasicDirectory!) This would happen to about (~4000 nodes), which are fully read and written for nothing. This PR adds a new SetLink method to the HAMT Shard which, instead of taking an ipld.Node like Set(), takes directly an ipld.Link. Then it updates switchToSharding() to pass the links in the BasicDirectory directy, rather than reading all the nodes. Note that switchToBasic() works like this already, only using the links in the HAMT directory. This commit was moved from ipfs/go-unixfs@e0ef9c44e92cab793bdf321ac1531ceff418fd5f --- unixfs/hamt/hamt.go | 20 ++++++++++++++++++++ unixfs/io/directory.go | 7 +------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index ac1c5e458..593b64627 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -229,6 +229,26 @@ func (ds *Shard) Set(ctx context.Context, name string, nd ipld.Node) error { return err } +// Set sets 'name' = nd in the HAMT, using directly the information in the +// given link. This avoids writing the given node, then reading it to making a +// link out of it. +func (ds *Shard) SetLink(ctx context.Context, name string, lnk *ipld.Link) error { + hv := newHashBits(name) + + newLink := ipld.Link{ + Name: lnk.Name, + Size: lnk.Size, + Cid: lnk.Cid, + } + + // FIXME: We don't need to set the name here, it will get overwritten. + // This is confusing, confirm and remove this line. + newLink.Name = ds.linkNamePrefix(0) + name + + _, err := ds.swapValue(ctx, hv, name, &newLink) + return err +} + // Swap sets a link pointing to the passed node as the value under the // name key in this Shard or its children. It also returns the previous link // under that name key (if any). diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index 2ec862247..b602bf9ab 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -334,12 +334,7 @@ func (d *BasicDirectory) switchToSharding(ctx context.Context) (*HAMTDirectory, hamtDir.shard = shard for _, lnk := range d.node.Links() { - node, err := d.dserv.Get(ctx, lnk.Cid) - if err != nil { - return nil, err - } - - err = hamtDir.shard.Set(ctx, lnk.Name, node) + err = hamtDir.shard.SetLink(ctx, lnk.Name, lnk) if err != nil { return nil, err } From 33899cd0d2da468aed7dbd7a9f45bedc844f6572 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 29 Jun 2022 16:26:39 +0100 Subject: [PATCH 3659/3817] Add API to regenerate index from CARv1 or CARv2 The index generation APIs either allowed reading an existing index from a CARv2 or explicitly required a CARv1 to generate index. Introduce APIs to make it easier for users that want to regenerate the index regardless of whether it exists in a CAR file or not. The index generation APIs are changed to accept either of the formats and re-generate the index from the data payload unless `ReadOrGenerate` is called. Adjust the tests to run for all flavours of index generation with both CARv1 and CARv2 payload. This commit was moved from ipld/go-car@7ba9372ba205c2baf620c0b104dc08ed80b1dffb --- ipld/car/v2/index_gen.go | 98 ++++++++++++++++++++----- ipld/car/v2/index_gen_test.go | 134 ++++++++++++++++------------------ 2 files changed, 144 insertions(+), 88 deletions(-) diff --git a/ipld/car/v2/index_gen.go b/ipld/car/v2/index_gen.go index 81d23919d..10c86dc34 100644 --- a/ipld/car/v2/index_gen.go +++ b/ipld/car/v2/index_gen.go @@ -13,9 +13,12 @@ import ( "github.com/multiformats/go-varint" ) -// GenerateIndex generates index for a given car in v1 format. +// GenerateIndex generates index for the given car payload reader. // The index can be stored in serialized format using index.WriteTo. -// See LoadIndex. +// +// Note, the index is re-generated every time even if the payload is in CARv2 format and already has +// an index. To read existing index when available see ReadOrGenerateIndex. +// See: LoadIndex. func GenerateIndex(v1r io.Reader, opts ...Option) (index.Index, error) { wopts := ApplyOptions(opts...) idx, err := index.New(wopts.IndexCodec) @@ -28,21 +31,63 @@ func GenerateIndex(v1r io.Reader, opts ...Option) (index.Index, error) { return idx, nil } -// LoadIndex populates idx with index records generated from v1r. -// The v1r must be data payload in CARv1 format. -func LoadIndex(idx index.Index, v1r io.Reader, opts ...Option) error { - reader := internalio.ToByteReadSeeker(v1r) - header, err := carv1.ReadHeader(reader) +// LoadIndex populates idx with index records generated from r. +// The r may be in CARv1 or CARv2 format. +// +// Note, the index is re-generated every time even if r is in CARv2 format and already has an index. +// To read existing index when available see ReadOrGenerateIndex. +func LoadIndex(idx index.Index, r io.Reader, opts ...Option) error { + reader := internalio.ToByteReadSeeker(r) + pragma, err := carv1.ReadHeader(r) if err != nil { return fmt.Errorf("error reading car header: %w", err) } - if header.Version != 1 { - return fmt.Errorf("expected version to be 1, got %v", header.Version) - } + var dataSize, dataOffset int64 + switch pragma.Version { + case 1: + break + case 2: + // Read V2 header which should appear immediately after pragma according to CARv2 spec. + var v2h Header + _, err := v2h.ReadFrom(r) + if err != nil { + return err + } - // Parse Options. - o := ApplyOptions(opts...) + // Sanity-check the CARv2 header + if v2h.DataOffset < HeaderSize { + return fmt.Errorf("malformed CARv2; data offset too small: %d", v2h.DataOffset) + } + if v2h.DataSize < 1 { + return fmt.Errorf("malformed CARv2; data payload size too small: %d", v2h.DataSize) + } + + // Seek to the beginning of the inner CARv1 payload + _, err = reader.Seek(int64(v2h.DataOffset), io.SeekStart) + if err != nil { + return err + } + + // Set dataSize and dataOffset which are then used during index loading logic to decide + // where to stop and adjust section offset respectively. + // Note that we could use a LimitReader here and re-define reader with it. However, it means + // the internalio.ToByteReadSeeker will be less efficient since LimitReader does not + // implement ByteReader nor ReadSeeker. + dataSize = int64(v2h.DataSize) + dataOffset = int64(v2h.DataOffset) + + // Read the inner CARv1 header to skip it and sanity check it. + v1h, err := carv1.ReadHeader(reader) + if err != nil { + return err + } + if v1h.Version != 1 { + return fmt.Errorf("expected data payload header version of 1; got %d", v1h.Version) + } + default: + return fmt.Errorf("expected either version 1 or 2; got %d", pragma.Version) + } // Record the start of each section, with first section starring from current position in the // reader, i.e. right after the header, since we have only read the header so far. @@ -55,6 +100,13 @@ func LoadIndex(idx index.Index, v1r io.Reader, opts ...Option) error { return err } + // Subtract the data offset; if CARv1 this would be zero otherwise the value will come from the + // CARv2 header. + sectionOffset -= dataOffset + + // Parse Options. + o := ApplyOptions(opts...) + records := make([]index.Record, 0) for { // Read the section's length. @@ -94,6 +146,14 @@ func LoadIndex(idx index.Index, v1r io.Reader, opts ...Option) error { if sectionOffset, err = reader.Seek(remainingSectionLen, io.SeekCurrent); err != nil { return err } + // Subtract the data offset which will be non-zero when reader represents a CARv2. + sectionOffset -= dataOffset + + // Check if we have reached the end of data payload and if so treat it as an EOF. + // Note, dataSize will be non-zero only if we are reading from a CARv2. + if dataSize != 0 && sectionOffset >= dataSize { + break + } } if err := idx.Load(records); err != nil { @@ -103,16 +163,20 @@ func LoadIndex(idx index.Index, v1r io.Reader, opts ...Option) error { return nil } -// GenerateIndexFromFile walks a car v1 file at the give path and generates an index of cid->byte offset. -// The index can be stored using index.WriteTo. -// See GenerateIndex. -func GenerateIndexFromFile(path string) (index.Index, error) { +// GenerateIndexFromFile walks a CAR file at the give path and generates an index of cid->byte offset. +// The index can be stored using index.WriteTo. Both CARv1 and CARv2 formats are accepted. +// +// Note, the index is re-generated every time even if the given CAR file is in CARv2 format and +// already has an index. To read existing index when available see ReadOrGenerateIndex. +// +// See: GenerateIndex. +func GenerateIndexFromFile(path string, opts ...Option) (index.Index, error) { f, err := os.Open(path) if err != nil { return nil, err } defer f.Close() - return GenerateIndex(f) + return GenerateIndex(f, opts...) } // ReadOrGenerateIndex accepts both CARv1 and CARv2 formats, and reads or generates an index for it. diff --git a/ipld/car/v2/index_gen_test.go b/ipld/car/v2/index_gen_test.go index 529fb9137..11f0530fb 100644 --- a/ipld/car/v2/index_gen_test.go +++ b/ipld/car/v2/index_gen_test.go @@ -17,7 +17,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestReadOrGenerateIndex(t *testing.T) { +func TestGenerateIndex(t *testing.T) { tests := []struct { name string carPath string @@ -26,10 +26,9 @@ func TestReadOrGenerateIndex(t *testing.T) { wantErr bool }{ { - "CarV1IsIndexedAsExpected", - "testdata/sample-v1.car", - []carv2.Option{}, - func(t *testing.T) index.Index { + name: "CarV1IsIndexedAsExpected", + carPath: "testdata/sample-v1.car", + wantIndexer: func(t *testing.T) index.Index { v1, err := os.Open("testdata/sample-v1.car") require.NoError(t, err) defer v1.Close() @@ -37,13 +36,11 @@ func TestReadOrGenerateIndex(t *testing.T) { require.NoError(t, err) return want }, - false, }, { - "CarV2WithIndexIsReturnedAsExpected", - "testdata/sample-wrapped-v2.car", - []carv2.Option{}, - func(t *testing.T) index.Index { + name: "CarV2WithIndexIsReturnedAsExpected", + carPath: "testdata/sample-wrapped-v2.car", + wantIndexer: func(t *testing.T) index.Index { v2, err := os.Open("testdata/sample-wrapped-v2.car") require.NoError(t, err) defer v2.Close() @@ -53,13 +50,12 @@ func TestReadOrGenerateIndex(t *testing.T) { require.NoError(t, err) return want }, - false, }, { - "CarV1WithZeroLenSectionIsGeneratedAsExpected", - "testdata/sample-v1-with-zero-len-section.car", - []carv2.Option{carv2.ZeroLengthSectionAsEOF(true)}, - func(t *testing.T) index.Index { + name: "CarV1WithZeroLenSectionIsGeneratedAsExpected", + carPath: "testdata/sample-v1-with-zero-len-section.car", + opts: []carv2.Option{carv2.ZeroLengthSectionAsEOF(true)}, + wantIndexer: func(t *testing.T) index.Index { v1, err := os.Open("testdata/sample-v1-with-zero-len-section.car") require.NoError(t, err) defer v1.Close() @@ -67,13 +63,12 @@ func TestReadOrGenerateIndex(t *testing.T) { require.NoError(t, err) return want }, - false, }, { - "AnotherCarV1WithZeroLenSectionIsGeneratedAsExpected", - "testdata/sample-v1-with-zero-len-section2.car", - []carv2.Option{carv2.ZeroLengthSectionAsEOF(true)}, - func(t *testing.T) index.Index { + name: "AnotherCarV1WithZeroLenSectionIsGeneratedAsExpected", + carPath: "testdata/sample-v1-with-zero-len-section2.car", + opts: []carv2.Option{carv2.ZeroLengthSectionAsEOF(true)}, + wantIndexer: func(t *testing.T) index.Index { v1, err := os.Open("testdata/sample-v1-with-zero-len-section2.car") require.NoError(t, err) defer v1.Close() @@ -81,25 +76,21 @@ func TestReadOrGenerateIndex(t *testing.T) { require.NoError(t, err) return want }, - false, }, { - "CarV1WithZeroLenSectionWithoutOptionIsError", - "testdata/sample-v1-with-zero-len-section.car", - []carv2.Option{}, - func(t *testing.T) index.Index { return nil }, - true, + name: "CarV1WithZeroLenSectionWithoutOptionIsError", + carPath: "testdata/sample-v1-with-zero-len-section.car", + wantErr: true, }, { - "CarOtherThanV1OrV2IsError", - "testdata/sample-rootless-v42.car", - []carv2.Option{}, - func(t *testing.T) index.Index { return nil }, - true, + name: "CarOtherThanV1OrV2IsError", + carPath: "testdata/sample-rootless-v42.car", + wantIndexer: func(t *testing.T) index.Index { return nil }, + wantErr: true, }, } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { + t.Run("ReadOrGenerateIndex_"+tt.name, func(t *testing.T) { carFile, err := os.Open(tt.carPath) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, carFile.Close()) }) @@ -108,54 +99,55 @@ func TestReadOrGenerateIndex(t *testing.T) { require.Error(t, err) } else { require.NoError(t, err) - want := tt.wantIndexer(t) + var want index.Index + if tt.wantIndexer != nil { + want = tt.wantIndexer(t) + } require.Equal(t, want, got) } }) - } -} - -func TestGenerateIndexFromFile(t *testing.T) { - tests := []struct { - name string - carPath string - wantIndexer func(t *testing.T) index.Index - wantErr bool - }{ - { - "CarV1IsIndexedAsExpected", - "testdata/sample-v1.car", - func(t *testing.T) index.Index { - v1, err := os.Open("testdata/sample-v1.car") + t.Run("GenerateIndexFromFile_"+tt.name, func(t *testing.T) { + got, err := carv2.GenerateIndexFromFile(tt.carPath, tt.opts...) + if tt.wantErr { + require.Error(t, err) + } else { require.NoError(t, err) - defer v1.Close() - want, err := carv2.GenerateIndex(v1) + var want index.Index + if tt.wantIndexer != nil { + want = tt.wantIndexer(t) + } + require.Equal(t, want, got) + } + }) + t.Run("LoadIndex_"+tt.name, func(t *testing.T) { + carFile, err := os.Open(tt.carPath) + require.NoError(t, err) + got, err := index.New(multicodec.CarMultihashIndexSorted) + require.NoError(t, err) + err = carv2.LoadIndex(got, carFile, tt.opts...) + if tt.wantErr { + require.Error(t, err) + } else { require.NoError(t, err) - return want - }, - false, - }, - { - "CarV2IsErrorSinceOnlyV1PayloadIsExpected", - "testdata/sample-wrapped-v2.car", - func(t *testing.T) index.Index { return nil }, - true, - }, - { - "CarOtherThanV1OrV2IsError", - "testdata/sample-rootless-v42.car", - func(t *testing.T) index.Index { return nil }, - true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := carv2.GenerateIndexFromFile(tt.carPath) + var want index.Index + if tt.wantIndexer != nil { + want = tt.wantIndexer(t) + } + require.Equal(t, want, got) + } + }) + t.Run("GenerateIndex_"+tt.name, func(t *testing.T) { + carFile, err := os.Open(tt.carPath) + require.NoError(t, err) + got, err := carv2.GenerateIndex(carFile, tt.opts...) if tt.wantErr { require.Error(t, err) } else { require.NoError(t, err) - want := tt.wantIndexer(t) + var want index.Index + if tt.wantIndexer != nil { + want = tt.wantIndexer(t) + } require.Equal(t, want, got) } }) From 3c6b45ebe0543be40eead8c4cde9540aa3a67f56 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Thu, 16 Jun 2022 23:41:32 +0200 Subject: [PATCH 3660/3817] fix: don't OOM if the header size is too big This commit was moved from ipld/go-car@a35f04821d5835e46997da3317c09a336a7de86e --- ipld/car/util/util.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ipld/car/util/util.go b/ipld/car/util/util.go index 08048f333..320ecf89f 100644 --- a/ipld/car/util/util.go +++ b/ipld/car/util/util.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "encoding/binary" + "errors" "fmt" "io" @@ -11,6 +12,13 @@ import ( mh "github.com/multiformats/go-multihash" ) +// MaxAllowedHeaderSize hint about how big the header red are allowed to be. +// This value is a hint to avoid OOMs, a parser that cannot OOM because it is +// streaming for example, isn't forced to follow that value. +// Deprecated: You should use v2#NewReader instead since it allows for options +// to be passed in. +var MaxAllowedHeaderSize uint = 1024 + var cidv0Pref = []byte{0x12, 0x20} type BytesReader interface { @@ -112,6 +120,10 @@ func LdRead(r *bufio.Reader) ([]byte, error) { return nil, err } + if l > uint64(MaxAllowedHeaderSize) { // Don't OOM + return nil, errors.New("malformed car; header is bigger than util.MaxAllowedHeaderSize") + } + buf := make([]byte, l) if _, err := io.ReadFull(r, buf); err != nil { return nil, err From 6ed9f165316e8c3b42595e70d34617e7de8ef748 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Thu, 16 Jun 2022 23:42:38 +0200 Subject: [PATCH 3661/3817] fix: do bound check while checking for CIDv0 This commit was moved from ipld/go-car@24feaada4ddc7936153c652538d7743d51afae6b --- ipld/car/util/util.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ipld/car/util/util.go b/ipld/car/util/util.go index 320ecf89f..fbd6a86bc 100644 --- a/ipld/car/util/util.go +++ b/ipld/car/util/util.go @@ -28,9 +28,13 @@ type BytesReader interface { // TODO: this belongs in the go-cid package func ReadCid(buf []byte) (cid.Cid, int, error) { - if bytes.Equal(buf[:2], cidv0Pref) { - c, err := cid.Cast(buf[:34]) - return c, 34, err + if len(buf) >= 2 && bytes.Equal(buf[:2], cidv0Pref) { + i := 34 + if len(buf) < i { + i = len(buf) + } + c, err := cid.Cast(buf[:i]) + return c, i, err } br := bytes.NewReader(buf) From eed3b0b1664729e395304e8f0d4acfc2d4fb02f6 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Thu, 16 Jun 2022 23:43:10 +0200 Subject: [PATCH 3662/3817] test: add fuzzing of NewCarReader This commit was moved from ipld/go-car@d648fc1dbf3310dc8648455b4755b25f13946d9d --- ipld/car/car_test.go | 6 ++-- ipld/car/fuzz_test.go | 35 +++++++++++++++++++ ...133bae39a14164874ed8abdee1f6a6795311a0e546 | 2 ++ ...30cc13b5570849c89b3dbf5bc0152abc66c9642f3e | 2 ++ 4 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 ipld/car/fuzz_test.go create mode 100644 ipld/car/testdata/fuzz/FuzzCarReader/21a90a70853c333c6b9ddc133bae39a14164874ed8abdee1f6a6795311a0e546 create mode 100644 ipld/car/testdata/fuzz/FuzzCarReader/5857e57e4072c6b0d8684030cc13b5570849c89b3dbf5bc0152abc66c9642f3e diff --git a/ipld/car/car_test.go b/ipld/car/car_test.go index 9ae309099..3c6340be3 100644 --- a/ipld/car/car_test.go +++ b/ipld/car/car_test.go @@ -75,9 +75,11 @@ func TestRoundtrip(t *testing.T) { } } +// fixture is a clean single-block, single-root CAR +const fixtureStr = "3aa265726f6f747381d82a58250001711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80b6776657273696f6e012c01711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80ba165646f646779f5" + func TestEOFHandling(t *testing.T) { - // fixture is a clean single-block, single-root CAR - fixture, err := hex.DecodeString("3aa265726f6f747381d82a58250001711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80b6776657273696f6e012c01711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80ba165646f646779f5") + fixture, err := hex.DecodeString(fixtureStr) if err != nil { t.Fatal(err) } diff --git a/ipld/car/fuzz_test.go b/ipld/car/fuzz_test.go new file mode 100644 index 000000000..8ac04bbd0 --- /dev/null +++ b/ipld/car/fuzz_test.go @@ -0,0 +1,35 @@ +//go:build go1.18 +// +build go1.18 + +package car_test + +import ( + "bytes" + "encoding/hex" + "io" + "testing" + + car "github.com/ipld/go-car" +) + +func FuzzCarReader(f *testing.F) { + fixture, err := hex.DecodeString(fixtureStr) + if err != nil { + f.Fatal(err) + } + f.Add(fixture) + + f.Fuzz(func(t *testing.T, data []byte) { + r, err := car.NewCarReader(bytes.NewReader(data)) + if err != nil { + return + } + + for { + _, err = r.Next() + if err == io.EOF { + return + } + } + }) +} diff --git a/ipld/car/testdata/fuzz/FuzzCarReader/21a90a70853c333c6b9ddc133bae39a14164874ed8abdee1f6a6795311a0e546 b/ipld/car/testdata/fuzz/FuzzCarReader/21a90a70853c333c6b9ddc133bae39a14164874ed8abdee1f6a6795311a0e546 new file mode 100644 index 000000000..a7ab1d519 --- /dev/null +++ b/ipld/car/testdata/fuzz/FuzzCarReader/21a90a70853c333c6b9ddc133bae39a14164874ed8abdee1f6a6795311a0e546 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("\xe0\xe0\xe0\xe0\xa7\x06\folLÊ”<#oK\x19g#H\x96\b\xed\xb4*\x8b\x8f\xa8\vgversion\x19") diff --git a/ipld/car/testdata/fuzz/FuzzCarReader/5857e57e4072c6b0d8684030cc13b5570849c89b3dbf5bc0152abc66c9642f3e b/ipld/car/testdata/fuzz/FuzzCarReader/5857e57e4072c6b0d8684030cc13b5570849c89b3dbf5bc0152abc66c9642f3e new file mode 100644 index 000000000..3e680cf60 --- /dev/null +++ b/ipld/car/testdata/fuzz/FuzzCarReader/5857e57e4072c6b0d8684030cc13b5570849c89b3dbf5bc0152abc66c9642f3e @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte(":\xa2eroots\x81\xd80X%\x00\x0100 00000000000000000000000000000000gversion\x01\x010") From 18a60b08fc58cc3cbd8f7d7000b8899fb93a9b89 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 17 Jun 2022 01:04:46 +0200 Subject: [PATCH 3663/3817] fix: v2 don't OOM if the header size is too big This commit was moved from ipld/go-car@bf8f97ae8afb64741de0e3f5cab927fd943523a9 --- ipld/car/v2/internal/carv1/util/util.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ipld/car/v2/internal/carv1/util/util.go b/ipld/car/v2/internal/carv1/util/util.go index 6b9495613..dd543ac50 100644 --- a/ipld/car/v2/internal/carv1/util/util.go +++ b/ipld/car/v2/internal/carv1/util/util.go @@ -1,6 +1,7 @@ package util import ( + "errors" "io" internalio "github.com/ipld/go-car/v2/internal/io" @@ -73,6 +74,11 @@ func LdRead(r io.Reader, zeroLenAsEOF bool) ([]byte, error) { return nil, io.EOF } + const maxAllowedHeaderSize = 1024 * 1024 + if l > maxAllowedHeaderSize { // Don't OOM + return nil, errors.New("invalid input, too big header") + } + buf := make([]byte, l) if _, err := io.ReadFull(r, buf); err != nil { return nil, err From 879fc55928e5739f4b7114c42694ddb8c3c1e9b9 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 17 Jun 2022 01:05:04 +0200 Subject: [PATCH 3664/3817] test: v2 add fuzzing to BlockReader This commit was moved from ipld/go-car@78a5c3c56412afed13be28fd83f21876849732a2 --- ipld/car/v2/fuzz_test.go | 58 +++++++++++++++++++ ...1ba9f5138a38c882a7fa06456595998e740a9f5a14 | 2 + 2 files changed, 60 insertions(+) create mode 100644 ipld/car/v2/fuzz_test.go create mode 100644 ipld/car/v2/testdata/fuzz/FuzzBlockReader/c3c7eedeb4968a5b3131371ba9f5138a38c882a7fa06456595998e740a9f5a14 diff --git a/ipld/car/v2/fuzz_test.go b/ipld/car/v2/fuzz_test.go new file mode 100644 index 000000000..82155ae9e --- /dev/null +++ b/ipld/car/v2/fuzz_test.go @@ -0,0 +1,58 @@ +//go:build go1.18 +// +build go1.18 + +package car_test + +import ( + "bytes" + "encoding/hex" + "io" + "os" + "path/filepath" + "testing" + + car "github.com/ipld/go-car/v2" +) + +// v1FixtureStr is a clean carv1 single-block, single-root CAR +const v1FixtureStr = "3aa265726f6f747381d82a58250001711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80b6776657273696f6e012c01711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80ba165646f646779f5" + +func FuzzBlockReader(f *testing.F) { + fixture, err := hex.DecodeString(v1FixtureStr) + if err != nil { + f.Fatal(err) + } + f.Add(fixture) + files, err := filepath.Glob("testdata/*.car") + if err != nil { + f.Fatal(err) + } + for _, fname := range files { + func() { + file, err := os.Open(fname) + if err != nil { + f.Fatal(err) + } + defer file.Close() + data, err := io.ReadAll(file) + if err != nil { + f.Fatal(err) + } + f.Add(data) + }() + } + + f.Fuzz(func(t *testing.T, data []byte) { + r, err := car.NewBlockReader(bytes.NewReader(data)) + if err != nil { + return + } + + for { + _, err = r.Next() + if err == io.EOF { + return + } + } + }) +} diff --git a/ipld/car/v2/testdata/fuzz/FuzzBlockReader/c3c7eedeb4968a5b3131371ba9f5138a38c882a7fa06456595998e740a9f5a14 b/ipld/car/v2/testdata/fuzz/FuzzBlockReader/c3c7eedeb4968a5b3131371ba9f5138a38c882a7fa06456595998e740a9f5a14 new file mode 100644 index 000000000..ae9262d49 --- /dev/null +++ b/ipld/car/v2/testdata/fuzz/FuzzBlockReader/c3c7eedeb4968a5b3131371ba9f5138a38c882a7fa06456595998e740a9f5a14 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("\xff\x80\xaa\x95\xa6sion\x01,\x01q\x12 \x15\x1f\xe9\xe7 Date: Fri, 17 Jun 2022 01:50:16 +0200 Subject: [PATCH 3665/3817] fix: v2 don't accept overflowing offsets while reading v2 headers This commit was moved from ipld/go-car@8414960d76acbe27f340ddc9668f2cb69a9581d0 --- ipld/car/v2/car.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/ipld/car/v2/car.go b/ipld/car/v2/car.go index f2885d9d6..194731363 100644 --- a/ipld/car/v2/car.go +++ b/ipld/car/v2/car.go @@ -2,6 +2,7 @@ package car import ( "encoding/binary" + "errors" "io" ) @@ -166,8 +167,16 @@ func (h *Header) ReadFrom(r io.Reader) (int64, error) { if err != nil { return n, err } - h.DataOffset = binary.LittleEndian.Uint64(buf[:8]) - h.DataSize = binary.LittleEndian.Uint64(buf[8:16]) - h.IndexOffset = binary.LittleEndian.Uint64(buf[16:]) + dataOffset := binary.LittleEndian.Uint64(buf[:8]) + dataSize := binary.LittleEndian.Uint64(buf[8:16]) + indexOffset := binary.LittleEndian.Uint64(buf[16:]) + if int64(dataOffset) < 0 || + int64(dataSize) < 0 || + int64(indexOffset) < 0 { + return n, errors.New("malformed car, overflowing offsets") + } + h.DataOffset = dataOffset + h.DataSize = dataSize + h.IndexOffset = indexOffset return n, nil } From a1f8ca251b06853c2bc64a8cb601fe6b951e77bc Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 17 Jun 2022 01:23:10 +0200 Subject: [PATCH 3666/3817] test: v2 add fuzzing to Reader This commit was moved from ipld/go-car@b80929dd7ee763597b6d1a2a4b01bf5af176973b --- ipld/car/v2/fuzz_test.go | 25 ++++++++++++++++++- ...9ac8fb6717ecba6e7e591a1ab111514f30e4b3594e | 2 ++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 ipld/car/v2/testdata/fuzz/FuzzReader/e1d7f87ee37f48386642fa9ac8fb6717ecba6e7e591a1ab111514f30e4b3594e diff --git a/ipld/car/v2/fuzz_test.go b/ipld/car/v2/fuzz_test.go index 82155ae9e..a11dafceb 100644 --- a/ipld/car/v2/fuzz_test.go +++ b/ipld/car/v2/fuzz_test.go @@ -12,12 +12,13 @@ import ( "testing" car "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/index" ) // v1FixtureStr is a clean carv1 single-block, single-root CAR const v1FixtureStr = "3aa265726f6f747381d82a58250001711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80b6776657273696f6e012c01711220151fe9e73c6267a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80ba165646f646779f5" -func FuzzBlockReader(f *testing.F) { +func seedWithCarFiles(f *testing.F) { fixture, err := hex.DecodeString(v1FixtureStr) if err != nil { f.Fatal(err) @@ -41,6 +42,10 @@ func FuzzBlockReader(f *testing.F) { f.Add(data) }() } +} + +func FuzzBlockReader(f *testing.F) { + seedWithCarFiles(f) f.Fuzz(func(t *testing.T, data []byte) { r, err := car.NewBlockReader(bytes.NewReader(data)) @@ -56,3 +61,21 @@ func FuzzBlockReader(f *testing.F) { } }) } + +func FuzzReader(f *testing.F) { + seedWithCarFiles(f) + + f.Fuzz(func(t *testing.T, data []byte) { + subject, err := car.NewReader(bytes.NewReader(data)) + if err != nil { + return + } + + subject.Roots() + ir := subject.IndexReader() + if ir != nil { + index.ReadFrom(ir) + } + car.GenerateIndex(subject.DataReader()) + }) +} diff --git a/ipld/car/v2/testdata/fuzz/FuzzReader/e1d7f87ee37f48386642fa9ac8fb6717ecba6e7e591a1ab111514f30e4b3594e b/ipld/car/v2/testdata/fuzz/FuzzReader/e1d7f87ee37f48386642fa9ac8fb6717ecba6e7e591a1ab111514f30e4b3594e new file mode 100644 index 000000000..36168e1c6 --- /dev/null +++ b/ipld/car/v2/testdata/fuzz/FuzzReader/e1d7f87ee37f48386642fa9ac8fb6717ecba6e7e591a1ab111514f30e4b3594e @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("0\xa1gversion\x0200000000000000000000000\xb70000000\x8100000000") From 5cc8a7442863a905979855873bf0cc89326d363f Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 17 Jun 2022 01:56:12 +0200 Subject: [PATCH 3667/3817] fix: v2 don't allocate indexes too big This commit was moved from ipld/go-car@0b68762f86708992ed3267075e6eeca4f6757a70 --- ipld/car/v2/index/indexsorted.go | 5 +++++ ...1356ad2fe2217292374cd93b65a482fec3a43e6021fe87f5607bd8857 | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 ipld/car/v2/testdata/fuzz/FuzzReader/fe93cec1356ad2fe2217292374cd93b65a482fec3a43e6021fe87f5607bd8857 diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index 2c05a9227..8eb0651ed 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -3,6 +3,7 @@ package index import ( "bytes" "encoding/binary" + "errors" "fmt" "io" "sort" @@ -67,6 +68,10 @@ func (s *singleWidthIndex) Unmarshal(r io.Reader) error { if err := binary.Read(r, binary.LittleEndian, &s.len); err != nil { return err } + const maxSingleWidthIndexSize = 1024 * 1024 + if s.len > maxSingleWidthIndexSize { + return errors.New("single width index is too big") + } s.index = make([]byte, s.len) s.len /= uint64(s.width) _, err := io.ReadFull(r, s.index) diff --git a/ipld/car/v2/testdata/fuzz/FuzzReader/fe93cec1356ad2fe2217292374cd93b65a482fec3a43e6021fe87f5607bd8857 b/ipld/car/v2/testdata/fuzz/FuzzReader/fe93cec1356ad2fe2217292374cd93b65a482fec3a43e6021fe87f5607bd8857 new file mode 100644 index 000000000..1552def12 --- /dev/null +++ b/ipld/car/v2/testdata/fuzz/FuzzReader/fe93cec1356ad2fe2217292374cd93b65a482fec3a43e6021fe87f5607bd8857 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("\n\xa1gversion\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x00\x00\x00\x00\x1c\x01\x00\x00\x00\x00\x00\x00O\x01\x00\x00\x00\x00\x00\x00:\xa2eroots\x81\xd8*X%\x00\x01p\x12 \n4Hq\xde\xe6\xc7T \x7fYl\xfc\x95\xae)Ô \xa2\x1ep#\x92Z]a% mÔª\xb4gversion\x010\x01U\x12 \xa9H\x90O/\x0fG\x9b\x8f\x81\x97iK0\x18K\r.\xd1\xc1\xcd*\x1e\xc0\xfb\x85Ò™\xa1\x92\xa4Ghello \x80\xffrld\nY\x01p\x12 \xcb\xce\x128i\xf8U\xaf\x03\xccIu,\x1b\x05\x83 \xb7\xb8\xac\x1c\xeb|\x8eJ\xcbg\x1d\xf6y\x81\x12/\n$\x01U\x12 \xa9H\x90O/\x0fG\x9b\x8f\x81\x97iK0\x18K\r.\xd1\xc1\xcd*\x1e\xc0\xfb\x85Ò™\xa1\x92\xa4G\x12\x05b.txt\x18\f\n\x02\b\x01U\x01p\x12 \n4Hq\xde\xe6\xc7T \x7fYl\xfc\x95\xae)Ô \xa2\x1ep#\x92Z]a% mÔª\xb4\x12+\n$\x01p\x12 \xcb\xce\x128i\xf8U\xaf\x03\xccIu,\x1b\x05\x83 \xb7\xb8\xac\x1c\xeb|\x8eJ\xcbg\x1d\xf6y\x81\x12\x01a\x18\x00\n\x02\b\x01\x81\b\x12\x00\x00\n4Hq\xde\xe6\xc7T \x7fYl\xfc\x95\xae)Ô \xa2\x1ep#\x92Z]a% \x86\x86\x86\x86\x86\x86\x86\x00\x00\x00\x00\x00\xa9H\x90O/\x0fG\x9b\x8f\x81\x97iK0\x18K\r.\xd1\xc1\xcd*\x1e\xc0\xfb\x85Ò™\xa1\x92\xa4G;\x00\x00\x00\x00\x00\x00\x00\xcb\xce\x128i\xf8U\xaf\x03\xccIu,\x1b\x05\x83 \xb7\xb8\xac\x1c\xeb|\x8eJ\xcbg\x1d\xf6y\x81l\x00\x00\x00\x00\x00\x00\x00") From f4e13fd728624eeb00f5a6f588bc27347a021f2e Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 17 Jun 2022 02:01:37 +0200 Subject: [PATCH 3668/3817] fix: v2 don't divide by zero in width indexes This commit was moved from ipld/go-car@c43fcbca574d4d73454670e76b8f9a6abb1a4624 --- ipld/car/v2/index/indexsorted.go | 3 +++ ...d07c02728da32cdaa17e13646f587ea278d888a655f683d6e42f19c0316 | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 ipld/car/v2/testdata/fuzz/FuzzReader/c640dd07c02728da32cdaa17e13646f587ea278d888a655f683d6e42f19c0316 diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index 8eb0651ed..9e58a7cbb 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -65,6 +65,9 @@ func (s *singleWidthIndex) Unmarshal(r io.Reader) error { if err := binary.Read(r, binary.LittleEndian, &s.width); err != nil { return err } + if s.width == 0 { + return errors.New("malformed car width index cannot be 0") + } if err := binary.Read(r, binary.LittleEndian, &s.len); err != nil { return err } diff --git a/ipld/car/v2/testdata/fuzz/FuzzReader/c640dd07c02728da32cdaa17e13646f587ea278d888a655f683d6e42f19c0316 b/ipld/car/v2/testdata/fuzz/FuzzReader/c640dd07c02728da32cdaa17e13646f587ea278d888a655f683d6e42f19c0316 new file mode 100644 index 000000000..5af80cf1e --- /dev/null +++ b/ipld/car/v2/testdata/fuzz/FuzzReader/c640dd07c02728da32cdaa17e13646f587ea278d888a655f683d6e42f19c0316 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("0\xa1gversion\x0200000000000000000000000000000000O\x01\x00\x00\x00\x00\x00\x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\x81\b0000000000000000\x00\x00\x00\x0000\x00\x00\x00\x00\x00\x00") From 99417ba82c3a76fa39997510b524a5f7919f9dce Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 17 Jun 2022 03:27:53 +0200 Subject: [PATCH 3669/3817] test: v2 add fuzzing of the index This commit was moved from ipld/go-car@a504086a5bb8bea3d32c61f089aef77910e37717 --- ipld/car/v2/fuzz_test.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/ipld/car/v2/fuzz_test.go b/ipld/car/v2/fuzz_test.go index a11dafceb..2b74fb6b7 100644 --- a/ipld/car/v2/fuzz_test.go +++ b/ipld/car/v2/fuzz_test.go @@ -79,3 +79,36 @@ func FuzzReader(f *testing.F) { car.GenerateIndex(subject.DataReader()) }) } + +func FuzzIndex(f *testing.F) { + files, err := filepath.Glob("testdata/*.car") + if err != nil { + f.Fatal(err) + } + for _, fname := range files { + func() { + file, err := os.Open(fname) + if err != nil { + f.Fatal(err) + } + defer file.Close() + subject, err := car.NewReader(file) + if err != nil { + return + } + index := subject.IndexReader() + if index == nil { + return + } + data, err := io.ReadAll(index) + if err != nil { + f.Fatal(err) + } + f.Add(data) + }() + } + + f.Fuzz(func(t *testing.T, data []byte) { + index.ReadFrom(bytes.NewReader(data)) + }) +} From 9ec5f980c4182ad4421b3de714d30440dfd5ba4c Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 17 Jun 2022 03:36:50 +0200 Subject: [PATCH 3670/3817] ci: add fuzzing on CI This commit was moved from ipld/go-car@32c1008b892a5b3b25fdc2001f6a132637008315 --- ipld/car/.github/workflows/go-fuzz.yml | 50 ++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 ipld/car/.github/workflows/go-fuzz.yml diff --git a/ipld/car/.github/workflows/go-fuzz.yml b/ipld/car/.github/workflows/go-fuzz.yml new file mode 100644 index 000000000..4b51f69c1 --- /dev/null +++ b/ipld/car/.github/workflows/go-fuzz.yml @@ -0,0 +1,50 @@ +on: [push, pull_request] +name: Go Fuzz + +jobs: + v1: + strategy: + fail-fast: true + matrix: + target: [ "CarReader" ] + runs-on: ubuntu-latest + name: Fuzz V1 ${{ matrix.target }} + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + - uses: actions/setup-go@v2 + with: + go-version: 1.18.x + - name: Go information + run: | + go version + go env + - name: Run Fuzzing for 1m + uses: protocol/multiple-go-modules@v1.2 + with: + run: go test -v -fuzz=Fuzz${{ matrix.target }} -fuzztime=1m . + v2: + strategy: + fail-fast: true + matrix: + target: [ "BlockReader", "Reader", "Index" ] + runs-on: ubuntu-latest + name: Fuzz V2 ${{ matrix.target }} + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + - uses: actions/setup-go@v2 + with: + go-version: 1.18.x + - name: Go information + run: | + go version + go env + - name: Run Fuzzing for 1m + uses: protocol/multiple-go-modules@v1.2 + with: + run: | + cd v2 + go test -v -fuzz=Fuzz${{ matrix.target }} -fuzztime=1m . From 821a3d4362f7489ae4cc919c42458a46417d56aa Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 17 Jun 2022 19:57:56 +0200 Subject: [PATCH 3671/3817] feat: Refactor indexes to put storage considerations on consumers There is no way I can make a safe implementation of the parser by slurping thing into memory, indexes people use are just too big. So I made a new API which force consumers to manage that. They can choose to use a bytes.Reader, *os.File, mmaped thing, ... This commit was moved from ipld/go-car@3923d315f1083ce25ee4e790287cc5702a381b13 --- ipld/car/v2/blockstore/insertionindex.go | 24 +++ ipld/car/v2/blockstore/readonly.go | 13 +- ipld/car/v2/blockstore/readwrite.go | 23 ++- ipld/car/v2/blockstore/readwrite_test.go | 23 +-- ipld/car/v2/fuzz_test.go | 10 +- ipld/car/v2/index/index.go | 50 ++++-- ipld/car/v2/index/indexsorted.go | 165 +++++++++++++++--- ipld/car/v2/index/indexsorted_test.go | 3 +- ipld/car/v2/index/mhindexsorted.go | 59 ++++++- ipld/car/v2/index/mhindexsorted_test.go | 2 +- ipld/car/v2/index/testutil/equal_index.go | 65 +++++++ ipld/car/v2/index_gen_test.go | 7 +- ipld/car/v2/internal/errsort/search.go | 54 ++++++ ipld/car/v2/internal/io/fullReaderAt.go | 20 +++ ipld/car/v2/internal/io/offset_read_seeker.go | 124 ++++++++++--- ipld/car/v2/reader.go | 4 +- ipld/car/v2/reader_test.go | 11 +- ipld/car/v2/writer_test.go | 3 +- 18 files changed, 549 insertions(+), 111 deletions(-) create mode 100644 ipld/car/v2/index/testutil/equal_index.go create mode 100644 ipld/car/v2/internal/errsort/search.go create mode 100644 ipld/car/v2/internal/io/fullReaderAt.go diff --git a/ipld/car/v2/blockstore/insertionindex.go b/ipld/car/v2/blockstore/insertionindex.go index e8575ee1d..95971aa21 100644 --- a/ipld/car/v2/blockstore/insertionindex.go +++ b/ipld/car/v2/blockstore/insertionindex.go @@ -9,6 +9,7 @@ import ( "github.com/ipfs/go-cid" "github.com/ipld/go-car/v2/index" + internalio "github.com/ipld/go-car/v2/internal/io" "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" "github.com/petar/GoLLRB/llrb" @@ -121,6 +122,20 @@ func (ii *insertionIndex) Marshal(w io.Writer) (uint64, error) { return l, err } +func (ii *insertionIndex) ForEach(f func(multihash.Multihash, uint64) error) error { + var errr error + ii.items.AscendGreaterOrEqual(ii.items.Min(), func(i llrb.Item) bool { + r := i.(recordDigest).Record + err := f(r.Cid.Hash(), r.Offset) + if err != nil { + errr = err + return false + } + return true + }) + return errr +} + func (ii *insertionIndex) Unmarshal(r io.Reader) error { var length int64 if err := binary.Read(r, binary.LittleEndian, &length); err != nil { @@ -137,6 +152,15 @@ func (ii *insertionIndex) Unmarshal(r io.Reader) error { return nil } +func (ii *insertionIndex) UnmarshalLazyRead(r io.ReaderAt) (int64, error) { + rdr := internalio.NewOffsetReadSeeker(r, 0) + err := ii.Unmarshal(rdr) + if err != nil { + return 0, err + } + return rdr.Seek(0, io.SeekCurrent) +} + func (ii *insertionIndex) Codec() multicodec.Code { return insertionIndexCodec } diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index f0a15e782..141088b71 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -182,8 +182,11 @@ func OpenReadOnly(path string, opts ...carv2.Option) (*ReadOnly, error) { } func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) { - bcid, data, err := util.ReadNode(internalio.NewOffsetReadSeeker(b.backing, idx), b.opts.ZeroLengthSectionAsEOF) - return bcid, data, err + r, err := internalio.NewOffsetReadSeekerWithError(b.backing, idx) + if err != nil { + return cid.Cid{}, nil, err + } + return util.ReadNode(r, b.opts.ZeroLengthSectionAsEOF) } // DeleteBlock is unsupported and always errors. @@ -441,7 +444,11 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { } } - thisItemForNxt := rdr.Offset() + thisItemForNxt, err := rdr.Seek(0, io.SeekCurrent) + if err != nil { + maybeReportError(ctx, err) + return + } _, c, err := cid.CidFromReader(rdr) if err != nil { maybeReportError(ctx, err) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 090633c05..de43999f7 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -86,12 +86,12 @@ func AllowDuplicatePuts(allow bool) carv2.Option { // successfully. On resumption the roots argument and WithDataPadding option must match the // previous instantiation of ReadWrite blockstore that created the file. More explicitly, the file // resuming from must: -// 1. start with a complete CARv2 car.Pragma. -// 2. contain a complete CARv1 data header with root CIDs matching the CIDs passed to the -// constructor, starting at offset optionally padded by WithDataPadding, followed by zero or -// more complete data sections. If any corrupt data sections are present the resumption will fail. -// Note, if set previously, the blockstore must use the same WithDataPadding option as before, -// since this option is used to locate the CARv1 data payload. +// 1. start with a complete CARv2 car.Pragma. +// 2. contain a complete CARv1 data header with root CIDs matching the CIDs passed to the +// constructor, starting at offset optionally padded by WithDataPadding, followed by zero or +// more complete data sections. If any corrupt data sections are present the resumption will fail. +// Note, if set previously, the blockstore must use the same WithDataPadding option as before, +// since this option is used to locate the CARv1 data payload. // // Note, resumption should be used with WithCidDeduplication, so that blocks that are successfully // written into the file are not re-written. Unless, the user explicitly wants duplicate blocks. @@ -139,7 +139,10 @@ func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.Option) (*ReadWri offset = 0 } rwbs.dataWriter = internalio.NewOffsetWriter(rwbs.f, offset) - v1r := internalio.NewOffsetReadSeeker(rwbs.f, offset) + v1r, err := internalio.NewOffsetReadSeekerWithError(rwbs.f, offset) + if err != nil { + return nil, err + } rwbs.ronly.backing = v1r rwbs.ronly.idx = rwbs.idx rwbs.ronly.carv2Closer = rwbs.f @@ -190,7 +193,11 @@ func (b *ReadWrite) resumeWithRoots(v2 bool, roots []cid.Cid) error { // Check if file was finalized by trying to read the CARv2 header. // We check because if finalized the CARv1 reader behaviour needs to be adjusted since // EOF will not signify end of CARv1 payload. i.e. index is most likely present. - _, err = headerInFile.ReadFrom(internalio.NewOffsetReadSeeker(b.f, carv2.PragmaSize)) + r, err := internalio.NewOffsetReadSeekerWithError(b.f, carv2.PragmaSize) + if err != nil { + return err + } + _, err = headerInFile.ReadFrom(r) // If reading CARv2 header succeeded, and CARv1 offset in header is not zero then the file is // most-likely finalized. Check padding and truncate the file to remove index. diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 86ab06ea1..bc78083ab 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -842,20 +842,15 @@ func TestOpenReadWrite_WritesIdentityCIDsWhenOptionIsEnabled(t *testing.T) { expectedOffset := len(object) + 1 // Assert index is iterable and has exactly one record with expected multihash and offset. - switch idx := gotIdx.(type) { - case index.IterableIndex: - var i int - err := idx.ForEach(func(mh multihash.Multihash, offset uint64) error { - i++ - require.Equal(t, idmh, mh) - require.Equal(t, uint64(expectedOffset), offset) - return nil - }) - require.NoError(t, err) - require.Equal(t, 1, i) - default: - require.Failf(t, "unexpected index type", "wanted %v but got %v", multicodec.CarMultihashIndexSorted, idx.Codec()) - } + var count int + err = gotIdx.ForEach(func(mh multihash.Multihash, offset uint64) error { + count++ + require.Equal(t, idmh, mh) + require.Equal(t, uint64(expectedOffset), offset) + return nil + }) + require.NoError(t, err) + require.Equal(t, 1, count) } func TestOpenReadWrite_ErrorsWhenWritingTooLargeOfACid(t *testing.T) { diff --git a/ipld/car/v2/fuzz_test.go b/ipld/car/v2/fuzz_test.go index 2b74fb6b7..8187457b5 100644 --- a/ipld/car/v2/fuzz_test.go +++ b/ipld/car/v2/fuzz_test.go @@ -96,11 +96,15 @@ func FuzzIndex(f *testing.F) { if err != nil { return } - index := subject.IndexReader() - if index == nil { + indexRdr := subject.IndexReader() + if indexRdr == nil { return } - data, err := io.ReadAll(index) + _, n, err := index.ReadFromWithSize(indexRdr) + if err != nil { + return + } + data, err := io.ReadAll(io.NewSectionReader(indexRdr, 0, n)) if err != nil { f.Fatal(err) } diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 10195b43a..204bc1221 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -45,8 +45,14 @@ type ( // Marshal encodes the index in serial form. Marshal(w io.Writer) (uint64, error) // Unmarshal decodes the index from its serial form. + // Deprecated: This function is slurpy and will copy everything into memory. Unmarshal(r io.Reader) error + // UnmarshalLazyRead is the safe alternative to to Unmarshal. + // Instead of slurping it will keep a reference to the the io.ReaderAt passed in + // and ask for data as needed. + UnmarshalLazyRead(r io.ReaderAt) (indexSize int64, err error) + // Load inserts a number of records into the index. // Note that Index will load all given records. Any filtering of the records such as // exclusion of CIDs with multihash.IDENTITY code must occur prior to calling this function. @@ -66,18 +72,6 @@ type ( // meaning that no callbacks happen, // ErrNotFound is returned. GetAll(cid.Cid, func(uint64) bool) error - } - - // IterableIndex extends Index in cases where the Index is able to - // provide an iterator for getting the list of all multihashes in the - // index. - // - // Note that it is possible for an index to contain multiple offsets for - // a given multihash. - // - // See: IterableIndex.ForEach, Index.GetAll. - IterableIndex interface { - Index // ForEach takes a callback function that will be called // on each entry in the index. The arguments to the callback are @@ -93,6 +87,12 @@ type ( // The order of calls to the given function is deterministic, but entirely index-specific. ForEach(func(multihash.Multihash, uint64) error) error } + + // IterableIndex is an index which support iterating over it's elements + // Deprecated: IterableIndex has been moved into Index. Just use Index now. + IterableIndex interface { + Index + } ) // GetFirst is a wrapper over Index.GetAll, returning the offset for the first @@ -136,18 +136,30 @@ func WriteTo(idx Index, w io.Writer) (uint64, error) { // ReadFrom reads index from r. // The reader decodes the index by reading the first byte to interpret the encoding. // Returns error if the encoding is not known. -func ReadFrom(r io.Reader) (Index, error) { - code, err := varint.ReadUvarint(internalio.ToByteReader(r)) +func ReadFrom(r io.ReaderAt) (Index, error) { + idx, _, err := ReadFromWithSize(r) + return idx, err +} + +// ReadFromWithSize is just like ReadFrom but return the size of the Index. +// The size is only valid when err != nil. +func ReadFromWithSize(r io.ReaderAt) (Index, int64, error) { + code, err := varint.ReadUvarint(internalio.NewOffsetReadSeeker(r, 0)) if err != nil { - return nil, err + return nil, 0, err } codec := multicodec.Code(code) idx, err := New(codec) if err != nil { - return nil, err + return nil, 0, err + } + rdr, err := internalio.NewOffsetReadSeekerWithError(r, int64(varint.UvarintSize(code))) + if err != nil { + return nil, 0, err } - if err := idx.Unmarshal(r); err != nil { - return nil, err + n, err := idx.UnmarshalLazyRead(rdr) + if err != nil { + return nil, 0, err } - return idx, nil + return idx, n, nil } diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index 9e58a7cbb..16367ed52 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -8,12 +8,19 @@ import ( "io" "sort" + "github.com/ipld/go-car/v2/internal/errsort" + internalio "github.com/ipld/go-car/v2/internal/io" "github.com/multiformats/go-multicodec" "github.com/ipfs/go-cid" "github.com/multiformats/go-multihash" ) +type sizedReaderAt interface { + io.ReaderAt + Size() int64 +} + var _ Index = (*multiWidthIndex)(nil) type ( @@ -25,7 +32,7 @@ type ( singleWidthIndex struct { width uint32 len uint64 // in struct, len is #items. when marshaled, it's saved as #bytes. - index []byte + index sizedReaderAt } multiWidthIndex map[uint32]singleWidthIndex ) @@ -53,36 +60,72 @@ func (s *singleWidthIndex) Marshal(w io.Writer) (uint64, error) { return 0, err } l += 4 - if err := binary.Write(w, binary.LittleEndian, int64(len(s.index))); err != nil { + sz := s.index.Size() + if err := binary.Write(w, binary.LittleEndian, sz); err != nil { return l, err } l += 8 - n, err := w.Write(s.index) + n, err := io.Copy(w, io.NewSectionReader(s.index, 0, sz)) return l + uint64(n), err } +// Unmarshal decodes the index from its serial form. +// Deprecated: This function is slurpy and will copy the index in memory. func (s *singleWidthIndex) Unmarshal(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &s.width); err != nil { + var width uint32 + if err := binary.Read(r, binary.LittleEndian, &width); err != nil { return err } - if s.width == 0 { - return errors.New("malformed car width index cannot be 0") + var dataLen uint64 + if err := binary.Read(r, binary.LittleEndian, &dataLen); err != nil { + return err } - if err := binary.Read(r, binary.LittleEndian, &s.len); err != nil { + + if err := s.checkUnmarshalLengths(width, dataLen, 0); err != nil { + return err + } + + buf := make([]byte, dataLen) + if _, err := io.ReadFull(r, buf); err != nil { return err } - const maxSingleWidthIndexSize = 1024 * 1024 - if s.len > maxSingleWidthIndexSize { - return errors.New("single width index is too big") + s.index = bytes.NewReader(buf) + return nil +} + +func (s *singleWidthIndex) UnmarshalLazyRead(r io.ReaderAt) (indexSize int64, err error) { + var b [12]byte + _, err = internalio.FullReadAt(r, b[:], 0) + if err != nil { + return 0, err + } + + width := binary.LittleEndian.Uint32(b[:4]) + dataLen := binary.LittleEndian.Uint64(b[4:12]) + if err := s.checkUnmarshalLengths(width, dataLen, uint64(len(b))); err != nil { + return 0, err } - s.index = make([]byte, s.len) - s.len /= uint64(s.width) - _, err := io.ReadFull(r, s.index) - return err + s.index = io.NewSectionReader(r, int64(len(b)), int64(dataLen)) + return int64(dataLen) + int64(len(b)), nil } -func (s *singleWidthIndex) Less(i int, digest []byte) bool { - return bytes.Compare(digest[:], s.index[i*int(s.width):((i+1)*int(s.width)-8)]) <= 0 +func (s *singleWidthIndex) checkUnmarshalLengths(width uint32, dataLen, extra uint64) error { + if width <= 8 { + return errors.New("malformed index; width must be bigger than 8") + } + if int32(width) < 0 { + return errors.New("index too big; singleWidthIndex width is overflowing int32") + } + oldDataLen, dataLen := dataLen, dataLen+extra + if oldDataLen > dataLen { + return errors.New("index too big; singleWidthIndex len is overflowing") + } + if int64(dataLen) < 0 { + return errors.New("index too big; singleWidthIndex len is overflowing int64") + } + s.width = width + s.len = dataLen / uint64(width) + return nil } func (s *singleWidthIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { @@ -94,18 +137,35 @@ func (s *singleWidthIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { } func (s *singleWidthIndex) getAll(d []byte, fn func(uint64) bool) error { - idx := sort.Search(int(s.len), func(i int) bool { - return s.Less(i, d) + digestLen := int64(s.width) - 8 + b := make([]byte, digestLen) + idxI, err := errsort.Search(int(s.len), func(i int) (bool, error) { + digestStart := int64(i) * int64(s.width) + _, err := internalio.FullReadAt(s.index, b, digestStart) + if err != nil { + return false, err + } + return bytes.Compare(d, b) <= 0, nil }) + if err != nil { + return err + } + idx := int64(idxI) var any bool for ; uint64(idx) < s.len; idx++ { - digestStart := idx * int(s.width) - offsetEnd := (idx + 1) * int(s.width) + digestStart := idx * int64(s.width) + offsetEnd := digestStart + int64(s.width) digestEnd := offsetEnd - 8 - if bytes.Equal(d[:], s.index[digestStart:digestEnd]) { + digestLen := digestEnd - digestStart + b := make([]byte, offsetEnd-digestStart) + _, err := internalio.FullReadAt(s.index, b, digestStart) + if err != nil { + return err + } + if bytes.Equal(d, b[:digestLen]) { any = true - offset := binary.LittleEndian.Uint64(s.index[digestEnd:offsetEnd]) + offset := binary.LittleEndian.Uint64(b[digestLen:]) if !fn(offset) { // User signalled to stop searching; therefore, break. break @@ -139,13 +199,19 @@ func (s *singleWidthIndex) Load(items []Record) error { } func (s *singleWidthIndex) forEachDigest(f func(digest []byte, offset uint64) error) error { - segmentCount := len(s.index) / int(s.width) - for i := 0; i < segmentCount; i++ { - digestStart := i * int(s.width) - offsetEnd := (i + 1) * int(s.width) + segmentCount := s.index.Size() / int64(s.width) + for i := int64(0); i < segmentCount; i++ { + digestStart := i * int64(s.width) + offsetEnd := digestStart + int64(s.width) digestEnd := offsetEnd - 8 - digest := s.index[digestStart:digestEnd] - offset := binary.LittleEndian.Uint64(s.index[digestEnd:offsetEnd]) + digestLen := digestEnd - digestStart + b := make([]byte, offsetEnd-digestStart) + _, err := internalio.FullReadAt(s.index, b, digestStart) + if err != nil { + return err + } + digest := b[:digestLen] + offset := binary.LittleEndian.Uint64(b[digestLen:]) if err := f(digest, offset); err != nil { return err } @@ -212,6 +278,37 @@ func (m *multiWidthIndex) Unmarshal(r io.Reader) error { return nil } +func (m *multiWidthIndex) UnmarshalLazyRead(r io.ReaderAt) (sum int64, err error) { + var b [4]byte + _, err = internalio.FullReadAt(r, b[:], 0) + if err != nil { + return 0, err + } + count := binary.LittleEndian.Uint32(b[:4]) + if int32(count) < 0 { + return 0, errors.New("index too big; multiWidthIndex count is overflowing int32") + } + sum += int64(len(b)) + for ; count > 0; count-- { + s := singleWidthIndex{} + or, err := internalio.NewOffsetReadSeekerWithError(r, sum) + if err != nil { + return 0, err + } + n, err := s.UnmarshalLazyRead(or) + if err != nil { + return 0, err + } + oldSum := sum + sum += n + if sum < oldSum { + return 0, errors.New("index too big; multiWidthIndex len is overflowing int64") + } + (*m)[s.width] = s + } + return sum, nil +} + func (m *multiWidthIndex) Load(items []Record) error { // Split cids on their digest length idxs := make(map[int][]digestRecord) @@ -241,13 +338,23 @@ func (m *multiWidthIndex) Load(items []Record) error { s := singleWidthIndex{ width: uint32(rcrdWdth), len: uint64(len(lst)), - index: compact, + index: bytes.NewReader(compact), } (*m)[uint32(width)+8] = s } return nil } +func (m *multiWidthIndex) ForEach(f func(multihash.Multihash, uint64) error) error { + return m.forEachDigest(func(digest []byte, offset uint64) error { + mh, err := multihash.Cast(digest) + if err != nil { + return err + } + return f(mh, offset) + }) +} + func (m *multiWidthIndex) forEachDigest(f func(digest []byte, offset uint64) error) error { sizes := make([]uint32, 0, len(*m)) for k := range *m { diff --git a/ipld/car/v2/index/indexsorted_test.go b/ipld/car/v2/index/indexsorted_test.go index 5c1ee4495..f7e038a01 100644 --- a/ipld/car/v2/index/indexsorted_test.go +++ b/ipld/car/v2/index/indexsorted_test.go @@ -1,6 +1,7 @@ package index import ( + "bytes" "encoding/binary" "testing" @@ -51,7 +52,7 @@ func TestSingleWidthIndex_GetAll(t *testing.T) { subject := &singleWidthIndex{ width: 9, len: uint64(l), - index: buf, + index: bytes.NewReader(buf), } var foundCount int diff --git a/ipld/car/v2/index/mhindexsorted.go b/ipld/car/v2/index/mhindexsorted.go index 55975b8e5..0200f7007 100644 --- a/ipld/car/v2/index/mhindexsorted.go +++ b/ipld/car/v2/index/mhindexsorted.go @@ -2,17 +2,18 @@ package index import ( "encoding/binary" + "errors" "io" "sort" "github.com/ipfs/go-cid" + internalio "github.com/ipld/go-car/v2/internal/io" "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" ) var ( - _ Index = (*MultihashIndexSorted)(nil) - _ IterableIndex = (*MultihashIndexSorted)(nil) + _ Index = (*MultihashIndexSorted)(nil) ) type ( @@ -46,6 +47,29 @@ func (m *multiWidthCodedIndex) Unmarshal(r io.Reader) error { return m.multiWidthIndex.Unmarshal(r) } +func (m *multiWidthCodedIndex) UnmarshalLazyRead(r io.ReaderAt) (int64, error) { + var b [8]byte + _, err := internalio.FullReadAt(r, b[:], 0) + if err != nil { + return 0, err + } + m.code = binary.LittleEndian.Uint64(b[:8]) + rdr, err := internalio.NewOffsetReadSeekerWithError(r, int64(len(b))) + if err != nil { + return 0, err + } + sum, err := m.multiWidthIndex.UnmarshalLazyRead(rdr) + if err != nil { + return 0, err + } + oldSum := sum + sum += int64(len(b)) + if sum < oldSum { + return 0, errors.New("index too big; multiWidthCodedIndex len is overflowing") + } + return sum, nil +} + func (m *multiWidthCodedIndex) forEach(f func(mh multihash.Multihash, offset uint64) error) error { return m.multiWidthIndex.forEachDigest(func(digest []byte, offset uint64) error { mh, err := multihash.Encode(digest, m.code) @@ -107,6 +131,37 @@ func (m *MultihashIndexSorted) Unmarshal(r io.Reader) error { return nil } +func (m *MultihashIndexSorted) UnmarshalLazyRead(r io.ReaderAt) (sum int64, err error) { + var b [4]byte + _, err = internalio.FullReadAt(r, b[:], 0) + if err != nil { + return 0, err + } + sum += int64(len(b)) + count := binary.LittleEndian.Uint32(b[:4]) + if int32(count) < 0 { + return 0, errors.New("index too big; MultihashIndexSorted count is overflowing int32") + } + for ; count > 0; count-- { + mwci := newMultiWidthCodedIndex() + or, err := internalio.NewOffsetReadSeekerWithError(r, sum) + if err != nil { + return 0, err + } + n, err := mwci.UnmarshalLazyRead(or) + if err != nil { + return 0, err + } + oldSum := sum + sum += n + if sum < oldSum { + return 0, errors.New("index too big; MultihashIndexSorted sum is overflowing int64") + } + m.put(mwci) + } + return sum, nil +} + func (m *MultihashIndexSorted) put(mwci *multiWidthCodedIndex) { (*m)[mwci.code] = mwci } diff --git a/ipld/car/v2/index/mhindexsorted_test.go b/ipld/car/v2/index/mhindexsorted_test.go index 79fc9c5f0..d97d1e6f1 100644 --- a/ipld/car/v2/index/mhindexsorted_test.go +++ b/ipld/car/v2/index/mhindexsorted_test.go @@ -58,7 +58,7 @@ func TestMultiWidthCodedIndex_StableIterate(t *testing.T) { err = subject.Load(records) require.NoError(t, err) - iterable := subject.(index.IterableIndex) + iterable := subject.(index.Index) mh := make([]multihash.Multihash, 0, len(records)) require.NoError(t, iterable.ForEach(func(m multihash.Multihash, _ uint64) error { mh = append(mh, m) diff --git a/ipld/car/v2/index/testutil/equal_index.go b/ipld/car/v2/index/testutil/equal_index.go new file mode 100644 index 000000000..1314d25e8 --- /dev/null +++ b/ipld/car/v2/index/testutil/equal_index.go @@ -0,0 +1,65 @@ +package testutil + +import ( + "sync" + "testing" + + "github.com/ipld/go-car/v2/index" + + "github.com/multiformats/go-multihash" + "github.com/stretchr/testify/require" +) + +// insertUint64 perform one round of insertion sort on the last element +func insertUint64(s []uint64) { + switch len(s) { + case 0, 1: + return + default: + cur := s[len(s)-1] + for j := len(s) - 1; j > 0; { + j-- + if cur >= s[j] { + s[j+1] = cur + break + } + s[j+1] = s[j] + } + } +} + +func AssertIndenticalIndexes(t *testing.T, a, b index.Index) { + var wg sync.Mutex + wg.Lock() + // key is multihash.Multihash.HexString + var aCount uint + aMap := make(map[string][]uint64) + go func() { + defer wg.Unlock() + a.ForEach(func(mh multihash.Multihash, off uint64) error { + aCount++ + str := mh.HexString() + slice, _ := aMap[str] + slice = append(slice, off) + insertUint64(slice) + aMap[str] = slice + return nil + }) + }() + + var bCount uint + bMap := make(map[string][]uint64) + a.ForEach(func(mh multihash.Multihash, off uint64) error { + bCount++ + str := mh.HexString() + slice, _ := bMap[str] + slice = append(slice, off) + insertUint64(slice) + bMap[str] = slice + return nil + }) + wg.Lock() + + require.Equal(t, aCount, bCount) + require.Equal(t, aMap, bMap) +} diff --git a/ipld/car/v2/index_gen_test.go b/ipld/car/v2/index_gen_test.go index 11f0530fb..64a73adcf 100644 --- a/ipld/car/v2/index_gen_test.go +++ b/ipld/car/v2/index_gen_test.go @@ -8,6 +8,7 @@ import ( "github.com/ipfs/go-cid" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-car/v2/index/testutil" "github.com/ipld/go-car/v2/internal/carv1" internalio "github.com/ipld/go-car/v2/internal/io" "github.com/multiformats/go-multicodec" @@ -103,7 +104,11 @@ func TestGenerateIndex(t *testing.T) { if tt.wantIndexer != nil { want = tt.wantIndexer(t) } - require.Equal(t, want, got) + if want == nil { + require.Nil(t, got) + } else { + testutil.AssertIndenticalIndexes(t, want, got) + } } }) t.Run("GenerateIndexFromFile_"+tt.name, func(t *testing.T) { diff --git a/ipld/car/v2/internal/errsort/search.go b/ipld/car/v2/internal/errsort/search.go new file mode 100644 index 000000000..fb8861794 --- /dev/null +++ b/ipld/car/v2/internal/errsort/search.go @@ -0,0 +1,54 @@ +/* +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package errsort + +// Search is like sort.Search but accepts an erroring closure. +// If it errors the search is terminated immediately +func Search(n int, f func(int) (bool, error)) (int, error) { + // Define f(-1) == false and f(n) == true. + // Invariant: f(i-1) == false, f(j) == true. + i, j := 0, n + for i < j { + h := int(uint(i+j) >> 1) // avoid overflow when computing h + // i ≤ h < j + less, err := f(h) + if err != nil { + return 0, err + } + if !less { + i = h + 1 // preserves f(i-1) == false + } else { + j = h // preserves f(j) == true + } + } + // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i. + return i, nil +} diff --git a/ipld/car/v2/internal/io/fullReaderAt.go b/ipld/car/v2/internal/io/fullReaderAt.go new file mode 100644 index 000000000..57f266859 --- /dev/null +++ b/ipld/car/v2/internal/io/fullReaderAt.go @@ -0,0 +1,20 @@ +package io + +import "io" + +func FullReadAt(r io.ReaderAt, b []byte, off int64) (sum int64, err error) { + for int64(len(b)) > sum { + n, err := r.ReadAt(b[sum:], off+sum) + sum += int64(n) + if err != nil { + if err == io.EOF { + if sum < int64(len(b)) { + return sum, io.ErrUnexpectedEOF + } + return sum, nil + } + return sum, err + } + } + return sum, nil +} diff --git a/ipld/car/v2/internal/io/offset_read_seeker.go b/ipld/car/v2/internal/io/offset_read_seeker.go index 4b701351f..bbdcf4c63 100644 --- a/ipld/car/v2/internal/io/offset_read_seeker.go +++ b/ipld/car/v2/internal/io/offset_read_seeker.go @@ -1,63 +1,123 @@ package io -import "io" +import ( + "errors" + "io" +) var ( - _ io.ReaderAt = (*OffsetReadSeeker)(nil) - _ io.ReadSeeker = (*OffsetReadSeeker)(nil) + _ io.ReaderAt = (*offsetReadSeeker)(nil) + _ io.ReadSeeker = (*offsetReadSeeker)(nil) ) -// OffsetReadSeeker implements Read, and ReadAt on a section +// offsetReadSeeker implements Read, and ReadAt on a section // of an underlying io.ReaderAt. -// The main difference between io.SectionReader and OffsetReadSeeker is that +// The main difference between io.SectionReader and offsetReadSeeker is that // NewOffsetReadSeeker does not require the user to know the number of readable bytes. // // It also partially implements Seek, where the implementation panics if io.SeekEnd is passed. -// This is because, OffsetReadSeeker does not know the end of the file therefore cannot seek relative +// This is because, offsetReadSeeker does not know the end of the file therefore cannot seek relative // to it. -type OffsetReadSeeker struct { +type offsetReadSeeker struct { r io.ReaderAt base int64 off int64 + b [1]byte // avoid alloc in ReadByte +} + +type ReadSeekerAt interface { + io.Reader + io.ReaderAt + io.Seeker + io.ByteReader } -// NewOffsetReadSeeker returns an OffsetReadSeeker that reads from r +// NewOffsetReadSeeker returns an ReadSeekerAt that reads from r // starting offset offset off and stops with io.EOF when r reaches its end. // The Seek function will panic if whence io.SeekEnd is passed. -func NewOffsetReadSeeker(r io.ReaderAt, off int64) *OffsetReadSeeker { - return &OffsetReadSeeker{r, off, off} +func NewOffsetReadSeeker(r io.ReaderAt, off int64) ReadSeekerAt { + nr, err := NewOffsetReadSeekerWithError(r, off) + if err != nil { + return erroringReader{err} + } + return nr +} + +func NewOffsetReadSeekerWithError(r io.ReaderAt, off int64) (ReadSeekerAt, error) { + if or, ok := r.(*offsetReadSeeker); ok { + oldBase := or.base + newBase := or.base + off + if newBase < oldBase { + return nil, errors.New("NewOffsetReadSeeker overflow int64") + } + return &offsetReadSeeker{ + r: or.r, + base: newBase, + off: newBase, + }, nil + } + return &offsetReadSeeker{ + r: r, + base: off, + off: off, + }, nil } -func (o *OffsetReadSeeker) Read(p []byte) (n int, err error) { +func (o *offsetReadSeeker) Read(p []byte) (n int, err error) { n, err = o.r.ReadAt(p, o.off) - o.off += int64(n) + oldOffset := o.off + off := oldOffset + int64(n) + if off < oldOffset { + return 0, errors.New("ReadAt offset overflow") + } + o.off = off return } -func (o *OffsetReadSeeker) ReadAt(p []byte, off int64) (n int, err error) { +func (o *offsetReadSeeker) ReadAt(p []byte, off int64) (n int, err error) { if off < 0 { return 0, io.EOF } + oldOffset := off off += o.base + if off < oldOffset { + return 0, errors.New("ReadAt offset overflow") + } return o.r.ReadAt(p, off) } -func (o *OffsetReadSeeker) ReadByte() (byte, error) { - b := []byte{0} - _, err := o.Read(b) - return b[0], err +func (o *offsetReadSeeker) ReadByte() (byte, error) { + _, err := o.Read(o.b[:]) + return o.b[0], err } -func (o *OffsetReadSeeker) Offset() int64 { +func (o *offsetReadSeeker) Offset() int64 { return o.off } -func (o *OffsetReadSeeker) Seek(offset int64, whence int) (int64, error) { +func (o *offsetReadSeeker) Seek(offset int64, whence int) (int64, error) { switch whence { case io.SeekStart: - o.off = offset + o.base + oldOffset := offset + off := offset + o.base + if off < oldOffset { + return 0, errors.New("Seek offset overflow") + } + o.off = off case io.SeekCurrent: - o.off += offset + oldOffset := o.off + if offset < 0 { + if -offset > oldOffset { + return 0, errors.New("Seek offset underflow") + } + o.off = oldOffset + offset + } else { + off := oldOffset + offset + if off < oldOffset { + return 0, errors.New("Seek offset overflow") + } + o.off = off + } case io.SeekEnd: panic("unsupported whence: SeekEnd") } @@ -65,6 +125,26 @@ func (o *OffsetReadSeeker) Seek(offset int64, whence int) (int64, error) { } // Position returns the current position of this reader relative to the initial offset. -func (o *OffsetReadSeeker) Position() int64 { +func (o *offsetReadSeeker) Position() int64 { return o.off - o.base } + +type erroringReader struct { + err error +} + +func (e erroringReader) Read(_ []byte) (int, error) { + return 0, e.err +} + +func (e erroringReader) ReadAt(_ []byte, n int64) (int, error) { + return 0, e.err +} + +func (e erroringReader) ReadByte() (byte, error) { + return 0, e.err +} + +func (e erroringReader) Seek(_ int64, _ int) (int64, error) { + return 0, e.err +} diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 9394a736b..6651b2907 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -109,11 +109,11 @@ func (r *Reader) DataReader() SectionReader { // IndexReader provides an io.Reader containing the index for the data payload if the index is // present. Otherwise, returns nil. // Note, this function will always return nil if the backing payload represents a CARv1. -func (r *Reader) IndexReader() io.Reader { +func (r *Reader) IndexReader() io.ReaderAt { if r.Version == 1 || !r.Header.HasIndex() { return nil } - return internalio.NewOffsetReadSeeker(r.r, int64(r.Header.IndexOffset)) + return io.NewSectionReader(r.r, int64(r.Header.IndexOffset), int64(r.Header.DataSize)-int64(r.Header.IndexOffset)) } // Close closes the underlying reader if it was opened by OpenReader. diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go index a0c6e3cda..f653b60b2 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/reader_test.go @@ -7,6 +7,7 @@ import ( carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-car/v2/index/testutil" "github.com/ipld/go-car/v2/internal/carv1" "github.com/stretchr/testify/require" ) @@ -173,7 +174,7 @@ func TestReader_WithCarV2Consistency(t *testing.T) { require.NoError(t, err) wantIndex, err := carv2.GenerateIndex(subject.DataReader()) require.NoError(t, err) - require.Equal(t, wantIndex, gotIndex) + testutil.AssertIndenticalIndexes(t, wantIndex, gotIndex) }) } } @@ -186,8 +187,8 @@ func TestOpenReader_DoesNotPanicForReadersCreatedBeforeClosure(t *testing.T) { require.NoError(t, subject.Close()) buf := make([]byte, 1) - panicTest := func(r io.Reader) { - _, err := r.Read(buf) + panicTest := func(r io.ReaderAt) { + _, err := r.ReadAt(buf, 0) require.EqualError(t, err, "mmap: closed") } @@ -203,8 +204,8 @@ func TestOpenReader_DoesNotPanicForReadersCreatedAfterClosure(t *testing.T) { iReaderAfterClosure := subject.IndexReader() buf := make([]byte, 1) - panicTest := func(r io.Reader) { - _, err := r.Read(buf) + panicTest := func(r io.ReaderAt) { + _, err := r.ReadAt(buf, 0) require.EqualError(t, err, "mmap: closed") } diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index 11b5ff129..2044e17ba 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-car/v2/index/testutil" "github.com/ipld/go-car/v2/internal/carv1" "github.com/stretchr/testify/require" @@ -56,7 +57,7 @@ func TestWrapV1(t *testing.T) { require.NoError(t, err) gotIdx, err := index.ReadFrom(subject.IndexReader()) require.NoError(t, err) - require.Equal(t, wantIdx, gotIdx) + testutil.AssertIndenticalIndexes(t, wantIdx, gotIdx) } func TestExtractV1(t *testing.T) { From 6431951d859a67854b0d410a0e6e22183ab52a99 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 21 Jun 2022 17:30:11 +1000 Subject: [PATCH 3672/3817] fix index comparisons This commit was moved from ipld/go-car@bad4539d0c65c708ec1cecac74cff65cb561ce3c --- ipld/car/v2/blockstore/readwrite_test.go | 3 ++- ipld/car/v2/index/example_test.go | 24 ++++++++++++++++++----- ipld/car/v2/index/index_test.go | 3 ++- ipld/car/v2/index/testutil/equal_index.go | 8 +++++--- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index bc78083ab..0ccd53e09 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -21,6 +21,7 @@ import ( carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/blockstore" "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-car/v2/index/testutil" "github.com/ipld/go-car/v2/internal/carv1" "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" @@ -519,7 +520,7 @@ func TestBlockstoreResumption(t *testing.T) { require.NoError(t, err) wantIdx, err := carv2.GenerateIndex(v2r.DataReader()) require.NoError(t, err) - require.Equal(t, wantIdx, gotIdx) + testutil.AssertIndenticalIndexes(t, wantIdx, gotIdx) } func TestBlockstoreResumptionIsSupportedOnFinalizedFile(t *testing.T) { diff --git a/ipld/car/v2/index/example_test.go b/ipld/car/v2/index/example_test.go index 3e484afb9..c6f83ea28 100644 --- a/ipld/car/v2/index/example_test.go +++ b/ipld/car/v2/index/example_test.go @@ -5,10 +5,10 @@ import ( "io" "io/ioutil" "os" - "reflect" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" + "github.com/multiformats/go-multihash" ) // ExampleReadFrom unmarshalls an index from an indexed CARv2 file, and for each root CID prints the @@ -94,13 +94,27 @@ func ExampleWriteTo() { panic(err) } - // Expect indices to be equal. - if reflect.DeepEqual(idx, reReadIdx) { - fmt.Printf("Saved index file matches the index embedded in CARv2 at %v.\n", src) - } else { + // Expect indices to be equal - collect all of the multihashes and their + // offsets from the first and compare to the second + mha := make(map[string]uint64, 0) + _ = idx.ForEach(func(mh multihash.Multihash, off uint64) error { + mha[mh.HexString()] = off + return nil + }) + var count int + _ = reReadIdx.ForEach(func(mh multihash.Multihash, off uint64) error { + count++ + if expectedOffset, ok := mha[mh.HexString()]; !ok || expectedOffset != off { + panic("expected to get the same index as the CARv2 file") + } + return nil + }) + if count != len(mha) { panic("expected to get the same index as the CARv2 file") } + fmt.Printf("Saved index file matches the index embedded in CARv2 at %v.\n", src) + // Output: // Saved index file matches the index embedded in CARv2 at ../testdata/sample-wrapped-v2.car. } diff --git a/ipld/car/v2/index/index_test.go b/ipld/car/v2/index/index_test.go index 267beb034..883bd5d60 100644 --- a/ipld/car/v2/index/index_test.go +++ b/ipld/car/v2/index/index_test.go @@ -8,6 +8,7 @@ import ( "testing" blocks "github.com/ipfs/go-block-format" + "github.com/ipld/go-car/v2/index/testutil" "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/carv1/util" "github.com/multiformats/go-multicodec" @@ -112,7 +113,7 @@ func TestWriteTo(t *testing.T) { require.NoError(t, err) // Assert they are equal - require.Equal(t, wantIdx, gotIdx) + testutil.AssertIndenticalIndexes(t, wantIdx, gotIdx) } func TestMarshalledIndexStartsWithCodec(t *testing.T) { diff --git a/ipld/car/v2/index/testutil/equal_index.go b/ipld/car/v2/index/testutil/equal_index.go index 1314d25e8..798985b94 100644 --- a/ipld/car/v2/index/testutil/equal_index.go +++ b/ipld/car/v2/index/testutil/equal_index.go @@ -4,12 +4,14 @@ import ( "sync" "testing" - "github.com/ipld/go-car/v2/index" - "github.com/multiformats/go-multihash" "github.com/stretchr/testify/require" ) +type Index interface { + ForEach(func(multihash.Multihash, uint64) error) error +} + // insertUint64 perform one round of insertion sort on the last element func insertUint64(s []uint64) { switch len(s) { @@ -28,7 +30,7 @@ func insertUint64(s []uint64) { } } -func AssertIndenticalIndexes(t *testing.T, a, b index.Index) { +func AssertIndenticalIndexes(t *testing.T, a, b Index) { var wg sync.Mutex wg.Lock() // key is multihash.Multihash.HexString From 58e8f1a2c2f65d93766fbda72cc24bcdddd2916d Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 21 Jun 2022 19:24:31 +1000 Subject: [PATCH 3673/3817] fix: revert to internalio.NewOffsetReadSeeker in Reader#IndexReader This commit was moved from ipld/go-car@a74d510a3200aac5ed8c25ed9a1b8232c89f625c --- ipld/car/v2/reader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 6651b2907..fa94e6724 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -113,7 +113,7 @@ func (r *Reader) IndexReader() io.ReaderAt { if r.Version == 1 || !r.Header.HasIndex() { return nil } - return io.NewSectionReader(r.r, int64(r.Header.IndexOffset), int64(r.Header.DataSize)-int64(r.Header.IndexOffset)) + return internalio.NewOffsetReadSeeker(r.r, int64(r.Header.IndexOffset)) } // Close closes the underlying reader if it was opened by OpenReader. From 48d19ae14a69e272d87a34a211648861a4908a64 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 21 Jun 2022 19:29:52 +1000 Subject: [PATCH 3674/3817] fix: staticcheck catches This commit was moved from ipld/go-car@e64294da9c86bc64665a152fd73e0b68293b00f8 --- ipld/car/v2/index/mhindexsorted_test.go | 5 ++--- ipld/car/v2/index/testutil/equal_index.go | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ipld/car/v2/index/mhindexsorted_test.go b/ipld/car/v2/index/mhindexsorted_test.go index d97d1e6f1..7704d3a23 100644 --- a/ipld/car/v2/index/mhindexsorted_test.go +++ b/ipld/car/v2/index/mhindexsorted_test.go @@ -58,16 +58,15 @@ func TestMultiWidthCodedIndex_StableIterate(t *testing.T) { err = subject.Load(records) require.NoError(t, err) - iterable := subject.(index.Index) mh := make([]multihash.Multihash, 0, len(records)) - require.NoError(t, iterable.ForEach(func(m multihash.Multihash, _ uint64) error { + require.NoError(t, subject.ForEach(func(m multihash.Multihash, _ uint64) error { mh = append(mh, m) return nil })) for i := 0; i < 10; i++ { candidate := make([]multihash.Multihash, 0, len(records)) - require.NoError(t, iterable.ForEach(func(m multihash.Multihash, _ uint64) error { + require.NoError(t, subject.ForEach(func(m multihash.Multihash, _ uint64) error { candidate = append(candidate, m) return nil })) diff --git a/ipld/car/v2/index/testutil/equal_index.go b/ipld/car/v2/index/testutil/equal_index.go index 798985b94..c5da756a5 100644 --- a/ipld/car/v2/index/testutil/equal_index.go +++ b/ipld/car/v2/index/testutil/equal_index.go @@ -41,7 +41,7 @@ func AssertIndenticalIndexes(t *testing.T, a, b Index) { a.ForEach(func(mh multihash.Multihash, off uint64) error { aCount++ str := mh.HexString() - slice, _ := aMap[str] + slice := aMap[str] slice = append(slice, off) insertUint64(slice) aMap[str] = slice @@ -54,7 +54,7 @@ func AssertIndenticalIndexes(t *testing.T, a, b Index) { a.ForEach(func(mh multihash.Multihash, off uint64) error { bCount++ str := mh.HexString() - slice, _ := bMap[str] + slice := bMap[str] slice = append(slice, off) insertUint64(slice) bMap[str] = slice From 6481ccfa60fa4f5505a0e51169c13bd659160387 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 21 Jun 2022 19:34:06 +1000 Subject: [PATCH 3675/3817] fix: don't use multiple-go-modules for fuzzing This commit was moved from ipld/go-car@5b911130533375f5d0874db6ed37bd874385927a --- ipld/car/.github/workflows/go-fuzz.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/ipld/car/.github/workflows/go-fuzz.yml b/ipld/car/.github/workflows/go-fuzz.yml index 4b51f69c1..3aa7f8530 100644 --- a/ipld/car/.github/workflows/go-fuzz.yml +++ b/ipld/car/.github/workflows/go-fuzz.yml @@ -21,7 +21,6 @@ jobs: go version go env - name: Run Fuzzing for 1m - uses: protocol/multiple-go-modules@v1.2 with: run: go test -v -fuzz=Fuzz${{ matrix.target }} -fuzztime=1m . v2: @@ -43,7 +42,6 @@ jobs: go version go env - name: Run Fuzzing for 1m - uses: protocol/multiple-go-modules@v1.2 with: run: | cd v2 From 674eb25762b078d8b3be931d267e5278f85f73b1 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 21 Jun 2022 14:28:43 +1000 Subject: [PATCH 3676/3817] fix: use CidFromReader() which has overread and OOM protection This commit was moved from ipld/go-car@79c91e22e8ade045b8e99b0af53f110e9f0df971 --- ipld/car/util/util.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/car/util/util.go b/ipld/car/util/util.go index fbd6a86bc..59aa5e283 100644 --- a/ipld/car/util/util.go +++ b/ipld/car/util/util.go @@ -26,7 +26,7 @@ type BytesReader interface { io.ByteReader } -// TODO: this belongs in the go-cid package +// Deprecated: ReadCid shouldn't be used directly, use CidFromReader from go-cid func ReadCid(buf []byte) (cid.Cid, int, error) { if len(buf) >= 2 && bytes.Equal(buf[:2], cidv0Pref) { i := 34 @@ -70,7 +70,7 @@ func ReadNode(br *bufio.Reader) (cid.Cid, []byte, error) { return cid.Cid{}, nil, err } - c, n, err := ReadCid(data) + n, c, err := cid.CidFromReader(bytes.NewReader(data)) if err != nil { return cid.Cid{}, nil, err } From 2eead95fa71c23cf49a9ba4b290855550fddd0a5 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 21 Jun 2022 14:45:58 +1000 Subject: [PATCH 3677/3817] feat: MaxAllowedSectionSize default to 32M This commit was moved from ipld/go-car@1f15d9d38efead479b1a494ecd5651df3eaeb64d --- ipld/car/util/util.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/ipld/car/util/util.go b/ipld/car/util/util.go index 59aa5e283..af2f38e42 100644 --- a/ipld/car/util/util.go +++ b/ipld/car/util/util.go @@ -12,12 +12,11 @@ import ( mh "github.com/multiformats/go-multihash" ) -// MaxAllowedHeaderSize hint about how big the header red are allowed to be. -// This value is a hint to avoid OOMs, a parser that cannot OOM because it is -// streaming for example, isn't forced to follow that value. -// Deprecated: You should use v2#NewReader instead since it allows for options -// to be passed in. -var MaxAllowedHeaderSize uint = 1024 +// MaxAllowedSectionSize dictates the maximum number of bytes that a CARv1 header +// or section is allowed to occupy without causing a decode to error. +// This cannot be supplied as an option, only adjusted as a global. You should +// use v2#NewReader instead since it allows for options to be passed in. +var MaxAllowedSectionSize uint = 32 << 20 // 32MiB var cidv0Pref = []byte{0x12, 0x20} @@ -124,8 +123,8 @@ func LdRead(r *bufio.Reader) ([]byte, error) { return nil, err } - if l > uint64(MaxAllowedHeaderSize) { // Don't OOM - return nil, errors.New("malformed car; header is bigger than util.MaxAllowedHeaderSize") + if l > uint64(MaxAllowedSectionSize) { // Don't OOM + return nil, errors.New("malformed car; header is bigger than util.MaxAllowedSectionSize") } buf := make([]byte, l) From aaed1d13f399c8063438183e186fbeed37ed762f Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 21 Jun 2022 16:56:14 +1000 Subject: [PATCH 3678/3817] feat: MaxAllowed{Header,Section}Size option This commit was moved from ipld/go-car@d91369e78463b55dc2c7a20f0dfe6d03adc48937 --- ipld/car/v2/block_reader.go | 10 +++-- ipld/car/v2/block_reader_test.go | 55 ++++++++++++++++++++++++ ipld/car/v2/blockstore/readonly.go | 12 +++--- ipld/car/v2/blockstore/readwrite.go | 6 +-- ipld/car/v2/blockstore/readwrite_test.go | 2 +- ipld/car/v2/car_test.go | 2 +- ipld/car/v2/index/index_test.go | 2 +- ipld/car/v2/index_gen.go | 12 +++--- ipld/car/v2/index_gen_test.go | 2 +- ipld/car/v2/internal/carv1/car.go | 34 +++++++++------ ipld/car/v2/internal/carv1/util/util.go | 14 +++--- ipld/car/v2/options.go | 48 ++++++++++++++++++++- ipld/car/v2/options_test.go | 12 ++++-- ipld/car/v2/reader.go | 9 ++-- ipld/car/v2/writer.go | 12 +++--- 15 files changed, 176 insertions(+), 56 deletions(-) diff --git a/ipld/car/v2/block_reader.go b/ipld/car/v2/block_reader.go index 456d8d317..a74e3996a 100644 --- a/ipld/car/v2/block_reader.go +++ b/ipld/car/v2/block_reader.go @@ -30,9 +30,11 @@ type BlockReader struct { // // See BlockReader.Next func NewBlockReader(r io.Reader, opts ...Option) (*BlockReader, error) { + options := ApplyOptions(opts...) + // Read CARv1 header or CARv2 pragma. // Both are a valid CARv1 header, therefore are read as such. - pragmaOrV1Header, err := carv1.ReadHeader(r) + pragmaOrV1Header, err := carv1.ReadHeader(r, options.MaxAllowedHeaderSize) if err != nil { return nil, err } @@ -40,7 +42,7 @@ func NewBlockReader(r io.Reader, opts ...Option) (*BlockReader, error) { // Populate the block reader version and options. br := &BlockReader{ Version: pragmaOrV1Header.Version, - opts: ApplyOptions(opts...), + opts: options, } // Expect either version 1 or 2. @@ -92,7 +94,7 @@ func NewBlockReader(r io.Reader, opts ...Option) (*BlockReader, error) { br.r = io.LimitReader(r, dataSize) // Populate br.Roots by reading the inner CARv1 data payload header. - header, err := carv1.ReadHeader(br.r) + header, err := carv1.ReadHeader(br.r, options.MaxAllowedHeaderSize) if err != nil { return nil, err } @@ -120,7 +122,7 @@ func NewBlockReader(r io.Reader, opts ...Option) (*BlockReader, error) { // immediately upon encountering a zero-length section without reading any further bytes from the // underlying io.Reader. func (br *BlockReader) Next() (blocks.Block, error) { - c, data, err := util.ReadNode(br.r, br.opts.ZeroLengthSectionAsEOF) + c, data, err := util.ReadNode(br.r, br.opts.ZeroLengthSectionAsEOF, br.opts.MaxAllowedSectionSize) if err != nil { return nil, err } diff --git a/ipld/car/v2/block_reader_test.go b/ipld/car/v2/block_reader_test.go index afffc806c..384a6ed88 100644 --- a/ipld/car/v2/block_reader_test.go +++ b/ipld/car/v2/block_reader_test.go @@ -1,12 +1,17 @@ package car_test import ( + "bytes" + "encoding/hex" "io" "os" "testing" + "github.com/ipfs/go-cid" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/internal/carv1" + mh "github.com/multiformats/go-multihash" + "github.com/multiformats/go-varint" "github.com/stretchr/testify/require" ) @@ -104,6 +109,56 @@ func TestBlockReader_WithCarV1Consistency(t *testing.T) { } } +func TestMaxSectionLength(t *testing.T) { + // headerHex is the zero-roots CARv1 header + const headerHex = "11a265726f6f7473806776657273696f6e01" + headerBytes, _ := hex.DecodeString(headerHex) + // 8 MiB block of zeros + block := make([]byte, 8<<20) + // CID for that block + pfx := cid.NewPrefixV1(cid.Raw, mh.SHA2_256) + cid, err := pfx.Sum(block) + require.NoError(t, err) + + // construct CAR + var buf bytes.Buffer + buf.Write(headerBytes) + buf.Write(varint.ToUvarint(uint64(len(cid.Bytes()) + len(block)))) + buf.Write(cid.Bytes()) + buf.Write(block) + + // try to read it + car, err := carv2.NewBlockReader(bytes.NewReader(buf.Bytes())) + require.NoError(t, err) + // error should occur on first section read + _, err = car.Next() + require.EqualError(t, err, "invalid section data, length of read beyond allowable maximum") + + // successful read by expanding the max section size + car, err = carv2.NewBlockReader(bytes.NewReader(buf.Bytes()), carv2.MaxAllowedSectionSize((8<<20)+40)) + require.NoError(t, err) + // can now read block and get our 8 MiB zeroed byte array + readBlock, err := car.Next() + require.NoError(t, err) + require.True(t, bytes.Equal(block, readBlock.RawData())) +} + +func TestMaxHeaderLength(t *testing.T) { + // headerHex is the is a 5 root CARv1 header + const headerHex = "de01a265726f6f747385d82a58250001711220785197229dc8bb1152945da58e2348f7e279eeded06cc2ca736d0e879858b501d82a58250001711220785197229dc8bb1152945da58e2348f7e279eeded06cc2ca736d0e879858b501d82a58250001711220785197229dc8bb1152945da58e2348f7e279eeded06cc2ca736d0e879858b501d82a58250001711220785197229dc8bb1152945da58e2348f7e279eeded06cc2ca736d0e879858b501d82a58250001711220785197229dc8bb1152945da58e2348f7e279eeded06cc2ca736d0e879858b5016776657273696f6e01" + headerBytes, _ := hex.DecodeString(headerHex) + c, _ := cid.Decode("bafyreidykglsfhoixmivffc5uwhcgshx4j465xwqntbmu43nb2dzqwfvae") + + // successful read + car, err := carv2.NewBlockReader(bytes.NewReader(headerBytes)) + require.NoError(t, err) + require.ElementsMatch(t, []cid.Cid{c, c, c, c, c}, car.Roots) + + // unsuccessful read, low allowable max header length (length - 3 because there are 2 bytes in the length varint prefix) + _, err = carv2.NewBlockReader(bytes.NewReader(headerBytes), carv2.MaxAllowedHeaderSize(uint64(len(headerBytes)-3))) + require.EqualError(t, err, "invalid header data, length of read beyond allowable maximum") +} + func requireReaderFromPath(t *testing.T, path string) io.Reader { f, err := os.Open(path) require.NoError(t, err) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 141088b71..c7c9127bd 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -98,7 +98,7 @@ func NewReadOnly(backing io.ReaderAt, idx index.Index, opts ...carv2.Option) (*R opts: carv2.ApplyOptions(opts...), } - version, err := readVersion(backing) + version, err := readVersion(backing, opts...) if err != nil { return nil, err } @@ -135,7 +135,7 @@ func NewReadOnly(backing io.ReaderAt, idx index.Index, opts ...carv2.Option) (*R } } -func readVersion(at io.ReaderAt) (uint64, error) { +func readVersion(at io.ReaderAt, opts ...carv2.Option) (uint64, error) { var rr io.Reader switch r := at.(type) { case io.Reader: @@ -143,7 +143,7 @@ func readVersion(at io.ReaderAt) (uint64, error) { default: rr = internalio.NewOffsetReadSeeker(r, 0) } - return carv2.ReadVersion(rr) + return carv2.ReadVersion(rr, opts...) } func generateIndex(at io.ReaderAt, opts ...carv2.Option) (index.Index, error) { @@ -186,7 +186,7 @@ func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) { if err != nil { return cid.Cid{}, nil, err } - return util.ReadNode(r, b.opts.ZeroLengthSectionAsEOF) + return util.ReadNode(r, b.opts.ZeroLengthSectionAsEOF, b.opts.MaxAllowedSectionSize) } // DeleteBlock is unsupported and always errors. @@ -401,7 +401,7 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // TODO we may use this walk for populating the index, and we need to be able to iterate keys in this way somewhere for index generation. In general though, when it's asked for all keys from a blockstore with an index, we should iterate through the index when possible rather than linear reads through the full car. rdr := internalio.NewOffsetReadSeeker(b.backing, 0) - header, err := carv1.ReadHeader(rdr) + header, err := carv1.ReadHeader(rdr, b.opts.MaxAllowedHeaderSize) if err != nil { b.mu.RUnlock() // don't hold the mutex forever return nil, fmt.Errorf("error reading car header: %w", err) @@ -491,7 +491,7 @@ func (b *ReadOnly) HashOnRead(bool) { // Roots returns the root CIDs of the backing CAR. func (b *ReadOnly) Roots() ([]cid.Cid, error) { - header, err := carv1.ReadHeader(internalio.NewOffsetReadSeeker(b.backing, 0)) + header, err := carv1.ReadHeader(internalio.NewOffsetReadSeeker(b.backing, 0), b.opts.MaxAllowedHeaderSize) if err != nil { return nil, fmt.Errorf("error reading car header: %w", err) } diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index de43999f7..0fad9893b 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -169,11 +169,11 @@ func (b *ReadWrite) initWithRoots(v2 bool, roots []cid.Cid) error { return carv1.WriteHeader(&carv1.CarHeader{Roots: roots, Version: 1}, b.dataWriter) } -func (b *ReadWrite) resumeWithRoots(v2 bool, roots []cid.Cid) error { +func (b *ReadWrite) resumeWithRoots(v2 bool, roots []cid.Cid, opts ...carv2.Option) error { // On resumption it is expected that the CARv2 Pragma, and the CARv1 header is successfully written. // Otherwise we cannot resume from the file. // Read pragma to assert if b.f is indeed a CARv2. - version, err := carv2.ReadVersion(b.f) + version, err := carv2.ReadVersion(b.f, opts...) if err != nil { // The file is not a valid CAR file and cannot resume from it. // Or the write must have failed before pragma was written. @@ -224,7 +224,7 @@ func (b *ReadWrite) resumeWithRoots(v2 bool, roots []cid.Cid) error { // Use the given CARv1 padding to instantiate the CARv1 reader on file. v1r := internalio.NewOffsetReadSeeker(b.ronly.backing, 0) - header, err := carv1.ReadHeader(v1r) + header, err := carv1.ReadHeader(v1r, b.opts.MaxAllowedHeaderSize) if err != nil { // Cannot read the CARv1 header; the file is most likely corrupt. return fmt.Errorf("error reading car header: %w", err) diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 0ccd53e09..3dc528665 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -836,7 +836,7 @@ func TestOpenReadWrite_WritesIdentityCIDsWhenOptionIsEnabled(t *testing.T) { require.NoError(t, err) // Determine expected offset as the length of header plus one - header, err := carv1.ReadHeader(r.DataReader()) + header, err := carv1.ReadHeader(r.DataReader(), carv1.DefaultMaxAllowedHeaderSize) require.NoError(t, err) object, err := cbor.DumpObject(header) require.NoError(t, err) diff --git a/ipld/car/v2/car_test.go b/ipld/car/v2/car_test.go index 64519b297..9e113259e 100644 --- a/ipld/car/v2/car_test.go +++ b/ipld/car/v2/car_test.go @@ -37,7 +37,7 @@ func TestCarV2PragmaLength(t *testing.T) { } func TestCarV2PragmaIsValidCarV1Header(t *testing.T) { - v1h, err := carv1.ReadHeader(bytes.NewReader(carv2.Pragma)) + v1h, err := carv1.ReadHeader(bytes.NewReader(carv2.Pragma), carv1.DefaultMaxAllowedHeaderSize) assert.NoError(t, err, "cannot decode pragma as CBOR with CARv1 header structure") assert.Equal(t, &carv1.CarHeader{ Roots: nil, diff --git a/ipld/car/v2/index/index_test.go b/ipld/car/v2/index/index_test.go index 883bd5d60..f895bc2ff 100644 --- a/ipld/car/v2/index/index_test.go +++ b/ipld/car/v2/index/index_test.go @@ -78,7 +78,7 @@ func TestReadFrom(t *testing.T) { require.NoError(t, err) // Read the fame at offset and assert the frame corresponds to the expected block. - gotCid, gotData, err := util.ReadNode(crf, false) + gotCid, gotData, err := util.ReadNode(crf, false, carv1.DefaultMaxAllowedSectionSize) require.NoError(t, err) gotBlock, err := blocks.NewBlockWithCid(gotData, gotCid) require.NoError(t, err) diff --git a/ipld/car/v2/index_gen.go b/ipld/car/v2/index_gen.go index 10c86dc34..33ba7800f 100644 --- a/ipld/car/v2/index_gen.go +++ b/ipld/car/v2/index_gen.go @@ -37,8 +37,11 @@ func GenerateIndex(v1r io.Reader, opts ...Option) (index.Index, error) { // Note, the index is re-generated every time even if r is in CARv2 format and already has an index. // To read existing index when available see ReadOrGenerateIndex. func LoadIndex(idx index.Index, r io.Reader, opts ...Option) error { + // Parse Options. + o := ApplyOptions(opts...) + reader := internalio.ToByteReadSeeker(r) - pragma, err := carv1.ReadHeader(r) + pragma, err := carv1.ReadHeader(r, o.MaxAllowedHeaderSize) if err != nil { return fmt.Errorf("error reading car header: %w", err) } @@ -78,7 +81,7 @@ func LoadIndex(idx index.Index, r io.Reader, opts ...Option) error { dataOffset = int64(v2h.DataOffset) // Read the inner CARv1 header to skip it and sanity check it. - v1h, err := carv1.ReadHeader(reader) + v1h, err := carv1.ReadHeader(reader, o.MaxAllowedHeaderSize) if err != nil { return err } @@ -104,9 +107,6 @@ func LoadIndex(idx index.Index, r io.Reader, opts ...Option) error { // CARv2 header. sectionOffset -= dataOffset - // Parse Options. - o := ApplyOptions(opts...) - records := make([]index.Record, 0) for { // Read the section's length. @@ -188,7 +188,7 @@ func GenerateIndexFromFile(path string, opts ...Option) (index.Index, error) { // given reader to fulfill index lookup. func ReadOrGenerateIndex(rs io.ReadSeeker, opts ...Option) (index.Index, error) { // Read version. - version, err := ReadVersion(rs) + version, err := ReadVersion(rs, opts...) if err != nil { return nil, err } diff --git a/ipld/car/v2/index_gen_test.go b/ipld/car/v2/index_gen_test.go index 64a73adcf..cae76ddf8 100644 --- a/ipld/car/v2/index_gen_test.go +++ b/ipld/car/v2/index_gen_test.go @@ -256,7 +256,7 @@ func generateMultihashSortedIndex(t *testing.T, path string) *index.MultihashInd require.NoError(t, err) t.Cleanup(func() { require.NoError(t, f.Close()) }) reader := internalio.ToByteReadSeeker(f) - header, err := carv1.ReadHeader(reader) + header, err := carv1.ReadHeader(reader, carv1.DefaultMaxAllowedHeaderSize) require.NoError(t, err) require.Equal(t, uint64(1), header.Version) diff --git a/ipld/car/v2/internal/carv1/car.go b/ipld/car/v2/internal/carv1/car.go index 48b7c86be..f62899b71 100644 --- a/ipld/car/v2/internal/carv1/car.go +++ b/ipld/car/v2/internal/carv1/car.go @@ -14,6 +14,9 @@ import ( "github.com/ipfs/go-merkledag" ) +const DefaultMaxAllowedHeaderSize uint64 = 32 << 20 // 32MiB +const DefaultMaxAllowedSectionSize uint64 = 8 << 20 // 8MiB + func init() { cbor.RegisterCborType(CarHeader{}) } @@ -56,9 +59,12 @@ func WriteCar(ctx context.Context, ds format.NodeGetter, roots []cid.Cid, w io.W return nil } -func ReadHeader(r io.Reader) (*CarHeader, error) { - hb, err := util.LdRead(r, false) +func ReadHeader(r io.Reader, maxReadBytes uint64) (*CarHeader, error) { + hb, err := util.LdRead(r, false, maxReadBytes) if err != nil { + if err == util.ErrSectionTooLarge { + err = util.ErrHeaderTooLarge + } return nil, err } @@ -106,21 +112,22 @@ func (cw *carWriter) writeNode(ctx context.Context, nd format.Node) error { } type CarReader struct { - r io.Reader - Header *CarHeader - zeroLenAsEOF bool + r io.Reader + Header *CarHeader + zeroLenAsEOF bool + maxAllowedSectionSize uint64 } func NewCarReaderWithZeroLengthSectionAsEOF(r io.Reader) (*CarReader, error) { - return newCarReader(r, true) + return NewCarReaderWithoutDefaults(r, true, DefaultMaxAllowedHeaderSize, DefaultMaxAllowedSectionSize) } func NewCarReader(r io.Reader) (*CarReader, error) { - return newCarReader(r, false) + return NewCarReaderWithoutDefaults(r, false, DefaultMaxAllowedHeaderSize, DefaultMaxAllowedSectionSize) } -func newCarReader(r io.Reader, zeroLenAsEOF bool) (*CarReader, error) { - ch, err := ReadHeader(r) +func NewCarReaderWithoutDefaults(r io.Reader, zeroLenAsEOF bool, maxAllowedHeaderSize uint64, maxAllowedSectionSize uint64) (*CarReader, error) { + ch, err := ReadHeader(r, maxAllowedHeaderSize) if err != nil { return nil, err } @@ -134,14 +141,15 @@ func newCarReader(r io.Reader, zeroLenAsEOF bool) (*CarReader, error) { } return &CarReader{ - r: r, - Header: ch, - zeroLenAsEOF: zeroLenAsEOF, + r: r, + Header: ch, + zeroLenAsEOF: zeroLenAsEOF, + maxAllowedSectionSize: maxAllowedSectionSize, }, nil } func (cr *CarReader) Next() (blocks.Block, error) { - c, data, err := util.ReadNode(cr.r, cr.zeroLenAsEOF) + c, data, err := util.ReadNode(cr.r, cr.zeroLenAsEOF, cr.maxAllowedSectionSize) if err != nil { return nil, err } diff --git a/ipld/car/v2/internal/carv1/util/util.go b/ipld/car/v2/internal/carv1/util/util.go index dd543ac50..7963812e4 100644 --- a/ipld/car/v2/internal/carv1/util/util.go +++ b/ipld/car/v2/internal/carv1/util/util.go @@ -11,13 +11,16 @@ import ( cid "github.com/ipfs/go-cid" ) +var ErrSectionTooLarge = errors.New("invalid section data, length of read beyond allowable maximum") +var ErrHeaderTooLarge = errors.New("invalid header data, length of read beyond allowable maximum") + type BytesReader interface { io.Reader io.ByteReader } -func ReadNode(r io.Reader, zeroLenAsEOF bool) (cid.Cid, []byte, error) { - data, err := LdRead(r, zeroLenAsEOF) +func ReadNode(r io.Reader, zeroLenAsEOF bool, maxReadBytes uint64) (cid.Cid, []byte, error) { + data, err := LdRead(r, zeroLenAsEOF, maxReadBytes) if err != nil { return cid.Cid{}, nil, err } @@ -62,7 +65,7 @@ func LdSize(d ...[]byte) uint64 { return sum + uint64(s) } -func LdRead(r io.Reader, zeroLenAsEOF bool) ([]byte, error) { +func LdRead(r io.Reader, zeroLenAsEOF bool, maxReadBytes uint64) ([]byte, error) { l, err := varint.ReadUvarint(internalio.ToByteReader(r)) if err != nil { // If the length of bytes read is non-zero when the error is EOF then signal an unclean EOF. @@ -74,9 +77,8 @@ func LdRead(r io.Reader, zeroLenAsEOF bool) ([]byte, error) { return nil, io.EOF } - const maxAllowedHeaderSize = 1024 * 1024 - if l > maxAllowedHeaderSize { // Don't OOM - return nil, errors.New("invalid input, too big header") + if l > maxReadBytes { // Don't OOM + return nil, ErrSectionTooLarge } buf := make([]byte, l) diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go index d2923b214..d2e526c42 100644 --- a/ipld/car/v2/options.go +++ b/ipld/car/v2/options.go @@ -6,11 +6,29 @@ import ( "github.com/ipld/go-car/v2/index" "github.com/ipld/go-ipld-prime/traversal" "github.com/multiformats/go-multicodec" + + "github.com/ipld/go-car/v2/internal/carv1" ) // DefaultMaxIndexCidSize specifies the maximum size in byptes accepted as a section CID by CARv2 index. const DefaultMaxIndexCidSize = 2 << 10 // 2 KiB +// DefaultMaxAllowedHeaderSize specifies the default maximum size that a CARv1 +// decode (including within a CARv2 container) will allow a header to be without +// erroring. This is to prevent OOM errors where a header prefix includes a +// too-large size specifier. +// Currently set to 32 MiB. +const DefaultMaxAllowedHeaderSize = carv1.DefaultMaxAllowedHeaderSize + +// DefaultMaxAllowedHeaderSize specifies the default maximum size that a CARv1 +// decode (including within a CARv2 container) will allow a section to be +// without erroring. This is to prevent OOM errors where a section prefix +// includes a too-large size specifier. +// Typically IPLD blocks should be under 2 MiB (ideally under 1 MiB), so unless +// atypical data is expected, this should not be a large value. +// Currently set to 8 MiB. +const DefaultMaxAllowedSectionSize = carv1.DefaultMaxAllowedSectionSize + // Option describes an option which affects behavior when interacting with CAR files. type Option func(*Options) @@ -42,14 +60,20 @@ type Options struct { MaxTraversalLinks uint64 WriteAsCarV1 bool TraversalPrototypeChooser traversal.LinkTargetNodePrototypeChooser + + MaxAllowedHeaderSize uint64 + MaxAllowedSectionSize uint64 } // ApplyOptions applies given opts and returns the resulting Options. // This function should not be used directly by end users; it's only exposed as a // side effect of Option. func ApplyOptions(opt ...Option) Options { - var opts Options - opts.MaxTraversalLinks = math.MaxInt64 //default: traverse all + opts := Options{ + MaxTraversalLinks: math.MaxInt64, //default: traverse all + MaxAllowedHeaderSize: carv1.DefaultMaxAllowedHeaderSize, + MaxAllowedSectionSize: carv1.DefaultMaxAllowedSectionSize, + } for _, o := range opt { o(&opts) } @@ -128,3 +152,23 @@ func WithTraversalPrototypeChooser(t traversal.LinkTargetNodePrototypeChooser) O o.TraversalPrototypeChooser = t } } + +// MaxAllowedHeaderSize overrides the default maximum size (of 32 MiB) that a +// CARv1 decode (including within a CARv2 container) will allow a header to be +// without erroring. +func MaxAllowedHeaderSize(max uint64) Option { + return func(o *Options) { + o.MaxAllowedHeaderSize = max + } +} + +// MaxAllowedSectionSize overrides the default maximum size (of 8 MiB) that a +// CARv1 decode (including within a CARv2 container) will allow a header to be +// without erroring. +// Typically IPLD blocks should be under 2 MiB (ideally under 1 MiB), so unless +// atypical data is expected, this should not be a large value. +func MaxAllowedSectionSize(max uint64) Option { + return func(o *Options) { + o.MaxAllowedSectionSize = max + } +} diff --git a/ipld/car/v2/options_test.go b/ipld/car/v2/options_test.go index 7e060acf0..6daa473fe 100644 --- a/ipld/car/v2/options_test.go +++ b/ipld/car/v2/options_test.go @@ -12,9 +12,11 @@ import ( func TestApplyOptions_SetsExpectedDefaults(t *testing.T) { require.Equal(t, carv2.Options{ - IndexCodec: multicodec.CarMultihashIndexSorted, - MaxIndexCidSize: carv2.DefaultMaxIndexCidSize, - MaxTraversalLinks: math.MaxInt64, + IndexCodec: multicodec.CarMultihashIndexSorted, + MaxIndexCidSize: carv2.DefaultMaxIndexCidSize, + MaxTraversalLinks: math.MaxInt64, + MaxAllowedHeaderSize: 32 << 20, + MaxAllowedSectionSize: 8 << 20, }, carv2.ApplyOptions()) } @@ -30,6 +32,8 @@ func TestApplyOptions_AppliesOptions(t *testing.T) { BlockstoreAllowDuplicatePuts: true, BlockstoreUseWholeCIDs: true, MaxTraversalLinks: math.MaxInt64, + MaxAllowedHeaderSize: 101, + MaxAllowedSectionSize: 202, }, carv2.ApplyOptions( carv2.UseDataPadding(123), @@ -38,6 +42,8 @@ func TestApplyOptions_AppliesOptions(t *testing.T) { carv2.ZeroLengthSectionAsEOF(true), carv2.MaxIndexCidSize(789), carv2.StoreIdentityCIDs(true), + carv2.MaxAllowedHeaderSize(101), + carv2.MaxAllowedSectionSize(202), blockstore.AllowDuplicatePuts(true), blockstore.UseWholeCIDs(true), )) diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index fa94e6724..40c5d8c8d 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -51,7 +51,7 @@ func NewReader(r io.ReaderAt, opts ...Option) (*Reader, error) { or := internalio.NewOffsetReadSeeker(r, 0) var err error - cr.Version, err = ReadVersion(or) + cr.Version, err = ReadVersion(or, opts...) if err != nil { return nil, err } @@ -75,7 +75,7 @@ func (r *Reader) Roots() ([]cid.Cid, error) { if r.roots != nil { return r.roots, nil } - header, err := carv1.ReadHeader(r.DataReader()) + header, err := carv1.ReadHeader(r.DataReader(), r.opts.MaxAllowedHeaderSize) if err != nil { return nil, err } @@ -126,8 +126,9 @@ func (r *Reader) Close() error { // ReadVersion reads the version from the pragma. // This function accepts both CARv1 and CARv2 payloads. -func ReadVersion(r io.Reader) (uint64, error) { - header, err := carv1.ReadHeader(r) +func ReadVersion(r io.Reader, opts ...Option) (uint64, error) { + o := ApplyOptions(opts...) + header, err := carv1.ReadHeader(r, o.MaxAllowedHeaderSize) if err != nil { return 0, err } diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index e82c4b3b3..f859dabe8 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -105,7 +105,7 @@ func WrapV1(src io.ReadSeeker, dst io.Writer, opts ...Option) error { // for example, it should use copy_file_range on recent Linux versions. // This API should be preferred over copying directly via Reader.DataReader, // as it should allow for better performance while always being at least as efficient. -func ExtractV1File(srcPath, dstPath string) (err error) { +func ExtractV1File(srcPath, dstPath string, opts ...Option) (err error) { src, err := os.Open(srcPath) if err != nil { return err @@ -115,7 +115,7 @@ func ExtractV1File(srcPath, dstPath string) (err error) { defer src.Close() // Detect CAR version. - version, err := ReadVersion(src) + version, err := ReadVersion(src, opts...) if err != nil { return err } @@ -220,7 +220,7 @@ func AttachIndex(path string, idx index.Index, offset uint64) error { // // Note that the roots are only replaced if their total serialized size exactly matches the total // serialized size of existing roots in CAR file. -func ReplaceRootsInFile(path string, roots []cid.Cid) (err error) { +func ReplaceRootsInFile(path string, roots []cid.Cid, opts ...Option) (err error) { f, err := os.OpenFile(path, os.O_RDWR, 0o666) if err != nil { return err @@ -232,8 +232,10 @@ func ReplaceRootsInFile(path string, roots []cid.Cid) (err error) { } }() + options := ApplyOptions(opts...) + // Read header or pragma; note that both are a valid CARv1 header. - header, err := carv1.ReadHeader(f) + header, err := carv1.ReadHeader(f, options.MaxAllowedHeaderSize) if err != nil { return err } @@ -267,7 +269,7 @@ func ReplaceRootsInFile(path string, roots []cid.Cid) (err error) { return err } var innerV1Header *carv1.CarHeader - innerV1Header, err = carv1.ReadHeader(f) + innerV1Header, err = carv1.ReadHeader(f, options.MaxAllowedHeaderSize) if err != nil { return err } From e69b00156536338dc281b4ce7354b42fb1763c31 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Wed, 22 Jun 2022 16:01:51 +1000 Subject: [PATCH 3679/3817] fix: explicitly disable serialization of insertionindex This commit was moved from ipld/go-car@82be1c37758ca012d0d43e141241790225c69474 --- ipld/car/v2/blockstore/insertionindex.go | 44 ++++-------------------- ipld/car/v2/blockstore/readonly.go | 1 + ipld/car/v2/index/index.go | 1 + 3 files changed, 9 insertions(+), 37 deletions(-) diff --git a/ipld/car/v2/blockstore/insertionindex.go b/ipld/car/v2/blockstore/insertionindex.go index 95971aa21..192eb5c34 100644 --- a/ipld/car/v2/blockstore/insertionindex.go +++ b/ipld/car/v2/blockstore/insertionindex.go @@ -2,20 +2,21 @@ package blockstore import ( "bytes" - "encoding/binary" "errors" "fmt" "io" "github.com/ipfs/go-cid" "github.com/ipld/go-car/v2/index" - internalio "github.com/ipld/go-car/v2/internal/io" "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" "github.com/petar/GoLLRB/llrb" - cbor "github.com/whyrusleeping/cbor/go" ) +// This index is intended to be efficient for random-access, in-memory lookups +// and is not intended to be an index type that is attached to a CARv2. +// See flatten() for conversion of this data to a known, existing index type. + var ( errUnsupported = errors.New("not supported") insertionIndexCodec = multicodec.Code(0x300003) @@ -105,21 +106,7 @@ func (ii *insertionIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { } func (ii *insertionIndex) Marshal(w io.Writer) (uint64, error) { - l := uint64(0) - if err := binary.Write(w, binary.LittleEndian, int64(ii.items.Len())); err != nil { - return l, err - } - l += 8 - - var err error - iter := func(i llrb.Item) bool { - if err = cbor.Encode(w, i.(recordDigest).Record); err != nil { - return false - } - return true - } - ii.items.AscendGreaterOrEqual(ii.items.Min(), iter) - return l, err + return 0, fmt.Errorf("unimplemented, index type not intended for serialization") } func (ii *insertionIndex) ForEach(f func(multihash.Multihash, uint64) error) error { @@ -137,28 +124,11 @@ func (ii *insertionIndex) ForEach(f func(multihash.Multihash, uint64) error) err } func (ii *insertionIndex) Unmarshal(r io.Reader) error { - var length int64 - if err := binary.Read(r, binary.LittleEndian, &length); err != nil { - return err - } - d := cbor.NewDecoder(r) - for i := int64(0); i < length; i++ { - var rec index.Record - if err := d.Decode(&rec); err != nil { - return err - } - ii.items.InsertNoReplace(newRecordDigest(rec)) - } - return nil + return fmt.Errorf("unimplemented, index type not intended for deserialization") } func (ii *insertionIndex) UnmarshalLazyRead(r io.ReaderAt) (int64, error) { - rdr := internalio.NewOffsetReadSeeker(r, 0) - err := ii.Unmarshal(rdr) - if err != nil { - return 0, err - } - return rdr.Seek(0, io.SeekCurrent) + return 0, fmt.Errorf("unimplemented, index type not intended for deserialization") } func (ii *insertionIndex) Codec() multicodec.Code { diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index c7c9127bd..307486d79 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -47,6 +47,7 @@ type ReadOnly struct { // The backing containing the data payload in CARv1 format. backing io.ReaderAt + // The CARv1 content index. idx index.Index diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 204bc1221..eeff16add 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -44,6 +44,7 @@ type ( // Marshal encodes the index in serial form. Marshal(w io.Writer) (uint64, error) + // Unmarshal decodes the index from its serial form. // Deprecated: This function is slurpy and will copy everything into memory. Unmarshal(r io.Reader) error From 108f8e15502c1758d5c0847fce4867459ea54dbc Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 23 Jun 2022 21:04:12 +1000 Subject: [PATCH 3680/3817] fix: tighter constraint of singleWidthIndex width, add index recommentation docs This commit was moved from ipld/go-car@73b8fe324796ad0316fb79621fb416663aec07ea --- ipld/car/v2/index/index.go | 11 ++++++++--- ipld/car/v2/index/indexsorted.go | 5 +++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index eeff16add..ca2c2490b 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -49,9 +49,12 @@ type ( // Deprecated: This function is slurpy and will copy everything into memory. Unmarshal(r io.Reader) error - // UnmarshalLazyRead is the safe alternative to to Unmarshal. - // Instead of slurping it will keep a reference to the the io.ReaderAt passed in - // and ask for data as needed. + // UnmarshalLazyRead lazily decodes the index from its serial form. It is a + // safer alternative to to Unmarshal, particularly when reading index data + // from untrusted sources (which is not recommended) but also in more + // constrained memory environments. + // Instead of slurping UnmarshalLazyRead will keep a reference to the the + // io.ReaderAt passed in and ask for data as needed. UnmarshalLazyRead(r io.ReaderAt) (indexSize int64, err error) // Load inserts a number of records into the index. @@ -137,6 +140,7 @@ func WriteTo(idx Index, w io.Writer) (uint64, error) { // ReadFrom reads index from r. // The reader decodes the index by reading the first byte to interpret the encoding. // Returns error if the encoding is not known. +// Attempting to read index data from untrusted sources is not recommended. func ReadFrom(r io.ReaderAt) (Index, error) { idx, _, err := ReadFromWithSize(r) return idx, err @@ -144,6 +148,7 @@ func ReadFrom(r io.ReaderAt) (Index, error) { // ReadFromWithSize is just like ReadFrom but return the size of the Index. // The size is only valid when err != nil. +// Attempting to read index data from untrusted sources is not recommended. func ReadFromWithSize(r io.ReaderAt) (Index, int64, error) { code, err := varint.ReadUvarint(internalio.NewOffsetReadSeeker(r, 0)) if err != nil { diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index 16367ed52..b9f9a654e 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -113,8 +113,9 @@ func (s *singleWidthIndex) checkUnmarshalLengths(width uint32, dataLen, extra ui if width <= 8 { return errors.New("malformed index; width must be bigger than 8") } - if int32(width) < 0 { - return errors.New("index too big; singleWidthIndex width is overflowing int32") + const maxWidth = 32 << 20 // 32MiB, to ~match the go-cid maximum + if width > maxWidth { + return errors.New("index too big; singleWidthIndex width is larger than allowed maximum") } oldDataLen, dataLen := dataLen, dataLen+extra if oldDataLen > dataLen { From 1cca428c92b287dd605161c24289bfe9d9a295ff Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 30 Jun 2022 11:35:57 +0100 Subject: [PATCH 3681/3817] Fix testutil assertion logic and update index generation tests Update index generation tests to assert indices are identical. Fix minor typo in the test utility name and a bug where the check was not using both index instances to assert they are identical. Also refactor the use of lock in favour of wait group for better readability of the assertion logic. This commit was moved from ipld/go-car@906d56920df534e8b1cdf754377e78db14208fe7 --- ipld/car/v2/blockstore/readwrite_test.go | 2 +- ipld/car/v2/index/index_test.go | 2 +- ipld/car/v2/index/testutil/equal_index.go | 18 +++-- ipld/car/v2/index_gen_test.go | 89 +++++++++-------------- ipld/car/v2/reader_test.go | 2 +- ipld/car/v2/writer_test.go | 2 +- 6 files changed, 49 insertions(+), 66 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 3dc528665..6f64ac72f 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -520,7 +520,7 @@ func TestBlockstoreResumption(t *testing.T) { require.NoError(t, err) wantIdx, err := carv2.GenerateIndex(v2r.DataReader()) require.NoError(t, err) - testutil.AssertIndenticalIndexes(t, wantIdx, gotIdx) + testutil.AssertIdenticalIndexes(t, wantIdx, gotIdx) } func TestBlockstoreResumptionIsSupportedOnFinalizedFile(t *testing.T) { diff --git a/ipld/car/v2/index/index_test.go b/ipld/car/v2/index/index_test.go index f895bc2ff..92774d7c6 100644 --- a/ipld/car/v2/index/index_test.go +++ b/ipld/car/v2/index/index_test.go @@ -113,7 +113,7 @@ func TestWriteTo(t *testing.T) { require.NoError(t, err) // Assert they are equal - testutil.AssertIndenticalIndexes(t, wantIdx, gotIdx) + testutil.AssertIdenticalIndexes(t, wantIdx, gotIdx) } func TestMarshalledIndexStartsWithCodec(t *testing.T) { diff --git a/ipld/car/v2/index/testutil/equal_index.go b/ipld/car/v2/index/testutil/equal_index.go index c5da756a5..43d0b3e91 100644 --- a/ipld/car/v2/index/testutil/equal_index.go +++ b/ipld/car/v2/index/testutil/equal_index.go @@ -30,15 +30,17 @@ func insertUint64(s []uint64) { } } -func AssertIndenticalIndexes(t *testing.T, a, b Index) { - var wg sync.Mutex - wg.Lock() +func AssertIdenticalIndexes(t *testing.T, a, b Index) { + var wg sync.WaitGroup // key is multihash.Multihash.HexString var aCount uint + var aErr error aMap := make(map[string][]uint64) + wg.Add(1) + go func() { - defer wg.Unlock() - a.ForEach(func(mh multihash.Multihash, off uint64) error { + defer wg.Done() + aErr = a.ForEach(func(mh multihash.Multihash, off uint64) error { aCount++ str := mh.HexString() slice := aMap[str] @@ -51,7 +53,7 @@ func AssertIndenticalIndexes(t *testing.T, a, b Index) { var bCount uint bMap := make(map[string][]uint64) - a.ForEach(func(mh multihash.Multihash, off uint64) error { + bErr := b.ForEach(func(mh multihash.Multihash, off uint64) error { bCount++ str := mh.HexString() slice := bMap[str] @@ -60,7 +62,9 @@ func AssertIndenticalIndexes(t *testing.T, a, b Index) { bMap[str] = slice return nil }) - wg.Lock() + wg.Wait() + require.NoError(t, aErr) + require.NoError(t, bErr) require.Equal(t, aCount, bCount) require.Equal(t, aMap, bMap) diff --git a/ipld/car/v2/index_gen_test.go b/ipld/car/v2/index_gen_test.go index cae76ddf8..43a9c2aca 100644 --- a/ipld/car/v2/index_gen_test.go +++ b/ipld/car/v2/index_gen_test.go @@ -1,6 +1,7 @@ package car_test import ( + "github.com/stretchr/testify/assert" "io" "os" "testing" @@ -14,25 +15,25 @@ import ( "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" "github.com/multiformats/go-varint" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestGenerateIndex(t *testing.T) { - tests := []struct { + type testCase struct { name string carPath string opts []carv2.Option wantIndexer func(t *testing.T) index.Index wantErr bool - }{ + } + tests := []testCase{ { name: "CarV1IsIndexedAsExpected", carPath: "testdata/sample-v1.car", wantIndexer: func(t *testing.T) index.Index { v1, err := os.Open("testdata/sample-v1.car") require.NoError(t, err) - defer v1.Close() + t.Cleanup(func() { assert.NoError(t, v1.Close()) }) want, err := carv2.GenerateIndex(v1) require.NoError(t, err) return want @@ -44,7 +45,7 @@ func TestGenerateIndex(t *testing.T) { wantIndexer: func(t *testing.T) index.Index { v2, err := os.Open("testdata/sample-wrapped-v2.car") require.NoError(t, err) - defer v2.Close() + t.Cleanup(func() { assert.NoError(t, v2.Close()) }) reader, err := carv2.NewReader(v2) require.NoError(t, err) want, err := index.ReadFrom(reader.IndexReader()) @@ -59,7 +60,7 @@ func TestGenerateIndex(t *testing.T) { wantIndexer: func(t *testing.T) index.Index { v1, err := os.Open("testdata/sample-v1-with-zero-len-section.car") require.NoError(t, err) - defer v1.Close() + t.Cleanup(func() { assert.NoError(t, v1.Close()) }) want, err := carv2.GenerateIndex(v1, carv2.ZeroLengthSectionAsEOF(true)) require.NoError(t, err) return want @@ -72,7 +73,7 @@ func TestGenerateIndex(t *testing.T) { wantIndexer: func(t *testing.T) index.Index { v1, err := os.Open("testdata/sample-v1-with-zero-len-section2.car") require.NoError(t, err) - defer v1.Close() + t.Cleanup(func() { assert.NoError(t, v1.Close()) }) want, err := carv2.GenerateIndex(v1, carv2.ZeroLengthSectionAsEOF(true)) require.NoError(t, err) return want @@ -90,71 +91,49 @@ func TestGenerateIndex(t *testing.T) { wantErr: true, }, } + + requireWant := func(tt testCase, got index.Index, gotErr error) { + if tt.wantErr { + require.Error(t, gotErr) + } else { + require.NoError(t, gotErr) + var want index.Index + if tt.wantIndexer != nil { + want = tt.wantIndexer(t) + } + if want == nil { + require.Nil(t, got) + } else { + testutil.AssertIdenticalIndexes(t, want, got) + } + } + } + for _, tt := range tests { t.Run("ReadOrGenerateIndex_"+tt.name, func(t *testing.T) { carFile, err := os.Open(tt.carPath) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, carFile.Close()) }) - got, err := carv2.ReadOrGenerateIndex(carFile, tt.opts...) - if tt.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - var want index.Index - if tt.wantIndexer != nil { - want = tt.wantIndexer(t) - } - if want == nil { - require.Nil(t, got) - } else { - testutil.AssertIndenticalIndexes(t, want, got) - } - } + got, gotErr := carv2.ReadOrGenerateIndex(carFile, tt.opts...) + requireWant(tt, got, gotErr) }) t.Run("GenerateIndexFromFile_"+tt.name, func(t *testing.T) { - got, err := carv2.GenerateIndexFromFile(tt.carPath, tt.opts...) - if tt.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - var want index.Index - if tt.wantIndexer != nil { - want = tt.wantIndexer(t) - } - require.Equal(t, want, got) - } + got, gotErr := carv2.GenerateIndexFromFile(tt.carPath, tt.opts...) + requireWant(tt, got, gotErr) }) t.Run("LoadIndex_"+tt.name, func(t *testing.T) { carFile, err := os.Open(tt.carPath) require.NoError(t, err) got, err := index.New(multicodec.CarMultihashIndexSorted) require.NoError(t, err) - err = carv2.LoadIndex(got, carFile, tt.opts...) - if tt.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - var want index.Index - if tt.wantIndexer != nil { - want = tt.wantIndexer(t) - } - require.Equal(t, want, got) - } + gotErr := carv2.LoadIndex(got, carFile, tt.opts...) + requireWant(tt, got, gotErr) }) t.Run("GenerateIndex_"+tt.name, func(t *testing.T) { carFile, err := os.Open(tt.carPath) require.NoError(t, err) - got, err := carv2.GenerateIndex(carFile, tt.opts...) - if tt.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - var want index.Index - if tt.wantIndexer != nil { - want = tt.wantIndexer(t) - } - require.Equal(t, want, got) - } + got, gotErr := carv2.GenerateIndex(carFile, tt.opts...) + requireWant(tt, got, gotErr) }) } } diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go index f653b60b2..c010445fb 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/reader_test.go @@ -174,7 +174,7 @@ func TestReader_WithCarV2Consistency(t *testing.T) { require.NoError(t, err) wantIndex, err := carv2.GenerateIndex(subject.DataReader()) require.NoError(t, err) - testutil.AssertIndenticalIndexes(t, wantIndex, gotIndex) + testutil.AssertIdenticalIndexes(t, wantIndex, gotIndex) }) } } diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index 2044e17ba..1bf3ca3d9 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -57,7 +57,7 @@ func TestWrapV1(t *testing.T) { require.NoError(t, err) gotIdx, err := index.ReadFrom(subject.IndexReader()) require.NoError(t, err) - testutil.AssertIndenticalIndexes(t, wantIdx, gotIdx) + testutil.AssertIdenticalIndexes(t, wantIdx, gotIdx) } func TestExtractV1(t *testing.T) { From e6180e7a3ef3972168c42a8070d71fb7afa86a42 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 30 Jun 2022 14:42:26 +0100 Subject: [PATCH 3682/3817] Use a fix code as the multihash code for `CarIndexSorted` Previous changes added the `ForEach` interface to `Index` type which enables iteration through the index by multihash and offset. However, not all index types contain enough information to construct the full multihash. Namely, `CarIndexSorted` only stores the digest portion of the multihashes. In order to implement `ForEach` for this index type correctly uses the `uint64` max value as the code in the multihash and document the behaviour where the iterations over this index type should not rely on the returned code. Note that the max value is used as a code that doesn't match any existing multicodec.Code to avoid misleading users. This commit was moved from ipld/go-car@3aa026c977b11ed604de39268bac76d70ad2b978 --- ipld/car/v2/index/index.go | 7 ++++--- ipld/car/v2/index/indexsorted.go | 9 ++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index ca2c2490b..74b7fbcf4 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -5,14 +5,12 @@ import ( "fmt" "io" + "github.com/ipfs/go-cid" internalio "github.com/ipld/go-car/v2/internal/io" "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" - "github.com/multiformats/go-varint" - - "github.com/ipfs/go-cid" ) // CarIndexNone is a sentinal value used as a multicodec code for the index indicating no index. @@ -82,6 +80,9 @@ type ( // the multihash of the element, and the offset in the car file // where the element appears. // + // Note that index with codec multicodec.CarIndexSorted does not store the multihash code. + // The multihashes passed to ForEach on this index type should only rely on the digest part. + // // If the callback returns a non-nil error, the iteration is aborted, // and the ForEach function returns the error to the user. // diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index b9f9a654e..1e7ed3f63 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "math" "sort" "github.com/ipld/go-car/v2/internal/errsort" @@ -348,7 +349,13 @@ func (m *multiWidthIndex) Load(items []Record) error { func (m *multiWidthIndex) ForEach(f func(multihash.Multihash, uint64) error) error { return m.forEachDigest(func(digest []byte, offset uint64) error { - mh, err := multihash.Cast(digest) + // multicodec.CarIndexSorted does not contain the multihash code; it only contains the digest. + // To implement ForEach on this index kind we encode the digest with multihash code math.MaxUint64. + // The CarIndexSorted documents this behaviour and the user should not take the multihash code + // as the actual code of the multihashes of CAR blocks. + // The rationale for using math.MaxUint64 is to avoid using a reserved multihash code that could + // become error-prone later, including 0x00 which is a valid multihash code for IDENTITY. + mh, err := multihash.Encode(digest, math.MaxUint64) if err != nil { return err } From 0b4355f83ef942d3409037b0fdd2ca7af21d7a55 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 30 Jun 2022 16:07:28 +0100 Subject: [PATCH 3683/3817] Remove support for `ForEach` enumeration from car-index-sorted This index type does not store enough information to satisfy `ForEach`. It only contains the digest of mulithashes and not their code. Instead of some partial functionality simply return an error when `ForEach` is called on this function type. Because, there is no valid use for this index type and the user should ber regenerating the index to the newer `car-multihash-index-sorted` anyway. Update tests to include samples of both types and assert IO operations and index generation for both formats. This commit was moved from ipld/go-car@5c7e07edac3835e83ee3452507a9ba3bc94f8d2e --- ipld/car/v2/index/index.go | 6 +- ipld/car/v2/index/index_test.go | 62 +++++++++++++----- ipld/car/v2/index/indexsorted.go | 17 +---- ipld/car/v2/index/indexsorted_test.go | 12 ++++ .../sample-multihash-index-sorted.carindex | Bin 0 -> 41750 bytes 5 files changed, 65 insertions(+), 32 deletions(-) create mode 100644 ipld/car/v2/testdata/sample-multihash-index-sorted.carindex diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 74b7fbcf4..3a2b3f1d9 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -80,8 +80,10 @@ type ( // the multihash of the element, and the offset in the car file // where the element appears. // - // Note that index with codec multicodec.CarIndexSorted does not store the multihash code. - // The multihashes passed to ForEach on this index type should only rely on the digest part. + // Note that index with codec multicodec.CarIndexSorted does not support ForEach enumeration. + // Because this index type only contains the multihash digest and not the code. + // Calling ForEach on this index type will result in error. + // Use multicodec.CarMultihashIndexSorted index type instead. // // If the callback returns a non-nil error, the iteration is aborted, // and the ForEach function returns the error to the user. diff --git a/ipld/car/v2/index/index_test.go b/ipld/car/v2/index/index_test.go index 92774d7c6..0d380cafe 100644 --- a/ipld/car/v2/index/index_test.go +++ b/ipld/car/v2/index/index_test.go @@ -55,6 +55,13 @@ func TestReadFrom(t *testing.T) { subject, err := ReadFrom(idxf) require.NoError(t, err) + idxf2, err := os.Open("../testdata/sample-multihash-index-sorted.carindex") + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, idxf2.Close()) }) + + subjectInAltFormat, err := ReadFrom(idxf) + require.NoError(t, err) + crf, err := os.Open("../testdata/sample-v1.car") require.NoError(t, err) t.Cleanup(func() { require.NoError(t, crf.Close()) }) @@ -68,11 +75,17 @@ func TestReadFrom(t *testing.T) { } require.NoError(t, err) + wantCid := wantBlock.Cid() // Get offset from the index for a CID and assert it exists - gotOffset, err := GetFirst(subject, wantBlock.Cid()) + gotOffset, err := GetFirst(subject, wantCid) require.NoError(t, err) require.NotZero(t, gotOffset) + // Get offset from the index in alternative format for a CID and assert it exists + gotOffset2, err := GetFirst(subjectInAltFormat, wantCid) + require.NoError(t, err) + require.NotZero(t, gotOffset2) + // Seek to the offset on CARv1 file _, err = crf.Seek(int64(gotOffset), io.SeekStart) require.NoError(t, err) @@ -88,7 +101,7 @@ func TestReadFrom(t *testing.T) { func TestWriteTo(t *testing.T) { // Read sample index on file - idxf, err := os.Open("../testdata/sample-index.carindex") + idxf, err := os.Open("../testdata/sample-multihash-index-sorted.carindex") require.NoError(t, err) t.Cleanup(func() { require.NoError(t, idxf.Close()) }) @@ -117,18 +130,37 @@ func TestWriteTo(t *testing.T) { } func TestMarshalledIndexStartsWithCodec(t *testing.T) { - // Read sample index on file - idxf, err := os.Open("../testdata/sample-index.carindex") - require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, idxf.Close()) }) - - // Unmarshall to get expected index - wantIdx, err := ReadFrom(idxf) - require.NoError(t, err) - // Assert the first two bytes are the corresponding multicodec code. - buf := new(bytes.Buffer) - _, err = WriteTo(wantIdx, buf) - require.NoError(t, err) - require.Equal(t, varint.ToUvarint(uint64(multicodec.CarIndexSorted)), buf.Bytes()[:2]) + tests := []struct { + path string + codec multicodec.Code + }{ + { + path: "../testdata/sample-multihash-index-sorted.carindex", + codec: multicodec.CarMultihashIndexSorted, + }, + { + path: "../testdata/sample-index.carindex", + codec: multicodec.CarIndexSorted, + }, + } + for _, test := range tests { + test := test + t.Run(test.codec.String(), func(t *testing.T) { + // Read sample index on file + idxf, err := os.Open(test.path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, idxf.Close()) }) + + // Unmarshall to get expected index + wantIdx, err := ReadFrom(idxf) + require.NoError(t, err) + + // Assert the first two bytes are the corresponding multicodec code. + buf := new(bytes.Buffer) + _, err = WriteTo(wantIdx, buf) + require.NoError(t, err) + require.Equal(t, varint.ToUvarint(uint64(test.codec)), buf.Bytes()[:2]) + }) + } } diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index 1e7ed3f63..60d0e87d3 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io" - "math" "sort" "github.com/ipld/go-car/v2/internal/errsort" @@ -347,20 +346,8 @@ func (m *multiWidthIndex) Load(items []Record) error { return nil } -func (m *multiWidthIndex) ForEach(f func(multihash.Multihash, uint64) error) error { - return m.forEachDigest(func(digest []byte, offset uint64) error { - // multicodec.CarIndexSorted does not contain the multihash code; it only contains the digest. - // To implement ForEach on this index kind we encode the digest with multihash code math.MaxUint64. - // The CarIndexSorted documents this behaviour and the user should not take the multihash code - // as the actual code of the multihashes of CAR blocks. - // The rationale for using math.MaxUint64 is to avoid using a reserved multihash code that could - // become error-prone later, including 0x00 which is a valid multihash code for IDENTITY. - mh, err := multihash.Encode(digest, math.MaxUint64) - if err != nil { - return err - } - return f(mh, offset) - }) +func (m *multiWidthIndex) ForEach(func(multihash.Multihash, uint64) error) error { + return fmt.Errorf("%s does not support ForEach enumeration; use %s instead", multicodec.CarIndexSorted, multicodec.CarMultihashIndexSorted) } func (m *multiWidthIndex) forEachDigest(f func(digest []byte, offset uint64) error) error { diff --git a/ipld/car/v2/index/indexsorted_test.go b/ipld/car/v2/index/indexsorted_test.go index f7e038a01..8e1a45272 100644 --- a/ipld/car/v2/index/indexsorted_test.go +++ b/ipld/car/v2/index/indexsorted_test.go @@ -7,9 +7,21 @@ import ( "github.com/ipfs/go-merkledag" "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-multihash" "github.com/stretchr/testify/require" ) +func TestSortedIndex_ErrorsOnForEach(t *testing.T) { + subject, err := New(multicodec.CarIndexSorted) + require.NoError(t, err) + err = subject.ForEach(func(multihash.Multihash, uint64) error { return nil }) + require.Error(t, err) + require.Equal(t, + "car-index-sorted does not support ForEach enumeration; use car-multihash-index-sorted instead", + err.Error(), + ) +} + func TestSortedIndexCodec(t *testing.T) { require.Equal(t, multicodec.CarIndexSorted, newSorted().Codec()) } diff --git a/ipld/car/v2/testdata/sample-multihash-index-sorted.carindex b/ipld/car/v2/testdata/sample-multihash-index-sorted.carindex new file mode 100644 index 0000000000000000000000000000000000000000..ddf8a5fb27bedb35b876822e92f2c6bb9bac7cdb GIT binary patch literal 41750 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�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 fFXv6DTmOE2|GI+}-4KXKw!?RDarG Date: Thu, 30 Jun 2022 20:00:02 +1000 Subject: [PATCH 3684/3817] feat: add Reader#Inspect() function to check basic validity of a CAR and return stats This commit was moved from ipld/go-car@1de342e2019eb49ea54abcc07211ad77da1c6276 --- ipld/car/v2/block_reader.go | 18 +-- ipld/car/v2/car.go | 21 +++- ipld/car/v2/car_test.go | 9 +- ipld/car/v2/reader.go | 147 ++++++++++++++++++++++++ ipld/car/v2/reader_test.go | 222 ++++++++++++++++++++++++++++++++++++ 5 files changed, 393 insertions(+), 24 deletions(-) diff --git a/ipld/car/v2/block_reader.go b/ipld/car/v2/block_reader.go index a74e3996a..55ebd1cf6 100644 --- a/ipld/car/v2/block_reader.go +++ b/ipld/car/v2/block_reader.go @@ -64,20 +64,6 @@ func NewBlockReader(r io.Reader, opts ...Option) (*BlockReader, error) { if _, err := v2h.ReadFrom(r); err != nil { return nil, err } - // Assert the data payload offset validity. - // It must be at least 51 ( + ). - dataOffset := int64(v2h.DataOffset) - if dataOffset < PragmaSize+HeaderSize { - return nil, fmt.Errorf("invalid data payload offset: %v", dataOffset) - } - // Assert the data size validity. - // It must be larger than zero. - // Technically, it should be at least 11 bytes (i.e. a valid CARv1 header with no roots) but - // we let further parsing of the header to signal invalid data payload header. - dataSize := int64(v2h.DataSize) - if dataSize <= 0 { - return nil, fmt.Errorf("invalid data payload size: %v", dataSize) - } // Skip to the beginning of inner CARv1 data payload. // Note, at this point the pragma and CARv1 header have been read. @@ -86,12 +72,12 @@ func NewBlockReader(r io.Reader, opts ...Option) (*BlockReader, error) { // fast forward to the beginning of data payload by subtracting pragma and header size from // dataOffset. rs := internalio.ToByteReadSeeker(r) - if _, err := rs.Seek(dataOffset-PragmaSize-HeaderSize, io.SeekCurrent); err != nil { + if _, err := rs.Seek(int64(v2h.DataOffset)-PragmaSize-HeaderSize, io.SeekCurrent); err != nil { return nil, err } // Set br.r to a LimitReader reading from r limited to dataSize. - br.r = io.LimitReader(r, dataSize) + br.r = io.LimitReader(r, int64(v2h.DataSize)) // Populate br.Roots by reading the inner CARv1 data payload header. header, err := carv1.ReadHeader(br.r, options.MaxAllowedHeaderSize) diff --git a/ipld/car/v2/car.go b/ipld/car/v2/car.go index 194731363..571eb1140 100644 --- a/ipld/car/v2/car.go +++ b/ipld/car/v2/car.go @@ -2,7 +2,7 @@ package car import ( "encoding/binary" - "errors" + "fmt" "io" ) @@ -170,10 +170,21 @@ func (h *Header) ReadFrom(r io.Reader) (int64, error) { dataOffset := binary.LittleEndian.Uint64(buf[:8]) dataSize := binary.LittleEndian.Uint64(buf[8:16]) indexOffset := binary.LittleEndian.Uint64(buf[16:]) - if int64(dataOffset) < 0 || - int64(dataSize) < 0 || - int64(indexOffset) < 0 { - return n, errors.New("malformed car, overflowing offsets") + // Assert the data payload offset validity. + // It must be at least 51 ( + ). + if int64(dataOffset) < PragmaSize+HeaderSize { + return n, fmt.Errorf("invalid data payload offset: %v", dataOffset) + } + // Assert the data size validity. + // It must be larger than zero. + // Technically, it should be at least 11 bytes (i.e. a valid CARv1 header with no roots) but + // we let further parsing of the header to signal invalid data payload header. + if int64(dataSize) <= 0 { + return n, fmt.Errorf("invalid data payload size: %v", dataSize) + } + // Assert the index offset validity. + if int64(indexOffset) < 0 { + return n, fmt.Errorf("invalid index offset: %v", indexOffset) } h.DataOffset = dataOffset h.DataSize = dataSize diff --git a/ipld/car/v2/car_test.go b/ipld/car/v2/car_test.go index 9e113259e..d993a3746 100644 --- a/ipld/car/v2/car_test.go +++ b/ipld/car/v2/car_test.go @@ -56,11 +56,12 @@ func TestHeader_WriteTo(t *testing.T) { "HeaderWithEmptyCharacteristicsIsWrittenAsExpected", carv2.Header{ Characteristics: carv2.Characteristics{}, + DataOffset: 99, }, []byte{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x63, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, }, @@ -114,12 +115,14 @@ func TestHeader_ReadFrom(t *testing.T) { []byte{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x63, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x64, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, }, carv2.Header{ Characteristics: carv2.Characteristics{}, + DataOffset: 99, + DataSize: 100, }, false, }, diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 40c5d8c8d..0208284c3 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -3,10 +3,15 @@ package car import ( "fmt" "io" + "math" "github.com/ipfs/go-cid" + "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1" + "github.com/ipld/go-car/v2/internal/carv1/util" internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-varint" "golang.org/x/exp/mmap" ) @@ -116,6 +121,148 @@ func (r *Reader) IndexReader() io.ReaderAt { return internalio.NewOffsetReadSeeker(r.r, int64(r.Header.IndexOffset)) } +// CarStats is returned by an Inspect() call +type CarStats struct { + Version uint64 + Header Header + Roots []cid.Cid + RootsPresent bool + BlockCount uint64 + CodecCounts map[multicodec.Code]uint64 + MhTypeCounts map[multicodec.Code]uint64 + AvgCidLength uint64 + MaxCidLength uint64 + MinCidLength uint64 + AvgBlockLength uint64 + MaxBlockLength uint64 + MinBlockLength uint64 + IndexCodec multicodec.Code + IndexSize uint64 +} + +// Inspect does a quick scan of a CAR, performing basic validation of the format +// and returning a CarStats object that provides a high-level description of the +// contents of the CAR. +// Inspect works for CARv1 and CARv2 contents. A CARv1 will return an +// uninitialized Header value. +// Inspect will perform a basic check of a CARv2 index, where present, but this +// does not guarantee that the index is correct. Attempting to read index data +// from untrusted sources is not recommended. If required, further validation of +// an index can be performed by loading the index and performing a ForEach() and +// sanity checking that the offsets are within the data payload section of the +// CAR. However, re-generation of index data in this case is the recommended +// course of action. +func (r *Reader) Inspect() (CarStats, error) { + stats := CarStats{ + Version: r.Version, + Header: r.Header, + CodecCounts: make(map[multicodec.Code]uint64), + MhTypeCounts: make(map[multicodec.Code]uint64), + } + + var totalCidLength uint64 + var totalBlockLength uint64 + var minCidLength uint64 = math.MaxUint64 + var minBlockLength uint64 = math.MaxUint64 + + dr := r.DataReader() + bdr := internalio.ToByteReader(dr) + + // read roots, not using Roots(), because we need the offset setup in the data trader + header, err := carv1.ReadHeader(dr, r.opts.MaxAllowedHeaderSize) + if err != nil { + return CarStats{}, err + } + stats.Roots = header.Roots + var rootsPresentCount int + rootsPresent := make([]bool, len(stats.Roots)) + + // read block sections + for { + sectionLength, err := varint.ReadUvarint(bdr) + if err != nil { + if err == io.EOF { + // if the length of bytes read is non-zero when the error is EOF then signal an unclean EOF. + if sectionLength > 0 { + return CarStats{}, io.ErrUnexpectedEOF + } + // otherwise, this is a normal ending + break + } + } else if sectionLength == 0 && r.opts.ZeroLengthSectionAsEOF { + // normal ending for this read mode + break + } + if sectionLength > r.opts.MaxAllowedSectionSize { + return CarStats{}, util.ErrSectionTooLarge + } + + // decode just the CID bytes + cidLen, c, err := cid.CidFromReader(dr) + if err != nil { + return CarStats{}, err + } + + // is this a root block? (also account for duplicate root CIDs) + if rootsPresentCount < len(stats.Roots) { + for i, r := range stats.Roots { + if !rootsPresent[i] && c == r { + rootsPresent[i] = true + rootsPresentCount++ + } + } + } + + cp := c.Prefix() + codec := multicodec.Code(cp.Codec) + count := stats.CodecCounts[codec] + stats.CodecCounts[codec] = count + 1 + mhtype := multicodec.Code(cp.MhType) + count = stats.MhTypeCounts[mhtype] + stats.MhTypeCounts[mhtype] = count + 1 + + blockLength := sectionLength - uint64(cidLen) + dr.Seek(int64(blockLength), io.SeekCurrent) + + stats.BlockCount++ + totalCidLength += uint64(cidLen) + totalBlockLength += blockLength + if uint64(cidLen) < minCidLength { + minCidLength = uint64(cidLen) + } + if uint64(cidLen) > stats.MaxCidLength { + stats.MaxCidLength = uint64(cidLen) + } + if uint64(blockLength) < minBlockLength { + minBlockLength = uint64(blockLength) + } + if uint64(blockLength) > stats.MaxBlockLength { + stats.MaxBlockLength = uint64(blockLength) + } + } + + stats.RootsPresent = len(stats.Roots) == rootsPresentCount + if stats.BlockCount > 0 { + stats.MinCidLength = minCidLength + stats.MinBlockLength = minBlockLength + stats.AvgCidLength = totalCidLength / stats.BlockCount + stats.AvgBlockLength = totalBlockLength / stats.BlockCount + } + + if stats.Version != 1 && stats.Header.HasIndex() { + // performs an UnmarshalLazyRead which should have its own validation and + // is intended to be a fast initial scan + ind, size, err := index.ReadFromWithSize(r.IndexReader()) + if err != nil { + return CarStats{}, err + } + stats.IndexCodec = ind.Codec() + stats.IndexSize = uint64(size) + } + + return stats, nil +} + // Close closes the underlying reader if it was opened by OpenReader. func (r *Reader) Close() error { if r.closer != nil { diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go index c010445fb..58b9a3286 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/reader_test.go @@ -1,14 +1,19 @@ package car_test import ( + "bytes" + "encoding/hex" "io" "os" + "strings" "testing" + "github.com/ipfs/go-cid" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/index/testutil" "github.com/ipld/go-car/v2/internal/carv1" + "github.com/multiformats/go-multicodec" "github.com/stretchr/testify/require" ) @@ -268,3 +273,220 @@ func requireNewCarV1Reader(t *testing.T, r io.Reader, zerLenAsEOF bool) *carv1.C require.NoError(t, err) return cr } + +func TestInspect(t *testing.T) { + tests := []struct { + name string + path string + zerLenAsEOF bool + expectedStats carv2.CarStats + }{ + { + name: "IndexlessCarV2", + path: "testdata/sample-v2-indexless.car", + expectedStats: carv2.CarStats{ + Version: 2, + Header: carv2.Header{ + Characteristics: carv2.Characteristics{0, 0}, + DataOffset: 51, + DataSize: 479907, + IndexOffset: 0, + }, + Roots: []cid.Cid{mustCidDecode("bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy")}, + RootsPresent: true, + AvgBlockLength: 417, // 417.6644423260248 + MinBlockLength: 1, + MaxBlockLength: 1342, + AvgCidLength: 37, // 37.86939942802669 + MinCidLength: 14, + MaxCidLength: 38, + BlockCount: 1049, + CodecCounts: map[multicodec.Code]uint64{ + multicodec.Raw: 6, + multicodec.DagCbor: 1043, + }, + MhTypeCounts: map[multicodec.Code]uint64{ + multicodec.Identity: 6, + multicodec.Blake2b256: 1043, + }, + }, + }, + { + // same payload as IndexlessCarV2, so only difference is the Version & Header + name: "CarV1", + path: "testdata/sample-v1.car", + expectedStats: carv2.CarStats{ + Version: 1, + Header: carv2.Header{}, + Roots: []cid.Cid{mustCidDecode("bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy")}, + RootsPresent: true, + AvgBlockLength: 417, // 417.6644423260248 + MinBlockLength: 1, + MaxBlockLength: 1342, + AvgCidLength: 37, // 37.86939942802669 + MinCidLength: 14, + MaxCidLength: 38, + BlockCount: 1049, + CodecCounts: map[multicodec.Code]uint64{ + multicodec.Raw: 6, + multicodec.DagCbor: 1043, + }, + MhTypeCounts: map[multicodec.Code]uint64{ + multicodec.Identity: 6, + multicodec.Blake2b256: 1043, + }, + }, + }, + { + // same payload as IndexlessCarV2, so only difference is the Header + name: "CarV2ProducedByBlockstore", + path: "testdata/sample-rw-bs-v2.car", + expectedStats: carv2.CarStats{ + Version: 2, + Header: carv2.Header{ + DataOffset: 1464, + DataSize: 273, + IndexOffset: 1737, + }, + Roots: []cid.Cid{ + mustCidDecode("bafkreifuosuzujyf4i6psbneqtwg2fhplc2wxptc5euspa2gn3bwhnihfu"), + mustCidDecode("bafkreifc4hca3inognou377hfhvu2xfchn2ltzi7yu27jkaeujqqqdbjju"), + mustCidDecode("bafkreig5lvr4l6b4fr3un4xvzeyt3scevgsqjgrhlnwxw2unwbn5ro276u"), + }, + RootsPresent: true, + BlockCount: 3, + CodecCounts: map[multicodec.Code]uint64{multicodec.Raw: 3}, + MhTypeCounts: map[multicodec.Code]uint64{multicodec.Sha2_256: 3}, + AvgCidLength: 36, + MaxCidLength: 36, + MinCidLength: 36, + AvgBlockLength: 6, + MaxBlockLength: 9, + MinBlockLength: 4, + IndexCodec: multicodec.CarMultihashIndexSorted, + IndexSize: 148, + }, + }, + // same as CarV1 but with a zero-byte EOF to test options + { + name: "CarV1VersionWithZeroLenSectionIsOne", + path: "testdata/sample-v1-with-zero-len-section.car", + zerLenAsEOF: true, + expectedStats: carv2.CarStats{ + Version: 1, + Header: carv2.Header{}, + Roots: []cid.Cid{mustCidDecode("bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy")}, + RootsPresent: true, + AvgBlockLength: 417, // 417.6644423260248 + MinBlockLength: 1, + MaxBlockLength: 1342, + AvgCidLength: 37, // 37.86939942802669 + MinCidLength: 14, + MaxCidLength: 38, + BlockCount: 1049, + CodecCounts: map[multicodec.Code]uint64{ + multicodec.Raw: 6, + multicodec.DagCbor: 1043, + }, + MhTypeCounts: map[multicodec.Code]uint64{ + multicodec.Identity: 6, + multicodec.Blake2b256: 1043, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + reader, err := carv2.OpenReader(tt.path, carv2.ZeroLengthSectionAsEOF(tt.zerLenAsEOF)) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, reader.Close()) }) + stats, err := reader.Inspect() + require.NoError(t, err) + require.Equal(t, tt.expectedStats, stats) + }) + } +} + +func TestInspectError(t *testing.T) { + tests := []struct { + name string + carHex string + expectedOpenError string + expectedInspectError string + }{ + { + name: "BadCidV0", + carHex: "3aa265726f6f747381d8305825000130302030303030303030303030303030303030303030303030303030303030303030306776657273696f6e010130", + expectedInspectError: "expected 1 as the cid version number, got: 48", + }, + { + name: "BadHeaderLength", + carHex: "e0e0e0e0a7060c6f6c4cca943c236f4b196723489608edb42a8b8fa80b6776657273696f6e19", + expectedOpenError: "invalid header data, length of read beyond allowable maximum", + }, + { + name: "BadSectionLength", + carHex: "11a265726f6f7473806776657273696f6e01e0e0e0e0a7060155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000", + expectedInspectError: "invalid section data, length of read beyond allowable maximum", + }, + // the bad index tests are manually constructed from this single-block CARv2 by adjusting the Uint32 and Uint64 values in the index: + // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset + // 0aa16776657273696f6e02 00000000000000000000000000000000330000000000000041000000000000007400000000000000 11a265726f6f7473806776657273696f6e012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000 8108 01000000 1200000000000000 01000000 28000000 2800000000000000 01d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca 1200000000000000 + { + name: "BadIndexCountOverflow", + // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset + carHex: "0aa16776657273696f6e02 00000000000000000000000000000000330000000000000041000000000000007400000000000000 11a265726f6f7473806776657273696f6e012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000 8108 ffffffff 1200000000000000 01000000 28000000 2800000000000000 01d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca 1200000000000000", + expectedInspectError: "index too big; MultihashIndexSorted count is overflowing int32", + }, + { + name: "BadIndexCountTooMany", + // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset + carHex: "0aa16776657273696f6e02 00000000000000000000000000000000330000000000000041000000000000007400000000000000 11a265726f6f7473806776657273696f6e012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000 8108 ffffff7f 1200000000000000 01000000 28000000 2800000000000000 01d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca 1200000000000000", + expectedInspectError: "unexpected EOF", + }, + { + name: "BadIndexMultiWidthOverflow", + // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset + carHex: "0aa16776657273696f6e02 00000000000000000000000000000000330000000000000041000000000000007400000000000000 11a265726f6f7473806776657273696f6e012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000 8108 01000000 1200000000000000 ffffffff 28000000 2800000000000000 01d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca 1200000000000000", + expectedInspectError: "index too big; multiWidthIndex count is overflowing int32", + }, + { + name: "BadIndexMultiWidthTooMany", + // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset + carHex: "0aa16776657273696f6e02 00000000000000000000000000000000330000000000000041000000000000007400000000000000 11a265726f6f7473806776657273696f6e012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000 8108 01000000 1200000000000000 ffffff7f 28000000 2800000000000000 01d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca 1200000000000000", + expectedInspectError: "unexpected EOF", + }, + // we don't test any further into the index, to do that, a user should do a ForEach across the loaded index (and sanity check the offsets) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + car, _ := hex.DecodeString(strings.ReplaceAll(tt.carHex, " ", "")) + reader, err := carv2.NewReader(bytes.NewReader(car)) + if tt.expectedOpenError != "" { + require.Error(t, err) + require.Equal(t, err.Error(), tt.expectedOpenError) + return + } else { + require.NoError(t, err) + } + t.Cleanup(func() { require.NoError(t, reader.Close()) }) + _, err = reader.Inspect() + if tt.expectedInspectError != "" { + require.Error(t, err) + require.Equal(t, err.Error(), tt.expectedInspectError) + } else { + require.NoError(t, err) + } + }) + } +} + +func mustCidDecode(s string) cid.Cid { + c, err := cid.Decode(s) + if err != nil { + panic(err) + } + return c +} From 2df9663a7a3b76084b15a898bc90ad0513e250fa Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 1 Jul 2022 13:17:03 +1000 Subject: [PATCH 3685/3817] feat: add block hash validation to Inspect() This commit was moved from ipld/go-car@466dbf1db7950d5658752ddfed2730cd81893177 --- ipld/car/v2/block_reader.go | 2 +- ipld/car/v2/reader.go | 67 ++++++++++++++++++++++++++++++++++-- ipld/car/v2/reader_test.go | 68 +++++++++++++++++++++++++++++++++---- 3 files changed, 127 insertions(+), 10 deletions(-) diff --git a/ipld/car/v2/block_reader.go b/ipld/car/v2/block_reader.go index 55ebd1cf6..252885c31 100644 --- a/ipld/car/v2/block_reader.go +++ b/ipld/car/v2/block_reader.go @@ -119,7 +119,7 @@ func (br *BlockReader) Next() (blocks.Block, error) { } if !hashed.Equals(c) { - return nil, fmt.Errorf("mismatch in content integrity, name: %s, data: %s", c, hashed) + return nil, fmt.Errorf("mismatch in content integrity, expected: %s, got: %s", c, hashed) } return blocks.NewBlockWithCid(data, c) diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 0208284c3..c3ef36535 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -145,6 +145,13 @@ type CarStats struct { // contents of the CAR. // Inspect works for CARv1 and CARv2 contents. A CARv1 will return an // uninitialized Header value. +// +// If validateBlockHash is true, all block data in the payload will be hashed +// and compared to the CID for that block and an error will return if there +// is a mismatch. If false, block data will be skipped over and not checked. +// Performing a full block hash validation is similar to using a BlockReader and +// calling Next over all blocks. +// // Inspect will perform a basic check of a CARv2 index, where present, but this // does not guarantee that the index is correct. Attempting to read index data // from untrusted sources is not recommended. If required, further validation of @@ -152,7 +159,34 @@ type CarStats struct { // sanity checking that the offsets are within the data payload section of the // CAR. However, re-generation of index data in this case is the recommended // course of action. -func (r *Reader) Inspect() (CarStats, error) { +// +// Beyond the checks performed by Inspect, a valid / good CAR is somewhat +// use-case dependent. Factors to consider include: +// +// * Bad indexes, including incorrect offsets, duplicate entries, or other +// faulty data. Indexes should be re-generated, regardless, if you need to use +// them and have any reason to not trust the source. +// +// * Blocks use codecs that your system doesn't have access to—which may mean +// you can't traverse a DAG or use the contained data. CarStats#CodecCounts +// contains a list of codecs found in the CAR so this can be checked. +// +// * CIDs use multihashes that your system doesn't have access to—which will +// mean you can't validate block hashes are correct (using validateBlockHash +// in this case will result in a failure). CarStats#MhTypeCounts contains a +// list of multihashes found in the CAR so this can bechecked. +// +// * The presence of IDENTITY CIDs, which may not be supported (or desired) by +// the consumer of the CAR. CarStats#CodecCounts can determine the presence +// of IDENTITY CIDs. +// +// * Roots: the number of roots, duplicates, and whether they are related to the +// blocks contained within the CAR. CarStats contains a list of Roots and a +// RootsPresent bool so further checks can be performed. +// +// * DAG completeness is not checked. Any properties relating to the DAG, or +// DAGs contained within a CAR are the responsibility of the user to check. +func (r *Reader) Inspect(validateBlockHash bool) (CarStats, error) { stats := CarStats{ Version: r.Version, Header: r.Header, @@ -189,7 +223,8 @@ func (r *Reader) Inspect() (CarStats, error) { // otherwise, this is a normal ending break } - } else if sectionLength == 0 && r.opts.ZeroLengthSectionAsEOF { + } + if sectionLength == 0 && r.opts.ZeroLengthSectionAsEOF { // normal ending for this read mode break } @@ -203,6 +238,13 @@ func (r *Reader) Inspect() (CarStats, error) { return CarStats{}, err } + if sectionLength < uint64(cidLen) { + // this case is handled different in the normal ReadNode() path since it + // slurps in the whole section bytes and decodes CID from there - so an + // error should come from a failing io.ReadFull + return CarStats{}, fmt.Errorf("section length shorter than CID length") + } + // is this a root block? (also account for duplicate root CIDs) if rootsPresentCount < len(stats.Roots) { for i, r := range stats.Roots { @@ -222,7 +264,26 @@ func (r *Reader) Inspect() (CarStats, error) { stats.MhTypeCounts[mhtype] = count + 1 blockLength := sectionLength - uint64(cidLen) - dr.Seek(int64(blockLength), io.SeekCurrent) + + if validateBlockHash { + // read the block data, hash it and compare it + buf := make([]byte, blockLength) + if _, err := io.ReadFull(dr, buf); err != nil { + return CarStats{}, err + } + + hashed, err := cp.Sum(buf) + if err != nil { + return CarStats{}, err + } + + if !hashed.Equals(c) { + return CarStats{}, fmt.Errorf("mismatch in content integrity, expected: %s, got: %s", c, hashed) + } + } else { + // otherwise, skip over it + dr.Seek(int64(blockLength), io.SeekCurrent) + } stats.BlockCount++ totalCidLength += uint64(cidLen) diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go index 58b9a3286..85605dadc 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/reader_test.go @@ -278,6 +278,7 @@ func TestInspect(t *testing.T) { tests := []struct { name string path string + carHex string zerLenAsEOF bool expectedStats carv2.CarStats }{ @@ -394,14 +395,43 @@ func TestInspect(t *testing.T) { }, }, }, + { + // A case where this _could_ be a valid CAR if we allowed identity CIDs + // and not matching block contents to exist, there's no block bytes in + // this. It will only fail if you don't validate the CID matches the, + // bytes (see TestInspectError for that case). + name: "IdentityCID", + // 47 {version:1,roots:[identity cid]} 25 identity cid (dag-json {"identity":"block"}) + carHex: "2f a265726f6f747381d82a581a0001a90200147b226964656e74697479223a22626c6f636b227d6776657273696f6e01 19 01a90200147b226964656e74697479223a22626c6f636b227d", + expectedStats: carv2.CarStats{ + Version: 1, + Roots: []cid.Cid{mustCidDecode("baguqeaaupmrgszdfnz2gs5dzei5ceytmn5rwwit5")}, + RootsPresent: true, + BlockCount: 1, + CodecCounts: map[multicodec.Code]uint64{multicodec.DagJson: 1}, + MhTypeCounts: map[multicodec.Code]uint64{multicodec.Identity: 1}, + AvgCidLength: 25, + MaxCidLength: 25, + MinCidLength: 25, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - reader, err := carv2.OpenReader(tt.path, carv2.ZeroLengthSectionAsEOF(tt.zerLenAsEOF)) - require.NoError(t, err) + var reader *carv2.Reader + var err error + if tt.path != "" { + reader, err = carv2.OpenReader(tt.path, carv2.ZeroLengthSectionAsEOF(tt.zerLenAsEOF)) + require.NoError(t, err) + } else { + byts, err := hex.DecodeString(strings.ReplaceAll(tt.carHex, " ", "")) + require.NoError(t, err) + reader, err = carv2.NewReader(bytes.NewReader(byts), carv2.ZeroLengthSectionAsEOF(tt.zerLenAsEOF)) + require.NoError(t, err) + } t.Cleanup(func() { require.NoError(t, reader.Close()) }) - stats, err := reader.Inspect() + stats, err := reader.Inspect(false) require.NoError(t, err) require.Equal(t, tt.expectedStats, stats) }) @@ -414,6 +444,7 @@ func TestInspectError(t *testing.T) { carHex string expectedOpenError string expectedInspectError string + validateBlockHash bool }{ { name: "BadCidV0", @@ -430,6 +461,31 @@ func TestInspectError(t *testing.T) { carHex: "11a265726f6f7473806776657273696f6e01e0e0e0e0a7060155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000", expectedInspectError: "invalid section data, length of read beyond allowable maximum", }, + { + name: "BadSectionLength2", + carHex: "3aa265726f6f747381d8305825000130302030303030303030303030303030303030303030303030303030303030303030306776657273696f6e01200130302030303030303030303030303030303030303030303030303030303030303030303030303030303030", + expectedInspectError: "section length shorter than CID length", + validateBlockHash: true, + }, + { + name: "BadBlockHash(SanityCheck)", // this should pass because we don't ask the CID be validated even though it doesn't match + // header cid data + carHex: "11a265726f6f7473806776657273696f6e 012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca ffffffffffffffffffff", + }, + { + name: "BadBlockHash", // same as above, but we ask for CID validation + // header cid data + carHex: "11a265726f6f7473806776657273696f6e 012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca ffffffffffffffffffff", + validateBlockHash: true, + expectedInspectError: "mismatch in content integrity, expected: bafkreiab2rek7wjiazkfrt3hbnqpljmu24226alszdlh6ivic2abgjubzi, got: bafkreiaaqoxrddiyuy6gxnks6ioqytxhq5a7tchm2mm5htigznwiljukmm", + }, + { + name: "IdentityCID", // a case where this _could_ be a valid CAR if we allowed identity CIDs and not matching block contents to exist, there's no block bytes in this + // 47 {version:1,roots:[identity cid]} 25 identity cid (dag-json {"identity":"block"}) + carHex: "2f a265726f6f747381d82a581a0001a90200147b226964656e74697479223a22626c6f636b227d6776657273696f6e01 19 01a90200147b226964656e74697479223a22626c6f636b227d", + validateBlockHash: true, + expectedInspectError: "mismatch in content integrity, expected: baguqeaaupmrgszdfnz2gs5dzei5ceytmn5rwwit5, got: baguqeaaa", + }, // the bad index tests are manually constructed from this single-block CARv2 by adjusting the Uint32 and Uint64 values in the index: // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset // 0aa16776657273696f6e02 00000000000000000000000000000000330000000000000041000000000000007400000000000000 11a265726f6f7473806776657273696f6e012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000 8108 01000000 1200000000000000 01000000 28000000 2800000000000000 01d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca 1200000000000000 @@ -466,16 +522,16 @@ func TestInspectError(t *testing.T) { reader, err := carv2.NewReader(bytes.NewReader(car)) if tt.expectedOpenError != "" { require.Error(t, err) - require.Equal(t, err.Error(), tt.expectedOpenError) + require.Equal(t, tt.expectedOpenError, err.Error()) return } else { require.NoError(t, err) } t.Cleanup(func() { require.NoError(t, reader.Close()) }) - _, err = reader.Inspect() + _, err = reader.Inspect(tt.validateBlockHash) if tt.expectedInspectError != "" { require.Error(t, err) - require.Equal(t, err.Error(), tt.expectedInspectError) + require.Equal(t, tt.expectedInspectError, err.Error()) } else { require.NoError(t, err) } From 94eb990a7ada0fb66174b1d332542e7b913e7426 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 1 Jul 2022 05:22:51 +0200 Subject: [PATCH 3686/3817] test: add fuzzing for reader#Inspect This commit was moved from ipld/go-car@952fcb9c29769315109336b24e5a8611466f0fae --- ipld/car/.github/workflows/go-fuzz.yml | 2 +- ipld/car/v2/fuzz_test.go | 55 ++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/ipld/car/.github/workflows/go-fuzz.yml b/ipld/car/.github/workflows/go-fuzz.yml index 3aa7f8530..548a4a91d 100644 --- a/ipld/car/.github/workflows/go-fuzz.yml +++ b/ipld/car/.github/workflows/go-fuzz.yml @@ -27,7 +27,7 @@ jobs: strategy: fail-fast: true matrix: - target: [ "BlockReader", "Reader", "Index" ] + target: [ "BlockReader", "Reader", "Index", "Inspect" ] runs-on: ubuntu-latest name: Fuzz V2 ${{ matrix.target }} steps: diff --git a/ipld/car/v2/fuzz_test.go b/ipld/car/v2/fuzz_test.go index 8187457b5..c74737505 100644 --- a/ipld/car/v2/fuzz_test.go +++ b/ipld/car/v2/fuzz_test.go @@ -13,6 +13,7 @@ import ( car "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-car/v2/internal/carv1" ) // v1FixtureStr is a clean carv1 single-block, single-root CAR @@ -116,3 +117,57 @@ func FuzzIndex(f *testing.F) { index.ReadFrom(bytes.NewReader(data)) }) } + +func FuzzInspect(f *testing.F) { + seedWithCarFiles(f) + + f.Fuzz(func(t *testing.T, data []byte) { + reader, err := car.NewReader(bytes.NewReader(data)) + if err != nil { + return + } + + // Do differential fuzzing between Inspect and the normal parser + _, inspectErr := reader.Inspect(true) + if inspectErr == nil { + return + } + + reader, err = car.NewReader(bytes.NewReader(data)) + if err != nil { + t.Fatal("second NewReader on same data failed", err.Error()) + } + + if i := reader.IndexReader(); i != nil { + _, err = index.ReadFrom(i) + if err != nil { + return + } + } + + dr := reader.DataReader() + + _, err = carv1.ReadHeader(dr, carv1.DefaultMaxAllowedHeaderSize) + if err != nil { + return + } + + blocks, err := car.NewBlockReader(dr) + if err != nil { + return + } + + for { + _, err := blocks.Next() + if err != nil { + if err == io.EOF { + break + } + // caught error as expected + return + } + } + + t.Fatal("Inspect found error but we red this file correctly:", inspectErr.Error()) + }) +} From 39fdfc066b261c9d866585035f6ad77afad95072 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Fri, 1 Jul 2022 15:49:42 +0100 Subject: [PATCH 3687/3817] Use streaming APIs to verify the hash of blocks in CAR `Inspect` `go-cid` exposes `Sum` API that facilitates calculation of the CID from `[]byte` payload. `go-multihash` now exposes `SumStream` which can calculate digest from `io.Reader` as well as `[]byte`. But, unfortunately the equivalent API does not exist in `go-cid`. To avoid copying the entire block into memory, implement CID calculation using the streaming multihash sum during inspection of CAR payload. This commit was moved from ipld/go-car@f2498bcfddbce5f67e94d13ef8d8b2117c37cd10 --- ipld/car/v2/reader.go | 42 +++++++++++++++++++++++++------------- ipld/car/v2/reader_test.go | 4 ++-- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index c3ef36535..cd10b81da 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -11,6 +11,7 @@ import ( "github.com/ipld/go-car/v2/internal/carv1/util" internalio "github.com/ipld/go-car/v2/internal/io" "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-multihash" "github.com/multiformats/go-varint" "golang.org/x/exp/mmap" ) @@ -266,23 +267,36 @@ func (r *Reader) Inspect(validateBlockHash bool) (CarStats, error) { blockLength := sectionLength - uint64(cidLen) if validateBlockHash { - // read the block data, hash it and compare it - buf := make([]byte, blockLength) - if _, err := io.ReadFull(dr, buf); err != nil { - return CarStats{}, err + // Use multihash.SumStream to avoid having to copy the entire block content into memory. + // The SumStream uses a buffered copy to write bytes into the hasher which will take + // advantage of streaming hash calculation depending on the hash function. + // TODO: introduce SumStream in go-cid to simplify the code here. + blockReader := io.LimitReader(dr, int64(blockLength)) + mhl := cp.MhLength + if mhtype == multicodec.Identity { + mhl = -1 } - - hashed, err := cp.Sum(buf) + mh, err := multihash.SumStream(blockReader, cp.MhType, mhl) if err != nil { return CarStats{}, err } - - if !hashed.Equals(c) { - return CarStats{}, fmt.Errorf("mismatch in content integrity, expected: %s, got: %s", c, hashed) + var wantCid cid.Cid + switch cp.Version { + case 0: + wantCid = cid.NewCidV0(mh) + case 1: + wantCid = cid.NewCidV1(cp.Codec, mh) + default: + return CarStats{}, fmt.Errorf("invalid cid version: %d", cp.Version) + } + if !wantCid.Equals(c) { + return CarStats{}, fmt.Errorf("mismatch in content integrity, expected: %s, got: %s", wantCid, c) } } else { // otherwise, skip over it - dr.Seek(int64(blockLength), io.SeekCurrent) + if _, err := dr.Seek(int64(blockLength), io.SeekCurrent); err != nil { + return CarStats{}, err + } } stats.BlockCount++ @@ -294,11 +308,11 @@ func (r *Reader) Inspect(validateBlockHash bool) (CarStats, error) { if uint64(cidLen) > stats.MaxCidLength { stats.MaxCidLength = uint64(cidLen) } - if uint64(blockLength) < minBlockLength { - minBlockLength = uint64(blockLength) + if blockLength < minBlockLength { + minBlockLength = blockLength } - if uint64(blockLength) > stats.MaxBlockLength { - stats.MaxBlockLength = uint64(blockLength) + if blockLength > stats.MaxBlockLength { + stats.MaxBlockLength = blockLength } } diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go index 85605dadc..5686d8442 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/reader_test.go @@ -477,14 +477,14 @@ func TestInspectError(t *testing.T) { // header cid data carHex: "11a265726f6f7473806776657273696f6e 012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca ffffffffffffffffffff", validateBlockHash: true, - expectedInspectError: "mismatch in content integrity, expected: bafkreiab2rek7wjiazkfrt3hbnqpljmu24226alszdlh6ivic2abgjubzi, got: bafkreiaaqoxrddiyuy6gxnks6ioqytxhq5a7tchm2mm5htigznwiljukmm", + expectedInspectError: "mismatch in content integrity, expected: bafkreiaaqoxrddiyuy6gxnks6ioqytxhq5a7tchm2mm5htigznwiljukmm, got: bafkreiab2rek7wjiazkfrt3hbnqpljmu24226alszdlh6ivic2abgjubzi", }, { name: "IdentityCID", // a case where this _could_ be a valid CAR if we allowed identity CIDs and not matching block contents to exist, there's no block bytes in this // 47 {version:1,roots:[identity cid]} 25 identity cid (dag-json {"identity":"block"}) carHex: "2f a265726f6f747381d82a581a0001a90200147b226964656e74697479223a22626c6f636b227d6776657273696f6e01 19 01a90200147b226964656e74697479223a22626c6f636b227d", validateBlockHash: true, - expectedInspectError: "mismatch in content integrity, expected: baguqeaaupmrgszdfnz2gs5dzei5ceytmn5rwwit5, got: baguqeaaa", + expectedInspectError: "mismatch in content integrity, expected: baguqeaaa, got: baguqeaaupmrgszdfnz2gs5dzei5ceytmn5rwwit5", }, // the bad index tests are manually constructed from this single-block CARv2 by adjusting the Uint32 and Uint64 values in the index: // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset From c8d51501eb84638e8b8b7b68a3286270daa1caee Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Sat, 2 Jul 2022 09:51:07 +0100 Subject: [PATCH 3688/3817] Use consistent CID mismatch error in `Inspect` and `BlockReader.Next` This reverts the earlier changes to get the message consistent. Note, the CID we expect is the one in the CAR payload, not the calculated CID for the block. This commit was moved from ipld/go-car@38f20d6e6c3ef292680ff2f14d05432a331a1f81 --- ipld/car/v2/reader.go | 10 +++++----- ipld/car/v2/reader_test.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index cd10b81da..f67638af2 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -280,17 +280,17 @@ func (r *Reader) Inspect(validateBlockHash bool) (CarStats, error) { if err != nil { return CarStats{}, err } - var wantCid cid.Cid + var gotCid cid.Cid switch cp.Version { case 0: - wantCid = cid.NewCidV0(mh) + gotCid = cid.NewCidV0(mh) case 1: - wantCid = cid.NewCidV1(cp.Codec, mh) + gotCid = cid.NewCidV1(cp.Codec, mh) default: return CarStats{}, fmt.Errorf("invalid cid version: %d", cp.Version) } - if !wantCid.Equals(c) { - return CarStats{}, fmt.Errorf("mismatch in content integrity, expected: %s, got: %s", wantCid, c) + if !gotCid.Equals(c) { + return CarStats{}, fmt.Errorf("mismatch in content integrity, expected: %s, got: %s", c, gotCid) } } else { // otherwise, skip over it diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go index 5686d8442..85605dadc 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/reader_test.go @@ -477,14 +477,14 @@ func TestInspectError(t *testing.T) { // header cid data carHex: "11a265726f6f7473806776657273696f6e 012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca ffffffffffffffffffff", validateBlockHash: true, - expectedInspectError: "mismatch in content integrity, expected: bafkreiaaqoxrddiyuy6gxnks6ioqytxhq5a7tchm2mm5htigznwiljukmm, got: bafkreiab2rek7wjiazkfrt3hbnqpljmu24226alszdlh6ivic2abgjubzi", + expectedInspectError: "mismatch in content integrity, expected: bafkreiab2rek7wjiazkfrt3hbnqpljmu24226alszdlh6ivic2abgjubzi, got: bafkreiaaqoxrddiyuy6gxnks6ioqytxhq5a7tchm2mm5htigznwiljukmm", }, { name: "IdentityCID", // a case where this _could_ be a valid CAR if we allowed identity CIDs and not matching block contents to exist, there's no block bytes in this // 47 {version:1,roots:[identity cid]} 25 identity cid (dag-json {"identity":"block"}) carHex: "2f a265726f6f747381d82a581a0001a90200147b226964656e74697479223a22626c6f636b227d6776657273696f6e01 19 01a90200147b226964656e74697479223a22626c6f636b227d", validateBlockHash: true, - expectedInspectError: "mismatch in content integrity, expected: baguqeaaa, got: baguqeaaupmrgszdfnz2gs5dzei5ceytmn5rwwit5", + expectedInspectError: "mismatch in content integrity, expected: baguqeaaupmrgszdfnz2gs5dzei5ceytmn5rwwit5, got: baguqeaaa", }, // the bad index tests are manually constructed from this single-block CARv2 by adjusting the Uint32 and Uint64 values in the index: // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset From 8685c69e72a2df3275d166ea18081dc1d7debfb2 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Sat, 2 Jul 2022 10:10:41 +0100 Subject: [PATCH 3689/3817] Benchmark `Reader.Inspect` with and without hash validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Benchmark the `Reader.Inspect` with and without hash validation using a randomly generated CARv2 file of size 10 MiB. Results from running the benchmark in parallel locally on MacOS `Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz` repeated 10 times: ``` Reader_InspectWithBlockValidation-8 5.30ms ±48% Reader_InspectWithoutBlockValidation-8 231µs ±42% name speed Reader_InspectWithBlockValidation-8 2.08GB/s ±35% Reader_InspectWithoutBlockValidation-8 46.8GB/s ±32% name alloc/op Reader_InspectWithBlockValidation-8 10.7MB ± 0% Reader_InspectWithoutBlockValidation-8 60.7kB ± 0% name allocs/op Reader_InspectWithBlockValidation-8 4.54k ± 0% Reader_InspectWithoutBlockValidation-8 2.29k ± 0% ``` This commit was moved from ipld/go-car@3a0f2a4b93df6696b7651c5c29a7b7281331abe5 --- ipld/car/v2/bench_test.go | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/ipld/car/v2/bench_test.go b/ipld/car/v2/bench_test.go index 6e7669359..e413180bb 100644 --- a/ipld/car/v2/bench_test.go +++ b/ipld/car/v2/bench_test.go @@ -119,6 +119,57 @@ func BenchmarkExtractV1UsingReader(b *testing.B) { }) } +// BenchmarkReader_InspectWithBlockValidation benchmarks Reader.Inspect with block hash validation +// for a randomly generated CARv2 file of size 10 MiB. +func BenchmarkReader_InspectWithBlockValidation(b *testing.B) { + path := filepath.Join(b.TempDir(), "bench-large-v2.car") + generateRandomCarV2File(b, path, 10<<20) // 10 MiB + defer os.Remove(path) + + info, err := os.Stat(path) + if err != nil { + b.Fatal(err) + } + b.SetBytes(info.Size()) + b.ReportAllocs() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + benchmarkInspect(b, path, true) + } + }) +} + +// BenchmarkReader_InspectWithoutBlockValidation benchmarks Reader.Inspect without block hash +// validation for a randomly generated CARv2 file of size 10 MiB. +func BenchmarkReader_InspectWithoutBlockValidation(b *testing.B) { + path := filepath.Join(b.TempDir(), "bench-large-v2.car") + generateRandomCarV2File(b, path, 10<<20) // 10 MiB + defer os.Remove(path) + + info, err := os.Stat(path) + if err != nil { + b.Fatal(err) + } + b.SetBytes(info.Size()) + b.ReportAllocs() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + benchmarkInspect(b, path, false) + } + }) +} + +func benchmarkInspect(b *testing.B, path string, validateBlockHash bool) { + reader, err := carv2.OpenReader(path) + if err != nil { + b.Fatal(err) + } + if _, err := reader.Inspect(validateBlockHash); err != nil { + b.Fatal(err) + } +} func generateRandomCarV2File(b *testing.B, path string, minTotalBlockSize int) { // Use fixed RNG for determinism across benchmarks. rng := rand.New(rand.NewSource(1413)) From 86c35fc117075a896784f754153faa5d388491e0 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Sun, 3 Jul 2022 09:22:25 +0100 Subject: [PATCH 3690/3817] Drop repeated package name from `CarStats` Cosmetic refactor to rename `car.CarStats` to `car.Stats`, which looks more fluent when using the API. This commit was moved from ipld/go-car@1fabdd7cf8d33f8b58aeea33f1ccc236bcd3b2bc --- ipld/car/v2/reader.go | 40 +++++++++++++++++++------------------- ipld/car/v2/reader_test.go | 12 ++++++------ 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index f67638af2..df73f2937 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -122,8 +122,8 @@ func (r *Reader) IndexReader() io.ReaderAt { return internalio.NewOffsetReadSeeker(r.r, int64(r.Header.IndexOffset)) } -// CarStats is returned by an Inspect() call -type CarStats struct { +// Stats is returned by an Inspect() call +type Stats struct { Version uint64 Header Header Roots []cid.Cid @@ -142,7 +142,7 @@ type CarStats struct { } // Inspect does a quick scan of a CAR, performing basic validation of the format -// and returning a CarStats object that provides a high-level description of the +// and returning a Stats object that provides a high-level description of the // contents of the CAR. // Inspect works for CARv1 and CARv2 contents. A CARv1 will return an // uninitialized Header value. @@ -169,26 +169,26 @@ type CarStats struct { // them and have any reason to not trust the source. // // * Blocks use codecs that your system doesn't have access to—which may mean -// you can't traverse a DAG or use the contained data. CarStats#CodecCounts +// you can't traverse a DAG or use the contained data. Stats.CodecCounts // contains a list of codecs found in the CAR so this can be checked. // // * CIDs use multihashes that your system doesn't have access to—which will // mean you can't validate block hashes are correct (using validateBlockHash -// in this case will result in a failure). CarStats#MhTypeCounts contains a -// list of multihashes found in the CAR so this can bechecked. +// in this case will result in a failure). Stats.MhTypeCounts contains a +// list of multihashes found in the CAR so this can be checked. // // * The presence of IDENTITY CIDs, which may not be supported (or desired) by -// the consumer of the CAR. CarStats#CodecCounts can determine the presence +// the consumer of the CAR. Stats.CodecCounts can determine the presence // of IDENTITY CIDs. // // * Roots: the number of roots, duplicates, and whether they are related to the -// blocks contained within the CAR. CarStats contains a list of Roots and a +// blocks contained within the CAR. Stats contains a list of Roots and a // RootsPresent bool so further checks can be performed. // // * DAG completeness is not checked. Any properties relating to the DAG, or // DAGs contained within a CAR are the responsibility of the user to check. -func (r *Reader) Inspect(validateBlockHash bool) (CarStats, error) { - stats := CarStats{ +func (r *Reader) Inspect(validateBlockHash bool) (Stats, error) { + stats := Stats{ Version: r.Version, Header: r.Header, CodecCounts: make(map[multicodec.Code]uint64), @@ -206,7 +206,7 @@ func (r *Reader) Inspect(validateBlockHash bool) (CarStats, error) { // read roots, not using Roots(), because we need the offset setup in the data trader header, err := carv1.ReadHeader(dr, r.opts.MaxAllowedHeaderSize) if err != nil { - return CarStats{}, err + return Stats{}, err } stats.Roots = header.Roots var rootsPresentCount int @@ -219,7 +219,7 @@ func (r *Reader) Inspect(validateBlockHash bool) (CarStats, error) { if err == io.EOF { // if the length of bytes read is non-zero when the error is EOF then signal an unclean EOF. if sectionLength > 0 { - return CarStats{}, io.ErrUnexpectedEOF + return Stats{}, io.ErrUnexpectedEOF } // otherwise, this is a normal ending break @@ -230,20 +230,20 @@ func (r *Reader) Inspect(validateBlockHash bool) (CarStats, error) { break } if sectionLength > r.opts.MaxAllowedSectionSize { - return CarStats{}, util.ErrSectionTooLarge + return Stats{}, util.ErrSectionTooLarge } // decode just the CID bytes cidLen, c, err := cid.CidFromReader(dr) if err != nil { - return CarStats{}, err + return Stats{}, err } if sectionLength < uint64(cidLen) { // this case is handled different in the normal ReadNode() path since it // slurps in the whole section bytes and decodes CID from there - so an // error should come from a failing io.ReadFull - return CarStats{}, fmt.Errorf("section length shorter than CID length") + return Stats{}, fmt.Errorf("section length shorter than CID length") } // is this a root block? (also account for duplicate root CIDs) @@ -278,7 +278,7 @@ func (r *Reader) Inspect(validateBlockHash bool) (CarStats, error) { } mh, err := multihash.SumStream(blockReader, cp.MhType, mhl) if err != nil { - return CarStats{}, err + return Stats{}, err } var gotCid cid.Cid switch cp.Version { @@ -287,15 +287,15 @@ func (r *Reader) Inspect(validateBlockHash bool) (CarStats, error) { case 1: gotCid = cid.NewCidV1(cp.Codec, mh) default: - return CarStats{}, fmt.Errorf("invalid cid version: %d", cp.Version) + return Stats{}, fmt.Errorf("invalid cid version: %d", cp.Version) } if !gotCid.Equals(c) { - return CarStats{}, fmt.Errorf("mismatch in content integrity, expected: %s, got: %s", c, gotCid) + return Stats{}, fmt.Errorf("mismatch in content integrity, expected: %s, got: %s", c, gotCid) } } else { // otherwise, skip over it if _, err := dr.Seek(int64(blockLength), io.SeekCurrent); err != nil { - return CarStats{}, err + return Stats{}, err } } @@ -329,7 +329,7 @@ func (r *Reader) Inspect(validateBlockHash bool) (CarStats, error) { // is intended to be a fast initial scan ind, size, err := index.ReadFromWithSize(r.IndexReader()) if err != nil { - return CarStats{}, err + return Stats{}, err } stats.IndexCodec = ind.Codec() stats.IndexSize = uint64(size) diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go index 85605dadc..7c43d9311 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/reader_test.go @@ -280,12 +280,12 @@ func TestInspect(t *testing.T) { path string carHex string zerLenAsEOF bool - expectedStats carv2.CarStats + expectedStats carv2.Stats }{ { name: "IndexlessCarV2", path: "testdata/sample-v2-indexless.car", - expectedStats: carv2.CarStats{ + expectedStats: carv2.Stats{ Version: 2, Header: carv2.Header{ Characteristics: carv2.Characteristics{0, 0}, @@ -316,7 +316,7 @@ func TestInspect(t *testing.T) { // same payload as IndexlessCarV2, so only difference is the Version & Header name: "CarV1", path: "testdata/sample-v1.car", - expectedStats: carv2.CarStats{ + expectedStats: carv2.Stats{ Version: 1, Header: carv2.Header{}, Roots: []cid.Cid{mustCidDecode("bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy")}, @@ -342,7 +342,7 @@ func TestInspect(t *testing.T) { // same payload as IndexlessCarV2, so only difference is the Header name: "CarV2ProducedByBlockstore", path: "testdata/sample-rw-bs-v2.car", - expectedStats: carv2.CarStats{ + expectedStats: carv2.Stats{ Version: 2, Header: carv2.Header{ DataOffset: 1464, @@ -373,7 +373,7 @@ func TestInspect(t *testing.T) { name: "CarV1VersionWithZeroLenSectionIsOne", path: "testdata/sample-v1-with-zero-len-section.car", zerLenAsEOF: true, - expectedStats: carv2.CarStats{ + expectedStats: carv2.Stats{ Version: 1, Header: carv2.Header{}, Roots: []cid.Cid{mustCidDecode("bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy")}, @@ -403,7 +403,7 @@ func TestInspect(t *testing.T) { name: "IdentityCID", // 47 {version:1,roots:[identity cid]} 25 identity cid (dag-json {"identity":"block"}) carHex: "2f a265726f6f747381d82a581a0001a90200147b226964656e74697479223a22626c6f636b227d6776657273696f6e01 19 01a90200147b226964656e74697479223a22626c6f636b227d", - expectedStats: carv2.CarStats{ + expectedStats: carv2.Stats{ Version: 1, Roots: []cid.Cid{mustCidDecode("baguqeaaupmrgszdfnz2gs5dzei5ceytmn5rwwit5")}, RootsPresent: true, From 9da4299cafd14dcd8a969308cd8dafb456720a81 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Mon, 4 Jul 2022 09:41:53 +0100 Subject: [PATCH 3691/3817] Return error when section length is invalid `varint` Return potential error when reading section error as varint. Add test to verify the error is indeed returned. Use `errors.New` instead of `fmt.Errorf` when no formatting is needed in error message. This commit was moved from ipld/go-car@8bb203bef695893ae57a26a32ea2ac0b355de791 --- ipld/car/v2/reader.go | 4 +++- ipld/car/v2/reader_test.go | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index df73f2937..c34a0672d 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -1,6 +1,7 @@ package car import ( + "errors" "fmt" "io" "math" @@ -224,6 +225,7 @@ func (r *Reader) Inspect(validateBlockHash bool) (Stats, error) { // otherwise, this is a normal ending break } + return Stats{}, err } if sectionLength == 0 && r.opts.ZeroLengthSectionAsEOF { // normal ending for this read mode @@ -243,7 +245,7 @@ func (r *Reader) Inspect(validateBlockHash bool) (Stats, error) { // this case is handled different in the normal ReadNode() path since it // slurps in the whole section bytes and decodes CID from there - so an // error should come from a failing io.ReadFull - return Stats{}, fmt.Errorf("section length shorter than CID length") + return Stats{}, errors.New("section length shorter than CID length") } // is this a root block? (also account for duplicate root CIDs) diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go index 7c43d9311..ed2b78e25 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/reader_test.go @@ -467,6 +467,11 @@ func TestInspectError(t *testing.T) { expectedInspectError: "section length shorter than CID length", validateBlockHash: true, }, + { + name: "BadSectionLength3", + carHex: "11a265726f6f7473f66776657273696f6e0180", + expectedInspectError: "unexpected EOF", + }, { name: "BadBlockHash(SanityCheck)", // this should pass because we don't ask the CID be validated even though it doesn't match // header cid data From 8006b6f3a9f5948d9697736131394ed491ac1652 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Mon, 4 Jul 2022 20:19:12 +0100 Subject: [PATCH 3692/3817] Fix fuzz CI job This commit was moved from ipld/go-car@6f66dc0b59af60636b29b575ad581928f642c367 --- ipld/car/.github/workflows/go-fuzz.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ipld/car/.github/workflows/go-fuzz.yml b/ipld/car/.github/workflows/go-fuzz.yml index 548a4a91d..cb7b4b272 100644 --- a/ipld/car/.github/workflows/go-fuzz.yml +++ b/ipld/car/.github/workflows/go-fuzz.yml @@ -1,4 +1,4 @@ -on: [push, pull_request] +on: [ push, pull_request ] name: Go Fuzz jobs: @@ -21,8 +21,7 @@ jobs: go version go env - name: Run Fuzzing for 1m - with: - run: go test -v -fuzz=Fuzz${{ matrix.target }} -fuzztime=1m . + run: go test -v -fuzz=Fuzz${{ matrix.target }} -fuzztime=1m . v2: strategy: fail-fast: true @@ -42,7 +41,6 @@ jobs: go version go env - name: Run Fuzzing for 1m - with: - run: | - cd v2 - go test -v -fuzz=Fuzz${{ matrix.target }} -fuzztime=1m . + run: | + cd v2 + go test -v -fuzz=Fuzz${{ matrix.target }} -fuzztime=1m . From 51fed4dfb3d0288d21801752ad98bf98a8077307 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Mon, 4 Jul 2022 20:08:34 +0100 Subject: [PATCH 3693/3817] Revert changes to `index.Index` while keeping most of security fixes Revert the changes to `index.Index` interface such that it is the same as the current go-car main branch head. Reverting the changes, however, means that unmarshalling untrusted indices is indeed dangerous and should not be done on untrusted files. Note, the `carv2.Reader` APIs are changed to return errors as well as readers when getting `DataReader` and `IndexReader`. This is to accommodate issues detected by fuzz testing while removing boiler plate code in internal IO reader conversion. This is a breaking change to the current API but should be straight forward to roll out. Remove index fuzz tests and change inspection to only read the index codec instead of reading the entire index. This commit was moved from ipld/go-car@4bc677484af39d1153e075ae162c5ebcc6e396a3 --- ipld/car/v2/bench_test.go | 6 +- ipld/car/v2/blockstore/readonly.go | 59 ++++++-- ipld/car/v2/blockstore/readonly_test.go | 4 +- ipld/car/v2/blockstore/readwrite.go | 13 +- ipld/car/v2/blockstore/readwrite_test.go | 49 +++--- ipld/car/v2/example_test.go | 6 +- ipld/car/v2/fuzz_test.go | 69 +++------ ipld/car/v2/index/example_test.go | 36 ++--- ipld/car/v2/index/index.go | 64 ++++---- ipld/car/v2/index/index_test.go | 6 +- ipld/car/v2/index/indexsorted.go | 141 ++++++------------ ipld/car/v2/index/indexsorted_test.go | 15 +- ipld/car/v2/index/mhindexsorted.go | 74 +++------ ipld/car/v2/index/mhindexsorted_test.go | 7 +- ipld/car/v2/index/testutil/equal_index.go | 71 --------- ipld/car/v2/index_gen.go | 12 +- ipld/car/v2/index_gen_test.go | 7 +- ipld/car/v2/internal/io/fullReaderAt.go | 20 --- ipld/car/v2/internal/io/offset_read_seeker.go | 30 +--- ipld/car/v2/reader.go | 37 +++-- ipld/car/v2/reader_test.go | 39 +++-- ipld/car/v2/writer_test.go | 11 +- 22 files changed, 304 insertions(+), 472 deletions(-) delete mode 100644 ipld/car/v2/index/testutil/equal_index.go delete mode 100644 ipld/car/v2/internal/io/fullReaderAt.go diff --git a/ipld/car/v2/bench_test.go b/ipld/car/v2/bench_test.go index e413180bb..2f7dcc63d 100644 --- a/ipld/car/v2/bench_test.go +++ b/ipld/car/v2/bench_test.go @@ -108,7 +108,11 @@ func BenchmarkExtractV1UsingReader(b *testing.B) { if err != nil { b.Fatal(err) } - _, err = io.Copy(dst, reader.DataReader()) + dr, err := reader.DataReader() + if err != nil { + b.Fatal(err) + } + _, err = io.Copy(dst, dr) if err != nil { b.Fatal(err) } diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 307486d79..32c49046b 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -120,15 +120,28 @@ func NewReadOnly(backing io.ReaderAt, idx index.Index, opts ...carv2.Option) (*R } if idx == nil { if v2r.Header.HasIndex() { - idx, err = index.ReadFrom(v2r.IndexReader()) + ir, err := v2r.IndexReader() if err != nil { return nil, err } - } else if idx, err = generateIndex(v2r.DataReader(), opts...); err != nil { - return nil, err + idx, err = index.ReadFrom(ir) + if err != nil { + return nil, err + } + } else { + dr, err := v2r.DataReader() + if err != nil { + return nil, err + } + if idx, err = generateIndex(dr, opts...); err != nil { + return nil, err + } } } - b.backing = v2r.DataReader() + b.backing, err = v2r.DataReader() + if err != nil { + return nil, err + } b.idx = idx return b, nil default: @@ -142,7 +155,11 @@ func readVersion(at io.ReaderAt, opts ...carv2.Option) (uint64, error) { case io.Reader: rr = r default: - rr = internalio.NewOffsetReadSeeker(r, 0) + var err error + rr, err = internalio.NewOffsetReadSeeker(r, 0) + if err != nil { + return 0, err + } } return carv2.ReadVersion(rr, opts...) } @@ -157,7 +174,11 @@ func generateIndex(at io.ReaderAt, opts ...carv2.Option) (index.Index, error) { return nil, err } default: - rs = internalio.NewOffsetReadSeeker(r, 0) + var err error + rs, err = internalio.NewOffsetReadSeeker(r, 0) + if err != nil { + return nil, err + } } // Note, we do not set any write options so that all write options fall back onto defaults. @@ -183,7 +204,7 @@ func OpenReadOnly(path string, opts ...carv2.Option) (*ReadOnly, error) { } func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) { - r, err := internalio.NewOffsetReadSeekerWithError(b.backing, idx) + r, err := internalio.NewOffsetReadSeeker(b.backing, idx) if err != nil { return cid.Cid{}, nil, err } @@ -216,8 +237,11 @@ func (b *ReadOnly) Has(ctx context.Context, key cid.Cid) (bool, error) { var fnFound bool var fnErr error err := b.idx.GetAll(key, func(offset uint64) bool { - uar := internalio.NewOffsetReadSeeker(b.backing, int64(offset)) - var err error + uar, err := internalio.NewOffsetReadSeeker(b.backing, int64(offset)) + if err != nil { + fnErr = err + return false + } _, err = varint.ReadUvarint(uar) if err != nil { fnErr = err @@ -317,7 +341,11 @@ func (b *ReadOnly) GetSize(ctx context.Context, key cid.Cid) (int, error) { fnSize := -1 var fnErr error err := b.idx.GetAll(key, func(offset uint64) bool { - rdr := internalio.NewOffsetReadSeeker(b.backing, int64(offset)) + rdr, err := internalio.NewOffsetReadSeeker(b.backing, int64(offset)) + if err != nil { + fnErr = err + return false + } sectionLen, err := varint.ReadUvarint(rdr) if err != nil { fnErr = err @@ -401,7 +429,10 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { } // TODO we may use this walk for populating the index, and we need to be able to iterate keys in this way somewhere for index generation. In general though, when it's asked for all keys from a blockstore with an index, we should iterate through the index when possible rather than linear reads through the full car. - rdr := internalio.NewOffsetReadSeeker(b.backing, 0) + rdr, err := internalio.NewOffsetReadSeeker(b.backing, 0) + if err != nil { + return nil, err + } header, err := carv1.ReadHeader(rdr, b.opts.MaxAllowedHeaderSize) if err != nil { b.mu.RUnlock() // don't hold the mutex forever @@ -492,7 +523,11 @@ func (b *ReadOnly) HashOnRead(bool) { // Roots returns the root CIDs of the backing CAR. func (b *ReadOnly) Roots() ([]cid.Cid, error) { - header, err := carv1.ReadHeader(internalio.NewOffsetReadSeeker(b.backing, 0), b.opts.MaxAllowedHeaderSize) + ors, err := internalio.NewOffsetReadSeeker(b.backing, 0) + if err != nil { + return nil, err + } + header, err := carv1.ReadHeader(ors, b.opts.MaxAllowedHeaderSize) if err != nil { return nil, fmt.Errorf("error reading car header: %w", err) } diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index f55a1e50c..5a947a75b 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -226,7 +226,9 @@ func newV1ReaderFromV2File(t *testing.T, carv2Path string, zeroLenSectionAsEOF b t.Cleanup(func() { f.Close() }) v2r, err := carv2.NewReader(f) require.NoError(t, err) - v1r, err := newV1Reader(v2r.DataReader(), zeroLenSectionAsEOF) + dr, err := v2r.DataReader() + require.NoError(t, err) + v1r, err := newV1Reader(dr, zeroLenSectionAsEOF) require.NoError(t, err) return v1r } diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 0fad9893b..774f37ff6 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -139,7 +139,7 @@ func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.Option) (*ReadWri offset = 0 } rwbs.dataWriter = internalio.NewOffsetWriter(rwbs.f, offset) - v1r, err := internalio.NewOffsetReadSeekerWithError(rwbs.f, offset) + v1r, err := internalio.NewOffsetReadSeeker(rwbs.f, offset) if err != nil { return nil, err } @@ -169,11 +169,11 @@ func (b *ReadWrite) initWithRoots(v2 bool, roots []cid.Cid) error { return carv1.WriteHeader(&carv1.CarHeader{Roots: roots, Version: 1}, b.dataWriter) } -func (b *ReadWrite) resumeWithRoots(v2 bool, roots []cid.Cid, opts ...carv2.Option) error { +func (b *ReadWrite) resumeWithRoots(v2 bool, roots []cid.Cid) error { // On resumption it is expected that the CARv2 Pragma, and the CARv1 header is successfully written. // Otherwise we cannot resume from the file. // Read pragma to assert if b.f is indeed a CARv2. - version, err := carv2.ReadVersion(b.f, opts...) + version, err := carv2.ReadVersion(b.f) if err != nil { // The file is not a valid CAR file and cannot resume from it. // Or the write must have failed before pragma was written. @@ -193,7 +193,7 @@ func (b *ReadWrite) resumeWithRoots(v2 bool, roots []cid.Cid, opts ...carv2.Opti // Check if file was finalized by trying to read the CARv2 header. // We check because if finalized the CARv1 reader behaviour needs to be adjusted since // EOF will not signify end of CARv1 payload. i.e. index is most likely present. - r, err := internalio.NewOffsetReadSeekerWithError(b.f, carv2.PragmaSize) + r, err := internalio.NewOffsetReadSeeker(b.f, carv2.PragmaSize) if err != nil { return err } @@ -223,7 +223,10 @@ func (b *ReadWrite) resumeWithRoots(v2 bool, roots []cid.Cid, opts ...carv2.Opti } // Use the given CARv1 padding to instantiate the CARv1 reader on file. - v1r := internalio.NewOffsetReadSeeker(b.ronly.backing, 0) + v1r, err := internalio.NewOffsetReadSeeker(b.ronly.backing, 0) + if err != nil { + return err + } header, err := carv1.ReadHeader(v1r, b.opts.MaxAllowedHeaderSize) if err != nil { // Cannot read the CARv1 header; the file is most likely corrupt. diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 6f64ac72f..11cc99a22 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -21,7 +21,6 @@ import ( carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/blockstore" "github.com/ipld/go-car/v2/index" - "github.com/ipld/go-car/v2/index/testutil" "github.com/ipld/go-car/v2/internal/carv1" "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" @@ -488,7 +487,9 @@ func TestBlockstoreResumption(t *testing.T) { wantPayloadReader, err := carv1.NewCarReader(v1f) require.NoError(t, err) - gotPayloadReader, err := carv1.NewCarReader(v2r.DataReader()) + dr, err := v2r.DataReader() + require.NoError(t, err) + gotPayloadReader, err := carv1.NewCarReader(dr) require.NoError(t, err) require.Equal(t, wantPayloadReader.Header, gotPayloadReader.Header) @@ -516,11 +517,15 @@ func TestBlockstoreResumption(t *testing.T) { // Assert index in resumed from file is identical to index generated from the data payload portion of the generated CARv2 file. _, err = v1f.Seek(0, io.SeekStart) require.NoError(t, err) - gotIdx, err := index.ReadFrom(v2r.IndexReader()) + ir, err := v2r.IndexReader() + require.NoError(t, err) + gotIdx, err := index.ReadFrom(ir) require.NoError(t, err) - wantIdx, err := carv2.GenerateIndex(v2r.DataReader()) + dr, err = v2r.DataReader() require.NoError(t, err) - testutil.AssertIdenticalIndexes(t, wantIdx, gotIdx) + wantIdx, err := carv2.GenerateIndex(dr) + require.NoError(t, err) + require.Equal(t, wantIdx, gotIdx) } func TestBlockstoreResumptionIsSupportedOnFinalizedFile(t *testing.T) { @@ -829,29 +834,37 @@ func TestOpenReadWrite_WritesIdentityCIDsWhenOptionIsEnabled(t *testing.T) { t.Cleanup(func() { require.NoError(t, r.Close()) }) require.True(t, r.Header.HasIndex()) - ir := r.IndexReader() + ir, err := r.IndexReader() + require.NoError(t, err) require.NotNil(t, ir) gotIdx, err := index.ReadFrom(ir) require.NoError(t, err) // Determine expected offset as the length of header plus one - header, err := carv1.ReadHeader(r.DataReader(), carv1.DefaultMaxAllowedHeaderSize) + dr, err := r.DataReader() + require.NoError(t, err) + header, err := carv1.ReadHeader(dr, carv1.DefaultMaxAllowedHeaderSize) require.NoError(t, err) object, err := cbor.DumpObject(header) require.NoError(t, err) expectedOffset := len(object) + 1 // Assert index is iterable and has exactly one record with expected multihash and offset. - var count int - err = gotIdx.ForEach(func(mh multihash.Multihash, offset uint64) error { - count++ - require.Equal(t, idmh, mh) - require.Equal(t, uint64(expectedOffset), offset) - return nil - }) - require.NoError(t, err) - require.Equal(t, 1, count) + switch idx := gotIdx.(type) { + case index.IterableIndex: + var i int + err := idx.ForEach(func(mh multihash.Multihash, offset uint64) error { + i++ + require.Equal(t, idmh, mh) + require.Equal(t, uint64(expectedOffset), offset) + return nil + }) + require.NoError(t, err) + require.Equal(t, 1, i) + default: + require.Failf(t, "unexpected index type", "wanted %v but got %v", multicodec.CarMultihashIndexSorted, idx.Codec()) + } } func TestOpenReadWrite_ErrorsWhenWritingTooLargeOfACid(t *testing.T) { @@ -914,7 +927,9 @@ func TestReadWrite_ReWritingCARv1WithIdentityCidIsIdenticalToOriginalWithOptions // Note, we hash instead of comparing bytes to avoid excessive memory usage when sample CARv1 is large. hasher := sha512.New() - gotWritten, err := io.Copy(hasher, v2r.DataReader()) + dr, err := v2r.DataReader() + require.NoError(t, err) + gotWritten, err := io.Copy(hasher, dr) require.NoError(t, err) gotSum := hasher.Sum(nil) diff --git a/ipld/car/v2/example_test.go b/ipld/car/v2/example_test.go index 53dfa3491..57378aeaa 100644 --- a/ipld/car/v2/example_test.go +++ b/ipld/car/v2/example_test.go @@ -51,7 +51,11 @@ func ExampleWrapV1File() { if err != nil { panic(err) } - inner, err := ioutil.ReadAll(cr.DataReader()) + dr, err := cr.DataReader() + if err != nil { + panic(err) + } + inner, err := ioutil.ReadAll(dr) if err != nil { panic(err) } diff --git a/ipld/car/v2/fuzz_test.go b/ipld/car/v2/fuzz_test.go index c74737505..c45d83cb0 100644 --- a/ipld/car/v2/fuzz_test.go +++ b/ipld/car/v2/fuzz_test.go @@ -12,7 +12,6 @@ import ( "testing" car "github.com/ipld/go-car/v2" - "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1" ) @@ -73,48 +72,15 @@ func FuzzReader(f *testing.F) { } subject.Roots() - ir := subject.IndexReader() - if ir != nil { - index.ReadFrom(ir) + _, err = subject.IndexReader() + if err != nil { + return } - car.GenerateIndex(subject.DataReader()) - }) -} - -func FuzzIndex(f *testing.F) { - files, err := filepath.Glob("testdata/*.car") - if err != nil { - f.Fatal(err) - } - for _, fname := range files { - func() { - file, err := os.Open(fname) - if err != nil { - f.Fatal(err) - } - defer file.Close() - subject, err := car.NewReader(file) - if err != nil { - return - } - indexRdr := subject.IndexReader() - if indexRdr == nil { - return - } - _, n, err := index.ReadFromWithSize(indexRdr) - if err != nil { - return - } - data, err := io.ReadAll(io.NewSectionReader(indexRdr, 0, n)) - if err != nil { - f.Fatal(err) - } - f.Add(data) - }() - } - - f.Fuzz(func(t *testing.T, data []byte) { - index.ReadFrom(bytes.NewReader(data)) + dr, err := subject.DataReader() + if err != nil { + return + } + car.GenerateIndex(dr) }) } @@ -137,15 +103,18 @@ func FuzzInspect(f *testing.F) { if err != nil { t.Fatal("second NewReader on same data failed", err.Error()) } - - if i := reader.IndexReader(); i != nil { - _, err = index.ReadFrom(i) - if err != nil { - return - } + i, err := reader.IndexReader() + if err != nil { + return + } + // FIXME: Once indexes are safe to parse, do not skip .car with index in the differential fuzzing. + if i == nil { + return + } + dr, err := reader.DataReader() + if err != nil { + return } - - dr := reader.DataReader() _, err = carv1.ReadHeader(dr, carv1.DefaultMaxAllowedHeaderSize) if err != nil { diff --git a/ipld/car/v2/index/example_test.go b/ipld/car/v2/index/example_test.go index c6f83ea28..d2a9da54b 100644 --- a/ipld/car/v2/index/example_test.go +++ b/ipld/car/v2/index/example_test.go @@ -5,10 +5,10 @@ import ( "io" "io/ioutil" "os" + "reflect" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" - "github.com/multiformats/go-multihash" ) // ExampleReadFrom unmarshalls an index from an indexed CARv2 file, and for each root CID prints the @@ -28,7 +28,11 @@ func ExampleReadFrom() { } // Read and unmarshall index within CARv2 file. - idx, err := index.ReadFrom(cr.IndexReader()) + ir, err := cr.IndexReader() + if err != nil { + panic(err) + } + idx, err := index.ReadFrom(ir) if err != nil { panic(err) } @@ -62,7 +66,11 @@ func ExampleWriteTo() { }() // Read and unmarshall index within CARv2 file. - idx, err := index.ReadFrom(cr.IndexReader()) + ir, err := cr.IndexReader() + if err != nil { + panic(err) + } + idx, err := index.ReadFrom(ir) if err != nil { panic(err) } @@ -94,27 +102,13 @@ func ExampleWriteTo() { panic(err) } - // Expect indices to be equal - collect all of the multihashes and their - // offsets from the first and compare to the second - mha := make(map[string]uint64, 0) - _ = idx.ForEach(func(mh multihash.Multihash, off uint64) error { - mha[mh.HexString()] = off - return nil - }) - var count int - _ = reReadIdx.ForEach(func(mh multihash.Multihash, off uint64) error { - count++ - if expectedOffset, ok := mha[mh.HexString()]; !ok || expectedOffset != off { - panic("expected to get the same index as the CARv2 file") - } - return nil - }) - if count != len(mha) { + // Expect indices to be equal. + if reflect.DeepEqual(idx, reReadIdx) { + fmt.Printf("Saved index file matches the index embedded in CARv2 at %v.\n", src) + } else { panic("expected to get the same index as the CARv2 file") } - fmt.Printf("Saved index file matches the index embedded in CARv2 at %v.\n", src) - // Output: // Saved index file matches the index embedded in CARv2 at ../testdata/sample-wrapped-v2.car. } diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 3a2b3f1d9..911eaa7ae 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -44,17 +44,12 @@ type ( Marshal(w io.Writer) (uint64, error) // Unmarshal decodes the index from its serial form. - // Deprecated: This function is slurpy and will copy everything into memory. + // Note, this function will copy the entire index into memory. + // + // Do not unmarshal index from untrusted CARv2 files. Instead the index should be + // regenerated from the CARv2 data payload. Unmarshal(r io.Reader) error - // UnmarshalLazyRead lazily decodes the index from its serial form. It is a - // safer alternative to to Unmarshal, particularly when reading index data - // from untrusted sources (which is not recommended) but also in more - // constrained memory environments. - // Instead of slurping UnmarshalLazyRead will keep a reference to the the - // io.ReaderAt passed in and ask for data as needed. - UnmarshalLazyRead(r io.ReaderAt) (indexSize int64, err error) - // Load inserts a number of records into the index. // Note that Index will load all given records. Any filtering of the records such as // exclusion of CIDs with multihash.IDENTITY code must occur prior to calling this function. @@ -74,17 +69,17 @@ type ( // meaning that no callbacks happen, // ErrNotFound is returned. GetAll(cid.Cid, func(uint64) bool) error + } + + // IterableIndex is an index which support iterating over it's elements + IterableIndex interface { + Index // ForEach takes a callback function that will be called // on each entry in the index. The arguments to the callback are // the multihash of the element, and the offset in the car file // where the element appears. // - // Note that index with codec multicodec.CarIndexSorted does not support ForEach enumeration. - // Because this index type only contains the multihash digest and not the code. - // Calling ForEach on this index type will result in error. - // Use multicodec.CarMultihashIndexSorted index type instead. - // // If the callback returns a non-nil error, the iteration is aborted, // and the ForEach function returns the error to the user. // @@ -94,12 +89,6 @@ type ( // The order of calls to the given function is deterministic, but entirely index-specific. ForEach(func(multihash.Multihash, uint64) error) error } - - // IterableIndex is an index which support iterating over it's elements - // Deprecated: IterableIndex has been moved into Index. Just use Index now. - IterableIndex interface { - Index - } ) // GetFirst is a wrapper over Index.GetAll, returning the offset for the first @@ -143,32 +132,29 @@ func WriteTo(idx Index, w io.Writer) (uint64, error) { // ReadFrom reads index from r. // The reader decodes the index by reading the first byte to interpret the encoding. // Returns error if the encoding is not known. +// // Attempting to read index data from untrusted sources is not recommended. -func ReadFrom(r io.ReaderAt) (Index, error) { - idx, _, err := ReadFromWithSize(r) - return idx, err -} - -// ReadFromWithSize is just like ReadFrom but return the size of the Index. -// The size is only valid when err != nil. -// Attempting to read index data from untrusted sources is not recommended. -func ReadFromWithSize(r io.ReaderAt) (Index, int64, error) { - code, err := varint.ReadUvarint(internalio.NewOffsetReadSeeker(r, 0)) +// Instead the index should be regenerated from the CARv2 data payload. +func ReadFrom(r io.Reader) (Index, error) { + codec, err := ReadCodec(r) if err != nil { - return nil, 0, err + return nil, err } - codec := multicodec.Code(code) idx, err := New(codec) if err != nil { - return nil, 0, err + return nil, err } - rdr, err := internalio.NewOffsetReadSeekerWithError(r, int64(varint.UvarintSize(code))) - if err != nil { - return nil, 0, err + if err := idx.Unmarshal(r); err != nil { + return nil, err } - n, err := idx.UnmarshalLazyRead(rdr) + return idx, nil +} + +// ReadCodec reads the codec of the index by decoding the first varint read from r. +func ReadCodec(r io.Reader) (multicodec.Code, error) { + code, err := varint.ReadUvarint(internalio.ToByteReader(r)) if err != nil { - return nil, 0, err + return 0, err } - return idx, n, nil + return multicodec.Code(code), nil } diff --git a/ipld/car/v2/index/index_test.go b/ipld/car/v2/index/index_test.go index 0d380cafe..972b4c669 100644 --- a/ipld/car/v2/index/index_test.go +++ b/ipld/car/v2/index/index_test.go @@ -8,7 +8,6 @@ import ( "testing" blocks "github.com/ipfs/go-block-format" - "github.com/ipld/go-car/v2/index/testutil" "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/carv1/util" "github.com/multiformats/go-multicodec" @@ -55,6 +54,9 @@ func TestReadFrom(t *testing.T) { subject, err := ReadFrom(idxf) require.NoError(t, err) + _, err = idxf.Seek(0, io.SeekStart) + require.NoError(t, err) + idxf2, err := os.Open("../testdata/sample-multihash-index-sorted.carindex") require.NoError(t, err) t.Cleanup(func() { require.NoError(t, idxf2.Close()) }) @@ -126,7 +128,7 @@ func TestWriteTo(t *testing.T) { require.NoError(t, err) // Assert they are equal - testutil.AssertIdenticalIndexes(t, wantIdx, gotIdx) + require.Equal(t, wantIdx, gotIdx) } func TestMarshalledIndexStartsWithCodec(t *testing.T) { diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index 60d0e87d3..aeed4c11c 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -5,22 +5,16 @@ import ( "encoding/binary" "errors" "fmt" + internalio "github.com/ipld/go-car/v2/internal/io" "io" "sort" - "github.com/ipld/go-car/v2/internal/errsort" - internalio "github.com/ipld/go-car/v2/internal/io" "github.com/multiformats/go-multicodec" "github.com/ipfs/go-cid" "github.com/multiformats/go-multihash" ) -type sizedReaderAt interface { - io.ReaderAt - Size() int64 -} - var _ Index = (*multiWidthIndex)(nil) type ( @@ -32,7 +26,7 @@ type ( singleWidthIndex struct { width uint32 len uint64 // in struct, len is #items. when marshaled, it's saved as #bytes. - index sizedReaderAt + index []byte } multiWidthIndex map[uint32]singleWidthIndex ) @@ -60,24 +54,27 @@ func (s *singleWidthIndex) Marshal(w io.Writer) (uint64, error) { return 0, err } l += 4 - sz := s.index.Size() - if err := binary.Write(w, binary.LittleEndian, sz); err != nil { + if err := binary.Write(w, binary.LittleEndian, int64(len(s.index))); err != nil { return l, err } l += 8 - n, err := io.Copy(w, io.NewSectionReader(s.index, 0, sz)) + n, err := w.Write(s.index) return l + uint64(n), err } -// Unmarshal decodes the index from its serial form. -// Deprecated: This function is slurpy and will copy the index in memory. func (s *singleWidthIndex) Unmarshal(r io.Reader) error { var width uint32 if err := binary.Read(r, binary.LittleEndian, &width); err != nil { + if err == io.EOF { + return io.ErrUnexpectedEOF + } return err } var dataLen uint64 if err := binary.Read(r, binary.LittleEndian, &dataLen); err != nil { + if err == io.EOF { + return io.ErrUnexpectedEOF + } return err } @@ -89,26 +86,10 @@ func (s *singleWidthIndex) Unmarshal(r io.Reader) error { if _, err := io.ReadFull(r, buf); err != nil { return err } - s.index = bytes.NewReader(buf) + s.index = buf return nil } -func (s *singleWidthIndex) UnmarshalLazyRead(r io.ReaderAt) (indexSize int64, err error) { - var b [12]byte - _, err = internalio.FullReadAt(r, b[:], 0) - if err != nil { - return 0, err - } - - width := binary.LittleEndian.Uint32(b[:4]) - dataLen := binary.LittleEndian.Uint64(b[4:12]) - if err := s.checkUnmarshalLengths(width, dataLen, uint64(len(b))); err != nil { - return 0, err - } - s.index = io.NewSectionReader(r, int64(len(b)), int64(dataLen)) - return int64(dataLen) + int64(len(b)), nil -} - func (s *singleWidthIndex) checkUnmarshalLengths(width uint32, dataLen, extra uint64) error { if width <= 8 { return errors.New("malformed index; width must be bigger than 8") @@ -129,6 +110,10 @@ func (s *singleWidthIndex) checkUnmarshalLengths(width uint32, dataLen, extra ui return nil } +func (s *singleWidthIndex) Less(i int, digest []byte) bool { + return bytes.Compare(digest[:], s.index[i*int(s.width):((i+1)*int(s.width)-8)]) <= 0 +} + func (s *singleWidthIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { d, err := multihash.Decode(c.Hash()) if err != nil { @@ -138,35 +123,18 @@ func (s *singleWidthIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { } func (s *singleWidthIndex) getAll(d []byte, fn func(uint64) bool) error { - digestLen := int64(s.width) - 8 - b := make([]byte, digestLen) - idxI, err := errsort.Search(int(s.len), func(i int) (bool, error) { - digestStart := int64(i) * int64(s.width) - _, err := internalio.FullReadAt(s.index, b, digestStart) - if err != nil { - return false, err - } - return bytes.Compare(d, b) <= 0, nil + idx := sort.Search(int(s.len), func(i int) bool { + return s.Less(i, d) }) - if err != nil { - return err - } - idx := int64(idxI) var any bool for ; uint64(idx) < s.len; idx++ { - digestStart := idx * int64(s.width) - offsetEnd := digestStart + int64(s.width) + digestStart := idx * int(s.width) + offsetEnd := (idx + 1) * int(s.width) digestEnd := offsetEnd - 8 - digestLen := digestEnd - digestStart - b := make([]byte, offsetEnd-digestStart) - _, err := internalio.FullReadAt(s.index, b, digestStart) - if err != nil { - return err - } - if bytes.Equal(d, b[:digestLen]) { + if bytes.Equal(d[:], s.index[digestStart:digestEnd]) { any = true - offset := binary.LittleEndian.Uint64(b[digestLen:]) + offset := binary.LittleEndian.Uint64(s.index[digestEnd:offsetEnd]) if !fn(offset) { // User signalled to stop searching; therefore, break. break @@ -200,19 +168,13 @@ func (s *singleWidthIndex) Load(items []Record) error { } func (s *singleWidthIndex) forEachDigest(f func(digest []byte, offset uint64) error) error { - segmentCount := s.index.Size() / int64(s.width) - for i := int64(0); i < segmentCount; i++ { - digestStart := i * int64(s.width) - offsetEnd := digestStart + int64(s.width) + segmentCount := len(s.index) / int(s.width) + for i := 0; i < segmentCount; i++ { + digestStart := i * int(s.width) + offsetEnd := (i + 1) * int(s.width) digestEnd := offsetEnd - 8 - digestLen := digestEnd - digestStart - b := make([]byte, offsetEnd-digestStart) - _, err := internalio.FullReadAt(s.index, b, digestStart) - if err != nil { - return err - } - digest := b[:digestLen] - offset := binary.LittleEndian.Uint64(b[digestLen:]) + digest := s.index[digestStart:digestEnd] + offset := binary.LittleEndian.Uint64(s.index[digestEnd:offsetEnd]) if err := f(digest, offset); err != nil { return err } @@ -265,49 +227,38 @@ func (m *multiWidthIndex) Marshal(w io.Writer) (uint64, error) { } func (m *multiWidthIndex) Unmarshal(r io.Reader) error { + reader := internalio.ToByteReadSeeker(r) var l int32 - if err := binary.Read(r, binary.LittleEndian, &l); err != nil { - return err - } - for i := 0; i < int(l); i++ { - s := singleWidthIndex{} - if err := s.Unmarshal(r); err != nil { - return err + if err := binary.Read(reader, binary.LittleEndian, &l); err != nil { + if err == io.EOF { + return io.ErrUnexpectedEOF } - (*m)[s.width] = s + return err } - return nil -} - -func (m *multiWidthIndex) UnmarshalLazyRead(r io.ReaderAt) (sum int64, err error) { - var b [4]byte - _, err = internalio.FullReadAt(r, b[:], 0) + sum, err := reader.Seek(0, io.SeekCurrent) if err != nil { - return 0, err + return err } - count := binary.LittleEndian.Uint32(b[:4]) - if int32(count) < 0 { - return 0, errors.New("index too big; multiWidthIndex count is overflowing int32") + if int32(l) < 0 { + return errors.New("index too big; multiWidthIndex count is overflowing int32") } - sum += int64(len(b)) - for ; count > 0; count-- { + for i := 0; i < int(l); i++ { s := singleWidthIndex{} - or, err := internalio.NewOffsetReadSeekerWithError(r, sum) - if err != nil { - return 0, err + if err := s.Unmarshal(r); err != nil { + return err } - n, err := s.UnmarshalLazyRead(or) + n, err := reader.Seek(0, io.SeekCurrent) if err != nil { - return 0, err + return err } oldSum := sum sum += n if sum < oldSum { - return 0, errors.New("index too big; multiWidthIndex len is overflowing int64") + return errors.New("index too big; multiWidthIndex len is overflowing int64") } (*m)[s.width] = s } - return sum, nil + return nil } func (m *multiWidthIndex) Load(items []Record) error { @@ -339,17 +290,13 @@ func (m *multiWidthIndex) Load(items []Record) error { s := singleWidthIndex{ width: uint32(rcrdWdth), len: uint64(len(lst)), - index: bytes.NewReader(compact), + index: compact, } (*m)[uint32(width)+8] = s } return nil } -func (m *multiWidthIndex) ForEach(func(multihash.Multihash, uint64) error) error { - return fmt.Errorf("%s does not support ForEach enumeration; use %s instead", multicodec.CarIndexSorted, multicodec.CarMultihashIndexSorted) -} - func (m *multiWidthIndex) forEachDigest(f func(digest []byte, offset uint64) error) error { sizes := make([]uint32, 0, len(*m)) for k := range *m { diff --git a/ipld/car/v2/index/indexsorted_test.go b/ipld/car/v2/index/indexsorted_test.go index 8e1a45272..5c1ee4495 100644 --- a/ipld/car/v2/index/indexsorted_test.go +++ b/ipld/car/v2/index/indexsorted_test.go @@ -1,27 +1,14 @@ package index import ( - "bytes" "encoding/binary" "testing" "github.com/ipfs/go-merkledag" "github.com/multiformats/go-multicodec" - "github.com/multiformats/go-multihash" "github.com/stretchr/testify/require" ) -func TestSortedIndex_ErrorsOnForEach(t *testing.T) { - subject, err := New(multicodec.CarIndexSorted) - require.NoError(t, err) - err = subject.ForEach(func(multihash.Multihash, uint64) error { return nil }) - require.Error(t, err) - require.Equal(t, - "car-index-sorted does not support ForEach enumeration; use car-multihash-index-sorted instead", - err.Error(), - ) -} - func TestSortedIndexCodec(t *testing.T) { require.Equal(t, multicodec.CarIndexSorted, newSorted().Codec()) } @@ -64,7 +51,7 @@ func TestSingleWidthIndex_GetAll(t *testing.T) { subject := &singleWidthIndex{ width: 9, len: uint64(l), - index: bytes.NewReader(buf), + index: buf, } var foundCount int diff --git a/ipld/car/v2/index/mhindexsorted.go b/ipld/car/v2/index/mhindexsorted.go index 0200f7007..e0ef675de 100644 --- a/ipld/car/v2/index/mhindexsorted.go +++ b/ipld/car/v2/index/mhindexsorted.go @@ -3,17 +3,18 @@ package index import ( "encoding/binary" "errors" + internalio "github.com/ipld/go-car/v2/internal/io" "io" "sort" "github.com/ipfs/go-cid" - internalio "github.com/ipld/go-car/v2/internal/io" "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" ) var ( - _ Index = (*MultihashIndexSorted)(nil) + _ Index = (*MultihashIndexSorted)(nil) + _ IterableIndex = (*MultihashIndexSorted)(nil) ) type ( @@ -42,34 +43,14 @@ func (m *multiWidthCodedIndex) Marshal(w io.Writer) (uint64, error) { func (m *multiWidthCodedIndex) Unmarshal(r io.Reader) error { if err := binary.Read(r, binary.LittleEndian, &m.code); err != nil { + if err == io.EOF { + return io.ErrUnexpectedEOF + } return err } return m.multiWidthIndex.Unmarshal(r) } -func (m *multiWidthCodedIndex) UnmarshalLazyRead(r io.ReaderAt) (int64, error) { - var b [8]byte - _, err := internalio.FullReadAt(r, b[:], 0) - if err != nil { - return 0, err - } - m.code = binary.LittleEndian.Uint64(b[:8]) - rdr, err := internalio.NewOffsetReadSeekerWithError(r, int64(len(b))) - if err != nil { - return 0, err - } - sum, err := m.multiWidthIndex.UnmarshalLazyRead(rdr) - if err != nil { - return 0, err - } - oldSum := sum - sum += int64(len(b)) - if sum < oldSum { - return 0, errors.New("index too big; multiWidthCodedIndex len is overflowing") - } - return sum, nil -} - func (m *multiWidthCodedIndex) forEach(f func(mh multihash.Multihash, offset uint64) error) error { return m.multiWidthIndex.forEachDigest(func(digest []byte, offset uint64) error { mh, err := multihash.Encode(digest, m.code) @@ -117,49 +98,38 @@ func (m *MultihashIndexSorted) sortedMultihashCodes() []uint64 { } func (m *MultihashIndexSorted) Unmarshal(r io.Reader) error { + reader := internalio.ToByteReadSeeker(r) var l int32 - if err := binary.Read(r, binary.LittleEndian, &l); err != nil { - return err - } - for i := 0; i < int(l); i++ { - mwci := newMultiWidthCodedIndex() - if err := mwci.Unmarshal(r); err != nil { - return err + if err := binary.Read(reader, binary.LittleEndian, &l); err != nil { + if err == io.EOF { + return io.ErrUnexpectedEOF } - m.put(mwci) + return err } - return nil -} - -func (m *MultihashIndexSorted) UnmarshalLazyRead(r io.ReaderAt) (sum int64, err error) { - var b [4]byte - _, err = internalio.FullReadAt(r, b[:], 0) + sum, err := reader.Seek(0, io.SeekCurrent) if err != nil { - return 0, err + return err } - sum += int64(len(b)) - count := binary.LittleEndian.Uint32(b[:4]) - if int32(count) < 0 { - return 0, errors.New("index too big; MultihashIndexSorted count is overflowing int32") + if int32(l) < 0 { + return errors.New("index too big; MultihashIndexSorted count is overflowing int32") } - for ; count > 0; count-- { + for i := 0; i < int(l); i++ { mwci := newMultiWidthCodedIndex() - or, err := internalio.NewOffsetReadSeekerWithError(r, sum) - if err != nil { - return 0, err + if err := mwci.Unmarshal(r); err != nil { + return err } - n, err := mwci.UnmarshalLazyRead(or) + n, err := reader.Seek(0, io.SeekCurrent) if err != nil { - return 0, err + return err } oldSum := sum sum += n if sum < oldSum { - return 0, errors.New("index too big; MultihashIndexSorted sum is overflowing int64") + return errors.New("index too big; MultihashIndexSorted len is overflowing int64") } m.put(mwci) } - return sum, nil + return nil } func (m *MultihashIndexSorted) put(mwci *multiWidthCodedIndex) { diff --git a/ipld/car/v2/index/mhindexsorted_test.go b/ipld/car/v2/index/mhindexsorted_test.go index 7704d3a23..520157e44 100644 --- a/ipld/car/v2/index/mhindexsorted_test.go +++ b/ipld/car/v2/index/mhindexsorted_test.go @@ -53,11 +53,14 @@ func TestMultiWidthCodedIndex_StableIterate(t *testing.T) { records = append(records, generateIndexRecords(t, multihash.IDENTITY, rng)...) // Create a new mh sorted index and load randomly generated records into it. - subject, err := index.New(multicodec.CarMultihashIndexSorted) + idx, err := index.New(multicodec.CarMultihashIndexSorted) require.NoError(t, err) - err = subject.Load(records) + err = idx.Load(records) require.NoError(t, err) + subject, ok := idx.(index.IterableIndex) + require.True(t, ok) + mh := make([]multihash.Multihash, 0, len(records)) require.NoError(t, subject.ForEach(func(m multihash.Multihash, _ uint64) error { mh = append(mh, m) diff --git a/ipld/car/v2/index/testutil/equal_index.go b/ipld/car/v2/index/testutil/equal_index.go deleted file mode 100644 index 43d0b3e91..000000000 --- a/ipld/car/v2/index/testutil/equal_index.go +++ /dev/null @@ -1,71 +0,0 @@ -package testutil - -import ( - "sync" - "testing" - - "github.com/multiformats/go-multihash" - "github.com/stretchr/testify/require" -) - -type Index interface { - ForEach(func(multihash.Multihash, uint64) error) error -} - -// insertUint64 perform one round of insertion sort on the last element -func insertUint64(s []uint64) { - switch len(s) { - case 0, 1: - return - default: - cur := s[len(s)-1] - for j := len(s) - 1; j > 0; { - j-- - if cur >= s[j] { - s[j+1] = cur - break - } - s[j+1] = s[j] - } - } -} - -func AssertIdenticalIndexes(t *testing.T, a, b Index) { - var wg sync.WaitGroup - // key is multihash.Multihash.HexString - var aCount uint - var aErr error - aMap := make(map[string][]uint64) - wg.Add(1) - - go func() { - defer wg.Done() - aErr = a.ForEach(func(mh multihash.Multihash, off uint64) error { - aCount++ - str := mh.HexString() - slice := aMap[str] - slice = append(slice, off) - insertUint64(slice) - aMap[str] = slice - return nil - }) - }() - - var bCount uint - bMap := make(map[string][]uint64) - bErr := b.ForEach(func(mh multihash.Multihash, off uint64) error { - bCount++ - str := mh.HexString() - slice := bMap[str] - slice = append(slice, off) - insertUint64(slice) - bMap[str] = slice - return nil - }) - wg.Wait() - require.NoError(t, aErr) - require.NoError(t, bErr) - - require.Equal(t, aCount, bCount) - require.Equal(t, aMap, bMap) -} diff --git a/ipld/car/v2/index_gen.go b/ipld/car/v2/index_gen.go index 33ba7800f..b0b87453e 100644 --- a/ipld/car/v2/index_gen.go +++ b/ipld/car/v2/index_gen.go @@ -209,10 +209,18 @@ func ReadOrGenerateIndex(rs io.ReadSeeker, opts ...Option) (index.Index, error) } // If index is present, then no need to generate; decode and return it. if v2r.Header.HasIndex() { - return index.ReadFrom(v2r.IndexReader()) + ir, err := v2r.IndexReader() + if err != nil { + return nil, err + } + return index.ReadFrom(ir) } // Otherwise, generate index from CARv1 payload wrapped within CARv2 format. - return GenerateIndex(v2r.DataReader(), opts...) + dr, err := v2r.DataReader() + if err != nil { + return nil, err + } + return GenerateIndex(dr, opts...) default: return nil, fmt.Errorf("unknown version %v", version) } diff --git a/ipld/car/v2/index_gen_test.go b/ipld/car/v2/index_gen_test.go index 43a9c2aca..11011e81a 100644 --- a/ipld/car/v2/index_gen_test.go +++ b/ipld/car/v2/index_gen_test.go @@ -9,7 +9,6 @@ import ( "github.com/ipfs/go-cid" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" - "github.com/ipld/go-car/v2/index/testutil" "github.com/ipld/go-car/v2/internal/carv1" internalio "github.com/ipld/go-car/v2/internal/io" "github.com/multiformats/go-multicodec" @@ -48,7 +47,9 @@ func TestGenerateIndex(t *testing.T) { t.Cleanup(func() { assert.NoError(t, v2.Close()) }) reader, err := carv2.NewReader(v2) require.NoError(t, err) - want, err := index.ReadFrom(reader.IndexReader()) + ir, err := reader.IndexReader() + require.NoError(t, err) + want, err := index.ReadFrom(ir) require.NoError(t, err) return want }, @@ -104,7 +105,7 @@ func TestGenerateIndex(t *testing.T) { if want == nil { require.Nil(t, got) } else { - testutil.AssertIdenticalIndexes(t, want, got) + require.Equal(t, want, got) } } } diff --git a/ipld/car/v2/internal/io/fullReaderAt.go b/ipld/car/v2/internal/io/fullReaderAt.go deleted file mode 100644 index 57f266859..000000000 --- a/ipld/car/v2/internal/io/fullReaderAt.go +++ /dev/null @@ -1,20 +0,0 @@ -package io - -import "io" - -func FullReadAt(r io.ReaderAt, b []byte, off int64) (sum int64, err error) { - for int64(len(b)) > sum { - n, err := r.ReadAt(b[sum:], off+sum) - sum += int64(n) - if err != nil { - if err == io.EOF { - if sum < int64(len(b)) { - return sum, io.ErrUnexpectedEOF - } - return sum, nil - } - return sum, err - } - } - return sum, nil -} diff --git a/ipld/car/v2/internal/io/offset_read_seeker.go b/ipld/car/v2/internal/io/offset_read_seeker.go index bbdcf4c63..b3899ab78 100644 --- a/ipld/car/v2/internal/io/offset_read_seeker.go +++ b/ipld/car/v2/internal/io/offset_read_seeker.go @@ -35,15 +35,7 @@ type ReadSeekerAt interface { // NewOffsetReadSeeker returns an ReadSeekerAt that reads from r // starting offset offset off and stops with io.EOF when r reaches its end. // The Seek function will panic if whence io.SeekEnd is passed. -func NewOffsetReadSeeker(r io.ReaderAt, off int64) ReadSeekerAt { - nr, err := NewOffsetReadSeekerWithError(r, off) - if err != nil { - return erroringReader{err} - } - return nr -} - -func NewOffsetReadSeekerWithError(r io.ReaderAt, off int64) (ReadSeekerAt, error) { +func NewOffsetReadSeeker(r io.ReaderAt, off int64) (ReadSeekerAt, error) { if or, ok := r.(*offsetReadSeeker); ok { oldBase := or.base newBase := or.base + off @@ -128,23 +120,3 @@ func (o *offsetReadSeeker) Seek(offset int64, whence int) (int64, error) { func (o *offsetReadSeeker) Position() int64 { return o.off - o.base } - -type erroringReader struct { - err error -} - -func (e erroringReader) Read(_ []byte) (int, error) { - return 0, e.err -} - -func (e erroringReader) ReadAt(_ []byte, n int64) (int, error) { - return 0, e.err -} - -func (e erroringReader) ReadByte() (byte, error) { - return 0, e.err -} - -func (e erroringReader) Seek(_ int64, _ int) (int64, error) { - return 0, e.err -} diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index c34a0672d..4628fd897 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -56,8 +56,10 @@ func NewReader(r io.ReaderAt, opts ...Option) (*Reader, error) { } cr.opts = ApplyOptions(opts...) - or := internalio.NewOffsetReadSeeker(r, 0) - var err error + or, err := internalio.NewOffsetReadSeeker(r, 0) + if err != nil { + return nil, err + } cr.Version, err = ReadVersion(or, opts...) if err != nil { return nil, err @@ -82,7 +84,11 @@ func (r *Reader) Roots() ([]cid.Cid, error) { if r.roots != nil { return r.roots, nil } - header, err := carv1.ReadHeader(r.DataReader(), r.opts.MaxAllowedHeaderSize) + dr, err := r.DataReader() + if err != nil { + return nil, err + } + header, err := carv1.ReadHeader(dr, r.opts.MaxAllowedHeaderSize) if err != nil { return nil, err } @@ -106,9 +112,9 @@ type SectionReader interface { } // DataReader provides a reader containing the data payload in CARv1 format. -func (r *Reader) DataReader() SectionReader { +func (r *Reader) DataReader() (SectionReader, error) { if r.Version == 2 { - return io.NewSectionReader(r.r, int64(r.Header.DataOffset), int64(r.Header.DataSize)) + return io.NewSectionReader(r.r, int64(r.Header.DataOffset), int64(r.Header.DataSize)), nil } return internalio.NewOffsetReadSeeker(r.r, 0) } @@ -116,9 +122,9 @@ func (r *Reader) DataReader() SectionReader { // IndexReader provides an io.Reader containing the index for the data payload if the index is // present. Otherwise, returns nil. // Note, this function will always return nil if the backing payload represents a CARv1. -func (r *Reader) IndexReader() io.ReaderAt { +func (r *Reader) IndexReader() (io.Reader, error) { if r.Version == 1 || !r.Header.HasIndex() { - return nil + return nil, nil } return internalio.NewOffsetReadSeeker(r.r, int64(r.Header.IndexOffset)) } @@ -139,7 +145,6 @@ type Stats struct { MaxBlockLength uint64 MinBlockLength uint64 IndexCodec multicodec.Code - IndexSize uint64 } // Inspect does a quick scan of a CAR, performing basic validation of the format @@ -201,7 +206,10 @@ func (r *Reader) Inspect(validateBlockHash bool) (Stats, error) { var minCidLength uint64 = math.MaxUint64 var minBlockLength uint64 = math.MaxUint64 - dr := r.DataReader() + dr, err := r.DataReader() + if err != nil { + return Stats{}, err + } bdr := internalio.ToByteReader(dr) // read roots, not using Roots(), because we need the offset setup in the data trader @@ -327,14 +335,15 @@ func (r *Reader) Inspect(validateBlockHash bool) (Stats, error) { } if stats.Version != 1 && stats.Header.HasIndex() { - // performs an UnmarshalLazyRead which should have its own validation and - // is intended to be a fast initial scan - ind, size, err := index.ReadFromWithSize(r.IndexReader()) + idxr, err := r.IndexReader() + if err != nil { + return Stats{}, err + } + idx, err := index.ReadFrom(idxr) if err != nil { return Stats{}, err } - stats.IndexCodec = ind.Codec() - stats.IndexSize = uint64(size) + stats.IndexCodec = idx.Codec() } return stats, nil diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go index ed2b78e25..cdeb74d75 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/reader_test.go @@ -11,7 +11,6 @@ import ( "github.com/ipfs/go-cid" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" - "github.com/ipld/go-car/v2/index/testutil" "github.com/ipld/go-car/v2/internal/carv1" "github.com/multiformats/go-multicodec" "github.com/stretchr/testify/require" @@ -141,7 +140,9 @@ func TestReader_WithCarV1Consistency(t *testing.T) { gotRoots, err := subject.Roots() require.NoError(t, err) require.Equal(t, wantReader.Header.Roots, gotRoots) - require.Nil(t, subject.IndexReader()) + ir, err := subject.IndexReader() + require.Nil(t, ir) + require.NoError(t, err) }) } } @@ -173,13 +174,16 @@ func TestReader_WithCarV2Consistency(t *testing.T) { require.NoError(t, err) require.Equal(t, wantReader.Header.Roots, gotRoots) - gotIndexReader := subject.IndexReader() + gotIndexReader, err := subject.IndexReader() + require.NoError(t, err) require.NotNil(t, gotIndexReader) gotIndex, err := index.ReadFrom(gotIndexReader) require.NoError(t, err) - wantIndex, err := carv2.GenerateIndex(subject.DataReader()) + dr, err := subject.DataReader() require.NoError(t, err) - testutil.AssertIdenticalIndexes(t, wantIndex, gotIndex) + wantIndex, err := carv2.GenerateIndex(dr) + require.NoError(t, err) + require.Equal(t, wantIndex, gotIndex) }) } } @@ -187,13 +191,15 @@ func TestReader_WithCarV2Consistency(t *testing.T) { func TestOpenReader_DoesNotPanicForReadersCreatedBeforeClosure(t *testing.T) { subject, err := carv2.OpenReader("testdata/sample-wrapped-v2.car") require.NoError(t, err) - dReaderBeforeClosure := subject.DataReader() - iReaderBeforeClosure := subject.IndexReader() + dReaderBeforeClosure, err := subject.DataReader() + require.NoError(t, err) + iReaderBeforeClosure, err := subject.IndexReader() + require.NoError(t, err) require.NoError(t, subject.Close()) buf := make([]byte, 1) - panicTest := func(r io.ReaderAt) { - _, err := r.ReadAt(buf, 0) + panicTest := func(r io.Reader) { + _, err := r.Read(buf) require.EqualError(t, err, "mmap: closed") } @@ -205,12 +211,14 @@ func TestOpenReader_DoesNotPanicForReadersCreatedAfterClosure(t *testing.T) { subject, err := carv2.OpenReader("testdata/sample-wrapped-v2.car") require.NoError(t, err) require.NoError(t, subject.Close()) - dReaderAfterClosure := subject.DataReader() - iReaderAfterClosure := subject.IndexReader() + dReaderAfterClosure, err := subject.DataReader() + require.NoError(t, err) + iReaderAfterClosure, err := subject.IndexReader() + require.NoError(t, err) buf := make([]byte, 1) - panicTest := func(r io.ReaderAt) { - _, err := r.ReadAt(buf, 0) + panicTest := func(r io.Reader) { + _, err := r.Read(buf) require.EqualError(t, err, "mmap: closed") } @@ -237,7 +245,9 @@ func TestReader_ReturnsNilWhenThereIsNoIndex(t *testing.T) { subject, err := carv2.OpenReader(tt.path) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, subject.Close()) }) - require.Nil(t, subject.IndexReader()) + ir, err := subject.IndexReader() + require.NoError(t, err) + require.Nil(t, ir) }) } } @@ -365,7 +375,6 @@ func TestInspect(t *testing.T) { MaxBlockLength: 9, MinBlockLength: 4, IndexCodec: multicodec.CarMultihashIndexSorted, - IndexSize: 148, }, }, // same as CarV1 but with a zero-byte EOF to test options diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index 1bf3ca3d9..12dcd6a9c 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/ipld/go-car/v2/index" - "github.com/ipld/go-car/v2/index/testutil" "github.com/ipld/go-car/v2/internal/carv1" "github.com/stretchr/testify/require" @@ -48,16 +47,20 @@ func TestWrapV1(t *testing.T) { require.NoError(t, err) wantPayload, err := ioutil.ReadAll(sf) require.NoError(t, err) - gotPayload, err := ioutil.ReadAll(subject.DataReader()) + dr, err := subject.DataReader() + require.NoError(t, err) + gotPayload, err := ioutil.ReadAll(dr) require.NoError(t, err) require.Equal(t, wantPayload, gotPayload) // Assert embedded index in CARv2 is same as index generated from the original CARv1. wantIdx, err := GenerateIndexFromFile(src) require.NoError(t, err) - gotIdx, err := index.ReadFrom(subject.IndexReader()) + ir, err := subject.IndexReader() + require.NoError(t, err) + gotIdx, err := index.ReadFrom(ir) require.NoError(t, err) - testutil.AssertIdenticalIndexes(t, wantIdx, gotIdx) + require.Equal(t, wantIdx, gotIdx) } func TestExtractV1(t *testing.T) { From da2c8b780df421260dd7b2523428d360b3b8b67e Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 6 Jul 2022 09:32:31 +0100 Subject: [PATCH 3694/3817] Revert changes to `insertionindex` Revert changes to serialization of `insertionindex` postponed until the streaming index work stream. This commit was moved from ipld/go-car@fb7948544918fbbb5bbd1a9461ce1fbbfde2e597 --- ipld/car/v2/blockstore/insertionindex.go | 42 +++++++++++++++++++----- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/ipld/car/v2/blockstore/insertionindex.go b/ipld/car/v2/blockstore/insertionindex.go index 192eb5c34..1e480b3f9 100644 --- a/ipld/car/v2/blockstore/insertionindex.go +++ b/ipld/car/v2/blockstore/insertionindex.go @@ -2,6 +2,7 @@ package blockstore import ( "bytes" + "encoding/binary" "errors" "fmt" "io" @@ -11,6 +12,7 @@ import ( "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" "github.com/petar/GoLLRB/llrb" + cbor "github.com/whyrusleeping/cbor/go" ) // This index is intended to be efficient for random-access, in-memory lookups @@ -106,7 +108,37 @@ func (ii *insertionIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { } func (ii *insertionIndex) Marshal(w io.Writer) (uint64, error) { - return 0, fmt.Errorf("unimplemented, index type not intended for serialization") + l := uint64(0) + if err := binary.Write(w, binary.LittleEndian, int64(ii.items.Len())); err != nil { + return l, err + } + l += 8 + + var err error + iter := func(i llrb.Item) bool { + if err = cbor.Encode(w, i.(recordDigest).Record); err != nil { + return false + } + return true + } + ii.items.AscendGreaterOrEqual(ii.items.Min(), iter) + return l, err +} + +func (ii *insertionIndex) Unmarshal(r io.Reader) error { + var length int64 + if err := binary.Read(r, binary.LittleEndian, &length); err != nil { + return err + } + d := cbor.NewDecoder(r) + for i := int64(0); i < length; i++ { + var rec index.Record + if err := d.Decode(&rec); err != nil { + return err + } + ii.items.InsertNoReplace(newRecordDigest(rec)) + } + return nil } func (ii *insertionIndex) ForEach(f func(multihash.Multihash, uint64) error) error { @@ -123,14 +155,6 @@ func (ii *insertionIndex) ForEach(f func(multihash.Multihash, uint64) error) err return errr } -func (ii *insertionIndex) Unmarshal(r io.Reader) error { - return fmt.Errorf("unimplemented, index type not intended for deserialization") -} - -func (ii *insertionIndex) UnmarshalLazyRead(r io.ReaderAt) (int64, error) { - return 0, fmt.Errorf("unimplemented, index type not intended for deserialization") -} - func (ii *insertionIndex) Codec() multicodec.Code { return insertionIndexCodec } From 7503800fb67efe91633c075454d1799f754da5d1 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Wed, 6 Jul 2022 10:42:52 +0200 Subject: [PATCH 3695/3817] ci: remove the reverted FuzzIndex fuzzer This commit was moved from ipld/go-car@6d5bd66ccd7a960bc93ac27de65985da9849948a --- ipld/car/.github/workflows/go-fuzz.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/car/.github/workflows/go-fuzz.yml b/ipld/car/.github/workflows/go-fuzz.yml index cb7b4b272..830fc9ec2 100644 --- a/ipld/car/.github/workflows/go-fuzz.yml +++ b/ipld/car/.github/workflows/go-fuzz.yml @@ -26,7 +26,7 @@ jobs: strategy: fail-fast: true matrix: - target: [ "BlockReader", "Reader", "Index", "Inspect" ] + target: [ "BlockReader", "Reader", "Inspect" ] runs-on: ubuntu-latest name: Fuzz V2 ${{ matrix.target }} steps: From bbcd79c8fa2a3f2e31b773d79f9c59fa6857672c Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 6 Jul 2022 16:27:40 +0100 Subject: [PATCH 3696/3817] Empty identity CID should be indexed when options are set There is an edge-case where if the storing identity CIDs are enabled in a CARv2, one could technically store an empty identity CID which ends up with a `singlewidthindex` width of 8. Such CAR files indeed exist out there and the validation changes introduced in `2.4.0` means such CAR file indices are no longer readable , even if regenerated. The question is should this be considered a valid index/readable index? This commit was moved from ipld/go-car@b2e14f01b32855928ef6fd1b8cd5148622e3662d --- ipld/car/v2/blockstore/readwrite_test.go | 50 ++++++++++++++++++++++++ ipld/car/v2/index/indexsorted.go | 4 +- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 11cc99a22..22525e789 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "math/rand" "os" + "path" "path/filepath" "sync" "testing" @@ -943,3 +944,52 @@ func TestReadWrite_ReWritingCARv1WithIdentityCidIsIdenticalToOriginalWithOptions require.Equal(t, wantWritten, gotWritten) require.Equal(t, wantSum, gotSum) } + +func TestBlockstore_IdentityCidWithEmptyDataIsIndexed(t *testing.T) { + p := path.Join(t.TempDir(), "car-id-cid-empty.carv2") + var noData []byte + + mh, err := multihash.Sum(noData, multihash.IDENTITY, -1) + require.NoError(t, err) + w, err := blockstore.OpenReadWrite(p, nil, carv2.StoreIdentityCIDs(true)) + require.NoError(t, err) + + blk, err := blocks.NewBlockWithCid(noData, cid.NewCidV1(cid.Raw, mh)) + require.NoError(t, err) + + err = w.Put(context.TODO(), blk) + require.NoError(t, err) + require.NoError(t, w.Finalize()) + + r, err := carv2.OpenReader(p) + require.NoError(t, err) + defer func() { require.NoError(t, r.Close()) }() + + dr, err := r.DataReader() + require.NoError(t, err) + header, err := carv1.ReadHeader(dr, carv1.DefaultMaxAllowedHeaderSize) + require.NoError(t, err) + wantOffset, err := carv1.HeaderSize(header) + require.NoError(t, err) + + ir, err := r.IndexReader() + require.NoError(t, err) + idx, err := index.ReadFrom(ir) + require.NoError(t, err) + + itidx, ok := idx.(index.IterableIndex) + require.True(t, ok) + var count int + err = itidx.ForEach(func(m multihash.Multihash, u uint64) error { + dm, err := multihash.Decode(m) + require.NoError(t, err) + require.Equal(t, multicodec.Identity, multicodec.Code(dm.Code)) + require.Equal(t, 0, dm.Length) + require.Empty(t, dm.Digest) + require.Equal(t, wantOffset, u) + count++ + return nil + }) + require.NoError(t, err) + require.Equal(t, 1, count) +} diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index aeed4c11c..ed94ed8f7 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -91,8 +91,8 @@ func (s *singleWidthIndex) Unmarshal(r io.Reader) error { } func (s *singleWidthIndex) checkUnmarshalLengths(width uint32, dataLen, extra uint64) error { - if width <= 8 { - return errors.New("malformed index; width must be bigger than 8") + if width < 8 { + return errors.New("malformed index; width must be at least 8") } const maxWidth = 32 << 20 // 32MiB, to ~match the go-cid maximum if width > maxWidth { From 5494ea899a8a99ffe21fa0791af1abc4786b374e Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 7 Jul 2022 11:44:47 +0100 Subject: [PATCH 3697/3817] Upgrade to the latest `go-car/v2` Upgrade to the latest `go-car/v2` and reflect changes in tests. This commit was moved from ipld/go-car@771fb74175cdd1d25a45ca2adcdbc35e65cabcfb --- ipld/car/cmd/car/detach.go | 6 +++++- ipld/car/cmd/car/get.go | 5 +++-- ipld/car/cmd/car/index.go | 11 +++++++++-- ipld/car/cmd/car/testdata/script/get-block.txt | 2 +- ipld/car/cmd/car/verify.go | 6 +++++- 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/ipld/car/cmd/car/detach.go b/ipld/car/cmd/car/detach.go index da0c236b5..e04eba9dd 100644 --- a/ipld/car/cmd/car/detach.go +++ b/ipld/car/cmd/car/detach.go @@ -32,7 +32,11 @@ func DetachCar(c *cli.Context) error { } defer outStream.Close() - _, err = io.Copy(outStream, r.IndexReader()) + ir, err := r.IndexReader() + if err != nil { + return err + } + _, err = io.Copy(outStream, ir) return err } diff --git a/ipld/car/cmd/car/get.go b/ipld/car/cmd/car/get.go index 84531b7a6..f5d5b1cdf 100644 --- a/ipld/car/cmd/car/get.go +++ b/ipld/car/cmd/car/get.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "io" "os" @@ -16,7 +17,7 @@ import ( _ "github.com/ipld/go-ipld-prime/codec/raw" "github.com/ipfs/go-cid" - ipfsbs "github.com/ipfs/go-ipfs-blockstore" + ipldfmt "github.com/ipfs/go-ipld-format" "github.com/ipfs/go-unixfsnode" "github.com/ipld/go-car" "github.com/ipld/go-car/v2/blockstore" @@ -135,7 +136,7 @@ func writeCarV2(ctx context.Context, rootCid cid.Cid, output string, bs *blockst if cl, ok := l.(cidlink.Link); ok { blk, err := bs.Get(ctx, cl.Cid) if err != nil { - if err == ipfsbs.ErrNotFound { + if ipldfmt.IsNotFound(err) { if strict { return nil, err } diff --git a/ipld/car/cmd/car/index.go b/ipld/car/cmd/car/index.go index 8fd2f7e87..031df2ada 100644 --- a/ipld/car/cmd/car/index.go +++ b/ipld/car/cmd/car/index.go @@ -36,7 +36,11 @@ func IndexCar(c *cli.Context) error { } defer outStream.Close() - _, err := io.Copy(outStream, r.DataReader()) + dr, err := r.DataReader() + if err != nil { + return err + } + _, err = io.Copy(outStream, dr) return err } @@ -65,7 +69,10 @@ func IndexCar(c *cli.Context) error { } defer outStream.Close() - v1r := r.DataReader() + v1r, err := r.DataReader() + if err != nil { + return err + } if r.Version == 1 { fi, err := os.Stat(c.Args().Get(0)) diff --git a/ipld/car/cmd/car/testdata/script/get-block.txt b/ipld/car/cmd/car/testdata/script/get-block.txt index 1e5fd7c27..3f22cef1f 100644 --- a/ipld/car/cmd/car/testdata/script/get-block.txt +++ b/ipld/car/cmd/car/testdata/script/get-block.txt @@ -16,4 +16,4 @@ cmp stdout ${INPUTS}/${SAMPLE_CID}.block # "get-block" on a missing CID. ! car get-block ${INPUTS}/sample-v1.car ${MISSING_CID} -stderr 'block not found' +stderr 'bafy2bzacebohz654namrgmwjjx4qmtwgxixsd7pn4tlanyrc3g3hwj75xxxxw not found' diff --git a/ipld/car/cmd/car/verify.go b/ipld/car/cmd/car/verify.go index 3a43de938..faee3aa36 100644 --- a/ipld/car/cmd/car/verify.go +++ b/ipld/car/cmd/car/verify.go @@ -86,7 +86,11 @@ func VerifyCar(c *cli.Context) error { // index if rx.Version == 2 && rx.Header.HasIndex() { - idx, err := index.ReadFrom(rx.IndexReader()) + ir, err := rx.IndexReader() + if err != nil { + return err + } + idx, err := index.ReadFrom(ir) if err != nil { return err } From 9abb6939f634a72024c77e12953015d69d67f1a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Wed, 13 Jul 2022 15:55:37 +0200 Subject: [PATCH 3698/3817] Rename HasBlock to NotifyNewBlocks, and make it accept multiple blocks The goal here is twofold: - allows for batched handling of multiple blocks at once - act as a noisy signal for the breaking change that Bitswap is not adding blocks in the blockstore itself anymore (see https://github.com/ipfs/go-bitswap/pull/571) This commit was moved from ipfs/go-ipfs-exchange-interface@1786a28c67d2a28f623e87c95d78f5ae054169e1 --- exchange/interface.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index 7640c5733..0d98750b5 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -13,9 +13,8 @@ import ( type Interface interface { // type Exchanger interface Fetcher - // TODO Should callers be concerned with whether the block was made - // available on the network? - HasBlock(context.Context, blocks.Block) error + // NotifyNewBlocks tells the exchange that new blocks are available and can be served. + NotifyNewBlocks(ctx context.Context, blocks ...blocks.Block) error IsOnline() bool From 1b9178f9538b24947ab602e20ee143aa4dc2236c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Wed, 13 Jul 2022 16:28:04 +0200 Subject: [PATCH 3699/3817] remove IsOnline() as it's not used anywhere This commit was moved from ipfs/go-ipfs-exchange-interface@1181846dc171626f47c736bf66f031264fb755e1 --- exchange/interface.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/exchange/interface.go b/exchange/interface.go index 0d98750b5..2c04628e3 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -16,8 +16,6 @@ type Interface interface { // type Exchanger interface // NotifyNewBlocks tells the exchange that new blocks are available and can be served. NotifyNewBlocks(ctx context.Context, blocks ...blocks.Block) error - IsOnline() bool - io.Closer } From 8af2916814bf67f78a775b37b6dcd9f433ca0a7b Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 12 Jul 2022 15:34:13 +0000 Subject: [PATCH 3700/3817] Only read index codec during inspection Instead of fully reading the index, only peak the index codec until streaming index read is implemented. This would offer a safer `Inspect()` call but with the tradeoff that overflow in index size is no longer detected. Hence the tests that are now removed. This commit was moved from ipld/go-car@903a2b0ee800a1921cc9b07f4fea6008a9ba4ce4 --- ipld/car/v2/reader.go | 3 +-- ipld/car/v2/reader_test.go | 24 ------------------------ 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 4628fd897..f29469a02 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -339,11 +339,10 @@ func (r *Reader) Inspect(validateBlockHash bool) (Stats, error) { if err != nil { return Stats{}, err } - idx, err := index.ReadFrom(idxr) + stats.IndexCodec, err = index.ReadCodec(idxr) if err != nil { return Stats{}, err } - stats.IndexCodec = idx.Codec() } return stats, nil diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go index cdeb74d75..5eaff8d03 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/reader_test.go @@ -503,30 +503,6 @@ func TestInspectError(t *testing.T) { // the bad index tests are manually constructed from this single-block CARv2 by adjusting the Uint32 and Uint64 values in the index: // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset // 0aa16776657273696f6e02 00000000000000000000000000000000330000000000000041000000000000007400000000000000 11a265726f6f7473806776657273696f6e012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000 8108 01000000 1200000000000000 01000000 28000000 2800000000000000 01d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca 1200000000000000 - { - name: "BadIndexCountOverflow", - // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset - carHex: "0aa16776657273696f6e02 00000000000000000000000000000000330000000000000041000000000000007400000000000000 11a265726f6f7473806776657273696f6e012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000 8108 ffffffff 1200000000000000 01000000 28000000 2800000000000000 01d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca 1200000000000000", - expectedInspectError: "index too big; MultihashIndexSorted count is overflowing int32", - }, - { - name: "BadIndexCountTooMany", - // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset - carHex: "0aa16776657273696f6e02 00000000000000000000000000000000330000000000000041000000000000007400000000000000 11a265726f6f7473806776657273696f6e012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000 8108 ffffff7f 1200000000000000 01000000 28000000 2800000000000000 01d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca 1200000000000000", - expectedInspectError: "unexpected EOF", - }, - { - name: "BadIndexMultiWidthOverflow", - // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset - carHex: "0aa16776657273696f6e02 00000000000000000000000000000000330000000000000041000000000000007400000000000000 11a265726f6f7473806776657273696f6e012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000 8108 01000000 1200000000000000 ffffffff 28000000 2800000000000000 01d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca 1200000000000000", - expectedInspectError: "index too big; multiWidthIndex count is overflowing int32", - }, - { - name: "BadIndexMultiWidthTooMany", - // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset - carHex: "0aa16776657273696f6e02 00000000000000000000000000000000330000000000000041000000000000007400000000000000 11a265726f6f7473806776657273696f6e012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000 8108 01000000 1200000000000000 ffffff7f 28000000 2800000000000000 01d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca 1200000000000000", - expectedInspectError: "unexpected EOF", - }, // we don't test any further into the index, to do that, a user should do a ForEach across the loaded index (and sanity check the offsets) } From f439298055dbe6717ef5f7912eb8bbd124f860ea Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 14 Jul 2022 10:01:25 +0000 Subject: [PATCH 3701/3817] Separate `index.ReadFrom` tests This is to keep the tests in the repo, and have them run against `index.ReadFrom` without removing them completely now that inspection does not fully read the index. This commit was moved from ipld/go-car@e0b4de3e2593673b44fe003fd918a5a9c7bfa356 --- ipld/car/v2/reader_test.go | 54 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/ipld/car/v2/reader_test.go b/ipld/car/v2/reader_test.go index 5eaff8d03..b398a870a 100644 --- a/ipld/car/v2/reader_test.go +++ b/ipld/car/v2/reader_test.go @@ -529,6 +529,60 @@ func TestInspectError(t *testing.T) { } } +func TestIndex_ReadFromCorruptIndex(t *testing.T) { + tests := []struct { + name string + givenCarHex string + wantErr string + }{ + { + name: "BadIndexCountOverflow", + // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset + givenCarHex: "0aa16776657273696f6e02 00000000000000000000000000000000330000000000000041000000000000007400000000000000 11a265726f6f7473806776657273696f6e012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000 8108 ffffffff 1200000000000000 01000000 28000000 2800000000000000 01d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca 1200000000000000", + wantErr: "index too big; MultihashIndexSorted count is overflowing int32", + }, + { + name: "BadIndexCountTooMany", + // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset + givenCarHex: "0aa16776657273696f6e02 00000000000000000000000000000000330000000000000041000000000000007400000000000000 11a265726f6f7473806776657273696f6e012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000 8108 ffffff7f 1200000000000000 01000000 28000000 2800000000000000 01d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca 1200000000000000", + wantErr: "unexpected EOF", + }, + { + name: "BadIndexMultiWidthOverflow", + // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset + givenCarHex: "0aa16776657273696f6e02 00000000000000000000000000000000330000000000000041000000000000007400000000000000 11a265726f6f7473806776657273696f6e012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000 8108 01000000 1200000000000000 ffffffff 28000000 2800000000000000 01d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca 1200000000000000", + wantErr: "index too big; multiWidthIndex count is overflowing int32", + }, + { + name: "BadIndexMultiWidthTooMany", + // pragma carv2 header carv1 icodec count codec count (swi) width dataLen mh offset + givenCarHex: "0aa16776657273696f6e02 00000000000000000000000000000000330000000000000041000000000000007400000000000000 11a265726f6f7473806776657273696f6e012e0155122001d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca00000000000000000000 8108 01000000 1200000000000000 ffffff7f 28000000 2800000000000000 01d448afd928065458cf670b60f5a594d735af0172c8d67f22a81680132681ca 1200000000000000", + wantErr: "unexpected EOF", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + car, _ := hex.DecodeString(strings.ReplaceAll(test.givenCarHex, " ", "")) + reader, err := carv2.NewReader(bytes.NewReader(car)) + require.NoError(t, err) + + ir, err := reader.IndexReader() + require.NoError(t, err) + require.NotNil(t, ir) + + gotIdx, err := index.ReadFrom(ir) + if test.wantErr == "" { + require.NoError(t, err) + require.NotNil(t, gotIdx) + } else { + require.Error(t, err) + require.Equal(t, test.wantErr, err.Error()) + require.Nil(t, gotIdx) + } + }) + } +} + func mustCidDecode(s string) cid.Cid { c, err := cid.Decode(s) if err != nil { From 179030bc59b29fc16e1e4dbf6aa536873692d521 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 15 Jul 2022 11:04:48 +1000 Subject: [PATCH 3702/3817] feat: add `car inspect` command to cmd pkg (#320) This commit was moved from ipld/go-car@3264624f19239211abfa12bf91471ceb0cfc9afc --- ipld/car/cmd/car/car.go | 12 ++ ipld/car/cmd/car/inspect.go | 127 ++++++++++++++++++ .../car/testdata/inputs/badheaderlength.car | 1 + .../car/testdata/inputs/badsectionlength.car | Bin 0 -> 70 bytes .../car/cmd/car/testdata/script/get-block.txt | 2 +- ipld/car/cmd/car/testdata/script/inspect.txt | 43 ++++++ 6 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 ipld/car/cmd/car/inspect.go create mode 100644 ipld/car/cmd/car/testdata/inputs/badheaderlength.car create mode 100644 ipld/car/cmd/car/testdata/inputs/badsectionlength.car create mode 100644 ipld/car/cmd/car/testdata/script/inspect.txt diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index 90cf9425d..9957c8a54 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -128,6 +128,18 @@ func main1() int { }, }, }, + { + Name: "inspect", + Usage: "verifies a car and prints a basic report about its contents", + Action: InspectCar, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "full", + Value: false, + Usage: "Check that the block data hash digests match the CIDs", + }, + }, + }, { Name: "list", Aliases: []string{"l", "ls"}, diff --git a/ipld/car/cmd/car/inspect.go b/ipld/car/cmd/car/inspect.go new file mode 100644 index 000000000..76bb41ce9 --- /dev/null +++ b/ipld/car/cmd/car/inspect.go @@ -0,0 +1,127 @@ +package main + +import ( + "bytes" + "fmt" + "os" + "sort" + "strings" + + carv2 "github.com/ipld/go-car/v2" + "github.com/multiformats/go-multicodec" + "github.com/urfave/cli/v2" +) + +// InspectCar verifies a CAR and prints a basic report about its contents +func InspectCar(c *cli.Context) (err error) { + inStream := os.Stdin + if c.Args().Len() >= 1 { + inStream, err = os.Open(c.Args().First()) + if err != nil { + return err + } + } + + rd, err := carv2.NewReader(inStream) + if err != nil { + return err + } + stats, err := rd.Inspect(c.IsSet("full")) + if err != nil { + return err + } + + var v2s string + if stats.Version == 2 { + idx := "(none)" + if stats.IndexCodec != 0 { + idx = stats.IndexCodec.String() + } + var buf bytes.Buffer + stats.Header.Characteristics.WriteTo(&buf) + v2s = fmt.Sprintf(`Characteristics: %x +Data offset: %d +Data (payload) length: %d +Index offset: %d +Index type: %s +`, buf.Bytes(), stats.Header.DataOffset, stats.Header.DataSize, stats.Header.IndexOffset, idx) + } + + var roots strings.Builder + switch len(stats.Roots) { + case 0: + roots.WriteString(" (none)") + case 1: + roots.WriteString(" ") + roots.WriteString(stats.Roots[0].String()) + default: + for _, r := range stats.Roots { + roots.WriteString("\n\t") + roots.WriteString(r.String()) + } + } + + var codecs strings.Builder + { + keys := make([]int, len(stats.CodecCounts)) + i := 0 + for codec := range stats.CodecCounts { + keys[i] = int(codec) + i++ + } + sort.Ints(keys) + for _, code := range keys { + codec := multicodec.Code(code) + codecs.WriteString(fmt.Sprintf("\n\t%s: %d", codec, stats.CodecCounts[codec])) + } + } + + var hashers strings.Builder + { + keys := make([]int, len(stats.MhTypeCounts)) + i := 0 + for codec := range stats.MhTypeCounts { + keys[i] = int(codec) + i++ + } + sort.Ints(keys) + for _, code := range keys { + codec := multicodec.Code(code) + hashers.WriteString(fmt.Sprintf("\n\t%s: %d", codec, stats.MhTypeCounts[codec])) + } + } + + rp := "No" + if stats.RootsPresent { + rp = "Yes" + } + + pfmt := `Version: %d +%sRoots:%s +Root blocks present in data: %s +Block count: %d +Min / average / max block length (bytes): %d / %d / %d +Min / average / max CID length (bytes): %d / %d / %d +Block count per codec:%s +CID count per multihash:%s +` + + fmt.Printf( + pfmt, + stats.Version, + v2s, + roots.String(), + rp, + stats.BlockCount, + stats.MinBlockLength, + stats.AvgBlockLength, + stats.MaxBlockLength, + stats.MinCidLength, + stats.AvgCidLength, + stats.MaxCidLength, + codecs.String(), + hashers.String(), + ) + + return nil +} diff --git a/ipld/car/cmd/car/testdata/inputs/badheaderlength.car b/ipld/car/cmd/car/testdata/inputs/badheaderlength.car new file mode 100644 index 000000000..4f6aa5487 --- /dev/null +++ b/ipld/car/cmd/car/testdata/inputs/badheaderlength.car @@ -0,0 +1 @@ +àààৠolLÊ”<#oKg#H–í´*‹¨ gversion \ No newline at end of file diff --git a/ipld/car/cmd/car/testdata/inputs/badsectionlength.car b/ipld/car/cmd/car/testdata/inputs/badsectionlength.car new file mode 100644 index 0000000000000000000000000000000000000000..8569fb18d01a75ade5ba6885832c924ab1310f43 GIT binary patch literal 70 zcmWe!lv Date: Fri, 8 Jul 2022 18:53:22 +0200 Subject: [PATCH 3703/3817] write blocks retrieved from the exchange to the blockstore This follows the change in bitswap where that responsibility was removed. This commit was moved from ipfs/go-blockservice@4a60416c7b617009b95b917e1ef6b9a4ed1bb086 --- blockservice/blockservice.go | 60 ++++++++++++++++++++------ blockservice/blockservice_test.go | 71 ++++++++++++++++++++++++++++--- 2 files changed, 113 insertions(+), 18 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 007d1131c..134de222b 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -12,13 +12,14 @@ import ( "go.opentelemetry.io/otel/trace" blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-blockservice/internal" cid "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" exchange "github.com/ipfs/go-ipfs-exchange-interface" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log/v2" "github.com/ipfs/go-verifcid" + + "github.com/ipfs/go-blockservice/internal" ) var logger = logging.Logger("blockservice") @@ -84,7 +85,7 @@ func New(bs blockstore.Blockstore, rem exchange.Interface) BlockService { } } -// NewWriteThrough ceates a BlockService that guarantees writes will go +// NewWriteThrough creates a BlockService that guarantees writes will go // through to the blockstore and are not skipped by cache checks. func NewWriteThrough(bs blockstore.Blockstore, rem exchange.Interface) BlockService { if rem == nil { @@ -131,7 +132,6 @@ func NewSession(ctx context.Context, bs BlockService) *Session { } // AddBlock adds a particular block to the service, Putting it into the datastore. -// TODO pass a context into this if the remote.HasBlock is going to remain here. func (s *blockService) AddBlock(ctx context.Context, o blocks.Block) error { ctx, span := internal.StartSpan(ctx, "blockService.AddBlock") defer span.End() @@ -155,8 +155,8 @@ func (s *blockService) AddBlock(ctx context.Context, o blocks.Block) error { logger.Debugf("BlockService.BlockAdded %s", c) if s.exchange != nil { - if err := s.exchange.HasBlock(ctx, o); err != nil { - logger.Errorf("HasBlock: %s", err.Error()) + if err := s.exchange.NotifyNewBlocks(ctx, o); err != nil { + logger.Errorf("NotifyNewBlocks: %s", err.Error()) } } @@ -200,11 +200,9 @@ func (s *blockService) AddBlocks(ctx context.Context, bs []blocks.Block) error { } if s.exchange != nil { - for _, o := range toput { - logger.Debugf("BlockService.BlockAdded %s", o.Cid()) - if err := s.exchange.HasBlock(ctx, o); err != nil { - logger.Errorf("HasBlock: %s", err.Error()) - } + logger.Debugf("BlockService.BlockAdded %d blocks", len(toput)) + if err := s.exchange.NotifyNewBlocks(ctx, toput...); err != nil { + logger.Errorf("NotifyNewBlocks: %s", err.Error()) } } return nil @@ -249,6 +247,11 @@ func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget fun if err != nil { return nil, err } + // also write in the blockstore for caching + err = bs.Put(ctx, blk) + if err != nil { + return nil, err + } logger.Debugf("BlockService.BlockFetched %s", c) return blk, nil } @@ -325,12 +328,43 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget } for b := range rblocks { + // batch available blocks together + batch := make([]blocks.Block, 0, 8) + batch = append(batch, b) logger.Debugf("BlockService.BlockFetched %s", b.Cid()) - select { - case out <- b: - case <-ctx.Done(): + + batchLoop: + for { + select { + case moreBlock, ok := <-rblocks: + if !ok { + // rblock has been closed, we set it to nil to avoid pulling zero values + rblocks = nil + } else { + logger.Debugf("BlockService.BlockFetched %s", moreBlock.Cid()) + batch = append(batch, moreBlock) + } + case <-ctx.Done(): + return + default: + break batchLoop + } + } + + // also write in the blockstore for caching + err = bs.PutMany(ctx, batch) + if err != nil { + logger.Errorf("could not write blocks from the network to the blockstore: %s", err) return } + + for _, b = range batch { + select { + case out <- b: + case <-ctx.Done(): + return + } + } } }() return out diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index c29f0cfc5..5753d3a9d 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -5,6 +5,7 @@ import ( "testing" blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" blockstore "github.com/ipfs/go-ipfs-blockstore" @@ -19,8 +20,8 @@ func TestWriteThroughWorks(t *testing.T) { blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())), 0, } - bstore2 := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) - exch := offline.Exchange(bstore2) + exchbstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) + exch := offline.Exchange(exchbstore) bserv := NewWriteThrough(bstore, exch) bgen := butil.NewBlockGenerator() @@ -44,6 +45,57 @@ func TestWriteThroughWorks(t *testing.T) { } } +func TestExchangeWrite(t *testing.T) { + bstore := &PutCountingBlockstore{ + blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())), + 0, + } + exchbstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) + exch := offline.Exchange(exchbstore) + bserv := NewWriteThrough(bstore, exch) + bgen := butil.NewBlockGenerator() + + // GetBlock + block := bgen.Next() + err := exchbstore.Put(context.Background(), block) + if err != nil { + t.Fatal(err) + } + got, err := bserv.GetBlock(context.Background(), block.Cid()) + if err != nil { + t.Fatal(err) + } + if got.Cid() != block.Cid() { + t.Fatalf("GetBlock returned unexpected block") + } + if bstore.PutCounter != 1 { + t.Fatalf("expected one Put call, have: %d", bstore.PutCounter) + } + + // GetBlocks + b1 := bgen.Next() + err = exchbstore.Put(context.Background(), b1) + if err != nil { + t.Fatal(err) + } + b2 := bgen.Next() + err = exchbstore.Put(context.Background(), b2) + if err != nil { + t.Fatal(err) + } + bchan := bserv.GetBlocks(context.Background(), []cid.Cid{b1.Cid(), b2.Cid()}) + var gotBlocks []blocks.Block + for b := range bchan { + gotBlocks = append(gotBlocks, b) + } + if len(gotBlocks) != 2 { + t.Fatalf("expected to retrieve 2 blocks, got %d", len(gotBlocks)) + } + if bstore.PutCounter != 3 { + t.Fatalf("expected 3 Put call, have: %d", bstore.PutCounter) + } +} + func TestLazySessionInitialization(t *testing.T) { ctx := context.Background() ctx, cancel := context.WithCancel(ctx) @@ -53,8 +105,8 @@ func TestLazySessionInitialization(t *testing.T) { bstore2 := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) bstore3 := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) session := offline.Exchange(bstore2) - exchange := offline.Exchange(bstore3) - sessionExch := &fakeSessionExchange{Interface: exchange, session: session} + exch := offline.Exchange(bstore3) + sessionExch := &fakeSessionExchange{Interface: exch, session: session} bservSessEx := NewWriteThrough(bstore, sessionExch) bgen := butil.NewBlockGenerator() @@ -64,7 +116,11 @@ func TestLazySessionInitialization(t *testing.T) { t.Fatal(err) } block2 := bgen.Next() - err = session.HasBlock(ctx, block2) + err = bstore2.Put(ctx, block2) + if err != nil { + t.Fatal(err) + } + err = session.NotifyNewBlocks(ctx, block2) if err != nil { t.Fatal(err) } @@ -107,6 +163,11 @@ func (bs *PutCountingBlockstore) Put(ctx context.Context, block blocks.Block) er return bs.Blockstore.Put(ctx, block) } +func (bs *PutCountingBlockstore) PutMany(ctx context.Context, blocks []blocks.Block) error { + bs.PutCounter += len(blocks) + return bs.Blockstore.PutMany(ctx, blocks) +} + var _ exchange.SessionExchange = (*fakeSessionExchange)(nil) type fakeSessionExchange struct { From 931b05363833d5de3b64e86a6c75f5c6cf5e7fd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Thu, 14 Jul 2022 12:27:39 +0200 Subject: [PATCH 3704/3817] Exchange don't add blocks on their own anymore Follows: - https://github.com/ipfs/go-ipfs-exchange-interface/pull/23 - https://github.com/ipfs/go-bitswap/pull/571 This commit was moved from ipfs/go-ipfs-exchange-offline@678648ad69e71cacaee5caff28ce179f5aa96bfa --- exchange/offline/offline.go | 11 ++++------- exchange/offline/offline_test.go | 16 ++++++++++------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/exchange/offline/offline.go b/exchange/offline/offline.go index 7c5d7c5ea..430a8d744 100644 --- a/exchange/offline/offline.go +++ b/exchange/offline/offline.go @@ -34,9 +34,10 @@ func (e *offlineExchange) GetBlock(ctx context.Context, k cid.Cid) (blocks.Block return blk, err } -// HasBlock always returns nil. -func (e *offlineExchange) HasBlock(ctx context.Context, b blocks.Block) error { - return e.bs.Put(ctx, b) +// NotifyNewBlocks tells the exchange that new blocks are available and can be served. +func (e *offlineExchange) NotifyNewBlocks(ctx context.Context, blocks ...blocks.Block) error { + // as an offline exchange we have nothing to do + return nil } // Close always returns nil. @@ -71,7 +72,3 @@ func (e *offlineExchange) GetBlocks(ctx context.Context, ks []cid.Cid) (<-chan b }() return out, nil } - -func (e *offlineExchange) IsOnline() bool { - return false -} diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index 0ac95a6b6..e329372b7 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -28,13 +28,14 @@ func TestHasBlockReturnsNil(t *testing.T) { ex := Exchange(store) block := blocks.NewBlock([]byte("data")) - err := ex.HasBlock(context.Background(), block) - if err != nil { - t.Fail() + // we don't need to do that for the test, but that illustrate the normal workflow + if err := store.Put(context.Background(), block); err != nil { + t.Fatal(err) } - if _, err := store.Get(context.Background(), block.Cid()); err != nil { - t.Fatal(err) + err := ex.NotifyNewBlocks(context.Background(), block) + if err != nil { + t.Fail() } } @@ -46,7 +47,10 @@ func TestGetBlocks(t *testing.T) { expected := g.Blocks(2) for _, b := range expected { - if err := ex.HasBlock(context.Background(), b); err != nil { + if err := store.Put(context.Background(), b); err != nil { + t.Fatal(err) + } + if err := ex.NotifyNewBlocks(context.Background(), b); err != nil { t.Fail() } } From b739af18ef85e7c9c667be9b4d620a3d2484bc76 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Wed, 27 Jul 2022 17:23:20 +0200 Subject: [PATCH 3705/3817] test: remove TestHasBlockReturnsNil The function body is just ```go return nil ``` And it's testing that this code returns nil. Pointless This commit was moved from ipfs/go-ipfs-exchange-offline@cdbe3d1b96514186d529dad54f95557b28e2fb2e --- exchange/offline/offline_test.go | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/exchange/offline/offline_test.go b/exchange/offline/offline_test.go index e329372b7..07ac1d001 100644 --- a/exchange/offline/offline_test.go +++ b/exchange/offline/offline_test.go @@ -4,7 +4,6 @@ import ( "context" "testing" - blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" ds_sync "github.com/ipfs/go-datastore/sync" @@ -23,22 +22,6 @@ func TestBlockReturnsErr(t *testing.T) { t.Fail() } -func TestHasBlockReturnsNil(t *testing.T) { - store := bstore() - ex := Exchange(store) - block := blocks.NewBlock([]byte("data")) - - // we don't need to do that for the test, but that illustrate the normal workflow - if err := store.Put(context.Background(), block); err != nil { - t.Fatal(err) - } - - err := ex.NotifyNewBlocks(context.Background(), block) - if err != nil { - t.Fail() - } -} - func TestGetBlocks(t *testing.T) { store := bstore() ex := Exchange(store) From 8ee16bb46719f767ee357b1d7bd66207d5225bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Wed, 27 Jul 2022 19:21:20 +0200 Subject: [PATCH 3706/3817] blockservice should notify the exchange when caching blocks in GetBlock(s) This commit was moved from ipfs/go-blockservice@f2a4f4f21dfc5f9ff7d4abb3c1f96368fa707600 --- blockservice/blockservice.go | 33 ++++++--- blockservice/blockservice_test.go | 110 +++++++++++++++++++----------- 2 files changed, 93 insertions(+), 50 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 134de222b..015f81a06 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -214,7 +214,7 @@ func (s *blockService) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, e ctx, span := internal.StartSpan(ctx, "blockService.GetBlock", trace.WithAttributes(attribute.Stringer("CID", c))) defer span.End() - var f func() exchange.Fetcher + var f func() exchange.Interface if s.exchange != nil { f = s.getExchange } @@ -222,11 +222,11 @@ func (s *blockService) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, e return getBlock(ctx, c, s.blockstore, f) // hash security } -func (s *blockService) getExchange() exchange.Fetcher { +func (s *blockService) getExchange() exchange.Interface { return s.exchange } -func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget func() exchange.Fetcher) (blocks.Block, error) { +func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget func() exchange.Interface) (blocks.Block, error) { err := verifcid.ValidateCid(c) // hash security if err != nil { return nil, err @@ -247,11 +247,15 @@ func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget fun if err != nil { return nil, err } - // also write in the blockstore for caching + // also write in the blockstore for caching, inform the exchange that the block is available err = bs.Put(ctx, blk) if err != nil { return nil, err } + err = f.NotifyNewBlocks(ctx, blk) + if err != nil { + return nil, err + } logger.Debugf("BlockService.BlockFetched %s", c) return blk, nil } @@ -267,7 +271,7 @@ func (s *blockService) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan block ctx, span := internal.StartSpan(ctx, "blockService.GetBlocks") defer span.End() - var f func() exchange.Fetcher + var f func() exchange.Interface if s.exchange != nil { f = s.getExchange } @@ -275,7 +279,7 @@ func (s *blockService) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan block return getBlocks(ctx, ks, s.blockstore, f) // hash security } -func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget func() exchange.Fetcher) <-chan blocks.Block { +func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget func() exchange.Interface) <-chan blocks.Block { out := make(chan blocks.Block) go func() { @@ -351,13 +355,19 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget } } - // also write in the blockstore for caching + // also write in the blockstore for caching, inform the exchange that the blocks are available err = bs.PutMany(ctx, batch) if err != nil { logger.Errorf("could not write blocks from the network to the blockstore: %s", err) return } + err = f.NotifyNewBlocks(ctx, batch...) + if err != nil { + logger.Errorf("could not tell the exchange about new blocks: %s", err) + return + } + for _, b = range batch { select { case out <- b: @@ -396,14 +406,15 @@ type Session struct { lk sync.Mutex } -func (s *Session) getSession() exchange.Fetcher { +func (s *Session) getSession() exchange.Interface { s.lk.Lock() defer s.lk.Unlock() if s.ses == nil { s.ses = s.sessEx.NewSession(s.sessCtx) } - return s.ses + // TODO: don't do that + return s.ses.(exchange.Interface) } // GetBlock gets a block in the context of a request session @@ -411,7 +422,7 @@ func (s *Session) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) ctx, span := internal.StartSpan(ctx, "Session.GetBlock", trace.WithAttributes(attribute.Stringer("CID", c))) defer span.End() - var f func() exchange.Fetcher + var f func() exchange.Interface if s.sessEx != nil { f = s.getSession } @@ -423,7 +434,7 @@ func (s *Session) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan blocks.Blo ctx, span := internal.StartSpan(ctx, "Session.GetBlocks") defer span.End() - var f func() exchange.Fetcher + var f func() exchange.Interface if s.sessEx != nil { f = s.getSession } diff --git a/blockservice/blockservice_test.go b/blockservice/blockservice_test.go index 5753d3a9d..846ae7169 100644 --- a/blockservice/blockservice_test.go +++ b/blockservice/blockservice_test.go @@ -51,48 +51,68 @@ func TestExchangeWrite(t *testing.T) { 0, } exchbstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) - exch := offline.Exchange(exchbstore) + exch := ¬ifyCountingExchange{ + offline.Exchange(exchbstore), + 0, + } bserv := NewWriteThrough(bstore, exch) bgen := butil.NewBlockGenerator() - // GetBlock - block := bgen.Next() - err := exchbstore.Put(context.Background(), block) - if err != nil { - t.Fatal(err) - } - got, err := bserv.GetBlock(context.Background(), block.Cid()) - if err != nil { - t.Fatal(err) - } - if got.Cid() != block.Cid() { - t.Fatalf("GetBlock returned unexpected block") - } - if bstore.PutCounter != 1 { - t.Fatalf("expected one Put call, have: %d", bstore.PutCounter) - } - - // GetBlocks - b1 := bgen.Next() - err = exchbstore.Put(context.Background(), b1) - if err != nil { - t.Fatal(err) - } - b2 := bgen.Next() - err = exchbstore.Put(context.Background(), b2) - if err != nil { - t.Fatal(err) - } - bchan := bserv.GetBlocks(context.Background(), []cid.Cid{b1.Cid(), b2.Cid()}) - var gotBlocks []blocks.Block - for b := range bchan { - gotBlocks = append(gotBlocks, b) - } - if len(gotBlocks) != 2 { - t.Fatalf("expected to retrieve 2 blocks, got %d", len(gotBlocks)) - } - if bstore.PutCounter != 3 { - t.Fatalf("expected 3 Put call, have: %d", bstore.PutCounter) + for name, fetcher := range map[string]BlockGetter{ + "blockservice": bserv, + "session": NewSession(context.Background(), bserv), + } { + t.Run(name, func(t *testing.T) { + // GetBlock + block := bgen.Next() + err := exchbstore.Put(context.Background(), block) + if err != nil { + t.Fatal(err) + } + got, err := fetcher.GetBlock(context.Background(), block.Cid()) + if err != nil { + t.Fatal(err) + } + if got.Cid() != block.Cid() { + t.Fatalf("GetBlock returned unexpected block") + } + if bstore.PutCounter != 1 { + t.Fatalf("expected one Put call, have: %d", bstore.PutCounter) + } + if exch.notifyCount != 1 { + t.Fatalf("expected one NotifyNewBlocks call, have: %d", exch.notifyCount) + } + + // GetBlocks + b1 := bgen.Next() + err = exchbstore.Put(context.Background(), b1) + if err != nil { + t.Fatal(err) + } + b2 := bgen.Next() + err = exchbstore.Put(context.Background(), b2) + if err != nil { + t.Fatal(err) + } + bchan := fetcher.GetBlocks(context.Background(), []cid.Cid{b1.Cid(), b2.Cid()}) + var gotBlocks []blocks.Block + for b := range bchan { + gotBlocks = append(gotBlocks, b) + } + if len(gotBlocks) != 2 { + t.Fatalf("expected to retrieve 2 blocks, got %d", len(gotBlocks)) + } + if bstore.PutCounter != 3 { + t.Fatalf("expected 3 Put call, have: %d", bstore.PutCounter) + } + if exch.notifyCount != 3 { + t.Fatalf("expected one NotifyNewBlocks call, have: %d", exch.notifyCount) + } + + // reset counts + bstore.PutCounter = 0 + exch.notifyCount = 0 + }) } } @@ -168,6 +188,18 @@ func (bs *PutCountingBlockstore) PutMany(ctx context.Context, blocks []blocks.Bl return bs.Blockstore.PutMany(ctx, blocks) } +var _ exchange.Interface = (*notifyCountingExchange)(nil) + +type notifyCountingExchange struct { + exchange.Interface + notifyCount int +} + +func (n *notifyCountingExchange) NotifyNewBlocks(ctx context.Context, blocks ...blocks.Block) error { + n.notifyCount += len(blocks) + return n.Interface.NotifyNewBlocks(ctx, blocks...) +} + var _ exchange.SessionExchange = (*fakeSessionExchange)(nil) type fakeSessionExchange struct { From 620a34f5e1f01e3fd7bf89d48c8ce01d92822a08 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Wed, 27 Jul 2022 22:01:37 +0200 Subject: [PATCH 3707/3817] fix: correctly fallback to non session exchanges with sessions. This commit was moved from ipfs/go-blockservice@c72dade75a0ec8b9a719ed80dada29bdde114f42 --- blockservice/blockservice.go | 83 +++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 30 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index 015f81a06..b86f2ebbb 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -118,16 +118,18 @@ func NewSession(ctx context.Context, bs BlockService) *Session { exch := bs.Exchange() if sessEx, ok := exch.(exchange.SessionExchange); ok { return &Session{ - sessCtx: ctx, - ses: nil, - sessEx: sessEx, - bs: bs.Blockstore(), + sessCtx: ctx, + ses: nil, + sessEx: sessEx, + bs: bs.Blockstore(), + notifier: exch, } } return &Session{ - ses: exch, - sessCtx: ctx, - bs: bs.Blockstore(), + ses: exch, + sessCtx: ctx, + bs: bs.Blockstore(), + notifier: exch, } } @@ -214,7 +216,7 @@ func (s *blockService) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, e ctx, span := internal.StartSpan(ctx, "blockService.GetBlock", trace.WithAttributes(attribute.Stringer("CID", c))) defer span.End() - var f func() exchange.Interface + var f func() notifiableFetcher if s.exchange != nil { f = s.getExchange } @@ -222,11 +224,11 @@ func (s *blockService) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, e return getBlock(ctx, c, s.blockstore, f) // hash security } -func (s *blockService) getExchange() exchange.Interface { +func (s *blockService) getExchange() notifiableFetcher { return s.exchange } -func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget func() exchange.Interface) (blocks.Block, error) { +func getBlock(ctx context.Context, c cid.Cid, bs blockstore.Blockstore, fget func() notifiableFetcher) (blocks.Block, error) { err := verifcid.ValidateCid(c) // hash security if err != nil { return nil, err @@ -271,7 +273,7 @@ func (s *blockService) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan block ctx, span := internal.StartSpan(ctx, "blockService.GetBlocks") defer span.End() - var f func() exchange.Interface + var f func() notifiableFetcher if s.exchange != nil { f = s.getExchange } @@ -279,7 +281,7 @@ func (s *blockService) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan block return getBlocks(ctx, ks, s.blockstore, f) // hash security } -func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget func() exchange.Interface) <-chan blocks.Block { +func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget func() notifiableFetcher) <-chan blocks.Block { out := make(chan blocks.Block) go func() { @@ -397,24 +399,53 @@ func (s *blockService) Close() error { return s.exchange.Close() } +type notifier interface { + NotifyNewBlocks(context.Context, ...blocks.Block) error +} + // Session is a helper type to provide higher level access to bitswap sessions type Session struct { - bs blockstore.Blockstore - ses exchange.Fetcher - sessEx exchange.SessionExchange - sessCtx context.Context - lk sync.Mutex + bs blockstore.Blockstore + ses exchange.Fetcher + sessEx exchange.SessionExchange + sessCtx context.Context + notifier notifier + lk sync.Mutex } -func (s *Session) getSession() exchange.Interface { +type notifiableFetcher interface { + exchange.Fetcher + notifier +} + +type notifiableFetcherWrapper struct { + exchange.Fetcher + notifier +} + +func (s *Session) getSession() notifiableFetcher { s.lk.Lock() defer s.lk.Unlock() if s.ses == nil { s.ses = s.sessEx.NewSession(s.sessCtx) } - // TODO: don't do that - return s.ses.(exchange.Interface) + return notifiableFetcherWrapper{s.ses, s.notifier} +} + +func (s *Session) getExchange() notifiableFetcher { + return notifiableFetcherWrapper{s.ses, s.notifier} +} + +func (s *Session) getFetcherFactory() func() notifiableFetcher { + if s.sessEx != nil { + return s.getSession + } + if s.ses != nil { + // Our exchange isn't session compatible, let's fallback to non sessions fetches + return s.getExchange + } + return nil } // GetBlock gets a block in the context of a request session @@ -422,11 +453,7 @@ func (s *Session) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) ctx, span := internal.StartSpan(ctx, "Session.GetBlock", trace.WithAttributes(attribute.Stringer("CID", c))) defer span.End() - var f func() exchange.Interface - if s.sessEx != nil { - f = s.getSession - } - return getBlock(ctx, c, s.bs, f) // hash security + return getBlock(ctx, c, s.bs, s.getFetcherFactory()) // hash security } // GetBlocks gets blocks in the context of a request session @@ -434,11 +461,7 @@ func (s *Session) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan blocks.Blo ctx, span := internal.StartSpan(ctx, "Session.GetBlocks") defer span.End() - var f func() exchange.Interface - if s.sessEx != nil { - f = s.getSession - } - return getBlocks(ctx, ks, s.bs, f) // hash security + return getBlocks(ctx, ks, s.bs, s.getFetcherFactory()) // hash security } var _ BlockGetter = (*Session)(nil) From 00087bb91924e9ce5944e164cccb9cb575be5613 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Thu, 28 Jul 2022 05:14:06 +0200 Subject: [PATCH 3708/3817] refactor: rewrite getBlocks batch loop to be clearer This commit was moved from ipfs/go-blockservice@30ca6aad09fdc64f3cda352c3a2a52468df7ceb1 --- blockservice/blockservice.go | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/blockservice/blockservice.go b/blockservice/blockservice.go index b86f2ebbb..8594e253a 100644 --- a/blockservice/blockservice.go +++ b/blockservice/blockservice.go @@ -333,23 +333,22 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget return } - for b := range rblocks { - // batch available blocks together - batch := make([]blocks.Block, 0, 8) - batch = append(batch, b) - logger.Debugf("BlockService.BlockFetched %s", b.Cid()) - + // batch available blocks together + const batchSize = 32 + batch := make([]blocks.Block, 0, batchSize) + for { + var noMoreBlocks bool batchLoop: - for { + for len(batch) < batchSize { select { - case moreBlock, ok := <-rblocks: + case b, ok := <-rblocks: if !ok { - // rblock has been closed, we set it to nil to avoid pulling zero values - rblocks = nil - } else { - logger.Debugf("BlockService.BlockFetched %s", moreBlock.Cid()) - batch = append(batch, moreBlock) + noMoreBlocks = true + break batchLoop } + + logger.Debugf("BlockService.BlockFetched %s", b.Cid()) + batch = append(batch, b) case <-ctx.Done(): return default: @@ -370,13 +369,17 @@ func getBlocks(ctx context.Context, ks []cid.Cid, bs blockstore.Blockstore, fget return } - for _, b = range batch { + for _, b := range batch { select { case out <- b: case <-ctx.Done(): return } } + batch = batch[:0] + if noMoreBlocks { + break + } } }() return out From 8c46cf0a4537c08ab094a545a07fc5676fb6cc28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Mon, 1 Aug 2022 09:30:18 +0200 Subject: [PATCH 3709/3817] blockstore: allow to pass a file to write in (#323) This allow the caller to control the file lifecycle and implement a cleaning strategy. An example would be: - open a file in a temporary folder - if the write suceed, close the file and move it to its final destination - on error, remove the file If the underlying filesystem support it, an anonymous file can be used instead: open then immediately delete, use the file descriptor to write. A crash before the end of the write process would release the used storage automatically. Co-authored-by: Rod Vagg This commit was moved from ipld/go-car@ed56551bf6e54d68b783a75ac396e45f053bcc98 --- ipld/car/v2/blockstore/readwrite.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 774f37ff6..542d0df42 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -10,12 +10,13 @@ import ( blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" + "github.com/multiformats/go-varint" + carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/carv1/util" internalio "github.com/ipld/go-car/v2/internal/io" - "github.com/multiformats/go-varint" ) var _ blockstore.Blockstore = (*ReadWrite)(nil) @@ -103,6 +104,18 @@ func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.Option) (*ReadWri if err != nil { return nil, fmt.Errorf("could not open read/write file: %w", err) } + rwbs, err := OpenReadWriteFile(f, roots, opts...) + if err != nil { + return nil, err + } + // close the file when finalizing + rwbs.ronly.carv2Closer = rwbs.f + return rwbs, nil +} + +// OpenReadWriteFile is similar as OpenReadWrite but lets you control the file lifecycle. +// You are responsible for closing the given file. +func OpenReadWriteFile(f *os.File, roots []cid.Cid, opts ...carv2.Option) (*ReadWrite, error) { stat, err := f.Stat() if err != nil { // Note, we should not get a an os.ErrNotExist here because the flags used to open file includes os.O_CREATE @@ -145,7 +158,6 @@ func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.Option) (*ReadWri } rwbs.ronly.backing = v1r rwbs.ronly.idx = rwbs.idx - rwbs.ronly.carv2Closer = rwbs.f if resume { if err = rwbs.resumeWithRoots(!rwbs.opts.WriteAsCarV1, roots); err != nil { From 51ec22e2dff4d58189ac1d05821a2f6478bc348b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Mon, 1 Aug 2022 10:36:44 +0200 Subject: [PATCH 3710/3817] OpenReadWriteFile: add test This commit was moved from ipld/go-car@02ff91a599ea2f47d45345712171d10cf7249a1f --- ipld/car/v2/blockstore/readwrite_test.go | 35 ++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 22525e789..bfa4e23c1 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -945,6 +945,41 @@ func TestReadWrite_ReWritingCARv1WithIdentityCidIsIdenticalToOriginalWithOptions require.Equal(t, wantSum, gotSum) } +func TestReadWriteOpenFile(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + dir := t.TempDir() // auto cleanup + f, err := ioutil.TempFile(dir, "") + require.NoError(t, err) + + root := blocks.NewBlock([]byte("foo")) + + bs, err := blockstore.OpenReadWriteFile(f, []cid.Cid{root.Cid()}) + require.NoError(t, err) + + err = bs.Put(ctx, root) + require.NoError(t, err) + + roots, err := bs.Roots() + require.NoError(t, err) + _, err = bs.Has(ctx, roots[0]) + require.NoError(t, err) + _, err = bs.Get(ctx, roots[0]) + require.NoError(t, err) + _, err = bs.GetSize(ctx, roots[0]) + require.NoError(t, err) + + err = bs.Finalize() + require.NoError(t, err) + + _, err = f.Seek(0, 0) + require.NoError(t, err) // file should not be closed, let the caller do it + + err = f.Close() + require.NoError(t, err) +} + func TestBlockstore_IdentityCidWithEmptyDataIsIndexed(t *testing.T) { p := path.Join(t.TempDir(), "car-id-cid-empty.carv2") var noData []byte From 89bd5ed0e52937d443c00d802dfe5f22fe7d9bd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Fri, 5 Aug 2022 19:55:21 +0200 Subject: [PATCH 3711/3817] docs: Add proper documenation to the interface. This commit was moved from ipfs/go-ipfs-exchange-interface@7604dcd563e1eda7f15bd4bbbfbec4fad67c9ac1 --- exchange/interface.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/exchange/interface.go b/exchange/interface.go index 2c04628e3..3ae174d5c 100644 --- a/exchange/interface.go +++ b/exchange/interface.go @@ -21,8 +21,11 @@ type Interface interface { // type Exchanger interface // Fetcher is an object that can be used to retrieve blocks type Fetcher interface { - // GetBlock returns the block associated with a given key. + // GetBlock returns the block associated with a given cid. GetBlock(context.Context, cid.Cid) (blocks.Block, error) + // GetBlocks returns the blocks associated with the given cids. + // If the requested blocks are not found immediately, this function should hang until + // they are found. If they can't be found later, it's also acceptable to terminate. GetBlocks(context.Context, []cid.Cid) (<-chan blocks.Block, error) } @@ -30,5 +33,8 @@ type Fetcher interface { // sessions. type SessionExchange interface { Interface + // NewSession generates a new exchange session. You should use this, rather + // that calling GetBlocks, any time you intend to do several related calls + // in a row. The exchange can leverage that to be more efficient. NewSession(context.Context) Fetcher } From 912fc33114889cd525c7c401b277acba3607e78c Mon Sep 17 00:00:00 2001 From: Claudia Richoux Date: Thu, 30 Jun 2022 16:06:22 -0400 Subject: [PATCH 3712/3817] feat: add blake3 as a good hash This include fixing versions, adding tests, restraining blake3 to 20 <= x <= 128 bytes of length, improving error messages, fixing deprecation warnings. This commit was moved from ipfs/go-verifcid@71377ec686e3333895ca71bdbf85e829ddcc73b4 --- verifcid/validate.go | 14 ++++++++++---- verifcid/validate_test.go | 15 +++++++++++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/verifcid/validate.go b/verifcid/validate.go index 8a76e4933..e594629f4 100644 --- a/verifcid/validate.go +++ b/verifcid/validate.go @@ -2,15 +2,16 @@ package verifcid import ( "fmt" - cid "github.com/ipfs/go-cid" mh "github.com/multiformats/go-multihash" ) var ErrPossiblyInsecureHashFunction = fmt.Errorf("potentially insecure hash functions not allowed") -var ErrBelowMinimumHashLength = fmt.Errorf("hashes must be at %d least bytes long", minimumHashLength) +var ErrBelowMinimumHashLength = fmt.Errorf("hashes must be at least %d bytes long", minimumHashLength) +var ErrAboveMaximumHashLength = fmt.Errorf("hashes must be at most %d bytes long", maximumHashLength) const minimumHashLength = 20 +const maximumHashLength = 128 var goodset = map[uint64]bool{ mh.SHA2_256: true, @@ -25,7 +26,8 @@ var goodset = map[uint64]bool{ mh.KECCAK_256: true, mh.KECCAK_384: true, mh.KECCAK_512: true, - mh.ID: true, + mh.BLAKE3: true, + mh.IDENTITY: true, mh.SHA1: true, // not really secure but still useful } @@ -54,9 +56,13 @@ func ValidateCid(c cid.Cid) error { return ErrPossiblyInsecureHashFunction } - if pref.MhType != mh.ID && pref.MhLength < minimumHashLength { + if pref.MhType != mh.IDENTITY && pref.MhLength < minimumHashLength { return ErrBelowMinimumHashLength } + if pref.MhType != mh.IDENTITY && pref.MhLength > maximumHashLength { + return ErrAboveMaximumHashLength + } + return nil } diff --git a/verifcid/validate_test.go b/verifcid/validate_test.go index 1d31e5464..5129b861a 100644 --- a/verifcid/validate_test.go +++ b/verifcid/validate_test.go @@ -35,7 +35,7 @@ func TestValidateCids(t *testing.T) { mhcid := func(code uint64, length int) cid.Cid { mhash, err := mh.Sum([]byte{}, code, length) if err != nil { - t.Fatal(err) + t.Fatalf("%v: code: %x length: %d", err, code, length) } return cid.NewCidV1(cid.DagCBOR, mhash) } @@ -46,7 +46,10 @@ func TestValidateCids(t *testing.T) { }{ {mhcid(mh.SHA2_256, 32), nil}, {mhcid(mh.SHA2_256, 16), ErrBelowMinimumHashLength}, - {mhcid(mh.MURMUR3, 4), ErrPossiblyInsecureHashFunction}, + {mhcid(mh.MURMUR3X64_64, 4), ErrPossiblyInsecureHashFunction}, + {mhcid(mh.BLAKE3, 32), nil}, + {mhcid(mh.BLAKE3, 69), nil}, + {mhcid(mh.BLAKE3, 128), nil}, } for i, cas := range cases { @@ -56,4 +59,12 @@ func TestValidateCids(t *testing.T) { } } + longBlake3Hex := "1e810104e0bb39f30b1a3feb89f536c93be15055482df748674b00d26e5a75777702e9791074b7511b59d31c71c62f5a745689fa6c9497f68bdf1061fe07f518d410c0b0c27f41b3cf083f8a7fdc67a877e21790515762a754a45dcb8a356722698a7af5ed2bb608983d5aa75d4d61691ef132efe8631ce0afc15553a08fffc60ee9369b" + longBlake3Mh, err := mh.FromHexString(longBlake3Hex) + if err != nil { + t.Fatalf("failed to produce a multihash from the long blake3 hash: %v", err) + } + if ValidateCid(cid.NewCidV1(cid.DagCBOR, longBlake3Mh)) != ErrAboveMaximumHashLength { + t.Errorf("a CID that was longer than the maximum hash length did not error with ErrAboveMaximumHashLength") + } } From 0f23770ba4b900470ac98c832b64f6087c33e353 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Fri, 19 Aug 2022 17:06:46 +0300 Subject: [PATCH 3713/3817] use peer.IDFromBytes instead of peer.IDFromString (#38) This commit was moved from ipfs/go-ipns@0d8e99ba180a607c39a3a1f4a787aba188489f70 --- ipns/record.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ipns/record.go b/ipns/record.go index cd2ec3cdd..43750bf3b 100644 --- a/ipns/record.go +++ b/ipns/record.go @@ -6,10 +6,10 @@ import ( pb "github.com/ipfs/go-ipns/pb" - proto "github.com/gogo/protobuf/proto" + "github.com/gogo/protobuf/proto" logging "github.com/ipfs/go-log" ic "github.com/libp2p/go-libp2p-core/crypto" - peer "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p-core/peer" pstore "github.com/libp2p/go-libp2p-core/peerstore" record "github.com/libp2p/go-libp2p-record" ) @@ -46,7 +46,7 @@ func (v Validator) Validate(key string, value []byte) error { } // Get the public key defined by the ipns path - pid, err := peer.IDFromString(pidString) + pid, err := peer.IDFromBytes([]byte(pidString)) if err != nil { log.Debugf("failed to parse ipns record key %s into peer ID", pidString) return ErrKeyFormat From 1b8377b58d915a538afce6617d60ac901da615c6 Mon Sep 17 00:00:00 2001 From: web3-bot <81333946+web3-bot@users.noreply.github.com> Date: Tue, 23 Aug 2022 18:50:03 +0200 Subject: [PATCH 3714/3817] sync: update CI config files (#105) This commit was moved from ipfs/go-ipfs-blockstore@9904c18f1d0af576cdd27bbc825a920e14a71b9e --- blockstore/arc_cache.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/blockstore/arc_cache.go b/blockstore/arc_cache.go index 2733dfc37..ba8ecbb63 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/arc_cache.go @@ -366,9 +366,9 @@ func (b *arccache) cacheInvalidate(key string) { // queryCache checks if the CID is in the cache. If so, it returns: // -// * exists (bool): whether the CID is known to exist or not. -// * size (int): the size if cached, or -1 if not cached. -// * ok (bool): whether present in the cache. +// - exists (bool): whether the CID is known to exist or not. +// - size (int): the size if cached, or -1 if not cached. +// - ok (bool): whether present in the cache. // // When ok is false, the answer in inconclusive and the caller must ignore the // other two return values. Querying the underying store is necessary. From 3cb06eb87f657b119bcaa7c49049cc541d801e2c Mon Sep 17 00:00:00 2001 From: web3-bot <81333946+web3-bot@users.noreply.github.com> Date: Tue, 23 Aug 2022 18:52:01 +0200 Subject: [PATCH 3715/3817] sync: update CI config files (#34) This commit was moved from ipfs/go-ipfs-routing@bf0374f99212a07b1a33f92cb7e341a2e2a8061b --- routing/none/none_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 9604ab07c..2ba1a8f8f 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -53,5 +53,5 @@ func ConstructNilRouting(_ context.Context, _ host.Host, _ ds.Batching, _ record return &nilclient{}, nil } -// ensure nilclient satisfies interface +// ensure nilclient satisfies interface var _ routing.Routing = &nilclient{} From 5cfe8e6e48cfb6c80c5322260b033c1b48ced66f Mon Sep 17 00:00:00 2001 From: web3-bot <81333946+web3-bot@users.noreply.github.com> Date: Tue, 23 Aug 2022 18:52:25 +0200 Subject: [PATCH 3716/3817] sync: update CI config files (#60) This commit was moved from ipfs/go-path@34ee0e38d040b22a4baf20740af45e43c8a5d50e --- path/path.go | 8 ++++---- path/resolver/resolver.go | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/path/path.go b/path/path.go index f49dde110..e70d96384 100644 --- a/path/path.go +++ b/path/path.go @@ -10,10 +10,10 @@ import ( ) // A Path represents an ipfs content path: -// * /path/to/file -// * /ipfs/ -// * /ipns//path/to/folder -// * etc +// - /path/to/file +// - /ipfs/ +// - /ipns//path/to/folder +// - etc type Path string // ^^^ diff --git a/path/resolver/resolver.go b/path/resolver/resolver.go index ba7e1a2c1..74c5b27ed 100644 --- a/path/resolver/resolver.go +++ b/path/resolver/resolver.go @@ -63,7 +63,8 @@ type Resolver interface { // basicResolver implements the Resolver interface. // It references a FetcherFactory, which is uses to resolve nodes. // TODO: now that this is more modular, try to unify this code with the -// the resolvers in namesys. +// +// the resolvers in namesys. type basicResolver struct { FetcherFactory fetcher.Factory } From b180aa18693c06de87594f3a36e489c962c33d04 Mon Sep 17 00:00:00 2001 From: web3-bot <81333946+web3-bot@users.noreply.github.com> Date: Tue, 23 Aug 2022 18:53:02 +0200 Subject: [PATCH 3717/3817] sync: update CI config files (#39) This commit was moved from ipfs/go-ipfs-chunker@7d123e096f645cc95d8fe48365e1d17b0336a005 --- chunker/buzhash_norace_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/chunker/buzhash_norace_test.go b/chunker/buzhash_norace_test.go index 7d1d03d6d..50dc0e5ce 100644 --- a/chunker/buzhash_norace_test.go +++ b/chunker/buzhash_norace_test.go @@ -1,5 +1,4 @@ //go:build !race -// +build !race package chunk From e5532fe4f9dba73d3762eab87fd749614c8dfb1d Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 16 Aug 2022 22:03:28 +1000 Subject: [PATCH 3718/3817] fix!: keep deserialised state stable until explicit mutation When decoding a badly serialised block with Links out of order, don't sort the list until we receive an explicit mutation operation. This ensures stable DAG traversal ordering based on the links as they appear in the serialised form and removes surprise-sorting when performing certain operations that wouldn't be expected to mutate. The pre-v0.4.0 behaviour was to always sort, but this behaviour wasn't baked in to the dag-pb spec and wasn't built into go-codec-dagpb which now forms the backend of ProtoNode, although remnants of sorting remain in some operations. Almost all CAR-from-DAG creation code in Go uses go-codec-dagpb and go-ipld-prime's traversal engine. However this can result in a different order when encountering badly encoded blocks (unsorted Links) where certain intermediate operations are performed on the ProtoNode prior to obtaining the Links() list (Links() itself doesn't sort, but e.g. RawData() does). The included "TestLinkSorting/decode" test is the only case that passes without this patch. Ref: https://github.com/ipld/ipld/pull/233 Ref: https://github.com/filecoin-project/boost/issues/673 Ref: https://github.com/filecoin-project/boost/pull/675 This commit was moved from ipfs/go-merkledag@48c72029ffaeed0c3f864af03bc0906d1d5fcb2c --- ipld/merkledag/coding.go | 33 ++++- ipld/merkledag/merkledag_test.go | 220 +++++++++++++++++++++++++++++++ ipld/merkledag/node.go | 99 +++++++++++--- 3 files changed, 328 insertions(+), 24 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index bdc1dcde2..0a71b0ac5 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -23,6 +23,14 @@ const _ = pb.DoNotUpgradeFileEverItWillChangeYourHashes // for now, we use a PBNode intermediate thing. // because native go objects are nice. +// pbLinkSlice is a slice of pb.PBLink, similar to LinkSlice but for sorting the +// PB form +type pbLinkSlice []*pb.PBLink + +func (pbls pbLinkSlice) Len() int { return len(pbls) } +func (pbls pbLinkSlice) Swap(a, b int) { pbls[a], pbls[b] = pbls[b], pbls[a] } +func (pbls pbLinkSlice) Less(a, b int) bool { return *pbls[a].Name < *pbls[b].Name } + // unmarshal decodes raw data into a *Node instance. // The conversion uses an intermediate PBNode. func unmarshal(encodedBytes []byte) (*ProtoNode, error) { @@ -41,6 +49,9 @@ func fromImmutableNode(encoded *immutableProtoNode) *ProtoNode { n.data = n.encoded.PBNode.Data.Must().Bytes() } numLinks := n.encoded.PBNode.Links.Length() + // links may not be sorted after deserialization, but we don't change + // them until we mutate this node since we're representing the current, + // as-serialized state n.links = make([]*format.Link, numLinks) linkAllocs := make([]format.Link, numLinks) for i := int64(0); i < numLinks; i++ { @@ -60,12 +71,15 @@ func fromImmutableNode(encoded *immutableProtoNode) *ProtoNode { link.Cid = c n.links[i] = link } + // we don't set n.linksDirty because the order of the links list from + // serialized form needs to be stable, until we start mutating the ProtoNode return n } func (n *ProtoNode) marshalImmutable() (*immutableProtoNode, error) { + links := n.Links() nd, err := qp.BuildMap(dagpb.Type.PBNode, 2, func(ma ipld.MapAssembler) { - qp.MapEntry(ma, "Links", qp.List(int64(len(n.links)), func(la ipld.ListAssembler) { - for _, link := range n.links { + qp.MapEntry(ma, "Links", qp.List(int64(len(links)), func(la ipld.ListAssembler) { + for _, link := range links { qp.ListEntry(la, qp.Map(3, func(ma ipld.MapAssembler) { if link.Cid.Defined() { qp.MapEntry(ma, "Hash", qp.Link(cidlink.Link{Cid: link.Cid})) @@ -113,7 +127,6 @@ func (n *ProtoNode) GetPBNode() *pb.PBNode { pbn.Links = make([]*pb.PBLink, len(n.links)) } - sort.Stable(LinkSlice(n.links)) // keep links sorted for i, l := range n.links { pbn.Links[i] = &pb.PBLink{} pbn.Links[i].Name = &l.Name @@ -123,6 +136,11 @@ func (n *ProtoNode) GetPBNode() *pb.PBNode { } } + // Ensure links are sorted prior to encode, regardless of `linksDirty`. They + // may not have come sorted if we deserialized a badly encoded form that + // didn't have links already sorted. + sort.Stable(pbLinkSlice(pbn.Links)) + if len(n.data) > 0 { pbn.Data = n.data } @@ -132,8 +150,13 @@ func (n *ProtoNode) GetPBNode() *pb.PBNode { // EncodeProtobuf returns the encoded raw data version of a Node instance. // It may use a cached encoded version, unless the force flag is given. func (n *ProtoNode) EncodeProtobuf(force bool) ([]byte, error) { - sort.Stable(LinkSlice(n.links)) // keep links sorted - if n.encoded == nil || force { + if n.encoded == nil || n.linksDirty || force { + if n.linksDirty { + // there was a mutation involving links, make sure we sort before we build + // and cache a `Node` form that captures the current state + sort.Stable(LinkSlice(n.links)) + n.linksDirty = false + } n.cached = cid.Undef var err error n.encoded, err = n.marshalImmutable() diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 17a05c6a4..a9a0f9142 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -25,6 +25,7 @@ import ( offline "github.com/ipfs/go-ipfs-exchange-offline" u "github.com/ipfs/go-ipfs-util" ipld "github.com/ipfs/go-ipld-format" + prime "github.com/ipld/go-ipld-prime" ) // makeDepthTestingGraph makes a small DAG with two levels. The level-two @@ -745,6 +746,225 @@ func TestEnumerateAsyncFailsNotFound(t *testing.T) { } } +func TestLinkSorting(t *testing.T) { + az := "az" + aaaa := "aaaa" + bbbb := "bbbb" + cccc := "cccc" + + azBlk := NewRawNode([]byte(az)) + aaaaBlk := NewRawNode([]byte(aaaa)) + bbbbBlk := NewRawNode([]byte(bbbb)) + ccccBlk := NewRawNode([]byte(cccc)) + pbn := &mdpb.PBNode{ + Links: []*mdpb.PBLink{ + {Hash: bbbbBlk.Cid().Bytes(), Name: &bbbb}, + {Hash: azBlk.Cid().Bytes(), Name: &az}, + {Hash: aaaaBlk.Cid().Bytes(), Name: &aaaa}, + {Hash: ccccBlk.Cid().Bytes(), Name: &cccc}, + }, + } + byts, err := pbn.Marshal() + if err != nil { + t.Fatal(err) + } + + mustLookupNodeString := func(t *testing.T, node prime.Node, name string) prime.Node { + subNode, err := node.LookupByString(name) + if err != nil { + t.Fatal(err) + } + return subNode + } + + mustLookupNodeIndex := func(t *testing.T, node prime.Node, idx int64) prime.Node { + subNode, err := node.LookupByIndex(idx) + if err != nil { + t.Fatal(err) + } + return subNode + } + + mustNodeAsString := func(t *testing.T, node prime.Node) string { + str, err := node.AsString() + if err != nil { + t.Fatal(err) + } + return str + } + + verifyUnsortedNode := func(t *testing.T, node *ProtoNode) { + links := node.Links() + if len(links) != 4 { + t.Errorf("wrong number of links, expected 4 but got %d", len(links)) + } + if links[0].Name != bbbb { + t.Errorf("expected link 0 to be 'bbbb', got %s", links[0].Name) + } + if links[1].Name != az { + t.Errorf("expected link 0 to be 'az', got %s", links[1].Name) + } + if links[2].Name != aaaa { + t.Errorf("expected link 0 to be 'aaaa', got %s", links[2].Name) + } + if links[3].Name != cccc { + t.Errorf("expected link 0 to be 'cccc', got %s", links[3].Name) + } + + // check the go-ipld-prime form + linksNode := mustLookupNodeString(t, node, "Links") + if linksNode.Length() != 4 { + t.Errorf("(Node) wrong number of links, expected 4 but got %d", len(links)) + } + if name := mustNodeAsString(t, mustLookupNodeString(t, mustLookupNodeIndex(t, linksNode, 0), "Name")); name != bbbb { + t.Errorf("(Node) expected link 0 to be 'bbbb', got %s", name) + } + if name := mustNodeAsString(t, mustLookupNodeString(t, mustLookupNodeIndex(t, linksNode, 1), "Name")); name != az { + t.Errorf("(Node) expected link 0 to be 'az', got %s", name) + } + if name := mustNodeAsString(t, mustLookupNodeString(t, mustLookupNodeIndex(t, linksNode, 2), "Name")); name != aaaa { + t.Errorf("(Node) expected link 0 to be 'aaaa', got %s", name) + } + if name := mustNodeAsString(t, mustLookupNodeString(t, mustLookupNodeIndex(t, linksNode, 3), "Name")); name != cccc { + t.Errorf("(Node) expected link 0 to be 'cccc', got %s", name) + } + } + + verifySortedNode := func(t *testing.T, node *ProtoNode) { + links := node.Links() + if len(links) != 4 { + t.Errorf("wrong number of links, expected 4 but got %d", len(links)) + } + if links[0].Name != aaaa { + t.Errorf("expected link 0 to be 'aaaa', got %s", links[0].Name) + } + if links[1].Name != az { + t.Errorf("expected link 0 to be 'az', got %s", links[1].Name) + } + if links[2].Name != bbbb { + t.Errorf("expected link 0 to be 'bbbb', got %s", links[2].Name) + } + if links[3].Name != cccc { + t.Errorf("expected link 0 to be 'cccc', got %s", links[3].Name) + } + + // check the go-ipld-prime form + linksNode := mustLookupNodeString(t, node, "Links") + if linksNode.Length() != 4 { + t.Errorf("(Node) wrong number of links, expected 4 but got %d", len(links)) + } + if name := mustNodeAsString(t, mustLookupNodeString(t, mustLookupNodeIndex(t, linksNode, 0), "Name")); name != aaaa { + t.Errorf("(Node) expected link 0 to be 'aaaa', got %s", name) + } + if name := mustNodeAsString(t, mustLookupNodeString(t, mustLookupNodeIndex(t, linksNode, 1), "Name")); name != az { + t.Errorf("(Node) expected link 0 to be 'az', got %s", name) + } + if name := mustNodeAsString(t, mustLookupNodeString(t, mustLookupNodeIndex(t, linksNode, 2), "Name")); name != bbbb { + t.Errorf("(Node) expected link 0 to be 'bbbb', got %s", name) + } + if name := mustNodeAsString(t, mustLookupNodeString(t, mustLookupNodeIndex(t, linksNode, 3), "Name")); name != cccc { + t.Errorf("(Node) expected link 0 to be 'cccc', got %s", name) + } + } + + t.Run("decode", func(t *testing.T) { + node, err := DecodeProtobuf(byts) + if err != nil { + t.Fatal(err) + } + verifyUnsortedNode(t, node) + }) + + t.Run("RawData() should not mutate, should return original form", func(t *testing.T) { + node, err := DecodeProtobuf(byts) + if err != nil { + t.Fatal(err) + } + rawData := node.RawData() + verifyUnsortedNode(t, node) + if !bytes.Equal(rawData, byts) { + t.Error("RawData() did not return original bytes") + } + }) + + t.Run("Size() should not mutate", func(t *testing.T) { + node, err := DecodeProtobuf(byts) + if err != nil { + t.Fatal(err) + } + sz, err := node.Size() + if err != nil { + t.Fatal(err) + } + if sz != 182 { + t.Errorf("expected size to be 182, got %d", sz) + } + verifyUnsortedNode(t, node) + }) + + t.Run("GetPBNode() should not mutate, returned PBNode should be sorted", func(t *testing.T) { + node, err := DecodeProtobuf(byts) + if err != nil { + t.Fatal(err) + } + rtPBNode := node.GetPBNode() + rtByts, err := rtPBNode.Marshal() + if err != nil { + t.Fatal(err) + } + verifyUnsortedNode(t, node) + rtNode, err := DecodeProtobuf(rtByts) + if err != nil { + t.Fatal(err) + } + verifySortedNode(t, rtNode) + }) + + t.Run("add and remove link should mutate", func(t *testing.T) { + node, err := DecodeProtobuf(byts) + if err != nil { + t.Fatal(err) + } + someCid, _ := cid.Cast([]byte{1, 85, 0, 5, 0, 1, 2, 3, 4}) + if err = node.AddRawLink("foo", &ipld.Link{ + Size: 10, + Cid: someCid, + }); err != nil { + t.Fatal(err) + } + if err = node.RemoveNodeLink("foo"); err != nil { + t.Fatal(err) + } + verifySortedNode(t, node) + }) + + t.Run("update link should not mutate, returned ProtoNode should be sorted", func(t *testing.T) { + node, err := DecodeProtobuf(byts) + if err != nil { + t.Fatal(err) + } + newNode, err := node.UpdateNodeLink("self", node) + if err != nil { + t.Fatal(err) + } + if err = newNode.RemoveNodeLink("self"); err != nil { + t.Fatal(err) + } + verifySortedNode(t, newNode) + verifyUnsortedNode(t, node) + }) + + t.Run("SetLinks() should mutate", func(t *testing.T) { + node, err := DecodeProtobuf(byts) + if err != nil { + t.Fatal(err) + } + links := node.Links() // clone + node.SetLinks(links) + verifySortedNode(t, node) + }) +} + func TestProgressIndicator(t *testing.T) { testProgressIndicator(t, 5) } diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index cafd9c39c..a3a719769 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "sort" blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" @@ -41,10 +42,12 @@ type immutableProtoNode struct { // this mutable protonode implementation is needed to support go-unixfs, // the only library that implements both read and write for UnixFS v1. type ProtoNode struct { - links []*format.Link - data []byte + links []*format.Link + linksDirty bool + data []byte - // cache encoded/marshaled value + // cache encoded/marshaled value, kept to make the go-ipld-prime Node interface + // work (see prime.go), and to provide a cached []byte encoded form available encoded *immutableProtoNode cached cid.Cid @@ -115,7 +118,15 @@ func NodeWithData(d []byte) *ProtoNode { return &ProtoNode{data: d} } -// AddNodeLink adds a link to another node. +// AddNodeLink adds a link to another node. The link will be added in +// sorted order. +// +// If sorting has not already been applied to this node (because +// it was deserialized from a form that did not have sorted links), the links +// list will be sorted. If a ProtoNode was deserialized from a badly encoded +// form that did not already have its links sorted, calling AddNodeLink and then +// RemoveNodeLink for the same link, will not result in an identically encoded +// form as the links will have been sorted. func (n *ProtoNode) AddNodeLink(name string, that format.Node) error { lnk, err := format.MakeLink(that) if err != nil { @@ -129,22 +140,30 @@ func (n *ProtoNode) AddNodeLink(name string, that format.Node) error { return nil } -// AddRawLink adds a copy of a link to this node +// AddRawLink adds a copy of a link to this node. The link will be added in +// sorted order. +// +// If sorting has not already been applied to this node (because +// it was deserialized from a form that did not have sorted links), the links +// list will be sorted. If a ProtoNode was deserialized from a badly encoded +// form that did not already have its links sorted, calling AddRawLink and then +// RemoveNodeLink for the same link, will not result in an identically encoded +// form as the links will have been sorted. func (n *ProtoNode) AddRawLink(name string, l *format.Link) error { - n.encoded = nil n.links = append(n.links, &format.Link{ Name: name, Size: l.Size, Cid: l.Cid, }) - + n.linksDirty = true // needs a sort + n.encoded = nil return nil } -// RemoveNodeLink removes a link on this node by the given name. +// RemoveNodeLink removes a link on this node by the given name. If there are +// no links with this name, ErrLinkNotFound will be returned. If there are more +// than one link with this name, they will all be removed. func (n *ProtoNode) RemoveNodeLink(name string) error { - n.encoded = nil - ref := n.links[:0] found := false @@ -161,6 +180,11 @@ func (n *ProtoNode) RemoveNodeLink(name string) error { } n.links = ref + // Even though a removal won't change sorting, this node may have come from + // a deserialized state with badly sorted links. Now that we are mutating, + // we need to ensure the resulting link list is sorted when it gets consumed. + n.linksDirty = true + n.encoded = nil return nil } @@ -204,8 +228,10 @@ func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds format.DAGService, nam return lnk.GetNode(ctx, ds) } -// Copy returns a copy of the node. -// NOTE: Does not make copies of Node objects in the links. +// Copy returns a copy of the node. The resulting node will have a properly +// sorted Links list regardless of whether the original came from a badly +// serialized form that didn't have a sorted list. +// NOTE: This does not make copies of Node objects in the links. func (n *ProtoNode) Copy() format.Node { nnode := new(ProtoNode) if len(n.data) > 0 { @@ -214,8 +240,11 @@ func (n *ProtoNode) Copy() format.Node { } if len(n.links) > 0 { - nnode.links = make([]*format.Link, len(n.links)) - copy(nnode.links, n.links) + nnode.links = append([]*format.Link(nil), n.links...) + // Sort links regardless of linksDirty state, this may have come from a + // serialized form that had badly sorted links, in which case linksDirty + // will not be true. + sort.Stable(LinkSlice(nnode.links)) } nnode.builder = n.builder @@ -244,7 +273,12 @@ func (n *ProtoNode) SetData(d []byte) { } // UpdateNodeLink return a copy of the node with the link name set to point to -// that. If a link of the same name existed, it is removed. +// that. The link will be added in sorted order. If a link of the same name +// existed, it is removed. +// +// If sorting has not already been applied to this node (because +// it was deserialized from a form that did not have sorted links), the links +// list will be sorted in the returned copy. func (n *ProtoNode) UpdateNodeLink(name string, that *ProtoNode) (*ProtoNode, error) { newnode := n.Copy().(*ProtoNode) _ = newnode.RemoveNodeLink(name) // ignore error @@ -309,12 +343,23 @@ func (n *ProtoNode) UnmarshalJSON(b []byte) error { } n.data = s.Data + // Links may not be sorted after deserialization, but we don't change + // them until we mutate this node since we're representing the current, + // as-serialized state. So n.linksDirty is not set here. n.links = s.Links + n.encoded = nil return nil } // MarshalJSON returns a JSON representation of the node. func (n *ProtoNode) MarshalJSON() ([]byte, error) { + if n.linksDirty { + // there was a mutation involving links, make sure we sort + sort.Stable(LinkSlice(n.links)) + n.linksDirty = false + n.encoded = nil + } + out := map[string]interface{}{ "data": n.data, "links": n.links, @@ -358,14 +403,23 @@ func (n *ProtoNode) Multihash() mh.Multihash { return n.cached.Hash() } -// Links returns the node links. +// Links returns a copy of the node's links. func (n *ProtoNode) Links() []*format.Link { - return n.links + if n.linksDirty { + // there was a mutation involving links, make sure we sort + sort.Stable(LinkSlice(n.links)) + n.linksDirty = false + n.encoded = nil + } + return append([]*format.Link(nil), n.links...) } -// SetLinks replaces the node links with the given ones. +// SetLinks replaces the node links with a copy of the provided links. Sorting +// will be applied to the list. func (n *ProtoNode) SetLinks(links []*format.Link) { - n.links = links + n.links = append([]*format.Link(nil), links...) + n.linksDirty = true // needs a sort + n.encoded = nil } // Resolve is an alias for ResolveLink. @@ -397,6 +451,13 @@ func (n *ProtoNode) Tree(p string, depth int) []string { return nil } + if n.linksDirty { + // there was a mutation involving links, make sure we sort + sort.Stable(LinkSlice(n.links)) + n.linksDirty = false + n.encoded = nil + } + out := make([]string, 0, len(n.links)) for _, lnk := range n.links { out = append(out, lnk.Name) From 16fda5637687707b36fa172cf234c5eb925e44db Mon Sep 17 00:00:00 2001 From: web3-bot Date: Wed, 24 Aug 2022 11:46:27 +0000 Subject: [PATCH 3719/3817] run gofmt -s This commit was moved from ipfs/go-merkledag@c2a53ba102346563c810c0abc55be30d76cd4b7d --- ipld/merkledag/merkledag.go | 3 ++- ipld/merkledag/traverse/traverse.go | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ipld/merkledag/merkledag.go b/ipld/merkledag/merkledag.go index 73b06d925..91c6e9844 100644 --- a/ipld/merkledag/merkledag.go +++ b/ipld/merkledag/merkledag.go @@ -46,7 +46,8 @@ func NewDAGService(bs bserv.BlockService) *dagService { // - the root is virtual (like a forest) // - stores nodes' data in a BlockService // TODO: should cache Nodes that are in memory, and be -// able to free some of them when vm pressure is high +// +// able to free some of them when vm pressure is high type dagService struct { Blocks bserv.BlockService } diff --git a/ipld/merkledag/traverse/traverse.go b/ipld/merkledag/traverse/traverse.go index 7cb2dfe7d..dbc426fa9 100644 --- a/ipld/merkledag/traverse/traverse.go +++ b/ipld/merkledag/traverse/traverse.go @@ -93,13 +93,12 @@ type Func func(current State) error // ErrFunc is provided to handle problems when walking to the Node. Traverse // will call ErrFunc with the error encountered. ErrFunc can decide how to // handle that error, and return an error back to Traversal with how to proceed: -// * nil - skip the Node and its children, but continue processing -// * all other errors halt processing immediately. +// - nil - skip the Node and its children, but continue processing +// - all other errors halt processing immediately. // // If ErrFunc is nil, Traversal will stop, as if: // -// opts.ErrFunc = func(err error) { return err } -// +// opts.ErrFunc = func(err error) { return err } type ErrFunc func(err error) error // Traverse initiates a DAG traversal with the given options starting at From c71dcd84e640d73e494820f5d8cefa8a30be1199 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Wed, 24 Aug 2022 14:07:51 +1000 Subject: [PATCH 3720/3817] fix: remove use of ioutil This commit was moved from ipfs/go-merkledag@5d2c09d1fe2edbda5dba999222d8a1cffa1e1ba7 --- ipld/merkledag/merkledag_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index a9a0f9142..abbdbfc3c 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "math/rand" "strings" "sync" @@ -252,7 +251,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { dagr := makeTestDAGReader(t, root, dagservs[0]) - expected, err := ioutil.ReadAll(dagr) + expected, err := io.ReadAll(dagr) if err != nil { t.Fatal(err) } @@ -284,7 +283,7 @@ func runBatchFetchTest(t *testing.T, read io.Reader) { errs <- ErrNotProtobuf } read := makeTestDAGReader(t, firstpb, dagservs[i]) - datagot, err := ioutil.ReadAll(read) + datagot, err := io.ReadAll(read) if err != nil { errs <- err } From ae1afa3948e92816008fc20219bbf72e65671b5e Mon Sep 17 00:00:00 2001 From: web3-bot Date: Wed, 24 Aug 2022 11:47:30 +0000 Subject: [PATCH 3721/3817] bump go.mod to Go 1.18 and run go fix This commit was moved from ipld/go-car@a18b68d3ea04856a079e7b1cd06abe59224beecb --- ipld/car/fuzz_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ipld/car/fuzz_test.go b/ipld/car/fuzz_test.go index 8ac04bbd0..cf68d946e 100644 --- a/ipld/car/fuzz_test.go +++ b/ipld/car/fuzz_test.go @@ -1,5 +1,4 @@ //go:build go1.18 -// +build go1.18 package car_test From 0301bfb2eb528840bf6a56cd49c98c0ffd185e7d Mon Sep 17 00:00:00 2001 From: web3-bot Date: Wed, 24 Aug 2022 11:47:33 +0000 Subject: [PATCH 3722/3817] bump go.mod to Go 1.18 and run go fix This commit was moved from ipld/go-car@9e3e435a838b6e91968b6bb6810d108aa8147951 --- ipld/car/v2/fuzz_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ipld/car/v2/fuzz_test.go b/ipld/car/v2/fuzz_test.go index c45d83cb0..32c548547 100644 --- a/ipld/car/v2/fuzz_test.go +++ b/ipld/car/v2/fuzz_test.go @@ -1,5 +1,4 @@ //go:build go1.18 -// +build go1.18 package car_test From 354d1c8624a3bcc245aaa5f7a491a31a2d20efd8 Mon Sep 17 00:00:00 2001 From: web3-bot Date: Wed, 24 Aug 2022 11:47:37 +0000 Subject: [PATCH 3723/3817] run gofmt -s This commit was moved from ipld/go-car@ca1482a654f791455f397bc1a052c2dfbbf69d55 --- ipld/car/v2/blockstore/doc.go | 8 ++--- ipld/car/v2/index/doc.go | 1 - ipld/car/v2/internal/carv1/car.go | 5 +-- ipld/car/v2/internal/loader/writing_loader.go | 4 ++- ipld/car/v2/reader.go | 36 +++++++++---------- 5 files changed, 28 insertions(+), 26 deletions(-) diff --git a/ipld/car/v2/blockstore/doc.go b/ipld/car/v2/blockstore/doc.go index 479442e12..4aa4cfdfc 100644 --- a/ipld/car/v2/blockstore/doc.go +++ b/ipld/car/v2/blockstore/doc.go @@ -3,10 +3,10 @@ // // The ReadOnly blockstore provides a read-only random access from a given data payload either in // unindexed CARv1 format or indexed/unindexed v2 format: -// * ReadOnly.NewReadOnly can be used to instantiate a new read-only blockstore for a given CARv1 -// or CARv2 data payload with an optional index override. -// * ReadOnly.OpenReadOnly can be used to instantiate a new read-only blockstore for a given CARv1 -// or CARv2 file with automatic index generation if the index is not present. +// - ReadOnly.NewReadOnly can be used to instantiate a new read-only blockstore for a given CARv1 +// or CARv2 data payload with an optional index override. +// - ReadOnly.OpenReadOnly can be used to instantiate a new read-only blockstore for a given CARv1 +// or CARv2 file with automatic index generation if the index is not present. // // The ReadWrite blockstore allows writing and reading of the blocks concurrently. The user of this // blockstore is responsible for calling ReadWrite.Finalize when finished writing blocks. diff --git a/ipld/car/v2/index/doc.go b/ipld/car/v2/index/doc.go index 41b860216..b8062cc18 100644 --- a/ipld/car/v2/index/doc.go +++ b/ipld/car/v2/index/doc.go @@ -3,5 +3,4 @@ // // Index can be written or read using the following static functions: index.WriteTo and // index.ReadFrom. -// package index diff --git a/ipld/car/v2/internal/carv1/car.go b/ipld/car/v2/internal/carv1/car.go index f62899b71..8ad012aa6 100644 --- a/ipld/car/v2/internal/carv1/car.go +++ b/ipld/car/v2/internal/carv1/car.go @@ -229,8 +229,9 @@ func loadCarSlow(ctx context.Context, s Store, cr *CarReader) (*CarHeader, error // Matches checks whether two headers match. // Two headers are considered matching if: -// 1. They have the same version number, and -// 2. They contain the same root CIDs in any order. +// 1. They have the same version number, and +// 2. They contain the same root CIDs in any order. +// // Note, this function explicitly ignores the order of roots. // If order of roots matter use reflect.DeepEqual instead. func (h CarHeader) Matches(other CarHeader) bool { diff --git a/ipld/car/v2/internal/loader/writing_loader.go b/ipld/car/v2/internal/loader/writing_loader.go index b4d0e6efe..0bf2ae3c8 100644 --- a/ipld/car/v2/internal/loader/writing_loader.go +++ b/ipld/car/v2/internal/loader/writing_loader.go @@ -87,7 +87,9 @@ func (w *writingReader) Read(p []byte) (int, error) { // that block is also written as a CAR block to the provided io.Writer. Metadata // (the size of data written) is provided in the second return value. // The `initialOffset` is used to calculate the offsets recorded for the index, and will be -// included in the `.Size()` of the IndexTracker. +// +// included in the `.Size()` of the IndexTracker. +// // An indexCodec of `index.CarIndexNoIndex` can be used to not track these offsets. func TeeingLinkSystem(ls ipld.LinkSystem, w io.Writer, initialOffset uint64, indexCodec multicodec.Code) (ipld.LinkSystem, IndexTracker) { wo := writerOutput{ diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index f29469a02..ca6cad1e9 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -170,29 +170,29 @@ type Stats struct { // Beyond the checks performed by Inspect, a valid / good CAR is somewhat // use-case dependent. Factors to consider include: // -// * Bad indexes, including incorrect offsets, duplicate entries, or other -// faulty data. Indexes should be re-generated, regardless, if you need to use -// them and have any reason to not trust the source. +// - Bad indexes, including incorrect offsets, duplicate entries, or other +// faulty data. Indexes should be re-generated, regardless, if you need to use +// them and have any reason to not trust the source. // -// * Blocks use codecs that your system doesn't have access to—which may mean -// you can't traverse a DAG or use the contained data. Stats.CodecCounts -// contains a list of codecs found in the CAR so this can be checked. +// - Blocks use codecs that your system doesn't have access to—which may mean +// you can't traverse a DAG or use the contained data. Stats.CodecCounts +// contains a list of codecs found in the CAR so this can be checked. // -// * CIDs use multihashes that your system doesn't have access to—which will -// mean you can't validate block hashes are correct (using validateBlockHash -// in this case will result in a failure). Stats.MhTypeCounts contains a -// list of multihashes found in the CAR so this can be checked. +// - CIDs use multihashes that your system doesn't have access to—which will +// mean you can't validate block hashes are correct (using validateBlockHash +// in this case will result in a failure). Stats.MhTypeCounts contains a +// list of multihashes found in the CAR so this can be checked. // -// * The presence of IDENTITY CIDs, which may not be supported (or desired) by -// the consumer of the CAR. Stats.CodecCounts can determine the presence -// of IDENTITY CIDs. +// - The presence of IDENTITY CIDs, which may not be supported (or desired) by +// the consumer of the CAR. Stats.CodecCounts can determine the presence +// of IDENTITY CIDs. // -// * Roots: the number of roots, duplicates, and whether they are related to the -// blocks contained within the CAR. Stats contains a list of Roots and a -// RootsPresent bool so further checks can be performed. +// - Roots: the number of roots, duplicates, and whether they are related to the +// blocks contained within the CAR. Stats contains a list of Roots and a +// RootsPresent bool so further checks can be performed. // -// * DAG completeness is not checked. Any properties relating to the DAG, or -// DAGs contained within a CAR are the responsibility of the user to check. +// - DAG completeness is not checked. Any properties relating to the DAG, or +// DAGs contained within a CAR are the responsibility of the user to check. func (r *Reader) Inspect(validateBlockHash bool) (Stats, error) { stats := Stats{ Version: r.Version, From 444973fd849ff7c209afc72c8980de80a4531c70 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Wed, 24 Aug 2022 13:58:52 +1000 Subject: [PATCH 3724/3817] fix: remove use of ioutil This commit was moved from ipld/go-car@9be1c2e7e07a8ba3763201044b33124cddd1d5fb --- ipld/car/v2/blockstore/example_test.go | 3 +-- ipld/car/v2/blockstore/readonly_test.go | 3 +-- ipld/car/v2/blockstore/readwrite_test.go | 9 ++++----- ipld/car/v2/example_test.go | 7 +++---- ipld/car/v2/index/example_test.go | 3 +-- ipld/car/v2/internal/io/converter.go | 5 ++--- ipld/car/v2/selective.go | 3 +-- ipld/car/v2/writer_test.go | 11 +++++------ 8 files changed, 18 insertions(+), 26 deletions(-) diff --git a/ipld/car/v2/blockstore/example_test.go b/ipld/car/v2/blockstore/example_test.go index 198443c9d..ec16383eb 100644 --- a/ipld/car/v2/blockstore/example_test.go +++ b/ipld/car/v2/blockstore/example_test.go @@ -3,7 +3,6 @@ package blockstore_test import ( "context" "fmt" - "io/ioutil" "os" "path/filepath" "time" @@ -86,7 +85,7 @@ func ExampleOpenReadWrite() { thatBlock := merkledag.NewRawNode([]byte("lobster")).Block andTheOtherBlock := merkledag.NewRawNode([]byte("barreleye")).Block - tdir, err := ioutil.TempDir(os.TempDir(), "example-*") + tdir, err := os.MkdirTemp(os.TempDir(), "example-*") if err != nil { panic(err) } diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 5a947a75b..a799d328f 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "io" - "io/ioutil" "os" "testing" "time" @@ -277,7 +276,7 @@ func TestReadOnlyErrorAfterClose(t *testing.T) { } func TestNewReadOnly_CarV1WithoutIndexWorksAsExpected(t *testing.T) { - carV1Bytes, err := ioutil.ReadFile("../testdata/sample-v1.car") + carV1Bytes, err := os.ReadFile("../testdata/sample-v1.car") require.NoError(t, err) reader := bytes.NewReader(carV1Bytes) diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index bfa4e23c1..4553fd15f 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -5,7 +5,6 @@ import ( "crypto/sha512" "fmt" "io" - "io/ioutil" "math/rand" "os" "path" @@ -360,7 +359,7 @@ func TestBlockstoreNullPadding(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - paddedV1, err := ioutil.ReadFile("../testdata/sample-v1-with-zero-len-section.car") + paddedV1, err := os.ReadFile("../testdata/sample-v1-with-zero-len-section.car") require.NoError(t, err) rbs, err := blockstore.NewReadOnly(bufferReaderAt(paddedV1), nil, @@ -665,7 +664,7 @@ func TestReadWriteResumptionFromNonV2FileIsError(t *testing.T) { func TestReadWriteResumptionMismatchingRootsIsError(t *testing.T) { tmpPath := requireTmpCopy(t, "../testdata/sample-wrapped-v2.car") - origContent, err := ioutil.ReadFile(tmpPath) + origContent, err := os.ReadFile(tmpPath) require.NoError(t, err) badRoot, err := cid.NewPrefixV1(cid.Raw, multihash.SHA2_256).Sum([]byte("bad root")) @@ -675,7 +674,7 @@ func TestReadWriteResumptionMismatchingRootsIsError(t *testing.T) { require.EqualError(t, err, "cannot resume on file with mismatching data header") require.Nil(t, subject) - newContent, err := ioutil.ReadFile(tmpPath) + newContent, err := os.ReadFile(tmpPath) require.NoError(t, err) // Expect the bad file to be left untouched; check the size first. @@ -950,7 +949,7 @@ func TestReadWriteOpenFile(t *testing.T) { defer cancel() dir := t.TempDir() // auto cleanup - f, err := ioutil.TempFile(dir, "") + f, err := os.CreateTemp(dir, "") require.NoError(t, err) root := blocks.NewBlock([]byte("foo")) diff --git a/ipld/car/v2/example_test.go b/ipld/car/v2/example_test.go index 57378aeaa..532f87d6a 100644 --- a/ipld/car/v2/example_test.go +++ b/ipld/car/v2/example_test.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "os" "path/filepath" @@ -19,7 +18,7 @@ func ExampleWrapV1File() { // Writing the result to testdata allows reusing that file in other tests, // and also helps ensure that the result is deterministic. src := "testdata/sample-v1.car" - tdir, err := ioutil.TempDir(os.TempDir(), "example-*") + tdir, err := os.MkdirTemp(os.TempDir(), "example-*") if err != nil { panic(err) } @@ -47,7 +46,7 @@ func ExampleWrapV1File() { fmt.Println("Has index:", cr.Header.HasIndex()) // Verify that the CARv1 remains exactly the same. - orig, err := ioutil.ReadFile(src) + orig, err := os.ReadFile(src) if err != nil { panic(err) } @@ -55,7 +54,7 @@ func ExampleWrapV1File() { if err != nil { panic(err) } - inner, err := ioutil.ReadAll(dr) + inner, err := io.ReadAll(dr) if err != nil { panic(err) } diff --git a/ipld/car/v2/index/example_test.go b/ipld/car/v2/index/example_test.go index d2a9da54b..2b9cca7c6 100644 --- a/ipld/car/v2/index/example_test.go +++ b/ipld/car/v2/index/example_test.go @@ -3,7 +3,6 @@ package index_test import ( "fmt" "io" - "io/ioutil" "os" "reflect" @@ -76,7 +75,7 @@ func ExampleWriteTo() { } // Store the index alone onto destination file. - f, err := ioutil.TempFile(os.TempDir(), "example-index-*.carindex") + f, err := os.CreateTemp(os.TempDir(), "example-index-*.carindex") if err != nil { panic(err) } diff --git a/ipld/car/v2/internal/io/converter.go b/ipld/car/v2/internal/io/converter.go index 21011b6ec..2b29d0a88 100644 --- a/ipld/car/v2/internal/io/converter.go +++ b/ipld/car/v2/internal/io/converter.go @@ -2,7 +2,6 @@ package io import ( "io" - "io/ioutil" "sync" ) @@ -97,10 +96,10 @@ func (drsb *discardingReadSeekerPlusByte) Seek(offset int64, whence int) (int64, if n < 0 { panic("unsupported rewind via whence: io.SeekStart") } - _, err := io.CopyN(ioutil.Discard, drsb, n) + _, err := io.CopyN(io.Discard, drsb, n) return drsb.offset, err case io.SeekCurrent: - _, err := io.CopyN(ioutil.Discard, drsb, offset) + _, err := io.CopyN(io.Discard, drsb, offset) return drsb.offset, err default: panic("unsupported whence: io.SeekEnd") diff --git a/ipld/car/v2/selective.go b/ipld/car/v2/selective.go index 39bb5f911..5aaac5594 100644 --- a/ipld/car/v2/selective.go +++ b/ipld/car/v2/selective.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "math" "os" @@ -276,7 +275,7 @@ func traverse(ctx context.Context, ls *ipld.LinkSystem, root cid.Cid, s ipld.Nod if err != nil { return err } - _, err = io.Copy(ioutil.Discard, s) + _, err = io.Copy(io.Discard, s) if err != nil { return err } diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index 12dcd6a9c..a38ef34ad 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -3,7 +3,6 @@ package car import ( "context" "io" - "io/ioutil" "os" "path/filepath" "testing" @@ -45,11 +44,11 @@ func TestWrapV1(t *testing.T) { // Assert CARv1 data payloads are identical. _, err = sf.Seek(0, io.SeekStart) require.NoError(t, err) - wantPayload, err := ioutil.ReadAll(sf) + wantPayload, err := io.ReadAll(sf) require.NoError(t, err) dr, err := subject.DataReader() require.NoError(t, err) - gotPayload, err := ioutil.ReadAll(dr) + gotPayload, err := io.ReadAll(dr) require.NoError(t, err) require.Equal(t, wantPayload, gotPayload) @@ -73,7 +72,7 @@ func TestExtractV1(t *testing.T) { require.NoError(t, carv1.WriteCar(context.Background(), dagSvc, generateRootCid(t, dagSvc), v1f)) _, err = v1f.Seek(0, io.SeekStart) require.NoError(t, err) - wantV1, err := ioutil.ReadAll(v1f) + wantV1, err := io.ReadAll(v1f) require.NoError(t, err) // Wrap the produced CARv1 into a CARv2 to use for testing. @@ -83,13 +82,13 @@ func TestExtractV1(t *testing.T) { // Assert extract from CARv2 file is as expected. dstPath := filepath.Join(t.TempDir(), "extract-file-test-v1.car") require.NoError(t, ExtractV1File(v2path, dstPath)) - gotFromFile, err := ioutil.ReadFile(dstPath) + gotFromFile, err := os.ReadFile(dstPath) require.NoError(t, err) require.Equal(t, wantV1, gotFromFile) // Assert extract from CARv2 file in-place is as expected require.NoError(t, ExtractV1File(v2path, v2path)) - gotFromInPlaceFile, err := ioutil.ReadFile(v2path) + gotFromInPlaceFile, err := os.ReadFile(v2path) require.NoError(t, err) require.Equal(t, wantV1, gotFromInPlaceFile) } From ea6ac8cb6aa02f6cd88cb141f3f077e206277829 Mon Sep 17 00:00:00 2001 From: web3-bot <81333946+web3-bot@users.noreply.github.com> Date: Thu, 25 Aug 2022 15:07:58 +0200 Subject: [PATCH 3725/3817] sync: update CI config files (#87) This commit was moved from ipfs/interface-go-ipfs-core@de3410bbe2bbdbf50e090a06eda0f741cad5381c --- coreiface/block.go | 3 ++- coreiface/dht.go | 1 + coreiface/key.go | 1 + coreiface/name.go | 1 + coreiface/object.go | 3 ++- coreiface/options/key.go | 2 +- coreiface/options/name.go | 1 - coreiface/options/pin.go | 40 +++++++++++++++++++-------------------- coreiface/path/path.go | 2 +- coreiface/pin.go | 1 + coreiface/tests/block.go | 5 ++--- coreiface/tests/dag.go | 3 ++- coreiface/tests/object.go | 10 +++++----- coreiface/tests/pin.go | 2 +- coreiface/tests/pubsub.go | 2 +- coreiface/tests/unixfs.go | 9 ++++----- coreiface/unixfs.go | 3 ++- 17 files changed, 47 insertions(+), 42 deletions(-) diff --git a/coreiface/block.go b/coreiface/block.go index b105b079d..49ffe75d7 100644 --- a/coreiface/block.go +++ b/coreiface/block.go @@ -2,9 +2,10 @@ package iface import ( "context" - path "github.com/ipfs/interface-go-ipfs-core/path" "io" + path "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/interface-go-ipfs-core/options" ) diff --git a/coreiface/dht.go b/coreiface/dht.go index 5f49e74a3..81a20ee2b 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -2,6 +2,7 @@ package iface import ( "context" + path "github.com/ipfs/interface-go-ipfs-core/path" "github.com/ipfs/interface-go-ipfs-core/options" diff --git a/coreiface/key.go b/coreiface/key.go index db729b3b4..967255665 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -2,6 +2,7 @@ package iface import ( "context" + path "github.com/ipfs/interface-go-ipfs-core/path" "github.com/ipfs/interface-go-ipfs-core/options" diff --git a/coreiface/name.go b/coreiface/name.go index 3dc9f6878..d2725e028 100644 --- a/coreiface/name.go +++ b/coreiface/name.go @@ -3,6 +3,7 @@ package iface import ( "context" "errors" + path "github.com/ipfs/interface-go-ipfs-core/path" "github.com/ipfs/interface-go-ipfs-core/options" diff --git a/coreiface/object.go b/coreiface/object.go index 86536d421..733dc2bee 100644 --- a/coreiface/object.go +++ b/coreiface/object.go @@ -2,9 +2,10 @@ package iface import ( "context" - path "github.com/ipfs/interface-go-ipfs-core/path" "io" + path "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/interface-go-ipfs-core/options" "github.com/ipfs/go-cid" diff --git a/coreiface/options/key.go b/coreiface/options/key.go index 80beea352..4bc53a65f 100644 --- a/coreiface/options/key.go +++ b/coreiface/options/key.go @@ -69,7 +69,7 @@ func (keyOpts) Type(algorithm string) KeyGenerateOption { // generated. Default is -1 // // value of -1 means 'use default size for key type': -// * 2048 for RSA +// - 2048 for RSA func (keyOpts) Size(size int) KeyGenerateOption { return func(settings *KeyGenerateSettings) error { settings.Size = size diff --git a/coreiface/options/name.go b/coreiface/options/name.go index 59aaf2ca3..aa8082863 100644 --- a/coreiface/options/name.go +++ b/coreiface/options/name.go @@ -113,7 +113,6 @@ func (nameOpts) Cache(cache bool) NameResolveOption { } } -// func (nameOpts) ResolveOption(opt ropts.ResolveOpt) NameResolveOption { return func(settings *NameResolveSettings) error { settings.ResolveOpts = append(settings.ResolveOpts, opt) diff --git a/coreiface/options/pin.go b/coreiface/options/pin.go index 5014a2d2b..75c2b8a26 100644 --- a/coreiface/options/pin.go +++ b/coreiface/options/pin.go @@ -164,11 +164,11 @@ func (pinLsOpts) Indirect() PinLsOption { // type. // // Supported values: -// * "direct" - directly pinned objects -// * "recursive" - roots of recursive pins -// * "indirect" - indirectly pinned objects (referenced by recursively pinned -// objects) -// * "all" - all pinned objects (default) +// - "direct" - directly pinned objects +// - "recursive" - roots of recursive pins +// - "indirect" - indirectly pinned objects (referenced by recursively pinned +// objects) +// - "all" - all pinned objects (default) func (pinLsOpts) Type(typeStr string) (PinLsOption, error) { switch typeStr { case "all", "direct", "indirect", "recursive": @@ -182,11 +182,11 @@ func (pinLsOpts) Type(typeStr string) (PinLsOption, error) { // be returned // // Supported values: -// * "direct" - directly pinned objects -// * "recursive" - roots of recursive pins -// * "indirect" - indirectly pinned objects (referenced by recursively pinned -// objects) -// * "all" - all pinned objects (default) +// - "direct" - directly pinned objects +// - "recursive" - roots of recursive pins +// - "indirect" - indirectly pinned objects (referenced by recursively pinned +// objects) +// - "all" - all pinned objects (default) func (pinLsOpts) pinType(t string) PinLsOption { return func(settings *PinLsSettings) error { settings.Type = t @@ -224,11 +224,11 @@ func (pinIsPinnedOpts) Indirect() PinIsPinnedOption { // type. // // Supported values: -// * "direct" - directly pinned objects -// * "recursive" - roots of recursive pins -// * "indirect" - indirectly pinned objects (referenced by recursively pinned -// objects) -// * "all" - all pinned objects (default) +// - "direct" - directly pinned objects +// - "recursive" - roots of recursive pins +// - "indirect" - indirectly pinned objects (referenced by recursively pinned +// objects) +// - "all" - all pinned objects (default) func (pinIsPinnedOpts) Type(typeStr string) (PinIsPinnedOption, error) { switch typeStr { case "all", "direct", "indirect", "recursive": @@ -242,11 +242,11 @@ func (pinIsPinnedOpts) Type(typeStr string) (PinIsPinnedOption, error) { // pin is expected to be, speeding up the research. // // Supported values: -// * "direct" - directly pinned objects -// * "recursive" - roots of recursive pins -// * "indirect" - indirectly pinned objects (referenced by recursively pinned -// objects) -// * "all" - all pinned objects (default) +// - "direct" - directly pinned objects +// - "recursive" - roots of recursive pins +// - "indirect" - indirectly pinned objects (referenced by recursively pinned +// objects) +// - "all" - all pinned objects (default) func (pinIsPinnedOpts) pinType(t string) PinIsPinnedOption { return func(settings *PinIsPinnedSettings) error { settings.WithType = t diff --git a/coreiface/path/path.go b/coreiface/path/path.go index 01b1673b1..e2562936d 100644 --- a/coreiface/path/path.go +++ b/coreiface/path/path.go @@ -15,7 +15,7 @@ import ( // * /ipfs - Immutable unixfs path (files) // * /ipld - Immutable ipld path (data) // * /ipns - Mutable names. Usually resolves to one of the immutable paths -//TODO: /local (MFS) +// TODO: /local (MFS) type Path interface { // String returns the path as a string. String() string diff --git a/coreiface/pin.go b/coreiface/pin.go index 4c1788c68..6205a9b20 100644 --- a/coreiface/pin.go +++ b/coreiface/pin.go @@ -2,6 +2,7 @@ package iface import ( "context" + path "github.com/ipfs/interface-go-ipfs-core/path" "github.com/ipfs/interface-go-ipfs-core/options" diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go index 916e52dd3..a81969916 100644 --- a/coreiface/tests/block.go +++ b/coreiface/tests/block.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "io" - "io/ioutil" "strings" "testing" @@ -211,7 +210,7 @@ func (tp *TestSuite) TestBlockGet(t *testing.T) { t.Fatal(err) } - d, err := ioutil.ReadAll(r) + d, err := io.ReadAll(r) if err != nil { t.Fatal(err) } @@ -249,7 +248,7 @@ func (tp *TestSuite) TestBlockRm(t *testing.T) { t.Fatal(err) } - d, err := ioutil.ReadAll(r) + d, err := io.ReadAll(r) if err != nil { t.Fatal(err) } diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go index 6f9d9659e..5ea0d3eb1 100644 --- a/coreiface/tests/dag.go +++ b/coreiface/tests/dag.go @@ -2,12 +2,13 @@ package tests import ( "context" - path "github.com/ipfs/interface-go-ipfs-core/path" "math" gopath "path" "strings" "testing" + path "github.com/ipfs/interface-go-ipfs-core/path" + coreiface "github.com/ipfs/interface-go-ipfs-core" ipldcbor "github.com/ipfs/go-ipld-cbor" diff --git a/coreiface/tests/object.go b/coreiface/tests/object.go index e8ab1a7f4..c3437853c 100644 --- a/coreiface/tests/object.go +++ b/coreiface/tests/object.go @@ -4,11 +4,11 @@ import ( "bytes" "context" "encoding/hex" - "io/ioutil" + "io" "strings" "testing" - "github.com/ipfs/interface-go-ipfs-core" + iface "github.com/ipfs/interface-go-ipfs-core" opt "github.com/ipfs/interface-go-ipfs-core/options" ) @@ -143,7 +143,7 @@ func (tp *TestSuite) TestObjectData(t *testing.T) { t.Fatal(err) } - data, err := ioutil.ReadAll(r) + data, err := io.ReadAll(r) if err != nil { t.Fatal(err) } @@ -383,7 +383,7 @@ func (tp *TestSuite) TestObjectAddData(t *testing.T) { t.Fatal(err) } - data, err := ioutil.ReadAll(r) + data, err := io.ReadAll(r) if err != nil { t.Fatal(err) } @@ -416,7 +416,7 @@ func (tp *TestSuite) TestObjectSetData(t *testing.T) { t.Fatal(err) } - data, err := ioutil.ReadAll(r) + data, err := io.ReadAll(r) if err != nil { t.Fatal(err) } diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go index d378d1015..ad1a0fdd2 100644 --- a/coreiface/tests/pin.go +++ b/coreiface/tests/pin.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/ipfs/interface-go-ipfs-core" + iface "github.com/ipfs/interface-go-ipfs-core" opt "github.com/ipfs/interface-go-ipfs-core/options" "github.com/ipfs/interface-go-ipfs-core/path" diff --git a/coreiface/tests/pubsub.go b/coreiface/tests/pubsub.go index f8339f228..18da2103d 100644 --- a/coreiface/tests/pubsub.go +++ b/coreiface/tests/pubsub.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/ipfs/interface-go-ipfs-core" + iface "github.com/ipfs/interface-go-ipfs-core" "github.com/ipfs/interface-go-ipfs-core/options" ) diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index f47d34d0a..05226dbbf 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -6,7 +6,6 @@ import ( "encoding/hex" "fmt" "io" - "io/ioutil" "math" "math/rand" "os" @@ -113,7 +112,7 @@ func (tp *TestSuite) TestAdd(t *testing.T) { return path.IpfsPath(c) } - rf, err := ioutil.TempFile(os.TempDir(), "unixfs-add-real") + rf, err := os.CreateTemp(os.TempDir(), "unixfs-add-real") if err != nil { t.Fatal(err) } @@ -134,7 +133,7 @@ func (tp *TestSuite) TestAdd(t *testing.T) { defer os.Remove(rfp) realFile := func() files.Node { - n, err := files.NewReaderPathFile(rfp, ioutil.NopCloser(strings.NewReader(helloStr)), stat) + n, err := files.NewReaderPathFile(rfp, io.NopCloser(strings.NewReader(helloStr)), stat) if err != nil { t.Fatal(err) } @@ -474,12 +473,12 @@ func (tp *TestSuite) TestAdd(t *testing.T) { defer orig.Close() defer got.Close() - do, err := ioutil.ReadAll(orig.(files.File)) + do, err := io.ReadAll(orig.(files.File)) if err != nil { t.Fatal(err) } - dg, err := ioutil.ReadAll(got.(files.File)) + dg, err := io.ReadAll(got.(files.File)) if err != nil { t.Fatal(err) } diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index 686c40298..c398b6722 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -2,11 +2,12 @@ package iface import ( "context" + "github.com/ipfs/interface-go-ipfs-core/options" path "github.com/ipfs/interface-go-ipfs-core/path" "github.com/ipfs/go-cid" - "github.com/ipfs/go-ipfs-files" + files "github.com/ipfs/go-ipfs-files" ) type AddEvent struct { From c5d4aa055b7b5f76a69644436e150593fa2842df Mon Sep 17 00:00:00 2001 From: web3-bot <81333946+web3-bot@users.noreply.github.com> Date: Fri, 26 Aug 2022 12:34:06 +0200 Subject: [PATCH 3726/3817] sync: update CI config files (#125) This commit was moved from ipfs/go-unixfs@2c23c3ea6fae3ef1b487cfc0c606a4ffc7893676 --- unixfs/hamt/hamt.go | 16 +- unixfs/importer/balanced/balanced_test.go | 11 +- unixfs/importer/balanced/builder.go | 176 +++++++++++----------- unixfs/importer/helpers/helpers.go | 19 +-- unixfs/importer/importer_test.go | 7 +- unixfs/importer/trickle/trickle_test.go | 25 ++- unixfs/io/completehamt_test.go | 22 +-- unixfs/io/dagreader_test.go | 5 +- unixfs/mod/dagmodifier_test.go | 15 +- unixfs/pb/unixfs.pb.go | 3 +- unixfs/test/utils.go | 3 +- unixfs/unixfs.go | 6 +- 12 files changed, 155 insertions(+), 153 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index 593b64627..d9947770f 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -9,8 +9,10 @@ // wikipedia article is the collapsing of empty shards. // Given the following tree: ( '[' = shards, '{' = values ) // [ 'A' ] -> [ 'B' ] -> { "ABC" } -// | L-> { "ABD" } -// L-> { "ASDF" } +// +// | L-> { "ABD" } +// L-> { "ASDF" } +// // If we simply removed "ABC", we would end up with a tree where shard 'B' only // has a single child. This causes two issues, the first, is that now we have // an extra lookup required to get to "ABD". The second issue is that now we @@ -460,11 +462,11 @@ func (ds *Shard) walkChildren(processLinkValues func(formattedLink *ipld.Link) e // parallelShardWalk is quite similar to the DAG walking algorithm from https://github.com/ipfs/go-merkledag/blob/594e515f162e764183243b72c2ba84f743424c8c/merkledag.go#L464 // However, there are a few notable differences: -// 1. Some children are actualized Shard structs and some are in the blockstore, this will leverage walking over the in memory Shards as well as the stored blocks -// 2. Instead of just passing each child into the worker pool by itself we group them so that we can leverage optimizations from GetMany. -// This optimization also makes the walk a little more biased towards depth (as opposed to BFS) in the earlier part of the DAG. -// This is particularly helpful for operations like estimating the directory size which should complete quickly when possible. -// 3. None of the extra options from that package are needed +// 1. Some children are actualized Shard structs and some are in the blockstore, this will leverage walking over the in memory Shards as well as the stored blocks +// 2. Instead of just passing each child into the worker pool by itself we group them so that we can leverage optimizations from GetMany. +// This optimization also makes the walk a little more biased towards depth (as opposed to BFS) in the earlier part of the DAG. +// This is particularly helpful for operations like estimating the directory size which should complete quickly when possible. +// 3. None of the extra options from that package are needed func parallelShardWalk(ctx context.Context, root *Shard, dserv ipld.DAGService, processShardValues func(formattedLink *ipld.Link) error) error { const concurrency = 32 diff --git a/unixfs/importer/balanced/balanced_test.go b/unixfs/importer/balanced/balanced_test.go index b2069e3a9..bfb348ed9 100644 --- a/unixfs/importer/balanced/balanced_test.go +++ b/unixfs/importer/balanced/balanced_test.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "io" - "io/ioutil" mrand "math/rand" "testing" @@ -53,7 +52,7 @@ func getTestDag(t *testing.T, ds ipld.DAGService, size int64, blksize int64) (*d return nd, data } -//Test where calls to read are smaller than the chunk size +// Test where calls to read are smaller than the chunk size func TestSizeBasedSplit(t *testing.T) { if testing.Short() { t.SkipNow() @@ -119,7 +118,7 @@ func arrComp(a, b []byte) error { } func dagrArrComp(t *testing.T, r io.Reader, should []byte) { - out, err := ioutil.ReadAll(r) + out, err := io.ReadAll(r) if err != nil { t.Fatal(err) } @@ -138,7 +137,7 @@ func TestIndirectBlocks(t *testing.T) { t.Fatal(err) } - out, err := ioutil.ReadAll(reader) + out, err := io.ReadAll(reader) if err != nil { t.Fatal(err) } @@ -179,7 +178,7 @@ func TestSeekToBegin(t *testing.T) { t.Fatal(err) } - n, err := io.CopyN(ioutil.Discard, rs, 1024*4) + n, err := io.CopyN(io.Discard, rs, 1024*4) if err != nil { t.Fatal(err) } @@ -207,7 +206,7 @@ func TestSeekToAlmostBegin(t *testing.T) { t.Fatal(err) } - n, err := io.CopyN(ioutil.Discard, rs, 1024*4) + n, err := io.CopyN(io.Discard, rs, 1024*4) if err != nil { t.Fatal(err) } diff --git a/unixfs/importer/balanced/builder.go b/unixfs/importer/balanced/builder.go index 407117dad..3379e9765 100644 --- a/unixfs/importer/balanced/builder.go +++ b/unixfs/importer/balanced/builder.go @@ -17,36 +17,37 @@ // that the UnixFS node. // // Notes: -// 1. In the implementation. `FSNodeOverDag` structure is used for representing -// the UnixFS node encoded inside the DAG node. -// (see https://github.com/ipfs/go-ipfs/pull/5118.) -// 2. `TFile` is used for backwards-compatibility. It was a bug causing the leaf -// nodes to be generated with this type instead of `TRaw`. The former one -// should be used (like the trickle builder does). -// (See https://github.com/ipfs/go-ipfs/pull/5120.) // -// +-------------+ -// | Root 4 | -// +-------------+ -// | -// +--------------------------+----------------------------+ -// | | -// +-------------+ +-------------+ -// | Node 2 | | Node 5 | -// +-------------+ +-------------+ -// | | -// +-------------+-------------+ +-------------+ -// | | | -// +-------------+ +-------------+ +-------------+ -// | Node 1 | | Node 3 | | Node 6 | -// +-------------+ +-------------+ +-------------+ -// | | | -// +------+------+ +------+------+ +------+ -// | | | | | -// +=========+ +=========+ +=========+ +=========+ +=========+ -// | Chunk 1 | | Chunk 2 | | Chunk 3 | | Chunk 4 | | Chunk 5 | -// +=========+ +=========+ +=========+ +=========+ +=========+ +// 1. In the implementation. `FSNodeOverDag` structure is used for representing +// the UnixFS node encoded inside the DAG node. +// (see https://github.com/ipfs/go-ipfs/pull/5118.) // +// 2. `TFile` is used for backwards-compatibility. It was a bug causing the leaf +// nodes to be generated with this type instead of `TRaw`. The former one +// should be used (like the trickle builder does). +// (See https://github.com/ipfs/go-ipfs/pull/5120.) +// +// +-------------+ +// | Root 4 | +// +-------------+ +// | +// +--------------------------+----------------------------+ +// | | +// +-------------+ +-------------+ +// | Node 2 | | Node 5 | +// +-------------+ +-------------+ +// | | +// +-------------+-------------+ +-------------+ +// | | | +// +-------------+ +-------------+ +-------------+ +// | Node 1 | | Node 3 | | Node 6 | +// +-------------+ +-------------+ +-------------+ +// | | | +// +------+------+ +------+------+ +------+ +// | | | | | +// +=========+ +=========+ +=========+ +=========+ +=========+ +// | Chunk 1 | | Chunk 2 | | Chunk 3 | | Chunk 4 | | Chunk 5 | +// +=========+ +=========+ +=========+ +=========+ +=========+ package balanced import ( @@ -80,55 +81,54 @@ import ( // offset in the file the graph represents: each internal node uses the file size // of its children as an index when seeking. // -// `Layout` creates a root and hands it off to be filled: -// -// +-------------+ -// | Root 1 | -// +-------------+ -// | -// ( fillNodeRec fills in the ) -// ( chunks on the root. ) -// | -// +------+------+ -// | | -// + - - - - + + - - - - + -// | Chunk 1 | | Chunk 2 | -// + - - - - + + - - - - + +// `Layout` creates a root and hands it off to be filled: // -// ↓ -// When the root is full but there's more data... -// ↓ +// +-------------+ +// | Root 1 | +// +-------------+ +// | +// ( fillNodeRec fills in the ) +// ( chunks on the root. ) +// | +// +------+------+ +// | | +// + - - - - + + - - - - + +// | Chunk 1 | | Chunk 2 | +// + - - - - + + - - - - + // -// +-------------+ -// | Root 1 | -// +-------------+ -// | -// +------+------+ -// | | -// +=========+ +=========+ + - - - - + -// | Chunk 1 | | Chunk 2 | | Chunk 3 | -// +=========+ +=========+ + - - - - + +// ↓ +// When the root is full but there's more data... +// ↓ // -// ↓ -// ...Layout's job is to create a new root. -// ↓ +// +-------------+ +// | Root 1 | +// +-------------+ +// | +// +------+------+ +// | | +// +=========+ +=========+ + - - - - + +// | Chunk 1 | | Chunk 2 | | Chunk 3 | +// +=========+ +=========+ + - - - - + // -// +-------------+ -// | Root 2 | -// +-------------+ -// | -// +-------------+ - - - - - - - - + -// | | -// +-------------+ ( fillNodeRec creates the ) -// | Node 1 | ( branch that connects ) -// +-------------+ ( "Root 2" to "Chunk 3." ) -// | | -// +------+------+ + - - - - -+ -// | | | -// +=========+ +=========+ + - - - - + -// | Chunk 1 | | Chunk 2 | | Chunk 3 | -// +=========+ +=========+ + - - - - + +// ↓ +// ...Layout's job is to create a new root. +// ↓ // +// +-------------+ +// | Root 2 | +// +-------------+ +// | +// +-------------+ - - - - - - - - + +// | | +// +-------------+ ( fillNodeRec creates the ) +// | Node 1 | ( branch that connects ) +// +-------------+ ( "Root 2" to "Chunk 3." ) +// | | +// +------+------+ + - - - - -+ +// | | | +// +=========+ +=========+ + - - - - + +// | Chunk 1 | | Chunk 2 | | Chunk 3 | +// +=========+ +=========+ + - - - - + func Layout(db *h.DagBuilderHelper) (ipld.Node, error) { if db.Done() { // No data, return just an empty node. @@ -185,22 +185,22 @@ func Layout(db *h.DagBuilderHelper) (ipld.Node, error) { // root, and those children will in turn be filled (calling `fillNodeRec` // recursively). // -// +-------------+ -// | `node` | -// | (new root) | -// +-------------+ -// | -// +-------------+ - - - - - - + - - - - - - - - - - - + -// | | | -// +--------------+ + - - - - - + + - - - - - + -// | (old root) | | new child | | | -// +--------------+ + - - - - - + + - - - - - + -// | | | -// +------+------+ + - - + - - - + -// | | | | -// +=========+ +=========+ + - - - - + + - - - - + -// | Chunk 1 | | Chunk 2 | | Chunk 3 | | Chunk 4 | -// +=========+ +=========+ + - - - - + + - - - - + +// +-------------+ +// | `node` | +// | (new root) | +// +-------------+ +// | +// +-------------+ - - - - - - + - - - - - - - - - - - + +// | | | +// +--------------+ + - - - - - + + - - - - - + +// | (old root) | | new child | | | +// +--------------+ + - - - - - + + - - - - - + +// | | | +// +------+------+ + - - + - - - + +// | | | | +// +=========+ +=========+ + - - - - + + - - - - + +// | Chunk 1 | | Chunk 2 | | Chunk 3 | | Chunk 4 | +// +=========+ +=========+ + - - - - + + - - - - + // // The `node` to be filled uses the `FSNodeOverDag` abstraction that allows adding // child nodes without packing/unpacking the UnixFS layer node (having an internal diff --git a/unixfs/importer/helpers/helpers.go b/unixfs/importer/helpers/helpers.go index 075b2d2d2..20cb598e6 100644 --- a/unixfs/importer/helpers/helpers.go +++ b/unixfs/importer/helpers/helpers.go @@ -13,17 +13,18 @@ var roughLinkSize = 34 + 8 + 5 // sha256 multihash + size + no name + protobuf // DefaultLinksPerBlock governs how the importer decides how many links there // will be per block. This calculation is based on expected distributions of: -// * the expected distribution of block sizes -// * the expected distribution of link sizes -// * desired access speed +// - the expected distribution of block sizes +// - the expected distribution of link sizes +// - desired access speed +// // For now, we use: // -// var roughLinkBlockSize = 1 << 13 // 8KB -// var roughLinkSize = 34 + 8 + 5 // sha256 multihash + size + no name -// // + protobuf framing -// var DefaultLinksPerBlock = (roughLinkBlockSize / roughLinkSize) -// = ( 8192 / 47 ) -// = (approximately) 174 +// var roughLinkBlockSize = 1 << 13 // 8KB +// var roughLinkSize = 34 + 8 + 5 // sha256 multihash + size + no name +// // + protobuf framing +// var DefaultLinksPerBlock = (roughLinkBlockSize / roughLinkSize) +// = ( 8192 / 47 ) +// = (approximately) 174 var DefaultLinksPerBlock = roughLinkBlockSize / roughLinkSize // ErrSizeLimitExceeded signals that a block is larger than BlockSizeLimit. diff --git a/unixfs/importer/importer_test.go b/unixfs/importer/importer_test.go index b39aff57d..96732be20 100644 --- a/unixfs/importer/importer_test.go +++ b/unixfs/importer/importer_test.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "io" - "io/ioutil" "testing" uio "github.com/ipfs/go-unixfs/io" @@ -60,7 +59,7 @@ func TestStableCid(t *testing.T) { t.Fatal(err) } - out, err := ioutil.ReadAll(dr) + out, err := io.ReadAll(dr) if err != nil { t.Fatal(err) } @@ -86,7 +85,7 @@ func TestBalancedDag(t *testing.T) { t.Fatal(err) } - out, err := ioutil.ReadAll(dr) + out, err := io.ReadAll(dr) if err != nil { t.Fatal(err) } @@ -144,7 +143,7 @@ func runReadBench(b *testing.B, nd ipld.Node, ds ipld.DAGService) { b.Fatal(err) } - _, err = read.WriteTo(ioutil.Discard) + _, err = read.WriteTo(io.Discard) if err != nil && err != io.EOF { b.Fatal(err) } diff --git a/unixfs/importer/trickle/trickle_test.go b/unixfs/importer/trickle/trickle_test.go index 2b6e0bd46..5965a490a 100644 --- a/unixfs/importer/trickle/trickle_test.go +++ b/unixfs/importer/trickle/trickle_test.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "io" - "io/ioutil" mrand "math/rand" "testing" @@ -62,7 +61,7 @@ func buildTestDag(ds ipld.DAGService, spl chunker.Splitter, rawLeaves UseRawLeav }) } -//Test where calls to read are smaller than the chunk size +// Test where calls to read are smaller than the chunk size func TestSizeBasedSplit(t *testing.T) { runBothSubtests(t, testSizeBasedSplit) } @@ -103,7 +102,7 @@ func testFileConsistency(t *testing.T, bs chunker.SplitterGen, nbytes int, rawLe t.Fatal(err) } - out, err := ioutil.ReadAll(r) + out, err := io.ReadAll(r) if err != nil { t.Fatal(err) } @@ -133,7 +132,7 @@ func testBuilderConsistency(t *testing.T, rawLeaves UseRawLeaves) { t.Fatal(err) } - out, err := ioutil.ReadAll(r) + out, err := io.ReadAll(r) if err != nil { t.Fatal(err) } @@ -179,7 +178,7 @@ func testIndirectBlocks(t *testing.T, rawLeaves UseRawLeaves) { t.Fatal(err) } - out, err := ioutil.ReadAll(reader) + out, err := io.ReadAll(reader) if err != nil { t.Fatal(err) } @@ -219,7 +218,7 @@ func testSeekingBasic(t *testing.T, rawLeaves UseRawLeaves) { t.Fatal("Failed to seek to correct offset") } - out, err := ioutil.ReadAll(rs) + out, err := io.ReadAll(rs) if err != nil { t.Fatal(err) } @@ -251,7 +250,7 @@ func testSeekToBegin(t *testing.T, rawLeaves UseRawLeaves) { t.Fatal(err) } - n, err := io.CopyN(ioutil.Discard, rs, 1024*4) + n, err := io.CopyN(io.Discard, rs, 1024*4) if err != nil { t.Fatal(err) } @@ -267,7 +266,7 @@ func testSeekToBegin(t *testing.T, rawLeaves UseRawLeaves) { t.Fatal("Failed to seek to beginning") } - out, err := ioutil.ReadAll(rs) + out, err := io.ReadAll(rs) if err != nil { t.Fatal(err) } @@ -299,7 +298,7 @@ func testSeekToAlmostBegin(t *testing.T, rawLeaves UseRawLeaves) { t.Fatal(err) } - n, err := io.CopyN(ioutil.Discard, rs, 1024*4) + n, err := io.CopyN(io.Discard, rs, 1024*4) if err != nil { t.Fatal(err) } @@ -315,7 +314,7 @@ func testSeekToAlmostBegin(t *testing.T, rawLeaves UseRawLeaves) { t.Fatal("Failed to seek to almost beginning") } - out, err := ioutil.ReadAll(rs) + out, err := io.ReadAll(rs) if err != nil { t.Fatal(err) } @@ -534,7 +533,7 @@ func testAppend(t *testing.T, rawLeaves UseRawLeaves) { t.Fatal(err) } - out, err := ioutil.ReadAll(fread) + out, err := io.ReadAll(fread) if err != nil { t.Fatal(err) } @@ -600,7 +599,7 @@ func testMultipleAppends(t *testing.T, rawLeaves UseRawLeaves) { t.Fatal(err) } - out, err := ioutil.ReadAll(fread) + out, err := io.ReadAll(fread) if err != nil { t.Fatal(err) } @@ -654,7 +653,7 @@ func TestAppendSingleBytesToEmpty(t *testing.T) { t.Fatal(err) } - out, err := ioutil.ReadAll(fread) + out, err := io.ReadAll(fread) if err != nil { t.Fatal(err) } diff --git a/unixfs/io/completehamt_test.go b/unixfs/io/completehamt_test.go index 2af652e32..9be35d8ad 100644 --- a/unixfs/io/completehamt_test.go +++ b/unixfs/io/completehamt_test.go @@ -4,10 +4,11 @@ import ( "context" "encoding/binary" "fmt" - "github.com/ipfs/go-unixfs/internal" "math" "testing" + "github.com/ipfs/go-unixfs/internal" + mdtest "github.com/ipfs/go-merkledag/test" "github.com/stretchr/testify/assert" @@ -18,14 +19,16 @@ import ( ) // CreateCompleteHAMT creates a HAMT the following properties: -// * its height (distance/edges from root to deepest node) is specified by treeHeight. -// * all leaf Shard nodes have the same depth (and have only 'value' links). -// * all internal Shard nodes point only to other Shards (and hence have zero 'value' links). -// * the total number of 'value' links (directory entries) is: -// childsPerNode ^ (treeHeight). -// treeHeight: The number of layers of non-value HAMT nodes (e.g. height = 1 is a single shard pointing to some values) +// - its height (distance/edges from root to deepest node) is specified by treeHeight. +// - all leaf Shard nodes have the same depth (and have only 'value' links). +// - all internal Shard nodes point only to other Shards (and hence have zero 'value' links). +// - the total number of 'value' links (directory entries) is: +// childsPerNode ^ (treeHeight). +// treeHeight: The number of layers of non-value HAMT nodes (e.g. height = 1 is a single shard pointing to some values) +// // FIXME: HAMTHashFunction needs to be set to idHash by the caller. We depend on -// this simplification for the current logic to work. +// +// this simplification for the current logic to work. func CreateCompleteHAMT(ds ipld.DAGService, treeHeight int, childsPerNode int) (ipld.Node, error) { if treeHeight < 1 { panic("treeHeight < 1") @@ -75,7 +78,8 @@ func idHash(val []byte) []byte { } // FIXME: This is not checking the exact height of the tree but just making -// sure there are as many children as we would have with a complete HAMT. +// +// sure there are as many children as we would have with a complete HAMT. func TestCreateCompleteShard(t *testing.T) { oldHashFunc := internal.HAMTHashFunction defer func() { internal.HAMTHashFunction = oldHashFunc }() diff --git a/unixfs/io/dagreader_test.go b/unixfs/io/dagreader_test.go index de664370c..1ec3225bb 100644 --- a/unixfs/io/dagreader_test.go +++ b/unixfs/io/dagreader_test.go @@ -3,7 +3,6 @@ package io import ( "bytes" "io" - "io/ioutil" "strings" "testing" @@ -26,7 +25,7 @@ func TestBasicRead(t *testing.T) { t.Fatal(err) } - outbuf, err := ioutil.ReadAll(reader) + outbuf, err := io.ReadAll(reader) if err != nil { t.Fatal(err) } @@ -257,7 +256,7 @@ func TestMetadataNode(t *testing.T) { if err != nil { t.Fatal(err) } - readdata, err := ioutil.ReadAll(reader) + readdata, err := io.ReadAll(reader) if err != nil { t.Fatal(err) } diff --git a/unixfs/mod/dagmodifier_test.go b/unixfs/mod/dagmodifier_test.go index 9870b2022..a37590679 100644 --- a/unixfs/mod/dagmodifier_test.go +++ b/unixfs/mod/dagmodifier_test.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "testing" dag "github.com/ipfs/go-merkledag" @@ -63,7 +62,7 @@ func verifyNode(t *testing.T, orig []byte, dm *DagModifier, opts testu.NodeOpts) t.Fatal(err) } - after, err := ioutil.ReadAll(rd) + after, err := io.ReadAll(rd) if err != nil { t.Fatal(err) } @@ -329,7 +328,7 @@ func testLargeWriteChunks(t *testing.T, opts testu.NodeOpts) { t.Fatal(err) } - out, err := ioutil.ReadAll(dagmod) + out, err := io.ReadAll(dagmod) if err != nil { t.Fatal(err) } @@ -374,7 +373,7 @@ func testDagTruncate(t *testing.T, opts testu.NodeOpts) { t.Fatal(err) } - out, err := ioutil.ReadAll(dagmod) + out, err := io.ReadAll(dagmod) if err != nil { t.Fatal(err) } @@ -458,7 +457,7 @@ func TestDagSync(t *testing.T) { t.Fatal(err) } - out, err := ioutil.ReadAll(dagmod) + out, err := io.ReadAll(dagmod) if err != nil { t.Fatal(err) } @@ -540,7 +539,7 @@ func testSparseWrite(t *testing.T, opts testu.NodeOpts) { t.Fatal(err) } - out, err := ioutil.ReadAll(dagmod) + out, err := io.ReadAll(dagmod) if err != nil { t.Fatal(err) } @@ -593,7 +592,7 @@ func testSeekPastEndWrite(t *testing.T, opts testu.NodeOpts) { t.Fatal(err) } - out, err := ioutil.ReadAll(dagmod) + out, err := io.ReadAll(dagmod) if err != nil { t.Fatal(err) } @@ -627,7 +626,7 @@ func testRelativeSeek(t *testing.T, opts testu.NodeOpts) { } } - out, err := ioutil.ReadAll(dagmod) + out, err := io.ReadAll(dagmod) if err != nil { t.Fatal(err) } diff --git a/unixfs/pb/unixfs.pb.go b/unixfs/pb/unixfs.pb.go index 6f1c8fe83..e52314007 100644 --- a/unixfs/pb/unixfs.pb.go +++ b/unixfs/pb/unixfs.pb.go @@ -5,8 +5,9 @@ package unixfs_pb import ( fmt "fmt" - proto "github.com/gogo/protobuf/proto" math "math" + + proto "github.com/gogo/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. diff --git a/unixfs/test/utils.go b/unixfs/test/utils.go index bb251bc11..b30f43c21 100644 --- a/unixfs/test/utils.go +++ b/unixfs/test/utils.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "testing" ft "github.com/ipfs/go-unixfs" @@ -87,7 +86,7 @@ func GetEmptyNode(t testing.TB, dserv ipld.DAGService, opts NodeOpts) ipld.Node // GetRandomNode returns a random unixfs file node. func GetRandomNode(t testing.TB, dserv ipld.DAGService, size int64, opts NodeOpts) ([]byte, ipld.Node) { in := io.LimitReader(u.NewTimeSeededRand(), size) - buf, err := ioutil.ReadAll(in) + buf, err := io.ReadAll(in) if err != nil { t.Fatal(err) } diff --git a/unixfs/unixfs.go b/unixfs/unixfs.go index 026b8bb3f..cd3481ea4 100644 --- a/unixfs/unixfs.go +++ b/unixfs/unixfs.go @@ -69,7 +69,7 @@ func FilePBData(data []byte, totalsize uint64) []byte { return data } -//FolderPBData returns Bytes that represent a Directory. +// FolderPBData returns Bytes that represent a Directory. func FolderPBData() []byte { pbfile := new(pb.Data) typ := pb.Data_Directory @@ -83,7 +83,7 @@ func FolderPBData() []byte { return data } -//WrapData marshals raw bytes into a `Data_Raw` type protobuf message. +// WrapData marshals raw bytes into a `Data_Raw` type protobuf message. func WrapData(b []byte) []byte { pbdata := new(pb.Data) typ := pb.Data_Raw @@ -100,7 +100,7 @@ func WrapData(b []byte) []byte { return out } -//SymlinkData returns a `Data_Symlink` protobuf message for the path you specify. +// SymlinkData returns a `Data_Symlink` protobuf message for the path you specify. func SymlinkData(path string) ([]byte, error) { pbdata := new(pb.Data) typ := pb.Data_Symlink From fe8211a5ec28bd79928a2923a4620b76c55f0798 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sat, 27 Aug 2022 16:06:24 +0300 Subject: [PATCH 3727/3817] update go-libp2p to v0.22.0, release v0.2.0 (#39) * chore: update go-libp2p to v0.22.0 * release v0.2.0 This commit was moved from ipfs/go-ipns@1df1d60158451c21e93737844a09036fd9cce3ad --- ipns/examples/embed.go | 4 ++-- ipns/examples/examples_test.go | 2 +- ipns/examples/key.go | 2 +- ipns/ipns.go | 9 ++++----- ipns/ipns_test.go | 4 ++-- ipns/record.go | 8 ++++---- ipns/select_test.go | 4 ++-- ipns/validate_test.go | 34 ++++++++++++++++++++++------------ 8 files changed, 38 insertions(+), 29 deletions(-) diff --git a/ipns/examples/embed.go b/ipns/examples/embed.go index cfd6ea754..97757d91f 100644 --- a/ipns/examples/embed.go +++ b/ipns/examples/embed.go @@ -5,8 +5,8 @@ import ( pb "github.com/ipfs/go-ipns/pb" - ipns "github.com/ipfs/go-ipns" - crypto "github.com/libp2p/go-libp2p-core/crypto" + "github.com/ipfs/go-ipns" + "github.com/libp2p/go-libp2p/core/crypto" ) // CreateEntryWithEmbed shows how you can create an IPNS entry diff --git a/ipns/examples/examples_test.go b/ipns/examples/examples_test.go index caa21e34c..17d905051 100644 --- a/ipns/examples/examples_test.go +++ b/ipns/examples/examples_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/ipfs/go-ipns/examples" - crypto "github.com/libp2p/go-libp2p-core/crypto" + "github.com/libp2p/go-libp2p/core/crypto" ) var testPath = "/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5" diff --git a/ipns/examples/key.go b/ipns/examples/key.go index 6354521ee..94f219b8d 100644 --- a/ipns/examples/key.go +++ b/ipns/examples/key.go @@ -1,7 +1,7 @@ package examples import ( - crypto "github.com/libp2p/go-libp2p-core/crypto" + "github.com/libp2p/go-libp2p/core/crypto" ) // GenerateRSAKeyPair is used to generate an RSA key pair diff --git a/ipns/ipns.go b/ipns/ipns.go index f85b1ad8e..5036afba5 100644 --- a/ipns/ipns.go +++ b/ipns/ipns.go @@ -6,22 +6,21 @@ import ( "sort" "time" + "github.com/multiformats/go-multicodec" "github.com/pkg/errors" "github.com/ipld/go-ipld-prime" _ "github.com/ipld/go-ipld-prime/codec/dagcbor" // used to import the DagCbor encoder/decoder ipldcodec "github.com/ipld/go-ipld-prime/multicodec" - "github.com/ipld/go-ipld-prime/node/basic" - - "github.com/multiformats/go-multicodec" + basicnode "github.com/ipld/go-ipld-prime/node/basic" "github.com/gogo/protobuf/proto" pb "github.com/ipfs/go-ipns/pb" u "github.com/ipfs/go-ipfs-util" - ic "github.com/libp2p/go-libp2p-core/crypto" - peer "github.com/libp2p/go-libp2p-core/peer" + ic "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" ) const ( diff --git a/ipns/ipns_test.go b/ipns/ipns_test.go index f56d1e7e2..711c7f6fd 100644 --- a/ipns/ipns_test.go +++ b/ipns/ipns_test.go @@ -6,8 +6,8 @@ import ( "time" u "github.com/ipfs/go-ipfs-util" - ci "github.com/libp2p/go-libp2p-core/crypto" - peer "github.com/libp2p/go-libp2p-core/peer" + ci "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" ) func TestEmbedPublicKey(t *testing.T) { diff --git a/ipns/record.go b/ipns/record.go index 43750bf3b..a7710a4c3 100644 --- a/ipns/record.go +++ b/ipns/record.go @@ -7,11 +7,11 @@ import ( pb "github.com/ipfs/go-ipns/pb" "github.com/gogo/protobuf/proto" - logging "github.com/ipfs/go-log" - ic "github.com/libp2p/go-libp2p-core/crypto" - "github.com/libp2p/go-libp2p-core/peer" - pstore "github.com/libp2p/go-libp2p-core/peerstore" + logging "github.com/ipfs/go-log/v2" record "github.com/libp2p/go-libp2p-record" + ic "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" + pstore "github.com/libp2p/go-libp2p/core/peerstore" ) var log = logging.Logger("ipns") diff --git a/ipns/select_test.go b/ipns/select_test.go index 905afb1da..7dbdfdf12 100644 --- a/ipns/select_test.go +++ b/ipns/select_test.go @@ -8,9 +8,9 @@ import ( pb "github.com/ipfs/go-ipns/pb" - proto "github.com/gogo/protobuf/proto" + "github.com/gogo/protobuf/proto" u "github.com/ipfs/go-ipfs-util" - ci "github.com/libp2p/go-libp2p-core/crypto" + ci "github.com/libp2p/go-libp2p/core/crypto" ) func shuffle(a []*pb.IpnsEntry) { diff --git a/ipns/validate_test.go b/ipns/validate_test.go index 40b41b6e3..e231934a9 100644 --- a/ipns/validate_test.go +++ b/ipns/validate_test.go @@ -9,18 +9,16 @@ import ( "testing" "time" + "github.com/gogo/protobuf/proto" + u "github.com/ipfs/go-ipfs-util" pb "github.com/ipfs/go-ipns/pb" - ipldcodec "github.com/ipld/go-ipld-prime/multicodec" basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" + pstore "github.com/libp2p/go-libp2p/core/peerstore" + "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem" "github.com/multiformats/go-multicodec" - - proto "github.com/gogo/protobuf/proto" - u "github.com/ipfs/go-ipfs-util" - "github.com/libp2p/go-libp2p-core/crypto" - peer "github.com/libp2p/go-libp2p-core/peer" - pstore "github.com/libp2p/go-libp2p-core/peerstore" - pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem" ) func testValidatorCase(t *testing.T, priv crypto.PrivKey, kbook pstore.KeyBook, key string, val []byte, eol time.Time, exp error) { @@ -69,11 +67,17 @@ func TestValidator(t *testing.T) { priv, id, _ := genKeys(t) priv2, id2, _ := genKeys(t) - kbook := pstoremem.NewPeerstore() + kbook, err := pstoremem.NewPeerstore() + if err != nil { + t.Fatal(err) + } if err := kbook.AddPubKey(id, priv.GetPublic()); err != nil { t.Fatal(err) } - emptyKbook := pstoremem.NewPeerstore() + emptyKbook, err := pstoremem.NewPeerstore() + if err != nil { + t.Fatal(err) + } testValidatorCase(t, priv, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour), nil) testValidatorCase(t, priv, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour*-1), ErrExpiredRecord) @@ -97,7 +101,10 @@ func mustMarshal(t *testing.T, entry *pb.IpnsEntry) []byte { func TestEmbeddedPubKeyValidate(t *testing.T) { goodeol := time.Now().Add(time.Hour) - kbook := pstoremem.NewPeerstore() + kbook, err := pstoremem.NewPeerstore() + if err != nil { + t.Fatal(err) + } pth := []byte("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") @@ -139,7 +146,10 @@ func TestPeerIDPubKeyValidate(t *testing.T) { t.Skip("disabled until libp2p/go-libp2p-crypto#51 is fixed") goodeol := time.Now().Add(time.Hour) - kbook := pstoremem.NewPeerstore() + kbook, err := pstoremem.NewPeerstore() + if err != nil { + t.Fatal(err) + } pth := []byte("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") From e23fe4499b93bd863cf109baef766bb0031249ee Mon Sep 17 00:00:00 2001 From: web3-bot Date: Mon, 29 Aug 2022 14:25:59 +0000 Subject: [PATCH 3728/3817] stop using the deprecated io/ioutil package This commit was moved from ipfs/go-filestore@866d05c88c5e5f307864f6afcece8012eddcd009 --- filestore/filestore_test.go | 6 +++--- filestore/pb/dataobj.pb.go | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/filestore/filestore_test.go b/filestore/filestore_test.go index 8117a2392..f3f3e64dd 100644 --- a/filestore/filestore_test.go +++ b/filestore/filestore_test.go @@ -3,8 +3,8 @@ package filestore import ( "bytes" "context" - "io/ioutil" "math/rand" + "os" "testing" dag "github.com/ipfs/go-merkledag" @@ -21,7 +21,7 @@ var bg = context.Background() func newTestFilestore(t *testing.T) (string, *Filestore) { mds := ds.NewMapDatastore() - testdir, err := ioutil.TempDir("", "filestore-test") + testdir, err := os.MkdirTemp("", "filestore-test") if err != nil { t.Fatal(err) } @@ -34,7 +34,7 @@ func newTestFilestore(t *testing.T) (string, *Filestore) { } func makeFile(dir string, data []byte) (string, error) { - f, err := ioutil.TempFile(dir, "file") + f, err := os.CreateTemp(dir, "file") if err != nil { return "", err } diff --git a/filestore/pb/dataobj.pb.go b/filestore/pb/dataobj.pb.go index 5ecc2489e..d342cabe5 100644 --- a/filestore/pb/dataobj.pb.go +++ b/filestore/pb/dataobj.pb.go @@ -5,10 +5,11 @@ package datastore_pb import ( fmt "fmt" - proto "github.com/gogo/protobuf/proto" io "io" math "math" math_bits "math/bits" + + proto "github.com/gogo/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. From 5c8841a14c44a54ac2961776e7af536bad402fff Mon Sep 17 00:00:00 2001 From: web3-bot Date: Mon, 29 Aug 2022 14:27:13 +0000 Subject: [PATCH 3729/3817] stop using the deprecated io/ioutil package This commit was moved from ipfs/go-pinning-service-http-client@905bf818553964cd19e4194bcd502ed0d252312b --- pinning/remote/client/cmd/main.go | 5 ++- pinning/remote/client/model.go | 3 +- pinning/remote/client/openapi/api_pins.go | 45 +++++++++++++---------- pinning/remote/client/openapi/client.go | 3 +- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/pinning/remote/client/cmd/main.go b/pinning/remote/client/cmd/main.go index c095ed08c..3136bbd16 100644 --- a/pinning/remote/client/cmd/main.go +++ b/pinning/remote/client/cmd/main.go @@ -3,10 +3,11 @@ package main import ( "context" "fmt" - "github.com/ipfs/go-cid" - pinclient "github.com/ipfs/go-pinning-service-http-client" "os" "time" + + "github.com/ipfs/go-cid" + pinclient "github.com/ipfs/go-pinning-service-http-client" ) func main() { diff --git a/pinning/remote/client/model.go b/pinning/remote/client/model.go index 506c43ae3..a87f9c913 100644 --- a/pinning/remote/client/model.go +++ b/pinning/remote/client/model.go @@ -3,10 +3,11 @@ package go_pinning_service_http_client import ( "encoding/json" "fmt" + "time" + "github.com/ipfs/go-cid" "github.com/ipfs/go-pinning-service-http-client/openapi" "github.com/multiformats/go-multiaddr" - "time" ) // PinGetter Getter for Pin object diff --git a/pinning/remote/client/openapi/api_pins.go b/pinning/remote/client/openapi/api_pins.go index 8c45df5a3..341abfd69 100644 --- a/pinning/remote/client/openapi/api_pins.go +++ b/pinning/remote/client/openapi/api_pins.go @@ -11,7 +11,6 @@ package openapi import ( _context "context" - _ioutil "io/ioutil" _nethttp "net/http" _neturl "net/url" "strings" @@ -76,7 +75,8 @@ func (r apiPinsGetRequest) Meta(meta map[string]string) apiPinsGetRequest { /* PinsGet List pin objects List all the pin objects, matching optional filters; when no filter is provided, only successful pins are returned - * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return apiPinsGetRequest */ func (a *PinsApiService) PinsGet(ctx _context.Context) apiPinsGetRequest { @@ -88,7 +88,8 @@ func (a *PinsApiService) PinsGet(ctx _context.Context) apiPinsGetRequest { /* Execute executes the request - @return PinResults + + @return PinResults */ func (r apiPinsGetRequest) Execute() (PinResults, *_nethttp.Response, error) { var ( @@ -159,7 +160,7 @@ func (r apiPinsGetRequest) Execute() (PinResults, *_nethttp.Response, error) { return localVarReturnValue, localVarHTTPResponse, err } - localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarBody, err := _io.ReadAll(localVarHTTPResponse.Body) localVarHTTPResponse.Body.Close() if err != nil { return localVarReturnValue, localVarHTTPResponse, err @@ -209,7 +210,8 @@ func (r apiPinsPostRequest) Pin(pin Pin) apiPinsPostRequest { /* PinsPost Add pin object Add a new pin object for the current access token - * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return apiPinsPostRequest */ func (a *PinsApiService) PinsPost(ctx _context.Context) apiPinsPostRequest { @@ -221,7 +223,8 @@ func (a *PinsApiService) PinsPost(ctx _context.Context) apiPinsPostRequest { /* Execute executes the request - @return PinStatus + + @return PinStatus */ func (r apiPinsPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { var ( @@ -277,7 +280,7 @@ func (r apiPinsPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { return localVarReturnValue, localVarHTTPResponse, err } - localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarBody, err := _io.ReadAll(localVarHTTPResponse.Body) localVarHTTPResponse.Body.Close() if err != nil { return localVarReturnValue, localVarHTTPResponse, err @@ -322,8 +325,9 @@ type apiPinsRequestidDeleteRequest struct { /* PinsRequestidDelete Remove pin object Remove a pin object - * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - * @param requestid + - @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param requestid + @return apiPinsRequestidDeleteRequest */ func (a *PinsApiService) PinsRequestidDelete(ctx _context.Context, requestid string) apiPinsRequestidDeleteRequest { @@ -336,7 +340,6 @@ func (a *PinsApiService) PinsRequestidDelete(ctx _context.Context, requestid str /* Execute executes the request - */ func (r apiPinsRequestidDeleteRequest) Execute() (*_nethttp.Response, error) { var ( @@ -386,7 +389,7 @@ func (r apiPinsRequestidDeleteRequest) Execute() (*_nethttp.Response, error) { return localVarHTTPResponse, err } - localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarBody, err := _io.ReadAll(localVarHTTPResponse.Body) localVarHTTPResponse.Body.Close() if err != nil { return localVarHTTPResponse, err @@ -422,8 +425,9 @@ type apiPinsRequestidGetRequest struct { /* PinsRequestidGet Get pin object Get a pin object and its status - * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - * @param requestid + - @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param requestid + @return apiPinsRequestidGetRequest */ func (a *PinsApiService) PinsRequestidGet(ctx _context.Context, requestid string) apiPinsRequestidGetRequest { @@ -436,7 +440,8 @@ func (a *PinsApiService) PinsRequestidGet(ctx _context.Context, requestid string /* Execute executes the request - @return PinStatus + + @return PinStatus */ func (r apiPinsRequestidGetRequest) Execute() (PinStatus, *_nethttp.Response, error) { var ( @@ -487,7 +492,7 @@ func (r apiPinsRequestidGetRequest) Execute() (PinStatus, *_nethttp.Response, er return localVarReturnValue, localVarHTTPResponse, err } - localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarBody, err := _io.ReadAll(localVarHTTPResponse.Body) localVarHTTPResponse.Body.Close() if err != nil { return localVarReturnValue, localVarHTTPResponse, err @@ -538,8 +543,9 @@ func (r apiPinsRequestidPostRequest) Pin(pin Pin) apiPinsRequestidPostRequest { /* PinsRequestidPost Replace pin object Replace an existing pin object (shortcut for executing remove and add operations in one step to avoid unnecessary garbage collection of blocks present in both recursive pins) - * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - * @param requestid + - @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param requestid + @return apiPinsRequestidPostRequest */ func (a *PinsApiService) PinsRequestidPost(ctx _context.Context, requestid string) apiPinsRequestidPostRequest { @@ -552,7 +558,8 @@ func (a *PinsApiService) PinsRequestidPost(ctx _context.Context, requestid strin /* Execute executes the request - @return PinStatus + + @return PinStatus */ func (r apiPinsRequestidPostRequest) Execute() (PinStatus, *_nethttp.Response, error) { var ( @@ -609,7 +616,7 @@ func (r apiPinsRequestidPostRequest) Execute() (PinStatus, *_nethttp.Response, e return localVarReturnValue, localVarHTTPResponse, err } - localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarBody, err := _io.ReadAll(localVarHTTPResponse.Body) localVarHTTPResponse.Body.Close() if err != nil { return localVarReturnValue, localVarHTTPResponse, err diff --git a/pinning/remote/client/openapi/client.go b/pinning/remote/client/openapi/client.go index 029d1cd72..a0fc90e6f 100644 --- a/pinning/remote/client/openapi/client.go +++ b/pinning/remote/client/openapi/client.go @@ -16,7 +16,6 @@ import ( "encoding/xml" "errors" "fmt" - "golang.org/x/oauth2" "io" "log" "mime/multipart" @@ -29,6 +28,8 @@ import ( "regexp" "strings" "time" + + "golang.org/x/oauth2" ) var ( From eea2118fc329c9ed7e3e157884286bc7fc91db01 Mon Sep 17 00:00:00 2001 From: web3-bot Date: Mon, 29 Aug 2022 14:27:28 +0000 Subject: [PATCH 3730/3817] stop using the deprecated io/ioutil package This commit was moved from ipfs/go-mfs@80d7a0f99971271980f5806fbe07829b3501ef17 --- mfs/mfs_test.go | 11 +++++------ mfs/repub.go | 8 ++++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/mfs/mfs_test.go b/mfs/mfs_test.go index 1ea90ef33..ace1f7667 100644 --- a/mfs/mfs_test.go +++ b/mfs/mfs_test.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "math/rand" "os" "sort" @@ -176,7 +175,7 @@ func assertFileAtPath(ds ipld.DAGService, root *Directory, expn ipld.Node, pth s return err } - out, err := ioutil.ReadAll(rfd) + out, err := io.ReadAll(rfd) if err != nil { return err } @@ -199,7 +198,7 @@ func catNode(ds ipld.DAGService, nd *dag.ProtoNode) ([]byte, error) { } defer r.Close() - return ioutil.ReadAll(r) + return io.ReadAll(r) } func setupRoot(ctx context.Context, t *testing.T) (ipld.DAGService, *Root) { @@ -831,7 +830,7 @@ func actorReadFile(d *Directory) error { return err } - _, err = ioutil.ReadAll(rfd) + _, err = io.ReadAll(rfd) if err != nil { return err } @@ -1116,7 +1115,7 @@ func writeFile(rt *Root, path string, transform func([]byte) []byte) error { } defer fd.Close() - data, err := ioutil.ReadAll(fd) + data, err := io.ReadAll(fd) if err != nil { return err } @@ -1383,7 +1382,7 @@ func TestTruncateAndWrite(t *testing.T) { t.Fatal(err) } - data, err := ioutil.ReadAll(fd) + data, err := io.ReadAll(fd) if err != nil { t.Fatal(err) } diff --git a/mfs/repub.go b/mfs/repub.go index 2c9dbd25d..463810414 100644 --- a/mfs/repub.go +++ b/mfs/repub.go @@ -87,10 +87,10 @@ func (rp *Republisher) Update(c cid.Cid) { // updates. // // Algorithm: -// 1. When we receive the first update after publishing, we set a `longer` timer. -// 2. When we receive any update, we reset the `quick` timer. -// 3. If either the `quick` timeout or the `longer` timeout elapses, -// we call `publish` with the latest updated value. +// 1. When we receive the first update after publishing, we set a `longer` timer. +// 2. When we receive any update, we reset the `quick` timer. +// 3. If either the `quick` timeout or the `longer` timeout elapses, +// we call `publish` with the latest updated value. // // The `longer` timer ensures that we delay publishing by at most // `TimeoutLong`. The `quick` timer allows us to publish sooner if From c7bfc09efe5ffc65230fb0b740c1dfe04ae0ead7 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 31 Aug 2022 22:36:30 +0200 Subject: [PATCH 3731/3817] fix: import io as _io This commit was moved from ipfs/go-pinning-service-http-client@61e9e65db4dbcbfa78fc94230694760219135f66 --- pinning/remote/client/openapi/api_pins.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pinning/remote/client/openapi/api_pins.go b/pinning/remote/client/openapi/api_pins.go index 341abfd69..b2858c5ee 100644 --- a/pinning/remote/client/openapi/api_pins.go +++ b/pinning/remote/client/openapi/api_pins.go @@ -11,6 +11,7 @@ package openapi import ( _context "context" + _io "io" _nethttp "net/http" _neturl "net/url" "strings" From 3bf94cdfb16ef7db42a7e2b740c0c3c6f994d674 Mon Sep 17 00:00:00 2001 From: web3-bot Date: Thu, 1 Sep 2022 09:14:36 +0000 Subject: [PATCH 3732/3817] stop using the deprecated io/ioutil package This commit was moved from ipfs/go-ipfs-keystore@fadb8f32307c568bdf8eaf185042b17ee16ab5ca --- keystore/keystore.go | 3 +-- keystore/keystore_test.go | 13 ++++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index 9b2109ccd..10606a8c9 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -2,7 +2,6 @@ package keystore import ( "fmt" - "io/ioutil" "os" "path/filepath" "strings" @@ -112,7 +111,7 @@ func (ks *FSKeystore) Get(name string) (ci.PrivKey, error) { kp := filepath.Join(ks.dir, name) - data, err := ioutil.ReadFile(kp) + data, err := os.ReadFile(kp) if err != nil { if os.IsNotExist(err) { return nil, ErrNoSuchKey diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index 06f2fccc5..bbfde6c86 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -2,7 +2,6 @@ package keystore import ( "fmt" - "io/ioutil" "math/rand" "os" "path/filepath" @@ -27,7 +26,7 @@ func privKeyOrFatal(t *testing.T) ci.PrivKey { } func TestKeystoreBasics(t *testing.T) { - tdir, err := ioutil.TempDir("", "keystore-test") + tdir, err := os.MkdirTemp("", "keystore-test") if err != nil { t.Fatal(err) } @@ -146,7 +145,7 @@ func TestKeystoreBasics(t *testing.T) { } func TestInvalidKeyFiles(t *testing.T) { - tdir, err := ioutil.TempDir("", "keystore-test") + tdir, err := os.MkdirTemp("", "keystore-test") if err != nil { t.Fatal(err) @@ -171,12 +170,12 @@ func TestInvalidKeyFiles(t *testing.T) { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(ks.dir, encodedName), bytes, 0644) + err = os.WriteFile(filepath.Join(ks.dir, encodedName), bytes, 0644) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(ks.dir, "z.invalid"), bytes, 0644) + err = os.WriteFile(filepath.Join(ks.dir, "z.invalid"), bytes, 0644) if err != nil { t.Fatal(err) } @@ -205,7 +204,7 @@ func TestInvalidKeyFiles(t *testing.T) { } func TestNonExistingKey(t *testing.T) { - tdir, err := ioutil.TempDir("", "keystore-test") + tdir, err := os.MkdirTemp("", "keystore-test") if err != nil { t.Fatal(err) } @@ -245,7 +244,7 @@ func assertGetKey(ks Keystore, name string, exp ci.PrivKey) error { } func assertDirContents(dir string, exp []string) error { - finfos, err := ioutil.ReadDir(dir) + finfos, err := os.ReadDir(dir) if err != nil { return err } From dbe72d0f8d30503aaef183ef58bef13cf23b6ac1 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Thu, 18 Aug 2022 15:15:37 +0200 Subject: [PATCH 3733/3817] refactor: cleanup Sprintf for Bearer token This commit was moved from ipfs/go-pinning-service-http-client@67b82bcbd5d6c5e0333140c2a0a79c91d1843079 --- pinning/remote/client/client.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pinning/remote/client/client.go b/pinning/remote/client/client.go index f1bed8af8..3d8c8331d 100644 --- a/pinning/remote/client/client.go +++ b/pinning/remote/client/client.go @@ -27,8 +27,7 @@ type Client struct { func NewClient(url, bearerToken string) *Client { config := openapi.NewConfiguration() config.UserAgent = UserAgent - bearer := fmt.Sprintf("Bearer %s", bearerToken) - config.AddDefaultHeader("Authorization", bearer) + config.AddDefaultHeader("Authorization", "Bearer "+bearerToken) config.Servers = openapi.ServerConfigurations{ openapi.ServerConfiguration{ URL: url, From 62bb9386990edcb7dbbdeb2ac54e4aae50e9cfe8 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Thu, 18 Aug 2022 15:25:55 +0200 Subject: [PATCH 3734/3817] fix: send up to nanosecond precision This commit was moved from ipfs/go-pinning-service-http-client@05198b4897e722900553b3242a94a28d512769b4 --- pinning/remote/client/openapi/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/remote/client/openapi/client.go b/pinning/remote/client/openapi/client.go index a0fc90e6f..b3cea998b 100644 --- a/pinning/remote/client/openapi/client.go +++ b/pinning/remote/client/openapi/client.go @@ -121,7 +121,7 @@ func parameterToString(obj interface{}, collectionFormat string) string { if reflect.TypeOf(obj).Kind() == reflect.Slice { return strings.Trim(strings.Replace(fmt.Sprint(obj), " ", delimiter, -1), "[]") } else if t, ok := obj.(time.Time); ok { - return t.Format(time.RFC3339) + return t.Format(time.RFC3339Nano) } return fmt.Sprintf("%v", obj) From 0599510d4100e6bbf67daa57df3fe88c932e73a1 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 2 Sep 2022 20:53:57 +1000 Subject: [PATCH 3735/3817] feat: Has() and Get() will respect StoreIdentityCIDs option When StoreIdentityCIDs is set, it will defer to the index to check whether the blocks are in the CAR. When the CAR is a v1 and StoreIdentityCIDs is set, the index will contain the identity CIDs. When it's a v2 with an existing index, however that index was created will determine whether the identity CIDs are that are in the CAR are found. When StoreIdentityCIDs is not set, Has() will always return true and Get() will always return the block. This commit was moved from ipld/go-car@02d658faa7dfbc01920fc42ad7210c3505d049f4 --- ipld/car/v2/blockstore/readonly.go | 46 +++++++++++++++++-------- ipld/car/v2/blockstore/readonly_test.go | 39 ++++++++++++++++++--- ipld/car/v2/index_gen.go | 4 +++ ipld/car/v2/options.go | 11 ++++-- 4 files changed, 80 insertions(+), 20 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 32c49046b..bfca69112 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -217,14 +217,23 @@ func (b *ReadOnly) DeleteBlock(_ context.Context, _ cid.Cid) error { } // Has indicates if the store contains a block that corresponds to the given key. -// This function always returns true for any given key with multihash.IDENTITY code. +// This function always returns true for any given key with multihash.IDENTITY +// code unless the StoreIdentityCIDs option is on, in which case it will defer +// to the index to check for the existence of the block; the index may or may +// not contain identity CIDs included in this CAR, depending on whether +// StoreIdentityCIDs was on when the index was created. If the CAR is a CARv1 +// and StoreIdentityCIDs is on, then the index will contain identity CIDs and +// this will always return true. func (b *ReadOnly) Has(ctx context.Context, key cid.Cid) (bool, error) { - // Check if the given CID has multihash.IDENTITY code - // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. - if _, ok, err := isIdentity(key); err != nil { - return false, err - } else if ok { - return true, nil + if !b.opts.StoreIdentityCIDs { + // If we don't store identity CIDs then we can return them straight away as if they are here, + // otherwise we need to check for their existence. + // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. + if _, ok, err := isIdentity(key); err != nil { + return false, err + } else if ok { + return true, nil + } } b.mu.RLock() @@ -269,14 +278,23 @@ func (b *ReadOnly) Has(ctx context.Context, key cid.Cid) (bool, error) { } // Get gets a block corresponding to the given key. -// This API will always return true if the given key has multihash.IDENTITY code. +// This function always returns the block for any given key with +// multihash.IDENTITY code unless the StoreIdentityCIDs option is on, in which +// case it will defer to the index to check for the existence of the block; the +// index may or may not contain identity CIDs included in this CAR, depending on +// whether StoreIdentityCIDs was on when the index was created. If the CAR is a +// CARv1 and StoreIdentityCIDs is on, then the index will contain identity CIDs +// and this will always return true. func (b *ReadOnly) Get(ctx context.Context, key cid.Cid) (blocks.Block, error) { - // Check if the given CID has multihash.IDENTITY code - // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. - if digest, ok, err := isIdentity(key); err != nil { - return nil, err - } else if ok { - return blocks.NewBlockWithCid(digest, key) + if !b.opts.StoreIdentityCIDs { + // If we don't store identity CIDs then we can return them straight away as if they are here, + // otherwise we need to check for their existence. + // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. + if digest, ok, err := isIdentity(key); err != nil { + return nil, err + } else if ok { + return blocks.NewBlockWithCid(digest, key) + } } b.mu.RLock() diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index a799d328f..6b1b009f8 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -14,6 +14,7 @@ import ( "github.com/ipfs/go-merkledag" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/internal/carv1" + "github.com/multiformats/go-multicodec" "github.com/stretchr/testify/require" ) @@ -33,31 +34,53 @@ func TestReadOnly(t *testing.T) { name string v1OrV2path string opts []carv2.Option + noIdCids bool }{ { "OpenedWithCarV1", "../testdata/sample-v1.car", []carv2.Option{UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, + // index is made, but identity CIDs are included so they'll be found + false, + }, + { + "OpenedWithCarV1_NoIdentityCID", + "../testdata/sample-v1.car", + []carv2.Option{UseWholeCIDs(true)}, + // index is made, identity CIDs are not included, but we always short-circuit when StoreIdentityCIDs(false) + false, }, { "OpenedWithCarV2", "../testdata/sample-wrapped-v2.car", []carv2.Option{UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, + // index already exists, but was made without identity CIDs, but opening with StoreIdentityCIDs(true) means we check the index + true, + }, + { + "OpenedWithCarV2_NoIdentityCID", + "../testdata/sample-wrapped-v2.car", + []carv2.Option{UseWholeCIDs(true)}, + // index already exists, it was made without identity CIDs, but we always short-circuit when StoreIdentityCIDs(false) + false, }, { "OpenedWithCarV1ZeroLenSection", "../testdata/sample-v1-with-zero-len-section.car", []carv2.Option{UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, + false, }, { "OpenedWithAnotherCarV1ZeroLenSection", "../testdata/sample-v1-with-zero-len-section2.car", []carv2.Option{UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, + false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := context.TODO() + subject, err := OpenReadOnly(tt.v1OrV2path, tt.opts...) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, subject.Close()) }) @@ -89,7 +112,13 @@ func TestReadOnly(t *testing.T) { // Assert blockstore contains key. has, err := subject.Has(ctx, key) require.NoError(t, err) - require.True(t, has) + if key.Prefix().MhType == uint64(multicodec.Identity) && tt.noIdCids { + // fixture wasn't made with StoreIdentityCIDs, but we opened it with StoreIdentityCIDs, + // so they aren't there to find + require.False(t, has) + } else { + require.True(t, has) + } // Assert size matches block raw data length. gotSize, err := subject.GetSize(ctx, key) @@ -98,9 +127,11 @@ func TestReadOnly(t *testing.T) { require.Equal(t, wantSize, gotSize) // Assert block itself matches v1 payload block. - gotBlock, err := subject.Get(ctx, key) - require.NoError(t, err) - require.Equal(t, wantBlock, gotBlock) + if has { + gotBlock, err := subject.Get(ctx, key) + require.NoError(t, err) + require.Equal(t, wantBlock, gotBlock) + } // Assert write operations error require.Error(t, subject.Put(ctx, wantBlock)) diff --git a/ipld/car/v2/index_gen.go b/ipld/car/v2/index_gen.go index b0b87453e..092ed434c 100644 --- a/ipld/car/v2/index_gen.go +++ b/ipld/car/v2/index_gen.go @@ -34,6 +34,10 @@ func GenerateIndex(v1r io.Reader, opts ...Option) (index.Index, error) { // LoadIndex populates idx with index records generated from r. // The r may be in CARv1 or CARv2 format. // +// If the StoreIdentityCIDs option is set when calling LoadIndex, identity +// CIDs will be included in the index. By default this option is off, and +// identity CIDs will not be included in the index. +// // Note, the index is re-generated every time even if r is in CARv2 format and already has an index. // To read existing index when available see ReadOrGenerateIndex. func LoadIndex(idx index.Index, r io.Reader, opts ...Option) error { diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go index d2e526c42..8b5fe9b4e 100644 --- a/ipld/car/v2/options.go +++ b/ipld/car/v2/options.go @@ -127,8 +127,15 @@ func WithoutIndex() Option { // StoreIdentityCIDs sets whether to persist sections that are referenced by // CIDs with multihash.IDENTITY digest. -// When writing CAR files with this option, -// Characteristics.IsFullyIndexed will be set. +// When writing CAR files with this option, Characteristics.IsFullyIndexed will +// be set. +// +// By default, the blockstore interface will always return true for Has() called +// with identity CIDs, but when this option is turned on, it will defer to the +// index. +// +// When creating an index (or loading a CARv1 as a blockstore), when this option +// is on, identity CIDs will be included in the index. // // This option is disabled by default. func StoreIdentityCIDs(b bool) Option { From f02f43cbc8781a933c09aa63e96bb4fb5d011d82 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Mon, 12 Sep 2022 21:34:59 +0200 Subject: [PATCH 3736/3817] chore: require V2 signatures This is part of deprecation described in https://github.com/ipfs/kubo/issues/9240 - record creation continues to create both V1 and V2 signatures - record validation no longer accepts V1 signatures Meaning: - modern nodes are 100% V2, they ignore V1 signatures - legacy nodes (go-ipfs < v0.9.0) are still able to resolve names created by go-ipns, because V1 is still present This commit was moved from ipfs/go-ipns@b655f6b85fee922023f92d2def55aab47c7c9cd4 --- ipns/README.md | 7 +++---- ipns/ipns.go | 11 +++++++---- ipns/validate_test.go | 15 +++++---------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/ipns/README.md b/ipns/README.md index eb1158e2a..148778df1 100644 --- a/ipns/README.md +++ b/ipns/README.md @@ -1,14 +1,13 @@ # go-ipns -[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai) -[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) -[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](https://protocol.ai) +[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.tech/) [![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) [![GoDoc](https://godoc.org/github.com/ipfs/go-datastore?status.svg)](https://godoc.org/github.com/ipfs/go-ipns) > ipns record definitions -This package contains all of the components necessary to create, understand, and validate IPNS records. It does *not* publish or resolve those records. [`go-ipfs`](https://github.com/ipfs/go-ipfs) uses this package internally to manipulate records. +This package contains all of the components necessary to create, understand, and validate IPNS records. It does *not* publish or resolve those records. [Kubo](https://github.com/ipfs/kubo) uses this package internally to manipulate records. ## Lead Maintainer diff --git a/ipns/ipns.go b/ipns/ipns.go index 5036afba5..5f9eb25d1 100644 --- a/ipns/ipns.go +++ b/ipns/ipns.go @@ -53,6 +53,10 @@ func Create(sk ic.PrivKey, val []byte, seq uint64, eol time.Time, ttl time.Durat } entry.Data = cborData + // For now we still create V1 signatures. These are deprecated, and not + // used during verification anymore (Validate func requires SignatureV2), + // but setting it here allows legacy nodes (e.g., go-ipfs < v0.9.0) to + // still resolve IPNS published by modern nodes. sig1, err := sk.Sign(ipnsEntryDataForSigV1(entry)) if err != nil { return nil, errors.Wrap(err, "could not compute signature data") @@ -130,7 +134,7 @@ func createCborDataForIpnsEntry(e *pb.IpnsEntry) ([]byte, error) { func Validate(pk ic.PubKey, entry *pb.IpnsEntry) error { // Check the ipns record signature with the public key - // Check v2 signature if it's available, otherwise use the v1 signature + // Check v2 signature if it's available if entry.GetSignatureV2() != nil { sig2Data, err := ipnsEntryDataForSigV2(entry) if err != nil { @@ -147,9 +151,8 @@ func Validate(pk ic.PubKey, entry *pb.IpnsEntry) error { return err } } else { - if ok, err := pk.Verify(ipnsEntryDataForSigV1(entry), entry.GetSignatureV1()); err != nil || !ok { - return ErrSignature - } + // always error if no valid signature could be found + return ErrSignature } eol, err := GetEOL(entry) diff --git a/ipns/validate_test.go b/ipns/validate_test.go index e231934a9..a0d7f7e02 100644 --- a/ipns/validate_test.go +++ b/ipns/validate_test.go @@ -178,7 +178,7 @@ func TestPeerIDPubKeyValidate(t *testing.T) { testValidatorCase(t, sk, kbook, ipnsk, dataNoKey, goodeol, nil) } -func TestBothSignatureVersionsValidate(t *testing.T) { +func TestOnlySignatureV2Validate(t *testing.T) { goodeol := time.Now().Add(time.Hour) sk, pk, err := crypto.GenerateEd25519Key(rand.New(rand.NewSource(42))) @@ -197,17 +197,12 @@ func TestBothSignatureVersionsValidate(t *testing.T) { } entry.SignatureV2 = nil - if err := Validate(pk, entry); err != nil { - t.Fatal(err) - } - - entry.SignatureV1 = nil if err := Validate(pk, entry); !errors.Is(err, ErrSignature) { t.Fatal(err) } } -func TestNewSignatureVersionPreferred(t *testing.T) { +func TestSignatureV1Ignored(t *testing.T) { goodeol := time.Now().Add(time.Hour) sk, pk, err := crypto.GenerateEd25519Key(rand.New(rand.NewSource(42))) @@ -251,13 +246,13 @@ func TestNewSignatureVersionPreferred(t *testing.T) { t.Fatal("entry2 should be better than entry1") } - // Having only the v1 signature should be valid + // Having only the v1 signature should be invalid entry2.SignatureV2 = nil - if err := Validate(pk, entry2); err != nil { + if err := Validate(pk, entry2); !errors.Is(err, ErrSignature) { t.Fatal(err) } - // However the v2 signature should be preferred + // Record with v2 signature should always be preferred best, err = v.Select(ipnsk, [][]byte{mustMarshal(t, entry1), mustMarshal(t, entry2)}) if err != nil { t.Fatal(err) From c7a481767eaf3ad8d7a51e8e9927374d61bb5590 Mon Sep 17 00:00:00 2001 From: nisainan Date: Tue, 13 Sep 2022 15:06:46 +0800 Subject: [PATCH 3737/3817] chore: update go-libp2p to v0.22.0 This commit was moved from ipfs/go-ipfs-routing@e8c4a5b70efe2092ef35af35d543f2189b52b84f --- routing/mock/centralized_client.go | 14 ++++++-------- routing/mock/centralized_server.go | 10 ++++------ routing/mock/centralized_test.go | 5 ++--- routing/mock/interface.go | 7 +++---- routing/none/none_client.go | 10 ++++------ routing/offline/offline.go | 10 ++++------ routing/offline/offline_test.go | 4 ++-- 7 files changed, 25 insertions(+), 35 deletions(-) diff --git a/routing/mock/centralized_client.go b/routing/mock/centralized_client.go index e57d03239..ac3f938cc 100644 --- a/routing/mock/centralized_client.go +++ b/routing/mock/centralized_client.go @@ -4,13 +4,11 @@ import ( "context" "time" - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log" - - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-core/routing" - "github.com/libp2p/go-libp2p-testing/net" - + tnet "github.com/libp2p/go-libp2p-testing/net" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" ma "github.com/multiformats/go-multiaddr" ) @@ -22,13 +20,13 @@ type client struct { peer tnet.Identity } -// FIXME(brian): is this method meant to simulate putting a value into the network? +// PutValue FIXME(brian): is this method meant to simulate putting a value into the network? func (c *client) PutValue(ctx context.Context, key string, val []byte, opts ...routing.Option) error { log.Debugf("PutValue: %s", key) return c.vs.PutValue(ctx, key, val, opts...) } -// FIXME(brian): is this method meant to simulate getting a value from the network? +// GetValue FIXME(brian): is this method meant to simulate getting a value from the network? func (c *client) GetValue(ctx context.Context, key string, opts ...routing.Option) ([]byte, error) { log.Debugf("GetValue: %s", key) return c.vs.GetValue(ctx, key, opts...) diff --git a/routing/mock/centralized_server.go b/routing/mock/centralized_server.go index 9c8bd853c..8ff614ca7 100644 --- a/routing/mock/centralized_server.go +++ b/routing/mock/centralized_server.go @@ -6,14 +6,12 @@ import ( "sync" "time" - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" - - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-testing/net" - - offline "github.com/ipfs/go-ipfs-routing/offline" + "github.com/ipfs/go-ipfs-routing/offline" + tnet "github.com/libp2p/go-libp2p-testing/net" + "github.com/libp2p/go-libp2p/core/peer" ) // server is the mockrouting.Client's private interface to the routing server diff --git a/routing/mock/centralized_test.go b/routing/mock/centralized_test.go index fc832cf7a..6c36e492a 100644 --- a/routing/mock/centralized_test.go +++ b/routing/mock/centralized_test.go @@ -8,9 +8,8 @@ import ( "github.com/ipfs/go-cid" delay "github.com/ipfs/go-ipfs-delay" u "github.com/ipfs/go-ipfs-util" - - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-testing/net" + tnet "github.com/libp2p/go-libp2p-testing/net" + "github.com/libp2p/go-libp2p/core/peer" ) func TestKeyNotFound(t *testing.T) { diff --git a/routing/mock/interface.go b/routing/mock/interface.go index 6b0206534..35430a72c 100644 --- a/routing/mock/interface.go +++ b/routing/mock/interface.go @@ -9,10 +9,9 @@ import ( ds "github.com/ipfs/go-datastore" delay "github.com/ipfs/go-ipfs-delay" - - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-core/routing" - "github.com/libp2p/go-libp2p-testing/net" + tnet "github.com/libp2p/go-libp2p-testing/net" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" ) // MockValidator is a record validator that always returns success. diff --git a/routing/none/none_client.go b/routing/none/none_client.go index 2ba1a8f8f..6f400b54a 100644 --- a/routing/none/none_client.go +++ b/routing/none/none_client.go @@ -5,14 +5,12 @@ import ( "context" "errors" - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" - - "github.com/libp2p/go-libp2p-core/host" - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-core/routing" - record "github.com/libp2p/go-libp2p-record" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" ) type nilclient struct { diff --git a/routing/offline/offline.go b/routing/offline/offline.go index 0b3083c59..7e4292f56 100644 --- a/routing/offline/offline.go +++ b/routing/offline/offline.go @@ -8,16 +8,14 @@ import ( "errors" "time" - proto "github.com/gogo/protobuf/proto" - cid "github.com/ipfs/go-cid" + "github.com/gogo/protobuf/proto" + "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dshelp "github.com/ipfs/go-ipfs-ds-help" - - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-core/routing" - record "github.com/libp2p/go-libp2p-record" pb "github.com/libp2p/go-libp2p-record/pb" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" ) // ErrOffline is returned when trying to perform operations that diff --git a/routing/offline/offline_test.go b/routing/offline/offline_test.go index 00e0174ba..9a17e8689 100644 --- a/routing/offline/offline_test.go +++ b/routing/offline/offline_test.go @@ -8,8 +8,8 @@ import ( cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" - "github.com/libp2p/go-libp2p-core/routing" - "github.com/libp2p/go-libp2p-core/test" + "github.com/libp2p/go-libp2p/core/routing" + "github.com/libp2p/go-libp2p/core/test" mh "github.com/multiformats/go-multihash" ) From fc4155a824f26f21fbebd0aa0c3c5fa7b42c0a41 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 20 Sep 2022 23:06:50 +0200 Subject: [PATCH 3738/3817] refactor: avoid nested code https://github.com/ipfs/go-ipns/pull/41#discussion_r975078116 This commit was moved from ipfs/go-ipns@237111a8d232ee4adfe2c8907fa95f779acf7170 --- ipns/ipns.go | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/ipns/ipns.go b/ipns/ipns.go index 5f9eb25d1..fae3f6e2c 100644 --- a/ipns/ipns.go +++ b/ipns/ipns.go @@ -133,28 +133,27 @@ func createCborDataForIpnsEntry(e *pb.IpnsEntry) ([]byte, error) { // Validates validates the given IPNS entry against the given public key. func Validate(pk ic.PubKey, entry *pb.IpnsEntry) error { // Check the ipns record signature with the public key - - // Check v2 signature if it's available - if entry.GetSignatureV2() != nil { - sig2Data, err := ipnsEntryDataForSigV2(entry) - if err != nil { - return fmt.Errorf("could not compute signature data: %w", err) - } - if ok, err := pk.Verify(sig2Data, entry.GetSignatureV2()); err != nil || !ok { - return ErrSignature - } - - // TODO: If we switch from pb.IpnsEntry to a more generic IpnsRecord type then perhaps we should only check - // this if there is no v1 signature. In the meanwhile this helps avoid some potential rough edges around people - // checking the entry fields instead of doing CBOR decoding everywhere. - if err := validateCborDataMatchesPbData(entry); err != nil { - return err - } - } else { + if entry.GetSignatureV2() == nil { // always error if no valid signature could be found return ErrSignature } + sig2Data, err := ipnsEntryDataForSigV2(entry) + if err != nil { + return fmt.Errorf("could not compute signature data: %w", err) + } + if ok, err := pk.Verify(sig2Data, entry.GetSignatureV2()); err != nil || !ok { + return ErrSignature + } + + // TODO: If we switch from pb.IpnsEntry to a more generic IpnsRecord type then perhaps we should only check + // this if there is no v1 signature. In the meanwhile this helps avoid some potential rough edges around people + // checking the entry fields instead of doing CBOR decoding everywhere. + // See https://github.com/ipfs/go-ipns/pull/42 for next steps here + if err := validateCborDataMatchesPbData(entry); err != nil { + return err + } + eol, err := GetEOL(entry) if err != nil { return err From f748bd4f578b55eeeee5b7026f5857e57b279e38 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 20 Sep 2022 23:50:44 +0200 Subject: [PATCH 3739/3817] fix: MaxRecordSize of 10 KiB See rationale: https://github.com/ipfs/specs/pull/319#discussion_r968304911 This commit was moved from ipfs/go-ipns@e5d96b34e49b2e60e6dd9985b607185d3be4f7e5 --- ipns/errors.go | 7 +++++++ ipns/ipns.go | 5 +++++ ipns/validate_test.go | 20 ++++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/ipns/errors.go b/ipns/errors.go index ebcd4e263..d78aafffa 100644 --- a/ipns/errors.go +++ b/ipns/errors.go @@ -35,3 +35,10 @@ var ErrPublicKeyMismatch = errors.New("public key in record did not match expect // ErrBadRecord should be returned when an ipns record cannot be unmarshalled var ErrBadRecord = errors.New("record could not be unmarshalled") + +// 10 KiB limit defined in https://github.com/ipfs/specs/pull/319 +const MaxRecordSize int = 10 << (10 * 1) + +// ErrRecordSize should be returned when an ipns record is +// invalid due to being too big +var ErrRecordSize = errors.New("record exceeds allowed size limit") diff --git a/ipns/ipns.go b/ipns/ipns.go index fae3f6e2c..8782356cf 100644 --- a/ipns/ipns.go +++ b/ipns/ipns.go @@ -132,6 +132,11 @@ func createCborDataForIpnsEntry(e *pb.IpnsEntry) ([]byte, error) { // Validates validates the given IPNS entry against the given public key. func Validate(pk ic.PubKey, entry *pb.IpnsEntry) error { + // Make sure max size is respected + if entry.Size() > MaxRecordSize { + return ErrRecordSize + } + // Check the ipns record signature with the public key if entry.GetSignatureV2() == nil { // always error if no valid signature could be found diff --git a/ipns/validate_test.go b/ipns/validate_test.go index a0d7f7e02..0b38329fc 100644 --- a/ipns/validate_test.go +++ b/ipns/validate_test.go @@ -274,6 +274,26 @@ func TestSignatureV1Ignored(t *testing.T) { } } +func TestMaxSizeValidate(t *testing.T) { + goodeol := time.Now().Add(time.Hour) + + sk, pk, err := crypto.GenerateEd25519Key(rand.New(rand.NewSource(42))) + if err != nil { + t.Fatal(err) + } + + // Create record over the max size (value+other fields) + value := make([]byte, MaxRecordSize) + entry, err := Create(sk, value, 1, goodeol, 0) + if err != nil { + t.Fatal(err) + } + // Must fail with ErrRecordSize + if err := Validate(pk, entry); !errors.Is(err, ErrRecordSize) { + t.Fatal(err) + } +} + func TestCborDataCanonicalization(t *testing.T) { goodeol := time.Now().Add(time.Hour) From 6866c15931e2e65d844344e29b852e516cc7f9c2 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Wed, 28 Sep 2022 18:25:04 +1000 Subject: [PATCH 3740/3817] feat: check that the CidBuilder hasher is usable Ref: https://github.com/ipfs/go-merkledag/issues/90 This commit was moved from ipfs/go-merkledag@bb220e822546588d78e161591f38ff80613c2c4f --- ipld/merkledag/merkledag_test.go | 35 ++++++++++++++++++++++++++++++++ ipld/merkledag/node.go | 23 ++++++++++++++++----- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index abbdbfc3c..b433a3292 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -25,6 +25,7 @@ import ( u "github.com/ipfs/go-ipfs-util" ipld "github.com/ipfs/go-ipld-format" prime "github.com/ipld/go-ipld-prime" + mh "github.com/multiformats/go-multihash" ) // makeDepthTestingGraph makes a small DAG with two levels. The level-two @@ -73,6 +74,40 @@ func traverseAndCheck(t *testing.T, root ipld.Node, ds ipld.DAGService, hasF fun } } +func TestBadBuilderEncode(t *testing.T) { + n := NodeWithData([]byte("boop")) + _, err := n.EncodeProtobuf(false) + if err != nil { + t.Fatal(err) + } + err = n.SetCidBuilder( + &cid.Prefix{ + MhType: mh.SHA2_256, + MhLength: -1, + Version: 1, + Codec: cid.DagProtobuf, + }, + ) + if err != nil { + t.Fatal(err) + } + err = n.SetCidBuilder( + &cid.Prefix{ + MhType: mh.SHA2_256_TRUNC254_PADDED, + MhLength: 256, + Version: 1, + Codec: cid.DagProtobuf, + }, + ) + if err == nil { + t.Fatal("expected SetCidBuilder to error on unusable hasher") + } + _, err = n.EncodeProtobuf(false) + if err != nil { + t.Fatalf("expected EncodeProtobuf to use safe CidBuilder: %v", err) + } +} + func TestNode(t *testing.T) { n1 := NodeWithData([]byte("beep")) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index a3a719769..53fd0a4c4 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -13,6 +13,7 @@ import ( dagpb "github.com/ipld/go-codec-dagpb" ipld "github.com/ipld/go-ipld-prime" mh "github.com/multiformats/go-multihash" + mhcore "github.com/multiformats/go-multihash/core" ) // Common errors @@ -96,14 +97,26 @@ func (n *ProtoNode) CidBuilder() cid.Builder { } // SetCidBuilder sets the CID builder if it is non nil, if nil then it -// is reset to the default value -func (n *ProtoNode) SetCidBuilder(builder cid.Builder) { +// is reset to the default value. An error will be returned if the builder +// is not usable. +func (n *ProtoNode) SetCidBuilder(builder cid.Builder) error { if builder == nil { n.builder = v0CidPrefix - } else { - n.builder = builder.WithCodec(cid.DagProtobuf) - n.cached = cid.Undef + return nil + } + if p, ok := builder.(*cid.Prefix); ok { + mhLen := p.MhLength + if mhLen <= 0 { + mhLen = -1 + } + _, err := mhcore.GetVariableHasher(p.MhType, mhLen) + if err != nil { + return err + } } + n.builder = builder.WithCodec(cid.DagProtobuf) + n.cached = cid.Undef + return nil } // LinkSlice is a slice of format.Links From a9af67f100e7966b2199fc9829380d368a3558af Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 29 Sep 2022 20:33:58 +1000 Subject: [PATCH 3741/3817] feat: check links on setting and sanitise on encoding Attempt to keep ProtoNode always in a state that can be encoded without errors to avoid cases where the panicing methods may be forced to panic on error. go-codec-dagpb will error when encoding either of the following cases: * The Hash field in links should always be set, cannot be cid.Undef * The Tsize field needs to fit into an int64, otherwise it'll overflow to negative which is not allowed Error on cases where a user may attempt to set links that will eventually error on encode. Then when we do encode, silently handle these cases if they manage to slip through (e.g. if they come in from a decoded block with a bad form). This commit was moved from ipfs/go-merkledag@2a1cd355824c8eaa2bbca003e7ce3cdfdf41d294 --- ipld/merkledag/coding.go | 19 ++++-- ipld/merkledag/merkledag_test.go | 101 ++++++++++++++++++++++++++++++- ipld/merkledag/node.go | 34 ++++++++++- 3 files changed, 143 insertions(+), 11 deletions(-) diff --git a/ipld/merkledag/coding.go b/ipld/merkledag/coding.go index 0a71b0ac5..811e25fee 100644 --- a/ipld/merkledag/coding.go +++ b/ipld/merkledag/coding.go @@ -80,13 +80,20 @@ func (n *ProtoNode) marshalImmutable() (*immutableProtoNode, error) { nd, err := qp.BuildMap(dagpb.Type.PBNode, 2, func(ma ipld.MapAssembler) { qp.MapEntry(ma, "Links", qp.List(int64(len(links)), func(la ipld.ListAssembler) { for _, link := range links { - qp.ListEntry(la, qp.Map(3, func(ma ipld.MapAssembler) { - if link.Cid.Defined() { + // it shouldn't be possible to get here with an undefined CID, but in + // case it is we're going to drop this link from the encoded form + // entirely + if link.Cid.Defined() { + qp.ListEntry(la, qp.Map(3, func(ma ipld.MapAssembler) { qp.MapEntry(ma, "Hash", qp.Link(cidlink.Link{Cid: link.Cid})) - } - qp.MapEntry(ma, "Name", qp.String(link.Name)) - qp.MapEntry(ma, "Tsize", qp.Int(int64(link.Size))) - })) + qp.MapEntry(ma, "Name", qp.String(link.Name)) + sz := int64(link.Size) + if sz < 0 { // overflow, >MaxInt64 is almost certainly an error + sz = 0 + } + qp.MapEntry(ma, "Tsize", qp.Int(sz)) + })) + } } })) if n.data != nil { diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index b433a3292..e7ca4fb1d 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -3,10 +3,12 @@ package merkledag_test import ( "bytes" "context" + "encoding/hex" "encoding/json" "errors" "fmt" "io" + "math" "math/rand" "strings" "sync" @@ -28,6 +30,11 @@ import ( mh "github.com/multiformats/go-multihash" ) +var someCid cid.Cid = func() cid.Cid { + c, _ := cid.Cast([]byte{1, 85, 0, 5, 0, 1, 2, 3, 4}) + return c +}() + // makeDepthTestingGraph makes a small DAG with two levels. The level-two // nodes are both children of the root and of one of the level 1 nodes. // This is meant to test the Walk*Depth functions. @@ -108,6 +115,97 @@ func TestBadBuilderEncode(t *testing.T) { } } +func TestLinkChecking(t *testing.T) { + cases := []struct { + name string + fn func(*ProtoNode) error + }{ + { + name: "AddRawLink overflow Tsize", + fn: func(n *ProtoNode) error { + return n.AddRawLink("foo", &ipld.Link{Size: math.MaxUint64, Cid: someCid}) + }, + }, + + { + name: "AddRawLink undefined CID", + fn: func(n *ProtoNode) error { + return n.AddRawLink("foo", &ipld.Link{Cid: cid.Undef}) + }, + }, + + { + name: "SetLinks overflow Tsize", + fn: func(n *ProtoNode) error { + return n.SetLinks([]*ipld.Link{{Size: math.MaxUint64, Cid: someCid}}) + }, + }, + + { + name: "SetLinks undefined CID", + fn: func(n *ProtoNode) error { + return n.SetLinks([]*ipld.Link{{Cid: cid.Undef}}) + }, + }, + + { + name: "UnmarshalJSON overflow Tsize", + fn: func(n *ProtoNode) error { + return n.UnmarshalJSON([]byte(`{"data":null,"links":[{"Name":"","Size":18446744073709549568,"Cid":{"/":"QmNPWHBrVQiiV8FpyNuEPhB9E2rbvdy9Yx79EY1EJuyf9o"}}]}`)) + }, + }, + + { + name: "UnmarshalJSON undefined CID", + fn: func(n *ProtoNode) error { + return n.UnmarshalJSON([]byte(`{"data":null,"links":[{"Name":"","Size":100}]}`)) + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + n := NodeWithData([]byte("boop")) + err := tc.fn(n) + if err == nil { + t.Fatal("expected error") + } + }) + } + + t.Run("round-trip block with bad Tsize", func(t *testing.T) { + badblock, _ := hex.DecodeString("122f0a22122000bb3604d2ecd386227007c548249521fbb9a394e1e26460091d0a692888e7361880f0ffffffffffffff01") + n, err := DecodeProtobuf(badblock) + if err != nil { + t.Fatal(err) + } + // sanity + if len(n.Links()) != 1 { + t.Fatal("expected a link") + } + // sanity + if n.Links()[0].Size <= math.MaxInt64 { + t.Fatal("expected link Tsize to be oversized") + } + + // forced round-trip + byts, err := n.EncodeProtobuf(true) + if err != nil { + t.Fatal(err) + } + n, err = DecodeProtobuf(byts) + if err != nil { + t.Fatal(err) + } + if len(n.Links()) != 1 { + t.Fatal("expected a link") + } + if n.Links()[0].Size != 0 { + t.Fatal("expected link Tsize to be truncated on reencode") + } + }) +} + func TestNode(t *testing.T) { n1 := NodeWithData([]byte("beep")) @@ -604,7 +702,7 @@ func TestGetRawNodes(t *testing.T) { func TestProtoNodeResolve(t *testing.T) { nd := new(ProtoNode) - nd.SetLinks([]*ipld.Link{{Name: "foo"}}) + nd.SetLinks([]*ipld.Link{{Name: "foo", Cid: someCid}}) lnk, left, err := nd.ResolveLink([]string{"foo", "bar"}) if err != nil { @@ -959,7 +1057,6 @@ func TestLinkSorting(t *testing.T) { if err != nil { t.Fatal(err) } - someCid, _ := cid.Cast([]byte{1, 85, 0, 5, 0, 1, 2, 3, 4}) if err = node.AddRawLink("foo", &ipld.Link{ Size: 10, Cid: someCid, diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 53fd0a4c4..1dad902fc 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -3,7 +3,9 @@ package merkledag import ( "context" "encoding/json" + "errors" "fmt" + "math" "sort" blocks "github.com/ipfs/go-block-format" @@ -163,11 +165,15 @@ func (n *ProtoNode) AddNodeLink(name string, that format.Node) error { // RemoveNodeLink for the same link, will not result in an identically encoded // form as the links will have been sorted. func (n *ProtoNode) AddRawLink(name string, l *format.Link) error { - n.links = append(n.links, &format.Link{ + lnk := &format.Link{ Name: name, Size: l.Size, Cid: l.Cid, - }) + } + if err := checkLink(lnk); err != nil { + return err + } + n.links = append(n.links, lnk) n.linksDirty = true // needs a sort n.encoded = nil return nil @@ -360,10 +366,26 @@ func (n *ProtoNode) UnmarshalJSON(b []byte) error { // them until we mutate this node since we're representing the current, // as-serialized state. So n.linksDirty is not set here. n.links = s.Links + for _, lnk := range s.Links { + if err := checkLink(lnk); err != nil { + return err + } + } + n.encoded = nil return nil } +func checkLink(lnk *format.Link) error { + if lnk.Size > math.MaxInt64 { + return fmt.Errorf("value of Tsize is too large: %d", lnk.Size) + } + if !lnk.Cid.Defined() { + return errors.New("link must have a value Cid value") + } + return nil +} + // MarshalJSON returns a JSON representation of the node. func (n *ProtoNode) MarshalJSON() ([]byte, error) { if n.linksDirty { @@ -429,10 +451,16 @@ func (n *ProtoNode) Links() []*format.Link { // SetLinks replaces the node links with a copy of the provided links. Sorting // will be applied to the list. -func (n *ProtoNode) SetLinks(links []*format.Link) { +func (n *ProtoNode) SetLinks(links []*format.Link) error { + for _, lnk := range links { + if err := checkLink(lnk); err != nil { + return err + } + } n.links = append([]*format.Link(nil), links...) n.linksDirty = true // needs a sort n.encoded = nil + return nil } // Resolve is an alias for ResolveLink. From e072e42fe9ec9803d3de4443f806aaaaad96386b Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 29 Sep 2022 22:00:44 +1000 Subject: [PATCH 3742/3817] fix: simplify Cid generation cache & usage This commit was moved from ipfs/go-merkledag@3b5c1ef458d2bca38cf6700411ea62cd596a5149 --- ipld/merkledag/node.go | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 1dad902fc..7b9ad4f96 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -406,19 +406,12 @@ func (n *ProtoNode) MarshalJSON() ([]byte, error) { // Cid returns the node's Cid, calculated according to its prefix // and raw data contents. func (n *ProtoNode) Cid() cid.Cid { - if n.encoded != nil && n.cached.Defined() { - return n.cached - } - - c, err := n.CidBuilder().Sum(n.RawData()) - if err != nil { - // programmer error - err = fmt.Errorf("invalid CID of length %d: %x: %v", len(n.RawData()), n.RawData(), err) + // re-encode if necessary and we'll get a new cached CID + if _, err := n.EncodeProtobuf(false); err != nil { panic(err) } - n.cached = c - return c + return n.cached } // String prints the node's Cid. @@ -428,14 +421,7 @@ func (n *ProtoNode) String() string { // Multihash hashes the encoded data of this node. func (n *ProtoNode) Multihash() mh.Multihash { - // NOTE: EncodeProtobuf generates the hash and puts it in n.cached. - _, err := n.EncodeProtobuf(false) - if err != nil { - // Note: no possibility exists for an error to be returned through here - panic(err) - } - - return n.cached.Hash() + return n.Cid().Hash() } // Links returns a copy of the node's links. From 212136162409c1db29838e202d849e05a017bd71 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 30 Sep 2022 13:37:13 +1000 Subject: [PATCH 3743/3817] doc: document potential panics and how to avoid them This commit was moved from ipfs/go-merkledag@62ec19c946b29623492c8962fa482d8ab3e72822 --- ipld/merkledag/node.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 7b9ad4f96..93084ed0d 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -271,6 +271,11 @@ func (n *ProtoNode) Copy() format.Node { return nnode } +// RawData returns the encoded byte form of this node. +// +// Note that this method can panic if a new encode is required and there is an +// error performing the encode. To avoid a panic, use node.EncodeProtobuf(false) +// instead (or prior to calling RawData) and check for its returned error value. func (n *ProtoNode) RawData() []byte { out, err := n.EncodeProtobuf(false) if err != nil { @@ -405,21 +410,35 @@ func (n *ProtoNode) MarshalJSON() ([]byte, error) { // Cid returns the node's Cid, calculated according to its prefix // and raw data contents. +// +// Note that this method can panic if a new encode is required and there is an +// error performing the encode. To avoid a panic, call +// node.EncodeProtobuf(false) prior to calling Cid and check for its returned +// error value. func (n *ProtoNode) Cid() cid.Cid { // re-encode if necessary and we'll get a new cached CID if _, err := n.EncodeProtobuf(false); err != nil { panic(err) } - return n.cached } // String prints the node's Cid. +// +// Note that this method can panic if a new encode is required and there is an +// error performing the encode. To avoid a panic, call +// node.EncodeProtobuf(false) prior to calling String and check for its returned +// error value. func (n *ProtoNode) String() string { return n.Cid().String() } // Multihash hashes the encoded data of this node. +// +// Note that this method can panic if a new encode is required and there is an +// error performing the encode. To avoid a panic, call +// node.EncodeProtobuf(false) prior to calling Multihash and check for its +// returned error value. func (n *ProtoNode) Multihash() mh.Multihash { return n.Cid().Hash() } From 105eb5fd191434d8f5375ebf5b18cfecff042b23 Mon Sep 17 00:00:00 2001 From: Antonio Navarro Perez Date: Wed, 12 Oct 2022 17:02:12 +0200 Subject: [PATCH 3744/3817] Fix: panic when childer is nil (#127) * Fix panic when childer is nil Signed-off-by: Antonio Navarro Perez * Apply suggestions from code review Co-authored-by: Gus Eggert Signed-off-by: Antonio Navarro Perez Co-authored-by: Gus Eggert This commit was moved from ipfs/go-unixfs@fede2ed6837d42eb20c2e125025ee591b7eda04f --- unixfs/hamt/hamt.go | 22 +++++++++++++++++----- unixfs/hamt/hamt_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index d9947770f..c6cae88ea 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -86,7 +86,12 @@ type Shard struct { // NewShard creates a new, empty HAMT shard with the given size. func NewShard(dserv ipld.DAGService, size int) (*Shard, error) { - ds, err := makeShard(dserv, size) + return NewShardValue(dserv, size, "", nil) +} + +// NewShardValue creates a new, empty HAMT shard with the given key, value and size. +func NewShardValue(dserv ipld.DAGService, size int, key string, value *ipld.Link) (*Shard, error) { + ds, err := makeShard(dserv, size, key, value) if err != nil { return nil, err } @@ -96,7 +101,7 @@ func NewShard(dserv ipld.DAGService, size int) (*Shard, error) { return ds, nil } -func makeShard(ds ipld.DAGService, size int) (*Shard, error) { +func makeShard(ds ipld.DAGService, size int, key string, val *ipld.Link) (*Shard, error) { lg2s, err := Logtwo(size) if err != nil { return nil, err @@ -109,6 +114,9 @@ func makeShard(ds ipld.DAGService, size int) (*Shard, error) { childer: newChilder(ds, size), tableSize: size, dserv: ds, + + key: key, + val: val, } s.childer.sd = s @@ -138,7 +146,7 @@ func NewHamtFromDag(dserv ipld.DAGService, nd ipld.Node) (*Shard, error) { size := int(fsn.Fanout()) - ds, err := makeShard(dserv, size) + ds, err := makeShard(dserv, size, "", nil) if err != nil { return nil, err } @@ -214,7 +222,7 @@ func (ds *Shard) Node() (ipld.Node, error) { func (ds *Shard) makeShardValue(lnk *ipld.Link) (*Shard, error) { lnk2 := *lnk - s, err := makeShard(ds.dserv, ds.tableSize) + s, err := makeShard(ds.dserv, ds.tableSize, "", nil) if err != nil { return nil, err } @@ -795,7 +803,11 @@ func (s *childer) insert(key string, lnk *ipld.Link, idx int) error { lnk.Name = s.sd.linkNamePrefix(idx) + key i := s.sliceIndex(idx) - sd := &Shard{key: key, val: lnk} + + sd, err := NewShardValue(s.dserv, 256, key, lnk) + if err != nil { + return err + } s.children = append(s.children[:i], append([]*Shard{sd}, s.children[i:]...)...) s.links = append(s.links[:i], append([]*ipld.Link{nil}, s.links[i:]...)...) diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 8d0b93889..150b97b90 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -435,6 +435,38 @@ func TestDuplicateAddShard(t *testing.T) { } } +// fix https://github.com/ipfs/kubo/issues/9063 +func TestSetLink(t *testing.T) { + ds := mdtest.Mock() + dir, _ := NewShard(ds, 256) + _, s, err := makeDir(ds, 300) + if err != nil { + t.Fatal(err) + } + + lnk, err := s.Link() + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + err = dir.SetLink(ctx, "test", lnk) + if err != nil { + t.Fatal(err) + } + + if len(dir.childer.children) != 1 { + t.Fatal("no child") + } + + for _, sh := range dir.childer.children { + if sh.childer == nil { + t.Fatal("no childer on shard") + } + } +} + func TestLoadFailsFromNonShard(t *testing.T) { ds := mdtest.Mock() nd := ft.EmptyDirNode() From 8399426c4e7da1c17d5e08eb3e6b4eb6ff3796b8 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 20 Oct 2022 11:26:59 +0200 Subject: [PATCH 3745/3817] add a `SkipNext` method on block reader (#338) * add a `SkipNext` method on block reader Co-authored-by: Rod Vagg This commit was moved from ipld/go-car@50e029b9990efc2a9f70df098445fe5a5cf10df6 --- ipld/car/v2/block_reader.go | 97 ++++++++++++++++++++++++- ipld/car/v2/block_reader_test.go | 36 +++++++++ ipld/car/v2/internal/carv1/util/util.go | 18 +++-- 3 files changed, 144 insertions(+), 7 deletions(-) diff --git a/ipld/car/v2/block_reader.go b/ipld/car/v2/block_reader.go index 252885c31..0c38412fd 100644 --- a/ipld/car/v2/block_reader.go +++ b/ipld/car/v2/block_reader.go @@ -9,6 +9,7 @@ import ( "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/carv1/util" internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/multiformats/go-varint" ) // BlockReader facilitates iteration over CAR blocks for both CARv1 and CARv2. @@ -20,8 +21,10 @@ type BlockReader struct { Roots []cid.Cid // Used internally only, by BlockReader.Next during iteration over blocks. - r io.Reader - opts Options + r io.Reader + offset uint64 + readerSize int64 + opts Options } // NewBlockReader instantiates a new BlockReader facilitating iteration over blocks in CARv1 or @@ -52,6 +55,8 @@ func NewBlockReader(r io.Reader, opts ...Option) (*BlockReader, error) { // Simply populate br.Roots and br.r without modifying r. br.Roots = pragmaOrV1Header.Roots br.r = r + br.readerSize = -1 + br.offset, _ = carv1.HeaderSize(pragmaOrV1Header) case 2: // If the version is 2: // 1. Read CARv2 specific header to locate the inner CARv1 data payload offset and size. @@ -75,6 +80,8 @@ func NewBlockReader(r io.Reader, opts ...Option) (*BlockReader, error) { if _, err := rs.Seek(int64(v2h.DataOffset)-PragmaSize-HeaderSize, io.SeekCurrent); err != nil { return nil, err } + br.offset = uint64(v2h.DataOffset) + br.readerSize = int64(v2h.DataOffset + v2h.DataSize) // Set br.r to a LimitReader reading from r limited to dataSize. br.r = io.LimitReader(r, int64(v2h.DataSize)) @@ -122,5 +129,91 @@ func (br *BlockReader) Next() (blocks.Block, error) { return nil, fmt.Errorf("mismatch in content integrity, expected: %s, got: %s", c, hashed) } + ss := uint64(c.ByteLen()) + uint64(len(data)) + br.offset += uint64(varint.UvarintSize(ss)) + ss return blocks.NewBlockWithCid(data, c) } + +type BlockMetadata struct { + cid.Cid + Offset uint64 + Size uint64 +} + +// SkipNext jumps over the next block, returning metadata about what it is (the CID, offset, and size). +// Like Next it will return an io.EOF once it has reached the end. +// +// If the underlying reader used by the BlockReader is actually a ReadSeeker, this method will attempt to +// seek over the underlying data rather than reading it into memory. +func (br *BlockReader) SkipNext() (*BlockMetadata, error) { + sctSize, err := util.LdReadSize(br.r, br.opts.ZeroLengthSectionAsEOF, br.opts.MaxAllowedSectionSize) + if err != nil { + return nil, err + } + + if sctSize == 0 { + _, _, err := cid.CidFromBytes([]byte{}) + return nil, err + } + + cidSize, c, err := cid.CidFromReader(io.LimitReader(br.r, int64(sctSize))) + if err != nil { + return nil, err + } + + blkSize := sctSize - uint64(cidSize) + if brs, ok := br.r.(io.ReadSeeker); ok { + // carv1 and we don't know the size, so work it out and cache it + if br.readerSize == -1 { + cur, err := brs.Seek(0, io.SeekCurrent) + if err != nil { + return nil, err + } + end, err := brs.Seek(0, io.SeekEnd) + if err != nil { + return nil, err + } + br.readerSize = end + if _, err = brs.Seek(cur, io.SeekStart); err != nil { + return nil, err + } + } + // seek. + finalOffset, err := brs.Seek(int64(blkSize), io.SeekCurrent) + if err != nil { + return nil, err + } + if finalOffset != int64(br.offset)+int64(sctSize)+int64(varint.UvarintSize(sctSize)) { + return nil, fmt.Errorf("unexpected length") + } + if finalOffset > br.readerSize { + return nil, io.ErrUnexpectedEOF + } + br.offset = uint64(finalOffset) + return &BlockMetadata{ + c, + uint64(finalOffset) - sctSize - uint64(varint.UvarintSize(sctSize)), + blkSize, + }, nil + } + + // read to end. + readCnt, err := io.CopyN(io.Discard, br.r, int64(blkSize)) + if err != nil { + if err == io.EOF { + return nil, io.ErrUnexpectedEOF + } + return nil, err + } + if readCnt != int64(blkSize) { + return nil, fmt.Errorf("unexpected length") + } + origOffset := br.offset + br.offset += uint64(varint.UvarintSize(sctSize)) + sctSize + + return &BlockMetadata{ + c, + origOffset, + blkSize, + }, nil +} diff --git a/ipld/car/v2/block_reader_test.go b/ipld/car/v2/block_reader_test.go index 384a6ed88..b5958c7f0 100644 --- a/ipld/car/v2/block_reader_test.go +++ b/ipld/car/v2/block_reader_test.go @@ -3,6 +3,7 @@ package car_test import ( "bytes" "encoding/hex" + "fmt" "io" "os" "testing" @@ -106,6 +107,41 @@ func TestBlockReader_WithCarV1Consistency(t *testing.T) { } } }) + t.Run(tt.name+"-skipping-reads", func(t *testing.T) { + r := requireReaderFromPath(t, tt.path) + subject, err := carv2.NewBlockReader(r, carv2.ZeroLengthSectionAsEOF(tt.zerLenAsEOF)) + require.NoError(t, err) + + require.Equal(t, tt.wantVersion, subject.Version) + + var wantReader *carv1.CarReader + switch tt.wantVersion { + case 1: + wantReader = requireNewCarV1ReaderFromV1File(t, tt.path, tt.zerLenAsEOF) + case 2: + wantReader = requireNewCarV1ReaderFromV2File(t, tt.path, tt.zerLenAsEOF) + default: + require.Failf(t, "invalid test-case", "unknown wantVersion %v", tt.wantVersion) + } + require.Equal(t, wantReader.Header.Roots, subject.Roots) + + for { + gotBlock, gotErr := subject.SkipNext() + wantBlock, wantErr := wantReader.Next() + if wantErr != nil && gotErr == nil { + fmt.Printf("want was %+v\n", wantReader) + fmt.Printf("want was err, got was %+v / %d\n", gotBlock, gotBlock.Size) + } + require.Equal(t, wantErr, gotErr) + if gotErr == io.EOF { + break + } + if gotErr == nil { + require.Equal(t, wantBlock.Cid(), gotBlock.Cid) + require.Equal(t, uint64(len(wantBlock.RawData())), gotBlock.Size) + } + } + }) } } diff --git a/ipld/car/v2/internal/carv1/util/util.go b/ipld/car/v2/internal/carv1/util/util.go index 7963812e4..00cd885b2 100644 --- a/ipld/car/v2/internal/carv1/util/util.go +++ b/ipld/car/v2/internal/carv1/util/util.go @@ -65,20 +65,28 @@ func LdSize(d ...[]byte) uint64 { return sum + uint64(s) } -func LdRead(r io.Reader, zeroLenAsEOF bool, maxReadBytes uint64) ([]byte, error) { +func LdReadSize(r io.Reader, zeroLenAsEOF bool, maxReadBytes uint64) (uint64, error) { l, err := varint.ReadUvarint(internalio.ToByteReader(r)) if err != nil { // If the length of bytes read is non-zero when the error is EOF then signal an unclean EOF. if l > 0 && err == io.EOF { - return nil, io.ErrUnexpectedEOF + return 0, io.ErrUnexpectedEOF } - return nil, err + return 0, err } else if l == 0 && zeroLenAsEOF { - return nil, io.EOF + return 0, io.EOF } if l > maxReadBytes { // Don't OOM - return nil, ErrSectionTooLarge + return 0, ErrSectionTooLarge + } + return l, nil +} + +func LdRead(r io.Reader, zeroLenAsEOF bool, maxReadBytes uint64) ([]byte, error) { + l, err := LdReadSize(r, zeroLenAsEOF, maxReadBytes) + if err != nil { + return nil, err } buf := make([]byte, l) From 528afb5a92ec0ec41450e965ecda3c371f5390af Mon Sep 17 00:00:00 2001 From: Antonio Navarro Perez Date: Fri, 21 Oct 2022 15:37:00 +0200 Subject: [PATCH 3746/3817] docs: Update commands list This commit was moved from ipld/go-car@df02e3e53629d69505e2ad9d45c1b85a120c394b --- ipld/car/cmd/car/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ipld/car/cmd/car/README.md b/ipld/car/cmd/car/README.md index b9a4ca9a8..ef04a0dc3 100644 --- a/ipld/car/cmd/car/README.md +++ b/ipld/car/cmd/car/README.md @@ -16,11 +16,14 @@ USAGE: COMMANDS: create, c Create a car file detach-index Detach an index to a detached file + extract, x Extract the contents of a car when the car encodes UnixFS data filter, f Filter the CIDs in a car get-block, gb Get a block out of a car get-dag, gd Get a dag out of a car index, i write out the car with an index - list, l List the CIDs in a car + inspect verifies a car and prints a basic report about its contents + list, l, ls List the CIDs in a car + root Get the root CID of a car verify, v Verify a CAR is wellformed help, h Shows a list of commands or help for one command ``` From 3f6809177cecacb9ceb368e62656c225c3788ed6 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 18 Nov 2022 12:41:45 +0100 Subject: [PATCH 3747/3817] Add a debugging form for car files. (#341) * Add a debugging form for car files. This change adds two new sub-commands to the car CLI car debug file.car creates a patch-file-compatible representation of the content of the car file. Blocks will be represented in dag-json pretty-printed form. car compile file.patch will do the inverse process of building a car file from a debug patch file. CIDs will be re-compiled based on the contents of blocks, with links in parent blocks updated to point to the compiled values. Co-authored-by: Rod Vagg This commit was moved from ipld/go-car@dab0fd5bb19dead0da1377270f37be9acf858cf0 --- ipld/car/cmd/car/car.go | 26 ++ ipld/car/cmd/car/compile.go | 463 +++++++++++++++++++ ipld/car/cmd/car/testdata/script/compile.txt | 28 ++ 3 files changed, 517 insertions(+) create mode 100644 ipld/car/cmd/car/compile.go create mode 100644 ipld/car/cmd/car/testdata/script/compile.txt diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index 9957c8a54..c66232f7d 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -15,6 +15,19 @@ func main1() int { Name: "car", Usage: "Utility for working with car files", Commands: []*cli.Command{ + { + Name: "compile", + Usage: "compile a car file from a debug patch", + Action: CompileCar, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "output", + Aliases: []string{"o", "f"}, + Usage: "The file to write to", + TakesFile: true, + }, + }, + }, { Name: "create", Usage: "Create a car file", @@ -34,6 +47,19 @@ func main1() int { }, }, }, + { + Name: "debug", + Usage: "debug a car file", + Action: DebugCar, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "output", + Aliases: []string{"o", "f"}, + Usage: "The file to write to", + TakesFile: true, + }, + }, + }, { Name: "detach-index", Usage: "Detach an index to a detached file", diff --git a/ipld/car/cmd/car/compile.go b/ipld/car/cmd/car/compile.go new file mode 100644 index 000000000..0a74d1c7e --- /dev/null +++ b/ipld/car/cmd/car/compile.go @@ -0,0 +1,463 @@ +package main + +import ( + "bufio" + "bytes" + "context" + "fmt" + "io" + "os" + "regexp" + "strings" + "unicode/utf8" + + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + carv1 "github.com/ipld/go-car" + "github.com/ipld/go-car/util" + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/blockstore" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/codec" + "github.com/ipld/go-ipld-prime/codec/dagjson" + "github.com/ipld/go-ipld-prime/datamodel" + "github.com/ipld/go-ipld-prime/linking" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + "github.com/ipld/go-ipld-prime/node/basicnode" + "github.com/ipld/go-ipld-prime/storage/memstore" + "github.com/polydawn/refmt/json" + "github.com/urfave/cli/v2" + "golang.org/x/exp/slices" +) + +var ( + plusLineRegex = regexp.MustCompile(`^\+\+\+ ([\w-]+) ([\S]+ )?([\w]+)$`) +) + +// Compile is a command to translate between a human-debuggable patch-like format and a car file. +func CompileCar(c *cli.Context) error { + var err error + inStream := os.Stdin + if c.Args().Len() >= 1 { + inStream, err = os.Open(c.Args().First()) + if err != nil { + return err + } + } + + //parse headers. + br := bufio.NewReader(inStream) + header, _, err := br.ReadLine() + if err != nil { + return err + } + + v2 := strings.HasPrefix(string(header), "car compile --v2 ") + rest := strings.TrimPrefix(string(header), "car compile ") + if v2 { + rest = strings.TrimPrefix(rest, "--v2 ") + } + carName := strings.TrimSpace(rest) + + roots := make([]cid.Cid, 0) + for { + peek, err := br.Peek(4) + if err == io.EOF { + break + } else if err != nil { + return err + } + if bytes.Equal(peek, []byte("--- ")) { + break + } + rootLine, _, err := br.ReadLine() + if err != nil { + return err + } + if strings.HasPrefix(string(rootLine), "root ") { + var rCidS string + fmt.Sscanf(string(rootLine), "root %s", &rCidS) + rCid, err := cid.Parse(rCidS) + if err != nil { + return err + } + roots = append(roots, rCid) + } + } + + //parse blocks. + cidList := make([]cid.Cid, 0) + rawBlocks := make(map[cid.Cid][]byte) + rawCodecs := make(map[cid.Cid]string) + + for { + nextCid, mode, nextBlk, err := parsePatch(br) + if err == io.EOF { + break + } else if err != nil { + return err + } + rawBlocks[nextCid] = nextBlk + rawCodecs[nextCid] = mode + cidList = append(cidList, nextCid) + } + + // Re-create the original IPLD encoded blocks, but allowing for modifications of the + // patch data which may generate new CIDs; so we track the DAG relationships and + // rewrite CIDs in other referring where they get updated. + + // structure as a tree + childMap := make(map[cid.Cid][]cid.Cid) + for c := range rawBlocks { + if _, ok := childMap[c]; !ok { + childMap[c] = make([]cid.Cid, 0) + } + for d, blk := range rawBlocks { + if c.Equals(d) { + continue + } + if strings.Contains(string(blk), c.String()) { + if _, ok := childMap[d]; !ok { + childMap[d] = make([]cid.Cid, 0) + } + childMap[d] = append(childMap[d], c) + } else if strings.Contains(string(blk), string(c.Bytes())) { + if _, ok := childMap[d]; !ok { + childMap[d] = make([]cid.Cid, 0) + } + childMap[d] = append(childMap[d], c) + } + } + } + + // re-parse/re-build CIDs + outBlocks := make(map[cid.Cid][]byte) + for len(childMap) > 0 { + for origCid, kids := range childMap { + if len(kids) == 0 { + // compile to final cid + blk := rawBlocks[origCid] + finalCid, finalBlk, err := serializeBlock(c.Context, origCid.Prefix(), rawCodecs[origCid], blk) + if err != nil { + return err + } + outBlocks[finalCid] = finalBlk + idx := slices.Index(cidList, origCid) + cidList[idx] = finalCid + + // update other remaining nodes of the new cid. + for otherCid, otherKids := range childMap { + for i, otherKid := range otherKids { + if otherKid.Equals(origCid) { + if !finalCid.Equals(origCid) { + // update block + rawBlocks[otherCid] = bytes.ReplaceAll(rawBlocks[otherCid], origCid.Bytes(), finalCid.Bytes()) + rawBlocks[otherCid] = bytes.ReplaceAll(rawBlocks[otherCid], []byte(origCid.String()), []byte(finalCid.String())) + } + // remove from childMap + nok := append(otherKids[0:i], otherKids[i+1:]...) + childMap[otherCid] = nok + break // to next child map entry. + } + } + } + + delete(childMap, origCid) + } + } + } + + if !v2 { + // write output + outStream := os.Stdout + if c.IsSet("output") { + outFileName := c.String("output") + if outFileName == "" { + outFileName = carName + } + outFile, err := os.Create(outFileName) + if err != nil { + return err + } + defer outFile.Close() + outStream = outFile + } + + if err := carv1.WriteHeader(&carv1.CarHeader{ + Roots: roots, + Version: 1, + }, outStream); err != nil { + return err + } + for c, blk := range outBlocks { + if err := util.LdWrite(outStream, c.Bytes(), blk); err != nil { + return err + } + } + } else { + outFileName := c.String("output") + if outFileName == "" { + outFileName = carName + } + + if outFileName == "-" && !c.IsSet("output") { + return fmt.Errorf("cannot stream carv2's to stdout") + } + bs, err := blockstore.OpenReadWrite(outFileName, roots) + if err != nil { + return err + } + for _, bc := range cidList { + blk := outBlocks[bc] + ob, _ := blocks.NewBlockWithCid(blk, bc) + bs.Put(c.Context, ob) + } + return bs.Finalize() + } + + return nil +} + +func serializeBlock(ctx context.Context, codec cid.Prefix, encoding string, raw []byte) (cid.Cid, []byte, error) { + ls := cidlink.DefaultLinkSystem() + store := memstore.Store{Bag: map[string][]byte{}} + ls.SetReadStorage(&store) + ls.SetWriteStorage(&store) + b := basicnode.Prototype.Any.NewBuilder() + if encoding == "dag-json" { + if err := dagjson.Decode(b, bytes.NewBuffer(raw)); err != nil { + return cid.Undef, nil, err + } + } else if encoding == "raw" { + if err := b.AssignBytes(raw); err != nil { + return cid.Undef, nil, err + } + } else { + return cid.Undef, nil, fmt.Errorf("unknown encoding: %s", encoding) + } + lnk, err := ls.Store(linking.LinkContext{Ctx: ctx}, cidlink.LinkPrototype{Prefix: codec}, b.Build()) + if err != nil { + return cid.Undef, nil, err + } + outCid := lnk.(cidlink.Link).Cid + outBytes, outErr := store.Get(ctx, outCid.KeyString()) + return outCid, outBytes, outErr +} + +// DebugCar is a command to translate between a car file, and a human-debuggable patch-like format. +func DebugCar(c *cli.Context) error { + var err error + inStream := os.Stdin + inFile := "-" + if c.Args().Len() >= 1 { + inFile = c.Args().First() + inStream, err = os.Open(inFile) + if err != nil { + return err + } + } + + rd, err := carv2.NewBlockReader(inStream) + if err != nil { + return err + } + + // patch the header. + outStream := os.Stdout + if c.IsSet("output") { + outFileName := c.String("output") + outFile, err := os.Create(outFileName) + if err != nil { + return err + } + defer outFile.Close() + outStream = outFile + } + + outStream.WriteString("car compile ") + if rd.Version == 2 { + outStream.WriteString("--v2 ") + } + + outStream.WriteString(inFile + "\n") + for _, rt := range rd.Roots { + fmt.Fprintf(outStream, "root %s\n", rt.String()) + } + + // patch each block. + nxt, err := rd.Next() + if err != nil { + return err + } + for nxt != nil { + chunk, err := patch(c.Context, nxt.Cid(), nxt.RawData()) + if err != nil { + return err + } + outStream.Write(chunk) + + nxt, err = rd.Next() + if err == io.EOF { + return nil + } + } + + return nil +} + +func patch(ctx context.Context, c cid.Cid, blk []byte) ([]byte, error) { + ls := cidlink.DefaultLinkSystem() + store := memstore.Store{Bag: map[string][]byte{}} + ls.SetReadStorage(&store) + ls.SetWriteStorage(&store) + store.Put(ctx, c.KeyString(), blk) + node, err := ls.Load(linking.LinkContext{Ctx: ctx}, cidlink.Link{Cid: c}, basicnode.Prototype.Any) + if err != nil { + return nil, fmt.Errorf("could not load block: %q", err) + } + + outMode := "dag-json" + if node.Kind() == datamodel.Kind_Bytes && isPrintable(node) { + outMode = "raw" + } + finalBuf := bytes.NewBuffer(nil) + + if outMode == "dag-json" { + opts := dagjson.EncodeOptions{ + EncodeLinks: true, + EncodeBytes: true, + MapSortMode: codec.MapSortMode_Lexical, + } + if err := dagjson.Marshal(node, json.NewEncoder(finalBuf, json.EncodeOptions{Line: []byte{'\n'}, Indent: []byte{'\t'}}), opts); err != nil { + return nil, err + } + } else if outMode == "raw" { + nb, err := node.AsBytes() + if err != nil { + return nil, err + } + finalBuf.Write(nb) + } + + // figure out number of lines. + lcnt := strings.Count(finalBuf.String(), "\n") + crStr := " (no-end-cr)" + if finalBuf.Bytes()[len(finalBuf.Bytes())-1] == '\n' { + crStr = "" + } + + outBuf := bytes.NewBuffer(nil) + outBuf.WriteString("--- " + c.String() + "\n") + outBuf.WriteString("+++ " + outMode + crStr + " " + c.String() + "\n") + outBuf.WriteString(fmt.Sprintf("@@ -%d,%d +%d,%d @@\n", 0, lcnt, 0, lcnt)) + outBuf.Write(finalBuf.Bytes()) + outBuf.WriteString("\n") + return outBuf.Bytes(), nil +} + +func isPrintable(n ipld.Node) bool { + b, err := n.AsBytes() + if err != nil { + return false + } + if !utf8.Valid(b) { + return false + } + if bytes.ContainsAny(b, string([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x10, 0x11, 0x12, 0x13, 0x14, 0x16, 0x17, 0x18, 0x19, 0x1c, 0x1d, 0x1e, 0x1f})) { + return false + } + // check if would confuse the 'end of patch' checker. + if bytes.Contains(b, []byte("\n--- ")) { + return false + } + return true +} + +func parsePatch(br *bufio.Reader) (cid.Cid, string, []byte, error) { + // read initial line to parse CID. + l1, isPrefix, err := br.ReadLine() + if err != nil { + return cid.Undef, "", nil, err + } + if isPrefix { + return cid.Undef, "", nil, fmt.Errorf("unexpected long header l1") + } + var cs string + if _, err := fmt.Sscanf(string(l1), "--- %s", &cs); err != nil { + return cid.Undef, "", nil, fmt.Errorf("could not parse patch cid line (%s): %q", l1, err) + } + l2, isPrefix, err := br.ReadLine() + if err != nil { + return cid.Undef, "", nil, err + } + if isPrefix { + return cid.Undef, "", nil, fmt.Errorf("unexpected long header l2") + } + var mode string + var noEndReturn bool + matches := plusLineRegex.FindSubmatch(l2) + if len(matches) >= 2 { + mode = string(matches[1]) + } + if len(matches) < 2 || string(matches[len(matches)-1]) != cs { + return cid.Undef, "", nil, fmt.Errorf("mismatched cid lines: %v", string(l2)) + } + if len(matches[2]) > 0 { + noEndReturn = (string(matches[2]) == "(no-end-cr) ") + } + c, err := cid.Parse(cs) + if err != nil { + return cid.Undef, "", nil, err + } + + // skip over @@ line. + l3, isPrefix, err := br.ReadLine() + if err != nil { + return cid.Undef, "", nil, err + } + if isPrefix { + return cid.Undef, "", nil, fmt.Errorf("unexpected long header l3") + } + if !strings.HasPrefix(string(l3), "@@") { + return cid.Undef, "", nil, fmt.Errorf("unexpected missing chunk prefix") + } + + // keep going until next chunk or end. + outBuf := bytes.NewBuffer(nil) + for { + peek, err := br.Peek(4) + if err != nil && err != io.EOF { + return cid.Undef, "", nil, err + } + if bytes.Equal(peek, []byte("--- ")) { + break + } + // accumulate to buffer. + l, err := br.ReadBytes('\n') + if l != nil { + outBuf.Write(l) + } + if err == io.EOF { + break + } else if err != nil { + return cid.Undef, "", nil, err + } + } + + ob := outBuf.Bytes() + + // remove the final line return + if len(ob) > 2 && bytes.Equal(ob[len(ob)-2:], []byte("\r\n")) { + ob = ob[:len(ob)-2] + } else if len(ob) > 1 && bytes.Equal(ob[len(ob)-1:], []byte("\n")) { + ob = ob[:len(ob)-1] + } + + if noEndReturn && len(ob) > 2 && bytes.Equal(ob[len(ob)-2:], []byte("\r\n")) { + ob = ob[:len(ob)-2] + } else if noEndReturn && len(ob) > 1 && bytes.Equal(ob[len(ob)-1:], []byte("\n")) { + ob = ob[:len(ob)-1] + } + + return c, mode, ob, nil +} diff --git a/ipld/car/cmd/car/testdata/script/compile.txt b/ipld/car/cmd/car/testdata/script/compile.txt new file mode 100644 index 000000000..4607ca6c6 --- /dev/null +++ b/ipld/car/cmd/car/testdata/script/compile.txt @@ -0,0 +1,28 @@ +# debug a car to patch +car debug -o out.patch ${INPUTS}/sample-v1.car +! stderr . +grep -count=1049 \+\+\+ out.patch + +# recompile to binary +car compile -o out.car out.patch +! stderr . + +# should have same blocks as it started with. +car ls out.car +stdout -count=1043 '^bafy' +stdout -count=6 '^bafk' + +# make a small car +car create --file=small.car foo.txt + +car debug -o small.patch small.car +! stderr . + +car compile -o new.car small.patch +! stderr . + +# confirm roundtrip is stable. +cmp small.car new.car + +-- foo.txt -- +hello world \ No newline at end of file From 1b9a642bf0771fb55fa5e25d83a10d976805eef7 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 31 Oct 2022 12:19:18 +0000 Subject: [PATCH 3748/3817] feat: improve broken cid.Builder testing for CidBuilder If you don't use something derived from a cid.Prefix then it'll test execute your hasher to make sure it doesn't error. The reason for this is to avoid more cases where a panic could occur when encoding a ProtoNode. fix: apply code-review feedback Co-authored-by: Masih H. Derkani This commit was moved from ipfs/go-merkledag@51b4c32dd3df813bdad9bad154e8ffab39a4daa7 --- ipld/merkledag/merkledag_test.go | 86 +++++++++++++++++++++----------- ipld/merkledag/node.go | 32 +++++++++--- 2 files changed, 82 insertions(+), 36 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index e7ca4fb1d..665e79200 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -81,38 +81,64 @@ func traverseAndCheck(t *testing.T, root ipld.Node, ds ipld.DAGService, hasF fun } } +type brokenBuilder struct{} + +func (brokenBuilder) Sum([]byte) (cid.Cid, error) { return cid.Undef, errors.New("Nope!") } +func (brokenBuilder) GetCodec() uint64 { return 0 } +func (b brokenBuilder) WithCodec(uint64) cid.Builder { return b } + func TestBadBuilderEncode(t *testing.T) { n := NodeWithData([]byte("boop")) - _, err := n.EncodeProtobuf(false) - if err != nil { - t.Fatal(err) - } - err = n.SetCidBuilder( - &cid.Prefix{ - MhType: mh.SHA2_256, - MhLength: -1, - Version: 1, - Codec: cid.DagProtobuf, - }, - ) - if err != nil { - t.Fatal(err) - } - err = n.SetCidBuilder( - &cid.Prefix{ - MhType: mh.SHA2_256_TRUNC254_PADDED, - MhLength: 256, - Version: 1, - Codec: cid.DagProtobuf, - }, - ) - if err == nil { - t.Fatal("expected SetCidBuilder to error on unusable hasher") - } - _, err = n.EncodeProtobuf(false) - if err != nil { - t.Fatalf("expected EncodeProtobuf to use safe CidBuilder: %v", err) - } + + t.Run("good builder sanity check", func(t *testing.T) { + if _, err := n.EncodeProtobuf(false); err != nil { + t.Fatal(err) + } + if err := n.SetCidBuilder( + &cid.Prefix{ + MhType: mh.SHA2_256, + MhLength: -1, + Version: 1, + Codec: cid.DagProtobuf, + }, + ); err != nil { + t.Fatal(err) + } + }) + + t.Run("hasher we can't use, should error", func(t *testing.T) { + if err := n.SetCidBuilder( + &cid.Prefix{ + MhType: mh.SHA2_256_TRUNC254_PADDED, + MhLength: 256, + Version: 1, + Codec: cid.DagProtobuf, + }, + ); err == nil { + t.Fatal("expected SetCidBuilder to error on unusable hasher") + } + if _, err := n.EncodeProtobuf(false); err != nil { + t.Fatalf("expected EncodeProtobuf to use safe CidBuilder: %v", err) + } + }) + + t.Run("broken custom builder, should error", func(t *testing.T) { + if err := n.SetCidBuilder(brokenBuilder{}); err == nil { + t.Fatal("expected SetCidBuilder to error on unusable hasher") + } + if _, err := n.EncodeProtobuf(false); err != nil { + t.Fatalf("expected EncodeProtobuf to use safe CidBuilder: %v", err) + } + }) + + t.Run("broken custom builder as pointer, should error", func(t *testing.T) { + if err := n.SetCidBuilder(&brokenBuilder{}); err == nil { + t.Fatal("expected SetCidBuilder to error on unusable hasher") + } + if _, err := n.EncodeProtobuf(false); err != nil { + t.Fatalf("expected EncodeProtobuf to use safe CidBuilder: %v", err) + } + }) } func TestLinkChecking(t *testing.T) { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 93084ed0d..59e5695f3 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -25,6 +25,9 @@ var ( ErrLinkNotFound = fmt.Errorf("no link by that name") ) +// for testing custom CidBuilders +var zeros [256]byte + type immutableProtoNode struct { encoded []byte dagpb.PBNode @@ -106,13 +109,20 @@ func (n *ProtoNode) SetCidBuilder(builder cid.Builder) error { n.builder = v0CidPrefix return nil } - if p, ok := builder.(*cid.Prefix); ok { - mhLen := p.MhLength - if mhLen <= 0 { - mhLen = -1 + switch b := builder.(type) { + case cid.Prefix: + if err := checkHasher(b.MhType, b.MhLength); err != nil { + return err + } + case *cid.Prefix: + if err := checkHasher(b.MhType, b.MhLength); err != nil { + return err } - _, err := mhcore.GetVariableHasher(p.MhType, mhLen) - if err != nil { + default: + // We have to test it's a usable hasher by invoking it and checking it + // doesn't error. This is only a basic check, there are still ways it may + // break + if _, err := builder.Sum(zeros[:]); err != nil { return err } } @@ -121,6 +131,16 @@ func (n *ProtoNode) SetCidBuilder(builder cid.Builder) error { return nil } +// check whether the hasher is likely to be a usable one +func checkHasher(indicator uint64, sizeHint int) error { + mhLen := sizeHint + if mhLen <= 0 { + mhLen = -1 + } + _, err := mhcore.GetVariableHasher(indicator, mhLen) + return err +} + // LinkSlice is a slice of format.Links type LinkSlice []*format.Link From 499e8f0007ce6fbd08d56f0417edc5eea6ff4c12 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 22 Nov 2022 15:42:35 +1100 Subject: [PATCH 3749/3817] feat: remove panic() from non-error methods This commit was moved from ipfs/go-merkledag@738cf434c9a254c335d170bdeddbac490bb06570 --- ipld/merkledag/merkledag_test.go | 30 +++++++++++++++ ipld/merkledag/node.go | 63 +++++++++++++++++++++++--------- 2 files changed, 76 insertions(+), 17 deletions(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 665e79200..61fb8141d 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -87,6 +87,18 @@ func (brokenBuilder) Sum([]byte) (cid.Cid, error) { return cid.Undef, errors. func (brokenBuilder) GetCodec() uint64 { return 0 } func (b brokenBuilder) WithCodec(uint64) cid.Builder { return b } +// builder that will pass the basic SetCidBuilder tests but fail otherwise +type sneakyBrokenBuilder struct{} + +func (sneakyBrokenBuilder) Sum(data []byte) (cid.Cid, error) { + if len(data) == 256 { + return V1CidPrefix().Sum(data) + } + return cid.Undef, errors.New("Nope!") +} +func (sneakyBrokenBuilder) GetCodec() uint64 { return 0 } +func (b sneakyBrokenBuilder) WithCodec(uint64) cid.Builder { return b } + func TestBadBuilderEncode(t *testing.T) { n := NodeWithData([]byte("boop")) @@ -139,6 +151,24 @@ func TestBadBuilderEncode(t *testing.T) { t.Fatalf("expected EncodeProtobuf to use safe CidBuilder: %v", err) } }) + + t.Run("broken sneaky custom builder, should error", func(t *testing.T) { + if err := n.SetCidBuilder(sneakyBrokenBuilder{}); err != nil { + t.Fatalf("expected SetCidBuilder to not error with sneaky custom builder: %v", err) + } + if _, err := n.EncodeProtobuf(false); err == nil { + t.Fatal("expected EncodeProtobuf to fail using the sneaky custom builder") + } + if len(n.RawData()) != 0 { + t.Fatal("expected RawData to return zero-byte slice") + } + if n.Cid().String() != "bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku" { + t.Fatal("expected Cid to return the zero dag-pb CID") + } + if n.String() != "bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku" { + t.Fatal("expected String to return the zero dag-pb CID string") + } + }) } func TestLinkChecking(t *testing.T) { diff --git a/ipld/merkledag/node.go b/ipld/merkledag/node.go index 59e5695f3..c0f5f02f7 100644 --- a/ipld/merkledag/node.go +++ b/ipld/merkledag/node.go @@ -12,6 +12,7 @@ import ( cid "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" legacy "github.com/ipfs/go-ipld-legacy" + logging "github.com/ipfs/go-log/v2" dagpb "github.com/ipld/go-codec-dagpb" ipld "github.com/ipld/go-ipld-prime" mh "github.com/multiformats/go-multihash" @@ -25,8 +26,11 @@ var ( ErrLinkNotFound = fmt.Errorf("no link by that name") ) +var log = logging.Logger("merkledag") + // for testing custom CidBuilders var zeros [256]byte +var zeroCid = mustZeroCid() type immutableProtoNode struct { encoded []byte @@ -293,13 +297,16 @@ func (n *ProtoNode) Copy() format.Node { // RawData returns the encoded byte form of this node. // -// Note that this method can panic if a new encode is required and there is an -// error performing the encode. To avoid a panic, use node.EncodeProtobuf(false) -// instead (or prior to calling RawData) and check for its returned error value. +// Note that this method may return an empty byte slice if there is an error +// performing the encode. To check whether such an error may have occurred, use +// node.EncodeProtobuf(false), instead (or prior to calling RawData) and check +// for its returned error value; the result of EncodeProtobuf is cached so there +// is minimal overhead when invoking both methods. func (n *ProtoNode) RawData() []byte { out, err := n.EncodeProtobuf(false) if err != nil { - panic(err) + log.Errorf("failed to encode dag-pb block: %s", err.Error()) + return nil } return out } @@ -431,34 +438,47 @@ func (n *ProtoNode) MarshalJSON() ([]byte, error) { // Cid returns the node's Cid, calculated according to its prefix // and raw data contents. // -// Note that this method can panic if a new encode is required and there is an -// error performing the encode. To avoid a panic, call -// node.EncodeProtobuf(false) prior to calling Cid and check for its returned -// error value. +// Note that this method may return a CID representing a zero-length byte slice +// if there is an error performing the encode. To check whether such an error +// may have occurred, use node.EncodeProtobuf(false), instead (or prior to +// calling RawData) and check for its returned error value; the result of +// EncodeProtobuf is cached so there is minimal overhead when invoking both +// methods. func (n *ProtoNode) Cid() cid.Cid { // re-encode if necessary and we'll get a new cached CID if _, err := n.EncodeProtobuf(false); err != nil { - panic(err) + log.Errorf("failed to encode dag-pb block: %s", err.Error()) + // error, return a zero-CID + c, err := n.CidBuilder().Sum([]byte{}) + if err != nil { + // CidBuilder was a source of error, return _the_ dag-pb zero CIDv1 + return zeroCid + } + return c } return n.cached } // String prints the node's Cid. // -// Note that this method can panic if a new encode is required and there is an -// error performing the encode. To avoid a panic, call -// node.EncodeProtobuf(false) prior to calling String and check for its returned -// error value. +// Note that this method may return a CID representing a zero-length byte slice +// if there is an error performing the encode. To check whether such an error +// may have occurred, use node.EncodeProtobuf(false), instead (or prior to +// calling RawData) and check for its returned error value; the result of +// EncodeProtobuf is cached so there is minimal overhead when invoking both +// methods. func (n *ProtoNode) String() string { return n.Cid().String() } // Multihash hashes the encoded data of this node. // -// Note that this method can panic if a new encode is required and there is an -// error performing the encode. To avoid a panic, call -// node.EncodeProtobuf(false) prior to calling Multihash and check for its -// returned error value. +// Note that this method may return a multihash representing a zero-length byte +// slice if there is an error performing the encode. To check whether such an +// error may have occurred, use node.EncodeProtobuf(false), instead (or prior to +// calling RawData) and check for its returned error value; the result of +// EncodeProtobuf is cached so there is minimal overhead when invoking both +// methods. func (n *ProtoNode) Multihash() mh.Multihash { return n.Cid().Hash() } @@ -543,4 +563,13 @@ func ProtoNodeConverter(b blocks.Block, nd ipld.Node) (legacy.UniversalNode, err return pn, nil } +// TODO: replace with cid.MustParse() when we bump go-cid +func mustZeroCid() cid.Cid { + c, err := cid.Parse("bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku") + if err != nil { + panic(err) + } + return c +} + var _ legacy.UniversalNode = &ProtoNode{} From 3605c1683b7ca482719c534eb781f72caa46f4e0 Mon Sep 17 00:00:00 2001 From: web3-bot Date: Tue, 6 Dec 2022 13:47:01 +0000 Subject: [PATCH 3750/3817] stop using the deprecated io/ioutil package This commit was moved from ipfs/go-ipfs-provider@93e3121406f147f1b5246359451e005a47eb30ea --- provider/offline.go | 1 + provider/provider.go | 1 + provider/system.go | 1 + 3 files changed, 3 insertions(+) diff --git a/provider/offline.go b/provider/offline.go index 5511364ed..030a70ab1 100644 --- a/provider/offline.go +++ b/provider/offline.go @@ -2,6 +2,7 @@ package provider import ( "context" + "github.com/ipfs/go-cid" ) diff --git a/provider/provider.go b/provider/provider.go index 7dec4c172..3b9c6ba3e 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -2,6 +2,7 @@ package provider import ( "context" + "github.com/ipfs/go-cid" ) diff --git a/provider/system.go b/provider/system.go index b3e17ee40..9fc3e8879 100644 --- a/provider/system.go +++ b/provider/system.go @@ -2,6 +2,7 @@ package provider import ( "context" + "github.com/ipfs/go-cid" ) From 65758c3d975e4cc15191b1afc43203057a687026 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Mon, 21 Nov 2022 10:20:57 +1300 Subject: [PATCH 3751/3817] chore: update go-lib2p, avoid depending on go-libp2p-core, bump go.mod version This commit was moved from ipfs/go-ipfs-provider@ed88972f33b66970ae456228a3860b464d192a1a --- provider/simple/provider.go | 2 +- provider/simple/provider_test.go | 8 ++++---- provider/simple/reprovide.go | 2 +- provider/simple/reprovide_test.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/provider/simple/provider.go b/provider/simple/provider.go index d43cd6ac8..ab0b18998 100644 --- a/provider/simple/provider.go +++ b/provider/simple/provider.go @@ -10,7 +10,7 @@ import ( "github.com/ipfs/go-cid" q "github.com/ipfs/go-ipfs-provider/queue" logging "github.com/ipfs/go-log" - "github.com/libp2p/go-libp2p-core/routing" + "github.com/libp2p/go-libp2p/core/routing" ) var logP = logging.Logger("provider.simple") diff --git a/provider/simple/provider_test.go b/provider/simple/provider_test.go index d8dbf96f0..b4e170bff 100644 --- a/provider/simple/provider_test.go +++ b/provider/simple/provider_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" - cid "github.com/ipfs/go-cid" - datastore "github.com/ipfs/go-datastore" - sync "github.com/ipfs/go-datastore/sync" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/sync" blocksutil "github.com/ipfs/go-ipfs-blocksutil" - peer "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p/core/peer" q "github.com/ipfs/go-ipfs-provider/queue" diff --git a/provider/simple/reprovide.go b/provider/simple/reprovide.go index 38d6f86d7..0225340d1 100644 --- a/provider/simple/reprovide.go +++ b/provider/simple/reprovide.go @@ -15,7 +15,7 @@ import ( logging "github.com/ipfs/go-log" "github.com/ipfs/go-verifcid" cidlink "github.com/ipld/go-ipld-prime/linking/cid" - "github.com/libp2p/go-libp2p-core/routing" + "github.com/libp2p/go-libp2p/core/routing" ) var logR = logging.Logger("reprovider.simple") diff --git a/provider/simple/reprovide_test.go b/provider/simple/reprovide_test.go index 20b066e60..4d5563b0d 100644 --- a/provider/simple/reprovide_test.go +++ b/provider/simple/reprovide_test.go @@ -20,8 +20,8 @@ import ( "github.com/ipld/go-ipld-prime/fluent/qp" cidlink "github.com/ipld/go-ipld-prime/linking/cid" basicnode "github.com/ipld/go-ipld-prime/node/basic" - peer "github.com/libp2p/go-libp2p-core/peer" testutil "github.com/libp2p/go-libp2p-testing/net" + "github.com/libp2p/go-libp2p/core/peer" mh "github.com/multiformats/go-multihash" . "github.com/ipfs/go-ipfs-provider/simple" From 02df8225c4e4071110d2229fd152654d232eb754 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 9 Dec 2022 12:02:52 +0100 Subject: [PATCH 3752/3817] fix: multihash keying in the tests This commit was moved from ipfs/go-ipfs-provider@8d650d573dc43033fca464c58856bcaa36c24ec8 --- provider/simple/reprovide_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/provider/simple/reprovide_test.go b/provider/simple/reprovide_test.go index 4d5563b0d..e29d6e408 100644 --- a/provider/simple/reprovide_test.go +++ b/provider/simple/reprovide_test.go @@ -127,6 +127,14 @@ func testReprovide(t *testing.T, trigger func(r *Reprovider, ctx context.Context maxProvs := 100 for _, c := range nodes { + // We provide raw cids because of the multihash keying + // FIXME(@Jorropo): I think this change should be done in the DHT layer, probably an issue with our routing mock. + b := c.Bytes() + b[1] = 0x55 // rewrite the cid to raw + _, c, err := cid.CidFromBytes(b) + if err != nil { + t.Fatal(err) + } provChan := clB.FindProvidersAsync(ctx, c, maxProvs) for p := range provChan { providers = append(providers, p) From a8baf452edb9062caa14c4512346ef8db0ac1296 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Mon, 21 Nov 2022 10:11:01 +1300 Subject: [PATCH 3753/3817] chore: update go-libp2p This commit was moved from ipfs/go-ipfs-keystore@dea784f37c219c0478105d3ac78a5e6a75935327 --- keystore/keystore.go | 4 ++-- keystore/keystore_test.go | 4 ++-- keystore/memkeystore.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/keystore/keystore.go b/keystore/keystore.go index 10606a8c9..fc6793a1e 100644 --- a/keystore/keystore.go +++ b/keystore/keystore.go @@ -6,10 +6,10 @@ import ( "path/filepath" "strings" - base32 "encoding/base32" + "encoding/base32" logging "github.com/ipfs/go-log" - ci "github.com/libp2p/go-libp2p-core/crypto" + ci "github.com/libp2p/go-libp2p/core/crypto" ) var log = logging.Logger("keystore") diff --git a/keystore/keystore_test.go b/keystore/keystore_test.go index bbfde6c86..9a4406217 100644 --- a/keystore/keystore_test.go +++ b/keystore/keystore_test.go @@ -8,7 +8,7 @@ import ( "sort" "testing" - ci "github.com/libp2p/go-libp2p-core/crypto" + ci "github.com/libp2p/go-libp2p/core/crypto" ) type rr struct{} @@ -160,7 +160,7 @@ func TestInvalidKeyFiles(t *testing.T) { key := privKeyOrFatal(t) - bytes, err := key.Bytes() + bytes, err := key.Raw() if err != nil { t.Fatal(err) } diff --git a/keystore/memkeystore.go b/keystore/memkeystore.go index 94411144d..0ea62f4e1 100644 --- a/keystore/memkeystore.go +++ b/keystore/memkeystore.go @@ -3,7 +3,7 @@ package keystore import ( "errors" - ci "github.com/libp2p/go-libp2p-core/crypto" + ci "github.com/libp2p/go-libp2p/core/crypto" ) // MemKeystore is an in memory keystore implementation that is not persisted to From 90ca296f80108f2fd10943895a8df31e28967850 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Mon, 21 Nov 2022 09:38:01 +1300 Subject: [PATCH 3754/3817] chore: update go-libp2p to v0.23.4 This commit was moved from ipfs/interface-go-ipfs-core@96e9f233339ef16c3f1be4db6ced89ff82accfbb --- coreiface/dht.go | 4 ++-- coreiface/idfmt.go | 2 +- coreiface/key.go | 4 ++-- coreiface/pubsub.go | 4 ++-- coreiface/swarm.go | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/coreiface/dht.go b/coreiface/dht.go index 81a20ee2b..73bf48305 100644 --- a/coreiface/dht.go +++ b/coreiface/dht.go @@ -3,11 +3,11 @@ package iface import ( "context" - path "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/interface-go-ipfs-core/path" "github.com/ipfs/interface-go-ipfs-core/options" - "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p/core/peer" ) // DhtAPI specifies the interface to the DHT diff --git a/coreiface/idfmt.go b/coreiface/idfmt.go index 1ba79e602..80fd0f822 100644 --- a/coreiface/idfmt.go +++ b/coreiface/idfmt.go @@ -1,7 +1,7 @@ package iface import ( - peer "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p/core/peer" mbase "github.com/multiformats/go-multibase" ) diff --git a/coreiface/key.go b/coreiface/key.go index 967255665..b0e739cb8 100644 --- a/coreiface/key.go +++ b/coreiface/key.go @@ -3,11 +3,11 @@ package iface import ( "context" - path "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/interface-go-ipfs-core/path" "github.com/ipfs/interface-go-ipfs-core/options" - "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p/core/peer" ) // Key specifies the interface to Keys in KeyAPI Keystore diff --git a/coreiface/pubsub.go b/coreiface/pubsub.go index d9826551d..427256251 100644 --- a/coreiface/pubsub.go +++ b/coreiface/pubsub.go @@ -4,9 +4,9 @@ import ( "context" "io" - options "github.com/ipfs/interface-go-ipfs-core/options" + "github.com/ipfs/interface-go-ipfs-core/options" - "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p/core/peer" ) // PubSubSubscription is an active PubSub subscription diff --git a/coreiface/swarm.go b/coreiface/swarm.go index d7b25d5e8..9aa5466ba 100644 --- a/coreiface/swarm.go +++ b/coreiface/swarm.go @@ -5,9 +5,9 @@ import ( "errors" "time" - "github.com/libp2p/go-libp2p-core/network" - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-core/protocol" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/protocol" ma "github.com/multiformats/go-multiaddr" ) From 7f249456102362475b5ca6b9f8f602e9c95813bb Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 9 Dec 2022 13:31:02 +0100 Subject: [PATCH 3755/3817] fix: make queue 64bits on 32bits platforms too `int` is 32bits on 32bits platforms, it is realistical that you would overflow it (afaik by having more than 2b blocks in one datastore) Also we don't need 63 leading digits, a uint64 can always be represented in 20 base 10 digits. Fix bug introduced in 9bf7907fe1cf811df1328255c953028569c00087.Fix bug introduced in 9bf7907fe1cf811df1328255c953028569c00087. This commit was moved from ipfs/go-ipfs-provider@ef94782b5be979858fa8e4c57c68308dab035d7e --- provider/queue/queue.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/provider/queue/queue.go b/provider/queue/queue.go index d2ba30a79..618256bbe 100644 --- a/provider/queue/queue.go +++ b/provider/queue/queue.go @@ -28,7 +28,7 @@ type Queue struct { close context.CancelFunc closed chan struct{} - counter int + counter uint64 } // NewQueue creates a queue for cids @@ -117,7 +117,7 @@ func (q *Queue) work() { select { case toQueue := <-q.enqueue: - keyPath := fmt.Sprintf("%063d/%s", q.counter, c.String()) + keyPath := fmt.Sprintf("%020d/%s", q.counter, c.String()) q.counter++ nextKey := datastore.NewKey(keyPath) From 8d416893043ebba71a6c0610f8db2fed1a6d6526 Mon Sep 17 00:00:00 2001 From: web3-bot Date: Tue, 6 Dec 2022 13:47:37 +0000 Subject: [PATCH 3756/3817] stop using the deprecated io/ioutil package This commit was moved from ipfs/go-namesys@6e2d8f84f7f12d14cb179f57e4a34d61377b7c71 --- namesys/interface.go | 18 +++++++++--------- namesys/namesys.go | 1 - 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index b4136dfcc..471bf6501 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -7,15 +7,15 @@ That works well for many use cases, but doesn't allow you to answer questions like "what is Alice's current homepage?". The mutable name system allows Alice to publish information like: - The current homepage for alice.example.com is - /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj + The current homepage for alice.example.com is + /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj or: - The current homepage for node - QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - is - /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj + The current homepage for node + QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + is + /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj The mutable name system also allows users to resolve those references to find the immutable IPFS object currently referenced by a given @@ -23,9 +23,9 @@ mutable name. For command-line bindings to this functionality, see: - ipfs name - ipfs dns - ipfs resolve + ipfs name + ipfs dns + ipfs resolve */ package namesys diff --git a/namesys/namesys.go b/namesys/namesys.go index 6dfad0b71..11335c411 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -41,7 +41,6 @@ import ( // (b) dns domains: resolves using links in DNS TXT records // // It can only publish to: (a) IPFS routing naming. -// type mpns struct { ds ds.Datastore From 156fc8445014c80abc194f2af5a55d73ee623729 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Mon, 21 Nov 2022 09:26:40 +1300 Subject: [PATCH 3757/3817] chore: update go-libp2p to v0.23.4, update go.mod version to 1.18 This commit was moved from ipfs/go-namesys@64a7679c04fdf0f314226fa51fe89c9d679de1b5 --- namesys/interface.go | 6 +++--- namesys/ipns_resolver_validation_test.go | 18 +++++++++--------- namesys/namesys.go | 12 ++++++------ namesys/namesys_test.go | 10 +++++----- namesys/publisher.go | 14 +++++++------- namesys/publisher_test.go | 6 +++--- namesys/republisher/repub.go | 14 +++++++------- namesys/republisher/repub_test.go | 14 +++++++------- namesys/routing.go | 12 ++++++------ 9 files changed, 53 insertions(+), 53 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index 471bf6501..94045129b 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -33,11 +33,11 @@ import ( "errors" "time" - context "context" + "context" - path "github.com/ipfs/go-path" + "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" - ci "github.com/libp2p/go-libp2p-core/crypto" + ci "github.com/libp2p/go-libp2p/core/crypto" ) // ErrResolveFailed signals an error when attempting to resolve. diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index cc3b58f36..9653a4245 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -8,19 +8,19 @@ import ( ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" mockrouting "github.com/ipfs/go-ipfs-routing/mock" - offline "github.com/ipfs/go-ipfs-routing/offline" - ipns "github.com/ipfs/go-ipns" + "github.com/ipfs/go-ipfs-routing/offline" + "github.com/ipfs/go-ipns" ipns_pb "github.com/ipfs/go-ipns/pb" - path "github.com/ipfs/go-path" + "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" - ci "github.com/libp2p/go-libp2p-core/crypto" - peer "github.com/libp2p/go-libp2p-core/peer" - pstore "github.com/libp2p/go-libp2p-core/peerstore" - routing "github.com/libp2p/go-libp2p-core/routing" - "github.com/libp2p/go-libp2p-core/test" - pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem" record "github.com/libp2p/go-libp2p-record" testutil "github.com/libp2p/go-libp2p-testing/net" + ci "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" + pstore "github.com/libp2p/go-libp2p/core/peerstore" + "github.com/libp2p/go-libp2p/core/routing" + "github.com/libp2p/go-libp2p/core/test" + "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem" ) func TestResolverValidation(t *testing.T) { diff --git a/namesys/namesys.go b/namesys/namesys.go index 11335c411..f8218d371 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -20,15 +20,15 @@ import ( "time" lru "github.com/hashicorp/golang-lru" - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" - path "github.com/ipfs/go-path" + "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" - ci "github.com/libp2p/go-libp2p-core/crypto" - peer "github.com/libp2p/go-libp2p-core/peer" - routing "github.com/libp2p/go-libp2p-core/routing" - dns "github.com/miekg/dns" + ci "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" + "github.com/miekg/dns" madns "github.com/multiformats/go-multiaddr-dns" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index af115ac2b..b56aa763a 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -10,13 +10,13 @@ import ( ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" offroute "github.com/ipfs/go-ipfs-routing/offline" - ipns "github.com/ipfs/go-ipns" - path "github.com/ipfs/go-path" + "github.com/ipfs/go-ipns" + "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" - ci "github.com/libp2p/go-libp2p-core/crypto" - peer "github.com/libp2p/go-libp2p-core/peer" - pstoremem "github.com/libp2p/go-libp2p-peerstore/pstoremem" record "github.com/libp2p/go-libp2p-record" + ci "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem" ) type mockResolver struct { diff --git a/namesys/publisher.go b/namesys/publisher.go index bf1c46d9d..317e0e7be 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -6,16 +6,16 @@ import ( "sync" "time" - proto "github.com/gogo/protobuf/proto" + "github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-datastore" dsquery "github.com/ipfs/go-datastore/query" - ipns "github.com/ipfs/go-ipns" + "github.com/ipfs/go-ipns" pb "github.com/ipfs/go-ipns/pb" - path "github.com/ipfs/go-path" - "github.com/libp2p/go-libp2p-core/crypto" - peer "github.com/libp2p/go-libp2p-core/peer" - routing "github.com/libp2p/go-libp2p-core/routing" - base32 "github.com/whyrusleeping/base32" + "github.com/ipfs/go-path" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" + "github.com/whyrusleeping/base32" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) diff --git a/namesys/publisher_test.go b/namesys/publisher_test.go index 4be9ec846..3b9b66f9d 100644 --- a/namesys/publisher_test.go +++ b/namesys/publisher_test.go @@ -12,10 +12,10 @@ import ( dssync "github.com/ipfs/go-datastore/sync" dshelp "github.com/ipfs/go-ipfs-ds-help" mockrouting "github.com/ipfs/go-ipfs-routing/mock" - ipns "github.com/ipfs/go-ipns" - ci "github.com/libp2p/go-libp2p-core/crypto" - peer "github.com/libp2p/go-libp2p-core/peer" + "github.com/ipfs/go-ipns" testutil "github.com/libp2p/go-libp2p-testing/net" + ci "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" ) diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index a24e59dff..c1259d8c4 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -8,19 +8,19 @@ import ( "time" keystore "github.com/ipfs/go-ipfs-keystore" - namesys "github.com/ipfs/go-namesys" - path "github.com/ipfs/go-path" + "github.com/ipfs/go-namesys" + "github.com/ipfs/go-path" "go.opentelemetry.io/otel/attribute" - proto "github.com/gogo/protobuf/proto" + "github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-datastore" - ipns "github.com/ipfs/go-ipns" + "github.com/ipfs/go-ipns" pb "github.com/ipfs/go-ipns/pb" logging "github.com/ipfs/go-log" - goprocess "github.com/jbenet/goprocess" + "github.com/jbenet/goprocess" gpctx "github.com/jbenet/goprocess/context" - ic "github.com/libp2p/go-libp2p-core/crypto" - peer "github.com/libp2p/go-libp2p-core/peer" + ic "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" ) var errNoEntry = errors.New("no previous entry") diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index c7c0f0185..e73edef95 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -8,22 +8,22 @@ import ( "github.com/gogo/protobuf/proto" - goprocess "github.com/jbenet/goprocess" + "github.com/jbenet/goprocess" "github.com/libp2p/go-libp2p" - ic "github.com/libp2p/go-libp2p-core/crypto" - host "github.com/libp2p/go-libp2p-core/host" - peer "github.com/libp2p/go-libp2p-core/peer" - routing "github.com/libp2p/go-libp2p-core/routing" dht "github.com/libp2p/go-libp2p-kad-dht" + ic "github.com/libp2p/go-libp2p/core/crypto" + host "github.com/libp2p/go-libp2p/core/host" + peer "github.com/libp2p/go-libp2p/core/peer" + routing "github.com/libp2p/go-libp2p/core/routing" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-ipns" ipns_pb "github.com/ipfs/go-ipns/pb" - path "github.com/ipfs/go-path" + "github.com/ipfs/go-path" keystore "github.com/ipfs/go-ipfs-keystore" - namesys "github.com/ipfs/go-namesys" + "github.com/ipfs/go-namesys" . "github.com/ipfs/go-namesys/republisher" ) diff --git a/namesys/routing.go b/namesys/routing.go index c73e23ed7..8c8fbee3e 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -5,16 +5,16 @@ import ( "strings" "time" - proto "github.com/gogo/protobuf/proto" - cid "github.com/ipfs/go-cid" - ipns "github.com/ipfs/go-ipns" + "github.com/gogo/protobuf/proto" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-ipns" pb "github.com/ipfs/go-ipns/pb" logging "github.com/ipfs/go-log" - path "github.com/ipfs/go-path" + "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" - peer "github.com/libp2p/go-libp2p-core/peer" - routing "github.com/libp2p/go-libp2p-core/routing" dht "github.com/libp2p/go-libp2p-kad-dht" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" mh "github.com/multiformats/go-multihash" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" From ff2eb2bff265f34012cca335447987da546ecee2 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Mon, 12 Dec 2022 21:09:45 +0100 Subject: [PATCH 3758/3817] feat: add UseCumulativeSize UnixfsLs option (#95) This commit was moved from ipfs/interface-go-ipfs-core@b1299abda0c69529c7efa02d5efb9f8905fdd4fe --- coreiface/options/unixfs.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go index 3fd96f772..cd15991e2 100644 --- a/coreiface/options/unixfs.go +++ b/coreiface/options/unixfs.go @@ -39,7 +39,8 @@ type UnixfsAddSettings struct { } type UnixfsLsSettings struct { - ResolveChildren bool + ResolveChildren bool + UseCumulativeSize bool } type UnixfsAddOption func(*UnixfsAddSettings) error @@ -283,3 +284,10 @@ func (unixfsOpts) ResolveChildren(resolve bool) UnixfsLsOption { return nil } } + +func (unixfsOpts) UseCumulativeSize(use bool) UnixfsLsOption { + return func(settings *UnixfsLsSettings) error { + settings.UseCumulativeSize = use + return nil + } +} From 01de18ff3f4ccd639e02226048fda8a8f140941c Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 12 Jan 2023 11:45:13 +0100 Subject: [PATCH 3759/3817] chore: migrate files (#97) This commit was moved from ipfs/interface-go-ipfs-core@f7b346b76c5724489877c511754f0f11923d3214 --- coreiface/tests/name.go | 2 +- coreiface/tests/unixfs.go | 2 +- coreiface/unixfs.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index 2a8b4d76a..2e648baba 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -10,7 +10,7 @@ import ( path "github.com/ipfs/interface-go-ipfs-core/path" - files "github.com/ipfs/go-ipfs-files" + "github.com/ipfs/go-libipfs/files" coreiface "github.com/ipfs/interface-go-ipfs-core" opt "github.com/ipfs/interface-go-ipfs-core/options" diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go index 05226dbbf..121d3db69 100644 --- a/coreiface/tests/unixfs.go +++ b/coreiface/tests/unixfs.go @@ -20,9 +20,9 @@ import ( "github.com/ipfs/interface-go-ipfs-core/options" "github.com/ipfs/go-cid" - files "github.com/ipfs/go-ipfs-files" cbor "github.com/ipfs/go-ipld-cbor" ipld "github.com/ipfs/go-ipld-format" + "github.com/ipfs/go-libipfs/files" mdag "github.com/ipfs/go-merkledag" "github.com/ipfs/go-unixfs" "github.com/ipfs/go-unixfs/importer/helpers" diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go index c398b6722..3b21a8e23 100644 --- a/coreiface/unixfs.go +++ b/coreiface/unixfs.go @@ -7,7 +7,7 @@ import ( path "github.com/ipfs/interface-go-ipfs-core/path" "github.com/ipfs/go-cid" - files "github.com/ipfs/go-ipfs-files" + "github.com/ipfs/go-libipfs/files" ) type AddEvent struct { From bcf04980d0a2830974b19b530c5e616105ee3f20 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 12 Jan 2023 11:45:23 +0100 Subject: [PATCH 3760/3817] chore: migrate files (#134) This commit was moved from ipfs/go-unixfs@a76f0e5f3e2ec94638ea0fd358c5e13bd7ed4745 --- unixfs/file/unixfile.go | 2 +- unixfs/importer/helpers/dagbuilder.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unixfs/file/unixfile.go b/unixfs/file/unixfile.go index df3ce9e89..82ee20a4d 100644 --- a/unixfs/file/unixfile.go +++ b/unixfs/file/unixfile.go @@ -7,8 +7,8 @@ import ( ft "github.com/ipfs/go-unixfs" uio "github.com/ipfs/go-unixfs/io" - files "github.com/ipfs/go-ipfs-files" ipld "github.com/ipfs/go-ipld-format" + "github.com/ipfs/go-libipfs/files" dag "github.com/ipfs/go-merkledag" ) diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index e3cf7b44f..b59f41380 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -13,9 +13,9 @@ import ( cid "github.com/ipfs/go-cid" chunker "github.com/ipfs/go-ipfs-chunker" - files "github.com/ipfs/go-ipfs-files" pi "github.com/ipfs/go-ipfs-posinfo" ipld "github.com/ipfs/go-ipld-format" + "github.com/ipfs/go-libipfs/files" ) var ErrMissingFsRef = errors.New("missing file path or URL, can't create filestore reference") From e3a1b784c55339b84c60ad47b94e95076738f1f8 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Mon, 16 Jan 2023 00:26:48 -0500 Subject: [PATCH 3761/3817] feat(cmd): add index create subcommand to create an external carv2 index This commit was moved from ipld/go-car@7df51ce8b18b10ccdef60575174470f19486dc24 --- ipld/car/cmd/car/car.go | 5 +++++ ipld/car/cmd/car/index.go | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index c66232f7d..70c9eb32a 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -153,6 +153,11 @@ func main1() int { Usage: "Write output as a v1 or v2 format car", }, }, + Subcommands: []*cli.Command{{ + Name: "create", + Usage: "Write out a detached index", + Action: CreateIndex, + }}, }, { Name: "inspect", diff --git a/ipld/car/cmd/car/index.go b/ipld/car/cmd/car/index.go index 031df2ada..fc6bd5d61 100644 --- a/ipld/car/cmd/car/index.go +++ b/ipld/car/cmd/car/index.go @@ -166,3 +166,45 @@ func IndexCar(c *cli.Context) error { _, err = index.WriteTo(idx, outStream) return err } + +// CreateIndex is a command to write out an index of the CAR file +func CreateIndex(c *cli.Context) error { + r, err := carv2.OpenReader(c.Args().Get(0)) + if err != nil { + return err + } + defer r.Close() + + outStream := os.Stdout + if c.Args().Len() >= 2 { + outStream, err = os.Create(c.Args().Get(1)) + if err != nil { + return err + } + } + defer outStream.Close() + + var mc multicodec.Code + if err := mc.Set(c.String("codec")); err != nil { + return err + } + idx, err := index.New(mc) + if err != nil { + return err + } + + dr, err := r.DataReader() + if err != nil { + return err + } + + if err := carv2.LoadIndex(idx, dr); err != nil { + return err + } + + if _, err := index.WriteTo(idx, outStream); err != nil { + return err + } + + return nil +} From d9db510a2d28c1ca712654c576028fff98c5731d Mon Sep 17 00:00:00 2001 From: Andrew Gillis Date: Wed, 18 Jan 2023 14:45:05 -0800 Subject: [PATCH 3762/3817] Update install instructions in README.md `go get` is no longer supported outside a module. To build and install a command, use 'go install' with a version. For more information, see https://golang.org/doc/go-get-install-deprecation This commit was moved from ipld/go-car@8d96b72babb190da5d08562a22f82b44ec05ff94 --- ipld/car/README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ipld/car/README.md b/ipld/car/README.md index 0f2157991..91302e2f8 100644 --- a/ipld/car/README.md +++ b/ipld/car/README.md @@ -30,15 +30,12 @@ Most users should use v2, especially for new software, since the v2 API transpar ## Install -To install the latest version of `go-car/v2` module, run: +To install the latest version of the `car` executable, run: ```shell script -go get github.com/ipld/go-car/v2 +go install github.com/ipld/go-car/cmd/car@latest ``` -Alternatively, to install the v0 module, run: -```shell script -go get github.com/ipld/go-car -``` +This will install the `car` executable into `$GOPATH/bin/` ## API Documentation From 06ff64e3087e1f86fa3b90c1378d8010e052673b Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 23 Jan 2023 08:45:44 +0100 Subject: [PATCH 3763/3817] cleanup readme a bit to make the cli more discoverable (#353) This commit was moved from ipld/go-car@2e16e87304353f704b8aff0614b96681911f6e72 --- ipld/car/README.md | 30 +++++++++++++++++------------- ipld/car/cmd/car/README.md | 4 ++-- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/ipld/car/README.md b/ipld/car/README.md index 91302e2f8..af75fa1d7 100644 --- a/ipld/car/README.md +++ b/ipld/car/README.md @@ -7,18 +7,30 @@ go-car (go!) [![Go Reference](https://pkg.go.dev/badge/github.com/ipld/go-car.svg)](https://pkg.go.dev/github.com/ipld/go-car) [![Coverage Status](https://codecov.io/gh/ipld/go-car/branch/master/graph/badge.svg)](https://codecov.io/gh/ipld/go-car/branch/master) -> A library to interact with merkledags stored as a single file +> Work with car (Content addressed ARchive) files! -This is a Go implementation of the [CAR specifications](https://ipld.io/specs/transport/car/), both [CARv1](https://ipld.io/specs/transport/car/carv1/) and [CARv2](https://ipld.io/specs/transport/car/carv2/). +This is a Golang implementation of the [CAR specifications](https://ipld.io/specs/transport/car/), both [CARv1](https://ipld.io/specs/transport/car/carv1/) and [CARv2](https://ipld.io/specs/transport/car/carv2/). -Note that there are two major module versions: +As a format, there are two major module versions: * [`go-car/v2`](v2/) is geared towards reading and writing CARv2 files, and also supports consuming CARv1 files and using CAR files as an IPFS blockstore. -* `go-car` v0, in the root directory, just supports reading and writing CARv1 files. +* `go-car`, in the root directory, only supports reading and writing CARv1 files. Most users should use v2, especially for new software, since the v2 API transparently supports both CAR formats. +## Usage / Installation + +This repository provides a `car` binary that can be used for creating, extracting, and working with car files. + +To install the latest version of `car`, run: +```shell script +go install github.com/ipld/go-car/cmd/car@latest +``` + +More information about this binary is available in [`cmd/car`](cmd/car/) + + ## Features [CARv2](v2) features: @@ -28,14 +40,6 @@ Most users should use v2, especially for new software, since the v2 API transpar * Write CARv2 files via [Read-Write blockstore](https://pkg.go.dev/github.com/ipld/go-car/v2/blockstore#OpenReadWrite) API, with support for appending blocks to an existing CARv2 file, and resumption from a partially written CARv2 files. * Individual access to [inner CARv1 data payload]((https://pkg.go.dev/github.com/ipld/go-car/v2#Reader.DataReader)) and [index]((https://pkg.go.dev/github.com/ipld/go-car/v2#Reader.IndexReader)) of a CARv2 file via the `Reader` API. -## Install - -To install the latest version of the `car` executable, run: -```shell script -go install github.com/ipld/go-car/cmd/car@latest -``` - -This will install the `car` executable into `$GOPATH/bin/` ## API Documentation @@ -53,8 +57,8 @@ Here is a shortlist of other examples from the documentation ## Maintainers -* [Daniel Martí](https://github.com/mvdan) * [Masih Derkani](https://github.com/masih) +* [Will Scott](https://github.com/willscott) ## Contribute diff --git a/ipld/car/cmd/car/README.md b/ipld/car/cmd/car/README.md index ef04a0dc3..901b75cb0 100644 --- a/ipld/car/cmd/car/README.md +++ b/ipld/car/cmd/car/README.md @@ -5,7 +5,7 @@ car - The CLI tool [![](https://img.shields.io/badge/project-ipld-orange.svg?style=flat-square)](https://github.com/ipld/ipld) [![](https://img.shields.io/badge/matrix-%23ipld-blue.svg?style=flat-square)](https://matrix.to/#/#ipld:ipfs.io) -> A CLI to interact with car files +> A CLI for interacting with car files ## Usage @@ -30,7 +30,7 @@ COMMANDS: ## Install -To install the latest version of `car` module, run: +To install the latest version of `car`, run: ```shell script go install github.com/ipld/go-car/cmd/car@latest ``` From bcb9190c2bdbc0ad4823a1b1785fb87ec8014723 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Tue, 24 Jan 2023 23:44:55 +0100 Subject: [PATCH 3764/3817] feat: add namesys publish options (#94) * feat: add namesys publish options * feat: export DefaultIPNSRecordEOL * feat: export DefaultIPNSRecordTTL This commit was moved from ipfs/interface-go-ipfs-core@468dea4bb45aec6ddce2a6225334dcc062d6e752 --- coreiface/options/namesys/opts.go | 49 +++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/coreiface/options/namesys/opts.go b/coreiface/options/namesys/opts.go index ee2bd5ac2..0cd1ba778 100644 --- a/coreiface/options/namesys/opts.go +++ b/coreiface/options/namesys/opts.go @@ -13,6 +13,15 @@ const ( // trust resolution to eventually complete and can't put an upper // limit on how many steps it will take. UnlimitedDepth = 0 + + // DefaultIPNSRecordTTL specifies the time that the record can be cached + // before checking if its validity again. + DefaultIPNSRecordTTL = time.Minute + + // DefaultIPNSRecordEOL specifies the time that the network will cache IPNS + // records after being published. Records should be re-published before this + // interval expires. We use the same default expiration as the DHT. + DefaultIPNSRecordEOL = 48 * time.Hour ) // ResolveOpts specifies options for resolving an IPNS path @@ -72,3 +81,43 @@ func ProcessOpts(opts []ResolveOpt) ResolveOpts { } return rsopts } + +// PublishOptions specifies options for publishing an IPNS record. +type PublishOptions struct { + EOL time.Time + TTL time.Duration +} + +// DefaultPublishOptions returns the default options for publishing an IPNS record. +func DefaultPublishOptions() PublishOptions { + return PublishOptions{ + EOL: time.Now().Add(DefaultIPNSRecordEOL), + TTL: DefaultIPNSRecordTTL, + } +} + +// PublishOption is used to set an option for PublishOpts. +type PublishOption func(*PublishOptions) + +// PublishWithEOL sets an EOL. +func PublishWithEOL(eol time.Time) PublishOption { + return func(o *PublishOptions) { + o.EOL = eol + } +} + +// PublishWithEOL sets a TTL. +func PublishWithTTL(ttl time.Duration) PublishOption { + return func(o *PublishOptions) { + o.TTL = ttl + } +} + +// ProcessPublishOptions converts an array of PublishOpt into a PublishOpts object. +func ProcessPublishOptions(opts []PublishOption) PublishOptions { + rsopts := DefaultPublishOptions() + for _, option := range opts { + option(&rsopts) + } + return rsopts +} From 00661e346e6eb11d375830e762f4dbc64b0230d9 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 25 Jan 2023 00:34:07 +0100 Subject: [PATCH 3765/3817] feat: use PublishOptions for publishing IPNS records (#35) * feat: use PublishOptions for publishing IPNS records * chore: interface-go-ipfs-core v0.9.0 Co-authored-by: Marcin Rataj This commit was moved from ipfs/go-namesys@3f6313c8c9d00ac4104def9edc36ad159f5c6b14 --- namesys/interface.go | 8 +--- namesys/namesys.go | 24 ++++++------ namesys/namesys_test.go | 3 +- namesys/publisher.go | 64 ++++++++----------------------- namesys/republisher/repub.go | 3 +- namesys/republisher/repub_test.go | 5 ++- 6 files changed, 35 insertions(+), 72 deletions(-) diff --git a/namesys/interface.go b/namesys/interface.go index 94045129b..bd72538da 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -31,7 +31,6 @@ package namesys import ( "errors" - "time" "context" @@ -95,12 +94,7 @@ type Resolver interface { // Publisher is an object capable of publishing particular names. type Publisher interface { - // Publish establishes a name-value mapping. // TODO make this not PrivKey specific. - Publish(ctx context.Context, name ci.PrivKey, value path.Path) error - - // TODO: to be replaced by a more generic 'PublishWithValidity' type - // call once the records spec is implemented - PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error + Publish(ctx context.Context, name ci.PrivKey, value path.Path, options ...opts.PublishOption) error } diff --git a/namesys/namesys.go b/namesys/namesys.go index f8218d371..540aba4ea 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -285,22 +285,24 @@ func emitOnceResult(ctx context.Context, outCh chan<- onceResult, r onceResult) } // Publish implements Publisher -func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error { +func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path, options ...opts.PublishOption) error { ctx, span := StartSpan(ctx, "MPNS.Publish") defer span.End() - return ns.PublishWithEOL(ctx, name, value, time.Now().Add(DefaultRecordEOL)) -} -func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error { - ctx, span := StartSpan(ctx, "MPNS.PublishWithEOL", trace.WithAttributes(attribute.String("Value", value.String()))) - defer span.End() + // This is a bit hacky. We do this because the EOL is based on the current + // time, but also needed in the end of the function. Therefore, we parse + // the options immediately and add an option PublishWithEOL with the EOL + // calculated in this moment. + publishOpts := opts.ProcessPublishOptions(options) + options = append(options, opts.PublishWithEOL(publishOpts.EOL)) + id, err := peer.IDFromPrivateKey(name) if err != nil { span.RecordError(err) return err } span.SetAttributes(attribute.String("ID", id.String())) - if err := ns.ipnsPublisher.PublishWithEOL(ctx, name, value, eol); err != nil { + if err := ns.ipnsPublisher.Publish(ctx, name, value, options...); err != nil { // Invalidate the cache. Publishing may _partially_ succeed but // still return an error. ns.cacheInvalidate(string(id)) @@ -308,11 +310,11 @@ func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path. return err } ttl := DefaultResolverCacheTTL - if setTTL, ok := checkCtxTTL(ctx); ok { - ttl = setTTL + if publishOpts.TTL >= 0 { + ttl = publishOpts.TTL } - if ttEol := time.Until(eol); ttEol < ttl { - ttl = ttEol + if ttEOL := time.Until(publishOpts.EOL); ttEOL < ttl { + ttl = ttEOL } ns.cacheSet(string(id), value, ttl) return nil diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index b56aa763a..c641d4911 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -166,8 +166,7 @@ func TestPublishWithTTL(t *testing.T) { ttl := 1 * time.Second eol := time.Now().Add(2 * time.Second) - ctx := ContextWithTTL(context.Background(), ttl) - err = nsys.Publish(ctx, priv, p) + err = nsys.Publish(context.Background(), priv, p, opts.PublishWithEOL(eol), opts.PublishWithTTL(ttl)) if err != nil { t.Fatal(err) } diff --git a/namesys/publisher.go b/namesys/publisher.go index 317e0e7be..37590f621 100644 --- a/namesys/publisher.go +++ b/namesys/publisher.go @@ -12,6 +12,7 @@ import ( "github.com/ipfs/go-ipns" pb "github.com/ipfs/go-ipns/pb" "github.com/ipfs/go-path" + opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/routing" @@ -22,11 +23,6 @@ import ( const ipnsPrefix = "/ipns/" -// DefaultRecordEOL specifies the time that the network will cache IPNS -// records after being publihsed. Records should be re-published before this -// interval expires. -const DefaultRecordEOL = 24 * time.Hour - // IpnsPublisher is capable of publishing and resolving names to the IPFS // routing system. type IpnsPublisher struct { @@ -47,9 +43,18 @@ func NewIpnsPublisher(route routing.ValueStore, ds ds.Datastore) *IpnsPublisher // Publish implements Publisher. Accepts a keypair and a value, // and publishes it out to the routing system -func (p *IpnsPublisher) Publish(ctx context.Context, k crypto.PrivKey, value path.Path) error { +func (p *IpnsPublisher) Publish(ctx context.Context, k crypto.PrivKey, value path.Path, options ...opts.PublishOption) error { log.Debugf("Publish %s", value) - return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordEOL)) + + ctx, span := StartSpan(ctx, "IpnsPublisher.Publish", trace.WithAttributes(attribute.String("Value", value.String()))) + defer span.End() + + record, err := p.updateRecord(ctx, k, value, options...) + if err != nil { + return err + } + + return PutRecordToRouting(ctx, p.routing, k.GetPublic(), record) } // IpnsDsKey returns a datastore key given an IPNS identifier (peer @@ -142,7 +147,7 @@ func (p *IpnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouti return e, nil } -func (p *IpnsPublisher) updateRecord(ctx context.Context, k crypto.PrivKey, value path.Path, eol time.Time) (*pb.IpnsEntry, error) { +func (p *IpnsPublisher) updateRecord(ctx context.Context, k crypto.PrivKey, value path.Path, options ...opts.PublishOption) (*pb.IpnsEntry, error) { id, err := peer.IDFromPrivateKey(k) if err != nil { return nil, err @@ -164,12 +169,10 @@ func (p *IpnsPublisher) updateRecord(ctx context.Context, k crypto.PrivKey, valu seqno++ } - // Set the TTL - // TODO: Make this less hacky. - ttl, _ := checkCtxTTL(ctx) + opts := opts.ProcessPublishOptions(options) // Create record - entry, err := ipns.Create(k, []byte(value), seqno, eol, ttl) + entry, err := ipns.Create(k, []byte(value), seqno, opts.EOL, opts.TTL) if err != nil { return nil, err } @@ -190,33 +193,6 @@ func (p *IpnsPublisher) updateRecord(ctx context.Context, k crypto.PrivKey, valu return entry, nil } -// PublishWithEOL is a temporary stand in for the ipns records implementation -// see here for more details: https://github.com/ipfs/specs/tree/master/records -func (p *IpnsPublisher) PublishWithEOL(ctx context.Context, k crypto.PrivKey, value path.Path, eol time.Time) error { - ctx, span := StartSpan(ctx, "IpnsPublisher.PublishWithEOL", trace.WithAttributes(attribute.String("Value", value.String()))) - defer span.End() - - record, err := p.updateRecord(ctx, k, value, eol) - if err != nil { - return err - } - - return PutRecordToRouting(ctx, p.routing, k.GetPublic(), record) -} - -// setting the TTL on published records is an experimental feature. -// as such, i'm using the context to wire it through to avoid changing too -// much code along the way. -func checkCtxTTL(ctx context.Context) (time.Duration, bool) { - v := ctx.Value(ttlContextKey) - if v == nil { - return 0, false - } - - d, ok := v.(time.Duration) - return d, ok -} - // PutRecordToRouting publishes the given entry using the provided ValueStore, // keyed on the ID associated with the provided public key. The public key is // also made available to the routing system so that entries can be verified. @@ -307,13 +283,3 @@ func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec func PkKeyForID(id peer.ID) string { return "/pk/" + string(id) } - -// contextKey is a private comparable type used to hold value keys in contexts -type contextKey string - -var ttlContextKey contextKey = "ipns-publish-ttl" - -// ContextWithTTL returns a copy of the parent context with an added value representing the TTL -func ContextWithTTL(ctx context.Context, ttl time.Duration) context.Context { - return context.WithValue(context.Background(), ttlContextKey, ttl) -} diff --git a/namesys/republisher/repub.go b/namesys/republisher/repub.go index c1259d8c4..d0857b48a 100644 --- a/namesys/republisher/repub.go +++ b/namesys/republisher/repub.go @@ -17,6 +17,7 @@ import ( "github.com/ipfs/go-ipns" pb "github.com/ipfs/go-ipns/pb" logging "github.com/ipfs/go-log" + opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" "github.com/jbenet/goprocess" gpctx "github.com/jbenet/goprocess/context" ic "github.com/libp2p/go-libp2p/core/crypto" @@ -161,7 +162,7 @@ func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) erro if prevEol.After(eol) { eol = prevEol } - err = rp.ns.PublishWithEOL(ctx, priv, p, eol) + err = rp.ns.Publish(ctx, priv, p, opts.PublishWithEOL(eol)) span.RecordError(err) return err } diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index e73edef95..cf857023a 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -21,6 +21,7 @@ import ( "github.com/ipfs/go-ipns" ipns_pb "github.com/ipfs/go-ipns/pb" "github.com/ipfs/go-path" + opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" keystore "github.com/ipfs/go-ipfs-keystore" "github.com/ipfs/go-namesys" @@ -103,7 +104,7 @@ func TestRepublish(t *testing.T) { timeout := time.Second for { expiration = time.Now().Add(time.Second) - err := rp.PublishWithEOL(ctx, publisher.privKey, p, expiration) + err := rp.Publish(ctx, publisher.privKey, p, opts.PublishWithEOL(expiration)) if err != nil { t.Fatal(err) } @@ -179,7 +180,7 @@ func TestLongEOLRepublish(t *testing.T) { name := "/ipns/" + publisher.id expiration := time.Now().Add(time.Hour) - err := rp.PublishWithEOL(ctx, publisher.privKey, p, expiration) + err := rp.Publish(ctx, publisher.privKey, p, opts.PublishWithEOL(expiration)) if err != nil { t.Fatal(err) } From b3ab88834562c689b5eadfed2e08e732fea4392a Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Mon, 14 Nov 2022 17:18:43 +0100 Subject: [PATCH 3766/3817] feat: add RoutingAPI to CoreAPI This commit was moved from ipfs/interface-go-ipfs-core@177d25ba92ed67ab4916cb13827321c389961de0 --- coreiface/coreapi.go | 3 +++ coreiface/routing.go | 14 ++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 coreiface/routing.go diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go index 894ffb318..722c00a0f 100644 --- a/coreiface/coreapi.go +++ b/coreiface/coreapi.go @@ -44,6 +44,9 @@ type CoreAPI interface { // PubSub returns an implementation of PubSub API PubSub() PubSubAPI + // Routing returns an implementation of Routing API + Routing() RoutingAPI + // ResolvePath resolves the path using Unixfs resolver ResolvePath(context.Context, path.Path) (path.Resolved, error) diff --git a/coreiface/routing.go b/coreiface/routing.go new file mode 100644 index 000000000..a28ceb9e7 --- /dev/null +++ b/coreiface/routing.go @@ -0,0 +1,14 @@ +package iface + +import ( + "context" +) + +// RoutingAPI specifies the interface to the routing layer. +type RoutingAPI interface { + // Get retrieves the best value for a given key + Get(context.Context, string) ([]byte, error) + + // Put sets a value for a given key + Put(ctx context.Context, key string, value []byte) error +} From bafac68d186a7e769e444c5a37d4ff6fbe1fb05f Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 27 Jan 2023 10:59:40 +0100 Subject: [PATCH 3767/3817] feat: upgrade from go-block-format to go-libipfs/blocks This commit was moved from ipld/go-car@377b78873a76a59450745c83d15079318e2bb58a --- ipld/car/car.go | 2 +- ipld/car/cmd/car/compile.go | 2 +- ipld/car/cmd/car/create.go | 2 +- ipld/car/selectivecar_test.go | 2 +- ipld/car/v2/block_reader.go | 2 +- ipld/car/v2/blockstore/example_test.go | 2 +- ipld/car/v2/blockstore/readonly.go | 2 +- ipld/car/v2/blockstore/readonly_test.go | 2 +- ipld/car/v2/blockstore/readwrite.go | 2 +- ipld/car/v2/blockstore/readwrite_test.go | 2 +- ipld/car/v2/index/index_test.go | 2 +- ipld/car/v2/internal/carv1/car.go | 2 +- ipld/car/v2/selective_test.go | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index bf773779a..909a83ad9 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -6,10 +6,10 @@ import ( "fmt" "io" - blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" format "github.com/ipfs/go-ipld-format" + blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipfs/go-merkledag" util "github.com/ipld/go-car/util" diff --git a/ipld/car/cmd/car/compile.go b/ipld/car/cmd/car/compile.go index 0a74d1c7e..f6a1b4979 100644 --- a/ipld/car/cmd/car/compile.go +++ b/ipld/car/cmd/car/compile.go @@ -11,8 +11,8 @@ import ( "strings" "unicode/utf8" - blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" + blocks "github.com/ipfs/go-libipfs/blocks" carv1 "github.com/ipld/go-car" "github.com/ipld/go-car/util" carv2 "github.com/ipld/go-car/v2" diff --git a/ipld/car/cmd/car/create.go b/ipld/car/cmd/car/create.go index 3a76c9131..7b50b6458 100644 --- a/ipld/car/cmd/car/create.go +++ b/ipld/car/cmd/car/create.go @@ -7,8 +7,8 @@ import ( "io" "path" - blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" + blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipfs/go-unixfsnode/data/builder" "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/blockstore" diff --git a/ipld/car/selectivecar_test.go b/ipld/car/selectivecar_test.go index 59a4c8e1f..33c2ce705 100644 --- a/ipld/car/selectivecar_test.go +++ b/ipld/car/selectivecar_test.go @@ -5,9 +5,9 @@ import ( "context" "testing" - blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" + blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipfs/go-merkledag" dstest "github.com/ipfs/go-merkledag/test" car "github.com/ipld/go-car" diff --git a/ipld/car/v2/block_reader.go b/ipld/car/v2/block_reader.go index 0c38412fd..27d134b05 100644 --- a/ipld/car/v2/block_reader.go +++ b/ipld/car/v2/block_reader.go @@ -4,8 +4,8 @@ import ( "fmt" "io" - blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" + blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/carv1/util" internalio "github.com/ipld/go-car/v2/internal/io" diff --git a/ipld/car/v2/blockstore/example_test.go b/ipld/car/v2/blockstore/example_test.go index ec16383eb..9c7524a9c 100644 --- a/ipld/car/v2/blockstore/example_test.go +++ b/ipld/car/v2/blockstore/example_test.go @@ -7,8 +7,8 @@ import ( "path/filepath" "time" - blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" + blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipfs/go-merkledag" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/blockstore" diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index bfca69112..ab1c6b527 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -8,10 +8,10 @@ import ( "io" "sync" - blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" format "github.com/ipfs/go-ipld-format" + blocks "github.com/ipfs/go-libipfs/blocks" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1" diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 6b1b009f8..8a03597d2 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - blocks "github.com/ipfs/go-block-format" + blocks "github.com/ipfs/go-libipfs/blocks\" "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" "github.com/ipfs/go-merkledag" diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 542d0df42..6d45a0fa0 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -7,9 +7,9 @@ import ( "io" "os" - blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" + blocks "github.com/ipfs/go-libipfs/blocks" "github.com/multiformats/go-varint" carv2 "github.com/ipld/go-car/v2" diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 4553fd15f..cbcd1d4c1 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -13,10 +13,10 @@ import ( "testing" "time" - blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" format "github.com/ipfs/go-ipld-format" + blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipfs/go-merkledag" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/blockstore" diff --git a/ipld/car/v2/index/index_test.go b/ipld/car/v2/index/index_test.go index 972b4c669..b7f83d3c4 100644 --- a/ipld/car/v2/index/index_test.go +++ b/ipld/car/v2/index/index_test.go @@ -7,7 +7,7 @@ import ( "path/filepath" "testing" - blocks "github.com/ipfs/go-block-format" + blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/carv1/util" "github.com/multiformats/go-multicodec" diff --git a/ipld/car/v2/internal/carv1/car.go b/ipld/car/v2/internal/carv1/car.go index 8ad012aa6..f56b38246 100644 --- a/ipld/car/v2/internal/carv1/car.go +++ b/ipld/car/v2/internal/carv1/car.go @@ -7,10 +7,10 @@ import ( "github.com/ipld/go-car/v2/internal/carv1/util" - blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" format "github.com/ipfs/go-ipld-format" + blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipfs/go-merkledag" ) diff --git a/ipld/car/v2/selective_test.go b/ipld/car/v2/selective_test.go index 924351d46..969330928 100644 --- a/ipld/car/v2/selective_test.go +++ b/ipld/car/v2/selective_test.go @@ -8,8 +8,8 @@ import ( "path" "testing" - blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" + blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipfs/go-unixfsnode" "github.com/ipfs/go-unixfsnode/data/builder" "github.com/ipld/go-car/v2" From b4cd7d47161f7856a8e190c9241946baecb5270c Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 27 Jan 2023 14:29:53 +0100 Subject: [PATCH 3768/3817] fix: update go-block-format to the version that includes the stubs This commit was moved from ipld/go-car@020b14ce754924964ef37c66876688e9b84a17b1 --- ipld/car/v2/blockstore/readonly_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 8a03597d2..0bb4df42e 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -8,9 +8,9 @@ import ( "testing" "time" - blocks "github.com/ipfs/go-libipfs/blocks\" "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" + blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipfs/go-merkledag" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/internal/carv1" From 8016547c7eec77d4c68eacc26d8b9c6000111f7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Leszko?= Date: Fri, 27 Jan 2023 20:35:43 +0100 Subject: [PATCH 3769/3817] Allow using WalkOption in WriteCar function This commit was moved from ipld/go-car@620c2941db620ad850ea02f644caca7340539661 --- ipld/car/car.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ipld/car/car.go b/ipld/car/car.go index 909a83ad9..026bbb735 100644 --- a/ipld/car/car.go +++ b/ipld/car/car.go @@ -40,11 +40,11 @@ type carWriter struct { type WalkFunc func(format.Node) ([]*format.Link, error) -func WriteCar(ctx context.Context, ds format.NodeGetter, roots []cid.Cid, w io.Writer) error { - return WriteCarWithWalker(ctx, ds, roots, w, DefaultWalkFunc) +func WriteCar(ctx context.Context, ds format.NodeGetter, roots []cid.Cid, w io.Writer, options ...merkledag.WalkOption) error { + return WriteCarWithWalker(ctx, ds, roots, w, DefaultWalkFunc, options...) } -func WriteCarWithWalker(ctx context.Context, ds format.NodeGetter, roots []cid.Cid, w io.Writer, walk WalkFunc) error { +func WriteCarWithWalker(ctx context.Context, ds format.NodeGetter, roots []cid.Cid, w io.Writer, walk WalkFunc, options ...merkledag.WalkOption) error { h := &CarHeader{ Roots: roots, @@ -58,7 +58,7 @@ func WriteCarWithWalker(ctx context.Context, ds format.NodeGetter, roots []cid.C cw := &carWriter{ds: ds, w: w, walk: walk} seen := cid.NewSet() for _, r := range roots { - if err := merkledag.Walk(ctx, cw.enumGetLinks, r, seen.Visit); err != nil { + if err := merkledag.Walk(ctx, cw.enumGetLinks, r, seen.Visit, options...); err != nil { return err } } From f17129cc05a628c4939899740ff0e7fac138dfdc Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 27 Jan 2023 18:18:06 +1100 Subject: [PATCH 3770/3817] fix: use goreleaser instead This commit was moved from ipld/go-car@c839519a92d238c9f3b8d3ddfb2dec1c5ae7c5ca --- ipld/car/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/ipld/car/.gitignore b/ipld/car/.gitignore index 3b831d277..b3f7c18ae 100644 --- a/ipld/car/.gitignore +++ b/ipld/car/.gitignore @@ -1,3 +1,4 @@ car/car main coverage.txt +dist/ From 3706bb9a0a95c3ef9d10b4f8171d0ff7ae216883 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Mon, 30 Jan 2023 11:03:31 -0500 Subject: [PATCH 3771/3817] test(cmd): add index creation test This commit was moved from ipld/go-car@4038bee446e9a87507783aea936493f0a5f9234b --- ipld/car/cmd/car/testdata/script/index-create.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 ipld/car/cmd/car/testdata/script/index-create.txt diff --git a/ipld/car/cmd/car/testdata/script/index-create.txt b/ipld/car/cmd/car/testdata/script/index-create.txt new file mode 100644 index 000000000..bfdfe65c1 --- /dev/null +++ b/ipld/car/cmd/car/testdata/script/index-create.txt @@ -0,0 +1,3 @@ +car index create ${INPUTS}/sample-v1.car sample-v1.car.idx +car detach-index ${INPUTS}/sample-wrapped-v2.car sample-wrapped-v2.car.idx +cmp sample-v1.car.idx sample-wrapped-v2.car.idx From 599eae33f8d0c6ca62160b679dade5bb9240ee20 Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Fri, 13 Jan 2023 21:04:12 -0800 Subject: [PATCH 3772/3817] feat(blockstore): implement a streaming read only storage This commit was moved from ipld/go-car@b72cec8b70c1a634b327c92ac355ede7785c608f --- ipld/car/v2/blockstore/readonlystorage.go | 161 ++++++++++++++++++ .../car/v2/blockstore/readonlystorage_test.go | 116 +++++++++++++ 2 files changed, 277 insertions(+) create mode 100644 ipld/car/v2/blockstore/readonlystorage.go create mode 100644 ipld/car/v2/blockstore/readonlystorage_test.go diff --git a/ipld/car/v2/blockstore/readonlystorage.go b/ipld/car/v2/blockstore/readonlystorage.go new file mode 100644 index 000000000..dfd5021fa --- /dev/null +++ b/ipld/car/v2/blockstore/readonlystorage.go @@ -0,0 +1,161 @@ +package blockstore + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + + "github.com/ipfs/go-cid" + blockstore "github.com/ipfs/go-ipfs-blockstore" + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/index" + internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/ipld/go-ipld-prime/storage" + "github.com/multiformats/go-varint" +) + +var _ storage.ReadableStorage = (*ReadOnlyStorage)(nil) +var _ storage.StreamingReadableStorage = (*ReadOnlyStorage)(nil) + +type ReadOnlyStorage struct { + ro *ReadOnly +} + +func NewReadOnlyStorage(backing io.ReaderAt, idx index.Index, opts ...carv2.Option) (*ReadOnlyStorage, error) { + ro, err := NewReadOnly(backing, idx, opts...) + if err != nil { + return nil, err + } + return &ReadOnlyStorage{ro: ro}, nil +} + +// OpenReadOnlyStorage opens a read-only blockstore from a CAR file (either v1 or v2), generating an index if it does not exist. +// Note, the generated index if the index does not exist is ephemeral and only stored in memory. +// See car.GenerateIndex and Index.Attach for persisting index onto a CAR file. +func OpenReadOnlyStorage(path string, opts ...carv2.Option) (*ReadOnlyStorage, error) { + ro, err := OpenReadOnly(path, opts...) + if err != nil { + return nil, err + } + return &ReadOnlyStorage{ro: ro}, nil +} + +func (ros *ReadOnlyStorage) Has(ctx context.Context, keyStr string) (bool, error) { + // Do the inverse of cid.KeyString(), + // which is how a valid key for this adapter must've been produced. + key, err := cidFromBinString(keyStr) + if err != nil { + return false, err + } + + return ros.ro.Has(ctx, key) +} + +func (ros *ReadOnlyStorage) Get(ctx context.Context, key string) ([]byte, error) { + // Do the inverse of cid.KeyString(), + // which is how a valid key for this adapter must've been produced. + k, err := cidFromBinString(key) + if err != nil { + return nil, err + } + + // Delegate the Get call. + block, err := ros.ro.Get(ctx, k) + if err != nil { + return nil, err + } + + // Unwrap the actual raw data for return. + // Discard the rest. (It's a shame there was an alloc for that structure.) + return block.RawData(), nil +} + +func (ros *ReadOnlyStorage) GetStream(ctx context.Context, keyStr string) (io.ReadCloser, error) { + // Do the inverse of cid.KeyString(), + // which is how a valid key for this adapter must've been produced. + key, err := cidFromBinString(keyStr) + if err != nil { + return nil, err + } + + // Check if the given CID has multihash.IDENTITY code + // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. + if digest, ok, err := isIdentity(key); err != nil { + return nil, err + } else if ok { + return io.NopCloser(bytes.NewReader(digest)), nil + } + + ros.ro.mu.RLock() + defer ros.ro.mu.RUnlock() + + if ros.ro.closed { + return nil, errClosed + } + + fnSize := -1 + var fnErr error + var foundOffset int64 + err = ros.ro.idx.GetAll(key, func(offset uint64) bool { + rdr := internalio.NewOffsetReadSeeker(ros.ro.backing, int64(offset)) + sectionLen, err := varint.ReadUvarint(rdr) + if err != nil { + fnErr = err + return false + } + cidLen, readCid, err := cid.CidFromReader(rdr) + if err != nil { + fnErr = err + return false + } + if ros.ro.opts.BlockstoreUseWholeCIDs { + if readCid.Equals(key) { + fnSize = int(sectionLen) - cidLen + foundOffset = rdr.Offset() + return false + } else { + return true // continue looking + } + } else { + if bytes.Equal(readCid.Hash(), key.Hash()) { + fnSize = int(sectionLen) - cidLen + foundOffset = rdr.Offset() + } + return false + } + }) + if errors.Is(err, index.ErrNotFound) { + return nil, blockstore.ErrNotFound + } else if err != nil { + return nil, err + } else if fnErr != nil { + return nil, fnErr + } + if fnSize == -1 { + return nil, blockstore.ErrNotFound + } + return io.NopCloser(io.NewSectionReader(ros.ro.backing, foundOffset, int64(fnSize))), nil +} + +func (ros *ReadOnlyStorage) Close() error { + return ros.ro.Close() +} + +func (ros *ReadOnlyStorage) Roots() ([]cid.Cid, error) { + return ros.ro.Roots() +} + +// Do the inverse of cid.KeyString(). +// (Unclear why go-cid doesn't offer a function for this itself.) +func cidFromBinString(key string) (cid.Cid, error) { + l, k, err := cid.CidFromBytes([]byte(key)) + if err != nil { + return cid.Undef, fmt.Errorf("key was not a cid: %w", err) + } + if l != len(key) { + return cid.Undef, fmt.Errorf("key was not a cid: had %d bytes leftover", len(key)-l) + } + return k, nil +} diff --git a/ipld/car/v2/blockstore/readonlystorage_test.go b/ipld/car/v2/blockstore/readonlystorage_test.go new file mode 100644 index 000000000..d783c0c90 --- /dev/null +++ b/ipld/car/v2/blockstore/readonlystorage_test.go @@ -0,0 +1,116 @@ +package blockstore + +import ( + "context" + "io" + "os" + "testing" + "time" + + "github.com/ipfs/go-cid" + blockstore "github.com/ipfs/go-ipfs-blockstore" + "github.com/ipfs/go-merkledag" + carv2 "github.com/ipld/go-car/v2" + "github.com/stretchr/testify/require" +) + +func TestReadOnlyStorageGetReturnsBlockstoreNotFoundWhenCidDoesNotExist(t *testing.T) { + subject, err := OpenReadOnlyStorage("../testdata/sample-v1.car") + require.NoError(t, err) + nonExistingKey := merkledag.NewRawNode([]byte("lobstermuncher")).Block.Cid() + + // Assert blockstore API returns blockstore.ErrNotFound + gotBlock, err := subject.Get(context.TODO(), string(nonExistingKey.Bytes())) + require.Equal(t, blockstore.ErrNotFound, err) + require.Nil(t, gotBlock) +} + +func TestReadOnlyStorage(t *testing.T) { + tests := []struct { + name string + v1OrV2path string + opts []carv2.Option + }{ + { + "OpenedWithCarV1", + "../testdata/sample-v1.car", + []carv2.Option{UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, + }, + { + "OpenedWithCarV2", + "../testdata/sample-wrapped-v2.car", + []carv2.Option{UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, + }, + { + "OpenedWithCarV1ZeroLenSection", + "../testdata/sample-v1-with-zero-len-section.car", + []carv2.Option{UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, + }, + { + "OpenedWithAnotherCarV1ZeroLenSection", + "../testdata/sample-v1-with-zero-len-section2.car", + []carv2.Option{UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.TODO() + subject, err := OpenReadOnlyStorage(tt.v1OrV2path, tt.opts...) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, subject.Close()) }) + + f, err := os.Open(tt.v1OrV2path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, f.Close()) }) + + reader, err := carv2.NewBlockReader(f, tt.opts...) + require.NoError(t, err) + + // Assert roots match v1 payload. + wantRoots := reader.Roots + gotRoots, err := subject.Roots() + require.NoError(t, err) + require.Equal(t, wantRoots, gotRoots) + + var wantCids []cid.Cid + for { + wantBlock, err := reader.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + + key := wantBlock.Cid() + wantCids = append(wantCids, key) + + // Assert blockstore contains key. + has, err := subject.Has(ctx, key.KeyString()) + require.NoError(t, err) + require.True(t, has) + + // Assert block itself matches v1 payload block. + gotBlock, err := subject.Get(ctx, key.KeyString()) + require.NoError(t, err) + require.Equal(t, wantBlock.RawData(), gotBlock) + + reader, err := subject.GetStream(ctx, key.KeyString()) + require.NoError(t, err) + data, err := io.ReadAll(reader) + require.NoError(t, err) + require.Equal(t, wantBlock.RawData(), data) + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) + defer cancel() + }) + } +} + +func TestNewReadOnlyStorageFailsOnUnknownVersion(t *testing.T) { + f, err := os.Open("../testdata/sample-rootless-v42.car") + require.NoError(t, err) + t.Cleanup(func() { f.Close() }) + subject, err := NewReadOnlyStorage(f, nil) + require.Errorf(t, err, "unsupported car version: 42") + require.Nil(t, subject) +} From 0e32290886fd8dd771af800d09c471e475e3ad6c Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 2 Feb 2023 15:53:15 +1100 Subject: [PATCH 3773/3817] feat: StorageCar as a Readable storage, separate from blockstore This commit was moved from ipld/go-car@369ddf3e702226bb2981722b22a6ba244e40fa43 --- ipld/car/v2/blockstore/readonlystorage.go | 161 ----------- .../car/v2/blockstore/readonlystorage_test.go | 116 -------- ipld/car/v2/blockstore/readwrite.go | 13 +- ipld/car/v2/internal/carv1/car.go | 16 ++ .../insertionindex}/insertionindex.go | 46 ++- ipld/car/v2/internal/store/identity.go | 16 ++ ipld/car/v2/internal/store/index.go | 30 ++ ipld/car/v2/internal/store/version.go | 17 ++ ipld/car/v2/storage/storage.go | 271 ++++++++++++++++++ ipld/car/v2/storage/storage_test.go | 122 ++++++++ 10 files changed, 501 insertions(+), 307 deletions(-) delete mode 100644 ipld/car/v2/blockstore/readonlystorage.go delete mode 100644 ipld/car/v2/blockstore/readonlystorage_test.go rename ipld/car/v2/{blockstore => internal/insertionindex}/insertionindex.go (84%) create mode 100644 ipld/car/v2/internal/store/identity.go create mode 100644 ipld/car/v2/internal/store/index.go create mode 100644 ipld/car/v2/internal/store/version.go create mode 100644 ipld/car/v2/storage/storage.go create mode 100644 ipld/car/v2/storage/storage_test.go diff --git a/ipld/car/v2/blockstore/readonlystorage.go b/ipld/car/v2/blockstore/readonlystorage.go deleted file mode 100644 index dfd5021fa..000000000 --- a/ipld/car/v2/blockstore/readonlystorage.go +++ /dev/null @@ -1,161 +0,0 @@ -package blockstore - -import ( - "bytes" - "context" - "errors" - "fmt" - "io" - - "github.com/ipfs/go-cid" - blockstore "github.com/ipfs/go-ipfs-blockstore" - carv2 "github.com/ipld/go-car/v2" - "github.com/ipld/go-car/v2/index" - internalio "github.com/ipld/go-car/v2/internal/io" - "github.com/ipld/go-ipld-prime/storage" - "github.com/multiformats/go-varint" -) - -var _ storage.ReadableStorage = (*ReadOnlyStorage)(nil) -var _ storage.StreamingReadableStorage = (*ReadOnlyStorage)(nil) - -type ReadOnlyStorage struct { - ro *ReadOnly -} - -func NewReadOnlyStorage(backing io.ReaderAt, idx index.Index, opts ...carv2.Option) (*ReadOnlyStorage, error) { - ro, err := NewReadOnly(backing, idx, opts...) - if err != nil { - return nil, err - } - return &ReadOnlyStorage{ro: ro}, nil -} - -// OpenReadOnlyStorage opens a read-only blockstore from a CAR file (either v1 or v2), generating an index if it does not exist. -// Note, the generated index if the index does not exist is ephemeral and only stored in memory. -// See car.GenerateIndex and Index.Attach for persisting index onto a CAR file. -func OpenReadOnlyStorage(path string, opts ...carv2.Option) (*ReadOnlyStorage, error) { - ro, err := OpenReadOnly(path, opts...) - if err != nil { - return nil, err - } - return &ReadOnlyStorage{ro: ro}, nil -} - -func (ros *ReadOnlyStorage) Has(ctx context.Context, keyStr string) (bool, error) { - // Do the inverse of cid.KeyString(), - // which is how a valid key for this adapter must've been produced. - key, err := cidFromBinString(keyStr) - if err != nil { - return false, err - } - - return ros.ro.Has(ctx, key) -} - -func (ros *ReadOnlyStorage) Get(ctx context.Context, key string) ([]byte, error) { - // Do the inverse of cid.KeyString(), - // which is how a valid key for this adapter must've been produced. - k, err := cidFromBinString(key) - if err != nil { - return nil, err - } - - // Delegate the Get call. - block, err := ros.ro.Get(ctx, k) - if err != nil { - return nil, err - } - - // Unwrap the actual raw data for return. - // Discard the rest. (It's a shame there was an alloc for that structure.) - return block.RawData(), nil -} - -func (ros *ReadOnlyStorage) GetStream(ctx context.Context, keyStr string) (io.ReadCloser, error) { - // Do the inverse of cid.KeyString(), - // which is how a valid key for this adapter must've been produced. - key, err := cidFromBinString(keyStr) - if err != nil { - return nil, err - } - - // Check if the given CID has multihash.IDENTITY code - // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. - if digest, ok, err := isIdentity(key); err != nil { - return nil, err - } else if ok { - return io.NopCloser(bytes.NewReader(digest)), nil - } - - ros.ro.mu.RLock() - defer ros.ro.mu.RUnlock() - - if ros.ro.closed { - return nil, errClosed - } - - fnSize := -1 - var fnErr error - var foundOffset int64 - err = ros.ro.idx.GetAll(key, func(offset uint64) bool { - rdr := internalio.NewOffsetReadSeeker(ros.ro.backing, int64(offset)) - sectionLen, err := varint.ReadUvarint(rdr) - if err != nil { - fnErr = err - return false - } - cidLen, readCid, err := cid.CidFromReader(rdr) - if err != nil { - fnErr = err - return false - } - if ros.ro.opts.BlockstoreUseWholeCIDs { - if readCid.Equals(key) { - fnSize = int(sectionLen) - cidLen - foundOffset = rdr.Offset() - return false - } else { - return true // continue looking - } - } else { - if bytes.Equal(readCid.Hash(), key.Hash()) { - fnSize = int(sectionLen) - cidLen - foundOffset = rdr.Offset() - } - return false - } - }) - if errors.Is(err, index.ErrNotFound) { - return nil, blockstore.ErrNotFound - } else if err != nil { - return nil, err - } else if fnErr != nil { - return nil, fnErr - } - if fnSize == -1 { - return nil, blockstore.ErrNotFound - } - return io.NopCloser(io.NewSectionReader(ros.ro.backing, foundOffset, int64(fnSize))), nil -} - -func (ros *ReadOnlyStorage) Close() error { - return ros.ro.Close() -} - -func (ros *ReadOnlyStorage) Roots() ([]cid.Cid, error) { - return ros.ro.Roots() -} - -// Do the inverse of cid.KeyString(). -// (Unclear why go-cid doesn't offer a function for this itself.) -func cidFromBinString(key string) (cid.Cid, error) { - l, k, err := cid.CidFromBytes([]byte(key)) - if err != nil { - return cid.Undef, fmt.Errorf("key was not a cid: %w", err) - } - if l != len(key) { - return cid.Undef, fmt.Errorf("key was not a cid: had %d bytes leftover", len(key)-l) - } - return k, nil -} diff --git a/ipld/car/v2/blockstore/readonlystorage_test.go b/ipld/car/v2/blockstore/readonlystorage_test.go deleted file mode 100644 index d783c0c90..000000000 --- a/ipld/car/v2/blockstore/readonlystorage_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package blockstore - -import ( - "context" - "io" - "os" - "testing" - "time" - - "github.com/ipfs/go-cid" - blockstore "github.com/ipfs/go-ipfs-blockstore" - "github.com/ipfs/go-merkledag" - carv2 "github.com/ipld/go-car/v2" - "github.com/stretchr/testify/require" -) - -func TestReadOnlyStorageGetReturnsBlockstoreNotFoundWhenCidDoesNotExist(t *testing.T) { - subject, err := OpenReadOnlyStorage("../testdata/sample-v1.car") - require.NoError(t, err) - nonExistingKey := merkledag.NewRawNode([]byte("lobstermuncher")).Block.Cid() - - // Assert blockstore API returns blockstore.ErrNotFound - gotBlock, err := subject.Get(context.TODO(), string(nonExistingKey.Bytes())) - require.Equal(t, blockstore.ErrNotFound, err) - require.Nil(t, gotBlock) -} - -func TestReadOnlyStorage(t *testing.T) { - tests := []struct { - name string - v1OrV2path string - opts []carv2.Option - }{ - { - "OpenedWithCarV1", - "../testdata/sample-v1.car", - []carv2.Option{UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, - }, - { - "OpenedWithCarV2", - "../testdata/sample-wrapped-v2.car", - []carv2.Option{UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, - }, - { - "OpenedWithCarV1ZeroLenSection", - "../testdata/sample-v1-with-zero-len-section.car", - []carv2.Option{UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, - }, - { - "OpenedWithAnotherCarV1ZeroLenSection", - "../testdata/sample-v1-with-zero-len-section2.car", - []carv2.Option{UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ctx := context.TODO() - subject, err := OpenReadOnlyStorage(tt.v1OrV2path, tt.opts...) - require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, subject.Close()) }) - - f, err := os.Open(tt.v1OrV2path) - require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, f.Close()) }) - - reader, err := carv2.NewBlockReader(f, tt.opts...) - require.NoError(t, err) - - // Assert roots match v1 payload. - wantRoots := reader.Roots - gotRoots, err := subject.Roots() - require.NoError(t, err) - require.Equal(t, wantRoots, gotRoots) - - var wantCids []cid.Cid - for { - wantBlock, err := reader.Next() - if err == io.EOF { - break - } - require.NoError(t, err) - - key := wantBlock.Cid() - wantCids = append(wantCids, key) - - // Assert blockstore contains key. - has, err := subject.Has(ctx, key.KeyString()) - require.NoError(t, err) - require.True(t, has) - - // Assert block itself matches v1 payload block. - gotBlock, err := subject.Get(ctx, key.KeyString()) - require.NoError(t, err) - require.Equal(t, wantBlock.RawData(), gotBlock) - - reader, err := subject.GetStream(ctx, key.KeyString()) - require.NoError(t, err) - data, err := io.ReadAll(reader) - require.NoError(t, err) - require.Equal(t, wantBlock.RawData(), data) - } - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) - defer cancel() - }) - } -} - -func TestNewReadOnlyStorageFailsOnUnknownVersion(t *testing.T) { - f, err := os.Open("../testdata/sample-rootless-v42.car") - require.NoError(t, err) - t.Cleanup(func() { f.Close() }) - subject, err := NewReadOnlyStorage(f, nil) - require.Errorf(t, err, "unsupported car version: 42") - require.Nil(t, subject) -} diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 6d45a0fa0..c6a7e9b8f 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -16,6 +16,7 @@ import ( "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/carv1/util" + "github.com/ipld/go-car/v2/internal/insertionindex" internalio "github.com/ipld/go-car/v2/internal/io" ) @@ -35,7 +36,7 @@ type ReadWrite struct { f *os.File dataWriter *internalio.OffsetWriteSeeker - idx *insertionIndex + idx *insertionindex.InsertionIndex header carv2.Header opts carv2.Options @@ -134,7 +135,7 @@ func OpenReadWriteFile(f *os.File, roots []cid.Cid, opts ...carv2.Option) (*Read // Set the header fileld before applying options since padding options may modify header. rwbs := &ReadWrite{ f: f, - idx: newInsertionIndex(), + idx: insertionindex.NewInsertionIndex(), header: carv2.NewHeader(0), opts: carv2.ApplyOptions(opts...), } @@ -309,7 +310,7 @@ func (b *ReadWrite) resumeWithRoots(v2 bool, roots []cid.Cid) error { if err != nil { return err } - b.idx.insertNoReplace(c, uint64(sectionOffset)) + b.idx.InsertNoReplace(c, uint64(sectionOffset)) // Seek to the next section by skipping the block. // The section length includes the CID, so subtract it. @@ -366,7 +367,7 @@ func (b *ReadWrite) PutMany(ctx context.Context, blks []blocks.Block) error { } if !b.opts.BlockstoreAllowDuplicatePuts { - if b.ronly.opts.BlockstoreUseWholeCIDs && b.idx.hasExactCID(c) { + if b.ronly.opts.BlockstoreUseWholeCIDs && b.idx.HasExactCID(c) { continue // deduplicated by CID } if !b.ronly.opts.BlockstoreUseWholeCIDs { @@ -381,7 +382,7 @@ func (b *ReadWrite) PutMany(ctx context.Context, blks []blocks.Block) error { if err := util.LdWrite(b.dataWriter, c.Bytes(), bl.RawData()); err != nil { return err } - b.idx.insertNoReplace(c, n) + b.idx.InsertNoReplace(c, n) } return nil } @@ -428,7 +429,7 @@ func (b *ReadWrite) Finalize() error { defer b.ronly.closeWithoutMutex() // TODO if index not needed don't bother flattening it. - fi, err := b.idx.flatten(b.opts.IndexCodec) + fi, err := b.idx.Flatten(b.opts.IndexCodec) if err != nil { return err } diff --git a/ipld/car/v2/internal/carv1/car.go b/ipld/car/v2/internal/carv1/car.go index f56b38246..a38744a3c 100644 --- a/ipld/car/v2/internal/carv1/car.go +++ b/ipld/car/v2/internal/carv1/car.go @@ -12,6 +12,7 @@ import ( format "github.com/ipfs/go-ipld-format" blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipfs/go-merkledag" + internalio "github.com/ipld/go-car/v2/internal/io" ) const DefaultMaxAllowedHeaderSize uint64 = 32 << 20 // 32MiB @@ -59,6 +60,21 @@ func WriteCar(ctx context.Context, ds format.NodeGetter, roots []cid.Cid, w io.W return nil } +func ReadHeaderAt(at io.ReaderAt, maxReadBytes uint64) (*CarHeader, error) { + var rr io.Reader + switch r := at.(type) { + case io.Reader: + rr = r + default: + var err error + rr, err = internalio.NewOffsetReadSeeker(r, 0) + if err != nil { + return nil, err + } + } + return ReadHeader(rr, maxReadBytes) +} + func ReadHeader(r io.Reader, maxReadBytes uint64) (*CarHeader, error) { hb, err := util.LdRead(r, false, maxReadBytes) if err != nil { diff --git a/ipld/car/v2/blockstore/insertionindex.go b/ipld/car/v2/internal/insertionindex/insertionindex.go similarity index 84% rename from ipld/car/v2/blockstore/insertionindex.go rename to ipld/car/v2/internal/insertionindex/insertionindex.go index 1e480b3f9..449176acb 100644 --- a/ipld/car/v2/blockstore/insertionindex.go +++ b/ipld/car/v2/internal/insertionindex/insertionindex.go @@ -1,4 +1,4 @@ -package blockstore +package insertionindex import ( "bytes" @@ -24,16 +24,18 @@ var ( insertionIndexCodec = multicodec.Code(0x300003) ) -type ( - insertionIndex struct { - items llrb.LLRB - } +type InsertionIndex struct { + items llrb.LLRB +} - recordDigest struct { - digest []byte - index.Record - } -) +func NewInsertionIndex() *InsertionIndex { + return &InsertionIndex{} +} + +type recordDigest struct { + digest []byte + index.Record +} func (r recordDigest) Less(than llrb.Item) bool { other, ok := than.(recordDigest) @@ -61,11 +63,11 @@ func newRecordFromCid(c cid.Cid, at uint64) recordDigest { return recordDigest{d.Digest, index.Record{Cid: c, Offset: at}} } -func (ii *insertionIndex) insertNoReplace(key cid.Cid, n uint64) { +func (ii *InsertionIndex) InsertNoReplace(key cid.Cid, n uint64) { ii.items.InsertNoReplace(newRecordFromCid(key, n)) } -func (ii *insertionIndex) Get(c cid.Cid) (uint64, error) { +func (ii *InsertionIndex) Get(c cid.Cid) (uint64, error) { d, err := multihash.Decode(c.Hash()) if err != nil { return 0, err @@ -83,7 +85,7 @@ func (ii *insertionIndex) Get(c cid.Cid) (uint64, error) { return r.Record.Offset, nil } -func (ii *insertionIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { +func (ii *InsertionIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { d, err := multihash.Decode(c.Hash()) if err != nil { return err @@ -107,7 +109,7 @@ func (ii *insertionIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { return nil } -func (ii *insertionIndex) Marshal(w io.Writer) (uint64, error) { +func (ii *InsertionIndex) Marshal(w io.Writer) (uint64, error) { l := uint64(0) if err := binary.Write(w, binary.LittleEndian, int64(ii.items.Len())); err != nil { return l, err @@ -125,7 +127,7 @@ func (ii *insertionIndex) Marshal(w io.Writer) (uint64, error) { return l, err } -func (ii *insertionIndex) Unmarshal(r io.Reader) error { +func (ii *InsertionIndex) Unmarshal(r io.Reader) error { var length int64 if err := binary.Read(r, binary.LittleEndian, &length); err != nil { return err @@ -141,7 +143,7 @@ func (ii *insertionIndex) Unmarshal(r io.Reader) error { return nil } -func (ii *insertionIndex) ForEach(f func(multihash.Multihash, uint64) error) error { +func (ii *InsertionIndex) ForEach(f func(multihash.Multihash, uint64) error) error { var errr error ii.items.AscendGreaterOrEqual(ii.items.Min(), func(i llrb.Item) bool { r := i.(recordDigest).Record @@ -155,11 +157,11 @@ func (ii *insertionIndex) ForEach(f func(multihash.Multihash, uint64) error) err return errr } -func (ii *insertionIndex) Codec() multicodec.Code { +func (ii *InsertionIndex) Codec() multicodec.Code { return insertionIndexCodec } -func (ii *insertionIndex) Load(rs []index.Record) error { +func (ii *InsertionIndex) Load(rs []index.Record) error { for _, r := range rs { rec := newRecordDigest(r) if rec.digest == nil { @@ -170,12 +172,8 @@ func (ii *insertionIndex) Load(rs []index.Record) error { return nil } -func newInsertionIndex() *insertionIndex { - return &insertionIndex{} -} - // flatten returns a formatted index in the given codec for more efficient subsequent loading. -func (ii *insertionIndex) flatten(codec multicodec.Code) (index.Index, error) { +func (ii *InsertionIndex) Flatten(codec multicodec.Code) (index.Index, error) { si, err := index.New(codec) if err != nil { return nil, err @@ -200,7 +198,7 @@ func (ii *insertionIndex) flatten(codec multicodec.Code) (index.Index, error) { // but it's separate as it allows us to compare Record.Cid directly, // whereas GetAll just provides Record.Offset. -func (ii *insertionIndex) hasExactCID(c cid.Cid) bool { +func (ii *InsertionIndex) HasExactCID(c cid.Cid) bool { d, err := multihash.Decode(c.Hash()) if err != nil { panic(err) diff --git a/ipld/car/v2/internal/store/identity.go b/ipld/car/v2/internal/store/identity.go new file mode 100644 index 000000000..85dcadc5d --- /dev/null +++ b/ipld/car/v2/internal/store/identity.go @@ -0,0 +1,16 @@ +package store + +import ( + "github.com/ipfs/go-cid" + "github.com/multiformats/go-multihash" +) + +func IsIdentity(key cid.Cid) (digest []byte, ok bool, err error) { + dmh, err := multihash.Decode(key.Hash()) + if err != nil { + return nil, false, err + } + ok = dmh.Code == multihash.IDENTITY + digest = dmh.Digest + return digest, ok, nil +} diff --git a/ipld/car/v2/internal/store/index.go b/ipld/car/v2/internal/store/index.go new file mode 100644 index 000000000..55b12755a --- /dev/null +++ b/ipld/car/v2/internal/store/index.go @@ -0,0 +1,30 @@ +package store + +import ( + "io" + + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/index" + internalio "github.com/ipld/go-car/v2/internal/io" +) + +func GenerateIndex(at io.ReaderAt, opts ...carv2.Option) (index.Index, error) { + var rs io.ReadSeeker + switch r := at.(type) { + case io.ReadSeeker: + rs = r + // The version may have been read from the given io.ReaderAt; therefore move back to the begining. + if _, err := rs.Seek(0, io.SeekStart); err != nil { + return nil, err + } + default: + var err error + rs, err = internalio.NewOffsetReadSeeker(r, 0) + if err != nil { + return nil, err + } + } + + // Note, we do not set any write options so that all write options fall back onto defaults. + return carv2.GenerateIndex(rs, opts...) +} diff --git a/ipld/car/v2/internal/store/version.go b/ipld/car/v2/internal/store/version.go new file mode 100644 index 000000000..b43496ef7 --- /dev/null +++ b/ipld/car/v2/internal/store/version.go @@ -0,0 +1,17 @@ +package store + +import ( + "io" + + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/internal/carv1" +) + +func ReadVersion(at io.ReaderAt, opts ...carv2.Option) (uint64, error) { + o := carv2.ApplyOptions(opts...) + header, err := carv1.ReadHeaderAt(at, o.MaxAllowedHeaderSize) + if err != nil { + return 0, err + } + return header.Version, nil +} diff --git a/ipld/car/v2/storage/storage.go b/ipld/car/v2/storage/storage.go new file mode 100644 index 000000000..3e41c9a1f --- /dev/null +++ b/ipld/car/v2/storage/storage.go @@ -0,0 +1,271 @@ +package storage + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "sync" + + "github.com/ipfs/go-cid" + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-car/v2/internal/carv1" + internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/ipld/go-car/v2/internal/store" + ipldstorage "github.com/ipld/go-ipld-prime/storage" + "github.com/multiformats/go-varint" +) + +var errClosed = fmt.Errorf("cannot use a carv2 storage after closing") + +// compatible with the go-ipld-format ErrNotFound, match against +// interface{NotFound() bool} + +type ErrNotFound struct { + Cid cid.Cid +} + +func (e ErrNotFound) Error() string { + if e.Cid == cid.Undef { + return "ipld: could not find node" + } + return "ipld: could not find " + e.Cid.String() +} + +func (e ErrNotFound) NotFound() bool { + return true +} + +type ReadableCar interface { + ipldstorage.ReadableStorage + ipldstorage.StreamingReadableStorage + Roots() ([]cid.Cid, error) +} + +type WritableCar interface { + ipldstorage.WritableStorage + ipldstorage.StreamingWritableStorage +} + +var _ ipldstorage.ReadableStorage = (*StorageCar)(nil) +var _ ipldstorage.StreamingReadableStorage = (*StorageCar)(nil) +var _ ReadableCar = (*StorageCar)(nil) + +type StorageCar struct { + idx index.Index + // iidx *insertionindex.InsertionIndex + reader io.ReaderAt + writer io.Writer + // header carv2.Header + opts carv2.Options + + closed bool + mu sync.RWMutex +} + +func NewReadable(reader io.ReaderAt, idx index.Index, opts ...carv2.Option) (ReadableCar, error) { + sc := &StorageCar{ + opts: carv2.ApplyOptions(opts...), + } + + version, err := store.ReadVersion(reader, opts...) + if err != nil { + return nil, err + } + switch version { + case 1: + if idx == nil { + if idx, err = store.GenerateIndex(reader, opts...); err != nil { + return nil, err + } + } + sc.reader = reader + sc.idx = idx + return sc, nil + case 2: + v2r, err := carv2.NewReader(reader, opts...) + if err != nil { + return nil, err + } + if idx == nil { + if v2r.Header.HasIndex() { + ir, err := v2r.IndexReader() + if err != nil { + return nil, err + } + idx, err = index.ReadFrom(ir) + if err != nil { + return nil, err + } + } else { + dr, err := v2r.DataReader() + if err != nil { + return nil, err + } + if idx, err = store.GenerateIndex(dr, opts...); err != nil { + return nil, err + } + } + } + sc.reader, err = v2r.DataReader() + if err != nil { + return nil, err + } + sc.idx = idx + return sc, nil + default: + return nil, fmt.Errorf("unsupported car version: %v", version) + } +} + +func (sc *StorageCar) Roots() ([]cid.Cid, error) { + ors, err := internalio.NewOffsetReadSeeker(sc.reader, 0) + if err != nil { + return nil, err + } + header, err := carv1.ReadHeader(ors, sc.opts.MaxAllowedHeaderSize) + if err != nil { + return nil, fmt.Errorf("error reading car header: %w", err) + } + return header.Roots, nil +} + +func (sc *StorageCar) Has(ctx context.Context, keyStr string) (bool, error) { + keyCid, err := cid.Cast([]byte(keyStr)) + if err != nil { + return false, err + } + + if !sc.opts.StoreIdentityCIDs { + // If we don't store identity CIDs then we can return them straight away as if they are here, + // otherwise we need to check for their existence. + // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. + if _, ok, err := store.IsIdentity(keyCid); err != nil { + return false, err + } else if ok { + return true, nil + } + } + + sc.mu.RLock() + defer sc.mu.RUnlock() + + if sc.closed { + return false, errClosed + } + + var fnFound bool + var fnErr error + err = sc.idx.GetAll(keyCid, func(offset uint64) bool { + uar, err := internalio.NewOffsetReadSeeker(sc.reader, int64(offset)) + if err != nil { + fnErr = err + return false + } + _, err = varint.ReadUvarint(uar) + if err != nil { + fnErr = err + return false + } + _, readCid, err := cid.CidFromReader(uar) + if err != nil { + fnErr = err + return false + } + if sc.opts.BlockstoreUseWholeCIDs { + fnFound = readCid.Equals(keyCid) + return !fnFound // continue looking if we haven't found it + } else { + fnFound = bytes.Equal(readCid.Hash(), keyCid.Hash()) + return false + } + }) + if errors.Is(err, index.ErrNotFound) { + return false, nil + } else if err != nil { + return false, err + } + return fnFound, fnErr +} + +func (sc *StorageCar) Get(ctx context.Context, keyStr string) ([]byte, error) { + rdr, err := sc.GetStream(ctx, keyStr) + if err != nil { + return nil, err + } + return io.ReadAll(rdr) +} + +func (sc *StorageCar) GetStream(ctx context.Context, keyStr string) (io.ReadCloser, error) { + keyCid, err := cid.Cast([]byte(keyStr)) + if err != nil { + return nil, err + } + + if !sc.opts.StoreIdentityCIDs { + // If we don't store identity CIDs then we can return them straight away as if they are here, + // otherwise we need to check for their existence. + // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. + if digest, ok, err := store.IsIdentity(keyCid); err != nil { + return nil, err + } else if ok { + return io.NopCloser(bytes.NewReader(digest)), nil + } + } + + sc.mu.RLock() + defer sc.mu.RUnlock() + + if sc.closed { + return nil, errClosed + } + + fnSize := -1 + var fnErr error + var foundOffset int64 + err = sc.idx.GetAll(keyCid, func(offset uint64) bool { + rdr, err := internalio.NewOffsetReadSeeker(sc.reader, int64(offset)) + if err != nil { + fnErr = err + return false + } + sectionLen, err := varint.ReadUvarint(rdr) + if err != nil { + fnErr = err + return false + } + cidLen, readCid, err := cid.CidFromReader(rdr) + if err != nil { + fnErr = err + return false + } + if sc.opts.BlockstoreUseWholeCIDs { + if readCid.Equals(keyCid) { + fnSize = int(sectionLen) - cidLen + foundOffset = rdr.(interface{ Offset() int64 }).Offset() + return false + } else { + return true // continue looking + } + } else { + if bytes.Equal(readCid.Hash(), keyCid.Hash()) { + fnSize = int(sectionLen) - cidLen + foundOffset = rdr.(interface{ Offset() int64 }).Offset() + } + return false + } + }) + if errors.Is(err, index.ErrNotFound) { + return nil, ErrNotFound{Cid: keyCid} + } else if err != nil { + return nil, err + } else if fnErr != nil { + return nil, fnErr + } + if fnSize == -1 { + return nil, ErrNotFound{Cid: keyCid} + } + return io.NopCloser(io.NewSectionReader(sc.reader, foundOffset, int64(fnSize))), nil +} diff --git a/ipld/car/v2/storage/storage_test.go b/ipld/car/v2/storage/storage_test.go new file mode 100644 index 000000000..10f71c86b --- /dev/null +++ b/ipld/car/v2/storage/storage_test.go @@ -0,0 +1,122 @@ +package storage_test + +import ( + "context" + "io" + "os" + "testing" + + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/blockstore" + "github.com/ipld/go-car/v2/storage" + "github.com/multiformats/go-multicodec" + "github.com/stretchr/testify/require" +) + +func TestReadable(t *testing.T) { + tests := []struct { + name string + v1OrV2path string + opts []carv2.Option + noIdCids bool + }{ + { + "OpenedWithCarV1", + "../testdata/sample-v1.car", + []carv2.Option{blockstore.UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, + // index is made, but identity CIDs are included so they'll be found + false, + }, + { + "OpenedWithCarV1_NoIdentityCID", + "../testdata/sample-v1.car", + []carv2.Option{blockstore.UseWholeCIDs(true)}, + // index is made, identity CIDs are not included, but we always short-circuit when StoreIdentityCIDs(false) + false, + }, + { + "OpenedWithCarV2", + "../testdata/sample-wrapped-v2.car", + []carv2.Option{blockstore.UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, + // index already exists, but was made without identity CIDs, but opening with StoreIdentityCIDs(true) means we check the index + true, + }, + { + "OpenedWithCarV2_NoIdentityCID", + "../testdata/sample-wrapped-v2.car", + []carv2.Option{blockstore.UseWholeCIDs(true)}, + // index already exists, it was made without identity CIDs, but we always short-circuit when StoreIdentityCIDs(false) + false, + }, + { + "OpenedWithCarV1ZeroLenSection", + "../testdata/sample-v1-with-zero-len-section.car", + []carv2.Option{blockstore.UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, + false, + }, + { + "OpenedWithAnotherCarV1ZeroLenSection", + "../testdata/sample-v1-with-zero-len-section2.car", + []carv2.Option{blockstore.UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, + false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.TODO() + subjectReader, err := os.Open(tt.v1OrV2path) + require.NoError(t, err) + subject, err := storage.NewReadable(subjectReader, nil, tt.opts...) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, subjectReader.Close()) }) + + f, err := os.Open(tt.v1OrV2path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, f.Close()) }) + + reader, err := carv2.NewBlockReader(f, tt.opts...) + require.NoError(t, err) + + // Assert roots match v1 payload. + wantRoots := reader.Roots + gotRoots, err := subject.Roots() + require.NoError(t, err) + require.Equal(t, wantRoots, gotRoots) + + for { + wantBlock, err := reader.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + + key := wantBlock.Cid() + + // Assert blockstore contains key. + has, err := subject.Has(ctx, key.KeyString()) + require.NoError(t, err) + if key.Prefix().MhType == uint64(multicodec.Identity) && tt.noIdCids { + // fixture wasn't made with StoreIdentityCIDs, but we opened it with StoreIdentityCIDs, + // so they aren't there to find + require.False(t, has) + } else { + require.True(t, has) + } + + // Assert block itself matches v1 payload block. + if has { + gotBlock, err := subject.Get(ctx, key.KeyString()) + require.NoError(t, err) + require.Equal(t, wantBlock.RawData(), gotBlock) + + reader, err := subject.GetStream(ctx, key.KeyString()) + require.NoError(t, err) + data, err := io.ReadAll(reader) + require.NoError(t, err) + require.Equal(t, wantBlock.RawData(), data) + } + } + }) + } +} From d851b580d01f997967d4eb339ea56eaee299209b Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 3 Feb 2023 17:59:45 +1100 Subject: [PATCH 3774/3817] feat: add Writable functionality to StorageCar This commit was moved from ipld/go-car@b42a961df8d402396f121c2c0fe492b484b9a0b1 --- ipld/car/v2/blockstore/readonly.go | 18 +- ipld/car/v2/blockstore/readwrite.go | 3 +- ipld/car/v2/blockstore/readwrite_test.go | 2 +- .../internal/insertionindex/insertionindex.go | 24 +- ipld/car/v2/internal/io/converter.go | 38 ++ ipld/car/v2/storage/notfound.go | 39 ++ ipld/car/v2/storage/storage.go | 346 ++++++++++++------ ipld/car/v2/storage/storage_test.go | 255 ++++++++++++- 8 files changed, 568 insertions(+), 157 deletions(-) create mode 100644 ipld/car/v2/storage/notfound.go diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index ab1c6b527..217771cbc 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -17,7 +17,7 @@ import ( "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/carv1/util" internalio "github.com/ipld/go-car/v2/internal/io" - "github.com/multiformats/go-multihash" + "github.com/ipld/go-car/v2/internal/store" "github.com/multiformats/go-varint" "golang.org/x/exp/mmap" ) @@ -229,7 +229,7 @@ func (b *ReadOnly) Has(ctx context.Context, key cid.Cid) (bool, error) { // If we don't store identity CIDs then we can return them straight away as if they are here, // otherwise we need to check for their existence. // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. - if _, ok, err := isIdentity(key); err != nil { + if _, ok, err := store.IsIdentity(key); err != nil { return false, err } else if ok { return true, nil @@ -290,7 +290,7 @@ func (b *ReadOnly) Get(ctx context.Context, key cid.Cid) (blocks.Block, error) { // If we don't store identity CIDs then we can return them straight away as if they are here, // otherwise we need to check for their existence. // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. - if digest, ok, err := isIdentity(key); err != nil { + if digest, ok, err := store.IsIdentity(key); err != nil { return nil, err } else if ok { return blocks.NewBlockWithCid(digest, key) @@ -343,7 +343,7 @@ func (b *ReadOnly) Get(ctx context.Context, key cid.Cid) (blocks.Block, error) { func (b *ReadOnly) GetSize(ctx context.Context, key cid.Cid) (int, error) { // Check if the given CID has multihash.IDENTITY code // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. - if digest, ok, err := isIdentity(key); err != nil { + if digest, ok, err := store.IsIdentity(key); err != nil { return 0, err } else if ok { return len(digest), nil @@ -401,16 +401,6 @@ func (b *ReadOnly) GetSize(ctx context.Context, key cid.Cid) (int, error) { return fnSize, nil } -func isIdentity(key cid.Cid) (digest []byte, ok bool, err error) { - dmh, err := multihash.Decode(key.Hash()) - if err != nil { - return nil, false, err - } - ok = dmh.Code == multihash.IDENTITY - digest = dmh.Digest - return digest, ok, nil -} - // Put is not supported and always returns an error. func (b *ReadOnly) Put(context.Context, blocks.Block) error { return errReadOnly diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index c6a7e9b8f..43b42d019 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -18,6 +18,7 @@ import ( "github.com/ipld/go-car/v2/internal/carv1/util" "github.com/ipld/go-car/v2/internal/insertionindex" internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/ipld/go-car/v2/internal/store" ) var _ blockstore.Blockstore = (*ReadWrite)(nil) @@ -350,7 +351,7 @@ func (b *ReadWrite) PutMany(ctx context.Context, blks []blocks.Block) error { // If StoreIdentityCIDs option is disabled then treat IDENTITY CIDs like IdStore. if !b.opts.StoreIdentityCIDs { // Check for IDENTITY CID. If IDENTITY, ignore and move to the next block. - if _, ok, err := isIdentity(c); err != nil { + if _, ok, err := store.IsIdentity(c); err != nil { return err } else if ok { continue diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index cbcd1d4c1..04da36fe8 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -47,7 +47,7 @@ func TestReadWriteGetReturnsBlockstoreNotFoundWhenCidDoesNotExist(t *testing.T) require.Nil(t, gotBlock) } -func TestBlockstoreX(t *testing.T) { +func TestBlockstore(t *testing.T) { originalCARv1Path := "../testdata/sample-v1.car" originalCARv1ComparePath := "../testdata/sample-v1-noidentity.car" originalCARv1ComparePathStat, err := os.Stat(originalCARv1ComparePath) diff --git a/ipld/car/v2/internal/insertionindex/insertionindex.go b/ipld/car/v2/internal/insertionindex/insertionindex.go index 449176acb..67dcb8979 100644 --- a/ipld/car/v2/internal/insertionindex/insertionindex.go +++ b/ipld/car/v2/internal/insertionindex/insertionindex.go @@ -68,21 +68,37 @@ func (ii *InsertionIndex) InsertNoReplace(key cid.Cid, n uint64) { } func (ii *InsertionIndex) Get(c cid.Cid) (uint64, error) { - d, err := multihash.Decode(c.Hash()) + record, err := ii.getRecord(c) if err != nil { return 0, err } + return record.Offset, nil +} + +func (ii *InsertionIndex) getRecord(c cid.Cid) (index.Record, error) { + d, err := multihash.Decode(c.Hash()) + if err != nil { + return index.Record{}, err + } entry := recordDigest{digest: d.Digest} e := ii.items.Get(entry) if e == nil { - return 0, index.ErrNotFound + return index.Record{}, index.ErrNotFound } r, ok := e.(recordDigest) if !ok { - return 0, errUnsupported + return index.Record{}, errUnsupported } - return r.Record.Offset, nil + return r.Record, nil +} + +func (ii *InsertionIndex) GetCid(c cid.Cid) (uint64, cid.Cid, error) { + record, err := ii.getRecord(c) + if err != nil { + return 0, cid.Undef, err + } + return record.Offset, record.Cid, nil } func (ii *InsertionIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { diff --git a/ipld/car/v2/internal/io/converter.go b/ipld/car/v2/internal/io/converter.go index 2b29d0a88..5550f86f2 100644 --- a/ipld/car/v2/internal/io/converter.go +++ b/ipld/car/v2/internal/io/converter.go @@ -10,6 +10,7 @@ var ( _ io.ByteReader = (*readSeekerPlusByte)(nil) _ io.ByteReader = (*discardingReadSeekerPlusByte)(nil) _ io.ReadSeeker = (*discardingReadSeekerPlusByte)(nil) + _ io.ReadSeeker = (*readerAtSeeker)(nil) _ io.ReaderAt = (*readSeekerAt)(nil) ) @@ -42,6 +43,12 @@ type ( rs io.ReadSeeker mu sync.Mutex } + + readerAtSeeker struct { + ra io.ReaderAt + position int64 + mu sync.Mutex + } ) func ToByteReader(r io.Reader) io.ByteReader { @@ -61,6 +68,13 @@ func ToByteReadSeeker(r io.Reader) ByteReadSeeker { return &discardingReadSeekerPlusByte{Reader: r} } +func ToReadSeeker(ra io.ReaderAt) io.ReadSeeker { + if rs, ok := ra.(io.ReadSeeker); ok { + return rs + } + return &readerAtSeeker{ra: ra} +} + func ToReaderAt(rs io.ReadSeeker) io.ReaderAt { if ra, ok := rs.(io.ReaderAt); ok { return ra @@ -106,6 +120,30 @@ func (drsb *discardingReadSeekerPlusByte) Seek(offset int64, whence int) (int64, } } +func (ras *readerAtSeeker) Read(p []byte) (n int, err error) { + ras.mu.Lock() + defer ras.mu.Unlock() + n, err = ras.ra.ReadAt(p, ras.position) + ras.position += int64(n) + return n, err +} + +func (ras *readerAtSeeker) Seek(offset int64, whence int) (int64, error) { + ras.mu.Lock() + defer ras.mu.Unlock() + switch whence { + case io.SeekStart: + ras.position = offset + case io.SeekCurrent: + ras.position += offset + case io.SeekEnd: + panic("unsupported whence: io.SeekEnd") + default: + panic("unsupported whence") + } + return ras.position, nil +} + func (rsa *readSeekerAt) ReadAt(p []byte, off int64) (n int, err error) { rsa.mu.Lock() defer rsa.mu.Unlock() diff --git a/ipld/car/v2/storage/notfound.go b/ipld/car/v2/storage/notfound.go new file mode 100644 index 000000000..82153e2f2 --- /dev/null +++ b/ipld/car/v2/storage/notfound.go @@ -0,0 +1,39 @@ +package storage + +import "github.com/ipfs/go-cid" + +// compatible with the go-ipld-format ErrNotFound, match against +// interface{NotFound() bool} +// this could go into go-ipld-prime, but for now we'll just exercise the +// feature-test pattern + +type ErrNotFound struct { + Cid cid.Cid +} + +func (e ErrNotFound) Error() string { + if e.Cid == cid.Undef { + return "ipld: could not find node" + } + return "ipld: could not find " + e.Cid.String() +} + +func (e ErrNotFound) NotFound() bool { + return true +} + +func (e ErrNotFound) Is(err error) bool { + switch err.(type) { + case ErrNotFound: + return true + default: + return false + } +} + +func IsNotFound(err error) bool { + if nf, ok := err.(interface{ NotFound() bool }); ok { + return nf.NotFound() + } + return false +} diff --git a/ipld/car/v2/storage/storage.go b/ipld/car/v2/storage/storage.go index 3e41c9a1f..82ad0806e 100644 --- a/ipld/car/v2/storage/storage.go +++ b/ipld/car/v2/storage/storage.go @@ -12,6 +12,8 @@ import ( carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1" + "github.com/ipld/go-car/v2/internal/carv1/util" + "github.com/ipld/go-car/v2/internal/insertionindex" internalio "github.com/ipld/go-car/v2/internal/io" "github.com/ipld/go-car/v2/internal/store" ipldstorage "github.com/ipld/go-ipld-prime/storage" @@ -20,33 +22,21 @@ import ( var errClosed = fmt.Errorf("cannot use a carv2 storage after closing") -// compatible with the go-ipld-format ErrNotFound, match against -// interface{NotFound() bool} - -type ErrNotFound struct { - Cid cid.Cid -} - -func (e ErrNotFound) Error() string { - if e.Cid == cid.Undef { - return "ipld: could not find node" - } - return "ipld: could not find " + e.Cid.String() -} - -func (e ErrNotFound) NotFound() bool { - return true -} - type ReadableCar interface { ipldstorage.ReadableStorage ipldstorage.StreamingReadableStorage Roots() ([]cid.Cid, error) } +// WritableCar is compatible with storage.WritableStorage but also returns +// the roots of the CAR. It does not implement ipld.StreamingWritableStorage +// as the CAR format does not support streaming data followed by its CID, so +// any streaming implementation would perform buffering and copy the +// existing storage.PutStream() implementation. type WritableCar interface { ipldstorage.WritableStorage - ipldstorage.StreamingWritableStorage + Roots() ([]cid.Cid, error) + Finalize() error } var _ ipldstorage.ReadableStorage = (*StorageCar)(nil) @@ -54,70 +44,160 @@ var _ ipldstorage.StreamingReadableStorage = (*StorageCar)(nil) var _ ReadableCar = (*StorageCar)(nil) type StorageCar struct { - idx index.Index - // iidx *insertionindex.InsertionIndex - reader io.ReaderAt - writer io.Writer - // header carv2.Header - opts carv2.Options + idx *insertionindex.InsertionIndex + reader io.ReaderAt + writer positionedWriter + dataWriter *internalio.OffsetWriteSeeker + header carv2.Header + opts carv2.Options closed bool mu sync.RWMutex } -func NewReadable(reader io.ReaderAt, idx index.Index, opts ...carv2.Option) (ReadableCar, error) { +type positionedWriter interface { + io.Writer + Position() int64 +} + +func NewReadable(reader io.ReaderAt, opts ...carv2.Option) (ReadableCar, error) { sc := &StorageCar{ opts: carv2.ApplyOptions(opts...), + idx: insertionindex.NewInsertionIndex(), } - version, err := store.ReadVersion(reader, opts...) + rr := internalio.ToReadSeeker(reader) + header, err := carv1.ReadHeader(rr, sc.opts.MaxAllowedHeaderSize) if err != nil { return nil, err } - switch version { + switch header.Version { case 1: - if idx == nil { - if idx, err = store.GenerateIndex(reader, opts...); err != nil { - return nil, err - } + rr.Seek(0, io.SeekStart) + if err := carv2.LoadIndex(sc.idx, rr, opts...); err != nil { + return nil, err } sc.reader = reader - sc.idx = idx - return sc, nil case 2: v2r, err := carv2.NewReader(reader, opts...) if err != nil { return nil, err } - if idx == nil { - if v2r.Header.HasIndex() { - ir, err := v2r.IndexReader() - if err != nil { - return nil, err - } - idx, err = index.ReadFrom(ir) - if err != nil { - return nil, err - } - } else { - dr, err := v2r.DataReader() - if err != nil { - return nil, err - } - if idx, err = store.GenerateIndex(dr, opts...); err != nil { - return nil, err - } - } - } - sc.reader, err = v2r.DataReader() + dr, err := v2r.DataReader() if err != nil { return nil, err } - sc.idx = idx - return sc, nil + if err := carv2.LoadIndex(sc.idx, dr, opts...); err != nil { + return nil, err + } + if sc.reader, err = v2r.DataReader(); err != nil { + return nil, err + } default: - return nil, fmt.Errorf("unsupported car version: %v", version) + return nil, fmt.Errorf("unsupported car version: %v", header.Version) + } + + return sc, nil +} + +func NewWritable(writer io.Writer, roots []cid.Cid, opts ...carv2.Option) (WritableCar, error) { + sc := &StorageCar{ + writer: &positionTrackingWriter{w: writer}, + idx: insertionindex.NewInsertionIndex(), + header: carv2.NewHeader(0), + opts: carv2.ApplyOptions(opts...), + } + + if p := sc.opts.DataPadding; p > 0 { + sc.header = sc.header.WithDataPadding(p) + } + if p := sc.opts.IndexPadding; p > 0 { + sc.header = sc.header.WithIndexPadding(p) + } + + offset := int64(sc.header.DataOffset) + if sc.opts.WriteAsCarV1 { + offset = 0 + } + + if writerAt, ok := writer.(io.WriterAt); ok { + sc.dataWriter = internalio.NewOffsetWriter(writerAt, offset) + } else { + if !sc.opts.WriteAsCarV1 { + return nil, fmt.Errorf("cannot write as carv2 to a non-seekable writer") + } + } + + if err := sc.initWithRoots(writer, !sc.opts.WriteAsCarV1, roots); err != nil { + return nil, err + } + + return sc, nil +} + +func (sc *StorageCar) initWithRoots(writer io.Writer, v2 bool, roots []cid.Cid) error { + if v2 { + if _, err := writer.Write(carv2.Pragma); err != nil { + return err + } + return carv1.WriteHeader(&carv1.CarHeader{Roots: roots, Version: 1}, sc.dataWriter) + } + return carv1.WriteHeader(&carv1.CarHeader{Roots: roots, Version: 1}, writer) +} + +func (sc *StorageCar) Put(ctx context.Context, keyStr string, data []byte) error { + keyCid, err := cid.Cast([]byte(keyStr)) + if err != nil { + return err + } + + sc.mu.Lock() + defer sc.mu.Unlock() + + // If StoreIdentityCIDs option is disabled then treat IDENTITY CIDs like IdStore. + if !sc.opts.StoreIdentityCIDs { + // Check for IDENTITY CID. If IDENTITY, ignore and move to the next block. + if _, ok, err := store.IsIdentity(keyCid); err != nil { + return err + } else if ok { + return nil + } + } + + // Check if its size is too big. + // If larger than maximum allowed size, return error. + // Note, we need to check this regardless of whether we have IDENTITY CID or not. + // Since multhihash codes other than IDENTITY can result in large digests. + cSize := uint64(len(keyCid.Bytes())) + if cSize > sc.opts.MaxIndexCidSize { + return &carv2.ErrCidTooLarge{MaxSize: sc.opts.MaxIndexCidSize, CurrentSize: cSize} + } + + // TODO: if we are write-only and BlockstoreAllowDuplicatePuts then we don't + // really need an index at all + if !sc.opts.BlockstoreAllowDuplicatePuts { + if sc.opts.BlockstoreUseWholeCIDs && sc.idx.HasExactCID(keyCid) { + return nil // deduplicated by CID + } + if !sc.opts.BlockstoreUseWholeCIDs { + _, err := sc.idx.Get(keyCid) + if err == nil { + return nil // deduplicated by hash + } + } + } + + w := sc.writer + if sc.dataWriter != nil { + w = sc.dataWriter + } + n := uint64(w.Position()) + if err := util.LdWrite(w, keyCid.Bytes(), data); err != nil { + return err } + sc.idx.InsertNoReplace(keyCid, n) + + return nil } func (sc *StorageCar) Roots() ([]cid.Cid, error) { @@ -156,38 +236,23 @@ func (sc *StorageCar) Has(ctx context.Context, keyStr string) (bool, error) { return false, errClosed } - var fnFound bool - var fnErr error - err = sc.idx.GetAll(keyCid, func(offset uint64) bool { - uar, err := internalio.NewOffsetReadSeeker(sc.reader, int64(offset)) - if err != nil { - fnErr = err - return false - } - _, err = varint.ReadUvarint(uar) + if sc.opts.BlockstoreUseWholeCIDs { + var foundCid cid.Cid + _, foundCid, err = sc.idx.GetCid(keyCid) if err != nil { - fnErr = err - return false - } - _, readCid, err := cid.CidFromReader(uar) - if err != nil { - fnErr = err - return false - } - if sc.opts.BlockstoreUseWholeCIDs { - fnFound = readCid.Equals(keyCid) - return !fnFound // continue looking if we haven't found it - } else { - fnFound = bytes.Equal(readCid.Hash(), keyCid.Hash()) - return false + if !foundCid.Equals(keyCid) { + return false, nil + } } - }) + } else { + _, err = sc.idx.Get(keyCid) + } if errors.Is(err, index.ErrNotFound) { return false, nil } else if err != nil { return false, err } - return fnFound, fnErr + return true, nil } func (sc *StorageCar) Get(ctx context.Context, keyStr string) ([]byte, error) { @@ -223,49 +288,96 @@ func (sc *StorageCar) GetStream(ctx context.Context, keyStr string) (io.ReadClos } fnSize := -1 - var fnErr error - var foundOffset int64 - err = sc.idx.GetAll(keyCid, func(offset uint64) bool { - rdr, err := internalio.NewOffsetReadSeeker(sc.reader, int64(offset)) - if err != nil { - fnErr = err - return false - } - sectionLen, err := varint.ReadUvarint(rdr) + var offset uint64 + if sc.opts.BlockstoreUseWholeCIDs { + var foundCid cid.Cid + offset, foundCid, err = sc.idx.GetCid(keyCid) if err != nil { - fnErr = err - return false - } - cidLen, readCid, err := cid.CidFromReader(rdr) - if err != nil { - fnErr = err - return false - } - if sc.opts.BlockstoreUseWholeCIDs { - if readCid.Equals(keyCid) { - fnSize = int(sectionLen) - cidLen - foundOffset = rdr.(interface{ Offset() int64 }).Offset() - return false - } else { - return true // continue looking - } - } else { - if bytes.Equal(readCid.Hash(), keyCid.Hash()) { - fnSize = int(sectionLen) - cidLen - foundOffset = rdr.(interface{ Offset() int64 }).Offset() + if !foundCid.Equals(keyCid) { + return nil, ErrNotFound{Cid: keyCid} } - return false } - }) + } else { + offset, err = sc.idx.Get(keyCid) + } if errors.Is(err, index.ErrNotFound) { return nil, ErrNotFound{Cid: keyCid} } else if err != nil { return nil, err - } else if fnErr != nil { - return nil, fnErr } + + rdr, err := internalio.NewOffsetReadSeeker(sc.reader, int64(offset)) + if err != nil { + return nil, err + } + sectionLen, err := varint.ReadUvarint(rdr) + if err != nil { + return nil, err + } + cidLen, _, err := cid.CidFromReader(rdr) + if err != nil { + return nil, err + } + fnSize = int(sectionLen) - cidLen + offset = uint64(rdr.(interface{ Offset() int64 }).Offset()) if fnSize == -1 { return nil, ErrNotFound{Cid: keyCid} } - return io.NopCloser(io.NewSectionReader(sc.reader, foundOffset, int64(fnSize))), nil + return io.NopCloser(io.NewSectionReader(sc.reader, int64(offset), int64(fnSize))), nil +} + +func (sc *StorageCar) Finalize() error { + if sc.opts.WriteAsCarV1 { + return nil + } + + wat, ok := sc.writer.(*positionTrackingWriter).w.(io.WriterAt) + if !ok { // should should already be checked at construction if this is a writable + return fmt.Errorf("cannot finalize a CARv2 without an io.WriterAt") + } + + sc.mu.Lock() + defer sc.mu.Unlock() + + if sc.closed { + // Allow duplicate Finalize calls, just like Close. + // Still error, just like ReadOnly.Close; it should be discarded. + return fmt.Errorf("called Finalize on a closed blockstore") + } + + // TODO check if add index option is set and don't write the index then set index offset to zero. + sc.header = sc.header.WithDataSize(uint64(sc.dataWriter.Position())) + sc.header.Characteristics.SetFullyIndexed(sc.opts.StoreIdentityCIDs) + + sc.closed = true + + fi, err := sc.idx.Flatten(sc.opts.IndexCodec) + if err != nil { + return err + } + if _, err := index.WriteTo(fi, internalio.NewOffsetWriter(wat, int64(sc.header.IndexOffset))); err != nil { + return err + } + var buf bytes.Buffer + sc.header.WriteTo(&buf) + if _, err := sc.header.WriteTo(internalio.NewOffsetWriter(wat, carv2.PragmaSize)); err != nil { + return err + } + + return nil +} + +type positionTrackingWriter struct { + w io.Writer + offset int64 +} + +func (ptw *positionTrackingWriter) Write(p []byte) (int, error) { + written, err := ptw.w.Write(p) + ptw.offset += int64(written) + return written, err +} + +func (ptw *positionTrackingWriter) Position() int64 { + return ptw.offset } diff --git a/ipld/car/v2/storage/storage_test.go b/ipld/car/v2/storage/storage_test.go index 10f71c86b..0b34b5025 100644 --- a/ipld/car/v2/storage/storage_test.go +++ b/ipld/car/v2/storage/storage_test.go @@ -2,63 +2,62 @@ package storage_test import ( "context" + "crypto/sha512" + "errors" + "fmt" "io" + "math/rand" "os" + "path/filepath" "testing" + "time" + "github.com/ipfs/go-cid" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/blockstore" + "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/storage" - "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-multihash" "github.com/stretchr/testify/require" ) +var rng = rand.New(rand.NewSource(1413)) + func TestReadable(t *testing.T) { tests := []struct { name string v1OrV2path string opts []carv2.Option - noIdCids bool }{ { "OpenedWithCarV1", "../testdata/sample-v1.car", []carv2.Option{blockstore.UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, - // index is made, but identity CIDs are included so they'll be found - false, }, { "OpenedWithCarV1_NoIdentityCID", "../testdata/sample-v1.car", []carv2.Option{blockstore.UseWholeCIDs(true)}, - // index is made, identity CIDs are not included, but we always short-circuit when StoreIdentityCIDs(false) - false, }, { "OpenedWithCarV2", "../testdata/sample-wrapped-v2.car", []carv2.Option{blockstore.UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, - // index already exists, but was made without identity CIDs, but opening with StoreIdentityCIDs(true) means we check the index - true, }, { "OpenedWithCarV2_NoIdentityCID", "../testdata/sample-wrapped-v2.car", []carv2.Option{blockstore.UseWholeCIDs(true)}, - // index already exists, it was made without identity CIDs, but we always short-circuit when StoreIdentityCIDs(false) - false, }, { "OpenedWithCarV1ZeroLenSection", "../testdata/sample-v1-with-zero-len-section.car", []carv2.Option{blockstore.UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, - false, }, { "OpenedWithAnotherCarV1ZeroLenSection", "../testdata/sample-v1-with-zero-len-section2.car", []carv2.Option{blockstore.UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, - false, }, } @@ -67,7 +66,7 @@ func TestReadable(t *testing.T) { ctx := context.TODO() subjectReader, err := os.Open(tt.v1OrV2path) require.NoError(t, err) - subject, err := storage.NewReadable(subjectReader, nil, tt.opts...) + subject, err := storage.NewReadable(subjectReader, tt.opts...) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, subjectReader.Close()) }) @@ -96,13 +95,7 @@ func TestReadable(t *testing.T) { // Assert blockstore contains key. has, err := subject.Has(ctx, key.KeyString()) require.NoError(t, err) - if key.Prefix().MhType == uint64(multicodec.Identity) && tt.noIdCids { - // fixture wasn't made with StoreIdentityCIDs, but we opened it with StoreIdentityCIDs, - // so they aren't there to find - require.False(t, has) - } else { - require.True(t, has) - } + require.True(t, has) // Assert block itself matches v1 payload block. if has { @@ -117,6 +110,228 @@ func TestReadable(t *testing.T) { require.Equal(t, wantBlock.RawData(), data) } } + + // not exists + c := randCid() + has, err := subject.Has(ctx, c.KeyString()) + require.NoError(t, err) + require.False(t, has) + + _, err = subject.Get(ctx, c.KeyString()) + require.True(t, errors.Is(err, storage.ErrNotFound{})) + require.True(t, storage.IsNotFound(err)) + require.Contains(t, err.Error(), c.String()) + + // random identity, should only find this if we _don't_ store identity CIDs + storeIdentity := carv2.ApplyOptions(tt.opts...).StoreIdentityCIDs + c = randIdentityCid() + + has, err = subject.Has(ctx, c.KeyString()) + require.NoError(t, err) + require.Equal(t, !storeIdentity, has) + + got, err := subject.Get(ctx, c.KeyString()) + if !storeIdentity { + require.NoError(t, err) + mh, err := multihash.Decode(c.Hash()) + require.NoError(t, err) + require.Equal(t, mh.Digest, got) + } else { + require.True(t, errors.Is(err, storage.ErrNotFound{})) + require.True(t, storage.IsNotFound(err)) + require.Contains(t, err.Error(), c.String()) + } + }) + } +} + +func TestWritable(t *testing.T) { + originalCarV1Path := "../testdata/sample-v1.car" + + variants := []struct { + name string + compareCarV1 string + options []carv2.Option + expectedV1StartOffset int64 + }{ + // no options, expect a standard CARv2 with the noidentity inner CARv1 + {"carv2_noopt", "sample-v1-noidentity.car", []carv2.Option{}, int64(carv2.PragmaSize + carv2.HeaderSize)}, + // no options, expect a standard CARv2 with the noidentity inner CARv1 + {"carv2_identity", "sample-v1.car", []carv2.Option{carv2.StoreIdentityCIDs(true)}, int64(carv2.PragmaSize + carv2.HeaderSize)}, + // option to only write as a CARv1, expect the noidentity inner CARv1 + {"carv1", "sample-v1-noidentity.car", []carv2.Option{blockstore.WriteAsCarV1(true)}, int64(0)}, + // option to only write as a CARv1, expect the noidentity inner CARv1 + {"carv1_identity", "sample-v1.car", []carv2.Option{blockstore.WriteAsCarV1(true), carv2.StoreIdentityCIDs(true)}, int64(0)}, + } + + for _, variant := range variants { + t.Run(variant.name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + opts := carv2.ApplyOptions(variant.options...) + + srcFile, err := os.Open(originalCarV1Path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, srcFile.Close()) }) + r, err := carv1.NewCarReader(srcFile) + require.NoError(t, err) + + path := filepath.Join("/tmp/", fmt.Sprintf("writable_%s.car", variant.name)) + dstFile, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644) + require.NoError(t, err) + var writer io.Writer = &writerOnly{dstFile} + if !opts.WriteAsCarV1 { + writer = &writerAtOnly{dstFile} + } + ingester, err := storage.NewWritable(writer, r.Header.Roots, variant.options...) + require.NoError(t, err) + t.Cleanup(func() { dstFile.Close() }) + + cids := make([]cid.Cid, 0) + var idCidCount int + for { + b, err := r.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + + err = ingester.Put(ctx, b.Cid().KeyString(), b.RawData()) + require.NoError(t, err) + cids = append(cids, b.Cid()) + + dmh, err := multihash.Decode(b.Cid().Hash()) + require.NoError(t, err) + if dmh.Code == multihash.IDENTITY { + idCidCount++ + } + + // try reading a random one: + candIndex := rng.Intn(len(cids)) + var candidate cid.Cid + for _, c := range cids { + if candIndex == 0 { + candidate = c + break + } + candIndex-- + } + has, err := ingester.Has(ctx, candidate.KeyString()) + require.NoError(t, err) + require.True(t, has) + + // not exists + has, err = ingester.Has(ctx, randCid().KeyString()) + require.NoError(t, err) + require.False(t, has) + + // random identity + has, err = ingester.Has(ctx, randIdentityCid().KeyString()) + require.NoError(t, err) + require.Equal(t, !opts.StoreIdentityCIDs, has) + } + + err = ingester.Finalize() + require.NoError(t, err) + + err = dstFile.Close() + require.NoError(t, err) + + reopen, err := os.Open(path) + require.NoError(t, err) + rd, err := carv2.NewReader(reopen) + require.NoError(t, err) + require.Equal(t, opts.WriteAsCarV1, rd.Version == 1) + require.NoError(t, reopen.Close()) + + robs, err := blockstore.OpenReadOnly(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, robs.Close()) }) + + allKeysCh, err := robs.AllKeysChan(ctx) + require.NoError(t, err) + numKeysCh := 0 + for c := range allKeysCh { + b, err := robs.Get(ctx, c) + require.NoError(t, err) + if !b.Cid().Equals(c) { + t.Fatal("wrong item returned") + } + numKeysCh++ + } + expectedCidCount := len(cids) + if !opts.StoreIdentityCIDs { + expectedCidCount -= idCidCount + } + require.Equal(t, expectedCidCount, numKeysCh, "AllKeysChan returned an unexpected amount of keys; expected %v but got %v", expectedCidCount, numKeysCh) + + for _, c := range cids { + b, err := robs.Get(ctx, c) + require.NoError(t, err) + if !b.Cid().Equals(c) { + t.Fatal("wrong item returned") + } + } + + comparePath := filepath.Join("../testdata/", variant.compareCarV1) + compareStat, err := os.Stat(comparePath) + require.NoError(t, err) + + wrote, err := os.Open(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, wrote.Close()) }) + _, err = wrote.Seek(variant.expectedV1StartOffset, io.SeekStart) + require.NoError(t, err) + hasher := sha512.New() + gotWritten, err := io.Copy(hasher, io.LimitReader(wrote, compareStat.Size())) + require.NoError(t, err) + gotSum := hasher.Sum(nil) + + hasher.Reset() + compareV1, err := os.Open(comparePath) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, compareV1.Close()) }) + wantWritten, err := io.Copy(hasher, compareV1) + require.NoError(t, err) + wantSum := hasher.Sum(nil) + + require.Equal(t, wantWritten, gotWritten) + require.Equal(t, wantSum, gotSum) }) } } + +type writerOnly struct { + io.Writer +} + +func (w *writerOnly) Write(p []byte) (n int, err error) { + return w.Writer.Write(p) +} + +type writerAtOnly struct { + *os.File +} + +func (w *writerAtOnly) WriteAt(p []byte, off int64) (n int, err error) { + return w.File.WriteAt(p, off) +} + +func (w *writerAtOnly) Write(p []byte) (n int, err error) { + return w.File.Write(p) +} + +func randCid() cid.Cid { + b := make([]byte, 32) + rng.Read(b) + mh, _ := multihash.Encode(b, multihash.SHA2_256) + return cid.NewCidV1(cid.DagProtobuf, mh) +} + +func randIdentityCid() cid.Cid { + b := make([]byte, 32) + rng.Read(b) + mh, _ := multihash.Encode(b, multihash.IDENTITY) + return cid.NewCidV1(cid.Raw, mh) +} From a6bbce9c1a8d7cb2e188582bb6c544212beedc3c Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 6 Feb 2023 14:21:23 +1100 Subject: [PATCH 3775/3817] feat: ReadableWritable; dedupe shared code * New and Open(=resumable) functionality for ReadableWritable * Pull up blockstore options to carv2 package * Extracted shared blockstore code into internal/store This commit was moved from ipld/go-car@f9a08295ac7b1127a52a815c0c1fd6b05885c193 --- ipld/car/v2/blockstore/readonly.go | 159 +-- ipld/car/v2/blockstore/readwrite.go | 244 +--- .../internal/insertionindex/insertionindex.go | 8 - ipld/car/v2/internal/store/identity.go | 1 + ipld/car/v2/internal/store/index.go | 109 +- ipld/car/v2/internal/store/put.go | 54 + ipld/car/v2/internal/store/resume.go | 200 +++ ipld/car/v2/internal/store/version.go | 17 - ipld/car/v2/options.go | 50 + ipld/car/v2/storage/storage.go | 297 +++-- ipld/car/v2/storage/storage_test.go | 1157 +++++++++++++++-- 11 files changed, 1648 insertions(+), 648 deletions(-) create mode 100644 ipld/car/v2/internal/store/put.go create mode 100644 ipld/car/v2/internal/store/resume.go delete mode 100644 ipld/car/v2/internal/store/version.go diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 217771cbc..c59bdcf68 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -1,7 +1,6 @@ package blockstore import ( - "bytes" "context" "errors" "fmt" @@ -15,7 +14,6 @@ import ( carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1" - "github.com/ipld/go-car/v2/internal/carv1/util" internalio "github.com/ipld/go-car/v2/internal/io" "github.com/ipld/go-car/v2/internal/store" "github.com/multiformats/go-varint" @@ -61,29 +59,7 @@ type contextKey string const asyncErrHandlerKey contextKey = "asyncErrorHandlerKey" -// UseWholeCIDs is a read option which makes a CAR blockstore identify blocks by -// whole CIDs, and not just their multihashes. The default is to use -// multihashes, which matches the current semantics of go-ipfs-blockstore v1. -// -// Enabling this option affects a number of methods, including read-only ones: -// -// • Get, Has, and HasSize will only return a block -// only if the entire CID is present in the CAR file. -// -// • AllKeysChan will return the original whole CIDs, instead of with their -// multicodec set to "raw" to just provide multihashes. -// -// • If AllowDuplicatePuts isn't set, -// Put and PutMany will deduplicate by the whole CID, -// allowing different CIDs with equal multihashes. -// -// Note that this option only affects the blockstore, and is ignored by the root -// go-car/v2 package. -func UseWholeCIDs(enable bool) carv2.Option { - return func(o *carv2.Options) { - o.BlockstoreUseWholeCIDs = enable - } -} +var UseWholeCIDs = carv2.UseWholeCIDs // NewReadOnly creates a new ReadOnly blockstore from the backing with a optional index as idx. // This function accepts both CARv1 and CARv2 backing. @@ -203,14 +179,6 @@ func OpenReadOnly(path string, opts ...carv2.Option) (*ReadOnly, error) { return robs, nil } -func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) { - r, err := internalio.NewOffsetReadSeeker(b.backing, idx) - if err != nil { - return cid.Cid{}, nil, err - } - return util.ReadNode(r, b.opts.ZeroLengthSectionAsEOF, b.opts.MaxAllowedSectionSize) -} - // DeleteBlock is unsupported and always errors. func (b *ReadOnly) DeleteBlock(_ context.Context, _ cid.Cid) error { return errReadOnly @@ -243,38 +211,21 @@ func (b *ReadOnly) Has(ctx context.Context, key cid.Cid) (bool, error) { return false, errClosed } - var fnFound bool - var fnErr error - err := b.idx.GetAll(key, func(offset uint64) bool { - uar, err := internalio.NewOffsetReadSeeker(b.backing, int64(offset)) - if err != nil { - fnErr = err - return false - } - _, err = varint.ReadUvarint(uar) - if err != nil { - fnErr = err - return false - } - _, readCid, err := cid.CidFromReader(uar) - if err != nil { - fnErr = err - return false - } - if b.opts.BlockstoreUseWholeCIDs { - fnFound = readCid.Equals(key) - return !fnFound // continue looking if we haven't found it - } else { - fnFound = bytes.Equal(readCid.Hash(), key.Hash()) - return false - } - }) + _, _, size, err := store.FindCid( + b.backing, + b.idx, + key, + b.opts.BlockstoreUseWholeCIDs, + b.opts.ZeroLengthSectionAsEOF, + b.opts.MaxAllowedSectionSize, + false, + ) if errors.Is(err, index.ErrNotFound) { return false, nil } else if err != nil { return false, err } - return fnFound, fnErr + return size > -1, nil } // Get gets a block corresponding to the given key. @@ -304,39 +255,21 @@ func (b *ReadOnly) Get(ctx context.Context, key cid.Cid) (blocks.Block, error) { return nil, errClosed } - var fnData []byte - var fnErr error - err := b.idx.GetAll(key, func(offset uint64) bool { - readCid, data, err := b.readBlock(int64(offset)) - if err != nil { - fnErr = err - return false - } - if b.opts.BlockstoreUseWholeCIDs { - if readCid.Equals(key) { - fnData = data - return false - } else { - return true // continue looking - } - } else { - if bytes.Equal(readCid.Hash(), key.Hash()) { - fnData = data - } - return false - } - }) + data, _, _, err := store.FindCid( + b.backing, + b.idx, + key, + b.opts.BlockstoreUseWholeCIDs, + b.opts.ZeroLengthSectionAsEOF, + b.opts.MaxAllowedSectionSize, + true, + ) if errors.Is(err, index.ErrNotFound) { return nil, format.ErrNotFound{Cid: key} } else if err != nil { - return nil, format.ErrNotFound{Cid: key} - } else if fnErr != nil { - return nil, fnErr - } - if fnData == nil { - return nil, format.ErrNotFound{Cid: key} + return nil, err } - return blocks.NewBlockWithCid(fnData, key) + return blocks.NewBlockWithCid(data, key) } // GetSize gets the size of an item corresponding to the given key. @@ -356,49 +289,21 @@ func (b *ReadOnly) GetSize(ctx context.Context, key cid.Cid) (int, error) { return 0, errClosed } - fnSize := -1 - var fnErr error - err := b.idx.GetAll(key, func(offset uint64) bool { - rdr, err := internalio.NewOffsetReadSeeker(b.backing, int64(offset)) - if err != nil { - fnErr = err - return false - } - sectionLen, err := varint.ReadUvarint(rdr) - if err != nil { - fnErr = err - return false - } - cidLen, readCid, err := cid.CidFromReader(rdr) - if err != nil { - fnErr = err - return false - } - if b.opts.BlockstoreUseWholeCIDs { - if readCid.Equals(key) { - fnSize = int(sectionLen) - cidLen - return false - } else { - return true // continue looking - } - } else { - if bytes.Equal(readCid.Hash(), key.Hash()) { - fnSize = int(sectionLen) - cidLen - } - return false - } - }) + _, _, size, err := store.FindCid( + b.backing, + b.idx, + key, + b.opts.BlockstoreUseWholeCIDs, + b.opts.ZeroLengthSectionAsEOF, + b.opts.MaxAllowedSectionSize, + false, + ) if errors.Is(err, index.ErrNotFound) { return -1, format.ErrNotFound{Cid: key} } else if err != nil { return -1, err - } else if fnErr != nil { - return -1, fnErr - } - if fnSize == -1 { - return -1, format.ErrNotFound{Cid: key} } - return fnSize, nil + return size, nil } // Put is not supported and always returns an error. diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 43b42d019..032565b15 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -2,18 +2,14 @@ package blockstore import ( "context" - "errors" "fmt" - "io" "os" "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" blocks "github.com/ipfs/go-libipfs/blocks" - "github.com/multiformats/go-varint" carv2 "github.com/ipld/go-car/v2" - "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/carv1/util" "github.com/ipld/go-car/v2/internal/insertionindex" @@ -43,29 +39,8 @@ type ReadWrite struct { opts carv2.Options } -// WriteAsCarV1 is a write option which makes a CAR blockstore write the output -// as a CARv1 only, with no CARv2 header or index. Indexing is used internally -// during write but is discarded upon finalization. -// -// Note that this option only affects the blockstore, and is ignored by the root -// go-car/v2 package. -func WriteAsCarV1(asCarV1 bool) carv2.Option { - return func(o *carv2.Options) { - o.WriteAsCarV1 = asCarV1 - } -} - -// AllowDuplicatePuts is a write option which makes a CAR blockstore not -// deduplicate blocks in Put and PutMany. The default is to deduplicate, -// which matches the current semantics of go-ipfs-blockstore v1. -// -// Note that this option only affects the blockstore, and is ignored by the root -// go-car/v2 package. -func AllowDuplicatePuts(allow bool) carv2.Option { - return func(o *carv2.Options) { - o.BlockstoreAllowDuplicatePuts = allow - } -} +var WriteAsCarV1 = carv2.WriteAsCarV1 +var AllowDuplicatePuts = carv2.AllowDuplicatePuts // OpenReadWrite creates a new ReadWrite at the given path with a provided set of root CIDs and options. // @@ -162,7 +137,20 @@ func OpenReadWriteFile(f *os.File, roots []cid.Cid, opts ...carv2.Option) (*Read rwbs.ronly.idx = rwbs.idx if resume { - if err = rwbs.resumeWithRoots(!rwbs.opts.WriteAsCarV1, roots); err != nil { + if err := store.ResumableVersion(f, rwbs.opts.WriteAsCarV1); err != nil { + return nil, err + } + if err := store.Resume( + f, + rwbs.ronly.backing, + rwbs.dataWriter, + rwbs.idx, + roots, + rwbs.header.DataOffset, + rwbs.opts.WriteAsCarV1, + rwbs.opts.MaxAllowedHeaderSize, + rwbs.opts.ZeroLengthSectionAsEOF, + ); err != nil { return nil, err } } else { @@ -183,152 +171,6 @@ func (b *ReadWrite) initWithRoots(v2 bool, roots []cid.Cid) error { return carv1.WriteHeader(&carv1.CarHeader{Roots: roots, Version: 1}, b.dataWriter) } -func (b *ReadWrite) resumeWithRoots(v2 bool, roots []cid.Cid) error { - // On resumption it is expected that the CARv2 Pragma, and the CARv1 header is successfully written. - // Otherwise we cannot resume from the file. - // Read pragma to assert if b.f is indeed a CARv2. - version, err := carv2.ReadVersion(b.f) - if err != nil { - // The file is not a valid CAR file and cannot resume from it. - // Or the write must have failed before pragma was written. - return err - } - switch { - case version == 1 && !v2: - case version == 2 && v2: - default: - // The file is not the expected version and we cannot resume from it. - return fmt.Errorf("cannot resume on CAR file with version %v", version) - } - - var headerInFile carv2.Header - - if v2 { - // Check if file was finalized by trying to read the CARv2 header. - // We check because if finalized the CARv1 reader behaviour needs to be adjusted since - // EOF will not signify end of CARv1 payload. i.e. index is most likely present. - r, err := internalio.NewOffsetReadSeeker(b.f, carv2.PragmaSize) - if err != nil { - return err - } - _, err = headerInFile.ReadFrom(r) - - // If reading CARv2 header succeeded, and CARv1 offset in header is not zero then the file is - // most-likely finalized. Check padding and truncate the file to remove index. - // Otherwise, carry on reading the v1 payload at offset determined from b.header. - if err == nil && headerInFile.DataOffset != 0 { - if headerInFile.DataOffset != b.header.DataOffset { - // Assert that the padding on file matches the given WithDataPadding option. - wantPadding := headerInFile.DataOffset - carv2.PragmaSize - carv2.HeaderSize - gotPadding := b.header.DataOffset - carv2.PragmaSize - carv2.HeaderSize - return fmt.Errorf( - "cannot resume from file with mismatched CARv1 offset; "+ - "`WithDataPadding` option must match the padding on file. "+ - "Expected padding value of %v but got %v", wantPadding, gotPadding, - ) - } else if headerInFile.DataSize == 0 { - // If CARv1 size is zero, since CARv1 offset wasn't, then the CARv2 header was - // most-likely partially written. Since we write the header last in Finalize then the - // file most-likely contains the index and we cannot know where it starts, therefore - // can't resume. - return errors.New("corrupt CARv2 header; cannot resume from file") - } - } - } - - // Use the given CARv1 padding to instantiate the CARv1 reader on file. - v1r, err := internalio.NewOffsetReadSeeker(b.ronly.backing, 0) - if err != nil { - return err - } - header, err := carv1.ReadHeader(v1r, b.opts.MaxAllowedHeaderSize) - if err != nil { - // Cannot read the CARv1 header; the file is most likely corrupt. - return fmt.Errorf("error reading car header: %w", err) - } - if !header.Matches(carv1.CarHeader{Roots: roots, Version: 1}) { - // Cannot resume if version and root does not match. - return errors.New("cannot resume on file with mismatching data header") - } - - if headerInFile.DataOffset != 0 { - // If header in file contains the size of car v1, then the index is most likely present. - // Since we will need to re-generate the index, as the one in file is flattened, truncate - // the file so that the Readonly.backing has the right set of bytes to deal with. - // This effectively means resuming from a finalized file will wipe its index even if there - // are no blocks put unless the user calls finalize. - if err := b.f.Truncate(int64(headerInFile.DataOffset + headerInFile.DataSize)); err != nil { - return err - } - } - - if v2 { - // Now that CARv2 header is present on file, clear it to avoid incorrect size and offset in - // header in case blocksotre is closed without finalization and is resumed from. - if err := b.unfinalize(); err != nil { - return fmt.Errorf("could not un-finalize: %w", err) - } - } - - // TODO See how we can reduce duplicate code here. - // The code here comes from car.GenerateIndex. - // Copied because we need to populate an insertindex, not a sorted index. - // Producing a sorted index via generate, then converting it to insertindex is not possible. - // Because Index interface does not expose internal records. - // This may be done as part of https://github.com/ipld/go-car/issues/95 - - offset, err := carv1.HeaderSize(header) - if err != nil { - return err - } - sectionOffset := int64(0) - if sectionOffset, err = v1r.Seek(int64(offset), io.SeekStart); err != nil { - return err - } - - for { - // Grab the length of the section. - // Note that ReadUvarint wants a ByteReader. - length, err := varint.ReadUvarint(v1r) - if err != nil { - if err == io.EOF { - break - } - return err - } - - // Null padding; by default it's an error. - if length == 0 { - if b.ronly.opts.ZeroLengthSectionAsEOF { - break - } else { - return fmt.Errorf("carv1 null padding not allowed by default; see WithZeroLegthSectionAsEOF") - } - } - - // Grab the CID. - n, c, err := cid.CidFromReader(v1r) - if err != nil { - return err - } - b.idx.InsertNoReplace(c, uint64(sectionOffset)) - - // Seek to the next section by skipping the block. - // The section length includes the CID, so subtract it. - if sectionOffset, err = v1r.Seek(int64(length)-int64(n), io.SeekCurrent); err != nil { - return err - } - } - // Seek to the end of last skipped block where the writer should resume writing. - _, err = b.dataWriter.Seek(sectionOffset, io.SeekStart) - return err -} - -func (b *ReadWrite) unfinalize() error { - _, err := new(carv2.Header).WriteTo(internalio.NewOffsetWriter(b.f, carv2.PragmaSize)) - return err -} - // Put puts a given block to the underlying datastore func (b *ReadWrite) Put(ctx context.Context, blk blocks.Block) error { // PutMany already checks b.ronly.closed. @@ -348,35 +190,17 @@ func (b *ReadWrite) PutMany(ctx context.Context, blks []blocks.Block) error { for _, bl := range blks { c := bl.Cid() - // If StoreIdentityCIDs option is disabled then treat IDENTITY CIDs like IdStore. - if !b.opts.StoreIdentityCIDs { - // Check for IDENTITY CID. If IDENTITY, ignore and move to the next block. - if _, ok, err := store.IsIdentity(c); err != nil { - return err - } else if ok { - continue - } - } - - // Check if its size is too big. - // If larger than maximum allowed size, return error. - // Note, we need to check this regardless of whether we have IDENTITY CID or not. - // Since multhihash codes other than IDENTITY can result in large digests. - cSize := uint64(len(c.Bytes())) - if cSize > b.opts.MaxIndexCidSize { - return &carv2.ErrCidTooLarge{MaxSize: b.opts.MaxIndexCidSize, CurrentSize: cSize} - } - - if !b.opts.BlockstoreAllowDuplicatePuts { - if b.ronly.opts.BlockstoreUseWholeCIDs && b.idx.HasExactCID(c) { - continue // deduplicated by CID - } - if !b.ronly.opts.BlockstoreUseWholeCIDs { - _, err := b.idx.Get(c) - if err == nil { - continue // deduplicated by hash - } - } + if should, err := store.ShouldPut( + b.idx, + c, + b.opts.MaxIndexCidSize, + b.opts.StoreIdentityCIDs, + b.opts.BlockstoreAllowDuplicatePuts, + b.opts.BlockstoreUseWholeCIDs, + ); err != nil { + return err + } else if !should { + continue } n := uint64(b.dataWriter.Position()) @@ -421,23 +245,11 @@ func (b *ReadWrite) Finalize() error { return fmt.Errorf("called Finalize on a closed blockstore") } - // TODO check if add index option is set and don't write the index then set index offset to zero. - b.header = b.header.WithDataSize(uint64(b.dataWriter.Position())) - b.header.Characteristics.SetFullyIndexed(b.opts.StoreIdentityCIDs) - // Note that we can't use b.Close here, as that tries to grab the same // mutex we're holding here. defer b.ronly.closeWithoutMutex() - // TODO if index not needed don't bother flattening it. - fi, err := b.idx.Flatten(b.opts.IndexCodec) - if err != nil { - return err - } - if _, err := index.WriteTo(fi, internalio.NewOffsetWriter(b.f, int64(b.header.IndexOffset))); err != nil { - return err - } - if _, err := b.header.WriteTo(internalio.NewOffsetWriter(b.f, carv2.PragmaSize)); err != nil { + if err := store.Finalize(b.f, b.header, b.idx, uint64(b.dataWriter.Position()), b.opts.StoreIdentityCIDs, b.opts.IndexCodec); err != nil { return err } diff --git a/ipld/car/v2/internal/insertionindex/insertionindex.go b/ipld/car/v2/internal/insertionindex/insertionindex.go index 67dcb8979..7aac338ec 100644 --- a/ipld/car/v2/internal/insertionindex/insertionindex.go +++ b/ipld/car/v2/internal/insertionindex/insertionindex.go @@ -93,14 +93,6 @@ func (ii *InsertionIndex) getRecord(c cid.Cid) (index.Record, error) { return r.Record, nil } -func (ii *InsertionIndex) GetCid(c cid.Cid) (uint64, cid.Cid, error) { - record, err := ii.getRecord(c) - if err != nil { - return 0, cid.Undef, err - } - return record.Offset, record.Cid, nil -} - func (ii *InsertionIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { d, err := multihash.Decode(c.Hash()) if err != nil { diff --git a/ipld/car/v2/internal/store/identity.go b/ipld/car/v2/internal/store/identity.go index 85dcadc5d..d61a57c48 100644 --- a/ipld/car/v2/internal/store/identity.go +++ b/ipld/car/v2/internal/store/identity.go @@ -5,6 +5,7 @@ import ( "github.com/multiformats/go-multihash" ) +// IsIdentity inspects the CID and determines whether it is an IDENTITY CID. func IsIdentity(key cid.Cid) (digest []byte, ok bool, err error) { dmh, err := multihash.Decode(key.Hash()) if err != nil { diff --git a/ipld/car/v2/internal/store/index.go b/ipld/car/v2/internal/store/index.go index 55b12755a..c7d188600 100644 --- a/ipld/car/v2/internal/store/index.go +++ b/ipld/car/v2/internal/store/index.go @@ -1,30 +1,109 @@ package store import ( + "bytes" "io" + "github.com/ipfs/go-cid" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-car/v2/internal/carv1/util" + "github.com/ipld/go-car/v2/internal/insertionindex" internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-varint" ) -func GenerateIndex(at io.ReaderAt, opts ...carv2.Option) (index.Index, error) { - var rs io.ReadSeeker - switch r := at.(type) { - case io.ReadSeeker: - rs = r - // The version may have been read from the given io.ReaderAt; therefore move back to the begining. - if _, err := rs.Seek(0, io.SeekStart); err != nil { - return nil, err - } - default: - var err error - rs, err = internalio.NewOffsetReadSeeker(r, 0) +// FindCid can be used to either up the existence, size and offset of a block +// if it exists in CAR as specified by the index; and optionally the data bytes +// of the block. +func FindCid( + reader io.ReaderAt, + idx index.Index, + key cid.Cid, + useWholeCids bool, + zeroLenAsEOF bool, + maxReadBytes uint64, + readBytes bool, +) ([]byte, int64, int, error) { + + var fnData []byte + var fnOffset int64 + var fnLen int = -1 + var fnErr error + err := idx.GetAll(key, func(offset uint64) bool { + reader, err := internalio.NewOffsetReadSeeker(reader, int64(offset)) if err != nil { - return nil, err + fnErr = err + return false + } + var readCid cid.Cid + if readBytes { + readCid, fnData, err = util.ReadNode(reader, zeroLenAsEOF, maxReadBytes) + if err != nil { + fnErr = err + return false + } + fnLen = len(fnData) + } else { + sectionLen, err := varint.ReadUvarint(reader) + if err != nil { + fnErr = err + return false + } + var cidLen int + cidLen, readCid, err = cid.CidFromReader(reader) + if err != nil { + fnErr = err + return false + } + fnLen = int(sectionLen) - cidLen + fnOffset = int64(offset) + reader.(interface{ Position() int64 }).Position() } + if useWholeCids { + if !readCid.Equals(key) { + fnLen = -1 + return true // continue looking + } + return false + } else { + if !bytes.Equal(readCid.Hash(), key.Hash()) { + // weird, bad index, continue looking + fnLen = -1 + return true + } + return false + } + }) + if err != nil { + return nil, -1, -1, err + } + if fnErr != nil { + return nil, -1, -1, fnErr + } + if fnLen == -1 { + return nil, -1, -1, index.ErrNotFound } + return fnData, fnOffset, fnLen, nil +} + +// Finalize will write the index to the writer at the offset specified in the header. It should only +// be used for a CARv2 and when the CAR interface is being closed. +func Finalize(writer io.WriterAt, header carv2.Header, idx *insertionindex.InsertionIndex, dataSize uint64, storeIdentityCIDs bool, indexCodec multicodec.Code) error { + // TODO check if add index option is set and don't write the index then set index offset to zero. + header = header.WithDataSize(dataSize) + header.Characteristics.SetFullyIndexed(storeIdentityCIDs) - // Note, we do not set any write options so that all write options fall back onto defaults. - return carv2.GenerateIndex(rs, opts...) + // TODO if index not needed don't bother flattening it. + fi, err := idx.Flatten(indexCodec) + if err != nil { + return err + } + if _, err := index.WriteTo(fi, internalio.NewOffsetWriter(writer, int64(header.IndexOffset))); err != nil { + return err + } + if _, err := header.WriteTo(internalio.NewOffsetWriter(writer, carv2.PragmaSize)); err != nil { + return err + } + return nil } diff --git a/ipld/car/v2/internal/store/put.go b/ipld/car/v2/internal/store/put.go new file mode 100644 index 000000000..c9c1867ff --- /dev/null +++ b/ipld/car/v2/internal/store/put.go @@ -0,0 +1,54 @@ +package store + +import ( + "github.com/ipfs/go-cid" + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/internal/insertionindex" +) + +// ShouldPut returns true if the block should be put into the CAR according to the options provided +// and the index. It returns false if the block should not be put into the CAR, either because it +// is an identity block and StoreIdentityCIDs is false, or because it already exists and +// BlockstoreAllowDuplicatePuts is false. +func ShouldPut( + idx *insertionindex.InsertionIndex, + c cid.Cid, + maxIndexCidSize uint64, + storeIdentityCIDs bool, + blockstoreAllowDuplicatePuts bool, + blockstoreUseWholeCIDs bool, +) (bool, error) { + + // If StoreIdentityCIDs option is disabled then treat IDENTITY CIDs like IdStore. + if !storeIdentityCIDs { + // Check for IDENTITY CID. If IDENTITY, ignore and move to the next block. + if _, ok, err := IsIdentity(c); err != nil { + return false, err + } else if ok { + return false, nil + } + } + + // Check if its size is too big. + // If larger than maximum allowed size, return error. + // Note, we need to check this regardless of whether we have IDENTITY CID or not. + // Since multhihash codes other than IDENTITY can result in large digests. + cSize := uint64(len(c.Bytes())) + if cSize > maxIndexCidSize { + return false, &carv2.ErrCidTooLarge{MaxSize: maxIndexCidSize, CurrentSize: cSize} + } + + if !blockstoreAllowDuplicatePuts { + if blockstoreUseWholeCIDs && idx.HasExactCID(c) { + return false, nil // deduplicated by CID + } + if !blockstoreUseWholeCIDs { + _, err := idx.Get(c) + if err == nil { + return false, nil // deduplicated by hash + } + } + } + + return true, nil +} diff --git a/ipld/car/v2/internal/store/resume.go b/ipld/car/v2/internal/store/resume.go new file mode 100644 index 000000000..0167b4154 --- /dev/null +++ b/ipld/car/v2/internal/store/resume.go @@ -0,0 +1,200 @@ +package store + +import ( + "errors" + "fmt" + "io" + + "github.com/ipfs/go-cid" + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/internal/carv1" + "github.com/ipld/go-car/v2/internal/insertionindex" + internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/multiformats/go-varint" +) + +type ReaderWriterAt interface { + io.ReaderAt + io.Writer + io.WriterAt +} + +// ResumableVersion performs two tasks - check if there is a valid header at the start of the, +// reader, then check whether the version of that header matches what we expect. +func ResumableVersion(reader io.Reader, writeAsV1 bool) error { + version, err := carv2.ReadVersion(reader) + if err != nil { + // The file is not a valid CAR file and cannot resume from it. + // Or the write must have failed before pragma was written. + return err + } + + switch { + case version == 1 && writeAsV1: + case version == 2 && !writeAsV1: + default: + // The file is not the expected version and we cannot resume from it. + return fmt.Errorf("cannot resume on CAR file with version %v", version) + } + return nil +} + +// Resume will attempt to resume a CARv2 or CARv1 file by checking that there exists an existing +// CAR and that the CAR header details match what is being requested for resumption. +// Resumption of a CARv2 involves "unfinalizing" the header by resetting it back to a bare state +// and then truncating the file to remove the index. Truncation is important because it allows a +// non-finalized CARv2 to be resumed from as the header won't contain the DataSize of the payload +// body and if the file also contains an index, we cannot determine the end of the payload. +// Therefore, when using a resumed, existing and finalized, CARv2, whose body may not extend +// beyond the index and then closing without finalization (e.g. due to a crash), the file will no +// longer be parseable because we won't have DataSize, and we won't be able to determine it by +// parsing the payload to EOF. +func Resume( + rw ReaderWriterAt, + dataReader io.ReaderAt, + dataWriter *internalio.OffsetWriteSeeker, + idx *insertionindex.InsertionIndex, + roots []cid.Cid, + dataOffset uint64, + v1 bool, + maxAllowedHeaderSize uint64, + zeroLengthSectionAsEOF bool, +) error { + + var headerInFile carv2.Header + var v1r internalio.ReadSeekerAt + + if !v1 { + if _, ok := rw.(interface{ Truncate(size int64) error }); !ok { + return fmt.Errorf("cannot resume a CARv2 without the ability to truncate (e.g. an io.File)") + } + + // Check if file was finalized by trying to read the CARv2 header. + // We check because if finalized the CARv1 reader behaviour needs to be adjusted since + // EOF will not signify end of CARv1 payload. i.e. index is most likely present. + r, err := internalio.NewOffsetReadSeeker(rw, carv2.PragmaSize) + if err != nil { + return err + } + _, err = headerInFile.ReadFrom(r) + + // If reading CARv2 header succeeded, and CARv1 offset in header is not zero then the file is + // most-likely finalized. Check padding and truncate the file to remove index. + // Otherwise, carry on reading the v1 payload at offset determined from b.header. + if err == nil && headerInFile.DataOffset != 0 { + if headerInFile.DataOffset != dataOffset { + // Assert that the padding on file matches the given WithDataPadding option. + wantPadding := headerInFile.DataOffset - carv2.PragmaSize - carv2.HeaderSize + gotPadding := dataOffset - carv2.PragmaSize - carv2.HeaderSize + return fmt.Errorf( + "cannot resume from file with mismatched CARv1 offset; "+ + "`WithDataPadding` option must match the padding on file. "+ + "Expected padding value of %v but got %v", wantPadding, gotPadding, + ) + } else if headerInFile.DataSize == 0 { + // If CARv1 size is zero, since CARv1 offset wasn't, then the CARv2 header was + // most-likely partially written. Since we write the header last in Finalize then the + // file most-likely contains the index and we cannot know where it starts, therefore + // can't resume. + return errors.New("corrupt CARv2 header; cannot resume from file") + } + } + + v1r, err = internalio.NewOffsetReadSeeker(dataReader, 0) + if err != nil { + return err + } + } else { + var err error + v1r, err = internalio.NewOffsetReadSeeker(rw, 0) + if err != nil { + return err + } + } + + header, err := carv1.ReadHeader(v1r, maxAllowedHeaderSize) + if err != nil { + // Cannot read the CARv1 header; the file is most likely corrupt. + return fmt.Errorf("error reading car header: %w", err) + } + if !header.Matches(carv1.CarHeader{Roots: roots, Version: 1}) { + // Cannot resume if version and root does not match. + return errors.New("cannot resume on file with mismatching data header") + } + + if headerInFile.DataOffset != 0 { + // If header in file contains the size of car v1, then the index is most likely present. + // Since we will need to re-generate the index, as the one in file is flattened, truncate + // the file so that the Readonly.backing has the right set of bytes to deal with. + // This effectively means resuming from a finalized file will wipe its index even if there + // are no blocks put unless the user calls finalize. + if err := rw.(interface{ Truncate(size int64) error }).Truncate(int64(headerInFile.DataOffset + headerInFile.DataSize)); err != nil { + return err + } + } + + if !v1 { + // Now that CARv2 header is present on file, clear it to avoid incorrect size and offset in + // header in case blocksotre is closed without finalization and is resumed from. + wat, ok := rw.(io.WriterAt) + if !ok { // how would we get this far?? + return errors.New("cannot resume from file without io.WriterAt") + } + if _, err := new(carv2.Header).WriteTo(internalio.NewOffsetWriter(wat, carv2.PragmaSize)); err != nil { + return fmt.Errorf("could not un-finalize: %w", err) + } + } + + // TODO See how we can reduce duplicate code here. + // The code here comes from car.GenerateIndex. + // Copied because we need to populate an insertindex, not a sorted index. + // Producing a sorted index via generate, then converting it to insertindex is not possible. + // Because Index interface does not expose internal records. + // This may be done as part of https://github.com/ipld/go-car/issues/95 + + offset, err := carv1.HeaderSize(header) + if err != nil { + return err + } + sectionOffset := int64(0) + if sectionOffset, err = v1r.Seek(int64(offset), io.SeekStart); err != nil { + return err + } + + for { + // Grab the length of the section. + // Note that ReadUvarint wants a ByteReader. + length, err := varint.ReadUvarint(v1r) + if err != nil { + if err == io.EOF { + break + } + return err + } + + // Null padding; by default it's an error. + if length == 0 { + if zeroLengthSectionAsEOF { + break + } else { + return fmt.Errorf("carv1 null padding not allowed by default; see WithZeroLegthSectionAsEOF") + } + } + + // Grab the CID. + n, c, err := cid.CidFromReader(v1r) + if err != nil { + return err + } + idx.InsertNoReplace(c, uint64(sectionOffset)) + + // Seek to the next section by skipping the block. + // The section length includes the CID, so subtract it. + if sectionOffset, err = v1r.Seek(int64(length)-int64(n), io.SeekCurrent); err != nil { + return err + } + } + // Seek to the end of last skipped block where the writer should resume writing. + _, err = dataWriter.Seek(sectionOffset, io.SeekStart) + return err +} diff --git a/ipld/car/v2/internal/store/version.go b/ipld/car/v2/internal/store/version.go deleted file mode 100644 index b43496ef7..000000000 --- a/ipld/car/v2/internal/store/version.go +++ /dev/null @@ -1,17 +0,0 @@ -package store - -import ( - "io" - - carv2 "github.com/ipld/go-car/v2" - "github.com/ipld/go-car/v2/internal/carv1" -) - -func ReadVersion(at io.ReaderAt, opts ...carv2.Option) (uint64, error) { - o := carv2.ApplyOptions(opts...) - header, err := carv1.ReadHeaderAt(at, o.MaxAllowedHeaderSize) - if err != nil { - return 0, err - } - return header.Version, nil -} diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go index 8b5fe9b4e..92a5d11d9 100644 --- a/ipld/car/v2/options.go +++ b/ipld/car/v2/options.go @@ -179,3 +179,53 @@ func MaxAllowedSectionSize(max uint64) Option { o.MaxAllowedSectionSize = max } } + +// --------------------------------------------------- storage interface options + +// UseWholeCIDs is a read option which makes a CAR storage interface (blockstore +// or storage) identify blocks by whole CIDs, and not just their multihashes. +// The default is to use multihashes, which matches the current semantics of +// go-ipfs-blockstore v1. +// +// Enabling this option affects a number of methods, including read-only ones: +// +// - Get, Has, and HasSize will only return a block only if the entire CID is +// present in the CAR file. +// +// • AllKeysChan will return the original whole CIDs, instead of with their +// multicodec set to "raw" to just provide multihashes. +// +// • If AllowDuplicatePuts isn't set, Put and PutMany will deduplicate by the +// whole CID, allowing different CIDs with equal multihashes. +// +// Note that this option only affects the storage interfaces (blockstore +// or storage), and is ignored by the root go-car/v2 package. +func UseWholeCIDs(enable bool) Option { + return func(o *Options) { + o.BlockstoreUseWholeCIDs = enable + } +} + +// WriteAsCarV1 is a write option which makes a CAR interface (blockstore or +// storage) write the output as a CARv1 only, with no CARv2 header or index. +// Indexing is used internally during write but is discarded upon finalization. +// +// Note that this option only affects the storage interfaces (blockstore +// or storage), and is ignored by the root go-car/v2 package. +func WriteAsCarV1(asCarV1 bool) Option { + return func(o *Options) { + o.WriteAsCarV1 = asCarV1 + } +} + +// AllowDuplicatePuts is a write option which makes a CAR interface (blockstore +// or storage) not deduplicate blocks in Put and PutMany. The default is to +// deduplicate, which matches the current semantics of go-ipfs-blockstore v1. +// +// Note that this option only affects the storage interfaces (blockstore +// or storage), and is ignored by the root go-car/v2 package. +func AllowDuplicatePuts(allow bool) Option { + return func(o *Options) { + o.BlockstoreAllowDuplicatePuts = allow + } +} diff --git a/ipld/car/v2/storage/storage.go b/ipld/car/v2/storage/storage.go index 82ad0806e..6bbed612c 100644 --- a/ipld/car/v2/storage/storage.go +++ b/ipld/car/v2/storage/storage.go @@ -17,15 +17,20 @@ import ( internalio "github.com/ipld/go-car/v2/internal/io" "github.com/ipld/go-car/v2/internal/store" ipldstorage "github.com/ipld/go-ipld-prime/storage" - "github.com/multiformats/go-varint" ) -var errClosed = fmt.Errorf("cannot use a carv2 storage after closing") +var errClosed = errors.New("cannot use a CARv2 storage after closing") + +type ReaderWriterAt interface { + io.ReaderAt + io.Writer + io.WriterAt +} type ReadableCar interface { ipldstorage.ReadableStorage ipldstorage.StreamingReadableStorage - Roots() ([]cid.Cid, error) + Roots() []cid.Cid } // WritableCar is compatible with storage.WritableStorage but also returns @@ -35,20 +40,22 @@ type ReadableCar interface { // existing storage.PutStream() implementation. type WritableCar interface { ipldstorage.WritableStorage - Roots() ([]cid.Cid, error) + Roots() []cid.Cid Finalize() error } var _ ipldstorage.ReadableStorage = (*StorageCar)(nil) var _ ipldstorage.StreamingReadableStorage = (*StorageCar)(nil) var _ ReadableCar = (*StorageCar)(nil) +var _ ipldstorage.WritableStorage = (*StorageCar)(nil) type StorageCar struct { - idx *insertionindex.InsertionIndex + idx index.Index reader io.ReaderAt writer positionedWriter dataWriter *internalio.OffsetWriteSeeker header carv2.Header + roots []cid.Cid opts carv2.Options closed bool @@ -61,10 +68,7 @@ type positionedWriter interface { } func NewReadable(reader io.ReaderAt, opts ...carv2.Option) (ReadableCar, error) { - sc := &StorageCar{ - opts: carv2.ApplyOptions(opts...), - idx: insertionindex.NewInsertionIndex(), - } + sc := &StorageCar{opts: carv2.ApplyOptions(opts...)} rr := internalio.ToReadSeeker(reader) header, err := carv1.ReadHeader(rr, sc.opts.MaxAllowedHeaderSize) @@ -73,39 +77,66 @@ func NewReadable(reader io.ReaderAt, opts ...carv2.Option) (ReadableCar, error) } switch header.Version { case 1: + sc.roots = header.Roots + sc.reader = reader rr.Seek(0, io.SeekStart) + sc.idx = insertionindex.NewInsertionIndex() if err := carv2.LoadIndex(sc.idx, rr, opts...); err != nil { return nil, err } - sc.reader = reader case 2: v2r, err := carv2.NewReader(reader, opts...) if err != nil { return nil, err } - dr, err := v2r.DataReader() + sc.roots, err = v2r.Roots() if err != nil { return nil, err } - if err := carv2.LoadIndex(sc.idx, dr, opts...); err != nil { - return nil, err + if v2r.Header.HasIndex() { + ir, err := v2r.IndexReader() + if err != nil { + return nil, err + } + sc.idx, err = index.ReadFrom(ir) + if err != nil { + return nil, err + } + } else { + dr, err := v2r.DataReader() + if err != nil { + return nil, err + } + sc.idx = insertionindex.NewInsertionIndex() + if err := carv2.LoadIndex(sc.idx, dr, opts...); err != nil { + return nil, err + } } if sc.reader, err = v2r.DataReader(); err != nil { return nil, err } default: - return nil, fmt.Errorf("unsupported car version: %v", header.Version) + return nil, fmt.Errorf("unsupported CAR version: %v", header.Version) } return sc, nil } func NewWritable(writer io.Writer, roots []cid.Cid, opts ...carv2.Option) (WritableCar, error) { + sc, err := newWritable(writer, roots, opts...) + if err != nil { + return nil, err + } + return sc.init() +} + +func newWritable(writer io.Writer, roots []cid.Cid, opts ...carv2.Option) (*StorageCar, error) { sc := &StorageCar{ writer: &positionTrackingWriter{w: writer}, idx: insertionindex.NewInsertionIndex(), header: carv2.NewHeader(0), opts: carv2.ApplyOptions(opts...), + roots: roots, } if p := sc.opts.DataPadding; p > 0 { @@ -124,67 +155,120 @@ func NewWritable(writer io.Writer, roots []cid.Cid, opts ...carv2.Option) (Writa sc.dataWriter = internalio.NewOffsetWriter(writerAt, offset) } else { if !sc.opts.WriteAsCarV1 { - return nil, fmt.Errorf("cannot write as carv2 to a non-seekable writer") + return nil, fmt.Errorf("cannot write as CARv2 to a non-seekable writer") + } + } + + return sc, nil +} + +func newReadableWritable(rw ReaderWriterAt, roots []cid.Cid, opts ...carv2.Option) (*StorageCar, error) { + sc, err := newWritable(rw, roots, opts...) + if err != nil { + return nil, err + } + + sc.reader = rw + if !sc.opts.WriteAsCarV1 { + sc.reader, err = internalio.NewOffsetReadSeeker(rw, int64(sc.header.DataOffset)) + if err != nil { + return nil, err } } - if err := sc.initWithRoots(writer, !sc.opts.WriteAsCarV1, roots); err != nil { + return sc, nil +} + +func NewReadableWritable(rw ReaderWriterAt, roots []cid.Cid, opts ...carv2.Option) (*StorageCar, error) { + sc, err := newReadableWritable(rw, roots, opts...) + if err != nil { + return nil, err + } + if _, err := sc.init(); err != nil { + return nil, err + } + return sc, nil +} + +func OpenReadableWritable(rw ReaderWriterAt, roots []cid.Cid, opts ...carv2.Option) (*StorageCar, error) { + sc, err := newReadableWritable(rw, roots, opts...) + if err != nil { return nil, err } + // attempt to resume + rs, err := internalio.NewOffsetReadSeeker(rw, 0) + if err != nil { + return nil, err + } + if err := store.ResumableVersion(rs, sc.opts.WriteAsCarV1); err != nil { + return nil, err + } + if err := store.Resume( + rw, + sc.reader, + sc.dataWriter, + sc.idx.(*insertionindex.InsertionIndex), + roots, + sc.header.DataOffset, + sc.opts.WriteAsCarV1, + sc.opts.MaxAllowedHeaderSize, + sc.opts.ZeroLengthSectionAsEOF, + ); err != nil { + return nil, err + } return sc, nil } -func (sc *StorageCar) initWithRoots(writer io.Writer, v2 bool, roots []cid.Cid) error { - if v2 { - if _, err := writer.Write(carv2.Pragma); err != nil { - return err +func (sc *StorageCar) init() (WritableCar, error) { + if !sc.opts.WriteAsCarV1 { + if _, err := sc.writer.Write(carv2.Pragma); err != nil { + return nil, err } - return carv1.WriteHeader(&carv1.CarHeader{Roots: roots, Version: 1}, sc.dataWriter) } - return carv1.WriteHeader(&carv1.CarHeader{Roots: roots, Version: 1}, writer) + var w io.Writer = sc.dataWriter + if sc.dataWriter == nil { + w = sc.writer + } + if err := carv1.WriteHeader(&carv1.CarHeader{Roots: sc.roots, Version: 1}, w); err != nil { + return nil, err + } + return sc, nil +} + +func (sc *StorageCar) Roots() []cid.Cid { + return sc.roots } func (sc *StorageCar) Put(ctx context.Context, keyStr string, data []byte) error { keyCid, err := cid.Cast([]byte(keyStr)) if err != nil { - return err + return fmt.Errorf("bad CID key: %w", err) } sc.mu.Lock() defer sc.mu.Unlock() - // If StoreIdentityCIDs option is disabled then treat IDENTITY CIDs like IdStore. - if !sc.opts.StoreIdentityCIDs { - // Check for IDENTITY CID. If IDENTITY, ignore and move to the next block. - if _, ok, err := store.IsIdentity(keyCid); err != nil { - return err - } else if ok { - return nil - } + if sc.closed { + return errClosed } - // Check if its size is too big. - // If larger than maximum allowed size, return error. - // Note, we need to check this regardless of whether we have IDENTITY CID or not. - // Since multhihash codes other than IDENTITY can result in large digests. - cSize := uint64(len(keyCid.Bytes())) - if cSize > sc.opts.MaxIndexCidSize { - return &carv2.ErrCidTooLarge{MaxSize: sc.opts.MaxIndexCidSize, CurrentSize: cSize} + idx, ok := sc.idx.(*insertionindex.InsertionIndex) + if !ok || sc.writer == nil { + return fmt.Errorf("cannot put into a read-only CAR") } - // TODO: if we are write-only and BlockstoreAllowDuplicatePuts then we don't - // really need an index at all - if !sc.opts.BlockstoreAllowDuplicatePuts { - if sc.opts.BlockstoreUseWholeCIDs && sc.idx.HasExactCID(keyCid) { - return nil // deduplicated by CID - } - if !sc.opts.BlockstoreUseWholeCIDs { - _, err := sc.idx.Get(keyCid) - if err == nil { - return nil // deduplicated by hash - } - } + if should, err := store.ShouldPut( + idx, + keyCid, + sc.opts.MaxIndexCidSize, + sc.opts.StoreIdentityCIDs, + sc.opts.BlockstoreAllowDuplicatePuts, + sc.opts.BlockstoreUseWholeCIDs, + ); err != nil { + return err + } else if !should { + return nil } w := sc.writer @@ -195,27 +279,15 @@ func (sc *StorageCar) Put(ctx context.Context, keyStr string, data []byte) error if err := util.LdWrite(w, keyCid.Bytes(), data); err != nil { return err } - sc.idx.InsertNoReplace(keyCid, n) + idx.InsertNoReplace(keyCid, n) return nil } -func (sc *StorageCar) Roots() ([]cid.Cid, error) { - ors, err := internalio.NewOffsetReadSeeker(sc.reader, 0) - if err != nil { - return nil, err - } - header, err := carv1.ReadHeader(ors, sc.opts.MaxAllowedHeaderSize) - if err != nil { - return nil, fmt.Errorf("error reading car header: %w", err) - } - return header.Roots, nil -} - func (sc *StorageCar) Has(ctx context.Context, keyStr string) (bool, error) { keyCid, err := cid.Cast([]byte(keyStr)) if err != nil { - return false, err + return false, fmt.Errorf("bad CID key: %w", err) } if !sc.opts.StoreIdentityCIDs { @@ -236,23 +308,21 @@ func (sc *StorageCar) Has(ctx context.Context, keyStr string) (bool, error) { return false, errClosed } - if sc.opts.BlockstoreUseWholeCIDs { - var foundCid cid.Cid - _, foundCid, err = sc.idx.GetCid(keyCid) - if err != nil { - if !foundCid.Equals(keyCid) { - return false, nil - } - } - } else { - _, err = sc.idx.Get(keyCid) - } + _, _, size, err := store.FindCid( + sc.reader, + sc.idx, + keyCid, + sc.opts.BlockstoreUseWholeCIDs, + sc.opts.ZeroLengthSectionAsEOF, + sc.opts.MaxAllowedSectionSize, + false, + ) if errors.Is(err, index.ErrNotFound) { return false, nil } else if err != nil { return false, err } - return true, nil + return size > -1, nil } func (sc *StorageCar) Get(ctx context.Context, keyStr string) ([]byte, error) { @@ -264,9 +334,13 @@ func (sc *StorageCar) Get(ctx context.Context, keyStr string) ([]byte, error) { } func (sc *StorageCar) GetStream(ctx context.Context, keyStr string) (io.ReadCloser, error) { + if sc.reader == nil { + return nil, fmt.Errorf("cannot read from a write-only CAR") + } + keyCid, err := cid.Cast([]byte(keyStr)) if err != nil { - return nil, err + return nil, fmt.Errorf("bad CID key: %w", err) } if !sc.opts.StoreIdentityCIDs { @@ -287,46 +361,30 @@ func (sc *StorageCar) GetStream(ctx context.Context, keyStr string) (io.ReadClos return nil, errClosed } - fnSize := -1 - var offset uint64 - if sc.opts.BlockstoreUseWholeCIDs { - var foundCid cid.Cid - offset, foundCid, err = sc.idx.GetCid(keyCid) - if err != nil { - if !foundCid.Equals(keyCid) { - return nil, ErrNotFound{Cid: keyCid} - } - } - } else { - offset, err = sc.idx.Get(keyCid) - } + _, offset, size, err := store.FindCid( + sc.reader, + sc.idx, + keyCid, + sc.opts.BlockstoreUseWholeCIDs, + sc.opts.ZeroLengthSectionAsEOF, + sc.opts.MaxAllowedSectionSize, + false, + ) if errors.Is(err, index.ErrNotFound) { return nil, ErrNotFound{Cid: keyCid} } else if err != nil { return nil, err } - - rdr, err := internalio.NewOffsetReadSeeker(sc.reader, int64(offset)) - if err != nil { - return nil, err - } - sectionLen, err := varint.ReadUvarint(rdr) - if err != nil { - return nil, err - } - cidLen, _, err := cid.CidFromReader(rdr) - if err != nil { - return nil, err - } - fnSize = int(sectionLen) - cidLen - offset = uint64(rdr.(interface{ Offset() int64 }).Offset()) - if fnSize == -1 { - return nil, ErrNotFound{Cid: keyCid} - } - return io.NopCloser(io.NewSectionReader(sc.reader, int64(offset), int64(fnSize))), nil + return io.NopCloser(io.NewSectionReader(sc.reader, offset, int64(size))), nil } func (sc *StorageCar) Finalize() error { + idx, ok := sc.idx.(*insertionindex.InsertionIndex) + if !ok || sc.writer == nil { + // ignore this, it's not writable + return nil + } + if sc.opts.WriteAsCarV1 { return nil } @@ -342,29 +400,12 @@ func (sc *StorageCar) Finalize() error { if sc.closed { // Allow duplicate Finalize calls, just like Close. // Still error, just like ReadOnly.Close; it should be discarded. - return fmt.Errorf("called Finalize on a closed blockstore") + return fmt.Errorf("called Finalize on a closed storage CAR") } - // TODO check if add index option is set and don't write the index then set index offset to zero. - sc.header = sc.header.WithDataSize(uint64(sc.dataWriter.Position())) - sc.header.Characteristics.SetFullyIndexed(sc.opts.StoreIdentityCIDs) - sc.closed = true - fi, err := sc.idx.Flatten(sc.opts.IndexCodec) - if err != nil { - return err - } - if _, err := index.WriteTo(fi, internalio.NewOffsetWriter(wat, int64(sc.header.IndexOffset))); err != nil { - return err - } - var buf bytes.Buffer - sc.header.WriteTo(&buf) - if _, err := sc.header.WriteTo(internalio.NewOffsetWriter(wat, carv2.PragmaSize)); err != nil { - return err - } - - return nil + return store.Finalize(wat, sc.header, idx, uint64(sc.dataWriter.Position()), sc.opts.StoreIdentityCIDs, sc.opts.IndexCodec) } type positionTrackingWriter struct { diff --git a/ipld/car/v2/storage/storage_test.go b/ipld/car/v2/storage/storage_test.go index 0b34b5025..ba1d01878 100644 --- a/ipld/car/v2/storage/storage_test.go +++ b/ipld/car/v2/storage/storage_test.go @@ -1,6 +1,9 @@ package storage_test +// TODO: test readable can't write and writable can't read + import ( + "bytes" "context" "crypto/sha512" "errors" @@ -9,82 +12,100 @@ import ( "math/rand" "os" "path/filepath" + "sync" "testing" "time" "github.com/ipfs/go-cid" carv2 "github.com/ipld/go-car/v2" - "github.com/ipld/go-car/v2/blockstore" + "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/storage" + "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" + mh "github.com/multiformats/go-multihash" "github.com/stretchr/testify/require" ) var rng = rand.New(rand.NewSource(1413)) +var rngLk sync.Mutex func TestReadable(t *testing.T) { tests := []struct { - name string - v1OrV2path string - opts []carv2.Option + name string + inputPath string + opts []carv2.Option + noIdCids bool }{ { "OpenedWithCarV1", "../testdata/sample-v1.car", - []carv2.Option{blockstore.UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, + []carv2.Option{carv2.UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, + false, }, { "OpenedWithCarV1_NoIdentityCID", "../testdata/sample-v1.car", - []carv2.Option{blockstore.UseWholeCIDs(true)}, + []carv2.Option{carv2.UseWholeCIDs(true)}, + false, }, { "OpenedWithCarV2", "../testdata/sample-wrapped-v2.car", - []carv2.Option{blockstore.UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, + []carv2.Option{carv2.UseWholeCIDs(true), carv2.StoreIdentityCIDs(true)}, + // index already exists, but was made without identity CIDs, but opening with StoreIdentityCIDs(true) means we check the index + true, }, { "OpenedWithCarV2_NoIdentityCID", "../testdata/sample-wrapped-v2.car", - []carv2.Option{blockstore.UseWholeCIDs(true)}, + []carv2.Option{carv2.UseWholeCIDs(true)}, + false, }, { "OpenedWithCarV1ZeroLenSection", "../testdata/sample-v1-with-zero-len-section.car", - []carv2.Option{blockstore.UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, + []carv2.Option{carv2.UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, + false, }, { "OpenedWithAnotherCarV1ZeroLenSection", "../testdata/sample-v1-with-zero-len-section2.car", - []carv2.Option{blockstore.UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, + []carv2.Option{carv2.UseWholeCIDs(true), carv2.ZeroLengthSectionAsEOF(true)}, + false, + }, + { + "IndexlessV2", + "../testdata/sample-v2-indexless.car", + []carv2.Option{carv2.UseWholeCIDs(true)}, + false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx := context.TODO() - subjectReader, err := os.Open(tt.v1OrV2path) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + // Setup new StorageCar + inputReader, err := os.Open(tt.inputPath) require.NoError(t, err) - subject, err := storage.NewReadable(subjectReader, tt.opts...) + t.Cleanup(func() { require.NoError(t, inputReader.Close()) }) + readable, err := storage.NewReadable(inputReader, tt.opts...) require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, subjectReader.Close()) }) - f, err := os.Open(tt.v1OrV2path) + // Setup BlockReader to compare against + actualReader, err := os.Open(tt.inputPath) require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, f.Close()) }) - - reader, err := carv2.NewBlockReader(f, tt.opts...) + t.Cleanup(func() { require.NoError(t, actualReader.Close()) }) + actual, err := carv2.NewBlockReader(actualReader, tt.opts...) require.NoError(t, err) // Assert roots match v1 payload. - wantRoots := reader.Roots - gotRoots, err := subject.Roots() - require.NoError(t, err) - require.Equal(t, wantRoots, gotRoots) + require.Equal(t, actual.Roots, readable.Roots()) for { - wantBlock, err := reader.Next() + wantBlock, err := actual.Next() if err == io.EOF { break } @@ -92,18 +113,24 @@ func TestReadable(t *testing.T) { key := wantBlock.Cid() - // Assert blockstore contains key. - has, err := subject.Has(ctx, key.KeyString()) + // Assert StorageCar contains key. + has, err := readable.Has(ctx, key.KeyString()) require.NoError(t, err) - require.True(t, has) + if key.Prefix().MhType == uint64(multicodec.Identity) && tt.noIdCids { + // fixture wasn't made with StoreIdentityCIDs, but we opened it with StoreIdentityCIDs, + // so they aren't there to find + require.False(t, has) + } else { + require.True(t, has) + } // Assert block itself matches v1 payload block. if has { - gotBlock, err := subject.Get(ctx, key.KeyString()) + gotBlock, err := readable.Get(ctx, key.KeyString()) require.NoError(t, err) require.Equal(t, wantBlock.RawData(), gotBlock) - reader, err := subject.GetStream(ctx, key.KeyString()) + reader, err := readable.GetStream(ctx, key.KeyString()) require.NoError(t, err) data, err := io.ReadAll(reader) require.NoError(t, err) @@ -111,13 +138,13 @@ func TestReadable(t *testing.T) { } } - // not exists + // test not exists c := randCid() - has, err := subject.Has(ctx, c.KeyString()) + has, err := readable.Has(ctx, c.KeyString()) require.NoError(t, err) require.False(t, has) - _, err = subject.Get(ctx, c.KeyString()) + _, err = readable.Get(ctx, c.KeyString()) require.True(t, errors.Is(err, storage.ErrNotFound{})) require.True(t, storage.IsNotFound(err)) require.Contains(t, err.Error(), c.String()) @@ -126,11 +153,11 @@ func TestReadable(t *testing.T) { storeIdentity := carv2.ApplyOptions(tt.opts...).StoreIdentityCIDs c = randIdentityCid() - has, err = subject.Has(ctx, c.KeyString()) + has, err = readable.Has(ctx, c.KeyString()) require.NoError(t, err) require.Equal(t, !storeIdentity, has) - got, err := subject.Get(ctx, c.KeyString()) + got, err := readable.Get(ctx, c.KeyString()) if !storeIdentity { require.NoError(t, err) mh, err := multihash.Decode(c.Hash()) @@ -145,6 +172,15 @@ func TestReadable(t *testing.T) { } } +func TestReadableBadVersion(t *testing.T) { + f, err := os.Open("../testdata/sample-rootless-v42.car") + require.NoError(t, err) + t.Cleanup(func() { f.Close() }) + subject, err := storage.NewReadable(f) + require.Errorf(t, err, "unsupported car version: 42") + require.Nil(t, subject) +} + func TestWritable(t *testing.T) { originalCarV1Path := "../testdata/sample-v1.car" @@ -154,154 +190,971 @@ func TestWritable(t *testing.T) { options []carv2.Option expectedV1StartOffset int64 }{ - // no options, expect a standard CARv2 with the noidentity inner CARv1 {"carv2_noopt", "sample-v1-noidentity.car", []carv2.Option{}, int64(carv2.PragmaSize + carv2.HeaderSize)}, - // no options, expect a standard CARv2 with the noidentity inner CARv1 {"carv2_identity", "sample-v1.car", []carv2.Option{carv2.StoreIdentityCIDs(true)}, int64(carv2.PragmaSize + carv2.HeaderSize)}, - // option to only write as a CARv1, expect the noidentity inner CARv1 - {"carv1", "sample-v1-noidentity.car", []carv2.Option{blockstore.WriteAsCarV1(true)}, int64(0)}, - // option to only write as a CARv1, expect the noidentity inner CARv1 - {"carv1_identity", "sample-v1.car", []carv2.Option{blockstore.WriteAsCarV1(true), carv2.StoreIdentityCIDs(true)}, int64(0)}, + {"carv1", "sample-v1-noidentity.car", []carv2.Option{carv2.WriteAsCarV1(true)}, int64(0)}, + {"carv1_identity", "sample-v1.car", []carv2.Option{carv2.WriteAsCarV1(true), carv2.StoreIdentityCIDs(true)}, int64(0)}, } - for _, variant := range variants { - t.Run(variant.name, func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() + for _, mode := range []string{"WithRead", "WithoutRead"} { + t.Run(mode, func(t *testing.T) { + for _, variant := range variants { + t.Run(variant.name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + opts := carv2.ApplyOptions(variant.options...) + + // Setup input file using standard CarV1 reader + srcFile, err := os.Open(originalCarV1Path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, srcFile.Close()) }) + r, err := carv1.NewCarReader(srcFile) + require.NoError(t, err) + + path := filepath.Join(t.TempDir(), fmt.Sprintf("writable_%s_%s.car", mode, variant.name)) + var dstFile *os.File + + var writable *storage.StorageCar + if mode == "WithoutRead" { + dstFile, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) + require.NoError(t, err) + t.Cleanup(func() { dstFile.Close() }) + var writer io.Writer = &writerOnly{dstFile} + if !opts.WriteAsCarV1 { + writer = &writerAtOnly{dstFile} + } + w, err := storage.NewWritable(writer, r.Header.Roots, variant.options...) + require.NoError(t, err) + writable = w.(*storage.StorageCar) + } else { + dstFile, err = os.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644) + require.NoError(t, err) + t.Cleanup(func() { dstFile.Close() }) + writable, err = storage.NewReadableWritable(dstFile, r.Header.Roots, variant.options...) + require.NoError(t, err) + } + + require.Equal(t, r.Header.Roots, writable.Roots()) + + cids := make([]cid.Cid, 0) + var idCidCount int + for { + // read from source + b, err := r.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + + // write to dest + err = writable.Put(ctx, b.Cid().KeyString(), b.RawData()) + require.NoError(t, err) + cids = append(cids, b.Cid()) + + dmh, err := multihash.Decode(b.Cid().Hash()) + require.NoError(t, err) + if dmh.Code == multihash.IDENTITY { + idCidCount++ + } + + if mode == "WithRead" { + // writable is a ReadableWritable / StorageCar + + // read back out the one we just wrote + gotBlock, err := writable.Get(ctx, b.Cid().KeyString()) + require.NoError(t, err) + require.Equal(t, b.RawData(), gotBlock) + + reader, err := writable.GetStream(ctx, b.Cid().KeyString()) + require.NoError(t, err) + data, err := io.ReadAll(reader) + require.NoError(t, err) + require.Equal(t, b.RawData(), data) + + // try reading a random one: + candIndex := rng.Intn(len(cids)) + var candidate cid.Cid + for _, c := range cids { + if candIndex == 0 { + candidate = c + break + } + candIndex-- + } + has, err := writable.Has(ctx, candidate.KeyString()) + require.NoError(t, err) + require.True(t, has) + + // not exists + c := randCid() + has, err = writable.Has(ctx, c.KeyString()) + require.NoError(t, err) + require.False(t, has) + _, err = writable.Get(ctx, c.KeyString()) + require.True(t, errors.Is(err, storage.ErrNotFound{})) + require.True(t, storage.IsNotFound(err)) + require.Contains(t, err.Error(), c.String()) + + // random identity, should only find this if we _don't_ store identity CIDs + c = randIdentityCid() + has, err = writable.Has(ctx, c.KeyString()) + require.NoError(t, err) + require.Equal(t, !opts.StoreIdentityCIDs, has) + + got, err := writable.Get(ctx, c.KeyString()) + if !opts.StoreIdentityCIDs { + require.NoError(t, err) + mh, err := multihash.Decode(c.Hash()) + require.NoError(t, err) + require.Equal(t, mh.Digest, got) + } else { + require.True(t, errors.Is(err, storage.ErrNotFound{})) + require.True(t, storage.IsNotFound(err)) + require.Contains(t, err.Error(), c.String()) + } + } + } + + err = writable.Finalize() + require.NoError(t, err) + + err = dstFile.Close() + require.NoError(t, err) + + // test header version using carv2 reader + reopen, err := os.Open(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, reopen.Close()) }) + rd, err := carv2.NewReader(reopen) + require.NoError(t, err) + require.Equal(t, opts.WriteAsCarV1, rd.Version == 1) + + // now compare the binary contents of the written file to the expected file + comparePath := filepath.Join("../testdata/", variant.compareCarV1) + compareStat, err := os.Stat(comparePath) + require.NoError(t, err) + + wrote, err := os.Open(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, wrote.Close()) }) + _, err = wrote.Seek(variant.expectedV1StartOffset, io.SeekStart) + require.NoError(t, err) + hasher := sha512.New() + gotWritten, err := io.Copy(hasher, io.LimitReader(wrote, compareStat.Size())) + require.NoError(t, err) + gotSum := hasher.Sum(nil) + + hasher.Reset() + compareV1, err := os.Open(comparePath) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, compareV1.Close()) }) + wantWritten, err := io.Copy(hasher, compareV1) + require.NoError(t, err) + wantSum := hasher.Sum(nil) + + require.Equal(t, wantWritten, gotWritten) + require.Equal(t, wantSum, gotSum) + }) + } + }) + } +} + +func TestCannotWriteableV2WithoutWriterAt(t *testing.T) { + w, err := storage.NewWritable(&writerOnly{os.Stdout}, []cid.Cid{}) + require.Error(t, err) + require.Nil(t, w) +} + +func TestErrorsWhenWritingCidTooLarge(t *testing.T) { + maxAllowedCidSize := uint64(20) + + path := filepath.Join(t.TempDir(), "writable-with-id-enabled-too-large.car") + out, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, out.Close()) }) + subject, err := storage.NewWritable(out, []cid.Cid{}, carv2.MaxIndexCidSize(maxAllowedCidSize)) + require.NoError(t, err) + + // normal block but shorten the CID to make it acceptable + testCid, testData := randBlock() + mh, err := mh.Decode(testCid.Hash()) + require.NoError(t, err) + dig := mh.Digest[:10] + shortMh, err := multihash.Encode(dig, mh.Code) + require.NoError(t, err) + testCid = cid.NewCidV1(mh.Code, shortMh) - opts := carv2.ApplyOptions(variant.options...) + err = subject.Put(context.TODO(), testCid.KeyString(), testData) + require.NoError(t, err) - srcFile, err := os.Open(originalCarV1Path) + // standard CID but too long for options + testCid, testData = randBlock() + err = subject.Put(context.TODO(), testCid.KeyString(), testData) + require.Equal(t, &carv2.ErrCidTooLarge{MaxSize: maxAllowedCidSize, CurrentSize: uint64(testCid.ByteLen())}, err) +} + +func TestConcurrentUse(t *testing.T) { + dst, err := os.OpenFile(filepath.Join(t.TempDir(), "readwrite.car"), os.O_CREATE|os.O_RDWR, 0644) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, dst.Close()) }) + wbs, err := storage.NewReadableWritable(dst, nil) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + require.NoError(t, err) + t.Cleanup(func() { wbs.Finalize() }) + + var wg sync.WaitGroup + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + testCid, testData := randBlock() + + has, err := wbs.Has(ctx, testCid.KeyString()) require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, srcFile.Close()) }) - r, err := carv1.NewCarReader(srcFile) + require.False(t, has) + + err = wbs.Put(ctx, testCid.KeyString(), testData) require.NoError(t, err) - path := filepath.Join("/tmp/", fmt.Sprintf("writable_%s.car", variant.name)) - dstFile, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644) + got, err := wbs.Get(ctx, testCid.KeyString()) require.NoError(t, err) - var writer io.Writer = &writerOnly{dstFile} - if !opts.WriteAsCarV1 { - writer = &writerAtOnly{dstFile} + require.Equal(t, testData, got) + }() + } + wg.Wait() +} + +func TestNullPadding(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + paddedV1, err := os.ReadFile("../testdata/sample-v1-with-zero-len-section.car") + require.NoError(t, err) + + readable, err := storage.NewReadable(bufferReaderAt(paddedV1), carv2.ZeroLengthSectionAsEOF(true)) + require.NoError(t, err) + + roots := readable.Roots() + require.Len(t, roots, 1) + has, err := readable.Has(ctx, roots[0].KeyString()) + require.NoError(t, err) + require.True(t, has) + + actual, err := carv2.NewBlockReader(bytes.NewReader(paddedV1), carv2.ZeroLengthSectionAsEOF(true)) + require.NoError(t, err) + + for { + wantBlock, err := actual.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + b, err := readable.Get(ctx, wantBlock.Cid().KeyString()) + require.NoError(t, err) + require.Equal(t, wantBlock.RawData(), b) + } +} + +func TestPutSameHashes(t *testing.T) { + tdir := t.TempDir() + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + // This writable allows duplicate puts, and identifies by multihash as per the default. + pathAllowDups := filepath.Join(tdir, "writable-allowdup.car") + dstAllowDups, err := os.OpenFile(pathAllowDups, os.O_CREATE|os.O_RDWR, 0644) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, dstAllowDups.Close()) }) + wbsAllowDups, err := storage.NewReadableWritable(dstAllowDups, nil, carv2.AllowDuplicatePuts(true)) + require.NoError(t, err) + + // This writable deduplicates puts by CID. + pathByCID := filepath.Join(tdir, "writable-dedup-wholecid.car") + dstByCID, err := os.OpenFile(pathByCID, os.O_CREATE|os.O_RDWR, 0644) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, dstByCID.Close()) }) + wbsByCID, err := storage.NewReadableWritable(dstByCID, nil, carv2.UseWholeCIDs(true)) + require.NoError(t, err) + + // This writable deduplicates puts by multihash + pathByHash := filepath.Join(tdir, "writable-dedup-byhash.car") + dstByHash, err := os.OpenFile(pathByHash, os.O_CREATE|os.O_RDWR, 0644) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, dstByHash.Close()) }) + wbsByHash, err := storage.NewReadableWritable(dstByHash, nil) + require.NoError(t, err) + + var blockList []struct { + cid cid.Cid + data []byte + } + + appendBlock := func(data []byte, version, codec uint64) { + c, err := cid.Prefix{ + Version: version, + Codec: codec, + MhType: multihash.SHA2_256, + MhLength: -1, + }.Sum(data) + require.NoError(t, err) + blockList = append(blockList, struct { + cid cid.Cid + data []byte + }{c, data}) + } + + // Two raw blocks, meaning we have two unique multihashes. + // However, we have multiple CIDs for each multihash. + // We also have two duplicate CIDs. + data1 := []byte("foo bar") + appendBlock(data1, 0, cid.DagProtobuf) + appendBlock(data1, 1, cid.DagProtobuf) + appendBlock(data1, 1, cid.DagCBOR) + appendBlock(data1, 1, cid.DagCBOR) // duplicate CID + + data2 := []byte("foo bar baz") + appendBlock(data2, 0, cid.DagProtobuf) + appendBlock(data2, 1, cid.DagProtobuf) + appendBlock(data2, 1, cid.DagProtobuf) // duplicate CID + appendBlock(data2, 1, cid.DagCBOR) + + countBlocks := func(path string) int { + f, err := os.Open(path) + require.NoError(t, err) + rdr, err := carv2.NewBlockReader(f) + require.NoError(t, err) + + n := 0 + for { + _, err := rdr.Next() + if err == io.EOF { + break } - ingester, err := storage.NewWritable(writer, r.Header.Roots, variant.options...) + n++ + } + return n + } + + putBlockList := func(writable *storage.StorageCar) { + for i, block := range blockList { + // Has should never error here. + // The first block should be missing. + // Others might not, given the duplicate hashes. + has, err := writable.Has(ctx, block.cid.KeyString()) + require.NoError(t, err) + if i == 0 { + require.False(t, has) + } + + err = writable.Put(ctx, block.cid.KeyString(), block.data) + require.NoError(t, err) + + // Has and Get need to work right after a Put + has, err = writable.Has(ctx, block.cid.KeyString()) require.NoError(t, err) - t.Cleanup(func() { dstFile.Close() }) + require.True(t, has) + + got, err := writable.Get(ctx, block.cid.KeyString()) + require.NoError(t, err) + require.Equal(t, block.data, got) + } + } + + putBlockList(wbsAllowDups) + err = wbsAllowDups.Finalize() + require.NoError(t, err) + require.Equal(t, len(blockList), countBlocks(pathAllowDups)) + + // Put the same list of blocks to the CAR that deduplicates by CID. + // We should end up with two fewer blocks, as two are entire CID duplicates. + putBlockList(wbsByCID) + err = wbsByCID.Finalize() + require.NoError(t, err) + require.Equal(t, len(blockList)-2, countBlocks(pathByCID)) + + // Put the same list of blocks to the CAR that deduplicates by CID. + // We should end up with just two blocks, as the original set of blocks only + // has two distinct multihashes. + putBlockList(wbsByHash) + err = wbsByHash.Finalize() + require.NoError(t, err) + require.Equal(t, 2, countBlocks(pathByHash)) +} + +func TestReadableCantWrite(t *testing.T) { + inp, err := os.Open("../testdata/sample-v1.car") + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, inp.Close()) }) + readable, err := storage.NewReadable(inp) + require.NoError(t, err) + require.ErrorContains(t, readable.(*storage.StorageCar).Put(context.Background(), randCid().KeyString(), []byte("bar")), "read-only") + // Finalize() is nonsense for a readable, but it should be safe + require.NoError(t, readable.(*storage.StorageCar).Finalize()) +} + +func TestWritableCantRead(t *testing.T) { + // an io.Writer with no io.WriterAt capabilities + path := filepath.Join(t.TempDir(), "writable.car") + out, err := os.Create(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, out.Close()) }) + + // This should fail because the writer is not an io.WriterAt + _, err = storage.NewWritable(&writerOnly{out}, nil) + require.ErrorContains(t, err, "CARv2") + require.ErrorContains(t, err, "non-seekable") + + writable, err := storage.NewWritable(&writerOnly{out}, nil, carv2.WriteAsCarV1(true)) + require.NoError(t, err) + + _, err = writable.(*storage.StorageCar).Get(context.Background(), randCid().KeyString()) + require.ErrorContains(t, err, "write-only") + + _, err = writable.(*storage.StorageCar).GetStream(context.Background(), randCid().KeyString()) + require.ErrorContains(t, err, "write-only") + + require.NoError(t, writable.Finalize()) +} + +func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + testCid1, testData1 := randBlock() + testCid2, testData2 := randBlock() + + wantRoots := []cid.Cid{testCid1, testCid2} + path := filepath.Join(t.TempDir(), "readwrite-with-padding.car") + writer, err := os.Create(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, writer.Close()) }) + + wantCarV1Padding := uint64(1413) + wantIndexPadding := uint64(1314) + subject, err := storage.NewReadableWritable( + writer, + wantRoots, + carv2.UseDataPadding(wantCarV1Padding), + carv2.UseIndexPadding(wantIndexPadding)) + require.NoError(t, err) + require.NoError(t, subject.Put(ctx, testCid1.KeyString(), testData1)) + require.NoError(t, subject.Put(ctx, testCid2.KeyString(), testData2)) + require.NoError(t, subject.Finalize()) + + // Assert CARv2 header contains right offsets. + gotCarV2, err := carv2.OpenReader(path) + t.Cleanup(func() { gotCarV2.Close() }) + require.NoError(t, err) + wantCarV1Offset := carv2.PragmaSize + carv2.HeaderSize + wantCarV1Padding + wantIndexOffset := wantCarV1Offset + gotCarV2.Header.DataSize + wantIndexPadding + require.Equal(t, wantCarV1Offset, gotCarV2.Header.DataOffset) + require.Equal(t, wantIndexOffset, gotCarV2.Header.IndexOffset) + require.NoError(t, gotCarV2.Close()) - cids := make([]cid.Cid, 0) - var idCidCount int + f, err := os.Open(path) + require.NoError(t, err) + t.Cleanup(func() { f.Close() }) + + // Assert reading CARv1 directly at offset and size is as expected. + gotCarV1, err := carv1.NewCarReader(io.NewSectionReader(f, int64(wantCarV1Offset), int64(gotCarV2.Header.DataSize))) + require.NoError(t, err) + require.Equal(t, wantRoots, gotCarV1.Header.Roots) + gotBlock, err := gotCarV1.Next() + require.NoError(t, err) + require.Equal(t, testCid1, gotBlock.Cid()) + require.Equal(t, testData1, gotBlock.RawData()) + gotBlock, err = gotCarV1.Next() + require.NoError(t, err) + require.Equal(t, testCid2, gotBlock.Cid()) + require.Equal(t, testData2, gotBlock.RawData()) + + _, err = gotCarV1.Next() + require.Equal(t, io.EOF, err) + + // Assert reading index directly from file is parsable and has expected CIDs. + stat, err := f.Stat() + require.NoError(t, err) + indexSize := stat.Size() - int64(wantIndexOffset) + gotIdx, err := index.ReadFrom(io.NewSectionReader(f, int64(wantIndexOffset), indexSize)) + require.NoError(t, err) + _, err = index.GetFirst(gotIdx, testCid1) + require.NoError(t, err) + _, err = index.GetFirst(gotIdx, testCid2) + require.NoError(t, err) +} + +func TestResumption(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + srcPath := "../testdata/sample-v1.car" + + v1f, err := os.Open(srcPath) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, v1f.Close()) }) + rd, err := carv2.NewReader(v1f) + require.NoError(t, err) + roots, err := rd.Roots() + require.NoError(t, err) + + blockSource := func() <-chan simpleBlock { + v1f, err := os.Open(srcPath) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, v1f.Close()) }) + r, err := carv1.NewCarReader(v1f) + require.NoError(t, err) + ret := make(chan simpleBlock) + + go func() { for { b, err := r.Next() if err == io.EOF { + close(ret) break } require.NoError(t, err) + ret <- simpleBlock{cid: b.Cid(), data: b.RawData()} + } + }() - err = ingester.Put(ctx, b.Cid().KeyString(), b.RawData()) - require.NoError(t, err) - cids = append(cids, b.Cid()) + return ret + } - dmh, err := multihash.Decode(b.Cid().Hash()) - require.NoError(t, err) - if dmh.Code == multihash.IDENTITY { - idCidCount++ - } + path := filepath.Join(t.TempDir(), "readwrite-resume.car") + writer, err := os.Create(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, writer.Close()) }) + // Create an incomplete CARv2 file with no blocks put. + subject, err := storage.NewReadableWritable(writer, roots, carv2.UseWholeCIDs(true)) + require.NoError(t, err) - // try reading a random one: - candIndex := rng.Intn(len(cids)) - var candidate cid.Cid - for _, c := range cids { - if candIndex == 0 { - candidate = c - break - } - candIndex-- - } - has, err := ingester.Has(ctx, candidate.KeyString()) - require.NoError(t, err) - require.True(t, has) + // For each block resume on the same file, putting blocks one at a time. + var wantBlockCountSoFar, idCidCount int + wantBlocks := make(map[cid.Cid]simpleBlock) + for b := range blockSource() { + wantBlockCountSoFar++ + wantBlocks[b.cid] = b - // not exists - has, err = ingester.Has(ctx, randCid().KeyString()) - require.NoError(t, err) - require.False(t, has) + dmh, err := multihash.Decode(b.cid.Hash()) + require.NoError(t, err) + if dmh.Code == multihash.IDENTITY { + idCidCount++ + } - // random identity - has, err = ingester.Has(ctx, randIdentityCid().KeyString()) - require.NoError(t, err) - require.Equal(t, !opts.StoreIdentityCIDs, has) + // 30% chance of subject failing; more concretely: re-instantiating the StorageCar with the same + // file without calling Finalize. The higher this percentage the slower the test runs + // considering the number of blocks in the original CARv1 test payload. + resume := rng.Float32() <= 0.3 + // If testing resume case, then flip a coin to decide whether to finalize before the StorageCar + // re-instantiation or not. Note, both cases should work for resumption since we do not + // limit resumption to unfinalized files. + finalizeBeforeResumption := rng.Float32() <= 0.5 + if resume { + if finalizeBeforeResumption { + require.NoError(t, subject.Finalize()) } - err = ingester.Finalize() + _, err := writer.Seek(0, io.SeekStart) require.NoError(t, err) - - err = dstFile.Close() + subject, err = storage.OpenReadableWritable(writer, roots, carv2.UseWholeCIDs(true)) require.NoError(t, err) + } + require.NoError(t, subject.Put(ctx, b.cid.KeyString(), b.data)) - reopen, err := os.Open(path) - require.NoError(t, err) - rd, err := carv2.NewReader(reopen) - require.NoError(t, err) - require.Equal(t, opts.WriteAsCarV1, rd.Version == 1) - require.NoError(t, reopen.Close()) + // With 10% chance test read operations on an resumed read-write StorageCar. + // We don't test on every put to reduce test runtime. + testRead := rng.Float32() <= 0.1 + if testRead { + // Assert read operations on the read-write StorageCar are as expected when resumed from an + // existing file + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + t.Cleanup(cancel) + for k, wantBlock := range wantBlocks { + has, err := subject.Has(ctx, k.KeyString()) + require.NoError(t, err) + require.True(t, has) + gotBlock, err := subject.Get(ctx, k.KeyString()) + require.NoError(t, err) + require.Equal(t, wantBlock.data, gotBlock) + } + // Assert the number of blocks in file are as expected calculated via AllKeysChan + require.Equal(t, wantBlockCountSoFar, len(wantBlocks)) + } + } - robs, err := blockstore.OpenReadOnly(path) - require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, robs.Close()) }) + // Finalize the StorageCar to complete partially written CARv2 file. + subject, err = storage.OpenReadableWritable(writer, roots, carv2.UseWholeCIDs(true)) + require.NoError(t, err) + require.NoError(t, subject.Finalize()) - allKeysCh, err := robs.AllKeysChan(ctx) - require.NoError(t, err) - numKeysCh := 0 - for c := range allKeysCh { - b, err := robs.Get(ctx, c) - require.NoError(t, err) - if !b.Cid().Equals(c) { - t.Fatal("wrong item returned") + // Assert resumed from file is a valid CARv2 with index. + v2f, err := os.Open(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, v2f.Close()) }) + v2r, err := carv2.NewReader(v2f) + require.NoError(t, err) + require.True(t, v2r.Header.HasIndex()) + + // Assert CARv1 payload in file matches the original CARv1 payload. + _, err = v1f.Seek(0, io.SeekStart) + require.NoError(t, err) + wantPayloadReader, err := carv1.NewCarReader(v1f) + require.NoError(t, err) + + dr, err := v2r.DataReader() + require.NoError(t, err) + gotPayloadReader, err := carv1.NewCarReader(dr) + require.NoError(t, err) + + require.Equal(t, wantPayloadReader.Header, gotPayloadReader.Header) + for { + wantNextBlock, wantErr := wantPayloadReader.Next() + if wantErr == io.EOF { + gotNextBlock, gotErr := gotPayloadReader.Next() + require.Equal(t, wantErr, gotErr) + require.Nil(t, gotNextBlock) + break + } + require.NoError(t, wantErr) + + dmh, err := multihash.Decode(wantNextBlock.Cid().Hash()) + require.NoError(t, err) + if dmh.Code == multihash.IDENTITY { + continue + } + + gotNextBlock, gotErr := gotPayloadReader.Next() + require.NoError(t, gotErr) + require.Equal(t, wantNextBlock, gotNextBlock) + } + + // Assert index in resumed from file is identical to index generated from the data payload portion of the generated CARv2 file. + _, err = v1f.Seek(0, io.SeekStart) + require.NoError(t, err) + ir, err := v2r.IndexReader() + require.NoError(t, err) + gotIdx, err := index.ReadFrom(ir) + require.NoError(t, err) + dr, err = v2r.DataReader() + require.NoError(t, err) + wantIdx, err := carv2.GenerateIndex(dr) + require.NoError(t, err) + require.Equal(t, wantIdx, gotIdx) +} + +func TestResumptionV1(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + srcPath := "../testdata/sample-v1.car" + + v1f, err := os.Open(srcPath) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, v1f.Close()) }) + rd, err := carv2.NewReader(v1f) + require.NoError(t, err) + roots, err := rd.Roots() + require.NoError(t, err) + + blockSource := func() <-chan simpleBlock { + v1f, err := os.Open(srcPath) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, v1f.Close()) }) + r, err := carv1.NewCarReader(v1f) + require.NoError(t, err) + ret := make(chan simpleBlock) + + go func() { + for { + b, err := r.Next() + if err == io.EOF { + close(ret) + break } - numKeysCh++ + require.NoError(t, err) + ret <- simpleBlock{cid: b.Cid(), data: b.RawData()} } - expectedCidCount := len(cids) - if !opts.StoreIdentityCIDs { - expectedCidCount -= idCidCount + }() + + return ret + } + + path := filepath.Join(t.TempDir(), "readwrite-resume-v1.car") + writer, err := os.Create(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, writer.Close()) }) + // Create an incomplete CARv2 file with no blocks put. + subject, err := storage.NewReadableWritable(writer, roots, carv2.UseWholeCIDs(true), carv2.WriteAsCarV1(true)) + require.NoError(t, err) + + // For each block resume on the same file, putting blocks one at a time. + var wantBlockCountSoFar, idCidCount int + wantBlocks := make(map[cid.Cid]simpleBlock) + for b := range blockSource() { + wantBlockCountSoFar++ + wantBlocks[b.cid] = b + + dmh, err := multihash.Decode(b.cid.Hash()) + require.NoError(t, err) + if dmh.Code == multihash.IDENTITY { + idCidCount++ + } + + // 30% chance of subject failing; more concretely: re-instantiating the StorageCar with the same + // file without calling Finalize. The higher this percentage the slower the test runs + // considering the number of blocks in the original CARv1 test payload. + resume := rng.Float32() <= 0.3 + // If testing resume case, then flip a coin to decide whether to finalize before the StorageCar + // re-instantiation or not. Note, both cases should work for resumption since we do not + // limit resumption to unfinalized files. + finalizeBeforeResumption := rng.Float32() <= 0.5 + if resume { + if finalizeBeforeResumption { + require.NoError(t, subject.Finalize()) } - require.Equal(t, expectedCidCount, numKeysCh, "AllKeysChan returned an unexpected amount of keys; expected %v but got %v", expectedCidCount, numKeysCh) - for _, c := range cids { - b, err := robs.Get(ctx, c) + _, err := writer.Seek(0, io.SeekStart) + require.NoError(t, err) + subject, err = storage.OpenReadableWritable(writer, roots, carv2.UseWholeCIDs(true), carv2.WriteAsCarV1(true)) + require.NoError(t, err) + } + require.NoError(t, subject.Put(ctx, b.cid.KeyString(), b.data)) + + // With 10% chance test read operations on an resumed read-write StorageCar. + // We don't test on every put to reduce test runtime. + testRead := rng.Float32() <= 0.1 + if testRead { + // Assert read operations on the read-write StorageCar are as expected when resumed from an + // existing file + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + t.Cleanup(cancel) + for k, wantBlock := range wantBlocks { + has, err := subject.Has(ctx, k.KeyString()) require.NoError(t, err) - if !b.Cid().Equals(c) { - t.Fatal("wrong item returned") - } + require.True(t, has) + gotBlock, err := subject.Get(ctx, k.KeyString()) + require.NoError(t, err) + require.Equal(t, wantBlock.data, gotBlock) } + // Assert the number of blocks in file are as expected calculated via AllKeysChan + require.Equal(t, wantBlockCountSoFar, len(wantBlocks)) + } + } - comparePath := filepath.Join("../testdata/", variant.compareCarV1) - compareStat, err := os.Stat(comparePath) - require.NoError(t, err) + // Finalize the StorageCar to complete partially written CARv2 file. + subject, err = storage.OpenReadableWritable(writer, roots, carv2.UseWholeCIDs(true), carv2.WriteAsCarV1(true)) + require.NoError(t, err) + require.NoError(t, subject.Finalize()) - wrote, err := os.Open(path) - require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, wrote.Close()) }) - _, err = wrote.Seek(variant.expectedV1StartOffset, io.SeekStart) - require.NoError(t, err) - hasher := sha512.New() - gotWritten, err := io.Copy(hasher, io.LimitReader(wrote, compareStat.Size())) - require.NoError(t, err) - gotSum := hasher.Sum(nil) + // Assert resumed from file is a valid CARv2 with index. + v2f, err := os.Open(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, v2f.Close()) }) + v2r, err := carv2.NewReader(v2f) + require.NoError(t, err) + require.False(t, v2r.Header.HasIndex()) + require.Equal(t, uint64(1), v2r.Version) - hasher.Reset() - compareV1, err := os.Open(comparePath) - require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, compareV1.Close()) }) - wantWritten, err := io.Copy(hasher, compareV1) - require.NoError(t, err) - wantSum := hasher.Sum(nil) + _, err = v1f.Seek(0, io.SeekStart) + require.NoError(t, err) + wantPayloadReader, err := carv1.NewCarReader(v1f) + require.NoError(t, err) - require.Equal(t, wantWritten, gotWritten) - require.Equal(t, wantSum, gotSum) - }) + dr, err := v2r.DataReader() // since this is a v1 we're just reading from the top with this + require.NoError(t, err) + gotPayloadReader, err := carv1.NewCarReader(dr) + require.NoError(t, err) + + require.Equal(t, wantPayloadReader.Header, gotPayloadReader.Header) + for { + wantNextBlock, wantErr := wantPayloadReader.Next() + if wantErr == io.EOF { + gotNextBlock, gotErr := gotPayloadReader.Next() + require.Equal(t, wantErr, gotErr) + require.Nil(t, gotNextBlock) + break + } + require.NoError(t, wantErr) + + dmh, err := multihash.Decode(wantNextBlock.Cid().Hash()) + require.NoError(t, err) + if dmh.Code == multihash.IDENTITY { + continue + } + + gotNextBlock, gotErr := gotPayloadReader.Next() + require.NoError(t, gotErr) + require.Equal(t, wantNextBlock, gotNextBlock) } } +func TestResumptionIsSupportedOnFinalizedFile(t *testing.T) { + path := filepath.Join(t.TempDir(), "readwrite-resume-finalized.car") + v2f, err := os.Create(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, v2f.Close()) }) + // Create an incomplete CARv2 file with no blocks put. + subject, err := storage.NewReadableWritable(v2f, []cid.Cid{}) + require.NoError(t, err) + require.NoError(t, subject.Finalize()) + + reopen, err := os.OpenFile(path, os.O_RDWR, 0o666) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, reopen.Close()) }) + subject, err = storage.NewReadableWritable(reopen, []cid.Cid{}) + require.NoError(t, err) + t.Cleanup(func() { subject.Finalize() }) +} + +func TestReadWriteErrorsOnlyWhenFinalized(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + testCid1, testData1 := randBlock() + testCid2, testData2 := randBlock() + + wantRoots := []cid.Cid{testCid1, testCid2} + path := filepath.Join(t.TempDir(), "readwrite-finalized-panic.car") + writer, err := os.Create(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, writer.Close()) }) + + subject, err := storage.NewReadableWritable(writer, wantRoots) + require.NoError(t, err) + + require.NoError(t, subject.Put(ctx, testCid1.KeyString(), testData1)) + require.NoError(t, subject.Put(ctx, testCid2.KeyString(), testData2)) + + gotBlock, err := subject.Get(ctx, testCid1.KeyString()) + require.NoError(t, err) + require.Equal(t, testData1, gotBlock) + + gotRoots := subject.Roots() + require.Equal(t, wantRoots, gotRoots) + + has, err := subject.Has(ctx, testCid1.KeyString()) + require.NoError(t, err) + require.True(t, has) + + require.NoError(t, subject.Finalize()) + require.Error(t, subject.Finalize()) + + _, ok := (interface{})(subject).(io.Closer) + require.False(t, ok) + + _, err = subject.Get(ctx, testCid1.KeyString()) + require.Error(t, err) + require.Error(t, err) + _, err = subject.Has(ctx, testCid2.KeyString()) + require.Error(t, err) + + require.Error(t, subject.Put(ctx, testCid1.KeyString(), testData1)) +} + +func TestReadWriteResumptionMismatchingRootsIsError(t *testing.T) { + tmpPath := requireTmpCopy(t, "../testdata/sample-wrapped-v2.car") + + origContent, err := os.ReadFile(tmpPath) + require.NoError(t, err) + + badRoot := randCid() + writer, err := os.OpenFile(tmpPath, os.O_RDWR, 0o666) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, writer.Close()) }) + subject, err := storage.OpenReadableWritable(writer, []cid.Cid{badRoot}) + require.EqualError(t, err, "cannot resume on file with mismatching data header") + require.Nil(t, subject) + + newContent, err := os.ReadFile(tmpPath) + require.NoError(t, err) + + // Expect the bad file to be left untouched; check the size first. + // If the sizes mismatch, printing a huge diff would not help us. + require.Equal(t, len(origContent), len(newContent)) + require.Equal(t, origContent, newContent) +} + +func requireTmpCopy(t *testing.T, src string) string { + srcF, err := os.Open(src) + require.NoError(t, err) + defer func() { require.NoError(t, srcF.Close()) }() + stats, err := srcF.Stat() + require.NoError(t, err) + + dst := filepath.Join(t.TempDir(), stats.Name()) + dstF, err := os.Create(dst) + require.NoError(t, err) + defer func() { require.NoError(t, dstF.Close()) }() + + _, err = io.Copy(dstF, srcF) + require.NoError(t, err) + return dst +} + +func TestReadWriteResumptionFromFileWithDifferentCarV1PaddingIsError(t *testing.T) { + testCid1, testData1 := randBlock() + wantRoots := []cid.Cid{testCid1} + path := filepath.Join(t.TempDir(), "readwrite-resume-with-padding.car") + writer, err := os.Create(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, writer.Close()) }) + subject, err := storage.NewReadableWritable( + writer, + wantRoots, + carv2.UseDataPadding(1413)) + require.NoError(t, err) + require.NoError(t, subject.Put(context.TODO(), testCid1.KeyString(), testData1)) + require.NoError(t, subject.Finalize()) + + subject, err = storage.OpenReadableWritable( + writer, + wantRoots, + carv2.UseDataPadding(1314)) + require.EqualError(t, err, "cannot resume from file with mismatched CARv1 offset; "+ + "`WithDataPadding` option must match the padding on file. "+ + "Expected padding value of 1413 but got 1314") + require.Nil(t, subject) +} + +func TestOperationsErrorWithBadCidStrings(t *testing.T) { + testCid, testData := randBlock() + path := filepath.Join(t.TempDir(), "badkeys.car") + writer, err := os.Create(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, writer.Close()) }) + subject, err := storage.NewReadableWritable(writer, []cid.Cid{}) + require.NoError(t, err) + + require.NoError(t, subject.Put(context.TODO(), testCid.KeyString(), testData)) + require.ErrorContains(t, subject.Put(context.TODO(), fmt.Sprintf("%s/nope", testCid.KeyString()), testData), "bad CID key") + require.ErrorContains(t, subject.Put(context.TODO(), "nope", testData), "bad CID key") + + has, err := subject.Has(context.TODO(), testCid.KeyString()) + require.NoError(t, err) + require.True(t, has) + has, err = subject.Has(context.TODO(), fmt.Sprintf("%s/nope", testCid.KeyString())) + require.ErrorContains(t, err, "bad CID key") + require.False(t, has) + has, err = subject.Has(context.TODO(), "nope") + require.ErrorContains(t, err, "bad CID key") + require.False(t, has) + + got, err := subject.Get(context.TODO(), testCid.KeyString()) + require.NoError(t, err) + require.NotNil(t, got) + got, err = subject.Get(context.TODO(), fmt.Sprintf("%s/nope", testCid.KeyString())) + require.ErrorContains(t, err, "bad CID key") + require.Nil(t, got) + got, err = subject.Get(context.TODO(), "nope") + require.ErrorContains(t, err, "bad CID key") + require.Nil(t, got) +} + type writerOnly struct { io.Writer } @@ -322,16 +1175,46 @@ func (w *writerAtOnly) Write(p []byte) (n int, err error) { return w.File.Write(p) } +func randBlock() (cid.Cid, []byte) { + data := make([]byte, 1024) + rngLk.Lock() + rng.Read(data) + rngLk.Unlock() + h, err := mh.Sum(data, mh.SHA2_512, -1) + if err != nil { + panic(err) + } + return cid.NewCidV1(cid.Raw, h), data +} + func randCid() cid.Cid { b := make([]byte, 32) + rngLk.Lock() rng.Read(b) + rngLk.Unlock() mh, _ := multihash.Encode(b, multihash.SHA2_256) return cid.NewCidV1(cid.DagProtobuf, mh) } func randIdentityCid() cid.Cid { b := make([]byte, 32) + rngLk.Lock() rng.Read(b) + rngLk.Unlock() mh, _ := multihash.Encode(b, multihash.IDENTITY) return cid.NewCidV1(cid.Raw, mh) } + +type bufferReaderAt []byte + +func (b bufferReaderAt) ReadAt(p []byte, off int64) (int, error) { + if off >= int64(len(b)) { + return 0, io.EOF + } + return copy(p, b[off:]), nil +} + +type simpleBlock struct { + cid cid.Cid + data []byte +} From cd86ec8a55071af637ba9269a5ce4a283af7bdc9 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 6 Feb 2023 15:56:22 +1100 Subject: [PATCH 3776/3817] feat: docs for StorageCar interfaces This commit was moved from ipld/go-car@3b0851e9c3cb4463865d8fdb31acb3a101e95040 --- ipld/car/v2/internal/store/resume.go | 1 - ipld/car/v2/options.go | 4 +- ipld/car/v2/storage/doc.go | 73 +++++++++++++++++++++++ ipld/car/v2/storage/storage.go | 86 +++++++++++++++++++++++++--- ipld/car/v2/storage/storage_test.go | 8 +-- 5 files changed, 157 insertions(+), 15 deletions(-) create mode 100644 ipld/car/v2/storage/doc.go diff --git a/ipld/car/v2/internal/store/resume.go b/ipld/car/v2/internal/store/resume.go index 0167b4154..a1f97de0f 100644 --- a/ipld/car/v2/internal/store/resume.go +++ b/ipld/car/v2/internal/store/resume.go @@ -15,7 +15,6 @@ import ( type ReaderWriterAt interface { io.ReaderAt - io.Writer io.WriterAt } diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go index 92a5d11d9..dbde3f3e1 100644 --- a/ipld/car/v2/options.go +++ b/ipld/car/v2/options.go @@ -189,8 +189,8 @@ func MaxAllowedSectionSize(max uint64) Option { // // Enabling this option affects a number of methods, including read-only ones: // -// - Get, Has, and HasSize will only return a block only if the entire CID is -// present in the CAR file. +// • Get, Has, and HasSize will only return a block only if the entire CID is +// present in the CAR file. // // • AllKeysChan will return the original whole CIDs, instead of with their // multicodec set to "raw" to just provide multihashes. diff --git a/ipld/car/v2/storage/doc.go b/ipld/car/v2/storage/doc.go new file mode 100644 index 000000000..43ddb7578 --- /dev/null +++ b/ipld/car/v2/storage/doc.go @@ -0,0 +1,73 @@ +// package storage provides a CAR abstraction for the +// github.com/ipld/go-ipld-prime/storage interfaces in the form of a StorageCar. +// +// StorageCar as ReadableStorage provides basic Get and Has operations. It also +// implements StreamingReadableStorage for the more efficient GetStreaming +// operation which is easily supported by the CAR format. +// +// StorageCar as WritableStorage provides the Put operation. It does not +// implement StreamingWritableStorage because the CAR format requires CIDs to +// be written before the blocks themselves, which is not possible with +// StreamingWritableStorage without buffering. Therefore, the PutStream function +// in github.com/ipld/go-ipld-prime/storage will provide equivalent +// functionality if it were to be implemented here. +// +// StorageCar can be used with an IPLD LinkSystem, defined by +// github.com/ipld/go-ipld-prime/linking, with the +// linking.SetReadStorage and linking.SetWriteStorage functions, to provide +// read and/or write to and/or from a CAR format as required. +// +// The focus of the StorageCar interfaces is to use the minimal possible IO +// interface for the operation(s) being performed. +// +// • OpenReadable requires an io.ReaderAt as seeking is required for +// random-access reads as a ReadableStore. +// +// • NewWritable requires an io.Writer when used to write a CARv1 as this format +// can be written in a continuous stream as blocks are written through a +// WritableStore (i.e. when the WriteAsCarV1 option is turned on). When used to +// write a CARv2, the default mode, a random-access io.WriterAt is required as +// the CARv2 header must be written after the payload is finalized and index +// written in order to indicate payload location in the output. The plain +// Writable store may be used to stream CARv1 contents without buffering; +// only storing CIDs in memory for de-duplication (where required) and to still +// allow Has operations. +// +// • NewReadableWritable requires an io.ReaderAt and an io.Writer as it combines +// the functionality of a NewWritable with OpenReadable, being able to random- +// access read any written blocks. +// +// • OpenReadableWritable requires an io.ReaderAt, an io.Writer and an +// io.WriterAt as it extends the NewReadableWritable functionality with the +// ability to resume an existing CAR. In addition, if the CAR being resumed is +// a CARv2, the IO object being provided must have a Truncate() method (e.g. +// an io.File) in order to properly manage CAR lifecycle and avoid writing a +// corrupt CAR. +// +// The following options are available to customize the behavior of the +// StorageCar: +// +// • WriteAsCarV1 +// +// • StoreIdentityCIDs +// +// • AllowDuplicatePuts +// +// • UseWholeCIDs +// +// • ZeroLengthSectionAsEOF +// +// • UseIndexCodec +// +// • UseDataPadding +// +// • UseIndexPadding +// +// • MaxIndexCidSize +// +// • MaxAllowedHeaderSize +// +// • MaxAllowedSectionSize +// + +package storage diff --git a/ipld/car/v2/storage/storage.go b/ipld/car/v2/storage/storage.go index 6bbed612c..9e765865b 100644 --- a/ipld/car/v2/storage/storage.go +++ b/ipld/car/v2/storage/storage.go @@ -21,7 +21,7 @@ import ( var errClosed = errors.New("cannot use a CARv2 storage after closing") -type ReaderWriterAt interface { +type ReaderAtWriterAt interface { io.ReaderAt io.Writer io.WriterAt @@ -44,10 +44,8 @@ type WritableCar interface { Finalize() error } -var _ ipldstorage.ReadableStorage = (*StorageCar)(nil) -var _ ipldstorage.StreamingReadableStorage = (*StorageCar)(nil) var _ ReadableCar = (*StorageCar)(nil) -var _ ipldstorage.WritableStorage = (*StorageCar)(nil) +var _ WritableCar = (*StorageCar)(nil) type StorageCar struct { idx index.Index @@ -67,7 +65,23 @@ type positionedWriter interface { Position() int64 } -func NewReadable(reader io.ReaderAt, opts ...carv2.Option) (ReadableCar, error) { +// OpenReadable opens a CARv1 or CARv2 file for reading as a ReadableStorage +// and StreamingReadableStorage as defined by +// github.com/ipld/go-ipld-prime/storage. +// +// The returned ReadableStorage is compatible with a linksystem SetReadStorage +// method as defined by github.com/ipld/go-ipld-prime/linking +// to provide a block source backed by a CAR. +// +// When opening a CAR, an initial scan is performed to generate an index, or +// load an index from a CARv2 index where available. This index data is kept in +// memory while the CAR is being used in order to provide efficient random +// Get access to blocks and Has operations. +// +// The Readable supports StreamingReadableStorage, which allows for efficient +// GetStreaming operations straight out of the underlying CAR where the +// linksystem can make use of it. +func OpenReadable(reader io.ReaderAt, opts ...carv2.Option) (ReadableCar, error) { sc := &StorageCar{opts: carv2.ApplyOptions(opts...)} rr := internalio.ToReadSeeker(reader) @@ -122,6 +136,29 @@ func NewReadable(reader io.ReaderAt, opts ...carv2.Option) (ReadableCar, error) return sc, nil } +// NewWritable creates a new WritableStorage as defined by +// github.com/ipld/go-ipld-prime/storage that writes a CARv1 or CARv2 format to +// the given io.Writer. +// +// The returned WritableStorage is compatible with a linksystem SetWriteStorage +// method as defined by github.com/ipld/go-ipld-prime/linking +// to provide a block sink backed by a CAR. +// +// The WritableStorage supports Put operations, which will write +// blocks to the CAR in the order they are received. +// +// When writing a CARv2 format (the default), the provided writer must be +// compatible with io.WriterAt in order to provide random access as the CARv2 +// header must be written after the blocks in order to indicate the size of the +// CARv2 data payload. +// +// A CARv1 (generated using the WriteAsCarV1 option) only requires an io.Writer +// and can therefore stream CAR contents as blocks are written while still +// providing Has operations and the ability to avoid writing duplicate blocks +// as required. +// +// When writing a CARv2 format, it is important to call the Finalize method on +// the returned WritableStorage in order to write the CARv2 header and index. func NewWritable(writer io.Writer, roots []cid.Cid, opts ...carv2.Option) (WritableCar, error) { sc, err := newWritable(writer, roots, opts...) if err != nil { @@ -162,7 +199,7 @@ func newWritable(writer io.Writer, roots []cid.Cid, opts ...carv2.Option) (*Stor return sc, nil } -func newReadableWritable(rw ReaderWriterAt, roots []cid.Cid, opts ...carv2.Option) (*StorageCar, error) { +func newReadableWritable(rw ReaderAtWriterAt, roots []cid.Cid, opts ...carv2.Option) (*StorageCar, error) { sc, err := newWritable(rw, roots, opts...) if err != nil { return nil, err @@ -179,7 +216,15 @@ func newReadableWritable(rw ReaderWriterAt, roots []cid.Cid, opts ...carv2.Optio return sc, nil } -func NewReadableWritable(rw ReaderWriterAt, roots []cid.Cid, opts ...carv2.Option) (*StorageCar, error) { +// NewReadableWritable creates a new StorageCar that is able to provide both +// StorageReader and StorageWriter functionality. +// +// The returned StorageCar is compatible with a linksystem SetReadStorage and +// SetWriteStorage methods as defined by github.com/ipld/go-ipld-prime/linking. +// +// When writing a CARv2 format, it is important to call the Finalize method on +// the returned WritableStorage in order to write the CARv2 header and index. +func NewReadableWritable(rw ReaderAtWriterAt, roots []cid.Cid, opts ...carv2.Option) (*StorageCar, error) { sc, err := newReadableWritable(rw, roots, opts...) if err != nil { return nil, err @@ -190,7 +235,15 @@ func NewReadableWritable(rw ReaderWriterAt, roots []cid.Cid, opts ...carv2.Optio return sc, nil } -func OpenReadableWritable(rw ReaderWriterAt, roots []cid.Cid, opts ...carv2.Option) (*StorageCar, error) { +// OpenReadableWritable creates a new StorageCar that is able to provide both +// StorageReader and StorageWriter functionality. +// +// The returned StorageCar is compatible with a linksystem SetReadStorage and +// SetWriteStorage methods as defined by github.com/ipld/go-ipld-prime/linking. +// +// It attempts to resume a CARv2 file that was previously written to by +// NewWritable, or NewReadableWritable. +func OpenReadableWritable(rw ReaderAtWriterAt, roots []cid.Cid, opts ...carv2.Option) (*StorageCar, error) { sc, err := newReadableWritable(rw, roots, opts...) if err != nil { return nil, err @@ -236,10 +289,14 @@ func (sc *StorageCar) init() (WritableCar, error) { return sc, nil } +// Roots returns the roots of the CAR. func (sc *StorageCar) Roots() []cid.Cid { return sc.roots } +// Put adds a block to the CAR, where the block is identified by the given CID +// provided in string form. The keyStr value must be a valid CID binary string +// (not a multibase string representation), i.e. generated with CID#KeyString(). func (sc *StorageCar) Put(ctx context.Context, keyStr string, data []byte) error { keyCid, err := cid.Cast([]byte(keyStr)) if err != nil { @@ -284,6 +341,9 @@ func (sc *StorageCar) Put(ctx context.Context, keyStr string, data []byte) error return nil } +// Has returns true if the CAR contains a block identified by the given CID +// provided in string form. The keyStr value must be a valid CID binary string +// (not a multibase string representation), i.e. generated with CID#KeyString(). func (sc *StorageCar) Has(ctx context.Context, keyStr string) (bool, error) { keyCid, err := cid.Cast([]byte(keyStr)) if err != nil { @@ -325,6 +385,9 @@ func (sc *StorageCar) Has(ctx context.Context, keyStr string) (bool, error) { return size > -1, nil } +// Get returns the block bytes identified by the given CID provided in string +// form. The keyStr value must be a valid CID binary string (not a multibase +// string representation), i.e. generated with CID#KeyString(). func (sc *StorageCar) Get(ctx context.Context, keyStr string) ([]byte, error) { rdr, err := sc.GetStream(ctx, keyStr) if err != nil { @@ -333,6 +396,9 @@ func (sc *StorageCar) Get(ctx context.Context, keyStr string) ([]byte, error) { return io.ReadAll(rdr) } +// GetStream returns a stream of the block bytes identified by the given CID +// provided in string form. The keyStr value must be a valid CID binary string +// (not a multibase string representation), i.e. generated with CID#KeyString(). func (sc *StorageCar) GetStream(ctx context.Context, keyStr string) (io.ReadCloser, error) { if sc.reader == nil { return nil, fmt.Errorf("cannot read from a write-only CAR") @@ -378,6 +444,10 @@ func (sc *StorageCar) GetStream(ctx context.Context, keyStr string) (io.ReadClos return io.NopCloser(io.NewSectionReader(sc.reader, offset, int64(size))), nil } +// Finalize writes the CAR index to the underlying writer if the CAR being +// written is a CARv2. It also writes a finalized CARv2 header which details +// payload location. This should be called on a writable StorageCar in order to +// avoid data loss. func (sc *StorageCar) Finalize() error { idx, ok := sc.idx.(*insertionindex.InsertionIndex) if !ok || sc.writer == nil { diff --git a/ipld/car/v2/storage/storage_test.go b/ipld/car/v2/storage/storage_test.go index ba1d01878..928157e3b 100644 --- a/ipld/car/v2/storage/storage_test.go +++ b/ipld/car/v2/storage/storage_test.go @@ -91,7 +91,7 @@ func TestReadable(t *testing.T) { inputReader, err := os.Open(tt.inputPath) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, inputReader.Close()) }) - readable, err := storage.NewReadable(inputReader, tt.opts...) + readable, err := storage.OpenReadable(inputReader, tt.opts...) require.NoError(t, err) // Setup BlockReader to compare against @@ -176,7 +176,7 @@ func TestReadableBadVersion(t *testing.T) { f, err := os.Open("../testdata/sample-rootless-v42.car") require.NoError(t, err) t.Cleanup(func() { f.Close() }) - subject, err := storage.NewReadable(f) + subject, err := storage.OpenReadable(f) require.Errorf(t, err, "unsupported car version: 42") require.Nil(t, subject) } @@ -436,7 +436,7 @@ func TestNullPadding(t *testing.T) { paddedV1, err := os.ReadFile("../testdata/sample-v1-with-zero-len-section.car") require.NoError(t, err) - readable, err := storage.NewReadable(bufferReaderAt(paddedV1), carv2.ZeroLengthSectionAsEOF(true)) + readable, err := storage.OpenReadable(bufferReaderAt(paddedV1), carv2.ZeroLengthSectionAsEOF(true)) require.NoError(t, err) roots := readable.Roots() @@ -590,7 +590,7 @@ func TestReadableCantWrite(t *testing.T) { inp, err := os.Open("../testdata/sample-v1.car") require.NoError(t, err) t.Cleanup(func() { require.NoError(t, inp.Close()) }) - readable, err := storage.NewReadable(inp) + readable, err := storage.OpenReadable(inp) require.NoError(t, err) require.ErrorContains(t, readable.(*storage.StorageCar).Put(context.Background(), randCid().KeyString(), []byte("bar")), "read-only") // Finalize() is nonsense for a readable, but it should be safe From bdfb88dc883913fc5f6a3744cf3e4a8754e6c248 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 6 Feb 2023 22:02:01 +1100 Subject: [PATCH 3777/3817] fix: minor lint & windows fd test problems This commit was moved from ipld/go-car@676dd5f08af665789b30851a18aa02b81fef132a --- ipld/car/v2/blockstore/readwrite.go | 7 ++++--- ipld/car/v2/storage/storage_test.go | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 032565b15..85c9efd5b 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -129,7 +129,8 @@ func OpenReadWriteFile(f *os.File, roots []cid.Cid, opts ...carv2.Option) (*Read offset = 0 } rwbs.dataWriter = internalio.NewOffsetWriter(rwbs.f, offset) - v1r, err := internalio.NewOffsetReadSeeker(rwbs.f, offset) + var v1r internalio.ReadSeekerAt + v1r, err = internalio.NewOffsetReadSeeker(rwbs.f, offset) if err != nil { return nil, err } @@ -137,10 +138,10 @@ func OpenReadWriteFile(f *os.File, roots []cid.Cid, opts ...carv2.Option) (*Read rwbs.ronly.idx = rwbs.idx if resume { - if err := store.ResumableVersion(f, rwbs.opts.WriteAsCarV1); err != nil { + if err = store.ResumableVersion(f, rwbs.opts.WriteAsCarV1); err != nil { return nil, err } - if err := store.Resume( + if err = store.Resume( f, rwbs.ronly.backing, rwbs.dataWriter, diff --git a/ipld/car/v2/storage/storage_test.go b/ipld/car/v2/storage/storage_test.go index 928157e3b..ea00f1d5e 100644 --- a/ipld/car/v2/storage/storage_test.go +++ b/ipld/car/v2/storage/storage_test.go @@ -23,7 +23,6 @@ import ( "github.com/ipld/go-car/v2/storage" "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" - mh "github.com/multiformats/go-multihash" "github.com/stretchr/testify/require" ) @@ -379,7 +378,7 @@ func TestErrorsWhenWritingCidTooLarge(t *testing.T) { // normal block but shorten the CID to make it acceptable testCid, testData := randBlock() - mh, err := mh.Decode(testCid.Hash()) + mh, err := multihash.Decode(testCid.Hash()) require.NoError(t, err) dig := mh.Digest[:10] shortMh, err := multihash.Encode(dig, mh.Code) @@ -526,6 +525,7 @@ func TestPutSameHashes(t *testing.T) { countBlocks := func(path string) int { f, err := os.Open(path) require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, f.Close()) }) rdr, err := carv2.NewBlockReader(f) require.NoError(t, err) @@ -1065,10 +1065,11 @@ func TestReadWriteResumptionMismatchingRootsIsError(t *testing.T) { badRoot := randCid() writer, err := os.OpenFile(tmpPath, os.O_RDWR, 0o666) require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, writer.Close()) }) + t.Cleanup(func() { writer.Close() }) subject, err := storage.OpenReadableWritable(writer, []cid.Cid{badRoot}) require.EqualError(t, err, "cannot resume on file with mismatching data header") require.Nil(t, subject) + require.NoError(t, writer.Close()) newContent, err := os.ReadFile(tmpPath) require.NoError(t, err) @@ -1180,7 +1181,7 @@ func randBlock() (cid.Cid, []byte) { rngLk.Lock() rng.Read(data) rngLk.Unlock() - h, err := mh.Sum(data, mh.SHA2_512, -1) + h, err := multihash.Sum(data, multihash.SHA2_512, -1) if err != nil { panic(err) } From 2fdb14e01e17887ddb6934bf89021bd311d15536 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 6 Feb 2023 22:03:44 +1100 Subject: [PATCH 3778/3817] chore: add experimental note This commit was moved from ipld/go-car@9bfbf3ecac68000a093648c5bba6b86c8057b344 --- ipld/car/v2/storage/doc.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ipld/car/v2/storage/doc.go b/ipld/car/v2/storage/doc.go index 43ddb7578..eed6a1460 100644 --- a/ipld/car/v2/storage/doc.go +++ b/ipld/car/v2/storage/doc.go @@ -1,6 +1,10 @@ // package storage provides a CAR abstraction for the // github.com/ipld/go-ipld-prime/storage interfaces in the form of a StorageCar. // +// THIS PACKAGE IS EXPERIMENTAL. Breaking changes may be introduced in +// semver-minor releases before this package stabilizes. Use with caution and +// prefer the blockstore API if stability is required. +// // StorageCar as ReadableStorage provides basic Get and Has operations. It also // implements StreamingReadableStorage for the more efficient GetStreaming // operation which is easily supported by the CAR format. From 1ad58eada38897e8b544dd29bba347e20a1c5468 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 6 Feb 2023 22:32:23 +1100 Subject: [PATCH 3779/3817] chore: move insertionindex into store pkg This commit was moved from ipld/go-car@2afb69abaf72926034e8428aab5725cfe1274cb6 --- ipld/car/v2/blockstore/readwrite.go | 5 ++--- ipld/car/v2/internal/store/index.go | 3 +-- .../{insertionindex => store}/insertionindex.go | 2 +- ipld/car/v2/internal/store/put.go | 3 +-- ipld/car/v2/internal/store/resume.go | 3 +-- ipld/car/v2/storage/storage.go | 13 ++++++------- 6 files changed, 12 insertions(+), 17 deletions(-) rename ipld/car/v2/internal/{insertionindex => store}/insertionindex.go (99%) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 85c9efd5b..3f5f7841b 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -12,7 +12,6 @@ import ( carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/carv1/util" - "github.com/ipld/go-car/v2/internal/insertionindex" internalio "github.com/ipld/go-car/v2/internal/io" "github.com/ipld/go-car/v2/internal/store" ) @@ -33,7 +32,7 @@ type ReadWrite struct { f *os.File dataWriter *internalio.OffsetWriteSeeker - idx *insertionindex.InsertionIndex + idx *store.InsertionIndex header carv2.Header opts carv2.Options @@ -111,7 +110,7 @@ func OpenReadWriteFile(f *os.File, roots []cid.Cid, opts ...carv2.Option) (*Read // Set the header fileld before applying options since padding options may modify header. rwbs := &ReadWrite{ f: f, - idx: insertionindex.NewInsertionIndex(), + idx: store.NewInsertionIndex(), header: carv2.NewHeader(0), opts: carv2.ApplyOptions(opts...), } diff --git a/ipld/car/v2/internal/store/index.go b/ipld/car/v2/internal/store/index.go index c7d188600..62c9d2a02 100644 --- a/ipld/car/v2/internal/store/index.go +++ b/ipld/car/v2/internal/store/index.go @@ -8,7 +8,6 @@ import ( carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1/util" - "github.com/ipld/go-car/v2/internal/insertionindex" internalio "github.com/ipld/go-car/v2/internal/io" "github.com/multiformats/go-multicodec" "github.com/multiformats/go-varint" @@ -89,7 +88,7 @@ func FindCid( // Finalize will write the index to the writer at the offset specified in the header. It should only // be used for a CARv2 and when the CAR interface is being closed. -func Finalize(writer io.WriterAt, header carv2.Header, idx *insertionindex.InsertionIndex, dataSize uint64, storeIdentityCIDs bool, indexCodec multicodec.Code) error { +func Finalize(writer io.WriterAt, header carv2.Header, idx *InsertionIndex, dataSize uint64, storeIdentityCIDs bool, indexCodec multicodec.Code) error { // TODO check if add index option is set and don't write the index then set index offset to zero. header = header.WithDataSize(dataSize) header.Characteristics.SetFullyIndexed(storeIdentityCIDs) diff --git a/ipld/car/v2/internal/insertionindex/insertionindex.go b/ipld/car/v2/internal/store/insertionindex.go similarity index 99% rename from ipld/car/v2/internal/insertionindex/insertionindex.go rename to ipld/car/v2/internal/store/insertionindex.go index 7aac338ec..f52fb3f2e 100644 --- a/ipld/car/v2/internal/insertionindex/insertionindex.go +++ b/ipld/car/v2/internal/store/insertionindex.go @@ -1,4 +1,4 @@ -package insertionindex +package store import ( "bytes" diff --git a/ipld/car/v2/internal/store/put.go b/ipld/car/v2/internal/store/put.go index c9c1867ff..f82d0c1ea 100644 --- a/ipld/car/v2/internal/store/put.go +++ b/ipld/car/v2/internal/store/put.go @@ -3,7 +3,6 @@ package store import ( "github.com/ipfs/go-cid" carv2 "github.com/ipld/go-car/v2" - "github.com/ipld/go-car/v2/internal/insertionindex" ) // ShouldPut returns true if the block should be put into the CAR according to the options provided @@ -11,7 +10,7 @@ import ( // is an identity block and StoreIdentityCIDs is false, or because it already exists and // BlockstoreAllowDuplicatePuts is false. func ShouldPut( - idx *insertionindex.InsertionIndex, + idx *InsertionIndex, c cid.Cid, maxIndexCidSize uint64, storeIdentityCIDs bool, diff --git a/ipld/car/v2/internal/store/resume.go b/ipld/car/v2/internal/store/resume.go index a1f97de0f..ff4722373 100644 --- a/ipld/car/v2/internal/store/resume.go +++ b/ipld/car/v2/internal/store/resume.go @@ -8,7 +8,6 @@ import ( "github.com/ipfs/go-cid" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/internal/carv1" - "github.com/ipld/go-car/v2/internal/insertionindex" internalio "github.com/ipld/go-car/v2/internal/io" "github.com/multiformats/go-varint" ) @@ -52,7 +51,7 @@ func Resume( rw ReaderWriterAt, dataReader io.ReaderAt, dataWriter *internalio.OffsetWriteSeeker, - idx *insertionindex.InsertionIndex, + idx *InsertionIndex, roots []cid.Cid, dataOffset uint64, v1 bool, diff --git a/ipld/car/v2/storage/storage.go b/ipld/car/v2/storage/storage.go index 9e765865b..a6f92ae22 100644 --- a/ipld/car/v2/storage/storage.go +++ b/ipld/car/v2/storage/storage.go @@ -13,7 +13,6 @@ import ( "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/carv1/util" - "github.com/ipld/go-car/v2/internal/insertionindex" internalio "github.com/ipld/go-car/v2/internal/io" "github.com/ipld/go-car/v2/internal/store" ipldstorage "github.com/ipld/go-ipld-prime/storage" @@ -94,7 +93,7 @@ func OpenReadable(reader io.ReaderAt, opts ...carv2.Option) (ReadableCar, error) sc.roots = header.Roots sc.reader = reader rr.Seek(0, io.SeekStart) - sc.idx = insertionindex.NewInsertionIndex() + sc.idx = store.NewInsertionIndex() if err := carv2.LoadIndex(sc.idx, rr, opts...); err != nil { return nil, err } @@ -121,7 +120,7 @@ func OpenReadable(reader io.ReaderAt, opts ...carv2.Option) (ReadableCar, error) if err != nil { return nil, err } - sc.idx = insertionindex.NewInsertionIndex() + sc.idx = store.NewInsertionIndex() if err := carv2.LoadIndex(sc.idx, dr, opts...); err != nil { return nil, err } @@ -170,7 +169,7 @@ func NewWritable(writer io.Writer, roots []cid.Cid, opts ...carv2.Option) (Writa func newWritable(writer io.Writer, roots []cid.Cid, opts ...carv2.Option) (*StorageCar, error) { sc := &StorageCar{ writer: &positionTrackingWriter{w: writer}, - idx: insertionindex.NewInsertionIndex(), + idx: store.NewInsertionIndex(), header: carv2.NewHeader(0), opts: carv2.ApplyOptions(opts...), roots: roots, @@ -261,7 +260,7 @@ func OpenReadableWritable(rw ReaderAtWriterAt, roots []cid.Cid, opts ...carv2.Op rw, sc.reader, sc.dataWriter, - sc.idx.(*insertionindex.InsertionIndex), + sc.idx.(*store.InsertionIndex), roots, sc.header.DataOffset, sc.opts.WriteAsCarV1, @@ -310,7 +309,7 @@ func (sc *StorageCar) Put(ctx context.Context, keyStr string, data []byte) error return errClosed } - idx, ok := sc.idx.(*insertionindex.InsertionIndex) + idx, ok := sc.idx.(*store.InsertionIndex) if !ok || sc.writer == nil { return fmt.Errorf("cannot put into a read-only CAR") } @@ -449,7 +448,7 @@ func (sc *StorageCar) GetStream(ctx context.Context, keyStr string) (io.ReadClos // payload location. This should be called on a writable StorageCar in order to // avoid data loss. func (sc *StorageCar) Finalize() error { - idx, ok := sc.idx.(*insertionindex.InsertionIndex) + idx, ok := sc.idx.(*store.InsertionIndex) if !ok || sc.writer == nil { // ignore this, it's not writable return nil From 38af65947ac6300bd4da7fdb3f75bbd04868293c Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Wed, 8 Feb 2023 12:54:53 +1100 Subject: [PATCH 3780/3817] fix: return errors for unsupported operations This commit was moved from ipld/go-car@48ea0794819d9210a3468089c3d29481b63a6750 --- ipld/car/v2/internal/io/converter.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ipld/car/v2/internal/io/converter.go b/ipld/car/v2/internal/io/converter.go index 5550f86f2..2e2844bd0 100644 --- a/ipld/car/v2/internal/io/converter.go +++ b/ipld/car/v2/internal/io/converter.go @@ -1,6 +1,7 @@ package io import ( + "errors" "io" "sync" ) @@ -108,7 +109,7 @@ func (drsb *discardingReadSeekerPlusByte) Seek(offset int64, whence int) (int64, case io.SeekStart: n := offset - drsb.offset if n < 0 { - panic("unsupported rewind via whence: io.SeekStart") + return 0, errors.New("unsupported rewind via whence: io.SeekStart") } _, err := io.CopyN(io.Discard, drsb, n) return drsb.offset, err @@ -116,7 +117,7 @@ func (drsb *discardingReadSeekerPlusByte) Seek(offset int64, whence int) (int64, _, err := io.CopyN(io.Discard, drsb, offset) return drsb.offset, err default: - panic("unsupported whence: io.SeekEnd") + return 0, errors.New("unsupported whence: io.SeekEnd") } } @@ -137,9 +138,9 @@ func (ras *readerAtSeeker) Seek(offset int64, whence int) (int64, error) { case io.SeekCurrent: ras.position += offset case io.SeekEnd: - panic("unsupported whence: io.SeekEnd") + return 0, errors.New("unsupported whence: io.SeekEnd") default: - panic("unsupported whence") + return 0, errors.New("unsupported whence") } return ras.position, nil } From 050974f1ff79c8eee8f83649b6ffcb18c84783a8 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Wed, 8 Feb 2023 13:15:30 +1100 Subject: [PATCH 3781/3817] fix(doc): fix storage package doc formatting This commit was moved from ipld/go-car@317491d313d02b11fe40165c38ec4aba74806309 --- ipld/car/v2/storage/doc.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ipld/car/v2/storage/doc.go b/ipld/car/v2/storage/doc.go index eed6a1460..21e049b21 100644 --- a/ipld/car/v2/storage/doc.go +++ b/ipld/car/v2/storage/doc.go @@ -1,4 +1,4 @@ -// package storage provides a CAR abstraction for the +// Package storage provides a CAR abstraction for the // github.com/ipld/go-ipld-prime/storage interfaces in the form of a StorageCar. // // THIS PACKAGE IS EXPERIMENTAL. Breaking changes may be introduced in @@ -72,6 +72,4 @@ // • MaxAllowedHeaderSize // // • MaxAllowedSectionSize -// - package storage From 61995069b81268f3e5dc8d7cea930087c60406c1 Mon Sep 17 00:00:00 2001 From: web3-bot <81333946+web3-bot@users.noreply.github.com> Date: Wed, 8 Feb 2023 16:03:14 +0100 Subject: [PATCH 3782/3817] sync: update CI config files (#45) This commit was moved from ipfs/go-ipns@72be64e27e743b828fe4eb8a721abd1b97610421 --- ipns/pb/ipns.pb.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ipns/pb/ipns.pb.go b/ipns/pb/ipns.pb.go index 8bcace7fc..8bbb654d2 100644 --- a/ipns/pb/ipns.pb.go +++ b/ipns/pb/ipns.pb.go @@ -5,10 +5,11 @@ package ipns_pb import ( fmt "fmt" - proto "github.com/gogo/protobuf/proto" io "io" math "math" math_bits "math/bits" + + proto "github.com/gogo/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. From 790ce94eb6c76f9f1003c17232eec2539b040202 Mon Sep 17 00:00:00 2001 From: web3-bot <81333946+web3-bot@users.noreply.github.com> Date: Wed, 8 Feb 2023 16:04:00 +0100 Subject: [PATCH 3783/3817] sync: update CI config files (#18) This commit was moved from ipfs/go-verifcid@c45e93b5f91e708e398cf06f3257262f2dc5d56a --- verifcid/validate.go | 1 + 1 file changed, 1 insertion(+) diff --git a/verifcid/validate.go b/verifcid/validate.go index e594629f4..7b27debc9 100644 --- a/verifcid/validate.go +++ b/verifcid/validate.go @@ -2,6 +2,7 @@ package verifcid import ( "fmt" + cid "github.com/ipfs/go-cid" mh "github.com/multiformats/go-multihash" ) From 590286709b8d6ff7d3e3e1221268f9549b3573e1 Mon Sep 17 00:00:00 2001 From: web3-bot Date: Wed, 8 Feb 2023 12:52:47 +0000 Subject: [PATCH 3784/3817] stop using the deprecated io/ioutil package This commit was moved from ipfs/go-merkledag@00ca9d3458d036f07729a2b59ce4b9e50e0eb088 --- ipld/merkledag/pb/merkledagpb_test.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ipld/merkledag/pb/merkledagpb_test.go b/ipld/merkledag/pb/merkledagpb_test.go index 20fe06bd8..f72b306da 100644 --- a/ipld/merkledag/pb/merkledagpb_test.go +++ b/ipld/merkledag/pb/merkledagpb_test.go @@ -5,15 +5,16 @@ package merkledag_pb import ( fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - github_com_gogo_protobuf_jsonpb "github.com/gogo/protobuf/jsonpb" - github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" - proto "github.com/gogo/protobuf/proto" go_parser "go/parser" math "math" math_rand "math/rand" testing "testing" time "time" + + _ "github.com/gogo/protobuf/gogoproto" + github_com_gogo_protobuf_jsonpb "github.com/gogo/protobuf/jsonpb" + github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" + proto "github.com/gogo/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. From 18b8f6f095e0a66abe5187a7788fecb997fec1b3 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 9 Feb 2023 15:21:24 +1100 Subject: [PATCH 3785/3817] fix: switch to crypto/rand.Read This commit was moved from ipfs/go-merkledag@a9e11509794e1439ad208a9bf7dbc95504986456 --- ipld/merkledag/merkledag_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 61fb8141d..d02ae7465 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -3,13 +3,13 @@ package merkledag_test import ( "bytes" "context" + "crypto/rand" "encoding/hex" "encoding/json" "errors" "fmt" "io" "math" - "math/rand" "strings" "sync" "testing" From 48f8c69a903b1e66f8725f6b5ce9741b8e88c5a1 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 8 Feb 2023 08:37:20 +0100 Subject: [PATCH 3786/3817] test: basic routing interface test This commit was moved from ipfs/interface-go-ipfs-core@d069f41be1eea938a4bbaa16dae953eed24bc945 --- coreiface/tests/api.go | 1 + coreiface/tests/routing.go | 92 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 coreiface/tests/routing.go diff --git a/coreiface/tests/api.go b/coreiface/tests/api.go index 0801b3ca7..ec1f63ae6 100644 --- a/coreiface/tests/api.go +++ b/coreiface/tests/api.go @@ -69,6 +69,7 @@ func TestApi(p Provider) func(t *testing.T) { t.Run("Path", tp.TestPath) t.Run("Pin", tp.TestPin) t.Run("PubSub", tp.TestPubSub) + t.Run("Routing", tp.TestRouting) t.Run("Unixfs", tp.TestUnixfs) apis <- -1 diff --git a/coreiface/tests/routing.go b/coreiface/tests/routing.go new file mode 100644 index 000000000..14e0d2e66 --- /dev/null +++ b/coreiface/tests/routing.go @@ -0,0 +1,92 @@ +package tests + +import ( + "context" + "testing" + "time" + + "github.com/gogo/protobuf/proto" + ipns_pb "github.com/ipfs/go-ipns/pb" + iface "github.com/ipfs/interface-go-ipfs-core" +) + +func (tp *TestSuite) TestRouting(t *testing.T) { + tp.hasApi(t, func(api iface.CoreAPI) error { + if api.Routing() == nil { + return errAPINotImplemented + } + return nil + }) + + t.Run("TestRoutingGet", tp.TestRoutingGet) + t.Run("TestRoutingPut", tp.TestRoutingPut) +} + +func (tp *TestSuite) testRoutingPublishKey(t *testing.T, ctx context.Context, api iface.CoreAPI) iface.IpnsEntry { + p, err := addTestObject(ctx, api) + if err != nil { + t.Fatal(err) + } + + entry, err := api.Name().Publish(ctx, p) + if err != nil { + t.Fatal(err) + } + + time.Sleep(3 * time.Second) + return entry +} + +func (tp *TestSuite) TestRoutingGet(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + apis, err := tp.MakeAPISwarm(ctx, true, 2) + if err != nil { + t.Fatal(err) + } + + // Node 1: publishes an IPNS name + ipnsEntry := tp.testRoutingPublishKey(t, ctx, apis[0]) + + // Node 2: retrieves the best value for the IPNS name. + data, err := apis[1].Routing().Get(ctx, "/ipns/"+ipnsEntry.Name()) + if err != nil { + t.Fatal(err) + } + + // Checks if values match. + var entry ipns_pb.IpnsEntry + err = proto.Unmarshal(data, &entry) + if err != nil { + t.Fatal(err) + } + + if string(entry.GetValue()) != ipnsEntry.Value().String() { + t.Fatalf("routing key has wrong value, expected %s, got %s", ipnsEntry.Value().String(), string(entry.GetValue())) + } +} + +func (tp *TestSuite) TestRoutingPut(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + apis, err := tp.MakeAPISwarm(ctx, true, 1) + if err != nil { + t.Fatal(err) + } + + // Create and publish IPNS entry. + ipnsEntry := tp.testRoutingPublishKey(t, ctx, apis[0]) + + // Get valid routing value. + data, err := apis[0].Routing().Get(ctx, "/ipns/"+ipnsEntry.Name()) + if err != nil { + t.Fatal(err) + } + + // Put routing value. + err = apis[0].Routing().Put(ctx, "/ipns/"+ipnsEntry.Name(), data) + if err != nil { + t.Fatal(err) + } +} From 4e262c8820c1d17c07c579b85d09b9a04955585a Mon Sep 17 00:00:00 2001 From: Jorropo Date: Tue, 10 Jan 2023 14:31:19 +0100 Subject: [PATCH 3787/3817] fix: correctly handle degenerate hamts while reading data Fixes https://github.com/ipfs/go-unixfs/security/advisories/GHSA-q264-w97q-q778 This commit was moved from ipfs/go-unixfs@dbcc43ec3e2db0d01e8d80c55040bba3cf22cb4b --- unixfs/hamt/hamt.go | 22 ++++++++++++++++++---- unixfs/hamt/hamt_test.go | 8 +++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/unixfs/hamt/hamt.go b/unixfs/hamt/hamt.go index c6cae88ea..3dc7b8a6f 100644 --- a/unixfs/hamt/hamt.go +++ b/unixfs/hamt/hamt.go @@ -106,12 +106,16 @@ func makeShard(ds ipld.DAGService, size int, key string, val *ipld.Link) (*Shard if err != nil { return nil, err } + childer, err := newChilder(ds, size) + if err != nil { + return nil, err + } maxpadding := fmt.Sprintf("%X", size-1) s := &Shard{ tableSizeLg2: lg2s, prefixPadStr: fmt.Sprintf("%%0%dX", len(maxpadding)), maxpadlen: len(maxpadding), - childer: newChilder(ds, size), + childer: childer, tableSize: size, dserv: ds, @@ -765,11 +769,21 @@ type childer struct { children []*Shard } -func newChilder(ds ipld.DAGService, size int) *childer { +const maximumHamtWidth = 1 << 10 // FIXME: Spec this and decide of a correct value + +func newChilder(ds ipld.DAGService, size int) (*childer, error) { + if size > maximumHamtWidth { + return nil, fmt.Errorf("hamt witdh (%d) exceed maximum allowed (%d)", size, maximumHamtWidth) + } + bf, err := bitfield.NewBitfield(size) + if err != nil { + return nil, err + } + return &childer{ dserv: ds, - bitfield: bitfield.NewBitfield(size), - } + bitfield: bf, + }, nil } func (s *childer) makeChilder(data []byte, links []*ipld.Link) *childer { diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index 150b97b90..c68e05632 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -737,8 +737,10 @@ func BenchmarkHAMTSet(b *testing.B) { } func TestHamtBadSize(t *testing.T) { - _, err := NewShard(nil, 7) - if err == nil { - t.Fatal("should have failed to construct hamt with bad size") + for _, size := range [...]int{-8, 7, 2, 1337, 1024 + 8, -3} { + _, err := NewShard(nil, size) + if err == nil { + t.Error("should have failed to construct hamt with bad size: %d", size) + } } } From 6c761b340929aac8fc4cd4f580539adb251af1cf Mon Sep 17 00:00:00 2001 From: Jorropo Date: Thu, 9 Feb 2023 20:02:06 +0100 Subject: [PATCH 3788/3817] test: fix tests after hamt issues fixes This commit was moved from ipfs/go-unixfs@6727e33d441dba5b2c97c309c1c9fa1b49e78047 --- unixfs/hamt/hamt_test.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/unixfs/hamt/hamt_test.go b/unixfs/hamt/hamt_test.go index c68e05632..2b9a7f404 100644 --- a/unixfs/hamt/hamt_test.go +++ b/unixfs/hamt/hamt_test.go @@ -31,7 +31,10 @@ func makeDir(ds ipld.DAGService, size int) ([]string, *Shard, error) { func makeDirWidth(ds ipld.DAGService, size, width int) ([]string, *Shard, error) { ctx := context.Background() - s, _ := NewShard(ds, width) + s, err := NewShard(ds, width) + if err != nil { + return nil, nil, err + } var dirs []string for i := 0; i < size; i++ { @@ -42,8 +45,11 @@ func makeDirWidth(ds ipld.DAGService, size, width int) ([]string, *Shard, error) for i := 0; i < len(dirs); i++ { nd := ft.EmptyDirNode() - ds.Add(ctx, nd) - err := s.Set(ctx, dirs[i], nd) + err := ds.Add(ctx, nd) + if err != nil { + return nil, nil, err + } + err = s.Set(ctx, dirs[i], nd) if err != nil { return nil, nil, err } @@ -126,7 +132,7 @@ func assertSerializationWorks(ds ipld.DAGService, s *Shard) error { func TestBasicSet(t *testing.T) { ds := mdtest.Mock() - for _, w := range []int{128, 256, 512, 1024, 2048, 4096} { + for _, w := range []int{128, 256, 512, 1024} { t.Run(fmt.Sprintf("BasicSet%d", w), func(t *testing.T) { names, s, err := makeDirWidth(ds, 1000, w) if err != nil { @@ -740,7 +746,7 @@ func TestHamtBadSize(t *testing.T) { for _, size := range [...]int{-8, 7, 2, 1337, 1024 + 8, -3} { _, err := NewShard(nil, size) if err == nil { - t.Error("should have failed to construct hamt with bad size: %d", size) + t.Errorf("should have failed to construct hamt with bad size: %d", size) } } } From 54d20f01739e73f7c770db8f2dc0be1c2185ce20 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 10 Feb 2023 03:25:12 +0100 Subject: [PATCH 3789/3817] test: use two nodes in publish This spin up online nodes instead of offline ones. This commit was moved from ipfs/interface-go-ipfs-core@a8d2741bbe08a6ba54cf4a4e229eff6978be1b77 --- coreiface/tests/routing.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreiface/tests/routing.go b/coreiface/tests/routing.go index 14e0d2e66..64287487e 100644 --- a/coreiface/tests/routing.go +++ b/coreiface/tests/routing.go @@ -70,7 +70,7 @@ func (tp *TestSuite) TestRoutingGet(t *testing.T) { func (tp *TestSuite) TestRoutingPut(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - apis, err := tp.MakeAPISwarm(ctx, true, 1) + apis, err := tp.MakeAPISwarm(ctx, true, 2) if err != nil { t.Fatal(err) } @@ -85,7 +85,7 @@ func (tp *TestSuite) TestRoutingPut(t *testing.T) { } // Put routing value. - err = apis[0].Routing().Put(ctx, "/ipns/"+ipnsEntry.Name(), data) + err = apis[1].Routing().Put(ctx, "/ipns/"+ipnsEntry.Name(), data) if err != nil { t.Fatal(err) } From 6e23bc24bd8fa077df3b119c43b332ea216e53d0 Mon Sep 17 00:00:00 2001 From: zuuluuz <55690197+cpucorecore@users.noreply.github.com> Date: Wed, 14 Dec 2022 10:21:35 +0800 Subject: [PATCH 3790/3817] fix: correctly handle errors in balancedbuilder's Layout this will lose block when newRoot.AddChild returns err. in ipfs-cluster single dag service add(context.TODO(), node) maybe failed when not enough peers to allocate for this block node This commit was moved from ipfs/go-unixfs@98e2622dffd9735f0638b178d4387e4eab65ad1c --- unixfs/importer/balanced/builder.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/unixfs/importer/balanced/builder.go b/unixfs/importer/balanced/builder.go index 3379e9765..a58bc1f1a 100644 --- a/unixfs/importer/balanced/builder.go +++ b/unixfs/importer/balanced/builder.go @@ -158,7 +158,10 @@ func Layout(db *h.DagBuilderHelper) (ipld.Node, error) { // Add the old `root` as a child of the `newRoot`. newRoot := db.NewFSNodeOverDag(ft.TFile) - newRoot.AddChild(root, fileSize, db) + err = newRoot.AddChild(root, fileSize, db) + if err != nil { + return nil, err + } // Fill the `newRoot` (that has the old `root` already as child) // and make it the current `root` for the next iteration (when From 9f118149f9db2d1de8fe5970eda531661185f9a1 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 10 Feb 2023 14:22:08 +0100 Subject: [PATCH 3791/3817] fix: correctly handle return errors This commit was moved from ipfs/go-unixfs@b7f6de0737d9ba79aded9ae3c52bf367bee2febf --- unixfs/importer/helpers/dagbuilder.go | 5 ++--- unixfs/io/directory.go | 7 ++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/unixfs/importer/helpers/dagbuilder.go b/unixfs/importer/helpers/dagbuilder.go index b59f41380..e72424667 100644 --- a/unixfs/importer/helpers/dagbuilder.go +++ b/unixfs/importer/helpers/dagbuilder.go @@ -189,11 +189,10 @@ func (db *DagBuilderHelper) FillNodeLayer(node *FSNodeOverDag) error { return err } } - node.Commit() // TODO: Do we need to commit here? The caller who created the // `FSNodeOverDag` should be in charge of that. - - return nil + _, err := node.Commit() + return err } // NewLeafDataNode builds the `node` with the data obtained from the diff --git a/unixfs/io/directory.go b/unixfs/io/directory.go index b602bf9ab..d591e08d2 100644 --- a/unixfs/io/directory.go +++ b/unixfs/io/directory.go @@ -164,7 +164,8 @@ func NewDirectoryFromNode(dserv ipld.DAGService, node ipld.Node) (Directory, err func (d *BasicDirectory) computeEstimatedSize() { d.estimatedSize = 0 - d.ForEachLink(context.TODO(), func(l *ipld.Link) error { + // err is just breaking the iteration and we always return nil + _ = d.ForEachLink(context.TODO(), func(l *ipld.Link) error { d.addToEstimatedSize(l.Name, l.Cid) return nil }) @@ -570,7 +571,7 @@ func (d *DynamicDirectory) AddChild(ctx context.Context, name string, nd ipld.No if err != nil { return err } - hamtDir.AddChild(ctx, name, nd) + err = hamtDir.AddChild(ctx, name, nd) if err != nil { return err } @@ -600,7 +601,7 @@ func (d *DynamicDirectory) RemoveChild(ctx context.Context, name string) error { if err != nil { return err } - basicDir.RemoveChild(ctx, name) + err = basicDir.RemoveChild(ctx, name) if err != nil { return err } From 21784f07c855b9aecb49f13104fc3701963097e0 Mon Sep 17 00:00:00 2001 From: web3-bot Date: Wed, 8 Feb 2023 12:55:15 +0000 Subject: [PATCH 3792/3817] stop using the deprecated io/ioutil package This commit was moved from ipld/go-car@4b4d55a1c3cb3161ce3940f197d567692a85ba04 --- ipld/car/v2/index/indexsorted.go | 3 ++- ipld/car/v2/index/mhindexsorted.go | 3 ++- ipld/car/v2/index_gen_test.go | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index ed94ed8f7..ab1462dc4 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -5,10 +5,11 @@ import ( "encoding/binary" "errors" "fmt" - internalio "github.com/ipld/go-car/v2/internal/io" "io" "sort" + internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/multiformats/go-multicodec" "github.com/ipfs/go-cid" diff --git a/ipld/car/v2/index/mhindexsorted.go b/ipld/car/v2/index/mhindexsorted.go index e0ef675de..598f1701f 100644 --- a/ipld/car/v2/index/mhindexsorted.go +++ b/ipld/car/v2/index/mhindexsorted.go @@ -3,10 +3,11 @@ package index import ( "encoding/binary" "errors" - internalio "github.com/ipld/go-car/v2/internal/io" "io" "sort" + internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/ipfs/go-cid" "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" diff --git a/ipld/car/v2/index_gen_test.go b/ipld/car/v2/index_gen_test.go index 11011e81a..61fd055da 100644 --- a/ipld/car/v2/index_gen_test.go +++ b/ipld/car/v2/index_gen_test.go @@ -1,11 +1,12 @@ package car_test import ( - "github.com/stretchr/testify/assert" "io" "os" "testing" + "github.com/stretchr/testify/assert" + "github.com/ipfs/go-cid" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" From 2506711a6eb731d1b75cb71d2cff4797c82f1487 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 9 Feb 2023 15:17:50 +1100 Subject: [PATCH 3793/3817] fix: switch to crypto/rand.Read This commit was moved from ipld/go-car@17628413d82ee031aa49a1102ac1b3b2aafe1a3e --- ipld/car/util/util_test.go | 3 ++- ipld/car/v2/internal/carv1/util/util_test.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ipld/car/util/util_test.go b/ipld/car/util/util_test.go index e85aed334..3f8f59af4 100644 --- a/ipld/car/util/util_test.go +++ b/ipld/car/util/util_test.go @@ -2,6 +2,7 @@ package util_test import ( "bytes" + crand "crypto/rand" "math/rand" "testing" @@ -15,7 +16,7 @@ func TestLdSize(t *testing.T) { data := make([][]byte, 5) for j := 0; j < 5; j++ { data[j] = make([]byte, rand.Intn(30)) - _, err := rand.Read(data[j]) + _, err := crand.Read(data[j]) require.NoError(t, err) } size := util.LdSize(data...) diff --git a/ipld/car/v2/internal/carv1/util/util_test.go b/ipld/car/v2/internal/carv1/util/util_test.go index 76828be9a..27719183a 100644 --- a/ipld/car/v2/internal/carv1/util/util_test.go +++ b/ipld/car/v2/internal/carv1/util/util_test.go @@ -2,6 +2,7 @@ package util_test import ( "bytes" + crand "crypto/rand" "math/rand" "testing" @@ -16,7 +17,7 @@ func TestLdSize(t *testing.T) { data := make([][]byte, 5) for j := 0; j < 5; j++ { data[j] = make([]byte, rand.Intn(30)) - _, err := rand.Read(data[j]) + _, err := crand.Read(data[j]) require.NoError(t, err) } size := util.LdSize(data...) From e8e0049acd1ed32bfb1323d95ead8652ccd7461a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Tue, 14 Feb 2023 20:14:36 +0100 Subject: [PATCH 3794/3817] blockstore: fast path for AllKeysChan using the index close https://github.com/ipld/go-car/issues/242 Crude benchmark: ``` func BenchmarkAllKeysChan(b *testing.B) { path := filepath.Join(b.TempDir(), "bench-large-v2.car") generateRandomCarV2File(b, path, 10<<20) // 10 MiB defer os.Remove(path) bs, err := blockstore.OpenReadWrite(path, nil) if err != nil { b.Fatal(err) } b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { c, err := bs.AllKeysChan(context.Background()) if err != nil { b.Fatal(err) } for range c { } } } func BenchmarkAllKeysChanUseWholeCIDs(b *testing.B) { path := filepath.Join(b.TempDir(), "bench-large-v2.car") generateRandomCarV2File(b, path, 10<<20) // 10 MiB defer os.Remove(path) bs, err := blockstore.OpenReadWrite(path, nil, carv2.UseWholeCIDs(true)) if err != nil { b.Fatal(err) } b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { c, err := bs.AllKeysChan(context.Background()) if err != nil { b.Fatal(err) } for range c { } } } ``` Before: > BenchmarkAllKeysChan-12 885 1256865 ns/op 88911 B/op 1617 allocs/op > BenchmarkAllKeysChanUseWholeCIDs-12 1010 1253543 ns/op 57861 B/op 976 allocs/op After > BenchmarkAllKeysChan-12 8971 135906 ns/op 30864 B/op 642 allocs/op > BenchmarkAllKeysChanUseWholeCIDs-12 13904 86140 ns/op 144 B/op 2 allocs/op BenchmarkAllKeysChan --- 9.25X faster, allocate 2.9X less memory BenchmarkAllKeysChanUseWholeCID --- 14.5X faster, allocate 401X less memory. This commit was moved from ipld/go-car@a4629d3384d214f62e7aa466be9b86b29cc69124 --- ipld/car/v2/blockstore/readwrite.go | 33 +++++++++++++++++++- ipld/car/v2/internal/store/insertionindex.go | 22 ++++++++----- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 3f5f7841b..29cb9cedb 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -260,7 +260,38 @@ func (b *ReadWrite) Finalize() error { } func (b *ReadWrite) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { - return b.ronly.AllKeysChan(ctx) + if ctx.Err() != nil { + return nil, ctx.Err() + } + + b.ronly.mu.Lock() + defer b.ronly.mu.Unlock() + + if b.ronly.closed { + return nil, errClosed + } + + out := make(chan cid.Cid) + + go func() { + defer close(out) + err := b.idx.ForEachCid(func(c cid.Cid, _ uint64) error { + if !b.opts.BlockstoreUseWholeCIDs { + c = cid.NewCidV1(cid.Raw, c.Hash()) + } + select { + case out <- c: + case <-ctx.Done(): + return ctx.Err() + } + return nil + }) + if err != nil { + maybeReportError(ctx, err) + } + }() + + return out, nil } func (b *ReadWrite) Has(ctx context.Context, key cid.Cid) (bool, error) { diff --git a/ipld/car/v2/internal/store/insertionindex.go b/ipld/car/v2/internal/store/insertionindex.go index f52fb3f2e..ffdf96fa4 100644 --- a/ipld/car/v2/internal/store/insertionindex.go +++ b/ipld/car/v2/internal/store/insertionindex.go @@ -152,17 +152,23 @@ func (ii *InsertionIndex) Unmarshal(r io.Reader) error { } func (ii *InsertionIndex) ForEach(f func(multihash.Multihash, uint64) error) error { - var errr error + var err error ii.items.AscendGreaterOrEqual(ii.items.Min(), func(i llrb.Item) bool { r := i.(recordDigest).Record - err := f(r.Cid.Hash(), r.Offset) - if err != nil { - errr = err - return false - } - return true + err = f(r.Cid.Hash(), r.Offset) + return err == nil + }) + return err +} + +func (ii *InsertionIndex) ForEachCid(f func(cid.Cid, uint64) error) error { + var err error + ii.items.AscendGreaterOrEqual(ii.items.Min(), func(i llrb.Item) bool { + r := i.(recordDigest).Record + err = f(r.Cid, r.Offset) + return err == nil }) - return errr + return err } func (ii *InsertionIndex) Codec() multicodec.Code { From e19b61d2a7c71464d1a6f0ef95302d5215725548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Wed, 22 Feb 2023 15:46:32 +0100 Subject: [PATCH 3795/3817] feat!: add and connect missing context, remove RemovePinWithMode (#23) This commit was moved from ipfs/go-ipfs-pinner@9abb80fb49ff5c8567bf4746ee4fc543da941c64 --- pinning/pinner/dspinner/pin.go | 165 ++++++++++++---------------- pinning/pinner/dspinner/pin_test.go | 44 +------- pinning/pinner/pin.go | 9 +- 3 files changed, 74 insertions(+), 144 deletions(-) diff --git a/pinning/pinner/dspinner/pin.go b/pinning/pinner/dspinner/pin.go index fa3d9e754..efe36df55 100644 --- a/pinning/pinner/dspinner/pin.go +++ b/pinning/pinner/dspinner/pin.go @@ -13,14 +13,15 @@ import ( "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore/query" - ipfspinner "github.com/ipfs/go-ipfs-pinner" - "github.com/ipfs/go-ipfs-pinner/dsindex" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" "github.com/ipfs/go-merkledag" "github.com/ipfs/go-merkledag/dagutils" "github.com/polydawn/refmt/cbor" "github.com/polydawn/refmt/obj/atlas" + + ipfspinner "github.com/ipfs/go-ipfs-pinner" + "github.com/ipfs/go-ipfs-pinner/dsindex" ) const ( @@ -179,23 +180,30 @@ func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { return err } - c := node.Cid() + if recurse { + return p.doPinRecursive(ctx, node.Cid(), true) + } else { + return p.doPinDirect(ctx, node.Cid()) + } +} + +func (p *pinner) doPinRecursive(ctx context.Context, c cid.Cid, fetch bool) error { cidKey := c.KeyString() p.lock.Lock() defer p.lock.Unlock() - if recurse { - found, err := p.cidRIndex.HasAny(ctx, cidKey) - if err != nil { - return err - } - if found { - return nil - } + found, err := p.cidRIndex.HasAny(ctx, cidKey) + if err != nil { + return err + } + if found { + return nil + } - dirtyBefore := p.dirty + dirtyBefore := p.dirty + if fetch { // temporary unlock to fetch the entire graph p.lock.Unlock() // Fetch graph starting at node identified by cid @@ -204,54 +212,63 @@ func (p *pinner) Pin(ctx context.Context, node ipld.Node, recurse bool) error { if err != nil { return err } + } - // If autosyncing, sync dag service before making any change to pins - err = p.flushDagService(ctx, false) - if err != nil { - return err - } - - // Only look again if something has changed. - if p.dirty != dirtyBefore { - found, err = p.cidRIndex.HasAny(ctx, cidKey) - if err != nil { - return err - } - if found { - return nil - } - } + // If autosyncing, sync dag service before making any change to pins + err = p.flushDagService(ctx, false) + if err != nil { + return err + } - // TODO: remove this to support multiple pins per CID - found, err = p.cidDIndex.HasAny(ctx, cidKey) + // Only look again if something has changed. + if p.dirty != dirtyBefore { + found, err = p.cidRIndex.HasAny(ctx, cidKey) if err != nil { return err } if found { - _, err = p.removePinsForCid(ctx, c, ipfspinner.Direct) - if err != nil { - return err - } + return nil } + } - _, err = p.addPin(ctx, c, ipfspinner.Recursive, "") - if err != nil { - return err - } - } else { - found, err := p.cidRIndex.HasAny(ctx, cidKey) + // TODO: remove this to support multiple pins per CID + found, err = p.cidDIndex.HasAny(ctx, cidKey) + if err != nil { + return err + } + if found { + _, err = p.removePinsForCid(ctx, c, ipfspinner.Direct) if err != nil { return err } - if found { - return fmt.Errorf("%s already pinned recursively", c.String()) - } + } - _, err = p.addPin(ctx, c, ipfspinner.Direct, "") - if err != nil { - return err - } + _, err = p.addPin(ctx, c, ipfspinner.Recursive, "") + if err != nil { + return err + } + return p.flushPins(ctx, false) +} + +func (p *pinner) doPinDirect(ctx context.Context, c cid.Cid) error { + cidKey := c.KeyString() + + p.lock.Lock() + defer p.lock.Unlock() + + found, err := p.cidRIndex.HasAny(ctx, cidKey) + if err != nil { + return err } + if found { + return fmt.Errorf("%s already pinned recursively", c.String()) + } + + _, err = p.addPin(ctx, c, ipfspinner.Direct, "") + if err != nil { + return err + } + return p.flushPins(ctx, false) } @@ -555,35 +572,6 @@ func (p *pinner) CheckIfPinned(ctx context.Context, cids ...cid.Cid) ([]ipfspinn return pinned, nil } -// RemovePinWithMode is for manually editing the pin structure. -// Use with care! If used improperly, garbage collection may not -// be successful. -func (p *pinner) RemovePinWithMode(c cid.Cid, mode ipfspinner.Mode) { - ctx := context.TODO() - // Check cache to see if CID is pinned - switch mode { - case ipfspinner.Direct, ipfspinner.Recursive: - default: - // programmer error, panic OK - panic("unrecognized pin type") - } - - p.lock.Lock() - defer p.lock.Unlock() - - removed, err := p.removePinsForCid(ctx, c, mode) - if err != nil { - log.Error("cound not remove pins: %s", err) - return - } - if !removed { - return - } - if err = p.flushPins(ctx, false); err != nil { - log.Error("cound not remove pins: %s", err) - } -} - // removePinsForCid removes all pins for a cid that has the specified mode. // Returns true if any pins, and all corresponding CID index entries, were // removed. Otherwise, returns false. @@ -826,32 +814,15 @@ func (p *pinner) Flush(ctx context.Context) error { // PinWithMode allows the user to have fine grained control over pin // counts -func (p *pinner) PinWithMode(c cid.Cid, mode ipfspinner.Mode) { - ctx := context.TODO() - - p.lock.Lock() - defer p.lock.Unlock() - +func (p *pinner) PinWithMode(ctx context.Context, c cid.Cid, mode ipfspinner.Mode) error { // TODO: remove his to support multiple pins per CID switch mode { case ipfspinner.Recursive: - if has, _ := p.cidRIndex.HasAny(ctx, c.KeyString()); has { - return // already a recursive pin for this CID - } + return p.doPinRecursive(ctx, c, false) case ipfspinner.Direct: - if has, _ := p.cidDIndex.HasAny(ctx, c.KeyString()); has { - return // already a direct pin for this CID - } + return p.doPinDirect(ctx, c) default: - panic("unrecognized pin mode") - } - - _, err := p.addPin(ctx, c, mode, "") - if err != nil { - return - } - if err = p.flushPins(ctx, false); err != nil { - log.Errorf("failed to create %s pin: %s", mode, err) + return fmt.Errorf("unrecognized pin mode") } } diff --git a/pinning/pinner/dspinner/pin_test.go b/pinning/pinner/dspinner/pin_test.go index 4e12fefb7..11c7ade19 100644 --- a/pinning/pinner/dspinner/pin_test.go +++ b/pinning/pinner/dspinner/pin_test.go @@ -19,10 +19,11 @@ import ( lds "github.com/ipfs/go-ds-leveldb" blockstore "github.com/ipfs/go-ipfs-blockstore" offline "github.com/ipfs/go-ipfs-exchange-offline" - ipfspin "github.com/ipfs/go-ipfs-pinner" util "github.com/ipfs/go-ipfs-util" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" + + ipfspin "github.com/ipfs/go-ipfs-pinner" ) var rand = util.NewTimeSeededRand() @@ -375,45 +376,6 @@ func TestAddLoadPin(t *testing.T) { } } -func TestRemovePinWithMode(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - dstore := dssync.MutexWrap(ds.NewMapDatastore()) - bstore := blockstore.NewBlockstore(dstore) - bserv := bs.New(bstore, offline.Exchange(bstore)) - - dserv := mdag.NewDAGService(bserv) - - p, err := New(ctx, dstore, dserv) - if err != nil { - t.Fatal(err) - } - - a, ak := randNode() - err = dserv.Add(ctx, a) - if err != nil { - panic(err) - } - - err = p.Pin(ctx, a, false) - if err != nil { - t.Fatal(err) - } - - ok, err := p.removePinsForCid(ctx, ak, ipfspin.Recursive) - if err != nil { - t.Fatal(err) - } - if ok { - t.Error("pin should not have been removed") - } - - p.RemovePinWithMode(ak, ipfspin.Direct) - - assertUnpinned(t, p, ak, "pin was not removed") -} - func TestIsPinnedLookup(t *testing.T) { // Test that lookups work in pins which share // the same branches. For that construct this tree: @@ -523,7 +485,7 @@ func TestFlush(t *testing.T) { } _, k := randNode() - p.PinWithMode(k, ipfspin.Recursive) + p.PinWithMode(ctx, k, ipfspin.Recursive) if err = p.Flush(ctx); err != nil { t.Fatal(err) } diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index bbabac5a0..27f4b4065 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -92,6 +92,8 @@ type Pinner interface { IsPinnedWithType(ctx context.Context, c cid.Cid, mode Mode) (string, bool, error) // Pin the given node, optionally recursively. + // Pin will make sure that the given node and its children if recursive is set + // are stored locally. Pin(ctx context.Context, node ipld.Node, recursive bool) error // Unpin the given cid. If recursive is true, removes either a recursive or @@ -111,12 +113,7 @@ type Pinner interface { // PinWithMode is for manually editing the pin structure. Use with // care! If used improperly, garbage collection may not be // successful. - PinWithMode(cid.Cid, Mode) - - // RemovePinWithMode is for manually editing the pin structure. - // Use with care! If used improperly, garbage collection may not - // be successful. - RemovePinWithMode(cid.Cid, Mode) + PinWithMode(context.Context, cid.Cid, Mode) error // Flush writes the pin state to the backing datastore Flush(ctx context.Context) error From cd0d3b130253593931b98af387e38576de773699 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 23 Feb 2023 15:00:37 +0100 Subject: [PATCH 3796/3817] feat: expose ErrInvalidPath and implement .Is function (#66) This commit was moved from ipfs/go-path@679319821f9cc9314cd7ca478adc2b48e37aa938 --- path/error.go | 16 ++++++++++------ path/error_test.go | 16 ++++++++++++++++ path/path.go | 20 ++++++++++---------- 3 files changed, 36 insertions(+), 16 deletions(-) create mode 100644 path/error_test.go diff --git a/path/error.go b/path/error.go index ca2e8416d..dafc446b5 100644 --- a/path/error.go +++ b/path/error.go @@ -4,20 +4,24 @@ import ( "fmt" ) -// helper type so path parsing errors include the path -type pathError struct { +type ErrInvalidPath struct { error error path string } -func (e *pathError) Error() string { +func (e ErrInvalidPath) Error() string { return fmt.Sprintf("invalid path %q: %s", e.path, e.error) } -func (e *pathError) Unwrap() error { +func (e ErrInvalidPath) Unwrap() error { return e.error } -func (e *pathError) Path() string { - return e.path +func (e ErrInvalidPath) Is(err error) bool { + switch err.(type) { + case ErrInvalidPath: + return true + default: + return false + } } diff --git a/path/error_test.go b/path/error_test.go new file mode 100644 index 000000000..07aab6408 --- /dev/null +++ b/path/error_test.go @@ -0,0 +1,16 @@ +package path + +import ( + "errors" + "testing" +) + +func TestErrorIs(t *testing.T) { + if !errors.Is(ErrInvalidPath{path: "foo", error: errors.New("bar")}, ErrInvalidPath{}) { + t.Fatal("error must be error") + } + + if !errors.Is(&ErrInvalidPath{path: "foo", error: errors.New("bar")}, ErrInvalidPath{}) { + t.Fatal("pointer to error must be error") + } +} diff --git a/path/path.go b/path/path.go index e70d96384..6d53ade04 100644 --- a/path/path.go +++ b/path/path.go @@ -97,33 +97,33 @@ func ParsePath(txt string) (Path, error) { // we expect this to start with a hash, and be an 'ipfs' path if parts[0] != "" { if _, err := decodeCid(parts[0]); err != nil { - return "", &pathError{error: err, path: txt} + return "", &ErrInvalidPath{error: err, path: txt} } // The case when the path starts with hash without a protocol prefix return Path("/ipfs/" + txt), nil } if len(parts) < 3 { - return "", &pathError{error: fmt.Errorf("invalid ipfs path"), path: txt} + return "", &ErrInvalidPath{error: fmt.Errorf("invalid ipfs path"), path: txt} } //TODO: make this smarter switch parts[1] { case "ipfs", "ipld": if parts[2] == "" { - return "", &pathError{error: fmt.Errorf("not enough path components"), path: txt} + return "", &ErrInvalidPath{error: fmt.Errorf("not enough path components"), path: txt} } // Validate Cid. _, err := decodeCid(parts[2]) if err != nil { - return "", &pathError{error: fmt.Errorf("invalid CID: %s", err), path: txt} + return "", &ErrInvalidPath{error: fmt.Errorf("invalid CID: %w", err), path: txt} } case "ipns": if parts[2] == "" { - return "", &pathError{error: fmt.Errorf("not enough path components"), path: txt} + return "", &ErrInvalidPath{error: fmt.Errorf("not enough path components"), path: txt} } default: - return "", &pathError{error: fmt.Errorf("unknown namespace %q", parts[1]), path: txt} + return "", &ErrInvalidPath{error: fmt.Errorf("unknown namespace %q", parts[1]), path: txt} } return Path(txt), nil @@ -132,12 +132,12 @@ func ParsePath(txt string) (Path, error) { // ParseCidToPath takes a CID in string form and returns a valid ipfs Path. func ParseCidToPath(txt string) (Path, error) { if txt == "" { - return "", &pathError{error: fmt.Errorf("empty"), path: txt} + return "", &ErrInvalidPath{error: fmt.Errorf("empty"), path: txt} } c, err := decodeCid(txt) if err != nil { - return "", &pathError{error: err, path: txt} + return "", &ErrInvalidPath{error: err, path: txt} } return FromCid(c), nil @@ -169,13 +169,13 @@ func SplitAbsPath(fpath Path) (cid.Cid, []string, error) { // if nothing, bail. if len(parts) == 0 { - return cid.Cid{}, nil, &pathError{error: fmt.Errorf("empty"), path: string(fpath)} + return cid.Cid{}, nil, &ErrInvalidPath{error: fmt.Errorf("empty"), path: string(fpath)} } c, err := decodeCid(parts[0]) // first element in the path is a cid if err != nil { - return cid.Cid{}, nil, &pathError{error: fmt.Errorf("invalid CID: %s", err), path: string(fpath)} + return cid.Cid{}, nil, &ErrInvalidPath{error: fmt.Errorf("invalid CID: %w", err), path: string(fpath)} } return c, parts[1:], nil From fbb6fad3849a6bf8fa69db30d59ae7e207d7a2dd Mon Sep 17 00:00:00 2001 From: Steve Moyer Date: Mon, 27 Feb 2023 09:44:25 -0500 Subject: [PATCH 3797/3817] docs(pinning): eliminate copy-n-paste typo (#28) This commit was moved from ipfs/go-ipfs-pinner@1174ddd23d65dd4ff4355c049e1e5e662a424b3f --- pinning/pinner/pin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinning/pinner/pin.go b/pinning/pinner/pin.go index 27f4b4065..fcf7d764a 100644 --- a/pinning/pinner/pin.go +++ b/pinning/pinner/pin.go @@ -121,7 +121,7 @@ type Pinner interface { // DirectKeys returns all directly pinned cids DirectKeys(ctx context.Context) ([]cid.Cid, error) - // DirectKeys returns all recursively pinned cids + // RecursiveKeys returns all recursively pinned cids RecursiveKeys(ctx context.Context) ([]cid.Cid, error) // InternalPins returns all cids kept pinned for the internal state of the From 41a43838859e46cb9e6ce6e5cf32f8d94b14a074 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 6 Mar 2023 23:14:18 +0000 Subject: [PATCH 3798/3817] feat: add WithTrustedCar() reader option (#381) * Add NextInsecure() method Attempt to make CAR traversal a little bit faster by not forcing users to hash every single in a CAR. * Add TrustedCAR option to BlockReader (remove NextInsecure()) * Add test for TrustedCAR option * fix: apply suggestions from code review --------- Co-authored-by: Rod Vagg This commit was moved from ipld/go-car@e9a77cb2313fe6f942cf03cc7fedd4fcf4151d1d --- ipld/car/v2/block_reader.go | 14 +++++++------ ipld/car/v2/block_reader_test.go | 34 ++++++++++++++++++++++++++++++++ ipld/car/v2/options.go | 9 +++++++++ 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/ipld/car/v2/block_reader.go b/ipld/car/v2/block_reader.go index 27d134b05..fff477a6f 100644 --- a/ipld/car/v2/block_reader.go +++ b/ipld/car/v2/block_reader.go @@ -120,13 +120,15 @@ func (br *BlockReader) Next() (blocks.Block, error) { return nil, err } - hashed, err := c.Prefix().Sum(data) - if err != nil { - return nil, err - } + if !br.opts.TrustedCAR { + hashed, err := c.Prefix().Sum(data) + if err != nil { + return nil, err + } - if !hashed.Equals(c) { - return nil, fmt.Errorf("mismatch in content integrity, expected: %s, got: %s", c, hashed) + if !hashed.Equals(c) { + return nil, fmt.Errorf("mismatch in content integrity, expected: %s, got: %s", c, hashed) + } } ss := uint64(c.ByteLen()) + uint64(len(data)) diff --git a/ipld/car/v2/block_reader_test.go b/ipld/car/v2/block_reader_test.go index b5958c7f0..ca6c2404d 100644 --- a/ipld/car/v2/block_reader_test.go +++ b/ipld/car/v2/block_reader_test.go @@ -179,6 +179,40 @@ func TestMaxSectionLength(t *testing.T) { require.True(t, bytes.Equal(block, readBlock.RawData())) } +func TestTrustedCAR(t *testing.T) { + // headerHex is the zero-roots CARv1 header + const headerHex = "11a265726f6f7473806776657273696f6e01" + headerBytes, _ := hex.DecodeString(headerHex) + // block of zeros + block := make([]byte, 5) + // CID for that block + pfx := cid.NewPrefixV1(cid.Raw, mh.SHA2_256) + cid, err := pfx.Sum(block) + require.NoError(t, err) + + // Modify the block so it won't match CID anymore + block[2] = 0xFF + // construct CAR + var buf bytes.Buffer + buf.Write(headerBytes) + buf.Write(varint.ToUvarint(uint64(len(cid.Bytes()) + len(block)))) + buf.Write(cid.Bytes()) + buf.Write(block) + + // try to read it as trusted + car, err := carv2.NewBlockReader(bytes.NewReader(buf.Bytes()), carv2.WithTrustedCAR(true)) + require.NoError(t, err) + _, err = car.Next() + require.NoError(t, err) + + // Try to read it as untrusted - should fail + car, err = carv2.NewBlockReader(bytes.NewReader(buf.Bytes()), carv2.WithTrustedCAR(false)) + require.NoError(t, err) + // error should occur on first section read + _, err = car.Next() + require.EqualError(t, err, "mismatch in content integrity, expected: bafkreieikviivlpbn3cxhuq6njef37ikoysaqxa2cs26zxleqxpay2bzuq, got: bafkreidgklrppelx4fxcsna7cxvo3g7ayedfojkqeuus6kz6e4hy7gukmy") +} + func TestMaxHeaderLength(t *testing.T) { // headerHex is the is a 5 root CARv1 header const headerHex = "de01a265726f6f747385d82a58250001711220785197229dc8bb1152945da58e2348f7e279eeded06cc2ca736d0e879858b501d82a58250001711220785197229dc8bb1152945da58e2348f7e279eeded06cc2ca736d0e879858b501d82a58250001711220785197229dc8bb1152945da58e2348f7e279eeded06cc2ca736d0e879858b501d82a58250001711220785197229dc8bb1152945da58e2348f7e279eeded06cc2ca736d0e879858b501d82a58250001711220785197229dc8bb1152945da58e2348f7e279eeded06cc2ca736d0e879858b5016776657273696f6e01" diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go index dbde3f3e1..e28589e89 100644 --- a/ipld/car/v2/options.go +++ b/ipld/car/v2/options.go @@ -60,6 +60,7 @@ type Options struct { MaxTraversalLinks uint64 WriteAsCarV1 bool TraversalPrototypeChooser traversal.LinkTargetNodePrototypeChooser + TrustedCAR bool MaxAllowedHeaderSize uint64 MaxAllowedSectionSize uint64 @@ -160,6 +161,14 @@ func WithTraversalPrototypeChooser(t traversal.LinkTargetNodePrototypeChooser) O } } +// WithTrustedCAR specifies whether CIDs match the block data as they are read +// from the CAR files. +func WithTrustedCAR(t bool) Option { + return func(o *Options) { + o.TrustedCAR = t + } +} + // MaxAllowedHeaderSize overrides the default maximum size (of 32 MiB) that a // CARv1 decode (including within a CARv2 container) will allow a header to be // without erroring. From f4e1df129ecebef3bdf30ecba8a88f682f1a2537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Thu, 16 Feb 2023 16:34:22 +0100 Subject: [PATCH 3799/3817] ReadWrite: add an alternative FinalizeReadOnly+Close flow The goal being to be able to keep reading blocks and the index after safely finalizing the file on disk. Typically for indexing purpose. This commit was moved from ipld/go-car@b6ef2a4924430e92c46b115651d4a2f01b65644b --- ipld/car/v2/blockstore/readwrite.go | 78 +++++++++++++++++++----- ipld/car/v2/blockstore/readwrite_test.go | 64 ++++++++++++++++++- 2 files changed, 125 insertions(+), 17 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 29cb9cedb..7cbd0ca29 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -18,6 +18,10 @@ import ( var _ blockstore.Blockstore = (*ReadWrite)(nil) +var ( + errFinalized = fmt.Errorf("cannot write in a carv2 blockstore after finalize") +) + // ReadWrite implements a blockstore that stores blocks in CARv2 format. // Blocks put into the blockstore can be read back once they are successfully written. // This implementation is preferable for a write-heavy workload. @@ -35,6 +39,8 @@ type ReadWrite struct { idx *store.InsertionIndex header carv2.Header + finalized bool // also protected by ronly.mu + opts carv2.Options } @@ -109,10 +115,11 @@ func OpenReadWriteFile(f *os.File, roots []cid.Cid, opts ...carv2.Option) (*Read // Instantiate block store. // Set the header fileld before applying options since padding options may modify header. rwbs := &ReadWrite{ - f: f, - idx: store.NewInsertionIndex(), - header: carv2.NewHeader(0), - opts: carv2.ApplyOptions(opts...), + f: f, + idx: store.NewInsertionIndex(), + header: carv2.NewHeader(0), + opts: carv2.ApplyOptions(opts...), + finalized: false, } rwbs.ronly.opts = rwbs.opts @@ -186,6 +193,9 @@ func (b *ReadWrite) PutMany(ctx context.Context, blks []blocks.Block) error { if b.ronly.closed { return errClosed } + if b.finalized { + return errFinalized + } for _, bl := range blks { c := bl.Cid() @@ -227,30 +237,70 @@ func (b *ReadWrite) Discard() { // Finalize finalizes this blockstore by writing the CARv2 header, along with flattened index // for more efficient subsequent read. +// This is the equivalent to calling FinalizeReadOnly and Close. // After this call, the blockstore can no longer be used. func (b *ReadWrite) Finalize() error { + b.ronly.mu.Lock() + defer b.ronly.mu.Unlock() + + if err := b.finalizeReadOnlyWithoutMutex(); err != nil { + return err + } + if err := b.closeWithoutMutex(); err != nil { + return err + } + return nil +} + +// Finalize finalizes this blockstore by writing the CARv2 header, along with flattened index +// for more efficient subsequent read, but keep it open read-only. +// This call should be complemented later by a call to Close. +func (b *ReadWrite) FinalizeReadOnly() error { + b.ronly.mu.Lock() + defer b.ronly.mu.Unlock() + + return b.finalizeReadOnlyWithoutMutex() +} + +func (b *ReadWrite) finalizeReadOnlyWithoutMutex() error { if b.opts.WriteAsCarV1 { // all blocks are already properly written to the CARv1 inner container and there's // no additional finalization required at the end of the file for a complete v1 - b.ronly.Close() + b.finalized = true return nil } - b.ronly.mu.Lock() - defer b.ronly.mu.Unlock() - if b.ronly.closed { // Allow duplicate Finalize calls, just like Close. // Still error, just like ReadOnly.Close; it should be discarded. - return fmt.Errorf("called Finalize on a closed blockstore") + return fmt.Errorf("called Finalize or FinalizeReadOnly on a closed blockstore") + } + if b.finalized { + return fmt.Errorf("called Finalize or FinalizeReadOnly on an already finalized blockstore") } - // Note that we can't use b.Close here, as that tries to grab the same - // mutex we're holding here. - defer b.ronly.closeWithoutMutex() + b.finalized = true - if err := store.Finalize(b.f, b.header, b.idx, uint64(b.dataWriter.Position()), b.opts.StoreIdentityCIDs, b.opts.IndexCodec); err != nil { - return err + return store.Finalize(b.f, b.header, b.idx, uint64(b.dataWriter.Position()), b.opts.StoreIdentityCIDs, b.opts.IndexCodec) +} + +// Close closes the blockstore. +// After this call, the blockstore can no longer be used. +func (b *ReadWrite) Close() error { + b.ronly.mu.Lock() + defer b.ronly.mu.Unlock() + + return b.closeWithoutMutex() +} + +func (b *ReadWrite) closeWithoutMutex() error { + if !b.opts.WriteAsCarV1 && !b.finalized { + return fmt.Errorf("called Close without FinalizeReadOnly first") + } + if b.ronly.closed { + // Allow duplicate Close calls + // Still error, just like ReadOnly.Close; it should be discarded. + return fmt.Errorf("called Close on a closed blockstore") } if err := b.ronly.closeWithoutMutex(); err != nil { diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 04da36fe8..45fcb21ac 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -577,9 +577,6 @@ func TestReadWritePanicsOnlyWhenFinalized(t *testing.T) { require.NoError(t, subject.Finalize()) require.Error(t, subject.Finalize()) - _, ok := (interface{})(subject).(io.Closer) - require.False(t, ok) - _, err = subject.Get(ctx, oneTestBlockCid) require.Error(t, err) _, err = subject.GetSize(ctx, anotherTestBlockCid) @@ -1027,3 +1024,64 @@ func TestBlockstore_IdentityCidWithEmptyDataIsIndexed(t *testing.T) { require.NoError(t, err) require.Equal(t, 1, count) } + +func TestBlockstoreFinalizeReadOnly(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + root := blocks.NewBlock([]byte("foo")) + + p := filepath.Join(t.TempDir(), "readwrite.car") + bs, err := blockstore.OpenReadWrite(p, []cid.Cid{root.Cid()}) + require.NoError(t, err) + + err = bs.Put(ctx, root) + require.NoError(t, err) + + roots, err := bs.Roots() + require.NoError(t, err) + _, err = bs.Has(ctx, roots[0]) + require.NoError(t, err) + _, err = bs.Get(ctx, roots[0]) + require.NoError(t, err) + _, err = bs.GetSize(ctx, roots[0]) + require.NoError(t, err) + _, err = bs.AllKeysChan(ctx) + require.NoError(t, err) + + // soft finalize, we can still read, but not write + err = bs.FinalizeReadOnly() + require.NoError(t, err) + + _, err = bs.Roots() + require.NoError(t, err) + _, err = bs.Has(ctx, roots[0]) + require.NoError(t, err) + _, err = bs.Get(ctx, roots[0]) + require.NoError(t, err) + _, err = bs.GetSize(ctx, roots[0]) + require.NoError(t, err) + _, err = bs.AllKeysChan(ctx) + require.NoError(t, err) + + err = bs.Put(ctx, root) + require.Error(t, err) + + // final close, nothing works anymore + err = bs.Close() + require.NoError(t, err) + + _, err = bs.Roots() + require.Error(t, err) + _, err = bs.Has(ctx, roots[0]) + require.Error(t, err) + _, err = bs.Get(ctx, roots[0]) + require.Error(t, err) + _, err = bs.GetSize(ctx, roots[0]) + require.Error(t, err) + _, err = bs.AllKeysChan(ctx) + require.Error(t, err) + + err = bs.Put(ctx, root) + require.Error(t, err) +} From 7349b74b2b8711224d2f367c48d838d3d05c1408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Tue, 7 Mar 2023 12:15:23 +0100 Subject: [PATCH 3800/3817] blockstore: try to close during Finalize(), even in case of previous error Co-authored-by: Rod Vagg This commit was moved from ipld/go-car@34f4b63d45e446dbd833c0488204ec9a263275e3 --- ipld/car/v2/blockstore/readwrite.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 7cbd0ca29..e605414f2 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -243,11 +243,10 @@ func (b *ReadWrite) Finalize() error { b.ronly.mu.Lock() defer b.ronly.mu.Unlock() - if err := b.finalizeReadOnlyWithoutMutex(); err != nil { - return err - } - if err := b.closeWithoutMutex(); err != nil { - return err + for _, err := range []error{b.finalizeReadOnlyWithoutMutex(), b.closeWithoutMutex()} { + if err != nil { + return err + } } return nil } From 7ecc10dc83088535a5c9c95811725714fe85c3ab Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 6 Mar 2023 22:14:17 +1100 Subject: [PATCH 3801/3817] fix: if we don't read the full block data, don't error on !EOF This commit was moved from ipld/go-car@dbd9059c689272c22f2dd6f805cccc0e398625ad --- ipld/car/cmd/car/inspect.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ipld/car/cmd/car/inspect.go b/ipld/car/cmd/car/inspect.go index 76bb41ce9..f320500cd 100644 --- a/ipld/car/cmd/car/inspect.go +++ b/ipld/car/cmd/car/inspect.go @@ -3,6 +3,7 @@ package main import ( "bytes" "fmt" + "io" "os" "sort" "strings" @@ -22,7 +23,7 @@ func InspectCar(c *cli.Context) (err error) { } } - rd, err := carv2.NewReader(inStream) + rd, err := carv2.NewReader(inStream, carv2.ZeroLengthSectionAsEOF(true)) if err != nil { return err } @@ -31,6 +32,15 @@ func InspectCar(c *cli.Context) (err error) { return err } + if stats.Version == 1 && c.IsSet("full") { // check that we've read all the data + got, err := inStream.Read(make([]byte, 1)) // force EOF + if err != nil && err != io.EOF { + return err + } else if got > 0 { + return fmt.Errorf("unexpected data after EOF: %d", got) + } + } + var v2s string if stats.Version == 2 { idx := "(none)" From 0904e49f59e278f7cc385849b13d9a0bb7022add Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 6 Mar 2023 22:15:01 +1100 Subject: [PATCH 3802/3817] feat: extract specific path, accept stdin as streaming input This commit was moved from ipld/go-car@15e65826705b0a1d37302e8a5b1a9af899ee2a8b --- ipld/car/cmd/car/car.go | 8 +- ipld/car/cmd/car/extract.go | 354 ++++++++++++------ .../car/testdata/script/create-extract.txt | 2 +- 3 files changed, 255 insertions(+), 109 deletions(-) diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index 70c9eb32a..81cec5a38 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -79,10 +79,16 @@ func main1() int { &cli.StringFlag{ Name: "file", Aliases: []string{"f"}, - Usage: "The car file to extract from", + Usage: "The car file to extract from, or '-' to read from stdin", Required: true, TakesFile: true, }, + &cli.StringFlag{ + Name: "path", + Aliases: []string{"p"}, + Usage: "The unixfs path to extract", + Required: false, + }, &cli.BoolFlag{ Name: "verbose", Aliases: []string{"v"}, diff --git a/ipld/car/cmd/car/extract.go b/ipld/car/cmd/car/extract.go index ae2da8751..db4f207f7 100644 --- a/ipld/car/cmd/car/extract.go +++ b/ipld/car/cmd/car/extract.go @@ -1,23 +1,27 @@ package main import ( - "bytes" + "context" "errors" "fmt" "io" "os" "path" "path/filepath" + "strings" + "sync" "github.com/ipfs/go-cid" "github.com/ipfs/go-unixfsnode" "github.com/ipfs/go-unixfsnode/data" "github.com/ipfs/go-unixfsnode/file" - "github.com/ipld/go-car/v2/blockstore" + "github.com/ipld/go-car/v2" + carstorage "github.com/ipld/go-car/v2/storage" dagpb "github.com/ipld/go-codec-dagpb" "github.com/ipld/go-ipld-prime" cidlink "github.com/ipld/go-ipld-prime/linking/cid" basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/ipld/go-ipld-prime/storage" "github.com/urfave/cli/v2" ) @@ -33,92 +37,109 @@ func ExtractCar(c *cli.Context) error { outputDir = c.Args().First() } - bs, err := blockstore.OpenReadOnly(c.String("file")) - if err != nil { - return err - } + var store storage.ReadableStorage + var roots []cid.Cid - ls := cidlink.DefaultLinkSystem() - ls.TrustedStorage = true - ls.StorageReadOpener = func(_ ipld.LinkContext, l ipld.Link) (io.Reader, error) { - cl, ok := l.(cidlink.Link) - if !ok { - return nil, fmt.Errorf("not a cidlink") + if c.String("file") == "-" { + var err error + store, roots, err = NewStdinReadStorage(c.App.Reader) + if err != nil { + return err } - blk, err := bs.Get(c.Context, cl.Cid) + } else { + carFile, err := os.Open(c.String("file")) if err != nil { - return nil, err + return err + } + store, err = carstorage.OpenReadable(carFile) + if err != nil { + return err } - return bytes.NewBuffer(blk.RawData()), nil + roots = store.(carstorage.ReadableCar).Roots() } - roots, err := bs.Roots() + ls := cidlink.DefaultLinkSystem() + ls.TrustedStorage = true + ls.SetReadStorage(store) + + path, err := pathSegments(c.String("path")) if err != nil { return err } + var extractedFiles int for _, root := range roots { - if err := extractRoot(c, &ls, root, outputDir); err != nil { + count, err := extractRoot(c, &ls, root, outputDir, path) + if err != nil { return err } + extractedFiles += count + } + if extractedFiles == 0 { + fmt.Fprintf(c.App.ErrWriter, "no files extracted\n") + } else { + fmt.Fprintf(c.App.ErrWriter, "extracted %d file(s)\n", extractedFiles) } return nil } -func extractRoot(c *cli.Context, ls *ipld.LinkSystem, root cid.Cid, outputDir string) error { +func extractRoot(c *cli.Context, ls *ipld.LinkSystem, root cid.Cid, outputDir string, path []string) (int, error) { if root.Prefix().Codec == cid.Raw { if c.IsSet("verbose") { fmt.Fprintf(c.App.ErrWriter, "skipping raw root %s\n", root) } - return nil + return 0, nil } pbn, err := ls.Load(ipld.LinkContext{}, cidlink.Link{Cid: root}, dagpb.Type.PBNode) if err != nil { - return err + return 0, err } pbnode := pbn.(dagpb.PBNode) ufn, err := unixfsnode.Reify(ipld.LinkContext{}, pbnode, ls) if err != nil { - return err + return 0, err } outputResolvedDir, err := filepath.EvalSymlinks(outputDir) if err != nil { - return err + return 0, err } if _, err := os.Stat(outputResolvedDir); os.IsNotExist(err) { if err := os.Mkdir(outputResolvedDir, 0755); err != nil { - return err + return 0, err } } - if err := extractDir(c, ls, ufn, outputResolvedDir, "/"); err != nil { + count, err := extractDir(c, ls, ufn, outputResolvedDir, "/", path) + if err != nil { if !errors.Is(err, ErrNotDir) { - return fmt.Errorf("%s: %w", root, err) + return 0, fmt.Errorf("%s: %w", root, err) } + + // if it's not a directory, it's a file. ufsData, err := pbnode.LookupByString("Data") if err != nil { - return err + return 0, err } ufsBytes, err := ufsData.AsBytes() if err != nil { - return err + return 0, err } ufsNode, err := data.DecodeUnixFSData(ufsBytes) if err != nil { - return err + return 0, err } if ufsNode.DataType.Int() == data.Data_File || ufsNode.DataType.Int() == data.Data_Raw { if err := extractFile(c, ls, pbnode, filepath.Join(outputResolvedDir, "unknown")); err != nil { - return err + return 0, err } } - return nil + return 1, nil } - return nil + return count, nil } func resolvePath(root, pth string) (string, error) { @@ -139,99 +160,131 @@ func resolvePath(root, pth string) (string, error) { return joined, nil } -func extractDir(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputRoot, outputPath string) error { +func extractDir(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputRoot, outputPath string, matchPath []string) (int, error) { dirPath, err := resolvePath(outputRoot, outputPath) if err != nil { - return err + return 0, err } // make the directory. if err := os.MkdirAll(dirPath, 0755); err != nil { - return err + return 0, err } - if n.Kind() == ipld.Kind_Map { - mi := n.MapIterator() - for !mi.Done() { - key, val, err := mi.Next() - if err != nil { - return err - } - ks, err := key.AsString() - if err != nil { - return err - } - nextRes, err := resolvePath(outputRoot, path.Join(outputPath, ks)) - if err != nil { - return err - } - if c.IsSet("verbose") { - fmt.Fprintf(c.App.Writer, "%s\n", nextRes) - } + if n.Kind() != ipld.Kind_Map { + return 0, ErrNotDir + } - if val.Kind() != ipld.Kind_Link { - return fmt.Errorf("unexpected map value for %s at %s", ks, outputPath) - } - // a directory may be represented as a map of name: if unixADL is applied - vl, err := val.AsLink() - if err != nil { - return err - } - dest, err := ls.Load(ipld.LinkContext{}, vl, basicnode.Prototype.Any) - if err != nil { - return err + subPath := matchPath + if len(matchPath) > 0 { + subPath = matchPath[1:] + } + + extractElement := func(name string, n ipld.Node) (int, error) { + nextRes, err := resolvePath(outputRoot, path.Join(outputPath, name)) + if err != nil { + return 0, err + } + if c.IsSet("verbose") { + fmt.Fprintf(c.App.Writer, "%s\n", nextRes) + } + + if n.Kind() != ipld.Kind_Link { + return 0, fmt.Errorf("unexpected map value for %s at %s", name, outputPath) + } + // a directory may be represented as a map of name: if unixADL is applied + vl, err := n.AsLink() + if err != nil { + return 0, err + } + dest, err := ls.Load(ipld.LinkContext{}, vl, basicnode.Prototype.Any) + if err != nil { + if nf, ok := err.(interface{ NotFound() bool }); ok && nf.NotFound() { + fmt.Fprintf(c.App.ErrWriter, "data for directory entry not found: %s (skipping...)\n", name) + return 0, nil } - // degenerate files are handled here. - if dest.Kind() == ipld.Kind_Bytes { - if err := extractFile(c, ls, dest, nextRes); err != nil { - return err - } - continue - } else { - // dir / pbnode - pbb := dagpb.Type.PBNode.NewBuilder() - if err := pbb.AssignNode(dest); err != nil { - return err - } - dest = pbb.Build() + return 0, err + } + // degenerate files are handled here. + if dest.Kind() == ipld.Kind_Bytes { + if err := extractFile(c, ls, dest, nextRes); err != nil { + return 0, err } - pbnode := dest.(dagpb.PBNode) + return 1, nil + } - // interpret dagpb 'data' as unixfs data and look at type. - ufsData, err := pbnode.LookupByString("Data") - if err != nil { - return err - } - ufsBytes, err := ufsData.AsBytes() + // dir / pbnode + pbb := dagpb.Type.PBNode.NewBuilder() + if err := pbb.AssignNode(dest); err != nil { + return 0, err + } + pbnode := pbb.Build().(dagpb.PBNode) + + // interpret dagpb 'data' as unixfs data and look at type. + ufsData, err := pbnode.LookupByString("Data") + if err != nil { + return 0, err + } + ufsBytes, err := ufsData.AsBytes() + if err != nil { + return 0, err + } + ufsNode, err := data.DecodeUnixFSData(ufsBytes) + if err != nil { + return 0, err + } + + switch ufsNode.DataType.Int() { + case data.Data_Directory, data.Data_HAMTShard: + ufn, err := unixfsnode.Reify(ipld.LinkContext{}, pbnode, ls) if err != nil { - return err + return 0, err } - ufsNode, err := data.DecodeUnixFSData(ufsBytes) - if err != nil { - return err + return extractDir(c, ls, ufn, outputRoot, path.Join(outputPath, name), subPath) + case data.Data_File, data.Data_Raw: + if err := extractFile(c, ls, pbnode, nextRes); err != nil { + return 0, err } - if ufsNode.DataType.Int() == data.Data_Directory || ufsNode.DataType.Int() == data.Data_HAMTShard { - ufn, err := unixfsnode.Reify(ipld.LinkContext{}, pbnode, ls) - if err != nil { - return err - } - - if err := extractDir(c, ls, ufn, outputRoot, path.Join(outputPath, ks)); err != nil { - return err - } - } else if ufsNode.DataType.Int() == data.Data_File || ufsNode.DataType.Int() == data.Data_Raw { - if err := extractFile(c, ls, pbnode, nextRes); err != nil { - return err - } - } else if ufsNode.DataType.Int() == data.Data_Symlink { - data := ufsNode.Data.Must().Bytes() - if err := os.Symlink(string(data), nextRes); err != nil { - return err - } + return 1, nil + case data.Data_Symlink: + data := ufsNode.Data.Must().Bytes() + if err := os.Symlink(string(data), nextRes); err != nil { + return 0, err } + return 1, nil + default: + return 0, fmt.Errorf("unknown unixfs type: %d", ufsNode.DataType.Int()) + } + } + + // specific path segment + if len(matchPath) > 0 { + val, err := n.LookupByString(matchPath[0]) + if err != nil { + return 0, err + } + return extractElement(matchPath[0], val) + } + + // everything + var count int + mi := n.MapIterator() + for !mi.Done() { + key, val, err := mi.Next() + if err != nil { + return 0, err } - return nil + ks, err := key.AsString() + if err != nil { + return 0, err + } + ecount, err := extractElement(ks, val) + if err != nil { + return 0, err + } + count += ecount } - return ErrNotDir + + return count, nil } func extractFile(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputName string) error { @@ -253,3 +306,90 @@ func extractFile(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputName st return err } + +// TODO: dedupe this with lassie, probably into go-unixfsnode +func pathSegments(path string) ([]string, error) { + segments := strings.Split(path, "/") + filtered := make([]string, 0, len(segments)) + for i := 0; i < len(segments); i++ { + if segments[i] == "" { + // Allow one leading and one trailing '/' at most + if i == 0 || i == len(segments)-1 { + continue + } + return nil, fmt.Errorf("invalid empty path segment at position %d", i) + } + if segments[i] == "." || segments[i] == ".." { + return nil, fmt.Errorf("'%s' is unsupported in paths", segments[i]) + } + filtered = append(filtered, segments[i]) + } + return filtered, nil +} + +var _ storage.ReadableStorage = (*stdinReadStorage)(nil) + +type stdinReadStorage struct { + blocks map[string][]byte + done bool + lk *sync.RWMutex + cond *sync.Cond +} + +func NewStdinReadStorage(reader io.Reader) (*stdinReadStorage, []cid.Cid, error) { + var lk sync.RWMutex + srs := &stdinReadStorage{ + blocks: make(map[string][]byte), + lk: &lk, + cond: sync.NewCond(&lk), + } + rdr, err := car.NewBlockReader(reader) + if err != nil { + return nil, nil, err + } + go func() { + for { + blk, err := rdr.Next() + if err == io.EOF { + srs.lk.Lock() + srs.done = true + srs.lk.Unlock() + return + } + if err != nil { + panic(err) + } + srs.lk.Lock() + srs.blocks[string(blk.Cid().Hash())] = blk.RawData() + srs.cond.Broadcast() + srs.lk.Unlock() + } + }() + return srs, rdr.Roots, nil +} + +func (srs *stdinReadStorage) Has(ctx context.Context, key string) (bool, error) { + _, err := srs.Get(ctx, key) + if err != nil { + return false, err + } + return true, nil +} + +func (srs *stdinReadStorage) Get(ctx context.Context, key string) ([]byte, error) { + c, err := cid.Cast([]byte(key)) + if err != nil { + return nil, err + } + srs.lk.Lock() + defer srs.lk.Unlock() + for { + if data, ok := srs.blocks[string(c.Hash())]; ok { + return data, nil + } + if srs.done { + return nil, carstorage.ErrNotFound{Cid: c} + } + srs.cond.Wait() + } +} diff --git a/ipld/car/cmd/car/testdata/script/create-extract.txt b/ipld/car/cmd/car/testdata/script/create-extract.txt index 648bafc49..6dac510b2 100644 --- a/ipld/car/cmd/car/testdata/script/create-extract.txt +++ b/ipld/car/cmd/car/testdata/script/create-extract.txt @@ -1,8 +1,8 @@ car create --file=out.car foo.txt bar.txt mkdir out car extract -v -f out.car out -! stderr . stdout -count=2 'txt$' +stderr -count=1 '^extracted 2 file\(s\)$' car create --file=out2.car out/foo.txt out/bar.txt cmp out.car out2.car From 30034dabd956dc3eb10ef9d8d9751bb1d9d942c1 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 6 Mar 2023 22:49:12 +1100 Subject: [PATCH 3803/3817] feat: extract accepts '-' as an output path for stdout This commit was moved from ipld/go-car@ba7e4d732f81b98791ac3af04a9c71a372507cd5 --- ipld/car/cmd/car/car.go | 9 +++-- ipld/car/cmd/car/extract.go | 76 ++++++++++++++++++++++++------------- 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index 81cec5a38..7fb0a7156 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -71,10 +71,11 @@ func main1() int { }}, }, { - Name: "extract", - Aliases: []string{"x"}, - Usage: "Extract the contents of a car when the car encodes UnixFS data", - Action: ExtractCar, + Name: "extract", + Aliases: []string{"x"}, + Usage: "Extract the contents of a car when the car encodes UnixFS data", + Action: ExtractCar, + ArgsUsage: "[output directory|-]", Flags: []cli.Flag{ &cli.StringFlag{ Name: "file", diff --git a/ipld/car/cmd/car/extract.go b/ipld/car/cmd/car/extract.go index db4f207f7..b14d5cdb1 100644 --- a/ipld/car/cmd/car/extract.go +++ b/ipld/car/cmd/car/extract.go @@ -103,15 +103,19 @@ func extractRoot(c *cli.Context, ls *ipld.LinkSystem, root cid.Cid, outputDir st return 0, err } - outputResolvedDir, err := filepath.EvalSymlinks(outputDir) - if err != nil { - return 0, err - } - if _, err := os.Stat(outputResolvedDir); os.IsNotExist(err) { - if err := os.Mkdir(outputResolvedDir, 0755); err != nil { + var outputResolvedDir string + if outputDir != "-" { + outputResolvedDir, err = filepath.EvalSymlinks(outputDir) + if err != nil { return 0, err } + if _, err := os.Stat(outputResolvedDir); os.IsNotExist(err) { + if err := os.Mkdir(outputResolvedDir, 0755); err != nil { + return 0, err + } + } } + count, err := extractDir(c, ls, ufn, outputResolvedDir, "/", path) if err != nil { if !errors.Is(err, ErrNotDir) { @@ -131,8 +135,12 @@ func extractRoot(c *cli.Context, ls *ipld.LinkSystem, root cid.Cid, outputDir st if err != nil { return 0, err } + var outputName string + if outputDir != "-" { + outputName = filepath.Join(outputResolvedDir, "unknown") + } if ufsNode.DataType.Int() == data.Data_File || ufsNode.DataType.Int() == data.Data_Raw { - if err := extractFile(c, ls, pbnode, filepath.Join(outputResolvedDir, "unknown")); err != nil { + if err := extractFile(c, ls, pbnode, outputName); err != nil { return 0, err } } @@ -161,13 +169,15 @@ func resolvePath(root, pth string) (string, error) { } func extractDir(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputRoot, outputPath string, matchPath []string) (int, error) { - dirPath, err := resolvePath(outputRoot, outputPath) - if err != nil { - return 0, err - } - // make the directory. - if err := os.MkdirAll(dirPath, 0755); err != nil { - return 0, err + if outputRoot != "" { + dirPath, err := resolvePath(outputRoot, outputPath) + if err != nil { + return 0, err + } + // make the directory. + if err := os.MkdirAll(dirPath, 0755); err != nil { + return 0, err + } } if n.Kind() != ipld.Kind_Map { @@ -180,12 +190,16 @@ func extractDir(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputRoot, ou } extractElement := func(name string, n ipld.Node) (int, error) { - nextRes, err := resolvePath(outputRoot, path.Join(outputPath, name)) - if err != nil { - return 0, err - } - if c.IsSet("verbose") { - fmt.Fprintf(c.App.Writer, "%s\n", nextRes) + var nextRes string + if outputRoot != "" { + var err error + nextRes, err = resolvePath(outputRoot, path.Join(outputPath, name)) + if err != nil { + return 0, err + } + if c.IsSet("verbose") { + fmt.Fprintf(c.App.Writer, "%s\n", nextRes) + } } if n.Kind() != ipld.Kind_Link { @@ -246,6 +260,9 @@ func extractDir(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputRoot, ou } return 1, nil case data.Data_Symlink: + if nextRes == "" { + return 0, fmt.Errorf("cannot extract a symlink to stdout") + } data := ufsNode.Data.Must().Bytes() if err := os.Symlink(string(data), nextRes); err != nil { return 0, err @@ -265,6 +282,10 @@ func extractDir(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputRoot, ou return extractElement(matchPath[0], val) } + if outputPath == "-" && len(matchPath) == 0 { + return 0, fmt.Errorf("cannot extract a directory to stdout, use a path to extract a specific file") + } + // everything var count int mi := n.MapIterator() @@ -296,14 +317,17 @@ func extractFile(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputName st if err != nil { return err } - - f, err := os.Create(outputName) - if err != nil { - return err + var f *os.File + if outputName == "" { + f = os.Stdout + } else { + f, err = os.Create(outputName) + if err != nil { + return err + } + defer f.Close() } - defer f.Close() _, err = io.Copy(f, nlr) - return err } From e1368b197bc3a177bbba4b4bf2ef22f43fd02d21 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 7 Mar 2023 20:56:42 +1100 Subject: [PATCH 3804/3817] chore: add test cases for extract modes This commit was moved from ipld/go-car@5647641f655cc1880488b2559ff7c3d70a7222a0 --- ipld/car/cmd/car/extract.go | 2 +- .../inputs/simple-unixfs-missing-blocks.car | Bin 0 -> 1620 bytes .../cmd/car/testdata/inputs/simple-unixfs.car | Bin 0 -> 1933 bytes .../wikipedia-cryptographic-hash-function.car | Bin 0 -> 161731 bytes ipld/car/cmd/car/testdata/script/extract.txt | 97 ++++++++++++++++++ 5 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 ipld/car/cmd/car/testdata/inputs/simple-unixfs-missing-blocks.car create mode 100644 ipld/car/cmd/car/testdata/inputs/simple-unixfs.car create mode 100644 ipld/car/cmd/car/testdata/inputs/wikipedia-cryptographic-hash-function.car create mode 100644 ipld/car/cmd/car/testdata/script/extract.txt diff --git a/ipld/car/cmd/car/extract.go b/ipld/car/cmd/car/extract.go index b14d5cdb1..c742cf4c7 100644 --- a/ipld/car/cmd/car/extract.go +++ b/ipld/car/cmd/car/extract.go @@ -213,7 +213,7 @@ func extractDir(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputRoot, ou dest, err := ls.Load(ipld.LinkContext{}, vl, basicnode.Prototype.Any) if err != nil { if nf, ok := err.(interface{ NotFound() bool }); ok && nf.NotFound() { - fmt.Fprintf(c.App.ErrWriter, "data for directory entry not found: %s (skipping...)\n", name) + fmt.Fprintf(c.App.ErrWriter, "data for entry not found: %s (skipping...)\n", name) return 0, nil } return 0, err diff --git a/ipld/car/cmd/car/testdata/inputs/simple-unixfs-missing-blocks.car b/ipld/car/cmd/car/testdata/inputs/simple-unixfs-missing-blocks.car new file mode 100644 index 0000000000000000000000000000000000000000..7e808871384b1890dd492276eaa8ea5bf7cc151d GIT binary patch literal 1620 zcmcCmlv-Bl&LX1gpwNn3N zzhv)j&FHvo*SVu~E{`BPZ|~vgn4OZ79eytMefCp`F&U_qi;06#Pe|cO$QcLYh~0-B z>|!c8yepr~mX8-sGyZSazL(dP1AG(xgFZ+`Jwt}l( zEOj%J5TludJ;-07LJECnOiEE~qSM0~?+AzhF= z<8JdD`^f+BdcZ2l#d{nkm?x)(#2TH>eDFEKXzG#hbzVZO9(pAeB@#R!Q$gM*7-wKV z*;+44^3!?V`*|(<)O{UoiOhAeoZic9{JbJ5ay@ktmxyHSal{JI6s4b zpZykt;)&{olU}}6u05;%bKSq&r9zBGFr~3-qRA#kmtyyc)Hh3=vS0J9Ql&6&*PO54 zo^Pp{C3HVah|w70ePFZ_@)$H)Ej)nH3RJ#F+JD9-oeyieSj_fuDm@R8)Z^XZa`c<7 zs^~x0q{zZgQ03b{CYxZG3%!-vjl9n`Y)_0geo_30xAba9l;<0M;#$U zg;<>sQ3o_t+mUh&P(TQ=x}%s% zFs8w74RZBp$O|!V{I@B`FJ|F-ZthiWq6aE7zwVgv(D?MH?^QyqjtEmh$&6qY1}8Iu zUSc$dr5%FVfzbk{bXU61R)OdHJtOoouH5WlU-q13_HT=&F1PruUVnCagTA#8qa{3- z5zM?`?{5>{e|AOvSJkK#8<~GeF7i$@EvmDRa#l!hc){VwR^}$e>Wc6_FscY9bg-#} k5}^>QGs0A08YdJ9&{SyX2uXzmg8}RaLdB;Lt0%${0FM8K+yDRo literal 0 HcmV?d00001 diff --git a/ipld/car/cmd/car/testdata/inputs/simple-unixfs.car b/ipld/car/cmd/car/testdata/inputs/simple-unixfs.car new file mode 100644 index 0000000000000000000000000000000000000000..55b8a571a9478462f93b04e4164814d17f0b9853 GIT binary patch literal 1933 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�(AsN1(udpy5WJUk2 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 Mv0_(~>wfzG14P_fZvX%Q literal 0 HcmV?d00001 diff --git a/ipld/car/cmd/car/testdata/script/extract.txt b/ipld/car/cmd/car/testdata/script/extract.txt new file mode 100644 index 000000000..4e5061ef4 --- /dev/null +++ b/ipld/car/cmd/car/testdata/script/extract.txt @@ -0,0 +1,97 @@ +# full DAG export, everything in the CAR +mkdir actual-full +car extract -f ${INPUTS}/simple-unixfs.car actual-full +stderr '^extracted 9 file\(s\)$' +cmp actual-full/a/1/A.txt expected/a/1/A.txt +cmp actual-full/a/2/B.txt expected/a/2/B.txt +cmp actual-full/a/3/C.txt expected/a/3/C.txt +cmp actual-full/b/5/E.txt expected/b/5/E.txt +cmp actual-full/b/6/F.txt expected/b/6/F.txt +cmp actual-full/b/4/D.txt expected/b/4/D.txt +cmp actual-full/c/9/I.txt expected/c/9/I.txt +cmp actual-full/c/7/G.txt expected/c/7/G.txt +cmp actual-full/c/8/H.txt expected/c/8/H.txt + +# full DAG export, everything in the CAR, accepted from stdin +mkdir actual-stdin +stdin ${INPUTS}/simple-unixfs.car +car extract -f - actual-stdin +stderr '^extracted 9 file\(s\)$' +cmp actual-stdin/a/1/A.txt expected/a/1/A.txt +cmp actual-stdin/a/2/B.txt expected/a/2/B.txt +cmp actual-stdin/a/3/C.txt expected/a/3/C.txt +cmp actual-stdin/b/5/E.txt expected/b/5/E.txt +cmp actual-stdin/b/6/F.txt expected/b/6/F.txt +cmp actual-stdin/b/4/D.txt expected/b/4/D.txt +cmp actual-stdin/c/9/I.txt expected/c/9/I.txt +cmp actual-stdin/c/7/G.txt expected/c/7/G.txt +cmp actual-stdin/c/8/H.txt expected/c/8/H.txt + +# full DAG export, everything in the CAR, but the CAR is missing blocks (incomplete DAG) +mkdir actual-missing +car extract -f ${INPUTS}/simple-unixfs-missing-blocks.car actual-missing +stderr -count=1 'data for entry not found: 4 \(skipping\.\.\.\)' +stderr -count=1 'data for entry not found: E.txt \(skipping\.\.\.\)' +stderr -count=1 'data for entry not found: 6 \(skipping\.\.\.\)' +stderr -count=1 '^extracted 6 file\(s\)$' +cmp actual-missing/a/1/A.txt expected/a/1/A.txt +cmp actual-missing/a/2/B.txt expected/a/2/B.txt +cmp actual-missing/a/3/C.txt expected/a/3/C.txt +! exists actual-missing/b/5/E.txt +! exists actual-missing/b/6/F.txt +! exists actual-missing/b/4/D.txt +cmp actual-missing/c/9/I.txt expected/c/9/I.txt +cmp actual-missing/c/7/G.txt expected/c/7/G.txt +cmp actual-missing/c/8/H.txt expected/c/8/H.txt + +# path-based partial export, everything under the path specified (also without leading / in path) +mkdir actual-partial +car extract -f ${INPUTS}/simple-unixfs.car -p b actual-partial +stderr '^extracted 3 file\(s\)$' +! exists actual-partial/a/1/A.txt +! exists actual-partial/a/2/B.txt +! exists actual-partial/a/3/C.txt +cmp actual-partial/b/5/E.txt expected/b/5/E.txt +cmp actual-partial/b/6/F.txt expected/b/6/F.txt +cmp actual-partial/b/4/D.txt expected/b/4/D.txt +! exists actual-partial/c/9/I.txt +! exists actual-partial/c/7/G.txt +! exists actual-partial/c/8/H.txt + +# path-based single-file export (also with leading /) +mkdir actual-single +car extract -f ${INPUTS}/simple-unixfs.car -p /a/2/B.txt actual-single +stderr '^extracted 1 file\(s\)$' +! exists actual-single/a/1/A.txt +cmp actual-single/a/2/B.txt expected/a/2/B.txt +! exists actual-single/a/3/C.txt +! exists actual-single/b/5/E.txt +! exists actual-single/b/6/F.txt +! exists actual-single/b/4/D.txt +! exists actual-single/c/9/I.txt +! exists actual-single/c/7/G.txt +! exists actual-single/c/8/H.txt + +# car with only one file, nested inside sharded directory, output to stdout +car extract -f ${INPUTS}/wikipedia-cryptographic-hash-function.car -p wiki/Cryptographic_hash_function - +stderr '^extracted 1 file\(s\)$' +stdout -count=1 '^ Cryptographic hash function$' + +-- expected/a/1/A.txt -- +a1A +-- expected/a/2/B.txt -- +a2B +-- expected/a/3/C.txt -- +a3C +-- expected/b/5/E.txt -- +b5E +-- expected/b/6/F.txt -- +b6F +-- expected/b/4/D.txt -- +b4D +-- expected/c/9/I.txt -- +c9I +-- expected/c/7/G.txt -- +c7G +-- expected/c/8/H.txt -- +c8H \ No newline at end of file From 663fd91cd2297dfebc326bbd563440ed36c1b9f9 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 7 Mar 2023 21:04:17 +1100 Subject: [PATCH 3805/3817] fix: update cmd/car/README with latest description This commit was moved from ipld/go-car@5e5e40aaf3920be0359aec7680fc32d4ea9bc253 --- ipld/car/cmd/car/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ipld/car/cmd/car/README.md b/ipld/car/cmd/car/README.md index 901b75cb0..995850fd7 100644 --- a/ipld/car/cmd/car/README.md +++ b/ipld/car/cmd/car/README.md @@ -14,7 +14,9 @@ USAGE: car [global options] command [command options] [arguments...] COMMANDS: + compile compile a car file from a debug patch create, c Create a car file + debug debug a car file detach-index Detach an index to a detached file extract, x Extract the contents of a car when the car encodes UnixFS data filter, f Filter the CIDs in a car From 0771beee5e0f651a6a436fd2416a2bce89fd13a2 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 7 Mar 2023 21:47:30 +1100 Subject: [PATCH 3806/3817] fix: make -f optional, read from stdin if omitted This commit was moved from ipld/go-car@413fe0dcde5f3cd16f0b18aa59585517e23fee59 --- ipld/car/cmd/car/car.go | 4 ++-- ipld/car/cmd/car/extract.go | 19 ++++++++++++++++++- ipld/car/cmd/car/testdata/script/extract.txt | 2 +- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index 7fb0a7156..d2356484c 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -80,8 +80,8 @@ func main1() int { &cli.StringFlag{ Name: "file", Aliases: []string{"f"}, - Usage: "The car file to extract from, or '-' to read from stdin", - Required: true, + Usage: "The car file to extract from, or stdin if omitted", + Required: false, TakesFile: true, }, &cli.StringFlag{ diff --git a/ipld/car/cmd/car/extract.go b/ipld/car/cmd/car/extract.go index c742cf4c7..67c6620bd 100644 --- a/ipld/car/cmd/car/extract.go +++ b/ipld/car/cmd/car/extract.go @@ -8,6 +8,7 @@ import ( "os" "path" "path/filepath" + "runtime" "strings" "sync" @@ -40,7 +41,23 @@ func ExtractCar(c *cli.Context) error { var store storage.ReadableStorage var roots []cid.Cid - if c.String("file") == "-" { + if c.String("file") == "" { + if f, ok := c.App.Reader.(*os.File); ok { + stat, err := f.Stat() + if err != nil { + return err + } + if (stat.Mode() & os.ModeCharDevice) != 0 { + // Is a terminal. In reality the user is unlikely to actually paste + // CAR data into this terminal, but this message may serve to make + // them aware that they can/should pipe data into this command. + stopKeys := "Ctrl+D" + if runtime.GOOS == "windows" { + stopKeys = "Ctrl+Z, Enter" + } + fmt.Fprintf(c.App.ErrWriter, "Reading from stdin; use %s to end\n", stopKeys) + } + } var err error store, roots, err = NewStdinReadStorage(c.App.Reader) if err != nil { diff --git a/ipld/car/cmd/car/testdata/script/extract.txt b/ipld/car/cmd/car/testdata/script/extract.txt index 4e5061ef4..898579086 100644 --- a/ipld/car/cmd/car/testdata/script/extract.txt +++ b/ipld/car/cmd/car/testdata/script/extract.txt @@ -15,7 +15,7 @@ cmp actual-full/c/8/H.txt expected/c/8/H.txt # full DAG export, everything in the CAR, accepted from stdin mkdir actual-stdin stdin ${INPUTS}/simple-unixfs.car -car extract -f - actual-stdin +car extract actual-stdin stderr '^extracted 9 file\(s\)$' cmp actual-stdin/a/1/A.txt expected/a/1/A.txt cmp actual-stdin/a/2/B.txt expected/a/2/B.txt From 9127d28f3b487a82941a282c78932284621d7d96 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 7 Mar 2023 21:53:50 +1100 Subject: [PATCH 3807/3817] fix: error when no files extracted This commit was moved from ipld/go-car@2d5490984123d403b979b123aae7eaeabb92c1e9 --- ipld/car/cmd/car/extract.go | 2 +- ipld/car/cmd/car/testdata/script/extract.txt | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ipld/car/cmd/car/extract.go b/ipld/car/cmd/car/extract.go index 67c6620bd..d842b26a2 100644 --- a/ipld/car/cmd/car/extract.go +++ b/ipld/car/cmd/car/extract.go @@ -93,7 +93,7 @@ func ExtractCar(c *cli.Context) error { extractedFiles += count } if extractedFiles == 0 { - fmt.Fprintf(c.App.ErrWriter, "no files extracted\n") + return cli.Exit("no files extracted", 1) } else { fmt.Fprintf(c.App.ErrWriter, "extracted %d file(s)\n", extractedFiles) } diff --git a/ipld/car/cmd/car/testdata/script/extract.txt b/ipld/car/cmd/car/testdata/script/extract.txt index 898579086..7dd25aafb 100644 --- a/ipld/car/cmd/car/testdata/script/extract.txt +++ b/ipld/car/cmd/car/testdata/script/extract.txt @@ -72,6 +72,10 @@ cmp actual-single/a/2/B.txt expected/a/2/B.txt ! exists actual-single/c/7/G.txt ! exists actual-single/c/8/H.txt +# extract that doesn't yield any files should error +! car extract -f ${INPUTS}/simple-unixfs-missing-blocks.car -p b +stderr '^no files extracted$' + # car with only one file, nested inside sharded directory, output to stdout car extract -f ${INPUTS}/wikipedia-cryptographic-hash-function.car -p wiki/Cryptographic_hash_function - stderr '^extracted 1 file\(s\)$' From dc90d162f51e294d04f5efdc64d7e06bf28da770 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Mon, 27 Feb 2023 23:57:38 +0100 Subject: [PATCH 3808/3817] Allow using a NewWriteThrough() blockstore. Similar to blockservice.NewWriteThrough(). Currently the default blockstore trades read amplification for every write. While this is fine in cases where writes are very expensive and the datastore offers a quick Has() method, it is not always the case. Some datastore backends may be better off just getting all the writes no matter what. At least I would like to try it out. This commit was moved from ipfs/go-ipfs-blockstore@498084aa0095be077c5c590d806ddbb03c943ccc --- blockstore/blockstore.go | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 509e678f5..5f183cfc3 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -114,8 +114,22 @@ func NewBlockstore(d ds.Batching) Blockstore { dd := dsns.Wrap(d, BlockPrefix) dsb = dd return &blockstore{ - datastore: dsb, - rehash: uatomic.NewBool(false), + datastore: dsb, + rehash: uatomic.NewBool(false), + checkFirst: true, + } +} + +// NewWriteThrough returns a default Blockstore implementation +// which does not tries to check if blocks exist prior to writing. +func NewWriteThrough(d ds.Batching) Blockstore { + var dsb ds.Batching + dd := dsns.Wrap(d, BlockPrefix) + dsb = dd + return &blockstore{ + datastore: dsb, + rehash: uatomic.NewBool(false), + checkFirst: false, } } @@ -132,7 +146,8 @@ func NewBlockstoreNoPrefix(d ds.Batching) Blockstore { type blockstore struct { datastore ds.Batching - rehash *uatomic.Bool + rehash *uatomic.Bool + checkFirst bool } func (bs *blockstore) HashOnRead(enabled bool) { @@ -170,9 +185,11 @@ func (bs *blockstore) Put(ctx context.Context, block blocks.Block) error { k := dshelp.MultihashToDsKey(block.Cid().Hash()) // Has is cheaper than Put, so see if we already have it - exists, err := bs.datastore.Has(ctx, k) - if err == nil && exists { - return nil // already stored. + if bs.checkFirst { + exists, err := bs.datastore.Has(ctx, k) + if err == nil && exists { + return nil // already stored. + } } return bs.datastore.Put(ctx, k, block.RawData()) } @@ -189,9 +206,12 @@ func (bs *blockstore) PutMany(ctx context.Context, blocks []blocks.Block) error } for _, b := range blocks { k := dshelp.MultihashToDsKey(b.Cid().Hash()) - exists, err := bs.datastore.Has(ctx, k) - if err == nil && exists { - continue + + if bs.checkFirst { + exists, err := bs.datastore.Has(ctx, k) + if err == nil && exists { + continue + } } err = t.Put(ctx, k, b.RawData()) From 2cd3f887e0910aa3b29a10156833381d2d889651 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 8 Mar 2023 10:43:43 +0100 Subject: [PATCH 3809/3817] Accept options for blockstore: start with WriteThrough and NoPrefix This commit was moved from ipfs/go-ipfs-blockstore@7ad322b313506226f416ad1ae0421c775792103e --- blockstore/blockstore.go | 65 +++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 5f183cfc3..4d88ba54d 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -107,47 +107,56 @@ type gcBlockstore struct { GCLocker } -// NewBlockstore returns a default Blockstore implementation -// using the provided datastore.Batching backend. -func NewBlockstore(d ds.Batching) Blockstore { - var dsb ds.Batching - dd := dsns.Wrap(d, BlockPrefix) - dsb = dd - return &blockstore{ - datastore: dsb, - rehash: uatomic.NewBool(false), - checkFirst: true, +// Option is a default implementation Blockstore option +type Option struct { + f func(bs *blockstore) +} + +// WriteThrough skips checking if the blockstore already has a block before +// writing it. +func WriteThrough() Option { + return Option{ + func(bs *blockstore) { + bs.writeThrough = true + }, } } -// NewWriteThrough returns a default Blockstore implementation -// which does not tries to check if blocks exist prior to writing. -func NewWriteThrough(d ds.Batching) Blockstore { - var dsb ds.Batching - dd := dsns.Wrap(d, BlockPrefix) - dsb = dd - return &blockstore{ - datastore: dsb, - rehash: uatomic.NewBool(false), - checkFirst: false, +// NoPrefix avoids wrapping the blockstore into the BlockPrefix namespace +// ("/blocks"), so keys will not be modified in any way. +func NoPrefix() Option { + return Option{ + func(bs *blockstore) { + bs.noPrefix = true + }, } } -// NewBlockstoreNoPrefix returns a default Blockstore implementation +// NewBlockstore returns a default Blockstore implementation // using the provided datastore.Batching backend. -// This constructor does not modify input keys in any way -func NewBlockstoreNoPrefix(d ds.Batching) Blockstore { - return &blockstore{ +func NewBlockstore(d ds.Batching, opts ...Option) Blockstore { + bs := &blockstore{ datastore: d, rehash: uatomic.NewBool(false), } + + for _, o := range opts { + o.f(bs) + } + + if !bs.noPrefix { + dd := dsns.Wrap(d, BlockPrefix) + bs.datastore = dd + } + return bs } type blockstore struct { datastore ds.Batching - rehash *uatomic.Bool - checkFirst bool + rehash *uatomic.Bool + writeThrough bool + noPrefix bool } func (bs *blockstore) HashOnRead(enabled bool) { @@ -185,7 +194,7 @@ func (bs *blockstore) Put(ctx context.Context, block blocks.Block) error { k := dshelp.MultihashToDsKey(block.Cid().Hash()) // Has is cheaper than Put, so see if we already have it - if bs.checkFirst { + if !bs.writeThrough { exists, err := bs.datastore.Has(ctx, k) if err == nil && exists { return nil // already stored. @@ -207,7 +216,7 @@ func (bs *blockstore) PutMany(ctx context.Context, blocks []blocks.Block) error for _, b := range blocks { k := dshelp.MultihashToDsKey(b.Cid().Hash()) - if bs.checkFirst { + if !bs.writeThrough { exists, err := bs.datastore.Has(ctx, k) if err == nil && exists { continue From eeceee548e4b497be22116877752077987171e10 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Wed, 8 Mar 2023 11:21:47 +0100 Subject: [PATCH 3810/3817] feat: stub and deprecate NewBlockstoreNoPrefix This commit was moved from ipfs/go-ipfs-blockstore@1323a474b64ac660ba8d9991ba2dcdffd8b3d3a3 --- blockstore/blockstore.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/blockstore/blockstore.go b/blockstore/blockstore.go index 4d88ba54d..81fafe617 100644 --- a/blockstore/blockstore.go +++ b/blockstore/blockstore.go @@ -145,12 +145,20 @@ func NewBlockstore(d ds.Batching, opts ...Option) Blockstore { } if !bs.noPrefix { - dd := dsns.Wrap(d, BlockPrefix) - bs.datastore = dd + bs.datastore = dsns.Wrap(bs.datastore, BlockPrefix) } return bs } +// NewBlockstoreNoPrefix returns a default Blockstore implementation +// using the provided datastore.Batching backend. +// This constructor does not modify input keys in any way +// +// Deprecated: Use NewBlockstore with the NoPrefix option instead. +func NewBlockstoreNoPrefix(d ds.Batching) Blockstore { + return NewBlockstore(d, NoPrefix()) +} + type blockstore struct { datastore ds.Batching From fdfe12ca8806576f06fcc048f10a65b8467c5c8c Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Wed, 8 Mar 2023 18:28:30 +0100 Subject: [PATCH 3811/3817] feat: human-readable cache keys for IPFS_NS_MAP (#38) * feat: use peer.String() to use a human-readable cache key * fix: use normalized key representation * fix: cache ipns key can be any format This commit was moved from ipfs/go-namesys@af35385a1f71a71f75ab8007e1d4e5d7f04d8968 --- namesys/namesys.go | 31 ++++++++++++++++++++++++------- namesys/namesys_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/namesys/namesys.go b/namesys/namesys.go index 540aba4ea..256dc4293 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -24,6 +24,7 @@ import ( ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-path" + iface "github.com/ipfs/interface-go-ipfs-core" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" ci "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" @@ -87,6 +88,23 @@ func WithDatastore(ds ds.Datastore) Option { } } +func loadStaticMap(list string) (map[string]path.Path, error) { + staticMap := make(map[string]path.Path) + for _, pair := range strings.Split(list, ",") { + mapping := strings.SplitN(pair, ":", 2) + key := mapping[0] + value := path.FromString(mapping[1]) + + ipnsKey, err := peer.Decode(key) + if err == nil { + key = iface.FormatKeyID(ipnsKey) + } + + staticMap[key] = value + } + return staticMap, nil +} + // NewNameSystem will construct the IPFS naming system based on Routing func NewNameSystem(r routing.ValueStore, opts ...Option) (NameSystem, error) { var staticMap map[string]path.Path @@ -96,12 +114,11 @@ func NewNameSystem(r routing.ValueStore, opts ...Option) (NameSystem, error) { // Example: // IPFS_NS_MAP="dnslink-test.example.com:/ipfs/bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am" if list := os.Getenv("IPFS_NS_MAP"); list != "" { - staticMap = make(map[string]path.Path) - for _, pair := range strings.Split(list, ",") { - mapping := strings.SplitN(pair, ":", 2) - key := mapping[0] - value := path.FromString(mapping[1]) - staticMap[key] = value + var err error + staticMap, err = loadStaticMap(list) + + if err != nil { + return nil, err } } @@ -215,7 +232,7 @@ func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts. cacheKey := key if err == nil { - cacheKey = string(ipnsKey) + cacheKey = iface.FormatKeyID(ipnsKey) } if p, ok := ns.cacheGet(cacheKey); ok { diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index c641d4911..3441f4106 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -88,6 +88,32 @@ func TestNamesysResolution(t *testing.T) { testResolution(t, r, "/ipns/bafzbeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", 1, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", ErrResolveRecursion) } +func TestNamesysResolutionWithCache(t *testing.T) { + nsMap := "dnslink-test.example.com:/ipfs/bafyaaeykceeaeeqlgiydemzngazc2mrtbimaw,12D3KooWQbpsnyzdBcxw6GUMbijV8WgXE4L8EtfnbcQWLfyxBKho:/ipfs/bafyaagakcyeaeeqqgiydemzngazc2mrtfvuxa3ttbimba,k51qzi5uqu5dkwkqm42v9j9kqcam2jiuvloi16g72i4i4amoo2m8u3ol3mqu6s:/ipfs/bafyaahikdmeaeeqvgiydemzngazc2mrtfvuxa3ttfvsgk5lybimbk" + + staticMap, err := loadStaticMap(nsMap) + if err != nil { + t.Fatal(err) + } + + r := &mpns{ + ipnsResolver: mockResolverOne(), + dnsResolver: mockResolverTwo(), + staticMap: staticMap, + } + + testResolution(t, r, "/ipns/dnslink-test.example.com", opts.DefaultDepthLimit, "/ipfs/bafyaaeykceeaeeqlgiydemzngazc2mrtbimaw", nil) + + testResolution(t, r, "/ipns/bafzaajaiaejcbw5i6oyqsktsn36r2vxgl2jzosyao46rybqztxt4rx4tfa3hpogg", opts.DefaultDepthLimit, "/ipfs/bafyaagakcyeaeeqqgiydemzngazc2mrtfvuxa3ttbimba", nil) + testResolution(t, r, "/ipns/k51qzi5uqu5dlnojhwrggtpty9c0cp5hvnkdozowth4eqb726jvoros8k9niyu", opts.DefaultDepthLimit, "/ipfs/bafyaagakcyeaeeqqgiydemzngazc2mrtfvuxa3ttbimba", nil) + testResolution(t, r, "/ipns/12D3KooWQbpsnyzdBcxw6GUMbijV8WgXE4L8EtfnbcQWLfyxBKho", opts.DefaultDepthLimit, "/ipfs/bafyaagakcyeaeeqqgiydemzngazc2mrtfvuxa3ttbimba", nil) + + testResolution(t, r, "/ipns/bafzaajaiaejcbpltl72da5f3y7ojrtsa7hsfn5bbnkjbkwyesziqqtdry6vjilku", opts.DefaultDepthLimit, "/ipfs/bafyaahikdmeaeeqvgiydemzngazc2mrtfvuxa3ttfvsgk5lybimbk", nil) + testResolution(t, r, "/ipns/k51qzi5uqu5dkwkqm42v9j9kqcam2jiuvloi16g72i4i4amoo2m8u3ol3mqu6s", opts.DefaultDepthLimit, "/ipfs/bafyaahikdmeaeeqvgiydemzngazc2mrtfvuxa3ttfvsgk5lybimbk", nil) + testResolution(t, r, "/ipns/12D3KooWNZuG8phqhoNK9KWcUhwfzA3biDKNCUNVWEaJgigr6Acj", opts.DefaultDepthLimit, "/ipfs/bafyaahikdmeaeeqvgiydemzngazc2mrtfvuxa3ttfvsgk5lybimbk", nil) + +} + func TestPublishWithCache0(t *testing.T) { dst := dssync.MutexWrap(ds.NewMapDatastore()) priv, _, err := ci.GenerateKeyPair(ci.RSA, 2048) From a8700eb893b4d63c7ec4cd90aa20b7bd8a57cfef Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 8 Mar 2023 18:20:58 +0100 Subject: [PATCH 3812/3817] docs: improved DNSLink lookup error - updates docs link to .tech - makes it very clear what is missing This commit was moved from ipfs/go-namesys@e30a7b84705484a67d9b378a541f5242bfd19776 --- namesys/dns.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namesys/dns.go b/namesys/dns.go index ba1906162..a47e380a7 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -133,7 +133,7 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options // dnslink, then output a more specific error message if rootResErr == ErrResolveFailed && subResErr == ErrResolveFailed { // Wrap error so that it can be tested if it is a ErrResolveFailed - err := fmt.Errorf("%w: %q is missing a DNSLink record (https://docs.ipfs.io/concepts/dnslink/)", ErrResolveFailed, gpath.Base(name)) + err := fmt.Errorf("%w: _dnslink subdomain at %q is missing a TXT record (https://docs.ipfs.tech/concepts/dnslink/)", ErrResolveFailed, gpath.Base(name)) emitOnceResult(ctx, out, onceResult{err: err}) } return From 8a1244426508c2b580cacfabf90bf52431001903 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Wed, 8 Mar 2023 15:01:31 +1100 Subject: [PATCH 3813/3817] fix: let `extract` skip missing unixfs shard links allows non-path "full" extraction of a unixfs CAR that doesn't have complete data Ref: https://github.com/ipfs/go-unixfsnode/pull/44 This commit was moved from ipld/go-car@22c855bbc572a02b0e6b85ac6945259ec20d43c3 --- ipld/car/cmd/car/extract.go | 11 +++++++++-- ipld/car/cmd/car/testdata/script/extract.txt | 18 +++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/ipld/car/cmd/car/extract.go b/ipld/car/cmd/car/extract.go index d842b26a2..f9373fd37 100644 --- a/ipld/car/cmd/car/extract.go +++ b/ipld/car/cmd/car/extract.go @@ -230,7 +230,7 @@ func extractDir(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputRoot, ou dest, err := ls.Load(ipld.LinkContext{}, vl, basicnode.Prototype.Any) if err != nil { if nf, ok := err.(interface{ NotFound() bool }); ok && nf.NotFound() { - fmt.Fprintf(c.App.ErrWriter, "data for entry not found: %s (skipping...)\n", name) + fmt.Fprintf(c.App.ErrWriter, "data for entry not found: %s (skipping...)\n", path.Join(outputPath, name)) return 0, nil } return 0, err @@ -305,10 +305,15 @@ func extractDir(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputRoot, ou // everything var count int + var shardSkip int mi := n.MapIterator() for !mi.Done() { key, val, err := mi.Next() if err != nil { + if nf, ok := err.(interface{ NotFound() bool }); ok && nf.NotFound() { + shardSkip++ + continue + } return 0, err } ks, err := key.AsString() @@ -321,7 +326,9 @@ func extractDir(c *cli.Context, ls *ipld.LinkSystem, n ipld.Node, outputRoot, ou } count += ecount } - + if shardSkip > 0 { + fmt.Fprintf(c.App.ErrWriter, "data for entry not found for %d unknown sharded entries (skipped...)\n", shardSkip) + } return count, nil } diff --git a/ipld/car/cmd/car/testdata/script/extract.txt b/ipld/car/cmd/car/testdata/script/extract.txt index 7dd25aafb..aa2713beb 100644 --- a/ipld/car/cmd/car/testdata/script/extract.txt +++ b/ipld/car/cmd/car/testdata/script/extract.txt @@ -30,9 +30,9 @@ cmp actual-stdin/c/8/H.txt expected/c/8/H.txt # full DAG export, everything in the CAR, but the CAR is missing blocks (incomplete DAG) mkdir actual-missing car extract -f ${INPUTS}/simple-unixfs-missing-blocks.car actual-missing -stderr -count=1 'data for entry not found: 4 \(skipping\.\.\.\)' -stderr -count=1 'data for entry not found: E.txt \(skipping\.\.\.\)' -stderr -count=1 'data for entry not found: 6 \(skipping\.\.\.\)' +stderr -count=1 'data for entry not found: /b/4 \(skipping\.\.\.\)' +stderr -count=1 'data for entry not found: /b/5/E.txt \(skipping\.\.\.\)' +stderr -count=1 'data for entry not found: /b/6 \(skipping\.\.\.\)' stderr -count=1 '^extracted 6 file\(s\)$' cmp actual-missing/a/1/A.txt expected/a/1/A.txt cmp actual-missing/a/2/B.txt expected/a/2/B.txt @@ -81,6 +81,18 @@ car extract -f ${INPUTS}/wikipedia-cryptographic-hash-function.car -p wiki/Crypt stderr '^extracted 1 file\(s\)$' stdout -count=1 '^ Cryptographic hash function$' +# car with only one file, full extract, lots of errors +mkdir actual-wiki +car extract -f ${INPUTS}/wikipedia-cryptographic-hash-function.car actual-wiki +stderr '^extracted 1 file\(s\)$' +stderr -count=1 '^data for entry not found for 570 unknown sharded entries \(skipped\.\.\.\)$' +# random sampling of expected skip errors +stderr -count=1 '^data for entry not found: /wiki/1969_Men''s_World_Ice_Hockey_Championships \(skipping\.\.\.\)$' +stderr -count=1 '^data for entry not found: /wiki/Wrestle_mania_30 \(skipping\.\.\.\)$' +stderr -count=1 '^data for entry not found: /zimdump_version \(skipping\.\.\.\)$' +stderr -count=1 '^data for entry not found: /favicon.ico \(skipping\.\.\.\)$' +stderr -count=1 '^data for entry not found: /index.html \(skipping\.\.\.\)$' + -- expected/a/1/A.txt -- a1A -- expected/a/2/B.txt -- From 040544995144393afef587c485ddcf9125525b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Tue, 14 Mar 2023 12:57:44 +0100 Subject: [PATCH 3814/3817] ReadWrite: faster Has() by using the in-memory index instead of reading on disk Before: // BenchmarkHas-8 190216 6368 ns/op 744 B/op 16 allocs/op After // BenchmarkHas-8 1419169 845.6 ns/op 320 B/op 6 allocs/op ``` func BenchmarkHas(b *testing.B) { ctx := context.TODO() path := filepath.Join(b.TempDir(), "bench-large-v2.car") generateRandomCarV2File(b, path, 200<<20) // 10 MiB defer os.Remove(path) subject, err := blockstore.OpenReadWrite(path, nil) c, err := subject.AllKeysChan(ctx) require.NoError(b, err) var allCids []cid.Cid for c2 := range c { allCids = append(allCids, c2) } b.ReportAllocs() b.ResetTimer() var idx int for i := 0; i < b.N; i++ { _, _ = subject.Has(ctx, allCids[idx]) // require.NoError(b, err) // require.True(b, has) idx = (idx + 1) % len(allCids) } } ``` This commit was moved from ipld/go-car@d51b4a127544b128806aea86062f4bc7bc7b3776 --- ipld/car/v2/blockstore/readwrite.go | 24 ++++++++++++++- ipld/car/v2/index/index.go | 6 ++-- ipld/car/v2/internal/store/insertionindex.go | 31 ++++++++++++++++++-- ipld/car/v2/internal/store/put.go | 8 +++-- 4 files changed, 60 insertions(+), 9 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index e605414f2..5ba24f9c5 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -344,7 +344,29 @@ func (b *ReadWrite) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { } func (b *ReadWrite) Has(ctx context.Context, key cid.Cid) (bool, error) { - return b.ronly.Has(ctx, key) + if !b.opts.StoreIdentityCIDs { + // If we don't store identity CIDs then we can return them straight away as if they are here, + // otherwise we need to check for their existence. + // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. + if _, ok, err := store.IsIdentity(key); err != nil { + return false, err + } else if ok { + return true, nil + } + } + + if ctx.Err() != nil { + return false, ctx.Err() + } + + b.ronly.mu.Lock() + defer b.ronly.mu.Unlock() + + if b.ronly.closed { + return false, errClosed + } + + return b.idx.HasMultihash(key.Hash()) } func (b *ReadWrite) Get(ctx context.Context, key cid.Cid) (blocks.Block, error) { diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 911eaa7ae..1634dda5d 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -13,7 +13,7 @@ import ( "github.com/multiformats/go-varint" ) -// CarIndexNone is a sentinal value used as a multicodec code for the index indicating no index. +// CarIndexNone is a sentinel value used as a multicodec code for the index indicating no index. const CarIndexNone = 0x300000 type ( @@ -46,7 +46,7 @@ type ( // Unmarshal decodes the index from its serial form. // Note, this function will copy the entire index into memory. // - // Do not unmarshal index from untrusted CARv2 files. Instead the index should be + // Do not unmarshal index from untrusted CARv2 files. Instead, the index should be // regenerated from the CARv2 data payload. Unmarshal(r io.Reader) error @@ -84,7 +84,7 @@ type ( // and the ForEach function returns the error to the user. // // An index may contain multiple offsets corresponding to the same multihash, e.g. via duplicate blocks. - // In such cases, the given function may be called multiple times with the same multhihash but different offset. + // In such cases, the given function may be called multiple times with the same multihash but different offset. // // The order of calls to the given function is deterministic, but entirely index-specific. ForEach(func(multihash.Multihash, uint64) error) error diff --git a/ipld/car/v2/internal/store/insertionindex.go b/ipld/car/v2/internal/store/insertionindex.go index ffdf96fa4..bc25fffbc 100644 --- a/ipld/car/v2/internal/store/insertionindex.go +++ b/ipld/car/v2/internal/store/insertionindex.go @@ -212,10 +212,10 @@ func (ii *InsertionIndex) Flatten(codec multicodec.Code) (index.Index, error) { // but it's separate as it allows us to compare Record.Cid directly, // whereas GetAll just provides Record.Offset. -func (ii *InsertionIndex) HasExactCID(c cid.Cid) bool { +func (ii *InsertionIndex) HasExactCID(c cid.Cid) (bool, error) { d, err := multihash.Decode(c.Hash()) if err != nil { - panic(err) + return false, err } entry := recordDigest{digest: d.Digest} @@ -235,5 +235,30 @@ func (ii *InsertionIndex) HasExactCID(c cid.Cid) bool { return true } ii.items.AscendGreaterOrEqual(entry, iter) - return found + return found, nil +} + +func (ii *InsertionIndex) HasMultihash(mh multihash.Multihash) (bool, error) { + d, err := multihash.Decode(mh) + if err != nil { + return false, err + } + entry := recordDigest{digest: d.Digest} + + found := false + iter := func(i llrb.Item) bool { + existing := i.(recordDigest) + if !bytes.Equal(existing.digest, entry.digest) { + // We've already looked at all entries with matching digests. + return false + } + if bytes.Equal(existing.Record.Cid.Hash(), mh) { + found = true + return false + } + // Continue looking in ascending order. + return true + } + ii.items.AscendGreaterOrEqual(entry, iter) + return found, nil } diff --git a/ipld/car/v2/internal/store/put.go b/ipld/car/v2/internal/store/put.go index f82d0c1ea..2a74dd0d0 100644 --- a/ipld/car/v2/internal/store/put.go +++ b/ipld/car/v2/internal/store/put.go @@ -38,8 +38,12 @@ func ShouldPut( } if !blockstoreAllowDuplicatePuts { - if blockstoreUseWholeCIDs && idx.HasExactCID(c) { - return false, nil // deduplicated by CID + if blockstoreUseWholeCIDs { + has, err := idx.HasExactCID(c) + if err != nil { + return false, err + } + return !has, nil // deduplicated by CID } if !blockstoreUseWholeCIDs { _, err := idx.Get(c) From ca32fefcd1a543897aeeeda7eac6f759ac3429ec Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 17 Mar 2023 17:34:41 +1100 Subject: [PATCH 3815/3817] fix: handle (and test) WholeCID vs not; fast Has() path for storage This commit was moved from ipld/go-car@c58b233d27e147e4adefa0388a8190102d4e8bf8 --- ipld/car/v2/blockstore/readwrite.go | 24 +++----- ipld/car/v2/blockstore/readwrite_test.go | 50 +++++++++++++++++ .../internal/store/{put.go => indexcheck.go} | 27 +++++++++ ipld/car/v2/storage/storage.go | 26 ++++++--- ipld/car/v2/storage/storage_test.go | 55 +++++++++++++++++++ 5 files changed, 159 insertions(+), 23 deletions(-) rename ipld/car/v2/internal/store/{put.go => indexcheck.go} (70%) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 5ba24f9c5..85c564978 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -344,21 +344,6 @@ func (b *ReadWrite) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { } func (b *ReadWrite) Has(ctx context.Context, key cid.Cid) (bool, error) { - if !b.opts.StoreIdentityCIDs { - // If we don't store identity CIDs then we can return them straight away as if they are here, - // otherwise we need to check for their existence. - // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. - if _, ok, err := store.IsIdentity(key); err != nil { - return false, err - } else if ok { - return true, nil - } - } - - if ctx.Err() != nil { - return false, ctx.Err() - } - b.ronly.mu.Lock() defer b.ronly.mu.Unlock() @@ -366,7 +351,14 @@ func (b *ReadWrite) Has(ctx context.Context, key cid.Cid) (bool, error) { return false, errClosed } - return b.idx.HasMultihash(key.Hash()) + return store.Has( + b.idx, + key, + b.opts.MaxIndexCidSize, + b.opts.StoreIdentityCIDs, + b.opts.BlockstoreAllowDuplicatePuts, + b.opts.BlockstoreUseWholeCIDs, + ) } func (b *ReadWrite) Get(ctx context.Context, key cid.Cid) (blocks.Block, error) { diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 45fcb21ac..5766c2d3b 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -1085,3 +1085,53 @@ func TestBlockstoreFinalizeReadOnly(t *testing.T) { err = bs.Put(ctx, root) require.Error(t, err) } + +func TestWholeCID(t *testing.T) { + for _, whole := range []bool{true, false} { + whole := whole + t.Run(fmt.Sprintf("whole=%t", whole), func(t *testing.T) { + t.Parallel() + ctx := context.Background() + path := filepath.Join(t.TempDir(), fmt.Sprintf("writable_%t.car", whole)) + rw, err := blockstore.OpenReadWrite(path, []cid.Cid{}, carv2.UseWholeCIDs(whole)) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, rw.Finalize()) }) + + require.NoError(t, rw.Put(ctx, oneTestBlockWithCidV1)) + has, err := rw.Has(ctx, oneTestBlockWithCidV1.Cid()) + require.NoError(t, err) + require.True(t, has) + + pref := oneTestBlockWithCidV1.Cid().Prefix() + pref.Codec = cid.DagCBOR + pref.Version = 1 + cpb1, err := pref.Sum(oneTestBlockWithCidV1.RawData()) + require.NoError(t, err) + + has, err = rw.Has(ctx, cpb1) + require.NoError(t, err) + require.Equal(t, has, !whole) + + require.NoError(t, rw.Put(ctx, anotherTestBlockWithCidV0)) + has, err = rw.Has(ctx, anotherTestBlockWithCidV0.Cid()) + require.NoError(t, err) + require.True(t, has) + has, err = rw.Has(ctx, cpb1) + require.NoError(t, err) + require.Equal(t, has, !whole) + + pref = anotherTestBlockWithCidV0.Cid().Prefix() + pref.Codec = cid.DagJSON + pref.Version = 1 + cpb2, err := pref.Sum(anotherTestBlockWithCidV0.RawData()) + require.NoError(t, err) + + has, err = rw.Has(ctx, cpb2) + require.NoError(t, err) + require.Equal(t, has, !whole) + has, err = rw.Has(ctx, cpb1) + require.NoError(t, err) + require.Equal(t, has, !whole) + }) + } +} diff --git a/ipld/car/v2/internal/store/put.go b/ipld/car/v2/internal/store/indexcheck.go similarity index 70% rename from ipld/car/v2/internal/store/put.go rename to ipld/car/v2/internal/store/indexcheck.go index 2a74dd0d0..04b673e05 100644 --- a/ipld/car/v2/internal/store/put.go +++ b/ipld/car/v2/internal/store/indexcheck.go @@ -55,3 +55,30 @@ func ShouldPut( return true, nil } + +// Has returns true if the block exists in in the store according to the various +// rules associated with the options. Similar to ShouldPut, but for the simpler +// Has() case. +func Has( + idx *InsertionIndex, + c cid.Cid, + maxIndexCidSize uint64, + storeIdentityCIDs bool, + blockstoreAllowDuplicatePuts bool, + blockstoreUseWholeCIDs bool, +) (bool, error) { + + // If StoreIdentityCIDs option is disabled then treat IDENTITY CIDs like IdStore. + if !storeIdentityCIDs { + if _, ok, err := IsIdentity(c); err != nil { + return false, err + } else if ok { + return true, nil + } + } + + if blockstoreUseWholeCIDs { + return idx.HasExactCID(c) + } + return idx.HasMultihash(c.Hash()) +} diff --git a/ipld/car/v2/storage/storage.go b/ipld/car/v2/storage/storage.go index a6f92ae22..1a8b499b9 100644 --- a/ipld/car/v2/storage/storage.go +++ b/ipld/car/v2/storage/storage.go @@ -349,6 +349,25 @@ func (sc *StorageCar) Has(ctx context.Context, keyStr string) (bool, error) { return false, fmt.Errorf("bad CID key: %w", err) } + sc.mu.RLock() + defer sc.mu.RUnlock() + + if sc.closed { + return false, errClosed + } + + if idx, ok := sc.idx.(*store.InsertionIndex); ok && sc.writer != nil { + // writable CAR, fast path using InsertionIndex + return store.Has( + idx, + keyCid, + sc.opts.MaxIndexCidSize, + sc.opts.StoreIdentityCIDs, + sc.opts.BlockstoreAllowDuplicatePuts, + sc.opts.BlockstoreUseWholeCIDs, + ) + } + if !sc.opts.StoreIdentityCIDs { // If we don't store identity CIDs then we can return them straight away as if they are here, // otherwise we need to check for their existence. @@ -360,13 +379,6 @@ func (sc *StorageCar) Has(ctx context.Context, keyStr string) (bool, error) { } } - sc.mu.RLock() - defer sc.mu.RUnlock() - - if sc.closed { - return false, errClosed - } - _, _, size, err := store.FindCid( sc.reader, sc.idx, diff --git a/ipld/car/v2/storage/storage_test.go b/ipld/car/v2/storage/storage_test.go index ea00f1d5e..12b9ebce3 100644 --- a/ipld/car/v2/storage/storage_test.go +++ b/ipld/car/v2/storage/storage_test.go @@ -1156,6 +1156,61 @@ func TestOperationsErrorWithBadCidStrings(t *testing.T) { require.Nil(t, got) } +func TestWholeCID(t *testing.T) { + for _, whole := range []bool{true, false} { + whole := whole + t.Run(fmt.Sprintf("whole=%t", whole), func(t *testing.T) { + t.Parallel() + ctx := context.Background() + path := filepath.Join(t.TempDir(), fmt.Sprintf("writable_%t.car", whole)) + out, err := os.Create(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, out.Close()) }) + store, err := storage.NewReadableWritable(out, []cid.Cid{}, carv2.UseWholeCIDs(whole)) + require.NoError(t, err) + + c1, b1 := randBlock() + c2, b2 := randBlock() + + require.NoError(t, store.Put(ctx, c1.KeyString(), b1)) + has, err := store.Has(ctx, c1.KeyString()) + require.NoError(t, err) + require.True(t, has) + + pref := c1.Prefix() + pref.Codec = cid.DagProtobuf + pref.Version = 1 + cpb1, err := pref.Sum(b1) + require.NoError(t, err) + + has, err = store.Has(ctx, cpb1.KeyString()) + require.NoError(t, err) + require.Equal(t, has, !whole) + + require.NoError(t, store.Put(ctx, c2.KeyString(), b1)) + has, err = store.Has(ctx, c2.KeyString()) + require.NoError(t, err) + require.True(t, has) + has, err = store.Has(ctx, cpb1.KeyString()) + require.NoError(t, err) + require.Equal(t, has, !whole) + + pref = c2.Prefix() + pref.Codec = cid.DagProtobuf + pref.Version = 1 + cpb2, err := pref.Sum(b2) + require.NoError(t, err) + + has, err = store.Has(ctx, cpb2.KeyString()) + require.NoError(t, err) + require.Equal(t, has, !whole) + has, err = store.Has(ctx, cpb1.KeyString()) + require.NoError(t, err) + require.Equal(t, has, !whole) + }) + } +} + type writerOnly struct { io.Writer } From 4cb37910dd3f937456ec8c521831583eefc45ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Thu, 16 Mar 2023 16:20:04 +0100 Subject: [PATCH 3816/3817] blockstore: only close the file on error in OpenReadWrite, not OpenReadWriteFile OpenReadWriteFile gives full control of the file to the caller, so it should not decide to close the file, even on error. This commit was moved from ipld/go-car@794f22231c0526e1123ee2a11bbc1567f9a011ae --- ipld/car/v2/blockstore/readwrite.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 85c564978..0f9cd4cae 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -86,6 +86,12 @@ func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.Option) (*ReadWri if err != nil { return nil, fmt.Errorf("could not open read/write file: %w", err) } + // If construction of blockstore fails, make sure to close off the open file. + defer func() { + if err != nil { + f.Close() + } + }() rwbs, err := OpenReadWriteFile(f, roots, opts...) if err != nil { return nil, err @@ -100,17 +106,11 @@ func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.Option) (*ReadWri func OpenReadWriteFile(f *os.File, roots []cid.Cid, opts ...carv2.Option) (*ReadWrite, error) { stat, err := f.Stat() if err != nil { - // Note, we should not get a an os.ErrNotExist here because the flags used to open file includes os.O_CREATE + // Note, we should not get an os.ErrNotExist here because the flags used to open file includes os.O_CREATE return nil, err } // Try and resume by default if the file size is non-zero. resume := stat.Size() != 0 - // If construction of blockstore fails, make sure to close off the open file. - defer func() { - if err != nil { - f.Close() - } - }() // Instantiate block store. // Set the header fileld before applying options since padding options may modify header. From c270c83fbd8be200d709fa4b7cc71d7a0ff31e9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Fri, 10 Mar 2023 18:13:35 +0100 Subject: [PATCH 3817/3817] blockstore: give a direct access to the index for read operations This commit was moved from ipld/go-car@2f5967471b1b5b74ccb07b4283b95a241b717b13 --- ipld/car/v2/blockstore/readonly.go | 6 +++ ipld/car/v2/blockstore/readonly_test.go | 67 +++++++++++++++++++++++- ipld/car/v2/blockstore/readwrite.go | 7 +++ ipld/car/v2/blockstore/readwrite_test.go | 48 +++++++++++++++-- ipld/car/v2/index/index.go | 2 +- 5 files changed, 123 insertions(+), 7 deletions(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index c59bdcf68..a102b20c4 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -179,6 +179,12 @@ func OpenReadOnly(path string, opts ...carv2.Option) (*ReadOnly, error) { return robs, nil } +// Index gives direct access to the index. +// You should never add records on your own there. +func (b *ReadOnly) Index() index.Index { + return b.idx +} + // DeleteBlock is unsupported and always errors. func (b *ReadOnly) DeleteBlock(_ context.Context, _ cid.Cid) error { return errReadOnly diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 0bb4df42e..f2bc7f105 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -12,10 +12,14 @@ import ( format "github.com/ipfs/go-ipld-format" blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipfs/go-merkledag" - carv2 "github.com/ipld/go-car/v2" - "github.com/ipld/go-car/v2/internal/carv1" "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-multihash" "github.com/stretchr/testify/require" + + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-car/v2/internal/carv1" + "github.com/ipld/go-car/v2/internal/store" ) func TestReadOnlyGetReturnsBlockstoreNotFoundWhenCidDoesNotExist(t *testing.T) { @@ -331,3 +335,62 @@ func TestNewReadOnly_CarV1WithoutIndexWorksAsExpected(t *testing.T) { require.NoError(t, err) require.Equal(t, wantBlock, gotBlock) } + +func TestReadOnlyIndex(t *testing.T) { + tests := []struct { + name string + path string + wantCIDs []cid.Cid + }{ + { + "IndexCarV1", + "../testdata/sample-v1.car", + listCids(t, newV1ReaderFromV1File(t, "../testdata/sample-v1.car", false)), + }, + { + "IndexCarV2", + "../testdata/sample-wrapped-v2.car", + listCids(t, newV1ReaderFromV2File(t, "../testdata/sample-wrapped-v2.car", false)), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + subject, err := OpenReadOnly(tt.path, UseWholeCIDs(true)) + require.NoError(t, err) + + idx := subject.Index() + + for _, c := range tt.wantCIDs { + _, isIdentity, err := store.IsIdentity(c) + require.NoError(t, err) + if isIdentity { + // the index doesn't hold identity CIDs + continue + } + _, err = index.GetFirst(idx, c) + require.NoError(t, err) + } + + if idx, ok := idx.(index.IterableIndex); ok { + expected := make([]multihash.Multihash, 0, len(tt.wantCIDs)) + for _, c := range tt.wantCIDs { + _, isIdentity, err := store.IsIdentity(c) + require.NoError(t, err) + if isIdentity { + // the index doesn't hold identity CIDs + continue + } + expected = append(expected, c.Hash()) + } + + var got []multihash.Multihash + err = idx.ForEach(func(m multihash.Multihash, u uint64) error { + got = append(got, m) + return nil + }) + require.NoError(t, err) + require.ElementsMatch(t, expected, got) + } + }) + } +} diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 0f9cd4cae..e33f13104 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -10,6 +10,7 @@ import ( blocks "github.com/ipfs/go-libipfs/blocks" carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/carv1/util" internalio "github.com/ipld/go-car/v2/internal/io" @@ -178,6 +179,12 @@ func (b *ReadWrite) initWithRoots(v2 bool, roots []cid.Cid) error { return carv1.WriteHeader(&carv1.CarHeader{Roots: roots, Version: 1}, b.dataWriter) } +// Index gives direct access to the index. +// You should never add records on your own there. +func (b *ReadWrite) Index() index.Index { + return b.idx +} + // Put puts a given block to the underlying datastore func (b *ReadWrite) Put(ctx context.Context, blk blocks.Block) error { // PutMany already checks b.ronly.closed. diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 5766c2d3b..40731e548 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -18,14 +18,15 @@ import ( format "github.com/ipfs/go-ipld-format" blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipfs/go-merkledag" - carv2 "github.com/ipld/go-car/v2" - "github.com/ipld/go-car/v2/blockstore" - "github.com/ipld/go-car/v2/index" - "github.com/ipld/go-car/v2/internal/carv1" "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/blockstore" + "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-car/v2/internal/carv1" ) var ( @@ -1135,3 +1136,42 @@ func TestWholeCID(t *testing.T) { }) } } + +func TestReadWriteIndex(t *testing.T) { + tmpPath := requireTmpCopy(t, "../testdata/sample-wrapped-v2.car") + + root := cid.MustParse("bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy") + subject, err := blockstore.OpenReadWrite(tmpPath, []cid.Cid{root}) + require.NoError(t, err) + + defer func() { + err = subject.Finalize() + require.NoError(t, err) + }() + + var wantCids []cid.Cid + var wantMh []multihash.Multihash + ch, err := subject.AllKeysChan(context.Background()) + require.NoError(t, err) + for c := range ch { + wantCids = append(wantCids, c) + wantMh = append(wantMh, c.Hash()) + } + + idx := subject.Index() + + for _, c := range wantCids { + _, err = index.GetFirst(idx, c) + require.NoError(t, err) + } + + if idx, ok := idx.(index.IterableIndex); ok { + var got []multihash.Multihash + err = idx.ForEach(func(m multihash.Multihash, u uint64) error { + got = append(got, m) + return nil + }) + require.NoError(t, err) + require.ElementsMatch(t, wantMh, got) + } +} diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 1634dda5d..af16fcc1e 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -134,7 +134,7 @@ func WriteTo(idx Index, w io.Writer) (uint64, error) { // Returns error if the encoding is not known. // // Attempting to read index data from untrusted sources is not recommended. -// Instead the index should be regenerated from the CARv2 data payload. +// Instead, the index should be regenerated from the CARv2 data payload. func ReadFrom(r io.Reader) (Index, error) { codec, err := ReadCodec(r) if err != nil {

    oI>s!@CYwgFh=$ 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 Date: Wed, 1 Sep 2021 14:21:41 +0100 Subject: [PATCH 3553/3817] Implement new index type that also includes mutltihash code Implement a new CARv2 index that contains enough information to reconstruct the multihashes of the data payload, since `CarIndexSorted` only includes multihash digests. The new index builds on top of the existing `IndexSorted` by adding an additional layer of grouping the multi-width indices by their multihash code. Note, this index intentionally ignores any given record with `multihash.IDENTITY` CID hash. Add a test that asserts offsets for the same CID across sorted index and new multihash sorted index are consistent. Add tests that assert marshal unmarshalling of the new index type is as expected, and it does not load records with `multihash.IDENTITY` digest. Relates to: - https://github.com/multiformats/multicodec/pull/227 Fixes: - https://github.com/ipld/go-car/issues/214 This commit was moved from ipld/go-car@42b9e28c3297412b78511f76c003ddd4d682fcc4 --- ipld/car/v2/index/index.go | 2 + ipld/car/v2/index/mhindexsorted.go | 158 ++++++++++++++++++++++++ ipld/car/v2/index/mhindexsorted_test.go | 107 ++++++++++++++++ ipld/car/v2/index_gen_test.go | 128 ++++++++++++++++--- 4 files changed, 380 insertions(+), 15 deletions(-) create mode 100644 ipld/car/v2/index/mhindexsorted.go create mode 100644 ipld/car/v2/index/mhindexsorted_test.go diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 3408dfbd2..cc9ff70bc 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -72,6 +72,8 @@ func New(codec multicodec.Code) (Index, error) { switch codec { case multicodec.CarIndexSorted: return newSorted(), nil + case multicodec.CarMultihashIndexSorted: + return newMultihashSorted(), nil default: return nil, fmt.Errorf("unknwon index codec: %v", codec) } diff --git a/ipld/car/v2/index/mhindexsorted.go b/ipld/car/v2/index/mhindexsorted.go new file mode 100644 index 000000000..95ab64743 --- /dev/null +++ b/ipld/car/v2/index/mhindexsorted.go @@ -0,0 +1,158 @@ +package index + +import ( + "encoding/binary" + "io" + "sort" + + "github.com/ipfs/go-cid" + "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-multihash" +) + +type ( + // multihashIndexSorted maps multihash code (i.e. hashing algorithm) to multiWidthCodedIndex. + // This index ignores any Record with multihash.IDENTITY. + multihashIndexSorted map[uint64]*multiWidthCodedIndex + // multiWidthCodedIndex stores multihash code for each multiWidthIndex. + multiWidthCodedIndex struct { + multiWidthIndex + code uint64 + } +) + +func newMultiWidthCodedIndex() *multiWidthCodedIndex { + return &multiWidthCodedIndex{ + multiWidthIndex: make(multiWidthIndex), + } +} + +func (m *multiWidthCodedIndex) Marshal(w io.Writer) error { + if err := binary.Write(w, binary.LittleEndian, m.code); err != nil { + return err + } + return m.multiWidthIndex.Marshal(w) +} + +func (m *multiWidthCodedIndex) Unmarshal(r io.Reader) error { + if err := binary.Read(r, binary.LittleEndian, &m.code); err != nil { + return err + } + return m.multiWidthIndex.Unmarshal(r) +} + +func (m *multihashIndexSorted) Codec() multicodec.Code { + return multicodec.CarMultihashIndexSorted +} + +func (m *multihashIndexSorted) Marshal(w io.Writer) error { + if err := binary.Write(w, binary.LittleEndian, int32(len(*m))); err != nil { + return err + } + // The codes are unique, but ranging over a map isn't deterministic. + // As per the CARv2 spec, we must order buckets by digest length. + // TODO update CARv2 spec to reflect this for the new index type. + codes := m.sortedMultihashCodes() + + for _, code := range codes { + mwci := (*m)[code] + if err := mwci.Marshal(w); err != nil { + return err + } + } + return nil +} + +func (m *multihashIndexSorted) sortedMultihashCodes() []uint64 { + codes := make([]uint64, 0, len(*m)) + for code := range *m { + codes = append(codes, code) + } + sort.Slice(codes, func(i, j int) bool { + return codes[i] < codes[j] + }) + return codes +} + +func (m *multihashIndexSorted) Unmarshal(r io.Reader) error { + var l int32 + if err := binary.Read(r, binary.LittleEndian, &l); err != nil { + return err + } + for i := 0; i < int(l); i++ { + mwci := newMultiWidthCodedIndex() + if err := mwci.Unmarshal(r); err != nil { + return err + } + m.put(mwci) + } + return nil +} + +func (m *multihashIndexSorted) put(mwci *multiWidthCodedIndex) { + (*m)[mwci.code] = mwci +} + +func (m *multihashIndexSorted) Load(records []Record) error { + // TODO optimize load by avoiding multihash decoding twice. + // This implementation decodes multihashes twice: once here to group by code, and once in the + // internals of multiWidthIndex to group by digest length. The code can be optimized by + // combining the grouping logic into one step where the multihash of a CID is only decoded once. + // The optimization would need refactoring of the IndexSorted compaction logic. + + // Group records by multihash code + byCode := make(map[uint64][]Record) + for _, record := range records { + dmh, err := multihash.Decode(record.Hash()) + if err != nil { + return err + } + code := dmh.Code + // Ignore IDENTITY multihash in the index. + if code == multihash.IDENTITY { + continue + } + recsByCode, ok := byCode[code] + if !ok { + recsByCode = make([]Record, 0) + byCode[code] = recsByCode + } + byCode[code] = append(recsByCode, record) + } + + // Load each record group. + for code, recsByCode := range byCode { + mwci := newMultiWidthCodedIndex() + mwci.code = code + if err := mwci.Load(recsByCode); err != nil { + return err + } + m.put(mwci) + } + return nil +} + +func (m *multihashIndexSorted) GetAll(cid cid.Cid, f func(uint64) bool) error { + hash := cid.Hash() + dmh, err := multihash.Decode(hash) + if err != nil { + return err + } + mwci, err := m.get(dmh) + if err != nil { + return err + } + return mwci.GetAll(cid, f) +} + +func (m *multihashIndexSorted) get(dmh *multihash.DecodedMultihash) (*multiWidthCodedIndex, error) { + if codedIdx, ok := (*m)[dmh.Code]; ok { + return codedIdx, nil + } + return nil, ErrNotFound +} + +func newMultihashSorted() Index { + index := make(multihashIndexSorted) + return &index +} diff --git a/ipld/car/v2/index/mhindexsorted_test.go b/ipld/car/v2/index/mhindexsorted_test.go new file mode 100644 index 000000000..ced8a921a --- /dev/null +++ b/ipld/car/v2/index/mhindexsorted_test.go @@ -0,0 +1,107 @@ +package index_test + +import ( + "bytes" + "fmt" + "math/rand" + "testing" + + "github.com/multiformats/go-multicodec" + + "github.com/ipfs/go-cid" + "github.com/ipld/go-car/v2/index" + "github.com/multiformats/go-multihash" + "github.com/stretchr/testify/require" +) + +func TestMutilhashSortedIndex_Codec(t *testing.T) { + subject, err := index.New(multicodec.CarMultihashIndexSorted) + require.NoError(t, err) + require.Equal(t, multicodec.CarMultihashIndexSorted, subject.Codec()) +} + +func TestMultiWidthCodedIndex_LoadDoesNotLoadIdentityMultihash(t *testing.T) { + rng := rand.New(rand.NewSource(1413)) + identityRecords := generateIndexRecords(t, multihash.IDENTITY, rng) + nonIdentityRecords := generateIndexRecords(t, multihash.SHA2_256, rng) + records := append(identityRecords, nonIdentityRecords...) + + subject, err := index.New(multicodec.CarMultihashIndexSorted) + require.NoError(t, err) + err = subject.Load(records) + require.NoError(t, err) + + // Assert index does not contain any records with IDENTITY multihash code. + for _, r := range identityRecords { + wantCid := r.Cid + err = subject.GetAll(wantCid, func(o uint64) bool { + require.Fail(t, "subject should not contain any records with IDENTITY multihash code") + return false + }) + require.Equal(t, index.ErrNotFound, err) + } + + // Assert however, index does contain the non IDENTITY records. + requireContainsAll(t, subject, nonIdentityRecords) +} + +func TestMultiWidthCodedIndex_MarshalUnmarshal(t *testing.T) { + rng := rand.New(rand.NewSource(1413)) + records := generateIndexRecords(t, multihash.SHA2_256, rng) + + // Create a new mh sorted index and load randomly generated records into it. + subject, err := index.New(multicodec.CarMultihashIndexSorted) + require.NoError(t, err) + err = subject.Load(records) + require.NoError(t, err) + + // Marshal the index. + buf := new(bytes.Buffer) + err = subject.Marshal(buf) + require.NoError(t, err) + + // Unmarshal it back to another instance of mh sorted index. + umSubject, err := index.New(multicodec.CarMultihashIndexSorted) + require.NoError(t, err) + err = umSubject.Unmarshal(buf) + require.NoError(t, err) + + // Assert original records are present in both index instances with expected offset. + requireContainsAll(t, subject, records) + requireContainsAll(t, umSubject, records) +} + +func generateIndexRecords(t *testing.T, hasherCode uint64, rng *rand.Rand) []index.Record { + var records []index.Record + recordCount := rng.Intn(99) + 1 // Up to 100 records + for i := 0; i < recordCount; i++ { + records = append(records, index.Record{ + Cid: generateCidV1(t, hasherCode, rng), + Offset: rng.Uint64(), + }) + } + return records +} + +func generateCidV1(t *testing.T, hasherCode uint64, rng *rand.Rand) cid.Cid { + data := []byte(fmt.Sprintf("🌊d-%d", rng.Uint64())) + mh, err := multihash.Sum(data, hasherCode, -1) + require.NoError(t, err) + return cid.NewCidV1(cid.Raw, mh) +} + +func requireContainsAll(t *testing.T, subject index.Index, nonIdentityRecords []index.Record) { + for _, r := range nonIdentityRecords { + wantCid := r.Cid + wantOffset := r.Offset + + var gotOffsets []uint64 + err := subject.GetAll(wantCid, func(o uint64) bool { + gotOffsets = append(gotOffsets, o) + return false + }) + require.NoError(t, err) + require.Equal(t, 1, len(gotOffsets)) + require.Equal(t, wantOffset, gotOffsets[0]) + } +} diff --git a/ipld/car/v2/index_gen_test.go b/ipld/car/v2/index_gen_test.go index 058d07e6e..635a07cb2 100644 --- a/ipld/car/v2/index_gen_test.go +++ b/ipld/car/v2/index_gen_test.go @@ -1,9 +1,19 @@ -package car +package car_test import ( + "io" "os" "testing" + "github.com/multiformats/go-multihash" + + "github.com/ipfs/go-cid" + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/internal/carv1" + internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-varint" + "github.com/ipld/go-car/v2/index" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -13,19 +23,19 @@ func TestReadOrGenerateIndex(t *testing.T) { tests := []struct { name string carPath string - readOpts []ReadOption + readOpts []carv2.ReadOption wantIndexer func(t *testing.T) index.Index wantErr bool }{ { "CarV1IsIndexedAsExpected", "testdata/sample-v1.car", - []ReadOption{}, + []carv2.ReadOption{}, func(t *testing.T) index.Index { v1, err := os.Open("testdata/sample-v1.car") require.NoError(t, err) defer v1.Close() - want, err := GenerateIndex(v1) + want, err := carv2.GenerateIndex(v1) require.NoError(t, err) return want }, @@ -34,12 +44,12 @@ func TestReadOrGenerateIndex(t *testing.T) { { "CarV2WithIndexIsReturnedAsExpected", "testdata/sample-wrapped-v2.car", - []ReadOption{}, + []carv2.ReadOption{}, func(t *testing.T) index.Index { v2, err := os.Open("testdata/sample-wrapped-v2.car") require.NoError(t, err) defer v2.Close() - reader, err := NewReader(v2) + reader, err := carv2.NewReader(v2) require.NoError(t, err) want, err := index.ReadFrom(reader.IndexReader()) require.NoError(t, err) @@ -50,12 +60,12 @@ func TestReadOrGenerateIndex(t *testing.T) { { "CarV1WithZeroLenSectionIsGeneratedAsExpected", "testdata/sample-v1-with-zero-len-section.car", - []ReadOption{ZeroLengthSectionAsEOF(true)}, + []carv2.ReadOption{carv2.ZeroLengthSectionAsEOF(true)}, func(t *testing.T) index.Index { v1, err := os.Open("testdata/sample-v1-with-zero-len-section.car") require.NoError(t, err) defer v1.Close() - want, err := GenerateIndex(v1, ZeroLengthSectionAsEOF(true)) + want, err := carv2.GenerateIndex(v1, carv2.ZeroLengthSectionAsEOF(true)) require.NoError(t, err) return want }, @@ -64,12 +74,12 @@ func TestReadOrGenerateIndex(t *testing.T) { { "AnotherCarV1WithZeroLenSectionIsGeneratedAsExpected", "testdata/sample-v1-with-zero-len-section2.car", - []ReadOption{ZeroLengthSectionAsEOF(true)}, + []carv2.ReadOption{carv2.ZeroLengthSectionAsEOF(true)}, func(t *testing.T) index.Index { v1, err := os.Open("testdata/sample-v1-with-zero-len-section2.car") require.NoError(t, err) defer v1.Close() - want, err := GenerateIndex(v1, ZeroLengthSectionAsEOF(true)) + want, err := carv2.GenerateIndex(v1, carv2.ZeroLengthSectionAsEOF(true)) require.NoError(t, err) return want }, @@ -78,14 +88,14 @@ func TestReadOrGenerateIndex(t *testing.T) { { "CarV1WithZeroLenSectionWithoutOptionIsError", "testdata/sample-v1-with-zero-len-section.car", - []ReadOption{}, + []carv2.ReadOption{}, func(t *testing.T) index.Index { return nil }, true, }, { "CarOtherThanV1OrV2IsError", "testdata/sample-rootless-v42.car", - []ReadOption{}, + []carv2.ReadOption{}, func(t *testing.T) index.Index { return nil }, true, }, @@ -95,7 +105,7 @@ func TestReadOrGenerateIndex(t *testing.T) { carFile, err := os.Open(tt.carPath) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, carFile.Close()) }) - got, err := ReadOrGenerateIndex(carFile, tt.readOpts...) + got, err := carv2.ReadOrGenerateIndex(carFile, tt.readOpts...) if tt.wantErr { require.Error(t, err) } else { @@ -121,7 +131,7 @@ func TestGenerateIndexFromFile(t *testing.T) { v1, err := os.Open("testdata/sample-v1.car") require.NoError(t, err) defer v1.Close() - want, err := GenerateIndex(v1) + want, err := carv2.GenerateIndex(v1) require.NoError(t, err) return want }, @@ -142,7 +152,7 @@ func TestGenerateIndexFromFile(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GenerateIndexFromFile(tt.carPath) + got, err := carv2.GenerateIndexFromFile(tt.carPath) if tt.wantErr { require.Error(t, err) } else { @@ -153,3 +163,91 @@ func TestGenerateIndexFromFile(t *testing.T) { }) } } + +func TestMultihashIndexSortedConsistencyWithIndexSorted(t *testing.T) { + path := "testdata/sample-v1.car" + + sortedIndex, err := carv2.GenerateIndexFromFile(path) + require.NoError(t, err) + require.Equal(t, multicodec.CarIndexSorted, sortedIndex.Codec()) + + f, err := os.Open(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, f.Close()) }) + br, err := carv2.NewBlockReader(f) + require.NoError(t, err) + + subject := generateMultihashSortedIndex(t, path) + for { + wantNext, err := br.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + + dmh, err := multihash.Decode(wantNext.Cid().Hash()) + require.NoError(t, err) + if dmh.Code == multihash.IDENTITY { + continue + } + + wantCid := wantNext.Cid() + var wantOffsets []uint64 + err = sortedIndex.GetAll(wantCid, func(o uint64) bool { + wantOffsets = append(wantOffsets, o) + return false + }) + require.NoError(t, err) + + var gotOffsets []uint64 + err = subject.GetAll(wantCid, func(o uint64) bool { + gotOffsets = append(gotOffsets, o) + return false + }) + + require.NoError(t, err) + require.Equal(t, wantOffsets, gotOffsets) + } +} + +func generateMultihashSortedIndex(t *testing.T, path string) index.Index { + f, err := os.Open(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, f.Close()) }) + reader := internalio.ToByteReadSeeker(f) + header, err := carv1.ReadHeader(reader) + require.NoError(t, err) + require.Equal(t, uint64(1), header.Version) + + idx, err := index.New(multicodec.CarMultihashIndexSorted) + require.NoError(t, err) + records := make([]index.Record, 0) + + var sectionOffset int64 + sectionOffset, err = reader.Seek(0, io.SeekCurrent) + require.NoError(t, err) + + for { + sectionLen, err := varint.ReadUvarint(reader) + if err == io.EOF { + break + } + require.NoError(t, err) + + if sectionLen == 0 { + break + } + + cidLen, c, err := cid.CidFromReader(reader) + require.NoError(t, err) + records = append(records, index.Record{Cid: c, Offset: uint64(sectionOffset)}) + remainingSectionLen := int64(sectionLen) - int64(cidLen) + sectionOffset, err = reader.Seek(remainingSectionLen, io.SeekCurrent) + require.NoError(t, err) + } + + err = idx.Load(records) + require.NoError(t, err) + + return idx +} From 9df5a4e5bd0b99c1974ee452f22d6a32d18590dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Tue, 7 Sep 2021 15:43:09 +0200 Subject: [PATCH 3554/3817] avoid allocating on every byte read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need a ByteReader for some APIs, such as reading CIDs. However, our entrypoints often don't, such as Reader or ReaderAt. Thus, we have a type that does the wrapping to support ReadByte. We were converting a non-pointer to an interface, which forcibly allocates, since interfaces must contain pointers. Fix that by making the ReadByte methods use pointer receivers. name old time/op new time/op delta ReadBlocks-16 1.18ms ± 2% 1.15ms ± 5% -2.73% (p=0.003 n=11+11) name old speed new speed delta ReadBlocks-16 441MB/s ± 2% 453MB/s ± 5% +2.85% (p=0.003 n=11+11) name old alloc/op new alloc/op delta ReadBlocks-16 1.33MB ± 0% 1.29MB ± 0% -2.41% (p=0.000 n=12+12) name old allocs/op new allocs/op delta ReadBlocks-16 13.5k ± 0% 11.5k ± 0% -14.79% (p=0.000 n=12+12) This commit was moved from ipld/go-car@7a82ec58a3c84adb19224987581f4aead0c55cba --- ipld/car/v2/internal/io/converter.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/car/v2/internal/io/converter.go b/ipld/car/v2/internal/io/converter.go index 65350d34b..5435471f1 100644 --- a/ipld/car/v2/internal/io/converter.go +++ b/ipld/car/v2/internal/io/converter.go @@ -63,11 +63,11 @@ func ToReaderAt(rs io.ReadSeeker) io.ReaderAt { return &readSeekerAt{rs: rs} } -func (rb readerPlusByte) ReadByte() (byte, error) { +func (rb *readerPlusByte) ReadByte() (byte, error) { return readByte(rb) } -func (rsb readSeekerPlusByte) ReadByte() (byte, error) { +func (rsb *readSeekerPlusByte) ReadByte() (byte, error) { return readByte(rsb) } From dd0c4220ec583650bda62dea909a0b969055c66c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Tue, 7 Sep 2021 16:31:55 +0200 Subject: [PATCH 3555/3817] avoid another alloc per read byte MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Like the last commit, avoid an extra allocation per read byte. In this case, we created a one-byte buffer for each readByte call. Unfortunately, since io.Reader is an interface, the compiler can't know if it holds onto the memory, so the buffer escapes and cannot be placed in the stack. To sidestep this issue, reuse a preallocated buffer. We know this is fine, because we only do sequential reads. name old time/op new time/op delta ReadBlocks-16 1.15ms ± 5% 1.09ms ± 4% -5.13% (p=0.000 n=11+11) name old speed new speed delta ReadBlocks-16 453MB/s ± 5% 478MB/s ± 4% +5.41% (p=0.000 n=11+11) name old alloc/op new alloc/op delta ReadBlocks-16 1.29MB ± 0% 1.30MB ± 0% +0.48% (p=0.000 n=12+12) name old allocs/op new allocs/op delta ReadBlocks-16 11.5k ± 0% 9.5k ± 0% -17.35% (p=0.000 n=12+12) This commit was moved from ipld/go-car@ccffb5c06ed2c79d67dba037a4754b4212480790 --- ipld/car/v2/internal/io/converter.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/ipld/car/v2/internal/io/converter.go b/ipld/car/v2/internal/io/converter.go index 5435471f1..21011b6ec 100644 --- a/ipld/car/v2/internal/io/converter.go +++ b/ipld/car/v2/internal/io/converter.go @@ -17,15 +17,21 @@ var ( type ( readerPlusByte struct { io.Reader + + byteBuf [1]byte // escapes via io.Reader.Read; preallocate } readSeekerPlusByte struct { io.ReadSeeker + + byteBuf [1]byte // escapes via io.Reader.Read; preallocate } discardingReadSeekerPlusByte struct { io.Reader offset int64 + + byteBuf [1]byte // escapes via io.Reader.Read; preallocate } ByteReadSeeker interface { @@ -43,7 +49,7 @@ func ToByteReader(r io.Reader) io.ByteReader { if br, ok := r.(io.ByteReader); ok { return br } - return &readerPlusByte{r} + return &readerPlusByte{Reader: r} } func ToByteReadSeeker(r io.Reader) ByteReadSeeker { @@ -51,7 +57,7 @@ func ToByteReadSeeker(r io.Reader) ByteReadSeeker { return brs } if rs, ok := r.(io.ReadSeeker); ok { - return &readSeekerPlusByte{rs} + return &readSeekerPlusByte{ReadSeeker: rs} } return &discardingReadSeekerPlusByte{Reader: r} } @@ -64,15 +70,18 @@ func ToReaderAt(rs io.ReadSeeker) io.ReaderAt { } func (rb *readerPlusByte) ReadByte() (byte, error) { - return readByte(rb) + _, err := io.ReadFull(rb, rb.byteBuf[:]) + return rb.byteBuf[0], err } func (rsb *readSeekerPlusByte) ReadByte() (byte, error) { - return readByte(rsb) + _, err := io.ReadFull(rsb, rsb.byteBuf[:]) + return rsb.byteBuf[0], err } func (drsb *discardingReadSeekerPlusByte) ReadByte() (byte, error) { - return readByte(drsb) + _, err := io.ReadFull(drsb, drsb.byteBuf[:]) + return drsb.byteBuf[0], err } func (drsb *discardingReadSeekerPlusByte) Read(p []byte) (read int, err error) { @@ -98,12 +107,6 @@ func (drsb *discardingReadSeekerPlusByte) Seek(offset int64, whence int) (int64, } } -func readByte(r io.Reader) (byte, error) { - var p [1]byte - _, err := io.ReadFull(r, p[:]) - return p[0], err -} - func (rsa *readSeekerAt) ReadAt(p []byte, off int64) (n int, err error) { rsa.mu.Lock() defer rsa.mu.Unlock() From 0e8f05f98432712de1b69c5ccfb0b6b1f036568e Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 7 Sep 2021 17:36:08 +0200 Subject: [PATCH 3556/3817] Expose the ability to iterate over records in `MultihasIndexSorted` Implement API that allows iteration over multihashes and offsets in `MultihasIndexSorted`. The API is specific to this index type; therefore, the type is now exported along with its constructor funtion. Write test that asserts `GetAll` and `ForEach` behave consistently. Relates to #95 This commit was moved from ipld/go-car@1398bba4351c96adaccb92464e45769339f17a9d --- ipld/car/v2/index/index.go | 4 +-- ipld/car/v2/index/indexsorted.go | 26 +++++++++++++++ ipld/car/v2/index/mhindexsorted.go | 46 ++++++++++++++++++++------- ipld/car/v2/index_gen_test.go | 51 ++++++++++++++++++++++++++++-- 4 files changed, 110 insertions(+), 17 deletions(-) diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index cc9ff70bc..3a28a49a3 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -44,7 +44,7 @@ type ( // Load inserts a number of records into the index. Load([]Record) error - // Get looks up all blocks matching a given CID, + // GetAll looks up all blocks matching a given CID, // calling a function for each one of their offsets. // // If the function returns false, GetAll stops. @@ -73,7 +73,7 @@ func New(codec multicodec.Code) (Index, error) { case multicodec.CarIndexSorted: return newSorted(), nil case multicodec.CarMultihashIndexSorted: - return newMultihashSorted(), nil + return NewMultihashSorted(), nil default: return nil, fmt.Errorf("unknwon index codec: %v", codec) } diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index 5f37eee44..5af6f4da2 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -13,6 +13,8 @@ import ( "github.com/multiformats/go-multihash" ) +var _ Index = (*multiWidthIndex)(nil) + type ( digestRecord struct { digest []byte @@ -120,6 +122,21 @@ func (s *singleWidthIndex) Load(items []Record) error { return nil } +func (s *singleWidthIndex) forEachDigest(f func(digest []byte, offset uint64) error) error { + segmentCount := len(s.index) / int(s.width) + for i := 0; i < segmentCount; i++ { + digestStart := i * int(s.width) + offsetEnd := (i + 1) * int(s.width) + digestEnd := offsetEnd - 8 + digest := s.index[digestStart:digestEnd] + offset := binary.LittleEndian.Uint64(s.index[digestEnd:offsetEnd]) + if err := f(digest, offset); err != nil { + return err + } + } + return nil +} + func (m *multiWidthIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { d, err := multihash.Decode(c.Hash()) if err != nil { @@ -210,6 +227,15 @@ func (m *multiWidthIndex) Load(items []Record) error { return nil } +func (m *multiWidthIndex) forEachDigest(f func(digest []byte, offset uint64) error) error { + for _, swi := range *m { + if err := swi.forEachDigest(f); err != nil { + return err + } + } + return nil +} + func newSorted() Index { m := make(multiWidthIndex) return &m diff --git a/ipld/car/v2/index/mhindexsorted.go b/ipld/car/v2/index/mhindexsorted.go index 95ab64743..75d309716 100644 --- a/ipld/car/v2/index/mhindexsorted.go +++ b/ipld/car/v2/index/mhindexsorted.go @@ -10,10 +10,12 @@ import ( "github.com/multiformats/go-multihash" ) +var _ Index = (*MultihashIndexSorted)(nil) + type ( - // multihashIndexSorted maps multihash code (i.e. hashing algorithm) to multiWidthCodedIndex. + // MultihashIndexSorted maps multihash code (i.e. hashing algorithm) to multiWidthCodedIndex. // This index ignores any Record with multihash.IDENTITY. - multihashIndexSorted map[uint64]*multiWidthCodedIndex + MultihashIndexSorted map[uint64]*multiWidthCodedIndex // multiWidthCodedIndex stores multihash code for each multiWidthIndex. multiWidthCodedIndex struct { multiWidthIndex @@ -41,11 +43,21 @@ func (m *multiWidthCodedIndex) Unmarshal(r io.Reader) error { return m.multiWidthIndex.Unmarshal(r) } -func (m *multihashIndexSorted) Codec() multicodec.Code { +func (m *multiWidthCodedIndex) forEach(f func(mh multihash.Multihash, offset uint64) error) error { + return m.multiWidthIndex.forEachDigest(func(digest []byte, offset uint64) error { + mh, err := multihash.Encode(digest, m.code) + if err != nil { + return err + } + return f(mh, offset) + }) +} + +func (m *MultihashIndexSorted) Codec() multicodec.Code { return multicodec.CarMultihashIndexSorted } -func (m *multihashIndexSorted) Marshal(w io.Writer) error { +func (m *MultihashIndexSorted) Marshal(w io.Writer) error { if err := binary.Write(w, binary.LittleEndian, int32(len(*m))); err != nil { return err } @@ -63,7 +75,7 @@ func (m *multihashIndexSorted) Marshal(w io.Writer) error { return nil } -func (m *multihashIndexSorted) sortedMultihashCodes() []uint64 { +func (m *MultihashIndexSorted) sortedMultihashCodes() []uint64 { codes := make([]uint64, 0, len(*m)) for code := range *m { codes = append(codes, code) @@ -74,7 +86,7 @@ func (m *multihashIndexSorted) sortedMultihashCodes() []uint64 { return codes } -func (m *multihashIndexSorted) Unmarshal(r io.Reader) error { +func (m *MultihashIndexSorted) Unmarshal(r io.Reader) error { var l int32 if err := binary.Read(r, binary.LittleEndian, &l); err != nil { return err @@ -89,11 +101,11 @@ func (m *multihashIndexSorted) Unmarshal(r io.Reader) error { return nil } -func (m *multihashIndexSorted) put(mwci *multiWidthCodedIndex) { +func (m *MultihashIndexSorted) put(mwci *multiWidthCodedIndex) { (*m)[mwci.code] = mwci } -func (m *multihashIndexSorted) Load(records []Record) error { +func (m *MultihashIndexSorted) Load(records []Record) error { // TODO optimize load by avoiding multihash decoding twice. // This implementation decodes multihashes twice: once here to group by code, and once in the // internals of multiWidthIndex to group by digest length. The code can be optimized by @@ -132,7 +144,7 @@ func (m *multihashIndexSorted) Load(records []Record) error { return nil } -func (m *multihashIndexSorted) GetAll(cid cid.Cid, f func(uint64) bool) error { +func (m *MultihashIndexSorted) GetAll(cid cid.Cid, f func(uint64) bool) error { hash := cid.Hash() dmh, err := multihash.Decode(hash) if err != nil { @@ -145,14 +157,24 @@ func (m *multihashIndexSorted) GetAll(cid cid.Cid, f func(uint64) bool) error { return mwci.GetAll(cid, f) } -func (m *multihashIndexSorted) get(dmh *multihash.DecodedMultihash) (*multiWidthCodedIndex, error) { +// ForEach calls f for every multihash and its associated offset stored by this index. +func (m *MultihashIndexSorted) ForEach(f func(mh multihash.Multihash, offset uint64) error) error { + for _, mwci := range *m { + if err := mwci.forEach(f); err != nil { + return err + } + } + return nil +} + +func (m *MultihashIndexSorted) get(dmh *multihash.DecodedMultihash) (*multiWidthCodedIndex, error) { if codedIdx, ok := (*m)[dmh.Code]; ok { return codedIdx, nil } return nil, ErrNotFound } -func newMultihashSorted() Index { - index := make(multihashIndexSorted) +func NewMultihashSorted() *MultihashIndexSorted { + index := make(MultihashIndexSorted) return &index } diff --git a/ipld/car/v2/index_gen_test.go b/ipld/car/v2/index_gen_test.go index 635a07cb2..762c38762 100644 --- a/ipld/car/v2/index_gen_test.go +++ b/ipld/car/v2/index_gen_test.go @@ -210,7 +210,53 @@ func TestMultihashIndexSortedConsistencyWithIndexSorted(t *testing.T) { } } -func generateMultihashSortedIndex(t *testing.T, path string) index.Index { +func TestMultihashSorted_ForEachIsConsistentWithGetAll(t *testing.T) { + path := "testdata/sample-v1.car" + f, err := os.Open(path) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, f.Close()) }) + + br, err := carv2.NewBlockReader(f) + require.NoError(t, err) + subject := generateMultihashSortedIndex(t, path) + + gotForEach := make(map[string]uint64) + err = subject.ForEach(func(mh multihash.Multihash, offset uint64) error { + gotForEach[mh.String()] = offset + return nil + }) + require.NoError(t, err) + + for { + b, err := br.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + c := b.Cid() + dmh, err := multihash.Decode(c.Hash()) + require.NoError(t, err) + if dmh.Code == multihash.IDENTITY { + continue + } + + wantMh := c.Hash() + + var wantOffset uint64 + err = subject.GetAll(c, func(u uint64) bool { + wantOffset = u + return false + }) + require.NoError(t, err) + + s := wantMh.String() + gotOffset, ok := gotForEach[s] + require.True(t, ok) + require.Equal(t, wantOffset, gotOffset) + } +} + +func generateMultihashSortedIndex(t *testing.T, path string) *index.MultihashIndexSorted { f, err := os.Open(path) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, f.Close()) }) @@ -219,8 +265,7 @@ func generateMultihashSortedIndex(t *testing.T, path string) index.Index { require.NoError(t, err) require.Equal(t, uint64(1), header.Version) - idx, err := index.New(multicodec.CarMultihashIndexSorted) - require.NoError(t, err) + idx := index.NewMultihashSorted() records := make([]index.Record, 0) var sectionOffset int64 From c98783b0ddc939e4c15d23ea79b2315a2280e920 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 7 Sep 2021 19:08:14 +0200 Subject: [PATCH 3557/3817] Fix index GetAll infinite loop if function always returns `true` Refine `GetAll` logic so that it stops iteration if either function returns false or there are no more entries to search. Fixes #216 This commit was moved from ipld/go-car@2bff9fa743d09860709e7b20ff72b1ac1a853fe9 --- ipld/car/v2/index/indexsorted.go | 22 ++++++++++++------- ipld/car/v2/index/indexsorted_test.go | 31 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index 5af6f4da2..e78935083 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -87,15 +87,21 @@ func (s *singleWidthIndex) getAll(d []byte, fn func(uint64) bool) error { idx := sort.Search(int(s.len), func(i int) bool { return s.Less(i, d) }) - if uint64(idx) == s.len { - return ErrNotFound - } - any := false - for bytes.Equal(d[:], s.index[idx*int(s.width):(idx+1)*int(s.width)-8]) { - any = true - offset := binary.LittleEndian.Uint64(s.index[(idx+1)*int(s.width)-8 : (idx+1)*int(s.width)]) - if !fn(offset) { + var any bool + for ; uint64(idx) < s.len; idx++ { + digestStart := idx * int(s.width) + offsetEnd := (idx + 1) * int(s.width) + digestEnd := offsetEnd - 8 + if bytes.Equal(d[:], s.index[digestStart:digestEnd]) { + any = true + offset := binary.LittleEndian.Uint64(s.index[digestEnd:offsetEnd]) + if !fn(offset) { + // User signalled to stop searching; therefore, break. + break + } + } else { + // No more matches; therefore, break. break } } diff --git a/ipld/car/v2/index/indexsorted_test.go b/ipld/car/v2/index/indexsorted_test.go index 767937b79..3a939d5c8 100644 --- a/ipld/car/v2/index/indexsorted_test.go +++ b/ipld/car/v2/index/indexsorted_test.go @@ -1,6 +1,7 @@ package index import ( + "encoding/binary" "testing" "github.com/ipfs/go-merkledag" @@ -31,3 +32,33 @@ func TestSortedIndex_GetReturnsNotFoundWhenCidDoesNotExist(t *testing.T) { }) } } + +func TestSingleWidthIndex_GetAll(t *testing.T) { + l := 4 + width := 9 + buf := make([]byte, width*l) + + // Populate the index bytes as total of four records. + // The last record should not match the getAll. + for i := 0; i < l; i++ { + if i < l-1 { + buf[i*width] = 1 + } else { + buf[i*width] = 2 + } + binary.LittleEndian.PutUint64(buf[(i*width)+1:(i*width)+width], uint64(14)) + } + subject := &singleWidthIndex{ + width: 9, + len: uint64(l), + index: buf, + } + + var foundCount int + err := subject.getAll([]byte{1}, func(u uint64) bool { + foundCount++ + return true + }) + require.NoError(t, err) + require.Equal(t, 3, foundCount) +} From a016d21425767ce0c0214b4310589248227eecdc Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 2 Sep 2021 14:09:11 +0100 Subject: [PATCH 3558/3817] Ignore records with `IDENTITY` CID in `IndexSorted` Skip `IDENTITY` CIDs in `IndexSorted`, as required by CARv2 specification. Re-generate `sample-wrapped-v2.car` testdata, since its previous index contained CIDs with `IDENTITY` multihash. Gracefully handle CIDs with `IDENTITY` multihash code in the `blockstore` API. Since `Get`, `GetSize` and `Has` interfaces rely on the index, decode given keys directly to satisfy calls for such CIDs. See: - https://ipld.io/specs/transport/car/carv2/#index-format Fixes #215 This commit was moved from ipld/go-car@acd3ead768ebba6ea81d31a7164f1fc031f5c276 --- ipld/car/v2/blockstore/doc.go | 12 +++++- ipld/car/v2/blockstore/readonly.go | 40 ++++++++++++++++++- ipld/car/v2/blockstore/readwrite.go | 7 ++++ ipld/car/v2/blockstore/readwrite_test.go | 37 +++++++++++++---- ipld/car/v2/index/index.go | 4 ++ ipld/car/v2/index/indexsorted.go | 7 ++++ ipld/car/v2/index/indexsorted_test.go | 44 ++++++++++++++++++++- ipld/car/v2/testdata/sample-wrapped-v2.car | Bin 521859 -> 521696 bytes 8 files changed, 139 insertions(+), 12 deletions(-) diff --git a/ipld/car/v2/blockstore/doc.go b/ipld/car/v2/blockstore/doc.go index a82723419..6b96b7a6e 100644 --- a/ipld/car/v2/blockstore/doc.go +++ b/ipld/car/v2/blockstore/doc.go @@ -1,4 +1,4 @@ -// package blockstore implements the IPFS blockstore interface backed by a CAR file. +// Package blockstore implements the IPFS blockstore interface backed by a CAR file. // This package provides two flavours of blockstore: ReadOnly and ReadWrite. // // The ReadOnly blockstore provides a read-only random access from a given data payload either in @@ -15,4 +15,14 @@ // instantiated from the same file path using OpenReadOnly. // A user may resume reading/writing from files produced by an instance of ReadWrite blockstore. The // resumption is attempted automatically, if the path passed to OpenReadWrite exists. +// +// Note that the blockstore implementations in this package behave similarly to IPFS IdStore wrapper +// when given CIDs with multihash.IDENTITY code. +// More specifically, for CIDs with multhash.IDENTITY code: +// * blockstore.Has will always return true. +// * blockstore.Get will always succeed, returning the multihash digest of the given CID. +// * blockstore.GetSize will always succeed, returning the multihash digest length of the given CID. +// * blockstore.Put and blockstore.PutMany will always succeed without performing any operation. +// +// See: https://pkg.go.dev/github.com/ipfs/go-ipfs-blockstore#NewIdStore package blockstore diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 7e165bd24..75113c635 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -10,8 +10,6 @@ import ( "golang.org/x/exp/mmap" - "github.com/multiformats/go-varint" - blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" @@ -20,6 +18,8 @@ import ( "github.com/ipld/go-car/v2/internal/carv1" "github.com/ipld/go-car/v2/internal/carv1/util" internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/multiformats/go-multihash" + "github.com/multiformats/go-varint" ) var _ blockstore.Blockstore = (*ReadOnly)(nil) @@ -187,7 +187,16 @@ func (b *ReadOnly) DeleteBlock(_ cid.Cid) error { } // Has indicates if the store contains a block that corresponds to the given key. +// This function always returns true for any given key with multihash.IDENTITY code. func (b *ReadOnly) Has(key cid.Cid) (bool, error) { + // Check if the given CID has multihash.IDENTITY code + // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. + if _, ok, err := isIdentity(key); err != nil { + return false, err + } else if ok { + return true, nil + } + b.mu.RLock() defer b.mu.RUnlock() @@ -227,7 +236,16 @@ func (b *ReadOnly) Has(key cid.Cid) (bool, error) { } // Get gets a block corresponding to the given key. +// This API will always return true if the given key has multihash.IDENTITY code. func (b *ReadOnly) Get(key cid.Cid) (blocks.Block, error) { + // Check if the given CID has multihash.IDENTITY code + // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. + if digest, ok, err := isIdentity(key); err != nil { + return nil, err + } else if ok { + return blocks.NewBlockWithCid(digest, key) + } + b.mu.RLock() defer b.mu.RUnlock() @@ -272,6 +290,14 @@ func (b *ReadOnly) Get(key cid.Cid) (blocks.Block, error) { // GetSize gets the size of an item corresponding to the given key. func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { + // Check if the given CID has multihash.IDENTITY code + // Note, we do this without locking, since there is no shared information to lock for in order to perform the check. + if digest, ok, err := isIdentity(key); err != nil { + return 0, err + } else if ok { + return len(digest), nil + } + b.mu.RLock() defer b.mu.RUnlock() @@ -320,6 +346,16 @@ func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { return fnSize, nil } +func isIdentity(key cid.Cid) (digest []byte, ok bool, err error) { + dmh, err := multihash.Decode(key.Hash()) + if err != nil { + return nil, false, err + } + ok = dmh.Code == multihash.IDENTITY + digest = dmh.Digest + return digest, ok, nil +} + // Put is not supported and always returns an error. func (b *ReadOnly) Put(blocks.Block) error { return errReadOnly diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index ae482a7f9..a35961532 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -304,6 +304,13 @@ func (b *ReadWrite) PutMany(blks []blocks.Block) error { for _, bl := range blks { c := bl.Cid() + // Check for IDENTITY CID. If IDENTITY, ignore and move to the next block. + if _, ok, err := isIdentity(c); err != nil { + return err + } else if ok { + continue + } + if !b.wopts.BlockstoreAllowDuplicatePuts { if b.ronly.ropts.BlockstoreUseWholeCIDs && b.idx.hasExactCID(c) { continue // deduplicated by CID diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index a30bbb235..cfdad4642 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -64,6 +64,7 @@ func TestBlockstore(t *testing.T) { t.Cleanup(func() { ingester.Finalize() }) cids := make([]cid.Cid, 0) + var idCidCount int for { b, err := r.Next() if err == io.EOF { @@ -80,6 +81,12 @@ func TestBlockstore(t *testing.T) { if has, err := ingester.Has(candidate); !has || err != nil { t.Fatalf("expected to find %s but didn't: %s", candidate, err) } + + dmh, err := multihash.Decode(b.Cid().Hash()) + require.NoError(t, err) + if dmh.Code == multihash.IDENTITY { + idCidCount++ + } } for _, c := range cids { @@ -107,9 +114,8 @@ func TestBlockstore(t *testing.T) { } numKeysCh++ } - if numKeysCh != len(cids) { - t.Fatalf("AllKeysChan returned an unexpected amount of keys; expected %v but got %v", len(cids), numKeysCh) - } + expectedCidCount := len(cids) - idCidCount + require.Equal(t, expectedCidCount, numKeysCh, "AllKeysChan returned an unexpected amount of keys; expected %v but got %v", expectedCidCount, numKeysCh) for _, c := range cids { b, err := robs.Get(c) @@ -347,7 +353,7 @@ func TestBlockstoreResumption(t *testing.T) { require.NoError(t, err) // For each block resume on the same file, putting blocks one at a time. - var wantBlockCountSoFar int + var wantBlockCountSoFar, idCidCount int wantBlocks := make(map[cid.Cid]blocks.Block) for { b, err := r.Next() @@ -358,6 +364,12 @@ func TestBlockstoreResumption(t *testing.T) { wantBlockCountSoFar++ wantBlocks[b.Cid()] = b + dmh, err := multihash.Decode(b.Cid().Hash()) + require.NoError(t, err) + if dmh.Code == multihash.IDENTITY { + idCidCount++ + } + // 30% chance of subject failing; more concretely: re-instantiating blockstore with the same // file without calling Finalize. The higher this percentage the slower the test runs // considering the number of blocks in the original CARv1 test payload. @@ -402,7 +414,7 @@ func TestBlockstoreResumption(t *testing.T) { gotBlockCountSoFar++ } // Assert the number of blocks in file are as expected calculated via AllKeysChan - require.Equal(t, wantBlockCountSoFar, gotBlockCountSoFar) + require.Equal(t, wantBlockCountSoFar-idCidCount, gotBlockCountSoFar) } } subject.Discard() @@ -433,22 +445,31 @@ func TestBlockstoreResumption(t *testing.T) { require.Equal(t, wantPayloadReader.Header, gotPayloadReader.Header) for { wantNextBlock, wantErr := wantPayloadReader.Next() - gotNextBlock, gotErr := gotPayloadReader.Next() if wantErr == io.EOF { + gotNextBlock, gotErr := gotPayloadReader.Next() require.Equal(t, wantErr, gotErr) + require.Nil(t, gotNextBlock) break } require.NoError(t, wantErr) + + dmh, err := multihash.Decode(wantNextBlock.Cid().Hash()) + require.NoError(t, err) + if dmh.Code == multihash.IDENTITY { + continue + } + + gotNextBlock, gotErr := gotPayloadReader.Next() require.NoError(t, gotErr) require.Equal(t, wantNextBlock, gotNextBlock) } - // Assert index in resumed from file is identical to index generated directly from original CARv1 payload. + // Assert index in resumed from file is identical to index generated from the data payload portion of the generated CARv2 file. _, err = v1f.Seek(0, io.SeekStart) require.NoError(t, err) gotIdx, err := index.ReadFrom(v2r.IndexReader()) require.NoError(t, err) - wantIdx, err := carv2.GenerateIndex(v1f) + wantIdx, err := carv2.GenerateIndex(v2r.DataReader()) require.NoError(t, err) require.Equal(t, wantIdx, gotIdx) } diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 3a28a49a3..071e8e6e1 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -29,6 +29,10 @@ type ( // blocks even if the CID's encoding multicodec differs. Other index // implementations might index the entire CID, the entire multihash, or // just part of a multihash's digest. + // + // In accordance with the CARv2 specification, Index will never contain information about CIDs + // with multihash.IDENTITY code. + // See: https://ipld.io/specs/transport/car/carv2/#index-format Index interface { // Codec provides the multicodec code that the index implements. // diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index e78935083..c6165ffa7 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -206,6 +206,13 @@ func (m *multiWidthIndex) Load(items []Record) error { if err != nil { return err } + + // Ignore records with IDENTITY as required by CARv2 spec. + // See: https://ipld.io/specs/transport/car/carv2/#index-format + if decHash.Code == multihash.IDENTITY { + continue + } + digest := decHash.Digest idx, ok := idxs[len(digest)] if !ok { diff --git a/ipld/car/v2/index/indexsorted_test.go b/ipld/car/v2/index/indexsorted_test.go index 3a939d5c8..46322e543 100644 --- a/ipld/car/v2/index/indexsorted_test.go +++ b/ipld/car/v2/index/indexsorted_test.go @@ -4,6 +4,9 @@ import ( "encoding/binary" "testing" + "github.com/ipfs/go-cid" + "github.com/multiformats/go-multihash" + "github.com/ipfs/go-merkledag" "github.com/multiformats/go-multicodec" "github.com/stretchr/testify/require" @@ -13,7 +16,7 @@ func TestSortedIndexCodec(t *testing.T) { require.Equal(t, multicodec.CarIndexSorted, newSorted().Codec()) } -func TestSortedIndex_GetReturnsNotFoundWhenCidDoesNotExist(t *testing.T) { +func TestIndexSorted_GetReturnsNotFoundWhenCidDoesNotExist(t *testing.T) { nonExistingKey := merkledag.NewRawNode([]byte("lobstermuncher")).Block.Cid() tests := []struct { name string @@ -62,3 +65,42 @@ func TestSingleWidthIndex_GetAll(t *testing.T) { require.NoError(t, err) require.Equal(t, 3, foundCount) } + +func TestIndexSorted_IgnoresIdentityCids(t *testing.T) { + data := []byte("🟠in da 🌊d") + // Generate a record with IDENTITY multihash + idMh, err := multihash.Sum(data, multihash.IDENTITY, -1) + require.NoError(t, err) + idRec := Record{ + Cid: cid.NewCidV1(cid.Raw, idMh), + Offset: 1, + } + // Generate a record with non-IDENTITY multihash + nonIdMh, err := multihash.Sum(data, multihash.SHA2_256, -1) + require.NoError(t, err) + noIdRec := Record{ + Cid: cid.NewCidV1(cid.Raw, nonIdMh), + Offset: 2, + } + + subject := newSorted() + err = subject.Load([]Record{idRec, noIdRec}) + require.NoError(t, err) + + // Assert record with IDENTITY CID is not present. + err = subject.GetAll(idRec.Cid, func(u uint64) bool { + require.Fail(t, "no IDENTITY record shoul be found") + return false + }) + require.Equal(t, ErrNotFound, err) + + // Assert record with non-IDENTITY CID is indeed present. + var found bool + err = subject.GetAll(noIdRec.Cid, func(gotOffset uint64) bool { + found = true + require.Equal(t, noIdRec.Offset, gotOffset) + return false + }) + require.NoError(t, err) + require.True(t, found) +} diff --git a/ipld/car/v2/testdata/sample-wrapped-v2.car b/ipld/car/v2/testdata/sample-wrapped-v2.car index 7307bff71a854c4e6cdad611b4cc46097e4dbe23..232ba3136f8928d7a19f062527b1cb773ab28046 100644 GIT binary patch delta 31 ncmZpEEC1lNd_xOk3sVbo3rh=Y3)>d<8$pbW+iQZ^*%t!<$vF!} delta 195 zcmaFxTE6+Md_xOk3sVbo3rh=Y3)>d<8$tD~3=9lHK&%49Adr@sqi?96T$G>Z|B?|& zBr`9wq>GUWEFugf)xa#6`rORC)S?L}8Vd5uQ;QadLUf1#Rf8}HKy1iOEXqzTF; Date: Wed, 8 Sep 2021 05:45:55 -0700 Subject: [PATCH 3559/3817] Add carve utility for updating the index of a car{v1,v2} file (#219) * add car utility for updating the index of a car{v1,v2} file This commit was moved from ipld/go-car@2b19e2e305d4a9d3976c968ef524b60c7372fda9 --- ipld/car/v2/cmd/car/car.go | 158 +++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 ipld/car/v2/cmd/car/car.go diff --git a/ipld/car/v2/cmd/car/car.go b/ipld/car/v2/cmd/car/car.go new file mode 100644 index 000000000..586cd75f7 --- /dev/null +++ b/ipld/car/v2/cmd/car/car.go @@ -0,0 +1,158 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "log" + "os" + + "github.com/ipfs/go-cid" + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/index" + icarv1 "github.com/ipld/go-car/v2/internal/carv1" + "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-varint" + "github.com/urfave/cli/v2" +) + +func main() { + app := &cli.App{ + Name: "car", + Usage: "Utility for working with car files", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "codec", + Aliases: []string{"c"}, + Usage: "The type of index to write", + Value: multicodec.CarMultihashIndexSorted.String(), + }, + }, + Commands: []*cli.Command{ + { + Name: "index", + Aliases: []string{"i"}, + Usage: "write out the car with an index", + Action: func(c *cli.Context) error { + r, err := carv2.OpenReader(c.Args().Get(0)) + if err != nil { + return err + } + defer r.Close() + + var idx index.Index + if c.String("codec") != "none" { + var mc multicodec.Code + if err := mc.Set(c.String("codec")); err != nil { + return err + } + idx, err = index.New(mc) + if err != nil { + return err + } + } + + outStream := os.Stdout + if c.Args().Len() >= 2 { + outStream, err = os.Create(c.Args().Get(1)) + if err != nil { + return err + } + } + defer outStream.Close() + + v1r := r.DataReader() + + v2Header := carv2.NewHeader(r.Header.DataSize) + if c.String("codec") == "none" { + v2Header.IndexOffset = 0 + if _, err := outStream.Write(carv2.Pragma); err != nil { + return err + } + if _, err := v2Header.WriteTo(outStream); err != nil { + return err + } + if _, err := io.Copy(outStream, v1r); err != nil { + return err + } + return nil + } + + if _, err := outStream.Write(carv2.Pragma); err != nil { + return err + } + if _, err := v2Header.WriteTo(outStream); err != nil { + return err + } + + // collect records as we go through the v1r + hdr, err := icarv1.ReadHeader(v1r) + if err != nil { + return fmt.Errorf("error reading car header: %w", err) + } + if err := icarv1.WriteHeader(hdr, outStream); err != nil { + return err + } + + records := make([]index.Record, 0) + var sectionOffset int64 + if sectionOffset, err = v1r.Seek(0, io.SeekCurrent); err != nil { + return err + } + + br := bufio.NewReader(v1r) + for { + // Read the section's length. + sectionLen, err := varint.ReadUvarint(br) + if err != nil { + if err == io.EOF { + break + } + return err + } + if _, err := outStream.Write(varint.ToUvarint(sectionLen)); err != nil { + return err + } + + // Null padding; by default it's an error. + // TODO: integrate corresponding ReadOption + if sectionLen == 0 { + // TODO: pad writer to expected length. + break + } + + // Read the CID. + cidLen, c, err := cid.CidFromReader(br) + if err != nil { + return err + } + records = append(records, index.Record{Cid: c, Offset: uint64(sectionOffset)}) + if _, err := c.WriteBytes(outStream); err != nil { + return err + } + + // Seek to the next section by skipping the block. + // The section length includes the CID, so subtract it. + remainingSectionLen := int64(sectionLen) - int64(cidLen) + if _, err := io.CopyN(outStream, br, remainingSectionLen); err != nil { + return err + } + sectionOffset += int64(sectionLen) + int64(varint.UvarintSize(sectionLen)) + } + + if err := idx.Load(records); err != nil { + return err + } + + return index.WriteTo(idx, outStream) + }, + }, + }, + } + + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + os.Exit(1) + } +} From 003b295b5e46cc76c12f729758175507cc4a251b Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 8 Sep 2021 13:00:48 +0200 Subject: [PATCH 3560/3817] Make `MultihashIndexSorted` the default index codec for CARv2 Provide an option to choose the index codec when writing a CARv2, with default value of `multicodec.CarMultihashIndexSorted`. Implement utility functions to work with options with ability to split and merge between different flavours to reduce boilerplate code across the repo. Re-generate sample files with default index. Fixes #224 This commit was moved from ipld/go-car@29b166da5c28ba952d597767b73ba7c5fa4afc70 --- ipld/car/v2/block_reader.go | 8 +-- ipld/car/v2/blockstore/insertionindex.go | 9 ++-- ipld/car/v2/blockstore/readonly.go | 8 +-- ipld/car/v2/blockstore/readwrite.go | 11 +++-- ipld/car/v2/index_gen.go | 54 +++++++++++---------- ipld/car/v2/index_gen_test.go | 2 +- ipld/car/v2/internal/options/options.go | 19 ++++++++ ipld/car/v2/options.go | 32 ++++++++++++ ipld/car/v2/reader.go | 4 +- ipld/car/v2/testdata/sample-rw-bs-v2.car | Bin 1875 -> 1887 bytes ipld/car/v2/testdata/sample-wrapped-v2.car | Bin 521696 -> 521708 bytes ipld/car/v2/writer.go | 22 +++++++-- 12 files changed, 118 insertions(+), 51 deletions(-) create mode 100644 ipld/car/v2/internal/options/options.go diff --git a/ipld/car/v2/block_reader.go b/ipld/car/v2/block_reader.go index 5c3350294..8cd23e72c 100644 --- a/ipld/car/v2/block_reader.go +++ b/ipld/car/v2/block_reader.go @@ -37,14 +37,10 @@ func NewBlockReader(r io.Reader, opts ...ReadOption) (*BlockReader, error) { return nil, err } - // Populate the block reader version. + // Populate the block reader version and options. br := &BlockReader{ Version: pragmaOrV1Header.Version, - } - - // Populate read options - for _, o := range opts { - o(&br.ropts) + ropts: ApplyReadOptions(opts...), } // Expect either version 1 or 2. diff --git a/ipld/car/v2/blockstore/insertionindex.go b/ipld/car/v2/blockstore/insertionindex.go index 00d414656..7cca61b31 100644 --- a/ipld/car/v2/blockstore/insertionindex.go +++ b/ipld/car/v2/blockstore/insertionindex.go @@ -7,10 +7,9 @@ import ( "fmt" "io" + "github.com/ipfs/go-cid" "github.com/ipld/go-car/v2/index" "github.com/multiformats/go-multicodec" - - "github.com/ipfs/go-cid" "github.com/multiformats/go-multihash" "github.com/petar/GoLLRB/llrb" cbor "github.com/whyrusleeping/cbor/go" @@ -154,9 +153,9 @@ func newInsertionIndex() *insertionIndex { return &insertionIndex{} } -// flatten returns a 'indexsorted' formatted index for more efficient subsequent loading -func (ii *insertionIndex) flatten() (index.Index, error) { - si, err := index.New(multicodec.CarIndexSorted) +// flatten returns a formatted index in the given codec for more efficient subsequent loading. +func (ii *insertionIndex) flatten(codec multicodec.Code) (index.Index, error) { + si, err := index.New(codec) if err != nil { return nil, err } diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 75113c635..4e7e82027 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -94,9 +94,9 @@ func UseWholeCIDs(enable bool) carv2.ReadOption { // // There is no need to call ReadOnly.Close on instances returned by this function. func NewReadOnly(backing io.ReaderAt, idx index.Index, opts ...carv2.ReadOption) (*ReadOnly, error) { - b := &ReadOnly{} - for _, opt := range opts { - opt(&b.ropts) + ropts := carv2.ApplyReadOptions(opts...) + b := &ReadOnly{ + ropts: ropts, } version, err := readVersion(backing) @@ -155,6 +155,8 @@ func generateIndex(at io.ReaderAt, opts ...carv2.ReadOption) (index.Index, error default: rs = internalio.NewOffsetReadSeeker(r, 0) } + + // Note, we do not set any write options so that all write options fall back onto defaults. return carv2.GenerateIndex(rs, opts...) } diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index a35961532..7b14b55f9 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -116,14 +116,19 @@ func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.ReadWriteOption) header: carv2.NewHeader(0), } + var ro []carv2.ReadOption + var wo []carv2.WriteOption for _, opt := range opts { switch opt := opt.(type) { case carv2.ReadOption: - opt(&rwbs.ronly.ropts) + ro = append(ro, opt) case carv2.WriteOption: - opt(&rwbs.wopts) + wo = append(wo, opt) } } + rwbs.ronly.ropts = carv2.ApplyReadOptions(ro...) + rwbs.wopts = carv2.ApplyWriteOptions(wo...) + if p := rwbs.wopts.DataPadding; p > 0 { rwbs.header = rwbs.header.WithDataPadding(p) } @@ -366,7 +371,7 @@ func (b *ReadWrite) Finalize() error { defer b.ronly.closeWithoutMutex() // TODO if index not needed don't bother flattening it. - fi, err := b.idx.flatten() + fi, err := b.idx.flatten(b.wopts.IndexCodec) if err != nil { return err } diff --git a/ipld/car/v2/index_gen.go b/ipld/car/v2/index_gen.go index 11bf36bee..91ebb5891 100644 --- a/ipld/car/v2/index_gen.go +++ b/ipld/car/v2/index_gen.go @@ -5,40 +5,40 @@ import ( "io" "os" - internalio "github.com/ipld/go-car/v2/internal/io" - - "github.com/ipld/go-car/v2/index" - "github.com/multiformats/go-multicodec" - - "github.com/multiformats/go-varint" - "github.com/ipfs/go-cid" + "github.com/ipld/go-car/v2/index" "github.com/ipld/go-car/v2/internal/carv1" + internalio "github.com/ipld/go-car/v2/internal/io" + "github.com/multiformats/go-varint" ) // GenerateIndex generates index for a given car in v1 format. -// The index can be stored using index.Save into a file or serialized using index.WriteTo. +// The generated index will be in multicodec.CarMultihashIndexSorted, the default index codec. +// The index can be stored in serialized format using index.WriteTo. +// See LoadIndex. func GenerateIndex(v1r io.Reader, opts ...ReadOption) (index.Index, error) { - var o ReadOptions - for _, opt := range opts { - opt(&o) + idx := index.NewMultihashSorted() + if err := LoadIndex(idx, v1r, opts...); err != nil { + return nil, err } + return idx, nil +} +// LoadIndex populates idx with index records generated from v1r. +// The v1r must be data payload in CARv1 format. +func LoadIndex(idx index.Index, v1r io.Reader, opts ...ReadOption) error { reader := internalio.ToByteReadSeeker(v1r) header, err := carv1.ReadHeader(reader) if err != nil { - return nil, fmt.Errorf("error reading car header: %w", err) + return fmt.Errorf("error reading car header: %w", err) } if header.Version != 1 { - return nil, fmt.Errorf("expected version to be 1, got %v", header.Version) + return fmt.Errorf("expected version to be 1, got %v", header.Version) } - idx, err := index.New(multicodec.CarIndexSorted) - if err != nil { - return nil, err - } - records := make([]index.Record, 0) + // Parse Options. + ropts := ApplyReadOptions(opts...) // Record the start of each section, with first section starring from current position in the // reader, i.e. right after the header, since we have only read the header so far. @@ -48,9 +48,10 @@ func GenerateIndex(v1r io.Reader, opts ...ReadOption) (index.Index, error) { // We get it through Seek to only depend on APIs of a typical io.Seeker. // This would also reduce refactoring in case the utility reader is moved. if sectionOffset, err = reader.Seek(0, io.SeekCurrent); err != nil { - return nil, err + return err } + records := make([]index.Record, 0) for { // Read the section's length. sectionLen, err := varint.ReadUvarint(reader) @@ -58,22 +59,22 @@ func GenerateIndex(v1r io.Reader, opts ...ReadOption) (index.Index, error) { if err == io.EOF { break } - return nil, err + return err } // Null padding; by default it's an error. if sectionLen == 0 { - if o.ZeroLengthSectionAsEOF { + if ropts.ZeroLengthSectionAsEOF { break } else { - return nil, fmt.Errorf("carv1 null padding not allowed by default; see ZeroLengthSectionAsEOF") + return fmt.Errorf("carv1 null padding not allowed by default; see ZeroLengthSectionAsEOF") } } // Read the CID. cidLen, c, err := cid.CidFromReader(reader) if err != nil { - return nil, err + return err } records = append(records, index.Record{Cid: c, Offset: uint64(sectionOffset)}) @@ -81,19 +82,20 @@ func GenerateIndex(v1r io.Reader, opts ...ReadOption) (index.Index, error) { // The section length includes the CID, so subtract it. remainingSectionLen := int64(sectionLen) - int64(cidLen) if sectionOffset, err = reader.Seek(remainingSectionLen, io.SeekCurrent); err != nil { - return nil, err + return err } } if err := idx.Load(records); err != nil { - return nil, err + return err } - return idx, nil + return nil } // GenerateIndexFromFile walks a car v1 file at the give path and generates an index of cid->byte offset. // The index can be stored using index.Save into a file or serialized using index.WriteTo. +// See GenerateIndex. func GenerateIndexFromFile(path string) (index.Index, error) { f, err := os.Open(path) if err != nil { diff --git a/ipld/car/v2/index_gen_test.go b/ipld/car/v2/index_gen_test.go index 762c38762..601fb8cb5 100644 --- a/ipld/car/v2/index_gen_test.go +++ b/ipld/car/v2/index_gen_test.go @@ -169,7 +169,7 @@ func TestMultihashIndexSortedConsistencyWithIndexSorted(t *testing.T) { sortedIndex, err := carv2.GenerateIndexFromFile(path) require.NoError(t, err) - require.Equal(t, multicodec.CarIndexSorted, sortedIndex.Codec()) + require.Equal(t, multicodec.CarMultihashIndexSorted, sortedIndex.Codec()) f, err := os.Open(path) require.NoError(t, err) diff --git a/ipld/car/v2/internal/options/options.go b/ipld/car/v2/internal/options/options.go new file mode 100644 index 000000000..3add2b5a4 --- /dev/null +++ b/ipld/car/v2/internal/options/options.go @@ -0,0 +1,19 @@ +// Package options provides utilities to work with ReadOption and WriteOption used internally. +package options + +import carv2 "github.com/ipld/go-car/v2" + +// SplitReadWriteOptions splits the rwopts by type into ReadOption and WriteOption slices. +func SplitReadWriteOptions(rwopts ...carv2.ReadWriteOption) ([]carv2.ReadOption, []carv2.WriteOption) { + var ropts []carv2.ReadOption + var wopts []carv2.WriteOption + for _, opt := range rwopts { + switch opt := opt.(type) { + case carv2.ReadOption: + ropts = append(ropts, opt) + case carv2.WriteOption: + wopts = append(wopts, opt) + } + } + return ropts, wopts +} diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go index 86aaa9e55..ed6502cc9 100644 --- a/ipld/car/v2/options.go +++ b/ipld/car/v2/options.go @@ -1,5 +1,7 @@ package car +import "github.com/multiformats/go-multicodec" + // ReadOptions holds the configured options after applying a number of // ReadOption funcs. // @@ -11,6 +13,15 @@ type ReadOptions struct { BlockstoreUseWholeCIDs bool } +// ApplyReadOptions applies ropts and returns the resulting ReadOptions. +func ApplyReadOptions(ropts ...ReadOption) ReadOptions { + var opts ReadOptions + for _, opt := range ropts { + opt(&opts) + } + return opts +} + // ReadOption describes an option which affects behavior when parsing CAR files. type ReadOption func(*ReadOptions) @@ -26,10 +37,24 @@ var _ ReadWriteOption = ReadOption(nil) type WriteOptions struct { DataPadding uint64 IndexPadding uint64 + IndexCodec multicodec.Code BlockstoreAllowDuplicatePuts bool } +// ApplyWriteOptions applies the given ropts and returns the resulting WriteOptions. +func ApplyWriteOptions(ropts ...WriteOption) WriteOptions { + var opts WriteOptions + for _, opt := range ropts { + opt(&opts) + } + // Set defaults for zero valued fields. + if opts.IndexCodec == 0 { + opts.IndexCodec = multicodec.CarMultihashIndexSorted + } + return opts +} + // WriteOption describes an option which affects behavior when encoding CAR files. type WriteOption func(*WriteOptions) @@ -67,3 +92,10 @@ func UseIndexPadding(p uint64) WriteOption { o.IndexPadding = p } } + +// UseIndexCodec is a write option which sets the codec used for index generation. +func UseIndexCodec(c multicodec.Code) WriteOption { + return func(o *WriteOptions) { + o.IndexCodec = c + } +} diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index 2f7cbb18e..b9742ec72 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -48,9 +48,7 @@ func NewReader(r io.ReaderAt, opts ...ReadOption) (*Reader, error) { cr := &Reader{ r: r, } - for _, o := range opts { - o(&cr.ropts) - } + cr.ropts = ApplyReadOptions(opts...) or := internalio.NewOffsetReadSeeker(r, 0) var err error diff --git a/ipld/car/v2/testdata/sample-rw-bs-v2.car b/ipld/car/v2/testdata/sample-rw-bs-v2.car index 9f7b56df358e0e3a0eb3734743b13cb3d88a12f4..22e357249441fd690a9d77a4223d009a6bea586e 100644 GIT binary patch delta 26 dcmcc2cb{*=NjAPl4n_tB1|c8@fyt-YIsjer1>^t# delta 14 Vcmcc5cbRX)Nj9bij>*^AIsh&(1<(Kh diff --git a/ipld/car/v2/testdata/sample-wrapped-v2.car b/ipld/car/v2/testdata/sample-wrapped-v2.car index 232ba3136f8928d7a19f062527b1cb773ab28046..35c4d36899c9947bac061b783fee9ac3615b5d16 100644 GIT binary patch delta 44 xcmaFxTK>&z`Gyw87N!>F7M2#)7Pc+y*Mj&OIT#rj7!)=E2_V>hBZ!@2F#u?>4Q>Dc delta 32 ocmaF!TK>Un`Gyw87N!>F7M2#)7Pc+y*MgWDIJQ3tV&_;40Oe8)ng9R* diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index c34482815..2a22414db 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -6,9 +6,8 @@ import ( "io" "os" - internalio "github.com/ipld/go-car/v2/internal/io" - "github.com/ipld/go-car/v2/index" + internalio "github.com/ipld/go-car/v2/internal/io" ) // ErrAlreadyV1 signals that the given payload is already in CARv1 format. @@ -47,15 +46,30 @@ func WrapV1File(srcPath, dstPath string) error { // WrapV1 takes a CARv1 file and wraps it as a CARv2 file with an index. // The resulting CARv2 file's inner CARv1 payload is left unmodified, // and does not use any padding before the innner CARv1 or index. -func WrapV1(src io.ReadSeeker, dst io.Writer) error { +func WrapV1(src io.ReadSeeker, dst io.Writer, opts ...ReadWriteOption) error { // TODO: verify src is indeed a CARv1 to prevent misuse. // GenerateIndex should probably be in charge of that. - idx, err := GenerateIndex(src) + var ro []ReadOption + var wo []WriteOption + for _, opt := range opts { + switch opt := opt.(type) { + case ReadOption: + ro = append(ro, opt) + case WriteOption: + wo = append(wo, opt) + } + } + wopts := ApplyWriteOptions(wo...) + idx, err := index.New(wopts.IndexCodec) if err != nil { return err } + if err := LoadIndex(idx, src, ro...); err != nil { + return err + } + // Use Seek to learn the size of the CARv1 before reading it. v1Size, err := src.Seek(0, io.SeekEnd) if err != nil { From 80c32df9e5ec1d23aff502a0bd1e36d0f2ab65f1 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 8 Sep 2021 08:38:10 -0700 Subject: [PATCH 3561/3817] Add `car split` command (#226) Add `car split` command to detach index This commit was moved from ipld/go-car@385b612ddece835b9f44ed585d6bf492452f98d5 --- ipld/car/v2/cmd/car/car.go | 142 ++++------------------------------- ipld/car/v2/cmd/car/index.go | 131 ++++++++++++++++++++++++++++++++ ipld/car/v2/cmd/car/split.go | 35 +++++++++ 3 files changed, 180 insertions(+), 128 deletions(-) create mode 100644 ipld/car/v2/cmd/car/index.go create mode 100644 ipld/car/v2/cmd/car/split.go diff --git a/ipld/car/v2/cmd/car/car.go b/ipld/car/v2/cmd/car/car.go index 586cd75f7..9f481cb41 100644 --- a/ipld/car/v2/cmd/car/car.go +++ b/ipld/car/v2/cmd/car/car.go @@ -1,18 +1,10 @@ package main import ( - "bufio" - "fmt" - "io" "log" "os" - "github.com/ipfs/go-cid" - carv2 "github.com/ipld/go-car/v2" - "github.com/ipld/go-car/v2/index" - icarv1 "github.com/ipld/go-car/v2/internal/carv1" "github.com/multiformats/go-multicodec" - "github.com/multiformats/go-varint" "github.com/urfave/cli/v2" ) @@ -20,133 +12,27 @@ func main() { app := &cli.App{ Name: "car", Usage: "Utility for working with car files", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "codec", - Aliases: []string{"c"}, - Usage: "The type of index to write", - Value: multicodec.CarMultihashIndexSorted.String(), - }, - }, Commands: []*cli.Command{ { Name: "index", Aliases: []string{"i"}, Usage: "write out the car with an index", - Action: func(c *cli.Context) error { - r, err := carv2.OpenReader(c.Args().Get(0)) - if err != nil { - return err - } - defer r.Close() - - var idx index.Index - if c.String("codec") != "none" { - var mc multicodec.Code - if err := mc.Set(c.String("codec")); err != nil { - return err - } - idx, err = index.New(mc) - if err != nil { - return err - } - } - - outStream := os.Stdout - if c.Args().Len() >= 2 { - outStream, err = os.Create(c.Args().Get(1)) - if err != nil { - return err - } - } - defer outStream.Close() - - v1r := r.DataReader() - - v2Header := carv2.NewHeader(r.Header.DataSize) - if c.String("codec") == "none" { - v2Header.IndexOffset = 0 - if _, err := outStream.Write(carv2.Pragma); err != nil { - return err - } - if _, err := v2Header.WriteTo(outStream); err != nil { - return err - } - if _, err := io.Copy(outStream, v1r); err != nil { - return err - } - return nil - } - - if _, err := outStream.Write(carv2.Pragma); err != nil { - return err - } - if _, err := v2Header.WriteTo(outStream); err != nil { - return err - } - - // collect records as we go through the v1r - hdr, err := icarv1.ReadHeader(v1r) - if err != nil { - return fmt.Errorf("error reading car header: %w", err) - } - if err := icarv1.WriteHeader(hdr, outStream); err != nil { - return err - } - - records := make([]index.Record, 0) - var sectionOffset int64 - if sectionOffset, err = v1r.Seek(0, io.SeekCurrent); err != nil { - return err - } - - br := bufio.NewReader(v1r) - for { - // Read the section's length. - sectionLen, err := varint.ReadUvarint(br) - if err != nil { - if err == io.EOF { - break - } - return err - } - if _, err := outStream.Write(varint.ToUvarint(sectionLen)); err != nil { - return err - } - - // Null padding; by default it's an error. - // TODO: integrate corresponding ReadOption - if sectionLen == 0 { - // TODO: pad writer to expected length. - break - } - - // Read the CID. - cidLen, c, err := cid.CidFromReader(br) - if err != nil { - return err - } - records = append(records, index.Record{Cid: c, Offset: uint64(sectionOffset)}) - if _, err := c.WriteBytes(outStream); err != nil { - return err - } - - // Seek to the next section by skipping the block. - // The section length includes the CID, so subtract it. - remainingSectionLen := int64(sectionLen) - int64(cidLen) - if _, err := io.CopyN(outStream, br, remainingSectionLen); err != nil { - return err - } - sectionOffset += int64(sectionLen) + int64(varint.UvarintSize(sectionLen)) - } - - if err := idx.Load(records); err != nil { - return err - } - - return index.WriteTo(idx, outStream) + Action: IndexCar, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "codec", + Aliases: []string{"c"}, + Usage: "The type of index to write", + Value: multicodec.CarMultihashIndexSorted.String(), + }, }, }, + { + Name: "split", + Aliases: []string{"s"}, + Usage: "Split an index to a detached file", + Action: SplitCar, + }, }, } diff --git a/ipld/car/v2/cmd/car/index.go b/ipld/car/v2/cmd/car/index.go new file mode 100644 index 000000000..4470e3d0e --- /dev/null +++ b/ipld/car/v2/cmd/car/index.go @@ -0,0 +1,131 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "os" + + "github.com/ipfs/go-cid" + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/index" + icarv1 "github.com/ipld/go-car/v2/internal/carv1" + "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-varint" + "github.com/urfave/cli/v2" +) + +// IndexCar is a command to add an index to a car +func IndexCar(c *cli.Context) error { + r, err := carv2.OpenReader(c.Args().Get(0)) + if err != nil { + return err + } + defer r.Close() + + var idx index.Index + if c.String("codec") != "none" { + var mc multicodec.Code + if err := mc.Set(c.String("codec")); err != nil { + return err + } + idx, err = index.New(mc) + if err != nil { + return err + } + } + + outStream := os.Stdout + if c.Args().Len() >= 2 { + outStream, err = os.Create(c.Args().Get(1)) + if err != nil { + return err + } + } + defer outStream.Close() + + v1r := r.DataReader() + + v2Header := carv2.NewHeader(r.Header.DataSize) + if c.String("codec") == "none" { + v2Header.IndexOffset = 0 + if _, err := outStream.Write(carv2.Pragma); err != nil { + return err + } + if _, err := v2Header.WriteTo(outStream); err != nil { + return err + } + if _, err := io.Copy(outStream, v1r); err != nil { + return err + } + return nil + } + + if _, err := outStream.Write(carv2.Pragma); err != nil { + return err + } + if _, err := v2Header.WriteTo(outStream); err != nil { + return err + } + + // collect records as we go through the v1r + hdr, err := icarv1.ReadHeader(v1r) + if err != nil { + return fmt.Errorf("error reading car header: %w", err) + } + if err := icarv1.WriteHeader(hdr, outStream); err != nil { + return err + } + + records := make([]index.Record, 0) + var sectionOffset int64 + if sectionOffset, err = v1r.Seek(0, io.SeekCurrent); err != nil { + return err + } + + br := bufio.NewReader(v1r) + for { + // Read the section's length. + sectionLen, err := varint.ReadUvarint(br) + if err != nil { + if err == io.EOF { + break + } + return err + } + if _, err := outStream.Write(varint.ToUvarint(sectionLen)); err != nil { + return err + } + + // Null padding; by default it's an error. + // TODO: integrate corresponding ReadOption + if sectionLen == 0 { + // TODO: pad writer to expected length. + break + } + + // Read the CID. + cidLen, c, err := cid.CidFromReader(br) + if err != nil { + return err + } + records = append(records, index.Record{Cid: c, Offset: uint64(sectionOffset)}) + if _, err := c.WriteBytes(outStream); err != nil { + return err + } + + // Seek to the next section by skipping the block. + // The section length includes the CID, so subtract it. + remainingSectionLen := int64(sectionLen) - int64(cidLen) + if _, err := io.CopyN(outStream, br, remainingSectionLen); err != nil { + return err + } + sectionOffset += int64(sectionLen) + int64(varint.UvarintSize(sectionLen)) + } + + if err := idx.Load(records); err != nil { + return err + } + + return index.WriteTo(idx, outStream) +} diff --git a/ipld/car/v2/cmd/car/split.go b/ipld/car/v2/cmd/car/split.go new file mode 100644 index 000000000..7733e19c3 --- /dev/null +++ b/ipld/car/v2/cmd/car/split.go @@ -0,0 +1,35 @@ +package main + +import ( + "fmt" + "io" + "os" + + carv2 "github.com/ipld/go-car/v2" + "github.com/urfave/cli/v2" +) + +// SplitCar is a command to output the index part of a car. +func SplitCar(c *cli.Context) error { + r, err := carv2.OpenReader(c.Args().Get(0)) + if err != nil { + return err + } + defer r.Close() + + if !r.Header.HasIndex() { + return fmt.Errorf("no index present") + } + + outStream := os.Stdout + if c.Args().Len() >= 2 { + outStream, err = os.Create(c.Args().Get(1)) + if err != nil { + return err + } + } + defer outStream.Close() + + _, err = io.Copy(outStream, r.IndexReader()) + return err +} From 0828bdc3116891b09708fb29c9981326dd639775 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 10 Sep 2021 02:17:31 -0700 Subject: [PATCH 3562/3817] Add `list` and `filter` commands (#227) * better name for detaching index command * add list and filter commands This commit was moved from ipld/go-car@afcc7b3d15e2d402035ea2ca447d102a3f7468fb --- ipld/car/v2/cmd/car/car.go | 26 +++++- ipld/car/v2/cmd/car/{split.go => detach.go} | 4 +- ipld/car/v2/cmd/car/filter.go | 98 +++++++++++++++++++++ ipld/car/v2/cmd/car/list.go | 53 +++++++++++ 4 files changed, 175 insertions(+), 6 deletions(-) rename ipld/car/v2/cmd/car/{split.go => detach.go} (83%) create mode 100644 ipld/car/v2/cmd/car/filter.go create mode 100644 ipld/car/v2/cmd/car/list.go diff --git a/ipld/car/v2/cmd/car/car.go b/ipld/car/v2/cmd/car/car.go index 9f481cb41..df9629386 100644 --- a/ipld/car/v2/cmd/car/car.go +++ b/ipld/car/v2/cmd/car/car.go @@ -28,10 +28,28 @@ func main() { }, }, { - Name: "split", - Aliases: []string{"s"}, - Usage: "Split an index to a detached file", - Action: SplitCar, + Name: "detach-index", + Usage: "Detach an index to a detached file", + Action: DetachCar, + }, + { + Name: "list", + Aliases: []string{"l"}, + Usage: "List the CIDs in a car", + Action: ListCar, + }, + { + Name: "filter", + Aliases: []string{"f"}, + Usage: "Filter the CIDs in a car", + Action: FilterCar, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "cid-file", + Usage: "A file to read CIDs from", + TakesFile: true, + }, + }, }, }, } diff --git a/ipld/car/v2/cmd/car/split.go b/ipld/car/v2/cmd/car/detach.go similarity index 83% rename from ipld/car/v2/cmd/car/split.go rename to ipld/car/v2/cmd/car/detach.go index 7733e19c3..276d73b47 100644 --- a/ipld/car/v2/cmd/car/split.go +++ b/ipld/car/v2/cmd/car/detach.go @@ -9,8 +9,8 @@ import ( "github.com/urfave/cli/v2" ) -// SplitCar is a command to output the index part of a car. -func SplitCar(c *cli.Context) error { +// DetachCar is a command to output the index part of a car. +func DetachCar(c *cli.Context) error { r, err := carv2.OpenReader(c.Args().Get(0)) if err != nil { return err diff --git a/ipld/car/v2/cmd/car/filter.go b/ipld/car/v2/cmd/car/filter.go new file mode 100644 index 000000000..cb58b4c15 --- /dev/null +++ b/ipld/car/v2/cmd/car/filter.go @@ -0,0 +1,98 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "os" + "strings" + + "github.com/ipfs/go-cid" + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/blockstore" + icarv1 "github.com/ipld/go-car/v2/internal/carv1" + "github.com/urfave/cli/v2" +) + +// FilterCar is a command to select a subset of a car by CID. +func FilterCar(c *cli.Context) error { + r, err := carv2.OpenReader(c.Args().Get(0)) + if err != nil { + return err + } + defer r.Close() + + if c.Args().Len() < 2 { + return fmt.Errorf("an output filename must be provided") + } + roots, err := r.Roots() + if err != nil { + return err + } + bs, err := blockstore.OpenReadWrite(c.Args().Get(1), roots) + if err != nil { + return err + } + + // Get the set of CIDs from stdin. + inStream := os.Stdin + if c.IsSet("cidFile") { + inStream, err = os.Open(c.String("cidFile")) + if err != nil { + return err + } + defer inStream.Close() + } + cidMap, err := parseCIDS(inStream) + if err != nil { + return err + } + fmt.Printf("filtering to %d cids\n", len(cidMap)) + + rd, err := icarv1.NewCarReader(r.DataReader()) + if err != nil { + return err + } + + for { + blk, err := rd.Next() + if err != nil { + if err == io.EOF { + break + } + return err + } + if _, ok := cidMap[blk.Cid()]; ok { + if err := bs.Put(blk); err != nil { + return err + } + } + } + return bs.Finalize() +} + +func parseCIDS(r io.Reader) (map[cid.Cid]struct{}, error) { + cids := make(map[cid.Cid]struct{}) + br := bufio.NewReader(r) + for { + line, _, err := br.ReadLine() + if err != nil { + if err == io.EOF { + return cids, nil + } + return nil, err + } + trimLine := strings.TrimSpace(string(line)) + if len(trimLine) == 0 { + continue + } + c, err := cid.Parse(trimLine) + if err != nil { + return nil, err + } + if _, ok := cids[c]; ok { + fmt.Fprintf(os.Stderr, "duplicate cid: %s\n", c) + } + cids[c] = struct{}{} + } +} diff --git a/ipld/car/v2/cmd/car/list.go b/ipld/car/v2/cmd/car/list.go new file mode 100644 index 000000000..e9cf1f7e1 --- /dev/null +++ b/ipld/car/v2/cmd/car/list.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + "io" + "os" + + carv2 "github.com/ipld/go-car/v2" + "github.com/urfave/cli/v2" +) + +// ListCar is a command to output the cids in a car. +func ListCar(c *cli.Context) error { + inStream := os.Stdin + var err error + if c.Args().Len() >= 1 { + inStream, err = os.Open(c.Args().First()) + if err != nil { + return err + } + defer inStream.Close() + } + rd, err := carv2.NewBlockReader(inStream) + if err != nil { + return err + } + + outStream := os.Stdout + if c.Args().Len() >= 2 { + outStream, err = os.Create(c.Args().Get(1)) + if err != nil { + return err + } + } + defer outStream.Close() + + if err != nil { + return err + } + + for { + blk, err := rd.Next() + if err != nil { + if err == io.EOF { + break + } + return err + } + fmt.Fprintf(outStream, "%s\n", blk.Cid()) + } + + return err +} From 71afb300d38f0b7772ffe5ff6b60a7553e6202c4 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 10 Sep 2021 06:53:19 -0700 Subject: [PATCH 3563/3817] add interface describing iteration (#228) * add interface describing iteration This commit was moved from ipld/go-car@1220a0ccb28ea7da8d08ab89377a037ce5a36650 --- ipld/car/v2/index/index.go | 17 +++++++++++++++++ ipld/car/v2/index/mhindexsorted.go | 1 + 2 files changed, 18 insertions(+) diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 071e8e6e1..8abd1d3a3 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -8,6 +8,7 @@ import ( internalio "github.com/ipld/go-car/v2/internal/io" "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-multihash" "github.com/multiformats/go-varint" @@ -58,6 +59,22 @@ type ( // ErrNotFound is returned. GetAll(cid.Cid, func(uint64) bool) error } + + // IterableIndex extends Index in cases where the Index is able to + // provide an iterator for getting the list of all entries in the + // index. + IterableIndex interface { + Index + + // ForEach takes a callback function that will be called + // on each entry in the index. The arguments to the callback are + // the multihash of the element, and the offset in the car file + // where the element appears. + // + // If the callback returns a non-nil error, the iteration is aborted, + // and the ForEach function returns the error to the user. + ForEach(func(multihash.Multihash, uint64) error) error + } ) // GetFirst is a wrapper over Index.GetAll, returning the offset for the first diff --git a/ipld/car/v2/index/mhindexsorted.go b/ipld/car/v2/index/mhindexsorted.go index 75d309716..15a731c80 100644 --- a/ipld/car/v2/index/mhindexsorted.go +++ b/ipld/car/v2/index/mhindexsorted.go @@ -11,6 +11,7 @@ import ( ) var _ Index = (*MultihashIndexSorted)(nil) +var _ IterableIndex = (*MultihashIndexSorted)(nil) type ( // MultihashIndexSorted maps multihash code (i.e. hashing algorithm) to multiWidthCodedIndex. From a61644972f168c6a8bbddc9484e5960761fe6210 Mon Sep 17 00:00:00 2001 From: Will Date: Sat, 11 Sep 2021 08:49:33 -0700 Subject: [PATCH 3564/3817] use file size when loading from v1 car (#229) This commit was moved from ipld/go-car@44693776b044002eaf45988ddb82e514749ded5d --- ipld/car/v2/cmd/car/index.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ipld/car/v2/cmd/car/index.go b/ipld/car/v2/cmd/car/index.go index 4470e3d0e..13836c6f3 100644 --- a/ipld/car/v2/cmd/car/index.go +++ b/ipld/car/v2/cmd/car/index.go @@ -46,6 +46,13 @@ func IndexCar(c *cli.Context) error { v1r := r.DataReader() + if r.Version == 1 { + fi, err := os.Stat(c.Args().Get(0)) + if err != nil { + return err + } + r.Header.DataSize = uint64(fi.Size()) + } v2Header := carv2.NewHeader(r.Header.DataSize) if c.String("codec") == "none" { v2Header.IndexOffset = 0 From 99b49ea6828e0a0816c0ecb7b8ff4f7007c00cdc Mon Sep 17 00:00:00 2001 From: Will Date: Sun, 12 Sep 2021 05:30:33 -0700 Subject: [PATCH 3565/3817] add `get block` to car cli (#230) add `get block` to car cli for extracting an individual block by CID from a car This commit was moved from ipld/go-car@5390c3359d08412773a688f25c006bc73690c429 --- ipld/car/v2/cmd/car/car.go | 42 ++++++++++++++++++++--------------- ipld/car/v2/cmd/car/get.go | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 18 deletions(-) create mode 100644 ipld/car/v2/cmd/car/get.go diff --git a/ipld/car/v2/cmd/car/car.go b/ipld/car/v2/cmd/car/car.go index df9629386..5afb3c06f 100644 --- a/ipld/car/v2/cmd/car/car.go +++ b/ipld/car/v2/cmd/car/car.go @@ -13,6 +13,30 @@ func main() { Name: "car", Usage: "Utility for working with car files", Commands: []*cli.Command{ + { + Name: "detach-index", + Usage: "Detach an index to a detached file", + Action: DetachCar, + }, + { + Name: "filter", + Aliases: []string{"f"}, + Usage: "Filter the CIDs in a car", + Action: FilterCar, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "cid-file", + Usage: "A file to read CIDs from", + TakesFile: true, + }, + }, + }, + { + Name: "get-block", + Aliases: []string{"gb"}, + Usage: "Get a block out of a car", + Action: GetCarBlock, + }, { Name: "index", Aliases: []string{"i"}, @@ -27,30 +51,12 @@ func main() { }, }, }, - { - Name: "detach-index", - Usage: "Detach an index to a detached file", - Action: DetachCar, - }, { Name: "list", Aliases: []string{"l"}, Usage: "List the CIDs in a car", Action: ListCar, }, - { - Name: "filter", - Aliases: []string{"f"}, - Usage: "Filter the CIDs in a car", - Action: FilterCar, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "cid-file", - Usage: "A file to read CIDs from", - TakesFile: true, - }, - }, - }, }, } diff --git a/ipld/car/v2/cmd/car/get.go b/ipld/car/v2/cmd/car/get.go new file mode 100644 index 000000000..72aaff4ba --- /dev/null +++ b/ipld/car/v2/cmd/car/get.go @@ -0,0 +1,45 @@ +package main + +import ( + "fmt" + "os" + + "github.com/ipfs/go-cid" + "github.com/ipld/go-car/v2/blockstore" + "github.com/urfave/cli/v2" +) + +// GetCarBlock is a command to get a block out of a car +func GetCarBlock(c *cli.Context) error { + if c.Args().Len() < 2 { + return fmt.Errorf("usage: car get-block [output file]") + } + + bs, err := blockstore.OpenReadOnly(c.Args().Get(0)) + if err != nil { + return err + } + + // string to CID + blkCid, err := cid.Parse(c.Args().Get(1)) + if err != nil { + return err + } + + blk, err := bs.Get(blkCid) + if err != nil { + return err + } + + outStream := os.Stdout + if c.Args().Len() >= 3 { + outStream, err = os.Create(c.Args().Get(2)) + if err != nil { + return err + } + defer outStream.Close() + } + + _, err = outStream.Write(blk.RawData()) + return err +} From 1649aa733ee4f8dc72d00d28d9b2041cacd30528 Mon Sep 17 00:00:00 2001 From: Will Date: Sun, 12 Sep 2021 06:07:35 -0700 Subject: [PATCH 3566/3817] Separate CLI to separate module (#231) refactor cmd module `v2/cmd/car` -> `cmd/car` This commit was moved from ipld/go-car@d252cb92b99f082f8aa31feca43dd125b99c2342 --- ipld/car/{v2 => }/cmd/car/car.go | 0 ipld/car/{v2 => }/cmd/car/detach.go | 0 ipld/car/{v2 => }/cmd/car/filter.go | 4 ++-- ipld/car/{v2 => }/cmd/car/get.go | 0 ipld/car/{v2 => }/cmd/car/index.go | 9 +++++---- ipld/car/{v2 => }/cmd/car/list.go | 0 6 files changed, 7 insertions(+), 6 deletions(-) rename ipld/car/{v2 => }/cmd/car/car.go (100%) rename ipld/car/{v2 => }/cmd/car/detach.go (100%) rename ipld/car/{v2 => }/cmd/car/filter.go (94%) rename ipld/car/{v2 => }/cmd/car/get.go (100%) rename ipld/car/{v2 => }/cmd/car/index.go (94%) rename ipld/car/{v2 => }/cmd/car/list.go (100%) diff --git a/ipld/car/v2/cmd/car/car.go b/ipld/car/cmd/car/car.go similarity index 100% rename from ipld/car/v2/cmd/car/car.go rename to ipld/car/cmd/car/car.go diff --git a/ipld/car/v2/cmd/car/detach.go b/ipld/car/cmd/car/detach.go similarity index 100% rename from ipld/car/v2/cmd/car/detach.go rename to ipld/car/cmd/car/detach.go diff --git a/ipld/car/v2/cmd/car/filter.go b/ipld/car/cmd/car/filter.go similarity index 94% rename from ipld/car/v2/cmd/car/filter.go rename to ipld/car/cmd/car/filter.go index cb58b4c15..5650065fe 100644 --- a/ipld/car/v2/cmd/car/filter.go +++ b/ipld/car/cmd/car/filter.go @@ -8,9 +8,9 @@ import ( "strings" "github.com/ipfs/go-cid" + carv1 "github.com/ipld/go-car" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/blockstore" - icarv1 "github.com/ipld/go-car/v2/internal/carv1" "github.com/urfave/cli/v2" ) @@ -49,7 +49,7 @@ func FilterCar(c *cli.Context) error { } fmt.Printf("filtering to %d cids\n", len(cidMap)) - rd, err := icarv1.NewCarReader(r.DataReader()) + rd, err := carv1.NewCarReader(r.DataReader()) if err != nil { return err } diff --git a/ipld/car/v2/cmd/car/get.go b/ipld/car/cmd/car/get.go similarity index 100% rename from ipld/car/v2/cmd/car/get.go rename to ipld/car/cmd/car/get.go diff --git a/ipld/car/v2/cmd/car/index.go b/ipld/car/cmd/car/index.go similarity index 94% rename from ipld/car/v2/cmd/car/index.go rename to ipld/car/cmd/car/index.go index 13836c6f3..a1fa6d864 100644 --- a/ipld/car/v2/cmd/car/index.go +++ b/ipld/car/cmd/car/index.go @@ -7,9 +7,9 @@ import ( "os" "github.com/ipfs/go-cid" + carv1 "github.com/ipld/go-car" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/index" - icarv1 "github.com/ipld/go-car/v2/internal/carv1" "github.com/multiformats/go-multicodec" "github.com/multiformats/go-varint" "github.com/urfave/cli/v2" @@ -76,11 +76,12 @@ func IndexCar(c *cli.Context) error { } // collect records as we go through the v1r - hdr, err := icarv1.ReadHeader(v1r) + br := bufio.NewReader(v1r) + hdr, err := carv1.ReadHeader(br) if err != nil { return fmt.Errorf("error reading car header: %w", err) } - if err := icarv1.WriteHeader(hdr, outStream); err != nil { + if err := carv1.WriteHeader(hdr, outStream); err != nil { return err } @@ -89,8 +90,8 @@ func IndexCar(c *cli.Context) error { if sectionOffset, err = v1r.Seek(0, io.SeekCurrent); err != nil { return err } + sectionOffset -= int64(br.Buffered()) - br := bufio.NewReader(v1r) for { // Read the section's length. sectionLen, err := varint.ReadUvarint(br) diff --git a/ipld/car/v2/cmd/car/list.go b/ipld/car/cmd/car/list.go similarity index 100% rename from ipld/car/v2/cmd/car/list.go rename to ipld/car/cmd/car/list.go From 30a38c1093e74a56d6818468ae37f38e33626e71 Mon Sep 17 00:00:00 2001 From: Will Date: Sun, 12 Sep 2021 23:52:40 -0700 Subject: [PATCH 3567/3817] Add `car get-dag` command (#232) * clean up filter command * Add `get-dag` subcommand This commit was moved from ipld/go-car@c51a79e5fc1cf958015afcc058e17c5cb1648afb --- ipld/car/cmd/car/car.go | 17 ++++++ ipld/car/cmd/car/filter.go | 24 ++++---- ipld/car/cmd/car/get.go | 116 +++++++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 10 deletions(-) diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index 5afb3c06f..69059b1d3 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -37,6 +37,23 @@ func main() { Usage: "Get a block out of a car", Action: GetCarBlock, }, + { + Name: "get-dag", + Aliases: []string{"gd"}, + Usage: "Get a dag out of a car", + Action: GetCarDag, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "selector", + Aliases: []string{"s"}, + Usage: "A selector over the dag", + }, + &cli.BoolFlag{ + Name: "strict", + Usage: "Fail if the selector finds links to blocks not in the original car", + }, + }, + }, { Name: "index", Aliases: []string{"i"}, diff --git a/ipld/car/cmd/car/filter.go b/ipld/car/cmd/car/filter.go index 5650065fe..a941a8b36 100644 --- a/ipld/car/cmd/car/filter.go +++ b/ipld/car/cmd/car/filter.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/ipfs/go-cid" - carv1 "github.com/ipld/go-car" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/blockstore" "github.com/urfave/cli/v2" @@ -16,20 +15,16 @@ import ( // FilterCar is a command to select a subset of a car by CID. func FilterCar(c *cli.Context) error { - r, err := carv2.OpenReader(c.Args().Get(0)) - if err != nil { - return err - } - defer r.Close() - if c.Args().Len() < 2 { return fmt.Errorf("an output filename must be provided") } - roots, err := r.Roots() + + fd, err := os.Open(c.Args().First()) if err != nil { return err } - bs, err := blockstore.OpenReadWrite(c.Args().Get(1), roots) + defer fd.Close() + rd, err := carv2.NewBlockReader(fd) if err != nil { return err } @@ -49,7 +44,16 @@ func FilterCar(c *cli.Context) error { } fmt.Printf("filtering to %d cids\n", len(cidMap)) - rd, err := carv1.NewCarReader(r.DataReader()) + outRoots := make([]cid.Cid, 0) + for _, r := range rd.Roots { + if _, ok := cidMap[r]; ok { + outRoots = append(outRoots, r) + } + } + if len(outRoots) == 0 { + fmt.Fprintf(os.Stderr, "warning: no roots defined after filtering\n") + } + bs, err := blockstore.OpenReadWrite(c.Args().Get(1), outRoots) if err != nil { return err } diff --git a/ipld/car/cmd/car/get.go b/ipld/car/cmd/car/get.go index 72aaff4ba..9cdc7a4cc 100644 --- a/ipld/car/cmd/car/get.go +++ b/ipld/car/cmd/car/get.go @@ -1,11 +1,29 @@ package main import ( + "bytes" "fmt" + "io" "os" + _ "github.com/ipld/go-codec-dagpb" + _ "github.com/ipld/go-ipld-prime/codec/cbor" + _ "github.com/ipld/go-ipld-prime/codec/dagcbor" + _ "github.com/ipld/go-ipld-prime/codec/dagjson" + _ "github.com/ipld/go-ipld-prime/codec/json" + _ "github.com/ipld/go-ipld-prime/codec/raw" + + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" + ipfsbs "github.com/ipfs/go-ipfs-blockstore" "github.com/ipld/go-car/v2/blockstore" + "github.com/ipld/go-ipld-prime/datamodel" + "github.com/ipld/go-ipld-prime/linking" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + "github.com/ipld/go-ipld-prime/node/basicnode" + "github.com/ipld/go-ipld-prime/traversal" + "github.com/ipld/go-ipld-prime/traversal/selector" + selectorParser "github.com/ipld/go-ipld-prime/traversal/selector/parse" "github.com/urfave/cli/v2" ) @@ -43,3 +61,101 @@ func GetCarBlock(c *cli.Context) error { _, err = outStream.Write(blk.RawData()) return err } + +// GetCarDag is a command to get a dag out of a car +func GetCarDag(c *cli.Context) error { + if c.Args().Len() < 3 { + return fmt.Errorf("usage: car get-dag [-s selector] ") + } + + bs, err := blockstore.OpenReadOnly(c.Args().Get(0)) + if err != nil { + return err + } + + // string to CID + blkCid, err := cid.Parse(c.Args().Get(1)) + if err != nil { + return err + } + + outStore, err := blockstore.OpenReadWrite(c.Args().Get(2), []cid.Cid{blkCid}) + if err != nil { + return err + } + + ls := cidlink.DefaultLinkSystem() + ls.StorageReadOpener = func(_ linking.LinkContext, l datamodel.Link) (io.Reader, error) { + if cl, ok := l.(*cidlink.Link); ok { + blk, err := bs.Get(cl.Cid) + if err != nil { + if err == ipfsbs.ErrNotFound { + if c.Bool("strict") { + return nil, err + } + return nil, traversal.SkipMe{} + } + return nil, err + } + return bytes.NewBuffer(blk.RawData()), nil + } + return nil, fmt.Errorf("unknown link type: %T", l) + } + ls.StorageWriteOpener = func(_ linking.LinkContext) (io.Writer, linking.BlockWriteCommitter, error) { + buf := bytes.NewBuffer(nil) + return buf, func(l datamodel.Link) error { + if cl, ok := l.(*cidlink.Link); ok { + blk, err := blocks.NewBlockWithCid(buf.Bytes(), cl.Cid) + if err != nil { + return err + } + return outStore.Put(blk) + } + return fmt.Errorf("unknown link type: %T", l) + }, nil + } + + rootlnk := cidlink.Link{ + Cid: blkCid, + } + node, err := ls.Load(linking.LinkContext{}, rootlnk, basicnode.Prototype.Any) + if err != nil { + return err + } + + // selector traversal + s := selectorParser.CommonSelector_MatchAllRecursively + if c.IsSet("selector") { + sn, err := selectorParser.ParseJSONSelector(c.String("selector")) + if err != nil { + return err + } + s, err = selector.CompileSelector(sn) + if err != nil { + return err + } + } + + lnkProto := cidlink.LinkPrototype{ + Prefix: blkCid.Prefix(), + } + err = traversal.WalkMatching(node, s, func(p traversal.Progress, n datamodel.Node) error { + if p.LastBlock.Link != nil { + if cl, ok := p.LastBlock.Link.(*cidlink.Link); ok { + lnkProto = cidlink.LinkPrototype{ + Prefix: cl.Prefix(), + } + } + } + _, err = ls.Store(linking.LinkContext{}, lnkProto, n) + if err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + + return outStore.Finalize() +} From ddf7bef0fe53e9fe473b64a635ac8062a2a0d8ba Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 14 Sep 2021 00:15:49 -0700 Subject: [PATCH 3568/3817] integrate `car/` cli into `cmd/car` (#233) * integrate `car/` cli into `cmd/car` This commit was moved from ipld/go-car@4fd3550e490d9425190b9d1562e136da68f8785a --- ipld/car/car/main.go | 114 ------------------------------------- ipld/car/cmd/car/car.go | 6 ++ ipld/car/cmd/car/verify.go | 99 ++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 114 deletions(-) delete mode 100644 ipld/car/car/main.go create mode 100644 ipld/car/cmd/car/verify.go diff --git a/ipld/car/car/main.go b/ipld/car/car/main.go deleted file mode 100644 index e1be8830e..000000000 --- a/ipld/car/car/main.go +++ /dev/null @@ -1,114 +0,0 @@ -package main - -import ( - "bufio" - "encoding/json" - "fmt" - "io" - "os" - - "github.com/ipld/go-car" - - cli "github.com/urfave/cli" -) - -var headerCmd = cli.Command{ - Name: "header", - Action: func(c *cli.Context) error { - if !c.Args().Present() { - return fmt.Errorf("must pass a car file to inspect") - } - arg := c.Args().First() - - fi, err := os.Open(arg) - if err != nil { - return err - } - defer fi.Close() - - ch, err := car.ReadHeader(bufio.NewReader(fi)) - if err != nil { - return err - } - - b, err := json.MarshalIndent(ch, "", " ") - if err != nil { - return err - } - fmt.Println(string(b)) - return nil - }, -} - -var verifyCmd = cli.Command{ - Name: "verify", - Action: func(c *cli.Context) error { - if !c.Args().Present() { - return fmt.Errorf("must pass a car file to inspect") - } - arg := c.Args().First() - - fi, err := os.Open(arg) - if err != nil { - return err - } - defer fi.Close() - - cr, err := car.NewCarReader(fi) - if err != nil { - return err - } - - for { - _, err := cr.Next() - if err == io.EOF { - return nil - } - if err != nil { - return err - } - } - }, -} - -var lsCmd = cli.Command{ - Name: "ls", - Action: func(c *cli.Context) error { - if !c.Args().Present() { - return fmt.Errorf("must pass a car file to inspect") - } - arg := c.Args().First() - - fi, err := os.Open(arg) - if err != nil { - return err - } - defer fi.Close() - - cr, err := car.NewCarReader(fi) - if err != nil { - return err - } - - for { - blk, err := cr.Next() - if err == io.EOF { - return nil - } - if err != nil { - return err - } - fmt.Println(blk.Cid()) - } - }, -} - -func main() { - app := cli.NewApp() - app.Commands = []cli.Command{ - headerCmd, - lsCmd, - verifyCmd, - } - app.Run(os.Args) -} diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index 69059b1d3..523372ed0 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -74,6 +74,12 @@ func main() { Usage: "List the CIDs in a car", Action: ListCar, }, + { + Name: "verify", + Aliases: []string{"v"}, + Usage: "Verify a CAR is wellformed", + Action: VerifyCar, + }, }, } diff --git a/ipld/car/cmd/car/verify.go b/ipld/car/cmd/car/verify.go new file mode 100644 index 000000000..c9c753d78 --- /dev/null +++ b/ipld/car/cmd/car/verify.go @@ -0,0 +1,99 @@ +package main + +import ( + "fmt" + "io" + "os" + + "github.com/ipfs/go-cid" + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/index" + "github.com/urfave/cli/v2" +) + +// VerifyCar is a command to check a files validity +func VerifyCar(c *cli.Context) error { + if c.Args().Len() == 0 { + return fmt.Errorf("usage: car verify ") + } + + // header + rx, err := carv2.OpenReader(c.Args().First()) + if err != nil { + return err + } + defer rx.Close() + roots, err := rx.Roots() + if err != nil { + return err + } + if len(roots) == 0 { + return fmt.Errorf("no roots listed in car header") + } + rootMap := make(map[cid.Cid]struct{}) + for _, r := range roots { + rootMap[r] = struct{}{} + } + + if rx.Version == 2 { + if rx.Header.DataSize == 0 { + return fmt.Errorf("size of wrapped v1 car listed as '0'") + } + + flen, err := os.Stat(c.Args().First()) + if err != nil { + return err + } + lengthToIndex := carv2.PragmaSize + carv2.HeaderSize + rx.Header.DataOffset + rx.Header.DataSize + if uint64(flen.Size()) > lengthToIndex && rx.Header.IndexOffset == 0 { + return fmt.Errorf("header claims no index, but extra bytes in file beyond data size") + } + if rx.Header.IndexOffset < lengthToIndex { + return fmt.Errorf("index offset overlaps with data") + } + } + + // blocks + fd, err := os.Open(c.Args().First()) + if err != nil { + return err + } + rd, err := carv2.NewBlockReader(fd) + if err != nil { + return err + } + + cidList := make([]cid.Cid, 0) + for { + blk, err := rd.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + delete(rootMap, blk.Cid()) + cidList = append(cidList, blk.Cid()) + } + + if len(rootMap) > 0 { + return fmt.Errorf("header lists root(s) not present as a block: %v", rootMap) + } + + // index + if rx.Version == 2 && rx.Header.HasIndex() { + idx, err := index.ReadFrom(rx.IndexReader()) + if err != nil { + return err + } + for _, c := range cidList { + if err := idx.GetAll(c, func(_ uint64) bool { + return true + }); err != nil { + return fmt.Errorf("could not look up known cid %s in index: %w", c, err) + } + } + } + + return nil +} From 5b9ab3208a53515b6fb171c5916bbae89d750457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Mon, 13 Sep 2021 14:32:29 +0200 Subject: [PATCH 3569/3817] cmd/car: add first testscript tests As an introductory proof of concept, covering "list" and "get-block". The script language is shell-like, with some test helpers, and narrowed down to just a linear list of commands. See: https://pkg.go.dev/github.com/rogpeppe/go-internal/testscript This commit was moved from ipld/go-car@7544041c42bbf97c169fdd2440ac18e7898530d3 --- ipld/car/cmd/car/car.go | 9 +++-- ipld/car/cmd/car/script_test.go | 34 ++++++++++++++++++ ...x4qmtwgxixsd7pn4tlanyrc3g3hwj75hlxrw.block | Bin 0 -> 907 bytes .../car/cmd/car/testdata/inputs/sample-v1.car | Bin 0 -> 479907 bytes .../car/testdata/inputs/sample-wrapped-v2.car | Bin 0 -> 521708 bytes .../car/cmd/car/testdata/script/get-block.txt | 19 ++++++++++ ipld/car/cmd/car/testdata/script/list.txt | 16 +++++++++ 7 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 ipld/car/cmd/car/script_test.go create mode 100644 ipld/car/cmd/car/testdata/inputs/bafy2bzacebohz654namrgmwjjx4qmtwgxixsd7pn4tlanyrc3g3hwj75hlxrw.block create mode 100644 ipld/car/cmd/car/testdata/inputs/sample-v1.car create mode 100644 ipld/car/cmd/car/testdata/inputs/sample-wrapped-v2.car create mode 100644 ipld/car/cmd/car/testdata/script/get-block.txt create mode 100644 ipld/car/cmd/car/testdata/script/list.txt diff --git a/ipld/car/cmd/car/car.go b/ipld/car/cmd/car/car.go index 523372ed0..12a1f1f1b 100644 --- a/ipld/car/cmd/car/car.go +++ b/ipld/car/cmd/car/car.go @@ -8,7 +8,9 @@ import ( "github.com/urfave/cli/v2" ) -func main() { +func main() { os.Exit(main1()) } + +func main1() int { app := &cli.App{ Name: "car", Usage: "Utility for working with car files", @@ -85,7 +87,8 @@ func main() { err := app.Run(os.Args) if err != nil { - log.Fatal(err) - os.Exit(1) + log.Println(err) + return 1 } + return 0 } diff --git a/ipld/car/cmd/car/script_test.go b/ipld/car/cmd/car/script_test.go new file mode 100644 index 000000000..dcf8f6b7a --- /dev/null +++ b/ipld/car/cmd/car/script_test.go @@ -0,0 +1,34 @@ +package main + +import ( + "flag" + "os" + "path/filepath" + "testing" + + "github.com/rogpeppe/go-internal/testscript" +) + +func TestMain(m *testing.M) { + os.Exit(testscript.RunMain(m, map[string]func() int{ + "car": main1, + })) +} + +var update = flag.Bool("u", false, "update testscript output files") + +func TestScript(t *testing.T) { + t.Parallel() + testscript.Run(t, testscript.Params{ + Dir: filepath.Join("testdata", "script"), + Setup: func(env *testscript.Env) error { + wd, err := os.Getwd() + if err != nil { + return err + } + env.Setenv("INPUTS", filepath.Join(wd, "testdata", "inputs")) + return nil + }, + UpdateScripts: *update, + }) +} diff --git a/ipld/car/cmd/car/testdata/inputs/bafy2bzacebohz654namrgmwjjx4qmtwgxixsd7pn4tlanyrc3g3hwj75hlxrw.block b/ipld/car/cmd/car/testdata/inputs/bafy2bzacebohz654namrgmwjjx4qmtwgxixsd7pn4tlanyrc3g3hwj75hlxrw.block new file mode 100644 index 0000000000000000000000000000000000000000..257d8522bd8bdf42b89f1f4b072bf1fb30bbee49 GIT binary patch literal 907 zcmeBmW;n~#7?H3-PVc86|Lt0X2=~1wuS{KZbQ@F6#Z||A%9l0%nHlhr^G(g4+y}qx ztTnf*%4O8vN?_jM{+X*=L*eb;cN#bD*riEpepdE15HaX3n8@wNztQiv#Ca2yskt1_ z`;wyGwme?a9C2XLv7G0HTB3{ZG0Qsde$zEw@6~&Uy4;sv4v9QGC%nf|_5a1UTW5a~ zILhy&tQlk)Zo2%qT~%T~_qWv#zW*!Qr~T{r0ofz9HP>q9u*p2FV4S$=AWucHQo|gU z;5jbyyz3;4Mckc#pXR+d#e2cQ+4)+h)i-)S3i4AqWbWW7Eb;$OwS>f38S(mbt11lZyywxryT#Vi}gX(%_LTx=ZmN93(PPma*-BYC(l}> z8uYlGEh1sPnL??;Dc8E-xc0J$?8CP;;x8Skn>~5c+0`HQxtk816|V{F+P`q!niV^y zP3~Q`bgHoKn=394-mCj_%*s8q;?1+z#|budx4pME2CgmR$X46fCZ+6V@;dsG%gOJ? zIm&Iajm;4Xe-=kvnJVwI=-+=`_u!dsTCqBJcYNJ9J0VRm;{M%?#gdW&%#6)9v?A0Q z7z-CXVN!^9uwA9&eR%eajqAQN*#!!f#byQYh6rn?3b@?**JtaLoOXIpycQrYx|OaS4&`YW{j>itL28)YSJ;AH9&2_U7|(K5#W_-O-pa^=#Pk zclzfN7}VY=uW^a3_@Hp`ltV-!(*XwB0;xDz^%X#S))_nAd(xSGlvifXf&;nyX1A=g zV{V_#Uv~Ji&+%`q5XmHoy?ry=|-QC@TySuwXfZ!6`JxGAyhwQW0D%N+C|GW6B z&f>a9O{&Ly-nU1O9+NxI*xts*(ZO$tT8#n-*e+)mgz#PvS&t~k82RzAB9>MjOd+o_4OvAu(tjWzIhi2rX3qJ)6ff&J9kP7mV^XFOuvNS(Jd z{qMJ#FJ(8=_wi8BHQ}u=;<6T|Q=!iO zNagh)J~5HNCL!IMHV1)65=PfX6{g=_2mH!-7ERt++QP$L zF9`?O)_d_m#~buJ~qfQNd<{?*_Wvv1wjvydgi3JwF;2fgnvgXnOaj(8EsnL_~UKh{jg9!E! zc+84I=z?-6Rn5l;4CEwW$VA)=+TrX@(`9fOd&f&m8=xi7@U5BpuZN>~jE^d+nv==6Y~5oF#z9g3{DpJZ9+ zqM&7n$6;!v9Wh$eR%gyd5|mWF1?0UjUad|(!F~szc|(H7>(*YkZsVb5g_^aIQqtT3 z>cGB*FMJcYB@ls{NOg*`EY6~`1oxFO^caW zb9d>NuU_afcpyN=P7H`+JOPVCWllW)JZpV3eKbT#AW<(7sMAE zK>YBc_O2;aFRm35{t<7uWR;p;fkY;ifZ*EjZ&eiFk?u^mJ-f8NrxRqaYZ{?0uZ*FWo zn^7jViqYO&QbyBAS{LLQZ1W|iI(m1Vnx2`cgiq(Av18j}s$TjnE_qiMQ+b#~?rXCm zgP#zVZF#(W2tUTKZ#Jz1%C|t()+?gr1}T~=%8^=MXQNdPD%VV5#9Vk3vjC~To4a8w ztT=1gvu1Vq(a-@|4NgW5ZU7-et0g(BrkCc!`p`=dQ$|00*B$Vj_HC~%SR5x>;%sh2 z>S?S?!1XewedO68u!GBRHu3ESDGG}+NV;{%p?J3V^� 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 Tx_{@>Ah$cBb;X>)D9fct)9`U;$XV@y4$E$ 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�))Ql(v8hmmZlds4Ky~+?@9HxaF6h_xH{) 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

  • ecf&5+ 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 Date: Fri, 16 Jul 2021 12:04:25 +0100 Subject: [PATCH 3490/3817] Refactor utility io conversions into one internal package Improve reader type conversion by checking if type satisfies ReaderAt to avoid unnecessary wrapping. Move io converters into one place. Fixes: - https://github.com/ipld/go-car/issues/145 This commit was moved from ipld/go-car@f3fc595830a066d8ba80517b316b59a7c2233f5f --- ipld/car/v2/index_gen.go | 19 +------------------ ipld/car/v2/internal/io/converter.go | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/ipld/car/v2/index_gen.go b/ipld/car/v2/index_gen.go index 223939d20..859909cae 100644 --- a/ipld/car/v2/index_gen.go +++ b/ipld/car/v2/index_gen.go @@ -4,7 +4,6 @@ import ( "fmt" "io" "os" - "sync" internalio "github.com/ipld/go-car/v2/internal/io" @@ -97,22 +96,6 @@ func GenerateIndexFromFile(path string) (index.Index, error) { return GenerateIndex(f) } -var _ io.ReaderAt = (*readSeekerAt)(nil) - -type readSeekerAt struct { - rs io.ReadSeeker - mu sync.Mutex -} - -func (rsa *readSeekerAt) ReadAt(p []byte, off int64) (n int, err error) { - rsa.mu.Lock() - defer rsa.mu.Unlock() - if _, err := rsa.rs.Seek(off, io.SeekStart); err != nil { - return 0, err - } - return rsa.rs.Read(p) -} - // ReadOrGenerateIndex accepts both CAR v1 and v2 format, and reads or generates an index for it. // When the given reader is in CAR v1 format an index is always generated. // For a payload in CAR v2 format, an index is only generated if Header.HasIndex returns false. @@ -137,7 +120,7 @@ func ReadOrGenerateIndex(rs io.ReadSeeker) (index.Index, error) { return GenerateIndex(rs) case 2: // Read CAR v2 format - v2r, err := NewReader(&readSeekerAt{rs: rs}) + v2r, err := NewReader(internalio.ToReaderAt(rs)) if err != nil { return nil, err } diff --git a/ipld/car/v2/internal/io/converter.go b/ipld/car/v2/internal/io/converter.go index 4c723ec46..65350d34b 100644 --- a/ipld/car/v2/internal/io/converter.go +++ b/ipld/car/v2/internal/io/converter.go @@ -3,6 +3,7 @@ package io import ( "io" "io/ioutil" + "sync" ) var ( @@ -10,6 +11,7 @@ var ( _ io.ByteReader = (*readSeekerPlusByte)(nil) _ io.ByteReader = (*discardingReadSeekerPlusByte)(nil) _ io.ReadSeeker = (*discardingReadSeekerPlusByte)(nil) + _ io.ReaderAt = (*readSeekerAt)(nil) ) type ( @@ -30,6 +32,11 @@ type ( io.ReadSeeker io.ByteReader } + + readSeekerAt struct { + rs io.ReadSeeker + mu sync.Mutex + } ) func ToByteReader(r io.Reader) io.ByteReader { @@ -49,6 +56,13 @@ func ToByteReadSeeker(r io.Reader) ByteReadSeeker { return &discardingReadSeekerPlusByte{Reader: r} } +func ToReaderAt(rs io.ReadSeeker) io.ReaderAt { + if ra, ok := rs.(io.ReaderAt); ok { + return ra + } + return &readSeekerAt{rs: rs} +} + func (rb readerPlusByte) ReadByte() (byte, error) { return readByte(rb) } @@ -89,3 +103,12 @@ func readByte(r io.Reader) (byte, error) { _, err := io.ReadFull(r, p[:]) return p[0], err } + +func (rsa *readSeekerAt) ReadAt(p []byte, off int64) (n int, err error) { + rsa.mu.Lock() + defer rsa.mu.Unlock() + if _, err := rsa.rs.Seek(off, io.SeekStart); err != nil { + return 0, err + } + return rsa.rs.Read(p) +} From 520dcfae1229dbc0dac24431c151b0614d07483b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Thu, 15 Jul 2021 17:33:35 +0100 Subject: [PATCH 3491/3817] unify options and add more blockstore options We've agreed to have unified options, since many will be shared between the root and blockstore packages. Include docs, and update the tests. This commit was moved from ipld/go-car@7d8f54ffa8521591d11a6ca44312de47900792f6 --- ipld/car/v2/blockstore/readonly.go | 73 ++++++++++++++---- ipld/car/v2/blockstore/readwrite.go | 96 +++++++++++++----------- ipld/car/v2/blockstore/readwrite_test.go | 16 ++-- ipld/car/v2/index_gen.go | 39 ++++++---- ipld/car/v2/options.go | 67 +++++++++++++++++ ipld/car/v2/reader.go | 6 +- 6 files changed, 215 insertions(+), 82 deletions(-) create mode 100644 ipld/car/v2/options.go diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index cf799fe7a..5b9c8535d 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -24,7 +24,7 @@ import ( var _ blockstore.Blockstore = (*ReadOnly)(nil) -// ReadOnly provides a read-only Car Block Store. +// ReadOnly provides a read-only CAR Block Store. type ReadOnly struct { // mu allows ReadWrite to be safe for concurrent use. // It's in ReadOnly so that read operations also grab read locks, @@ -41,6 +41,35 @@ type ReadOnly struct { // If we called carv2.NewReaderMmap, remember to close it too. carv2Closer io.Closer + + ropts carv2.ReadOptions +} + +// UseWholeCIDs is a read option which makes a CAR blockstore identify blocks by +// whole CIDs, and not just their multihashes. The default is to use +// multihashes, which matches the current semantics of go-ipfs-blockstore v1. +// +// Enabling this option affects a number of methods, including read-only ones: +// +// • Get, Has, and HasSize will only return a block +// only if the entire CID is present in the CAR file. +// +// • DeleteBlock will delete a block only when the entire CID matches. +// +// • AllKeysChan will return the original whole CIDs, instead of with their +// multicodec set to "raw" to just provide multihashes. +// +// • If AllowDuplicatePuts isn't set, +// Put and PutMany will deduplicate by the whole CID, +// allowing different CIDs with equal multihashes. +// +// Note that this option only affects the blockstore, and is ignored by the root +// go-car/v2 package. +func UseWholeCIDs(enable bool) carv2.ReadOption { + return func(o *carv2.ReadOptions) { + // TODO: update methods like Get, Has, and AllKeysChan to obey this. + o.BlockstoreUseWholeCIDs = enable + } } // NewReadOnly creates a new ReadOnly blockstore from the backing with a optional index as idx. @@ -52,7 +81,12 @@ type ReadOnly struct { // * For a CAR v2 backing an index is only generated if Header.HasIndex returns false. // // There is no need to call ReadOnly.Close on instances returned by this function. -func NewReadOnly(backing io.ReaderAt, idx index.Index) (*ReadOnly, error) { +func NewReadOnly(backing io.ReaderAt, idx index.Index, opts ...carv2.ReadOption) (*ReadOnly, error) { + b := &ReadOnly{} + for _, opt := range opts { + opt(&b.ropts) + } + version, err := readVersion(backing) if err != nil { return nil, err @@ -60,13 +94,15 @@ func NewReadOnly(backing io.ReaderAt, idx index.Index) (*ReadOnly, error) { switch version { case 1: if idx == nil { - if idx, err = generateIndex(backing); err != nil { + if idx, err = generateIndex(backing, opts...); err != nil { return nil, err } } - return &ReadOnly{backing: backing, idx: idx}, nil + b.backing = backing + b.idx = idx + return b, nil case 2: - v2r, err := carv2.NewReader(backing) + v2r, err := carv2.NewReader(backing, opts...) if err != nil { return nil, err } @@ -76,11 +112,13 @@ func NewReadOnly(backing io.ReaderAt, idx index.Index) (*ReadOnly, error) { if err != nil { return nil, err } - } else if idx, err = generateIndex(v2r.CarV1Reader()); err != nil { + } else if idx, err = generateIndex(v2r.CarV1Reader(), opts...); err != nil { return nil, err } } - return &ReadOnly{backing: v2r.CarV1Reader(), idx: idx}, nil + b.backing = v2r.CarV1Reader() + b.idx = idx + return b, nil default: return nil, fmt.Errorf("unsupported car version: %v", version) } @@ -97,7 +135,7 @@ func readVersion(at io.ReaderAt) (uint64, error) { return carv2.ReadVersion(rr) } -func generateIndex(at io.ReaderAt) (index.Index, error) { +func generateIndex(at io.ReaderAt, opts ...carv2.ReadOption) (index.Index, error) { var rs io.ReadSeeker switch r := at.(type) { case io.ReadSeeker: @@ -105,19 +143,19 @@ func generateIndex(at io.ReaderAt) (index.Index, error) { default: rs = internalio.NewOffsetReadSeeker(r, 0) } - return carv2.GenerateIndex(rs) + return carv2.GenerateIndex(rs, opts...) } // OpenReadOnly opens a read-only blockstore from a CAR file (either v1 or v2), generating an index if it does not exist. // Note, the generated index if the index does not exist is ephemeral and only stored in memory. // See car.GenerateIndex and Index.Attach for persisting index onto a CAR file. -func OpenReadOnly(path string) (*ReadOnly, error) { +func OpenReadOnly(path string, opts ...carv2.ReadOption) (*ReadOnly, error) { f, err := mmap.Open(path) if err != nil { return nil, err } - robs, err := NewReadOnly(f, nil) + robs, err := NewReadOnly(f, nil, opts...) if err != nil { return nil, err } @@ -191,7 +229,7 @@ func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { return -1, err } rdr := internalio.NewOffsetReadSeeker(b.backing, int64(idx)) - frameLen, err := varint.ReadUvarint(rdr) + sectionLen, err := varint.ReadUvarint(rdr) if err != nil { return -1, blockstore.ErrNotFound } @@ -202,7 +240,7 @@ func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { if !readCid.Equals(key) { return -1, blockstore.ErrNotFound } - return int(frameLen) - cidLen, err + return int(sectionLen) - cidLen, err } // Put is not supported and always returns an error. @@ -249,9 +287,14 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return // TODO: log this error } - // Null padding; treat it as EOF. + // Null padding; by default it's an error. if length == 0 { - break // TODO make this an optional behaviour; by default we should error + if b.ropts.ZeroLegthSectionAsEOF { + break + } else { + return // TODO: log this error + // return fmt.Errorf("carv1 null padding not allowed by default; see WithZeroLegthSectionAsEOF") + } } thisItemForNxt := rdr.Offset() diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 0829b2c7d..a1c7fd84f 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -39,33 +39,19 @@ type ReadWrite struct { idx *insertionIndex header carv2.Header - dedupCids bool + wopts carv2.WriteOptions } -// TODO consider exposing interfaces -type Option func(*ReadWrite) // TODO consider unifying with writer options - -// WithCarV1Padding sets the padding to be added between CAR v2 header and its data payload on Finalize. -func WithCarV1Padding(p uint64) Option { - return func(b *ReadWrite) { - b.header = b.header.WithCarV1Padding(p) - } -} - -// WithIndexPadding sets the padding between data payload and its index on Finalize. -func WithIndexPadding(p uint64) Option { - return func(b *ReadWrite) { - b.header = b.header.WithIndexPadding(p) - } -} - -// WithCidDeduplication makes Put calls ignore blocks if the blockstore already -// has the exact same CID. -// This can help avoid redundancy in a CARv1's list of CID-Block pairs. +// AllowDuplicatePuts is a write option which makes a CAR blockstore not +// deduplicate blocks in Put and PutMany. The default is to deduplicate, +// which matches the current semantics of go-ipfs-blockstore v1. // -// Note that this compares whole CIDs, not just multihashes. -func WithCidDeduplication(b *ReadWrite) { // TODO should this take a bool and return an option to allow disabling dedupliation? - b.dedupCids = true +// Note that this option only affects the blockstore, and is ignored by the root +// go-car/v2 package. +func AllowDuplicatePuts(allow bool) carv2.WriteOption { + return func(o *carv2.WriteOptions) { + o.BlockstoreAllowDuplicatePuts = allow + } } // OpenReadWrite creates a new ReadWrite at the given path with a provided set of root CIDs and options. @@ -80,7 +66,7 @@ func WithCidDeduplication(b *ReadWrite) { // TODO should this take a bool and re // header (i.e. the inner CAR v1 header) onto the file before returning. // // When the given path already exists, the blockstore will attempt to resume from it. -// On resumption the existing data frames in file are re-indexed, allowing the caller to continue +// On resumption the existing data sections in file are re-indexed, allowing the caller to continue // putting any remaining blocks without having to re-ingest blocks for which previous ReadWrite.Put // returned successfully. // @@ -93,7 +79,7 @@ func WithCidDeduplication(b *ReadWrite) { // TODO should this take a bool and re // 1. start with a complete CAR v2 car.Pragma. // 2. contain a complete CAR v1 data header with root CIDs matching the CIDs passed to the // constructor, starting at offset optionally padded by WithCarV1Padding, followed by zero or -// more complete data frames. If any corrupt data frames are present the resumption will fail. +// more complete data sections. If any corrupt data sections are present the resumption will fail. // Note, if set previously, the blockstore must use the same WithCarV1Padding option as before, // since this option is used to locate the CAR v1 data payload. // @@ -102,7 +88,7 @@ func WithCidDeduplication(b *ReadWrite) { // TODO should this take a bool and re // // Resuming from finalized files is allowed. However, resumption will regenerate the index // regardless by scanning every existing block in file. -func OpenReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, error) { +func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.ReadWriteOption) (*ReadWrite, error) { // TODO: enable deduplication by default now that resumption is automatically attempted. f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0o666) // TODO: Should the user be able to configure FileMode permissions? if err != nil { @@ -129,13 +115,27 @@ func OpenReadWrite(path string, roots []cid.Cid, opts ...Option) (*ReadWrite, er idx: newInsertionIndex(), header: carv2.NewHeader(0), } + for _, opt := range opts { - opt(rwbs) + switch opt := opt.(type) { + case carv2.ReadOption: + opt(&rwbs.ropts) + case carv2.WriteOption: + opt(&rwbs.wopts) + } + } + if p := rwbs.wopts.CarV1Padding; p > 0 { + rwbs.header = rwbs.header.WithCarV1Padding(p) + } + if p := rwbs.wopts.IndexPadding; p > 0 { + rwbs.header = rwbs.header.WithIndexPadding(p) } rwbs.carV1Writer = internalio.NewOffsetWriter(rwbs.f, int64(rwbs.header.CarV1Offset)) v1r := internalio.NewOffsetReadSeeker(rwbs.f, int64(rwbs.header.CarV1Offset)) - rwbs.ReadOnly = ReadOnly{backing: v1r, idx: rwbs.idx, carv2Closer: rwbs.f} + rwbs.ReadOnly.backing = v1r + rwbs.ReadOnly.idx = rwbs.idx + rwbs.ReadOnly.carv2Closer = rwbs.f if resume { if err = rwbs.resumeWithRoots(roots); err != nil { @@ -237,13 +237,13 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { if err != nil { return err } - frameOffset := int64(0) - if frameOffset, err = v1r.Seek(int64(offset), io.SeekStart); err != nil { + sectionOffset := int64(0) + if sectionOffset, err = v1r.Seek(int64(offset), io.SeekStart); err != nil { return err } for { - // Grab the length of the frame. + // Grab the length of the section. // Note that ReadUvarint wants a ByteReader. length, err := varint.ReadUvarint(v1r) if err != nil { @@ -253,10 +253,13 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { return err } - // Null padding; treat zero-length frames as an EOF. - // They don't contain a CID nor block, so they're not useful. + // Null padding; by default it's an error. if length == 0 { - break // TODO This behaviour should be an option, not default. By default we should error. Hook this up to a write option + if b.ropts.ZeroLegthSectionAsEOF { + break + } else { + return fmt.Errorf("carv1 null padding not allowed by default; see WithZeroLegthSectionAsEOF") + } } // Grab the CID. @@ -264,16 +267,16 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { if err != nil { return err } - b.idx.insertNoReplace(c, uint64(frameOffset)) + b.idx.insertNoReplace(c, uint64(sectionOffset)) - // Seek to the next frame by skipping the block. - // The frame length includes the CID, so subtract it. - if frameOffset, err = v1r.Seek(int64(length)-int64(n), io.SeekCurrent); err != nil { + // Seek to the next section by skipping the block. + // The section length includes the CID, so subtract it. + if sectionOffset, err = v1r.Seek(int64(length)-int64(n), io.SeekCurrent); err != nil { return err } } // Seek to the end of last skipped block where the writer should resume writing. - _, err = b.carV1Writer.Seek(frameOffset, io.SeekStart) + _, err = b.carV1Writer.Seek(sectionOffset, io.SeekStart) return err } @@ -304,8 +307,17 @@ func (b *ReadWrite) PutMany(blks []blocks.Block) error { for _, bl := range blks { c := bl.Cid() - if b.dedupCids && b.idx.hasExactCID(c) { - continue + + if !b.wopts.BlockstoreAllowDuplicatePuts { + if b.ropts.BlockstoreUseWholeCIDs && b.idx.hasExactCID(c) { + continue // deduplicated by CID + } + if !b.ropts.BlockstoreUseWholeCIDs { + _, err := b.idx.Get(c) + if err == nil { + continue // deduplicated by hash + } + } } n := uint64(b.carV1Writer.Position()) diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 884edca75..3a8099fbd 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -122,15 +122,19 @@ func TestBlockstore(t *testing.T) { func TestBlockstorePutSameHashes(t *testing.T) { tdir := t.TempDir() + + // wbs allows duplicate puts. wbs, err := blockstore.OpenReadWrite( filepath.Join(tdir, "readwrite.car"), nil, + blockstore.AllowDuplicatePuts(true), ) require.NoError(t, err) t.Cleanup(func() { wbs.Finalize() }) + // wbs deduplicates puts by CID. wbsd, err := blockstore.OpenReadWrite( filepath.Join(tdir, "readwrite-dedup.car"), nil, - blockstore.WithCidDeduplication, + blockstore.UseWholeCIDs(true), ) require.NoError(t, err) t.Cleanup(func() { wbsd.Finalize() }) @@ -276,7 +280,7 @@ func TestBlockstoreNullPadding(t *testing.T) { // A sample null-padded CARv1 file. paddedV1 = append(paddedV1, make([]byte, 2048)...) - rbs, err := blockstore.NewReadOnly(bufferReaderAt(paddedV1), nil) + rbs, err := blockstore.NewReadOnly(bufferReaderAt(paddedV1), nil, carv2.ZeroLegthSectionAsEOF) require.NoError(t, err) roots, err := rbs.Roots() @@ -483,8 +487,8 @@ func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) { subject, err := blockstore.OpenReadWrite( path, WantRoots, - blockstore.WithCarV1Padding(wantCarV1Padding), - blockstore.WithIndexPadding(wantIndexPadding)) + carv2.UseCarV1Padding(wantCarV1Padding), + carv2.UseIndexPadding(wantIndexPadding)) require.NoError(t, err) t.Cleanup(func() { subject.Close() }) require.NoError(t, subject.Put(oneTestBlockWithCidV1)) @@ -544,7 +548,7 @@ func TestReadWriteResumptionFromFileWithDifferentCarV1PaddingIsError(t *testing. subject, err := blockstore.OpenReadWrite( path, WantRoots, - blockstore.WithCarV1Padding(1413)) + carv2.UseCarV1Padding(1413)) require.NoError(t, err) t.Cleanup(func() { subject.Close() }) require.NoError(t, subject.Put(oneTestBlockWithCidV1)) @@ -553,7 +557,7 @@ func TestReadWriteResumptionFromFileWithDifferentCarV1PaddingIsError(t *testing. resumingSubject, err := blockstore.OpenReadWrite( path, WantRoots, - blockstore.WithCarV1Padding(1314)) + carv2.UseCarV1Padding(1314)) require.EqualError(t, err, "cannot resume from file with mismatched CARv1 offset; "+ "`WithCarV1Padding` option must match the padding on file. "+ "Expected padding value of 1413 but got 1314") diff --git a/ipld/car/v2/index_gen.go b/ipld/car/v2/index_gen.go index 859909cae..4fa9ac1df 100644 --- a/ipld/car/v2/index_gen.go +++ b/ipld/car/v2/index_gen.go @@ -18,7 +18,12 @@ import ( // GenerateIndex generates index for a given car in v1 format. // The index can be stored using index.Save into a file or serialized using index.WriteTo. -func GenerateIndex(v1r io.Reader) (index.Index, error) { +func GenerateIndex(v1r io.Reader, opts ...ReadOption) (index.Index, error) { + var o ReadOptions + for _, opt := range opts { + opt(&o) + } + reader := internalio.ToByteReadSeeker(v1r) header, err := carv1.ReadHeader(reader) if err != nil { @@ -35,20 +40,20 @@ func GenerateIndex(v1r io.Reader) (index.Index, error) { } records := make([]index.Record, 0) - // Record the start of each frame, with first frame starring from current position in the + // Record the start of each section, with first section starring from current position in the // reader, i.e. right after the header, since we have only read the header so far. - var frameOffset int64 + var sectionOffset int64 // The Seek call below is equivalent to getting the reader.offset directly. // We get it through Seek to only depend on APIs of a typical io.Seeker. // This would also reduce refactoring in case the utility reader is moved. - if frameOffset, err = reader.Seek(0, io.SeekCurrent); err != nil { + if sectionOffset, err = reader.Seek(0, io.SeekCurrent); err != nil { return nil, err } for { - // Read the frame's length. - frameLen, err := varint.ReadUvarint(reader) + // Read the section's length. + sectionLen, err := varint.ReadUvarint(reader) if err != nil { if err == io.EOF { break @@ -56,11 +61,13 @@ func GenerateIndex(v1r io.Reader) (index.Index, error) { return nil, err } - // Null padding; treat zero-length frames as an EOF. - // They don't contain a CID nor block, so they're not useful. - // TODO: Amend the CARv1 spec to explicitly allow this. - if frameLen == 0 { - break + // Null padding; by default it's an error. + if sectionLen == 0 { + if o.ZeroLegthSectionAsEOF { + break + } else { + return nil, fmt.Errorf("carv1 null padding not allowed by default; see ZeroLegthSectionAsEOF") + } } // Read the CID. @@ -68,12 +75,12 @@ func GenerateIndex(v1r io.Reader) (index.Index, error) { if err != nil { return nil, err } - records = append(records, index.Record{Cid: c, Idx: uint64(frameOffset)}) + records = append(records, index.Record{Cid: c, Idx: uint64(sectionOffset)}) - // Seek to the next frame by skipping the block. - // The frame length includes the CID, so subtract it. - remainingFrameLen := int64(frameLen) - int64(cidLen) - if frameOffset, err = reader.Seek(remainingFrameLen, io.SeekCurrent); err != nil { + // Seek to the next section by skipping the block. + // The section length includes the CID, so subtract it. + remainingSectionLen := int64(sectionLen) - int64(cidLen) + if sectionOffset, err = reader.Seek(remainingSectionLen, io.SeekCurrent); err != nil { return nil, err } } diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go new file mode 100644 index 000000000..ad859d1a0 --- /dev/null +++ b/ipld/car/v2/options.go @@ -0,0 +1,67 @@ +package car + +// ReadOptions holds the configured options after applying a number of +// ReadOption funcs. +// +// This type should not be used directly by end users; it's only exposed as a +// side effect of ReadOption. +type ReadOptions struct { + ZeroLegthSectionAsEOF bool + + BlockstoreUseWholeCIDs bool +} + +// ReadOption describes an option which affects behavior when parsing CAR files. +type ReadOption func(*ReadOptions) + +func (ReadOption) readWriteOption() {} + +var _ ReadWriteOption = ReadOption(nil) + +// WriteOptions holds the configured options after applying a number of +// WriteOption funcs. +// +// This type should not be used directly by end users; it's only exposed as a +// side effect of WriteOption. +type WriteOptions struct { + CarV1Padding uint64 + IndexPadding uint64 + + BlockstoreAllowDuplicatePuts bool +} + +// WriteOption describes an option which affects behavior when encoding CAR files. +type WriteOption func(*WriteOptions) + +func (WriteOption) readWriteOption() {} + +var _ ReadWriteOption = WriteOption(nil) + +// ReadWriteOption is either a ReadOption or a WriteOption. +type ReadWriteOption interface { + readWriteOption() +} + +// ZeroLegthSectionAsEOF is a read option which allows a CARv1 decoder to treat +// a zero-length section as the end of the input CAR file. For example, this can +// be useful to allow "null padding" after a CARv1 without knowing where the +// padding begins. +func ZeroLegthSectionAsEOF(o *ReadOptions) { + o.ZeroLegthSectionAsEOF = true +} + +// UseCarV1Padding is a write option which sets the padding to be added between +// CAR v2 header and its data payload on Finalize. +func UseCarV1Padding(p uint64) WriteOption { + return func(o *WriteOptions) { + o.CarV1Padding = p + } +} + +// UseIndexPadding is a write option which sets the padding between data payload +// and its index on Finalize. +func UseIndexPadding(p uint64) WriteOption { + return func(o *WriteOptions) { + o.IndexPadding = p + } +} diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index ad231c0f1..f845a81ce 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -20,13 +20,13 @@ type Reader struct { } // OpenReader is a wrapper for NewReader which opens the file at path. -func OpenReader(path string) (*Reader, error) { +func OpenReader(path string, opts ...ReadOption) (*Reader, error) { f, err := mmap.Open(path) if err != nil { return nil, err } - r, err := NewReader(f) + r, err := NewReader(f, opts...) if err != nil { return nil, err } @@ -38,7 +38,7 @@ func OpenReader(path string) (*Reader, error) { // NewReader constructs a new reader that reads CAR v2 from the given r. // Upon instantiation, the reader inspects the payload by reading the pragma and will return // an error if the pragma does not represent a CAR v2. -func NewReader(r io.ReaderAt) (*Reader, error) { +func NewReader(r io.ReaderAt, opts ...ReadOption) (*Reader, error) { cr := &Reader{ r: r, } From 4adbd3d63414e7a501e2a21aa2a9f5b7bd0456ca Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Fri, 16 Jul 2021 11:50:48 +0100 Subject: [PATCH 3492/3817] Implement examples and tests for `index` package Add examples that show how to read and write an index to/from file. Test marshalling and unmarshalling index files. Run `gofumt` on repo. This commit was moved from ipld/go-car@0aefa5b13039d83b109a8cc2921cf0f41f46933a --- ipld/car/v2/blockstore/readonly_test.go | 5 +- ipld/car/v2/index/example_test.go | 93 +++++++++++ ipld/car/v2/index/index_test.go | 172 +++++++++++++++++++++ ipld/car/v2/index/indexsorted_test.go | 3 +- ipld/car/v2/testdata/sample-index.carindex | Bin 0 -> 209505 bytes 5 files changed, 270 insertions(+), 3 deletions(-) create mode 100644 ipld/car/v2/index/example_test.go create mode 100644 ipld/car/v2/index/index_test.go create mode 100644 ipld/car/v2/testdata/sample-index.carindex diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 97ed73091..10f9804cd 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -2,13 +2,14 @@ package blockstore import ( "context" - blockstore "github.com/ipfs/go-ipfs-blockstore" - "github.com/ipfs/go-merkledag" "io" "os" "testing" "time" + blockstore "github.com/ipfs/go-ipfs-blockstore" + "github.com/ipfs/go-merkledag" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" diff --git a/ipld/car/v2/index/example_test.go b/ipld/car/v2/index/example_test.go new file mode 100644 index 000000000..cf6d56a63 --- /dev/null +++ b/ipld/car/v2/index/example_test.go @@ -0,0 +1,93 @@ +package index_test + +import ( + "fmt" + "os" + "reflect" + + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/index" +) + +// ExampleReadFrom unmarshalls an index from an indexed CARv2 file, and for each root CID prints the +// offset at which its corresponding block starts relative to the wrapped CARv1 data payload. +func ExampleReadFrom() { + // Open the CARv2 file + cr, err := carv2.OpenReader("../testdata/sample-wrapped-v2.car") + if err != nil { + panic(err) + } + defer cr.Close() + + // Get root CIDs in the CARv1 file. + roots, err := cr.Roots() + if err != nil { + panic(err) + } + + // Read and unmarshall index within CARv2 file. + idx, err := index.ReadFrom(cr.IndexReader()) + if err != nil { + panic(err) + } + + // For each root CID print the offset relative to CARv1 data payload. + for _, r := range roots { + offset, err := idx.Get(r) + if err != nil { + panic(err) + } + fmt.Printf("Frame with CID %v starts at offset %v relative to CARv1 data payload.\n", r, offset) + } + + // Output: + // Frame with CID bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy starts at offset 61 relative to CARv1 data payload. +} + +// ExampleSave unmarshalls an index from an indexed CARv2 file, and stores it as a separate +// file on disk. +func ExampleSave() { + // Open the CARv2 file + src := "../testdata/sample-wrapped-v2.car" + cr, err := carv2.OpenReader(src) + if err != nil { + panic(err) + } + defer cr.Close() + + // Read and unmarshall index within CARv2 file. + idx, err := index.ReadFrom(cr.IndexReader()) + if err != nil { + panic(err) + } + + // Store the index alone onto destination file. + dest := "../testdata/sample-index.carindex" + err = index.Save(idx, dest) + if err != nil { + panic(err) + } + + // Open the destination file that contains the index only. + f, err := os.Open(dest) + if err != nil { + panic(err) + } + defer f.Close() + + // Read and unmarshall the destination file as a separate index instance. + reReadIdx, err := index.ReadFrom(f) + if err != nil { + panic(err) + } + + // Expect indices to be equal. + if reflect.DeepEqual(idx, reReadIdx) { + fmt.Printf("Saved index file at %v matches the index embedded in CARv2 at %v.\n", dest, src) + } else { + panic("expected to get the same index as the CARv2 file") + } + + // Output: + // Saved index file at ../testdata/sample-index.carindex matches the index embedded in CARv2 at ../testdata/sample-wrapped-v2.car. +} diff --git a/ipld/car/v2/index/index_test.go b/ipld/car/v2/index/index_test.go new file mode 100644 index 000000000..945ca1bbc --- /dev/null +++ b/ipld/car/v2/index/index_test.go @@ -0,0 +1,172 @@ +package index + +import ( + "bytes" + "io" + "os" + "path/filepath" + "testing" + + blocks "github.com/ipfs/go-block-format" + "github.com/ipld/go-car/v2/internal/carv1" + "github.com/ipld/go-car/v2/internal/carv1/util" + "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-varint" + "github.com/stretchr/testify/require" +) + +func TestNew(t *testing.T) { + tests := []struct { + name string + codec multicodec.Code + want Index + wantErr bool + }{ + { + name: "CarSortedIndexCodecIsConstructed", + codec: multicodec.CarIndexSorted, + want: newSorted(), + }, + { + name: "ValidMultiCodecButUnknwonToIndexIsError", + codec: multicodec.Cidv1, + wantErr: true, + }, + { + name: "IndexSingleSortedMultiCodecIsError", + codec: multicodec.Code(indexSingleSorted), + wantErr: true, + }, + { + name: "IndexHashedMultiCodecIsError", + codec: multicodec.Code(indexHashed), + wantErr: true, + }, + { + name: "IndexGobHashedMultiCodecIsError", + codec: multicodec.Code(indexGobHashed), + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := New(tt.codec) + if tt.wantErr { + require.Error(t, err) + } else { + require.Equal(t, tt.want, got) + } + }) + } +} + +func TestReadFrom(t *testing.T) { + idxf, err := os.Open("../testdata/sample-index.carindex") + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, idxf.Close()) }) + + subject, err := ReadFrom(idxf) + require.NoError(t, err) + + crf, err := os.Open("../testdata/sample-v1.car") + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, crf.Close()) }) + cr, err := carv1.NewCarReader(crf) + require.NoError(t, err) + + for { + wantBlock, err := cr.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + + // Get offset from the index for a CID and assert it exists + gotOffset, err := subject.Get(wantBlock.Cid()) + require.NoError(t, err) + require.NotZero(t, gotOffset) + + // Seek to the offset on CARv1 file + _, err = crf.Seek(int64(gotOffset), io.SeekStart) + require.NoError(t, err) + + // Read the fame at offset and assert the frame corresponds to the expected block. + gotCid, gotData, err := util.ReadNode(crf) + require.NoError(t, err) + gotBlock, err := blocks.NewBlockWithCid(gotData, gotCid) + require.NoError(t, err) + require.Equal(t, wantBlock, gotBlock) + } +} + +func TestWriteTo(t *testing.T) { + // Read sample index on file + idxf, err := os.Open("../testdata/sample-index.carindex") + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, idxf.Close()) }) + + // Unmarshall to get expected index + wantIdx, err := ReadFrom(idxf) + require.NoError(t, err) + + // Write the same index out + dest := filepath.Join(t.TempDir(), "index-write-to-test.carindex") + destF, err := os.Create(dest) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, destF.Close()) }) + require.NoError(t, WriteTo(wantIdx, destF)) + + // Seek to the beginning of the written out file. + _, err = destF.Seek(0, io.SeekStart) + require.NoError(t, err) + + // Read the written index back + gotIdx, err := ReadFrom(destF) + require.NoError(t, err) + + // Assert they are equal + require.Equal(t, wantIdx, gotIdx) +} + +func TestSave(t *testing.T) { + // Read sample index on file + idxf, err := os.Open("../testdata/sample-index.carindex") + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, idxf.Close()) }) + + // Unmarshall to get expected index + wantIdx, err := ReadFrom(idxf) + require.NoError(t, err) + + // Save the same index at destination + dest := filepath.Join(t.TempDir(), "index-write-to-test.carindex") + require.NoError(t, Save(wantIdx, dest)) + + // Open the saved file + destF, err := os.Open(dest) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, destF.Close()) }) + + // Read the written index back + gotIdx, err := ReadFrom(destF) + require.NoError(t, err) + + // Assert they are equal + require.Equal(t, wantIdx, gotIdx) +} + +func TestMarshalledIndexStartsWithCodec(t *testing.T) { + // Read sample index on file + idxf, err := os.Open("../testdata/sample-index.carindex") + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, idxf.Close()) }) + + // Unmarshall to get expected index + wantIdx, err := ReadFrom(idxf) + require.NoError(t, err) + + // Assert the first two bytes are the corresponding multicodec code. + buf := new(bytes.Buffer) + require.NoError(t, WriteTo(wantIdx, buf)) + require.Equal(t, varint.ToUvarint(uint64(multicodec.CarIndexSorted)), buf.Bytes()[:2]) +} diff --git a/ipld/car/v2/index/indexsorted_test.go b/ipld/car/v2/index/indexsorted_test.go index c491bedf5..fe0ca961a 100644 --- a/ipld/car/v2/index/indexsorted_test.go +++ b/ipld/car/v2/index/indexsorted_test.go @@ -1,10 +1,11 @@ package index import ( + "testing" + "github.com/ipfs/go-merkledag" "github.com/multiformats/go-multicodec" "github.com/stretchr/testify/require" - "testing" ) func TestSortedIndexCodec(t *testing.T) { diff --git a/ipld/car/v2/testdata/sample-index.carindex b/ipld/car/v2/testdata/sample-index.carindex new file mode 100644 index 0000000000000000000000000000000000000000..0f91e36507fc21d3a93a00f32bf330e024405af8 GIT binary patch literal 209505 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
    N*||>TJ{<{;C;+BF=Tv` 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 Date: Fri, 16 Jul 2021 12:51:13 +0100 Subject: [PATCH 3493/3817] Remove memory intensive writer and add warping tests Remove the memory intensive CARv2 writer implementation since it needs to be reimplemented anyway and not used. We will release writers if needed when it comes to SelectiveCar writer implementation. Just now it is not clear if we want a CARv2 writer that works with the deprecated `format.NodeGetter`. Add tests for V1 wrapping functionality. Reformat code in repo for consistency using `gofumpt`. This commit was moved from ipld/go-car@0f79c3e852cf5fab1e9b02066267a2d78d43d991 --- ipld/car/v2/writer.go | 124 ------------------------------------- ipld/car/v2/writer_test.go | 95 ++++++++++++++-------------- 2 files changed, 45 insertions(+), 174 deletions(-) diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index 367597228..fa94913e9 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -1,138 +1,14 @@ package car import ( - "bytes" - "context" "io" "os" internalio "github.com/ipld/go-car/v2/internal/io" - "github.com/ipfs/go-cid" - format "github.com/ipfs/go-ipld-format" "github.com/ipld/go-car/v2/index" - "github.com/ipld/go-car/v2/internal/carv1" ) -const bulkPaddingBytesSize = 1024 - -var bulkPadding = make([]byte, bulkPaddingBytesSize) - -type ( - // padding represents the number of padding bytes. - padding uint64 - // writer writes CAR v2 into a given io.Writer. - writer struct { - NodeGetter format.NodeGetter - CarV1Padding uint64 - IndexPadding uint64 - - ctx context.Context - roots []cid.Cid - encodedCarV1 *bytes.Buffer - } -) - -// WriteTo writes this padding to the given writer as default value bytes. -func (p padding) WriteTo(w io.Writer) (n int64, err error) { - var reminder int64 - if p > bulkPaddingBytesSize { - reminder = int64(p % bulkPaddingBytesSize) - iter := int(p / bulkPaddingBytesSize) - for i := 0; i < iter; i++ { - if _, err = w.Write(bulkPadding); err != nil { - return - } - n += bulkPaddingBytesSize - } - } else { - reminder = int64(p) - } - - paddingBytes := make([]byte, reminder) - _, err = w.Write(paddingBytes) - n += reminder - return -} - -// newWriter instantiates a new CAR v2 writer. -func newWriter(ctx context.Context, ng format.NodeGetter, roots []cid.Cid) *writer { - return &writer{ - NodeGetter: ng, - ctx: ctx, - roots: roots, - encodedCarV1: new(bytes.Buffer), - } -} - -// WriteTo writes the given root CIDs according to CAR v2 specification. -func (w *writer) WriteTo(writer io.Writer) (n int64, err error) { - _, err = writer.Write(Pragma) - if err != nil { - return - } - n += int64(PragmaSize) - // We read the entire car into memory because GenerateIndex takes a reader. - // TODO Future PRs will make this more efficient by exposing necessary interfaces in index pacakge so that - // this can be done in an streaming manner. - if err = carv1.WriteCar(w.ctx, w.NodeGetter, w.roots, w.encodedCarV1); err != nil { - return - } - carV1Len := w.encodedCarV1.Len() - - wn, err := w.writeHeader(writer, carV1Len) - if err != nil { - return - } - n += wn - - wn, err = padding(w.CarV1Padding).WriteTo(writer) - if err != nil { - return - } - n += wn - - carV1Bytes := w.encodedCarV1.Bytes() - wwn, err := writer.Write(carV1Bytes) - if err != nil { - return - } - n += int64(wwn) - - wn, err = padding(w.IndexPadding).WriteTo(writer) - if err != nil { - return - } - n += wn - - wn, err = w.writeIndex(writer, carV1Bytes) - if err == nil { - n += wn - } - return -} - -func (w *writer) writeHeader(writer io.Writer, carV1Len int) (int64, error) { - header := NewHeader(uint64(carV1Len)). - WithCarV1Padding(w.CarV1Padding). - WithIndexPadding(w.IndexPadding) - return header.WriteTo(writer) -} - -func (w *writer) writeIndex(writer io.Writer, carV1 []byte) (int64, error) { - // TODO avoid recopying the bytes by refactoring index once it is integrated here. - // Right now we copy the bytes since index takes a writer. - // Consider refactoring index to make this process more efficient. - // We should avoid reading the entire car into memory since it can be large. - reader := bytes.NewReader(carV1) - idx, err := GenerateIndex(reader) - if err != nil { - return 0, err - } - // FIXME refactor index to expose the number of bytes written. - return 0, index.WriteTo(idx, writer) -} - // WrapV1File is a wrapper around WrapV1 that takes filesystem paths. // The source path is assumed to exist, and the destination path is overwritten. // Note that the destination path might still be created even if an error diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index 633fc4626..ff8f3bff2 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -1,10 +1,17 @@ package car import ( - "bytes" "context" + "io" + "io/ioutil" + "os" + "path/filepath" "testing" + "github.com/ipld/go-car/v2/index" + "github.com/ipld/go-car/v2/internal/carv1" + "github.com/stretchr/testify/require" + "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" "github.com/ipfs/go-merkledag" @@ -12,56 +19,44 @@ import ( "github.com/stretchr/testify/assert" ) -func TestPadding_WriteTo(t *testing.T) { - tests := []struct { - name string - padding padding - wantBytes []byte - wantN int64 - wantErr bool - }{ - { - "ZeroPaddingIsNoBytes", - padding(0), - nil, - 0, - false, - }, - { - "NonZeroPaddingIsCorrespondingZeroValueBytes", - padding(3), - []byte{0x00, 0x00, 0x00}, - 3, - false, - }, - { - "PaddingLargerThanTheBulkPaddingSizeIsCorrespondingZeroValueBytes", - padding(1025), - make([]byte, 1025), - 1025, - false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - w := &bytes.Buffer{} - gotN, gotErr := tt.padding.WriteTo(w) - if tt.wantErr { - assert.Error(t, gotErr) - return - } - gotBytes := w.Bytes() - assert.Equal(t, tt.wantN, gotN) - assert.Equal(t, tt.wantBytes, gotBytes) - }) - } -} +func TestWrapV1(t *testing.T) { + // Produce a CARv1 file to test wrapping with. + dagSvc := dstest.Mock() + src := filepath.Join(t.TempDir(), "unwrapped-test-v1.car") + sf, err := os.Create(src) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, sf.Close()) }) + require.NoError(t, carv1.WriteCar(context.Background(), dagSvc, generateRootCid(t, dagSvc), sf)) + + // Wrap the test CARv1 file + dest := filepath.Join(t.TempDir(), "wrapped-test-v1.car") + df, err := os.Create(dest) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, df.Close()) }) + _, err = sf.Seek(0, io.SeekStart) + require.NoError(t, err) + require.NoError(t, WrapV1(sf, df)) + + // Assert wrapped file is valid CARv2 with CARv1 data payload matching the original CARv1 file. + subject, err := OpenReader(dest) + t.Cleanup(func() { require.NoError(t, subject.Close()) }) + require.NoError(t, err) + + // Assert CARv1 data payloads are identical. + _, err = sf.Seek(0, io.SeekStart) + require.NoError(t, err) + wantPayload, err := ioutil.ReadAll(sf) + require.NoError(t, err) + gotPayload, err := ioutil.ReadAll(subject.CarV1Reader()) + require.NoError(t, err) + require.Equal(t, wantPayload, gotPayload) -func TestNewWriter(t *testing.T) { - dagService := dstest.Mock() - wantRoots := generateRootCid(t, dagService) - writer := newWriter(context.Background(), dagService, wantRoots) - assert.Equal(t, wantRoots, writer.roots) + // Assert embedded index in CARv2 is same as index generated from the original CARv1. + wantIdx, err := GenerateIndexFromFile(src) + require.NoError(t, err) + gotIdx, err := index.ReadFrom(subject.IndexReader()) + require.NoError(t, err) + require.Equal(t, wantIdx, gotIdx) } func generateRootCid(t *testing.T, adder format.NodeAdder) []cid.Cid { From dd5c6d89a665c02ef5e3162d27b2f222ff0cf2d3 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" <301855+masih@users.noreply.github.com> Date: Fri, 16 Jul 2021 14:23:05 +0100 Subject: [PATCH 3494/3817] Rename `CarV1` with `Data` in API to be consistent with spec Rename terminology to match what the spec uses to describe the inner CARv1 payload, i.e. Data payload. Update docs to use CARv1 and CARv2 consistently. Fix typo in Options API. See: - https://ipld.io/specs/transport/car/carv2/ This commit was moved from ipld/go-car@aa62719de471679b027e9e0f18d420f4692aa3f9 --- ipld/car/v2/blockstore/doc.go | 10 +-- ipld/car/v2/blockstore/readonly.go | 20 +++--- ipld/car/v2/blockstore/readonly_test.go | 2 +- ipld/car/v2/blockstore/readwrite.go | 78 ++++++++++++------------ ipld/car/v2/blockstore/readwrite_test.go | 32 +++++----- ipld/car/v2/car.go | 64 +++++++++---------- ipld/car/v2/car_test.go | 26 ++++---- ipld/car/v2/doc.go | 4 +- ipld/car/v2/example_test.go | 2 +- ipld/car/v2/index/doc.go | 4 +- ipld/car/v2/index_gen.go | 18 +++--- ipld/car/v2/internal/carv1/doc.go | 2 +- ipld/car/v2/options.go | 18 +++--- ipld/car/v2/reader.go | 35 +++++------ ipld/car/v2/writer.go | 6 +- ipld/car/v2/writer_test.go | 2 +- 16 files changed, 161 insertions(+), 162 deletions(-) diff --git a/ipld/car/v2/blockstore/doc.go b/ipld/car/v2/blockstore/doc.go index 7210d742b..180dc7292 100644 --- a/ipld/car/v2/blockstore/doc.go +++ b/ipld/car/v2/blockstore/doc.go @@ -2,11 +2,11 @@ // This package provides two flavours of blockstore: ReadOnly and ReadWrite. // // The ReadOnly blockstore provides a read-only random access from a given data payload either in -// unindexed v1 format or indexed/unindexed v2 format: -// * ReadOnly.NewReadOnly can be used to instantiate a new read-only blockstore for a given CAR v1 -// or CAR v2 data payload with an optional index override. -// * ReadOnly.OpenReadOnly can be used to instantiate a new read-only blockstore for a given CAR v1 -// or CAR v2 file with automatic index generation if the index is not present. +// unindexed CARv1 format or indexed/unindexed v2 format: +// * ReadOnly.NewReadOnly can be used to instantiate a new read-only blockstore for a given CARv1 +// or CARv2 data payload with an optional index override. +// * ReadOnly.OpenReadOnly can be used to instantiate a new read-only blockstore for a given CARv1 +// or CARv2 file with automatic index generation if the index is not present. // // The ReadWrite blockstore allows writing and reading of the blocks concurrently. The user of this // blockstore is responsible for calling ReadWrite.Finalize when finished writing blocks. diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 5b9c8535d..bbbba8f4e 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -34,9 +34,9 @@ type ReadOnly struct { // For simplicity, the entirety of the blockstore methods grab the mutex. mu sync.RWMutex - // The backing containing the CAR in v1 format. + // The backing containing the data payload in CARv1 format. backing io.ReaderAt - // The CAR v1 content index. + // The CARv1 content index. idx index.Index // If we called carv2.NewReaderMmap, remember to close it too. @@ -54,8 +54,6 @@ type ReadOnly struct { // • Get, Has, and HasSize will only return a block // only if the entire CID is present in the CAR file. // -// • DeleteBlock will delete a block only when the entire CID matches. -// // • AllKeysChan will return the original whole CIDs, instead of with their // multicodec set to "raw" to just provide multihashes. // @@ -73,12 +71,12 @@ func UseWholeCIDs(enable bool) carv2.ReadOption { } // NewReadOnly creates a new ReadOnly blockstore from the backing with a optional index as idx. -// This function accepts both CAR v1 and v2 backing. +// This function accepts both CARv1 and CARv2 backing. // The blockstore is instantiated with the given index if it is not nil. // // Otherwise: -// * For a CAR v1 backing an index is generated. -// * For a CAR v2 backing an index is only generated if Header.HasIndex returns false. +// * For a CARv1 backing an index is generated. +// * For a CARv2 backing an index is only generated if Header.HasIndex returns false. // // There is no need to call ReadOnly.Close on instances returned by this function. func NewReadOnly(backing io.ReaderAt, idx index.Index, opts ...carv2.ReadOption) (*ReadOnly, error) { @@ -112,11 +110,11 @@ func NewReadOnly(backing io.ReaderAt, idx index.Index, opts ...carv2.ReadOption) if err != nil { return nil, err } - } else if idx, err = generateIndex(v2r.CarV1Reader(), opts...); err != nil { + } else if idx, err = generateIndex(v2r.DataReader(), opts...); err != nil { return nil, err } } - b.backing = v2r.CarV1Reader() + b.backing = v2r.DataReader() b.idx = idx return b, nil default: @@ -169,7 +167,7 @@ func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) { return bcid, data, err } -// DeleteBlock is unsupported and always returns an error. +// DeleteBlock is unsupported and always panics. func (b *ReadOnly) DeleteBlock(_ cid.Cid) error { panic("called write method on a read-only blockstore") } @@ -289,7 +287,7 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { // Null padding; by default it's an error. if length == 0 { - if b.ropts.ZeroLegthSectionAsEOF { + if b.ropts.ZeroLengthSectionAsEOF { break } else { return // TODO: log this error diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 10f9804cd..3d75fd96d 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -131,7 +131,7 @@ func newReaderFromV2File(t *testing.T, carv2Path string) *carv1.CarReader { t.Cleanup(func() { f.Close() }) v2r, err := carv2.NewReader(f) require.NoError(t, err) - v1r, err := carv1.NewCarReader(v2r.CarV1Reader()) + v1r, err := carv1.NewCarReader(v2r.DataReader()) require.NoError(t, err) return v1r } diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index a1c7fd84f..693e52f91 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -23,7 +23,7 @@ import ( var _ blockstore.Blockstore = (*ReadWrite)(nil) -// ReadWrite implements a blockstore that stores blocks in CAR v2 format. +// ReadWrite implements a blockstore that stores blocks in CARv2 format. // Blocks put into the blockstore can be read back once they are successfully written. // This implementation is preferable for a write-heavy workload. // The blocks are written immediately on Put and PutAll calls, while the index is stored in memory @@ -33,8 +33,8 @@ var _ blockstore.Blockstore = (*ReadWrite)(nil) // Upon calling Finalize header is finalized and index is written out. // Once finalized, all read and write calls to this blockstore will result in panics. type ReadWrite struct { - f *os.File - carV1Writer *internalio.OffsetWriteSeeker + f *os.File + dataWriter *internalio.OffsetWriteSeeker ReadOnly idx *insertionIndex header carv2.Header @@ -57,13 +57,13 @@ func AllowDuplicatePuts(allow bool) carv2.WriteOption { // OpenReadWrite creates a new ReadWrite at the given path with a provided set of root CIDs and options. // // ReadWrite.Finalize must be called once putting and reading blocks are no longer needed. -// Upon calling ReadWrite.Finalize the CAR v2 header and index are written out onto the file and the +// Upon calling ReadWrite.Finalize the CARv2 header and index are written out onto the file and the // backing file is closed. Once finalized, all read and write calls to this blockstore will result // in panics. Note, ReadWrite.Finalize must be called on an open instance regardless of whether any // blocks were put or not. // // If a file at given path does not exist, the instantiation will write car.Pragma and data payload -// header (i.e. the inner CAR v1 header) onto the file before returning. +// header (i.e. the inner CARv1 header) onto the file before returning. // // When the given path already exists, the blockstore will attempt to resume from it. // On resumption the existing data sections in file are re-indexed, allowing the caller to continue @@ -73,15 +73,15 @@ func AllowDuplicatePuts(allow bool) carv2.WriteOption { // Resumption only works on files that were created by a previous instance of a ReadWrite // blockstore. This means a file created as a result of a successful call to OpenReadWrite can be // resumed from as long as write operations such as ReadWrite.Put, ReadWrite.PutMany returned -// successfully. On resumption the roots argument and WithCarV1Padding option must match the +// successfully. On resumption the roots argument and WithDataPadding option must match the // previous instantiation of ReadWrite blockstore that created the file. More explicitly, the file // resuming from must: -// 1. start with a complete CAR v2 car.Pragma. -// 2. contain a complete CAR v1 data header with root CIDs matching the CIDs passed to the -// constructor, starting at offset optionally padded by WithCarV1Padding, followed by zero or +// 1. start with a complete CARv2 car.Pragma. +// 2. contain a complete CARv1 data header with root CIDs matching the CIDs passed to the +// constructor, starting at offset optionally padded by WithDataPadding, followed by zero or // more complete data sections. If any corrupt data sections are present the resumption will fail. -// Note, if set previously, the blockstore must use the same WithCarV1Padding option as before, -// since this option is used to locate the CAR v1 data payload. +// Note, if set previously, the blockstore must use the same WithDataPadding option as before, +// since this option is used to locate the CARv1 data payload. // // Note, resumption should be used with WithCidDeduplication, so that blocks that are successfully // written into the file are not re-written. Unless, the user explicitly wants duplicate blocks. @@ -124,15 +124,15 @@ func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.ReadWriteOption) opt(&rwbs.wopts) } } - if p := rwbs.wopts.CarV1Padding; p > 0 { - rwbs.header = rwbs.header.WithCarV1Padding(p) + if p := rwbs.wopts.DataPadding; p > 0 { + rwbs.header = rwbs.header.WithDataPadding(p) } if p := rwbs.wopts.IndexPadding; p > 0 { rwbs.header = rwbs.header.WithIndexPadding(p) } - rwbs.carV1Writer = internalio.NewOffsetWriter(rwbs.f, int64(rwbs.header.CarV1Offset)) - v1r := internalio.NewOffsetReadSeeker(rwbs.f, int64(rwbs.header.CarV1Offset)) + rwbs.dataWriter = internalio.NewOffsetWriter(rwbs.f, int64(rwbs.header.DataOffset)) + v1r := internalio.NewOffsetReadSeeker(rwbs.f, int64(rwbs.header.DataOffset)) rwbs.ReadOnly.backing = v1r rwbs.ReadOnly.idx = rwbs.idx rwbs.ReadOnly.carv2Closer = rwbs.f @@ -154,13 +154,13 @@ func (b *ReadWrite) initWithRoots(roots []cid.Cid) error { if _, err := b.f.WriteAt(carv2.Pragma, 0); err != nil { return err } - return carv1.WriteHeader(&carv1.CarHeader{Roots: roots, Version: 1}, b.carV1Writer) + return carv1.WriteHeader(&carv1.CarHeader{Roots: roots, Version: 1}, b.dataWriter) } func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { - // On resumption it is expected that the CAR v2 Pragma, and the CAR v1 header is successfully written. + // On resumption it is expected that the CARv2 Pragma, and the CARv1 header is successfully written. // Otherwise we cannot resume from the file. - // Read pragma to assert if b.f is indeed a CAR v2. + // Read pragma to assert if b.f is indeed a CARv2. version, err := carv2.ReadVersion(b.f) if err != nil { // The file is not a valid CAR file and cannot resume from it. @@ -168,36 +168,36 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { return err } if version != 2 { - // The file is not a CAR v2 and we cannot resume from it. + // The file is not a CARv2 and we cannot resume from it. return fmt.Errorf("cannot resume on CAR file with version %v", version) } - // Check if file was finalized by trying to read the CAR v2 header. + // Check if file was finalized by trying to read the CARv2 header. // We check because if finalized the CARv1 reader behaviour needs to be adjusted since - // EOF will not signify end of CAR v1 payload. i.e. index is most likely present. + // EOF will not signify end of CARv1 payload. i.e. index is most likely present. var headerInFile carv2.Header _, err = headerInFile.ReadFrom(internalio.NewOffsetReadSeeker(b.f, carv2.PragmaSize)) // If reading CARv2 header succeeded, and CARv1 offset in header is not zero then the file is // most-likely finalized. Check padding and truncate the file to remove index. // Otherwise, carry on reading the v1 payload at offset determined from b.header. - if err == nil && headerInFile.CarV1Offset != 0 { - if headerInFile.CarV1Offset != b.header.CarV1Offset { - // Assert that the padding on file matches the given WithCarV1Padding option. - wantPadding := headerInFile.CarV1Offset - carv2.PragmaSize - carv2.HeaderSize - gotPadding := b.header.CarV1Offset - carv2.PragmaSize - carv2.HeaderSize + if err == nil && headerInFile.DataOffset != 0 { + if headerInFile.DataOffset != b.header.DataOffset { + // Assert that the padding on file matches the given WithDataPadding option. + wantPadding := headerInFile.DataOffset - carv2.PragmaSize - carv2.HeaderSize + gotPadding := b.header.DataOffset - carv2.PragmaSize - carv2.HeaderSize return fmt.Errorf( "cannot resume from file with mismatched CARv1 offset; "+ - "`WithCarV1Padding` option must match the padding on file. "+ + "`WithDataPadding` option must match the padding on file. "+ "Expected padding value of %v but got %v", wantPadding, gotPadding, ) - } else if headerInFile.CarV1Size != 0 { + } else if headerInFile.DataSize != 0 { // If header in file contains the size of car v1, then the index is most likely present. // Since we will need to re-generate the index, as the one in file is flattened, truncate // the file so that the Readonly.backing has the right set of bytes to deal with. // This effectively means resuming from a finalized file will wipe its index even if there // are no blocks put unless the user calls finalize. - if err := b.f.Truncate(int64(headerInFile.CarV1Offset + headerInFile.CarV1Size)); err != nil { + if err := b.f.Truncate(int64(headerInFile.DataOffset + headerInFile.DataSize)); err != nil { return err } } else { @@ -214,11 +214,11 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { } } - // Use the given CAR v1 padding to instantiate the CAR v1 reader on file. + // Use the given CARv1 padding to instantiate the CARv1 reader on file. v1r := internalio.NewOffsetReadSeeker(b.ReadOnly.backing, 0) header, err := carv1.ReadHeader(v1r) if err != nil { - // Cannot read the CAR v1 header; the file is most likely corrupt. + // Cannot read the CARv1 header; the file is most likely corrupt. return fmt.Errorf("error reading car header: %w", err) } if !header.Matches(carv1.CarHeader{Roots: roots, Version: 1}) { @@ -255,7 +255,7 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { // Null padding; by default it's an error. if length == 0 { - if b.ropts.ZeroLegthSectionAsEOF { + if b.ropts.ZeroLengthSectionAsEOF { break } else { return fmt.Errorf("carv1 null padding not allowed by default; see WithZeroLegthSectionAsEOF") @@ -276,7 +276,7 @@ func (b *ReadWrite) resumeWithRoots(roots []cid.Cid) error { } } // Seek to the end of last skipped block where the writer should resume writing. - _, err = b.carV1Writer.Seek(sectionOffset, io.SeekStart) + _, err = b.dataWriter.Seek(sectionOffset, io.SeekStart) return err } @@ -286,7 +286,7 @@ func (b *ReadWrite) unfinalize() error { } func (b *ReadWrite) panicIfFinalized() { - if b.header.CarV1Size != 0 { + if b.header.DataSize != 0 { panic("must not use a read-write blockstore after finalizing") } } @@ -320,8 +320,8 @@ func (b *ReadWrite) PutMany(blks []blocks.Block) error { } } - n := uint64(b.carV1Writer.Position()) - if err := util.LdWrite(b.carV1Writer, c.Bytes(), bl.RawData()); err != nil { + n := uint64(b.dataWriter.Position()) + if err := util.LdWrite(b.dataWriter, c.Bytes(), bl.RawData()); err != nil { return err } b.idx.insertNoReplace(c, n) @@ -329,11 +329,11 @@ func (b *ReadWrite) PutMany(blks []blocks.Block) error { return nil } -// Finalize finalizes this blockstore by writing the CAR v2 header, along with flattened index +// Finalize finalizes this blockstore by writing the CARv2 header, along with flattened index // for more efficient subsequent read. // After this call, this blockstore can no longer be used for read or write. func (b *ReadWrite) Finalize() error { - if b.header.CarV1Size != 0 { + if b.header.DataSize != 0 { // Allow duplicate Finalize calls, just like Close. // Still error, just like ReadOnly.Close; it should be discarded. return fmt.Errorf("called Finalize twice") @@ -342,7 +342,7 @@ func (b *ReadWrite) Finalize() error { b.mu.Lock() defer b.mu.Unlock() // TODO check if add index option is set and don't write the index then set index offset to zero. - b.header = b.header.WithCarV1Size(uint64(b.carV1Writer.Position())) + b.header = b.header.WithDataSize(uint64(b.dataWriter.Position())) defer b.Close() // TODO if index not needed don't bother flattening it. diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 3a8099fbd..6612268f1 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -280,7 +280,7 @@ func TestBlockstoreNullPadding(t *testing.T) { // A sample null-padded CARv1 file. paddedV1 = append(paddedV1, make([]byte, 2048)...) - rbs, err := blockstore.NewReadOnly(bufferReaderAt(paddedV1), nil, carv2.ZeroLegthSectionAsEOF) + rbs, err := blockstore.NewReadOnly(bufferReaderAt(paddedV1), nil, carv2.ZeroLengthSectionAsEOF) require.NoError(t, err) roots, err := rbs.Roots() @@ -312,7 +312,7 @@ func TestBlockstoreResumption(t *testing.T) { require.NoError(t, err) path := filepath.Join(t.TempDir(), "readwrite-resume.car") - // Create an incomplete CAR v2 file with no blocks put. + // Create an incomplete CARv2 file with no blocks put. subject, err := blockstore.OpenReadWrite(path, r.Header.Roots) require.NoError(t, err) @@ -330,7 +330,7 @@ func TestBlockstoreResumption(t *testing.T) { // 30% chance of subject failing; more concretely: re-instantiating blockstore with the same // file without calling Finalize. The higher this percentage the slower the test runs - // considering the number of blocks in the original CAR v1 test payload. + // considering the number of blocks in the original CARv1 test payload. resume := rng.Float32() <= 0.3 // If testing resume case, then flip a coin to decide whether to finalize before blockstore // re-instantiation or not. Note, both cases should work for resumption since we do not @@ -376,12 +376,12 @@ func TestBlockstoreResumption(t *testing.T) { } require.NoError(t, subject.Close()) - // Finalize the blockstore to complete partially written CAR v2 file. + // Finalize the blockstore to complete partially written CARv2 file. subject, err = blockstore.OpenReadWrite(path, r.Header.Roots) require.NoError(t, err) require.NoError(t, subject.Finalize()) - // Assert resumed from file is a valid CAR v2 with index. + // Assert resumed from file is a valid CARv2 with index. v2f, err := os.Open(path) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, v2f.Close()) }) @@ -389,13 +389,13 @@ func TestBlockstoreResumption(t *testing.T) { require.NoError(t, err) require.True(t, v2r.Header.HasIndex()) - // Assert CAR v1 payload in file matches the original CAR v1 payload. + // Assert CARv1 payload in file matches the original CARv1 payload. _, err = v1f.Seek(0, io.SeekStart) require.NoError(t, err) wantPayloadReader, err := carv1.NewCarReader(v1f) require.NoError(t, err) - gotPayloadReader, err := carv1.NewCarReader(v2r.CarV1Reader()) + gotPayloadReader, err := carv1.NewCarReader(v2r.DataReader()) require.NoError(t, err) require.Equal(t, wantPayloadReader.Header, gotPayloadReader.Header) @@ -411,7 +411,7 @@ func TestBlockstoreResumption(t *testing.T) { require.Equal(t, wantNextBlock, gotNextBlock) } - // Assert index in resumed from file is identical to index generated directly from original CAR v1 payload. + // Assert index in resumed from file is identical to index generated directly from original CARv1 payload. _, err = v1f.Seek(0, io.SeekStart) require.NoError(t, err) gotIdx, err := index.ReadFrom(v2r.IndexReader()) @@ -423,7 +423,7 @@ func TestBlockstoreResumption(t *testing.T) { func TestBlockstoreResumptionIsSupportedOnFinalizedFile(t *testing.T) { path := filepath.Join(t.TempDir(), "readwrite-resume-finalized.car") - // Create an incomplete CAR v2 file with no blocks put. + // Create an incomplete CARv2 file with no blocks put. subject, err := blockstore.OpenReadWrite(path, []cid.Cid{}) require.NoError(t, err) require.NoError(t, subject.Finalize()) @@ -487,7 +487,7 @@ func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) { subject, err := blockstore.OpenReadWrite( path, WantRoots, - carv2.UseCarV1Padding(wantCarV1Padding), + carv2.UseDataPadding(wantCarV1Padding), carv2.UseIndexPadding(wantIndexPadding)) require.NoError(t, err) t.Cleanup(func() { subject.Close() }) @@ -500,8 +500,8 @@ func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) { t.Cleanup(func() { gotCarV2.Close() }) require.NoError(t, err) wantCarV1Offset := carv2.PragmaSize + carv2.HeaderSize + wantCarV1Padding - wantIndexOffset := wantCarV1Offset + gotCarV2.Header.CarV1Size + wantIndexPadding - require.Equal(t, wantCarV1Offset, gotCarV2.Header.CarV1Offset) + wantIndexOffset := wantCarV1Offset + gotCarV2.Header.DataSize + wantIndexPadding + require.Equal(t, wantCarV1Offset, gotCarV2.Header.DataOffset) require.Equal(t, wantIndexOffset, gotCarV2.Header.IndexOffset) require.NoError(t, gotCarV2.Close()) @@ -510,7 +510,7 @@ func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) { t.Cleanup(func() { f.Close() }) // Assert reading CARv1 directly at offset and size is as expected. - gotCarV1, err := carv1.NewCarReader(io.NewSectionReader(f, int64(wantCarV1Offset), int64(gotCarV2.Header.CarV1Size))) + gotCarV1, err := carv1.NewCarReader(io.NewSectionReader(f, int64(wantCarV1Offset), int64(gotCarV2.Header.DataSize))) require.NoError(t, err) require.Equal(t, WantRoots, gotCarV1.Header.Roots) gotOneBlock, err := gotCarV1.Next() @@ -548,7 +548,7 @@ func TestReadWriteResumptionFromFileWithDifferentCarV1PaddingIsError(t *testing. subject, err := blockstore.OpenReadWrite( path, WantRoots, - carv2.UseCarV1Padding(1413)) + carv2.UseDataPadding(1413)) require.NoError(t, err) t.Cleanup(func() { subject.Close() }) require.NoError(t, subject.Put(oneTestBlockWithCidV1)) @@ -557,9 +557,9 @@ func TestReadWriteResumptionFromFileWithDifferentCarV1PaddingIsError(t *testing. resumingSubject, err := blockstore.OpenReadWrite( path, WantRoots, - carv2.UseCarV1Padding(1314)) + carv2.UseDataPadding(1314)) require.EqualError(t, err, "cannot resume from file with mismatched CARv1 offset; "+ - "`WithCarV1Padding` option must match the padding on file. "+ + "`WithDataPadding` option must match the padding on file. "+ "Expected padding value of 1413 but got 1314") require.Nil(t, resumingSubject) } diff --git a/ipld/car/v2/car.go b/ipld/car/v2/car.go index 4d8c3f50a..d12683313 100644 --- a/ipld/car/v2/car.go +++ b/ipld/car/v2/car.go @@ -6,16 +6,16 @@ import ( ) const ( - // PragmaSize is the size of the CAR v2 pragma in bytes. + // PragmaSize is the size of the CARv2 pragma in bytes. PragmaSize = 11 - // HeaderSize is the fixed size of CAR v2 header in number of bytes. + // HeaderSize is the fixed size of CARv2 header in number of bytes. HeaderSize = 40 - // CharacteristicsSize is the fixed size of Characteristics bitfield within CAR v2 header in number of bytes. + // CharacteristicsSize is the fixed size of Characteristics bitfield within CARv2 header in number of bytes. CharacteristicsSize = 16 ) -// The pragma of a CAR v2, containing the version number.. -// This is a valid CAR v1 header, with version number set to 2. +// The pragma of a CARv2, containing the version number. +// This is a valid CARv1 header, with version number of 2 and no root CIDs. var Pragma = []byte{ 0x0a, // unit(10) 0xa1, // map(1) @@ -25,18 +25,18 @@ var Pragma = []byte{ } type ( - // Header represents the CAR v2 header/pragma. + // Header represents the CARv2 header/pragma. Header struct { - // 128-bit characteristics of this CAR v2 file, such as order, deduplication, etc. Reserved for future use. + // 128-bit characteristics of this CARv2 file, such as order, deduplication, etc. Reserved for future use. Characteristics Characteristics - // The offset from the beginning of the file at which the dump of CAR v1 starts. - CarV1Offset uint64 - // The size of CAR v1 encapsulated in this CAR v2 as bytes. - CarV1Size uint64 - // The offset from the beginning of the file at which the CAR v2 index begins. + // The byte-offset from the beginning of the CARv2 to the first byte of the CARv1 data payload. + DataOffset uint64 + // The byte-length of the CARv1 data payload. + DataSize uint64 + // The byte-offset from the beginning of the CARv2 to the first byte of the index payload. This value may be 0 to indicate the absence of index data. IndexOffset uint64 } - // Characteristics is a bitfield placeholder for capturing the characteristics of a CAR v2 such as order and determinism. + // Characteristics is a bitfield placeholder for capturing the characteristics of a CARv2 such as order and determinism. Characteristics struct { Hi uint64 Lo uint64 @@ -64,37 +64,37 @@ func (c *Characteristics) ReadFrom(r io.Reader) (int64, error) { return n, nil } -// NewHeader instantiates a new CAR v2 header, given the byte length of a CAR v1. -func NewHeader(carV1Size uint64) Header { +// NewHeader instantiates a new CARv2 header, given the data size. +func NewHeader(dataSize uint64) Header { header := Header{ - CarV1Size: carV1Size, + DataSize: dataSize, } - header.CarV1Offset = PragmaSize + HeaderSize - header.IndexOffset = header.CarV1Offset + carV1Size + header.DataOffset = PragmaSize + HeaderSize + header.IndexOffset = header.DataOffset + dataSize return header } -// WithIndexPadding sets the index offset from the beginning of the file for this header and returns the -// header for convenient chained calls. +// WithIndexPadding sets the index offset from the beginning of the file for this header and returns +// the header for convenient chained calls. // The index offset is calculated as the sum of PragmaSize, HeaderSize, -// Header.CarV1Size, and the given padding. +// Header.DataSize, and the given padding. func (h Header) WithIndexPadding(padding uint64) Header { h.IndexOffset = h.IndexOffset + padding return h } -// WithCarV1Padding sets the CAR v1 dump offset from the beginning of the file for this header and returns the -// header for convenient chained calls. -// The CAR v1 offset is calculated as the sum of PragmaSize, HeaderSize and the given padding. +// WithDataPadding sets the data payload byte-offset from the beginning of the file for this header +// and returns the header for convenient chained calls. +// The Data offset is calculated as the sum of PragmaSize, HeaderSize and the given padding. // The call to this function also shifts the Header.IndexOffset forward by the given padding. -func (h Header) WithCarV1Padding(padding uint64) Header { - h.CarV1Offset = PragmaSize + HeaderSize + padding +func (h Header) WithDataPadding(padding uint64) Header { + h.DataOffset = PragmaSize + HeaderSize + padding h.IndexOffset = h.IndexOffset + padding return h } -func (h Header) WithCarV1Size(size uint64) Header { - h.CarV1Size = size +func (h Header) WithDataSize(size uint64) Header { + h.DataSize = size h.IndexOffset = size + h.IndexOffset return h } @@ -112,8 +112,8 @@ func (h Header) WriteTo(w io.Writer) (n int64, err error) { return } buf := make([]byte, 24) - binary.LittleEndian.PutUint64(buf[:8], h.CarV1Offset) - binary.LittleEndian.PutUint64(buf[8:16], h.CarV1Size) + binary.LittleEndian.PutUint64(buf[:8], h.DataOffset) + binary.LittleEndian.PutUint64(buf[8:16], h.DataSize) binary.LittleEndian.PutUint64(buf[16:], h.IndexOffset) written, err := w.Write(buf) n += int64(written) @@ -132,8 +132,8 @@ func (h *Header) ReadFrom(r io.Reader) (int64, error) { if err != nil { return n, err } - h.CarV1Offset = binary.LittleEndian.Uint64(buf[:8]) - h.CarV1Size = binary.LittleEndian.Uint64(buf[8:16]) + h.DataOffset = binary.LittleEndian.Uint64(buf[:8]) + h.DataSize = binary.LittleEndian.Uint64(buf[8:16]) h.IndexOffset = binary.LittleEndian.Uint64(buf[16:]) return n, nil } diff --git a/ipld/car/v2/car_test.go b/ipld/car/v2/car_test.go index 83a552ba6..4223a5600 100644 --- a/ipld/car/v2/car_test.go +++ b/ipld/car/v2/car_test.go @@ -36,11 +36,11 @@ func TestCarV2PragmaLength(t *testing.T) { func TestCarV2PragmaIsValidCarV1Header(t *testing.T) { v1h, err := carv1.ReadHeader(bytes.NewReader(carv2.Pragma)) - assert.NoError(t, err, "cannot decode pragma as CBOR with CAR v1 header structure") + assert.NoError(t, err, "cannot decode pragma as CBOR with CARv1 header structure") assert.Equal(t, &carv1.CarHeader{ Roots: nil, Version: 2, - }, v1h, "CAR v2 pragma must be a valid CAR v1 header") + }, v1h, "CARv2 pragma must be a valid CARv1 header") } func TestHeader_WriteTo(t *testing.T) { @@ -70,8 +70,8 @@ func TestHeader_WriteTo(t *testing.T) { Characteristics: carv2.Characteristics{ Hi: 1001, Lo: 1002, }, - CarV1Offset: 99, - CarV1Size: 100, + DataOffset: 99, + DataSize: 100, IndexOffset: 101, }, []byte{ @@ -94,8 +94,8 @@ func TestHeader_WriteTo(t *testing.T) { } gotWrite := buf.Bytes() assert.Equal(t, tt.wantWrite, gotWrite, "Header.WriteTo() gotWrite = %v, wantWrite %v", gotWrite, tt.wantWrite) - assert.EqualValues(t, carv2.HeaderSize, uint64(len(gotWrite)), "WriteTo() CAR v2 header length must always be %v bytes long", carv2.HeaderSize) - assert.EqualValues(t, carv2.HeaderSize, uint64(written), "WriteTo() CAR v2 header byte count must always be %v bytes long", carv2.HeaderSize) + assert.EqualValues(t, carv2.HeaderSize, uint64(len(gotWrite)), "WriteTo() CARv2 header length must always be %v bytes long", carv2.HeaderSize) + assert.EqualValues(t, carv2.HeaderSize, uint64(written), "WriteTo() CARv2 header byte count must always be %v bytes long", carv2.HeaderSize) }) } } @@ -135,8 +135,8 @@ func TestHeader_ReadFrom(t *testing.T) { Characteristics: carv2.Characteristics{ Hi: 1001, Lo: 1002, }, - CarV1Offset: 99, - CarV1Size: 100, + DataOffset: 99, + DataSize: 100, IndexOffset: 101, }, false, @@ -168,13 +168,13 @@ func TestHeader_WithPadding(t *testing.T) { }, { "WhenOnlyPaddingCarV1BothOffsetsShift", - carv2.NewHeader(123).WithCarV1Padding(3), + carv2.NewHeader(123).WithDataPadding(3), carv2.PragmaSize + carv2.HeaderSize + 3, carv2.PragmaSize + carv2.HeaderSize + 3 + 123, }, { "WhenPaddingBothCarV1AndIndexBothOffsetsShiftWithAdditionalIndexShift", - carv2.NewHeader(123).WithCarV1Padding(3).WithIndexPadding(7), + carv2.NewHeader(123).WithDataPadding(3).WithIndexPadding(7), carv2.PragmaSize + carv2.HeaderSize + 3, carv2.PragmaSize + carv2.HeaderSize + 3 + 123 + 7, }, @@ -182,7 +182,7 @@ func TestHeader_WithPadding(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.EqualValues(t, tt.wantCarV1Offset, tt.subject.CarV1Offset) + assert.EqualValues(t, tt.wantCarV1Offset, tt.subject.DataOffset) assert.EqualValues(t, tt.wantIndexOffset, tt.subject.IndexOffset) }) } @@ -192,8 +192,8 @@ func TestNewHeaderHasExpectedValues(t *testing.T) { wantCarV1Len := uint64(1413) want := carv2.Header{ Characteristics: carv2.Characteristics{}, - CarV1Offset: carv2.PragmaSize + carv2.HeaderSize, - CarV1Size: wantCarV1Len, + DataOffset: carv2.PragmaSize + carv2.HeaderSize, + DataSize: wantCarV1Len, IndexOffset: carv2.PragmaSize + carv2.HeaderSize + wantCarV1Len, } got := carv2.NewHeader(wantCarV1Len) diff --git a/ipld/car/v2/doc.go b/ipld/car/v2/doc.go index 5b210211b..2029d7cf3 100644 --- a/ipld/car/v2/doc.go +++ b/ipld/car/v2/doc.go @@ -1,3 +1,3 @@ -// Package car represents the CAR v2 implementation. -// TODO add CAR v2 byte structure here. +// Package car represents the CARv2 implementation. +// TODO add CARv2 byte structure here. package car diff --git a/ipld/car/v2/example_test.go b/ipld/car/v2/example_test.go index 4e7446628..6944f2fa7 100644 --- a/ipld/car/v2/example_test.go +++ b/ipld/car/v2/example_test.go @@ -38,7 +38,7 @@ func ExampleWrapV1File() { if err != nil { panic(err) } - inner, err := ioutil.ReadAll(cr.CarV1Reader()) + inner, err := ioutil.ReadAll(cr.DataReader()) if err != nil { panic(err) } diff --git a/ipld/car/v2/index/doc.go b/ipld/car/v2/index/doc.go index 4c7beb1f8..41b860216 100644 --- a/ipld/car/v2/index/doc.go +++ b/ipld/car/v2/index/doc.go @@ -1,5 +1,5 @@ -// package index provides indexing functionality for CAR v1 data payload represented as a mapping of -// CID to offset. This can then be used to implement random access over a CAR v1. +// package index provides indexing functionality for CARv1 data payload represented as a mapping of +// CID to offset. This can then be used to implement random access over a CARv1. // // Index can be written or read using the following static functions: index.WriteTo and // index.ReadFrom. diff --git a/ipld/car/v2/index_gen.go b/ipld/car/v2/index_gen.go index 4fa9ac1df..5a60fb711 100644 --- a/ipld/car/v2/index_gen.go +++ b/ipld/car/v2/index_gen.go @@ -63,10 +63,10 @@ func GenerateIndex(v1r io.Reader, opts ...ReadOption) (index.Index, error) { // Null padding; by default it's an error. if sectionLen == 0 { - if o.ZeroLegthSectionAsEOF { + if o.ZeroLengthSectionAsEOF { break } else { - return nil, fmt.Errorf("carv1 null padding not allowed by default; see ZeroLegthSectionAsEOF") + return nil, fmt.Errorf("carv1 null padding not allowed by default; see ZeroLengthSectionAsEOF") } } @@ -103,10 +103,10 @@ func GenerateIndexFromFile(path string) (index.Index, error) { return GenerateIndex(f) } -// ReadOrGenerateIndex accepts both CAR v1 and v2 format, and reads or generates an index for it. -// When the given reader is in CAR v1 format an index is always generated. -// For a payload in CAR v2 format, an index is only generated if Header.HasIndex returns false. -// An error is returned for all other formats, i.e. versions other than 1 or 2. +// ReadOrGenerateIndex accepts both CARv1 and CARv2 formats, and reads or generates an index for it. +// When the given reader is in CARv1 format an index is always generated. +// For a payload in CARv2 format, an index is only generated if Header.HasIndex returns false. +// An error is returned for all other formats, i.e. pragma with versions other than 1 or 2. // // Note, the returned index lives entirely in memory and will not depend on the // given reader to fulfill index lookup. @@ -126,7 +126,7 @@ func ReadOrGenerateIndex(rs io.ReadSeeker) (index.Index, error) { // Simply generate the index, since there can't be a pre-existing one. return GenerateIndex(rs) case 2: - // Read CAR v2 format + // Read CARv2 format v2r, err := NewReader(internalio.ToReaderAt(rs)) if err != nil { return nil, err @@ -135,8 +135,8 @@ func ReadOrGenerateIndex(rs io.ReadSeeker) (index.Index, error) { if v2r.Header.HasIndex() { return index.ReadFrom(v2r.IndexReader()) } - // Otherwise, generate index from CAR v1 payload wrapped within CAR v2 format. - return GenerateIndex(v2r.CarV1Reader()) + // Otherwise, generate index from CARv1 payload wrapped within CARv2 format. + return GenerateIndex(v2r.DataReader()) default: return nil, fmt.Errorf("unknown version %v", version) } diff --git a/ipld/car/v2/internal/carv1/doc.go b/ipld/car/v2/internal/carv1/doc.go index a13ffdfc2..821ca2f0a 100644 --- a/ipld/car/v2/internal/carv1/doc.go +++ b/ipld/car/v2/internal/carv1/doc.go @@ -1,2 +1,2 @@ -// Forked from CAR v1 to avoid dependency to ipld-prime 0.9.0 due to outstanding upgrades in filecoin. +// Forked from CARv1 to avoid dependency to ipld-prime 0.9.0 due to outstanding upgrades in filecoin. package carv1 diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go index ad859d1a0..89044a761 100644 --- a/ipld/car/v2/options.go +++ b/ipld/car/v2/options.go @@ -6,7 +6,7 @@ package car // This type should not be used directly by end users; it's only exposed as a // side effect of ReadOption. type ReadOptions struct { - ZeroLegthSectionAsEOF bool + ZeroLengthSectionAsEOF bool BlockstoreUseWholeCIDs bool } @@ -24,7 +24,7 @@ var _ ReadWriteOption = ReadOption(nil) // This type should not be used directly by end users; it's only exposed as a // side effect of WriteOption. type WriteOptions struct { - CarV1Padding uint64 + DataPadding uint64 IndexPadding uint64 BlockstoreAllowDuplicatePuts bool @@ -42,19 +42,19 @@ type ReadWriteOption interface { readWriteOption() } -// ZeroLegthSectionAsEOF is a read option which allows a CARv1 decoder to treat +// ZeroLengthSectionAsEOF is a read option which allows a CARv1 decoder to treat // a zero-length section as the end of the input CAR file. For example, this can // be useful to allow "null padding" after a CARv1 without knowing where the // padding begins. -func ZeroLegthSectionAsEOF(o *ReadOptions) { - o.ZeroLegthSectionAsEOF = true +func ZeroLengthSectionAsEOF(o *ReadOptions) { + o.ZeroLengthSectionAsEOF = true } -// UseCarV1Padding is a write option which sets the padding to be added between -// CAR v2 header and its data payload on Finalize. -func UseCarV1Padding(p uint64) WriteOption { +// UseDataPadding is a write option which sets the padding to be added between +// CARv2 header and its data payload on Finalize. +func UseDataPadding(p uint64) WriteOption { return func(o *WriteOptions) { - o.CarV1Padding = p + o.DataPadding = p } } diff --git a/ipld/car/v2/reader.go b/ipld/car/v2/reader.go index f845a81ce..98d3715d3 100644 --- a/ipld/car/v2/reader.go +++ b/ipld/car/v2/reader.go @@ -11,12 +11,12 @@ import ( "golang.org/x/exp/mmap" ) -// Reader represents a reader of CAR v2. +// Reader represents a reader of CARv2. type Reader struct { - Header Header - r io.ReaderAt - roots []cid.Cid - carv2Closer io.Closer + Header Header + r io.ReaderAt + roots []cid.Cid + closer io.Closer } // OpenReader is a wrapper for NewReader which opens the file at path. @@ -31,13 +31,13 @@ func OpenReader(path string, opts ...ReadOption) (*Reader, error) { return nil, err } - r.carv2Closer = f + r.closer = f return r, nil } -// NewReader constructs a new reader that reads CAR v2 from the given r. +// NewReader constructs a new reader that reads CARv2 from the given r. // Upon instantiation, the reader inspects the payload by reading the pragma and will return -// an error if the pragma does not represent a CAR v2. +// an error if the pragma does not represent a CARv2. func NewReader(r io.ReaderAt, opts ...ReadOption) (*Reader, error) { cr := &Reader{ r: r, @@ -63,12 +63,13 @@ func (r *Reader) requireVersion2() (err error) { return } -// Roots returns the root CIDs of this CAR +// Roots returns the root CIDs. +// The root CIDs are extracted lazily from the data payload header. func (r *Reader) Roots() ([]cid.Cid, error) { if r.roots != nil { return r.roots, nil } - header, err := carv1.ReadHeader(r.CarV1Reader()) + header, err := carv1.ReadHeader(r.DataReader()) if err != nil { return nil, err } @@ -91,26 +92,26 @@ type SectionReader interface { io.ReaderAt } -// CarV1Reader provides a reader containing the CAR v1 section encapsulated in this CAR v2. -func (r *Reader) CarV1Reader() SectionReader { - return io.NewSectionReader(r.r, int64(r.Header.CarV1Offset), int64(r.Header.CarV1Size)) +// DataReader provides a reader containing the data payload in CARv1 format. +func (r *Reader) DataReader() SectionReader { + return io.NewSectionReader(r.r, int64(r.Header.DataOffset), int64(r.Header.DataSize)) } -// IndexReader provides an io.Reader containing the index of this CAR v2. +// IndexReader provides an io.Reader containing the index for the data payload. func (r *Reader) IndexReader() io.Reader { return internalio.NewOffsetReadSeeker(r.r, int64(r.Header.IndexOffset)) } // Close closes the underlying reader if it was opened by OpenReader. func (r *Reader) Close() error { - if r.carv2Closer != nil { - return r.carv2Closer.Close() + if r.closer != nil { + return r.closer.Close() } return nil } // ReadVersion reads the version from the pragma. -// This function accepts both CAR v1 and v2 payloads. +// This function accepts both CARv1 and CARv2 payloads. func ReadVersion(r io.Reader) (version uint64, err error) { // TODO if the user provides a reader that sufficiently satisfies what carv1.ReadHeader is asking then use that instead of wrapping every time. header, err := carv1.ReadHeader(r) diff --git a/ipld/car/v2/writer.go b/ipld/car/v2/writer.go index fa94913e9..40004648e 100644 --- a/ipld/car/v2/writer.go +++ b/ipld/car/v2/writer.go @@ -79,11 +79,11 @@ func WrapV1(src io.ReadSeeker, dst io.Writer) error { return nil } -// AttachIndex attaches a given index to an existing car v2 file at given path and offset. +// AttachIndex attaches a given index to an existing CARv2 file at given path and offset. func AttachIndex(path string, idx index.Index, offset uint64) error { // TODO: instead of offset, maybe take padding? - // TODO: check that the given path is indeed a CAR v2. - // TODO: update CAR v2 header according to the offset at which index is written out. + // TODO: check that the given path is indeed a CARv2. + // TODO: update CARv2 header according to the offset at which index is written out. out, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o640) if err != nil { return err diff --git a/ipld/car/v2/writer_test.go b/ipld/car/v2/writer_test.go index ff8f3bff2..3cf119cee 100644 --- a/ipld/car/v2/writer_test.go +++ b/ipld/car/v2/writer_test.go @@ -47,7 +47,7 @@ func TestWrapV1(t *testing.T) { require.NoError(t, err) wantPayload, err := ioutil.ReadAll(sf) require.NoError(t, err) - gotPayload, err := ioutil.ReadAll(subject.CarV1Reader()) + gotPayload, err := ioutil.ReadAll(subject.DataReader()) require.NoError(t, err) require.Equal(t, wantPayload, gotPayload) From ece6c8bd54956e00832599191bf8338986627125 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Fri, 16 Jul 2021 16:45:26 +0100 Subject: [PATCH 3495/3817] Replace a file if exists on `index.Save` If appendage is needed there is already `car.AttachIndex`. This commit was moved from ipld/go-car@64bb51b2aba026731017fbb8fb62eaf79bce8ac3 --- ipld/car/v2/index/index.go | 4 ++-- ipld/car/v2/testdata/sample-index.carindex | Bin 209505 -> 41901 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 96b730f16..669728bba 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -52,9 +52,9 @@ func New(codec multicodec.Code) (Index, error) { } } -// Save writes a generated index into the given `path`. +// Save writes a generated index into the given `path` replacing the file if it exists. func Save(idx Index, path string) error { - stream, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o640) + stream, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o640) if err != nil { return err } diff --git a/ipld/car/v2/testdata/sample-index.carindex b/ipld/car/v2/testdata/sample-index.carindex index 0f91e36507fc21d3a93a00f32bf330e024405af8..5df07d379e444d8fa3a0f5b96a59874f208bd0c1 100644 GIT binary patch delta 9 RcmaF(glFw>rVVQs0{|Or1xNq@ delta 21 ScmZ2`oax~co(*dkBO?Hf?+r)* From fc756e15fe8f9450ca62edcf99d6bb01369d8688 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Fri, 16 Jul 2021 17:09:49 +0100 Subject: [PATCH 3496/3817] Remove `index.Save` API It's two lines of code extra and we already have read from and write to APIs for index. Remove `hydrate` cli since the assumptions it makes about appending index are no longer true. header needs updating for example. Removing to be re-added when needed as part of a propper CARv2 CLI. This commit was moved from ipld/go-car@e45d3b24a640861b95d35069e8a784250c5ca839 --- ipld/car/v2/example_test.go | 7 ++++- ipld/car/v2/index/example_test.go | 27 ++++++++++++++----- ipld/car/v2/index/index.go | 11 -------- ipld/car/v2/index/index_test.go | 27 ------------------- ipld/car/v2/internal/carbs/doc.go | 3 --- ipld/car/v2/internal/carbs/util/hydrate.go | 30 ---------------------- 6 files changed, 26 insertions(+), 79 deletions(-) delete mode 100644 ipld/car/v2/internal/carbs/doc.go delete mode 100644 ipld/car/v2/internal/carbs/util/hydrate.go diff --git a/ipld/car/v2/example_test.go b/ipld/car/v2/example_test.go index 6944f2fa7..dd3e002b2 100644 --- a/ipld/car/v2/example_test.go +++ b/ipld/car/v2/example_test.go @@ -25,7 +25,12 @@ func ExampleWrapV1File() { if err != nil { panic(err) } - defer cr.Close() + defer func() { + if err := cr.Close(); err != nil { + panic(err) + } + }() + roots, err := cr.Roots() if err != nil { panic(err) diff --git a/ipld/car/v2/index/example_test.go b/ipld/car/v2/index/example_test.go index cf6d56a63..9f8368d1a 100644 --- a/ipld/car/v2/index/example_test.go +++ b/ipld/car/v2/index/example_test.go @@ -2,6 +2,7 @@ package index_test import ( "fmt" + "io" "os" "reflect" @@ -44,16 +45,20 @@ func ExampleReadFrom() { // Frame with CID bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy starts at offset 61 relative to CARv1 data payload. } -// ExampleSave unmarshalls an index from an indexed CARv2 file, and stores it as a separate +// ExampleWriteTo unmarshalls an index from an indexed CARv2 file, and stores it as a separate // file on disk. -func ExampleSave() { +func ExampleWriteTo() { // Open the CARv2 file src := "../testdata/sample-wrapped-v2.car" cr, err := carv2.OpenReader(src) if err != nil { panic(err) } - defer cr.Close() + defer func() { + if err := cr.Close(); err != nil { + panic(err) + } + }() // Read and unmarshall index within CARv2 file. idx, err := index.ReadFrom(cr.IndexReader()) @@ -63,17 +68,25 @@ func ExampleSave() { // Store the index alone onto destination file. dest := "../testdata/sample-index.carindex" - err = index.Save(idx, dest) + f, err := os.Create(dest) + if err != nil { + panic(err) + } + defer func() { + if err := f.Close(); err != nil { + panic(err) + } + }() + err = index.WriteTo(idx, f) if err != nil { panic(err) } - // Open the destination file that contains the index only. - f, err := os.Open(dest) + // Seek to the beginning of tile to read it back. + _, err = f.Seek(0, io.SeekStart) if err != nil { panic(err) } - defer f.Close() // Read and unmarshall the destination file as a separate index instance. reReadIdx, err := index.ReadFrom(f) diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 669728bba..119821714 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -4,7 +4,6 @@ import ( "encoding/binary" "fmt" "io" - "os" internalio "github.com/ipld/go-car/v2/internal/io" @@ -52,16 +51,6 @@ func New(codec multicodec.Code) (Index, error) { } } -// Save writes a generated index into the given `path` replacing the file if it exists. -func Save(idx Index, path string) error { - stream, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o640) - if err != nil { - return err - } - defer stream.Close() - return WriteTo(idx, stream) -} - // WriteTo writes the given idx into w. // The written bytes include the index encoding. // This can then be read back using index.ReadFrom diff --git a/ipld/car/v2/index/index_test.go b/ipld/car/v2/index/index_test.go index 945ca1bbc..a32205dca 100644 --- a/ipld/car/v2/index/index_test.go +++ b/ipld/car/v2/index/index_test.go @@ -128,33 +128,6 @@ func TestWriteTo(t *testing.T) { require.Equal(t, wantIdx, gotIdx) } -func TestSave(t *testing.T) { - // Read sample index on file - idxf, err := os.Open("../testdata/sample-index.carindex") - require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, idxf.Close()) }) - - // Unmarshall to get expected index - wantIdx, err := ReadFrom(idxf) - require.NoError(t, err) - - // Save the same index at destination - dest := filepath.Join(t.TempDir(), "index-write-to-test.carindex") - require.NoError(t, Save(wantIdx, dest)) - - // Open the saved file - destF, err := os.Open(dest) - require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, destF.Close()) }) - - // Read the written index back - gotIdx, err := ReadFrom(destF) - require.NoError(t, err) - - // Assert they are equal - require.Equal(t, wantIdx, gotIdx) -} - func TestMarshalledIndexStartsWithCodec(t *testing.T) { // Read sample index on file idxf, err := os.Open("../testdata/sample-index.carindex") diff --git a/ipld/car/v2/internal/carbs/doc.go b/ipld/car/v2/internal/carbs/doc.go deleted file mode 100644 index 099875379..000000000 --- a/ipld/car/v2/internal/carbs/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package carbs provides a read-only blockstore interface directly reading out of a car file. -// TODO to be refactored -package carbs diff --git a/ipld/car/v2/internal/carbs/util/hydrate.go b/ipld/car/v2/internal/carbs/util/hydrate.go deleted file mode 100644 index 96bb4c953..000000000 --- a/ipld/car/v2/internal/carbs/util/hydrate.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "fmt" - "os" - - carv2 "github.com/ipld/go-car/v2" - - "github.com/ipld/go-car/v2/index" -) - -func main() { - if len(os.Args) < 2 { - fmt.Printf("Usage: hydrate \n") - return - } - db := os.Args[1] - - idx, err := carv2.GenerateIndexFromFile(db) - if err != nil { - fmt.Printf("Error generating index: %v\n", err) - return - } - - fmt.Printf("Saving...\n") - - if err := index.Save(idx, db); err != nil { - fmt.Printf("Error saving : %v\n", err) - } -} From 9780e1f0f324f738c1a9a9c5274da59c83d4b1f4 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Fri, 16 Jul 2021 16:18:31 +0100 Subject: [PATCH 3497/3817] Add example blockstore examples Implement examples that show how to open a read-only blockstore, and a read-write blockstore along with resumption from the same file. The example surfaced a race condition where if the `AllKeysChan` is partially consumed and file is closed then the gorutie started by chan population causes the race condition. Locking on Close will make the closure hang. Better solution is needed but choices are limited due to `AllKeysChan` signature. Fixes: - https://github.com/ipld/go-car/issues/124 This commit was moved from ipld/go-car@ae4ddd418cce2f49d44a4dcb1ab4fbb989db7bba --- ipld/car/v2/blockstore/example_test.go | 148 +++++++++++++++++++++++ ipld/car/v2/testdata/sample-rw-bs-v2.car | Bin 0 -> 1875 bytes 2 files changed, 148 insertions(+) create mode 100644 ipld/car/v2/blockstore/example_test.go create mode 100644 ipld/car/v2/testdata/sample-rw-bs-v2.car diff --git a/ipld/car/v2/blockstore/example_test.go b/ipld/car/v2/blockstore/example_test.go new file mode 100644 index 000000000..72540e5fb --- /dev/null +++ b/ipld/car/v2/blockstore/example_test.go @@ -0,0 +1,148 @@ +package blockstore_test + +import ( + "context" + "fmt" + + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-merkledag" + carv2 "github.com/ipld/go-car/v2" + "github.com/ipld/go-car/v2/blockstore" +) + +const cidPrintCount = 5 + +// ExampleOpenReadOnly opens a read-only blockstore from a CARv1 file, and prints its root CIDs +// along with CID mapping to raw data size of blocks for first five sections in the CAR file. +func ExampleOpenReadOnly() { + // Open a new ReadOnly blockstore from a CARv1 file. + // Note, `OpenReadOnly` accepts bot CARv1 and CARv2 formats and transparently generate index + // in the background if necessary. + // This instance sets ZeroLengthSectionAsEOF option to treat zero sized sections in file as EOF. + robs, err := blockstore.OpenReadOnly("../testdata/sample-v1.car", carv2.ZeroLengthSectionAsEOF) + if err != nil { + panic(err) + } + defer robs.Close() + + // Print root CIDs. + roots, err := robs.Roots() + if err != nil { + panic(err) + } + fmt.Printf("Contains %v root CID(s):\n", len(roots)) + for _, r := range roots { + fmt.Printf("\t%v\n", r) + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Print the raw data size for the first 5 CIDs in the CAR file. + keysChan, err := robs.AllKeysChan(ctx) + if err != nil { + panic(err) + } + fmt.Printf("List of first %v CIDs and their raw data size:\n", cidPrintCount) + i := 1 + for k := range keysChan { + if i > cidPrintCount { + cancel() + break + } + size, err := robs.GetSize(k) + if err != nil { + panic(err) + } + fmt.Printf("\t%v -> %v bytes\n", k, size) + i++ + } + + // Output: + // Contains 1 root CID(s): + // bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy + // List of first 5 CIDs and their raw data size: + // bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy -> 821 bytes + // bafy2bzaceaycv7jhaegckatnncu5yugzkrnzeqsppzegufr35lroxxnsnpspu -> 1053 bytes + // bafy2bzaceb62wdepofqu34afqhbcn4a7jziwblt2ih5hhqqm6zitd3qpzhdp4 -> 1094 bytes + // bafy2bzaceb3utcspm5jqcdqpih3ztbaztv7yunzkiyfq7up7xmokpxemwgu5u -> 1051 bytes + // bafy2bzacedjwekyjresrwjqj4n2r5bnuuu3klncgjo2r3slsp6wgqb37sz4ck -> 821 bytes +} + +// ExampleOpenReadWrite creates a read-write blockstore and puts +func ExampleOpenReadWrite() { + thisBlock := merkledag.NewRawNode([]byte("fish")).Block + thatBlock := merkledag.NewRawNode([]byte("lobster")).Block + andTheOtherBlock := merkledag.NewRawNode([]byte("barreleye")).Block + + dest := "../testdata/sample-rw-bs-v2.car" + roots := []cid.Cid{thisBlock.Cid(), thatBlock.Cid(), andTheOtherBlock.Cid()} + + rwbs, err := blockstore.OpenReadWrite(dest, roots, carv2.UseDataPadding(1413), carv2.UseIndexPadding(42)) + if err != nil { + panic(err) + } + defer rwbs.Close() + + // Put all blocks onto the blockstore. + blocks := []blocks.Block{thisBlock, thatBlock} + if err := rwbs.PutMany(blocks); err != nil { + panic(err) + } + fmt.Printf("Successfully wrote %v blocks into the blockstore.\n", len(blocks)) + + // Any blocks put can be read back using the same blockstore instance. + block, err := rwbs.Get(thatBlock.Cid()) + if err != nil { + panic(err) + } + fmt.Printf("Read back block just put with raw value of `%v`.\n", string(block.RawData())) + + // Finalize the blockstore to flush out the index and make a complete CARv2. + if err := rwbs.Finalize(); err != nil { + panic(err) + } + + // Resume from the same file to add more blocks. + // Note the UseDataPadding and roots must match the values passed to the blockstore instance + // that created the original file. Otherwise, we cannot resume from the same file. + resumedRwbos, err := blockstore.OpenReadWrite(dest, roots, carv2.UseDataPadding(1413)) + if err != nil { + panic(err) + } + defer resumedRwbos.Close() + + // Put another block, appending it to the set of blocks that are written previously. + if err := resumedRwbos.Put(andTheOtherBlock); err != nil { + panic(err) + } + + // Read back the the block put before resumption. + // Blocks previously put are present. + block, err = resumedRwbos.Get(thatBlock.Cid()) + if err != nil { + panic(err) + } + fmt.Printf("Resumed blockstore contains blocks put previously with raw value of `%v`.\n", string(block.RawData())) + + // Put an additional block to the CAR. + // Blocks put after resumption are also present. + block, err = resumedRwbos.Get(andTheOtherBlock.Cid()) + if err != nil { + panic(err) + } + fmt.Printf("It also contains the block put after resumption with raw value of `%v`.\n", string(block.RawData())) + + // Finalize the blockstore to flush out the index and make a complete CARv2. + // Note, Finalize must be called on an open ReadWrite blockstore to flush out a complete CARv2. + if err := resumedRwbos.Finalize(); err != nil { + panic(err) + } + + // Output: + // Successfully wrote 2 blocks into the blockstore. + // Read back block just put with raw value of `lobster`. + // Resumed blockstore contains blocks put previously with raw value of `lobster`. + // It also contains the block put after resumption with raw value of `barreleye`. +} diff --git a/ipld/car/v2/testdata/sample-rw-bs-v2.car b/ipld/car/v2/testdata/sample-rw-bs-v2.car new file mode 100644 index 0000000000000000000000000000000000000000..9f7b56df358e0e3a0eb3734743b13cb3d88a12f4 GIT binary patch literal 1875 zcmd;Dm|m7zRGgWg$HagJcCbPO1Q{XpNj5YEqukLD7!85Z5Eu;s`iDRd%Su5i)ABfJaO z8OQqmf3Er3H)fG_$ Date: Fri, 16 Jul 2021 17:44:54 +0100 Subject: [PATCH 3498/3817] make Close safe, not letting Finalize block forever either This commit was moved from ipld/go-car@c06b4f2ff65cc894d088d7d8ee55063834f12bcc --- ipld/car/v2/blockstore/readonly.go | 11 +++++++++++ ipld/car/v2/blockstore/readwrite.go | 7 ++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index bbbba8f4e..34672cad7 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -330,7 +330,18 @@ func (b *ReadOnly) Roots() ([]cid.Cid, error) { } // Close closes the underlying reader if it was opened by OpenReadOnly. +// +// Note that this call may block if any blockstore operations are currently in +// progress, including an AllKeysChan that hasn't been fully consumed or +// cancelled. func (b *ReadOnly) Close() error { + b.mu.Lock() + defer b.mu.Unlock() + + return b.closeWithoutMutex() +} + +func (b *ReadOnly) closeWithoutMutex() error { if b.carv2Closer != nil { return b.carv2Closer.Close() } diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 693e52f91..665a65384 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -343,7 +343,12 @@ func (b *ReadWrite) Finalize() error { defer b.mu.Unlock() // TODO check if add index option is set and don't write the index then set index offset to zero. b.header = b.header.WithDataSize(uint64(b.dataWriter.Position())) - defer b.Close() + + // Note that we can't use b.Close here, as that tries to grab the same + // mutex we're holding here. + // TODO: should we check the error here? especially with OpenReadWrite, + // we should care about close errors. + defer b.closeWithoutMutex() // TODO if index not needed don't bother flattening it. fi, err := b.idx.flatten() From 1ccc7972721df2e65e0858fc54a0ebf0fe70d4f5 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 20 Jul 2021 13:12:50 +1000 Subject: [PATCH 3499/3817] Fix staticcheck warnings This file has evolved beyond its codegen status and has been manually edited a few times already. It's unlikely to change and is slowly being deprecated in favour of https://github.com/ipld/go-codec-dagpb Ref: https://github.com/ipfs/go-merkledag/pull/69 This commit was moved from ipfs/go-merkledag@40f50349468726875e33945f8eb1631b4494f25d --- ipld/merkledag/pb/merkledag.pb.go | 175 +++++++++++++++--------------- 1 file changed, 87 insertions(+), 88 deletions(-) diff --git a/ipld/merkledag/pb/merkledag.pb.go b/ipld/merkledag/pb/merkledag.pb.go index c5d2c7caf..45806ce92 100644 --- a/ipld/merkledag/pb/merkledag.pb.go +++ b/ipld/merkledag/pb/merkledag.pb.go @@ -1,18 +1,19 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: merkledag.proto +// Code originally generated by protoc-gen-gogo from merkledag.proto, +// now manually managed package merkledag_pb import ( bytes "bytes" fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" io "io" math "math" math_bits "math/bits" reflect "reflect" strings "strings" + + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" ) // DoNotUpgradeFileEverItWillChangeYourHashes warns users about not breaking @@ -184,9 +185,9 @@ var fileDescriptor_10837cc3557cec00 = []byte{ 0x01, 0x00, 0x00, } -func (this *PBLink) VerboseEqual(that interface{}) error { +func (pbLink *PBLink) VerboseEqual(that interface{}) error { if that == nil { - if this == nil { + if pbLink == nil { return nil } return fmt.Errorf("that == nil && this != nil") @@ -202,42 +203,42 @@ func (this *PBLink) VerboseEqual(that interface{}) error { } } if that1 == nil { - if this == nil { + if pbLink == nil { return nil } return fmt.Errorf("that is type *PBLink but is nil && this != nil") - } else if this == nil { + } else if pbLink == nil { return fmt.Errorf("that is type *PBLink but is not nil && this == nil") } - if !bytes.Equal(this.Hash, that1.Hash) { - return fmt.Errorf("Hash this(%v) Not Equal that(%v)", this.Hash, that1.Hash) + if !bytes.Equal(pbLink.Hash, that1.Hash) { + return fmt.Errorf("this.Hash(%v) is not equal to that.Hash(%v)", pbLink.Hash, that1.Hash) } - if this.Name != nil && that1.Name != nil { - if *this.Name != *that1.Name { - return fmt.Errorf("Name this(%v) Not Equal that(%v)", *this.Name, *that1.Name) + if pbLink.Name != nil && that1.Name != nil { + if *pbLink.Name != *that1.Name { + return fmt.Errorf("this.Name(%v) is not equal to that.Name(%v)", *pbLink.Name, *that1.Name) } - } else if this.Name != nil { + } else if pbLink.Name != nil { return fmt.Errorf("this.Name == nil && that.Name != nil") } else if that1.Name != nil { - return fmt.Errorf("Name this(%v) Not Equal that(%v)", this.Name, that1.Name) + return fmt.Errorf("this.Name(%v) is not equal to that.Name(%v)", pbLink.Name, that1.Name) } - if this.Tsize != nil && that1.Tsize != nil { - if *this.Tsize != *that1.Tsize { - return fmt.Errorf("Tsize this(%v) Not Equal that(%v)", *this.Tsize, *that1.Tsize) + if pbLink.Tsize != nil && that1.Tsize != nil { + if *pbLink.Tsize != *that1.Tsize { + return fmt.Errorf("this.Tsize(%v) is not equal to that.Tsize(%v)", *pbLink.Tsize, *that1.Tsize) } - } else if this.Tsize != nil { + } else if pbLink.Tsize != nil { return fmt.Errorf("this.Tsize == nil && that.Tsize != nil") } else if that1.Tsize != nil { - return fmt.Errorf("Tsize this(%v) Not Equal that(%v)", this.Tsize, that1.Tsize) + return fmt.Errorf("this.Tsize(%v) is not equal to that.Tsize(%v)", pbLink.Tsize, that1.Tsize) } - if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { - return fmt.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) + if !bytes.Equal(pbLink.XXX_unrecognized, that1.XXX_unrecognized) { + return fmt.Errorf("XXX_unrecognized this(%v) is not equal to that(%v)", pbLink.XXX_unrecognized, that1.XXX_unrecognized) } return nil } -func (this *PBLink) Equal(that interface{}) bool { +func (pbLink *PBLink) Equal(that interface{}) bool { if that == nil { - return this == nil + return pbLink == nil } that1, ok := that.(*PBLink) @@ -250,39 +251,39 @@ func (this *PBLink) Equal(that interface{}) bool { } } if that1 == nil { - return this == nil - } else if this == nil { + return pbLink == nil + } else if pbLink == nil { return false } - if !bytes.Equal(this.Hash, that1.Hash) { + if !bytes.Equal(pbLink.Hash, that1.Hash) { return false } - if this.Name != nil && that1.Name != nil { - if *this.Name != *that1.Name { + if pbLink.Name != nil && that1.Name != nil { + if *pbLink.Name != *that1.Name { return false } - } else if this.Name != nil { + } else if pbLink.Name != nil { return false } else if that1.Name != nil { return false } - if this.Tsize != nil && that1.Tsize != nil { - if *this.Tsize != *that1.Tsize { + if pbLink.Tsize != nil && that1.Tsize != nil { + if *pbLink.Tsize != *that1.Tsize { return false } - } else if this.Tsize != nil { + } else if pbLink.Tsize != nil { return false } else if that1.Tsize != nil { return false } - if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + if !bytes.Equal(pbLink.XXX_unrecognized, that1.XXX_unrecognized) { return false } return true } -func (this *PBNode) VerboseEqual(that interface{}) error { +func (pbLink *PBNode) VerboseEqual(that interface{}) error { if that == nil { - if this == nil { + if pbLink == nil { return nil } return fmt.Errorf("that == nil && this != nil") @@ -298,32 +299,33 @@ func (this *PBNode) VerboseEqual(that interface{}) error { } } if that1 == nil { - if this == nil { + if pbLink == nil { return nil } return fmt.Errorf("that is type *PBNode but is nil && this != nil") - } else if this == nil { + } else if pbLink == nil { return fmt.Errorf("that is type *PBNode but is not nil && this == nil") } - if len(this.Links) != len(that1.Links) { - return fmt.Errorf("Links this(%v) Not Equal that(%v)", len(this.Links), len(that1.Links)) + if len(pbLink.Links) != len(that1.Links) { + return fmt.Errorf("len(this.Links)(%v) is not equal to len(that.Links)(%v)", len(pbLink.Links), len(that1.Links)) } - for i := range this.Links { - if !this.Links[i].Equal(that1.Links[i]) { - return fmt.Errorf("Links this[%v](%v) Not Equal that[%v](%v)", i, this.Links[i], i, that1.Links[i]) + for i := range pbLink.Links { + if !pbLink.Links[i].Equal(that1.Links[i]) { + return fmt.Errorf("this.Links[%v](%v) is not equal to that.Links[%v](%v)", i, pbLink.Links[i], i, that1.Links[i]) } } - if !bytes.Equal(this.Data, that1.Data) { - return fmt.Errorf("Data this(%v) Not Equal that(%v)", this.Data, that1.Data) + if !bytes.Equal(pbLink.Data, that1.Data) { + return fmt.Errorf("this.Data(%v) is not equal to that.Data(%v)", pbLink.Data, that1.Data) } - if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { - return fmt.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized) + if !bytes.Equal(pbLink.XXX_unrecognized, that1.XXX_unrecognized) { + return fmt.Errorf("this.XXX_unrecognized(%v) is not equal to that.XXX_unrecognized(%v)", pbLink.XXX_unrecognized, that1.XXX_unrecognized) } return nil } -func (this *PBNode) Equal(that interface{}) bool { + +func (pbNode *PBNode) Equal(that interface{}) bool { if that == nil { - return this == nil + return pbNode == nil } that1, ok := that.(*PBNode) @@ -336,61 +338,61 @@ func (this *PBNode) Equal(that interface{}) bool { } } if that1 == nil { - return this == nil - } else if this == nil { + return pbNode == nil + } else if pbNode == nil { return false } - if len(this.Links) != len(that1.Links) { + if len(pbNode.Links) != len(that1.Links) { return false } - for i := range this.Links { - if !this.Links[i].Equal(that1.Links[i]) { + for i := range pbNode.Links { + if !pbNode.Links[i].Equal(that1.Links[i]) { return false } } - if !bytes.Equal(this.Data, that1.Data) { + if !bytes.Equal(pbNode.Data, that1.Data) { return false } - if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + if !bytes.Equal(pbNode.XXX_unrecognized, that1.XXX_unrecognized) { return false } return true } -func (this *PBLink) GoString() string { - if this == nil { +func (pbLink *PBLink) GoString() string { + if pbLink == nil { return "nil" } s := make([]string, 0, 7) s = append(s, "&merkledag_pb.PBLink{") - if this.Hash != nil { - s = append(s, "Hash: "+valueToGoStringMerkledag(this.Hash, "byte")+",\n") + if pbLink.Hash != nil { + s = append(s, "Hash: "+valueToGoStringMerkledag(pbLink.Hash, "byte")+",\n") } - if this.Name != nil { - s = append(s, "Name: "+valueToGoStringMerkledag(this.Name, "string")+",\n") + if pbLink.Name != nil { + s = append(s, "Name: "+valueToGoStringMerkledag(pbLink.Name, "string")+",\n") } - if this.Tsize != nil { - s = append(s, "Tsize: "+valueToGoStringMerkledag(this.Tsize, "uint64")+",\n") + if pbLink.Tsize != nil { + s = append(s, "Tsize: "+valueToGoStringMerkledag(pbLink.Tsize, "uint64")+",\n") } - if this.XXX_unrecognized != nil { - s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + if pbLink.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", pbLink.XXX_unrecognized)+",\n") } s = append(s, "}") return strings.Join(s, "") } -func (this *PBNode) GoString() string { - if this == nil { +func (pbNode *PBNode) GoString() string { + if pbNode == nil { return "nil" } s := make([]string, 0, 6) s = append(s, "&merkledag_pb.PBNode{") - if this.Links != nil { - s = append(s, "Links: "+fmt.Sprintf("%#v", this.Links)+",\n") + if pbNode.Links != nil { + s = append(s, "Links: "+fmt.Sprintf("%#v", pbNode.Links)+",\n") } - if this.Data != nil { - s = append(s, "Data: "+valueToGoStringMerkledag(this.Data, "byte")+",\n") + if pbNode.Data != nil { + s = append(s, "Data: "+valueToGoStringMerkledag(pbNode.Data, "byte")+",\n") } - if this.XXX_unrecognized != nil { - s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + if pbNode.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", pbNode.XXX_unrecognized)+",\n") } s = append(s, "}") return strings.Join(s, "") @@ -673,35 +675,32 @@ func (m *PBNode) Size() (n int) { func sovMerkledag(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } -func sozMerkledag(x uint64) (n int) { - return sovMerkledag(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (this *PBLink) String() string { - if this == nil { +func (pbLink *PBLink) String() string { + if pbLink == nil { return "nil" } s := strings.Join([]string{`&PBLink{`, - `Hash:` + valueToStringMerkledag(this.Hash) + `,`, - `Name:` + valueToStringMerkledag(this.Name) + `,`, - `Tsize:` + valueToStringMerkledag(this.Tsize) + `,`, - `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `Hash:` + valueToStringMerkledag(pbLink.Hash) + `,`, + `Name:` + valueToStringMerkledag(pbLink.Name) + `,`, + `Tsize:` + valueToStringMerkledag(pbLink.Tsize) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", pbLink.XXX_unrecognized) + `,`, `}`, }, "") return s } -func (this *PBNode) String() string { - if this == nil { +func (pbNode *PBNode) String() string { + if pbNode == nil { return "nil" } repeatedStringForLinks := "[]*PBLink{" - for _, f := range this.Links { + for _, f := range pbNode.Links { repeatedStringForLinks += strings.Replace(f.String(), "PBLink", "PBLink", 1) + "," } repeatedStringForLinks += "}" s := strings.Join([]string{`&PBNode{`, - `Data:` + valueToStringMerkledag(this.Data) + `,`, + `Data:` + valueToStringMerkledag(pbNode.Data) + `,`, `Links:` + repeatedStringForLinks + `,`, - `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", pbNode.XXX_unrecognized) + `,`, `}`, }, "") return s From 5f4138f1bcfe1f3d32c626b4116a5f146fb5003f Mon Sep 17 00:00:00 2001 From: web3-bot Date: Tue, 1 Jun 2021 16:54:06 +0000 Subject: [PATCH 3500/3817] run gofmt -s This commit was moved from ipfs/go-merkledag@9fd869bbf68c51259efae29e2c3fbb07253fe3b8 --- ipld/merkledag/dagutils/diffenum_test.go | 36 ++++++++++++------------ ipld/merkledag/merkledag_test.go | 10 +++---- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/ipld/merkledag/dagutils/diffenum_test.go b/ipld/merkledag/dagutils/diffenum_test.go index c4181073f..7400d2d31 100644 --- a/ipld/merkledag/dagutils/diffenum_test.go +++ b/ipld/merkledag/dagutils/diffenum_test.go @@ -46,61 +46,61 @@ func mkGraph(desc map[string]ndesc) map[string]ipld.Node { } var tg1 = map[string]ndesc{ - "a1": ndesc{ + "a1": { "foo": "b", }, - "b": ndesc{}, - "a2": ndesc{ + "b": {}, + "a2": { "foo": "b", "bar": "c", }, - "c": ndesc{}, + "c": {}, } var tg2 = map[string]ndesc{ - "a1": ndesc{ + "a1": { "foo": "b", }, - "b": ndesc{}, - "a2": ndesc{ + "b": {}, + "a2": { "foo": "b", "bar": "c", }, - "c": ndesc{"baz": "d"}, - "d": ndesc{}, + "c": {"baz": "d"}, + "d": {}, } var tg3 = map[string]ndesc{ - "a1": ndesc{ + "a1": { "foo": "b", "bar": "c", }, - "b": ndesc{}, - "a2": ndesc{ + "b": {}, + "a2": { "foo": "b", "bar": "d", }, - "c": ndesc{}, - "d": ndesc{}, + "c": {}, + "d": {}, } var tg4 = map[string]ndesc{ - "a1": ndesc{ + "a1": { "key1": "b", "key2": "c", }, - "a2": ndesc{ + "a2": { "key1": "b", "key2": "d", }, } var tg5 = map[string]ndesc{ - "a1": ndesc{ + "a1": { "key1": "a", "key2": "b", }, - "a2": ndesc{ + "a2": { "key1": "c", "key2": "d", }, diff --git a/ipld/merkledag/merkledag_test.go b/ipld/merkledag/merkledag_test.go index 3ff6c3f09..ec4b1f163 100644 --- a/ipld/merkledag/merkledag_test.go +++ b/ipld/merkledag/merkledag_test.go @@ -350,11 +350,11 @@ func TestFetchGraphWithDepthLimit(t *testing.T) { } tests := []testcase{ - testcase{1, 4}, - testcase{0, 1}, - testcase{-1, 6}, - testcase{2, 6}, - testcase{3, 6}, + {1, 4}, + {0, 1}, + {-1, 6}, + {2, 6}, + {3, 6}, } testF := func(t *testing.T, tc testcase) { From d229bf7b9a2805bbbbd30b04e06491820fa0d508 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 19 Jul 2021 20:48:52 -0700 Subject: [PATCH 3501/3817] fix: make MergeDiffs deterministic This commit was moved from ipfs/go-merkledag@5729b259f6618d7a7cddc9aec80aea84493a7617 --- ipld/merkledag/dagutils/diff.go | 18 +++++++++++------- ipld/merkledag/dagutils/diff_test.go | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/ipld/merkledag/dagutils/diff.go b/ipld/merkledag/dagutils/diff.go index 9fef3f964..6c80f0156 100644 --- a/ipld/merkledag/dagutils/diff.go +++ b/ipld/merkledag/dagutils/diff.go @@ -176,24 +176,28 @@ type Conflict struct { // Changes involved (which share the same path). func MergeDiffs(a, b []*Change) ([]*Change, []Conflict) { paths := make(map[string]*Change) - for _, c := range a { + for _, c := range b { paths[c.Path] = c } var changes []*Change var conflicts []Conflict - for _, changeB := range b { - if changeA, ok := paths[changeB.Path]; ok { + // NOTE: we avoid iterating over maps here to ensure iteration order is determistic. We + // include changes from a first, then b. + for _, changeA := range a { + if changeB, ok := paths[changeA.Path]; ok { conflicts = append(conflicts, Conflict{changeA, changeB}) } else { - changes = append(changes, changeB) + changes = append(changes, changeA) } - delete(paths, changeB.Path) + delete(paths, changeA.Path) } - for _, c := range paths { - changes = append(changes, c) + for _, c := range b { + if _, ok := paths[c.Path]; ok { + changes = append(changes, c) + } } return changes, conflicts diff --git a/ipld/merkledag/dagutils/diff_test.go b/ipld/merkledag/dagutils/diff_test.go index 9cafe13bc..7b90bd93a 100644 --- a/ipld/merkledag/dagutils/diff_test.go +++ b/ipld/merkledag/dagutils/diff_test.go @@ -33,9 +33,9 @@ func TestMergeDiffs(t *testing.T) { } expect := []*Change{ - changesB[1], changesA[0], changesA[2], + changesB[1], } for i, change := range changes { From aa123ddcdf87e06f114c37f50f25c5218c23f2fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Mon, 19 Jul 2021 18:11:09 +0100 Subject: [PATCH 3502/3817] blockstore: implement UseWholeCIDs Update the tests too, as a few expected whole CIDs. For now, just make them use the option. This required index.Index to gain a new method, GetAll, so that when using whole CIDs, methods like Get can return true if any of the matching indexed CIDs are an exact whole-CID match, and not just looking at the first matching indexed CID. GetAll is akin to an iteration over all matching indexed CIDs, and its callback returns a boolean to say if the iteration should continue. This allows stopping as soon as we're done. We also remove Index.Get, instead replacing it with a helper called GetFirst, which simply makes simple uses of GetAll a single line. We remove the non-specced and unused index implementations, too. They were left in place in case they were useful again, but they haven't been so far, and their code is still in git. Keeping them around just means updating more code when refactoring. While at it, make ZeroLengthSectionAsEOF take a boolean and return an option, just like the other boolean options, for consistency. Fixes #130. This commit was moved from ipld/go-car@c2e497e22825ba8d5a6329bbf9f06be08197e316 --- ipld/car/v2/blockstore/example_test.go | 5 +- ipld/car/v2/blockstore/insertionindex.go | 32 +++++- ipld/car/v2/blockstore/readonly.go | 127 ++++++++++++++++------ ipld/car/v2/blockstore/readonly_test.go | 4 +- ipld/car/v2/blockstore/readwrite.go | 1 - ipld/car/v2/blockstore/readwrite_test.go | 133 ++++++++++++++--------- ipld/car/v2/index/example_test.go | 2 +- ipld/car/v2/index/index.go | 50 +++++++-- ipld/car/v2/index/index_test.go | 17 +-- ipld/car/v2/index/indexgobhash.go | 48 -------- ipld/car/v2/index/indexhashed.go | 47 -------- ipld/car/v2/index/indexsorted.go | 45 ++++---- ipld/car/v2/index/indexsorted_test.go | 6 +- ipld/car/v2/index_gen.go | 2 +- ipld/car/v2/options.go | 6 +- 15 files changed, 281 insertions(+), 244 deletions(-) delete mode 100644 ipld/car/v2/index/indexgobhash.go delete mode 100644 ipld/car/v2/index/indexhashed.go diff --git a/ipld/car/v2/blockstore/example_test.go b/ipld/car/v2/blockstore/example_test.go index 72540e5fb..00a81dc7f 100644 --- a/ipld/car/v2/blockstore/example_test.go +++ b/ipld/car/v2/blockstore/example_test.go @@ -20,7 +20,10 @@ func ExampleOpenReadOnly() { // Note, `OpenReadOnly` accepts bot CARv1 and CARv2 formats and transparently generate index // in the background if necessary. // This instance sets ZeroLengthSectionAsEOF option to treat zero sized sections in file as EOF. - robs, err := blockstore.OpenReadOnly("../testdata/sample-v1.car", carv2.ZeroLengthSectionAsEOF) + robs, err := blockstore.OpenReadOnly("../testdata/sample-v1.car", + blockstore.UseWholeCIDs(true), + carv2.ZeroLengthSectionAsEOF(true), + ) if err != nil { panic(err) } diff --git a/ipld/car/v2/blockstore/insertionindex.go b/ipld/car/v2/blockstore/insertionindex.go index 8e664eac9..00d414656 100644 --- a/ipld/car/v2/blockstore/insertionindex.go +++ b/ipld/car/v2/blockstore/insertionindex.go @@ -55,7 +55,7 @@ func newRecordFromCid(c cid.Cid, at uint64) recordDigest { panic(err) } - return recordDigest{d.Digest, index.Record{Cid: c, Idx: at}} + return recordDigest{d.Digest, index.Record{Cid: c, Offset: at}} } func (ii *insertionIndex) insertNoReplace(key cid.Cid, n uint64) { @@ -77,7 +77,31 @@ func (ii *insertionIndex) Get(c cid.Cid) (uint64, error) { return 0, errUnsupported } - return r.Record.Idx, nil + return r.Record.Offset, nil +} + +func (ii *insertionIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { + d, err := multihash.Decode(c.Hash()) + if err != nil { + return err + } + entry := recordDigest{digest: d.Digest} + + any := false + iter := func(i llrb.Item) bool { + existing := i.(recordDigest) + if !bytes.Equal(existing.digest, entry.digest) { + // We've already looked at all entries with matching digests. + return false + } + any = true + return fn(existing.Record.Offset) + } + ii.items.AscendGreaterOrEqual(entry, iter) + if !any { + return index.ErrNotFound + } + return nil } func (ii *insertionIndex) Marshal(w io.Writer) error { @@ -152,6 +176,10 @@ func (ii *insertionIndex) flatten() (index.Index, error) { return si, nil } +// note that hasExactCID is very similar to GetAll, +// but it's separate as it allows us to compare Record.Cid directly, +// whereas GetAll just provides Record.Offset. + func (ii *insertionIndex) hasExactCID(c cid.Cid) bool { d, err := multihash.Decode(c.Hash()) if err != nil { diff --git a/ipld/car/v2/blockstore/readonly.go b/ipld/car/v2/blockstore/readonly.go index 34672cad7..6c2397f99 100644 --- a/ipld/car/v2/blockstore/readonly.go +++ b/ipld/car/v2/blockstore/readonly.go @@ -65,7 +65,6 @@ type ReadOnly struct { // go-car/v2 package. func UseWholeCIDs(enable bool) carv2.ReadOption { return func(o *carv2.ReadOptions) { - // TODO: update methods like Get, Has, and AllKeysChan to obey this. o.BlockstoreUseWholeCIDs = enable } } @@ -177,22 +176,35 @@ func (b *ReadOnly) Has(key cid.Cid) (bool, error) { b.mu.RLock() defer b.mu.RUnlock() - offset, err := b.idx.Get(key) + var fnFound bool + var fnErr error + err := b.idx.GetAll(key, func(offset uint64) bool { + uar := internalio.NewOffsetReadSeeker(b.backing, int64(offset)) + var err error + _, err = varint.ReadUvarint(uar) + if err != nil { + fnErr = err + return false + } + _, readCid, err := cid.CidFromReader(uar) + if err != nil { + fnErr = err + return false + } + if b.ropts.BlockstoreUseWholeCIDs { + fnFound = readCid.Equals(key) + return !fnFound // continue looking if we haven't found it + } else { + fnFound = bytes.Equal(readCid.Hash(), key.Hash()) + return false + } + }) if errors.Is(err, index.ErrNotFound) { return false, nil } else if err != nil { return false, err } - uar := internalio.NewOffsetReadSeeker(b.backing, int64(offset)) - _, err = varint.ReadUvarint(uar) - if err != nil { - return false, err - } - _, c, err := cid.CidFromReader(uar) - if err != nil { - return false, err - } - return bytes.Equal(key.Hash(), c.Hash()), nil + return fnFound, fnErr } // Get gets a block corresponding to the given key. @@ -200,21 +212,39 @@ func (b *ReadOnly) Get(key cid.Cid) (blocks.Block, error) { b.mu.RLock() defer b.mu.RUnlock() - offset, err := b.idx.Get(key) - if err != nil { - if err == index.ErrNotFound { - err = blockstore.ErrNotFound + var fnData []byte + var fnErr error + err := b.idx.GetAll(key, func(offset uint64) bool { + readCid, data, err := b.readBlock(int64(offset)) + if err != nil { + fnErr = err + return false } + if b.ropts.BlockstoreUseWholeCIDs { + if readCid.Equals(key) { + fnData = data + return false + } else { + return true // continue looking + } + } else { + if bytes.Equal(readCid.Hash(), key.Hash()) { + fnData = data + } + return false + } + }) + if errors.Is(err, index.ErrNotFound) { + return nil, blockstore.ErrNotFound + } else if err != nil { return nil, err + } else if fnErr != nil { + return nil, fnErr } - entry, data, err := b.readBlock(int64(offset)) - if err != nil { - return nil, err - } - if !bytes.Equal(key.Hash(), entry.Hash()) { + if fnData == nil { return nil, blockstore.ErrNotFound } - return blocks.NewBlockWithCid(data, key) + return blocks.NewBlockWithCid(fnData, key) } // GetSize gets the size of an item corresponding to the given key. @@ -222,23 +252,45 @@ func (b *ReadOnly) GetSize(key cid.Cid) (int, error) { b.mu.RLock() defer b.mu.RUnlock() - idx, err := b.idx.Get(key) - if err != nil { - return -1, err - } - rdr := internalio.NewOffsetReadSeeker(b.backing, int64(idx)) - sectionLen, err := varint.ReadUvarint(rdr) - if err != nil { + var fnSize int = -1 + var fnErr error + err := b.idx.GetAll(key, func(offset uint64) bool { + rdr := internalio.NewOffsetReadSeeker(b.backing, int64(offset)) + sectionLen, err := varint.ReadUvarint(rdr) + if err != nil { + fnErr = err + return false + } + cidLen, readCid, err := cid.CidFromReader(rdr) + if err != nil { + fnErr = err + return false + } + if b.ropts.BlockstoreUseWholeCIDs { + if readCid.Equals(key) { + fnSize = int(sectionLen) - cidLen + return false + } else { + return true // continue looking + } + } else { + if bytes.Equal(readCid.Hash(), key.Hash()) { + fnSize = int(sectionLen) - cidLen + } + return false + } + }) + if errors.Is(err, index.ErrNotFound) { return -1, blockstore.ErrNotFound + } else if err != nil { + return -1, err + } else if fnErr != nil { + return -1, fnErr } - cidLen, readCid, err := cid.CidFromReader(rdr) - if err != nil { - return 0, err - } - if !readCid.Equals(key) { + if fnSize == -1 { return -1, blockstore.ErrNotFound } - return int(sectionLen) - cidLen, err + return fnSize, nil } // Put is not supported and always returns an error. @@ -304,6 +356,11 @@ func (b *ReadOnly) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return // TODO: log this error } + // If we're just using multihashes, flatten to the "raw" codec. + if !b.ropts.BlockstoreUseWholeCIDs { + c = cid.NewCidV1(cid.Raw, c.Hash()) + } + select { case ch <- c: case <-ctx.Done(): diff --git a/ipld/car/v2/blockstore/readonly_test.go b/ipld/car/v2/blockstore/readonly_test.go index 3d75fd96d..4d443c3cf 100644 --- a/ipld/car/v2/blockstore/readonly_test.go +++ b/ipld/car/v2/blockstore/readonly_test.go @@ -49,7 +49,9 @@ func TestReadOnly(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - subject, err := OpenReadOnly(tt.v1OrV2path) + subject, err := OpenReadOnly(tt.v1OrV2path, + UseWholeCIDs(true), + ) t.Cleanup(func() { subject.Close() }) require.NoError(t, err) diff --git a/ipld/car/v2/blockstore/readwrite.go b/ipld/car/v2/blockstore/readwrite.go index 665a65384..ba87ee601 100644 --- a/ipld/car/v2/blockstore/readwrite.go +++ b/ipld/car/v2/blockstore/readwrite.go @@ -89,7 +89,6 @@ func AllowDuplicatePuts(allow bool) carv2.WriteOption { // Resuming from finalized files is allowed. However, resumption will regenerate the index // regardless by scanning every existing block in file. func OpenReadWrite(path string, roots []cid.Cid, opts ...carv2.ReadWriteOption) (*ReadWrite, error) { - // TODO: enable deduplication by default now that resumption is automatically attempted. f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0o666) // TODO: Should the user be able to configure FileMode permissions? if err != nil { return nil, fmt.Errorf("could not open read/write file: %w", err) diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 6612268f1..2494da6ab 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -123,21 +123,29 @@ func TestBlockstore(t *testing.T) { func TestBlockstorePutSameHashes(t *testing.T) { tdir := t.TempDir() - // wbs allows duplicate puts. - wbs, err := blockstore.OpenReadWrite( - filepath.Join(tdir, "readwrite.car"), nil, + // This blockstore allows duplicate puts, + // and identifies by multihash as per the default. + wbsAllowDups, err := blockstore.OpenReadWrite( + filepath.Join(tdir, "readwrite-allowdup.car"), nil, blockstore.AllowDuplicatePuts(true), ) require.NoError(t, err) - t.Cleanup(func() { wbs.Finalize() }) + t.Cleanup(func() { wbsAllowDups.Finalize() }) - // wbs deduplicates puts by CID. - wbsd, err := blockstore.OpenReadWrite( - filepath.Join(tdir, "readwrite-dedup.car"), nil, + // This blockstore deduplicates puts by CID. + wbsByCID, err := blockstore.OpenReadWrite( + filepath.Join(tdir, "readwrite-dedup-wholecid.car"), nil, blockstore.UseWholeCIDs(true), ) require.NoError(t, err) - t.Cleanup(func() { wbsd.Finalize() }) + t.Cleanup(func() { wbsByCID.Finalize() }) + + // This blockstore deduplicates puts by multihash. + wbsByHash, err := blockstore.OpenReadWrite( + filepath.Join(tdir, "readwrite-dedup-hash.car"), nil, + ) + require.NoError(t, err) + t.Cleanup(func() { wbsByHash.Finalize() }) var blockList []blocks.Block @@ -160,15 +168,15 @@ func TestBlockstorePutSameHashes(t *testing.T) { // However, we have multiple CIDs for each multihash. // We also have two duplicate CIDs. data1 := []byte("foo bar") - appendBlock(data1, 0, cid.Raw) - appendBlock(data1, 1, cid.Raw) + appendBlock(data1, 0, cid.DagProtobuf) + appendBlock(data1, 1, cid.DagProtobuf) appendBlock(data1, 1, cid.DagCBOR) appendBlock(data1, 1, cid.DagCBOR) // duplicate CID data2 := []byte("foo bar baz") - appendBlock(data2, 0, cid.Raw) - appendBlock(data2, 1, cid.Raw) - appendBlock(data2, 1, cid.Raw) // duplicate CID + appendBlock(data2, 0, cid.DagProtobuf) + appendBlock(data2, 1, cid.DagProtobuf) + appendBlock(data2, 1, cid.DagProtobuf) // duplicate CID appendBlock(data2, 1, cid.DagCBOR) countBlocks := func(bs *blockstore.ReadWrite) int { @@ -176,52 +184,75 @@ func TestBlockstorePutSameHashes(t *testing.T) { require.NoError(t, err) n := 0 - for range ch { + for c := range ch { + if c.Prefix().Codec == cid.Raw { + if bs == wbsByCID { + t.Error("expected blockstore with UseWholeCIDs to not flatten on AllKeysChan") + } + } else { + if bs != wbsByCID { + t.Error("expected blockstore without UseWholeCIDs to flatten on AllKeysChan") + } + } n++ } return n } - for i, block := range blockList { - // Has should never error here. - // The first block should be missing. - // Others might not, given the duplicate hashes. - has, err := wbs.Has(block.Cid()) - require.NoError(t, err) - if i == 0 { - require.False(t, has) - } + putBlockList := func(bs *blockstore.ReadWrite) { + for i, block := range blockList { + // Has should never error here. + // The first block should be missing. + // Others might not, given the duplicate hashes. + has, err := bs.Has(block.Cid()) + require.NoError(t, err) + if i == 0 { + require.False(t, has) + } - err = wbs.Put(block) - require.NoError(t, err) - } + err = bs.Put(block) + require.NoError(t, err) - for _, block := range blockList { - has, err := wbs.Has(block.Cid()) - require.NoError(t, err) - require.True(t, has) + // Has, Get, and GetSize need to work right after a Put. + has, err = bs.Has(block.Cid()) + require.NoError(t, err) + require.True(t, has) - got, err := wbs.Get(block.Cid()) - require.NoError(t, err) - require.Equal(t, block.Cid(), got.Cid()) - require.Equal(t, block.RawData(), got.RawData()) + got, err := bs.Get(block.Cid()) + require.NoError(t, err) + require.Equal(t, block.Cid(), got.Cid()) + require.Equal(t, block.RawData(), got.RawData()) + + size, err := bs.GetSize(block.Cid()) + require.NoError(t, err) + require.Equal(t, len(block.RawData()), size) + } } - require.Equal(t, len(blockList), countBlocks(wbs)) + putBlockList(wbsAllowDups) + require.Equal(t, len(blockList), countBlocks(wbsAllowDups)) - err = wbs.Finalize() + err = wbsAllowDups.Finalize() require.NoError(t, err) // Put the same list of blocks to the blockstore that // deduplicates by CID. - // We should end up with two fewer blocks. - for _, block := range blockList { - err = wbsd.Put(block) - require.NoError(t, err) - } - require.Equal(t, len(blockList)-2, countBlocks(wbsd)) + // We should end up with two fewer blocks, + // as two are entire CID duplicates. + putBlockList(wbsByCID) + require.Equal(t, len(blockList)-2, countBlocks(wbsByCID)) + + err = wbsByCID.Finalize() + require.NoError(t, err) + + // Put the same list of blocks to the blockstore that + // deduplicates by CID. + // We should end up with just two blocks, + // as the original set of blocks only has two distinct multihashes. + putBlockList(wbsByHash) + require.Equal(t, 2, countBlocks(wbsByHash)) - err = wbsd.Finalize() + err = wbsByHash.Finalize() require.NoError(t, err) } @@ -280,7 +311,8 @@ func TestBlockstoreNullPadding(t *testing.T) { // A sample null-padded CARv1 file. paddedV1 = append(paddedV1, make([]byte, 2048)...) - rbs, err := blockstore.NewReadOnly(bufferReaderAt(paddedV1), nil, carv2.ZeroLengthSectionAsEOF) + rbs, err := blockstore.NewReadOnly(bufferReaderAt(paddedV1), nil, + carv2.ZeroLengthSectionAsEOF(true)) require.NoError(t, err) roots, err := rbs.Roots() @@ -313,7 +345,8 @@ func TestBlockstoreResumption(t *testing.T) { path := filepath.Join(t.TempDir(), "readwrite-resume.car") // Create an incomplete CARv2 file with no blocks put. - subject, err := blockstore.OpenReadWrite(path, r.Header.Roots) + subject, err := blockstore.OpenReadWrite(path, r.Header.Roots, + blockstore.UseWholeCIDs(true)) require.NoError(t, err) // For each block resume on the same file, putting blocks one at a time. @@ -345,7 +378,8 @@ func TestBlockstoreResumption(t *testing.T) { // We do this to avoid resource leak during testing. require.NoError(t, subject.Close()) } - subject, err = blockstore.OpenReadWrite(path, r.Header.Roots) + subject, err = blockstore.OpenReadWrite(path, r.Header.Roots, + blockstore.UseWholeCIDs(true)) require.NoError(t, err) } require.NoError(t, subject.Put(b)) @@ -377,7 +411,8 @@ func TestBlockstoreResumption(t *testing.T) { require.NoError(t, subject.Close()) // Finalize the blockstore to complete partially written CARv2 file. - subject, err = blockstore.OpenReadWrite(path, r.Header.Roots) + subject, err = blockstore.OpenReadWrite(path, r.Header.Roots, + blockstore.UseWholeCIDs(true)) require.NoError(t, err) require.NoError(t, subject.Finalize()) @@ -528,9 +563,9 @@ func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) { indexSize := stat.Size() - int64(wantIndexOffset) gotIdx, err := index.ReadFrom(io.NewSectionReader(f, int64(wantIndexOffset), indexSize)) require.NoError(t, err) - _, err = gotIdx.Get(oneTestBlockCid) + _, err = index.GetFirst(gotIdx, oneTestBlockCid) require.NoError(t, err) - _, err = gotIdx.Get(anotherTestBlockCid) + _, err = index.GetFirst(gotIdx, anotherTestBlockCid) require.NoError(t, err) } diff --git a/ipld/car/v2/index/example_test.go b/ipld/car/v2/index/example_test.go index 9f8368d1a..47347d835 100644 --- a/ipld/car/v2/index/example_test.go +++ b/ipld/car/v2/index/example_test.go @@ -34,7 +34,7 @@ func ExampleReadFrom() { // For each root CID print the offset relative to CARv1 data payload. for _, r := range roots { - offset, err := idx.Get(r) + offset, err := index.GetFirst(idx, r) if err != nil { panic(err) } diff --git a/ipld/car/v2/index/index.go b/ipld/car/v2/index/index.go index 119821714..3408dfbd2 100644 --- a/ipld/car/v2/index/index.go +++ b/ipld/car/v2/index/index.go @@ -14,33 +14,59 @@ import ( "github.com/ipfs/go-cid" ) -// Codec table is a first var-int in CAR indexes -const ( - indexHashed codec = 0x300000 + iota - indexSingleSorted - indexGobHashed -) - type ( - // codec is used as a multicodec identifier for CAR index files - codec int - // Record is a pre-processed record of a car item and location. Record struct { cid.Cid - Idx uint64 + Offset uint64 } // Index provides an interface for looking up byte offset of a given CID. + // + // Note that each indexing mechanism is free to match CIDs however it + // sees fit. For example, multicodec.CarIndexSorted only indexes + // multihash digests, meaning that Get and GetAll will find matching + // blocks even if the CID's encoding multicodec differs. Other index + // implementations might index the entire CID, the entire multihash, or + // just part of a multihash's digest. Index interface { + // Codec provides the multicodec code that the index implements. + // + // Note that this may return a reserved code if the index + // implementation is not defined in a spec. Codec() multicodec.Code + + // Marshal encodes the index in serial form. Marshal(w io.Writer) error + // Unmarshal decodes the index from its serial form. Unmarshal(r io.Reader) error - Get(cid.Cid) (uint64, error) + + // Load inserts a number of records into the index. Load([]Record) error + + // Get looks up all blocks matching a given CID, + // calling a function for each one of their offsets. + // + // If the function returns false, GetAll stops. + // + // If no error occurred and the CID isn't indexed, + // meaning that no callbacks happen, + // ErrNotFound is returned. + GetAll(cid.Cid, func(uint64) bool) error } ) +// GetFirst is a wrapper over Index.GetAll, returning the offset for the first +// matching indexed CID. +func GetFirst(idx Index, key cid.Cid) (uint64, error) { + var firstOffset uint64 + err := idx.GetAll(key, func(offset uint64) bool { + firstOffset = offset + return false + }) + return firstOffset, err +} + // New constructs a new index corresponding to the given CAR index codec. func New(codec multicodec.Code) (Index, error) { switch codec { diff --git a/ipld/car/v2/index/index_test.go b/ipld/car/v2/index/index_test.go index a32205dca..03efbc94d 100644 --- a/ipld/car/v2/index/index_test.go +++ b/ipld/car/v2/index/index_test.go @@ -32,21 +32,6 @@ func TestNew(t *testing.T) { codec: multicodec.Cidv1, wantErr: true, }, - { - name: "IndexSingleSortedMultiCodecIsError", - codec: multicodec.Code(indexSingleSorted), - wantErr: true, - }, - { - name: "IndexHashedMultiCodecIsError", - codec: multicodec.Code(indexHashed), - wantErr: true, - }, - { - name: "IndexGobHashedMultiCodecIsError", - codec: multicodec.Code(indexGobHashed), - wantErr: true, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -82,7 +67,7 @@ func TestReadFrom(t *testing.T) { require.NoError(t, err) // Get offset from the index for a CID and assert it exists - gotOffset, err := subject.Get(wantBlock.Cid()) + gotOffset, err := GetFirst(subject, wantBlock.Cid()) require.NoError(t, err) require.NotZero(t, gotOffset) diff --git a/ipld/car/v2/index/indexgobhash.go b/ipld/car/v2/index/indexgobhash.go deleted file mode 100644 index a74e8b92b..000000000 --- a/ipld/car/v2/index/indexgobhash.go +++ /dev/null @@ -1,48 +0,0 @@ -package index - -import ( - "encoding/gob" - "io" - - "github.com/multiformats/go-multicodec" - - "github.com/ipfs/go-cid" -) - -//lint:ignore U1000 kept for potential future use. -type mapGobIndex map[cid.Cid]uint64 - -func (m *mapGobIndex) Get(c cid.Cid) (uint64, error) { - el, ok := (*m)[c] - if !ok { - return 0, ErrNotFound - } - return el, nil -} - -func (m *mapGobIndex) Marshal(w io.Writer) error { - e := gob.NewEncoder(w) - return e.Encode(m) -} - -func (m *mapGobIndex) Unmarshal(r io.Reader) error { - d := gob.NewDecoder(r) - return d.Decode(m) -} - -func (m *mapGobIndex) Codec() multicodec.Code { - return multicodec.Code(indexHashed) -} - -func (m *mapGobIndex) Load(rs []Record) error { - for _, r := range rs { - (*m)[r.Cid] = r.Idx - } - return nil -} - -//lint:ignore U1000 kept for potential future use. -func newGobHashed() Index { - mi := make(mapGobIndex) - return &mi -} diff --git a/ipld/car/v2/index/indexhashed.go b/ipld/car/v2/index/indexhashed.go deleted file mode 100644 index 84b0ad157..000000000 --- a/ipld/car/v2/index/indexhashed.go +++ /dev/null @@ -1,47 +0,0 @@ -package index - -import ( - "io" - - "github.com/multiformats/go-multicodec" - - "github.com/ipfs/go-cid" - cbor "github.com/whyrusleeping/cbor/go" -) - -//lint:ignore U1000 kept for potential future use. -type mapIndex map[cid.Cid]uint64 - -func (m *mapIndex) Get(c cid.Cid) (uint64, error) { - el, ok := (*m)[c] - if !ok { - return 0, ErrNotFound - } - return el, nil -} - -func (m *mapIndex) Marshal(w io.Writer) error { - return cbor.Encode(w, m) -} - -func (m *mapIndex) Unmarshal(r io.Reader) error { - d := cbor.NewDecoder(r) - return d.Decode(m) -} - -func (m *mapIndex) Codec() multicodec.Code { - return multicodec.Code(indexHashed) -} - -func (m *mapIndex) Load(rs []Record) error { - for _, r := range rs { - (*m)[r.Cid] = r.Idx - } - return nil -} - -//lint:ignore U1000 kept for potential future use. -func newHashed() Index { - mi := make(mapIndex) - return &mi -} diff --git a/ipld/car/v2/index/indexsorted.go b/ipld/car/v2/index/indexsorted.go index 65446f665..5f37eee44 100644 --- a/ipld/car/v2/index/indexsorted.go +++ b/ipld/car/v2/index/indexsorted.go @@ -44,10 +44,6 @@ func (r recordSet) Swap(i, j int) { r[i], r[j] = r[j], r[i] } -func (s *singleWidthIndex) Codec() multicodec.Code { - return multicodec.Code(indexSingleSorted) -} - func (s *singleWidthIndex) Marshal(w io.Writer) error { if err := binary.Write(w, binary.LittleEndian, s.width); err != nil { return err @@ -77,25 +73,34 @@ func (s *singleWidthIndex) Less(i int, digest []byte) bool { return bytes.Compare(digest[:], s.index[i*int(s.width):((i+1)*int(s.width)-8)]) <= 0 } -func (s *singleWidthIndex) Get(c cid.Cid) (uint64, error) { +func (s *singleWidthIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { d, err := multihash.Decode(c.Hash()) if err != nil { - return 0, err + return err } - return s.get(d.Digest) + return s.getAll(d.Digest, fn) } -func (s *singleWidthIndex) get(d []byte) (uint64, error) { +func (s *singleWidthIndex) getAll(d []byte, fn func(uint64) bool) error { idx := sort.Search(int(s.len), func(i int) bool { return s.Less(i, d) }) if uint64(idx) == s.len { - return 0, ErrNotFound + return ErrNotFound + } + + any := false + for bytes.Equal(d[:], s.index[idx*int(s.width):(idx+1)*int(s.width)-8]) { + any = true + offset := binary.LittleEndian.Uint64(s.index[(idx+1)*int(s.width)-8 : (idx+1)*int(s.width)]) + if !fn(offset) { + break + } } - if !bytes.Equal(d[:], s.index[idx*int(s.width):(idx+1)*int(s.width)-8]) { - return 0, ErrNotFound + if !any { + return ErrNotFound } - return binary.LittleEndian.Uint64(s.index[(idx+1)*int(s.width)-8 : (idx+1)*int(s.width)]), nil + return nil } func (s *singleWidthIndex) Load(items []Record) error { @@ -115,15 +120,15 @@ func (s *singleWidthIndex) Load(items []Record) error { return nil } -func (m *multiWidthIndex) Get(c cid.Cid) (uint64, error) { +func (m *multiWidthIndex) GetAll(c cid.Cid, fn func(uint64) bool) error { d, err := multihash.Decode(c.Hash()) if err != nil { - return 0, err + return err } if s, ok := (*m)[uint32(len(d.Digest)+8)]; ok { - return s.get(d.Digest) + return s.getAll(d.Digest, fn) } - return 0, ErrNotFound + return ErrNotFound } func (m *multiWidthIndex) Codec() multicodec.Code { @@ -184,7 +189,7 @@ func (m *multiWidthIndex) Load(items []Record) error { idxs[len(digest)] = make([]digestRecord, 0) idx = idxs[len(digest)] } - idxs[len(digest)] = append(idx, digestRecord{digest, item.Idx}) + idxs[len(digest)] = append(idx, digestRecord{digest, item.Offset}) } // Sort each list. then write to compact form. @@ -209,9 +214,3 @@ func newSorted() Index { m := make(multiWidthIndex) return &m } - -//lint:ignore U1000 kept for potential future use. -func newSingleSorted() Index { - s := singleWidthIndex{} - return &s -} diff --git a/ipld/car/v2/index/indexsorted_test.go b/ipld/car/v2/index/indexsorted_test.go index fe0ca961a..767937b79 100644 --- a/ipld/car/v2/index/indexsorted_test.go +++ b/ipld/car/v2/index/indexsorted_test.go @@ -18,10 +18,6 @@ func TestSortedIndex_GetReturnsNotFoundWhenCidDoesNotExist(t *testing.T) { name string subject Index }{ - { - "SingleSorted", - newSingleSorted(), - }, { "Sorted", newSorted(), @@ -29,7 +25,7 @@ func TestSortedIndex_GetReturnsNotFoundWhenCidDoesNotExist(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - gotOffset, err := tt.subject.Get(nonExistingKey) + gotOffset, err := GetFirst(tt.subject, nonExistingKey) require.Equal(t, ErrNotFound, err) require.Equal(t, uint64(0), gotOffset) }) diff --git a/ipld/car/v2/index_gen.go b/ipld/car/v2/index_gen.go index 5a60fb711..ae938498a 100644 --- a/ipld/car/v2/index_gen.go +++ b/ipld/car/v2/index_gen.go @@ -75,7 +75,7 @@ func GenerateIndex(v1r io.Reader, opts ...ReadOption) (index.Index, error) { if err != nil { return nil, err } - records = append(records, index.Record{Cid: c, Idx: uint64(sectionOffset)}) + records = append(records, index.Record{Cid: c, Offset: uint64(sectionOffset)}) // Seek to the next section by skipping the block. // The section length includes the CID, so subtract it. diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go index 89044a761..b206c8342 100644 --- a/ipld/car/v2/options.go +++ b/ipld/car/v2/options.go @@ -46,8 +46,10 @@ type ReadWriteOption interface { // a zero-length section as the end of the input CAR file. For example, this can // be useful to allow "null padding" after a CARv1 without knowing where the // padding begins. -func ZeroLengthSectionAsEOF(o *ReadOptions) { - o.ZeroLengthSectionAsEOF = true +func ZeroLengthSectionAsEOF(enable bool) ReadOption { + return func(o *ReadOptions) { + o.ZeroLengthSectionAsEOF = true + } } // UseDataPadding is a write option which sets the padding to be added between From b56791a73872334f23bff0c9f1a1451b7b8a18aa Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 20 Jul 2021 16:47:59 +0100 Subject: [PATCH 3503/3817] Avoid writing to files in testdata Avoid writing to files in `testdata` through examples. This is because when tests that use those file are run in parallel will fail if files are modified. Instead write to a temporary file in examples, and whenever opening testdata files in `RW` mode, make a temporary copy. Thanks to @mvdan for pointing this out. Fixes #175 This commit was moved from ipld/go-car@c3a595560230886d34e35e63ce258eb79c815ccc --- ipld/car/v2/blockstore/example_test.go | 13 ++++++++++--- ipld/car/v2/blockstore/readwrite_test.go | 20 +++++++++++++++++++- ipld/car/v2/example_test.go | 8 +++++++- ipld/car/v2/index/example_test.go | 14 ++++++++++---- 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/ipld/car/v2/blockstore/example_test.go b/ipld/car/v2/blockstore/example_test.go index 00a81dc7f..3091e471e 100644 --- a/ipld/car/v2/blockstore/example_test.go +++ b/ipld/car/v2/blockstore/example_test.go @@ -3,6 +3,9 @@ package blockstore_test import ( "context" "fmt" + "io/ioutil" + "os" + "path/filepath" blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" @@ -79,10 +82,14 @@ func ExampleOpenReadWrite() { thatBlock := merkledag.NewRawNode([]byte("lobster")).Block andTheOtherBlock := merkledag.NewRawNode([]byte("barreleye")).Block - dest := "../testdata/sample-rw-bs-v2.car" + tdir, err := ioutil.TempDir(os.TempDir(), "example-*") + if err != nil { + panic(err) + } + dst := filepath.Join(tdir, "sample-rw-bs-v2.car") roots := []cid.Cid{thisBlock.Cid(), thatBlock.Cid(), andTheOtherBlock.Cid()} - rwbs, err := blockstore.OpenReadWrite(dest, roots, carv2.UseDataPadding(1413), carv2.UseIndexPadding(42)) + rwbs, err := blockstore.OpenReadWrite(dst, roots, carv2.UseDataPadding(1413), carv2.UseIndexPadding(42)) if err != nil { panic(err) } @@ -110,7 +117,7 @@ func ExampleOpenReadWrite() { // Resume from the same file to add more blocks. // Note the UseDataPadding and roots must match the values passed to the blockstore instance // that created the original file. Otherwise, we cannot resume from the same file. - resumedRwbos, err := blockstore.OpenReadWrite(dest, roots, carv2.UseDataPadding(1413)) + resumedRwbos, err := blockstore.OpenReadWrite(dst, roots, carv2.UseDataPadding(1413)) if err != nil { panic(err) } diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index 2494da6ab..d53379fc5 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -570,11 +570,29 @@ func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) { } func TestReadWriteResumptionFromNonV2FileIsError(t *testing.T) { - subject, err := blockstore.OpenReadWrite("../testdata/sample-rootless-v42.car", []cid.Cid{}) + tmpPath := requireTmpCopy(t, "../testdata/sample-rootless-v42.car") + subject, err := blockstore.OpenReadWrite(tmpPath, []cid.Cid{}) require.EqualError(t, err, "cannot resume on CAR file with version 42") require.Nil(t, subject) } +func requireTmpCopy(t *testing.T, src string) string { + srcF, err := os.Open(src) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, srcF.Close()) }) + stats, err := srcF.Stat() + require.NoError(t, err) + + dst := filepath.Join(t.TempDir(), stats.Name()) + dstF, err := os.Create(dst) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, dstF.Close()) }) + + _, err = io.Copy(dstF, srcF) + require.NoError(t, err) + return dst +} + func TestReadWriteResumptionFromFileWithDifferentCarV1PaddingIsError(t *testing.T) { oneTestBlockCid := oneTestBlockWithCidV1.Cid() WantRoots := []cid.Cid{oneTestBlockCid} diff --git a/ipld/car/v2/example_test.go b/ipld/car/v2/example_test.go index dd3e002b2..37bcc22b5 100644 --- a/ipld/car/v2/example_test.go +++ b/ipld/car/v2/example_test.go @@ -4,6 +4,8 @@ import ( "bytes" "fmt" "io/ioutil" + "os" + "path/filepath" carv2 "github.com/ipld/go-car/v2" "github.com/ipld/go-car/v2/blockstore" @@ -15,7 +17,11 @@ func ExampleWrapV1File() { // Writing the result to testdata allows reusing that file in other tests, // and also helps ensure that the result is deterministic. src := "testdata/sample-v1.car" - dst := "testdata/sample-wrapped-v2.car" + tdir, err := ioutil.TempDir(os.TempDir(), "example-*") + if err != nil { + panic(err) + } + dst := filepath.Join(tdir, "wrapped-v2.car") if err := carv2.WrapV1File(src, dst); err != nil { panic(err) } diff --git a/ipld/car/v2/index/example_test.go b/ipld/car/v2/index/example_test.go index 47347d835..507046207 100644 --- a/ipld/car/v2/index/example_test.go +++ b/ipld/car/v2/index/example_test.go @@ -3,7 +3,9 @@ package index_test import ( "fmt" "io" + "io/ioutil" "os" + "path/filepath" "reflect" carv2 "github.com/ipld/go-car/v2" @@ -67,8 +69,12 @@ func ExampleWriteTo() { } // Store the index alone onto destination file. - dest := "../testdata/sample-index.carindex" - f, err := os.Create(dest) + tdir, err := ioutil.TempDir(os.TempDir(), "example-*") + if err != nil { + panic(err) + } + dst := filepath.Join(tdir, "index.carindex") + f, err := os.Create(dst) if err != nil { panic(err) } @@ -96,11 +102,11 @@ func ExampleWriteTo() { // Expect indices to be equal. if reflect.DeepEqual(idx, reReadIdx) { - fmt.Printf("Saved index file at %v matches the index embedded in CARv2 at %v.\n", dest, src) + fmt.Printf("Saved index file matches the index embedded in CARv2 at %v.\n", src) } else { panic("expected to get the same index as the CARv2 file") } // Output: - // Saved index file at ../testdata/sample-index.carindex matches the index embedded in CARv2 at ../testdata/sample-wrapped-v2.car. + // Saved index file matches the index embedded in CARv2 at ../testdata/sample-wrapped-v2.car. } From 4644dbb113cdb0ea750fda7565f973597760a16e Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 20 Jul 2021 17:36:44 +0100 Subject: [PATCH 3504/3817] Use `ioutil.TempFile` to simplify file creation in index example Use existing SDK to create temp file in index example. This commit was moved from ipld/go-car@326783fe5b60a7e80510fb3267ca08d13343b559 --- ipld/car/v2/index/example_test.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/ipld/car/v2/index/example_test.go b/ipld/car/v2/index/example_test.go index 507046207..ca0ac73ab 100644 --- a/ipld/car/v2/index/example_test.go +++ b/ipld/car/v2/index/example_test.go @@ -5,7 +5,6 @@ import ( "io" "io/ioutil" "os" - "path/filepath" "reflect" carv2 "github.com/ipld/go-car/v2" @@ -69,12 +68,7 @@ func ExampleWriteTo() { } // Store the index alone onto destination file. - tdir, err := ioutil.TempDir(os.TempDir(), "example-*") - if err != nil { - panic(err) - } - dst := filepath.Join(tdir, "index.carindex") - f, err := os.Create(dst) + f, err := ioutil.TempFile(os.TempDir(), "example-index-*.carindex") if err != nil { panic(err) } From 2299fcbdc3e19b34a96cf933d96b2af5760f4d64 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 21 Jul 2021 10:04:43 +0100 Subject: [PATCH 3505/3817] Allow `ReadOption`s to be set when getting or generating index Fix a bug in read options where the value of zero-len option is always set to `true` even if the flag passed is false. This meant if the option was present it would have always enabled the zero-len as EOF. Allow the user to specify read options on `ReadOrGenerateIndex`. Pass the options onto downstream reader/generators. Generate a CARv1 file with null padding and check it into `testdata`. In addition check in another such file from ignite just to have two different samples of this case. Enhance index generation tests to expect error when zero-len option is not set, and generate index when it is for files with null padding. Make RW blockstore test use the fixed test files from `testdata` instead of generating every time. This commit was moved from ipld/go-car@396cc2289891953aa5f2445d28e22a92994b938b --- ipld/car/v2/blockstore/readwrite_test.go | 5 +-- ipld/car/v2/index_gen.go | 8 ++-- ipld/car/v2/index_gen_test.go | 41 +++++++++++++++++- ipld/car/v2/options.go | 2 +- .../sample-v1-with-zero-len-section.car | Bin 0 -> 481955 bytes .../sample-v1-with-zero-len-section2.car | Bin 0 -> 32512 bytes 6 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 ipld/car/v2/testdata/sample-v1-with-zero-len-section.car create mode 100644 ipld/car/v2/testdata/sample-v1-with-zero-len-section2.car diff --git a/ipld/car/v2/blockstore/readwrite_test.go b/ipld/car/v2/blockstore/readwrite_test.go index d53379fc5..f3016e25d 100644 --- a/ipld/car/v2/blockstore/readwrite_test.go +++ b/ipld/car/v2/blockstore/readwrite_test.go @@ -305,12 +305,9 @@ func (b bufferReaderAt) ReadAt(p []byte, off int64) (int, error) { } func TestBlockstoreNullPadding(t *testing.T) { - paddedV1, err := ioutil.ReadFile("../testdata/sample-v1.car") + paddedV1, err := ioutil.ReadFile("../testdata/sample-v1-with-zero-len-section.car") require.NoError(t, err) - // A sample null-padded CARv1 file. - paddedV1 = append(paddedV1, make([]byte, 2048)...) - rbs, err := blockstore.NewReadOnly(bufferReaderAt(paddedV1), nil, carv2.ZeroLengthSectionAsEOF(true)) require.NoError(t, err) diff --git a/ipld/car/v2/index_gen.go b/ipld/car/v2/index_gen.go index ae938498a..11bf36bee 100644 --- a/ipld/car/v2/index_gen.go +++ b/ipld/car/v2/index_gen.go @@ -110,7 +110,7 @@ func GenerateIndexFromFile(path string) (index.Index, error) { // // Note, the returned index lives entirely in memory and will not depend on the // given reader to fulfill index lookup. -func ReadOrGenerateIndex(rs io.ReadSeeker) (index.Index, error) { +func ReadOrGenerateIndex(rs io.ReadSeeker, opts ...ReadOption) (index.Index, error) { // Read version. version, err := ReadVersion(rs) if err != nil { @@ -124,10 +124,10 @@ func ReadOrGenerateIndex(rs io.ReadSeeker) (index.Index, error) { switch version { case 1: // Simply generate the index, since there can't be a pre-existing one. - return GenerateIndex(rs) + return GenerateIndex(rs, opts...) case 2: // Read CARv2 format - v2r, err := NewReader(internalio.ToReaderAt(rs)) + v2r, err := NewReader(internalio.ToReaderAt(rs), opts...) if err != nil { return nil, err } @@ -136,7 +136,7 @@ func ReadOrGenerateIndex(rs io.ReadSeeker) (index.Index, error) { return index.ReadFrom(v2r.IndexReader()) } // Otherwise, generate index from CARv1 payload wrapped within CARv2 format. - return GenerateIndex(v2r.DataReader()) + return GenerateIndex(v2r.DataReader(), opts...) default: return nil, fmt.Errorf("unknown version %v", version) } diff --git a/ipld/car/v2/index_gen_test.go b/ipld/car/v2/index_gen_test.go index 133aaf925..97eb244b4 100644 --- a/ipld/car/v2/index_gen_test.go +++ b/ipld/car/v2/index_gen_test.go @@ -13,12 +13,14 @@ func TestReadOrGenerateIndex(t *testing.T) { tests := []struct { name string carPath string + readOpts []ReadOption wantIndexer func(t *testing.T) index.Index wantErr bool }{ { "CarV1IsIndexedAsExpected", "testdata/sample-v1.car", + []ReadOption{}, func(t *testing.T) index.Index { v1, err := os.Open("testdata/sample-v1.car") require.NoError(t, err) @@ -32,6 +34,7 @@ func TestReadOrGenerateIndex(t *testing.T) { { "CarV2WithIndexIsReturnedAsExpected", "testdata/sample-wrapped-v2.car", + []ReadOption{}, func(t *testing.T) index.Index { v2, err := os.Open("testdata/sample-wrapped-v2.car") require.NoError(t, err) @@ -44,9 +47,45 @@ func TestReadOrGenerateIndex(t *testing.T) { }, false, }, + { + "CarV1WithZeroLenSectionIsGeneratedAsExpected", + "testdata/sample-v1-with-zero-len-section.car", + []ReadOption{ZeroLengthSectionAsEOF(true)}, + func(t *testing.T) index.Index { + v1, err := os.Open("testdata/sample-v1-with-zero-len-section.car") + require.NoError(t, err) + defer v1.Close() + want, err := GenerateIndex(v1, ZeroLengthSectionAsEOF(true)) + require.NoError(t, err) + return want + }, + false, + }, + { + "AnotherCarV1WithZeroLenSectionIsGeneratedAsExpected", + "testdata/sample-v1-with-zero-len-section2.car", + []ReadOption{ZeroLengthSectionAsEOF(true)}, + func(t *testing.T) index.Index { + v1, err := os.Open("testdata/sample-v1-with-zero-len-section2.car") + require.NoError(t, err) + defer v1.Close() + want, err := GenerateIndex(v1, ZeroLengthSectionAsEOF(true)) + require.NoError(t, err) + return want + }, + false, + }, + { + "CarV1WithZeroLenSectionWithoutOptionIsError", + "testdata/sample-v1-with-zero-len-section.car", + []ReadOption{}, + func(t *testing.T) index.Index { return nil }, + true, + }, { "CarOtherThanV1OrV2IsError", "testdata/sample-rootless-v42.car", + []ReadOption{}, func(t *testing.T) index.Index { return nil }, true, }, @@ -56,7 +95,7 @@ func TestReadOrGenerateIndex(t *testing.T) { carFile, err := os.Open(tt.carPath) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, carFile.Close()) }) - got, err := ReadOrGenerateIndex(carFile) + got, err := ReadOrGenerateIndex(carFile, tt.readOpts...) if tt.wantErr { require.Error(t, err) } diff --git a/ipld/car/v2/options.go b/ipld/car/v2/options.go index b206c8342..86aaa9e55 100644 --- a/ipld/car/v2/options.go +++ b/ipld/car/v2/options.go @@ -48,7 +48,7 @@ type ReadWriteOption interface { // padding begins. func ZeroLengthSectionAsEOF(enable bool) ReadOption { return func(o *ReadOptions) { - o.ZeroLengthSectionAsEOF = true + o.ZeroLengthSectionAsEOF = enable } } diff --git a/ipld/car/v2/testdata/sample-v1-with-zero-len-section.car b/ipld/car/v2/testdata/sample-v1-with-zero-len-section.car new file mode 100644 index 0000000000000000000000000000000000000000..2bb7dd2c211ebe3f990f6d2a11260db7fb215264 GIT binary patch literal 481955 zcmdSBRZtz;(yooW>%`q5XmHoy?ry=|-QC@TySuwXfZ!6`JxGAyhwQW0D%N+C|GW6B z&f>a9O{&Ly-nU1O9+NxI*xts*(ZO$tT8#n-*e+)mgz#PvS&t~k82RzAB9>MjOd+o_4OvAu(tjWzIhi2rX3qJ)6ff&J9kP7mV^XFOuvNS(Jd z{qMJ#FJ(8=_wi8BHQ}u=;<6T|Q=!iO zNagh)J~5HNCL!IMHV1)65=PfX6{g=_2mH!-7ERt++QP$L zF9`?O)_d_m#~buJ~qfQNd<{?*_Wvv1wjvydgi3JwF;2fgnvgXnOaj(8EsnL_~UKh{jg9!E! zc+84I=z?-6Rn5l;4CEwW$VA)=+TrX@(`9fOd&f&m8=xi7@U5BpuZN>~jE^d+nv==6Y~5oF#z9g3{DpJZ9+ zqM&7n$6;!v9Wh$eR%gyd5|mWF1?0UjUad|(!F~szc|(H7>(*YkZsVb5g_^aIQqtT3 z>cGB*FMJcYB@ls{NOg*`EY6~`1oxFO^caW zb9d>NuU_afcpyN=P7H`+JOPVCWllW)JZpV3eKbT#AW<(7sMAE zK>YBc_O2;aFRm35{t<7uWR;p;fkY;ifZ*EjZ&eiFk?u^mJ-f8NrxRqaYZ{?0uZ*FWo zn^7jViqYO&QbyBAS{LLQZ1W|iI(m1Vnx2`cgiq(Av18j}s$TjnE_qiMQ+b#~?rXCm zgP#zVZF#(W2tUTKZ#Jz1%C|t()+?gr1}T~=%8^=MXQNdPD%VV5#9Vk3vjC~To4a8w ztT=1gvu1Vq(a-@|4NgW5ZU7-et0g(BrkCc!`p`=dQ$|00*B$Vj_HC~%SR5x>;%sh2 z>S?S?!1XewedO68u!GBRHu3ESDGG}+NV;{%p?J3V^� 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 Tx_{@>Ah$cBb;X>)D9fct)9`U;$XV@y4$E$ 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